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]
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.
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
71 #if defined(lint) && !defined(DEBUG)
75 #include <sys/types.h>
76 #include <sys/cmn_err.h>
78 #include <sys/ddi_impldefs.h>
79 #include <sys/autoconf.h>
83 #include <sys/errno.h>
84 #include <sys/systm.h>
85 #include <sys/modctl.h>
88 #include <sys/sunddi.h>
89 #include <sys/sunndi.h>
90 #include <sys/devctl.h>
92 #include <sys/utsname.h>
93 #include <sys/pshot.h>
94 #include <sys/debug.h>
96 static int pshot_log
= 0;
97 static int pshot_devctl_debug
= 0;
98 static int pshot_debug_busy
= 0;
100 static void *pshot_softstatep
;
102 static int pshot_prop_autoattach
;
108 * device configuration data
111 /* should keep in sync with current release */
115 } pshot_nodetypes
[] = {
116 {"DDI_NT_SERIAL", DDI_NT_SERIAL
},
117 {"DDI_NT_SERIAL_MB", DDI_NT_SERIAL_MB
},
118 {"DDI_NT_SERIAL_DO", DDI_NT_SERIAL_DO
},
119 {"DDI_NT_SERIAL_MB_DO", DDI_NT_SERIAL_MB_DO
},
120 {"DDI_NT_SERIAL_LOMCON", DDI_NT_SERIAL_LOMCON
},
121 {"DDI_NT_BLOCK", DDI_NT_BLOCK
},
122 {"DDI_NT_BLOCK_CHAN", DDI_NT_BLOCK_CHAN
},
123 {"DDI_NT_BLOCK_WWN", DDI_NT_BLOCK_WWN
},
124 {"DDI_NT_BLOCK_SAS", DDI_NT_BLOCK_SAS
},
125 {"DDI_NT_CD", DDI_NT_CD
},
126 {"DDI_NT_CD_CHAN", DDI_NT_CD_CHAN
},
127 {"DDI_NT_FD", DDI_NT_FD
},
128 {"DDI_NT_ENCLOSURE", DDI_NT_ENCLOSURE
},
129 {"DDI_NT_SCSI_ENCLOSURE", DDI_NT_SCSI_ENCLOSURE
},
130 {"DDI_NT_TAPE", DDI_NT_TAPE
},
131 {"DDI_NT_NET", DDI_NT_NET
},
132 {"DDI_NT_DISPLAY", DDI_NT_DISPLAY
},
133 {"DDI_PSEUDO", DDI_PSEUDO
},
134 {"DDI_NT_AUDIO", DDI_NT_AUDIO
},
135 {"DDI_NT_MOUSE", DDI_NT_MOUSE
},
136 {"DDI_NT_KEYBOARD", DDI_NT_KEYBOARD
},
137 {"DDI_NT_PARALLEL", DDI_NT_PARALLEL
},
138 {"DDI_NT_PRINTER", DDI_NT_PRINTER
},
139 {"DDI_NT_UGEN", DDI_NT_UGEN
},
140 {"DDI_NT_NEXUS", DDI_NT_NEXUS
},
141 {"DDI_NT_SCSI_NEXUS", DDI_NT_SCSI_NEXUS
},
142 {"DDI_NT_ATTACHMENT_POINT", DDI_NT_ATTACHMENT_POINT
},
143 {"DDI_NT_SCSI_ATTACHMENT_POINT", DDI_NT_SCSI_ATTACHMENT_POINT
},
144 {"DDI_NT_PCI_ATTACHMENT_POINT", DDI_NT_PCI_ATTACHMENT_POINT
},
145 {"DDI_NT_SBD_ATTACHMENT_POINT", DDI_NT_SBD_ATTACHMENT_POINT
},
146 {"DDI_NT_FC_ATTACHMENT_POINT", DDI_NT_FC_ATTACHMENT_POINT
},
147 {"DDI_NT_USB_ATTACHMENT_POINT", DDI_NT_USB_ATTACHMENT_POINT
},
148 {"DDI_NT_BLOCK_FABRIC", DDI_NT_BLOCK_FABRIC
},
149 {"DDI_NT_AV_ASYNC", DDI_NT_AV_ASYNC
},
150 {"DDI_NT_AV_ISOCH", DDI_NT_AV_ISOCH
},
155 static char *pshot_compat_diskname
= "cdisk";
157 /* Compatible names... */
158 static char *pshot_compat_psramdisks
[] = {
167 * devices "natively" supported by pshot (i.e. included with SUNWiotu)
168 * used to initialize pshot_devices with
170 static pshot_device_t pshot_stock_devices
[] = {
171 {"disk", DDI_NT_BLOCK
, "gen_drv"},
172 {"disk_chan", DDI_NT_BLOCK_CHAN
, "gen_drv"},
173 {"disk_wwn", DDI_NT_BLOCK_WWN
, "gen_drv"},
174 {"disk_cdrom", DDI_NT_CD
, "gen_drv"},
175 {"disk_cdrom.chan", DDI_NT_CD_CHAN
, "gen_drv"},
176 /* Note: use bad_drv to force attach errors */
177 {"disk_fd", DDI_NT_FD
, "bad_drv"},
178 {"tape", DDI_NT_TAPE
, "gen_drv"},
179 {"net", DDI_NT_NET
, "gen_drv"},
180 {"display", DDI_NT_DISPLAY
, "gen_drv"},
181 {"pseudo", DDI_PSEUDO
, "gen_drv"},
182 {"audio", DDI_NT_AUDIO
, "gen_drv"},
183 {"mouse", DDI_NT_MOUSE
, "gen_drv"},
184 {"keyboard", DDI_NT_KEYBOARD
, "gen_drv"},
185 {"nexus", DDI_NT_NEXUS
, "pshot"}
187 #define PSHOT_N_STOCK_DEVICES \
188 (sizeof (pshot_stock_devices) / sizeof (pshot_device_t))
190 static pshot_device_t
*pshot_devices
= NULL
;
191 static size_t pshot_devices_len
= 0;
193 /* protects <pshot_devices>, <pshot_devices_len> */
194 static kmutex_t pshot_devices_lock
;
201 static ndi_event_definition_t pshot_ndi_event_defs
[] = {
202 { PSHOT_EVENT_TAG_OFFLINE
, PSHOT_EVENT_NAME_DEV_OFFLINE
,
203 EPL_INTERRUPT
, NDI_EVENT_POST_TO_ALL
},
205 { PSHOT_EVENT_TAG_DEV_RESET
, PSHOT_EVENT_NAME_DEV_RESET
,
206 EPL_INTERRUPT
, NDI_EVENT_POST_TO_TGT
},
208 { PSHOT_EVENT_TAG_BUS_RESET
, PSHOT_EVENT_NAME_BUS_RESET
,
209 EPL_INTERRUPT
, NDI_EVENT_POST_TO_ALL
},
211 { PSHOT_EVENT_TAG_BUS_QUIESCE
, PSHOT_EVENT_NAME_BUS_QUIESCE
,
212 EPL_INTERRUPT
, NDI_EVENT_POST_TO_ALL
},
214 { PSHOT_EVENT_TAG_BUS_UNQUIESCE
, PSHOT_EVENT_NAME_BUS_UNQUIESCE
,
215 EPL_INTERRUPT
, NDI_EVENT_POST_TO_ALL
},
217 { PSHOT_EVENT_TAG_TEST_POST
, PSHOT_EVENT_NAME_BUS_TEST_POST
,
218 EPL_INTERRUPT
, NDI_EVENT_POST_TO_TGT
}
222 #define PSHOT_N_NDI_EVENTS \
223 (sizeof (pshot_ndi_event_defs) / sizeof (ndi_event_definition_t))
227 static ndi_event_definition_t pshot_test_events
[] = {
228 { 10, "test event 0", EPL_INTERRUPT
, NDI_EVENT_POST_TO_ALL
},
229 { 11, "test event 1", EPL_KERNEL
, NDI_EVENT_POST_TO_TGT
},
230 { 12, "test event 2", EPL_INTERRUPT
, NDI_EVENT_POST_TO_TGT
},
231 { 13, "test event 3", EPL_INTERRUPT
, NDI_EVENT_POST_TO_ALL
},
232 { 14, "test event 4", EPL_KERNEL
, NDI_EVENT_POST_TO_ALL
},
233 { 15, "test event 5", EPL_INTERRUPT
, NDI_EVENT_POST_TO_ALL
},
234 { 16, "test event 6", EPL_KERNEL
, NDI_EVENT_POST_TO_ALL
},
235 { 17, "test event 7", EPL_INTERRUPT
, NDI_EVENT_POST_TO_ALL
}
238 static ndi_event_definition_t pshot_test_events_high
[] = {
239 { 20, "test event high 0", EPL_HIGHLEVEL
, NDI_EVENT_POST_TO_ALL
}
242 #define PSHOT_N_TEST_EVENTS \
243 (sizeof (pshot_test_events)/sizeof (ndi_event_definition_t))
246 struct register_events
{
248 ddi_eventcookie_t event_cookie
;
249 void (*event_callback
)
254 ddi_callback_id_t cb_id
;
257 struct register_events pshot_register_events
[] = {
258 { PSHOT_EVENT_NAME_DEV_OFFLINE
, 0, pshot_event_cb
, 0 },
259 { PSHOT_EVENT_NAME_DEV_RESET
, 0, pshot_event_cb
, 0 },
260 { PSHOT_EVENT_NAME_BUS_RESET
, 0, pshot_event_cb
, 0 },
261 { PSHOT_EVENT_NAME_BUS_QUIESCE
, 0, pshot_event_cb
, 0 },
262 { PSHOT_EVENT_NAME_BUS_UNQUIESCE
, 0, pshot_event_cb
, 0 },
263 { PSHOT_EVENT_NAME_BUS_TEST_POST
, 0, pshot_event_cb
, 0 }
266 #define PSHOT_N_DDI_EVENTS \
267 (sizeof (pshot_register_events) / sizeof (struct register_events))
272 static struct register_events pshot_register_test
[] = {
273 { "test event 0", 0, pshot_event_cb_test
, 0},
274 { "test event 1", 0, pshot_event_cb_test
, 0},
275 { "test event 2", 0, pshot_event_cb_test
, 0},
276 { "test event 3", 0, pshot_event_cb_test
, 0},
277 { "test event 4", 0, pshot_event_cb_test
, 0},
278 { "test event 5", 0, pshot_event_cb_test
, 0},
279 { "test event 6", 0, pshot_event_cb_test
, 0},
280 { "test event 7", 0, pshot_event_cb_test
, 0}
284 static struct register_events pshot_register_high_test
[] = {
285 {"test event high 0", 0, pshot_event_cb_test
, 0}
293 } pshot_devctls
[] = {
294 {DEVCTL_DEVICE_GETSTATE
, "DEVCTL_DEVICE_GETSTATE"},
295 {DEVCTL_DEVICE_ONLINE
, "DEVCTL_DEVICE_ONLINE"},
296 {DEVCTL_DEVICE_OFFLINE
, "DEVCTL_DEVICE_OFFLINE"},
297 {DEVCTL_DEVICE_REMOVE
, "DEVCTL_DEVICE_REMOVE"},
298 {DEVCTL_BUS_GETSTATE
, "DEVCTL_BUS_GETSTATE"},
299 {DEVCTL_BUS_DEV_CREATE
, "DEVCTL_BUS_DEV_CREATE"},
300 {DEVCTL_BUS_RESET
, "DEVCTL_BUS_RESET"},
301 {DEVCTL_BUS_RESETALL
, "DEVCTL_BUS_RESETALL"},
305 static struct bus_ops pshot_bus_ops
= {
306 BUSO_REV
, /* busops_rev */
307 nullbusmap
, /* bus_map */
308 NULL
, /* bus_get_intrspec */
309 NULL
, /* bus_add_interspec */
310 NULL
, /* bus_remove_interspec */
311 i_ddi_map_fault
, /* bus_map_fault */
312 NULL
, /* bus_dma_map */
313 ddi_dma_allochdl
, /* bus_dma_allochdl */
314 ddi_dma_freehdl
, /* bus_dma_freehdl */
315 ddi_dma_bindhdl
, /* bus_dma_bindhdl */
316 ddi_dma_unbindhdl
, /* bus_dma_unbindhdl */
317 ddi_dma_flush
, /* bus_dma_flush */
318 ddi_dma_win
, /* bus_dma_win */
319 ddi_dma_mctl
, /* bus_dma_ctl */
320 pshot_ctl
, /* bus_ctl */
321 ddi_bus_prop_op
, /* bus_prop_op */
322 pshot_get_eventcookie
, /* bus_get_eventcookie */
323 pshot_add_eventcall
, /* bus_add_eventcall */
324 pshot_remove_eventcall
, /* bus_remove_event */
325 pshot_post_event
, /* bus_post_event */
326 NULL
, /* bus_intr_ctl */
327 pshot_bus_config
, /* bus_config */
328 pshot_bus_unconfig
, /* bus_unconfig */
329 NULL
, /* bus_fm_init */
330 NULL
, /* bus_fm_fini */
331 NULL
, /* bus_fm_access_enter */
332 NULL
, /* bus_fm_access_exit */
333 pshot_bus_power
, /* bus_power */
334 pshot_bus_introp
/* bus_intr_op */
337 static struct cb_ops pshot_cb_ops
= {
338 pshot_open
, /* open */
339 pshot_close
, /* close */
340 nodev
, /* strategy */
345 pshot_ioctl
, /* ioctl */
350 ddi_prop_op
, /* prop_op */
351 NULL
, /* streamtab */
352 D_NEW
| D_MP
| D_HOTPLUG
, /* flags */
358 static struct dev_ops pshot_ops
= {
359 DEVO_REV
, /* devo_rev, */
361 pshot_info
, /* getinfo */
362 nulldev
, /* identify */
363 pshot_probe
, /* probe */
364 pshot_attach
, /* attach */
365 pshot_detach
, /* detach */
367 &pshot_cb_ops
, /* driver operations */
368 &pshot_bus_ops
, /* bus operations */
369 pshot_power
, /* power */
370 ddi_quiesce_not_supported
, /* devo_quiesce */
376 * Module linkage information for the kernel.
378 static struct modldrv modldrv
= {
384 static struct modlinkage modlinkage
= {
385 MODREV_1
, &modldrv
, NULL
390 * pshot_devices is set up on the first attach and destroyed on fini
392 * therefore PSHOT_PROP_DEV* properties may be set just for the root device,
393 * instead of being set globably, in pshot.conf by specifying the properties
394 * on a single line in the form:
395 * name="pshot" parent="/" <dev props ..>
396 * to unclutter a device tree snapshot.
397 * this of course produces a long single line that may wrap around several
406 rv
= ddi_soft_state_init(&pshot_softstatep
, sizeof (pshot_t
), 0);
408 if (rv
!= DDI_SUCCESS
)
411 mutex_init(&pshot_devices_lock
, NULL
, MUTEX_DRIVER
, NULL
);
412 pshot_devices
= NULL
;
413 pshot_devices_len
= 0;
415 if ((rv
= mod_install(&modlinkage
)) != 0) {
416 ddi_soft_state_fini(&pshot_softstatep
);
417 mutex_destroy(&pshot_devices_lock
);
427 if ((rv
= mod_remove(&modlinkage
)) != 0)
430 ddi_soft_state_fini(&pshot_softstatep
);
431 mutex_destroy(&pshot_devices_lock
);
433 pshot_devices_free(pshot_devices
, pshot_devices_len
);
438 _info(struct modinfo
*modinfop
)
440 return (mod_info(&modlinkage
, modinfop
));
446 pshot_probe(dev_info_t
*devi
)
448 int instance
= ddi_get_instance(devi
);
452 * Hook for tests to force probe fail
454 if (ddi_prop_lookup_string(DDI_DEV_T_ANY
, devi
, 0, "bus-addr",
455 &bus_addr
) == DDI_PROP_SUCCESS
) {
456 if (strncmp(bus_addr
, "failprobe", 9) == 0) {
458 cmn_err(CE_CONT
, "pshot%d: "
459 "%s forced probe failure\n",
461 ddi_prop_free(bus_addr
);
462 return (DDI_PROBE_FAILURE
);
464 ddi_prop_free(bus_addr
);
467 return (DDI_PROBE_SUCCESS
);
473 pshot_info(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
, void **result
)
479 minor
= getminor((dev_t
)arg
);
480 instance
= pshot_minor_decode_inst(minor
);
482 case DDI_INFO_DEVT2DEVINFO
:
483 pshot
= ddi_get_soft_state(pshot_softstatep
, instance
);
485 cmn_err(CE_WARN
, "pshot_info: get soft state failed "
486 "on minor %u, instance %d", minor
, instance
);
487 return (DDI_FAILURE
);
489 *result
= (void *)pshot
->dip
;
491 case DDI_INFO_DEVT2INSTANCE
:
492 *result
= (void *)(uintptr_t)instance
;
495 cmn_err(CE_WARN
, "pshot_info: unrecognized cmd 0x%x on "
496 "minor %u, instance %d", infocmd
, minor
, instance
);
497 return (DDI_FAILURE
);
500 return (DDI_SUCCESS
);
505 pshot_attach(dev_info_t
*devi
, ddi_attach_cmd_t cmd
)
507 int instance
= ddi_get_instance(devi
);
510 int prop_flags
= DDI_PROP_DONTPASS
| DDI_PROP_NOTPROM
;
518 char *pm_hw_state
= {"needs-suspend-resume"};
520 pshot_prop_autoattach
= ddi_prop_get_int(DDI_DEV_T_ANY
, devi
,
521 prop_flags
, "autoattach", 0);
527 cmn_err(CE_CONT
, "attach: %s%d/pshot%d\n",
528 ddi_get_name(ddi_get_parent(devi
)),
529 ddi_get_instance(ddi_get_parent(devi
)),
533 * Hook for tests to force attach fail
535 if ((ddi_prop_lookup_string(DDI_DEV_T_ANY
, devi
, 0, "bus-addr",
536 &bus_addr
) == DDI_PROP_SUCCESS
) && bus_addr
!= NULL
) {
537 if (strncmp(bus_addr
, "failattach", 10) == 0) {
539 cmn_err(CE_CONT
, "pshot%d: "
540 "%s forced attach failure\n",
542 ddi_prop_free(bus_addr
);
543 return (DDI_FAILURE
);
545 ddi_prop_free(bus_addr
);
551 if (ddi_soft_state_zalloc(pshot_softstatep
, instance
) !=
553 return (DDI_FAILURE
);
555 pshot
= ddi_get_soft_state(pshot_softstatep
, instance
);
557 pshot
->instance
= instance
;
558 mutex_init(&pshot
->lock
, NULL
, MUTEX_DRIVER
, NULL
);
560 /* set each minor, then create on dip all together */
562 i
= PSHOT_NODENUM_DEVCTL
;
563 pshot
->nodes
[i
].pshot
= pshot
;
564 pshot
->nodes
[i
].minor
= pshot_minor_encode(instance
, i
);
565 (void) strncpy(pshot
->nodes
[i
].name
, PSHOT_NODENAME_DEVCTL
,
566 PSHOT_MAX_MINOR_NAMELEN
);
568 i
= PSHOT_NODENUM_TESTCTL
;
569 pshot
->nodes
[i
].pshot
= pshot
;
570 pshot
->nodes
[i
].minor
= pshot_minor_encode(instance
, i
);
571 (void) strncpy(pshot
->nodes
[i
].name
, PSHOT_NODENAME_TESTCTL
,
572 PSHOT_MAX_MINOR_NAMELEN
);
574 /* this assumes contiguous a filling */
575 for (i
= 0; i
<= PSHOT_MAX_NODENUM
; i
++) {
576 if (ddi_create_minor_node(devi
, pshot
->nodes
[i
].name
,
577 S_IFCHR
, pshot
->nodes
[i
].minor
, DDI_NT_NEXUS
, 0) !=
579 cmn_err(CE_WARN
, "attach: cannot create "
580 "minor %s", pshot
->nodes
[i
].name
);
586 * pshot_devices setup
588 if (pshot_devices_setup(devi
)) {
589 cmn_err(CE_WARN
, "attach: pshot devices setup "
597 for (i
= 0; i
< PSHOT_N_DDI_EVENTS
; i
++) {
598 rval
= ddi_get_eventcookie(devi
,
599 pshot_register_events
[i
].event_name
,
600 &pshot_register_events
[i
].event_cookie
);
603 cmn_err(CE_CONT
, "pshot%d: event=%s:"
604 "ddi_get_eventcookie rval=%d\n",
606 pshot_register_events
[i
].event_name
, rval
);
608 if (rval
== DDI_SUCCESS
) {
609 rval
= ddi_add_event_handler(devi
,
610 pshot_register_events
[i
].event_cookie
,
611 pshot_register_events
[i
].event_callback
,
613 &pshot
->callback_cache
[i
]);
616 cmn_err(CE_CONT
, "pshot%d: event=%s: "
617 "ddi_add_event_handler rval=%d\n",
619 pshot_register_events
[i
].event_name
,
625 if (pshot_event_test_enable
) {
626 pshot_event_test((void *)pshot
);
627 (void) timeout(pshot_event_test_post_one
, (void *)pshot
,
628 instance
* drv_usectohz(60000000));
633 * allocate an ndi event handle
635 if (ndi_event_alloc_hdl(devi
, NULL
, &pshot
->ndi_event_hdl
,
636 NDI_SLEEP
) != NDI_SUCCESS
) {
640 pshot
->ndi_events
.ndi_events_version
= NDI_EVENTS_REV1
;
641 pshot
->ndi_events
.ndi_n_events
= PSHOT_N_NDI_EVENTS
;
642 pshot
->ndi_events
.ndi_event_defs
= pshot_ndi_event_defs
;
644 if (ndi_event_bind_set(pshot
->ndi_event_hdl
, &pshot
->ndi_events
,
645 NDI_SLEEP
) != NDI_SUCCESS
) {
646 cmn_err(CE_CONT
, "pshot%d bind set failed\n",
651 * setup a test for nexus auto-attach iff we are
652 * a second level pshot node (parent == /SUNW,pshot)
653 * enable by setting "autoattach=1" in pshot.conf
655 if ((PARENT_IS_PSHOT(devi
)) && (pshot_prop_autoattach
!= 0) &&
656 (ddi_get_instance(ddi_get_parent(devi
))) == 0)
657 pshot_setup_autoattach(devi
);
660 * initialize internal state to idle: busy = 0,
663 mutex_enter(&pshot
->lock
);
665 pshot
->busy_ioctl
= 0;
667 pshot
->state
&= ~STRICT_PARENT
;
668 pshot
->state
|= PM_SUPPORTED
;
669 mutex_exit(&pshot
->lock
);
672 * Create the "pm-want-child-notification?" property
673 * for the root node /devices/pshot
677 cmn_err(CE_CONT
, "pshot%d: DDI_ATTACH:\n\t"
679 " \"pm-want-child-notification?\" property"
680 " for the root node\n", instance
);
682 if (ddi_prop_create(DDI_DEV_T_NONE
, devi
, 0,
683 "pm-want-child-notification?", NULL
, 0)
684 != DDI_PROP_SUCCESS
) {
685 cmn_err(CE_WARN
, "%s%d:\n\t"
686 " unable to create the"
687 " \"pm-want-child-notification?\""
688 " property", ddi_get_name(devi
),
689 ddi_get_instance(devi
));
696 * Check if the pm-want-child-notification? property was
697 * created in pshot_bus_config_setup_nexus() by the parent.
698 * Set the STRICT_PARENT flag if not.
700 if (ddi_prop_exists(DDI_DEV_T_ANY
, devi
,
701 (DDI_PROP_DONTPASS
| DDI_PROP_NOTPROM
),
702 "pm-want-child-notification?") != 1) {
704 cmn_err(CE_CONT
, "pshot%d: DDI_ATTACH:"
705 " STRICT PARENT\n", instance
);
707 mutex_enter(&pshot
->lock
);
708 pshot
->state
|= STRICT_PARENT
;
709 mutex_exit(&pshot
->lock
);
712 cmn_err(CE_CONT
, "pshot%d: DDI_ATTACH:"
713 " INVOLVED PARENT\n", instance
);
715 mutex_enter(&pshot
->lock
);
716 pshot
->state
&= ~STRICT_PARENT
;
717 mutex_exit(&pshot
->lock
);
721 * create the pm-components property: one component
722 * with 4 power levels.
723 * - skip for pshot@XXX,nopm and pshot@XXX,nopm_strict:
724 * "no-pm-components" property
726 if (ddi_prop_exists(DDI_DEV_T_ANY
, devi
,
727 (DDI_PROP_DONTPASS
| DDI_PROP_NOTPROM
),
728 "no-pm-components") == 0) {
730 cmn_err(CE_CONT
, "pshot%d: DDI_ATTACH:"
731 " create the \"pm_components\" property\n",
734 if (ddi_prop_update_string_array(DDI_DEV_T_NONE
, devi
,
735 "pm-components", pm_comp
, 5) != DDI_PROP_SUCCESS
) {
736 cmn_err(CE_WARN
, "%s%d: DDI_ATTACH:\n\t"
737 " unable to create the \"pm-components\""
738 " property", ddi_get_name(devi
),
739 ddi_get_instance(devi
));
745 cmn_err(CE_CONT
, "pshot%d: DDI_ATTACH:"
746 " NO-PM_COMPONENTS PARENT\n", instance
);
748 mutex_enter(&pshot
->lock
);
749 pshot
->state
&= ~PM_SUPPORTED
;
750 mutex_exit(&pshot
->lock
);
754 * create the property needed to get DDI_SUSPEND
755 * and DDI_RESUME calls
758 cmn_err(CE_CONT
, "pshot%d: DDI_ATTACH:"
759 " create pm-hardware-state property\n",
762 if (ddi_prop_update_string(DDI_DEV_T_NONE
, devi
,
763 "pm-hardware-state", pm_hw_state
) != DDI_PROP_SUCCESS
) {
764 cmn_err(CE_WARN
, "%s%d: DDI_ATTACH:\n\t"
765 " unable to create the \"pm-hardware-state\""
766 " property", ddi_get_name(devi
),
767 ddi_get_instance(devi
));
773 * set power level to max via pm_raise_power(),
774 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm)
776 if (pshot
->state
& PM_SUPPORTED
) {
778 cmn_err(CE_CONT
, "pshot%d: DDI_ATTACH:"
779 " raise power to MAXPWR\n", instance
);
781 if (pm_raise_power(pshot
->dip
, 0, MAXPWR
) !=
783 cmn_err(CE_WARN
, "%s%d: DDI_ATTACH:"
784 " pm_raise_power failed",
786 ddi_get_instance(devi
));
794 cmn_err(CE_CONT
, "pshot%d attached\n", instance
);
795 ddi_report_dev(devi
);
797 return (DDI_SUCCESS
);
800 ddi_remove_minor_node(devi
, NULL
);
801 mutex_destroy(&pshot
->lock
);
802 ddi_soft_state_free(pshot_softstatep
, instance
);
803 return (DDI_FAILURE
);
807 cmn_err(CE_CONT
, "pshot%d: DDI_RESUME: resuming\n",
810 pshot
= ddi_get_soft_state(pshot_softstatep
, instance
);
813 * set power level to max via pm_raise_power(),
814 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm)
816 if (pshot
->state
& PM_SUPPORTED
) {
818 cmn_err(CE_CONT
, "pshot%d: DDI_RESUME:"
819 " raise power to MAXPWR\n", instance
);
821 if (pm_raise_power(pshot
->dip
, 0, MAXPWR
) !=
823 cmn_err(CE_WARN
, "%s%d: DDI_RESUME:"
824 " pm_raise_power failed",
826 ddi_get_instance(devi
));
831 cmn_err(CE_CONT
, "pshot%d: DDI_RESUME: resumed\n",
834 return (DDI_SUCCESS
);
837 return (DDI_FAILURE
);
842 pshot_detach(dev_info_t
*devi
, ddi_detach_cmd_t cmd
)
844 int instance
= ddi_get_instance(devi
);
846 pshot_t
*pshot
= ddi_get_soft_state(pshot_softstatep
, instance
);
850 return (DDI_FAILURE
);
856 cmn_err(CE_CONT
, "pshot%d: DDI_DETACH\n", instance
);
858 * power off component 0
859 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm)
861 if (pshot
->state
& PM_SUPPORTED
) {
863 cmn_err(CE_CONT
, "pshot%d: DDI_DETACH:"
864 " power off\n", instance
);
866 if (pm_lower_power(pshot
->dip
, 0, 0) != DDI_SUCCESS
) {
867 cmn_err(CE_WARN
, "%s%d: DDI_DETACH:\n\t"
868 "pm_lower_power failed for comp 0 to"
869 " level 0", ddi_get_name(devi
),
870 ddi_get_instance(devi
));
872 return (DDI_FAILURE
);
876 * Check if the power level is actually OFF.
877 * Issue pm_power_has_changed if not.
879 mutex_enter(&pshot
->lock
);
880 if (pshot
->level
!= 0) {
882 cmn_err(CE_NOTE
, "pshot%d:"
883 " DDI_DETACH: power off via"
884 " pm_power_has_changed instead\n",
887 level_tmp
= pshot
->level
;
889 if (pm_power_has_changed(pshot
->dip
, 0, 0) !=
892 cmn_err(CE_NOTE
, "pshot%d:"
894 " pm_power_has_changed"
895 " failed\n", instance
);
897 pshot
->level
= level_tmp
;
898 mutex_exit(&pshot
->lock
);
900 return (DDI_FAILURE
);
903 mutex_exit(&pshot
->lock
);
906 for (i
= 0; i
< PSHOT_N_DDI_EVENTS
; i
++) {
907 if (pshot
->callback_cache
[i
] != NULL
) {
908 rval
= ddi_remove_event_handler(
909 pshot
->callback_cache
[i
]);
910 ASSERT(rval
== DDI_SUCCESS
);
915 for (i
= 0; i
< PSHOT_N_TEST_EVENTS
; i
++) {
916 if (pshot
->test_callback_cache
[i
] != NULL
) {
917 rval
= ddi_remove_event_handler(
918 pshot
->test_callback_cache
[i
]);
919 ASSERT(rval
== DDI_SUCCESS
);
923 rval
= ndi_event_free_hdl(pshot
->ndi_event_hdl
);
924 ASSERT(rval
== DDI_SUCCESS
);
927 cmn_err(CE_CONT
, "pshot%d detached\n", instance
);
929 ddi_remove_minor_node(devi
, NULL
);
930 mutex_destroy(&pshot
->lock
);
931 ddi_soft_state_free(pshot_softstatep
, instance
);
936 cmn_err(CE_CONT
, "pshot%d: DDI_SUSPEND\n", instance
);
938 * fail the suspend if FAIL_SUSPEND_FLAG is set.
939 * clear the FAIL_SUSPEND_FLAG flag
941 mutex_enter(&pshot
->lock
);
942 if (pshot
->state
& FAIL_SUSPEND_FLAG
) {
944 cmn_err(CE_CONT
, "pshot%d:"
945 " FAIL_SUSPEND_FLAG set, fail suspend\n",
946 ddi_get_instance(devi
));
948 pshot
->state
&= ~FAIL_SUSPEND_FLAG
;
953 mutex_exit(&pshot
->lock
);
956 * power OFF via pm_power_has_changed
958 mutex_enter(&pshot
->lock
);
959 if (pshot
->state
& PM_SUPPORTED
) {
961 cmn_err(CE_CONT
, "pshot%d: DDI_SUSPEND:"
962 " power off via pm_power_has_changed\n",
965 level_tmp
= pshot
->level
;
967 if (pm_power_has_changed(pshot
->dip
, 0, 0) !=
970 cmn_err(CE_NOTE
, "pshot%d:"
972 " pm_power_has_changed failed\n",
975 pshot
->level
= level_tmp
;
979 mutex_exit(&pshot
->lock
);
986 return (DDI_SUCCESS
);
991 * returns number of bits to represent <val>
994 pshot_numbits(size_t val
)
1000 for (bitcnt
= 1; 1 << bitcnt
< val
; bitcnt
++)
1006 * returns a minor number encoded with instance <inst> and an index <nodenum>
1007 * that identifies the minor node for this instance
1010 pshot_minor_encode(int inst
, minor_t nodenum
)
1012 return (((minor_t
)inst
<< PSHOT_NODENUM_BITS()) |
1013 (((1 << PSHOT_NODENUM_BITS()) - 1) & nodenum
));
1017 * returns instance of <minor>
1020 pshot_minor_decode_inst(minor_t minor
)
1022 return (minor
>> PSHOT_NODENUM_BITS());
1026 * returns node number indexing a minor node for the instance in <minor>
1029 pshot_minor_decode_nodenum(minor_t minor
)
1031 return (minor
& ((1 << PSHOT_NODENUM_BITS()) - 1));
1036 * pshot_bus_introp: pshot convert an interrupt number to an
1037 * interrupt. NO OP for pseudo drivers.
1041 pshot_bus_introp(dev_info_t
*dip
, dev_info_t
*rdip
, ddi_intr_op_t intr_op
,
1042 ddi_intr_handle_impl_t
*hdlp
, void *result
)
1044 return (DDI_FAILURE
);
1047 pshot_ctl(dev_info_t
*dip
, dev_info_t
*rdip
,
1048 ddi_ctl_enum_t ctlop
, void *arg
, void *result
)
1056 struct attachspec
*as
;
1057 struct detachspec
*ds
;
1058 int rval
= DDI_SUCCESS
;
1059 int no_pm_components_child
;
1061 name
= ddi_get_name(dip
);
1062 instance
= ddi_get_instance(dip
);
1063 pshot
= ddi_get_soft_state(pshot_softstatep
, instance
);
1064 if (pshot
== NULL
) {
1069 case DDI_CTLOPS_REPORTDEV
:
1070 if (rdip
== (dev_info_t
*)0)
1071 return (DDI_FAILURE
);
1072 cmn_err(CE_CONT
, "?pshot-device: %s%d\n",
1073 ddi_get_name(rdip
), ddi_get_instance(rdip
));
1074 return (DDI_SUCCESS
);
1076 case DDI_CTLOPS_INITCHILD
:
1078 dev_info_t
*child
= (dev_info_t
*)arg
;
1081 cmn_err(CE_CONT
, "initchild %s%d/%s%d state 0x%x\n",
1082 ddi_get_name(dip
), ddi_get_instance(dip
),
1083 ddi_node_name(child
), ddi_get_instance(child
),
1084 DEVI(child
)->devi_state
);
1087 return (pshot_initchild(dip
, child
));
1090 case DDI_CTLOPS_UNINITCHILD
:
1092 dev_info_t
*child
= (dev_info_t
*)arg
;
1095 cmn_err(CE_CONT
, "uninitchild %s%d/%s%d state 0x%x\n",
1096 ddi_get_name(dip
), ddi_get_instance(dip
),
1097 ddi_node_name(child
), ddi_get_instance(child
),
1098 DEVI(child
)->devi_state
);
1101 return (pshot_uninitchild(dip
, child
));
1104 case DDI_CTLOPS_DMAPMAPC
:
1105 case DDI_CTLOPS_REPORTINT
:
1106 case DDI_CTLOPS_REGSIZE
:
1107 case DDI_CTLOPS_NREGS
:
1108 case DDI_CTLOPS_SIDDEV
:
1109 case DDI_CTLOPS_SLAVEONLY
:
1110 case DDI_CTLOPS_AFFINITY
:
1111 case DDI_CTLOPS_POKE
:
1112 case DDI_CTLOPS_PEEK
:
1114 * These ops correspond to functions that "shouldn't" be called
1115 * by a pseudo driver. So we whine when we're called.
1117 cmn_err(CE_CONT
, "%s%d: invalid op (%d) from %s%d\n",
1118 ddi_get_name(dip
), ddi_get_instance(dip
),
1119 ctlop
, ddi_get_name(rdip
), ddi_get_instance(rdip
));
1120 return (DDI_FAILURE
);
1122 case DDI_CTLOPS_ATTACH
:
1124 dev_info_t
*child
= (dev_info_t
*)rdip
;
1125 childname
= ddi_node_name(child
);
1126 childinstance
= ddi_get_instance(child
);
1127 as
= (struct attachspec
*)arg
;
1129 no_pm_components_child
= 0;
1130 if (ddi_prop_exists(DDI_DEV_T_ANY
, child
,
1131 (DDI_PROP_DONTPASS
| DDI_PROP_NOTPROM
),
1132 "no-pm-components") == 1) {
1133 no_pm_components_child
= 1;
1136 cmn_err(CE_CONT
, "%s%d: ctl_attach %s%d [%d]\n",
1137 name
, instance
, childname
, childinstance
,
1138 no_pm_components_child
);
1141 ndi_devi_enter(dip
, &circ
);
1146 * Mark nexus busy before a child attaches.
1147 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm
1148 * - pshot@XXX,nopm_strict)
1150 if (!(pshot
->state
& PM_SUPPORTED
))
1152 mutex_enter(&pshot
->lock
);
1154 if (pshot_debug_busy
) {
1155 cmn_err(CE_CONT
, "%s%d:"
1156 " ctl_attach_pre: busy for %s%d:"
1157 " busy = %d\n", name
, instance
,
1158 childname
, childinstance
,
1161 mutex_exit(&pshot
->lock
);
1162 rval
= pm_busy_component(dip
, 0);
1163 ASSERT(rval
== DDI_SUCCESS
);
1167 * Mark nexus idle after a child attaches.
1168 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm).
1169 * - also skip if this is not a stict parent and
1170 * - the child is a tape device or a no-pm-components
1173 if (!(pshot
->state
& PM_SUPPORTED
) ||
1174 (strcmp(childname
, "tape") == 0 &&
1175 !(pshot
->state
& STRICT_PARENT
)) ||
1176 no_pm_components_child
)
1178 mutex_enter(&pshot
->lock
);
1179 ASSERT(pshot
->busy
> 0);
1181 if (pshot_debug_busy
) {
1182 cmn_err(CE_CONT
, "%s%d:"
1183 " ctl_attach_post: idle for %s%d:"
1184 " busy = %d\n", name
, instance
,
1185 childname
, childinstance
,
1188 mutex_exit(&pshot
->lock
);
1189 rval
= pm_idle_component(dip
, 0);
1190 ASSERT(rval
== DDI_SUCCESS
);
1194 ndi_devi_exit(dip
, circ
);
1198 case DDI_CTLOPS_DETACH
:
1200 dev_info_t
*child
= (dev_info_t
*)rdip
;
1201 childname
= ddi_node_name(child
);
1202 childinstance
= ddi_get_instance(child
);
1203 ds
= (struct detachspec
*)arg
;
1205 no_pm_components_child
= 0;
1206 if (ddi_prop_exists(DDI_DEV_T_ANY
, child
,
1207 (DDI_PROP_DONTPASS
| DDI_PROP_NOTPROM
),
1208 "no-pm-components") == 1) {
1209 no_pm_components_child
= 1;
1213 "%s%d: ctl_detach %s%d [%d]\n",
1214 name
, instance
, childname
, childinstance
,
1215 no_pm_components_child
);
1218 ndi_devi_enter(dip
, &circ
);
1223 * Mark nexus busy before a child detaches.
1224 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm
1225 * - pshot@XXX,nopm_strict), or if the child is a
1226 * - no-pm-components nexus node.
1228 if (!(pshot
->state
& PM_SUPPORTED
) ||
1229 (strcmp(childname
, "tape") == 0 &&
1230 !(pshot
->state
& STRICT_PARENT
)) ||
1231 no_pm_components_child
)
1233 mutex_enter(&pshot
->lock
);
1235 if (pshot_debug_busy
) {
1236 cmn_err(CE_CONT
, "%s%d:"
1237 " ctl_detach_pre: busy for %s%d:"
1238 " busy = %d\n", name
, instance
,
1239 childname
, childinstance
,
1242 mutex_exit(&pshot
->lock
);
1243 rval
= pm_busy_component(dip
, 0);
1244 ASSERT(rval
== DDI_SUCCESS
);
1249 * Mark nexus idle after a child detaches.
1250 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm)
1252 if (!(pshot
->state
& PM_SUPPORTED
))
1254 mutex_enter(&pshot
->lock
);
1255 ASSERT(pshot
->busy
> 0);
1257 if (pshot_debug_busy
) {
1258 cmn_err(CE_CONT
, "%s%d:"
1259 " ctl_detach_post: idle for %s%d:"
1260 " busy = %d\n", name
, instance
,
1261 childname
, childinstance
,
1264 mutex_exit(&pshot
->lock
);
1265 rval
= pm_idle_component(dip
, 0);
1266 ASSERT(rval
== DDI_SUCCESS
);
1269 * Mark the driver idle if the NO_INVOL_FLAG
1270 * is set. This is needed to make sure the
1271 * parent is idle after the child detaches
1272 * without calling pm_lower_power().
1273 * Clear the NO_INVOL_FLAG.
1274 * - also mark idle if a tape device has detached
1276 if (!(pshot
->state
& NO_INVOL_FLAG
))
1278 mutex_enter(&pshot
->lock
);
1279 ASSERT(pshot
->busy
> 0);
1281 if (pshot_debug_busy
) {
1282 cmn_err(CE_CONT
, "%s%d:"
1283 " ctl_detach_post: NO_INVOL:"
1284 " idle for %s%d: busy = %d\n",
1285 name
, instance
, childname
,
1286 childinstance
, pshot
->busy
);
1288 pshot
->state
&= ~NO_INVOL_FLAG
;
1289 mutex_exit(&pshot
->lock
);
1290 rval
= pm_idle_component(dip
, 0);
1291 ASSERT(rval
== DDI_SUCCESS
);
1296 ndi_devi_exit(dip
, circ
);
1301 case DDI_CTLOPS_BTOP
:
1302 case DDI_CTLOPS_BTOPR
:
1303 case DDI_CTLOPS_DVMAPAGESIZE
:
1304 case DDI_CTLOPS_IOMIN
:
1305 case DDI_CTLOPS_PTOB
:
1308 * The ops that we pass up (default). We pass up memory
1309 * allocation oriented ops that we receive - these may be
1310 * associated with pseudo HBA drivers below us with target
1311 * drivers below them that use ddi memory allocation
1312 * interfaces like scsi_alloc_consistent_buf.
1314 return (ddi_ctlops(dip
, rdip
, ctlop
, arg
, result
));
1320 pshot_power(dev_info_t
*dip
, int cmpt
, int level
)
1323 int instance
= ddi_get_instance(dip
);
1324 char *name
= ddi_node_name(dip
);
1328 pshot
= ddi_get_soft_state(pshot_softstatep
, instance
);
1329 if (pshot
== NULL
) {
1331 return (DDI_FAILURE
);
1334 ndi_devi_enter(dip
, &circ
);
1337 * set POWER_FLAG when power() is called.
1338 * ioctl(DEVCT_PM_POWER) is a clear on read call.
1340 mutex_enter(&pshot
->lock
);
1341 pshot
->state
|= POWER_FLAG
;
1343 * refuse to power OFF if the component is busy
1345 if (pshot
->busy
!= 0 && pshot
->level
> level
) {
1346 cmn_err(CE_WARN
, "%s%d: power: REFUSING POWER LEVEL CHANGE"
1347 " (%d->%d), DEVICE NOT IDLE: busy = %d",
1348 name
, instance
, pshot
->level
, level
, pshot
->busy
);
1352 cmn_err(CE_CONT
, "%s%d: power: comp %d (%d->%d)\n",
1353 name
, instance
, cmpt
, pshot
->level
, level
);
1355 pshot
->level
= level
;
1358 mutex_exit(&pshot
->lock
);
1360 ndi_devi_exit(dip
, circ
);
1367 pshot_bus_power(dev_info_t
*dip
, void *impl_arg
, pm_bus_power_op_t op
,
1368 void *arg
, void *result
)
1372 int instance
= ddi_get_instance(dip
);
1373 char *name
= ddi_node_name(dip
);
1375 pm_bp_child_pwrchg_t
*bpc
;
1376 pm_bp_nexus_pwrup_t bpn
;
1377 pm_bp_has_changed_t
*bphc
;
1380 int pwrup_res_failed
= 0;
1382 pshot
= ddi_get_soft_state(pshot_softstatep
, instance
);
1383 if (pshot
== NULL
) {
1385 return (DDI_FAILURE
);
1389 case BUS_POWER_PRE_NOTIFICATION
:
1390 bpc
= (pm_bp_child_pwrchg_t
*)arg
;
1392 cmn_err(CE_CONT
, "%s%d: pre_bus_power:"
1393 " %s%d comp %d (%d->%d)\n",
1394 name
, instance
, ddi_node_name(bpc
->bpc_dip
),
1395 ddi_get_instance(bpc
->bpc_dip
),
1396 bpc
->bpc_comp
, bpc
->bpc_olevel
,
1401 * mark parent busy if old_level is either -1 or 0,
1402 * and new level is == MAXPWR
1403 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm)
1405 if ((bpc
->bpc_comp
== 0 && bpc
->bpc_nlevel
== MAXPWR
&&
1406 bpc
->bpc_olevel
<= 0) && (pshot
->state
& PM_SUPPORTED
)) {
1407 mutex_enter(&pshot
->lock
);
1409 if (pshot_debug_busy
) {
1411 "%s%d: pre_bus_power:"
1412 " busy parent for %s%d (%d->%d): "
1415 ddi_node_name(bpc
->bpc_dip
),
1416 ddi_get_instance(bpc
->bpc_dip
),
1417 bpc
->bpc_olevel
, bpc
->bpc_nlevel
,
1420 mutex_exit(&pshot
->lock
);
1421 ret
= pm_busy_component(dip
, 0);
1422 ASSERT(ret
== DDI_SUCCESS
);
1426 * if new_level > 0, power up parent, if not already at
1427 * MAXPWR, via pm_busop_bus_power
1428 * - skip for the no-pm nexus (pshot@XXX,nopm)
1430 if (bpc
->bpc_comp
== 0 && bpc
->bpc_nlevel
> 0 &&
1431 pshot
->level
< MAXPWR
&& (pshot
->state
& PM_SUPPORTED
)) {
1433 * stuff the bpn struct
1436 bpn
.bpn_level
= MAXPWR
;
1437 bpn
.bpn_private
= bpc
->bpc_private
;
1441 * ask pm to power parent up
1444 cmn_err(CE_CONT
, "%s%d: pre_bus_power:"
1445 " pm_busop_bus_power on parent for %s%d"
1446 " (%d->%d): enter", name
, instance
,
1447 ddi_node_name(bpc
->bpc_dip
),
1448 ddi_get_instance(bpc
->bpc_dip
),
1449 bpc
->bpc_olevel
, bpc
->bpc_nlevel
);
1451 ret
= pm_busop_bus_power(dip
, impl_arg
,
1452 BUS_POWER_NEXUS_PWRUP
, (void *)&bpn
,
1453 (void *)&pwrup_res
);
1456 * check the return status individually,
1457 * idle parent and exit if either failed.
1459 if (ret
!= DDI_SUCCESS
) {
1461 "%s%d: pre_bus_power:"
1462 " pm_busop_bus_power FAILED (ret) FOR"
1465 ddi_node_name(bpc
->bpc_dip
),
1466 ddi_get_instance(bpc
->bpc_dip
),
1467 bpc
->bpc_olevel
, bpc
->bpc_nlevel
);
1470 if (pwrup_res
!= DDI_SUCCESS
) {
1472 "%s%d: pre_bus_power:"
1473 " pm_busop_bus_power FAILED (pwrup_res)"
1474 " FOR %s%d (%d->%d)",
1476 ddi_node_name(bpc
->bpc_dip
),
1477 ddi_get_instance(bpc
->bpc_dip
),
1478 bpc
->bpc_olevel
, bpc
->bpc_nlevel
);
1479 pwrup_res_failed
= 1;
1481 if (ret_failed
|| pwrup_res_failed
) {
1483 * decrement the busy count if it
1484 * had been incremented.
1486 if ((bpc
->bpc_comp
== 0 &&
1487 bpc
->bpc_nlevel
== MAXPWR
&&
1488 bpc
->bpc_olevel
<= 0) &&
1489 (pshot
->state
& PM_SUPPORTED
)) {
1490 mutex_enter(&pshot
->lock
);
1491 ASSERT(pshot
->busy
> 0);
1493 if (pshot_debug_busy
) {
1494 cmn_err(CE_CONT
, "%s%d:"
1495 " pm_busop_bus_power"
1496 " failed: idle parent for"
1508 mutex_exit(&pshot
->lock
);
1509 ret
= pm_idle_component(dip
, 0);
1510 ASSERT(ret
== DDI_SUCCESS
);
1512 return (DDI_FAILURE
);
1517 "%s%d: pre_bus_power:"
1518 " pm_busop_bus_power on parent"
1519 " for %s%d (%d->%d)\n",
1521 ddi_node_name(bpc
->bpc_dip
),
1522 ddi_get_instance(bpc
->bpc_dip
),
1523 bpc
->bpc_olevel
, bpc
->bpc_nlevel
);
1529 case BUS_POWER_POST_NOTIFICATION
:
1530 bpc
= (pm_bp_child_pwrchg_t
*)arg
;
1532 cmn_err(CE_CONT
, "%s%d: post_bus_power:"
1533 " %s%d comp %d (%d->%d) result %d\n",
1534 name
, instance
, ddi_node_name(bpc
->bpc_dip
),
1535 ddi_get_instance(bpc
->bpc_dip
),
1536 bpc
->bpc_comp
, bpc
->bpc_olevel
,
1537 bpc
->bpc_nlevel
, *(int *)result
);
1541 * handle pm_busop_bus_power() failure case.
1542 * mark parent idle if had been marked busy.
1543 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm)
1545 if (*(int *)result
!= DDI_SUCCESS
) {
1547 "pshot%d: post_bus_power_failed:"
1548 " pm_busop_bus_power FAILED FOR %s%d (%d->%d)",
1549 instance
, ddi_node_name(bpc
->bpc_dip
),
1550 ddi_get_instance(bpc
->bpc_dip
),
1551 bpc
->bpc_olevel
, bpc
->bpc_nlevel
);
1553 if ((bpc
->bpc_comp
== 0 && bpc
->bpc_nlevel
== MAXPWR
&&
1554 bpc
->bpc_olevel
<= 0) &&
1555 (pshot
->state
& PM_SUPPORTED
)) {
1556 mutex_enter(&pshot
->lock
);
1557 ASSERT(pshot
->busy
> 0);
1559 if (pshot_debug_busy
) {
1560 cmn_err(CE_CONT
, "%s%d:"
1561 " post_bus_power_failed:"
1562 " idle parent for %s%d"
1563 " (%d->%d): busy = %d\n",
1565 ddi_node_name(bpc
->bpc_dip
),
1566 ddi_get_instance(bpc
->bpc_dip
),
1567 bpc
->bpc_olevel
, bpc
->bpc_nlevel
,
1570 mutex_exit(&pshot
->lock
);
1571 ret
= pm_idle_component(dip
, 0);
1572 ASSERT(ret
== DDI_SUCCESS
);
1577 * Mark nexus idle when a child's comp 0
1578 * is set to level 0 from level 1, 2, or 3 only.
1579 * And only if result arg == DDI_SUCCESS.
1580 * This will leave the parent busy when the child
1581 * does not call pm_lower_power() on detach after
1582 * unsetting the NO_LOWER_POWER flag.
1583 * If so, need to notify the parent to mark itself
1584 * idle anyway, else the no-involumtary-power-cycles
1585 * test cases will report false passes!
1586 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm)
1588 if ((bpc
->bpc_comp
== 0 && bpc
->bpc_nlevel
== 0 &&
1589 !(bpc
->bpc_olevel
<= 0) &&
1590 *(int *)result
== DDI_SUCCESS
) &&
1591 (pshot
->state
& PM_SUPPORTED
)) {
1592 mutex_enter(&pshot
->lock
);
1593 ASSERT(pshot
->busy
> 0);
1595 if (pshot_debug_busy
) {
1597 "%s%d: post_bus_power:"
1598 " idle parent for %s%d (%d->%d):"
1599 " busy = %d\n", name
, instance
,
1600 ddi_node_name(bpc
->bpc_dip
),
1601 ddi_get_instance(bpc
->bpc_dip
),
1602 bpc
->bpc_olevel
, bpc
->bpc_nlevel
,
1605 mutex_exit(&pshot
->lock
);
1606 ret
= pm_idle_component(dip
, 0);
1607 ASSERT(ret
== DDI_SUCCESS
);
1611 case BUS_POWER_HAS_CHANGED
:
1612 bphc
= (pm_bp_has_changed_t
*)arg
;
1614 cmn_err(CE_CONT
, "%s%d: has_changed_bus_power:"
1615 " %s%d comp %d (%d->%d) result %d\n",
1616 name
, instance
, ddi_node_name(bphc
->bphc_dip
),
1617 ddi_get_instance(bphc
->bphc_dip
),
1618 bphc
->bphc_comp
, bphc
->bphc_olevel
,
1619 bphc
->bphc_nlevel
, *(int *)result
);
1623 * Mark nexus idle when a child's comp 0
1624 * is set to level 0 from levels 1, 2, or 3 only.
1626 * If powering up child leaf/nexus nodes via
1627 * pm_power_has_changed() calls, first issue
1628 * DEVCTL_PM_BUSY_COMP ioctl to mark parent busy
1629 * before powering the parent up, then power up the
1631 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm)
1633 if ((bphc
->bphc_comp
== 0 && bphc
->bphc_nlevel
== 0 &&
1634 !(bphc
->bphc_olevel
<= 0)) &&
1635 pshot
->state
& PM_SUPPORTED
) {
1636 mutex_enter(&pshot
->lock
);
1637 ASSERT(pshot
->busy
> 0);
1639 if (pshot_debug_busy
) {
1641 "%s%d: has_changed_bus_power:"
1642 " idle parent for %s%d (%d->%d):"
1643 " busy = %d\n", name
, instance
,
1644 ddi_node_name(bphc
->bphc_dip
),
1645 ddi_get_instance(bphc
->bphc_dip
),
1647 bphc
->bphc_nlevel
, pshot
->busy
);
1649 mutex_exit(&pshot
->lock
);
1650 ret
= pm_idle_component(dip
, 0);
1651 ASSERT(ret
== DDI_SUCCESS
);
1656 return (pm_busop_bus_power(dip
, impl_arg
, op
, arg
, result
));
1660 return (DDI_SUCCESS
);
1664 pshot_initchild(dev_info_t
*dip
, dev_info_t
*child
)
1670 dev_info_t
*enum_child
;
1675 /* check for bus_enum node */
1678 if (impl_ddi_merge_child(child
) != DDI_SUCCESS
)
1679 return (DDI_FAILURE
);
1682 enum_base
= ddi_prop_get_int(DDI_DEV_T_ANY
, child
, DDI_PROP_DONTPASS
,
1685 enum_extent
= ddi_prop_get_int(DDI_DEV_T_ANY
, child
,
1686 DDI_PROP_DONTPASS
, "busid_range", 0);
1689 * bus enumeration node
1691 if ((enum_base
!= 0) && (enum_extent
!= 0)) {
1692 c_nodename
= ddi_node_name(child
);
1694 for (; bus_id
< enum_extent
; bus_id
++) {
1695 if (ndi_devi_alloc(dip
, c_nodename
, DEVI_PSEUDO_NODEID
,
1696 &enum_child
) != NDI_SUCCESS
)
1697 return (DDI_FAILURE
);
1699 (void) sprintf(name
, "%d", bus_id
);
1700 if (ndi_prop_update_string(DDI_DEV_T_NONE
, enum_child
,
1701 "bus-addr", name
) != DDI_PROP_SUCCESS
) {
1702 (void) ndi_devi_free(enum_child
);
1703 return (DDI_FAILURE
);
1706 if (ndi_devi_online(enum_child
, 0) !=
1708 (void) ndi_devi_free(enum_child
);
1709 return (DDI_FAILURE
);
1713 * fail the enumeration node itself
1715 return (DDI_FAILURE
);
1718 if (ddi_prop_lookup_string(DDI_DEV_T_ANY
, child
, 0, "bus-addr",
1719 &bus_addr
) != DDI_PROP_SUCCESS
) {
1720 cmn_err(CE_WARN
, "pshot_initchild: bus-addr not defined (%s)",
1721 ddi_node_name(child
));
1722 return (DDI_NOT_WELL_FORMED
);
1725 if (strlen(bus_addr
) == 0) {
1726 cmn_err(CE_WARN
, "pshot_initchild: NULL bus-addr (%s)",
1727 ddi_node_name(child
));
1728 ddi_prop_free(bus_addr
);
1729 return (DDI_FAILURE
);
1732 if (strncmp(bus_addr
, "failinit", 8) == 0) {
1735 "pshot%d: %s forced INITCHILD failure\n",
1736 ddi_get_instance(dip
), bus_addr
);
1737 ddi_prop_free(bus_addr
);
1738 return (DDI_FAILURE
);
1742 cmn_err(CE_CONT
, "initchild %s%d/%s@%s\n",
1743 ddi_get_name(dip
), ddi_get_instance(dip
),
1744 ddi_node_name(child
), bus_addr
);
1747 ddi_set_name_addr(child
, bus_addr
);
1748 ddi_prop_free(bus_addr
);
1749 return (DDI_SUCCESS
);
1754 pshot_uninitchild(dev_info_t
*dip
, dev_info_t
*child
)
1756 ddi_set_name_addr(child
, NULL
);
1757 return (DDI_SUCCESS
);
1762 * devctl IOCTL support
1766 pshot_open(dev_t
*devp
, int flags
, int otyp
, cred_t
*credp
)
1771 if (otyp
!= OTYP_CHR
)
1774 instance
= pshot_minor_decode_inst(getminor(*devp
));
1775 if ((pshot
= ddi_get_soft_state(pshot_softstatep
, instance
)) == NULL
)
1779 * Access is currently determined on a per-instance basis.
1780 * If we want per-node, then need to add state and lock members to
1783 mutex_enter(&pshot
->lock
);
1784 if (((flags
& FEXCL
) && (pshot
->state
& IS_OPEN
)) ||
1785 (!(flags
& FEXCL
) && (pshot
->state
& IS_OPEN_EXCL
))) {
1786 mutex_exit(&pshot
->lock
);
1789 pshot
->state
|= IS_OPEN
;
1791 pshot
->state
|= IS_OPEN_EXCL
;
1794 cmn_err(CE_CONT
, "pshot%d open\n", instance
);
1796 mutex_exit(&pshot
->lock
);
1805 pshot_close(dev_t dev
, int flag
, int otyp
, cred_t
*credp
)
1810 if (otyp
!= OTYP_CHR
)
1813 instance
= pshot_minor_decode_inst(getminor(dev
));
1814 if ((pshot
= ddi_get_soft_state(pshot_softstatep
, instance
)) == NULL
)
1817 mutex_enter(&pshot
->lock
);
1818 pshot
->state
&= ~(IS_OPEN
| IS_OPEN_EXCL
);
1819 mutex_exit(&pshot
->lock
);
1821 cmn_err(CE_CONT
, "pshot%d closed\n", instance
);
1827 * pshot_ioctl: redirects to appropriate command handler based on various
1832 pshot_ioctl(dev_t dev
, int cmd
, intptr_t arg
, int mode
, cred_t
*credp
,
1840 instance
= pshot_minor_decode_inst(getminor(dev
));
1841 if ((pshot
= ddi_get_soft_state(pshot_softstatep
, instance
)) == NULL
)
1844 nodenum
= pshot_minor_decode_nodenum(getminor(dev
));
1845 nodename
= pshot
->nodes
[nodenum
].name
;
1849 "pshot%d ioctl: dev=%p, cmd=%x, arg=%p, mode=%x\n",
1850 instance
, (void *)dev
, cmd
, (void *)arg
, mode
);
1852 if (strcmp(nodename
, PSHOT_NODENAME_DEVCTL
) == 0)
1853 return (pshot_devctl(pshot
, nodenum
, cmd
, arg
, mode
, credp
,
1856 if (strcmp(nodename
, PSHOT_NODENAME_TESTCTL
) == 0)
1857 return (pshot_testctl(pshot
, nodenum
, cmd
, arg
, mode
, credp
,
1860 cmn_err(CE_WARN
, "pshot_ioctl: unmatched nodename on minor %u",
1861 pshot
->nodes
[nodenum
].minor
);
1867 * pshot_devctl: handle DEVCTL operations
1871 pshot_devctl(pshot_t
*pshot
, minor_t nodenum
, int cmd
, intptr_t arg
, int mode
,
1872 cred_t
*credp
, int *rvalp
)
1875 dev_info_t
*child
= NULL
;
1876 struct devctl_iocdata
*dcp
;
1886 flags
= (pshot_devctl_debug
) ? NDI_DEVI_DEBUG
: 0;
1887 instance
= pshot
->instance
;
1890 * We can use the generic implementation for these ioctls
1892 for (i
= 0; pshot_devctls
[i
].ioctl_int
!= 0; i
++) {
1893 if (pshot_devctls
[i
].ioctl_int
== cmd
) {
1895 cmn_err(CE_CONT
, "pshot%d devctl: %s",
1896 instance
, pshot_devctls
[i
].ioctl_char
);
1900 case DEVCTL_DEVICE_GETSTATE
:
1901 case DEVCTL_DEVICE_ONLINE
:
1902 case DEVCTL_DEVICE_OFFLINE
:
1903 case DEVCTL_DEVICE_REMOVE
:
1904 case DEVCTL_BUS_GETSTATE
:
1905 case DEVCTL_BUS_DEV_CREATE
:
1906 rv
= ndi_devctl_ioctl(self
, cmd
, arg
, mode
, flags
);
1907 if (pshot_debug
&& rv
!= 0) {
1908 cmn_err(CE_CONT
, "pshot%d ndi_devctl_ioctl:"
1909 " failed, rv = %d", instance
, rv
);
1916 * read devctl ioctl data
1918 if (ndi_dc_allochdl((void *)arg
, &dcp
) != NDI_SUCCESS
)
1923 case DEVCTL_DEVICE_RESET
:
1925 cmn_err(CE_CONT
, "pshot%d devctl:"
1926 " DEVCTL_DEVICE_RESET\n", instance
);
1927 rv
= pshot_event(pshot
, PSHOT_EVENT_TAG_DEV_RESET
,
1928 child
, (void *)self
);
1929 ASSERT(rv
== NDI_SUCCESS
);
1932 case DEVCTL_BUS_QUIESCE
:
1934 cmn_err(CE_CONT
, "pshot%d devctl:"
1935 " DEVCTL_BUS_QUIESCE\n", instance
);
1936 if (ndi_get_bus_state(self
, &state
) == NDI_SUCCESS
) {
1937 if (state
== BUS_QUIESCED
) {
1940 (void) ndi_set_bus_state(self
, BUS_QUIESCED
);
1942 rv
= pshot_event(pshot
, PSHOT_EVENT_TAG_BUS_QUIESCE
,
1943 child
, (void *)self
);
1944 ASSERT(rv
== NDI_SUCCESS
);
1948 case DEVCTL_BUS_UNQUIESCE
:
1950 cmn_err(CE_CONT
, "pshot%d devctl:"
1951 " DEVCTL_BUS_UNQUIESCE\n", instance
);
1952 if (ndi_get_bus_state(self
, &state
) == NDI_SUCCESS
) {
1953 if (state
== BUS_ACTIVE
) {
1959 * quiesce the bus through bus-specific means
1961 (void) ndi_set_bus_state(self
, BUS_ACTIVE
);
1962 rv
= pshot_event(pshot
, PSHOT_EVENT_TAG_BUS_UNQUIESCE
,
1963 child
, (void *)self
);
1964 ASSERT(rv
== NDI_SUCCESS
);
1967 case DEVCTL_BUS_RESET
:
1968 case DEVCTL_BUS_RESETALL
:
1970 * no reset support for the pseudo bus
1971 * but if there were....
1973 rv
= pshot_event(pshot
, PSHOT_EVENT_TAG_BUS_RESET
,
1974 child
, (void *)self
);
1975 ASSERT(rv
== NDI_SUCCESS
);
1981 case DEVCTL_PM_BUSY_COMP
:
1983 * mark component 0 busy.
1984 * Keep track of ioctl updates to the busy count
1985 * via pshot->busy_ioctl.
1988 cmn_err(CE_CONT
, "pshot%d devctl:"
1989 " DEVCTL_PM_BUSY_COMP\n", instance
);
1991 mutex_enter(&pshot
->lock
);
1993 ++(pshot
->busy_ioctl
);
1994 if (pshot_debug_busy
) {
1995 cmn_err(CE_CONT
, "pshot%d:"
1996 " DEVCTL_PM_BUSY_COMP comp 0 busy"
1997 " %d busy_ioctl %d\n", instance
, pshot
->busy
,
2000 mutex_exit(&pshot
->lock
);
2001 ret
= pm_busy_component(pshot
->dip
, 0);
2002 ASSERT(ret
== DDI_SUCCESS
);
2006 case DEVCTL_PM_BUSY_COMP_TEST
:
2008 * test bus's busy state
2011 cmn_err(CE_CONT
, "pshot%d devctl:"
2012 " DEVCTL_PM_BUSY_COMP_TEST\n", instance
);
2014 mutex_enter(&pshot
->lock
);
2015 state
= pshot
->busy
;
2016 if (copyout(&state
, dcp
->cpyout_buf
,
2017 sizeof (uint_t
)) != 0) {
2018 cmn_err(CE_WARN
, "pshot%d devctl:"
2019 " DEVCTL_PM_BUSY_COMP_TEST: copyout failed",
2023 if (pshot_debug_busy
) {
2024 cmn_err(CE_CONT
, "pshot%d: DEVCTL_PM_BUSY_COMP_TEST:"
2025 " comp 0 busy %d busy_ioctl %d\n", instance
,
2026 state
, pshot
->busy_ioctl
);
2028 mutex_exit(&pshot
->lock
);
2031 case DEVCTL_PM_IDLE_COMP
:
2033 * mark component 0 idle.
2034 * NOP if pshot->busy_ioctl <= 0.
2037 cmn_err(CE_CONT
, "pshot%d devctl:"
2038 " DEVCTL_PM_IDLE_COMP\n", instance
);
2040 mutex_enter(&pshot
->lock
);
2041 if (pshot
->busy_ioctl
> 0) {
2042 ASSERT(pshot
->busy
> 0);
2044 --(pshot
->busy_ioctl
);
2045 if (pshot_debug_busy
) {
2046 cmn_err(CE_CONT
, "pshot%d:"
2047 " DEVCTL_PM_IDLE_COM: comp 0"
2048 " busy %d busy_ioctl %d\n", instance
,
2049 pshot
->busy
, pshot
->busy_ioctl
);
2051 mutex_exit(&pshot
->lock
);
2052 ret
= pm_idle_component(pshot
->dip
, 0);
2053 ASSERT(ret
== DDI_SUCCESS
);
2056 mutex_exit(&pshot
->lock
);
2060 case DEVCTL_PM_RAISE_PWR
:
2062 * raise component 0 to full power level MAXPWR via a
2063 * pm_raise_power() call
2066 cmn_err(CE_CONT
, "pshot%d devctl:"
2067 " DEVCTL_PM_RAISE_PWR\n", instance
);
2069 if (pm_raise_power(pshot
->dip
, 0, MAXPWR
) != DDI_SUCCESS
) {
2072 mutex_enter(&pshot
->lock
);
2074 cmn_err(CE_CONT
, "pshot%d:"
2075 " DEVCTL_PM_RAISE_POWER: comp 0"
2076 " to level %d\n", instance
, pshot
->level
);
2078 mutex_exit(&pshot
->lock
);
2082 case DEVCTL_PM_LOWER_PWR
:
2084 * pm_lower_power() call for negative testing
2088 cmn_err(CE_CONT
, "pshot%d devctl:"
2089 " DEVCTL_PM_LOWER_PWR\n", instance
);
2091 if (pm_lower_power(pshot
->dip
, 0, 0) != DDI_SUCCESS
) {
2094 mutex_enter(&pshot
->lock
);
2096 cmn_err(CE_CONT
, "pshot%d:"
2097 " DEVCTL_PM_LOWER_POWER comp 0"
2098 " to level %d\n", instance
, pshot
->level
);
2100 mutex_exit(&pshot
->lock
);
2104 case DEVCTL_PM_CHANGE_PWR_LOW
:
2106 * inform the PM framework that component 0 has changed
2107 * power level to 0 via a pm_power_has_changed() call
2110 cmn_err(CE_CONT
, "pshot%d devctl:"
2111 " DEVCTL_PM_CHANGE_PWR_LOW\n", instance
);
2113 mutex_enter(&pshot
->lock
);
2115 if (pm_power_has_changed(pshot
->dip
, 0, 0) != DDI_SUCCESS
) {
2119 cmn_err(CE_CONT
, "pshot%d:"
2120 " DEVCTL_PM_CHANGE_PWR_LOW comp 0 to"
2121 " level %d\n", instance
, pshot
->level
);
2124 mutex_exit(&pshot
->lock
);
2127 case DEVCTL_PM_CHANGE_PWR_HIGH
:
2129 * inform the PM framework that component 0 has changed
2130 * power level to MAXPWR via a pm_power_has_changed() call
2133 cmn_err(CE_CONT
, "pshot%d devctl:"
2134 " DEVCTL_PM_CHANGE_PWR_HIGH\n", instance
);
2136 mutex_enter(&pshot
->lock
);
2137 pshot
->level
= MAXPWR
;
2138 if (pm_power_has_changed(pshot
->dip
, 0, MAXPWR
)
2143 cmn_err(CE_CONT
, "pshot%d:"
2144 " DEVCTL_PM_CHANGE_PWR_HIGH comp 0 to"
2145 " level %d\n", instance
, pshot
->level
);
2148 mutex_exit(&pshot
->lock
);
2151 case DEVCTL_PM_POWER
:
2153 * test if the pshot_power() routine has been called,
2157 cmn_err(CE_CONT
, "pshot%d devctl:"
2158 " DEVCTL_PM_POWER\n", instance
);
2160 mutex_enter(&pshot
->lock
);
2161 state
= (pshot
->state
& POWER_FLAG
) ? 1 : 0;
2162 if (copyout(&state
, dcp
->cpyout_buf
,
2163 sizeof (uint_t
)) != 0) {
2164 cmn_err(CE_WARN
, "pshot%d devctl:"
2165 " DEVCTL_PM_POWER: copyout failed",
2170 cmn_err(CE_CONT
, "pshot%d: DEVCTL_PM_POWER:"
2171 " POWER_FLAG = %d\n", instance
, state
);
2173 pshot
->state
&= ~POWER_FLAG
;
2174 mutex_exit(&pshot
->lock
);
2177 case DEVCTL_PM_FAIL_SUSPEND
:
2182 cmn_err(CE_CONT
, "pshot%d devctl:"
2183 " DEVCTL_PM_FAIL_SUSPEND\n", instance
);
2185 mutex_enter(&pshot
->lock
);
2186 pshot
->state
|= FAIL_SUSPEND_FLAG
;
2187 mutex_exit(&pshot
->lock
);
2189 cmn_err(CE_CONT
, "pshot%d: DEVCTL_PM_FAIL_SUSPEND\n",
2194 case DEVCTL_PM_BUS_STRICT_TEST
:
2196 * test the STRICT_PARENT flag:
2197 * set => STRICT PARENT
2198 * not set => INVOLVED PARENT
2200 mutex_enter(&pshot
->lock
);
2201 state
= (pshot
->state
& STRICT_PARENT
) ? 1 : 0;
2202 if (copyout(&state
, dcp
->cpyout_buf
,
2203 sizeof (uint_t
)) != 0) {
2204 cmn_err(CE_WARN
, "pshot%d devctl:"
2205 " DEVCTL_PM_BUS_STRICT_TEST: copyout failed",
2210 cmn_err(CE_CONT
, "pshot%d devctl:"
2211 " DEVCTL_PM_BUS_STRICT_TEST: type = %s\n",
2212 instance
, ((state
== 0) ? "INVOLVED" : "STRICT"));
2214 mutex_exit(&pshot
->lock
);
2217 case DEVCTL_PM_BUS_NO_INVOL
:
2219 * Set the NO_INVOL_FLAG flag to
2220 * notify the driver that the child will not
2221 * call pm_lower_power() on detach.
2222 * The driver needs to mark itself idle twice
2223 * during DDI_CTLOPS_DETACH (post).
2226 cmn_err(CE_CONT
, "pshot%d devctl:"
2227 " DEVCTL_PM_BUS_NO_INVOL\n", instance
);
2229 mutex_enter(&pshot
->lock
);
2230 pshot
->state
|= NO_INVOL_FLAG
;
2231 mutex_exit(&pshot
->lock
);
2238 ndi_dc_freehdl(dcp
);
2244 * pshot_testctl: handle other test operations
2245 * - If <cmd> is a DEVCTL cmd, then <arg> is a dev_t indicating which
2246 * child to direct the DEVCTL to, if applicable;
2247 * furthermore, any cmd here can be sent by layered ioctls (unlike
2248 * those to pshot_devctl() which must come from userland)
2252 pshot_testctl(pshot_t
*pshot
, minor_t nodenum
, int cmd
, intptr_t arg
, int mode
,
2253 cred_t
*credp
, int *rvalp
)
2256 dev_info_t
*child
= NULL
;
2264 /* flags = (pshot_devctl_debug) ? NDI_DEVI_DEBUG : 0; */
2266 instance
= pshot
->instance
;
2268 if (cmd
& DEVCTL_IOC
) {
2269 child
= e_ddi_hold_devi_by_dev((dev_t
)arg
, 0);
2272 for (i
= 0; pshot_devctls
[i
].ioctl_int
!= 0; i
++) {
2273 if (pshot_devctls
[i
].ioctl_int
== cmd
) {
2275 cmn_err(CE_CONT
, "pshot%d devctl: %s",
2276 instance
, pshot_devctls
[i
].ioctl_char
);
2280 case DEVCTL_DEVICE_RESET
:
2282 cmn_err(CE_CONT
, "pshot%d testctl:"
2283 " DEVCTL_PM_POWER\n", instance
);
2284 rv
= pshot_event(pshot
, PSHOT_EVENT_TAG_DEV_RESET
,
2285 child
, (void *)self
);
2286 ASSERT(rv
== NDI_SUCCESS
);
2289 case DEVCTL_BUS_QUIESCE
:
2291 cmn_err(CE_CONT
, "pshot%d testctl:"
2292 " DEVCTL_PM_POWER\n", instance
);
2293 if (ndi_get_bus_state(self
, &state
) == NDI_SUCCESS
) {
2294 if (state
== BUS_QUIESCED
) {
2297 (void) ndi_set_bus_state(self
, BUS_QUIESCED
);
2299 rv
= pshot_event(pshot
, PSHOT_EVENT_TAG_BUS_QUIESCE
,
2300 child
, (void *)self
);
2301 ASSERT(rv
== NDI_SUCCESS
);
2305 case DEVCTL_BUS_UNQUIESCE
:
2307 cmn_err(CE_CONT
, "pshot%d testctl:"
2308 " DEVCTL_PM_POWER\n", instance
);
2309 if (ndi_get_bus_state(self
, &state
) == NDI_SUCCESS
) {
2310 if (state
== BUS_ACTIVE
) {
2316 * quiesce the bus through bus-specific means
2318 (void) ndi_set_bus_state(self
, BUS_ACTIVE
);
2319 rv
= pshot_event(pshot
, PSHOT_EVENT_TAG_BUS_UNQUIESCE
,
2320 child
, (void *)self
);
2321 ASSERT(rv
== NDI_SUCCESS
);
2324 case DEVCTL_BUS_RESET
:
2325 case DEVCTL_BUS_RESETALL
:
2327 * no reset support for the pseudo bus
2328 * but if there were....
2330 rv
= pshot_event(pshot
, PSHOT_EVENT_TAG_BUS_RESET
,
2331 child
, (void *)self
);
2332 ASSERT(rv
== NDI_SUCCESS
);
2340 ddi_release_devi(child
);
2346 pshot_get_eventcookie(dev_info_t
*dip
, dev_info_t
*rdip
,
2347 char *eventname
, ddi_eventcookie_t
*event_cookiep
)
2349 int instance
= ddi_get_instance(dip
);
2350 pshot_t
*pshot
= ddi_get_soft_state(pshot_softstatep
, instance
);
2353 cmn_err(CE_CONT
, "pshot%d: "
2354 "pshot_get_eventcookie:\n\t"
2355 "dip = 0x%p rdip = 0x%p (%s/%d) eventname = %s\n",
2356 instance
, (void *)dip
, (void *)rdip
,
2357 ddi_node_name(rdip
), ddi_get_instance(rdip
),
2361 return (ndi_event_retrieve_cookie(pshot
->ndi_event_hdl
,
2362 rdip
, eventname
, event_cookiep
, NDI_EVENT_NOPASS
));
2366 pshot_add_eventcall(dev_info_t
*dip
, dev_info_t
*rdip
,
2367 ddi_eventcookie_t cookie
,
2368 void (*callback
)(), void *arg
, ddi_callback_id_t
*cb_id
)
2370 int instance
= ddi_get_instance(dip
);
2371 pshot_t
*pshot
= ddi_get_soft_state(pshot_softstatep
, instance
);
2374 cmn_err(CE_CONT
, "pshot%d: "
2375 "pshot_add_eventcall:\n\t"
2376 "dip = 0x%p rdip = 0x%p (%s%d)\n\tcookie = 0x%p (%s)\n\t"
2377 "cb = 0x%p, arg = 0x%p\n",
2378 instance
, (void *)dip
, (void *)rdip
,
2379 ddi_node_name(rdip
), ddi_get_instance(rdip
), (void *)cookie
,
2380 NDI_EVENT_NAME(cookie
), (void *)callback
, arg
);
2382 /* add callback to our event handle */
2383 return (ndi_event_add_callback(pshot
->ndi_event_hdl
, rdip
,
2384 cookie
, callback
, arg
, NDI_SLEEP
, cb_id
));
2388 pshot_remove_eventcall(dev_info_t
*dip
, ddi_callback_id_t cb_id
)
2391 ndi_event_callbacks_t
*cb
= (ndi_event_callbacks_t
*)cb_id
;
2393 int instance
= ddi_get_instance(dip
);
2394 pshot_t
*pshot
= ddi_get_soft_state(pshot_softstatep
, instance
);
2399 cmn_err(CE_CONT
, "pshot%d: "
2400 "pshot_remove_eventcall:\n\t"
2401 "dip = 0x%p rdip = 0x%p (%s%d)\n\tcookie = 0x%p (%s)\n",
2402 instance
, (void *)dip
, (void *)cb
->ndi_evtcb_dip
,
2403 ddi_node_name(cb
->ndi_evtcb_dip
),
2404 ddi_get_instance(cb
->ndi_evtcb_dip
),
2405 (void *)cb
->ndi_evtcb_cookie
,
2406 NDI_EVENT_NAME(cb
->ndi_evtcb_cookie
));
2408 return (ndi_event_remove_callback(pshot
->ndi_event_hdl
, cb_id
));
2412 pshot_post_event(dev_info_t
*dip
, dev_info_t
*rdip
,
2413 ddi_eventcookie_t cookie
, void *impl_data
)
2415 int instance
= ddi_get_instance(dip
);
2416 pshot_t
*pshot
= ddi_get_soft_state(pshot_softstatep
, instance
);
2420 cmn_err(CE_CONT
, "pshot%d: "
2421 "pshot_post_event:\n\t"
2422 "dip = 0x%p rdip = 0x%p (%s%d\n\t"
2423 "cookie = 0x%p (%s)\n\tbus_impl = 0x%p\n",
2424 instance
, (void *)dip
, (void *)rdip
,
2425 ddi_node_name(rdip
), ddi_get_instance(rdip
),
2427 NDI_EVENT_NAME(cookie
), impl_data
);
2429 cmn_err(CE_CONT
, "pshot%d: "
2430 "pshot_post_event:\n\t"
2431 "dip = 0x%p cookie = 0x%p (%s) bus_impl = 0x%p\n",
2432 instance
, (void *)dip
, (void *)cookie
,
2433 NDI_EVENT_NAME(cookie
), impl_data
);
2437 /* run callbacks for this event */
2438 return (ndi_event_run_callbacks(pshot
->ndi_event_hdl
, rdip
,
2439 cookie
, impl_data
));
2443 * the nexus driver will generate events
2444 * that need to go to children
2447 pshot_event(pshot_t
*pshot
, int event_tag
, dev_info_t
*child
,
2450 ddi_eventcookie_t cookie
= ndi_event_tag_to_cookie(
2451 pshot
->ndi_event_hdl
, event_tag
);
2455 cmn_err(CE_CONT
, "pshot%d: "
2456 "pshot_event: event_tag = 0x%x (%s)\n\t"
2457 "child = 0x%p (%s%d) bus_impl = 0x%p (%s%d)\n",
2458 pshot
->instance
, event_tag
,
2459 ndi_event_tag_to_name(pshot
->ndi_event_hdl
,
2461 (void *)child
, ddi_node_name(child
),
2462 ddi_get_instance(child
), bus_impldata
,
2463 ddi_node_name((dev_info_t
*)bus_impldata
),
2464 ddi_get_instance((dev_info_t
*)bus_impldata
));
2466 cmn_err(CE_CONT
, "pshot%d: "
2467 "pshot_event: event_tag = 0x%x (%s)\n\t"
2468 "child = NULL, bus_impl = 0x%p (%s%d)\n",
2469 pshot
->instance
, event_tag
,
2470 ndi_event_tag_to_name(pshot
->ndi_event_hdl
,
2473 ddi_node_name((dev_info_t
*)bus_impldata
),
2474 ddi_get_instance((dev_info_t
*)bus_impldata
));
2478 return (ndi_event_run_callbacks(pshot
->ndi_event_hdl
,
2479 child
, cookie
, bus_impldata
));
2484 * the pshot driver event notification callback
2487 pshot_event_cb(dev_info_t
*dip
, ddi_eventcookie_t cookie
,
2488 void *arg
, void *bus_impldata
)
2490 pshot_t
*pshot
= (pshot_t
*)arg
;
2493 /* look up the event */
2494 event_tag
= NDI_EVENT_TAG(cookie
);
2497 cmn_err(CE_CONT
, "pshot%d: "
2498 "pshot_event_cb:\n\t"
2499 "dip = 0x%p cookie = 0x%p (%s), tag = 0x%x\n\t"
2500 "arg = 0x%p bus_impl = 0x%p (%s%d)\n",
2501 pshot
->instance
, (void *)dip
, (void *)cookie
,
2502 NDI_EVENT_NAME(cookie
), event_tag
, arg
, bus_impldata
,
2503 ddi_node_name((dev_info_t
*)bus_impldata
),
2504 ddi_get_instance((dev_info_t
*)bus_impldata
));
2507 switch (event_tag
) {
2508 case PSHOT_EVENT_TAG_OFFLINE
:
2509 case PSHOT_EVENT_TAG_BUS_RESET
:
2510 case PSHOT_EVENT_TAG_BUS_QUIESCE
:
2511 case PSHOT_EVENT_TAG_BUS_UNQUIESCE
:
2512 /* notify all subscribers of the this event */
2513 (void) ndi_event_run_callbacks(pshot
->ndi_event_hdl
,
2514 NULL
, cookie
, bus_impldata
);
2516 cmn_err(CE_CONT
, "pshot%d: event=%s\n\t"
2517 "pshot_event_cb\n", pshot
->instance
,
2518 NDI_EVENT_NAME(cookie
));
2521 case PSHOT_EVENT_TAG_TEST_POST
:
2522 case PSHOT_EVENT_TAG_DEV_RESET
:
2529 pshot_bus_config(dev_info_t
*parent
, uint_t flags
,
2530 ddi_bus_config_op_t op
, void *arg
, dev_info_t
**childp
)
2534 char *devstr
, *cname
, *caddr
;
2538 int instance
= ddi_get_instance(parent
);
2541 flags
|= NDI_DEVI_DEBUG
;
2543 "pshot%d: bus_config %s flags=0x%x\n",
2544 ddi_get_instance(parent
),
2545 (op
== BUS_CONFIG_ONE
) ? (char *)arg
: "", flags
);
2548 pshot
= ddi_get_soft_state(pshot_softstatep
, instance
);
2549 if (pshot
== NULL
) {
2551 return (NDI_FAILURE
);
2555 * Hold the nexus across the bus_config
2557 ndi_devi_enter(parent
, &circ
);
2560 case BUS_CONFIG_ONE
:
2563 * lookup and hold child device, create if not found
2565 devname
= (char *)arg
;
2566 devstrlen
= strlen(devname
) + 1;
2567 devstr
= i_ddi_strdup(devname
, KM_SLEEP
);
2568 i_ddi_parse_name(devstr
, &cname
, &caddr
, NULL
);
2571 * The framework ensures that the node has
2572 * a name but each nexus is responsible for
2573 * the bus address name space. This driver
2574 * requires that a bus address be specified,
2575 * as will most nexus drivers.
2577 ASSERT(cname
&& strlen(cname
) > 0);
2578 if (caddr
== NULL
|| strlen(caddr
) == 0) {
2580 "pshot%d: malformed name %s (no bus address)",
2581 ddi_get_instance(parent
), devname
);
2582 kmem_free(devstr
, devstrlen
);
2583 ndi_devi_exit(parent
, circ
);
2584 return (NDI_FAILURE
);
2588 * Handle a few special cases for testing purposes
2590 rval
= pshot_bus_config_test_specials(parent
,
2591 devname
, cname
, caddr
);
2593 if (rval
== NDI_SUCCESS
) {
2595 * Set up either a leaf or nexus device
2597 if (strcmp(cname
, "pshot") == 0) {
2598 rval
= pshot_bus_config_setup_nexus(parent
,
2601 rval
= pshot_bus_config_setup_leaf(parent
,
2606 kmem_free(devstr
, devstrlen
);
2609 case BUS_CONFIG_DRIVER
:
2610 case BUS_CONFIG_ALL
:
2619 if (rval
== NDI_SUCCESS
)
2620 rval
= ndi_busop_bus_config(parent
, flags
, op
, arg
, childp
, 0);
2622 ndi_devi_exit(parent
, circ
);
2625 cmn_err(CE_CONT
, "pshot%d: bus_config %s\n",
2626 ddi_get_instance(parent
),
2627 (rval
== NDI_SUCCESS
) ? "ok" : "failed");
2633 pshot_bus_unconfig(dev_info_t
*parent
, uint_t flags
,
2634 ddi_bus_config_op_t op
, void *arg
)
2637 int rval
= NDI_SUCCESS
;
2641 flags
|= NDI_DEVI_DEBUG
;
2643 "pshot%d: bus_unconfig %s flags=0x%x\n",
2644 ddi_get_instance(parent
),
2645 (op
== BUS_UNCONFIG_ONE
) ? (char *)arg
: "", flags
);
2649 * Hold the nexus across the bus_unconfig
2651 ndi_devi_enter(parent
, &circ
);
2654 case BUS_UNCONFIG_ONE
:
2656 * Nothing special required here
2659 cmn_err(CE_CONT
, "pshot%d: bus_unconfig:"
2660 " BUS_UNCONFIG_ONE\n", ddi_get_instance(parent
));
2664 case BUS_UNCONFIG_DRIVER
:
2665 if (pshot_debug
> 0) {
2666 major
= (major_t
)(uintptr_t)arg
;
2668 "pshot%d: BUS_UNCONFIG_DRIVER: %s\n",
2669 ddi_get_instance(parent
),
2670 ddi_major_to_name(major
));
2674 case BUS_UNCONFIG_ALL
:
2676 cmn_err(CE_CONT
, "pshot%d: bus_unconfig:"
2677 " BUS_UNCONFIG_ALL\n", ddi_get_instance(parent
));
2683 cmn_err(CE_CONT
, "pshot%d: bus_unconfig: DEFAULT\n",
2684 ddi_get_instance(parent
));
2689 if (rval
== NDI_SUCCESS
)
2690 rval
= ndi_busop_bus_unconfig(parent
, flags
, op
, arg
);
2692 ndi_devi_exit(parent
, circ
);
2695 cmn_err(CE_CONT
, "pshot%d: bus_unconfig %s\n",
2696 ddi_get_instance(parent
),
2697 (rval
== NDI_SUCCESS
) ? "ok" : "failed");
2703 pshot_findchild(dev_info_t
*pdip
, char *cname
, char *caddr
)
2708 ASSERT(cname
!= NULL
&& caddr
!= NULL
);
2709 ASSERT(DEVI_BUSY_OWNED(pdip
));
2711 for (dip
= ddi_get_child(pdip
); dip
!= NULL
;
2712 dip
= ddi_get_next_sibling(dip
)) {
2713 if (strcmp(cname
, ddi_node_name(dip
)) != 0)
2716 if ((addr
= ddi_get_name_addr(dip
)) == NULL
) {
2717 if (ddi_prop_lookup_string(DDI_DEV_T_ANY
, dip
, 0,
2718 "bus-addr", &addr
) == DDI_PROP_SUCCESS
) {
2719 if (strcmp(caddr
, addr
) == 0) {
2720 ddi_prop_free(addr
);
2723 ddi_prop_free(addr
);
2726 if (strcmp(caddr
, addr
) == 0)
2735 pshot_nexus_properties(dev_info_t
*parent
, dev_info_t
*child
, char *cname
,
2741 * extract the address extension
2743 extension
= strstr(caddr
, ",");
2744 if (extension
!= NULL
) {
2751 * Create the "pm-want-child-notification?" property for all
2752 * nodes that do not have the "pm_strict" or "nopm_strict"
2755 if (strcmp(extension
, "pm_strict") != 0 &&
2756 strcmp(extension
, "nopm_strict") != 0) {
2757 if (ddi_prop_exists(DDI_DEV_T_ANY
, child
,
2758 (DDI_PROP_DONTPASS
| DDI_PROP_NOTPROM
),
2759 "pm-want-child-notification?") == 0) {
2761 cmn_err(CE_CONT
, "pshot%d:"
2762 " nexus_properties:\n\tcreate the"
2763 " \"pm-want-child-notification?\""
2764 " property for %s@%s\n",
2765 ddi_get_instance(parent
), cname
, caddr
);
2767 if (ddi_prop_create(DDI_DEV_T_NONE
, child
, 0,
2768 "pm-want-child-notification?", NULL
, 0)
2769 != DDI_PROP_SUCCESS
) {
2770 cmn_err(CE_WARN
, "pshot%d:"
2771 " nexus_properties:\n\tunable to create"
2772 " the \"pm-want-child-notification?\""
2773 " property for %s@%s",
2774 ddi_get_instance(parent
), cname
, caddr
);
2780 * Create the "no-pm-components" property for all nodes
2781 * with extension "nopm" or "nopm_strict"
2783 if (strcmp(extension
, "nopm") == 0 ||
2784 strcmp(extension
, "nopm_strict") == 0) {
2785 if (ddi_prop_exists(DDI_DEV_T_ANY
, child
,
2786 (DDI_PROP_DONTPASS
| DDI_PROP_NOTPROM
),
2787 "no-pm-components") == 0) {
2789 cmn_err(CE_CONT
, "pshot%d:"
2790 " nexus_properties:\n\tcreate the"
2791 " \"no-pm-components\""
2792 " property for %s@%s\n",
2793 ddi_get_instance(parent
), cname
, caddr
);
2795 if (ddi_prop_create(DDI_DEV_T_NONE
, child
, 0,
2796 "no-pm-components", NULL
, 0)
2797 != DDI_PROP_SUCCESS
) {
2798 cmn_err(CE_WARN
, "pshot%d:"
2799 " nexus_properties:\n\tunable to create"
2800 " the \"no-pm-components\""
2801 " property for %s@%s",
2802 ddi_get_instance(parent
), cname
, caddr
);
2809 pshot_leaf_properties(dev_info_t
*parent
, dev_info_t
*child
, char *cname
,
2815 * extract the address extension
2817 extension
= strstr(caddr
, ",");
2818 if (extension
!= NULL
) {
2825 * Create the "no-involuntary-power-cycles" property for
2826 * all leaf nodes with extension "no_invol"
2828 if (strcmp(extension
, "no_invol") == 0) {
2829 if (ddi_prop_exists(DDI_DEV_T_ANY
, child
,
2830 (DDI_PROP_DONTPASS
| DDI_PROP_NOTPROM
),
2831 "no-involuntary-power-cycles") == 0) {
2833 cmn_err(CE_CONT
, "pshot%d:"
2834 " leaf_properties:\n\tcreate the"
2835 " \"no-involuntary-power-cycles\""
2836 " property for %s@%s\n",
2837 ddi_get_instance(parent
), cname
, caddr
);
2839 if (ddi_prop_create(DDI_DEV_T_NONE
, child
,
2841 "no-involuntary-power-cycles", NULL
, 0)
2842 != DDI_PROP_SUCCESS
) {
2843 cmn_err(CE_WARN
, "pshot%d:"
2844 " leaf_properties:\n\tunable to create the"
2845 " \"no-involuntary-power-cycles\""
2846 " property for %s@%s",
2847 ddi_get_instance(parent
), cname
, caddr
);
2853 * Create the "dependency-property" property for all leaf
2854 * nodes with extension "dep_prop"
2855 * to be used with the PM_ADD_DEPENDENT_PROPERTY ioctl
2857 if (strcmp(extension
, "dep_prop") == 0) {
2858 if (ddi_prop_exists(DDI_DEV_T_ANY
, child
,
2859 (DDI_PROP_DONTPASS
| DDI_PROP_NOTPROM
),
2860 "dependency-property") == 0) {
2862 cmn_err(CE_CONT
, "pshot%d:"
2863 " leaf_properties:\n\tcreate the"
2864 " \"dependency-property\""
2865 " property for %s@%s\n",
2866 ddi_get_instance(parent
), cname
, caddr
);
2868 if (ddi_prop_create(DDI_DEV_T_NONE
, child
,
2869 DDI_PROP_CANSLEEP
, "dependency-property", NULL
, 0)
2870 != DDI_PROP_SUCCESS
) {
2871 cmn_err(CE_WARN
, "pshot%d:"
2872 " leaf_properties:\n\tunable to create the"
2873 " \"dependency-property\" property for"
2874 " %s@%s", ddi_get_instance(parent
),
2882 * BUS_CONFIG_ONE: setup a child nexus instance.
2885 pshot_bus_config_setup_nexus(dev_info_t
*parent
, char *cname
, char *caddr
)
2890 ASSERT(parent
!= 0);
2891 ASSERT(cname
!= NULL
);
2892 ASSERT(caddr
!= NULL
);
2894 child
= pshot_findchild(parent
, cname
, caddr
);
2898 "pshot%d: bus_config one %s@%s found\n",
2899 ddi_get_instance(parent
), cname
, caddr
);
2903 * create the "pm-want-child-notification?" property
2904 * for this child, if it doesn't already exist
2906 (void) pshot_nexus_properties(parent
, child
, cname
, caddr
);
2908 return (NDI_SUCCESS
);
2911 ndi_devi_alloc_sleep(parent
, cname
, DEVI_SID_NODEID
, &child
);
2912 ASSERT(child
!= NULL
);
2914 if (ndi_prop_update_string(DDI_DEV_T_NONE
, child
,
2915 "bus-addr", caddr
) != DDI_PROP_SUCCESS
) {
2916 cmn_err(CE_WARN
, "pshot%d: _prop_update %s@%s failed",
2917 ddi_get_instance(parent
), cname
, caddr
);
2918 (void) ndi_devi_free(child
);
2919 return (NDI_FAILURE
);
2922 rval
= ndi_devi_bind_driver(child
, 0);
2923 if (rval
!= NDI_SUCCESS
) {
2924 cmn_err(CE_WARN
, "pshot%d: bind_driver %s failed",
2925 ddi_get_instance(parent
), cname
);
2926 (void) ndi_devi_free(child
);
2927 return (NDI_FAILURE
);
2931 * create the "pm-want-child-notification?" property
2933 (void) pshot_nexus_properties(parent
, child
, cname
, caddr
);
2935 return (NDI_SUCCESS
);
2939 * BUS_CONFIG_ONE: setup a child leaf device instance.
2940 * for testing purposes, we will create nodes of a variety of types.
2943 pshot_bus_config_setup_leaf(dev_info_t
*parent
, char *cname
, char *caddr
)
2951 ASSERT(parent
!= 0);
2952 ASSERT(cname
!= NULL
);
2953 ASSERT(caddr
!= NULL
);
2956 * if we already have a node with this name, return it
2958 if ((child
= pshot_findchild(parent
, cname
, caddr
)) != NULL
) {
2960 * create the "no-involuntary-power-cycles" or
2961 * the "dependency-property" property, if they
2962 * don't already exit
2964 (void) pshot_leaf_properties(parent
, child
, cname
, caddr
);
2966 return (NDI_SUCCESS
);
2969 ndi_devi_alloc_sleep(parent
, cname
, DEVI_SID_NODEID
, &child
);
2970 ASSERT(child
!= NULL
);
2972 if (ndi_prop_update_string(DDI_DEV_T_NONE
, child
, "bus-addr",
2973 caddr
) != DDI_PROP_SUCCESS
) {
2974 (void) ndi_devi_free(child
);
2975 return (NDI_FAILURE
);
2979 * test compatible naming
2980 * if the child nodename is "cdisk", attach the list of compatible
2983 if (strcmp(cname
, pshot_compat_diskname
) == 0) {
2984 if ((ndi_prop_update_string_array(DDI_DEV_T_NONE
,
2985 child
, "compatible", (char **)pshot_compat_psramdisks
,
2986 5)) != DDI_PROP_SUCCESS
) {
2987 (void) ndi_devi_free(child
);
2988 return (NDI_FAILURE
);
2991 for (i
= 0; i
< pshot_devices_len
&& pshot_devices
[i
].name
;
2993 if (strcmp(cname
, pshot_devices
[i
].name
) == 0) {
2994 compat_name
= pshot_devices
[i
].compat
;
2995 nodetype
= pshot_devices
[i
].nodetype
;
2997 cmn_err(CE_CONT
, "pshot%d: %s %s %s\n",
2998 ddi_get_instance(parent
), cname
,
2999 compat_name
, nodetype
);
3001 if ((ndi_prop_update_string_array(
3002 DDI_DEV_T_NONE
, child
, "compatible",
3003 &compat_name
, 1)) != DDI_PROP_SUCCESS
) {
3004 (void) ndi_devi_free(child
);
3005 return (NDI_FAILURE
);
3007 if ((ndi_prop_update_string(
3008 DDI_DEV_T_NONE
, child
, "node-type",
3009 nodetype
)) != DDI_PROP_SUCCESS
) {
3010 (void) ndi_devi_free(child
);
3011 return (NDI_FAILURE
);
3017 rval
= ndi_devi_bind_driver(child
, 0);
3018 if (rval
!= NDI_SUCCESS
) {
3019 cmn_err(CE_WARN
, "pshot%d: bind_driver %s failed",
3020 ddi_get_instance(parent
), cname
);
3021 (void) ndi_devi_free(child
);
3022 return (NDI_FAILURE
);
3026 * create the "no-involuntary-power-cycles" or
3027 * the "dependency-property" property
3029 (void) pshot_leaf_properties(parent
, child
, cname
, caddr
);
3031 return (NDI_SUCCESS
);
3035 * Handle some special cases for testing bus_config via pshot
3037 * Match these special address formats to behavior:
3039 * err.* - induce bus_config error
3040 * delay - induce 1 second of bus_config delay time
3041 * delay,n - induce n seconds of bus_config delay time
3042 * wait - induce 1 second of bus_config wait time
3043 * wait,n - induce n seconds of bus_config wait time
3044 * failinit.* - induce error at INITCHILD
3045 * failprobe.* - induce error at probe
3046 * failattach.* - induce error at attach
3050 pshot_bus_config_test_specials(dev_info_t
*parent
, char *devname
,
3051 char *cname
, char *caddr
)
3056 if (strncmp(caddr
, "err", 3) == 0) {
3059 "pshot%d: %s forced failure\n",
3060 ddi_get_instance(parent
), devname
);
3061 return (NDI_FAILURE
);
3065 * The delay and wait strings have the same effect.
3066 * The "wait[,]" support should be removed once the
3067 * devfs test suites are fixed.
3068 * NOTE: delay should not be called from interrupt context
3070 ASSERT(!servicing_interrupt());
3072 if (strncmp(caddr
, "delay,", 6) == 0) {
3079 "pshot%d: %s delay %d second\n",
3080 ddi_get_instance(parent
), devname
, n
);
3081 delay(n
* drv_usectohz(1000000));
3082 } else if (strncmp(caddr
, "delay", 5) == 0) {
3085 "pshot%d: %s delay 1 second\n",
3086 ddi_get_instance(parent
), devname
);
3087 delay(drv_usectohz(1000000));
3088 } else if (strncmp(caddr
, "wait,", 5) == 0) {
3095 "pshot%d: %s wait %d second\n",
3096 ddi_get_instance(parent
), devname
, n
);
3097 delay(n
* drv_usectohz(1000000));
3098 } else if (strncmp(caddr
, "wait", 4) == 0) {
3101 "pshot%d: %s wait 1 second\n",
3102 ddi_get_instance(parent
), devname
);
3103 delay(drv_usectohz(1000000));
3106 return (NDI_SUCCESS
);
3110 * translate nodetype name to actual value
3113 pshot_str2nt(char *str
)
3117 for (i
= 0; pshot_nodetypes
[i
].name
; i
++) {
3118 if (strcmp(pshot_nodetypes
[i
].name
, str
) == 0)
3119 return (pshot_nodetypes
[i
].val
);
3125 * grows array pointed to by <dstp>, with <src> data
3126 * <dstlen> = # elements of the original <*dstp>
3127 * <srclen> = # elements of <src>
3129 * on success, returns 0 and a pointer to the new array through <dstp> with
3130 * <srclen> + <dstlen> number of elements;
3131 * else returns non-zero
3133 * a NULL <*dstp> is OK (a NULL <dstp> is not) and so is a zero <dstlen>
3136 pshot_devices_grow(pshot_device_t
**dstp
, size_t dstlen
,
3137 const pshot_device_t
*src
, size_t srclen
)
3140 pshot_device_t
*newdst
;
3142 newdst
= kmem_alloc((srclen
+ dstlen
) * sizeof (*src
),
3145 /* keep old pointers and dup new ones */
3147 bcopy(*dstp
, newdst
, dstlen
* sizeof (*src
));
3148 for (i
= 0; i
< srclen
; i
++) {
3149 newdst
[i
+ dstlen
].name
=
3150 i_ddi_strdup(src
[i
].name
, KM_SLEEP
);
3152 newdst
[i
+ dstlen
].nodetype
=
3153 i_ddi_strdup(src
[i
].nodetype
, KM_SLEEP
);
3155 newdst
[i
+ dstlen
].compat
=
3156 i_ddi_strdup(src
[i
].compat
, KM_SLEEP
);
3161 kmem_free(*dstp
, dstlen
* sizeof (*src
));
3167 * free a pshot_device_t array <dp> with <len> elements
3168 * null pointers within the elements are ok
3171 pshot_devices_free(pshot_device_t
*dp
, size_t len
)
3175 for (i
= 0; i
< len
; i
++) {
3177 kmem_free(dp
[i
].name
, strlen(dp
[i
].name
) + 1);
3179 kmem_free(dp
[i
].nodetype
, strlen(dp
[i
].nodetype
) + 1);
3181 kmem_free(dp
[i
].compat
, strlen(dp
[i
].compat
) + 1);
3183 kmem_free(dp
, len
* sizeof (*dp
));
3187 * returns an array of pshot_device_t parsed from <dip>'s properties
3189 * property structure (i.e. pshot.conf) for pshot:
3191 * corresponding | pshot_device_t array elements
3193 * member by prop name | [0] [1] [2]
3194 * ----------------------|--------------|-------------|-----------------------
3195 * <PSHOT_PROP_DEVNAME> ="disk", "tape", "testdev";
3196 * <PSHOT_PROP_DEVNT> ="DDI_NT_BLOCK","DDI_NT_TAPE","ddi_testdev_nodetype";
3197 * <PSHOT_PROP_DEVCOMPAT>="testdrv", "testdrv", "testdrv";
3200 * if any of these properties are specified, then:
3201 * - all the members must be specified
3202 * - the number of elements for each string array property must be the same
3203 * - no empty strings allowed
3204 * - nodetypes (PSHOT_PROP_DEVNT) must be the nodetype name as specified in
3207 * NOTE: the pshot_nodetypes[] table should be kept in sync with the list
3208 * of ddi nodetypes. It's not normally critical to always be in sync so
3209 * keeping this up-to-date can usually be done "on-demand".
3211 * if <flags> & PSHOT_DEV_ANYNT, then custom nodetype strings are allowed.
3212 * these will be duplicated verbatim
3214 static pshot_device_t
*
3215 pshot_devices_from_props(dev_info_t
*dip
, size_t *lenp
, int flags
)
3217 pshot_device_t
*devarr
= NULL
;
3218 char **name_arr
= NULL
, **nt_arr
= NULL
, **compat_arr
= NULL
;
3219 uint_t name_arr_len
, nt_arr_len
, compat_arr_len
;
3223 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY
, dip
, 0,
3224 PSHOT_PROP_DEVNAME
, &name_arr
, &name_arr_len
) !=
3228 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY
, dip
, 0,
3229 PSHOT_PROP_DEVNT
, &nt_arr
, &nt_arr_len
) !=
3233 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY
, dip
, 0,
3234 PSHOT_PROP_DEVCOMPAT
, &compat_arr
, &compat_arr_len
) !=
3239 * warn about any incorrect usage, if specified
3241 if (!(name_arr
|| nt_arr
|| compat_arr
))
3244 if (!(name_arr
&& nt_arr
&& compat_arr
) ||
3245 (name_arr_len
!= nt_arr_len
) ||
3246 (name_arr_len
!= compat_arr_len
))
3249 for (i
= 0; i
< name_arr_len
; i
++) {
3250 if (*name_arr
[i
] == '\0' ||
3251 *nt_arr
[i
] == '\0' ||
3252 *compat_arr
[i
] == '\0')
3256 devarr
= kmem_zalloc(name_arr_len
* sizeof (*devarr
), KM_SLEEP
);
3257 for (i
= 0; i
< name_arr_len
; i
++) {
3258 devarr
[i
].name
= i_ddi_strdup(name_arr
[i
], KM_SLEEP
);
3259 devarr
[i
].compat
= i_ddi_strdup(compat_arr
[i
], KM_SLEEP
);
3261 if ((str
= pshot_str2nt(nt_arr
[i
])) == NULL
)
3262 if (flags
& PSHOT_DEV_ANYNT
)
3266 devarr
[i
].nodetype
= i_ddi_strdup(str
, KM_SLEEP
);
3268 ddi_prop_free(name_arr
);
3269 ddi_prop_free(nt_arr
);
3270 ddi_prop_free(compat_arr
);
3272 /* set <*lenp> ONLY on success */
3273 *lenp
= name_arr_len
;
3278 cmn_err(CE_WARN
, "malformed device specification property");
3280 ddi_prop_free(name_arr
);
3282 ddi_prop_free(nt_arr
);
3284 ddi_prop_free(compat_arr
);
3286 pshot_devices_free(devarr
, name_arr_len
);
3291 * if global <pshot_devices> was not set up already (i.e. is NULL):
3292 * sets up global <pshot_devices> and <pshot_devices_len>,
3293 * using device properties from <dip> and global <pshot_stock_devices>.
3294 * device properties, if any, overrides pshot_stock_devices.
3296 * returns 0 on success (or if pshot_devices already set up)
3298 * INTERNAL LOCKING: <pshot_devices_lock>
3301 pshot_devices_setup(dev_info_t
*dip
)
3303 pshot_device_t
*newdevs
= NULL
;
3304 size_t newdevs_len
= 0;
3307 mutex_enter(&pshot_devices_lock
);
3308 if (pshot_devices
!= NULL
)
3311 ASSERT(pshot_devices_len
== 0);
3313 newdevs
= pshot_devices_from_props(dip
, &newdevs_len
, PSHOT_DEV_ANYNT
);
3314 rv
= pshot_devices_grow(&newdevs
, newdevs_len
, pshot_stock_devices
,
3315 PSHOT_N_STOCK_DEVICES
);
3317 cmn_err(CE_WARN
, "pshot_devices_setup: pshot_devices_grow "
3321 newdevs_len
+= PSHOT_N_STOCK_DEVICES
;
3323 pshot_devices
= newdevs
;
3324 pshot_devices_len
= newdevs_len
;
3328 pshot_devices_free(newdevs
, newdevs_len
);
3329 mutex_exit(&pshot_devices_lock
);
3337 pshot_probe_family(dev_info_t
*self
, ddi_probe_method_t probe_how
,
3338 dev_info_t
**return_dip
)
3344 for (bus_id
= 10; bus_id
< 20; bus_id
++) {
3345 (void) sprintf(name
, "%d", bus_id
);
3346 if ((ndi_devi_alloc(self
, "psramd", DEVI_SID_NODEID
,
3347 &child
)) != NDI_SUCCESS
) {
3348 return (DDI_FAILURE
);
3351 if (ndi_prop_update_string(DDI_DEV_T_NONE
, child
,
3352 "bus-addr", name
) != DDI_PROP_SUCCESS
) {
3353 (void) ndi_devi_free(child
);
3354 if (return_dip
!= NULL
)
3355 *return_dip
= (dev_info_t
*)NULL
;
3356 return (DDI_FAILURE
);
3359 if (ndi_devi_online(child
, 0) != NDI_SUCCESS
) {
3360 return (DDI_FAILURE
);
3363 return (DDI_SUCCESS
);
3372 for (val
= 0, c
= *str
++; c
>= '0' && c
<= '9'; c
= *str
++) {
3382 pshot_setup_autoattach(dev_info_t
*devi
)
3384 dev_info_t
*l1child
, *l2child
;
3387 rv
= ndi_devi_alloc(devi
, "pshot", DEVI_SID_NODEID
, &l1child
);
3388 if (rv
== NDI_SUCCESS
) {
3389 (void) ndi_prop_update_string(DDI_DEV_T_NONE
, l1child
,
3391 rv
= ndi_devi_alloc(l1child
, "port", DEVI_SID_NODEID
,
3393 if (rv
== NDI_SUCCESS
)
3394 (void) ndi_prop_update_string(DDI_DEV_T_NONE
,
3395 l2child
, "bus-addr", "99");
3398 rv
= ndi_devi_alloc(devi
, "port", DEVI_SID_NODEID
, &l1child
);
3399 if (rv
== NDI_SUCCESS
)
3400 (void) ndi_prop_update_string(DDI_DEV_T_NONE
, l1child
,
3403 rv
= ndi_devi_alloc(devi
, "gen_drv", DEVI_SID_NODEID
, &l1child
);
3404 if (rv
== NDI_SUCCESS
)
3405 (void) ndi_prop_update_string(DDI_DEV_T_NONE
, l1child
,
3408 rv
= ndi_devi_alloc(devi
, "no_driver", DEVI_SID_NODEID
, &l1child
);
3409 if (rv
== NDI_SUCCESS
)
3410 (void) ndi_devi_alloc(l1child
, "no_driver", DEVI_SID_NODEID
,
3416 #define PRUNE_THIS_NODE(d) (((d)->devi_node_name != NULL) && \
3417 (DEVI_PROM_NODE((d)->devi_nodeid)) && \
3418 ((d)->devi_addr == NULL))
3420 * test code to remove OBP nodes that have not attached
3423 prune_snubs(const char *name
)
3425 struct dev_info
*nex_dip
, *cdip
, *cndip
;
3429 maj
= ddi_name_to_major((char *)name
);
3431 nex_dip
= (struct dev_info
*)devnamesp
[maj
].dn_head
;
3432 while (nex_dip
!= NULL
) {
3433 cndip
= ddi_get_child(nex_dip
);
3434 while ((cdip
= cndip
) != NULL
) {
3435 cndip
= cdip
->devi_sibling
;
3436 if (PRUNE_THIS_NODE(cdip
)) {
3438 "parent %s@%s pruning node %s",
3439 nex_dip
->devi_node_name
,
3441 cdip
->devi_node_name
);
3442 rv
= ndi_devi_offline(cdip
,
3444 if (rv
!= NDI_SUCCESS
)
3446 "failed to prune node, "
3450 nex_dip
= nex_dip
->devi_next
;
3455 #endif /* PRUBE_SNUBS */
3457 #ifdef KERNEL_DEVICE_TREE_WALKER
3458 static kthread_id_t pwt
;
3459 static kmutex_t pwl
;
3460 static kcondvar_t pwcv
;
3465 static int pshot_devnode(dev_info_t
*dip
, void * arg
);
3467 dev_info_t
*root
= ddi_root_node();
3468 ddi_walk_devs(root
, pshot_devnode
, NULL
);
3474 static void pshot_timeout(void *arg
);
3475 static kthread_id_t pwt
;
3478 mutex_init(&pwl
, NULL
, MUTEX_DRIVER
, NULL
);
3479 cv_init(&pwcv
, NULL
, CV_DRIVER
, NULL
);
3484 (void) timeout(pshot_timeout
, NULL
, 5 * drv_usectohz(1000000));
3485 cv_wait(&pwcv
, &pwl
);
3491 pshot_timeout(void *arg
)
3499 pshot_devnode(dev_info_t
*dip
, void *arg
)
3503 if (dip
!= ddi_root_node()) {
3504 f_dip
= ndi_devi_find((dev_info_t
*)DEVI(dip
)->devi_parent
,
3505 DEVI(dip
)->devi_node_name
, DEVI(dip
)->devi_addr
);
3507 cmn_err(CE_NOTE
, "!pshot_devnode: failed lookup"
3508 "node (%s/%s@%s)\n",
3509 DEVI(DEVI(dip
)->devi_parent
)->devi_node_name
,
3510 (DEVI(dip
)->devi_node_name
?
3511 DEVI(dip
)->devi_node_name
: "NULL"),
3512 (DEVI(dip
)->devi_addr
? DEVI(dip
)->devi_addr
:
3516 return (DDI_WALK_CONTINUE
);
3518 #endif /* KERNEL_DEVICE_TREE_WALKER */
3522 pshot_event_cb_test(dev_info_t
*dip
, ddi_eventcookie_t cookie
,
3523 void *arg
, void *bus_impldata
)
3525 pshot_t
*softstate
= (pshot_t
*)arg
;
3528 /* look up the event */
3529 event_tag
= NDI_EVENT_TAG(cookie
);
3530 cmn_err(CE_CONT
, "pshot_event_cb_test:\n\t"
3531 "dip = 0x%p cookie = 0x%p (%s), tag = %d\n\t"
3532 "arg = 0x%p bus_impl = 0x%p\n",
3533 (void *)dip
, (void *)cookie
, NDI_EVENT_NAME(cookie
),
3534 event_tag
, (void *)softstate
, (void *)bus_impldata
);
3539 pshot_event_test(void *arg
)
3541 pshot_t
*pshot
= (pshot_t
*)arg
;
3542 ndi_event_hdl_t hdl
;
3543 ndi_event_set_t events
;
3546 (void) ndi_event_alloc_hdl(pshot
->dip
, NULL
, &hdl
, NDI_SLEEP
);
3548 events
.ndi_events_version
= NDI_EVENTS_REV1
;
3549 events
.ndi_n_events
= PSHOT_N_TEST_EVENTS
;
3550 events
.ndi_event_defs
= pshot_test_events
;
3552 cmn_err(CE_CONT
, "pshot: binding set of 8 events\n");
3553 delay(drv_usectohz(1000000));
3554 rval
= ndi_event_bind_set(hdl
, &events
, NDI_SLEEP
);
3555 cmn_err(CE_CONT
, "pshot: ndi_event_bind_set rval = %d\n", rval
);
3557 cmn_err(CE_CONT
, "pshot: binding the same set of 8 events\n");
3558 delay(drv_usectohz(1000000));
3559 rval
= ndi_event_bind_set(hdl
, &events
, NDI_SLEEP
);
3560 cmn_err(CE_CONT
, "pshot: ndi_event_bind_set rval = %d\n", rval
);
3562 cmn_err(CE_CONT
, "pshot: unbinding all events\n");
3563 delay(drv_usectohz(1000000));
3564 rval
= ndi_event_unbind_set(hdl
, &events
, NDI_SLEEP
);
3565 cmn_err(CE_CONT
, "pshot: ndi_event_unbind_set rval = %d\n", rval
);
3568 cmn_err(CE_CONT
, "pshot: binding one highlevel event\n");
3569 delay(drv_usectohz(1000000));
3570 events
.ndi_n_events
= 1;
3571 events
.ndi_event_defs
= pshot_test_events_high
;
3572 rval
= ndi_event_bind_set(hdl
, &events
, NDI_SLEEP
);
3573 cmn_err(CE_CONT
, "pshot: ndi_event_bind_set rval = %d\n", rval
);
3575 cmn_err(CE_CONT
, "pshot: binding the same set of 8 events\n");
3576 delay(drv_usectohz(1000000));
3577 events
.ndi_n_events
= PSHOT_N_TEST_EVENTS
;
3578 events
.ndi_event_defs
= pshot_test_events
;
3579 rval
= ndi_event_bind_set(hdl
, &events
, NDI_SLEEP
);
3580 cmn_err(CE_CONT
, "pshot: ndi_event_bind_set rval = %d\n", rval
);
3582 cmn_err(CE_CONT
, "pshot: unbinding one highlevel event\n");
3583 delay(drv_usectohz(1000000));
3584 events
.ndi_n_events
= 1;
3585 events
.ndi_event_defs
= pshot_test_events_high
;
3586 rval
= ndi_event_unbind_set(hdl
, &events
, NDI_SLEEP
);
3587 cmn_err(CE_CONT
, "pshot: ndi_event_bind_set rval = %d\n", rval
);
3589 cmn_err(CE_CONT
, "pshot: binding one highlevel event\n");
3590 delay(drv_usectohz(1000000));
3591 events
.ndi_n_events
= 1;
3592 events
.ndi_event_defs
= pshot_test_events_high
;
3593 rval
= ndi_event_bind_set(hdl
, &events
, NDI_SLEEP
);
3594 cmn_err(CE_CONT
, "pshot: ndi_event_bind_set rval = %d\n", rval
);
3596 cmn_err(CE_CONT
, "pshot: unbinding one highlevel event\n");
3597 delay(drv_usectohz(1000000));
3598 events
.ndi_n_events
= 1;
3599 events
.ndi_event_defs
= pshot_test_events_high
;
3600 rval
= ndi_event_unbind_set(hdl
, &events
, NDI_SLEEP
);
3601 cmn_err(CE_CONT
, "pshot: ndi_event_bind_set rval = %d\n", rval
);
3603 cmn_err(CE_CONT
, "pshot: binding the same set of 8 events\n");
3604 delay(drv_usectohz(1000000));
3605 events
.ndi_n_events
= PSHOT_N_TEST_EVENTS
;
3606 events
.ndi_event_defs
= pshot_test_events
;
3607 rval
= ndi_event_bind_set(hdl
, &events
, NDI_SLEEP
);
3608 cmn_err(CE_CONT
, "pshot: ndi_event_bind_set rval = %d\n", rval
);
3610 cmn_err(CE_CONT
, "pshot: unbinding first 2 events\n");
3611 delay(drv_usectohz(1000000));
3612 events
.ndi_n_events
= 2;
3613 events
.ndi_event_defs
= pshot_test_events
;
3614 rval
= ndi_event_unbind_set(hdl
, &events
, NDI_SLEEP
);
3615 cmn_err(CE_CONT
, "pshot: ndi_event_unbind_set rval = %d\n", rval
);
3617 cmn_err(CE_CONT
, "pshot: unbinding first 2 events again\n");
3618 delay(drv_usectohz(1000000));
3619 events
.ndi_n_events
= 2;
3620 events
.ndi_event_defs
= pshot_test_events
;
3621 rval
= ndi_event_unbind_set(hdl
, &events
, NDI_SLEEP
);
3622 cmn_err(CE_CONT
, "pshot: ndi_event_unbind_set rval = %d\n", rval
);
3624 cmn_err(CE_CONT
, "pshot: unbinding middle 2 events\n");
3625 delay(drv_usectohz(1000000));
3626 events
.ndi_n_events
= 2;
3627 events
.ndi_event_defs
= &pshot_test_events
[4];
3628 rval
= ndi_event_unbind_set(hdl
, &events
, NDI_SLEEP
);
3629 cmn_err(CE_CONT
, "pshot: ndi_event_unbind_set rval = %d\n", rval
);
3631 cmn_err(CE_CONT
, "pshot: binding those 2 events back\n");
3632 delay(drv_usectohz(1000000));
3633 events
.ndi_n_events
= 2;
3634 events
.ndi_event_defs
= &pshot_test_events
[4];
3635 rval
= ndi_event_bind_set(hdl
, &events
, NDI_SLEEP
);
3636 cmn_err(CE_CONT
, "pshot: ndi_event_bind_set rval = %d\n", rval
);
3638 cmn_err(CE_CONT
, "pshot: unbinding 2 events\n");
3639 delay(drv_usectohz(1000000));
3640 events
.ndi_n_events
= 2;
3641 events
.ndi_event_defs
= &pshot_test_events
[4];
3642 rval
= ndi_event_unbind_set(hdl
, &events
, NDI_SLEEP
);
3643 cmn_err(CE_CONT
, "pshot: ndi_event_unbind_set rval = %d\n", rval
);
3645 cmn_err(CE_CONT
, "pshot: unbinding all events\n");
3646 delay(drv_usectohz(1000000));
3647 events
.ndi_n_events
= PSHOT_N_TEST_EVENTS
;
3648 events
.ndi_event_defs
= pshot_test_events
;
3649 rval
= ndi_event_unbind_set(hdl
, &events
, NDI_SLEEP
);
3650 cmn_err(CE_CONT
, "pshot: ndi_event_unbind_set rval = %d\n", rval
);
3652 cmn_err(CE_CONT
, "pshot: unbinding 1 event\n");
3653 delay(drv_usectohz(1000000));
3654 events
.ndi_n_events
= 1;
3655 events
.ndi_event_defs
= &pshot_test_events
[2];
3656 rval
= ndi_event_unbind_set(hdl
, &events
, NDI_SLEEP
);
3657 cmn_err(CE_CONT
, "pshot: ndi_event_unbind_set rval = %d\n", rval
);
3659 cmn_err(CE_CONT
, "pshot: unbinding 1 event\n");
3660 delay(drv_usectohz(1000000));
3661 events
.ndi_n_events
= 1;
3662 events
.ndi_event_defs
= &pshot_test_events
[3];
3663 rval
= ndi_event_unbind_set(hdl
, &events
, NDI_SLEEP
);
3664 cmn_err(CE_CONT
, "pshot: ndi_event_unbind_set rval = %d\n", rval
);
3666 cmn_err(CE_CONT
, "pshot: unbinding 1 event\n");
3667 delay(drv_usectohz(1000000));
3668 events
.ndi_n_events
= 1;
3669 events
.ndi_event_defs
= &pshot_test_events
[6];
3670 rval
= ndi_event_unbind_set(hdl
, &events
, NDI_SLEEP
);
3671 cmn_err(CE_CONT
, "pshot: ndi_event_unbind_set rval = %d\n", rval
);
3673 cmn_err(CE_CONT
, "pshot: unbinding 1 event\n");
3674 delay(drv_usectohz(1000000));
3675 events
.ndi_n_events
= 1;
3676 events
.ndi_event_defs
= &pshot_test_events
[7];
3677 rval
= ndi_event_unbind_set(hdl
, &events
, NDI_SLEEP
);
3678 cmn_err(CE_CONT
, "pshot: ndi_event_unbind_set rval = %d\n", rval
);
3680 events
.ndi_n_events
= PSHOT_N_TEST_EVENTS
;
3681 events
.ndi_event_defs
= pshot_test_events
;
3683 cmn_err(CE_CONT
, "pshot: binding set of 8 events\n");
3684 delay(drv_usectohz(1000000));
3685 rval
= ndi_event_bind_set(hdl
, &events
, NDI_SLEEP
);
3686 cmn_err(CE_CONT
, "pshot: ndi_event_bind_set rval = %d\n", rval
);
3688 cmn_err(CE_CONT
, "pshot: adding 8 callbacks\n");
3689 delay(drv_usectohz(1000000));
3690 for (i
= 0; i
< 8; i
++) {
3691 rval
= ndi_event_add_callback(hdl
, pshot
->dip
,
3692 ndi_event_tag_to_cookie(hdl
,
3693 pshot_test_events
[i
].ndi_event_tag
),
3694 pshot_event_cb_test
,
3695 (void *)(uintptr_t)pshot_test_events
[i
].ndi_event_tag
,
3696 NDI_SLEEP
, &pshot
->test_callback_cache
[i
]);
3697 ASSERT(rval
== NDI_SUCCESS
);
3700 cmn_err(CE_CONT
, "pshot: event callbacks\n");
3702 for (i
= 10; i
< 18; i
++) {
3703 ddi_eventcookie_t cookie
= ndi_event_tag_to_cookie(hdl
, i
);
3705 rval
= ndi_event_run_callbacks(hdl
, pshot
->dip
, cookie
,
3708 cmn_err(CE_CONT
, "pshot: callback, tag=%d rval=%d\n",
3710 delay(drv_usectohz(1000000));
3713 cmn_err(CE_CONT
, "pshot: redo event callbacks\n");
3715 for (i
= 10; i
< 18; i
++) {
3716 ddi_eventcookie_t cookie
= ndi_event_tag_to_cookie(hdl
, i
);
3718 rval
= ndi_event_run_callbacks(hdl
,
3719 pshot
->dip
, cookie
, (void *)hdl
);
3721 cmn_err(CE_CONT
, "pshot: callback, tag=%d rval=%d\n",
3723 delay(drv_usectohz(1000000));
3726 cmn_err(CE_CONT
, "pshot: removing 8 callbacks\n");
3727 delay(drv_usectohz(1000000));
3729 for (i
= 0; i
< 8; i
++) {
3730 (void) ndi_event_remove_callback(hdl
,
3731 pshot
->test_callback_cache
[i
]);
3733 pshot
->test_callback_cache
[i
] = 0;
3736 cmn_err(CE_CONT
, "pshot: freeing handle with bound set\n");
3737 delay(drv_usectohz(1000000));
3739 rval
= ndi_event_free_hdl(hdl
);
3741 ASSERT(rval
== NDI_SUCCESS
);
3746 pshot_event_test_post_one(void *arg
)
3748 pshot_t
*pshot
= (pshot_t
*)arg
;
3750 ddi_eventcookie_t cookie
;
3752 cmn_err(CE_CONT
, "pshot%d: pshot_event_post_one event\n",
3755 if (ddi_get_eventcookie(pshot
->dip
, PSHOT_EVENT_NAME_BUS_TEST_POST
,
3756 &cookie
) != DDI_SUCCESS
) {
3757 cmn_err(CE_NOTE
, "pshot_bus_test_post cookie not found");
3761 rval
= ndi_post_event(pshot
->dip
, pshot
->dip
, cookie
, NULL
);
3763 cmn_err(CE_CONT
, "pshot%d: pshot_event_post_one rval=%d\n",
3764 pshot
->instance
, rval
);
3766 (void) timeout(pshot_event_test_post_one
, (void *)pshot
,
3767 pshot
->instance
* drv_usectohz(60000000));