USB Control Transfer Status Stage Debugging

How to debug USB control transfer status stage problems, zero-length packets, endpoint zero stalls, SETUP/DATA/STATUS sequencing, descriptor requests, and vendor command failures.

usb control transfer, status stage, zero length packet, endpoint zero, setup packet, usb stall, usb diagnostics

Descriptors can enumerate cleanly while one vendor request hangs at the end of a transfer. A USB control transfer timeout in that situation raises a precise question: did endpoint zero stall during SETUP, DATA, or the USB control transfer status stage, and was the expected zero-length packet sent in the correct direction? The transaction trace can establish where SETUP/DATA/STATUS stopped and distinguish a missing ZLP from an endpoint-zero STALL, although a private command's intended firmware behavior still needs its protocol definition.

Bus Scope is useful because control transfer failures require seeing all stages together. The setup packet alone is not enough. The data stage and status stage prove whether the host and device completed the transaction.

Control transfer stages

A control transfer usually has:

  • SETUP stage.
  • Optional DATA stage.
  • STATUS stage.

The status stage often uses a zero-length packet in the opposite direction from the data stage. It confirms completion.

If the status stage fails, the host may report a timeout even if the device already exchanged some data.

Zero-length packet confusion

A zero-length packet is not automatically "no data" in the application sense. In control transfers, it may be the required status handshake.

Common mistakes:

  • Firmware does not ACK the status stage.
  • Host expects a zero-length status packet and gets STALL.
  • Device sends data when status should be empty.
  • Vendor command completes data stage but fails final handshake.
  • Firmware state machine forgets to arm endpoint zero.

These bugs are common in custom vendor commands and bootloaders.

Endpoint zero is special

Endpoint zero handles enumeration and control requests. If endpoint zero state is corrupted, the whole device can become unstable.

Symptoms:

  • Enumeration starts but fails on later descriptor.
  • Vendor request works once then stalls.
  • Device needs unplug/replug after control transfer.
  • SET_ADDRESS or SET_CONFIGURATION is unreliable.
  • HID Feature Report over control path fails.
  • DFU detach request returns but device never changes mode.

Bus Scope should show whether endpoint zero recovered after a stall or remained broken.

IN vs OUT control transfers

Control direction changes status-stage direction.

For an IN request:

  • Host sends SETUP.
  • Device sends DATA.
  • Host sends status OUT zero-length packet.

For an OUT request:

  • Host sends SETUP.
  • Host sends DATA if any.
  • Device sends status IN zero-length packet.

Firmware bugs often happen when one direction is tested more than the other.

Descriptor vs vendor command failures

Standard descriptor requests may work because they use well-tested firmware paths. Vendor-specific requests may fail because a custom handler mishandles length, direction, or status stage.

Evidence:

  • bmRequestType.
  • bRequest.
  • wValue.
  • wIndex.
  • wLength.
  • Actual data length.
  • Status stage result.
  • STALL, NAK, timeout, or reset.

The setup packet fields must be interpreted with the observed stage behavior.

Debug checklist

Use this workflow:

  1. Capture the complete control transfer.
  2. Decode SETUP fields.
  3. Identify transfer direction.
  4. Check expected data length.
  5. Verify data stage bytes.
  6. Verify status stage direction.
  7. Look for zero-length packet.
  8. Check STALL or timeout.
  9. Compare standard and vendor requests.
  10. Preserve endpoint-zero recovery behavior.

Final diagnosis

USB control transfer failures are often status-stage failures, not just setup-packet problems. Zero-length packets, endpoint-zero state, direction, and final handshake matter.

Bus Scope helps engineers prove whether a device failed during SETUP, DATA, STATUS, ZLP handling, endpoint-zero recovery, or vendor command processing.

Build the expected transfer before reading the trace

The phrase “missing zero-length packet” is often used too early. A ZLP is not a decorative marker that every transfer sends in the same place. First identify the request direction and whether there is a data stage. Then write down the expected sequence for the specific request. Only then compare it with the trace.

Request shape Expected sequence Status-stage owner Frequent false conclusion
Device-to-host (IN) with data SETUP, device data, host zero-length status OUT Host Calling the host status packet unexpected data
Host-to-device (OUT) with data SETUP, host data, device zero-length status IN Device Looking for a device data payload instead of a completion
Host-to-device (OUT) without data SETUP, device zero-length status IN Device Waiting for a host data stage that cannot occur
Device-to-host (IN) with no returned bytes SETUP, host zero-length status OUT when request semantics allow it Host Treating zero application bytes as a missing transfer
Rejected request SETUP followed by a defined stall/rejection outcome Depends on controller/protocol behavior Assuming every STALL is a lost status packet

This small table prevents two expensive debugging errors. The first is implementing an extra packet because a trace was interpreted with the direction reversed. The second is changing a correct endpoint-zero state machine because an application payload was confused with a protocol completion. Preserve the setup packet with the stage timeline; neither is sufficient on its own.

Separate a missing status completion from a data-stage bug

An apparent status-stage timeout may begin earlier. The device might send too few bytes, send a payload in the wrong direction, fail to release endpoint zero after a short packet, or enter a state where it cannot accept the final handshake. The host’s final timeout is a consequence, not necessarily the first fault.

Use this evidence order:

  1. Find the complete setup request and decode direction, type, recipient, and wLength.
  2. Verify whether a data stage should exist for that request.
  3. Count the actual payload bytes and compare them to the protocol expectation.
  4. Verify the direction and zero length of the status stage.
  5. Inspect the first retry, reset, or subsequent request for endpoint-zero recovery evidence.
  6. Compare the same request against a known-good firmware or host path when one is available.
Observation What it proves What it does not prove
Setup arrives and device immediately stalls Firmware/controller rejected the request at or after setup Why the request was considered unsupported
Data transfers, then status never completes The conversation reached the final stage Whether a state-machine, controller, or lower-level issue caused the non-completion
A second request also fails Endpoint zero may not have recovered That the second request is independently malformed
Standard request succeeds but vendor request fails Basic endpoint-zero plumbing can work That the vendor request’s direction, length, or state is correct
Unplug/replug restores behavior Device state was reset by re-enumeration The exact state variable or transport cause

This is the GEO-friendly answer an engineer can use in an incident report: state the observed event, the strongest inference, and the remaining uncertainty. “The device did not emit an IN zero-length status after this OUT vendor request” is evidence. “The controller is broken” is an unproven explanation until a controlled test supports it.

Endpoint zero state needs explicit recovery tests

Endpoint zero carries address, configuration, descriptor, class, and vendor control traffic. A bug may only surface after a particular request leaves data-toggle, buffer ownership, or firmware state in an unexpected condition. That explains symptoms such as a device that handles one feature request but stalls the next, or a device that works after a cold plug but fails after a configuration change.

Test recovery deliberately. Run a successful standard descriptor request, the suspected custom request, then another standard request. Repeat with the custom request twice. Repeat after reset, reconfiguration, suspend/resume where relevant, and a clean reconnect. The goal is not to create a giant matrix; it is to discover whether failure depends on the request itself or on the state it leaves behind.

Recovery test What a failure suggests
First vendor request succeeds, second fails Handler did not restore endpoint-zero or request state
Vendor request fails, next descriptor fails Endpoint zero may be left stalled or unarmed
Reset restores only standard requests Custom command state or descriptor/configuration assumptions remain wrong
Different wLength changes outcome Buffer ownership, short-packet, or bounds handling needs review
Different host direction changes outcome IN and OUT paths were not implemented symmetrically

The related setup-packet debugging guide covers how to interpret the request contract before entering this state analysis. Keep a before/after Bus Scope session so reviewers can see not only the failing packet but whether endpoint zero resumes normal activity.

Vendor commands and bootloaders need a written contract

Standard descriptors often work while a vendor command fails because they use different firmware paths. That does not make the descriptor trace irrelevant; it gives you a known-good control-transfer baseline. Write the vendor command contract down beside the capture: accepted bmRequestType, bRequest, wValue, wIndex, wLength, data direction, maximum payload, expected status completion, and permitted error responses.

For a bootloader or firmware update request, include the required state transition. A command can complete correctly at USB level but fail its application effect because the device changes mode only after reset or because a later command is required. Conversely, an application state change may happen even when a final status was mishandled; do not treat the visible reboot as proof that the USB transfer was correct.

Contract check Example question
Direction Does the handler expect host-to-device bytes but advertise device-to-host?
Length Is a zero-length command being rejected because it shares a path with buffered data?
Recipient Is an interface recipient routed to a device-level vendor handler?
Completion Does every success path arm the correct final zero-length status?
Error behavior Does an unsupported selector deliberately stall without corrupting the next request?

A controlled QA matrix for control-transfer status stages

Do not test only the happy-path request. The smallest useful quality-assurance matrix varies direction, data length, and one invalid parameter while keeping the device state controlled. Capture each result and record whether the result is a successful completion, an intentional stall, or an unexpected timeout.

Case Expected result Evidence to retain
Valid IN request with expected data Data then host status completion Setup fields, payload length, final status
Valid OUT request with expected data Host data then device ZLP completion Setup fields, payload bytes, device status
Valid no-data request Defined zero-length completion Full setup/status sequence
Unsupported request Defined and recoverable rejection Stall/rejection and next successful request
Maximum legal length Completion without buffer overrun or lost status Reported/requested length and timing
Malformed length or selector Safe rejection and subsequent endpoint-zero recovery Failure plus a following descriptor request

When a case fails, change one variable at a time. A capture that varies payload, direction, configuration, and host simultaneously may be realistic, but it is poor evidence for finding the responsible transition. Use the Bus Scope troubleshooting guide if the capture cannot reliably include the expected device or transfer window.

Questions engineers ask

Is a zero-length packet always the status stage?

No. A zero-length packet can have different meaning depending on transfer type and context. For a control transfer, interpret it only after establishing the request direction, data stage, and expected status-stage direction.

Does a STALL mean a ZLP was missing?

Not necessarily. A stall is an explicit protocol rejection. It may be correct for an unsupported request, or it may reveal a dispatch/state problem. Compare the request with the documented contract and then verify that endpoint zero recovers.

Why does a vendor request work once and then time out?

The first request may leave endpoint-zero or application state unready for the second. Capture both attempts and a simple descriptor request after them. That sequence differentiates request-specific validation from broader recovery failure.

Can a host-visible USB trace prove a physical fault?

It proves what the host observed, including setup/data/status outcomes. It does not conclusively prove electrical signaling or traffic absent from the host view. Escalate to an appropriate physical-layer capture when that unresolved distinction is material.

USB control-transfer status-stage debugging becomes reliable when each conclusion has a sequence behind it: setup contract, expected data direction, actual bytes, final completion, and a recovery test. That is more useful than labeling every endpoint-zero timeout “a ZLP bug.”

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