Skip to main content

embassy_usb_host/class/vcp/
cp210x.rs

1//! Silicon Labs CP210x USB ↔ UART bridge driver.
2//!
3//! Implements the AN571 protocol: vendor-class bulk data transport
4//! (one bulk IN + one bulk OUT per interface) plus vendor-interface
5//! control requests for line coding, flow control, modem signalling
6//! and break. Covers single- and multi-port parts.
7//!
8//! A [`Cp210xDevice`] owns the device-level control pipe on endpoint 0.
9//! Each UART interface is then opened as a [`Cp210xPort`] that borrows
10//! the device; control requests from all ports are serialized through
11//! the shared control pipe.
12//!
13//! [`Cp210xDevice::new`] guards the control pipe with a `NoopRawMutex`,
14//! which is free but `!Sync`; use it for single-port parts or whenever
15//! the device stays in one task. For multi-port parts driven concurrently,
16//! use [`Cp210xDevice::new_with_raw_mutex`] with a `Sync` raw mutex such as
17//! `CriticalSectionRawMutex`.
18//!
19//! # Example
20//!
21//! ```rust,ignore
22//! use embassy_usb_host::class::vcp::cp210x::{Cp210xDevice, LineCoding, Parity, StopBits, id};
23//!
24//! if enum_info.device_desc.vendor_id != id::VID_SILABS {
25//!     continue;
26//! }
27//!
28//! let device = Cp210xDevice::new(&bus, &enum_info)?;
29//! let mut port = device.port(&config_buf[..config_len], 0)?;
30//! port.enable().await?;
31//! port.set_line_coding(&LineCoding {
32//!     baud_rate: 115200,
33//!     data_bits: 8,
34//!     parity: Parity::None,
35//!     stop_bits: StopBits::One,
36//! }).await?;
37//! port.set_control_line_state(true, true).await?;
38//!
39//! let mut buf = [0u8; 64];
40//! let n = port.read(&mut buf).await?;
41//! port.write(&buf[..n]).await?;
42//! ```
43
44use core::marker::PhantomData;
45
46use embassy_sync::blocking_mutex::raw::{NoopRawMutex, RawMutex};
47use embassy_sync::mutex::Mutex;
48use embassy_usb_driver::host::{PipeError, SplitInfo, UsbHostAllocator, UsbPipe, pipe};
49use embassy_usb_driver::{Direction as UsbDirection, EndpointAddress, EndpointInfo, EndpointType};
50
51use crate::control::SetupPacket;
52use crate::descriptor::ConfigurationDescriptor;
53use crate::handler::EnumerationInfo;
54
55/// Silicon Labs VID and CP210x PIDs.
56pub mod id {
57    /// Silicon Laboratories vendor ID.
58    pub const VID_SILABS: u16 = 0x10C4;
59    /// Default CP210x product ID.
60    pub const PID_CP210X: u16 = 0xEA60;
61    /// Alternate CP210x product ID used by some SKUs.
62    pub const PID_CP210X_ALT: u16 = 0xEA70;
63}
64
65// AN571 §5, Table 6.
66const IFC_ENABLE: u8 = 0x00;
67const SET_BAUDDIV: u8 = 0x01;
68const GET_BAUDDIV: u8 = 0x02;
69const SET_LINE_CTL: u8 = 0x03;
70const GET_LINE_CTL: u8 = 0x04;
71const SET_BREAK: u8 = 0x05;
72const SET_MHS: u8 = 0x07;
73const GET_MDMSTS: u8 = 0x08;
74const PURGE: u8 = 0x12;
75const SET_FLOW: u8 = 0x13;
76const GET_FLOW: u8 = 0x14;
77const GET_BAUDRATE: u8 = 0x1D;
78const SET_BAUDRATE: u8 = 0x1E;
79
80const VENDOR_CLASS: u8 = 0xFF;
81
82/// Baud-rate generator reference clock (AN571 §5.1).
83const BAUD_CLOCK: u32 = 3_686_400;
84
85/// Cached baud-rate API flavour supported by the device's firmware.
86#[derive(Copy, Clone, PartialEq, Eq)]
87enum BaudMode {
88    /// Not yet probed.
89    Unknown,
90    /// `SET_BAUDRATE` / `GET_BAUDRATE` (CP2102 with current firmware,
91    /// CP2102N, CP2103/4/5/8).
92    Direct,
93    /// `SET_BAUDDIV` / `GET_BAUDDIV` (CP2101, early CP2102/3 firmware).
94    Divisor,
95}
96
97/// Shared state protected by [`Cp210xDevice::ctrl`].
98struct CtrlState<P> {
99    pipe: P,
100    baud_mode: BaudMode,
101}
102
103async fn vendor_out<P>(pipe: &mut P, interface: u8, request: u8, value: u16, data: &[u8]) -> Result<(), Cp210xError>
104where
105    P: UsbPipe<pipe::Control, pipe::InOut>,
106{
107    let setup = SetupPacket::vendor_interface_out(request, value, interface as u16, data.len() as u16);
108    pipe.control_out(&setup.to_bytes(), data).await?;
109    Ok(())
110}
111
112async fn vendor_in<P>(pipe: &mut P, interface: u8, request: u8, value: u16, buf: &mut [u8]) -> Result<(), Cp210xError>
113where
114    P: UsbPipe<pipe::Control, pipe::InOut>,
115{
116    let setup = SetupPacket::vendor_interface_in(request, value, interface as u16, buf.len() as u16);
117    let n = pipe.control_in(&setup.to_bytes(), buf).await?;
118    if n != buf.len() {
119        return Err(Cp210xError::InvalidResponse);
120    }
121    Ok(())
122}
123
124async fn set_baud_rate_direct<P>(pipe: &mut P, interface: u8, baud: u32) -> Result<(), Cp210xError>
125where
126    P: UsbPipe<pipe::Control, pipe::InOut>,
127{
128    vendor_out(pipe, interface, SET_BAUDRATE, 0, &baud.to_le_bytes()).await
129}
130
131async fn set_baud_rate_divisor<P>(pipe: &mut P, interface: u8, baud: u32) -> Result<(), Cp210xError>
132where
133    P: UsbPipe<pipe::Control, pipe::InOut>,
134{
135    let div = (BAUD_CLOCK + baud / 2) / baud;
136    if div == 0 || div > u16::MAX as u32 {
137        return Err(Cp210xError::InvalidArgument);
138    }
139    vendor_out(pipe, interface, SET_BAUDDIV, div as u16, &[]).await
140}
141
142async fn get_baud_rate_direct<P>(pipe: &mut P, interface: u8) -> Result<u32, Cp210xError>
143where
144    P: UsbPipe<pipe::Control, pipe::InOut>,
145{
146    let mut buf = [0u8; 4];
147    vendor_in(pipe, interface, GET_BAUDRATE, 0, &mut buf).await?;
148    Ok(u32::from_le_bytes(buf))
149}
150
151async fn get_baud_rate_divisor<P>(pipe: &mut P, interface: u8) -> Result<u32, Cp210xError>
152where
153    P: UsbPipe<pipe::Control, pipe::InOut>,
154{
155    let mut buf = [0u8; 2];
156    vendor_in(pipe, interface, GET_BAUDDIV, 0, &mut buf).await?;
157    let div = u16::from_le_bytes(buf);
158    if div == 0 {
159        return Err(Cp210xError::InvalidResponse);
160    }
161    Ok(BAUD_CLOCK / div as u32)
162}
163
164/// Parity setting.
165#[derive(Copy, Clone, Debug, PartialEq, Eq)]
166#[cfg_attr(feature = "defmt", derive(defmt::Format))]
167#[repr(u8)]
168pub enum Parity {
169    /// No parity bit.
170    None = 0,
171    /// Odd parity.
172    Odd = 1,
173    /// Even parity.
174    Even = 2,
175    /// Always 1.
176    Mark = 3,
177    /// Always 0.
178    Space = 4,
179}
180
181impl Parity {
182    fn from_bits(b: u8) -> Option<Self> {
183        Some(match b {
184            0 => Self::None,
185            1 => Self::Odd,
186            2 => Self::Even,
187            3 => Self::Mark,
188            4 => Self::Space,
189            _ => return None,
190        })
191    }
192}
193
194/// Number of stop bits.
195#[derive(Copy, Clone, Debug, PartialEq, Eq)]
196#[cfg_attr(feature = "defmt", derive(defmt::Format))]
197#[repr(u8)]
198pub enum StopBits {
199    /// 1 stop bit.
200    One = 0,
201    /// 1.5 stop bits.
202    OneAndHalf = 1,
203    /// 2 stop bits.
204    Two = 2,
205}
206
207impl StopBits {
208    fn from_bits(b: u8) -> Option<Self> {
209        Some(match b {
210            0 => Self::One,
211            1 => Self::OneAndHalf,
212            2 => Self::Two,
213            _ => return None,
214        })
215    }
216}
217
218/// Serial line parameters.
219#[derive(Copy, Clone, Debug, PartialEq, Eq)]
220#[cfg_attr(feature = "defmt", derive(defmt::Format))]
221pub struct LineCoding {
222    /// Baud rate in bits per second.
223    pub baud_rate: u32,
224    /// Data bits. Legal values are 5, 6, 7 and 8.
225    pub data_bits: u8,
226    /// Parity setting.
227    pub parity: Parity,
228    /// Stop bits.
229    pub stop_bits: StopBits,
230}
231
232impl Default for LineCoding {
233    fn default() -> Self {
234        Self {
235            baud_rate: 115200,
236            data_bits: 8,
237            parity: Parity::None,
238            stop_bits: StopBits::One,
239        }
240    }
241}
242
243macro_rules! bitflags {
244    ($($tt:tt)*) => {
245        #[cfg(feature = "defmt")]
246        defmt::bitflags! { $($tt)* }
247        #[cfg(not(feature = "defmt"))]
248        bitflags::bitflags! { #[derive(Debug, Clone, PartialEq)] $($tt)* }
249    };
250}
251
252bitflags! {
253    /// Modem status byte returned by `GET_MDMSTS` (AN571 §5.10).
254    pub struct ModemStatus: u8 {
255        /// DTR output asserted.
256        const DTR = 1 << 0;
257        /// RTS output asserted.
258        const RTS = 1 << 1;
259        /// CTS input asserted.
260        const CTS = 1 << 4;
261        /// DSR input asserted.
262        const DSR = 1 << 5;
263        /// Ring indicator input asserted.
264        const RI  = 1 << 6;
265        /// Data-carrier-detect input asserted.
266        const DCD = 1 << 7;
267    }
268}
269
270bitflags! {
271    /// Bitmask passed to [`Cp210xPort::purge`] (AN571 §5.27).
272    pub struct PurgeMask: u16 {
273        /// Clear the transmit queue.
274        const TX = (1 << 0) | (1 << 2);
275        /// Clear the receive queue.
276        const RX = (1 << 1) | (1 << 3);
277        /// Clear both queues.
278        const ALL = Self::TX.bits() | Self::RX.bits();
279    }
280}
281
282/// DTR output mode (AN571 Table 10, `SERIAL_DTR_MASK`).
283#[derive(Copy, Clone, Debug, PartialEq, Eq)]
284#[cfg_attr(feature = "defmt", derive(defmt::Format))]
285#[repr(u8)]
286pub enum DtrMode {
287    /// DTR held inactive.
288    Inactive = 0,
289    /// DTR held active.
290    Active = 1,
291    /// DTR driven by the CP210x flow-control logic.
292    FlowControl = 2,
293}
294
295/// RTS output mode (AN571 Table 11, `SERIAL_RTS_MASK`).
296#[derive(Copy, Clone, Debug, PartialEq, Eq)]
297#[cfg_attr(feature = "defmt", derive(defmt::Format))]
298#[repr(u8)]
299pub enum RtsMode {
300    /// RTS statically inactive.
301    Inactive = 0,
302    /// RTS statically active.
303    Active = 1,
304    /// RTS used for receive flow control.
305    FlowControl = 2,
306    /// RTS acts as a transmit-active signal.
307    TxActive = 3,
308}
309
310/// Flow-control configuration (AN571 Tables 9–11).
311///
312/// Maps the commonly used bits of the 16-byte `SET_FLOW` / `GET_FLOW`
313/// payload. For bits not covered here (e.g. `SERIAL_XOFF_CONTINUE`,
314/// `SERIAL_NULL_STRIPPING`, `SERIAL_ERROR_CHAR`, `SERIAL_BREAK_CHAR`),
315/// use [`Cp210xPort::set_flow_control_raw`].
316#[derive(Copy, Clone, Debug, PartialEq, Eq)]
317#[cfg_attr(feature = "defmt", derive(defmt::Format))]
318pub struct FlowControl {
319    /// DTR output mode.
320    pub dtr: DtrMode,
321    /// RTS output mode.
322    pub rts: RtsMode,
323    /// Treat CTS as a handshake line.
324    pub cts_handshake: bool,
325    /// Treat DSR as a handshake line.
326    pub dsr_handshake: bool,
327    /// Treat DCD as a handshake line.
328    pub dcd_handshake: bool,
329    /// Discard received data while DSR is low.
330    pub dsr_sensitivity: bool,
331    /// Act on XON/XOFF received from the end device.
332    pub auto_transmit: bool,
333    /// Emit XON/XOFF to the end device based on local buffer fill.
334    pub auto_receive: bool,
335    /// Threshold (bytes of free space) for sending XON in auto-receive mode.
336    pub xon_limit: u32,
337    /// Threshold (bytes of free space) for sending XOFF in auto-receive mode.
338    pub xoff_limit: u32,
339}
340
341impl Default for FlowControl {
342    fn default() -> Self {
343        Self {
344            dtr: DtrMode::Active,
345            rts: RtsMode::Active,
346            cts_handshake: false,
347            dsr_handshake: false,
348            dcd_handshake: false,
349            dsr_sensitivity: false,
350            auto_transmit: false,
351            auto_receive: false,
352            xon_limit: 0,
353            xoff_limit: 0,
354        }
355    }
356}
357
358impl FlowControl {
359    fn to_bytes(self) -> [u8; 16] {
360        let mut ctrl = self.dtr as u32;
361        if self.cts_handshake {
362            ctrl |= 1 << 3;
363        }
364        if self.dsr_handshake {
365            ctrl |= 1 << 4;
366        }
367        if self.dcd_handshake {
368            ctrl |= 1 << 5;
369        }
370        if self.dsr_sensitivity {
371            ctrl |= 1 << 6;
372        }
373
374        let mut repl = 0u32;
375        if self.auto_transmit {
376            repl |= 1 << 0;
377        }
378        if self.auto_receive {
379            repl |= 1 << 1;
380        }
381        repl |= (self.rts as u32) << 6;
382
383        let mut buf = [0u8; 16];
384        buf[0..4].copy_from_slice(&ctrl.to_le_bytes());
385        buf[4..8].copy_from_slice(&repl.to_le_bytes());
386        buf[8..12].copy_from_slice(&self.xon_limit.to_le_bytes());
387        buf[12..16].copy_from_slice(&self.xoff_limit.to_le_bytes());
388        buf
389    }
390
391    fn from_bytes(buf: &[u8; 16]) -> Self {
392        let ctrl = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
393        let repl = u32::from_le_bytes([buf[4], buf[5], buf[6], buf[7]]);
394        let xon = u32::from_le_bytes([buf[8], buf[9], buf[10], buf[11]]);
395        let xoff = u32::from_le_bytes([buf[12], buf[13], buf[14], buf[15]]);
396
397        let dtr = match ctrl & 0b11 {
398            0 => DtrMode::Inactive,
399            1 => DtrMode::Active,
400            _ => DtrMode::FlowControl,
401        };
402        let rts = match (repl >> 6) & 0b11 {
403            0 => RtsMode::Inactive,
404            1 => RtsMode::Active,
405            2 => RtsMode::FlowControl,
406            _ => RtsMode::TxActive,
407        };
408
409        Self {
410            dtr,
411            rts,
412            cts_handshake: ctrl & (1 << 3) != 0,
413            dsr_handshake: ctrl & (1 << 4) != 0,
414            dcd_handshake: ctrl & (1 << 5) != 0,
415            dsr_sensitivity: ctrl & (1 << 6) != 0,
416            auto_transmit: repl & (1 << 0) != 0,
417            auto_receive: repl & (1 << 1) != 0,
418            xon_limit: xon,
419            xoff_limit: xoff,
420        }
421    }
422}
423
424/// CP210x host driver error.
425#[derive(Debug)]
426#[cfg_attr(feature = "defmt", derive(defmt::Format))]
427pub enum Cp210xError {
428    /// Transfer error.
429    Transfer(PipeError),
430    /// No vendor-class interface at `interface_idx` with a bulk IN/OUT pair.
431    NoInterface,
432    /// Failed to allocate a pipe.
433    NoPipe,
434    /// Device response had an unexpected length or out-of-range field.
435    InvalidResponse,
436    /// Argument was out of range for the CP210x protocol.
437    InvalidArgument,
438}
439
440impl From<PipeError> for Cp210xError {
441    fn from(e: PipeError) -> Self {
442        Self::Transfer(e)
443    }
444}
445
446impl core::fmt::Display for Cp210xError {
447    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
448        match self {
449            Self::Transfer(_) => write!(f, "Transfer error"),
450            Self::NoInterface => write!(f, "No CP210x interface found"),
451            Self::NoPipe => write!(f, "No free pipe"),
452            Self::InvalidResponse => write!(f, "Invalid response from device"),
453            Self::InvalidArgument => write!(f, "Invalid argument"),
454        }
455    }
456}
457
458impl core::error::Error for Cp210xError {}
459
460impl embedded_io_async::Error for Cp210xError {
461    fn kind(&self) -> embedded_io_async::ErrorKind {
462        match self {
463            Self::Transfer(e) => match e {
464                PipeError::Disconnected => embedded_io_async::ErrorKind::NotConnected,
465                PipeError::BufferOverflow => embedded_io_async::ErrorKind::OutOfMemory,
466                PipeError::Timeout => embedded_io_async::ErrorKind::TimedOut,
467                _ => embedded_io_async::ErrorKind::Other,
468            },
469            Self::NoInterface => embedded_io_async::ErrorKind::NotFound,
470            Self::NoPipe => embedded_io_async::ErrorKind::OutOfMemory,
471            Self::InvalidResponse => embedded_io_async::ErrorKind::InvalidData,
472            Self::InvalidArgument => embedded_io_async::ErrorKind::InvalidInput,
473        }
474    }
475}
476
477/// Descriptor-located info for a single CP210x interface.
478#[derive(Copy, Clone, Debug)]
479#[cfg_attr(feature = "defmt", derive(defmt::Format))]
480pub struct Cp210xInfo {
481    /// USB interface number.
482    pub interface: u8,
483    /// Bulk IN endpoint address.
484    pub bulk_in_ep: u8,
485    /// Bulk IN max packet size.
486    pub bulk_in_mps: u16,
487    /// Bulk OUT endpoint address.
488    pub bulk_out_ep: u8,
489    /// Bulk OUT max packet size.
490    pub bulk_out_mps: u16,
491}
492
493/// Return the `n`th (0-indexed) vendor-class interface in `config_desc`
494/// that exposes a bulk IN + bulk OUT endpoint pair.
495///
496/// Use `interface_idx = 0` for single-port CP210x parts; `0..2` for
497/// CP2105; `0..4` for CP2108.
498pub fn find_cp210x(config_desc: &[u8], interface_idx: u8) -> Option<Cp210xInfo> {
499    let cfg = ConfigurationDescriptor::try_from_slice(config_desc).ok()?;
500
501    let mut seen = 0u8;
502    for iface in cfg.iter_interface() {
503        if iface.interface_class != VENDOR_CLASS || iface.alternate_setting != 0 {
504            continue;
505        }
506
507        let mut in_ep = None;
508        let mut out_ep = None;
509        for ep in iface.iter_endpoints() {
510            if ep.ep_type() != EndpointType::Bulk {
511                continue;
512            }
513            if ep.is_in() {
514                in_ep = Some((ep.endpoint_address, ep.max_packet_size));
515            } else {
516                out_ep = Some((ep.endpoint_address, ep.max_packet_size));
517            }
518        }
519
520        if let (Some((in_a, in_m)), Some((out_a, out_m))) = (in_ep, out_ep) {
521            if seen == interface_idx {
522                return Some(Cp210xInfo {
523                    interface: iface.interface_number,
524                    bulk_in_ep: in_a,
525                    bulk_in_mps: in_m,
526                    bulk_out_ep: out_a,
527                    bulk_out_mps: out_m,
528                });
529            }
530            seen += 1;
531        }
532    }
533
534    None
535}
536
537/// CP210x device — owns the shared control pipe on endpoint 0.
538///
539/// Open one [`Cp210xPort`] per UART interface via [`Cp210xDevice::port`].
540/// Control requests from all open ports are serialized through the
541/// device's internal async mutex; bulk I/O on different ports runs
542/// concurrently.
543///
544/// Construct with [`Cp210xDevice::new`] for the common single-task case;
545/// the control-pipe mutex is a [`NoopRawMutex`] and has no runtime cost.
546/// To use ports concurrently, construct with [`Cp210xDevice::new_with_raw_mutex`]
547/// and pick a `Sync` raw mutex:
548///
549/// ```rust,ignore
550/// use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
551/// let device = Cp210xDevice::<_, CriticalSectionRawMutex>::new_with_raw_mutex(
552///     &bus, &enum_info,
553/// )?;
554/// ```
555pub struct Cp210xDevice<'d, A, M = NoopRawMutex>
556where
557    A: UsbHostAllocator<'d>,
558    M: RawMutex,
559{
560    alloc: A,
561    /// Device-level control pipe bundled with any state that needs to
562    /// stay consistent with it (currently the lazily-probed baud-rate
563    /// API flavour).
564    ctrl: Mutex<M, CtrlState<A::Pipe<pipe::Control, pipe::InOut>>>,
565    device_address: u8,
566    split: Option<SplitInfo>,
567    _phantom: PhantomData<&'d ()>,
568}
569
570impl<'d, A> Cp210xDevice<'d, A, NoopRawMutex>
571where
572    A: UsbHostAllocator<'d>,
573{
574    /// Allocate the device-level control pipe on endpoint 0, using a
575    /// [`NoopRawMutex`] for the shared control pipe.
576    ///
577    /// The resulting device is `!Sync`. Use this constructor for
578    /// single-port parts, or whenever the device and all its ports
579    /// stay confined to one task. For multi-task sharing, use
580    /// [`Cp210xDevice::new_with_raw_mutex`] instead.
581    ///
582    /// Performs no I/O.
583    pub fn new(alloc: &A, enum_info: &EnumerationInfo) -> Result<Self, Cp210xError> {
584        Self::new_with_raw_mutex(alloc, enum_info)
585    }
586}
587
588impl<'d, A, M> Cp210xDevice<'d, A, M>
589where
590    A: UsbHostAllocator<'d>,
591    M: RawMutex,
592{
593    /// Allocate the device-level control pipe on endpoint 0, using
594    /// the caller-chosen raw mutex `M` for the shared control pipe.
595    ///
596    /// Pick a `Sync` raw mutex (e.g. `CriticalSectionRawMutex`) to
597    /// drive multiple ports concurrently. For single-port devices,
598    /// prefer [`Cp210xDevice::new`].
599    ///
600    /// Performs no I/O.
601    pub fn new_with_raw_mutex(alloc: &A, enum_info: &EnumerationInfo) -> Result<Self, Cp210xError> {
602        let ctrl_ep_info = EndpointInfo {
603            addr: EndpointAddress::from_parts(0, UsbDirection::In),
604            ep_type: EndpointType::Control,
605            max_packet_size: enum_info.device_desc.max_packet_size0 as u16,
606            interval_ms: 0,
607        };
608
609        let device_address = enum_info.device_address;
610        let split = enum_info.split();
611
612        let ctrl = alloc
613            .alloc_pipe::<pipe::Control, pipe::InOut>(device_address, &ctrl_ep_info, split)
614            .map_err(|_| Cp210xError::NoPipe)?;
615
616        Ok(Self {
617            alloc: alloc.clone(),
618            ctrl: Mutex::new(CtrlState {
619                pipe: ctrl,
620                baud_mode: BaudMode::Unknown,
621            }),
622            device_address,
623            split,
624            _phantom: PhantomData,
625        })
626    }
627
628    /// Open the `interface_idx`-th UART port.
629    ///
630    /// Locates the `interface_idx`-th vendor-class interface with a
631    /// bulk IN / bulk OUT pair (use `0` for single-port parts; `0..2`
632    /// for CP2105; `0..4` for CP2108) and allocates its bulk pipes.
633    ///
634    /// No I/O is performed with the device. The caller must call
635    /// [`Cp210xPort::enable`] before transferring data; existing
636    /// baud-rate, line-coding, flow-control, and modem-line settings
637    /// inside the CP210x are preserved.
638    ///
639    /// The driver does not validate which port is in use.
640    /// Avoid opening the same port multiple times.
641    pub fn port<'dev>(
642        &'dev self,
643        config_desc: &[u8],
644        interface_idx: u8,
645    ) -> Result<Cp210xPort<'dev, 'd, A, M>, Cp210xError> {
646        let info = find_cp210x(config_desc, interface_idx).ok_or(Cp210xError::NoInterface)?;
647
648        let in_ep_info = EndpointInfo {
649            addr: EndpointAddress::from_parts((info.bulk_in_ep & 0x0F) as usize, UsbDirection::In),
650            ep_type: EndpointType::Bulk,
651            max_packet_size: info.bulk_in_mps,
652            interval_ms: 0,
653        };
654
655        let out_ep_info = EndpointInfo {
656            addr: EndpointAddress::from_parts((info.bulk_out_ep & 0x0F) as usize, UsbDirection::Out),
657            ep_type: EndpointType::Bulk,
658            max_packet_size: info.bulk_out_mps,
659            interval_ms: 0,
660        };
661
662        let in_ch = self
663            .alloc
664            .alloc_pipe::<pipe::Bulk, pipe::In>(self.device_address, &in_ep_info, self.split)
665            .map_err(|_| Cp210xError::NoPipe)?;
666        let out_ch = self
667            .alloc
668            .alloc_pipe::<pipe::Bulk, pipe::Out>(self.device_address, &out_ep_info, self.split)
669            .map_err(|_| Cp210xError::NoPipe)?;
670
671        Ok(Cp210xPort {
672            device: self,
673            in_ch,
674            out_ch,
675            interface: info.interface,
676        })
677    }
678}
679
680/// A single UART port on a [`Cp210xDevice`].
681///
682/// Owns the bulk IN/OUT pipes for one interface and borrows the
683/// device for control requests.
684pub struct Cp210xPort<'dev, 'd, A, M = NoopRawMutex>
685where
686    A: UsbHostAllocator<'d>,
687    M: RawMutex,
688{
689    device: &'dev Cp210xDevice<'d, A, M>,
690    in_ch: A::Pipe<pipe::Bulk, pipe::In>,
691    out_ch: A::Pipe<pipe::Bulk, pipe::Out>,
692    interface: u8,
693}
694
695impl<'dev, 'd, A, M> Cp210xPort<'dev, 'd, A, M>
696where
697    A: UsbHostAllocator<'d>,
698    M: RawMutex,
699{
700    /// USB interface number this port is bound to.
701    pub fn interface(&self) -> u8 {
702        self.interface
703    }
704
705    async fn vendor_out(&mut self, request: u8, value: u16, data: &[u8]) -> Result<(), Cp210xError> {
706        let mut ctrl = self.device.ctrl.lock().await;
707        vendor_out(&mut ctrl.pipe, self.interface, request, value, data).await
708    }
709
710    async fn vendor_in(&mut self, request: u8, value: u16, buf: &mut [u8]) -> Result<(), Cp210xError> {
711        let mut ctrl = self.device.ctrl.lock().await;
712        vendor_in(&mut ctrl.pipe, self.interface, request, value, buf).await
713    }
714
715    /// Enable the UART interface.
716    ///
717    /// Issues `IFC_ENABLE(1)`.
718    pub async fn enable(&mut self) -> Result<(), Cp210xError> {
719        self.vendor_out(IFC_ENABLE, 1, &[]).await
720    }
721
722    /// Disable the UART interface.
723    ///
724    /// Issues `IFC_ENABLE(0)`.
725    pub async fn disable(&mut self) -> Result<(), Cp210xError> {
726        self.vendor_out(IFC_ENABLE, 0, &[]).await
727    }
728
729    /// Program the UART baud rate in bauds per second.
730    ///
731    /// Issues `SET_BAUDRATE`, or `SET_BAUDDIV` with a rounded divisor
732    /// of the 3.6864 MHz baud-rate generator clock on legacy firmware
733    /// that stalls the modern request (CP2101, early CP2102/3). The
734    /// flavour is probed on the first baud-rate call and cached on
735    /// the device.
736    pub async fn set_baud_rate(&mut self, baud: u32) -> Result<(), Cp210xError> {
737        if baud == 0 {
738            return Err(Cp210xError::InvalidArgument);
739        }
740        let iface = self.interface;
741        let mut ctrl = self.device.ctrl.lock().await;
742        let CtrlState { pipe, baud_mode } = &mut *ctrl;
743        match *baud_mode {
744            BaudMode::Direct => set_baud_rate_direct(pipe, iface, baud).await,
745            BaudMode::Divisor => set_baud_rate_divisor(pipe, iface, baud).await,
746            BaudMode::Unknown => match set_baud_rate_direct(pipe, iface, baud).await {
747                Ok(()) => {
748                    *baud_mode = BaudMode::Direct;
749                    Ok(())
750                }
751                Err(Cp210xError::Transfer(PipeError::Stall)) => {
752                    *baud_mode = BaudMode::Divisor;
753                    set_baud_rate_divisor(pipe, iface, baud).await
754                }
755                Err(e) => Err(e),
756            },
757        }
758    }
759
760    /// Read the UART baud rate in bauds per second.
761    ///
762    /// Issues `GET_BAUDRATE`, falling back to `GET_BAUDDIV` (divided
763    /// out through the 3.6864 MHz baud-rate generator clock) on legacy
764    /// firmware that stalls the modern request.
765    pub async fn baud_rate(&mut self) -> Result<u32, Cp210xError> {
766        let iface = self.interface;
767        let mut ctrl = self.device.ctrl.lock().await;
768        let CtrlState { pipe, baud_mode } = &mut *ctrl;
769        match *baud_mode {
770            BaudMode::Direct => get_baud_rate_direct(pipe, iface).await,
771            BaudMode::Divisor => get_baud_rate_divisor(pipe, iface).await,
772            BaudMode::Unknown => match get_baud_rate_direct(pipe, iface).await {
773                Ok(b) => {
774                    *baud_mode = BaudMode::Direct;
775                    Ok(b)
776                }
777                Err(Cp210xError::Transfer(PipeError::Stall)) => {
778                    *baud_mode = BaudMode::Divisor;
779                    get_baud_rate_divisor(pipe, iface).await
780                }
781                Err(e) => Err(e),
782            },
783        }
784    }
785
786    /// Program baud rate, data/stop bits and parity.
787    ///
788    /// Issues `SET_LINE_CTL` followed by [`set_baud_rate`](Self::set_baud_rate).
789    ///
790    /// # Cancellation
791    ///
792    /// Not cancel-safe: dropping the future between the two control
793    /// transfers leaves the device with the new framing but the old
794    /// baud rate. Re-issue the full line coding before resuming data
795    /// transfer.
796    pub async fn set_line_coding(&mut self, coding: &LineCoding) -> Result<(), Cp210xError> {
797        if !matches!(coding.data_bits, 5..=8) {
798            return Err(Cp210xError::InvalidArgument);
799        }
800        let line_ctl = (coding.stop_bits as u16) | ((coding.parity as u16) << 4) | ((coding.data_bits as u16) << 8);
801        self.vendor_out(SET_LINE_CTL, line_ctl, &[]).await?;
802        self.set_baud_rate(coding.baud_rate).await
803    }
804
805    /// Read baud rate, data/stop bits and parity.
806    ///
807    /// Issues `GET_LINE_CTL` followed by [`baud_rate`](Self::baud_rate).
808    pub async fn line_coding(&mut self) -> Result<LineCoding, Cp210xError> {
809        let mut buf = [0u8; 2];
810        self.vendor_in(GET_LINE_CTL, 0, &mut buf).await?;
811        let ctl = u16::from_le_bytes(buf);
812        let stop_bits = StopBits::from_bits((ctl & 0xF) as u8).ok_or(Cp210xError::InvalidResponse)?;
813        let parity = Parity::from_bits(((ctl >> 4) & 0xF) as u8).ok_or(Cp210xError::InvalidResponse)?;
814        let data_bits = (ctl >> 8) as u8;
815        if !matches!(data_bits, 5..=8) {
816            return Err(Cp210xError::InvalidResponse);
817        }
818        let baud_rate = self.baud_rate().await?;
819        Ok(LineCoding {
820            baud_rate,
821            data_bits,
822            parity,
823            stop_bits,
824        })
825    }
826
827    /// Drive DTR and RTS to the given levels.
828    ///
829    /// Issues `SET_MHS` asserting both DTR and RTS masks, overriding
830    /// any [`DtrMode::FlowControl`] or [`RtsMode::FlowControl`] setting
831    /// until the next flow-control event.
832    pub async fn set_control_line_state(&mut self, dtr: bool, rts: bool) -> Result<(), Cp210xError> {
833        let value = (dtr as u16) | ((rts as u16) << 1) | (1 << 8) | (1 << 9);
834        self.vendor_out(SET_MHS, value, &[]).await
835    }
836
837    /// Read the modem status lines.
838    ///
839    /// Issues `GET_MDMSTS`. Reserved bits 2 and 3 of the response are
840    /// discarded.
841    pub async fn modem_status(&mut self) -> Result<ModemStatus, Cp210xError> {
842        let mut buf = [0u8; 1];
843        self.vendor_in(GET_MDMSTS, 0, &mut buf).await?;
844        Ok(ModemStatus::from_bits_truncate(buf[0]))
845    }
846
847    /// Assert or release a break condition on TX.
848    ///
849    /// Issues `SET_BREAK`.
850    pub async fn set_break(&mut self, asserted: bool) -> Result<(), Cp210xError> {
851        self.vendor_out(SET_BREAK, asserted as u16, &[]).await
852    }
853
854    /// Clear the selected TX and/or RX queues.
855    ///
856    /// Issues `PURGE` with the given mask. On CP2102N this also clears
857    /// the current `SET_FLOW` configuration; re-apply flow control
858    /// afterwards if needed.
859    pub async fn purge(&mut self, mask: PurgeMask) -> Result<(), Cp210xError> {
860        self.vendor_out(PURGE, mask.bits(), &[]).await
861    }
862
863    /// Apply a flow-control configuration.
864    ///
865    /// Issues `SET_FLOW`. For bits not covered by [`FlowControl`], use
866    /// [`set_flow_control_raw`](Self::set_flow_control_raw).
867    pub async fn set_flow_control(&mut self, fc: &FlowControl) -> Result<(), Cp210xError> {
868        let bytes = fc.to_bytes();
869        self.set_flow_control_raw(&bytes).await
870    }
871
872    /// Apply a raw flow-control configuration.
873    ///
874    /// Issues `SET_FLOW` with the given payload. Layout is little-endian
875    /// `ulControlHandshake` | `ulFlowReplace` | `ulXonLimit` |
876    /// `ulXoffLimit` (4 B each); see AN571 Tables 9–11.
877    pub async fn set_flow_control_raw(&mut self, raw: &[u8; 16]) -> Result<(), Cp210xError> {
878        self.vendor_out(SET_FLOW, 0, raw).await
879    }
880
881    /// Read the flow-control configuration.
882    ///
883    /// Issues `GET_FLOW`.
884    pub async fn flow_control(&mut self) -> Result<FlowControl, Cp210xError> {
885        let mut buf = [0u8; 16];
886        self.vendor_in(GET_FLOW, 0, &mut buf).await?;
887        Ok(FlowControl::from_bytes(&buf))
888    }
889
890    /// Read bytes from the UART receive stream.
891    ///
892    /// # Cancellation
893    ///
894    /// Not cancel-safe: bytes already received from the device but
895    /// not yet copied into `buf` are lost if the future is dropped.
896    pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Cp210xError> {
897        Ok(self.in_ch.request_in(buf).await?)
898    }
899
900    /// Write bytes to the UART transmit stream.
901    ///
902    /// # Cancellation
903    ///
904    /// Not cancel-safe: the remote may observe partial data if the
905    /// future is dropped mid-transfer.
906    pub async fn write(&mut self, data: &[u8]) -> Result<usize, Cp210xError> {
907        self.out_ch.request_out(data, false).await?;
908        Ok(data.len())
909    }
910}
911
912impl<'dev, 'd, A, M> embedded_io_async::ErrorType for Cp210xPort<'dev, 'd, A, M>
913where
914    A: UsbHostAllocator<'d>,
915    M: RawMutex,
916{
917    type Error = Cp210xError;
918}
919
920impl<'dev, 'd, A, M> embedded_io_async::Read for Cp210xPort<'dev, 'd, A, M>
921where
922    A: UsbHostAllocator<'d>,
923    M: RawMutex,
924{
925    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
926        Cp210xPort::read(self, buf).await
927    }
928}
929
930impl<'dev, 'd, A, M> embedded_io_async::Write for Cp210xPort<'dev, 'd, A, M>
931where
932    A: UsbHostAllocator<'d>,
933    M: RawMutex,
934{
935    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
936        Cp210xPort::write(self, buf).await
937    }
938
939    async fn flush(&mut self) -> Result<(), Self::Error> {
940        Ok(())
941    }
942}