* No MAC code in esp-radio anymore
* get the trace back
* Changelog entries
* get `radio_mac_address` under `Efuse` impl
* fmt
* address reviews
* No need for that complicated flexibility with const generic yet
sure
* address reviews
rebase
lint
* address reviews
* implement `Debug` and `defmt` for `MacAddress`, tweak doc comment
* rebase
* Extract public fns, use raw version internally
* Cleanup
* Add RunLevel, remove Priority::None
* Make enable_on_cpu infallible, and more
* Remove reading CPU id
* Optimize interrupt handlers a bit
* remove noise
* Merge bind_interrupt and enable functions
* Clarify what enable and disable functions do
* Remove map
* Fix docs
* Hide current_runlevel
* Move free-standing functions into CpuInterrupt
* Remove unused Error, rename direct binding errors
* Explain watchpoint manipulation
* Add procmacro changelog
* Fixes
* Set up interrupts on second core
* ESP32: Listen for GPIO interrupts on second core
* Make init_vectoring available for multicore API
* GPIO fixups
* Derive defmt, add non-exhaustive to RunLevel
* Bring ieee802154 driver up to date with ESP-IDF C equivalent (#2)
* Initial plan
* Port ESP-IDF C driver improvements to Rust ieee802154 driver
- Add RxAck and TxEnhAck states matching C driver state machine
- Overhaul ISR handler with C driver event processing order:
RX_ABORT(phase1) -> SFD -> TX_DONE -> RX_DONE -> ACK_TX_DONE ->
ACK_RX_DONE -> RX_ABORT(phase2) -> TX_ABORT
- TX done: transition to RxAck state when ACK required instead of
immediately calling next_operation
- RX done: queue frame then handle ACK state transitions
- ACK TX done: clear security, deliver received frame, next_operation
- ACK RX done: signal tx_done on ACK receipt
- Two-phase RX abort: phase 1 for RX-state, phase 2 for TX-ACK-state
- TX abort: differentiate reasons (timeout, coex break, CCA busy, etc.)
- Per-state stop_current_operation dispatch (stop_rx, stop_tx, etc.)
- Fix tx_init to only set RX buffer when ACK is actually required
- Allow ieee802154_receive re-entry from TxAck state
- Add HAL: get_tx/rx_abort_reason, rx_auto_ack, disable abort events
- Add tx_failed callback for transmit failure notification
- Align mac_init abort events with C driver configuration
- Enable CCA TX support in ieee802154_transmit
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Address code review: fix duplicate frame queuing and doc comments
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Fix clippy warnings: remove unused HAL functions, needless returns, wire up per-state stop
- Remove unused `clear_tx_abort_status`, `clear_rx_abort_status`,
`disable_tx_abort_events`, `disable_rx_abort_events` from hal.rs
- Remove needless `return` statements in match arms in raw.rs
- Wire up `tx_init` and `rx_init` to use `stop_current_operation_inner`
with proper per-state dispatch (matching C driver) instead of simple
stop+clear which was hiding a bug
- Remove unused `stop_current_operation` wrapper (callers now pass
state directly)
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Revert unrelated xtask formatting changes
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Fix unused variable: remove unused `state` parameter from `stop_rx_ack`
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Add timer0-based 200ms ACK receive timeout matching C driver
- Add HAL functions: timer0_set_threshold(), timer0_start(), timer0_stop()
- Start timer0 with 200ms timeout when entering RxAck state in
isr_handle_tx_done, matching C driver's receive_ack_timeout_timer_start
- Handle Timer0Overflow event in ISR: signals tx_failed + next_operation
- Stop timer0 in stop_rx_ack, isr_handle_ack_rx_done, and
isr_handle_tx_abort(RxAckTimeout) to prevent stale timeouts
- Add event_end_process() calls matching C driver: stops timer0 at the
start of each ISR handler and in stop_current_operation_inner
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Extract ACK timeout magic number to named constant ACK_TIMEOUT_US
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Fix coex mode: align TX/RX abort events with C driver, add TX deferral, coex break notify
Three critical coex-mode fixes matching the C driver:
1. TX abort events aligned with C driver's mac_init: only enable
RxAckTimeout, TxCoexBreak, TxSecurityError, CcaFailed, CcaBusy.
Previously enabled 9 extra events (RxAckSfdTimeout, RxAckCrcError,
etc.) that the C driver deliberately disables — these cause
premature TX failures in BLE coex mode by immediately failing on
transient ACK reception errors instead of letting the timer0 200ms
timeout handle them.
2. TX deferral: ieee802154_transmit() now checks if currently
receiving a frame (is_current_rx_frame) or sending ACK (TxAck/
TxEnhAck state), and reports TX failure instead of aborting. This
matches the C driver's transmit() and prevents losing in-flight
frames during BLE coex arbitration.
3. Coex break notification: calls esp_coex_ieee802154_coex_break_notify()
when TX is aborted due to BLE coex break, matching C driver's
isr_handle_tx_abort. This allows the coex manager to adjust scheduling.
Also aligns RX abort events with C driver (TxAckTimeout + TxAckCoexBreak
only, removed extra RxStop and TxAckStop).
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Revert coex commit 6584a85: remove TX deferral that caused regression
Reverts all changes from commit 6584a85 which caused severe stability
regression — commissioning was no longer possible.
Root cause: the TX deferral check (`is_current_rx_frame()` + TxAck/
TxEnhAck state check) was too aggressive. With `rx_when_idle` enabled
(as in Thread/OpenThread), the driver is always in RX state, and
`is_current_rx_frame()` returns true frequently in any RF environment.
This caused most TX attempts to be deferred and reported as failures
via `tx_failed()` callback, which the upper layer (openthread crate)
doesn't handle as a retry signal.
Reverted changes:
- TX deferral in ieee802154_transmit() (the regression cause)
- TX abort events reduction (from 14 to 5) — may need re-evaluation
but needs to be done separately with proper testing
- is_current_rx_frame() HAL function (no longer needed)
- esp_coex_ieee802154_coex_break_notify() call (harmless but part of
the regressed commit)
The ISR overhaul, state machine, timer0 ACK timeout, and other
improvements from earlier commits are preserved.
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Re-introduce safe coex improvements from reverted 6584a85
Re-apply three improvements from the reverted coex commit that are
independent of the TX deferral regression:
1. TX abort events reduced from 14 to 5 matching C driver's mac_init:
only RxAckTimeout, TxCoexBreak, TxSecurityError, CcaFailed, CcaBusy.
The 9 extra events (RxAckSfdTimeout, RxAckCrcError, etc.) caused
duplicate tx_failed callbacks: first from the ACK error, then again
from the timer0 200ms timeout. The C driver deliberately disables
these so transient ACK errors don't immediately fail the TX.
2. RX abort events reduced to 2 matching C driver: TxAckTimeout and
TxAckCoexBreak only.
3. esp_coex_ieee802154_coex_break_notify() called on TX coex break,
allowing the coex manager to adjust BLE/802.15.4 scheduling.
TX deferral (the regression cause) is NOT re-introduced - that requires
the openthread crate to properly handle tx_failed as a retry signal.
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Fix nightly rustfmt: collapse single-line enable_rx_abort_events call
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Fix CCA TODO: add `cca` parameter to transmit() and transmit_raw()
The "// what about CCA?" comments were TODOs — both transmit() and
transmit_raw() hardcoded `cca: false`, meaning CCA was never performed
before transmitting. In IEEE 802.15.4, CCA is essential to avoid
collisions (the C driver's esp_ieee802154_transmit passes through the
cca parameter from its caller).
The fix adds a `cca: bool` parameter to both public API functions,
allowing callers (like the openthread crate) to request CCA before
transmission, matching the C driver's behavior.
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Re-introduce TX deferral matching C driver
Now safe to re-introduce because the openthread crate's for-review
branch properly handles tx_failed:
- TX_SIGNAL changed from Signal<()> to Signal<bool>
- tx_done_callback signals true, tx_failed_callback signals false
- EspRadio::transmit() returns Err(RadioErrorKind::TxFailed) on false
- OpenThread SubMac retries with CSMA backoff on any error
The TX deferral checks in ieee802154_transmit() match the C driver:
- If currently sending ACK (TxAck/TxEnhAck state), defer TX
- If currently receiving a frame past SFD (is_current_rx_frame), defer TX
- Deferred TX calls tx_failed() callback instead of aborting
Also adds is_current_rx_frame() HAL function that reads rx_state > 1
from the rx_status register, matching the C driver's
ieee802154_ll_is_current_rx_frame().
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Fix TX power: default to 20 dBm matching C driver, fix >= boundary
Two TX power bugs that explain instability at longer distances:
1. Default TX power was 10 dBm in both Config::default() and pib_init()
The C driver initializes all channels to IEEE802154_TXPOWER_VALUE_MAX
(20 dBm). This means the Rust driver was transmitting at half the
power of the C driver — 10x less RF power, severely reducing range.
2. txpower_convert() used `>` instead of `>=` for the max boundary.
When txpower == 20 (IEEE802154_TXPOWER_VALUE_MAX), the C driver
returns index 15 (max), but our code fell through to the formula
and returned 14 — one step below maximum power.
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Align with ESP-IDF 5.5.2: ACK frame return, pending TX, fix double-callback
Changes compared to 5.5.2 C driver:
1. ACK frame capture: isr_handle_ack_rx_done and stop_rx_ack now
store the received ACK frame in ACK_FRAME, exposed via
get_ack_frame(). OpenThread needs this for Frame Pending bit.
2. Pending TX mechanism: replaces tx_failed() deferral with the C
driver's internal pending TX. When TX is deferred, the frame is
stored and transmitted automatically via next_operation() when
the current op completes. More efficient than OpenThread retry.
3. event_end_process() now includes set_transmit_security(false)
matching 5.5.2. Removed redundant ieee802154_sec_update() calls
from ISR handlers (now covered by event_end_process).
4. Fixed double-callback bug: removed notify_state() from
next_operation() which generated duplicate tx_done/rx_available
callbacks after ISR handlers already issued them.
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Fix "Receive queue full": eliminate duplicate frame queuing
Root cause: isr_handle_rx_done() queued frames immediately via
receive_done(), but when the ACK phase completed and next_operation()
dispatched a pending TX, stop_tx_ack() (called from
stop_current_operation_inner) also called receive_done(), queuing
the same frame twice. Every TX during frame reception doubled the
queue entries.
Fix aligns with the C driver pattern:
1. isr_handle_rx_done: only queue+notify when NO ACK needed. When
ACK is required, defer queuing until ACK phase completes (frame
is safe in RX_BUFFER during TX_ACK since hardware isn't receiving).
2. isr_handle_ack_tx_done: now calls receive_done() to queue the
deferred frame, then rx_available() to notify upper layer.
3. Phase 2 abort handlers (TxAckTimeout, TxAckCoexBreak,
EnhackSecurityError): also queue the deferred frame.
4. next_operation_inner: sets state to Idle before dispatching, so
stop_current_operation_inner sees Idle (not stale TxAck) and
just calls set_cmd(Stop) without re-queuing via stop_tx_ack.
5. stop_tx_ack: keeps receive_done() for the interrupted case
(e.g., ieee802154_transmit called directly while in TxAck).
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Fix frame corruption: copy RX_BUFFER immediately, not deferred
Root cause of "Failed to process Parent Response: Parse" errors:
With a single RX_BUFFER, deferring the copy to isr_handle_ack_tx_done
allowed the hardware to overwrite the buffer before the copy happened.
The C driver can defer because it has multiple RX buffers and advances
the index in isr_handle_rx_done via next_rx_buffer().
Fix: copy RX_BUFFER immediately in isr_handle_rx_done (for ALL cases
including ACK), but defer the rx_available() notification until ACK
completes. Remove receive_done() from isr_handle_ack_tx_done, phase 2
abort handlers, stop_tx_ack, and stop_tx (TxEnhAck case) — frame is
already safely copied.
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Fix unused assignment warning: remove dead events &= line
The `events &= !(Event::Timer0Overflow as u16)` at line 578 was a dead
assignment — events is never read after this point in the ISR function.
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Remove dead `events &= !()` assignments in ISR
All `events &= !(Event::X as u16)` lines were dead assignments — each
event bit is only checked once in the sequential ISR, so clearing it
after processing has no effect. The only multi-check event is RxAbort
(phase 1 and phase 2) which is intentionally NOT cleared between phases.
Also removed `mut` from `let events` since it's no longer mutated.
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Add CHANGELOG.md entry for IEEE 802.15.4 driver improvements
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Fix CHANGELOG.md: add missing PR number (#2)
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Fix ACK_FRAME data race: move from static mut into mutex-protected IeeeState (#4)
* Initial plan
* fix: move ACK_FRAME from static mut into mutex-protected IeeeState
Move the `ack_frame` field into `IeeeState` (protected by `NonReentrantMutex`)
to eliminate a data race between ISR context writes and thread context reads.
All accesses now go through `STATE.with()`, ensuring interrupt-safety.
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Fix Timer0Overflow ISR to stop timer and disable event on ACK timeout (#6)
* Initial plan
* fix: stop Timer0 and disable Timer0Overflow event in isr_handle_timer0_done
On ACK timeout (Timer0Overflow), isr_handle_timer0_done now stops Timer0
and disables the Timer0Overflow event before calling tx_failed(), mirroring
the behavior in isr_handle_ack_rx_done and the RxAckTimeout abort handler.
This prevents repeated Timer0Overflow interrupts and repeated tx_failed
callbacks.
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Fix ieee802154: route TxEnhAck to stop_tx() instead of stop_tx_ack() (#8)
* Initial plan
* Fix ieee802154: route TxEnhAck to stop_tx() instead of stop_tx_ack()
In stop_current_operation_inner(), TxEnhAck was incorrectly routed to
stop_tx_ack(), making the TxEnhAck check in stop_tx() unreachable dead
code. The C driver routes both Transmit and TxEnhAck to stop_tx().
This fix matches that behavior, making stop_tx()'s TxEnhAck handling
reachable.
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Document IEEE 802.15.4 CCA parameter breaking change in migration guide (#13)
* Initial plan
* Add 802.15.4 transmit methods CCA parameter migration guide
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Fix IEEE 802.15.4 examples to use CCA parameter
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Fix ieee802154 driver: RxAbortReason mask and TX abort state transition (#15)
* Initial plan
* Fix RxAbortReason::all() mask and needs_next_op for RX ACK errors in ieee802154 driver
Issue 1: Expand mask from 0x00FF_FFFF to 0x03FF_FFFF to cover EdStop (bit 24) and EdCoexReject (bit 25).
Issue 3: Set needs_next_op to true for RX ACK error cases so the state machine transitions properly after tx_failed().
Issue 2 was a false positive - clear_events already executes unconditionally.
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Update TX examples with the new cca parameter; fix CHANGELOG
* Address code review feedback
---------
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com>
* Clean up duplicate interrupt setting code
* Enable PARL_IO on C5
* Model PARL_IO clocks
* Split PARL_IO into two versions
* Disable C5
* Re-enable clock after starting rx
* Clean up
* esp-radio: add basic WiFi support for ESP32-C5
* CHANGELOG.md
* CHANGELOG.md
* Introduce mac_version
* use mac_version more
* Enable (most) WiFi related tests
* Don't scan all channels in tests
* CI: Add a workflow for checking and notifying about merge conflicts
* Update .github/workflows/merge-conflict.yml
Co-authored-by: Scott Mabin <scott@mabez.dev>
---------
Co-authored-by: Scott Mabin <scott@mabez.dev>
* esp-println: Improve error log when multiple features have been enabled
* Move macros to esp-metadata-generated
* Format error message and hint default features
* esp-radio: Add top level example focused on starting esp-rtos before initiallizing the radio
* reviews
* hack
* Fix formatting
* Break comment to fit on screen
* Break line to fit screen
* Hide boilerplate, import used types
---------
Co-authored-by: Dániel Buga <bugadani@gmail.com>
* Change signature of is_connected and is_started
* CHANGELOG and MG
* Clippy
* Remove usage of `is_*` in examples (embassy_dhcp)
* Simplify wifi related examples
* Simplify wifi related examples
* CHANGELOG update
* Fix
* Unwrap less
* Enable CI and fix clippy
* Fix doctests
* Fix rustdoc issues
* Fix example
* Process multiline doc comments
* Do not expose C5 LP core yet
* Do not enable baseline check
* Prefer the Ssid type over Strings and byte arrays
* Fix: Make sure all needed events are enabled
* CHANGELOG.md / migration-guide
* Make sure `Ssid::as_str` returns a valid str
* WIP crap almost
* post-rebase
* Enable UART tests, disable UHCI
* Do not disable UART0 SCLK, disable signal inversion test
* Enable working `test_send_receive_inverted` test for c5
* changelog + cleanup
* remove uhci (post-rebase)
* Address reviews
* survive the rebase
---------
Co-authored-by: Dániel Buga <bugadani@gmail.com>