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_if::cfg_if! {
142 if #[cfg(wifi_mac_version = "1")] {
143 SecondaryChannel::from_raw(unsafe {
144 (*self.inner).rx_ctrl.secondary_channel()
145 })
146 } else {
147 SecondaryChannel::from_raw(unsafe {
148 (*self.inner).rx_ctrl.second()
149 })
150 }
151 }
152 }
153
154 #[instability::unstable]
157 pub fn timestamp(&self) -> Instant {
158 let raw_ticks = unsafe { (*self.inner).rx_ctrl.timestamp() as u64 };
159
160 Instant::EPOCH + Duration::from_micros(raw_ticks)
161 }
162
163 #[instability::unstable]
166 #[cfg(wifi_mac_version = "1")]
167 pub fn antenna(&self) -> u8 {
168 unsafe { (*self.inner).rx_ctrl.ant() as u8 }
169 }
170
171 #[instability::unstable]
173 pub fn signal_length(&self) -> u16 {
174 unsafe { (*self.inner).rx_ctrl.sig_len() as u16 }
175 }
176
177 #[instability::unstable]
180 pub fn rx_state(&self) -> u8 {
181 unsafe { (*self.inner).rx_ctrl.rx_state() as u8 }
182 }
183
184 #[instability::unstable]
186 #[cfg(esp32c6)]
187 pub fn rx_match0(&self) -> bool {
188 unsafe { (*self.inner).rx_ctrl.rxmatch0() != 0 }
189 }
190
191 #[instability::unstable]
193 #[cfg(not(wifi_mac_version = "1"))]
194 pub fn rx_match1(&self) -> bool {
195 unsafe { (*self.inner).rx_ctrl.rxmatch1() != 0 }
196 }
197
198 #[instability::unstable]
200 #[cfg(not(wifi_mac_version = "1"))]
201 pub fn rx_match2(&self) -> bool {
202 unsafe { (*self.inner).rx_ctrl.rxmatch2() != 0 }
203 }
204
205 #[instability::unstable]
207 #[cfg(not(wifi_mac_version = "1"))]
208 pub fn rx_match3(&self) -> bool {
209 unsafe { (*self.inner).rx_ctrl.rxmatch3() != 0 }
210 }
211
212 #[instability::unstable]
214 #[cfg(not(wifi_mac_version = "1"))]
215 pub fn he_siga1(&self) -> u32 {
216 unsafe { (*self.inner).rx_ctrl.he_siga1 }
217 }
218
219 #[instability::unstable]
221 #[cfg(not(wifi_mac_version = "1"))]
222 pub fn rx_end_state(&self) -> u8 {
223 unsafe { (*self.inner).rx_ctrl.rxend_state() as u8 }
224 }
225
226 #[instability::unstable]
228 #[cfg(not(wifi_mac_version = "1"))]
229 pub fn he_siga2(&self) -> u16 {
230 unsafe { (*self.inner).rx_ctrl.he_siga2 }
231 }
232
233 #[instability::unstable]
235 #[cfg(not(wifi_mac_version = "1"))]
236 pub fn is_group(&self) -> bool {
237 unsafe { (*self.inner).rx_ctrl.is_group() != 0 }
238 }
239
240 #[instability::unstable]
242 #[cfg(not(wifi_mac_version = "1"))]
243 pub fn rx_channel_estimate_length(&self) -> u32 {
244 unsafe { (*self.inner).rx_ctrl.rx_channel_estimate_len() }
245 }
246
247 #[instability::unstable]
249 #[cfg(not(wifi_mac_version = "1"))]
250 pub fn rx_channel_estimate_info_valid(&self) -> bool {
251 unsafe { (*self.inner).rx_ctrl.rx_channel_estimate_info_vld() != 0 }
252 }
253
254 #[instability::unstable]
256 #[cfg(not(wifi_mac_version = "1"))]
257 pub fn cur_bb_format(&self) -> u8 {
258 unsafe { (*self.inner).rx_ctrl.cur_bb_format() as u8 }
259 }
260
261 #[instability::unstable]
263 #[cfg(wifi_mac_version = "2")]
264 pub fn cur_single_mpdu(&self) -> bool {
265 unsafe { (*self.inner).rx_ctrl.cur_single_mpdu() != 0 }
266 }
267
268 #[instability::unstable]
270 #[cfg(wifi_mac_version = "2")]
271 pub fn he_sigb_length(&self) -> u8 {
272 unsafe { (*self.inner).rx_ctrl.he_sigb_len() as u8 }
273 }
274
275 #[instability::unstable]
277 #[cfg(not(wifi_mac_version = "1"))]
278 pub fn dump_length(&self) -> u32 {
279 unsafe { (*self.inner).rx_ctrl.dump_len() }
280 }
281
282 #[instability::unstable]
284 pub fn mac(&self) -> &[u8; 6] {
285 unsafe { &(*self.inner).mac }
286 }
287
288 #[instability::unstable]
290 pub fn destination_mac(&self) -> &[u8; 6] {
291 unsafe { &(*self.inner).dmac }
292 }
293
294 #[instability::unstable]
297 pub fn first_word_invalid(&self) -> bool {
298 unsafe { (*self.inner).first_word_invalid }
299 }
300
301 #[instability::unstable]
303 pub fn buf(&self) -> &[i8] {
304 unsafe {
305 if (*self.inner).buf.is_null() || (*self.inner).len == 0 {
306 &[]
307 } else {
308 core::slice::from_raw_parts((*self.inner).buf, (*self.inner).len as usize)
309 }
310 }
311 }
312
313 #[instability::unstable]
315 pub fn header(&self) -> &[u8] {
316 unsafe {
317 if (*self.inner).hdr.is_null() {
318 &[]
319 } else {
320 const HDR_LEN: usize = 24;
321 core::slice::from_raw_parts((*self.inner).hdr, HDR_LEN)
322 }
323 }
324 }
325
326 #[instability::unstable]
328 pub fn payload(&self) -> &[u8] {
329 unsafe {
330 if (*self.inner).payload.is_null() || (*self.inner).payload_len == 0 {
331 &[]
332 } else {
333 core::slice::from_raw_parts(
334 (*self.inner).payload,
335 (*self.inner).payload_len as usize,
336 )
337 }
338 }
339 }
340
341 #[instability::unstable]
343 pub fn rx_sequence(&self) -> u16 {
344 unsafe { (*self.inner).rx_seq }
345 }
346}
347
348pub(crate) trait CsiCallback: FnMut(WifiCsiInfo<'_>) {}
349
350impl<T> CsiCallback for T where T: FnMut(WifiCsiInfo<'_>) {}
351
352unsafe extern "C" fn csi_rx_cb<C: CsiCallback>(ctx: *mut c_void, data: *mut wifi_csi_info_t) {
353 unsafe {
354 let csi_callback = &mut *(ctx as *mut C);
355
356 let data = WifiCsiInfo {
357 inner: data,
358 _lt: PhantomData,
359 };
360 csi_callback(data);
361 }
362}
363
364#[derive(Debug, Clone, PartialEq, Eq)]
366#[cfg_attr(feature = "defmt", derive(defmt::Format))]
367#[cfg(wifi_mac_version = "1")]
368#[instability::unstable]
369pub struct CsiConfig {
370 pub lltf_en: bool,
372 pub htltf_en: bool,
374 pub stbc_htltf2_en: bool,
377 pub ltf_merge_en: bool,
380 pub channel_filter_en: bool,
383 pub manu_scale: bool,
387 pub shift: u8,
390 pub dump_ack_en: bool,
392}
393
394#[derive(Debug, Clone, PartialEq, Eq)]
396#[cfg_attr(feature = "defmt", derive(defmt::Format))]
397#[cfg(wifi_mac_version = "2")]
398#[instability::unstable]
399pub struct CsiConfig {
400 pub enable: u32,
402 pub acquire_csi_legacy: u32,
404 pub acquire_csi_ht20: u32,
406 pub acquire_csi_ht40: u32,
408 pub acquire_csi_su: u32,
410 pub acquire_csi_mu: u32,
412 pub acquire_csi_dcm: u32,
414 pub acquire_csi_beamformed: u32,
416 pub acquire_csi_he_stbc: u32,
420 pub val_scale_cfg: u32,
422 pub dump_ack_en: u32,
424 pub reserved: u32,
426}
427
428#[derive(Debug, Clone, PartialEq, Eq)]
430#[cfg_attr(feature = "defmt", derive(defmt::Format))]
431#[cfg(wifi_mac_version = "3")]
432#[instability::unstable]
433pub struct CsiConfig {
434 pub enable: u32,
436 pub acquire_csi_legacy: u32,
438 pub acquire_csi_force_lltf: bool,
440 pub acquire_csi_ht20: u32,
442 pub acquire_csi_ht40: u32,
444 pub acquire_csi_vht: bool,
446 pub acquire_csi_su: u32,
448 pub acquire_csi_mu: u32,
450 pub acquire_csi_dcm: u32,
452 pub acquire_csi_beamformed: u32,
454 pub acquire_csi_he_stbc: u32,
458 pub val_scale_cfg: u32,
460 pub dump_ack_en: u32,
462 pub reserved: u32,
464}
465
466impl CsiConfig {
467 pub(crate) fn apply_config(&self) -> Result<(), WifiError> {
469 let conf: wifi_csi_config_t = self.clone().into();
470 unsafe { esp_wifi_result!(esp_wifi_set_csi_config(&conf))? };
471 Ok(())
472 }
473
474 pub(crate) fn set_receive_cb<C>(&mut self, cb: C) -> Result<(), WifiError>
477 where
478 C: CsiCallback,
479 {
480 let cb = Box::new(cb);
481 let cb_ptr = Box::into_raw(cb) as *mut c_void;
482
483 unsafe { esp_wifi_result!(esp_wifi_set_csi_rx_cb(Some(csi_rx_cb::<C>), cb_ptr))? };
484 Ok(())
485 }
486
487 pub(crate) fn set_csi(&self, enable: bool) -> Result<(), WifiError> {
489 unsafe { esp_wifi_result!(esp_wifi_set_csi(enable))? };
491 Ok(())
492 }
493}
494
495impl Default for CsiConfig {
496 #[cfg(wifi_mac_version = "1")]
497 fn default() -> Self {
498 Self {
499 lltf_en: true,
500 htltf_en: true,
501 stbc_htltf2_en: true,
502 ltf_merge_en: true,
503 channel_filter_en: true,
504 manu_scale: false,
505 shift: 0,
506 dump_ack_en: false,
507 }
508 }
509
510 #[cfg(wifi_mac_version = "2")]
511 fn default() -> Self {
512 Self {
514 enable: 1,
515 acquire_csi_legacy: 1,
516 acquire_csi_ht20: 1,
517 acquire_csi_ht40: 1,
518 acquire_csi_su: 1,
519 acquire_csi_mu: 1,
520 acquire_csi_dcm: 1,
521 acquire_csi_beamformed: 1,
522 acquire_csi_he_stbc: 2,
523 val_scale_cfg: 2,
524 dump_ack_en: 1,
525 reserved: 19,
526 }
527 }
528
529 #[cfg(wifi_mac_version = "3")]
530 fn default() -> Self {
531 Self {
533 enable: 1,
534 acquire_csi_legacy: 1,
535 acquire_csi_force_lltf: true,
536 acquire_csi_ht20: 1,
537 acquire_csi_ht40: 1,
538 acquire_csi_vht: true,
539 acquire_csi_su: 1,
540 acquire_csi_mu: 1,
541 acquire_csi_dcm: 1,
542 acquire_csi_beamformed: 1,
543 acquire_csi_he_stbc: 2,
544 val_scale_cfg: 2,
545 dump_ack_en: 1,
546 reserved: 0,
547 }
548 }
549}
550
551#[doc(hidden)]
552impl From<CsiConfig> for wifi_csi_config_t {
553 fn from(config: CsiConfig) -> Self {
554 #[cfg(wifi_mac_version = "1")]
555 {
556 wifi_csi_config_t {
557 lltf_en: config.lltf_en,
558 htltf_en: config.htltf_en,
559 stbc_htltf2_en: config.stbc_htltf2_en,
560 ltf_merge_en: config.ltf_merge_en,
561 channel_filter_en: config.channel_filter_en,
562 manu_scale: config.manu_scale,
563 shift: config.shift,
564 dump_ack_en: config.dump_ack_en,
565 }
566 }
567 #[cfg(wifi_mac_version = "2")]
568 {
569 wifi_csi_acquire_config_t {
570 _bitfield_align_1: [0; 0],
571 _bitfield_1: wifi_csi_acquire_config_t::new_bitfield_1(
572 config.enable,
573 config.acquire_csi_legacy,
574 config.acquire_csi_ht20,
575 config.acquire_csi_ht40,
576 config.acquire_csi_su,
577 config.acquire_csi_mu,
578 config.acquire_csi_dcm,
579 config.acquire_csi_beamformed,
580 config.acquire_csi_he_stbc,
581 config.val_scale_cfg,
582 config.dump_ack_en,
583 config.reserved,
584 ),
585 }
586 }
587 #[cfg(wifi_mac_version = "3")]
588 {
589 wifi_csi_acquire_config_t {
590 _bitfield_align_1: [0; 0],
591 _bitfield_1: wifi_csi_acquire_config_t::new_bitfield_1(
592 config.enable,
593 config.acquire_csi_legacy,
594 config.acquire_csi_ht20,
595 config.acquire_csi_ht40,
596 config.acquire_csi_su,
597 config.acquire_csi_mu,
598 config.acquire_csi_dcm,
599 config.acquire_csi_beamformed,
600 config.acquire_csi_he_stbc,
601 config.val_scale_cfg,
602 config.dump_ack_en,
603 config.reserved,
604 0, 0, 0, ),
608 }
609 }
610 }
611}