USB Descriptor Viewer Workflow for HID and CDC
Use a USB descriptor viewer workflow to validate raw device, configuration, interface, endpoint, HID, CDC, string, BOS, and class-request evidence.
A USB descriptor viewer is useful only when it preserves the exact bytes the host received and connects them to enumeration, class requests, and endpoint traffic. A pretty tree can reveal obvious fields, but it can also hide a malformed length, stale descriptor revision, missing interface, or request that returned different bytes on the failing host.
Direct answer: capture the device from connection, inspect the decoded descriptor hierarchy, and validate each decoded field against raw bytes and the request that retrieved it. Check lengths and boundaries before interpreting class meaning. Then compare HID report layouts or CDC functional relationships with the class requests and endpoint transfers that follow configuration.
Bus Scope is designed to keep descriptor context, raw packet bytes, transfer status, filters, and saved .bscope sessions together. It can show USB evidence. It cannot prove how an operating system ranked an INF or how an application transformed a valid HID or serial event, so pair the trace with host logs when the bus contract is already correct.
What a USB descriptor viewer must show
The useful view is a hierarchy tied to original capture evidence:
| Descriptor layer | Fields that anchor diagnosis | Failure it can expose |
|---|---|---|
| Device | USB version, class tuple, EP0 size, VID, PID, bcdDevice, string indexes |
wrong identity or control assumptions |
| Configuration | total length, interface count, attributes, power | truncated or malformed tree |
| IAD | first interface, count, function class | incorrect composite grouping |
| Interface | number, alternate setting, endpoint count, class tuple | wrong driver identity |
| Class-specific | HID, CDC, audio, video, DFU fields | broken class contract |
| Endpoint | address, direction, type, packet size, interval | unusable transfer path |
| String | language ID, index, UTF-16 data | identity or retrieval inconsistency |
| BOS/capability | capability type, platform UUID, nested length | discovery or WinUSB/WebUSB mismatch |
The viewer should let an engineer jump from a decoded field to raw bytes and then to the control transfer that returned the block. If those three views disagree, raw capture bytes and the request context are the diagnostic ground truth.
Start with the control request, not the label
Most descriptors are retrieved with a standard GET_DESCRIPTOR control request. Decode:
- direction, request type, and recipient from
bmRequestType; GET_DESCRIPTORinbRequest;- descriptor type and index in
wValue; - language ID or interface context in
wIndex, depending on descriptor type; - maximum requested length in
wLength.
The response may legitimately be shorter than wLength. During enumeration, a host can request a small prefix first, learn a length, and request the full descriptor later. Do not flag every short response as truncation. Instead ask:
- How many bytes did the device declare?
- How many did the host request?
- How many were returned?
- Did the data stage complete normally?
- Did the host retry with a different length?
That sequence separates normal enumeration probes from firmware that ends a descriptor block early or stalls a valid request.
Validate descriptor boundaries before values
Every standard descriptor begins with bLength and bDescriptorType. For a configuration block, walk from offset zero by each descriptor's bLength:
next offset = current offset + bLength
The walk should remain inside the returned data and end at the declared configuration wTotalLength. Check that:
- no
bLengthis zero; - every descriptor fits in the captured block;
- known descriptor types have at least their required base length;
- the final offset matches the expected boundary;
- interface and endpoint counts agree with the parsed structure.
A wrong bLength can make every later field appear corrupt even if firmware source arrays look correct. A wrong wTotalLength can omit trailing interfaces or cause a host to request bytes that firmware does not provide. Fix the first boundary error before debating class-specific fields.
Read the device descriptor as identity and capability
The device descriptor establishes the identity used for later driver and cache behavior:
| Field | Diagnostic use |
|---|---|
bcdUSB |
USB specification level claimed by the device |
bDeviceClass/SubClass/Protocol |
device-level class or per-interface delegation |
bMaxPacketSize0 |
default control endpoint packet size |
idVendor / idProduct |
device identity |
bcdDevice |
firmware or device release identity |
| string indexes | references for manufacturer, product, and serial |
bNumConfigurations |
number of configurations advertised |
Record all of them in known-good and failing captures. A changed PID, serial, or release number may create a different OS device instance. A changed class tuple can alter compatible IDs and binding. Those are meaningful experimental differences, not metadata noise.
If the host cannot complete the device descriptor stage, use the broader USB enumeration failure guide before debugging HID or CDC.
Inspect configuration, interfaces, and alternate settings as one graph
An interface number identifies a function path; alternate settings provide mutually selectable endpoint layouts for that interface. bNumInterfaces counts interface numbers, not every alternate descriptor.
Build a map:
| Interface | Alternate | Class | Endpoints | Intended function |
|---|---|---|---|---|
| 0 | 0 | HID | interrupt IN 0x81 |
controls |
| 1 | 0 | CDC control | interrupt IN 0x82 |
serial notifications |
| 2 | 0 | CDC data | bulk OUT 0x03, bulk IN 0x83 |
serial data |
| 3 | 0 | vendor | bulk OUT 0x04, bulk IN 0x84 |
diagnostics |
Then verify endpoint addresses, directions, transfer types, maximum packet sizes, and intervals. Check class-specific descriptors in the correct interface context. If the same endpoint address appears in simultaneously active interfaces, or an interface declares two endpoints but only one descriptor exists, the host's later behavior may be a consequence of the malformed map.
For streaming devices with multiple alternate settings, continue with the USB alternate-setting bandwidth guide.
HID descriptor viewing requires the report descriptor too
The HID descriptor inside the configuration tree identifies HID class descriptors and advertises the report-descriptor length. The report descriptor itself is retrieved separately for the target interface. Validate:
- the HID descriptor is located after the intended HID interface;
- the advertised report length equals the complete returned report descriptor;
wIndexaddresses the correct interface;- Report IDs, sizes, counts, usages, and logical ranges form valid layouts;
- actual interrupt and control reports match those layouts.
A generic tree that stops at "HID descriptor: report length 73" has not finished the job. The report item stream and actual report bytes are the behavioral contract. The dedicated HID report descriptor guide shows how to build a bit-offset table.
CDC viewing means checking relationships
CDC ACM usually spans a communication interface and a data interface. Relevant descriptors may include:
- CDC Header Functional Descriptor;
- Call Management Functional Descriptor;
- Abstract Control Management Functional Descriptor;
- Union Functional Descriptor;
- notification interrupt endpoint;
- data-interface bulk IN and OUT endpoints.
The union descriptor's master and subordinate interface numbers must refer to the intended interfaces. If an IAD is present, its range should agree with the function layout. After descriptors, inspect class requests such as SET_LINE_CODING and SET_CONTROL_LINE_STATE, then data transfers.
Use a decision table:
| Observation | Next likely layer |
|---|---|
| CDC child never appears | descriptor grouping or driver identity |
| child appears, class request stalls | firmware request handler or interface routing |
| class setup succeeds, no bulk traffic | application open state, DTR/RTS policy, endpoint queue |
| bulk OUT succeeds, no bulk IN | firmware receive/response path |
| device resets after port open | control-line-triggered reset or power/firmware fault |
The USB serial COM port guide covers resets and bootloader identity changes after the port opens.
Decode endpoint fields in speed context
An endpoint descriptor does not stand alone. Interpret its maximum packet size and interval using the negotiated speed and transfer type. Bulk, interrupt, and isochronous endpoints have different scheduling and retry behavior.
For every endpoint, record:
- endpoint address and direction;
- transfer type from
bmAttributes; - maximum packet size and any speed-specific transaction fields;
bInterval;- owning interface and alternate setting;
- first successful and first failed transfer.
A viewer that lists wMaxPacketSize = 512 without speed and endpoint type omits the context needed for diagnosis. If descriptors look correct but a bulk path times out, use the USB endpoint STALL and timeout guide.
String descriptors can change identity
String descriptor index zero returns supported language IDs. Other indexes are requested in a language context. Validate:
- string index zero completes and lists intended language IDs;
- nonzero indexes are valid for the requested language;
- descriptor length is even and matches returned UTF-16 data;
- serial number is stable when firmware promises a stable identity;
- the host does not receive different values across resets.
A bad string can produce a warning, different device-instance behavior, or confusing support logs even when basic configuration succeeds. The USB string descriptor guide covers that path.
BOS and platform capabilities are nested structures
USB 2.01 and later capability discovery can involve a Binary Object Store (BOS) descriptor containing one or more device-capability descriptors. Validate outer and nested lengths before decoding platform UUIDs or vendor payloads.
For Microsoft OS 2.0 or WebUSB discovery, preserve:
- BOS request and complete response;
- capability type and UUID;
- vendor code;
- descriptor-set length or landing-page index where applicable;
- subsequent vendor request;
- complete returned descriptor set.
The BOS and Microsoft OS descriptor guide explains how those layers affect WinUSB discovery without confusing a bus result with final OS policy.
Compare raw bytes before and after a firmware change
Use the same host, port, cable, and test sequence when possible. Compare:
- exact request order;
- every descriptor response length;
- raw device and configuration blocks;
- HID report descriptor;
- CDC functional descriptors;
- BOS/platform descriptors;
- selected configuration and alternate settings;
- first class request and transfer that differs.
Do not use screenshots as the primary comparison artifact. Save raw sessions so reviewers can verify offsets and decode new fields later. A screenshot can illustrate the conclusion, but it cannot replace the evidence.
A repeatable USB descriptor viewer workflow
- Record host, controller path, device speed, firmware, VID, PID, and serial.
- Start capture before connection or reset.
- Follow endpoint-zero requests in chronological order.
- Validate device-descriptor identity and EP0 size.
- Validate configuration total length and descriptor boundaries.
- Map IADs, interface numbers, alternate settings, and endpoints.
- Decode class-specific descriptors in interface context.
- Retrieve and parse the complete HID report descriptor where applicable.
- Validate CDC functional relationships where applicable.
- Inspect BOS, string, and Microsoft OS descriptors when exposed.
- Follow the first class requests after configuration.
- Compare endpoint traffic with the declared map.
- Save a bounded
.bscopesession and a concise evidence table.
Use Bus Scope platform capture before interpreting an empty trace. USBPcap controller choice and Linux usbmon bus choice must match the physical device path.
QA questions
What should a USB descriptor viewer show besides decoded fields?
It should preserve raw bytes, original control-request context, transfer status, descriptor offsets, hierarchy, interface ownership, and the endpoint traffic that follows. A decoded tree alone is not enough for malformed-length or stale-revision cases.
Why does the host request a descriptor twice with different lengths?
Hosts often request a short prefix to learn a descriptor's size, then request the full block. Compare declared, requested, and returned lengths before calling the first short response a failure.
Is a valid configuration descriptor enough to prove HID or CDC works?
No. HID depends on the separately retrieved report descriptor and matching reports. CDC depends on coherent functional descriptors, class requests, control-line state, and bulk endpoint behavior.
Can the USB descriptor viewer prove Windows chose the wrong INF?
It can prove identity descriptors, interface classes, Microsoft OS descriptor traffic, and USB request results. Windows setup and device logs are still needed to prove INF ranking and binding policy.
Why keep both decoded and raw views?
Decoded fields accelerate review; raw bytes settle length, offset, endianness, and decoder disagreements. A reliable diagnosis uses both and ties them to the exact transfer.
Final diagnosis
A USB descriptor viewer should turn an opaque driver error into a verifiable contract: exact bytes, valid boundaries, coherent class relationships, and matching requests and endpoint traffic. Validate structure first, class meaning second, and host policy last. That order prevents a malformed descriptor from being disguised as a driver problem and prevents a valid bus contract from being blamed for an application-layer failure.
<!-- multilingual-related-reading:start -->Related guides
Continue with the same-language pages below. They cover adjacent stages without changing the canonical owner of this topic:
<!-- multilingual-related-reading:end -->