Remove embassy_hal_common::usb.

The replacement is `embassy-usb`. There's a WIP driver for stm32 USBD in #709,
there's no WIP driver for stm32 USB_OTG. This means we're left without
USB_OTG support for now.

Reason for removing is I'm going to soon remove `embassy::io`, and
USB uses it. I don't want to spend time maintaining "dead" code
that is going to be removed. Volunteers welcome, either to update
old USB to the new IO, or write a USB_OTG driver fo the new USB.
This commit is contained in:
Dario Nieuwenhuis
2022-05-04 01:00:38 +02:00
parent 85c0525e01
commit fc32b3750c
14 changed files with 39 additions and 1342 deletions

View File

@ -10,7 +10,6 @@ mod macros;
pub mod peripheral;
pub mod ratio;
pub mod ring_buffer;
pub mod usb;
/// Low power blocking wait loop using WFE/SEV.
pub fn low_power_wait_until(mut condition: impl FnMut() -> bool) {

View File

@ -1,338 +0,0 @@
// Copied from https://github.com/mvirkkunen/usbd-serial
#![allow(dead_code)]
use core::convert::TryInto;
use core::mem;
use usb_device::class_prelude::*;
use usb_device::Result;
/// This should be used as `device_class` when building the `UsbDevice`.
pub const USB_CLASS_CDC: u8 = 0x02;
const USB_CLASS_CDC_DATA: u8 = 0x0a;
const CDC_SUBCLASS_ACM: u8 = 0x02;
const CDC_PROTOCOL_NONE: u8 = 0x00;
const CS_INTERFACE: u8 = 0x24;
const CDC_TYPE_HEADER: u8 = 0x00;
const CDC_TYPE_CALL_MANAGEMENT: u8 = 0x01;
const CDC_TYPE_ACM: u8 = 0x02;
const CDC_TYPE_UNION: u8 = 0x06;
const REQ_SEND_ENCAPSULATED_COMMAND: u8 = 0x00;
#[allow(unused)]
const REQ_GET_ENCAPSULATED_COMMAND: u8 = 0x01;
const REQ_SET_LINE_CODING: u8 = 0x20;
const REQ_GET_LINE_CODING: u8 = 0x21;
const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22;
/// Packet level implementation of a CDC-ACM serial port.
///
/// This class can be used directly and it has the least overhead due to directly reading and
/// writing USB packets with no intermediate buffers, but it will not act like a stream-like serial
/// port. The following constraints must be followed if you use this class directly:
///
/// - `read_packet` must be called with a buffer large enough to hold max_packet_size bytes, and the
/// method will return a `WouldBlock` error if there is no packet to be read.
/// - `write_packet` must not be called with a buffer larger than max_packet_size bytes, and the
/// method will return a `WouldBlock` error if the previous packet has not been sent yet.
/// - If you write a packet that is exactly max_packet_size bytes long, it won't be processed by the
/// host operating system until a subsequent shorter packet is sent. A zero-length packet (ZLP)
/// can be sent if there is no other data to send. This is because USB bulk transactions must be
/// terminated with a short packet, even if the bulk endpoint is used for stream-like data.
pub struct CdcAcmClass<'a, B: UsbBus> {
comm_if: InterfaceNumber,
comm_ep: EndpointIn<'a, B>,
data_if: InterfaceNumber,
read_ep: EndpointOut<'a, B>,
write_ep: EndpointIn<'a, B>,
line_coding: LineCoding,
dtr: bool,
rts: bool,
}
impl<B: UsbBus> CdcAcmClass<'_, B> {
/// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For
/// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64.
pub fn new(alloc: &UsbBusAllocator<B>, max_packet_size: u16) -> CdcAcmClass<'_, B> {
CdcAcmClass {
comm_if: alloc.interface(),
comm_ep: alloc.interrupt(8, 255),
data_if: alloc.interface(),
read_ep: alloc.bulk(max_packet_size),
write_ep: alloc.bulk(max_packet_size),
line_coding: LineCoding {
stop_bits: StopBits::One,
data_bits: 8,
parity_type: ParityType::None,
data_rate: 8_000,
},
dtr: false,
rts: false,
}
}
/// Gets the maximum packet size in bytes.
pub fn max_packet_size(&self) -> u16 {
// The size is the same for both endpoints.
self.read_ep.max_packet_size()
}
/// Gets the current line coding. The line coding contains information that's mainly relevant
/// for USB to UART serial port emulators, and can be ignored if not relevant.
pub fn line_coding(&self) -> &LineCoding {
&self.line_coding
}
/// Gets the DTR (data terminal ready) state
pub fn dtr(&self) -> bool {
self.dtr
}
/// Gets the RTS (request to send) state
pub fn rts(&self) -> bool {
self.rts
}
/// Writes a single packet into the IN endpoint.
pub fn write_packet(&mut self, data: &[u8]) -> Result<usize> {
self.write_ep.write(data)
}
/// Reads a single packet from the OUT endpoint.
pub fn read_packet(&mut self, data: &mut [u8]) -> Result<usize> {
self.read_ep.read(data)
}
/// Gets the address of the IN endpoint.
pub fn write_ep_address(&self) -> EndpointAddress {
self.write_ep.address()
}
/// Gets the address of the OUT endpoint.
pub fn read_ep_address(&self) -> EndpointAddress {
self.read_ep.address()
}
}
impl<B: UsbBus> UsbClass<B> for CdcAcmClass<'_, B> {
fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> {
writer.iad(
self.comm_if,
2,
USB_CLASS_CDC,
CDC_SUBCLASS_ACM,
CDC_PROTOCOL_NONE,
)?;
writer.interface(
self.comm_if,
USB_CLASS_CDC,
CDC_SUBCLASS_ACM,
CDC_PROTOCOL_NONE,
)?;
writer.write(
CS_INTERFACE,
&[
CDC_TYPE_HEADER, // bDescriptorSubtype
0x10,
0x01, // bcdCDC (1.10)
],
)?;
writer.write(
CS_INTERFACE,
&[
CDC_TYPE_ACM, // bDescriptorSubtype
0x00, // bmCapabilities
],
)?;
writer.write(
CS_INTERFACE,
&[
CDC_TYPE_UNION, // bDescriptorSubtype
self.comm_if.into(), // bControlInterface
self.data_if.into(), // bSubordinateInterface
],
)?;
writer.write(
CS_INTERFACE,
&[
CDC_TYPE_CALL_MANAGEMENT, // bDescriptorSubtype
0x00, // bmCapabilities
self.data_if.into(), // bDataInterface
],
)?;
writer.endpoint(&self.comm_ep)?;
writer.interface(self.data_if, USB_CLASS_CDC_DATA, 0x00, 0x00)?;
writer.endpoint(&self.write_ep)?;
writer.endpoint(&self.read_ep)?;
Ok(())
}
fn reset(&mut self) {
self.line_coding = LineCoding::default();
self.dtr = false;
self.rts = false;
}
fn control_in(&mut self, xfer: ControlIn<B>) {
let req = xfer.request();
if !(req.request_type == control::RequestType::Class
&& req.recipient == control::Recipient::Interface
&& req.index == u8::from(self.comm_if) as u16)
{
return;
}
match req.request {
// REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below.
REQ_GET_LINE_CODING if req.length == 7 => {
xfer.accept(|data| {
data[0..4].copy_from_slice(&self.line_coding.data_rate.to_le_bytes());
data[4] = self.line_coding.stop_bits as u8;
data[5] = self.line_coding.parity_type as u8;
data[6] = self.line_coding.data_bits;
Ok(7)
})
.ok();
}
_ => {
xfer.reject().ok();
}
}
}
fn control_out(&mut self, xfer: ControlOut<B>) {
let req = xfer.request();
if !(req.request_type == control::RequestType::Class
&& req.recipient == control::Recipient::Interface
&& req.index == u8::from(self.comm_if) as u16)
{
return;
}
match req.request {
REQ_SEND_ENCAPSULATED_COMMAND => {
// We don't actually support encapsulated commands but pretend we do for standards
// compatibility.
xfer.accept().ok();
}
REQ_SET_LINE_CODING if xfer.data().len() >= 7 => {
self.line_coding.data_rate =
u32::from_le_bytes(xfer.data()[0..4].try_into().unwrap());
self.line_coding.stop_bits = xfer.data()[4].into();
self.line_coding.parity_type = xfer.data()[5].into();
self.line_coding.data_bits = xfer.data()[6];
xfer.accept().ok();
}
REQ_SET_CONTROL_LINE_STATE => {
self.dtr = (req.value & 0x0001) != 0;
self.rts = (req.value & 0x0002) != 0;
xfer.accept().ok();
}
_ => {
xfer.reject().ok();
}
};
}
}
/// Number of stop bits for LineCoding
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum StopBits {
/// 1 stop bit
One = 0,
/// 1.5 stop bits
OnePointFive = 1,
/// 2 stop bits
Two = 2,
}
impl From<u8> for StopBits {
fn from(value: u8) -> Self {
if value <= 2 {
unsafe { mem::transmute(value) }
} else {
StopBits::One
}
}
}
/// Parity for LineCoding
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum ParityType {
None = 0,
Odd = 1,
Event = 2,
Mark = 3,
Space = 4,
}
impl From<u8> for ParityType {
fn from(value: u8) -> Self {
if value <= 4 {
unsafe { mem::transmute(value) }
} else {
ParityType::None
}
}
}
/// Line coding parameters
///
/// This is provided by the host for specifying the standard UART parameters such as baud rate. Can
/// be ignored if you don't plan to interface with a physical UART.
pub struct LineCoding {
stop_bits: StopBits,
data_bits: u8,
parity_type: ParityType,
data_rate: u32,
}
impl LineCoding {
/// Gets the number of stop bits for UART communication.
pub fn stop_bits(&self) -> StopBits {
self.stop_bits
}
/// Gets the number of data bits for UART communication.
pub fn data_bits(&self) -> u8 {
self.data_bits
}
/// Gets the parity type for UART communication.
pub fn parity_type(&self) -> ParityType {
self.parity_type
}
/// Gets the data rate in bits per second for UART communication.
pub fn data_rate(&self) -> u32 {
self.data_rate
}
}
impl Default for LineCoding {
fn default() -> Self {
LineCoding {
stop_bits: StopBits::One,
data_bits: 8,
parity_type: ParityType::None,
data_rate: 8_000,
}
}
}

View File

@ -1,267 +0,0 @@
use core::cell::RefCell;
use core::marker::PhantomData;
use core::pin::Pin;
use usb_device::bus::UsbBus;
use usb_device::class::UsbClass;
use usb_device::device::UsbDevice;
mod cdc_acm;
pub mod usb_serial;
use crate::peripheral::{PeripheralMutex, PeripheralState, StateStorage};
use embassy::interrupt::Interrupt;
pub use usb_serial::{ReadInterface, UsbSerial, WriteInterface};
/// Marker trait to mark an interrupt to be used with the [`Usb`] abstraction.
pub unsafe trait USBInterrupt: Interrupt + Send {}
pub struct State<'bus, B, T, I>(StateStorage<StateInner<'bus, B, T, I>>)
where
B: UsbBus,
T: ClassSet<B>,
I: USBInterrupt;
impl<'bus, B, T, I> State<'bus, B, T, I>
where
B: UsbBus,
T: ClassSet<B>,
I: USBInterrupt,
{
pub fn new() -> Self {
Self(StateStorage::new())
}
}
pub(crate) struct StateInner<'bus, B, T, I>
where
B: UsbBus,
T: ClassSet<B>,
I: USBInterrupt,
{
device: UsbDevice<'bus, B>,
pub(crate) classes: T,
_interrupt: PhantomData<I>,
}
pub struct Usb<'bus, B, T, I>
where
B: UsbBus,
T: ClassSet<B>,
I: USBInterrupt,
{
// Don't you dare moving out `PeripheralMutex`
inner: RefCell<PeripheralMutex<'bus, StateInner<'bus, B, T, I>>>,
}
impl<'bus, B, T, I> Usb<'bus, B, T, I>
where
B: UsbBus,
T: ClassSet<B>,
I: USBInterrupt,
{
/// safety: the returned instance is not leak-safe
pub unsafe fn new<S: IntoClassSet<B, T>>(
state: &'bus mut State<'bus, B, T, I>,
device: UsbDevice<'bus, B>,
class_set: S,
irq: I,
) -> Self {
let mutex = PeripheralMutex::new_unchecked(irq, &mut state.0, || StateInner {
device,
classes: class_set.into_class_set(),
_interrupt: PhantomData,
});
Self {
inner: RefCell::new(mutex),
}
}
}
impl<'bus, 'c, B, T, I> Usb<'bus, B, T, I>
where
B: UsbBus,
T: ClassSet<B> + SerialState<'bus, 'c, B, Index0>,
I: USBInterrupt,
{
/// Take a serial class that was passed as the first class in a tuple
pub fn take_serial_0<'a>(
self: Pin<&'a Self>,
) -> (
ReadInterface<'a, 'bus, 'c, Index0, B, T, I>,
WriteInterface<'a, 'bus, 'c, Index0, B, T, I>,
) {
let this = self.get_ref();
let r = ReadInterface {
inner: &this.inner,
_buf_lifetime: PhantomData,
_index: PhantomData,
};
let w = WriteInterface {
inner: &this.inner,
_buf_lifetime: PhantomData,
_index: PhantomData,
};
(r, w)
}
}
impl<'bus, 'c, B, T, I> Usb<'bus, B, T, I>
where
B: UsbBus,
T: ClassSet<B> + SerialState<'bus, 'c, B, Index1>,
I: USBInterrupt,
{
/// Take a serial class that was passed as the second class in a tuple
pub fn take_serial_1<'a>(
self: Pin<&'a Self>,
) -> (
ReadInterface<'a, 'bus, 'c, Index1, B, T, I>,
WriteInterface<'a, 'bus, 'c, Index1, B, T, I>,
) {
let this = self.get_ref();
let r = ReadInterface {
inner: &this.inner,
_buf_lifetime: PhantomData,
_index: PhantomData,
};
let w = WriteInterface {
inner: &this.inner,
_buf_lifetime: PhantomData,
_index: PhantomData,
};
(r, w)
}
}
impl<'bus, B, T, I> PeripheralState for StateInner<'bus, B, T, I>
where
B: UsbBus,
T: ClassSet<B>,
I: USBInterrupt,
{
type Interrupt = I;
fn on_interrupt(&mut self) {
self.classes.poll_all(&mut self.device);
}
}
pub trait ClassSet<B: UsbBus>: Send {
fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool;
}
pub trait IntoClassSet<B: UsbBus, C: ClassSet<B>> {
fn into_class_set(self) -> C;
}
pub struct ClassSet1<B, C1>
where
B: UsbBus,
C1: UsbClass<B>,
{
class: C1,
_bus: PhantomData<B>,
}
pub struct ClassSet2<B, C1, C2>
where
B: UsbBus,
C1: UsbClass<B>,
C2: UsbClass<B>,
{
class1: C1,
class2: C2,
_bus: PhantomData<B>,
}
/// The first class into a [`ClassSet`]
pub struct Index0;
/// The second class into a [`ClassSet`]
pub struct Index1;
impl<B, C1> ClassSet<B> for ClassSet1<B, C1>
where
B: UsbBus + Send,
C1: UsbClass<B> + Send,
{
fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool {
device.poll(&mut [&mut self.class])
}
}
impl<B, C1, C2> ClassSet<B> for ClassSet2<B, C1, C2>
where
B: UsbBus + Send,
C1: UsbClass<B> + Send,
C2: UsbClass<B> + Send,
{
fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool {
device.poll(&mut [&mut self.class1, &mut self.class2])
}
}
impl<B, C1> IntoClassSet<B, ClassSet1<B, C1>> for C1
where
B: UsbBus + Send,
C1: UsbClass<B> + Send,
{
fn into_class_set(self) -> ClassSet1<B, C1> {
ClassSet1 {
class: self,
_bus: PhantomData,
}
}
}
impl<B, C1, C2> IntoClassSet<B, ClassSet2<B, C1, C2>> for (C1, C2)
where
B: UsbBus + Send,
C1: UsbClass<B> + Send,
C2: UsbClass<B> + Send,
{
fn into_class_set(self) -> ClassSet2<B, C1, C2> {
ClassSet2 {
class1: self.0,
class2: self.1,
_bus: PhantomData,
}
}
}
/// Trait for a USB State that has a serial class inside
pub trait SerialState<'bus, 'a, B: UsbBus, I> {
fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B>;
}
impl<'bus, 'a, B: UsbBus> SerialState<'bus, 'a, B, Index0>
for ClassSet1<B, UsbSerial<'bus, 'a, B>>
{
fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B> {
&mut self.class
}
}
impl<'bus, 'a, B, C2> SerialState<'bus, 'a, B, Index0> for ClassSet2<B, UsbSerial<'bus, 'a, B>, C2>
where
B: UsbBus,
C2: UsbClass<B>,
{
fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B> {
&mut self.class1
}
}
impl<'bus, 'a, B, C1> SerialState<'bus, 'a, B, Index1> for ClassSet2<B, C1, UsbSerial<'bus, 'a, B>>
where
B: UsbBus,
C1: UsbClass<B>,
{
fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B> {
&mut self.class2
}
}

View File

@ -1,345 +0,0 @@
use core::cell::RefCell;
use core::marker::{PhantomData, Unpin};
use core::pin::Pin;
use core::task::{Context, Poll};
use embassy::io::{self, AsyncBufRead, AsyncWrite};
use embassy::waitqueue::WakerRegistration;
use usb_device::bus::UsbBus;
use usb_device::class_prelude::*;
use usb_device::UsbError;
use super::cdc_acm::CdcAcmClass;
use super::StateInner;
use crate::peripheral::PeripheralMutex;
use crate::ring_buffer::RingBuffer;
use crate::usb::{ClassSet, SerialState, USBInterrupt};
pub struct ReadInterface<'a, 'bus, 'c, I, B, T, INT>
where
I: Unpin,
B: UsbBus,
T: SerialState<'bus, 'c, B, I> + ClassSet<B>,
INT: USBInterrupt,
{
// Don't you dare moving out `PeripheralMutex`
pub(crate) inner: &'a RefCell<PeripheralMutex<'bus, StateInner<'bus, B, T, INT>>>,
pub(crate) _buf_lifetime: PhantomData<&'c T>,
pub(crate) _index: PhantomData<I>,
}
/// Write interface for USB CDC_ACM
///
/// This interface is buffered, meaning that after the write returns the bytes might not be fully
/// on the wire just yet
pub struct WriteInterface<'a, 'bus, 'c, I, B, T, INT>
where
I: Unpin,
B: UsbBus,
T: SerialState<'bus, 'c, B, I> + ClassSet<B>,
INT: USBInterrupt,
{
// Don't you dare moving out `PeripheralMutex`
pub(crate) inner: &'a RefCell<PeripheralMutex<'bus, StateInner<'bus, B, T, INT>>>,
pub(crate) _buf_lifetime: PhantomData<&'c T>,
pub(crate) _index: PhantomData<I>,
}
impl<'a, 'bus, 'c, I, B, T, INT> AsyncBufRead for ReadInterface<'a, 'bus, 'c, I, B, T, INT>
where
I: Unpin,
B: UsbBus,
T: SerialState<'bus, 'c, B, I> + ClassSet<B>,
INT: USBInterrupt,
{
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
let this = self.get_mut();
let mut mutex = this.inner.borrow_mut();
mutex.with(|state| {
let serial = state.classes.get_serial();
let serial = Pin::new(serial);
match serial.poll_fill_buf(cx) {
Poll::Ready(Ok(buf)) => {
let buf: &[u8] = buf;
// NOTE(unsafe) This part of the buffer won't be modified until the user calls
// consume, which will invalidate this ref
let buf: &[u8] = unsafe { core::mem::transmute(buf) };
Poll::Ready(Ok(buf))
}
Poll::Ready(Err(_)) => Poll::Ready(Err(io::Error::Other)),
Poll::Pending => Poll::Pending,
}
})
}
fn consume(self: Pin<&mut Self>, amt: usize) {
let this = self.get_mut();
let mut mutex = this.inner.borrow_mut();
mutex.with(|state| {
let serial = state.classes.get_serial();
let serial = Pin::new(serial);
serial.consume(amt);
})
}
}
impl<'a, 'bus, 'c, I, B, T, INT> AsyncWrite for WriteInterface<'a, 'bus, 'c, I, B, T, INT>
where
I: Unpin,
B: UsbBus,
T: SerialState<'bus, 'c, B, I> + ClassSet<B>,
INT: USBInterrupt,
{
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
let this = self.get_mut();
let mut mutex = this.inner.borrow_mut();
mutex.with(|state| {
let serial = state.classes.get_serial();
let serial = Pin::new(serial);
serial.poll_write(cx, buf)
})
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
let this = self.get_mut();
let mut mutex = this.inner.borrow_mut();
mutex.with(|state| {
let serial = state.classes.get_serial();
let serial = Pin::new(serial);
serial.poll_flush(cx)
})
}
}
pub struct UsbSerial<'bus, 'a, B: UsbBus> {
inner: CdcAcmClass<'bus, B>,
read_buf: RingBuffer<'a>,
write_buf: RingBuffer<'a>,
read_waker: WakerRegistration,
write_waker: WakerRegistration,
write_state: WriteState,
read_error: bool,
write_error: bool,
}
impl<'bus, 'a, B: UsbBus> AsyncBufRead for UsbSerial<'bus, 'a, B> {
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
let this = self.get_mut();
if this.read_error {
this.read_error = false;
return Poll::Ready(Err(io::Error::Other));
}
let buf = this.read_buf.pop_buf();
if buf.is_empty() {
this.read_waker.register(cx.waker());
return Poll::Pending;
}
Poll::Ready(Ok(buf))
}
fn consume(self: Pin<&mut Self>, amt: usize) {
self.get_mut().read_buf.pop(amt);
}
}
impl<'bus, 'a, B: UsbBus> AsyncWrite for UsbSerial<'bus, 'a, B> {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
let this = self.get_mut();
if this.write_error {
this.write_error = false;
return Poll::Ready(Err(io::Error::Other));
}
let write_buf = this.write_buf.push_buf();
if write_buf.is_empty() {
trace!("buf full, registering waker");
this.write_waker.register(cx.waker());
return Poll::Pending;
}
let count = write_buf.len().min(buf.len());
write_buf[..count].copy_from_slice(&buf[..count]);
this.write_buf.push(count);
this.flush_write();
Poll::Ready(Ok(count))
}
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
}
/// Keeps track of the type of the last written packet.
enum WriteState {
/// No packets in-flight
Idle,
/// Short packet currently in-flight
Short,
/// Full packet current in-flight. A full packet must be followed by a short packet for the host
/// OS to see the transaction. The data is the number of subsequent full packets sent so far. A
/// short packet is forced every SHORT_PACKET_INTERVAL packets so that the OS sees data in a
/// timely manner.
Full(usize),
}
impl<'bus, 'a, B: UsbBus> UsbSerial<'bus, 'a, B> {
pub fn new(
alloc: &'bus UsbBusAllocator<B>,
read_buf: &'a mut [u8],
write_buf: &'a mut [u8],
) -> Self {
Self {
inner: CdcAcmClass::new(alloc, 64),
read_buf: RingBuffer::new(read_buf),
write_buf: RingBuffer::new(write_buf),
read_waker: WakerRegistration::new(),
write_waker: WakerRegistration::new(),
write_state: WriteState::Idle,
read_error: false,
write_error: false,
}
}
fn flush_write(&mut self) {
/// If this many full size packets have been sent in a row, a short packet will be sent so that the
/// host sees the data in a timely manner.
const SHORT_PACKET_INTERVAL: usize = 10;
let full_size_packets = match self.write_state {
WriteState::Full(c) => c,
_ => 0,
};
let ep_size = self.inner.max_packet_size() as usize;
let max_size = if full_size_packets > SHORT_PACKET_INTERVAL {
ep_size - 1
} else {
ep_size
};
let buf = {
let buf = self.write_buf.pop_buf();
if buf.len() > max_size {
&buf[..max_size]
} else {
buf
}
};
if !buf.is_empty() {
trace!("writing packet len {}", buf.len());
let count = match self.inner.write_packet(buf) {
Ok(c) => {
trace!("write packet: OK {}", c);
c
}
Err(UsbError::WouldBlock) => {
trace!("write packet: WouldBlock");
0
}
Err(_) => {
trace!("write packet: error");
self.write_error = true;
return;
}
};
if buf.len() == ep_size {
self.write_state = WriteState::Full(full_size_packets + 1);
} else {
self.write_state = WriteState::Short;
}
self.write_buf.pop(count);
} else if full_size_packets > 0 {
trace!("writing empty packet");
match self.inner.write_packet(&[]) {
Ok(_) => {
trace!("write empty packet: OK");
}
Err(UsbError::WouldBlock) => {
trace!("write empty packet: WouldBlock");
return;
}
Err(_) => {
trace!("write empty packet: Error");
self.write_error = true;
return;
}
}
self.write_state = WriteState::Idle;
}
}
}
impl<B> UsbClass<B> for UsbSerial<'_, '_, B>
where
B: UsbBus,
{
fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<(), UsbError> {
self.inner.get_configuration_descriptors(writer)
}
fn reset(&mut self) {
self.inner.reset();
self.read_buf.clear();
self.write_buf.clear();
self.write_state = WriteState::Idle;
self.read_waker.wake();
self.write_waker.wake();
}
fn endpoint_in_complete(&mut self, addr: EndpointAddress) {
trace!("DONE endpoint_in_complete");
if addr == self.inner.write_ep_address() {
trace!("DONE writing packet, waking");
self.write_waker.wake();
self.flush_write();
}
}
fn endpoint_out(&mut self, addr: EndpointAddress) {
if addr == self.inner.read_ep_address() {
let buf = self.read_buf.push_buf();
let count = match self.inner.read_packet(buf) {
Ok(c) => c,
Err(UsbError::WouldBlock) => 0,
Err(_) => {
self.read_error = true;
return;
}
};
if count > 0 {
self.read_buf.push(count);
self.read_waker.wake();
}
}
}
fn control_in(&mut self, xfer: ControlIn<B>) {
self.inner.control_in(xfer);
}
fn control_out(&mut self, xfer: ControlOut<B>) {
self.inner.control_out(xfer);
}
}