mathoudebine / mathoudebine/turing-smart-screen-python
Rev C (5", chs_5inch): cdc_acm serial path jams after one frame — proposal for an opt-in libusb transport (Linux)
Nobody has claimed this yet.
- Dominant language
- Rich Text Format
- Stars
- 2.3k
- Forks
- 412
- Avg merge
- 21h 10m
- Merged PRs (30d)
- 7
Description
Summary
On Linux, a Turing 5" revision C display (chs_5inch.dev1_rom1.88) cannot be
driven through the stock pyserial/cdc_acm path: the bulk OUT endpoint accepts
exactly one ~250-byte frame and then jams until a USB reset. The identical
endpoints work perfectly when driven with raw libusb bulk transfers. I'd like to
propose an opt-in, Linux-gated libusb transport for rev C (pyserial stays the
default everywhere), and I'm opening this first to gauge interest and preferred
shape.
This is tested on a single unit, and the proposal deliberately changes no
defaults — see the caveats and open questions at the end.
Environment
- Display: Turing 5" rev C, ID
chs_5inch.dev1_rom1.88- awake:
1d6b:0106"Android", serial20080411→/dev/ttyACM1 - asleep:
1a86:ca21"UsbMonitor", serialCT21INCH→/dev/ttyACM0
- awake:
- OS: Linux (kernel 6.x/liquorix), Python 3.13
- Project: current
main(3.10.0-109-g4df61d9), i.e. with the new
SERIAL_OPEN_ATTEMPTSopen-retry loop inlcd_comm.py - Kernel driver:
cdc_acmbinds the awake interface and creates/dev/ttyACM1
Symptom
With REVISION: C, the app opens the port fine, then _hello() reads an empty
display ID and loops forever ("Display returned invalid or unsupported ID, try
again in 1 second"). Nothing ever draws.
What I measured
I drove the panel through the stock rev C driver (pristine origin/main, no
local changes) and instrumented every serial_write to record the exact stall
point. Key results:
- Open works. Stock
openSerial(with the retry loop) opens/dev/ttyACM1
on the first attempt. So the retry loop does its job — the failure is not at
the open stage. - Handshake fails. The HELLO exchange returns an empty ID; stock
_hello
retries indefinitely. - Byte-level, flow control ON (stock
rtscts=True): the very first 250-byte
write drains 0 bytes (write timeout). The stock port is opened with
hardware RTS/CTS flow control, and this CDC-ACM gadget appears not to assert
CTS, so nothing transmits. - Byte-level, flow control OFF (
rtscts=False): the first 250-byte frame
drains, but the next write stalls (Write timeout). i.e. the bulk OUT
endpoint accepts exactly one ~250-byte frame, then jams. Recovery requires
a USB reset / physical replug. - Same endpoints over raw libusb: HELLO returns
chs_5inch.dev1_rom1.88
immediately and full-screen frames stream without limit.
So the jam is a cdc_acm data-path limit, reproduced against current
upstream, and independent of the retry loop and of flow control (I tested
both). Happy to share the standalone repro harness.
Corroboration
The OEM Windows software for these panels also bypasses the serial/COM path and
speaks raw USB bulk (WinUsb_WritePipe / LibUsbDotNet) — consistent with the
serial path being unusable for sustained transfer on this hardware.
Proposal
An opt-in, Linux-only libusb transport for rev C:
- A config option (default off) selects it, e.g.
REV_C_TRANSPORT: SERIAL | LIBUSB, read with a safe default so pyserial stays the default for everyone
and existing rev C users are unaffected. - Implemented inside the rev C class (no new
REVISION; routes through the
existing factory). - Uses
pyusb(already a dependency forTUR_USB). On Linux it detaches
cdc_acm(detach_kernel_driver) and drives bulk OUT/IN directly. - Gated to Linux with graceful fallback:
detach_kernel_driveris a
Linux/usbfs capability (not supported on libusb's Windows backend), so this
only engages on Linux; Windows/macOS keep pyserial untouched.
Caveats & open questions
- Single unit tested (5", ROM 88). Rev C USB IDs are unreliable across sizes,
so I can't promise other rev C variants behave identically — which is exactly
why this is opt-in and default-off. - Do Windows/macOS rev C users see any equivalent stall over the COM port?
The jam is a Linuxcdc_acmbehavior; Windows usesusbser.sys. If the serial
path already works there, the libusb transport only ever needs to engage on
Linux (as proposed). - Preferred opt-in mechanism? I noticed
config.yamlchanges are blocked by
CI (do-not-commit-config-yaml), so I'd read the option with a default and let
you add the documented key to the shipped template — unless you'd prefer a
different mechanism.
Would you welcome a PR along these lines? Happy to adjust the shape (config key
name, gating, docs) to whatever fits the project.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing the existing rev C class and factory, then read lcd_comm.py and the current SERIAL_OPEN_ATTEMPTS behavior. Review how TUR_USB and config.yaml are handled before evaluating the proposed Linux-only transport. Done means an opt-in path can use libusb on Linux while pyserial remains the default elsewhere, with graceful fallback and no changed defaults.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- linux, python
- Domain
- embedded-iot, operating-systems
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100