android_usb_serial/lib.rs
1//! Pure Rust USB serial drivers for Android (and Linux), built on [nusb](https://docs.rs/nusb).
2//!
3//! Protocol logic is ported from
4//! [usb-serial-for-android](https://github.com/mik3y/usb-serial-for-android) and checked against
5//! golden USB control-transfer fixtures under `tests/fixtures/`.
6//!
7//! ## Overview
8//!
9//! This crate does **not** talk to Android `UsbManager`. The host app (typically Kotlin) owns
10//! USB permission, opens a `UsbDeviceConnection`, and passes its raw file descriptor into Rust.
11//! The crate then:
12//!
13//! 1. [`from_raw_fd`] — `dup`s the fd so Java can keep the connection alive.
14//! 2. [`NusbTransport`] — claims interfaces via nusb (`detach_and_claim`).
15//! 3. [`ProbeTable`] / [`open_port`] — selects a vendor driver and returns a [`SerialPortHandle`].
16//!
17//! ```text
18//! UsbManager (Kotlin) → permission → UsbDeviceConnection → fd (unclaimed)
19//! ↓
20//! from_raw_fd / NusbTransport
21//! ↓
22//! ProbeTable → Ftdi / Cp21xx / Ch34x / … drivers
23//! ↓
24//! SerialPortHandle (write / reader / modem / purge)
25//! ```
26//!
27//! ## Android usage
28//!
29//! - Declare `android.hardware.usb.host` and a `device_filter.xml` in the app.
30//! - Request runtime USB permission before `UsbManager.openDevice()`.
31//! - **Do not** call `UsbDeviceConnection.claimInterface()` in Kotlin — nusb claims after
32//! [`from_raw_fd`]. Pre-claim causes `io interface is busy`.
33//! - Keep the `UsbDeviceConnection` open for the whole session; close it only after Rust
34//! [`SerialPortHandle::close`].
35//! - Prefer [`SerialPortHandle::start_reader`] **after** line config and DTR/RTS (important for
36//! weak OTG / CH340).
37//! - Multi-port chips: pass `port_index` to [`open_port`] (`0`, `1`, …). App-level enumerate
38//! often exposes paths as `deviceName` / `deviceName#N`.
39//!
40//! Full Kotlin/permission walkthrough: crate README (*Using on Android*) in the repository.
41//!
42//! ## Quick start (real USB fd)
43//!
44//! ```ignore
45//! // fd from UsbDeviceConnection.fileDescriptor (dup'd inside from_raw_fd)
46//! use android_usb_serial::{from_raw_fd, open_port, NusbTransport, Transport};
47//! use std::sync::Arc;
48//!
49//! let device = from_raw_fd(fd)?;
50//! let transport = Arc::new(NusbTransport::from_device(device)?) as Arc<dyn Transport>;
51//! let mut port = open_port(transport, 0)?;
52//! port.write(b"AT\r\n")?;
53//! ```
54//!
55//! ## Quick start (`fake-transport`)
56//!
57//! ```
58//! # #[cfg(feature = "fake-transport")]
59//! # {
60//! use android_usb_serial::{open_port, FakeTransport, Transport};
61//! use std::sync::Arc;
62//!
63//! let fake = FakeTransport::cdc_single_iface();
64//! let transport: Arc<dyn Transport> = Arc::new(fake.clone());
65//! let mut port = open_port(transport, 0).unwrap();
66//! port.write(b"PING").unwrap();
67//! assert_eq!(fake.take_tx(), b"PING");
68//! # }
69//! ```
70//!
71//! ## Features
72//!
73//! | Feature | Default | Description |
74//! |---------|---------|-------------|
75//! | `serialport-compat` | yes | [`serialport::SerialPort`] adapter ([`serialport_compat`]) |
76//! | `fake-transport` | no | [`FakeTransport`] + `golden_record` binary |
77//!
78//! ## Platform notes
79//!
80//! - **Android / Linux:** real USB via nusb ([`from_raw_fd`], [`NusbTransport`]).
81//! - **Other hosts:** drivers + [`fake`] for tests; supply your own [`Transport`]
82//! for hardware.
83
84#![cfg_attr(docsrs, feature(doc_cfg))]
85
86/// Line / flow / purge configuration types.
87pub mod config;
88/// Device probe and port open helpers.
89pub mod device;
90/// Chip-specific USB serial drivers.
91pub mod drivers;
92/// Error types.
93pub mod error;
94/// High-level serial port handle.
95pub mod port;
96/// VID/PID probe table (ported from usb-serial-for-android).
97pub mod probe;
98/// Continuous bulk-IN reader thread.
99pub mod reader;
100/// RX filter chain (FTDI header strip, XON/XOFF).
101pub mod rx_filter;
102/// USB transport trait and request types.
103pub mod transport;
104/// XON/XOFF inline filter.
105pub mod xonxoff;
106
107#[cfg(feature = "fake-transport")]
108#[cfg_attr(docsrs, doc(cfg(feature = "fake-transport")))]
109/// In-memory [`Transport`](crate::transport::Transport) for golden parity and harnesses.
110pub mod fake;
111
112#[cfg(any(target_os = "android", target_os = "linux"))]
113#[cfg_attr(docsrs, doc(cfg(any(target_os = "android", target_os = "linux"))))]
114/// `nusb`-backed transport (`from_raw_fd` / Android `UsbDeviceConnection`).
115pub mod nusb_transport;
116
117#[cfg(feature = "serialport-compat")]
118#[cfg_attr(docsrs, doc(cfg(feature = "serialport-compat")))]
119/// Adapter implementing [`serialport::SerialPort`].
120pub mod serialport_compat;
121
122pub use config::*;
123pub use device::{describe_device, open_port, DeviceDescriptor, PortDescriptor};
124pub use drivers::ModemStatus;
125pub use error::{ReadOutcome, Result, TransferError, UsbSerialError};
126pub use port::SerialPortHandle;
127pub use probe::{DriverType, ProbeTable};
128pub use transport::{
129 BulkIn, BulkOut, ControlRequest, EndpointInfo, InterfaceInfo, SharedTransport, Transport,
130};
131
132#[cfg(feature = "fake-transport")]
133#[cfg_attr(docsrs, doc(cfg(feature = "fake-transport")))]
134pub use fake::FakeTransport;
135
136#[cfg(any(target_os = "android", target_os = "linux"))]
137#[cfg_attr(docsrs, doc(cfg(any(target_os = "android", target_os = "linux"))))]
138pub use nusb_transport::{from_raw_fd, NusbTransport};