3.0.0
Major v3 release: Channel-based watch() API, native AT exchange / FIFO queue, unified RX hub on desktop and Android, and a hard cut from vendored Java usb-serial-for-android to the pure-Rust android-usb-serial crate (nusb).
Breaking Changes
- Streaming API:
startListening()/listen()/disconnected()/cancelListen()removed. Usewatch({ onData, onDisconnect?, onError?, onUrc? })→SerialEventover TauriChannel(desktop + Android). - Capabilities:
SerialPort.getCapabilities()(transport,platform,version); commandscapabilities,watch,unwatchreplacestart_listening/stop_listening. - Removed:
available_ports_direct— useavailable_ports(). - Removed
AtCommandQueue/port.at: UsesendAt(),sendAtPhases(),sendSmsPdu(),cancelAt(),configureAtSession(). onUrcremoved fromAtSessionOptions: Usewatch({ onUrc })for live URC lines.exchange()returnsExchangeResponse, notUint8Array. Use.rawfor bytes.clearRx: truemaps topurge; default is nowdrainwhen unset.- Android USB stack: Vendored
android/usbserialJava tree removed. Kotlin no longer runs serial I/O commands on the main thread — onlyUsbFdBridge(enumerate, permission, fd). All drivers and bulk I/O live in Rust (android-usb-serial+ nusb). - Rust module layout: Removed compat top-level modules (
desktop_api,mobile_api,port_rx_hub,exchange_runtime, …). Useapi::serial,hub,exchange,port,state,android. - guest-js:
AtCommandResult.raw/ExchangeResponse.rawareUint8Array;timedOutis a nativeboolean. rust-version: 1.79 minimum.
Migration (v2 → v3)
| v2 | v3 |
|---|---|
startListening() + listen(fn) |
watch({ onData: fn }) |
disconnected(fn) |
watch({ onDisconnect: fn }) |
cancelListen() / stopListening() |
handle.unwatch() |
port.at.enable() / enqueue() |
sendAt() / sendAtPhases() |
Kotlin UsbBridge + Java drivers |
UsbFdBridge fd → Rust driver_host |
Android app requirements: uses-feature android.hardware.usb.host, device_filter.xml, runtime USB permission before openDevice(). Do not claimInterface() in Kotlin before handing fd to Rust. See crates/android-usb-serial/README.md.
Features
android-usb-serialcrate: Pure Rust USB serial on Android via nusb; 567 golden parity fixtures; drivers for FTDI, CP21xx, CH34x, PL2303, CDC-ACM, GSM modem, Chrome CCD (20b54c0).UsbFdBridge: Kotlin provides USB fd only; Rustdriver_hostowns probe, claim, drivers, RX reader, and I/O.- Unified
api::serial: Singleserialport::SerialPortfacade on desktop and Android;PortRxHubpoll-loop on both platforms (4071ff6). - Rust-first Android JNI (29faa29): bulk-IN reader thread, chunked write, CMUX virtual paths through Rust session.
- Unified RX hub (desktop + Android): Single consumer per port;
watch,exchange,read, and drain share one hub (7024fc0, caa5700). take_idle_bytes: Stale RX in the hub idle buffer is replayed into the nextexchangeafter write.- Native FIFO queue (Rust + Android): All
exchange/ AT jobs on one port serialize in FIFO order; parallel invokes wait instead of"Exchange already in progress"(e1eb63f). - Native
exchange/cancel_exchange: Write + read-until terminators, idle silence, wall timeout, max response size; structuredExchangeResponse(status,lines,solicitedBody,urcLines,raw). - Line-framed AT completion: Final line
OK/ERROR/+CME ERROR/+CMS ERROR;completionMode: 'substring'for legacy/binary. rxPrepare: Defaultdrain;purgeopt-in;noneunchanged.watchAvailablePorts()/watch_ports: Hotplug via Channel —snapshot, thenadded/removed(desktop poll; Android USB attach/detach).open()canonical path: Returns session key (Android device path /device#Nfor multi-port).- CMUX virtual
exchange: Pathsphysical#dlci=Nrouted through Rust CMUX session like desktop. usb-driver-tester: Standalone hardware self-test app underexamples/usb-driver-tester/.- guest-js: Modular v3 SDK; auto-reconnect restores
open()+watch()after disconnect. - macOS:
available_ports({ singlePortPerDevice: true })— one path per device (prefers/dev/cu.*). - Extended AT grammar: Vendor prefixes, V.250 finals,
derive_solicited_prefixes(command);ExchangeDemuxfor live URC before echo. pauseWatchdefaultfalse— watch stays on during AT; passpauseWatch: truefor legacy behavior.
Bug Fixes
- Android CH340 / weak OTG: Bulk IN reader starts after line/DTR setup; in-flight URBs reduced to 2; clearer detach reason in logs.
- Android enumerate: Kotlin exports
interfaces[]; Rust expands multi-port paths (device#N) viaProbeTablewithout opening fd. - Android write after listen:
EndpointPair::writeno longer re-opens bulk IN owned bySerialReader(endpoint already in use). - Android Kotlin fd bridge: Removed pre-
claimInterface(fixesio interface is busywith nusbdetach_and_claim). - Android logcat: Rust plugin logs use tag
SerialPluginvia__android_log_write. - Android: USB permission
PendingIntentusesFLAG_MUTABLEon API 31+; detach + IO errors →SerialEvent::Disconnecton Channel (#27). - Android:
cancel_exchangewakes hub waiter; CMUX virtual cancel clears DLCI TX queue (c6c94dd). - Android: JNI exceptions, session path re-key, teardown leaks (e9fc307).
- Android: Serialize fail/shutdown on dedicated usb-io thread (cd74b84).
- desktop:
write/write_binaryflush viawrite_all(#29). - desktop: Unblock hub drain when watch is active (55f1cb3).
- desktop: Lock order, Opening-state open, async
enable_mux(3a4bd88). - desktop (Windows): Enrich truncated USB
serial_numberfrom WMI (#23). - Hub / watch:
lock_or_recoverin watch_registry and hub channel paths (2071e15); panic/poison hardening (8672de9). - TX queue: Errors no longer halt the port queue until reopen (e1eb63f).
- guest-js:
readBinaryrequires open port; watch-preservingchange()(9b22af7).