Skip to main content

android_usb_serial/
port.rs

1//! High-level serial port handle over a proprietary USB serial driver.
2//!
3//! Prefer this API for Android/Linux USB adapters. With feature `serialport-compat`, wrap
4//! the handle in [`crate::serialport_compat`] for a `serialport::SerialPort` facade.
5
6use crate::config::{FlowControl, LineConfig, PurgeKind};
7use crate::drivers::{Driver, ModemStatus};
8use crate::error::Result;
9use crate::reader::SerialReader;
10use crate::transport::SharedTransport;
11
12/// Open USB serial session: sync I/O plus optional background bulk-IN reader.
13pub struct SerialPortHandle {
14    transport: SharedTransport,
15    driver: Box<dyn Driver>,
16    reader: Option<SerialReader>,
17    closed: bool,
18}
19
20impl SerialPortHandle {
21    pub(crate) fn new(transport: SharedTransport, driver: Box<dyn Driver>) -> Self {
22        Self {
23            transport,
24            driver,
25            reader: None,
26            closed: false,
27        }
28    }
29
30    /// Bulk OUT write. Opens OUT only — IN belongs to the optional [`Self::start_reader`].
31    pub fn write(&mut self, data: &[u8]) -> Result<usize> {
32        self.driver.write(data)
33    }
34
35    /// Blocking/synchronous bulk IN read through the driver (not the background reader).
36    pub fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
37        self.driver.read(buf)
38    }
39
40    /// Baud / framing line coding.
41    pub fn set_line_config(&mut self, cfg: LineConfig) -> Result<()> {
42        self.driver.set_line_config(cfg)
43    }
44
45    pub fn set_flow_control(&mut self, flow: FlowControl) -> Result<()> {
46        self.driver.set_flow_control(flow)
47    }
48
49    pub fn set_dtr(&mut self, value: bool) -> Result<()> {
50        self.driver.set_dtr(value)
51    }
52
53    pub fn set_rts(&mut self, value: bool) -> Result<()> {
54        self.driver.set_rts(value)
55    }
56
57    pub fn set_break(&mut self, enabled: bool) -> Result<()> {
58        self.driver.set_break(enabled)
59    }
60
61    /// Clear RX and/or TX driver buffers (host-side purge).
62    pub fn purge(&mut self, kind: PurgeKind) -> Result<()> {
63        self.driver.purge(kind)
64    }
65
66    /// Alias for [`Self::purge`] (clear input/output buffers).
67    pub fn clear(&mut self, kind: PurgeKind) -> Result<()> {
68        self.purge(kind)
69    }
70
71    /// Latched modem status lines (CTS/DSR/RI/CD), when the chip reports them.
72    pub fn modem_status(&mut self) -> Result<ModemStatus> {
73        self.driver.modem_status()
74    }
75
76    /// Stop the bulk-IN reader, close the driver, then mark closed (idempotent).
77    pub fn close(&mut self) {
78        if self.closed {
79            return;
80        }
81        self.stop_reader();
82        let _ = self.driver.close();
83        self.closed = true;
84    }
85
86    /// Start the background bulk-IN reader (takes ownership of the driver's IN endpoint).
87    ///
88    /// Call **after** [`Self::set_line_config`] and DTR/RTS on weak OTG / CH340 adapters.
89    pub fn start_reader(&mut self) -> Result<()> {
90        if self.reader.is_some() {
91            return Ok(());
92        }
93        let reader = self.driver.start_reader()?;
94        self.reader = Some(reader);
95        Ok(())
96    }
97
98    /// Non-blocking read from the background reader if running; else [`Self::read`].
99    pub fn try_read(&mut self, buf: &mut [u8]) -> Result<usize> {
100        if let Some(reader) = &mut self.reader {
101            return reader.try_read(buf);
102        }
103        self.read(buf)
104    }
105
106    /// Stop and join the background reader without closing the port.
107    pub fn stop_reader(&mut self) {
108        if let Some(mut r) = self.reader.take() {
109            r.stop();
110        }
111    }
112
113    /// Shared transport (for advanced control or re-probe).
114    pub fn transport(&self) -> &SharedTransport {
115        &self.transport
116    }
117}
118
119impl Drop for SerialPortHandle {
120    fn drop(&mut self) {
121        self.close();
122    }
123}