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 2008 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 2023 Oxide Computer Company
28 */
29
30 /*
31 * usb multi interface and common class driver
32 *
33 * this driver attempts to attach each interface to a driver
34 * and may eventually handle common class features such as
35 * shared endpoints
36 */
37
38 #if defined(lint) && !defined(DEBUG)
39 #define DEBUG 1
40 #endif
41 #include <sys/usb/usba/usbai_version.h>
42 #include <sys/usb/usba.h>
43 #include <sys/usb/usba/usba_types.h>
44 #include <sys/usb/usba/usba_impl.h>
45 #include <sys/usb/usba/usba_ugen.h>
46 #include <sys/usb/usb_mid/usb_midvar.h>
47
48 void usba_free_evdata(usba_evdata_t *);
49
50 /* Debugging support */
51 uint_t usb_mid_errlevel = USB_LOG_L4;
52 uint_t usb_mid_errmask = (uint_t)DPRINT_MASK_ALL;
53 uint_t usb_mid_instance_debug = (uint_t)-1;
54 uint_t usb_mid_bus_config_debug = 0;
55
56 _NOTE(DATA_READABLE_WITHOUT_LOCK(usb_mid_errlevel))
57 _NOTE(DATA_READABLE_WITHOUT_LOCK(usb_mid_errmask))
58 _NOTE(DATA_READABLE_WITHOUT_LOCK(usb_mid_instance_debug))
59
60 _NOTE(SCHEME_PROTECTS_DATA("unique", msgb))
61 _NOTE(SCHEME_PROTECTS_DATA("unique", dev_info))
62 _NOTE(SCHEME_PROTECTS_DATA("unique", usb_pipe_policy))
63
64 /*
65 * Hotplug support
66 * Leaf ops (hotplug controls for client devices)
67 */
68 static int usb_mid_open(dev_t *, int, int, cred_t *);
69 static int usb_mid_close(dev_t, int, int, cred_t *);
70 static int usb_mid_read(dev_t, struct uio *, cred_t *);
71 static int usb_mid_write(dev_t, struct uio *, cred_t *);
72 static int usb_mid_poll(dev_t, short, int, short *,
73 struct pollhead **);
74
75 static struct cb_ops usb_mid_cb_ops = {
76 usb_mid_open,
77 usb_mid_close,
78 nodev, /* strategy */
79 nodev, /* print */
80 nodev, /* dump */
81 usb_mid_read, /* read */
82 usb_mid_write, /* write */
83 nodev,
84 nodev, /* devmap */
85 nodev, /* mmap */
86 nodev, /* segmap */
87 usb_mid_poll, /* poll */
88 ddi_prop_op, /* prop_op */
89 NULL,
90 D_MP
91 };
92
93 static int usb_mid_busop_get_eventcookie(dev_info_t *dip,
94 dev_info_t *rdip,
95 char *eventname,
96 ddi_eventcookie_t *cookie);
97 static int usb_mid_busop_add_eventcall(dev_info_t *dip,
98 dev_info_t *rdip,
99 ddi_eventcookie_t cookie,
100 void (*callback)(dev_info_t *dip,
101 ddi_eventcookie_t cookie, void *arg,
102 void *bus_impldata),
103 void *arg, ddi_callback_id_t *cb_id);
104 static int usb_mid_busop_remove_eventcall(dev_info_t *dip,
105 ddi_callback_id_t cb_id);
106 static int usb_mid_busop_post_event(dev_info_t *dip,
107 dev_info_t *rdip,
108 ddi_eventcookie_t cookie,
109 void *bus_impldata);
110 static int usb_mid_bus_config(dev_info_t *dip,
111 uint_t flag,
112 ddi_bus_config_op_t op,
113 void *arg,
114 dev_info_t **child);
115 static int usb_mid_bus_unconfig(dev_info_t *dip,
116 uint_t flag,
117 ddi_bus_config_op_t op,
118 void *arg);
119
120
121 /*
122 * autoconfiguration data and routines.
123 */
124 static int usb_mid_info(dev_info_t *, ddi_info_cmd_t,
125 void *, void **);
126 static int usb_mid_attach(dev_info_t *, ddi_attach_cmd_t);
127 static int usb_mid_detach(dev_info_t *, ddi_detach_cmd_t);
128
129 /* other routines */
130 static void usb_mid_create_pm_components(dev_info_t *, usb_mid_t *);
131 static int usb_mid_bus_ctl(dev_info_t *, dev_info_t *,
132 ddi_ctl_enum_t, void *, void *);
133 static int usb_mid_power(dev_info_t *, int, int);
134 static int usb_mid_restore_device_state(dev_info_t *, usb_mid_t *);
135 static usb_mid_t *usb_mid_obtain_state(dev_info_t *);
136 static void usb_mid_event_cb(dev_info_t *, ddi_eventcookie_t, void *, void *);
137
138 /*
139 * Busops vector
140 */
141 static struct bus_ops usb_mid_busops = {
142 BUSO_REV,
143 nullbusmap, /* bus_map */
144 NULL, /* bus_get_intrspec */
145 NULL, /* bus_add_intrspec */
146 NULL, /* bus_remove_intrspec */
147 NULL, /* XXXX bus_map_fault */
148 NULL, /* bus_dma_map */
149 ddi_dma_allochdl,
150 ddi_dma_freehdl,
151 ddi_dma_bindhdl,
152 ddi_dma_unbindhdl,
153 ddi_dma_flush,
154 ddi_dma_win,
155 ddi_dma_mctl, /* bus_dma_ctl */
156 usb_mid_bus_ctl, /* bus_ctl */
157 ddi_bus_prop_op, /* bus_prop_op */
158 usb_mid_busop_get_eventcookie,
159 usb_mid_busop_add_eventcall,
160 usb_mid_busop_remove_eventcall,
161 usb_mid_busop_post_event, /* bus_post_event */
162 NULL, /* bus_intr_ctl */
163 usb_mid_bus_config, /* bus_config */
164 usb_mid_bus_unconfig, /* bus_unconfig */
165 NULL, /* bus_fm_init */
166 NULL, /* bus_fm_fini */
167 NULL, /* bus_fm_access_enter */
168 NULL, /* bus_fm_access_exit */
169 NULL /* bus_power */
170 };
171
172
173 static struct dev_ops usb_mid_ops = {
174 DEVO_REV, /* devo_rev, */
175 0, /* refcnt */
176 usb_mid_info, /* info */
177 nulldev, /* identify */
178 nulldev, /* probe */
179 usb_mid_attach, /* attach */
180 usb_mid_detach, /* detach */
181 nodev, /* reset */
182 &usb_mid_cb_ops, /* driver operations */
183 &usb_mid_busops, /* bus operations */
184 usb_mid_power, /* power */
185 ddi_quiesce_not_needed, /* quiesce */
186 };
187
188 static struct modldrv modldrv = {
189 &mod_driverops, /* Type of module. This one is a driver */
190 "USB Multi Interface Driver", /* Name of the module. */
191 &usb_mid_ops, /* driver ops */
192 };
193
194 static struct modlinkage modlinkage = {
195 MODREV_1, (void *)&modldrv, NULL
196 };
197
198 #define USB_MID_INITIAL_SOFT_SPACE 4
199 static void *usb_mid_statep;
200
201
202 /*
203 * prototypes
204 */
205 static void usb_mid_create_children(usb_mid_t *usb_mid);
206 static int usb_mid_cleanup(dev_info_t *dip, usb_mid_t *usb_mid);
207
208 /*
209 * event definition
210 */
211 static ndi_event_definition_t usb_mid_ndi_event_defs[] = {
212 {USBA_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL,
213 NDI_EVENT_POST_TO_ALL},
214 {USBA_EVENT_TAG_HOT_INSERTION, DDI_DEVI_INSERT_EVENT, EPL_KERNEL,
215 NDI_EVENT_POST_TO_ALL},
216 {USBA_EVENT_TAG_POST_RESUME, USBA_POST_RESUME_EVENT, EPL_KERNEL,
217 NDI_EVENT_POST_TO_ALL},
218 {USBA_EVENT_TAG_PRE_SUSPEND, USBA_PRE_SUSPEND_EVENT, EPL_KERNEL,
219 NDI_EVENT_POST_TO_ALL}
220 };
221
222 #define USB_MID_N_NDI_EVENTS \
223 (sizeof (usb_mid_ndi_event_defs) / sizeof (ndi_event_definition_t))
224
225 static ndi_event_set_t usb_mid_ndi_events = {
226 NDI_EVENTS_REV1, USB_MID_N_NDI_EVENTS, usb_mid_ndi_event_defs};
227
228
229 /*
230 * standard driver entry points
231 */
232 int
_init(void)233 _init(void)
234 {
235 int rval;
236
237 rval = ddi_soft_state_init(&usb_mid_statep, sizeof (struct usb_mid),
238 USB_MID_INITIAL_SOFT_SPACE);
239 if (rval != 0) {
240 return (rval);
241 }
242
243 if ((rval = mod_install(&modlinkage)) != 0) {
244 ddi_soft_state_fini(&usb_mid_statep);
245 return (rval);
246 }
247
248 return (rval);
249 }
250
251
252 int
_fini(void)253 _fini(void)
254 {
255 int rval;
256
257 rval = mod_remove(&modlinkage);
258
259 if (rval) {
260 return (rval);
261 }
262
263 ddi_soft_state_fini(&usb_mid_statep);
264
265 return (rval);
266 }
267
268
269 int
_info(struct modinfo * modinfop)270 _info(struct modinfo *modinfop)
271 {
272 return (mod_info(&modlinkage, modinfop));
273 }
274
275
276 /*ARGSUSED*/
277 static int
usb_mid_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)278 usb_mid_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
279 {
280 usb_mid_t *usb_mid;
281 int instance =
282 USB_MID_MINOR_TO_INSTANCE(getminor((dev_t)arg));
283 int error = DDI_FAILURE;
284
285 switch (infocmd) {
286 case DDI_INFO_DEVT2DEVINFO:
287 if ((usb_mid = ddi_get_soft_state(usb_mid_statep,
288 instance)) != NULL) {
289 *result = (void *)usb_mid->mi_dip;
290 if (*result != NULL) {
291 error = DDI_SUCCESS;
292 }
293 } else {
294 *result = NULL;
295 }
296 break;
297
298 case DDI_INFO_DEVT2INSTANCE:
299 *result = (void *)(intptr_t)instance;
300 error = DDI_SUCCESS;
301 break;
302 default:
303 break;
304 }
305
306 return (error);
307 }
308
309
310 /*
311 * child post attach/detach notification
312 */
313 static void
usb_mid_post_attach(usb_mid_t * usb_mid,uint8_t ifno,struct attachspec * as)314 usb_mid_post_attach(usb_mid_t *usb_mid, uint8_t ifno, struct attachspec *as)
315 {
316 USB_DPRINTF_L2(DPRINT_MASK_PM, usb_mid->mi_log_handle,
317 "usb_mid_post_attach: ifno = %d result = %d", ifno, as->result);
318
319 /* if child successfully attached, set power */
320 if (as->result == DDI_SUCCESS) {
321 /*
322 * Check if the child created wants to be power managed.
323 * If yes, the childs power level gets automatically tracked
324 * by DDI_CTLOPS_POWER busctl.
325 * If no, we set power of the new child by default
326 * to USB_DEV_OS_FULL_PWR. Because we should never suspend.
327 */
328 mutex_enter(&usb_mid->mi_mutex);
329 usb_mid->mi_attach_count++;
330 mutex_exit(&usb_mid->mi_mutex);
331 }
332 }
333
334
335 static void
usb_mid_post_detach(usb_mid_t * usb_mid,uint8_t ifno,struct detachspec * ds)336 usb_mid_post_detach(usb_mid_t *usb_mid, uint8_t ifno, struct detachspec *ds)
337 {
338 USB_DPRINTF_L2(DPRINT_MASK_PM, usb_mid->mi_log_handle,
339 "usb_mid_post_detach: ifno = %d result = %d", ifno, ds->result);
340
341 /*
342 * if the device is successfully detached,
343 * mark component as idle
344 */
345 if (ds->result == DDI_SUCCESS) {
346 usba_device_t *usba_device =
347 usba_get_usba_device(usb_mid->mi_dip);
348
349 mutex_enter(&usb_mid->mi_mutex);
350
351 /* check for leaks except when where is a ugen open */
352 if ((ds->cmd == DDI_DETACH) &&
353 (--usb_mid->mi_attach_count == 0) && usba_device &&
354 (usb_mid->mi_ugen_open_count == 0)) {
355 usba_check_for_leaks(usba_device);
356 }
357 mutex_exit(&usb_mid->mi_mutex);
358 }
359 }
360
361
362 /*
363 * bus ctl support. we handle notifications here and the
364 * rest goes up to root hub/hcd
365 */
366 /*ARGSUSED*/
367 static int
usb_mid_bus_ctl(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)368 usb_mid_bus_ctl(dev_info_t *dip,
369 dev_info_t *rdip,
370 ddi_ctl_enum_t op,
371 void *arg,
372 void *result)
373 {
374 usba_device_t *hub_usba_device = usba_get_usba_device(rdip);
375 dev_info_t *root_hub_dip = hub_usba_device->usb_root_hub_dip;
376 usb_mid_t *usb_mid;
377 struct attachspec *as;
378 struct detachspec *ds;
379
380 usb_mid = usb_mid_obtain_state(dip);
381
382 USB_DPRINTF_L2(DPRINT_MASK_PM, usb_mid->mi_log_handle,
383 "usb_mid_bus_ctl:\n\t"
384 "dip = 0x%p, rdip = 0x%p, op = 0x%x, arg = 0x%p",
385 (void *)dip, (void *)rdip, op, arg);
386
387 switch (op) {
388 case DDI_CTLOPS_ATTACH:
389 as = (struct attachspec *)arg;
390
391 switch (as->when) {
392 case DDI_PRE :
393 /* nothing to do basically */
394 USB_DPRINTF_L2(DPRINT_MASK_PM, usb_mid->mi_log_handle,
395 "DDI_PRE DDI_CTLOPS_ATTACH");
396 break;
397 case DDI_POST :
398 usb_mid_post_attach(usb_mid, usba_get_ifno(rdip),
399 (struct attachspec *)arg);
400 break;
401 }
402
403 break;
404 case DDI_CTLOPS_DETACH:
405 ds = (struct detachspec *)arg;
406
407 switch (ds->when) {
408 case DDI_PRE :
409 /* nothing to do basically */
410 USB_DPRINTF_L2(DPRINT_MASK_PM, usb_mid->mi_log_handle,
411 "DDI_PRE DDI_CTLOPS_DETACH");
412 break;
413 case DDI_POST :
414 usb_mid_post_detach(usb_mid, usba_get_ifno(rdip),
415 (struct detachspec *)arg);
416 break;
417 }
418
419 break;
420 default:
421 /* pass to root hub to handle */
422 return (usba_bus_ctl(root_hub_dip, rdip, op, arg, result));
423 }
424
425 return (DDI_SUCCESS);
426 }
427
428
429 /*
430 * bus enumeration entry points
431 */
432 static int
usb_mid_bus_config(dev_info_t * dip,uint_t flag,ddi_bus_config_op_t op,void * arg,dev_info_t ** child)433 usb_mid_bus_config(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
434 void *arg, dev_info_t **child)
435 {
436 int rval;
437 usb_mid_t *usb_mid = usb_mid_obtain_state(dip);
438
439 USB_DPRINTF_L2(DPRINT_MASK_ALL, usb_mid->mi_log_handle,
440 "usb_mid_bus_config: op=%d", op);
441
442 if (usb_mid_bus_config_debug) {
443 flag |= NDI_DEVI_DEBUG;
444 }
445
446 ndi_devi_enter(dip);
447
448 /* enumerate each interface below us */
449 mutex_enter(&usb_mid->mi_mutex);
450 usb_mid_create_children(usb_mid);
451 mutex_exit(&usb_mid->mi_mutex);
452
453 rval = ndi_busop_bus_config(dip, flag, op, arg, child, 0);
454 ndi_devi_exit(dip);
455
456 return (rval);
457 }
458
459
460 static int
usb_mid_bus_unconfig(dev_info_t * dip,uint_t flag,ddi_bus_config_op_t op,void * arg)461 usb_mid_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
462 void *arg)
463 {
464 usb_mid_t *usb_mid = usb_mid_obtain_state(dip);
465
466 dev_info_t *cdip, *mdip;
467 int interface;
468 int rval = NDI_SUCCESS;
469
470 USB_DPRINTF_L4(DPRINT_MASK_ALL, usb_mid->mi_log_handle,
471 "usb_mid_bus_unconfig: op=%d", op);
472
473 if (usb_mid_bus_config_debug) {
474 flag |= NDI_DEVI_DEBUG;
475 }
476
477 /*
478 * first offline and if offlining successful, then
479 * remove children
480 */
481 if (op == BUS_UNCONFIG_ALL) {
482 flag &= ~(NDI_DEVI_REMOVE | NDI_UNCONFIG);
483 }
484
485 ndi_devi_enter(dip);
486 rval = ndi_busop_bus_unconfig(dip, flag, op, arg);
487
488 if (op == BUS_UNCONFIG_ALL && rval == NDI_SUCCESS &&
489 (flag & NDI_AUTODETACH) == 0) {
490 flag |= NDI_DEVI_REMOVE;
491 rval = ndi_busop_bus_unconfig(dip, flag, op, arg);
492 }
493
494 /* update children's list */
495 mutex_enter(&usb_mid->mi_mutex);
496 for (interface = 0; usb_mid->mi_children_dips &&
497 (interface < usb_mid->mi_n_ifs) &&
498 (usb_mid->mi_children_ifs[interface]); interface++) {
499 mdip = usb_mid->mi_children_dips[interface];
500
501 /* now search if this dip still exists */
502 for (cdip = ddi_get_child(dip); cdip && (cdip != mdip); )
503 cdip = ddi_get_next_sibling(cdip);
504
505 if (cdip != mdip) {
506 /* we lost the dip on this interface */
507 usb_mid->mi_children_dips[interface] = NULL;
508 } else if (cdip) {
509 /*
510 * keep in DS_INITALIZED to prevent parent
511 * from detaching
512 */
513 (void) ddi_initchild(ddi_get_parent(cdip), cdip);
514 }
515 }
516 mutex_exit(&usb_mid->mi_mutex);
517
518 ndi_devi_exit(dip);
519
520 USB_DPRINTF_L4(DPRINT_MASK_ALL, usb_mid->mi_log_handle,
521 "usb_mid_bus_config: rval=%d", rval);
522
523 return (rval);
524 }
525
526
527 /* power entry point */
528 /* ARGSUSED */
529 static int
usb_mid_power(dev_info_t * dip,int comp,int level)530 usb_mid_power(dev_info_t *dip, int comp, int level)
531 {
532 usb_mid_t *usb_mid;
533 usb_common_power_t *midpm;
534 int rval = DDI_FAILURE;
535
536 usb_mid = usb_mid_obtain_state(dip);
537
538 USB_DPRINTF_L4(DPRINT_MASK_PM, usb_mid->mi_log_handle,
539 "usb_mid_power: Begin: usb_mid = %p, level = %d",
540 (void *)usb_mid, level);
541
542 mutex_enter(&usb_mid->mi_mutex);
543 midpm = usb_mid->mi_pm;
544
545 /* check if we are transitioning to a legal power level */
546 if (USB_DEV_PWRSTATE_OK(midpm->uc_pwr_states, level)) {
547 USB_DPRINTF_L2(DPRINT_MASK_PM, usb_mid->mi_log_handle,
548 "usb_mid_power: illegal power level = %d "
549 "uc_pwr_states = %x", level, midpm->uc_pwr_states);
550
551 mutex_exit(&usb_mid->mi_mutex);
552
553 return (rval);
554 }
555
556 rval = usba_common_power(dip, &(midpm->uc_current_power),
557 &(usb_mid->mi_dev_state), level);
558
559 mutex_exit(&usb_mid->mi_mutex);
560
561 return (rval);
562 }
563
564
565 /*
566 * attach/resume entry point
567 */
568 static int
usb_mid_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)569 usb_mid_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
570 {
571 int instance = ddi_get_instance(dip);
572 usb_mid_t *usb_mid = NULL;
573 uint_t n_ifs, i;
574 size_t size;
575
576 switch (cmd) {
577 case DDI_ATTACH:
578
579 break;
580 case DDI_RESUME:
581 usb_mid = (usb_mid_t *)ddi_get_soft_state(usb_mid_statep,
582 instance);
583 (void) usb_mid_restore_device_state(dip, usb_mid);
584
585 if (usb_mid->mi_ugen_hdl) {
586 (void) usb_ugen_attach(usb_mid->mi_ugen_hdl,
587 DDI_RESUME);
588 }
589
590 return (DDI_SUCCESS);
591 default:
592
593 return (DDI_FAILURE);
594 }
595
596 /*
597 * Attach:
598 *
599 * Allocate soft state and initialize
600 */
601 if (ddi_soft_state_zalloc(usb_mid_statep, instance) != DDI_SUCCESS) {
602 goto fail;
603 }
604
605 usb_mid = ddi_get_soft_state(usb_mid_statep, instance);
606 if (usb_mid == NULL) {
607
608 goto fail;
609 }
610
611 /* allocate handle for logging of messages */
612 usb_mid->mi_log_handle = usb_alloc_log_hdl(dip, "mid",
613 &usb_mid_errlevel,
614 &usb_mid_errmask, &usb_mid_instance_debug,
615 0);
616
617 usb_mid->mi_usba_device = usba_get_usba_device(dip);
618 usb_mid->mi_dip = dip;
619 usb_mid->mi_instance = instance;
620 usb_mid->mi_n_ifs = usb_mid->mi_usba_device->usb_n_ifs;
621
622 /* attach client driver to USBA */
623 if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) {
624 USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
625 "usb_client_attach failed");
626 goto fail;
627 }
628 if (usb_get_dev_data(dip, &usb_mid->mi_dev_data, USB_PARSE_LVL_NONE,
629 0) != USB_SUCCESS) {
630 USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
631 "usb_get_dev_data failed");
632 goto fail;
633 }
634
635 mutex_init(&usb_mid->mi_mutex, NULL, MUTEX_DRIVER,
636 usb_mid->mi_dev_data->dev_iblock_cookie);
637
638 usb_free_dev_data(dip, usb_mid->mi_dev_data);
639 usb_mid->mi_dev_data = NULL;
640
641 usb_mid->mi_init_state |= USB_MID_LOCK_INIT;
642
643 if (ddi_create_minor_node(dip, "usb_mid", S_IFCHR,
644 instance << USB_MID_MINOR_INSTANCE_SHIFT,
645 DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
646 USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
647 "cannot create devctl minor node");
648 goto fail;
649 }
650
651 usb_mid->mi_init_state |= USB_MID_MINOR_NODE_CREATED;
652
653 /*
654 * allocate array for keeping track of child dips
655 */
656 n_ifs = usb_mid->mi_n_ifs;
657 usb_mid->mi_cd_list_length = size = (sizeof (dev_info_t *)) * n_ifs;
658
659 usb_mid->mi_children_dips = kmem_zalloc(size, KM_SLEEP);
660 usb_mid->mi_child_events = kmem_zalloc(sizeof (uint8_t) * n_ifs,
661 KM_SLEEP);
662 usb_mid->mi_children_ifs = kmem_zalloc(sizeof (uint_t) * n_ifs,
663 KM_SLEEP);
664 for (i = 0; i < n_ifs; i++) {
665 usb_mid->mi_children_ifs[i] = 1;
666 }
667
668 /*
669 * Event handling: definition and registration
670 * get event handle for events that we have defined
671 */
672 (void) ndi_event_alloc_hdl(dip, 0, &usb_mid->mi_ndi_event_hdl,
673 NDI_SLEEP);
674
675 /* bind event set to the handle */
676 if (ndi_event_bind_set(usb_mid->mi_ndi_event_hdl, &usb_mid_ndi_events,
677 NDI_SLEEP)) {
678 USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
679 "usb_mid_attach: binding event set failed");
680
681 goto fail;
682 }
683
684 usb_mid->mi_dev_state = USB_DEV_ONLINE;
685
686 /*
687 * now create components to power manage this device
688 * before attaching children
689 */
690 usb_mid_create_pm_components(dip, usb_mid);
691
692 /* event registration for events from our parent */
693 usba_common_register_events(usb_mid->mi_dip, 1, usb_mid_event_cb);
694
695 usb_mid->mi_init_state |= USB_MID_EVENTS_REGISTERED;
696
697 ddi_report_dev(dip);
698
699 return (DDI_SUCCESS);
700
701 fail:
702 USB_DPRINTF_L2(DPRINT_MASK_ATTA, NULL, "usb_mid%d cannot attach",
703 instance);
704
705 if (usb_mid) {
706 (void) usb_mid_cleanup(dip, usb_mid);
707 }
708
709 return (DDI_FAILURE);
710 }
711
712
713 /* detach or suspend this instance */
714 static int
usb_mid_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)715 usb_mid_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
716 {
717 usb_mid_t *usb_mid = usb_mid_obtain_state(dip);
718
719 USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
720 "usb_mid_detach: cmd = 0x%x", cmd);
721
722 switch (cmd) {
723 case DDI_DETACH:
724
725 return (usb_mid_cleanup(dip, usb_mid));
726 case DDI_SUSPEND:
727 /* nothing to do */
728 mutex_enter(&usb_mid->mi_mutex);
729 usb_mid->mi_dev_state = USB_DEV_SUSPENDED;
730 mutex_exit(&usb_mid->mi_mutex);
731
732 if (usb_mid->mi_ugen_hdl) {
733 int rval = usb_ugen_detach(usb_mid->mi_ugen_hdl,
734 DDI_SUSPEND);
735 return (rval == USB_SUCCESS ? DDI_SUCCESS :
736 DDI_FAILURE);
737 }
738
739 return (DDI_SUCCESS);
740 default:
741
742 return (DDI_FAILURE);
743 }
744
745 _NOTE(NOT_REACHED)
746 /* NOTREACHED */
747 }
748
749 /*
750 * usb_mid_cleanup:
751 * cleanup usb_mid and deallocate. this function is called for
752 * handling attach failures and detaching including dynamic
753 * reconfiguration
754 */
755 /*ARGSUSED*/
756 static int
usb_mid_cleanup(dev_info_t * dip,usb_mid_t * usb_mid)757 usb_mid_cleanup(dev_info_t *dip, usb_mid_t *usb_mid)
758 {
759 usb_common_power_t *midpm;
760 int rval;
761
762 USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
763 "usb_mid_cleanup:");
764
765 if ((usb_mid->mi_init_state & USB_MID_LOCK_INIT) == 0) {
766
767 goto done;
768 }
769
770 /*
771 * deallocate events, if events are still registered
772 * (ie. children still attached) then we have to fail the detach
773 */
774 if (usb_mid->mi_ndi_event_hdl &&
775 (ndi_event_free_hdl(usb_mid->mi_ndi_event_hdl) != NDI_SUCCESS)) {
776
777 USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
778 "usb_mid_cleanup: ndi_event_free_hdl failed");
779
780 return (DDI_FAILURE);
781 }
782
783 /*
784 * Disable the event callbacks, after this point, event
785 * callbacks will never get called. Note we shouldn't hold
786 * mutex while unregistering events because there may be a
787 * competing event callback thread. Event callbacks are done
788 * with ndi mutex held and this can cause a potential deadlock.
789 * Note that cleanup can't fail after deregistration of events.
790 */
791 if (usb_mid->mi_init_state & USB_MID_EVENTS_REGISTERED) {
792 usba_common_unregister_events(usb_mid->mi_dip, 1);
793 }
794
795 midpm = usb_mid->mi_pm;
796
797 mutex_enter(&usb_mid->mi_mutex);
798
799 if ((midpm) && (usb_mid->mi_dev_state != USB_DEV_DISCONNECTED)) {
800
801 mutex_exit(&usb_mid->mi_mutex);
802
803 (void) pm_busy_component(dip, 0);
804 if (midpm->uc_wakeup_enabled) {
805
806 /* First bring the device to full power */
807 (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
808
809 rval = usb_handle_remote_wakeup(dip,
810 USB_REMOTE_WAKEUP_DISABLE);
811
812 if (rval != DDI_SUCCESS) {
813 USB_DPRINTF_L2(DPRINT_MASK_EVENTS,
814 usb_mid->mi_log_handle,
815 "usb_cleanup: disable remote "
816 "wakeup failed, rval=%d", rval);
817 }
818 }
819
820 (void) pm_lower_power(usb_mid->mi_dip, 0, USB_DEV_OS_PWR_OFF);
821 (void) pm_idle_component(dip, 0);
822 } else {
823 mutex_exit(&usb_mid->mi_mutex);
824 }
825
826 if (midpm) {
827 kmem_free(midpm, sizeof (usb_common_power_t));
828 }
829
830 /* free children list */
831 if (usb_mid->mi_children_dips) {
832 kmem_free(usb_mid->mi_children_dips,
833 usb_mid->mi_cd_list_length);
834 }
835
836 if (usb_mid->mi_child_events) {
837 kmem_free(usb_mid->mi_child_events, sizeof (uint8_t) *
838 usb_mid->mi_n_ifs);
839 }
840
841 if (usb_mid->mi_children_ifs) {
842 kmem_free(usb_mid->mi_children_ifs, sizeof (uint_t) *
843 usb_mid->mi_n_ifs);
844 }
845
846 if (usb_mid->mi_init_state & USB_MID_MINOR_NODE_CREATED) {
847 ddi_remove_minor_node(dip, NULL);
848 }
849
850 mutex_destroy(&usb_mid->mi_mutex);
851
852 done:
853 usb_client_detach(dip, usb_mid->mi_dev_data);
854
855 if (usb_mid->mi_ugen_hdl) {
856 (void) usb_ugen_detach(usb_mid->mi_ugen_hdl, DDI_DETACH);
857 usb_ugen_release_hdl(usb_mid->mi_ugen_hdl);
858 }
859
860 usb_free_log_hdl(usb_mid->mi_log_handle);
861 ddi_soft_state_free(usb_mid_statep, ddi_get_instance(dip));
862
863 ddi_prop_remove_all(dip);
864
865 return (DDI_SUCCESS);
866 }
867
868
869 static void
usb_mid_ugen_attach(usb_mid_t * usb_mid,boolean_t remove_children)870 usb_mid_ugen_attach(usb_mid_t *usb_mid, boolean_t remove_children)
871 {
872 _NOTE(NO_COMPETING_THREADS_NOW);
873
874 if (usb_mid->mi_ugen_hdl == NULL) {
875 usb_ugen_info_t usb_ugen_info;
876 int rval;
877 usb_ugen_hdl_t hdl;
878
879 USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
880 "usb_mid_ugen_attach: get handle");
881
882 bzero(&usb_ugen_info, sizeof (usb_ugen_info));
883
884 usb_ugen_info.usb_ugen_flags = (remove_children ?
885 USB_UGEN_REMOVE_CHILDREN : 0);
886 usb_ugen_info.usb_ugen_minor_node_ugen_bits_mask =
887 (dev_t)USB_MID_MINOR_UGEN_BITS_MASK;
888 usb_ugen_info.usb_ugen_minor_node_instance_mask =
889 (dev_t)~USB_MID_MINOR_UGEN_BITS_MASK;
890
891 mutex_exit(&usb_mid->mi_mutex);
892 hdl = usb_ugen_get_hdl(usb_mid->mi_dip,
893 &usb_ugen_info);
894
895 if ((rval = usb_ugen_attach(hdl, DDI_ATTACH)) != USB_SUCCESS) {
896 USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
897 "failed to create ugen support (%d)", rval);
898 usb_ugen_release_hdl(hdl);
899
900 mutex_enter(&usb_mid->mi_mutex);
901 } else {
902 mutex_enter(&usb_mid->mi_mutex);
903 usb_mid->mi_ugen_hdl = hdl;
904 }
905 }
906
907 #ifndef lint
908 _NOTE(COMPETING_THREADS_NOW);
909 #endif
910 }
911
912
913 /*
914 * usb_mid_create_children:
915 */
916 static void
usb_mid_create_children(usb_mid_t * usb_mid)917 usb_mid_create_children(usb_mid_t *usb_mid)
918 {
919 usba_device_t *usba_device;
920 uint_t n_ifs, if_count;
921 uint_t i, j;
922 dev_info_t *cdip, *ia_dip;
923 uint_t ugen_bound = 0;
924 uint_t bound_children = 0;
925
926 usba_device = usba_get_usba_device(usb_mid->mi_dip);
927
928 USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
929 "usb_mid_attach_child_drivers: port = %d, address = %d",
930 usba_device->usb_port, usba_device->usb_addr);
931
932 if (usb_mid->mi_removed_children) {
933
934 return;
935 }
936
937 n_ifs = usb_mid->mi_n_ifs;
938 if_count = 1;
939
940 USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
941 "usb_mid_create_children: #interfaces = %d", n_ifs);
942
943 /*
944 * create all children if not already present
945 */
946 for (i = 0; i < n_ifs; i += if_count) {
947
948 /* ignore since this if is included by an ia */
949 if (usb_mid->mi_children_ifs[i] == 0) {
950
951 continue;
952 }
953
954 if (usb_mid->mi_children_dips[i] != NULL) {
955 if (i_ddi_node_state(
956 usb_mid->mi_children_dips[i]) >=
957 DS_BOUND) {
958 bound_children++;
959 }
960
961 continue;
962 }
963
964 mutex_exit(&usb_mid->mi_mutex);
965 ia_dip = usba_ready_interface_association_node(usb_mid->mi_dip,
966 i, &if_count);
967
968 if (ia_dip != NULL) {
969 if (usba_bind_driver(ia_dip) == USB_SUCCESS) {
970 bound_children++;
971 if (strcmp(ddi_driver_name(ia_dip),
972 "ugen") == 0) {
973 ugen_bound++;
974 }
975 }
976
977 /*
978 * IA node owns if_count interfaces.
979 * The rest interfaces own none.
980 */
981 mutex_enter(&usb_mid->mi_mutex);
982 usb_mid->mi_children_dips[i] = ia_dip;
983 usb_mid->mi_children_ifs[i] = if_count;
984 for (j = i + 1; j < i + if_count; j++) {
985 usb_mid->mi_children_ifs[j] = 0;
986 }
987
988 continue;
989 }
990
991 cdip = usba_ready_interface_node(usb_mid->mi_dip, i);
992
993 if (cdip != NULL) {
994 if (usba_bind_driver(cdip) ==
995 USB_SUCCESS) {
996 bound_children++;
997 if (strcmp(ddi_driver_name(cdip),
998 "ugen") == 0) {
999 ugen_bound++;
1000 }
1001 }
1002
1003 /*
1004 * interface node owns 1 interface always.
1005 */
1006 mutex_enter(&usb_mid->mi_mutex);
1007 usb_mid->mi_children_dips[i] = cdip;
1008 usb_mid->mi_children_ifs[i] = 1;
1009 mutex_exit(&usb_mid->mi_mutex);
1010
1011 }
1012
1013 mutex_enter(&usb_mid->mi_mutex);
1014 }
1015
1016 usb_mid->mi_removed_children = (bound_children ? B_FALSE : B_TRUE);
1017
1018 /*
1019 * if there are no ugen interface children, create ugen support at
1020 * device level, use a separate thread because we may be at interrupt
1021 * level
1022 */
1023 if ((ugen_bound == 0) && (usb_mid->mi_ugen_hdl == NULL)) {
1024 /*
1025 * we only need to remove the children if there are
1026 * multiple configurations which would fail if there
1027 * are child interfaces
1028 */
1029 if ((usb_mid->mi_removed_children == B_FALSE) &&
1030 (usba_device->usb_n_cfgs > 1)) {
1031 USB_DPRINTF_L1(DPRINT_MASK_ATTA,
1032 usb_mid->mi_log_handle,
1033 "can't support ugen for multiple "
1034 "configurations devices that have attached "
1035 "child interface drivers");
1036 } else {
1037 usb_mid_ugen_attach(usb_mid,
1038 usb_mid->mi_removed_children);
1039 }
1040 }
1041 }
1042
1043
1044 /*
1045 * event support
1046 */
1047 static int
usb_mid_busop_get_eventcookie(dev_info_t * dip,dev_info_t * rdip,char * eventname,ddi_eventcookie_t * cookie)1048 usb_mid_busop_get_eventcookie(dev_info_t *dip,
1049 dev_info_t *rdip, char *eventname, ddi_eventcookie_t *cookie)
1050 {
1051 usb_mid_t *usb_mid = usb_mid_obtain_state(dip);
1052
1053 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1054 "usb_mid_busop_get_eventcookie: dip=0x%p, rdip=0x%p, "
1055 "event=%s", (void *)dip, (void *)rdip, eventname);
1056 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1057 "(dip=%s%d rdip=%s%d)",
1058 ddi_driver_name(dip), ddi_get_instance(dip),
1059 ddi_driver_name(rdip), ddi_get_instance(rdip));
1060
1061 /* return event cookie, iblock cookie, and level */
1062 return (ndi_event_retrieve_cookie(usb_mid->mi_ndi_event_hdl,
1063 rdip, eventname, cookie, NDI_EVENT_NOPASS));
1064 }
1065
1066
1067 static int
usb_mid_busop_add_eventcall(dev_info_t * dip,dev_info_t * rdip,ddi_eventcookie_t cookie,ddi_event_cb_f callback,void * arg,ddi_callback_id_t * cb_id)1068 usb_mid_busop_add_eventcall(dev_info_t *dip,
1069 dev_info_t *rdip,
1070 ddi_eventcookie_t cookie,
1071 ddi_event_cb_f callback,
1072 void *arg, ddi_callback_id_t *cb_id)
1073 {
1074 usb_mid_t *usb_mid = usb_mid_obtain_state(dip);
1075 int ifno = usba_get_ifno(rdip);
1076
1077 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1078 "usb_mid_busop_add_eventcall: dip=0x%p, rdip=0x%p "
1079 "cookie=0x%p, cb=0x%p, arg=0x%p",
1080 (void *)dip, (void *)rdip, (void *)cookie, (void *)callback, arg);
1081 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1082 "(dip=%s%d rdip=%s%d event=%s)",
1083 ddi_driver_name(dip), ddi_get_instance(dip),
1084 ddi_driver_name(rdip), ddi_get_instance(rdip),
1085 ndi_event_cookie_to_name(usb_mid->mi_ndi_event_hdl, cookie));
1086
1087 /* Set flag on children registering events */
1088 switch (ndi_event_cookie_to_tag(usb_mid->mi_ndi_event_hdl, cookie)) {
1089 case USBA_EVENT_TAG_HOT_REMOVAL:
1090 mutex_enter(&usb_mid->mi_mutex);
1091 usb_mid->mi_child_events[ifno] |=
1092 USB_MID_CHILD_EVENT_DISCONNECT;
1093 mutex_exit(&usb_mid->mi_mutex);
1094
1095 break;
1096 case USBA_EVENT_TAG_PRE_SUSPEND:
1097 mutex_enter(&usb_mid->mi_mutex);
1098 usb_mid->mi_child_events[ifno] |=
1099 USB_MID_CHILD_EVENT_PRESUSPEND;
1100 mutex_exit(&usb_mid->mi_mutex);
1101
1102 break;
1103 default:
1104
1105 break;
1106 }
1107 /* add callback (perform registration) */
1108 return (ndi_event_add_callback(usb_mid->mi_ndi_event_hdl,
1109 rdip, cookie, callback, arg, NDI_SLEEP, cb_id));
1110 }
1111
1112
1113 static int
usb_mid_busop_remove_eventcall(dev_info_t * dip,ddi_callback_id_t cb_id)1114 usb_mid_busop_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id)
1115 {
1116 usb_mid_t *usb_mid = usb_mid_obtain_state(dip);
1117 ndi_event_callbacks_t *cb = (ndi_event_callbacks_t *)cb_id;
1118
1119 ASSERT(cb);
1120
1121 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1122 "usb_mid_busop_remove_eventcall: dip=0x%p, rdip=0x%p "
1123 "cookie=0x%p", (void *)dip, (void *)cb->ndi_evtcb_dip,
1124 (void *)cb->ndi_evtcb_cookie);
1125 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1126 "(dip=%s%d rdip=%s%d event=%s)",
1127 ddi_driver_name(dip), ddi_get_instance(dip),
1128 ddi_driver_name(cb->ndi_evtcb_dip),
1129 ddi_get_instance(cb->ndi_evtcb_dip),
1130 ndi_event_cookie_to_name(usb_mid->mi_ndi_event_hdl,
1131 cb->ndi_evtcb_cookie));
1132
1133 /* remove event registration from our event set */
1134 return (ndi_event_remove_callback(usb_mid->mi_ndi_event_hdl, cb_id));
1135 }
1136
1137
1138 static int
usb_mid_busop_post_event(dev_info_t * dip,dev_info_t * rdip,ddi_eventcookie_t cookie,void * bus_impldata)1139 usb_mid_busop_post_event(dev_info_t *dip,
1140 dev_info_t *rdip,
1141 ddi_eventcookie_t cookie,
1142 void *bus_impldata)
1143 {
1144 usb_mid_t *usb_mid = usb_mid_obtain_state(dip);
1145
1146 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1147 "usb_mid_busop_post_event: dip=0x%p, rdip=0x%p "
1148 "cookie=0x%p, impl=0x%p",
1149 (void *)dip, (void *)rdip, (void *)cookie, bus_impldata);
1150 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1151 "(dip=%s%d rdip=%s%d event=%s)",
1152 ddi_driver_name(dip), ddi_get_instance(dip),
1153 ddi_driver_name(rdip), ddi_get_instance(rdip),
1154 ndi_event_cookie_to_name(usb_mid->mi_ndi_event_hdl, cookie));
1155
1156 /* post event to all children registered for this event */
1157 return (ndi_event_run_callbacks(usb_mid->mi_ndi_event_hdl, rdip,
1158 cookie, bus_impldata));
1159 }
1160
1161
1162 /*
1163 * usb_mid_restore_device_state
1164 * set the original configuration of the device
1165 */
1166 static int
usb_mid_restore_device_state(dev_info_t * dip,usb_mid_t * usb_mid)1167 usb_mid_restore_device_state(dev_info_t *dip, usb_mid_t *usb_mid)
1168 {
1169 usb_common_power_t *midpm;
1170
1171 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1172 "usb_mid_restore_device_state: usb_mid = %p", (void *)usb_mid);
1173
1174 mutex_enter(&usb_mid->mi_mutex);
1175 midpm = usb_mid->mi_pm;
1176 mutex_exit(&usb_mid->mi_mutex);
1177
1178 /* First bring the device to full power */
1179 (void) pm_busy_component(dip, 0);
1180 (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
1181
1182 if (usb_check_same_device(dip, usb_mid->mi_log_handle, USB_LOG_L0,
1183 DPRINT_MASK_EVENTS, USB_CHK_VIDPID, NULL) != USB_SUCCESS) {
1184
1185 /* change the device state from suspended to disconnected */
1186 mutex_enter(&usb_mid->mi_mutex);
1187 usb_mid->mi_dev_state = USB_DEV_DISCONNECTED;
1188 mutex_exit(&usb_mid->mi_mutex);
1189 (void) pm_idle_component(dip, 0);
1190
1191 return (USB_FAILURE);
1192 }
1193
1194 /*
1195 * if the device had remote wakeup earlier,
1196 * enable it again
1197 */
1198 if (midpm->uc_wakeup_enabled) {
1199 (void) usb_handle_remote_wakeup(usb_mid->mi_dip,
1200 USB_REMOTE_WAKEUP_ENABLE);
1201 }
1202
1203 mutex_enter(&usb_mid->mi_mutex);
1204 usb_mid->mi_dev_state = USB_DEV_ONLINE;
1205 mutex_exit(&usb_mid->mi_mutex);
1206
1207 (void) pm_idle_component(dip, 0);
1208
1209 return (USB_SUCCESS);
1210 }
1211
1212
1213 /*
1214 * usb_mid_event_cb()
1215 * handle disconnect and connect events
1216 */
1217 static void
usb_mid_event_cb(dev_info_t * dip,ddi_eventcookie_t cookie,void * arg,void * bus_impldata)1218 usb_mid_event_cb(dev_info_t *dip, ddi_eventcookie_t cookie,
1219 void *arg, void *bus_impldata)
1220 {
1221 int i, tag;
1222 usb_mid_t *usb_mid = usb_mid_obtain_state(dip);
1223 dev_info_t *child_dip;
1224 ddi_eventcookie_t rm_cookie, ins_cookie, suspend_cookie, resume_cookie;
1225
1226 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1227 "usb_mid_event_cb: dip=0x%p, cookie=0x%p, "
1228 "arg=0x%p, impl=0x%p",
1229 (void *)dip, (void *)cookie, arg, bus_impldata);
1230 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1231 "(dip=%s%d event=%s)",
1232 ddi_driver_name(dip), ddi_get_instance(dip),
1233 ndi_event_cookie_to_name(usb_mid->mi_ndi_event_hdl, cookie));
1234
1235 tag = NDI_EVENT_TAG(cookie);
1236 rm_cookie = ndi_event_tag_to_cookie(
1237 usb_mid->mi_ndi_event_hdl, USBA_EVENT_TAG_HOT_REMOVAL);
1238 suspend_cookie = ndi_event_tag_to_cookie(
1239 usb_mid->mi_ndi_event_hdl, USBA_EVENT_TAG_PRE_SUSPEND);
1240 ins_cookie = ndi_event_tag_to_cookie(
1241 usb_mid->mi_ndi_event_hdl, USBA_EVENT_TAG_HOT_INSERTION);
1242 resume_cookie = ndi_event_tag_to_cookie(
1243 usb_mid->mi_ndi_event_hdl, USBA_EVENT_TAG_POST_RESUME);
1244
1245 mutex_enter(&usb_mid->mi_mutex);
1246 switch (tag) {
1247 case USBA_EVENT_TAG_HOT_REMOVAL:
1248 if (usb_mid->mi_dev_state == USB_DEV_DISCONNECTED) {
1249 USB_DPRINTF_L2(DPRINT_MASK_EVENTS,
1250 usb_mid->mi_log_handle,
1251 "usb_mid_event_cb: Device already disconnected");
1252 } else {
1253 /* we are disconnected so set our state now */
1254 usb_mid->mi_dev_state = USB_DEV_DISCONNECTED;
1255 for (i = 0; i < usb_mid->mi_n_ifs; i++) {
1256 usb_mid->mi_child_events[i] &= ~
1257 USB_MID_CHILD_EVENT_DISCONNECT;
1258 }
1259 mutex_exit(&usb_mid->mi_mutex);
1260
1261 /* pass disconnect event to all the children */
1262 (void) ndi_event_run_callbacks(
1263 usb_mid->mi_ndi_event_hdl, NULL,
1264 rm_cookie, bus_impldata);
1265
1266 if (usb_mid->mi_ugen_hdl) {
1267 (void) usb_ugen_disconnect_ev_cb(
1268 usb_mid->mi_ugen_hdl);
1269 }
1270 mutex_enter(&usb_mid->mi_mutex);
1271 }
1272 break;
1273 case USBA_EVENT_TAG_PRE_SUSPEND:
1274 /* set our state *after* suspending children */
1275 mutex_exit(&usb_mid->mi_mutex);
1276
1277 /* pass pre_suspend event to all the children */
1278 (void) ndi_event_run_callbacks(usb_mid->mi_ndi_event_hdl,
1279 NULL, suspend_cookie, bus_impldata);
1280
1281 mutex_enter(&usb_mid->mi_mutex);
1282 for (i = 0; i < usb_mid->mi_n_ifs; i++) {
1283 usb_mid->mi_child_events[i] &= ~
1284 USB_MID_CHILD_EVENT_PRESUSPEND;
1285 }
1286 break;
1287 case USBA_EVENT_TAG_HOT_INSERTION:
1288 mutex_exit(&usb_mid->mi_mutex);
1289 if (usb_mid_restore_device_state(dip, usb_mid) == USB_SUCCESS) {
1290
1291 /*
1292 * Check to see if this child has missed the disconnect
1293 * event before it registered for event cb
1294 */
1295 mutex_enter(&usb_mid->mi_mutex);
1296 for (i = 0; i < usb_mid->mi_n_ifs; i++) {
1297 if ((usb_mid->mi_child_events[i] &
1298 USB_MID_CHILD_EVENT_DISCONNECT) &&
1299 usb_mid->mi_children_ifs[i]) {
1300 usb_mid->mi_child_events[i] &=
1301 ~USB_MID_CHILD_EVENT_DISCONNECT;
1302 child_dip =
1303 usb_mid->mi_children_dips[i];
1304 mutex_exit(&usb_mid->mi_mutex);
1305
1306 /* post the missed disconnect */
1307 (void) ndi_event_do_callback(
1308 usb_mid->mi_ndi_event_hdl,
1309 child_dip,
1310 rm_cookie,
1311 bus_impldata);
1312 mutex_enter(&usb_mid->mi_mutex);
1313 }
1314 }
1315 mutex_exit(&usb_mid->mi_mutex);
1316
1317 /* pass reconnect event to all the children */
1318 (void) ndi_event_run_callbacks(
1319 usb_mid->mi_ndi_event_hdl, NULL,
1320 ins_cookie, bus_impldata);
1321
1322 if (usb_mid->mi_ugen_hdl) {
1323 (void) usb_ugen_reconnect_ev_cb(
1324 usb_mid->mi_ugen_hdl);
1325 }
1326 }
1327 mutex_enter(&usb_mid->mi_mutex);
1328 break;
1329 case USBA_EVENT_TAG_POST_RESUME:
1330 /*
1331 * Check to see if this child has missed the pre-suspend
1332 * event before it registered for event cb
1333 */
1334 for (i = 0; i < usb_mid->mi_n_ifs; i++) {
1335 if ((usb_mid->mi_child_events[i] &
1336 USB_MID_CHILD_EVENT_PRESUSPEND) &&
1337 usb_mid->mi_children_ifs[i]) {
1338 usb_mid->mi_child_events[i] &=
1339 ~USB_MID_CHILD_EVENT_PRESUSPEND;
1340 child_dip = usb_mid->mi_children_dips[i];
1341 mutex_exit(&usb_mid->mi_mutex);
1342
1343 /* post the missed pre-suspend event */
1344 (void) ndi_event_do_callback(
1345 usb_mid->mi_ndi_event_hdl,
1346 child_dip, suspend_cookie,
1347 bus_impldata);
1348 mutex_enter(&usb_mid->mi_mutex);
1349 }
1350 }
1351 mutex_exit(&usb_mid->mi_mutex);
1352
1353 /* pass post_resume event to all the children */
1354 (void) ndi_event_run_callbacks(usb_mid->mi_ndi_event_hdl,
1355 NULL, resume_cookie, bus_impldata);
1356
1357 mutex_enter(&usb_mid->mi_mutex);
1358 break;
1359 }
1360 mutex_exit(&usb_mid->mi_mutex);
1361
1362 }
1363
1364
1365 /*
1366 * create the pm components required for power management
1367 */
1368 static void
usb_mid_create_pm_components(dev_info_t * dip,usb_mid_t * usb_mid)1369 usb_mid_create_pm_components(dev_info_t *dip, usb_mid_t *usb_mid)
1370 {
1371 usb_common_power_t *midpm;
1372 uint_t pwr_states;
1373
1374 USB_DPRINTF_L4(DPRINT_MASK_PM, usb_mid->mi_log_handle,
1375 "usb_mid_create_pm_components: Begin");
1376
1377 /* Allocate the PM state structure */
1378 midpm = kmem_zalloc(sizeof (usb_common_power_t), KM_SLEEP);
1379
1380 mutex_enter(&usb_mid->mi_mutex);
1381 usb_mid->mi_pm = midpm;
1382 midpm->uc_usb_statep = usb_mid;
1383 midpm->uc_pm_capabilities = 0; /* XXXX should this be 0?? */
1384 midpm->uc_current_power = USB_DEV_OS_FULL_PWR;
1385 mutex_exit(&usb_mid->mi_mutex);
1386
1387 /*
1388 * By not enabling parental notification, PM enforces
1389 * "strict parental dependency" meaning, usb_mid won't
1390 * power off until any of its children are in full power.
1391 */
1392
1393 /*
1394 * there are 3 scenarios:
1395 * 1. a well behaved device should have remote wakeup
1396 * at interface and device level. If the interface
1397 * wakes up, usb_mid will wake up
1398 * 2. if the device doesn't have remote wake up and
1399 * the interface has, PM will still work, ie.
1400 * the interfaces wakes up and usb_mid wakes up
1401 * 3. if neither the interface nor device has remote
1402 * wakeup, the interface will wake up when it is opened
1403 * and goes to sleep after being closed for a while
1404 * In this case usb_mid should also go to sleep shortly
1405 * thereafter
1406 * In all scenarios it doesn't really matter whether
1407 * remote wakeup at the device level is enabled or not
1408 * but we do it anyways
1409 */
1410 if (usb_handle_remote_wakeup(dip, USB_REMOTE_WAKEUP_ENABLE) ==
1411 USB_SUCCESS) {
1412 USB_DPRINTF_L3(DPRINT_MASK_PM, usb_mid->mi_log_handle,
1413 "usb_mid_create_pm_components: "
1414 "Remote Wakeup Enabled");
1415 midpm->uc_wakeup_enabled = 1;
1416 }
1417
1418 if (usb_create_pm_components(dip, &pwr_states) ==
1419 USB_SUCCESS) {
1420 midpm->uc_pwr_states = (uint8_t)pwr_states;
1421 (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
1422 }
1423
1424 USB_DPRINTF_L4(DPRINT_MASK_PM, usb_mid->mi_log_handle,
1425 "usb_mid_create_pm_components: End");
1426 }
1427
1428
1429 /*
1430 * usb_mid_obtain_state:
1431 */
1432 usb_mid_t *
usb_mid_obtain_state(dev_info_t * dip)1433 usb_mid_obtain_state(dev_info_t *dip)
1434 {
1435 int instance = ddi_get_instance(dip);
1436 usb_mid_t *statep = ddi_get_soft_state(usb_mid_statep, instance);
1437
1438 ASSERT(statep != NULL);
1439
1440 return (statep);
1441 }
1442
1443
1444 /*
1445 * ugen support
1446 */
1447 /* ARGSUSED3 */
1448 static int
usb_mid_open(dev_t * devp,int flags,int otyp,cred_t * credp)1449 usb_mid_open(dev_t *devp, int flags, int otyp, cred_t *credp)
1450 {
1451 struct usb_mid *usb_mid;
1452 int rval;
1453
1454 if ((usb_mid = ddi_get_soft_state(usb_mid_statep,
1455 USB_MID_MINOR_TO_INSTANCE(getminor(*devp)))) == NULL) {
1456
1457 return (ENXIO);
1458 }
1459
1460 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, usb_mid->mi_log_handle,
1461 "usb_mid_open: usb_mid = 0x%p *devp = 0x%lx",
1462 (void *)usb_mid, *devp);
1463
1464 /* First bring the device to full power */
1465 (void) pm_busy_component(usb_mid->mi_dip, 0);
1466 (void) pm_raise_power(usb_mid->mi_dip, 0, USB_DEV_OS_FULL_PWR);
1467
1468
1469 rval = usb_ugen_open(usb_mid->mi_ugen_hdl, devp, flags, otyp,
1470 credp);
1471 if (rval) {
1472 (void) pm_idle_component(usb_mid->mi_dip, 0);
1473 } else {
1474 /*
1475 * since all ugen opens are exclusive we can count the
1476 * opens
1477 */
1478 mutex_enter(&usb_mid->mi_mutex);
1479 usb_mid->mi_ugen_open_count++;
1480 mutex_exit(&usb_mid->mi_mutex);
1481 }
1482
1483 return (rval);
1484 }
1485
1486
1487 /* ARGSUSED */
1488 static int
usb_mid_close(dev_t dev,int flag,int otyp,cred_t * credp)1489 usb_mid_close(dev_t dev, int flag, int otyp, cred_t *credp)
1490 {
1491 struct usb_mid *usb_mid;
1492 int rval;
1493
1494 if ((usb_mid = ddi_get_soft_state(usb_mid_statep,
1495 USB_MID_MINOR_TO_INSTANCE(getminor(dev)))) == NULL) {
1496
1497 return (ENXIO);
1498 }
1499
1500 rval = usb_ugen_close(usb_mid->mi_ugen_hdl, dev, flag, otyp,
1501 credp);
1502 if (rval == 0) {
1503 (void) pm_idle_component(usb_mid->mi_dip, 0);
1504 mutex_enter(&usb_mid->mi_mutex);
1505 usb_mid->mi_ugen_open_count--;
1506 mutex_exit(&usb_mid->mi_mutex);
1507 }
1508
1509 return (rval);
1510 }
1511
1512
1513 static int
usb_mid_read(dev_t dev,struct uio * uio,cred_t * credp)1514 usb_mid_read(dev_t dev, struct uio *uio, cred_t *credp)
1515 {
1516 struct usb_mid *usb_mid;
1517
1518 if ((usb_mid = ddi_get_soft_state(usb_mid_statep,
1519 USB_MID_MINOR_TO_INSTANCE(getminor(dev)))) == NULL) {
1520
1521 return (ENXIO);
1522 }
1523
1524 return (usb_ugen_read(usb_mid->mi_ugen_hdl, dev, uio, credp));
1525 }
1526
1527
1528 static int
usb_mid_write(dev_t dev,struct uio * uio,cred_t * credp)1529 usb_mid_write(dev_t dev, struct uio *uio, cred_t *credp)
1530 {
1531 struct usb_mid *usb_mid;
1532
1533 if ((usb_mid = ddi_get_soft_state(usb_mid_statep,
1534 USB_MID_MINOR_TO_INSTANCE(getminor(dev)))) == NULL) {
1535
1536 return (ENXIO);
1537 }
1538
1539 return (usb_ugen_write(usb_mid->mi_ugen_hdl, dev, uio, credp));
1540 }
1541
1542
1543 static int
usb_mid_poll(dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)1544 usb_mid_poll(dev_t dev, short events, int anyyet, short *reventsp,
1545 struct pollhead **phpp)
1546 {
1547 struct usb_mid *usb_mid;
1548
1549 if ((usb_mid = ddi_get_soft_state(usb_mid_statep,
1550 USB_MID_MINOR_TO_INSTANCE(getminor(dev)))) == NULL) {
1551
1552 return (ENXIO);
1553 }
1554
1555 return (usb_ugen_poll(usb_mid->mi_ugen_hdl, dev, events,
1556 anyyet, reventsp, phpp));
1557 }
1558