usb_parse.c revision 213435
1/* $FreeBSD: head/sys/dev/usb/usb_parse.c 213435 2010-10-04 23:18:05Z hselasky $ */
2/*-
3 * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/stdint.h>
28#include <sys/stddef.h>
29#include <sys/param.h>
30#include <sys/queue.h>
31#include <sys/types.h>
32#include <sys/systm.h>
33#include <sys/kernel.h>
34#include <sys/bus.h>
35#include <sys/linker_set.h>
36#include <sys/module.h>
37#include <sys/lock.h>
38#include <sys/mutex.h>
39#include <sys/condvar.h>
40#include <sys/sysctl.h>
41#include <sys/sx.h>
42#include <sys/unistd.h>
43#include <sys/callout.h>
44#include <sys/malloc.h>
45#include <sys/priv.h>
46
47#include <dev/usb/usb.h>
48#include <dev/usb/usbdi.h>
49#include <dev/usb/usbdi_util.h>
50
51
52/*------------------------------------------------------------------------*
53 *	usb_desc_foreach
54 *
55 * This function is the safe way to iterate across the USB config
56 * descriptor. It contains several checks against invalid
57 * descriptors. If the "desc" argument passed to this function is
58 * "NULL" the first descriptor, if any, will be returned.
59 *
60 * Return values:
61 *   NULL: End of descriptors
62 *   Else: Next descriptor after "desc"
63 *------------------------------------------------------------------------*/
64struct usb_descriptor *
65usb_desc_foreach(struct usb_config_descriptor *cd,
66    struct usb_descriptor *_desc)
67{
68	uint8_t *desc_next;
69	uint8_t *start;
70	uint8_t *end;
71	uint8_t *desc;
72
73	/* be NULL safe */
74	if (cd == NULL)
75		return (NULL);
76
77	/* We assume that the "wTotalLength" has been checked. */
78	start = (uint8_t *)cd;
79	end = start + UGETW(cd->wTotalLength);
80	desc = (uint8_t *)_desc;
81
82	/* Get start of next USB descriptor. */
83	if (desc == NULL)
84		desc = start;
85	else
86		desc = desc + desc[0];
87
88	/* Check that the next USB descriptor is within the range. */
89	if ((desc < start) || (desc >= end))
90		return (NULL);		/* out of range, or EOD */
91
92	/* Check that the second next USB descriptor is within range. */
93	desc_next = desc + desc[0];
94	if ((desc_next < start) || (desc_next > end))
95		return (NULL);		/* out of range */
96
97	/* Check minimum descriptor length. */
98	if (desc[0] < 3)
99		return (NULL);		/* too short descriptor */
100
101	/* Return start of next descriptor. */
102	return ((struct usb_descriptor *)desc);
103}
104
105/*------------------------------------------------------------------------*
106 *	usb_idesc_foreach
107 *
108 * This function will iterate the interface descriptors in the config
109 * descriptor. The parse state structure should be zeroed before
110 * calling this function the first time.
111 *
112 * Return values:
113 *   NULL: End of descriptors
114 *   Else: A valid interface descriptor
115 *------------------------------------------------------------------------*/
116struct usb_interface_descriptor *
117usb_idesc_foreach(struct usb_config_descriptor *cd,
118    struct usb_idesc_parse_state *ps)
119{
120	struct usb_interface_descriptor *id;
121	uint8_t new_iface;
122
123	/* retrieve current descriptor */
124	id = (struct usb_interface_descriptor *)ps->desc;
125	/* default is to start a new interface */
126	new_iface = 1;
127
128	while (1) {
129		id = (struct usb_interface_descriptor *)
130		    usb_desc_foreach(cd, (struct usb_descriptor *)id);
131		if (id == NULL)
132			break;
133		if ((id->bDescriptorType == UDESC_INTERFACE) &&
134		    (id->bLength >= sizeof(*id))) {
135			if (ps->iface_no_last == id->bInterfaceNumber)
136				new_iface = 0;
137			ps->iface_no_last = id->bInterfaceNumber;
138			break;
139		}
140	}
141
142	if (ps->desc == NULL) {
143		/* first time */
144	} else if (new_iface) {
145		/* new interface */
146		ps->iface_index ++;
147		ps->iface_index_alt = 0;
148	} else {
149		/* new alternate interface */
150		ps->iface_index_alt ++;
151	}
152
153	/* store and return current descriptor */
154	ps->desc = (struct usb_descriptor *)id;
155	return (id);
156}
157
158/*------------------------------------------------------------------------*
159 *	usb_edesc_foreach
160 *
161 * This function will iterate all the endpoint descriptors within an
162 * interface descriptor. Starting value for the "ped" argument should
163 * be a valid interface descriptor.
164 *
165 * Return values:
166 *   NULL: End of descriptors
167 *   Else: A valid endpoint descriptor
168 *------------------------------------------------------------------------*/
169struct usb_endpoint_descriptor *
170usb_edesc_foreach(struct usb_config_descriptor *cd,
171    struct usb_endpoint_descriptor *ped)
172{
173	struct usb_descriptor *desc;
174
175	desc = ((struct usb_descriptor *)ped);
176
177	while ((desc = usb_desc_foreach(cd, desc))) {
178		if (desc->bDescriptorType == UDESC_INTERFACE) {
179			break;
180		}
181		if (desc->bDescriptorType == UDESC_ENDPOINT) {
182			if (desc->bLength < sizeof(*ped)) {
183				/* endpoint descriptor is invalid */
184				break;
185			}
186			return ((struct usb_endpoint_descriptor *)desc);
187		}
188	}
189	return (NULL);
190}
191
192/*------------------------------------------------------------------------*
193 *	usb_ed_comp_foreach
194 *
195 * This function will iterate all the endpoint companion descriptors
196 * within an endpoint descriptor in an interface descriptor. Starting
197 * value for the "ped" argument should be a valid endpoint companion
198 * descriptor.
199 *
200 * Return values:
201 *   NULL: End of descriptors
202 *   Else: A valid endpoint companion descriptor
203 *------------------------------------------------------------------------*/
204struct usb_endpoint_ss_comp_descriptor *
205usb_ed_comp_foreach(struct usb_config_descriptor *cd,
206    struct usb_endpoint_ss_comp_descriptor *ped)
207{
208	struct usb_descriptor *desc;
209
210	desc = ((struct usb_descriptor *)ped);
211
212	while ((desc = usb_desc_foreach(cd, desc))) {
213		if (desc->bDescriptorType == UDESC_INTERFACE)
214			break;
215		if (desc->bDescriptorType == UDESC_ENDPOINT)
216			break;
217		if (desc->bDescriptorType == UDESC_ENDPOINT_SS_COMP) {
218			if (desc->bLength < sizeof(*ped)) {
219				/* endpoint companion descriptor is invalid */
220				break;
221			}
222			return ((struct usb_endpoint_ss_comp_descriptor *)desc);
223		}
224	}
225	return (NULL);
226}
227
228/*------------------------------------------------------------------------*
229 *	usbd_get_no_descriptors
230 *
231 * This function will count the total number of descriptors in the
232 * configuration descriptor of type "type".
233 *------------------------------------------------------------------------*/
234uint8_t
235usbd_get_no_descriptors(struct usb_config_descriptor *cd, uint8_t type)
236{
237	struct usb_descriptor *desc = NULL;
238	uint8_t count = 0;
239
240	while ((desc = usb_desc_foreach(cd, desc))) {
241		if (desc->bDescriptorType == type) {
242			count++;
243			if (count == 0xFF)
244				break;			/* crazy */
245		}
246	}
247	return (count);
248}
249
250/*------------------------------------------------------------------------*
251 *	usbd_get_no_alts
252 *
253 * Return value:
254 *   Number of alternate settings for the given interface descriptor
255 *   pointer. If the USB descriptor is corrupt, the returned value can
256 *   be greater than the actual number of alternate settings.
257 *------------------------------------------------------------------------*/
258uint8_t
259usbd_get_no_alts(struct usb_config_descriptor *cd,
260    struct usb_interface_descriptor *id)
261{
262	struct usb_descriptor *desc;
263	uint8_t n;
264	uint8_t ifaceno;
265
266	/* Reset interface count */
267
268	n = 0;
269
270	/* Get the interface number */
271
272	ifaceno = id->bInterfaceNumber;
273
274	/* Iterate all the USB descriptors */
275
276	desc = NULL;
277	while ((desc = usb_desc_foreach(cd, desc))) {
278		if ((desc->bDescriptorType == UDESC_INTERFACE) &&
279		    (desc->bLength >= sizeof(*id))) {
280			id = (struct usb_interface_descriptor *)desc;
281			if (id->bInterfaceNumber == ifaceno) {
282				n++;
283				if (n == 0xFF)
284					break;		/* crazy */
285			}
286		}
287	}
288	return (n);
289}
290