Skip to main content

android_usb_serial/
device.rs

1//! Device discovery and port enumeration.
2//!
3//! Use [`describe_device`] after the transport is ready (interfaces readable) to learn
4//! how many serial ports the chip exposes, then [`open_port`] with a zero-based index.
5
6use crate::drivers::create_driver;
7use crate::error::{Result, UsbSerialError};
8use crate::probe::{DriverType, ProbeTable};
9use crate::transport::SharedTransport;
10
11/// One logical serial port exposed by a USB composite / multi-interface device.
12#[derive(Debug, Clone)]
13pub struct PortDescriptor {
14    /// Zero-based index passed to [`open_port`].
15    pub port_index: usize,
16    /// Driver selected by [`ProbeTable`] for this product.
17    pub driver: DriverType,
18    pub vendor_id: u16,
19    pub product_id: u16,
20}
21
22/// USB device identify + probed driver and ports.
23#[derive(Debug, Clone)]
24pub struct DeviceDescriptor {
25    pub vendor_id: u16,
26    pub product_id: u16,
27    pub driver: DriverType,
28    /// One entry per openable port (`port_index` 0..n).
29    pub ports: Vec<PortDescriptor>,
30}
31
32/// Probe VID/PID + interface layout without opening a driver session.
33pub fn describe_device(transport: &SharedTransport) -> Result<DeviceDescriptor> {
34    let desc = transport.raw_device_descriptor();
35    let vendor_id = u16::from_le_bytes([desc[8], desc[9]]);
36    let product_id = u16::from_le_bytes([desc[10], desc[11]]);
37    let table = ProbeTable::default_table();
38    let ifaces = transport.interfaces();
39    let driver = table.find(vendor_id, product_id, &ifaces);
40    let count = table.port_count(driver, &ifaces).max(1);
41    let ports = (0..count)
42        .map(|i| PortDescriptor {
43            port_index: i,
44            driver,
45            vendor_id,
46            product_id,
47        })
48        .collect();
49    Ok(DeviceDescriptor {
50        vendor_id,
51        product_id,
52        driver,
53        ports,
54    })
55}
56
57/// Open port `port_index` (0 for single-port chips like CH340).
58///
59/// Claims / initializes the matching vendor driver on `transport`. The reader is **not**
60/// started here — call [`crate::SerialPortHandle::start_reader`] after line config / DTR.
61pub fn open_port(
62    transport: SharedTransport,
63    port_index: usize,
64) -> Result<crate::port::SerialPortHandle> {
65    let device = describe_device(&transport)?;
66    let port = device
67        .ports
68        .get(port_index)
69        .ok_or_else(|| UsbSerialError::Unsupported(format!("port {port_index}")))?;
70    let mut driver = create_driver(port.driver, port_index);
71    driver.open(&transport)?;
72    Ok(crate::port::SerialPortHandle::new(transport, driver))
73}