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
27194677Sthompsa#include <sys/stdint.h>
28194677Sthompsa#include <sys/stddef.h>
29194677Sthompsa#include <sys/param.h>
30194677Sthompsa#include <sys/queue.h>
31194677Sthompsa#include <sys/types.h>
32194677Sthompsa#include <sys/systm.h>
33194677Sthompsa#include <sys/kernel.h>
34194677Sthompsa#include <sys/bus.h>
35194677Sthompsa#include <sys/module.h>
36194677Sthompsa#include <sys/lock.h>
37194677Sthompsa#include <sys/mutex.h>
38194677Sthompsa#include <sys/condvar.h>
39194677Sthompsa#include <sys/sysctl.h>
40194677Sthompsa#include <sys/sx.h>
41194677Sthompsa#include <sys/unistd.h>
42194677Sthompsa#include <sys/callout.h>
43194677Sthompsa#include <sys/malloc.h>
44194677Sthompsa#include <sys/priv.h>
45194677Sthompsa
46188942Sthompsa#include <dev/usb/usb.h>
47194677Sthompsa#include <dev/usb/usbdi.h>
48194677Sthompsa#include <dev/usb/usbdi_util.h>
49184610Salfred
50184610Salfred
51184610Salfred/*------------------------------------------------------------------------*
52194228Sthompsa *	usb_desc_foreach
53184610Salfred *
54184610Salfred * This function is the safe way to iterate across the USB config
55184610Salfred * descriptor. It contains several checks against invalid
56184610Salfred * descriptors. If the "desc" argument passed to this function is
57184610Salfred * "NULL" the first descriptor, if any, will be returned.
58184610Salfred *
59184610Salfred * Return values:
60184610Salfred *   NULL: End of descriptors
61184610Salfred *   Else: Next descriptor after "desc"
62184610Salfred *------------------------------------------------------------------------*/
63192984Sthompsastruct usb_descriptor *
64194228Sthompsausb_desc_foreach(struct usb_config_descriptor *cd,
65192984Sthompsa    struct usb_descriptor *_desc)
66184610Salfred{
67187178Sthompsa	uint8_t *desc_next;
68187178Sthompsa	uint8_t *start;
69187178Sthompsa	uint8_t *end;
70187178Sthompsa	uint8_t *desc;
71184610Salfred
72187178Sthompsa	/* be NULL safe */
73187178Sthompsa	if (cd == NULL)
74184610Salfred		return (NULL);
75184610Salfred
76187178Sthompsa	/* We assume that the "wTotalLength" has been checked. */
77187178Sthompsa	start = (uint8_t *)cd;
78187178Sthompsa	end = start + UGETW(cd->wTotalLength);
79187178Sthompsa	desc = (uint8_t *)_desc;
80187178Sthompsa
81187178Sthompsa	/* Get start of next USB descriptor. */
82187178Sthompsa	if (desc == NULL)
83187178Sthompsa		desc = start;
84187178Sthompsa	else
85187178Sthompsa		desc = desc + desc[0];
86187178Sthompsa
87187178Sthompsa	/* Check that the next USB descriptor is within the range. */
88187178Sthompsa	if ((desc < start) || (desc >= end))
89187178Sthompsa		return (NULL);		/* out of range, or EOD */
90187178Sthompsa
91187178Sthompsa	/* Check that the second next USB descriptor is within range. */
92187178Sthompsa	desc_next = desc + desc[0];
93187178Sthompsa	if ((desc_next < start) || (desc_next > end))
94187178Sthompsa		return (NULL);		/* out of range */
95187178Sthompsa
96187178Sthompsa	/* Check minimum descriptor length. */
97187178Sthompsa	if (desc[0] < 3)
98187178Sthompsa		return (NULL);		/* too short descriptor */
99187178Sthompsa
100187178Sthompsa	/* Return start of next descriptor. */
101192984Sthompsa	return ((struct usb_descriptor *)desc);
102184610Salfred}
103184610Salfred
104184610Salfred/*------------------------------------------------------------------------*
105194228Sthompsa *	usb_idesc_foreach
106184610Salfred *
107190730Sthompsa * This function will iterate the interface descriptors in the config
108190730Sthompsa * descriptor. The parse state structure should be zeroed before
109190730Sthompsa * calling this function the first time.
110184610Salfred *
111184610Salfred * Return values:
112184610Salfred *   NULL: End of descriptors
113184610Salfred *   Else: A valid interface descriptor
114184610Salfred *------------------------------------------------------------------------*/
115192984Sthompsastruct usb_interface_descriptor *
116194228Sthompsausb_idesc_foreach(struct usb_config_descriptor *cd,
117192984Sthompsa    struct usb_idesc_parse_state *ps)
118184610Salfred{
119192984Sthompsa	struct usb_interface_descriptor *id;
120190730Sthompsa	uint8_t new_iface;
121184610Salfred
122190730Sthompsa	/* retrieve current descriptor */
123192984Sthompsa	id = (struct usb_interface_descriptor *)ps->desc;
124190730Sthompsa	/* default is to start a new interface */
125190730Sthompsa	new_iface = 1;
126184610Salfred
127190730Sthompsa	while (1) {
128192984Sthompsa		id = (struct usb_interface_descriptor *)
129194228Sthompsa		    usb_desc_foreach(cd, (struct usb_descriptor *)id);
130190730Sthompsa		if (id == NULL)
131190730Sthompsa			break;
132190730Sthompsa		if ((id->bDescriptorType == UDESC_INTERFACE) &&
133190730Sthompsa		    (id->bLength >= sizeof(*id))) {
134190730Sthompsa			if (ps->iface_no_last == id->bInterfaceNumber)
135190730Sthompsa				new_iface = 0;
136190730Sthompsa			ps->iface_no_last = id->bInterfaceNumber;
137190730Sthompsa			break;
138190730Sthompsa		}
139190730Sthompsa	}
140184610Salfred
141190730Sthompsa	if (ps->desc == NULL) {
142190730Sthompsa		/* first time */
143190730Sthompsa	} else if (new_iface) {
144190730Sthompsa		/* new interface */
145190730Sthompsa		ps->iface_index ++;
146190730Sthompsa		ps->iface_index_alt = 0;
147190730Sthompsa	} else {
148190730Sthompsa		/* new alternate interface */
149190730Sthompsa		ps->iface_index_alt ++;
150190730Sthompsa	}
151184610Salfred
152190730Sthompsa	/* store and return current descriptor */
153192984Sthompsa	ps->desc = (struct usb_descriptor *)id;
154190730Sthompsa	return (id);
155184610Salfred}
156184610Salfred
157184610Salfred/*------------------------------------------------------------------------*
158194228Sthompsa *	usb_edesc_foreach
159184610Salfred *
160190730Sthompsa * This function will iterate all the endpoint descriptors within an
161190730Sthompsa * interface descriptor. Starting value for the "ped" argument should
162190730Sthompsa * be a valid interface descriptor.
163184610Salfred *
164184610Salfred * Return values:
165184610Salfred *   NULL: End of descriptors
166184610Salfred *   Else: A valid endpoint descriptor
167184610Salfred *------------------------------------------------------------------------*/
168192984Sthompsastruct usb_endpoint_descriptor *
169194228Sthompsausb_edesc_foreach(struct usb_config_descriptor *cd,
170192984Sthompsa    struct usb_endpoint_descriptor *ped)
171184610Salfred{
172192984Sthompsa	struct usb_descriptor *desc;
173184610Salfred
174192984Sthompsa	desc = ((struct usb_descriptor *)ped);
175184610Salfred
176194228Sthompsa	while ((desc = usb_desc_foreach(cd, desc))) {
177184610Salfred		if (desc->bDescriptorType == UDESC_INTERFACE) {
178184610Salfred			break;
179184610Salfred		}
180184610Salfred		if (desc->bDescriptorType == UDESC_ENDPOINT) {
181190730Sthompsa			if (desc->bLength < sizeof(*ped)) {
182213435Shselasky				/* endpoint descriptor is invalid */
183190730Sthompsa				break;
184184610Salfred			}
185192984Sthompsa			return ((struct usb_endpoint_descriptor *)desc);
186184610Salfred		}
187184610Salfred	}
188184610Salfred	return (NULL);
189184610Salfred}
190184610Salfred
191184610Salfred/*------------------------------------------------------------------------*
192213435Shselasky *	usb_ed_comp_foreach
193213435Shselasky *
194213435Shselasky * This function will iterate all the endpoint companion descriptors
195213435Shselasky * within an endpoint descriptor in an interface descriptor. Starting
196213435Shselasky * value for the "ped" argument should be a valid endpoint companion
197213435Shselasky * descriptor.
198213435Shselasky *
199213435Shselasky * Return values:
200213435Shselasky *   NULL: End of descriptors
201213435Shselasky *   Else: A valid endpoint companion descriptor
202213435Shselasky *------------------------------------------------------------------------*/
203213435Shselaskystruct usb_endpoint_ss_comp_descriptor *
204213435Shselaskyusb_ed_comp_foreach(struct usb_config_descriptor *cd,
205213435Shselasky    struct usb_endpoint_ss_comp_descriptor *ped)
206213435Shselasky{
207213435Shselasky	struct usb_descriptor *desc;
208213435Shselasky
209213435Shselasky	desc = ((struct usb_descriptor *)ped);
210213435Shselasky
211213435Shselasky	while ((desc = usb_desc_foreach(cd, desc))) {
212213435Shselasky		if (desc->bDescriptorType == UDESC_INTERFACE)
213213435Shselasky			break;
214213435Shselasky		if (desc->bDescriptorType == UDESC_ENDPOINT)
215213435Shselasky			break;
216213435Shselasky		if (desc->bDescriptorType == UDESC_ENDPOINT_SS_COMP) {
217213435Shselasky			if (desc->bLength < sizeof(*ped)) {
218213435Shselasky				/* endpoint companion descriptor is invalid */
219213435Shselasky				break;
220213435Shselasky			}
221213435Shselasky			return ((struct usb_endpoint_ss_comp_descriptor *)desc);
222213435Shselasky		}
223213435Shselasky	}
224213435Shselasky	return (NULL);
225213435Shselasky}
226213435Shselasky
227213435Shselasky/*------------------------------------------------------------------------*
228194228Sthompsa *	usbd_get_no_descriptors
229184610Salfred *
230190730Sthompsa * This function will count the total number of descriptors in the
231190730Sthompsa * configuration descriptor of type "type".
232184610Salfred *------------------------------------------------------------------------*/
233190730Sthompsauint8_t
234194228Sthompsausbd_get_no_descriptors(struct usb_config_descriptor *cd, uint8_t type)
235184610Salfred{
236192984Sthompsa	struct usb_descriptor *desc = NULL;
237190730Sthompsa	uint8_t count = 0;
238184610Salfred
239194228Sthompsa	while ((desc = usb_desc_foreach(cd, desc))) {
240190730Sthompsa		if (desc->bDescriptorType == type) {
241184610Salfred			count++;
242190730Sthompsa			if (count == 0xFF)
243190730Sthompsa				break;			/* crazy */
244184610Salfred		}
245184610Salfred	}
246184610Salfred	return (count);
247184610Salfred}
248184610Salfred
249184610Salfred/*------------------------------------------------------------------------*
250194228Sthompsa *	usbd_get_no_alts
251184610Salfred *
252184610Salfred * Return value:
253195963Salfred *   Number of alternate settings for the given interface descriptor
254195963Salfred *   pointer. If the USB descriptor is corrupt, the returned value can
255195963Salfred *   be greater than the actual number of alternate settings.
256184610Salfred *------------------------------------------------------------------------*/
257190730Sthompsauint8_t
258194228Sthompsausbd_get_no_alts(struct usb_config_descriptor *cd,
259192984Sthompsa    struct usb_interface_descriptor *id)
260184610Salfred{
261192984Sthompsa	struct usb_descriptor *desc;
262195963Salfred	uint8_t n;
263190730Sthompsa	uint8_t ifaceno;
264184610Salfred
265195963Salfred	/* Reset interface count */
266195963Salfred
267195963Salfred	n = 0;
268195963Salfred
269195963Salfred	/* Get the interface number */
270195963Salfred
271190730Sthompsa	ifaceno = id->bInterfaceNumber;
272190730Sthompsa
273195963Salfred	/* Iterate all the USB descriptors */
274190730Sthompsa
275195963Salfred	desc = NULL;
276194228Sthompsa	while ((desc = usb_desc_foreach(cd, desc))) {
277184610Salfred		if ((desc->bDescriptorType == UDESC_INTERFACE) &&
278184610Salfred		    (desc->bLength >= sizeof(*id))) {
279192984Sthompsa			id = (struct usb_interface_descriptor *)desc;
280184610Salfred			if (id->bInterfaceNumber == ifaceno) {
281184610Salfred				n++;
282190730Sthompsa				if (n == 0xFF)
283190730Sthompsa					break;		/* crazy */
284195963Salfred			}
285184610Salfred		}
286184610Salfred	}
287184610Salfred	return (n);
288184610Salfred}
289