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
impl BusAdapter
sourcepub fn new<P, const SIZE: usize, const EP_COUNT: usize>(
peripherals: P,
buffer: &'static EndpointMemory<SIZE>,
state: &'static EndpointState<EP_COUNT>
) -> BusAdapterwhere
P: Peripherals,
pub fn new<P, const SIZE: usize, const EP_COUNT: usize>(
peripherals: P,
buffer: &'static EndpointMemory<SIZE>,
state: &'static EndpointState<EP_COUNT>
) -> BusAdapterwhere
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.
sourcepub fn with_speed<P, const SIZE: usize, const EP_COUNT: usize>(
peripherals: P,
buffer: &'static EndpointMemory<SIZE>,
state: &'static EndpointState<EP_COUNT>,
speed: Speed
) -> BusAdapterwhere
P: Peripherals,
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
) -> BusAdapterwhere
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.
sourcepub 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
) -> BusAdapterwhere
P: Peripherals,
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
) -> BusAdapterwhere
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.
sourcepub fn set_interrupts(&self, interrupts: bool)
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()
.
sourcepub fn enable_zlt(&self, ep_addr: EndpointAddress)
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.
sourcepub fn configure(&self)
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.
sourcepub fn gpt_mut<R>(
&self,
instance: Instance,
func: impl FnOnce(&mut Gpt<'_>) -> R
) -> R
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
impl UsbBus for BusAdapter
source§const QUIRK_SET_ADDRESS_BEFORE_STATUS: bool = true
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>
fn alloc_ep( &mut self, ep_dir: UsbDirection, ep_addr: Option<EndpointAddress>, ep_type: EndpointType, max_packet_size: u16, _interval: u8 ) -> Result<EndpointAddress, UsbError>
source§fn set_device_address(&self, addr: u8)
fn set_device_address(&self, addr: u8)
addr
.source§fn enable(&mut self)
fn enable(&mut self)
source§fn reset(&self)
fn reset(&self)
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>
fn write(&self, ep_addr: EndpointAddress, buf: &[u8]) -> Result<usize, UsbError>
source§fn read(
&self,
ep_addr: EndpointAddress,
buf: &mut [u8]
) -> Result<usize, UsbError>
fn read( &self, ep_addr: EndpointAddress, buf: &mut [u8] ) -> Result<usize, UsbError>
source§fn set_stalled(&self, ep_addr: EndpointAddress, stalled: bool)
fn set_stalled(&self, ep_addr: EndpointAddress, stalled: bool)
source§fn is_stalled(&self, ep_addr: EndpointAddress) -> bool
fn is_stalled(&self, ep_addr: EndpointAddress) -> bool
source§fn suspend(&self)
fn suspend(&self)
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)
fn resume(&self)
source§fn poll(&self) -> PollResult
fn poll(&self) -> PollResult
PollResult
] struct for more information.