xref: /titanic_52/usr/src/uts/common/io/usb/usba/usbai.c (revision fa9e4066f08beec538e775443c5be79dd423fcab)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  *
22  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * USBA: Solaris USB Architecture support
30  *
31  * all functions exposed to client drivers  have prefix usb_ while all USBA
32  * internal functions or functions exposed to HCD or hubd only have prefix
33  * usba_
34  *
35  * this file contains initializations, logging/tracing support and PM
36  * support
37  */
38 #define	USBA_FRAMEWORK
39 #include <sys/varargs.h>
40 #include <sys/usb/usba/usba_impl.h>
41 #include <sys/usb/usba/usba10.h>
42 
43 /*
44  * print buffer protected by mutex for debug stuff. the mutex also
45  * ensures serializing debug messages
46  */
47 static kmutex_t	usba_print_mutex;
48 static char usba_print_buf[USBA_PRINT_BUF_LEN];
49 kmutex_t usbai_mutex;
50 
51 /*
52  * debug stuff
53  */
54 usb_log_handle_t	usbai_log_handle;
55 static uint_t		usbai_errlevel = USB_LOG_L4;
56 static uint_t		usbai_errmask = (uint_t)-1;
57 
58 #define	USBA_DEBUG_SIZE_EXTRA_ALLOC	8
59 #ifdef	DEBUG
60 #define	USBA_DEBUG_BUF_SIZE		0x40000
61 #else
62 #define	USBA_DEBUG_BUF_SIZE		0x4000
63 #endif	/* DEBUG */
64 
65 #define	USBA_POWER_STR_SIZE		40
66 
67 static int usba_suppress_dprintf;		/* Suppress debug printing */
68 static int usba_clear_debug_buf_flag;		/* clear debug buf */
69 static int usba_buffer_dprintf = 1;		/* Use a debug print buffer */
70 static int usba_timestamp_dprintf = 0;		/* get time stamps in trace */
71 static hrtime_t usba_last_timestamp;		/* last time stamp in trace */
72 static int usba_debug_buf_size = USBA_DEBUG_BUF_SIZE;	/* Size of debug buf */
73 static char *usba_debug_buf = NULL;			/* The debug buf */
74 static char *usba_buf_sptr, *usba_buf_eptr;
75 static int usba_debug_chatty;			/* L1 msg on console */
76 
77 /*
78  * Set to 1 to enable PM.
79  */
80 int usb_force_enable_pm = 0;
81 
82 
83 /* USBA framework initializations */
84 void
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
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
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
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
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
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
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 *
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
218 usb_vprintf(dev_info_t *dip, int level, char *label, char *fmt, va_list ap)
219 {
220 	size_t len;
221 	int instance;
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 = 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
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
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
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
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
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
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
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
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
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
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
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
676 usb_is_pm_enabled(dev_info_t *dip)
677 {
678 #if defined(__sparc)
679 	usba_device_t  *usba_device = usba_get_usba_device(dip);
680 #endif
681 
682 	switch (usb_force_enable_pm) {
683 	case -1:
684 		/* no PM at all */
685 
686 		return (USB_FAILURE);
687 	case 1:
688 		/* PM on all platforms, regardless of hcd */
689 
690 		return (USB_SUCCESS);
691 	case 0:
692 	default:
693 
694 		break;
695 
696 	}
697 
698 #if defined(__sparc)
699 	/* uhci does not have PM support, ehci has resume problems */
700 	if (usba_device &&
701 	    ((strcmp(ddi_driver_name(usba_device->usb_root_hub_dip),
702 	    "uhci") == 0) ||
703 	    (strcmp(ddi_driver_name(usba_device->usb_root_hub_dip),
704 	    "ehci") == 0))) {
705 		USB_DPRINTF_L1(DPRINT_MASK_USBA, usbai_log_handle,
706 		    "%s%d: no PM enabled for this device",
707 		    ddi_driver_name(dip), ddi_get_instance(dip));
708 
709 		return (USB_FAILURE);
710 	}
711 
712 	return (USB_SUCCESS);
713 #else
714 	return (USB_FAILURE);
715 #endif
716 }
717 
718 
719 /*
720  * usba_handle_device_remote_wakeup:
721  *	internal function to enable/disable remote wakeup in the device
722  *	or interface
723  */
724 static int
725 usba_handle_device_remote_wakeup(dev_info_t *dip, int cmd)
726 {
727 	int		rval;
728 	uint8_t 	bmRequest = USB_DEV_REQ_HOST_TO_DEV;
729 	uchar_t		bRequest;
730 	uint16_t	wIndex = 0;
731 	usb_cr_t	completion_reason = 0;
732 	usb_cb_flags_t	cb_flags;
733 	usb_pipe_handle_t ph;
734 
735 	USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
736 	    "usba_handle_device_remote_wakeup: dip = 0x%p", dip);
737 
738 	USBA_CHECK_CONTEXT();
739 
740 	/* get the default pipe */
741 	ph = usba_get_dflt_pipe_handle(dip);
742 
743 	/* do we own the device? */
744 	if (usb_owns_device(dip)) {
745 		bmRequest |= USB_DEV_REQ_RCPT_DEV;
746 	} else {
747 		bmRequest |= USB_DEV_REQ_RCPT_IF;
748 		wIndex = usba_get_ifno(dip);
749 	}
750 	bRequest = ((cmd == USB_REMOTE_WAKEUP_ENABLE) ? USB_REQ_SET_FEATURE :
751 	    USB_REQ_CLEAR_FEATURE);
752 
753 	if ((rval = usb_pipe_sync_ctrl_xfer(dip, ph,
754 	    bmRequest,			/* bmRequest */
755 	    bRequest,			/* bRequest */
756 	    USB_DEV_REMOTE_WAKEUP,	/* wValue */
757 	    wIndex,			/* wIndex */
758 	    0,				/* wLength */
759 	    NULL, 0,
760 	    &completion_reason,
761 	    &cb_flags, USB_FLAGS_SLEEP)) != USB_SUCCESS) {
762 		USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle,
763 		    "Set/ClearFeature (RemoteWakep) failed: "
764 		    "rval = %d, cmd = %d, cr = 0x%x cb = 0x%x",
765 		    rval, cmd, completion_reason, cb_flags);
766 	}
767 
768 	return (rval);
769 }
770 
771 
772 void
773 usb_enable_parent_notification(dev_info_t *dip)
774 {
775 	USBA_CHECK_CONTEXT();
776 	(void) ndi_prop_create_boolean(DDI_DEV_T_NONE, dip,
777 	    "pm-want-child-notification?");
778 }
779 
780 
781 /*
782  * usb_handle_remote_wakeup:
783  *	check if device supports remote wakeup and, if so, enable/disable
784  *	remote wake up in the device depending upon the command
785  */
786 int
787 usb_handle_remote_wakeup(dev_info_t *dip, int cmd)
788 {
789 	usb_cfg_descr_t	cfg_descr;
790 	uchar_t 	*usb_cfg;	/* buf for config descriptor */
791 	size_t		cfg_length;
792 	int		rval;
793 
794 	USBA_CHECK_CONTEXT();
795 
796 	/* Obtain the raw configuration descriptor */
797 	usb_cfg = usb_get_raw_cfg_data(dip, &cfg_length);
798 
799 	/* get configuration descriptor, must succeed */
800 	rval = usb_parse_cfg_descr(usb_cfg, cfg_length,
801 	    &cfg_descr, USB_CFG_DESCR_SIZE);
802 	ASSERT(rval == USB_CFG_DESCR_SIZE);
803 
804 	/*
805 	 * If the device supports remote wakeup, and PM is enabled,
806 	 * we enable remote wakeup in the device
807 	 */
808 	if ((usb_is_pm_enabled(dip) == USB_SUCCESS) &&
809 	    (cfg_descr.bmAttributes & USB_CFG_ATTR_REMOTE_WAKEUP)) {
810 
811 		rval = usba_handle_device_remote_wakeup(dip, cmd);
812 	} else {
813 		rval = USB_FAILURE;
814 	}
815 
816 	return (rval);
817 }
818 
819 
820 /*
821  * usb_create_pm_components:
822  *	map descriptor into  pm properties
823  */
824 int
825 usb_create_pm_components(dev_info_t *dip, uint_t *pwr_states)
826 {
827 	uchar_t 		*usb_cfg;	/* buf for config descriptor */
828 	usb_cfg_descr_t		cfg_descr;
829 	size_t			cfg_length;
830 	usba_cfg_pwr_descr_t	confpwr_descr;
831 	usba_if_pwr_descr_t	ifpwr_descr;
832 	uint8_t 		cfg_attrib;
833 	int			i, lvl, rval;
834 	int			n_prop = 0;
835 	uint8_t 		*ptr;
836 	char			*drvname;
837 	char			str[USBA_POWER_STR_SIZE];
838 	char			*pm_comp[USBA_N_PMCOMP];
839 
840 	USBA_CHECK_CONTEXT();
841 
842 	if (usb_is_pm_enabled(dip) != USB_SUCCESS) {
843 
844 		return (USB_FAILURE);
845 	}
846 
847 	/* Obtain the raw configuration descriptor */
848 	usb_cfg = usb_get_raw_cfg_data(dip, &cfg_length);
849 
850 	/* get configuration descriptor, must succceed */
851 	rval = usb_parse_cfg_descr(usb_cfg, cfg_length,
852 	    &cfg_descr, USB_CFG_DESCR_SIZE);
853 	ASSERT(rval == USB_CFG_DESCR_SIZE);
854 
855 	cfg_attrib = cfg_descr.bmAttributes;
856 	*pwr_states = 0;
857 
858 	/*
859 	 * Now start creating the pm-components strings
860 	 */
861 	drvname = (char *)ddi_driver_name(dip);
862 	(void) snprintf(str, USBA_POWER_STR_SIZE, "NAME= %s%d Power",
863 	    drvname, ddi_get_instance(dip));
864 
865 	pm_comp[n_prop] = kmem_zalloc(strlen(str) + 1, KM_SLEEP);
866 	(void) strcpy(pm_comp[n_prop++], str);
867 
868 	/*
869 	 * if the device is bus powered we look at the bBusPowerSavingDx
870 	 * fields else we look at bSelfPowerSavingDx fields.
871 	 * OS and USB power states are numerically reversed,
872 	 *
873 	 * Here is the mapping :-
874 	 *	OS State	USB State
875 	 *	0		D3	(minimal or no power)
876 	 *	1		D2
877 	 *	2		D1
878 	 *	3		D0	(Full power)
879 	 *
880 	 * if we own the whole device, we look at the config pwr descr
881 	 * else at the interface pwr descr.
882 	 */
883 	if (usb_owns_device(dip)) {
884 		/* Parse the configuration power descriptor */
885 		rval = usba_parse_cfg_pwr_descr(usb_cfg, cfg_length,
886 		    &confpwr_descr, USBA_CFG_PWR_DESCR_SIZE);
887 
888 		if (rval != USBA_CFG_PWR_DESCR_SIZE) {
889 			USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle,
890 			    "usb_create_pm_components: "
891 			    "usb_parse_cfg_pwr_descr returns length of %d, "
892 			    "expecting %d", rval, USBA_CFG_PWR_DESCR_SIZE);
893 
894 			return (USB_FAILURE);
895 		}
896 
897 		if (cfg_attrib & USB_CFG_ATTR_SELFPWR) {
898 			ptr = &confpwr_descr.bSelfPowerSavingD3;
899 		} else {
900 			ptr = &confpwr_descr.bBusPowerSavingD3;
901 		}
902 	} else {
903 		/* Parse the interface power descriptor */
904 		rval = usba_parse_if_pwr_descr(usb_cfg,
905 			cfg_length,
906 			usba_get_ifno(dip),	/* interface index */
907 			0,			/* XXXX alt interface index */
908 			&ifpwr_descr,
909 			USBA_IF_PWR_DESCR_SIZE);
910 
911 		if (rval != USBA_IF_PWR_DESCR_SIZE) {
912 			USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle,
913 			    "usb_create_pm_components: "
914 			    "usb_parse_if_pwr_descr "
915 			    "returns length of %d, "
916 			    "expecting %d", rval, USBA_CFG_PWR_DESCR_SIZE);
917 
918 			return (USB_FAILURE);
919 		}
920 
921 		if (cfg_attrib & USB_CFG_ATTR_SELFPWR) {
922 			ptr =  &ifpwr_descr.bSelfPowerSavingD3;
923 		} else {
924 			ptr =  &ifpwr_descr.bBusPowerSavingD3;
925 		}
926 	}
927 
928 	/* walk thru levels and create prop level=name strings */
929 	for (lvl = USB_DEV_OS_PWR_0; lvl <= USB_DEV_OS_PWR_3; lvl++) {
930 		if (*ptr || (lvl == USB_DEV_OS_PWR_3)) {
931 			(void) snprintf(str, USBA_POWER_STR_SIZE,
932 			    "%d=USB D%d State",
933 			    lvl, USB_DEV_OS_PWR2USB_PWR(lvl));
934 			pm_comp[n_prop] = kmem_zalloc(strlen(str) + 1,
935 								KM_SLEEP);
936 			(void) strcpy(pm_comp[n_prop++], str);
937 
938 			*pwr_states |= USB_DEV_PWRMASK(lvl);
939 		}
940 
941 		ptr -= 2; /* skip to the next power state */
942 	}
943 
944 	USB_DPRINTF_L3(DPRINT_MASK_USBAI, usbai_log_handle,
945 	    "usb_create_pm_components: pwr_states: %x", *pwr_states);
946 
947 	/* now create the actual components */
948 	rval = ddi_prop_update_string_array(DDI_DEV_T_NONE, dip,
949 			"pm-components", pm_comp, n_prop);
950 	if (rval == DDI_PROP_SUCCESS) {
951 		rval = USB_SUCCESS;
952 	} else {
953 		rval = USB_FAILURE;
954 	}
955 
956 	/* display & delete properties */
957 	USB_DPRINTF_L3(DPRINT_MASK_USBAI, usbai_log_handle,
958 	    "usb_create_pm_components: The properties are:");
959 	for (i = 0; i < n_prop; i++) {
960 		USB_DPRINTF_L3(DPRINT_MASK_USBAI, usbai_log_handle,
961 		    "\t%s", pm_comp[i]);
962 		kmem_free(pm_comp[i], strlen(pm_comp[i]) + 1);
963 	}
964 
965 	return (rval);
966 }
967 
968 
969 /*
970  * Generic Functions to set the power level of any usb device
971  *
972  * Since OS and USB power states are numerically reverse,
973  * Here is the mapping :-
974  *	OS State	USB State
975  *	0		D3	(minimal or no power)
976  *	1		D2
977  *	2		D1
978  *	3		D0	(Full power)
979  */
980 
981 /* set device power level to 0 (full power) */
982 /*ARGSUSED*/
983 int
984 usb_set_device_pwrlvl0(dev_info_t *dip)
985 {
986 	USB_DPRINTF_L4(DPRINT_MASK_USBAI, usbai_log_handle,
987 	    "usb_set_device_pwrlvl0 : Not Yet Implemented");
988 
989 	return (USB_SUCCESS);
990 }
991 
992 
993 /* set device power level to 1	*/
994 /*ARGSUSED*/
995 int
996 usb_set_device_pwrlvl1(dev_info_t *dip)
997 {
998 	USB_DPRINTF_L4(DPRINT_MASK_USBAI, usbai_log_handle,
999 	    "usb_set_device_pwrlvl1 : Not Yet Implemented");
1000 
1001 	return (USB_SUCCESS);
1002 }
1003 
1004 
1005 /* set device power level to 2	*/
1006 /*ARGSUSED*/
1007 int
1008 usb_set_device_pwrlvl2(dev_info_t *dip)
1009 {
1010 	USB_DPRINTF_L4(DPRINT_MASK_USBAI, usbai_log_handle,
1011 	    "usb_set_device_pwrlvl2 : Not Yet Implemented");
1012 
1013 	return (USB_SUCCESS);
1014 }
1015 
1016 
1017 /* set device power level to 3	*/
1018 /*ARGSUSED*/
1019 int
1020 usb_set_device_pwrlvl3(dev_info_t *dip)
1021 {
1022 	USB_DPRINTF_L4(DPRINT_MASK_USBAI, usbai_log_handle,
1023 	    "usb_set_device_pwrlvl3 : Not Yet Implemented");
1024 
1025 	return (USB_SUCCESS);
1026 }
1027 
1028 
1029 /*
1030  * USB event management
1031  */
1032 typedef void (*peh_t)(dev_info_t *, ddi_eventcookie_t, void *, void *);
1033 
1034 
1035 /*
1036  * usb_register_hotplug_cbs:
1037  *	Register to get callbacks for hotplug events
1038  */
1039 /*ARGSUSED*/
1040 int
1041 usb_register_hotplug_cbs(dev_info_t *dip,
1042     int (*disconnect_event_handler)(dev_info_t *),
1043     int (*reconnect_event_handler)(dev_info_t *))
1044 {
1045 	usba_device_t		*usba_device;
1046 	usba_evdata_t		*evdata;
1047 
1048 	if ((dip == NULL) || (disconnect_event_handler == NULL) ||
1049 	    (reconnect_event_handler == NULL)) {
1050 		USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle,
1051 		    "usb_register_hotplug_cbs: Bad argument(s)");
1052 
1053 		return (USB_FAILURE);
1054 	}
1055 
1056 	USB_DPRINTF_L4(DPRINT_MASK_USBAI, usbai_log_handle,
1057 	    "usb_register_hotplug_cbs: entry");
1058 
1059 	/*
1060 	 * The event list searches by ddi_get_eventcookie calls below, go
1061 	 * through hubd and so do not apply to host controllers.
1062 	 */
1063 	ASSERT(!usba_is_root_hub(dip));
1064 
1065 	usba_device = usba_get_usba_device(dip);
1066 	evdata = usba_get_evdata(dip);
1067 
1068 	if (usba_device->rm_cookie == NULL) {
1069 		if (ddi_get_eventcookie(dip, DDI_DEVI_REMOVE_EVENT,
1070 		    &usba_device->rm_cookie) != DDI_SUCCESS) {
1071 			USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle,
1072 			    "usb_register_hotplug_cbs: get rm cookie failed");
1073 
1074 			goto fail;
1075 		}
1076 	}
1077 	if (ddi_add_event_handler(dip, usba_device->rm_cookie,
1078 	    (peh_t)disconnect_event_handler,
1079 	    NULL, &evdata->ev_rm_cb_id) != DDI_SUCCESS) {
1080 		USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle,
1081 		    "usb_register_hotplug_cbs: add disconnect handler failed");
1082 
1083 		goto fail;
1084 	}
1085 
1086 	if (usba_device->ins_cookie == NULL) {
1087 		if (ddi_get_eventcookie(dip, DDI_DEVI_INSERT_EVENT,
1088 		    &usba_device->ins_cookie) != DDI_SUCCESS) {
1089 			USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle,
1090 			    "usb_register_hotplug_cbs: get ins cookie failed");
1091 
1092 			goto fail;
1093 		}
1094 	}
1095 	if (ddi_add_event_handler(dip, usba_device->ins_cookie,
1096 	    (peh_t)reconnect_event_handler,
1097 	    NULL, &evdata->ev_ins_cb_id) != DDI_SUCCESS) {
1098 		USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle,
1099 		    "usb_register_hotplug_cbs: add reconnect handler failed");
1100 
1101 		goto fail;
1102 	}
1103 
1104 	mutex_enter(&usba_device->usb_mutex);
1105 	usba_device->usb_client_flags[usba_get_ifno(dip)] |=
1106 						USBA_CLIENT_FLAG_EV_CBS;
1107 	usba_device->usb_client_ev_cb_list->dip = dip;
1108 	mutex_exit(&usba_device->usb_mutex);
1109 
1110 	return (USB_SUCCESS);
1111 
1112 fail:
1113 	usb_unregister_hotplug_cbs(dip);
1114 
1115 	return (USB_FAILURE);
1116 
1117 }
1118 
1119 
1120 /*
1121  * usb_unregister_hotplug_cbs:
1122  *	Unregister hotplug callbacks
1123  */
1124 /*ARGSUSED*/
1125 void
1126 usb_unregister_hotplug_cbs(dev_info_t *dip)
1127 {
1128 	usb_unregister_event_cbs(dip, NULL);
1129 }
1130 
1131 
1132 /*
1133  * usb_register_event_cbs:
1134  *	Register to get callbacks for USB events
1135  */
1136 /*ARGSUSED*/
1137 int
1138 usb_register_event_cbs(dev_info_t *dip, usb_event_t *usb_evdata,
1139 	usb_flags_t flags)
1140 {
1141 	usba_device_t		*usba_device;
1142 	usba_evdata_t		*evdata;
1143 
1144 	if ((dip == NULL) || (usb_evdata == NULL)) {
1145 
1146 		return (USB_FAILURE);
1147 	}
1148 
1149 	/*
1150 	 * The event list searches by ddi_get_eventcookie calls below, go
1151 	 * through hubd and so do not apply to host controllers.
1152 	 */
1153 	ASSERT(!usba_is_root_hub(dip));
1154 
1155 	usba_device = usba_get_usba_device(dip);
1156 	evdata = usba_get_evdata(dip);
1157 
1158 	if (usb_evdata->disconnect_event_handler != NULL) {
1159 		if (usba_device->rm_cookie == NULL) {
1160 			if (ddi_get_eventcookie(dip, DDI_DEVI_REMOVE_EVENT,
1161 			    &usba_device->rm_cookie) != DDI_SUCCESS) {
1162 
1163 				goto fail;
1164 			}
1165 		}
1166 		if (ddi_add_event_handler(dip, usba_device->rm_cookie,
1167 		    (peh_t)usb_evdata->disconnect_event_handler,
1168 		    NULL, &evdata->ev_rm_cb_id) != DDI_SUCCESS) {
1169 
1170 			goto fail;
1171 		}
1172 	}
1173 	if (usb_evdata->reconnect_event_handler != NULL) {
1174 		if (usba_device->ins_cookie == NULL) {
1175 			if (ddi_get_eventcookie(dip, DDI_DEVI_INSERT_EVENT,
1176 			    &usba_device->ins_cookie) != DDI_SUCCESS) {
1177 
1178 				goto fail;
1179 			}
1180 		}
1181 		if (ddi_add_event_handler(dip, usba_device->ins_cookie,
1182 		    (peh_t)usb_evdata->reconnect_event_handler,
1183 		    NULL, &evdata->ev_ins_cb_id) != DDI_SUCCESS) {
1184 
1185 			goto fail;
1186 		}
1187 	}
1188 	if (usb_evdata->post_resume_event_handler != NULL) {
1189 		if (usba_device->resume_cookie == NULL) {
1190 			if (ddi_get_eventcookie(dip, USBA_POST_RESUME_EVENT,
1191 			    &usba_device->resume_cookie) != DDI_SUCCESS) {
1192 
1193 				goto fail;
1194 			}
1195 		}
1196 		if (ddi_add_event_handler(dip, usba_device->resume_cookie,
1197 		    (peh_t)usb_evdata->post_resume_event_handler,
1198 		    NULL, &evdata->ev_resume_cb_id) != DDI_SUCCESS) {
1199 
1200 			goto fail;
1201 		}
1202 	}
1203 	if (usb_evdata->pre_suspend_event_handler != NULL) {
1204 		if (usba_device->suspend_cookie == NULL) {
1205 			if (ddi_get_eventcookie(dip, USBA_PRE_SUSPEND_EVENT,
1206 			    &usba_device->suspend_cookie) != DDI_SUCCESS) {
1207 
1208 				goto fail;
1209 			}
1210 		}
1211 		if (ddi_add_event_handler(dip, usba_device->suspend_cookie,
1212 		    (peh_t)usb_evdata->pre_suspend_event_handler,
1213 		    NULL, &evdata->ev_suspend_cb_id) != DDI_SUCCESS) {
1214 
1215 			goto fail;
1216 		}
1217 	}
1218 
1219 	mutex_enter(&usba_device->usb_mutex);
1220 	usba_device->usb_client_flags[usba_get_ifno(dip)] |=
1221 						USBA_CLIENT_FLAG_EV_CBS;
1222 	usba_device->usb_client_ev_cb_list->dip = dip;
1223 	usba_device->usb_client_ev_cb_list->ev_data = usb_evdata;
1224 	mutex_exit(&usba_device->usb_mutex);
1225 
1226 	return (USB_SUCCESS);
1227 
1228 fail:
1229 	usb_unregister_event_cbs(dip, usb_evdata);
1230 
1231 	return (USB_FAILURE);
1232 
1233 }
1234 
1235 
1236 /*
1237  * usb_unregister_event_cbs:
1238  *	Unregister all event callbacks
1239  */
1240 /*ARGSUSED*/
1241 void
1242 usb_unregister_event_cbs(dev_info_t *dip, usb_event_t *usb_evdata)
1243 {
1244 	usba_evdata_t		*evdata;
1245 	usba_device_t		*usba_device = usba_get_usba_device(dip);
1246 
1247 	evdata = usba_get_evdata(dip);
1248 
1249 	if (evdata->ev_rm_cb_id != NULL) {
1250 		(void) ddi_remove_event_handler(evdata->ev_rm_cb_id);
1251 		evdata->ev_rm_cb_id = NULL;
1252 	}
1253 
1254 	if (evdata->ev_ins_cb_id != NULL) {
1255 		(void) ddi_remove_event_handler(evdata->ev_ins_cb_id);
1256 		evdata->ev_ins_cb_id = NULL;
1257 	}
1258 
1259 	if (evdata->ev_suspend_cb_id != NULL) {
1260 		(void) ddi_remove_event_handler(evdata->ev_suspend_cb_id);
1261 		evdata->ev_suspend_cb_id = NULL;
1262 	}
1263 
1264 	if (evdata->ev_resume_cb_id != NULL) {
1265 		(void) ddi_remove_event_handler(evdata->ev_resume_cb_id);
1266 		evdata->ev_resume_cb_id = NULL;
1267 	}
1268 
1269 	mutex_enter(&usba_device->usb_mutex);
1270 	usba_device->usb_client_flags[usba_get_ifno(dip)] &=
1271 						~USBA_CLIENT_FLAG_EV_CBS;
1272 	mutex_exit(&usba_device->usb_mutex);
1273 }
1274