From 2d77cc590427719e53df0d344130f825965f39ee Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Feb 2026 07:49:43 +0000 Subject: [PATCH 1/3] Initial plan From 4a31083bbfc20f5ff3daa5b2f11b5a7625756893 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Feb 2026 08:04:10 +0000 Subject: [PATCH 2/3] Fix rx_when_idle not being properly configured in radio - Add otPlatRadioSetRxOnWhenIdle callback in platform.rs for OpenThread C library builds that call it (e.g. when OT_RADIO_CAPS_RX_ON_WHEN_IDLE is supported) - Query link mode in plat_changed callback and sync rx_when_idle to radio_conf, ensuring the radio hardware is told to stay in receive mode when the Thread link mode requires it - Add plat_radio_set_rx_on_when_idle method in OtContext Previously, radio_conf.rx_when_idle was initialized to false and never updated, causing the ESP radio hardware to not stay in receive mode during idle periods. This led to missed 802.15.4 fragments and reassembly-timeout errors. Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com> --- openthread/src/lib.rs | 30 ++++++++++++++++++++++++++++-- openthread/src/platform.rs | 5 +++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/openthread/src/lib.rs b/openthread/src/lib.rs index c556cb9..4a049e0 100644 --- a/openthread/src/lib.rs +++ b/openthread/src/lib.rs @@ -77,7 +77,7 @@ use sys::{ otOperationalDataset, otOperationalDatasetTlvs, otPlatAlarmMilliFired, otPlatRadioReceiveDone, otPlatRadioTxDone, otPlatRadioTxStarted, otRadioCaps, otRadioFrame, otSetStateChangedCallback, otTaskletsArePending, otTaskletsProcess, otThreadGetDeviceRole, otThreadGetExtendedPanId, - otThreadSetEnabled, OT_RADIO_CAPS_ACK_TIMEOUT, OT_RADIO_FRAME_MAX_SIZE, + otThreadGetLinkMode, otThreadSetEnabled, OT_RADIO_CAPS_ACK_TIMEOUT, OT_RADIO_FRAME_MAX_SIZE, }; /// A newtype wrapper over the native OpenThread error type (`otError`). @@ -1394,7 +1394,20 @@ impl<'a> OtContext<'a> { fn plat_changed(&mut self, _flags: u32) { trace!("Plat changed callback"); - self.state().ot.changes.signal(()); + + let state = self.state(); + + let rx_on_when_idle = unsafe { otThreadGetLinkMode(state.ot.instance) }.mRxOnWhenIdle(); + + if state.ot.radio_conf.rx_when_idle != rx_on_when_idle { + info!( + "Updating rx_when_idle: {} -> {}", + state.ot.radio_conf.rx_when_idle, rx_on_when_idle + ); + state.ot.radio_conf.rx_when_idle = rx_on_when_idle; + } + + state.ot.changes.signal(()); } fn plat_now(&mut self) -> u32 { @@ -1506,6 +1519,19 @@ impl<'a> OtContext<'a> { } } + fn plat_radio_set_rx_on_when_idle(&mut self, rx_on_when_idle: bool) { + info!( + "Plat radio set rx_on_when_idle callback, rx_on_when_idle: {}", + rx_on_when_idle + ); + + let state = self.state(); + + if state.ot.radio_conf.rx_when_idle != rx_on_when_idle { + state.ot.radio_conf.rx_when_idle = rx_on_when_idle; + } + } + fn plat_radio_set_extended_address(&mut self, address: u64) { info!( "Plat radio set extended address callback, addr: 0x{:08x}", diff --git a/openthread/src/platform.rs b/openthread/src/platform.rs index 7a38fb8..be10344 100644 --- a/openthread/src/platform.rs +++ b/openthread/src/platform.rs @@ -105,6 +105,11 @@ extern "C" fn otPlatRadioSetPromiscuous(instance: *const otInstance, enable: boo OtContext::callback(instance).plat_radio_set_promiscuous(enable) } +#[no_mangle] +extern "C" fn otPlatRadioSetRxOnWhenIdle(instance: *mut otInstance, enable: bool) { + OtContext::callback(instance).plat_radio_set_rx_on_when_idle(enable) +} + #[no_mangle] extern "C" fn otPlatRadioGetRssi(instance: *const otInstance) -> i8 { OtContext::callback(instance).plat_radio_get_rssi() From 9ea61a8d22d3134aa891ed92de0d8d0caa3018ad Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Feb 2026 08:05:14 +0000 Subject: [PATCH 3/3] Refactor: extract update_rx_when_idle helper to avoid code duplication Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com> --- openthread/src/lib.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/openthread/src/lib.rs b/openthread/src/lib.rs index 4a049e0..0e7e33d 100644 --- a/openthread/src/lib.rs +++ b/openthread/src/lib.rs @@ -1398,14 +1398,7 @@ impl<'a> OtContext<'a> { let state = self.state(); let rx_on_when_idle = unsafe { otThreadGetLinkMode(state.ot.instance) }.mRxOnWhenIdle(); - - if state.ot.radio_conf.rx_when_idle != rx_on_when_idle { - info!( - "Updating rx_when_idle: {} -> {}", - state.ot.radio_conf.rx_when_idle, rx_on_when_idle - ); - state.ot.radio_conf.rx_when_idle = rx_on_when_idle; - } + Self::update_rx_when_idle(state, rx_on_when_idle); state.ot.changes.signal(()); } @@ -1525,9 +1518,15 @@ impl<'a> OtContext<'a> { rx_on_when_idle ); - let state = self.state(); + Self::update_rx_when_idle(self.state(), rx_on_when_idle); + } + fn update_rx_when_idle(state: &mut OtActiveState<'_>, rx_on_when_idle: bool) { if state.ot.radio_conf.rx_when_idle != rx_on_when_idle { + info!( + "Updating rx_when_idle: {} -> {}", + state.ot.radio_conf.rx_when_idle, rx_on_when_idle + ); state.ot.radio_conf.rx_when_idle = rx_on_when_idle; } }