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 */
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, circ;
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, &circ);
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, circ);
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, circular_count;
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, &circular_count);
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, circular_count);
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,void (* callback)(dev_info_t * dip,ddi_eventcookie_t cookie,void * arg,void * bus_impldata),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 void (*callback)(dev_info_t *dip,
1072 ddi_eventcookie_t cookie, void *arg,
1073 void *bus_impldata),
1074 void *arg, ddi_callback_id_t *cb_id)
1075 {
1076 usb_mid_t *usb_mid = usb_mid_obtain_state(dip);
1077 int ifno = usba_get_ifno(rdip);
1078
1079 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1080 "usb_mid_busop_add_eventcall: dip=0x%p, rdip=0x%p "
1081 "cookie=0x%p, cb=0x%p, arg=0x%p",
1082 (void *)dip, (void *)rdip, (void *)cookie, (void *)callback, arg);
1083 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1084 "(dip=%s%d rdip=%s%d event=%s)",
1085 ddi_driver_name(dip), ddi_get_instance(dip),
1086 ddi_driver_name(rdip), ddi_get_instance(rdip),
1087 ndi_event_cookie_to_name(usb_mid->mi_ndi_event_hdl, cookie));
1088
1089 /* Set flag on children registering events */
1090 switch (ndi_event_cookie_to_tag(usb_mid->mi_ndi_event_hdl, cookie)) {
1091 case USBA_EVENT_TAG_HOT_REMOVAL:
1092 mutex_enter(&usb_mid->mi_mutex);
1093 usb_mid->mi_child_events[ifno] |=
1094 USB_MID_CHILD_EVENT_DISCONNECT;
1095 mutex_exit(&usb_mid->mi_mutex);
1096
1097 break;
1098 case USBA_EVENT_TAG_PRE_SUSPEND:
1099 mutex_enter(&usb_mid->mi_mutex);
1100 usb_mid->mi_child_events[ifno] |=
1101 USB_MID_CHILD_EVENT_PRESUSPEND;
1102 mutex_exit(&usb_mid->mi_mutex);
1103
1104 break;
1105 default:
1106
1107 break;
1108 }
1109 /* add callback (perform registration) */
1110 return (ndi_event_add_callback(usb_mid->mi_ndi_event_hdl,
1111 rdip, cookie, callback, arg, NDI_SLEEP, cb_id));
1112 }
1113
1114
1115 static int
usb_mid_busop_remove_eventcall(dev_info_t * dip,ddi_callback_id_t cb_id)1116 usb_mid_busop_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id)
1117 {
1118 usb_mid_t *usb_mid = usb_mid_obtain_state(dip);
1119 ndi_event_callbacks_t *cb = (ndi_event_callbacks_t *)cb_id;
1120
1121 ASSERT(cb);
1122
1123 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1124 "usb_mid_busop_remove_eventcall: dip=0x%p, rdip=0x%p "
1125 "cookie=0x%p", (void *)dip, (void *)cb->ndi_evtcb_dip,
1126 (void *)cb->ndi_evtcb_cookie);
1127 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1128 "(dip=%s%d rdip=%s%d event=%s)",
1129 ddi_driver_name(dip), ddi_get_instance(dip),
1130 ddi_driver_name(cb->ndi_evtcb_dip),
1131 ddi_get_instance(cb->ndi_evtcb_dip),
1132 ndi_event_cookie_to_name(usb_mid->mi_ndi_event_hdl,
1133 cb->ndi_evtcb_cookie));
1134
1135 /* remove event registration from our event set */
1136 return (ndi_event_remove_callback(usb_mid->mi_ndi_event_hdl, cb_id));
1137 }
1138
1139
1140 static int
usb_mid_busop_post_event(dev_info_t * dip,dev_info_t * rdip,ddi_eventcookie_t cookie,void * bus_impldata)1141 usb_mid_busop_post_event(dev_info_t *dip,
1142 dev_info_t *rdip,
1143 ddi_eventcookie_t cookie,
1144 void *bus_impldata)
1145 {
1146 usb_mid_t *usb_mid = usb_mid_obtain_state(dip);
1147
1148 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1149 "usb_mid_busop_post_event: dip=0x%p, rdip=0x%p "
1150 "cookie=0x%p, impl=0x%p",
1151 (void *)dip, (void *)rdip, (void *)cookie, bus_impldata);
1152 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1153 "(dip=%s%d rdip=%s%d event=%s)",
1154 ddi_driver_name(dip), ddi_get_instance(dip),
1155 ddi_driver_name(rdip), ddi_get_instance(rdip),
1156 ndi_event_cookie_to_name(usb_mid->mi_ndi_event_hdl, cookie));
1157
1158 /* post event to all children registered for this event */
1159 return (ndi_event_run_callbacks(usb_mid->mi_ndi_event_hdl, rdip,
1160 cookie, bus_impldata));
1161 }
1162
1163
1164 /*
1165 * usb_mid_restore_device_state
1166 * set the original configuration of the device
1167 */
1168 static int
usb_mid_restore_device_state(dev_info_t * dip,usb_mid_t * usb_mid)1169 usb_mid_restore_device_state(dev_info_t *dip, usb_mid_t *usb_mid)
1170 {
1171 usb_common_power_t *midpm;
1172
1173 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1174 "usb_mid_restore_device_state: usb_mid = %p", (void *)usb_mid);
1175
1176 mutex_enter(&usb_mid->mi_mutex);
1177 midpm = usb_mid->mi_pm;
1178 mutex_exit(&usb_mid->mi_mutex);
1179
1180 /* First bring the device to full power */
1181 (void) pm_busy_component(dip, 0);
1182 (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
1183
1184 if (usb_check_same_device(dip, usb_mid->mi_log_handle, USB_LOG_L0,
1185 DPRINT_MASK_EVENTS, USB_CHK_VIDPID, NULL) != USB_SUCCESS) {
1186
1187 /* change the device state from suspended to disconnected */
1188 mutex_enter(&usb_mid->mi_mutex);
1189 usb_mid->mi_dev_state = USB_DEV_DISCONNECTED;
1190 mutex_exit(&usb_mid->mi_mutex);
1191 (void) pm_idle_component(dip, 0);
1192
1193 return (USB_FAILURE);
1194 }
1195
1196 /*
1197 * if the device had remote wakeup earlier,
1198 * enable it again
1199 */
1200 if (midpm->uc_wakeup_enabled) {
1201 (void) usb_handle_remote_wakeup(usb_mid->mi_dip,
1202 USB_REMOTE_WAKEUP_ENABLE);
1203 }
1204
1205 mutex_enter(&usb_mid->mi_mutex);
1206 usb_mid->mi_dev_state = USB_DEV_ONLINE;
1207 mutex_exit(&usb_mid->mi_mutex);
1208
1209 (void) pm_idle_component(dip, 0);
1210
1211 return (USB_SUCCESS);
1212 }
1213
1214
1215 /*
1216 * usb_mid_event_cb()
1217 * handle disconnect and connect events
1218 */
1219 static void
usb_mid_event_cb(dev_info_t * dip,ddi_eventcookie_t cookie,void * arg,void * bus_impldata)1220 usb_mid_event_cb(dev_info_t *dip, ddi_eventcookie_t cookie,
1221 void *arg, void *bus_impldata)
1222 {
1223 int i, tag;
1224 usb_mid_t *usb_mid = usb_mid_obtain_state(dip);
1225 dev_info_t *child_dip;
1226 ddi_eventcookie_t rm_cookie, ins_cookie, suspend_cookie, resume_cookie;
1227
1228 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1229 "usb_mid_event_cb: dip=0x%p, cookie=0x%p, "
1230 "arg=0x%p, impl=0x%p",
1231 (void *)dip, (void *)cookie, arg, bus_impldata);
1232 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
1233 "(dip=%s%d event=%s)",
1234 ddi_driver_name(dip), ddi_get_instance(dip),
1235 ndi_event_cookie_to_name(usb_mid->mi_ndi_event_hdl, cookie));
1236
1237 tag = NDI_EVENT_TAG(cookie);
1238 rm_cookie = ndi_event_tag_to_cookie(
1239 usb_mid->mi_ndi_event_hdl, USBA_EVENT_TAG_HOT_REMOVAL);
1240 suspend_cookie = ndi_event_tag_to_cookie(
1241 usb_mid->mi_ndi_event_hdl, USBA_EVENT_TAG_PRE_SUSPEND);
1242 ins_cookie = ndi_event_tag_to_cookie(
1243 usb_mid->mi_ndi_event_hdl, USBA_EVENT_TAG_HOT_INSERTION);
1244 resume_cookie = ndi_event_tag_to_cookie(
1245 usb_mid->mi_ndi_event_hdl, USBA_EVENT_TAG_POST_RESUME);
1246
1247 mutex_enter(&usb_mid->mi_mutex);
1248 switch (tag) {
1249 case USBA_EVENT_TAG_HOT_REMOVAL:
1250 if (usb_mid->mi_dev_state == USB_DEV_DISCONNECTED) {
1251 USB_DPRINTF_L2(DPRINT_MASK_EVENTS,
1252 usb_mid->mi_log_handle,
1253 "usb_mid_event_cb: Device already disconnected");
1254 } else {
1255 /* we are disconnected so set our state now */
1256 usb_mid->mi_dev_state = USB_DEV_DISCONNECTED;
1257 for (i = 0; i < usb_mid->mi_n_ifs; i++) {
1258 usb_mid->mi_child_events[i] &= ~
1259 USB_MID_CHILD_EVENT_DISCONNECT;
1260 }
1261 mutex_exit(&usb_mid->mi_mutex);
1262
1263 /* pass disconnect event to all the children */
1264 (void) ndi_event_run_callbacks(
1265 usb_mid->mi_ndi_event_hdl, NULL,
1266 rm_cookie, bus_impldata);
1267
1268 if (usb_mid->mi_ugen_hdl) {
1269 (void) usb_ugen_disconnect_ev_cb(
1270 usb_mid->mi_ugen_hdl);
1271 }
1272 mutex_enter(&usb_mid->mi_mutex);
1273 }
1274 break;
1275 case USBA_EVENT_TAG_PRE_SUSPEND:
1276 /* set our state *after* suspending children */
1277 mutex_exit(&usb_mid->mi_mutex);
1278
1279 /* pass pre_suspend event to all the children */
1280 (void) ndi_event_run_callbacks(usb_mid->mi_ndi_event_hdl,
1281 NULL, suspend_cookie, bus_impldata);
1282
1283 mutex_enter(&usb_mid->mi_mutex);
1284 for (i = 0; i < usb_mid->mi_n_ifs; i++) {
1285 usb_mid->mi_child_events[i] &= ~
1286 USB_MID_CHILD_EVENT_PRESUSPEND;
1287 }
1288 break;
1289 case USBA_EVENT_TAG_HOT_INSERTION:
1290 mutex_exit(&usb_mid->mi_mutex);
1291 if (usb_mid_restore_device_state(dip, usb_mid) == USB_SUCCESS) {
1292
1293 /*
1294 * Check to see if this child has missed the disconnect
1295 * event before it registered for event cb
1296 */
1297 mutex_enter(&usb_mid->mi_mutex);
1298 for (i = 0; i < usb_mid->mi_n_ifs; i++) {
1299 if ((usb_mid->mi_child_events[i] &
1300 USB_MID_CHILD_EVENT_DISCONNECT) &&
1301 usb_mid->mi_children_ifs[i]) {
1302 usb_mid->mi_child_events[i] &=
1303 ~USB_MID_CHILD_EVENT_DISCONNECT;
1304 child_dip =
1305 usb_mid->mi_children_dips[i];
1306 mutex_exit(&usb_mid->mi_mutex);
1307
1308 /* post the missed disconnect */
1309 (void) ndi_event_do_callback(
1310 usb_mid->mi_ndi_event_hdl,
1311 child_dip,
1312 rm_cookie,
1313 bus_impldata);
1314 mutex_enter(&usb_mid->mi_mutex);
1315 }
1316 }
1317 mutex_exit(&usb_mid->mi_mutex);
1318
1319 /* pass reconnect event to all the children */
1320 (void) ndi_event_run_callbacks(
1321 usb_mid->mi_ndi_event_hdl, NULL,
1322 ins_cookie, bus_impldata);
1323
1324 if (usb_mid->mi_ugen_hdl) {
1325 (void) usb_ugen_reconnect_ev_cb(
1326 usb_mid->mi_ugen_hdl);
1327 }
1328 }
1329 mutex_enter(&usb_mid->mi_mutex);
1330 break;
1331 case USBA_EVENT_TAG_POST_RESUME:
1332 /*
1333 * Check to see if this child has missed the pre-suspend
1334 * event before it registered for event cb
1335 */
1336 for (i = 0; i < usb_mid->mi_n_ifs; i++) {
1337 if ((usb_mid->mi_child_events[i] &
1338 USB_MID_CHILD_EVENT_PRESUSPEND) &&
1339 usb_mid->mi_children_ifs[i]) {
1340 usb_mid->mi_child_events[i] &=
1341 ~USB_MID_CHILD_EVENT_PRESUSPEND;
1342 child_dip = usb_mid->mi_children_dips[i];
1343 mutex_exit(&usb_mid->mi_mutex);
1344
1345 /* post the missed pre-suspend event */
1346 (void) ndi_event_do_callback(
1347 usb_mid->mi_ndi_event_hdl,
1348 child_dip, suspend_cookie,
1349 bus_impldata);
1350 mutex_enter(&usb_mid->mi_mutex);
1351 }
1352 }
1353 mutex_exit(&usb_mid->mi_mutex);
1354
1355 /* pass post_resume event to all the children */
1356 (void) ndi_event_run_callbacks(usb_mid->mi_ndi_event_hdl,
1357 NULL, resume_cookie, bus_impldata);
1358
1359 mutex_enter(&usb_mid->mi_mutex);
1360 break;
1361 }
1362 mutex_exit(&usb_mid->mi_mutex);
1363
1364 }
1365
1366
1367 /*
1368 * create the pm components required for power management
1369 */
1370 static void
usb_mid_create_pm_components(dev_info_t * dip,usb_mid_t * usb_mid)1371 usb_mid_create_pm_components(dev_info_t *dip, usb_mid_t *usb_mid)
1372 {
1373 usb_common_power_t *midpm;
1374 uint_t pwr_states;
1375
1376 USB_DPRINTF_L4(DPRINT_MASK_PM, usb_mid->mi_log_handle,
1377 "usb_mid_create_pm_components: Begin");
1378
1379 /* Allocate the PM state structure */
1380 midpm = kmem_zalloc(sizeof (usb_common_power_t), KM_SLEEP);
1381
1382 mutex_enter(&usb_mid->mi_mutex);
1383 usb_mid->mi_pm = midpm;
1384 midpm->uc_usb_statep = usb_mid;
1385 midpm->uc_pm_capabilities = 0; /* XXXX should this be 0?? */
1386 midpm->uc_current_power = USB_DEV_OS_FULL_PWR;
1387 mutex_exit(&usb_mid->mi_mutex);
1388
1389 /*
1390 * By not enabling parental notification, PM enforces
1391 * "strict parental dependency" meaning, usb_mid won't
1392 * power off until any of its children are in full power.
1393 */
1394
1395 /*
1396 * there are 3 scenarios:
1397 * 1. a well behaved device should have remote wakeup
1398 * at interface and device level. If the interface
1399 * wakes up, usb_mid will wake up
1400 * 2. if the device doesn't have remote wake up and
1401 * the interface has, PM will still work, ie.
1402 * the interfaces wakes up and usb_mid wakes up
1403 * 3. if neither the interface nor device has remote
1404 * wakeup, the interface will wake up when it is opened
1405 * and goes to sleep after being closed for a while
1406 * In this case usb_mid should also go to sleep shortly
1407 * thereafter
1408 * In all scenarios it doesn't really matter whether
1409 * remote wakeup at the device level is enabled or not
1410 * but we do it anyways
1411 */
1412 if (usb_handle_remote_wakeup(dip, USB_REMOTE_WAKEUP_ENABLE) ==
1413 USB_SUCCESS) {
1414 USB_DPRINTF_L3(DPRINT_MASK_PM, usb_mid->mi_log_handle,
1415 "usb_mid_create_pm_components: "
1416 "Remote Wakeup Enabled");
1417 midpm->uc_wakeup_enabled = 1;
1418 }
1419
1420 if (usb_create_pm_components(dip, &pwr_states) ==
1421 USB_SUCCESS) {
1422 midpm->uc_pwr_states = (uint8_t)pwr_states;
1423 (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
1424 }
1425
1426 USB_DPRINTF_L4(DPRINT_MASK_PM, usb_mid->mi_log_handle,
1427 "usb_mid_create_pm_components: End");
1428 }
1429
1430
1431 /*
1432 * usb_mid_obtain_state:
1433 */
1434 usb_mid_t *
usb_mid_obtain_state(dev_info_t * dip)1435 usb_mid_obtain_state(dev_info_t *dip)
1436 {
1437 int instance = ddi_get_instance(dip);
1438 usb_mid_t *statep = ddi_get_soft_state(usb_mid_statep, instance);
1439
1440 ASSERT(statep != NULL);
1441
1442 return (statep);
1443 }
1444
1445
1446 /*
1447 * ugen support
1448 */
1449 /* ARGSUSED3 */
1450 static int
usb_mid_open(dev_t * devp,int flags,int otyp,cred_t * credp)1451 usb_mid_open(dev_t *devp, int flags, int otyp, cred_t *credp)
1452 {
1453 struct usb_mid *usb_mid;
1454 int rval;
1455
1456 if ((usb_mid = ddi_get_soft_state(usb_mid_statep,
1457 USB_MID_MINOR_TO_INSTANCE(getminor(*devp)))) == NULL) {
1458
1459 return (ENXIO);
1460 }
1461
1462 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, usb_mid->mi_log_handle,
1463 "usb_mid_open: usb_mid = 0x%p *devp = 0x%lx",
1464 (void *)usb_mid, *devp);
1465
1466 /* First bring the device to full power */
1467 (void) pm_busy_component(usb_mid->mi_dip, 0);
1468 (void) pm_raise_power(usb_mid->mi_dip, 0, USB_DEV_OS_FULL_PWR);
1469
1470
1471 rval = usb_ugen_open(usb_mid->mi_ugen_hdl, devp, flags, otyp,
1472 credp);
1473 if (rval) {
1474 (void) pm_idle_component(usb_mid->mi_dip, 0);
1475 } else {
1476 /*
1477 * since all ugen opens are exclusive we can count the
1478 * opens
1479 */
1480 mutex_enter(&usb_mid->mi_mutex);
1481 usb_mid->mi_ugen_open_count++;
1482 mutex_exit(&usb_mid->mi_mutex);
1483 }
1484
1485 return (rval);
1486 }
1487
1488
1489 /* ARGSUSED */
1490 static int
usb_mid_close(dev_t dev,int flag,int otyp,cred_t * credp)1491 usb_mid_close(dev_t dev, int flag, int otyp, cred_t *credp)
1492 {
1493 struct usb_mid *usb_mid;
1494 int rval;
1495
1496 if ((usb_mid = ddi_get_soft_state(usb_mid_statep,
1497 USB_MID_MINOR_TO_INSTANCE(getminor(dev)))) == NULL) {
1498
1499 return (ENXIO);
1500 }
1501
1502 rval = usb_ugen_close(usb_mid->mi_ugen_hdl, dev, flag, otyp,
1503 credp);
1504 if (rval == 0) {
1505 (void) pm_idle_component(usb_mid->mi_dip, 0);
1506 mutex_enter(&usb_mid->mi_mutex);
1507 usb_mid->mi_ugen_open_count--;
1508 mutex_exit(&usb_mid->mi_mutex);
1509 }
1510
1511 return (rval);
1512 }
1513
1514
1515 static int
usb_mid_read(dev_t dev,struct uio * uio,cred_t * credp)1516 usb_mid_read(dev_t dev, struct uio *uio, cred_t *credp)
1517 {
1518 struct usb_mid *usb_mid;
1519
1520 if ((usb_mid = ddi_get_soft_state(usb_mid_statep,
1521 USB_MID_MINOR_TO_INSTANCE(getminor(dev)))) == NULL) {
1522
1523 return (ENXIO);
1524 }
1525
1526 return (usb_ugen_read(usb_mid->mi_ugen_hdl, dev, uio, credp));
1527 }
1528
1529
1530 static int
usb_mid_write(dev_t dev,struct uio * uio,cred_t * credp)1531 usb_mid_write(dev_t dev, struct uio *uio, cred_t *credp)
1532 {
1533 struct usb_mid *usb_mid;
1534
1535 if ((usb_mid = ddi_get_soft_state(usb_mid_statep,
1536 USB_MID_MINOR_TO_INSTANCE(getminor(dev)))) == NULL) {
1537
1538 return (ENXIO);
1539 }
1540
1541 return (usb_ugen_write(usb_mid->mi_ugen_hdl, dev, uio, credp));
1542 }
1543
1544
1545 static int
usb_mid_poll(dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)1546 usb_mid_poll(dev_t dev, short events, int anyyet, short *reventsp,
1547 struct pollhead **phpp)
1548 {
1549 struct usb_mid *usb_mid;
1550
1551 if ((usb_mid = ddi_get_soft_state(usb_mid_statep,
1552 USB_MID_MINOR_TO_INSTANCE(getminor(dev)))) == NULL) {
1553
1554 return (ENXIO);
1555 }
1556
1557 return (usb_ugen_poll(usb_mid->mi_ugen_hdl, dev, events,
1558 anyyet, reventsp, phpp));
1559 }
1560