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