fix(devices): USB truncation mode 2 — close() kills the in-flight usblp URB
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:
@@ -104,6 +104,23 @@ deadline with a `(N/M bytes accepted)` diagnostic. Driven by fake-handle tests (
|
||||
EAGAIN interleave, wedged-printer timeout, non-EAGAIN passthrough) since a real file can't
|
||||
reproduce the char device's behaviour.
|
||||
|
||||
**Second truncation mode — close() cancels the in-flight transfer (lab bench, 2026-07-07).** The
|
||||
chunked loop alone STILL truncated on hardware (test slip stopped mid-sentence, no feed, no cut —
|
||||
"press the feed button to see the text"). Verified against `drivers/usb/class/usblp.c`: `write()`
|
||||
returns at URB *submission* (not completion), only ONE write URB is in flight at a time (the next
|
||||
write EAGAINs until it completes), and `usblp_release()` — i.e. 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**. So
|
||||
`writeAllUsb` now writes the payload's FINAL BYTE alone: when that 1-byte write is accepted, every
|
||||
byte before it is physically in the printer; a short drain pause (`USB_DRAIN_MS` 300 ms) covers
|
||||
the lone final-byte packet, then close is safe. (usblp also implements `poll(POLLOUT)` as the true
|
||||
completion signal, but Node cannot poll an arbitrary char-device fd without a native dep — the
|
||||
hold-back + drain gets the same guarantee for all but the final byte, whose packet the printer
|
||||
ACKs immediately after having just freed its buffer.)
|
||||
|
||||
> Driver-choice note for this clone: the ICS XP-K200L does NOT serve the Rongta `/prn_stat.htm`
|
||||
> status page (checked on hardware at 10.0.10.11 — print socket 9100 open, status page absent),
|
||||
> so on NETWORK the honest driver is **cashino** (reachability-only monitoring); under `rongta`
|
||||
|
||||
+11
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user