source: mainline/uspace/lib/usbdev/src/dp.c

Last change on this file was 3bacee1, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Make ccheck-fix again and commit more good files.

  • Property mode set to 100644
File size: 9.5 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 <usb/dev/dp.h>
44#include <usb/descriptor.h>
45
46#include <assert.h>
47#include <errno.h>
48#include <stdlib.h>
49#include <stdbool.h>
50#include <stddef.h>
51#include <stdint.h>
52
53#define NESTING(parentname, childname) \
54 { \
55 .child = USB_DESCTYPE_##childname, \
56 .parent = USB_DESCTYPE_##parentname, \
57 }
58#define LAST_NESTING { -1, -1 }
59
60/** Nesting of standard USB descriptors. */
61const usb_dp_descriptor_nesting_t usb_dp_standard_descriptor_nesting[] = {
62 NESTING(CONFIGURATION, INTERFACE),
63 NESTING(INTERFACE, ENDPOINT),
64 NESTING(ENDPOINT, SSPEED_EP_COMPANION),
65 NESTING(INTERFACE, HUB),
66 NESTING(INTERFACE, HID),
67 NESTING(HID, HID_REPORT),
68 LAST_NESTING
69};
70
71#undef NESTING
72#undef LAST_NESTING
73
74/** Tells whether pointer points inside descriptor data.
75 *
76 * @param data Parser data.
77 * @param ptr Pointer to be verified.
78 * @return Whether @p ptr points inside <code>data->data</code> field.
79 */
80static bool is_valid_descriptor_pointer(const usb_dp_parser_data_t *data,
81 const uint8_t *ptr)
82{
83 if (ptr == NULL) {
84 return false;
85 }
86
87 if (ptr < data->data) {
88 return false;
89 }
90
91 if ((size_t)(ptr - data->data) >= data->size) {
92 return false;
93 }
94
95 return true;
96}
97
98/** Get next descriptor regardless of the nesting.
99 *
100 * @param data Parser data.
101 * @param current Pointer to current descriptor.
102 * @return Pointer to start of next descriptor.
103 * @retval NULL Invalid input or no next descriptor.
104 */
105static const uint8_t *get_next_descriptor(const usb_dp_parser_data_t *data,
106 const uint8_t *current)
107{
108 assert(is_valid_descriptor_pointer(data, current));
109
110 const uint8_t current_length = *current;
111 const uint8_t *next = current + current_length;
112
113 if (!is_valid_descriptor_pointer(data, next)) {
114 return NULL;
115 }
116
117 return next;
118}
119
120/** Get descriptor type.
121 *
122 * @see usb_descriptor_type_t
123 *
124 * @param data Parser data.
125 * @param start Pointer to start of the descriptor.
126 * @return Descriptor type.
127 * @retval -1 Invalid input.
128 */
129static int get_descriptor_type(const usb_dp_parser_data_t *data,
130 const uint8_t *start)
131{
132 if (start == NULL) {
133 return -1;
134 }
135
136 start++;
137 if (!is_valid_descriptor_pointer(data, start)) {
138 return -1;
139 } else {
140 return (int) (*start);
141 }
142}
143
144/** Tells whether descriptors could be nested.
145 *
146 * @param parser Parser.
147 * @param child Child descriptor type.
148 * @param parent Parent descriptor type.
149 * @return Whether @p child could be child of @p parent.
150 */
151static bool is_nested_descriptor_type(const usb_dp_parser_t *parser,
152 int child, int parent)
153{
154 const usb_dp_descriptor_nesting_t *nesting = parser->nesting;
155 while ((nesting->child > 0) && (nesting->parent > 0)) {
156 if ((nesting->child == child) && (nesting->parent == parent)) {
157 return true;
158 }
159 nesting++;
160 }
161 return false;
162}
163
164/** Tells whether descriptors could be nested.
165 *
166 * @param parser Parser.
167 * @param data Parser data.
168 * @param child Pointer to child descriptor.
169 * @param parent Pointer to parent descriptor.
170 * @return Whether @p child could be child of @p parent.
171 */
172static bool is_nested_descriptor(const usb_dp_parser_t *parser,
173 const usb_dp_parser_data_t *data, const uint8_t *child, const uint8_t *parent)
174{
175 return is_nested_descriptor_type(parser,
176 get_descriptor_type(data, child),
177 get_descriptor_type(data, parent));
178}
179
180/** Find first nested descriptor of given parent.
181 *
182 * @param parser Parser.
183 * @param data Parser data.
184 * @param parent Pointer to the beginning of parent descriptor.
185 * @return Pointer to the beginning of the first nested (child) descriptor.
186 * @retval NULL No child descriptor found.
187 * @retval NULL Invalid input.
188 */
189const uint8_t *usb_dp_get_nested_descriptor(const usb_dp_parser_t *parser,
190 const usb_dp_parser_data_t *data, const uint8_t *parent)
191{
192 if (!is_valid_descriptor_pointer(data, parent)) {
193 return NULL;
194 }
195
196 const uint8_t *next = get_next_descriptor(data, parent);
197 if (next == NULL) {
198 return NULL;
199 }
200
201 if (is_nested_descriptor(parser, data, next, parent)) {
202 return next;
203 } else {
204 return NULL;
205 }
206}
207
208/** Skip all nested descriptors.
209 *
210 * @param parser Parser.
211 * @param data Parser data.
212 * @param parent Pointer to the beginning of parent descriptor.
213 * @return Pointer to first non-child descriptor.
214 * @retval NULL No next descriptor.
215 * @retval NULL Invalid input.
216 */
217static const uint8_t *skip_nested_descriptors(const usb_dp_parser_t *parser,
218 const usb_dp_parser_data_t *data, const uint8_t *parent)
219{
220 const uint8_t *child =
221 usb_dp_get_nested_descriptor(parser, data, parent);
222 if (child == NULL) {
223 return get_next_descriptor(data, parent);
224 }
225 const uint8_t *next_child =
226 skip_nested_descriptors(parser, data, child);
227 while (is_nested_descriptor(parser, data, next_child, parent)) {
228 next_child = skip_nested_descriptors(parser, data, next_child);
229 }
230
231 return next_child;
232}
233
234/** Get sibling descriptor.
235 *
236 * @param parser Parser.
237 * @param data Parser data.
238 * @param parent Pointer to common parent descriptor.
239 * @param sibling Left sibling.
240 * @return Pointer to first right sibling of @p sibling.
241 * @retval NULL No sibling exist.
242 * @retval NULL Invalid input.
243 */
244const uint8_t *usb_dp_get_sibling_descriptor(
245 const usb_dp_parser_t *parser, const usb_dp_parser_data_t *data,
246 const uint8_t *parent, const uint8_t *sibling)
247{
248 if (!is_valid_descriptor_pointer(data, parent) ||
249 !is_valid_descriptor_pointer(data, sibling)) {
250 return NULL;
251 }
252
253 const uint8_t *possible_sibling =
254 skip_nested_descriptors(parser, data, sibling);
255 if (possible_sibling == NULL) {
256 return NULL;
257 }
258
259 int parent_type = get_descriptor_type(data, parent);
260 int possible_sibling_type = get_descriptor_type(data, possible_sibling);
261 if (is_nested_descriptor_type(parser,
262 possible_sibling_type, parent_type)) {
263 return possible_sibling;
264 } else {
265 return NULL;
266 }
267}
268
269/** Browser of the descriptor tree.
270 *
271 * @see usb_dp_walk_simple
272 *
273 * @param parser Descriptor parser.
274 * @param data Data for descriptor parser.
275 * @param root Pointer to current root of the tree.
276 * @param depth Current nesting depth.
277 * @param callback Callback for each found descriptor.
278 * @param arg Custom (user) argument.
279 */
280static void usb_dp_browse_simple_internal(const usb_dp_parser_t *parser,
281 const usb_dp_parser_data_t *data, const uint8_t *root, size_t depth,
282 void (*callback)(const uint8_t *, size_t, void *), void *arg)
283{
284 if (root == NULL) {
285 return;
286 }
287 callback(root, depth, arg);
288 const uint8_t *child = usb_dp_get_nested_descriptor(parser, data, root);
289 do {
290 usb_dp_browse_simple_internal(parser, data, child, depth + 1,
291 callback, arg);
292 child = usb_dp_get_sibling_descriptor(parser, data,
293 root, child);
294 } while (child != NULL);
295}
296
297/** Browse flatten descriptor tree.
298 *
299 * The callback is called with following arguments: pointer to the start
300 * of the descriptor (somewhere inside @p descriptors), depth of the nesting
301 * (starting from 0 for the first descriptor) and the custom argument.
302 * Note that the size of the descriptor is not passed because it can
303 * be read from the first byte of the descriptor.
304 *
305 * @param descriptors Descriptor data.
306 * @param descriptors_size Size of descriptor data (in bytes).
307 * @param descriptor_nesting Possible descriptor nesting.
308 * @param callback Callback for each found descriptor.
309 * @param arg Custom (user) argument.
310 */
311void usb_dp_walk_simple(const uint8_t *descriptors, size_t descriptors_size,
312 const usb_dp_descriptor_nesting_t *descriptor_nesting,
313 walk_callback_t callback, void *arg)
314{
315 if ((descriptors == NULL) || (descriptors_size == 0) ||
316 (descriptor_nesting == NULL) || (callback == NULL)) {
317 return;
318 }
319
320 const usb_dp_parser_data_t data = {
321 .data = descriptors,
322 .size = descriptors_size,
323 .arg = NULL
324 };
325
326 const usb_dp_parser_t parser = {
327 .nesting = descriptor_nesting
328 };
329
330 usb_dp_browse_simple_internal(&parser, &data, descriptors,
331 0, callback, arg);
332}
333
334/** @}
335 */
Note: See TracBrowser for help on using the repository browser.