Skip to main content

android_usb_serial/drivers/
cdc_acm.rs

1//! CDC ACM driver.
2
3use super::{line_coding_bytes, Driver, EndpointPair, ModemStatus, WRITE_TIMEOUT_MS};
4use crate::config::{FlowControl, LineConfig, PurgeKind};
5use crate::error::{ReadOutcome, Result, UsbSerialError};
6use crate::reader::SerialReader;
7use crate::transport::{
8    BulkIn, ControlRequest, SharedTransport, USB_RECIP_INTERFACE, USB_TYPE_CLASS,
9};
10use std::sync::atomic::{AtomicBool, AtomicU16, Ordering};
11use std::sync::{Arc, Mutex};
12use std::thread::{self, JoinHandle};
13
14const USB_SUBCLASS_ACM: u8 = 2;
15const SET_LINE_CODING: u8 = 0x20;
16const SET_CONTROL_LINE_STATE: u8 = 0x22;
17const SEND_BREAK: u8 = 0x23;
18const NOTIFICATION_REQUEST_TYPE: u8 = 0xa1;
19const SERIAL_STATE_NOTIFICATION: u8 = 0x20;
20const SERIAL_STATE_NOTIFICATION_SIZE: usize = 10;
21const SERIAL_STATE_RX_CARRIER: u16 = 1 << 0;
22const SERIAL_STATE_TX_CARRIER: u16 = 1 << 1;
23const SERIAL_STATE_RING_SIGNAL: u16 = 1 << 3;
24const NOTIFICATION_READ_TIMEOUT_MS: u32 = 200;
25
26struct CdcNotificationReader {
27    state: Arc<AtomicU16>,
28    error: Arc<Mutex<Option<String>>>,
29    stop: Arc<AtomicBool>,
30    thread: Option<JoinHandle<()>>,
31}
32
33impl CdcNotificationReader {
34    fn start(mut endpoint: Box<dyn BulkIn>, max_packet_size: u16, control_index: u8) -> Self {
35        let state = Arc::new(AtomicU16::new(0));
36        let error = Arc::new(Mutex::new(None));
37        let stop = Arc::new(AtomicBool::new(false));
38        let thread_state = Arc::clone(&state);
39        let thread_error = Arc::clone(&error);
40        let thread_stop = Arc::clone(&stop);
41        let buffer_size = usize::from(max_packet_size).max(SERIAL_STATE_NOTIFICATION_SIZE);
42
43        let thread = thread::spawn(move || {
44            let mut buffer = vec![0; buffer_size];
45            while !thread_stop.load(Ordering::Relaxed) {
46                match endpoint.read(&mut buffer, NOTIFICATION_READ_TIMEOUT_MS) {
47                    Ok(ReadOutcome::Data(data)) if !data.is_empty() => {
48                        // Skip junk / other CDC notifications; keep polling like
49                        // usb-serial-for-android. Only transport I/O errors stop the thread.
50                        if let Some(serial_state) =
51                            parse_serial_state_notification(&data, control_index)
52                        {
53                            thread_state.store(serial_state, Ordering::Relaxed);
54                        }
55                    }
56                    Ok(ReadOutcome::TimedOut) | Ok(ReadOutcome::Data(_)) => {}
57                    Ok(ReadOutcome::Cancelled) => break,
58                    Err(error) => {
59                        if !thread_stop.load(Ordering::Relaxed) {
60                            *thread_error.lock().unwrap() = Some(error.to_string());
61                        }
62                        break;
63                    }
64                }
65            }
66        });
67
68        Self {
69            state,
70            error,
71            stop,
72            thread: Some(thread),
73        }
74    }
75
76    fn modem_status(&self) -> Result<ModemStatus> {
77        if let Some(error) = self.error.lock().unwrap().take() {
78            return Err(UsbSerialError::Io(error));
79        }
80
81        let state = self.state.load(Ordering::Relaxed);
82        Ok(ModemStatus {
83            cts: false,
84            dsr: state & SERIAL_STATE_TX_CARRIER != 0,
85            ri: state & SERIAL_STATE_RING_SIGNAL != 0,
86            cd: state & SERIAL_STATE_RX_CARRIER != 0,
87        })
88    }
89
90    fn stop(&mut self) {
91        self.stop.store(true, Ordering::Relaxed);
92        if let Some(thread) = self.thread.take() {
93            let _ = thread.join();
94        }
95    }
96}
97
98impl Drop for CdcNotificationReader {
99    fn drop(&mut self) {
100        self.stop();
101    }
102}
103
104/// Returns `Some(bitmap)` for a valid SERIAL_STATE frame, otherwise `None`.
105/// Malformed / unrelated interrupt payloads are ignored so the reader stays alive.
106fn parse_serial_state_notification(data: &[u8], control_index: u8) -> Option<u16> {
107    if data.len() < SERIAL_STATE_NOTIFICATION_SIZE {
108        return None;
109    }
110    if data[0] != NOTIFICATION_REQUEST_TYPE || data[1] != SERIAL_STATE_NOTIFICATION {
111        return None;
112    }
113    let index = u16::from_le_bytes([data[4], data[5]]);
114    if index != u16::from(control_index) {
115        return None;
116    }
117    let payload_size = usize::from(u16::from_le_bytes([data[6], data[7]]));
118    // SERIAL_STATE carries 2 UART-state bytes; allow longer transfers (host padding).
119    if payload_size < 2 || data.len() < 8 + 2 {
120        return None;
121    }
122    Some(u16::from_le_bytes([data[8], data[9]]))
123}
124
125pub struct CdcAcmDriver {
126    port_index: usize,
127    control_index: u8,
128    control_iface: u8,
129    data_iface: u8,
130    dtr: bool,
131    rts: bool,
132    endpoints: Option<EndpointPair>,
133    transport: Option<SharedTransport>,
134    control_claimed: bool,
135    data_claimed: bool,
136    reader: Option<SerialReader>,
137    notification_reader: Option<CdcNotificationReader>,
138}
139
140impl CdcAcmDriver {
141    pub fn new(port_index: usize) -> Self {
142        Self {
143            port_index,
144            control_index: 0,
145            control_iface: 0,
146            data_iface: 0,
147            dtr: false,
148            rts: false,
149            endpoints: None,
150            transport: None,
151            control_claimed: false,
152            data_claimed: false,
153            reader: None,
154            notification_reader: None,
155        }
156    }
157
158    fn acm_control(&self, request: u8, value: u16, data: Vec<u8>) -> Result<()> {
159        let transport = self.transport.as_ref().unwrap();
160        let req = ControlRequest {
161            request_type: USB_TYPE_CLASS | USB_RECIP_INTERFACE,
162            request,
163            value,
164            index: self.control_index as u16,
165            data,
166            timeout_ms: WRITE_TIMEOUT_MS,
167        };
168        transport.control_out(&req)?;
169        Ok(())
170    }
171
172    fn resolve_interfaces(&mut self, transport: &SharedTransport) -> Result<()> {
173        let ifaces = transport.interfaces();
174        let desc = transport.raw_device_descriptor();
175        let is_iad = desc.len() >= 7 && desc[4] == 0xEF && desc[5] == 0x02 && desc[6] == 0x01;
176        if is_iad {
177            if let Some((ctrl, data)) = resolve_iad_pair(transport, self.port_index) {
178                self.control_iface = ctrl;
179                self.data_iface = data;
180                self.control_index = ctrl;
181                return Ok(());
182            }
183        }
184        let comm: Vec<u8> = ifaces
185            .iter()
186            .filter(|i| i.class == 2 && i.subclass == USB_SUBCLASS_ACM)
187            .map(|i| i.id)
188            .collect();
189        let data: Vec<u8> = ifaces
190            .iter()
191            .filter(|i| i.class == 10)
192            .map(|i| i.id)
193            .collect();
194        if comm.is_empty() && data.is_empty() {
195            // single-interface castrated ACM
196            if let Some(iface) = ifaces.first() {
197                self.control_iface = iface.id;
198                self.data_iface = iface.id;
199                self.control_index = iface.id;
200                return Ok(());
201            }
202            return Err(UsbSerialError::ProbeFailed("no CDC interfaces".into()));
203        }
204        if comm.is_empty() {
205            return Err(UsbSerialError::ProbeFailed("no CDC comm interfaces".into()));
206        }
207        let idx = self.port_index.min(comm.len() - 1);
208        self.control_iface = comm[idx];
209        self.data_iface = data.get(idx).copied().unwrap_or(comm[idx]);
210        self.control_index = self.control_iface;
211        Ok(())
212    }
213}
214
215fn resolve_iad_pair(transport: &SharedTransport, port_index: usize) -> Option<(u8, u8)> {
216    let raw = transport.raw_descriptors();
217    let mut iad_ports: Vec<(u8, u8)> = Vec::new();
218    let mut pos = 0usize;
219    while pos + 2 <= raw.len() {
220        let len = raw[pos] as usize;
221        if len < 2 || pos + len > raw.len() {
222            break;
223        }
224        if raw[pos + 1] == 0x0B && len >= 8 && raw[pos + 4] == 2 && raw[pos + 5] == 2 {
225            let first = raw[pos + 2];
226            let count = raw[pos + 3];
227            if count >= 2 {
228                iad_ports.push((first, first + 1));
229            }
230        }
231        pos += len;
232    }
233    iad_ports.get(port_index).copied()
234}
235
236impl Driver for CdcAcmDriver {
237    fn open(&mut self, transport: &SharedTransport) -> Result<()> {
238        self.resolve_interfaces(transport)?;
239        self.transport = Some(transport.clone());
240
241        let result = (|| {
242            transport.claim_interface(self.control_iface)?;
243            self.control_claimed = true;
244            if self.data_iface != self.control_iface {
245                transport.claim_interface(self.data_iface)?;
246                self.data_claimed = true;
247            }
248
249            if let Some(endpoint) = transport
250                .endpoints(self.control_iface)
251                .into_iter()
252                .find(|endpoint| endpoint.is_interrupt_in())
253            {
254                let interrupt_in =
255                    transport.open_interrupt_in(endpoint.address, endpoint.max_packet_size)?;
256                self.notification_reader = Some(CdcNotificationReader::start(
257                    interrupt_in,
258                    endpoint.max_packet_size,
259                    self.control_index,
260                ));
261            }
262
263            self.endpoints = Some(EndpointPair::open(transport, self.data_iface)?);
264            Ok(())
265        })();
266
267        if result.is_err() {
268            let _ = self.close();
269        }
270        result
271    }
272
273    fn close(&mut self) -> Result<()> {
274        if let Some(mut r) = self.reader.take() {
275            r.stop();
276        }
277        if let Some(mut r) = self.notification_reader.take() {
278            r.stop();
279        }
280        if let Some(t) = &self.transport {
281            if self.data_claimed {
282                self.data_claimed = false;
283                let _ = t.release_interface(self.data_iface);
284            }
285            if self.control_claimed {
286                self.control_claimed = false;
287                let _ = t.release_interface(self.control_iface);
288            }
289        }
290        self.endpoints = None;
291        Ok(())
292    }
293
294    fn write(&mut self, data: &[u8]) -> Result<usize> {
295        let transport = self.transport.as_ref().unwrap();
296        self.endpoints.as_mut().unwrap().write(transport, data)
297    }
298
299    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
300        if let Some(reader) = &mut self.reader {
301            return reader.try_read(buf);
302        }
303        Ok(0)
304    }
305
306    fn set_line_config(&mut self, cfg: LineConfig) -> Result<()> {
307        self.acm_control(SET_LINE_CODING, 0, line_coding_bytes(&cfg).to_vec())
308    }
309
310    fn set_flow_control(&mut self, flow: FlowControl) -> Result<()> {
311        if flow == FlowControl::None {
312            Ok(())
313        } else {
314            Err(UsbSerialError::Unsupported("flow control".into()))
315        }
316    }
317
318    fn set_dtr(&mut self, value: bool) -> Result<()> {
319        self.dtr = value;
320        let v = (self.rts as u16) << 1 | (self.dtr as u16);
321        self.acm_control(SET_CONTROL_LINE_STATE, v, vec![])
322    }
323
324    fn set_rts(&mut self, value: bool) -> Result<()> {
325        self.rts = value;
326        let v = (self.rts as u16) << 1 | (self.dtr as u16);
327        self.acm_control(SET_CONTROL_LINE_STATE, v, vec![])
328    }
329
330    fn set_break(&mut self, enabled: bool) -> Result<()> {
331        self.acm_control(SEND_BREAK, if enabled { 0xffff } else { 0 }, vec![])
332    }
333
334    fn purge(&mut self, _kind: PurgeKind) -> Result<()> {
335        Ok(())
336    }
337
338    fn modem_status(&mut self) -> Result<ModemStatus> {
339        self.notification_reader
340            .as_ref()
341            .map(CdcNotificationReader::modem_status)
342            .unwrap_or_else(|| Ok(ModemStatus::default()))
343    }
344
345    fn bulk_in_mps(&self) -> u16 {
346        self.endpoints.as_ref().map(|e| e.mps).unwrap_or(64)
347    }
348
349    fn take_bulk_in(&mut self) -> Option<Box<dyn crate::transport::BulkIn>> {
350        let transport = self.transport.as_ref()?;
351        self.endpoints.as_mut()?.take_in(transport)
352    }
353}