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 (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved.
24 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
25 * Copyright 2019, Joyent, Inc.
26 * Copyright 2023 Oxide Computer Company
27 */
28
29 /*
30 * USBA: Solaris USB Architecture support for the hub
31 * including root hub
32 * Most of the code for hubd resides in this file and
33 * is shared between the HCD root hub support and hubd
34 */
35 #define USBA_FRAMEWORK
36 #include <sys/usb/usba.h>
37 #include <sys/usb/usba/usba_devdb.h>
38 #include <sys/sunndi.h>
39 #include <sys/usb/usba/usba_impl.h>
40 #include <sys/usb/usba/usba_types.h>
41 #include <sys/usb/usba/hubdi.h>
42 #include <sys/usb/usba/hcdi_impl.h>
43 #include <sys/usb/hubd/hub.h>
44 #include <sys/usb/hubd/hubdvar.h>
45 #include <sys/usb/hubd/hubd_impl.h>
46 #include <sys/kobj.h>
47 #include <sys/kobj_lex.h>
48 #include <sys/fs/dv_node.h>
49 #include <sys/strsun.h>
50
51 /*
52 * External functions
53 */
54 extern boolean_t consconfig_console_is_ready(void);
55
56 /*
57 * Prototypes for static functions
58 */
59 static int usba_hubdi_bus_ctl(dev_info_t *dip,
60 dev_info_t *rdip,
61 ddi_ctl_enum_t op,
62 void *arg,
63 void *result);
64
65 static int usba_hubdi_map_fault(dev_info_t *dip,
66 dev_info_t *rdip,
67 struct hat *hat,
68 struct seg *seg,
69 caddr_t addr,
70 struct devpage *dp,
71 pfn_t pfn,
72 uint_t prot,
73 uint_t lock);
74
75 static int hubd_busop_get_eventcookie(dev_info_t *dip,
76 dev_info_t *rdip,
77 char *eventname,
78 ddi_eventcookie_t *cookie);
79 static int hubd_busop_add_eventcall(dev_info_t *dip,
80 dev_info_t *rdip,
81 ddi_eventcookie_t cookie,
82 ddi_event_cb_f callback,
83 void *arg,
84 ddi_callback_id_t *cb_id);
85 static int hubd_busop_remove_eventcall(dev_info_t *dip,
86 ddi_callback_id_t cb_id);
87 static int hubd_bus_config(dev_info_t *dip,
88 uint_t flag,
89 ddi_bus_config_op_t op,
90 void *arg,
91 dev_info_t **child);
92 static int hubd_bus_unconfig(dev_info_t *dip,
93 uint_t flag,
94 ddi_bus_config_op_t op,
95 void *arg);
96 static int hubd_bus_power(dev_info_t *dip, void *impl_arg,
97 pm_bus_power_op_t op, void *arg, void *result);
98
99 static usb_port_t hubd_get_port_num(hubd_t *, struct devctl_iocdata *);
100 static dev_info_t *hubd_get_child_dip(hubd_t *, usb_port_t);
101 static uint_t hubd_cfgadm_state(hubd_t *, usb_port_t);
102 static int hubd_toggle_port(hubd_t *, usb_port_t);
103 static void hubd_register_cpr_callback(hubd_t *);
104 static void hubd_unregister_cpr_callback(hubd_t *);
105
106 /*
107 * Busops vector for USB HUB's
108 */
109 struct bus_ops usba_hubdi_busops = {
110 BUSO_REV,
111 nullbusmap, /* bus_map */
112 NULL, /* bus_get_intrspec */
113 NULL, /* bus_add_intrspec */
114 NULL, /* bus_remove_intrspec */
115 usba_hubdi_map_fault, /* bus_map_fault */
116 NULL, /* bus_dma_map */
117 ddi_dma_allochdl,
118 ddi_dma_freehdl,
119 ddi_dma_bindhdl,
120 ddi_dma_unbindhdl,
121 ddi_dma_flush,
122 ddi_dma_win,
123 ddi_dma_mctl, /* bus_dma_ctl */
124 usba_hubdi_bus_ctl, /* bus_ctl */
125 ddi_bus_prop_op, /* bus_prop_op */
126 hubd_busop_get_eventcookie,
127 hubd_busop_add_eventcall,
128 hubd_busop_remove_eventcall,
129 NULL, /* bus_post_event */
130 NULL, /* bus_intr_ctl */
131 hubd_bus_config, /* bus_config */
132 hubd_bus_unconfig, /* bus_unconfig */
133 NULL, /* bus_fm_init */
134 NULL, /* bus_fm_fini */
135 NULL, /* bus_fm_access_enter */
136 NULL, /* bus_fm_access_exit */
137 hubd_bus_power /* bus_power */
138 };
139
140 #define USB_HUB_INTEL_VID 0x8087
141 #define USB_HUB_INTEL_PID 0x0020
142
143 /*
144 * local variables
145 */
146 static kmutex_t usba_hubdi_mutex; /* protects USBA HUB data structures */
147
148 static usba_list_entry_t usba_hubdi_list;
149
150 usb_log_handle_t hubdi_log_handle;
151 uint_t hubdi_errlevel = USB_LOG_L4;
152 uint_t hubdi_errmask = (uint_t)-1;
153 uint8_t hubdi_min_pm_threshold = 5; /* seconds */
154 uint8_t hubdi_reset_delay = 20; /* seconds */
155 extern int modrootloaded;
156
157 /*
158 * initialize private data
159 */
160 void
usba_hubdi_initialization()161 usba_hubdi_initialization()
162 {
163 hubdi_log_handle = usb_alloc_log_hdl(NULL, "hubdi", &hubdi_errlevel,
164 &hubdi_errmask, NULL, 0);
165
166 USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle,
167 "usba_hubdi_initialization");
168
169 mutex_init(&usba_hubdi_mutex, NULL, MUTEX_DRIVER, NULL);
170
171 usba_init_list(&usba_hubdi_list, NULL, NULL);
172 }
173
174
175 void
usba_hubdi_destroy()176 usba_hubdi_destroy()
177 {
178 USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle,
179 "usba_hubdi_destroy");
180
181 mutex_destroy(&usba_hubdi_mutex);
182 usba_destroy_list(&usba_hubdi_list);
183
184 usb_free_log_hdl(hubdi_log_handle);
185 }
186
187
188 /*
189 * Called by an HUB to attach an instance of the driver
190 * make this instance known to USBA
191 * the HUB should initialize usba_hubdi structure prior
192 * to calling this interface
193 */
194 int
usba_hubdi_register(dev_info_t * dip,uint_t flags)195 usba_hubdi_register(dev_info_t *dip, uint_t flags)
196 {
197 usba_hubdi_t *hubdi = kmem_zalloc(sizeof (usba_hubdi_t), KM_SLEEP);
198 usba_device_t *usba_device = usba_get_usba_device(dip);
199
200 USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle,
201 "usba_hubdi_register: %s", ddi_node_name(dip));
202
203 hubdi->hubdi_dip = dip;
204 hubdi->hubdi_flags = flags;
205
206 usba_device->usb_hubdi = hubdi;
207
208 /*
209 * add this hubdi instance to the list of known hubdi's
210 */
211 usba_init_list(&hubdi->hubdi_list, (usb_opaque_t)hubdi,
212 usba_hcdi_get_hcdi(usba_device->usb_root_hub_dip)->
213 hcdi_iblock_cookie);
214 mutex_enter(&usba_hubdi_mutex);
215 usba_add_to_list(&usba_hubdi_list, &hubdi->hubdi_list);
216 mutex_exit(&usba_hubdi_mutex);
217
218 return (DDI_SUCCESS);
219 }
220
221
222 /*
223 * Called by an HUB to detach an instance of the driver
224 */
225 int
usba_hubdi_unregister(dev_info_t * dip)226 usba_hubdi_unregister(dev_info_t *dip)
227 {
228 usba_device_t *usba_device = usba_get_usba_device(dip);
229 usba_hubdi_t *hubdi = usba_device->usb_hubdi;
230
231 USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle,
232 "usba_hubdi_unregister: %s", ddi_node_name(dip));
233
234 mutex_enter(&usba_hubdi_mutex);
235 (void) usba_rm_from_list(&usba_hubdi_list, &hubdi->hubdi_list);
236 mutex_exit(&usba_hubdi_mutex);
237
238 usba_destroy_list(&hubdi->hubdi_list);
239
240 kmem_free(hubdi, sizeof (usba_hubdi_t));
241
242 return (DDI_SUCCESS);
243 }
244
245
246 /*
247 * misc bus routines currently not used
248 */
249 /*ARGSUSED*/
250 static int
usba_hubdi_map_fault(dev_info_t * dip,dev_info_t * rdip,struct hat * hat,struct seg * seg,caddr_t addr,struct devpage * dp,pfn_t pfn,uint_t prot,uint_t lock)251 usba_hubdi_map_fault(dev_info_t *dip,
252 dev_info_t *rdip,
253 struct hat *hat,
254 struct seg *seg,
255 caddr_t addr,
256 struct devpage *dp,
257 pfn_t pfn,
258 uint_t prot,
259 uint_t lock)
260 {
261 return (DDI_FAILURE);
262 }
263
264
265 /*
266 * root hub support. the root hub uses the same devi as the HCD
267 */
268 int
usba_hubdi_bind_root_hub(dev_info_t * dip,uchar_t * root_hub_config_descriptor,size_t config_length,usb_dev_descr_t * root_hub_device_descriptor)269 usba_hubdi_bind_root_hub(dev_info_t *dip,
270 uchar_t *root_hub_config_descriptor,
271 size_t config_length,
272 usb_dev_descr_t *root_hub_device_descriptor)
273 {
274 usba_device_t *usba_device;
275 usba_hcdi_t *hcdi = usba_hcdi_get_hcdi(dip);
276 hubd_t *root_hubd;
277 usb_pipe_handle_t ph = NULL;
278 dev_info_t *child = ddi_get_child(dip);
279
280 if (ndi_prop_create_boolean(DDI_DEV_T_NONE, dip,
281 "root-hub") != NDI_SUCCESS) {
282
283 return (USB_FAILURE);
284 }
285
286 usba_add_root_hub(dip);
287
288 root_hubd = kmem_zalloc(sizeof (hubd_t), KM_SLEEP);
289
290 /*
291 * create and initialize a usba_device structure
292 */
293 usba_device = usba_alloc_usba_device(dip);
294
295 mutex_enter(&usba_device->usb_mutex);
296 usba_device->usb_hcdi_ops = hcdi->hcdi_ops;
297 usba_device->usb_cfg = root_hub_config_descriptor;
298 usba_device->usb_cfg_length = config_length;
299 usba_device->usb_dev_descr = root_hub_device_descriptor;
300 usba_device->usb_port = 1;
301 usba_device->usb_addr = ROOT_HUB_ADDR;
302 usba_device->usb_root_hubd = root_hubd;
303 usba_device->usb_cfg_array = kmem_zalloc(sizeof (uchar_t *),
304 KM_SLEEP);
305 usba_device->usb_cfg_array_length = sizeof (uchar_t *);
306
307 usba_device->usb_cfg_array_len = kmem_zalloc(sizeof (uint16_t),
308 KM_SLEEP);
309 usba_device->usb_cfg_array_len_length = sizeof (uint16_t);
310
311 usba_device->usb_cfg_array[0] = root_hub_config_descriptor;
312 usba_device->usb_cfg_array_len[0] =
313 sizeof (root_hub_config_descriptor);
314
315 usba_device->usb_cfg_str_descr = kmem_zalloc(sizeof (uchar_t *),
316 KM_SLEEP);
317 usba_device->usb_n_cfgs = 1;
318 usba_device->usb_n_ifs = 1;
319 usba_device->usb_dip = dip;
320
321 usba_device->usb_client_flags = kmem_zalloc(
322 usba_device->usb_n_ifs * USBA_CLIENT_FLAG_SIZE, KM_SLEEP);
323
324 usba_device->usb_client_attach_list = kmem_zalloc(
325 usba_device->usb_n_ifs *
326 sizeof (*usba_device->usb_client_attach_list), KM_SLEEP);
327
328 usba_device->usb_client_ev_cb_list = kmem_zalloc(
329 usba_device->usb_n_ifs *
330 sizeof (*usba_device->usb_client_ev_cb_list), KM_SLEEP);
331
332 /*
333 * The bDeviceProtocol field of root hub device specifies,
334 * whether root hub is a Super, High, or Full speed usb device.
335 */
336 if (root_hub_device_descriptor->bDeviceProtocol >= 0x3) {
337 usba_device->usb_port_status = USBA_SUPER_SPEED_DEV;
338 } else if (root_hub_device_descriptor->bDeviceProtocol > 0) {
339 usba_device->usb_port_status = USBA_HIGH_SPEED_DEV;
340 } else {
341 usba_device->usb_port_status = USBA_FULL_SPEED_DEV;
342 }
343
344 mutex_exit(&usba_device->usb_mutex);
345
346 usba_set_usba_device(dip, usba_device);
347
348 /*
349 * For the root hub the default pipe is not yet open
350 */
351 if (usb_pipe_open(dip, NULL, NULL,
352 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph) != USB_SUCCESS) {
353 goto fail;
354 }
355
356 /*
357 * kill off all OBP children, they may not be fully
358 * enumerated
359 */
360 while (child) {
361 dev_info_t *next = ddi_get_next_sibling(child);
362 (void) ddi_remove_child(child, 0);
363 child = next;
364 }
365
366 /*
367 * "attach" the root hub driver
368 */
369 if (usba_hubdi_attach(dip, DDI_ATTACH) != DDI_SUCCESS) {
370 goto fail;
371 }
372
373 return (USB_SUCCESS);
374
375 fail:
376 if (ph) {
377 usb_pipe_close(dip, ph,
378 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
379 }
380
381 kmem_free(usba_device->usb_cfg_array,
382 usba_device->usb_cfg_array_length);
383 kmem_free(usba_device->usb_cfg_array_len,
384 usba_device->usb_cfg_array_len_length);
385
386 kmem_free(usba_device->usb_cfg_str_descr, sizeof (uchar_t *));
387
388 usba_free_usba_device(usba_device);
389
390 usba_set_usba_device(dip, NULL);
391
392 if (root_hubd) {
393 kmem_free(root_hubd, sizeof (hubd_t));
394 }
395
396 (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "root-hub");
397
398 usba_rem_root_hub(dip);
399
400 return (USB_FAILURE);
401 }
402
403
404 int
usba_hubdi_unbind_root_hub(dev_info_t * dip)405 usba_hubdi_unbind_root_hub(dev_info_t *dip)
406 {
407 usba_device_t *usba_device;
408
409 /* was root hub attached? */
410 if (!(usba_is_root_hub(dip))) {
411
412 /* return success anyway */
413 return (USB_SUCCESS);
414 }
415
416 /*
417 * usba_hubdi_detach also closes the default pipe
418 * and removes properties so there is no need to
419 * do it here
420 */
421 if (usba_hubdi_detach(dip, DDI_DETACH) != DDI_SUCCESS) {
422
423 if (DEVI_IS_ATTACHING(dip)) {
424 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
425 "failure to unbind root hub after attach failure");
426 }
427
428 return (USB_FAILURE);
429 }
430
431 usba_device = usba_get_usba_device(dip);
432
433 kmem_free(usba_device->usb_root_hubd, sizeof (hubd_t));
434
435 kmem_free(usba_device->usb_cfg_array,
436 usba_device->usb_cfg_array_length);
437 kmem_free(usba_device->usb_cfg_array_len,
438 usba_device->usb_cfg_array_len_length);
439
440 kmem_free(usba_device->usb_cfg_str_descr, sizeof (uchar_t *));
441
442 usba_free_usba_device(usba_device);
443
444 usba_rem_root_hub(dip);
445
446 (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "root-hub");
447
448 return (USB_SUCCESS);
449 }
450
451
452 /*
453 * Actual Hub Driver support code:
454 * shared by root hub and non-root hubs
455 */
456 #include <sys/usb/usba/usbai_version.h>
457
458 /* Debugging support */
459 uint_t hubd_errlevel = USB_LOG_L4;
460 uint_t hubd_errmask = (uint_t)DPRINT_MASK_ALL;
461 uint_t hubd_instance_debug = (uint_t)-1;
462 static uint_t hubdi_bus_config_debug = 0;
463
464 _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_errlevel))
465 _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_errmask))
466 _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_instance_debug))
467
468 _NOTE(SCHEME_PROTECTS_DATA("unique", msgb))
469 _NOTE(SCHEME_PROTECTS_DATA("unique", dev_info))
470
471
472 /*
473 * local variables:
474 *
475 * Amount of time to wait between resetting the port and accessing
476 * the device. The value is in microseconds.
477 */
478 static uint_t hubd_device_delay = 1000000;
479
480 /*
481 * enumeration retry
482 */
483 #define HUBD_PORT_RETRY 5
484 static uint_t hubd_retry_enumerate = HUBD_PORT_RETRY;
485
486 /*
487 * Stale hotremoved device cleanup delay
488 */
489 #define HUBD_STALE_DIP_CLEANUP_DELAY 5000000
490 static uint_t hubd_dip_cleanup_delay = HUBD_STALE_DIP_CLEANUP_DELAY;
491
492 /*
493 * retries for USB suspend and resume
494 */
495 #define HUBD_SUS_RES_RETRY 2
496
497 void *hubd_statep;
498
499 /*
500 * prototypes
501 */
502 static int hubd_cleanup(dev_info_t *dip, hubd_t *hubd);
503 static int hubd_check_ports(hubd_t *hubd);
504
505 static int hubd_open_intr_pipe(hubd_t *hubd);
506 static void hubd_start_polling(hubd_t *hubd, int always);
507 static void hubd_stop_polling(hubd_t *hubd);
508 static void hubd_close_intr_pipe(hubd_t *hubd);
509
510 static void hubd_read_cb(usb_pipe_handle_t pipe, usb_intr_req_t *req);
511 static void hubd_exception_cb(usb_pipe_handle_t pipe,
512 usb_intr_req_t *req);
513 static void hubd_hotplug_thread(void *arg);
514 static void hubd_reset_thread(void *arg);
515 static int hubd_create_child(dev_info_t *dip,
516 hubd_t *hubd,
517 usba_device_t *usba_device,
518 usb_port_status_t port_status,
519 usb_port_t port,
520 int iteration);
521
522 static int hubd_delete_child(hubd_t *hubd, usb_port_t port, uint_t flag,
523 boolean_t retry);
524
525 static int hubd_get_hub_descriptor(hubd_t *hubd);
526
527 static int hubd_set_hub_depth(hubd_t *hubd);
528
529 static int hubd_get_hub_status_words(hubd_t *hubd, uint16_t *status);
530
531 static int hubd_reset_port(hubd_t *hubd, usb_port_t port);
532
533 static int hubd_get_hub_status(hubd_t *hubd);
534
535 static int hubd_handle_port_connect(hubd_t *hubd, usb_port_t port);
536
537 static int hubd_disable_port(hubd_t *hubd, usb_port_t port);
538
539 static int hubd_enable_port(hubd_t *hubd, usb_port_t port);
540 static int hubd_recover_disabled_port(hubd_t *hubd, usb_port_t port);
541
542 static int hubd_determine_port_status(hubd_t *hubd, usb_port_t port,
543 uint16_t *status, uint16_t *change, usb_port_status_t *speed,
544 uint_t ack_flag);
545
546 static int hubd_enable_all_port_power(hubd_t *hubd);
547 static int hubd_disable_all_port_power(hubd_t *hubd);
548 static int hubd_disable_port_power(hubd_t *hubd, usb_port_t port);
549 static int hubd_enable_port_power(hubd_t *hubd, usb_port_t port);
550
551 static void hubd_free_usba_device(hubd_t *hubd, usba_device_t *usba_device);
552
553 static int hubd_can_suspend(hubd_t *hubd);
554 static void hubd_restore_device_state(dev_info_t *dip, hubd_t *hubd);
555 static int hubd_setdevaddr(hubd_t *hubd, usb_port_t port);
556 static void hubd_setdevconfig(hubd_t *hubd, usb_port_t port);
557
558 static int hubd_register_events(hubd_t *hubd);
559 static void hubd_do_callback(hubd_t *hubd, dev_info_t *dip,
560 ddi_eventcookie_t cookie);
561 static void hubd_run_callbacks(hubd_t *hubd, usba_event_t type);
562 static void hubd_post_event(hubd_t *hubd, usb_port_t port, usba_event_t type);
563 static void hubd_create_pm_components(dev_info_t *dip, hubd_t *hubd);
564
565 static int hubd_disconnect_event_cb(dev_info_t *dip);
566 static int hubd_reconnect_event_cb(dev_info_t *dip);
567 static int hubd_pre_suspend_event_cb(dev_info_t *dip);
568 static int hubd_post_resume_event_cb(dev_info_t *dip);
569 static int hubd_cpr_suspend(hubd_t *hubd);
570 static void hubd_cpr_resume(dev_info_t *dip);
571 static int hubd_restore_state_cb(dev_info_t *dip);
572 static int hubd_check_same_device(hubd_t *hubd, usb_port_t port);
573
574 static int hubd_init_power_budget(hubd_t *hubd);
575
576 static ndi_event_definition_t hubd_ndi_event_defs[] = {
577 {USBA_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL,
578 NDI_EVENT_POST_TO_ALL},
579 {USBA_EVENT_TAG_HOT_INSERTION, DDI_DEVI_INSERT_EVENT, EPL_KERNEL,
580 NDI_EVENT_POST_TO_ALL},
581 {USBA_EVENT_TAG_POST_RESUME, USBA_POST_RESUME_EVENT, EPL_KERNEL,
582 NDI_EVENT_POST_TO_ALL},
583 {USBA_EVENT_TAG_PRE_SUSPEND, USBA_PRE_SUSPEND_EVENT, EPL_KERNEL,
584 NDI_EVENT_POST_TO_ALL}
585 };
586
587 #define HUBD_N_NDI_EVENTS \
588 (sizeof (hubd_ndi_event_defs) / sizeof (ndi_event_definition_t))
589
590 static ndi_event_set_t hubd_ndi_events = {
591 NDI_EVENTS_REV1, HUBD_N_NDI_EVENTS, hubd_ndi_event_defs};
592
593 /* events received from parent */
594 static usb_event_t hubd_events = {
595 hubd_disconnect_event_cb,
596 hubd_reconnect_event_cb,
597 hubd_pre_suspend_event_cb,
598 hubd_post_resume_event_cb
599 };
600
601
602 /*
603 * hubd_get_soft_state() returns the hubd soft state
604 */
605 hubd_t *
hubd_get_soft_state(dev_info_t * dip)606 hubd_get_soft_state(dev_info_t *dip)
607 {
608 if (dip == NULL) {
609 return (NULL);
610 }
611
612 if (usba_is_root_hub(dip)) {
613 usba_device_t *usba_device = usba_get_usba_device(dip);
614
615 return (usba_device->usb_root_hubd);
616 } else {
617 int instance = ddi_get_instance(dip);
618
619 return (ddi_get_soft_state(hubd_statep, instance));
620 }
621 }
622
623
624 /*
625 * PM support functions:
626 */
627 /*ARGSUSED*/
628 static void
hubd_pm_busy_component(hubd_t * hubd,dev_info_t * dip,int component)629 hubd_pm_busy_component(hubd_t *hubd, dev_info_t *dip, int component)
630 {
631 if (hubd->h_hubpm != NULL) {
632 hubd->h_hubpm->hubp_busy_pm++;
633 mutex_exit(HUBD_MUTEX(hubd));
634 if (pm_busy_component(dip, 0) != DDI_SUCCESS) {
635 mutex_enter(HUBD_MUTEX(hubd));
636 hubd->h_hubpm->hubp_busy_pm--;
637 mutex_exit(HUBD_MUTEX(hubd));
638 }
639 mutex_enter(HUBD_MUTEX(hubd));
640 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
641 "hubd_pm_busy_component: %d", hubd->h_hubpm->hubp_busy_pm);
642 }
643 }
644
645
646 /*ARGSUSED*/
647 static void
hubd_pm_idle_component(hubd_t * hubd,dev_info_t * dip,int component)648 hubd_pm_idle_component(hubd_t *hubd, dev_info_t *dip, int component)
649 {
650 if (hubd->h_hubpm != NULL) {
651 mutex_exit(HUBD_MUTEX(hubd));
652 if (pm_idle_component(dip, 0) == DDI_SUCCESS) {
653 mutex_enter(HUBD_MUTEX(hubd));
654 ASSERT(hubd->h_hubpm->hubp_busy_pm > 0);
655 hubd->h_hubpm->hubp_busy_pm--;
656 mutex_exit(HUBD_MUTEX(hubd));
657 }
658 mutex_enter(HUBD_MUTEX(hubd));
659 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
660 "hubd_pm_idle_component: %d", hubd->h_hubpm->hubp_busy_pm);
661 }
662 }
663
664
665 /*
666 * track power level changes for children of this instance
667 */
668 static void
hubd_set_child_pwrlvl(hubd_t * hubd,usb_port_t port,uint8_t power)669 hubd_set_child_pwrlvl(hubd_t *hubd, usb_port_t port, uint8_t power)
670 {
671 int old_power, new_power, pwr;
672 usb_port_t portno;
673 hub_power_t *hubpm;
674
675 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
676 "hubd_set_child_pwrlvl: port=%d power=%d",
677 port, power);
678
679 mutex_enter(HUBD_MUTEX(hubd));
680 hubpm = hubd->h_hubpm;
681
682 old_power = 0;
683 for (portno = 1; portno <= hubd->h_nports; portno++) {
684 old_power += hubpm->hubp_child_pwrstate[portno];
685 }
686
687 /* assign the port power */
688 pwr = hubd->h_hubpm->hubp_child_pwrstate[port];
689 hubd->h_hubpm->hubp_child_pwrstate[port] = power;
690 new_power = old_power - pwr + power;
691
692 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
693 "hubd_set_child_pwrlvl: new_power=%d old_power=%d",
694 new_power, old_power);
695
696 if ((new_power > 0) && (old_power == 0)) {
697 /* we have the first child coming out of low power */
698 (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0);
699 } else if ((new_power == 0) && (old_power > 0)) {
700 /* we have the last child going to low power */
701 (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
702 }
703 mutex_exit(HUBD_MUTEX(hubd));
704 }
705
706
707 /*
708 * given a child dip, locate its port number
709 */
710 static usb_port_t
hubd_child_dip2port(hubd_t * hubd,dev_info_t * dip)711 hubd_child_dip2port(hubd_t *hubd, dev_info_t *dip)
712 {
713 usb_port_t port;
714
715 mutex_enter(HUBD_MUTEX(hubd));
716 for (port = 1; port <= hubd->h_nports; port++) {
717 if (hubd->h_children_dips[port] == dip) {
718
719 break;
720 }
721 }
722 ASSERT(port <= hubd->h_nports);
723 mutex_exit(HUBD_MUTEX(hubd));
724
725 return (port);
726 }
727
728
729 /*
730 * if the hub can be put into low power mode, return success
731 * NOTE: suspend here means going to lower power, not CPR suspend.
732 */
733 static int
hubd_can_suspend(hubd_t * hubd)734 hubd_can_suspend(hubd_t *hubd)
735 {
736 hub_power_t *hubpm;
737 int total_power = 0;
738 usb_port_t port;
739
740 hubpm = hubd->h_hubpm;
741
742 if (DEVI_IS_DETACHING(hubd->h_dip)) {
743
744 return (USB_SUCCESS);
745 }
746
747 /*
748 * Don't go to lower power if haven't been at full power for enough
749 * time to let hotplug thread kickoff.
750 */
751 if (gethrtime() < (hubpm->hubp_time_at_full_power +
752 hubpm->hubp_min_pm_threshold)) {
753
754 return (USB_FAILURE);
755 }
756
757 for (port = 1; (total_power == 0) &&
758 (port <= hubd->h_nports); port++) {
759 total_power += hubpm->hubp_child_pwrstate[port];
760 }
761
762 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
763 "hubd_can_suspend: %d", total_power);
764
765 return (total_power ? USB_FAILURE : USB_SUCCESS);
766 }
767
768
769 /*
770 * resume port depending on current device state
771 */
772 static int
hubd_resume_port(hubd_t * hubd,usb_port_t port)773 hubd_resume_port(hubd_t *hubd, usb_port_t port)
774 {
775 int rval, retry;
776 usb_cr_t completion_reason;
777 usb_cb_flags_t cb_flags;
778 uint16_t status;
779 uint16_t change;
780 int retval = USB_FAILURE;
781
782 mutex_enter(HUBD_MUTEX(hubd));
783
784 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
785 "hubd_resume_port: port=%d state=0x%x (%s)", port,
786 hubd->h_dev_state, usb_str_dev_state(hubd->h_dev_state));
787
788 switch (hubd->h_dev_state) {
789 case USB_DEV_HUB_CHILD_PWRLVL:
790 /*
791 * This could be a bus ctl for a port other than the one
792 * that has a remote wakeup condition. So check.
793 */
794 if ((hubd->h_port_state[port] & PORT_STATUS_PSS) == 0) {
795 /* the port isn't suspended, so don't resume */
796 retval = USB_SUCCESS;
797
798 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
799 "hubd_resume_port: port=%d not suspended", port);
800
801 break;
802 }
803 /*
804 * Device has initiated a wakeup.
805 * Issue a ClearFeature(PortSuspend)
806 */
807 mutex_exit(HUBD_MUTEX(hubd));
808 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
809 hubd->h_default_pipe,
810 HUB_HANDLE_PORT_FEATURE_TYPE,
811 USB_REQ_CLEAR_FEATURE,
812 CFS_PORT_SUSPEND,
813 port,
814 0, NULL, 0,
815 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
816 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
817 "ClearFeature(PortSuspend) fails "
818 "rval=%d cr=%d cb=0x%x", rval,
819 completion_reason, cb_flags);
820 }
821 mutex_enter(HUBD_MUTEX(hubd));
822
823 /* either way ack changes on the port */
824 (void) hubd_determine_port_status(hubd, port,
825 &status, &change, NULL, PORT_CHANGE_PSSC);
826 retval = USB_SUCCESS;
827
828 break;
829 case USB_DEV_HUB_STATE_RECOVER:
830 /*
831 * When hubd's connect event callback posts a connect
832 * event to its child, it results in this busctl call
833 * which is valid
834 */
835 /* FALLTHRU */
836 case USB_DEV_ONLINE:
837 if (((hubd->h_port_state[port] & PORT_STATUS_CCS) == 0) ||
838 ((hubd->h_port_state[port] & PORT_STATUS_PSS) == 0)) {
839 /*
840 * the port isn't suspended, or connected
841 * so don't resume
842 */
843 retval = USB_SUCCESS;
844
845 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
846 "hubd_resume_port: port=%d not suspended", port);
847
848 break;
849 }
850 /*
851 * prevent kicking off the hotplug thread
852 */
853 hubd->h_hotplug_thread++;
854 hubd_stop_polling(hubd);
855
856 /* Now ClearFeature(PortSuspend) */
857 for (retry = 0; retry < HUBD_SUS_RES_RETRY; retry++) {
858 mutex_exit(HUBD_MUTEX(hubd));
859 rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
860 hubd->h_default_pipe,
861 HUB_HANDLE_PORT_FEATURE_TYPE,
862 USB_REQ_CLEAR_FEATURE,
863 CFS_PORT_SUSPEND,
864 port,
865 0, NULL, 0,
866 &completion_reason, &cb_flags, 0);
867 mutex_enter(HUBD_MUTEX(hubd));
868 if (rval != USB_SUCCESS) {
869 USB_DPRINTF_L2(DPRINT_MASK_PM,
870 hubd->h_log_handle,
871 "ClearFeature(PortSuspend) fails"
872 "rval=%d cr=%d cb=0x%x", rval,
873 completion_reason, cb_flags);
874 } else {
875 /*
876 * As per spec section 11.9 and 7.1.7.7
877 * hub need to provide at least 20ms of
878 * resume signalling, and s/w provide 10ms of
879 * recovery time before accessing the port.
880 */
881 mutex_exit(HUBD_MUTEX(hubd));
882 delay(drv_usectohz(40000));
883 mutex_enter(HUBD_MUTEX(hubd));
884 (void) hubd_determine_port_status(hubd, port,
885 &status, &change, NULL, PORT_CHANGE_PSSC);
886
887 if ((status & PORT_STATUS_PSS) == 0) {
888 /* the port did finally resume */
889 retval = USB_SUCCESS;
890
891 break;
892 }
893 }
894 }
895
896 /* allow hotplug thread again */
897 hubd->h_hotplug_thread--;
898 hubd_start_polling(hubd, 0);
899
900 break;
901 case USB_DEV_DISCONNECTED:
902 /* Ignore - NO Operation */
903 retval = USB_SUCCESS;
904
905 break;
906 case USB_DEV_SUSPENDED:
907 case USB_DEV_PWRED_DOWN:
908 default:
909 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
910 "Improper state for port Resume");
911
912 break;
913 }
914 mutex_exit(HUBD_MUTEX(hubd));
915
916 return (retval);
917 }
918
919
920 /*
921 * suspend port depending on device state
922 */
923 static int
hubd_suspend_port(hubd_t * hubd,usb_port_t port)924 hubd_suspend_port(hubd_t *hubd, usb_port_t port)
925 {
926 int rval, retry;
927 int retval = USB_FAILURE;
928 usb_cr_t completion_reason;
929 usb_cb_flags_t cb_flags;
930 uint16_t status;
931 uint16_t change;
932
933 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
934 "hubd_suspend_port: port=%d", port);
935
936 mutex_enter(HUBD_MUTEX(hubd));
937
938 switch (hubd->h_dev_state) {
939 case USB_DEV_HUB_STATE_RECOVER:
940 /*
941 * When hubd's connect event callback posts a connect
942 * event to its child, it results in this busctl call
943 * which is valid
944 */
945 /* FALLTHRU */
946 case USB_DEV_HUB_CHILD_PWRLVL:
947 /*
948 * When one child is resuming, the other could timeout
949 * and go to low power mode, which is valid
950 */
951 /* FALLTHRU */
952 case USB_DEV_ONLINE:
953 hubd->h_hotplug_thread++;
954 hubd_stop_polling(hubd);
955
956 /*
957 * Some devices start an unprovoked resume. According to spec,
958 * normal resume time for port is 10ms. Wait for double that
959 * time, then check to be sure port is really suspended.
960 */
961 for (retry = 0; retry < HUBD_SUS_RES_RETRY; retry++) {
962 /* Now SetFeature(PortSuspend) */
963 mutex_exit(HUBD_MUTEX(hubd));
964 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
965 hubd->h_default_pipe,
966 HUB_HANDLE_PORT_FEATURE_TYPE,
967 USB_REQ_SET_FEATURE,
968 CFS_PORT_SUSPEND,
969 port,
970 0, NULL, 0,
971 &completion_reason, &cb_flags, 0)) !=
972 USB_SUCCESS) {
973 USB_DPRINTF_L2(DPRINT_MASK_PM,
974 hubd->h_log_handle,
975 "SetFeature(PortSuspend) fails"
976 "rval=%d cr=%d cb=0x%x",
977 rval, completion_reason, cb_flags);
978 }
979
980 /*
981 * some devices start an unprovoked resume
982 * wait and check port status after some time
983 */
984 delay(drv_usectohz(20000));
985
986 /* either ways ack changes on the port */
987 mutex_enter(HUBD_MUTEX(hubd));
988 (void) hubd_determine_port_status(hubd, port,
989 &status, &change, NULL, PORT_CHANGE_PSSC);
990 if (status & PORT_STATUS_PSS) {
991 /* the port is indeed suspended */
992 retval = USB_SUCCESS;
993
994 break;
995 } else {
996 USB_DPRINTF_L0(DPRINT_MASK_PM,
997 hubd->h_log_handle,
998 "hubdi: port%d failed to be suspended!",
999 port);
1000 }
1001 }
1002
1003 hubd->h_hotplug_thread--;
1004 hubd_start_polling(hubd, 0);
1005
1006 break;
1007
1008 case USB_DEV_DISCONNECTED:
1009 /* Ignore - No Operation */
1010 retval = USB_SUCCESS;
1011
1012 break;
1013
1014 case USB_DEV_SUSPENDED:
1015 case USB_DEV_PWRED_DOWN:
1016 default:
1017 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
1018 "Improper state for port Suspend");
1019
1020 break;
1021 }
1022 mutex_exit(HUBD_MUTEX(hubd));
1023
1024 return (retval);
1025 }
1026
1027
1028 /*
1029 * child post attach/detach notifications
1030 */
1031 static void
hubd_post_attach(hubd_t * hubd,usb_port_t port,struct attachspec * as)1032 hubd_post_attach(hubd_t *hubd, usb_port_t port, struct attachspec *as)
1033 {
1034 dev_info_t *dip;
1035
1036 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
1037 "hubd_post_attach: port=%d result=%d",
1038 port, as->result);
1039
1040 if (as->result == DDI_SUCCESS) {
1041 /*
1042 * Check if the child created wants to be power managed.
1043 * If yes, the childs power level gets automatically tracked
1044 * by DDI_CTLOPS_POWER busctl.
1045 * If no, we set power of the new child by default
1046 * to USB_DEV_OS_FULL_PWR. Because we should never suspend.
1047 */
1048 mutex_enter(HUBD_MUTEX(hubd));
1049 dip = hubd->h_children_dips[port];
1050 mutex_exit(HUBD_MUTEX(hubd));
1051 if (DEVI(dip)->devi_pm_info == NULL) {
1052 hubd_set_child_pwrlvl(hubd, port, USB_DEV_OS_FULL_PWR);
1053 }
1054 }
1055 }
1056
1057
1058 static void
hubd_post_detach(hubd_t * hubd,usb_port_t port,struct detachspec * ds)1059 hubd_post_detach(hubd_t *hubd, usb_port_t port, struct detachspec *ds)
1060 {
1061 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
1062 "hubd_post_detach: port=%d result=%d", port, ds->result);
1063
1064 /*
1065 * if the device is successfully detached and is the
1066 * last device to detach, mark component as idle
1067 */
1068 mutex_enter(HUBD_MUTEX(hubd));
1069 if (ds->result == DDI_SUCCESS) {
1070 usba_device_t *usba_device = hubd->h_usba_devices[port];
1071 dev_info_t *pdip = hubd->h_dip;
1072 mutex_exit(HUBD_MUTEX(hubd));
1073
1074 usba_hubdi_incr_power_budget(pdip, usba_device);
1075
1076 /*
1077 * We set power of the detached child
1078 * to 0, so that we can suspend if all
1079 * our children are gone
1080 */
1081 hubd_set_child_pwrlvl(hubd, port, USB_DEV_OS_PWR_OFF);
1082
1083 /* check for leaks on detaching */
1084 if ((usba_device) && (ds->cmd == DDI_DETACH)) {
1085 usba_check_for_leaks(usba_device);
1086 }
1087 } else {
1088 mutex_exit(HUBD_MUTEX(hubd));
1089 }
1090 }
1091
1092
1093 /*
1094 * hubd_post_power
1095 * After the child's power entry point has been called
1096 * we record its power level in our local struct.
1097 * If the device has powered off, we suspend port
1098 */
1099 static int
hubd_post_power(hubd_t * hubd,usb_port_t port,pm_bp_child_pwrchg_t * bpc,int result)1100 hubd_post_power(hubd_t *hubd, usb_port_t port, pm_bp_child_pwrchg_t *bpc,
1101 int result)
1102 {
1103 int retval = USB_SUCCESS;
1104
1105 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
1106 "hubd_post_power: port=%d", port);
1107
1108 if (result == DDI_SUCCESS) {
1109
1110 /* record this power in our local struct */
1111 hubd_set_child_pwrlvl(hubd, port, bpc->bpc_nlevel);
1112
1113 if (bpc->bpc_nlevel == USB_DEV_OS_PWR_OFF) {
1114
1115 /* now suspend the port */
1116 retval = hubd_suspend_port(hubd, port);
1117 } else if (bpc->bpc_nlevel == USB_DEV_OS_FULL_PWR) {
1118
1119 /* make sure the port is resumed */
1120 retval = hubd_resume_port(hubd, port);
1121 }
1122 } else {
1123
1124 /* record old power in our local struct */
1125 hubd_set_child_pwrlvl(hubd, port, bpc->bpc_olevel);
1126
1127 if (bpc->bpc_olevel == USB_DEV_OS_PWR_OFF) {
1128
1129 /*
1130 * As this device failed to transition from
1131 * power off state, suspend the port again
1132 */
1133 retval = hubd_suspend_port(hubd, port);
1134 }
1135 }
1136
1137 return (retval);
1138 }
1139
1140
1141 /*
1142 * bus ctl notifications are handled here, the rest goes up to root hub/hcd
1143 */
1144 static int
usba_hubdi_bus_ctl(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)1145 usba_hubdi_bus_ctl(dev_info_t *dip,
1146 dev_info_t *rdip,
1147 ddi_ctl_enum_t op,
1148 void *arg,
1149 void *result)
1150 {
1151 usba_device_t *hub_usba_device = usba_get_usba_device(rdip);
1152 dev_info_t *root_hub_dip = hub_usba_device->usb_root_hub_dip;
1153 struct attachspec *as;
1154 struct detachspec *ds;
1155 hubd_t *hubd;
1156 usb_port_t port;
1157 int rval;
1158 int retval = DDI_FAILURE;
1159
1160 hubd = hubd_get_soft_state(dip);
1161
1162 mutex_enter(HUBD_MUTEX(hubd));
1163
1164 /* flag that we are currently running bus_ctl */
1165 hubd->h_bus_ctls++;
1166 mutex_exit(HUBD_MUTEX(hubd));
1167
1168 USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle,
1169 "usba_hubdi_bus_ctl:\n\t"
1170 "dip=0x%p, rdip=0x%p, op=0x%x, arg=0x%p",
1171 (void *)dip, (void *)rdip, op, arg);
1172
1173 switch (op) {
1174 case DDI_CTLOPS_ATTACH:
1175 as = (struct attachspec *)arg;
1176 port = hubd_child_dip2port(hubd, rdip);
1177
1178 /* there is nothing to do at resume time */
1179 if (as->cmd == DDI_RESUME) {
1180 break;
1181 }
1182
1183 /* serialize access */
1184 ndi_devi_enter(hubd->h_dip);
1185
1186 switch (as->when) {
1187 case DDI_PRE:
1188 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
1189 "DDI_PRE DDI_CTLOPS_ATTACH: dip=%p, port=%d",
1190 (void *)rdip, port);
1191
1192 mutex_enter(HUBD_MUTEX(hubd));
1193 hubd->h_port_state[port] |= HUBD_CHILD_ATTACHING;
1194
1195 /* Go busy here. Matching idle is DDI_POST case. */
1196 (void) hubd_pm_busy_component(hubd, dip, 0);
1197 mutex_exit(HUBD_MUTEX(hubd));
1198
1199 /*
1200 * if we suspended the port previously
1201 * because child went to low power state, and
1202 * someone unloaded the driver, the port would
1203 * still be suspended and needs to be resumed
1204 */
1205 rval = hubd_resume_port(hubd, port);
1206 if (rval == USB_SUCCESS) {
1207 retval = DDI_SUCCESS;
1208 }
1209
1210 break;
1211 case DDI_POST:
1212 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
1213 "DDI_POST DDI_CTLOPS_ATTACH: dip=%p, port=%d",
1214 (void *)rdip, port);
1215
1216 mutex_enter(HUBD_MUTEX(hubd));
1217 hubd->h_port_state[port] &= ~HUBD_CHILD_ATTACHING;
1218 mutex_exit(HUBD_MUTEX(hubd));
1219
1220 hubd_post_attach(hubd, port, (struct attachspec *)arg);
1221 retval = DDI_SUCCESS;
1222 mutex_enter(HUBD_MUTEX(hubd));
1223
1224 /* Matching idle call for DDI_PRE busy call. */
1225 (void) hubd_pm_idle_component(hubd, dip, 0);
1226 mutex_exit(HUBD_MUTEX(hubd));
1227 }
1228 ndi_devi_exit(hubd->h_dip);
1229
1230 break;
1231 case DDI_CTLOPS_DETACH:
1232 ds = (struct detachspec *)arg;
1233 port = hubd_child_dip2port(hubd, rdip);
1234
1235 /* there is nothing to do at suspend time */
1236 if (ds->cmd == DDI_SUSPEND) {
1237 break;
1238 }
1239
1240 /* serialize access */
1241 ndi_devi_enter(hubd->h_dip);
1242
1243 switch (ds->when) {
1244 case DDI_PRE:
1245 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
1246 "DDI_PRE DDI_CTLOPS_DETACH: dip=%p port=%d",
1247 (void *)rdip, port);
1248
1249 mutex_enter(HUBD_MUTEX(hubd));
1250 hubd->h_port_state[port] |= HUBD_CHILD_DETACHING;
1251
1252 /* Go busy here. Matching idle is DDI_POST case. */
1253 (void) hubd_pm_busy_component(hubd, dip, 0);
1254
1255 mutex_exit(HUBD_MUTEX(hubd));
1256 retval = DDI_SUCCESS;
1257
1258 break;
1259 case DDI_POST:
1260 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
1261 "DDI_POST DDI_CTLOPS_DETACH: dip=%p port=%d",
1262 (void *)rdip, port);
1263
1264 mutex_enter(HUBD_MUTEX(hubd));
1265 hubd->h_port_state[port] &= ~HUBD_CHILD_DETACHING;
1266 mutex_exit(HUBD_MUTEX(hubd));
1267
1268 /* Matching idle call for DDI_PRE busy call. */
1269 hubd_post_detach(hubd, port, (struct detachspec *)arg);
1270 retval = DDI_SUCCESS;
1271 mutex_enter(HUBD_MUTEX(hubd));
1272 (void) hubd_pm_idle_component(hubd, dip, 0);
1273 mutex_exit(HUBD_MUTEX(hubd));
1274
1275 break;
1276 }
1277 ndi_devi_exit(hubd->h_dip);
1278
1279 break;
1280 default:
1281 retval = usba_bus_ctl(root_hub_dip, rdip, op, arg, result);
1282 }
1283
1284 /* decrement bus_ctls count */
1285 mutex_enter(HUBD_MUTEX(hubd));
1286 hubd->h_bus_ctls--;
1287 ASSERT(hubd->h_bus_ctls >= 0);
1288 mutex_exit(HUBD_MUTEX(hubd));
1289
1290 return (retval);
1291 }
1292
1293 /*
1294 * hubd_config_one:
1295 * enumerate one child according to 'port'
1296 */
1297
1298 static boolean_t
hubd_config_one(hubd_t * hubd,int port)1299 hubd_config_one(hubd_t *hubd, int port)
1300 {
1301 dev_info_t *hdip = hubd->h_dip;
1302 dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip;
1303 boolean_t online_child = B_FALSE, found = B_FALSE;
1304
1305 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
1306 "hubd_config_one: started, hubd_reset_port = 0x%x", port);
1307
1308 ndi_hold_devi(hdip); /* so we don't race with detach */
1309
1310 /*
1311 * this ensures one config activity per system at a time.
1312 * we enter the parent PCI node to have this serialization.
1313 * this also excludes ioctls and deathrow thread
1314 */
1315 ndi_devi_enter(ddi_get_parent(rh_dip));
1316 ndi_devi_enter(rh_dip);
1317
1318 /* exclude other threads */
1319 ndi_devi_enter(hdip);
1320 mutex_enter(HUBD_MUTEX(hubd));
1321
1322 hubd_pm_busy_component(hubd, hubd->h_dip, 0);
1323
1324 if (!hubd->h_children_dips[port]) {
1325 uint16_t status, change;
1326
1327 (void) hubd_determine_port_status(hubd, port,
1328 &status, &change, NULL, HUBD_ACK_ALL_CHANGES);
1329
1330 if (status & PORT_STATUS_CCS) {
1331 online_child |= (hubd_handle_port_connect(hubd,
1332 port) == USB_SUCCESS);
1333 found = online_child;
1334 }
1335 } else {
1336 found = B_TRUE;
1337 }
1338
1339 mutex_exit(HUBD_MUTEX(hubd));
1340
1341 ndi_devi_exit(hdip);
1342 ndi_devi_exit(rh_dip);
1343 ndi_devi_exit(ddi_get_parent(rh_dip));
1344
1345 if (online_child) {
1346 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
1347 "hubd_config_one: onlining child");
1348
1349 (void) ndi_devi_online(hubd->h_dip, 0);
1350 }
1351
1352 mutex_enter(HUBD_MUTEX(hubd));
1353
1354 (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
1355
1356 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
1357 "hubd_config_one: exit");
1358
1359 mutex_exit(HUBD_MUTEX(hubd));
1360
1361 ndi_rele_devi(hdip);
1362
1363 return (found);
1364 }
1365
1366 /*
1367 * bus enumeration entry points
1368 */
1369 static int
hubd_bus_config(dev_info_t * dip,uint_t flag,ddi_bus_config_op_t op,void * arg,dev_info_t ** child)1370 hubd_bus_config(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
1371 void *arg, dev_info_t **child)
1372 {
1373 hubd_t *hubd = hubd_get_soft_state(dip);
1374 int rval;
1375 long port;
1376
1377 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
1378 "hubd_bus_config: op=%d", op);
1379
1380 if (hubdi_bus_config_debug) {
1381 flag |= NDI_DEVI_DEBUG;
1382 }
1383
1384 if (op == BUS_CONFIG_ONE) {
1385 boolean_t found;
1386 char cname[80];
1387 char *name, *addr;
1388
1389 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
1390 "hubd_bus_config: op=%d (BUS_CONFIG_ONE)", op);
1391
1392 (void) snprintf(cname, 80, "%s", (char *)arg);
1393 /* split name into "name@addr" parts */
1394 i_ddi_parse_name(cname, &name, &addr, NULL);
1395 if (addr && *addr) {
1396 (void) ddi_strtol(addr, NULL, 16, &port);
1397 } else {
1398 return (NDI_FAILURE);
1399 }
1400
1401 found = hubd_config_one(hubd, port);
1402
1403 if (found == 0) {
1404 return (NDI_FAILURE);
1405 }
1406
1407 }
1408 ndi_devi_enter(hubd->h_dip);
1409 rval = ndi_busop_bus_config(dip, flag, op, arg, child, 0);
1410 ndi_devi_exit(hubd->h_dip);
1411
1412 return (rval);
1413 }
1414
1415
1416 static int
hubd_bus_unconfig(dev_info_t * dip,uint_t flag,ddi_bus_config_op_t op,void * arg)1417 hubd_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
1418 void *arg)
1419 {
1420 hubd_t *hubd = hubd_get_soft_state(dip);
1421 dev_info_t *cdip;
1422 usb_port_t port;
1423 int rval;
1424
1425 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
1426 "hubd_bus_unconfig: op=%d", op);
1427
1428 if (hubdi_bus_config_debug) {
1429 flag |= NDI_DEVI_DEBUG;
1430 }
1431
1432 if ((op == BUS_UNCONFIG_ALL) && (flag & NDI_AUTODETACH) == 0) {
1433 flag |= NDI_DEVI_REMOVE;
1434 }
1435
1436 /* serialize access */
1437 ndi_devi_enter(dip);
1438
1439 rval = ndi_busop_bus_unconfig(dip, flag, op, arg);
1440
1441 /* logically zap children's list */
1442 mutex_enter(HUBD_MUTEX(hubd));
1443 for (port = 1; port <= hubd->h_nports; port++) {
1444 hubd->h_port_state[port] |= HUBD_CHILD_ZAP;
1445 }
1446 mutex_exit(HUBD_MUTEX(hubd));
1447
1448 /* fill in what's left */
1449 for (cdip = ddi_get_child(dip); cdip;
1450 cdip = ddi_get_next_sibling(cdip)) {
1451 usba_device_t *usba_device = usba_get_usba_device(cdip);
1452
1453 if (usba_device == NULL) {
1454
1455 continue;
1456 }
1457 mutex_enter(HUBD_MUTEX(hubd));
1458 port = usba_device->usb_port;
1459 hubd->h_children_dips[port] = cdip;
1460 hubd->h_port_state[port] &= ~HUBD_CHILD_ZAP;
1461 mutex_exit(HUBD_MUTEX(hubd));
1462 }
1463
1464 /* physically zap the children we didn't find */
1465 mutex_enter(HUBD_MUTEX(hubd));
1466 for (port = 1; port <= hubd->h_nports; port++) {
1467 if (hubd->h_port_state[port] & HUBD_CHILD_ZAP) {
1468 /* zap the dip and usba_device structure as well */
1469 hubd_free_usba_device(hubd, hubd->h_usba_devices[port]);
1470 hubd->h_children_dips[port] = NULL;
1471 hubd->h_port_state[port] &= ~HUBD_CHILD_ZAP;
1472 }
1473 }
1474 mutex_exit(HUBD_MUTEX(hubd));
1475
1476 ndi_devi_exit(dip);
1477
1478 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
1479 "hubd_bus_unconfig: rval=%d", rval);
1480
1481 return (rval);
1482 }
1483
1484
1485 /* bus_power entry point */
1486 static int
hubd_bus_power(dev_info_t * dip,void * impl_arg,pm_bus_power_op_t op,void * arg,void * result)1487 hubd_bus_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op,
1488 void *arg, void *result)
1489 {
1490 hubd_t *hubd;
1491 int rval, pwrup_res;
1492 usb_port_t port;
1493 int retval = DDI_FAILURE;
1494 pm_bp_child_pwrchg_t *bpc;
1495 pm_bp_nexus_pwrup_t bpn;
1496
1497 hubd = hubd_get_soft_state(dip);
1498
1499 USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubd->h_log_handle,
1500 "hubd_bus_power: dip=%p, impl_arg=%p, power_op=%d, arg=%p, "
1501 "result=%d\n", (void *)dip, impl_arg, op, arg, *(int *)result);
1502
1503 bpc = (pm_bp_child_pwrchg_t *)arg;
1504
1505 mutex_enter(HUBD_MUTEX(hubd));
1506 hubd->h_bus_pwr++;
1507 mutex_exit(HUBD_MUTEX(hubd));
1508
1509 switch (op) {
1510 case BUS_POWER_PRE_NOTIFICATION:
1511 port = hubd_child_dip2port(hubd, bpc->bpc_dip);
1512 USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle,
1513 "hubd_bus_power: BUS_POWER_PRE_NOTIFICATION, port=%d",
1514 port);
1515
1516 /* go to full power if we are powered down */
1517 mutex_enter(HUBD_MUTEX(hubd));
1518
1519 /*
1520 * If this case completes normally, idle will be in
1521 * hubd_bus_power / BUS_POWER_POST_NOTIFICATION
1522 */
1523 hubd_pm_busy_component(hubd, dip, 0);
1524
1525 /*
1526 * raise power only if we have created the components
1527 * and are currently in low power
1528 */
1529 if ((hubd->h_dev_state == USB_DEV_PWRED_DOWN) &&
1530 hubd->h_hubpm->hubp_wakeup_enabled) {
1531 mutex_exit(HUBD_MUTEX(hubd));
1532
1533 bpn.bpn_comp = 0;
1534 bpn.bpn_dip = dip;
1535 bpn.bpn_level = USB_DEV_OS_FULL_PWR;
1536 bpn.bpn_private = bpc->bpc_private;
1537
1538 rval = pm_busop_bus_power(dip, impl_arg,
1539 BUS_POWER_NEXUS_PWRUP, (void *)&bpn,
1540 (void *)&pwrup_res);
1541
1542 if (rval != DDI_SUCCESS || pwrup_res != DDI_SUCCESS) {
1543 mutex_enter(HUBD_MUTEX(hubd));
1544 hubd_pm_idle_component(hubd, dip, 0);
1545 mutex_exit(HUBD_MUTEX(hubd));
1546
1547 break;
1548 }
1549 mutex_enter(HUBD_MUTEX(hubd));
1550 }
1551
1552 /* indicate that child is changing power level */
1553 hubd->h_port_state[port] |= HUBD_CHILD_PWRLVL_CHNG;
1554 mutex_exit(HUBD_MUTEX(hubd));
1555
1556 if ((bpc->bpc_olevel == 0) &&
1557 (bpc->bpc_nlevel > bpc->bpc_olevel)) {
1558 /*
1559 * this child is transitioning from power off
1560 * to power on state - resume port
1561 */
1562 rval = hubd_resume_port(hubd, port);
1563 if (rval == USB_SUCCESS) {
1564 retval = DDI_SUCCESS;
1565 } else {
1566 /* reset this flag on failure */
1567 mutex_enter(HUBD_MUTEX(hubd));
1568 hubd->h_port_state[port] &=
1569 ~HUBD_CHILD_PWRLVL_CHNG;
1570 hubd_pm_idle_component(hubd, dip, 0);
1571 mutex_exit(HUBD_MUTEX(hubd));
1572 }
1573 } else {
1574 retval = DDI_SUCCESS;
1575 }
1576
1577 break;
1578 case BUS_POWER_POST_NOTIFICATION:
1579 port = hubd_child_dip2port(hubd, bpc->bpc_dip);
1580 USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle,
1581 "hubd_bus_power: BUS_POWER_POST_NOTIFICATION, port=%d",
1582 port);
1583
1584 mutex_enter(HUBD_MUTEX(hubd));
1585 hubd->h_port_state[port] &= ~HUBD_CHILD_PWRLVL_CHNG;
1586 mutex_exit(HUBD_MUTEX(hubd));
1587
1588 /* record child's pwr and suspend port if required */
1589 rval = hubd_post_power(hubd, port, bpc, *(int *)result);
1590 if (rval == USB_SUCCESS) {
1591
1592 retval = DDI_SUCCESS;
1593 }
1594
1595 mutex_enter(HUBD_MUTEX(hubd));
1596
1597 /*
1598 * Matching idle for the busy in
1599 * hubd_bus_power / BUS_POWER_PRE_NOTIFICATION
1600 */
1601 hubd_pm_idle_component(hubd, dip, 0);
1602
1603 mutex_exit(HUBD_MUTEX(hubd));
1604
1605 break;
1606 default:
1607 retval = pm_busop_bus_power(dip, impl_arg, op, arg, result);
1608
1609 break;
1610 }
1611
1612 mutex_enter(HUBD_MUTEX(hubd));
1613 hubd->h_bus_pwr--;
1614 mutex_exit(HUBD_MUTEX(hubd));
1615
1616 return (retval);
1617 }
1618
1619
1620 /*
1621 * functions to handle power transition for OS levels 0 -> 3
1622 */
1623 static int
hubd_pwrlvl0(hubd_t * hubd)1624 hubd_pwrlvl0(hubd_t *hubd)
1625 {
1626 hub_power_t *hubpm;
1627
1628 /* We can't power down if hotplug thread is running */
1629 if (hubd->h_hotplug_thread || hubd->h_hubpm->hubp_busy_pm ||
1630 (hubd_can_suspend(hubd) == USB_FAILURE)) {
1631
1632 return (USB_FAILURE);
1633 }
1634
1635 switch (hubd->h_dev_state) {
1636 case USB_DEV_ONLINE:
1637 hubpm = hubd->h_hubpm;
1638
1639 /*
1640 * To avoid race with bus_power pre_notify on check over
1641 * dev_state, we need to correctly set the dev state
1642 * before the mutex is dropped in stop polling.
1643 */
1644 hubd->h_dev_state = USB_DEV_PWRED_DOWN;
1645 hubpm->hubp_current_power = USB_DEV_OS_PWR_OFF;
1646
1647 /*
1648 * if we are the root hub, do not stop polling
1649 * otherwise, we will never see a resume
1650 */
1651 if (usba_is_root_hub(hubd->h_dip)) {
1652 /* place holder to implement Global Suspend */
1653 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
1654 "Global Suspend: Not Yet Implemented");
1655 } else {
1656 hubd_stop_polling(hubd);
1657 }
1658
1659 /* Issue USB D3 command to the device here */
1660 (void) usb_set_device_pwrlvl3(hubd->h_dip);
1661
1662 break;
1663 case USB_DEV_DISCONNECTED:
1664 case USB_DEV_SUSPENDED:
1665 case USB_DEV_PWRED_DOWN:
1666 default:
1667
1668 break;
1669 }
1670
1671 return (USB_SUCCESS);
1672 }
1673
1674
1675 /* ARGSUSED */
1676 static int
hubd_pwrlvl1(hubd_t * hubd)1677 hubd_pwrlvl1(hubd_t *hubd)
1678 {
1679 /* Issue USB D2 command to the device here */
1680 (void) usb_set_device_pwrlvl2(hubd->h_dip);
1681
1682 return (USB_FAILURE);
1683 }
1684
1685
1686 /* ARGSUSED */
1687 static int
hubd_pwrlvl2(hubd_t * hubd)1688 hubd_pwrlvl2(hubd_t *hubd)
1689 {
1690 /* Issue USB D1 command to the device here */
1691 (void) usb_set_device_pwrlvl1(hubd->h_dip);
1692
1693 return (USB_FAILURE);
1694 }
1695
1696
1697 static int
hubd_pwrlvl3(hubd_t * hubd)1698 hubd_pwrlvl3(hubd_t *hubd)
1699 {
1700 hub_power_t *hubpm;
1701 int rval;
1702
1703 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, "hubd_pwrlvl3");
1704
1705 hubpm = hubd->h_hubpm;
1706 switch (hubd->h_dev_state) {
1707 case USB_DEV_PWRED_DOWN:
1708 ASSERT(hubpm->hubp_current_power == USB_DEV_OS_PWR_OFF);
1709 if (usba_is_root_hub(hubd->h_dip)) {
1710 /* implement global resume here */
1711 USB_DPRINTF_L2(DPRINT_MASK_PM,
1712 hubd->h_log_handle,
1713 "Global Resume: Not Yet Implemented");
1714 }
1715 /* Issue USB D0 command to the device here */
1716 rval = usb_set_device_pwrlvl0(hubd->h_dip);
1717 ASSERT(rval == USB_SUCCESS);
1718 hubd->h_dev_state = USB_DEV_ONLINE;
1719 hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
1720 hubpm->hubp_time_at_full_power = gethrtime();
1721 hubd_start_polling(hubd, 0);
1722
1723 /* FALLTHRU */
1724 case USB_DEV_ONLINE:
1725 /* we are already in full power */
1726
1727 /* FALLTHRU */
1728 case USB_DEV_DISCONNECTED:
1729 case USB_DEV_SUSPENDED:
1730 /*
1731 * PM framework tries to put you in full power
1732 * during system shutdown. If we are disconnected
1733 * return success. Also, we should not change state
1734 * when we are disconnected or suspended or about to
1735 * transition to that state
1736 */
1737
1738 return (USB_SUCCESS);
1739 default:
1740 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
1741 "hubd_pwrlvl3: Illegal dev_state=%d", hubd->h_dev_state);
1742
1743 return (USB_FAILURE);
1744 }
1745 }
1746
1747
1748 /* power entry point */
1749 /* ARGSUSED */
1750 int
usba_hubdi_power(dev_info_t * dip,int comp,int level)1751 usba_hubdi_power(dev_info_t *dip, int comp, int level)
1752 {
1753 hubd_t *hubd;
1754 hub_power_t *hubpm;
1755 int retval;
1756
1757 hubd = hubd_get_soft_state(dip);
1758 USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle,
1759 "usba_hubdi_power: level=%d", level);
1760
1761 ndi_devi_enter(dip);
1762
1763 mutex_enter(HUBD_MUTEX(hubd));
1764 hubpm = hubd->h_hubpm;
1765
1766 /* check if we are transitioning to a legal power level */
1767 if (USB_DEV_PWRSTATE_OK(hubpm->hubp_pwr_states, level)) {
1768 USB_DPRINTF_L2(DPRINT_MASK_HUBDI, hubd->h_log_handle,
1769 "usba_hubdi_power: illegal power level=%d "
1770 "hubp_pwr_states=0x%x", level, hubpm->hubp_pwr_states);
1771 mutex_exit(HUBD_MUTEX(hubd));
1772
1773 ndi_devi_exit(dip);
1774
1775 return (DDI_FAILURE);
1776 }
1777
1778 switch (level) {
1779 case USB_DEV_OS_PWR_OFF:
1780 retval = hubd_pwrlvl0(hubd);
1781
1782 break;
1783 case USB_DEV_OS_PWR_1:
1784 retval = hubd_pwrlvl1(hubd);
1785
1786 break;
1787 case USB_DEV_OS_PWR_2:
1788 retval = hubd_pwrlvl2(hubd);
1789
1790 break;
1791 case USB_DEV_OS_FULL_PWR:
1792 retval = hubd_pwrlvl3(hubd);
1793
1794 break;
1795 default:
1796 retval = USB_FAILURE;
1797
1798 break;
1799 }
1800 mutex_exit(HUBD_MUTEX(hubd));
1801
1802 ndi_devi_exit(dip);
1803
1804 return ((retval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
1805 }
1806
1807
1808 /* power entry point for the root hub */
1809 int
usba_hubdi_root_hub_power(dev_info_t * dip,int comp,int level)1810 usba_hubdi_root_hub_power(dev_info_t *dip, int comp, int level)
1811 {
1812 return (usba_hubdi_power(dip, comp, level));
1813 }
1814
1815
1816 /*
1817 * standard driver entry points support code
1818 */
1819 int
usba_hubdi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)1820 usba_hubdi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1821 {
1822 int instance = ddi_get_instance(dip);
1823 hubd_t *hubd = NULL;
1824 int i, rval;
1825 int minor;
1826 uint8_t ports_count;
1827 char *log_name = NULL;
1828 const char *root_hub_drvname;
1829 usb_ep_data_t *ep_data;
1830 usba_device_t *child_ud = NULL;
1831 usb_dev_descr_t *usb_dev_descr;
1832 usb_port_status_t parent_port_status, child_port_status;
1833
1834 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubdi_log_handle,
1835 "hubd_attach instance %d, cmd=0x%x", instance, cmd);
1836
1837 switch (cmd) {
1838 case DDI_ATTACH:
1839
1840 break;
1841 case DDI_RESUME:
1842 hubd_cpr_resume(dip);
1843
1844 return (DDI_SUCCESS);
1845 default:
1846 return (DDI_FAILURE);
1847 }
1848
1849 /*
1850 * Allocate softc information.
1851 */
1852 if (usba_is_root_hub(dip)) {
1853 /* soft state has already been allocated */
1854 hubd = hubd_get_soft_state(dip);
1855 minor = HUBD_IS_ROOT_HUB;
1856
1857 /* generate readable labels for different root hubs */
1858 root_hub_drvname = ddi_driver_name(dip);
1859 if (strcmp(root_hub_drvname, "xhci") == 0) {
1860 log_name = "xusb";
1861 } else if (strcmp(root_hub_drvname, "ehci") == 0) {
1862 log_name = "eusb";
1863 } else if (strcmp(root_hub_drvname, "uhci") == 0) {
1864 log_name = "uusb";
1865 } else {
1866 /* std. for ohci */
1867 log_name = "usb";
1868 }
1869 } else {
1870 rval = ddi_soft_state_zalloc(hubd_statep, instance);
1871 minor = 0;
1872
1873 if (rval != DDI_SUCCESS) {
1874 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
1875 "cannot allocate soft state (%d)", instance);
1876 goto fail;
1877 }
1878
1879 hubd = hubd_get_soft_state(dip);
1880 if (hubd == NULL) {
1881 goto fail;
1882 }
1883 }
1884
1885 hubd->h_log_handle = usb_alloc_log_hdl(dip, log_name, &hubd_errlevel,
1886 &hubd_errmask, &hubd_instance_debug, 0);
1887
1888 hubd->h_usba_device = child_ud = usba_get_usba_device(dip);
1889 hubd->h_dip = dip;
1890 hubd->h_instance = instance;
1891
1892 mutex_enter(&child_ud->usb_mutex);
1893 child_port_status = child_ud->usb_port_status;
1894 usb_dev_descr = child_ud->usb_dev_descr;
1895 parent_port_status = (child_ud->usb_hs_hub_usba_dev) ?
1896 child_ud->usb_hs_hub_usba_dev->usb_port_status : 0;
1897 mutex_exit(&child_ud->usb_mutex);
1898
1899 if ((child_port_status == USBA_FULL_SPEED_DEV) &&
1900 (parent_port_status >= USBA_HIGH_SPEED_DEV) &&
1901 (usb_dev_descr->bcdUSB == 0x100)) {
1902 USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubd->h_log_handle,
1903 "Use of a USB1.0 hub behind a higher speed port may "
1904 "cause unexpected failures");
1905 }
1906
1907 hubd->h_pipe_policy.pp_max_async_reqs = 1;
1908
1909 /* register with USBA as client driver */
1910 if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) {
1911 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
1912 "client attach failed");
1913
1914 goto fail;
1915 }
1916
1917 if (usb_get_dev_data(dip, &hubd->h_dev_data,
1918 USB_PARSE_LVL_IF, 0) != USB_SUCCESS) {
1919 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
1920 "cannot get dev_data");
1921
1922 goto fail;
1923 }
1924
1925 if ((ep_data = usb_lookup_ep_data(dip, hubd->h_dev_data,
1926 hubd->h_dev_data->dev_curr_if, 0, 0,
1927 (uint_t)USB_EP_ATTR_INTR, (uint_t)USB_EP_DIR_IN)) == NULL) {
1928 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
1929 "no interrupt IN endpoint found");
1930
1931 goto fail;
1932 }
1933
1934 if (usb_ep_xdescr_fill(USB_EP_XDESCR_CURRENT_VERSION, dip, ep_data,
1935 &hubd->h_ep1_xdescr) != USB_SUCCESS) {
1936 goto fail;
1937 }
1938
1939 hubd->h_default_pipe = hubd->h_dev_data->dev_default_ph;
1940
1941 mutex_init(HUBD_MUTEX(hubd), NULL, MUTEX_DRIVER,
1942 hubd->h_dev_data->dev_iblock_cookie);
1943 cv_init(&hubd->h_cv_reset_port, NULL, CV_DRIVER, NULL);
1944 cv_init(&hubd->h_cv_hotplug_dev, NULL, CV_DRIVER, NULL);
1945
1946 hubd->h_init_state |= HUBD_LOCKS_DONE;
1947
1948 usb_free_descr_tree(dip, hubd->h_dev_data);
1949
1950 /*
1951 * register this hub instance with usba
1952 */
1953 rval = usba_hubdi_register(dip, 0);
1954 if (rval != USB_SUCCESS) {
1955 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
1956 "usba_hubdi_register failed");
1957 goto fail;
1958 }
1959
1960 mutex_enter(HUBD_MUTEX(hubd));
1961 hubd->h_init_state |= HUBD_HUBDI_REGISTERED;
1962 hubd->h_dev_state = USB_DEV_ONLINE;
1963 mutex_exit(HUBD_MUTEX(hubd));
1964
1965 /* now create components to power manage this device */
1966 hubd_create_pm_components(dip, hubd);
1967
1968 /*
1969 * Event handling: definition and registration
1970 *
1971 * first the definition:
1972 * get event handle
1973 */
1974 (void) ndi_event_alloc_hdl(dip, 0, &hubd->h_ndi_event_hdl, NDI_SLEEP);
1975
1976 /* bind event set to the handle */
1977 if (ndi_event_bind_set(hubd->h_ndi_event_hdl, &hubd_ndi_events,
1978 NDI_SLEEP)) {
1979 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
1980 "binding event set failed");
1981
1982 goto fail;
1983 }
1984
1985 /* event registration */
1986 if (hubd_register_events(hubd) != USB_SUCCESS) {
1987 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
1988 "hubd_register_events failed");
1989
1990 goto fail;
1991 }
1992
1993 mutex_enter(HUBD_MUTEX(hubd));
1994 hubd->h_init_state |= HUBD_EVENTS_REGISTERED;
1995
1996 if (hubd_get_hub_descriptor(hubd) != USB_SUCCESS) {
1997 mutex_exit(HUBD_MUTEX(hubd));
1998
1999 goto fail;
2000 }
2001
2002 /*
2003 * Now that we have our descriptors, we need to give the host controller
2004 * a chance to properly configure the device if needed (this is required
2005 * for xHCI).
2006 *
2007 * Note, if anyone ever adds support for using the Multi TT mode for USB
2008 * 2 High speed Hubs, the xhci driver will need to be updated and that
2009 * will need to be done before this is called.
2010 */
2011 if (hubd->h_usba_device->usb_hcdi_ops->usba_hcdi_hub_update != NULL &&
2012 !usba_is_root_hub(dip)) {
2013 int ret;
2014 uint8_t chars;
2015 usba_device_t *ud = hubd->h_usba_device;
2016
2017 chars = (hubd->h_hub_chars & HUB_CHARS_TT_THINK_TIME) >>
2018 HUB_CHARS_TT_SHIFT;
2019 ret = ud->usb_hcdi_ops->usba_hcdi_hub_update(ud,
2020 hubd->h_nports, chars);
2021 if (ret != USB_SUCCESS) {
2022 mutex_exit(HUBD_MUTEX(hubd));
2023 goto fail;
2024 }
2025 }
2026
2027 if (hubd_set_hub_depth(hubd) != USB_SUCCESS) {
2028
2029 goto fail;
2030 }
2031
2032 if (ddi_prop_exists(DDI_DEV_T_ANY, dip,
2033 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
2034 "hub-ignore-power-budget") == 1) {
2035 hubd->h_ignore_pwr_budget = B_TRUE;
2036 } else {
2037 hubd->h_ignore_pwr_budget = B_FALSE;
2038
2039 /* initialize hub power budget variables */
2040 if (hubd_init_power_budget(hubd) != USB_SUCCESS) {
2041 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
2042 "hubd_init_power_budget failed");
2043 mutex_exit(HUBD_MUTEX(hubd));
2044
2045 goto fail;
2046 }
2047 }
2048
2049 /* initialize and create children */
2050 if (hubd_check_ports(hubd) != USB_SUCCESS) {
2051 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
2052 "hubd_check_ports failed");
2053 mutex_exit(HUBD_MUTEX(hubd));
2054
2055 goto fail;
2056 }
2057
2058 /*
2059 * create cfgadm nodes
2060 */
2061 hubd->h_ancestry_str = (char *)kmem_zalloc(HUBD_APID_NAMELEN, KM_SLEEP);
2062 hubd_get_ancestry_str(hubd);
2063
2064 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
2065 "#ports=0x%x", hubd->h_nports);
2066
2067 for (i = 1; i <= hubd->h_nports; i++) {
2068 char ap_name[HUBD_APID_NAMELEN];
2069
2070 (void) snprintf(ap_name, HUBD_APID_NAMELEN, "%s%d",
2071 hubd->h_ancestry_str, i);
2072 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
2073 "ap_name=%s", ap_name);
2074
2075 if (ddi_create_minor_node(dip, ap_name, S_IFCHR, instance,
2076 DDI_NT_USB_ATTACHMENT_POINT, 0) != DDI_SUCCESS) {
2077 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
2078 "cannot create attachment point node (%d)",
2079 instance);
2080 mutex_exit(HUBD_MUTEX(hubd));
2081
2082 goto fail;
2083 }
2084 }
2085
2086 ports_count = hubd->h_nports;
2087 mutex_exit(HUBD_MUTEX(hubd));
2088
2089 /* create minor nodes */
2090 if (ddi_create_minor_node(dip, "hubd", S_IFCHR,
2091 instance | minor, DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
2092
2093 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
2094 "cannot create devctl minor node (%d)", instance);
2095
2096 goto fail;
2097 }
2098
2099 mutex_enter(HUBD_MUTEX(hubd));
2100 hubd->h_init_state |= HUBD_MINOR_NODE_CREATED;
2101 mutex_exit(HUBD_MUTEX(hubd));
2102
2103 if (ndi_prop_update_int(DDI_DEV_T_NONE, dip,
2104 "usb-port-count", ports_count) != DDI_PROP_SUCCESS) {
2105 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
2106 "usb-port-count update failed");
2107 }
2108
2109 /*
2110 * host controller driver has already reported this dev
2111 * if we are the root hub
2112 */
2113 if (!usba_is_root_hub(dip)) {
2114 ddi_report_dev(dip);
2115 }
2116
2117 /* enable deathrow thread */
2118 hubd->h_cleanup_enabled = B_TRUE;
2119 mutex_enter(HUBD_MUTEX(hubd));
2120 hubd_pm_idle_component(hubd, dip, 0);
2121 mutex_exit(HUBD_MUTEX(hubd));
2122
2123 return (DDI_SUCCESS);
2124
2125 fail:
2126 {
2127 char *pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
2128
2129 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
2130 "cannot attach %s", ddi_pathname(dip, pathname));
2131
2132 kmem_free(pathname, MAXPATHLEN);
2133 }
2134
2135 if (hubd != NULL) {
2136 mutex_enter(HUBD_MUTEX(hubd));
2137 hubd_pm_idle_component(hubd, dip, 0);
2138 mutex_exit(HUBD_MUTEX(hubd));
2139
2140 rval = hubd_cleanup(dip, hubd);
2141 if (rval != USB_SUCCESS) {
2142 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
2143 "failure to complete cleanup after attach failure");
2144 }
2145 }
2146
2147 return (DDI_FAILURE);
2148 }
2149
2150
2151 int
usba_hubdi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)2152 usba_hubdi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2153 {
2154 hubd_t *hubd = hubd_get_soft_state(dip);
2155 int rval;
2156
2157 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
2158 "hubd_detach: cmd=0x%x", cmd);
2159
2160 switch (cmd) {
2161 case DDI_DETACH:
2162 rval = hubd_cleanup(dip, hubd);
2163
2164 return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
2165 case DDI_SUSPEND:
2166 rval = hubd_cpr_suspend(hubd);
2167
2168 return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
2169 default:
2170 return (DDI_FAILURE);
2171 }
2172 }
2173
2174
2175 /*
2176 * hubd_setdevaddr
2177 * set the device addrs on this port
2178 */
2179 static int
hubd_setdevaddr(hubd_t * hubd,usb_port_t port)2180 hubd_setdevaddr(hubd_t *hubd, usb_port_t port)
2181 {
2182 int rval = USB_FAILURE;
2183 usb_cr_t completion_reason;
2184 usb_cb_flags_t cb_flags;
2185 usb_pipe_handle_t ph;
2186 dev_info_t *child_dip = NULL;
2187 uchar_t address = 0;
2188 usba_device_t *usba_device;
2189 int retry = 0;
2190 long time_delay;
2191
2192 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
2193 "hubd_setdevaddr: port=%d", port);
2194
2195 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
2196
2197 child_dip = hubd->h_children_dips[port];
2198 address = hubd->h_usba_devices[port]->usb_addr;
2199 usba_device = hubd->h_usba_devices[port];
2200
2201 /* close the default pipe with addr x */
2202 mutex_exit(HUBD_MUTEX(hubd));
2203 ph = usba_get_dflt_pipe_handle(child_dip);
2204 usb_pipe_close(child_dip, ph,
2205 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
2206 mutex_enter(HUBD_MUTEX(hubd));
2207
2208 /*
2209 * If the host controller is in charge of addressing, have it do that
2210 * now and skip everything else.
2211 */
2212 if (usba_device->usb_hcdi_ops->usba_hcdi_device_address != NULL) {
2213 mutex_exit(HUBD_MUTEX(hubd));
2214 rval = usba_device->usb_hcdi_ops->usba_hcdi_device_address(
2215 usba_device);
2216 mutex_enter(HUBD_MUTEX(hubd));
2217
2218 usba_clear_data_toggle(usba_device);
2219 return (rval);
2220 }
2221
2222 /*
2223 * As this device has been reset, temporarily
2224 * assign the default address
2225 */
2226 mutex_enter(&usba_device->usb_mutex);
2227 address = usba_device->usb_addr;
2228 usba_device->usb_addr = USBA_DEFAULT_ADDR;
2229 mutex_exit(&usba_device->usb_mutex);
2230
2231 mutex_exit(HUBD_MUTEX(hubd));
2232
2233 time_delay = drv_usectohz(hubd_device_delay / 20);
2234 for (retry = 0; retry < hubd_retry_enumerate; retry++) {
2235
2236 /* open child's default pipe with USBA_DEFAULT_ADDR */
2237 if ((rval = usb_pipe_open(child_dip, NULL, NULL,
2238 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) !=
2239 USB_SUCCESS) {
2240 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
2241 "hubd_setdevaddr: Unable to open default pipe");
2242
2243 break;
2244 }
2245
2246 /* Set the address of the device */
2247 if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
2248 USB_DEV_REQ_HOST_TO_DEV,
2249 USB_REQ_SET_ADDRESS, /* bRequest */
2250 address, /* wValue */
2251 0, /* wIndex */
2252 0, /* wLength */
2253 NULL, 0,
2254 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
2255 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
2256 "hubd_setdevaddr(%d): rval=%d cr=%d cb_fl=0x%x",
2257 retry, rval, completion_reason, cb_flags);
2258 }
2259
2260 usb_pipe_close(child_dip, ph,
2261 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
2262
2263 if (rval == USB_SUCCESS) {
2264
2265 break;
2266 }
2267
2268 delay(time_delay);
2269 }
2270
2271 /* Reset to the old address */
2272 mutex_enter(&usba_device->usb_mutex);
2273 usba_device->usb_addr = address;
2274 mutex_exit(&usba_device->usb_mutex);
2275 mutex_enter(HUBD_MUTEX(hubd));
2276
2277 usba_clear_data_toggle(usba_device);
2278
2279 return (rval);
2280 }
2281
2282
2283 /*
2284 * hubd_setdevconfig
2285 * set the device addrs on this port
2286 */
2287 static void
hubd_setdevconfig(hubd_t * hubd,usb_port_t port)2288 hubd_setdevconfig(hubd_t *hubd, usb_port_t port)
2289 {
2290 int rval;
2291 usb_cr_t completion_reason;
2292 usb_cb_flags_t cb_flags;
2293 usb_pipe_handle_t ph;
2294 dev_info_t *child_dip = NULL;
2295 usba_device_t *usba_device = NULL;
2296 uint16_t config_value;
2297
2298 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
2299 "hubd_setdevconfig: port=%d", port);
2300
2301 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
2302
2303 child_dip = hubd->h_children_dips[port];
2304 usba_device = hubd->h_usba_devices[port];
2305 config_value = hubd->h_usba_devices[port]->usb_cfg_value;
2306 mutex_exit(HUBD_MUTEX(hubd));
2307
2308 /* open the default control pipe */
2309 if ((rval = usb_pipe_open(child_dip, NULL, NULL,
2310 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) ==
2311 USB_SUCCESS) {
2312
2313 /* Set the default configuration of the device */
2314 if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
2315 USB_DEV_REQ_HOST_TO_DEV,
2316 USB_REQ_SET_CFG, /* bRequest */
2317 config_value, /* wValue */
2318 0, /* wIndex */
2319 0, /* wLength */
2320 NULL, 0,
2321 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
2322 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
2323 "hubd_setdevconfig: set device config failed: "
2324 "cr=%d cb_fl=0x%x rval=%d",
2325 completion_reason, cb_flags, rval);
2326 }
2327 /*
2328 * After setting the configuration, we make this default
2329 * control pipe persistent, so that it gets re-opened
2330 * on posting a connect event
2331 */
2332 usba_persistent_pipe_close(usba_device);
2333 } else {
2334 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
2335 "pipe open fails: rval=%d", rval);
2336 }
2337 mutex_enter(HUBD_MUTEX(hubd));
2338 }
2339
2340
2341 /*ARGSUSED*/
2342 static int
hubd_check_disconnected_ports(dev_info_t * dip,void * arg)2343 hubd_check_disconnected_ports(dev_info_t *dip, void *arg)
2344 {
2345 usb_port_t port;
2346 hubd_t *hubd;
2347 major_t hub_major = ddi_name_to_major("hubd");
2348 major_t usbmid_major = ddi_name_to_major("usb_mid");
2349
2350 /*
2351 * make sure dip is a usb hub, major of root hub is HCD
2352 * major
2353 */
2354 if (!usba_is_root_hub(dip)) {
2355 if (ddi_driver_major(dip) == usbmid_major) {
2356 /*
2357 * need to walk the children since it might be a
2358 * HWA device
2359 */
2360
2361 return (DDI_WALK_CONTINUE);
2362 }
2363
2364 if ((ddi_driver_major(dip) != hub_major) ||
2365 !i_ddi_devi_attached(dip)) {
2366 return (DDI_WALK_PRUNECHILD);
2367 }
2368 }
2369
2370 hubd = hubd_get_soft_state(dip);
2371 if (hubd == NULL) {
2372
2373 return (DDI_WALK_PRUNECHILD);
2374 }
2375
2376 /* walk child list and remove nodes with flag DEVI_DEVICE_REMOVED */
2377 ndi_devi_enter(dip);
2378
2379 /* for normal usb hub or root hub */
2380 mutex_enter(HUBD_MUTEX(hubd));
2381 for (port = 1; port <= hubd->h_nports; port++) {
2382 dev_info_t *cdip = hubd->h_children_dips[port];
2383
2384 if (cdip == NULL || DEVI_IS_DEVICE_REMOVED(cdip) == 0) {
2385 continue;
2386 }
2387
2388 (void) hubd_delete_child(hubd, port, NDI_DEVI_REMOVE,
2389 B_TRUE);
2390 }
2391 mutex_exit(HUBD_MUTEX(hubd));
2392
2393 ndi_devi_exit(dip);
2394
2395 /* skip siblings of root hub */
2396 if (usba_is_root_hub(dip)) {
2397
2398 return (DDI_WALK_PRUNESIB);
2399 }
2400
2401 return (DDI_WALK_CONTINUE);
2402 }
2403
2404
2405 /*
2406 * this thread will walk all children under the root hub for this
2407 * USB bus instance and attempt to remove them
2408 */
2409 static void
hubd_root_hub_cleanup_thread(void * arg)2410 hubd_root_hub_cleanup_thread(void *arg)
2411 {
2412 hubd_t *root_hubd = (hubd_t *)arg;
2413 dev_info_t *rh_dip = root_hubd->h_dip;
2414 #ifndef __lock_lint
2415 callb_cpr_t cprinfo;
2416
2417 CALLB_CPR_INIT(&cprinfo, HUBD_MUTEX(root_hubd), callb_generic_cpr,
2418 "USB root hub");
2419 #endif
2420
2421 for (;;) {
2422 /* don't race with detach */
2423 ndi_hold_devi(rh_dip);
2424
2425 mutex_enter(HUBD_MUTEX(root_hubd));
2426 root_hubd->h_cleanup_needed = 0;
2427 mutex_exit(HUBD_MUTEX(root_hubd));
2428
2429 (void) devfs_clean(rh_dip, NULL, 0);
2430
2431 ndi_devi_enter(ddi_get_parent(rh_dip));
2432 ddi_walk_devs(rh_dip, hubd_check_disconnected_ports,
2433 NULL);
2434 #ifdef __lock_lint
2435 (void) hubd_check_disconnected_ports(rh_dip, NULL);
2436 #endif
2437 ndi_devi_exit(ddi_get_parent(rh_dip));
2438
2439 /* quit if we are not enabled anymore */
2440 mutex_enter(HUBD_MUTEX(root_hubd));
2441 if ((root_hubd->h_cleanup_enabled == B_FALSE) ||
2442 (root_hubd->h_cleanup_needed == B_FALSE)) {
2443 root_hubd->h_cleanup_active = B_FALSE;
2444 mutex_exit(HUBD_MUTEX(root_hubd));
2445 ndi_rele_devi(rh_dip);
2446
2447 break;
2448 }
2449 mutex_exit(HUBD_MUTEX(root_hubd));
2450 ndi_rele_devi(rh_dip);
2451
2452 #ifndef __lock_lint
2453 mutex_enter(HUBD_MUTEX(root_hubd));
2454 CALLB_CPR_SAFE_BEGIN(&cprinfo);
2455 mutex_exit(HUBD_MUTEX(root_hubd));
2456
2457 delay(drv_usectohz(hubd_dip_cleanup_delay));
2458
2459 mutex_enter(HUBD_MUTEX(root_hubd));
2460 CALLB_CPR_SAFE_END(&cprinfo, HUBD_MUTEX(root_hubd));
2461 mutex_exit(HUBD_MUTEX(root_hubd));
2462 #endif
2463 }
2464
2465 #ifndef __lock_lint
2466 mutex_enter(HUBD_MUTEX(root_hubd));
2467 CALLB_CPR_EXIT(&cprinfo);
2468 #endif
2469 }
2470
2471
2472 void
hubd_schedule_cleanup(dev_info_t * rh_dip)2473 hubd_schedule_cleanup(dev_info_t *rh_dip)
2474 {
2475 hubd_t *root_hubd;
2476
2477 /*
2478 * The usb_root_hub_dip pointer for the child hub of the WUSB
2479 * wire adapter class device points to the wire adapter, not
2480 * the root hub. Need to find the real root hub dip so that
2481 * the cleanup thread only starts from the root hub.
2482 */
2483 while (!usba_is_root_hub(rh_dip)) {
2484 root_hubd = hubd_get_soft_state(rh_dip);
2485 if (root_hubd != NULL) {
2486 rh_dip = root_hubd->h_usba_device->usb_root_hub_dip;
2487 if (rh_dip == NULL) {
2488 USB_DPRINTF_L2(DPRINT_MASK_ATTA,
2489 root_hubd->h_log_handle,
2490 "hubd_schedule_cleanup: null rh dip");
2491
2492 return;
2493 }
2494 } else {
2495 USB_DPRINTF_L2(DPRINT_MASK_ATTA,
2496 root_hubd->h_log_handle,
2497 "hubd_schedule_cleanup: cannot find root hub");
2498
2499 return;
2500 }
2501 }
2502 root_hubd = hubd_get_soft_state(rh_dip);
2503
2504 mutex_enter(HUBD_MUTEX(root_hubd));
2505 root_hubd->h_cleanup_needed = B_TRUE;
2506 if (root_hubd->h_cleanup_enabled && !(root_hubd->h_cleanup_active)) {
2507 root_hubd->h_cleanup_active = B_TRUE;
2508 mutex_exit(HUBD_MUTEX(root_hubd));
2509 (void) thread_create(NULL, 0,
2510 hubd_root_hub_cleanup_thread,
2511 (void *)root_hubd, 0, &p0, TS_RUN,
2512 minclsyspri);
2513 } else {
2514 mutex_exit(HUBD_MUTEX(root_hubd));
2515 }
2516 }
2517
2518
2519 /*
2520 * hubd_restore_device_state:
2521 * - set config for the hub
2522 * - power cycle all the ports
2523 * - for each port that was connected
2524 * - reset port
2525 * - assign addrs to the device on this port
2526 * - restart polling
2527 * - reset suspend flag
2528 */
2529 static void
hubd_restore_device_state(dev_info_t * dip,hubd_t * hubd)2530 hubd_restore_device_state(dev_info_t *dip, hubd_t *hubd)
2531 {
2532 int rval;
2533 int retry;
2534 uint_t hub_prev_state;
2535 usb_port_t port;
2536 uint16_t status;
2537 uint16_t change;
2538 dev_info_t *ch_dip;
2539 boolean_t ehci_root_hub;
2540
2541 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
2542 "hubd_restore_device_state:");
2543
2544 mutex_enter(HUBD_MUTEX(hubd));
2545 hub_prev_state = hubd->h_dev_state;
2546 ASSERT(hub_prev_state != USB_DEV_PWRED_DOWN);
2547
2548 /* First bring the device to full power */
2549 (void) hubd_pm_busy_component(hubd, dip, 0);
2550 mutex_exit(HUBD_MUTEX(hubd));
2551
2552 (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
2553
2554 if (!usba_is_root_hub(dip) &&
2555 (usb_check_same_device(dip, hubd->h_log_handle, USB_LOG_L0,
2556 DPRINT_MASK_HOTPLUG,
2557 USB_CHK_BASIC|USB_CHK_CFG, NULL) != USB_SUCCESS)) {
2558
2559 /* change the device state to disconnected */
2560 mutex_enter(HUBD_MUTEX(hubd));
2561 hubd->h_dev_state = USB_DEV_DISCONNECTED;
2562 (void) hubd_pm_idle_component(hubd, dip, 0);
2563 mutex_exit(HUBD_MUTEX(hubd));
2564
2565 return;
2566 }
2567
2568 ehci_root_hub = (strcmp(ddi_driver_name(dip), "ehci") == 0);
2569
2570 mutex_enter(HUBD_MUTEX(hubd));
2571 /* First turn off all port power */
2572 rval = hubd_disable_all_port_power(hubd);
2573 if (rval != USB_SUCCESS) {
2574 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
2575 "hubd_restore_device_state:"
2576 "turning off port power failed");
2577 }
2578
2579 /* Settling time before turning on again */
2580 mutex_exit(HUBD_MUTEX(hubd));
2581 delay(drv_usectohz(hubd_device_delay / 100));
2582 mutex_enter(HUBD_MUTEX(hubd));
2583
2584 /* enable power on all ports so we can see connects */
2585 if (hubd_enable_all_port_power(hubd) != USB_SUCCESS) {
2586 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
2587 "hubd_restore_device_state: turn on port power failed");
2588
2589 /* disable whatever was enabled */
2590 (void) hubd_disable_all_port_power(hubd);
2591
2592 (void) hubd_pm_idle_component(hubd, dip, 0);
2593 mutex_exit(HUBD_MUTEX(hubd));
2594
2595 return;
2596 }
2597
2598 /*
2599 * wait at least 3 frames before accessing devices
2600 * (note that delay's minimal time is one clock tick).
2601 */
2602 mutex_exit(HUBD_MUTEX(hubd));
2603 delay(drv_usectohz(10000));
2604 mutex_enter(HUBD_MUTEX(hubd));
2605
2606 hubd->h_dev_state = USB_DEV_HUB_STATE_RECOVER;
2607
2608 for (port = 1; port <= hubd->h_nports; port++) {
2609 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
2610 "hubd_restore_device_state: port=%d", port);
2611
2612 /*
2613 * the childen_dips list may have dips that have been
2614 * already deallocated. we only get a post_detach notification
2615 * but not a destroy notification
2616 */
2617 ch_dip = hubd->h_children_dips[port];
2618 if (ch_dip) {
2619 /* get port status */
2620 (void) hubd_determine_port_status(hubd, port,
2621 &status, &change, NULL, PORT_CHANGE_CSC);
2622
2623 /* check if it is truly connected */
2624 if (status & PORT_STATUS_CCS) {
2625 /*
2626 * Now reset port and assign the device
2627 * its original address
2628 */
2629 retry = 0;
2630 do {
2631 (void) hubd_reset_port(hubd, port);
2632
2633 /* required for ppx */
2634 (void) hubd_enable_port(hubd, port);
2635
2636 if (retry) {
2637 mutex_exit(HUBD_MUTEX(hubd));
2638 delay(drv_usectohz(
2639 hubd_device_delay/2));
2640 mutex_enter(HUBD_MUTEX(hubd));
2641 }
2642
2643 rval = hubd_setdevaddr(hubd, port);
2644 retry++;
2645 } while ((rval != USB_SUCCESS) &&
2646 (retry < hubd_retry_enumerate));
2647
2648 hubd_setdevconfig(hubd, port);
2649
2650 if (hub_prev_state == USB_DEV_DISCONNECTED) {
2651 /* post a connect event */
2652 mutex_exit(HUBD_MUTEX(hubd));
2653 hubd_post_event(hubd, port,
2654 USBA_EVENT_TAG_HOT_INSERTION);
2655 mutex_enter(HUBD_MUTEX(hubd));
2656 } else {
2657 /*
2658 * Since we have this device connected
2659 * mark it reinserted to prevent
2660 * cleanup thread from stepping in.
2661 */
2662 mutex_exit(HUBD_MUTEX(hubd));
2663 mutex_enter(&(DEVI(ch_dip)->devi_lock));
2664 DEVI_SET_DEVICE_REINSERTED(ch_dip);
2665 mutex_exit(&(DEVI(ch_dip)->devi_lock));
2666
2667 /*
2668 * reopen pipes for children for
2669 * their DDI_RESUME
2670 */
2671 rval = usba_persistent_pipe_open(
2672 usba_get_usba_device(ch_dip));
2673 mutex_enter(HUBD_MUTEX(hubd));
2674 ASSERT(rval == USB_SUCCESS);
2675 }
2676 } else {
2677 /*
2678 * Mark this dip for deletion as the device
2679 * is not physically present, and schedule
2680 * cleanup thread upon post resume
2681 */
2682 mutex_exit(HUBD_MUTEX(hubd));
2683
2684 USB_DPRINTF_L2(DPRINT_MASK_ATTA,
2685 hubd->h_log_handle,
2686 "hubd_restore_device_state: "
2687 "dip=%p on port=%d marked for cleanup",
2688 (void *)ch_dip, port);
2689 mutex_enter(&(DEVI(ch_dip)->devi_lock));
2690 DEVI_SET_DEVICE_REMOVED(ch_dip);
2691 mutex_exit(&(DEVI(ch_dip)->devi_lock));
2692
2693 mutex_enter(HUBD_MUTEX(hubd));
2694 }
2695 } else if (ehci_root_hub) {
2696 /* get port status */
2697 (void) hubd_determine_port_status(hubd, port,
2698 &status, &change, NULL, PORT_CHANGE_CSC);
2699
2700 /* check if it is truly connected */
2701 if (status & PORT_STATUS_CCS) {
2702 /*
2703 * reset the port to find out if we have
2704 * 2.0 device connected or 1.X. A 2.0
2705 * device will still be seen as connected,
2706 * while a 1.X device will switch over to
2707 * the companion controller.
2708 */
2709 (void) hubd_reset_port(hubd, port);
2710
2711 (void) hubd_determine_port_status(hubd, port,
2712 &status, &change, NULL, PORT_CHANGE_CSC);
2713
2714 if (status &
2715 (PORT_STATUS_CCS | PORT_STATUS_HSDA)) {
2716 /*
2717 * We have a USB 2.0 device
2718 * connected. Power cycle this port
2719 * so that hotplug thread can
2720 * enumerate this device.
2721 */
2722 (void) hubd_toggle_port(hubd, port);
2723 } else {
2724 USB_DPRINTF_L2(DPRINT_MASK_ATTA,
2725 hubd->h_log_handle,
2726 "hubd_restore_device_state: "
2727 "device on port %d switched over",
2728 port);
2729 }
2730 }
2731
2732 }
2733 }
2734
2735
2736 /* if the device had remote wakeup earlier, enable it again */
2737 if (hubd->h_hubpm->hubp_wakeup_enabled) {
2738 mutex_exit(HUBD_MUTEX(hubd));
2739 (void) usb_handle_remote_wakeup(hubd->h_dip,
2740 USB_REMOTE_WAKEUP_ENABLE);
2741 mutex_enter(HUBD_MUTEX(hubd));
2742 }
2743
2744 hubd->h_dev_state = USB_DEV_ONLINE;
2745 hubd_start_polling(hubd, 0);
2746 (void) hubd_pm_idle_component(hubd, dip, 0);
2747 mutex_exit(HUBD_MUTEX(hubd));
2748 }
2749
2750
2751 /*
2752 * hubd_cleanup:
2753 * cleanup hubd and deallocate. this function is called for
2754 * handling attach failures and detaching including dynamic
2755 * reconfiguration. If called from attaching, it must clean
2756 * up the whole thing and return success.
2757 */
2758 /*ARGSUSED*/
2759 static int
hubd_cleanup(dev_info_t * dip,hubd_t * hubd)2760 hubd_cleanup(dev_info_t *dip, hubd_t *hubd)
2761 {
2762 int rval, old_dev_state;
2763 hub_power_t *hubpm;
2764 #ifdef DEBUG
2765 usb_port_t port;
2766 #endif
2767
2768 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
2769 "hubd_cleanup:");
2770
2771 if ((hubd->h_init_state & HUBD_LOCKS_DONE) == 0) {
2772 goto done;
2773 }
2774
2775 /* ensure we are the only one active */
2776 ndi_devi_enter(dip);
2777
2778 mutex_enter(HUBD_MUTEX(hubd));
2779
2780 /* Cleanup failure is only allowed if called from detach */
2781 if (DEVI_IS_DETACHING(dip)) {
2782 dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip;
2783
2784 /*
2785 * We are being called from detach.
2786 * Fail immediately if the hotplug thread is running
2787 * else set the dev_state to disconnected so that
2788 * hotplug thread just exits without doing anything.
2789 */
2790 if (hubd->h_bus_ctls || hubd->h_bus_pwr ||
2791 hubd->h_hotplug_thread) {
2792 mutex_exit(HUBD_MUTEX(hubd));
2793 ndi_devi_exit(dip);
2794
2795 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
2796 "hubd_cleanup: hotplug thread/bus ctl active "
2797 "- failing detach");
2798
2799 return (USB_FAILURE);
2800 }
2801
2802 /*
2803 * if the deathrow thread is still active or about
2804 * to become active, fail detach
2805 * the roothup can only be detached if nexus drivers
2806 * are unloaded or explicitly offlined
2807 */
2808 if (rh_dip == dip) {
2809 if (hubd->h_cleanup_needed ||
2810 hubd->h_cleanup_active) {
2811 mutex_exit(HUBD_MUTEX(hubd));
2812 ndi_devi_exit(dip);
2813
2814 USB_DPRINTF_L2(DPRINT_MASK_ATTA,
2815 hubd->h_log_handle,
2816 "hubd_cleanup: deathrow still active?"
2817 "- failing detach");
2818
2819 return (USB_FAILURE);
2820 }
2821 }
2822 }
2823
2824 old_dev_state = hubd->h_dev_state;
2825 hubd->h_dev_state = USB_DEV_DISCONNECTED;
2826
2827 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
2828 "hubd_cleanup: stop polling");
2829 hubd_close_intr_pipe(hubd);
2830
2831 ASSERT((hubd->h_bus_ctls || hubd->h_bus_pwr ||
2832 hubd->h_hotplug_thread) == 0);
2833 mutex_exit(HUBD_MUTEX(hubd));
2834
2835 /*
2836 * deallocate events, if events are still registered
2837 * (ie. children still attached) then we have to fail the detach
2838 */
2839 if (hubd->h_ndi_event_hdl) {
2840
2841 rval = ndi_event_free_hdl(hubd->h_ndi_event_hdl);
2842 if (DEVI_IS_ATTACHING(dip)) {
2843
2844 /* It must return success if attaching. */
2845 ASSERT(rval == NDI_SUCCESS);
2846
2847 } else if (rval != NDI_SUCCESS) {
2848
2849 USB_DPRINTF_L2(DPRINT_MASK_ALL, hubd->h_log_handle,
2850 "hubd_cleanup: ndi_event_free_hdl failed");
2851 ndi_devi_exit(dip);
2852
2853 return (USB_FAILURE);
2854
2855 }
2856 }
2857
2858 mutex_enter(HUBD_MUTEX(hubd));
2859
2860 if (hubd->h_init_state & HUBD_CHILDREN_CREATED) {
2861 #ifdef DEBUG
2862 for (port = 1; port <= hubd->h_nports; port++) {
2863 ASSERT(hubd->h_usba_devices[port] == NULL);
2864 ASSERT(hubd->h_children_dips[port] == NULL);
2865 }
2866 #endif
2867 kmem_free(hubd->h_children_dips, hubd->h_cd_list_length);
2868 kmem_free(hubd->h_usba_devices, hubd->h_cd_list_length);
2869 }
2870
2871 /*
2872 * Disable the event callbacks first, after this point, event
2873 * callbacks will never get called. Note we shouldn't hold
2874 * mutex while unregistering events because there may be a
2875 * competing event callback thread. Event callbacks are done
2876 * with ndi mutex held and this can cause a potential deadlock.
2877 * Note that cleanup can't fail after deregistration of events.
2878 */
2879 if (hubd->h_init_state & HUBD_EVENTS_REGISTERED) {
2880 mutex_exit(HUBD_MUTEX(hubd));
2881 usb_unregister_event_cbs(dip, &hubd_events);
2882 hubd_unregister_cpr_callback(hubd);
2883 mutex_enter(HUBD_MUTEX(hubd));
2884 }
2885
2886 /* restore the old dev state so that device can be put into low power */
2887 hubd->h_dev_state = old_dev_state;
2888 hubpm = hubd->h_hubpm;
2889
2890 if ((hubpm) && (hubd->h_dev_state != USB_DEV_DISCONNECTED)) {
2891 (void) hubd_pm_busy_component(hubd, dip, 0);
2892 mutex_exit(HUBD_MUTEX(hubd));
2893 if (hubd->h_hubpm->hubp_wakeup_enabled) {
2894 /*
2895 * Bring the hub to full power before
2896 * issuing the disable remote wakeup command
2897 */
2898 (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
2899
2900 if ((rval = usb_handle_remote_wakeup(hubd->h_dip,
2901 USB_REMOTE_WAKEUP_DISABLE)) != USB_SUCCESS) {
2902 USB_DPRINTF_L2(DPRINT_MASK_PM,
2903 hubd->h_log_handle,
2904 "hubd_cleanup: disable remote wakeup "
2905 "fails=%d", rval);
2906 }
2907 }
2908
2909 (void) pm_lower_power(hubd->h_dip, 0, USB_DEV_OS_PWR_OFF);
2910
2911 mutex_enter(HUBD_MUTEX(hubd));
2912 (void) hubd_pm_idle_component(hubd, dip, 0);
2913 }
2914
2915 if (hubpm) {
2916 if (hubpm->hubp_child_pwrstate) {
2917 kmem_free(hubpm->hubp_child_pwrstate,
2918 MAX_PORTS + 1);
2919 }
2920 kmem_free(hubpm, sizeof (hub_power_t));
2921 }
2922 mutex_exit(HUBD_MUTEX(hubd));
2923
2924 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
2925 "hubd_cleanup: freeing space");
2926
2927 if (hubd->h_init_state & HUBD_HUBDI_REGISTERED) {
2928 rval = usba_hubdi_unregister(dip);
2929 ASSERT(rval == USB_SUCCESS);
2930 }
2931
2932 if (hubd->h_init_state & HUBD_LOCKS_DONE) {
2933 mutex_destroy(HUBD_MUTEX(hubd));
2934 cv_destroy(&hubd->h_cv_reset_port);
2935 cv_destroy(&hubd->h_cv_hotplug_dev);
2936 }
2937
2938 ndi_devi_exit(dip);
2939
2940 if (hubd->h_init_state & HUBD_MINOR_NODE_CREATED) {
2941 ddi_remove_minor_node(dip, NULL);
2942 }
2943
2944 if (usba_is_root_hub(dip)) {
2945 usb_pipe_close(dip, hubd->h_default_pipe,
2946 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
2947 }
2948
2949 done:
2950 if (hubd->h_ancestry_str) {
2951 kmem_free(hubd->h_ancestry_str, HUBD_APID_NAMELEN);
2952 }
2953
2954 usb_client_detach(dip, hubd->h_dev_data);
2955
2956 usb_free_log_hdl(hubd->h_log_handle);
2957
2958 if (!usba_is_root_hub(dip)) {
2959 ddi_soft_state_free(hubd_statep, ddi_get_instance(dip));
2960 }
2961
2962 ddi_prop_remove_all(dip);
2963
2964 return (USB_SUCCESS);
2965 }
2966
2967
2968 /*
2969 * hubd_determine_port_connection:
2970 * Determine which port is in connect status but does not
2971 * have connect status change bit set, and mark port change
2972 * bit accordingly.
2973 * This function is applied during hub attach time.
2974 */
2975 static usb_port_mask_t
hubd_determine_port_connection(hubd_t * hubd)2976 hubd_determine_port_connection(hubd_t *hubd)
2977 {
2978 usb_port_t port;
2979 uint16_t status;
2980 uint16_t change;
2981 usb_port_mask_t port_change = 0;
2982
2983 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
2984
2985 for (port = 1; port <= hubd->h_nports; port++) {
2986
2987 (void) hubd_determine_port_status(hubd, port, &status,
2988 &change, NULL, 0);
2989
2990 /* Check if port is in connect status */
2991 if (!(status & PORT_STATUS_CCS)) {
2992
2993 continue;
2994 }
2995
2996 /*
2997 * Check if port Connect Status Change bit has been set.
2998 * If already set, the connection will be handled by
2999 * intr polling callback, not during attach.
3000 */
3001 if (change & PORT_CHANGE_CSC) {
3002
3003 continue;
3004 }
3005
3006 port_change |= 1 << port;
3007 }
3008
3009 return (port_change);
3010 }
3011
3012
3013 /*
3014 * hubd_check_ports:
3015 * - get hub descriptor
3016 * - check initial port status
3017 * - enable power on all ports
3018 * - enable polling on ep1
3019 */
3020 static int
hubd_check_ports(hubd_t * hubd)3021 hubd_check_ports(hubd_t *hubd)
3022 {
3023 int rval;
3024 usb_port_mask_t port_change = 0;
3025 hubd_hotplug_arg_t *arg;
3026
3027 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
3028
3029 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
3030 "hubd_check_ports: addr=0x%x", usb_get_addr(hubd->h_dip));
3031
3032 /*
3033 * First turn off all port power
3034 */
3035 if ((rval = hubd_disable_all_port_power(hubd)) != USB_SUCCESS) {
3036
3037 /* disable whatever was enabled */
3038 (void) hubd_disable_all_port_power(hubd);
3039
3040 return (rval);
3041 }
3042
3043 /*
3044 * do not switch on immediately (instantly on root hub)
3045 * and allow time to settle
3046 */
3047 mutex_exit(HUBD_MUTEX(hubd));
3048 delay(drv_usectohz(10000));
3049 mutex_enter(HUBD_MUTEX(hubd));
3050
3051 /*
3052 * enable power on all ports so we can see connects
3053 */
3054 if ((rval = hubd_enable_all_port_power(hubd)) != USB_SUCCESS) {
3055 /* disable whatever was enabled */
3056 (void) hubd_disable_all_port_power(hubd);
3057
3058 return (rval);
3059 }
3060
3061 /* wait at least 3 frames before accessing devices */
3062 mutex_exit(HUBD_MUTEX(hubd));
3063 delay(drv_usectohz(10000));
3064 mutex_enter(HUBD_MUTEX(hubd));
3065
3066 /*
3067 * allocate arrays for saving the dips of each child per port
3068 *
3069 * ports go from 1 - n, allocate 1 more entry
3070 */
3071 hubd->h_cd_list_length =
3072 (sizeof (dev_info_t **)) * (hubd->h_nports + 1);
3073
3074 hubd->h_children_dips = (dev_info_t **)kmem_zalloc(
3075 hubd->h_cd_list_length, KM_SLEEP);
3076 hubd->h_usba_devices = (usba_device_t **)kmem_zalloc(
3077 hubd->h_cd_list_length, KM_SLEEP);
3078
3079 hubd->h_init_state |= HUBD_CHILDREN_CREATED;
3080
3081 mutex_exit(HUBD_MUTEX(hubd));
3082 arg = (hubd_hotplug_arg_t *)kmem_zalloc(
3083 sizeof (hubd_hotplug_arg_t), KM_SLEEP);
3084 mutex_enter(HUBD_MUTEX(hubd));
3085
3086 if ((rval = hubd_open_intr_pipe(hubd)) != USB_SUCCESS) {
3087 kmem_free(arg, sizeof (hubd_hotplug_arg_t));
3088
3089 return (rval);
3090 }
3091
3092 hubd_start_polling(hubd, 0);
3093
3094 /*
3095 * Some hub devices, like the embedded hub in the CKS ErgoMagic
3096 * keyboard, may only have connection status bit set, but not
3097 * have connect status change bit set when a device has been
3098 * connected to its downstream port before the hub is enumerated.
3099 * Then when the hub is in enumeration, the devices connected to
3100 * it cannot be detected by the intr pipe and won't be enumerated.
3101 * We need to check such situation here and enumerate the downstream
3102 * devices for such hubs.
3103 */
3104 port_change = hubd_determine_port_connection(hubd);
3105
3106 if (port_change != 0 || hubd->h_port_change != 0) {
3107 hubd_pm_busy_component(hubd, hubd->h_dip, 0);
3108
3109 arg->hubd = hubd;
3110 arg->hotplug_during_attach = B_TRUE;
3111 hubd->h_port_change |= port_change;
3112
3113 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
3114 "hubd_check_ports: port change=0x%x, need to connect",
3115 hubd->h_port_change);
3116
3117 if (usb_async_req(hubd->h_dip, hubd_hotplug_thread,
3118 (void *)arg, 0) == USB_SUCCESS) {
3119 hubd->h_hotplug_thread++;
3120 } else {
3121 /* mark this device as idle */
3122 hubd_pm_idle_component(hubd, hubd->h_dip, 0);
3123 kmem_free(arg, sizeof (hubd_hotplug_arg_t));
3124 }
3125 } else {
3126 kmem_free(arg, sizeof (hubd_hotplug_arg_t));
3127 }
3128
3129 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
3130 "hubd_check_ports done");
3131
3132 return (USB_SUCCESS);
3133 }
3134
3135
3136 /*
3137 * hubd_get_hub_descriptor:
3138 */
3139 static int
hubd_get_hub_descriptor(hubd_t * hubd)3140 hubd_get_hub_descriptor(hubd_t *hubd)
3141 {
3142 mblk_t *data = NULL;
3143 usb_cr_t completion_reason;
3144 usb_cb_flags_t cb_flags;
3145 uint16_t length, wValue;
3146 int rval;
3147 usb_req_attrs_t attr = 0;
3148
3149 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
3150 "hubd_get_hub_descriptor:");
3151
3152 if ((hubd->h_dev_data->dev_descr->idVendor == USB_HUB_INTEL_VID) &&
3153 (hubd->h_dev_data->dev_descr->idProduct == USB_HUB_INTEL_PID)) {
3154 attr = USB_ATTRS_SHORT_XFER_OK;
3155 }
3156
3157 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
3158 ASSERT(hubd->h_default_pipe != 0);
3159
3160 mutex_exit(HUBD_MUTEX(hubd));
3161
3162 /*
3163 * The contents of wValue change depending on whether this is a USB 2 or
3164 * USB 3 device. SuperSpeed Hubs have different descriptors and you
3165 * cannot ask them for the traditional USB 2 descriptor.
3166 */
3167 if (hubd->h_usba_device->usb_port_status >= USBA_SUPER_SPEED_DEV) {
3168 wValue = USB_DESCR_TYPE_SS_HUB << 8 | HUBD_DEFAULT_DESC_INDEX;
3169 } else {
3170 wValue = USB_DESCR_TYPE_HUB << 8 | HUBD_DEFAULT_DESC_INDEX;
3171 }
3172
3173 /*
3174 * The hub descriptor length varies in various versions of USB. For
3175 * example, in USB 2 it's at least 9 bytes long. To start with, we
3176 * always get the first 8 bytes so we can figure out how long it
3177 * actually is.
3178 */
3179 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
3180 hubd->h_default_pipe,
3181 HUB_CLASS_REQ_TYPE,
3182 USB_REQ_GET_DESCR, /* bRequest */
3183 wValue, /* wValue */
3184 0, /* wIndex */
3185 8, /* wLength */
3186 &data, 0,
3187 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
3188 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
3189 "get hub descriptor failed: cr=%d cb_fl=0x%x rval=%d",
3190 completion_reason, cb_flags, rval);
3191 freemsg(data);
3192 mutex_enter(HUBD_MUTEX(hubd));
3193
3194 return (rval);
3195 }
3196
3197 length = *(data->b_rptr);
3198
3199 if (length > 8) {
3200 freemsg(data);
3201 data = NULL;
3202
3203 /* get complete hub descriptor */
3204 rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
3205 hubd->h_default_pipe,
3206 HUB_CLASS_REQ_TYPE,
3207 USB_REQ_GET_DESCR, /* bRequest */
3208 wValue, /* wValue */
3209 0, /* wIndex */
3210 length, /* wLength */
3211 &data, attr,
3212 &completion_reason, &cb_flags, 0);
3213
3214 /*
3215 * Hub descriptor data less than 9 bytes is not valid and
3216 * may cause trouble if we use it. See USB2.0 Tab11-13.
3217 */
3218 if ((rval != USB_SUCCESS) || (MBLKL(data) <= 8)) {
3219 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
3220 "get hub descriptor failed: "
3221 "cr=%d cb_fl=0x%x rval=%d, len=%ld",
3222 completion_reason, cb_flags, rval,
3223 (data)?MBLKL(data):0);
3224 freemsg(data);
3225 mutex_enter(HUBD_MUTEX(hubd));
3226
3227 return (rval);
3228 }
3229 }
3230
3231 mutex_enter(HUBD_MUTEX(hubd));
3232
3233 /*
3234 * Parse the hub descriptor. Note that the format of the descriptor
3235 * itself depends on the USB version. We handle the different ones and
3236 * transform it into a single uniform view.
3237 */
3238
3239 ASSERT(*(data->b_rptr + 2) <= (MAX_PORTS + 1));
3240 if (hubd->h_usba_device->usb_port_status >= USBA_SUPER_SPEED_DEV) {
3241 usb_ss_hub_descr_t hub_descr;
3242 char *desc = "cccscccs";
3243 ASSERT(*(data->b_rptr + 1) == ROOT_HUB_SS_DESCRIPTOR_TYPE);
3244
3245 /*
3246 * Note many hubs may support less than the 255 devices that the
3247 * USB specification allows for. In those cases, we'll simply
3248 * read less and it should be okay.
3249 */
3250 if (usb_parse_CV_descr(desc, data->b_rptr, MBLKL(data),
3251 (void *)&hub_descr, sizeof (hub_descr)) == 0) {
3252 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
3253 "parsing hub descriptor failed");
3254 freemsg(data);
3255 return (USB_FAILURE);
3256 }
3257
3258 hubd->h_nports = hub_descr.bNbrPorts;
3259 hubd->h_hub_chars = hub_descr.wHubCharacteristics;
3260 hubd->h_power_good = hub_descr.bPwrOn2PwrGood;
3261 hubd->h_current = hub_descr.bHubContrCurrent;
3262 } else {
3263 usb_hub_descr_t hub_descr;
3264 if (usb_parse_CV_descr("cccscccccc",
3265 data->b_rptr, MBLKL(data),
3266 (void *)&hub_descr, sizeof (usb_hub_descr_t)) == 0) {
3267 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
3268 "parsing hub descriptor failed");
3269 freemsg(data);
3270 return (USB_FAILURE);
3271 }
3272
3273 hubd->h_nports = hub_descr.bNbrPorts;
3274 hubd->h_hub_chars = hub_descr.wHubCharacteristics;
3275 hubd->h_power_good = hub_descr.bPwrOn2PwrGood;
3276 hubd->h_current = hub_descr.bHubContrCurrent;
3277 }
3278
3279 freemsg(data);
3280
3281 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
3282 "rval=0x%x bNbrPorts=0x%x wHubChars=0x%x "
3283 "PwrOn2PwrGood=0x%x HubContrCurrent=%dmA", rval,
3284 hubd->h_nports, hubd->h_hub_chars,
3285 hubd->h_power_good, hubd->h_current);
3286
3287 if (hubd->h_nports > MAX_PORTS) {
3288 USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubd->h_log_handle,
3289 "Hub driver supports max of %d ports on hub. "
3290 "Hence using the first %d port of %d ports available",
3291 MAX_PORTS, MAX_PORTS, hubd->h_nports);
3292
3293 hubd->h_nports = MAX_PORTS;
3294 }
3295
3296 return (USB_SUCCESS);
3297 }
3298
3299 static int
hubd_set_hub_depth(hubd_t * hubd)3300 hubd_set_hub_depth(hubd_t *hubd)
3301 {
3302 int rval;
3303 usb_cr_t completion_reason;
3304 usb_cb_flags_t cb_flags;
3305 usba_device_t *ud;
3306 uint16_t depth;
3307
3308 /*
3309 * We only need to set the hub depth devices for hubs that are at least
3310 * SuperSpeed devices. This didn't exist for USB 2.0 and older hubs.
3311 * There's also no need to call this on the root hub.
3312 */
3313 if (hubd->h_usba_device->usb_port_status < USBA_SUPER_SPEED_DEV ||
3314 usba_is_root_hub(hubd->h_dip))
3315 return (USB_SUCCESS);
3316
3317 depth = 0;
3318 ud = hubd->h_usba_device;
3319 while (ud->usb_parent_hub != NULL) {
3320 depth++;
3321 ud = ud->usb_parent_hub;
3322 }
3323 ASSERT(depth > 0);
3324
3325 if (depth > HUBD_SS_MAX_DEPTH) {
3326 const char *mfg, *prod;
3327
3328 ud = hubd->h_usba_device;
3329 prod = ud->usb_product_str;
3330 if (prod == NULL)
3331 prod = "Unknown Device";
3332 mfg = ud->usb_mfg_str;
3333 if (mfg == NULL)
3334 mfg = "Unknown Manufacturer";
3335 cmn_err(CE_WARN, "Unable to attach USB 3.x hub %s %s. A "
3336 "maximum of %d hubs may be cascaded", mfg, prod,
3337 HUBD_SS_MAX_DEPTH);
3338 return (USB_FAILURE);
3339 }
3340
3341 /*
3342 * When making the HUB_REQ_SET_HUB_DEPTH request, a hub connected to a
3343 * root port is considered to have a hub depth of zero whereas we
3344 * consider having a hub depth of one above.
3345 */
3346 depth--;
3347
3348 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
3349 hubd->h_default_pipe,
3350 HUB_SET_HUB_DEPTH_TYPE,
3351 HUB_REQ_SET_HUB_DEPTH, /* bRequest */
3352 depth, /* wValue */
3353 0, /* wIndex */
3354 0, /* wLength */
3355 NULL, 0,
3356 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
3357 USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
3358 "get set hub depth failed: cr=%d cb=0x%x",
3359 completion_reason, cb_flags);
3360 }
3361
3362 return (rval);
3363 }
3364
3365 /*
3366 * hubd_get_hub_status_words:
3367 */
3368 static int
hubd_get_hub_status_words(hubd_t * hubd,uint16_t * status)3369 hubd_get_hub_status_words(hubd_t *hubd, uint16_t *status)
3370 {
3371 usb_cr_t completion_reason;
3372 usb_cb_flags_t cb_flags;
3373 mblk_t *data = NULL;
3374
3375 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
3376
3377 mutex_exit(HUBD_MUTEX(hubd));
3378
3379 if (usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe,
3380 HUB_CLASS_REQ_TYPE,
3381 USB_REQ_GET_STATUS,
3382 0,
3383 0,
3384 GET_STATUS_LENGTH,
3385 &data, 0,
3386 &completion_reason, &cb_flags, 0) != USB_SUCCESS) {
3387 USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
3388 "get hub status failed: cr=%d cb=0x%x",
3389 completion_reason, cb_flags);
3390
3391 if (data) {
3392 freemsg(data);
3393 }
3394
3395 mutex_enter(HUBD_MUTEX(hubd));
3396
3397 return (USB_FAILURE);
3398 }
3399
3400 mutex_enter(HUBD_MUTEX(hubd));
3401
3402 status[0] = (*(data->b_rptr + 1) << 8) | *(data->b_rptr);
3403 status[1] = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2);
3404
3405 USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
3406 "hub status=0x%x change=0x%x", status[0], status[1]);
3407
3408 freemsg(data);
3409
3410 return (USB_SUCCESS);
3411 }
3412
3413
3414 /*
3415 * hubd_open_intr_pipe:
3416 * we read all descriptors first for curiosity and then simply
3417 * open the pipe
3418 */
3419 static int
hubd_open_intr_pipe(hubd_t * hubd)3420 hubd_open_intr_pipe(hubd_t *hubd)
3421 {
3422 int rval;
3423
3424 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
3425 "hubd_open_intr_pipe:");
3426
3427 ASSERT(hubd->h_intr_pipe_state == HUBD_INTR_PIPE_IDLE);
3428
3429 hubd->h_intr_pipe_state = HUBD_INTR_PIPE_OPENING;
3430 mutex_exit(HUBD_MUTEX(hubd));
3431
3432 if ((rval = usb_pipe_xopen(hubd->h_dip,
3433 &hubd->h_ep1_xdescr, &hubd->h_pipe_policy,
3434 0, &hubd->h_ep1_ph)) != USB_SUCCESS) {
3435 USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
3436 "open intr pipe failed (%d)", rval);
3437
3438 mutex_enter(HUBD_MUTEX(hubd));
3439 hubd->h_intr_pipe_state = HUBD_INTR_PIPE_IDLE;
3440
3441 return (rval);
3442 }
3443
3444 mutex_enter(HUBD_MUTEX(hubd));
3445 hubd->h_intr_pipe_state = HUBD_INTR_PIPE_ACTIVE;
3446
3447 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
3448 "open intr pipe succeeded, ph=0x%p", (void *)hubd->h_ep1_ph);
3449
3450 return (USB_SUCCESS);
3451 }
3452
3453
3454 /*
3455 * hubd_start_polling:
3456 * start or restart the polling
3457 */
3458 static void
hubd_start_polling(hubd_t * hubd,int always)3459 hubd_start_polling(hubd_t *hubd, int always)
3460 {
3461 usb_intr_req_t *reqp;
3462 int rval;
3463 usb_pipe_state_t pipe_state;
3464
3465 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
3466 "start polling: always=%d dev_state=%d pipe_state=%d\n\t"
3467 "thread=%d ep1_ph=0x%p",
3468 always, hubd->h_dev_state, hubd->h_intr_pipe_state,
3469 hubd->h_hotplug_thread, (void *)hubd->h_ep1_ph);
3470
3471 /*
3472 * start or restart polling on the intr pipe
3473 * only if hotplug thread is not running
3474 */
3475 if ((always == HUBD_ALWAYS_START_POLLING) ||
3476 ((hubd->h_dev_state == USB_DEV_ONLINE) &&
3477 (hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE) &&
3478 (hubd->h_hotplug_thread == 0) && hubd->h_ep1_ph)) {
3479 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
3480 "start polling requested");
3481
3482 reqp = usb_alloc_intr_req(hubd->h_dip, 0, USB_FLAGS_SLEEP);
3483
3484 reqp->intr_client_private = (usb_opaque_t)hubd;
3485 reqp->intr_attributes = USB_ATTRS_SHORT_XFER_OK |
3486 USB_ATTRS_AUTOCLEARING;
3487 reqp->intr_len = hubd->h_ep1_xdescr.uex_ep.wMaxPacketSize;
3488 reqp->intr_cb = hubd_read_cb;
3489 reqp->intr_exc_cb = hubd_exception_cb;
3490 mutex_exit(HUBD_MUTEX(hubd));
3491 if ((rval = usb_pipe_intr_xfer(hubd->h_ep1_ph, reqp,
3492 USB_FLAGS_SLEEP)) != USB_SUCCESS) {
3493 USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
3494 "start polling failed, rval=%d", rval);
3495 usb_free_intr_req(reqp);
3496 }
3497
3498 rval = usb_pipe_get_state(hubd->h_ep1_ph, &pipe_state,
3499 USB_FLAGS_SLEEP);
3500 if (pipe_state != USB_PIPE_STATE_ACTIVE) {
3501 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
3502 "intr pipe state=%d, rval=%d", pipe_state, rval);
3503 }
3504 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
3505 "start polling request 0x%p", (void *)reqp);
3506
3507 mutex_enter(HUBD_MUTEX(hubd));
3508 }
3509 }
3510
3511
3512 /*
3513 * hubd_stop_polling
3514 * stop polling but do not close the pipe
3515 */
3516 static void
hubd_stop_polling(hubd_t * hubd)3517 hubd_stop_polling(hubd_t *hubd)
3518 {
3519 int rval;
3520 usb_pipe_state_t pipe_state;
3521
3522 if (hubd->h_ep1_ph) {
3523 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
3524 "hubd_stop_polling:");
3525 hubd->h_intr_pipe_state = HUBD_INTR_PIPE_STOPPED;
3526 mutex_exit(HUBD_MUTEX(hubd));
3527
3528 usb_pipe_stop_intr_polling(hubd->h_ep1_ph, USB_FLAGS_SLEEP);
3529 rval = usb_pipe_get_state(hubd->h_ep1_ph, &pipe_state,
3530 USB_FLAGS_SLEEP);
3531
3532 if (pipe_state != USB_PIPE_STATE_IDLE) {
3533 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
3534 "intr pipe state=%d, rval=%d", pipe_state, rval);
3535 }
3536 mutex_enter(HUBD_MUTEX(hubd));
3537 if (hubd->h_intr_pipe_state == HUBD_INTR_PIPE_STOPPED) {
3538 hubd->h_intr_pipe_state = HUBD_INTR_PIPE_ACTIVE;
3539 }
3540 }
3541 }
3542
3543
3544 /*
3545 * hubd_close_intr_pipe:
3546 * close the pipe (which also stops the polling
3547 * and wait for the hotplug thread to exit
3548 */
3549 static void
hubd_close_intr_pipe(hubd_t * hubd)3550 hubd_close_intr_pipe(hubd_t *hubd)
3551 {
3552 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
3553 "hubd_close_intr_pipe:");
3554
3555 /*
3556 * Now that no async operation is outstanding on pipe,
3557 * we can change the state to HUBD_INTR_PIPE_CLOSING
3558 */
3559 hubd->h_intr_pipe_state = HUBD_INTR_PIPE_CLOSING;
3560
3561 ASSERT(hubd->h_hotplug_thread == 0);
3562
3563 if (hubd->h_ep1_ph) {
3564 mutex_exit(HUBD_MUTEX(hubd));
3565 usb_pipe_close(hubd->h_dip, hubd->h_ep1_ph, USB_FLAGS_SLEEP,
3566 NULL, NULL);
3567 mutex_enter(HUBD_MUTEX(hubd));
3568 hubd->h_ep1_ph = NULL;
3569 }
3570
3571 hubd->h_intr_pipe_state = HUBD_INTR_PIPE_IDLE;
3572 }
3573
3574
3575 /*
3576 * hubd_exception_cb
3577 * interrupt ep1 exception callback function.
3578 * this callback executes in taskq thread context and assumes
3579 * autoclearing
3580 */
3581 /*ARGSUSED*/
3582 static void
hubd_exception_cb(usb_pipe_handle_t pipe,usb_intr_req_t * reqp)3583 hubd_exception_cb(usb_pipe_handle_t pipe, usb_intr_req_t *reqp)
3584 {
3585 hubd_t *hubd = (hubd_t *)(reqp->intr_client_private);
3586
3587 USB_DPRINTF_L2(DPRINT_MASK_CALLBACK, hubd->h_log_handle,
3588 "hubd_exception_cb: "
3589 "req=0x%p cr=%d data=0x%p cb_flags=0x%x", (void *)reqp,
3590 reqp->intr_completion_reason, (void *)reqp->intr_data,
3591 reqp->intr_cb_flags);
3592
3593 ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
3594
3595 mutex_enter(HUBD_MUTEX(hubd));
3596 (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0);
3597
3598 switch (reqp->intr_completion_reason) {
3599 case USB_CR_PIPE_RESET:
3600 /* only restart polling after autoclearing */
3601 if ((hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE) &&
3602 (hubd->h_port_reset_wait == 0)) {
3603 hubd_start_polling(hubd, 0);
3604 }
3605
3606 break;
3607 case USB_CR_DEV_NOT_RESP:
3608 case USB_CR_STOPPED_POLLING:
3609 case USB_CR_PIPE_CLOSING:
3610 case USB_CR_UNSPECIFIED_ERR:
3611 /* never restart polling on these conditions */
3612 default:
3613 /* for all others, wait for the autoclearing PIPE_RESET cb */
3614
3615 break;
3616 }
3617
3618 usb_free_intr_req(reqp);
3619 (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
3620 mutex_exit(HUBD_MUTEX(hubd));
3621 }
3622
3623
3624 /*
3625 * helper function to convert LE bytes to a portmask
3626 */
3627 static usb_port_mask_t
hubd_mblk2portmask(mblk_t * data)3628 hubd_mblk2portmask(mblk_t *data)
3629 {
3630 int len = min(MBLKL(data), sizeof (usb_port_mask_t));
3631 usb_port_mask_t rval = 0;
3632 int i;
3633
3634 for (i = 0; i < len; i++) {
3635 rval |= data->b_rptr[i] << (i * 8);
3636 }
3637
3638 return (rval);
3639 }
3640
3641
3642 /*
3643 * hubd_read_cb:
3644 * interrupt ep1 callback function
3645 *
3646 * the status indicates just a change on the pipe with no indication
3647 * of what the change was
3648 *
3649 * known conditions:
3650 * - reset port completion
3651 * - connect
3652 * - disconnect
3653 *
3654 * for handling the hotplugging, create a new thread that can do
3655 * synchronous usba calls
3656 */
3657 static void
hubd_read_cb(usb_pipe_handle_t pipe,usb_intr_req_t * reqp)3658 hubd_read_cb(usb_pipe_handle_t pipe, usb_intr_req_t *reqp)
3659 {
3660 hubd_t *hubd = (hubd_t *)(reqp->intr_client_private);
3661 size_t length;
3662 mblk_t *data = reqp->intr_data;
3663 int mem_flag = 0;
3664 hubd_hotplug_arg_t *arg;
3665
3666 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
3667 "hubd_read_cb: ph=0x%p req=0x%p", (void *)pipe, (void *)reqp);
3668
3669 ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
3670
3671 /*
3672 * At present, we are not handling notification for completion of
3673 * asynchronous pipe reset, for which this data ptr could be NULL
3674 */
3675
3676 if (data == NULL) {
3677 usb_free_intr_req(reqp);
3678
3679 return;
3680 }
3681
3682 arg = (hubd_hotplug_arg_t *)kmem_zalloc(
3683 sizeof (hubd_hotplug_arg_t), KM_SLEEP);
3684 mem_flag = 1;
3685
3686 mutex_enter(HUBD_MUTEX(hubd));
3687
3688 if ((hubd->h_dev_state == USB_DEV_SUSPENDED) ||
3689 (hubd->h_intr_pipe_state != HUBD_INTR_PIPE_ACTIVE)) {
3690 mutex_exit(HUBD_MUTEX(hubd));
3691 usb_free_intr_req(reqp);
3692 kmem_free(arg, sizeof (hubd_hotplug_arg_t));
3693
3694 return;
3695 }
3696
3697 ASSERT(hubd->h_ep1_ph == pipe);
3698
3699 length = MBLKL(data);
3700
3701 /*
3702 * Only look at the data and startup the hotplug thread if
3703 * there actually is data.
3704 */
3705 if (length != 0) {
3706 usb_port_mask_t port_change = hubd_mblk2portmask(data);
3707
3708 /*
3709 * if a port change was already reported and we are waiting for
3710 * reset port completion then wake up the hotplug thread which
3711 * should be waiting on reset port completion
3712 *
3713 * if there is disconnect event instead of reset completion, let
3714 * the hotplug thread figure this out
3715 */
3716
3717 /* remove the reset wait bits from the status */
3718 hubd->h_port_change |= port_change &
3719 ~hubd->h_port_reset_wait;
3720
3721 USB_DPRINTF_L3(DPRINT_MASK_CALLBACK, hubd->h_log_handle,
3722 "port change=0x%x port_reset_wait=0x%x",
3723 hubd->h_port_change, hubd->h_port_reset_wait);
3724
3725 /* there should be only one reset bit active at the time */
3726 if (hubd->h_port_reset_wait & port_change) {
3727 hubd->h_port_reset_wait = 0;
3728 cv_signal(&hubd->h_cv_reset_port);
3729 }
3730
3731 /*
3732 * kick off the thread only if device is ONLINE and it is not
3733 * during attaching or detaching
3734 */
3735 if ((hubd->h_dev_state == USB_DEV_ONLINE) &&
3736 (!DEVI_IS_ATTACHING(hubd->h_dip)) &&
3737 (!DEVI_IS_DETACHING(hubd->h_dip)) &&
3738 (hubd->h_port_change) &&
3739 (hubd->h_hotplug_thread == 0)) {
3740 USB_DPRINTF_L3(DPRINT_MASK_CALLBACK, hubd->h_log_handle,
3741 "creating hotplug thread: "
3742 "dev_state=%d", hubd->h_dev_state);
3743
3744 /*
3745 * Mark this device as busy. The will be marked idle
3746 * if the async req fails or at the exit of hotplug
3747 * thread
3748 */
3749 (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0);
3750
3751 arg->hubd = hubd;
3752 arg->hotplug_during_attach = B_FALSE;
3753
3754 if (usb_async_req(hubd->h_dip,
3755 hubd_hotplug_thread,
3756 (void *)arg, 0) == USB_SUCCESS) {
3757 hubd->h_hotplug_thread++;
3758 mem_flag = 0;
3759 } else {
3760 /* mark this device as idle */
3761 (void) hubd_pm_idle_component(hubd,
3762 hubd->h_dip, 0);
3763 }
3764 }
3765 }
3766 mutex_exit(HUBD_MUTEX(hubd));
3767
3768 if (mem_flag == 1) {
3769 kmem_free(arg, sizeof (hubd_hotplug_arg_t));
3770 }
3771
3772 usb_free_intr_req(reqp);
3773 }
3774
3775
3776 /*
3777 * hubd_hotplug_thread:
3778 * handles resetting of port, and creating children
3779 *
3780 * the ports to check are indicated in h_port_change bit mask
3781 * XXX note that one time poll doesn't work on the root hub
3782 */
3783 static void
hubd_hotplug_thread(void * arg)3784 hubd_hotplug_thread(void *arg)
3785 {
3786 hubd_hotplug_arg_t *hd_arg = (hubd_hotplug_arg_t *)arg;
3787 hubd_t *hubd = hd_arg->hubd;
3788 boolean_t attach_flg = hd_arg->hotplug_during_attach;
3789 usb_port_t port;
3790 uint16_t nports;
3791 uint16_t status, change;
3792 hub_power_t *hubpm;
3793 dev_info_t *hdip = hubd->h_dip;
3794 dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip;
3795 dev_info_t *child_dip;
3796 boolean_t online_child = B_FALSE;
3797 boolean_t offline_child = B_FALSE;
3798 boolean_t pwrup_child = B_FALSE;
3799 int old_state;
3800
3801 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3802 "hubd_hotplug_thread: started");
3803
3804 /*
3805 * Before console is init'd, we temporarily block the hotplug
3806 * threads so that BUS_CONFIG_ONE through hubd_bus_config() can be
3807 * processed quickly. This reduces the time needed for vfs_mountroot()
3808 * to mount the root FS from a USB disk. And on SPARC platform,
3809 * in order to load 'consconfig' successfully after OBP is gone,
3810 * we need to check 'modrootloaded' to make sure root filesystem is
3811 * available.
3812 */
3813 while (!modrootloaded || !consconfig_console_is_ready()) {
3814 delay(drv_usectohz(10000));
3815 }
3816
3817 kmem_free(arg, sizeof (hubd_hotplug_arg_t));
3818
3819 /*
3820 * if our bus power entry point is active, process the change
3821 * on the next notification of interrupt pipe
3822 */
3823 mutex_enter(HUBD_MUTEX(hubd));
3824 if (hubd->h_bus_pwr || (hubd->h_hotplug_thread > 1)) {
3825 hubd->h_hotplug_thread--;
3826
3827 /* mark this device as idle */
3828 hubd_pm_idle_component(hubd, hubd->h_dip, 0);
3829 mutex_exit(HUBD_MUTEX(hubd));
3830
3831 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3832 "hubd_hotplug_thread: "
3833 "bus_power in progress/hotplugging undesirable - quit");
3834
3835 return;
3836 }
3837 mutex_exit(HUBD_MUTEX(hubd));
3838
3839 ndi_hold_devi(hdip); /* so we don't race with detach */
3840
3841 mutex_enter(HUBD_MUTEX(hubd));
3842
3843 /* is this the root hub? */
3844 if (hdip == rh_dip) {
3845 if (hubd->h_dev_state == USB_DEV_PWRED_DOWN) {
3846 hubpm = hubd->h_hubpm;
3847
3848 /* mark the root hub as full power */
3849 hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
3850 hubpm->hubp_time_at_full_power = gethrtime();
3851 mutex_exit(HUBD_MUTEX(hubd));
3852
3853 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3854 "hubd_hotplug_thread: call pm_power_has_changed");
3855
3856 (void) pm_power_has_changed(hdip, 0,
3857 USB_DEV_OS_FULL_PWR);
3858
3859 mutex_enter(HUBD_MUTEX(hubd));
3860 hubd->h_dev_state = USB_DEV_ONLINE;
3861 }
3862
3863 } else {
3864 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3865 "hubd_hotplug_thread: not root hub");
3866 }
3867
3868 mutex_exit(HUBD_MUTEX(hubd));
3869
3870 /*
3871 * this ensures one hotplug activity per system at a time.
3872 * we enter the parent PCI node to have this serialization.
3873 * this also excludes ioctls and deathrow thread
3874 * (a bit crude but easier to debug)
3875 */
3876 ndi_devi_enter(ddi_get_parent(rh_dip));
3877 ndi_devi_enter(rh_dip);
3878
3879 /* exclude other threads */
3880 ndi_devi_enter(hdip);
3881 mutex_enter(HUBD_MUTEX(hubd));
3882
3883 ASSERT(hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE);
3884
3885 nports = hubd->h_nports;
3886
3887 hubd_stop_polling(hubd);
3888
3889 while ((hubd->h_dev_state == USB_DEV_ONLINE) &&
3890 (hubd->h_port_change)) {
3891 /*
3892 * The 0th bit is the hub status change bit.
3893 * handle loss of local power here
3894 */
3895 if (hubd->h_port_change & HUB_CHANGE_STATUS) {
3896 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3897 "hubd_hotplug_thread: hub status change!");
3898
3899 /*
3900 * This should be handled properly. For now,
3901 * mask off the bit.
3902 */
3903 hubd->h_port_change &= ~HUB_CHANGE_STATUS;
3904
3905 /*
3906 * check and ack hub status
3907 * this causes stall conditions
3908 * when local power is removed
3909 */
3910 (void) hubd_get_hub_status(hubd);
3911 }
3912
3913 for (port = 1; port <= nports; port++) {
3914 usb_port_mask_t port_mask;
3915 boolean_t was_connected;
3916
3917 port_mask = 1 << port;
3918 was_connected =
3919 (hubd->h_port_state[port] & PORT_STATUS_CCS) &&
3920 (hubd->h_children_dips[port]);
3921
3922 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3923 "hubd_hotplug_thread: "
3924 "port %d mask=0x%x change=0x%x connected=0x%x",
3925 port, port_mask, hubd->h_port_change,
3926 was_connected);
3927
3928 /*
3929 * is this a port connection that changed?
3930 */
3931 if ((hubd->h_port_change & port_mask) == 0) {
3932
3933 continue;
3934 }
3935 hubd->h_port_change &= ~port_mask;
3936
3937 /* ack all changes */
3938 (void) hubd_determine_port_status(hubd, port,
3939 &status, &change, NULL, HUBD_ACK_ALL_CHANGES);
3940
3941 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3942 "handle port %d:\n\t"
3943 "new status=0x%x change=0x%x was_conn=0x%x ",
3944 port, status, change, was_connected);
3945
3946 /* Recover a disabled port */
3947 if (change & PORT_CHANGE_PESC) {
3948 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG,
3949 hubd->h_log_handle,
3950 "port%d Disabled - "
3951 "status=0x%x, change=0x%x",
3952 port, status, change);
3953
3954 /*
3955 * if the port was connected and is still
3956 * connected, recover the port
3957 */
3958 if (was_connected && (status &
3959 PORT_STATUS_CCS)) {
3960 online_child |=
3961 (hubd_recover_disabled_port(hubd,
3962 port) == USB_SUCCESS);
3963 }
3964 }
3965
3966 /*
3967 * Now check what changed on the port
3968 */
3969 if ((change & PORT_CHANGE_CSC) || attach_flg) {
3970 if ((status & PORT_STATUS_CCS) &&
3971 (!was_connected)) {
3972 /* new device plugged in */
3973 online_child |=
3974 (hubd_handle_port_connect(hubd,
3975 port) == USB_SUCCESS);
3976
3977 } else if ((status & PORT_STATUS_CCS) &&
3978 was_connected) {
3979 /*
3980 * In this case we can never be sure
3981 * if the device indeed got hotplugged
3982 * or the hub is falsely reporting the
3983 * change.
3984 */
3985 child_dip = hubd->h_children_dips[port];
3986
3987 mutex_exit(HUBD_MUTEX(hubd));
3988 /*
3989 * this ensures we do not race with
3990 * other threads which are detaching
3991 * the child driver at the same time.
3992 */
3993 ndi_devi_enter(child_dip);
3994 /*
3995 * Now check if the driver remains
3996 * attached.
3997 */
3998 if (i_ddi_devi_attached(child_dip)) {
3999 /*
4000 * first post a disconnect event
4001 * to the child.
4002 */
4003 hubd_post_event(hubd, port,
4004 USBA_EVENT_TAG_HOT_REMOVAL);
4005 mutex_enter(HUBD_MUTEX(hubd));
4006
4007 /*
4008 * then reset the port and
4009 * recover the device
4010 */
4011 online_child |=
4012 (hubd_handle_port_connect(
4013 hubd, port) == USB_SUCCESS);
4014
4015 mutex_exit(HUBD_MUTEX(hubd));
4016 }
4017
4018 ndi_devi_exit(child_dip);
4019 mutex_enter(HUBD_MUTEX(hubd));
4020 } else if (was_connected) {
4021 /* this is a disconnect */
4022 mutex_exit(HUBD_MUTEX(hubd));
4023 hubd_post_event(hubd, port,
4024 USBA_EVENT_TAG_HOT_REMOVAL);
4025 mutex_enter(HUBD_MUTEX(hubd));
4026
4027 offline_child = B_TRUE;
4028 }
4029 }
4030
4031 /*
4032 * Check if any port is coming out of suspend
4033 */
4034 if (change & PORT_CHANGE_PSSC) {
4035 /* a resuming device could have disconnected */
4036 if (was_connected &&
4037 hubd->h_children_dips[port]) {
4038
4039 /* device on this port resuming */
4040 dev_info_t *dip;
4041
4042 dip = hubd->h_children_dips[port];
4043
4044 /*
4045 * Don't raise power on detaching child
4046 */
4047 if (!DEVI_IS_DETACHING(dip)) {
4048 /*
4049 * As this child is not
4050 * detaching, we set this
4051 * flag, causing bus_ctls
4052 * to stall detach till
4053 * pm_raise_power returns
4054 * and flag it for a deferred
4055 * raise_power.
4056 *
4057 * pm_raise_power is deferred
4058 * because we need to release
4059 * the locks first.
4060 */
4061 hubd->h_port_state[port] |=
4062 HUBD_CHILD_RAISE_POWER;
4063 pwrup_child = B_TRUE;
4064 mutex_exit(HUBD_MUTEX(hubd));
4065
4066 /*
4067 * make sure that child
4068 * doesn't disappear
4069 */
4070 ndi_hold_devi(dip);
4071
4072 mutex_enter(HUBD_MUTEX(hubd));
4073 }
4074 }
4075 }
4076
4077 /*
4078 * Check if the port is over-current
4079 */
4080 if (change & PORT_CHANGE_OCIC) {
4081 USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG,
4082 hubd->h_log_handle,
4083 "Port%d in over current condition, "
4084 "please check the attached device to "
4085 "clear the condition. The system will "
4086 "try to recover the port, but if not "
4087 "successful, you need to re-connect "
4088 "the hub or reboot the system to bring "
4089 "the port back to work", port);
4090
4091 if (!(status & PORT_STATUS_PPS)) {
4092 /*
4093 * Try to enable port power, but
4094 * possibly fail. Ignore failure
4095 */
4096 (void) hubd_enable_port_power(hubd,
4097 port);
4098
4099 /*
4100 * Delay some time to avoid
4101 * over-current event to happen
4102 * too frequently in some cases
4103 */
4104 mutex_exit(HUBD_MUTEX(hubd));
4105 delay(drv_usectohz(500000));
4106 mutex_enter(HUBD_MUTEX(hubd));
4107 }
4108 }
4109 }
4110 }
4111
4112 /* release locks so we can do a devfs_clean */
4113 mutex_exit(HUBD_MUTEX(hubd));
4114
4115 /* delete cached dv_node's but drop locks first */
4116 ndi_devi_exit(hdip);
4117 ndi_devi_exit(rh_dip);
4118 ndi_devi_exit(ddi_get_parent(rh_dip));
4119
4120 (void) devfs_clean(rh_dip, NULL, 0);
4121
4122 /* now check if any children need onlining */
4123 if (online_child) {
4124 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4125 "hubd_hotplug_thread: onlining children");
4126
4127 (void) ndi_devi_online(hubd->h_dip, 0);
4128 }
4129
4130 /* now check if any disconnected devices need to be cleaned up */
4131 if (offline_child) {
4132 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4133 "hubd_hotplug_thread: scheduling cleanup");
4134
4135 hubd_schedule_cleanup(hubd->h_usba_device->usb_root_hub_dip);
4136 }
4137
4138 mutex_enter(HUBD_MUTEX(hubd));
4139
4140 /* now raise power on the children that have woken up */
4141 if (pwrup_child) {
4142 old_state = hubd->h_dev_state;
4143 hubd->h_dev_state = USB_DEV_HUB_CHILD_PWRLVL;
4144 for (port = 1; port <= nports; port++) {
4145 if (hubd->h_port_state[port] & HUBD_CHILD_RAISE_POWER) {
4146 dev_info_t *dip = hubd->h_children_dips[port];
4147
4148 mutex_exit(HUBD_MUTEX(hubd));
4149
4150 /* Get the device to full power */
4151 (void) pm_busy_component(dip, 0);
4152 (void) pm_raise_power(dip, 0,
4153 USB_DEV_OS_FULL_PWR);
4154 (void) pm_idle_component(dip, 0);
4155
4156 /* release the hold on the child */
4157 ndi_rele_devi(dip);
4158 mutex_enter(HUBD_MUTEX(hubd));
4159 hubd->h_port_state[port] &=
4160 ~HUBD_CHILD_RAISE_POWER;
4161 }
4162 }
4163 /*
4164 * make sure that we don't accidentally
4165 * over write the disconnect state
4166 */
4167 if (hubd->h_dev_state == USB_DEV_HUB_CHILD_PWRLVL) {
4168 hubd->h_dev_state = old_state;
4169 }
4170 }
4171
4172 /*
4173 * start polling can immediately kick off read callback
4174 * we need to set the h_hotplug_thread to 0 so that
4175 * the callback is not dropped
4176 *
4177 * if there is device during reset, still stop polling to avoid the
4178 * read callback interrupting the reset, the polling will be started
4179 * in hubd_reset_thread.
4180 */
4181 for (port = 1; port <= MAX_PORTS; port++) {
4182 if (hubd->h_reset_port[port]) {
4183
4184 break;
4185 }
4186 }
4187 if (port > MAX_PORTS) {
4188 hubd_start_polling(hubd, HUBD_ALWAYS_START_POLLING);
4189 }
4190
4191 /*
4192 * Earlier we would set the h_hotplug_thread = 0 before
4193 * polling was restarted so that
4194 * if there is any root hub status change interrupt, we can still kick
4195 * off the hotplug thread. This was valid when this interrupt was
4196 * delivered in hardware, and only ONE interrupt would be delivered.
4197 * Now that we poll on the root hub looking for status change in
4198 * software, this assignment is no longer required.
4199 */
4200 hubd->h_hotplug_thread--;
4201
4202 /* mark this device as idle */
4203 (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
4204
4205 cv_broadcast(&hubd->h_cv_hotplug_dev);
4206
4207 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4208 "hubd_hotplug_thread: exit");
4209
4210 mutex_exit(HUBD_MUTEX(hubd));
4211
4212 ndi_rele_devi(hdip);
4213 }
4214
4215
4216 /*
4217 * hubd_handle_port_connect:
4218 * Transition a port from Disabled to Enabled. Ensure that the
4219 * port is in the correct state before attempting to
4220 * access the device.
4221 */
4222 static int
hubd_handle_port_connect(hubd_t * hubd,usb_port_t port)4223 hubd_handle_port_connect(hubd_t *hubd, usb_port_t port)
4224 {
4225 int rval;
4226 int retry;
4227 long time_delay;
4228 long settling_time;
4229 uint16_t status;
4230 uint16_t change;
4231 usb_port_status_t speed;
4232 usb_addr_t hubd_usb_addr;
4233 usba_device_t *usba_device;
4234 usb_port_status_t port_status = 0;
4235 usb_port_status_t hub_port_status = 0;
4236
4237 /* Get the hub address and port status */
4238 usba_device = hubd->h_usba_device;
4239 mutex_enter(&usba_device->usb_mutex);
4240 hubd_usb_addr = usba_device->usb_addr;
4241 hub_port_status = usba_device->usb_port_status;
4242 mutex_exit(&usba_device->usb_mutex);
4243
4244 /*
4245 * If a device is connected, transition the
4246 * port from Disabled to the Enabled state.
4247 * The device will receive downstream packets
4248 * in the Enabled state.
4249 *
4250 * reset port and wait for the hub to report
4251 * completion
4252 */
4253 change = status = 0;
4254
4255 /*
4256 * According to section 9.1.2 of USB 2.0 spec, the host should
4257 * wait for atleast 100ms to allow completion of an insertion
4258 * process and for power at the device to become stable.
4259 * We wait for 200 ms
4260 */
4261 settling_time = drv_usectohz(hubd_device_delay / 5);
4262 mutex_exit(HUBD_MUTEX(hubd));
4263 delay(settling_time);
4264 mutex_enter(HUBD_MUTEX(hubd));
4265
4266 /* calculate 600 ms delay time */
4267 time_delay = (6 * drv_usectohz(hubd_device_delay)) / 10;
4268
4269 for (retry = 0; (hubd->h_dev_state == USB_DEV_ONLINE) &&
4270 (retry < hubd_retry_enumerate); retry++) {
4271 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4272 "resetting port%d, retry=%d", port, retry);
4273
4274 if ((rval = hubd_reset_port(hubd, port)) != USB_SUCCESS) {
4275 (void) hubd_determine_port_status(hubd,
4276 port, &status, &change, &speed, 0);
4277
4278 /* continue only if port is still connected */
4279 if (status & PORT_STATUS_CCS) {
4280 continue;
4281 }
4282
4283 /* carry on regardless */
4284 }
4285
4286 /*
4287 * according to USB 2.0 spec section 11.24.2.7.1.2
4288 * at the end of port reset, the hub enables the port.
4289 * But for some strange reasons, uhci port remains disabled.
4290 * And because the port remains disabled for the settling
4291 * time below, the device connected to the port gets wedged
4292 * - fails to enumerate (device not responding)
4293 * Hence, we enable it here immediately and later again after
4294 * the delay
4295 */
4296 (void) hubd_enable_port(hubd, port);
4297
4298 /* we skip this delay in the first iteration */
4299 if (retry) {
4300 /*
4301 * delay for device to signal disconnect/connect so
4302 * that hub properly recognizes the speed of the device
4303 */
4304 mutex_exit(HUBD_MUTEX(hubd));
4305 delay(settling_time);
4306 mutex_enter(HUBD_MUTEX(hubd));
4307
4308 /*
4309 * When a low speed device is connected to any port of
4310 * PPX it has to be explicitly enabled
4311 * Also, if device intentionally signals
4312 * disconnect/connect, it will disable the port.
4313 * So enable it again.
4314 */
4315 (void) hubd_enable_port(hubd, port);
4316 }
4317
4318 if ((rval = hubd_determine_port_status(hubd, port, &status,
4319 &change, &speed, 0)) != USB_SUCCESS) {
4320
4321 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4322 "getting status failed (%d)", rval);
4323
4324 (void) hubd_disable_port(hubd, port);
4325
4326 continue;
4327 }
4328
4329 if (status & PORT_STATUS_POCI) {
4330 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4331 "port %d overcurrent", port);
4332
4333 (void) hubd_disable_port(hubd, port);
4334
4335 /* ack changes */
4336 (void) hubd_determine_port_status(hubd,
4337 port, &status, &change, &speed, PORT_CHANGE_OCIC);
4338
4339 continue;
4340 }
4341
4342 /* is status really OK? */
4343 if ((status & PORT_STATUS_OK) != PORT_STATUS_OK) {
4344 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4345 "port %d status (0x%x) not OK on retry %d",
4346 port, status, retry);
4347
4348 /* check if we still have the connection */
4349 if (!(status & PORT_STATUS_CCS)) {
4350 /* lost connection, set exit condition */
4351 retry = hubd_retry_enumerate;
4352
4353 break;
4354 }
4355 } else {
4356 port_status = speed;
4357 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4358 "creating child port%d, status=0x%x "
4359 "port status=0x%x",
4360 port, status, port_status);
4361
4362 /*
4363 * if the child already exists, set addrs and config
4364 * to the device post connect event to the child
4365 */
4366 if (hubd->h_children_dips[port]) {
4367 /* set addrs to this device */
4368 rval = hubd_setdevaddr(hubd, port);
4369
4370 /*
4371 * This delay is important for the CATC hub
4372 * to enumerate. But, avoid delay in the first
4373 * iteration
4374 */
4375 if (retry) {
4376 mutex_exit(HUBD_MUTEX(hubd));
4377 delay(drv_usectohz(
4378 hubd_device_delay/100));
4379 mutex_enter(HUBD_MUTEX(hubd));
4380 }
4381
4382 if (rval == USB_SUCCESS) {
4383 /*
4384 * if the port is resetting, check if
4385 * device's descriptors have changed.
4386 */
4387 if ((hubd->h_reset_port[port]) &&
4388 (hubd_check_same_device(hubd,
4389 port) != USB_SUCCESS)) {
4390 retry = hubd_retry_enumerate;
4391
4392 break;
4393 }
4394
4395 /*
4396 * set the default config for
4397 * this device
4398 */
4399 hubd_setdevconfig(hubd, port);
4400
4401 /*
4402 * if we are doing Default reset, do
4403 * not post reconnect event since we
4404 * don't know where reset function is
4405 * called.
4406 */
4407 if (hubd->h_reset_port[port]) {
4408
4409 return (USB_SUCCESS);
4410 }
4411
4412 /*
4413 * indicate to the child that
4414 * it is online again
4415 */
4416 mutex_exit(HUBD_MUTEX(hubd));
4417 hubd_post_event(hubd, port,
4418 USBA_EVENT_TAG_HOT_INSERTION);
4419 mutex_enter(HUBD_MUTEX(hubd));
4420
4421 return (USB_SUCCESS);
4422 }
4423 } else {
4424 /*
4425 * We need to release access here
4426 * so that busctls on other ports can
4427 * continue and don't cause a deadlock
4428 * when busctl and removal of prom node
4429 * takes concurrently. This also ensures
4430 * busctls for attach of successfully
4431 * enumerated devices on other ports can
4432 * continue concurrently with the process
4433 * of enumerating the new devices. This
4434 * reduces the overall boot time of the system.
4435 */
4436 rval = hubd_create_child(hubd->h_dip,
4437 hubd,
4438 hubd->h_usba_device,
4439 port_status, port,
4440 retry);
4441 if (rval == USB_SUCCESS) {
4442 usba_update_hotplug_stats(hubd->h_dip,
4443 USBA_TOTAL_HOTPLUG_SUCCESS|
4444 USBA_HOTPLUG_SUCCESS);
4445 hubd->h_total_hotplug_success++;
4446
4447 if (retry > 0) {
4448 USB_DPRINTF_L2(
4449 DPRINT_MASK_HOTPLUG,
4450 hubd->h_log_handle,
4451 "device on port %d "
4452 "enumerated after %d %s",
4453 port, retry,
4454 (retry > 1) ? "retries" :
4455 "retry");
4456
4457 }
4458
4459 return (USB_SUCCESS);
4460 }
4461 }
4462 }
4463
4464 /* wait a while until it settles? */
4465 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4466 "disabling port %d again", port);
4467
4468 (void) hubd_disable_port(hubd, port);
4469 if (retry) {
4470 mutex_exit(HUBD_MUTEX(hubd));
4471 delay(time_delay);
4472 mutex_enter(HUBD_MUTEX(hubd));
4473 }
4474
4475 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4476 "retrying on port %d", port);
4477 }
4478
4479 if (retry >= hubd_retry_enumerate) {
4480 /*
4481 * If it is a High Speed Root Hub and connected device
4482 * Is a Low/Full Speed, it will be handled by USB 1.1
4483 * Host Controller. In this case, USB 2.0 Host Controller
4484 * will transfer the ownership of this port to USB 1.1
4485 * Host Controller. So don't display any error message on
4486 * the console. Note, this isn't the case for USB 3.x.
4487 */
4488 if ((hubd_usb_addr == ROOT_HUB_ADDR) &&
4489 (hub_port_status == USBA_HIGH_SPEED_DEV) &&
4490 (port_status != USBA_HIGH_SPEED_DEV)) {
4491 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
4492 hubd->h_log_handle,
4493 "hubd_handle_port_connect: Low/Full speed "
4494 "device is connected to High Speed root hub");
4495 } else {
4496 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
4497 hubd->h_log_handle,
4498 "Connecting device on port %d failed", port);
4499 }
4500
4501 (void) hubd_disable_port(hubd, port);
4502 usba_update_hotplug_stats(hubd->h_dip,
4503 USBA_TOTAL_HOTPLUG_FAILURE|USBA_HOTPLUG_FAILURE);
4504 hubd->h_total_hotplug_failure++;
4505
4506 /*
4507 * the port should be automagically
4508 * disabled but just in case, we do
4509 * it here
4510 */
4511 (void) hubd_disable_port(hubd, port);
4512
4513 /* ack all changes because we disabled this port */
4514 (void) hubd_determine_port_status(hubd,
4515 port, &status, &change, NULL, HUBD_ACK_ALL_CHANGES);
4516
4517 }
4518
4519 return (USB_FAILURE);
4520 }
4521
4522
4523 /*
4524 * hubd_get_hub_status:
4525 */
4526 static int
hubd_get_hub_status(hubd_t * hubd)4527 hubd_get_hub_status(hubd_t *hubd)
4528 {
4529 int rval;
4530 usb_cr_t completion_reason;
4531 usb_cb_flags_t cb_flags;
4532 uint16_t stword[2];
4533 uint16_t status;
4534 uint16_t change;
4535 usb_cfg_descr_t cfg_descr;
4536 size_t cfg_length;
4537 uchar_t *usb_cfg;
4538 uint8_t MaxPower;
4539 usb_port_t port;
4540
4541 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4542 "hubd_get_hub_status:");
4543
4544 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
4545
4546 if ((hubd_get_hub_status_words(hubd, stword)) != USB_SUCCESS) {
4547
4548 return (USB_FAILURE);
4549 }
4550 status = stword[0];
4551 change = stword[1];
4552
4553 mutex_exit(HUBD_MUTEX(hubd));
4554
4555 /* Obtain the raw configuration descriptor */
4556 usb_cfg = usb_get_raw_cfg_data(hubd->h_dip, &cfg_length);
4557
4558 /* get configuration descriptor */
4559 rval = usb_parse_cfg_descr(usb_cfg, cfg_length,
4560 &cfg_descr, USB_CFG_DESCR_SIZE);
4561
4562 if (rval != USB_CFG_DESCR_SIZE) {
4563
4564 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4565 "get hub configuration descriptor failed.");
4566
4567 mutex_enter(HUBD_MUTEX(hubd));
4568
4569 return (USB_FAILURE);
4570 } else {
4571 MaxPower = cfg_descr.bMaxPower;
4572 }
4573
4574 /* check if local power status changed. */
4575 if (change & C_HUB_LOCAL_POWER_STATUS) {
4576
4577 /*
4578 * local power has been lost, check the maximum
4579 * power consumption of current configuration.
4580 * see USB2.0 spec Table 11-12.
4581 */
4582 if (status & HUB_LOCAL_POWER_STATUS) {
4583
4584 if (MaxPower == 0) {
4585
4586 /*
4587 * Self-powered only hub. Because it could
4588 * not draw any power from USB bus.
4589 * It can't work well on this condition.
4590 */
4591 USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG,
4592 hubd->h_log_handle,
4593 "local power has been lost, "
4594 "please disconnect hub");
4595 } else {
4596
4597 /*
4598 * Bus-powered only or self/bus-powered hub.
4599 */
4600 USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG,
4601 hubd->h_log_handle,
4602 "local power has been lost,"
4603 "the hub could draw %d"
4604 " mA power from the USB bus.",
4605 2*MaxPower);
4606 }
4607
4608 }
4609
4610 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4611 "clearing feature C_HUB_LOCAL_POWER ");
4612
4613 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
4614 hubd->h_default_pipe,
4615 HUB_HANDLE_HUB_FEATURE_TYPE,
4616 USB_REQ_CLEAR_FEATURE,
4617 CFS_C_HUB_LOCAL_POWER,
4618 0,
4619 0,
4620 NULL, 0,
4621 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
4622 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
4623 hubd->h_log_handle,
4624 "clear feature C_HUB_LOCAL_POWER "
4625 "failed (%d 0x%x %d)",
4626 rval, completion_reason, cb_flags);
4627 }
4628
4629 }
4630
4631 if (change & C_HUB_OVER_CURRENT) {
4632
4633 if (status & HUB_OVER_CURRENT) {
4634
4635 if (usba_is_root_hub(hubd->h_dip)) {
4636 /*
4637 * The root hub should be automatically
4638 * recovered when over-current condition is
4639 * cleared. But there might be exception and
4640 * need user interaction to recover.
4641 */
4642 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
4643 hubd->h_log_handle,
4644 "Root hub over current condition, "
4645 "please check your system to clear the "
4646 "condition as soon as possible. And you "
4647 "may need to reboot the system to bring "
4648 "the root hub back to work if it cannot "
4649 "recover automatically");
4650 } else {
4651 /*
4652 * The driver would try to recover port power
4653 * on over current condition. When the recovery
4654 * fails, the user may still need to offline
4655 * this hub in order to recover.
4656 * The port power is automatically disabled,
4657 * so we won't see disconnects.
4658 */
4659 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
4660 hubd->h_log_handle,
4661 "Hub global over current condition, "
4662 "please disconnect the devices connected "
4663 "to the hub to clear the condition. And "
4664 "you may need to re-connect the hub if "
4665 "the ports do not work");
4666 }
4667 }
4668
4669 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4670 "clearing feature C_HUB_OVER_CURRENT");
4671
4672 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
4673 hubd->h_default_pipe,
4674 HUB_HANDLE_HUB_FEATURE_TYPE,
4675 USB_REQ_CLEAR_FEATURE,
4676 CFS_C_HUB_OVER_CURRENT,
4677 0,
4678 0,
4679 NULL, 0,
4680 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
4681 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
4682 hubd->h_log_handle,
4683 "clear feature C_HUB_OVER_CURRENT "
4684 "failed (%d 0x%x %d)",
4685 rval, completion_reason, cb_flags);
4686 }
4687
4688 /*
4689 * Try to recover all port power if they are turned off.
4690 * Don't do this for root hub, but rely on the root hub
4691 * to recover itself.
4692 */
4693 if (!usba_is_root_hub(hubd->h_dip)) {
4694
4695 mutex_enter(HUBD_MUTEX(hubd));
4696
4697 /*
4698 * Only check the power status of the 1st port
4699 * since all port power status should be the same.
4700 */
4701 (void) hubd_determine_port_status(hubd, 1, &status,
4702 &change, NULL, 0);
4703
4704 if (status & PORT_STATUS_PPS) {
4705 return (USB_SUCCESS);
4706 }
4707
4708 for (port = 1; port <= hubd->h_nports; port++) {
4709 (void) hubd_enable_port_power(hubd, port);
4710 }
4711
4712 mutex_exit(HUBD_MUTEX(hubd));
4713
4714 /*
4715 * Delay some time to avoid over-current event
4716 * to happen too frequently in some cases
4717 */
4718 delay(drv_usectohz(500000));
4719 }
4720 }
4721
4722 mutex_enter(HUBD_MUTEX(hubd));
4723
4724 return (USB_SUCCESS);
4725 }
4726
4727 /*
4728 * Convert a series of USB status requests from USB 2 and USB 3 into a single
4729 * uniform type. We separate out the speed into its own value from both USB 2
4730 * and USB 3 and from there we transform the status to look like a USB 2 one.
4731 */
4732 static void
hubd_status_uniform(hubd_t * hubd,usb_port_t port,uint16_t * status,usb_port_status_t * speed)4733 hubd_status_uniform(hubd_t *hubd, usb_port_t port, uint16_t *status,
4734 usb_port_status_t *speed)
4735 {
4736 uint16_t os = *status;
4737
4738 hubd->h_port_raw[port] = os;
4739
4740 if (hubd->h_usba_device->usb_port_status >= USBA_SUPER_SPEED_DEV) {
4741 /*
4742 * USB 3 devices are always at super speed when plugged into a
4743 * super speed hub. However, this is only true if we're talking
4744 * about actual hubs. This doesn't hold for the root hub, which
4745 * can support USB 3.x, USB 2.x, and USB 1.x devices operating
4746 * at different speeds. To handle this, the USB 3 HCD driver
4747 * (xhci) uses some of the extra status bits to stash the
4748 * current device's detected speed.
4749 */
4750 if (usba_is_root_hub(hubd->h_dip)) {
4751 if (speed != NULL) {
4752 *speed = (os & PORT_STATUS_SPMASK_SS) >>
4753 PORT_STATUS_SPSHIFT_SS;
4754 }
4755 } else {
4756 if (speed != NULL)
4757 *speed = USBA_SUPER_SPEED_DEV;
4758 }
4759
4760 if (os & PORT_STATUS_PPS_SS) {
4761 os &= ~PORT_STATUS_PPS_SS;
4762 os |= PORT_STATUS_PPS;
4763 *status = os;
4764 }
4765 } else {
4766 /*
4767 * For USB 2, the only thing we need to do is transform the
4768 * speed.
4769 */
4770 if (speed == NULL)
4771 return;
4772
4773 if (os & PORT_STATUS_HSDA)
4774 *speed = USBA_HIGH_SPEED_DEV;
4775 else if (os & PORT_STATUS_LSDA)
4776 *speed = USBA_LOW_SPEED_DEV;
4777 else
4778 *speed = USBA_FULL_SPEED_DEV;
4779 }
4780 }
4781
4782
4783 /*
4784 * Attempt to reset a port. This feels a bit more complicated than it should be
4785 * in part due to how HCD, change status notifications, and the hotplug thread
4786 * might interact. Basically we try to block port changes by using the
4787 * h_port_reset_wait which says we should get signalled rather than kicking off
4788 * the hotplug thread. We'll give this a shot for about 100ms at best.
4789 */
4790 static int
hubd_reset_port(hubd_t * hubd,usb_port_t port)4791 hubd_reset_port(hubd_t *hubd, usb_port_t port)
4792 {
4793 int rval;
4794 usb_cr_t completion_reason;
4795 usb_cb_flags_t cb_flags;
4796 usb_port_mask_t port_mask = 1 << port;
4797 mblk_t *data;
4798 uint16_t status;
4799 uint16_t change;
4800 clock_t delta;
4801 boolean_t first;
4802
4803 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
4804 "hubd_reset_port: port=%d", port);
4805
4806 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
4807
4808 hubd->h_port_reset_wait |= port_mask;
4809
4810 mutex_exit(HUBD_MUTEX(hubd));
4811
4812 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
4813 hubd->h_default_pipe,
4814 HUB_HANDLE_PORT_FEATURE_TYPE,
4815 USB_REQ_SET_FEATURE,
4816 CFS_PORT_RESET,
4817 port,
4818 0,
4819 NULL, 0,
4820 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
4821 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
4822 "reset port%d failed (%d 0x%x %d)",
4823 port, completion_reason, cb_flags, rval);
4824
4825 mutex_enter(HUBD_MUTEX(hubd));
4826
4827 return (USB_FAILURE);
4828 }
4829
4830 mutex_enter(HUBD_MUTEX(hubd));
4831
4832 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
4833 "waiting on cv for reset completion");
4834
4835 /*
4836 * wait for port status change event
4837 */
4838 delta = drv_usectohz(hubd_device_delay / 10);
4839
4840 first = B_TRUE;
4841 for (;;) {
4842 if (delta < 0) {
4843 rval = USB_FAILURE;
4844 break;
4845 }
4846
4847 if (first == B_FALSE)
4848 hubd->h_port_reset_wait |= port_mask;
4849 else
4850 first = B_FALSE;
4851
4852 hubd_start_polling(hubd, HUBD_ALWAYS_START_POLLING);
4853
4854 /*
4855 * Regardless of the status, we always check to see if the port
4856 * has been reset.
4857 */
4858 delta = cv_reltimedwait(&hubd->h_cv_reset_port,
4859 &hubd->h_mutex, delta, TR_CLOCK_TICK);
4860 if (delta < 0)
4861 hubd->h_port_reset_wait &= ~port_mask;
4862
4863 hubd_stop_polling(hubd);
4864
4865 data = NULL;
4866
4867 /* check status to determine whether reset completed */
4868 mutex_exit(HUBD_MUTEX(hubd));
4869 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
4870 hubd->h_default_pipe,
4871 HUB_GET_PORT_STATUS_TYPE,
4872 USB_REQ_GET_STATUS,
4873 0,
4874 port,
4875 GET_STATUS_LENGTH,
4876 &data, 0,
4877 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
4878 USB_DPRINTF_L2(DPRINT_MASK_PORT,
4879 hubd->h_log_handle,
4880 "get status port%d failed (%d 0x%x %d)",
4881 port, completion_reason, cb_flags, rval);
4882
4883 if (data) {
4884 freemsg(data);
4885 data = NULL;
4886 }
4887 mutex_enter(HUBD_MUTEX(hubd));
4888
4889 continue;
4890 }
4891
4892 status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr);
4893 change = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2);
4894
4895 freemsg(data);
4896
4897 hubd_status_uniform(hubd, port, &status, NULL);
4898
4899 /* continue only if port is still connected */
4900 if (!(status & PORT_STATUS_CCS)) {
4901
4902 /* lost connection, set exit condition */
4903 delta = -1;
4904
4905 mutex_enter(HUBD_MUTEX(hubd));
4906
4907 break;
4908 }
4909
4910 if (status & PORT_STATUS_PRS) {
4911 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
4912 "port%d reset active", port);
4913 mutex_enter(HUBD_MUTEX(hubd));
4914
4915 continue;
4916 } else {
4917 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
4918 "port%d reset inactive", port);
4919 }
4920
4921 if (change & PORT_CHANGE_PRSC) {
4922 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
4923 "clearing feature CFS_C_PORT_RESET");
4924
4925 if (usb_pipe_sync_ctrl_xfer(hubd->h_dip,
4926 hubd->h_default_pipe,
4927 HUB_HANDLE_PORT_FEATURE_TYPE,
4928 USB_REQ_CLEAR_FEATURE,
4929 CFS_C_PORT_RESET,
4930 port,
4931 0,
4932 NULL, 0,
4933 &completion_reason, &cb_flags, 0) != USB_SUCCESS) {
4934 USB_DPRINTF_L2(DPRINT_MASK_PORT,
4935 hubd->h_log_handle,
4936 "clear feature CFS_C_PORT_RESET"
4937 " port%d failed (%d 0x%x %d)",
4938 port, completion_reason, cb_flags, rval);
4939 }
4940 }
4941
4942 /*
4943 * In addition to a normal reset, a warm reset may have
4944 * happened. Acknowledge that as well.
4945 */
4946 if (change & PORT_CHANGE_BHPR) {
4947 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
4948 "clearing feature CFS_C_BH_PORT_RESET");
4949
4950 if (usb_pipe_sync_ctrl_xfer(hubd->h_dip,
4951 hubd->h_default_pipe,
4952 HUB_HANDLE_PORT_FEATURE_TYPE,
4953 USB_REQ_CLEAR_FEATURE,
4954 CFS_C_BH_PORT_RESET,
4955 port,
4956 0,
4957 NULL, 0,
4958 &completion_reason, &cb_flags, 0) != USB_SUCCESS) {
4959 USB_DPRINTF_L2(DPRINT_MASK_PORT,
4960 hubd->h_log_handle,
4961 "clear feature CFS_C_BH_PORT_RESET"
4962 " port%d failed (%d 0x%x %d)",
4963 port, completion_reason, cb_flags, rval);
4964 }
4965 }
4966
4967 rval = USB_SUCCESS;
4968 mutex_enter(HUBD_MUTEX(hubd));
4969
4970 break;
4971 }
4972
4973 return (rval);
4974 }
4975
4976
4977 /*
4978 * hubd_enable_port:
4979 * this may fail if the hub as been disconnected
4980 */
4981 static int
hubd_enable_port(hubd_t * hubd,usb_port_t port)4982 hubd_enable_port(hubd_t *hubd, usb_port_t port)
4983 {
4984 int rval;
4985 usb_cr_t completion_reason;
4986 usb_cb_flags_t cb_flags;
4987
4988 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
4989 "hubd_enable_port: port=%d", port);
4990
4991 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
4992
4993 mutex_exit(HUBD_MUTEX(hubd));
4994
4995 /* Do not issue a SetFeature(PORT_ENABLE) on external hubs */
4996 if (!usba_is_root_hub(hubd->h_dip)) {
4997 mutex_enter(HUBD_MUTEX(hubd));
4998
4999 return (USB_SUCCESS);
5000 }
5001
5002 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5003 hubd->h_default_pipe,
5004 HUB_HANDLE_PORT_FEATURE_TYPE,
5005 USB_REQ_SET_FEATURE,
5006 CFS_PORT_ENABLE,
5007 port,
5008 0,
5009 NULL, 0,
5010 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
5011 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5012 "enable port%d failed (%d 0x%x %d)",
5013 port, completion_reason, cb_flags, rval);
5014 }
5015
5016 mutex_enter(HUBD_MUTEX(hubd));
5017
5018 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5019 "enabling port done");
5020
5021 return (rval);
5022 }
5023
5024
5025 /*
5026 * hubd_disable_port
5027 */
5028 static int
hubd_disable_port(hubd_t * hubd,usb_port_t port)5029 hubd_disable_port(hubd_t *hubd, usb_port_t port)
5030 {
5031 int rval;
5032 usb_cr_t completion_reason;
5033 usb_cb_flags_t cb_flags;
5034
5035 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5036 "hubd_disable_port: port=%d", port);
5037
5038 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
5039
5040 mutex_exit(HUBD_MUTEX(hubd));
5041
5042 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5043 hubd->h_default_pipe,
5044 HUB_HANDLE_PORT_FEATURE_TYPE,
5045 USB_REQ_CLEAR_FEATURE,
5046 CFS_PORT_ENABLE,
5047 port,
5048 0,
5049 NULL, 0,
5050 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
5051 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5052 "disable port%d failed (%d 0x%x %d)", port,
5053 completion_reason, cb_flags, rval);
5054 mutex_enter(HUBD_MUTEX(hubd));
5055
5056 return (USB_FAILURE);
5057 }
5058
5059 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5060 "clearing feature CFS_C_PORT_ENABLE");
5061
5062 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5063 hubd->h_default_pipe,
5064 HUB_HANDLE_PORT_FEATURE_TYPE,
5065 USB_REQ_CLEAR_FEATURE,
5066 CFS_C_PORT_ENABLE,
5067 port,
5068 0,
5069 NULL, 0,
5070 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
5071 USB_DPRINTF_L2(DPRINT_MASK_PORT,
5072 hubd->h_log_handle,
5073 "clear feature CFS_C_PORT_ENABLE port%d failed "
5074 "(%d 0x%x %d)",
5075 port, completion_reason, cb_flags, rval);
5076
5077 mutex_enter(HUBD_MUTEX(hubd));
5078
5079 return (USB_FAILURE);
5080 }
5081
5082 mutex_enter(HUBD_MUTEX(hubd));
5083
5084 return (USB_SUCCESS);
5085 }
5086
5087
5088 /*
5089 * hubd_determine_port_status:
5090 */
5091 static int
hubd_determine_port_status(hubd_t * hubd,usb_port_t port,uint16_t * status,uint16_t * change,usb_port_status_t * speed,uint_t ack_flag)5092 hubd_determine_port_status(hubd_t *hubd, usb_port_t port, uint16_t *status,
5093 uint16_t *change, usb_port_status_t *speed, uint_t ack_flag)
5094 {
5095 int rval;
5096 mblk_t *data = NULL;
5097 usb_cr_t completion_reason;
5098 usb_cb_flags_t cb_flags;
5099 uint16_t st, ch;
5100 usb_port_status_t sp;
5101
5102 if (status == NULL)
5103 status = &st;
5104 if (change == NULL)
5105 change = &ch;
5106 if (speed == NULL)
5107 speed = &sp;
5108
5109 *status = *change = 0;
5110 *speed = 0;
5111
5112 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5113 "hubd_determine_port_status: port=%d, state=0x%x ack=0x%x", port,
5114 hubd->h_port_state[port], ack_flag);
5115
5116 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
5117
5118 mutex_exit(HUBD_MUTEX(hubd));
5119
5120 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5121 hubd->h_default_pipe,
5122 HUB_GET_PORT_STATUS_TYPE,
5123 USB_REQ_GET_STATUS,
5124 0,
5125 port,
5126 GET_STATUS_LENGTH,
5127 &data, 0,
5128 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
5129 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5130 "port=%d get status failed (%d 0x%x %d)",
5131 port, completion_reason, cb_flags, rval);
5132
5133 if (data) {
5134 freemsg(data);
5135 }
5136
5137 mutex_enter(HUBD_MUTEX(hubd));
5138
5139 return (rval);
5140 }
5141
5142 mutex_enter(HUBD_MUTEX(hubd));
5143 if (MBLKL(data) != GET_STATUS_LENGTH) {
5144 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5145 "port %d: length incorrect %ld",
5146 port, MBLKL(data));
5147 freemsg(data);
5148
5149 return (rval);
5150 }
5151
5152
5153 *status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr);
5154 *change = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2);
5155 hubd_status_uniform(hubd, port, status, speed);
5156
5157 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5158 "port%d status=0x%x, change=0x%x", port, *status, *change);
5159
5160 freemsg(data);
5161
5162 if (*status & PORT_STATUS_CCS) {
5163 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5164 "port%d connected", port);
5165
5166 hubd->h_port_state[port] |= (PORT_STATUS_CCS & ack_flag);
5167 } else {
5168 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5169 "port%d disconnected", port);
5170
5171 hubd->h_port_state[port] &= ~(PORT_STATUS_CCS & ack_flag);
5172 }
5173
5174 if (*status & PORT_STATUS_PES) {
5175 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5176 "port%d enabled", port);
5177
5178 hubd->h_port_state[port] |= (PORT_STATUS_PES & ack_flag);
5179 } else {
5180 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5181 "port%d disabled", port);
5182
5183 hubd->h_port_state[port] &= ~(PORT_STATUS_PES & ack_flag);
5184 }
5185
5186 if (*status & PORT_STATUS_PSS) {
5187 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5188 "port%d suspended", port);
5189
5190 hubd->h_port_state[port] |= (PORT_STATUS_PSS & ack_flag);
5191 } else {
5192 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5193 "port%d not suspended", port);
5194
5195 hubd->h_port_state[port] &= ~(PORT_STATUS_PSS & ack_flag);
5196 }
5197
5198 if (*change & PORT_CHANGE_PRSC) {
5199 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5200 "port%d reset completed", port);
5201
5202 hubd->h_port_state[port] |= (PORT_CHANGE_PRSC & ack_flag);
5203 } else {
5204
5205 hubd->h_port_state[port] &= ~(PORT_CHANGE_PRSC & ack_flag);
5206 }
5207
5208 if (*status & PORT_STATUS_POCI) {
5209 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5210 "port%d overcurrent!", port);
5211
5212 hubd->h_port_state[port] |= (PORT_STATUS_POCI & ack_flag);
5213 } else {
5214
5215 hubd->h_port_state[port] &= ~(PORT_STATUS_POCI & ack_flag);
5216 }
5217
5218 if (*status & PORT_STATUS_PRS) {
5219 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5220 "port%d reset active", port);
5221
5222 hubd->h_port_state[port] |= (PORT_STATUS_PRS & ack_flag);
5223 } else {
5224 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5225 "port%d reset inactive", port);
5226
5227 hubd->h_port_state[port] &= ~(PORT_STATUS_PRS & ack_flag);
5228 }
5229 if (*status & PORT_STATUS_PPS) {
5230 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5231 "port%d power on", port);
5232
5233 hubd->h_port_state[port] |= (PORT_STATUS_PPS & ack_flag);
5234 } else {
5235 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5236 "port%d power off", port);
5237
5238 hubd->h_port_state[port] &= ~(PORT_STATUS_PPS & ack_flag);
5239 }
5240
5241 /*
5242 * Acknowledge connection, enable, reset status
5243 */
5244 if (ack_flag) {
5245 mutex_exit(HUBD_MUTEX(hubd));
5246 if (*change & PORT_CHANGE_CSC & ack_flag) {
5247 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5248 "clearing feature CFS_C_PORT_CONNECTION");
5249 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5250 hubd->h_default_pipe,
5251 HUB_HANDLE_PORT_FEATURE_TYPE,
5252 USB_REQ_CLEAR_FEATURE,
5253 CFS_C_PORT_CONNECTION,
5254 port,
5255 0, NULL, 0,
5256 &completion_reason, &cb_flags, 0)) !=
5257 USB_SUCCESS) {
5258 USB_DPRINTF_L2(DPRINT_MASK_PORT,
5259 hubd->h_log_handle,
5260 "clear feature CFS_C_PORT_CONNECTION"
5261 " port%d failed (%d 0x%x %d)",
5262 port, completion_reason, cb_flags, rval);
5263 }
5264 }
5265 if (*change & PORT_CHANGE_PESC & ack_flag) {
5266 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5267 "clearing feature CFS_C_PORT_ENABLE");
5268 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5269 hubd->h_default_pipe,
5270 HUB_HANDLE_PORT_FEATURE_TYPE,
5271 USB_REQ_CLEAR_FEATURE,
5272 CFS_C_PORT_ENABLE,
5273 port,
5274 0, NULL, 0,
5275 &completion_reason, &cb_flags, 0)) !=
5276 USB_SUCCESS) {
5277 USB_DPRINTF_L2(DPRINT_MASK_PORT,
5278 hubd->h_log_handle,
5279 "clear feature CFS_C_PORT_ENABLE"
5280 " port%d failed (%d 0x%x %d)",
5281 port, completion_reason, cb_flags, rval);
5282 }
5283 }
5284 if (*change & PORT_CHANGE_PSSC & ack_flag) {
5285 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5286 "clearing feature CFS_C_PORT_SUSPEND");
5287
5288 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5289 hubd->h_default_pipe,
5290 HUB_HANDLE_PORT_FEATURE_TYPE,
5291 USB_REQ_CLEAR_FEATURE,
5292 CFS_C_PORT_SUSPEND,
5293 port,
5294 0, NULL, 0,
5295 &completion_reason, &cb_flags, 0)) !=
5296 USB_SUCCESS) {
5297 USB_DPRINTF_L2(DPRINT_MASK_PORT,
5298 hubd->h_log_handle,
5299 "clear feature CFS_C_PORT_SUSPEND"
5300 " port%d failed (%d 0x%x %d)",
5301 port, completion_reason, cb_flags, rval);
5302 }
5303 }
5304 if (*change & PORT_CHANGE_OCIC & ack_flag) {
5305 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5306 "clearing feature CFS_C_PORT_OVER_CURRENT");
5307
5308 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5309 hubd->h_default_pipe,
5310 HUB_HANDLE_PORT_FEATURE_TYPE,
5311 USB_REQ_CLEAR_FEATURE,
5312 CFS_C_PORT_OVER_CURRENT,
5313 port,
5314 0, NULL, 0,
5315 &completion_reason, &cb_flags, 0)) !=
5316 USB_SUCCESS) {
5317 USB_DPRINTF_L2(DPRINT_MASK_PORT,
5318 hubd->h_log_handle,
5319 "clear feature CFS_C_PORT_OVER_CURRENT"
5320 " port%d failed (%d 0x%x %d)",
5321 port, completion_reason, cb_flags, rval);
5322 }
5323 }
5324 if (*change & PORT_CHANGE_PRSC & ack_flag) {
5325 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5326 "clearing feature CFS_C_PORT_RESET");
5327 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5328 hubd->h_default_pipe,
5329 HUB_HANDLE_PORT_FEATURE_TYPE,
5330 USB_REQ_CLEAR_FEATURE,
5331 CFS_C_PORT_RESET,
5332 port,
5333 0, NULL, 0,
5334 &completion_reason, &cb_flags, 0)) !=
5335 USB_SUCCESS) {
5336 USB_DPRINTF_L2(DPRINT_MASK_PORT,
5337 hubd->h_log_handle,
5338 "clear feature CFS_C_PORT_RESET"
5339 " port%d failed (%d 0x%x %d)",
5340 port, completion_reason, cb_flags, rval);
5341 }
5342 }
5343 if (*change & PORT_CHANGE_BHPR & ack_flag) {
5344 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5345 "clearing feature CFS_C_BH_PORT_RESET");
5346 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5347 hubd->h_default_pipe,
5348 HUB_HANDLE_PORT_FEATURE_TYPE,
5349 USB_REQ_CLEAR_FEATURE,
5350 CFS_C_BH_PORT_RESET,
5351 port,
5352 0, NULL, 0,
5353 &completion_reason, &cb_flags, 0)) !=
5354 USB_SUCCESS) {
5355 USB_DPRINTF_L2(DPRINT_MASK_PORT,
5356 hubd->h_log_handle,
5357 "clear feature CFS_C_BH_PORT_RESET"
5358 " port%d failed (%d 0x%x %d)",
5359 port, completion_reason, cb_flags, rval);
5360 }
5361 }
5362 if (*change & PORT_CHANGE_PLSC & ack_flag) {
5363 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5364 "clearing feature CFS_C_PORT_LINK_STATE");
5365 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5366 hubd->h_default_pipe,
5367 HUB_HANDLE_PORT_FEATURE_TYPE,
5368 USB_REQ_CLEAR_FEATURE,
5369 CFS_C_PORT_LINK_STATE,
5370 port,
5371 0, NULL, 0,
5372 &completion_reason, &cb_flags, 0)) !=
5373 USB_SUCCESS) {
5374 USB_DPRINTF_L2(DPRINT_MASK_PORT,
5375 hubd->h_log_handle,
5376 "clear feature CFS_C_PORT_LINK_STATE"
5377 " port%d failed (%d 0x%x %d)",
5378 port, completion_reason, cb_flags, rval);
5379 }
5380 }
5381 if (*change & PORT_CHANGE_PCE & ack_flag) {
5382 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5383 "clearing feature CFS_C_PORT_CONFIG_ERROR");
5384 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5385 hubd->h_default_pipe,
5386 HUB_HANDLE_PORT_FEATURE_TYPE,
5387 USB_REQ_CLEAR_FEATURE,
5388 CFS_C_PORT_CONFIG_ERROR,
5389 port,
5390 0, NULL, 0,
5391 &completion_reason, &cb_flags, 0)) !=
5392 USB_SUCCESS) {
5393 USB_DPRINTF_L2(DPRINT_MASK_PORT,
5394 hubd->h_log_handle,
5395 "clear feature CFS_C_PORT_CONFIG_ERROR"
5396 " port%d failed (%d 0x%x %d)",
5397 port, completion_reason, cb_flags, rval);
5398 }
5399 }
5400 mutex_enter(HUBD_MUTEX(hubd));
5401 }
5402
5403 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5404 "new port%d state 0x%x", port, hubd->h_port_state[port]);
5405
5406
5407 return (USB_SUCCESS);
5408 }
5409
5410
5411 /*
5412 * hubd_recover_disabled_port
5413 * if the port got disabled because of an error
5414 * enable it. If hub doesn't suport enable port,
5415 * reset the port to bring the device to life again
5416 */
5417 static int
hubd_recover_disabled_port(hubd_t * hubd,usb_port_t port)5418 hubd_recover_disabled_port(hubd_t *hubd, usb_port_t port)
5419 {
5420 uint16_t status;
5421 uint16_t change;
5422 int rval = USB_FAILURE;
5423
5424 /* first try enabling the port */
5425 (void) hubd_enable_port(hubd, port);
5426
5427 /* read the port status */
5428 (void) hubd_determine_port_status(hubd, port, &status, &change, NULL,
5429 PORT_CHANGE_PESC);
5430
5431 if (status & PORT_STATUS_PES) {
5432 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5433 "Port%d now Enabled", port);
5434 } else if (status & PORT_STATUS_CCS) {
5435 /* first post a disconnect event to the child */
5436 mutex_exit(HUBD_MUTEX(hubd));
5437 hubd_post_event(hubd, port, USBA_EVENT_TAG_HOT_REMOVAL);
5438 mutex_enter(HUBD_MUTEX(hubd));
5439
5440 /* then reset the port and recover the device */
5441 rval = hubd_handle_port_connect(hubd, port);
5442
5443 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5444 "Port%d now Enabled by force", port);
5445 }
5446
5447 return (rval);
5448 }
5449
5450
5451 /*
5452 * hubd_enable_all_port_power:
5453 */
5454 static int
hubd_enable_all_port_power(hubd_t * hubd)5455 hubd_enable_all_port_power(hubd_t *hubd)
5456 {
5457 int wait;
5458 usb_port_t port;
5459 uint_t retry;
5460 uint16_t status;
5461 uint16_t change;
5462
5463 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5464 "hubd_enable_all_port_power");
5465
5466 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
5467
5468 /*
5469 * According to section 11.11 of USB, for hubs with no power
5470 * switches, bPwrOn2PwrGood is zero. But we wait for some
5471 * arbitrary time to enable power to become stable.
5472 *
5473 * If an hub supports port power switching, we need to wait
5474 * at least 20ms before accessing corresponding usb port. Note,
5475 * this member is stored in the h_power_good member.
5476 */
5477 if ((hubd->h_hub_chars & HUB_CHARS_NO_POWER_SWITCHING) ||
5478 (hubd->h_power_good == 0)) {
5479 wait = hubd_device_delay / 10;
5480 } else {
5481 wait = max(HUB_DEFAULT_POPG,
5482 hubd->h_power_good) * 2 * 1000;
5483 }
5484
5485 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5486 "hubd_enable_all_port_power: popg=%d wait=%d",
5487 hubd->h_power_good, wait);
5488
5489 /*
5490 * Enable power per port. we ignore gang power and power mask
5491 * and always enable all ports one by one.
5492 */
5493 for (port = 1; port <= hubd->h_nports; port++) {
5494 /*
5495 * Transition the port from the Powered Off to the
5496 * Disconnected state by supplying power to the port.
5497 */
5498 USB_DPRINTF_L4(DPRINT_MASK_PORT,
5499 hubd->h_log_handle,
5500 "hubd_enable_all_port_power: power port=%d", port);
5501
5502 (void) hubd_enable_port_power(hubd, port);
5503 }
5504
5505 mutex_exit(HUBD_MUTEX(hubd));
5506 delay(drv_usectohz(wait));
5507 mutex_enter(HUBD_MUTEX(hubd));
5508
5509 /* For retry if any, use some extra delay */
5510 wait = max(wait, hubd_device_delay / 10);
5511
5512 /* Check each port power status for a given usb hub */
5513 for (port = 1; port <= hubd->h_nports; port++) {
5514
5515 /* Get port status */
5516 (void) hubd_determine_port_status(hubd, port,
5517 &status, &change, NULL, 0);
5518
5519 for (retry = 0; ((!(status & PORT_STATUS_PPS)) &&
5520 (retry < HUBD_PORT_RETRY)); retry++) {
5521
5522 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5523 "Retry is in progress %d: port %d status %d",
5524 retry, port, status);
5525
5526 (void) hubd_enable_port_power(hubd, port);
5527
5528 mutex_exit(HUBD_MUTEX(hubd));
5529 delay(drv_usectohz(wait));
5530 mutex_enter(HUBD_MUTEX(hubd));
5531
5532 /* Get port status */
5533 (void) hubd_determine_port_status(hubd, port,
5534 &status, &change, NULL, 0);
5535 }
5536
5537 /* Print warning message if port has no power */
5538 if (!(status & PORT_STATUS_PPS)) {
5539
5540 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5541 "hubd_enable_all_port_power: port %d power-on "
5542 "failed, port status 0x%x", port, status);
5543 }
5544 }
5545
5546 return (USB_SUCCESS);
5547 }
5548
5549
5550 /*
5551 * hubd_enable_port_power:
5552 * enable individual port power
5553 */
5554 static int
hubd_enable_port_power(hubd_t * hubd,usb_port_t port)5555 hubd_enable_port_power(hubd_t *hubd, usb_port_t port)
5556 {
5557 int rval;
5558 usb_cr_t completion_reason;
5559 usb_cb_flags_t cb_flags;
5560
5561 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5562 "hubd_enable_port_power: port=%d", port);
5563
5564 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
5565 ASSERT(hubd->h_default_pipe != 0);
5566
5567 mutex_exit(HUBD_MUTEX(hubd));
5568
5569 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5570 hubd->h_default_pipe,
5571 HUB_HANDLE_PORT_FEATURE_TYPE,
5572 USB_REQ_SET_FEATURE,
5573 CFS_PORT_POWER,
5574 port,
5575 0, NULL, 0,
5576 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
5577 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5578 "set port power failed (%d 0x%x %d)",
5579 completion_reason, cb_flags, rval);
5580 mutex_enter(HUBD_MUTEX(hubd));
5581
5582 return (USB_FAILURE);
5583 } else {
5584 mutex_enter(HUBD_MUTEX(hubd));
5585 hubd->h_port_state[port] |= PORT_STATUS_PPS;
5586
5587 return (USB_SUCCESS);
5588 }
5589 }
5590
5591
5592 /*
5593 * hubd_disable_all_port_power:
5594 */
5595 static int
hubd_disable_all_port_power(hubd_t * hubd)5596 hubd_disable_all_port_power(hubd_t *hubd)
5597 {
5598 usb_port_t port;
5599
5600 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5601 "hubd_disable_all_port_power");
5602
5603 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
5604
5605 /*
5606 * disable power per port, ignore gang power and power mask
5607 */
5608 for (port = 1; port <= hubd->h_nports; port++) {
5609 (void) hubd_disable_port_power(hubd, port);
5610 }
5611
5612 return (USB_SUCCESS);
5613 }
5614
5615
5616 /*
5617 * hubd_disable_port_power:
5618 * disable individual port power
5619 */
5620 static int
hubd_disable_port_power(hubd_t * hubd,usb_port_t port)5621 hubd_disable_port_power(hubd_t *hubd, usb_port_t port)
5622 {
5623 int rval;
5624 usb_cr_t completion_reason;
5625 usb_cb_flags_t cb_flags;
5626
5627 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5628 "hubd_disable_port_power: port=%d", port);
5629
5630 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
5631
5632 mutex_exit(HUBD_MUTEX(hubd));
5633
5634 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5635 hubd->h_default_pipe,
5636 HUB_HANDLE_PORT_FEATURE_TYPE,
5637 USB_REQ_CLEAR_FEATURE,
5638 CFS_PORT_POWER,
5639 port,
5640 0, NULL, 0,
5641 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
5642 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5643 "clearing port%d power failed (%d 0x%x %d)",
5644 port, completion_reason, cb_flags, rval);
5645
5646 mutex_enter(HUBD_MUTEX(hubd));
5647
5648 return (USB_FAILURE);
5649 } else {
5650
5651 mutex_enter(HUBD_MUTEX(hubd));
5652 ASSERT(completion_reason == 0);
5653 hubd->h_port_state[port] &= ~PORT_STATUS_PPS;
5654
5655 return (USB_SUCCESS);
5656 }
5657 }
5658
5659
5660 /*
5661 * Search the database of user preferences and find out the preferred
5662 * configuration for this new device
5663 */
5664 int
hubd_select_device_configuration(hubd_t * hubd,usb_port_t port,dev_info_t * child_dip,usba_device_t * child_ud)5665 hubd_select_device_configuration(hubd_t *hubd, usb_port_t port,
5666 dev_info_t *child_dip, usba_device_t *child_ud)
5667 {
5668 char *pathname = NULL;
5669 char *tmp_path = NULL;
5670 int user_conf;
5671 int pathlen;
5672 usb_dev_descr_t *usbdev_ptr;
5673 usba_configrec_t *user_pref;
5674
5675 mutex_enter(&child_ud->usb_mutex);
5676 usbdev_ptr = child_ud->usb_dev_descr;
5677 mutex_exit(&child_ud->usb_mutex);
5678
5679 /* try to get pathname for this device */
5680 tmp_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
5681 (void) ddi_pathname(child_dip, tmp_path);
5682
5683 pathlen = strlen(tmp_path) + 32;
5684 pathname = kmem_zalloc(pathlen, KM_SLEEP);
5685
5686 /*
5687 * We haven't initialized the node and it doesn't have an address
5688 * yet. Append port number to the physical pathname
5689 */
5690 (void) sprintf(pathname, "%s@%d", tmp_path, port);
5691
5692 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5693 "hubd_select_device_configuration: Device=%s\n\t"
5694 "Child path=%s",
5695 usba_get_mfg_prod_sn_str(child_dip, tmp_path, MAXPATHLEN),
5696 pathname);
5697 kmem_free(tmp_path, MAXPATHLEN);
5698
5699
5700 /* database search for user preferences */
5701 user_pref = usba_devdb_get_user_preferences(usbdev_ptr->idVendor,
5702 usbdev_ptr->idProduct, child_ud->usb_serialno_str, pathname);
5703
5704 if (user_pref) {
5705 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5706 "hubd_select_device_configuration: "
5707 "usba_devdb_get_user_preferences "
5708 "return user_conf=%d\npreferred driver=%s path=%s",
5709 user_pref->cfg_index, user_pref->driver,
5710 user_pref->pathname);
5711
5712 user_conf = user_pref->cfg_index;
5713
5714 if (user_pref->driver) {
5715 mutex_enter(&child_ud->usb_mutex);
5716 child_ud->usb_preferred_driver = user_pref->driver;
5717 mutex_exit(&child_ud->usb_mutex);
5718 }
5719 } else {
5720 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5721 "hubd_select_device_configuration: No match found");
5722
5723 /* select default configuration for this device */
5724 user_conf = USBA_DEV_CONFIG_INDEX_UNDEFINED;
5725 }
5726 kmem_free(pathname, pathlen);
5727
5728 /* if the device has just one configuration, set default value */
5729 if (usbdev_ptr->bNumConfigurations == 1) {
5730 user_conf = USB_DEV_DEFAULT_CONFIG_INDEX;
5731 }
5732
5733 return (user_conf);
5734 }
5735
5736
5737 /*
5738 * Retrieves config cloud for this configuration
5739 */
5740 int
hubd_get_this_config_cloud(hubd_t * hubd,dev_info_t * dip,usba_device_t * child_ud,uint16_t conf_index)5741 hubd_get_this_config_cloud(hubd_t *hubd, dev_info_t *dip,
5742 usba_device_t *child_ud, uint16_t conf_index)
5743 {
5744 usb_cfg_descr_t *confdescr;
5745 mblk_t *pdata = NULL;
5746 int rval;
5747 size_t size;
5748 char *tmpbuf;
5749 usb_cr_t completion_reason;
5750 usb_cb_flags_t cb_flags;
5751 usb_pipe_handle_t def_ph;
5752
5753 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5754 "hubd_get_this_config_cloud: conf_index=%d", conf_index);
5755
5756
5757 /* alloc temporary space for config descriptor */
5758 confdescr = (usb_cfg_descr_t *)kmem_zalloc(USB_CFG_DESCR_SIZE,
5759 KM_SLEEP);
5760
5761 /* alloc temporary space for string descriptor */
5762 tmpbuf = kmem_zalloc(USB_MAXSTRINGLEN, KM_SLEEP);
5763
5764 def_ph = usba_get_dflt_pipe_handle(dip);
5765
5766 if ((rval = usb_pipe_sync_ctrl_xfer(dip, def_ph,
5767 USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
5768 USB_REQ_GET_DESCR,
5769 USB_DESCR_TYPE_SETUP_CFG | conf_index,
5770 0,
5771 USB_CFG_DESCR_SIZE,
5772 &pdata,
5773 0,
5774 &completion_reason,
5775 &cb_flags,
5776 0)) == USB_SUCCESS) {
5777
5778 /* this must be true since we didn't allow data underruns */
5779 if (MBLKL(pdata) != USB_CFG_DESCR_SIZE) {
5780 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5781 "device returned incorrect configuration "
5782 "descriptor size.");
5783
5784 rval = USB_FAILURE;
5785 goto done;
5786 }
5787
5788 /*
5789 * Parse the configuration descriptor
5790 */
5791 size = usb_parse_cfg_descr(pdata->b_rptr,
5792 MBLKL(pdata), confdescr,
5793 USB_CFG_DESCR_SIZE);
5794
5795 /* if parse cfg descr error, it should return failure */
5796 if (size == USB_PARSE_ERROR) {
5797
5798 if (pdata->b_rptr[1] != USB_DESCR_TYPE_CFG) {
5799 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
5800 hubd->h_log_handle,
5801 "device returned incorrect "
5802 "configuration descriptor type.");
5803 }
5804 rval = USB_FAILURE;
5805 goto done;
5806 }
5807
5808 if (confdescr->wTotalLength < USB_CFG_DESCR_SIZE) {
5809 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
5810 hubd->h_log_handle,
5811 "device returned incorrect "
5812 "configuration descriptor size.");
5813
5814 rval = USB_FAILURE;
5815 goto done;
5816 }
5817
5818 freemsg(pdata);
5819 pdata = NULL;
5820
5821 /* Now fetch the complete config cloud */
5822 if ((rval = usb_pipe_sync_ctrl_xfer(dip, def_ph,
5823 USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
5824 USB_REQ_GET_DESCR,
5825 USB_DESCR_TYPE_SETUP_CFG | conf_index,
5826 0,
5827 confdescr->wTotalLength,
5828 &pdata,
5829 0,
5830 &completion_reason,
5831 &cb_flags,
5832 0)) == USB_SUCCESS) {
5833
5834 if (MBLKL(pdata) !=
5835 confdescr->wTotalLength) {
5836
5837 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
5838 hubd->h_log_handle,
5839 "device returned incorrect "
5840 "configuration descriptor.");
5841
5842 rval = USB_FAILURE;
5843 goto done;
5844 }
5845
5846 /*
5847 * copy config descriptor into usba_device
5848 */
5849 mutex_enter(&child_ud->usb_mutex);
5850 child_ud->usb_cfg_array[conf_index] =
5851 kmem_alloc(confdescr->wTotalLength, KM_SLEEP);
5852 child_ud->usb_cfg_array_len[conf_index] =
5853 confdescr->wTotalLength;
5854 bcopy((caddr_t)pdata->b_rptr,
5855 (caddr_t)child_ud->usb_cfg_array[conf_index],
5856 confdescr->wTotalLength);
5857 mutex_exit(&child_ud->usb_mutex);
5858
5859 /*
5860 * retrieve string descriptor describing this
5861 * configuration
5862 */
5863 if (confdescr->iConfiguration) {
5864
5865 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG,
5866 hubd->h_log_handle,
5867 "Get conf str descr for config_index=%d",
5868 conf_index);
5869
5870 /*
5871 * Now fetch the string descriptor describing
5872 * this configuration
5873 */
5874 if ((rval = usb_get_string_descr(dip,
5875 USB_LANG_ID, confdescr->iConfiguration,
5876 tmpbuf, USB_MAXSTRINGLEN)) ==
5877 USB_SUCCESS) {
5878 size = strlen(tmpbuf);
5879 if (size > 0) {
5880 child_ud->usb_cfg_str_descr
5881 [conf_index] = (char *)
5882 kmem_zalloc(size + 1,
5883 KM_SLEEP);
5884 (void) strcpy(
5885 child_ud->usb_cfg_str_descr
5886 [conf_index], tmpbuf);
5887 }
5888 } else {
5889 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
5890 hubd->h_log_handle,
5891 "hubd_get_this_config_cloud: "
5892 "getting config string (%d) "
5893 "failed",
5894 confdescr->iConfiguration);
5895
5896 /* ignore this error */
5897 rval = USB_SUCCESS;
5898 }
5899 }
5900 }
5901 }
5902
5903 done:
5904 if (rval != USB_SUCCESS) {
5905 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5906 "hubd_get_this_config_cloud: "
5907 "error in retrieving config descriptor for "
5908 "config index=%d rval=%d cr=%d",
5909 conf_index, rval, completion_reason);
5910 }
5911
5912 if (pdata) {
5913 freemsg(pdata);
5914 pdata = NULL;
5915 }
5916
5917 kmem_free(confdescr, USB_CFG_DESCR_SIZE);
5918 kmem_free(tmpbuf, USB_MAXSTRINGLEN);
5919
5920 return (rval);
5921 }
5922
5923
5924 /*
5925 * Retrieves the entire config cloud for all configurations of the device
5926 */
5927 int
hubd_get_all_device_config_cloud(hubd_t * hubd,dev_info_t * dip,usba_device_t * child_ud)5928 hubd_get_all_device_config_cloud(hubd_t *hubd, dev_info_t *dip,
5929 usba_device_t *child_ud)
5930 {
5931 int rval = USB_SUCCESS;
5932 int ncfgs;
5933 uint16_t size;
5934 uint16_t conf_index;
5935 uchar_t **cfg_array;
5936 uint16_t *cfg_array_len;
5937 char **str_descr;
5938
5939 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5940 "hubd_get_all_device_config_cloud: Start");
5941
5942 /* alloc pointer array for conf. descriptors */
5943 mutex_enter(&child_ud->usb_mutex);
5944 ncfgs = child_ud->usb_n_cfgs;
5945 mutex_exit(&child_ud->usb_mutex);
5946
5947 size = sizeof (uchar_t *) * ncfgs;
5948 cfg_array = kmem_zalloc(size, KM_SLEEP);
5949 cfg_array_len = kmem_zalloc(ncfgs * sizeof (uint16_t), KM_SLEEP);
5950 str_descr = kmem_zalloc(size, KM_SLEEP);
5951
5952 mutex_enter(&child_ud->usb_mutex);
5953 child_ud->usb_cfg_array = cfg_array;
5954 child_ud->usb_cfg_array_len = cfg_array_len;
5955 child_ud->usb_cfg_array_length = size;
5956 child_ud->usb_cfg_array_len_length = ncfgs * sizeof (uint16_t);
5957 child_ud->usb_cfg_str_descr = str_descr;
5958 mutex_exit(&child_ud->usb_mutex);
5959
5960 /* Get configuration descriptor for each configuration */
5961 for (conf_index = 0; (conf_index < ncfgs) &&
5962 (rval == USB_SUCCESS); conf_index++) {
5963
5964 rval = hubd_get_this_config_cloud(hubd, dip, child_ud,
5965 conf_index);
5966 }
5967
5968 return (rval);
5969 }
5970
5971
5972 /*
5973 * hubd_ready_device:
5974 * Update the usba_device structure
5975 * Set the given configuration
5976 * Prepares the device node for driver to online. If an existing
5977 * OBP node is found, it will switch to the OBP node.
5978 */
5979 dev_info_t *
hubd_ready_device(hubd_t * hubd,dev_info_t * child_dip,usba_device_t * child_ud,uint_t config_index)5980 hubd_ready_device(hubd_t *hubd, dev_info_t *child_dip, usba_device_t *child_ud,
5981 uint_t config_index)
5982 {
5983 usb_cr_t completion_reason;
5984 usb_cb_flags_t cb_flags;
5985 size_t size;
5986 usb_cfg_descr_t config_descriptor;
5987 usb_pipe_handle_t def_ph;
5988 usba_pipe_handle_data_t *ph;
5989
5990 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5991 "hubd_ready_device: dip=0x%p, user_conf_index=%d",
5992 (void *)child_dip, config_index);
5993
5994 size = usb_parse_cfg_descr(
5995 child_ud->usb_cfg_array[config_index], USB_CFG_DESCR_SIZE,
5996 &config_descriptor, USB_CFG_DESCR_SIZE);
5997 ASSERT(size == USB_CFG_DESCR_SIZE);
5998
5999 def_ph = usba_get_dflt_pipe_handle(child_dip);
6000
6001 /* Set the configuration */
6002 (void) usb_pipe_sync_ctrl_xfer(child_dip, def_ph,
6003 USB_DEV_REQ_HOST_TO_DEV,
6004 USB_REQ_SET_CFG, /* bRequest */
6005 config_descriptor.bConfigurationValue, /* wValue */
6006 0, /* wIndex */
6007 0, /* wLength */
6008 NULL,
6009 0,
6010 &completion_reason,
6011 &cb_flags,
6012 0);
6013
6014 mutex_enter(&child_ud->usb_mutex);
6015 child_ud->usb_active_cfg_ndx = config_index;
6016 child_ud->usb_cfg = child_ud->usb_cfg_array[config_index];
6017 child_ud->usb_cfg_length = config_descriptor.wTotalLength;
6018 child_ud->usb_cfg_value = config_descriptor.bConfigurationValue;
6019 child_ud->usb_n_ifs = config_descriptor.bNumInterfaces;
6020 child_ud->usb_dip = child_dip;
6021
6022 child_ud->usb_client_flags = kmem_zalloc(
6023 child_ud->usb_n_ifs * USBA_CLIENT_FLAG_SIZE, KM_SLEEP);
6024
6025 child_ud->usb_client_attach_list = kmem_zalloc(
6026 child_ud->usb_n_ifs *
6027 sizeof (*child_ud->usb_client_attach_list), KM_SLEEP);
6028
6029 child_ud->usb_client_ev_cb_list = kmem_zalloc(
6030 child_ud->usb_n_ifs *
6031 sizeof (*child_ud->usb_client_ev_cb_list), KM_SLEEP);
6032
6033 mutex_exit(&child_ud->usb_mutex);
6034
6035 /* ready the device node */
6036 child_dip = usba_ready_device_node(child_dip);
6037
6038 /* set owner of default pipe to child dip */
6039 ph = usba_get_ph_data(def_ph);
6040 mutex_enter(&ph->p_mutex);
6041 mutex_enter(&ph->p_ph_impl->usba_ph_mutex);
6042 ph->p_ph_impl->usba_ph_dip = ph->p_dip = child_dip;
6043 mutex_exit(&ph->p_ph_impl->usba_ph_mutex);
6044 mutex_exit(&ph->p_mutex);
6045
6046 return (child_dip);
6047 }
6048
6049 /*
6050 * hubd_create_child
6051 * - create child dip
6052 * - open default pipe
6053 * - get device descriptor
6054 * - set the address
6055 * - get device string descriptors
6056 * - get the entire config cloud (all configurations) of the device
6057 * - set user preferred configuration
6058 * - close default pipe
6059 * - load appropriate driver(s)
6060 */
6061 static int
hubd_create_child(dev_info_t * dip,hubd_t * hubd,usba_device_t * hubd_ud,usb_port_status_t port_status,usb_port_t port,int iteration)6062 hubd_create_child(dev_info_t *dip,
6063 hubd_t *hubd,
6064 usba_device_t *hubd_ud,
6065 usb_port_status_t port_status,
6066 usb_port_t port,
6067 int iteration)
6068 {
6069 dev_info_t *child_dip = NULL;
6070 usb_dev_descr_t usb_dev_descr;
6071 int rval;
6072 usba_device_t *child_ud = NULL;
6073 usba_device_t *parent_ud = NULL;
6074 usb_pipe_handle_t ph = NULL; /* default pipe handle */
6075 mblk_t *pdata = NULL;
6076 usb_cr_t completion_reason;
6077 int user_conf_index;
6078 uint_t config_index;
6079 usb_cb_flags_t cb_flags;
6080 uchar_t address = 0;
6081 uint16_t length;
6082 size_t size;
6083 usb_addr_t parent_usb_addr;
6084 usb_port_t parent_usb_port;
6085 usba_device_t *parent_usba_dev;
6086 usb_port_status_t parent_port_status;
6087 boolean_t hcd_called = B_FALSE;
6088
6089 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6090 "hubd_create_child: port=%d", port);
6091
6092 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
6093 ASSERT(hubd->h_usba_devices[port] == NULL);
6094
6095 mutex_exit(HUBD_MUTEX(hubd));
6096
6097 /*
6098 * create a dip which can be used to open the pipe. we set
6099 * the name after getting the descriptors from the device
6100 */
6101 rval = usba_create_child_devi(dip,
6102 "device", /* driver name */
6103 hubd_ud->usb_hcdi_ops, /* usba_hcdi ops */
6104 hubd_ud->usb_root_hub_dip,
6105 port_status, /* low speed device */
6106 child_ud,
6107 &child_dip);
6108
6109 if (rval != USB_SUCCESS) {
6110
6111 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6112 "usb_create_child_devi failed (%d)", rval);
6113
6114 goto fail_cleanup;
6115 }
6116
6117 child_ud = usba_get_usba_device(child_dip);
6118 ASSERT(child_ud != NULL);
6119
6120 parent_ud = hubd->h_usba_device;
6121 mutex_enter(&parent_ud->usb_mutex);
6122 parent_port_status = parent_ud->usb_port_status;
6123
6124 /*
6125 * To support split transactions, update address and port of high speed
6126 * hub to which given device is connected. Note, split transactions
6127 * only exist for high speed devices.
6128 */
6129 if (parent_port_status == USBA_HIGH_SPEED_DEV) {
6130 parent_usba_dev = parent_ud;
6131 parent_usb_addr = parent_ud->usb_addr;
6132 parent_usb_port = port;
6133 } else {
6134 parent_usba_dev = parent_ud->usb_hs_hub_usba_dev;
6135 parent_usb_addr = parent_ud->usb_hs_hub_addr;
6136 parent_usb_port = parent_ud->usb_hs_hub_port;
6137 }
6138 mutex_exit(&parent_ud->usb_mutex);
6139
6140 mutex_enter(&child_ud->usb_mutex);
6141 address = child_ud->usb_addr;
6142 child_ud->usb_addr = 0;
6143 child_ud->usb_dev_descr = kmem_alloc(sizeof (usb_dev_descr_t),
6144 KM_SLEEP);
6145 bzero(&usb_dev_descr, sizeof (usb_dev_descr_t));
6146
6147 switch (port_status) {
6148 case USBA_SUPER_SPEED_DEV:
6149 usb_dev_descr.bMaxPacketSize0 = 9;
6150 break;
6151 case USBA_LOW_SPEED_DEV:
6152 usb_dev_descr.bMaxPacketSize0 = 8;
6153 break;
6154 default:
6155 usb_dev_descr.bMaxPacketSize0 = 64;
6156 break;
6157 }
6158 bcopy(&usb_dev_descr, child_ud->usb_dev_descr,
6159 sizeof (usb_dev_descr_t));
6160 child_ud->usb_port = port;
6161
6162 /*
6163 * The parent hub always keeps track of the hub this device is connected
6164 * to; however, the hs_hub_* variables are only keeping track of the
6165 * closest high speed hub. Unfortunately, we need both.
6166 */
6167 child_ud->usb_parent_hub = parent_ud;
6168 child_ud->usb_hs_hub_usba_dev = parent_usba_dev;
6169 child_ud->usb_hs_hub_addr = parent_usb_addr;
6170 child_ud->usb_hs_hub_port = parent_usb_port;
6171 mutex_exit(&child_ud->usb_mutex);
6172
6173 /*
6174 * Before we open up the default pipe, give the HCD a chance to do
6175 * something here.
6176 */
6177 if (child_ud->usb_hcdi_ops->usba_hcdi_device_init != NULL) {
6178 int rval;
6179 void *priv = NULL;
6180
6181 rval = child_ud->usb_hcdi_ops->usba_hcdi_device_init(child_ud,
6182 port, &priv);
6183 if (rval != USB_SUCCESS) {
6184 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6185 "HCD usba_hcdi_Device_init failed (%d)", rval);
6186 goto fail_cleanup;
6187 }
6188
6189 child_ud->usb_hcd_private = priv;
6190 hcd_called = B_TRUE;
6191 }
6192
6193
6194
6195 /* Open the default pipe */
6196 if ((rval = usb_pipe_open(child_dip, NULL, NULL,
6197 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) != USB_SUCCESS) {
6198 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6199 "usb_pipe_open failed (%d)", rval);
6200
6201 goto fail_cleanup;
6202 }
6203
6204 /*
6205 * get device descriptor
6206 */
6207 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6208 "hubd_create_child: get device descriptor: 64 bytes");
6209
6210 rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
6211 USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
6212 USB_REQ_GET_DESCR, /* bRequest */
6213 USB_DESCR_TYPE_SETUP_DEV, /* wValue */
6214 0, /* wIndex */
6215 64, /* wLength */
6216 &pdata, USB_ATTRS_SHORT_XFER_OK,
6217 &completion_reason, &cb_flags, 0);
6218
6219 /*
6220 * If this is a full speed device, we cannot assume that its default
6221 * packet size is 64 bytes, it may be 8 bytes.
6222 */
6223
6224 if ((rval != USB_SUCCESS) &&
6225 (!((completion_reason == USB_CR_DATA_OVERRUN) && pdata))) {
6226
6227 /*
6228 * rval != USB_SUCCESS AND
6229 * completion_reason != USB_CR_DATA_OVERRUN
6230 * pdata could be != NULL.
6231 * Free pdata now to prevent memory leak.
6232 */
6233 freemsg(pdata);
6234 pdata = NULL;
6235
6236 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6237 "hubd_create_child: get device descriptor: 8 bytes");
6238
6239 rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
6240 USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
6241 USB_REQ_GET_DESCR, /* bRequest */
6242 USB_DESCR_TYPE_SETUP_DEV, /* wValue */
6243 0, /* wIndex */
6244 8, /* wLength */
6245 &pdata, USB_ATTRS_NONE,
6246 &completion_reason, &cb_flags, 0);
6247
6248 if (rval != USB_SUCCESS) {
6249 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6250 "getting device descriptor failed (%s 0x%x %d)",
6251 usb_str_cr(completion_reason), cb_flags, rval);
6252 goto fail_cleanup;
6253 }
6254 } else {
6255 ASSERT(completion_reason == USB_CR_OK);
6256 }
6257
6258 ASSERT(pdata != NULL);
6259
6260 size = usb_parse_dev_descr(
6261 pdata->b_rptr,
6262 MBLKL(pdata),
6263 &usb_dev_descr,
6264 sizeof (usb_dev_descr_t));
6265
6266 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6267 "parsing device descriptor returned %lu", size);
6268
6269 length = *(pdata->b_rptr);
6270 freemsg(pdata);
6271 pdata = NULL;
6272 if (size < 8) {
6273 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6274 "get device descriptor returned %lu bytes", size);
6275
6276 goto fail_cleanup;
6277 }
6278
6279 if (length < 8) {
6280 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6281 "fail enumeration: bLength=%d", length);
6282
6283 goto fail_cleanup;
6284 }
6285
6286 if (child_ud->usb_hcdi_ops->usba_hcdi_device_address != NULL) {
6287 rval = child_ud->usb_hcdi_ops->usba_hcdi_device_address(
6288 child_ud);
6289 if (rval != USB_SUCCESS)
6290 goto fail_cleanup;
6291 } else {
6292 /* Set the address of the device */
6293 if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
6294 USB_DEV_REQ_HOST_TO_DEV,
6295 USB_REQ_SET_ADDRESS, /* bRequest */
6296 address, /* wValue */
6297 0, /* wIndex */
6298 0, /* wLength */
6299 NULL, 0,
6300 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
6301 char buffer[64];
6302 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6303 "setting address failed (cr=%s cb_flags=%s "
6304 "rval=%d)", usb_str_cr(completion_reason),
6305 usb_str_cb_flags(cb_flags, buffer, sizeof (buffer)),
6306 rval);
6307
6308 goto fail_cleanup;
6309 }
6310 }
6311
6312 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6313 "set address 0x%x done", address);
6314
6315 /* now close the pipe for addr 0 */
6316 usb_pipe_close(child_dip, ph,
6317 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
6318
6319 /*
6320 * This delay is important for the CATC hub to enumerate
6321 * But, avoid delay in the first iteration
6322 */
6323 if (iteration) {
6324 delay(drv_usectohz(hubd_device_delay/100));
6325 }
6326
6327 /* assign the address in the usba_device structure */
6328 mutex_enter(&child_ud->usb_mutex);
6329 child_ud->usb_addr = address;
6330 child_ud->usb_no_cpr = 0;
6331 child_ud->usb_port_status = port_status;
6332 /* save this device descriptor */
6333 bcopy(&usb_dev_descr, child_ud->usb_dev_descr,
6334 sizeof (usb_dev_descr_t));
6335 child_ud->usb_n_cfgs = usb_dev_descr.bNumConfigurations;
6336 mutex_exit(&child_ud->usb_mutex);
6337
6338 /* re-open the pipe for the device with the new address */
6339 if ((rval = usb_pipe_open(child_dip, NULL, NULL,
6340 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) != USB_SUCCESS) {
6341 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6342 "usb_pipe_open failed (%d)", rval);
6343
6344 goto fail_cleanup;
6345 }
6346
6347 /*
6348 * Get full device descriptor only if we have not received full
6349 * device descriptor earlier.
6350 */
6351 if (size < length) {
6352 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6353 "hubd_create_child: get full device descriptor: "
6354 "%d bytes", length);
6355
6356 if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
6357 USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
6358 USB_REQ_GET_DESCR, /* bRequest */
6359 USB_DESCR_TYPE_SETUP_DEV, /* wValue */
6360 0, /* wIndex */
6361 length, /* wLength */
6362 &pdata, 0,
6363 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
6364 freemsg(pdata);
6365 pdata = NULL;
6366
6367 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG,
6368 hubd->h_log_handle,
6369 "hubd_create_child: get full device descriptor: "
6370 "64 bytes");
6371
6372 rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
6373 USB_DEV_REQ_DEV_TO_HOST |
6374 USB_DEV_REQ_TYPE_STANDARD,
6375 USB_REQ_GET_DESCR, /* bRequest */
6376 USB_DESCR_TYPE_SETUP_DEV, /* wValue */
6377 0, /* wIndex */
6378 64, /* wLength */
6379 &pdata, USB_ATTRS_SHORT_XFER_OK,
6380 &completion_reason, &cb_flags, 0);
6381
6382 /* we have to trust the data now */
6383 if (pdata) {
6384 int len = *(pdata->b_rptr);
6385
6386 length = MBLKL(pdata);
6387 if (length < len) {
6388
6389 goto fail_cleanup;
6390 }
6391 } else if (rval != USB_SUCCESS) {
6392 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
6393 hubd->h_log_handle,
6394 "getting device descriptor failed "
6395 "(%d 0x%x %d)",
6396 completion_reason, cb_flags, rval);
6397
6398 goto fail_cleanup;
6399 }
6400 }
6401
6402 size = usb_parse_dev_descr(
6403 pdata->b_rptr,
6404 MBLKL(pdata),
6405 &usb_dev_descr,
6406 sizeof (usb_dev_descr_t));
6407
6408 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6409 "parsing device descriptor returned %lu", size);
6410
6411 /*
6412 * For now, free the data
6413 * eventually, each configuration may need to be looked at
6414 */
6415 freemsg(pdata);
6416 pdata = NULL;
6417
6418 if (size != USB_DEV_DESCR_SIZE) {
6419 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6420 "fail enumeration: descriptor size=%lu "
6421 "expected size=%u", size, USB_DEV_DESCR_SIZE);
6422
6423 goto fail_cleanup;
6424 }
6425
6426 /*
6427 * save the device descriptor in usba_device since it is needed
6428 * later on again
6429 */
6430 mutex_enter(&child_ud->usb_mutex);
6431 bcopy(&usb_dev_descr, child_ud->usb_dev_descr,
6432 sizeof (usb_dev_descr_t));
6433 child_ud->usb_n_cfgs = usb_dev_descr.bNumConfigurations;
6434 mutex_exit(&child_ud->usb_mutex);
6435 }
6436
6437 if (usb_dev_descr.bNumConfigurations == 0) {
6438 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6439 "device descriptor:\n\t"
6440 "l=0x%x type=0x%x USB=0x%x class=0x%x subclass=0x%x\n\t"
6441 "protocol=0x%x maxpktsize=0x%x "
6442 "Vid=0x%x Pid=0x%x rel=0x%x\n\t"
6443 "Mfg=0x%x P=0x%x sn=0x%x #config=0x%x",
6444 usb_dev_descr.bLength, usb_dev_descr.bDescriptorType,
6445 usb_dev_descr.bcdUSB, usb_dev_descr.bDeviceClass,
6446 usb_dev_descr.bDeviceSubClass,
6447 usb_dev_descr.bDeviceProtocol,
6448 usb_dev_descr.bMaxPacketSize0,
6449 usb_dev_descr.idVendor,
6450 usb_dev_descr.idProduct, usb_dev_descr.bcdDevice,
6451 usb_dev_descr.iManufacturer, usb_dev_descr.iProduct,
6452 usb_dev_descr.iSerialNumber,
6453 usb_dev_descr.bNumConfigurations);
6454 goto fail_cleanup;
6455 }
6456
6457 /* Read the BOS data */
6458 usba_get_binary_object_store(child_dip, child_ud);
6459
6460 /* get the device string descriptor(s) */
6461 usba_get_dev_string_descrs(child_dip, child_ud);
6462
6463 /* retrieve config cloud for all configurations */
6464 rval = hubd_get_all_device_config_cloud(hubd, child_dip, child_ud);
6465 if (rval != USB_SUCCESS) {
6466 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6467 "failed to get configuration descriptor(s)");
6468
6469 goto fail_cleanup;
6470 }
6471
6472 /* get the preferred configuration for this device */
6473 user_conf_index = hubd_select_device_configuration(hubd, port,
6474 child_dip, child_ud);
6475
6476 /* Check if the user selected configuration index is in range */
6477 if ((user_conf_index >= usb_dev_descr.bNumConfigurations) ||
6478 (user_conf_index < 0)) {
6479 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6480 "Configuration index for device idVendor=%d "
6481 "idProduct=%d is=%d, and is out of range[0..%d]",
6482 usb_dev_descr.idVendor, usb_dev_descr.idProduct,
6483 user_conf_index, usb_dev_descr.bNumConfigurations - 1);
6484
6485 /* treat this as user didn't specify configuration */
6486 user_conf_index = USBA_DEV_CONFIG_INDEX_UNDEFINED;
6487 }
6488
6489
6490 /*
6491 * Warn users of a performance hit if connecting a
6492 * High Speed behind a 1.1 hub, which is behind a
6493 * 2.0 port. Don't worry about this for USB 3.x for now.
6494 */
6495 if ((parent_port_status != USBA_HIGH_SPEED_DEV) &&
6496 !(usba_is_root_hub(parent_ud->usb_dip)) &&
6497 (parent_usb_addr)) {
6498
6499 /*
6500 * Now that we know the root port is a high speed port
6501 * and that the parent port is not a high speed port,
6502 * let's find out if the device itself is a high speed
6503 * device. If it is a high speed device,
6504 * USB_DESCR_TYPE_SETUP_DEV_QLF should return a value,
6505 * otherwise the command will fail.
6506 */
6507 rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
6508 USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
6509 USB_REQ_GET_DESCR, /* bRequest */
6510 USB_DESCR_TYPE_SETUP_DEV_QLF, /* wValue */
6511 0, /* wIndex */
6512 10, /* wLength */
6513 &pdata, USB_ATTRS_SHORT_XFER_OK,
6514 &completion_reason, &cb_flags, 0);
6515
6516 if (pdata) {
6517 freemsg(pdata);
6518 pdata = NULL;
6519 }
6520
6521 /*
6522 * USB_DESCR_TYPE_SETUP_DEV_QLF query was successful
6523 * that means this is a high speed device behind a
6524 * high speed root hub, but running at full speed
6525 * because there is a full speed hub in the middle.
6526 */
6527 if (rval == USB_SUCCESS) {
6528 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
6529 hubd->h_log_handle,
6530 "Connecting a high speed device to a "
6531 "non high speed hub (port %d) will result "
6532 "in a loss of performance. Please connect "
6533 "the device to a high speed hub to get "
6534 "the maximum performance.",
6535 port);
6536 }
6537 }
6538
6539 /*
6540 * Now we try to online the device by attaching a driver
6541 * The following truth table illustrates the logic:-
6542 * Cfgndx Driver Action
6543 * 0 0 loop all configs for driver with full
6544 * compatible properties.
6545 * 0 1 set first configuration,
6546 * compatible prop = drivername.
6547 * 1 0 Set config, full compatible prop
6548 * 1 1 Set config, compatible prop = drivername.
6549 *
6550 * Note:
6551 * cfgndx = user_conf_index
6552 * Driver = usb_preferred_driver
6553 */
6554 if (user_conf_index == USBA_DEV_CONFIG_INDEX_UNDEFINED) {
6555 if (child_ud->usb_preferred_driver) {
6556 /*
6557 * It is the job of the "preferred driver" to put the
6558 * device in the desired configuration. Till then
6559 * put the device in config index 0.
6560 */
6561 if ((rval = usba_hubdi_check_power_budget(dip, child_ud,
6562 USB_DEV_DEFAULT_CONFIG_INDEX)) != USB_SUCCESS) {
6563
6564 goto fail_cleanup;
6565 }
6566
6567 child_dip = hubd_ready_device(hubd, child_dip,
6568 child_ud, USB_DEV_DEFAULT_CONFIG_INDEX);
6569
6570 /*
6571 * Assign the dip before onlining to avoid race
6572 * with busctl
6573 */
6574 mutex_enter(HUBD_MUTEX(hubd));
6575 hubd->h_children_dips[port] = child_dip;
6576 mutex_exit(HUBD_MUTEX(hubd));
6577
6578 (void) usba_bind_driver(child_dip);
6579 } else {
6580 /*
6581 * loop through all the configurations to see if we
6582 * can find a driver for any one config. If not, set
6583 * the device in config_index 0
6584 */
6585 rval = USB_FAILURE;
6586 for (config_index = 0;
6587 (config_index < usb_dev_descr.bNumConfigurations) &&
6588 (rval != USB_SUCCESS); config_index++) {
6589
6590 child_dip = hubd_ready_device(hubd, child_dip,
6591 child_ud, config_index);
6592
6593 /*
6594 * Assign the dip before onlining to avoid race
6595 * with busctl
6596 */
6597 mutex_enter(HUBD_MUTEX(hubd));
6598 hubd->h_children_dips[port] = child_dip;
6599 mutex_exit(HUBD_MUTEX(hubd));
6600
6601 rval = usba_bind_driver(child_dip);
6602
6603 /*
6604 * Normally power budget should be checked
6605 * before device is configured. A failure in
6606 * power budget checking will stop the device
6607 * from being configured with current
6608 * config_index and may enable the device to
6609 * be configured in another configuration.
6610 * This may break the user experience that a
6611 * device which previously worked in config
6612 * A now works in config B after power budget
6613 * control is enabled. To avoid such situation,
6614 * power budget checking is moved here and will
6615 * fail the child creation directly if config
6616 * A exceeds the power available.
6617 */
6618 if (rval == USB_SUCCESS) {
6619 if ((usba_hubdi_check_power_budget(dip,
6620 child_ud, config_index)) !=
6621 USB_SUCCESS) {
6622
6623 goto fail_cleanup;
6624 }
6625 }
6626 }
6627 if (rval != USB_SUCCESS) {
6628
6629 if ((usba_hubdi_check_power_budget(dip,
6630 child_ud, 0)) != USB_SUCCESS) {
6631
6632 goto fail_cleanup;
6633 }
6634
6635 child_dip = hubd_ready_device(hubd, child_dip,
6636 child_ud, 0);
6637 mutex_enter(HUBD_MUTEX(hubd));
6638 hubd->h_children_dips[port] = child_dip;
6639 mutex_exit(HUBD_MUTEX(hubd));
6640 }
6641 } /* end else loop all configs */
6642 } else {
6643
6644 if ((usba_hubdi_check_power_budget(dip, child_ud,
6645 (uint_t)user_conf_index)) != USB_SUCCESS) {
6646
6647 goto fail_cleanup;
6648 }
6649
6650 child_dip = hubd_ready_device(hubd, child_dip,
6651 child_ud, (uint_t)user_conf_index);
6652
6653 /*
6654 * Assign the dip before onlining to avoid race
6655 * with busctl
6656 */
6657 mutex_enter(HUBD_MUTEX(hubd));
6658 hubd->h_children_dips[port] = child_dip;
6659 mutex_exit(HUBD_MUTEX(hubd));
6660
6661 (void) usba_bind_driver(child_dip);
6662 }
6663
6664 usba_hubdi_decr_power_budget(dip, child_ud);
6665
6666 mutex_enter(HUBD_MUTEX(hubd));
6667 if (hubd->h_usba_devices[port] == NULL) {
6668 hubd->h_usba_devices[port] = usba_get_usba_device(child_dip);
6669 } else {
6670 ASSERT(hubd->h_usba_devices[port] ==
6671 usba_get_usba_device(child_dip));
6672 }
6673
6674 return (USB_SUCCESS);
6675
6676
6677 fail_cleanup:
6678 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6679 "hubd_create_child: fail_cleanup");
6680
6681 mutex_enter(HUBD_MUTEX(hubd));
6682 hubd->h_children_dips[port] = NULL;
6683 mutex_exit(HUBD_MUTEX(hubd));
6684
6685 if (pdata) {
6686 freemsg(pdata);
6687 }
6688
6689 if (ph) {
6690 usb_pipe_close(child_dip, ph,
6691 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
6692 }
6693
6694 if (child_ud != NULL && hcd_called == B_TRUE &&
6695 child_ud->usb_hcdi_ops->usba_hcdi_device_fini != NULL) {
6696 child_ud->usb_hcdi_ops->usba_hcdi_device_fini(child_ud,
6697 child_ud->usb_hcd_private);
6698 child_ud->usb_hcd_private = NULL;
6699 }
6700
6701
6702 if (child_dip) {
6703 int rval = usba_destroy_child_devi(child_dip,
6704 NDI_DEVI_REMOVE);
6705 if (rval != USB_SUCCESS) {
6706 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6707 "failure to remove child node");
6708 }
6709 }
6710
6711 if (child_ud) {
6712 /* to make sure we free the address */
6713 mutex_enter(&child_ud->usb_mutex);
6714 child_ud->usb_addr = address;
6715 ASSERT(child_ud->usb_ref_count == 0);
6716 mutex_exit(&child_ud->usb_mutex);
6717
6718 mutex_enter(HUBD_MUTEX(hubd));
6719 if (hubd->h_usba_devices[port] == NULL) {
6720 mutex_exit(HUBD_MUTEX(hubd));
6721 usba_free_usba_device(child_ud);
6722 } else {
6723 hubd_free_usba_device(hubd, hubd->h_usba_devices[port]);
6724 mutex_exit(HUBD_MUTEX(hubd));
6725 }
6726 }
6727
6728 mutex_enter(HUBD_MUTEX(hubd));
6729
6730 return (USB_FAILURE);
6731 }
6732
6733
6734 /*
6735 * hubd_delete_child:
6736 * - free usb address
6737 * - lookup child dips, there may be multiple on this port
6738 * - offline each child devi
6739 */
6740 static int
hubd_delete_child(hubd_t * hubd,usb_port_t port,uint_t flag,boolean_t retry)6741 hubd_delete_child(hubd_t *hubd, usb_port_t port, uint_t flag, boolean_t retry)
6742 {
6743 dev_info_t *child_dip;
6744 usba_device_t *usba_device;
6745 int rval = USB_SUCCESS;
6746
6747 child_dip = hubd->h_children_dips[port];
6748 usba_device = hubd->h_usba_devices[port];
6749
6750 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6751 "hubd_delete_child: port=%d, dip=0x%p usba_device=0x%p",
6752 port, (void *)child_dip, (void *)usba_device);
6753
6754 mutex_exit(HUBD_MUTEX(hubd));
6755 if (child_dip) {
6756 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6757 "hubd_delete_child:\n\t"
6758 "dip = 0x%p (%s) at port %d",
6759 (void *)child_dip, ddi_node_name(child_dip), port);
6760
6761 if (usba_device) {
6762 usba_hubdi_incr_power_budget(hubd->h_dip, usba_device);
6763 }
6764
6765
6766 rval = usba_destroy_child_devi(child_dip, flag);
6767
6768 if ((rval == USB_SUCCESS) && (flag & NDI_DEVI_REMOVE)) {
6769 /*
6770 * if the child was still < DS_INITIALIZED
6771 * then our bus_unconfig was not called and
6772 * we have to zap the child here
6773 */
6774 mutex_enter(HUBD_MUTEX(hubd));
6775 if (hubd->h_children_dips[port] == child_dip) {
6776 usba_device_t *ud =
6777 hubd->h_usba_devices[port];
6778 hubd->h_children_dips[port] = NULL;
6779 if (ud) {
6780 mutex_exit(HUBD_MUTEX(hubd));
6781
6782 mutex_enter(&ud->usb_mutex);
6783 ud->usb_ref_count = 0;
6784 mutex_exit(&ud->usb_mutex);
6785
6786 usba_free_usba_device(ud);
6787 mutex_enter(HUBD_MUTEX(hubd));
6788 hubd->h_usba_devices[port] = NULL;
6789 }
6790 }
6791 mutex_exit(HUBD_MUTEX(hubd));
6792 }
6793 }
6794
6795 if ((rval != USB_SUCCESS) && retry) {
6796
6797 hubd_schedule_cleanup(usba_device->usb_root_hub_dip);
6798 }
6799 mutex_enter(HUBD_MUTEX(hubd));
6800
6801 return (rval);
6802 }
6803
6804
6805 /*
6806 * hubd_free_usba_device:
6807 * free usb device structure unless it is associated with
6808 * the root hub which is handled differently
6809 */
6810 static void
hubd_free_usba_device(hubd_t * hubd,usba_device_t * usba_device)6811 hubd_free_usba_device(hubd_t *hubd, usba_device_t *usba_device)
6812 {
6813 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6814 "hubd_free_usba_device: hubd=0x%p, usba_device=0x%p",
6815 (void *)hubd, (void *)usba_device);
6816
6817 if (usba_device && (usba_device->usb_addr != ROOT_HUB_ADDR)) {
6818 usb_port_t port = usba_device->usb_port;
6819 dev_info_t *dip = hubd->h_children_dips[port];
6820
6821 #ifdef DEBUG
6822 if (dip) {
6823 ASSERT(i_ddi_node_state(dip) < DS_INITIALIZED);
6824 }
6825 #endif
6826 port = usba_device->usb_port;
6827 hubd->h_usba_devices[port] = NULL;
6828
6829 mutex_exit(HUBD_MUTEX(hubd));
6830 usba_free_usba_device(usba_device);
6831 mutex_enter(HUBD_MUTEX(hubd));
6832 }
6833 }
6834
6835
6836 /*
6837 * event support
6838 *
6839 * busctl event support
6840 */
6841 static int
hubd_busop_get_eventcookie(dev_info_t * dip,dev_info_t * rdip,char * eventname,ddi_eventcookie_t * cookie)6842 hubd_busop_get_eventcookie(dev_info_t *dip,
6843 dev_info_t *rdip,
6844 char *eventname,
6845 ddi_eventcookie_t *cookie)
6846 {
6847 hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip);
6848
6849 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6850 "hubd_busop_get_eventcookie: dip=0x%p, rdip=0x%p, "
6851 "event=%s", (void *)dip, (void *)rdip, eventname);
6852 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6853 "(dip=%s%d, rdip=%s%d)",
6854 ddi_driver_name(dip), ddi_get_instance(dip),
6855 ddi_driver_name(rdip), ddi_get_instance(rdip));
6856
6857 /* return event cookie, iblock cookie, and level */
6858 return (ndi_event_retrieve_cookie(hubd->h_ndi_event_hdl,
6859 rdip, eventname, cookie, NDI_EVENT_NOPASS));
6860 }
6861
6862
6863 static int
hubd_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)6864 hubd_busop_add_eventcall(dev_info_t *dip,
6865 dev_info_t *rdip,
6866 ddi_eventcookie_t cookie,
6867 ddi_event_cb_f callback,
6868 void *arg, ddi_callback_id_t *cb_id)
6869 {
6870 hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip);
6871 usb_port_t port = hubd_child_dip2port(hubd, rdip);
6872
6873 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6874 "hubd_busop_add_eventcall: dip=0x%p, rdip=0x%p "
6875 "cookie=0x%p, cb=0x%p, arg=0x%p",
6876 (void *)dip, (void *)rdip, (void *)cookie, (void *)callback, arg);
6877 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6878 "(dip=%s%d, rdip=%s%d, event=%s)",
6879 ddi_driver_name(dip), ddi_get_instance(dip),
6880 ddi_driver_name(rdip), ddi_get_instance(rdip),
6881 ndi_event_cookie_to_name(hubd->h_ndi_event_hdl, cookie));
6882
6883 /* Set flag on children registering events */
6884 switch (ndi_event_cookie_to_tag(hubd->h_ndi_event_hdl, cookie)) {
6885 case USBA_EVENT_TAG_HOT_REMOVAL:
6886 mutex_enter(HUBD_MUTEX(hubd));
6887 hubd->h_child_events[port] |= HUBD_CHILD_EVENT_DISCONNECT;
6888 mutex_exit(HUBD_MUTEX(hubd));
6889
6890 break;
6891 case USBA_EVENT_TAG_PRE_SUSPEND:
6892 mutex_enter(HUBD_MUTEX(hubd));
6893 hubd->h_child_events[port] |= HUBD_CHILD_EVENT_PRESUSPEND;
6894 mutex_exit(HUBD_MUTEX(hubd));
6895
6896 break;
6897 default:
6898
6899 break;
6900 }
6901
6902 /* add callback to our event set */
6903 return (ndi_event_add_callback(hubd->h_ndi_event_hdl,
6904 rdip, cookie, callback, arg, NDI_SLEEP, cb_id));
6905 }
6906
6907
6908 static int
hubd_busop_remove_eventcall(dev_info_t * dip,ddi_callback_id_t cb_id)6909 hubd_busop_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id)
6910 {
6911 hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip);
6912 ndi_event_callbacks_t *id = (ndi_event_callbacks_t *)cb_id;
6913
6914 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6915 "hubd_busop_remove_eventcall: dip=0x%p, rdip=0x%p "
6916 "cookie=0x%p", (void *)dip, (void *)id->ndi_evtcb_dip,
6917 (void *)id->ndi_evtcb_cookie);
6918 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6919 "(dip=%s%d, rdip=%s%d, event=%s)",
6920 ddi_driver_name(dip), ddi_get_instance(dip),
6921 ddi_driver_name(id->ndi_evtcb_dip),
6922 ddi_get_instance(id->ndi_evtcb_dip),
6923 ndi_event_cookie_to_name(hubd->h_ndi_event_hdl,
6924 id->ndi_evtcb_cookie));
6925
6926 /* remove event registration from our event set */
6927 return (ndi_event_remove_callback(hubd->h_ndi_event_hdl, cb_id));
6928 }
6929
6930
6931 /*
6932 * event distribution
6933 *
6934 * hubd_do_callback:
6935 * Post this event to the specified child
6936 */
6937 static void
hubd_do_callback(hubd_t * hubd,dev_info_t * cdip,ddi_eventcookie_t cookie)6938 hubd_do_callback(hubd_t *hubd, dev_info_t *cdip, ddi_eventcookie_t cookie)
6939 {
6940 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6941 "hubd_do_callback");
6942
6943 (void) ndi_event_do_callback(hubd->h_ndi_event_hdl, cdip, cookie, NULL);
6944 }
6945
6946
6947 /*
6948 * hubd_run_callbacks:
6949 * Send this event to all children
6950 */
6951 static void
hubd_run_callbacks(hubd_t * hubd,usba_event_t type)6952 hubd_run_callbacks(hubd_t *hubd, usba_event_t type)
6953 {
6954 usb_port_t port;
6955
6956 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6957 "hubd_run_callbacks");
6958
6959 mutex_enter(HUBD_MUTEX(hubd));
6960 for (port = 1; port <= hubd->h_nports; port++) {
6961 /*
6962 * the childen_dips list may have dips that have been
6963 * already deallocated. we only get a post_detach notification
6964 * but not a destroy notification
6965 */
6966 if (hubd->h_children_dips[port]) {
6967 mutex_exit(HUBD_MUTEX(hubd));
6968 hubd_post_event(hubd, port, type);
6969 mutex_enter(HUBD_MUTEX(hubd));
6970 }
6971 }
6972 mutex_exit(HUBD_MUTEX(hubd));
6973 }
6974
6975
6976 /*
6977 * hubd_post_event
6978 * post event to a child on the port depending on the type
6979 */
6980 static void
hubd_post_event(hubd_t * hubd,usb_port_t port,usba_event_t type)6981 hubd_post_event(hubd_t *hubd, usb_port_t port, usba_event_t type)
6982 {
6983 int rval;
6984 dev_info_t *dip;
6985 usba_device_t *usba_device;
6986 ddi_eventcookie_t cookie, rm_cookie, suspend_cookie;
6987
6988 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6989 "hubd_post_event: port=%d event=%s", port,
6990 ndi_event_tag_to_name(hubd->h_ndi_event_hdl, type));
6991
6992 cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl, type);
6993 rm_cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl,
6994 USBA_EVENT_TAG_HOT_REMOVAL);
6995 suspend_cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl,
6996 USBA_EVENT_TAG_PRE_SUSPEND);
6997
6998 /*
6999 * Hotplug daemon may be attaching a driver that may be registering
7000 * event callbacks. So it already has got the device tree lock and
7001 * event handle mutex. So to prevent a deadlock while posting events,
7002 * we grab and release the locks in the same order.
7003 */
7004 mutex_enter(HUBD_MUTEX(hubd));
7005 dip = hubd->h_children_dips[port];
7006 usba_device = hubd->h_usba_devices[port];
7007 mutex_exit(HUBD_MUTEX(hubd));
7008
7009 switch (type) {
7010 case USBA_EVENT_TAG_HOT_REMOVAL:
7011 /* Clear the registered event flag */
7012 mutex_enter(HUBD_MUTEX(hubd));
7013 hubd->h_child_events[port] &= ~HUBD_CHILD_EVENT_DISCONNECT;
7014 mutex_exit(HUBD_MUTEX(hubd));
7015
7016 hubd_do_callback(hubd, dip, cookie);
7017 usba_persistent_pipe_close(usba_device);
7018
7019 /*
7020 * Mark the dip for deletion only after the driver has
7021 * seen the disconnect event to prevent cleanup thread
7022 * from stepping in between.
7023 */
7024 mutex_enter(&(DEVI(dip)->devi_lock));
7025 DEVI_SET_DEVICE_REMOVED(dip);
7026 mutex_exit(&(DEVI(dip)->devi_lock));
7027
7028 break;
7029 case USBA_EVENT_TAG_PRE_SUSPEND:
7030 mutex_enter(HUBD_MUTEX(hubd));
7031 hubd->h_child_events[port] &= ~HUBD_CHILD_EVENT_PRESUSPEND;
7032 mutex_exit(HUBD_MUTEX(hubd));
7033
7034 hubd_do_callback(hubd, dip, cookie);
7035 /*
7036 * persistent pipe close for this event is taken care by the
7037 * caller after verfying that all children can suspend
7038 */
7039
7040 break;
7041 case USBA_EVENT_TAG_HOT_INSERTION:
7042 /*
7043 * Check if this child has missed the disconnect event before
7044 * it registered for event callbacks
7045 */
7046 mutex_enter(HUBD_MUTEX(hubd));
7047 if (hubd->h_child_events[port] & HUBD_CHILD_EVENT_DISCONNECT) {
7048 /* clear the flag and post disconnect event */
7049 hubd->h_child_events[port] &=
7050 ~HUBD_CHILD_EVENT_DISCONNECT;
7051 mutex_exit(HUBD_MUTEX(hubd));
7052 hubd_do_callback(hubd, dip, rm_cookie);
7053 usba_persistent_pipe_close(usba_device);
7054 mutex_enter(HUBD_MUTEX(hubd));
7055 }
7056 mutex_exit(HUBD_MUTEX(hubd));
7057
7058 /*
7059 * Mark the dip as reinserted to prevent cleanup thread
7060 * from stepping in.
7061 */
7062 mutex_enter(&(DEVI(dip)->devi_lock));
7063 DEVI_SET_DEVICE_REINSERTED(dip);
7064 mutex_exit(&(DEVI(dip)->devi_lock));
7065
7066 rval = usba_persistent_pipe_open(usba_device);
7067 if (rval != USB_SUCCESS) {
7068 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
7069 hubd->h_log_handle,
7070 "failed to reopen all pipes on reconnect");
7071 }
7072
7073 hubd_do_callback(hubd, dip, cookie);
7074
7075 /*
7076 * We might see a connect event only if hotplug thread for
7077 * disconnect event don't run in time.
7078 * Set the flag again, so we don't miss posting a
7079 * disconnect event.
7080 */
7081 mutex_enter(HUBD_MUTEX(hubd));
7082 hubd->h_child_events[port] |= HUBD_CHILD_EVENT_DISCONNECT;
7083 mutex_exit(HUBD_MUTEX(hubd));
7084
7085 break;
7086 case USBA_EVENT_TAG_POST_RESUME:
7087 /*
7088 * Check if this child has missed the pre-suspend event before
7089 * it registered for event callbacks
7090 */
7091 mutex_enter(HUBD_MUTEX(hubd));
7092 if (hubd->h_child_events[port] & HUBD_CHILD_EVENT_PRESUSPEND) {
7093 /* clear the flag and post pre_suspend event */
7094 hubd->h_port_state[port] &=
7095 ~HUBD_CHILD_EVENT_PRESUSPEND;
7096 mutex_exit(HUBD_MUTEX(hubd));
7097 hubd_do_callback(hubd, dip, suspend_cookie);
7098 mutex_enter(HUBD_MUTEX(hubd));
7099 }
7100 mutex_exit(HUBD_MUTEX(hubd));
7101
7102 mutex_enter(&usba_device->usb_mutex);
7103 usba_device->usb_no_cpr = 0;
7104 mutex_exit(&usba_device->usb_mutex);
7105
7106 /*
7107 * Since the pipe has already been opened by hub
7108 * at DDI_RESUME time, there is no need for a
7109 * persistent pipe open
7110 */
7111 hubd_do_callback(hubd, dip, cookie);
7112
7113 /*
7114 * Set the flag again, so we don't miss posting a
7115 * pre-suspend event. This enforces a tighter
7116 * dev_state model.
7117 */
7118 mutex_enter(HUBD_MUTEX(hubd));
7119 hubd->h_child_events[port] |= HUBD_CHILD_EVENT_PRESUSPEND;
7120 mutex_exit(HUBD_MUTEX(hubd));
7121 break;
7122 }
7123 }
7124
7125
7126 /*
7127 * handling of events coming from above
7128 */
7129 static int
hubd_disconnect_event_cb(dev_info_t * dip)7130 hubd_disconnect_event_cb(dev_info_t *dip)
7131 {
7132 hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip);
7133 usb_port_t port, nports;
7134 usba_device_t *usba_dev;
7135 usba_event_t tag = USBA_EVENT_TAG_HOT_REMOVAL;
7136
7137 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
7138 "hubd_disconnect_event_cb: tag=%d", tag);
7139
7140 ndi_devi_enter(dip);
7141
7142 mutex_enter(HUBD_MUTEX(hubd));
7143 switch (hubd->h_dev_state) {
7144 case USB_DEV_ONLINE:
7145 case USB_DEV_PWRED_DOWN:
7146 hubd->h_dev_state = USB_DEV_DISCONNECTED;
7147 /* stop polling on the interrupt pipe */
7148 hubd_stop_polling(hubd);
7149
7150 /* FALLTHROUGH */
7151 case USB_DEV_SUSPENDED:
7152 /* we remain in this state */
7153 mutex_exit(HUBD_MUTEX(hubd));
7154 hubd_run_callbacks(hubd, tag);
7155 mutex_enter(HUBD_MUTEX(hubd));
7156
7157 /* close all the open pipes of our children */
7158 nports = hubd->h_nports;
7159 for (port = 1; port <= nports; port++) {
7160 usba_dev = hubd->h_usba_devices[port];
7161 if (usba_dev != NULL) {
7162 mutex_exit(HUBD_MUTEX(hubd));
7163 usba_persistent_pipe_close(usba_dev);
7164 mutex_enter(HUBD_MUTEX(hubd));
7165 }
7166 }
7167
7168 break;
7169 case USB_DEV_DISCONNECTED:
7170 /* avoid passing multiple disconnects to children */
7171 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
7172 "hubd_disconnect_event_cb: Already disconnected");
7173
7174 break;
7175 default:
7176 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
7177 "hubd_disconnect_event_cb: Illegal devstate=%d",
7178 hubd->h_dev_state);
7179
7180 break;
7181 }
7182 mutex_exit(HUBD_MUTEX(hubd));
7183
7184 ndi_devi_exit(dip);
7185
7186 return (USB_SUCCESS);
7187 }
7188
7189
7190 static int
hubd_reconnect_event_cb(dev_info_t * dip)7191 hubd_reconnect_event_cb(dev_info_t *dip)
7192 {
7193 int rval;
7194
7195 ndi_devi_enter(dip);
7196 rval = hubd_restore_state_cb(dip);
7197 ndi_devi_exit(dip);
7198
7199 return (rval);
7200 }
7201
7202
7203 /*
7204 * hubd_pre_suspend_event_cb
7205 * propogate event for binary compatibility of old drivers
7206 */
7207 static int
hubd_pre_suspend_event_cb(dev_info_t * dip)7208 hubd_pre_suspend_event_cb(dev_info_t *dip)
7209 {
7210 hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip);
7211
7212 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
7213 "hubd_pre_suspend_event_cb");
7214
7215 /* disable hotplug thread */
7216 mutex_enter(HUBD_MUTEX(hubd));
7217 hubd->h_hotplug_thread++;
7218 hubd_stop_polling(hubd);
7219
7220 /* keep PM out till we see a cpr resume */
7221 (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0);
7222 mutex_exit(HUBD_MUTEX(hubd));
7223
7224 ndi_devi_enter(dip);
7225 hubd_run_callbacks(hubd, USBA_EVENT_TAG_PRE_SUSPEND);
7226 ndi_devi_exit(dip);
7227
7228 return (USB_SUCCESS);
7229 }
7230
7231
7232 /*
7233 * hubd_post_resume_event_cb
7234 * propogate event for binary compatibility of old drivers
7235 */
7236 static int
hubd_post_resume_event_cb(dev_info_t * dip)7237 hubd_post_resume_event_cb(dev_info_t *dip)
7238 {
7239 hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip);
7240
7241 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
7242 "hubd_post_resume_event_cb");
7243
7244 ndi_devi_enter(dip);
7245 hubd_run_callbacks(hubd, USBA_EVENT_TAG_POST_RESUME);
7246 ndi_devi_exit(dip);
7247
7248 mutex_enter(HUBD_MUTEX(hubd));
7249
7250 /* enable PM */
7251 (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
7252
7253 /* allow hotplug thread */
7254 hubd->h_hotplug_thread--;
7255
7256 /* start polling */
7257 hubd_start_polling(hubd, 0);
7258 mutex_exit(HUBD_MUTEX(hubd));
7259
7260 return (USB_SUCCESS);
7261 }
7262
7263
7264 /*
7265 * hubd_cpr_suspend
7266 * save the current state of the driver/device
7267 */
7268 static int
hubd_cpr_suspend(hubd_t * hubd)7269 hubd_cpr_suspend(hubd_t *hubd)
7270 {
7271 usb_port_t port, nports;
7272 usba_device_t *usba_dev;
7273 uchar_t no_cpr = 0;
7274 int rval = USB_FAILURE;
7275
7276 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
7277 "hubd_cpr_suspend: Begin");
7278
7279 /* Make sure device is powered up to save state. */
7280 mutex_enter(HUBD_MUTEX(hubd));
7281 hubd_pm_busy_component(hubd, hubd->h_dip, 0);
7282 mutex_exit(HUBD_MUTEX(hubd));
7283
7284 /* bring the device to full power */
7285 (void) pm_raise_power(hubd->h_dip, 0, USB_DEV_OS_FULL_PWR);
7286 mutex_enter(HUBD_MUTEX(hubd));
7287
7288 switch (hubd->h_dev_state) {
7289 case USB_DEV_ONLINE:
7290 case USB_DEV_PWRED_DOWN:
7291 case USB_DEV_DISCONNECTED:
7292 /* find out if all our children have been quiesced */
7293 nports = hubd->h_nports;
7294 for (port = 1; (no_cpr == 0) && (port <= nports); port++) {
7295 usba_dev = hubd->h_usba_devices[port];
7296 if (usba_dev != NULL) {
7297 mutex_enter(&usba_dev->usb_mutex);
7298 no_cpr += usba_dev->usb_no_cpr;
7299 mutex_exit(&usba_dev->usb_mutex);
7300 }
7301 }
7302 if (no_cpr > 0) {
7303 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
7304 "Children busy - can't checkpoint");
7305 /* remain in same state to fail checkpoint */
7306
7307 break;
7308 } else {
7309 /*
7310 * do not suspend if our hotplug thread
7311 * or the deathrow thread is active
7312 */
7313 if ((hubd->h_hotplug_thread > 1) ||
7314 (hubd->h_cleanup_active == B_TRUE)) {
7315 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
7316 hubd->h_log_handle,
7317 "hotplug thread active - can't cpr");
7318 /* remain in same state to fail checkpoint */
7319
7320 break;
7321 }
7322
7323 /* quiesce ourselves now */
7324 hubd_stop_polling(hubd);
7325
7326 /* close all the open pipes of our children */
7327 for (port = 1; port <= nports; port++) {
7328 usba_dev = hubd->h_usba_devices[port];
7329 if (usba_dev != NULL) {
7330 mutex_exit(HUBD_MUTEX(hubd));
7331 usba_persistent_pipe_close(usba_dev);
7332 if (hubd_suspend_port(hubd, port)) {
7333 USB_DPRINTF_L0(
7334 DPRINT_MASK_HOTPLUG,
7335 hubd->h_log_handle,
7336 "suspending port %d failed",
7337 port);
7338 }
7339 mutex_enter(HUBD_MUTEX(hubd));
7340 }
7341
7342 }
7343 hubd->h_dev_state = USB_DEV_SUSPENDED;
7344
7345 /*
7346 * if we are the root hub, we close our pipes
7347 * ourselves.
7348 */
7349 if (usba_is_root_hub(hubd->h_dip)) {
7350 mutex_exit(HUBD_MUTEX(hubd));
7351 usba_persistent_pipe_close(
7352 usba_get_usba_device(hubd->h_dip));
7353 mutex_enter(HUBD_MUTEX(hubd));
7354 }
7355 rval = USB_SUCCESS;
7356
7357 break;
7358 }
7359 case USB_DEV_SUSPENDED:
7360 default:
7361 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
7362 "hubd_cpr_suspend: Illegal dev state=%d",
7363 hubd->h_dev_state);
7364
7365 break;
7366 }
7367
7368 hubd_pm_idle_component(hubd, hubd->h_dip, 0);
7369 mutex_exit(HUBD_MUTEX(hubd));
7370
7371 return (rval);
7372 }
7373
7374 static void
hubd_cpr_resume(dev_info_t * dip)7375 hubd_cpr_resume(dev_info_t *dip)
7376 {
7377 int rval;
7378
7379 ndi_devi_enter(dip);
7380 /*
7381 * if we are the root hub, we open our pipes
7382 * ourselves.
7383 */
7384 if (usba_is_root_hub(dip)) {
7385 rval = usba_persistent_pipe_open(
7386 usba_get_usba_device(dip));
7387 ASSERT(rval == USB_SUCCESS);
7388 }
7389 (void) hubd_restore_state_cb(dip);
7390 ndi_devi_exit(dip);
7391 }
7392
7393
7394 /*
7395 * hubd_restore_state_cb
7396 * Event callback to restore device state
7397 */
7398 static int
hubd_restore_state_cb(dev_info_t * dip)7399 hubd_restore_state_cb(dev_info_t *dip)
7400 {
7401 hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip);
7402
7403 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
7404 "hubd_restore_state_cb: Begin");
7405
7406 /* restore the state of this device */
7407 hubd_restore_device_state(dip, hubd);
7408
7409 return (USB_SUCCESS);
7410 }
7411
7412
7413 /*
7414 * registering for events
7415 */
7416 static int
hubd_register_events(hubd_t * hubd)7417 hubd_register_events(hubd_t *hubd)
7418 {
7419 int rval = USB_SUCCESS;
7420
7421 if (usba_is_root_hub(hubd->h_dip)) {
7422 hubd_register_cpr_callback(hubd);
7423 } else {
7424 rval = usb_register_event_cbs(hubd->h_dip, &hubd_events, 0);
7425 }
7426
7427 return (rval);
7428 }
7429
7430
7431 /*
7432 * hubd cpr callback related functions
7433 *
7434 * hubd_cpr_post_user_callb:
7435 * This function is called during checkpoint & resume -
7436 * 1. after user threads are stopped during checkpoint
7437 * 2. after kernel threads are resumed during resume
7438 */
7439 /* ARGSUSED */
7440 static boolean_t
hubd_cpr_post_user_callb(void * arg,int code)7441 hubd_cpr_post_user_callb(void *arg, int code)
7442 {
7443 hubd_cpr_t *cpr_cb = (hubd_cpr_t *)arg;
7444 hubd_t *hubd = cpr_cb->statep;
7445 int retry = 0;
7446
7447 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
7448 "hubd_cpr_post_user_callb");
7449
7450 switch (code) {
7451 case CB_CODE_CPR_CHKPT:
7452 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, hubd->h_log_handle,
7453 "hubd_cpr_post_user_callb: CB_CODE_CPR_CHKPT");
7454
7455 mutex_enter(HUBD_MUTEX(hubd));
7456
7457 /* turn off deathrow thread */
7458 hubd->h_cleanup_enabled = B_FALSE;
7459
7460 /* give up if deathrow thread doesn't exit */
7461 while ((hubd->h_cleanup_active == B_TRUE) && (retry++ < 3)) {
7462 mutex_exit(HUBD_MUTEX(hubd));
7463 delay(drv_usectohz(hubd_dip_cleanup_delay));
7464
7465 USB_DPRINTF_L2(DPRINT_MASK_EVENTS, hubd->h_log_handle,
7466 "hubd_cpr_post_user_callb, waiting for "
7467 "deathrow thread to exit");
7468 mutex_enter(HUBD_MUTEX(hubd));
7469 }
7470
7471 mutex_exit(HUBD_MUTEX(hubd));
7472
7473 /* save the state of the device */
7474 (void) hubd_pre_suspend_event_cb(hubd->h_dip);
7475
7476 return (B_TRUE);
7477 case CB_CODE_CPR_RESUME:
7478 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, hubd->h_log_handle,
7479 "hubd_cpr_post_user_callb: CB_CODE_CPR_RESUME");
7480
7481 /* restore the state of the device */
7482 (void) hubd_post_resume_event_cb(hubd->h_dip);
7483
7484 /* turn on deathrow thread */
7485 mutex_enter(HUBD_MUTEX(hubd));
7486 hubd->h_cleanup_enabled = B_TRUE;
7487 mutex_exit(HUBD_MUTEX(hubd));
7488
7489 hubd_schedule_cleanup(hubd->h_usba_device->usb_root_hub_dip);
7490
7491 return (B_TRUE);
7492 default:
7493
7494 return (B_FALSE);
7495 }
7496
7497 }
7498
7499
7500 /* register callback with cpr framework */
7501 void
hubd_register_cpr_callback(hubd_t * hubd)7502 hubd_register_cpr_callback(hubd_t *hubd)
7503 {
7504 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
7505 "hubd_register_cpr_callback");
7506
7507 mutex_enter(HUBD_MUTEX(hubd));
7508 hubd->h_cpr_cb =
7509 (hubd_cpr_t *)kmem_zalloc(sizeof (hubd_cpr_t), KM_SLEEP);
7510 mutex_exit(HUBD_MUTEX(hubd));
7511 mutex_init(&hubd->h_cpr_cb->lockp, NULL, MUTEX_DRIVER,
7512 hubd->h_dev_data->dev_iblock_cookie);
7513 hubd->h_cpr_cb->statep = hubd;
7514 hubd->h_cpr_cb->cpr.cc_lockp = &hubd->h_cpr_cb->lockp;
7515 hubd->h_cpr_cb->cpr.cc_id = callb_add(hubd_cpr_post_user_callb,
7516 (void *)hubd->h_cpr_cb, CB_CL_CPR_POST_USER, "hubd");
7517 }
7518
7519
7520 /* unregister callback with cpr framework */
7521 void
hubd_unregister_cpr_callback(hubd_t * hubd)7522 hubd_unregister_cpr_callback(hubd_t *hubd)
7523 {
7524 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
7525 "hubd_unregister_cpr_callback");
7526
7527 if (hubd->h_cpr_cb) {
7528 (void) callb_delete(hubd->h_cpr_cb->cpr.cc_id);
7529 mutex_destroy(&hubd->h_cpr_cb->lockp);
7530 mutex_enter(HUBD_MUTEX(hubd));
7531 kmem_free(hubd->h_cpr_cb, sizeof (hubd_cpr_t));
7532 mutex_exit(HUBD_MUTEX(hubd));
7533 }
7534 }
7535
7536
7537 /*
7538 * Power management
7539 *
7540 * create the pm components required for power management
7541 */
7542 static void
hubd_create_pm_components(dev_info_t * dip,hubd_t * hubd)7543 hubd_create_pm_components(dev_info_t *dip, hubd_t *hubd)
7544 {
7545 hub_power_t *hubpm;
7546
7547 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
7548 "hubd_create_pm_components: Begin");
7549
7550 /* Allocate the state structure */
7551 hubpm = kmem_zalloc(sizeof (hub_power_t), KM_SLEEP);
7552
7553 hubd->h_hubpm = hubpm;
7554 hubpm->hubp_hubd = hubd;
7555 hubpm->hubp_pm_capabilities = 0;
7556 hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
7557 hubpm->hubp_time_at_full_power = gethrtime();
7558 hubpm->hubp_min_pm_threshold = hubdi_min_pm_threshold * NANOSEC;
7559
7560 /* alloc memory to save power states of children */
7561 hubpm->hubp_child_pwrstate = (uint8_t *)
7562 kmem_zalloc(MAX_PORTS + 1, KM_SLEEP);
7563
7564 /*
7565 * if the enable remote wakeup fails
7566 * we still want to enable
7567 * parent notification so we can PM the children
7568 */
7569 usb_enable_parent_notification(dip);
7570
7571 if (usb_handle_remote_wakeup(dip,
7572 USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
7573 uint_t pwr_states;
7574
7575 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
7576 "hubd_create_pm_components: "
7577 "Remote Wakeup Enabled");
7578
7579 if (usb_create_pm_components(dip, &pwr_states) ==
7580 USB_SUCCESS) {
7581 mutex_enter(HUBD_MUTEX(hubd));
7582 hubpm->hubp_wakeup_enabled = 1;
7583 hubpm->hubp_pwr_states = (uint8_t)pwr_states;
7584
7585 /* we are busy now till end of the attach */
7586 hubd_pm_busy_component(hubd, dip, 0);
7587 mutex_exit(HUBD_MUTEX(hubd));
7588
7589 /* bring the device to full power */
7590 (void) pm_raise_power(dip, 0,
7591 USB_DEV_OS_FULL_PWR);
7592 }
7593 }
7594
7595 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
7596 "hubd_create_pm_components: END");
7597 }
7598
7599
7600 /*
7601 * Attachment point management
7602 */
7603 /* ARGSUSED */
7604 int
usba_hubdi_open(dev_info_t * dip,dev_t * devp,int flags,int otyp,cred_t * credp)7605 usba_hubdi_open(dev_info_t *dip, dev_t *devp, int flags, int otyp,
7606 cred_t *credp)
7607 {
7608 hubd_t *hubd;
7609
7610 if (otyp != OTYP_CHR)
7611 return (EINVAL);
7612
7613 hubd = hubd_get_soft_state(dip);
7614 if (hubd == NULL) {
7615 return (ENXIO);
7616 }
7617
7618 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
7619 "hubd_open:");
7620
7621 mutex_enter(HUBD_MUTEX(hubd));
7622 if ((flags & FEXCL) && (hubd->h_softstate & HUBD_SS_ISOPEN)) {
7623 mutex_exit(HUBD_MUTEX(hubd));
7624
7625 return (EBUSY);
7626 }
7627
7628 hubd->h_softstate |= HUBD_SS_ISOPEN;
7629 mutex_exit(HUBD_MUTEX(hubd));
7630
7631 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "opened");
7632
7633 return (0);
7634 }
7635
7636
7637 /* ARGSUSED */
7638 int
usba_hubdi_close(dev_info_t * dip,dev_t dev,int flag,int otyp,cred_t * credp)7639 usba_hubdi_close(dev_info_t *dip, dev_t dev, int flag, int otyp,
7640 cred_t *credp)
7641 {
7642 hubd_t *hubd;
7643
7644 if (otyp != OTYP_CHR) {
7645 return (EINVAL);
7646 }
7647
7648 hubd = hubd_get_soft_state(dip);
7649
7650 if (hubd == NULL) {
7651 return (ENXIO);
7652 }
7653
7654 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "hubd_close:");
7655
7656 mutex_enter(HUBD_MUTEX(hubd));
7657 hubd->h_softstate &= ~HUBD_SS_ISOPEN;
7658 mutex_exit(HUBD_MUTEX(hubd));
7659
7660 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "closed");
7661
7662 return (0);
7663 }
7664
7665
7666 /*
7667 * hubd_ioctl: cfgadm controls
7668 */
7669 /* ARGSUSED */
7670 int
usba_hubdi_ioctl(dev_info_t * self,dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)7671 usba_hubdi_ioctl(dev_info_t *self, dev_t dev, int cmd, intptr_t arg,
7672 int mode, cred_t *credp, int *rvalp)
7673 {
7674 int rv = 0;
7675 char *msg; /* for messages */
7676 hubd_t *hubd;
7677 usb_port_t port = 0;
7678 dev_info_t *child_dip = NULL;
7679 dev_info_t *rh_dip;
7680 devctl_ap_state_t ap_state;
7681 struct devctl_iocdata *dcp = NULL;
7682 usb_pipe_state_t prev_pipe_state = 0;
7683
7684 if ((hubd = hubd_get_soft_state(self)) == NULL) {
7685
7686 return (ENXIO);
7687 }
7688
7689 rh_dip = hubd->h_usba_device->usb_root_hub_dip;
7690
7691 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
7692 "usba_hubdi_ioctl: "
7693 "cmd=%x, arg=%lx, mode=%x, cred=%p, rval=%p dev=0x%lx",
7694 cmd, arg, mode, (void *)credp, (void *)rvalp, dev);
7695
7696 /* read devctl ioctl data */
7697 if ((cmd != DEVCTL_AP_CONTROL) &&
7698 (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)) {
7699
7700 return (EFAULT);
7701 }
7702
7703 /*
7704 * make sure the hub is connected before trying any
7705 * of the following operations:
7706 * configure, connect, disconnect
7707 */
7708 mutex_enter(HUBD_MUTEX(hubd));
7709
7710 switch (cmd) {
7711 case DEVCTL_AP_DISCONNECT:
7712 case DEVCTL_AP_UNCONFIGURE:
7713 case DEVCTL_AP_CONFIGURE:
7714 if (hubd->h_dev_state == USB_DEV_DISCONNECTED) {
7715 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
7716 "hubd: already gone");
7717 mutex_exit(HUBD_MUTEX(hubd));
7718 if (dcp) {
7719 ndi_dc_freehdl(dcp);
7720 }
7721
7722 return (EIO);
7723 }
7724
7725 /* FALLTHROUGH */
7726 case DEVCTL_AP_GETSTATE:
7727 if ((port = hubd_get_port_num(hubd, dcp)) == 0) {
7728 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
7729 "hubd: bad port");
7730 mutex_exit(HUBD_MUTEX(hubd));
7731 if (dcp) {
7732 ndi_dc_freehdl(dcp);
7733 }
7734
7735 return (EINVAL);
7736 }
7737 break;
7738
7739 case DEVCTL_AP_CONTROL:
7740
7741 break;
7742 default:
7743 mutex_exit(HUBD_MUTEX(hubd));
7744 if (dcp) {
7745 ndi_dc_freehdl(dcp);
7746 }
7747
7748 return (ENOTTY);
7749 }
7750
7751 /* should not happen, just in case */
7752 if (hubd->h_dev_state == USB_DEV_SUSPENDED) {
7753 mutex_exit(HUBD_MUTEX(hubd));
7754 if (dcp) {
7755 ndi_dc_freehdl(dcp);
7756 }
7757
7758 return (EIO);
7759 }
7760
7761 if (hubd->h_reset_port[port]) {
7762 USB_DPRINTF_L2(DPRINT_MASK_CBOPS, hubd->h_log_handle,
7763 "This port is resetting, just return");
7764 mutex_exit(HUBD_MUTEX(hubd));
7765 if (dcp) {
7766 ndi_dc_freehdl(dcp);
7767 }
7768
7769 return (EIO);
7770 }
7771
7772 hubd_pm_busy_component(hubd, hubd->h_dip, 0);
7773 mutex_exit(HUBD_MUTEX(hubd));
7774
7775 /* go full power */
7776 (void) pm_raise_power(hubd->h_dip, 0, USB_DEV_OS_FULL_PWR);
7777
7778 ndi_devi_enter(ddi_get_parent(rh_dip));
7779 ndi_devi_enter(rh_dip);
7780 ndi_devi_enter(hubd->h_dip);
7781
7782 mutex_enter(HUBD_MUTEX(hubd));
7783
7784 hubd->h_hotplug_thread++;
7785
7786 /* stop polling if it was active */
7787 if (hubd->h_ep1_ph) {
7788 mutex_exit(HUBD_MUTEX(hubd));
7789 (void) usb_pipe_get_state(hubd->h_ep1_ph, &prev_pipe_state,
7790 USB_FLAGS_SLEEP);
7791 mutex_enter(HUBD_MUTEX(hubd));
7792
7793 if (prev_pipe_state == USB_PIPE_STATE_ACTIVE) {
7794 hubd_stop_polling(hubd);
7795 }
7796 }
7797
7798 switch (cmd) {
7799 case DEVCTL_AP_DISCONNECT:
7800 if (hubd_delete_child(hubd, port,
7801 NDI_DEVI_REMOVE, B_FALSE) != USB_SUCCESS) {
7802 rv = EIO;
7803 }
7804
7805 break;
7806 case DEVCTL_AP_UNCONFIGURE:
7807 if (hubd_delete_child(hubd, port,
7808 NDI_UNCONFIG, B_FALSE) != USB_SUCCESS) {
7809 rv = EIO;
7810 }
7811
7812 break;
7813 case DEVCTL_AP_CONFIGURE:
7814 /* toggle port */
7815 if (hubd_toggle_port(hubd, port) != USB_SUCCESS) {
7816 rv = EIO;
7817
7818 break;
7819 }
7820
7821 (void) hubd_handle_port_connect(hubd, port);
7822 child_dip = hubd_get_child_dip(hubd, port);
7823 mutex_exit(HUBD_MUTEX(hubd));
7824
7825 ndi_devi_exit(hubd->h_dip);
7826 ndi_devi_exit(rh_dip);
7827 ndi_devi_exit(ddi_get_parent(rh_dip));
7828 if (child_dip == NULL) {
7829 rv = EIO;
7830 } else {
7831 ndi_hold_devi(child_dip);
7832 if (ndi_devi_online(child_dip, 0) != NDI_SUCCESS)
7833 rv = EIO;
7834 ndi_rele_devi(child_dip);
7835 }
7836 ndi_devi_enter(ddi_get_parent(rh_dip));
7837 ndi_devi_enter(rh_dip);
7838 ndi_devi_enter(hubd->h_dip);
7839
7840 mutex_enter(HUBD_MUTEX(hubd));
7841
7842 break;
7843 case DEVCTL_AP_GETSTATE:
7844 switch (hubd_cfgadm_state(hubd, port)) {
7845 case HUBD_CFGADM_DISCONNECTED:
7846 /* port previously 'disconnected' by cfgadm */
7847 ap_state.ap_rstate = AP_RSTATE_DISCONNECTED;
7848 ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
7849 ap_state.ap_condition = AP_COND_OK;
7850
7851 break;
7852 case HUBD_CFGADM_UNCONFIGURED:
7853 ap_state.ap_rstate = AP_RSTATE_CONNECTED;
7854 ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
7855 ap_state.ap_condition = AP_COND_OK;
7856
7857 break;
7858 case HUBD_CFGADM_CONFIGURED:
7859 ap_state.ap_rstate = AP_RSTATE_CONNECTED;
7860 ap_state.ap_ostate = AP_OSTATE_CONFIGURED;
7861 ap_state.ap_condition = AP_COND_OK;
7862
7863 break;
7864 case HUBD_CFGADM_STILL_REFERENCED:
7865 ap_state.ap_rstate = AP_RSTATE_EMPTY;
7866 ap_state.ap_ostate = AP_OSTATE_CONFIGURED;
7867 ap_state.ap_condition = AP_COND_UNUSABLE;
7868
7869 break;
7870 case HUBD_CFGADM_EMPTY:
7871 default:
7872 ap_state.ap_rstate = AP_RSTATE_EMPTY;
7873 ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
7874 ap_state.ap_condition = AP_COND_OK;
7875
7876 break;
7877 }
7878
7879 ap_state.ap_last_change = (time_t)-1;
7880 ap_state.ap_error_code = 0;
7881 ap_state.ap_in_transition = 0;
7882
7883 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
7884 "DEVCTL_AP_GETSTATE: "
7885 "ostate=0x%x, rstate=0x%x, condition=0x%x",
7886 ap_state.ap_ostate,
7887 ap_state.ap_rstate, ap_state.ap_condition);
7888
7889 /* copy the return-AP-state information to the user space */
7890 if (ndi_dc_return_ap_state(&ap_state, dcp) != NDI_SUCCESS) {
7891 rv = EFAULT;
7892 }
7893
7894 break;
7895 case DEVCTL_AP_CONTROL:
7896 {
7897 /*
7898 * Generic devctl for hardware-specific functionality.
7899 * For list of sub-commands see hubd_impl.h
7900 */
7901 hubd_ioctl_data_t ioc; /* for 64 byte copies */
7902
7903 /* copy user ioctl data in first */
7904 #ifdef _MULTI_DATAMODEL
7905 if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
7906 hubd_ioctl_data_32_t ioc32;
7907
7908 if (ddi_copyin((void *)arg, (void *)&ioc32,
7909 sizeof (ioc32), mode) != 0) {
7910 rv = EFAULT;
7911
7912 break;
7913 }
7914 ioc.cmd = (uint_t)ioc32.cmd;
7915 ioc.port = (uint_t)ioc32.port;
7916 ioc.get_size = (uint_t)ioc32.get_size;
7917 ioc.buf = (caddr_t)(uintptr_t)ioc32.buf;
7918 ioc.bufsiz = (uint_t)ioc32.bufsiz;
7919 ioc.misc_arg = (uint_t)ioc32.misc_arg;
7920 } else
7921 #endif /* _MULTI_DATAMODEL */
7922 if (ddi_copyin((void *)arg, (void *)&ioc, sizeof (ioc),
7923 mode) != 0) {
7924 rv = EFAULT;
7925
7926 break;
7927 }
7928
7929 USB_DPRINTF_L3(DPRINT_MASK_CBOPS, hubd->h_log_handle,
7930 "DEVCTL_AP_CONTROL: ioc: cmd=0x%x port=%d get_size=%d"
7931 "\n\tbuf=0x%p, bufsiz=%d, misc_arg=%d", ioc.cmd,
7932 ioc.port, ioc.get_size, (void *)ioc.buf, ioc.bufsiz,
7933 ioc.misc_arg);
7934
7935 /*
7936 * To avoid BE/LE and 32/64 issues, a get_size always
7937 * returns a 32-bit number.
7938 */
7939 if (ioc.get_size != 0 && ioc.bufsiz != (sizeof (uint32_t))) {
7940 rv = EINVAL;
7941
7942 break;
7943 }
7944
7945 switch (ioc.cmd) {
7946 case USB_DESCR_TYPE_DEV:
7947 msg = "DEVCTL_AP_CONTROL: GET_DEVICE_DESC";
7948 if (ioc.get_size) {
7949 /* uint32 so this works 32/64 */
7950 uint32_t size = sizeof (usb_dev_descr_t);
7951
7952 if (ddi_copyout((void *)&size, ioc.buf,
7953 ioc.bufsiz, mode) != 0) {
7954 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
7955 hubd->h_log_handle,
7956 "%s: get_size copyout failed", msg);
7957 rv = EIO;
7958
7959 break;
7960 }
7961 } else { /* send out the actual descr */
7962 usb_dev_descr_t *dev_descrp;
7963
7964 /* check child_dip */
7965 if ((child_dip = hubd_get_child_dip(hubd,
7966 ioc.port)) == NULL) {
7967 rv = EINVAL;
7968
7969 break;
7970 }
7971
7972 dev_descrp = usb_get_dev_descr(child_dip);
7973 if (ioc.bufsiz != sizeof (*dev_descrp)) {
7974 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
7975 hubd->h_log_handle,
7976 "%s: bufsize passed (%d) != sizeof "
7977 "usba_device_descr_t (%d)", msg,
7978 ioc.bufsiz, dev_descrp->bLength);
7979 rv = EINVAL;
7980
7981 break;
7982 }
7983
7984 if (ddi_copyout((void *)dev_descrp,
7985 ioc.buf, ioc.bufsiz, mode) != 0) {
7986 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
7987 hubd->h_log_handle,
7988 "%s: copyout failed.", msg);
7989 rv = EIO;
7990
7991 break;
7992 }
7993 }
7994 break;
7995 case USB_DESCR_TYPE_STRING:
7996 {
7997 char *str;
7998 uint32_t size;
7999 usba_device_t *usba_device;
8000
8001 msg = "DEVCTL_AP_CONTROL: GET_STRING_DESCR";
8002 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
8003 "%s: string request: %d", msg, ioc.misc_arg);
8004
8005 /* recheck */
8006 if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) ==
8007 NULL) {
8008 rv = EINVAL;
8009
8010 break;
8011 }
8012 usba_device = usba_get_usba_device(child_dip);
8013
8014 switch (ioc.misc_arg) {
8015 case HUBD_MFG_STR:
8016 str = usba_device->usb_mfg_str;
8017
8018 break;
8019 case HUBD_PRODUCT_STR:
8020 str = usba_device->usb_product_str;
8021
8022 break;
8023 case HUBD_SERIALNO_STR:
8024 str = usba_device->usb_serialno_str;
8025
8026 break;
8027 case HUBD_CFG_DESCR_STR:
8028 mutex_enter(&usba_device->usb_mutex);
8029 str = usba_device->usb_cfg_str_descr[
8030 usba_device->usb_active_cfg_ndx];
8031 mutex_exit(&usba_device->usb_mutex);
8032
8033 break;
8034 default:
8035 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8036 hubd->h_log_handle,
8037 "%s: Invalid string request", msg);
8038 rv = EINVAL;
8039
8040 break;
8041 } /* end of switch */
8042
8043 if (rv != 0) {
8044
8045 break;
8046 }
8047
8048 size = (str != NULL) ? strlen(str) + 1 : 0;
8049 if (ioc.get_size) {
8050 if (ddi_copyout((void *)&size, ioc.buf,
8051 ioc.bufsiz, mode) != 0) {
8052 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8053 hubd->h_log_handle,
8054 "%s: copyout of size failed.", msg);
8055 rv = EIO;
8056
8057 break;
8058 }
8059 } else {
8060 if (size == 0) {
8061 USB_DPRINTF_L3(DPRINT_MASK_CBOPS,
8062 hubd->h_log_handle,
8063 "%s: String is NULL", msg);
8064 rv = EINVAL;
8065
8066 break;
8067 }
8068
8069 if (ioc.bufsiz != size) {
8070 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8071 hubd->h_log_handle,
8072 "%s: string buf size wrong", msg);
8073 rv = EINVAL;
8074
8075 break;
8076 }
8077
8078 if (ddi_copyout((void *)str, ioc.buf,
8079 ioc.bufsiz, mode) != 0) {
8080 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8081 hubd->h_log_handle,
8082 "%s: copyout failed.", msg);
8083 rv = EIO;
8084
8085 break;
8086 }
8087 }
8088 break;
8089 }
8090 case HUBD_GET_CFGADM_NAME:
8091 {
8092 uint32_t name_len;
8093 const char *name;
8094
8095 /* recheck */
8096 if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) ==
8097 NULL) {
8098 rv = EINVAL;
8099
8100 break;
8101 }
8102 name = ddi_node_name(child_dip);
8103 if (name == NULL) {
8104 name = "unsupported";
8105 }
8106 name_len = strlen(name) + 1;
8107
8108 msg = "DEVCTL_AP_CONTROL: HUBD_GET_CFGADM_NAME";
8109 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
8110 "%s: name=%s name_len=%d", msg, name, name_len);
8111
8112 if (ioc.get_size) {
8113 if (ddi_copyout((void *)&name_len,
8114 ioc.buf, ioc.bufsiz, mode) != 0) {
8115 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8116 hubd->h_log_handle,
8117 "%s: copyout of size failed", msg);
8118 rv = EIO;
8119
8120 break;
8121 }
8122 } else {
8123 if (ioc.bufsiz != name_len) {
8124 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8125 hubd->h_log_handle,
8126 "%s: string buf length wrong", msg);
8127 rv = EINVAL;
8128
8129 break;
8130 }
8131
8132 if (ddi_copyout((void *)name, ioc.buf,
8133 ioc.bufsiz, mode) != 0) {
8134 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8135 hubd->h_log_handle,
8136 "%s: copyout failed.", msg);
8137 rv = EIO;
8138
8139 break;
8140 }
8141 }
8142
8143 break;
8144 }
8145
8146 /*
8147 * Return the config index for the currently-configured
8148 * configuration.
8149 */
8150 case HUBD_GET_CURRENT_CONFIG:
8151 {
8152 uint_t config_index;
8153 uint32_t size = sizeof (config_index);
8154 usba_device_t *usba_device;
8155
8156 msg = "DEVCTL_AP_CONTROL: GET_CURRENT_CONFIG";
8157 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
8158 "%s", msg);
8159
8160 /*
8161 * Return the config index for the configuration
8162 * currently in use.
8163 * Recheck if child_dip exists
8164 */
8165 if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) ==
8166 NULL) {
8167 rv = EINVAL;
8168
8169 break;
8170 }
8171
8172 usba_device = usba_get_usba_device(child_dip);
8173 mutex_enter(&usba_device->usb_mutex);
8174 config_index = usba_device->usb_active_cfg_ndx;
8175 mutex_exit(&usba_device->usb_mutex);
8176
8177 if (ioc.get_size) {
8178 if (ddi_copyout((void *)&size,
8179 ioc.buf, ioc.bufsiz, mode) != 0) {
8180 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8181 hubd->h_log_handle,
8182 "%s: copyout of size failed.", msg);
8183 rv = EIO;
8184
8185 break;
8186 }
8187 } else {
8188 if (ioc.bufsiz != size) {
8189 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8190 hubd->h_log_handle,
8191 "%s: buffer size wrong", msg);
8192 rv = EINVAL;
8193
8194 break;
8195 }
8196 if (ddi_copyout((void *)&config_index,
8197 ioc.buf, ioc.bufsiz, mode) != 0) {
8198 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8199 hubd->h_log_handle,
8200 "%s: copyout failed", msg);
8201 rv = EIO;
8202 }
8203 }
8204
8205 break;
8206 }
8207 case HUBD_GET_DEVICE_PATH:
8208 {
8209 char *path;
8210 uint32_t size;
8211
8212 msg = "DEVCTL_AP_CONTROL: GET_DEVICE_PATH";
8213 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
8214 "%s", msg);
8215
8216 /* Recheck if child_dip exists */
8217 if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) ==
8218 NULL) {
8219 rv = EINVAL;
8220
8221 break;
8222 }
8223
8224 /* ddi_pathname doesn't supply /devices, so we do. */
8225 path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
8226 (void) strcpy(path, "/devices");
8227 (void) ddi_pathname(child_dip, path + strlen(path));
8228 size = strlen(path) + 1;
8229
8230 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
8231 "%s: device path=%s size=%d", msg, path, size);
8232
8233 if (ioc.get_size) {
8234 if (ddi_copyout((void *)&size,
8235 ioc.buf, ioc.bufsiz, mode) != 0) {
8236
8237 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8238 hubd->h_log_handle,
8239 "%s: copyout of size failed.", msg);
8240 rv = EIO;
8241 }
8242 } else {
8243 if (ioc.bufsiz != size) {
8244 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8245 hubd->h_log_handle,
8246 "%s: buffer wrong size.", msg);
8247 rv = EINVAL;
8248 } else if (ddi_copyout((void *)path,
8249 ioc.buf, ioc.bufsiz, mode) != 0) {
8250 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8251 hubd->h_log_handle,
8252 "%s: copyout failed.", msg);
8253 rv = EIO;
8254 }
8255 }
8256 kmem_free(path, MAXPATHLEN);
8257
8258 break;
8259 }
8260 case HUBD_REFRESH_DEVDB:
8261 msg = "DEVCTL_AP_CONTROL: HUBD_REFRESH_DEVDB";
8262 USB_DPRINTF_L3(DPRINT_MASK_CBOPS, hubd->h_log_handle,
8263 "%s", msg);
8264
8265 if ((rv = usba_devdb_refresh()) != USB_SUCCESS) {
8266 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8267 hubd->h_log_handle,
8268 "%s: Failed: %d", msg, rv);
8269 rv = EIO;
8270 }
8271
8272 break;
8273 default:
8274 rv = ENOTSUP;
8275 } /* end switch */
8276
8277 break;
8278 }
8279
8280 default:
8281 rv = ENOTTY;
8282 }
8283
8284 if (dcp) {
8285 ndi_dc_freehdl(dcp);
8286 }
8287
8288 /* allow hotplug thread now */
8289 hubd->h_hotplug_thread--;
8290
8291 if ((hubd->h_dev_state == USB_DEV_ONLINE) &&
8292 hubd->h_ep1_ph && (prev_pipe_state == USB_PIPE_STATE_ACTIVE)) {
8293 hubd_start_polling(hubd, 0);
8294 }
8295 mutex_exit(HUBD_MUTEX(hubd));
8296
8297 ndi_devi_exit(hubd->h_dip);
8298 ndi_devi_exit(rh_dip);
8299 ndi_devi_exit(ddi_get_parent(rh_dip));
8300
8301 mutex_enter(HUBD_MUTEX(hubd));
8302 hubd_pm_idle_component(hubd, hubd->h_dip, 0);
8303 mutex_exit(HUBD_MUTEX(hubd));
8304
8305 return (rv);
8306 }
8307
8308
8309 /*
8310 * Helper func used only to help construct the names for the attachment point
8311 * minor nodes. Used only in usba_hubdi_attach.
8312 * Returns whether it found ancestry or not (USB_SUCCESS if yes).
8313 * ports between the root hub and the device represented by dip.
8314 * E.g., "2.4.3.1" means this device is
8315 * plugged into port 1 of a hub that is
8316 * plugged into port 3 of a hub that is
8317 * plugged into port 4 of a hub that is
8318 * plugged into port 2 of the root hub.
8319 * NOTE: Max ap_id path len is HUBD_APID_NAMELEN (32 chars), which is
8320 * more than sufficient (as hubs are a max 6 levels deep, port needs 3
8321 * chars plus NULL each)
8322 */
8323 void
hubd_get_ancestry_str(hubd_t * hubd)8324 hubd_get_ancestry_str(hubd_t *hubd)
8325 {
8326 char ap_name[HUBD_APID_NAMELEN];
8327 dev_info_t *pdip;
8328 hubd_t *phubd;
8329 usb_port_t port;
8330
8331 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
8332 "hubd_get_ancestry_str: hubd=0x%p", (void *)hubd);
8333
8334 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
8335
8336 /*
8337 * The function is extended to support wire adapter class
8338 * devices introduced by WUSB spec. The node name is no
8339 * longer "hub" only.
8340 * Generate the ap_id str based on the parent and child
8341 * relationship instead of retrieving it from the hub
8342 * device path, which simplifies the algorithm.
8343 */
8344 if (usba_is_root_hub(hubd->h_dip)) {
8345 hubd->h_ancestry_str[0] = '\0';
8346 } else {
8347 port = hubd->h_usba_device->usb_port;
8348 mutex_exit(HUBD_MUTEX(hubd));
8349
8350 pdip = ddi_get_parent(hubd->h_dip);
8351 /*
8352 * The parent of wire adapter device might be usb_mid.
8353 * Need to look further up for hub device
8354 */
8355 if (strcmp(ddi_driver_name(pdip), "usb_mid") == 0) {
8356 pdip = ddi_get_parent(pdip);
8357 ASSERT(pdip != NULL);
8358 }
8359
8360 phubd = hubd_get_soft_state(pdip);
8361
8362 mutex_enter(HUBD_MUTEX(phubd));
8363 (void) snprintf(ap_name, HUBD_APID_NAMELEN, "%s%d",
8364 phubd->h_ancestry_str, port);
8365 mutex_exit(HUBD_MUTEX(phubd));
8366
8367 mutex_enter(HUBD_MUTEX(hubd));
8368 (void) strcpy(hubd->h_ancestry_str, ap_name);
8369 (void) strcat(hubd->h_ancestry_str, ".");
8370 }
8371 }
8372
8373
8374 /* Get which port to operate on. */
8375 static usb_port_t
hubd_get_port_num(hubd_t * hubd,struct devctl_iocdata * dcp)8376 hubd_get_port_num(hubd_t *hubd, struct devctl_iocdata *dcp)
8377 {
8378 int32_t port;
8379
8380 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
8381
8382 /* Get which port to operate on. */
8383 if (nvlist_lookup_int32(ndi_dc_get_ap_data(dcp), "port", &port) != 0) {
8384 USB_DPRINTF_L2(DPRINT_MASK_CBOPS, hubd->h_log_handle,
8385 "hubd_get_port_num: port lookup failed");
8386 port = 0;
8387 }
8388
8389 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
8390 "hubd_get_port_num: hubd=0x%p, port=%d", (void *)hubd, port);
8391
8392 return ((usb_port_t)port);
8393 }
8394
8395
8396 /* check if child still exists */
8397 static dev_info_t *
hubd_get_child_dip(hubd_t * hubd,usb_port_t port)8398 hubd_get_child_dip(hubd_t *hubd, usb_port_t port)
8399 {
8400 dev_info_t *child_dip = hubd->h_children_dips[port];
8401
8402 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
8403 "hubd_get_child_dip: hubd=0x%p, port=%d", (void *)hubd, port);
8404
8405 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
8406
8407 return (child_dip);
8408 }
8409
8410
8411 /*
8412 * hubd_cfgadm_state:
8413 *
8414 * child_dip list port_state cfgadm_state
8415 * -------------- ---------- ------------
8416 * != NULL connected configured or
8417 * unconfigured
8418 * != NULL not connected disconnect but
8419 * busy/still referenced
8420 * NULL connected logically disconnected
8421 * NULL not connected empty
8422 */
8423 static uint_t
hubd_cfgadm_state(hubd_t * hubd,usb_port_t port)8424 hubd_cfgadm_state(hubd_t *hubd, usb_port_t port)
8425 {
8426 uint_t state;
8427 dev_info_t *child_dip = hubd_get_child_dip(hubd, port);
8428
8429 if (child_dip) {
8430 if (hubd->h_port_state[port] & PORT_STATUS_CCS) {
8431 /*
8432 * connected, now check if driver exists
8433 */
8434 if (DEVI_IS_DEVICE_OFFLINE(child_dip) ||
8435 !i_ddi_devi_attached(child_dip)) {
8436 state = HUBD_CFGADM_UNCONFIGURED;
8437 } else {
8438 state = HUBD_CFGADM_CONFIGURED;
8439 }
8440 } else {
8441 /*
8442 * this means that the dip is around for
8443 * a device that is still referenced but
8444 * has been yanked out. So the cfgadm info
8445 * for this state should be EMPTY (port empty)
8446 * and CONFIGURED (dip still valid).
8447 */
8448 state = HUBD_CFGADM_STILL_REFERENCED;
8449 }
8450 } else {
8451 /* connected but no child dip */
8452 if (hubd->h_port_state[port] & PORT_STATUS_CCS) {
8453 /* logically disconnected */
8454 state = HUBD_CFGADM_DISCONNECTED;
8455 } else {
8456 /* physically disconnected */
8457 state = HUBD_CFGADM_EMPTY;
8458 }
8459 }
8460
8461 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
8462 "hubd_cfgadm_state: hubd=0x%p, port=%d state=0x%x",
8463 (void *)hubd, port, state);
8464
8465 return (state);
8466 }
8467
8468
8469 /*
8470 * hubd_toggle_port:
8471 */
8472 static int
hubd_toggle_port(hubd_t * hubd,usb_port_t port)8473 hubd_toggle_port(hubd_t *hubd, usb_port_t port)
8474 {
8475 int wait;
8476 uint_t retry;
8477 uint16_t status;
8478 uint16_t change;
8479
8480 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
8481 "hubd_toggle_port: hubd=0x%p, port=%d", (void *)hubd, port);
8482
8483 if ((hubd_disable_port_power(hubd, port)) != USB_SUCCESS) {
8484
8485 return (USB_FAILURE);
8486 }
8487
8488 /*
8489 * see hubd_enable_all_port_power() which
8490 * requires longer delay for hubs.
8491 */
8492 mutex_exit(HUBD_MUTEX(hubd));
8493 delay(drv_usectohz(hubd_device_delay / 10));
8494 mutex_enter(HUBD_MUTEX(hubd));
8495
8496 /*
8497 * According to section 11.11 of USB, for hubs with no power
8498 * switches, bPwrOn2PwrGood is zero. But we wait for some
8499 * arbitrary time to enable power to become stable.
8500 *
8501 * If an hub supports port power swicthing, we need to wait
8502 * at least 20ms before accesing corresonding usb port. Note
8503 * this member is stored in the h_power_good member.
8504 */
8505 if ((hubd->h_hub_chars & HUB_CHARS_NO_POWER_SWITCHING) ||
8506 (hubd->h_power_good == 0)) {
8507 wait = hubd_device_delay / 10;
8508 } else {
8509 wait = max(HUB_DEFAULT_POPG,
8510 hubd->h_power_good) * 2 * 1000;
8511 }
8512
8513 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
8514 "hubd_toggle_port: popg=%d wait=%d",
8515 hubd->h_power_good, wait);
8516
8517 retry = 0;
8518
8519 do {
8520 (void) hubd_enable_port_power(hubd, port);
8521
8522 mutex_exit(HUBD_MUTEX(hubd));
8523 delay(drv_usectohz(wait));
8524 mutex_enter(HUBD_MUTEX(hubd));
8525
8526 /* Get port status */
8527 (void) hubd_determine_port_status(hubd, port,
8528 &status, &change, NULL, 0);
8529
8530 /* For retry if any, use some extra delay */
8531 wait = max(wait, hubd_device_delay / 10);
8532
8533 retry++;
8534
8535 } while ((!(status & PORT_STATUS_PPS)) && (retry < HUBD_PORT_RETRY));
8536
8537 /* Print warning message if port has no power */
8538 if (!(status & PORT_STATUS_PPS)) {
8539
8540 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
8541 "hubd_toggle_port: port %d power-on failed, "
8542 "port status 0x%x", port, status);
8543
8544 return (USB_FAILURE);
8545 }
8546
8547 return (USB_SUCCESS);
8548 }
8549
8550
8551 /*
8552 * hubd_init_power_budget:
8553 * Init power budget variables in hubd structure. According
8554 * to USB spec, the power budget rules are:
8555 * 1. local-powered hubs including root-hubs can supply
8556 * 500mA to each port at maximum
8557 * 2. two bus-powered hubs are not allowed to concatenate
8558 * 3. bus-powered hubs can supply 100mA to each port at
8559 * maximum, and the power consumed by all downstream
8560 * ports and the hub itself cannot exceed the max power
8561 * supplied by the upstream port, i.e., 500mA
8562 * The routine is only called during hub attach time
8563 */
8564 static int
hubd_init_power_budget(hubd_t * hubd)8565 hubd_init_power_budget(hubd_t *hubd)
8566 {
8567 uint16_t status = 0;
8568 usba_device_t *hubd_ud = NULL;
8569 size_t size;
8570 usb_cfg_descr_t cfg_descr;
8571 dev_info_t *pdip = NULL;
8572 hubd_t *phubd = NULL;
8573
8574 if (hubd->h_ignore_pwr_budget) {
8575
8576 return (USB_SUCCESS);
8577 }
8578
8579 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
8580 "hubd_init_power_budget:");
8581
8582 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
8583 ASSERT(hubd->h_default_pipe != 0);
8584 mutex_exit(HUBD_MUTEX(hubd));
8585
8586 /* get device status */
8587 if ((usb_get_status(hubd->h_dip, hubd->h_default_pipe,
8588 HUB_GET_DEVICE_STATUS_TYPE,
8589 0, &status, 0)) != USB_SUCCESS) {
8590 mutex_enter(HUBD_MUTEX(hubd));
8591
8592 return (USB_FAILURE);
8593 }
8594
8595 hubd_ud = usba_get_usba_device(hubd->h_dip);
8596
8597 size = usb_parse_cfg_descr(hubd_ud->usb_cfg, hubd_ud->usb_cfg_length,
8598 &cfg_descr, USB_CFG_DESCR_SIZE);
8599
8600 if (size != USB_CFG_DESCR_SIZE) {
8601 USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
8602 "get hub configuration descriptor failed");
8603 mutex_enter(HUBD_MUTEX(hubd));
8604
8605 return (USB_FAILURE);
8606 }
8607
8608 mutex_enter(HUBD_MUTEX(hubd));
8609
8610 hubd->h_local_pwr_capable = (cfg_descr.bmAttributes &
8611 USB_CFG_ATTR_SELFPWR);
8612
8613 if (hubd->h_local_pwr_capable) {
8614 USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
8615 "hub is capable of local power");
8616 }
8617
8618 hubd->h_local_pwr_on = (status &
8619 USB_DEV_SLF_PWRD_STATUS) && hubd->h_local_pwr_capable;
8620
8621 if (hubd->h_local_pwr_on) {
8622 USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
8623 "hub is local-powered");
8624
8625 hubd->h_pwr_limit = (USB_PWR_UNIT_LOAD *
8626 USB_HIGH_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT;
8627 } else {
8628 hubd->h_pwr_limit = (USB_PWR_UNIT_LOAD *
8629 USB_LOW_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT;
8630
8631 hubd->h_pwr_left = (USB_PWR_UNIT_LOAD *
8632 USB_HIGH_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT;
8633
8634 ASSERT(!usba_is_root_hub(hubd->h_dip));
8635
8636 if (!usba_is_root_hub(hubd->h_dip)) {
8637 /*
8638 * two bus-powered hubs are not
8639 * allowed to be concatenated
8640 */
8641 mutex_exit(HUBD_MUTEX(hubd));
8642
8643 pdip = ddi_get_parent(hubd->h_dip);
8644 phubd = hubd_get_soft_state(pdip);
8645 ASSERT(phubd != NULL);
8646
8647 if (!phubd->h_ignore_pwr_budget) {
8648 mutex_enter(HUBD_MUTEX(phubd));
8649 if (phubd->h_local_pwr_on == B_FALSE) {
8650 USB_DPRINTF_L1(DPRINT_MASK_HUB,
8651 hubd->h_log_handle,
8652 "two bus-powered hubs cannot "
8653 "be concatenated");
8654
8655 mutex_exit(HUBD_MUTEX(phubd));
8656 mutex_enter(HUBD_MUTEX(hubd));
8657
8658 return (USB_FAILURE);
8659 }
8660 mutex_exit(HUBD_MUTEX(phubd));
8661 }
8662
8663 mutex_enter(HUBD_MUTEX(hubd));
8664
8665 USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
8666 "hub is bus-powered");
8667 } else {
8668 USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
8669 "root-hub must be local-powered");
8670 }
8671
8672 /*
8673 * Subtract the power consumed by the hub itself
8674 * and get the power that can be supplied to
8675 * downstream ports
8676 */
8677 hubd->h_pwr_left -= hubd->h_current / USB_CFG_DESCR_PWR_UNIT;
8678 if (hubd->h_pwr_left < 0) {
8679 USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
8680 "hubd->h_pwr_left is less than bHubContrCurrent, "
8681 "should fail");
8682
8683 return (USB_FAILURE);
8684 }
8685 }
8686
8687 return (USB_SUCCESS);
8688 }
8689
8690
8691 /*
8692 * usba_hubdi_check_power_budget:
8693 * Check if the hub has enough power budget to allow a
8694 * child device to select a configuration of config_index.
8695 */
8696 int
usba_hubdi_check_power_budget(dev_info_t * dip,usba_device_t * child_ud,uint_t config_index)8697 usba_hubdi_check_power_budget(dev_info_t *dip, usba_device_t *child_ud,
8698 uint_t config_index)
8699 {
8700 int16_t pwr_left, pwr_limit, pwr_required;
8701 size_t size;
8702 usb_cfg_descr_t cfg_descr;
8703 hubd_t *hubd;
8704
8705 if ((hubd = hubd_get_soft_state(dip)) == NULL) {
8706
8707 return (USB_FAILURE);
8708 }
8709
8710 if (hubd->h_ignore_pwr_budget) {
8711
8712 return (USB_SUCCESS);
8713 }
8714
8715 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8716 "usba_hubdi_check_power_budget: "
8717 "dip=0x%p child_ud=0x%p conf_index=%d", (void *)dip,
8718 (void *)child_ud, config_index);
8719
8720 mutex_enter(HUBD_MUTEX(hubd));
8721 pwr_limit = hubd->h_pwr_limit;
8722 if (hubd->h_local_pwr_on == B_FALSE) {
8723 pwr_left = hubd->h_pwr_left;
8724 pwr_limit = (pwr_limit <= pwr_left) ? pwr_limit : pwr_left;
8725 }
8726 mutex_exit(HUBD_MUTEX(hubd));
8727
8728 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8729 "usba_hubdi_check_power_budget: "
8730 "available power is %dmA", pwr_limit * USB_CFG_DESCR_PWR_UNIT);
8731
8732 size = usb_parse_cfg_descr(
8733 child_ud->usb_cfg_array[config_index], USB_CFG_DESCR_SIZE,
8734 &cfg_descr, USB_CFG_DESCR_SIZE);
8735
8736 if (size != USB_CFG_DESCR_SIZE) {
8737 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8738 "get hub configuration descriptor failed");
8739
8740 return (USB_FAILURE);
8741 }
8742
8743 pwr_required = cfg_descr.bMaxPower;
8744
8745 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8746 "usba_hubdi_check_power_budget: "
8747 "child bmAttributes=0x%x bMaxPower=%d "
8748 "with config_index=%d", cfg_descr.bmAttributes,
8749 pwr_required, config_index);
8750
8751 if (pwr_required > pwr_limit) {
8752 USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8753 "configuration %d for device %s %s at port %d "
8754 "exceeds power available for this port, please "
8755 "re-insert your device into another hub port which "
8756 "has enough power",
8757 config_index,
8758 child_ud->usb_mfg_str,
8759 child_ud->usb_product_str,
8760 child_ud->usb_port);
8761
8762 return (USB_FAILURE);
8763 }
8764
8765 return (USB_SUCCESS);
8766 }
8767
8768
8769 /*
8770 * usba_hubdi_incr_power_budget:
8771 * Increase the hub power budget value when a child device
8772 * is removed from a bus-powered hub port.
8773 */
8774 void
usba_hubdi_incr_power_budget(dev_info_t * dip,usba_device_t * child_ud)8775 usba_hubdi_incr_power_budget(dev_info_t *dip, usba_device_t *child_ud)
8776 {
8777 uint16_t pwr_value;
8778 hubd_t *hubd = hubd_get_soft_state(dip);
8779
8780 ASSERT(hubd != NULL);
8781
8782 if (hubd->h_ignore_pwr_budget) {
8783
8784 return;
8785 }
8786
8787 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
8788 "usba_hubdi_incr_power_budget: "
8789 "dip=0x%p child_ud=0x%p", (void *)dip, (void *)child_ud);
8790
8791 mutex_enter(HUBD_MUTEX(hubd));
8792 if (hubd->h_local_pwr_on == B_TRUE) {
8793 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
8794 "usba_hubdi_incr_power_budget: "
8795 "hub is local powered");
8796 mutex_exit(HUBD_MUTEX(hubd));
8797
8798 return;
8799 }
8800 mutex_exit(HUBD_MUTEX(hubd));
8801
8802 mutex_enter(&child_ud->usb_mutex);
8803 if (child_ud->usb_pwr_from_hub == 0) {
8804 mutex_exit(&child_ud->usb_mutex);
8805
8806 return;
8807 }
8808 pwr_value = child_ud->usb_pwr_from_hub;
8809 mutex_exit(&child_ud->usb_mutex);
8810
8811 mutex_enter(HUBD_MUTEX(hubd));
8812 hubd->h_pwr_left += pwr_value;
8813
8814 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
8815 "usba_hubdi_incr_power_budget: "
8816 "available power is %dmA, increased by %dmA",
8817 hubd->h_pwr_left * USB_CFG_DESCR_PWR_UNIT,
8818 pwr_value * USB_CFG_DESCR_PWR_UNIT);
8819
8820 mutex_exit(HUBD_MUTEX(hubd));
8821
8822 mutex_enter(&child_ud->usb_mutex);
8823 child_ud->usb_pwr_from_hub = 0;
8824 mutex_exit(&child_ud->usb_mutex);
8825 }
8826
8827
8828 /*
8829 * usba_hubdi_decr_power_budget:
8830 * Decrease the hub power budget value when a child device
8831 * is inserted to a bus-powered hub port.
8832 */
8833 void
usba_hubdi_decr_power_budget(dev_info_t * dip,usba_device_t * child_ud)8834 usba_hubdi_decr_power_budget(dev_info_t *dip, usba_device_t *child_ud)
8835 {
8836 uint16_t pwr_value;
8837 size_t size;
8838 usb_cfg_descr_t cfg_descr;
8839 hubd_t *hubd = hubd_get_soft_state(dip);
8840
8841 ASSERT(hubd != NULL);
8842
8843 if (hubd->h_ignore_pwr_budget) {
8844
8845 return;
8846 }
8847
8848 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
8849 "usba_hubdi_decr_power_budget: "
8850 "dip=0x%p child_ud=0x%p", (void *)dip, (void *)child_ud);
8851
8852 mutex_enter(HUBD_MUTEX(hubd));
8853 if (hubd->h_local_pwr_on == B_TRUE) {
8854 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
8855 "usba_hubdi_decr_power_budget: "
8856 "hub is local powered");
8857 mutex_exit(HUBD_MUTEX(hubd));
8858
8859 return;
8860 }
8861 mutex_exit(HUBD_MUTEX(hubd));
8862
8863 mutex_enter(&child_ud->usb_mutex);
8864 if (child_ud->usb_pwr_from_hub > 0) {
8865 mutex_exit(&child_ud->usb_mutex);
8866
8867 return;
8868 }
8869 mutex_exit(&child_ud->usb_mutex);
8870
8871 size = usb_parse_cfg_descr(
8872 child_ud->usb_cfg, child_ud->usb_cfg_length,
8873 &cfg_descr, USB_CFG_DESCR_SIZE);
8874 ASSERT(size == USB_CFG_DESCR_SIZE);
8875
8876 mutex_enter(HUBD_MUTEX(hubd));
8877 pwr_value = cfg_descr.bMaxPower;
8878 hubd->h_pwr_left -= pwr_value;
8879 ASSERT(hubd->h_pwr_left >= 0);
8880
8881 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
8882 "usba_hubdi_decr_power_budget: "
8883 "available power is %dmA, decreased by %dmA",
8884 hubd->h_pwr_left * USB_CFG_DESCR_PWR_UNIT,
8885 pwr_value * USB_CFG_DESCR_PWR_UNIT);
8886
8887 mutex_exit(HUBD_MUTEX(hubd));
8888
8889 mutex_enter(&child_ud->usb_mutex);
8890 child_ud->usb_pwr_from_hub = pwr_value;
8891 mutex_exit(&child_ud->usb_mutex);
8892 }
8893
8894 /*
8895 * hubd_wait_for_hotplug_exit:
8896 * Waiting for the exit of the running hotplug thread or ioctl thread.
8897 */
8898 static int
hubd_wait_for_hotplug_exit(hubd_t * hubd)8899 hubd_wait_for_hotplug_exit(hubd_t *hubd)
8900 {
8901 clock_t until = drv_usectohz(1000000);
8902 int rval;
8903
8904 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
8905
8906 if (hubd->h_hotplug_thread) {
8907 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8908 "waiting for hubd hotplug thread exit");
8909 rval = cv_reltimedwait(&hubd->h_cv_hotplug_dev,
8910 &hubd->h_mutex, until, TR_CLOCK_TICK);
8911
8912 if ((rval <= 0) && (hubd->h_hotplug_thread)) {
8913
8914 return (USB_FAILURE);
8915 }
8916 }
8917
8918 return (USB_SUCCESS);
8919 }
8920
8921 /*
8922 * hubd_reset_thread:
8923 * handles the "USB_RESET_LVL_REATTACH" reset of usb device.
8924 *
8925 * - delete the child (force detaching the device and its children)
8926 * - reset the corresponding parent hub port
8927 * - create the child (force re-attaching the device and its children)
8928 */
8929 static void
hubd_reset_thread(void * arg)8930 hubd_reset_thread(void *arg)
8931 {
8932 hubd_reset_arg_t *hd_arg = (hubd_reset_arg_t *)arg;
8933 hubd_t *hubd = hd_arg->hubd;
8934 uint16_t reset_port = hd_arg->reset_port;
8935 uint16_t status, change;
8936 hub_power_t *hubpm;
8937 dev_info_t *hdip = hubd->h_dip;
8938 dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip;
8939 dev_info_t *child_dip;
8940 boolean_t online_child = B_FALSE;
8941 int devinst;
8942 char *devname;
8943 int i = 0;
8944 int rval = USB_FAILURE;
8945
8946 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8947 "hubd_reset_thread: started, hubd_reset_port = 0x%x", reset_port);
8948
8949 kmem_free(arg, sizeof (hubd_reset_arg_t));
8950
8951 mutex_enter(HUBD_MUTEX(hubd));
8952
8953 child_dip = hubd->h_children_dips[reset_port];
8954 ASSERT(child_dip != NULL);
8955
8956 devname = (char *)ddi_driver_name(child_dip);
8957 devinst = ddi_get_instance(child_dip);
8958
8959 /* if our bus power entry point is active, quit the reset */
8960 if (hubd->h_bus_pwr) {
8961 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8962 "%s%d is under bus power management, cannot be reset. "
8963 "Please disconnect and reconnect this device.",
8964 devname, devinst);
8965
8966 goto Fail;
8967 }
8968
8969 if (hubd_wait_for_hotplug_exit(hubd) == USB_FAILURE) {
8970 /* we got woken up because of a timeout */
8971 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
8972 hubd->h_log_handle, "Time out when resetting the device"
8973 " %s%d. Please disconnect and reconnect this device.",
8974 devname, devinst);
8975
8976 goto Fail;
8977 }
8978
8979 hubd->h_hotplug_thread++;
8980
8981 /* is this the root hub? */
8982 if ((hdip == rh_dip) &&
8983 (hubd->h_dev_state == USB_DEV_PWRED_DOWN)) {
8984 hubpm = hubd->h_hubpm;
8985
8986 /* mark the root hub as full power */
8987 hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
8988 hubpm->hubp_time_at_full_power = gethrtime();
8989 mutex_exit(HUBD_MUTEX(hubd));
8990
8991 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8992 "hubd_reset_thread: call pm_power_has_changed");
8993
8994 (void) pm_power_has_changed(hdip, 0,
8995 USB_DEV_OS_FULL_PWR);
8996
8997 mutex_enter(HUBD_MUTEX(hubd));
8998 hubd->h_dev_state = USB_DEV_ONLINE;
8999 }
9000
9001 mutex_exit(HUBD_MUTEX(hubd));
9002
9003 /*
9004 * this ensures one reset activity per system at a time.
9005 * we enter the parent PCI node to have this serialization.
9006 * this also excludes ioctls and deathrow thread
9007 */
9008 ndi_devi_enter(ddi_get_parent(rh_dip));
9009 ndi_devi_enter(rh_dip);
9010
9011 /* exclude other threads */
9012 ndi_devi_enter(hdip);
9013 mutex_enter(HUBD_MUTEX(hubd));
9014
9015 /*
9016 * We need to make sure that the child is still online for a hotplug
9017 * thread could have inserted which detached the child.
9018 */
9019 if (hubd->h_children_dips[reset_port]) {
9020 mutex_exit(HUBD_MUTEX(hubd));
9021 /* First disconnect the device */
9022 hubd_post_event(hubd, reset_port, USBA_EVENT_TAG_HOT_REMOVAL);
9023
9024 /* delete cached dv_node's but drop locks first */
9025 ndi_devi_exit(hdip);
9026 ndi_devi_exit(rh_dip);
9027 ndi_devi_exit(ddi_get_parent(rh_dip));
9028
9029 (void) devfs_clean(rh_dip, NULL, DV_CLEAN_FORCE);
9030
9031 /*
9032 * workaround only for storage device. When it's able to force
9033 * detach a driver, this code can be removed safely.
9034 *
9035 * If we're to reset storage device and the device is used, we
9036 * will wait at most extra 20s for applications to exit and
9037 * close the device. This is especially useful for HAL-based
9038 * applications.
9039 */
9040 if ((strcmp(devname, "scsa2usb") == 0) &&
9041 DEVI(child_dip)->devi_ref != 0) {
9042 while (i++ < hubdi_reset_delay) {
9043 mutex_enter(HUBD_MUTEX(hubd));
9044 rval = hubd_delete_child(hubd, reset_port,
9045 NDI_DEVI_REMOVE, B_FALSE);
9046 mutex_exit(HUBD_MUTEX(hubd));
9047 if (rval == USB_SUCCESS)
9048 break;
9049
9050 delay(drv_usectohz(1000000)); /* 1s */
9051 }
9052 }
9053
9054 ndi_devi_enter(ddi_get_parent(rh_dip));
9055 ndi_devi_enter(rh_dip);
9056 ndi_devi_enter(hdip);
9057
9058 mutex_enter(HUBD_MUTEX(hubd));
9059
9060 /* Then force detaching the device */
9061 if ((rval != USB_SUCCESS) && (hubd_delete_child(hubd,
9062 reset_port, NDI_DEVI_REMOVE, B_FALSE) != USB_SUCCESS)) {
9063 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
9064 "%s%d cannot be reset due to other applications "
9065 "are using it, please first close these "
9066 "applications, then disconnect and reconnect"
9067 "the device.", devname, devinst);
9068
9069 mutex_exit(HUBD_MUTEX(hubd));
9070 /* post a re-connect event */
9071 hubd_post_event(hubd, reset_port,
9072 USBA_EVENT_TAG_HOT_INSERTION);
9073 mutex_enter(HUBD_MUTEX(hubd));
9074 } else {
9075 (void) hubd_determine_port_status(hubd, reset_port,
9076 &status, &change, NULL, HUBD_ACK_ALL_CHANGES);
9077
9078 /* Reset the parent hubd port and create new child */
9079 if (status & PORT_STATUS_CCS) {
9080 online_child |= (hubd_handle_port_connect(hubd,
9081 reset_port) == USB_SUCCESS);
9082 }
9083 }
9084 }
9085
9086 /* release locks so we can do a devfs_clean */
9087 mutex_exit(HUBD_MUTEX(hubd));
9088
9089 /* delete cached dv_node's but drop locks first */
9090 ndi_devi_exit(hdip);
9091 ndi_devi_exit(rh_dip);
9092 ndi_devi_exit(ddi_get_parent(rh_dip));
9093
9094 (void) devfs_clean(rh_dip, NULL, 0);
9095
9096 /* now check if any children need onlining */
9097 if (online_child) {
9098 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
9099 "hubd_reset_thread: onlining children");
9100
9101 (void) ndi_devi_online(hubd->h_dip, 0);
9102 }
9103
9104 mutex_enter(HUBD_MUTEX(hubd));
9105
9106 /* allow hotplug thread now */
9107 hubd->h_hotplug_thread--;
9108 Fail:
9109 hubd_start_polling(hubd, 0);
9110
9111 /* mark this device as idle */
9112 (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
9113
9114 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
9115 "hubd_reset_thread: exit, %d", hubd->h_hotplug_thread);
9116
9117 hubd->h_reset_port[reset_port] = B_FALSE;
9118
9119 mutex_exit(HUBD_MUTEX(hubd));
9120
9121 ndi_rele_devi(hdip);
9122 }
9123
9124 /*
9125 * hubd_check_same_device:
9126 * - open the default pipe of the device.
9127 * - compare the old and new descriptors of the device.
9128 * - close the default pipe.
9129 */
9130 static int
hubd_check_same_device(hubd_t * hubd,usb_port_t port)9131 hubd_check_same_device(hubd_t *hubd, usb_port_t port)
9132 {
9133 dev_info_t *dip = hubd->h_children_dips[port];
9134 usb_pipe_handle_t ph;
9135 int rval = USB_FAILURE;
9136
9137 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
9138
9139 mutex_exit(HUBD_MUTEX(hubd));
9140 /* Open the default pipe to operate the device */
9141 if (usb_pipe_open(dip, NULL, NULL,
9142 USB_FLAGS_SLEEP| USBA_FLAGS_PRIVILEGED,
9143 &ph) == USB_SUCCESS) {
9144 /*
9145 * Check that if the device's descriptors are different
9146 * from the values saved before the port reset.
9147 */
9148 rval = usb_check_same_device(dip,
9149 hubd->h_log_handle, USB_LOG_L0,
9150 DPRINT_MASK_ALL, USB_CHK_ALL, NULL);
9151
9152 usb_pipe_close(dip, ph, USB_FLAGS_SLEEP |
9153 USBA_FLAGS_PRIVILEGED, NULL, NULL);
9154 }
9155 mutex_enter(HUBD_MUTEX(hubd));
9156
9157 return (rval);
9158 }
9159
9160 /*
9161 * usba_hubdi_reset_device
9162 * Called by usb_reset_device to handle usb device reset.
9163 */
9164 int
usba_hubdi_reset_device(dev_info_t * dip,usb_dev_reset_lvl_t reset_level)9165 usba_hubdi_reset_device(dev_info_t *dip, usb_dev_reset_lvl_t reset_level)
9166 {
9167 hubd_t *hubd;
9168 usb_port_t port = 0;
9169 dev_info_t *hdip;
9170 usb_pipe_state_t prev_pipe_state = 0;
9171 usba_device_t *usba_device = NULL;
9172 hubd_reset_arg_t *arg;
9173 int i, ph_open_cnt;
9174 int rval = USB_FAILURE;
9175
9176 if ((!dip) || usba_is_root_hub(dip)) {
9177 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
9178 "usba_hubdi_reset_device: NULL dip or root hub");
9179
9180 return (USB_INVALID_ARGS);
9181 }
9182
9183 if (!usb_owns_device(dip)) {
9184 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
9185 "usba_hubdi_reset_device: Not owns the device");
9186
9187 return (USB_INVALID_PERM);
9188 }
9189
9190 if ((reset_level != USB_RESET_LVL_REATTACH) &&
9191 (reset_level != USB_RESET_LVL_DEFAULT)) {
9192 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
9193 "usba_hubdi_reset_device: Unknown flags");
9194
9195 return (USB_INVALID_ARGS);
9196 }
9197
9198 if ((hdip = ddi_get_parent(dip)) == NULL) {
9199 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
9200 "usba_hubdi_reset_device: fail to get parent hub");
9201
9202 return (USB_INVALID_ARGS);
9203 }
9204
9205 if ((hubd = hubd_get_soft_state(hdip)) == NULL) {
9206 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
9207 "usba_hubdi_reset_device: fail to get hub softstate");
9208
9209 return (USB_INVALID_ARGS);
9210 }
9211
9212 mutex_enter(HUBD_MUTEX(hubd));
9213
9214 /* make sure the hub is connected before trying any kinds of reset. */
9215 if ((hubd->h_dev_state == USB_DEV_DISCONNECTED) ||
9216 (hubd->h_dev_state == USB_DEV_SUSPENDED)) {
9217 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
9218 "usb_reset_device: the state %d of the hub/roothub "
9219 "associated to the device 0x%p is incorrect",
9220 hubd->h_dev_state, (void *)dip);
9221 mutex_exit(HUBD_MUTEX(hubd));
9222
9223 return (USB_INVALID_ARGS);
9224 }
9225
9226 mutex_exit(HUBD_MUTEX(hubd));
9227
9228 port = hubd_child_dip2port(hubd, dip);
9229
9230 mutex_enter(HUBD_MUTEX(hubd));
9231
9232 if (hubd->h_reset_port[port]) {
9233 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
9234 "usb_reset_device: the corresponding port is resetting");
9235 mutex_exit(HUBD_MUTEX(hubd));
9236
9237 return (USB_SUCCESS);
9238 }
9239
9240 /*
9241 * For Default reset, client drivers should first close all the pipes
9242 * except default pipe before calling the function, also should not
9243 * call the function during interrupt context.
9244 */
9245 if (reset_level == USB_RESET_LVL_DEFAULT) {
9246 usba_device = hubd->h_usba_devices[port];
9247 mutex_exit(HUBD_MUTEX(hubd));
9248
9249 if (servicing_interrupt()) {
9250 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
9251 "usb_reset_device: during interrput context, quit");
9252
9253 return (USB_INVALID_CONTEXT);
9254 }
9255 /* Check if all the pipes have been closed */
9256 for (ph_open_cnt = 0, i = 1; i < USBA_N_ENDPOINTS; i++) {
9257 if (usba_device->usb_ph_list[i].usba_ph_data) {
9258 ph_open_cnt++;
9259 break;
9260 }
9261 }
9262 if (ph_open_cnt) {
9263 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
9264 "usb_reset_device: %d pipes are still open",
9265 ph_open_cnt);
9266
9267 return (USB_BUSY);
9268 }
9269 mutex_enter(HUBD_MUTEX(hubd));
9270 }
9271
9272 /* Don't perform reset while the device is detaching */
9273 if (hubd->h_port_state[port] & HUBD_CHILD_DETACHING) {
9274 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
9275 "usb_reset_device: the device is detaching, "
9276 "cannot be reset");
9277 mutex_exit(HUBD_MUTEX(hubd));
9278
9279 return (USB_FAILURE);
9280 }
9281
9282 hubd->h_reset_port[port] = B_TRUE;
9283 hdip = hubd->h_dip;
9284 mutex_exit(HUBD_MUTEX(hubd));
9285
9286 /* Don't allow hub detached during the reset */
9287 ndi_hold_devi(hdip);
9288
9289 mutex_enter(HUBD_MUTEX(hubd));
9290 hubd_pm_busy_component(hubd, hdip, 0);
9291 mutex_exit(HUBD_MUTEX(hubd));
9292 /* go full power */
9293 (void) pm_raise_power(hdip, 0, USB_DEV_OS_FULL_PWR);
9294 mutex_enter(HUBD_MUTEX(hubd));
9295
9296 hubd->h_hotplug_thread++;
9297
9298 /* stop polling if it was active */
9299 if (hubd->h_ep1_ph) {
9300 mutex_exit(HUBD_MUTEX(hubd));
9301 (void) usb_pipe_get_state(hubd->h_ep1_ph, &prev_pipe_state,
9302 USB_FLAGS_SLEEP);
9303 mutex_enter(HUBD_MUTEX(hubd));
9304
9305 if (prev_pipe_state == USB_PIPE_STATE_ACTIVE) {
9306 hubd_stop_polling(hubd);
9307 }
9308 }
9309
9310 switch (reset_level) {
9311 case USB_RESET_LVL_REATTACH:
9312 mutex_exit(HUBD_MUTEX(hubd));
9313 arg = (hubd_reset_arg_t *)kmem_zalloc(
9314 sizeof (hubd_reset_arg_t), KM_SLEEP);
9315 arg->hubd = hubd;
9316 arg->reset_port = port;
9317 mutex_enter(HUBD_MUTEX(hubd));
9318
9319 if ((rval = usb_async_req(hdip, hubd_reset_thread,
9320 (void *)arg, 0)) == USB_SUCCESS) {
9321 hubd->h_hotplug_thread--;
9322 mutex_exit(HUBD_MUTEX(hubd));
9323
9324 return (USB_SUCCESS);
9325 } else {
9326 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
9327 "Cannot create reset thread, the device %s%d failed"
9328 " to reset", ddi_driver_name(dip),
9329 ddi_get_instance(dip));
9330
9331 kmem_free(arg, sizeof (hubd_reset_arg_t));
9332 }
9333
9334 break;
9335 case USB_RESET_LVL_DEFAULT:
9336 /*
9337 * Reset hub port and then recover device's address, set back
9338 * device's configuration, hubd_handle_port_connect() will
9339 * handle errors happened during this process.
9340 */
9341 if ((rval = hubd_handle_port_connect(hubd, port))
9342 == USB_SUCCESS) {
9343 mutex_exit(HUBD_MUTEX(hubd));
9344 /* re-open the default pipe */
9345 ASSERT3P(usba_device, !=, NULL);
9346 rval = usba_persistent_pipe_open(usba_device);
9347 mutex_enter(HUBD_MUTEX(hubd));
9348 if (rval != USB_SUCCESS) {
9349 USB_DPRINTF_L2(DPRINT_MASK_ATTA,
9350 hubd->h_log_handle, "failed to reopen "
9351 "default pipe after reset, disable hub"
9352 "port for %s%d", ddi_driver_name(dip),
9353 ddi_get_instance(dip));
9354 /*
9355 * Disable port to set out a hotplug thread
9356 * which will handle errors.
9357 */
9358 (void) hubd_disable_port(hubd, port);
9359 }
9360 }
9361
9362 break;
9363 default:
9364
9365 break;
9366 }
9367
9368 /* allow hotplug thread now */
9369 hubd->h_hotplug_thread--;
9370
9371 if ((hubd->h_dev_state == USB_DEV_ONLINE) && hubd->h_ep1_ph &&
9372 (prev_pipe_state == USB_PIPE_STATE_ACTIVE)) {
9373 hubd_start_polling(hubd, 0);
9374 }
9375
9376 hubd_pm_idle_component(hubd, hdip, 0);
9377
9378 /* Clear reset mark for the port. */
9379 hubd->h_reset_port[port] = B_FALSE;
9380
9381 mutex_exit(HUBD_MUTEX(hubd));
9382
9383 ndi_rele_devi(hdip);
9384
9385 return (rval);
9386 }
9387