1use alloc::boxed::Box;
4use core::marker::PhantomData;
5
6use esp_hal::time::{Duration, Instant};
7
8use super::{WifiError, c_types::c_void};
9#[cfg(any(wifi_mac_version = "2", wifi_mac_version = "3"))]
10use crate::sys::include::wifi_csi_acquire_config_t;
11use crate::{
12 sys::include::{
13 esp_wifi_set_csi,
14 esp_wifi_set_csi_config,
15 esp_wifi_set_csi_rx_cb,
16 wifi_csi_config_t,
17 wifi_csi_info_t,
18 },
19 wifi::{SecondaryChannel, esp_wifi_result},
20};
21
22#[repr(transparent)]
27#[derive(Debug)]
28#[cfg_attr(feature = "defmt", derive(defmt::Format))]
29#[instability::unstable]
30pub struct WifiCsiInfo<'a> {
31 inner: *const wifi_csi_info_t,
32 _lt: PhantomData<&'a ()>,
33}
34
35impl<'a> WifiCsiInfo<'_> {
36 #[instability::unstable]
38 pub fn rssi(&self) -> i8 {
39 unsafe { (*self.inner).rx_ctrl.rssi() as i8 }
43 }
44
45 #[instability::unstable]
47 pub fn rate(&self) -> u8 {
48 unsafe { (*self.inner).rx_ctrl.rate() as u8 }
49 }
50
51 #[instability::unstable]
54 #[cfg(wifi_mac_version = "1")]
55 pub fn packet_mode(&self) -> u8 {
56 unsafe { (*self.inner).rx_ctrl.sig_mode() as u8 }
57 }
58
59 #[instability::unstable]
62 #[cfg(wifi_mac_version = "1")]
63 pub fn modulation_coding_scheme(&self) -> u8 {
64 unsafe { (*self.inner).rx_ctrl.mcs() as u8 }
65 }
66
67 #[instability::unstable]
69 #[cfg(wifi_mac_version = "1")]
70 pub fn cwb(&self) -> bool {
71 unsafe { (*self.inner).rx_ctrl.cwb() != 0 }
72 }
73
74 #[instability::unstable]
78 #[cfg(wifi_mac_version = "1")]
79 pub fn smoothing(&self) -> bool {
80 unsafe { (*self.inner).rx_ctrl.smoothing() != 0 }
81 }
82
83 #[instability::unstable]
86 #[cfg(wifi_mac_version = "1")]
87 pub fn not_sounding(&self) -> bool {
88 unsafe { (*self.inner).rx_ctrl.not_sounding() != 0 }
89 }
90
91 #[instability::unstable]
93 #[cfg(wifi_mac_version = "1")]
94 pub fn aggregation(&self) -> bool {
95 unsafe { (*self.inner).rx_ctrl.aggregation() != 0 }
96 }
97
98 #[instability::unstable]
100 #[cfg(wifi_mac_version = "1")]
101 pub fn space_time_block_code(&self) -> u8 {
102 unsafe { (*self.inner).rx_ctrl.stbc() as u8 }
103 }
104
105 #[instability::unstable]
107 #[cfg(wifi_mac_version = "1")]
108 pub fn forward_error_correction_coding(&self) -> bool {
109 unsafe { (*self.inner).rx_ctrl.fec_coding() != 0 }
110 }
111
112 #[instability::unstable]
114 #[cfg(wifi_mac_version = "1")]
115 pub fn short_guide_interval(&self) -> bool {
116 unsafe { (*self.inner).rx_ctrl.sgi() != 0 }
117 }
118
119 #[instability::unstable]
121 pub fn noise_floor(&self) -> i8 {
122 unsafe { (*self.inner).rx_ctrl.noise_floor() as i8 }
123 }
124
125 #[instability::unstable]
127 #[cfg(wifi_mac_version = "1")]
128 pub fn ampdu_count(&self) -> u8 {
129 unsafe { (*self.inner).rx_ctrl.ampdu_cnt() as u8 }
130 }
131
132 #[instability::unstable]
134 pub fn channel(&self) -> u8 {
135 unsafe { (*self.inner).rx_ctrl.channel() as u8 }
136 }
137
138 #[instability::unstable]
140 pub fn secondary_channel(&self) -> SecondaryChannel {
141 cfg_select! {
142 wifi_mac_version = "1" => {
143 SecondaryChannel::from_raw(unsafe { (*self.inner).rx_ctrl.secondary_channel() })
144 }
145 _ => SecondaryChannel::from_raw(unsafe { (*self.inner).rx_ctrl.second() }),
146 }
147 }
148
149 #[instability::unstable]
152 pub fn timestamp(&self) -> Instant {
153 let raw_ticks = unsafe { (*self.inner).rx_ctrl.timestamp() as u64 };
154
155 Instant::EPOCH + Duration::from_micros(raw_ticks)
156 }
157
158 #[instability::unstable]
161 #[cfg(wifi_mac_version = "1")]
162 pub fn antenna(&self) -> u8 {
163 unsafe { (*self.inner).rx_ctrl.ant() as u8 }
164 }
165
166 #[instability::unstable]
168 pub fn signal_length(&self) -> u16 {
169 unsafe { (*self.inner).rx_ctrl.sig_len() as u16 }
170 }
171
172 #[instability::unstable]
175 pub fn rx_state(&self) -> u8 {
176 unsafe { (*self.inner).rx_ctrl.rx_state() as u8 }
177 }
178
179 #[instability::unstable]
181 #[cfg(esp32c6)]
182 pub fn rx_match0(&self) -> bool {
183 unsafe { (*self.inner).rx_ctrl.rxmatch0() != 0 }
184 }
185
186 #[instability::unstable]
188 #[cfg(not(wifi_mac_version = "1"))]
189 pub fn rx_match1(&self) -> bool {
190 unsafe { (*self.inner).rx_ctrl.rxmatch1() != 0 }
191 }
192
193 #[instability::unstable]
195 #[cfg(not(wifi_mac_version = "1"))]
196 pub fn rx_match2(&self) -> bool {
197 unsafe { (*self.inner).rx_ctrl.rxmatch2() != 0 }
198 }
199
200 #[instability::unstable]
202 #[cfg(not(wifi_mac_version = "1"))]
203 pub fn rx_match3(&self) -> bool {
204 unsafe { (*self.inner).rx_ctrl.rxmatch3() != 0 }
205 }
206
207 #[instability::unstable]
209 #[cfg(not(wifi_mac_version = "1"))]
210 pub fn he_siga1(&self) -> u32 {
211 unsafe { (*self.inner).rx_ctrl.he_siga1 }
212 }
213
214 #[instability::unstable]
216 #[cfg(not(wifi_mac_version = "1"))]
217 pub fn rx_end_state(&self) -> u8 {
218 unsafe { (*self.inner).rx_ctrl.rxend_state() as u8 }
219 }
220
221 #[instability::unstable]
223 #[cfg(not(wifi_mac_version = "1"))]
224 pub fn he_siga2(&self) -> u16 {
225 unsafe { (*self.inner).rx_ctrl.he_siga2 }
226 }
227
228 #[instability::unstable]
230 #[cfg(not(wifi_mac_version = "1"))]
231 pub fn is_group(&self) -> bool {
232 unsafe { (*self.inner).rx_ctrl.is_group() != 0 }
233 }
234
235 #[instability::unstable]
237 #[cfg(not(wifi_mac_version = "1"))]
238 pub fn rx_channel_estimate_length(&self) -> u32 {
239 unsafe { (*self.inner).rx_ctrl.rx_channel_estimate_len() }
240 }
241
242 #[instability::unstable]
244 #[cfg(not(wifi_mac_version = "1"))]
245 pub fn rx_channel_estimate_info_valid(&self) -> bool {
246 unsafe { (*self.inner).rx_ctrl.rx_channel_estimate_info_vld() != 0 }
247 }
248
249 #[instability::unstable]
251 #[cfg(not(wifi_mac_version = "1"))]
252 pub fn cur_bb_format(&self) -> u8 {
253 unsafe { (*self.inner).rx_ctrl.cur_bb_format() as u8 }
254 }
255
256 #[instability::unstable]
258 #[cfg(wifi_mac_version = "2")]
259 pub fn cur_single_mpdu(&self) -> bool {
260 unsafe { (*self.inner).rx_ctrl.cur_single_mpdu() != 0 }
261 }
262
263 #[instability::unstable]
265 #[cfg(wifi_mac_version = "2")]
266 pub fn he_sigb_length(&self) -> u8 {
267 unsafe { (*self.inner).rx_ctrl.he_sigb_len() as u8 }
268 }
269
270 #[instability::unstable]
272 #[cfg(not(wifi_mac_version = "1"))]
273 pub fn dump_length(&self) -> u32 {
274 unsafe { (*self.inner).rx_ctrl.dump_len() }
275 }
276
277 #[instability::unstable]
279 pub fn mac(&self) -> &[u8; 6] {
280 unsafe { &(*self.inner).mac }
281 }
282
283 #[instability::unstable]
285 pub fn destination_mac(&self) -> &[u8; 6] {
286 unsafe { &(*self.inner).dmac }
287 }
288
289 #[instability::unstable]
292 pub fn first_word_invalid(&self) -> bool {
293 unsafe { (*self.inner).first_word_invalid }
294 }
295
296 #[instability::unstable]
298 pub fn buf(&self) -> &[i8] {
299 unsafe {
300 if (*self.inner).buf.is_null() || (*self.inner).len == 0 {
301 &[]
302 } else {
303 core::slice::from_raw_parts((*self.inner).buf, (*self.inner).len as usize)
304 }
305 }
306 }
307
308 #[instability::unstable]
310 pub fn header(&self) -> &[u8] {
311 unsafe {
312 if (*self.inner).hdr.is_null() {
313 &[]
314 } else {
315 const HDR_LEN: usize = 24;
316 core::slice::from_raw_parts((*self.inner).hdr, HDR_LEN)
317 }
318 }
319 }
320
321 #[instability::unstable]
323 pub fn payload(&self) -> &[u8] {
324 unsafe {
325 if (*self.inner).payload.is_null() || (*self.inner).payload_len == 0 {
326 &[]
327 } else {
328 core::slice::from_raw_parts(
329 (*self.inner).payload,
330 (*self.inner).payload_len as usize,
331 )
332 }
333 }
334 }
335
336 #[instability::unstable]
338 pub fn rx_sequence(&self) -> u16 {
339 unsafe { (*self.inner).rx_seq }
340 }
341}
342
343pub(crate) trait CsiCallback: FnMut(WifiCsiInfo<'_>) {}
344
345impl<T> CsiCallback for T where T: FnMut(WifiCsiInfo<'_>) {}
346
347unsafe extern "C" fn csi_rx_cb<C: CsiCallback>(ctx: *mut c_void, data: *mut wifi_csi_info_t) {
348 unsafe {
349 let csi_callback = &mut *(ctx as *mut C);
350
351 let data = WifiCsiInfo {
352 inner: data,
353 _lt: PhantomData,
354 };
355 csi_callback(data);
356 }
357}
358
359#[derive(Debug, Clone, PartialEq, Eq)]
361#[cfg_attr(feature = "defmt", derive(defmt::Format))]
362#[cfg(wifi_mac_version = "1")]
363#[instability::unstable]
364pub struct CsiConfig {
365 pub lltf_en: bool,
367 pub htltf_en: bool,
369 pub stbc_htltf2_en: bool,
372 pub ltf_merge_en: bool,
375 pub channel_filter_en: bool,
378 pub manu_scale: bool,
382 pub shift: u8,
385 pub dump_ack_en: bool,
387}
388
389#[derive(Debug, Clone, PartialEq, Eq)]
391#[cfg_attr(feature = "defmt", derive(defmt::Format))]
392#[cfg(wifi_mac_version = "2")]
393#[instability::unstable]
394pub struct CsiConfig {
395 pub enable: u32,
397 pub acquire_csi_legacy: u32,
399 pub acquire_csi_ht20: u32,
401 pub acquire_csi_ht40: u32,
403 pub acquire_csi_su: u32,
405 pub acquire_csi_mu: u32,
407 pub acquire_csi_dcm: u32,
409 pub acquire_csi_beamformed: u32,
411 pub acquire_csi_he_stbc: u32,
415 pub val_scale_cfg: u32,
417 pub dump_ack_en: u32,
419 pub reserved: u32,
421}
422
423#[derive(Debug, Clone, PartialEq, Eq)]
425#[cfg_attr(feature = "defmt", derive(defmt::Format))]
426#[cfg(wifi_mac_version = "3")]
427#[instability::unstable]
428pub struct CsiConfig {
429 pub enable: u32,
431 pub acquire_csi_legacy: u32,
433 pub acquire_csi_force_lltf: bool,
435 pub acquire_csi_ht20: u32,
437 pub acquire_csi_ht40: u32,
439 pub acquire_csi_vht: bool,
441 pub acquire_csi_su: u32,
443 pub acquire_csi_mu: u32,
445 pub acquire_csi_dcm: u32,
447 pub acquire_csi_beamformed: u32,
449 pub acquire_csi_he_stbc: u32,
453 pub val_scale_cfg: u32,
455 pub dump_ack_en: u32,
457 pub reserved: u32,
459}
460
461impl CsiConfig {
462 pub(crate) fn apply_config(&self) -> Result<(), WifiError> {
464 let conf: wifi_csi_config_t = self.clone().into();
465 unsafe { esp_wifi_result!(esp_wifi_set_csi_config(&conf))? };
466 Ok(())
467 }
468
469 pub(crate) fn set_receive_cb<C>(&mut self, cb: C) -> Result<(), WifiError>
472 where
473 C: CsiCallback,
474 {
475 let cb = Box::new(cb);
476 let cb_ptr = Box::into_raw(cb) as *mut c_void;
477
478 unsafe { esp_wifi_result!(esp_wifi_set_csi_rx_cb(Some(csi_rx_cb::<C>), cb_ptr))? };
479 Ok(())
480 }
481
482 pub(crate) fn set_csi(&self, enable: bool) -> Result<(), WifiError> {
484 unsafe { esp_wifi_result!(esp_wifi_set_csi(enable))? };
486 Ok(())
487 }
488}
489
490impl Default for CsiConfig {
491 #[cfg(wifi_mac_version = "1")]
492 fn default() -> Self {
493 Self {
494 lltf_en: true,
495 htltf_en: true,
496 stbc_htltf2_en: true,
497 ltf_merge_en: true,
498 channel_filter_en: true,
499 manu_scale: false,
500 shift: 0,
501 dump_ack_en: false,
502 }
503 }
504
505 #[cfg(wifi_mac_version = "2")]
506 fn default() -> Self {
507 Self {
509 enable: 1,
510 acquire_csi_legacy: 1,
511 acquire_csi_ht20: 1,
512 acquire_csi_ht40: 1,
513 acquire_csi_su: 1,
514 acquire_csi_mu: 1,
515 acquire_csi_dcm: 1,
516 acquire_csi_beamformed: 1,
517 acquire_csi_he_stbc: 2,
518 val_scale_cfg: 2,
519 dump_ack_en: 1,
520 reserved: 19,
521 }
522 }
523
524 #[cfg(wifi_mac_version = "3")]
525 fn default() -> Self {
526 Self {
528 enable: 1,
529 acquire_csi_legacy: 1,
530 acquire_csi_force_lltf: true,
531 acquire_csi_ht20: 1,
532 acquire_csi_ht40: 1,
533 acquire_csi_vht: true,
534 acquire_csi_su: 1,
535 acquire_csi_mu: 1,
536 acquire_csi_dcm: 1,
537 acquire_csi_beamformed: 1,
538 acquire_csi_he_stbc: 2,
539 val_scale_cfg: 2,
540 dump_ack_en: 1,
541 reserved: 0,
542 }
543 }
544}
545
546#[doc(hidden)]
547impl From<CsiConfig> for wifi_csi_config_t {
548 fn from(config: CsiConfig) -> Self {
549 #[cfg(wifi_mac_version = "1")]
550 {
551 wifi_csi_config_t {
552 lltf_en: config.lltf_en,
553 htltf_en: config.htltf_en,
554 stbc_htltf2_en: config.stbc_htltf2_en,
555 ltf_merge_en: config.ltf_merge_en,
556 channel_filter_en: config.channel_filter_en,
557 manu_scale: config.manu_scale,
558 shift: config.shift,
559 dump_ack_en: config.dump_ack_en,
560 }
561 }
562 #[cfg(wifi_mac_version = "2")]
563 {
564 wifi_csi_acquire_config_t {
565 _bitfield_align_1: [0; 0],
566 _bitfield_1: wifi_csi_acquire_config_t::new_bitfield_1(
567 config.enable,
568 config.acquire_csi_legacy,
569 config.acquire_csi_ht20,
570 config.acquire_csi_ht40,
571 config.acquire_csi_su,
572 config.acquire_csi_mu,
573 config.acquire_csi_dcm,
574 config.acquire_csi_beamformed,
575 config.acquire_csi_he_stbc,
576 config.val_scale_cfg,
577 config.dump_ack_en,
578 config.reserved,
579 ),
580 }
581 }
582 #[cfg(wifi_mac_version = "3")]
583 {
584 wifi_csi_acquire_config_t {
585 _bitfield_align_1: [0; 0],
586 _bitfield_1: wifi_csi_acquire_config_t::new_bitfield_1(
587 config.enable,
588 config.acquire_csi_legacy,
589 config.acquire_csi_ht20,
590 config.acquire_csi_ht40,
591 config.acquire_csi_su,
592 config.acquire_csi_mu,
593 config.acquire_csi_dcm,
594 config.acquire_csi_beamformed,
595 config.acquire_csi_he_stbc,
596 config.val_scale_cfg,
597 config.dump_ack_en,
598 config.reserved,
599 0, 0, 0, ),
603 }
604 }
605 }
606}