xref: /onnv-gate/usr/src/uts/common/io/usb/usba/parser.c (revision 9430:637732b28916)
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*9430SRaymond.Chen@Sun.COM  * Copyright 2009 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>
327492SZhigang.Lu@Sun.COM #include <sys/strsun.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
usb_parse_data(char * format,uchar_t * data,size_t datalen,void * structure,size_t structlen)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) &
807425SGongtian.Zhao@Sun.COM 			    ~(_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 *)
977425SGongtian.Zhao@Sun.COM 			    (((uintptr_t)sp + _SHORT_ALIGNMENT - 1) &
987425SGongtian.Zhao@Sun.COM 			    ~(_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 *)
1167425SGongtian.Zhao@Sun.COM 			    (((uintptr_t)lp + _INT_ALIGNMENT - 1) &
1177425SGongtian.Zhao@Sun.COM 			    ~(_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++ = (((((
1237425SGongtian.Zhao@Sun.COM 			    (uint32_t)data[3] << 8) | data[2]) << 8) |
1247425SGongtian.Zhao@Sun.COM 			    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 *)
1377425SGongtian.Zhao@Sun.COM 			    (((uintptr_t)llp + _LONG_LONG_ALIGNMENT - 1) &
1387425SGongtian.Zhao@Sun.COM 			    ~(_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) |
1447425SGongtian.Zhao@Sun.COM 			    data[6]) << 8) | data[5]) << 8) |
1457425SGongtian.Zhao@Sun.COM 			    data[4]) << 8) | data[3]) << 8) |
1467425SGongtian.Zhao@Sun.COM 			    data[2]) << 8) | data[1]) << 8) |
1477425SGongtian.Zhao@Sun.COM 			    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
usb_parse_CV_descr(char * format,uchar_t * data,size_t datalen,void * structure,size_t structlen)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,
1787425SGongtian.Zhao@Sun.COM 	    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 *
usb_nth_descr(uchar_t * buf,size_t buflen,int descr_type,uint_t n,int stop_descr_type1,int stop_descr_type2)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
usb_parse_dev_descr(uchar_t * buf,size_t buflen,usb_dev_descr_t * ret_descr,size_t ret_buf_len)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",
2427425SGongtian.Zhao@Sun.COM 	    buf, buflen, ret_descr, ret_buf_len));
2430Sstevel@tonic-gate }
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate 
2460Sstevel@tonic-gate size_t
usb_parse_cfg_descr(uchar_t * buf,size_t buflen,usb_cfg_descr_t * ret_descr,size_t ret_buf_len)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",
2597425SGongtian.Zhao@Sun.COM 	    buf, buflen, ret_descr, ret_buf_len));
2600Sstevel@tonic-gate }
2610Sstevel@tonic-gate 
2620Sstevel@tonic-gate 
2630Sstevel@tonic-gate size_t
usba_parse_cfg_pwr_descr(uchar_t * buf,size_t buflen,usba_cfg_pwr_descr_t * ret_descr,size_t ret_buf_len)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",
2807425SGongtian.Zhao@Sun.COM 			    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
usb_parse_ia_descr(uchar_t * buf,size_t buflen,size_t first_if,usb_ia_descr_t * ret_descr,size_t ret_buf_len)2993341Sgc161489 usb_parse_ia_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
3003341Sgc161489 	size_t			buflen,
3013341Sgc161489 	size_t			first_if,
3023341Sgc161489 	usb_ia_descr_t		*ret_descr,
3033341Sgc161489 	size_t			ret_buf_len)
3043341Sgc161489 {
3053341Sgc161489 	uchar_t *bufend = buf + buflen;
3063341Sgc161489 
3073341Sgc161489 	if ((buf == NULL) || (ret_descr == NULL)) {
3083341Sgc161489 
3093341Sgc161489 		return (USB_PARSE_ERROR);
3103341Sgc161489 	}
3113341Sgc161489 
3123341Sgc161489 	while (buf + USB_IA_DESCR_SIZE <= bufend) {
3133341Sgc161489 		if ((buf[1] == USB_DESCR_TYPE_IA) &&
3143341Sgc161489 		    (buf[2] == first_if)) {
3153341Sgc161489 
3163341Sgc161489 			return (usb_parse_data("cccccccc",
3177492SZhigang.Lu@Sun.COM 			    buf, _PTRDIFF(bufend, buf),
3187492SZhigang.Lu@Sun.COM 			    ret_descr, ret_buf_len));
3193341Sgc161489 		}
3203341Sgc161489 
3213341Sgc161489 		/*
3223341Sgc161489 		 * Check for a bad buffer.
3233341Sgc161489 		 * If buf[0] is 0, then this will be an infinite loop
3243341Sgc161489 		 */
3253341Sgc161489 		INCREMENT_BUF(buf);
3263341Sgc161489 	}
3273341Sgc161489 
3283341Sgc161489 	return (USB_PARSE_ERROR);
3293341Sgc161489 }
3303341Sgc161489 
3313341Sgc161489 
3323341Sgc161489 size_t
usb_parse_if_descr(uchar_t * buf,size_t buflen,uint_t if_number,uint_t alt_if_setting,usb_if_descr_t * ret_descr,size_t ret_buf_len)3330Sstevel@tonic-gate usb_parse_if_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
3340Sstevel@tonic-gate 	size_t			buflen,
3350Sstevel@tonic-gate 	uint_t			if_number,
3360Sstevel@tonic-gate 	uint_t			alt_if_setting,
3370Sstevel@tonic-gate 	usb_if_descr_t		*ret_descr,
3380Sstevel@tonic-gate 	size_t			ret_buf_len)
3390Sstevel@tonic-gate {
3400Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL)) {
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
3450Sstevel@tonic-gate 	}
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate 	while (buf + 4 <= bufend) {
3480Sstevel@tonic-gate 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
3490Sstevel@tonic-gate 		    (buf[2] == if_number) &&
3500Sstevel@tonic-gate 		    (buf[3] == alt_if_setting)) {
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate 			return (usb_parse_data("ccccccccc",
3537492SZhigang.Lu@Sun.COM 			    buf, _PTRDIFF(bufend, buf),
3547492SZhigang.Lu@Sun.COM 			    ret_descr, ret_buf_len));
3550Sstevel@tonic-gate 		}
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate 		/*
3580Sstevel@tonic-gate 		 * Check for a bad buffer.
3590Sstevel@tonic-gate 		 * If buf[0] is 0, then this will be an infinite loop
3600Sstevel@tonic-gate 		 */
3610Sstevel@tonic-gate 		INCREMENT_BUF(buf);
3620Sstevel@tonic-gate 	}
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate 	return (USB_PARSE_ERROR);
3650Sstevel@tonic-gate }
3660Sstevel@tonic-gate 
3670Sstevel@tonic-gate size_t
usba_parse_if_pwr_descr(uchar_t * buf,size_t buflen,uint_t if_number,uint_t alt_if_setting,usba_if_pwr_descr_t * ret_descr,size_t ret_buf_len)3680Sstevel@tonic-gate usba_parse_if_pwr_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
3690Sstevel@tonic-gate 	size_t			buflen,
3700Sstevel@tonic-gate 	uint_t			if_number,
3710Sstevel@tonic-gate 	uint_t			alt_if_setting,
3720Sstevel@tonic-gate 	usba_if_pwr_descr_t	*ret_descr,
3730Sstevel@tonic-gate 	size_t			ret_buf_len)
3740Sstevel@tonic-gate {
3750Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
3760Sstevel@tonic-gate 
3770Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL)) {
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
3800Sstevel@tonic-gate 	}
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate 	while (buf + 4 <= bufend) {
3830Sstevel@tonic-gate 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
3840Sstevel@tonic-gate 		    (buf[2] == if_number) &&
3850Sstevel@tonic-gate 		    (buf[3] == alt_if_setting)) {
3860Sstevel@tonic-gate 
3870Sstevel@tonic-gate 			buf += buf[0];
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate 			if (buf + 2 <= bufend) {
3900Sstevel@tonic-gate 				if (buf[1] == USBA_DESCR_TYPE_IF_PWR_1_1) {
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate 					return (
3937492SZhigang.Lu@Sun.COM 					    usb_parse_data("cccccccccsss", buf,
3947492SZhigang.Lu@Sun.COM 					    _PTRDIFF(bufend, buf), ret_descr,
3957425SGongtian.Zhao@Sun.COM 					    ret_buf_len));
3960Sstevel@tonic-gate 				} else {
3970Sstevel@tonic-gate 					break;
3980Sstevel@tonic-gate 				}
3990Sstevel@tonic-gate 			} else {
4000Sstevel@tonic-gate 				break;
4010Sstevel@tonic-gate 			}
4020Sstevel@tonic-gate 		}
4030Sstevel@tonic-gate 
4040Sstevel@tonic-gate 		/*
4050Sstevel@tonic-gate 		 * Check for a bad buffer.
4060Sstevel@tonic-gate 		 * If buf[0] is 0, then this will be an infinite loop
4070Sstevel@tonic-gate 		 */
4080Sstevel@tonic-gate 		INCREMENT_BUF(buf);
4090Sstevel@tonic-gate 	}
4100Sstevel@tonic-gate 
4110Sstevel@tonic-gate 	/* return the default interface power descriptor */
4120Sstevel@tonic-gate 	bcopy(&default_if_power, ret_descr, USBA_IF_PWR_DESCR_SIZE);
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate 	return (ret_descr->bLength);
4150Sstevel@tonic-gate }
4160Sstevel@tonic-gate 
4170Sstevel@tonic-gate 
4180Sstevel@tonic-gate /*
4190Sstevel@tonic-gate  * the endpoint index is relative to the interface. index 0 is
4200Sstevel@tonic-gate  * the first endpoint
4210Sstevel@tonic-gate  */
4220Sstevel@tonic-gate size_t
usb_parse_ep_descr(uchar_t * buf,size_t buflen,uint_t if_number,uint_t alt_if_setting,uint_t ep_index,usb_ep_descr_t * ret_descr,size_t ret_buf_len)4230Sstevel@tonic-gate usb_parse_ep_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
4240Sstevel@tonic-gate 	size_t			buflen,
4250Sstevel@tonic-gate 	uint_t			if_number,
4260Sstevel@tonic-gate 	uint_t			alt_if_setting,
4270Sstevel@tonic-gate 	uint_t			ep_index,
4280Sstevel@tonic-gate 	usb_ep_descr_t		*ret_descr,
4290Sstevel@tonic-gate 	size_t			ret_buf_len)
4300Sstevel@tonic-gate {
4310Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
4320Sstevel@tonic-gate 
4330Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL)) {
4340Sstevel@tonic-gate 
4350Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
4360Sstevel@tonic-gate 	}
4370Sstevel@tonic-gate 
4380Sstevel@tonic-gate 	while ((buf + 4) <= bufend) {
4390Sstevel@tonic-gate 		if (buf[1] == USB_DESCR_TYPE_IF &&
4407425SGongtian.Zhao@Sun.COM 		    buf[2] == if_number &&
4417425SGongtian.Zhao@Sun.COM 		    buf[3] == alt_if_setting) {
4427492SZhigang.Lu@Sun.COM 			if ((buf = usb_nth_descr(buf,
4437492SZhigang.Lu@Sun.COM 			    _PTRDIFF(bufend, buf),
4440Sstevel@tonic-gate 			    USB_DESCR_TYPE_EP, ep_index,
4450Sstevel@tonic-gate 			    USB_DESCR_TYPE_IF, -1)) == NULL) {
4460Sstevel@tonic-gate 
4470Sstevel@tonic-gate 				break;
4480Sstevel@tonic-gate 			}
4490Sstevel@tonic-gate 
4500Sstevel@tonic-gate 			return (usb_parse_data("ccccsc",
4517492SZhigang.Lu@Sun.COM 			    buf, _PTRDIFF(bufend, buf),
4527425SGongtian.Zhao@Sun.COM 			    ret_descr, ret_buf_len));
4530Sstevel@tonic-gate 		}
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate 		/*
4560Sstevel@tonic-gate 		 * Check for a bad buffer.
4570Sstevel@tonic-gate 		 * If buf[0] is 0, then this will be an infinite loop
4580Sstevel@tonic-gate 		 */
4590Sstevel@tonic-gate 		INCREMENT_BUF(buf);
4600Sstevel@tonic-gate 	}
4610Sstevel@tonic-gate 
4620Sstevel@tonic-gate 	return (USB_PARSE_ERROR);
4630Sstevel@tonic-gate }
4640Sstevel@tonic-gate 
4650Sstevel@tonic-gate 
4660Sstevel@tonic-gate /*
4670Sstevel@tonic-gate  * Returns (at ret_descr) a null-terminated string.  Null termination is
4680Sstevel@tonic-gate  * guaranteed, even if the string is longer than the buffer.  Thus, a
4690Sstevel@tonic-gate  * maximum of (ret_buf_len - 1) characters are returned.
4700Sstevel@tonic-gate  * Stops silently on first character not in UNICODE format.
4710Sstevel@tonic-gate  */
4720Sstevel@tonic-gate /*ARGSUSED*/
4730Sstevel@tonic-gate size_t
usba_ascii_string_descr(uchar_t * buf,size_t buflen,char * ret_descr,size_t ret_buf_len)4740Sstevel@tonic-gate usba_ascii_string_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(STRING) */
4750Sstevel@tonic-gate 	size_t			buflen,
4760Sstevel@tonic-gate 	char			*ret_descr,
4770Sstevel@tonic-gate 	size_t			ret_buf_len)
4780Sstevel@tonic-gate {
4790Sstevel@tonic-gate 	int	i = 1;
4800Sstevel@tonic-gate 	char	*retstart = ret_descr;
4810Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL) ||
4840Sstevel@tonic-gate 	    (ret_buf_len == 0) || (buflen < 2) ||
4850Sstevel@tonic-gate 	    (buf[0] < 2) || (buf[1] != USB_DESCR_TYPE_STRING)) {
4860Sstevel@tonic-gate 
4870Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
4880Sstevel@tonic-gate 	}
4890Sstevel@tonic-gate 
4900Sstevel@tonic-gate 	for (buf = buf + 2; buf+1 < bufend && ret_buf_len > 1 &&
4910Sstevel@tonic-gate 	    buf[0] != 0 && buf[1] == 0 && (i < ret_buf_len); buf += 2, i++) {
4920Sstevel@tonic-gate 		*ret_descr++ = buf[0];
4930Sstevel@tonic-gate 	}
4940Sstevel@tonic-gate 
4950Sstevel@tonic-gate 	*ret_descr++ = 0;
4960Sstevel@tonic-gate 
4977492SZhigang.Lu@Sun.COM 	return (_PTRDIFF(ret_descr, retstart));
4980Sstevel@tonic-gate }
4990Sstevel@tonic-gate 
5000Sstevel@tonic-gate 
5010Sstevel@tonic-gate size_t
usb_parse_CV_cfg_descr(uchar_t * buf,size_t buflen,char * fmt,uint_t descr_type,uint_t descr_index,void * ret_descr,size_t ret_buf_len)5020Sstevel@tonic-gate usb_parse_CV_cfg_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
5030Sstevel@tonic-gate 	size_t			buflen,
5040Sstevel@tonic-gate 	char			*fmt,
5050Sstevel@tonic-gate 	uint_t			descr_type,
5060Sstevel@tonic-gate 	uint_t			descr_index,
5070Sstevel@tonic-gate 	void			*ret_descr,
5080Sstevel@tonic-gate 	size_t			ret_buf_len)
5090Sstevel@tonic-gate {
5100Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
5110Sstevel@tonic-gate 
5120Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL) || (fmt == NULL) ||
5130Sstevel@tonic-gate 	    (buflen < 2) || ((buf = usb_nth_descr(buf, buflen, descr_type,
5147425SGongtian.Zhao@Sun.COM 	    descr_index, -1, -1)) == NULL)) {
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
5170Sstevel@tonic-gate 	}
5180Sstevel@tonic-gate 
5197492SZhigang.Lu@Sun.COM 	return (usb_parse_data(fmt, buf,
5207492SZhigang.Lu@Sun.COM 	    _PTRDIFF(bufend, buf), ret_descr,
5217425SGongtian.Zhao@Sun.COM 	    ret_buf_len));
5220Sstevel@tonic-gate }
5230Sstevel@tonic-gate 
5240Sstevel@tonic-gate 
5250Sstevel@tonic-gate size_t
usb_parse_CV_if_descr(uchar_t * buf,size_t buflen,char * fmt,uint_t if_number,uint_t alt_if_setting,uint_t descr_type,uint_t descr_index,void * ret_descr,size_t ret_buf_len)5260Sstevel@tonic-gate usb_parse_CV_if_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
5270Sstevel@tonic-gate 	size_t			buflen,
5280Sstevel@tonic-gate 	char			*fmt,
5290Sstevel@tonic-gate 	uint_t			if_number,
5300Sstevel@tonic-gate 	uint_t			alt_if_setting,
5310Sstevel@tonic-gate 	uint_t			descr_type,
5320Sstevel@tonic-gate 	uint_t			descr_index,
5330Sstevel@tonic-gate 	void			*ret_descr,
5340Sstevel@tonic-gate 	size_t			ret_buf_len)
5350Sstevel@tonic-gate {
5360Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
5370Sstevel@tonic-gate 
5380Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL) || (fmt == NULL)) {
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
5410Sstevel@tonic-gate 	}
5420Sstevel@tonic-gate 
5430Sstevel@tonic-gate 	while (buf + 4 <= bufend) {
5440Sstevel@tonic-gate 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
5450Sstevel@tonic-gate 		    (buf[2] == if_number) &&
5460Sstevel@tonic-gate 		    (buf[3] == alt_if_setting)) {
5477492SZhigang.Lu@Sun.COM 			if ((buf = usb_nth_descr(buf,
5487492SZhigang.Lu@Sun.COM 			    _PTRDIFF(bufend, buf), descr_type,
5490Sstevel@tonic-gate 			    descr_index, USB_DESCR_TYPE_IF, -1)) ==
5500Sstevel@tonic-gate 			    NULL) {
5510Sstevel@tonic-gate 				break;
5520Sstevel@tonic-gate 			}
5530Sstevel@tonic-gate 
5547492SZhigang.Lu@Sun.COM 			return (usb_parse_data(fmt, buf,
5557492SZhigang.Lu@Sun.COM 			    _PTRDIFF(bufend, buf),
5567492SZhigang.Lu@Sun.COM 			    ret_descr, ret_buf_len));
5570Sstevel@tonic-gate 		}
5580Sstevel@tonic-gate 
5590Sstevel@tonic-gate 		/*
5600Sstevel@tonic-gate 		 * Check for a bad buffer.
5610Sstevel@tonic-gate 		 * If buf[0] is 0, then this will be an infinite loop
5620Sstevel@tonic-gate 		 */
5630Sstevel@tonic-gate 		INCREMENT_BUF(buf);
5640Sstevel@tonic-gate 	}
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate 	return (USB_PARSE_ERROR);
5670Sstevel@tonic-gate }
5680Sstevel@tonic-gate 
5690Sstevel@tonic-gate 
5700Sstevel@tonic-gate size_t
usb_parse_CV_ep_descr(uchar_t * buf,size_t buflen,char * fmt,uint_t if_number,uint_t alt_if_setting,uint_t ep_index,uint_t descr_type,uint_t descr_index,void * ret_descr,size_t ret_buf_len)5710Sstevel@tonic-gate usb_parse_CV_ep_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
5720Sstevel@tonic-gate 	size_t			buflen,
5730Sstevel@tonic-gate 	char			*fmt,
5740Sstevel@tonic-gate 	uint_t			if_number,
5750Sstevel@tonic-gate 	uint_t			alt_if_setting,
5760Sstevel@tonic-gate 	uint_t			ep_index,
5770Sstevel@tonic-gate 	uint_t			descr_type,
5780Sstevel@tonic-gate 	uint_t			descr_index,
5790Sstevel@tonic-gate 	void			*ret_descr,
5800Sstevel@tonic-gate 	size_t			ret_buf_len)
5810Sstevel@tonic-gate {
5820Sstevel@tonic-gate 	uchar_t *bufend = buf + buflen;
5830Sstevel@tonic-gate 
5840Sstevel@tonic-gate 	if ((buf == NULL) || (ret_descr == NULL) || (fmt == NULL)) {
5850Sstevel@tonic-gate 
5860Sstevel@tonic-gate 		return (USB_PARSE_ERROR);
5870Sstevel@tonic-gate 	}
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate 	while (buf + 4 <= bufend) {
5900Sstevel@tonic-gate 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
5910Sstevel@tonic-gate 		    (buf[2] == if_number) &&
5920Sstevel@tonic-gate 		    (buf[3] == alt_if_setting)) {
5937492SZhigang.Lu@Sun.COM 			if ((buf = usb_nth_descr(buf,
5947492SZhigang.Lu@Sun.COM 			    _PTRDIFF(bufend, buf),
5950Sstevel@tonic-gate 			    USB_DESCR_TYPE_EP, ep_index,
5960Sstevel@tonic-gate 			    USB_DESCR_TYPE_IF, -1)) == NULL) {
5970Sstevel@tonic-gate 
5980Sstevel@tonic-gate 				break;
5990Sstevel@tonic-gate 			}
6000Sstevel@tonic-gate 
6017492SZhigang.Lu@Sun.COM 			if ((buf = usb_nth_descr(buf,
6027492SZhigang.Lu@Sun.COM 			    _PTRDIFF(bufend, buf),
6030Sstevel@tonic-gate 			    descr_type, descr_index,
6040Sstevel@tonic-gate 			    USB_DESCR_TYPE_EP,
6050Sstevel@tonic-gate 			    USB_DESCR_TYPE_IF)) == NULL) {
6060Sstevel@tonic-gate 
6070Sstevel@tonic-gate 				break;
6080Sstevel@tonic-gate 			}
6090Sstevel@tonic-gate 
6107492SZhigang.Lu@Sun.COM 			return (usb_parse_data(fmt, buf,
6117492SZhigang.Lu@Sun.COM 			    _PTRDIFF(bufend, buf),
6127425SGongtian.Zhao@Sun.COM 			    ret_descr, ret_buf_len));
6130Sstevel@tonic-gate 		}
6140Sstevel@tonic-gate 
6150Sstevel@tonic-gate 		/*
6160Sstevel@tonic-gate 		 * Check for a bad buffer.
6170Sstevel@tonic-gate 		 * If buf[0] is 0, then this will be an infite loop
6180Sstevel@tonic-gate 		 */
6190Sstevel@tonic-gate 		INCREMENT_BUF(buf);
6200Sstevel@tonic-gate 	}
6210Sstevel@tonic-gate 
6220Sstevel@tonic-gate 	return (USB_PARSE_ERROR);
6230Sstevel@tonic-gate }
624*9430SRaymond.Chen@Sun.COM 
625*9430SRaymond.Chen@Sun.COM size_t
usb_parse_bos_descr(uchar_t * buf,size_t buflen,usb_bos_descr_t * ret_descr,size_t ret_buf_len)626*9430SRaymond.Chen@Sun.COM usb_parse_bos_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(BOS) */
627*9430SRaymond.Chen@Sun.COM 	size_t			buflen,
628*9430SRaymond.Chen@Sun.COM 	usb_bos_descr_t		*ret_descr,
629*9430SRaymond.Chen@Sun.COM 	size_t			ret_buf_len)
630*9430SRaymond.Chen@Sun.COM {
631*9430SRaymond.Chen@Sun.COM 	if ((buf == NULL) || (ret_descr == NULL) ||
632*9430SRaymond.Chen@Sun.COM 	    (buflen < 2) || (buf[1] != USB_DESCR_TYPE_BOS)) {
633*9430SRaymond.Chen@Sun.COM 
634*9430SRaymond.Chen@Sun.COM 		return (USB_PARSE_ERROR);
635*9430SRaymond.Chen@Sun.COM 	}
636*9430SRaymond.Chen@Sun.COM 
637*9430SRaymond.Chen@Sun.COM 	return (usb_parse_data("ccsc",
638*9430SRaymond.Chen@Sun.COM 	    buf, buflen, ret_descr, ret_buf_len));
639*9430SRaymond.Chen@Sun.COM }
640*9430SRaymond.Chen@Sun.COM 
641*9430SRaymond.Chen@Sun.COM size_t
usb_parse_uwb_bos_descr(uchar_t * buf,size_t buflen,usb_uwb_cap_descr_t * ret_descr,size_t ret_buf_len)642*9430SRaymond.Chen@Sun.COM usb_parse_uwb_bos_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(BOS) */
643*9430SRaymond.Chen@Sun.COM 	size_t			buflen,
644*9430SRaymond.Chen@Sun.COM 	usb_uwb_cap_descr_t	*ret_descr,
645*9430SRaymond.Chen@Sun.COM 	size_t			ret_buf_len)
646*9430SRaymond.Chen@Sun.COM {
647*9430SRaymond.Chen@Sun.COM 	uchar_t *bufend = buf + buflen;
648*9430SRaymond.Chen@Sun.COM 
649*9430SRaymond.Chen@Sun.COM 	if ((buf == NULL) || (ret_descr == NULL)) {
650*9430SRaymond.Chen@Sun.COM 
651*9430SRaymond.Chen@Sun.COM 		return (USB_PARSE_ERROR);
652*9430SRaymond.Chen@Sun.COM 	}
653*9430SRaymond.Chen@Sun.COM 
654*9430SRaymond.Chen@Sun.COM 	while (buf + 3 <= bufend) {
655*9430SRaymond.Chen@Sun.COM 		if ((buf[1] == USB_DESCR_TYPE_DEV_CAPABILITY) &&
656*9430SRaymond.Chen@Sun.COM 		    (buf[2] == USB_CAP_TYPE_WUSB)) {
657*9430SRaymond.Chen@Sun.COM 
658*9430SRaymond.Chen@Sun.COM 			return (usb_parse_data("ccccsccsc",
659*9430SRaymond.Chen@Sun.COM 			    buf, _PTRDIFF(bufend, buf), ret_descr,
660*9430SRaymond.Chen@Sun.COM 			    ret_buf_len));
661*9430SRaymond.Chen@Sun.COM 		}
662*9430SRaymond.Chen@Sun.COM 
663*9430SRaymond.Chen@Sun.COM 		INCREMENT_BUF(buf);
664*9430SRaymond.Chen@Sun.COM 	}
665*9430SRaymond.Chen@Sun.COM 
666*9430SRaymond.Chen@Sun.COM 	return (USB_PARSE_ERROR);
667*9430SRaymond.Chen@Sun.COM }
668*9430SRaymond.Chen@Sun.COM 
669*9430SRaymond.Chen@Sun.COM size_t
usb_parse_comp_ep_descr(uchar_t * buf,size_t buflen,uint_t if_number,uint_t alt_if_setting,uint_t ep_index,usb_ep_comp_descr_t * ret_descr,size_t ret_buf_len)670*9430SRaymond.Chen@Sun.COM usb_parse_comp_ep_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
671*9430SRaymond.Chen@Sun.COM 	size_t			buflen,
672*9430SRaymond.Chen@Sun.COM 	uint_t			if_number,
673*9430SRaymond.Chen@Sun.COM 	uint_t			alt_if_setting,
674*9430SRaymond.Chen@Sun.COM 	uint_t			ep_index,
675*9430SRaymond.Chen@Sun.COM 	usb_ep_comp_descr_t	*ret_descr,
676*9430SRaymond.Chen@Sun.COM 	size_t			ret_buf_len)
677*9430SRaymond.Chen@Sun.COM {
678*9430SRaymond.Chen@Sun.COM 	return (usb_parse_CV_ep_descr(buf, buflen, "ccccsscc",
679*9430SRaymond.Chen@Sun.COM 	    if_number, alt_if_setting, ep_index,
680*9430SRaymond.Chen@Sun.COM 	    USB_DESCR_TYPE_WIRELESS_EP_COMP, 0,
681*9430SRaymond.Chen@Sun.COM 	    ret_descr, ret_buf_len));
682*9430SRaymond.Chen@Sun.COM }
683