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