Skip to content

Linux Chromium Web Serial after pyserial

A Station G2 browser timeout on firmware source 1e421ff4fc6a3b56d43db7a18aab94a72129b527 was traced to the Linux host's inherited TTY read settings. Chromium 139.0.7258.127 closed its receive stream with NetworkError / device lost before the first ver request. The radio was still running and an exclusive pyserial query answered in 21.330 ms without a reset or replug. This finding explains this controlled host failure; it does not establish that every reported USB hang has the same cause.

Why an idle port looked disconnected

pyserial normally sets VMIN=0, VTIME=0 and performs its timeout handling with select. These kernel TTY attributes survive closing the port. With those settings, an idle nonblocking Linux read() can return zero bytes. pyserial's POSIX implementation sets these values and closes without restoring the previous attributes.

In the exact Chromium 139 serial implementation, port configuration preserves the prior VMIN and VTIME. Its receive handler interprets a zero-byte read as device loss. Setting VMIN=1, VTIME=0 makes an idle nonblocking read return EAGAIN, so the browser waits for data instead.

Upstream Chromium fixed this in commit 45ccdf6e on 2026-09-18, under bug 559872992. The change explicitly configures both read settings and adds a real Linux pseudoterminal regression for inherited settings. The presence of this fix upstream does not prove a particular installed browser contains it; the Pi's tested Chromium 139 predates the fix.

Controlled G2 comparison

The same firmware, native Pi Chromium, and unmodified official flasher console were used for both attempts. Console source SHA-256 was 107d3ad867915aea8b2454c324327397a0f9e2f0469656046054011bc64475f9. Instrumentation observed and forwarded existing native API calls.

Attempt Host settings and observation Result
203 VMIN=0, VTIME=0. The receive stream was already lost before Enter. The native five-byte ver\r\n write nevertheless completed in 28.1 ms. FAIL. No displayed reply by the unchanged 5,000 ms deadline.
204 Only host VMIN changed to 1; VTIME stayed 0. The receive stream remained open. PASS. First ordinary ver reply was displayed in 154.1 ms.

No firmware or website patch, modem-signal change, reset, replug, added command retry, keepalive, or longer deadline was used to obtain the passing comparison. Before this comparison, stats-core reported uptime 1,728 seconds and error bitmask 2; this is evidence of continued operation, not an error-free radio qualification. The comparison does not qualify a fresh flash, a long idle window, another host OS/browser, or every USB transport.

Safe receipt names and hashes are retained for the comparison:

Receipt SHA-256
stock-browser-diagnostic-203-safe.json e41b0f009b8dce42ad9ee23ad843032b97a47fc2cfc5385aefbf093ac46a9788
controlled-browser-host-setting-safe.json 88527690ca8d8740fd415831123c45ac3249134aec3929f7fd2b0224ddbe8fce
stock-browser-diagnostic-204-safe.json 6ba3249a23364a589653a0a1a4abb7e453a1cb2f095cec6138f3cebc171d5f39

Hardware-test preflight

The tracked Linux Web Serial helper changes and verifies only VMIN=1, VTIME=0 immediately before a native browser test. It requires an explicit absolute TTY path and restores the complete original termios attributes after the browser closes, including on a Python exception or interrupt. Failed preparation also restores the original state. It sends no serial data and makes no DTR/RTS or USB-reset calls.

Pause other serial clients and finish all pyserial preflight queries before entering the context. Another opener can overwrite the read settings, and a second reader can consume the radio's reply. The helper closes its preparation descriptor before the trial, so it does not keep the port open across the stock browser's Disconnect action. Controllers must close their native reader, writer and port in their own finally cleanup before the context exits, and route termination into that cleanup. If the endpoint is replaced or disappears, restoration fails explicitly rather than applying old attributes to a different device.

Record browser version, TTY settings, native stream state/error, native write completion, raw receive-byte count and rendered reply separately. A zero-byte console display alone does not prove that the USB endpoint received no bytes. For example, the stock console buffers input until a complete CRLF line arrives. See the HIL instructions for the context-manager example and the hardware-free pseudoterminal test.