1184610Salfred/* $FreeBSD$ */
2184610Salfred/*-
3184610Salfred * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
4184610Salfred *
5184610Salfred * Redistribution and use in source and binary forms, with or without
6184610Salfred * modification, are permitted provided that the following conditions
7184610Salfred * are met:
8184610Salfred * 1. Redistributions of source code must retain the above copyright
9184610Salfred *    notice, this list of conditions and the following disclaimer.
10184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
11184610Salfred *    notice, this list of conditions and the following disclaimer in the
12184610Salfred *    documentation and/or other materials provided with the distribution.
13184610Salfred *
14184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17184610Salfred * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24184610Salfred * SUCH DAMAGE.
25184610Salfred */
26184610Salfred
27246122Shselasky#ifdef USB_GLOBAL_INCLUDE_FILE
28246122Shselasky#include USB_GLOBAL_INCLUDE_FILE
29246122Shselasky#else
30194677Sthompsa#include <sys/stdint.h>
31194677Sthompsa#include <sys/stddef.h>
32194677Sthompsa#include <sys/param.h>
33194677Sthompsa#include <sys/queue.h>
34194677Sthompsa#include <sys/types.h>
35194677Sthompsa#include <sys/systm.h>
36194677Sthompsa#include <sys/kernel.h>
37194677Sthompsa#include <sys/bus.h>
38194677Sthompsa#include <sys/module.h>
39194677Sthompsa#include <sys/lock.h>
40194677Sthompsa#include <sys/mutex.h>
41194677Sthompsa#include <sys/condvar.h>
42194677Sthompsa#include <sys/sysctl.h>
43194677Sthompsa#include <sys/sx.h>
44194677Sthompsa#include <sys/unistd.h>
45194677Sthompsa#include <sys/callout.h>
46194677Sthompsa#include <sys/malloc.h>
47194677Sthompsa#include <sys/priv.h>
48194677Sthompsa
49188942Sthompsa#include <dev/usb/usb.h>
50194677Sthompsa#include <dev/usb/usbdi.h>
51194677Sthompsa#include <dev/usb/usbdi_util.h>
52250204Shselasky
53250204Shselasky#define	USB_DEBUG_VAR usb_debug
54250204Shselasky
55250204Shselasky#include <dev/usb/usb_core.h>
56250204Shselasky#include <dev/usb/usb_debug.h>
57246122Shselasky#endif			/* USB_GLOBAL_INCLUDE_FILE */
58184610Salfred
59184610Salfred/*------------------------------------------------------------------------*
60194228Sthompsa *	usb_desc_foreach
61184610Salfred *
62184610Salfred * This function is the safe way to iterate across the USB config
63184610Salfred * descriptor. It contains several checks against invalid
64184610Salfred * descriptors. If the "desc" argument passed to this function is
65184610Salfred * "NULL" the first descriptor, if any, will be returned.
66184610Salfred *
67184610Salfred * Return values:
68184610Salfred *   NULL: End of descriptors
69184610Salfred *   Else: Next descriptor after "desc"
70184610Salfred *------------------------------------------------------------------------*/
71192984Sthompsastruct usb_descriptor *
72194228Sthompsausb_desc_foreach(struct usb_config_descriptor *cd,
73192984Sthompsa    struct usb_descriptor *_desc)
74184610Salfred{
75187178Sthompsa	uint8_t *desc_next;
76187178Sthompsa	uint8_t *start;
77187178Sthompsa	uint8_t *end;
78187178Sthompsa	uint8_t *desc;
79184610Salfred
80187178Sthompsa	/* be NULL safe */
81187178Sthompsa	if (cd == NULL)
82184610Salfred		return (NULL);
83184610Salfred
84187178Sthompsa	/* We assume that the "wTotalLength" has been checked. */
85187178Sthompsa	start = (uint8_t *)cd;
86187178Sthompsa	end = start + UGETW(cd->wTotalLength);
87187178Sthompsa	desc = (uint8_t *)_desc;
88187178Sthompsa
89187178Sthompsa	/* Get start of next USB descriptor. */
90187178Sthompsa	if (desc == NULL)
91187178Sthompsa		desc = start;
92187178Sthompsa	else
93187178Sthompsa		desc = desc + desc[0];
94187178Sthompsa
95187178Sthompsa	/* Check that the next USB descriptor is within the range. */
96187178Sthompsa	if ((desc < start) || (desc >= end))
97187178Sthompsa		return (NULL);		/* out of range, or EOD */
98187178Sthompsa
99187178Sthompsa	/* Check that the second next USB descriptor is within range. */
100187178Sthompsa	desc_next = desc + desc[0];
101187178Sthompsa	if ((desc_next < start) || (desc_next > end))
102187178Sthompsa		return (NULL);		/* out of range */
103187178Sthompsa
104187178Sthompsa	/* Check minimum descriptor length. */
105187178Sthompsa	if (desc[0] < 3)
106187178Sthompsa		return (NULL);		/* too short descriptor */
107187178Sthompsa
108187178Sthompsa	/* Return start of next descriptor. */
109192984Sthompsa	return ((struct usb_descriptor *)desc);
110184610Salfred}
111184610Salfred
112184610Salfred/*------------------------------------------------------------------------*
113194228Sthompsa *	usb_idesc_foreach
114184610Salfred *
115190730Sthompsa * This function will iterate the interface descriptors in the config
116190730Sthompsa * descriptor. The parse state structure should be zeroed before
117190730Sthompsa * calling this function the first time.
118184610Salfred *
119184610Salfred * Return values:
120184610Salfred *   NULL: End of descriptors
121184610Salfred *   Else: A valid interface descriptor
122184610Salfred *------------------------------------------------------------------------*/
123192984Sthompsastruct usb_interface_descriptor *
124194228Sthompsausb_idesc_foreach(struct usb_config_descriptor *cd,
125192984Sthompsa    struct usb_idesc_parse_state *ps)
126184610Salfred{
127192984Sthompsa	struct usb_interface_descriptor *id;
128190730Sthompsa	uint8_t new_iface;
129184610Salfred
130190730Sthompsa	/* retrieve current descriptor */
131192984Sthompsa	id = (struct usb_interface_descriptor *)ps->desc;
132190730Sthompsa	/* default is to start a new interface */
133190730Sthompsa	new_iface = 1;
134184610Salfred
135190730Sthompsa	while (1) {
136192984Sthompsa		id = (struct usb_interface_descriptor *)
137194228Sthompsa		    usb_desc_foreach(cd, (struct usb_descriptor *)id);
138190730Sthompsa		if (id == NULL)
139190730Sthompsa			break;
140190730Sthompsa		if ((id->bDescriptorType == UDESC_INTERFACE) &&
141190730Sthompsa		    (id->bLength >= sizeof(*id))) {
142190730Sthompsa			if (ps->iface_no_last == id->bInterfaceNumber)
143190730Sthompsa				new_iface = 0;
144190730Sthompsa			ps->iface_no_last = id->bInterfaceNumber;
145190730Sthompsa			break;
146190730Sthompsa		}
147190730Sthompsa	}
148184610Salfred
149190730Sthompsa	if (ps->desc == NULL) {
150250204Shselasky		/* first time or zero descriptors */
151190730Sthompsa	} else if (new_iface) {
152190730Sthompsa		/* new interface */
153190730Sthompsa		ps->iface_index ++;
154190730Sthompsa		ps->iface_index_alt = 0;
155190730Sthompsa	} else {
156190730Sthompsa		/* new alternate interface */
157190730Sthompsa		ps->iface_index_alt ++;
158190730Sthompsa	}
159250204Shselasky#if (USB_IFACE_MAX <= 0)
160250204Shselasky#error "USB_IFACE_MAX must be defined greater than zero"
161250204Shselasky#endif
162250204Shselasky	/* check for too many interfaces */
163250204Shselasky	if (ps->iface_index >= USB_IFACE_MAX) {
164250204Shselasky		DPRINTF("Interface limit reached\n");
165250204Shselasky		id = NULL;
166250204Shselasky	}
167184610Salfred
168190730Sthompsa	/* store and return current descriptor */
169192984Sthompsa	ps->desc = (struct usb_descriptor *)id;
170190730Sthompsa	return (id);
171184610Salfred}
172184610Salfred
173184610Salfred/*------------------------------------------------------------------------*
174194228Sthompsa *	usb_edesc_foreach
175184610Salfred *
176190730Sthompsa * This function will iterate all the endpoint descriptors within an
177190730Sthompsa * interface descriptor. Starting value for the "ped" argument should
178190730Sthompsa * be a valid interface descriptor.
179184610Salfred *
180184610Salfred * Return values:
181184610Salfred *   NULL: End of descriptors
182184610Salfred *   Else: A valid endpoint descriptor
183184610Salfred *------------------------------------------------------------------------*/
184192984Sthompsastruct usb_endpoint_descriptor *
185194228Sthompsausb_edesc_foreach(struct usb_config_descriptor *cd,
186192984Sthompsa    struct usb_endpoint_descriptor *ped)
187184610Salfred{
188192984Sthompsa	struct usb_descriptor *desc;
189184610Salfred
190192984Sthompsa	desc = ((struct usb_descriptor *)ped);
191184610Salfred
192194228Sthompsa	while ((desc = usb_desc_foreach(cd, desc))) {
193184610Salfred		if (desc->bDescriptorType == UDESC_INTERFACE) {
194184610Salfred			break;
195184610Salfred		}
196184610Salfred		if (desc->bDescriptorType == UDESC_ENDPOINT) {
197190730Sthompsa			if (desc->bLength < sizeof(*ped)) {
198213435Shselasky				/* endpoint descriptor is invalid */
199190730Sthompsa				break;
200184610Salfred			}
201192984Sthompsa			return ((struct usb_endpoint_descriptor *)desc);
202184610Salfred		}
203184610Salfred	}
204184610Salfred	return (NULL);
205184610Salfred}
206184610Salfred
207184610Salfred/*------------------------------------------------------------------------*
208213435Shselasky *	usb_ed_comp_foreach
209213435Shselasky *
210213435Shselasky * This function will iterate all the endpoint companion descriptors
211213435Shselasky * within an endpoint descriptor in an interface descriptor. Starting
212213435Shselasky * value for the "ped" argument should be a valid endpoint companion
213213435Shselasky * descriptor.
214213435Shselasky *
215213435Shselasky * Return values:
216213435Shselasky *   NULL: End of descriptors
217213435Shselasky *   Else: A valid endpoint companion descriptor
218213435Shselasky *------------------------------------------------------------------------*/
219213435Shselaskystruct usb_endpoint_ss_comp_descriptor *
220213435Shselaskyusb_ed_comp_foreach(struct usb_config_descriptor *cd,
221213435Shselasky    struct usb_endpoint_ss_comp_descriptor *ped)
222213435Shselasky{
223213435Shselasky	struct usb_descriptor *desc;
224213435Shselasky
225213435Shselasky	desc = ((struct usb_descriptor *)ped);
226213435Shselasky
227213435Shselasky	while ((desc = usb_desc_foreach(cd, desc))) {
228213435Shselasky		if (desc->bDescriptorType == UDESC_INTERFACE)
229213435Shselasky			break;
230213435Shselasky		if (desc->bDescriptorType == UDESC_ENDPOINT)
231213435Shselasky			break;
232213435Shselasky		if (desc->bDescriptorType == UDESC_ENDPOINT_SS_COMP) {
233213435Shselasky			if (desc->bLength < sizeof(*ped)) {
234213435Shselasky				/* endpoint companion descriptor is invalid */
235213435Shselasky				break;
236213435Shselasky			}
237213435Shselasky			return ((struct usb_endpoint_ss_comp_descriptor *)desc);
238213435Shselasky		}
239213435Shselasky	}
240213435Shselasky	return (NULL);
241213435Shselasky}
242213435Shselasky
243213435Shselasky/*------------------------------------------------------------------------*
244194228Sthompsa *	usbd_get_no_descriptors
245184610Salfred *
246190730Sthompsa * This function will count the total number of descriptors in the
247190730Sthompsa * configuration descriptor of type "type".
248184610Salfred *------------------------------------------------------------------------*/
249190730Sthompsauint8_t
250194228Sthompsausbd_get_no_descriptors(struct usb_config_descriptor *cd, uint8_t type)
251184610Salfred{
252192984Sthompsa	struct usb_descriptor *desc = NULL;
253190730Sthompsa	uint8_t count = 0;
254184610Salfred
255194228Sthompsa	while ((desc = usb_desc_foreach(cd, desc))) {
256190730Sthompsa		if (desc->bDescriptorType == type) {
257184610Salfred			count++;
258190730Sthompsa			if (count == 0xFF)
259190730Sthompsa				break;			/* crazy */
260184610Salfred		}
261184610Salfred	}
262184610Salfred	return (count);
263184610Salfred}
264184610Salfred
265184610Salfred/*------------------------------------------------------------------------*
266194228Sthompsa *	usbd_get_no_alts
267184610Salfred *
268184610Salfred * Return value:
269195963Salfred *   Number of alternate settings for the given interface descriptor
270195963Salfred *   pointer. If the USB descriptor is corrupt, the returned value can
271195963Salfred *   be greater than the actual number of alternate settings.
272184610Salfred *------------------------------------------------------------------------*/
273190730Sthompsauint8_t
274194228Sthompsausbd_get_no_alts(struct usb_config_descriptor *cd,
275192984Sthompsa    struct usb_interface_descriptor *id)
276184610Salfred{
277192984Sthompsa	struct usb_descriptor *desc;
278195963Salfred	uint8_t n;
279190730Sthompsa	uint8_t ifaceno;
280184610Salfred
281195963Salfred	/* Reset interface count */
282195963Salfred
283195963Salfred	n = 0;
284195963Salfred
285195963Salfred	/* Get the interface number */
286195963Salfred
287190730Sthompsa	ifaceno = id->bInterfaceNumber;
288190730Sthompsa
289195963Salfred	/* Iterate all the USB descriptors */
290190730Sthompsa
291195963Salfred	desc = NULL;
292194228Sthompsa	while ((desc = usb_desc_foreach(cd, desc))) {
293184610Salfred		if ((desc->bDescriptorType == UDESC_INTERFACE) &&
294184610Salfred		    (desc->bLength >= sizeof(*id))) {
295192984Sthompsa			id = (struct usb_interface_descriptor *)desc;
296184610Salfred			if (id->bInterfaceNumber == ifaceno) {
297184610Salfred				n++;
298190730Sthompsa				if (n == 0xFF)
299190730Sthompsa					break;		/* crazy */
300195963Salfred			}
301184610Salfred		}
302184610Salfred	}
303184610Salfred	return (n);
304184610Salfred}
305