Commit Graph
8 Commits
Author SHA1 Message Date
iceman1001andClaude Opus 5 9e4ee9c6ad lf read: fix realtime sample streaming over USB
A realtime `lf read` streams samples straight to the host at the LF sample rate,
around 125 kB/s. If the host stopped draining for even a moment,
async_usb_write_requestWrite() returned false, and ReadLF_realtime() treated that
as fatal: it returned PM3_EIO through a goto that also skipped
async_usb_write_stop(). The IN endpoint was left busy, every later usb_write()
then returned PM3_EIO, and the device went silent until it was physically
replugged. A USB bus reset does not clear it -- EP0 keeps working, descriptors
read fine, only bulk IN is dead. That is what a long `lf read` looked like from
the client: a transfer that stopped early, and then a device that would not
answer the next command.

A busy endpoint is back pressure, not an error. The sampling loop now waits for
the host, up to 100ms, and only gives up if it never comes back. Every exit path
closes the async write. The spins inside the USB helpers are bounded and drop the
stuck packet instead of turning into an infinite loop, which is the other half of
why the device never recovered. Measured against a reader deliberately held at
41 kB/s: before, the stream died at 212238 of 300000 bytes and the unit needed a
replug; after, 300000 of 300000 and it still answers.

The last packet of a stream was being lost as well. The host's CDC read buffer is
two max sized packets, and a bulk IN transfer only completes on a short packet or
a full buffer, so a stream ending on a full 64 byte packet was left sitting in a
half filled buffer. It showed as exact parity on the packet count:

  513 packets requested -> 512 delivered      514 -> 514
  515 packets requested -> 514 delivered      516 -> 516

async_usb_write_stop() now always sends a closing packet, the leftover partial
bytes when there are any and a zero length packet otherwise, the same way
usb_write() already did for its own transfers.

On the client side a short transfer is reported instead of being presented as a
complete read, the device is told to stop streaming on that path too, and the in
place byte counter is repeated with its final value, since the loop only samples
it every 10ms and the last line printed was stale.

Lastly `lf read` and `lf sniff` cap the sample count to the graph buffer size.
Anything past it was streamed and then discarded by getSamplesFromBufEx(), so
`-s 1500000` spent about two extra seconds collecting 220000 samples that were
thrown away, and printed "Received 1500000 / 1500000 bytes" followed by "Got
1280000 samples" with nothing to explain the gap.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 22:49:41 +02:00
iceman1001andClaude Opus 5 89305b2f8b usb: bound the wait for the host to collect a STALL
AT91F_USB_SendStall spun on STALLSENT with no exit. That bit only
arrives once the host polls the endpoint and takes the handshake, so a
host that vanishes in between wedges the device. It was the last
unbounded wait in the file without an escape.

Exit on RXSETUP (host abandoned the request and sent a new SETUP), on
ENDBUSRES, or on a spin count, reusing the 0x1FFF that usb_read already
uses. usb_check() is not usable here: it calls back into
AT91F_CDC_Enumerate, which is what calls this.

Do not test RXSUSP. Nothing writes it back to UDP_ICR, so it latches on
the first bus idle and stays set, which makes the guard fire on every
stall and stops the STALL from ever being delivered.

The second wait is bounded too, since leaving the first one early can
let the host set STALLSENT after the clear.

Also ISOERROR -> STALLSENT. Same bit 3, but only one of those names is
true on a control endpoint.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 18:12:34 +02:00
iceman1001andClaude Opus 5 fcb61153f8 usb: pack the CDC line coding struct to its 7 wire bytes
uint32_t plus three uint8_t pads to 8, so sizeof(line) was 8 where CDC
defines 7. Hosts asking for more than 7 got a byte of uninitialised
static padding. The commented out SET_LINE_CODING loop already hardcodes
i < 7, so the wire format was understood; the struct just did not match.

No observable change for cdc_acm or usbser.sys, which both ask for
exactly 7.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 18:00:09 +02:00
iceman1001andClaude Opus 5 5cf1b18c1e usb: terminate short control IN transfers with a ZLP
AT91F_USB_SendData only took length, and every caller passed
MIN(sizeof(x), wLength), so the function could not tell it had
truncated. A control IN transfer ends on wLength bytes or on a short
packet; returning fewer than wLength in whole 8 byte packets is neither,
and the host polls until it times out.

Take wLength too, do the MIN inside, and send a ZLP when the reply is
shorter than asked and lands on a packet boundary. The length > 0 guard
avoids a second termination, since the send loop already emits an empty
packet for an empty payload.

This is what the serial number descriptor's '(size % 8) == 0 OS bug
workaround' padding was dodging. Not an OS bug.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 17:54:18 +02:00
iceman1001andClaude Opus 5 485180e055 usb: answer the WCID vendor request instead of stalling it
StrMS_OSDescriptor advertises MSFT100 and vendor code 0x1C, but the
handler for that request was commented out, so it fell through to the
standard switch and stalled. Windows caches that under
usbflags\<VID><PID><bcdDevice> and stops asking, and bcdDevice never
changes, so no reflash could recover it.

Enable both feature descriptors and answer 0x1C in AT91F_CDC_Enumerate.
The compatible ID is left empty so usbser.sys keeps binding; WINUSB
there would take interface 0 away from CDC and kill the COM port.
DeviceInterfaceGUID was GUID_DEVCLASS_PORTS, a setup class GUID in an
interface GUID field - replaced with a fixed project GUID.

AT32 is unchanged; both descriptors are behind #ifndef PM5.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 17:41:43 +02:00
Young, Nathen C 186c72eefd fix(usb): terminate packet-aligned CDC transfers with ZLP
Send a synchronous zero-length packet after CDC responses whose length is
an exact multiple of the endpoint packet size, preventing oversized host
reads from remaining pending.
2026-09-09 08:57:09 -07:00
iceman1001 35c098d489 make style 2026-08-29 17:42:29 +02:00
dxl 36f1c7e44c Make common_arm to hardware abstraction layer. 2026-08-19 15:32:39 +02:00