xref: /dragonfly/sys/bus/u4b/usb_parse.c (revision 2b3f93ea)
1b86003c4SMarkus Pfeiffer /* $FreeBSD: head/sys/dev/usb/usb_parse.c 250204 2013-05-03 09:23:06Z hselasky $ */
212bd3c8bSSascha Wildner /*-
312bd3c8bSSascha Wildner  * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
412bd3c8bSSascha Wildner  *
512bd3c8bSSascha Wildner  * Redistribution and use in source and binary forms, with or without
612bd3c8bSSascha Wildner  * modification, are permitted provided that the following conditions
712bd3c8bSSascha Wildner  * are met:
812bd3c8bSSascha Wildner  * 1. Redistributions of source code must retain the above copyright
912bd3c8bSSascha Wildner  *    notice, this list of conditions and the following disclaimer.
1012bd3c8bSSascha Wildner  * 2. Redistributions in binary form must reproduce the above copyright
1112bd3c8bSSascha Wildner  *    notice, this list of conditions and the following disclaimer in the
1212bd3c8bSSascha Wildner  *    documentation and/or other materials provided with the distribution.
1312bd3c8bSSascha Wildner  *
1412bd3c8bSSascha Wildner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1512bd3c8bSSascha Wildner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1612bd3c8bSSascha Wildner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1712bd3c8bSSascha Wildner  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1812bd3c8bSSascha Wildner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1912bd3c8bSSascha Wildner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2012bd3c8bSSascha Wildner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2112bd3c8bSSascha Wildner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2212bd3c8bSSascha Wildner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2312bd3c8bSSascha Wildner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2412bd3c8bSSascha Wildner  * SUCH DAMAGE.
2512bd3c8bSSascha Wildner  */
2612bd3c8bSSascha Wildner 
2712bd3c8bSSascha Wildner #include <sys/stdint.h>
2812bd3c8bSSascha Wildner #include <sys/param.h>
2912bd3c8bSSascha Wildner #include <sys/queue.h>
3012bd3c8bSSascha Wildner #include <sys/types.h>
3112bd3c8bSSascha Wildner #include <sys/systm.h>
3212bd3c8bSSascha Wildner #include <sys/kernel.h>
3312bd3c8bSSascha Wildner #include <sys/bus.h>
3412bd3c8bSSascha Wildner #include <sys/module.h>
3512bd3c8bSSascha Wildner #include <sys/lock.h>
3612bd3c8bSSascha Wildner #include <sys/condvar.h>
3712bd3c8bSSascha Wildner #include <sys/sysctl.h>
3812bd3c8bSSascha Wildner #include <sys/unistd.h>
3912bd3c8bSSascha Wildner #include <sys/callout.h>
4012bd3c8bSSascha Wildner #include <sys/malloc.h>
41*2b3f93eaSMatthew Dillon #include <sys/caps.h>
4212bd3c8bSSascha Wildner 
43722d05c3SSascha Wildner #include <bus/u4b/usb.h>
44722d05c3SSascha Wildner #include <bus/u4b/usbdi.h>
45722d05c3SSascha Wildner #include <bus/u4b/usbdi_util.h>
4612bd3c8bSSascha Wildner 
4757bed822SMarkus Pfeiffer #define USB_DEBUG_VAR usb_debug
4857bed822SMarkus Pfeiffer 
4957bed822SMarkus Pfeiffer #include <bus/u4b/usb_core.h>
5057bed822SMarkus Pfeiffer #include <bus/u4b/usb_debug.h>
5112bd3c8bSSascha Wildner 
5212bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
5312bd3c8bSSascha Wildner  *	usb_desc_foreach
5412bd3c8bSSascha Wildner  *
5512bd3c8bSSascha Wildner  * This function is the safe way to iterate across the USB config
5612bd3c8bSSascha Wildner  * descriptor. It contains several checks against invalid
5712bd3c8bSSascha Wildner  * descriptors. If the "desc" argument passed to this function is
5812bd3c8bSSascha Wildner  * "NULL" the first descriptor, if any, will be returned.
5912bd3c8bSSascha Wildner  *
6012bd3c8bSSascha Wildner  * Return values:
6112bd3c8bSSascha Wildner  *   NULL: End of descriptors
6212bd3c8bSSascha Wildner  *   Else: Next descriptor after "desc"
6312bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
6412bd3c8bSSascha Wildner struct usb_descriptor *
usb_desc_foreach(struct usb_config_descriptor * cd,struct usb_descriptor * _desc)6512bd3c8bSSascha Wildner usb_desc_foreach(struct usb_config_descriptor *cd,
6612bd3c8bSSascha Wildner     struct usb_descriptor *_desc)
6712bd3c8bSSascha Wildner {
6812bd3c8bSSascha Wildner 	uint8_t *desc_next;
6912bd3c8bSSascha Wildner 	uint8_t *start;
7012bd3c8bSSascha Wildner 	uint8_t *end;
7112bd3c8bSSascha Wildner 	uint8_t *desc;
7212bd3c8bSSascha Wildner 
7312bd3c8bSSascha Wildner 	/* be NULL safe */
7412bd3c8bSSascha Wildner 	if (cd == NULL)
7512bd3c8bSSascha Wildner 		return (NULL);
7612bd3c8bSSascha Wildner 
7712bd3c8bSSascha Wildner 	/* We assume that the "wTotalLength" has been checked. */
7812bd3c8bSSascha Wildner 	start = (uint8_t *)cd;
7912bd3c8bSSascha Wildner 	end = start + UGETW(cd->wTotalLength);
8012bd3c8bSSascha Wildner 	desc = (uint8_t *)_desc;
8112bd3c8bSSascha Wildner 
8212bd3c8bSSascha Wildner 	/* Get start of next USB descriptor. */
8312bd3c8bSSascha Wildner 	if (desc == NULL)
8412bd3c8bSSascha Wildner 		desc = start;
8512bd3c8bSSascha Wildner 	else
8612bd3c8bSSascha Wildner 		desc = desc + desc[0];
8712bd3c8bSSascha Wildner 
8812bd3c8bSSascha Wildner 	/* Check that the next USB descriptor is within the range. */
8912bd3c8bSSascha Wildner 	if ((desc < start) || (desc >= end))
9012bd3c8bSSascha Wildner 		return (NULL);		/* out of range, or EOD */
9112bd3c8bSSascha Wildner 
9212bd3c8bSSascha Wildner 	/* Check that the second next USB descriptor is within range. */
9312bd3c8bSSascha Wildner 	desc_next = desc + desc[0];
9412bd3c8bSSascha Wildner 	if ((desc_next < start) || (desc_next > end))
9512bd3c8bSSascha Wildner 		return (NULL);		/* out of range */
9612bd3c8bSSascha Wildner 
9712bd3c8bSSascha Wildner 	/* Check minimum descriptor length. */
9812bd3c8bSSascha Wildner 	if (desc[0] < 3)
9912bd3c8bSSascha Wildner 		return (NULL);		/* too short descriptor */
10012bd3c8bSSascha Wildner 
10112bd3c8bSSascha Wildner 	/* Return start of next descriptor. */
10212bd3c8bSSascha Wildner 	return ((struct usb_descriptor *)desc);
10312bd3c8bSSascha Wildner }
10412bd3c8bSSascha Wildner 
10512bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
10612bd3c8bSSascha Wildner  *	usb_idesc_foreach
10712bd3c8bSSascha Wildner  *
10812bd3c8bSSascha Wildner  * This function will iterate the interface descriptors in the config
10912bd3c8bSSascha Wildner  * descriptor. The parse state structure should be zeroed before
11012bd3c8bSSascha Wildner  * calling this function the first time.
11112bd3c8bSSascha Wildner  *
11212bd3c8bSSascha Wildner  * Return values:
11312bd3c8bSSascha Wildner  *   NULL: End of descriptors
11412bd3c8bSSascha Wildner  *   Else: A valid interface descriptor
11512bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
11612bd3c8bSSascha Wildner struct usb_interface_descriptor *
usb_idesc_foreach(struct usb_config_descriptor * cd,struct usb_idesc_parse_state * ps)11712bd3c8bSSascha Wildner usb_idesc_foreach(struct usb_config_descriptor *cd,
11812bd3c8bSSascha Wildner     struct usb_idesc_parse_state *ps)
11912bd3c8bSSascha Wildner {
12012bd3c8bSSascha Wildner 	struct usb_interface_descriptor *id;
12112bd3c8bSSascha Wildner 	uint8_t new_iface;
12212bd3c8bSSascha Wildner 
12312bd3c8bSSascha Wildner 	/* retrieve current descriptor */
12412bd3c8bSSascha Wildner 	id = (struct usb_interface_descriptor *)ps->desc;
12512bd3c8bSSascha Wildner 	/* default is to start a new interface */
12612bd3c8bSSascha Wildner 	new_iface = 1;
12712bd3c8bSSascha Wildner 
12812bd3c8bSSascha Wildner 	while (1) {
12912bd3c8bSSascha Wildner 		id = (struct usb_interface_descriptor *)
13012bd3c8bSSascha Wildner 		    usb_desc_foreach(cd, (struct usb_descriptor *)id);
13112bd3c8bSSascha Wildner 		if (id == NULL)
13212bd3c8bSSascha Wildner 			break;
13312bd3c8bSSascha Wildner 		if ((id->bDescriptorType == UDESC_INTERFACE) &&
13412bd3c8bSSascha Wildner 		    (id->bLength >= sizeof(*id))) {
13512bd3c8bSSascha Wildner 			if (ps->iface_no_last == id->bInterfaceNumber)
13612bd3c8bSSascha Wildner 				new_iface = 0;
13712bd3c8bSSascha Wildner 			ps->iface_no_last = id->bInterfaceNumber;
13812bd3c8bSSascha Wildner 			break;
13912bd3c8bSSascha Wildner 		}
14012bd3c8bSSascha Wildner 	}
14112bd3c8bSSascha Wildner 
14212bd3c8bSSascha Wildner 	if (ps->desc == NULL) {
14357bed822SMarkus Pfeiffer 		/* first time or zero descriptors */
14412bd3c8bSSascha Wildner 	} else if (new_iface) {
14512bd3c8bSSascha Wildner 		/* new interface */
14612bd3c8bSSascha Wildner 		ps->iface_index ++;
14712bd3c8bSSascha Wildner 		ps->iface_index_alt = 0;
14812bd3c8bSSascha Wildner 	} else {
14912bd3c8bSSascha Wildner 		/* new alternate interface */
15012bd3c8bSSascha Wildner 		ps->iface_index_alt ++;
15112bd3c8bSSascha Wildner 	}
15257bed822SMarkus Pfeiffer #if (USB_IFACE_MAX <= 0)
15357bed822SMarkus Pfeiffer #error "USB_IFACE_MAX must be defined greater than zero"
15457bed822SMarkus Pfeiffer #endif
15557bed822SMarkus Pfeiffer 	/* check for too many interfaces */
15657bed822SMarkus Pfeiffer 	if (ps->iface_index >= USB_IFACE_MAX) {
15757bed822SMarkus Pfeiffer 		DPRINTF("Interface limit reached\n");
15857bed822SMarkus Pfeiffer 		id = NULL;
15957bed822SMarkus Pfeiffer 	}
16012bd3c8bSSascha Wildner 
16112bd3c8bSSascha Wildner 	/* store and return current descriptor */
16212bd3c8bSSascha Wildner 	ps->desc = (struct usb_descriptor *)id;
16312bd3c8bSSascha Wildner 	return (id);
16412bd3c8bSSascha Wildner }
16512bd3c8bSSascha Wildner 
16612bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
16712bd3c8bSSascha Wildner  *	usb_edesc_foreach
16812bd3c8bSSascha Wildner  *
16912bd3c8bSSascha Wildner  * This function will iterate all the endpoint descriptors within an
17012bd3c8bSSascha Wildner  * interface descriptor. Starting value for the "ped" argument should
17112bd3c8bSSascha Wildner  * be a valid interface descriptor.
17212bd3c8bSSascha Wildner  *
17312bd3c8bSSascha Wildner  * Return values:
17412bd3c8bSSascha Wildner  *   NULL: End of descriptors
17512bd3c8bSSascha Wildner  *   Else: A valid endpoint descriptor
17612bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
17712bd3c8bSSascha Wildner struct usb_endpoint_descriptor *
usb_edesc_foreach(struct usb_config_descriptor * cd,struct usb_endpoint_descriptor * ped)17812bd3c8bSSascha Wildner usb_edesc_foreach(struct usb_config_descriptor *cd,
17912bd3c8bSSascha Wildner     struct usb_endpoint_descriptor *ped)
18012bd3c8bSSascha Wildner {
18112bd3c8bSSascha Wildner 	struct usb_descriptor *desc;
18212bd3c8bSSascha Wildner 
18312bd3c8bSSascha Wildner 	desc = ((struct usb_descriptor *)ped);
18412bd3c8bSSascha Wildner 
18512bd3c8bSSascha Wildner 	while ((desc = usb_desc_foreach(cd, desc))) {
18612bd3c8bSSascha Wildner 		if (desc->bDescriptorType == UDESC_INTERFACE) {
18712bd3c8bSSascha Wildner 			break;
18812bd3c8bSSascha Wildner 		}
18912bd3c8bSSascha Wildner 		if (desc->bDescriptorType == UDESC_ENDPOINT) {
19012bd3c8bSSascha Wildner 			if (desc->bLength < sizeof(*ped)) {
19112bd3c8bSSascha Wildner 				/* endpoint descriptor is invalid */
19212bd3c8bSSascha Wildner 				break;
19312bd3c8bSSascha Wildner 			}
19412bd3c8bSSascha Wildner 			return ((struct usb_endpoint_descriptor *)desc);
19512bd3c8bSSascha Wildner 		}
19612bd3c8bSSascha Wildner 	}
19712bd3c8bSSascha Wildner 	return (NULL);
19812bd3c8bSSascha Wildner }
19912bd3c8bSSascha Wildner 
20012bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
20112bd3c8bSSascha Wildner  *	usb_ed_comp_foreach
20212bd3c8bSSascha Wildner  *
20312bd3c8bSSascha Wildner  * This function will iterate all the endpoint companion descriptors
20412bd3c8bSSascha Wildner  * within an endpoint descriptor in an interface descriptor. Starting
20512bd3c8bSSascha Wildner  * value for the "ped" argument should be a valid endpoint companion
20612bd3c8bSSascha Wildner  * descriptor.
20712bd3c8bSSascha Wildner  *
20812bd3c8bSSascha Wildner  * Return values:
20912bd3c8bSSascha Wildner  *   NULL: End of descriptors
21012bd3c8bSSascha Wildner  *   Else: A valid endpoint companion descriptor
21112bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
21212bd3c8bSSascha Wildner struct usb_endpoint_ss_comp_descriptor *
usb_ed_comp_foreach(struct usb_config_descriptor * cd,struct usb_endpoint_ss_comp_descriptor * ped)21312bd3c8bSSascha Wildner usb_ed_comp_foreach(struct usb_config_descriptor *cd,
21412bd3c8bSSascha Wildner     struct usb_endpoint_ss_comp_descriptor *ped)
21512bd3c8bSSascha Wildner {
21612bd3c8bSSascha Wildner 	struct usb_descriptor *desc;
21712bd3c8bSSascha Wildner 
21812bd3c8bSSascha Wildner 	desc = ((struct usb_descriptor *)ped);
21912bd3c8bSSascha Wildner 
22012bd3c8bSSascha Wildner 	while ((desc = usb_desc_foreach(cd, desc))) {
22112bd3c8bSSascha Wildner 		if (desc->bDescriptorType == UDESC_INTERFACE)
22212bd3c8bSSascha Wildner 			break;
22312bd3c8bSSascha Wildner 		if (desc->bDescriptorType == UDESC_ENDPOINT)
22412bd3c8bSSascha Wildner 			break;
22512bd3c8bSSascha Wildner 		if (desc->bDescriptorType == UDESC_ENDPOINT_SS_COMP) {
22612bd3c8bSSascha Wildner 			if (desc->bLength < sizeof(*ped)) {
22712bd3c8bSSascha Wildner 				/* endpoint companion descriptor is invalid */
22812bd3c8bSSascha Wildner 				break;
22912bd3c8bSSascha Wildner 			}
23012bd3c8bSSascha Wildner 			return ((struct usb_endpoint_ss_comp_descriptor *)desc);
23112bd3c8bSSascha Wildner 		}
23212bd3c8bSSascha Wildner 	}
23312bd3c8bSSascha Wildner 	return (NULL);
23412bd3c8bSSascha Wildner }
23512bd3c8bSSascha Wildner 
23612bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
23712bd3c8bSSascha Wildner  *	usbd_get_no_descriptors
23812bd3c8bSSascha Wildner  *
23912bd3c8bSSascha Wildner  * This function will count the total number of descriptors in the
24012bd3c8bSSascha Wildner  * configuration descriptor of type "type".
24112bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
24212bd3c8bSSascha Wildner uint8_t
usbd_get_no_descriptors(struct usb_config_descriptor * cd,uint8_t type)24312bd3c8bSSascha Wildner usbd_get_no_descriptors(struct usb_config_descriptor *cd, uint8_t type)
24412bd3c8bSSascha Wildner {
24512bd3c8bSSascha Wildner 	struct usb_descriptor *desc = NULL;
24612bd3c8bSSascha Wildner 	uint8_t count = 0;
24712bd3c8bSSascha Wildner 
24812bd3c8bSSascha Wildner 	while ((desc = usb_desc_foreach(cd, desc))) {
24912bd3c8bSSascha Wildner 		if (desc->bDescriptorType == type) {
25012bd3c8bSSascha Wildner 			count++;
25112bd3c8bSSascha Wildner 			if (count == 0xFF)
25212bd3c8bSSascha Wildner 				break;			/* crazy */
25312bd3c8bSSascha Wildner 		}
25412bd3c8bSSascha Wildner 	}
25512bd3c8bSSascha Wildner 	return (count);
25612bd3c8bSSascha Wildner }
25712bd3c8bSSascha Wildner 
25812bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
25912bd3c8bSSascha Wildner  *	usbd_get_no_alts
26012bd3c8bSSascha Wildner  *
26112bd3c8bSSascha Wildner  * Return value:
26212bd3c8bSSascha Wildner  *   Number of alternate settings for the given interface descriptor
26312bd3c8bSSascha Wildner  *   pointer. If the USB descriptor is corrupt, the returned value can
26412bd3c8bSSascha Wildner  *   be greater than the actual number of alternate settings.
26512bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
26612bd3c8bSSascha Wildner uint8_t
usbd_get_no_alts(struct usb_config_descriptor * cd,struct usb_interface_descriptor * id)26712bd3c8bSSascha Wildner usbd_get_no_alts(struct usb_config_descriptor *cd,
26812bd3c8bSSascha Wildner     struct usb_interface_descriptor *id)
26912bd3c8bSSascha Wildner {
27012bd3c8bSSascha Wildner 	struct usb_descriptor *desc;
27112bd3c8bSSascha Wildner 	uint8_t n;
27212bd3c8bSSascha Wildner 	uint8_t ifaceno;
27312bd3c8bSSascha Wildner 
27412bd3c8bSSascha Wildner 	/* Reset interface count */
27512bd3c8bSSascha Wildner 
27612bd3c8bSSascha Wildner 	n = 0;
27712bd3c8bSSascha Wildner 
27812bd3c8bSSascha Wildner 	/* Get the interface number */
27912bd3c8bSSascha Wildner 
28012bd3c8bSSascha Wildner 	ifaceno = id->bInterfaceNumber;
28112bd3c8bSSascha Wildner 
28212bd3c8bSSascha Wildner 	/* Iterate all the USB descriptors */
28312bd3c8bSSascha Wildner 
28412bd3c8bSSascha Wildner 	desc = NULL;
28512bd3c8bSSascha Wildner 	while ((desc = usb_desc_foreach(cd, desc))) {
28612bd3c8bSSascha Wildner 		if ((desc->bDescriptorType == UDESC_INTERFACE) &&
28712bd3c8bSSascha Wildner 		    (desc->bLength >= sizeof(*id))) {
28812bd3c8bSSascha Wildner 			id = (struct usb_interface_descriptor *)desc;
28912bd3c8bSSascha Wildner 			if (id->bInterfaceNumber == ifaceno) {
29012bd3c8bSSascha Wildner 				n++;
29112bd3c8bSSascha Wildner 				if (n == 0xFF)
29212bd3c8bSSascha Wildner 					break;		/* crazy */
29312bd3c8bSSascha Wildner 			}
29412bd3c8bSSascha Wildner 		}
29512bd3c8bSSascha Wildner 	}
29612bd3c8bSSascha Wildner 	return (n);
29712bd3c8bSSascha Wildner }
298