Skip to main content

esp_radio/ieee802154/
pib.rs

1use esp_sync::NonReentrantMutex;
2
3use super::hal::{
4    set_cca_mode,
5    set_cca_threshold,
6    set_coordinator,
7    set_freq,
8    set_multipan_enable_mask,
9    set_multipan_ext_addr,
10    set_multipan_panid,
11    set_multipan_short_addr,
12    set_pending_mode,
13    set_power,
14    set_promiscuous,
15    set_rx_auto_ack,
16    set_tx_auto_ack,
17    set_tx_enhance_ack,
18};
19
20pub(crate) const CONFIG_IEEE802154_CCA_THRESHOLD: i8 = -60;
21pub(crate) const IEEE802154_FRAME_EXT_ADDR_SIZE: usize = 8;
22
23const IEEE802154_MULTIPAN_0: u8 = 0;
24const IEEE802154_MULTIPAN_MAX: usize = 4;
25
26/// Frame pending mode
27#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
28#[cfg_attr(feature = "defmt", derive(defmt::Format))]
29#[instability::unstable]
30pub enum PendingMode {
31    /// Frame pending bit always set to 1 in the ack to Data Request
32    #[default]
33    Disable  = 0,
34    /// Frame pending bit set to 1 if src address matches, in the ack to Data
35    /// Request
36    Enable   = 1,
37    /// Frame pending bit set to 1 if src address matches, in all ack frames
38    Enhanced = 2,
39    /// Frame pending bit set to 0 only if src address is short address and
40    /// matches in table, in the ack to Data Request
41    Zigbee   = 3,
42}
43
44/// CCA mode
45#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
46#[cfg_attr(feature = "defmt", derive(defmt::Format))]
47#[instability::unstable]
48pub enum CcaMode {
49    /// Carrier only
50    #[default]
51    Carrier      = 0x00,
52    /// Energy Detect only
53    Ed           = 0x01,
54    /// Carrier or Energy Detect
55    CarrierOrEd  = 0x02,
56    /// Carrier and Energy Detect
57    CarrierAndEd = 0x03,
58}
59
60#[derive(Debug, Default, Clone, Copy)]
61struct Pib {
62    auto_ack_tx: bool,
63    auto_ack_rx: bool,
64    enhance_ack_tx: bool,
65    promiscuous: bool,
66    coordinator: bool,
67    rx_when_idle: bool,
68    txpower: i8,
69    channel: u8,
70    pending_mode: PendingMode,
71    multipan_mask: u8,
72    panid: [u16; IEEE802154_MULTIPAN_MAX],
73    short_addr: [u16; IEEE802154_MULTIPAN_MAX],
74    ext_addr: [[u8; IEEE802154_FRAME_EXT_ADDR_SIZE]; IEEE802154_MULTIPAN_MAX],
75    cca_threshold: i8,
76    cca_mode: CcaMode,
77}
78
79static PIB: NonReentrantMutex<Pib> = NonReentrantMutex::new(Pib {
80    auto_ack_tx: false,
81    auto_ack_rx: false,
82    enhance_ack_tx: false,
83    coordinator: false,
84    promiscuous: false,
85    rx_when_idle: false,
86    txpower: 0,
87    channel: 0,
88    pending_mode: PendingMode::Disable,
89    multipan_mask: 0,
90    panid: [0u16; 4],
91    short_addr: [0u16; IEEE802154_MULTIPAN_MAX],
92    ext_addr: [[0; IEEE802154_FRAME_EXT_ADDR_SIZE]; IEEE802154_MULTIPAN_MAX],
93    cca_threshold: 0,
94    cca_mode: CcaMode::Carrier,
95});
96
97pub(crate) fn ieee802154_pib_init() {
98    PIB.with(|pib| {
99        *pib = Pib {
100            auto_ack_tx: true,
101            auto_ack_rx: true,
102            enhance_ack_tx: true,
103            coordinator: false,
104            promiscuous: true,
105            rx_when_idle: false,
106            txpower: 20,
107            channel: 11,
108            pending_mode: PendingMode::Disable,
109            multipan_mask: 1 << IEEE802154_MULTIPAN_0,
110            panid: [0u16; 4],
111            short_addr: [0u16; IEEE802154_MULTIPAN_MAX],
112            ext_addr: [[0xffu8; IEEE802154_FRAME_EXT_ADDR_SIZE]; IEEE802154_MULTIPAN_MAX],
113            cca_threshold: CONFIG_IEEE802154_CCA_THRESHOLD,
114            cca_mode: CcaMode::Ed,
115        }
116    });
117}
118
119pub(crate) fn ieee802154_pib_set_panid(index: u8, panid: u16) {
120    PIB.with(|pib| pib.panid[index as usize] = panid)
121}
122
123pub(crate) fn ieee802154_pib_set_promiscuous(enable: bool) {
124    PIB.with(|pib| pib.promiscuous = enable)
125}
126
127pub(crate) fn ieee802154_pib_set_auto_ack_tx(enable: bool) {
128    PIB.with(|pib| pib.auto_ack_tx = enable)
129}
130
131pub(crate) fn ieee802154_pib_set_auto_ack_rx(enable: bool) {
132    PIB.with(|pib| pib.auto_ack_rx = enable)
133}
134
135pub(crate) fn ieee802154_pib_set_enhance_ack_tx(enable: bool) {
136    PIB.with(|pib| pib.enhance_ack_tx = enable)
137}
138
139pub(crate) fn ieee802154_pib_set_coordinator(enable: bool) {
140    PIB.with(|pib| pib.coordinator = enable)
141}
142
143pub(crate) fn ieee802154_pib_set_rx_when_idle(enable: bool) {
144    PIB.with(|pib| pib.rx_when_idle = enable)
145}
146
147pub(crate) fn ieee802154_pib_get_rx_when_idle() -> bool {
148    PIB.with(|pib| pib.rx_when_idle)
149}
150
151pub(crate) fn ieee802154_pib_set_tx_power(power: i8) {
152    PIB.with(|pib| pib.txpower = power)
153}
154
155pub(crate) fn ieee802154_pib_set_channel(channel: u8) {
156    PIB.with(|pib| pib.channel = channel)
157}
158
159pub(crate) fn ieee802154_pib_set_pending_mode(mode: PendingMode) {
160    PIB.with(|pib| pib.pending_mode = mode)
161}
162
163pub(crate) fn ieee802154_pib_set_short_address(index: u8, address: u16) {
164    PIB.with(|pib| pib.short_addr[index as usize] = address)
165}
166
167pub(crate) fn ieee802154_pib_set_extended_address(
168    index: u8,
169    address: [u8; IEEE802154_FRAME_EXT_ADDR_SIZE],
170) {
171    PIB.with(|pib| pib.ext_addr[index as usize] = address)
172}
173
174pub(crate) fn ieee802154_pib_set_cca_theshold(cca_threshold: i8) {
175    PIB.with(|pib| pib.cca_threshold = cca_threshold)
176}
177
178pub(crate) fn ieee802154_pib_set_cca_mode(mode: CcaMode) {
179    PIB.with(|pib| pib.cca_mode = mode)
180}
181
182pub(crate) fn ieee802154_pib_update() {
183    PIB.with(|pib| {
184        set_freq(channel_to_freq(pib.channel));
185        set_power(ieee802154_txpower_convert(pib.txpower));
186
187        set_multipan_enable_mask(pib.multipan_mask);
188        ieee802154_set_multipan_hal(pib);
189
190        set_cca_mode(pib.cca_mode);
191        set_cca_threshold(pib.cca_threshold);
192
193        set_tx_auto_ack(pib.auto_ack_tx);
194        set_rx_auto_ack(pib.auto_ack_rx);
195        set_tx_enhance_ack(pib.enhance_ack_tx);
196
197        set_coordinator(pib.coordinator);
198        set_promiscuous(pib.promiscuous);
199        set_pending_mode(pib.pending_mode == PendingMode::Enhanced);
200    })
201}
202
203fn channel_to_freq(channel: u8) -> u8 {
204    (channel - 11) * 5 + 3
205}
206
207fn ieee802154_set_multipan_hal(pib: &Pib) {
208    for index in 0..IEEE802154_MULTIPAN_MAX {
209        if (pib.multipan_mask & (1 << index)) != 0 {
210            set_multipan_panid(index.into(), pib.panid[index]);
211            set_multipan_short_addr(index.into(), pib.short_addr[index]);
212            set_multipan_ext_addr(index.into(), pib.ext_addr[index]);
213        }
214    }
215}
216
217// https://github.com/espressif/esp-idf/blob/v5.3/components/ieee802154/driver/esp_ieee802154_pib.c#L48
218fn ieee802154_txpower_convert(txpower: i8) -> u8 {
219    cfg_if::cfg_if! {
220        if #[cfg(esp32h2)] {
221            // https://github.com/espressif/esp-idf/blob/v5.3/components/hal/esp32h2/include/hal/ieee802154_ll.h
222            const IEEE802154_TXPOWER_VALUE_MAX: i8 = 20;
223            const IEEE802154_TXPOWER_VALUE_MIN: i8 = -24;
224            const IEEE802154_TXPOWER_INDEX_MIN: i8 = 0;
225        } else if #[cfg(any(esp32c6, esp32c5))] {
226            // https://github.com/espressif/esp-idf/blob/v5.3/components/hal/esp32c6/include/hal/ieee802154_ll.h
227            const IEEE802154_TXPOWER_VALUE_MAX: i8 = 20;
228            const IEEE802154_TXPOWER_VALUE_MIN: i8 = -15;
229            const IEEE802154_TXPOWER_INDEX_MIN: i8 = 3;
230        }
231    }
232    if txpower >= IEEE802154_TXPOWER_VALUE_MAX {
233        15
234    } else if txpower <= IEEE802154_TXPOWER_VALUE_MIN {
235        IEEE802154_TXPOWER_INDEX_MIN as u8
236    } else {
237        (((txpower - IEEE802154_TXPOWER_VALUE_MIN) / 3) + IEEE802154_TXPOWER_INDEX_MIN) as u8
238    }
239}