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>
Three things that all cost memory or correctness once BigBuf is larger
than 64KB, which it already is on PM5.
BigBuf_max_traceLen() returned a uint16_t while s_bigbuf_hi is a
uint32_t. At 33272 on AT91 that is harmless; on AT32, where BigBuf runs
to several hundred kbyte, it truncates and LF sampling asks for a
fraction of the buffer that is there. Widened, along with the four
callers that assigned it straight back into a uint16_t.
BigBuf_malloc() and BigBuf_calloc() took a uint16_t chunksize, so a
request of exactly 65536 arrived as 0 and anything above wrapped. It
returned NULL for the 65536 case by accident, not by design. Both take a
uint32_t now and the guard tests the upper bound explicitly, so an
oversized request fails like any other allocation that does not fit.
The 14a tag simulation allocated its dynamic modulation buffer as a flat
512, or 4096 for ST25TA. prepare_tag_modulation() memcpy's the encoded
answer out of the ToSend buffer and tosend_stuffbit() hard caps that at
TOSEND_BUFFER_SIZE, so 1788 of ST25TA's 4096 could never be reached --
it was the largest single allocation of any tag simulation for nothing.
Meanwhile 512 is 68 bytes short of what a full 64 byte response encodes
to, at 9 bytes per byte plus 4, so those answers were refused at
modulation time.
Both are now derived from the response size and capped at what ToSend
can hold: 580 for the default tag types, 2308 for ST25TA. That also
fixes the reason the 4096 never helped -- all six call sites passed the
512 macro as max_buffer_size rather than the size actually allocated, so
ST25TA allocated 4096 and then bounds checked against 512.
Worst case for a simulation that also holds emulator memory drops from
12247 bytes of BigBuf to 10459.
Built for client, RDV4 and PM3GENERIC. Not yet run on hardware.
Co-Authored-By: Claude Opus 5 (1M context)
Bug triggered with:
lf read -s 50000
hw ping
ReadLF_realtime() in lfsampling.c never knew how many samples the client wanted — for the realtime path, lf_sample_payload_t.samples was never populated on the client side, and the ARM handler in appmain.c (CMD_LF_ACQ_RAW_ADC/CMD_LF_SNIFF_RAW_ADC) ignored it anyway. So the device just streamed samples indefinitely, only checking for a stop request roughly once per 64-sample USB burst, and even after noticing it, flushed one more trailing partial burst via async_usb_write_stop().
Meanwhile the client (WaitForRawDataTimeout in comms.c) stops listening in raw mode the instant it has collected exactly the number of bytes it asked for (e.g. 50000), then switches back to normal framed-packet parsing. Since the device kept sending more bytes past that point, those extra bytes sat unread in the pipe and got misinterpreted as a PacketResponseNGPreamble/OLD-frame header, corrupting every subsequent USB exchange (hw ping, etc.) — reproducible even with a plain lf read -s 50000, no trigger/COTAG needed.
Moreover, we make sure the client doesn't stop listening, even briefly, during stream reception.
in appmain.c and cmd added.
in cmd.c and add some code for test CEP
in em4x50.c(Do not timeout--)
in em4x70.c
in emvsim.c
in epa.c
in felica.c
in felicasim.c
in felicasim.c
in hfops.c
in hfsnoop.c
in hitag2.c
in hitag_common.c(Cross-platform implementation is incomplete.)
in hitagS.c
in hitagu.c
in i2c.c(Incomplete, continue to abstract.)
in i2c_direct.c
in iclass.c
in iso14443a.c(Sniff no finish yet)
in iso14443b.c and fixed bug for st25
in iso15693.c
in legicrf.c
in legicrfsim.c
in lfadc.c(lf_count_edge_periods_ex() improved)
in lfops.c(TI tag no finish yet)
in lfsampling.c
in lfzx.c
in mifarecmd.c
in mifaredesfire.c
in mifaresim.c
in mifaresniff_disabled.c
in mifareutil.c
in pcf7931.c
in sam_xxx
in secc & seos
in start.c
in thinfilm.c
in utils
- "lf cotag reader": now behaves mostly the same as "lf read",
except it is dedicated for COTAG.
- It makes sure to send COTAG start sequence and to use
real time sampling mode to get enough samples.
- "lf cotag demod": Working client-side COTAG demodulation implementation
(high-low raw demod + Manchester demodulation from high/low demod).
- Now uses samples directly from g_GraphBuffer
- Supports 3 types of COTAG preamble (1 active, 2 passive), which
can start anywhere in the samples.
- Prints full 128-bits of COTAG tag data in 3 formats: raw,
groupped by nibbles, and in hex, and identifies and
prints out the card number.
Signed-off-by: andrej@towelbyte.net
Check if memory allocation fails
Fix memory leak
Initialize struct in declaration
Add/Fix some notes
Remove unlikely() in favor of readability
Remove a hard-coded magic number