USB Composite Device Wrong Driver Binding

Diagnose USB composite device driver errors by checking interface descriptors, IAD ranges, class codes, Microsoft OS descriptors, requests, and the first function that fails.

usb composite device, wrong driver binding, usbccgp, code 10, code 43, interface association descriptor, usb diagnostics

A USB composite device can enumerate at the parent level while one child function never appears, binds to the wrong driver, or starts with Windows Code 10 or Code 43. That does not mean the whole device is dead. It usually means the host built a different function map from the descriptors than the firmware team intended, or the selected child driver issued a request that the function could not complete.

Direct answer: capture from physical connection through configuration and the first class-specific request. Map every interface number, alternate setting, endpoint, Interface Association Descriptor (IAD), and class-specific descriptor before looking at the driver error. Then identify the first divergence: an invalid grouping, an incorrect class tuple, a missing functional descriptor, a Microsoft OS descriptor mismatch, a request routed to the wrong interface, or a reset during child-driver startup.

Bus Scope can organize that USB evidence in one saved session. A capture can prove what descriptors and requests crossed the bus. It cannot by itself prove which Windows INF ranking rule won, so combine bus evidence with the operating system's device-instance and driver-install logs when binding policy is the remaining question.

What a composite device asks the host to build

A composite USB device exposes several logical functions behind one address. One configuration might contain:

  • interface 0 for HID controls;
  • interfaces 1 and 2 for a CDC ACM serial function;
  • interface 3 for vendor diagnostics;
  • interfaces 4 and 5 for audio or video streaming.

Each interface has a unique bInterfaceNumber within the configuration. Its class, subclass, protocol, alternate settings, endpoints, and adjacent class-specific descriptors describe the function. On Windows, the USB generic parent driver can create child devices for those functions, after which matching class or vendor drivers bind to the children.

That process creates several distinct failure layers:

Layer Evidence to inspect Typical visible symptom
Parent enumeration device and configuration descriptors entire device is unknown
Function grouping IAD or class-specific union descriptors one multi-interface function is split
Child identity interface class tuple and compatible IDs wrong class driver is selected
Driver startup class or vendor requests child exists but reports Code 10
Endpoint operation bulk, interrupt, or isochronous traffic driver starts but the function does not work
Re-enumeration reset, disconnect, new address, descriptor change child devices vanish and return

Do not collapse all six layers into "wrong driver." The first failed layer determines the next useful test.

Validate the configuration envelope first

Before interpreting an IAD, prove that the host received a coherent configuration descriptor. Check:

  1. wTotalLength covers the complete descriptor sequence.
  2. Every descriptor has a nonzero, plausible bLength.
  3. Walking by bLength lands exactly at the end of the returned block.
  4. bNumInterfaces agrees with the distinct interface numbers, not the number of alternate settings.
  5. Endpoint addresses are unique where simultaneous interfaces require them.
  6. Class-specific descriptors appear beside the interface or endpoint they extend.

A malformed total length can hide the last interface from the host even when the bytes exist in firmware. An off-by-one descriptor length can shift parsing so later interface fields are read at the wrong offsets. In either case, debugging the INF first wastes time because the host never saw the intended function map.

The broader USB descriptor debugging guide explains how to compare the decoded tree with raw bytes.

Check every IAD as a range

An Interface Association Descriptor groups consecutive interfaces into one function. The crucial fields are:

IAD field Diagnostic question
bFirstInterface Does this point to the first interface of the function?
bInterfaceCount Does the range contain every and only interface in that function?
bFunctionClass Does it identify the intended function class?
bFunctionSubClass Is it valid for that class?
bFunctionProtocol Does it match the function contract?
iFunction If nonzero, can the referenced string be read correctly?

The interfaces in an IAD range must be contiguous. An IAD beginning at interface 1 with a count of 2 describes interfaces 1 and 2; it cannot silently include interface 4. The IAD should be located immediately before the interfaces it groups. If a CDC control interface points to a data interface outside the IAD range, the host may create a function boundary that differs from the firmware's routing assumptions.

Some class models also use functional descriptors to relate interfaces. For CDC ACM, inspect the union functional descriptor and compare its master and subordinate interface numbers with the IAD and actual interface descriptors. The three views must tell one consistent story.

Do not apply the IAD device-class tuple blindly

Many simple composites use bDeviceClass, bDeviceSubClass, and bDeviceProtocol equal to zero, with class identity supplied per interface. Devices using IAD-aware composite signaling for Windows commonly use the miscellaneous/common/IAD tuple 0xEF, 0x02, 0x01. Which layout is correct depends on the device-class design and target hosts.

The practical rule is not "always use 0xEF." It is:

  • verify what the relevant class specification expects;
  • verify whether the firmware actually contains IADs;
  • verify that interface-level class fields remain correct;
  • compare the working and failing firmware descriptor bytes;
  • avoid changing the device-level tuple without understanding how it changes host identity.

A device-class change can produce a different compatible ID and therefore a different binding result. It may also create a new device instance in the operating system. Record the descriptor version and device identity so a successful change is reproducible.

Distinguish Code 10, Code 43, and missing children

Code 10 means a driver stack could not start the child successfully; Code 43 means a driver or the system reported a device problem. Neither code names the failing USB transaction.

Use the capture to divide the cases:

  • No child function appears: inspect configuration parsing, interface grouping, and class identity.
  • Child appears, then Code 10: inspect the first class requests and endpoint setup issued by that driver.
  • Parent resets or disconnects: inspect power, firmware reset paths, control transfer failures, and re-enumeration.
  • One child works, another fails: isolate descriptors and traffic belonging to the failing interface.
  • All children appear but an app cannot open one: inspect OS ownership, permissions, exclusive handles, and application selection after proving USB startup.

If a CDC child is missing, compare the IAD, CDC union descriptor, notification endpoint, and data-interface bulk endpoints. If HID is the failing child, inspect the HID descriptor, report-descriptor request, report length, and interrupt endpoint. If a vendor function should use WinUSB, inspect Microsoft OS descriptors and the interface number they name.

The BOS and Microsoft OS descriptor guide covers that last path in detail.

Route class requests by recipient and interface

Composite firmware often receives a valid class request but dispatches it to the wrong function. Decode the setup packet:

  • direction and type from bmRequestType;
  • recipient from the low bits of bmRequestType;
  • request code from bRequest;
  • class-specific value from wValue;
  • target interface or endpoint from wIndex;
  • expected data length from wLength.

For an interface-recipient request, the low byte of wIndex usually identifies the interface. Firmware that assumes "CDC is always interface 0" can work in a single-function build and fail after HID is inserted before it. A trace that shows a correct request to interface 2 followed by a STALL is stronger evidence than a generic Device Manager message.

Build a routing table during diagnosis:

Interface Intended function Startup request Observed result
0 HID report descriptor read completes
1 CDC control line coding or control line state stalls
2 CDC data bulk endpoint activation no traffic yet
3 Vendor diagnostics vendor initialization completes

This table makes partial failure visible and gives firmware owners an exact handler to inspect.

Compare known-good and failing descriptor identities

When one firmware revision binds correctly and the next does not, collect both from the same host port if possible. Compare:

  • VID, PID, bcdDevice, and serial string;
  • device-level class tuple;
  • configuration length and interface count;
  • IAD positions and ranges;
  • interface class tuples and alternate settings;
  • endpoint addresses, types, packet sizes, and intervals;
  • class-specific descriptor bytes;
  • BOS and Microsoft OS descriptor responses;
  • first request that differs after configuration.

Do not compare only the decoded labels. Export the raw descriptor blocks as well. A decoder can make a malformed sequence look plausible until the offset or original length is examined.

Changing identity during the test can also change operating-system cache behavior. If the new firmware uses a new PID or serial, say so in the report instead of attributing every difference to descriptor correctness.

Cross-platform results need careful interpretation

If Linux binds every function and Windows does not, the firmware is not automatically correct. The two systems may use different grouping rules, request sequences, driver matching, or cached state. Use the cross-platform result to narrow the question:

  • Did both hosts receive identical descriptors?
  • Did they select the same configuration and alternate settings?
  • Which host issued the first additional class request?
  • Did the device answer that request correctly?
  • Did the failing host group the same interface range?

Conversely, if Windows works and Linux fails, inspect kernel logs, bound drivers, permissions, and interface claims after confirming the bus sequence. A capture explains protocol behavior; host logs explain policy and driver decisions.

A repeatable composite-device workflow

Use this sequence for a USB composite device driver error:

  1. Capture from connection, before opening the application.
  2. Record host OS, port, hub or dock, device speed, firmware revision, VID, PID, and serial.
  3. Verify the device and configuration descriptor envelope.
  4. List every interface, alternate setting, class tuple, and endpoint.
  5. Mark every IAD and class-specific function relationship.
  6. Map those groups to expected child devices.
  7. Find the first expected child that is missing or unhealthy.
  8. Decode that child's first class or vendor requests.
  9. Check whether firmware routed each request by the actual interface number.
  10. Compare with a known-good device, firmware build, or host.
  11. Pair the trace with Windows setup/device logs or Linux kernel logs.
  12. Save a bounded .bscope session and a short evidence table.

Use the Bus Scope platform capture guide to confirm the correct controller or usbmon bus before testing. An empty capture from the wrong controller proves nothing.

QA questions

Can a USB composite device work partially?

Yes. Parent enumeration can succeed while only one child function fails. That is why the report must name the interface, child function, first failed request, and endpoint rather than calling the entire device broken.

Does an IAD replace all class-specific descriptors?

No. An IAD groups interfaces into a function. The function can still require class-specific descriptors, such as CDC functional descriptors, that define relationships and behavior inside the group.

Is usbccgp.sys itself usually the root cause?

Not from the name alone. It is the Windows generic parent for many composite devices. First prove the descriptors, function grouping, child identity, and startup request sequence. Use OS logs when the remaining issue is driver selection or policy.

Should firmware stall a request for another interface?

It may be correct to stall a genuinely unsupported request, but firmware must route interface-recipient requests using the actual interface number. A required class request sent to the intended interface and stalled during startup is actionable evidence.

Can Bus Scope prove which INF Windows selected?

No. It can prove the device identity, descriptors, requests, transfer results, resets, and endpoint behavior visible on the USB path. Windows driver-install and device logs are the source for INF ranking and installation policy.

Final diagnosis

A USB composite device wrong-driver symptom becomes solvable when the evidence states exactly what the host built: descriptor envelope, IAD ranges, interface class identities, child functions, and startup requests. If those are coherent and traffic completes, move to OS binding policy. If they diverge on the bus, fix the first broken descriptor or request-routing contract and capture the same test again to prove the change.

<!-- 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 -->