source: mainline/uspace/lib/usbdev/src/dp.c@ 9bfa8c8

Last change on this file since 9bfa8c8 was d7f7a4a, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 4 years ago

Replace some license headers with SPDX identifier

Headers are replaced using tools/transorm-copyright.sh only
when it can be matched verbatim with the license header used
throughout most of the codebase.

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