xref: /onnv-gate/usr/src/uts/common/io/usb/usba/parser.c (revision 3341:52e5b1750941)
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
5*3341Sgc161489  * Common Development and Distribution License (the "License").
6*3341Sgc161489  * 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*3341Sgc161489  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate  * Descriptor parsing functions
300Sstevel@tonic-gate  */
310Sstevel@tonic-gate #define	USBA_FRAMEWORK
320Sstevel@tonic-gate #include <sys/usb/usba/usba_impl.h>
330Sstevel@tonic-gate 
340Sstevel@tonic-gate #define	INCREMENT_BUF(buf) \
350Sstevel@tonic-gate 		if ((buf)[0] == 0) { \
360Sstevel@tonic-gate 			break; \
370Sstevel@tonic-gate 		} else { \
380Sstevel@tonic-gate 			(buf) += (buf)[0]; \
390Sstevel@tonic-gate 		}
400Sstevel@tonic-gate #define	isdigit(ch) ((ch >= '0') && (ch <= '9'))
410Sstevel@tonic-gate 
420Sstevel@tonic-gate extern usba_cfg_pwr_descr_t default_cfg_power;
430Sstevel@tonic-gate extern usba_if_pwr_descr_t default_if_power;
440Sstevel@tonic-gate 
450Sstevel@tonic-gate size_t
460Sstevel@tonic-gate usb_parse_data(char	*format,
470Sstevel@tonic-gate 	uchar_t 	*data,
480Sstevel@tonic-gate 	size_t		datalen,
490Sstevel@tonic-gate 	void		*structure,
500Sstevel@tonic-gate 	size_t		structlen)
510Sstevel@tonic-gate {
520Sstevel@tonic-gate 	int	fmt;
530Sstevel@tonic-gate 	int	counter = 1;
540Sstevel@tonic-gate 	int	multiplier = 0;
550Sstevel@tonic-gate 	uchar_t	*dataend = data + datalen;
560Sstevel@tonic-gate 	char	*structstart = (char *)structure;
570Sstevel@tonic-gate 	void	*structend = (void *)((intptr_t)structstart + structlen);
580Sstevel@tonic-gate 
590Sstevel@tonic-gate 	if ((format == NULL) || (data == NULL) || (structure == NULL)) {
600Sstevel@tonic-gate 
610Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
620Sstevel@tonic-gate 	}
630Sstevel@tonic-gate 
640Sstevel@tonic-gate 	while ((fmt = *format) != '\0') {
650Sstevel@tonic-gate 
660Sstevel@tonic-gate 		/*
670Sstevel@tonic-gate 		 * Could some one pass a "format" that is greater than
680Sstevel@tonic-gate 		 * the structlen? Conversely, one could pass a ret_buf_len
690Sstevel@tonic-gate 		 * that is less than the "format" length.
700Sstevel@tonic-gate 		 * If so, we need to protect against writing over memory.
710Sstevel@tonic-gate 		 */
720Sstevel@tonic-gate 		if (counter++ > structlen) {
730Sstevel@tonic-gate 			break;
740Sstevel@tonic-gate 		}
750Sstevel@tonic-gate 
760Sstevel@tonic-gate 		if (fmt == 'c') {
770Sstevel@tonic-gate 			uint8_t	*cp = (uint8_t *)structure;
780Sstevel@tonic-gate 
790Sstevel@tonic-gate 			cp = (uint8_t *)(((uintptr_t)cp + _CHAR_ALIGNMENT - 1) &
800Sstevel@tonic-gate 							~(_CHAR_ALIGNMENT - 1));
810Sstevel@tonic-gate 			if (((data + 1) > dataend) ||
820Sstevel@tonic-gate 			    ((cp + 1) > (uint8_t *)structend))
830Sstevel@tonic-gate 				break;
840Sstevel@tonic-gate 
850Sstevel@tonic-gate 			*cp++ = *data++;
860Sstevel@tonic-gate 			structure = (void *)cp;
870Sstevel@tonic-gate 			if (multiplier) {
880Sstevel@tonic-gate 				multiplier--;
890Sstevel@tonic-gate 			}
900Sstevel@tonic-gate 			if (multiplier == 0) {
910Sstevel@tonic-gate 				format++;
920Sstevel@tonic-gate 			}
930Sstevel@tonic-gate 		} else if (fmt == 's') {
940Sstevel@tonic-gate 			uint16_t	*sp = (uint16_t *)structure;
950Sstevel@tonic-gate 
960Sstevel@tonic-gate 			sp = (uint16_t *)
970Sstevel@tonic-gate 				(((uintptr_t)sp + _SHORT_ALIGNMENT - 1) &
980Sstevel@tonic-gate 						~(_SHORT_ALIGNMENT - 1));
990Sstevel@tonic-gate 			if (((data + 2) > dataend) ||
1000Sstevel@tonic-gate 			    ((sp + 1) > (uint16_t *)structend))
1010Sstevel@tonic-gate 				break;
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate 			*sp++ = (data[1] << 8) + data[0];
1040Sstevel@tonic-gate 			data += 2;
1050Sstevel@tonic-gate 			structure = (void *)sp;
1060Sstevel@tonic-gate 			if (multiplier) {
1070Sstevel@tonic-gate 				multiplier--;
1080Sstevel@tonic-gate 			}
1090Sstevel@tonic-gate 			if (multiplier == 0) {
1100Sstevel@tonic-gate 				format++;
1110Sstevel@tonic-gate 			}
1120Sstevel@tonic-gate 		} else if (fmt == 'l') {
1130Sstevel@tonic-gate 			uint32_t	*lp = (uint32_t *)structure;
1140Sstevel@tonic-gate 
1150Sstevel@tonic-gate 			lp = (uint32_t *)
1160Sstevel@tonic-gate 				(((uintptr_t)lp + _INT_ALIGNMENT - 1) &
1170Sstevel@tonic-gate 							~(_INT_ALIGNMENT - 1));
1180Sstevel@tonic-gate 			if (((data + 4) > dataend) ||
1190Sstevel@tonic-gate 			    ((lp + 1) > (uint32_t *)structend))
1200Sstevel@tonic-gate 				break;
1210Sstevel@tonic-gate 
1220Sstevel@tonic-gate 			*lp++ = (((((
1230Sstevel@tonic-gate 				(uint32_t)data[3] << 8) | data[2]) << 8) |
1240Sstevel@tonic-gate 						data[1]) << 8) | data[0];
1250Sstevel@tonic-gate 			data += 4;
1260Sstevel@tonic-gate 			structure = (void *)lp;
1270Sstevel@tonic-gate 			if (multiplier) {
1280Sstevel@tonic-gate 				multiplier--;
1290Sstevel@tonic-gate 			}
1300Sstevel@tonic-gate 			if (multiplier == 0) {
1310Sstevel@tonic-gate 				format++;
1320Sstevel@tonic-gate 			}
1330Sstevel@tonic-gate 		} else if (fmt == 'L') {
1340Sstevel@tonic-gate 			uint64_t	*llp = (uint64_t *)structure;
1350Sstevel@tonic-gate 
1360Sstevel@tonic-gate 			llp = (uint64_t *)
1370Sstevel@tonic-gate 				(((uintptr_t)llp + _LONG_LONG_ALIGNMENT - 1) &
1380Sstevel@tonic-gate 						~(_LONG_LONG_ALIGNMENT - 1));
1390Sstevel@tonic-gate 			if (((data + 8) > dataend) ||
1400Sstevel@tonic-gate 			    ((llp + 1) >= (uint64_t *)structend))
1410Sstevel@tonic-gate 				break;
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate 			*llp++ = (((((((((((((data[7] << 8) |
1440Sstevel@tonic-gate 					data[6]) << 8) | data[5]) << 8) |
1450Sstevel@tonic-gate 					data[4]) << 8) | data[3]) << 8) |
1460Sstevel@tonic-gate 					data[2]) << 8) | data[1]) << 8) |
1470Sstevel@tonic-gate 					data[0];
1480Sstevel@tonic-gate 			data += 8;
1490Sstevel@tonic-gate 			structure = (void *)llp;
1500Sstevel@tonic-gate 			if (multiplier) {
1510Sstevel@tonic-gate 				multiplier--;
1520Sstevel@tonic-gate 			}
1530Sstevel@tonic-gate 			if (multiplier == 0) {
1540Sstevel@tonic-gate 				format++;
1550Sstevel@tonic-gate 			}
1560Sstevel@tonic-gate 		} else if (isdigit(fmt)) {
1570Sstevel@tonic-gate 			multiplier = (multiplier * 10) + (fmt - '0');
1580Sstevel@tonic-gate 			format++;
159880Sfrits 			counter--;
1600Sstevel@tonic-gate 		} else {
1610Sstevel@tonic-gate 			multiplier = 0;
1620Sstevel@tonic-gate 			break;
1630Sstevel@tonic-gate 		}
1640Sstevel@tonic-gate 	}
1650Sstevel@tonic-gate 
1660Sstevel@tonic-gate 	return ((intptr_t)structure - (intptr_t)structstart);
1670Sstevel@tonic-gate }
1680Sstevel@tonic-gate 
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate size_t
1710Sstevel@tonic-gate usb_parse_CV_descr(char *format,
1720Sstevel@tonic-gate 	uchar_t *data,
1730Sstevel@tonic-gate 	size_t	datalen,
1740Sstevel@tonic-gate 	void	*structure,
1750Sstevel@tonic-gate 	size_t	structlen)
1760Sstevel@tonic-gate {
1770Sstevel@tonic-gate 	return (usb_parse_data(format, data, datalen, structure,
1780Sstevel@tonic-gate 		structlen));
1790Sstevel@tonic-gate }
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate /*
1830Sstevel@tonic-gate  *	Helper function: returns pointer to n-th descriptor of
1840Sstevel@tonic-gate  *	type descr_type, unless the end of the buffer or a descriptor
1850Sstevel@tonic-gate  *	of type	stop_descr_type1 or stop_descr_type2 is encountered first.
1860Sstevel@tonic-gate  */
1870Sstevel@tonic-gate static uchar_t *
1880Sstevel@tonic-gate usb_nth_descr(uchar_t	*buf,
1890Sstevel@tonic-gate 	size_t		buflen,
1900Sstevel@tonic-gate 	int		descr_type,
1910Sstevel@tonic-gate 	uint_t		n,
1920Sstevel@tonic-gate 	int		stop_descr_type1,
1930Sstevel@tonic-gate 	int		stop_descr_type2)
1940Sstevel@tonic-gate {
1950Sstevel@tonic-gate 	uchar_t	*bufstart = buf;
1960Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
1970Sstevel@tonic-gate 
1980Sstevel@tonic-gate 	if (buf == NULL) {
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate 		return (NULL);
2010Sstevel@tonic-gate 	}
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate 	while (buf + 2 <= bufend) {
2040Sstevel@tonic-gate 		if ((buf != bufstart) && ((buf[1] == stop_descr_type1) ||
2050Sstevel@tonic-gate 		    (buf[1] == stop_descr_type2))) {
2060Sstevel@tonic-gate 
2070Sstevel@tonic-gate 			return (NULL);
2080Sstevel@tonic-gate 		}
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate 		if ((descr_type == USB_DESCR_TYPE_ANY) ||
2110Sstevel@tonic-gate 		    (buf[1] == descr_type)) {
2120Sstevel@tonic-gate 			if (n-- == 0) {
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate 				return (buf);
2150Sstevel@tonic-gate 			}
2160Sstevel@tonic-gate 		}
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate 		/*
2190Sstevel@tonic-gate 		 * Check for a bad buffer.
2200Sstevel@tonic-gate 		 * If buf[0] is 0, then this will be an infite loop
2210Sstevel@tonic-gate 		 */
2220Sstevel@tonic-gate 		INCREMENT_BUF(buf);
2230Sstevel@tonic-gate 	}
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate 	return (NULL);
2260Sstevel@tonic-gate }
2270Sstevel@tonic-gate 
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate size_t
2300Sstevel@tonic-gate usb_parse_dev_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(DEVICE) */
2310Sstevel@tonic-gate 	size_t			buflen,
2320Sstevel@tonic-gate 	usb_dev_descr_t		*ret_descr,
2330Sstevel@tonic-gate 	size_t			ret_buf_len)
2340Sstevel@tonic-gate {
2350Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL) ||
2360Sstevel@tonic-gate 	    (buflen < 2) || (buf[1] != USB_DESCR_TYPE_DEV)) {
2370Sstevel@tonic-gate 
2380Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
2390Sstevel@tonic-gate 	}
2400Sstevel@tonic-gate 
2410Sstevel@tonic-gate 	return (usb_parse_data("ccsccccssscccc",
2420Sstevel@tonic-gate 		buf, buflen, ret_descr, ret_buf_len));
2430Sstevel@tonic-gate }
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate 
2460Sstevel@tonic-gate size_t
2470Sstevel@tonic-gate usb_parse_cfg_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
2480Sstevel@tonic-gate 	size_t			buflen,
2490Sstevel@tonic-gate 	usb_cfg_descr_t		*ret_descr,
2500Sstevel@tonic-gate 	size_t			ret_buf_len)
2510Sstevel@tonic-gate {
2520Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL) ||
2530Sstevel@tonic-gate 	    (buflen < 2) || (buf[1] != USB_DESCR_TYPE_CFG)) {
2540Sstevel@tonic-gate 
2550Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
2560Sstevel@tonic-gate 	}
2570Sstevel@tonic-gate 
2580Sstevel@tonic-gate 	return (usb_parse_data("ccsccccc",
2590Sstevel@tonic-gate 		buf, buflen, ret_descr, ret_buf_len));
2600Sstevel@tonic-gate }
2610Sstevel@tonic-gate 
2620Sstevel@tonic-gate 
2630Sstevel@tonic-gate size_t
2640Sstevel@tonic-gate usba_parse_cfg_pwr_descr(
2650Sstevel@tonic-gate 	uchar_t			*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
2660Sstevel@tonic-gate 	size_t			buflen,
2670Sstevel@tonic-gate 	usba_cfg_pwr_descr_t	*ret_descr,
2680Sstevel@tonic-gate 	size_t			ret_buf_len)
2690Sstevel@tonic-gate {
2700Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
2710Sstevel@tonic-gate 
2720Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL)) {
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
2750Sstevel@tonic-gate 	}
2760Sstevel@tonic-gate 	while (buf + 2 <= bufend) {
2770Sstevel@tonic-gate 
2780Sstevel@tonic-gate 		if (buf[1] == USBA_DESCR_TYPE_CFG_PWR_1_1) {
2790Sstevel@tonic-gate 			return (usb_parse_data("ccsccccccccsss",
2800Sstevel@tonic-gate 				buf, buflen, ret_descr, ret_buf_len));
2810Sstevel@tonic-gate 		}
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 		/*
2840Sstevel@tonic-gate 		 * Check for a bad buffer.
2850Sstevel@tonic-gate 		 * If buf[0] is 0, then this will be an infinite loop
2860Sstevel@tonic-gate 		 */
2870Sstevel@tonic-gate 		INCREMENT_BUF(buf);
2880Sstevel@tonic-gate 	}
2890Sstevel@tonic-gate 
2900Sstevel@tonic-gate 	/* return the default configuration power descriptor */
2910Sstevel@tonic-gate 	bcopy(&default_cfg_power, ret_descr, USBA_CFG_PWR_DESCR_SIZE);
2920Sstevel@tonic-gate 
2930Sstevel@tonic-gate 	return (ret_descr->bLength);
2940Sstevel@tonic-gate 
2950Sstevel@tonic-gate }
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate 
2980Sstevel@tonic-gate size_t
299*3341Sgc161489 usb_parse_ia_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
300*3341Sgc161489 	size_t			buflen,
301*3341Sgc161489 	size_t			first_if,
302*3341Sgc161489 	usb_ia_descr_t		*ret_descr,
303*3341Sgc161489 	size_t			ret_buf_len)
304*3341Sgc161489 {
305*3341Sgc161489 	uchar_t *bufend = buf + buflen;
306*3341Sgc161489 
307*3341Sgc161489 	if ((buf == NULL) || (ret_descr == NULL)) {
308*3341Sgc161489 
309*3341Sgc161489 		return (USB_PARSE_ERROR);
310*3341Sgc161489 	}
311*3341Sgc161489 
312*3341Sgc161489 	while (buf + USB_IA_DESCR_SIZE <= bufend) {
313*3341Sgc161489 		if ((buf[1] == USB_DESCR_TYPE_IA) &&
314*3341Sgc161489 		    (buf[2] == first_if)) {
315*3341Sgc161489 
316*3341Sgc161489 			return (usb_parse_data("cccccccc",
317*3341Sgc161489 			    buf, bufend - buf, ret_descr, ret_buf_len));
318*3341Sgc161489 		}
319*3341Sgc161489 
320*3341Sgc161489 		/*
321*3341Sgc161489 		 * Check for a bad buffer.
322*3341Sgc161489 		 * If buf[0] is 0, then this will be an infinite loop
323*3341Sgc161489 		 */
324*3341Sgc161489 		INCREMENT_BUF(buf);
325*3341Sgc161489 	}
326*3341Sgc161489 
327*3341Sgc161489 	return (USB_PARSE_ERROR);
328*3341Sgc161489 }
329*3341Sgc161489 
330*3341Sgc161489 
331*3341Sgc161489 size_t
3320Sstevel@tonic-gate usb_parse_if_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
3330Sstevel@tonic-gate 	size_t			buflen,
3340Sstevel@tonic-gate 	uint_t			if_number,
3350Sstevel@tonic-gate 	uint_t			alt_if_setting,
3360Sstevel@tonic-gate 	usb_if_descr_t		*ret_descr,
3370Sstevel@tonic-gate 	size_t			ret_buf_len)
3380Sstevel@tonic-gate {
3390Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL)) {
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
3440Sstevel@tonic-gate 	}
3450Sstevel@tonic-gate 
3460Sstevel@tonic-gate 	while (buf + 4 <= bufend) {
3470Sstevel@tonic-gate 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
3480Sstevel@tonic-gate 		    (buf[2] == if_number) &&
3490Sstevel@tonic-gate 		    (buf[3] == alt_if_setting)) {
3500Sstevel@tonic-gate 
3510Sstevel@tonic-gate 			return (usb_parse_data("ccccccccc",
3520Sstevel@tonic-gate 			    buf, bufend - buf, ret_descr, ret_buf_len));
3530Sstevel@tonic-gate 		}
3540Sstevel@tonic-gate 
3550Sstevel@tonic-gate 		/*
3560Sstevel@tonic-gate 		 * Check for a bad buffer.
3570Sstevel@tonic-gate 		 * If buf[0] is 0, then this will be an infinite loop
3580Sstevel@tonic-gate 		 */
3590Sstevel@tonic-gate 		INCREMENT_BUF(buf);
3600Sstevel@tonic-gate 	}
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate 	return (USB_PARSE_ERROR);
3630Sstevel@tonic-gate }
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate size_t
3660Sstevel@tonic-gate usba_parse_if_pwr_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
3670Sstevel@tonic-gate 	size_t			buflen,
3680Sstevel@tonic-gate 	uint_t			if_number,
3690Sstevel@tonic-gate 	uint_t			alt_if_setting,
3700Sstevel@tonic-gate 	usba_if_pwr_descr_t	*ret_descr,
3710Sstevel@tonic-gate 	size_t			ret_buf_len)
3720Sstevel@tonic-gate {
3730Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
3740Sstevel@tonic-gate 
3750Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL)) {
3760Sstevel@tonic-gate 
3770Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
3780Sstevel@tonic-gate 	}
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate 	while (buf + 4 <= bufend) {
3810Sstevel@tonic-gate 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
3820Sstevel@tonic-gate 		    (buf[2] == if_number) &&
3830Sstevel@tonic-gate 		    (buf[3] == alt_if_setting)) {
3840Sstevel@tonic-gate 
3850Sstevel@tonic-gate 			buf += buf[0];
3860Sstevel@tonic-gate 
3870Sstevel@tonic-gate 			if (buf + 2 <= bufend) {
3880Sstevel@tonic-gate 				if (buf[1] == USBA_DESCR_TYPE_IF_PWR_1_1) {
3890Sstevel@tonic-gate 
3900Sstevel@tonic-gate 					return (
3910Sstevel@tonic-gate 					    usb_parse_data("cccccccccsss",
3920Sstevel@tonic-gate 						buf, bufend - buf, ret_descr,
3930Sstevel@tonic-gate 						ret_buf_len));
3940Sstevel@tonic-gate 				} else {
3950Sstevel@tonic-gate 					break;
3960Sstevel@tonic-gate 				}
3970Sstevel@tonic-gate 			} else {
3980Sstevel@tonic-gate 				break;
3990Sstevel@tonic-gate 			}
4000Sstevel@tonic-gate 		}
4010Sstevel@tonic-gate 
4020Sstevel@tonic-gate 		/*
4030Sstevel@tonic-gate 		 * Check for a bad buffer.
4040Sstevel@tonic-gate 		 * If buf[0] is 0, then this will be an infinite loop
4050Sstevel@tonic-gate 		 */
4060Sstevel@tonic-gate 		INCREMENT_BUF(buf);
4070Sstevel@tonic-gate 	}
4080Sstevel@tonic-gate 
4090Sstevel@tonic-gate 	/* return the default interface power descriptor */
4100Sstevel@tonic-gate 	bcopy(&default_if_power, ret_descr, USBA_IF_PWR_DESCR_SIZE);
4110Sstevel@tonic-gate 
4120Sstevel@tonic-gate 	return (ret_descr->bLength);
4130Sstevel@tonic-gate }
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate 
4160Sstevel@tonic-gate /*
4170Sstevel@tonic-gate  * the endpoint index is relative to the interface. index 0 is
4180Sstevel@tonic-gate  * the first endpoint
4190Sstevel@tonic-gate  */
4200Sstevel@tonic-gate size_t
4210Sstevel@tonic-gate usb_parse_ep_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
4220Sstevel@tonic-gate 	size_t			buflen,
4230Sstevel@tonic-gate 	uint_t			if_number,
4240Sstevel@tonic-gate 	uint_t			alt_if_setting,
4250Sstevel@tonic-gate 	uint_t			ep_index,
4260Sstevel@tonic-gate 	usb_ep_descr_t		*ret_descr,
4270Sstevel@tonic-gate 	size_t			ret_buf_len)
4280Sstevel@tonic-gate {
4290Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL)) {
4320Sstevel@tonic-gate 
4330Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
4340Sstevel@tonic-gate 	}
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate 	while ((buf + 4) <= bufend) {
4370Sstevel@tonic-gate 		if (buf[1] == USB_DESCR_TYPE_IF &&
4380Sstevel@tonic-gate 			buf[2] == if_number &&
4390Sstevel@tonic-gate 			buf[3] == alt_if_setting) {
4400Sstevel@tonic-gate 			if ((buf = usb_nth_descr(buf, bufend - buf,
4410Sstevel@tonic-gate 			    USB_DESCR_TYPE_EP, ep_index,
4420Sstevel@tonic-gate 			    USB_DESCR_TYPE_IF, -1)) == NULL) {
4430Sstevel@tonic-gate 
4440Sstevel@tonic-gate 				break;
4450Sstevel@tonic-gate 			}
4460Sstevel@tonic-gate 
4470Sstevel@tonic-gate 			return (usb_parse_data("ccccsc",
4480Sstevel@tonic-gate 						buf, bufend - buf,
4490Sstevel@tonic-gate 						ret_descr, ret_buf_len));
4500Sstevel@tonic-gate 		}
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate 		/*
4530Sstevel@tonic-gate 		 * Check for a bad buffer.
4540Sstevel@tonic-gate 		 * If buf[0] is 0, then this will be an infinite loop
4550Sstevel@tonic-gate 		 */
4560Sstevel@tonic-gate 		INCREMENT_BUF(buf);
4570Sstevel@tonic-gate 	}
4580Sstevel@tonic-gate 
4590Sstevel@tonic-gate 	return (USB_PARSE_ERROR);
4600Sstevel@tonic-gate }
4610Sstevel@tonic-gate 
4620Sstevel@tonic-gate 
4630Sstevel@tonic-gate /*
4640Sstevel@tonic-gate  * Returns (at ret_descr) a null-terminated string.  Null termination is
4650Sstevel@tonic-gate  * guaranteed, even if the string is longer than the buffer.  Thus, a
4660Sstevel@tonic-gate  * maximum of (ret_buf_len - 1) characters are returned.
4670Sstevel@tonic-gate  * Stops silently on first character not in UNICODE format.
4680Sstevel@tonic-gate  */
4690Sstevel@tonic-gate /*ARGSUSED*/
4700Sstevel@tonic-gate size_t
4710Sstevel@tonic-gate usba_ascii_string_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(STRING) */
4720Sstevel@tonic-gate 	size_t			buflen,
4730Sstevel@tonic-gate 	char			*ret_descr,
4740Sstevel@tonic-gate 	size_t			ret_buf_len)
4750Sstevel@tonic-gate {
4760Sstevel@tonic-gate 	int	i = 1;
4770Sstevel@tonic-gate 	char	*retstart = ret_descr;
4780Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
4790Sstevel@tonic-gate 
4800Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL) ||
4810Sstevel@tonic-gate 	    (ret_buf_len == 0) || (buflen < 2) ||
4820Sstevel@tonic-gate 	    (buf[0] < 2) || (buf[1] != USB_DESCR_TYPE_STRING)) {
4830Sstevel@tonic-gate 
4840Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
4850Sstevel@tonic-gate 	}
4860Sstevel@tonic-gate 
4870Sstevel@tonic-gate 	for (buf = buf + 2; buf+1 < bufend && ret_buf_len > 1 &&
4880Sstevel@tonic-gate 	    buf[0] != 0 && buf[1] == 0 && (i < ret_buf_len); buf += 2, i++) {
4890Sstevel@tonic-gate 		*ret_descr++ = buf[0];
4900Sstevel@tonic-gate 	}
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate 	*ret_descr++ = 0;
4930Sstevel@tonic-gate 
4940Sstevel@tonic-gate 	return (ret_descr - retstart);
4950Sstevel@tonic-gate }
4960Sstevel@tonic-gate 
4970Sstevel@tonic-gate 
4980Sstevel@tonic-gate size_t
4990Sstevel@tonic-gate usb_parse_CV_cfg_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
5000Sstevel@tonic-gate 	size_t			buflen,
5010Sstevel@tonic-gate 	char			*fmt,
5020Sstevel@tonic-gate 	uint_t			descr_type,
5030Sstevel@tonic-gate 	uint_t			descr_index,
5040Sstevel@tonic-gate 	void			*ret_descr,
5050Sstevel@tonic-gate 	size_t			ret_buf_len)
5060Sstevel@tonic-gate {
5070Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
5080Sstevel@tonic-gate 
5090Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL) || (fmt == NULL) ||
5100Sstevel@tonic-gate 	    (buflen < 2) || ((buf = usb_nth_descr(buf, buflen, descr_type,
5110Sstevel@tonic-gate 				descr_index, -1, -1)) == NULL)) {
5120Sstevel@tonic-gate 
5130Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
5140Sstevel@tonic-gate 	}
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate 	return (usb_parse_data(fmt, buf, bufend - buf, ret_descr,
5170Sstevel@tonic-gate 			ret_buf_len));
5180Sstevel@tonic-gate }
5190Sstevel@tonic-gate 
5200Sstevel@tonic-gate 
5210Sstevel@tonic-gate size_t
5220Sstevel@tonic-gate usb_parse_CV_if_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
5230Sstevel@tonic-gate 	size_t			buflen,
5240Sstevel@tonic-gate 	char			*fmt,
5250Sstevel@tonic-gate 	uint_t			if_number,
5260Sstevel@tonic-gate 	uint_t			alt_if_setting,
5270Sstevel@tonic-gate 	uint_t			descr_type,
5280Sstevel@tonic-gate 	uint_t			descr_index,
5290Sstevel@tonic-gate 	void			*ret_descr,
5300Sstevel@tonic-gate 	size_t			ret_buf_len)
5310Sstevel@tonic-gate {
5320Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
5330Sstevel@tonic-gate 
5340Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL) || (fmt == NULL)) {
5350Sstevel@tonic-gate 
5360Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
5370Sstevel@tonic-gate 	}
5380Sstevel@tonic-gate 
5390Sstevel@tonic-gate 	while (buf + 4 <= bufend) {
5400Sstevel@tonic-gate 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
5410Sstevel@tonic-gate 		    (buf[2] == if_number) &&
5420Sstevel@tonic-gate 		    (buf[3] == alt_if_setting)) {
5430Sstevel@tonic-gate 			if ((buf = usb_nth_descr(buf, bufend - buf, descr_type,
5440Sstevel@tonic-gate 			    descr_index, USB_DESCR_TYPE_IF, -1)) ==
5450Sstevel@tonic-gate 			    NULL) {
5460Sstevel@tonic-gate 				break;
5470Sstevel@tonic-gate 			}
5480Sstevel@tonic-gate 
5490Sstevel@tonic-gate 			return (usb_parse_data(fmt,
5500Sstevel@tonic-gate 				buf, bufend - buf, ret_descr, ret_buf_len));
5510Sstevel@tonic-gate 		}
5520Sstevel@tonic-gate 
5530Sstevel@tonic-gate 		/*
5540Sstevel@tonic-gate 		 * Check for a bad buffer.
5550Sstevel@tonic-gate 		 * If buf[0] is 0, then this will be an infinite loop
5560Sstevel@tonic-gate 		 */
5570Sstevel@tonic-gate 		INCREMENT_BUF(buf);
5580Sstevel@tonic-gate 	}
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate 	return (USB_PARSE_ERROR);
5610Sstevel@tonic-gate }
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate 
5640Sstevel@tonic-gate size_t
5650Sstevel@tonic-gate usb_parse_CV_ep_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
5660Sstevel@tonic-gate 	size_t			buflen,
5670Sstevel@tonic-gate 	char			*fmt,
5680Sstevel@tonic-gate 	uint_t			if_number,
5690Sstevel@tonic-gate 	uint_t			alt_if_setting,
5700Sstevel@tonic-gate 	uint_t			ep_index,
5710Sstevel@tonic-gate 	uint_t			descr_type,
5720Sstevel@tonic-gate 	uint_t			descr_index,
5730Sstevel@tonic-gate 	void			*ret_descr,
5740Sstevel@tonic-gate 	size_t			ret_buf_len)
5750Sstevel@tonic-gate {
5760Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
5770Sstevel@tonic-gate 
5780Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL) || (fmt == NULL)) {
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
5810Sstevel@tonic-gate 	}
5820Sstevel@tonic-gate 
5830Sstevel@tonic-gate 	while (buf + 4 <= bufend) {
5840Sstevel@tonic-gate 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
5850Sstevel@tonic-gate 		    (buf[2] == if_number) &&
5860Sstevel@tonic-gate 		    (buf[3] == alt_if_setting)) {
5870Sstevel@tonic-gate 			if ((buf = usb_nth_descr(buf, bufend - buf,
5880Sstevel@tonic-gate 			    USB_DESCR_TYPE_EP, ep_index,
5890Sstevel@tonic-gate 			    USB_DESCR_TYPE_IF, -1)) == NULL) {
5900Sstevel@tonic-gate 
5910Sstevel@tonic-gate 				break;
5920Sstevel@tonic-gate 			}
5930Sstevel@tonic-gate 
5940Sstevel@tonic-gate 			if ((buf = usb_nth_descr(buf, bufend - buf,
5950Sstevel@tonic-gate 			    descr_type, descr_index,
5960Sstevel@tonic-gate 			    USB_DESCR_TYPE_EP,
5970Sstevel@tonic-gate 			    USB_DESCR_TYPE_IF)) == NULL) {
5980Sstevel@tonic-gate 
5990Sstevel@tonic-gate 				break;
6000Sstevel@tonic-gate 			}
6010Sstevel@tonic-gate 
6020Sstevel@tonic-gate 			return (usb_parse_data(fmt, buf, bufend - buf,
6030Sstevel@tonic-gate 						ret_descr, ret_buf_len));
6040Sstevel@tonic-gate 		}
6050Sstevel@tonic-gate 
6060Sstevel@tonic-gate 		/*
6070Sstevel@tonic-gate 		 * Check for a bad buffer.
6080Sstevel@tonic-gate 		 * If buf[0] is 0, then this will be an infite loop
6090Sstevel@tonic-gate 		 */
6100Sstevel@tonic-gate 		INCREMENT_BUF(buf);
6110Sstevel@tonic-gate 	}
6120Sstevel@tonic-gate 
6130Sstevel@tonic-gate 	return (USB_PARSE_ERROR);
6140Sstevel@tonic-gate }
615