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 hwahc_major = ddi_name_to_major("hwahc");
2349 major_t usbmid_major = ddi_name_to_major("usb_mid");
2350
2351 /*
2352 * make sure dip is a usb hub, major of root hub is HCD
2353 * major
2354 */
2355 if (!usba_is_root_hub(dip)) {
2356 if (ddi_driver_major(dip) == usbmid_major) {
2357 /*
2358 * need to walk the children since it might be a
2359 * HWA device
2360 */
2361
2362 return (DDI_WALK_CONTINUE);
2363 }
2364
2365 /* TODO: DWA device may also need special handling */
2366
2367 if (((ddi_driver_major(dip) != hub_major) &&
2368 (ddi_driver_major(dip) != hwahc_major)) ||
2369 !i_ddi_devi_attached(dip)) {
2370
2371 return (DDI_WALK_PRUNECHILD);
2372 }
2373 }
2374
2375 hubd = hubd_get_soft_state(dip);
2376 if (hubd == NULL) {
2377
2378 return (DDI_WALK_PRUNECHILD);
2379 }
2380
2381 /* walk child list and remove nodes with flag DEVI_DEVICE_REMOVED */
2382 ndi_devi_enter(dip);
2383
2384 if (ddi_driver_major(dip) != hwahc_major) {
2385 /* for normal usb hub or root hub */
2386 mutex_enter(HUBD_MUTEX(hubd));
2387 for (port = 1; port <= hubd->h_nports; port++) {
2388 dev_info_t *cdip = hubd->h_children_dips[port];
2389
2390 if (cdip == NULL || DEVI_IS_DEVICE_REMOVED(cdip) == 0) {
2391
2392 continue;
2393 }
2394
2395 (void) hubd_delete_child(hubd, port, NDI_DEVI_REMOVE,
2396 B_TRUE);
2397 }
2398 mutex_exit(HUBD_MUTEX(hubd));
2399 } else {
2400 /* for HWA */
2401 if (hubd->h_cleanup_child != NULL) {
2402 if (hubd->h_cleanup_child(dip) != USB_SUCCESS) {
2403 ndi_devi_exit(dip);
2404
2405 return (DDI_WALK_PRUNECHILD);
2406 }
2407 } else {
2408 ndi_devi_exit(dip);
2409
2410 return (DDI_WALK_PRUNECHILD);
2411 }
2412 }
2413
2414 ndi_devi_exit(dip);
2415
2416 /* skip siblings of root hub */
2417 if (usba_is_root_hub(dip)) {
2418
2419 return (DDI_WALK_PRUNESIB);
2420 }
2421
2422 return (DDI_WALK_CONTINUE);
2423 }
2424
2425
2426 /*
2427 * this thread will walk all children under the root hub for this
2428 * USB bus instance and attempt to remove them
2429 */
2430 static void
hubd_root_hub_cleanup_thread(void * arg)2431 hubd_root_hub_cleanup_thread(void *arg)
2432 {
2433 hubd_t *root_hubd = (hubd_t *)arg;
2434 dev_info_t *rh_dip = root_hubd->h_dip;
2435 #ifndef __lock_lint
2436 callb_cpr_t cprinfo;
2437
2438 CALLB_CPR_INIT(&cprinfo, HUBD_MUTEX(root_hubd), callb_generic_cpr,
2439 "USB root hub");
2440 #endif
2441
2442 for (;;) {
2443 /* don't race with detach */
2444 ndi_hold_devi(rh_dip);
2445
2446 mutex_enter(HUBD_MUTEX(root_hubd));
2447 root_hubd->h_cleanup_needed = 0;
2448 mutex_exit(HUBD_MUTEX(root_hubd));
2449
2450 (void) devfs_clean(rh_dip, NULL, 0);
2451
2452 ndi_devi_enter(ddi_get_parent(rh_dip));
2453 ddi_walk_devs(rh_dip, hubd_check_disconnected_ports,
2454 NULL);
2455 #ifdef __lock_lint
2456 (void) hubd_check_disconnected_ports(rh_dip, NULL);
2457 #endif
2458 ndi_devi_exit(ddi_get_parent(rh_dip));
2459
2460 /* quit if we are not enabled anymore */
2461 mutex_enter(HUBD_MUTEX(root_hubd));
2462 if ((root_hubd->h_cleanup_enabled == B_FALSE) ||
2463 (root_hubd->h_cleanup_needed == B_FALSE)) {
2464 root_hubd->h_cleanup_active = B_FALSE;
2465 mutex_exit(HUBD_MUTEX(root_hubd));
2466 ndi_rele_devi(rh_dip);
2467
2468 break;
2469 }
2470 mutex_exit(HUBD_MUTEX(root_hubd));
2471 ndi_rele_devi(rh_dip);
2472
2473 #ifndef __lock_lint
2474 mutex_enter(HUBD_MUTEX(root_hubd));
2475 CALLB_CPR_SAFE_BEGIN(&cprinfo);
2476 mutex_exit(HUBD_MUTEX(root_hubd));
2477
2478 delay(drv_usectohz(hubd_dip_cleanup_delay));
2479
2480 mutex_enter(HUBD_MUTEX(root_hubd));
2481 CALLB_CPR_SAFE_END(&cprinfo, HUBD_MUTEX(root_hubd));
2482 mutex_exit(HUBD_MUTEX(root_hubd));
2483 #endif
2484 }
2485
2486 #ifndef __lock_lint
2487 mutex_enter(HUBD_MUTEX(root_hubd));
2488 CALLB_CPR_EXIT(&cprinfo);
2489 #endif
2490 }
2491
2492
2493 void
hubd_schedule_cleanup(dev_info_t * rh_dip)2494 hubd_schedule_cleanup(dev_info_t *rh_dip)
2495 {
2496 hubd_t *root_hubd;
2497
2498 /*
2499 * The usb_root_hub_dip pointer for the child hub of the WUSB
2500 * wire adapter class device points to the wire adapter, not
2501 * the root hub. Need to find the real root hub dip so that
2502 * the cleanup thread only starts from the root hub.
2503 */
2504 while (!usba_is_root_hub(rh_dip)) {
2505 root_hubd = hubd_get_soft_state(rh_dip);
2506 if (root_hubd != NULL) {
2507 rh_dip = root_hubd->h_usba_device->usb_root_hub_dip;
2508 if (rh_dip == NULL) {
2509 USB_DPRINTF_L2(DPRINT_MASK_ATTA,
2510 root_hubd->h_log_handle,
2511 "hubd_schedule_cleanup: null rh dip");
2512
2513 return;
2514 }
2515 } else {
2516 USB_DPRINTF_L2(DPRINT_MASK_ATTA,
2517 root_hubd->h_log_handle,
2518 "hubd_schedule_cleanup: cannot find root hub");
2519
2520 return;
2521 }
2522 }
2523 root_hubd = hubd_get_soft_state(rh_dip);
2524
2525 mutex_enter(HUBD_MUTEX(root_hubd));
2526 root_hubd->h_cleanup_needed = B_TRUE;
2527 if (root_hubd->h_cleanup_enabled && !(root_hubd->h_cleanup_active)) {
2528 root_hubd->h_cleanup_active = B_TRUE;
2529 mutex_exit(HUBD_MUTEX(root_hubd));
2530 (void) thread_create(NULL, 0,
2531 hubd_root_hub_cleanup_thread,
2532 (void *)root_hubd, 0, &p0, TS_RUN,
2533 minclsyspri);
2534 } else {
2535 mutex_exit(HUBD_MUTEX(root_hubd));
2536 }
2537 }
2538
2539
2540 /*
2541 * hubd_restore_device_state:
2542 * - set config for the hub
2543 * - power cycle all the ports
2544 * - for each port that was connected
2545 * - reset port
2546 * - assign addrs to the device on this port
2547 * - restart polling
2548 * - reset suspend flag
2549 */
2550 static void
hubd_restore_device_state(dev_info_t * dip,hubd_t * hubd)2551 hubd_restore_device_state(dev_info_t *dip, hubd_t *hubd)
2552 {
2553 int rval;
2554 int retry;
2555 uint_t hub_prev_state;
2556 usb_port_t port;
2557 uint16_t status;
2558 uint16_t change;
2559 dev_info_t *ch_dip;
2560 boolean_t ehci_root_hub;
2561
2562 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
2563 "hubd_restore_device_state:");
2564
2565 mutex_enter(HUBD_MUTEX(hubd));
2566 hub_prev_state = hubd->h_dev_state;
2567 ASSERT(hub_prev_state != USB_DEV_PWRED_DOWN);
2568
2569 /* First bring the device to full power */
2570 (void) hubd_pm_busy_component(hubd, dip, 0);
2571 mutex_exit(HUBD_MUTEX(hubd));
2572
2573 (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
2574
2575 if (!usba_is_root_hub(dip) &&
2576 (usb_check_same_device(dip, hubd->h_log_handle, USB_LOG_L0,
2577 DPRINT_MASK_HOTPLUG,
2578 USB_CHK_BASIC|USB_CHK_CFG, NULL) != USB_SUCCESS)) {
2579
2580 /* change the device state to disconnected */
2581 mutex_enter(HUBD_MUTEX(hubd));
2582 hubd->h_dev_state = USB_DEV_DISCONNECTED;
2583 (void) hubd_pm_idle_component(hubd, dip, 0);
2584 mutex_exit(HUBD_MUTEX(hubd));
2585
2586 return;
2587 }
2588
2589 ehci_root_hub = (strcmp(ddi_driver_name(dip), "ehci") == 0);
2590
2591 mutex_enter(HUBD_MUTEX(hubd));
2592 /* First turn off all port power */
2593 rval = hubd_disable_all_port_power(hubd);
2594 if (rval != USB_SUCCESS) {
2595 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
2596 "hubd_restore_device_state:"
2597 "turning off port power failed");
2598 }
2599
2600 /* Settling time before turning on again */
2601 mutex_exit(HUBD_MUTEX(hubd));
2602 delay(drv_usectohz(hubd_device_delay / 100));
2603 mutex_enter(HUBD_MUTEX(hubd));
2604
2605 /* enable power on all ports so we can see connects */
2606 if (hubd_enable_all_port_power(hubd) != USB_SUCCESS) {
2607 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
2608 "hubd_restore_device_state: turn on port power failed");
2609
2610 /* disable whatever was enabled */
2611 (void) hubd_disable_all_port_power(hubd);
2612
2613 (void) hubd_pm_idle_component(hubd, dip, 0);
2614 mutex_exit(HUBD_MUTEX(hubd));
2615
2616 return;
2617 }
2618
2619 /*
2620 * wait at least 3 frames before accessing devices
2621 * (note that delay's minimal time is one clock tick).
2622 */
2623 mutex_exit(HUBD_MUTEX(hubd));
2624 delay(drv_usectohz(10000));
2625 mutex_enter(HUBD_MUTEX(hubd));
2626
2627 hubd->h_dev_state = USB_DEV_HUB_STATE_RECOVER;
2628
2629 for (port = 1; port <= hubd->h_nports; port++) {
2630 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
2631 "hubd_restore_device_state: port=%d", port);
2632
2633 /*
2634 * the childen_dips list may have dips that have been
2635 * already deallocated. we only get a post_detach notification
2636 * but not a destroy notification
2637 */
2638 ch_dip = hubd->h_children_dips[port];
2639 if (ch_dip) {
2640 /* get port status */
2641 (void) hubd_determine_port_status(hubd, port,
2642 &status, &change, NULL, PORT_CHANGE_CSC);
2643
2644 /* check if it is truly connected */
2645 if (status & PORT_STATUS_CCS) {
2646 /*
2647 * Now reset port and assign the device
2648 * its original address
2649 */
2650 retry = 0;
2651 do {
2652 (void) hubd_reset_port(hubd, port);
2653
2654 /* required for ppx */
2655 (void) hubd_enable_port(hubd, port);
2656
2657 if (retry) {
2658 mutex_exit(HUBD_MUTEX(hubd));
2659 delay(drv_usectohz(
2660 hubd_device_delay/2));
2661 mutex_enter(HUBD_MUTEX(hubd));
2662 }
2663
2664 rval = hubd_setdevaddr(hubd, port);
2665 retry++;
2666 } while ((rval != USB_SUCCESS) &&
2667 (retry < hubd_retry_enumerate));
2668
2669 hubd_setdevconfig(hubd, port);
2670
2671 if (hub_prev_state == USB_DEV_DISCONNECTED) {
2672 /* post a connect event */
2673 mutex_exit(HUBD_MUTEX(hubd));
2674 hubd_post_event(hubd, port,
2675 USBA_EVENT_TAG_HOT_INSERTION);
2676 mutex_enter(HUBD_MUTEX(hubd));
2677 } else {
2678 /*
2679 * Since we have this device connected
2680 * mark it reinserted to prevent
2681 * cleanup thread from stepping in.
2682 */
2683 mutex_exit(HUBD_MUTEX(hubd));
2684 mutex_enter(&(DEVI(ch_dip)->devi_lock));
2685 DEVI_SET_DEVICE_REINSERTED(ch_dip);
2686 mutex_exit(&(DEVI(ch_dip)->devi_lock));
2687
2688 /*
2689 * reopen pipes for children for
2690 * their DDI_RESUME
2691 */
2692 rval = usba_persistent_pipe_open(
2693 usba_get_usba_device(ch_dip));
2694 mutex_enter(HUBD_MUTEX(hubd));
2695 ASSERT(rval == USB_SUCCESS);
2696 }
2697 } else {
2698 /*
2699 * Mark this dip for deletion as the device
2700 * is not physically present, and schedule
2701 * cleanup thread upon post resume
2702 */
2703 mutex_exit(HUBD_MUTEX(hubd));
2704
2705 USB_DPRINTF_L2(DPRINT_MASK_ATTA,
2706 hubd->h_log_handle,
2707 "hubd_restore_device_state: "
2708 "dip=%p on port=%d marked for cleanup",
2709 (void *)ch_dip, port);
2710 mutex_enter(&(DEVI(ch_dip)->devi_lock));
2711 DEVI_SET_DEVICE_REMOVED(ch_dip);
2712 mutex_exit(&(DEVI(ch_dip)->devi_lock));
2713
2714 mutex_enter(HUBD_MUTEX(hubd));
2715 }
2716 } else if (ehci_root_hub) {
2717 /* get port status */
2718 (void) hubd_determine_port_status(hubd, port,
2719 &status, &change, NULL, PORT_CHANGE_CSC);
2720
2721 /* check if it is truly connected */
2722 if (status & PORT_STATUS_CCS) {
2723 /*
2724 * reset the port to find out if we have
2725 * 2.0 device connected or 1.X. A 2.0
2726 * device will still be seen as connected,
2727 * while a 1.X device will switch over to
2728 * the companion controller.
2729 */
2730 (void) hubd_reset_port(hubd, port);
2731
2732 (void) hubd_determine_port_status(hubd, port,
2733 &status, &change, NULL, PORT_CHANGE_CSC);
2734
2735 if (status &
2736 (PORT_STATUS_CCS | PORT_STATUS_HSDA)) {
2737 /*
2738 * We have a USB 2.0 device
2739 * connected. Power cycle this port
2740 * so that hotplug thread can
2741 * enumerate this device.
2742 */
2743 (void) hubd_toggle_port(hubd, port);
2744 } else {
2745 USB_DPRINTF_L2(DPRINT_MASK_ATTA,
2746 hubd->h_log_handle,
2747 "hubd_restore_device_state: "
2748 "device on port %d switched over",
2749 port);
2750 }
2751 }
2752
2753 }
2754 }
2755
2756
2757 /* if the device had remote wakeup earlier, enable it again */
2758 if (hubd->h_hubpm->hubp_wakeup_enabled) {
2759 mutex_exit(HUBD_MUTEX(hubd));
2760 (void) usb_handle_remote_wakeup(hubd->h_dip,
2761 USB_REMOTE_WAKEUP_ENABLE);
2762 mutex_enter(HUBD_MUTEX(hubd));
2763 }
2764
2765 hubd->h_dev_state = USB_DEV_ONLINE;
2766 hubd_start_polling(hubd, 0);
2767 (void) hubd_pm_idle_component(hubd, dip, 0);
2768 mutex_exit(HUBD_MUTEX(hubd));
2769 }
2770
2771
2772 /*
2773 * hubd_cleanup:
2774 * cleanup hubd and deallocate. this function is called for
2775 * handling attach failures and detaching including dynamic
2776 * reconfiguration. If called from attaching, it must clean
2777 * up the whole thing and return success.
2778 */
2779 /*ARGSUSED*/
2780 static int
hubd_cleanup(dev_info_t * dip,hubd_t * hubd)2781 hubd_cleanup(dev_info_t *dip, hubd_t *hubd)
2782 {
2783 int rval, old_dev_state;
2784 hub_power_t *hubpm;
2785 #ifdef DEBUG
2786 usb_port_t port;
2787 #endif
2788
2789 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
2790 "hubd_cleanup:");
2791
2792 if ((hubd->h_init_state & HUBD_LOCKS_DONE) == 0) {
2793 goto done;
2794 }
2795
2796 /* ensure we are the only one active */
2797 ndi_devi_enter(dip);
2798
2799 mutex_enter(HUBD_MUTEX(hubd));
2800
2801 /* Cleanup failure is only allowed if called from detach */
2802 if (DEVI_IS_DETACHING(dip)) {
2803 dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip;
2804
2805 /*
2806 * We are being called from detach.
2807 * Fail immediately if the hotplug thread is running
2808 * else set the dev_state to disconnected so that
2809 * hotplug thread just exits without doing anything.
2810 */
2811 if (hubd->h_bus_ctls || hubd->h_bus_pwr ||
2812 hubd->h_hotplug_thread) {
2813 mutex_exit(HUBD_MUTEX(hubd));
2814 ndi_devi_exit(dip);
2815
2816 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
2817 "hubd_cleanup: hotplug thread/bus ctl active "
2818 "- failing detach");
2819
2820 return (USB_FAILURE);
2821 }
2822
2823 /*
2824 * if the deathrow thread is still active or about
2825 * to become active, fail detach
2826 * the roothup can only be detached if nexus drivers
2827 * are unloaded or explicitly offlined
2828 */
2829 if (rh_dip == dip) {
2830 if (hubd->h_cleanup_needed ||
2831 hubd->h_cleanup_active) {
2832 mutex_exit(HUBD_MUTEX(hubd));
2833 ndi_devi_exit(dip);
2834
2835 USB_DPRINTF_L2(DPRINT_MASK_ATTA,
2836 hubd->h_log_handle,
2837 "hubd_cleanup: deathrow still active?"
2838 "- failing detach");
2839
2840 return (USB_FAILURE);
2841 }
2842 }
2843 }
2844
2845 old_dev_state = hubd->h_dev_state;
2846 hubd->h_dev_state = USB_DEV_DISCONNECTED;
2847
2848 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
2849 "hubd_cleanup: stop polling");
2850 hubd_close_intr_pipe(hubd);
2851
2852 ASSERT((hubd->h_bus_ctls || hubd->h_bus_pwr ||
2853 hubd->h_hotplug_thread) == 0);
2854 mutex_exit(HUBD_MUTEX(hubd));
2855
2856 /*
2857 * deallocate events, if events are still registered
2858 * (ie. children still attached) then we have to fail the detach
2859 */
2860 if (hubd->h_ndi_event_hdl) {
2861
2862 rval = ndi_event_free_hdl(hubd->h_ndi_event_hdl);
2863 if (DEVI_IS_ATTACHING(dip)) {
2864
2865 /* It must return success if attaching. */
2866 ASSERT(rval == NDI_SUCCESS);
2867
2868 } else if (rval != NDI_SUCCESS) {
2869
2870 USB_DPRINTF_L2(DPRINT_MASK_ALL, hubd->h_log_handle,
2871 "hubd_cleanup: ndi_event_free_hdl failed");
2872 ndi_devi_exit(dip);
2873
2874 return (USB_FAILURE);
2875
2876 }
2877 }
2878
2879 mutex_enter(HUBD_MUTEX(hubd));
2880
2881 if (hubd->h_init_state & HUBD_CHILDREN_CREATED) {
2882 #ifdef DEBUG
2883 for (port = 1; port <= hubd->h_nports; port++) {
2884 ASSERT(hubd->h_usba_devices[port] == NULL);
2885 ASSERT(hubd->h_children_dips[port] == NULL);
2886 }
2887 #endif
2888 kmem_free(hubd->h_children_dips, hubd->h_cd_list_length);
2889 kmem_free(hubd->h_usba_devices, hubd->h_cd_list_length);
2890 }
2891
2892 /*
2893 * Disable the event callbacks first, after this point, event
2894 * callbacks will never get called. Note we shouldn't hold
2895 * mutex while unregistering events because there may be a
2896 * competing event callback thread. Event callbacks are done
2897 * with ndi mutex held and this can cause a potential deadlock.
2898 * Note that cleanup can't fail after deregistration of events.
2899 */
2900 if (hubd->h_init_state & HUBD_EVENTS_REGISTERED) {
2901 mutex_exit(HUBD_MUTEX(hubd));
2902 usb_unregister_event_cbs(dip, &hubd_events);
2903 hubd_unregister_cpr_callback(hubd);
2904 mutex_enter(HUBD_MUTEX(hubd));
2905 }
2906
2907 /* restore the old dev state so that device can be put into low power */
2908 hubd->h_dev_state = old_dev_state;
2909 hubpm = hubd->h_hubpm;
2910
2911 if ((hubpm) && (hubd->h_dev_state != USB_DEV_DISCONNECTED)) {
2912 (void) hubd_pm_busy_component(hubd, dip, 0);
2913 mutex_exit(HUBD_MUTEX(hubd));
2914 if (hubd->h_hubpm->hubp_wakeup_enabled) {
2915 /*
2916 * Bring the hub to full power before
2917 * issuing the disable remote wakeup command
2918 */
2919 (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
2920
2921 if ((rval = usb_handle_remote_wakeup(hubd->h_dip,
2922 USB_REMOTE_WAKEUP_DISABLE)) != USB_SUCCESS) {
2923 USB_DPRINTF_L2(DPRINT_MASK_PM,
2924 hubd->h_log_handle,
2925 "hubd_cleanup: disable remote wakeup "
2926 "fails=%d", rval);
2927 }
2928 }
2929
2930 (void) pm_lower_power(hubd->h_dip, 0, USB_DEV_OS_PWR_OFF);
2931
2932 mutex_enter(HUBD_MUTEX(hubd));
2933 (void) hubd_pm_idle_component(hubd, dip, 0);
2934 }
2935
2936 if (hubpm) {
2937 if (hubpm->hubp_child_pwrstate) {
2938 kmem_free(hubpm->hubp_child_pwrstate,
2939 MAX_PORTS + 1);
2940 }
2941 kmem_free(hubpm, sizeof (hub_power_t));
2942 }
2943 mutex_exit(HUBD_MUTEX(hubd));
2944
2945 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
2946 "hubd_cleanup: freeing space");
2947
2948 if (hubd->h_init_state & HUBD_HUBDI_REGISTERED) {
2949 rval = usba_hubdi_unregister(dip);
2950 ASSERT(rval == USB_SUCCESS);
2951 }
2952
2953 if (hubd->h_init_state & HUBD_LOCKS_DONE) {
2954 mutex_destroy(HUBD_MUTEX(hubd));
2955 cv_destroy(&hubd->h_cv_reset_port);
2956 cv_destroy(&hubd->h_cv_hotplug_dev);
2957 }
2958
2959 ndi_devi_exit(dip);
2960
2961 if (hubd->h_init_state & HUBD_MINOR_NODE_CREATED) {
2962 ddi_remove_minor_node(dip, NULL);
2963 }
2964
2965 if (usba_is_root_hub(dip)) {
2966 usb_pipe_close(dip, hubd->h_default_pipe,
2967 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
2968 }
2969
2970 done:
2971 if (hubd->h_ancestry_str) {
2972 kmem_free(hubd->h_ancestry_str, HUBD_APID_NAMELEN);
2973 }
2974
2975 usb_client_detach(dip, hubd->h_dev_data);
2976
2977 usb_free_log_hdl(hubd->h_log_handle);
2978
2979 if (!usba_is_root_hub(dip)) {
2980 ddi_soft_state_free(hubd_statep, ddi_get_instance(dip));
2981 }
2982
2983 ddi_prop_remove_all(dip);
2984
2985 return (USB_SUCCESS);
2986 }
2987
2988
2989 /*
2990 * hubd_determine_port_connection:
2991 * Determine which port is in connect status but does not
2992 * have connect status change bit set, and mark port change
2993 * bit accordingly.
2994 * This function is applied during hub attach time.
2995 */
2996 static usb_port_mask_t
hubd_determine_port_connection(hubd_t * hubd)2997 hubd_determine_port_connection(hubd_t *hubd)
2998 {
2999 usb_port_t port;
3000 uint16_t status;
3001 uint16_t change;
3002 usb_port_mask_t port_change = 0;
3003
3004 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
3005
3006 for (port = 1; port <= hubd->h_nports; port++) {
3007
3008 (void) hubd_determine_port_status(hubd, port, &status,
3009 &change, NULL, 0);
3010
3011 /* Check if port is in connect status */
3012 if (!(status & PORT_STATUS_CCS)) {
3013
3014 continue;
3015 }
3016
3017 /*
3018 * Check if port Connect Status Change bit has been set.
3019 * If already set, the connection will be handled by
3020 * intr polling callback, not during attach.
3021 */
3022 if (change & PORT_CHANGE_CSC) {
3023
3024 continue;
3025 }
3026
3027 port_change |= 1 << port;
3028 }
3029
3030 return (port_change);
3031 }
3032
3033
3034 /*
3035 * hubd_check_ports:
3036 * - get hub descriptor
3037 * - check initial port status
3038 * - enable power on all ports
3039 * - enable polling on ep1
3040 */
3041 static int
hubd_check_ports(hubd_t * hubd)3042 hubd_check_ports(hubd_t *hubd)
3043 {
3044 int rval;
3045 usb_port_mask_t port_change = 0;
3046 hubd_hotplug_arg_t *arg;
3047
3048 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
3049
3050 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
3051 "hubd_check_ports: addr=0x%x", usb_get_addr(hubd->h_dip));
3052
3053 /*
3054 * First turn off all port power
3055 */
3056 if ((rval = hubd_disable_all_port_power(hubd)) != USB_SUCCESS) {
3057
3058 /* disable whatever was enabled */
3059 (void) hubd_disable_all_port_power(hubd);
3060
3061 return (rval);
3062 }
3063
3064 /*
3065 * do not switch on immediately (instantly on root hub)
3066 * and allow time to settle
3067 */
3068 mutex_exit(HUBD_MUTEX(hubd));
3069 delay(drv_usectohz(10000));
3070 mutex_enter(HUBD_MUTEX(hubd));
3071
3072 /*
3073 * enable power on all ports so we can see connects
3074 */
3075 if ((rval = hubd_enable_all_port_power(hubd)) != USB_SUCCESS) {
3076 /* disable whatever was enabled */
3077 (void) hubd_disable_all_port_power(hubd);
3078
3079 return (rval);
3080 }
3081
3082 /* wait at least 3 frames before accessing devices */
3083 mutex_exit(HUBD_MUTEX(hubd));
3084 delay(drv_usectohz(10000));
3085 mutex_enter(HUBD_MUTEX(hubd));
3086
3087 /*
3088 * allocate arrays for saving the dips of each child per port
3089 *
3090 * ports go from 1 - n, allocate 1 more entry
3091 */
3092 hubd->h_cd_list_length =
3093 (sizeof (dev_info_t **)) * (hubd->h_nports + 1);
3094
3095 hubd->h_children_dips = (dev_info_t **)kmem_zalloc(
3096 hubd->h_cd_list_length, KM_SLEEP);
3097 hubd->h_usba_devices = (usba_device_t **)kmem_zalloc(
3098 hubd->h_cd_list_length, KM_SLEEP);
3099
3100 hubd->h_init_state |= HUBD_CHILDREN_CREATED;
3101
3102 mutex_exit(HUBD_MUTEX(hubd));
3103 arg = (hubd_hotplug_arg_t *)kmem_zalloc(
3104 sizeof (hubd_hotplug_arg_t), KM_SLEEP);
3105 mutex_enter(HUBD_MUTEX(hubd));
3106
3107 if ((rval = hubd_open_intr_pipe(hubd)) != USB_SUCCESS) {
3108 kmem_free(arg, sizeof (hubd_hotplug_arg_t));
3109
3110 return (rval);
3111 }
3112
3113 hubd_start_polling(hubd, 0);
3114
3115 /*
3116 * Some hub devices, like the embedded hub in the CKS ErgoMagic
3117 * keyboard, may only have connection status bit set, but not
3118 * have connect status change bit set when a device has been
3119 * connected to its downstream port before the hub is enumerated.
3120 * Then when the hub is in enumeration, the devices connected to
3121 * it cannot be detected by the intr pipe and won't be enumerated.
3122 * We need to check such situation here and enumerate the downstream
3123 * devices for such hubs.
3124 */
3125 port_change = hubd_determine_port_connection(hubd);
3126
3127 if (port_change != 0 || hubd->h_port_change != 0) {
3128 hubd_pm_busy_component(hubd, hubd->h_dip, 0);
3129
3130 arg->hubd = hubd;
3131 arg->hotplug_during_attach = B_TRUE;
3132 hubd->h_port_change |= port_change;
3133
3134 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
3135 "hubd_check_ports: port change=0x%x, need to connect",
3136 hubd->h_port_change);
3137
3138 if (usb_async_req(hubd->h_dip, hubd_hotplug_thread,
3139 (void *)arg, 0) == USB_SUCCESS) {
3140 hubd->h_hotplug_thread++;
3141 } else {
3142 /* mark this device as idle */
3143 hubd_pm_idle_component(hubd, hubd->h_dip, 0);
3144 kmem_free(arg, sizeof (hubd_hotplug_arg_t));
3145 }
3146 } else {
3147 kmem_free(arg, sizeof (hubd_hotplug_arg_t));
3148 }
3149
3150 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
3151 "hubd_check_ports done");
3152
3153 return (USB_SUCCESS);
3154 }
3155
3156
3157 /*
3158 * hubd_get_hub_descriptor:
3159 */
3160 static int
hubd_get_hub_descriptor(hubd_t * hubd)3161 hubd_get_hub_descriptor(hubd_t *hubd)
3162 {
3163 mblk_t *data = NULL;
3164 usb_cr_t completion_reason;
3165 usb_cb_flags_t cb_flags;
3166 uint16_t length, wValue;
3167 int rval;
3168 usb_req_attrs_t attr = 0;
3169
3170 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
3171 "hubd_get_hub_descriptor:");
3172
3173 if ((hubd->h_dev_data->dev_descr->idVendor == USB_HUB_INTEL_VID) &&
3174 (hubd->h_dev_data->dev_descr->idProduct == USB_HUB_INTEL_PID)) {
3175 attr = USB_ATTRS_SHORT_XFER_OK;
3176 }
3177
3178 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
3179 ASSERT(hubd->h_default_pipe != 0);
3180
3181 mutex_exit(HUBD_MUTEX(hubd));
3182
3183 /*
3184 * The contents of wValue change depending on whether this is a USB 2 or
3185 * USB 3 device. SuperSpeed Hubs have different descriptors and you
3186 * cannot ask them for the traditional USB 2 descriptor.
3187 */
3188 if (hubd->h_usba_device->usb_port_status >= USBA_SUPER_SPEED_DEV) {
3189 wValue = USB_DESCR_TYPE_SS_HUB << 8 | HUBD_DEFAULT_DESC_INDEX;
3190 } else {
3191 wValue = USB_DESCR_TYPE_HUB << 8 | HUBD_DEFAULT_DESC_INDEX;
3192 }
3193
3194 /*
3195 * The hub descriptor length varies in various versions of USB. For
3196 * example, in USB 2 it's at least 9 bytes long. To start with, we
3197 * always get the first 8 bytes so we can figure out how long it
3198 * actually is.
3199 */
3200 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
3201 hubd->h_default_pipe,
3202 HUB_CLASS_REQ_TYPE,
3203 USB_REQ_GET_DESCR, /* bRequest */
3204 wValue, /* wValue */
3205 0, /* wIndex */
3206 8, /* wLength */
3207 &data, 0,
3208 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
3209 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
3210 "get hub descriptor failed: cr=%d cb_fl=0x%x rval=%d",
3211 completion_reason, cb_flags, rval);
3212 freemsg(data);
3213 mutex_enter(HUBD_MUTEX(hubd));
3214
3215 return (rval);
3216 }
3217
3218 length = *(data->b_rptr);
3219
3220 if (length > 8) {
3221 freemsg(data);
3222 data = NULL;
3223
3224 /* get complete hub descriptor */
3225 rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
3226 hubd->h_default_pipe,
3227 HUB_CLASS_REQ_TYPE,
3228 USB_REQ_GET_DESCR, /* bRequest */
3229 wValue, /* wValue */
3230 0, /* wIndex */
3231 length, /* wLength */
3232 &data, attr,
3233 &completion_reason, &cb_flags, 0);
3234
3235 /*
3236 * Hub descriptor data less than 9 bytes is not valid and
3237 * may cause trouble if we use it. See USB2.0 Tab11-13.
3238 */
3239 if ((rval != USB_SUCCESS) || (MBLKL(data) <= 8)) {
3240 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
3241 "get hub descriptor failed: "
3242 "cr=%d cb_fl=0x%x rval=%d, len=%ld",
3243 completion_reason, cb_flags, rval,
3244 (data)?MBLKL(data):0);
3245 freemsg(data);
3246 mutex_enter(HUBD_MUTEX(hubd));
3247
3248 return (rval);
3249 }
3250 }
3251
3252 mutex_enter(HUBD_MUTEX(hubd));
3253
3254 /*
3255 * Parse the hub descriptor. Note that the format of the descriptor
3256 * itself depends on the USB version. We handle the different ones and
3257 * transform it into a single uniform view.
3258 */
3259
3260 ASSERT(*(data->b_rptr + 2) <= (MAX_PORTS + 1));
3261 if (hubd->h_usba_device->usb_port_status >= USBA_SUPER_SPEED_DEV) {
3262 usb_ss_hub_descr_t hub_descr;
3263 char *desc = "cccscccs";
3264 ASSERT(*(data->b_rptr + 1) == ROOT_HUB_SS_DESCRIPTOR_TYPE);
3265
3266 /*
3267 * Note many hubs may support less than the 255 devices that the
3268 * USB specification allows for. In those cases, we'll simply
3269 * read less and it should be okay.
3270 */
3271 if (usb_parse_CV_descr(desc, data->b_rptr, MBLKL(data),
3272 (void *)&hub_descr, sizeof (hub_descr)) == 0) {
3273 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
3274 "parsing hub descriptor failed");
3275 freemsg(data);
3276 return (USB_FAILURE);
3277 }
3278
3279 hubd->h_nports = hub_descr.bNbrPorts;
3280 hubd->h_hub_chars = hub_descr.wHubCharacteristics;
3281 hubd->h_power_good = hub_descr.bPwrOn2PwrGood;
3282 hubd->h_current = hub_descr.bHubContrCurrent;
3283 } else {
3284 usb_hub_descr_t hub_descr;
3285 if (usb_parse_CV_descr("cccscccccc",
3286 data->b_rptr, MBLKL(data),
3287 (void *)&hub_descr, sizeof (usb_hub_descr_t)) == 0) {
3288 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
3289 "parsing hub descriptor failed");
3290 freemsg(data);
3291 return (USB_FAILURE);
3292 }
3293
3294 hubd->h_nports = hub_descr.bNbrPorts;
3295 hubd->h_hub_chars = hub_descr.wHubCharacteristics;
3296 hubd->h_power_good = hub_descr.bPwrOn2PwrGood;
3297 hubd->h_current = hub_descr.bHubContrCurrent;
3298 }
3299
3300 freemsg(data);
3301
3302 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
3303 "rval=0x%x bNbrPorts=0x%x wHubChars=0x%x "
3304 "PwrOn2PwrGood=0x%x HubContrCurrent=%dmA", rval,
3305 hubd->h_nports, hubd->h_hub_chars,
3306 hubd->h_power_good, hubd->h_current);
3307
3308 if (hubd->h_nports > MAX_PORTS) {
3309 USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubd->h_log_handle,
3310 "Hub driver supports max of %d ports on hub. "
3311 "Hence using the first %d port of %d ports available",
3312 MAX_PORTS, MAX_PORTS, hubd->h_nports);
3313
3314 hubd->h_nports = MAX_PORTS;
3315 }
3316
3317 return (USB_SUCCESS);
3318 }
3319
3320 static int
hubd_set_hub_depth(hubd_t * hubd)3321 hubd_set_hub_depth(hubd_t *hubd)
3322 {
3323 int rval;
3324 usb_cr_t completion_reason;
3325 usb_cb_flags_t cb_flags;
3326 usba_device_t *ud;
3327 uint16_t depth;
3328
3329 /*
3330 * We only need to set the hub depth devices for hubs that are at least
3331 * SuperSpeed devices. This didn't exist for USB 2.0 and older hubs.
3332 * There's also no need to call this on the root hub.
3333 */
3334 if (hubd->h_usba_device->usb_port_status < USBA_SUPER_SPEED_DEV ||
3335 usba_is_root_hub(hubd->h_dip))
3336 return (USB_SUCCESS);
3337
3338 depth = 0;
3339 ud = hubd->h_usba_device;
3340 while (ud->usb_parent_hub != NULL) {
3341 depth++;
3342 ud = ud->usb_parent_hub;
3343 }
3344 ASSERT(depth > 0);
3345
3346 if (depth > HUBD_SS_MAX_DEPTH) {
3347 const char *mfg, *prod;
3348
3349 ud = hubd->h_usba_device;
3350 prod = ud->usb_product_str;
3351 if (prod == NULL)
3352 prod = "Unknown Device";
3353 mfg = ud->usb_mfg_str;
3354 if (mfg == NULL)
3355 mfg = "Unknown Manufacturer";
3356 cmn_err(CE_WARN, "Unable to attach USB 3.x hub %s %s. A "
3357 "maximum of %d hubs may be cascaded", mfg, prod,
3358 HUBD_SS_MAX_DEPTH);
3359 return (USB_FAILURE);
3360 }
3361
3362 /*
3363 * When making the HUB_REQ_SET_HUB_DEPTH request, a hub connected to a
3364 * root port is considered to have a hub depth of zero whereas we
3365 * consider having a hub depth of one above.
3366 */
3367 depth--;
3368
3369 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
3370 hubd->h_default_pipe,
3371 HUB_SET_HUB_DEPTH_TYPE,
3372 HUB_REQ_SET_HUB_DEPTH, /* bRequest */
3373 depth, /* wValue */
3374 0, /* wIndex */
3375 0, /* wLength */
3376 NULL, 0,
3377 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
3378 USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
3379 "get set hub depth failed: cr=%d cb=0x%x",
3380 completion_reason, cb_flags);
3381 }
3382
3383 return (rval);
3384 }
3385
3386 /*
3387 * hubd_get_hub_status_words:
3388 */
3389 static int
hubd_get_hub_status_words(hubd_t * hubd,uint16_t * status)3390 hubd_get_hub_status_words(hubd_t *hubd, uint16_t *status)
3391 {
3392 usb_cr_t completion_reason;
3393 usb_cb_flags_t cb_flags;
3394 mblk_t *data = NULL;
3395
3396 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
3397
3398 mutex_exit(HUBD_MUTEX(hubd));
3399
3400 if (usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe,
3401 HUB_CLASS_REQ_TYPE,
3402 USB_REQ_GET_STATUS,
3403 0,
3404 0,
3405 GET_STATUS_LENGTH,
3406 &data, 0,
3407 &completion_reason, &cb_flags, 0) != USB_SUCCESS) {
3408 USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
3409 "get hub status failed: cr=%d cb=0x%x",
3410 completion_reason, cb_flags);
3411
3412 if (data) {
3413 freemsg(data);
3414 }
3415
3416 mutex_enter(HUBD_MUTEX(hubd));
3417
3418 return (USB_FAILURE);
3419 }
3420
3421 mutex_enter(HUBD_MUTEX(hubd));
3422
3423 status[0] = (*(data->b_rptr + 1) << 8) | *(data->b_rptr);
3424 status[1] = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2);
3425
3426 USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
3427 "hub status=0x%x change=0x%x", status[0], status[1]);
3428
3429 freemsg(data);
3430
3431 return (USB_SUCCESS);
3432 }
3433
3434
3435 /*
3436 * hubd_open_intr_pipe:
3437 * we read all descriptors first for curiosity and then simply
3438 * open the pipe
3439 */
3440 static int
hubd_open_intr_pipe(hubd_t * hubd)3441 hubd_open_intr_pipe(hubd_t *hubd)
3442 {
3443 int rval;
3444
3445 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
3446 "hubd_open_intr_pipe:");
3447
3448 ASSERT(hubd->h_intr_pipe_state == HUBD_INTR_PIPE_IDLE);
3449
3450 hubd->h_intr_pipe_state = HUBD_INTR_PIPE_OPENING;
3451 mutex_exit(HUBD_MUTEX(hubd));
3452
3453 if ((rval = usb_pipe_xopen(hubd->h_dip,
3454 &hubd->h_ep1_xdescr, &hubd->h_pipe_policy,
3455 0, &hubd->h_ep1_ph)) != USB_SUCCESS) {
3456 USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
3457 "open intr pipe failed (%d)", rval);
3458
3459 mutex_enter(HUBD_MUTEX(hubd));
3460 hubd->h_intr_pipe_state = HUBD_INTR_PIPE_IDLE;
3461
3462 return (rval);
3463 }
3464
3465 mutex_enter(HUBD_MUTEX(hubd));
3466 hubd->h_intr_pipe_state = HUBD_INTR_PIPE_ACTIVE;
3467
3468 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
3469 "open intr pipe succeeded, ph=0x%p", (void *)hubd->h_ep1_ph);
3470
3471 return (USB_SUCCESS);
3472 }
3473
3474
3475 /*
3476 * hubd_start_polling:
3477 * start or restart the polling
3478 */
3479 static void
hubd_start_polling(hubd_t * hubd,int always)3480 hubd_start_polling(hubd_t *hubd, int always)
3481 {
3482 usb_intr_req_t *reqp;
3483 int rval;
3484 usb_pipe_state_t pipe_state;
3485
3486 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
3487 "start polling: always=%d dev_state=%d pipe_state=%d\n\t"
3488 "thread=%d ep1_ph=0x%p",
3489 always, hubd->h_dev_state, hubd->h_intr_pipe_state,
3490 hubd->h_hotplug_thread, (void *)hubd->h_ep1_ph);
3491
3492 /*
3493 * start or restart polling on the intr pipe
3494 * only if hotplug thread is not running
3495 */
3496 if ((always == HUBD_ALWAYS_START_POLLING) ||
3497 ((hubd->h_dev_state == USB_DEV_ONLINE) &&
3498 (hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE) &&
3499 (hubd->h_hotplug_thread == 0) && hubd->h_ep1_ph)) {
3500 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
3501 "start polling requested");
3502
3503 reqp = usb_alloc_intr_req(hubd->h_dip, 0, USB_FLAGS_SLEEP);
3504
3505 reqp->intr_client_private = (usb_opaque_t)hubd;
3506 reqp->intr_attributes = USB_ATTRS_SHORT_XFER_OK |
3507 USB_ATTRS_AUTOCLEARING;
3508 reqp->intr_len = hubd->h_ep1_xdescr.uex_ep.wMaxPacketSize;
3509 reqp->intr_cb = hubd_read_cb;
3510 reqp->intr_exc_cb = hubd_exception_cb;
3511 mutex_exit(HUBD_MUTEX(hubd));
3512 if ((rval = usb_pipe_intr_xfer(hubd->h_ep1_ph, reqp,
3513 USB_FLAGS_SLEEP)) != USB_SUCCESS) {
3514 USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
3515 "start polling failed, rval=%d", rval);
3516 usb_free_intr_req(reqp);
3517 }
3518
3519 rval = usb_pipe_get_state(hubd->h_ep1_ph, &pipe_state,
3520 USB_FLAGS_SLEEP);
3521 if (pipe_state != USB_PIPE_STATE_ACTIVE) {
3522 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
3523 "intr pipe state=%d, rval=%d", pipe_state, rval);
3524 }
3525 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
3526 "start polling request 0x%p", (void *)reqp);
3527
3528 mutex_enter(HUBD_MUTEX(hubd));
3529 }
3530 }
3531
3532
3533 /*
3534 * hubd_stop_polling
3535 * stop polling but do not close the pipe
3536 */
3537 static void
hubd_stop_polling(hubd_t * hubd)3538 hubd_stop_polling(hubd_t *hubd)
3539 {
3540 int rval;
3541 usb_pipe_state_t pipe_state;
3542
3543 if (hubd->h_ep1_ph) {
3544 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
3545 "hubd_stop_polling:");
3546 hubd->h_intr_pipe_state = HUBD_INTR_PIPE_STOPPED;
3547 mutex_exit(HUBD_MUTEX(hubd));
3548
3549 usb_pipe_stop_intr_polling(hubd->h_ep1_ph, USB_FLAGS_SLEEP);
3550 rval = usb_pipe_get_state(hubd->h_ep1_ph, &pipe_state,
3551 USB_FLAGS_SLEEP);
3552
3553 if (pipe_state != USB_PIPE_STATE_IDLE) {
3554 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
3555 "intr pipe state=%d, rval=%d", pipe_state, rval);
3556 }
3557 mutex_enter(HUBD_MUTEX(hubd));
3558 if (hubd->h_intr_pipe_state == HUBD_INTR_PIPE_STOPPED) {
3559 hubd->h_intr_pipe_state = HUBD_INTR_PIPE_ACTIVE;
3560 }
3561 }
3562 }
3563
3564
3565 /*
3566 * hubd_close_intr_pipe:
3567 * close the pipe (which also stops the polling
3568 * and wait for the hotplug thread to exit
3569 */
3570 static void
hubd_close_intr_pipe(hubd_t * hubd)3571 hubd_close_intr_pipe(hubd_t *hubd)
3572 {
3573 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
3574 "hubd_close_intr_pipe:");
3575
3576 /*
3577 * Now that no async operation is outstanding on pipe,
3578 * we can change the state to HUBD_INTR_PIPE_CLOSING
3579 */
3580 hubd->h_intr_pipe_state = HUBD_INTR_PIPE_CLOSING;
3581
3582 ASSERT(hubd->h_hotplug_thread == 0);
3583
3584 if (hubd->h_ep1_ph) {
3585 mutex_exit(HUBD_MUTEX(hubd));
3586 usb_pipe_close(hubd->h_dip, hubd->h_ep1_ph, USB_FLAGS_SLEEP,
3587 NULL, NULL);
3588 mutex_enter(HUBD_MUTEX(hubd));
3589 hubd->h_ep1_ph = NULL;
3590 }
3591
3592 hubd->h_intr_pipe_state = HUBD_INTR_PIPE_IDLE;
3593 }
3594
3595
3596 /*
3597 * hubd_exception_cb
3598 * interrupt ep1 exception callback function.
3599 * this callback executes in taskq thread context and assumes
3600 * autoclearing
3601 */
3602 /*ARGSUSED*/
3603 static void
hubd_exception_cb(usb_pipe_handle_t pipe,usb_intr_req_t * reqp)3604 hubd_exception_cb(usb_pipe_handle_t pipe, usb_intr_req_t *reqp)
3605 {
3606 hubd_t *hubd = (hubd_t *)(reqp->intr_client_private);
3607
3608 USB_DPRINTF_L2(DPRINT_MASK_CALLBACK, hubd->h_log_handle,
3609 "hubd_exception_cb: "
3610 "req=0x%p cr=%d data=0x%p cb_flags=0x%x", (void *)reqp,
3611 reqp->intr_completion_reason, (void *)reqp->intr_data,
3612 reqp->intr_cb_flags);
3613
3614 ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
3615
3616 mutex_enter(HUBD_MUTEX(hubd));
3617 (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0);
3618
3619 switch (reqp->intr_completion_reason) {
3620 case USB_CR_PIPE_RESET:
3621 /* only restart polling after autoclearing */
3622 if ((hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE) &&
3623 (hubd->h_port_reset_wait == 0)) {
3624 hubd_start_polling(hubd, 0);
3625 }
3626
3627 break;
3628 case USB_CR_DEV_NOT_RESP:
3629 case USB_CR_STOPPED_POLLING:
3630 case USB_CR_PIPE_CLOSING:
3631 case USB_CR_UNSPECIFIED_ERR:
3632 /* never restart polling on these conditions */
3633 default:
3634 /* for all others, wait for the autoclearing PIPE_RESET cb */
3635
3636 break;
3637 }
3638
3639 usb_free_intr_req(reqp);
3640 (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
3641 mutex_exit(HUBD_MUTEX(hubd));
3642 }
3643
3644
3645 /*
3646 * helper function to convert LE bytes to a portmask
3647 */
3648 static usb_port_mask_t
hubd_mblk2portmask(mblk_t * data)3649 hubd_mblk2portmask(mblk_t *data)
3650 {
3651 int len = min(MBLKL(data), sizeof (usb_port_mask_t));
3652 usb_port_mask_t rval = 0;
3653 int i;
3654
3655 for (i = 0; i < len; i++) {
3656 rval |= data->b_rptr[i] << (i * 8);
3657 }
3658
3659 return (rval);
3660 }
3661
3662
3663 /*
3664 * hubd_read_cb:
3665 * interrupt ep1 callback function
3666 *
3667 * the status indicates just a change on the pipe with no indication
3668 * of what the change was
3669 *
3670 * known conditions:
3671 * - reset port completion
3672 * - connect
3673 * - disconnect
3674 *
3675 * for handling the hotplugging, create a new thread that can do
3676 * synchronous usba calls
3677 */
3678 static void
hubd_read_cb(usb_pipe_handle_t pipe,usb_intr_req_t * reqp)3679 hubd_read_cb(usb_pipe_handle_t pipe, usb_intr_req_t *reqp)
3680 {
3681 hubd_t *hubd = (hubd_t *)(reqp->intr_client_private);
3682 size_t length;
3683 mblk_t *data = reqp->intr_data;
3684 int mem_flag = 0;
3685 hubd_hotplug_arg_t *arg;
3686
3687 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
3688 "hubd_read_cb: ph=0x%p req=0x%p", (void *)pipe, (void *)reqp);
3689
3690 ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
3691
3692 /*
3693 * At present, we are not handling notification for completion of
3694 * asynchronous pipe reset, for which this data ptr could be NULL
3695 */
3696
3697 if (data == NULL) {
3698 usb_free_intr_req(reqp);
3699
3700 return;
3701 }
3702
3703 arg = (hubd_hotplug_arg_t *)kmem_zalloc(
3704 sizeof (hubd_hotplug_arg_t), KM_SLEEP);
3705 mem_flag = 1;
3706
3707 mutex_enter(HUBD_MUTEX(hubd));
3708
3709 if ((hubd->h_dev_state == USB_DEV_SUSPENDED) ||
3710 (hubd->h_intr_pipe_state != HUBD_INTR_PIPE_ACTIVE)) {
3711 mutex_exit(HUBD_MUTEX(hubd));
3712 usb_free_intr_req(reqp);
3713 kmem_free(arg, sizeof (hubd_hotplug_arg_t));
3714
3715 return;
3716 }
3717
3718 ASSERT(hubd->h_ep1_ph == pipe);
3719
3720 length = MBLKL(data);
3721
3722 /*
3723 * Only look at the data and startup the hotplug thread if
3724 * there actually is data.
3725 */
3726 if (length != 0) {
3727 usb_port_mask_t port_change = hubd_mblk2portmask(data);
3728
3729 /*
3730 * if a port change was already reported and we are waiting for
3731 * reset port completion then wake up the hotplug thread which
3732 * should be waiting on reset port completion
3733 *
3734 * if there is disconnect event instead of reset completion, let
3735 * the hotplug thread figure this out
3736 */
3737
3738 /* remove the reset wait bits from the status */
3739 hubd->h_port_change |= port_change &
3740 ~hubd->h_port_reset_wait;
3741
3742 USB_DPRINTF_L3(DPRINT_MASK_CALLBACK, hubd->h_log_handle,
3743 "port change=0x%x port_reset_wait=0x%x",
3744 hubd->h_port_change, hubd->h_port_reset_wait);
3745
3746 /* there should be only one reset bit active at the time */
3747 if (hubd->h_port_reset_wait & port_change) {
3748 hubd->h_port_reset_wait = 0;
3749 cv_signal(&hubd->h_cv_reset_port);
3750 }
3751
3752 /*
3753 * kick off the thread only if device is ONLINE and it is not
3754 * during attaching or detaching
3755 */
3756 if ((hubd->h_dev_state == USB_DEV_ONLINE) &&
3757 (!DEVI_IS_ATTACHING(hubd->h_dip)) &&
3758 (!DEVI_IS_DETACHING(hubd->h_dip)) &&
3759 (hubd->h_port_change) &&
3760 (hubd->h_hotplug_thread == 0)) {
3761 USB_DPRINTF_L3(DPRINT_MASK_CALLBACK, hubd->h_log_handle,
3762 "creating hotplug thread: "
3763 "dev_state=%d", hubd->h_dev_state);
3764
3765 /*
3766 * Mark this device as busy. The will be marked idle
3767 * if the async req fails or at the exit of hotplug
3768 * thread
3769 */
3770 (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0);
3771
3772 arg->hubd = hubd;
3773 arg->hotplug_during_attach = B_FALSE;
3774
3775 if (usb_async_req(hubd->h_dip,
3776 hubd_hotplug_thread,
3777 (void *)arg, 0) == USB_SUCCESS) {
3778 hubd->h_hotplug_thread++;
3779 mem_flag = 0;
3780 } else {
3781 /* mark this device as idle */
3782 (void) hubd_pm_idle_component(hubd,
3783 hubd->h_dip, 0);
3784 }
3785 }
3786 }
3787 mutex_exit(HUBD_MUTEX(hubd));
3788
3789 if (mem_flag == 1) {
3790 kmem_free(arg, sizeof (hubd_hotplug_arg_t));
3791 }
3792
3793 usb_free_intr_req(reqp);
3794 }
3795
3796
3797 /*
3798 * hubd_hotplug_thread:
3799 * handles resetting of port, and creating children
3800 *
3801 * the ports to check are indicated in h_port_change bit mask
3802 * XXX note that one time poll doesn't work on the root hub
3803 */
3804 static void
hubd_hotplug_thread(void * arg)3805 hubd_hotplug_thread(void *arg)
3806 {
3807 hubd_hotplug_arg_t *hd_arg = (hubd_hotplug_arg_t *)arg;
3808 hubd_t *hubd = hd_arg->hubd;
3809 boolean_t attach_flg = hd_arg->hotplug_during_attach;
3810 usb_port_t port;
3811 uint16_t nports;
3812 uint16_t status, change;
3813 hub_power_t *hubpm;
3814 dev_info_t *hdip = hubd->h_dip;
3815 dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip;
3816 dev_info_t *child_dip;
3817 boolean_t online_child = B_FALSE;
3818 boolean_t offline_child = B_FALSE;
3819 boolean_t pwrup_child = B_FALSE;
3820 int old_state;
3821
3822 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3823 "hubd_hotplug_thread: started");
3824
3825 /*
3826 * Before console is init'd, we temporarily block the hotplug
3827 * threads so that BUS_CONFIG_ONE through hubd_bus_config() can be
3828 * processed quickly. This reduces the time needed for vfs_mountroot()
3829 * to mount the root FS from a USB disk. And on SPARC platform,
3830 * in order to load 'consconfig' successfully after OBP is gone,
3831 * we need to check 'modrootloaded' to make sure root filesystem is
3832 * available.
3833 */
3834 while (!modrootloaded || !consconfig_console_is_ready()) {
3835 delay(drv_usectohz(10000));
3836 }
3837
3838 kmem_free(arg, sizeof (hubd_hotplug_arg_t));
3839
3840 /*
3841 * if our bus power entry point is active, process the change
3842 * on the next notification of interrupt pipe
3843 */
3844 mutex_enter(HUBD_MUTEX(hubd));
3845 if (hubd->h_bus_pwr || (hubd->h_hotplug_thread > 1)) {
3846 hubd->h_hotplug_thread--;
3847
3848 /* mark this device as idle */
3849 hubd_pm_idle_component(hubd, hubd->h_dip, 0);
3850 mutex_exit(HUBD_MUTEX(hubd));
3851
3852 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3853 "hubd_hotplug_thread: "
3854 "bus_power in progress/hotplugging undesirable - quit");
3855
3856 return;
3857 }
3858 mutex_exit(HUBD_MUTEX(hubd));
3859
3860 ndi_hold_devi(hdip); /* so we don't race with detach */
3861
3862 mutex_enter(HUBD_MUTEX(hubd));
3863
3864 /* is this the root hub? */
3865 if (hdip == rh_dip) {
3866 if (hubd->h_dev_state == USB_DEV_PWRED_DOWN) {
3867 hubpm = hubd->h_hubpm;
3868
3869 /* mark the root hub as full power */
3870 hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
3871 hubpm->hubp_time_at_full_power = gethrtime();
3872 mutex_exit(HUBD_MUTEX(hubd));
3873
3874 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3875 "hubd_hotplug_thread: call pm_power_has_changed");
3876
3877 (void) pm_power_has_changed(hdip, 0,
3878 USB_DEV_OS_FULL_PWR);
3879
3880 mutex_enter(HUBD_MUTEX(hubd));
3881 hubd->h_dev_state = USB_DEV_ONLINE;
3882 }
3883
3884 } else {
3885 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3886 "hubd_hotplug_thread: not root hub");
3887 }
3888
3889 mutex_exit(HUBD_MUTEX(hubd));
3890
3891 /*
3892 * this ensures one hotplug activity per system at a time.
3893 * we enter the parent PCI node to have this serialization.
3894 * this also excludes ioctls and deathrow thread
3895 * (a bit crude but easier to debug)
3896 */
3897 ndi_devi_enter(ddi_get_parent(rh_dip));
3898 ndi_devi_enter(rh_dip);
3899
3900 /* exclude other threads */
3901 ndi_devi_enter(hdip);
3902 mutex_enter(HUBD_MUTEX(hubd));
3903
3904 ASSERT(hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE);
3905
3906 nports = hubd->h_nports;
3907
3908 hubd_stop_polling(hubd);
3909
3910 while ((hubd->h_dev_state == USB_DEV_ONLINE) &&
3911 (hubd->h_port_change)) {
3912 /*
3913 * The 0th bit is the hub status change bit.
3914 * handle loss of local power here
3915 */
3916 if (hubd->h_port_change & HUB_CHANGE_STATUS) {
3917 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3918 "hubd_hotplug_thread: hub status change!");
3919
3920 /*
3921 * This should be handled properly. For now,
3922 * mask off the bit.
3923 */
3924 hubd->h_port_change &= ~HUB_CHANGE_STATUS;
3925
3926 /*
3927 * check and ack hub status
3928 * this causes stall conditions
3929 * when local power is removed
3930 */
3931 (void) hubd_get_hub_status(hubd);
3932 }
3933
3934 for (port = 1; port <= nports; port++) {
3935 usb_port_mask_t port_mask;
3936 boolean_t was_connected;
3937
3938 port_mask = 1 << port;
3939 was_connected =
3940 (hubd->h_port_state[port] & PORT_STATUS_CCS) &&
3941 (hubd->h_children_dips[port]);
3942
3943 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3944 "hubd_hotplug_thread: "
3945 "port %d mask=0x%x change=0x%x connected=0x%x",
3946 port, port_mask, hubd->h_port_change,
3947 was_connected);
3948
3949 /*
3950 * is this a port connection that changed?
3951 */
3952 if ((hubd->h_port_change & port_mask) == 0) {
3953
3954 continue;
3955 }
3956 hubd->h_port_change &= ~port_mask;
3957
3958 /* ack all changes */
3959 (void) hubd_determine_port_status(hubd, port,
3960 &status, &change, NULL, HUBD_ACK_ALL_CHANGES);
3961
3962 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3963 "handle port %d:\n\t"
3964 "new status=0x%x change=0x%x was_conn=0x%x ",
3965 port, status, change, was_connected);
3966
3967 /* Recover a disabled port */
3968 if (change & PORT_CHANGE_PESC) {
3969 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG,
3970 hubd->h_log_handle,
3971 "port%d Disabled - "
3972 "status=0x%x, change=0x%x",
3973 port, status, change);
3974
3975 /*
3976 * if the port was connected and is still
3977 * connected, recover the port
3978 */
3979 if (was_connected && (status &
3980 PORT_STATUS_CCS)) {
3981 online_child |=
3982 (hubd_recover_disabled_port(hubd,
3983 port) == USB_SUCCESS);
3984 }
3985 }
3986
3987 /*
3988 * Now check what changed on the port
3989 */
3990 if ((change & PORT_CHANGE_CSC) || attach_flg) {
3991 if ((status & PORT_STATUS_CCS) &&
3992 (!was_connected)) {
3993 /* new device plugged in */
3994 online_child |=
3995 (hubd_handle_port_connect(hubd,
3996 port) == USB_SUCCESS);
3997
3998 } else if ((status & PORT_STATUS_CCS) &&
3999 was_connected) {
4000 /*
4001 * In this case we can never be sure
4002 * if the device indeed got hotplugged
4003 * or the hub is falsely reporting the
4004 * change.
4005 */
4006 child_dip = hubd->h_children_dips[port];
4007
4008 mutex_exit(HUBD_MUTEX(hubd));
4009 /*
4010 * this ensures we do not race with
4011 * other threads which are detaching
4012 * the child driver at the same time.
4013 */
4014 ndi_devi_enter(child_dip);
4015 /*
4016 * Now check if the driver remains
4017 * attached.
4018 */
4019 if (i_ddi_devi_attached(child_dip)) {
4020 /*
4021 * first post a disconnect event
4022 * to the child.
4023 */
4024 hubd_post_event(hubd, port,
4025 USBA_EVENT_TAG_HOT_REMOVAL);
4026 mutex_enter(HUBD_MUTEX(hubd));
4027
4028 /*
4029 * then reset the port and
4030 * recover the device
4031 */
4032 online_child |=
4033 (hubd_handle_port_connect(
4034 hubd, port) == USB_SUCCESS);
4035
4036 mutex_exit(HUBD_MUTEX(hubd));
4037 }
4038
4039 ndi_devi_exit(child_dip);
4040 mutex_enter(HUBD_MUTEX(hubd));
4041 } else if (was_connected) {
4042 /* this is a disconnect */
4043 mutex_exit(HUBD_MUTEX(hubd));
4044 hubd_post_event(hubd, port,
4045 USBA_EVENT_TAG_HOT_REMOVAL);
4046 mutex_enter(HUBD_MUTEX(hubd));
4047
4048 offline_child = B_TRUE;
4049 }
4050 }
4051
4052 /*
4053 * Check if any port is coming out of suspend
4054 */
4055 if (change & PORT_CHANGE_PSSC) {
4056 /* a resuming device could have disconnected */
4057 if (was_connected &&
4058 hubd->h_children_dips[port]) {
4059
4060 /* device on this port resuming */
4061 dev_info_t *dip;
4062
4063 dip = hubd->h_children_dips[port];
4064
4065 /*
4066 * Don't raise power on detaching child
4067 */
4068 if (!DEVI_IS_DETACHING(dip)) {
4069 /*
4070 * As this child is not
4071 * detaching, we set this
4072 * flag, causing bus_ctls
4073 * to stall detach till
4074 * pm_raise_power returns
4075 * and flag it for a deferred
4076 * raise_power.
4077 *
4078 * pm_raise_power is deferred
4079 * because we need to release
4080 * the locks first.
4081 */
4082 hubd->h_port_state[port] |=
4083 HUBD_CHILD_RAISE_POWER;
4084 pwrup_child = B_TRUE;
4085 mutex_exit(HUBD_MUTEX(hubd));
4086
4087 /*
4088 * make sure that child
4089 * doesn't disappear
4090 */
4091 ndi_hold_devi(dip);
4092
4093 mutex_enter(HUBD_MUTEX(hubd));
4094 }
4095 }
4096 }
4097
4098 /*
4099 * Check if the port is over-current
4100 */
4101 if (change & PORT_CHANGE_OCIC) {
4102 USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG,
4103 hubd->h_log_handle,
4104 "Port%d in over current condition, "
4105 "please check the attached device to "
4106 "clear the condition. The system will "
4107 "try to recover the port, but if not "
4108 "successful, you need to re-connect "
4109 "the hub or reboot the system to bring "
4110 "the port back to work", port);
4111
4112 if (!(status & PORT_STATUS_PPS)) {
4113 /*
4114 * Try to enable port power, but
4115 * possibly fail. Ignore failure
4116 */
4117 (void) hubd_enable_port_power(hubd,
4118 port);
4119
4120 /*
4121 * Delay some time to avoid
4122 * over-current event to happen
4123 * too frequently in some cases
4124 */
4125 mutex_exit(HUBD_MUTEX(hubd));
4126 delay(drv_usectohz(500000));
4127 mutex_enter(HUBD_MUTEX(hubd));
4128 }
4129 }
4130 }
4131 }
4132
4133 /* release locks so we can do a devfs_clean */
4134 mutex_exit(HUBD_MUTEX(hubd));
4135
4136 /* delete cached dv_node's but drop locks first */
4137 ndi_devi_exit(hdip);
4138 ndi_devi_exit(rh_dip);
4139 ndi_devi_exit(ddi_get_parent(rh_dip));
4140
4141 (void) devfs_clean(rh_dip, NULL, 0);
4142
4143 /* now check if any children need onlining */
4144 if (online_child) {
4145 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4146 "hubd_hotplug_thread: onlining children");
4147
4148 (void) ndi_devi_online(hubd->h_dip, 0);
4149 }
4150
4151 /* now check if any disconnected devices need to be cleaned up */
4152 if (offline_child) {
4153 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4154 "hubd_hotplug_thread: scheduling cleanup");
4155
4156 hubd_schedule_cleanup(hubd->h_usba_device->usb_root_hub_dip);
4157 }
4158
4159 mutex_enter(HUBD_MUTEX(hubd));
4160
4161 /* now raise power on the children that have woken up */
4162 if (pwrup_child) {
4163 old_state = hubd->h_dev_state;
4164 hubd->h_dev_state = USB_DEV_HUB_CHILD_PWRLVL;
4165 for (port = 1; port <= nports; port++) {
4166 if (hubd->h_port_state[port] & HUBD_CHILD_RAISE_POWER) {
4167 dev_info_t *dip = hubd->h_children_dips[port];
4168
4169 mutex_exit(HUBD_MUTEX(hubd));
4170
4171 /* Get the device to full power */
4172 (void) pm_busy_component(dip, 0);
4173 (void) pm_raise_power(dip, 0,
4174 USB_DEV_OS_FULL_PWR);
4175 (void) pm_idle_component(dip, 0);
4176
4177 /* release the hold on the child */
4178 ndi_rele_devi(dip);
4179 mutex_enter(HUBD_MUTEX(hubd));
4180 hubd->h_port_state[port] &=
4181 ~HUBD_CHILD_RAISE_POWER;
4182 }
4183 }
4184 /*
4185 * make sure that we don't accidentally
4186 * over write the disconnect state
4187 */
4188 if (hubd->h_dev_state == USB_DEV_HUB_CHILD_PWRLVL) {
4189 hubd->h_dev_state = old_state;
4190 }
4191 }
4192
4193 /*
4194 * start polling can immediately kick off read callback
4195 * we need to set the h_hotplug_thread to 0 so that
4196 * the callback is not dropped
4197 *
4198 * if there is device during reset, still stop polling to avoid the
4199 * read callback interrupting the reset, the polling will be started
4200 * in hubd_reset_thread.
4201 */
4202 for (port = 1; port <= MAX_PORTS; port++) {
4203 if (hubd->h_reset_port[port]) {
4204
4205 break;
4206 }
4207 }
4208 if (port > MAX_PORTS) {
4209 hubd_start_polling(hubd, HUBD_ALWAYS_START_POLLING);
4210 }
4211
4212 /*
4213 * Earlier we would set the h_hotplug_thread = 0 before
4214 * polling was restarted so that
4215 * if there is any root hub status change interrupt, we can still kick
4216 * off the hotplug thread. This was valid when this interrupt was
4217 * delivered in hardware, and only ONE interrupt would be delivered.
4218 * Now that we poll on the root hub looking for status change in
4219 * software, this assignment is no longer required.
4220 */
4221 hubd->h_hotplug_thread--;
4222
4223 /* mark this device as idle */
4224 (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
4225
4226 cv_broadcast(&hubd->h_cv_hotplug_dev);
4227
4228 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4229 "hubd_hotplug_thread: exit");
4230
4231 mutex_exit(HUBD_MUTEX(hubd));
4232
4233 ndi_rele_devi(hdip);
4234 }
4235
4236
4237 /*
4238 * hubd_handle_port_connect:
4239 * Transition a port from Disabled to Enabled. Ensure that the
4240 * port is in the correct state before attempting to
4241 * access the device.
4242 */
4243 static int
hubd_handle_port_connect(hubd_t * hubd,usb_port_t port)4244 hubd_handle_port_connect(hubd_t *hubd, usb_port_t port)
4245 {
4246 int rval;
4247 int retry;
4248 long time_delay;
4249 long settling_time;
4250 uint16_t status;
4251 uint16_t change;
4252 usb_port_status_t speed;
4253 usb_addr_t hubd_usb_addr;
4254 usba_device_t *usba_device;
4255 usb_port_status_t port_status = 0;
4256 usb_port_status_t hub_port_status = 0;
4257
4258 /* Get the hub address and port status */
4259 usba_device = hubd->h_usba_device;
4260 mutex_enter(&usba_device->usb_mutex);
4261 hubd_usb_addr = usba_device->usb_addr;
4262 hub_port_status = usba_device->usb_port_status;
4263 mutex_exit(&usba_device->usb_mutex);
4264
4265 /*
4266 * If a device is connected, transition the
4267 * port from Disabled to the Enabled state.
4268 * The device will receive downstream packets
4269 * in the Enabled state.
4270 *
4271 * reset port and wait for the hub to report
4272 * completion
4273 */
4274 change = status = 0;
4275
4276 /*
4277 * According to section 9.1.2 of USB 2.0 spec, the host should
4278 * wait for atleast 100ms to allow completion of an insertion
4279 * process and for power at the device to become stable.
4280 * We wait for 200 ms
4281 */
4282 settling_time = drv_usectohz(hubd_device_delay / 5);
4283 mutex_exit(HUBD_MUTEX(hubd));
4284 delay(settling_time);
4285 mutex_enter(HUBD_MUTEX(hubd));
4286
4287 /* calculate 600 ms delay time */
4288 time_delay = (6 * drv_usectohz(hubd_device_delay)) / 10;
4289
4290 for (retry = 0; (hubd->h_dev_state == USB_DEV_ONLINE) &&
4291 (retry < hubd_retry_enumerate); retry++) {
4292 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4293 "resetting port%d, retry=%d", port, retry);
4294
4295 if ((rval = hubd_reset_port(hubd, port)) != USB_SUCCESS) {
4296 (void) hubd_determine_port_status(hubd,
4297 port, &status, &change, &speed, 0);
4298
4299 /* continue only if port is still connected */
4300 if (status & PORT_STATUS_CCS) {
4301 continue;
4302 }
4303
4304 /* carry on regardless */
4305 }
4306
4307 /*
4308 * according to USB 2.0 spec section 11.24.2.7.1.2
4309 * at the end of port reset, the hub enables the port.
4310 * But for some strange reasons, uhci port remains disabled.
4311 * And because the port remains disabled for the settling
4312 * time below, the device connected to the port gets wedged
4313 * - fails to enumerate (device not responding)
4314 * Hence, we enable it here immediately and later again after
4315 * the delay
4316 */
4317 (void) hubd_enable_port(hubd, port);
4318
4319 /* we skip this delay in the first iteration */
4320 if (retry) {
4321 /*
4322 * delay for device to signal disconnect/connect so
4323 * that hub properly recognizes the speed of the device
4324 */
4325 mutex_exit(HUBD_MUTEX(hubd));
4326 delay(settling_time);
4327 mutex_enter(HUBD_MUTEX(hubd));
4328
4329 /*
4330 * When a low speed device is connected to any port of
4331 * PPX it has to be explicitly enabled
4332 * Also, if device intentionally signals
4333 * disconnect/connect, it will disable the port.
4334 * So enable it again.
4335 */
4336 (void) hubd_enable_port(hubd, port);
4337 }
4338
4339 if ((rval = hubd_determine_port_status(hubd, port, &status,
4340 &change, &speed, 0)) != USB_SUCCESS) {
4341
4342 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4343 "getting status failed (%d)", rval);
4344
4345 (void) hubd_disable_port(hubd, port);
4346
4347 continue;
4348 }
4349
4350 if (status & PORT_STATUS_POCI) {
4351 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4352 "port %d overcurrent", port);
4353
4354 (void) hubd_disable_port(hubd, port);
4355
4356 /* ack changes */
4357 (void) hubd_determine_port_status(hubd,
4358 port, &status, &change, &speed, PORT_CHANGE_OCIC);
4359
4360 continue;
4361 }
4362
4363 /* is status really OK? */
4364 if ((status & PORT_STATUS_OK) != PORT_STATUS_OK) {
4365 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4366 "port %d status (0x%x) not OK on retry %d",
4367 port, status, retry);
4368
4369 /* check if we still have the connection */
4370 if (!(status & PORT_STATUS_CCS)) {
4371 /* lost connection, set exit condition */
4372 retry = hubd_retry_enumerate;
4373
4374 break;
4375 }
4376 } else {
4377 port_status = speed;
4378 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4379 "creating child port%d, status=0x%x "
4380 "port status=0x%x",
4381 port, status, port_status);
4382
4383 /*
4384 * if the child already exists, set addrs and config
4385 * to the device post connect event to the child
4386 */
4387 if (hubd->h_children_dips[port]) {
4388 /* set addrs to this device */
4389 rval = hubd_setdevaddr(hubd, port);
4390
4391 /*
4392 * This delay is important for the CATC hub
4393 * to enumerate. But, avoid delay in the first
4394 * iteration
4395 */
4396 if (retry) {
4397 mutex_exit(HUBD_MUTEX(hubd));
4398 delay(drv_usectohz(
4399 hubd_device_delay/100));
4400 mutex_enter(HUBD_MUTEX(hubd));
4401 }
4402
4403 if (rval == USB_SUCCESS) {
4404 /*
4405 * if the port is resetting, check if
4406 * device's descriptors have changed.
4407 */
4408 if ((hubd->h_reset_port[port]) &&
4409 (hubd_check_same_device(hubd,
4410 port) != USB_SUCCESS)) {
4411 retry = hubd_retry_enumerate;
4412
4413 break;
4414 }
4415
4416 /*
4417 * set the default config for
4418 * this device
4419 */
4420 hubd_setdevconfig(hubd, port);
4421
4422 /*
4423 * if we are doing Default reset, do
4424 * not post reconnect event since we
4425 * don't know where reset function is
4426 * called.
4427 */
4428 if (hubd->h_reset_port[port]) {
4429
4430 return (USB_SUCCESS);
4431 }
4432
4433 /*
4434 * indicate to the child that
4435 * it is online again
4436 */
4437 mutex_exit(HUBD_MUTEX(hubd));
4438 hubd_post_event(hubd, port,
4439 USBA_EVENT_TAG_HOT_INSERTION);
4440 mutex_enter(HUBD_MUTEX(hubd));
4441
4442 return (USB_SUCCESS);
4443 }
4444 } else {
4445 /*
4446 * We need to release access here
4447 * so that busctls on other ports can
4448 * continue and don't cause a deadlock
4449 * when busctl and removal of prom node
4450 * takes concurrently. This also ensures
4451 * busctls for attach of successfully
4452 * enumerated devices on other ports can
4453 * continue concurrently with the process
4454 * of enumerating the new devices. This
4455 * reduces the overall boot time of the system.
4456 */
4457 rval = hubd_create_child(hubd->h_dip,
4458 hubd,
4459 hubd->h_usba_device,
4460 port_status, port,
4461 retry);
4462 if (rval == USB_SUCCESS) {
4463 usba_update_hotplug_stats(hubd->h_dip,
4464 USBA_TOTAL_HOTPLUG_SUCCESS|
4465 USBA_HOTPLUG_SUCCESS);
4466 hubd->h_total_hotplug_success++;
4467
4468 if (retry > 0) {
4469 USB_DPRINTF_L2(
4470 DPRINT_MASK_HOTPLUG,
4471 hubd->h_log_handle,
4472 "device on port %d "
4473 "enumerated after %d %s",
4474 port, retry,
4475 (retry > 1) ? "retries" :
4476 "retry");
4477
4478 }
4479
4480 return (USB_SUCCESS);
4481 }
4482 }
4483 }
4484
4485 /* wait a while until it settles? */
4486 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4487 "disabling port %d again", port);
4488
4489 (void) hubd_disable_port(hubd, port);
4490 if (retry) {
4491 mutex_exit(HUBD_MUTEX(hubd));
4492 delay(time_delay);
4493 mutex_enter(HUBD_MUTEX(hubd));
4494 }
4495
4496 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4497 "retrying on port %d", port);
4498 }
4499
4500 if (retry >= hubd_retry_enumerate) {
4501 /*
4502 * If it is a High Speed Root Hub and connected device
4503 * Is a Low/Full Speed, it will be handled by USB 1.1
4504 * Host Controller. In this case, USB 2.0 Host Controller
4505 * will transfer the ownership of this port to USB 1.1
4506 * Host Controller. So don't display any error message on
4507 * the console. Note, this isn't the case for USB 3.x.
4508 */
4509 if ((hubd_usb_addr == ROOT_HUB_ADDR) &&
4510 (hub_port_status == USBA_HIGH_SPEED_DEV) &&
4511 (port_status != USBA_HIGH_SPEED_DEV)) {
4512 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
4513 hubd->h_log_handle,
4514 "hubd_handle_port_connect: Low/Full speed "
4515 "device is connected to High Speed root hub");
4516 } else {
4517 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
4518 hubd->h_log_handle,
4519 "Connecting device on port %d failed", port);
4520 }
4521
4522 (void) hubd_disable_port(hubd, port);
4523 usba_update_hotplug_stats(hubd->h_dip,
4524 USBA_TOTAL_HOTPLUG_FAILURE|USBA_HOTPLUG_FAILURE);
4525 hubd->h_total_hotplug_failure++;
4526
4527 /*
4528 * the port should be automagically
4529 * disabled but just in case, we do
4530 * it here
4531 */
4532 (void) hubd_disable_port(hubd, port);
4533
4534 /* ack all changes because we disabled this port */
4535 (void) hubd_determine_port_status(hubd,
4536 port, &status, &change, NULL, HUBD_ACK_ALL_CHANGES);
4537
4538 }
4539
4540 return (USB_FAILURE);
4541 }
4542
4543
4544 /*
4545 * hubd_get_hub_status:
4546 */
4547 static int
hubd_get_hub_status(hubd_t * hubd)4548 hubd_get_hub_status(hubd_t *hubd)
4549 {
4550 int rval;
4551 usb_cr_t completion_reason;
4552 usb_cb_flags_t cb_flags;
4553 uint16_t stword[2];
4554 uint16_t status;
4555 uint16_t change;
4556 usb_cfg_descr_t cfg_descr;
4557 size_t cfg_length;
4558 uchar_t *usb_cfg;
4559 uint8_t MaxPower;
4560 usb_port_t port;
4561
4562 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4563 "hubd_get_hub_status:");
4564
4565 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
4566
4567 if ((hubd_get_hub_status_words(hubd, stword)) != USB_SUCCESS) {
4568
4569 return (USB_FAILURE);
4570 }
4571 status = stword[0];
4572 change = stword[1];
4573
4574 mutex_exit(HUBD_MUTEX(hubd));
4575
4576 /* Obtain the raw configuration descriptor */
4577 usb_cfg = usb_get_raw_cfg_data(hubd->h_dip, &cfg_length);
4578
4579 /* get configuration descriptor */
4580 rval = usb_parse_cfg_descr(usb_cfg, cfg_length,
4581 &cfg_descr, USB_CFG_DESCR_SIZE);
4582
4583 if (rval != USB_CFG_DESCR_SIZE) {
4584
4585 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4586 "get hub configuration descriptor failed.");
4587
4588 mutex_enter(HUBD_MUTEX(hubd));
4589
4590 return (USB_FAILURE);
4591 } else {
4592 MaxPower = cfg_descr.bMaxPower;
4593 }
4594
4595 /* check if local power status changed. */
4596 if (change & C_HUB_LOCAL_POWER_STATUS) {
4597
4598 /*
4599 * local power has been lost, check the maximum
4600 * power consumption of current configuration.
4601 * see USB2.0 spec Table 11-12.
4602 */
4603 if (status & HUB_LOCAL_POWER_STATUS) {
4604
4605 if (MaxPower == 0) {
4606
4607 /*
4608 * Self-powered only hub. Because it could
4609 * not draw any power from USB bus.
4610 * It can't work well on this condition.
4611 */
4612 USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG,
4613 hubd->h_log_handle,
4614 "local power has been lost, "
4615 "please disconnect hub");
4616 } else {
4617
4618 /*
4619 * Bus-powered only or self/bus-powered hub.
4620 */
4621 USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG,
4622 hubd->h_log_handle,
4623 "local power has been lost,"
4624 "the hub could draw %d"
4625 " mA power from the USB bus.",
4626 2*MaxPower);
4627 }
4628
4629 }
4630
4631 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4632 "clearing feature C_HUB_LOCAL_POWER ");
4633
4634 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
4635 hubd->h_default_pipe,
4636 HUB_HANDLE_HUB_FEATURE_TYPE,
4637 USB_REQ_CLEAR_FEATURE,
4638 CFS_C_HUB_LOCAL_POWER,
4639 0,
4640 0,
4641 NULL, 0,
4642 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
4643 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
4644 hubd->h_log_handle,
4645 "clear feature C_HUB_LOCAL_POWER "
4646 "failed (%d 0x%x %d)",
4647 rval, completion_reason, cb_flags);
4648 }
4649
4650 }
4651
4652 if (change & C_HUB_OVER_CURRENT) {
4653
4654 if (status & HUB_OVER_CURRENT) {
4655
4656 if (usba_is_root_hub(hubd->h_dip)) {
4657 /*
4658 * The root hub should be automatically
4659 * recovered when over-current condition is
4660 * cleared. But there might be exception and
4661 * need user interaction to recover.
4662 */
4663 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
4664 hubd->h_log_handle,
4665 "Root hub over current condition, "
4666 "please check your system to clear the "
4667 "condition as soon as possible. And you "
4668 "may need to reboot the system to bring "
4669 "the root hub back to work if it cannot "
4670 "recover automatically");
4671 } else {
4672 /*
4673 * The driver would try to recover port power
4674 * on over current condition. When the recovery
4675 * fails, the user may still need to offline
4676 * this hub in order to recover.
4677 * The port power is automatically disabled,
4678 * so we won't see disconnects.
4679 */
4680 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
4681 hubd->h_log_handle,
4682 "Hub global over current condition, "
4683 "please disconnect the devices connected "
4684 "to the hub to clear the condition. And "
4685 "you may need to re-connect the hub if "
4686 "the ports do not work");
4687 }
4688 }
4689
4690 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4691 "clearing feature C_HUB_OVER_CURRENT");
4692
4693 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
4694 hubd->h_default_pipe,
4695 HUB_HANDLE_HUB_FEATURE_TYPE,
4696 USB_REQ_CLEAR_FEATURE,
4697 CFS_C_HUB_OVER_CURRENT,
4698 0,
4699 0,
4700 NULL, 0,
4701 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
4702 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
4703 hubd->h_log_handle,
4704 "clear feature C_HUB_OVER_CURRENT "
4705 "failed (%d 0x%x %d)",
4706 rval, completion_reason, cb_flags);
4707 }
4708
4709 /*
4710 * Try to recover all port power if they are turned off.
4711 * Don't do this for root hub, but rely on the root hub
4712 * to recover itself.
4713 */
4714 if (!usba_is_root_hub(hubd->h_dip)) {
4715
4716 mutex_enter(HUBD_MUTEX(hubd));
4717
4718 /*
4719 * Only check the power status of the 1st port
4720 * since all port power status should be the same.
4721 */
4722 (void) hubd_determine_port_status(hubd, 1, &status,
4723 &change, NULL, 0);
4724
4725 if (status & PORT_STATUS_PPS) {
4726 return (USB_SUCCESS);
4727 }
4728
4729 for (port = 1; port <= hubd->h_nports; port++) {
4730 (void) hubd_enable_port_power(hubd, port);
4731 }
4732
4733 mutex_exit(HUBD_MUTEX(hubd));
4734
4735 /*
4736 * Delay some time to avoid over-current event
4737 * to happen too frequently in some cases
4738 */
4739 delay(drv_usectohz(500000));
4740 }
4741 }
4742
4743 mutex_enter(HUBD_MUTEX(hubd));
4744
4745 return (USB_SUCCESS);
4746 }
4747
4748 /*
4749 * Convert a series of USB status requests from USB 2 and USB 3 into a single
4750 * uniform type. We separate out the speed into its own value from both USB 2
4751 * and USB 3 and from there we transform the status to look like a USB 2 one.
4752 */
4753 static void
hubd_status_uniform(hubd_t * hubd,usb_port_t port,uint16_t * status,usb_port_status_t * speed)4754 hubd_status_uniform(hubd_t *hubd, usb_port_t port, uint16_t *status,
4755 usb_port_status_t *speed)
4756 {
4757 uint16_t os = *status;
4758
4759 hubd->h_port_raw[port] = os;
4760
4761 if (hubd->h_usba_device->usb_port_status >= USBA_SUPER_SPEED_DEV) {
4762 /*
4763 * USB 3 devices are always at super speed when plugged into a
4764 * super speed hub. However, this is only true if we're talking
4765 * about actual hubs. This doesn't hold for the root hub, which
4766 * can support USB 3.x, USB 2.x, and USB 1.x devices operating
4767 * at different speeds. To handle this, the USB 3 HCD driver
4768 * (xhci) uses some of the extra status bits to stash the
4769 * current device's detected speed.
4770 */
4771 if (usba_is_root_hub(hubd->h_dip)) {
4772 if (speed != NULL) {
4773 *speed = (os & PORT_STATUS_SPMASK_SS) >>
4774 PORT_STATUS_SPSHIFT_SS;
4775 }
4776 } else {
4777 if (speed != NULL)
4778 *speed = USBA_SUPER_SPEED_DEV;
4779 }
4780
4781 if (os & PORT_STATUS_PPS_SS) {
4782 os &= ~PORT_STATUS_PPS_SS;
4783 os |= PORT_STATUS_PPS;
4784 *status = os;
4785 }
4786 } else {
4787 /*
4788 * For USB 2, the only thing we need to do is transform the
4789 * speed.
4790 */
4791 if (speed == NULL)
4792 return;
4793
4794 if (os & PORT_STATUS_HSDA)
4795 *speed = USBA_HIGH_SPEED_DEV;
4796 else if (os & PORT_STATUS_LSDA)
4797 *speed = USBA_LOW_SPEED_DEV;
4798 else
4799 *speed = USBA_FULL_SPEED_DEV;
4800 }
4801 }
4802
4803
4804 /*
4805 * Attempt to reset a port. This feels a bit more complicated than it should be
4806 * in part due to how HCD, change status notifications, and the hotplug thread
4807 * might interact. Basically we try to block port changes by using the
4808 * h_port_reset_wait which says we should get signalled rather than kicking off
4809 * the hotplug thread. We'll give this a shot for about 100ms at best.
4810 */
4811 static int
hubd_reset_port(hubd_t * hubd,usb_port_t port)4812 hubd_reset_port(hubd_t *hubd, usb_port_t port)
4813 {
4814 int rval;
4815 usb_cr_t completion_reason;
4816 usb_cb_flags_t cb_flags;
4817 usb_port_mask_t port_mask = 1 << port;
4818 mblk_t *data;
4819 uint16_t status;
4820 uint16_t change;
4821 clock_t delta;
4822 boolean_t first;
4823
4824 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
4825 "hubd_reset_port: port=%d", port);
4826
4827 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
4828
4829 hubd->h_port_reset_wait |= port_mask;
4830
4831 mutex_exit(HUBD_MUTEX(hubd));
4832
4833 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
4834 hubd->h_default_pipe,
4835 HUB_HANDLE_PORT_FEATURE_TYPE,
4836 USB_REQ_SET_FEATURE,
4837 CFS_PORT_RESET,
4838 port,
4839 0,
4840 NULL, 0,
4841 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
4842 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
4843 "reset port%d failed (%d 0x%x %d)",
4844 port, completion_reason, cb_flags, rval);
4845
4846 mutex_enter(HUBD_MUTEX(hubd));
4847
4848 return (USB_FAILURE);
4849 }
4850
4851 mutex_enter(HUBD_MUTEX(hubd));
4852
4853 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
4854 "waiting on cv for reset completion");
4855
4856 /*
4857 * wait for port status change event
4858 */
4859 delta = drv_usectohz(hubd_device_delay / 10);
4860
4861 first = B_TRUE;
4862 for (;;) {
4863 if (delta < 0) {
4864 rval = USB_FAILURE;
4865 break;
4866 }
4867
4868 if (first == B_FALSE)
4869 hubd->h_port_reset_wait |= port_mask;
4870 else
4871 first = B_FALSE;
4872
4873 hubd_start_polling(hubd, HUBD_ALWAYS_START_POLLING);
4874
4875 /*
4876 * Regardless of the status, we always check to see if the port
4877 * has been reset.
4878 */
4879 delta = cv_reltimedwait(&hubd->h_cv_reset_port,
4880 &hubd->h_mutex, delta, TR_CLOCK_TICK);
4881 if (delta < 0)
4882 hubd->h_port_reset_wait &= ~port_mask;
4883
4884 hubd_stop_polling(hubd);
4885
4886 data = NULL;
4887
4888 /* check status to determine whether reset completed */
4889 mutex_exit(HUBD_MUTEX(hubd));
4890 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
4891 hubd->h_default_pipe,
4892 HUB_GET_PORT_STATUS_TYPE,
4893 USB_REQ_GET_STATUS,
4894 0,
4895 port,
4896 GET_STATUS_LENGTH,
4897 &data, 0,
4898 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
4899 USB_DPRINTF_L2(DPRINT_MASK_PORT,
4900 hubd->h_log_handle,
4901 "get status port%d failed (%d 0x%x %d)",
4902 port, completion_reason, cb_flags, rval);
4903
4904 if (data) {
4905 freemsg(data);
4906 data = NULL;
4907 }
4908 mutex_enter(HUBD_MUTEX(hubd));
4909
4910 continue;
4911 }
4912
4913 status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr);
4914 change = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2);
4915
4916 freemsg(data);
4917
4918 hubd_status_uniform(hubd, port, &status, NULL);
4919
4920 /* continue only if port is still connected */
4921 if (!(status & PORT_STATUS_CCS)) {
4922
4923 /* lost connection, set exit condition */
4924 delta = -1;
4925
4926 mutex_enter(HUBD_MUTEX(hubd));
4927
4928 break;
4929 }
4930
4931 if (status & PORT_STATUS_PRS) {
4932 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
4933 "port%d reset active", port);
4934 mutex_enter(HUBD_MUTEX(hubd));
4935
4936 continue;
4937 } else {
4938 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
4939 "port%d reset inactive", port);
4940 }
4941
4942 if (change & PORT_CHANGE_PRSC) {
4943 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
4944 "clearing feature CFS_C_PORT_RESET");
4945
4946 if (usb_pipe_sync_ctrl_xfer(hubd->h_dip,
4947 hubd->h_default_pipe,
4948 HUB_HANDLE_PORT_FEATURE_TYPE,
4949 USB_REQ_CLEAR_FEATURE,
4950 CFS_C_PORT_RESET,
4951 port,
4952 0,
4953 NULL, 0,
4954 &completion_reason, &cb_flags, 0) != USB_SUCCESS) {
4955 USB_DPRINTF_L2(DPRINT_MASK_PORT,
4956 hubd->h_log_handle,
4957 "clear feature CFS_C_PORT_RESET"
4958 " port%d failed (%d 0x%x %d)",
4959 port, completion_reason, cb_flags, rval);
4960 }
4961 }
4962
4963 /*
4964 * In addition to a normal reset, a warm reset may have
4965 * happened. Acknowledge that as well.
4966 */
4967 if (change & PORT_CHANGE_BHPR) {
4968 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
4969 "clearing feature CFS_C_BH_PORT_RESET");
4970
4971 if (usb_pipe_sync_ctrl_xfer(hubd->h_dip,
4972 hubd->h_default_pipe,
4973 HUB_HANDLE_PORT_FEATURE_TYPE,
4974 USB_REQ_CLEAR_FEATURE,
4975 CFS_C_BH_PORT_RESET,
4976 port,
4977 0,
4978 NULL, 0,
4979 &completion_reason, &cb_flags, 0) != USB_SUCCESS) {
4980 USB_DPRINTF_L2(DPRINT_MASK_PORT,
4981 hubd->h_log_handle,
4982 "clear feature CFS_C_BH_PORT_RESET"
4983 " port%d failed (%d 0x%x %d)",
4984 port, completion_reason, cb_flags, rval);
4985 }
4986 }
4987
4988 rval = USB_SUCCESS;
4989 mutex_enter(HUBD_MUTEX(hubd));
4990
4991 break;
4992 }
4993
4994 return (rval);
4995 }
4996
4997
4998 /*
4999 * hubd_enable_port:
5000 * this may fail if the hub as been disconnected
5001 */
5002 static int
hubd_enable_port(hubd_t * hubd,usb_port_t port)5003 hubd_enable_port(hubd_t *hubd, usb_port_t port)
5004 {
5005 int rval;
5006 usb_cr_t completion_reason;
5007 usb_cb_flags_t cb_flags;
5008
5009 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5010 "hubd_enable_port: port=%d", port);
5011
5012 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
5013
5014 mutex_exit(HUBD_MUTEX(hubd));
5015
5016 /* Do not issue a SetFeature(PORT_ENABLE) on external hubs */
5017 if (!usba_is_root_hub(hubd->h_dip)) {
5018 mutex_enter(HUBD_MUTEX(hubd));
5019
5020 return (USB_SUCCESS);
5021 }
5022
5023 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5024 hubd->h_default_pipe,
5025 HUB_HANDLE_PORT_FEATURE_TYPE,
5026 USB_REQ_SET_FEATURE,
5027 CFS_PORT_ENABLE,
5028 port,
5029 0,
5030 NULL, 0,
5031 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
5032 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5033 "enable port%d failed (%d 0x%x %d)",
5034 port, completion_reason, cb_flags, rval);
5035 }
5036
5037 mutex_enter(HUBD_MUTEX(hubd));
5038
5039 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5040 "enabling port done");
5041
5042 return (rval);
5043 }
5044
5045
5046 /*
5047 * hubd_disable_port
5048 */
5049 static int
hubd_disable_port(hubd_t * hubd,usb_port_t port)5050 hubd_disable_port(hubd_t *hubd, usb_port_t port)
5051 {
5052 int rval;
5053 usb_cr_t completion_reason;
5054 usb_cb_flags_t cb_flags;
5055
5056 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5057 "hubd_disable_port: port=%d", port);
5058
5059 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
5060
5061 mutex_exit(HUBD_MUTEX(hubd));
5062
5063 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5064 hubd->h_default_pipe,
5065 HUB_HANDLE_PORT_FEATURE_TYPE,
5066 USB_REQ_CLEAR_FEATURE,
5067 CFS_PORT_ENABLE,
5068 port,
5069 0,
5070 NULL, 0,
5071 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
5072 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5073 "disable port%d failed (%d 0x%x %d)", port,
5074 completion_reason, cb_flags, rval);
5075 mutex_enter(HUBD_MUTEX(hubd));
5076
5077 return (USB_FAILURE);
5078 }
5079
5080 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5081 "clearing feature CFS_C_PORT_ENABLE");
5082
5083 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5084 hubd->h_default_pipe,
5085 HUB_HANDLE_PORT_FEATURE_TYPE,
5086 USB_REQ_CLEAR_FEATURE,
5087 CFS_C_PORT_ENABLE,
5088 port,
5089 0,
5090 NULL, 0,
5091 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
5092 USB_DPRINTF_L2(DPRINT_MASK_PORT,
5093 hubd->h_log_handle,
5094 "clear feature CFS_C_PORT_ENABLE port%d failed "
5095 "(%d 0x%x %d)",
5096 port, completion_reason, cb_flags, rval);
5097
5098 mutex_enter(HUBD_MUTEX(hubd));
5099
5100 return (USB_FAILURE);
5101 }
5102
5103 mutex_enter(HUBD_MUTEX(hubd));
5104
5105 return (USB_SUCCESS);
5106 }
5107
5108
5109 /*
5110 * hubd_determine_port_status:
5111 */
5112 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)5113 hubd_determine_port_status(hubd_t *hubd, usb_port_t port, uint16_t *status,
5114 uint16_t *change, usb_port_status_t *speed, uint_t ack_flag)
5115 {
5116 int rval;
5117 mblk_t *data = NULL;
5118 usb_cr_t completion_reason;
5119 usb_cb_flags_t cb_flags;
5120 uint16_t st, ch;
5121 usb_port_status_t sp;
5122
5123 if (status == NULL)
5124 status = &st;
5125 if (change == NULL)
5126 change = &ch;
5127 if (speed == NULL)
5128 speed = &sp;
5129
5130 *status = *change = 0;
5131 *speed = 0;
5132
5133 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5134 "hubd_determine_port_status: port=%d, state=0x%x ack=0x%x", port,
5135 hubd->h_port_state[port], ack_flag);
5136
5137 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
5138
5139 mutex_exit(HUBD_MUTEX(hubd));
5140
5141 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5142 hubd->h_default_pipe,
5143 HUB_GET_PORT_STATUS_TYPE,
5144 USB_REQ_GET_STATUS,
5145 0,
5146 port,
5147 GET_STATUS_LENGTH,
5148 &data, 0,
5149 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
5150 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5151 "port=%d get status failed (%d 0x%x %d)",
5152 port, completion_reason, cb_flags, rval);
5153
5154 if (data) {
5155 freemsg(data);
5156 }
5157
5158 mutex_enter(HUBD_MUTEX(hubd));
5159
5160 return (rval);
5161 }
5162
5163 mutex_enter(HUBD_MUTEX(hubd));
5164 if (MBLKL(data) != GET_STATUS_LENGTH) {
5165 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5166 "port %d: length incorrect %ld",
5167 port, MBLKL(data));
5168 freemsg(data);
5169
5170 return (rval);
5171 }
5172
5173
5174 *status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr);
5175 *change = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2);
5176 hubd_status_uniform(hubd, port, status, speed);
5177
5178 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5179 "port%d status=0x%x, change=0x%x", port, *status, *change);
5180
5181 freemsg(data);
5182
5183 if (*status & PORT_STATUS_CCS) {
5184 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5185 "port%d connected", port);
5186
5187 hubd->h_port_state[port] |= (PORT_STATUS_CCS & ack_flag);
5188 } else {
5189 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5190 "port%d disconnected", port);
5191
5192 hubd->h_port_state[port] &= ~(PORT_STATUS_CCS & ack_flag);
5193 }
5194
5195 if (*status & PORT_STATUS_PES) {
5196 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5197 "port%d enabled", port);
5198
5199 hubd->h_port_state[port] |= (PORT_STATUS_PES & ack_flag);
5200 } else {
5201 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5202 "port%d disabled", port);
5203
5204 hubd->h_port_state[port] &= ~(PORT_STATUS_PES & ack_flag);
5205 }
5206
5207 if (*status & PORT_STATUS_PSS) {
5208 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5209 "port%d suspended", port);
5210
5211 hubd->h_port_state[port] |= (PORT_STATUS_PSS & ack_flag);
5212 } else {
5213 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5214 "port%d not suspended", port);
5215
5216 hubd->h_port_state[port] &= ~(PORT_STATUS_PSS & ack_flag);
5217 }
5218
5219 if (*change & PORT_CHANGE_PRSC) {
5220 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5221 "port%d reset completed", port);
5222
5223 hubd->h_port_state[port] |= (PORT_CHANGE_PRSC & ack_flag);
5224 } else {
5225
5226 hubd->h_port_state[port] &= ~(PORT_CHANGE_PRSC & ack_flag);
5227 }
5228
5229 if (*status & PORT_STATUS_POCI) {
5230 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5231 "port%d overcurrent!", port);
5232
5233 hubd->h_port_state[port] |= (PORT_STATUS_POCI & ack_flag);
5234 } else {
5235
5236 hubd->h_port_state[port] &= ~(PORT_STATUS_POCI & ack_flag);
5237 }
5238
5239 if (*status & PORT_STATUS_PRS) {
5240 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5241 "port%d reset active", port);
5242
5243 hubd->h_port_state[port] |= (PORT_STATUS_PRS & ack_flag);
5244 } else {
5245 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5246 "port%d reset inactive", port);
5247
5248 hubd->h_port_state[port] &= ~(PORT_STATUS_PRS & ack_flag);
5249 }
5250 if (*status & PORT_STATUS_PPS) {
5251 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5252 "port%d power on", port);
5253
5254 hubd->h_port_state[port] |= (PORT_STATUS_PPS & ack_flag);
5255 } else {
5256 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5257 "port%d power off", port);
5258
5259 hubd->h_port_state[port] &= ~(PORT_STATUS_PPS & ack_flag);
5260 }
5261
5262 /*
5263 * Acknowledge connection, enable, reset status
5264 */
5265 if (ack_flag) {
5266 mutex_exit(HUBD_MUTEX(hubd));
5267 if (*change & PORT_CHANGE_CSC & ack_flag) {
5268 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5269 "clearing feature CFS_C_PORT_CONNECTION");
5270 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5271 hubd->h_default_pipe,
5272 HUB_HANDLE_PORT_FEATURE_TYPE,
5273 USB_REQ_CLEAR_FEATURE,
5274 CFS_C_PORT_CONNECTION,
5275 port,
5276 0, NULL, 0,
5277 &completion_reason, &cb_flags, 0)) !=
5278 USB_SUCCESS) {
5279 USB_DPRINTF_L2(DPRINT_MASK_PORT,
5280 hubd->h_log_handle,
5281 "clear feature CFS_C_PORT_CONNECTION"
5282 " port%d failed (%d 0x%x %d)",
5283 port, completion_reason, cb_flags, rval);
5284 }
5285 }
5286 if (*change & PORT_CHANGE_PESC & ack_flag) {
5287 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5288 "clearing feature CFS_C_PORT_ENABLE");
5289 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5290 hubd->h_default_pipe,
5291 HUB_HANDLE_PORT_FEATURE_TYPE,
5292 USB_REQ_CLEAR_FEATURE,
5293 CFS_C_PORT_ENABLE,
5294 port,
5295 0, NULL, 0,
5296 &completion_reason, &cb_flags, 0)) !=
5297 USB_SUCCESS) {
5298 USB_DPRINTF_L2(DPRINT_MASK_PORT,
5299 hubd->h_log_handle,
5300 "clear feature CFS_C_PORT_ENABLE"
5301 " port%d failed (%d 0x%x %d)",
5302 port, completion_reason, cb_flags, rval);
5303 }
5304 }
5305 if (*change & PORT_CHANGE_PSSC & ack_flag) {
5306 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5307 "clearing feature CFS_C_PORT_SUSPEND");
5308
5309 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5310 hubd->h_default_pipe,
5311 HUB_HANDLE_PORT_FEATURE_TYPE,
5312 USB_REQ_CLEAR_FEATURE,
5313 CFS_C_PORT_SUSPEND,
5314 port,
5315 0, NULL, 0,
5316 &completion_reason, &cb_flags, 0)) !=
5317 USB_SUCCESS) {
5318 USB_DPRINTF_L2(DPRINT_MASK_PORT,
5319 hubd->h_log_handle,
5320 "clear feature CFS_C_PORT_SUSPEND"
5321 " port%d failed (%d 0x%x %d)",
5322 port, completion_reason, cb_flags, rval);
5323 }
5324 }
5325 if (*change & PORT_CHANGE_OCIC & ack_flag) {
5326 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5327 "clearing feature CFS_C_PORT_OVER_CURRENT");
5328
5329 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5330 hubd->h_default_pipe,
5331 HUB_HANDLE_PORT_FEATURE_TYPE,
5332 USB_REQ_CLEAR_FEATURE,
5333 CFS_C_PORT_OVER_CURRENT,
5334 port,
5335 0, NULL, 0,
5336 &completion_reason, &cb_flags, 0)) !=
5337 USB_SUCCESS) {
5338 USB_DPRINTF_L2(DPRINT_MASK_PORT,
5339 hubd->h_log_handle,
5340 "clear feature CFS_C_PORT_OVER_CURRENT"
5341 " port%d failed (%d 0x%x %d)",
5342 port, completion_reason, cb_flags, rval);
5343 }
5344 }
5345 if (*change & PORT_CHANGE_PRSC & ack_flag) {
5346 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5347 "clearing feature CFS_C_PORT_RESET");
5348 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5349 hubd->h_default_pipe,
5350 HUB_HANDLE_PORT_FEATURE_TYPE,
5351 USB_REQ_CLEAR_FEATURE,
5352 CFS_C_PORT_RESET,
5353 port,
5354 0, NULL, 0,
5355 &completion_reason, &cb_flags, 0)) !=
5356 USB_SUCCESS) {
5357 USB_DPRINTF_L2(DPRINT_MASK_PORT,
5358 hubd->h_log_handle,
5359 "clear feature CFS_C_PORT_RESET"
5360 " port%d failed (%d 0x%x %d)",
5361 port, completion_reason, cb_flags, rval);
5362 }
5363 }
5364 if (*change & PORT_CHANGE_BHPR & ack_flag) {
5365 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5366 "clearing feature CFS_C_BH_PORT_RESET");
5367 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5368 hubd->h_default_pipe,
5369 HUB_HANDLE_PORT_FEATURE_TYPE,
5370 USB_REQ_CLEAR_FEATURE,
5371 CFS_C_BH_PORT_RESET,
5372 port,
5373 0, NULL, 0,
5374 &completion_reason, &cb_flags, 0)) !=
5375 USB_SUCCESS) {
5376 USB_DPRINTF_L2(DPRINT_MASK_PORT,
5377 hubd->h_log_handle,
5378 "clear feature CFS_C_BH_PORT_RESET"
5379 " port%d failed (%d 0x%x %d)",
5380 port, completion_reason, cb_flags, rval);
5381 }
5382 }
5383 if (*change & PORT_CHANGE_PLSC & ack_flag) {
5384 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5385 "clearing feature CFS_C_PORT_LINK_STATE");
5386 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5387 hubd->h_default_pipe,
5388 HUB_HANDLE_PORT_FEATURE_TYPE,
5389 USB_REQ_CLEAR_FEATURE,
5390 CFS_C_PORT_LINK_STATE,
5391 port,
5392 0, NULL, 0,
5393 &completion_reason, &cb_flags, 0)) !=
5394 USB_SUCCESS) {
5395 USB_DPRINTF_L2(DPRINT_MASK_PORT,
5396 hubd->h_log_handle,
5397 "clear feature CFS_C_PORT_LINK_STATE"
5398 " port%d failed (%d 0x%x %d)",
5399 port, completion_reason, cb_flags, rval);
5400 }
5401 }
5402 if (*change & PORT_CHANGE_PCE & ack_flag) {
5403 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5404 "clearing feature CFS_C_PORT_CONFIG_ERROR");
5405 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5406 hubd->h_default_pipe,
5407 HUB_HANDLE_PORT_FEATURE_TYPE,
5408 USB_REQ_CLEAR_FEATURE,
5409 CFS_C_PORT_CONFIG_ERROR,
5410 port,
5411 0, NULL, 0,
5412 &completion_reason, &cb_flags, 0)) !=
5413 USB_SUCCESS) {
5414 USB_DPRINTF_L2(DPRINT_MASK_PORT,
5415 hubd->h_log_handle,
5416 "clear feature CFS_C_PORT_CONFIG_ERROR"
5417 " port%d failed (%d 0x%x %d)",
5418 port, completion_reason, cb_flags, rval);
5419 }
5420 }
5421 mutex_enter(HUBD_MUTEX(hubd));
5422 }
5423
5424 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5425 "new port%d state 0x%x", port, hubd->h_port_state[port]);
5426
5427
5428 return (USB_SUCCESS);
5429 }
5430
5431
5432 /*
5433 * hubd_recover_disabled_port
5434 * if the port got disabled because of an error
5435 * enable it. If hub doesn't suport enable port,
5436 * reset the port to bring the device to life again
5437 */
5438 static int
hubd_recover_disabled_port(hubd_t * hubd,usb_port_t port)5439 hubd_recover_disabled_port(hubd_t *hubd, usb_port_t port)
5440 {
5441 uint16_t status;
5442 uint16_t change;
5443 int rval = USB_FAILURE;
5444
5445 /* first try enabling the port */
5446 (void) hubd_enable_port(hubd, port);
5447
5448 /* read the port status */
5449 (void) hubd_determine_port_status(hubd, port, &status, &change, NULL,
5450 PORT_CHANGE_PESC);
5451
5452 if (status & PORT_STATUS_PES) {
5453 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5454 "Port%d now Enabled", port);
5455 } else if (status & PORT_STATUS_CCS) {
5456 /* first post a disconnect event to the child */
5457 mutex_exit(HUBD_MUTEX(hubd));
5458 hubd_post_event(hubd, port, USBA_EVENT_TAG_HOT_REMOVAL);
5459 mutex_enter(HUBD_MUTEX(hubd));
5460
5461 /* then reset the port and recover the device */
5462 rval = hubd_handle_port_connect(hubd, port);
5463
5464 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5465 "Port%d now Enabled by force", port);
5466 }
5467
5468 return (rval);
5469 }
5470
5471
5472 /*
5473 * hubd_enable_all_port_power:
5474 */
5475 static int
hubd_enable_all_port_power(hubd_t * hubd)5476 hubd_enable_all_port_power(hubd_t *hubd)
5477 {
5478 int wait;
5479 usb_port_t port;
5480 uint_t retry;
5481 uint16_t status;
5482 uint16_t change;
5483
5484 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5485 "hubd_enable_all_port_power");
5486
5487 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
5488
5489 /*
5490 * According to section 11.11 of USB, for hubs with no power
5491 * switches, bPwrOn2PwrGood is zero. But we wait for some
5492 * arbitrary time to enable power to become stable.
5493 *
5494 * If an hub supports port power switching, we need to wait
5495 * at least 20ms before accessing corresponding usb port. Note,
5496 * this member is stored in the h_power_good member.
5497 */
5498 if ((hubd->h_hub_chars & HUB_CHARS_NO_POWER_SWITCHING) ||
5499 (hubd->h_power_good == 0)) {
5500 wait = hubd_device_delay / 10;
5501 } else {
5502 wait = max(HUB_DEFAULT_POPG,
5503 hubd->h_power_good) * 2 * 1000;
5504 }
5505
5506 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5507 "hubd_enable_all_port_power: popg=%d wait=%d",
5508 hubd->h_power_good, wait);
5509
5510 /*
5511 * Enable power per port. we ignore gang power and power mask
5512 * and always enable all ports one by one.
5513 */
5514 for (port = 1; port <= hubd->h_nports; port++) {
5515 /*
5516 * Transition the port from the Powered Off to the
5517 * Disconnected state by supplying power to the port.
5518 */
5519 USB_DPRINTF_L4(DPRINT_MASK_PORT,
5520 hubd->h_log_handle,
5521 "hubd_enable_all_port_power: power port=%d", port);
5522
5523 (void) hubd_enable_port_power(hubd, port);
5524 }
5525
5526 mutex_exit(HUBD_MUTEX(hubd));
5527 delay(drv_usectohz(wait));
5528 mutex_enter(HUBD_MUTEX(hubd));
5529
5530 /* For retry if any, use some extra delay */
5531 wait = max(wait, hubd_device_delay / 10);
5532
5533 /* Check each port power status for a given usb hub */
5534 for (port = 1; port <= hubd->h_nports; port++) {
5535
5536 /* Get port status */
5537 (void) hubd_determine_port_status(hubd, port,
5538 &status, &change, NULL, 0);
5539
5540 for (retry = 0; ((!(status & PORT_STATUS_PPS)) &&
5541 (retry < HUBD_PORT_RETRY)); retry++) {
5542
5543 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5544 "Retry is in progress %d: port %d status %d",
5545 retry, port, status);
5546
5547 (void) hubd_enable_port_power(hubd, port);
5548
5549 mutex_exit(HUBD_MUTEX(hubd));
5550 delay(drv_usectohz(wait));
5551 mutex_enter(HUBD_MUTEX(hubd));
5552
5553 /* Get port status */
5554 (void) hubd_determine_port_status(hubd, port,
5555 &status, &change, NULL, 0);
5556 }
5557
5558 /* Print warning message if port has no power */
5559 if (!(status & PORT_STATUS_PPS)) {
5560
5561 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5562 "hubd_enable_all_port_power: port %d power-on "
5563 "failed, port status 0x%x", port, status);
5564 }
5565 }
5566
5567 return (USB_SUCCESS);
5568 }
5569
5570
5571 /*
5572 * hubd_enable_port_power:
5573 * enable individual port power
5574 */
5575 static int
hubd_enable_port_power(hubd_t * hubd,usb_port_t port)5576 hubd_enable_port_power(hubd_t *hubd, usb_port_t port)
5577 {
5578 int rval;
5579 usb_cr_t completion_reason;
5580 usb_cb_flags_t cb_flags;
5581
5582 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5583 "hubd_enable_port_power: port=%d", port);
5584
5585 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
5586 ASSERT(hubd->h_default_pipe != 0);
5587
5588 mutex_exit(HUBD_MUTEX(hubd));
5589
5590 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5591 hubd->h_default_pipe,
5592 HUB_HANDLE_PORT_FEATURE_TYPE,
5593 USB_REQ_SET_FEATURE,
5594 CFS_PORT_POWER,
5595 port,
5596 0, NULL, 0,
5597 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
5598 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5599 "set port power failed (%d 0x%x %d)",
5600 completion_reason, cb_flags, rval);
5601 mutex_enter(HUBD_MUTEX(hubd));
5602
5603 return (USB_FAILURE);
5604 } else {
5605 mutex_enter(HUBD_MUTEX(hubd));
5606 hubd->h_port_state[port] |= PORT_STATUS_PPS;
5607
5608 return (USB_SUCCESS);
5609 }
5610 }
5611
5612
5613 /*
5614 * hubd_disable_all_port_power:
5615 */
5616 static int
hubd_disable_all_port_power(hubd_t * hubd)5617 hubd_disable_all_port_power(hubd_t *hubd)
5618 {
5619 usb_port_t port;
5620
5621 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5622 "hubd_disable_all_port_power");
5623
5624 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
5625
5626 /*
5627 * disable power per port, ignore gang power and power mask
5628 */
5629 for (port = 1; port <= hubd->h_nports; port++) {
5630 (void) hubd_disable_port_power(hubd, port);
5631 }
5632
5633 return (USB_SUCCESS);
5634 }
5635
5636
5637 /*
5638 * hubd_disable_port_power:
5639 * disable individual port power
5640 */
5641 static int
hubd_disable_port_power(hubd_t * hubd,usb_port_t port)5642 hubd_disable_port_power(hubd_t *hubd, usb_port_t port)
5643 {
5644 int rval;
5645 usb_cr_t completion_reason;
5646 usb_cb_flags_t cb_flags;
5647
5648 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5649 "hubd_disable_port_power: port=%d", port);
5650
5651 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
5652
5653 mutex_exit(HUBD_MUTEX(hubd));
5654
5655 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5656 hubd->h_default_pipe,
5657 HUB_HANDLE_PORT_FEATURE_TYPE,
5658 USB_REQ_CLEAR_FEATURE,
5659 CFS_PORT_POWER,
5660 port,
5661 0, NULL, 0,
5662 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
5663 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5664 "clearing port%d power failed (%d 0x%x %d)",
5665 port, completion_reason, cb_flags, rval);
5666
5667 mutex_enter(HUBD_MUTEX(hubd));
5668
5669 return (USB_FAILURE);
5670 } else {
5671
5672 mutex_enter(HUBD_MUTEX(hubd));
5673 ASSERT(completion_reason == 0);
5674 hubd->h_port_state[port] &= ~PORT_STATUS_PPS;
5675
5676 return (USB_SUCCESS);
5677 }
5678 }
5679
5680
5681 /*
5682 * Search the database of user preferences and find out the preferred
5683 * configuration for this new device
5684 */
5685 int
hubd_select_device_configuration(hubd_t * hubd,usb_port_t port,dev_info_t * child_dip,usba_device_t * child_ud)5686 hubd_select_device_configuration(hubd_t *hubd, usb_port_t port,
5687 dev_info_t *child_dip, usba_device_t *child_ud)
5688 {
5689 char *pathname = NULL;
5690 char *tmp_path = NULL;
5691 int user_conf;
5692 int pathlen;
5693 usb_dev_descr_t *usbdev_ptr;
5694 usba_configrec_t *user_pref;
5695
5696 mutex_enter(&child_ud->usb_mutex);
5697 usbdev_ptr = child_ud->usb_dev_descr;
5698 mutex_exit(&child_ud->usb_mutex);
5699
5700 /* try to get pathname for this device */
5701 tmp_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
5702 (void) ddi_pathname(child_dip, tmp_path);
5703
5704 pathlen = strlen(tmp_path) + 32;
5705 pathname = kmem_zalloc(pathlen, KM_SLEEP);
5706
5707 /*
5708 * We haven't initialized the node and it doesn't have an address
5709 * yet. Append port number to the physical pathname
5710 */
5711 (void) sprintf(pathname, "%s@%d", tmp_path, port);
5712
5713 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5714 "hubd_select_device_configuration: Device=%s\n\t"
5715 "Child path=%s",
5716 usba_get_mfg_prod_sn_str(child_dip, tmp_path, MAXPATHLEN),
5717 pathname);
5718 kmem_free(tmp_path, MAXPATHLEN);
5719
5720
5721 /* database search for user preferences */
5722 user_pref = usba_devdb_get_user_preferences(usbdev_ptr->idVendor,
5723 usbdev_ptr->idProduct, child_ud->usb_serialno_str, pathname);
5724
5725 if (user_pref) {
5726 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5727 "hubd_select_device_configuration: "
5728 "usba_devdb_get_user_preferences "
5729 "return user_conf=%d\npreferred driver=%s path=%s",
5730 user_pref->cfg_index, user_pref->driver,
5731 user_pref->pathname);
5732
5733 user_conf = user_pref->cfg_index;
5734
5735 if (user_pref->driver) {
5736 mutex_enter(&child_ud->usb_mutex);
5737 child_ud->usb_preferred_driver = user_pref->driver;
5738 mutex_exit(&child_ud->usb_mutex);
5739 }
5740 } else {
5741 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5742 "hubd_select_device_configuration: No match found");
5743
5744 /* select default configuration for this device */
5745 user_conf = USBA_DEV_CONFIG_INDEX_UNDEFINED;
5746 }
5747 kmem_free(pathname, pathlen);
5748
5749 /* if the device has just one configuration, set default value */
5750 if (usbdev_ptr->bNumConfigurations == 1) {
5751 user_conf = USB_DEV_DEFAULT_CONFIG_INDEX;
5752 }
5753
5754 return (user_conf);
5755 }
5756
5757
5758 /*
5759 * Retrieves config cloud for this configuration
5760 */
5761 int
hubd_get_this_config_cloud(hubd_t * hubd,dev_info_t * dip,usba_device_t * child_ud,uint16_t conf_index)5762 hubd_get_this_config_cloud(hubd_t *hubd, dev_info_t *dip,
5763 usba_device_t *child_ud, uint16_t conf_index)
5764 {
5765 usb_cfg_descr_t *confdescr;
5766 mblk_t *pdata = NULL;
5767 int rval;
5768 size_t size;
5769 char *tmpbuf;
5770 usb_cr_t completion_reason;
5771 usb_cb_flags_t cb_flags;
5772 usb_pipe_handle_t def_ph;
5773
5774 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5775 "hubd_get_this_config_cloud: conf_index=%d", conf_index);
5776
5777
5778 /* alloc temporary space for config descriptor */
5779 confdescr = (usb_cfg_descr_t *)kmem_zalloc(USB_CFG_DESCR_SIZE,
5780 KM_SLEEP);
5781
5782 /* alloc temporary space for string descriptor */
5783 tmpbuf = kmem_zalloc(USB_MAXSTRINGLEN, KM_SLEEP);
5784
5785 def_ph = usba_get_dflt_pipe_handle(dip);
5786
5787 if ((rval = usb_pipe_sync_ctrl_xfer(dip, def_ph,
5788 USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
5789 USB_REQ_GET_DESCR,
5790 USB_DESCR_TYPE_SETUP_CFG | conf_index,
5791 0,
5792 USB_CFG_DESCR_SIZE,
5793 &pdata,
5794 0,
5795 &completion_reason,
5796 &cb_flags,
5797 0)) == USB_SUCCESS) {
5798
5799 /* this must be true since we didn't allow data underruns */
5800 if (MBLKL(pdata) != USB_CFG_DESCR_SIZE) {
5801 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5802 "device returned incorrect configuration "
5803 "descriptor size.");
5804
5805 rval = USB_FAILURE;
5806 goto done;
5807 }
5808
5809 /*
5810 * Parse the configuration descriptor
5811 */
5812 size = usb_parse_cfg_descr(pdata->b_rptr,
5813 MBLKL(pdata), confdescr,
5814 USB_CFG_DESCR_SIZE);
5815
5816 /* if parse cfg descr error, it should return failure */
5817 if (size == USB_PARSE_ERROR) {
5818
5819 if (pdata->b_rptr[1] != USB_DESCR_TYPE_CFG) {
5820 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
5821 hubd->h_log_handle,
5822 "device returned incorrect "
5823 "configuration descriptor type.");
5824 }
5825 rval = USB_FAILURE;
5826 goto done;
5827 }
5828
5829 if (confdescr->wTotalLength < USB_CFG_DESCR_SIZE) {
5830 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
5831 hubd->h_log_handle,
5832 "device returned incorrect "
5833 "configuration descriptor size.");
5834
5835 rval = USB_FAILURE;
5836 goto done;
5837 }
5838
5839 freemsg(pdata);
5840 pdata = NULL;
5841
5842 /* Now fetch the complete config cloud */
5843 if ((rval = usb_pipe_sync_ctrl_xfer(dip, def_ph,
5844 USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
5845 USB_REQ_GET_DESCR,
5846 USB_DESCR_TYPE_SETUP_CFG | conf_index,
5847 0,
5848 confdescr->wTotalLength,
5849 &pdata,
5850 0,
5851 &completion_reason,
5852 &cb_flags,
5853 0)) == USB_SUCCESS) {
5854
5855 if (MBLKL(pdata) !=
5856 confdescr->wTotalLength) {
5857
5858 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
5859 hubd->h_log_handle,
5860 "device returned incorrect "
5861 "configuration descriptor.");
5862
5863 rval = USB_FAILURE;
5864 goto done;
5865 }
5866
5867 /*
5868 * copy config descriptor into usba_device
5869 */
5870 mutex_enter(&child_ud->usb_mutex);
5871 child_ud->usb_cfg_array[conf_index] =
5872 kmem_alloc(confdescr->wTotalLength, KM_SLEEP);
5873 child_ud->usb_cfg_array_len[conf_index] =
5874 confdescr->wTotalLength;
5875 bcopy((caddr_t)pdata->b_rptr,
5876 (caddr_t)child_ud->usb_cfg_array[conf_index],
5877 confdescr->wTotalLength);
5878 mutex_exit(&child_ud->usb_mutex);
5879
5880 /*
5881 * retrieve string descriptor describing this
5882 * configuration
5883 */
5884 if (confdescr->iConfiguration) {
5885
5886 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG,
5887 hubd->h_log_handle,
5888 "Get conf str descr for config_index=%d",
5889 conf_index);
5890
5891 /*
5892 * Now fetch the string descriptor describing
5893 * this configuration
5894 */
5895 if ((rval = usb_get_string_descr(dip,
5896 USB_LANG_ID, confdescr->iConfiguration,
5897 tmpbuf, USB_MAXSTRINGLEN)) ==
5898 USB_SUCCESS) {
5899 size = strlen(tmpbuf);
5900 if (size > 0) {
5901 child_ud->usb_cfg_str_descr
5902 [conf_index] = (char *)
5903 kmem_zalloc(size + 1,
5904 KM_SLEEP);
5905 (void) strcpy(
5906 child_ud->usb_cfg_str_descr
5907 [conf_index], tmpbuf);
5908 }
5909 } else {
5910 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
5911 hubd->h_log_handle,
5912 "hubd_get_this_config_cloud: "
5913 "getting config string (%d) "
5914 "failed",
5915 confdescr->iConfiguration);
5916
5917 /* ignore this error */
5918 rval = USB_SUCCESS;
5919 }
5920 }
5921 }
5922 }
5923
5924 done:
5925 if (rval != USB_SUCCESS) {
5926 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5927 "hubd_get_this_config_cloud: "
5928 "error in retrieving config descriptor for "
5929 "config index=%d rval=%d cr=%d",
5930 conf_index, rval, completion_reason);
5931 }
5932
5933 if (pdata) {
5934 freemsg(pdata);
5935 pdata = NULL;
5936 }
5937
5938 kmem_free(confdescr, USB_CFG_DESCR_SIZE);
5939 kmem_free(tmpbuf, USB_MAXSTRINGLEN);
5940
5941 return (rval);
5942 }
5943
5944
5945 /*
5946 * Retrieves the entire config cloud for all configurations of the device
5947 */
5948 int
hubd_get_all_device_config_cloud(hubd_t * hubd,dev_info_t * dip,usba_device_t * child_ud)5949 hubd_get_all_device_config_cloud(hubd_t *hubd, dev_info_t *dip,
5950 usba_device_t *child_ud)
5951 {
5952 int rval = USB_SUCCESS;
5953 int ncfgs;
5954 uint16_t size;
5955 uint16_t conf_index;
5956 uchar_t **cfg_array;
5957 uint16_t *cfg_array_len;
5958 char **str_descr;
5959
5960 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5961 "hubd_get_all_device_config_cloud: Start");
5962
5963 /* alloc pointer array for conf. descriptors */
5964 mutex_enter(&child_ud->usb_mutex);
5965 ncfgs = child_ud->usb_n_cfgs;
5966 mutex_exit(&child_ud->usb_mutex);
5967
5968 size = sizeof (uchar_t *) * ncfgs;
5969 cfg_array = kmem_zalloc(size, KM_SLEEP);
5970 cfg_array_len = kmem_zalloc(ncfgs * sizeof (uint16_t), KM_SLEEP);
5971 str_descr = kmem_zalloc(size, KM_SLEEP);
5972
5973 mutex_enter(&child_ud->usb_mutex);
5974 child_ud->usb_cfg_array = cfg_array;
5975 child_ud->usb_cfg_array_len = cfg_array_len;
5976 child_ud->usb_cfg_array_length = size;
5977 child_ud->usb_cfg_array_len_length = ncfgs * sizeof (uint16_t);
5978 child_ud->usb_cfg_str_descr = str_descr;
5979 mutex_exit(&child_ud->usb_mutex);
5980
5981 /* Get configuration descriptor for each configuration */
5982 for (conf_index = 0; (conf_index < ncfgs) &&
5983 (rval == USB_SUCCESS); conf_index++) {
5984
5985 rval = hubd_get_this_config_cloud(hubd, dip, child_ud,
5986 conf_index);
5987 }
5988
5989 return (rval);
5990 }
5991
5992
5993 /*
5994 * hubd_ready_device:
5995 * Update the usba_device structure
5996 * Set the given configuration
5997 * Prepares the device node for driver to online. If an existing
5998 * OBP node is found, it will switch to the OBP node.
5999 */
6000 dev_info_t *
hubd_ready_device(hubd_t * hubd,dev_info_t * child_dip,usba_device_t * child_ud,uint_t config_index)6001 hubd_ready_device(hubd_t *hubd, dev_info_t *child_dip, usba_device_t *child_ud,
6002 uint_t config_index)
6003 {
6004 usb_cr_t completion_reason;
6005 usb_cb_flags_t cb_flags;
6006 size_t size;
6007 usb_cfg_descr_t config_descriptor;
6008 usb_pipe_handle_t def_ph;
6009 usba_pipe_handle_data_t *ph;
6010
6011 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6012 "hubd_ready_device: dip=0x%p, user_conf_index=%d",
6013 (void *)child_dip, config_index);
6014
6015 size = usb_parse_cfg_descr(
6016 child_ud->usb_cfg_array[config_index], USB_CFG_DESCR_SIZE,
6017 &config_descriptor, USB_CFG_DESCR_SIZE);
6018 ASSERT(size == USB_CFG_DESCR_SIZE);
6019
6020 def_ph = usba_get_dflt_pipe_handle(child_dip);
6021
6022 /* Set the configuration */
6023 (void) usb_pipe_sync_ctrl_xfer(child_dip, def_ph,
6024 USB_DEV_REQ_HOST_TO_DEV,
6025 USB_REQ_SET_CFG, /* bRequest */
6026 config_descriptor.bConfigurationValue, /* wValue */
6027 0, /* wIndex */
6028 0, /* wLength */
6029 NULL,
6030 0,
6031 &completion_reason,
6032 &cb_flags,
6033 0);
6034
6035 mutex_enter(&child_ud->usb_mutex);
6036 child_ud->usb_active_cfg_ndx = config_index;
6037 child_ud->usb_cfg = child_ud->usb_cfg_array[config_index];
6038 child_ud->usb_cfg_length = config_descriptor.wTotalLength;
6039 child_ud->usb_cfg_value = config_descriptor.bConfigurationValue;
6040 child_ud->usb_n_ifs = config_descriptor.bNumInterfaces;
6041 child_ud->usb_dip = child_dip;
6042
6043 child_ud->usb_client_flags = kmem_zalloc(
6044 child_ud->usb_n_ifs * USBA_CLIENT_FLAG_SIZE, KM_SLEEP);
6045
6046 child_ud->usb_client_attach_list = kmem_zalloc(
6047 child_ud->usb_n_ifs *
6048 sizeof (*child_ud->usb_client_attach_list), KM_SLEEP);
6049
6050 child_ud->usb_client_ev_cb_list = kmem_zalloc(
6051 child_ud->usb_n_ifs *
6052 sizeof (*child_ud->usb_client_ev_cb_list), KM_SLEEP);
6053
6054 mutex_exit(&child_ud->usb_mutex);
6055
6056 /* ready the device node */
6057 child_dip = usba_ready_device_node(child_dip);
6058
6059 /* set owner of default pipe to child dip */
6060 ph = usba_get_ph_data(def_ph);
6061 mutex_enter(&ph->p_mutex);
6062 mutex_enter(&ph->p_ph_impl->usba_ph_mutex);
6063 ph->p_ph_impl->usba_ph_dip = ph->p_dip = child_dip;
6064 mutex_exit(&ph->p_ph_impl->usba_ph_mutex);
6065 mutex_exit(&ph->p_mutex);
6066
6067 return (child_dip);
6068 }
6069
6070 /*
6071 * hubd_create_child
6072 * - create child dip
6073 * - open default pipe
6074 * - get device descriptor
6075 * - set the address
6076 * - get device string descriptors
6077 * - get the entire config cloud (all configurations) of the device
6078 * - set user preferred configuration
6079 * - close default pipe
6080 * - load appropriate driver(s)
6081 */
6082 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)6083 hubd_create_child(dev_info_t *dip,
6084 hubd_t *hubd,
6085 usba_device_t *hubd_ud,
6086 usb_port_status_t port_status,
6087 usb_port_t port,
6088 int iteration)
6089 {
6090 dev_info_t *child_dip = NULL;
6091 usb_dev_descr_t usb_dev_descr;
6092 int rval;
6093 usba_device_t *child_ud = NULL;
6094 usba_device_t *parent_ud = NULL;
6095 usb_pipe_handle_t ph = NULL; /* default pipe handle */
6096 mblk_t *pdata = NULL;
6097 usb_cr_t completion_reason;
6098 int user_conf_index;
6099 uint_t config_index;
6100 usb_cb_flags_t cb_flags;
6101 uchar_t address = 0;
6102 uint16_t length;
6103 size_t size;
6104 usb_addr_t parent_usb_addr;
6105 usb_port_t parent_usb_port;
6106 usba_device_t *parent_usba_dev;
6107 usb_port_status_t parent_port_status;
6108 boolean_t hcd_called = B_FALSE;
6109
6110 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6111 "hubd_create_child: port=%d", port);
6112
6113 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
6114 ASSERT(hubd->h_usba_devices[port] == NULL);
6115
6116 mutex_exit(HUBD_MUTEX(hubd));
6117
6118 /*
6119 * create a dip which can be used to open the pipe. we set
6120 * the name after getting the descriptors from the device
6121 */
6122 rval = usba_create_child_devi(dip,
6123 "device", /* driver name */
6124 hubd_ud->usb_hcdi_ops, /* usba_hcdi ops */
6125 hubd_ud->usb_root_hub_dip,
6126 port_status, /* low speed device */
6127 child_ud,
6128 &child_dip);
6129
6130 if (rval != USB_SUCCESS) {
6131
6132 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6133 "usb_create_child_devi failed (%d)", rval);
6134
6135 goto fail_cleanup;
6136 }
6137
6138 child_ud = usba_get_usba_device(child_dip);
6139 ASSERT(child_ud != NULL);
6140
6141 parent_ud = hubd->h_usba_device;
6142 mutex_enter(&parent_ud->usb_mutex);
6143 parent_port_status = parent_ud->usb_port_status;
6144
6145 /*
6146 * To support split transactions, update address and port of high speed
6147 * hub to which given device is connected. Note, split transactions
6148 * only exist for high speed devices.
6149 */
6150 if (parent_port_status == USBA_HIGH_SPEED_DEV) {
6151 parent_usba_dev = parent_ud;
6152 parent_usb_addr = parent_ud->usb_addr;
6153 parent_usb_port = port;
6154 } else {
6155 parent_usba_dev = parent_ud->usb_hs_hub_usba_dev;
6156 parent_usb_addr = parent_ud->usb_hs_hub_addr;
6157 parent_usb_port = parent_ud->usb_hs_hub_port;
6158 }
6159 mutex_exit(&parent_ud->usb_mutex);
6160
6161 mutex_enter(&child_ud->usb_mutex);
6162 address = child_ud->usb_addr;
6163 child_ud->usb_addr = 0;
6164 child_ud->usb_dev_descr = kmem_alloc(sizeof (usb_dev_descr_t),
6165 KM_SLEEP);
6166 bzero(&usb_dev_descr, sizeof (usb_dev_descr_t));
6167
6168 switch (port_status) {
6169 case USBA_SUPER_SPEED_DEV:
6170 usb_dev_descr.bMaxPacketSize0 = 9;
6171 break;
6172 case USBA_LOW_SPEED_DEV:
6173 usb_dev_descr.bMaxPacketSize0 = 8;
6174 break;
6175 default:
6176 usb_dev_descr.bMaxPacketSize0 = 64;
6177 break;
6178 }
6179 bcopy(&usb_dev_descr, child_ud->usb_dev_descr,
6180 sizeof (usb_dev_descr_t));
6181 child_ud->usb_port = port;
6182
6183 /*
6184 * The parent hub always keeps track of the hub this device is connected
6185 * to; however, the hs_hub_* variables are only keeping track of the
6186 * closest high speed hub. Unfortunately, we need both.
6187 */
6188 child_ud->usb_parent_hub = parent_ud;
6189 child_ud->usb_hs_hub_usba_dev = parent_usba_dev;
6190 child_ud->usb_hs_hub_addr = parent_usb_addr;
6191 child_ud->usb_hs_hub_port = parent_usb_port;
6192 mutex_exit(&child_ud->usb_mutex);
6193
6194 /*
6195 * Before we open up the default pipe, give the HCD a chance to do
6196 * something here.
6197 */
6198 if (child_ud->usb_hcdi_ops->usba_hcdi_device_init != NULL) {
6199 int rval;
6200 void *priv = NULL;
6201
6202 rval = child_ud->usb_hcdi_ops->usba_hcdi_device_init(child_ud,
6203 port, &priv);
6204 if (rval != USB_SUCCESS) {
6205 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6206 "HCD usba_hcdi_Device_init failed (%d)", rval);
6207 goto fail_cleanup;
6208 }
6209
6210 child_ud->usb_hcd_private = priv;
6211 hcd_called = B_TRUE;
6212 }
6213
6214
6215
6216 /* Open the default pipe */
6217 if ((rval = usb_pipe_open(child_dip, NULL, NULL,
6218 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) != USB_SUCCESS) {
6219 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6220 "usb_pipe_open failed (%d)", rval);
6221
6222 goto fail_cleanup;
6223 }
6224
6225 /*
6226 * get device descriptor
6227 */
6228 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6229 "hubd_create_child: get device descriptor: 64 bytes");
6230
6231 rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
6232 USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
6233 USB_REQ_GET_DESCR, /* bRequest */
6234 USB_DESCR_TYPE_SETUP_DEV, /* wValue */
6235 0, /* wIndex */
6236 64, /* wLength */
6237 &pdata, USB_ATTRS_SHORT_XFER_OK,
6238 &completion_reason, &cb_flags, 0);
6239
6240 /*
6241 * If this is a full speed device, we cannot assume that its default
6242 * packet size is 64 bytes, it may be 8 bytes.
6243 */
6244
6245 if ((rval != USB_SUCCESS) &&
6246 (!((completion_reason == USB_CR_DATA_OVERRUN) && pdata))) {
6247
6248 /*
6249 * rval != USB_SUCCESS AND
6250 * completion_reason != USB_CR_DATA_OVERRUN
6251 * pdata could be != NULL.
6252 * Free pdata now to prevent memory leak.
6253 */
6254 freemsg(pdata);
6255 pdata = NULL;
6256
6257 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6258 "hubd_create_child: get device descriptor: 8 bytes");
6259
6260 rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
6261 USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
6262 USB_REQ_GET_DESCR, /* bRequest */
6263 USB_DESCR_TYPE_SETUP_DEV, /* wValue */
6264 0, /* wIndex */
6265 8, /* wLength */
6266 &pdata, USB_ATTRS_NONE,
6267 &completion_reason, &cb_flags, 0);
6268
6269 if (rval != USB_SUCCESS) {
6270 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6271 "getting device descriptor failed (%s 0x%x %d)",
6272 usb_str_cr(completion_reason), cb_flags, rval);
6273 goto fail_cleanup;
6274 }
6275 } else {
6276 ASSERT(completion_reason == USB_CR_OK);
6277 }
6278
6279 ASSERT(pdata != NULL);
6280
6281 size = usb_parse_dev_descr(
6282 pdata->b_rptr,
6283 MBLKL(pdata),
6284 &usb_dev_descr,
6285 sizeof (usb_dev_descr_t));
6286
6287 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6288 "parsing device descriptor returned %lu", size);
6289
6290 length = *(pdata->b_rptr);
6291 freemsg(pdata);
6292 pdata = NULL;
6293 if (size < 8) {
6294 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6295 "get device descriptor returned %lu bytes", size);
6296
6297 goto fail_cleanup;
6298 }
6299
6300 if (length < 8) {
6301 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6302 "fail enumeration: bLength=%d", length);
6303
6304 goto fail_cleanup;
6305 }
6306
6307 if (child_ud->usb_hcdi_ops->usba_hcdi_device_address != NULL) {
6308 rval = child_ud->usb_hcdi_ops->usba_hcdi_device_address(
6309 child_ud);
6310 if (rval != USB_SUCCESS)
6311 goto fail_cleanup;
6312 } else {
6313 /* Set the address of the device */
6314 if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
6315 USB_DEV_REQ_HOST_TO_DEV,
6316 USB_REQ_SET_ADDRESS, /* bRequest */
6317 address, /* wValue */
6318 0, /* wIndex */
6319 0, /* wLength */
6320 NULL, 0,
6321 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
6322 char buffer[64];
6323 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6324 "setting address failed (cr=%s cb_flags=%s "
6325 "rval=%d)", usb_str_cr(completion_reason),
6326 usb_str_cb_flags(cb_flags, buffer, sizeof (buffer)),
6327 rval);
6328
6329 goto fail_cleanup;
6330 }
6331 }
6332
6333 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6334 "set address 0x%x done", address);
6335
6336 /* now close the pipe for addr 0 */
6337 usb_pipe_close(child_dip, ph,
6338 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
6339
6340 /*
6341 * This delay is important for the CATC hub to enumerate
6342 * But, avoid delay in the first iteration
6343 */
6344 if (iteration) {
6345 delay(drv_usectohz(hubd_device_delay/100));
6346 }
6347
6348 /* assign the address in the usba_device structure */
6349 mutex_enter(&child_ud->usb_mutex);
6350 child_ud->usb_addr = address;
6351 child_ud->usb_no_cpr = 0;
6352 child_ud->usb_port_status = port_status;
6353 /* save this device descriptor */
6354 bcopy(&usb_dev_descr, child_ud->usb_dev_descr,
6355 sizeof (usb_dev_descr_t));
6356 child_ud->usb_n_cfgs = usb_dev_descr.bNumConfigurations;
6357 mutex_exit(&child_ud->usb_mutex);
6358
6359 /* re-open the pipe for the device with the new address */
6360 if ((rval = usb_pipe_open(child_dip, NULL, NULL,
6361 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) != USB_SUCCESS) {
6362 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6363 "usb_pipe_open failed (%d)", rval);
6364
6365 goto fail_cleanup;
6366 }
6367
6368 /*
6369 * Get full device descriptor only if we have not received full
6370 * device descriptor earlier.
6371 */
6372 if (size < length) {
6373 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6374 "hubd_create_child: get full device descriptor: "
6375 "%d bytes", length);
6376
6377 if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
6378 USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
6379 USB_REQ_GET_DESCR, /* bRequest */
6380 USB_DESCR_TYPE_SETUP_DEV, /* wValue */
6381 0, /* wIndex */
6382 length, /* wLength */
6383 &pdata, 0,
6384 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
6385 freemsg(pdata);
6386 pdata = NULL;
6387
6388 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG,
6389 hubd->h_log_handle,
6390 "hubd_create_child: get full device descriptor: "
6391 "64 bytes");
6392
6393 rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
6394 USB_DEV_REQ_DEV_TO_HOST |
6395 USB_DEV_REQ_TYPE_STANDARD,
6396 USB_REQ_GET_DESCR, /* bRequest */
6397 USB_DESCR_TYPE_SETUP_DEV, /* wValue */
6398 0, /* wIndex */
6399 64, /* wLength */
6400 &pdata, USB_ATTRS_SHORT_XFER_OK,
6401 &completion_reason, &cb_flags, 0);
6402
6403 /* we have to trust the data now */
6404 if (pdata) {
6405 int len = *(pdata->b_rptr);
6406
6407 length = MBLKL(pdata);
6408 if (length < len) {
6409
6410 goto fail_cleanup;
6411 }
6412 } else if (rval != USB_SUCCESS) {
6413 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
6414 hubd->h_log_handle,
6415 "getting device descriptor failed "
6416 "(%d 0x%x %d)",
6417 completion_reason, cb_flags, rval);
6418
6419 goto fail_cleanup;
6420 }
6421 }
6422
6423 size = usb_parse_dev_descr(
6424 pdata->b_rptr,
6425 MBLKL(pdata),
6426 &usb_dev_descr,
6427 sizeof (usb_dev_descr_t));
6428
6429 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6430 "parsing device descriptor returned %lu", size);
6431
6432 /*
6433 * For now, free the data
6434 * eventually, each configuration may need to be looked at
6435 */
6436 freemsg(pdata);
6437 pdata = NULL;
6438
6439 if (size != USB_DEV_DESCR_SIZE) {
6440 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6441 "fail enumeration: descriptor size=%lu "
6442 "expected size=%u", size, USB_DEV_DESCR_SIZE);
6443
6444 goto fail_cleanup;
6445 }
6446
6447 /*
6448 * save the device descriptor in usba_device since it is needed
6449 * later on again
6450 */
6451 mutex_enter(&child_ud->usb_mutex);
6452 bcopy(&usb_dev_descr, child_ud->usb_dev_descr,
6453 sizeof (usb_dev_descr_t));
6454 child_ud->usb_n_cfgs = usb_dev_descr.bNumConfigurations;
6455 mutex_exit(&child_ud->usb_mutex);
6456 }
6457
6458 if (usb_dev_descr.bNumConfigurations == 0) {
6459 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6460 "device descriptor:\n\t"
6461 "l=0x%x type=0x%x USB=0x%x class=0x%x subclass=0x%x\n\t"
6462 "protocol=0x%x maxpktsize=0x%x "
6463 "Vid=0x%x Pid=0x%x rel=0x%x\n\t"
6464 "Mfg=0x%x P=0x%x sn=0x%x #config=0x%x",
6465 usb_dev_descr.bLength, usb_dev_descr.bDescriptorType,
6466 usb_dev_descr.bcdUSB, usb_dev_descr.bDeviceClass,
6467 usb_dev_descr.bDeviceSubClass,
6468 usb_dev_descr.bDeviceProtocol,
6469 usb_dev_descr.bMaxPacketSize0,
6470 usb_dev_descr.idVendor,
6471 usb_dev_descr.idProduct, usb_dev_descr.bcdDevice,
6472 usb_dev_descr.iManufacturer, usb_dev_descr.iProduct,
6473 usb_dev_descr.iSerialNumber,
6474 usb_dev_descr.bNumConfigurations);
6475 goto fail_cleanup;
6476 }
6477
6478 /* Read the BOS data */
6479 usba_get_binary_object_store(child_dip, child_ud);
6480
6481 /* get the device string descriptor(s) */
6482 usba_get_dev_string_descrs(child_dip, child_ud);
6483
6484 /* retrieve config cloud for all configurations */
6485 rval = hubd_get_all_device_config_cloud(hubd, child_dip, child_ud);
6486 if (rval != USB_SUCCESS) {
6487 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6488 "failed to get configuration descriptor(s)");
6489
6490 goto fail_cleanup;
6491 }
6492
6493 /* get the preferred configuration for this device */
6494 user_conf_index = hubd_select_device_configuration(hubd, port,
6495 child_dip, child_ud);
6496
6497 /* Check if the user selected configuration index is in range */
6498 if ((user_conf_index >= usb_dev_descr.bNumConfigurations) ||
6499 (user_conf_index < 0)) {
6500 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6501 "Configuration index for device idVendor=%d "
6502 "idProduct=%d is=%d, and is out of range[0..%d]",
6503 usb_dev_descr.idVendor, usb_dev_descr.idProduct,
6504 user_conf_index, usb_dev_descr.bNumConfigurations - 1);
6505
6506 /* treat this as user didn't specify configuration */
6507 user_conf_index = USBA_DEV_CONFIG_INDEX_UNDEFINED;
6508 }
6509
6510
6511 /*
6512 * Warn users of a performance hit if connecting a
6513 * High Speed behind a 1.1 hub, which is behind a
6514 * 2.0 port. Don't worry about this for USB 3.x for now.
6515 */
6516 if ((parent_port_status != USBA_HIGH_SPEED_DEV) &&
6517 !(usba_is_root_hub(parent_ud->usb_dip)) &&
6518 (parent_usb_addr)) {
6519
6520 /*
6521 * Now that we know the root port is a high speed port
6522 * and that the parent port is not a high speed port,
6523 * let's find out if the device itself is a high speed
6524 * device. If it is a high speed device,
6525 * USB_DESCR_TYPE_SETUP_DEV_QLF should return a value,
6526 * otherwise the command will fail.
6527 */
6528 rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
6529 USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
6530 USB_REQ_GET_DESCR, /* bRequest */
6531 USB_DESCR_TYPE_SETUP_DEV_QLF, /* wValue */
6532 0, /* wIndex */
6533 10, /* wLength */
6534 &pdata, USB_ATTRS_SHORT_XFER_OK,
6535 &completion_reason, &cb_flags, 0);
6536
6537 if (pdata) {
6538 freemsg(pdata);
6539 pdata = NULL;
6540 }
6541
6542 /*
6543 * USB_DESCR_TYPE_SETUP_DEV_QLF query was successful
6544 * that means this is a high speed device behind a
6545 * high speed root hub, but running at full speed
6546 * because there is a full speed hub in the middle.
6547 */
6548 if (rval == USB_SUCCESS) {
6549 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
6550 hubd->h_log_handle,
6551 "Connecting a high speed device to a "
6552 "non high speed hub (port %d) will result "
6553 "in a loss of performance. Please connect "
6554 "the device to a high speed hub to get "
6555 "the maximum performance.",
6556 port);
6557 }
6558 }
6559
6560 /*
6561 * Now we try to online the device by attaching a driver
6562 * The following truth table illustrates the logic:-
6563 * Cfgndx Driver Action
6564 * 0 0 loop all configs for driver with full
6565 * compatible properties.
6566 * 0 1 set first configuration,
6567 * compatible prop = drivername.
6568 * 1 0 Set config, full compatible prop
6569 * 1 1 Set config, compatible prop = drivername.
6570 *
6571 * Note:
6572 * cfgndx = user_conf_index
6573 * Driver = usb_preferred_driver
6574 */
6575 if (user_conf_index == USBA_DEV_CONFIG_INDEX_UNDEFINED) {
6576 if (child_ud->usb_preferred_driver) {
6577 /*
6578 * It is the job of the "preferred driver" to put the
6579 * device in the desired configuration. Till then
6580 * put the device in config index 0.
6581 */
6582 if ((rval = usba_hubdi_check_power_budget(dip, child_ud,
6583 USB_DEV_DEFAULT_CONFIG_INDEX)) != USB_SUCCESS) {
6584
6585 goto fail_cleanup;
6586 }
6587
6588 child_dip = hubd_ready_device(hubd, child_dip,
6589 child_ud, USB_DEV_DEFAULT_CONFIG_INDEX);
6590
6591 /*
6592 * Assign the dip before onlining to avoid race
6593 * with busctl
6594 */
6595 mutex_enter(HUBD_MUTEX(hubd));
6596 hubd->h_children_dips[port] = child_dip;
6597 mutex_exit(HUBD_MUTEX(hubd));
6598
6599 (void) usba_bind_driver(child_dip);
6600 } else {
6601 /*
6602 * loop through all the configurations to see if we
6603 * can find a driver for any one config. If not, set
6604 * the device in config_index 0
6605 */
6606 rval = USB_FAILURE;
6607 for (config_index = 0;
6608 (config_index < usb_dev_descr.bNumConfigurations) &&
6609 (rval != USB_SUCCESS); config_index++) {
6610
6611 child_dip = hubd_ready_device(hubd, child_dip,
6612 child_ud, config_index);
6613
6614 /*
6615 * Assign the dip before onlining to avoid race
6616 * with busctl
6617 */
6618 mutex_enter(HUBD_MUTEX(hubd));
6619 hubd->h_children_dips[port] = child_dip;
6620 mutex_exit(HUBD_MUTEX(hubd));
6621
6622 rval = usba_bind_driver(child_dip);
6623
6624 /*
6625 * Normally power budget should be checked
6626 * before device is configured. A failure in
6627 * power budget checking will stop the device
6628 * from being configured with current
6629 * config_index and may enable the device to
6630 * be configured in another configuration.
6631 * This may break the user experience that a
6632 * device which previously worked in config
6633 * A now works in config B after power budget
6634 * control is enabled. To avoid such situation,
6635 * power budget checking is moved here and will
6636 * fail the child creation directly if config
6637 * A exceeds the power available.
6638 */
6639 if (rval == USB_SUCCESS) {
6640 if ((usba_hubdi_check_power_budget(dip,
6641 child_ud, config_index)) !=
6642 USB_SUCCESS) {
6643
6644 goto fail_cleanup;
6645 }
6646 }
6647 }
6648 if (rval != USB_SUCCESS) {
6649
6650 if ((usba_hubdi_check_power_budget(dip,
6651 child_ud, 0)) != USB_SUCCESS) {
6652
6653 goto fail_cleanup;
6654 }
6655
6656 child_dip = hubd_ready_device(hubd, child_dip,
6657 child_ud, 0);
6658 mutex_enter(HUBD_MUTEX(hubd));
6659 hubd->h_children_dips[port] = child_dip;
6660 mutex_exit(HUBD_MUTEX(hubd));
6661 }
6662 } /* end else loop all configs */
6663 } else {
6664
6665 if ((usba_hubdi_check_power_budget(dip, child_ud,
6666 (uint_t)user_conf_index)) != USB_SUCCESS) {
6667
6668 goto fail_cleanup;
6669 }
6670
6671 child_dip = hubd_ready_device(hubd, child_dip,
6672 child_ud, (uint_t)user_conf_index);
6673
6674 /*
6675 * Assign the dip before onlining to avoid race
6676 * with busctl
6677 */
6678 mutex_enter(HUBD_MUTEX(hubd));
6679 hubd->h_children_dips[port] = child_dip;
6680 mutex_exit(HUBD_MUTEX(hubd));
6681
6682 (void) usba_bind_driver(child_dip);
6683 }
6684
6685 usba_hubdi_decr_power_budget(dip, child_ud);
6686
6687 mutex_enter(HUBD_MUTEX(hubd));
6688 if (hubd->h_usba_devices[port] == NULL) {
6689 hubd->h_usba_devices[port] = usba_get_usba_device(child_dip);
6690 } else {
6691 ASSERT(hubd->h_usba_devices[port] ==
6692 usba_get_usba_device(child_dip));
6693 }
6694
6695 return (USB_SUCCESS);
6696
6697
6698 fail_cleanup:
6699 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6700 "hubd_create_child: fail_cleanup");
6701
6702 mutex_enter(HUBD_MUTEX(hubd));
6703 hubd->h_children_dips[port] = NULL;
6704 mutex_exit(HUBD_MUTEX(hubd));
6705
6706 if (pdata) {
6707 freemsg(pdata);
6708 }
6709
6710 if (ph) {
6711 usb_pipe_close(child_dip, ph,
6712 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
6713 }
6714
6715 if (child_ud != NULL && hcd_called == B_TRUE &&
6716 child_ud->usb_hcdi_ops->usba_hcdi_device_fini != NULL) {
6717 child_ud->usb_hcdi_ops->usba_hcdi_device_fini(child_ud,
6718 child_ud->usb_hcd_private);
6719 child_ud->usb_hcd_private = NULL;
6720 }
6721
6722
6723 if (child_dip) {
6724 int rval = usba_destroy_child_devi(child_dip,
6725 NDI_DEVI_REMOVE);
6726 if (rval != USB_SUCCESS) {
6727 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6728 "failure to remove child node");
6729 }
6730 }
6731
6732 if (child_ud) {
6733 /* to make sure we free the address */
6734 mutex_enter(&child_ud->usb_mutex);
6735 child_ud->usb_addr = address;
6736 ASSERT(child_ud->usb_ref_count == 0);
6737 mutex_exit(&child_ud->usb_mutex);
6738
6739 mutex_enter(HUBD_MUTEX(hubd));
6740 if (hubd->h_usba_devices[port] == NULL) {
6741 mutex_exit(HUBD_MUTEX(hubd));
6742 usba_free_usba_device(child_ud);
6743 } else {
6744 hubd_free_usba_device(hubd, hubd->h_usba_devices[port]);
6745 mutex_exit(HUBD_MUTEX(hubd));
6746 }
6747 }
6748
6749 mutex_enter(HUBD_MUTEX(hubd));
6750
6751 return (USB_FAILURE);
6752 }
6753
6754
6755 /*
6756 * hubd_delete_child:
6757 * - free usb address
6758 * - lookup child dips, there may be multiple on this port
6759 * - offline each child devi
6760 */
6761 static int
hubd_delete_child(hubd_t * hubd,usb_port_t port,uint_t flag,boolean_t retry)6762 hubd_delete_child(hubd_t *hubd, usb_port_t port, uint_t flag, boolean_t retry)
6763 {
6764 dev_info_t *child_dip;
6765 usba_device_t *usba_device;
6766 int rval = USB_SUCCESS;
6767
6768 child_dip = hubd->h_children_dips[port];
6769 usba_device = hubd->h_usba_devices[port];
6770
6771 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6772 "hubd_delete_child: port=%d, dip=0x%p usba_device=0x%p",
6773 port, (void *)child_dip, (void *)usba_device);
6774
6775 mutex_exit(HUBD_MUTEX(hubd));
6776 if (child_dip) {
6777 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6778 "hubd_delete_child:\n\t"
6779 "dip = 0x%p (%s) at port %d",
6780 (void *)child_dip, ddi_node_name(child_dip), port);
6781
6782 if (usba_device) {
6783 usba_hubdi_incr_power_budget(hubd->h_dip, usba_device);
6784 }
6785
6786
6787 rval = usba_destroy_child_devi(child_dip, flag);
6788
6789 if ((rval == USB_SUCCESS) && (flag & NDI_DEVI_REMOVE)) {
6790 /*
6791 * if the child was still < DS_INITIALIZED
6792 * then our bus_unconfig was not called and
6793 * we have to zap the child here
6794 */
6795 mutex_enter(HUBD_MUTEX(hubd));
6796 if (hubd->h_children_dips[port] == child_dip) {
6797 usba_device_t *ud =
6798 hubd->h_usba_devices[port];
6799 hubd->h_children_dips[port] = NULL;
6800 if (ud) {
6801 mutex_exit(HUBD_MUTEX(hubd));
6802
6803 mutex_enter(&ud->usb_mutex);
6804 ud->usb_ref_count = 0;
6805 mutex_exit(&ud->usb_mutex);
6806
6807 usba_free_usba_device(ud);
6808 mutex_enter(HUBD_MUTEX(hubd));
6809 hubd->h_usba_devices[port] = NULL;
6810 }
6811 }
6812 mutex_exit(HUBD_MUTEX(hubd));
6813 }
6814 }
6815
6816 if ((rval != USB_SUCCESS) && retry) {
6817
6818 hubd_schedule_cleanup(usba_device->usb_root_hub_dip);
6819 }
6820 mutex_enter(HUBD_MUTEX(hubd));
6821
6822 return (rval);
6823 }
6824
6825
6826 /*
6827 * hubd_free_usba_device:
6828 * free usb device structure unless it is associated with
6829 * the root hub which is handled differently
6830 */
6831 static void
hubd_free_usba_device(hubd_t * hubd,usba_device_t * usba_device)6832 hubd_free_usba_device(hubd_t *hubd, usba_device_t *usba_device)
6833 {
6834 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6835 "hubd_free_usba_device: hubd=0x%p, usba_device=0x%p",
6836 (void *)hubd, (void *)usba_device);
6837
6838 if (usba_device && (usba_device->usb_addr != ROOT_HUB_ADDR)) {
6839 usb_port_t port = usba_device->usb_port;
6840 dev_info_t *dip = hubd->h_children_dips[port];
6841
6842 #ifdef DEBUG
6843 if (dip) {
6844 ASSERT(i_ddi_node_state(dip) < DS_INITIALIZED);
6845 }
6846 #endif
6847 port = usba_device->usb_port;
6848 hubd->h_usba_devices[port] = NULL;
6849
6850 mutex_exit(HUBD_MUTEX(hubd));
6851 usba_free_usba_device(usba_device);
6852 mutex_enter(HUBD_MUTEX(hubd));
6853 }
6854 }
6855
6856
6857 /*
6858 * event support
6859 *
6860 * busctl event support
6861 */
6862 static int
hubd_busop_get_eventcookie(dev_info_t * dip,dev_info_t * rdip,char * eventname,ddi_eventcookie_t * cookie)6863 hubd_busop_get_eventcookie(dev_info_t *dip,
6864 dev_info_t *rdip,
6865 char *eventname,
6866 ddi_eventcookie_t *cookie)
6867 {
6868 hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip);
6869
6870 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6871 "hubd_busop_get_eventcookie: dip=0x%p, rdip=0x%p, "
6872 "event=%s", (void *)dip, (void *)rdip, eventname);
6873 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6874 "(dip=%s%d, rdip=%s%d)",
6875 ddi_driver_name(dip), ddi_get_instance(dip),
6876 ddi_driver_name(rdip), ddi_get_instance(rdip));
6877
6878 /* return event cookie, iblock cookie, and level */
6879 return (ndi_event_retrieve_cookie(hubd->h_ndi_event_hdl,
6880 rdip, eventname, cookie, NDI_EVENT_NOPASS));
6881 }
6882
6883
6884 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)6885 hubd_busop_add_eventcall(dev_info_t *dip,
6886 dev_info_t *rdip,
6887 ddi_eventcookie_t cookie,
6888 ddi_event_cb_f callback,
6889 void *arg, ddi_callback_id_t *cb_id)
6890 {
6891 hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip);
6892 usb_port_t port = hubd_child_dip2port(hubd, rdip);
6893
6894 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6895 "hubd_busop_add_eventcall: dip=0x%p, rdip=0x%p "
6896 "cookie=0x%p, cb=0x%p, arg=0x%p",
6897 (void *)dip, (void *)rdip, (void *)cookie, (void *)callback, arg);
6898 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6899 "(dip=%s%d, rdip=%s%d, event=%s)",
6900 ddi_driver_name(dip), ddi_get_instance(dip),
6901 ddi_driver_name(rdip), ddi_get_instance(rdip),
6902 ndi_event_cookie_to_name(hubd->h_ndi_event_hdl, cookie));
6903
6904 /* Set flag on children registering events */
6905 switch (ndi_event_cookie_to_tag(hubd->h_ndi_event_hdl, cookie)) {
6906 case USBA_EVENT_TAG_HOT_REMOVAL:
6907 mutex_enter(HUBD_MUTEX(hubd));
6908 hubd->h_child_events[port] |= HUBD_CHILD_EVENT_DISCONNECT;
6909 mutex_exit(HUBD_MUTEX(hubd));
6910
6911 break;
6912 case USBA_EVENT_TAG_PRE_SUSPEND:
6913 mutex_enter(HUBD_MUTEX(hubd));
6914 hubd->h_child_events[port] |= HUBD_CHILD_EVENT_PRESUSPEND;
6915 mutex_exit(HUBD_MUTEX(hubd));
6916
6917 break;
6918 default:
6919
6920 break;
6921 }
6922
6923 /* add callback to our event set */
6924 return (ndi_event_add_callback(hubd->h_ndi_event_hdl,
6925 rdip, cookie, callback, arg, NDI_SLEEP, cb_id));
6926 }
6927
6928
6929 static int
hubd_busop_remove_eventcall(dev_info_t * dip,ddi_callback_id_t cb_id)6930 hubd_busop_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id)
6931 {
6932 hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip);
6933 ndi_event_callbacks_t *id = (ndi_event_callbacks_t *)cb_id;
6934
6935 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6936 "hubd_busop_remove_eventcall: dip=0x%p, rdip=0x%p "
6937 "cookie=0x%p", (void *)dip, (void *)id->ndi_evtcb_dip,
6938 (void *)id->ndi_evtcb_cookie);
6939 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6940 "(dip=%s%d, rdip=%s%d, event=%s)",
6941 ddi_driver_name(dip), ddi_get_instance(dip),
6942 ddi_driver_name(id->ndi_evtcb_dip),
6943 ddi_get_instance(id->ndi_evtcb_dip),
6944 ndi_event_cookie_to_name(hubd->h_ndi_event_hdl,
6945 id->ndi_evtcb_cookie));
6946
6947 /* remove event registration from our event set */
6948 return (ndi_event_remove_callback(hubd->h_ndi_event_hdl, cb_id));
6949 }
6950
6951
6952 /*
6953 * event distribution
6954 *
6955 * hubd_do_callback:
6956 * Post this event to the specified child
6957 */
6958 static void
hubd_do_callback(hubd_t * hubd,dev_info_t * cdip,ddi_eventcookie_t cookie)6959 hubd_do_callback(hubd_t *hubd, dev_info_t *cdip, ddi_eventcookie_t cookie)
6960 {
6961 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6962 "hubd_do_callback");
6963
6964 (void) ndi_event_do_callback(hubd->h_ndi_event_hdl, cdip, cookie, NULL);
6965 }
6966
6967
6968 /*
6969 * hubd_run_callbacks:
6970 * Send this event to all children
6971 */
6972 static void
hubd_run_callbacks(hubd_t * hubd,usba_event_t type)6973 hubd_run_callbacks(hubd_t *hubd, usba_event_t type)
6974 {
6975 usb_port_t port;
6976
6977 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6978 "hubd_run_callbacks");
6979
6980 mutex_enter(HUBD_MUTEX(hubd));
6981 for (port = 1; port <= hubd->h_nports; port++) {
6982 /*
6983 * the childen_dips list may have dips that have been
6984 * already deallocated. we only get a post_detach notification
6985 * but not a destroy notification
6986 */
6987 if (hubd->h_children_dips[port]) {
6988 mutex_exit(HUBD_MUTEX(hubd));
6989 hubd_post_event(hubd, port, type);
6990 mutex_enter(HUBD_MUTEX(hubd));
6991 }
6992 }
6993 mutex_exit(HUBD_MUTEX(hubd));
6994 }
6995
6996
6997 /*
6998 * hubd_post_event
6999 * post event to a child on the port depending on the type
7000 */
7001 static void
hubd_post_event(hubd_t * hubd,usb_port_t port,usba_event_t type)7002 hubd_post_event(hubd_t *hubd, usb_port_t port, usba_event_t type)
7003 {
7004 int rval;
7005 dev_info_t *dip;
7006 usba_device_t *usba_device;
7007 ddi_eventcookie_t cookie, rm_cookie, suspend_cookie;
7008
7009 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
7010 "hubd_post_event: port=%d event=%s", port,
7011 ndi_event_tag_to_name(hubd->h_ndi_event_hdl, type));
7012
7013 cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl, type);
7014 rm_cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl,
7015 USBA_EVENT_TAG_HOT_REMOVAL);
7016 suspend_cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl,
7017 USBA_EVENT_TAG_PRE_SUSPEND);
7018
7019 /*
7020 * Hotplug daemon may be attaching a driver that may be registering
7021 * event callbacks. So it already has got the device tree lock and
7022 * event handle mutex. So to prevent a deadlock while posting events,
7023 * we grab and release the locks in the same order.
7024 */
7025 mutex_enter(HUBD_MUTEX(hubd));
7026 dip = hubd->h_children_dips[port];
7027 usba_device = hubd->h_usba_devices[port];
7028 mutex_exit(HUBD_MUTEX(hubd));
7029
7030 switch (type) {
7031 case USBA_EVENT_TAG_HOT_REMOVAL:
7032 /* Clear the registered event flag */
7033 mutex_enter(HUBD_MUTEX(hubd));
7034 hubd->h_child_events[port] &= ~HUBD_CHILD_EVENT_DISCONNECT;
7035 mutex_exit(HUBD_MUTEX(hubd));
7036
7037 hubd_do_callback(hubd, dip, cookie);
7038 usba_persistent_pipe_close(usba_device);
7039
7040 /*
7041 * Mark the dip for deletion only after the driver has
7042 * seen the disconnect event to prevent cleanup thread
7043 * from stepping in between.
7044 */
7045 mutex_enter(&(DEVI(dip)->devi_lock));
7046 DEVI_SET_DEVICE_REMOVED(dip);
7047 mutex_exit(&(DEVI(dip)->devi_lock));
7048
7049 break;
7050 case USBA_EVENT_TAG_PRE_SUSPEND:
7051 mutex_enter(HUBD_MUTEX(hubd));
7052 hubd->h_child_events[port] &= ~HUBD_CHILD_EVENT_PRESUSPEND;
7053 mutex_exit(HUBD_MUTEX(hubd));
7054
7055 hubd_do_callback(hubd, dip, cookie);
7056 /*
7057 * persistent pipe close for this event is taken care by the
7058 * caller after verfying that all children can suspend
7059 */
7060
7061 break;
7062 case USBA_EVENT_TAG_HOT_INSERTION:
7063 /*
7064 * Check if this child has missed the disconnect event before
7065 * it registered for event callbacks
7066 */
7067 mutex_enter(HUBD_MUTEX(hubd));
7068 if (hubd->h_child_events[port] & HUBD_CHILD_EVENT_DISCONNECT) {
7069 /* clear the flag and post disconnect event */
7070 hubd->h_child_events[port] &=
7071 ~HUBD_CHILD_EVENT_DISCONNECT;
7072 mutex_exit(HUBD_MUTEX(hubd));
7073 hubd_do_callback(hubd, dip, rm_cookie);
7074 usba_persistent_pipe_close(usba_device);
7075 mutex_enter(HUBD_MUTEX(hubd));
7076 }
7077 mutex_exit(HUBD_MUTEX(hubd));
7078
7079 /*
7080 * Mark the dip as reinserted to prevent cleanup thread
7081 * from stepping in.
7082 */
7083 mutex_enter(&(DEVI(dip)->devi_lock));
7084 DEVI_SET_DEVICE_REINSERTED(dip);
7085 mutex_exit(&(DEVI(dip)->devi_lock));
7086
7087 rval = usba_persistent_pipe_open(usba_device);
7088 if (rval != USB_SUCCESS) {
7089 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
7090 hubd->h_log_handle,
7091 "failed to reopen all pipes on reconnect");
7092 }
7093
7094 hubd_do_callback(hubd, dip, cookie);
7095
7096 /*
7097 * We might see a connect event only if hotplug thread for
7098 * disconnect event don't run in time.
7099 * Set the flag again, so we don't miss posting a
7100 * disconnect event.
7101 */
7102 mutex_enter(HUBD_MUTEX(hubd));
7103 hubd->h_child_events[port] |= HUBD_CHILD_EVENT_DISCONNECT;
7104 mutex_exit(HUBD_MUTEX(hubd));
7105
7106 break;
7107 case USBA_EVENT_TAG_POST_RESUME:
7108 /*
7109 * Check if this child has missed the pre-suspend event before
7110 * it registered for event callbacks
7111 */
7112 mutex_enter(HUBD_MUTEX(hubd));
7113 if (hubd->h_child_events[port] & HUBD_CHILD_EVENT_PRESUSPEND) {
7114 /* clear the flag and post pre_suspend event */
7115 hubd->h_port_state[port] &=
7116 ~HUBD_CHILD_EVENT_PRESUSPEND;
7117 mutex_exit(HUBD_MUTEX(hubd));
7118 hubd_do_callback(hubd, dip, suspend_cookie);
7119 mutex_enter(HUBD_MUTEX(hubd));
7120 }
7121 mutex_exit(HUBD_MUTEX(hubd));
7122
7123 mutex_enter(&usba_device->usb_mutex);
7124 usba_device->usb_no_cpr = 0;
7125 mutex_exit(&usba_device->usb_mutex);
7126
7127 /*
7128 * Since the pipe has already been opened by hub
7129 * at DDI_RESUME time, there is no need for a
7130 * persistent pipe open
7131 */
7132 hubd_do_callback(hubd, dip, cookie);
7133
7134 /*
7135 * Set the flag again, so we don't miss posting a
7136 * pre-suspend event. This enforces a tighter
7137 * dev_state model.
7138 */
7139 mutex_enter(HUBD_MUTEX(hubd));
7140 hubd->h_child_events[port] |= HUBD_CHILD_EVENT_PRESUSPEND;
7141 mutex_exit(HUBD_MUTEX(hubd));
7142 break;
7143 }
7144 }
7145
7146
7147 /*
7148 * handling of events coming from above
7149 */
7150 static int
hubd_disconnect_event_cb(dev_info_t * dip)7151 hubd_disconnect_event_cb(dev_info_t *dip)
7152 {
7153 hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip);
7154 usb_port_t port, nports;
7155 usba_device_t *usba_dev;
7156 usba_event_t tag = USBA_EVENT_TAG_HOT_REMOVAL;
7157
7158 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
7159 "hubd_disconnect_event_cb: tag=%d", tag);
7160
7161 ndi_devi_enter(dip);
7162
7163 mutex_enter(HUBD_MUTEX(hubd));
7164 switch (hubd->h_dev_state) {
7165 case USB_DEV_ONLINE:
7166 case USB_DEV_PWRED_DOWN:
7167 hubd->h_dev_state = USB_DEV_DISCONNECTED;
7168 /* stop polling on the interrupt pipe */
7169 hubd_stop_polling(hubd);
7170
7171 /* FALLTHROUGH */
7172 case USB_DEV_SUSPENDED:
7173 /* we remain in this state */
7174 mutex_exit(HUBD_MUTEX(hubd));
7175 hubd_run_callbacks(hubd, tag);
7176 mutex_enter(HUBD_MUTEX(hubd));
7177
7178 /* close all the open pipes of our children */
7179 nports = hubd->h_nports;
7180 for (port = 1; port <= nports; port++) {
7181 usba_dev = hubd->h_usba_devices[port];
7182 if (usba_dev != NULL) {
7183 mutex_exit(HUBD_MUTEX(hubd));
7184 usba_persistent_pipe_close(usba_dev);
7185 mutex_enter(HUBD_MUTEX(hubd));
7186 }
7187 }
7188
7189 break;
7190 case USB_DEV_DISCONNECTED:
7191 /* avoid passing multiple disconnects to children */
7192 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
7193 "hubd_disconnect_event_cb: Already disconnected");
7194
7195 break;
7196 default:
7197 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
7198 "hubd_disconnect_event_cb: Illegal devstate=%d",
7199 hubd->h_dev_state);
7200
7201 break;
7202 }
7203 mutex_exit(HUBD_MUTEX(hubd));
7204
7205 ndi_devi_exit(dip);
7206
7207 return (USB_SUCCESS);
7208 }
7209
7210
7211 static int
hubd_reconnect_event_cb(dev_info_t * dip)7212 hubd_reconnect_event_cb(dev_info_t *dip)
7213 {
7214 int rval;
7215
7216 ndi_devi_enter(dip);
7217 rval = hubd_restore_state_cb(dip);
7218 ndi_devi_exit(dip);
7219
7220 return (rval);
7221 }
7222
7223
7224 /*
7225 * hubd_pre_suspend_event_cb
7226 * propogate event for binary compatibility of old drivers
7227 */
7228 static int
hubd_pre_suspend_event_cb(dev_info_t * dip)7229 hubd_pre_suspend_event_cb(dev_info_t *dip)
7230 {
7231 hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip);
7232
7233 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
7234 "hubd_pre_suspend_event_cb");
7235
7236 /* disable hotplug thread */
7237 mutex_enter(HUBD_MUTEX(hubd));
7238 hubd->h_hotplug_thread++;
7239 hubd_stop_polling(hubd);
7240
7241 /* keep PM out till we see a cpr resume */
7242 (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0);
7243 mutex_exit(HUBD_MUTEX(hubd));
7244
7245 ndi_devi_enter(dip);
7246 hubd_run_callbacks(hubd, USBA_EVENT_TAG_PRE_SUSPEND);
7247 ndi_devi_exit(dip);
7248
7249 return (USB_SUCCESS);
7250 }
7251
7252
7253 /*
7254 * hubd_post_resume_event_cb
7255 * propogate event for binary compatibility of old drivers
7256 */
7257 static int
hubd_post_resume_event_cb(dev_info_t * dip)7258 hubd_post_resume_event_cb(dev_info_t *dip)
7259 {
7260 hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip);
7261
7262 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
7263 "hubd_post_resume_event_cb");
7264
7265 ndi_devi_enter(dip);
7266 hubd_run_callbacks(hubd, USBA_EVENT_TAG_POST_RESUME);
7267 ndi_devi_exit(dip);
7268
7269 mutex_enter(HUBD_MUTEX(hubd));
7270
7271 /* enable PM */
7272 (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
7273
7274 /* allow hotplug thread */
7275 hubd->h_hotplug_thread--;
7276
7277 /* start polling */
7278 hubd_start_polling(hubd, 0);
7279 mutex_exit(HUBD_MUTEX(hubd));
7280
7281 return (USB_SUCCESS);
7282 }
7283
7284
7285 /*
7286 * hubd_cpr_suspend
7287 * save the current state of the driver/device
7288 */
7289 static int
hubd_cpr_suspend(hubd_t * hubd)7290 hubd_cpr_suspend(hubd_t *hubd)
7291 {
7292 usb_port_t port, nports;
7293 usba_device_t *usba_dev;
7294 uchar_t no_cpr = 0;
7295 int rval = USB_FAILURE;
7296
7297 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
7298 "hubd_cpr_suspend: Begin");
7299
7300 /* Make sure device is powered up to save state. */
7301 mutex_enter(HUBD_MUTEX(hubd));
7302 hubd_pm_busy_component(hubd, hubd->h_dip, 0);
7303 mutex_exit(HUBD_MUTEX(hubd));
7304
7305 /* bring the device to full power */
7306 (void) pm_raise_power(hubd->h_dip, 0, USB_DEV_OS_FULL_PWR);
7307 mutex_enter(HUBD_MUTEX(hubd));
7308
7309 switch (hubd->h_dev_state) {
7310 case USB_DEV_ONLINE:
7311 case USB_DEV_PWRED_DOWN:
7312 case USB_DEV_DISCONNECTED:
7313 /* find out if all our children have been quiesced */
7314 nports = hubd->h_nports;
7315 for (port = 1; (no_cpr == 0) && (port <= nports); port++) {
7316 usba_dev = hubd->h_usba_devices[port];
7317 if (usba_dev != NULL) {
7318 mutex_enter(&usba_dev->usb_mutex);
7319 no_cpr += usba_dev->usb_no_cpr;
7320 mutex_exit(&usba_dev->usb_mutex);
7321 }
7322 }
7323 if (no_cpr > 0) {
7324 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
7325 "Children busy - can't checkpoint");
7326 /* remain in same state to fail checkpoint */
7327
7328 break;
7329 } else {
7330 /*
7331 * do not suspend if our hotplug thread
7332 * or the deathrow thread is active
7333 */
7334 if ((hubd->h_hotplug_thread > 1) ||
7335 (hubd->h_cleanup_active == B_TRUE)) {
7336 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
7337 hubd->h_log_handle,
7338 "hotplug thread active - can't cpr");
7339 /* remain in same state to fail checkpoint */
7340
7341 break;
7342 }
7343
7344 /* quiesce ourselves now */
7345 hubd_stop_polling(hubd);
7346
7347 /* close all the open pipes of our children */
7348 for (port = 1; port <= nports; port++) {
7349 usba_dev = hubd->h_usba_devices[port];
7350 if (usba_dev != NULL) {
7351 mutex_exit(HUBD_MUTEX(hubd));
7352 usba_persistent_pipe_close(usba_dev);
7353 if (hubd_suspend_port(hubd, port)) {
7354 USB_DPRINTF_L0(
7355 DPRINT_MASK_HOTPLUG,
7356 hubd->h_log_handle,
7357 "suspending port %d failed",
7358 port);
7359 }
7360 mutex_enter(HUBD_MUTEX(hubd));
7361 }
7362
7363 }
7364 hubd->h_dev_state = USB_DEV_SUSPENDED;
7365
7366 /*
7367 * if we are the root hub, we close our pipes
7368 * ourselves.
7369 */
7370 if (usba_is_root_hub(hubd->h_dip)) {
7371 mutex_exit(HUBD_MUTEX(hubd));
7372 usba_persistent_pipe_close(
7373 usba_get_usba_device(hubd->h_dip));
7374 mutex_enter(HUBD_MUTEX(hubd));
7375 }
7376 rval = USB_SUCCESS;
7377
7378 break;
7379 }
7380 case USB_DEV_SUSPENDED:
7381 default:
7382 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
7383 "hubd_cpr_suspend: Illegal dev state=%d",
7384 hubd->h_dev_state);
7385
7386 break;
7387 }
7388
7389 hubd_pm_idle_component(hubd, hubd->h_dip, 0);
7390 mutex_exit(HUBD_MUTEX(hubd));
7391
7392 return (rval);
7393 }
7394
7395 static void
hubd_cpr_resume(dev_info_t * dip)7396 hubd_cpr_resume(dev_info_t *dip)
7397 {
7398 int rval;
7399
7400 ndi_devi_enter(dip);
7401 /*
7402 * if we are the root hub, we open our pipes
7403 * ourselves.
7404 */
7405 if (usba_is_root_hub(dip)) {
7406 rval = usba_persistent_pipe_open(
7407 usba_get_usba_device(dip));
7408 ASSERT(rval == USB_SUCCESS);
7409 }
7410 (void) hubd_restore_state_cb(dip);
7411 ndi_devi_exit(dip);
7412 }
7413
7414
7415 /*
7416 * hubd_restore_state_cb
7417 * Event callback to restore device state
7418 */
7419 static int
hubd_restore_state_cb(dev_info_t * dip)7420 hubd_restore_state_cb(dev_info_t *dip)
7421 {
7422 hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip);
7423
7424 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
7425 "hubd_restore_state_cb: Begin");
7426
7427 /* restore the state of this device */
7428 hubd_restore_device_state(dip, hubd);
7429
7430 return (USB_SUCCESS);
7431 }
7432
7433
7434 /*
7435 * registering for events
7436 */
7437 static int
hubd_register_events(hubd_t * hubd)7438 hubd_register_events(hubd_t *hubd)
7439 {
7440 int rval = USB_SUCCESS;
7441
7442 if (usba_is_root_hub(hubd->h_dip)) {
7443 hubd_register_cpr_callback(hubd);
7444 } else {
7445 rval = usb_register_event_cbs(hubd->h_dip, &hubd_events, 0);
7446 }
7447
7448 return (rval);
7449 }
7450
7451
7452 /*
7453 * hubd cpr callback related functions
7454 *
7455 * hubd_cpr_post_user_callb:
7456 * This function is called during checkpoint & resume -
7457 * 1. after user threads are stopped during checkpoint
7458 * 2. after kernel threads are resumed during resume
7459 */
7460 /* ARGSUSED */
7461 static boolean_t
hubd_cpr_post_user_callb(void * arg,int code)7462 hubd_cpr_post_user_callb(void *arg, int code)
7463 {
7464 hubd_cpr_t *cpr_cb = (hubd_cpr_t *)arg;
7465 hubd_t *hubd = cpr_cb->statep;
7466 int retry = 0;
7467
7468 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
7469 "hubd_cpr_post_user_callb");
7470
7471 switch (code) {
7472 case CB_CODE_CPR_CHKPT:
7473 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, hubd->h_log_handle,
7474 "hubd_cpr_post_user_callb: CB_CODE_CPR_CHKPT");
7475
7476 mutex_enter(HUBD_MUTEX(hubd));
7477
7478 /* turn off deathrow thread */
7479 hubd->h_cleanup_enabled = B_FALSE;
7480
7481 /* give up if deathrow thread doesn't exit */
7482 while ((hubd->h_cleanup_active == B_TRUE) && (retry++ < 3)) {
7483 mutex_exit(HUBD_MUTEX(hubd));
7484 delay(drv_usectohz(hubd_dip_cleanup_delay));
7485
7486 USB_DPRINTF_L2(DPRINT_MASK_EVENTS, hubd->h_log_handle,
7487 "hubd_cpr_post_user_callb, waiting for "
7488 "deathrow thread to exit");
7489 mutex_enter(HUBD_MUTEX(hubd));
7490 }
7491
7492 mutex_exit(HUBD_MUTEX(hubd));
7493
7494 /* save the state of the device */
7495 (void) hubd_pre_suspend_event_cb(hubd->h_dip);
7496
7497 return (B_TRUE);
7498 case CB_CODE_CPR_RESUME:
7499 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, hubd->h_log_handle,
7500 "hubd_cpr_post_user_callb: CB_CODE_CPR_RESUME");
7501
7502 /* restore the state of the device */
7503 (void) hubd_post_resume_event_cb(hubd->h_dip);
7504
7505 /* turn on deathrow thread */
7506 mutex_enter(HUBD_MUTEX(hubd));
7507 hubd->h_cleanup_enabled = B_TRUE;
7508 mutex_exit(HUBD_MUTEX(hubd));
7509
7510 hubd_schedule_cleanup(hubd->h_usba_device->usb_root_hub_dip);
7511
7512 return (B_TRUE);
7513 default:
7514
7515 return (B_FALSE);
7516 }
7517
7518 }
7519
7520
7521 /* register callback with cpr framework */
7522 void
hubd_register_cpr_callback(hubd_t * hubd)7523 hubd_register_cpr_callback(hubd_t *hubd)
7524 {
7525 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
7526 "hubd_register_cpr_callback");
7527
7528 mutex_enter(HUBD_MUTEX(hubd));
7529 hubd->h_cpr_cb =
7530 (hubd_cpr_t *)kmem_zalloc(sizeof (hubd_cpr_t), KM_SLEEP);
7531 mutex_exit(HUBD_MUTEX(hubd));
7532 mutex_init(&hubd->h_cpr_cb->lockp, NULL, MUTEX_DRIVER,
7533 hubd->h_dev_data->dev_iblock_cookie);
7534 hubd->h_cpr_cb->statep = hubd;
7535 hubd->h_cpr_cb->cpr.cc_lockp = &hubd->h_cpr_cb->lockp;
7536 hubd->h_cpr_cb->cpr.cc_id = callb_add(hubd_cpr_post_user_callb,
7537 (void *)hubd->h_cpr_cb, CB_CL_CPR_POST_USER, "hubd");
7538 }
7539
7540
7541 /* unregister callback with cpr framework */
7542 void
hubd_unregister_cpr_callback(hubd_t * hubd)7543 hubd_unregister_cpr_callback(hubd_t *hubd)
7544 {
7545 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
7546 "hubd_unregister_cpr_callback");
7547
7548 if (hubd->h_cpr_cb) {
7549 (void) callb_delete(hubd->h_cpr_cb->cpr.cc_id);
7550 mutex_destroy(&hubd->h_cpr_cb->lockp);
7551 mutex_enter(HUBD_MUTEX(hubd));
7552 kmem_free(hubd->h_cpr_cb, sizeof (hubd_cpr_t));
7553 mutex_exit(HUBD_MUTEX(hubd));
7554 }
7555 }
7556
7557
7558 /*
7559 * Power management
7560 *
7561 * create the pm components required for power management
7562 */
7563 static void
hubd_create_pm_components(dev_info_t * dip,hubd_t * hubd)7564 hubd_create_pm_components(dev_info_t *dip, hubd_t *hubd)
7565 {
7566 hub_power_t *hubpm;
7567
7568 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
7569 "hubd_create_pm_components: Begin");
7570
7571 /* Allocate the state structure */
7572 hubpm = kmem_zalloc(sizeof (hub_power_t), KM_SLEEP);
7573
7574 hubd->h_hubpm = hubpm;
7575 hubpm->hubp_hubd = hubd;
7576 hubpm->hubp_pm_capabilities = 0;
7577 hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
7578 hubpm->hubp_time_at_full_power = gethrtime();
7579 hubpm->hubp_min_pm_threshold = hubdi_min_pm_threshold * NANOSEC;
7580
7581 /* alloc memory to save power states of children */
7582 hubpm->hubp_child_pwrstate = (uint8_t *)
7583 kmem_zalloc(MAX_PORTS + 1, KM_SLEEP);
7584
7585 /*
7586 * if the enable remote wakeup fails
7587 * we still want to enable
7588 * parent notification so we can PM the children
7589 */
7590 usb_enable_parent_notification(dip);
7591
7592 if (usb_handle_remote_wakeup(dip,
7593 USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
7594 uint_t pwr_states;
7595
7596 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
7597 "hubd_create_pm_components: "
7598 "Remote Wakeup Enabled");
7599
7600 if (usb_create_pm_components(dip, &pwr_states) ==
7601 USB_SUCCESS) {
7602 mutex_enter(HUBD_MUTEX(hubd));
7603 hubpm->hubp_wakeup_enabled = 1;
7604 hubpm->hubp_pwr_states = (uint8_t)pwr_states;
7605
7606 /* we are busy now till end of the attach */
7607 hubd_pm_busy_component(hubd, dip, 0);
7608 mutex_exit(HUBD_MUTEX(hubd));
7609
7610 /* bring the device to full power */
7611 (void) pm_raise_power(dip, 0,
7612 USB_DEV_OS_FULL_PWR);
7613 }
7614 }
7615
7616 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
7617 "hubd_create_pm_components: END");
7618 }
7619
7620
7621 /*
7622 * Attachment point management
7623 */
7624 /* ARGSUSED */
7625 int
usba_hubdi_open(dev_info_t * dip,dev_t * devp,int flags,int otyp,cred_t * credp)7626 usba_hubdi_open(dev_info_t *dip, dev_t *devp, int flags, int otyp,
7627 cred_t *credp)
7628 {
7629 hubd_t *hubd;
7630
7631 if (otyp != OTYP_CHR)
7632 return (EINVAL);
7633
7634 hubd = hubd_get_soft_state(dip);
7635 if (hubd == NULL) {
7636 return (ENXIO);
7637 }
7638
7639 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
7640 "hubd_open:");
7641
7642 mutex_enter(HUBD_MUTEX(hubd));
7643 if ((flags & FEXCL) && (hubd->h_softstate & HUBD_SS_ISOPEN)) {
7644 mutex_exit(HUBD_MUTEX(hubd));
7645
7646 return (EBUSY);
7647 }
7648
7649 hubd->h_softstate |= HUBD_SS_ISOPEN;
7650 mutex_exit(HUBD_MUTEX(hubd));
7651
7652 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "opened");
7653
7654 return (0);
7655 }
7656
7657
7658 /* ARGSUSED */
7659 int
usba_hubdi_close(dev_info_t * dip,dev_t dev,int flag,int otyp,cred_t * credp)7660 usba_hubdi_close(dev_info_t *dip, dev_t dev, int flag, int otyp,
7661 cred_t *credp)
7662 {
7663 hubd_t *hubd;
7664
7665 if (otyp != OTYP_CHR) {
7666 return (EINVAL);
7667 }
7668
7669 hubd = hubd_get_soft_state(dip);
7670
7671 if (hubd == NULL) {
7672 return (ENXIO);
7673 }
7674
7675 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "hubd_close:");
7676
7677 mutex_enter(HUBD_MUTEX(hubd));
7678 hubd->h_softstate &= ~HUBD_SS_ISOPEN;
7679 mutex_exit(HUBD_MUTEX(hubd));
7680
7681 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "closed");
7682
7683 return (0);
7684 }
7685
7686
7687 /*
7688 * hubd_ioctl: cfgadm controls
7689 */
7690 /* ARGSUSED */
7691 int
usba_hubdi_ioctl(dev_info_t * self,dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)7692 usba_hubdi_ioctl(dev_info_t *self, dev_t dev, int cmd, intptr_t arg,
7693 int mode, cred_t *credp, int *rvalp)
7694 {
7695 int rv = 0;
7696 char *msg; /* for messages */
7697 hubd_t *hubd;
7698 usb_port_t port = 0;
7699 dev_info_t *child_dip = NULL;
7700 dev_info_t *rh_dip;
7701 devctl_ap_state_t ap_state;
7702 struct devctl_iocdata *dcp = NULL;
7703 usb_pipe_state_t prev_pipe_state = 0;
7704
7705 if ((hubd = hubd_get_soft_state(self)) == NULL) {
7706
7707 return (ENXIO);
7708 }
7709
7710 rh_dip = hubd->h_usba_device->usb_root_hub_dip;
7711
7712 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
7713 "usba_hubdi_ioctl: "
7714 "cmd=%x, arg=%lx, mode=%x, cred=%p, rval=%p dev=0x%lx",
7715 cmd, arg, mode, (void *)credp, (void *)rvalp, dev);
7716
7717 /* read devctl ioctl data */
7718 if ((cmd != DEVCTL_AP_CONTROL) &&
7719 (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)) {
7720
7721 return (EFAULT);
7722 }
7723
7724 /*
7725 * make sure the hub is connected before trying any
7726 * of the following operations:
7727 * configure, connect, disconnect
7728 */
7729 mutex_enter(HUBD_MUTEX(hubd));
7730
7731 switch (cmd) {
7732 case DEVCTL_AP_DISCONNECT:
7733 case DEVCTL_AP_UNCONFIGURE:
7734 case DEVCTL_AP_CONFIGURE:
7735 if (hubd->h_dev_state == USB_DEV_DISCONNECTED) {
7736 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
7737 "hubd: already gone");
7738 mutex_exit(HUBD_MUTEX(hubd));
7739 if (dcp) {
7740 ndi_dc_freehdl(dcp);
7741 }
7742
7743 return (EIO);
7744 }
7745
7746 /* FALLTHROUGH */
7747 case DEVCTL_AP_GETSTATE:
7748 if ((port = hubd_get_port_num(hubd, dcp)) == 0) {
7749 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
7750 "hubd: bad port");
7751 mutex_exit(HUBD_MUTEX(hubd));
7752 if (dcp) {
7753 ndi_dc_freehdl(dcp);
7754 }
7755
7756 return (EINVAL);
7757 }
7758 break;
7759
7760 case DEVCTL_AP_CONTROL:
7761
7762 break;
7763 default:
7764 mutex_exit(HUBD_MUTEX(hubd));
7765 if (dcp) {
7766 ndi_dc_freehdl(dcp);
7767 }
7768
7769 return (ENOTTY);
7770 }
7771
7772 /* should not happen, just in case */
7773 if (hubd->h_dev_state == USB_DEV_SUSPENDED) {
7774 mutex_exit(HUBD_MUTEX(hubd));
7775 if (dcp) {
7776 ndi_dc_freehdl(dcp);
7777 }
7778
7779 return (EIO);
7780 }
7781
7782 if (hubd->h_reset_port[port]) {
7783 USB_DPRINTF_L2(DPRINT_MASK_CBOPS, hubd->h_log_handle,
7784 "This port is resetting, just return");
7785 mutex_exit(HUBD_MUTEX(hubd));
7786 if (dcp) {
7787 ndi_dc_freehdl(dcp);
7788 }
7789
7790 return (EIO);
7791 }
7792
7793 hubd_pm_busy_component(hubd, hubd->h_dip, 0);
7794 mutex_exit(HUBD_MUTEX(hubd));
7795
7796 /* go full power */
7797 (void) pm_raise_power(hubd->h_dip, 0, USB_DEV_OS_FULL_PWR);
7798
7799 ndi_devi_enter(ddi_get_parent(rh_dip));
7800 ndi_devi_enter(rh_dip);
7801 ndi_devi_enter(hubd->h_dip);
7802
7803 mutex_enter(HUBD_MUTEX(hubd));
7804
7805 hubd->h_hotplug_thread++;
7806
7807 /* stop polling if it was active */
7808 if (hubd->h_ep1_ph) {
7809 mutex_exit(HUBD_MUTEX(hubd));
7810 (void) usb_pipe_get_state(hubd->h_ep1_ph, &prev_pipe_state,
7811 USB_FLAGS_SLEEP);
7812 mutex_enter(HUBD_MUTEX(hubd));
7813
7814 if (prev_pipe_state == USB_PIPE_STATE_ACTIVE) {
7815 hubd_stop_polling(hubd);
7816 }
7817 }
7818
7819 switch (cmd) {
7820 case DEVCTL_AP_DISCONNECT:
7821 if (hubd_delete_child(hubd, port,
7822 NDI_DEVI_REMOVE, B_FALSE) != USB_SUCCESS) {
7823 rv = EIO;
7824 }
7825
7826 break;
7827 case DEVCTL_AP_UNCONFIGURE:
7828 if (hubd_delete_child(hubd, port,
7829 NDI_UNCONFIG, B_FALSE) != USB_SUCCESS) {
7830 rv = EIO;
7831 }
7832
7833 break;
7834 case DEVCTL_AP_CONFIGURE:
7835 /* toggle port */
7836 if (hubd_toggle_port(hubd, port) != USB_SUCCESS) {
7837 rv = EIO;
7838
7839 break;
7840 }
7841
7842 (void) hubd_handle_port_connect(hubd, port);
7843 child_dip = hubd_get_child_dip(hubd, port);
7844 mutex_exit(HUBD_MUTEX(hubd));
7845
7846 ndi_devi_exit(hubd->h_dip);
7847 ndi_devi_exit(rh_dip);
7848 ndi_devi_exit(ddi_get_parent(rh_dip));
7849 if (child_dip == NULL) {
7850 rv = EIO;
7851 } else {
7852 ndi_hold_devi(child_dip);
7853 if (ndi_devi_online(child_dip, 0) != NDI_SUCCESS)
7854 rv = EIO;
7855 ndi_rele_devi(child_dip);
7856 }
7857 ndi_devi_enter(ddi_get_parent(rh_dip));
7858 ndi_devi_enter(rh_dip);
7859 ndi_devi_enter(hubd->h_dip);
7860
7861 mutex_enter(HUBD_MUTEX(hubd));
7862
7863 break;
7864 case DEVCTL_AP_GETSTATE:
7865 switch (hubd_cfgadm_state(hubd, port)) {
7866 case HUBD_CFGADM_DISCONNECTED:
7867 /* port previously 'disconnected' by cfgadm */
7868 ap_state.ap_rstate = AP_RSTATE_DISCONNECTED;
7869 ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
7870 ap_state.ap_condition = AP_COND_OK;
7871
7872 break;
7873 case HUBD_CFGADM_UNCONFIGURED:
7874 ap_state.ap_rstate = AP_RSTATE_CONNECTED;
7875 ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
7876 ap_state.ap_condition = AP_COND_OK;
7877
7878 break;
7879 case HUBD_CFGADM_CONFIGURED:
7880 ap_state.ap_rstate = AP_RSTATE_CONNECTED;
7881 ap_state.ap_ostate = AP_OSTATE_CONFIGURED;
7882 ap_state.ap_condition = AP_COND_OK;
7883
7884 break;
7885 case HUBD_CFGADM_STILL_REFERENCED:
7886 ap_state.ap_rstate = AP_RSTATE_EMPTY;
7887 ap_state.ap_ostate = AP_OSTATE_CONFIGURED;
7888 ap_state.ap_condition = AP_COND_UNUSABLE;
7889
7890 break;
7891 case HUBD_CFGADM_EMPTY:
7892 default:
7893 ap_state.ap_rstate = AP_RSTATE_EMPTY;
7894 ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
7895 ap_state.ap_condition = AP_COND_OK;
7896
7897 break;
7898 }
7899
7900 ap_state.ap_last_change = (time_t)-1;
7901 ap_state.ap_error_code = 0;
7902 ap_state.ap_in_transition = 0;
7903
7904 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
7905 "DEVCTL_AP_GETSTATE: "
7906 "ostate=0x%x, rstate=0x%x, condition=0x%x",
7907 ap_state.ap_ostate,
7908 ap_state.ap_rstate, ap_state.ap_condition);
7909
7910 /* copy the return-AP-state information to the user space */
7911 if (ndi_dc_return_ap_state(&ap_state, dcp) != NDI_SUCCESS) {
7912 rv = EFAULT;
7913 }
7914
7915 break;
7916 case DEVCTL_AP_CONTROL:
7917 {
7918 /*
7919 * Generic devctl for hardware-specific functionality.
7920 * For list of sub-commands see hubd_impl.h
7921 */
7922 hubd_ioctl_data_t ioc; /* for 64 byte copies */
7923
7924 /* copy user ioctl data in first */
7925 #ifdef _MULTI_DATAMODEL
7926 if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
7927 hubd_ioctl_data_32_t ioc32;
7928
7929 if (ddi_copyin((void *)arg, (void *)&ioc32,
7930 sizeof (ioc32), mode) != 0) {
7931 rv = EFAULT;
7932
7933 break;
7934 }
7935 ioc.cmd = (uint_t)ioc32.cmd;
7936 ioc.port = (uint_t)ioc32.port;
7937 ioc.get_size = (uint_t)ioc32.get_size;
7938 ioc.buf = (caddr_t)(uintptr_t)ioc32.buf;
7939 ioc.bufsiz = (uint_t)ioc32.bufsiz;
7940 ioc.misc_arg = (uint_t)ioc32.misc_arg;
7941 } else
7942 #endif /* _MULTI_DATAMODEL */
7943 if (ddi_copyin((void *)arg, (void *)&ioc, sizeof (ioc),
7944 mode) != 0) {
7945 rv = EFAULT;
7946
7947 break;
7948 }
7949
7950 USB_DPRINTF_L3(DPRINT_MASK_CBOPS, hubd->h_log_handle,
7951 "DEVCTL_AP_CONTROL: ioc: cmd=0x%x port=%d get_size=%d"
7952 "\n\tbuf=0x%p, bufsiz=%d, misc_arg=%d", ioc.cmd,
7953 ioc.port, ioc.get_size, (void *)ioc.buf, ioc.bufsiz,
7954 ioc.misc_arg);
7955
7956 /*
7957 * To avoid BE/LE and 32/64 issues, a get_size always
7958 * returns a 32-bit number.
7959 */
7960 if (ioc.get_size != 0 && ioc.bufsiz != (sizeof (uint32_t))) {
7961 rv = EINVAL;
7962
7963 break;
7964 }
7965
7966 switch (ioc.cmd) {
7967 case USB_DESCR_TYPE_DEV:
7968 msg = "DEVCTL_AP_CONTROL: GET_DEVICE_DESC";
7969 if (ioc.get_size) {
7970 /* uint32 so this works 32/64 */
7971 uint32_t size = sizeof (usb_dev_descr_t);
7972
7973 if (ddi_copyout((void *)&size, ioc.buf,
7974 ioc.bufsiz, mode) != 0) {
7975 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
7976 hubd->h_log_handle,
7977 "%s: get_size copyout failed", msg);
7978 rv = EIO;
7979
7980 break;
7981 }
7982 } else { /* send out the actual descr */
7983 usb_dev_descr_t *dev_descrp;
7984
7985 /* check child_dip */
7986 if ((child_dip = hubd_get_child_dip(hubd,
7987 ioc.port)) == NULL) {
7988 rv = EINVAL;
7989
7990 break;
7991 }
7992
7993 dev_descrp = usb_get_dev_descr(child_dip);
7994 if (ioc.bufsiz != sizeof (*dev_descrp)) {
7995 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
7996 hubd->h_log_handle,
7997 "%s: bufsize passed (%d) != sizeof "
7998 "usba_device_descr_t (%d)", msg,
7999 ioc.bufsiz, dev_descrp->bLength);
8000 rv = EINVAL;
8001
8002 break;
8003 }
8004
8005 if (ddi_copyout((void *)dev_descrp,
8006 ioc.buf, ioc.bufsiz, mode) != 0) {
8007 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8008 hubd->h_log_handle,
8009 "%s: copyout failed.", msg);
8010 rv = EIO;
8011
8012 break;
8013 }
8014 }
8015 break;
8016 case USB_DESCR_TYPE_STRING:
8017 {
8018 char *str;
8019 uint32_t size;
8020 usba_device_t *usba_device;
8021
8022 msg = "DEVCTL_AP_CONTROL: GET_STRING_DESCR";
8023 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
8024 "%s: string request: %d", msg, ioc.misc_arg);
8025
8026 /* recheck */
8027 if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) ==
8028 NULL) {
8029 rv = EINVAL;
8030
8031 break;
8032 }
8033 usba_device = usba_get_usba_device(child_dip);
8034
8035 switch (ioc.misc_arg) {
8036 case HUBD_MFG_STR:
8037 str = usba_device->usb_mfg_str;
8038
8039 break;
8040 case HUBD_PRODUCT_STR:
8041 str = usba_device->usb_product_str;
8042
8043 break;
8044 case HUBD_SERIALNO_STR:
8045 str = usba_device->usb_serialno_str;
8046
8047 break;
8048 case HUBD_CFG_DESCR_STR:
8049 mutex_enter(&usba_device->usb_mutex);
8050 str = usba_device->usb_cfg_str_descr[
8051 usba_device->usb_active_cfg_ndx];
8052 mutex_exit(&usba_device->usb_mutex);
8053
8054 break;
8055 default:
8056 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8057 hubd->h_log_handle,
8058 "%s: Invalid string request", msg);
8059 rv = EINVAL;
8060
8061 break;
8062 } /* end of switch */
8063
8064 if (rv != 0) {
8065
8066 break;
8067 }
8068
8069 size = (str != NULL) ? strlen(str) + 1 : 0;
8070 if (ioc.get_size) {
8071 if (ddi_copyout((void *)&size, ioc.buf,
8072 ioc.bufsiz, mode) != 0) {
8073 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8074 hubd->h_log_handle,
8075 "%s: copyout of size failed.", msg);
8076 rv = EIO;
8077
8078 break;
8079 }
8080 } else {
8081 if (size == 0) {
8082 USB_DPRINTF_L3(DPRINT_MASK_CBOPS,
8083 hubd->h_log_handle,
8084 "%s: String is NULL", msg);
8085 rv = EINVAL;
8086
8087 break;
8088 }
8089
8090 if (ioc.bufsiz != size) {
8091 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8092 hubd->h_log_handle,
8093 "%s: string buf size wrong", msg);
8094 rv = EINVAL;
8095
8096 break;
8097 }
8098
8099 if (ddi_copyout((void *)str, ioc.buf,
8100 ioc.bufsiz, mode) != 0) {
8101 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8102 hubd->h_log_handle,
8103 "%s: copyout failed.", msg);
8104 rv = EIO;
8105
8106 break;
8107 }
8108 }
8109 break;
8110 }
8111 case HUBD_GET_CFGADM_NAME:
8112 {
8113 uint32_t name_len;
8114 const char *name;
8115
8116 /* recheck */
8117 if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) ==
8118 NULL) {
8119 rv = EINVAL;
8120
8121 break;
8122 }
8123 name = ddi_node_name(child_dip);
8124 if (name == NULL) {
8125 name = "unsupported";
8126 }
8127 name_len = strlen(name) + 1;
8128
8129 msg = "DEVCTL_AP_CONTROL: HUBD_GET_CFGADM_NAME";
8130 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
8131 "%s: name=%s name_len=%d", msg, name, name_len);
8132
8133 if (ioc.get_size) {
8134 if (ddi_copyout((void *)&name_len,
8135 ioc.buf, ioc.bufsiz, mode) != 0) {
8136 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8137 hubd->h_log_handle,
8138 "%s: copyout of size failed", msg);
8139 rv = EIO;
8140
8141 break;
8142 }
8143 } else {
8144 if (ioc.bufsiz != name_len) {
8145 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8146 hubd->h_log_handle,
8147 "%s: string buf length wrong", msg);
8148 rv = EINVAL;
8149
8150 break;
8151 }
8152
8153 if (ddi_copyout((void *)name, ioc.buf,
8154 ioc.bufsiz, mode) != 0) {
8155 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8156 hubd->h_log_handle,
8157 "%s: copyout failed.", msg);
8158 rv = EIO;
8159
8160 break;
8161 }
8162 }
8163
8164 break;
8165 }
8166
8167 /*
8168 * Return the config index for the currently-configured
8169 * configuration.
8170 */
8171 case HUBD_GET_CURRENT_CONFIG:
8172 {
8173 uint_t config_index;
8174 uint32_t size = sizeof (config_index);
8175 usba_device_t *usba_device;
8176
8177 msg = "DEVCTL_AP_CONTROL: GET_CURRENT_CONFIG";
8178 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
8179 "%s", msg);
8180
8181 /*
8182 * Return the config index for the configuration
8183 * currently in use.
8184 * Recheck if child_dip exists
8185 */
8186 if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) ==
8187 NULL) {
8188 rv = EINVAL;
8189
8190 break;
8191 }
8192
8193 usba_device = usba_get_usba_device(child_dip);
8194 mutex_enter(&usba_device->usb_mutex);
8195 config_index = usba_device->usb_active_cfg_ndx;
8196 mutex_exit(&usba_device->usb_mutex);
8197
8198 if (ioc.get_size) {
8199 if (ddi_copyout((void *)&size,
8200 ioc.buf, ioc.bufsiz, mode) != 0) {
8201 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8202 hubd->h_log_handle,
8203 "%s: copyout of size failed.", msg);
8204 rv = EIO;
8205
8206 break;
8207 }
8208 } else {
8209 if (ioc.bufsiz != size) {
8210 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8211 hubd->h_log_handle,
8212 "%s: buffer size wrong", msg);
8213 rv = EINVAL;
8214
8215 break;
8216 }
8217 if (ddi_copyout((void *)&config_index,
8218 ioc.buf, ioc.bufsiz, mode) != 0) {
8219 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8220 hubd->h_log_handle,
8221 "%s: copyout failed", msg);
8222 rv = EIO;
8223 }
8224 }
8225
8226 break;
8227 }
8228 case HUBD_GET_DEVICE_PATH:
8229 {
8230 char *path;
8231 uint32_t size;
8232
8233 msg = "DEVCTL_AP_CONTROL: GET_DEVICE_PATH";
8234 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
8235 "%s", msg);
8236
8237 /* Recheck if child_dip exists */
8238 if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) ==
8239 NULL) {
8240 rv = EINVAL;
8241
8242 break;
8243 }
8244
8245 /* ddi_pathname doesn't supply /devices, so we do. */
8246 path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
8247 (void) strcpy(path, "/devices");
8248 (void) ddi_pathname(child_dip, path + strlen(path));
8249 size = strlen(path) + 1;
8250
8251 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
8252 "%s: device path=%s size=%d", msg, path, size);
8253
8254 if (ioc.get_size) {
8255 if (ddi_copyout((void *)&size,
8256 ioc.buf, ioc.bufsiz, mode) != 0) {
8257
8258 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8259 hubd->h_log_handle,
8260 "%s: copyout of size failed.", msg);
8261 rv = EIO;
8262 }
8263 } else {
8264 if (ioc.bufsiz != size) {
8265 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8266 hubd->h_log_handle,
8267 "%s: buffer wrong size.", msg);
8268 rv = EINVAL;
8269 } else if (ddi_copyout((void *)path,
8270 ioc.buf, ioc.bufsiz, mode) != 0) {
8271 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8272 hubd->h_log_handle,
8273 "%s: copyout failed.", msg);
8274 rv = EIO;
8275 }
8276 }
8277 kmem_free(path, MAXPATHLEN);
8278
8279 break;
8280 }
8281 case HUBD_REFRESH_DEVDB:
8282 msg = "DEVCTL_AP_CONTROL: HUBD_REFRESH_DEVDB";
8283 USB_DPRINTF_L3(DPRINT_MASK_CBOPS, hubd->h_log_handle,
8284 "%s", msg);
8285
8286 if ((rv = usba_devdb_refresh()) != USB_SUCCESS) {
8287 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
8288 hubd->h_log_handle,
8289 "%s: Failed: %d", msg, rv);
8290 rv = EIO;
8291 }
8292
8293 break;
8294 default:
8295 rv = ENOTSUP;
8296 } /* end switch */
8297
8298 break;
8299 }
8300
8301 default:
8302 rv = ENOTTY;
8303 }
8304
8305 if (dcp) {
8306 ndi_dc_freehdl(dcp);
8307 }
8308
8309 /* allow hotplug thread now */
8310 hubd->h_hotplug_thread--;
8311
8312 if ((hubd->h_dev_state == USB_DEV_ONLINE) &&
8313 hubd->h_ep1_ph && (prev_pipe_state == USB_PIPE_STATE_ACTIVE)) {
8314 hubd_start_polling(hubd, 0);
8315 }
8316 mutex_exit(HUBD_MUTEX(hubd));
8317
8318 ndi_devi_exit(hubd->h_dip);
8319 ndi_devi_exit(rh_dip);
8320 ndi_devi_exit(ddi_get_parent(rh_dip));
8321
8322 mutex_enter(HUBD_MUTEX(hubd));
8323 hubd_pm_idle_component(hubd, hubd->h_dip, 0);
8324 mutex_exit(HUBD_MUTEX(hubd));
8325
8326 return (rv);
8327 }
8328
8329
8330 /*
8331 * Helper func used only to help construct the names for the attachment point
8332 * minor nodes. Used only in usba_hubdi_attach.
8333 * Returns whether it found ancestry or not (USB_SUCCESS if yes).
8334 * ports between the root hub and the device represented by dip.
8335 * E.g., "2.4.3.1" means this device is
8336 * plugged into port 1 of a hub that is
8337 * plugged into port 3 of a hub that is
8338 * plugged into port 4 of a hub that is
8339 * plugged into port 2 of the root hub.
8340 * NOTE: Max ap_id path len is HUBD_APID_NAMELEN (32 chars), which is
8341 * more than sufficient (as hubs are a max 6 levels deep, port needs 3
8342 * chars plus NULL each)
8343 */
8344 void
hubd_get_ancestry_str(hubd_t * hubd)8345 hubd_get_ancestry_str(hubd_t *hubd)
8346 {
8347 char ap_name[HUBD_APID_NAMELEN];
8348 dev_info_t *pdip;
8349 hubd_t *phubd;
8350 usb_port_t port;
8351
8352 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
8353 "hubd_get_ancestry_str: hubd=0x%p", (void *)hubd);
8354
8355 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
8356
8357 /*
8358 * The function is extended to support wire adapter class
8359 * devices introduced by WUSB spec. The node name is no
8360 * longer "hub" only.
8361 * Generate the ap_id str based on the parent and child
8362 * relationship instead of retrieving it from the hub
8363 * device path, which simplifies the algorithm.
8364 */
8365 if (usba_is_root_hub(hubd->h_dip)) {
8366 hubd->h_ancestry_str[0] = '\0';
8367 } else {
8368 port = hubd->h_usba_device->usb_port;
8369 mutex_exit(HUBD_MUTEX(hubd));
8370
8371 pdip = ddi_get_parent(hubd->h_dip);
8372 /*
8373 * The parent of wire adapter device might be usb_mid.
8374 * Need to look further up for hub device
8375 */
8376 if (strcmp(ddi_driver_name(pdip), "usb_mid") == 0) {
8377 pdip = ddi_get_parent(pdip);
8378 ASSERT(pdip != NULL);
8379 }
8380
8381 phubd = hubd_get_soft_state(pdip);
8382
8383 mutex_enter(HUBD_MUTEX(phubd));
8384 (void) snprintf(ap_name, HUBD_APID_NAMELEN, "%s%d",
8385 phubd->h_ancestry_str, port);
8386 mutex_exit(HUBD_MUTEX(phubd));
8387
8388 mutex_enter(HUBD_MUTEX(hubd));
8389 (void) strcpy(hubd->h_ancestry_str, ap_name);
8390 (void) strcat(hubd->h_ancestry_str, ".");
8391 }
8392 }
8393
8394
8395 /* Get which port to operate on. */
8396 static usb_port_t
hubd_get_port_num(hubd_t * hubd,struct devctl_iocdata * dcp)8397 hubd_get_port_num(hubd_t *hubd, struct devctl_iocdata *dcp)
8398 {
8399 int32_t port;
8400
8401 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
8402
8403 /* Get which port to operate on. */
8404 if (nvlist_lookup_int32(ndi_dc_get_ap_data(dcp), "port", &port) != 0) {
8405 USB_DPRINTF_L2(DPRINT_MASK_CBOPS, hubd->h_log_handle,
8406 "hubd_get_port_num: port lookup failed");
8407 port = 0;
8408 }
8409
8410 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
8411 "hubd_get_port_num: hubd=0x%p, port=%d", (void *)hubd, port);
8412
8413 return ((usb_port_t)port);
8414 }
8415
8416
8417 /* check if child still exists */
8418 static dev_info_t *
hubd_get_child_dip(hubd_t * hubd,usb_port_t port)8419 hubd_get_child_dip(hubd_t *hubd, usb_port_t port)
8420 {
8421 dev_info_t *child_dip = hubd->h_children_dips[port];
8422
8423 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
8424 "hubd_get_child_dip: hubd=0x%p, port=%d", (void *)hubd, port);
8425
8426 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
8427
8428 return (child_dip);
8429 }
8430
8431
8432 /*
8433 * hubd_cfgadm_state:
8434 *
8435 * child_dip list port_state cfgadm_state
8436 * -------------- ---------- ------------
8437 * != NULL connected configured or
8438 * unconfigured
8439 * != NULL not connected disconnect but
8440 * busy/still referenced
8441 * NULL connected logically disconnected
8442 * NULL not connected empty
8443 */
8444 static uint_t
hubd_cfgadm_state(hubd_t * hubd,usb_port_t port)8445 hubd_cfgadm_state(hubd_t *hubd, usb_port_t port)
8446 {
8447 uint_t state;
8448 dev_info_t *child_dip = hubd_get_child_dip(hubd, port);
8449
8450 if (child_dip) {
8451 if (hubd->h_port_state[port] & PORT_STATUS_CCS) {
8452 /*
8453 * connected, now check if driver exists
8454 */
8455 if (DEVI_IS_DEVICE_OFFLINE(child_dip) ||
8456 !i_ddi_devi_attached(child_dip)) {
8457 state = HUBD_CFGADM_UNCONFIGURED;
8458 } else {
8459 state = HUBD_CFGADM_CONFIGURED;
8460 }
8461 } else {
8462 /*
8463 * this means that the dip is around for
8464 * a device that is still referenced but
8465 * has been yanked out. So the cfgadm info
8466 * for this state should be EMPTY (port empty)
8467 * and CONFIGURED (dip still valid).
8468 */
8469 state = HUBD_CFGADM_STILL_REFERENCED;
8470 }
8471 } else {
8472 /* connected but no child dip */
8473 if (hubd->h_port_state[port] & PORT_STATUS_CCS) {
8474 /* logically disconnected */
8475 state = HUBD_CFGADM_DISCONNECTED;
8476 } else {
8477 /* physically disconnected */
8478 state = HUBD_CFGADM_EMPTY;
8479 }
8480 }
8481
8482 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
8483 "hubd_cfgadm_state: hubd=0x%p, port=%d state=0x%x",
8484 (void *)hubd, port, state);
8485
8486 return (state);
8487 }
8488
8489
8490 /*
8491 * hubd_toggle_port:
8492 */
8493 static int
hubd_toggle_port(hubd_t * hubd,usb_port_t port)8494 hubd_toggle_port(hubd_t *hubd, usb_port_t port)
8495 {
8496 int wait;
8497 uint_t retry;
8498 uint16_t status;
8499 uint16_t change;
8500
8501 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
8502 "hubd_toggle_port: hubd=0x%p, port=%d", (void *)hubd, port);
8503
8504 if ((hubd_disable_port_power(hubd, port)) != USB_SUCCESS) {
8505
8506 return (USB_FAILURE);
8507 }
8508
8509 /*
8510 * see hubd_enable_all_port_power() which
8511 * requires longer delay for hubs.
8512 */
8513 mutex_exit(HUBD_MUTEX(hubd));
8514 delay(drv_usectohz(hubd_device_delay / 10));
8515 mutex_enter(HUBD_MUTEX(hubd));
8516
8517 /*
8518 * According to section 11.11 of USB, for hubs with no power
8519 * switches, bPwrOn2PwrGood is zero. But we wait for some
8520 * arbitrary time to enable power to become stable.
8521 *
8522 * If an hub supports port power swicthing, we need to wait
8523 * at least 20ms before accesing corresonding usb port. Note
8524 * this member is stored in the h_power_good member.
8525 */
8526 if ((hubd->h_hub_chars & HUB_CHARS_NO_POWER_SWITCHING) ||
8527 (hubd->h_power_good == 0)) {
8528 wait = hubd_device_delay / 10;
8529 } else {
8530 wait = max(HUB_DEFAULT_POPG,
8531 hubd->h_power_good) * 2 * 1000;
8532 }
8533
8534 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
8535 "hubd_toggle_port: popg=%d wait=%d",
8536 hubd->h_power_good, wait);
8537
8538 retry = 0;
8539
8540 do {
8541 (void) hubd_enable_port_power(hubd, port);
8542
8543 mutex_exit(HUBD_MUTEX(hubd));
8544 delay(drv_usectohz(wait));
8545 mutex_enter(HUBD_MUTEX(hubd));
8546
8547 /* Get port status */
8548 (void) hubd_determine_port_status(hubd, port,
8549 &status, &change, NULL, 0);
8550
8551 /* For retry if any, use some extra delay */
8552 wait = max(wait, hubd_device_delay / 10);
8553
8554 retry++;
8555
8556 } while ((!(status & PORT_STATUS_PPS)) && (retry < HUBD_PORT_RETRY));
8557
8558 /* Print warning message if port has no power */
8559 if (!(status & PORT_STATUS_PPS)) {
8560
8561 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
8562 "hubd_toggle_port: port %d power-on failed, "
8563 "port status 0x%x", port, status);
8564
8565 return (USB_FAILURE);
8566 }
8567
8568 return (USB_SUCCESS);
8569 }
8570
8571
8572 /*
8573 * hubd_init_power_budget:
8574 * Init power budget variables in hubd structure. According
8575 * to USB spec, the power budget rules are:
8576 * 1. local-powered hubs including root-hubs can supply
8577 * 500mA to each port at maximum
8578 * 2. two bus-powered hubs are not allowed to concatenate
8579 * 3. bus-powered hubs can supply 100mA to each port at
8580 * maximum, and the power consumed by all downstream
8581 * ports and the hub itself cannot exceed the max power
8582 * supplied by the upstream port, i.e., 500mA
8583 * The routine is only called during hub attach time
8584 */
8585 static int
hubd_init_power_budget(hubd_t * hubd)8586 hubd_init_power_budget(hubd_t *hubd)
8587 {
8588 uint16_t status = 0;
8589 usba_device_t *hubd_ud = NULL;
8590 size_t size;
8591 usb_cfg_descr_t cfg_descr;
8592 dev_info_t *pdip = NULL;
8593 hubd_t *phubd = NULL;
8594
8595 if (hubd->h_ignore_pwr_budget) {
8596
8597 return (USB_SUCCESS);
8598 }
8599
8600 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
8601 "hubd_init_power_budget:");
8602
8603 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
8604 ASSERT(hubd->h_default_pipe != 0);
8605 mutex_exit(HUBD_MUTEX(hubd));
8606
8607 /* get device status */
8608 if ((usb_get_status(hubd->h_dip, hubd->h_default_pipe,
8609 HUB_GET_DEVICE_STATUS_TYPE,
8610 0, &status, 0)) != USB_SUCCESS) {
8611 mutex_enter(HUBD_MUTEX(hubd));
8612
8613 return (USB_FAILURE);
8614 }
8615
8616 hubd_ud = usba_get_usba_device(hubd->h_dip);
8617
8618 size = usb_parse_cfg_descr(hubd_ud->usb_cfg, hubd_ud->usb_cfg_length,
8619 &cfg_descr, USB_CFG_DESCR_SIZE);
8620
8621 if (size != USB_CFG_DESCR_SIZE) {
8622 USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
8623 "get hub configuration descriptor failed");
8624 mutex_enter(HUBD_MUTEX(hubd));
8625
8626 return (USB_FAILURE);
8627 }
8628
8629 mutex_enter(HUBD_MUTEX(hubd));
8630
8631 hubd->h_local_pwr_capable = (cfg_descr.bmAttributes &
8632 USB_CFG_ATTR_SELFPWR);
8633
8634 if (hubd->h_local_pwr_capable) {
8635 USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
8636 "hub is capable of local power");
8637 }
8638
8639 hubd->h_local_pwr_on = (status &
8640 USB_DEV_SLF_PWRD_STATUS) && hubd->h_local_pwr_capable;
8641
8642 if (hubd->h_local_pwr_on) {
8643 USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
8644 "hub is local-powered");
8645
8646 hubd->h_pwr_limit = (USB_PWR_UNIT_LOAD *
8647 USB_HIGH_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT;
8648 } else {
8649 hubd->h_pwr_limit = (USB_PWR_UNIT_LOAD *
8650 USB_LOW_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT;
8651
8652 hubd->h_pwr_left = (USB_PWR_UNIT_LOAD *
8653 USB_HIGH_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT;
8654
8655 ASSERT(!usba_is_root_hub(hubd->h_dip));
8656
8657 if (!usba_is_root_hub(hubd->h_dip)) {
8658 /*
8659 * two bus-powered hubs are not
8660 * allowed to be concatenated
8661 */
8662 mutex_exit(HUBD_MUTEX(hubd));
8663
8664 pdip = ddi_get_parent(hubd->h_dip);
8665 phubd = hubd_get_soft_state(pdip);
8666 ASSERT(phubd != NULL);
8667
8668 if (!phubd->h_ignore_pwr_budget) {
8669 mutex_enter(HUBD_MUTEX(phubd));
8670 if (phubd->h_local_pwr_on == B_FALSE) {
8671 USB_DPRINTF_L1(DPRINT_MASK_HUB,
8672 hubd->h_log_handle,
8673 "two bus-powered hubs cannot "
8674 "be concatenated");
8675
8676 mutex_exit(HUBD_MUTEX(phubd));
8677 mutex_enter(HUBD_MUTEX(hubd));
8678
8679 return (USB_FAILURE);
8680 }
8681 mutex_exit(HUBD_MUTEX(phubd));
8682 }
8683
8684 mutex_enter(HUBD_MUTEX(hubd));
8685
8686 USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
8687 "hub is bus-powered");
8688 } else {
8689 USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
8690 "root-hub must be local-powered");
8691 }
8692
8693 /*
8694 * Subtract the power consumed by the hub itself
8695 * and get the power that can be supplied to
8696 * downstream ports
8697 */
8698 hubd->h_pwr_left -= hubd->h_current / USB_CFG_DESCR_PWR_UNIT;
8699 if (hubd->h_pwr_left < 0) {
8700 USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
8701 "hubd->h_pwr_left is less than bHubContrCurrent, "
8702 "should fail");
8703
8704 return (USB_FAILURE);
8705 }
8706 }
8707
8708 return (USB_SUCCESS);
8709 }
8710
8711
8712 /*
8713 * usba_hubdi_check_power_budget:
8714 * Check if the hub has enough power budget to allow a
8715 * child device to select a configuration of config_index.
8716 */
8717 int
usba_hubdi_check_power_budget(dev_info_t * dip,usba_device_t * child_ud,uint_t config_index)8718 usba_hubdi_check_power_budget(dev_info_t *dip, usba_device_t *child_ud,
8719 uint_t config_index)
8720 {
8721 int16_t pwr_left, pwr_limit, pwr_required;
8722 size_t size;
8723 usb_cfg_descr_t cfg_descr;
8724 hubd_t *hubd;
8725
8726 if ((hubd = hubd_get_soft_state(dip)) == NULL) {
8727
8728 return (USB_FAILURE);
8729 }
8730
8731 if (hubd->h_ignore_pwr_budget) {
8732
8733 return (USB_SUCCESS);
8734 }
8735
8736 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8737 "usba_hubdi_check_power_budget: "
8738 "dip=0x%p child_ud=0x%p conf_index=%d", (void *)dip,
8739 (void *)child_ud, config_index);
8740
8741 mutex_enter(HUBD_MUTEX(hubd));
8742 pwr_limit = hubd->h_pwr_limit;
8743 if (hubd->h_local_pwr_on == B_FALSE) {
8744 pwr_left = hubd->h_pwr_left;
8745 pwr_limit = (pwr_limit <= pwr_left) ? pwr_limit : pwr_left;
8746 }
8747 mutex_exit(HUBD_MUTEX(hubd));
8748
8749 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8750 "usba_hubdi_check_power_budget: "
8751 "available power is %dmA", pwr_limit * USB_CFG_DESCR_PWR_UNIT);
8752
8753 size = usb_parse_cfg_descr(
8754 child_ud->usb_cfg_array[config_index], USB_CFG_DESCR_SIZE,
8755 &cfg_descr, USB_CFG_DESCR_SIZE);
8756
8757 if (size != USB_CFG_DESCR_SIZE) {
8758 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8759 "get hub configuration descriptor failed");
8760
8761 return (USB_FAILURE);
8762 }
8763
8764 pwr_required = cfg_descr.bMaxPower;
8765
8766 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8767 "usba_hubdi_check_power_budget: "
8768 "child bmAttributes=0x%x bMaxPower=%d "
8769 "with config_index=%d", cfg_descr.bmAttributes,
8770 pwr_required, config_index);
8771
8772 if (pwr_required > pwr_limit) {
8773 USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8774 "configuration %d for device %s %s at port %d "
8775 "exceeds power available for this port, please "
8776 "re-insert your device into another hub port which "
8777 "has enough power",
8778 config_index,
8779 child_ud->usb_mfg_str,
8780 child_ud->usb_product_str,
8781 child_ud->usb_port);
8782
8783 return (USB_FAILURE);
8784 }
8785
8786 return (USB_SUCCESS);
8787 }
8788
8789
8790 /*
8791 * usba_hubdi_incr_power_budget:
8792 * Increase the hub power budget value when a child device
8793 * is removed from a bus-powered hub port.
8794 */
8795 void
usba_hubdi_incr_power_budget(dev_info_t * dip,usba_device_t * child_ud)8796 usba_hubdi_incr_power_budget(dev_info_t *dip, usba_device_t *child_ud)
8797 {
8798 uint16_t pwr_value;
8799 hubd_t *hubd = hubd_get_soft_state(dip);
8800
8801 ASSERT(hubd != NULL);
8802
8803 if (hubd->h_ignore_pwr_budget) {
8804
8805 return;
8806 }
8807
8808 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
8809 "usba_hubdi_incr_power_budget: "
8810 "dip=0x%p child_ud=0x%p", (void *)dip, (void *)child_ud);
8811
8812 mutex_enter(HUBD_MUTEX(hubd));
8813 if (hubd->h_local_pwr_on == B_TRUE) {
8814 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
8815 "usba_hubdi_incr_power_budget: "
8816 "hub is local powered");
8817 mutex_exit(HUBD_MUTEX(hubd));
8818
8819 return;
8820 }
8821 mutex_exit(HUBD_MUTEX(hubd));
8822
8823 mutex_enter(&child_ud->usb_mutex);
8824 if (child_ud->usb_pwr_from_hub == 0) {
8825 mutex_exit(&child_ud->usb_mutex);
8826
8827 return;
8828 }
8829 pwr_value = child_ud->usb_pwr_from_hub;
8830 mutex_exit(&child_ud->usb_mutex);
8831
8832 mutex_enter(HUBD_MUTEX(hubd));
8833 hubd->h_pwr_left += pwr_value;
8834
8835 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
8836 "usba_hubdi_incr_power_budget: "
8837 "available power is %dmA, increased by %dmA",
8838 hubd->h_pwr_left * USB_CFG_DESCR_PWR_UNIT,
8839 pwr_value * USB_CFG_DESCR_PWR_UNIT);
8840
8841 mutex_exit(HUBD_MUTEX(hubd));
8842
8843 mutex_enter(&child_ud->usb_mutex);
8844 child_ud->usb_pwr_from_hub = 0;
8845 mutex_exit(&child_ud->usb_mutex);
8846 }
8847
8848
8849 /*
8850 * usba_hubdi_decr_power_budget:
8851 * Decrease the hub power budget value when a child device
8852 * is inserted to a bus-powered hub port.
8853 */
8854 void
usba_hubdi_decr_power_budget(dev_info_t * dip,usba_device_t * child_ud)8855 usba_hubdi_decr_power_budget(dev_info_t *dip, usba_device_t *child_ud)
8856 {
8857 uint16_t pwr_value;
8858 size_t size;
8859 usb_cfg_descr_t cfg_descr;
8860 hubd_t *hubd = hubd_get_soft_state(dip);
8861
8862 ASSERT(hubd != NULL);
8863
8864 if (hubd->h_ignore_pwr_budget) {
8865
8866 return;
8867 }
8868
8869 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
8870 "usba_hubdi_decr_power_budget: "
8871 "dip=0x%p child_ud=0x%p", (void *)dip, (void *)child_ud);
8872
8873 mutex_enter(HUBD_MUTEX(hubd));
8874 if (hubd->h_local_pwr_on == B_TRUE) {
8875 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
8876 "usba_hubdi_decr_power_budget: "
8877 "hub is local powered");
8878 mutex_exit(HUBD_MUTEX(hubd));
8879
8880 return;
8881 }
8882 mutex_exit(HUBD_MUTEX(hubd));
8883
8884 mutex_enter(&child_ud->usb_mutex);
8885 if (child_ud->usb_pwr_from_hub > 0) {
8886 mutex_exit(&child_ud->usb_mutex);
8887
8888 return;
8889 }
8890 mutex_exit(&child_ud->usb_mutex);
8891
8892 size = usb_parse_cfg_descr(
8893 child_ud->usb_cfg, child_ud->usb_cfg_length,
8894 &cfg_descr, USB_CFG_DESCR_SIZE);
8895 ASSERT(size == USB_CFG_DESCR_SIZE);
8896
8897 mutex_enter(HUBD_MUTEX(hubd));
8898 pwr_value = cfg_descr.bMaxPower;
8899 hubd->h_pwr_left -= pwr_value;
8900 ASSERT(hubd->h_pwr_left >= 0);
8901
8902 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
8903 "usba_hubdi_decr_power_budget: "
8904 "available power is %dmA, decreased by %dmA",
8905 hubd->h_pwr_left * USB_CFG_DESCR_PWR_UNIT,
8906 pwr_value * USB_CFG_DESCR_PWR_UNIT);
8907
8908 mutex_exit(HUBD_MUTEX(hubd));
8909
8910 mutex_enter(&child_ud->usb_mutex);
8911 child_ud->usb_pwr_from_hub = pwr_value;
8912 mutex_exit(&child_ud->usb_mutex);
8913 }
8914
8915 /*
8916 * hubd_wait_for_hotplug_exit:
8917 * Waiting for the exit of the running hotplug thread or ioctl thread.
8918 */
8919 static int
hubd_wait_for_hotplug_exit(hubd_t * hubd)8920 hubd_wait_for_hotplug_exit(hubd_t *hubd)
8921 {
8922 clock_t until = drv_usectohz(1000000);
8923 int rval;
8924
8925 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
8926
8927 if (hubd->h_hotplug_thread) {
8928 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8929 "waiting for hubd hotplug thread exit");
8930 rval = cv_reltimedwait(&hubd->h_cv_hotplug_dev,
8931 &hubd->h_mutex, until, TR_CLOCK_TICK);
8932
8933 if ((rval <= 0) && (hubd->h_hotplug_thread)) {
8934
8935 return (USB_FAILURE);
8936 }
8937 }
8938
8939 return (USB_SUCCESS);
8940 }
8941
8942 /*
8943 * hubd_reset_thread:
8944 * handles the "USB_RESET_LVL_REATTACH" reset of usb device.
8945 *
8946 * - delete the child (force detaching the device and its children)
8947 * - reset the corresponding parent hub port
8948 * - create the child (force re-attaching the device and its children)
8949 */
8950 static void
hubd_reset_thread(void * arg)8951 hubd_reset_thread(void *arg)
8952 {
8953 hubd_reset_arg_t *hd_arg = (hubd_reset_arg_t *)arg;
8954 hubd_t *hubd = hd_arg->hubd;
8955 uint16_t reset_port = hd_arg->reset_port;
8956 uint16_t status, change;
8957 hub_power_t *hubpm;
8958 dev_info_t *hdip = hubd->h_dip;
8959 dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip;
8960 dev_info_t *child_dip;
8961 boolean_t online_child = B_FALSE;
8962 int devinst;
8963 char *devname;
8964 int i = 0;
8965 int rval = USB_FAILURE;
8966
8967 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8968 "hubd_reset_thread: started, hubd_reset_port = 0x%x", reset_port);
8969
8970 kmem_free(arg, sizeof (hubd_reset_arg_t));
8971
8972 mutex_enter(HUBD_MUTEX(hubd));
8973
8974 child_dip = hubd->h_children_dips[reset_port];
8975 ASSERT(child_dip != NULL);
8976
8977 devname = (char *)ddi_driver_name(child_dip);
8978 devinst = ddi_get_instance(child_dip);
8979
8980 /* if our bus power entry point is active, quit the reset */
8981 if (hubd->h_bus_pwr) {
8982 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8983 "%s%d is under bus power management, cannot be reset. "
8984 "Please disconnect and reconnect this device.",
8985 devname, devinst);
8986
8987 goto Fail;
8988 }
8989
8990 if (hubd_wait_for_hotplug_exit(hubd) == USB_FAILURE) {
8991 /* we got woken up because of a timeout */
8992 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
8993 hubd->h_log_handle, "Time out when resetting the device"
8994 " %s%d. Please disconnect and reconnect this device.",
8995 devname, devinst);
8996
8997 goto Fail;
8998 }
8999
9000 hubd->h_hotplug_thread++;
9001
9002 /* is this the root hub? */
9003 if ((hdip == rh_dip) &&
9004 (hubd->h_dev_state == USB_DEV_PWRED_DOWN)) {
9005 hubpm = hubd->h_hubpm;
9006
9007 /* mark the root hub as full power */
9008 hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
9009 hubpm->hubp_time_at_full_power = gethrtime();
9010 mutex_exit(HUBD_MUTEX(hubd));
9011
9012 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
9013 "hubd_reset_thread: call pm_power_has_changed");
9014
9015 (void) pm_power_has_changed(hdip, 0,
9016 USB_DEV_OS_FULL_PWR);
9017
9018 mutex_enter(HUBD_MUTEX(hubd));
9019 hubd->h_dev_state = USB_DEV_ONLINE;
9020 }
9021
9022 mutex_exit(HUBD_MUTEX(hubd));
9023
9024 /*
9025 * this ensures one reset activity per system at a time.
9026 * we enter the parent PCI node to have this serialization.
9027 * this also excludes ioctls and deathrow thread
9028 */
9029 ndi_devi_enter(ddi_get_parent(rh_dip));
9030 ndi_devi_enter(rh_dip);
9031
9032 /* exclude other threads */
9033 ndi_devi_enter(hdip);
9034 mutex_enter(HUBD_MUTEX(hubd));
9035
9036 /*
9037 * We need to make sure that the child is still online for a hotplug
9038 * thread could have inserted which detached the child.
9039 */
9040 if (hubd->h_children_dips[reset_port]) {
9041 mutex_exit(HUBD_MUTEX(hubd));
9042 /* First disconnect the device */
9043 hubd_post_event(hubd, reset_port, USBA_EVENT_TAG_HOT_REMOVAL);
9044
9045 /* delete cached dv_node's but drop locks first */
9046 ndi_devi_exit(hdip);
9047 ndi_devi_exit(rh_dip);
9048 ndi_devi_exit(ddi_get_parent(rh_dip));
9049
9050 (void) devfs_clean(rh_dip, NULL, DV_CLEAN_FORCE);
9051
9052 /*
9053 * workaround only for storage device. When it's able to force
9054 * detach a driver, this code can be removed safely.
9055 *
9056 * If we're to reset storage device and the device is used, we
9057 * will wait at most extra 20s for applications to exit and
9058 * close the device. This is especially useful for HAL-based
9059 * applications.
9060 */
9061 if ((strcmp(devname, "scsa2usb") == 0) &&
9062 DEVI(child_dip)->devi_ref != 0) {
9063 while (i++ < hubdi_reset_delay) {
9064 mutex_enter(HUBD_MUTEX(hubd));
9065 rval = hubd_delete_child(hubd, reset_port,
9066 NDI_DEVI_REMOVE, B_FALSE);
9067 mutex_exit(HUBD_MUTEX(hubd));
9068 if (rval == USB_SUCCESS)
9069 break;
9070
9071 delay(drv_usectohz(1000000)); /* 1s */
9072 }
9073 }
9074
9075 ndi_devi_enter(ddi_get_parent(rh_dip));
9076 ndi_devi_enter(rh_dip);
9077 ndi_devi_enter(hdip);
9078
9079 mutex_enter(HUBD_MUTEX(hubd));
9080
9081 /* Then force detaching the device */
9082 if ((rval != USB_SUCCESS) && (hubd_delete_child(hubd,
9083 reset_port, NDI_DEVI_REMOVE, B_FALSE) != USB_SUCCESS)) {
9084 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
9085 "%s%d cannot be reset due to other applications "
9086 "are using it, please first close these "
9087 "applications, then disconnect and reconnect"
9088 "the device.", devname, devinst);
9089
9090 mutex_exit(HUBD_MUTEX(hubd));
9091 /* post a re-connect event */
9092 hubd_post_event(hubd, reset_port,
9093 USBA_EVENT_TAG_HOT_INSERTION);
9094 mutex_enter(HUBD_MUTEX(hubd));
9095 } else {
9096 (void) hubd_determine_port_status(hubd, reset_port,
9097 &status, &change, NULL, HUBD_ACK_ALL_CHANGES);
9098
9099 /* Reset the parent hubd port and create new child */
9100 if (status & PORT_STATUS_CCS) {
9101 online_child |= (hubd_handle_port_connect(hubd,
9102 reset_port) == USB_SUCCESS);
9103 }
9104 }
9105 }
9106
9107 /* release locks so we can do a devfs_clean */
9108 mutex_exit(HUBD_MUTEX(hubd));
9109
9110 /* delete cached dv_node's but drop locks first */
9111 ndi_devi_exit(hdip);
9112 ndi_devi_exit(rh_dip);
9113 ndi_devi_exit(ddi_get_parent(rh_dip));
9114
9115 (void) devfs_clean(rh_dip, NULL, 0);
9116
9117 /* now check if any children need onlining */
9118 if (online_child) {
9119 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
9120 "hubd_reset_thread: onlining children");
9121
9122 (void) ndi_devi_online(hubd->h_dip, 0);
9123 }
9124
9125 mutex_enter(HUBD_MUTEX(hubd));
9126
9127 /* allow hotplug thread now */
9128 hubd->h_hotplug_thread--;
9129 Fail:
9130 hubd_start_polling(hubd, 0);
9131
9132 /* mark this device as idle */
9133 (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
9134
9135 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
9136 "hubd_reset_thread: exit, %d", hubd->h_hotplug_thread);
9137
9138 hubd->h_reset_port[reset_port] = B_FALSE;
9139
9140 mutex_exit(HUBD_MUTEX(hubd));
9141
9142 ndi_rele_devi(hdip);
9143 }
9144
9145 /*
9146 * hubd_check_same_device:
9147 * - open the default pipe of the device.
9148 * - compare the old and new descriptors of the device.
9149 * - close the default pipe.
9150 */
9151 static int
hubd_check_same_device(hubd_t * hubd,usb_port_t port)9152 hubd_check_same_device(hubd_t *hubd, usb_port_t port)
9153 {
9154 dev_info_t *dip = hubd->h_children_dips[port];
9155 usb_pipe_handle_t ph;
9156 int rval = USB_FAILURE;
9157
9158 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
9159
9160 mutex_exit(HUBD_MUTEX(hubd));
9161 /* Open the default pipe to operate the device */
9162 if (usb_pipe_open(dip, NULL, NULL,
9163 USB_FLAGS_SLEEP| USBA_FLAGS_PRIVILEGED,
9164 &ph) == USB_SUCCESS) {
9165 /*
9166 * Check that if the device's descriptors are different
9167 * from the values saved before the port reset.
9168 */
9169 rval = usb_check_same_device(dip,
9170 hubd->h_log_handle, USB_LOG_L0,
9171 DPRINT_MASK_ALL, USB_CHK_ALL, NULL);
9172
9173 usb_pipe_close(dip, ph, USB_FLAGS_SLEEP |
9174 USBA_FLAGS_PRIVILEGED, NULL, NULL);
9175 }
9176 mutex_enter(HUBD_MUTEX(hubd));
9177
9178 return (rval);
9179 }
9180
9181 /*
9182 * usba_hubdi_reset_device
9183 * Called by usb_reset_device to handle usb device reset.
9184 */
9185 int
usba_hubdi_reset_device(dev_info_t * dip,usb_dev_reset_lvl_t reset_level)9186 usba_hubdi_reset_device(dev_info_t *dip, usb_dev_reset_lvl_t reset_level)
9187 {
9188 hubd_t *hubd;
9189 usb_port_t port = 0;
9190 dev_info_t *hdip;
9191 usb_pipe_state_t prev_pipe_state = 0;
9192 usba_device_t *usba_device = NULL;
9193 hubd_reset_arg_t *arg;
9194 int i, ph_open_cnt;
9195 int rval = USB_FAILURE;
9196
9197 if ((!dip) || usba_is_root_hub(dip)) {
9198 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
9199 "usba_hubdi_reset_device: NULL dip or root hub");
9200
9201 return (USB_INVALID_ARGS);
9202 }
9203
9204 if (!usb_owns_device(dip)) {
9205 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
9206 "usba_hubdi_reset_device: Not owns the device");
9207
9208 return (USB_INVALID_PERM);
9209 }
9210
9211 if ((reset_level != USB_RESET_LVL_REATTACH) &&
9212 (reset_level != USB_RESET_LVL_DEFAULT)) {
9213 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
9214 "usba_hubdi_reset_device: Unknown flags");
9215
9216 return (USB_INVALID_ARGS);
9217 }
9218
9219 if ((hdip = ddi_get_parent(dip)) == NULL) {
9220 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
9221 "usba_hubdi_reset_device: fail to get parent hub");
9222
9223 return (USB_INVALID_ARGS);
9224 }
9225
9226 if ((hubd = hubd_get_soft_state(hdip)) == NULL) {
9227 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
9228 "usba_hubdi_reset_device: fail to get hub softstate");
9229
9230 return (USB_INVALID_ARGS);
9231 }
9232
9233 mutex_enter(HUBD_MUTEX(hubd));
9234
9235 /* make sure the hub is connected before trying any kinds of reset. */
9236 if ((hubd->h_dev_state == USB_DEV_DISCONNECTED) ||
9237 (hubd->h_dev_state == USB_DEV_SUSPENDED)) {
9238 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
9239 "usb_reset_device: the state %d of the hub/roothub "
9240 "associated to the device 0x%p is incorrect",
9241 hubd->h_dev_state, (void *)dip);
9242 mutex_exit(HUBD_MUTEX(hubd));
9243
9244 return (USB_INVALID_ARGS);
9245 }
9246
9247 mutex_exit(HUBD_MUTEX(hubd));
9248
9249 port = hubd_child_dip2port(hubd, dip);
9250
9251 mutex_enter(HUBD_MUTEX(hubd));
9252
9253 if (hubd->h_reset_port[port]) {
9254 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
9255 "usb_reset_device: the corresponding port is resetting");
9256 mutex_exit(HUBD_MUTEX(hubd));
9257
9258 return (USB_SUCCESS);
9259 }
9260
9261 /*
9262 * For Default reset, client drivers should first close all the pipes
9263 * except default pipe before calling the function, also should not
9264 * call the function during interrupt context.
9265 */
9266 if (reset_level == USB_RESET_LVL_DEFAULT) {
9267 usba_device = hubd->h_usba_devices[port];
9268 mutex_exit(HUBD_MUTEX(hubd));
9269
9270 if (servicing_interrupt()) {
9271 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
9272 "usb_reset_device: during interrput context, quit");
9273
9274 return (USB_INVALID_CONTEXT);
9275 }
9276 /* Check if all the pipes have been closed */
9277 for (ph_open_cnt = 0, i = 1; i < USBA_N_ENDPOINTS; i++) {
9278 if (usba_device->usb_ph_list[i].usba_ph_data) {
9279 ph_open_cnt++;
9280 break;
9281 }
9282 }
9283 if (ph_open_cnt) {
9284 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
9285 "usb_reset_device: %d pipes are still open",
9286 ph_open_cnt);
9287
9288 return (USB_BUSY);
9289 }
9290 mutex_enter(HUBD_MUTEX(hubd));
9291 }
9292
9293 /* Don't perform reset while the device is detaching */
9294 if (hubd->h_port_state[port] & HUBD_CHILD_DETACHING) {
9295 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
9296 "usb_reset_device: the device is detaching, "
9297 "cannot be reset");
9298 mutex_exit(HUBD_MUTEX(hubd));
9299
9300 return (USB_FAILURE);
9301 }
9302
9303 hubd->h_reset_port[port] = B_TRUE;
9304 hdip = hubd->h_dip;
9305 mutex_exit(HUBD_MUTEX(hubd));
9306
9307 /* Don't allow hub detached during the reset */
9308 ndi_hold_devi(hdip);
9309
9310 mutex_enter(HUBD_MUTEX(hubd));
9311 hubd_pm_busy_component(hubd, hdip, 0);
9312 mutex_exit(HUBD_MUTEX(hubd));
9313 /* go full power */
9314 (void) pm_raise_power(hdip, 0, USB_DEV_OS_FULL_PWR);
9315 mutex_enter(HUBD_MUTEX(hubd));
9316
9317 hubd->h_hotplug_thread++;
9318
9319 /* stop polling if it was active */
9320 if (hubd->h_ep1_ph) {
9321 mutex_exit(HUBD_MUTEX(hubd));
9322 (void) usb_pipe_get_state(hubd->h_ep1_ph, &prev_pipe_state,
9323 USB_FLAGS_SLEEP);
9324 mutex_enter(HUBD_MUTEX(hubd));
9325
9326 if (prev_pipe_state == USB_PIPE_STATE_ACTIVE) {
9327 hubd_stop_polling(hubd);
9328 }
9329 }
9330
9331 switch (reset_level) {
9332 case USB_RESET_LVL_REATTACH:
9333 mutex_exit(HUBD_MUTEX(hubd));
9334 arg = (hubd_reset_arg_t *)kmem_zalloc(
9335 sizeof (hubd_reset_arg_t), KM_SLEEP);
9336 arg->hubd = hubd;
9337 arg->reset_port = port;
9338 mutex_enter(HUBD_MUTEX(hubd));
9339
9340 if ((rval = usb_async_req(hdip, hubd_reset_thread,
9341 (void *)arg, 0)) == USB_SUCCESS) {
9342 hubd->h_hotplug_thread--;
9343 mutex_exit(HUBD_MUTEX(hubd));
9344
9345 return (USB_SUCCESS);
9346 } else {
9347 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
9348 "Cannot create reset thread, the device %s%d failed"
9349 " to reset", ddi_driver_name(dip),
9350 ddi_get_instance(dip));
9351
9352 kmem_free(arg, sizeof (hubd_reset_arg_t));
9353 }
9354
9355 break;
9356 case USB_RESET_LVL_DEFAULT:
9357 /*
9358 * Reset hub port and then recover device's address, set back
9359 * device's configuration, hubd_handle_port_connect() will
9360 * handle errors happened during this process.
9361 */
9362 if ((rval = hubd_handle_port_connect(hubd, port))
9363 == USB_SUCCESS) {
9364 mutex_exit(HUBD_MUTEX(hubd));
9365 /* re-open the default pipe */
9366 ASSERT3P(usba_device, !=, NULL);
9367 rval = usba_persistent_pipe_open(usba_device);
9368 mutex_enter(HUBD_MUTEX(hubd));
9369 if (rval != USB_SUCCESS) {
9370 USB_DPRINTF_L2(DPRINT_MASK_ATTA,
9371 hubd->h_log_handle, "failed to reopen "
9372 "default pipe after reset, disable hub"
9373 "port for %s%d", ddi_driver_name(dip),
9374 ddi_get_instance(dip));
9375 /*
9376 * Disable port to set out a hotplug thread
9377 * which will handle errors.
9378 */
9379 (void) hubd_disable_port(hubd, port);
9380 }
9381 }
9382
9383 break;
9384 default:
9385
9386 break;
9387 }
9388
9389 /* allow hotplug thread now */
9390 hubd->h_hotplug_thread--;
9391
9392 if ((hubd->h_dev_state == USB_DEV_ONLINE) && hubd->h_ep1_ph &&
9393 (prev_pipe_state == USB_PIPE_STATE_ACTIVE)) {
9394 hubd_start_polling(hubd, 0);
9395 }
9396
9397 hubd_pm_idle_component(hubd, hdip, 0);
9398
9399 /* Clear reset mark for the port. */
9400 hubd->h_reset_port[port] = B_FALSE;
9401
9402 mutex_exit(HUBD_MUTEX(hubd));
9403
9404 ndi_rele_devi(hdip);
9405
9406 return (rval);
9407 }
9408