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, UsbSerialError};
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    // Adapter clones share this handle under a mutex. A detach can close it
31    // before a queued operation acquires that mutex; never enter a torn-down driver.
32    fn ensure_open(&self) -> Result<()> {
33        if self.closed {
34            Err(UsbSerialError::Disconnected)
35        } else {
36            Ok(())
37        }
38    }
39
40    /// Bulk OUT write. Opens OUT only — IN belongs to the optional [`Self::start_reader`].
41    pub fn write(&mut self, data: &[u8]) -> Result<usize> {
42        self.ensure_open()?;
43        self.driver.write(data)
44    }
45
46    /// Blocking/synchronous bulk IN read through the driver (not the background reader).
47    pub fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
48        self.ensure_open()?;
49        self.driver.read(buf)
50    }
51
52    /// Baud / framing line coding.
53    pub fn set_line_config(&mut self, cfg: LineConfig) -> Result<()> {
54        self.ensure_open()?;
55        self.driver.set_line_config(cfg)
56    }
57
58    pub fn set_flow_control(&mut self, flow: FlowControl) -> Result<()> {
59        self.ensure_open()?;
60        self.driver.set_flow_control(flow)
61    }
62
63    pub fn set_dtr(&mut self, value: bool) -> Result<()> {
64        self.ensure_open()?;
65        self.driver.set_dtr(value)
66    }
67
68    pub fn set_rts(&mut self, value: bool) -> Result<()> {
69        self.ensure_open()?;
70        self.driver.set_rts(value)
71    }
72
73    pub fn set_break(&mut self, enabled: bool) -> Result<()> {
74        self.ensure_open()?;
75        self.driver.set_break(enabled)
76    }
77
78    /// Clear RX and/or TX driver buffers (host-side purge).
79    pub fn purge(&mut self, kind: PurgeKind) -> Result<()> {
80        self.ensure_open()?;
81        self.driver.purge(kind)
82    }
83
84    /// Alias for [`Self::purge`] (clear input/output buffers).
85    pub fn clear(&mut self, kind: PurgeKind) -> Result<()> {
86        self.purge(kind)
87    }
88
89    /// Latched modem status lines (CTS/DSR/RI/CD), when the chip reports them.
90    pub fn modem_status(&mut self) -> Result<ModemStatus> {
91        self.ensure_open()?;
92        self.driver.modem_status()
93    }
94
95    /// Stop the bulk-IN reader, close the driver, then mark closed (idempotent).
96    pub fn close(&mut self) {
97        if self.closed {
98            return;
99        }
100        self.stop_reader();
101        let _ = self.driver.close();
102        self.closed = true;
103    }
104
105    /// Start the background bulk-IN reader (takes ownership of the driver's IN endpoint).
106    ///
107    /// Call **after** [`Self::set_line_config`] and DTR/RTS on weak OTG / CH340 adapters.
108    pub fn start_reader(&mut self) -> Result<()> {
109        self.ensure_open()?;
110        if self.reader.is_some() {
111            return Ok(());
112        }
113        let reader = self.driver.start_reader()?;
114        self.reader = Some(reader);
115        Ok(())
116    }
117
118    /// Non-blocking read from the background reader if running; else [`Self::read`].
119    pub fn try_read(&mut self, buf: &mut [u8]) -> Result<usize> {
120        self.ensure_open()?;
121        if let Some(reader) = &mut self.reader {
122            return reader.try_read(buf);
123        }
124        self.read(buf)
125    }
126
127    /// Stop and join the background reader without closing the port.
128    pub fn stop_reader(&mut self) {
129        if let Some(mut r) = self.reader.take() {
130            r.stop();
131        }
132    }
133
134    /// Shared transport (for advanced control or re-probe).
135    pub fn transport(&self) -> &SharedTransport {
136        &self.transport
137    }
138}
139
140impl Drop for SerialPortHandle {
141    fn drop(&mut self) {
142        self.close();
143    }
144}