xref: /onnv-gate/usr/src/uts/common/io/usb/usba/parser.c (revision 7425:e4dbffd35ebc)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
53341Sgc161489  * Common Development and Distribution License (the "License").
63341Sgc161489  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*7425SGongtian.Zhao@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate 
270Sstevel@tonic-gate /*
280Sstevel@tonic-gate  * Descriptor parsing functions
290Sstevel@tonic-gate  */
300Sstevel@tonic-gate #define	USBA_FRAMEWORK
310Sstevel@tonic-gate #include <sys/usb/usba/usba_impl.h>
320Sstevel@tonic-gate 
330Sstevel@tonic-gate #define	INCREMENT_BUF(buf) \
340Sstevel@tonic-gate 		if ((buf)[0] == 0) { \
350Sstevel@tonic-gate 			break; \
360Sstevel@tonic-gate 		} else { \
370Sstevel@tonic-gate 			(buf) += (buf)[0]; \
380Sstevel@tonic-gate 		}
390Sstevel@tonic-gate #define	isdigit(ch) ((ch >= '0') && (ch <= '9'))
400Sstevel@tonic-gate 
410Sstevel@tonic-gate extern usba_cfg_pwr_descr_t default_cfg_power;
420Sstevel@tonic-gate extern usba_if_pwr_descr_t default_if_power;
430Sstevel@tonic-gate 
440Sstevel@tonic-gate size_t
450Sstevel@tonic-gate usb_parse_data(char	*format,
460Sstevel@tonic-gate 	uchar_t 	*data,
470Sstevel@tonic-gate 	size_t		datalen,
480Sstevel@tonic-gate 	void		*structure,
490Sstevel@tonic-gate 	size_t		structlen)
500Sstevel@tonic-gate {
510Sstevel@tonic-gate 	int	fmt;
520Sstevel@tonic-gate 	int	counter = 1;
530Sstevel@tonic-gate 	int	multiplier = 0;
540Sstevel@tonic-gate 	uchar_t	*dataend = data + datalen;
550Sstevel@tonic-gate 	char	*structstart = (char *)structure;
560Sstevel@tonic-gate 	void	*structend = (void *)((intptr_t)structstart + structlen);
570Sstevel@tonic-gate 
580Sstevel@tonic-gate 	if ((format == NULL) || (data == NULL) || (structure == NULL)) {
590Sstevel@tonic-gate 
600Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
610Sstevel@tonic-gate 	}
620Sstevel@tonic-gate 
630Sstevel@tonic-gate 	while ((fmt = *format) != '\0') {
640Sstevel@tonic-gate 
650Sstevel@tonic-gate 		/*
660Sstevel@tonic-gate 		 * Could some one pass a "format" that is greater than
670Sstevel@tonic-gate 		 * the structlen? Conversely, one could pass a ret_buf_len
680Sstevel@tonic-gate 		 * that is less than the "format" length.
690Sstevel@tonic-gate 		 * If so, we need to protect against writing over memory.
700Sstevel@tonic-gate 		 */
710Sstevel@tonic-gate 		if (counter++ > structlen) {
720Sstevel@tonic-gate 			break;
730Sstevel@tonic-gate 		}
740Sstevel@tonic-gate 
750Sstevel@tonic-gate 		if (fmt == 'c') {
760Sstevel@tonic-gate 			uint8_t	*cp = (uint8_t *)structure;
770Sstevel@tonic-gate 
780Sstevel@tonic-gate 			cp = (uint8_t *)(((uintptr_t)cp + _CHAR_ALIGNMENT - 1) &
79*7425SGongtian.Zhao@Sun.COM 			    ~(_CHAR_ALIGNMENT - 1));
800Sstevel@tonic-gate 			if (((data + 1) > dataend) ||
810Sstevel@tonic-gate 			    ((cp + 1) > (uint8_t *)structend))
820Sstevel@tonic-gate 				break;
830Sstevel@tonic-gate 
840Sstevel@tonic-gate 			*cp++ = *data++;
850Sstevel@tonic-gate 			structure = (void *)cp;
860Sstevel@tonic-gate 			if (multiplier) {
870Sstevel@tonic-gate 				multiplier--;
880Sstevel@tonic-gate 			}
890Sstevel@tonic-gate 			if (multiplier == 0) {
900Sstevel@tonic-gate 				format++;
910Sstevel@tonic-gate 			}
920Sstevel@tonic-gate 		} else if (fmt == 's') {
930Sstevel@tonic-gate 			uint16_t	*sp = (uint16_t *)structure;
940Sstevel@tonic-gate 
950Sstevel@tonic-gate 			sp = (uint16_t *)
96*7425SGongtian.Zhao@Sun.COM 			    (((uintptr_t)sp + _SHORT_ALIGNMENT - 1) &
97*7425SGongtian.Zhao@Sun.COM 			    ~(_SHORT_ALIGNMENT - 1));
980Sstevel@tonic-gate 			if (((data + 2) > dataend) ||
990Sstevel@tonic-gate 			    ((sp + 1) > (uint16_t *)structend))
1000Sstevel@tonic-gate 				break;
1010Sstevel@tonic-gate 
1020Sstevel@tonic-gate 			*sp++ = (data[1] << 8) + data[0];
1030Sstevel@tonic-gate 			data += 2;
1040Sstevel@tonic-gate 			structure = (void *)sp;
1050Sstevel@tonic-gate 			if (multiplier) {
1060Sstevel@tonic-gate 				multiplier--;
1070Sstevel@tonic-gate 			}
1080Sstevel@tonic-gate 			if (multiplier == 0) {
1090Sstevel@tonic-gate 				format++;
1100Sstevel@tonic-gate 			}
1110Sstevel@tonic-gate 		} else if (fmt == 'l') {
1120Sstevel@tonic-gate 			uint32_t	*lp = (uint32_t *)structure;
1130Sstevel@tonic-gate 
1140Sstevel@tonic-gate 			lp = (uint32_t *)
115*7425SGongtian.Zhao@Sun.COM 			    (((uintptr_t)lp + _INT_ALIGNMENT - 1) &
116*7425SGongtian.Zhao@Sun.COM 			    ~(_INT_ALIGNMENT - 1));
1170Sstevel@tonic-gate 			if (((data + 4) > dataend) ||
1180Sstevel@tonic-gate 			    ((lp + 1) > (uint32_t *)structend))
1190Sstevel@tonic-gate 				break;
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate 			*lp++ = (((((
122*7425SGongtian.Zhao@Sun.COM 			    (uint32_t)data[3] << 8) | data[2]) << 8) |
123*7425SGongtian.Zhao@Sun.COM 			    data[1]) << 8) | data[0];
1240Sstevel@tonic-gate 			data += 4;
1250Sstevel@tonic-gate 			structure = (void *)lp;
1260Sstevel@tonic-gate 			if (multiplier) {
1270Sstevel@tonic-gate 				multiplier--;
1280Sstevel@tonic-gate 			}
1290Sstevel@tonic-gate 			if (multiplier == 0) {
1300Sstevel@tonic-gate 				format++;
1310Sstevel@tonic-gate 			}
1320Sstevel@tonic-gate 		} else if (fmt == 'L') {
1330Sstevel@tonic-gate 			uint64_t	*llp = (uint64_t *)structure;
1340Sstevel@tonic-gate 
1350Sstevel@tonic-gate 			llp = (uint64_t *)
136*7425SGongtian.Zhao@Sun.COM 			    (((uintptr_t)llp + _LONG_LONG_ALIGNMENT - 1) &
137*7425SGongtian.Zhao@Sun.COM 			    ~(_LONG_LONG_ALIGNMENT - 1));
1380Sstevel@tonic-gate 			if (((data + 8) > dataend) ||
1390Sstevel@tonic-gate 			    ((llp + 1) >= (uint64_t *)structend))
1400Sstevel@tonic-gate 				break;
1410Sstevel@tonic-gate 
1420Sstevel@tonic-gate 			*llp++ = (((((((((((((data[7] << 8) |
143*7425SGongtian.Zhao@Sun.COM 			    data[6]) << 8) | data[5]) << 8) |
144*7425SGongtian.Zhao@Sun.COM 			    data[4]) << 8) | data[3]) << 8) |
145*7425SGongtian.Zhao@Sun.COM 			    data[2]) << 8) | data[1]) << 8) |
146*7425SGongtian.Zhao@Sun.COM 			    data[0];
1470Sstevel@tonic-gate 			data += 8;
1480Sstevel@tonic-gate 			structure = (void *)llp;
1490Sstevel@tonic-gate 			if (multiplier) {
1500Sstevel@tonic-gate 				multiplier--;
1510Sstevel@tonic-gate 			}
1520Sstevel@tonic-gate 			if (multiplier == 0) {
1530Sstevel@tonic-gate 				format++;
1540Sstevel@tonic-gate 			}
1550Sstevel@tonic-gate 		} else if (isdigit(fmt)) {
1560Sstevel@tonic-gate 			multiplier = (multiplier * 10) + (fmt - '0');
1570Sstevel@tonic-gate 			format++;
158880Sfrits 			counter--;
1590Sstevel@tonic-gate 		} else {
1600Sstevel@tonic-gate 			multiplier = 0;
1610Sstevel@tonic-gate 			break;
1620Sstevel@tonic-gate 		}
1630Sstevel@tonic-gate 	}
1640Sstevel@tonic-gate 
1650Sstevel@tonic-gate 	return ((intptr_t)structure - (intptr_t)structstart);
1660Sstevel@tonic-gate }
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate 
1690Sstevel@tonic-gate size_t
1700Sstevel@tonic-gate usb_parse_CV_descr(char *format,
1710Sstevel@tonic-gate 	uchar_t *data,
1720Sstevel@tonic-gate 	size_t	datalen,
1730Sstevel@tonic-gate 	void	*structure,
1740Sstevel@tonic-gate 	size_t	structlen)
1750Sstevel@tonic-gate {
1760Sstevel@tonic-gate 	return (usb_parse_data(format, data, datalen, structure,
177*7425SGongtian.Zhao@Sun.COM 	    structlen));
1780Sstevel@tonic-gate }
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate /*
1820Sstevel@tonic-gate  *	Helper function: returns pointer to n-th descriptor of
1830Sstevel@tonic-gate  *	type descr_type, unless the end of the buffer or a descriptor
1840Sstevel@tonic-gate  *	of type	stop_descr_type1 or stop_descr_type2 is encountered first.
1850Sstevel@tonic-gate  */
1860Sstevel@tonic-gate static uchar_t *
1870Sstevel@tonic-gate usb_nth_descr(uchar_t	*buf,
1880Sstevel@tonic-gate 	size_t		buflen,
1890Sstevel@tonic-gate 	int		descr_type,
1900Sstevel@tonic-gate 	uint_t		n,
1910Sstevel@tonic-gate 	int		stop_descr_type1,
1920Sstevel@tonic-gate 	int		stop_descr_type2)
1930Sstevel@tonic-gate {
1940Sstevel@tonic-gate 	uchar_t	*bufstart = buf;
1950Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
1960Sstevel@tonic-gate 
1970Sstevel@tonic-gate 	if (buf == NULL) {
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate 		return (NULL);
2000Sstevel@tonic-gate 	}
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate 	while (buf + 2 <= bufend) {
2030Sstevel@tonic-gate 		if ((buf != bufstart) && ((buf[1] == stop_descr_type1) ||
2040Sstevel@tonic-gate 		    (buf[1] == stop_descr_type2))) {
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate 			return (NULL);
2070Sstevel@tonic-gate 		}
2080Sstevel@tonic-gate 
2090Sstevel@tonic-gate 		if ((descr_type == USB_DESCR_TYPE_ANY) ||
2100Sstevel@tonic-gate 		    (buf[1] == descr_type)) {
2110Sstevel@tonic-gate 			if (n-- == 0) {
2120Sstevel@tonic-gate 
2130Sstevel@tonic-gate 				return (buf);
2140Sstevel@tonic-gate 			}
2150Sstevel@tonic-gate 		}
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate 		/*
2180Sstevel@tonic-gate 		 * Check for a bad buffer.
2190Sstevel@tonic-gate 		 * If buf[0] is 0, then this will be an infite loop
2200Sstevel@tonic-gate 		 */
2210Sstevel@tonic-gate 		INCREMENT_BUF(buf);
2220Sstevel@tonic-gate 	}
2230Sstevel@tonic-gate 
2240Sstevel@tonic-gate 	return (NULL);
2250Sstevel@tonic-gate }
2260Sstevel@tonic-gate 
2270Sstevel@tonic-gate 
2280Sstevel@tonic-gate size_t
2290Sstevel@tonic-gate usb_parse_dev_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(DEVICE) */
2300Sstevel@tonic-gate 	size_t			buflen,
2310Sstevel@tonic-gate 	usb_dev_descr_t		*ret_descr,
2320Sstevel@tonic-gate 	size_t			ret_buf_len)
2330Sstevel@tonic-gate {
2340Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL) ||
2350Sstevel@tonic-gate 	    (buflen < 2) || (buf[1] != USB_DESCR_TYPE_DEV)) {
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
2380Sstevel@tonic-gate 	}
2390Sstevel@tonic-gate 
2400Sstevel@tonic-gate 	return (usb_parse_data("ccsccccssscccc",
241*7425SGongtian.Zhao@Sun.COM 	    buf, buflen, ret_descr, ret_buf_len));
2420Sstevel@tonic-gate }
2430Sstevel@tonic-gate 
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate size_t
2460Sstevel@tonic-gate usb_parse_cfg_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
2470Sstevel@tonic-gate 	size_t			buflen,
2480Sstevel@tonic-gate 	usb_cfg_descr_t		*ret_descr,
2490Sstevel@tonic-gate 	size_t			ret_buf_len)
2500Sstevel@tonic-gate {
2510Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL) ||
2520Sstevel@tonic-gate 	    (buflen < 2) || (buf[1] != USB_DESCR_TYPE_CFG)) {
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
2550Sstevel@tonic-gate 	}
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 	return (usb_parse_data("ccsccccc",
258*7425SGongtian.Zhao@Sun.COM 	    buf, buflen, ret_descr, ret_buf_len));
2590Sstevel@tonic-gate }
2600Sstevel@tonic-gate 
2610Sstevel@tonic-gate 
2620Sstevel@tonic-gate size_t
2630Sstevel@tonic-gate usba_parse_cfg_pwr_descr(
2640Sstevel@tonic-gate 	uchar_t			*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
2650Sstevel@tonic-gate 	size_t			buflen,
2660Sstevel@tonic-gate 	usba_cfg_pwr_descr_t	*ret_descr,
2670Sstevel@tonic-gate 	size_t			ret_buf_len)
2680Sstevel@tonic-gate {
2690Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
2700Sstevel@tonic-gate 
2710Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL)) {
2720Sstevel@tonic-gate 
2730Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
2740Sstevel@tonic-gate 	}
2750Sstevel@tonic-gate 	while (buf + 2 <= bufend) {
2760Sstevel@tonic-gate 
2770Sstevel@tonic-gate 		if (buf[1] == USBA_DESCR_TYPE_CFG_PWR_1_1) {
2780Sstevel@tonic-gate 			return (usb_parse_data("ccsccccccccsss",
279*7425SGongtian.Zhao@Sun.COM 			    buf, buflen, ret_descr, ret_buf_len));
2800Sstevel@tonic-gate 		}
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate 		/*
2830Sstevel@tonic-gate 		 * Check for a bad buffer.
2840Sstevel@tonic-gate 		 * If buf[0] is 0, then this will be an infinite loop
2850Sstevel@tonic-gate 		 */
2860Sstevel@tonic-gate 		INCREMENT_BUF(buf);
2870Sstevel@tonic-gate 	}
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 	/* return the default configuration power descriptor */
2900Sstevel@tonic-gate 	bcopy(&default_cfg_power, ret_descr, USBA_CFG_PWR_DESCR_SIZE);
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate 	return (ret_descr->bLength);
2930Sstevel@tonic-gate 
2940Sstevel@tonic-gate }
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate size_t
2983341Sgc161489 usb_parse_ia_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
2993341Sgc161489 	size_t			buflen,
3003341Sgc161489 	size_t			first_if,
3013341Sgc161489 	usb_ia_descr_t		*ret_descr,
3023341Sgc161489 	size_t			ret_buf_len)
3033341Sgc161489 {
3043341Sgc161489 	uchar_t *bufend = buf + buflen;
3053341Sgc161489 
3063341Sgc161489 	if ((buf == NULL) || (ret_descr == NULL)) {
3073341Sgc161489 
3083341Sgc161489 		return (USB_PARSE_ERROR);
3093341Sgc161489 	}
3103341Sgc161489 
3113341Sgc161489 	while (buf + USB_IA_DESCR_SIZE <= bufend) {
3123341Sgc161489 		if ((buf[1] == USB_DESCR_TYPE_IA) &&
3133341Sgc161489 		    (buf[2] == first_if)) {
3143341Sgc161489 
3153341Sgc161489 			return (usb_parse_data("cccccccc",
3163341Sgc161489 			    buf, bufend - buf, ret_descr, ret_buf_len));
3173341Sgc161489 		}
3183341Sgc161489 
3193341Sgc161489 		/*
3203341Sgc161489 		 * Check for a bad buffer.
3213341Sgc161489 		 * If buf[0] is 0, then this will be an infinite loop
3223341Sgc161489 		 */
3233341Sgc161489 		INCREMENT_BUF(buf);
3243341Sgc161489 	}
3253341Sgc161489 
3263341Sgc161489 	return (USB_PARSE_ERROR);
3273341Sgc161489 }
3283341Sgc161489 
3293341Sgc161489 
3303341Sgc161489 size_t
3310Sstevel@tonic-gate usb_parse_if_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
3320Sstevel@tonic-gate 	size_t			buflen,
3330Sstevel@tonic-gate 	uint_t			if_number,
3340Sstevel@tonic-gate 	uint_t			alt_if_setting,
3350Sstevel@tonic-gate 	usb_if_descr_t		*ret_descr,
3360Sstevel@tonic-gate 	size_t			ret_buf_len)
3370Sstevel@tonic-gate {
3380Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL)) {
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
3430Sstevel@tonic-gate 	}
3440Sstevel@tonic-gate 
3450Sstevel@tonic-gate 	while (buf + 4 <= bufend) {
3460Sstevel@tonic-gate 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
3470Sstevel@tonic-gate 		    (buf[2] == if_number) &&
3480Sstevel@tonic-gate 		    (buf[3] == alt_if_setting)) {
3490Sstevel@tonic-gate 
3500Sstevel@tonic-gate 			return (usb_parse_data("ccccccccc",
3510Sstevel@tonic-gate 			    buf, bufend - buf, ret_descr, ret_buf_len));
3520Sstevel@tonic-gate 		}
3530Sstevel@tonic-gate 
3540Sstevel@tonic-gate 		/*
3550Sstevel@tonic-gate 		 * Check for a bad buffer.
3560Sstevel@tonic-gate 		 * If buf[0] is 0, then this will be an infinite loop
3570Sstevel@tonic-gate 		 */
3580Sstevel@tonic-gate 		INCREMENT_BUF(buf);
3590Sstevel@tonic-gate 	}
3600Sstevel@tonic-gate 
3610Sstevel@tonic-gate 	return (USB_PARSE_ERROR);
3620Sstevel@tonic-gate }
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate size_t
3650Sstevel@tonic-gate usba_parse_if_pwr_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
3660Sstevel@tonic-gate 	size_t			buflen,
3670Sstevel@tonic-gate 	uint_t			if_number,
3680Sstevel@tonic-gate 	uint_t			alt_if_setting,
3690Sstevel@tonic-gate 	usba_if_pwr_descr_t	*ret_descr,
3700Sstevel@tonic-gate 	size_t			ret_buf_len)
3710Sstevel@tonic-gate {
3720Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL)) {
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
3770Sstevel@tonic-gate 	}
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate 	while (buf + 4 <= bufend) {
3800Sstevel@tonic-gate 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
3810Sstevel@tonic-gate 		    (buf[2] == if_number) &&
3820Sstevel@tonic-gate 		    (buf[3] == alt_if_setting)) {
3830Sstevel@tonic-gate 
3840Sstevel@tonic-gate 			buf += buf[0];
3850Sstevel@tonic-gate 
3860Sstevel@tonic-gate 			if (buf + 2 <= bufend) {
3870Sstevel@tonic-gate 				if (buf[1] == USBA_DESCR_TYPE_IF_PWR_1_1) {
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate 					return (
3900Sstevel@tonic-gate 					    usb_parse_data("cccccccccsss",
391*7425SGongtian.Zhao@Sun.COM 					    buf, bufend - buf, ret_descr,
392*7425SGongtian.Zhao@Sun.COM 					    ret_buf_len));
3930Sstevel@tonic-gate 				} else {
3940Sstevel@tonic-gate 					break;
3950Sstevel@tonic-gate 				}
3960Sstevel@tonic-gate 			} else {
3970Sstevel@tonic-gate 				break;
3980Sstevel@tonic-gate 			}
3990Sstevel@tonic-gate 		}
4000Sstevel@tonic-gate 
4010Sstevel@tonic-gate 		/*
4020Sstevel@tonic-gate 		 * Check for a bad buffer.
4030Sstevel@tonic-gate 		 * If buf[0] is 0, then this will be an infinite loop
4040Sstevel@tonic-gate 		 */
4050Sstevel@tonic-gate 		INCREMENT_BUF(buf);
4060Sstevel@tonic-gate 	}
4070Sstevel@tonic-gate 
4080Sstevel@tonic-gate 	/* return the default interface power descriptor */
4090Sstevel@tonic-gate 	bcopy(&default_if_power, ret_descr, USBA_IF_PWR_DESCR_SIZE);
4100Sstevel@tonic-gate 
4110Sstevel@tonic-gate 	return (ret_descr->bLength);
4120Sstevel@tonic-gate }
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate /*
4160Sstevel@tonic-gate  * the endpoint index is relative to the interface. index 0 is
4170Sstevel@tonic-gate  * the first endpoint
4180Sstevel@tonic-gate  */
4190Sstevel@tonic-gate size_t
4200Sstevel@tonic-gate usb_parse_ep_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
4210Sstevel@tonic-gate 	size_t			buflen,
4220Sstevel@tonic-gate 	uint_t			if_number,
4230Sstevel@tonic-gate 	uint_t			alt_if_setting,
4240Sstevel@tonic-gate 	uint_t			ep_index,
4250Sstevel@tonic-gate 	usb_ep_descr_t		*ret_descr,
4260Sstevel@tonic-gate 	size_t			ret_buf_len)
4270Sstevel@tonic-gate {
4280Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
4290Sstevel@tonic-gate 
4300Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL)) {
4310Sstevel@tonic-gate 
4320Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
4330Sstevel@tonic-gate 	}
4340Sstevel@tonic-gate 
4350Sstevel@tonic-gate 	while ((buf + 4) <= bufend) {
4360Sstevel@tonic-gate 		if (buf[1] == USB_DESCR_TYPE_IF &&
437*7425SGongtian.Zhao@Sun.COM 		    buf[2] == if_number &&
438*7425SGongtian.Zhao@Sun.COM 		    buf[3] == alt_if_setting) {
4390Sstevel@tonic-gate 			if ((buf = usb_nth_descr(buf, bufend - buf,
4400Sstevel@tonic-gate 			    USB_DESCR_TYPE_EP, ep_index,
4410Sstevel@tonic-gate 			    USB_DESCR_TYPE_IF, -1)) == NULL) {
4420Sstevel@tonic-gate 
4430Sstevel@tonic-gate 				break;
4440Sstevel@tonic-gate 			}
4450Sstevel@tonic-gate 
4460Sstevel@tonic-gate 			return (usb_parse_data("ccccsc",
447*7425SGongtian.Zhao@Sun.COM 			    buf, bufend - buf,
448*7425SGongtian.Zhao@Sun.COM 			    ret_descr, ret_buf_len));
4490Sstevel@tonic-gate 		}
4500Sstevel@tonic-gate 
4510Sstevel@tonic-gate 		/*
4520Sstevel@tonic-gate 		 * Check for a bad buffer.
4530Sstevel@tonic-gate 		 * If buf[0] is 0, then this will be an infinite loop
4540Sstevel@tonic-gate 		 */
4550Sstevel@tonic-gate 		INCREMENT_BUF(buf);
4560Sstevel@tonic-gate 	}
4570Sstevel@tonic-gate 
4580Sstevel@tonic-gate 	return (USB_PARSE_ERROR);
4590Sstevel@tonic-gate }
4600Sstevel@tonic-gate 
4610Sstevel@tonic-gate 
4620Sstevel@tonic-gate /*
4630Sstevel@tonic-gate  * Returns (at ret_descr) a null-terminated string.  Null termination is
4640Sstevel@tonic-gate  * guaranteed, even if the string is longer than the buffer.  Thus, a
4650Sstevel@tonic-gate  * maximum of (ret_buf_len - 1) characters are returned.
4660Sstevel@tonic-gate  * Stops silently on first character not in UNICODE format.
4670Sstevel@tonic-gate  */
4680Sstevel@tonic-gate /*ARGSUSED*/
4690Sstevel@tonic-gate size_t
4700Sstevel@tonic-gate usba_ascii_string_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(STRING) */
4710Sstevel@tonic-gate 	size_t			buflen,
4720Sstevel@tonic-gate 	char			*ret_descr,
4730Sstevel@tonic-gate 	size_t			ret_buf_len)
4740Sstevel@tonic-gate {
4750Sstevel@tonic-gate 	int	i = 1;
4760Sstevel@tonic-gate 	char	*retstart = ret_descr;
4770Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL) ||
4800Sstevel@tonic-gate 	    (ret_buf_len == 0) || (buflen < 2) ||
4810Sstevel@tonic-gate 	    (buf[0] < 2) || (buf[1] != USB_DESCR_TYPE_STRING)) {
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
4840Sstevel@tonic-gate 	}
4850Sstevel@tonic-gate 
4860Sstevel@tonic-gate 	for (buf = buf + 2; buf+1 < bufend && ret_buf_len > 1 &&
4870Sstevel@tonic-gate 	    buf[0] != 0 && buf[1] == 0 && (i < ret_buf_len); buf += 2, i++) {
4880Sstevel@tonic-gate 		*ret_descr++ = buf[0];
4890Sstevel@tonic-gate 	}
4900Sstevel@tonic-gate 
4910Sstevel@tonic-gate 	*ret_descr++ = 0;
4920Sstevel@tonic-gate 
4930Sstevel@tonic-gate 	return (ret_descr - retstart);
4940Sstevel@tonic-gate }
4950Sstevel@tonic-gate 
4960Sstevel@tonic-gate 
4970Sstevel@tonic-gate size_t
4980Sstevel@tonic-gate usb_parse_CV_cfg_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
4990Sstevel@tonic-gate 	size_t			buflen,
5000Sstevel@tonic-gate 	char			*fmt,
5010Sstevel@tonic-gate 	uint_t			descr_type,
5020Sstevel@tonic-gate 	uint_t			descr_index,
5030Sstevel@tonic-gate 	void			*ret_descr,
5040Sstevel@tonic-gate 	size_t			ret_buf_len)
5050Sstevel@tonic-gate {
5060Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
5070Sstevel@tonic-gate 
5080Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL) || (fmt == NULL) ||
5090Sstevel@tonic-gate 	    (buflen < 2) || ((buf = usb_nth_descr(buf, buflen, descr_type,
510*7425SGongtian.Zhao@Sun.COM 	    descr_index, -1, -1)) == NULL)) {
5110Sstevel@tonic-gate 
5120Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
5130Sstevel@tonic-gate 	}
5140Sstevel@tonic-gate 
5150Sstevel@tonic-gate 	return (usb_parse_data(fmt, buf, bufend - buf, ret_descr,
516*7425SGongtian.Zhao@Sun.COM 	    ret_buf_len));
5170Sstevel@tonic-gate }
5180Sstevel@tonic-gate 
5190Sstevel@tonic-gate 
5200Sstevel@tonic-gate size_t
5210Sstevel@tonic-gate usb_parse_CV_if_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
5220Sstevel@tonic-gate 	size_t			buflen,
5230Sstevel@tonic-gate 	char			*fmt,
5240Sstevel@tonic-gate 	uint_t			if_number,
5250Sstevel@tonic-gate 	uint_t			alt_if_setting,
5260Sstevel@tonic-gate 	uint_t			descr_type,
5270Sstevel@tonic-gate 	uint_t			descr_index,
5280Sstevel@tonic-gate 	void			*ret_descr,
5290Sstevel@tonic-gate 	size_t			ret_buf_len)
5300Sstevel@tonic-gate {
5310Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
5320Sstevel@tonic-gate 
5330Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL) || (fmt == NULL)) {
5340Sstevel@tonic-gate 
5350Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
5360Sstevel@tonic-gate 	}
5370Sstevel@tonic-gate 
5380Sstevel@tonic-gate 	while (buf + 4 <= bufend) {
5390Sstevel@tonic-gate 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
5400Sstevel@tonic-gate 		    (buf[2] == if_number) &&
5410Sstevel@tonic-gate 		    (buf[3] == alt_if_setting)) {
5420Sstevel@tonic-gate 			if ((buf = usb_nth_descr(buf, bufend - buf, descr_type,
5430Sstevel@tonic-gate 			    descr_index, USB_DESCR_TYPE_IF, -1)) ==
5440Sstevel@tonic-gate 			    NULL) {
5450Sstevel@tonic-gate 				break;
5460Sstevel@tonic-gate 			}
5470Sstevel@tonic-gate 
5480Sstevel@tonic-gate 			return (usb_parse_data(fmt,
549*7425SGongtian.Zhao@Sun.COM 			    buf, bufend - buf, ret_descr, ret_buf_len));
5500Sstevel@tonic-gate 		}
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate 		/*
5530Sstevel@tonic-gate 		 * Check for a bad buffer.
5540Sstevel@tonic-gate 		 * If buf[0] is 0, then this will be an infinite loop
5550Sstevel@tonic-gate 		 */
5560Sstevel@tonic-gate 		INCREMENT_BUF(buf);
5570Sstevel@tonic-gate 	}
5580Sstevel@tonic-gate 
5590Sstevel@tonic-gate 	return (USB_PARSE_ERROR);
5600Sstevel@tonic-gate }
5610Sstevel@tonic-gate 
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate size_t
5640Sstevel@tonic-gate usb_parse_CV_ep_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
5650Sstevel@tonic-gate 	size_t			buflen,
5660Sstevel@tonic-gate 	char			*fmt,
5670Sstevel@tonic-gate 	uint_t			if_number,
5680Sstevel@tonic-gate 	uint_t			alt_if_setting,
5690Sstevel@tonic-gate 	uint_t			ep_index,
5700Sstevel@tonic-gate 	uint_t			descr_type,
5710Sstevel@tonic-gate 	uint_t			descr_index,
5720Sstevel@tonic-gate 	void			*ret_descr,
5730Sstevel@tonic-gate 	size_t			ret_buf_len)
5740Sstevel@tonic-gate {
5750Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
5760Sstevel@tonic-gate 
5770Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL) || (fmt == NULL)) {
5780Sstevel@tonic-gate 
5790Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
5800Sstevel@tonic-gate 	}
5810Sstevel@tonic-gate 
5820Sstevel@tonic-gate 	while (buf + 4 <= bufend) {
5830Sstevel@tonic-gate 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
5840Sstevel@tonic-gate 		    (buf[2] == if_number) &&
5850Sstevel@tonic-gate 		    (buf[3] == alt_if_setting)) {
5860Sstevel@tonic-gate 			if ((buf = usb_nth_descr(buf, bufend - buf,
5870Sstevel@tonic-gate 			    USB_DESCR_TYPE_EP, ep_index,
5880Sstevel@tonic-gate 			    USB_DESCR_TYPE_IF, -1)) == NULL) {
5890Sstevel@tonic-gate 
5900Sstevel@tonic-gate 				break;
5910Sstevel@tonic-gate 			}
5920Sstevel@tonic-gate 
5930Sstevel@tonic-gate 			if ((buf = usb_nth_descr(buf, bufend - buf,
5940Sstevel@tonic-gate 			    descr_type, descr_index,
5950Sstevel@tonic-gate 			    USB_DESCR_TYPE_EP,
5960Sstevel@tonic-gate 			    USB_DESCR_TYPE_IF)) == NULL) {
5970Sstevel@tonic-gate 
5980Sstevel@tonic-gate 				break;
5990Sstevel@tonic-gate 			}
6000Sstevel@tonic-gate 
6010Sstevel@tonic-gate 			return (usb_parse_data(fmt, buf, bufend - buf,
602*7425SGongtian.Zhao@Sun.COM 			    ret_descr, ret_buf_len));
6030Sstevel@tonic-gate 		}
6040Sstevel@tonic-gate 
6050Sstevel@tonic-gate 		/*
6060Sstevel@tonic-gate 		 * Check for a bad buffer.
6070Sstevel@tonic-gate 		 * If buf[0] is 0, then this will be an infite loop
6080Sstevel@tonic-gate 		 */
6090Sstevel@tonic-gate 		INCREMENT_BUF(buf);
6100Sstevel@tonic-gate 	}
6110Sstevel@tonic-gate 
6120Sstevel@tonic-gate 	return (USB_PARSE_ERROR);
6130Sstevel@tonic-gate }
614