xref: /freebsd/sys/dev/usb/usb_parse.c (revision 39beb93c3f8bdbf72a61fda42300b5ebed7390c8)
1 /* $FreeBSD$ */
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 <dev/usb/usb.h>
28 #include <dev/usb/usb_mfunc.h>
29 
30 #include <dev/usb/usb_core.h>
31 #include <dev/usb/usb_parse.h>
32 
33 /*------------------------------------------------------------------------*
34  *	usb2_desc_foreach
35  *
36  * This function is the safe way to iterate across the USB config
37  * descriptor. It contains several checks against invalid
38  * descriptors. If the "desc" argument passed to this function is
39  * "NULL" the first descriptor, if any, will be returned.
40  *
41  * Return values:
42  *   NULL: End of descriptors
43  *   Else: Next descriptor after "desc"
44  *------------------------------------------------------------------------*/
45 struct usb2_descriptor *
46 usb2_desc_foreach(struct usb2_config_descriptor *cd,
47     struct usb2_descriptor *_desc)
48 {
49 	uint8_t *desc_next;
50 	uint8_t *start;
51 	uint8_t *end;
52 	uint8_t *desc;
53 
54 	/* be NULL safe */
55 	if (cd == NULL)
56 		return (NULL);
57 
58 	/* We assume that the "wTotalLength" has been checked. */
59 	start = (uint8_t *)cd;
60 	end = start + UGETW(cd->wTotalLength);
61 	desc = (uint8_t *)_desc;
62 
63 	/* Get start of next USB descriptor. */
64 	if (desc == NULL)
65 		desc = start;
66 	else
67 		desc = desc + desc[0];
68 
69 	/* Check that the next USB descriptor is within the range. */
70 	if ((desc < start) || (desc >= end))
71 		return (NULL);		/* out of range, or EOD */
72 
73 	/* Check that the second next USB descriptor is within range. */
74 	desc_next = desc + desc[0];
75 	if ((desc_next < start) || (desc_next > end))
76 		return (NULL);		/* out of range */
77 
78 	/* Check minimum descriptor length. */
79 	if (desc[0] < 3)
80 		return (NULL);		/* too short descriptor */
81 
82 	/* Return start of next descriptor. */
83 	return ((struct usb2_descriptor *)desc);
84 }
85 
86 /*------------------------------------------------------------------------*
87  *	usb2_find_idesc
88  *
89  * This function will return the interface descriptor, if any, that
90  * has index "iface_index" and alternate index "alt_index".
91  *
92  * Return values:
93  *   NULL: End of descriptors
94  *   Else: A valid interface descriptor
95  *------------------------------------------------------------------------*/
96 struct usb2_interface_descriptor *
97 usb2_find_idesc(struct usb2_config_descriptor *cd,
98     uint8_t iface_index, uint8_t alt_index)
99 {
100 	struct usb2_descriptor *desc = NULL;
101 	struct usb2_interface_descriptor *id;
102 	uint8_t curidx = 0;
103 	uint8_t lastidx = 0;
104 	uint8_t curaidx = 0;
105 	uint8_t first = 1;
106 
107 	while ((desc = usb2_desc_foreach(cd, desc))) {
108 		if ((desc->bDescriptorType == UDESC_INTERFACE) &&
109 		    (desc->bLength >= sizeof(*id))) {
110 			id = (void *)desc;
111 
112 			if (first) {
113 				first = 0;
114 				lastidx = id->bInterfaceNumber;
115 
116 			} else if (id->bInterfaceNumber != lastidx) {
117 
118 				lastidx = id->bInterfaceNumber;
119 				curidx++;
120 				curaidx = 0;
121 
122 			} else {
123 				curaidx++;
124 			}
125 
126 			if ((iface_index == curidx) && (alt_index == curaidx)) {
127 				return (id);
128 			}
129 		}
130 	}
131 	return (NULL);
132 }
133 
134 /*------------------------------------------------------------------------*
135  *	usb2_find_edesc
136  *
137  * This function will return the endpoint descriptor for the passed
138  * interface index, alternate index and endpoint index.
139  *
140  * Return values:
141  *   NULL: End of descriptors
142  *   Else: A valid endpoint descriptor
143  *------------------------------------------------------------------------*/
144 struct usb2_endpoint_descriptor *
145 usb2_find_edesc(struct usb2_config_descriptor *cd,
146     uint8_t iface_index, uint8_t alt_index, uint8_t ep_index)
147 {
148 	struct usb2_descriptor *desc = NULL;
149 	struct usb2_interface_descriptor *d;
150 	uint8_t curidx = 0;
151 
152 	d = usb2_find_idesc(cd, iface_index, alt_index);
153 	if (d == NULL)
154 		return (NULL);
155 
156 	if (ep_index >= d->bNumEndpoints)	/* quick exit */
157 		return (NULL);
158 
159 	desc = ((void *)d);
160 
161 	while ((desc = usb2_desc_foreach(cd, desc))) {
162 		if (desc->bDescriptorType == UDESC_INTERFACE) {
163 			break;
164 		}
165 		if (desc->bDescriptorType == UDESC_ENDPOINT) {
166 			if (curidx == ep_index) {
167 				if (desc->bLength <
168 				    sizeof(struct usb2_endpoint_descriptor)) {
169 					/* endpoint index is invalid */
170 					break;
171 				}
172 				return ((void *)desc);
173 			}
174 			curidx++;
175 		}
176 	}
177 	return (NULL);
178 }
179 
180 /*------------------------------------------------------------------------*
181  *	usb2_get_no_endpoints
182  *
183  * This function will count the total number of endpoints available.
184  *------------------------------------------------------------------------*/
185 uint16_t
186 usb2_get_no_endpoints(struct usb2_config_descriptor *cd)
187 {
188 	struct usb2_descriptor *desc = NULL;
189 	uint16_t count = 0;
190 
191 	while ((desc = usb2_desc_foreach(cd, desc))) {
192 		if (desc->bDescriptorType == UDESC_ENDPOINT) {
193 			count++;
194 		}
195 	}
196 	return (count);
197 }
198 
199 /*------------------------------------------------------------------------*
200  *	usb2_get_no_alts
201  *
202  * Return value:
203  *   Number of alternate settings for the given "ifaceno".
204  *
205  * NOTE: The returned can be larger than the actual number of
206  * alternate settings.
207  *------------------------------------------------------------------------*/
208 uint16_t
209 usb2_get_no_alts(struct usb2_config_descriptor *cd, uint8_t ifaceno)
210 {
211 	struct usb2_descriptor *desc = NULL;
212 	struct usb2_interface_descriptor *id;
213 	uint16_t n = 0;
214 
215 	while ((desc = usb2_desc_foreach(cd, desc))) {
216 		if ((desc->bDescriptorType == UDESC_INTERFACE) &&
217 		    (desc->bLength >= sizeof(*id))) {
218 			id = (void *)desc;
219 			if (id->bInterfaceNumber == ifaceno) {
220 				n++;
221 			}
222 		}
223 	}
224 	return (n);
225 }
226