mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-21 14:22:33 +00:00
Merge pull request #3314 from elagil/add_iso_endpoint_support
Add ISO endpoint support
This commit is contained in:
commit
ae8caf3f55
@ -80,6 +80,8 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
|||||||
|
|
||||||
if istr.ctr() {
|
if istr.ctr() {
|
||||||
let index = istr.ep_id() as usize;
|
let index = istr.ep_id() as usize;
|
||||||
|
CTR_TRIGGERED[index].store(true, Ordering::Relaxed);
|
||||||
|
|
||||||
let mut epr = regs.epr(index).read();
|
let mut epr = regs.epr(index).read();
|
||||||
if epr.ctr_rx() {
|
if epr.ctr_rx() {
|
||||||
if index == 0 && epr.setup() {
|
if index == 0 && epr.setup() {
|
||||||
@ -120,6 +122,10 @@ const USBRAM_ALIGN: usize = 4;
|
|||||||
const NEW_AW: AtomicWaker = AtomicWaker::new();
|
const NEW_AW: AtomicWaker = AtomicWaker::new();
|
||||||
static BUS_WAKER: AtomicWaker = NEW_AW;
|
static BUS_WAKER: AtomicWaker = NEW_AW;
|
||||||
static EP0_SETUP: AtomicBool = AtomicBool::new(false);
|
static EP0_SETUP: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
const NEW_CTR_TRIGGERED: AtomicBool = AtomicBool::new(false);
|
||||||
|
static CTR_TRIGGERED: [AtomicBool; EP_COUNT] = [NEW_CTR_TRIGGERED; EP_COUNT];
|
||||||
|
|
||||||
static EP_IN_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT];
|
static EP_IN_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT];
|
||||||
static EP_OUT_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT];
|
static EP_OUT_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT];
|
||||||
static IRQ_RESET: AtomicBool = AtomicBool::new(false);
|
static IRQ_RESET: AtomicBool = AtomicBool::new(false);
|
||||||
@ -163,20 +169,37 @@ fn calc_out_len(len: u16) -> (u16, u16) {
|
|||||||
mod btable {
|
mod btable {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub(super) fn write_in<T: Instance>(index: usize, addr: u16) {
|
pub(super) fn write_in_tx<T: Instance>(index: usize, addr: u16) {
|
||||||
USBRAM.mem(index * 4 + 0).write_value(addr);
|
USBRAM.mem(index * 4 + 0).write_value(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn write_in_len<T: Instance>(index: usize, _addr: u16, len: u16) {
|
pub(super) fn write_in_rx<T: Instance>(index: usize, addr: u16) {
|
||||||
|
USBRAM.mem(index * 4 + 2).write_value(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn write_in_len_rx<T: Instance>(index: usize, _addr: u16, len: u16) {
|
||||||
|
USBRAM.mem(index * 4 + 3).write_value(len);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn write_in_len_tx<T: Instance>(index: usize, _addr: u16, len: u16) {
|
||||||
USBRAM.mem(index * 4 + 1).write_value(len);
|
USBRAM.mem(index * 4 + 1).write_value(len);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn write_out<T: Instance>(index: usize, addr: u16, max_len_bits: u16) {
|
pub(super) fn write_out_rx<T: Instance>(index: usize, addr: u16, max_len_bits: u16) {
|
||||||
USBRAM.mem(index * 4 + 2).write_value(addr);
|
USBRAM.mem(index * 4 + 2).write_value(addr);
|
||||||
USBRAM.mem(index * 4 + 3).write_value(max_len_bits);
|
USBRAM.mem(index * 4 + 3).write_value(max_len_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn read_out_len<T: Instance>(index: usize) -> u16 {
|
pub(super) fn write_out_tx<T: Instance>(index: usize, addr: u16, max_len_bits: u16) {
|
||||||
|
USBRAM.mem(index * 4 + 0).write_value(addr);
|
||||||
|
USBRAM.mem(index * 4 + 1).write_value(max_len_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn read_out_len_tx<T: Instance>(index: usize) -> u16 {
|
||||||
|
USBRAM.mem(index * 4 + 1).read()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn read_out_len_rx<T: Instance>(index: usize) -> u16 {
|
||||||
USBRAM.mem(index * 4 + 3).read()
|
USBRAM.mem(index * 4 + 3).read()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -184,19 +207,37 @@ mod btable {
|
|||||||
mod btable {
|
mod btable {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub(super) fn write_in<T: Instance>(_index: usize, _addr: u16) {}
|
pub(super) fn write_in_tx<T: Instance>(_index: usize, _addr: u16) {}
|
||||||
|
|
||||||
pub(super) fn write_in_len<T: Instance>(index: usize, addr: u16, len: u16) {
|
pub(super) fn write_in_rx<T: Instance>(_index: usize, _addr: u16) {}
|
||||||
|
|
||||||
|
pub(super) fn write_in_len_tx<T: Instance>(index: usize, addr: u16, len: u16) {
|
||||||
USBRAM.mem(index * 2).write_value((addr as u32) | ((len as u32) << 16));
|
USBRAM.mem(index * 2).write_value((addr as u32) | ((len as u32) << 16));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn write_out<T: Instance>(index: usize, addr: u16, max_len_bits: u16) {
|
pub(super) fn write_in_len_rx<T: Instance>(index: usize, addr: u16, len: u16) {
|
||||||
|
USBRAM
|
||||||
|
.mem(index * 2 + 1)
|
||||||
|
.write_value((addr as u32) | ((len as u32) << 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn write_out_tx<T: Instance>(index: usize, addr: u16, max_len_bits: u16) {
|
||||||
|
USBRAM
|
||||||
|
.mem(index * 2)
|
||||||
|
.write_value((addr as u32) | ((max_len_bits as u32) << 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn write_out_rx<T: Instance>(index: usize, addr: u16, max_len_bits: u16) {
|
||||||
USBRAM
|
USBRAM
|
||||||
.mem(index * 2 + 1)
|
.mem(index * 2 + 1)
|
||||||
.write_value((addr as u32) | ((max_len_bits as u32) << 16));
|
.write_value((addr as u32) | ((max_len_bits as u32) << 16));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn read_out_len<T: Instance>(index: usize) -> u16 {
|
pub(super) fn read_out_len_tx<T: Instance>(index: usize) -> u16 {
|
||||||
|
(USBRAM.mem(index * 2).read() >> 16) as u16
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn read_out_len_rx<T: Instance>(index: usize) -> u16 {
|
||||||
(USBRAM.mem(index * 2 + 1).read() >> 16) as u16
|
(USBRAM.mem(index * 2 + 1).read() >> 16) as u16
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -327,6 +368,13 @@ impl<'d, T: Instance> Driver<'d, T> {
|
|||||||
return false; // reserved for control pipe
|
return false; // reserved for control pipe
|
||||||
}
|
}
|
||||||
let used = ep.used_out || ep.used_in;
|
let used = ep.used_out || ep.used_in;
|
||||||
|
if used && (ep.ep_type == EndpointType::Isochronous || ep.ep_type == EndpointType::Bulk) {
|
||||||
|
// Isochronous and bulk endpoints are double-buffered.
|
||||||
|
// Their corresponding endpoint/channel registers are forced to be unidirectional.
|
||||||
|
// Do not reuse this index.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
let used_dir = match D::dir() {
|
let used_dir = match D::dir() {
|
||||||
Direction::Out => ep.used_out,
|
Direction::Out => ep.used_out,
|
||||||
Direction::In => ep.used_in,
|
Direction::In => ep.used_in,
|
||||||
@ -350,7 +398,11 @@ impl<'d, T: Instance> Driver<'d, T> {
|
|||||||
let addr = self.alloc_ep_mem(len);
|
let addr = self.alloc_ep_mem(len);
|
||||||
|
|
||||||
trace!(" len_bits = {:04x}", len_bits);
|
trace!(" len_bits = {:04x}", len_bits);
|
||||||
btable::write_out::<T>(index, addr, len_bits);
|
btable::write_out_rx::<T>(index, addr, len_bits);
|
||||||
|
|
||||||
|
if ep_type == EndpointType::Isochronous {
|
||||||
|
btable::write_out_tx::<T>(index, addr, len_bits);
|
||||||
|
}
|
||||||
|
|
||||||
EndpointBuffer {
|
EndpointBuffer {
|
||||||
addr,
|
addr,
|
||||||
@ -366,7 +418,11 @@ impl<'d, T: Instance> Driver<'d, T> {
|
|||||||
let addr = self.alloc_ep_mem(len);
|
let addr = self.alloc_ep_mem(len);
|
||||||
|
|
||||||
// ep_in_len is written when actually TXing packets.
|
// ep_in_len is written when actually TXing packets.
|
||||||
btable::write_in::<T>(index, addr);
|
btable::write_in_tx::<T>(index, addr);
|
||||||
|
|
||||||
|
if ep_type == EndpointType::Isochronous {
|
||||||
|
btable::write_in_rx::<T>(index, addr);
|
||||||
|
}
|
||||||
|
|
||||||
EndpointBuffer {
|
EndpointBuffer {
|
||||||
addr,
|
addr,
|
||||||
@ -656,6 +712,18 @@ impl Dir for Out {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Selects the packet buffer.
|
||||||
|
///
|
||||||
|
/// For double-buffered endpoints, both the `Rx` and `Tx` buffer from a channel are used for the same
|
||||||
|
/// direction of transfer. This is opposed to single-buffered endpoints, where one channel can serve
|
||||||
|
/// two directions at the same time.
|
||||||
|
enum PacketBuffer {
|
||||||
|
/// The RX buffer - must be used for single-buffered OUT endpoints
|
||||||
|
Rx,
|
||||||
|
/// The TX buffer - must be used for single-buffered IN endpoints
|
||||||
|
Tx,
|
||||||
|
}
|
||||||
|
|
||||||
/// USB endpoint.
|
/// USB endpoint.
|
||||||
pub struct Endpoint<'d, T: Instance, D> {
|
pub struct Endpoint<'d, T: Instance, D> {
|
||||||
_phantom: PhantomData<(&'d mut T, D)>,
|
_phantom: PhantomData<(&'d mut T, D)>,
|
||||||
@ -664,15 +732,46 @@ pub struct Endpoint<'d, T: Instance, D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance, D> Endpoint<'d, T, D> {
|
impl<'d, T: Instance, D> Endpoint<'d, T, D> {
|
||||||
fn write_data(&mut self, buf: &[u8]) {
|
/// Write to a double-buffered endpoint.
|
||||||
|
///
|
||||||
|
/// For double-buffered endpoints, the data buffers overlap, but we still need to write to the right counter field.
|
||||||
|
/// The DTOG_TX bit indicates the buffer that is currently in use by the USB peripheral, that is, the buffer in
|
||||||
|
/// which the next transmit packet will be stored, so we need to write the counter of the OTHER buffer, which is
|
||||||
|
/// where the last transmitted packet was stored.
|
||||||
|
fn write_data_double_buffered(&mut self, buf: &[u8], packet_buffer: PacketBuffer) {
|
||||||
let index = self.info.addr.index();
|
let index = self.info.addr.index();
|
||||||
self.buf.write(buf);
|
self.buf.write(buf);
|
||||||
btable::write_in_len::<T>(index, self.buf.addr, buf.len() as _);
|
|
||||||
|
match packet_buffer {
|
||||||
|
PacketBuffer::Rx => btable::write_in_len_rx::<T>(index, self.buf.addr, buf.len() as _),
|
||||||
|
PacketBuffer::Tx => btable::write_in_len_tx::<T>(index, self.buf.addr, buf.len() as _),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_data(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {
|
/// Write to a single-buffered endpoint.
|
||||||
|
fn write_data(&mut self, buf: &[u8]) {
|
||||||
|
self.write_data_double_buffered(buf, PacketBuffer::Tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read from a double-buffered endpoint.
|
||||||
|
///
|
||||||
|
/// For double-buffered endpoints, the data buffers overlap, but we still need to read from the right counter field.
|
||||||
|
/// The DTOG_RX bit indicates the buffer that is currently in use by the USB peripheral, that is, the buffer in
|
||||||
|
/// which the next received packet will be stored, so we need to read the counter of the OTHER buffer, which is
|
||||||
|
/// where the last received packet was stored.
|
||||||
|
fn read_data_double_buffered(
|
||||||
|
&mut self,
|
||||||
|
buf: &mut [u8],
|
||||||
|
packet_buffer: PacketBuffer,
|
||||||
|
) -> Result<usize, EndpointError> {
|
||||||
let index = self.info.addr.index();
|
let index = self.info.addr.index();
|
||||||
let rx_len = btable::read_out_len::<T>(index) as usize & 0x3FF;
|
|
||||||
|
let rx_len = match packet_buffer {
|
||||||
|
PacketBuffer::Rx => btable::read_out_len_rx::<T>(index),
|
||||||
|
PacketBuffer::Tx => btable::read_out_len_tx::<T>(index),
|
||||||
|
} as usize
|
||||||
|
& 0x3FF;
|
||||||
|
|
||||||
trace!("READ DONE, rx_len = {}", rx_len);
|
trace!("READ DONE, rx_len = {}", rx_len);
|
||||||
if rx_len > buf.len() {
|
if rx_len > buf.len() {
|
||||||
return Err(EndpointError::BufferOverflow);
|
return Err(EndpointError::BufferOverflow);
|
||||||
@ -680,6 +779,11 @@ impl<'d, T: Instance, D> Endpoint<'d, T, D> {
|
|||||||
self.buf.read(&mut buf[..rx_len]);
|
self.buf.read(&mut buf[..rx_len]);
|
||||||
Ok(rx_len)
|
Ok(rx_len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read from a single-buffered endpoint.
|
||||||
|
fn read_data(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {
|
||||||
|
self.read_data_double_buffered(buf, PacketBuffer::Rx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> {
|
impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> {
|
||||||
@ -734,25 +838,53 @@ impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> {
|
|||||||
EP_OUT_WAKERS[index].register(cx.waker());
|
EP_OUT_WAKERS[index].register(cx.waker());
|
||||||
let regs = T::regs();
|
let regs = T::regs();
|
||||||
let stat = regs.epr(index).read().stat_rx();
|
let stat = regs.epr(index).read().stat_rx();
|
||||||
if matches!(stat, Stat::NAK | Stat::DISABLED) {
|
if self.info.ep_type == EndpointType::Isochronous {
|
||||||
Poll::Ready(stat)
|
// The isochronous endpoint does not change its `STAT_RX` field to `NAK` when receiving a packet.
|
||||||
|
// Therefore, this instead waits until the `CTR` interrupt was triggered.
|
||||||
|
if matches!(stat, Stat::DISABLED) || CTR_TRIGGERED[index].load(Ordering::Relaxed) {
|
||||||
|
Poll::Ready(stat)
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Poll::Pending
|
if matches!(stat, Stat::NAK | Stat::DISABLED) {
|
||||||
|
Poll::Ready(stat)
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
CTR_TRIGGERED[index].store(false, Ordering::Relaxed);
|
||||||
|
|
||||||
if stat == Stat::DISABLED {
|
if stat == Stat::DISABLED {
|
||||||
return Err(EndpointError::Disabled);
|
return Err(EndpointError::Disabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
let rx_len = self.read_data(buf)?;
|
|
||||||
|
|
||||||
let regs = T::regs();
|
let regs = T::regs();
|
||||||
|
|
||||||
|
let packet_buffer = if self.info.ep_type == EndpointType::Isochronous {
|
||||||
|
// Find the buffer, which is currently in use. Read from the OTHER buffer.
|
||||||
|
if regs.epr(index).read().dtog_rx() {
|
||||||
|
PacketBuffer::Rx
|
||||||
|
} else {
|
||||||
|
PacketBuffer::Tx
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PacketBuffer::Rx
|
||||||
|
};
|
||||||
|
|
||||||
|
let rx_len = self.read_data_double_buffered(buf, packet_buffer)?;
|
||||||
|
|
||||||
regs.epr(index).write(|w| {
|
regs.epr(index).write(|w| {
|
||||||
w.set_ep_type(convert_type(self.info.ep_type));
|
w.set_ep_type(convert_type(self.info.ep_type));
|
||||||
w.set_ea(self.info.addr.index() as _);
|
w.set_ea(self.info.addr.index() as _);
|
||||||
w.set_stat_rx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits()));
|
if self.info.ep_type == EndpointType::Isochronous {
|
||||||
|
w.set_stat_rx(Stat::from_bits(0)); // STAT_RX remains `VALID`.
|
||||||
|
} else {
|
||||||
|
w.set_stat_rx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits()));
|
||||||
|
}
|
||||||
w.set_stat_tx(Stat::from_bits(0));
|
w.set_stat_tx(Stat::from_bits(0));
|
||||||
w.set_ctr_rx(true); // don't clear
|
w.set_ctr_rx(true); // don't clear
|
||||||
w.set_ctr_tx(true); // don't clear
|
w.set_ctr_tx(true); // don't clear
|
||||||
@ -776,25 +908,54 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> {
|
|||||||
EP_IN_WAKERS[index].register(cx.waker());
|
EP_IN_WAKERS[index].register(cx.waker());
|
||||||
let regs = T::regs();
|
let regs = T::regs();
|
||||||
let stat = regs.epr(index).read().stat_tx();
|
let stat = regs.epr(index).read().stat_tx();
|
||||||
if matches!(stat, Stat::NAK | Stat::DISABLED) {
|
if self.info.ep_type == EndpointType::Isochronous {
|
||||||
Poll::Ready(stat)
|
// The isochronous endpoint does not change its `STAT_RX` field to `NAK` when receiving a packet.
|
||||||
|
// Therefore, this instead waits until the `CTR` interrupt was triggered.
|
||||||
|
if matches!(stat, Stat::DISABLED) || CTR_TRIGGERED[index].load(Ordering::Relaxed) {
|
||||||
|
Poll::Ready(stat)
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Poll::Pending
|
if matches!(stat, Stat::NAK | Stat::DISABLED) {
|
||||||
|
Poll::Ready(stat)
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
CTR_TRIGGERED[index].store(false, Ordering::Relaxed);
|
||||||
|
|
||||||
if stat == Stat::DISABLED {
|
if stat == Stat::DISABLED {
|
||||||
return Err(EndpointError::Disabled);
|
return Err(EndpointError::Disabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.write_data(buf);
|
let regs = T::regs();
|
||||||
|
|
||||||
|
let packet_buffer = if self.info.ep_type == EndpointType::Isochronous {
|
||||||
|
// Find the buffer, which is currently in use. Write to the OTHER buffer.
|
||||||
|
if regs.epr(index).read().dtog_tx() {
|
||||||
|
PacketBuffer::Tx
|
||||||
|
} else {
|
||||||
|
PacketBuffer::Rx
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PacketBuffer::Tx
|
||||||
|
};
|
||||||
|
|
||||||
|
self.write_data_double_buffered(buf, packet_buffer);
|
||||||
|
|
||||||
let regs = T::regs();
|
let regs = T::regs();
|
||||||
regs.epr(index).write(|w| {
|
regs.epr(index).write(|w| {
|
||||||
w.set_ep_type(convert_type(self.info.ep_type));
|
w.set_ep_type(convert_type(self.info.ep_type));
|
||||||
w.set_ea(self.info.addr.index() as _);
|
w.set_ea(self.info.addr.index() as _);
|
||||||
w.set_stat_tx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits()));
|
if self.info.ep_type == EndpointType::Isochronous {
|
||||||
|
w.set_stat_tx(Stat::from_bits(0)); // STAT_TX remains `VALID`.
|
||||||
|
} else {
|
||||||
|
w.set_stat_tx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits()));
|
||||||
|
}
|
||||||
w.set_stat_rx(Stat::from_bits(0));
|
w.set_stat_rx(Stat::from_bits(0));
|
||||||
w.set_ctr_rx(true); // don't clear
|
w.set_ctr_rx(true); // don't clear
|
||||||
w.set_ctr_tx(true); // don't clear
|
w.set_ctr_tx(true); // don't clear
|
||||||
|
@ -1071,6 +1071,21 @@ impl<'d> embassy_usb_driver::EndpointOut for Endpoint<'d, Out> {
|
|||||||
w.set_pktcnt(1);
|
w.set_pktcnt(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if self.info.ep_type == EndpointType::Isochronous {
|
||||||
|
// Isochronous endpoints must set the correct even/odd frame bit to
|
||||||
|
// correspond with the next frame's number.
|
||||||
|
let frame_number = self.regs.dsts().read().fnsof();
|
||||||
|
let frame_is_odd = frame_number & 0x01 == 1;
|
||||||
|
|
||||||
|
self.regs.doepctl(index).modify(|r| {
|
||||||
|
if frame_is_odd {
|
||||||
|
r.set_sd0pid_sevnfrm(true);
|
||||||
|
} else {
|
||||||
|
r.set_sd1pid_soddfrm(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Clear NAK to indicate we are ready to receive more data
|
// Clear NAK to indicate we are ready to receive more data
|
||||||
self.regs.doepctl(index).modify(|w| {
|
self.regs.doepctl(index).modify(|w| {
|
||||||
w.set_cnak(true);
|
w.set_cnak(true);
|
||||||
@ -1158,6 +1173,21 @@ impl<'d> embassy_usb_driver::EndpointIn for Endpoint<'d, In> {
|
|||||||
w.set_xfrsiz(buf.len() as _);
|
w.set_xfrsiz(buf.len() as _);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if self.info.ep_type == EndpointType::Isochronous {
|
||||||
|
// Isochronous endpoints must set the correct even/odd frame bit to
|
||||||
|
// correspond with the next frame's number.
|
||||||
|
let frame_number = self.regs.dsts().read().fnsof();
|
||||||
|
let frame_is_odd = frame_number & 0x01 == 1;
|
||||||
|
|
||||||
|
self.regs.diepctl(index).modify(|r| {
|
||||||
|
if frame_is_odd {
|
||||||
|
r.set_sd0pid_sevnfrm(true);
|
||||||
|
} else {
|
||||||
|
r.set_sd1pid_soddfrm(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Enable endpoint
|
// Enable endpoint
|
||||||
self.regs.diepctl(index).modify(|w| {
|
self.regs.diepctl(index).modify(|w| {
|
||||||
w.set_cnak(true);
|
w.set_cnak(true);
|
||||||
|
@ -795,15 +795,15 @@ pub mod regs {
|
|||||||
pub fn set_sd0pid_sevnfrm(&mut self, val: bool) {
|
pub fn set_sd0pid_sevnfrm(&mut self, val: bool) {
|
||||||
self.0 = (self.0 & !(0x01 << 28usize)) | (((val as u32) & 0x01) << 28usize);
|
self.0 = (self.0 & !(0x01 << 28usize)) | (((val as u32) & 0x01) << 28usize);
|
||||||
}
|
}
|
||||||
#[doc = "SODDFRM/SD1PID"]
|
#[doc = "SD1PID/SODDFRM"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub const fn soddfrm_sd1pid(&self) -> bool {
|
pub const fn sd1pid_soddfrm(&self) -> bool {
|
||||||
let val = (self.0 >> 29usize) & 0x01;
|
let val = (self.0 >> 29usize) & 0x01;
|
||||||
val != 0
|
val != 0
|
||||||
}
|
}
|
||||||
#[doc = "SODDFRM/SD1PID"]
|
#[doc = "SD1PID/SODDFRM"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_soddfrm_sd1pid(&mut self, val: bool) {
|
pub fn set_sd1pid_soddfrm(&mut self, val: bool) {
|
||||||
self.0 = (self.0 & !(0x01 << 29usize)) | (((val as u32) & 0x01) << 29usize);
|
self.0 = (self.0 & !(0x01 << 29usize)) | (((val as u32) & 0x01) << 29usize);
|
||||||
}
|
}
|
||||||
#[doc = "EPDIS"]
|
#[doc = "EPDIS"]
|
||||||
@ -1174,15 +1174,15 @@ pub mod regs {
|
|||||||
pub fn set_sd0pid_sevnfrm(&mut self, val: bool) {
|
pub fn set_sd0pid_sevnfrm(&mut self, val: bool) {
|
||||||
self.0 = (self.0 & !(0x01 << 28usize)) | (((val as u32) & 0x01) << 28usize);
|
self.0 = (self.0 & !(0x01 << 28usize)) | (((val as u32) & 0x01) << 28usize);
|
||||||
}
|
}
|
||||||
#[doc = "SODDFRM"]
|
#[doc = "SD1PID/SODDFRM"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub const fn soddfrm(&self) -> bool {
|
pub const fn sd1pid_soddfrm(&self) -> bool {
|
||||||
let val = (self.0 >> 29usize) & 0x01;
|
let val = (self.0 >> 29usize) & 0x01;
|
||||||
val != 0
|
val != 0
|
||||||
}
|
}
|
||||||
#[doc = "SODDFRM"]
|
#[doc = "SD1PID/SODDFRM"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_soddfrm(&mut self, val: bool) {
|
pub fn set_sd1pid_soddfrm(&mut self, val: bool) {
|
||||||
self.0 = (self.0 & !(0x01 << 29usize)) | (((val as u32) & 0x01) << 29usize);
|
self.0 = (self.0 & !(0x01 << 29usize)) | (((val as u32) & 0x01) << 29usize);
|
||||||
}
|
}
|
||||||
#[doc = "EPDIS"]
|
#[doc = "EPDIS"]
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use heapless::Vec;
|
use heapless::Vec;
|
||||||
|
|
||||||
use crate::config::MAX_HANDLER_COUNT;
|
use crate::config::MAX_HANDLER_COUNT;
|
||||||
use crate::descriptor::{BosWriter, DescriptorWriter};
|
use crate::descriptor::{BosWriter, DescriptorWriter, SynchronizationType, UsageType};
|
||||||
use crate::driver::{Driver, Endpoint, EndpointType};
|
use crate::driver::{Driver, Endpoint, EndpointInfo, EndpointType};
|
||||||
use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter};
|
use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter};
|
||||||
use crate::types::{InterfaceNumber, StringIndex};
|
use crate::types::{InterfaceNumber, StringIndex};
|
||||||
use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START};
|
use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START};
|
||||||
@ -414,7 +414,7 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
|
|||||||
/// Descriptors are written in the order builder functions are called. Note that some
|
/// Descriptors are written in the order builder functions are called. Note that some
|
||||||
/// classes care about the order.
|
/// classes care about the order.
|
||||||
pub fn descriptor(&mut self, descriptor_type: u8, descriptor: &[u8]) {
|
pub fn descriptor(&mut self, descriptor_type: u8, descriptor: &[u8]) {
|
||||||
self.builder.config_descriptor.write(descriptor_type, descriptor);
|
self.builder.config_descriptor.write(descriptor_type, descriptor, &[]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a custom Binary Object Store (BOS) descriptor to this alternate setting.
|
/// Add a custom Binary Object Store (BOS) descriptor to this alternate setting.
|
||||||
@ -422,26 +422,80 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
|
|||||||
self.builder.bos_descriptor.capability(capability_type, capability);
|
self.builder.bos_descriptor.capability(capability_type, capability);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn {
|
/// Write a custom endpoint descriptor for a certain endpoint.
|
||||||
|
///
|
||||||
|
/// This can be necessary, if the endpoint descriptors can only be written
|
||||||
|
/// after the endpoint was created. As an example, an endpoint descriptor
|
||||||
|
/// may contain the address of an endpoint that was allocated earlier.
|
||||||
|
pub fn endpoint_descriptor(
|
||||||
|
&mut self,
|
||||||
|
endpoint: &EndpointInfo,
|
||||||
|
synchronization_type: SynchronizationType,
|
||||||
|
usage_type: UsageType,
|
||||||
|
extra_fields: &[u8],
|
||||||
|
) {
|
||||||
|
self.builder
|
||||||
|
.config_descriptor
|
||||||
|
.endpoint(endpoint, synchronization_type, usage_type, extra_fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate an IN endpoint, without writing its descriptor.
|
||||||
|
///
|
||||||
|
/// Used for granular control over the order of endpoint and descriptor creation.
|
||||||
|
pub fn alloc_endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn {
|
||||||
let ep = self
|
let ep = self
|
||||||
.builder
|
.builder
|
||||||
.driver
|
.driver
|
||||||
.alloc_endpoint_in(ep_type, max_packet_size, interval_ms)
|
.alloc_endpoint_in(ep_type, max_packet_size, interval_ms)
|
||||||
.expect("alloc_endpoint_in failed");
|
.expect("alloc_endpoint_in failed");
|
||||||
|
|
||||||
self.builder.config_descriptor.endpoint(ep.info());
|
ep
|
||||||
|
}
|
||||||
|
|
||||||
|
fn endpoint_in(
|
||||||
|
&mut self,
|
||||||
|
ep_type: EndpointType,
|
||||||
|
max_packet_size: u16,
|
||||||
|
interval_ms: u8,
|
||||||
|
synchronization_type: SynchronizationType,
|
||||||
|
usage_type: UsageType,
|
||||||
|
extra_fields: &[u8],
|
||||||
|
) -> D::EndpointIn {
|
||||||
|
let ep = self.alloc_endpoint_in(ep_type, max_packet_size, interval_ms);
|
||||||
|
self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields);
|
||||||
|
|
||||||
ep
|
ep
|
||||||
}
|
}
|
||||||
|
|
||||||
fn endpoint_out(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut {
|
/// Allocate an OUT endpoint, without writing its descriptor.
|
||||||
|
///
|
||||||
|
/// Use for granular control over the order of endpoint and descriptor creation.
|
||||||
|
pub fn alloc_endpoint_out(
|
||||||
|
&mut self,
|
||||||
|
ep_type: EndpointType,
|
||||||
|
max_packet_size: u16,
|
||||||
|
interval_ms: u8,
|
||||||
|
) -> D::EndpointOut {
|
||||||
let ep = self
|
let ep = self
|
||||||
.builder
|
.builder
|
||||||
.driver
|
.driver
|
||||||
.alloc_endpoint_out(ep_type, max_packet_size, interval_ms)
|
.alloc_endpoint_out(ep_type, max_packet_size, interval_ms)
|
||||||
.expect("alloc_endpoint_out failed");
|
.expect("alloc_endpoint_out failed");
|
||||||
|
|
||||||
self.builder.config_descriptor.endpoint(ep.info());
|
ep
|
||||||
|
}
|
||||||
|
|
||||||
|
fn endpoint_out(
|
||||||
|
&mut self,
|
||||||
|
ep_type: EndpointType,
|
||||||
|
max_packet_size: u16,
|
||||||
|
interval_ms: u8,
|
||||||
|
synchronization_type: SynchronizationType,
|
||||||
|
usage_type: UsageType,
|
||||||
|
extra_fields: &[u8],
|
||||||
|
) -> D::EndpointOut {
|
||||||
|
let ep = self.alloc_endpoint_out(ep_type, max_packet_size, interval_ms);
|
||||||
|
self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields);
|
||||||
|
|
||||||
ep
|
ep
|
||||||
}
|
}
|
||||||
@ -451,7 +505,14 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
|
|||||||
/// Descriptors are written in the order builder functions are called. Note that some
|
/// Descriptors are written in the order builder functions are called. Note that some
|
||||||
/// classes care about the order.
|
/// classes care about the order.
|
||||||
pub fn endpoint_bulk_in(&mut self, max_packet_size: u16) -> D::EndpointIn {
|
pub fn endpoint_bulk_in(&mut self, max_packet_size: u16) -> D::EndpointIn {
|
||||||
self.endpoint_in(EndpointType::Bulk, max_packet_size, 0)
|
self.endpoint_in(
|
||||||
|
EndpointType::Bulk,
|
||||||
|
max_packet_size,
|
||||||
|
0,
|
||||||
|
SynchronizationType::NoSynchronization,
|
||||||
|
UsageType::DataEndpoint,
|
||||||
|
&[],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate a BULK OUT endpoint and write its descriptor.
|
/// Allocate a BULK OUT endpoint and write its descriptor.
|
||||||
@ -459,7 +520,14 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
|
|||||||
/// Descriptors are written in the order builder functions are called. Note that some
|
/// Descriptors are written in the order builder functions are called. Note that some
|
||||||
/// classes care about the order.
|
/// classes care about the order.
|
||||||
pub fn endpoint_bulk_out(&mut self, max_packet_size: u16) -> D::EndpointOut {
|
pub fn endpoint_bulk_out(&mut self, max_packet_size: u16) -> D::EndpointOut {
|
||||||
self.endpoint_out(EndpointType::Bulk, max_packet_size, 0)
|
self.endpoint_out(
|
||||||
|
EndpointType::Bulk,
|
||||||
|
max_packet_size,
|
||||||
|
0,
|
||||||
|
SynchronizationType::NoSynchronization,
|
||||||
|
UsageType::DataEndpoint,
|
||||||
|
&[],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate a INTERRUPT IN endpoint and write its descriptor.
|
/// Allocate a INTERRUPT IN endpoint and write its descriptor.
|
||||||
@ -467,24 +535,66 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
|
|||||||
/// Descriptors are written in the order builder functions are called. Note that some
|
/// Descriptors are written in the order builder functions are called. Note that some
|
||||||
/// classes care about the order.
|
/// classes care about the order.
|
||||||
pub fn endpoint_interrupt_in(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn {
|
pub fn endpoint_interrupt_in(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn {
|
||||||
self.endpoint_in(EndpointType::Interrupt, max_packet_size, interval_ms)
|
self.endpoint_in(
|
||||||
|
EndpointType::Interrupt,
|
||||||
|
max_packet_size,
|
||||||
|
interval_ms,
|
||||||
|
SynchronizationType::NoSynchronization,
|
||||||
|
UsageType::DataEndpoint,
|
||||||
|
&[],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate a INTERRUPT OUT endpoint and write its descriptor.
|
/// Allocate a INTERRUPT OUT endpoint and write its descriptor.
|
||||||
pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut {
|
pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut {
|
||||||
self.endpoint_out(EndpointType::Interrupt, max_packet_size, interval_ms)
|
self.endpoint_out(
|
||||||
|
EndpointType::Interrupt,
|
||||||
|
max_packet_size,
|
||||||
|
interval_ms,
|
||||||
|
SynchronizationType::NoSynchronization,
|
||||||
|
UsageType::DataEndpoint,
|
||||||
|
&[],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate a ISOCHRONOUS IN endpoint and write its descriptor.
|
/// Allocate a ISOCHRONOUS IN endpoint and write its descriptor.
|
||||||
///
|
///
|
||||||
/// Descriptors are written in the order builder functions are called. Note that some
|
/// Descriptors are written in the order builder functions are called. Note that some
|
||||||
/// classes care about the order.
|
/// classes care about the order.
|
||||||
pub fn endpoint_isochronous_in(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn {
|
pub fn endpoint_isochronous_in(
|
||||||
self.endpoint_in(EndpointType::Isochronous, max_packet_size, interval_ms)
|
&mut self,
|
||||||
|
max_packet_size: u16,
|
||||||
|
interval_ms: u8,
|
||||||
|
synchronization_type: SynchronizationType,
|
||||||
|
usage_type: UsageType,
|
||||||
|
extra_fields: &[u8],
|
||||||
|
) -> D::EndpointIn {
|
||||||
|
self.endpoint_in(
|
||||||
|
EndpointType::Isochronous,
|
||||||
|
max_packet_size,
|
||||||
|
interval_ms,
|
||||||
|
synchronization_type,
|
||||||
|
usage_type,
|
||||||
|
extra_fields,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor.
|
/// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor.
|
||||||
pub fn endpoint_isochronous_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut {
|
pub fn endpoint_isochronous_out(
|
||||||
self.endpoint_out(EndpointType::Isochronous, max_packet_size, interval_ms)
|
&mut self,
|
||||||
|
max_packet_size: u16,
|
||||||
|
interval_ms: u8,
|
||||||
|
synchronization_type: SynchronizationType,
|
||||||
|
usage_type: UsageType,
|
||||||
|
extra_fields: &[u8],
|
||||||
|
) -> D::EndpointOut {
|
||||||
|
self.endpoint_out(
|
||||||
|
EndpointType::Isochronous,
|
||||||
|
max_packet_size,
|
||||||
|
interval_ms,
|
||||||
|
synchronization_type,
|
||||||
|
usage_type,
|
||||||
|
extra_fields,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
//! Utilities for writing USB descriptors.
|
//! Utilities for writing USB descriptors.
|
||||||
|
use embassy_usb_driver::EndpointType;
|
||||||
|
|
||||||
use crate::builder::Config;
|
use crate::builder::Config;
|
||||||
use crate::driver::EndpointInfo;
|
use crate::driver::EndpointInfo;
|
||||||
@ -38,6 +39,40 @@ pub mod capability_type {
|
|||||||
pub const PLATFORM: u8 = 5;
|
pub const PLATFORM: u8 = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// USB endpoint synchronization type. The values of this enum can be directly
|
||||||
|
/// cast into `u8` to get the bmAttributes synchronization type bits.
|
||||||
|
/// Values other than `NoSynchronization` are only allowed on isochronous endpoints.
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum SynchronizationType {
|
||||||
|
/// No synchronization is used.
|
||||||
|
NoSynchronization = 0b00,
|
||||||
|
/// Unsynchronized, although sinks provide data rate feedback.
|
||||||
|
Asynchronous = 0b01,
|
||||||
|
/// Synchronized using feedback or feedforward data rate information.
|
||||||
|
Adaptive = 0b10,
|
||||||
|
/// Synchronized to the USB’s SOF.
|
||||||
|
Synchronous = 0b11,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// USB endpoint usage type. The values of this enum can be directly cast into
|
||||||
|
/// `u8` to get the bmAttributes usage type bits.
|
||||||
|
/// Values other than `DataEndpoint` are only allowed on isochronous endpoints.
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum UsageType {
|
||||||
|
/// Use the endpoint for regular data transfer.
|
||||||
|
DataEndpoint = 0b00,
|
||||||
|
/// Endpoint conveys explicit feedback information for one or more data endpoints.
|
||||||
|
FeedbackEndpoint = 0b01,
|
||||||
|
/// A data endpoint that also serves as an implicit feedback endpoint for one or more data endpoints.
|
||||||
|
ImplicitFeedbackDataEndpoint = 0b10,
|
||||||
|
/// Reserved usage type.
|
||||||
|
Reserved = 0b11,
|
||||||
|
}
|
||||||
|
|
||||||
/// A writer for USB descriptors.
|
/// A writer for USB descriptors.
|
||||||
pub(crate) struct DescriptorWriter<'a> {
|
pub(crate) struct DescriptorWriter<'a> {
|
||||||
pub buf: &'a mut [u8],
|
pub buf: &'a mut [u8],
|
||||||
@ -65,23 +100,26 @@ impl<'a> DescriptorWriter<'a> {
|
|||||||
self.position
|
self.position
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes an arbitrary (usually class-specific) descriptor.
|
/// Writes an arbitrary (usually class-specific) descriptor with optional extra fields.
|
||||||
pub fn write(&mut self, descriptor_type: u8, descriptor: &[u8]) {
|
pub fn write(&mut self, descriptor_type: u8, descriptor: &[u8], extra_fields: &[u8]) {
|
||||||
let length = descriptor.len();
|
let descriptor_length = descriptor.len();
|
||||||
|
let extra_fields_length = extra_fields.len();
|
||||||
|
let total_length = descriptor_length + extra_fields_length;
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
(self.position + 2 + length) <= self.buf.len() && (length + 2) <= 255,
|
(self.position + 2 + total_length) <= self.buf.len() && (total_length + 2) <= 255,
|
||||||
"Descriptor buffer full"
|
"Descriptor buffer full"
|
||||||
);
|
);
|
||||||
|
|
||||||
self.buf[self.position] = (length + 2) as u8;
|
self.buf[self.position] = (total_length + 2) as u8;
|
||||||
self.buf[self.position + 1] = descriptor_type;
|
self.buf[self.position + 1] = descriptor_type;
|
||||||
|
|
||||||
let start = self.position + 2;
|
let start = self.position + 2;
|
||||||
|
|
||||||
self.buf[start..start + length].copy_from_slice(descriptor);
|
self.buf[start..start + descriptor_length].copy_from_slice(descriptor);
|
||||||
|
self.buf[start + descriptor_length..start + total_length].copy_from_slice(extra_fields);
|
||||||
|
|
||||||
self.position = start + length;
|
self.position = start + total_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn configuration(&mut self, config: &Config) {
|
pub(crate) fn configuration(&mut self, config: &Config) {
|
||||||
@ -99,6 +137,7 @@ impl<'a> DescriptorWriter<'a> {
|
|||||||
| if config.supports_remote_wakeup { 0x20 } else { 0x00 }, // bmAttributes
|
| if config.supports_remote_wakeup { 0x20 } else { 0x00 }, // bmAttributes
|
||||||
(config.max_power / 2) as u8, // bMaxPower
|
(config.max_power / 2) as u8, // bMaxPower
|
||||||
],
|
],
|
||||||
|
&[],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,6 +184,7 @@ impl<'a> DescriptorWriter<'a> {
|
|||||||
function_protocol,
|
function_protocol,
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
|
&[],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,6 +235,7 @@ impl<'a> DescriptorWriter<'a> {
|
|||||||
interface_protocol, // bInterfaceProtocol
|
interface_protocol, // bInterfaceProtocol
|
||||||
str_index, // iInterface
|
str_index, // iInterface
|
||||||
],
|
],
|
||||||
|
&[],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,21 +245,50 @@ impl<'a> DescriptorWriter<'a> {
|
|||||||
///
|
///
|
||||||
/// * `endpoint` - Endpoint previously allocated with
|
/// * `endpoint` - Endpoint previously allocated with
|
||||||
/// [`UsbDeviceBuilder`](crate::bus::UsbDeviceBuilder).
|
/// [`UsbDeviceBuilder`](crate::bus::UsbDeviceBuilder).
|
||||||
pub fn endpoint(&mut self, endpoint: &EndpointInfo) {
|
/// * `synchronization_type` - The synchronization type of the endpoint.
|
||||||
|
/// * `usage_type` - The usage type of the endpoint.
|
||||||
|
/// * `extra_fields` - Additional, class-specific entries at the end of the endpoint descriptor.
|
||||||
|
pub fn endpoint(
|
||||||
|
&mut self,
|
||||||
|
endpoint: &EndpointInfo,
|
||||||
|
synchronization_type: SynchronizationType,
|
||||||
|
usage_type: UsageType,
|
||||||
|
extra_fields: &[u8],
|
||||||
|
) {
|
||||||
match self.num_endpoints_mark {
|
match self.num_endpoints_mark {
|
||||||
Some(mark) => self.buf[mark] += 1,
|
Some(mark) => self.buf[mark] += 1,
|
||||||
None => panic!("you can only call `endpoint` after `interface/interface_alt`."),
|
None => panic!("you can only call `endpoint` after `interface/interface_alt`."),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut bm_attributes = endpoint.ep_type as u8;
|
||||||
|
|
||||||
|
// Synchronization types other than `NoSynchronization`,
|
||||||
|
// and usage types other than `DataEndpoint`
|
||||||
|
// are only allowed for isochronous endpoints.
|
||||||
|
if endpoint.ep_type != EndpointType::Isochronous {
|
||||||
|
assert_eq!(synchronization_type, SynchronizationType::NoSynchronization);
|
||||||
|
assert_eq!(usage_type, UsageType::DataEndpoint);
|
||||||
|
} else {
|
||||||
|
if usage_type == UsageType::FeedbackEndpoint {
|
||||||
|
assert_eq!(synchronization_type, SynchronizationType::NoSynchronization)
|
||||||
|
}
|
||||||
|
|
||||||
|
let synchronization_bm_attibutes: u8 = (synchronization_type as u8) << 2;
|
||||||
|
let usage_bm_attibutes: u8 = (usage_type as u8) << 4;
|
||||||
|
|
||||||
|
bm_attributes |= usage_bm_attibutes | synchronization_bm_attibutes;
|
||||||
|
}
|
||||||
|
|
||||||
self.write(
|
self.write(
|
||||||
descriptor_type::ENDPOINT,
|
descriptor_type::ENDPOINT,
|
||||||
&[
|
&[
|
||||||
endpoint.addr.into(), // bEndpointAddress
|
endpoint.addr.into(), // bEndpointAddress
|
||||||
endpoint.ep_type as u8, // bmAttributes
|
bm_attributes, // bmAttributes
|
||||||
endpoint.max_packet_size as u8,
|
endpoint.max_packet_size as u8,
|
||||||
(endpoint.max_packet_size >> 8) as u8, // wMaxPacketSize
|
(endpoint.max_packet_size >> 8) as u8, // wMaxPacketSize
|
||||||
endpoint.interval_ms, // bInterval
|
endpoint.interval_ms, // bInterval
|
||||||
],
|
],
|
||||||
|
extra_fields,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,6 +388,7 @@ impl<'a> BosWriter<'a> {
|
|||||||
0x00, 0x00, // wTotalLength
|
0x00, 0x00, // wTotalLength
|
||||||
0x00, // bNumDeviceCaps
|
0x00, // bNumDeviceCaps
|
||||||
],
|
],
|
||||||
|
&[],
|
||||||
);
|
);
|
||||||
|
|
||||||
self.capability(capability_type::USB_2_0_EXTENSION, &[0; 4]);
|
self.capability(capability_type::USB_2_0_EXTENSION, &[0; 4]);
|
||||||
|
Loading…
Reference in New Issue
Block a user