diff --git a/embassy-hal-common/Cargo.toml b/embassy-hal-common/Cargo.toml index 2028b0e0c..fee6da6ff 100644 --- a/embassy-hal-common/Cargo.toml +++ b/embassy-hal-common/Cargo.toml @@ -12,5 +12,4 @@ embassy = { version = "0.1.0", path = "../embassy" } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } cortex-m = "0.7.3" -usb-device = "0.2.8" num-traits = { version = "0.2.14", default-features = false } diff --git a/embassy-hal-common/src/lib.rs b/embassy-hal-common/src/lib.rs index 1af30c6b4..6ee2ccd59 100644 --- a/embassy-hal-common/src/lib.rs +++ b/embassy-hal-common/src/lib.rs @@ -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) { diff --git a/embassy-hal-common/src/usb/cdc_acm.rs b/embassy-hal-common/src/usb/cdc_acm.rs deleted file mode 100644 index 5a85b3846..000000000 --- a/embassy-hal-common/src/usb/cdc_acm.rs +++ /dev/null @@ -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 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, 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 { - self.write_ep.write(data) - } - - /// Reads a single packet from the OUT endpoint. - pub fn read_packet(&mut self, data: &mut [u8]) -> Result { - 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 UsbClass 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) { - 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) { - 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 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 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, - } - } -} diff --git a/embassy-hal-common/src/usb/mod.rs b/embassy-hal-common/src/usb/mod.rs deleted file mode 100644 index bab72d8b6..000000000 --- a/embassy-hal-common/src/usb/mod.rs +++ /dev/null @@ -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>) -where - B: UsbBus, - T: ClassSet, - I: USBInterrupt; - -impl<'bus, B, T, I> State<'bus, B, T, I> -where - B: UsbBus, - T: ClassSet, - I: USBInterrupt, -{ - pub fn new() -> Self { - Self(StateStorage::new()) - } -} - -pub(crate) struct StateInner<'bus, B, T, I> -where - B: UsbBus, - T: ClassSet, - I: USBInterrupt, -{ - device: UsbDevice<'bus, B>, - pub(crate) classes: T, - _interrupt: PhantomData, -} - -pub struct Usb<'bus, B, T, I> -where - B: UsbBus, - T: ClassSet, - I: USBInterrupt, -{ - // Don't you dare moving out `PeripheralMutex` - inner: RefCell>>, -} - -impl<'bus, B, T, I> Usb<'bus, B, T, I> -where - B: UsbBus, - T: ClassSet, - I: USBInterrupt, -{ - /// safety: the returned instance is not leak-safe - pub unsafe fn new>( - 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 + 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 + 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, - I: USBInterrupt, -{ - type Interrupt = I; - fn on_interrupt(&mut self) { - self.classes.poll_all(&mut self.device); - } -} - -pub trait ClassSet: Send { - fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool; -} - -pub trait IntoClassSet> { - fn into_class_set(self) -> C; -} - -pub struct ClassSet1 -where - B: UsbBus, - C1: UsbClass, -{ - class: C1, - _bus: PhantomData, -} - -pub struct ClassSet2 -where - B: UsbBus, - C1: UsbClass, - C2: UsbClass, -{ - class1: C1, - class2: C2, - _bus: PhantomData, -} - -/// The first class into a [`ClassSet`] -pub struct Index0; - -/// The second class into a [`ClassSet`] -pub struct Index1; - -impl ClassSet for ClassSet1 -where - B: UsbBus + Send, - C1: UsbClass + Send, -{ - fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool { - device.poll(&mut [&mut self.class]) - } -} - -impl ClassSet for ClassSet2 -where - B: UsbBus + Send, - C1: UsbClass + Send, - C2: UsbClass + Send, -{ - fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool { - device.poll(&mut [&mut self.class1, &mut self.class2]) - } -} - -impl IntoClassSet> for C1 -where - B: UsbBus + Send, - C1: UsbClass + Send, -{ - fn into_class_set(self) -> ClassSet1 { - ClassSet1 { - class: self, - _bus: PhantomData, - } - } -} - -impl IntoClassSet> for (C1, C2) -where - B: UsbBus + Send, - C1: UsbClass + Send, - C2: UsbClass + Send, -{ - fn into_class_set(self) -> ClassSet2 { - 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> -{ - 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, C2> -where - B: UsbBus, - C2: UsbClass, -{ - 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> -where - B: UsbBus, - C1: UsbClass, -{ - fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B> { - &mut self.class2 - } -} diff --git a/embassy-hal-common/src/usb/usb_serial.rs b/embassy-hal-common/src/usb/usb_serial.rs deleted file mode 100644 index 94f687890..000000000 --- a/embassy-hal-common/src/usb/usb_serial.rs +++ /dev/null @@ -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, - INT: USBInterrupt, -{ - // Don't you dare moving out `PeripheralMutex` - pub(crate) inner: &'a RefCell>>, - pub(crate) _buf_lifetime: PhantomData<&'c T>, - pub(crate) _index: PhantomData, -} - -/// 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, - INT: USBInterrupt, -{ - // Don't you dare moving out `PeripheralMutex` - pub(crate) inner: &'a RefCell>>, - pub(crate) _buf_lifetime: PhantomData<&'c T>, - pub(crate) _index: PhantomData, -} - -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, - INT: USBInterrupt, -{ - fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - 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, - INT: USBInterrupt, -{ - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - 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> { - 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> { - 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> { - 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> { - 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, - 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 UsbClass 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) { - self.inner.control_in(xfer); - } - - fn control_out(&mut self, xfer: ControlOut) { - self.inner.control_out(xfer); - } -} diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 8152b07d4..ea9ac3b59 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -53,7 +53,6 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa rand_core = "0.6.3" sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/thalesfragoso/embedded-sdmmc-rs", branch = "async", optional = true } -synopsys-usb-otg = { version = "0.3", features = ["cortex-m", "hs"], optional = true } critical-section = "0.2.5" bare-metal = "1.0.0" atomic-polyfill = "0.1.5" @@ -76,7 +75,6 @@ net = ["embassy-net", "vcell"] memory-x = ["stm32-metapac/memory-x"] subghz = [] exti = [] -usb-otg = ["synopsys-usb-otg"] # Features starting with `_` are for internal use only. They're not intended # to be enabled by other crates, and are not covered by semver guarantees. diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index aa3e64131..db757764f 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -279,22 +279,22 @@ fn main() { (("dcmi", "HSYNC"), (quote!(crate::dcmi::HSyncPin), quote!())), (("dcmi", "VSYNC"), (quote!(crate::dcmi::VSyncPin), quote!())), (("dcmi", "PIXCLK"), (quote!(crate::dcmi::PixClkPin), quote!())), - (("otgfs", "DP"), (quote!(crate::usb_otg::DpPin), quote!(#[cfg(feature="usb-otg")]))), - (("otgfs", "DM"), (quote!(crate::usb_otg::DmPin), quote!(#[cfg(feature="usb-otg")]))), - (("otghs", "DP"), (quote!(crate::usb_otg::DpPin), quote!(#[cfg(feature="usb-otg")]))), - (("otghs", "DM"), (quote!(crate::usb_otg::DmPin), quote!(#[cfg(feature="usb-otg")]))), - (("otghs", "ULPI_CK"), (quote!(crate::usb_otg::UlpiClkPin), quote!(#[cfg(feature="usb-otg")]))), - (("otghs", "ULPI_DIR"), (quote!(crate::usb_otg::UlpiDirPin), quote!(#[cfg(feature="usb-otg")]))), - (("otghs", "ULPI_NXT"), (quote!(crate::usb_otg::UlpiNxtPin), quote!(#[cfg(feature="usb-otg")]))), - (("otghs", "ULPI_STP"), (quote!(crate::usb_otg::UlpiStpPin), quote!(#[cfg(feature="usb-otg")]))), - (("otghs", "ULPI_D0"), (quote!(crate::usb_otg::UlpiD0Pin), quote!(#[cfg(feature="usb-otg")]))), - (("otghs", "ULPI_D1"), (quote!(crate::usb_otg::UlpiD1Pin), quote!(#[cfg(feature="usb-otg")]))), - (("otghs", "ULPI_D2"), (quote!(crate::usb_otg::UlpiD2Pin), quote!(#[cfg(feature="usb-otg")]))), - (("otghs", "ULPI_D3"), (quote!(crate::usb_otg::UlpiD3Pin), quote!(#[cfg(feature="usb-otg")]))), - (("otghs", "ULPI_D4"), (quote!(crate::usb_otg::UlpiD4Pin), quote!(#[cfg(feature="usb-otg")]))), - (("otghs", "ULPI_D5"), (quote!(crate::usb_otg::UlpiD5Pin), quote!(#[cfg(feature="usb-otg")]))), - (("otghs", "ULPI_D6"), (quote!(crate::usb_otg::UlpiD6Pin), quote!(#[cfg(feature="usb-otg")]))), - (("otghs", "ULPI_D7"), (quote!(crate::usb_otg::UlpiD7Pin), quote!(#[cfg(feature="usb-otg")]))), + (("otgfs", "DP"), (quote!(crate::usb_otg::DpPin), quote!())), + (("otgfs", "DM"), (quote!(crate::usb_otg::DmPin), quote!())), + (("otghs", "DP"), (quote!(crate::usb_otg::DpPin), quote!())), + (("otghs", "DM"), (quote!(crate::usb_otg::DmPin), quote!())), + (("otghs", "ULPI_CK"), (quote!(crate::usb_otg::UlpiClkPin), quote!())), + (("otghs", "ULPI_DIR"), (quote!(crate::usb_otg::UlpiDirPin), quote!())), + (("otghs", "ULPI_NXT"), (quote!(crate::usb_otg::UlpiNxtPin), quote!())), + (("otghs", "ULPI_STP"), (quote!(crate::usb_otg::UlpiStpPin), quote!())), + (("otghs", "ULPI_D0"), (quote!(crate::usb_otg::UlpiD0Pin), quote!())), + (("otghs", "ULPI_D1"), (quote!(crate::usb_otg::UlpiD1Pin), quote!())), + (("otghs", "ULPI_D2"), (quote!(crate::usb_otg::UlpiD2Pin), quote!())), + (("otghs", "ULPI_D3"), (quote!(crate::usb_otg::UlpiD3Pin), quote!())), + (("otghs", "ULPI_D4"), (quote!(crate::usb_otg::UlpiD4Pin), quote!())), + (("otghs", "ULPI_D5"), (quote!(crate::usb_otg::UlpiD5Pin), quote!())), + (("otghs", "ULPI_D6"), (quote!(crate::usb_otg::UlpiD6Pin), quote!())), + (("otghs", "ULPI_D7"), (quote!(crate::usb_otg::UlpiD7Pin), quote!())), (("can", "TX"), (quote!(crate::can::TxPin), quote!())), (("can", "RX"), (quote!(crate::can::RxPin), quote!())), (("eth", "REF_CLK"), (quote!(crate::eth::RefClkPin), quote!(#[cfg(feature="net")]))), diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index ba426128f..6e9077b4c 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -61,7 +61,7 @@ pub mod sdmmc; pub mod spi; #[cfg(usart)] pub mod usart; -#[cfg(feature = "usb-otg")] +#[cfg(any(otgfs, otghs))] pub mod usb_otg; #[cfg(feature = "subghz")] diff --git a/embassy-stm32/src/usb_otg.rs b/embassy-stm32/src/usb_otg.rs index 8c2c1e99c..21b890d7c 100644 --- a/embassy-stm32/src/usb_otg.rs +++ b/embassy-stm32/src/usb_otg.rs @@ -1,15 +1,11 @@ use core::marker::PhantomData; use embassy::util::Unborrow; use embassy_hal_common::unborrow; -use synopsys_usb_otg::{PhyType, UsbPeripheral}; use crate::gpio::sealed::AFType; use crate::gpio::Speed; use crate::{peripherals, rcc::RccPeripheral}; -pub use embassy_hal_common::usb::*; -pub use synopsys_usb_otg::UsbBus; - macro_rules! config_ulpi_pins { ($($pin:ident),*) => { unborrow!($($pin),*); @@ -23,9 +19,24 @@ macro_rules! config_ulpi_pins { }; } +/// USB PHY type +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum PhyType { + /// Internal Full-Speed PHY + /// + /// Available on most High-Speed peripherals. + InternalFullSpeed, + /// Internal High-Speed PHY + /// + /// Available on a few STM32 chips. + InternalHighSpeed, + /// External ULPI High-Speed PHY + ExternalHighSpeed, +} + pub struct UsbOtg<'d, T: Instance> { phantom: PhantomData<&'d mut T>, - phy_type: PhyType, + _phy_type: PhyType, } impl<'d, T: Instance> UsbOtg<'d, T> { @@ -44,7 +55,7 @@ impl<'d, T: Instance> UsbOtg<'d, T> { Self { phantom: PhantomData, - phy_type: PhyType::InternalFullSpeed, + _phy_type: PhyType::InternalFullSpeed, } } @@ -71,7 +82,7 @@ impl<'d, T: Instance> UsbOtg<'d, T> { Self { phantom: PhantomData, - phy_type: PhyType::ExternalHighSpeed, + _phy_type: PhyType::ExternalHighSpeed, } } } @@ -83,29 +94,6 @@ impl<'d, T: Instance> Drop for UsbOtg<'d, T> { } } -unsafe impl<'d, T: Instance> Send for UsbOtg<'d, T> {} -unsafe impl<'d, T: Instance> Sync for UsbOtg<'d, T> {} - -unsafe impl<'d, T: Instance> UsbPeripheral for UsbOtg<'d, T> { - const REGISTERS: *const () = T::REGISTERS; - const HIGH_SPEED: bool = T::HIGH_SPEED; - const FIFO_DEPTH_WORDS: usize = T::FIFO_DEPTH_WORDS; - const ENDPOINT_COUNT: usize = T::ENDPOINT_COUNT; - - fn enable() { - ::enable(); - ::reset(); - } - - fn phy_type(&self) -> PhyType { - self.phy_type - } - - fn ahb_frequency_hz(&self) -> u32 { - ::frequency().0 - } -} - pub(crate) mod sealed { pub trait Instance { const REGISTERS: *const (); @@ -177,7 +165,7 @@ foreach_peripheral!( const FIFO_DEPTH_WORDS: usize = 512; const ENDPOINT_COUNT: usize = 8; } else { - compile_error!("USB_OTG_FS peripheral is not supported by this chip. Disable \"usb-otg-fs\" feature or select a different chip."); + compile_error!("USB_OTG_FS peripheral is not supported by this chip."); } } } @@ -214,7 +202,7 @@ foreach_peripheral!( const FIFO_DEPTH_WORDS: usize = 1024; const ENDPOINT_COUNT: usize = 9; } else { - compile_error!("USB_OTG_HS peripheral is not supported by this chip. Disable \"usb-otg-hs\" feature or select a different chip."); + compile_error!("USB_OTG_HS peripheral is not supported by this chip."); } } } @@ -222,12 +210,3 @@ foreach_peripheral!( impl Instance for peripherals::$inst {} }; ); - -foreach_interrupt!( - ($inst:ident, otgfs, $block:ident, GLOBAL, $irq:ident) => { - unsafe impl USBInterrupt for crate::interrupt::$irq {} - }; - ($inst:ident, otghs, $block:ident, GLOBAL, $irq:ident) => { - unsafe impl USBInterrupt for crate::interrupt::$irq {} - }; -); diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 1bc406495..e2065bed9 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -8,7 +8,7 @@ resolver = "2" [dependencies] embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "usb-otg"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } defmt = "0.3" defmt-rtt = "0.3" diff --git a/examples/stm32f4/src/bin/usb_uart.rs b/examples/stm32f4/src/bin/usb_uart.rs deleted file mode 100644 index 251ed1eb0..000000000 --- a/examples/stm32f4/src/bin/usb_uart.rs +++ /dev/null @@ -1,99 +0,0 @@ -#![no_std] -#![no_main] -#![feature(type_alias_impl_trait)] - -use defmt_rtt as _; // global logger -use panic_probe as _; - -use defmt::{info, unwrap}; -use defmt_rtt as _; // global logger -use embassy::interrupt::InterruptExt; -use futures::pin_mut; -use panic_probe as _; // print out panic messages - -use embassy::executor::Spawner; -use embassy::io::{AsyncBufReadExt, AsyncWriteExt}; -use embassy_stm32::usb_otg::{State, Usb, UsbBus, UsbOtg, UsbSerial}; -use embassy_stm32::{interrupt, time::Hertz, Config, Peripherals}; -use usb_device::device::{UsbDeviceBuilder, UsbVidPid}; - -static mut EP_MEMORY: [u32; 2048] = [0; 2048]; - -// USB requires at least 48 MHz clock -fn config() -> Config { - let mut config = Config::default(); - config.rcc.sys_ck = Some(Hertz(48_000_000)); - config -} - -#[embassy::main(config = "config()")] -async fn main(_spawner: Spawner, p: Peripherals) { - let mut rx_buffer = [0u8; 64]; - // we send back input + cr + lf - let mut tx_buffer = [0u8; 66]; - - let peri = UsbOtg::new_fs(p.USB_OTG_FS, p.PA12, p.PA11); - let usb_bus = UsbBus::new(peri, unsafe { &mut EP_MEMORY }); - - let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer); - - let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) - .manufacturer("Fake company") - .product("Serial port") - .serial_number("TEST") - .device_class(0x02) - .build(); - - let irq = interrupt::take!(OTG_FS); - irq.set_priority(interrupt::Priority::P3); - - let mut state = State::new(); - let usb = unsafe { Usb::new(&mut state, device, serial, irq) }; - pin_mut!(usb); - - let (mut reader, mut writer) = usb.as_ref().take_serial_0(); - - info!("usb initialized!"); - - unwrap!( - writer - .write_all(b"\r\nInput returned upper cased on CR+LF\r\n") - .await - ); - - let mut buf = [0u8; 64]; - loop { - let mut n = 0; - - async { - loop { - let char = unwrap!(reader.read_byte().await); - - if char == b'\r' || char == b'\n' { - break; - } - - buf[n] = char; - n += 1; - - // stop if we're out of room - if n == buf.len() { - break; - } - } - } - .await; - - if n > 0 { - for char in buf[..n].iter_mut() { - // upper case - if 0x61 <= *char && *char <= 0x7a { - *char &= !0x20; - } - } - unwrap!(writer.write_all(&buf[..n]).await); - unwrap!(writer.write_all(b"\r\n").await); - unwrap!(writer.flush().await); - } - } -} diff --git a/examples/stm32f4/src/bin/usb_uart_ulpi.rs b/examples/stm32f4/src/bin/usb_uart_ulpi.rs deleted file mode 100644 index f6c373602..000000000 --- a/examples/stm32f4/src/bin/usb_uart_ulpi.rs +++ /dev/null @@ -1,114 +0,0 @@ -#![no_std] -#![no_main] -#![feature(type_alias_impl_trait)] - -use defmt_rtt as _; // global logger -use panic_probe as _; - -use defmt::{info, unwrap}; -use defmt_rtt as _; // global logger -use embassy::interrupt::InterruptExt; -use futures::pin_mut; -use panic_probe as _; // print out panic messages - -use embassy::executor::Spawner; -use embassy::io::{AsyncBufReadExt, AsyncWriteExt}; -use embassy_stm32::usb_otg::{State, Usb, UsbBus, UsbOtg, UsbSerial}; -use embassy_stm32::{interrupt, time::Hertz, Config, Peripherals}; -use usb_device::device::{UsbDeviceBuilder, UsbVidPid}; - -static mut EP_MEMORY: [u32; 2048] = [0; 2048]; - -// USB requires at least 48 MHz clock -fn config() -> Config { - let mut config = Config::default(); - config.rcc.sys_ck = Some(Hertz(48_000_000)); - config -} - -#[embassy::main(config = "config()")] -async fn main(_spawner: Spawner, p: Peripherals) { - let mut rx_buffer = [0u8; 64]; - // we send back input + cr + lf - let mut tx_buffer = [0u8; 66]; - - // USB with external high-speed PHY - let peri = UsbOtg::new_hs_ulpi( - p.USB_OTG_HS, - p.PA5, - p.PC2, - p.PC3, - p.PC0, - p.PA3, - p.PB0, - p.PB1, - p.PB10, - p.PB11, - p.PB12, - p.PB13, - p.PB5, - ); - let usb_bus = UsbBus::new(peri, unsafe { &mut EP_MEMORY }); - - let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer); - - let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) - .manufacturer("Fake company") - .product("Serial port") - .serial_number("TEST") - .device_class(0x02) - .build(); - - let irq = interrupt::take!(OTG_FS); - irq.set_priority(interrupt::Priority::P3); - - let mut state = State::new(); - let usb = unsafe { Usb::new(&mut state, device, serial, irq) }; - pin_mut!(usb); - - let (mut reader, mut writer) = usb.as_ref().take_serial_0(); - - info!("usb initialized!"); - - unwrap!( - writer - .write_all(b"\r\nInput returned upper cased on CR+LF\r\n") - .await - ); - - let mut buf = [0u8; 64]; - loop { - let mut n = 0; - - async { - loop { - let char = unwrap!(reader.read_byte().await); - - if char == b'\r' || char == b'\n' { - break; - } - - buf[n] = char; - n += 1; - - // stop if we're out of room - if n == buf.len() { - break; - } - } - } - .await; - - if n > 0 { - for char in buf[..n].iter_mut() { - // upper case - if 0x61 <= *char && *char <= 0x7a { - *char &= !0x20; - } - } - unwrap!(writer.write_all(&buf[..n]).await); - unwrap!(writer.write_all(b"\r\n").await); - unwrap!(writer.flush().await); - } - } -} diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 2da549bb2..b3478f74e 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -10,7 +10,7 @@ resolver = "2" [dependencies] embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } embassy-traits = { version = "0.1.0", path = "../../embassy-traits" } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits", "usb-otg"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits"] } defmt = "0.3" defmt-rtt = "0.3" diff --git a/examples/stm32l4/src/bin/usb_uart.rs b/examples/stm32l4/src/bin/usb_uart.rs deleted file mode 100644 index 878309550..000000000 --- a/examples/stm32l4/src/bin/usb_uart.rs +++ /dev/null @@ -1,115 +0,0 @@ -#![no_std] -#![no_main] -#![feature(type_alias_impl_trait)] - -use defmt_rtt as _; // global logger -use panic_probe as _; - -use defmt::{info, unwrap}; -use defmt_rtt as _; // global logger -use embassy::interrupt::InterruptExt; -use futures::pin_mut; -use panic_probe as _; // print out panic messages - -use embassy::executor::Spawner; -use embassy::io::{AsyncBufReadExt, AsyncWriteExt}; -use embassy_stm32::pac::pwr::vals::Usv; -use embassy_stm32::pac::{PWR, RCC}; -use embassy_stm32::rcc::{ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv}; -use embassy_stm32::usb_otg::{State, Usb, UsbBus, UsbOtg, UsbSerial}; -use embassy_stm32::{interrupt, Config, Peripherals}; -use usb_device::device::{UsbDeviceBuilder, UsbVidPid}; - -static mut EP_MEMORY: [u32; 2048] = [0; 2048]; - -// USB requires at least 48 MHz clock -fn config() -> Config { - let mut config = Config::default(); - // set up a 80Mhz clock - config.rcc.mux = ClockSrc::PLL( - PLLSource::HSI16, - PLLClkDiv::Div2, - PLLSrcDiv::Div2, - PLLMul::Mul20, - None, - ); - // enable HSI48 clock for USB - config.rcc.hsi48 = true; - config -} - -#[embassy::main(config = "config()")] -async fn main(_spawner: Spawner, p: Peripherals) { - // Enable PWR peripheral - unsafe { RCC.apb1enr1().modify(|w| w.set_pwren(true)) }; - unsafe { PWR.cr2().modify(|w| w.set_usv(Usv::VALID)) } - - let mut rx_buffer = [0u8; 64]; - // we send back input + cr + lf - let mut tx_buffer = [0u8; 66]; - - let peri = UsbOtg::new_fs(p.USB_OTG_FS, p.PA12, p.PA11); - let usb_bus = UsbBus::new(peri, unsafe { &mut EP_MEMORY }); - - let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer); - - let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) - .manufacturer("Fake company") - .product("Serial port") - .serial_number("TEST") - .device_class(0x02) - .build(); - - let irq = interrupt::take!(OTG_FS); - irq.set_priority(interrupt::Priority::P3); - - let mut state = State::new(); - let usb = unsafe { Usb::new(&mut state, device, serial, irq) }; - pin_mut!(usb); - - let (mut reader, mut writer) = usb.as_ref().take_serial_0(); - - info!("usb initialized!"); - - unwrap!( - writer - .write_all(b"\r\nInput returned upper cased on CR+LF\r\n") - .await - ); - - let mut buf = [0u8; 64]; - loop { - let mut n = 0; - - async { - loop { - let char = unwrap!(reader.read_byte().await); - - if char == b'\r' || char == b'\n' { - break; - } - - buf[n] = char; - n += 1; - - // stop if we're out of room - if n == buf.len() { - break; - } - } - } - .await; - - if n > 0 { - for char in buf[..n].iter_mut() { - // upper case - if 0x61 <= *char && *char <= 0x7a { - *char &= !0x20; - } - } - unwrap!(writer.write_all(&buf[..n]).await); - unwrap!(writer.write_all(b"\r\n").await); - unwrap!(writer.flush().await); - } - } -}