Struct imxrt_hal::usbd::BusAdapter

source ·
pub struct BusAdapter { /* private fields */ }
Expand description

A full- and high-speed UsbBus implementation

The BusAdapter adapts the USB peripheral instances, and exposes a UsbBus implementation.

Requirements

The driver assumes that you’ve prepared all USB clocks (CCM clock gates, CCM analog PLLs).

Before polling for USB class traffic, you must call configure() after your device has been configured. This can be accomplished by polling the USB device and checking its state until it’s been configured. Once configured, use UsbDevice::bus() to access the i.MX RT BusAdapter, and call configure(). You should only do this once. After that, you may poll for class traffic.

Example

This example shows you how to create a BusAdapter, build a simple USB device, and prepare the device for class traffic.

Note that this example does not demonstrate USB class allocation or polling. See your USB class’ documentation for details. This example also skips the clock initialization.

use imxrt_usbd::BusAdapter;

static EP_MEMORY: imxrt_usbd::EndpointMemory<1024> = imxrt_usbd::EndpointMemory::new();
static EP_STATE: imxrt_usbd::EndpointState = imxrt_usbd::EndpointState::max_endpoints();

// TODO initialize clocks...

let my_usb_peripherals = // Your Peripherals instance...
let bus_adapter = BusAdapter::new(
    my_usb_peripherals,
    &EP_MEMORY,
    &EP_STATE,
);

// Create the USB device...
use usb_device::prelude::*;
let bus_allocator = usb_device::bus::UsbBusAllocator::new(bus_adapter);
let mut device = UsbDeviceBuilder::new(&bus_allocator, UsbVidPid(0x5824, 0x27dd))
    .product("imxrt-usbd")
    // Other builder methods...
    .build();

// Poll until configured...
loop {
    if device.poll(&mut []) {
        let state = device.state();
        if state == usb_device::device::UsbDeviceState::Configured {
            break;
        }
    }
}

// Configure the bus
device.bus().configure();

// Ready for class traffic!

Design

This section talks about the driver design. It assumes that you’re familiar with the details of the i.MX RT USB peripheral. If you just want to use the driver, you can skip this section.

Packets and transfers

All i.MX RT USB drivers manage queue heads (QH), and transfer descriptors (TD). For the driver, each (QH) is assigned only one (TD) to perform I/O. We then assume each TD describes a single packet. This is simple to implement, but it means that the driver can only have one packet in flight per endpoint. You’re expected to quickly respond to poll() outputs, and schedule the next transfer in the time required for devices. This becomes more important as you increase driver speeds.

The hardware can zero-length terminate (ZLT) packets as needed if you call enable_zlt. By default, this feature is off, because most usb-device classes / devices take care to send zero-length packets, and enabling this feature could interfere with the class / device behaviors.

Implementations§

source§

impl BusAdapter

source

pub fn new<P, const SIZE: usize, const EP_COUNT: usize>( peripherals: P, buffer: &'static EndpointMemory<SIZE>, state: &'static EndpointState<EP_COUNT> ) -> BusAdapter
where P: Peripherals,

Create a high-speed USB bus adapter

This is equivalent to BusAdapter::with_speed when supplying Speed::High. See the with_speed documentation for more information.

Panics

Panics if buffer or state has already been associated with another USB bus.

source

pub fn with_speed<P, const SIZE: usize, const EP_COUNT: usize>( peripherals: P, buffer: &'static EndpointMemory<SIZE>, state: &'static EndpointState<EP_COUNT>, speed: Speed ) -> BusAdapter
where P: Peripherals,

Create a USB bus adapter with the given speed

Specify Speed::LowFull to throttle the USB data rate.

When this function returns, the BusAdapter has initialized the PHY and USB core peripherals. The adapter expects to own these two peripherals, along with the other peripherals required by the Peripherals safety contract.

You must also provide a region of memory that will used for endpoint I/O. The memory region will be partitioned for the endpoints, based on their requirements.

Panics

Panics if buffer or state has already been associated with another USB bus.

source

pub unsafe fn without_critical_sections<P, const SIZE: usize, const EP_COUNT: usize>( peripherals: P, buffer: &'static EndpointMemory<SIZE>, state: &'static EndpointState<EP_COUNT>, speed: Speed ) -> BusAdapter
where P: Peripherals,

Create a USB bus adapter that never takes a critical section

See BusAdapter::with_speed for general information.

Safety

The returned object fakes its Sync safety. Specifically, the object will not take critical sections in its &[mut] self methods to ensure safe access. By using this object, you must manually hold the guarantees of Sync without the compiler’s help.

Panics

Panics if buffer or state has already been associated with another USB bus.

source

pub fn set_interrupts(&self, interrupts: bool)

Enable (true) or disable (false) interrupts for this USB peripheral

The interrupt causes are implementation specific. To handle the interrupt, call poll().

source

pub fn enable_zlt(&self, ep_addr: EndpointAddress)

Enable zero-length termination (ZLT) for the given endpoint

When ZLT is enabled, software does not need to send a zero-length packet to terminate a transfer where the number of bytes equals the max packet size. The hardware will send this zero-length packet itself. By default, ZLT is off, and software is expected to send these packets. Enable this if you’re confident that your (third-party) device / USB class isn’t already sending these packets.

This call does nothing if the endpoint isn’t allocated.

source

pub fn configure(&self)

Apply device configurations, and perform other post-configuration actions

You must invoke this once, and only after your device has been configured. If the device is reset and reconfigured, you must invoke configure() again. See the top-level example for how this could be achieved.

source

pub fn gpt_mut<R>( &self, instance: Instance, func: impl FnOnce(&mut Gpt<'_>) -> R ) -> R

Acquire one of the GPT timer instances.

instance identifies which GPT instance you’re accessing. This may take a critical section for the duration of func.

Panics

Panics if the GPT instance is already borrowed. This could happen if you call gpt_mut again within the func callback.

Trait Implementations§

source§

impl UsbBus for BusAdapter

source§

const QUIRK_SET_ADDRESS_BEFORE_STATUS: bool = true

The USB hardware can guarantee that we set the status before we receive the status, and we’re taking advantage of that. We expect this flag to result in a call to set_address before the status happens. This means that we can meet the timing requirements without help from software.

It’s not a quirk; it’s a feature :)

source§

fn alloc_ep( &mut self, ep_dir: UsbDirection, ep_addr: Option<EndpointAddress>, ep_type: EndpointType, max_packet_size: u16, _interval: u8 ) -> Result<EndpointAddress, UsbError>

Allocates an endpoint and specified endpoint parameters. This method is called by the device and class implementations to allocate endpoints, and can only be called before enable is called. Read more
source§

fn set_device_address(&self, addr: u8)

Sets the device USB address to addr.
source§

fn enable(&mut self)

Enables and initializes the USB peripheral. Soon after enabling the device will be reset, so there is no need to perform a USB reset in this method.
source§

fn reset(&self)

Called when the host resets the device. This will be soon called after poll returns [PollResult::Reset]. This method should reset the state of all endpoints and peripheral flags back to a state suitable for enumeration, as well as ensure that all endpoints previously allocated with alloc_ep are initialized as specified.
source§

fn write(&self, ep_addr: EndpointAddress, buf: &[u8]) -> Result<usize, UsbError>

Writes a single packet of data to the specified endpoint and returns number of bytes actually written. Read more
source§

fn read( &self, ep_addr: EndpointAddress, buf: &mut [u8] ) -> Result<usize, UsbError>

Reads a single packet of data from the specified endpoint and returns the actual length of the packet. Read more
source§

fn set_stalled(&self, ep_addr: EndpointAddress, stalled: bool)

Sets or clears the STALL condition for an endpoint. If the endpoint is an OUT endpoint, it should be prepared to receive data again.
source§

fn is_stalled(&self, ep_addr: EndpointAddress) -> bool

Gets whether the STALL condition is set for an endpoint.
source§

fn suspend(&self)

Causes the USB peripheral to enter USB suspend mode, lowering power consumption and preparing to detect a USB wakeup event. This will be called after poll returns [PollResult::Suspend]. The device will continue be polled, and it shall return a value other than Suspend from poll when it no longer detects the suspend condition.
source§

fn resume(&self)

Resumes from suspend mode. This may only be called after the peripheral has been previously suspended.
source§

fn poll(&self) -> PollResult

Gets information about events and incoming data. Usually called in a loop or from an interrupt handler. See the [PollResult] struct for more information.
§

fn force_reset(&self) -> Result<(), UsbError>

Simulates a disconnect from the USB bus, causing the host to reset and re-enumerate the device. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.