1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * pseudo bus nexus driver
28 * hotplug framework test facility
29 */
30
31 /*
32 * The pshot driver can be used to exercise the i/o framework together
33 * with devfs by configuring an arbitrarily complex device tree.
34 *
35 * The pshot driver is rooted at /devices/pshot. The following commands
36 * illustrate the operation of devfs together with pshot's bus_config.
37 * The first command demonstrates that, like the magician showing there's
38 * nothing up his sleeve, /devices/pshot is empty. The second command
39 * conjures up a branch of pshot nodes. Note that pshot's bus_config is
40 * called sequentially by devfs for each node, as part of the pathname
41 * resolution, and that each pshot node is fully configured and
42 * attached before that node's bus_config is called to configure the
43 * next child down the tree. The final result is a "disk" node configured
44 * at the bottom of the named hierarchy of pshot nodes.
45 *
46 * #
47 * # ls /devices/pshot
48 * #
49 * # ls -ld /devices/pshot/pshot@0/pshot@1/pshot@2/disk@3,0
50 * drwxr-xr-x 2 root sys 512 Feb 6 15:10
51 * /devices/pshot/pshot@0/pshot@1/pshot@2/disk@3,0
52 *
53 * pshot supports some unique behaviors as aids for test error cases.
54 *
55 * Match these special address formats to behavior:
56 *
57 * err.* - induce bus_config error
58 * delay - induce 1 second of bus_config delay time
59 * delay,n - induce n seconds of bus_config delay time
60 * wait - induce 1 second of bus_config wait time
61 * wait,n - induce n seconds of bus_config wait time
62 * failinit.* - induce error at INITCHILD
63 * failprobe.* - induce error at probe
64 * failattach.* - induce error at attach
65 */
66
67 #if defined(lint) && !defined(DEBUG)
68 #define DEBUG 1
69 #endif
70
71 #include <sys/types.h>
72 #include <sys/cmn_err.h>
73 #include <sys/conf.h>
74 #include <sys/ddi_impldefs.h>
75 #include <sys/autoconf.h>
76 #include <sys/open.h>
77 #include <sys/stat.h>
78 #include <sys/file.h>
79 #include <sys/errno.h>
80 #include <sys/systm.h>
81 #include <sys/modctl.h>
82 #include <sys/kmem.h>
83 #include <sys/ddi.h>
84 #include <sys/sunddi.h>
85 #include <sys/sunndi.h>
86 #include <sys/devctl.h>
87 #include <sys/disp.h>
88 #include <sys/utsname.h>
89 #include <sys/pshot.h>
90 #include <sys/debug.h>
91
92 static int pshot_log = 0;
93 static int pshot_devctl_debug = 0;
94 static int pshot_debug_busy = 0;
95
96 static void *pshot_softstatep;
97
98 static int pshot_prop_autoattach;
99
100 #define MAXPWR 3
101
102
103 /*
104 * device configuration data
105 */
106
107 /* should keep in sync with current release */
108 static struct {
109 char *name;
110 char *val;
111 } pshot_nodetypes[] = {
112 {"DDI_NT_SERIAL", DDI_NT_SERIAL},
113 {"DDI_NT_SERIAL_MB", DDI_NT_SERIAL_MB},
114 {"DDI_NT_SERIAL_DO", DDI_NT_SERIAL_DO},
115 {"DDI_NT_SERIAL_MB_DO", DDI_NT_SERIAL_MB_DO},
116 {"DDI_NT_SERIAL_LOMCON", DDI_NT_SERIAL_LOMCON},
117 {"DDI_NT_BLOCK", DDI_NT_BLOCK},
118 {"DDI_NT_BLOCK_CHAN", DDI_NT_BLOCK_CHAN},
119 {"DDI_NT_BLOCK_WWN", DDI_NT_BLOCK_WWN},
120 {"DDI_NT_BLOCK_SAS", DDI_NT_BLOCK_SAS},
121 {"DDI_NT_CD", DDI_NT_CD},
122 {"DDI_NT_CD_CHAN", DDI_NT_CD_CHAN},
123 {"DDI_NT_FD", DDI_NT_FD},
124 {"DDI_NT_ENCLOSURE", DDI_NT_ENCLOSURE},
125 {"DDI_NT_SCSI_ENCLOSURE", DDI_NT_SCSI_ENCLOSURE},
126 {"DDI_NT_TAPE", DDI_NT_TAPE},
127 {"DDI_NT_NET", DDI_NT_NET},
128 {"DDI_NT_DISPLAY", DDI_NT_DISPLAY},
129 {"DDI_PSEUDO", DDI_PSEUDO},
130 {"DDI_NT_AUDIO", DDI_NT_AUDIO},
131 {"DDI_NT_MOUSE", DDI_NT_MOUSE},
132 {"DDI_NT_KEYBOARD", DDI_NT_KEYBOARD},
133 {"DDI_NT_PARALLEL", DDI_NT_PARALLEL},
134 {"DDI_NT_PRINTER", DDI_NT_PRINTER},
135 {"DDI_NT_UGEN", DDI_NT_UGEN},
136 {"DDI_NT_NEXUS", DDI_NT_NEXUS},
137 {"DDI_NT_SCSI_NEXUS", DDI_NT_SCSI_NEXUS},
138 {"DDI_NT_ATTACHMENT_POINT", DDI_NT_ATTACHMENT_POINT},
139 {"DDI_NT_SCSI_ATTACHMENT_POINT", DDI_NT_SCSI_ATTACHMENT_POINT},
140 {"DDI_NT_PCI_ATTACHMENT_POINT", DDI_NT_PCI_ATTACHMENT_POINT},
141 {"DDI_NT_SBD_ATTACHMENT_POINT", DDI_NT_SBD_ATTACHMENT_POINT},
142 {"DDI_NT_FC_ATTACHMENT_POINT", DDI_NT_FC_ATTACHMENT_POINT},
143 {"DDI_NT_USB_ATTACHMENT_POINT", DDI_NT_USB_ATTACHMENT_POINT},
144 {"DDI_NT_BLOCK_FABRIC", DDI_NT_BLOCK_FABRIC},
145 {"DDI_NT_AV_ASYNC", DDI_NT_AV_ASYNC},
146 {"DDI_NT_AV_ISOCH", DDI_NT_AV_ISOCH},
147 { NULL, NULL }
148 };
149
150 /* Node name */
151 static char *pshot_compat_diskname = "cdisk";
152
153 /* Compatible names... */
154 static char *pshot_compat_psramdisks[] = {
155 "psramhead",
156 "psramrom",
157 "psramdisk",
158 "psramd",
159 "psramwhat"
160 };
161
162 /*
163 * devices "natively" supported by pshot (i.e. included with SUNWiotu)
164 * used to initialize pshot_devices with
165 */
166 static pshot_device_t pshot_stock_devices[] = {
167 {"disk", DDI_NT_BLOCK, "gen_drv"},
168 {"disk_chan", DDI_NT_BLOCK_CHAN, "gen_drv"},
169 {"disk_wwn", DDI_NT_BLOCK_WWN, "gen_drv"},
170 {"disk_cdrom", DDI_NT_CD, "gen_drv"},
171 {"disk_cdrom.chan", DDI_NT_CD_CHAN, "gen_drv"},
172 /* Note: use bad_drv to force attach errors */
173 {"disk_fd", DDI_NT_FD, "bad_drv"},
174 {"tape", DDI_NT_TAPE, "gen_drv"},
175 {"net", DDI_NT_NET, "gen_drv"},
176 {"display", DDI_NT_DISPLAY, "gen_drv"},
177 {"pseudo", DDI_PSEUDO, "gen_drv"},
178 {"audio", DDI_NT_AUDIO, "gen_drv"},
179 {"mouse", DDI_NT_MOUSE, "gen_drv"},
180 {"keyboard", DDI_NT_KEYBOARD, "gen_drv"},
181 {"nexus", DDI_NT_NEXUS, "pshot"}
182 };
183 #define PSHOT_N_STOCK_DEVICES \
184 (sizeof (pshot_stock_devices) / sizeof (pshot_device_t))
185
186 static pshot_device_t *pshot_devices = NULL;
187 static size_t pshot_devices_len = 0;
188
189 /* protects <pshot_devices>, <pshot_devices_len> */
190 static kmutex_t pshot_devices_lock;
191
192
193 /*
194 * event testing
195 */
196
197 static ndi_event_definition_t pshot_ndi_event_defs[] = {
198 { PSHOT_EVENT_TAG_OFFLINE, PSHOT_EVENT_NAME_DEV_OFFLINE,
199 EPL_INTERRUPT, NDI_EVENT_POST_TO_ALL },
200
201 { PSHOT_EVENT_TAG_DEV_RESET, PSHOT_EVENT_NAME_DEV_RESET,
202 EPL_INTERRUPT, NDI_EVENT_POST_TO_TGT },
203
204 { PSHOT_EVENT_TAG_BUS_RESET, PSHOT_EVENT_NAME_BUS_RESET,
205 EPL_INTERRUPT, NDI_EVENT_POST_TO_ALL },
206
207 { PSHOT_EVENT_TAG_BUS_QUIESCE, PSHOT_EVENT_NAME_BUS_QUIESCE,
208 EPL_INTERRUPT, NDI_EVENT_POST_TO_ALL },
209
210 { PSHOT_EVENT_TAG_BUS_UNQUIESCE, PSHOT_EVENT_NAME_BUS_UNQUIESCE,
211 EPL_INTERRUPT, NDI_EVENT_POST_TO_ALL },
212
213 { PSHOT_EVENT_TAG_TEST_POST, PSHOT_EVENT_NAME_BUS_TEST_POST,
214 EPL_INTERRUPT, NDI_EVENT_POST_TO_TGT }
215 };
216
217
218 #define PSHOT_N_NDI_EVENTS \
219 (sizeof (pshot_ndi_event_defs) / sizeof (ndi_event_definition_t))
220
221 #ifdef DEBUG
222
223 static ndi_event_definition_t pshot_test_events[] = {
224 { 10, "test event 0", EPL_INTERRUPT, NDI_EVENT_POST_TO_ALL },
225 { 11, "test event 1", EPL_KERNEL, NDI_EVENT_POST_TO_TGT },
226 { 12, "test event 2", EPL_INTERRUPT, NDI_EVENT_POST_TO_TGT },
227 { 13, "test event 3", EPL_INTERRUPT, NDI_EVENT_POST_TO_ALL },
228 { 14, "test event 4", EPL_KERNEL, NDI_EVENT_POST_TO_ALL},
229 { 15, "test event 5", EPL_INTERRUPT, NDI_EVENT_POST_TO_ALL },
230 { 16, "test event 6", EPL_KERNEL, NDI_EVENT_POST_TO_ALL },
231 { 17, "test event 7", EPL_INTERRUPT, NDI_EVENT_POST_TO_ALL }
232 };
233
234 static ndi_event_definition_t pshot_test_events_high[] = {
235 { 20, "test event high 0", EPL_HIGHLEVEL, NDI_EVENT_POST_TO_ALL}
236 };
237
238 #define PSHOT_N_TEST_EVENTS \
239 (sizeof (pshot_test_events)/sizeof (ndi_event_definition_t))
240 #endif
241
242 struct register_events {
243 char *event_name;
244 ddi_eventcookie_t event_cookie;
245 void (*event_callback)
246 (dev_info_t *,
247 ddi_eventcookie_t,
248 void *arg,
249 void *impldata);
250 ddi_callback_id_t cb_id;
251 };
252
253 struct register_events pshot_register_events[] = {
254 { PSHOT_EVENT_NAME_DEV_OFFLINE, 0, pshot_event_cb, 0 },
255 { PSHOT_EVENT_NAME_DEV_RESET, 0, pshot_event_cb, 0 },
256 { PSHOT_EVENT_NAME_BUS_RESET, 0, pshot_event_cb, 0 },
257 { PSHOT_EVENT_NAME_BUS_QUIESCE, 0, pshot_event_cb, 0 },
258 { PSHOT_EVENT_NAME_BUS_UNQUIESCE, 0, pshot_event_cb, 0 },
259 { PSHOT_EVENT_NAME_BUS_TEST_POST, 0, pshot_event_cb, 0 }
260 };
261
262 #define PSHOT_N_DDI_EVENTS \
263 (sizeof (pshot_register_events) / sizeof (struct register_events))
264
265
266 #ifdef DEBUG
267
268 static struct register_events pshot_register_test[] = {
269 { "test event 0", 0, pshot_event_cb_test, 0},
270 { "test event 1", 0, pshot_event_cb_test, 0},
271 { "test event 2", 0, pshot_event_cb_test, 0},
272 { "test event 3", 0, pshot_event_cb_test, 0},
273 { "test event 4", 0, pshot_event_cb_test, 0},
274 { "test event 5", 0, pshot_event_cb_test, 0},
275 { "test event 6", 0, pshot_event_cb_test, 0},
276 { "test event 7", 0, pshot_event_cb_test, 0}
277 };
278
279
280 static struct register_events pshot_register_high_test[] = {
281 {"test event high 0", 0, pshot_event_cb_test, 0}
282 };
283
284 #endif /* DEBUG */
285
286 static struct {
287 int ioctl_int;
288 char *ioctl_char;
289 } pshot_devctls[] = {
290 {DEVCTL_DEVICE_GETSTATE, "DEVCTL_DEVICE_GETSTATE"},
291 {DEVCTL_DEVICE_ONLINE, "DEVCTL_DEVICE_ONLINE"},
292 {DEVCTL_DEVICE_OFFLINE, "DEVCTL_DEVICE_OFFLINE"},
293 {DEVCTL_DEVICE_REMOVE, "DEVCTL_DEVICE_REMOVE"},
294 {DEVCTL_BUS_GETSTATE, "DEVCTL_BUS_GETSTATE"},
295 {DEVCTL_BUS_DEV_CREATE, "DEVCTL_BUS_DEV_CREATE"},
296 {DEVCTL_BUS_RESET, "DEVCTL_BUS_RESET"},
297 {DEVCTL_BUS_RESETALL, "DEVCTL_BUS_RESETALL"},
298 {0, NULL}
299 };
300
301 static struct bus_ops pshot_bus_ops = {
302 BUSO_REV, /* busops_rev */
303 nullbusmap, /* bus_map */
304 NULL, /* bus_get_intrspec */
305 NULL, /* bus_add_interspec */
306 NULL, /* bus_remove_interspec */
307 i_ddi_map_fault, /* bus_map_fault */
308 ddi_dma_map, /* bus_dma_map */
309 ddi_dma_allochdl, /* bus_dma_allochdl */
310 ddi_dma_freehdl, /* bus_dma_freehdl */
311 ddi_dma_bindhdl, /* bus_dma_bindhdl */
312 ddi_dma_unbindhdl, /* bus_dma_unbindhdl */
313 ddi_dma_flush, /* bus_dma_flush */
314 ddi_dma_win, /* bus_dma_win */
315 ddi_dma_mctl, /* bus_dma_ctl */
316 pshot_ctl, /* bus_ctl */
317 ddi_bus_prop_op, /* bus_prop_op */
318 pshot_get_eventcookie, /* bus_get_eventcookie */
319 pshot_add_eventcall, /* bus_add_eventcall */
320 pshot_remove_eventcall, /* bus_remove_event */
321 pshot_post_event, /* bus_post_event */
322 NULL, /* bus_intr_ctl */
323 pshot_bus_config, /* bus_config */
324 pshot_bus_unconfig, /* bus_unconfig */
325 NULL, /* bus_fm_init */
326 NULL, /* bus_fm_fini */
327 NULL, /* bus_fm_access_enter */
328 NULL, /* bus_fm_access_exit */
329 pshot_bus_power, /* bus_power */
330 pshot_bus_introp /* bus_intr_op */
331 };
332
333 static struct cb_ops pshot_cb_ops = {
334 pshot_open, /* open */
335 pshot_close, /* close */
336 nodev, /* strategy */
337 nodev, /* print */
338 nodev, /* dump */
339 nodev, /* read */
340 nodev, /* write */
341 pshot_ioctl, /* ioctl */
342 nodev, /* devmap */
343 nodev, /* mmap */
344 nodev, /* segmap */
345 nochpoll, /* poll */
346 ddi_prop_op, /* prop_op */
347 NULL, /* streamtab */
348 D_NEW | D_MP | D_HOTPLUG, /* flags */
349 CB_REV, /* cb_rev */
350 nodev, /* aread */
351 nodev, /* awrite */
352 };
353
354 static struct dev_ops pshot_ops = {
355 DEVO_REV, /* devo_rev, */
356 0, /* refcnt */
357 pshot_info, /* getinfo */
358 nulldev, /* identify */
359 pshot_probe, /* probe */
360 pshot_attach, /* attach */
361 pshot_detach, /* detach */
362 nodev, /* reset */
363 &pshot_cb_ops, /* driver operations */
364 &pshot_bus_ops, /* bus operations */
365 pshot_power, /* power */
366 ddi_quiesce_not_supported, /* devo_quiesce */
367
368 };
369
370
371 /*
372 * Module linkage information for the kernel.
373 */
374 static struct modldrv modldrv = {
375 &mod_driverops,
376 "pshotnex",
377 &pshot_ops,
378 };
379
380 static struct modlinkage modlinkage = {
381 MODREV_1, &modldrv, NULL
382 };
383
384
385 /*
386 * pshot_devices is set up on the first attach and destroyed on fini
387 *
388 * therefore PSHOT_PROP_DEV* properties may be set just for the root device,
389 * instead of being set globably, in pshot.conf by specifying the properties
390 * on a single line in the form:
391 * name="pshot" parent="/" <dev props ..>
392 * to unclutter a device tree snapshot.
393 * this of course produces a long single line that may wrap around several
394 * times on screen
395 */
396
397 int
_init(void)398 _init(void)
399 {
400 int rv;
401
402 rv = ddi_soft_state_init(&pshot_softstatep, sizeof (pshot_t), 0);
403
404 if (rv != DDI_SUCCESS)
405 return (rv);
406
407 mutex_init(&pshot_devices_lock, NULL, MUTEX_DRIVER, NULL);
408 pshot_devices = NULL;
409 pshot_devices_len = 0;
410
411 if ((rv = mod_install(&modlinkage)) != 0) {
412 ddi_soft_state_fini(&pshot_softstatep);
413 mutex_destroy(&pshot_devices_lock);
414 }
415 return (rv);
416 }
417
418 int
_fini(void)419 _fini(void)
420 {
421 int rv;
422
423 if ((rv = mod_remove(&modlinkage)) != 0)
424 return (rv);
425
426 ddi_soft_state_fini(&pshot_softstatep);
427 mutex_destroy(&pshot_devices_lock);
428 if (pshot_devices)
429 pshot_devices_free(pshot_devices, pshot_devices_len);
430 return (0);
431 }
432
433 int
_info(struct modinfo * modinfop)434 _info(struct modinfo *modinfop)
435 {
436 return (mod_info(&modlinkage, modinfop));
437 }
438
439
440 /*ARGSUSED*/
441 static int
pshot_probe(dev_info_t * devi)442 pshot_probe(dev_info_t *devi)
443 {
444 int instance = ddi_get_instance(devi);
445 char *bus_addr;
446
447 /*
448 * Hook for tests to force probe fail
449 */
450 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, devi, 0, "bus-addr",
451 &bus_addr) == DDI_PROP_SUCCESS) {
452 if (strncmp(bus_addr, "failprobe", 9) == 0) {
453 if (pshot_debug)
454 cmn_err(CE_CONT, "pshot%d: "
455 "%s forced probe failure\n",
456 instance, bus_addr);
457 ddi_prop_free(bus_addr);
458 return (DDI_PROBE_FAILURE);
459 }
460 ddi_prop_free(bus_addr);
461 }
462
463 return (DDI_PROBE_SUCCESS);
464 }
465
466
467 /*ARGSUSED*/
468 static int
pshot_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)469 pshot_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
470 {
471 int instance;
472 minor_t minor;
473 pshot_t *pshot;
474
475 minor = getminor((dev_t)arg);
476 instance = pshot_minor_decode_inst(minor);
477 switch (infocmd) {
478 case DDI_INFO_DEVT2DEVINFO:
479 pshot = ddi_get_soft_state(pshot_softstatep, instance);
480 if (pshot == NULL) {
481 cmn_err(CE_WARN, "pshot_info: get soft state failed "
482 "on minor %u, instance %d", minor, instance);
483 return (DDI_FAILURE);
484 }
485 *result = (void *)pshot->dip;
486 break;
487 case DDI_INFO_DEVT2INSTANCE:
488 *result = (void *)(uintptr_t)instance;
489 break;
490 default:
491 cmn_err(CE_WARN, "pshot_info: unrecognized cmd 0x%x on "
492 "minor %u, instance %d", infocmd, minor, instance);
493 return (DDI_FAILURE);
494 }
495
496 return (DDI_SUCCESS);
497 }
498
499
500 static int
pshot_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)501 pshot_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
502 {
503 int instance = ddi_get_instance(devi);
504 pshot_t *pshot;
505 int rval, i;
506 int prop_flags = DDI_PROP_DONTPASS | DDI_PROP_NOTPROM;
507 char *bus_addr;
508 char *pm_comp[] = {
509 "NAME=bus",
510 "0=B3",
511 "1=B2",
512 "2=B1",
513 "3=B0"};
514 char *pm_hw_state = {"needs-suspend-resume"};
515
516 pshot_prop_autoattach = ddi_prop_get_int(DDI_DEV_T_ANY, devi,
517 prop_flags, "autoattach", 0);
518
519 switch (cmd) {
520
521 case DDI_ATTACH:
522 if (pshot_debug)
523 cmn_err(CE_CONT, "attach: %s%d/pshot%d\n",
524 ddi_get_name(ddi_get_parent(devi)),
525 ddi_get_instance(ddi_get_parent(devi)),
526 instance);
527
528 /*
529 * Hook for tests to force attach fail
530 */
531 if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, devi, 0, "bus-addr",
532 &bus_addr) == DDI_PROP_SUCCESS) && bus_addr != NULL) {
533 if (strncmp(bus_addr, "failattach", 10) == 0) {
534 if (pshot_debug)
535 cmn_err(CE_CONT, "pshot%d: "
536 "%s forced attach failure\n",
537 instance, bus_addr);
538 ddi_prop_free(bus_addr);
539 return (DDI_FAILURE);
540 }
541 ddi_prop_free(bus_addr);
542 }
543
544 /*
545 * minor nodes setup
546 */
547 if (ddi_soft_state_zalloc(pshot_softstatep, instance) !=
548 DDI_SUCCESS) {
549 return (DDI_FAILURE);
550 }
551 pshot = ddi_get_soft_state(pshot_softstatep, instance);
552 pshot->dip = devi;
553 pshot->instance = instance;
554 mutex_init(&pshot->lock, NULL, MUTEX_DRIVER, NULL);
555
556 /* set each minor, then create on dip all together */
557
558 i = PSHOT_NODENUM_DEVCTL;
559 pshot->nodes[i].pshot = pshot;
560 pshot->nodes[i].minor = pshot_minor_encode(instance, i);
561 (void) strncpy(pshot->nodes[i].name, PSHOT_NODENAME_DEVCTL,
562 PSHOT_MAX_MINOR_NAMELEN);
563
564 i = PSHOT_NODENUM_TESTCTL;
565 pshot->nodes[i].pshot = pshot;
566 pshot->nodes[i].minor = pshot_minor_encode(instance, i);
567 (void) strncpy(pshot->nodes[i].name, PSHOT_NODENAME_TESTCTL,
568 PSHOT_MAX_MINOR_NAMELEN);
569
570 /* this assumes contiguous a filling */
571 for (i = 0; i <= PSHOT_MAX_NODENUM; i++) {
572 if (ddi_create_minor_node(devi, pshot->nodes[i].name,
573 S_IFCHR, pshot->nodes[i].minor, DDI_NT_NEXUS, 0) !=
574 DDI_SUCCESS) {
575 cmn_err(CE_WARN, "attach: cannot create "
576 "minor %s", pshot->nodes[i].name);
577 goto FAIL_ATTACH;
578 }
579 }
580
581 /*
582 * pshot_devices setup
583 */
584 if (pshot_devices_setup(devi)) {
585 cmn_err(CE_WARN, "attach: pshot devices setup "
586 "failed");
587 goto FAIL_ATTACH;
588 }
589
590 /*
591 * events setup
592 */
593 for (i = 0; i < PSHOT_N_DDI_EVENTS; i++) {
594 rval = ddi_get_eventcookie(devi,
595 pshot_register_events[i].event_name,
596 &pshot_register_events[i].event_cookie);
597
598 if (pshot_debug)
599 cmn_err(CE_CONT, "pshot%d: event=%s:"
600 "ddi_get_eventcookie rval=%d\n",
601 instance,
602 pshot_register_events[i].event_name, rval);
603
604 if (rval == DDI_SUCCESS) {
605 rval = ddi_add_event_handler(devi,
606 pshot_register_events[i].event_cookie,
607 pshot_register_events[i].event_callback,
608 (void *)pshot,
609 &pshot->callback_cache[i]);
610
611 if (pshot_debug)
612 cmn_err(CE_CONT, "pshot%d: event=%s: "
613 "ddi_add_event_handler rval=%d\n",
614 instance,
615 pshot_register_events[i].event_name,
616 rval);
617 }
618 }
619
620 #ifdef DEBUG
621 if (pshot_event_test_enable) {
622 pshot_event_test((void *)pshot);
623 (void) timeout(pshot_event_test_post_one, (void *)pshot,
624 instance * drv_usectohz(60000000));
625 }
626 #endif
627
628 /*
629 * allocate an ndi event handle
630 */
631 if (ndi_event_alloc_hdl(devi, NULL, &pshot->ndi_event_hdl,
632 NDI_SLEEP) != NDI_SUCCESS) {
633 goto FAIL_ATTACH;
634 }
635
636 pshot->ndi_events.ndi_events_version = NDI_EVENTS_REV1;
637 pshot->ndi_events.ndi_n_events = PSHOT_N_NDI_EVENTS;
638 pshot->ndi_events.ndi_event_defs = pshot_ndi_event_defs;
639
640 if (ndi_event_bind_set(pshot->ndi_event_hdl, &pshot->ndi_events,
641 NDI_SLEEP) != NDI_SUCCESS) {
642 cmn_err(CE_CONT, "pshot%d bind set failed\n",
643 instance);
644 }
645
646 /*
647 * setup a test for nexus auto-attach iff we are
648 * a second level pshot node (parent == /SUNW,pshot)
649 * enable by setting "autoattach=1" in pshot.conf
650 */
651 if ((PARENT_IS_PSHOT(devi)) && (pshot_prop_autoattach != 0) &&
652 (ddi_get_instance(ddi_get_parent(devi))) == 0)
653 pshot_setup_autoattach(devi);
654
655 /*
656 * initialize internal state to idle: busy = 0,
657 * power level = -1
658 */
659 mutex_enter(&pshot->lock);
660 pshot->busy = 0;
661 pshot->busy_ioctl = 0;
662 pshot->level = -1;
663 pshot->state &= ~STRICT_PARENT;
664 pshot->state |= PM_SUPPORTED;
665 mutex_exit(&pshot->lock);
666
667 /*
668 * Create the "pm-want-child-notification?" property
669 * for the root node /devices/pshot
670 */
671 if (instance == 0) {
672 if (pshot_debug) {
673 cmn_err(CE_CONT, "pshot%d: DDI_ATTACH:\n\t"
674 " create the"
675 " \"pm-want-child-notification?\" property"
676 " for the root node\n", instance);
677 }
678 if (ddi_prop_create(DDI_DEV_T_NONE, devi, 0,
679 "pm-want-child-notification?", NULL, 0)
680 != DDI_PROP_SUCCESS) {
681 cmn_err(CE_WARN, "%s%d:\n\t"
682 " unable to create the"
683 " \"pm-want-child-notification?\""
684 " property", ddi_get_name(devi),
685 ddi_get_instance(devi));
686
687 goto FAIL_ATTACH;
688 }
689 }
690
691 /*
692 * Check if the pm-want-child-notification? property was
693 * created in pshot_bus_config_setup_nexus() by the parent.
694 * Set the STRICT_PARENT flag if not.
695 */
696 if (ddi_prop_exists(DDI_DEV_T_ANY, devi,
697 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
698 "pm-want-child-notification?") != 1) {
699 if (pshot_debug) {
700 cmn_err(CE_CONT, "pshot%d: DDI_ATTACH:"
701 " STRICT PARENT\n", instance);
702 }
703 mutex_enter(&pshot->lock);
704 pshot->state |= STRICT_PARENT;
705 mutex_exit(&pshot->lock);
706 } else {
707 if (pshot_debug) {
708 cmn_err(CE_CONT, "pshot%d: DDI_ATTACH:"
709 " INVOLVED PARENT\n", instance);
710 }
711 mutex_enter(&pshot->lock);
712 pshot->state &= ~STRICT_PARENT;
713 mutex_exit(&pshot->lock);
714 }
715
716 /*
717 * create the pm-components property: one component
718 * with 4 power levels.
719 * - skip for pshot@XXX,nopm and pshot@XXX,nopm_strict:
720 * "no-pm-components" property
721 */
722 if (ddi_prop_exists(DDI_DEV_T_ANY, devi,
723 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
724 "no-pm-components") == 0) {
725 if (pshot_debug) {
726 cmn_err(CE_CONT, "pshot%d: DDI_ATTACH:"
727 " create the \"pm_components\" property\n",
728 instance);
729 }
730 if (ddi_prop_update_string_array(DDI_DEV_T_NONE, devi,
731 "pm-components", pm_comp, 5) != DDI_PROP_SUCCESS) {
732 cmn_err(CE_WARN, "%s%d: DDI_ATTACH:\n\t"
733 " unable to create the \"pm-components\""
734 " property", ddi_get_name(devi),
735 ddi_get_instance(devi));
736
737 goto FAIL_ATTACH;
738 }
739 } else {
740 if (pshot_debug) {
741 cmn_err(CE_CONT, "pshot%d: DDI_ATTACH:"
742 " NO-PM_COMPONENTS PARENT\n", instance);
743 }
744 mutex_enter(&pshot->lock);
745 pshot->state &= ~PM_SUPPORTED;
746 mutex_exit(&pshot->lock);
747 }
748
749 /*
750 * create the property needed to get DDI_SUSPEND
751 * and DDI_RESUME calls
752 */
753 if (pshot_debug) {
754 cmn_err(CE_CONT, "pshot%d: DDI_ATTACH:"
755 " create pm-hardware-state property\n",
756 instance);
757 }
758 if (ddi_prop_update_string(DDI_DEV_T_NONE, devi,
759 "pm-hardware-state", pm_hw_state) != DDI_PROP_SUCCESS) {
760 cmn_err(CE_WARN, "%s%d: DDI_ATTACH:\n\t"
761 " unable to create the \"pm-hardware-state\""
762 " property", ddi_get_name(devi),
763 ddi_get_instance(devi));
764
765 goto FAIL_ATTACH;
766 }
767
768 /*
769 * set power level to max via pm_raise_power(),
770 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm)
771 */
772 if (pshot->state & PM_SUPPORTED) {
773 if (pshot_debug) {
774 cmn_err(CE_CONT, "pshot%d: DDI_ATTACH:"
775 " raise power to MAXPWR\n", instance);
776 }
777 if (pm_raise_power(pshot->dip, 0, MAXPWR) !=
778 DDI_SUCCESS) {
779 cmn_err(CE_WARN, "%s%d: DDI_ATTACH:"
780 " pm_raise_power failed",
781 ddi_get_name(devi),
782 ddi_get_instance(devi));
783
784 goto FAIL_ATTACH;
785
786 }
787 }
788
789 if (pshot_log)
790 cmn_err(CE_CONT, "pshot%d attached\n", instance);
791 ddi_report_dev(devi);
792
793 return (DDI_SUCCESS);
794 /*NOTREACHED*/
795 FAIL_ATTACH:
796 ddi_remove_minor_node(devi, NULL);
797 mutex_destroy(&pshot->lock);
798 ddi_soft_state_free(pshot_softstatep, instance);
799 return (DDI_FAILURE);
800
801 case DDI_RESUME:
802 if (pshot_debug) {
803 cmn_err(CE_CONT, "pshot%d: DDI_RESUME: resuming\n",
804 instance);
805 }
806 pshot = ddi_get_soft_state(pshot_softstatep, instance);
807
808 /*
809 * set power level to max via pm_raise_power(),
810 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm)
811 */
812 if (pshot->state & PM_SUPPORTED) {
813 if (pshot_debug) {
814 cmn_err(CE_CONT, "pshot%d: DDI_RESUME:"
815 " raise power to MAXPWR\n", instance);
816 }
817 if (pm_raise_power(pshot->dip, 0, MAXPWR) !=
818 DDI_SUCCESS) {
819 cmn_err(CE_WARN, "%s%d: DDI_RESUME:"
820 " pm_raise_power failed",
821 ddi_get_name(devi),
822 ddi_get_instance(devi));
823 }
824 }
825
826 if (pshot_debug) {
827 cmn_err(CE_CONT, "pshot%d: DDI_RESUME: resumed\n",
828 instance);
829 }
830 return (DDI_SUCCESS);
831
832 default:
833 return (DDI_FAILURE);
834 }
835 }
836
837 static int
pshot_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)838 pshot_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
839 {
840 int instance = ddi_get_instance(devi);
841 int i, rval;
842 pshot_t *pshot = ddi_get_soft_state(pshot_softstatep, instance);
843 int level_tmp;
844
845 if (pshot == NULL)
846 return (DDI_FAILURE);
847
848 switch (cmd) {
849
850 case DDI_DETACH:
851 if (pshot_debug)
852 cmn_err(CE_CONT, "pshot%d: DDI_DETACH\n", instance);
853 /*
854 * power off component 0
855 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm)
856 */
857 if (pshot->state & PM_SUPPORTED) {
858 if (pshot_debug) {
859 cmn_err(CE_CONT, "pshot%d: DDI_DETACH:"
860 " power off\n", instance);
861 }
862 if (pm_lower_power(pshot->dip, 0, 0) != DDI_SUCCESS) {
863 cmn_err(CE_WARN, "%s%d: DDI_DETACH:\n\t"
864 "pm_lower_power failed for comp 0 to"
865 " level 0", ddi_get_name(devi),
866 ddi_get_instance(devi));
867
868 return (DDI_FAILURE);
869 }
870
871 /*
872 * Check if the power level is actually OFF.
873 * Issue pm_power_has_changed if not.
874 */
875 mutex_enter(&pshot->lock);
876 if (pshot->level != 0) {
877 if (pshot_debug) {
878 cmn_err(CE_NOTE, "pshot%d:"
879 " DDI_DETACH: power off via"
880 " pm_power_has_changed instead\n",
881 instance);
882 }
883 level_tmp = pshot->level;
884 pshot->level = 0;
885 if (pm_power_has_changed(pshot->dip, 0, 0) !=
886 DDI_SUCCESS) {
887 if (pshot_debug) {
888 cmn_err(CE_NOTE, "pshot%d:"
889 " DDI_DETACH:"
890 " pm_power_has_changed"
891 " failed\n", instance);
892 }
893 pshot->level = level_tmp;
894 mutex_exit(&pshot->lock);
895
896 return (DDI_FAILURE);
897 }
898 }
899 mutex_exit(&pshot->lock);
900 }
901
902 for (i = 0; i < PSHOT_N_DDI_EVENTS; i++) {
903 if (pshot->callback_cache[i] != NULL) {
904 rval = ddi_remove_event_handler(
905 pshot->callback_cache[i]);
906 ASSERT(rval == DDI_SUCCESS);
907 }
908 }
909
910 #ifdef DEBUG
911 for (i = 0; i < PSHOT_N_TEST_EVENTS; i++) {
912 if (pshot->test_callback_cache[i] != NULL) {
913 rval = ddi_remove_event_handler(
914 pshot->test_callback_cache[i]);
915 ASSERT(rval == DDI_SUCCESS);
916 }
917 }
918 #endif
919 rval = ndi_event_free_hdl(pshot->ndi_event_hdl);
920 ASSERT(rval == DDI_SUCCESS);
921
922 if (pshot_log)
923 cmn_err(CE_CONT, "pshot%d detached\n", instance);
924
925 ddi_remove_minor_node(devi, NULL);
926 mutex_destroy(&pshot->lock);
927 ddi_soft_state_free(pshot_softstatep, instance);
928 break;
929
930 case DDI_SUSPEND:
931 if (pshot_debug)
932 cmn_err(CE_CONT, "pshot%d: DDI_SUSPEND\n", instance);
933 /*
934 * fail the suspend if FAIL_SUSPEND_FLAG is set.
935 * clear the FAIL_SUSPEND_FLAG flag
936 */
937 mutex_enter(&pshot->lock);
938 if (pshot->state & FAIL_SUSPEND_FLAG) {
939 if (pshot_debug) {
940 cmn_err(CE_CONT, "pshot%d:"
941 " FAIL_SUSPEND_FLAG set, fail suspend\n",
942 ddi_get_instance(devi));
943 }
944 pshot->state &= ~FAIL_SUSPEND_FLAG;
945 rval = DDI_FAILURE;
946 } else {
947 rval = DDI_SUCCESS;
948 }
949 mutex_exit(&pshot->lock);
950
951 /*
952 * power OFF via pm_power_has_changed
953 */
954 mutex_enter(&pshot->lock);
955 if (pshot->state & PM_SUPPORTED) {
956 if (pshot_debug) {
957 cmn_err(CE_CONT, "pshot%d: DDI_SUSPEND:"
958 " power off via pm_power_has_changed\n",
959 instance);
960 }
961 level_tmp = pshot->level;
962 pshot->level = 0;
963 if (pm_power_has_changed(pshot->dip, 0, 0) !=
964 DDI_SUCCESS) {
965 if (pshot_debug) {
966 cmn_err(CE_NOTE, "pshot%d:"
967 " DDI_SUSPEND:"
968 " pm_power_has_changed failed\n",
969 instance);
970 }
971 pshot->level = level_tmp;
972 rval = DDI_FAILURE;
973 }
974 }
975 mutex_exit(&pshot->lock);
976 return (rval);
977
978 default:
979 break;
980 }
981
982 return (DDI_SUCCESS);
983 }
984
985
986 /*
987 * returns number of bits to represent <val>
988 */
989 static size_t
pshot_numbits(size_t val)990 pshot_numbits(size_t val)
991 {
992 size_t bitcnt;
993
994 if (val == 0)
995 return (0);
996 for (bitcnt = 1; 1 << bitcnt < val; bitcnt++)
997 ;
998 return (bitcnt);
999 }
1000
1001 /*
1002 * returns a minor number encoded with instance <inst> and an index <nodenum>
1003 * that identifies the minor node for this instance
1004 */
1005 static minor_t
pshot_minor_encode(int inst,minor_t nodenum)1006 pshot_minor_encode(int inst, minor_t nodenum)
1007 {
1008 return (((minor_t)inst << PSHOT_NODENUM_BITS()) |
1009 (((1 << PSHOT_NODENUM_BITS()) - 1) & nodenum));
1010 }
1011
1012 /*
1013 * returns instance of <minor>
1014 */
1015 static int
pshot_minor_decode_inst(minor_t minor)1016 pshot_minor_decode_inst(minor_t minor)
1017 {
1018 return (minor >> PSHOT_NODENUM_BITS());
1019 }
1020
1021 /*
1022 * returns node number indexing a minor node for the instance in <minor>
1023 */
1024 static minor_t
pshot_minor_decode_nodenum(minor_t minor)1025 pshot_minor_decode_nodenum(minor_t minor)
1026 {
1027 return (minor & ((1 << PSHOT_NODENUM_BITS()) - 1));
1028 }
1029
1030
1031 /*
1032 * pshot_bus_introp: pshot convert an interrupt number to an
1033 * interrupt. NO OP for pseudo drivers.
1034 */
1035 /*ARGSUSED*/
1036 static int
pshot_bus_introp(dev_info_t * dip,dev_info_t * rdip,ddi_intr_op_t intr_op,ddi_intr_handle_impl_t * hdlp,void * result)1037 pshot_bus_introp(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
1038 ddi_intr_handle_impl_t *hdlp, void *result)
1039 {
1040 return (DDI_FAILURE);
1041 }
1042 static int
pshot_ctl(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)1043 pshot_ctl(dev_info_t *dip, dev_info_t *rdip,
1044 ddi_ctl_enum_t ctlop, void *arg, void *result)
1045 {
1046 int instance;
1047 pshot_t *pshot;
1048 char *childname;
1049 int childinstance;
1050 char *name;
1051 int circ;
1052 struct attachspec *as;
1053 struct detachspec *ds;
1054 int rval = DDI_SUCCESS;
1055 int no_pm_components_child;
1056
1057 name = ddi_get_name(dip);
1058 instance = ddi_get_instance(dip);
1059 pshot = ddi_get_soft_state(pshot_softstatep, instance);
1060 if (pshot == NULL) {
1061 return (ENXIO);
1062 }
1063
1064 switch (ctlop) {
1065 case DDI_CTLOPS_REPORTDEV:
1066 if (rdip == (dev_info_t *)0)
1067 return (DDI_FAILURE);
1068 cmn_err(CE_CONT, "?pshot-device: %s%d\n",
1069 ddi_get_name(rdip), ddi_get_instance(rdip));
1070 return (DDI_SUCCESS);
1071
1072 case DDI_CTLOPS_INITCHILD:
1073 {
1074 dev_info_t *child = (dev_info_t *)arg;
1075
1076 if (pshot_debug) {
1077 cmn_err(CE_CONT, "initchild %s%d/%s%d state 0x%x\n",
1078 ddi_get_name(dip), ddi_get_instance(dip),
1079 ddi_node_name(child), ddi_get_instance(child),
1080 DEVI(child)->devi_state);
1081 }
1082
1083 return (pshot_initchild(dip, child));
1084 }
1085
1086 case DDI_CTLOPS_UNINITCHILD:
1087 {
1088 dev_info_t *child = (dev_info_t *)arg;
1089
1090 if (pshot_debug) {
1091 cmn_err(CE_CONT, "uninitchild %s%d/%s%d state 0x%x\n",
1092 ddi_get_name(dip), ddi_get_instance(dip),
1093 ddi_node_name(child), ddi_get_instance(child),
1094 DEVI(child)->devi_state);
1095 }
1096
1097 return (pshot_uninitchild(dip, child));
1098 }
1099
1100 case DDI_CTLOPS_DMAPMAPC:
1101 case DDI_CTLOPS_REPORTINT:
1102 case DDI_CTLOPS_REGSIZE:
1103 case DDI_CTLOPS_NREGS:
1104 case DDI_CTLOPS_SIDDEV:
1105 case DDI_CTLOPS_SLAVEONLY:
1106 case DDI_CTLOPS_AFFINITY:
1107 case DDI_CTLOPS_POKE:
1108 case DDI_CTLOPS_PEEK:
1109 /*
1110 * These ops correspond to functions that "shouldn't" be called
1111 * by a pseudo driver. So we whine when we're called.
1112 */
1113 cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n",
1114 ddi_get_name(dip), ddi_get_instance(dip),
1115 ctlop, ddi_get_name(rdip), ddi_get_instance(rdip));
1116 return (DDI_FAILURE);
1117
1118 case DDI_CTLOPS_ATTACH:
1119 {
1120 dev_info_t *child = (dev_info_t *)rdip;
1121 childname = ddi_node_name(child);
1122 childinstance = ddi_get_instance(child);
1123 as = (struct attachspec *)arg;
1124
1125 no_pm_components_child = 0;
1126 if (ddi_prop_exists(DDI_DEV_T_ANY, child,
1127 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
1128 "no-pm-components") == 1) {
1129 no_pm_components_child = 1;
1130 }
1131 if (pshot_debug) {
1132 cmn_err(CE_CONT, "%s%d: ctl_attach %s%d [%d]\n",
1133 name, instance, childname, childinstance,
1134 no_pm_components_child);
1135 }
1136
1137 ndi_devi_enter(dip, &circ);
1138
1139 switch (as->when) {
1140 case DDI_PRE:
1141 /*
1142 * Mark nexus busy before a child attaches.
1143 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm
1144 * - pshot@XXX,nopm_strict)
1145 */
1146 if (!(pshot->state & PM_SUPPORTED))
1147 break;
1148 mutex_enter(&pshot->lock);
1149 ++(pshot->busy);
1150 if (pshot_debug_busy) {
1151 cmn_err(CE_CONT, "%s%d:"
1152 " ctl_attach_pre: busy for %s%d:"
1153 " busy = %d\n", name, instance,
1154 childname, childinstance,
1155 pshot->busy);
1156 }
1157 mutex_exit(&pshot->lock);
1158 rval = pm_busy_component(dip, 0);
1159 ASSERT(rval == DDI_SUCCESS);
1160 break;
1161 case DDI_POST:
1162 /*
1163 * Mark nexus idle after a child attaches.
1164 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm).
1165 * - also skip if this is not a stict parent and
1166 * - the child is a tape device or a no-pm-components
1167 * - nexus node.
1168 */
1169 if (!(pshot->state & PM_SUPPORTED) ||
1170 (strcmp(childname, "tape") == 0 &&
1171 !(pshot->state & STRICT_PARENT)) ||
1172 no_pm_components_child)
1173 break;
1174 mutex_enter(&pshot->lock);
1175 ASSERT(pshot->busy > 0);
1176 --pshot->busy;
1177 if (pshot_debug_busy) {
1178 cmn_err(CE_CONT, "%s%d:"
1179 " ctl_attach_post: idle for %s%d:"
1180 " busy = %d\n", name, instance,
1181 childname, childinstance,
1182 pshot->busy);
1183 }
1184 mutex_exit(&pshot->lock);
1185 rval = pm_idle_component(dip, 0);
1186 ASSERT(rval == DDI_SUCCESS);
1187 break;
1188 }
1189
1190 ndi_devi_exit(dip, circ);
1191
1192 return (rval);
1193 }
1194 case DDI_CTLOPS_DETACH:
1195 {
1196 dev_info_t *child = (dev_info_t *)rdip;
1197 childname = ddi_node_name(child);
1198 childinstance = ddi_get_instance(child);
1199 ds = (struct detachspec *)arg;
1200
1201 no_pm_components_child = 0;
1202 if (ddi_prop_exists(DDI_DEV_T_ANY, child,
1203 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
1204 "no-pm-components") == 1) {
1205 no_pm_components_child = 1;
1206 }
1207 if (pshot_debug) {
1208 cmn_err(CE_CONT,
1209 "%s%d: ctl_detach %s%d [%d]\n",
1210 name, instance, childname, childinstance,
1211 no_pm_components_child);
1212 }
1213
1214 ndi_devi_enter(dip, &circ);
1215
1216 switch (ds->when) {
1217 case DDI_PRE:
1218 /*
1219 * Mark nexus busy before a child detaches.
1220 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm
1221 * - pshot@XXX,nopm_strict), or if the child is a
1222 * - no-pm-components nexus node.
1223 */
1224 if (!(pshot->state & PM_SUPPORTED) ||
1225 (strcmp(childname, "tape") == 0 &&
1226 !(pshot->state & STRICT_PARENT)) ||
1227 no_pm_components_child)
1228 break;
1229 mutex_enter(&pshot->lock);
1230 ++(pshot->busy);
1231 if (pshot_debug_busy) {
1232 cmn_err(CE_CONT, "%s%d:"
1233 " ctl_detach_pre: busy for %s%d:"
1234 " busy = %d\n", name, instance,
1235 childname, childinstance,
1236 pshot->busy);
1237 }
1238 mutex_exit(&pshot->lock);
1239 rval = pm_busy_component(dip, 0);
1240 ASSERT(rval == DDI_SUCCESS);
1241
1242 break;
1243 case DDI_POST:
1244 /*
1245 * Mark nexus idle after a child detaches.
1246 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm)
1247 */
1248 if (!(pshot->state & PM_SUPPORTED))
1249 break;
1250 mutex_enter(&pshot->lock);
1251 ASSERT(pshot->busy > 0);
1252 --pshot->busy;
1253 if (pshot_debug_busy) {
1254 cmn_err(CE_CONT, "%s%d:"
1255 " ctl_detach_post: idle for %s%d:"
1256 " busy = %d\n", name, instance,
1257 childname, childinstance,
1258 pshot->busy);
1259 }
1260 mutex_exit(&pshot->lock);
1261 rval = pm_idle_component(dip, 0);
1262 ASSERT(rval == DDI_SUCCESS);
1263
1264 /*
1265 * Mark the driver idle if the NO_INVOL_FLAG
1266 * is set. This is needed to make sure the
1267 * parent is idle after the child detaches
1268 * without calling pm_lower_power().
1269 * Clear the NO_INVOL_FLAG.
1270 * - also mark idle if a tape device has detached
1271 */
1272 if (!(pshot->state & NO_INVOL_FLAG))
1273 break;
1274 mutex_enter(&pshot->lock);
1275 ASSERT(pshot->busy > 0);
1276 --pshot->busy;
1277 if (pshot_debug_busy) {
1278 cmn_err(CE_CONT, "%s%d:"
1279 " ctl_detach_post: NO_INVOL:"
1280 " idle for %s%d: busy = %d\n",
1281 name, instance, childname,
1282 childinstance, pshot->busy);
1283 }
1284 pshot->state &= ~NO_INVOL_FLAG;
1285 mutex_exit(&pshot->lock);
1286 rval = pm_idle_component(dip, 0);
1287 ASSERT(rval == DDI_SUCCESS);
1288
1289 break;
1290 }
1291
1292 ndi_devi_exit(dip, circ);
1293
1294 return (rval);
1295 }
1296
1297 case DDI_CTLOPS_BTOP:
1298 case DDI_CTLOPS_BTOPR:
1299 case DDI_CTLOPS_DVMAPAGESIZE:
1300 case DDI_CTLOPS_IOMIN:
1301 case DDI_CTLOPS_PTOB:
1302 default:
1303 /*
1304 * The ops that we pass up (default). We pass up memory
1305 * allocation oriented ops that we receive - these may be
1306 * associated with pseudo HBA drivers below us with target
1307 * drivers below them that use ddi memory allocation
1308 * interfaces like scsi_alloc_consistent_buf.
1309 */
1310 return (ddi_ctlops(dip, rdip, ctlop, arg, result));
1311 }
1312 }
1313
1314 /*ARGSUSED0*/
1315 static int
pshot_power(dev_info_t * dip,int cmpt,int level)1316 pshot_power(dev_info_t *dip, int cmpt, int level)
1317 {
1318 pshot_t *pshot;
1319 int instance = ddi_get_instance(dip);
1320 char *name = ddi_node_name(dip);
1321 int circ;
1322 int rv;
1323
1324 pshot = ddi_get_soft_state(pshot_softstatep, instance);
1325 if (pshot == NULL) {
1326
1327 return (DDI_FAILURE);
1328 }
1329
1330 ndi_devi_enter(dip, &circ);
1331
1332 /*
1333 * set POWER_FLAG when power() is called.
1334 * ioctl(DEVCT_PM_POWER) is a clear on read call.
1335 */
1336 mutex_enter(&pshot->lock);
1337 pshot->state |= POWER_FLAG;
1338 /*
1339 * refuse to power OFF if the component is busy
1340 */
1341 if (pshot->busy != 0 && pshot->level > level) {
1342 cmn_err(CE_WARN, "%s%d: power: REFUSING POWER LEVEL CHANGE"
1343 " (%d->%d), DEVICE NOT IDLE: busy = %d",
1344 name, instance, pshot->level, level, pshot->busy);
1345 rv = DDI_FAILURE;
1346 } else {
1347 if (pshot_debug) {
1348 cmn_err(CE_CONT, "%s%d: power: comp %d (%d->%d)\n",
1349 name, instance, cmpt, pshot->level, level);
1350 }
1351 pshot->level = level;
1352 rv = DDI_SUCCESS;
1353 }
1354 mutex_exit(&pshot->lock);
1355
1356 ndi_devi_exit(dip, circ);
1357
1358 return (rv);
1359 }
1360
1361 /*ARGSUSED0*/
1362 static int
pshot_bus_power(dev_info_t * dip,void * impl_arg,pm_bus_power_op_t op,void * arg,void * result)1363 pshot_bus_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op,
1364 void *arg, void *result)
1365
1366 {
1367 int ret;
1368 int instance = ddi_get_instance(dip);
1369 char *name = ddi_node_name(dip);
1370 pshot_t *pshot;
1371 pm_bp_child_pwrchg_t *bpc;
1372 pm_bp_nexus_pwrup_t bpn;
1373 pm_bp_has_changed_t *bphc;
1374 int pwrup_res;
1375 int ret_failed = 0;
1376 int pwrup_res_failed = 0;
1377
1378 pshot = ddi_get_soft_state(pshot_softstatep, instance);
1379 if (pshot == NULL) {
1380
1381 return (DDI_FAILURE);
1382 }
1383
1384 switch (op) {
1385 case BUS_POWER_PRE_NOTIFICATION:
1386 bpc = (pm_bp_child_pwrchg_t *)arg;
1387 if (pshot_debug) {
1388 cmn_err(CE_CONT, "%s%d: pre_bus_power:"
1389 " %s%d comp %d (%d->%d)\n",
1390 name, instance, ddi_node_name(bpc->bpc_dip),
1391 ddi_get_instance(bpc->bpc_dip),
1392 bpc->bpc_comp, bpc->bpc_olevel,
1393 bpc->bpc_nlevel);
1394 }
1395
1396 /*
1397 * mark parent busy if old_level is either -1 or 0,
1398 * and new level is == MAXPWR
1399 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm)
1400 */
1401 if ((bpc->bpc_comp == 0 && bpc->bpc_nlevel == MAXPWR &&
1402 bpc->bpc_olevel <= 0) && (pshot->state & PM_SUPPORTED)) {
1403 mutex_enter(&pshot->lock);
1404 ++(pshot->busy);
1405 if (pshot_debug_busy) {
1406 cmn_err(CE_CONT,
1407 "%s%d: pre_bus_power:"
1408 " busy parent for %s%d (%d->%d): "
1409 " busy = %d\n",
1410 name, instance,
1411 ddi_node_name(bpc->bpc_dip),
1412 ddi_get_instance(bpc->bpc_dip),
1413 bpc->bpc_olevel, bpc->bpc_nlevel,
1414 pshot->busy);
1415 }
1416 mutex_exit(&pshot->lock);
1417 ret = pm_busy_component(dip, 0);
1418 ASSERT(ret == DDI_SUCCESS);
1419 }
1420
1421 /*
1422 * if new_level > 0, power up parent, if not already at
1423 * MAXPWR, via pm_busop_bus_power
1424 * - skip for the no-pm nexus (pshot@XXX,nopm)
1425 */
1426 if (bpc->bpc_comp == 0 && bpc->bpc_nlevel > 0 &&
1427 pshot->level < MAXPWR && (pshot->state & PM_SUPPORTED)) {
1428 /*
1429 * stuff the bpn struct
1430 */
1431 bpn.bpn_comp = 0;
1432 bpn.bpn_level = MAXPWR;
1433 bpn.bpn_private = bpc->bpc_private;
1434 bpn.bpn_dip = dip;
1435
1436 /*
1437 * ask pm to power parent up
1438 */
1439 if (pshot_debug) {
1440 cmn_err(CE_CONT, "%s%d: pre_bus_power:"
1441 " pm_busop_bus_power on parent for %s%d"
1442 " (%d->%d): enter", name, instance,
1443 ddi_node_name(bpc->bpc_dip),
1444 ddi_get_instance(bpc->bpc_dip),
1445 bpc->bpc_olevel, bpc->bpc_nlevel);
1446 }
1447 ret = pm_busop_bus_power(dip, impl_arg,
1448 BUS_POWER_NEXUS_PWRUP, (void *)&bpn,
1449 (void *)&pwrup_res);
1450
1451 /*
1452 * check the return status individually,
1453 * idle parent and exit if either failed.
1454 */
1455 if (ret != DDI_SUCCESS) {
1456 cmn_err(CE_WARN,
1457 "%s%d: pre_bus_power:"
1458 " pm_busop_bus_power FAILED (ret) FOR"
1459 " %s%d (%d->%d)",
1460 name, instance,
1461 ddi_node_name(bpc->bpc_dip),
1462 ddi_get_instance(bpc->bpc_dip),
1463 bpc->bpc_olevel, bpc->bpc_nlevel);
1464 ret_failed = 1;
1465 }
1466 if (pwrup_res != DDI_SUCCESS) {
1467 cmn_err(CE_WARN,
1468 "%s%d: pre_bus_power:"
1469 " pm_busop_bus_power FAILED (pwrup_res)"
1470 " FOR %s%d (%d->%d)",
1471 name, instance,
1472 ddi_node_name(bpc->bpc_dip),
1473 ddi_get_instance(bpc->bpc_dip),
1474 bpc->bpc_olevel, bpc->bpc_nlevel);
1475 pwrup_res_failed = 1;
1476 }
1477 if (ret_failed || pwrup_res_failed) {
1478 /*
1479 * decrement the busy count if it
1480 * had been incremented.
1481 */
1482 if ((bpc->bpc_comp == 0 &&
1483 bpc->bpc_nlevel == MAXPWR &&
1484 bpc->bpc_olevel <= 0) &&
1485 (pshot->state & PM_SUPPORTED)) {
1486 mutex_enter(&pshot->lock);
1487 ASSERT(pshot->busy > 0);
1488 --(pshot->busy);
1489 if (pshot_debug_busy) {
1490 cmn_err(CE_CONT, "%s%d:"
1491 " pm_busop_bus_power"
1492 " failed: idle parent for"
1493 " %s%d (%d->%d):"
1494 " busy = %d\n",
1495 name, instance,
1496 ddi_node_name(
1497 bpc->bpc_dip),
1498 ddi_get_instance(
1499 bpc->bpc_dip),
1500 bpc->bpc_olevel,
1501 bpc->bpc_nlevel,
1502 pshot->busy);
1503 }
1504 mutex_exit(&pshot->lock);
1505 ret = pm_idle_component(dip, 0);
1506 ASSERT(ret == DDI_SUCCESS);
1507 }
1508 return (DDI_FAILURE);
1509
1510 } else {
1511 if (pshot_debug) {
1512 cmn_err(CE_CONT,
1513 "%s%d: pre_bus_power:"
1514 " pm_busop_bus_power on parent"
1515 " for %s%d (%d->%d)\n",
1516 name, instance,
1517 ddi_node_name(bpc->bpc_dip),
1518 ddi_get_instance(bpc->bpc_dip),
1519 bpc->bpc_olevel, bpc->bpc_nlevel);
1520 }
1521 }
1522 }
1523 break;
1524
1525 case BUS_POWER_POST_NOTIFICATION:
1526 bpc = (pm_bp_child_pwrchg_t *)arg;
1527 if (pshot_debug) {
1528 cmn_err(CE_CONT, "%s%d: post_bus_power:"
1529 " %s%d comp %d (%d->%d) result %d\n",
1530 name, instance, ddi_node_name(bpc->bpc_dip),
1531 ddi_get_instance(bpc->bpc_dip),
1532 bpc->bpc_comp, bpc->bpc_olevel,
1533 bpc->bpc_nlevel, *(int *)result);
1534 }
1535
1536 /*
1537 * handle pm_busop_bus_power() failure case.
1538 * mark parent idle if had been marked busy.
1539 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm)
1540 */
1541 if (*(int *)result != DDI_SUCCESS) {
1542 cmn_err(CE_WARN,
1543 "pshot%d: post_bus_power_failed:"
1544 " pm_busop_bus_power FAILED FOR %s%d (%d->%d)",
1545 instance, ddi_node_name(bpc->bpc_dip),
1546 ddi_get_instance(bpc->bpc_dip),
1547 bpc->bpc_olevel, bpc->bpc_nlevel);
1548
1549 if ((bpc->bpc_comp == 0 && bpc->bpc_nlevel == MAXPWR &&
1550 bpc->bpc_olevel <= 0) &&
1551 (pshot->state & PM_SUPPORTED)) {
1552 mutex_enter(&pshot->lock);
1553 ASSERT(pshot->busy > 0);
1554 --(pshot->busy);
1555 if (pshot_debug_busy) {
1556 cmn_err(CE_CONT, "%s%d:"
1557 " post_bus_power_failed:"
1558 " idle parent for %s%d"
1559 " (%d->%d): busy = %d\n",
1560 name, instance,
1561 ddi_node_name(bpc->bpc_dip),
1562 ddi_get_instance(bpc->bpc_dip),
1563 bpc->bpc_olevel, bpc->bpc_nlevel,
1564 pshot->busy);
1565 }
1566 mutex_exit(&pshot->lock);
1567 ret = pm_idle_component(dip, 0);
1568 ASSERT(ret == DDI_SUCCESS);
1569 }
1570 }
1571
1572 /*
1573 * Mark nexus idle when a child's comp 0
1574 * is set to level 0 from level 1, 2, or 3 only.
1575 * And only if result arg == DDI_SUCCESS.
1576 * This will leave the parent busy when the child
1577 * does not call pm_lower_power() on detach after
1578 * unsetting the NO_LOWER_POWER flag.
1579 * If so, need to notify the parent to mark itself
1580 * idle anyway, else the no-involumtary-power-cycles
1581 * test cases will report false passes!
1582 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm)
1583 */
1584 if ((bpc->bpc_comp == 0 && bpc->bpc_nlevel == 0 &&
1585 !(bpc->bpc_olevel <= 0) &&
1586 *(int *)result == DDI_SUCCESS) &&
1587 (pshot->state & PM_SUPPORTED)) {
1588 mutex_enter(&pshot->lock);
1589 ASSERT(pshot->busy > 0);
1590 --(pshot->busy);
1591 if (pshot_debug_busy) {
1592 cmn_err(CE_CONT,
1593 "%s%d: post_bus_power:"
1594 " idle parent for %s%d (%d->%d):"
1595 " busy = %d\n", name, instance,
1596 ddi_node_name(bpc->bpc_dip),
1597 ddi_get_instance(bpc->bpc_dip),
1598 bpc->bpc_olevel, bpc->bpc_nlevel,
1599 pshot->busy);
1600 }
1601 mutex_exit(&pshot->lock);
1602 ret = pm_idle_component(dip, 0);
1603 ASSERT(ret == DDI_SUCCESS);
1604 }
1605 break;
1606
1607 case BUS_POWER_HAS_CHANGED:
1608 bphc = (pm_bp_has_changed_t *)arg;
1609 if (pshot_debug) {
1610 cmn_err(CE_CONT, "%s%d: has_changed_bus_power:"
1611 " %s%d comp %d (%d->%d) result %d\n",
1612 name, instance, ddi_node_name(bphc->bphc_dip),
1613 ddi_get_instance(bphc->bphc_dip),
1614 bphc->bphc_comp, bphc->bphc_olevel,
1615 bphc->bphc_nlevel, *(int *)result);
1616 }
1617
1618 /*
1619 * Mark nexus idle when a child's comp 0
1620 * is set to level 0 from levels 1, 2, or 3 only.
1621 *
1622 * If powering up child leaf/nexus nodes via
1623 * pm_power_has_changed() calls, first issue
1624 * DEVCTL_PM_BUSY_COMP ioctl to mark parent busy
1625 * before powering the parent up, then power up the
1626 * child node.
1627 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm)
1628 */
1629 if ((bphc->bphc_comp == 0 && bphc->bphc_nlevel == 0 &&
1630 !(bphc->bphc_olevel <= 0)) &&
1631 pshot->state & PM_SUPPORTED) {
1632 mutex_enter(&pshot->lock);
1633 ASSERT(pshot->busy > 0);
1634 --(pshot->busy);
1635 if (pshot_debug_busy) {
1636 cmn_err(CE_CONT,
1637 "%s%d: has_changed_bus_power:"
1638 " idle parent for %s%d (%d->%d):"
1639 " busy = %d\n", name, instance,
1640 ddi_node_name(bphc->bphc_dip),
1641 ddi_get_instance(bphc->bphc_dip),
1642 bphc->bphc_olevel,
1643 bphc->bphc_nlevel, pshot->busy);
1644 }
1645 mutex_exit(&pshot->lock);
1646 ret = pm_idle_component(dip, 0);
1647 ASSERT(ret == DDI_SUCCESS);
1648 }
1649 break;
1650
1651 default:
1652 return (pm_busop_bus_power(dip, impl_arg, op, arg, result));
1653
1654 }
1655
1656 return (DDI_SUCCESS);
1657 }
1658
1659 static int
pshot_initchild(dev_info_t * dip,dev_info_t * child)1660 pshot_initchild(dev_info_t *dip, dev_info_t *child)
1661 {
1662 char name[64];
1663 char *bus_addr;
1664 char *c_nodename;
1665 int bus_id;
1666 dev_info_t *enum_child;
1667 int enum_base;
1668 int enum_extent;
1669
1670
1671 /* check for bus_enum node */
1672
1673 #ifdef NOT_USED
1674 if (impl_ddi_merge_child(child) != DDI_SUCCESS)
1675 return (DDI_FAILURE);
1676 #endif
1677
1678 enum_base = ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
1679 "busid_ebase", 0);
1680
1681 enum_extent = ddi_prop_get_int(DDI_DEV_T_ANY, child,
1682 DDI_PROP_DONTPASS, "busid_range", 0);
1683
1684 /*
1685 * bus enumeration node
1686 */
1687 if ((enum_base != 0) && (enum_extent != 0)) {
1688 c_nodename = ddi_node_name(child);
1689 bus_id = enum_base;
1690 for (; bus_id < enum_extent; bus_id++) {
1691 if (ndi_devi_alloc(dip, c_nodename, DEVI_PSEUDO_NODEID,
1692 &enum_child) != NDI_SUCCESS)
1693 return (DDI_FAILURE);
1694
1695 (void) sprintf(name, "%d", bus_id);
1696 if (ndi_prop_update_string(DDI_DEV_T_NONE, enum_child,
1697 "bus-addr", name) != DDI_PROP_SUCCESS) {
1698 (void) ndi_devi_free(enum_child);
1699 return (DDI_FAILURE);
1700 }
1701
1702 if (ndi_devi_online(enum_child, 0) !=
1703 DDI_SUCCESS) {
1704 (void) ndi_devi_free(enum_child);
1705 return (DDI_FAILURE);
1706 }
1707 }
1708 /*
1709 * fail the enumeration node itself
1710 */
1711 return (DDI_FAILURE);
1712 }
1713
1714 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child, 0, "bus-addr",
1715 &bus_addr) != DDI_PROP_SUCCESS) {
1716 cmn_err(CE_WARN, "pshot_initchild: bus-addr not defined (%s)",
1717 ddi_node_name(child));
1718 return (DDI_NOT_WELL_FORMED);
1719 }
1720
1721 if (strlen(bus_addr) == 0) {
1722 cmn_err(CE_WARN, "pshot_initchild: NULL bus-addr (%s)",
1723 ddi_node_name(child));
1724 ddi_prop_free(bus_addr);
1725 return (DDI_FAILURE);
1726 }
1727
1728 if (strncmp(bus_addr, "failinit", 8) == 0) {
1729 if (pshot_debug)
1730 cmn_err(CE_CONT,
1731 "pshot%d: %s forced INITCHILD failure\n",
1732 ddi_get_instance(dip), bus_addr);
1733 ddi_prop_free(bus_addr);
1734 return (DDI_FAILURE);
1735 }
1736
1737 if (pshot_log) {
1738 cmn_err(CE_CONT, "initchild %s%d/%s@%s\n",
1739 ddi_get_name(dip), ddi_get_instance(dip),
1740 ddi_node_name(child), bus_addr);
1741 }
1742
1743 ddi_set_name_addr(child, bus_addr);
1744 ddi_prop_free(bus_addr);
1745 return (DDI_SUCCESS);
1746 }
1747
1748 /*ARGSUSED*/
1749 static int
pshot_uninitchild(dev_info_t * dip,dev_info_t * child)1750 pshot_uninitchild(dev_info_t *dip, dev_info_t *child)
1751 {
1752 ddi_set_name_addr(child, NULL);
1753 return (DDI_SUCCESS);
1754 }
1755
1756
1757 /*
1758 * devctl IOCTL support
1759 */
1760 /* ARGSUSED */
1761 static int
pshot_open(dev_t * devp,int flags,int otyp,cred_t * credp)1762 pshot_open(dev_t *devp, int flags, int otyp, cred_t *credp)
1763 {
1764 int instance;
1765 pshot_t *pshot;
1766
1767 if (otyp != OTYP_CHR)
1768 return (EINVAL);
1769
1770 instance = pshot_minor_decode_inst(getminor(*devp));
1771 if ((pshot = ddi_get_soft_state(pshot_softstatep, instance)) == NULL)
1772 return (ENXIO);
1773
1774 /*
1775 * Access is currently determined on a per-instance basis.
1776 * If we want per-node, then need to add state and lock members to
1777 * pshot_minor_t
1778 */
1779 mutex_enter(&pshot->lock);
1780 if (((flags & FEXCL) && (pshot->state & IS_OPEN)) ||
1781 (!(flags & FEXCL) && (pshot->state & IS_OPEN_EXCL))) {
1782 mutex_exit(&pshot->lock);
1783 return (EBUSY);
1784 }
1785 pshot->state |= IS_OPEN;
1786 if (flags & FEXCL)
1787 pshot->state |= IS_OPEN_EXCL;
1788
1789 if (pshot_debug)
1790 cmn_err(CE_CONT, "pshot%d open\n", instance);
1791
1792 mutex_exit(&pshot->lock);
1793 return (0);
1794 }
1795
1796 /*
1797 * pshot_close
1798 */
1799 /* ARGSUSED */
1800 static int
pshot_close(dev_t dev,int flag,int otyp,cred_t * credp)1801 pshot_close(dev_t dev, int flag, int otyp, cred_t *credp)
1802 {
1803 int instance;
1804 pshot_t *pshot;
1805
1806 if (otyp != OTYP_CHR)
1807 return (EINVAL);
1808
1809 instance = pshot_minor_decode_inst(getminor(dev));
1810 if ((pshot = ddi_get_soft_state(pshot_softstatep, instance)) == NULL)
1811 return (ENXIO);
1812
1813 mutex_enter(&pshot->lock);
1814 pshot->state &= ~(IS_OPEN | IS_OPEN_EXCL);
1815 mutex_exit(&pshot->lock);
1816 if (pshot_debug)
1817 cmn_err(CE_CONT, "pshot%d closed\n", instance);
1818 return (0);
1819 }
1820
1821
1822 /*
1823 * pshot_ioctl: redirects to appropriate command handler based on various
1824 * criteria
1825 */
1826 /* ARGSUSED */
1827 static int
pshot_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)1828 pshot_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
1829 int *rvalp)
1830 {
1831 pshot_t *pshot;
1832 int instance;
1833 minor_t nodenum;
1834 char *nodename;
1835
1836 instance = pshot_minor_decode_inst(getminor(dev));
1837 if ((pshot = ddi_get_soft_state(pshot_softstatep, instance)) == NULL)
1838 return (ENXIO);
1839
1840 nodenum = pshot_minor_decode_nodenum(getminor(dev));
1841 nodename = pshot->nodes[nodenum].name;
1842
1843 if (pshot_debug)
1844 cmn_err(CE_CONT,
1845 "pshot%d ioctl: dev=%p, cmd=%x, arg=%p, mode=%x\n",
1846 instance, (void *)dev, cmd, (void *)arg, mode);
1847
1848 if (strcmp(nodename, PSHOT_NODENAME_DEVCTL) == 0)
1849 return (pshot_devctl(pshot, nodenum, cmd, arg, mode, credp,
1850 rvalp));
1851
1852 if (strcmp(nodename, PSHOT_NODENAME_TESTCTL) == 0)
1853 return (pshot_testctl(pshot, nodenum, cmd, arg, mode, credp,
1854 rvalp));
1855
1856 cmn_err(CE_WARN, "pshot_ioctl: unmatched nodename on minor %u",
1857 pshot->nodes[nodenum].minor);
1858 return (ENXIO);
1859 }
1860
1861
1862 /*
1863 * pshot_devctl: handle DEVCTL operations
1864 */
1865 /* ARGSUSED */
1866 static int
pshot_devctl(pshot_t * pshot,minor_t nodenum,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)1867 pshot_devctl(pshot_t *pshot, minor_t nodenum, int cmd, intptr_t arg, int mode,
1868 cred_t *credp, int *rvalp)
1869 {
1870 dev_info_t *self;
1871 dev_info_t *child = NULL;
1872 struct devctl_iocdata *dcp;
1873 uint_t state;
1874 int rv = 0;
1875 uint_t flags;
1876 int instance;
1877 int i;
1878 int ret;
1879
1880 self = pshot->dip;
1881
1882 flags = (pshot_devctl_debug) ? NDI_DEVI_DEBUG : 0;
1883 instance = pshot->instance;
1884
1885 /*
1886 * We can use the generic implementation for these ioctls
1887 */
1888 for (i = 0; pshot_devctls[i].ioctl_int != 0; i++) {
1889 if (pshot_devctls[i].ioctl_int == cmd) {
1890 if (pshot_debug)
1891 cmn_err(CE_CONT, "pshot%d devctl: %s",
1892 instance, pshot_devctls[i].ioctl_char);
1893 }
1894 }
1895 switch (cmd) {
1896 case DEVCTL_DEVICE_GETSTATE:
1897 case DEVCTL_DEVICE_ONLINE:
1898 case DEVCTL_DEVICE_OFFLINE:
1899 case DEVCTL_DEVICE_REMOVE:
1900 case DEVCTL_BUS_GETSTATE:
1901 case DEVCTL_BUS_DEV_CREATE:
1902 rv = ndi_devctl_ioctl(self, cmd, arg, mode, flags);
1903 if (pshot_debug && rv != 0) {
1904 cmn_err(CE_CONT, "pshot%d ndi_devctl_ioctl:"
1905 " failed, rv = %d", instance, rv);
1906 }
1907
1908 return (rv);
1909 }
1910
1911 /*
1912 * read devctl ioctl data
1913 */
1914 if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
1915 return (EFAULT);
1916
1917 switch (cmd) {
1918
1919 case DEVCTL_DEVICE_RESET:
1920 if (pshot_debug)
1921 cmn_err(CE_CONT, "pshot%d devctl:"
1922 " DEVCTL_DEVICE_RESET\n", instance);
1923 rv = pshot_event(pshot, PSHOT_EVENT_TAG_DEV_RESET,
1924 child, (void *)self);
1925 ASSERT(rv == NDI_SUCCESS);
1926 break;
1927
1928 case DEVCTL_BUS_QUIESCE:
1929 if (pshot_debug)
1930 cmn_err(CE_CONT, "pshot%d devctl:"
1931 " DEVCTL_BUS_QUIESCE\n", instance);
1932 if (ndi_get_bus_state(self, &state) == NDI_SUCCESS) {
1933 if (state == BUS_QUIESCED) {
1934 break;
1935 }
1936 (void) ndi_set_bus_state(self, BUS_QUIESCED);
1937 }
1938 rv = pshot_event(pshot, PSHOT_EVENT_TAG_BUS_QUIESCE,
1939 child, (void *)self);
1940 ASSERT(rv == NDI_SUCCESS);
1941
1942 break;
1943
1944 case DEVCTL_BUS_UNQUIESCE:
1945 if (pshot_debug)
1946 cmn_err(CE_CONT, "pshot%d devctl:"
1947 " DEVCTL_BUS_UNQUIESCE\n", instance);
1948 if (ndi_get_bus_state(self, &state) == NDI_SUCCESS) {
1949 if (state == BUS_ACTIVE) {
1950 break;
1951 }
1952 }
1953
1954 /*
1955 * quiesce the bus through bus-specific means
1956 */
1957 (void) ndi_set_bus_state(self, BUS_ACTIVE);
1958 rv = pshot_event(pshot, PSHOT_EVENT_TAG_BUS_UNQUIESCE,
1959 child, (void *)self);
1960 ASSERT(rv == NDI_SUCCESS);
1961 break;
1962
1963 case DEVCTL_BUS_RESET:
1964 case DEVCTL_BUS_RESETALL:
1965 /*
1966 * no reset support for the pseudo bus
1967 * but if there were....
1968 */
1969 rv = pshot_event(pshot, PSHOT_EVENT_TAG_BUS_RESET,
1970 child, (void *)self);
1971 ASSERT(rv == NDI_SUCCESS);
1972 break;
1973
1974 /*
1975 * PM related ioctls
1976 */
1977 case DEVCTL_PM_BUSY_COMP:
1978 /*
1979 * mark component 0 busy.
1980 * Keep track of ioctl updates to the busy count
1981 * via pshot->busy_ioctl.
1982 */
1983 if (pshot_debug) {
1984 cmn_err(CE_CONT, "pshot%d devctl:"
1985 " DEVCTL_PM_BUSY_COMP\n", instance);
1986 }
1987 mutex_enter(&pshot->lock);
1988 ++(pshot->busy);
1989 ++(pshot->busy_ioctl);
1990 if (pshot_debug_busy) {
1991 cmn_err(CE_CONT, "pshot%d:"
1992 " DEVCTL_PM_BUSY_COMP comp 0 busy"
1993 " %d busy_ioctl %d\n", instance, pshot->busy,
1994 pshot->busy_ioctl);
1995 }
1996 mutex_exit(&pshot->lock);
1997 ret = pm_busy_component(pshot->dip, 0);
1998 ASSERT(ret == DDI_SUCCESS);
1999
2000 break;
2001
2002 case DEVCTL_PM_BUSY_COMP_TEST:
2003 /*
2004 * test bus's busy state
2005 */
2006 if (pshot_debug) {
2007 cmn_err(CE_CONT, "pshot%d devctl:"
2008 " DEVCTL_PM_BUSY_COMP_TEST\n", instance);
2009 }
2010 mutex_enter(&pshot->lock);
2011 state = pshot->busy;
2012 if (copyout(&state, dcp->cpyout_buf,
2013 sizeof (uint_t)) != 0) {
2014 cmn_err(CE_WARN, "pshot%d devctl:"
2015 " DEVCTL_PM_BUSY_COMP_TEST: copyout failed",
2016 instance);
2017 rv = EINVAL;
2018 }
2019 if (pshot_debug_busy) {
2020 cmn_err(CE_CONT, "pshot%d: DEVCTL_PM_BUSY_COMP_TEST:"
2021 " comp 0 busy %d busy_ioctl %d\n", instance,
2022 state, pshot->busy_ioctl);
2023 }
2024 mutex_exit(&pshot->lock);
2025 break;
2026
2027 case DEVCTL_PM_IDLE_COMP:
2028 /*
2029 * mark component 0 idle.
2030 * NOP if pshot->busy_ioctl <= 0.
2031 */
2032 if (pshot_debug) {
2033 cmn_err(CE_CONT, "pshot%d devctl:"
2034 " DEVCTL_PM_IDLE_COMP\n", instance);
2035 }
2036 mutex_enter(&pshot->lock);
2037 if (pshot->busy_ioctl > 0) {
2038 ASSERT(pshot->busy > 0);
2039 --(pshot->busy);
2040 --(pshot->busy_ioctl);
2041 if (pshot_debug_busy) {
2042 cmn_err(CE_CONT, "pshot%d:"
2043 " DEVCTL_PM_IDLE_COM: comp 0"
2044 " busy %d busy_ioctl %d\n", instance,
2045 pshot->busy, pshot->busy_ioctl);
2046 }
2047 mutex_exit(&pshot->lock);
2048 ret = pm_idle_component(pshot->dip, 0);
2049 ASSERT(ret == DDI_SUCCESS);
2050
2051 } else {
2052 mutex_exit(&pshot->lock);
2053 }
2054 break;
2055
2056 case DEVCTL_PM_RAISE_PWR:
2057 /*
2058 * raise component 0 to full power level MAXPWR via a
2059 * pm_raise_power() call
2060 */
2061 if (pshot_debug) {
2062 cmn_err(CE_CONT, "pshot%d devctl:"
2063 " DEVCTL_PM_RAISE_PWR\n", instance);
2064 }
2065 if (pm_raise_power(pshot->dip, 0, MAXPWR) != DDI_SUCCESS) {
2066 rv = EINVAL;
2067 } else {
2068 mutex_enter(&pshot->lock);
2069 if (pshot_debug) {
2070 cmn_err(CE_CONT, "pshot%d:"
2071 " DEVCTL_PM_RAISE_POWER: comp 0"
2072 " to level %d\n", instance, pshot->level);
2073 }
2074 mutex_exit(&pshot->lock);
2075 }
2076 break;
2077
2078 case DEVCTL_PM_LOWER_PWR:
2079 /*
2080 * pm_lower_power() call for negative testing
2081 * expected to fail.
2082 */
2083 if (pshot_debug) {
2084 cmn_err(CE_CONT, "pshot%d devctl:"
2085 " DEVCTL_PM_LOWER_PWR\n", instance);
2086 }
2087 if (pm_lower_power(pshot->dip, 0, 0) != DDI_SUCCESS) {
2088 rv = EINVAL;
2089 } else {
2090 mutex_enter(&pshot->lock);
2091 if (pshot_debug) {
2092 cmn_err(CE_CONT, "pshot%d:"
2093 " DEVCTL_PM_LOWER_POWER comp 0"
2094 " to level %d\n", instance, pshot->level);
2095 }
2096 mutex_exit(&pshot->lock);
2097 }
2098 break;
2099
2100 case DEVCTL_PM_CHANGE_PWR_LOW:
2101 /*
2102 * inform the PM framework that component 0 has changed
2103 * power level to 0 via a pm_power_has_changed() call
2104 */
2105 if (pshot_debug) {
2106 cmn_err(CE_CONT, "pshot%d devctl:"
2107 " DEVCTL_PM_CHANGE_PWR_LOW\n", instance);
2108 }
2109 mutex_enter(&pshot->lock);
2110 pshot->level = 0;
2111 if (pm_power_has_changed(pshot->dip, 0, 0) != DDI_SUCCESS) {
2112 rv = EINVAL;
2113 } else {
2114 if (pshot_debug) {
2115 cmn_err(CE_CONT, "pshot%d:"
2116 " DEVCTL_PM_CHANGE_PWR_LOW comp 0 to"
2117 " level %d\n", instance, pshot->level);
2118 }
2119 }
2120 mutex_exit(&pshot->lock);
2121 break;
2122
2123 case DEVCTL_PM_CHANGE_PWR_HIGH:
2124 /*
2125 * inform the PM framework that component 0 has changed
2126 * power level to MAXPWR via a pm_power_has_changed() call
2127 */
2128 if (pshot_debug) {
2129 cmn_err(CE_CONT, "pshot%d devctl:"
2130 " DEVCTL_PM_CHANGE_PWR_HIGH\n", instance);
2131 }
2132 mutex_enter(&pshot->lock);
2133 pshot->level = MAXPWR;
2134 if (pm_power_has_changed(pshot->dip, 0, MAXPWR)
2135 != DDI_SUCCESS) {
2136 rv = EINVAL;
2137 } else {
2138 if (pshot_debug) {
2139 cmn_err(CE_CONT, "pshot%d:"
2140 " DEVCTL_PM_CHANGE_PWR_HIGH comp 0 to"
2141 " level %d\n", instance, pshot->level);
2142 }
2143 }
2144 mutex_exit(&pshot->lock);
2145 break;
2146
2147 case DEVCTL_PM_POWER:
2148 /*
2149 * test if the pshot_power() routine has been called,
2150 * then clear
2151 */
2152 if (pshot_debug) {
2153 cmn_err(CE_CONT, "pshot%d devctl:"
2154 " DEVCTL_PM_POWER\n", instance);
2155 }
2156 mutex_enter(&pshot->lock);
2157 state = (pshot->state & POWER_FLAG) ? 1 : 0;
2158 if (copyout(&state, dcp->cpyout_buf,
2159 sizeof (uint_t)) != 0) {
2160 cmn_err(CE_WARN, "pshot%d devctl:"
2161 " DEVCTL_PM_POWER: copyout failed",
2162 instance);
2163 rv = EINVAL;
2164 }
2165 if (pshot_debug) {
2166 cmn_err(CE_CONT, "pshot%d: DEVCTL_PM_POWER:"
2167 " POWER_FLAG = %d\n", instance, state);
2168 }
2169 pshot->state &= ~POWER_FLAG;
2170 mutex_exit(&pshot->lock);
2171 break;
2172
2173 case DEVCTL_PM_FAIL_SUSPEND:
2174 /*
2175 * fail DDI_SUSPEND
2176 */
2177 if (pshot_debug) {
2178 cmn_err(CE_CONT, "pshot%d devctl:"
2179 " DEVCTL_PM_FAIL_SUSPEND\n", instance);
2180 }
2181 mutex_enter(&pshot->lock);
2182 pshot->state |= FAIL_SUSPEND_FLAG;
2183 mutex_exit(&pshot->lock);
2184 if (pshot_debug) {
2185 cmn_err(CE_CONT, "pshot%d: DEVCTL_PM_FAIL_SUSPEND\n",
2186 instance);
2187 }
2188 break;
2189
2190 case DEVCTL_PM_BUS_STRICT_TEST:
2191 /*
2192 * test the STRICT_PARENT flag:
2193 * set => STRICT PARENT
2194 * not set => INVOLVED PARENT
2195 */
2196 mutex_enter(&pshot->lock);
2197 state = (pshot->state & STRICT_PARENT) ? 1 : 0;
2198 if (copyout(&state, dcp->cpyout_buf,
2199 sizeof (uint_t)) != 0) {
2200 cmn_err(CE_WARN, "pshot%d devctl:"
2201 " DEVCTL_PM_BUS_STRICT_TEST: copyout failed",
2202 instance);
2203 rv = EINVAL;
2204 }
2205 if (pshot_debug) {
2206 cmn_err(CE_CONT, "pshot%d devctl:"
2207 " DEVCTL_PM_BUS_STRICT_TEST: type = %s\n",
2208 instance, ((state == 0) ? "INVOLVED" : "STRICT"));
2209 }
2210 mutex_exit(&pshot->lock);
2211 break;
2212
2213 case DEVCTL_PM_BUS_NO_INVOL:
2214 /*
2215 * Set the NO_INVOL_FLAG flag to
2216 * notify the driver that the child will not
2217 * call pm_lower_power() on detach.
2218 * The driver needs to mark itself idle twice
2219 * during DDI_CTLOPS_DETACH (post).
2220 */
2221 if (pshot_debug) {
2222 cmn_err(CE_CONT, "pshot%d devctl:"
2223 " DEVCTL_PM_BUS_NO_INVOL\n", instance);
2224 }
2225 mutex_enter(&pshot->lock);
2226 pshot->state |= NO_INVOL_FLAG;
2227 mutex_exit(&pshot->lock);
2228 break;
2229
2230 default:
2231 rv = ENOTTY;
2232 }
2233
2234 ndi_dc_freehdl(dcp);
2235 return (rv);
2236 }
2237
2238
2239 /*
2240 * pshot_testctl: handle other test operations
2241 * - If <cmd> is a DEVCTL cmd, then <arg> is a dev_t indicating which
2242 * child to direct the DEVCTL to, if applicable;
2243 * furthermore, any cmd here can be sent by layered ioctls (unlike
2244 * those to pshot_devctl() which must come from userland)
2245 */
2246 /* ARGSUSED */
2247 static int
pshot_testctl(pshot_t * pshot,minor_t nodenum,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)2248 pshot_testctl(pshot_t *pshot, minor_t nodenum, int cmd, intptr_t arg, int mode,
2249 cred_t *credp, int *rvalp)
2250 {
2251 dev_info_t *self;
2252 dev_info_t *child = NULL;
2253 uint_t state;
2254 int rv = 0;
2255 int instance;
2256 int i;
2257
2258 /* uint_t flags; */
2259
2260 /* flags = (pshot_devctl_debug) ? NDI_DEVI_DEBUG : 0; */
2261 self = pshot->dip;
2262 instance = pshot->instance;
2263
2264 if (cmd & DEVCTL_IOC) {
2265 child = e_ddi_hold_devi_by_dev((dev_t)arg, 0);
2266 }
2267
2268 for (i = 0; pshot_devctls[i].ioctl_int != 0; i++) {
2269 if (pshot_devctls[i].ioctl_int == cmd) {
2270 if (pshot_debug)
2271 cmn_err(CE_CONT, "pshot%d devctl: %s",
2272 instance, pshot_devctls[i].ioctl_char);
2273 }
2274 }
2275 switch (cmd) {
2276 case DEVCTL_DEVICE_RESET:
2277 if (pshot_debug)
2278 cmn_err(CE_CONT, "pshot%d testctl:"
2279 " DEVCTL_PM_POWER\n", instance);
2280 rv = pshot_event(pshot, PSHOT_EVENT_TAG_DEV_RESET,
2281 child, (void *)self);
2282 ASSERT(rv == NDI_SUCCESS);
2283 break;
2284
2285 case DEVCTL_BUS_QUIESCE:
2286 if (pshot_debug)
2287 cmn_err(CE_CONT, "pshot%d testctl:"
2288 " DEVCTL_PM_POWER\n", instance);
2289 if (ndi_get_bus_state(self, &state) == NDI_SUCCESS) {
2290 if (state == BUS_QUIESCED) {
2291 break;
2292 }
2293 (void) ndi_set_bus_state(self, BUS_QUIESCED);
2294 }
2295 rv = pshot_event(pshot, PSHOT_EVENT_TAG_BUS_QUIESCE,
2296 child, (void *)self);
2297 ASSERT(rv == NDI_SUCCESS);
2298
2299 break;
2300
2301 case DEVCTL_BUS_UNQUIESCE:
2302 if (pshot_debug)
2303 cmn_err(CE_CONT, "pshot%d testctl:"
2304 " DEVCTL_PM_POWER\n", instance);
2305 if (ndi_get_bus_state(self, &state) == NDI_SUCCESS) {
2306 if (state == BUS_ACTIVE) {
2307 break;
2308 }
2309 }
2310
2311 /*
2312 * quiesce the bus through bus-specific means
2313 */
2314 (void) ndi_set_bus_state(self, BUS_ACTIVE);
2315 rv = pshot_event(pshot, PSHOT_EVENT_TAG_BUS_UNQUIESCE,
2316 child, (void *)self);
2317 ASSERT(rv == NDI_SUCCESS);
2318 break;
2319
2320 case DEVCTL_BUS_RESET:
2321 case DEVCTL_BUS_RESETALL:
2322 /*
2323 * no reset support for the pseudo bus
2324 * but if there were....
2325 */
2326 rv = pshot_event(pshot, PSHOT_EVENT_TAG_BUS_RESET,
2327 child, (void *)self);
2328 ASSERT(rv == NDI_SUCCESS);
2329 break;
2330
2331 default:
2332 rv = ENOTTY;
2333 }
2334
2335 if (child != NULL)
2336 ddi_release_devi(child);
2337 return (rv);
2338 }
2339
2340
2341 static int
pshot_get_eventcookie(dev_info_t * dip,dev_info_t * rdip,char * eventname,ddi_eventcookie_t * event_cookiep)2342 pshot_get_eventcookie(dev_info_t *dip, dev_info_t *rdip,
2343 char *eventname, ddi_eventcookie_t *event_cookiep)
2344 {
2345 int instance = ddi_get_instance(dip);
2346 pshot_t *pshot = ddi_get_soft_state(pshot_softstatep, instance);
2347
2348 if (pshot_debug)
2349 cmn_err(CE_CONT, "pshot%d: "
2350 "pshot_get_eventcookie:\n\t"
2351 "dip = 0x%p rdip = 0x%p (%s/%d) eventname = %s\n",
2352 instance, (void *)dip, (void *)rdip,
2353 ddi_node_name(rdip), ddi_get_instance(rdip),
2354 eventname);
2355
2356
2357 return (ndi_event_retrieve_cookie(pshot->ndi_event_hdl,
2358 rdip, eventname, event_cookiep, NDI_EVENT_NOPASS));
2359 }
2360
2361 static int
pshot_add_eventcall(dev_info_t * dip,dev_info_t * rdip,ddi_eventcookie_t cookie,void (* callback)(),void * arg,ddi_callback_id_t * cb_id)2362 pshot_add_eventcall(dev_info_t *dip, dev_info_t *rdip,
2363 ddi_eventcookie_t cookie,
2364 void (*callback)(), void *arg, ddi_callback_id_t *cb_id)
2365 {
2366 int instance = ddi_get_instance(dip);
2367 pshot_t *pshot = ddi_get_soft_state(pshot_softstatep, instance);
2368
2369 if (pshot_debug)
2370 cmn_err(CE_CONT, "pshot%d: "
2371 "pshot_add_eventcall:\n\t"
2372 "dip = 0x%p rdip = 0x%p (%s%d)\n\tcookie = 0x%p (%s)\n\t"
2373 "cb = 0x%p, arg = 0x%p\n",
2374 instance, (void *)dip, (void *)rdip,
2375 ddi_node_name(rdip), ddi_get_instance(rdip), (void *)cookie,
2376 NDI_EVENT_NAME(cookie), (void *)callback, arg);
2377
2378 /* add callback to our event handle */
2379 return (ndi_event_add_callback(pshot->ndi_event_hdl, rdip,
2380 cookie, callback, arg, NDI_SLEEP, cb_id));
2381 }
2382
2383 static int
pshot_remove_eventcall(dev_info_t * dip,ddi_callback_id_t cb_id)2384 pshot_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id)
2385 {
2386
2387 ndi_event_callbacks_t *cb = (ndi_event_callbacks_t *)cb_id;
2388
2389 int instance = ddi_get_instance(dip);
2390 pshot_t *pshot = ddi_get_soft_state(pshot_softstatep, instance);
2391
2392 ASSERT(cb);
2393
2394 if (pshot_debug)
2395 cmn_err(CE_CONT, "pshot%d: "
2396 "pshot_remove_eventcall:\n\t"
2397 "dip = 0x%p rdip = 0x%p (%s%d)\n\tcookie = 0x%p (%s)\n",
2398 instance, (void *)dip, (void *)cb->ndi_evtcb_dip,
2399 ddi_node_name(cb->ndi_evtcb_dip),
2400 ddi_get_instance(cb->ndi_evtcb_dip),
2401 (void *)cb->ndi_evtcb_cookie,
2402 NDI_EVENT_NAME(cb->ndi_evtcb_cookie));
2403
2404 return (ndi_event_remove_callback(pshot->ndi_event_hdl, cb_id));
2405 }
2406
2407 static int
pshot_post_event(dev_info_t * dip,dev_info_t * rdip,ddi_eventcookie_t cookie,void * impl_data)2408 pshot_post_event(dev_info_t *dip, dev_info_t *rdip,
2409 ddi_eventcookie_t cookie, void *impl_data)
2410 {
2411 int instance = ddi_get_instance(dip);
2412 pshot_t *pshot = ddi_get_soft_state(pshot_softstatep, instance);
2413
2414 if (pshot_debug) {
2415 if (rdip) {
2416 cmn_err(CE_CONT, "pshot%d: "
2417 "pshot_post_event:\n\t"
2418 "dip = 0x%p rdip = 0x%p (%s%d\n\t"
2419 "cookie = 0x%p (%s)\n\tbus_impl = 0x%p\n",
2420 instance, (void *)dip, (void *)rdip,
2421 ddi_node_name(rdip), ddi_get_instance(rdip),
2422 (void *)cookie,
2423 NDI_EVENT_NAME(cookie), impl_data);
2424 } else {
2425 cmn_err(CE_CONT, "pshot%d: "
2426 "pshot_post_event:\n\t"
2427 "dip = 0x%p cookie = 0x%p (%s) bus_impl = 0x%p\n",
2428 instance, (void *)dip, (void *)cookie,
2429 NDI_EVENT_NAME(cookie), impl_data);
2430 }
2431 }
2432
2433 /* run callbacks for this event */
2434 return (ndi_event_run_callbacks(pshot->ndi_event_hdl, rdip,
2435 cookie, impl_data));
2436 }
2437
2438 /*
2439 * the nexus driver will generate events
2440 * that need to go to children
2441 */
2442 static int
pshot_event(pshot_t * pshot,int event_tag,dev_info_t * child,void * bus_impldata)2443 pshot_event(pshot_t *pshot, int event_tag, dev_info_t *child,
2444 void *bus_impldata)
2445 {
2446 ddi_eventcookie_t cookie = ndi_event_tag_to_cookie(
2447 pshot->ndi_event_hdl, event_tag);
2448
2449 if (pshot_debug) {
2450 if (child) {
2451 cmn_err(CE_CONT, "pshot%d: "
2452 "pshot_event: event_tag = 0x%x (%s)\n\t"
2453 "child = 0x%p (%s%d) bus_impl = 0x%p (%s%d)\n",
2454 pshot->instance, event_tag,
2455 ndi_event_tag_to_name(pshot->ndi_event_hdl,
2456 event_tag),
2457 (void *)child, ddi_node_name(child),
2458 ddi_get_instance(child), bus_impldata,
2459 ddi_node_name((dev_info_t *)bus_impldata),
2460 ddi_get_instance((dev_info_t *)bus_impldata));
2461 } else {
2462 cmn_err(CE_CONT, "pshot%d: "
2463 "pshot_event: event_tag = 0x%x (%s)\n\t"
2464 "child = NULL, bus_impl = 0x%p (%s%d)\n",
2465 pshot->instance, event_tag,
2466 ndi_event_tag_to_name(pshot->ndi_event_hdl,
2467 event_tag),
2468 bus_impldata,
2469 ddi_node_name((dev_info_t *)bus_impldata),
2470 ddi_get_instance((dev_info_t *)bus_impldata));
2471 }
2472 }
2473
2474 return (ndi_event_run_callbacks(pshot->ndi_event_hdl,
2475 child, cookie, bus_impldata));
2476 }
2477
2478
2479 /*
2480 * the pshot driver event notification callback
2481 */
2482 static void
pshot_event_cb(dev_info_t * dip,ddi_eventcookie_t cookie,void * arg,void * bus_impldata)2483 pshot_event_cb(dev_info_t *dip, ddi_eventcookie_t cookie,
2484 void *arg, void *bus_impldata)
2485 {
2486 pshot_t *pshot = (pshot_t *)arg;
2487 int event_tag;
2488
2489 /* look up the event */
2490 event_tag = NDI_EVENT_TAG(cookie);
2491
2492 if (pshot_debug) {
2493 cmn_err(CE_CONT, "pshot%d: "
2494 "pshot_event_cb:\n\t"
2495 "dip = 0x%p cookie = 0x%p (%s), tag = 0x%x\n\t"
2496 "arg = 0x%p bus_impl = 0x%p (%s%d)\n",
2497 pshot->instance, (void *)dip, (void *)cookie,
2498 NDI_EVENT_NAME(cookie), event_tag, arg, bus_impldata,
2499 ddi_node_name((dev_info_t *)bus_impldata),
2500 ddi_get_instance((dev_info_t *)bus_impldata));
2501 }
2502
2503 switch (event_tag) {
2504 case PSHOT_EVENT_TAG_OFFLINE:
2505 case PSHOT_EVENT_TAG_BUS_RESET:
2506 case PSHOT_EVENT_TAG_BUS_QUIESCE:
2507 case PSHOT_EVENT_TAG_BUS_UNQUIESCE:
2508 /* notify all subscribers of the this event */
2509 (void) ndi_event_run_callbacks(pshot->ndi_event_hdl,
2510 NULL, cookie, bus_impldata);
2511 if (pshot_debug) {
2512 cmn_err(CE_CONT, "pshot%d: event=%s\n\t"
2513 "pshot_event_cb\n", pshot->instance,
2514 NDI_EVENT_NAME(cookie));
2515 }
2516 /*FALLTHRU*/
2517 case PSHOT_EVENT_TAG_TEST_POST:
2518 case PSHOT_EVENT_TAG_DEV_RESET:
2519 default:
2520 return;
2521 }
2522 }
2523
2524 static int
pshot_bus_config(dev_info_t * parent,uint_t flags,ddi_bus_config_op_t op,void * arg,dev_info_t ** childp)2525 pshot_bus_config(dev_info_t *parent, uint_t flags,
2526 ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
2527 {
2528 int rval;
2529 char *devname;
2530 char *devstr, *cname, *caddr;
2531 int devstrlen;
2532 int circ;
2533 pshot_t *pshot;
2534 int instance = ddi_get_instance(parent);
2535
2536 if (pshot_debug) {
2537 flags |= NDI_DEVI_DEBUG;
2538 cmn_err(CE_CONT,
2539 "pshot%d: bus_config %s flags=0x%x\n",
2540 ddi_get_instance(parent),
2541 (op == BUS_CONFIG_ONE) ? (char *)arg : "", flags);
2542 }
2543
2544 pshot = ddi_get_soft_state(pshot_softstatep, instance);
2545 if (pshot == NULL) {
2546
2547 return (NDI_FAILURE);
2548 }
2549
2550 /*
2551 * Hold the nexus across the bus_config
2552 */
2553 ndi_devi_enter(parent, &circ);
2554
2555 switch (op) {
2556 case BUS_CONFIG_ONE:
2557
2558 /*
2559 * lookup and hold child device, create if not found
2560 */
2561 devname = (char *)arg;
2562 devstrlen = strlen(devname) + 1;
2563 devstr = i_ddi_strdup(devname, KM_SLEEP);
2564 i_ddi_parse_name(devstr, &cname, &caddr, NULL);
2565
2566 /*
2567 * The framework ensures that the node has
2568 * a name but each nexus is responsible for
2569 * the bus address name space. This driver
2570 * requires that a bus address be specified,
2571 * as will most nexus drivers.
2572 */
2573 ASSERT(cname && strlen(cname) > 0);
2574 if (caddr == NULL || strlen(caddr) == 0) {
2575 cmn_err(CE_WARN,
2576 "pshot%d: malformed name %s (no bus address)",
2577 ddi_get_instance(parent), devname);
2578 kmem_free(devstr, devstrlen);
2579 ndi_devi_exit(parent, circ);
2580 return (NDI_FAILURE);
2581 }
2582
2583 /*
2584 * Handle a few special cases for testing purposes
2585 */
2586 rval = pshot_bus_config_test_specials(parent,
2587 devname, cname, caddr);
2588
2589 if (rval == NDI_SUCCESS) {
2590 /*
2591 * Set up either a leaf or nexus device
2592 */
2593 if (strcmp(cname, "pshot") == 0) {
2594 rval = pshot_bus_config_setup_nexus(parent,
2595 cname, caddr);
2596 } else {
2597 rval = pshot_bus_config_setup_leaf(parent,
2598 cname, caddr);
2599 }
2600 }
2601
2602 kmem_free(devstr, devstrlen);
2603 break;
2604
2605 case BUS_CONFIG_DRIVER:
2606 case BUS_CONFIG_ALL:
2607 rval = NDI_SUCCESS;
2608 break;
2609
2610 default:
2611 rval = NDI_FAILURE;
2612 break;
2613 }
2614
2615 if (rval == NDI_SUCCESS)
2616 rval = ndi_busop_bus_config(parent, flags, op, arg, childp, 0);
2617
2618 ndi_devi_exit(parent, circ);
2619
2620 if (pshot_debug)
2621 cmn_err(CE_CONT, "pshot%d: bus_config %s\n",
2622 ddi_get_instance(parent),
2623 (rval == NDI_SUCCESS) ? "ok" : "failed");
2624
2625 return (rval);
2626 }
2627
2628 static int
pshot_bus_unconfig(dev_info_t * parent,uint_t flags,ddi_bus_config_op_t op,void * arg)2629 pshot_bus_unconfig(dev_info_t *parent, uint_t flags,
2630 ddi_bus_config_op_t op, void *arg)
2631 {
2632 major_t major;
2633 int rval = NDI_SUCCESS;
2634 int circ;
2635
2636 if (pshot_debug) {
2637 flags |= NDI_DEVI_DEBUG;
2638 cmn_err(CE_CONT,
2639 "pshot%d: bus_unconfig %s flags=0x%x\n",
2640 ddi_get_instance(parent),
2641 (op == BUS_UNCONFIG_ONE) ? (char *)arg : "", flags);
2642 }
2643
2644 /*
2645 * Hold the nexus across the bus_unconfig
2646 */
2647 ndi_devi_enter(parent, &circ);
2648
2649 switch (op) {
2650 case BUS_UNCONFIG_ONE:
2651 /*
2652 * Nothing special required here
2653 */
2654 if (pshot_debug) {
2655 cmn_err(CE_CONT, "pshot%d: bus_unconfig:"
2656 " BUS_UNCONFIG_ONE\n", ddi_get_instance(parent));
2657 }
2658 break;
2659
2660 case BUS_UNCONFIG_DRIVER:
2661 if (pshot_debug > 0) {
2662 major = (major_t)(uintptr_t)arg;
2663 cmn_err(CE_CONT,
2664 "pshot%d: BUS_UNCONFIG_DRIVER: %s\n",
2665 ddi_get_instance(parent),
2666 ddi_major_to_name(major));
2667 }
2668 break;
2669
2670 case BUS_UNCONFIG_ALL:
2671 if (pshot_debug) {
2672 cmn_err(CE_CONT, "pshot%d: bus_unconfig:"
2673 " BUS_UNCONFIG_ALL\n", ddi_get_instance(parent));
2674 }
2675 break;
2676
2677 default:
2678 if (pshot_debug) {
2679 cmn_err(CE_CONT, "pshot%d: bus_unconfig: DEFAULT\n",
2680 ddi_get_instance(parent));
2681 }
2682 rval = NDI_FAILURE;
2683 }
2684
2685 if (rval == NDI_SUCCESS)
2686 rval = ndi_busop_bus_unconfig(parent, flags, op, arg);
2687
2688 ndi_devi_exit(parent, circ);
2689
2690 if (pshot_debug)
2691 cmn_err(CE_CONT, "pshot%d: bus_unconfig %s\n",
2692 ddi_get_instance(parent),
2693 (rval == NDI_SUCCESS) ? "ok" : "failed");
2694
2695 return (rval);
2696 }
2697
2698 static dev_info_t *
pshot_findchild(dev_info_t * pdip,char * cname,char * caddr)2699 pshot_findchild(dev_info_t *pdip, char *cname, char *caddr)
2700 {
2701 dev_info_t *dip;
2702 char *addr;
2703
2704 ASSERT(cname != NULL && caddr != NULL);
2705 ASSERT(DEVI_BUSY_OWNED(pdip));
2706
2707 for (dip = ddi_get_child(pdip); dip != NULL;
2708 dip = ddi_get_next_sibling(dip)) {
2709 if (strcmp(cname, ddi_node_name(dip)) != 0)
2710 continue;
2711
2712 if ((addr = ddi_get_name_addr(dip)) == NULL) {
2713 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 0,
2714 "bus-addr", &addr) == DDI_PROP_SUCCESS) {
2715 if (strcmp(caddr, addr) == 0) {
2716 ddi_prop_free(addr);
2717 return (dip);
2718 }
2719 ddi_prop_free(addr);
2720 }
2721 } else {
2722 if (strcmp(caddr, addr) == 0)
2723 return (dip);
2724 }
2725 }
2726
2727 return (NULL);
2728 }
2729
2730 static void
pshot_nexus_properties(dev_info_t * parent,dev_info_t * child,char * cname,char * caddr)2731 pshot_nexus_properties(dev_info_t *parent, dev_info_t *child, char *cname,
2732 char *caddr)
2733 {
2734 char *extension;
2735
2736 /*
2737 * extract the address extension
2738 */
2739 extension = strstr(caddr, ",");
2740 if (extension != NULL) {
2741 ++extension;
2742 } else {
2743 extension = "null";
2744 }
2745
2746 /*
2747 * Create the "pm-want-child-notification?" property for all
2748 * nodes that do not have the "pm_strict" or "nopm_strict"
2749 * extension
2750 */
2751 if (strcmp(extension, "pm_strict") != 0 &&
2752 strcmp(extension, "nopm_strict") != 0) {
2753 if (ddi_prop_exists(DDI_DEV_T_ANY, child,
2754 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
2755 "pm-want-child-notification?") == 0) {
2756 if (pshot_debug) {
2757 cmn_err(CE_CONT, "pshot%d:"
2758 " nexus_properties:\n\tcreate the"
2759 " \"pm-want-child-notification?\""
2760 " property for %s@%s\n",
2761 ddi_get_instance(parent), cname, caddr);
2762 }
2763 if (ddi_prop_create(DDI_DEV_T_NONE, child, 0,
2764 "pm-want-child-notification?", NULL, 0)
2765 != DDI_PROP_SUCCESS) {
2766 cmn_err(CE_WARN, "pshot%d:"
2767 " nexus_properties:\n\tunable to create"
2768 " the \"pm-want-child-notification?\""
2769 " property for %s@%s",
2770 ddi_get_instance(parent), cname, caddr);
2771 }
2772 }
2773 }
2774
2775 /*
2776 * Create the "no-pm-components" property for all nodes
2777 * with extension "nopm" or "nopm_strict"
2778 */
2779 if (strcmp(extension, "nopm") == 0 ||
2780 strcmp(extension, "nopm_strict") == 0) {
2781 if (ddi_prop_exists(DDI_DEV_T_ANY, child,
2782 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
2783 "no-pm-components") == 0) {
2784 if (pshot_debug) {
2785 cmn_err(CE_CONT, "pshot%d:"
2786 " nexus_properties:\n\tcreate the"
2787 " \"no-pm-components\""
2788 " property for %s@%s\n",
2789 ddi_get_instance(parent), cname, caddr);
2790 }
2791 if (ddi_prop_create(DDI_DEV_T_NONE, child, 0,
2792 "no-pm-components", NULL, 0)
2793 != DDI_PROP_SUCCESS) {
2794 cmn_err(CE_WARN, "pshot%d:"
2795 " nexus_properties:\n\tunable to create"
2796 " the \"no-pm-components\""
2797 " property for %s@%s",
2798 ddi_get_instance(parent), cname, caddr);
2799 }
2800 }
2801 }
2802 }
2803
2804 static void
pshot_leaf_properties(dev_info_t * parent,dev_info_t * child,char * cname,char * caddr)2805 pshot_leaf_properties(dev_info_t *parent, dev_info_t *child, char *cname,
2806 char *caddr)
2807 {
2808 char *extension;
2809
2810 /*
2811 * extract the address extension
2812 */
2813 extension = strstr(caddr, ",");
2814 if (extension != NULL) {
2815 ++extension;
2816 } else {
2817 extension = "null";
2818 }
2819
2820 /*
2821 * Create the "no-involuntary-power-cycles" property for
2822 * all leaf nodes with extension "no_invol"
2823 */
2824 if (strcmp(extension, "no_invol") == 0) {
2825 if (ddi_prop_exists(DDI_DEV_T_ANY, child,
2826 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
2827 "no-involuntary-power-cycles") == 0) {
2828 if (pshot_debug) {
2829 cmn_err(CE_CONT, "pshot%d:"
2830 " leaf_properties:\n\tcreate the"
2831 " \"no-involuntary-power-cycles\""
2832 " property for %s@%s\n",
2833 ddi_get_instance(parent), cname, caddr);
2834 }
2835 if (ddi_prop_create(DDI_DEV_T_NONE, child,
2836 DDI_PROP_CANSLEEP,
2837 "no-involuntary-power-cycles", NULL, 0)
2838 != DDI_PROP_SUCCESS) {
2839 cmn_err(CE_WARN, "pshot%d:"
2840 " leaf_properties:\n\tunable to create the"
2841 " \"no-involuntary-power-cycles\""
2842 " property for %s@%s",
2843 ddi_get_instance(parent), cname, caddr);
2844 }
2845 }
2846 }
2847
2848 /*
2849 * Create the "dependency-property" property for all leaf
2850 * nodes with extension "dep_prop"
2851 * to be used with the PM_ADD_DEPENDENT_PROPERTY ioctl
2852 */
2853 if (strcmp(extension, "dep_prop") == 0) {
2854 if (ddi_prop_exists(DDI_DEV_T_ANY, child,
2855 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
2856 "dependency-property") == 0) {
2857 if (pshot_debug) {
2858 cmn_err(CE_CONT, "pshot%d:"
2859 " leaf_properties:\n\tcreate the"
2860 " \"dependency-property\""
2861 " property for %s@%s\n",
2862 ddi_get_instance(parent), cname, caddr);
2863 }
2864 if (ddi_prop_create(DDI_DEV_T_NONE, child,
2865 DDI_PROP_CANSLEEP, "dependency-property", NULL, 0)
2866 != DDI_PROP_SUCCESS) {
2867 cmn_err(CE_WARN, "pshot%d:"
2868 " leaf_properties:\n\tunable to create the"
2869 " \"dependency-property\" property for"
2870 " %s@%s", ddi_get_instance(parent),
2871 cname, caddr);
2872 }
2873 }
2874 }
2875 }
2876
2877 /*
2878 * BUS_CONFIG_ONE: setup a child nexus instance.
2879 */
2880 static int
pshot_bus_config_setup_nexus(dev_info_t * parent,char * cname,char * caddr)2881 pshot_bus_config_setup_nexus(dev_info_t *parent, char *cname, char *caddr)
2882 {
2883 dev_info_t *child;
2884 int rval;
2885
2886 ASSERT(parent != 0);
2887 ASSERT(cname != NULL);
2888 ASSERT(caddr != NULL);
2889
2890 child = pshot_findchild(parent, cname, caddr);
2891 if (child) {
2892 if (pshot_debug) {
2893 cmn_err(CE_CONT,
2894 "pshot%d: bus_config one %s@%s found\n",
2895 ddi_get_instance(parent), cname, caddr);
2896 }
2897
2898 /*
2899 * create the "pm-want-child-notification?" property
2900 * for this child, if it doesn't already exist
2901 */
2902 (void) pshot_nexus_properties(parent, child, cname, caddr);
2903
2904 return (NDI_SUCCESS);
2905 }
2906
2907 ndi_devi_alloc_sleep(parent, cname, DEVI_SID_NODEID, &child);
2908 ASSERT(child != NULL);
2909
2910 if (ndi_prop_update_string(DDI_DEV_T_NONE, child,
2911 "bus-addr", caddr) != DDI_PROP_SUCCESS) {
2912 cmn_err(CE_WARN, "pshot%d: _prop_update %s@%s failed",
2913 ddi_get_instance(parent), cname, caddr);
2914 (void) ndi_devi_free(child);
2915 return (NDI_FAILURE);
2916 }
2917
2918 rval = ndi_devi_bind_driver(child, 0);
2919 if (rval != NDI_SUCCESS) {
2920 cmn_err(CE_WARN, "pshot%d: bind_driver %s failed",
2921 ddi_get_instance(parent), cname);
2922 (void) ndi_devi_free(child);
2923 return (NDI_FAILURE);
2924 }
2925
2926 /*
2927 * create the "pm-want-child-notification?" property
2928 */
2929 (void) pshot_nexus_properties(parent, child, cname, caddr);
2930
2931 return (NDI_SUCCESS);
2932 }
2933
2934 /*
2935 * BUS_CONFIG_ONE: setup a child leaf device instance.
2936 * for testing purposes, we will create nodes of a variety of types.
2937 */
2938 static int
pshot_bus_config_setup_leaf(dev_info_t * parent,char * cname,char * caddr)2939 pshot_bus_config_setup_leaf(dev_info_t *parent, char *cname, char *caddr)
2940 {
2941 dev_info_t *child;
2942 char *compat_name;
2943 char *nodetype;
2944 int rval;
2945 int i;
2946
2947 ASSERT(parent != 0);
2948 ASSERT(cname != NULL);
2949 ASSERT(caddr != NULL);
2950
2951 /*
2952 * if we already have a node with this name, return it
2953 */
2954 if ((child = pshot_findchild(parent, cname, caddr)) != NULL) {
2955 /*
2956 * create the "no-involuntary-power-cycles" or
2957 * the "dependency-property" property, if they
2958 * don't already exit
2959 */
2960 (void) pshot_leaf_properties(parent, child, cname, caddr);
2961
2962 return (NDI_SUCCESS);
2963 }
2964
2965 ndi_devi_alloc_sleep(parent, cname, DEVI_SID_NODEID, &child);
2966 ASSERT(child != NULL);
2967
2968 if (ndi_prop_update_string(DDI_DEV_T_NONE, child, "bus-addr",
2969 caddr) != DDI_PROP_SUCCESS) {
2970 (void) ndi_devi_free(child);
2971 return (NDI_FAILURE);
2972 }
2973
2974 /*
2975 * test compatible naming
2976 * if the child nodename is "cdisk", attach the list of compatible
2977 * named disks
2978 */
2979 if (strcmp(cname, pshot_compat_diskname) == 0) {
2980 if ((ndi_prop_update_string_array(DDI_DEV_T_NONE,
2981 child, "compatible", (char **)pshot_compat_psramdisks,
2982 5)) != DDI_PROP_SUCCESS) {
2983 (void) ndi_devi_free(child);
2984 return (NDI_FAILURE);
2985 }
2986 } else {
2987 for (i = 0; i < pshot_devices_len && pshot_devices[i].name;
2988 i++) {
2989 if (strcmp(cname, pshot_devices[i].name) == 0) {
2990 compat_name = pshot_devices[i].compat;
2991 nodetype = pshot_devices[i].nodetype;
2992 if (pshot_debug) {
2993 cmn_err(CE_CONT, "pshot%d: %s %s %s\n",
2994 ddi_get_instance(parent), cname,
2995 compat_name, nodetype);
2996 }
2997 if ((ndi_prop_update_string_array(
2998 DDI_DEV_T_NONE, child, "compatible",
2999 &compat_name, 1)) != DDI_PROP_SUCCESS) {
3000 (void) ndi_devi_free(child);
3001 return (NDI_FAILURE);
3002 }
3003 if ((ndi_prop_update_string(
3004 DDI_DEV_T_NONE, child, "node-type",
3005 nodetype)) != DDI_PROP_SUCCESS) {
3006 (void) ndi_devi_free(child);
3007 return (NDI_FAILURE);
3008 }
3009 }
3010 }
3011 }
3012
3013 rval = ndi_devi_bind_driver(child, 0);
3014 if (rval != NDI_SUCCESS) {
3015 cmn_err(CE_WARN, "pshot%d: bind_driver %s failed",
3016 ddi_get_instance(parent), cname);
3017 (void) ndi_devi_free(child);
3018 return (NDI_FAILURE);
3019 }
3020
3021 /*
3022 * create the "no-involuntary-power-cycles" or
3023 * the "dependency-property" property
3024 */
3025 (void) pshot_leaf_properties(parent, child, cname, caddr);
3026
3027 return (NDI_SUCCESS);
3028 }
3029
3030 /*
3031 * Handle some special cases for testing bus_config via pshot
3032 *
3033 * Match these special address formats to behavior:
3034 *
3035 * err.* - induce bus_config error
3036 * delay - induce 1 second of bus_config delay time
3037 * delay,n - induce n seconds of bus_config delay time
3038 * wait - induce 1 second of bus_config wait time
3039 * wait,n - induce n seconds of bus_config wait time
3040 * failinit.* - induce error at INITCHILD
3041 * failprobe.* - induce error at probe
3042 * failattach.* - induce error at attach
3043 */
3044 /*ARGSUSED*/
3045 static int
pshot_bus_config_test_specials(dev_info_t * parent,char * devname,char * cname,char * caddr)3046 pshot_bus_config_test_specials(dev_info_t *parent, char *devname,
3047 char *cname, char *caddr)
3048 {
3049 char *p;
3050 int n;
3051
3052 if (strncmp(caddr, "err", 3) == 0) {
3053 if (pshot_debug)
3054 cmn_err(CE_CONT,
3055 "pshot%d: %s forced failure\n",
3056 ddi_get_instance(parent), devname);
3057 return (NDI_FAILURE);
3058 }
3059
3060 /*
3061 * The delay and wait strings have the same effect.
3062 * The "wait[,]" support should be removed once the
3063 * devfs test suites are fixed.
3064 * NOTE: delay should not be called from interrupt context
3065 */
3066 ASSERT(!servicing_interrupt());
3067
3068 if (strncmp(caddr, "delay,", 6) == 0) {
3069 p = caddr+6;
3070 n = stoi(&p);
3071 if (*p != 0)
3072 n = 1;
3073 if (pshot_debug)
3074 cmn_err(CE_CONT,
3075 "pshot%d: %s delay %d second\n",
3076 ddi_get_instance(parent), devname, n);
3077 delay(n * drv_usectohz(1000000));
3078 } else if (strncmp(caddr, "delay", 5) == 0) {
3079 if (pshot_debug)
3080 cmn_err(CE_CONT,
3081 "pshot%d: %s delay 1 second\n",
3082 ddi_get_instance(parent), devname);
3083 delay(drv_usectohz(1000000));
3084 } else if (strncmp(caddr, "wait,", 5) == 0) {
3085 p = caddr+5;
3086 n = stoi(&p);
3087 if (*p != 0)
3088 n = 1;
3089 if (pshot_debug)
3090 cmn_err(CE_CONT,
3091 "pshot%d: %s wait %d second\n",
3092 ddi_get_instance(parent), devname, n);
3093 delay(n * drv_usectohz(1000000));
3094 } else if (strncmp(caddr, "wait", 4) == 0) {
3095 if (pshot_debug)
3096 cmn_err(CE_CONT,
3097 "pshot%d: %s wait 1 second\n",
3098 ddi_get_instance(parent), devname);
3099 delay(drv_usectohz(1000000));
3100 }
3101
3102 return (NDI_SUCCESS);
3103 }
3104
3105 /*
3106 * translate nodetype name to actual value
3107 */
3108 static char *
pshot_str2nt(char * str)3109 pshot_str2nt(char *str)
3110 {
3111 int i;
3112
3113 for (i = 0; pshot_nodetypes[i].name; i++) {
3114 if (strcmp(pshot_nodetypes[i].name, str) == 0)
3115 return (pshot_nodetypes[i].val);
3116 }
3117 return (NULL);
3118 }
3119
3120 /*
3121 * grows array pointed to by <dstp>, with <src> data
3122 * <dstlen> = # elements of the original <*dstp>
3123 * <srclen> = # elements of <src>
3124 *
3125 * on success, returns 0 and a pointer to the new array through <dstp> with
3126 * <srclen> + <dstlen> number of elements;
3127 * else returns non-zero
3128 *
3129 * a NULL <*dstp> is OK (a NULL <dstp> is not) and so is a zero <dstlen>
3130 */
3131 static int
pshot_devices_grow(pshot_device_t ** dstp,size_t dstlen,const pshot_device_t * src,size_t srclen)3132 pshot_devices_grow(pshot_device_t **dstp, size_t dstlen,
3133 const pshot_device_t *src, size_t srclen)
3134 {
3135 size_t i;
3136 pshot_device_t *newdst;
3137
3138 newdst = kmem_alloc((srclen + dstlen) * sizeof (*src),
3139 KM_SLEEP);
3140
3141 /* keep old pointers and dup new ones */
3142 if (*dstp)
3143 bcopy(*dstp, newdst, dstlen * sizeof (*src));
3144 for (i = 0; i < srclen; i++) {
3145 newdst[i + dstlen].name =
3146 i_ddi_strdup(src[i].name, KM_SLEEP);
3147
3148 newdst[i + dstlen].nodetype =
3149 i_ddi_strdup(src[i].nodetype, KM_SLEEP);
3150
3151 newdst[i + dstlen].compat =
3152 i_ddi_strdup(src[i].compat, KM_SLEEP);
3153 }
3154
3155 /* do last */
3156 if (*dstp)
3157 kmem_free(*dstp, dstlen * sizeof (*src));
3158 *dstp = newdst;
3159 return (0);
3160 }
3161
3162 /*
3163 * free a pshot_device_t array <dp> with <len> elements
3164 * null pointers within the elements are ok
3165 */
3166 static void
pshot_devices_free(pshot_device_t * dp,size_t len)3167 pshot_devices_free(pshot_device_t *dp, size_t len)
3168 {
3169 size_t i;
3170
3171 for (i = 0; i < len; i++) {
3172 if (dp[i].name)
3173 kmem_free(dp[i].name, strlen(dp[i].name) + 1);
3174 if (dp[i].nodetype)
3175 kmem_free(dp[i].nodetype, strlen(dp[i].nodetype) + 1);
3176 if (dp[i].compat)
3177 kmem_free(dp[i].compat, strlen(dp[i].compat) + 1);
3178 }
3179 kmem_free(dp, len * sizeof (*dp));
3180 }
3181
3182 /*
3183 * returns an array of pshot_device_t parsed from <dip>'s properties
3184 *
3185 * property structure (i.e. pshot.conf) for pshot:
3186 *
3187 * corresponding | pshot_device_t array elements
3188 * pshot_device_t |
3189 * member by prop name | [0] [1] [2]
3190 * ----------------------|--------------|-------------|-----------------------
3191 * <PSHOT_PROP_DEVNAME> ="disk", "tape", "testdev";
3192 * <PSHOT_PROP_DEVNT> ="DDI_NT_BLOCK","DDI_NT_TAPE","ddi_testdev_nodetype";
3193 * <PSHOT_PROP_DEVCOMPAT>="testdrv", "testdrv", "testdrv";
3194 *
3195 *
3196 * if any of these properties are specified, then:
3197 * - all the members must be specified
3198 * - the number of elements for each string array property must be the same
3199 * - no empty strings allowed
3200 * - nodetypes (PSHOT_PROP_DEVNT) must be the nodetype name as specified in
3201 * sys/sunddi.h
3202 *
3203 * NOTE: the pshot_nodetypes[] table should be kept in sync with the list
3204 * of ddi nodetypes. It's not normally critical to always be in sync so
3205 * keeping this up-to-date can usually be done "on-demand".
3206 *
3207 * if <flags> & PSHOT_DEV_ANYNT, then custom nodetype strings are allowed.
3208 * these will be duplicated verbatim
3209 */
3210 static pshot_device_t *
pshot_devices_from_props(dev_info_t * dip,size_t * lenp,int flags)3211 pshot_devices_from_props(dev_info_t *dip, size_t *lenp, int flags)
3212 {
3213 pshot_device_t *devarr = NULL;
3214 char **name_arr = NULL, **nt_arr = NULL, **compat_arr = NULL;
3215 uint_t name_arr_len, nt_arr_len, compat_arr_len;
3216 uint_t i;
3217 char *str;
3218
3219 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, 0,
3220 PSHOT_PROP_DEVNAME, &name_arr, &name_arr_len) !=
3221 DDI_PROP_SUCCESS)
3222 name_arr = NULL;
3223
3224 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, 0,
3225 PSHOT_PROP_DEVNT, &nt_arr, &nt_arr_len) !=
3226 DDI_PROP_SUCCESS)
3227 nt_arr = NULL;
3228
3229 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, 0,
3230 PSHOT_PROP_DEVCOMPAT, &compat_arr, &compat_arr_len) !=
3231 DDI_PROP_SUCCESS)
3232 compat_arr = NULL;
3233
3234 /*
3235 * warn about any incorrect usage, if specified
3236 */
3237 if (!(name_arr || nt_arr || compat_arr))
3238 return (NULL);
3239
3240 if (!(name_arr && nt_arr && compat_arr) ||
3241 (name_arr_len != nt_arr_len) ||
3242 (name_arr_len != compat_arr_len))
3243 goto FAIL;
3244
3245 for (i = 0; i < name_arr_len; i++) {
3246 if (*name_arr[i] == '\0' ||
3247 *nt_arr[i] == '\0' ||
3248 *compat_arr[i] == '\0')
3249 goto FAIL;
3250 }
3251
3252 devarr = kmem_zalloc(name_arr_len * sizeof (*devarr), KM_SLEEP);
3253 for (i = 0; i < name_arr_len; i++) {
3254 devarr[i].name = i_ddi_strdup(name_arr[i], KM_SLEEP);
3255 devarr[i].compat = i_ddi_strdup(compat_arr[i], KM_SLEEP);
3256
3257 if ((str = pshot_str2nt(nt_arr[i])) == NULL)
3258 if (flags & PSHOT_DEV_ANYNT)
3259 str = nt_arr[i];
3260 else
3261 goto FAIL;
3262 devarr[i].nodetype = i_ddi_strdup(str, KM_SLEEP);
3263 }
3264 ddi_prop_free(name_arr);
3265 ddi_prop_free(nt_arr);
3266 ddi_prop_free(compat_arr);
3267
3268 /* set <*lenp> ONLY on success */
3269 *lenp = name_arr_len;
3270
3271 return (devarr);
3272 /*NOTREACHED*/
3273 FAIL:
3274 cmn_err(CE_WARN, "malformed device specification property");
3275 if (name_arr)
3276 ddi_prop_free(name_arr);
3277 if (nt_arr)
3278 ddi_prop_free(nt_arr);
3279 if (compat_arr)
3280 ddi_prop_free(compat_arr);
3281 if (devarr)
3282 pshot_devices_free(devarr, name_arr_len);
3283 return (NULL);
3284 }
3285
3286 /*
3287 * if global <pshot_devices> was not set up already (i.e. is NULL):
3288 * sets up global <pshot_devices> and <pshot_devices_len>,
3289 * using device properties from <dip> and global <pshot_stock_devices>.
3290 * device properties, if any, overrides pshot_stock_devices.
3291 *
3292 * returns 0 on success (or if pshot_devices already set up)
3293 *
3294 * INTERNAL LOCKING: <pshot_devices_lock>
3295 */
3296 static int
pshot_devices_setup(dev_info_t * dip)3297 pshot_devices_setup(dev_info_t *dip)
3298 {
3299 pshot_device_t *newdevs = NULL;
3300 size_t newdevs_len = 0;
3301 int rv = 0;
3302
3303 mutex_enter(&pshot_devices_lock);
3304 if (pshot_devices != NULL)
3305 goto FAIL;
3306
3307 ASSERT(pshot_devices_len == 0);
3308
3309 newdevs = pshot_devices_from_props(dip, &newdevs_len, PSHOT_DEV_ANYNT);
3310 rv = pshot_devices_grow(&newdevs, newdevs_len, pshot_stock_devices,
3311 PSHOT_N_STOCK_DEVICES);
3312 if (rv != 0) {
3313 cmn_err(CE_WARN, "pshot_devices_setup: pshot_devices_grow "
3314 "failed");
3315 goto FAIL;
3316 }
3317 newdevs_len += PSHOT_N_STOCK_DEVICES;
3318
3319 pshot_devices = newdevs;
3320 pshot_devices_len = newdevs_len;
3321 rv = 0;
3322 FAIL:
3323 if (rv && newdevs)
3324 pshot_devices_free(newdevs, newdevs_len);
3325 mutex_exit(&pshot_devices_lock);
3326 return (rv);
3327 }
3328
3329
3330 #ifdef NOTNEEDED
3331 /* ARGSUSED */
3332 static int
pshot_probe_family(dev_info_t * self,ddi_probe_method_t probe_how,dev_info_t ** return_dip)3333 pshot_probe_family(dev_info_t *self, ddi_probe_method_t probe_how,
3334 dev_info_t **return_dip)
3335 {
3336 char name[64];
3337 uint_t bus_id;
3338 dev_info_t *child;
3339
3340 for (bus_id = 10; bus_id < 20; bus_id++) {
3341 (void) sprintf(name, "%d", bus_id);
3342 if ((ndi_devi_alloc(self, "psramd", DEVI_SID_NODEID,
3343 &child)) != NDI_SUCCESS) {
3344 return (DDI_FAILURE);
3345 }
3346
3347 if (ndi_prop_update_string(DDI_DEV_T_NONE, child,
3348 "bus-addr", name) != DDI_PROP_SUCCESS) {
3349 (void) ndi_devi_free(child);
3350 if (return_dip != NULL)
3351 *return_dip = (dev_info_t *)NULL;
3352 return (DDI_FAILURE);
3353 }
3354
3355 if (ndi_devi_online(child, 0) != NDI_SUCCESS) {
3356 return (DDI_FAILURE);
3357 }
3358 }
3359 return (DDI_SUCCESS);
3360 }
3361
3362 static int
strtoi(char * str)3363 strtoi(char *str)
3364 {
3365 int c;
3366 int val;
3367
3368 for (val = 0, c = *str++; c >= '0' && c <= '9'; c = *str++) {
3369 val *= 10;
3370 val += c - '0';
3371 }
3372 return (val);
3373 }
3374
3375 #endif
3376
3377 static void
pshot_setup_autoattach(dev_info_t * devi)3378 pshot_setup_autoattach(dev_info_t *devi)
3379 {
3380 dev_info_t *l1child, *l2child;
3381 int rv;
3382
3383 rv = ndi_devi_alloc(devi, "pshot", DEVI_SID_NODEID, &l1child);
3384 if (rv == NDI_SUCCESS) {
3385 (void) ndi_prop_update_string(DDI_DEV_T_NONE, l1child,
3386 "bus-addr", "0");
3387 rv = ndi_devi_alloc(l1child, "port", DEVI_SID_NODEID,
3388 &l2child);
3389 if (rv == NDI_SUCCESS)
3390 (void) ndi_prop_update_string(DDI_DEV_T_NONE,
3391 l2child, "bus-addr", "99");
3392 }
3393
3394 rv = ndi_devi_alloc(devi, "port", DEVI_SID_NODEID, &l1child);
3395 if (rv == NDI_SUCCESS)
3396 (void) ndi_prop_update_string(DDI_DEV_T_NONE, l1child,
3397 "bus-addr", "99");
3398
3399 rv = ndi_devi_alloc(devi, "gen_drv", DEVI_SID_NODEID, &l1child);
3400 if (rv == NDI_SUCCESS)
3401 (void) ndi_prop_update_string(DDI_DEV_T_NONE, l1child,
3402 "bus-addr", "99");
3403
3404 rv = ndi_devi_alloc(devi, "no_driver", DEVI_SID_NODEID, &l1child);
3405 if (rv == NDI_SUCCESS)
3406 (void) ndi_devi_alloc(l1child, "no_driver", DEVI_SID_NODEID,
3407 &l2child);
3408 }
3409
3410 #ifdef PRUNE_SNUBS
3411
3412 #define PRUNE_THIS_NODE(d) (((d)->devi_node_name != NULL) && \
3413 (DEVI_PROM_NODE((d)->devi_nodeid)) && \
3414 ((d)->devi_addr == NULL))
3415 /*
3416 * test code to remove OBP nodes that have not attached
3417 */
3418 static void
prune_snubs(const char * name)3419 prune_snubs(const char *name)
3420 {
3421 struct dev_info *nex_dip, *cdip, *cndip;
3422 int maj;
3423 int rv;
3424
3425 maj = ddi_name_to_major((char *)name);
3426 if (maj != -1) {
3427 nex_dip = (struct dev_info *)devnamesp[maj].dn_head;
3428 while (nex_dip != NULL) {
3429 cndip = ddi_get_child(nex_dip);
3430 while ((cdip = cndip) != NULL) {
3431 cndip = cdip->devi_sibling;
3432 if (PRUNE_THIS_NODE(cdip)) {
3433 cmn_err(CE_NOTE,
3434 "parent %s@%s pruning node %s",
3435 nex_dip->devi_node_name,
3436 nex_dip->devi_addr,
3437 cdip->devi_node_name);
3438 rv = ndi_devi_offline(cdip,
3439 NDI_DEVI_REMOVE);
3440 if (rv != NDI_SUCCESS)
3441 cmn_err(CE_NOTE,
3442 "failed to prune node, "
3443 "err %d", rv);
3444 }
3445 }
3446 nex_dip = nex_dip->devi_next;
3447 }
3448 }
3449 }
3450
3451 #endif /* PRUBE_SNUBS */
3452
3453 #ifdef KERNEL_DEVICE_TREE_WALKER
3454 static kthread_id_t pwt;
3455 static kmutex_t pwl;
3456 static kcondvar_t pwcv;
3457
3458 static void
pshot_walk_tree()3459 pshot_walk_tree()
3460 {
3461 static int pshot_devnode(dev_info_t *dip, void * arg);
3462
3463 dev_info_t *root = ddi_root_node();
3464 ddi_walk_devs(root, pshot_devnode, NULL);
3465 }
3466
3467 static void
pshot_walk_thread()3468 pshot_walk_thread()
3469 {
3470 static void pshot_timeout(void *arg);
3471 static kthread_id_t pwt;
3472
3473 pwt = curthread;
3474 mutex_init(&pwl, NULL, MUTEX_DRIVER, NULL);
3475 cv_init(&pwcv, NULL, CV_DRIVER, NULL);
3476
3477 while (1) {
3478 pshot_walk_tree();
3479 mutex_enter(&pwl);
3480 (void) timeout(pshot_timeout, NULL, 5 * drv_usectohz(1000000));
3481 cv_wait(&pwcv, &pwl);
3482 mutex_exit(&pwl);
3483 }
3484 }
3485
3486 static void
pshot_timeout(void * arg)3487 pshot_timeout(void *arg)
3488 {
3489 mutex_enter(&pwl);
3490 cv_signal(&pwcv);
3491 mutex_exit(&pwl);
3492 }
3493
3494 static int
pshot_devnode(dev_info_t * dip,void * arg)3495 pshot_devnode(dev_info_t *dip, void *arg)
3496 {
3497 dev_info_t *f_dip;
3498
3499 if (dip != ddi_root_node()) {
3500 f_dip = ndi_devi_find((dev_info_t *)DEVI(dip)->devi_parent,
3501 DEVI(dip)->devi_node_name, DEVI(dip)->devi_addr);
3502 if (f_dip != dip) {
3503 cmn_err(CE_NOTE, "!pshot_devnode: failed lookup"
3504 "node (%s/%s@%s)\n",
3505 DEVI(DEVI(dip)->devi_parent)->devi_node_name,
3506 (DEVI(dip)->devi_node_name ?
3507 DEVI(dip)->devi_node_name : "NULL"),
3508 (DEVI(dip)->devi_addr ? DEVI(dip)->devi_addr :
3509 "NULL"));
3510 }
3511 }
3512 return (DDI_WALK_CONTINUE);
3513 }
3514 #endif /* KERNEL_DEVICE_TREE_WALKER */
3515
3516 #ifdef DEBUG
3517 static void
pshot_event_cb_test(dev_info_t * dip,ddi_eventcookie_t cookie,void * arg,void * bus_impldata)3518 pshot_event_cb_test(dev_info_t *dip, ddi_eventcookie_t cookie,
3519 void *arg, void *bus_impldata)
3520 {
3521 pshot_t *softstate = (pshot_t *)arg;
3522 int event_tag;
3523
3524 /* look up the event */
3525 event_tag = NDI_EVENT_TAG(cookie);
3526 cmn_err(CE_CONT, "pshot_event_cb_test:\n\t"
3527 "dip = 0x%p cookie = 0x%p (%s), tag = %d\n\t"
3528 "arg = 0x%p bus_impl = 0x%p\n",
3529 (void *)dip, (void *)cookie, NDI_EVENT_NAME(cookie),
3530 event_tag, (void *)softstate, (void *)bus_impldata);
3531
3532 }
3533
3534 static void
pshot_event_test(void * arg)3535 pshot_event_test(void *arg)
3536 {
3537 pshot_t *pshot = (pshot_t *)arg;
3538 ndi_event_hdl_t hdl;
3539 ndi_event_set_t events;
3540 int i, rval;
3541
3542 (void) ndi_event_alloc_hdl(pshot->dip, NULL, &hdl, NDI_SLEEP);
3543
3544 events.ndi_events_version = NDI_EVENTS_REV1;
3545 events.ndi_n_events = PSHOT_N_TEST_EVENTS;
3546 events.ndi_event_defs = pshot_test_events;
3547
3548 cmn_err(CE_CONT, "pshot: binding set of 8 events\n");
3549 delay(drv_usectohz(1000000));
3550 rval = ndi_event_bind_set(hdl, &events, NDI_SLEEP);
3551 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval);
3552
3553 cmn_err(CE_CONT, "pshot: binding the same set of 8 events\n");
3554 delay(drv_usectohz(1000000));
3555 rval = ndi_event_bind_set(hdl, &events, NDI_SLEEP);
3556 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval);
3557
3558 cmn_err(CE_CONT, "pshot: unbinding all events\n");
3559 delay(drv_usectohz(1000000));
3560 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP);
3561 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval);
3562
3563
3564 cmn_err(CE_CONT, "pshot: binding one highlevel event\n");
3565 delay(drv_usectohz(1000000));
3566 events.ndi_n_events = 1;
3567 events.ndi_event_defs = pshot_test_events_high;
3568 rval = ndi_event_bind_set(hdl, &events, NDI_SLEEP);
3569 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval);
3570
3571 cmn_err(CE_CONT, "pshot: binding the same set of 8 events\n");
3572 delay(drv_usectohz(1000000));
3573 events.ndi_n_events = PSHOT_N_TEST_EVENTS;
3574 events.ndi_event_defs = pshot_test_events;
3575 rval = ndi_event_bind_set(hdl, &events, NDI_SLEEP);
3576 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval);
3577
3578 cmn_err(CE_CONT, "pshot: unbinding one highlevel event\n");
3579 delay(drv_usectohz(1000000));
3580 events.ndi_n_events = 1;
3581 events.ndi_event_defs = pshot_test_events_high;
3582 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP);
3583 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval);
3584
3585 cmn_err(CE_CONT, "pshot: binding one highlevel event\n");
3586 delay(drv_usectohz(1000000));
3587 events.ndi_n_events = 1;
3588 events.ndi_event_defs = pshot_test_events_high;
3589 rval = ndi_event_bind_set(hdl, &events, NDI_SLEEP);
3590 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval);
3591
3592 cmn_err(CE_CONT, "pshot: unbinding one highlevel event\n");
3593 delay(drv_usectohz(1000000));
3594 events.ndi_n_events = 1;
3595 events.ndi_event_defs = pshot_test_events_high;
3596 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP);
3597 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval);
3598
3599 cmn_err(CE_CONT, "pshot: binding the same set of 8 events\n");
3600 delay(drv_usectohz(1000000));
3601 events.ndi_n_events = PSHOT_N_TEST_EVENTS;
3602 events.ndi_event_defs = pshot_test_events;
3603 rval = ndi_event_bind_set(hdl, &events, NDI_SLEEP);
3604 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval);
3605
3606 cmn_err(CE_CONT, "pshot: unbinding first 2 events\n");
3607 delay(drv_usectohz(1000000));
3608 events.ndi_n_events = 2;
3609 events.ndi_event_defs = pshot_test_events;
3610 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP);
3611 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval);
3612
3613 cmn_err(CE_CONT, "pshot: unbinding first 2 events again\n");
3614 delay(drv_usectohz(1000000));
3615 events.ndi_n_events = 2;
3616 events.ndi_event_defs = pshot_test_events;
3617 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP);
3618 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval);
3619
3620 cmn_err(CE_CONT, "pshot: unbinding middle 2 events\n");
3621 delay(drv_usectohz(1000000));
3622 events.ndi_n_events = 2;
3623 events.ndi_event_defs = &pshot_test_events[4];
3624 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP);
3625 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval);
3626
3627 cmn_err(CE_CONT, "pshot: binding those 2 events back\n");
3628 delay(drv_usectohz(1000000));
3629 events.ndi_n_events = 2;
3630 events.ndi_event_defs = &pshot_test_events[4];
3631 rval = ndi_event_bind_set(hdl, &events, NDI_SLEEP);
3632 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval);
3633
3634 cmn_err(CE_CONT, "pshot: unbinding 2 events\n");
3635 delay(drv_usectohz(1000000));
3636 events.ndi_n_events = 2;
3637 events.ndi_event_defs = &pshot_test_events[4];
3638 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP);
3639 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval);
3640
3641 cmn_err(CE_CONT, "pshot: unbinding all events\n");
3642 delay(drv_usectohz(1000000));
3643 events.ndi_n_events = PSHOT_N_TEST_EVENTS;
3644 events.ndi_event_defs = pshot_test_events;
3645 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP);
3646 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval);
3647
3648 cmn_err(CE_CONT, "pshot: unbinding 1 event\n");
3649 delay(drv_usectohz(1000000));
3650 events.ndi_n_events = 1;
3651 events.ndi_event_defs = &pshot_test_events[2];
3652 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP);
3653 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval);
3654
3655 cmn_err(CE_CONT, "pshot: unbinding 1 event\n");
3656 delay(drv_usectohz(1000000));
3657 events.ndi_n_events = 1;
3658 events.ndi_event_defs = &pshot_test_events[3];
3659 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP);
3660 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval);
3661
3662 cmn_err(CE_CONT, "pshot: unbinding 1 event\n");
3663 delay(drv_usectohz(1000000));
3664 events.ndi_n_events = 1;
3665 events.ndi_event_defs = &pshot_test_events[6];
3666 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP);
3667 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval);
3668
3669 cmn_err(CE_CONT, "pshot: unbinding 1 event\n");
3670 delay(drv_usectohz(1000000));
3671 events.ndi_n_events = 1;
3672 events.ndi_event_defs = &pshot_test_events[7];
3673 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP);
3674 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval);
3675
3676 events.ndi_n_events = PSHOT_N_TEST_EVENTS;
3677 events.ndi_event_defs = pshot_test_events;
3678
3679 cmn_err(CE_CONT, "pshot: binding set of 8 events\n");
3680 delay(drv_usectohz(1000000));
3681 rval = ndi_event_bind_set(hdl, &events, NDI_SLEEP);
3682 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval);
3683
3684 cmn_err(CE_CONT, "pshot: adding 8 callbacks\n");
3685 delay(drv_usectohz(1000000));
3686 for (i = 0; i < 8; i++) {
3687 rval = ndi_event_add_callback(hdl, pshot->dip,
3688 ndi_event_tag_to_cookie(hdl,
3689 pshot_test_events[i].ndi_event_tag),
3690 pshot_event_cb_test,
3691 (void *)(uintptr_t)pshot_test_events[i].ndi_event_tag,
3692 NDI_SLEEP, &pshot->test_callback_cache[i]);
3693 ASSERT(rval == NDI_SUCCESS);
3694 }
3695
3696 cmn_err(CE_CONT, "pshot: event callbacks\n");
3697
3698 for (i = 10; i < 18; i++) {
3699 ddi_eventcookie_t cookie = ndi_event_tag_to_cookie(hdl, i);
3700
3701 rval = ndi_event_run_callbacks(hdl, pshot->dip, cookie,
3702 (void *)hdl);
3703
3704 cmn_err(CE_CONT, "pshot: callback, tag=%d rval=%d\n",
3705 i, rval);
3706 delay(drv_usectohz(1000000));
3707 }
3708
3709 cmn_err(CE_CONT, "pshot: redo event callbacks\n");
3710
3711 for (i = 10; i < 18; i++) {
3712 ddi_eventcookie_t cookie = ndi_event_tag_to_cookie(hdl, i);
3713
3714 rval = ndi_event_run_callbacks(hdl,
3715 pshot->dip, cookie, (void *)hdl);
3716
3717 cmn_err(CE_CONT, "pshot: callback, tag=%d rval=%d\n",
3718 i, rval);
3719 delay(drv_usectohz(1000000));
3720 }
3721
3722 cmn_err(CE_CONT, "pshot: removing 8 callbacks\n");
3723 delay(drv_usectohz(1000000));
3724
3725 for (i = 0; i < 8; i++) {
3726 (void) ndi_event_remove_callback(hdl,
3727 pshot->test_callback_cache[i]);
3728
3729 pshot->test_callback_cache[i] = 0;
3730 }
3731
3732 cmn_err(CE_CONT, "pshot: freeing handle with bound set\n");
3733 delay(drv_usectohz(1000000));
3734
3735 rval = ndi_event_free_hdl(hdl);
3736
3737 ASSERT(rval == NDI_SUCCESS);
3738
3739 }
3740
3741 void
pshot_event_test_post_one(void * arg)3742 pshot_event_test_post_one(void *arg)
3743 {
3744 pshot_t *pshot = (pshot_t *)arg;
3745 int rval;
3746 ddi_eventcookie_t cookie;
3747
3748 cmn_err(CE_CONT, "pshot%d: pshot_event_post_one event\n",
3749 pshot->instance);
3750
3751 if (ddi_get_eventcookie(pshot->dip, PSHOT_EVENT_NAME_BUS_TEST_POST,
3752 &cookie) != DDI_SUCCESS) {
3753 cmn_err(CE_NOTE, "pshot_bus_test_post cookie not found");
3754 return;
3755 }
3756
3757 rval = ndi_post_event(pshot->dip, pshot->dip, cookie, NULL);
3758
3759 cmn_err(CE_CONT, "pshot%d: pshot_event_post_one rval=%d\n",
3760 pshot->instance, rval);
3761
3762 (void) timeout(pshot_event_test_post_one, (void *)pshot,
3763 pshot->instance * drv_usectohz(60000000));
3764
3765 }
3766 #endif /* DEBUG */
3767