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