| 1 | /*
 | 
|---|
| 2 |  * Copyright (c) 2011 Vojtech Horky
 | 
|---|
| 3 |  * Copyright (c) 2018 Ondrej Hlavaty
 | 
|---|
| 4 |  * All rights reserved.
 | 
|---|
| 5 |  *
 | 
|---|
| 6 |  * Redistribution and use in source and binary forms, with or without
 | 
|---|
| 7 |  * modification, are permitted provided that the following conditions
 | 
|---|
| 8 |  * are met:
 | 
|---|
| 9 |  *
 | 
|---|
| 10 |  * - Redistributions of source code must retain the above copyright
 | 
|---|
| 11 |  *   notice, this list of conditions and the following disclaimer.
 | 
|---|
| 12 |  * - Redistributions in binary form must reproduce the above copyright
 | 
|---|
| 13 |  *   notice, this list of conditions and the following disclaimer in the
 | 
|---|
| 14 |  *   documentation and/or other materials provided with the distribution.
 | 
|---|
| 15 |  * - The name of the author may not be used to endorse or promote products
 | 
|---|
| 16 |  *   derived from this software without specific prior written permission.
 | 
|---|
| 17 |  *
 | 
|---|
| 18 |  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 | 
|---|
| 19 |  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 | 
|---|
| 20 |  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 | 
|---|
| 21 |  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 | 
|---|
| 22 |  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 | 
|---|
| 23 |  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
|---|
| 24 |  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
|---|
| 25 |  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
|---|
| 26 |  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 | 
|---|
| 27 |  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
|---|
| 28 |  */
 | 
|---|
| 29 | 
 | 
|---|
| 30 | /** @addtogroup libusb
 | 
|---|
| 31 |  * @{
 | 
|---|
| 32 |  */
 | 
|---|
| 33 | /** @file
 | 
|---|
| 34 |  * Descriptor dumping.
 | 
|---|
| 35 |  */
 | 
|---|
| 36 | #include <stdlib.h>
 | 
|---|
| 37 | #include <stdio.h>
 | 
|---|
| 38 | #include <stddef.h>
 | 
|---|
| 39 | #include <usb/debug.h>
 | 
|---|
| 40 | #include <usb/descriptor.h>
 | 
|---|
| 41 | #include <usb/classes/classes.h>
 | 
|---|
| 42 | #include <usb/classes/hub.h>
 | 
|---|
| 43 | #include <usb/usb.h>
 | 
|---|
| 44 | 
 | 
|---|
| 45 | /** Mapping between descriptor id and dumping function. */
 | 
|---|
| 46 | typedef struct {
 | 
|---|
| 47 |         /** Descriptor id. */
 | 
|---|
| 48 |         int id;
 | 
|---|
| 49 |         /** Dumping function. */
 | 
|---|
| 50 |         void (*dump)(FILE *, const char *, const char *,
 | 
|---|
| 51 |             const uint8_t *, size_t);
 | 
|---|
| 52 | } descriptor_dump_t;
 | 
|---|
| 53 | 
 | 
|---|
| 54 | static void usb_dump_descriptor_device(FILE *, const char *, const char *,
 | 
|---|
| 55 |     const uint8_t *, size_t);
 | 
|---|
| 56 | static void usb_dump_descriptor_configuration(FILE *, const char *, const char *,
 | 
|---|
| 57 |     const uint8_t *, size_t);
 | 
|---|
| 58 | static void usb_dump_descriptor_interface(FILE *, const char *, const char *,
 | 
|---|
| 59 |     const uint8_t *, size_t);
 | 
|---|
| 60 | static void usb_dump_descriptor_string(FILE *, const char *, const char *,
 | 
|---|
| 61 |     const uint8_t *, size_t);
 | 
|---|
| 62 | static void usb_dump_descriptor_endpoint(FILE *, const char *, const char *,
 | 
|---|
| 63 |     const uint8_t *, size_t);
 | 
|---|
| 64 | static void usb_dump_descriptor_superspeed_endpoint_companion(FILE *, const char *, const char *,
 | 
|---|
| 65 |     const uint8_t *, size_t);
 | 
|---|
| 66 | static void usb_dump_descriptor_hid(FILE *, const char *, const char *,
 | 
|---|
| 67 |     const uint8_t *, size_t);
 | 
|---|
| 68 | static void usb_dump_descriptor_hub(FILE *, const char *, const char *,
 | 
|---|
| 69 |     const uint8_t *, size_t);
 | 
|---|
| 70 | static void usb_dump_descriptor_generic(FILE *, const char *, const char *,
 | 
|---|
| 71 |     const uint8_t *, size_t);
 | 
|---|
| 72 | 
 | 
|---|
| 73 | /** Descriptor dumpers mapping. */
 | 
|---|
| 74 | static descriptor_dump_t descriptor_dumpers[] = {
 | 
|---|
| 75 |         { USB_DESCTYPE_DEVICE, usb_dump_descriptor_device },
 | 
|---|
| 76 |         { USB_DESCTYPE_CONFIGURATION, usb_dump_descriptor_configuration },
 | 
|---|
| 77 |         { USB_DESCTYPE_STRING, usb_dump_descriptor_string },
 | 
|---|
| 78 |         { USB_DESCTYPE_INTERFACE, usb_dump_descriptor_interface },
 | 
|---|
| 79 |         { USB_DESCTYPE_ENDPOINT, usb_dump_descriptor_endpoint },
 | 
|---|
| 80 |         { USB_DESCTYPE_SSPEED_EP_COMPANION, usb_dump_descriptor_superspeed_endpoint_companion },
 | 
|---|
| 81 |         { USB_DESCTYPE_HID, usb_dump_descriptor_hid },
 | 
|---|
| 82 |         { USB_DESCTYPE_HUB, usb_dump_descriptor_hub },
 | 
|---|
| 83 |         { -1, usb_dump_descriptor_generic },
 | 
|---|
| 84 |         { -1, NULL }
 | 
|---|
| 85 | };
 | 
|---|
| 86 | 
 | 
|---|
| 87 | /** Dumps standard USB descriptor.
 | 
|---|
| 88 |  * The @p line_suffix must contain the newline <code>\\n</code> character.
 | 
|---|
| 89 |  * When @p line_suffix or @p line_prefix is NULL, they are substitued with
 | 
|---|
| 90 |  * default values
 | 
|---|
| 91 |  * (<code> - </code> for prefix and line termination for suffix).
 | 
|---|
| 92 |  *
 | 
|---|
| 93 |  * @param output Output file stream to dump descriptor to.
 | 
|---|
| 94 |  * @param line_prefix Prefix for each line of output.
 | 
|---|
| 95 |  * @param line_suffix Suffix of each line of output.
 | 
|---|
| 96 |  * @param descriptor Actual descriptor.
 | 
|---|
| 97 |  * @param descriptor_length Descriptor size.
 | 
|---|
| 98 |  */
 | 
|---|
| 99 | void usb_dump_standard_descriptor(FILE *output,
 | 
|---|
| 100 |     const char *line_prefix, const char *line_suffix,
 | 
|---|
| 101 |     const uint8_t *descriptor, size_t descriptor_length)
 | 
|---|
| 102 | {
 | 
|---|
| 103 |         if (descriptor_length < 2) {
 | 
|---|
| 104 |                 return;
 | 
|---|
| 105 |         }
 | 
|---|
| 106 |         int type = descriptor[1];
 | 
|---|
| 107 | 
 | 
|---|
| 108 |         descriptor_dump_t *dumper = descriptor_dumpers;
 | 
|---|
| 109 |         while (dumper->dump != NULL) {
 | 
|---|
| 110 |                 if ((dumper->id == type) || (dumper->id < 0)) {
 | 
|---|
| 111 |                         dumper->dump(output, line_prefix, line_suffix,
 | 
|---|
| 112 |                             descriptor, descriptor_length);
 | 
|---|
| 113 |                         return;
 | 
|---|
| 114 |                 }
 | 
|---|
| 115 |                 dumper++;
 | 
|---|
| 116 |         }
 | 
|---|
| 117 | }
 | 
|---|
| 118 | 
 | 
|---|
| 119 | /** Prints single line of USB descriptor dump.
 | 
|---|
| 120 |  * @warning This macro abuses heavily the naming conventions used
 | 
|---|
| 121 |  * by all dumping functions (i.e. names for output file stream (@c output) and
 | 
|---|
| 122 |  * line prefix and suffix (@c line_prefix and @c line_suffix respectively))-
 | 
|---|
| 123 |  *
 | 
|---|
| 124 |  * @param fmt Formatting string.
 | 
|---|
| 125 |  */
 | 
|---|
| 126 | #define PRINTLINE(fmt, ...) \
 | 
|---|
| 127 |         fprintf(output, "%s" fmt "%s", \
 | 
|---|
| 128 |             line_prefix ? line_prefix : " - ", \
 | 
|---|
| 129 |             __VA_ARGS__, \
 | 
|---|
| 130 |             line_suffix ? line_suffix : "\n")
 | 
|---|
| 131 | 
 | 
|---|
| 132 | #define BCD_INT(a) (((unsigned int)(a)) / 256)
 | 
|---|
| 133 | #define BCD_FRAC(a) (((unsigned int)(a)) % 256)
 | 
|---|
| 134 | 
 | 
|---|
| 135 | #define BCD_FMT "%x.%x"
 | 
|---|
| 136 | #define BCD_ARGS(a) BCD_INT((a)), BCD_FRAC((a))
 | 
|---|
| 137 | 
 | 
|---|
| 138 | static void usb_dump_descriptor_device(FILE *output,
 | 
|---|
| 139 |     const char *line_prefix, const char *line_suffix,
 | 
|---|
| 140 |     const uint8_t *descriptor, size_t descriptor_length)
 | 
|---|
| 141 | {
 | 
|---|
| 142 |         usb_standard_device_descriptor_t *d =
 | 
|---|
| 143 |             (usb_standard_device_descriptor_t *) descriptor;
 | 
|---|
| 144 |         if (descriptor_length < sizeof(*d)) {
 | 
|---|
| 145 |                 return;
 | 
|---|
| 146 |         }
 | 
|---|
| 147 | 
 | 
|---|
| 148 |         PRINTLINE("bLength = %d", d->length);
 | 
|---|
| 149 |         PRINTLINE("bDescriptorType = 0x%02x", d->descriptor_type);
 | 
|---|
| 150 |         PRINTLINE("bcdUSB = %d (" BCD_FMT ")", d->usb_spec_version,
 | 
|---|
| 151 |             BCD_ARGS(d->usb_spec_version));
 | 
|---|
| 152 |         PRINTLINE("bDeviceClass = 0x%02x", d->device_class);
 | 
|---|
| 153 |         PRINTLINE("bDeviceSubClass = 0x%02x", d->device_subclass);
 | 
|---|
| 154 |         PRINTLINE("bDeviceProtocol = 0x%02x", d->device_protocol);
 | 
|---|
| 155 |         PRINTLINE("bMaxPacketSize0 = %d", d->max_packet_size);
 | 
|---|
| 156 |         PRINTLINE("idVendor = 0x%04x", d->vendor_id);
 | 
|---|
| 157 |         PRINTLINE("idProduct = 0x%04x", d->product_id);
 | 
|---|
| 158 |         PRINTLINE("bcdDevice = %d", d->device_version);
 | 
|---|
| 159 |         PRINTLINE("iManufacturer = %d", d->str_manufacturer);
 | 
|---|
| 160 |         PRINTLINE("iProduct = %d", d->str_product);
 | 
|---|
| 161 |         PRINTLINE("iSerialNumber = %d", d->str_serial_number);
 | 
|---|
| 162 |         PRINTLINE("bNumConfigurations = %d", d->configuration_count);
 | 
|---|
| 163 | }
 | 
|---|
| 164 | 
 | 
|---|
| 165 | static void usb_dump_descriptor_configuration(FILE *output,
 | 
|---|
| 166 |     const char *line_prefix, const char *line_suffix,
 | 
|---|
| 167 |     const uint8_t *descriptor, size_t descriptor_length)
 | 
|---|
| 168 | {
 | 
|---|
| 169 |         usb_standard_configuration_descriptor_t *d =
 | 
|---|
| 170 |             (usb_standard_configuration_descriptor_t *) descriptor;
 | 
|---|
| 171 |         if (descriptor_length < sizeof(*d)) {
 | 
|---|
| 172 |                 return;
 | 
|---|
| 173 |         }
 | 
|---|
| 174 | 
 | 
|---|
| 175 |         bool self_powered = d->attributes & 64;
 | 
|---|
| 176 |         bool remote_wakeup = d->attributes & 32;
 | 
|---|
| 177 | 
 | 
|---|
| 178 |         PRINTLINE("bLength = %d", d->length);
 | 
|---|
| 179 |         PRINTLINE("bDescriptorType = 0x%02x", d->descriptor_type);
 | 
|---|
| 180 |         PRINTLINE("wTotalLength = %d", d->total_length);
 | 
|---|
| 181 |         PRINTLINE("bNumInterfaces = %d", d->interface_count);
 | 
|---|
| 182 |         PRINTLINE("bConfigurationValue = %d", d->configuration_number);
 | 
|---|
| 183 |         PRINTLINE("iConfiguration = %d", d->str_configuration);
 | 
|---|
| 184 |         PRINTLINE("bmAttributes = %d [%s%s%s]", d->attributes,
 | 
|---|
| 185 |             self_powered ? "self-powered" : "",
 | 
|---|
| 186 |             (self_powered & remote_wakeup) ? ", " : "",
 | 
|---|
| 187 |             remote_wakeup ? "remote-wakeup" : "");
 | 
|---|
| 188 |         PRINTLINE("MaxPower = %d (%dmA)", d->max_power,
 | 
|---|
| 189 |             2 * d->max_power);
 | 
|---|
| 190 | }
 | 
|---|
| 191 | 
 | 
|---|
| 192 | static void usb_dump_descriptor_interface(FILE *output,
 | 
|---|
| 193 |     const char *line_prefix, const char *line_suffix,
 | 
|---|
| 194 |     const uint8_t *descriptor, size_t descriptor_length)
 | 
|---|
| 195 | {
 | 
|---|
| 196 |         usb_standard_interface_descriptor_t *d =
 | 
|---|
| 197 |             (usb_standard_interface_descriptor_t *) descriptor;
 | 
|---|
| 198 |         if (descriptor_length < sizeof(*d)) {
 | 
|---|
| 199 |                 return;
 | 
|---|
| 200 |         }
 | 
|---|
| 201 | 
 | 
|---|
| 202 |         PRINTLINE("bLength = %d", d->length);
 | 
|---|
| 203 |         PRINTLINE("bDescriptorType = 0x%02x", d->descriptor_type);
 | 
|---|
| 204 |         PRINTLINE("bInterfaceNumber = %d", d->interface_number);
 | 
|---|
| 205 |         PRINTLINE("bAlternateSetting = %d", d->alternate_setting);
 | 
|---|
| 206 |         PRINTLINE("bNumEndpoints = %d", d->endpoint_count);
 | 
|---|
| 207 |         PRINTLINE("bInterfaceClass = %s", d->interface_class == 0 ?
 | 
|---|
| 208 |             "reserved (0)" : usb_str_class(d->interface_class));
 | 
|---|
| 209 |         PRINTLINE("bInterfaceSubClass = %d", d->interface_subclass);
 | 
|---|
| 210 |         PRINTLINE("bInterfaceProtocol = %d", d->interface_protocol);
 | 
|---|
| 211 |         PRINTLINE("iInterface = %d", d->str_interface);
 | 
|---|
| 212 | }
 | 
|---|
| 213 | 
 | 
|---|
| 214 | static void usb_dump_descriptor_string(FILE *output,
 | 
|---|
| 215 |     const char *line_prefix, const char *line_suffix,
 | 
|---|
| 216 |     const uint8_t *descriptor, size_t descriptor_length)
 | 
|---|
| 217 | {
 | 
|---|
| 218 | }
 | 
|---|
| 219 | 
 | 
|---|
| 220 | static void usb_dump_descriptor_endpoint(FILE *output,
 | 
|---|
| 221 |     const char *line_prefix, const char *line_suffix,
 | 
|---|
| 222 |     const uint8_t *descriptor, size_t descriptor_length)
 | 
|---|
| 223 | {
 | 
|---|
| 224 |         usb_standard_endpoint_descriptor_t *d =
 | 
|---|
| 225 |             (usb_standard_endpoint_descriptor_t *) descriptor;
 | 
|---|
| 226 |         if (descriptor_length < sizeof(*d)) {
 | 
|---|
| 227 |                 return;
 | 
|---|
| 228 |         }
 | 
|---|
| 229 | 
 | 
|---|
| 230 |         int endpoint = d->endpoint_address & 15;
 | 
|---|
| 231 |         usb_direction_t direction = d->endpoint_address & 128 ?
 | 
|---|
| 232 |             USB_DIRECTION_IN : USB_DIRECTION_OUT;
 | 
|---|
| 233 |         usb_transfer_type_t transfer_type = d->attributes & 3;
 | 
|---|
| 234 | 
 | 
|---|
| 235 |         PRINTLINE("bLength = %d", d->length);
 | 
|---|
| 236 |         PRINTLINE("bDescriptorType = 0x%02X", d->descriptor_type);
 | 
|---|
| 237 |         PRINTLINE("bEndpointAddress = 0x%02X [%d, %s]",
 | 
|---|
| 238 |             d->endpoint_address, endpoint,
 | 
|---|
| 239 |             direction == USB_DIRECTION_IN ? "in" : "out");
 | 
|---|
| 240 |         PRINTLINE("bmAttributes = %d [%s]", d->attributes,
 | 
|---|
| 241 |             usb_str_transfer_type(transfer_type));
 | 
|---|
| 242 |         PRINTLINE("wMaxPacketSize = %d", d->max_packet_size);
 | 
|---|
| 243 |         PRINTLINE("bInterval = %dms", d->poll_interval);
 | 
|---|
| 244 | }
 | 
|---|
| 245 | 
 | 
|---|
| 246 | static void usb_dump_descriptor_superspeed_endpoint_companion(FILE *output,
 | 
|---|
| 247 |     const char *line_prefix, const char *line_suffix,
 | 
|---|
| 248 |     const uint8_t *descriptor, size_t descriptor_length)
 | 
|---|
| 249 | {
 | 
|---|
| 250 |         usb_superspeed_endpoint_companion_descriptor_t *d =
 | 
|---|
| 251 |             (usb_superspeed_endpoint_companion_descriptor_t *) descriptor;
 | 
|---|
| 252 |         if (descriptor_length < sizeof(*d)) {
 | 
|---|
| 253 |                 return;
 | 
|---|
| 254 |         }
 | 
|---|
| 255 | 
 | 
|---|
| 256 |         PRINTLINE("bLength = %u", d->length);
 | 
|---|
| 257 |         PRINTLINE("bDescriptorType = 0x%02X", d->descriptor_type);
 | 
|---|
| 258 |         PRINTLINE("bMaxBurst = %u", d->max_burst);
 | 
|---|
| 259 |         PRINTLINE("bmAttributes = %d", d->attributes);
 | 
|---|
| 260 |         PRINTLINE("wBytesPerInterval = %u", d->bytes_per_interval);
 | 
|---|
| 261 | }
 | 
|---|
| 262 | 
 | 
|---|
| 263 | static void usb_dump_descriptor_hid(FILE *output,
 | 
|---|
| 264 |     const char *line_prefix, const char *line_suffix,
 | 
|---|
| 265 |     const uint8_t *descriptor, size_t descriptor_length)
 | 
|---|
| 266 | {
 | 
|---|
| 267 |         usb_standard_hid_descriptor_t *d =
 | 
|---|
| 268 |             (usb_standard_hid_descriptor_t *) descriptor;
 | 
|---|
| 269 |         if (descriptor_length < sizeof(*d)) {
 | 
|---|
| 270 |                 return;
 | 
|---|
| 271 |         }
 | 
|---|
| 272 | 
 | 
|---|
| 273 |         PRINTLINE("bLength = %d", d->length);
 | 
|---|
| 274 |         PRINTLINE("bDescriptorType = 0x%02x", d->descriptor_type);
 | 
|---|
| 275 |         PRINTLINE("bcdHID = %d (" BCD_FMT ")", d->spec_release,
 | 
|---|
| 276 |             BCD_ARGS(d->spec_release));
 | 
|---|
| 277 |         PRINTLINE("bCountryCode = %d", d->country_code);
 | 
|---|
| 278 |         PRINTLINE("bNumDescriptors = %d", d->class_desc_count);
 | 
|---|
| 279 |         PRINTLINE("bDescriptorType = %d", d->report_desc_info.type);
 | 
|---|
| 280 |         PRINTLINE("wDescriptorLength = %d", d->report_desc_info.length);
 | 
|---|
| 281 | 
 | 
|---|
| 282 |         /* Print info about report descriptors. */
 | 
|---|
| 283 |         size_t i;
 | 
|---|
| 284 |         size_t count = (descriptor_length - sizeof(*d)) /
 | 
|---|
| 285 |             sizeof(usb_standard_hid_class_descriptor_info_t);
 | 
|---|
| 286 |         usb_standard_hid_class_descriptor_info_t *d2 =
 | 
|---|
| 287 |             (usb_standard_hid_class_descriptor_info_t *)
 | 
|---|
| 288 |             (descriptor + sizeof(*d));
 | 
|---|
| 289 |         for (i = 0; i < count; i++, d2++) {
 | 
|---|
| 290 |                 PRINTLINE("bDescriptorType = %d", d2->type);
 | 
|---|
| 291 |                 PRINTLINE("wDescriptorLength = %d", d2->length);
 | 
|---|
| 292 |         }
 | 
|---|
| 293 | }
 | 
|---|
| 294 | 
 | 
|---|
| 295 | static void usb_dump_descriptor_hub(FILE *output,
 | 
|---|
| 296 |     const char *line_prefix, const char *line_suffix,
 | 
|---|
| 297 |     const uint8_t *descriptor, size_t descriptor_length)
 | 
|---|
| 298 | {
 | 
|---|
| 299 |         usb_hub_descriptor_header_t *d =
 | 
|---|
| 300 |             (usb_hub_descriptor_header_t *) descriptor;
 | 
|---|
| 301 |         if (descriptor_length < sizeof(d))
 | 
|---|
| 302 |                 return;
 | 
|---|
| 303 | 
 | 
|---|
| 304 |         PRINTLINE("bDescLength: = %d", d->length);
 | 
|---|
| 305 |         PRINTLINE("bDescriptorType = 0x%02x", d->descriptor_type);
 | 
|---|
| 306 |         PRINTLINE("bNbrPorts = %d", d->port_count);
 | 
|---|
| 307 |         PRINTLINE("bHubCharacteristics = 0x%02x%02x (%s;%s%s)",
 | 
|---|
| 308 |             d->characteristics_reserved, d->characteristics,
 | 
|---|
| 309 |             (d->characteristics & HUB_CHAR_NO_POWER_SWITCH_FLAG) ?
 | 
|---|
| 310 |             "No Power Switching" :
 | 
|---|
| 311 |             ((d->characteristics & HUB_CHAR_POWER_PER_PORT_FLAG) ?
 | 
|---|
| 312 |             "Per-Port Switching" : "Ganged Power Switching"),
 | 
|---|
| 313 |             (d->characteristics & HUB_CHAR_COMPOUND_DEVICE) ?
 | 
|---|
| 314 |             "Compound Device;" : "",
 | 
|---|
| 315 |             (d->characteristics & HUB_CHAR_NO_OC_FLAG) ?
 | 
|---|
| 316 |             "No OC Protection" :
 | 
|---|
| 317 |             ((d->characteristics & HUB_CHAR_OC_PER_PORT_FLAG) ?
 | 
|---|
| 318 |             "Individual Port OC Protection" :
 | 
|---|
| 319 |             "Global OC Protection"));
 | 
|---|
| 320 |         PRINTLINE("bPwrOn2PwrGood = %d (%d ms)",
 | 
|---|
| 321 |             d->power_good_time, d->power_good_time * 2);
 | 
|---|
| 322 |         PRINTLINE("bHubContrCurrent = %d (%d mA)",
 | 
|---|
| 323 |             d->max_current, d->max_current);
 | 
|---|
| 324 |         const size_t port_bytes = (descriptor_length - sizeof(*d)) / 2;
 | 
|---|
| 325 |         const uint8_t *removable_mask = descriptor + sizeof(*d);
 | 
|---|
| 326 |         const uint8_t *powered_mask = descriptor + sizeof(*d) + port_bytes;
 | 
|---|
| 327 | 
 | 
|---|
| 328 |         if (port_bytes == 0 ||
 | 
|---|
| 329 |             port_bytes > (((d->port_count / (unsigned)8) + 1) * 2)) {
 | 
|---|
| 330 |                 PRINTLINE("::CORRUPTED DESCRIPTOR:: (%zu bytes remain)",
 | 
|---|
| 331 |                     port_bytes * 2);
 | 
|---|
| 332 |         }
 | 
|---|
| 333 | 
 | 
|---|
| 334 |         fprintf(output, "%sDeviceRemovable = 0x",
 | 
|---|
| 335 |             line_prefix ? line_prefix : " - ");
 | 
|---|
| 336 |         for (unsigned i = port_bytes; i > 0; --i)
 | 
|---|
| 337 |                 fprintf(output, "%02x", removable_mask[i - 1]);
 | 
|---|
| 338 |         fprintf(output, " (0b1 - Device non-removable)%s",
 | 
|---|
| 339 |             line_suffix ? line_suffix : "\n");
 | 
|---|
| 340 | 
 | 
|---|
| 341 |         fprintf(output, "%sPortPwrCtrlMask = 0x",
 | 
|---|
| 342 |             line_prefix ? line_prefix : " - ");
 | 
|---|
| 343 |         for (unsigned i = port_bytes; i > 0; --i)
 | 
|---|
| 344 |                 fprintf(output, "%02x", powered_mask[i - 1]);
 | 
|---|
| 345 |         fprintf(output, " (Legacy - All should be 0b1)%s",
 | 
|---|
| 346 |             line_suffix ? line_suffix : "\n");
 | 
|---|
| 347 | }
 | 
|---|
| 348 | 
 | 
|---|
| 349 | static void usb_dump_descriptor_generic(FILE *output,
 | 
|---|
| 350 |     const char *line_prefix, const char *line_suffix,
 | 
|---|
| 351 |     const uint8_t *descriptor, size_t descriptor_length)
 | 
|---|
| 352 | {
 | 
|---|
| 353 |         /* TODO */
 | 
|---|
| 354 | }
 | 
|---|
| 355 | 
 | 
|---|
| 356 | /**
 | 
|---|
| 357 |  * @}
 | 
|---|
| 358 |  */
 | 
|---|