Merge commit 'cb41b9c565d4eec9e1f06e24d429696f59f2f07d'
[unleashed.git] / usr / src / uts / common / io / pshot.c
blob0d0d0fa2c5ca5281c75dd86773606ef8ada9445f
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved.
27 * Copyright (c) 2016 by Delphix. All rights reserved.
31 * pseudo bus nexus driver
32 * hotplug framework test facility
36 * The pshot driver can be used to exercise the i/o framework together
37 * with devfs by configuring an arbitrarily complex device tree.
39 * The pshot driver is rooted at /devices/pshot. The following commands
40 * illustrate the operation of devfs together with pshot's bus_config.
41 * The first command demonstrates that, like the magician showing there's
42 * nothing up their sleeve, /devices/pshot is empty. The second command
43 * conjures up a branch of pshot nodes. Note that pshot's bus_config is
44 * called sequentially by devfs for each node, as part of the pathname
45 * resolution, and that each pshot node is fully configured and
46 * attached before that node's bus_config is called to configure the
47 * next child down the tree. The final result is a "disk" node configured
48 * at the bottom of the named hierarchy of pshot nodes.
50 * #
51 * # ls /devices/pshot
52 * #
53 * # ls -ld /devices/pshot/pshot@0/pshot@1/pshot@2/disk@3,0
54 * drwxr-xr-x 2 root sys 512 Feb 6 15:10
55 * /devices/pshot/pshot@0/pshot@1/pshot@2/disk@3,0
57 * pshot supports some unique behaviors as aids for test error cases.
59 * Match these special address formats to behavior:
61 * err.* - induce bus_config error
62 * delay - induce 1 second of bus_config delay time
63 * delay,n - induce n seconds of bus_config delay time
64 * wait - induce 1 second of bus_config wait time
65 * wait,n - induce n seconds of bus_config wait time
66 * failinit.* - induce error at INITCHILD
67 * failprobe.* - induce error at probe
68 * failattach.* - induce error at attach
72 #include <sys/types.h>
73 #include <sys/cmn_err.h>
74 #include <sys/conf.h>
75 #include <sys/ddi_impldefs.h>
76 #include <sys/autoconf.h>
77 #include <sys/open.h>
78 #include <sys/stat.h>
79 #include <sys/file.h>
80 #include <sys/errno.h>
81 #include <sys/systm.h>
82 #include <sys/modctl.h>
83 #include <sys/kmem.h>
84 #include <sys/ddi.h>
85 #include <sys/sunddi.h>
86 #include <sys/sunndi.h>
87 #include <sys/devctl.h>
88 #include <sys/disp.h>
89 #include <sys/utsname.h>
90 #include <sys/pshot.h>
91 #include <sys/debug.h>
93 static int pshot_log = 0;
94 static int pshot_devctl_debug = 0;
95 static int pshot_debug_busy = 0;
97 static void *pshot_softstatep;
99 static int pshot_prop_autoattach;
101 #define MAXPWR 3
105 * device configuration data
108 /* should keep in sync with current release */
109 static struct {
110 char *name;
111 char *val;
112 } pshot_nodetypes[] = {
113 {"DDI_NT_SERIAL", DDI_NT_SERIAL},
114 {"DDI_NT_SERIAL_MB", DDI_NT_SERIAL_MB},
115 {"DDI_NT_SERIAL_DO", DDI_NT_SERIAL_DO},
116 {"DDI_NT_SERIAL_MB_DO", DDI_NT_SERIAL_MB_DO},
117 {"DDI_NT_SERIAL_LOMCON", DDI_NT_SERIAL_LOMCON},
118 {"DDI_NT_BLOCK", DDI_NT_BLOCK},
119 {"DDI_NT_BLOCK_CHAN", DDI_NT_BLOCK_CHAN},
120 {"DDI_NT_BLOCK_WWN", DDI_NT_BLOCK_WWN},
121 {"DDI_NT_BLOCK_SAS", DDI_NT_BLOCK_SAS},
122 {"DDI_NT_CD", DDI_NT_CD},
123 {"DDI_NT_CD_CHAN", DDI_NT_CD_CHAN},
124 {"DDI_NT_FD", DDI_NT_FD},
125 {"DDI_NT_ENCLOSURE", DDI_NT_ENCLOSURE},
126 {"DDI_NT_SCSI_ENCLOSURE", DDI_NT_SCSI_ENCLOSURE},
127 {"DDI_NT_TAPE", DDI_NT_TAPE},
128 {"DDI_NT_NET", DDI_NT_NET},
129 {"DDI_NT_DISPLAY", DDI_NT_DISPLAY},
130 {"DDI_PSEUDO", DDI_PSEUDO},
131 {"DDI_NT_AUDIO", DDI_NT_AUDIO},
132 {"DDI_NT_MOUSE", DDI_NT_MOUSE},
133 {"DDI_NT_KEYBOARD", DDI_NT_KEYBOARD},
134 {"DDI_NT_PARALLEL", DDI_NT_PARALLEL},
135 {"DDI_NT_PRINTER", DDI_NT_PRINTER},
136 {"DDI_NT_UGEN", DDI_NT_UGEN},
137 {"DDI_NT_NEXUS", DDI_NT_NEXUS},
138 {"DDI_NT_SCSI_NEXUS", DDI_NT_SCSI_NEXUS},
139 {"DDI_NT_ATTACHMENT_POINT", DDI_NT_ATTACHMENT_POINT},
140 {"DDI_NT_SCSI_ATTACHMENT_POINT", DDI_NT_SCSI_ATTACHMENT_POINT},
141 {"DDI_NT_PCI_ATTACHMENT_POINT", DDI_NT_PCI_ATTACHMENT_POINT},
142 {"DDI_NT_SBD_ATTACHMENT_POINT", DDI_NT_SBD_ATTACHMENT_POINT},
143 {"DDI_NT_FC_ATTACHMENT_POINT", DDI_NT_FC_ATTACHMENT_POINT},
144 {"DDI_NT_USB_ATTACHMENT_POINT", DDI_NT_USB_ATTACHMENT_POINT},
145 {"DDI_NT_BLOCK_FABRIC", DDI_NT_BLOCK_FABRIC},
146 {"DDI_NT_AV_ASYNC", DDI_NT_AV_ASYNC},
147 {"DDI_NT_AV_ISOCH", DDI_NT_AV_ISOCH},
148 { NULL, NULL }
151 /* Node name */
152 static char *pshot_compat_diskname = "cdisk";
154 /* Compatible names... */
155 static char *pshot_compat_psramdisks[] = {
156 "psramhead",
157 "psramrom",
158 "psramdisk",
159 "psramd",
160 "psramwhat"
164 * devices "natively" supported by pshot (i.e. included with SUNWiotu)
165 * used to initialize pshot_devices with
167 static pshot_device_t pshot_stock_devices[] = {
168 {"disk", DDI_NT_BLOCK, "gen_drv"},
169 {"disk_chan", DDI_NT_BLOCK_CHAN, "gen_drv"},
170 {"disk_wwn", DDI_NT_BLOCK_WWN, "gen_drv"},
171 {"disk_cdrom", DDI_NT_CD, "gen_drv"},
172 {"disk_cdrom.chan", DDI_NT_CD_CHAN, "gen_drv"},
173 /* Note: use bad_drv to force attach errors */
174 {"disk_fd", DDI_NT_FD, "bad_drv"},
175 {"tape", DDI_NT_TAPE, "gen_drv"},
176 {"net", DDI_NT_NET, "gen_drv"},
177 {"display", DDI_NT_DISPLAY, "gen_drv"},
178 {"pseudo", DDI_PSEUDO, "gen_drv"},
179 {"audio", DDI_NT_AUDIO, "gen_drv"},
180 {"mouse", DDI_NT_MOUSE, "gen_drv"},
181 {"keyboard", DDI_NT_KEYBOARD, "gen_drv"},
182 {"nexus", DDI_NT_NEXUS, "pshot"}
184 #define PSHOT_N_STOCK_DEVICES \
185 (sizeof (pshot_stock_devices) / sizeof (pshot_device_t))
187 static pshot_device_t *pshot_devices = NULL;
188 static size_t pshot_devices_len = 0;
190 /* protects <pshot_devices>, <pshot_devices_len> */
191 static kmutex_t pshot_devices_lock;
195 * event testing
198 static ndi_event_definition_t pshot_ndi_event_defs[] = {
199 { PSHOT_EVENT_TAG_OFFLINE, PSHOT_EVENT_NAME_DEV_OFFLINE,
200 EPL_INTERRUPT, NDI_EVENT_POST_TO_ALL },
202 { PSHOT_EVENT_TAG_DEV_RESET, PSHOT_EVENT_NAME_DEV_RESET,
203 EPL_INTERRUPT, NDI_EVENT_POST_TO_TGT },
205 { PSHOT_EVENT_TAG_BUS_RESET, PSHOT_EVENT_NAME_BUS_RESET,
206 EPL_INTERRUPT, NDI_EVENT_POST_TO_ALL },
208 { PSHOT_EVENT_TAG_BUS_QUIESCE, PSHOT_EVENT_NAME_BUS_QUIESCE,
209 EPL_INTERRUPT, NDI_EVENT_POST_TO_ALL },
211 { PSHOT_EVENT_TAG_BUS_UNQUIESCE, PSHOT_EVENT_NAME_BUS_UNQUIESCE,
212 EPL_INTERRUPT, NDI_EVENT_POST_TO_ALL },
214 { PSHOT_EVENT_TAG_TEST_POST, PSHOT_EVENT_NAME_BUS_TEST_POST,
215 EPL_INTERRUPT, NDI_EVENT_POST_TO_TGT }
219 #define PSHOT_N_NDI_EVENTS \
220 (sizeof (pshot_ndi_event_defs) / sizeof (ndi_event_definition_t))
222 #ifdef DEBUG
224 static ndi_event_definition_t pshot_test_events[] = {
225 { 10, "test event 0", EPL_INTERRUPT, NDI_EVENT_POST_TO_ALL },
226 { 11, "test event 1", EPL_KERNEL, NDI_EVENT_POST_TO_TGT },
227 { 12, "test event 2", EPL_INTERRUPT, NDI_EVENT_POST_TO_TGT },
228 { 13, "test event 3", EPL_INTERRUPT, NDI_EVENT_POST_TO_ALL },
229 { 14, "test event 4", EPL_KERNEL, NDI_EVENT_POST_TO_ALL},
230 { 15, "test event 5", EPL_INTERRUPT, NDI_EVENT_POST_TO_ALL },
231 { 16, "test event 6", EPL_KERNEL, NDI_EVENT_POST_TO_ALL },
232 { 17, "test event 7", EPL_INTERRUPT, NDI_EVENT_POST_TO_ALL }
235 static ndi_event_definition_t pshot_test_events_high[] = {
236 { 20, "test event high 0", EPL_HIGHLEVEL, NDI_EVENT_POST_TO_ALL}
239 #define PSHOT_N_TEST_EVENTS \
240 (sizeof (pshot_test_events)/sizeof (ndi_event_definition_t))
241 #endif
243 struct register_events {
244 char *event_name;
245 ddi_eventcookie_t event_cookie;
246 void (*event_callback)
247 (dev_info_t *,
248 ddi_eventcookie_t,
249 void *arg,
250 void *impldata);
251 ddi_callback_id_t cb_id;
254 struct register_events pshot_register_events[] = {
255 { PSHOT_EVENT_NAME_DEV_OFFLINE, 0, pshot_event_cb, 0 },
256 { PSHOT_EVENT_NAME_DEV_RESET, 0, pshot_event_cb, 0 },
257 { PSHOT_EVENT_NAME_BUS_RESET, 0, pshot_event_cb, 0 },
258 { PSHOT_EVENT_NAME_BUS_QUIESCE, 0, pshot_event_cb, 0 },
259 { PSHOT_EVENT_NAME_BUS_UNQUIESCE, 0, pshot_event_cb, 0 },
260 { PSHOT_EVENT_NAME_BUS_TEST_POST, 0, pshot_event_cb, 0 }
263 #define PSHOT_N_DDI_EVENTS \
264 (sizeof (pshot_register_events) / sizeof (struct register_events))
267 #ifdef DEBUG
269 static struct register_events pshot_register_test[] = {
270 { "test event 0", 0, pshot_event_cb_test, 0},
271 { "test event 1", 0, pshot_event_cb_test, 0},
272 { "test event 2", 0, pshot_event_cb_test, 0},
273 { "test event 3", 0, pshot_event_cb_test, 0},
274 { "test event 4", 0, pshot_event_cb_test, 0},
275 { "test event 5", 0, pshot_event_cb_test, 0},
276 { "test event 6", 0, pshot_event_cb_test, 0},
277 { "test event 7", 0, pshot_event_cb_test, 0}
281 static struct register_events pshot_register_high_test[] = {
282 {"test event high 0", 0, pshot_event_cb_test, 0}
285 #endif /* DEBUG */
287 static struct {
288 int ioctl_int;
289 char *ioctl_char;
290 } pshot_devctls[] = {
291 {DEVCTL_DEVICE_GETSTATE, "DEVCTL_DEVICE_GETSTATE"},
292 {DEVCTL_DEVICE_ONLINE, "DEVCTL_DEVICE_ONLINE"},
293 {DEVCTL_DEVICE_OFFLINE, "DEVCTL_DEVICE_OFFLINE"},
294 {DEVCTL_DEVICE_REMOVE, "DEVCTL_DEVICE_REMOVE"},
295 {DEVCTL_BUS_GETSTATE, "DEVCTL_BUS_GETSTATE"},
296 {DEVCTL_BUS_DEV_CREATE, "DEVCTL_BUS_DEV_CREATE"},
297 {DEVCTL_BUS_RESET, "DEVCTL_BUS_RESET"},
298 {DEVCTL_BUS_RESETALL, "DEVCTL_BUS_RESETALL"},
299 {0, NULL}
302 static struct bus_ops pshot_bus_ops = {
303 BUSO_REV, /* busops_rev */
304 nullbusmap, /* bus_map */
305 NULL, /* bus_get_intrspec */
306 NULL, /* bus_add_interspec */
307 NULL, /* bus_remove_interspec */
308 i_ddi_map_fault, /* bus_map_fault */
309 NULL, /* bus_dma_map */
310 ddi_dma_allochdl, /* bus_dma_allochdl */
311 ddi_dma_freehdl, /* bus_dma_freehdl */
312 ddi_dma_bindhdl, /* bus_dma_bindhdl */
313 ddi_dma_unbindhdl, /* bus_dma_unbindhdl */
314 ddi_dma_flush, /* bus_dma_flush */
315 ddi_dma_win, /* bus_dma_win */
316 ddi_dma_mctl, /* bus_dma_ctl */
317 pshot_ctl, /* bus_ctl */
318 ddi_bus_prop_op, /* bus_prop_op */
319 pshot_get_eventcookie, /* bus_get_eventcookie */
320 pshot_add_eventcall, /* bus_add_eventcall */
321 pshot_remove_eventcall, /* bus_remove_event */
322 pshot_post_event, /* bus_post_event */
323 NULL, /* bus_intr_ctl */
324 pshot_bus_config, /* bus_config */
325 pshot_bus_unconfig, /* bus_unconfig */
326 NULL, /* bus_fm_init */
327 NULL, /* bus_fm_fini */
328 NULL, /* bus_fm_access_enter */
329 NULL, /* bus_fm_access_exit */
330 pshot_bus_power, /* bus_power */
331 pshot_bus_introp /* bus_intr_op */
334 static struct cb_ops pshot_cb_ops = {
335 pshot_open, /* open */
336 pshot_close, /* close */
337 nodev, /* strategy */
338 nodev, /* print */
339 nodev, /* dump */
340 nodev, /* read */
341 nodev, /* write */
342 pshot_ioctl, /* ioctl */
343 nodev, /* devmap */
344 nodev, /* mmap */
345 nodev, /* segmap */
346 nochpoll, /* poll */
347 ddi_prop_op, /* prop_op */
348 NULL, /* streamtab */
349 D_NEW | D_MP | D_HOTPLUG, /* flags */
350 CB_REV, /* cb_rev */
351 nodev, /* aread */
352 nodev, /* awrite */
355 static struct dev_ops pshot_ops = {
356 DEVO_REV, /* devo_rev, */
357 0, /* refcnt */
358 pshot_info, /* getinfo */
359 nulldev, /* identify */
360 pshot_probe, /* probe */
361 pshot_attach, /* attach */
362 pshot_detach, /* detach */
363 nodev, /* reset */
364 &pshot_cb_ops, /* driver operations */
365 &pshot_bus_ops, /* bus operations */
366 pshot_power, /* power */
367 ddi_quiesce_not_supported, /* devo_quiesce */
373 * Module linkage information for the kernel.
375 static struct modldrv modldrv = {
376 &mod_driverops,
377 "pshotnex",
378 &pshot_ops,
381 static struct modlinkage modlinkage = {
382 MODREV_1, &modldrv, NULL
387 * pshot_devices is set up on the first attach and destroyed on fini
389 * therefore PSHOT_PROP_DEV* properties may be set just for the root device,
390 * instead of being set globably, in pshot.conf by specifying the properties
391 * on a single line in the form:
392 * name="pshot" parent="/" <dev props ..>
393 * to unclutter a device tree snapshot.
394 * this of course produces a long single line that may wrap around several
395 * times on screen
399 _init(void)
401 int rv;
403 rv = ddi_soft_state_init(&pshot_softstatep, sizeof (pshot_t), 0);
405 if (rv != DDI_SUCCESS)
406 return (rv);
408 mutex_init(&pshot_devices_lock, NULL, MUTEX_DRIVER, NULL);
409 pshot_devices = NULL;
410 pshot_devices_len = 0;
412 if ((rv = mod_install(&modlinkage)) != 0) {
413 ddi_soft_state_fini(&pshot_softstatep);
414 mutex_destroy(&pshot_devices_lock);
416 return (rv);
420 _fini(void)
422 int rv;
424 if ((rv = mod_remove(&modlinkage)) != 0)
425 return (rv);
427 ddi_soft_state_fini(&pshot_softstatep);
428 mutex_destroy(&pshot_devices_lock);
429 if (pshot_devices)
430 pshot_devices_free(pshot_devices, pshot_devices_len);
431 return (0);
435 _info(struct modinfo *modinfop)
437 return (mod_info(&modlinkage, modinfop));
441 /*ARGSUSED*/
442 static int
443 pshot_probe(dev_info_t *devi)
445 int instance = ddi_get_instance(devi);
446 char *bus_addr;
449 * Hook for tests to force probe fail
451 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, devi, 0, "bus-addr",
452 &bus_addr) == DDI_PROP_SUCCESS) {
453 if (strncmp(bus_addr, "failprobe", 9) == 0) {
454 if (pshot_debug)
455 cmn_err(CE_CONT, "pshot%d: "
456 "%s forced probe failure\n",
457 instance, bus_addr);
458 ddi_prop_free(bus_addr);
459 return (DDI_PROBE_FAILURE);
461 ddi_prop_free(bus_addr);
464 return (DDI_PROBE_SUCCESS);
468 /*ARGSUSED*/
469 static int
470 pshot_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
472 int instance;
473 minor_t minor;
474 pshot_t *pshot;
476 minor = getminor((dev_t)arg);
477 instance = pshot_minor_decode_inst(minor);
478 switch (infocmd) {
479 case DDI_INFO_DEVT2DEVINFO:
480 pshot = ddi_get_soft_state(pshot_softstatep, instance);
481 if (pshot == NULL) {
482 cmn_err(CE_WARN, "pshot_info: get soft state failed "
483 "on minor %u, instance %d", minor, instance);
484 return (DDI_FAILURE);
486 *result = (void *)pshot->dip;
487 break;
488 case DDI_INFO_DEVT2INSTANCE:
489 *result = (void *)(uintptr_t)instance;
490 break;
491 default:
492 cmn_err(CE_WARN, "pshot_info: unrecognized cmd 0x%x on "
493 "minor %u, instance %d", infocmd, minor, instance);
494 return (DDI_FAILURE);
497 return (DDI_SUCCESS);
501 static int
502 pshot_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
504 int instance = ddi_get_instance(devi);
505 pshot_t *pshot;
506 int rval, i;
507 int prop_flags = DDI_PROP_DONTPASS | DDI_PROP_NOTPROM;
508 char *bus_addr;
509 char *pm_comp[] = {
510 "NAME=bus",
511 "0=B3",
512 "1=B2",
513 "2=B1",
514 "3=B0"};
515 char *pm_hw_state = {"needs-suspend-resume"};
517 pshot_prop_autoattach = ddi_prop_get_int(DDI_DEV_T_ANY, devi,
518 prop_flags, "autoattach", 0);
520 switch (cmd) {
522 case DDI_ATTACH:
523 if (pshot_debug)
524 cmn_err(CE_CONT, "attach: %s%d/pshot%d\n",
525 ddi_get_name(ddi_get_parent(devi)),
526 ddi_get_instance(ddi_get_parent(devi)),
527 instance);
530 * Hook for tests to force attach fail
532 if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, devi, 0, "bus-addr",
533 &bus_addr) == DDI_PROP_SUCCESS) && bus_addr != NULL) {
534 if (strncmp(bus_addr, "failattach", 10) == 0) {
535 if (pshot_debug)
536 cmn_err(CE_CONT, "pshot%d: "
537 "%s forced attach failure\n",
538 instance, bus_addr);
539 ddi_prop_free(bus_addr);
540 return (DDI_FAILURE);
542 ddi_prop_free(bus_addr);
546 * minor nodes setup
548 if (ddi_soft_state_zalloc(pshot_softstatep, instance) !=
549 DDI_SUCCESS) {
550 return (DDI_FAILURE);
552 pshot = ddi_get_soft_state(pshot_softstatep, instance);
553 pshot->dip = devi;
554 pshot->instance = instance;
555 mutex_init(&pshot->lock, NULL, MUTEX_DRIVER, NULL);
557 /* set each minor, then create on dip all together */
559 i = PSHOT_NODENUM_DEVCTL;
560 pshot->nodes[i].pshot = pshot;
561 pshot->nodes[i].minor = pshot_minor_encode(instance, i);
562 (void) strncpy(pshot->nodes[i].name, PSHOT_NODENAME_DEVCTL,
563 PSHOT_MAX_MINOR_NAMELEN);
565 i = PSHOT_NODENUM_TESTCTL;
566 pshot->nodes[i].pshot = pshot;
567 pshot->nodes[i].minor = pshot_minor_encode(instance, i);
568 (void) strncpy(pshot->nodes[i].name, PSHOT_NODENAME_TESTCTL,
569 PSHOT_MAX_MINOR_NAMELEN);
571 /* this assumes contiguous a filling */
572 for (i = 0; i <= PSHOT_MAX_NODENUM; i++) {
573 if (ddi_create_minor_node(devi, pshot->nodes[i].name,
574 S_IFCHR, pshot->nodes[i].minor, DDI_NT_NEXUS, 0) !=
575 DDI_SUCCESS) {
576 cmn_err(CE_WARN, "attach: cannot create "
577 "minor %s", pshot->nodes[i].name);
578 goto FAIL_ATTACH;
583 * pshot_devices setup
585 if (pshot_devices_setup(devi)) {
586 cmn_err(CE_WARN, "attach: pshot devices setup "
587 "failed");
588 goto FAIL_ATTACH;
592 * events setup
594 for (i = 0; i < PSHOT_N_DDI_EVENTS; i++) {
595 rval = ddi_get_eventcookie(devi,
596 pshot_register_events[i].event_name,
597 &pshot_register_events[i].event_cookie);
599 if (pshot_debug)
600 cmn_err(CE_CONT, "pshot%d: event=%s:"
601 "ddi_get_eventcookie rval=%d\n",
602 instance,
603 pshot_register_events[i].event_name, rval);
605 if (rval == DDI_SUCCESS) {
606 rval = ddi_add_event_handler(devi,
607 pshot_register_events[i].event_cookie,
608 pshot_register_events[i].event_callback,
609 (void *)pshot,
610 &pshot->callback_cache[i]);
612 if (pshot_debug)
613 cmn_err(CE_CONT, "pshot%d: event=%s: "
614 "ddi_add_event_handler rval=%d\n",
615 instance,
616 pshot_register_events[i].event_name,
617 rval);
621 #ifdef DEBUG
622 if (pshot_event_test_enable) {
623 pshot_event_test((void *)pshot);
624 (void) timeout(pshot_event_test_post_one, (void *)pshot,
625 instance * drv_usectohz(60000000));
627 #endif
630 * allocate an ndi event handle
632 if (ndi_event_alloc_hdl(devi, NULL, &pshot->ndi_event_hdl,
633 NDI_SLEEP) != NDI_SUCCESS) {
634 goto FAIL_ATTACH;
637 pshot->ndi_events.ndi_events_version = NDI_EVENTS_REV1;
638 pshot->ndi_events.ndi_n_events = PSHOT_N_NDI_EVENTS;
639 pshot->ndi_events.ndi_event_defs = pshot_ndi_event_defs;
641 if (ndi_event_bind_set(pshot->ndi_event_hdl, &pshot->ndi_events,
642 NDI_SLEEP) != NDI_SUCCESS) {
643 cmn_err(CE_CONT, "pshot%d bind set failed\n",
644 instance);
648 * setup a test for nexus auto-attach iff we are
649 * a second level pshot node (parent == /SUNW,pshot)
650 * enable by setting "autoattach=1" in pshot.conf
652 if ((PARENT_IS_PSHOT(devi)) && (pshot_prop_autoattach != 0) &&
653 (ddi_get_instance(ddi_get_parent(devi))) == 0)
654 pshot_setup_autoattach(devi);
657 * initialize internal state to idle: busy = 0,
658 * power level = -1
660 mutex_enter(&pshot->lock);
661 pshot->busy = 0;
662 pshot->busy_ioctl = 0;
663 pshot->level = -1;
664 pshot->state &= ~STRICT_PARENT;
665 pshot->state |= PM_SUPPORTED;
666 mutex_exit(&pshot->lock);
669 * Create the "pm-want-child-notification?" property
670 * for the root node /devices/pshot
672 if (instance == 0) {
673 if (pshot_debug) {
674 cmn_err(CE_CONT, "pshot%d: DDI_ATTACH:\n\t"
675 " create the"
676 " \"pm-want-child-notification?\" property"
677 " for the root node\n", instance);
679 if (ddi_prop_create(DDI_DEV_T_NONE, devi, 0,
680 "pm-want-child-notification?", NULL, 0)
681 != DDI_PROP_SUCCESS) {
682 cmn_err(CE_WARN, "%s%d:\n\t"
683 " unable to create the"
684 " \"pm-want-child-notification?\""
685 " property", ddi_get_name(devi),
686 ddi_get_instance(devi));
688 goto FAIL_ATTACH;
693 * Check if the pm-want-child-notification? property was
694 * created in pshot_bus_config_setup_nexus() by the parent.
695 * Set the STRICT_PARENT flag if not.
697 if (ddi_prop_exists(DDI_DEV_T_ANY, devi,
698 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
699 "pm-want-child-notification?") != 1) {
700 if (pshot_debug) {
701 cmn_err(CE_CONT, "pshot%d: DDI_ATTACH:"
702 " STRICT PARENT\n", instance);
704 mutex_enter(&pshot->lock);
705 pshot->state |= STRICT_PARENT;
706 mutex_exit(&pshot->lock);
707 } else {
708 if (pshot_debug) {
709 cmn_err(CE_CONT, "pshot%d: DDI_ATTACH:"
710 " INVOLVED PARENT\n", instance);
712 mutex_enter(&pshot->lock);
713 pshot->state &= ~STRICT_PARENT;
714 mutex_exit(&pshot->lock);
718 * create the pm-components property: one component
719 * with 4 power levels.
720 * - skip for pshot@XXX,nopm and pshot@XXX,nopm_strict:
721 * "no-pm-components" property
723 if (ddi_prop_exists(DDI_DEV_T_ANY, devi,
724 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
725 "no-pm-components") == 0) {
726 if (pshot_debug) {
727 cmn_err(CE_CONT, "pshot%d: DDI_ATTACH:"
728 " create the \"pm_components\" property\n",
729 instance);
731 if (ddi_prop_update_string_array(DDI_DEV_T_NONE, devi,
732 "pm-components", pm_comp, 5) != DDI_PROP_SUCCESS) {
733 cmn_err(CE_WARN, "%s%d: DDI_ATTACH:\n\t"
734 " unable to create the \"pm-components\""
735 " property", ddi_get_name(devi),
736 ddi_get_instance(devi));
738 goto FAIL_ATTACH;
740 } else {
741 if (pshot_debug) {
742 cmn_err(CE_CONT, "pshot%d: DDI_ATTACH:"
743 " NO-PM_COMPONENTS PARENT\n", instance);
745 mutex_enter(&pshot->lock);
746 pshot->state &= ~PM_SUPPORTED;
747 mutex_exit(&pshot->lock);
751 * create the property needed to get DDI_SUSPEND
752 * and DDI_RESUME calls
754 if (pshot_debug) {
755 cmn_err(CE_CONT, "pshot%d: DDI_ATTACH:"
756 " create pm-hardware-state property\n",
757 instance);
759 if (ddi_prop_update_string(DDI_DEV_T_NONE, devi,
760 "pm-hardware-state", pm_hw_state) != DDI_PROP_SUCCESS) {
761 cmn_err(CE_WARN, "%s%d: DDI_ATTACH:\n\t"
762 " unable to create the \"pm-hardware-state\""
763 " property", ddi_get_name(devi),
764 ddi_get_instance(devi));
766 goto FAIL_ATTACH;
770 * set power level to max via pm_raise_power(),
771 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm)
773 if (pshot->state & PM_SUPPORTED) {
774 if (pshot_debug) {
775 cmn_err(CE_CONT, "pshot%d: DDI_ATTACH:"
776 " raise power to MAXPWR\n", instance);
778 if (pm_raise_power(pshot->dip, 0, MAXPWR) !=
779 DDI_SUCCESS) {
780 cmn_err(CE_WARN, "%s%d: DDI_ATTACH:"
781 " pm_raise_power failed",
782 ddi_get_name(devi),
783 ddi_get_instance(devi));
785 goto FAIL_ATTACH;
790 if (pshot_log)
791 cmn_err(CE_CONT, "pshot%d attached\n", instance);
792 ddi_report_dev(devi);
794 return (DDI_SUCCESS);
795 /*NOTREACHED*/
796 FAIL_ATTACH:
797 ddi_remove_minor_node(devi, NULL);
798 mutex_destroy(&pshot->lock);
799 ddi_soft_state_free(pshot_softstatep, instance);
800 return (DDI_FAILURE);
802 case DDI_RESUME:
803 if (pshot_debug) {
804 cmn_err(CE_CONT, "pshot%d: DDI_RESUME: resuming\n",
805 instance);
807 pshot = ddi_get_soft_state(pshot_softstatep, instance);
810 * set power level to max via pm_raise_power(),
811 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm)
813 if (pshot->state & PM_SUPPORTED) {
814 if (pshot_debug) {
815 cmn_err(CE_CONT, "pshot%d: DDI_RESUME:"
816 " raise power to MAXPWR\n", instance);
818 if (pm_raise_power(pshot->dip, 0, MAXPWR) !=
819 DDI_SUCCESS) {
820 cmn_err(CE_WARN, "%s%d: DDI_RESUME:"
821 " pm_raise_power failed",
822 ddi_get_name(devi),
823 ddi_get_instance(devi));
827 if (pshot_debug) {
828 cmn_err(CE_CONT, "pshot%d: DDI_RESUME: resumed\n",
829 instance);
831 return (DDI_SUCCESS);
833 default:
834 return (DDI_FAILURE);
838 static int
839 pshot_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
841 int instance = ddi_get_instance(devi);
842 int i, rval;
843 pshot_t *pshot = ddi_get_soft_state(pshot_softstatep, instance);
844 int level_tmp;
846 if (pshot == NULL)
847 return (DDI_FAILURE);
849 switch (cmd) {
851 case DDI_DETACH:
852 if (pshot_debug)
853 cmn_err(CE_CONT, "pshot%d: DDI_DETACH\n", instance);
855 * power off component 0
856 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm)
858 if (pshot->state & PM_SUPPORTED) {
859 if (pshot_debug) {
860 cmn_err(CE_CONT, "pshot%d: DDI_DETACH:"
861 " power off\n", instance);
863 if (pm_lower_power(pshot->dip, 0, 0) != DDI_SUCCESS) {
864 cmn_err(CE_WARN, "%s%d: DDI_DETACH:\n\t"
865 "pm_lower_power failed for comp 0 to"
866 " level 0", ddi_get_name(devi),
867 ddi_get_instance(devi));
869 return (DDI_FAILURE);
873 * Check if the power level is actually OFF.
874 * Issue pm_power_has_changed if not.
876 mutex_enter(&pshot->lock);
877 if (pshot->level != 0) {
878 if (pshot_debug) {
879 cmn_err(CE_NOTE, "pshot%d:"
880 " DDI_DETACH: power off via"
881 " pm_power_has_changed instead\n",
882 instance);
884 level_tmp = pshot->level;
885 pshot->level = 0;
886 if (pm_power_has_changed(pshot->dip, 0, 0) !=
887 DDI_SUCCESS) {
888 if (pshot_debug) {
889 cmn_err(CE_NOTE, "pshot%d:"
890 " DDI_DETACH:"
891 " pm_power_has_changed"
892 " failed\n", instance);
894 pshot->level = level_tmp;
895 mutex_exit(&pshot->lock);
897 return (DDI_FAILURE);
900 mutex_exit(&pshot->lock);
903 for (i = 0; i < PSHOT_N_DDI_EVENTS; i++) {
904 if (pshot->callback_cache[i] != NULL) {
905 rval = ddi_remove_event_handler(
906 pshot->callback_cache[i]);
907 ASSERT(rval == DDI_SUCCESS);
911 #ifdef DEBUG
912 for (i = 0; i < PSHOT_N_TEST_EVENTS; i++) {
913 if (pshot->test_callback_cache[i] != NULL) {
914 rval = ddi_remove_event_handler(
915 pshot->test_callback_cache[i]);
916 ASSERT(rval == DDI_SUCCESS);
919 #endif
920 rval = ndi_event_free_hdl(pshot->ndi_event_hdl);
921 ASSERT(rval == DDI_SUCCESS);
923 if (pshot_log)
924 cmn_err(CE_CONT, "pshot%d detached\n", instance);
926 ddi_remove_minor_node(devi, NULL);
927 mutex_destroy(&pshot->lock);
928 ddi_soft_state_free(pshot_softstatep, instance);
929 break;
931 case DDI_SUSPEND:
932 if (pshot_debug)
933 cmn_err(CE_CONT, "pshot%d: DDI_SUSPEND\n", instance);
935 * fail the suspend if FAIL_SUSPEND_FLAG is set.
936 * clear the FAIL_SUSPEND_FLAG flag
938 mutex_enter(&pshot->lock);
939 if (pshot->state & FAIL_SUSPEND_FLAG) {
940 if (pshot_debug) {
941 cmn_err(CE_CONT, "pshot%d:"
942 " FAIL_SUSPEND_FLAG set, fail suspend\n",
943 ddi_get_instance(devi));
945 pshot->state &= ~FAIL_SUSPEND_FLAG;
946 rval = DDI_FAILURE;
947 } else {
948 rval = DDI_SUCCESS;
950 mutex_exit(&pshot->lock);
953 * power OFF via pm_power_has_changed
955 mutex_enter(&pshot->lock);
956 if (pshot->state & PM_SUPPORTED) {
957 if (pshot_debug) {
958 cmn_err(CE_CONT, "pshot%d: DDI_SUSPEND:"
959 " power off via pm_power_has_changed\n",
960 instance);
962 level_tmp = pshot->level;
963 pshot->level = 0;
964 if (pm_power_has_changed(pshot->dip, 0, 0) !=
965 DDI_SUCCESS) {
966 if (pshot_debug) {
967 cmn_err(CE_NOTE, "pshot%d:"
968 " DDI_SUSPEND:"
969 " pm_power_has_changed failed\n",
970 instance);
972 pshot->level = level_tmp;
973 rval = DDI_FAILURE;
976 mutex_exit(&pshot->lock);
977 return (rval);
979 default:
980 break;
983 return (DDI_SUCCESS);
988 * returns number of bits to represent <val>
990 static size_t
991 pshot_numbits(size_t val)
993 size_t bitcnt;
995 if (val == 0)
996 return (0);
997 for (bitcnt = 1; 1 << bitcnt < val; bitcnt++)
999 return (bitcnt);
1003 * returns a minor number encoded with instance <inst> and an index <nodenum>
1004 * that identifies the minor node for this instance
1006 static minor_t
1007 pshot_minor_encode(int inst, minor_t nodenum)
1009 return (((minor_t)inst << PSHOT_NODENUM_BITS()) |
1010 (((1 << PSHOT_NODENUM_BITS()) - 1) & nodenum));
1014 * returns instance of <minor>
1016 static int
1017 pshot_minor_decode_inst(minor_t minor)
1019 return (minor >> PSHOT_NODENUM_BITS());
1023 * returns node number indexing a minor node for the instance in <minor>
1025 static minor_t
1026 pshot_minor_decode_nodenum(minor_t minor)
1028 return (minor & ((1 << PSHOT_NODENUM_BITS()) - 1));
1033 * pshot_bus_introp: pshot convert an interrupt number to an
1034 * interrupt. NO OP for pseudo drivers.
1036 /*ARGSUSED*/
1037 static int
1038 pshot_bus_introp(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
1039 ddi_intr_handle_impl_t *hdlp, void *result)
1041 return (DDI_FAILURE);
1043 static int
1044 pshot_ctl(dev_info_t *dip, dev_info_t *rdip,
1045 ddi_ctl_enum_t ctlop, void *arg, void *result)
1047 int instance;
1048 pshot_t *pshot;
1049 char *childname;
1050 int childinstance;
1051 char *name;
1052 int circ;
1053 struct attachspec *as;
1054 struct detachspec *ds;
1055 int rval = DDI_SUCCESS;
1056 int no_pm_components_child;
1058 name = ddi_get_name(dip);
1059 instance = ddi_get_instance(dip);
1060 pshot = ddi_get_soft_state(pshot_softstatep, instance);
1061 if (pshot == NULL) {
1062 return (ENXIO);
1065 switch (ctlop) {
1066 case DDI_CTLOPS_REPORTDEV:
1067 if (rdip == (dev_info_t *)0)
1068 return (DDI_FAILURE);
1069 cmn_err(CE_CONT, "?pshot-device: %s%d\n",
1070 ddi_get_name(rdip), ddi_get_instance(rdip));
1071 return (DDI_SUCCESS);
1073 case DDI_CTLOPS_INITCHILD:
1075 dev_info_t *child = (dev_info_t *)arg;
1077 if (pshot_debug) {
1078 cmn_err(CE_CONT, "initchild %s%d/%s%d state 0x%x\n",
1079 ddi_get_name(dip), ddi_get_instance(dip),
1080 ddi_node_name(child), ddi_get_instance(child),
1081 DEVI(child)->devi_state);
1084 return (pshot_initchild(dip, child));
1087 case DDI_CTLOPS_UNINITCHILD:
1089 dev_info_t *child = (dev_info_t *)arg;
1091 if (pshot_debug) {
1092 cmn_err(CE_CONT, "uninitchild %s%d/%s%d state 0x%x\n",
1093 ddi_get_name(dip), ddi_get_instance(dip),
1094 ddi_node_name(child), ddi_get_instance(child),
1095 DEVI(child)->devi_state);
1098 return (pshot_uninitchild(dip, child));
1101 case DDI_CTLOPS_DMAPMAPC:
1102 case DDI_CTLOPS_REPORTINT:
1103 case DDI_CTLOPS_REGSIZE:
1104 case DDI_CTLOPS_NREGS:
1105 case DDI_CTLOPS_SIDDEV:
1106 case DDI_CTLOPS_SLAVEONLY:
1107 case DDI_CTLOPS_AFFINITY:
1108 case DDI_CTLOPS_POKE:
1109 case DDI_CTLOPS_PEEK:
1111 * These ops correspond to functions that "shouldn't" be called
1112 * by a pseudo driver. So we whine when we're called.
1114 cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n",
1115 ddi_get_name(dip), ddi_get_instance(dip),
1116 ctlop, ddi_get_name(rdip), ddi_get_instance(rdip));
1117 return (DDI_FAILURE);
1119 case DDI_CTLOPS_ATTACH:
1121 dev_info_t *child = (dev_info_t *)rdip;
1122 childname = ddi_node_name(child);
1123 childinstance = ddi_get_instance(child);
1124 as = (struct attachspec *)arg;
1126 no_pm_components_child = 0;
1127 if (ddi_prop_exists(DDI_DEV_T_ANY, child,
1128 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
1129 "no-pm-components") == 1) {
1130 no_pm_components_child = 1;
1132 if (pshot_debug) {
1133 cmn_err(CE_CONT, "%s%d: ctl_attach %s%d [%d]\n",
1134 name, instance, childname, childinstance,
1135 no_pm_components_child);
1138 ndi_devi_enter(dip, &circ);
1140 switch (as->when) {
1141 case DDI_PRE:
1143 * Mark nexus busy before a child attaches.
1144 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm
1145 * - pshot@XXX,nopm_strict)
1147 if (!(pshot->state & PM_SUPPORTED))
1148 break;
1149 mutex_enter(&pshot->lock);
1150 ++(pshot->busy);
1151 if (pshot_debug_busy) {
1152 cmn_err(CE_CONT, "%s%d:"
1153 " ctl_attach_pre: busy for %s%d:"
1154 " busy = %d\n", name, instance,
1155 childname, childinstance,
1156 pshot->busy);
1158 mutex_exit(&pshot->lock);
1159 rval = pm_busy_component(dip, 0);
1160 ASSERT(rval == DDI_SUCCESS);
1161 break;
1162 case DDI_POST:
1164 * Mark nexus idle after a child attaches.
1165 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm).
1166 * - also skip if this is not a stict parent and
1167 * - the child is a tape device or a no-pm-components
1168 * - nexus node.
1170 if (!(pshot->state & PM_SUPPORTED) ||
1171 (strcmp(childname, "tape") == 0 &&
1172 !(pshot->state & STRICT_PARENT)) ||
1173 no_pm_components_child)
1174 break;
1175 mutex_enter(&pshot->lock);
1176 ASSERT(pshot->busy > 0);
1177 --pshot->busy;
1178 if (pshot_debug_busy) {
1179 cmn_err(CE_CONT, "%s%d:"
1180 " ctl_attach_post: idle for %s%d:"
1181 " busy = %d\n", name, instance,
1182 childname, childinstance,
1183 pshot->busy);
1185 mutex_exit(&pshot->lock);
1186 rval = pm_idle_component(dip, 0);
1187 ASSERT(rval == DDI_SUCCESS);
1188 break;
1191 ndi_devi_exit(dip, circ);
1193 return (rval);
1195 case DDI_CTLOPS_DETACH:
1197 dev_info_t *child = (dev_info_t *)rdip;
1198 childname = ddi_node_name(child);
1199 childinstance = ddi_get_instance(child);
1200 ds = (struct detachspec *)arg;
1202 no_pm_components_child = 0;
1203 if (ddi_prop_exists(DDI_DEV_T_ANY, child,
1204 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
1205 "no-pm-components") == 1) {
1206 no_pm_components_child = 1;
1208 if (pshot_debug) {
1209 cmn_err(CE_CONT,
1210 "%s%d: ctl_detach %s%d [%d]\n",
1211 name, instance, childname, childinstance,
1212 no_pm_components_child);
1215 ndi_devi_enter(dip, &circ);
1217 switch (ds->when) {
1218 case DDI_PRE:
1220 * Mark nexus busy before a child detaches.
1221 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm
1222 * - pshot@XXX,nopm_strict), or if the child is a
1223 * - no-pm-components nexus node.
1225 if (!(pshot->state & PM_SUPPORTED) ||
1226 (strcmp(childname, "tape") == 0 &&
1227 !(pshot->state & STRICT_PARENT)) ||
1228 no_pm_components_child)
1229 break;
1230 mutex_enter(&pshot->lock);
1231 ++(pshot->busy);
1232 if (pshot_debug_busy) {
1233 cmn_err(CE_CONT, "%s%d:"
1234 " ctl_detach_pre: busy for %s%d:"
1235 " busy = %d\n", name, instance,
1236 childname, childinstance,
1237 pshot->busy);
1239 mutex_exit(&pshot->lock);
1240 rval = pm_busy_component(dip, 0);
1241 ASSERT(rval == DDI_SUCCESS);
1243 break;
1244 case DDI_POST:
1246 * Mark nexus idle after a child detaches.
1247 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm)
1249 if (!(pshot->state & PM_SUPPORTED))
1250 break;
1251 mutex_enter(&pshot->lock);
1252 ASSERT(pshot->busy > 0);
1253 --pshot->busy;
1254 if (pshot_debug_busy) {
1255 cmn_err(CE_CONT, "%s%d:"
1256 " ctl_detach_post: idle for %s%d:"
1257 " busy = %d\n", name, instance,
1258 childname, childinstance,
1259 pshot->busy);
1261 mutex_exit(&pshot->lock);
1262 rval = pm_idle_component(dip, 0);
1263 ASSERT(rval == DDI_SUCCESS);
1266 * Mark the driver idle if the NO_INVOL_FLAG
1267 * is set. This is needed to make sure the
1268 * parent is idle after the child detaches
1269 * without calling pm_lower_power().
1270 * Clear the NO_INVOL_FLAG.
1271 * - also mark idle if a tape device has detached
1273 if (!(pshot->state & NO_INVOL_FLAG))
1274 break;
1275 mutex_enter(&pshot->lock);
1276 ASSERT(pshot->busy > 0);
1277 --pshot->busy;
1278 if (pshot_debug_busy) {
1279 cmn_err(CE_CONT, "%s%d:"
1280 " ctl_detach_post: NO_INVOL:"
1281 " idle for %s%d: busy = %d\n",
1282 name, instance, childname,
1283 childinstance, pshot->busy);
1285 pshot->state &= ~NO_INVOL_FLAG;
1286 mutex_exit(&pshot->lock);
1287 rval = pm_idle_component(dip, 0);
1288 ASSERT(rval == DDI_SUCCESS);
1290 break;
1293 ndi_devi_exit(dip, circ);
1295 return (rval);
1298 case DDI_CTLOPS_BTOP:
1299 case DDI_CTLOPS_BTOPR:
1300 case DDI_CTLOPS_DVMAPAGESIZE:
1301 case DDI_CTLOPS_IOMIN:
1302 case DDI_CTLOPS_PTOB:
1303 default:
1305 * The ops that we pass up (default). We pass up memory
1306 * allocation oriented ops that we receive - these may be
1307 * associated with pseudo HBA drivers below us with target
1308 * drivers below them that use ddi memory allocation
1309 * interfaces like scsi_alloc_consistent_buf.
1311 return (ddi_ctlops(dip, rdip, ctlop, arg, result));
1315 /*ARGSUSED0*/
1316 static int
1317 pshot_power(dev_info_t *dip, int cmpt, int level)
1319 pshot_t *pshot;
1320 int instance = ddi_get_instance(dip);
1321 char *name = ddi_node_name(dip);
1322 int circ;
1323 int rv;
1325 pshot = ddi_get_soft_state(pshot_softstatep, instance);
1326 if (pshot == NULL) {
1328 return (DDI_FAILURE);
1331 ndi_devi_enter(dip, &circ);
1334 * set POWER_FLAG when power() is called.
1335 * ioctl(DEVCT_PM_POWER) is a clear on read call.
1337 mutex_enter(&pshot->lock);
1338 pshot->state |= POWER_FLAG;
1340 * refuse to power OFF if the component is busy
1342 if (pshot->busy != 0 && pshot->level > level) {
1343 cmn_err(CE_WARN, "%s%d: power: REFUSING POWER LEVEL CHANGE"
1344 " (%d->%d), DEVICE NOT IDLE: busy = %d",
1345 name, instance, pshot->level, level, pshot->busy);
1346 rv = DDI_FAILURE;
1347 } else {
1348 if (pshot_debug) {
1349 cmn_err(CE_CONT, "%s%d: power: comp %d (%d->%d)\n",
1350 name, instance, cmpt, pshot->level, level);
1352 pshot->level = level;
1353 rv = DDI_SUCCESS;
1355 mutex_exit(&pshot->lock);
1357 ndi_devi_exit(dip, circ);
1359 return (rv);
1362 /*ARGSUSED0*/
1363 static int
1364 pshot_bus_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op,
1365 void *arg, void *result)
1368 int ret;
1369 int instance = ddi_get_instance(dip);
1370 char *name = ddi_node_name(dip);
1371 pshot_t *pshot;
1372 pm_bp_child_pwrchg_t *bpc;
1373 pm_bp_nexus_pwrup_t bpn;
1374 pm_bp_has_changed_t *bphc;
1375 int pwrup_res;
1376 int ret_failed = 0;
1377 int pwrup_res_failed = 0;
1379 pshot = ddi_get_soft_state(pshot_softstatep, instance);
1380 if (pshot == NULL) {
1382 return (DDI_FAILURE);
1385 switch (op) {
1386 case BUS_POWER_PRE_NOTIFICATION:
1387 bpc = (pm_bp_child_pwrchg_t *)arg;
1388 if (pshot_debug) {
1389 cmn_err(CE_CONT, "%s%d: pre_bus_power:"
1390 " %s%d comp %d (%d->%d)\n",
1391 name, instance, ddi_node_name(bpc->bpc_dip),
1392 ddi_get_instance(bpc->bpc_dip),
1393 bpc->bpc_comp, bpc->bpc_olevel,
1394 bpc->bpc_nlevel);
1398 * mark parent busy if old_level is either -1 or 0,
1399 * and new level is == MAXPWR
1400 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm)
1402 if ((bpc->bpc_comp == 0 && bpc->bpc_nlevel == MAXPWR &&
1403 bpc->bpc_olevel <= 0) && (pshot->state & PM_SUPPORTED)) {
1404 mutex_enter(&pshot->lock);
1405 ++(pshot->busy);
1406 if (pshot_debug_busy) {
1407 cmn_err(CE_CONT,
1408 "%s%d: pre_bus_power:"
1409 " busy parent for %s%d (%d->%d): "
1410 " busy = %d\n",
1411 name, instance,
1412 ddi_node_name(bpc->bpc_dip),
1413 ddi_get_instance(bpc->bpc_dip),
1414 bpc->bpc_olevel, bpc->bpc_nlevel,
1415 pshot->busy);
1417 mutex_exit(&pshot->lock);
1418 ret = pm_busy_component(dip, 0);
1419 ASSERT(ret == DDI_SUCCESS);
1423 * if new_level > 0, power up parent, if not already at
1424 * MAXPWR, via pm_busop_bus_power
1425 * - skip for the no-pm nexus (pshot@XXX,nopm)
1427 if (bpc->bpc_comp == 0 && bpc->bpc_nlevel > 0 &&
1428 pshot->level < MAXPWR && (pshot->state & PM_SUPPORTED)) {
1430 * stuff the bpn struct
1432 bpn.bpn_comp = 0;
1433 bpn.bpn_level = MAXPWR;
1434 bpn.bpn_private = bpc->bpc_private;
1435 bpn.bpn_dip = dip;
1438 * ask pm to power parent up
1440 if (pshot_debug) {
1441 cmn_err(CE_CONT, "%s%d: pre_bus_power:"
1442 " pm_busop_bus_power on parent for %s%d"
1443 " (%d->%d): enter", name, instance,
1444 ddi_node_name(bpc->bpc_dip),
1445 ddi_get_instance(bpc->bpc_dip),
1446 bpc->bpc_olevel, bpc->bpc_nlevel);
1448 ret = pm_busop_bus_power(dip, impl_arg,
1449 BUS_POWER_NEXUS_PWRUP, (void *)&bpn,
1450 (void *)&pwrup_res);
1453 * check the return status individually,
1454 * idle parent and exit if either failed.
1456 if (ret != DDI_SUCCESS) {
1457 cmn_err(CE_WARN,
1458 "%s%d: pre_bus_power:"
1459 " pm_busop_bus_power FAILED (ret) FOR"
1460 " %s%d (%d->%d)",
1461 name, instance,
1462 ddi_node_name(bpc->bpc_dip),
1463 ddi_get_instance(bpc->bpc_dip),
1464 bpc->bpc_olevel, bpc->bpc_nlevel);
1465 ret_failed = 1;
1467 if (pwrup_res != DDI_SUCCESS) {
1468 cmn_err(CE_WARN,
1469 "%s%d: pre_bus_power:"
1470 " pm_busop_bus_power FAILED (pwrup_res)"
1471 " FOR %s%d (%d->%d)",
1472 name, instance,
1473 ddi_node_name(bpc->bpc_dip),
1474 ddi_get_instance(bpc->bpc_dip),
1475 bpc->bpc_olevel, bpc->bpc_nlevel);
1476 pwrup_res_failed = 1;
1478 if (ret_failed || pwrup_res_failed) {
1480 * decrement the busy count if it
1481 * had been incremented.
1483 if ((bpc->bpc_comp == 0 &&
1484 bpc->bpc_nlevel == MAXPWR &&
1485 bpc->bpc_olevel <= 0) &&
1486 (pshot->state & PM_SUPPORTED)) {
1487 mutex_enter(&pshot->lock);
1488 ASSERT(pshot->busy > 0);
1489 --(pshot->busy);
1490 if (pshot_debug_busy) {
1491 cmn_err(CE_CONT, "%s%d:"
1492 " pm_busop_bus_power"
1493 " failed: idle parent for"
1494 " %s%d (%d->%d):"
1495 " busy = %d\n",
1496 name, instance,
1497 ddi_node_name(
1498 bpc->bpc_dip),
1499 ddi_get_instance(
1500 bpc->bpc_dip),
1501 bpc->bpc_olevel,
1502 bpc->bpc_nlevel,
1503 pshot->busy);
1505 mutex_exit(&pshot->lock);
1506 ret = pm_idle_component(dip, 0);
1507 ASSERT(ret == DDI_SUCCESS);
1509 return (DDI_FAILURE);
1511 } else {
1512 if (pshot_debug) {
1513 cmn_err(CE_CONT,
1514 "%s%d: pre_bus_power:"
1515 " pm_busop_bus_power on parent"
1516 " for %s%d (%d->%d)\n",
1517 name, instance,
1518 ddi_node_name(bpc->bpc_dip),
1519 ddi_get_instance(bpc->bpc_dip),
1520 bpc->bpc_olevel, bpc->bpc_nlevel);
1524 break;
1526 case BUS_POWER_POST_NOTIFICATION:
1527 bpc = (pm_bp_child_pwrchg_t *)arg;
1528 if (pshot_debug) {
1529 cmn_err(CE_CONT, "%s%d: post_bus_power:"
1530 " %s%d comp %d (%d->%d) result %d\n",
1531 name, instance, ddi_node_name(bpc->bpc_dip),
1532 ddi_get_instance(bpc->bpc_dip),
1533 bpc->bpc_comp, bpc->bpc_olevel,
1534 bpc->bpc_nlevel, *(int *)result);
1538 * handle pm_busop_bus_power() failure case.
1539 * mark parent idle if had been marked busy.
1540 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm)
1542 if (*(int *)result != DDI_SUCCESS) {
1543 cmn_err(CE_WARN,
1544 "pshot%d: post_bus_power_failed:"
1545 " pm_busop_bus_power FAILED FOR %s%d (%d->%d)",
1546 instance, ddi_node_name(bpc->bpc_dip),
1547 ddi_get_instance(bpc->bpc_dip),
1548 bpc->bpc_olevel, bpc->bpc_nlevel);
1550 if ((bpc->bpc_comp == 0 && bpc->bpc_nlevel == MAXPWR &&
1551 bpc->bpc_olevel <= 0) &&
1552 (pshot->state & PM_SUPPORTED)) {
1553 mutex_enter(&pshot->lock);
1554 ASSERT(pshot->busy > 0);
1555 --(pshot->busy);
1556 if (pshot_debug_busy) {
1557 cmn_err(CE_CONT, "%s%d:"
1558 " post_bus_power_failed:"
1559 " idle parent for %s%d"
1560 " (%d->%d): busy = %d\n",
1561 name, instance,
1562 ddi_node_name(bpc->bpc_dip),
1563 ddi_get_instance(bpc->bpc_dip),
1564 bpc->bpc_olevel, bpc->bpc_nlevel,
1565 pshot->busy);
1567 mutex_exit(&pshot->lock);
1568 ret = pm_idle_component(dip, 0);
1569 ASSERT(ret == DDI_SUCCESS);
1574 * Mark nexus idle when a child's comp 0
1575 * is set to level 0 from level 1, 2, or 3 only.
1576 * And only if result arg == DDI_SUCCESS.
1577 * This will leave the parent busy when the child
1578 * does not call pm_lower_power() on detach after
1579 * unsetting the NO_LOWER_POWER flag.
1580 * If so, need to notify the parent to mark itself
1581 * idle anyway, else the no-involumtary-power-cycles
1582 * test cases will report false passes!
1583 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm)
1585 if ((bpc->bpc_comp == 0 && bpc->bpc_nlevel == 0 &&
1586 !(bpc->bpc_olevel <= 0) &&
1587 *(int *)result == DDI_SUCCESS) &&
1588 (pshot->state & PM_SUPPORTED)) {
1589 mutex_enter(&pshot->lock);
1590 ASSERT(pshot->busy > 0);
1591 --(pshot->busy);
1592 if (pshot_debug_busy) {
1593 cmn_err(CE_CONT,
1594 "%s%d: post_bus_power:"
1595 " idle parent for %s%d (%d->%d):"
1596 " busy = %d\n", name, instance,
1597 ddi_node_name(bpc->bpc_dip),
1598 ddi_get_instance(bpc->bpc_dip),
1599 bpc->bpc_olevel, bpc->bpc_nlevel,
1600 pshot->busy);
1602 mutex_exit(&pshot->lock);
1603 ret = pm_idle_component(dip, 0);
1604 ASSERT(ret == DDI_SUCCESS);
1606 break;
1608 case BUS_POWER_HAS_CHANGED:
1609 bphc = (pm_bp_has_changed_t *)arg;
1610 if (pshot_debug) {
1611 cmn_err(CE_CONT, "%s%d: has_changed_bus_power:"
1612 " %s%d comp %d (%d->%d) result %d\n",
1613 name, instance, ddi_node_name(bphc->bphc_dip),
1614 ddi_get_instance(bphc->bphc_dip),
1615 bphc->bphc_comp, bphc->bphc_olevel,
1616 bphc->bphc_nlevel, *(int *)result);
1620 * Mark nexus idle when a child's comp 0
1621 * is set to level 0 from levels 1, 2, or 3 only.
1623 * If powering up child leaf/nexus nodes via
1624 * pm_power_has_changed() calls, first issue
1625 * DEVCTL_PM_BUSY_COMP ioctl to mark parent busy
1626 * before powering the parent up, then power up the
1627 * child node.
1628 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm)
1630 if ((bphc->bphc_comp == 0 && bphc->bphc_nlevel == 0 &&
1631 !(bphc->bphc_olevel <= 0)) &&
1632 pshot->state & PM_SUPPORTED) {
1633 mutex_enter(&pshot->lock);
1634 ASSERT(pshot->busy > 0);
1635 --(pshot->busy);
1636 if (pshot_debug_busy) {
1637 cmn_err(CE_CONT,
1638 "%s%d: has_changed_bus_power:"
1639 " idle parent for %s%d (%d->%d):"
1640 " busy = %d\n", name, instance,
1641 ddi_node_name(bphc->bphc_dip),
1642 ddi_get_instance(bphc->bphc_dip),
1643 bphc->bphc_olevel,
1644 bphc->bphc_nlevel, pshot->busy);
1646 mutex_exit(&pshot->lock);
1647 ret = pm_idle_component(dip, 0);
1648 ASSERT(ret == DDI_SUCCESS);
1650 break;
1652 default:
1653 return (pm_busop_bus_power(dip, impl_arg, op, arg, result));
1657 return (DDI_SUCCESS);
1660 static int
1661 pshot_initchild(dev_info_t *dip, dev_info_t *child)
1663 char name[64];
1664 char *bus_addr;
1665 char *c_nodename;
1666 int bus_id;
1667 dev_info_t *enum_child;
1668 int enum_base;
1669 int enum_extent;
1672 /* check for bus_enum node */
1674 #ifdef NOT_USED
1675 if (impl_ddi_merge_child(child) != DDI_SUCCESS)
1676 return (DDI_FAILURE);
1677 #endif
1679 enum_base = ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
1680 "busid_ebase", 0);
1682 enum_extent = ddi_prop_get_int(DDI_DEV_T_ANY, child,
1683 DDI_PROP_DONTPASS, "busid_range", 0);
1686 * bus enumeration node
1688 if ((enum_base != 0) && (enum_extent != 0)) {
1689 c_nodename = ddi_node_name(child);
1690 bus_id = enum_base;
1691 for (; bus_id < enum_extent; bus_id++) {
1692 if (ndi_devi_alloc(dip, c_nodename, DEVI_PSEUDO_NODEID,
1693 &enum_child) != NDI_SUCCESS)
1694 return (DDI_FAILURE);
1696 (void) sprintf(name, "%d", bus_id);
1697 if (ndi_prop_update_string(DDI_DEV_T_NONE, enum_child,
1698 "bus-addr", name) != DDI_PROP_SUCCESS) {
1699 (void) ndi_devi_free(enum_child);
1700 return (DDI_FAILURE);
1703 if (ndi_devi_online(enum_child, 0) !=
1704 DDI_SUCCESS) {
1705 (void) ndi_devi_free(enum_child);
1706 return (DDI_FAILURE);
1710 * fail the enumeration node itself
1712 return (DDI_FAILURE);
1715 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child, 0, "bus-addr",
1716 &bus_addr) != DDI_PROP_SUCCESS) {
1717 cmn_err(CE_WARN, "pshot_initchild: bus-addr not defined (%s)",
1718 ddi_node_name(child));
1719 return (DDI_NOT_WELL_FORMED);
1722 if (strlen(bus_addr) == 0) {
1723 cmn_err(CE_WARN, "pshot_initchild: NULL bus-addr (%s)",
1724 ddi_node_name(child));
1725 ddi_prop_free(bus_addr);
1726 return (DDI_FAILURE);
1729 if (strncmp(bus_addr, "failinit", 8) == 0) {
1730 if (pshot_debug)
1731 cmn_err(CE_CONT,
1732 "pshot%d: %s forced INITCHILD failure\n",
1733 ddi_get_instance(dip), bus_addr);
1734 ddi_prop_free(bus_addr);
1735 return (DDI_FAILURE);
1738 if (pshot_log) {
1739 cmn_err(CE_CONT, "initchild %s%d/%s@%s\n",
1740 ddi_get_name(dip), ddi_get_instance(dip),
1741 ddi_node_name(child), bus_addr);
1744 ddi_set_name_addr(child, bus_addr);
1745 ddi_prop_free(bus_addr);
1746 return (DDI_SUCCESS);
1749 /*ARGSUSED*/
1750 static int
1751 pshot_uninitchild(dev_info_t *dip, dev_info_t *child)
1753 ddi_set_name_addr(child, NULL);
1754 return (DDI_SUCCESS);
1759 * devctl IOCTL support
1761 /* ARGSUSED */
1762 static int
1763 pshot_open(dev_t *devp, int flags, int otyp, cred_t *credp)
1765 int instance;
1766 pshot_t *pshot;
1768 if (otyp != OTYP_CHR)
1769 return (EINVAL);
1771 instance = pshot_minor_decode_inst(getminor(*devp));
1772 if ((pshot = ddi_get_soft_state(pshot_softstatep, instance)) == NULL)
1773 return (ENXIO);
1776 * Access is currently determined on a per-instance basis.
1777 * If we want per-node, then need to add state and lock members to
1778 * pshot_minor_t
1780 mutex_enter(&pshot->lock);
1781 if (((flags & FEXCL) && (pshot->state & IS_OPEN)) ||
1782 (!(flags & FEXCL) && (pshot->state & IS_OPEN_EXCL))) {
1783 mutex_exit(&pshot->lock);
1784 return (EBUSY);
1786 pshot->state |= IS_OPEN;
1787 if (flags & FEXCL)
1788 pshot->state |= IS_OPEN_EXCL;
1790 if (pshot_debug)
1791 cmn_err(CE_CONT, "pshot%d open\n", instance);
1793 mutex_exit(&pshot->lock);
1794 return (0);
1798 * pshot_close
1800 /* ARGSUSED */
1801 static int
1802 pshot_close(dev_t dev, int flag, int otyp, cred_t *credp)
1804 int instance;
1805 pshot_t *pshot;
1807 if (otyp != OTYP_CHR)
1808 return (EINVAL);
1810 instance = pshot_minor_decode_inst(getminor(dev));
1811 if ((pshot = ddi_get_soft_state(pshot_softstatep, instance)) == NULL)
1812 return (ENXIO);
1814 mutex_enter(&pshot->lock);
1815 pshot->state &= ~(IS_OPEN | IS_OPEN_EXCL);
1816 mutex_exit(&pshot->lock);
1817 if (pshot_debug)
1818 cmn_err(CE_CONT, "pshot%d closed\n", instance);
1819 return (0);
1824 * pshot_ioctl: redirects to appropriate command handler based on various
1825 * criteria
1827 /* ARGSUSED */
1828 static int
1829 pshot_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
1830 int *rvalp)
1832 pshot_t *pshot;
1833 int instance;
1834 minor_t nodenum;
1835 char *nodename;
1837 instance = pshot_minor_decode_inst(getminor(dev));
1838 if ((pshot = ddi_get_soft_state(pshot_softstatep, instance)) == NULL)
1839 return (ENXIO);
1841 nodenum = pshot_minor_decode_nodenum(getminor(dev));
1842 nodename = pshot->nodes[nodenum].name;
1844 if (pshot_debug)
1845 cmn_err(CE_CONT,
1846 "pshot%d ioctl: dev=%p, cmd=%x, arg=%p, mode=%x\n",
1847 instance, (void *)dev, cmd, (void *)arg, mode);
1849 if (strcmp(nodename, PSHOT_NODENAME_DEVCTL) == 0)
1850 return (pshot_devctl(pshot, nodenum, cmd, arg, mode, credp,
1851 rvalp));
1853 if (strcmp(nodename, PSHOT_NODENAME_TESTCTL) == 0)
1854 return (pshot_testctl(pshot, nodenum, cmd, arg, mode, credp,
1855 rvalp));
1857 cmn_err(CE_WARN, "pshot_ioctl: unmatched nodename on minor %u",
1858 pshot->nodes[nodenum].minor);
1859 return (ENXIO);
1864 * pshot_devctl: handle DEVCTL operations
1866 /* ARGSUSED */
1867 static int
1868 pshot_devctl(pshot_t *pshot, minor_t nodenum, int cmd, intptr_t arg, int mode,
1869 cred_t *credp, int *rvalp)
1871 dev_info_t *self;
1872 dev_info_t *child = NULL;
1873 struct devctl_iocdata *dcp;
1874 uint_t state;
1875 int rv = 0;
1876 uint_t flags;
1877 int instance;
1878 int i;
1879 int ret;
1881 self = pshot->dip;
1883 flags = (pshot_devctl_debug) ? NDI_DEVI_DEBUG : 0;
1884 instance = pshot->instance;
1887 * We can use the generic implementation for these ioctls
1889 for (i = 0; pshot_devctls[i].ioctl_int != 0; i++) {
1890 if (pshot_devctls[i].ioctl_int == cmd) {
1891 if (pshot_debug)
1892 cmn_err(CE_CONT, "pshot%d devctl: %s",
1893 instance, pshot_devctls[i].ioctl_char);
1896 switch (cmd) {
1897 case DEVCTL_DEVICE_GETSTATE:
1898 case DEVCTL_DEVICE_ONLINE:
1899 case DEVCTL_DEVICE_OFFLINE:
1900 case DEVCTL_DEVICE_REMOVE:
1901 case DEVCTL_BUS_GETSTATE:
1902 case DEVCTL_BUS_DEV_CREATE:
1903 rv = ndi_devctl_ioctl(self, cmd, arg, mode, flags);
1904 if (pshot_debug && rv != 0) {
1905 cmn_err(CE_CONT, "pshot%d ndi_devctl_ioctl:"
1906 " failed, rv = %d", instance, rv);
1909 return (rv);
1913 * read devctl ioctl data
1915 if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
1916 return (EFAULT);
1918 switch (cmd) {
1920 case DEVCTL_DEVICE_RESET:
1921 if (pshot_debug)
1922 cmn_err(CE_CONT, "pshot%d devctl:"
1923 " DEVCTL_DEVICE_RESET\n", instance);
1924 rv = pshot_event(pshot, PSHOT_EVENT_TAG_DEV_RESET,
1925 child, (void *)self);
1926 ASSERT(rv == NDI_SUCCESS);
1927 break;
1929 case DEVCTL_BUS_QUIESCE:
1930 if (pshot_debug)
1931 cmn_err(CE_CONT, "pshot%d devctl:"
1932 " DEVCTL_BUS_QUIESCE\n", instance);
1933 if (ndi_get_bus_state(self, &state) == NDI_SUCCESS) {
1934 if (state == BUS_QUIESCED) {
1935 break;
1937 (void) ndi_set_bus_state(self, BUS_QUIESCED);
1939 rv = pshot_event(pshot, PSHOT_EVENT_TAG_BUS_QUIESCE,
1940 child, (void *)self);
1941 ASSERT(rv == NDI_SUCCESS);
1943 break;
1945 case DEVCTL_BUS_UNQUIESCE:
1946 if (pshot_debug)
1947 cmn_err(CE_CONT, "pshot%d devctl:"
1948 " DEVCTL_BUS_UNQUIESCE\n", instance);
1949 if (ndi_get_bus_state(self, &state) == NDI_SUCCESS) {
1950 if (state == BUS_ACTIVE) {
1951 break;
1956 * quiesce the bus through bus-specific means
1958 (void) ndi_set_bus_state(self, BUS_ACTIVE);
1959 rv = pshot_event(pshot, PSHOT_EVENT_TAG_BUS_UNQUIESCE,
1960 child, (void *)self);
1961 ASSERT(rv == NDI_SUCCESS);
1962 break;
1964 case DEVCTL_BUS_RESET:
1965 case DEVCTL_BUS_RESETALL:
1967 * no reset support for the pseudo bus
1968 * but if there were....
1970 rv = pshot_event(pshot, PSHOT_EVENT_TAG_BUS_RESET,
1971 child, (void *)self);
1972 ASSERT(rv == NDI_SUCCESS);
1973 break;
1976 * PM related ioctls
1978 case DEVCTL_PM_BUSY_COMP:
1980 * mark component 0 busy.
1981 * Keep track of ioctl updates to the busy count
1982 * via pshot->busy_ioctl.
1984 if (pshot_debug) {
1985 cmn_err(CE_CONT, "pshot%d devctl:"
1986 " DEVCTL_PM_BUSY_COMP\n", instance);
1988 mutex_enter(&pshot->lock);
1989 ++(pshot->busy);
1990 ++(pshot->busy_ioctl);
1991 if (pshot_debug_busy) {
1992 cmn_err(CE_CONT, "pshot%d:"
1993 " DEVCTL_PM_BUSY_COMP comp 0 busy"
1994 " %d busy_ioctl %d\n", instance, pshot->busy,
1995 pshot->busy_ioctl);
1997 mutex_exit(&pshot->lock);
1998 ret = pm_busy_component(pshot->dip, 0);
1999 ASSERT(ret == DDI_SUCCESS);
2001 break;
2003 case DEVCTL_PM_BUSY_COMP_TEST:
2005 * test bus's busy state
2007 if (pshot_debug) {
2008 cmn_err(CE_CONT, "pshot%d devctl:"
2009 " DEVCTL_PM_BUSY_COMP_TEST\n", instance);
2011 mutex_enter(&pshot->lock);
2012 state = pshot->busy;
2013 if (copyout(&state, dcp->cpyout_buf,
2014 sizeof (uint_t)) != 0) {
2015 cmn_err(CE_WARN, "pshot%d devctl:"
2016 " DEVCTL_PM_BUSY_COMP_TEST: copyout failed",
2017 instance);
2018 rv = EINVAL;
2020 if (pshot_debug_busy) {
2021 cmn_err(CE_CONT, "pshot%d: DEVCTL_PM_BUSY_COMP_TEST:"
2022 " comp 0 busy %d busy_ioctl %d\n", instance,
2023 state, pshot->busy_ioctl);
2025 mutex_exit(&pshot->lock);
2026 break;
2028 case DEVCTL_PM_IDLE_COMP:
2030 * mark component 0 idle.
2031 * NOP if pshot->busy_ioctl <= 0.
2033 if (pshot_debug) {
2034 cmn_err(CE_CONT, "pshot%d devctl:"
2035 " DEVCTL_PM_IDLE_COMP\n", instance);
2037 mutex_enter(&pshot->lock);
2038 if (pshot->busy_ioctl > 0) {
2039 ASSERT(pshot->busy > 0);
2040 --(pshot->busy);
2041 --(pshot->busy_ioctl);
2042 if (pshot_debug_busy) {
2043 cmn_err(CE_CONT, "pshot%d:"
2044 " DEVCTL_PM_IDLE_COM: comp 0"
2045 " busy %d busy_ioctl %d\n", instance,
2046 pshot->busy, pshot->busy_ioctl);
2048 mutex_exit(&pshot->lock);
2049 ret = pm_idle_component(pshot->dip, 0);
2050 ASSERT(ret == DDI_SUCCESS);
2052 } else {
2053 mutex_exit(&pshot->lock);
2055 break;
2057 case DEVCTL_PM_RAISE_PWR:
2059 * raise component 0 to full power level MAXPWR via a
2060 * pm_raise_power() call
2062 if (pshot_debug) {
2063 cmn_err(CE_CONT, "pshot%d devctl:"
2064 " DEVCTL_PM_RAISE_PWR\n", instance);
2066 if (pm_raise_power(pshot->dip, 0, MAXPWR) != DDI_SUCCESS) {
2067 rv = EINVAL;
2068 } else {
2069 mutex_enter(&pshot->lock);
2070 if (pshot_debug) {
2071 cmn_err(CE_CONT, "pshot%d:"
2072 " DEVCTL_PM_RAISE_POWER: comp 0"
2073 " to level %d\n", instance, pshot->level);
2075 mutex_exit(&pshot->lock);
2077 break;
2079 case DEVCTL_PM_LOWER_PWR:
2081 * pm_lower_power() call for negative testing
2082 * expected to fail.
2084 if (pshot_debug) {
2085 cmn_err(CE_CONT, "pshot%d devctl:"
2086 " DEVCTL_PM_LOWER_PWR\n", instance);
2088 if (pm_lower_power(pshot->dip, 0, 0) != DDI_SUCCESS) {
2089 rv = EINVAL;
2090 } else {
2091 mutex_enter(&pshot->lock);
2092 if (pshot_debug) {
2093 cmn_err(CE_CONT, "pshot%d:"
2094 " DEVCTL_PM_LOWER_POWER comp 0"
2095 " to level %d\n", instance, pshot->level);
2097 mutex_exit(&pshot->lock);
2099 break;
2101 case DEVCTL_PM_CHANGE_PWR_LOW:
2103 * inform the PM framework that component 0 has changed
2104 * power level to 0 via a pm_power_has_changed() call
2106 if (pshot_debug) {
2107 cmn_err(CE_CONT, "pshot%d devctl:"
2108 " DEVCTL_PM_CHANGE_PWR_LOW\n", instance);
2110 mutex_enter(&pshot->lock);
2111 pshot->level = 0;
2112 if (pm_power_has_changed(pshot->dip, 0, 0) != DDI_SUCCESS) {
2113 rv = EINVAL;
2114 } else {
2115 if (pshot_debug) {
2116 cmn_err(CE_CONT, "pshot%d:"
2117 " DEVCTL_PM_CHANGE_PWR_LOW comp 0 to"
2118 " level %d\n", instance, pshot->level);
2121 mutex_exit(&pshot->lock);
2122 break;
2124 case DEVCTL_PM_CHANGE_PWR_HIGH:
2126 * inform the PM framework that component 0 has changed
2127 * power level to MAXPWR via a pm_power_has_changed() call
2129 if (pshot_debug) {
2130 cmn_err(CE_CONT, "pshot%d devctl:"
2131 " DEVCTL_PM_CHANGE_PWR_HIGH\n", instance);
2133 mutex_enter(&pshot->lock);
2134 pshot->level = MAXPWR;
2135 if (pm_power_has_changed(pshot->dip, 0, MAXPWR)
2136 != DDI_SUCCESS) {
2137 rv = EINVAL;
2138 } else {
2139 if (pshot_debug) {
2140 cmn_err(CE_CONT, "pshot%d:"
2141 " DEVCTL_PM_CHANGE_PWR_HIGH comp 0 to"
2142 " level %d\n", instance, pshot->level);
2145 mutex_exit(&pshot->lock);
2146 break;
2148 case DEVCTL_PM_POWER:
2150 * test if the pshot_power() routine has been called,
2151 * then clear
2153 if (pshot_debug) {
2154 cmn_err(CE_CONT, "pshot%d devctl:"
2155 " DEVCTL_PM_POWER\n", instance);
2157 mutex_enter(&pshot->lock);
2158 state = (pshot->state & POWER_FLAG) ? 1 : 0;
2159 if (copyout(&state, dcp->cpyout_buf,
2160 sizeof (uint_t)) != 0) {
2161 cmn_err(CE_WARN, "pshot%d devctl:"
2162 " DEVCTL_PM_POWER: copyout failed",
2163 instance);
2164 rv = EINVAL;
2166 if (pshot_debug) {
2167 cmn_err(CE_CONT, "pshot%d: DEVCTL_PM_POWER:"
2168 " POWER_FLAG = %d\n", instance, state);
2170 pshot->state &= ~POWER_FLAG;
2171 mutex_exit(&pshot->lock);
2172 break;
2174 case DEVCTL_PM_FAIL_SUSPEND:
2176 * fail DDI_SUSPEND
2178 if (pshot_debug) {
2179 cmn_err(CE_CONT, "pshot%d devctl:"
2180 " DEVCTL_PM_FAIL_SUSPEND\n", instance);
2182 mutex_enter(&pshot->lock);
2183 pshot->state |= FAIL_SUSPEND_FLAG;
2184 mutex_exit(&pshot->lock);
2185 if (pshot_debug) {
2186 cmn_err(CE_CONT, "pshot%d: DEVCTL_PM_FAIL_SUSPEND\n",
2187 instance);
2189 break;
2191 case DEVCTL_PM_BUS_STRICT_TEST:
2193 * test the STRICT_PARENT flag:
2194 * set => STRICT PARENT
2195 * not set => INVOLVED PARENT
2197 mutex_enter(&pshot->lock);
2198 state = (pshot->state & STRICT_PARENT) ? 1 : 0;
2199 if (copyout(&state, dcp->cpyout_buf,
2200 sizeof (uint_t)) != 0) {
2201 cmn_err(CE_WARN, "pshot%d devctl:"
2202 " DEVCTL_PM_BUS_STRICT_TEST: copyout failed",
2203 instance);
2204 rv = EINVAL;
2206 if (pshot_debug) {
2207 cmn_err(CE_CONT, "pshot%d devctl:"
2208 " DEVCTL_PM_BUS_STRICT_TEST: type = %s\n",
2209 instance, ((state == 0) ? "INVOLVED" : "STRICT"));
2211 mutex_exit(&pshot->lock);
2212 break;
2214 case DEVCTL_PM_BUS_NO_INVOL:
2216 * Set the NO_INVOL_FLAG flag to
2217 * notify the driver that the child will not
2218 * call pm_lower_power() on detach.
2219 * The driver needs to mark itself idle twice
2220 * during DDI_CTLOPS_DETACH (post).
2222 if (pshot_debug) {
2223 cmn_err(CE_CONT, "pshot%d devctl:"
2224 " DEVCTL_PM_BUS_NO_INVOL\n", instance);
2226 mutex_enter(&pshot->lock);
2227 pshot->state |= NO_INVOL_FLAG;
2228 mutex_exit(&pshot->lock);
2229 break;
2231 default:
2232 rv = ENOTTY;
2235 ndi_dc_freehdl(dcp);
2236 return (rv);
2241 * pshot_testctl: handle other test operations
2242 * - If <cmd> is a DEVCTL cmd, then <arg> is a dev_t indicating which
2243 * child to direct the DEVCTL to, if applicable;
2244 * furthermore, any cmd here can be sent by layered ioctls (unlike
2245 * those to pshot_devctl() which must come from userland)
2247 /* ARGSUSED */
2248 static int
2249 pshot_testctl(pshot_t *pshot, minor_t nodenum, int cmd, intptr_t arg, int mode,
2250 cred_t *credp, int *rvalp)
2252 dev_info_t *self;
2253 dev_info_t *child = NULL;
2254 uint_t state;
2255 int rv = 0;
2256 int instance;
2257 int i;
2259 /* uint_t flags; */
2261 /* flags = (pshot_devctl_debug) ? NDI_DEVI_DEBUG : 0; */
2262 self = pshot->dip;
2263 instance = pshot->instance;
2265 if (cmd & DEVCTL_IOC) {
2266 child = e_ddi_hold_devi_by_dev((dev_t)arg, 0);
2269 for (i = 0; pshot_devctls[i].ioctl_int != 0; i++) {
2270 if (pshot_devctls[i].ioctl_int == cmd) {
2271 if (pshot_debug)
2272 cmn_err(CE_CONT, "pshot%d devctl: %s",
2273 instance, pshot_devctls[i].ioctl_char);
2276 switch (cmd) {
2277 case DEVCTL_DEVICE_RESET:
2278 if (pshot_debug)
2279 cmn_err(CE_CONT, "pshot%d testctl:"
2280 " DEVCTL_PM_POWER\n", instance);
2281 rv = pshot_event(pshot, PSHOT_EVENT_TAG_DEV_RESET,
2282 child, (void *)self);
2283 ASSERT(rv == NDI_SUCCESS);
2284 break;
2286 case DEVCTL_BUS_QUIESCE:
2287 if (pshot_debug)
2288 cmn_err(CE_CONT, "pshot%d testctl:"
2289 " DEVCTL_PM_POWER\n", instance);
2290 if (ndi_get_bus_state(self, &state) == NDI_SUCCESS) {
2291 if (state == BUS_QUIESCED) {
2292 break;
2294 (void) ndi_set_bus_state(self, BUS_QUIESCED);
2296 rv = pshot_event(pshot, PSHOT_EVENT_TAG_BUS_QUIESCE,
2297 child, (void *)self);
2298 ASSERT(rv == NDI_SUCCESS);
2300 break;
2302 case DEVCTL_BUS_UNQUIESCE:
2303 if (pshot_debug)
2304 cmn_err(CE_CONT, "pshot%d testctl:"
2305 " DEVCTL_PM_POWER\n", instance);
2306 if (ndi_get_bus_state(self, &state) == NDI_SUCCESS) {
2307 if (state == BUS_ACTIVE) {
2308 break;
2313 * quiesce the bus through bus-specific means
2315 (void) ndi_set_bus_state(self, BUS_ACTIVE);
2316 rv = pshot_event(pshot, PSHOT_EVENT_TAG_BUS_UNQUIESCE,
2317 child, (void *)self);
2318 ASSERT(rv == NDI_SUCCESS);
2319 break;
2321 case DEVCTL_BUS_RESET:
2322 case DEVCTL_BUS_RESETALL:
2324 * no reset support for the pseudo bus
2325 * but if there were....
2327 rv = pshot_event(pshot, PSHOT_EVENT_TAG_BUS_RESET,
2328 child, (void *)self);
2329 ASSERT(rv == NDI_SUCCESS);
2330 break;
2332 default:
2333 rv = ENOTTY;
2336 if (child != NULL)
2337 ddi_release_devi(child);
2338 return (rv);
2342 static int
2343 pshot_get_eventcookie(dev_info_t *dip, dev_info_t *rdip,
2344 char *eventname, ddi_eventcookie_t *event_cookiep)
2346 int instance = ddi_get_instance(dip);
2347 pshot_t *pshot = ddi_get_soft_state(pshot_softstatep, instance);
2349 if (pshot_debug)
2350 cmn_err(CE_CONT, "pshot%d: "
2351 "pshot_get_eventcookie:\n\t"
2352 "dip = 0x%p rdip = 0x%p (%s/%d) eventname = %s\n",
2353 instance, (void *)dip, (void *)rdip,
2354 ddi_node_name(rdip), ddi_get_instance(rdip),
2355 eventname);
2358 return (ndi_event_retrieve_cookie(pshot->ndi_event_hdl,
2359 rdip, eventname, event_cookiep, NDI_EVENT_NOPASS));
2362 static int
2363 pshot_add_eventcall(dev_info_t *dip, dev_info_t *rdip,
2364 ddi_eventcookie_t cookie,
2365 void (*callback)(), void *arg, ddi_callback_id_t *cb_id)
2367 int instance = ddi_get_instance(dip);
2368 pshot_t *pshot = ddi_get_soft_state(pshot_softstatep, instance);
2370 if (pshot_debug)
2371 cmn_err(CE_CONT, "pshot%d: "
2372 "pshot_add_eventcall:\n\t"
2373 "dip = 0x%p rdip = 0x%p (%s%d)\n\tcookie = 0x%p (%s)\n\t"
2374 "cb = 0x%p, arg = 0x%p\n",
2375 instance, (void *)dip, (void *)rdip,
2376 ddi_node_name(rdip), ddi_get_instance(rdip), (void *)cookie,
2377 NDI_EVENT_NAME(cookie), (void *)callback, arg);
2379 /* add callback to our event handle */
2380 return (ndi_event_add_callback(pshot->ndi_event_hdl, rdip,
2381 cookie, callback, arg, NDI_SLEEP, cb_id));
2384 static int
2385 pshot_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id)
2388 ndi_event_callbacks_t *cb = (ndi_event_callbacks_t *)cb_id;
2390 int instance = ddi_get_instance(dip);
2391 pshot_t *pshot = ddi_get_soft_state(pshot_softstatep, instance);
2393 ASSERT(cb);
2395 if (pshot_debug)
2396 cmn_err(CE_CONT, "pshot%d: "
2397 "pshot_remove_eventcall:\n\t"
2398 "dip = 0x%p rdip = 0x%p (%s%d)\n\tcookie = 0x%p (%s)\n",
2399 instance, (void *)dip, (void *)cb->ndi_evtcb_dip,
2400 ddi_node_name(cb->ndi_evtcb_dip),
2401 ddi_get_instance(cb->ndi_evtcb_dip),
2402 (void *)cb->ndi_evtcb_cookie,
2403 NDI_EVENT_NAME(cb->ndi_evtcb_cookie));
2405 return (ndi_event_remove_callback(pshot->ndi_event_hdl, cb_id));
2408 static int
2409 pshot_post_event(dev_info_t *dip, dev_info_t *rdip,
2410 ddi_eventcookie_t cookie, void *impl_data)
2412 int instance = ddi_get_instance(dip);
2413 pshot_t *pshot = ddi_get_soft_state(pshot_softstatep, instance);
2415 if (pshot_debug) {
2416 if (rdip) {
2417 cmn_err(CE_CONT, "pshot%d: "
2418 "pshot_post_event:\n\t"
2419 "dip = 0x%p rdip = 0x%p (%s%d\n\t"
2420 "cookie = 0x%p (%s)\n\tbus_impl = 0x%p\n",
2421 instance, (void *)dip, (void *)rdip,
2422 ddi_node_name(rdip), ddi_get_instance(rdip),
2423 (void *)cookie,
2424 NDI_EVENT_NAME(cookie), impl_data);
2425 } else {
2426 cmn_err(CE_CONT, "pshot%d: "
2427 "pshot_post_event:\n\t"
2428 "dip = 0x%p cookie = 0x%p (%s) bus_impl = 0x%p\n",
2429 instance, (void *)dip, (void *)cookie,
2430 NDI_EVENT_NAME(cookie), impl_data);
2434 /* run callbacks for this event */
2435 return (ndi_event_run_callbacks(pshot->ndi_event_hdl, rdip,
2436 cookie, impl_data));
2440 * the nexus driver will generate events
2441 * that need to go to children
2443 static int
2444 pshot_event(pshot_t *pshot, int event_tag, dev_info_t *child,
2445 void *bus_impldata)
2447 ddi_eventcookie_t cookie = ndi_event_tag_to_cookie(
2448 pshot->ndi_event_hdl, event_tag);
2450 if (pshot_debug) {
2451 if (child) {
2452 cmn_err(CE_CONT, "pshot%d: "
2453 "pshot_event: event_tag = 0x%x (%s)\n\t"
2454 "child = 0x%p (%s%d) bus_impl = 0x%p (%s%d)\n",
2455 pshot->instance, event_tag,
2456 ndi_event_tag_to_name(pshot->ndi_event_hdl,
2457 event_tag),
2458 (void *)child, ddi_node_name(child),
2459 ddi_get_instance(child), bus_impldata,
2460 ddi_node_name((dev_info_t *)bus_impldata),
2461 ddi_get_instance((dev_info_t *)bus_impldata));
2462 } else {
2463 cmn_err(CE_CONT, "pshot%d: "
2464 "pshot_event: event_tag = 0x%x (%s)\n\t"
2465 "child = NULL, bus_impl = 0x%p (%s%d)\n",
2466 pshot->instance, event_tag,
2467 ndi_event_tag_to_name(pshot->ndi_event_hdl,
2468 event_tag),
2469 bus_impldata,
2470 ddi_node_name((dev_info_t *)bus_impldata),
2471 ddi_get_instance((dev_info_t *)bus_impldata));
2475 return (ndi_event_run_callbacks(pshot->ndi_event_hdl,
2476 child, cookie, bus_impldata));
2481 * the pshot driver event notification callback
2483 static void
2484 pshot_event_cb(dev_info_t *dip, ddi_eventcookie_t cookie,
2485 void *arg, void *bus_impldata)
2487 pshot_t *pshot = (pshot_t *)arg;
2488 int event_tag;
2490 /* look up the event */
2491 event_tag = NDI_EVENT_TAG(cookie);
2493 if (pshot_debug) {
2494 cmn_err(CE_CONT, "pshot%d: "
2495 "pshot_event_cb:\n\t"
2496 "dip = 0x%p cookie = 0x%p (%s), tag = 0x%x\n\t"
2497 "arg = 0x%p bus_impl = 0x%p (%s%d)\n",
2498 pshot->instance, (void *)dip, (void *)cookie,
2499 NDI_EVENT_NAME(cookie), event_tag, arg, bus_impldata,
2500 ddi_node_name((dev_info_t *)bus_impldata),
2501 ddi_get_instance((dev_info_t *)bus_impldata));
2504 switch (event_tag) {
2505 case PSHOT_EVENT_TAG_OFFLINE:
2506 case PSHOT_EVENT_TAG_BUS_RESET:
2507 case PSHOT_EVENT_TAG_BUS_QUIESCE:
2508 case PSHOT_EVENT_TAG_BUS_UNQUIESCE:
2509 /* notify all subscribers of the this event */
2510 (void) ndi_event_run_callbacks(pshot->ndi_event_hdl,
2511 NULL, cookie, bus_impldata);
2512 if (pshot_debug) {
2513 cmn_err(CE_CONT, "pshot%d: event=%s\n\t"
2514 "pshot_event_cb\n", pshot->instance,
2515 NDI_EVENT_NAME(cookie));
2517 /*FALLTHRU*/
2518 case PSHOT_EVENT_TAG_TEST_POST:
2519 case PSHOT_EVENT_TAG_DEV_RESET:
2520 default:
2521 return;
2525 static int
2526 pshot_bus_config(dev_info_t *parent, uint_t flags,
2527 ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
2529 int rval;
2530 char *devname;
2531 char *devstr, *cname, *caddr;
2532 int devstrlen;
2533 int circ;
2534 pshot_t *pshot;
2535 int instance = ddi_get_instance(parent);
2537 if (pshot_debug) {
2538 flags |= NDI_DEVI_DEBUG;
2539 cmn_err(CE_CONT,
2540 "pshot%d: bus_config %s flags=0x%x\n",
2541 ddi_get_instance(parent),
2542 (op == BUS_CONFIG_ONE) ? (char *)arg : "", flags);
2545 pshot = ddi_get_soft_state(pshot_softstatep, instance);
2546 if (pshot == NULL) {
2548 return (NDI_FAILURE);
2552 * Hold the nexus across the bus_config
2554 ndi_devi_enter(parent, &circ);
2556 switch (op) {
2557 case BUS_CONFIG_ONE:
2560 * lookup and hold child device, create if not found
2562 devname = (char *)arg;
2563 devstrlen = strlen(devname) + 1;
2564 devstr = i_ddi_strdup(devname, KM_SLEEP);
2565 i_ddi_parse_name(devstr, &cname, &caddr, NULL);
2568 * The framework ensures that the node has
2569 * a name but each nexus is responsible for
2570 * the bus address name space. This driver
2571 * requires that a bus address be specified,
2572 * as will most nexus drivers.
2574 ASSERT(cname && strlen(cname) > 0);
2575 if (caddr == NULL || strlen(caddr) == 0) {
2576 cmn_err(CE_WARN,
2577 "pshot%d: malformed name %s (no bus address)",
2578 ddi_get_instance(parent), devname);
2579 kmem_free(devstr, devstrlen);
2580 ndi_devi_exit(parent, circ);
2581 return (NDI_FAILURE);
2585 * Handle a few special cases for testing purposes
2587 rval = pshot_bus_config_test_specials(parent,
2588 devname, cname, caddr);
2590 if (rval == NDI_SUCCESS) {
2592 * Set up either a leaf or nexus device
2594 if (strcmp(cname, "pshot") == 0) {
2595 rval = pshot_bus_config_setup_nexus(parent,
2596 cname, caddr);
2597 } else {
2598 rval = pshot_bus_config_setup_leaf(parent,
2599 cname, caddr);
2603 kmem_free(devstr, devstrlen);
2604 break;
2606 case BUS_CONFIG_DRIVER:
2607 case BUS_CONFIG_ALL:
2608 rval = NDI_SUCCESS;
2609 break;
2611 default:
2612 rval = NDI_FAILURE;
2613 break;
2616 if (rval == NDI_SUCCESS)
2617 rval = ndi_busop_bus_config(parent, flags, op, arg, childp, 0);
2619 ndi_devi_exit(parent, circ);
2621 if (pshot_debug)
2622 cmn_err(CE_CONT, "pshot%d: bus_config %s\n",
2623 ddi_get_instance(parent),
2624 (rval == NDI_SUCCESS) ? "ok" : "failed");
2626 return (rval);
2629 static int
2630 pshot_bus_unconfig(dev_info_t *parent, uint_t flags,
2631 ddi_bus_config_op_t op, void *arg)
2633 major_t major;
2634 int rval = NDI_SUCCESS;
2635 int circ;
2637 if (pshot_debug) {
2638 flags |= NDI_DEVI_DEBUG;
2639 cmn_err(CE_CONT,
2640 "pshot%d: bus_unconfig %s flags=0x%x\n",
2641 ddi_get_instance(parent),
2642 (op == BUS_UNCONFIG_ONE) ? (char *)arg : "", flags);
2646 * Hold the nexus across the bus_unconfig
2648 ndi_devi_enter(parent, &circ);
2650 switch (op) {
2651 case BUS_UNCONFIG_ONE:
2653 * Nothing special required here
2655 if (pshot_debug) {
2656 cmn_err(CE_CONT, "pshot%d: bus_unconfig:"
2657 " BUS_UNCONFIG_ONE\n", ddi_get_instance(parent));
2659 break;
2661 case BUS_UNCONFIG_DRIVER:
2662 if (pshot_debug > 0) {
2663 major = (major_t)(uintptr_t)arg;
2664 cmn_err(CE_CONT,
2665 "pshot%d: BUS_UNCONFIG_DRIVER: %s\n",
2666 ddi_get_instance(parent),
2667 ddi_major_to_name(major));
2669 break;
2671 case BUS_UNCONFIG_ALL:
2672 if (pshot_debug) {
2673 cmn_err(CE_CONT, "pshot%d: bus_unconfig:"
2674 " BUS_UNCONFIG_ALL\n", ddi_get_instance(parent));
2676 break;
2678 default:
2679 if (pshot_debug) {
2680 cmn_err(CE_CONT, "pshot%d: bus_unconfig: DEFAULT\n",
2681 ddi_get_instance(parent));
2683 rval = NDI_FAILURE;
2686 if (rval == NDI_SUCCESS)
2687 rval = ndi_busop_bus_unconfig(parent, flags, op, arg);
2689 ndi_devi_exit(parent, circ);
2691 if (pshot_debug)
2692 cmn_err(CE_CONT, "pshot%d: bus_unconfig %s\n",
2693 ddi_get_instance(parent),
2694 (rval == NDI_SUCCESS) ? "ok" : "failed");
2696 return (rval);
2699 static dev_info_t *
2700 pshot_findchild(dev_info_t *pdip, char *cname, char *caddr)
2702 dev_info_t *dip;
2703 char *addr;
2705 ASSERT(cname != NULL && caddr != NULL);
2706 ASSERT(DEVI_BUSY_OWNED(pdip));
2708 for (dip = ddi_get_child(pdip); dip != NULL;
2709 dip = ddi_get_next_sibling(dip)) {
2710 if (strcmp(cname, ddi_node_name(dip)) != 0)
2711 continue;
2713 if ((addr = ddi_get_name_addr(dip)) == NULL) {
2714 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 0,
2715 "bus-addr", &addr) == DDI_PROP_SUCCESS) {
2716 if (strcmp(caddr, addr) == 0) {
2717 ddi_prop_free(addr);
2718 return (dip);
2720 ddi_prop_free(addr);
2722 } else {
2723 if (strcmp(caddr, addr) == 0)
2724 return (dip);
2728 return (NULL);
2731 static void
2732 pshot_nexus_properties(dev_info_t *parent, dev_info_t *child, char *cname,
2733 char *caddr)
2735 char *extension;
2738 * extract the address extension
2740 extension = strstr(caddr, ",");
2741 if (extension != NULL) {
2742 ++extension;
2743 } else {
2744 extension = "null";
2748 * Create the "pm-want-child-notification?" property for all
2749 * nodes that do not have the "pm_strict" or "nopm_strict"
2750 * extension
2752 if (strcmp(extension, "pm_strict") != 0 &&
2753 strcmp(extension, "nopm_strict") != 0) {
2754 if (ddi_prop_exists(DDI_DEV_T_ANY, child,
2755 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
2756 "pm-want-child-notification?") == 0) {
2757 if (pshot_debug) {
2758 cmn_err(CE_CONT, "pshot%d:"
2759 " nexus_properties:\n\tcreate the"
2760 " \"pm-want-child-notification?\""
2761 " property for %s@%s\n",
2762 ddi_get_instance(parent), cname, caddr);
2764 if (ddi_prop_create(DDI_DEV_T_NONE, child, 0,
2765 "pm-want-child-notification?", NULL, 0)
2766 != DDI_PROP_SUCCESS) {
2767 cmn_err(CE_WARN, "pshot%d:"
2768 " nexus_properties:\n\tunable to create"
2769 " the \"pm-want-child-notification?\""
2770 " property for %s@%s",
2771 ddi_get_instance(parent), cname, caddr);
2777 * Create the "no-pm-components" property for all nodes
2778 * with extension "nopm" or "nopm_strict"
2780 if (strcmp(extension, "nopm") == 0 ||
2781 strcmp(extension, "nopm_strict") == 0) {
2782 if (ddi_prop_exists(DDI_DEV_T_ANY, child,
2783 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
2784 "no-pm-components") == 0) {
2785 if (pshot_debug) {
2786 cmn_err(CE_CONT, "pshot%d:"
2787 " nexus_properties:\n\tcreate the"
2788 " \"no-pm-components\""
2789 " property for %s@%s\n",
2790 ddi_get_instance(parent), cname, caddr);
2792 if (ddi_prop_create(DDI_DEV_T_NONE, child, 0,
2793 "no-pm-components", NULL, 0)
2794 != DDI_PROP_SUCCESS) {
2795 cmn_err(CE_WARN, "pshot%d:"
2796 " nexus_properties:\n\tunable to create"
2797 " the \"no-pm-components\""
2798 " property for %s@%s",
2799 ddi_get_instance(parent), cname, caddr);
2805 static void
2806 pshot_leaf_properties(dev_info_t *parent, dev_info_t *child, char *cname,
2807 char *caddr)
2809 char *extension;
2812 * extract the address extension
2814 extension = strstr(caddr, ",");
2815 if (extension != NULL) {
2816 ++extension;
2817 } else {
2818 extension = "null";
2822 * Create the "no-involuntary-power-cycles" property for
2823 * all leaf nodes with extension "no_invol"
2825 if (strcmp(extension, "no_invol") == 0) {
2826 if (ddi_prop_exists(DDI_DEV_T_ANY, child,
2827 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
2828 "no-involuntary-power-cycles") == 0) {
2829 if (pshot_debug) {
2830 cmn_err(CE_CONT, "pshot%d:"
2831 " leaf_properties:\n\tcreate the"
2832 " \"no-involuntary-power-cycles\""
2833 " property for %s@%s\n",
2834 ddi_get_instance(parent), cname, caddr);
2836 if (ddi_prop_create(DDI_DEV_T_NONE, child,
2837 DDI_PROP_CANSLEEP,
2838 "no-involuntary-power-cycles", NULL, 0)
2839 != DDI_PROP_SUCCESS) {
2840 cmn_err(CE_WARN, "pshot%d:"
2841 " leaf_properties:\n\tunable to create the"
2842 " \"no-involuntary-power-cycles\""
2843 " property for %s@%s",
2844 ddi_get_instance(parent), cname, caddr);
2850 * Create the "dependency-property" property for all leaf
2851 * nodes with extension "dep_prop"
2852 * to be used with the PM_ADD_DEPENDENT_PROPERTY ioctl
2854 if (strcmp(extension, "dep_prop") == 0) {
2855 if (ddi_prop_exists(DDI_DEV_T_ANY, child,
2856 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
2857 "dependency-property") == 0) {
2858 if (pshot_debug) {
2859 cmn_err(CE_CONT, "pshot%d:"
2860 " leaf_properties:\n\tcreate the"
2861 " \"dependency-property\""
2862 " property for %s@%s\n",
2863 ddi_get_instance(parent), cname, caddr);
2865 if (ddi_prop_create(DDI_DEV_T_NONE, child,
2866 DDI_PROP_CANSLEEP, "dependency-property", NULL, 0)
2867 != DDI_PROP_SUCCESS) {
2868 cmn_err(CE_WARN, "pshot%d:"
2869 " leaf_properties:\n\tunable to create the"
2870 " \"dependency-property\" property for"
2871 " %s@%s", ddi_get_instance(parent),
2872 cname, caddr);
2879 * BUS_CONFIG_ONE: setup a child nexus instance.
2881 static int
2882 pshot_bus_config_setup_nexus(dev_info_t *parent, char *cname, char *caddr)
2884 dev_info_t *child;
2885 int rval;
2887 ASSERT(parent != 0);
2888 ASSERT(cname != NULL);
2889 ASSERT(caddr != NULL);
2891 child = pshot_findchild(parent, cname, caddr);
2892 if (child) {
2893 if (pshot_debug) {
2894 cmn_err(CE_CONT,
2895 "pshot%d: bus_config one %s@%s found\n",
2896 ddi_get_instance(parent), cname, caddr);
2900 * create the "pm-want-child-notification?" property
2901 * for this child, if it doesn't already exist
2903 (void) pshot_nexus_properties(parent, child, cname, caddr);
2905 return (NDI_SUCCESS);
2908 ndi_devi_alloc_sleep(parent, cname, DEVI_SID_NODEID, &child);
2909 ASSERT(child != NULL);
2911 if (ndi_prop_update_string(DDI_DEV_T_NONE, child,
2912 "bus-addr", caddr) != DDI_PROP_SUCCESS) {
2913 cmn_err(CE_WARN, "pshot%d: _prop_update %s@%s failed",
2914 ddi_get_instance(parent), cname, caddr);
2915 (void) ndi_devi_free(child);
2916 return (NDI_FAILURE);
2919 rval = ndi_devi_bind_driver(child, 0);
2920 if (rval != NDI_SUCCESS) {
2921 cmn_err(CE_WARN, "pshot%d: bind_driver %s failed",
2922 ddi_get_instance(parent), cname);
2923 (void) ndi_devi_free(child);
2924 return (NDI_FAILURE);
2928 * create the "pm-want-child-notification?" property
2930 (void) pshot_nexus_properties(parent, child, cname, caddr);
2932 return (NDI_SUCCESS);
2936 * BUS_CONFIG_ONE: setup a child leaf device instance.
2937 * for testing purposes, we will create nodes of a variety of types.
2939 static int
2940 pshot_bus_config_setup_leaf(dev_info_t *parent, char *cname, char *caddr)
2942 dev_info_t *child;
2943 char *compat_name;
2944 char *nodetype;
2945 int rval;
2946 int i;
2948 ASSERT(parent != 0);
2949 ASSERT(cname != NULL);
2950 ASSERT(caddr != NULL);
2953 * if we already have a node with this name, return it
2955 if ((child = pshot_findchild(parent, cname, caddr)) != NULL) {
2957 * create the "no-involuntary-power-cycles" or
2958 * the "dependency-property" property, if they
2959 * don't already exit
2961 (void) pshot_leaf_properties(parent, child, cname, caddr);
2963 return (NDI_SUCCESS);
2966 ndi_devi_alloc_sleep(parent, cname, DEVI_SID_NODEID, &child);
2967 ASSERT(child != NULL);
2969 if (ndi_prop_update_string(DDI_DEV_T_NONE, child, "bus-addr",
2970 caddr) != DDI_PROP_SUCCESS) {
2971 (void) ndi_devi_free(child);
2972 return (NDI_FAILURE);
2976 * test compatible naming
2977 * if the child nodename is "cdisk", attach the list of compatible
2978 * named disks
2980 if (strcmp(cname, pshot_compat_diskname) == 0) {
2981 if ((ndi_prop_update_string_array(DDI_DEV_T_NONE,
2982 child, "compatible", (char **)pshot_compat_psramdisks,
2983 5)) != DDI_PROP_SUCCESS) {
2984 (void) ndi_devi_free(child);
2985 return (NDI_FAILURE);
2987 } else {
2988 for (i = 0; i < pshot_devices_len && pshot_devices[i].name;
2989 i++) {
2990 if (strcmp(cname, pshot_devices[i].name) == 0) {
2991 compat_name = pshot_devices[i].compat;
2992 nodetype = pshot_devices[i].nodetype;
2993 if (pshot_debug) {
2994 cmn_err(CE_CONT, "pshot%d: %s %s %s\n",
2995 ddi_get_instance(parent), cname,
2996 compat_name, nodetype);
2998 if ((ndi_prop_update_string_array(
2999 DDI_DEV_T_NONE, child, "compatible",
3000 &compat_name, 1)) != DDI_PROP_SUCCESS) {
3001 (void) ndi_devi_free(child);
3002 return (NDI_FAILURE);
3004 if ((ndi_prop_update_string(
3005 DDI_DEV_T_NONE, child, "node-type",
3006 nodetype)) != DDI_PROP_SUCCESS) {
3007 (void) ndi_devi_free(child);
3008 return (NDI_FAILURE);
3014 rval = ndi_devi_bind_driver(child, 0);
3015 if (rval != NDI_SUCCESS) {
3016 cmn_err(CE_WARN, "pshot%d: bind_driver %s failed",
3017 ddi_get_instance(parent), cname);
3018 (void) ndi_devi_free(child);
3019 return (NDI_FAILURE);
3023 * create the "no-involuntary-power-cycles" or
3024 * the "dependency-property" property
3026 (void) pshot_leaf_properties(parent, child, cname, caddr);
3028 return (NDI_SUCCESS);
3032 * Handle some special cases for testing bus_config via pshot
3034 * Match these special address formats to behavior:
3036 * err.* - induce bus_config error
3037 * delay - induce 1 second of bus_config delay time
3038 * delay,n - induce n seconds of bus_config delay time
3039 * wait - induce 1 second of bus_config wait time
3040 * wait,n - induce n seconds of bus_config wait time
3041 * failinit.* - induce error at INITCHILD
3042 * failprobe.* - induce error at probe
3043 * failattach.* - induce error at attach
3045 /*ARGSUSED*/
3046 static int
3047 pshot_bus_config_test_specials(dev_info_t *parent, char *devname,
3048 char *cname, char *caddr)
3050 char *p;
3051 int n;
3053 if (strncmp(caddr, "err", 3) == 0) {
3054 if (pshot_debug)
3055 cmn_err(CE_CONT,
3056 "pshot%d: %s forced failure\n",
3057 ddi_get_instance(parent), devname);
3058 return (NDI_FAILURE);
3062 * The delay and wait strings have the same effect.
3063 * The "wait[,]" support should be removed once the
3064 * devfs test suites are fixed.
3065 * NOTE: delay should not be called from interrupt context
3067 ASSERT(!servicing_interrupt());
3069 if (strncmp(caddr, "delay,", 6) == 0) {
3070 p = caddr+6;
3071 n = stoi(&p);
3072 if (*p != 0)
3073 n = 1;
3074 if (pshot_debug)
3075 cmn_err(CE_CONT,
3076 "pshot%d: %s delay %d second\n",
3077 ddi_get_instance(parent), devname, n);
3078 ddi_sleep(n);
3079 } else if (strncmp(caddr, "delay", 5) == 0) {
3080 if (pshot_debug)
3081 cmn_err(CE_CONT,
3082 "pshot%d: %s delay 1 second\n",
3083 ddi_get_instance(parent), devname);
3084 ddi_sleep(1);
3085 } else if (strncmp(caddr, "wait,", 5) == 0) {
3086 p = caddr+5;
3087 n = stoi(&p);
3088 if (*p != 0)
3089 n = 1;
3090 if (pshot_debug)
3091 cmn_err(CE_CONT,
3092 "pshot%d: %s wait %d second\n",
3093 ddi_get_instance(parent), devname, n);
3094 ddi_sleep(n);
3095 } else if (strncmp(caddr, "wait", 4) == 0) {
3096 if (pshot_debug)
3097 cmn_err(CE_CONT,
3098 "pshot%d: %s wait 1 second\n",
3099 ddi_get_instance(parent), devname);
3100 ddi_sleep(1);
3103 return (NDI_SUCCESS);
3107 * translate nodetype name to actual value
3109 static char *
3110 pshot_str2nt(char *str)
3112 int i;
3114 for (i = 0; pshot_nodetypes[i].name; i++) {
3115 if (strcmp(pshot_nodetypes[i].name, str) == 0)
3116 return (pshot_nodetypes[i].val);
3118 return (NULL);
3122 * grows array pointed to by <dstp>, with <src> data
3123 * <dstlen> = # elements of the original <*dstp>
3124 * <srclen> = # elements of <src>
3126 * on success, returns 0 and a pointer to the new array through <dstp> with
3127 * <srclen> + <dstlen> number of elements;
3128 * else returns non-zero
3130 * a NULL <*dstp> is OK (a NULL <dstp> is not) and so is a zero <dstlen>
3132 static int
3133 pshot_devices_grow(pshot_device_t **dstp, size_t dstlen,
3134 const pshot_device_t *src, size_t srclen)
3136 size_t i;
3137 pshot_device_t *newdst;
3139 newdst = kmem_alloc((srclen + dstlen) * sizeof (*src),
3140 KM_SLEEP);
3142 /* keep old pointers and dup new ones */
3143 if (*dstp)
3144 bcopy(*dstp, newdst, dstlen * sizeof (*src));
3145 for (i = 0; i < srclen; i++) {
3146 newdst[i + dstlen].name =
3147 i_ddi_strdup(src[i].name, KM_SLEEP);
3149 newdst[i + dstlen].nodetype =
3150 i_ddi_strdup(src[i].nodetype, KM_SLEEP);
3152 newdst[i + dstlen].compat =
3153 i_ddi_strdup(src[i].compat, KM_SLEEP);
3156 /* do last */
3157 if (*dstp)
3158 kmem_free(*dstp, dstlen * sizeof (*src));
3159 *dstp = newdst;
3160 return (0);
3164 * free a pshot_device_t array <dp> with <len> elements
3165 * null pointers within the elements are ok
3167 static void
3168 pshot_devices_free(pshot_device_t *dp, size_t len)
3170 size_t i;
3172 for (i = 0; i < len; i++) {
3173 if (dp[i].name)
3174 kmem_free(dp[i].name, strlen(dp[i].name) + 1);
3175 if (dp[i].nodetype)
3176 kmem_free(dp[i].nodetype, strlen(dp[i].nodetype) + 1);
3177 if (dp[i].compat)
3178 kmem_free(dp[i].compat, strlen(dp[i].compat) + 1);
3180 kmem_free(dp, len * sizeof (*dp));
3184 * returns an array of pshot_device_t parsed from <dip>'s properties
3186 * property structure (i.e. pshot.conf) for pshot:
3188 * corresponding | pshot_device_t array elements
3189 * pshot_device_t |
3190 * member by prop name | [0] [1] [2]
3191 * ----------------------|--------------|-------------|-----------------------
3192 * <PSHOT_PROP_DEVNAME> ="disk", "tape", "testdev";
3193 * <PSHOT_PROP_DEVNT> ="DDI_NT_BLOCK","DDI_NT_TAPE","ddi_testdev_nodetype";
3194 * <PSHOT_PROP_DEVCOMPAT>="testdrv", "testdrv", "testdrv";
3197 * if any of these properties are specified, then:
3198 * - all the members must be specified
3199 * - the number of elements for each string array property must be the same
3200 * - no empty strings allowed
3201 * - nodetypes (PSHOT_PROP_DEVNT) must be the nodetype name as specified in
3202 * sys/sunddi.h
3204 * NOTE: the pshot_nodetypes[] table should be kept in sync with the list
3205 * of ddi nodetypes. It's not normally critical to always be in sync so
3206 * keeping this up-to-date can usually be done "on-demand".
3208 * if <flags> & PSHOT_DEV_ANYNT, then custom nodetype strings are allowed.
3209 * these will be duplicated verbatim
3211 static pshot_device_t *
3212 pshot_devices_from_props(dev_info_t *dip, size_t *lenp, int flags)
3214 pshot_device_t *devarr = NULL;
3215 char **name_arr = NULL, **nt_arr = NULL, **compat_arr = NULL;
3216 uint_t name_arr_len, nt_arr_len, compat_arr_len;
3217 uint_t i;
3218 char *str;
3220 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, 0,
3221 PSHOT_PROP_DEVNAME, &name_arr, &name_arr_len) !=
3222 DDI_PROP_SUCCESS)
3223 name_arr = NULL;
3225 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, 0,
3226 PSHOT_PROP_DEVNT, &nt_arr, &nt_arr_len) !=
3227 DDI_PROP_SUCCESS)
3228 nt_arr = NULL;
3230 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, 0,
3231 PSHOT_PROP_DEVCOMPAT, &compat_arr, &compat_arr_len) !=
3232 DDI_PROP_SUCCESS)
3233 compat_arr = NULL;
3236 * warn about any incorrect usage, if specified
3238 if (!(name_arr || nt_arr || compat_arr))
3239 return (NULL);
3241 if (!(name_arr && nt_arr && compat_arr) ||
3242 (name_arr_len != nt_arr_len) ||
3243 (name_arr_len != compat_arr_len))
3244 goto FAIL;
3246 for (i = 0; i < name_arr_len; i++) {
3247 if (*name_arr[i] == '\0' ||
3248 *nt_arr[i] == '\0' ||
3249 *compat_arr[i] == '\0')
3250 goto FAIL;
3253 devarr = kmem_zalloc(name_arr_len * sizeof (*devarr), KM_SLEEP);
3254 for (i = 0; i < name_arr_len; i++) {
3255 devarr[i].name = i_ddi_strdup(name_arr[i], KM_SLEEP);
3256 devarr[i].compat = i_ddi_strdup(compat_arr[i], KM_SLEEP);
3258 if ((str = pshot_str2nt(nt_arr[i])) == NULL)
3259 if (flags & PSHOT_DEV_ANYNT)
3260 str = nt_arr[i];
3261 else
3262 goto FAIL;
3263 devarr[i].nodetype = i_ddi_strdup(str, KM_SLEEP);
3265 ddi_prop_free(name_arr);
3266 ddi_prop_free(nt_arr);
3267 ddi_prop_free(compat_arr);
3269 /* set <*lenp> ONLY on success */
3270 *lenp = name_arr_len;
3272 return (devarr);
3273 /*NOTREACHED*/
3274 FAIL:
3275 cmn_err(CE_WARN, "malformed device specification property");
3276 if (name_arr)
3277 ddi_prop_free(name_arr);
3278 if (nt_arr)
3279 ddi_prop_free(nt_arr);
3280 if (compat_arr)
3281 ddi_prop_free(compat_arr);
3282 if (devarr)
3283 pshot_devices_free(devarr, name_arr_len);
3284 return (NULL);
3288 * if global <pshot_devices> was not set up already (i.e. is NULL):
3289 * sets up global <pshot_devices> and <pshot_devices_len>,
3290 * using device properties from <dip> and global <pshot_stock_devices>.
3291 * device properties, if any, overrides pshot_stock_devices.
3293 * returns 0 on success (or if pshot_devices already set up)
3295 * INTERNAL LOCKING: <pshot_devices_lock>
3297 static int
3298 pshot_devices_setup(dev_info_t *dip)
3300 pshot_device_t *newdevs = NULL;
3301 size_t newdevs_len = 0;
3302 int rv = 0;
3304 mutex_enter(&pshot_devices_lock);
3305 if (pshot_devices != NULL)
3306 goto FAIL;
3308 ASSERT(pshot_devices_len == 0);
3310 newdevs = pshot_devices_from_props(dip, &newdevs_len, PSHOT_DEV_ANYNT);
3311 rv = pshot_devices_grow(&newdevs, newdevs_len, pshot_stock_devices,
3312 PSHOT_N_STOCK_DEVICES);
3313 if (rv != 0) {
3314 cmn_err(CE_WARN, "pshot_devices_setup: pshot_devices_grow "
3315 "failed");
3316 goto FAIL;
3318 newdevs_len += PSHOT_N_STOCK_DEVICES;
3320 pshot_devices = newdevs;
3321 pshot_devices_len = newdevs_len;
3322 rv = 0;
3323 FAIL:
3324 if (rv && newdevs)
3325 pshot_devices_free(newdevs, newdevs_len);
3326 mutex_exit(&pshot_devices_lock);
3327 return (rv);
3331 #ifdef NOTNEEDED
3332 /* ARGSUSED */
3333 static int
3334 pshot_probe_family(dev_info_t *self, ddi_probe_method_t probe_how,
3335 dev_info_t **return_dip)
3337 char name[64];
3338 uint_t bus_id;
3339 dev_info_t *child;
3341 for (bus_id = 10; bus_id < 20; bus_id++) {
3342 (void) sprintf(name, "%d", bus_id);
3343 if ((ndi_devi_alloc(self, "psramd", DEVI_SID_NODEID,
3344 &child)) != NDI_SUCCESS) {
3345 return (DDI_FAILURE);
3348 if (ndi_prop_update_string(DDI_DEV_T_NONE, child,
3349 "bus-addr", name) != DDI_PROP_SUCCESS) {
3350 (void) ndi_devi_free(child);
3351 if (return_dip != NULL)
3352 *return_dip = (dev_info_t *)NULL;
3353 return (DDI_FAILURE);
3356 if (ndi_devi_online(child, 0) != NDI_SUCCESS) {
3357 return (DDI_FAILURE);
3360 return (DDI_SUCCESS);
3363 static int
3364 strtoi(char *str)
3366 int c;
3367 int val;
3369 for (val = 0, c = *str++; c >= '0' && c <= '9'; c = *str++) {
3370 val *= 10;
3371 val += c - '0';
3373 return (val);
3376 #endif
3378 static void
3379 pshot_setup_autoattach(dev_info_t *devi)
3381 dev_info_t *l1child, *l2child;
3382 int rv;
3384 rv = ndi_devi_alloc(devi, "pshot", DEVI_SID_NODEID, &l1child);
3385 if (rv == NDI_SUCCESS) {
3386 (void) ndi_prop_update_string(DDI_DEV_T_NONE, l1child,
3387 "bus-addr", "0");
3388 rv = ndi_devi_alloc(l1child, "port", DEVI_SID_NODEID,
3389 &l2child);
3390 if (rv == NDI_SUCCESS)
3391 (void) ndi_prop_update_string(DDI_DEV_T_NONE,
3392 l2child, "bus-addr", "99");
3395 rv = ndi_devi_alloc(devi, "port", DEVI_SID_NODEID, &l1child);
3396 if (rv == NDI_SUCCESS)
3397 (void) ndi_prop_update_string(DDI_DEV_T_NONE, l1child,
3398 "bus-addr", "99");
3400 rv = ndi_devi_alloc(devi, "gen_drv", DEVI_SID_NODEID, &l1child);
3401 if (rv == NDI_SUCCESS)
3402 (void) ndi_prop_update_string(DDI_DEV_T_NONE, l1child,
3403 "bus-addr", "99");
3405 rv = ndi_devi_alloc(devi, "no_driver", DEVI_SID_NODEID, &l1child);
3406 if (rv == NDI_SUCCESS)
3407 (void) ndi_devi_alloc(l1child, "no_driver", DEVI_SID_NODEID,
3408 &l2child);
3411 #ifdef PRUNE_SNUBS
3413 #define PRUNE_THIS_NODE(d) (((d)->devi_node_name != NULL) && \
3414 (DEVI_PROM_NODE((d)->devi_nodeid)) && \
3415 ((d)->devi_addr == NULL))
3417 * test code to remove OBP nodes that have not attached
3419 static void
3420 prune_snubs(const char *name)
3422 struct dev_info *nex_dip, *cdip, *cndip;
3423 int maj;
3424 int rv;
3426 maj = ddi_name_to_major((char *)name);
3427 if (maj != -1) {
3428 nex_dip = (struct dev_info *)devnamesp[maj].dn_head;
3429 while (nex_dip != NULL) {
3430 cndip = ddi_get_child(nex_dip);
3431 while ((cdip = cndip) != NULL) {
3432 cndip = cdip->devi_sibling;
3433 if (PRUNE_THIS_NODE(cdip)) {
3434 cmn_err(CE_NOTE,
3435 "parent %s@%s pruning node %s",
3436 nex_dip->devi_node_name,
3437 nex_dip->devi_addr,
3438 cdip->devi_node_name);
3439 rv = ndi_devi_offline(cdip,
3440 NDI_DEVI_REMOVE);
3441 if (rv != NDI_SUCCESS)
3442 cmn_err(CE_NOTE,
3443 "failed to prune node, "
3444 "err %d", rv);
3447 nex_dip = nex_dip->devi_next;
3452 #endif /* PRUBE_SNUBS */
3454 #ifdef KERNEL_DEVICE_TREE_WALKER
3455 static kthread_id_t pwt;
3456 static kmutex_t pwl;
3457 static kcondvar_t pwcv;
3459 static void
3460 pshot_walk_tree()
3462 static int pshot_devnode(dev_info_t *dip, void * arg);
3464 dev_info_t *root = ddi_root_node();
3465 ddi_walk_devs(root, pshot_devnode, NULL);
3468 static void
3469 pshot_walk_thread()
3471 static void pshot_timeout(void *arg);
3472 static kthread_id_t pwt;
3474 pwt = curthread;
3475 mutex_init(&pwl, NULL, MUTEX_DRIVER, NULL);
3476 cv_init(&pwcv, NULL, CV_DRIVER, NULL);
3478 while (1) {
3479 pshot_walk_tree();
3480 mutex_enter(&pwl);
3481 (void) timeout(pshot_timeout, NULL, 5 * drv_usectohz(1000000));
3482 cv_wait(&pwcv, &pwl);
3483 mutex_exit(&pwl);
3487 static void
3488 pshot_timeout(void *arg)
3490 mutex_enter(&pwl);
3491 cv_signal(&pwcv);
3492 mutex_exit(&pwl);
3495 static int
3496 pshot_devnode(dev_info_t *dip, void *arg)
3498 dev_info_t *f_dip;
3500 if (dip != ddi_root_node()) {
3501 f_dip = ndi_devi_find((dev_info_t *)DEVI(dip)->devi_parent,
3502 DEVI(dip)->devi_node_name, DEVI(dip)->devi_addr);
3503 if (f_dip != dip) {
3504 cmn_err(CE_NOTE, "!pshot_devnode: failed lookup"
3505 "node (%s/%s@%s)\n",
3506 DEVI(DEVI(dip)->devi_parent)->devi_node_name,
3507 (DEVI(dip)->devi_node_name ?
3508 DEVI(dip)->devi_node_name : "NULL"),
3509 (DEVI(dip)->devi_addr ? DEVI(dip)->devi_addr :
3510 "NULL"));
3513 return (DDI_WALK_CONTINUE);
3515 #endif /* KERNEL_DEVICE_TREE_WALKER */
3517 #ifdef DEBUG
3518 static void
3519 pshot_event_cb_test(dev_info_t *dip, ddi_eventcookie_t cookie,
3520 void *arg, void *bus_impldata)
3522 pshot_t *softstate = (pshot_t *)arg;
3523 int event_tag;
3525 /* look up the event */
3526 event_tag = NDI_EVENT_TAG(cookie);
3527 cmn_err(CE_CONT, "pshot_event_cb_test:\n\t"
3528 "dip = 0x%p cookie = 0x%p (%s), tag = %d\n\t"
3529 "arg = 0x%p bus_impl = 0x%p\n",
3530 (void *)dip, (void *)cookie, NDI_EVENT_NAME(cookie),
3531 event_tag, (void *)softstate, (void *)bus_impldata);
3535 static void
3536 pshot_event_test(void *arg)
3538 pshot_t *pshot = (pshot_t *)arg;
3539 ndi_event_hdl_t hdl;
3540 ndi_event_set_t events;
3541 int i, rval;
3543 (void) ndi_event_alloc_hdl(pshot->dip, NULL, &hdl, NDI_SLEEP);
3545 events.ndi_events_version = NDI_EVENTS_REV1;
3546 events.ndi_n_events = PSHOT_N_TEST_EVENTS;
3547 events.ndi_event_defs = pshot_test_events;
3549 cmn_err(CE_CONT, "pshot: binding set of 8 events\n");
3550 ddi_sleep(1);
3551 rval = ndi_event_bind_set(hdl, &events, NDI_SLEEP);
3552 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval);
3554 cmn_err(CE_CONT, "pshot: binding the same set of 8 events\n");
3555 ddi_sleep(1);
3556 rval = ndi_event_bind_set(hdl, &events, NDI_SLEEP);
3557 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval);
3559 cmn_err(CE_CONT, "pshot: unbinding all events\n");
3560 ddi_sleep(1);
3561 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP);
3562 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval);
3565 cmn_err(CE_CONT, "pshot: binding one highlevel event\n");
3566 ddi_sleep(1);
3567 events.ndi_n_events = 1;
3568 events.ndi_event_defs = pshot_test_events_high;
3569 rval = ndi_event_bind_set(hdl, &events, NDI_SLEEP);
3570 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval);
3572 cmn_err(CE_CONT, "pshot: binding the same set of 8 events\n");
3573 ddi_sleep(1);
3574 events.ndi_n_events = PSHOT_N_TEST_EVENTS;
3575 events.ndi_event_defs = pshot_test_events;
3576 rval = ndi_event_bind_set(hdl, &events, NDI_SLEEP);
3577 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval);
3579 cmn_err(CE_CONT, "pshot: unbinding one highlevel event\n");
3580 ddi_sleep(1);
3581 events.ndi_n_events = 1;
3582 events.ndi_event_defs = pshot_test_events_high;
3583 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP);
3584 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval);
3586 cmn_err(CE_CONT, "pshot: binding one highlevel event\n");
3587 ddi_sleep(1);
3588 events.ndi_n_events = 1;
3589 events.ndi_event_defs = pshot_test_events_high;
3590 rval = ndi_event_bind_set(hdl, &events, NDI_SLEEP);
3591 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval);
3593 cmn_err(CE_CONT, "pshot: unbinding one highlevel event\n");
3594 ddi_sleep(1);
3595 events.ndi_n_events = 1;
3596 events.ndi_event_defs = pshot_test_events_high;
3597 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP);
3598 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval);
3600 cmn_err(CE_CONT, "pshot: binding the same set of 8 events\n");
3601 ddi_sleep(1);
3602 events.ndi_n_events = PSHOT_N_TEST_EVENTS;
3603 events.ndi_event_defs = pshot_test_events;
3604 rval = ndi_event_bind_set(hdl, &events, NDI_SLEEP);
3605 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval);
3607 cmn_err(CE_CONT, "pshot: unbinding first 2 events\n");
3608 ddi_sleep(1);
3609 events.ndi_n_events = 2;
3610 events.ndi_event_defs = pshot_test_events;
3611 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP);
3612 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval);
3614 cmn_err(CE_CONT, "pshot: unbinding first 2 events again\n");
3615 ddi_sleep(1);
3616 events.ndi_n_events = 2;
3617 events.ndi_event_defs = pshot_test_events;
3618 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP);
3619 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval);
3621 cmn_err(CE_CONT, "pshot: unbinding middle 2 events\n");
3622 ddi_sleep(1);
3623 events.ndi_n_events = 2;
3624 events.ndi_event_defs = &pshot_test_events[4];
3625 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP);
3626 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval);
3628 cmn_err(CE_CONT, "pshot: binding those 2 events back\n");
3629 ddi_sleep(1);
3630 events.ndi_n_events = 2;
3631 events.ndi_event_defs = &pshot_test_events[4];
3632 rval = ndi_event_bind_set(hdl, &events, NDI_SLEEP);
3633 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval);
3635 cmn_err(CE_CONT, "pshot: unbinding 2 events\n");
3636 ddi_sleep(1);
3637 events.ndi_n_events = 2;
3638 events.ndi_event_defs = &pshot_test_events[4];
3639 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP);
3640 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval);
3642 cmn_err(CE_CONT, "pshot: unbinding all events\n");
3643 ddi_sleep(1);
3644 events.ndi_n_events = PSHOT_N_TEST_EVENTS;
3645 events.ndi_event_defs = pshot_test_events;
3646 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP);
3647 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval);
3649 cmn_err(CE_CONT, "pshot: unbinding 1 event\n");
3650 ddi_sleep(1);
3651 events.ndi_n_events = 1;
3652 events.ndi_event_defs = &pshot_test_events[2];
3653 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP);
3654 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval);
3656 cmn_err(CE_CONT, "pshot: unbinding 1 event\n");
3657 ddi_sleep(1);
3658 events.ndi_n_events = 1;
3659 events.ndi_event_defs = &pshot_test_events[3];
3660 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP);
3661 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval);
3663 cmn_err(CE_CONT, "pshot: unbinding 1 event\n");
3664 ddi_sleep(1);
3665 events.ndi_n_events = 1;
3666 events.ndi_event_defs = &pshot_test_events[6];
3667 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP);
3668 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval);
3670 cmn_err(CE_CONT, "pshot: unbinding 1 event\n");
3671 ddi_sleep(1);
3672 events.ndi_n_events = 1;
3673 events.ndi_event_defs = &pshot_test_events[7];
3674 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP);
3675 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval);
3677 events.ndi_n_events = PSHOT_N_TEST_EVENTS;
3678 events.ndi_event_defs = pshot_test_events;
3680 cmn_err(CE_CONT, "pshot: binding set of 8 events\n");
3681 ddi_sleep(1);
3682 rval = ndi_event_bind_set(hdl, &events, NDI_SLEEP);
3683 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval);
3685 cmn_err(CE_CONT, "pshot: adding 8 callbacks\n");
3686 ddi_sleep(1);
3687 for (i = 0; i < 8; i++) {
3688 rval = ndi_event_add_callback(hdl, pshot->dip,
3689 ndi_event_tag_to_cookie(hdl,
3690 pshot_test_events[i].ndi_event_tag),
3691 pshot_event_cb_test,
3692 (void *)(uintptr_t)pshot_test_events[i].ndi_event_tag,
3693 NDI_SLEEP, &pshot->test_callback_cache[i]);
3694 ASSERT(rval == NDI_SUCCESS);
3697 cmn_err(CE_CONT, "pshot: event callbacks\n");
3699 for (i = 10; i < 18; i++) {
3700 ddi_eventcookie_t cookie = ndi_event_tag_to_cookie(hdl, i);
3702 rval = ndi_event_run_callbacks(hdl, pshot->dip, cookie,
3703 (void *)hdl);
3705 cmn_err(CE_CONT, "pshot: callback, tag=%d rval=%d\n",
3706 i, rval);
3707 ddi_sleep(1);
3710 cmn_err(CE_CONT, "pshot: redo event callbacks\n");
3712 for (i = 10; i < 18; i++) {
3713 ddi_eventcookie_t cookie = ndi_event_tag_to_cookie(hdl, i);
3715 rval = ndi_event_run_callbacks(hdl,
3716 pshot->dip, cookie, (void *)hdl);
3718 cmn_err(CE_CONT, "pshot: callback, tag=%d rval=%d\n",
3719 i, rval);
3720 ddi_sleep(1);
3723 cmn_err(CE_CONT, "pshot: removing 8 callbacks\n");
3724 ddi_sleep(1);
3726 for (i = 0; i < 8; i++) {
3727 (void) ndi_event_remove_callback(hdl,
3728 pshot->test_callback_cache[i]);
3730 pshot->test_callback_cache[i] = 0;
3733 cmn_err(CE_CONT, "pshot: freeing handle with bound set\n");
3734 ddi_sleep(1);
3736 rval = ndi_event_free_hdl(hdl);
3738 ASSERT(rval == NDI_SUCCESS);
3742 void
3743 pshot_event_test_post_one(void *arg)
3745 pshot_t *pshot = (pshot_t *)arg;
3746 int rval;
3747 ddi_eventcookie_t cookie;
3749 cmn_err(CE_CONT, "pshot%d: pshot_event_post_one event\n",
3750 pshot->instance);
3752 if (ddi_get_eventcookie(pshot->dip, PSHOT_EVENT_NAME_BUS_TEST_POST,
3753 &cookie) != DDI_SUCCESS) {
3754 cmn_err(CE_NOTE, "pshot_bus_test_post cookie not found");
3755 return;
3758 rval = ndi_post_event(pshot->dip, pshot->dip, cookie, NULL);
3760 cmn_err(CE_CONT, "pshot%d: pshot_event_post_one rval=%d\n",
3761 pshot->instance, rval);
3763 (void) timeout(pshot_event_test_post_one, (void *)pshot,
3764 pshot->instance * drv_usectohz(60000000));
3767 #endif /* DEBUG */