Skip to content
VLSI Mentor

USB · Module 13

Standard Device Requests

Eleven requests whose codes are not contiguous — and a legality table that is not a list of rules but a graph, in which one wrong entry makes the device permanently unenumerable.

Three chapters have moved eight bytes, a payload and a zero-length packet without ever saying what any request means.

This chapter is the eleven standard requests — and its subject is not a table of codes. It is which requests are legal in which device state, because Module 8's state machine and this request set are the same mechanism seen from two sides.

1. Eleven Requests, and the Codes Are Not Contiguous

CodeRequestTypical bmRequestTypeDirection
0x00GET_STATUS0x80IN
0x01CLEAR_FEATURE0x00OUT
0x02—gap
0x03SET_FEATURE0x00OUT
0x04—gap
0x05SET_ADDRESS0x00OUT
0x06GET_DESCRIPTOR0x80IN
0x07SET_DESCRIPTOR0x00OUT
0x08GET_CONFIGURATION0x80IN
0x09SET_CONFIGURATION0x00OUT
0x0AGET_INTERFACE0x80IN
0x0BSET_INTERFACE0x00OUT
0x0CSYNCH_FRAME0x80IN

Values from ch9.h's USB_REQ_* definitions, and the two gaps are real: 0x02 and 0x04 are not assigned.

Which is a small fact with a design consequence: a jump table indexed by bRequest has two entries that must be errors, and an implementation that treats code ≤ 0x0C as valid accepts two requests that do not exist. §6's R4 is the general form of that mistake, and the gaps are why the right structure is a case with an explicit default rather than a range check.

The get/set symmetry is nearly complete and not quite. GET_STATUS has no SET_STATUS; SYNCH_FRAME has no counterpart. Every pair that exists is adjacent except CLEAR_FEATURE/SET_FEATURE, which are 0x01 and 0x03 with a gap between them — so even the pattern that looks reliable is not.

2. The Legality Table

A request is not simply supported or unsupported. It is legal in some device states and not in others, and Module 8's three states are the axis.

RequestDefaultAddressConfigured
GET_DESCRIPTORyesyesyes
SET_ADDRESSyesyes—
CLEAR_FEATURE / SET_FEATUREdevice onlyyesyes
GET_STATUS—yesyes
SET_DESCRIPTOR—yesyes
GET_CONFIGURATION / SET_CONFIGURATION—yesyes
GET_INTERFACE / SET_INTERFACE——yes only
SYNCH_FRAME——yes only

Three rows carry the structure:

GET_DESCRIPTOR is legal everywhere, because the host must be able to read a device it knows nothing about. Chapter 13.2 §3's probe is the first control transfer of every enumeration and it happens in Default.

SET_ADDRESS is legal in Default, because that is its entire purpose — it is the request that leaves Default, and Chapter 8.4 is the state it leads to.

Interface requests are legal only in Configured, because interfaces do not exist until a configuration selects them — Chapter 8.5. Asking which alternate setting an interface is using, before any interface exists, is not a question with an answer.

3. The Table Is a Graph

The section this chapter exists for.

Reading the table row by row makes it look like eleven independent rules. It is not. Two of the requests change the device's state, so the table describes a graph:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   Default ──SET_ADDRESS(n≠0)──▶ Address ──SET_CONFIGURATION(n≠0)──▶ Configured
      ▲                             │                                     │
      └───────SET_ADDRESS(0)────────┘◀────────SET_CONFIGURATION(0)────────┘

And the question that matters is not is each rule right but is the graph connected.

4. The Bootstrap Set

Which requests must work in Default, and why exactly those.

A device in Default has no assigned address — Chapter 8.3 — so it answers at address 0, and every unenumerated device on the bus answers at address 0. Which is why only one may be in Default at a time, and why the set of requests legal there is deliberately tiny:

Legal in DefaultBecause
GET_DESCRIPTORthe host must read a device it knows nothing about
SET_ADDRESSit is the exit from Default
CLEAR_FEATURE / SET_FEATURE to the devicerecovery, before an address exists

Everything else is undefined in Default, and the reason is uniform: it refers to something that does not exist yet. There is no configuration to get, no interface to select, and a status whose meaning depends on a configuration that has not been chosen.

The Default state's request set is exactly the set that does not presuppose anything the host has not yet established.

Which makes it a bootstrap set in the strict sense — the minimum from which everything else becomes reachable, and Chapter 10.2 §1's bootstrap argument arriving as a concrete eleven-row table.

A block diagram of the device state graph formed by the standard request legality table. The Default state permits GET_DESCRIPTOR, SET_ADDRESS, and device-recipient feature requests. A SET_ADDRESS with a non-zero value is the only edge leaving Default, leading to the Address state. The Address state permits the full set of device requests including GET_STATUS, SET_DESCRIPTOR and the configuration requests. A SET_CONFIGURATION with a non-zero value leads to the Configured state, which additionally permits interface requests and synch-frame. A SET_CONFIGURATION of zero returns from Configured to Address, and a SET_ADDRESS of zero returns from Address to Default. An annotation notes that deleting the SET_ADDRESS edge out of Default disconnects the graph, leaving both other states unreachable.Bootstrap setGET_DESC · SET_ADDR ·featuresDefaultanswers at address 0Address+ status, config,descriptorConfigured+ interface, synch frameInterfaces existonly hereDelete this edgeboth states unreachablelegal hereADDR(n)CFG(n)enablesthe only exit12
Figure 1 — the legality table as the graph it actually is. The two state-changing requests are the only edges; everything else is a self-loop that must be legal in the states it is drawn against. Deleting the SET_ADDRESS edge out of Default leaves the device with no path to any other state.

5. Unsupported Is Not Illegal

Two different negatives that both produce a STALL and mean different things.

Unsupported: the device does not implement this request. SET_DESCRIPTOR is optional and most devices stall it in every state.

Illegal here: the device implements it and the request does not apply in the current state. GET_INTERFACE on a device that is not Configured.

Both stall, so a host cannot distinguish them — and it does not need to. But the device must, because they have different implications:

  • Unsupported is static. It will stall in every state, forever, and that is a complete answer.
  • Illegal here is dynamic. The same request will succeed after a SET_CONFIGURATION, and a device that conflates the two will stall it even then.

A device that answers illegal here with the same logic it uses for unsupported has turned a state-dependent rule into a permanent one.

§6's R5 is exactly that inversion, and it is the reverse of what you would expect: it computes legality perfectly and then ignores it when deciding whether to stall.

6. The Request Router, as RTL

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// usb_req_pkg + usb_std_request
//
// Classification: SIMPLIFIED SYNTHESIZABLE TEACHING RTL plus a CONCEPTUAL
// package. It answers three questions about a decoded SETUP: is this a
// standard request, do we know it, and is it legal in the state we are in.
//
// WHAT IT MODELS. Section 1's eleven codes and section 2's legality table.
//
// WHAT IT DOES NOT MODEL. The SETUP decode (Chapter 13.1); the stages that
// carry the request (Chapters 13.2 and 13.3); what each request DOES, which
// is the device's own business and is where wValue and wIndex are consumed;
// the device state machine itself (Module 8 -- consumed here as an input);
// and class or vendor requests, which this block deliberately claims none of.
//
// ── WHY A CASE AND NOT A RANGE CHECK ────────────────────────────────────
// Section 1: the codes are NOT contiguous -- 0x02 and 0x04 are gaps. A
// `bRequest <= 8'h0C` test accepts two requests that do not exist, and an
// indexed jump table has two entries that must be errors. The case's
// `default` arm is where both of those become one line.
// ─────────────────────────────────────────────────────────────────────────
package usb_req_pkg;

  // Module 8's states, encoded so that ">=" expresses "at least this far
  // through enumeration" -- which is how section 2's table actually reads.
  typedef enum logic [1:0] {
    ST_DEFAULT = 2'd0, ST_ADDRESS = 2'd1, ST_CONFIGURED = 2'd2
  } dev_state_e;

  // Section 1, from ch9.h's USB_REQ_* values.
  localparam logic [7:0] REQ_GET_STATUS        = 8'h00;
  localparam logic [7:0] REQ_CLEAR_FEATURE     = 8'h01;
  localparam logic [7:0] REQ_SET_FEATURE       = 8'h03;   // 0x02 is a GAP
  localparam logic [7:0] REQ_SET_ADDRESS       = 8'h05;   // 0x04 is a GAP
  localparam logic [7:0] REQ_GET_DESCRIPTOR    = 8'h06;
  localparam logic [7:0] REQ_SET_DESCRIPTOR    = 8'h07;
  localparam logic [7:0] REQ_GET_CONFIGURATION = 8'h08;
  localparam logic [7:0] REQ_SET_CONFIGURATION = 8'h09;
  localparam logic [7:0] REQ_GET_INTERFACE     = 8'h0A;
  localparam logic [7:0] REQ_SET_INTERFACE     = 8'h0B;
  localparam logic [7:0] REQ_SYNCH_FRAME       = 8'h0C;

endpackage

module usb_std_request
  import usb_req_pkg::*;
  import usb_setup_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  // From Chapter 13.1's decode.
  input  logic        req_valid,
  input  req_type_e   req_type,
  input  recipient_e  recipient,
  input  logic        recipient_defined,
  input  logic [7:0]  bRequest,
  input  logic [15:0] wValue,

  input  dev_state_e  dev_state,      // Module 8

  output logic        is_standard,
  output logic        known_request,
  output logic        legal_here,
  output logic        must_stall
);

  assign is_standard = req_valid && (req_type == RT_STANDARD);

  always_comb begin
    known_request = 1'b0;
    legal_here    = 1'b0;

    // Chapter 13.1 section 2: an undefined recipient is not a request we
    // can route, whatever bRequest says.
    if (is_standard && recipient_defined) begin
      case (bRequest)

        REQ_GET_STATUS: begin
          known_request = 1'b1;
          // Section 4: undefined in Default -- the status it would report
          // depends on a configuration that has not been chosen.
          legal_here = (dev_state >= ST_ADDRESS);
        end

        REQ_CLEAR_FEATURE, REQ_SET_FEATURE: begin
          known_request = 1'b1;
          // Legal in Default only for the DEVICE recipient: there are no
          // interfaces or non-zero endpoints to apply a feature to yet.
          legal_here = (dev_state >= ST_ADDRESS) ||
                       ((dev_state == ST_DEFAULT) && (recipient == RCP_DEVICE));
        end

        REQ_SET_ADDRESS: begin
          known_request = 1'b1;
          // SECTION 3'S EDGE. This is the only exit from Default, and
          // section 6's R2 measures what removing it costs: every state but
          // Default becomes unreachable.
          legal_here = (dev_state <= ST_ADDRESS);
        end

        REQ_GET_DESCRIPTOR: begin
          known_request = 1'b1;
          // Section 4: legal EVERYWHERE. The host must be able to read a
          // device it knows nothing about, which is how enumeration starts.
          legal_here = 1'b1;
        end

        REQ_SET_DESCRIPTOR: begin
          known_request = 1'b1;
          legal_here = (dev_state >= ST_ADDRESS);
        end

        REQ_GET_CONFIGURATION, REQ_SET_CONFIGURATION: begin
          known_request = 1'b1;
          legal_here = (dev_state >= ST_ADDRESS);
        end

        REQ_GET_INTERFACE, REQ_SET_INTERFACE, REQ_SYNCH_FRAME: begin
          known_request = 1'b1;
          // Chapter 8.5: interfaces do not exist until a configuration
          // selects them, so these have no meaning before Configured.
          legal_here = (dev_state == ST_CONFIGURED);
        end

        default: begin
          // Section 1's two gaps and every code above 0x0C land here.
          known_request = 1'b0;
          legal_here    = 1'b0;
        end

      endcase
    end
  end

  // Section 5: the two negatives are computed separately and produce the
  // same outward answer. Folding them -- section 6's R5 -- turns a
  // state-dependent rule into a permanent one.
  assign must_stall = req_valid && is_standard && (!known_request || !legal_here);

endmodule

What it models. Recognition and state-legality for the eleven standard requests.

Engineering reason. Because a request's legality is a property of the device's state, and the table those rules form must keep the state machine connected.

Inputs. The decoded SETUP fields and the device's state.

State retained. None — the device state arrives as an input and belongs to Module 8.

Outputs. Whether this is ours, whether we know it, whether it applies now, and the resulting stall.

Hardware implied. An 8-bit case and a handful of comparators.

Reset behaviour. Nothing to reset.

Assumptions. That dev_state is the state at the moment the SETUP arrived — a state change mid-transfer is Chapter 13.3 §4's deferral problem; that recipient_defined came from Chapter 13.1's five-bit mask; and that class and vendor requests are routed elsewhere entirely.

Omissions. The decode, the stages, the requests' effects, the state machine and the non-standard types — in the header.

What DV should verify. That the eleven codes are recognised and every other value of the byte is not — including the two gaps; that each request's legality matches the table in every state; that GET_DESCRIPTOR is legal in all three states; that SET_ADDRESS is legal in Default; that interface requests are legal only in Configured; and — the property no per-request check expresses — that the table leaves every state reachable.

7. Mutation Test

Five mutations over 3,116 requests — exhaustive over all 256 bRequest values × 3 device states × 4 recipients, plus 32 class and vendor requests and 12 with an undefined recipient.

The unmutated block: 82 accepted, 3,002 stalled, and 0 errors of every kind.

legality wrongstall wrongSET_ADDRESS stalled in Defaultinterface request before Configuredunknown accepted
golden00000
R1 GET_STATUS legal everywhere44000
R2 SET_ADDRESS illegal in Default88400
R3 interface requests from Address1212080
R4 unknown codes accepted2 9402 940002 940
R5 stall ignores legality0820160

R2 — make SET_ADDRESS illegal in Default

Measured: 8 wrong table entries, 4 of them SET_ADDRESS in Default.

Eight out of 3,116 is 0.26% of the bench. §3's reachability computation is the rest of the story: Address and Configured become unreachable, so the device can never enumerate on any host.

The smallest mutation in this chapter is the only one that makes the device completely useless.

R4 — accept unknown request codes

Measured: 2 940 wrong — by far the loudest, and the one that includes §1's two gaps.

Every undefined code becomes a recognised, legal request. The device then executes whatever its default arm does — which in a design that never expected to reach it is usually nothing, silently, with a successful status stage.

A host receives success for a request the device did not implement, which is Chapter 13.3 §6's T3 arriving from the other direction.

R3 — allow interface requests from Address

Measured: 12 wrong, of which 8 are interface requests before a configuration exists.

GET_INTERFACE on a device with no interfaces. The device must answer something, and whatever it answers is fabricated — there is no interface whose alternate setting could be reported.

The failure is a plausible wrong answer rather than an error, which is the hardest kind for a host to act on.

Measured: 4 wrong — one per recipient, in Default.

The mildest of the five. A device reporting its status before it has an address reports something that depends on a configuration it does not have, and a host that asked will act on it.

It is included because its count is nearly identical to R2's and its impact is nothing like it — which is §8's point.

R5 — let the stall decision ignore legality

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign must_stall = req_valid && !known_request;   // MUTANT R5

Measured: legal_here is wrong on zero of 3,116 requests. must_stall is wrong on 82.

The design computes legality perfectly and then does not use it. §5's inversion: every request the device recognises is accepted in every state, so GET_INTERFACE succeeds in Default and SET_CONFIGURATION succeeds before an address exists.

And a bench checking that the legality table is correct passes completely, because it is. Chapter 12.2 §6 measured the same shape on short packets: classification and use are separate, and only one of them changes behaviour.

8. Verification

This chapter's commit point is every request was answered according to the state the device was actually in — and the device could still get to every state.

Stimulus. Exhaustive: all 256 values of bRequest × three device states × four recipients = 3,072 combinations, plus class and vendor types and undefined recipients. There is no sampling question here — the input space is small enough to enumerate completely, which is what Chapter 11.3 §10 established removes every excuse about reachability and none about what is being checked.

Observation. Recognition, legality and the stall decision, against a reference table transcribed from the specification rather than derived from the design — plus four obligations stated independently of both:

  • SET_ADDRESS is never stalled in Default;
  • an interface request never succeeds before Configured;
  • an unrecognised standard request is always stalled;
  • GET_DESCRIPTOR is never stalled.

The first and last are reachability obligations in disguise — they are the two edges without which enumeration cannot start or proceed.

Reference model. The legality table as a function, transcribed from USB 2.0 §9.4. Its independence is real but partial — it is a second copy of the same table, so a misreading of the specification would appear in both. The four obligations are what guard against that, and they are stated as consequences (the device must be able to leave Default) rather than as table entries.

Coverage — the crosses are the exhaustive product, and four cells deserve naming:

  • SET_ADDRESS × Default — the edge
  • GET_DESCRIPTOR × Default — the bootstrap read
  • interface requests × Address — the cell R3 lives in
  • bRequest = 0x02 and 0x04 — the gaps, which a range check silently accepts

Negative cases with defined outcomes: no unrecognised code is ever accepted — all 245 of them; no interface request succeeds outside Configured; no request is claimed for a class or vendor type; and nothing is accepted with an undefined recipient.

9. Debugging: the Device That Works Until It Is Reset

A device enumerates and works. After a bus reset — triggered by suspend/resume, a hub power cycle, or a host driver reload — it never comes back. Power-cycling the device fixes it. The failure is completely reproducible.

What does power-cycling fixes it tell you? That the device's state after a bus reset differs from its state after power-on, and only one of them works. Chapter 8.3: a bus reset returns the device to Default, which is also where power-on leaves it — so the two should be identical.

Unless something is not reset. A device whose internal notion of state survives the bus reset believes it is still Configured while the host believes it is in Default.

What does that break? §2's table, from both sides. The host sends SET_ADDRESS — legal in Default. The device, believing itself Configured, finds SET_ADDRESS illegal and stalls it.

And the device is applying the table correctly. SET_ADDRESS genuinely is not legal in Configured. The bug is not in the table; it is in the state fed to it — which is why reading the request-handling code finds nothing wrong.

How do you confirm it? Read the device's state register immediately after a bus reset. If it is not Default, the reset path missed it — and Chapter 8.3's reset obligations are the list to check against.

What if the state is correctly Default? Then the fault is the opposite: the device is in Default and something else did not reset — the address, the configuration index, an endpoint's toggle. The symptom is the same and the search is different.

Why is it completely reproducible? Because it is a missing reset, not a race. This class of bug never presents as flakiness, which misleads: reproducibility suggests logic and this is omission.

The signature to keep: works from power-on and not from a bus reset means something survived a reset that should not have — and the fastest discriminator is whether the device's own state register agrees with the host's view.

10. Common Misconceptions

11. Reason It Through

A device supports two configurations. A host selects configuration 2, uses the device, then sends SET_CONFIGURATION(0). The device's endpoints stop working — correctly — but a subsequent SET_CONFIGURATION(2) is stalled, and the device has to be unplugged.

What does SET_CONFIGURATION(0) do? Chapter 8.5: it un-configures the device, returning it to Address. The endpoints stopping is correct.

Is SET_CONFIGURATION legal in Address? §2's table: yes — it is legal in Address and Configured. So the second request should succeed.

So why is it stalled? Two candidates, and they are distinguishable:

  • The device did not actually move to Address. It cleared its endpoints and left its state at Configured — and SET_CONFIGURATION is legal there too, so that alone would not stall it.
  • The device moved somewhere else. If it treated SET_CONFIGURATION(0) as a return to Default, then §2's table makes SET_CONFIGURATION illegal — and the stall is the device correctly applying the table to a wrong state.

Which is more likely? The second, and the reason is a plausible misreading: SET_ADDRESS(0) returns to Default and SET_CONFIGURATION(0) returns to Address, and the symmetry of zero means go back invites collapsing them into one rule. They go back different distances.

How would you confirm it without instrumenting the device? Send SET_ADDRESS after the failure. If it succeeds, the device thinks it is in Default — because SET_ADDRESS is legal there and illegal in Configured. One request distinguishes the two hypotheses.

Is the device recoverable? In principle yes — a device in Default with a valid address can be re-addressed and reconfigured. In practice the host will not try, because it believes the device is in Address and has no reason to send SET_ADDRESS again.

And the transferable point: the device and the host each maintain a state variable and neither transmits it — Chapter 12.4 §2's two beliefs, one level up. The legality table is the only place the disagreement becomes visible, and it becomes visible as a stall on a request that should have worked. A stall that contradicts the table is evidence about the state, not about the request.

12. Understanding Check

13. Summary

Eleven standard requests, and the codes are not contiguous — 0x02 and 0x04 are gaps, so a range check accepts two requests that do not exist and the correct structure is a case with an explicit default.

A request is legal in some device states and not others, and the axis is Module 8's three states. GET_DESCRIPTOR is legal everywhere, because the host must read a device it knows nothing about. SET_ADDRESS is legal in Default, because it is the exit from it. Interface requests are legal only in Configured, because interfaces do not exist before a configuration selects them.

And the table is not a list of rules. It is a graph, because two requests change state — so the property that matters is reachability, and it is not expressible as an assertion about any request.

§7 measured five mutations over 3,116 exhaustive combinations, and the two smallest are the lesson:

Making GET_STATUS legal in Default is four wrong entries and leaks a status report. Making SET_ADDRESS illegal in Default is four wrong entries and makes the device permanently unenumerable on every host.

A mutation score counts them equally. The graph does not.

The loudest mutation accepted every undefined code — 2 940 wrong, including §1's two gaps — and returned success for requests the device never implemented. And the subtlest computed the legality table perfectly and then ignored it: zero legality errors, 82 wrong stalls, with a bench checking the table passing completely.

§8's practice follows from §3, and it is a kind of check this curriculum has not needed before:

When a design's correctness depends on a table, check the table's structure, not only its entries — and do it at elaboration, because the table is fixed before a cycle runs.

Three states, two state-changing requests, four edges, a search that takes microseconds. It is the only thing that identifies a wrong SET_ADDRESS row as a disconnected graph rather than as four bad entries.

14. What Comes Next

Every piece is now in place: the stages, the requests, the states, the descriptors, the packets, the transactions. Chapter 13.5 assembles them.

It walks one real enumeration end to end — the reset, the 8-byte descriptor probe, the second reset, SET_ADDRESS, the full descriptor read, the configuration read in two parts, and SET_CONFIGURATION — as a sequence of control transfers, each one built from transactions, each transaction from packets.

And its subject is the ordering. Every step depends on the one before it, the dependencies are not all obvious, and §3's graph is what makes the sequence the only one that works.

Browse the full path on the USB tutorials index.

Continue learning

Standards & specifications

Governing standard
USB-IF (Universal Serial Bus Specification)(opens USB Implementers Forum (USB-IF) in a new tab)

Defines the USB bus — its electrical signalling, connectors, packet and transaction model, device framework and the descriptors a device must expose — together with the device-class specifications layered on it. It does not define host-controller register interfaces (xHCI and EHCI are separate documents) nor any operating system's driver architecture.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the USB curriculum.