fix(devices): USB truncation mode 2 — close() kills the in-flight usblp URB
Build desktop / desktop (push) Successful in 4m16s
CI / check (push) Successful in 43s
Build & push images / images (push) Successful in 2m51s

The chunked-write fix (81bc2e3) still truncated on hardware: the lab
test slip stopped mid-sentence with no feed and no cut (text hidden
until the feed button). Verified against drivers/usb/class/usblp.c:

- write() returns at URB SUBMISSION, not completion;
- only ONE write URB is in flight (the next write EAGAINs until it
  completes);
- usblp_release() — our close() — KILLS in-flight URBs.

The printer drains bulk data at PRINT speed (tiny internal buffer on
these clones), so closing right after the last accepted write cancels
the still-transferring tail — exactly where the feed + GS V cut bytes
live. Kernel-accepted ≠ printer-received.

Fix: the one-URB rule makes acceptance of write N a completion
certificate for write N−1. writeAllUsb now writes the payload's FINAL
BYTE alone — its acceptance proves everything before it is physically
in the printer — then drains 300 ms for that single packet before the
caller closes. New test pins the final-byte-alone chunking; wiki
printer-usb-transport.md carries the kernel-level account.

Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
2026-07-07 10:50:47 +02:00
parent 6f3f6ca596
commit 011fe5a4c4
4 changed files with 80 additions and 20 deletions
+11
View File
@@ -2485,3 +2485,14 @@ Lab bench (USB printer test, no relays on hand) hit a SECOND printer/relay coupl
blocks every non-access category while zero controllers exist. Printers are now exempt there too
— the binding fix removed the requirement inside the form; this removes the gate in front of it.
A controller-less box can configure + test a printer.
## [2026-07-07] update | USB truncation, mode 2: close() kills the in-flight usblp URB
Lab hardware test of the chunked-write fix STILL truncated (slip stopped mid-sentence, no cut,
text hidden until the feed button). Root cause verified against kernel usblp.c: write() returns at
URB submission; one URB in flight; usblp_release (close) kills it; the printer drains at print
speed — so the accepted-but-untransferred tail (incl. feed+cut, always the last bytes) died at
close. writeAllUsb now holds back the FINAL byte as its own write — usblp's one-URB rule makes its
acceptance a completion certificate for everything before it — then drains 300ms for that single
packet before close. Tests updated (+ final-byte-alone assertion). [[printer-usb-transport]] has
the full kernel-level account.