xref: /freebsd/sys/dev/usb/usb_parse.c (revision 02ac6454880b59bbc5f3f74dffaffa90b30adc8b)
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  *------------------------------------------------------------------------*/
4502ac6454SAndrew Thompson struct usb2_descriptor *
4602ac6454SAndrew Thompson usb2_desc_foreach(struct usb2_config_descriptor *cd,
4702ac6454SAndrew Thompson     struct usb2_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. */
8302ac6454SAndrew Thompson 	return ((struct usb2_descriptor *)desc);
8402ac6454SAndrew Thompson }
8502ac6454SAndrew Thompson 
8602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
8702ac6454SAndrew Thompson  *	usb2_find_idesc
8802ac6454SAndrew Thompson  *
8902ac6454SAndrew Thompson  * This function will return the interface descriptor, if any, that
9002ac6454SAndrew Thompson  * has index "iface_index" and alternate index "alt_index".
9102ac6454SAndrew Thompson  *
9202ac6454SAndrew Thompson  * Return values:
9302ac6454SAndrew Thompson  *   NULL: End of descriptors
9402ac6454SAndrew Thompson  *   Else: A valid interface descriptor
9502ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
9602ac6454SAndrew Thompson struct usb2_interface_descriptor *
9702ac6454SAndrew Thompson usb2_find_idesc(struct usb2_config_descriptor *cd,
9802ac6454SAndrew Thompson     uint8_t iface_index, uint8_t alt_index)
9902ac6454SAndrew Thompson {
10002ac6454SAndrew Thompson 	struct usb2_descriptor *desc = NULL;
10102ac6454SAndrew Thompson 	struct usb2_interface_descriptor *id;
10202ac6454SAndrew Thompson 	uint8_t curidx = 0;
10302ac6454SAndrew Thompson 	uint8_t lastidx = 0;
10402ac6454SAndrew Thompson 	uint8_t curaidx = 0;
10502ac6454SAndrew Thompson 	uint8_t first = 1;
10602ac6454SAndrew Thompson 
10702ac6454SAndrew Thompson 	while ((desc = usb2_desc_foreach(cd, desc))) {
10802ac6454SAndrew Thompson 		if ((desc->bDescriptorType == UDESC_INTERFACE) &&
10902ac6454SAndrew Thompson 		    (desc->bLength >= sizeof(*id))) {
11002ac6454SAndrew Thompson 			id = (void *)desc;
11102ac6454SAndrew Thompson 
11202ac6454SAndrew Thompson 			if (first) {
11302ac6454SAndrew Thompson 				first = 0;
11402ac6454SAndrew Thompson 				lastidx = id->bInterfaceNumber;
11502ac6454SAndrew Thompson 
11602ac6454SAndrew Thompson 			} else if (id->bInterfaceNumber != lastidx) {
11702ac6454SAndrew Thompson 
11802ac6454SAndrew Thompson 				lastidx = id->bInterfaceNumber;
11902ac6454SAndrew Thompson 				curidx++;
12002ac6454SAndrew Thompson 				curaidx = 0;
12102ac6454SAndrew Thompson 
12202ac6454SAndrew Thompson 			} else {
12302ac6454SAndrew Thompson 				curaidx++;
12402ac6454SAndrew Thompson 			}
12502ac6454SAndrew Thompson 
12602ac6454SAndrew Thompson 			if ((iface_index == curidx) && (alt_index == curaidx)) {
12702ac6454SAndrew Thompson 				return (id);
12802ac6454SAndrew Thompson 			}
12902ac6454SAndrew Thompson 		}
13002ac6454SAndrew Thompson 	}
13102ac6454SAndrew Thompson 	return (NULL);
13202ac6454SAndrew Thompson }
13302ac6454SAndrew Thompson 
13402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
13502ac6454SAndrew Thompson  *	usb2_find_edesc
13602ac6454SAndrew Thompson  *
13702ac6454SAndrew Thompson  * This function will return the endpoint descriptor for the passed
13802ac6454SAndrew Thompson  * interface index, alternate index and endpoint index.
13902ac6454SAndrew Thompson  *
14002ac6454SAndrew Thompson  * Return values:
14102ac6454SAndrew Thompson  *   NULL: End of descriptors
14202ac6454SAndrew Thompson  *   Else: A valid endpoint descriptor
14302ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
14402ac6454SAndrew Thompson struct usb2_endpoint_descriptor *
14502ac6454SAndrew Thompson usb2_find_edesc(struct usb2_config_descriptor *cd,
14602ac6454SAndrew Thompson     uint8_t iface_index, uint8_t alt_index, uint8_t ep_index)
14702ac6454SAndrew Thompson {
14802ac6454SAndrew Thompson 	struct usb2_descriptor *desc = NULL;
14902ac6454SAndrew Thompson 	struct usb2_interface_descriptor *d;
15002ac6454SAndrew Thompson 	uint8_t curidx = 0;
15102ac6454SAndrew Thompson 
15202ac6454SAndrew Thompson 	d = usb2_find_idesc(cd, iface_index, alt_index);
15302ac6454SAndrew Thompson 	if (d == NULL)
15402ac6454SAndrew Thompson 		return (NULL);
15502ac6454SAndrew Thompson 
15602ac6454SAndrew Thompson 	if (ep_index >= d->bNumEndpoints)	/* quick exit */
15702ac6454SAndrew Thompson 		return (NULL);
15802ac6454SAndrew Thompson 
15902ac6454SAndrew Thompson 	desc = ((void *)d);
16002ac6454SAndrew Thompson 
16102ac6454SAndrew Thompson 	while ((desc = usb2_desc_foreach(cd, desc))) {
16202ac6454SAndrew Thompson 		if (desc->bDescriptorType == UDESC_INTERFACE) {
16302ac6454SAndrew Thompson 			break;
16402ac6454SAndrew Thompson 		}
16502ac6454SAndrew Thompson 		if (desc->bDescriptorType == UDESC_ENDPOINT) {
16602ac6454SAndrew Thompson 			if (curidx == ep_index) {
16702ac6454SAndrew Thompson 				if (desc->bLength <
16802ac6454SAndrew Thompson 				    sizeof(struct usb2_endpoint_descriptor)) {
16902ac6454SAndrew Thompson 					/* endpoint index is invalid */
17002ac6454SAndrew Thompson 					break;
17102ac6454SAndrew Thompson 				}
17202ac6454SAndrew Thompson 				return ((void *)desc);
17302ac6454SAndrew Thompson 			}
17402ac6454SAndrew Thompson 			curidx++;
17502ac6454SAndrew Thompson 		}
17602ac6454SAndrew Thompson 	}
17702ac6454SAndrew Thompson 	return (NULL);
17802ac6454SAndrew Thompson }
17902ac6454SAndrew Thompson 
18002ac6454SAndrew Thompson /*------------------------------------------------------------------------*
18102ac6454SAndrew Thompson  *	usb2_get_no_endpoints
18202ac6454SAndrew Thompson  *
18302ac6454SAndrew Thompson  * This function will count the total number of endpoints available.
18402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
18502ac6454SAndrew Thompson uint16_t
18602ac6454SAndrew Thompson usb2_get_no_endpoints(struct usb2_config_descriptor *cd)
18702ac6454SAndrew Thompson {
18802ac6454SAndrew Thompson 	struct usb2_descriptor *desc = NULL;
18902ac6454SAndrew Thompson 	uint16_t count = 0;
19002ac6454SAndrew Thompson 
19102ac6454SAndrew Thompson 	while ((desc = usb2_desc_foreach(cd, desc))) {
19202ac6454SAndrew Thompson 		if (desc->bDescriptorType == UDESC_ENDPOINT) {
19302ac6454SAndrew Thompson 			count++;
19402ac6454SAndrew Thompson 		}
19502ac6454SAndrew Thompson 	}
19602ac6454SAndrew Thompson 	return (count);
19702ac6454SAndrew Thompson }
19802ac6454SAndrew Thompson 
19902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
20002ac6454SAndrew Thompson  *	usb2_get_no_alts
20102ac6454SAndrew Thompson  *
20202ac6454SAndrew Thompson  * Return value:
20302ac6454SAndrew Thompson  *   Number of alternate settings for the given "ifaceno".
20402ac6454SAndrew Thompson  *
20502ac6454SAndrew Thompson  * NOTE: The returned can be larger than the actual number of
20602ac6454SAndrew Thompson  * alternate settings.
20702ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
20802ac6454SAndrew Thompson uint16_t
20902ac6454SAndrew Thompson usb2_get_no_alts(struct usb2_config_descriptor *cd, uint8_t ifaceno)
21002ac6454SAndrew Thompson {
21102ac6454SAndrew Thompson 	struct usb2_descriptor *desc = NULL;
21202ac6454SAndrew Thompson 	struct usb2_interface_descriptor *id;
21302ac6454SAndrew Thompson 	uint16_t n = 0;
21402ac6454SAndrew Thompson 
21502ac6454SAndrew Thompson 	while ((desc = usb2_desc_foreach(cd, desc))) {
21602ac6454SAndrew Thompson 		if ((desc->bDescriptorType == UDESC_INTERFACE) &&
21702ac6454SAndrew Thompson 		    (desc->bLength >= sizeof(*id))) {
21802ac6454SAndrew Thompson 			id = (void *)desc;
21902ac6454SAndrew Thompson 			if (id->bInterfaceNumber == ifaceno) {
22002ac6454SAndrew Thompson 				n++;
22102ac6454SAndrew Thompson 			}
22202ac6454SAndrew Thompson 		}
22302ac6454SAndrew Thompson 	}
22402ac6454SAndrew Thompson 	return (n);
22502ac6454SAndrew Thompson }
226