1use serde::{Deserialize, Serialize};
5
6use crate::error::RvcsiError;
7use crate::frame::CsiFrame;
8use crate::ids::SessionId;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
12pub enum AdapterKind {
13 File,
15 Replay,
17 Nexmon,
19 Esp32,
21 Intel,
23 Atheros,
25 Synthetic,
27}
28
29impl AdapterKind {
30 pub fn slug(self) -> &'static str {
32 match self {
33 AdapterKind::File => "file",
34 AdapterKind::Replay => "replay",
35 AdapterKind::Nexmon => "nexmon",
36 AdapterKind::Esp32 => "esp32",
37 AdapterKind::Intel => "intel",
38 AdapterKind::Atheros => "atheros",
39 AdapterKind::Synthetic => "synthetic",
40 }
41 }
42}
43
44impl core::fmt::Display for AdapterKind {
45 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
46 f.write_str(self.slug())
47 }
48}
49
50#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
53pub struct AdapterProfile {
54 pub adapter_kind: AdapterKind,
56 pub chip: Option<String>,
58 pub firmware_version: Option<String>,
60 pub driver_version: Option<String>,
62 pub supported_channels: Vec<u16>,
64 pub supported_bandwidths_mhz: Vec<u16>,
66 pub expected_subcarrier_counts: Vec<u16>,
68 pub supports_live_capture: bool,
70 pub supports_injection: bool,
72 pub supports_monitor_mode: bool,
74}
75
76impl AdapterProfile {
77 pub fn offline(adapter_kind: AdapterKind) -> Self {
80 AdapterProfile {
81 adapter_kind,
82 chip: None,
83 firmware_version: None,
84 driver_version: None,
85 supported_channels: Vec::new(),
86 supported_bandwidths_mhz: Vec::new(),
87 expected_subcarrier_counts: Vec::new(),
88 supports_live_capture: false,
89 supports_injection: false,
90 supports_monitor_mode: false,
91 }
92 }
93
94 pub fn esp32_default() -> Self {
97 AdapterProfile {
98 adapter_kind: AdapterKind::Esp32,
99 chip: Some("ESP32-S3".to_string()),
100 firmware_version: None,
101 driver_version: None,
102 supported_channels: (1..=13).collect(),
103 supported_bandwidths_mhz: vec![20, 40],
104 expected_subcarrier_counts: vec![64, 128, 192],
105 supports_live_capture: true,
106 supports_injection: false,
107 supports_monitor_mode: false,
108 }
109 }
110
111 pub fn nexmon_default() -> Self {
113 AdapterProfile {
114 adapter_kind: AdapterKind::Nexmon,
115 chip: Some("BCM43455c0".to_string()),
116 firmware_version: None,
117 driver_version: None,
118 supported_channels: vec![1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161],
119 supported_bandwidths_mhz: vec![20, 40, 80],
120 expected_subcarrier_counts: vec![64, 128, 256],
121 supports_live_capture: true,
122 supports_injection: true,
123 supports_monitor_mode: true,
124 }
125 }
126
127 pub fn accepts_subcarrier_count(&self, count: u16) -> bool {
130 self.expected_subcarrier_counts.is_empty()
131 || self.expected_subcarrier_counts.contains(&count)
132 }
133
134 pub fn accepts_channel(&self, channel: u16) -> bool {
136 self.supported_channels.is_empty() || self.supported_channels.contains(&channel)
137 }
138}
139
140#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
143pub struct SourceHealth {
144 pub connected: bool,
146 pub frames_delivered: u64,
148 pub frames_rejected: u64,
150 pub status: Option<String>,
152}
153
154impl SourceHealth {
155 pub fn fresh(connected: bool) -> Self {
157 SourceHealth {
158 connected,
159 frames_delivered: 0,
160 frames_rejected: 0,
161 status: None,
162 }
163 }
164}
165
166#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
168pub struct SourceConfig {
169 pub source: String,
171 #[serde(default)]
173 pub target: Option<String>,
174 #[serde(default)]
176 pub channel: Option<u16>,
177 #[serde(default)]
179 pub bandwidth_mhz: Option<u16>,
180 #[serde(default)]
182 pub replay_speed: Option<f32>,
183 #[serde(default)]
185 pub options_json: Option<String>,
186}
187
188impl SourceConfig {
189 pub fn new(source: impl Into<String>) -> Self {
191 SourceConfig {
192 source: source.into(),
193 target: None,
194 channel: None,
195 bandwidth_mhz: None,
196 replay_speed: None,
197 options_json: None,
198 }
199 }
200
201 pub fn target(mut self, t: impl Into<String>) -> Self {
203 self.target = Some(t.into());
204 self
205 }
206
207 pub fn channel(mut self, c: u16) -> Self {
209 self.channel = Some(c);
210 self
211 }
212
213 pub fn bandwidth_mhz(mut self, b: u16) -> Self {
215 self.bandwidth_mhz = Some(b);
216 self
217 }
218}
219
220pub trait CsiSource: Send {
226 fn profile(&self) -> &AdapterProfile;
228
229 fn session_id(&self) -> SessionId;
231
232 fn source_id(&self) -> &crate::ids::SourceId;
234
235 fn next_frame(&mut self) -> Result<Option<CsiFrame>, RvcsiError>;
239
240 fn health(&self) -> SourceHealth;
242
243 fn stop(&mut self) -> Result<(), RvcsiError> {
245 Ok(())
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use super::*;
252
253 #[test]
254 fn offline_profile_accepts_anything() {
255 let p = AdapterProfile::offline(AdapterKind::File);
256 assert!(p.accepts_subcarrier_count(57));
257 assert!(p.accepts_channel(999));
258 assert!(!p.supports_live_capture);
259 }
260
261 #[test]
262 fn esp32_profile_bounds() {
263 let p = AdapterProfile::esp32_default();
264 assert!(p.accepts_subcarrier_count(64));
265 assert!(!p.accepts_subcarrier_count(57));
266 assert!(p.accepts_channel(6));
267 assert!(!p.accepts_channel(36));
268 assert!(p.supports_live_capture);
269 }
270
271 #[test]
272 fn source_config_builder() {
273 let c = SourceConfig::new("nexmon").target("wlan0").channel(6).bandwidth_mhz(20);
274 assert_eq!(c.source, "nexmon");
275 assert_eq!(c.target.as_deref(), Some("wlan0"));
276 assert_eq!(c.channel, Some(6));
277 let json = serde_json::to_string(&c).unwrap();
278 assert_eq!(serde_json::from_str::<SourceConfig>(&json).unwrap(), c);
279 }
280
281 #[test]
282 fn adapter_kind_slug_display() {
283 assert_eq!(AdapterKind::Nexmon.slug(), "nexmon");
284 assert_eq!(AdapterKind::Esp32.to_string(), "esp32");
285 }
286
287 #[test]
288 fn health_fresh() {
289 let h = SourceHealth::fresh(true);
290 assert!(h.connected);
291 assert_eq!(h.frames_delivered, 0);
292 }
293}