USB Descriptor and Request Parser

I wrote a tool to parse some USB data. You put in the data packet into the tool, and it’ll translate it into something you can understand.

CLICK HERE TO USE THE TOOL

The reason why I made this tool is because some USB traffic sniffers do not perform parsing on USB packets (especially the freeware ones), but the binary data can still be obtained. This tool will traverse through the binary data, and translate it into something human-readable, using the official documents from USB.org. The three types of data this tool can handle are “USB Standard Descriptors”, “USB Standard Requests”, and “USB HID Report Descriptors”.

The “parse USB HID report descriptor” function is the reverse of the (horrible) “HID Descriptor Tool” provided by USB.org, and the display format is very similar.

Because USB devices are generally embedded devices, this tool is designed with the C programming language in mind. The output can be imported as an array initializer.

I was frustrated when I couldn’t find a tool for this, so I wrote it in hopes that it will become useful for everybody looking for such a tool.

30 thoughts on “USB Descriptor and Request Parser

  1. Ferenc

    Thank you for this. Great tool! I am using it to cross-check my own HID report descriptor parser.

    I found a bug. In pCollection(s,v) the lookup table begins with “Undefined” and it should begin with “Physical” since that is the corresponding collection with ID = 0.

    Cheers!

    Reply
  2. anonymous

    You might want to consider looking at UsbPcap , the project produced some dissectors that were included in the recent version of wireshark. While they do need improvement, and perhaps you might consider adding more functionality to them, they are quite nice for free, and since pcap format is very accessible, hopefully more analyzers will start dumping in that format natively.

    Reply
    1. Admin Post author

      I also wish Total Phase Data Center can export as pcap.

      I use Wireshark very often for USB and Bluetooth but it’s a pain to generate *.pcap files since they are binary, I have to maintain several custom conversion scripts to do so. Sometimes I just want to do stuff in text/ASCII. I wish there was a XML or JSON format for pcap. (maybe there is and I haven’t found it)

      I use my own parser mainly to generate sane C comments so it’s readable once I put it into C code.

      Reply
  3. Aki

    Hi,

    It is a great tool to decipher USB descriptors. Thank you!

    I think possible_errors is incremented in pDeviceClass function regardless of whether the class is valid or not. Can you review your code?

    Thanks!
    Aki-

    function pDeviceClass(c)
    {
    var tbl = [
    “Use class information in the Interface Descriptors”,

    “Vendor Specific”,
    ];

    var str = “”;
    if (c < tbl.length) {
    str = tbl[c];
    }
    if (str != "") {
    str = "(" + str + ")";
    }
    possible_errors++; <<<– increment the error regardlessly
    return str;
    }

    Reply
    1. Aki

      Hi,

      I tweaked the javascript locally so that possible_error is not incremented if the class is valid.
      Now, the “guess” feature for a sample standard request descriptor:
      81 06 00 22 00 00 D4 00
      is no longer working correctly.
      The script now thinks it is a standard Descriptor, not a standard Request Descriptor.
      What I found was that the possible_error from standard Descriptor parser *was* 1 because of the above coding and after changing the code, both standard Descriptor parser and standard Request Descriptor parser produce 0 (means both legal) and the script thinks the input is a standard Descriptor.

      Is there any way to penalize when the “81 06 00 22 00 00 D4 00” is being parsed as a standard Descriptor???

      The decoded output (as a standard Descriptor) is bogus but seems legal.
      Any suggestions would be greatly appreciated.
      Aki-

      Reply
  4. Aki

    Hi,

    Your parser seems covers a wide range of descriptor types.
    But you didn’t cover a case for STRING descriptors.
    I’m wondering why you omitted the case as adding the case requires only minuscule efforts.
    Your comments on this would be greatly appreciated.
    Aki-

    Reply
  5. Aki

    Hi,

    Me, again.
    I think I found a bug in your code.
    The bcd decoding was not correctly implemented.
    The following bcdUSB case should be displayed as 1.10 but 1.16 was shown instead.

    0x10, 0x01, // bcdUSB 1.16

    Aki-

    Reply
  6. Jordan Klassen

    Hey, thanks for the tool; it would be great if you could host the source on github or something. Also, I’d like to put this into a package for nodejs on npm. I sent you an email with a bit more info.

    Reply
  7. Nick Rishel

    Heads up there there’s a very minor bug in your parser, the comments generated for logical and physical minimums don’t take into account negative numbers, e.g.
    0x16, 0x00, 0x80, // Logical Minimum (32768)
    … should be…
    0x16, 0x00, 0x80, // Logical Minimum (-32768)

    Reply
  8. beantowel

    Hi, Zhao, thanks for your tool. I’m now trying to develop a Force Feed Back joystick, and i almost did the same thing you do, i hope the PID (Physical Interface Device) usages can be add to the tool. I extract the definitions from the document “Device Class Definition for Physical Interface Devices (PID) Version 1.0” and that may be helpful to you:
    https://github.com/beantowel/FFBMK1/blob/master/HID%20Descriptor%20tool/HID_PID_Descriptor_Definitions.py
    Furthermore, i wonder if there is any tool could help the debugging in MCU programming, i mean, can we just test our code in some sort of simulation environment on PC and get debug imformation directly?

    Reply
  9. Rena

    With this report descriptor:

    05 01 09 04 A1 01 09 09 A1 00 05 01 09 30 09 31 09 32 09 33 09 34 09 35 09 40 09 41 09 42 09 46 95 0A 75 10 82 82 00 16 00 80 26 FF 7F C0 C0

    Your script says the logical minimum should be -32768 as I’d expect, but lsusb and Linux joystick driver seem to interpret it as +32768. Any idea?

    Reply
  10. Ben Lunt

    Hi everyone,

    I use this tool when I need a quick HID dump. I even have this tool mentioned in my book (http://www.fysnet.net/the_universal_serial_bus.htm). Thank you to all whom have contributed to this tool.

    However, I believe there is a bug in the code for HID parsing. If the code finds a “Report ID” field, it successfully parses and displays the ID, but incorrectly increments the position and skips the next byte. For example, here is the output for the first few bytes from my parser:
    05 01 Usage Page (Generic Desktop)
    09 02 Usage (Mouse)
    A1 01 Collection (Application)
    85 03 Report ID: 3
    09 01 Usage (Pointer)
    A1 00 Collection (Physical)
    05 09 Usage Page (Button)

    Here is the same sequence in the parser linked on this page:
    0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
    0x09, 0x02, // Usage (Mouse)
    0xA1, 0x01, // Collection (Application)
    0x85, 0x03, // Report ID (3)
    0x01, 0xA1, // Unknown (bTag: 0x00, bType: 0x00)
    0x00, // Unknown (bTag: 0x00, bType: 0x00)
    0x05, 0x09, // Usage Page (Button)

    Notice that the 09 is skipped. I have not looked through the code to tell you where it is, but you might wish to look into this.

    I can provide a complete HID Descriptor is needed.

    Thank you,
    Ben

    Reply
    1. Ben Lunt

      I had a look at your code. It isn’t the Report ID as I thought. I used the following data:
      05 01 09 02 A1 01 85 03-09 01 A1 00 05 09 19 01
      Notice the negative sign between the 03 and the 09. This is the standard DOS debug output. Your code thinks the 09 is negative? Is this an error in the code or does it allow negative inputs? Is this something you might look for?

      Thanks,
      Ben

      Reply
  11. dfrankland

    This tool is much easier for me to read than others I have seen! Would you mind providing a license for this code? I am planning to build some tools on top of it if that is okay.

    Reply
  12. Shoreus

    Hey,
    I wrote an own HID Report Deskriptor for my project and used your tool to debug and produce good looking Code.

    Maybe you want to update the parser for touchscreens. There are some “new” Usages. They are Requested on this side: https://www.usb.org/sites/default/files/hutrr34.pdf

    Especially these three worked for me and are Mandatory for multi-touch-devices.

    0x51 Contact identifier
    0x54 Contact count
    0x55 Contact count maximum

    Thank You for your nice Website
    Shoreus

    Reply
  13. Huan

    Hi Frank, I used this tool to help resolve a lot of difficulty I was having creating a custom HID device. Thank you so much!

    Reply
  14. Shea

    Thank you for your tool; it is very helpful.

    The following bytes:
    0x27, 0xFF, 0xFF, 0x00, 0x00

    are parsed as:
    0x27, 0xFF, 0xFF, 0x00, 0x00, // Logical Maximum (65534)

    I believe this is incorrect and should be interpreted as:
    // Logical Maximum (65535)

    Agreed?

    Thanks again for your work.

    Reply
    1. Nick Briggs

      I had the same observation — I see no reason that the Logical Maximum should be interpreted as 65534 rather than 65535.

      Reply
  15. Jörg Wartenberg

    Hello Frank,

    thanks, for providing this tool.

    I noticed a small bug. Report-IDs seems to be handled as signed char, but they are unsigned chars. I got this:
    0x85, 0xF3, // Report ID (-13)
    instead
    of
    0x85, 0xF3, // Report ID (243)

    KR Jörg

    Reply
  16. Tim Roberts

    Shea’s comment about the logical maximum is correct. The problem is in pItemVal, which mishandles 4-byte values because Javascript ints are only 4 bytes long. Thus, (1 << (8*s)) ends up producing -1. The solution is to change the "else" to:
    else if( s < 4 ) {
    and move the "return" line out of the else. For 4-byte values, no adjustment is necessary.

    Reply
  17. Nick Briggs

    This was a very useful tool, thanks! I’m debugging bogus report descriptors from a USB HID uninterruptible power supply — sending descriptors that indicate a range of 0..-1 such as:

    0x05, 0x84, // Usage Page (Power Pages)
    0x09, 0x12, // Usage (0x12)
    0xA1, 0x00, // Collection (Physical)
    0x95, 0x01, // Report Count (1)
    0x85, 0x20, // Report ID (32)
    0x05, 0x85, // Usage Page (Power Pages)
    0x09, 0x85, // Usage (0x85)
    0x75, 0x10, // Report Size (16)
    0x15, 0x00, // Logical Minimum (0)
    0x27, 0xFF, 0xFF, 0x00, 0x00, // Logical Maximum (65534)
    0x81, 0x22, // Input (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position)
    0x09, 0x85, // Usage (0x85)
    0xB1, 0xA2, // Feature (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position,Volatile)
    0x85, 0x21, // Report ID (33)
    0x05, 0x84, // Usage Page (Power Pages)
    0x09, 0x58, // Usage (0x58)
    0x75, 0x08, // Report Size (8)
    0x25, 0x06, // Logical Maximum (6)
    0x81, 0xA2, // Input (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position)
    0x09, 0x58, // Usage (0x58)
    0xB1, 0xA2, // Feature (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position,Volatile)
    0x85, 0x22, // Report ID (34)
    0x05, 0x85, // Usage Page (Power Pages)
    0x09, 0x66, // Usage (0x66)
    0x25, 0xFF, // Logical Maximum (-1)
    0xB1, 0xA2, // Feature (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position,Volatile)
    0x85, 0x23, // Report ID (35)
    0x09, 0x68, // Usage (0x68)
    0x75, 0x10, // Report Size (16)
    0x27, 0xFF, 0xFF, 0x00, 0x00, // Logical Maximum (65534)
    0x66, 0x01, 0x10, // Unit (System: SI Linear, Time: Seconds)
    0xB1, 0xA2, // Feature (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position,Volatile)
    0x85, 0x24, // Report ID (36)
    0x09, 0x2A, // Usage (0x2A)
    0xB1, 0xA2, // Feature (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position,Volatile)
    0x85, 0x25, // Report ID (37)
    0x05, 0x84, // Usage Page (Power Pages)
    0x09, 0x40, // Usage (0x40)
    0x67, 0x21, 0xD1, 0xF0, 0x00, // Unit (System: SI Linear, Length: Radians, Mass: Gram)
    0x55, 0x05, // Unit Exponent (5)
    0xB1, 0x22, // Feature (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position,Non-volatile)
    0x85, 0x26, // Report ID (38)
    0x09, 0x30, // Usage (0x30)
    0xB1, 0xA2, // Feature (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position,Volatile)
    0xC0, // End Collection

    (where I also noticed the 65534 vs 65535 bug in your decoder that others have mentioned previously)

    Reply
  18. Álvaro

    Hi!

    I noticed a bug in the units handling. When the report contains 0x65, 0x14 it reports “Unit (System: English Rotation, Length: Centimeter)” instad of “Unit (System: English Rotation, Length: Degrees)” (or perhaps Angle: Degrees). This is because in the pUnit() function you use “if (nib == […])” instead of “if (sysNib ==[…])”. You already created the variable, but then forgot to use it and used the nib variable instead. It is therefore using the current value of nib (which is the exponent) instead of the unit system.

    Reply
  19. Meme

    HID Usage Tables are updated recently (version 1.22, year 2021).
    Would you please release your code (though it is available on the html source) under an open source license?

    Reply
  20. Markus

    Hi, I found a bug in the decoding of bcdDevice. In your example standard descriptor, 1.00 is decoded as 2.00.

    0x00, 0x01, // bcdDevice 2.00

    The fix is to delete the line

    bcdDevice += inVals[I];

    You likely forgot to remove this line when copying the fix for bcdUSB (where 1.10 was shown as 1.16).

    Reply

Leave a Reply to Admin Cancel reply

Your email address will not be published. Required fields are marked *