use core::marker::PhantomData;
use embassy_sync::blocking_mutex::raw::{NoopRawMutex, RawMutex};
use embassy_sync::mutex::Mutex;
use embassy_usb_driver::host::{PipeError, SplitInfo, UsbHostAllocator, UsbPipe, pipe};
use embassy_usb_driver::{Direction as UsbDirection, EndpointAddress, EndpointInfo, EndpointType};
use crate::control::SetupPacket;
use crate::descriptor::ConfigurationDescriptor;
use crate::handler::EnumerationInfo;
pub mod id {
pub const VID_SILABS: u16 = 0x10C4;
pub const PID_CP210X: u16 = 0xEA60;
pub const PID_CP210X_ALT: u16 = 0xEA70;
}
const IFC_ENABLE: u8 = 0x00;
const SET_BAUDDIV: u8 = 0x01;
const GET_BAUDDIV: u8 = 0x02;
const SET_LINE_CTL: u8 = 0x03;
const GET_LINE_CTL: u8 = 0x04;
const SET_BREAK: u8 = 0x05;
const SET_MHS: u8 = 0x07;
const GET_MDMSTS: u8 = 0x08;
const PURGE: u8 = 0x12;
const SET_FLOW: u8 = 0x13;
const GET_FLOW: u8 = 0x14;
const GET_BAUDRATE: u8 = 0x1D;
const SET_BAUDRATE: u8 = 0x1E;
const VENDOR_CLASS: u8 = 0xFF;
const BAUD_CLOCK: u32 = 3_686_400;
#[derive(Copy, Clone, PartialEq, Eq)]
enum BaudMode {
Unknown,
Direct,
Divisor,
}
struct CtrlState<P> {
pipe: P,
baud_mode: BaudMode,
}
async fn vendor_out<P>(pipe: &mut P, interface: u8, request: u8, value: u16, data: &[u8]) -> Result<(), Cp210xError>
where
P: UsbPipe<pipe::Control, pipe::InOut>,
{
let setup = SetupPacket::vendor_interface_out(request, value, interface as u16, data.len() as u16);
pipe.control_out(&setup.to_bytes(), data).await?;
Ok(())
}
async fn vendor_in<P>(pipe: &mut P, interface: u8, request: u8, value: u16, buf: &mut [u8]) -> Result<(), Cp210xError>
where
P: UsbPipe<pipe::Control, pipe::InOut>,
{
let setup = SetupPacket::vendor_interface_in(request, value, interface as u16, buf.len() as u16);
let n = pipe.control_in(&setup.to_bytes(), buf).await?;
if n != buf.len() {
return Err(Cp210xError::InvalidResponse);
}
Ok(())
}
async fn set_baud_rate_direct<P>(pipe: &mut P, interface: u8, baud: u32) -> Result<(), Cp210xError>
where
P: UsbPipe<pipe::Control, pipe::InOut>,
{
vendor_out(pipe, interface, SET_BAUDRATE, 0, &baud.to_le_bytes()).await
}
async fn set_baud_rate_divisor<P>(pipe: &mut P, interface: u8, baud: u32) -> Result<(), Cp210xError>
where
P: UsbPipe<pipe::Control, pipe::InOut>,
{
let div = (BAUD_CLOCK + baud / 2) / baud;
if div == 0 || div > u16::MAX as u32 {
return Err(Cp210xError::InvalidArgument);
}
vendor_out(pipe, interface, SET_BAUDDIV, div as u16, &[]).await
}
async fn get_baud_rate_direct<P>(pipe: &mut P, interface: u8) -> Result<u32, Cp210xError>
where
P: UsbPipe<pipe::Control, pipe::InOut>,
{
let mut buf = [0u8; 4];
vendor_in(pipe, interface, GET_BAUDRATE, 0, &mut buf).await?;
Ok(u32::from_le_bytes(buf))
}
async fn get_baud_rate_divisor<P>(pipe: &mut P, interface: u8) -> Result<u32, Cp210xError>
where
P: UsbPipe<pipe::Control, pipe::InOut>,
{
let mut buf = [0u8; 2];
vendor_in(pipe, interface, GET_BAUDDIV, 0, &mut buf).await?;
let div = u16::from_le_bytes(buf);
if div == 0 {
return Err(Cp210xError::InvalidResponse);
}
Ok(BAUD_CLOCK / div as u32)
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum Parity {
None = 0,
Odd = 1,
Even = 2,
Mark = 3,
Space = 4,
}
impl Parity {
fn from_bits(b: u8) -> Option<Self> {
Some(match b {
0 => Self::None,
1 => Self::Odd,
2 => Self::Even,
3 => Self::Mark,
4 => Self::Space,
_ => return None,
})
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum StopBits {
One = 0,
OneAndHalf = 1,
Two = 2,
}
impl StopBits {
fn from_bits(b: u8) -> Option<Self> {
Some(match b {
0 => Self::One,
1 => Self::OneAndHalf,
2 => Self::Two,
_ => return None,
})
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct LineCoding {
pub baud_rate: u32,
pub data_bits: u8,
pub parity: Parity,
pub stop_bits: StopBits,
}
impl Default for LineCoding {
fn default() -> Self {
Self {
baud_rate: 115200,
data_bits: 8,
parity: Parity::None,
stop_bits: StopBits::One,
}
}
}
macro_rules! bitflags {
($($tt:tt)*) => {
#[cfg(feature = "defmt")]
defmt::bitflags! { $($tt)* }
#[cfg(not(feature = "defmt"))]
bitflags::bitflags! { #[derive(Debug, Clone, PartialEq)] $($tt)* }
};
}
bitflags! {
pub struct ModemStatus: u8 {
const DTR = 1 << 0;
const RTS = 1 << 1;
const CTS = 1 << 4;
const DSR = 1 << 5;
const RI = 1 << 6;
const DCD = 1 << 7;
}
}
bitflags! {
pub struct PurgeMask: u16 {
const TX = (1 << 0) | (1 << 2);
const RX = (1 << 1) | (1 << 3);
const ALL = Self::TX.bits() | Self::RX.bits();
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum DtrMode {
Inactive = 0,
Active = 1,
FlowControl = 2,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum RtsMode {
Inactive = 0,
Active = 1,
FlowControl = 2,
TxActive = 3,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FlowControl {
pub dtr: DtrMode,
pub rts: RtsMode,
pub cts_handshake: bool,
pub dsr_handshake: bool,
pub dcd_handshake: bool,
pub dsr_sensitivity: bool,
pub auto_transmit: bool,
pub auto_receive: bool,
pub xon_limit: u32,
pub xoff_limit: u32,
}
impl Default for FlowControl {
fn default() -> Self {
Self {
dtr: DtrMode::Active,
rts: RtsMode::Active,
cts_handshake: false,
dsr_handshake: false,
dcd_handshake: false,
dsr_sensitivity: false,
auto_transmit: false,
auto_receive: false,
xon_limit: 0,
xoff_limit: 0,
}
}
}
impl FlowControl {
fn to_bytes(self) -> [u8; 16] {
let mut ctrl = self.dtr as u32;
if self.cts_handshake {
ctrl |= 1 << 3;
}
if self.dsr_handshake {
ctrl |= 1 << 4;
}
if self.dcd_handshake {
ctrl |= 1 << 5;
}
if self.dsr_sensitivity {
ctrl |= 1 << 6;
}
let mut repl = 0u32;
if self.auto_transmit {
repl |= 1 << 0;
}
if self.auto_receive {
repl |= 1 << 1;
}
repl |= (self.rts as u32) << 6;
let mut buf = [0u8; 16];
buf[0..4].copy_from_slice(&ctrl.to_le_bytes());
buf[4..8].copy_from_slice(&repl.to_le_bytes());
buf[8..12].copy_from_slice(&self.xon_limit.to_le_bytes());
buf[12..16].copy_from_slice(&self.xoff_limit.to_le_bytes());
buf
}
fn from_bytes(buf: &[u8; 16]) -> Self {
let ctrl = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
let repl = u32::from_le_bytes([buf[4], buf[5], buf[6], buf[7]]);
let xon = u32::from_le_bytes([buf[8], buf[9], buf[10], buf[11]]);
let xoff = u32::from_le_bytes([buf[12], buf[13], buf[14], buf[15]]);
let dtr = match ctrl & 0b11 {
0 => DtrMode::Inactive,
1 => DtrMode::Active,
_ => DtrMode::FlowControl,
};
let rts = match (repl >> 6) & 0b11 {
0 => RtsMode::Inactive,
1 => RtsMode::Active,
2 => RtsMode::FlowControl,
_ => RtsMode::TxActive,
};
Self {
dtr,
rts,
cts_handshake: ctrl & (1 << 3) != 0,
dsr_handshake: ctrl & (1 << 4) != 0,
dcd_handshake: ctrl & (1 << 5) != 0,
dsr_sensitivity: ctrl & (1 << 6) != 0,
auto_transmit: repl & (1 << 0) != 0,
auto_receive: repl & (1 << 1) != 0,
xon_limit: xon,
xoff_limit: xoff,
}
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Cp210xError {
Transfer(PipeError),
NoInterface,
NoPipe,
InvalidResponse,
InvalidArgument,
}
impl From<PipeError> for Cp210xError {
fn from(e: PipeError) -> Self {
Self::Transfer(e)
}
}
impl core::fmt::Display for Cp210xError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Transfer(_) => write!(f, "Transfer error"),
Self::NoInterface => write!(f, "No CP210x interface found"),
Self::NoPipe => write!(f, "No free pipe"),
Self::InvalidResponse => write!(f, "Invalid response from device"),
Self::InvalidArgument => write!(f, "Invalid argument"),
}
}
}
impl core::error::Error for Cp210xError {}
impl embedded_io_async::Error for Cp210xError {
fn kind(&self) -> embedded_io_async::ErrorKind {
match self {
Self::Transfer(e) => match e {
PipeError::Disconnected => embedded_io_async::ErrorKind::NotConnected,
PipeError::BufferOverflow => embedded_io_async::ErrorKind::OutOfMemory,
PipeError::Timeout => embedded_io_async::ErrorKind::TimedOut,
_ => embedded_io_async::ErrorKind::Other,
},
Self::NoInterface => embedded_io_async::ErrorKind::NotFound,
Self::NoPipe => embedded_io_async::ErrorKind::OutOfMemory,
Self::InvalidResponse => embedded_io_async::ErrorKind::InvalidData,
Self::InvalidArgument => embedded_io_async::ErrorKind::InvalidInput,
}
}
}
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Cp210xInfo {
pub interface: u8,
pub bulk_in_ep: u8,
pub bulk_in_mps: u16,
pub bulk_out_ep: u8,
pub bulk_out_mps: u16,
}
pub fn find_cp210x(config_desc: &[u8], interface_idx: u8) -> Option<Cp210xInfo> {
let cfg = ConfigurationDescriptor::try_from_slice(config_desc).ok()?;
let mut seen = 0u8;
for iface in cfg.iter_interface() {
if iface.interface_class != VENDOR_CLASS || iface.alternate_setting != 0 {
continue;
}
let mut in_ep = None;
let mut out_ep = None;
for ep in iface.iter_endpoints() {
if ep.ep_type() != EndpointType::Bulk {
continue;
}
if ep.is_in() {
in_ep = Some((ep.endpoint_address, ep.max_packet_size));
} else {
out_ep = Some((ep.endpoint_address, ep.max_packet_size));
}
}
if let (Some((in_a, in_m)), Some((out_a, out_m))) = (in_ep, out_ep) {
if seen == interface_idx {
return Some(Cp210xInfo {
interface: iface.interface_number,
bulk_in_ep: in_a,
bulk_in_mps: in_m,
bulk_out_ep: out_a,
bulk_out_mps: out_m,
});
}
seen += 1;
}
}
None
}
pub struct Cp210xDevice<'d, A, M = NoopRawMutex>
where
A: UsbHostAllocator<'d>,
M: RawMutex,
{
alloc: A,
ctrl: Mutex<M, CtrlState<A::Pipe<pipe::Control, pipe::InOut>>>,
device_address: u8,
split: Option<SplitInfo>,
_phantom: PhantomData<&'d ()>,
}
impl<'d, A> Cp210xDevice<'d, A, NoopRawMutex>
where
A: UsbHostAllocator<'d>,
{
pub fn new(alloc: &A, enum_info: &EnumerationInfo) -> Result<Self, Cp210xError> {
Self::new_with_raw_mutex(alloc, enum_info)
}
}
impl<'d, A, M> Cp210xDevice<'d, A, M>
where
A: UsbHostAllocator<'d>,
M: RawMutex,
{
pub fn new_with_raw_mutex(alloc: &A, enum_info: &EnumerationInfo) -> Result<Self, Cp210xError> {
let ctrl_ep_info = EndpointInfo {
addr: EndpointAddress::from_parts(0, UsbDirection::In),
ep_type: EndpointType::Control,
max_packet_size: enum_info.device_desc.max_packet_size0 as u16,
interval_ms: 0,
};
let device_address = enum_info.device_address;
let split = enum_info.split();
let ctrl = alloc
.alloc_pipe::<pipe::Control, pipe::InOut>(device_address, &ctrl_ep_info, split)
.map_err(|_| Cp210xError::NoPipe)?;
Ok(Self {
alloc: alloc.clone(),
ctrl: Mutex::new(CtrlState {
pipe: ctrl,
baud_mode: BaudMode::Unknown,
}),
device_address,
split,
_phantom: PhantomData,
})
}
pub fn port<'dev>(
&'dev self,
config_desc: &[u8],
interface_idx: u8,
) -> Result<Cp210xPort<'dev, 'd, A, M>, Cp210xError> {
let info = find_cp210x(config_desc, interface_idx).ok_or(Cp210xError::NoInterface)?;
let in_ep_info = EndpointInfo {
addr: EndpointAddress::from_parts((info.bulk_in_ep & 0x0F) as usize, UsbDirection::In),
ep_type: EndpointType::Bulk,
max_packet_size: info.bulk_in_mps,
interval_ms: 0,
};
let out_ep_info = EndpointInfo {
addr: EndpointAddress::from_parts((info.bulk_out_ep & 0x0F) as usize, UsbDirection::Out),
ep_type: EndpointType::Bulk,
max_packet_size: info.bulk_out_mps,
interval_ms: 0,
};
let in_ch = self
.alloc
.alloc_pipe::<pipe::Bulk, pipe::In>(self.device_address, &in_ep_info, self.split)
.map_err(|_| Cp210xError::NoPipe)?;
let out_ch = self
.alloc
.alloc_pipe::<pipe::Bulk, pipe::Out>(self.device_address, &out_ep_info, self.split)
.map_err(|_| Cp210xError::NoPipe)?;
Ok(Cp210xPort {
device: self,
in_ch,
out_ch,
interface: info.interface,
})
}
}
pub struct Cp210xPort<'dev, 'd, A, M = NoopRawMutex>
where
A: UsbHostAllocator<'d>,
M: RawMutex,
{
device: &'dev Cp210xDevice<'d, A, M>,
in_ch: A::Pipe<pipe::Bulk, pipe::In>,
out_ch: A::Pipe<pipe::Bulk, pipe::Out>,
interface: u8,
}
impl<'dev, 'd, A, M> Cp210xPort<'dev, 'd, A, M>
where
A: UsbHostAllocator<'d>,
M: RawMutex,
{
pub fn interface(&self) -> u8 {
self.interface
}
async fn vendor_out(&mut self, request: u8, value: u16, data: &[u8]) -> Result<(), Cp210xError> {
let mut ctrl = self.device.ctrl.lock().await;
vendor_out(&mut ctrl.pipe, self.interface, request, value, data).await
}
async fn vendor_in(&mut self, request: u8, value: u16, buf: &mut [u8]) -> Result<(), Cp210xError> {
let mut ctrl = self.device.ctrl.lock().await;
vendor_in(&mut ctrl.pipe, self.interface, request, value, buf).await
}
pub async fn enable(&mut self) -> Result<(), Cp210xError> {
self.vendor_out(IFC_ENABLE, 1, &[]).await
}
pub async fn disable(&mut self) -> Result<(), Cp210xError> {
self.vendor_out(IFC_ENABLE, 0, &[]).await
}
pub async fn set_baud_rate(&mut self, baud: u32) -> Result<(), Cp210xError> {
if baud == 0 {
return Err(Cp210xError::InvalidArgument);
}
let iface = self.interface;
let mut ctrl = self.device.ctrl.lock().await;
let CtrlState { pipe, baud_mode } = &mut *ctrl;
match *baud_mode {
BaudMode::Direct => set_baud_rate_direct(pipe, iface, baud).await,
BaudMode::Divisor => set_baud_rate_divisor(pipe, iface, baud).await,
BaudMode::Unknown => match set_baud_rate_direct(pipe, iface, baud).await {
Ok(()) => {
*baud_mode = BaudMode::Direct;
Ok(())
}
Err(Cp210xError::Transfer(PipeError::Stall)) => {
*baud_mode = BaudMode::Divisor;
set_baud_rate_divisor(pipe, iface, baud).await
}
Err(e) => Err(e),
},
}
}
pub async fn baud_rate(&mut self) -> Result<u32, Cp210xError> {
let iface = self.interface;
let mut ctrl = self.device.ctrl.lock().await;
let CtrlState { pipe, baud_mode } = &mut *ctrl;
match *baud_mode {
BaudMode::Direct => get_baud_rate_direct(pipe, iface).await,
BaudMode::Divisor => get_baud_rate_divisor(pipe, iface).await,
BaudMode::Unknown => match get_baud_rate_direct(pipe, iface).await {
Ok(b) => {
*baud_mode = BaudMode::Direct;
Ok(b)
}
Err(Cp210xError::Transfer(PipeError::Stall)) => {
*baud_mode = BaudMode::Divisor;
get_baud_rate_divisor(pipe, iface).await
}
Err(e) => Err(e),
},
}
}
pub async fn set_line_coding(&mut self, coding: &LineCoding) -> Result<(), Cp210xError> {
if !matches!(coding.data_bits, 5..=8) {
return Err(Cp210xError::InvalidArgument);
}
let line_ctl = (coding.stop_bits as u16) | ((coding.parity as u16) << 4) | ((coding.data_bits as u16) << 8);
self.vendor_out(SET_LINE_CTL, line_ctl, &[]).await?;
self.set_baud_rate(coding.baud_rate).await
}
pub async fn line_coding(&mut self) -> Result<LineCoding, Cp210xError> {
let mut buf = [0u8; 2];
self.vendor_in(GET_LINE_CTL, 0, &mut buf).await?;
let ctl = u16::from_le_bytes(buf);
let stop_bits = StopBits::from_bits((ctl & 0xF) as u8).ok_or(Cp210xError::InvalidResponse)?;
let parity = Parity::from_bits(((ctl >> 4) & 0xF) as u8).ok_or(Cp210xError::InvalidResponse)?;
let data_bits = (ctl >> 8) as u8;
if !matches!(data_bits, 5..=8) {
return Err(Cp210xError::InvalidResponse);
}
let baud_rate = self.baud_rate().await?;
Ok(LineCoding {
baud_rate,
data_bits,
parity,
stop_bits,
})
}
pub async fn set_control_line_state(&mut self, dtr: bool, rts: bool) -> Result<(), Cp210xError> {
let value = (dtr as u16) | ((rts as u16) << 1) | (1 << 8) | (1 << 9);
self.vendor_out(SET_MHS, value, &[]).await
}
pub async fn modem_status(&mut self) -> Result<ModemStatus, Cp210xError> {
let mut buf = [0u8; 1];
self.vendor_in(GET_MDMSTS, 0, &mut buf).await?;
Ok(ModemStatus::from_bits_truncate(buf[0]))
}
pub async fn set_break(&mut self, asserted: bool) -> Result<(), Cp210xError> {
self.vendor_out(SET_BREAK, asserted as u16, &[]).await
}
pub async fn purge(&mut self, mask: PurgeMask) -> Result<(), Cp210xError> {
self.vendor_out(PURGE, mask.bits(), &[]).await
}
pub async fn set_flow_control(&mut self, fc: &FlowControl) -> Result<(), Cp210xError> {
let bytes = fc.to_bytes();
self.set_flow_control_raw(&bytes).await
}
pub async fn set_flow_control_raw(&mut self, raw: &[u8; 16]) -> Result<(), Cp210xError> {
self.vendor_out(SET_FLOW, 0, raw).await
}
pub async fn flow_control(&mut self) -> Result<FlowControl, Cp210xError> {
let mut buf = [0u8; 16];
self.vendor_in(GET_FLOW, 0, &mut buf).await?;
Ok(FlowControl::from_bytes(&buf))
}
pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Cp210xError> {
Ok(self.in_ch.request_in(buf).await?)
}
pub async fn write(&mut self, data: &[u8]) -> Result<usize, Cp210xError> {
self.out_ch.request_out(data, false).await?;
Ok(data.len())
}
}
impl<'dev, 'd, A, M> embedded_io_async::ErrorType for Cp210xPort<'dev, 'd, A, M>
where
A: UsbHostAllocator<'d>,
M: RawMutex,
{
type Error = Cp210xError;
}
impl<'dev, 'd, A, M> embedded_io_async::Read for Cp210xPort<'dev, 'd, A, M>
where
A: UsbHostAllocator<'d>,
M: RawMutex,
{
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
Cp210xPort::read(self, buf).await
}
}
impl<'dev, 'd, A, M> embedded_io_async::Write for Cp210xPort<'dev, 'd, A, M>
where
A: UsbHostAllocator<'d>,
M: RawMutex,
{
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
Cp210xPort::write(self, buf).await
}
async fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}