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