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