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