Conversation
|
Thanks for the fix @Algafix ! |
|
Can confirm this makes a normal timeout report as an unexpected error. Would be great to get in since this bug made it into 2025.10, but also 2025.10 contains gpif and fx3 dma changes which are nice to have. |
|
Yep, just found it today |
bradley-DS
added a commit
to DistributedSpectrum/bladeRF
that referenced
this pull request
Aug 24, 2026
Nuand#1060 thread.h: posix_cond_timedwait повертав -1 замість ETIMEDOUT. sync.c:393 порівнює з THREAD_TIMEOUT (=ETIMEDOUT), тож умова ніколи не спрацьовувала і таймаут очікування буфера ставав ERR_UNEXPECTED. Споживачі в bladeRF-fsk уже чекають ETIMEDOUT — патч їх узгоджує. Nuand#1024 sync_worker.c: 1000 мс на ініціалізацію воркера замало; при спрацюванні звільнявся s->worker, а потік ще виконувався. Nuand#1052 libusb.c: LIBUSB_ERROR_TIMEOUT * libbladeRF: do not log an error per transfer when the device is gone cancel_all_transfers() logs LIBUSB_ERROR_NO_DEVICE as an error for every in-flight transfer. That condition is expected when the device has been unplugged or reset by the host controller: the transfer cannot be cancelled because the device is no longer there. With num_transfers=32 this produces a burst of 32 identical error lines per reset, which buries the one line that actually matters. libusb still delivers the completion callback for these transfers, so the status is marked TRANSFER_CANCEL_PENDING like any other cancellation; only the logging changes. Measured on bladeRF 2.0 micro xA4 (FX3 2.6.0, FPGA 0.16.0) with the device reset via sysfs unbind/bind during continuous RX: error lines per reset go from tens to zero, and the stream still tears down and recovers. * libbladeRF: reject stream configs that exceed the usbfs memory budget Every in-flight USB transfer is pinned by the kernel, so a stream needs num_transfers * buffer_size_bytes of usbfs memory simultaneously. The default limit is 16 MiB. Exceeding it fails far from its cause: bladerf_sync_config() returns success, submit_transfer() then fails asynchronously with LIBUSB_ERROR_NO_MEM, and the caller only sees BLADERF_ERR_MEM from the first bladerf_sync_rx() - with nothing pointing at the transfer count. Measured on bladeRF 2.0 micro xA4 (FX3 2.6.0, FPGA 0.16.0), SC16_Q11_META at 61.44 Msps with buffer_size=131072 samples: num_transfers=31 -> ok num_transfers=32 -> ERR_MEM from the first sync_rx 32 * 512 KiB is exactly the 16 MiB limit. The larger GPIF buffers in FX3 2.6.0 make this easier to hit than before. Now checked up front: bladerf_sync_config() returns BLADERF_ERR_INVAL and logs the numbers plus the maximum transfer count that fits. * libbladeRF: unlock the device mutex on the 8-bit format error path bladerf_sync_config() and bladerf_init_stream() take dev->lock, then return BLADERF_ERR_UNSUPPORTED without releasing it when an SC8_Q7 format is requested on a bladeRF1. The device is left locked, so every subsequent API call on that handle blocks forever. The caller sees a hang rather than the error that was actually returned. Found while auditing 2.6.0 changes; both call sites are in bladerf.c. * libbladeRF: fix buffer overflows and a missing NULL check in gain calibration Three issues in the gain calibration paths added in 2.6.0: device_calibration.c: strcat() appends a caller-supplied CSV path of arbitrary length to a 1000-byte buffer that already holds the working directory. Long paths overflow the stack buffer. Print both parts with a format specifier instead of concatenating in place. bladerf.c: strcpy() copies a caller-supplied calibration path into a PATH_MAX buffer without a length check. Reject paths that do not fit. bladerf.c: malloc() result used by strcpy() without a NULL check. All three are reachable through bladerf_load_gain_calibration() with a caller-controlled path. * libbladeRF: document the usbfs memory limit on num_transfers The number of in-flight transfers is bounded by the kernel's usbfs memory limit, not just by num_buffers. With the larger GPIF buffers in FX3 2.6.0 the default 16 MiB is easy to reach: 32 transfers of a 131072-sample SC16_Q11 buffer is exactly 16 MiB. Documents the limit and the error now returned for configurations that exceed it. * libbladeRF: report RX overruns and leading discontinuities to sync_rx callers Two ways an RX gap could reach the caller unreported. First, the worker's overrun recovery. sync_worker.c carried a TODO for this: on overrun it resubmits buffers and logs, but nothing propagates the condition. Because recovery restarts the timestamp sequence, the gap is not visible in the message headers either, so bladerf_sync_rx() returns samples that are not contiguous with the previous call and reports success. Add buf_mgmt.overrun_pending, set by the worker and consumed by the next sync_rx(), which now raises BLADERF_META_STATUS_OVERRUN. Second, a discontinuity landing on the first message of a read. The check in sync_rx() required copied_data, so only gaps found mid-read were flagged; a gap at the start was silently skipped. The status flag is now raised regardless, while the early return stays conditional: with data already copied it must be handed back before the gap, with none copied the read continues past it. The first header of a stream has nothing to compare against, so meta.have_timestamp distinguishes it from a real discontinuity. It is cleared on init and whenever the stream restarts. Measured on a bladeRF 2.0 micro xA4 (FX3 2.6.0, FPGA 0.16.0), SC16_Q11_META at 61.44 Msps, host stalled 400 ms between reads to force overruns: before: driver logged 141 overruns, meta.status stayed 0x0 after: 2 of 6 reads report BLADERF_META_STATUS_OVERRUN no stall: 0 of 15 reads report it, driver logs none * no-OS: три виправлення ad9361 (гілка nuand-fixes) ad9361: do not leave the part in ALERT when update_rf_bandwidth fails ad9361: load the gain table with multibyte writes ad9361: express the gain-table row delay as udelay(), not dummy writes Перше усуває нескінченний потік Calibration TIMEOUT (0x247, 0x2) після одного збою: функція форсувала ALERT і виходила в обхід відновлення стану. Два інші здешевлюють перетин межі таблиці підсилення з 161.3 до 63.4 мс (p50, 15 перетинів), вміст SRAM при цьому побайтно тотожний. PR в апстрим: analogdevicesinc/no-OS#3273, #3274. * no-OS: помилка SPI більше не звітується як таймаут калібрування ad9361_check_cal_done порівнював результат ad9361_spi_readf із done_state, а той при збої повертає від'ємний errno. Від'ємне ніколи не дорівнює done_state, тож обрив транспорту крутив увесь цикл по мертвій шині й звітувався як Calibration TIMEOUT (0x247, 0x2). Тепер помилка читання повертається одразу, а справжній таймаут несе лічильники: polls, spi_err, first, last. PR analogdevicesinc/no-OS#3277. * bladerf2: wrap quick tune profile indices instead of running out The Nios quick tune profile counter only ever incremented, and was reset in exactly one place: board initialisation. An application that keeps requesting quick tunes therefore had a hard budget of NUM_BBP_FASTLOCK_PROFILES (256) for the lifetime of the device handle. Past that, every further bladerf_get_quick_tune() failed with BLADERF_ERR_UNEXPECTED and there was no way to recover short of closing and reopening the device. That budget is not a hardware limit on how many retune targets may exist over time. Both the RFIC and the Nios hold caches that this code already overwrites: the RFFE index is assigned modulo NUM_RFFE_FASTLOCK_PROFILES, so profile 8 has always overwritten profile 0. Letting the Nios index wrap the same way makes the two consistent and keeps long-running frequency sweeps working. Measured on a bladeRF 2.0 micro xA4 sweeping 70 MHz - 6 GHz with 4700 distinct stops, taking a quick tune profile on first visit to each: before: 320 stops -> counter stuck at 256, every later stop logged "Reached maximum number of RX quick tune profiles" after: 600 stops -> 600 profiles, no errors 3392 stops in 90 s -> 3392 profiles, no errors The counter is uint16_t and 65536 is a multiple of 256, so wrapping it does not disturb the modulo alignment. * no-OS: bump submodule to the RF PLL lock-wait bound Picks up 90c4d663f, which gives the RF PLL lock wait its own budget instead of sharing the calibration one. Measured ceiling on a failing retune drops from 1955 ms to 39.1 ms. * no-OS: bump submodule to the signed temperature read * bladerf2: leave AD9361 fastlock after an immediate quick tune The Nios recalls a quick tune profile by writing the RFIC directly. That leaves the part in fastlock mode with FORCE_ALC_ENABLE asserted, and the AD9361 driver cannot undo it on its own: its exit path is gated on phy->fastlock.current_profile, which a recall performed by the FPGA never touches. Ownership of the RFPLL returns to host tuning with forced controls still active, and the next bladerf_set_frequency() programs the synthesiser as if ALC were automatic. Measured on a bladeRF 2.0 micro xA4, interleaving bladerf_get_quick_tune() plus an immediate bladerf_schedule_retune() with ordinary tuning every fifth stop of a 242-point sweep across 82-5988 MHz, 65536 samples per stop: before 55 lock failures in 706 tunes, first at tune 280 after 2 lock failures in 758 tunes, first at tune 495 The shape matters more than the rate. Before, the failures form a series that never recovers: 44 consecutive failures with no successful tune inside them, and 0x247 reading 0x40 the whole time, meaning the RX charge pump has saturated low. After, there are no consecutive failures at all. Both figures reproduced across three runs each, identical to the tune. Leaked controls at the end of a run, before and after: 0x236 0xD3 (FORCE_ALC set) -> 0x50-0x5B (clear) 0x25A 0x01-0xA1 (mode set) -> 0x00 The leak was present on every recall rather than occasionally: 145 of 145, 146 of 146, 146 of 146 across three runs. Only immediate retunes are handled here. One scheduled for a future timestamp completes inside the FPGA long after the call returns, so the exit would have to happen there instead; that case is left alone rather than guessed at. Also picks up the corresponding no-OS change, which adds ad9361_fastlock_exit_foreign(). * bladerf2: leave foreign fastlock before ordinary host tuning A quick tune scheduled for a future timestamp is recalled by the FPGA's Nios core, which writes REG_(RX|TX)_FAST_LOCK_SETUP directly and leaves the part in fastlock mode behind the driver's back. The existing exit in bladerf2_schedule_retune() only covers BLADERF_RETUNE_NOW, because a deferred recall completes inside the FPGA long after that call returns. Tuning ordinarily while that leaked state is in place pins the RF PLL charge pump: 0x247 reads 0x80 (CP overrange high, no lock) during the failed tune and 0x40 after it. Measured on a bladeRF 2.0 micro xA4 running a sweep that mixes deferred quick tune recalls with ordinary tunes to off-grid frequencies, five 360 s runs: without this exit 190 / 327 / 587 / 330 / 667 lock failures with it 0 / 0 A sweep that uses only deferred recalls (never tuning ordinarily) shows zero failures either way, which is what isolated the mix as the trigger. ad9361_fastlock_exit_foreign() reads the setup register first and writes nothing when the part is not in fastlock mode, so the ordinary tuning path stays a single SPI read when no foreign recall happened. * no-OS: bump submodule to the documented 0x80 reading * libbladeRF: fix unit mix-up in ts_remaining() for X2 layouts curr_msg_off counts samples: it is advanced by samples_to_copy and compared against samples_per_msg. ts_remaining() subtracted it from samples_per_msg / samples_per_ts, which counts TIME STEPS per message. The units only coincide for single-channel layouts (samples_per_ts == 1). In X2 mode the unsigned subtraction underflows as soon as the offset passes half a message. With assertions enabled a bladerf_sync_rx() call that seeks to a timestamp inside the current message aborts the process: sync.c:451: ts_remaining: Assertion 'ret <= UINT_MAX' failed. With NDEBUG the huge result sends the seek logic into the 'fast forward within the current message' branch when the target lies beyond it, and curr_msg_off runs past the message boundary - the stream stalls instead of crashing. This matches the dual-channel stalls reported in Nuand#944. Reproduced on a bladeRF 2.0 micro xA4 (FX3 2.6.0, FPGA 0.16.0) with an RX_X2 SC16_Q11_META stream reading back-to-back explicit timestamps: the abort fires within the first few reads. With the units fixed the same sequence completes 3000/3000 contiguous reads with no timestamp drift, and single-channel metadata reads are unaffected. Subtract the sample offset first, then convert to time steps. The new assertion documents the invariant the old expression silently assumed. * libbladeRF: drop the stale RX backlog after an overrun When the consumer stalls and the ring fills, the worker stops storing: everything the hardware produces after that moment is discarded, so the buffers that sit full in the ring are the OLDEST data, not the newest. On resume the consumer first reads up to (num_buffers - num_transfers) buffers of history. With a metadata format the timestamps expose this; with SC16_Q11 and the other plain formats nothing does - the stale prefix is indistinguishable from live samples. Measured on a bladeRF 2.0 micro xA4 (TX1-to-RX1 loopback, FFT verdict per read): 8-16 stale reads after every stall across a stall-length matrix - the exact num_buffers - num_transfers of the configuration. This shows up as 'the receiver ignores what just changed on the air' and once masqueraded as a TX defect that cost half a day to debunk. The worker now flags the backlog when it detects the overrun, and the consumer drops the contiguous run of full buffers before claiming the next one, resuming at the live edge. In WAIT_FOR_BUFFER cons_i is never PARTIAL, so the walk is safe, and the producer resumes storing into the slots freed here. BLADERF_META_STATUS_OVERRUN reporting is unchanged. After the fix the same matrix shows 59-60/60 live reads in every combination; metadata timestamps and overrun reporting stay intact. * libbladeRF: exempt metadata formats from the stale-backlog drop Metadata consumers already see the gap in the timestamps, and they may legitimately want the backlog: a scheduled capture seeks THROUGH the buffers that are full at overrun time to reach its target timestamp. Dropping them under that seek discards the very samples the caller asked for - a sweep that overruns on every stop went from over a thousand detections to zero. The drop stays for the plain formats, where nothing marks the stale prefix and the caller has no way to tell history from live data. Verified after the change: the non-metadata matrix still shows 59-60/60 live reads, metadata timestamps advance, and META_STATUS_OVERRUN reporting is unaffected. * Keep the RFIC port when a direction is disabled The shutdown entry in the band port map exists for the RF SPDT switches: they have a genuine "no connection" state, RFFE_CONTROL_SPDT_SHUTDOWN is 0x0, and putting them there on disable is what the entry was added for. The rfic_port field in the same struct has no such state. Every value in that enum is a real input, and the 0 the shutdown entry carries is AD936X_A_BALANCED, the high-band input. So disabling an RX channel tuned to 915 MHz moved the RFIC input from B_BALANCED to A_BALANCED, and re-enabling moved it back. That round trip is not free. Measured on a TX1 -> 50 dB pad -> RX1 loopback at 915 MHz, manual gain control, an unmodulated tone that was never touched between reads: 20 disable/enable cycles, REG_INPUT_SELECT toggling 0x43 <-> 0x4C, reported gain constant at 40.0 dB: spread 0.48 dB, sigma 0.161 Isolating it further, with the TX stream never interrupted: before after sync_config only 0.20 / 0.045 0.09 / 0.028 enable(false->true) only 0.68 / 0.211 0.32 / 0.079 both 0.63 / 0.182 0.35 / 0.080 both + 300 ms settle 0.42 / 0.176 0.21 / 0.062 REG_INPUT_SELECT now reads 0x4C before and after each cycle. Leaving the port alone costs nothing while disabled: the direction is off and the SPDT is already disconnected. The port is still selected by frequency on every enable, so nothing else changes. Some residual spread remains, so this is not the whole story -- but it is half of it, and the half that had a clear mechanism. * fifo_reader: don't let a stale meta header read as META_NOW meta_p_time was derived from meta_current.meta_fifo_data, the copy of the meta FIFO output registered on the previous cycle. That copy is all zeros coming out of reset, and the reader subtracts 1 from the header timestamp, so 0 - 1 wraps to all ones. All ones is exactly the META_NOW sentinel, so META_WAIT's "meta_p_time = MAX_TIMESTAMP" test passes and the feed gate opens immediately for a burst that was scheduled in the future. The burst is emitted at once and nothing remains to send when its timestamp arrives. Take the timestamp from meta_fifo_data, the header the FIFO is actually presenting when META_LOAD issues the read. Adds fifo_reader_tb, which had no testbench before. Two cases: scheduled (TGT=4000) before: 2045 reads early, 0 on time after: 0 early, 2001 on time TX_NOW (TGT=0) before: 2045 reads after: 2045 reads Verified with GHDL against this source, including a negative control: reverting the one line makes the scheduled case fail the assertion with "gate leaked", so the test does detect the defect. Found while chasing a transmitter that goes silent after a TX module enable/disable cycle, where the RFIC holds a latched sample of the transmitted waveform while the timestamp counter keeps advancing. Whether this fully explains that symptom is not yet established - it needs an FPGA build and a hardware re-measurement. * bladerf2: read the layout of the direction being enabled board_data->sync is indexed by direction, so a plain sync-> is sync[0], which is always the RX stream. Enabling or disabling a TX channel therefore read the RX layout here and answered the MIMO question from the wrong stream. Found while chasing an intermittent TX wedge. It is not the cause of that wedge - measured on a bladeRF 2.0 micro xA4, this change alone leaves the wedge rate unchanged at 5 of 10 runs clean, same as master - but the read is wrong on its own terms and worth fixing. * libusb: do not cut a TX transfer in the middle of a buffer Cancelling an in-flight TX transfer ends it wherever it happens to be, and the device cannot use a partial buffer. On the wire the cancelled OUT transfers come back having moved 1024, 3072, 5120, 6144, 8192, 13312 and 27648 bytes - all multiples of the 1024-byte SuperSpeed packet, none a multiple of the 32768-byte buffer. The FX3 TX channel is CY_U3P_DMA_TYPE_AUTO over 8192-byte buffers, so it assembles whole buffers; the cut leaves it holding an unfinished one and later submissions have nowhere to complete. What follows is eight submissions sitting for a full stream timeout while the RX endpoint keeps completing normally, which is the "TX feed stops after repeating sync_config(TX) while RX is read" report. So an in-flight TX transfer is now given until its own timeout to finish by itself, and is cancelled only after that - by which point it was not going to complete anyway. Transfers that never started are unaffected: they complete immediately either way. Measured, same session, 10 s of continuous load with the TX stream rebuilt in a tight loop and RX read throughout (tools/repro/tx_wedge_soak.py in the sdr-scanner tree): before feed died at round 2, 5, 3 3 of 3 runs after survived 681, 676, 679 rounds 3 of 3 runs Feed rate is unchanged - roughly 19k buffers submitted across 680 stream teardown/setup cycles in 10 s. * libusb: wait for events the way libusb requires when two streams share a context RX and TX each run their own lusb_stream() loop calling libusb_handle_events_timeout() on the same libusb context. libusb permits that, but only one thread actually handles events while the others wait inside it, and a waiter that entered the wait before the state it is waiting on changed keeps waiting until its timeout expires. Checking stream->state outside the event lock, which a plain handle_events_timeout() call forces you to do, is exactly the lost-wakeup pattern that libusb_handle_events_timeout_completed() exists to close. The loop now passes a completion flag that libusb re-checks under its own event lock, and every transition to STREAM_DONE goes through mark_stream_done() so the state and the flag always move together. Measured: no change to the TX feed stall this was chased for (137 transfer timeouts before, 135 after, same conditions), so this is protocol correctness rather than a fix for that. The stall is still open. * sync: keep submitting until the TX ring is out of the parked state The previous commit found the buffer the callback still owed a submission for, but submitted exactly one. That is not enough to leave the ring in a live state: every buffer still FULL behind the first needs its own callback to be shipped, and the callbacks that would have run were consumed by libusb's cancellation path. So one reclaim moved one buffer and left submission parked again, with the rest of the data waiting. sync_tx now drains while the ring stays parked, stopping as soon as a reclaim moves nothing - which is what WOULD_BLOCK looks like from here. The check also runs before the wait rather than only after it times out: waiting in a state no callback can resolve just burns the stream timeout, which on the wire showed up as a 1002 ms silence with 941 RX completions inside it. tx_submission_parked() names the forbidden state: duty with the callback and a buffer recorded as owed. Buffer status deliberately is not used to count live transfers - IN_FLIGHT is only cleared in the sync callback, so a cancelled transfer leaves its buffer marked IN_FLIGHT forever, which is what made the ring look busy when nothing was. test_tx_submit_liveness.c replays the exact ordering from the trace (WOULD_BLOCK, duty to callback, cancelled completions bypassing the sync callback, stale cons_i, data behind it) and asserts the forbidden state is not reachable. Checked against the single-submission version: it fails on that case and passes the rest, so the test separates the two. Device check: feed rate unchanged, 1553 buffers in 3.8 s, same as before. * sync_deinit: retire the handle before destroying its mutexes sync_rx() and sync_tx() test sync->initialized without holding a lock - they cannot, because the lock they would take is the one being destroyed - and then go on to take buf_mgmt.lock. Clearing the flag last left a window where a concurrent reader passed the test and then locked a mutex MUTEX_DESTROY had already reclaimed. Clearing it first closes that window. This is a use-after-destroy on a mutex, reachable whenever one direction is reconfigured while the other is being read, which is exactly what bladerf_sync_config() does via sync_deinit(). Does NOT fix the TX wedge: measured 24 runs, mean repeat 13, identical to baseline 13. Committed on its own terms. * sync: do not reset the TX consumer index to 0 on worker restart SYNC_STATE_RESET_BUF_MGMT sets buf_mgmt.cons_i = 0 for both directions. That is right for RX, where the consumer index is where the reader picks up. For TX the same field means the opposite: it names the buffer tx_callback should ship next, and sync_init() sets BUFFER_MGMT_INVALID_INDEX to say 'nothing is deferred yet'. So a restarted TX worker comes back believing buffer 0 is a deferred, full buffer. tx_callback then submits a buffer the producer never filled, and the ring accounting drifts from there. Found while investigating the TX reconfiguration wedge. It is NOT that wedge: this path only runs when the worker is already IDLE, and instrumentation shows RESET_BUF_MGMT is never reached during a plain sync_config() reconfigure (0 occurrences). It will bite on the stream-error recovery path, where the caller is documented to be able to call again to restart the stream. * streaming: let a TX worker see STOP when nothing is completing A TX worker only inspects w->requests from inside tx_callback, and that callback only runs when a transfer completes. With the feed stopped nothing completes, so a STOP request is never observed: the worker sits in RUNNING until sync_worker_deinit() times out after 3 s and resorts to THREAD_CANCEL. Two halves: - sync_worker_submit_request() nudges a RUNNING TX stream into STREAM_SHUTTING_DOWN, so the backend event loop, which spins while the state is not STREAM_DONE, can leave on its own. - that loop now finishes the shutdown itself: cancel what is outstanding, and once every transfer is accounted for, move to STREAM_DONE. Previously the SHUTTING_DOWN to DONE transition lived only in lusb_stream_cb(), so a shutdown with nothing in flight had no way to complete - which is why setting SHUTTING_DOWN unconditionally used to hang the worker. Verified on a bladeRF 2.0 micro xA4: the worker stops cleanly with no THREAD_CANCEL, and RX is unaffected (40/40 frames across five enable cycles). Does NOT fix the TX reconfiguration wedge. Measured in one sitting, 8 reconfigurations per run: 7 of 10 runs clean with this change against 6 of 10 without, which is inside the spread. An early 4-of-4 result was luck. * sync: submission can be parked with the duty held by sync_tx too tx_submission_parked() only recognised the state where the duty sits with the worker callback. There is a second one, and it is what a wide TX ring runs into: the duty comes back to sync_tx, the callback path shipped what it could, and everything that piled up while the duty was parked stays FULL - because sync_tx only ever submits the buffer prod_i points at, and with nothing in flight no callback arrives to move the rest. Measured on a bladeRF 2.0 micro, TX stream of 64 buffers x 16384 samples with 16 transfers, RX read concurrently: submitter FN, 0 in flight, 26 of 64 buffers FULL, and sync_tx() timing out at 1000 ms. The feed had a full ring of data to send and sent none of it. oldest_full_buffer() now starts from prod_i when cons_i is INVALID, which is how the duty-is-ours case presents, so the same drain loop covers both. test_tx_submit_liveness.c gets the case, and its stuck() no longer cares which side holds the duty - what makes it stuck is data waiting with nothing in flight. Checked against the previous predicate: it fails on exactly this case and passes the rest. Also: a failed RX worker start was logged at debug level, so in practice it was invisible - the caller then waits out its timeout and reports a timeout, which reads like a device that went quiet rather than a worker that never started. Now a warning, matching the TX side. * libusb: say how much arrived when a transfer times out A timed-out transfer is reported as just "Transfer timed out for RX buffer 0x...". That leaves the two most useful facts out: how much of the buffer actually arrived, and whether this stream had ever received anything at all. Those two numbers separate faults that are otherwise identical from the caller's side, since both surface as BLADERF_ERR_TIMEOUT: - a device that never started producing samples (zero completed transfers, zero bytes) - a device that produced for a while and then stopped (nonzero completed transfers) Without them, working out which one you are looking at means adding printfs to the library and rebuilding it. I did exactly that while chasing an RX stall, and the counter was the fact that ended the search. Track completed full-buffer transfers per stream and include the byte counts in the timeout message. No behaviour change: same error code, same control flow, one extra counter and a longer log line on a path that is already an error path. * libbladeRF: strip development leftovers from the merged TX work PR Nuand#1086 came in with two standalone test programs dropped into src/streaming/. libbladeRF's CMakeLists lists its sources explicitly, so those files never built and never will; remove them. The same series left two log_verbose() calls in sync_rx() that were plainly debug instrumentation, three comment blocks stacked above a single forward declaration -- one belonging to each of oldest_full_buffer(), tx_submission_parked() and reclaim_tx_submission() -- and three overlapping comments on the TX wait path. Reattach each comment to the function it describes, collapse the TX wait ones into a single explanation, and drop a non-ASCII character that had crept into a C comment. No functional change. * bladeRF-cli: name the load_gain helptext after the command generate.bash derives the macro name from the second field of the Usage: line, so interactive-help.md's "Usage: load_gain <rx|tx> <filename>" yields CLI_CMD_HELPTEXT_load_gain. cmd.c and the checked-in cmd_help.h.in both spelled it CLI_CMD_HELPTEXT_gain_calibration_load, after the C identifier rather than the command, so any build with pandoc present regenerated a header without that symbol and failed to compile cmd.c. Rename the macro in both places. The C identifier and the source file keep their names; only the helptext macro moves. * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: wormuz <wormuz@gmail.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When using the bladeRF-cli I saw that the tx/rx wait Xs command was not returning.
After further inspection, I believe it is because
posix_cond_timedwait()is returning -1 instead ofETIMEDOUT.In the bladeRF-cli code, the comparison is done with
THREAD_TIMEOUT, which is mapped toETIMEDOUT.At a quick look, I have not seen any part of the codebase where the comparison is with -1 instead of
THREAD_TIMEOUTor similar. I might be wrong, then the change should be in the bladeRF-cli code to compare with -1.#1025