xref: /freebsd/sys/dev/usb/usb_parse.c (revision 963169b4af5d1a347560f060254e1df9541b2350)
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 
27ed6d949aSAndrew Thompson #include <sys/stdint.h>
28ed6d949aSAndrew Thompson #include <sys/stddef.h>
29ed6d949aSAndrew Thompson #include <sys/param.h>
30ed6d949aSAndrew Thompson #include <sys/queue.h>
31ed6d949aSAndrew Thompson #include <sys/types.h>
32ed6d949aSAndrew Thompson #include <sys/systm.h>
33ed6d949aSAndrew Thompson #include <sys/kernel.h>
34ed6d949aSAndrew Thompson #include <sys/bus.h>
35ed6d949aSAndrew Thompson #include <sys/linker_set.h>
36ed6d949aSAndrew Thompson #include <sys/module.h>
37ed6d949aSAndrew Thompson #include <sys/lock.h>
38ed6d949aSAndrew Thompson #include <sys/mutex.h>
39ed6d949aSAndrew Thompson #include <sys/condvar.h>
40ed6d949aSAndrew Thompson #include <sys/sysctl.h>
41ed6d949aSAndrew Thompson #include <sys/sx.h>
42ed6d949aSAndrew Thompson #include <sys/unistd.h>
43ed6d949aSAndrew Thompson #include <sys/callout.h>
44ed6d949aSAndrew Thompson #include <sys/malloc.h>
45ed6d949aSAndrew Thompson #include <sys/priv.h>
4602ac6454SAndrew Thompson 
47ed6d949aSAndrew Thompson #include <dev/usb/usb.h>
48ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
49ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h>
50ed6d949aSAndrew Thompson 
5102ac6454SAndrew Thompson 
5202ac6454SAndrew Thompson /*------------------------------------------------------------------------*
53a593f6b8SAndrew Thompson  *	usb_desc_foreach
5402ac6454SAndrew Thompson  *
5502ac6454SAndrew Thompson  * This function is the safe way to iterate across the USB config
5602ac6454SAndrew Thompson  * descriptor. It contains several checks against invalid
5702ac6454SAndrew Thompson  * descriptors. If the "desc" argument passed to this function is
5802ac6454SAndrew Thompson  * "NULL" the first descriptor, if any, will be returned.
5902ac6454SAndrew Thompson  *
6002ac6454SAndrew Thompson  * Return values:
6102ac6454SAndrew Thompson  *   NULL: End of descriptors
6202ac6454SAndrew Thompson  *   Else: Next descriptor after "desc"
6302ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
64760bc48eSAndrew Thompson struct usb_descriptor *
65a593f6b8SAndrew Thompson usb_desc_foreach(struct usb_config_descriptor *cd,
66760bc48eSAndrew Thompson     struct usb_descriptor *_desc)
6702ac6454SAndrew Thompson {
6802ac6454SAndrew Thompson 	uint8_t *desc_next;
6902ac6454SAndrew Thompson 	uint8_t *start;
7002ac6454SAndrew Thompson 	uint8_t *end;
7102ac6454SAndrew Thompson 	uint8_t *desc;
7202ac6454SAndrew Thompson 
7302ac6454SAndrew Thompson 	/* be NULL safe */
7402ac6454SAndrew Thompson 	if (cd == NULL)
7502ac6454SAndrew Thompson 		return (NULL);
7602ac6454SAndrew Thompson 
7702ac6454SAndrew Thompson 	/* We assume that the "wTotalLength" has been checked. */
7802ac6454SAndrew Thompson 	start = (uint8_t *)cd;
7902ac6454SAndrew Thompson 	end = start + UGETW(cd->wTotalLength);
8002ac6454SAndrew Thompson 	desc = (uint8_t *)_desc;
8102ac6454SAndrew Thompson 
8202ac6454SAndrew Thompson 	/* Get start of next USB descriptor. */
8302ac6454SAndrew Thompson 	if (desc == NULL)
8402ac6454SAndrew Thompson 		desc = start;
8502ac6454SAndrew Thompson 	else
8602ac6454SAndrew Thompson 		desc = desc + desc[0];
8702ac6454SAndrew Thompson 
8802ac6454SAndrew Thompson 	/* Check that the next USB descriptor is within the range. */
8902ac6454SAndrew Thompson 	if ((desc < start) || (desc >= end))
9002ac6454SAndrew Thompson 		return (NULL);		/* out of range, or EOD */
9102ac6454SAndrew Thompson 
9202ac6454SAndrew Thompson 	/* Check that the second next USB descriptor is within range. */
9302ac6454SAndrew Thompson 	desc_next = desc + desc[0];
9402ac6454SAndrew Thompson 	if ((desc_next < start) || (desc_next > end))
9502ac6454SAndrew Thompson 		return (NULL);		/* out of range */
9602ac6454SAndrew Thompson 
9702ac6454SAndrew Thompson 	/* Check minimum descriptor length. */
9802ac6454SAndrew Thompson 	if (desc[0] < 3)
9902ac6454SAndrew Thompson 		return (NULL);		/* too short descriptor */
10002ac6454SAndrew Thompson 
10102ac6454SAndrew Thompson 	/* Return start of next descriptor. */
102760bc48eSAndrew Thompson 	return ((struct usb_descriptor *)desc);
10302ac6454SAndrew Thompson }
10402ac6454SAndrew Thompson 
10502ac6454SAndrew Thompson /*------------------------------------------------------------------------*
106a593f6b8SAndrew Thompson  *	usb_idesc_foreach
10702ac6454SAndrew Thompson  *
108bdd41206SAndrew Thompson  * This function will iterate the interface descriptors in the config
109bdd41206SAndrew Thompson  * descriptor. The parse state structure should be zeroed before
110bdd41206SAndrew Thompson  * calling this function the first time.
11102ac6454SAndrew Thompson  *
11202ac6454SAndrew Thompson  * Return values:
11302ac6454SAndrew Thompson  *   NULL: End of descriptors
11402ac6454SAndrew Thompson  *   Else: A valid interface descriptor
11502ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
116760bc48eSAndrew Thompson struct usb_interface_descriptor *
117a593f6b8SAndrew Thompson usb_idesc_foreach(struct usb_config_descriptor *cd,
118760bc48eSAndrew Thompson     struct usb_idesc_parse_state *ps)
11902ac6454SAndrew Thompson {
120760bc48eSAndrew Thompson 	struct usb_interface_descriptor *id;
121bdd41206SAndrew Thompson 	uint8_t new_iface;
12202ac6454SAndrew Thompson 
123bdd41206SAndrew Thompson 	/* retrieve current descriptor */
124760bc48eSAndrew Thompson 	id = (struct usb_interface_descriptor *)ps->desc;
125bdd41206SAndrew Thompson 	/* default is to start a new interface */
126bdd41206SAndrew Thompson 	new_iface = 1;
12702ac6454SAndrew Thompson 
128bdd41206SAndrew Thompson 	while (1) {
129760bc48eSAndrew Thompson 		id = (struct usb_interface_descriptor *)
130a593f6b8SAndrew Thompson 		    usb_desc_foreach(cd, (struct usb_descriptor *)id);
131bdd41206SAndrew Thompson 		if (id == NULL)
132bdd41206SAndrew Thompson 			break;
133bdd41206SAndrew Thompson 		if ((id->bDescriptorType == UDESC_INTERFACE) &&
134bdd41206SAndrew Thompson 		    (id->bLength >= sizeof(*id))) {
135bdd41206SAndrew Thompson 			if (ps->iface_no_last == id->bInterfaceNumber)
136bdd41206SAndrew Thompson 				new_iface = 0;
137bdd41206SAndrew Thompson 			ps->iface_no_last = id->bInterfaceNumber;
138bdd41206SAndrew Thompson 			break;
139bdd41206SAndrew Thompson 		}
140bdd41206SAndrew Thompson 	}
14102ac6454SAndrew Thompson 
142bdd41206SAndrew Thompson 	if (ps->desc == NULL) {
143bdd41206SAndrew Thompson 		/* first time */
144bdd41206SAndrew Thompson 	} else if (new_iface) {
145bdd41206SAndrew Thompson 		/* new interface */
146bdd41206SAndrew Thompson 		ps->iface_index ++;
147bdd41206SAndrew Thompson 		ps->iface_index_alt = 0;
14802ac6454SAndrew Thompson 	} else {
149bdd41206SAndrew Thompson 		/* new alternate interface */
150bdd41206SAndrew Thompson 		ps->iface_index_alt ++;
15102ac6454SAndrew Thompson 	}
15202ac6454SAndrew Thompson 
153bdd41206SAndrew Thompson 	/* store and return current descriptor */
154760bc48eSAndrew Thompson 	ps->desc = (struct usb_descriptor *)id;
15502ac6454SAndrew Thompson 	return (id);
15602ac6454SAndrew Thompson }
15702ac6454SAndrew Thompson 
15802ac6454SAndrew Thompson /*------------------------------------------------------------------------*
159a593f6b8SAndrew Thompson  *	usb_edesc_foreach
16002ac6454SAndrew Thompson  *
161bdd41206SAndrew Thompson  * This function will iterate all the endpoint descriptors within an
162bdd41206SAndrew Thompson  * interface descriptor. Starting value for the "ped" argument should
163bdd41206SAndrew Thompson  * be a valid interface descriptor.
16402ac6454SAndrew Thompson  *
16502ac6454SAndrew Thompson  * Return values:
16602ac6454SAndrew Thompson  *   NULL: End of descriptors
16702ac6454SAndrew Thompson  *   Else: A valid endpoint descriptor
16802ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
169760bc48eSAndrew Thompson struct usb_endpoint_descriptor *
170a593f6b8SAndrew Thompson usb_edesc_foreach(struct usb_config_descriptor *cd,
171760bc48eSAndrew Thompson     struct usb_endpoint_descriptor *ped)
17202ac6454SAndrew Thompson {
173760bc48eSAndrew Thompson 	struct usb_descriptor *desc;
17402ac6454SAndrew Thompson 
175760bc48eSAndrew Thompson 	desc = ((struct usb_descriptor *)ped);
17602ac6454SAndrew Thompson 
177a593f6b8SAndrew Thompson 	while ((desc = usb_desc_foreach(cd, desc))) {
17802ac6454SAndrew Thompson 		if (desc->bDescriptorType == UDESC_INTERFACE) {
17902ac6454SAndrew Thompson 			break;
18002ac6454SAndrew Thompson 		}
18102ac6454SAndrew Thompson 		if (desc->bDescriptorType == UDESC_ENDPOINT) {
182bdd41206SAndrew Thompson 			if (desc->bLength < sizeof(*ped)) {
183*963169b4SHans Petter Selasky 				/* endpoint descriptor is invalid */
18402ac6454SAndrew Thompson 				break;
18502ac6454SAndrew Thompson 			}
186760bc48eSAndrew Thompson 			return ((struct usb_endpoint_descriptor *)desc);
18702ac6454SAndrew Thompson 		}
18802ac6454SAndrew Thompson 	}
18902ac6454SAndrew Thompson 	return (NULL);
19002ac6454SAndrew Thompson }
19102ac6454SAndrew Thompson 
19202ac6454SAndrew Thompson /*------------------------------------------------------------------------*
193*963169b4SHans Petter Selasky  *	usb_ed_comp_foreach
194*963169b4SHans Petter Selasky  *
195*963169b4SHans Petter Selasky  * This function will iterate all the endpoint companion descriptors
196*963169b4SHans Petter Selasky  * within an endpoint descriptor in an interface descriptor. Starting
197*963169b4SHans Petter Selasky  * value for the "ped" argument should be a valid endpoint companion
198*963169b4SHans Petter Selasky  * descriptor.
199*963169b4SHans Petter Selasky  *
200*963169b4SHans Petter Selasky  * Return values:
201*963169b4SHans Petter Selasky  *   NULL: End of descriptors
202*963169b4SHans Petter Selasky  *   Else: A valid endpoint companion descriptor
203*963169b4SHans Petter Selasky  *------------------------------------------------------------------------*/
204*963169b4SHans Petter Selasky struct usb_endpoint_ss_comp_descriptor *
205*963169b4SHans Petter Selasky usb_ed_comp_foreach(struct usb_config_descriptor *cd,
206*963169b4SHans Petter Selasky     struct usb_endpoint_ss_comp_descriptor *ped)
207*963169b4SHans Petter Selasky {
208*963169b4SHans Petter Selasky 	struct usb_descriptor *desc;
209*963169b4SHans Petter Selasky 
210*963169b4SHans Petter Selasky 	desc = ((struct usb_descriptor *)ped);
211*963169b4SHans Petter Selasky 
212*963169b4SHans Petter Selasky 	while ((desc = usb_desc_foreach(cd, desc))) {
213*963169b4SHans Petter Selasky 		if (desc->bDescriptorType == UDESC_INTERFACE)
214*963169b4SHans Petter Selasky 			break;
215*963169b4SHans Petter Selasky 		if (desc->bDescriptorType == UDESC_ENDPOINT)
216*963169b4SHans Petter Selasky 			break;
217*963169b4SHans Petter Selasky 		if (desc->bDescriptorType == UDESC_ENDPOINT_SS_COMP) {
218*963169b4SHans Petter Selasky 			if (desc->bLength < sizeof(*ped)) {
219*963169b4SHans Petter Selasky 				/* endpoint companion descriptor is invalid */
220*963169b4SHans Petter Selasky 				break;
221*963169b4SHans Petter Selasky 			}
222*963169b4SHans Petter Selasky 			return ((struct usb_endpoint_ss_comp_descriptor *)desc);
223*963169b4SHans Petter Selasky 		}
224*963169b4SHans Petter Selasky 	}
225*963169b4SHans Petter Selasky 	return (NULL);
226*963169b4SHans Petter Selasky }
227*963169b4SHans Petter Selasky 
228*963169b4SHans Petter Selasky /*------------------------------------------------------------------------*
229a593f6b8SAndrew Thompson  *	usbd_get_no_descriptors
23002ac6454SAndrew Thompson  *
231bdd41206SAndrew Thompson  * This function will count the total number of descriptors in the
232bdd41206SAndrew Thompson  * configuration descriptor of type "type".
23302ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
234bdd41206SAndrew Thompson uint8_t
235a593f6b8SAndrew Thompson usbd_get_no_descriptors(struct usb_config_descriptor *cd, uint8_t type)
23602ac6454SAndrew Thompson {
237760bc48eSAndrew Thompson 	struct usb_descriptor *desc = NULL;
238bdd41206SAndrew Thompson 	uint8_t count = 0;
23902ac6454SAndrew Thompson 
240a593f6b8SAndrew Thompson 	while ((desc = usb_desc_foreach(cd, desc))) {
241bdd41206SAndrew Thompson 		if (desc->bDescriptorType == type) {
24202ac6454SAndrew Thompson 			count++;
243bdd41206SAndrew Thompson 			if (count == 0xFF)
244bdd41206SAndrew Thompson 				break;			/* crazy */
24502ac6454SAndrew Thompson 		}
24602ac6454SAndrew Thompson 	}
24702ac6454SAndrew Thompson 	return (count);
24802ac6454SAndrew Thompson }
24902ac6454SAndrew Thompson 
25002ac6454SAndrew Thompson /*------------------------------------------------------------------------*
251a593f6b8SAndrew Thompson  *	usbd_get_no_alts
25202ac6454SAndrew Thompson  *
25302ac6454SAndrew Thompson  * Return value:
254bd73b187SAlfred Perlstein  *   Number of alternate settings for the given interface descriptor
255bd73b187SAlfred Perlstein  *   pointer. If the USB descriptor is corrupt, the returned value can
256bd73b187SAlfred Perlstein  *   be greater than the actual number of alternate settings.
25702ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
258bdd41206SAndrew Thompson uint8_t
259a593f6b8SAndrew Thompson usbd_get_no_alts(struct usb_config_descriptor *cd,
260760bc48eSAndrew Thompson     struct usb_interface_descriptor *id)
26102ac6454SAndrew Thompson {
262760bc48eSAndrew Thompson 	struct usb_descriptor *desc;
263bd73b187SAlfred Perlstein 	uint8_t n;
264bdd41206SAndrew Thompson 	uint8_t ifaceno;
265bdd41206SAndrew Thompson 
266bd73b187SAlfred Perlstein 	/* Reset interface count */
267bd73b187SAlfred Perlstein 
268bd73b187SAlfred Perlstein 	n = 0;
269bd73b187SAlfred Perlstein 
270bd73b187SAlfred Perlstein 	/* Get the interface number */
271bd73b187SAlfred Perlstein 
272bdd41206SAndrew Thompson 	ifaceno = id->bInterfaceNumber;
273bdd41206SAndrew Thompson 
274bd73b187SAlfred Perlstein 	/* Iterate all the USB descriptors */
27502ac6454SAndrew Thompson 
276bd73b187SAlfred Perlstein 	desc = NULL;
277a593f6b8SAndrew Thompson 	while ((desc = usb_desc_foreach(cd, desc))) {
27802ac6454SAndrew Thompson 		if ((desc->bDescriptorType == UDESC_INTERFACE) &&
27902ac6454SAndrew Thompson 		    (desc->bLength >= sizeof(*id))) {
280760bc48eSAndrew Thompson 			id = (struct usb_interface_descriptor *)desc;
28102ac6454SAndrew Thompson 			if (id->bInterfaceNumber == ifaceno) {
28202ac6454SAndrew Thompson 				n++;
283bdd41206SAndrew Thompson 				if (n == 0xFF)
284bdd41206SAndrew Thompson 					break;		/* crazy */
285bd73b187SAlfred Perlstein 			}
28602ac6454SAndrew Thompson 		}
28702ac6454SAndrew Thompson 	}
28802ac6454SAndrew Thompson 	return (n);
28902ac6454SAndrew Thompson }
290