| 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. */
|
|---|
| 39 | const 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 | */
|
|---|
| 58 | static 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 | */
|
|---|
| 83 | static 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 | */
|
|---|
| 107 | static 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 | */
|
|---|
| 129 | static 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 | */
|
|---|
| 150 | static 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 | */
|
|---|
| 167 | const 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 | */
|
|---|
| 195 | static 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 | */
|
|---|
| 222 | const 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 | */
|
|---|
| 258 | static 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 | */
|
|---|
| 289 | void 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 | */
|
|---|