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