Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/IRadio.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,10 @@ class IRadio {
virtual void StartRxLoop(Action_ParsedRadioPacket packetProcessor) = 0;

/* Ask a running StartRxLoop to exit (sets a flag; the caller then joins
* whatever thread runs StartRxLoop). Default no-op. */
* whatever thread runs StartRxLoop). Default no-op.
* Note: Covers the Init + StartRxLoop race window; the InitWrite +
* StartRxLoop-on-a-thread shape (DEVOURER_TX_WITH_RX=thread) operates outside
* this window and is out of scope. */
virtual void StopRxLoop() {}
virtual void SetMonitorChannel(SelectedChannel channel) = 0;

Expand Down
20 changes: 17 additions & 3 deletions src/jaguar1/RtlJaguarDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1667,6 +1667,8 @@ size_t RtlJaguarDevice::build_tx_block(const uint8_t *packet, size_t length,

void RtlJaguarDevice::Init(Action_ParsedRadioPacket packetProcessor,
SelectedChannel channel) {
should_stop = false;

/* A window armed before a (re-)bring-up describes a chip state that no
* longer exists; leaving it armed would make the next unrelated
* GetChannelBusy() take the armed branch and report a stale period. */
Expand Down Expand Up @@ -1798,14 +1800,26 @@ void RtlJaguarDevice::Init(Action_ParsedRadioPacket packetProcessor,
std::lock_guard<std::recursive_mutex> lock(_port0_mu);
_station_ready = true; /* every port-0 write of this bring-up is done */
}
StartRxLoop(std::move(packetProcessor));
start_rx_loop(std::move(packetProcessor), false);
rollback.release();
}

void RtlJaguarDevice::StartRxLoop(Action_ParsedRadioPacket packetProcessor) {
start_rx_loop(std::move(packetProcessor), true);
}

void RtlJaguarDevice::start_rx_loop(Action_ParsedRadioPacket packetProcessor, bool clear_stale) {
if (g_devourer_should_stop)
return;

_packetProcessor = std::move(packetProcessor);
/* Restartable: clear any stop request left by a prior StopRxLoop(). */
should_stop = false;

if (clear_stale) {
should_stop = false;
} else if (should_stop) {
/* Stop requested during Init must prevent its receive loop from starting */
return;
}

/* DEVOURER_RX_PATHS=0xNN restricts which RX chains the chip enables/combines,
* by masking the RX-path-enable register (0x808 byte 0: bits 0/4 = path A
Expand Down
2 changes: 2 additions & 0 deletions src/jaguar1/RtlJaguarDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,8 @@ class RtlJaguarDevice : public IRtlRadio {
return true;
}

void start_rx_loop(Action_ParsedRadioPacket packetProcessor, bool clear_stale);

/* Programs 0x520[14]/[15] and, for the EDCCA gate only, the BB thresholds
* at 0x8a4. SetCcaMode is apply_cca(d, d) and writes exactly what it
* wrote before the split existed. */
Expand Down
21 changes: 18 additions & 3 deletions src/jaguar2/RtlJaguar2Device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,8 @@ void RtlJaguar2Device::ClearAmpduMode() { SetAmpduMode(devourer::AmpduMode{}); }

void RtlJaguar2Device::Init(Action_ParsedRadioPacket packetProcessor,
SelectedChannel channel) {
_rx_stop = false;

/* A window armed before a (re-)bring-up describes a chip state that no
* longer exists; leaving it armed would make the next unrelated
* GetChannelBusy() take the armed branch and report a stale period. */
Expand Down Expand Up @@ -590,13 +592,26 @@ void RtlJaguar2Device::Init(Action_ParsedRadioPacket packetProcessor,
std::lock_guard<std::mutex> lk(_reg_mu);
_station_ready = true; /* every port-0 write of this bring-up is done */
}
StartRxLoop(std::move(packetProcessor));
start_rx_loop(std::move(packetProcessor), false);
}

void RtlJaguar2Device::StartRxLoop(Action_ParsedRadioPacket packetProcessor) {
start_rx_loop(std::move(packetProcessor), true);
}

void RtlJaguar2Device::start_rx_loop(Action_ParsedRadioPacket packetProcessor, bool clear_stale) {
if (g_devourer_should_stop)
return;

_packetProcessor = std::move(packetProcessor);
/* Restartable: clear any stop request left by a prior StopRxLoop(). */
_rx_stop = false;

if (clear_stale) {
_rx_stop = false;
} else if (_rx_stop) {
/* Stop requested during Init must prevent its receive loop from starting */
return;
}

/* Start the DIG thread: track IGI to the false-alarm rate so weak signals are
* caught without an FA storm (a fixed IGI can't span the range). */
_dig_stop = false;
Expand Down
2 changes: 2 additions & 0 deletions src/jaguar2/RtlJaguar2Device.h
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,8 @@ class RtlJaguar2Device : public IRtlRadio {
* signal-flag pattern used across the library (g_devourer_should_stop). */
volatile bool _rx_stop = false;

void start_rx_loop(Action_ParsedRadioPacket packetProcessor, bool clear_stale);

/* Shared cold bring-up (power-on -> DLFW -> MAC/BB/RF -> channel -> LCK ->
* IQK -> coex -> enable RX/TX engine). Used by both Init (RX) and InitWrite
* (TX). Leaves the chip fully calibrated on `channel`. */
Expand Down
21 changes: 18 additions & 3 deletions src/jaguar3/RtlJaguar3Device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ struct WriteBatchScope {

void RtlJaguar3Device::Init(Action_ParsedRadioPacket packetProcessor,
SelectedChannel channel) {
_rx_stop = false;

/* A new session has sent no wide frame: a re-bring-up must not inherit the
* previous session's wide-TX IGI floor (_wide_tx_seen). */
_wide_tx_seen.store(false, std::memory_order_relaxed);
Expand Down Expand Up @@ -205,13 +207,26 @@ void RtlJaguar3Device::Init(Action_ParsedRadioPacket packetProcessor,
std::lock_guard<std::mutex> lk(_reg_mu);
_station_ready = true; /* every port-0 write of this bring-up is done */
}
StartRxLoop(std::move(packetProcessor));
start_rx_loop(std::move(packetProcessor), false);
}

void RtlJaguar3Device::StartRxLoop(Action_ParsedRadioPacket packetProcessor) {
start_rx_loop(std::move(packetProcessor), true);
}

void RtlJaguar3Device::start_rx_loop(Action_ParsedRadioPacket packetProcessor, bool clear_stale) {
if (g_devourer_should_stop)
return;

_packetProcessor = std::move(packetProcessor);
/* Restartable: clear any stop request left by a prior StopRxLoop(). */
_rx_stop = false;

if (clear_stale) {
_rx_stop = false;
} else if (_rx_stop) {
/* Stop requested during Init must prevent its receive loop from starting */
return;
}

/* Take over bulk-IN from the coex thread's C2H drain up front — also during
* the register restore below, so its 200 ms bulk reads don't interleave with
* the RX-path enable sequence. */
Expand Down
3 changes: 3 additions & 0 deletions src/jaguar3/RtlJaguar3Device.h
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,9 @@ class RtlJaguar3Device : public IRtlRadio {
int64_t _tbtt_off_us = 0;
/* StartRxLoop stop request (StopRxLoop). */
volatile bool _rx_stop = false;

void start_rx_loop(Action_ParsedRadioPacket packetProcessor, bool clear_stale);

/* True while StartRxLoop owns bulk-IN (gates the coex thread's drain). */
std::atomic<bool> _rx_loop_active{false};
/* Set when InitWrite zeroes the RX filters (0x6A0-0x6A4) for TX-only
Expand Down
19 changes: 17 additions & 2 deletions src/kestrel/RtlKestrelDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ void RtlKestrelDevice::EnableTxScheduler() {

void RtlKestrelDevice::Init(Action_ParsedRadioPacket packetProcessor,
SelectedChannel channel) {
_rx_stop = false;
if (!BringUpMonitor(channel)) {
_logger->error("Kestrel: monitor bring-up failed");
return;
Expand All @@ -318,7 +319,7 @@ void RtlKestrelDevice::Init(Action_ParsedRadioPacket packetProcessor,
ClearAckResponder();
});
}
StartRxLoop(std::move(packetProcessor));
start_rx_loop(std::move(packetProcessor), false);
}

void RtlKestrelDevice::InitWrite(SelectedChannel channel) {
Expand Down Expand Up @@ -437,10 +438,24 @@ void RtlKestrelDevice::InitWriteTail(SelectedChannel channel) {
}

void RtlKestrelDevice::StartRxLoop(Action_ParsedRadioPacket packetProcessor) {
start_rx_loop(std::move(packetProcessor), true);
}

void RtlKestrelDevice::start_rx_loop(Action_ParsedRadioPacket packetProcessor, bool clear_stale) {
if (g_devourer_should_stop)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The g_devourer_should_stop early return is new behaviour on every backend and unrelated to the fix: the loop's own stop predicate already checks the same flag on its first pass. Harmless, but drop it (or justify it in the commit) so the change stays about the Init-time stop. Here specifically, stop_wp_drain() has already run by this point, so an early return leaves no bulk-IN reader at all — same as before, but worth not adding a second path to it.

return;

if (clear_stale) {
_rx_stop = false;
} else if (_rx_stop) {
/* Stop requested during Init must prevent its receive loop from starting */
return;
}

/* Take the bulk-IN over from the TX WP-release drain (this loop also consumes
* rpkt_type=7 releases) so the two never read the endpoint concurrently. */
stop_wp_drain();
_rx_stop = false;

_logger->info("Kestrel: starting RX loop on ch{}", _channel.Channel);
/* Async bulk-IN ring; walk each aggregate with the 11ax rxd parser. The
* buffer must hold a full RXAGG aggregate (the 8852C LEN_TH is ~20 KB), or
Expand Down
3 changes: 3 additions & 0 deletions src/kestrel/RtlKestrelDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,9 @@ class RtlKestrelDevice : public IRtlRadio {
SelectedChannel _channel{};
kestrel::EfuseInfo _efuse{};
volatile bool _rx_stop = false;

void start_rx_loop(Action_ParsedRadioPacket packetProcessor, bool clear_stale);

/* WP-release drain (Kestrel STF USB): the fw returns each transmitted frame's
* WD/PLE pages as an RX packet (rpkt_type=7, TX_PD_RELEASE_HOST) on the bulk-
* IN. A TX-only path must still drain the bulk-IN or the pages never recycle
Expand Down
17 changes: 15 additions & 2 deletions src/mt7612u/Mt7612uRadio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ void Mt7612uRadio::tick_loop() {

void Mt7612uRadio::Init(Action_ParsedRadioPacket packetProcessor,
SelectedChannel channel) {
_rx_stop = false;
/* Stop() on the way out, as the sibling backends do: a throw from the channel
* set or the config would otherwise leave the device open and the tick thread
* running until the destructor happens to run, and a caller that catches and
Expand Down Expand Up @@ -351,7 +352,7 @@ void Mt7612uRadio::Init(Action_ParsedRadioPacket packetProcessor,
* failed to start" left the device open and the tick running, which is the
* half-open object this whole guard exists to prevent. */
try {
StartRxLoop(std::move(packetProcessor));
start_rx_loop(std::move(packetProcessor), false);
} catch (...) {
Stop();
throw;
Expand Down Expand Up @@ -392,6 +393,13 @@ void Mt7612uRadio::InitWrite(SelectedChannel channel) {
}

void Mt7612uRadio::StartRxLoop(Action_ParsedRadioPacket packetProcessor) {
start_rx_loop(std::move(packetProcessor), true);
}

void Mt7612uRadio::start_rx_loop(Action_ParsedRadioPacket packetProcessor, bool clear_stale) {
if (g_devourer_should_stop)
return;

struct mt7612u_dev *mac_failed = nullptr;
/* An armed station keeps its managed filter: the monitor request below is
* recorded, not installed (mt7612u_set_monitor_rx). The log says which
Expand All @@ -416,7 +424,12 @@ void Mt7612uRadio::StartRxLoop(Action_ParsedRadioPacket packetProcessor) {
if (_rx_active.load())
throw std::runtime_error("MT7612U RX loop is already active");
_rx_processor = std::move(packetProcessor);
_rx_stop = false;
if (clear_stale) {
_rx_stop = false;
} else if (_rx_stop.load()) {
/* Stop requested during Init must prevent its receive loop from starting */
return;
}
/* 64 slots is about 45 ms of headroom at the measured 1400 fps - enough to
* ride out a slow processor without letting the producer block. The ring
* itself is allocated here; each slot's payload buffer then grows to the
Expand Down
1 change: 1 addition & 0 deletions src/mt7612u/Mt7612uRadio.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ class Mt7612uRadio : public IRadio {
void bring_up(SelectedChannel channel); /* _mu held */
void apply_config(); /* _mu held */
int txpower_target_dbm() const; /* _mu held */
void start_rx_loop(Action_ParsedRadioPacket packetProcessor, bool clear_stale);
void start_tick(); /* _mu held */
void stop_tick(); /* _mu NOT held */
void tick_loop();
Expand Down
18 changes: 16 additions & 2 deletions src/rtl8733b/Rtl8733bDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ void Rtl8733bDevice::bring_up_to_phy() {

void Rtl8733bDevice::Init(Action_ParsedRadioPacket packetProcessor,
SelectedChannel channel) {
_rx_stop = false;
try {
{
std::lock_guard<std::recursive_mutex> lock(_reg_mu);
Expand Down Expand Up @@ -225,7 +226,7 @@ void Rtl8733bDevice::Init(Action_ParsedRadioPacket packetProcessor,
ClearAckResponder();
});
}
StartRxLoop(std::move(packetProcessor));
start_rx_loop(std::move(packetProcessor), false);
} catch (...) {
Stop();
throw;
Expand Down Expand Up @@ -355,6 +356,14 @@ bool Rtl8733bDevice::configure_tx_power(SelectedChannel channel) {

void Rtl8733bDevice::StartRxLoop(
Action_ParsedRadioPacket packetProcessor) {
start_rx_loop(std::move(packetProcessor), true);
}

void Rtl8733bDevice::start_rx_loop(
Action_ParsedRadioPacket packetProcessor, bool clear_stale) {
if (g_devourer_should_stop)
return;

Action_ParsedRadioPacket rx_packet_processor = std::move(packetProcessor);
uint8_t rx_channel = 0;
{
Expand All @@ -363,7 +372,12 @@ void Rtl8733bDevice::StartRxLoop(
throw std::runtime_error("RTL8733B RX loop requires initialized hardware");
if (_rx_active)
throw std::runtime_error("RTL8733B RX loop is already active");
_rx_stop = false;
if (clear_stale) {
_rx_stop = false;
} else if (_rx_stop) {
/* Stop requested during Init must prevent its receive loop from starting */
return;
}
_rx_active = true;
rx_channel = _channel.Channel;
}
Expand Down
1 change: 1 addition & 0 deletions src/rtl8733b/Rtl8733bDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ class Rtl8733bDevice : public IRtlRadio {
return true;
}

void start_rx_loop(Action_ParsedRadioPacket packetProcessor, bool clear_stale);
void bring_up_to_phy();
bool configure_tx_power(SelectedChannel channel);
/* Fill one [txdesc][frame] block at `out`. `agg_num` is the USB TX
Expand Down
Loading