embassy-rs/embassy · error

invalid OUT length

Error message

invalid OUT length {}

What it means

calc_receive_len_bits (usb_host.rs) encodes a pipe's OUT receive buffer size into USBRAM register bits; valid encodings cover 1–60 bytes and 61–1024 bytes. A pipe buffer length outside that range (0 or >1024) has no hardware representation, so the driver panics when restore_control_channel or alloc_pipe sets up the pipe.

Solutions

  1. Use receive (OUT) pipe buffer sizes within 1..=1024 bytes (64 is the FS standard)
  2. Clamp: let len = len.clamp(1, 1024); before pipe allocation
  3. For >1024-byte transfers, rely on multi-packet transfers rather than enlarging a single pipe buffer
  4. Audit where the size constant comes from (descriptor or config) and validate at that boundary

Example fix

// before
host.alloc_pipe(EndpointType::Bulk, addr, ep, dir, 2048, interval)?; // >1024 panics
// after
host.alloc_pipe(EndpointType::Bulk, addr, ep, dir, 512, interval)?; // legal on 32-bit usbram
// or clamp
let len = requested_len.clamp(1, 1024);
Defensive patterns

Strategy: validation

Validate before calling

fn rx_len_ok(len: u16) -> bool { (1..=1024).contains(&len) }
// before alloc_pipe/restore_control_channel: assert!(rx_len_ok(rx_size));

Type guard

fn valid_pipe_rx_size(len: u16) -> bool { (1..=1024).contains(&len) }

Prevention

When it happens

Trigger: UsbHost alloc_pipe / restore_control_channel calls with an OUT (receive) max_packet_size of 0 or greater than 1024 on btable-based USBRAM parts.

Common situations: Host stacks copied from device code where packet size 0 means 'unused'; requesting high-speed 2048-byte receive buffers on full-speed btable parts; misreading direction and passing an IN size of 0 into the OUT pipe setup.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10). Data as JSON: /api/errors/c2283bc520feee73. Report an issue: GitHub.

Appendix: source

Thrown at embassy-stm32/src/usb/usb_host.rs:173

    r.set_stat_tx(Stat::from_bits(0));
    r
}

fn align_len_up(len: u16) -> u16 {
    ((len as usize + USBRAM_ALIGN - 1) / USBRAM_ALIGN * USBRAM_ALIGN) as u16
}

/// Calculates the register field values for configuring receive buffer descriptor.
/// Returns `(actual_len, len_bits)`
///
/// `actual_len` length in bytes rounded up to USBRAM_ALIGN
/// `len_bits` should be placed on the upper 16 bits of the register value
fn calc_receive_len_bits(len: u16) -> (u16, u16) {
    match len {
        // NOTE: this could be 1..=62 with 16bit USBRAM, but not with 32bit. Limit it to 60 for simplicity.
        1..=60 => (align_len_up(len), align_len_up(len) / 2 << 10),
        61..=1024 => ((len + 31) / 32 * 32, (((len + 31) / 32 - 1) << 10) | 0x8000),
        _ => panic!("invalid OUT length {}", len),
    }
}

#[cfg(any(usbram_32_2048, usbram_32_1024))]
mod btable {
    use super::*;

    pub(super) fn write_in<I: Instance>(_index: usize, _addr: u16) {}

    /// Writes to Transmit Buffer Descriptor for Channel/endpoint `index``
    /// For Device this is an IN endpoint for Host an OUT endpoint
    pub(super) fn write_transmit_buffer_descriptor<I: Instance>(index: usize, addr: u16, len: u16) {
        // Address offset: index*8 [bytes] thus index*2 in 32 bit words
        USBRAM.mem(index * 2).write_value((addr as u32) | ((len as u32) << 16));
    }

    /// Writes to Receive Buffer Descriptor for Channel/endpoint `index``
    /// For Device this is an OUT endpoint for Host an IN endpoint

View on GitHub (pinned to 463a07b963)