backout 998: breaks common closed drivers
[illumos-gate.git] / usr / src / uts / common / io / usb / usb_mid / usb_mid.c
blob7c6b507b728f9ec4e9ea7089731b4c5bf06ce138
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
28 * usb multi interface and common class driver
30 * this driver attempts to attach each interface to a driver
31 * and may eventually handle common class features such as
32 * shared endpoints
35 #if defined(lint) && !defined(DEBUG)
36 #define DEBUG 1
37 #endif
38 #include <sys/usb/usba/usbai_version.h>
39 #include <sys/usb/usba.h>
40 #include <sys/usb/usba/usba_types.h>
41 #include <sys/usb/usba/usba_impl.h>
42 #include <sys/usb/usba/usba_ugen.h>
43 #include <sys/usb/usb_mid/usb_midvar.h>
45 void usba_free_evdata(usba_evdata_t *);
47 /* Debugging support */
48 uint_t usb_mid_errlevel = USB_LOG_L4;
49 uint_t usb_mid_errmask = (uint_t)DPRINT_MASK_ALL;
50 uint_t usb_mid_instance_debug = (uint_t)-1;
51 uint_t usb_mid_bus_config_debug = 0;
53 _NOTE(DATA_READABLE_WITHOUT_LOCK(usb_mid_errlevel))
54 _NOTE(DATA_READABLE_WITHOUT_LOCK(usb_mid_errmask))
55 _NOTE(DATA_READABLE_WITHOUT_LOCK(usb_mid_instance_debug))
57 _NOTE(SCHEME_PROTECTS_DATA("unique", msgb))
58 _NOTE(SCHEME_PROTECTS_DATA("unique", dev_info))
59 _NOTE(SCHEME_PROTECTS_DATA("unique", usb_pipe_policy))
62 * Hotplug support
63 * Leaf ops (hotplug controls for client devices)
65 static int usb_mid_open(dev_t *, int, int, cred_t *);
66 static int usb_mid_close(dev_t, int, int, cred_t *);
67 static int usb_mid_read(dev_t, struct uio *, cred_t *);
68 static int usb_mid_write(dev_t, struct uio *, cred_t *);
69 static int usb_mid_poll(dev_t, short, int, short *,
70 struct pollhead **);
72 static struct cb_ops usb_mid_cb_ops = {
73 usb_mid_open,
74 usb_mid_close,
75 nodev, /* strategy */
76 nodev, /* print */
77 nodev, /* dump */
78 usb_mid_read, /* read */
79 usb_mid_write, /* write */
80 nodev,
81 nodev, /* devmap */
82 nodev, /* mmap */
83 nodev, /* segmap */
84 usb_mid_poll, /* poll */
85 ddi_prop_op, /* prop_op */
86 NULL,
87 D_MP
90 static int usb_mid_busop_get_eventcookie(dev_info_t *dip,
91 dev_info_t *rdip,
92 char *eventname,
93 ddi_eventcookie_t *cookie);
94 static int usb_mid_busop_add_eventcall(dev_info_t *dip,
95 dev_info_t *rdip,
96 ddi_eventcookie_t cookie,
97 void (*callback)(dev_info_t *dip,
98 ddi_eventcookie_t cookie, void *arg,
99 void *bus_impldata),
100 void *arg, ddi_callback_id_t *cb_id);
101 static int usb_mid_busop_remove_eventcall(dev_info_t *dip,
102 ddi_callback_id_t cb_id);
103 static int usb_mid_busop_post_event(dev_info_t *dip,
104 dev_info_t *rdip,
105 ddi_eventcookie_t cookie,
106 void *bus_impldata);
107 static int usb_mid_bus_config(dev_info_t *dip,
108 uint_t flag,
109 ddi_bus_config_op_t op,
110 void *arg,
111 dev_info_t **child);
112 static int usb_mid_bus_unconfig(dev_info_t *dip,
113 uint_t flag,
114 ddi_bus_config_op_t op,
115 void *arg);
119 * autoconfiguration data and routines.
121 static int usb_mid_info(dev_info_t *, ddi_info_cmd_t,
122 void *, void **);
123 static int usb_mid_attach(dev_info_t *, ddi_attach_cmd_t);
124 static int usb_mid_detach(dev_info_t *, ddi_detach_cmd_t);
126 /* other routines */
127 static void usb_mid_create_pm_components(dev_info_t *, usb_mid_t *);
128 static int usb_mid_bus_ctl(dev_info_t *, dev_info_t *,
129 ddi_ctl_enum_t, void *, void *);
130 static int usb_mid_power(dev_info_t *, int, int);
131 static int usb_mid_restore_device_state(dev_info_t *, usb_mid_t *);
132 static usb_mid_t *usb_mid_obtain_state(dev_info_t *);
133 static void usb_mid_event_cb(dev_info_t *, ddi_eventcookie_t, void *, void *);
136 * Busops vector
138 static struct bus_ops usb_mid_busops = {
139 BUSO_REV,
140 nullbusmap, /* bus_map */
141 NULL, /* bus_get_intrspec */
142 NULL, /* bus_add_intrspec */
143 NULL, /* bus_remove_intrspec */
144 NULL, /* XXXX bus_map_fault */
145 ddi_dma_map, /* bus_dma_map */
146 ddi_dma_allochdl,
147 ddi_dma_freehdl,
148 ddi_dma_bindhdl,
149 ddi_dma_unbindhdl,
150 ddi_dma_flush,
151 ddi_dma_win,
152 ddi_dma_mctl, /* bus_dma_ctl */
153 usb_mid_bus_ctl, /* bus_ctl */
154 ddi_bus_prop_op, /* bus_prop_op */
155 usb_mid_busop_get_eventcookie,
156 usb_mid_busop_add_eventcall,
157 usb_mid_busop_remove_eventcall,
158 usb_mid_busop_post_event, /* bus_post_event */
159 NULL, /* bus_intr_ctl */
160 usb_mid_bus_config, /* bus_config */
161 usb_mid_bus_unconfig, /* bus_unconfig */
162 NULL, /* bus_fm_init */
163 NULL, /* bus_fm_fini */
164 NULL, /* bus_fm_access_enter */
165 NULL, /* bus_fm_access_exit */
166 NULL /* bus_power */
170 static struct dev_ops usb_mid_ops = {
171 DEVO_REV, /* devo_rev, */
172 0, /* refcnt */
173 usb_mid_info, /* info */
174 nulldev, /* identify */
175 nulldev, /* probe */
176 usb_mid_attach, /* attach */
177 usb_mid_detach, /* detach */
178 nodev, /* reset */
179 &usb_mid_cb_ops, /* driver operations */
180 &usb_mid_busops, /* bus operations */
181 usb_mid_power, /* power */
182 ddi_quiesce_not_needed, /* quiesce */
185 static struct modldrv modldrv = {
186 &mod_driverops, /* Type of module. This one is a driver */
187 "USB Multi Interface Driver", /* Name of the module. */
188 &usb_mid_ops, /* driver ops */
191 static struct modlinkage modlinkage = {
192 MODREV_1, (void *)&modldrv, NULL
195 #define USB_MID_INITIAL_SOFT_SPACE 4
196 static void *usb_mid_statep;
200 * prototypes
202 static void usb_mid_create_children(usb_mid_t *usb_mid);
203 static int usb_mid_cleanup(dev_info_t *dip, usb_mid_t *usb_mid);
206 * event definition
208 static ndi_event_definition_t usb_mid_ndi_event_defs[] = {
209 {USBA_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL,
210 NDI_EVENT_POST_TO_ALL},
211 {USBA_EVENT_TAG_HOT_INSERTION, DDI_DEVI_INSERT_EVENT, EPL_KERNEL,
212 NDI_EVENT_POST_TO_ALL},
213 {USBA_EVENT_TAG_POST_RESUME, USBA_POST_RESUME_EVENT, EPL_KERNEL,
214 NDI_EVENT_POST_TO_ALL},
215 {USBA_EVENT_TAG_PRE_SUSPEND, USBA_PRE_SUSPEND_EVENT, EPL_KERNEL,
216 NDI_EVENT_POST_TO_ALL}
219 #define USB_MID_N_NDI_EVENTS \
220 (sizeof (usb_mid_ndi_event_defs) / sizeof (ndi_event_definition_t))
222 static ndi_event_set_t usb_mid_ndi_events = {
223 NDI_EVENTS_REV1, USB_MID_N_NDI_EVENTS, usb_mid_ndi_event_defs};
227 * standard driver entry points
230 _init(void)
232 int rval;
234 rval = ddi_soft_state_init(&usb_mid_statep, sizeof (struct usb_mid),
235 USB_MID_INITIAL_SOFT_SPACE);
236 if (rval != 0) {
237 return (rval);
240 if ((rval = mod_install(&modlinkage)) != 0) {
241 ddi_soft_state_fini(&usb_mid_statep);
242 return (rval);
245 return (rval);
250 _fini(void)
252 int rval;
254 rval = mod_remove(&modlinkage);
256 if (rval) {
257 return (rval);
260 ddi_soft_state_fini(&usb_mid_statep);
262 return (rval);
267 _info(struct modinfo *modinfop)
269 return (mod_info(&modlinkage, modinfop));
273 /*ARGSUSED*/
274 static int
275 usb_mid_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
277 usb_mid_t *usb_mid;
278 int instance =
279 USB_MID_MINOR_TO_INSTANCE(getminor((dev_t)arg));
280 int error = DDI_FAILURE;
282 switch (infocmd) {
283 case DDI_INFO_DEVT2DEVINFO:
284 if ((usb_mid = ddi_get_soft_state(usb_mid_statep,
285 instance)) != NULL) {
286 *result = (void *)usb_mid->mi_dip;
287 if (*result != NULL) {
288 error = DDI_SUCCESS;
290 } else {
291 *result = NULL;
293 break;
295 case DDI_INFO_DEVT2INSTANCE:
296 *result = (void *)(intptr_t)instance;
297 error = DDI_SUCCESS;
298 break;
299 default:
300 break;
303 return (error);
308 * child post attach/detach notification
310 static void
311 usb_mid_post_attach(usb_mid_t *usb_mid, uint8_t ifno, struct attachspec *as)
313 USB_DPRINTF_L2(DPRINT_MASK_PM, usb_mid->mi_log_handle,
314 "usb_mid_post_attach: ifno = %d result = %d", ifno, as->result);
316 /* if child successfully attached, set power */
317 if (as->result == DDI_SUCCESS) {
319 * Check if the child created wants to be power managed.
320 * If yes, the childs power level gets automatically tracked
321 * by DDI_CTLOPS_POWER busctl.
322 * If no, we set power of the new child by default
323 * to USB_DEV_OS_FULL_PWR. Because we should never suspend.
325 mutex_enter(&usb_mid->mi_mutex);
326 usb_mid->mi_attach_count++;
327 mutex_exit(&usb_mid->mi_mutex);
332 static void
333 usb_mid_post_detach(usb_mid_t *usb_mid, uint8_t ifno, struct detachspec *ds)
335 USB_DPRINTF_L2(DPRINT_MASK_PM, usb_mid->mi_log_handle,
336 "usb_mid_post_detach: ifno = %d result = %d", ifno, ds->result);
339 * if the device is successfully detached,
340 * mark component as idle
342 if (ds->result == DDI_SUCCESS) {
343 usba_device_t *usba_device =
344 usba_get_usba_device(usb_mid->mi_dip);
346 mutex_enter(&usb_mid->mi_mutex);
348 /* check for leaks except when where is a ugen open */
349 if ((ds->cmd == DDI_DETACH) &&
350 (--usb_mid->mi_attach_count == 0) && usba_device &&
351 (usb_mid->mi_ugen_open_count == 0)) {
352 usba_check_for_leaks(usba_device);
354 mutex_exit(&usb_mid->mi_mutex);
360 * bus ctl support. we handle notifications here and the
361 * rest goes up to root hub/hcd
363 /*ARGSUSED*/
364 static int
365 usb_mid_bus_ctl(dev_info_t *dip,
366 dev_info_t *rdip,
367 ddi_ctl_enum_t op,
368 void *arg,
369 void *result)
371 usba_device_t *hub_usba_device = usba_get_usba_device(rdip);
372 dev_info_t *root_hub_dip = hub_usba_device->usb_root_hub_dip;
373 usb_mid_t *usb_mid;
374 struct attachspec *as;
375 struct detachspec *ds;
377 usb_mid = usb_mid_obtain_state(dip);
379 USB_DPRINTF_L2(DPRINT_MASK_PM, usb_mid->mi_log_handle,
380 "usb_mid_bus_ctl:\n\t"
381 "dip = 0x%p, rdip = 0x%p, op = 0x%x, arg = 0x%p",
382 (void *)dip, (void *)rdip, op, arg);
384 switch (op) {
385 case DDI_CTLOPS_ATTACH:
386 as = (struct attachspec *)arg;
388 switch (as->when) {
389 case DDI_PRE :
390 /* nothing to do basically */
391 USB_DPRINTF_L2(DPRINT_MASK_PM, usb_mid->mi_log_handle,
392 "DDI_PRE DDI_CTLOPS_ATTACH");
393 break;
394 case DDI_POST :
395 usb_mid_post_attach(usb_mid, usba_get_ifno(rdip),
396 (struct attachspec *)arg);
397 break;
400 break;
401 case DDI_CTLOPS_DETACH:
402 ds = (struct detachspec *)arg;
404 switch (ds->when) {
405 case DDI_PRE :
406 /* nothing to do basically */
407 USB_DPRINTF_L2(DPRINT_MASK_PM, usb_mid->mi_log_handle,
408 "DDI_PRE DDI_CTLOPS_DETACH");
409 break;
410 case DDI_POST :
411 usb_mid_post_detach(usb_mid, usba_get_ifno(rdip),
412 (struct detachspec *)arg);
413 break;
416 break;
417 default:
418 /* pass to root hub to handle */
419 return (usba_bus_ctl(root_hub_dip, rdip, op, arg, result));
422 return (DDI_SUCCESS);
427 * bus enumeration entry points
429 static int
430 usb_mid_bus_config(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
431 void *arg, dev_info_t **child)
433 int rval, circ;
434 usb_mid_t *usb_mid = usb_mid_obtain_state(dip);
436 USB_DPRINTF_L2(DPRINT_MASK_ALL, usb_mid->mi_log_handle,
437 "usb_mid_bus_config: op=%d", op);
439 if (usb_mid_bus_config_debug) {
440 flag |= NDI_DEVI_DEBUG;
443 ndi_devi_enter(dip, &circ);
445 /* enumerate each interface below us */
446 mutex_enter(&usb_mid->mi_mutex);
447 usb_mid_create_children(usb_mid);
448 mutex_exit(&usb_mid->mi_mutex);
450 rval = ndi_busop_bus_config(dip, flag, op, arg, child, 0);
451 ndi_devi_exit(dip, circ);
453 return (rval);
457 static int
458 usb_mid_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
459 void *arg)
461 usb_mid_t *usb_mid = usb_mid_obtain_state(dip);
463 dev_info_t *cdip, *mdip;
464 int interface, circular_count;
465 int rval = NDI_SUCCESS;
467 USB_DPRINTF_L4(DPRINT_MASK_ALL, usb_mid->mi_log_handle,
468 "usb_mid_bus_unconfig: op=%d", op);
470 if (usb_mid_bus_config_debug) {
471 flag |= NDI_DEVI_DEBUG;
475 * first offline and if offlining successful, then
476 * remove children
478 if (op == BUS_UNCONFIG_ALL) {
479 flag &= ~(NDI_DEVI_REMOVE | NDI_UNCONFIG);
482 ndi_devi_enter(dip, &circular_count);
483 rval = ndi_busop_bus_unconfig(dip, flag, op, arg);
485 if (op == BUS_UNCONFIG_ALL && rval == NDI_SUCCESS &&
486 (flag & NDI_AUTODETACH) == 0) {
487 flag |= NDI_DEVI_REMOVE;
488 rval = ndi_busop_bus_unconfig(dip, flag, op, arg);
491 /* update children's list */
492 mutex_enter(&usb_mid->mi_mutex);
493 for (interface = 0; usb_mid->mi_children_dips &&
494 (interface < usb_mid->mi_n_ifs) &&
495 (usb_mid->mi_children_ifs[interface]); interface++) {
496 mdip = usb_mid->mi_children_dips[interface];
498 /* now search if this dip still exists */
499 for (cdip = ddi_get_child(dip); cdip && (cdip != mdip); )
500 cdip = ddi_get_next_sibling(cdip);
502 if (cdip != mdip) {
503 /* we lost the dip on this interface */
504 usb_mid->mi_children_dips[interface] = NULL;
505 } else if (cdip) {
507 * keep in DS_INITALIZED to prevent parent
508 * from detaching
510 (void) ddi_initchild(ddi_get_parent(cdip), cdip);
513 mutex_exit(&usb_mid->mi_mutex);
515 ndi_devi_exit(dip, circular_count);
517 USB_DPRINTF_L4(DPRINT_MASK_ALL, usb_mid->mi_log_handle,
518 "usb_mid_bus_config: rval=%d", rval);
520 return (rval);
524 /* power entry point */
525 /* ARGSUSED */
526 static int
527 usb_mid_power(dev_info_t *dip, int comp, int level)
529 usb_mid_t *usb_mid;
530 usb_common_power_t *midpm;
531 int rval = DDI_FAILURE;
533 usb_mid = usb_mid_obtain_state(dip);
535 USB_DPRINTF_L4(DPRINT_MASK_PM, usb_mid->mi_log_handle,
536 "usb_mid_power: Begin: usb_mid = %p, level = %d",
537 (void *)usb_mid, level);
539 mutex_enter(&usb_mid->mi_mutex);
540 midpm = usb_mid->mi_pm;
542 /* check if we are transitioning to a legal power level */
543 if (USB_DEV_PWRSTATE_OK(midpm->uc_pwr_states, level)) {
544 USB_DPRINTF_L2(DPRINT_MASK_PM, usb_mid->mi_log_handle,
545 "usb_mid_power: illegal power level = %d "
546 "uc_pwr_states = %x", level, midpm->uc_pwr_states);
548 mutex_exit(&usb_mid->mi_mutex);
550 return (rval);
553 rval = usba_common_power(dip, &(midpm->uc_current_power),
554 &(usb_mid->mi_dev_state), level);
556 mutex_exit(&usb_mid->mi_mutex);
558 return (rval);
563 * attach/resume entry point
565 static int
566 usb_mid_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
568 int instance = ddi_get_instance(dip);
569 usb_mid_t *usb_mid = NULL;
570 uint_t n_ifs, i;
571 size_t size;
573 switch (cmd) {
574 case DDI_ATTACH:
576 break;
577 case DDI_RESUME:
578 usb_mid = (usb_mid_t *)ddi_get_soft_state(usb_mid_statep,
579 instance);
580 (void) usb_mid_restore_device_state(dip, usb_mid);
582 if (usb_mid->mi_ugen_hdl) {
583 (void) usb_ugen_attach(usb_mid->mi_ugen_hdl,
584 DDI_RESUME);
587 return (DDI_SUCCESS);
588 default:
590 return (DDI_FAILURE);
594 * Attach:
596 * Allocate soft state and initialize
598 if (ddi_soft_state_zalloc(usb_mid_statep, instance) != DDI_SUCCESS) {
599 goto fail;
602 usb_mid = ddi_get_soft_state(usb_mid_statep, instance);
603 if (usb_mid == NULL) {
605 goto fail;
608 /* allocate handle for logging of messages */
609 usb_mid->mi_log_handle = usb_alloc_log_hdl(dip, "mid",
610 &usb_mid_errlevel,
611 &usb_mid_errmask, &usb_mid_instance_debug,
614 usb_mid->mi_usba_device = usba_get_usba_device(dip);
615 usb_mid->mi_dip = dip;
616 usb_mid->mi_instance = instance;
617 usb_mid->mi_n_ifs = usb_mid->mi_usba_device->usb_n_ifs;
619 /* attach client driver to USBA */
620 if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) {
621 USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
622 "usb_client_attach failed");
623 goto fail;
625 if (usb_get_dev_data(dip, &usb_mid->mi_dev_data, USB_PARSE_LVL_NONE,
626 0) != USB_SUCCESS) {
627 USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
628 "usb_get_dev_data failed");
629 goto fail;
632 mutex_init(&usb_mid->mi_mutex, NULL, MUTEX_DRIVER,
633 usb_mid->mi_dev_data->dev_iblock_cookie);
635 usb_free_dev_data(dip, usb_mid->mi_dev_data);
636 usb_mid->mi_dev_data = NULL;
638 usb_mid->mi_init_state |= USB_MID_LOCK_INIT;
640 if (ddi_create_minor_node(dip, "usb_mid", S_IFCHR,
641 instance << USB_MID_MINOR_INSTANCE_SHIFT,
642 DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
643 USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
644 "cannot create devctl minor node");
645 goto fail;
648 usb_mid->mi_init_state |= USB_MID_MINOR_NODE_CREATED;
651 * allocate array for keeping track of child dips
653 n_ifs = usb_mid->mi_n_ifs;
654 usb_mid->mi_cd_list_length = size = (sizeof (dev_info_t *)) * n_ifs;
656 usb_mid->mi_children_dips = kmem_zalloc(size, KM_SLEEP);
657 usb_mid->mi_child_events = kmem_zalloc(sizeof (uint8_t) * n_ifs,
658 KM_SLEEP);
659 usb_mid->mi_children_ifs = kmem_zalloc(sizeof (uint_t) * n_ifs,
660 KM_SLEEP);
661 for (i = 0; i < n_ifs; i++) {
662 usb_mid->mi_children_ifs[i] = 1;
666 * Event handling: definition and registration
667 * get event handle for events that we have defined
669 (void) ndi_event_alloc_hdl(dip, 0, &usb_mid->mi_ndi_event_hdl,
670 NDI_SLEEP);
672 /* bind event set to the handle */
673 if (ndi_event_bind_set(usb_mid->mi_ndi_event_hdl, &usb_mid_ndi_events,
674 NDI_SLEEP)) {
675 USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
676 "usb_mid_attach: binding event set failed");
678 goto fail;
681 usb_mid->mi_dev_state = USB_DEV_ONLINE;
684 * now create components to power manage this device
685 * before attaching children
687 usb_mid_create_pm_components(dip, usb_mid);
689 /* event registration for events from our parent */
690 usba_common_register_events(usb_mid->mi_dip, 1, usb_mid_event_cb);
692 usb_mid->mi_init_state |= USB_MID_EVENTS_REGISTERED;
694 ddi_report_dev(dip);
696 return (DDI_SUCCESS);
698 fail:
699 USB_DPRINTF_L2(DPRINT_MASK_ATTA, NULL, "usb_mid%d cannot attach",
700 instance);
702 if (usb_mid) {
703 (void) usb_mid_cleanup(dip, usb_mid);
706 return (DDI_FAILURE);
710 /* detach or suspend this instance */
711 static int
712 usb_mid_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
714 usb_mid_t *usb_mid = usb_mid_obtain_state(dip);
716 USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
717 "usb_mid_detach: cmd = 0x%x", cmd);
719 switch (cmd) {
720 case DDI_DETACH:
722 return (usb_mid_cleanup(dip, usb_mid));
723 case DDI_SUSPEND:
724 /* nothing to do */
725 mutex_enter(&usb_mid->mi_mutex);
726 usb_mid->mi_dev_state = USB_DEV_SUSPENDED;
727 mutex_exit(&usb_mid->mi_mutex);
729 if (usb_mid->mi_ugen_hdl) {
730 int rval = usb_ugen_detach(usb_mid->mi_ugen_hdl,
731 DDI_SUSPEND);
732 return (rval == USB_SUCCESS ? DDI_SUCCESS :
733 DDI_FAILURE);
736 return (DDI_SUCCESS);
737 default:
739 return (DDI_FAILURE);
742 _NOTE(NOT_REACHED)
743 /* NOTREACHED */
747 * usb_mid_cleanup:
748 * cleanup usb_mid and deallocate. this function is called for
749 * handling attach failures and detaching including dynamic
750 * reconfiguration
752 /*ARGSUSED*/
753 static int
754 usb_mid_cleanup(dev_info_t *dip, usb_mid_t *usb_mid)
756 usb_common_power_t *midpm;
757 int rval;
759 USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
760 "usb_mid_cleanup:");
762 if ((usb_mid->mi_init_state & USB_MID_LOCK_INIT) == 0) {
764 goto done;
768 * deallocate events, if events are still registered
769 * (ie. children still attached) then we have to fail the detach
771 if (usb_mid->mi_ndi_event_hdl &&
772 (ndi_event_free_hdl(usb_mid->mi_ndi_event_hdl) != NDI_SUCCESS)) {
774 USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
775 "usb_mid_cleanup: ndi_event_free_hdl failed");
777 return (DDI_FAILURE);
781 * Disable the event callbacks, after this point, event
782 * callbacks will never get called. Note we shouldn't hold
783 * mutex while unregistering events because there may be a
784 * competing event callback thread. Event callbacks are done
785 * with ndi mutex held and this can cause a potential deadlock.
786 * Note that cleanup can't fail after deregistration of events.
788 if (usb_mid->mi_init_state & USB_MID_EVENTS_REGISTERED) {
789 usba_common_unregister_events(usb_mid->mi_dip, 1);
792 midpm = usb_mid->mi_pm;
794 mutex_enter(&usb_mid->mi_mutex);
796 if ((midpm) && (usb_mid->mi_dev_state != USB_DEV_DISCONNECTED)) {
798 mutex_exit(&usb_mid->mi_mutex);
800 (void) pm_busy_component(dip, 0);
801 if (midpm->uc_wakeup_enabled) {
803 /* First bring the device to full power */
804 (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
806 rval = usb_handle_remote_wakeup(dip,
807 USB_REMOTE_WAKEUP_DISABLE);
809 if (rval != DDI_SUCCESS) {
810 USB_DPRINTF_L2(DPRINT_MASK_EVENTS,
811 usb_mid->mi_log_handle,
812 "usb_cleanup: disable remote "
813 "wakeup failed, rval=%d", rval);
817 (void) pm_lower_power(usb_mid->mi_dip, 0, USB_DEV_OS_PWR_OFF);
818 (void) pm_idle_component(dip, 0);
819 } else {
820 mutex_exit(&usb_mid->mi_mutex);
823 if (midpm) {
824 kmem_free(midpm, sizeof (usb_common_power_t));
827 /* free children list */
828 if (usb_mid->mi_children_dips) {
829 kmem_free(usb_mid->mi_children_dips,
830 usb_mid->mi_cd_list_length);
833 if (usb_mid->mi_child_events) {
834 kmem_free(usb_mid->mi_child_events, sizeof (uint8_t) *
835 usb_mid->mi_n_ifs);
838 if (usb_mid->mi_children_ifs) {
839 kmem_free(usb_mid->mi_children_ifs, sizeof (uint_t) *
840 usb_mid->mi_n_ifs);
843 if (usb_mid->mi_init_state & USB_MID_MINOR_NODE_CREATED) {
844 ddi_remove_minor_node(dip, NULL);
847 mutex_destroy(&usb_mid->mi_mutex);
849 done:
850 usb_client_detach(dip, usb_mid->mi_dev_data);
852 if (usb_mid->mi_ugen_hdl) {
853 (void) usb_ugen_detach(usb_mid->mi_ugen_hdl, DDI_DETACH);
854 usb_ugen_release_hdl(usb_mid->mi_ugen_hdl);
857 usb_free_log_hdl(usb_mid->mi_log_handle);
858 ddi_soft_state_free(usb_mid_statep, ddi_get_instance(dip));
860 ddi_prop_remove_all(dip);
862 return (DDI_SUCCESS);
866 static void
867 usb_mid_ugen_attach(usb_mid_t *usb_mid, boolean_t remove_children)
869 _NOTE(NO_COMPETING_THREADS_NOW);
871 if (usb_mid->mi_ugen_hdl == NULL) {
872 usb_ugen_info_t usb_ugen_info;
873 int rval;
874 usb_ugen_hdl_t hdl;
876 USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
877 "usb_mid_ugen_attach: get handle");
879 bzero(&usb_ugen_info, sizeof (usb_ugen_info));
881 usb_ugen_info.usb_ugen_flags = (remove_children ?
882 USB_UGEN_REMOVE_CHILDREN : 0);
883 usb_ugen_info.usb_ugen_minor_node_ugen_bits_mask =
884 (dev_t)USB_MID_MINOR_UGEN_BITS_MASK;
885 usb_ugen_info.usb_ugen_minor_node_instance_mask =
886 (dev_t)~USB_MID_MINOR_UGEN_BITS_MASK;
888 mutex_exit(&usb_mid->mi_mutex);
889 hdl = usb_ugen_get_hdl(usb_mid->mi_dip,
890 &usb_ugen_info);
892 if ((rval = usb_ugen_attach(hdl, DDI_ATTACH)) != USB_SUCCESS) {
893 USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
894 "failed to create ugen support (%d)", rval);
895 usb_ugen_release_hdl(hdl);
897 mutex_enter(&usb_mid->mi_mutex);
898 } else {
899 mutex_enter(&usb_mid->mi_mutex);
900 usb_mid->mi_ugen_hdl = hdl;
904 #ifndef lint
905 _NOTE(COMPETING_THREADS_NOW);
906 #endif
911 * usb_mid_create_children:
913 static void
914 usb_mid_create_children(usb_mid_t *usb_mid)
916 usba_device_t *usba_device;
917 uint_t n_ifs, if_count;
918 uint_t i, j;
919 dev_info_t *cdip, *ia_dip;
920 uint_t ugen_bound = 0;
921 uint_t bound_children = 0;
923 usba_device = usba_get_usba_device(usb_mid->mi_dip);
925 USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
926 "usb_mid_attach_child_drivers: port = %d, address = %d",
927 usba_device->usb_port, usba_device->usb_addr);
929 if (usb_mid->mi_removed_children) {
931 return;
934 n_ifs = usb_mid->mi_n_ifs;
935 if_count = 1;
937 USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
938 "usb_mid_create_children: #interfaces = %d", n_ifs);
941 * create all children if not already present
943 for (i = 0; i < n_ifs; i += if_count) {
945 /* ignore since this if is included by an ia */
946 if (usb_mid->mi_children_ifs[i] == 0) {
948 continue;
951 if (usb_mid->mi_children_dips[i] != NULL) {
952 if (i_ddi_node_state(
953 usb_mid->mi_children_dips[i]) >=
954 DS_BOUND) {
955 bound_children++;
958 continue;
961 mutex_exit(&usb_mid->mi_mutex);
962 ia_dip = usba_ready_interface_association_node(usb_mid->mi_dip,
963 i, &if_count);
965 if (ia_dip != NULL) {
966 if (usba_bind_driver(ia_dip) == USB_SUCCESS) {
967 bound_children++;
968 if (strcmp(ddi_driver_name(ia_dip),
969 "ugen") == 0) {
970 ugen_bound++;
975 * IA node owns if_count interfaces.
976 * The rest interfaces own none.
978 mutex_enter(&usb_mid->mi_mutex);
979 usb_mid->mi_children_dips[i] = ia_dip;
980 usb_mid->mi_children_ifs[i] = if_count;
981 for (j = i + 1; j < i + if_count; j++) {
982 usb_mid->mi_children_ifs[j] = 0;
985 continue;
988 cdip = usba_ready_interface_node(usb_mid->mi_dip, i);
990 if (cdip != NULL) {
991 if (usba_bind_driver(cdip) ==
992 USB_SUCCESS) {
993 bound_children++;
994 if (strcmp(ddi_driver_name(cdip),
995 "ugen") == 0) {
996 ugen_bound++;
1001 * interface node owns 1 interface always.
1003 mutex_enter(&usb_mid->mi_mutex);
1004 usb_mid->mi_children_dips[i] = cdip;
1005 usb_mid->mi_children_ifs[i] = 1;
1006 mutex_exit(&usb_mid->mi_mutex);
1010 mutex_enter(&usb_mid->mi_mutex);
1013 usb_mid->mi_removed_children = (bound_children ? B_FALSE : B_TRUE);
1016 * if there are no ugen interface children, create ugen support at
1017 * device level, use a separate thread because we may be at interrupt
1018 * level
1020 if ((ugen_bound == 0) && (usb_mid->mi_ugen_hdl == NULL)) {
1022 * we only need to remove the children if there are
1023 * multiple configurations which would fail if there
1024 * are child interfaces
1026 if ((usb_mid->mi_removed_children == B_FALSE) &&
1027 (usba_device->usb_n_cfgs > 1)) {
1028 USB_DPRINTF_L1(DPRINT_MASK_ATTA,
1029 usb_mid->mi_log_handle,
1030 "can't support ugen for multiple "
1031 "configurations devices that have attached "
1032 "child interface drivers");
1033 } else {
1034 usb_mid_ugen_attach(usb_mid,
1035 usb_mid->mi_removed_children);
1042 * event support
1044 static int
1045 usb_mid_busop_get_eventcookie(dev_info_t *dip,
1046 dev_info_t *rdip, char *eventname, ddi_eventcookie_t *cookie)
1048 usb_mid_t *usb_mid = usb_mid_obtain_state(dip);
1050 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1051 "usb_mid_busop_get_eventcookie: dip=0x%p, rdip=0x%p, "
1052 "event=%s", (void *)dip, (void *)rdip, eventname);
1053 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1054 "(dip=%s%d rdip=%s%d)",
1055 ddi_driver_name(dip), ddi_get_instance(dip),
1056 ddi_driver_name(rdip), ddi_get_instance(rdip));
1058 /* return event cookie, iblock cookie, and level */
1059 return (ndi_event_retrieve_cookie(usb_mid->mi_ndi_event_hdl,
1060 rdip, eventname, cookie, NDI_EVENT_NOPASS));
1064 static int
1065 usb_mid_busop_add_eventcall(dev_info_t *dip,
1066 dev_info_t *rdip,
1067 ddi_eventcookie_t cookie,
1068 void (*callback)(dev_info_t *dip,
1069 ddi_eventcookie_t cookie, void *arg,
1070 void *bus_impldata),
1071 void *arg, ddi_callback_id_t *cb_id)
1073 usb_mid_t *usb_mid = usb_mid_obtain_state(dip);
1074 int ifno = usba_get_ifno(rdip);
1076 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1077 "usb_mid_busop_add_eventcall: dip=0x%p, rdip=0x%p "
1078 "cookie=0x%p, cb=0x%p, arg=0x%p",
1079 (void *)dip, (void *)rdip, (void *)cookie, (void *)callback, arg);
1080 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1081 "(dip=%s%d rdip=%s%d event=%s)",
1082 ddi_driver_name(dip), ddi_get_instance(dip),
1083 ddi_driver_name(rdip), ddi_get_instance(rdip),
1084 ndi_event_cookie_to_name(usb_mid->mi_ndi_event_hdl, cookie));
1086 /* Set flag on children registering events */
1087 switch (ndi_event_cookie_to_tag(usb_mid->mi_ndi_event_hdl, cookie)) {
1088 case USBA_EVENT_TAG_HOT_REMOVAL:
1089 mutex_enter(&usb_mid->mi_mutex);
1090 usb_mid->mi_child_events[ifno] |=
1091 USB_MID_CHILD_EVENT_DISCONNECT;
1092 mutex_exit(&usb_mid->mi_mutex);
1094 break;
1095 case USBA_EVENT_TAG_PRE_SUSPEND:
1096 mutex_enter(&usb_mid->mi_mutex);
1097 usb_mid->mi_child_events[ifno] |=
1098 USB_MID_CHILD_EVENT_PRESUSPEND;
1099 mutex_exit(&usb_mid->mi_mutex);
1101 break;
1102 default:
1104 break;
1106 /* add callback (perform registration) */
1107 return (ndi_event_add_callback(usb_mid->mi_ndi_event_hdl,
1108 rdip, cookie, callback, arg, NDI_SLEEP, cb_id));
1112 static int
1113 usb_mid_busop_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id)
1115 usb_mid_t *usb_mid = usb_mid_obtain_state(dip);
1116 ndi_event_callbacks_t *cb = (ndi_event_callbacks_t *)cb_id;
1118 ASSERT(cb);
1120 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1121 "usb_mid_busop_remove_eventcall: dip=0x%p, rdip=0x%p "
1122 "cookie=0x%p", (void *)dip, (void *)cb->ndi_evtcb_dip,
1123 (void *)cb->ndi_evtcb_cookie);
1124 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1125 "(dip=%s%d rdip=%s%d event=%s)",
1126 ddi_driver_name(dip), ddi_get_instance(dip),
1127 ddi_driver_name(cb->ndi_evtcb_dip),
1128 ddi_get_instance(cb->ndi_evtcb_dip),
1129 ndi_event_cookie_to_name(usb_mid->mi_ndi_event_hdl,
1130 cb->ndi_evtcb_cookie));
1132 /* remove event registration from our event set */
1133 return (ndi_event_remove_callback(usb_mid->mi_ndi_event_hdl, cb_id));
1137 static int
1138 usb_mid_busop_post_event(dev_info_t *dip,
1139 dev_info_t *rdip,
1140 ddi_eventcookie_t cookie,
1141 void *bus_impldata)
1143 usb_mid_t *usb_mid = usb_mid_obtain_state(dip);
1145 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1146 "usb_mid_busop_post_event: dip=0x%p, rdip=0x%p "
1147 "cookie=0x%p, impl=0x%p",
1148 (void *)dip, (void *)rdip, (void *)cookie, bus_impldata);
1149 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1150 "(dip=%s%d rdip=%s%d event=%s)",
1151 ddi_driver_name(dip), ddi_get_instance(dip),
1152 ddi_driver_name(rdip), ddi_get_instance(rdip),
1153 ndi_event_cookie_to_name(usb_mid->mi_ndi_event_hdl, cookie));
1155 /* post event to all children registered for this event */
1156 return (ndi_event_run_callbacks(usb_mid->mi_ndi_event_hdl, rdip,
1157 cookie, bus_impldata));
1162 * usb_mid_restore_device_state
1163 * set the original configuration of the device
1165 static int
1166 usb_mid_restore_device_state(dev_info_t *dip, usb_mid_t *usb_mid)
1168 usb_common_power_t *midpm;
1170 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1171 "usb_mid_restore_device_state: usb_mid = %p", (void *)usb_mid);
1173 mutex_enter(&usb_mid->mi_mutex);
1174 midpm = usb_mid->mi_pm;
1175 mutex_exit(&usb_mid->mi_mutex);
1177 /* First bring the device to full power */
1178 (void) pm_busy_component(dip, 0);
1179 (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
1181 if (usb_check_same_device(dip, usb_mid->mi_log_handle, USB_LOG_L0,
1182 DPRINT_MASK_EVENTS, USB_CHK_VIDPID, NULL) != USB_SUCCESS) {
1184 /* change the device state from suspended to disconnected */
1185 mutex_enter(&usb_mid->mi_mutex);
1186 usb_mid->mi_dev_state = USB_DEV_DISCONNECTED;
1187 mutex_exit(&usb_mid->mi_mutex);
1188 (void) pm_idle_component(dip, 0);
1190 return (USB_FAILURE);
1194 * if the device had remote wakeup earlier,
1195 * enable it again
1197 if (midpm->uc_wakeup_enabled) {
1198 (void) usb_handle_remote_wakeup(usb_mid->mi_dip,
1199 USB_REMOTE_WAKEUP_ENABLE);
1202 mutex_enter(&usb_mid->mi_mutex);
1203 usb_mid->mi_dev_state = USB_DEV_ONLINE;
1204 mutex_exit(&usb_mid->mi_mutex);
1206 (void) pm_idle_component(dip, 0);
1208 return (USB_SUCCESS);
1213 * usb_mid_event_cb()
1214 * handle disconnect and connect events
1216 static void
1217 usb_mid_event_cb(dev_info_t *dip, ddi_eventcookie_t cookie,
1218 void *arg, void *bus_impldata)
1220 int i, tag;
1221 usb_mid_t *usb_mid = usb_mid_obtain_state(dip);
1222 dev_info_t *child_dip;
1223 ddi_eventcookie_t rm_cookie, ins_cookie, suspend_cookie, resume_cookie;
1225 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1226 "usb_mid_event_cb: dip=0x%p, cookie=0x%p, "
1227 "arg=0x%p, impl=0x%p",
1228 (void *)dip, (void *)cookie, arg, bus_impldata);
1229 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1230 "(dip=%s%d event=%s)",
1231 ddi_driver_name(dip), ddi_get_instance(dip),
1232 ndi_event_cookie_to_name(usb_mid->mi_ndi_event_hdl, cookie));
1234 tag = NDI_EVENT_TAG(cookie);
1235 rm_cookie = ndi_event_tag_to_cookie(
1236 usb_mid->mi_ndi_event_hdl, USBA_EVENT_TAG_HOT_REMOVAL);
1237 suspend_cookie = ndi_event_tag_to_cookie(
1238 usb_mid->mi_ndi_event_hdl, USBA_EVENT_TAG_PRE_SUSPEND);
1239 ins_cookie = ndi_event_tag_to_cookie(
1240 usb_mid->mi_ndi_event_hdl, USBA_EVENT_TAG_HOT_INSERTION);
1241 resume_cookie = ndi_event_tag_to_cookie(
1242 usb_mid->mi_ndi_event_hdl, USBA_EVENT_TAG_POST_RESUME);
1244 mutex_enter(&usb_mid->mi_mutex);
1245 switch (tag) {
1246 case USBA_EVENT_TAG_HOT_REMOVAL:
1247 if (usb_mid->mi_dev_state == USB_DEV_DISCONNECTED) {
1248 USB_DPRINTF_L2(DPRINT_MASK_EVENTS,
1249 usb_mid->mi_log_handle,
1250 "usb_mid_event_cb: Device already disconnected");
1251 } else {
1252 /* we are disconnected so set our state now */
1253 usb_mid->mi_dev_state = USB_DEV_DISCONNECTED;
1254 for (i = 0; i < usb_mid->mi_n_ifs; i++) {
1255 usb_mid->mi_child_events[i] &= ~
1256 USB_MID_CHILD_EVENT_DISCONNECT;
1258 mutex_exit(&usb_mid->mi_mutex);
1260 /* pass disconnect event to all the children */
1261 (void) ndi_event_run_callbacks(
1262 usb_mid->mi_ndi_event_hdl, NULL,
1263 rm_cookie, bus_impldata);
1265 if (usb_mid->mi_ugen_hdl) {
1266 (void) usb_ugen_disconnect_ev_cb(
1267 usb_mid->mi_ugen_hdl);
1269 mutex_enter(&usb_mid->mi_mutex);
1271 break;
1272 case USBA_EVENT_TAG_PRE_SUSPEND:
1273 /* set our state *after* suspending children */
1274 mutex_exit(&usb_mid->mi_mutex);
1276 /* pass pre_suspend event to all the children */
1277 (void) ndi_event_run_callbacks(usb_mid->mi_ndi_event_hdl,
1278 NULL, suspend_cookie, bus_impldata);
1280 mutex_enter(&usb_mid->mi_mutex);
1281 for (i = 0; i < usb_mid->mi_n_ifs; i++) {
1282 usb_mid->mi_child_events[i] &= ~
1283 USB_MID_CHILD_EVENT_PRESUSPEND;
1285 break;
1286 case USBA_EVENT_TAG_HOT_INSERTION:
1287 mutex_exit(&usb_mid->mi_mutex);
1288 if (usb_mid_restore_device_state(dip, usb_mid) == USB_SUCCESS) {
1291 * Check to see if this child has missed the disconnect
1292 * event before it registered for event cb
1294 mutex_enter(&usb_mid->mi_mutex);
1295 for (i = 0; i < usb_mid->mi_n_ifs; i++) {
1296 if ((usb_mid->mi_child_events[i] &
1297 USB_MID_CHILD_EVENT_DISCONNECT) &&
1298 usb_mid->mi_children_ifs[i]) {
1299 usb_mid->mi_child_events[i] &=
1300 ~USB_MID_CHILD_EVENT_DISCONNECT;
1301 child_dip =
1302 usb_mid->mi_children_dips[i];
1303 mutex_exit(&usb_mid->mi_mutex);
1305 /* post the missed disconnect */
1306 (void) ndi_event_do_callback(
1307 usb_mid->mi_ndi_event_hdl,
1308 child_dip,
1309 rm_cookie,
1310 bus_impldata);
1311 mutex_enter(&usb_mid->mi_mutex);
1314 mutex_exit(&usb_mid->mi_mutex);
1316 /* pass reconnect event to all the children */
1317 (void) ndi_event_run_callbacks(
1318 usb_mid->mi_ndi_event_hdl, NULL,
1319 ins_cookie, bus_impldata);
1321 if (usb_mid->mi_ugen_hdl) {
1322 (void) usb_ugen_reconnect_ev_cb(
1323 usb_mid->mi_ugen_hdl);
1326 mutex_enter(&usb_mid->mi_mutex);
1327 break;
1328 case USBA_EVENT_TAG_POST_RESUME:
1330 * Check to see if this child has missed the pre-suspend
1331 * event before it registered for event cb
1333 for (i = 0; i < usb_mid->mi_n_ifs; i++) {
1334 if ((usb_mid->mi_child_events[i] &
1335 USB_MID_CHILD_EVENT_PRESUSPEND) &&
1336 usb_mid->mi_children_ifs[i]) {
1337 usb_mid->mi_child_events[i] &=
1338 ~USB_MID_CHILD_EVENT_PRESUSPEND;
1339 child_dip = usb_mid->mi_children_dips[i];
1340 mutex_exit(&usb_mid->mi_mutex);
1342 /* post the missed pre-suspend event */
1343 (void) ndi_event_do_callback(
1344 usb_mid->mi_ndi_event_hdl,
1345 child_dip, suspend_cookie,
1346 bus_impldata);
1347 mutex_enter(&usb_mid->mi_mutex);
1350 mutex_exit(&usb_mid->mi_mutex);
1352 /* pass post_resume event to all the children */
1353 (void) ndi_event_run_callbacks(usb_mid->mi_ndi_event_hdl,
1354 NULL, resume_cookie, bus_impldata);
1356 mutex_enter(&usb_mid->mi_mutex);
1357 break;
1359 mutex_exit(&usb_mid->mi_mutex);
1365 * create the pm components required for power management
1367 static void
1368 usb_mid_create_pm_components(dev_info_t *dip, usb_mid_t *usb_mid)
1370 usb_common_power_t *midpm;
1371 uint_t pwr_states;
1373 USB_DPRINTF_L4(DPRINT_MASK_PM, usb_mid->mi_log_handle,
1374 "usb_mid_create_pm_components: Begin");
1376 /* Allocate the PM state structure */
1377 midpm = kmem_zalloc(sizeof (usb_common_power_t), KM_SLEEP);
1379 mutex_enter(&usb_mid->mi_mutex);
1380 usb_mid->mi_pm = midpm;
1381 midpm->uc_usb_statep = usb_mid;
1382 midpm->uc_pm_capabilities = 0; /* XXXX should this be 0?? */
1383 midpm->uc_current_power = USB_DEV_OS_FULL_PWR;
1384 mutex_exit(&usb_mid->mi_mutex);
1387 * By not enabling parental notification, PM enforces
1388 * "strict parental dependency" meaning, usb_mid won't
1389 * power off until any of its children are in full power.
1393 * there are 3 scenarios:
1394 * 1. a well behaved device should have remote wakeup
1395 * at interface and device level. If the interface
1396 * wakes up, usb_mid will wake up
1397 * 2. if the device doesn't have remote wake up and
1398 * the interface has, PM will still work, ie.
1399 * the interfaces wakes up and usb_mid wakes up
1400 * 3. if neither the interface nor device has remote
1401 * wakeup, the interface will wake up when it is opened
1402 * and goes to sleep after being closed for a while
1403 * In this case usb_mid should also go to sleep shortly
1404 * thereafter
1405 * In all scenarios it doesn't really matter whether
1406 * remote wakeup at the device level is enabled or not
1407 * but we do it anyways
1409 if (usb_handle_remote_wakeup(dip, USB_REMOTE_WAKEUP_ENABLE) ==
1410 USB_SUCCESS) {
1411 USB_DPRINTF_L3(DPRINT_MASK_PM, usb_mid->mi_log_handle,
1412 "usb_mid_create_pm_components: "
1413 "Remote Wakeup Enabled");
1414 midpm->uc_wakeup_enabled = 1;
1417 if (usb_create_pm_components(dip, &pwr_states) ==
1418 USB_SUCCESS) {
1419 midpm->uc_pwr_states = (uint8_t)pwr_states;
1420 (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
1423 USB_DPRINTF_L4(DPRINT_MASK_PM, usb_mid->mi_log_handle,
1424 "usb_mid_create_pm_components: End");
1429 * usb_mid_obtain_state:
1431 usb_mid_t *
1432 usb_mid_obtain_state(dev_info_t *dip)
1434 int instance = ddi_get_instance(dip);
1435 usb_mid_t *statep = ddi_get_soft_state(usb_mid_statep, instance);
1437 ASSERT(statep != NULL);
1439 return (statep);
1444 * ugen support
1446 /* ARGSUSED3 */
1447 static int
1448 usb_mid_open(dev_t *devp, int flags, int otyp, cred_t *credp)
1450 struct usb_mid *usb_mid;
1451 int rval;
1453 if ((usb_mid = ddi_get_soft_state(usb_mid_statep,
1454 USB_MID_MINOR_TO_INSTANCE(getminor(*devp)))) == NULL) {
1456 return (ENXIO);
1459 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, usb_mid->mi_log_handle,
1460 "usb_mid_open: usb_mid = 0x%p *devp = 0x%lx",
1461 (void *)usb_mid, *devp);
1463 /* First bring the device to full power */
1464 (void) pm_busy_component(usb_mid->mi_dip, 0);
1465 (void) pm_raise_power(usb_mid->mi_dip, 0, USB_DEV_OS_FULL_PWR);
1468 rval = usb_ugen_open(usb_mid->mi_ugen_hdl, devp, flags, otyp,
1469 credp);
1470 if (rval) {
1471 (void) pm_idle_component(usb_mid->mi_dip, 0);
1472 } else {
1474 * since all ugen opens are exclusive we can count the
1475 * opens
1477 mutex_enter(&usb_mid->mi_mutex);
1478 usb_mid->mi_ugen_open_count++;
1479 mutex_exit(&usb_mid->mi_mutex);
1482 return (rval);
1486 /* ARGSUSED */
1487 static int
1488 usb_mid_close(dev_t dev, int flag, int otyp, cred_t *credp)
1490 struct usb_mid *usb_mid;
1491 int rval;
1493 if ((usb_mid = ddi_get_soft_state(usb_mid_statep,
1494 USB_MID_MINOR_TO_INSTANCE(getminor(dev)))) == NULL) {
1496 return (ENXIO);
1499 rval = usb_ugen_close(usb_mid->mi_ugen_hdl, dev, flag, otyp,
1500 credp);
1501 if (rval == 0) {
1502 (void) pm_idle_component(usb_mid->mi_dip, 0);
1503 mutex_enter(&usb_mid->mi_mutex);
1504 usb_mid->mi_ugen_open_count--;
1505 mutex_exit(&usb_mid->mi_mutex);
1508 return (rval);
1512 static int
1513 usb_mid_read(dev_t dev, struct uio *uio, cred_t *credp)
1515 struct usb_mid *usb_mid;
1517 if ((usb_mid = ddi_get_soft_state(usb_mid_statep,
1518 USB_MID_MINOR_TO_INSTANCE(getminor(dev)))) == NULL) {
1520 return (ENXIO);
1523 return (usb_ugen_read(usb_mid->mi_ugen_hdl, dev, uio, credp));
1527 static int
1528 usb_mid_write(dev_t dev, struct uio *uio, cred_t *credp)
1530 struct usb_mid *usb_mid;
1532 if ((usb_mid = ddi_get_soft_state(usb_mid_statep,
1533 USB_MID_MINOR_TO_INSTANCE(getminor(dev)))) == NULL) {
1535 return (ENXIO);
1538 return (usb_ugen_write(usb_mid->mi_ugen_hdl, dev, uio, credp));
1542 static int
1543 usb_mid_poll(dev_t dev, short events, int anyyet, short *reventsp,
1544 struct pollhead **phpp)
1546 struct usb_mid *usb_mid;
1548 if ((usb_mid = ddi_get_soft_state(usb_mid_statep,
1549 USB_MID_MINOR_TO_INSTANCE(getminor(dev)))) == NULL) {
1551 return (ENXIO);
1554 return (usb_ugen_poll(usb_mid->mi_ugen_hdl, dev, events,
1555 anyyet, reventsp, phpp));