xref: /freebsd/sys/dev/usb/usb_parse.c (revision 760bc48e7ee4471fe04fa5fee89d00bf7d698ddb)
102ac6454SAndrew Thompson /* $FreeBSD$ */
202ac6454SAndrew Thompson /*-
302ac6454SAndrew Thompson  * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
402ac6454SAndrew Thompson  *
502ac6454SAndrew Thompson  * Redistribution and use in source and binary forms, with or without
602ac6454SAndrew Thompson  * modification, are permitted provided that the following conditions
702ac6454SAndrew Thompson  * are met:
802ac6454SAndrew Thompson  * 1. Redistributions of source code must retain the above copyright
902ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer.
1002ac6454SAndrew Thompson  * 2. Redistributions in binary form must reproduce the above copyright
1102ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer in the
1202ac6454SAndrew Thompson  *    documentation and/or other materials provided with the distribution.
1302ac6454SAndrew Thompson  *
1402ac6454SAndrew Thompson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1502ac6454SAndrew Thompson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1602ac6454SAndrew Thompson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1702ac6454SAndrew Thompson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1802ac6454SAndrew Thompson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1902ac6454SAndrew Thompson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2002ac6454SAndrew Thompson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2102ac6454SAndrew Thompson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2202ac6454SAndrew Thompson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2302ac6454SAndrew Thompson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2402ac6454SAndrew Thompson  * SUCH DAMAGE.
2502ac6454SAndrew Thompson  */
2602ac6454SAndrew Thompson 
2702ac6454SAndrew Thompson #include <dev/usb/usb.h>
2802ac6454SAndrew Thompson #include <dev/usb/usb_mfunc.h>
2902ac6454SAndrew Thompson 
3002ac6454SAndrew Thompson #include <dev/usb/usb_core.h>
3102ac6454SAndrew Thompson #include <dev/usb/usb_parse.h>
3202ac6454SAndrew Thompson 
3302ac6454SAndrew Thompson /*------------------------------------------------------------------------*
3402ac6454SAndrew Thompson  *	usb2_desc_foreach
3502ac6454SAndrew Thompson  *
3602ac6454SAndrew Thompson  * This function is the safe way to iterate across the USB config
3702ac6454SAndrew Thompson  * descriptor. It contains several checks against invalid
3802ac6454SAndrew Thompson  * descriptors. If the "desc" argument passed to this function is
3902ac6454SAndrew Thompson  * "NULL" the first descriptor, if any, will be returned.
4002ac6454SAndrew Thompson  *
4102ac6454SAndrew Thompson  * Return values:
4202ac6454SAndrew Thompson  *   NULL: End of descriptors
4302ac6454SAndrew Thompson  *   Else: Next descriptor after "desc"
4402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
45760bc48eSAndrew Thompson struct usb_descriptor *
46760bc48eSAndrew Thompson usb2_desc_foreach(struct usb_config_descriptor *cd,
47760bc48eSAndrew Thompson     struct usb_descriptor *_desc)
4802ac6454SAndrew Thompson {
4902ac6454SAndrew Thompson 	uint8_t *desc_next;
5002ac6454SAndrew Thompson 	uint8_t *start;
5102ac6454SAndrew Thompson 	uint8_t *end;
5202ac6454SAndrew Thompson 	uint8_t *desc;
5302ac6454SAndrew Thompson 
5402ac6454SAndrew Thompson 	/* be NULL safe */
5502ac6454SAndrew Thompson 	if (cd == NULL)
5602ac6454SAndrew Thompson 		return (NULL);
5702ac6454SAndrew Thompson 
5802ac6454SAndrew Thompson 	/* We assume that the "wTotalLength" has been checked. */
5902ac6454SAndrew Thompson 	start = (uint8_t *)cd;
6002ac6454SAndrew Thompson 	end = start + UGETW(cd->wTotalLength);
6102ac6454SAndrew Thompson 	desc = (uint8_t *)_desc;
6202ac6454SAndrew Thompson 
6302ac6454SAndrew Thompson 	/* Get start of next USB descriptor. */
6402ac6454SAndrew Thompson 	if (desc == NULL)
6502ac6454SAndrew Thompson 		desc = start;
6602ac6454SAndrew Thompson 	else
6702ac6454SAndrew Thompson 		desc = desc + desc[0];
6802ac6454SAndrew Thompson 
6902ac6454SAndrew Thompson 	/* Check that the next USB descriptor is within the range. */
7002ac6454SAndrew Thompson 	if ((desc < start) || (desc >= end))
7102ac6454SAndrew Thompson 		return (NULL);		/* out of range, or EOD */
7202ac6454SAndrew Thompson 
7302ac6454SAndrew Thompson 	/* Check that the second next USB descriptor is within range. */
7402ac6454SAndrew Thompson 	desc_next = desc + desc[0];
7502ac6454SAndrew Thompson 	if ((desc_next < start) || (desc_next > end))
7602ac6454SAndrew Thompson 		return (NULL);		/* out of range */
7702ac6454SAndrew Thompson 
7802ac6454SAndrew Thompson 	/* Check minimum descriptor length. */
7902ac6454SAndrew Thompson 	if (desc[0] < 3)
8002ac6454SAndrew Thompson 		return (NULL);		/* too short descriptor */
8102ac6454SAndrew Thompson 
8202ac6454SAndrew Thompson 	/* Return start of next descriptor. */
83760bc48eSAndrew Thompson 	return ((struct usb_descriptor *)desc);
8402ac6454SAndrew Thompson }
8502ac6454SAndrew Thompson 
8602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
87bdd41206SAndrew Thompson  *	usb2_idesc_foreach
8802ac6454SAndrew Thompson  *
89bdd41206SAndrew Thompson  * This function will iterate the interface descriptors in the config
90bdd41206SAndrew Thompson  * descriptor. The parse state structure should be zeroed before
91bdd41206SAndrew Thompson  * calling this function the first time.
9202ac6454SAndrew Thompson  *
9302ac6454SAndrew Thompson  * Return values:
9402ac6454SAndrew Thompson  *   NULL: End of descriptors
9502ac6454SAndrew Thompson  *   Else: A valid interface descriptor
9602ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
97760bc48eSAndrew Thompson struct usb_interface_descriptor *
98760bc48eSAndrew Thompson usb2_idesc_foreach(struct usb_config_descriptor *cd,
99760bc48eSAndrew Thompson     struct usb_idesc_parse_state *ps)
10002ac6454SAndrew Thompson {
101760bc48eSAndrew Thompson 	struct usb_interface_descriptor *id;
102bdd41206SAndrew Thompson 	uint8_t new_iface;
10302ac6454SAndrew Thompson 
104bdd41206SAndrew Thompson 	/* retrieve current descriptor */
105760bc48eSAndrew Thompson 	id = (struct usb_interface_descriptor *)ps->desc;
106bdd41206SAndrew Thompson 	/* default is to start a new interface */
107bdd41206SAndrew Thompson 	new_iface = 1;
10802ac6454SAndrew Thompson 
109bdd41206SAndrew Thompson 	while (1) {
110760bc48eSAndrew Thompson 		id = (struct usb_interface_descriptor *)
111760bc48eSAndrew Thompson 		    usb2_desc_foreach(cd, (struct usb_descriptor *)id);
112bdd41206SAndrew Thompson 		if (id == NULL)
113bdd41206SAndrew Thompson 			break;
114bdd41206SAndrew Thompson 		if ((id->bDescriptorType == UDESC_INTERFACE) &&
115bdd41206SAndrew Thompson 		    (id->bLength >= sizeof(*id))) {
116bdd41206SAndrew Thompson 			if (ps->iface_no_last == id->bInterfaceNumber)
117bdd41206SAndrew Thompson 				new_iface = 0;
118bdd41206SAndrew Thompson 			ps->iface_no_last = id->bInterfaceNumber;
119bdd41206SAndrew Thompson 			break;
120bdd41206SAndrew Thompson 		}
121bdd41206SAndrew Thompson 	}
12202ac6454SAndrew Thompson 
123bdd41206SAndrew Thompson 	if (ps->desc == NULL) {
124bdd41206SAndrew Thompson 		/* first time */
125bdd41206SAndrew Thompson 	} else if (new_iface) {
126bdd41206SAndrew Thompson 		/* new interface */
127bdd41206SAndrew Thompson 		ps->iface_index ++;
128bdd41206SAndrew Thompson 		ps->iface_index_alt = 0;
12902ac6454SAndrew Thompson 	} else {
130bdd41206SAndrew Thompson 		/* new alternate interface */
131bdd41206SAndrew Thompson 		ps->iface_index_alt ++;
13202ac6454SAndrew Thompson 	}
13302ac6454SAndrew Thompson 
134bdd41206SAndrew Thompson 	/* store and return current descriptor */
135760bc48eSAndrew Thompson 	ps->desc = (struct usb_descriptor *)id;
13602ac6454SAndrew Thompson 	return (id);
13702ac6454SAndrew Thompson }
13802ac6454SAndrew Thompson 
13902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
140bdd41206SAndrew Thompson  *	usb2_edesc_foreach
14102ac6454SAndrew Thompson  *
142bdd41206SAndrew Thompson  * This function will iterate all the endpoint descriptors within an
143bdd41206SAndrew Thompson  * interface descriptor. Starting value for the "ped" argument should
144bdd41206SAndrew Thompson  * be a valid interface descriptor.
14502ac6454SAndrew Thompson  *
14602ac6454SAndrew Thompson  * Return values:
14702ac6454SAndrew Thompson  *   NULL: End of descriptors
14802ac6454SAndrew Thompson  *   Else: A valid endpoint descriptor
14902ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
150760bc48eSAndrew Thompson struct usb_endpoint_descriptor *
151760bc48eSAndrew Thompson usb2_edesc_foreach(struct usb_config_descriptor *cd,
152760bc48eSAndrew Thompson     struct usb_endpoint_descriptor *ped)
15302ac6454SAndrew Thompson {
154760bc48eSAndrew Thompson 	struct usb_descriptor *desc;
15502ac6454SAndrew Thompson 
156760bc48eSAndrew Thompson 	desc = ((struct usb_descriptor *)ped);
15702ac6454SAndrew Thompson 
15802ac6454SAndrew Thompson 	while ((desc = usb2_desc_foreach(cd, desc))) {
15902ac6454SAndrew Thompson 		if (desc->bDescriptorType == UDESC_INTERFACE) {
16002ac6454SAndrew Thompson 			break;
16102ac6454SAndrew Thompson 		}
16202ac6454SAndrew Thompson 		if (desc->bDescriptorType == UDESC_ENDPOINT) {
163bdd41206SAndrew Thompson 			if (desc->bLength < sizeof(*ped)) {
16402ac6454SAndrew Thompson 				/* endpoint index is invalid */
16502ac6454SAndrew Thompson 				break;
16602ac6454SAndrew Thompson 			}
167760bc48eSAndrew Thompson 			return ((struct usb_endpoint_descriptor *)desc);
16802ac6454SAndrew Thompson 		}
16902ac6454SAndrew Thompson 	}
17002ac6454SAndrew Thompson 	return (NULL);
17102ac6454SAndrew Thompson }
17202ac6454SAndrew Thompson 
17302ac6454SAndrew Thompson /*------------------------------------------------------------------------*
174bdd41206SAndrew Thompson  *	usb2_get_no_descriptors
17502ac6454SAndrew Thompson  *
176bdd41206SAndrew Thompson  * This function will count the total number of descriptors in the
177bdd41206SAndrew Thompson  * configuration descriptor of type "type".
17802ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
179bdd41206SAndrew Thompson uint8_t
180760bc48eSAndrew Thompson usb2_get_no_descriptors(struct usb_config_descriptor *cd, uint8_t type)
18102ac6454SAndrew Thompson {
182760bc48eSAndrew Thompson 	struct usb_descriptor *desc = NULL;
183bdd41206SAndrew Thompson 	uint8_t count = 0;
18402ac6454SAndrew Thompson 
18502ac6454SAndrew Thompson 	while ((desc = usb2_desc_foreach(cd, desc))) {
186bdd41206SAndrew Thompson 		if (desc->bDescriptorType == type) {
18702ac6454SAndrew Thompson 			count++;
188bdd41206SAndrew Thompson 			if (count == 0xFF)
189bdd41206SAndrew Thompson 				break;			/* crazy */
19002ac6454SAndrew Thompson 		}
19102ac6454SAndrew Thompson 	}
19202ac6454SAndrew Thompson 	return (count);
19302ac6454SAndrew Thompson }
19402ac6454SAndrew Thompson 
19502ac6454SAndrew Thompson /*------------------------------------------------------------------------*
19602ac6454SAndrew Thompson  *	usb2_get_no_alts
19702ac6454SAndrew Thompson  *
19802ac6454SAndrew Thompson  * Return value:
199bdd41206SAndrew Thompson  *   Number of alternate settings for the given interface descriptor pointer.
20002ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
201bdd41206SAndrew Thompson uint8_t
202760bc48eSAndrew Thompson usb2_get_no_alts(struct usb_config_descriptor *cd,
203760bc48eSAndrew Thompson     struct usb_interface_descriptor *id)
20402ac6454SAndrew Thompson {
205760bc48eSAndrew Thompson 	struct usb_descriptor *desc;
206bdd41206SAndrew Thompson 	uint8_t n = 0;
207bdd41206SAndrew Thompson 	uint8_t ifaceno;
208bdd41206SAndrew Thompson 
209bdd41206SAndrew Thompson 	ifaceno = id->bInterfaceNumber;
210bdd41206SAndrew Thompson 
211760bc48eSAndrew Thompson 	desc = (struct usb_descriptor *)id;
21202ac6454SAndrew Thompson 
21302ac6454SAndrew Thompson 	while ((desc = usb2_desc_foreach(cd, desc))) {
21402ac6454SAndrew Thompson 		if ((desc->bDescriptorType == UDESC_INTERFACE) &&
21502ac6454SAndrew Thompson 		    (desc->bLength >= sizeof(*id))) {
216760bc48eSAndrew Thompson 			id = (struct usb_interface_descriptor *)desc;
21702ac6454SAndrew Thompson 			if (id->bInterfaceNumber == ifaceno) {
21802ac6454SAndrew Thompson 				n++;
219bdd41206SAndrew Thompson 				if (n == 0xFF)
220bdd41206SAndrew Thompson 					break;		/* crazy */
221bdd41206SAndrew Thompson 			} else
222bdd41206SAndrew Thompson 				break;			/* end */
22302ac6454SAndrew Thompson 		}
22402ac6454SAndrew Thompson 	}
22502ac6454SAndrew Thompson 	return (n);
22602ac6454SAndrew Thompson }
227