1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2019, Joyent, Inc.
24 */
25
26
27 /*
28 * USBA: Solaris USB Architecture support
29 *
30 * all functions exposed to client drivers have prefix usb_ while all USBA
31 * internal functions or functions exposed to HCD or hubd only have prefix
32 * usba_
33 *
34 * this file contains initializations, logging/tracing support and PM
35 * support
36 */
37 #define USBA_FRAMEWORK
38 #include <sys/varargs.h>
39 #include <sys/strsun.h>
40 #include <sys/usb/usba/usba_impl.h>
41 #include <sys/usb/usba/hcdi_impl.h>
42 #include <sys/usb/usba/usba10.h>
43
44 /*
45 * print buffer protected by mutex for debug stuff. the mutex also
46 * ensures serializing debug messages
47 */
48 static kmutex_t usba_print_mutex;
49 static char usba_print_buf[USBA_PRINT_BUF_LEN];
50 kmutex_t usbai_mutex;
51
52 /*
53 * debug stuff
54 */
55 usb_log_handle_t usbai_log_handle;
56 uint_t usbai_errlevel = USB_LOG_L4;
57 uint_t usbai_errmask = (uint_t)-1;
58
59 #define USBA_DEBUG_SIZE_EXTRA_ALLOC 8
60 #ifdef DEBUG
61 #define USBA_DEBUG_BUF_SIZE \
62 (0x40000 - USBA_DEBUG_SIZE_EXTRA_ALLOC)
63 #else
64 #define USBA_DEBUG_BUF_SIZE \
65 (0x4000 - USBA_DEBUG_SIZE_EXTRA_ALLOC)
66 #endif /* DEBUG */
67
68 #define USBA_POWER_STR_SIZE 40
69
70 int usba_suppress_dprintf; /* Suppress debug printing */
71 int usba_clear_debug_buf_flag; /* clear debug buf */
72 int usba_buffer_dprintf = 1; /* Use a debug print buffer */
73 int usba_timestamp_dprintf = 0; /* get time stamps in trace */
74 int usba_debug_buf_size = USBA_DEBUG_BUF_SIZE; /* Size of debug buf */
75 int usba_debug_chatty; /* L1 msg on console */
76
77 static char *usba_debug_buf = NULL; /* The debug buf */
78 static char *usba_buf_sptr, *usba_buf_eptr;
79 static hrtime_t usba_last_timestamp; /* last time stamp in trace */
80
81 usb_dev_cap_t usb_cap;
82
83 /* USBA framework initializations */
84 void
usba_usbai_initialization()85 usba_usbai_initialization()
86 {
87 usbai_log_handle = usb_alloc_log_hdl(NULL, "usbai", &usbai_errlevel,
88 &usbai_errmask, NULL, 0);
89
90 USB_DPRINTF_L4(DPRINT_MASK_USBAI, usbai_log_handle,
91 "usba_usbai_initialization");
92
93 mutex_init(&usba_print_mutex, NULL, MUTEX_DRIVER, NULL);
94 mutex_init(&usbai_mutex, NULL, MUTEX_DRIVER, NULL);
95 }
96
97
98 /* USBA framework destroys */
99 void
usba_usbai_destroy()100 usba_usbai_destroy()
101 {
102 USB_DPRINTF_L4(DPRINT_MASK_USBAI, usbai_log_handle,
103 "usba_usbai_destroy");
104
105 mutex_destroy(&usba_print_mutex);
106 mutex_destroy(&usbai_mutex);
107 if (usba_debug_buf) {
108 kmem_free(usba_debug_buf,
109 usba_debug_buf_size + USBA_DEBUG_SIZE_EXTRA_ALLOC);
110 }
111
112 usb_free_log_hdl(usbai_log_handle);
113 }
114
115
116 /*
117 * debug, log, and console message handling
118 */
119 usb_log_handle_t
usb_alloc_log_hdl(dev_info_t * dip,char * name,uint_t * errlevel,uint_t * mask,uint_t * instance_filter,usb_flags_t flags)120 usb_alloc_log_hdl(dev_info_t *dip, char *name,
121 uint_t *errlevel, uint_t *mask, uint_t *instance_filter,
122 usb_flags_t flags)
123 {
124 usba_log_handle_impl_t *hdl;
125
126 USBA_CHECK_CONTEXT();
127 hdl = kmem_zalloc(sizeof (*hdl), KM_SLEEP);
128
129 hdl->lh_dip = dip;
130 if (dip && (name == NULL)) {
131 hdl->lh_name = (char *)ddi_driver_name(dip);
132 } else {
133 hdl->lh_name = name;
134 }
135 hdl->lh_errlevel = errlevel;
136 hdl->lh_mask = mask;
137 hdl->lh_instance_filter = instance_filter;
138 hdl->lh_flags = flags;
139
140 #ifdef __lock_lint
141 (void) usb_alloc_log_handle(dip, name, errlevel, mask,
142 instance_filter, 0, flags);
143 usb_free_log_handle(NULL);
144 #endif
145
146 return ((usb_log_handle_t)hdl);
147 }
148
149
150 /*ARGSUSED*/
151 usb_log_handle_t
usb_alloc_log_handle(dev_info_t * dip,char * name,uint_t * errlevel,uint_t * mask,uint_t * instance_filter,uint_t reserved,usb_flags_t flags)152 usb_alloc_log_handle(dev_info_t *dip, char *name,
153 uint_t *errlevel, uint_t *mask, uint_t *instance_filter,
154 uint_t reserved, usb_flags_t flags)
155 {
156 return (usb_alloc_log_hdl(dip, name, errlevel, mask,
157 instance_filter, flags));
158 }
159
160 void
usb_free_log_handle(usb_log_handle_t handle)161 usb_free_log_handle(usb_log_handle_t handle)
162 {
163 if (handle) {
164 kmem_free(handle, sizeof (usba_log_handle_impl_t));
165 }
166 }
167
168 void
usb_free_log_hdl(usb_log_handle_t handle)169 usb_free_log_hdl(usb_log_handle_t handle)
170 {
171 if (handle) {
172 kmem_free(handle, sizeof (usba_log_handle_impl_t));
173 }
174 }
175
176
177 static void
usba_clear_dprint_buf()178 usba_clear_dprint_buf()
179 {
180 if (usba_debug_buf) {
181 usba_buf_sptr = usba_debug_buf;
182 usba_buf_eptr = usba_debug_buf + usba_debug_buf_size;
183 bzero(usba_debug_buf, usba_debug_buf_size +
184 USBA_DEBUG_SIZE_EXTRA_ALLOC);
185 }
186 }
187
188
189 #ifdef DEBUG
190 char *
usba_dbuf_tail(uint_t lines)191 usba_dbuf_tail(uint_t lines)
192 {
193 int count;
194 char *r = NULL;
195
196 mutex_enter(&usba_print_mutex);
197 if (usba_debug_buf) {
198 count = 0;
199 r = usba_buf_sptr;
200 while ((count < lines) && (r > usba_debug_buf)) {
201 if (*r == '\n') {
202 count++;
203 }
204 r--;
205 }
206 }
207 mutex_exit(&usba_print_mutex);
208
209 return (r);
210 }
211 #endif /* DEBUG */
212
213
214 static void usb_vprintf(dev_info_t *, int, char *, char *, va_list)
215 __KVPRINTFLIKE(4);
216
217 static void
usb_vprintf(dev_info_t * dip,int level,char * label,char * fmt,va_list ap)218 usb_vprintf(dev_info_t *dip, int level, char *label, char *fmt, va_list ap)
219 {
220 size_t len;
221 int instance = 0;
222 char driver_name[USBA_DRVNAME_LEN];
223 char *msg_ptr;
224
225 if (usba_suppress_dprintf) {
226
227 return;
228 }
229
230 *driver_name = '\0';
231 mutex_enter(&usba_print_mutex);
232
233 /*
234 * Check if we have a valid buf size?
235 * Suppress logging to usb_buffer if so.
236 */
237 if (usba_debug_buf_size <= 0) {
238
239 usba_buffer_dprintf = 0;
240 }
241
242 /*
243 * if there is label and dip, use <driver name><instance>:
244 * otherwise just use the label
245 */
246 if (dip) {
247 instance = ddi_get_instance(dip);
248 (void) snprintf(driver_name, USBA_DRVNAME_LEN,
249 "%s%d", ddi_driver_name(dip), instance);
250 }
251
252 if (label == (char *)NULL) {
253 len = snprintf(usba_print_buf, USBA_PRINT_BUF_LEN, "\t");
254 } else if (usba_timestamp_dprintf) {
255 hrtime_t t = gethrtime();
256 hrtime_t elapsed = (t - usba_last_timestamp)/1000;
257 usba_last_timestamp = t;
258
259 if (dip) {
260
261 len = snprintf(usba_print_buf, USBA_PRINT_BUF_LEN,
262 "+%lld->%p: %s%d: ", elapsed,
263 (void *)curthread, label, instance);
264 } else {
265 len = snprintf(usba_print_buf, USBA_PRINT_BUF_LEN,
266 "+%lld->%p: %s: ", elapsed,
267 (void *)curthread, label);
268 }
269 } else {
270 if (dip) {
271 len = snprintf(usba_print_buf, USBA_PRINT_BUF_LEN,
272 "%s%d:\t", label, instance);
273 } else {
274 len = snprintf(usba_print_buf, USBA_PRINT_BUF_LEN,
275 "%s:\t", label);
276 }
277 }
278
279
280 msg_ptr = usba_print_buf + len;
281 (void) vsnprintf(msg_ptr, USBA_PRINT_BUF_LEN - len - 2, fmt, ap);
282
283 len = min(strlen(usba_print_buf), USBA_PRINT_BUF_LEN - 2);
284 usba_print_buf[len++] = '\n';
285 usba_print_buf[len] = '\0';
286
287 /*
288 * stuff the message in the debug buf
289 */
290 if (usba_buffer_dprintf) {
291 if (usba_debug_buf == NULL) {
292 usba_debug_buf = kmem_alloc(
293 usba_debug_buf_size + USBA_DEBUG_SIZE_EXTRA_ALLOC,
294 KM_SLEEP);
295 usba_clear_dprint_buf();
296 } else if (usba_clear_debug_buf_flag) {
297 usba_clear_dprint_buf();
298 usba_clear_debug_buf_flag = 0;
299 }
300
301 /*
302 * overwrite >>>> that might be over the end of the
303 * the buffer
304 */
305 *(usba_debug_buf + usba_debug_buf_size) = '\0';
306
307 if ((usba_buf_sptr + len) > usba_buf_eptr) {
308 size_t left = _PTRDIFF(usba_buf_eptr, usba_buf_sptr);
309
310 bcopy(usba_print_buf, usba_buf_sptr, left);
311 bcopy((caddr_t)usba_print_buf + left,
312 usba_debug_buf, len - left);
313 usba_buf_sptr = usba_debug_buf + len - left;
314 } else {
315 bcopy(usba_print_buf, usba_buf_sptr, len);
316 usba_buf_sptr += len;
317 }
318 /* add marker */
319 (void) sprintf(usba_buf_sptr, ">>>>");
320 }
321
322 /*
323 * L4-L2 message may go to the log buf if not logged in usba_debug_buf
324 * L1 messages will go to the log buf in non-debug kernels and
325 * to console and log buf in debug kernels if usba_debug_chatty
326 * has been set
327 * L0 messages are warnings and will go to console and log buf and
328 * include the pathname, if available
329 */
330
331 switch (level) {
332 case USB_LOG_L4:
333 case USB_LOG_L3:
334 case USB_LOG_L2:
335 if (!usba_buffer_dprintf) {
336 cmn_err(CE_CONT, "^%s", usba_print_buf);
337 }
338 break;
339 case USB_LOG_L1:
340 if (dip) {
341 char *pathname = kmem_alloc(MAXPATHLEN, KM_NOSLEEP);
342 if (pathname) {
343 cmn_err(CE_CONT,
344 usba_debug_chatty ?
345 "%s (%s): %s" : "?%s (%s): %s",
346 ddi_pathname(dip, pathname),
347 driver_name, msg_ptr);
348 kmem_free(pathname, MAXPATHLEN);
349 } else {
350 cmn_err(CE_CONT,
351 usba_debug_chatty ?
352 "%s" : "?%s", usba_print_buf);
353 }
354 } else {
355 cmn_err(CE_CONT,
356 usba_debug_chatty ? "%s" : "?%s",
357 usba_print_buf);
358 }
359 break;
360 case USB_LOG_L0:
361 /* Strip the "\n" added earlier */
362 if (usba_print_buf[len - 1] == '\n') {
363 usba_print_buf[len - 1] = '\0';
364 }
365 if (msg_ptr[len - 1] == '\n') {
366 msg_ptr[len - 1] = '\0';
367 }
368 if (dip) {
369 char *pathname = kmem_alloc(MAXPATHLEN, KM_NOSLEEP);
370 if (pathname) {
371 cmn_err(CE_WARN, "%s (%s): %s",
372 ddi_pathname(dip, pathname),
373 driver_name, msg_ptr);
374 kmem_free(pathname, MAXPATHLEN);
375 } else {
376 cmn_err(CE_WARN, usba_print_buf);
377 }
378 } else {
379 cmn_err(CE_WARN, usba_print_buf);
380 }
381 break;
382 }
383
384 mutex_exit(&usba_print_mutex);
385 }
386
387 int
388 usba_vlog(usb_log_handle_t, uint_t, uint_t, char *, va_list)
389 __KVPRINTFLIKE(4);
390
391 /* When usba10_calls.c goes away, this function can be made static again. */
392 int
usba_vlog(usb_log_handle_t handle,uint_t level,uint_t mask,char * fmt,va_list ap)393 usba_vlog(usb_log_handle_t handle, uint_t level, uint_t mask,
394 char *fmt, va_list ap)
395 {
396 usba_log_handle_impl_t *hdl = (usba_log_handle_impl_t *)handle;
397 char *label;
398 uint_t hdl_errlevel, hdl_mask, hdl_instance_filter;
399
400 /* if there is no handle, use usba as label */
401 if (hdl == NULL) {
402 usb_vprintf(NULL, level, "usba", fmt, ap);
403
404 return (USB_SUCCESS);
405 }
406
407 /* look up the filters and set defaults */
408 if (hdl->lh_errlevel) {
409 hdl_errlevel = *(hdl->lh_errlevel);
410 } else {
411 hdl_errlevel = 0;
412 }
413
414 if (hdl->lh_mask) {
415 hdl_mask = *(hdl->lh_mask);
416 } else {
417 hdl_mask = (uint_t)-1;
418 }
419
420 if (hdl->lh_instance_filter) {
421 hdl_instance_filter = *(hdl->lh_instance_filter);
422 } else {
423 hdl_instance_filter = (uint_t)-1;
424 }
425
426 /* if threshold is lower or mask doesn't match, we are done */
427 if ((level > hdl_errlevel) || ((mask & hdl_mask) == 0)) {
428
429 return (USB_FAILURE);
430 }
431
432 /*
433 * if we have a dip, and it is not a warning, check
434 * the instance number
435 */
436 if (hdl->lh_dip && (level > USB_LOG_L0)) {
437 if ((hdl_instance_filter != (uint_t)-1) &&
438 (ddi_get_instance(hdl->lh_dip) != hdl_instance_filter)) {
439
440 return (USB_FAILURE);
441 }
442 }
443
444 label = hdl->lh_name;
445
446 usb_vprintf(hdl->lh_dip, level, label, fmt, ap);
447
448 return (USB_SUCCESS);
449 }
450
451
452 void
usb_dprintf4(uint_t mask,usb_log_handle_t handle,char * fmt,...)453 usb_dprintf4(uint_t mask, usb_log_handle_t handle, char *fmt, ...)
454 {
455 va_list ap;
456
457 va_start(ap, fmt);
458 (void) usba_vlog(handle, USB_LOG_L4, mask, fmt, ap);
459 va_end(ap);
460 }
461
462
463 void
usb_dprintf3(uint_t mask,usb_log_handle_t handle,char * fmt,...)464 usb_dprintf3(uint_t mask, usb_log_handle_t handle, char *fmt, ...)
465 {
466 va_list ap;
467
468 va_start(ap, fmt);
469 (void) usba_vlog(handle, USB_LOG_L3, mask, fmt, ap);
470 va_end(ap);
471 }
472
473
474 void
usb_dprintf2(uint_t mask,usb_log_handle_t handle,char * fmt,...)475 usb_dprintf2(uint_t mask, usb_log_handle_t handle, char *fmt, ...)
476 {
477 va_list ap;
478
479 va_start(ap, fmt);
480 (void) usba_vlog(handle, USB_LOG_L2, mask, fmt, ap);
481 va_end(ap);
482 }
483
484
485 void
usb_dprintf1(uint_t mask,usb_log_handle_t handle,char * fmt,...)486 usb_dprintf1(uint_t mask, usb_log_handle_t handle, char *fmt, ...)
487 {
488 va_list ap;
489
490 va_start(ap, fmt);
491 (void) usba_vlog(handle, USB_LOG_L1, mask, fmt, ap);
492 va_end(ap);
493 }
494
495
496 void
usb_dprintf0(uint_t mask,usb_log_handle_t handle,char * fmt,...)497 usb_dprintf0(uint_t mask, usb_log_handle_t handle, char *fmt, ...)
498 {
499 va_list ap;
500
501 va_start(ap, fmt);
502 (void) usba_vlog(handle, USB_LOG_L0, mask, fmt, ap);
503 va_end(ap);
504 }
505
506
507 int
usb_log(usb_log_handle_t handle,uint_t level,uint_t mask,char * fmt,...)508 usb_log(usb_log_handle_t handle, uint_t level, uint_t mask, char *fmt, ...)
509 {
510 va_list ap;
511 int rval;
512
513 va_start(ap, fmt);
514 rval = usba_vlog(handle, level, mask, fmt, ap);
515 va_end(ap);
516
517 return (rval);
518 }
519
520
521 /*
522 * Provide a default configuration power descriptor
523 */
524 usba_cfg_pwr_descr_t default_cfg_power = {
525 18, /* bLength */
526 USBA_DESCR_TYPE_CFG_PWR_1_1, /* bDescriptorType */
527 0, /* SelfPowerConsumedD0_l */
528 0, /* SelfPowerConsumedD0_h */
529 0, /* bPowerSummaryId */
530 0, /* bBusPowerSavingD1 */
531 0, /* bSelfPowerSavingD1 */
532 0, /* bBusPowerSavingD2 */
533 0, /* bSelfPowerSavingD2 */
534 100, /* bBusPowerSavingD3 */
535 100, /* bSelfPowerSavingD3 */
536 0, /* TransitionTimeFromD1 */
537 0, /* TransitionTimeFromD2 */
538 10, /* TransitionTimeFromD3 1 Second */
539 };
540
541
542 /*
543 * Provide a default interface power descriptor
544 */
545 usba_if_pwr_descr_t default_if_power = {
546 15, /* bLength */
547 USBA_DESCR_TYPE_IF_PWR_1_1, /* bDescriptorType */
548 8, /* bmCapabilitiesFlags */
549 0, /* bBusPowerSavingD1 */
550 0, /* bSelfPowerSavingD1 */
551 0, /* bBusPowerSavingD2 */
552 0, /* bSelfPowerSavingD2 */
553 100, /* bBusPowerSavingD3 */
554 100, /* bSelfPowerSavingD3 */
555 0, /* TransitionTimeFromD1 */
556 0, /* TransitionTimeFromD2 */
557 10, /* TransitionTimeFromD3 1 Second */
558 };
559
560
561 static void
usba_async_req_raise_power(void * arg)562 usba_async_req_raise_power(void *arg)
563 {
564 usba_pm_req_t *pmrq = (usba_pm_req_t *)arg;
565 int rval;
566
567 /*
568 * To eliminate race condition between the call to power entry
569 * point and our call to raise power level, we first mark the
570 * component busy and later idle
571 */
572 (void) pm_busy_component(pmrq->dip, pmrq->comp);
573 rval = pm_raise_power(pmrq->dip, pmrq->comp, pmrq->level);
574 (void) pm_idle_component(pmrq->dip, pmrq->comp);
575 pmrq->cb(pmrq->arg, rval);
576
577 /* We are done with pmrq. Free it now */
578 kmem_free(pmrq, sizeof (usba_pm_req_t));
579 }
580
581
582 /* usb function to perform async pm_request_power_change */
583 int
usb_req_raise_power(dev_info_t * dip,int comp,int level,void (* callback)(void *,int),void * arg,usb_flags_t flags)584 usb_req_raise_power(dev_info_t *dip, int comp, int level,
585 void (*callback)(void *, int), void *arg, usb_flags_t flags)
586 {
587 usba_pm_req_t *pmrq;
588
589 if (flags & USB_FLAGS_SLEEP) {
590
591 return (pm_raise_power(dip, comp, level));
592 }
593
594 if ((pmrq = kmem_alloc(sizeof (usba_pm_req_t), KM_NOSLEEP)) ==
595 NULL) {
596
597 return (USB_FAILURE);
598 }
599
600 pmrq->dip = dip;
601 pmrq->comp = comp;
602 pmrq->level = level;
603 pmrq->cb = callback;
604 pmrq->arg = arg;
605 pmrq->flags = flags;
606
607 if (usb_async_req(dip, usba_async_req_raise_power,
608 (void *)pmrq, USB_FLAGS_NOSLEEP | USB_FLAGS_NOQUEUE) !=
609 USB_SUCCESS) {
610 kmem_free(pmrq, sizeof (usba_pm_req_t));
611
612 return (USB_FAILURE);
613 }
614
615 return (USB_SUCCESS);
616 }
617
618
619 static void
usba_async_req_lower_power(void * arg)620 usba_async_req_lower_power(void *arg)
621 {
622 usba_pm_req_t *pmrq = (usba_pm_req_t *)arg;
623 int rval;
624
625 /*
626 * To eliminate race condition between the call to power entry
627 * point and our call to lower power level, we call idle component
628 * to push ahead the PM timestamp
629 */
630 (void) pm_idle_component(pmrq->dip, pmrq->comp);
631 rval = pm_lower_power(pmrq->dip, pmrq->comp, pmrq->level);
632 pmrq->cb(pmrq->arg, rval);
633 }
634
635
636 /* usb function to perform async pm_request_power_change */
637 int
usb_req_lower_power(dev_info_t * dip,int comp,int level,void (* callback)(void *,int),void * arg,usb_flags_t flags)638 usb_req_lower_power(dev_info_t *dip, int comp, int level,
639 void (*callback)(void *, int), void *arg, usb_flags_t flags)
640 {
641 usba_pm_req_t *pmrq;
642
643 if (flags & USB_FLAGS_SLEEP) {
644
645 return (pm_lower_power(dip, comp, level));
646 }
647
648 if ((pmrq = kmem_alloc(sizeof (usba_pm_req_t), KM_NOSLEEP)) ==
649 NULL) {
650
651 return (USB_FAILURE);
652 }
653
654 pmrq->dip = dip;
655 pmrq->comp = comp;
656 pmrq->level = level;
657 pmrq->cb = callback;
658 pmrq->arg = arg;
659 pmrq->flags = flags;
660
661 if (usb_async_req(dip, usba_async_req_lower_power,
662 (void *)pmrq, USB_FLAGS_NOSLEEP | USB_FLAGS_NOQUEUE) !=
663 USB_SUCCESS) {
664 kmem_free(pmrq, sizeof (usba_pm_req_t));
665
666 return (USB_FAILURE);
667 }
668
669 return (USB_SUCCESS);
670 }
671
672
673 /* function to see if pm is enabled for this device */
674 /*ARGSUSED*/
675 int
usb_is_pm_enabled(dev_info_t * dip)676 usb_is_pm_enabled(dev_info_t *dip)
677 {
678 /*
679 * At this point we should assume that all devices
680 * are capable of supporting PM
681 */
682 return (USB_SUCCESS);
683 }
684
685
686 /*
687 * usba_handle_device_remote_wakeup:
688 * internal function to enable/disable remote wakeup in the device
689 * or interface
690 */
691 static int
usba_handle_device_remote_wakeup(dev_info_t * dip,int cmd)692 usba_handle_device_remote_wakeup(dev_info_t *dip, int cmd)
693 {
694 int rval;
695 uint8_t bmRequest = USB_DEV_REQ_HOST_TO_DEV;
696 uchar_t bRequest;
697 uint16_t wIndex = 0;
698 usb_cr_t completion_reason = 0;
699 usb_cb_flags_t cb_flags;
700 usb_pipe_handle_t ph;
701
702 USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
703 "usba_handle_device_remote_wakeup: dip = 0x%p", (void *)dip);
704
705 USBA_CHECK_CONTEXT();
706
707 /* get the default pipe */
708 ph = usba_get_dflt_pipe_handle(dip);
709
710 /* do we own the device? */
711 if (usb_owns_device(dip)) {
712 bmRequest |= USB_DEV_REQ_RCPT_DEV;
713 } else {
714 bmRequest |= USB_DEV_REQ_RCPT_IF;
715 wIndex = usba_get_ifno(dip);
716 }
717 bRequest = ((cmd == USB_REMOTE_WAKEUP_ENABLE) ? USB_REQ_SET_FEATURE :
718 USB_REQ_CLEAR_FEATURE);
719
720 if ((rval = usb_pipe_sync_ctrl_xfer(dip, ph,
721 bmRequest, /* bmRequest */
722 bRequest, /* bRequest */
723 USB_DEV_REMOTE_WAKEUP, /* wValue */
724 wIndex, /* wIndex */
725 0, /* wLength */
726 NULL, 0,
727 &completion_reason,
728 &cb_flags, USB_FLAGS_SLEEP)) != USB_SUCCESS) {
729 USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle,
730 "Set/ClearFeature (RemoteWakep) failed: "
731 "rval = %d, cmd = %d, cr = 0x%x cb = 0x%x",
732 rval, cmd, completion_reason, cb_flags);
733 }
734
735 return (rval);
736 }
737
738
739 void
usb_enable_parent_notification(dev_info_t * dip)740 usb_enable_parent_notification(dev_info_t *dip)
741 {
742 USBA_CHECK_CONTEXT();
743 (void) ndi_prop_create_boolean(DDI_DEV_T_NONE, dip,
744 "pm-want-child-notification?");
745 }
746
747
748 /*
749 * usb_handle_remote_wakeup:
750 * check if device supports remote wakeup and, if so, enable/disable
751 * remote wake up in the device depending upon the command
752 */
753 int
usb_handle_remote_wakeup(dev_info_t * dip,int cmd)754 usb_handle_remote_wakeup(dev_info_t *dip, int cmd)
755 {
756 usb_cfg_descr_t cfg_descr;
757 uchar_t *usb_cfg; /* buf for config descriptor */
758 size_t cfg_length;
759 int rval;
760
761 USBA_CHECK_CONTEXT();
762
763 /* Obtain the raw configuration descriptor */
764 usb_cfg = usb_get_raw_cfg_data(dip, &cfg_length);
765
766 /* get configuration descriptor, must succeed */
767 rval = usb_parse_cfg_descr(usb_cfg, cfg_length,
768 &cfg_descr, USB_CFG_DESCR_SIZE);
769 ASSERT(rval == USB_CFG_DESCR_SIZE);
770
771 /*
772 * If the device supports remote wakeup, and PM is enabled,
773 * we enable remote wakeup in the device
774 */
775 if ((usb_is_pm_enabled(dip) == USB_SUCCESS) &&
776 (cfg_descr.bmAttributes & USB_CFG_ATTR_REMOTE_WAKEUP)) {
777
778 rval = usba_handle_device_remote_wakeup(dip, cmd);
779 } else {
780 rval = USB_FAILURE;
781 }
782
783 return (rval);
784 }
785
786
787 /*
788 * usb_create_pm_components:
789 * map descriptor into pm properties
790 */
791 int
usb_create_pm_components(dev_info_t * dip,uint_t * pwr_states)792 usb_create_pm_components(dev_info_t *dip, uint_t *pwr_states)
793 {
794 uchar_t *usb_cfg; /* buf for config descriptor */
795 usb_cfg_descr_t cfg_descr;
796 size_t cfg_length;
797 usba_cfg_pwr_descr_t confpwr_descr;
798 usba_if_pwr_descr_t ifpwr_descr;
799 uint8_t cfg_attrib;
800 int i, lvl, rval;
801 int n_prop = 0;
802 uint8_t *ptr;
803 char *drvname;
804 char str[USBA_POWER_STR_SIZE];
805 char *pm_comp[USBA_N_PMCOMP];
806
807 USBA_CHECK_CONTEXT();
808
809 if (usb_is_pm_enabled(dip) != USB_SUCCESS) {
810
811 return (USB_FAILURE);
812 }
813
814 /* Obtain the raw configuration descriptor */
815 usb_cfg = usb_get_raw_cfg_data(dip, &cfg_length);
816
817 /* get configuration descriptor, must succceed */
818 rval = usb_parse_cfg_descr(usb_cfg, cfg_length,
819 &cfg_descr, USB_CFG_DESCR_SIZE);
820 ASSERT(rval == USB_CFG_DESCR_SIZE);
821
822 cfg_attrib = cfg_descr.bmAttributes;
823 *pwr_states = 0;
824
825 /*
826 * Now start creating the pm-components strings
827 */
828 drvname = (char *)ddi_driver_name(dip);
829 (void) snprintf(str, USBA_POWER_STR_SIZE, "NAME= %s%d Power",
830 drvname, ddi_get_instance(dip));
831
832 pm_comp[n_prop] = kmem_zalloc(strlen(str) + 1, KM_SLEEP);
833 (void) strcpy(pm_comp[n_prop++], str);
834
835 /*
836 * if the device is bus powered we look at the bBusPowerSavingDx
837 * fields else we look at bSelfPowerSavingDx fields.
838 * OS and USB power states are numerically reversed,
839 *
840 * Here is the mapping :-
841 * OS State USB State
842 * 0 D3 (minimal or no power)
843 * 1 D2
844 * 2 D1
845 * 3 D0 (Full power)
846 *
847 * if we own the whole device, we look at the config pwr descr
848 * else at the interface pwr descr.
849 */
850 if (usb_owns_device(dip)) {
851 /* Parse the configuration power descriptor */
852 rval = usba_parse_cfg_pwr_descr(usb_cfg, cfg_length,
853 &confpwr_descr, USBA_CFG_PWR_DESCR_SIZE);
854
855 if (rval != USBA_CFG_PWR_DESCR_SIZE) {
856 USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle,
857 "usb_create_pm_components: "
858 "usb_parse_cfg_pwr_descr returns length of %d, "
859 "expecting %d", rval, USBA_CFG_PWR_DESCR_SIZE);
860
861 return (USB_FAILURE);
862 }
863
864 if (cfg_attrib & USB_CFG_ATTR_SELFPWR) {
865 ptr = &confpwr_descr.bSelfPowerSavingD3;
866 } else {
867 ptr = &confpwr_descr.bBusPowerSavingD3;
868 }
869 } else {
870 /* Parse the interface power descriptor */
871 rval = usba_parse_if_pwr_descr(usb_cfg,
872 cfg_length,
873 usba_get_ifno(dip), /* interface index */
874 0, /* XXXX alt interface index */
875 &ifpwr_descr,
876 USBA_IF_PWR_DESCR_SIZE);
877
878 if (rval != USBA_IF_PWR_DESCR_SIZE) {
879 USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle,
880 "usb_create_pm_components: "
881 "usb_parse_if_pwr_descr "
882 "returns length of %d, "
883 "expecting %d", rval, USBA_CFG_PWR_DESCR_SIZE);
884
885 return (USB_FAILURE);
886 }
887
888 if (cfg_attrib & USB_CFG_ATTR_SELFPWR) {
889 ptr = &ifpwr_descr.bSelfPowerSavingD3;
890 } else {
891 ptr = &ifpwr_descr.bBusPowerSavingD3;
892 }
893 }
894
895 /* walk thru levels and create prop level=name strings */
896 for (lvl = USB_DEV_OS_PWR_0; lvl <= USB_DEV_OS_PWR_3; lvl++) {
897 if (*ptr || (lvl == USB_DEV_OS_PWR_3)) {
898 (void) snprintf(str, USBA_POWER_STR_SIZE,
899 "%d=USB D%d State",
900 lvl, USB_DEV_OS_PWR2USB_PWR(lvl));
901 pm_comp[n_prop] = kmem_zalloc(strlen(str) + 1,
902 KM_SLEEP);
903 (void) strcpy(pm_comp[n_prop++], str);
904
905 *pwr_states |= USB_DEV_PWRMASK(lvl);
906 }
907
908 ptr -= 2; /* skip to the next power state */
909 }
910
911 USB_DPRINTF_L3(DPRINT_MASK_USBAI, usbai_log_handle,
912 "usb_create_pm_components: pwr_states: %x", *pwr_states);
913
914 /* now create the actual components */
915 rval = ddi_prop_update_string_array(DDI_DEV_T_NONE, dip,
916 "pm-components", pm_comp, n_prop);
917 if (rval == DDI_PROP_SUCCESS) {
918 rval = USB_SUCCESS;
919 } else {
920 rval = USB_FAILURE;
921 }
922
923 /* display & delete properties */
924 USB_DPRINTF_L3(DPRINT_MASK_USBAI, usbai_log_handle,
925 "usb_create_pm_components: The properties are:");
926 for (i = 0; i < n_prop; i++) {
927 USB_DPRINTF_L3(DPRINT_MASK_USBAI, usbai_log_handle,
928 "\t%s", pm_comp[i]);
929 kmem_free(pm_comp[i], strlen(pm_comp[i]) + 1);
930 }
931
932 return (rval);
933 }
934
935
936 /*
937 * Generic Functions to set the power level of any usb device
938 *
939 * Since OS and USB power states are numerically reverse,
940 * Here is the mapping :-
941 * OS State USB State
942 * 0 D3 (minimal or no power)
943 * 1 D2
944 * 2 D1
945 * 3 D0 (Full power)
946 */
947
948 /* set device power level to 0 (full power) */
949 /*ARGSUSED*/
950 int
usb_set_device_pwrlvl0(dev_info_t * dip)951 usb_set_device_pwrlvl0(dev_info_t *dip)
952 {
953 USB_DPRINTF_L4(DPRINT_MASK_USBAI, usbai_log_handle,
954 "usb_set_device_pwrlvl0 : Not Yet Implemented");
955
956 return (USB_SUCCESS);
957 }
958
959
960 /* set device power level to 1 */
961 /*ARGSUSED*/
962 int
usb_set_device_pwrlvl1(dev_info_t * dip)963 usb_set_device_pwrlvl1(dev_info_t *dip)
964 {
965 USB_DPRINTF_L4(DPRINT_MASK_USBAI, usbai_log_handle,
966 "usb_set_device_pwrlvl1 : Not Yet Implemented");
967
968 return (USB_SUCCESS);
969 }
970
971
972 /* set device power level to 2 */
973 /*ARGSUSED*/
974 int
usb_set_device_pwrlvl2(dev_info_t * dip)975 usb_set_device_pwrlvl2(dev_info_t *dip)
976 {
977 USB_DPRINTF_L4(DPRINT_MASK_USBAI, usbai_log_handle,
978 "usb_set_device_pwrlvl2 : Not Yet Implemented");
979
980 return (USB_SUCCESS);
981 }
982
983
984 /* set device power level to 3 */
985 /*ARGSUSED*/
986 int
usb_set_device_pwrlvl3(dev_info_t * dip)987 usb_set_device_pwrlvl3(dev_info_t *dip)
988 {
989 USB_DPRINTF_L4(DPRINT_MASK_USBAI, usbai_log_handle,
990 "usb_set_device_pwrlvl3 : Not Yet Implemented");
991
992 return (USB_SUCCESS);
993 }
994
995
996 /*
997 * USB event management
998 */
999 typedef void (*peh_t)(dev_info_t *, ddi_eventcookie_t, void *, void *);
1000
1001
1002 /*
1003 * usb_register_hotplug_cbs:
1004 * Register to get callbacks for hotplug events
1005 */
1006 /*ARGSUSED*/
1007 int
usb_register_hotplug_cbs(dev_info_t * dip,int (* disconnect_event_handler)(dev_info_t *),int (* reconnect_event_handler)(dev_info_t *))1008 usb_register_hotplug_cbs(dev_info_t *dip,
1009 int (*disconnect_event_handler)(dev_info_t *),
1010 int (*reconnect_event_handler)(dev_info_t *))
1011 {
1012 usba_device_t *usba_device;
1013 usba_evdata_t *evdata;
1014
1015 if ((dip == NULL) || (disconnect_event_handler == NULL) ||
1016 (reconnect_event_handler == NULL)) {
1017 USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle,
1018 "usb_register_hotplug_cbs: Bad argument(s)");
1019
1020 return (USB_FAILURE);
1021 }
1022
1023 USB_DPRINTF_L4(DPRINT_MASK_USBAI, usbai_log_handle,
1024 "usb_register_hotplug_cbs: entry");
1025
1026 /*
1027 * The event list searches by ddi_get_eventcookie calls below, go
1028 * through hubd and so do not apply to host controllers.
1029 */
1030 ASSERT(!usba_is_root_hub(dip));
1031
1032 usba_device = usba_get_usba_device(dip);
1033 evdata = usba_get_evdata(dip);
1034
1035 if (usba_device->rm_cookie == NULL) {
1036 if (ddi_get_eventcookie(dip, DDI_DEVI_REMOVE_EVENT,
1037 &usba_device->rm_cookie) != DDI_SUCCESS) {
1038 USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle,
1039 "usb_register_hotplug_cbs: get rm cookie failed");
1040
1041 goto fail;
1042 }
1043 }
1044 if (ddi_add_event_handler(dip, usba_device->rm_cookie,
1045 (peh_t)(uintptr_t)disconnect_event_handler,
1046 NULL, &evdata->ev_rm_cb_id) != DDI_SUCCESS) {
1047 USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle,
1048 "usb_register_hotplug_cbs: add disconnect handler failed");
1049
1050 goto fail;
1051 }
1052
1053 if (usba_device->ins_cookie == NULL) {
1054 if (ddi_get_eventcookie(dip, DDI_DEVI_INSERT_EVENT,
1055 &usba_device->ins_cookie) != DDI_SUCCESS) {
1056 USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle,
1057 "usb_register_hotplug_cbs: get ins cookie failed");
1058
1059 goto fail;
1060 }
1061 }
1062 if (ddi_add_event_handler(dip, usba_device->ins_cookie,
1063 (peh_t)(uintptr_t)reconnect_event_handler,
1064 NULL, &evdata->ev_ins_cb_id) != DDI_SUCCESS) {
1065 USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle,
1066 "usb_register_hotplug_cbs: add reconnect handler failed");
1067
1068 goto fail;
1069 }
1070
1071 mutex_enter(&usba_device->usb_mutex);
1072 usba_device->usb_client_flags[usba_get_ifno(dip)] |=
1073 USBA_CLIENT_FLAG_EV_CBS;
1074 usba_device->usb_client_ev_cb_list->dip = dip;
1075 mutex_exit(&usba_device->usb_mutex);
1076
1077 return (USB_SUCCESS);
1078
1079 fail:
1080 usb_unregister_hotplug_cbs(dip);
1081
1082 return (USB_FAILURE);
1083
1084 }
1085
1086
1087 /*
1088 * usb_unregister_hotplug_cbs:
1089 * Unregister hotplug callbacks
1090 */
1091 /*ARGSUSED*/
1092 void
usb_unregister_hotplug_cbs(dev_info_t * dip)1093 usb_unregister_hotplug_cbs(dev_info_t *dip)
1094 {
1095 usb_unregister_event_cbs(dip, NULL);
1096 }
1097
1098
1099 /*
1100 * usb_register_event_cbs:
1101 * Register to get callbacks for USB events
1102 */
1103 /*ARGSUSED*/
1104 int
usb_register_event_cbs(dev_info_t * dip,usb_event_t * usb_evdata,usb_flags_t flags)1105 usb_register_event_cbs(dev_info_t *dip, usb_event_t *usb_evdata,
1106 usb_flags_t flags)
1107 {
1108 usba_device_t *usba_device;
1109 usba_evdata_t *evdata;
1110
1111 if ((dip == NULL) || (usb_evdata == NULL)) {
1112
1113 return (USB_FAILURE);
1114 }
1115
1116 /*
1117 * The event list searches by ddi_get_eventcookie calls below, go
1118 * through hubd and so do not apply to host controllers.
1119 */
1120 ASSERT(!usba_is_root_hub(dip));
1121
1122 usba_device = usba_get_usba_device(dip);
1123 evdata = usba_get_evdata(dip);
1124
1125 if (usb_evdata->disconnect_event_handler != NULL) {
1126 if (usba_device->rm_cookie == NULL) {
1127 if (ddi_get_eventcookie(dip, DDI_DEVI_REMOVE_EVENT,
1128 &usba_device->rm_cookie) != DDI_SUCCESS) {
1129
1130 goto fail;
1131 }
1132 }
1133 if (ddi_add_event_handler(dip, usba_device->rm_cookie,
1134 (peh_t)(uintptr_t)usb_evdata->disconnect_event_handler,
1135 NULL, &evdata->ev_rm_cb_id) != DDI_SUCCESS) {
1136
1137 goto fail;
1138 }
1139 }
1140 if (usb_evdata->reconnect_event_handler != NULL) {
1141 if (usba_device->ins_cookie == NULL) {
1142 if (ddi_get_eventcookie(dip, DDI_DEVI_INSERT_EVENT,
1143 &usba_device->ins_cookie) != DDI_SUCCESS) {
1144
1145 goto fail;
1146 }
1147 }
1148 if (ddi_add_event_handler(dip, usba_device->ins_cookie,
1149 (peh_t)(uintptr_t)usb_evdata->reconnect_event_handler,
1150 NULL, &evdata->ev_ins_cb_id) != DDI_SUCCESS) {
1151
1152 goto fail;
1153 }
1154 }
1155 if (usb_evdata->post_resume_event_handler != NULL) {
1156 if (usba_device->resume_cookie == NULL) {
1157 if (ddi_get_eventcookie(dip, USBA_POST_RESUME_EVENT,
1158 &usba_device->resume_cookie) != DDI_SUCCESS) {
1159
1160 goto fail;
1161 }
1162 }
1163 if (ddi_add_event_handler(dip, usba_device->resume_cookie,
1164 (peh_t)(uintptr_t)usb_evdata->post_resume_event_handler,
1165 NULL, &evdata->ev_resume_cb_id) != DDI_SUCCESS) {
1166
1167 goto fail;
1168 }
1169 }
1170 if (usb_evdata->pre_suspend_event_handler != NULL) {
1171 if (usba_device->suspend_cookie == NULL) {
1172 if (ddi_get_eventcookie(dip, USBA_PRE_SUSPEND_EVENT,
1173 &usba_device->suspend_cookie) != DDI_SUCCESS) {
1174
1175 goto fail;
1176 }
1177 }
1178 if (ddi_add_event_handler(dip, usba_device->suspend_cookie,
1179 (peh_t)(uintptr_t)usb_evdata->pre_suspend_event_handler,
1180 NULL, &evdata->ev_suspend_cb_id) != DDI_SUCCESS) {
1181
1182 goto fail;
1183 }
1184 }
1185
1186 mutex_enter(&usba_device->usb_mutex);
1187 usba_device->usb_client_flags[usba_get_ifno(dip)] |=
1188 USBA_CLIENT_FLAG_EV_CBS;
1189 usba_device->usb_client_ev_cb_list->dip = dip;
1190 usba_device->usb_client_ev_cb_list->ev_data = usb_evdata;
1191 mutex_exit(&usba_device->usb_mutex);
1192
1193 return (USB_SUCCESS);
1194
1195 fail:
1196 usb_unregister_event_cbs(dip, usb_evdata);
1197
1198 return (USB_FAILURE);
1199
1200 }
1201
1202
1203 /*
1204 * usb_unregister_event_cbs:
1205 * Unregister all event callbacks
1206 */
1207 /*ARGSUSED*/
1208 void
usb_unregister_event_cbs(dev_info_t * dip,usb_event_t * usb_evdata)1209 usb_unregister_event_cbs(dev_info_t *dip, usb_event_t *usb_evdata)
1210 {
1211 usba_evdata_t *evdata;
1212 usba_device_t *usba_device = usba_get_usba_device(dip);
1213
1214 evdata = usba_get_evdata(dip);
1215
1216 if (evdata->ev_rm_cb_id != NULL) {
1217 (void) ddi_remove_event_handler(evdata->ev_rm_cb_id);
1218 evdata->ev_rm_cb_id = NULL;
1219 }
1220
1221 if (evdata->ev_ins_cb_id != NULL) {
1222 (void) ddi_remove_event_handler(evdata->ev_ins_cb_id);
1223 evdata->ev_ins_cb_id = NULL;
1224 }
1225
1226 if (evdata->ev_suspend_cb_id != NULL) {
1227 (void) ddi_remove_event_handler(evdata->ev_suspend_cb_id);
1228 evdata->ev_suspend_cb_id = NULL;
1229 }
1230
1231 if (evdata->ev_resume_cb_id != NULL) {
1232 (void) ddi_remove_event_handler(evdata->ev_resume_cb_id);
1233 evdata->ev_resume_cb_id = NULL;
1234 }
1235
1236 mutex_enter(&usba_device->usb_mutex);
1237 usba_device->usb_client_flags[usba_get_ifno(dip)] &=
1238 ~USBA_CLIENT_FLAG_EV_CBS;
1239 mutex_exit(&usba_device->usb_mutex);
1240 }
1241
1242 int
usb_reset_device(dev_info_t * dip,usb_dev_reset_lvl_t reset_level)1243 usb_reset_device(dev_info_t *dip, usb_dev_reset_lvl_t reset_level)
1244 {
1245 return (usba_hubdi_reset_device(dip, reset_level));
1246 }
1247
1248 /*
1249 * usb device driver registration
1250 */
1251 int
usb_register_dev_driver(dev_info_t * dip,usb_dev_driver_callback_t cb)1252 usb_register_dev_driver(dev_info_t *dip, usb_dev_driver_callback_t cb)
1253 {
1254 USB_DPRINTF_L4(DPRINT_MASK_USBAI, usbai_log_handle,
1255 "usb_register_dev_driver: register the specified driver "
1256 "in usba: dip = 0x%p", (void *)dip);
1257
1258 if (cb != NULL) {
1259 usb_cap.dip = dip;
1260 usb_cap.usba_dev_driver_cb = cb;
1261
1262 return (USB_SUCCESS);
1263 }
1264
1265 return (USB_FAILURE);
1266 }
1267
1268 /*
1269 * usb device driver unregistration
1270 */
1271 void
usb_unregister_dev_driver(dev_info_t * dip)1272 usb_unregister_dev_driver(dev_info_t *dip)
1273 {
1274 USB_DPRINTF_L4(DPRINT_MASK_USBAI, usbai_log_handle,
1275 "usb_unregister_dev_driver: unregister the registered "
1276 "driver: dip =0x%p", (void *)dip);
1277
1278 ASSERT(dip == usb_cap.dip);
1279 usb_cap.dip = NULL;
1280 usb_cap.usba_dev_driver_cb = NULL;
1281 }
1282