source: mainline/uspace/lib/usbdev/src/dp.c@ 2a5b62b

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 2a5b62b was 160b75e, checked in by Vojtech Horky <vojtechhorky@…>, 14 years ago

Fix Doxygen groups of USB libraries

  • Property mode set to 100644
File size: 9.2 KB
Line 
1/*
2 * Copyright (c) 2011 Vojtech Horky
3 * 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 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libusbdev
30 * @{
31 */
32/**
33 * @file
34 * USB descriptor parser (implementation).
35 *
36 * The descriptor parser is a generic parser for structure, where individual
37 * items are stored in single buffer and each item begins with length followed
38 * by type. These types are organized into tree hierarchy.
39 *
40 * The parser is able of only two actions: find first child and find next
41 * sibling.
42 */
43#include <stdio.h>
44#include <str_error.h>
45#include <errno.h>
46#include <assert.h>
47#include <bool.h>
48#include <usb/dev/dp.h>
49#include <usb/descriptor.h>
50
51#define NESTING(parentname, childname) \
52 { \
53 .child = USB_DESCTYPE_##childname, \
54 .parent = USB_DESCTYPE_##parentname, \
55 }
56#define LAST_NESTING { -1, -1 }
57
58/** Nesting of standard USB descriptors. */
59usb_dp_descriptor_nesting_t usb_dp_standard_descriptor_nesting[] = {
60 NESTING(CONFIGURATION, INTERFACE),
61 NESTING(INTERFACE, ENDPOINT),
62 NESTING(INTERFACE, HUB),
63 NESTING(INTERFACE, HID),
64 NESTING(HID, HID_REPORT),
65 LAST_NESTING
66};
67
68#undef NESTING
69#undef LAST_NESTING
70
71/** Tells whether pointer points inside descriptor data.
72 *
73 * @param data Parser data.
74 * @param ptr Pointer to be verified.
75 * @return Whether @p ptr points inside <code>data->data</code> field.
76 */
77static bool is_valid_descriptor_pointer(usb_dp_parser_data_t *data,
78 uint8_t *ptr)
79{
80 if (ptr == NULL) {
81 return false;
82 }
83
84 if (ptr < data->data) {
85 return false;
86 }
87
88 if ((size_t)(ptr - data->data) >= data->size) {
89 return false;
90 }
91
92 return true;
93}
94
95/** Get next descriptor regardless of the nesting.
96 *
97 * @param data Parser data.
98 * @param current Pointer to current descriptor.
99 * @return Pointer to start of next descriptor.
100 * @retval NULL Invalid input or no next descriptor.
101 */
102static uint8_t *get_next_descriptor(usb_dp_parser_data_t *data,
103 uint8_t *current)
104{
105 assert(is_valid_descriptor_pointer(data, current));
106
107 uint8_t current_length = *current;
108 uint8_t *next = current + current_length;
109
110 if (!is_valid_descriptor_pointer(data, next)) {
111 return NULL;
112 }
113
114 return next;
115}
116
117/** Get descriptor type.
118 *
119 * @see usb_descriptor_type_t
120 *
121 * @param data Parser data.
122 * @param start Pointer to start of the descriptor.
123 * @return Descriptor type.
124 * @retval -1 Invalid input.
125 */
126static int get_descriptor_type(usb_dp_parser_data_t *data, uint8_t *start)
127{
128 if (start == NULL) {
129 return -1;
130 }
131
132 start++;
133 if (!is_valid_descriptor_pointer(data, start)) {
134 return -1;
135 } else {
136 return (int) (*start);
137 }
138}
139
140/** Tells whether descriptors could be nested.
141 *
142 * @param parser Parser.
143 * @param child Child descriptor type.
144 * @param parent Parent descriptor type.
145 * @return Whether @p child could be child of @p parent.
146 */
147static bool is_nested_descriptor_type(usb_dp_parser_t *parser,
148 int child, int parent)
149{
150 usb_dp_descriptor_nesting_t *nesting = parser->nesting;
151 while ((nesting->child > 0) && (nesting->parent > 0)) {
152 if ((nesting->child == child) && (nesting->parent == parent)) {
153 return true;
154 }
155 nesting++;
156 }
157 return false;
158}
159
160/** Tells whether descriptors could be nested.
161 *
162 * @param parser Parser.
163 * @param data Parser data.
164 * @param child Pointer to child descriptor.
165 * @param parent Pointer to parent descriptor.
166 * @return Whether @p child could be child of @p parent.
167 */
168static bool is_nested_descriptor(usb_dp_parser_t *parser,
169 usb_dp_parser_data_t *data, uint8_t *child, uint8_t *parent)
170{
171 return is_nested_descriptor_type(parser,
172 get_descriptor_type(data, child),
173 get_descriptor_type(data, parent));
174}
175
176/** Find first nested descriptor of given parent.
177 *
178 * @param parser Parser.
179 * @param data Parser data.
180 * @param parent Pointer to the beginning of parent descriptor.
181 * @return Pointer to the beginning of the first nested (child) descriptor.
182 * @retval NULL No child descriptor found.
183 * @retval NULL Invalid input.
184 */
185uint8_t *usb_dp_get_nested_descriptor(usb_dp_parser_t *parser,
186 usb_dp_parser_data_t *data, uint8_t *parent)
187{
188 if (!is_valid_descriptor_pointer(data, parent)) {
189 return NULL;
190 }
191
192 uint8_t *next = get_next_descriptor(data, parent);
193 if (next == NULL) {
194 return NULL;
195 }
196
197 if (is_nested_descriptor(parser, data, next, parent)) {
198 return next;
199 } else {
200 return NULL;
201 }
202}
203
204/** Skip all nested descriptors.
205 *
206 * @param parser Parser.
207 * @param data Parser data.
208 * @param parent Pointer to the beginning of parent descriptor.
209 * @return Pointer to first non-child descriptor.
210 * @retval NULL No next descriptor.
211 * @retval NULL Invalid input.
212 */
213static uint8_t *skip_nested_descriptors(usb_dp_parser_t *parser,
214 usb_dp_parser_data_t *data, uint8_t *parent)
215{
216 uint8_t *child = usb_dp_get_nested_descriptor(parser, data, parent);
217 if (child == NULL) {
218 return get_next_descriptor(data, parent);
219 }
220 uint8_t *next_child = skip_nested_descriptors(parser, data, child);
221 while (is_nested_descriptor(parser, data, next_child, parent)) {
222 next_child = skip_nested_descriptors(parser, data, next_child);
223 }
224
225 return next_child;
226}
227
228/** Get sibling descriptor.
229 *
230 * @param parser Parser.
231 * @param data Parser data.
232 * @param parent Pointer to common parent descriptor.
233 * @param sibling Left sibling.
234 * @return Pointer to first right sibling of @p sibling.
235 * @retval NULL No sibling exist.
236 * @retval NULL Invalid input.
237 */
238uint8_t *usb_dp_get_sibling_descriptor(usb_dp_parser_t *parser,
239 usb_dp_parser_data_t *data, uint8_t *parent, uint8_t *sibling)
240{
241 if (!is_valid_descriptor_pointer(data, parent)
242 || !is_valid_descriptor_pointer(data, sibling)) {
243 return NULL;
244 }
245
246 uint8_t *possible_sibling = skip_nested_descriptors(parser, data, sibling);
247 if (possible_sibling == NULL) {
248 return NULL;
249 }
250
251 int parent_type = get_descriptor_type(data, parent);
252 int possible_sibling_type = get_descriptor_type(data, possible_sibling);
253 if (is_nested_descriptor_type(parser, possible_sibling_type, parent_type)) {
254 return possible_sibling;
255 } else {
256 return NULL;
257 }
258}
259
260/** Browser of the descriptor tree.
261 *
262 * @see usb_dp_walk_simple
263 *
264 * @param parser Descriptor parser.
265 * @param data Data for descriptor parser.
266 * @param root Pointer to current root of the tree.
267 * @param depth Current nesting depth.
268 * @param callback Callback for each found descriptor.
269 * @param arg Custom (user) argument.
270 */
271static void usb_dp_browse_simple_internal(usb_dp_parser_t *parser,
272 usb_dp_parser_data_t *data, uint8_t *root, size_t depth,
273 void (*callback)(uint8_t *, size_t, void *), void *arg)
274{
275 if (root == NULL) {
276 return;
277 }
278 callback(root, depth, arg);
279 uint8_t *child = usb_dp_get_nested_descriptor(parser, data, root);
280 do {
281 usb_dp_browse_simple_internal(parser, data, child, depth + 1,
282 callback, arg);
283 child = usb_dp_get_sibling_descriptor(parser, data,
284 root, child);
285 } while (child != NULL);
286}
287
288/** Browse flatten descriptor tree.
289 *
290 * The callback is called with following arguments: pointer to the start
291 * of the descriptor (somewhere inside @p descriptors), depth of the nesting
292 * (starting from 0 for the first descriptor) and the custom argument.
293 * Note that the size of the descriptor is not passed because it can
294 * be read from the first byte of the descriptor.
295 *
296 * @param descriptors Descriptor data.
297 * @param descriptors_size Size of descriptor data (in bytes).
298 * @param descriptor_nesting Possible descriptor nesting.
299 * @param callback Callback for each found descriptor.
300 * @param arg Custom (user) argument.
301 */
302void usb_dp_walk_simple(uint8_t *descriptors, size_t descriptors_size,
303 usb_dp_descriptor_nesting_t *descriptor_nesting,
304 void (*callback)(uint8_t *, size_t, void *), void *arg)
305{
306 if ((descriptors == NULL) || (descriptors_size == 0)
307 || (descriptor_nesting == NULL) || (callback == NULL)) {
308 return;
309 }
310
311 usb_dp_parser_data_t data = {
312 .data = descriptors,
313 .size = descriptors_size,
314 .arg = NULL
315 };
316
317 usb_dp_parser_t parser = {
318 .nesting = descriptor_nesting
319 };
320
321 usb_dp_browse_simple_internal(&parser, &data, descriptors,
322 0, callback, arg);
323}
324
325/** @}
326 */
Note: See TracBrowser for help on using the repository browser.