1use crate::ie;
6use anyhow::format_err;
7use fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211;
8use std::fmt;
9
10pub type MHz = u16;
17pub const BASE_FREQ_2GHZ: MHz = 2407;
18pub const BASE_FREQ_5GHZ: MHz = 5000;
19
20pub const INVALID_CHAN_IDX: u8 = 0;
21
22#[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq)]
26pub enum Bandwidth {
27 Cbw20,
28 Cbw40, Cbw40Below,
30 Cbw80,
31 Cbw160,
32 Cbw80P80 { vht_secondary_80_channel: u8 },
33}
34
35impl Bandwidth {
36 pub fn to_fidl(&self) -> (fidl_ieee80211::ChannelBandwidth, u8) {
38 match self {
39 Bandwidth::Cbw20 => (fidl_ieee80211::ChannelBandwidth::Cbw20, 0),
40 Bandwidth::Cbw40 => (fidl_ieee80211::ChannelBandwidth::Cbw40, 0),
41 Bandwidth::Cbw40Below => (fidl_ieee80211::ChannelBandwidth::Cbw40Below, 0),
42 Bandwidth::Cbw80 => (fidl_ieee80211::ChannelBandwidth::Cbw80, 0),
43 Bandwidth::Cbw160 => (fidl_ieee80211::ChannelBandwidth::Cbw160, 0),
44 Bandwidth::Cbw80P80 { vht_secondary_80_channel } => {
45 (fidl_ieee80211::ChannelBandwidth::Cbw80P80, *vht_secondary_80_channel)
46 }
47 }
48 }
49
50 pub fn from_fidl(
51 fidl_cbw: fidl_ieee80211::ChannelBandwidth,
52 fidl_vht_secondary_80_channel: u8,
53 ) -> Result<Self, anyhow::Error> {
54 match fidl_cbw {
55 fidl_ieee80211::ChannelBandwidth::Cbw20 => Ok(Bandwidth::Cbw20),
56 fidl_ieee80211::ChannelBandwidth::Cbw40 => Ok(Bandwidth::Cbw40),
57 fidl_ieee80211::ChannelBandwidth::Cbw40Below => Ok(Bandwidth::Cbw40Below),
58 fidl_ieee80211::ChannelBandwidth::Cbw80 => Ok(Bandwidth::Cbw80),
59 fidl_ieee80211::ChannelBandwidth::Cbw160 => Ok(Bandwidth::Cbw160),
60 fidl_ieee80211::ChannelBandwidth::Cbw80P80 => {
61 Ok(Bandwidth::Cbw80P80 { vht_secondary_80_channel: fidl_vht_secondary_80_channel })
62 }
63 fidl_ieee80211::ChannelBandwidthUnknown!() => {
64 Err(format_err!("Unknown channel bandwidth from fidl: {:?}", fidl_cbw))
65 }
66 }
67 }
68}
69
70#[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq)]
75pub struct Channel {
76 pub primary: u8,
77 pub bandwidth: Bandwidth,
78 pub band: fidl_ieee80211::WlanBand,
79}
80
81impl fmt::Display for Bandwidth {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 match self {
85 Bandwidth::Cbw20 => write!(f, ""), Bandwidth::Cbw40 => write!(f, "+"), Bandwidth::Cbw40Below => write!(f, "-"), Bandwidth::Cbw80 => write!(f, "V"), Bandwidth::Cbw160 => write!(f, "W"), Bandwidth::Cbw80P80 { vht_secondary_80_channel } => {
91 write!(f, "+{}P", vht_secondary_80_channel)
92 } }
94 }
95}
96
97impl fmt::Display for Channel {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 write!(f, "{}{} ({:?})", self.primary, self.bandwidth, self.band)
100 }
101}
102
103impl Channel {
104 pub const fn new(primary: u8, bandwidth: Bandwidth, band: fidl_ieee80211::WlanBand) -> Self {
105 Channel { primary, bandwidth, band }
106 }
107
108 fn get_band_start_freq(&self) -> Result<MHz, anyhow::Error> {
109 match self.band {
110 fidl_ieee80211::WlanBand::TwoGhz => Ok(BASE_FREQ_2GHZ),
111 fidl_ieee80211::WlanBand::FiveGhz => Ok(BASE_FREQ_5GHZ),
112 _ => Err(format_err!("cannot get band start freq for channel {}", self)),
113 }
114 }
115
116 fn get_center_chan_idx(&self) -> Result<u8, anyhow::Error> {
117 let is_valid = match self.band {
118 fidl_ieee80211::WlanBand::TwoGhz => self.primary <= 14,
119 fidl_ieee80211::WlanBand::FiveGhz => (36..=165).contains(&self.primary),
120 _ => false,
121 };
122 if !is_valid {
123 return Err(format_err!(
124 "cannot get center channel index for an invalid primary channel {}",
125 self
126 ));
127 }
128
129 let p = self.primary;
130 match self.bandwidth {
131 Bandwidth::Cbw20 => Ok(p),
132 Bandwidth::Cbw40 => Ok(p + 2),
133 Bandwidth::Cbw40Below => Ok(p - 2),
134 Bandwidth::Cbw80 | Bandwidth::Cbw80P80 { .. } => match p {
135 36..=48 => Ok(42),
136 52..=64 => Ok(58),
137 100..=112 => Ok(106),
138 116..=128 => Ok(122),
139 132..=144 => Ok(138),
140 148..=161_ => Ok(155),
141 _ => {
142 return Err(format_err!(
143 "cannot get center channel index for invalid channel {}",
144 self
145 ));
146 }
147 },
148 Bandwidth::Cbw160 => {
149 match p {
153 36..=64 => Ok(50),
154 100..=128 => Ok(114),
155 _ => {
156 return Err(format_err!(
157 "cannot get center channel index for invalid channel {}",
158 self
159 ));
160 }
161 }
162 }
163 }
164 }
165
166 pub fn get_center_freq(&self) -> Result<MHz, anyhow::Error> {
169 let start_freq = self.get_band_start_freq()?;
171 let center_chan_idx = self.get_center_chan_idx()?;
172 let spacing: MHz = 5;
173 Ok(start_freq + spacing * center_chan_idx as u16)
174 }
175}
176
177impl Into<fidl_ieee80211::ChannelNumber> for Channel {
178 fn into(self) -> fidl_ieee80211::ChannelNumber {
179 fidl_ieee80211::ChannelNumber { band: self.band, number: self.primary }
180 }
181}
182impl Channel {
183 pub fn from_fidl(
184 fidl_channel: fidl_ieee80211::ChannelNumber,
185 fidl_cbw: fidl_ieee80211::ChannelBandwidth,
186 fidl_vht_secondary_80_channel: fidl_ieee80211::ChannelNumber,
187 ) -> Result<Self, anyhow::Error> {
188 if fidl_cbw == fidl_ieee80211::ChannelBandwidth::Cbw80P80 {
189 if fidl_vht_secondary_80_channel.band != fidl_channel.band {
190 return Err(format_err!(
191 "vht_secondary_80_channel band ({:?}) does not match primary band ({:?})",
192 fidl_vht_secondary_80_channel.band,
193 fidl_channel.band
194 ));
195 }
196 }
197 let cbw = Bandwidth::from_fidl(fidl_cbw, fidl_vht_secondary_80_channel.number)?;
198 Ok(Channel::new(fidl_channel.number, cbw, fidl_channel.band))
199 }
200}
201
202pub fn derive_channel(
209 rx_primary_channel: fidl_ieee80211::ChannelNumber,
210 dsss_channel: Option<u8>,
211 ht_op: Option<ie::HtOperation>,
212 vht_op: Option<ie::VhtOperation>,
213) -> Channel {
214 let primary = ht_op
215 .as_ref()
216 .map(|ht_op| ht_op.primary_channel)
217 .or(dsss_channel)
218 .unwrap_or(rx_primary_channel.number);
219
220 let ht_op_cbw = ht_op.map(|ht_op| ht_op.ht_op_info.sta_chan_width());
221 let vht_cbw_and_segs =
222 vht_op.map(|vht_op| (vht_op.vht_cbw, vht_op.center_freq_seg0, vht_op.center_freq_seg1));
223
224 let cbw = match ht_op_cbw {
225 Some(ie::StaChanWidth::ANY) => {
227 let sec_chan_offset = ht_op.unwrap().ht_op_info.secondary_chan_offset();
229 derive_wide_channel_bandwidth(vht_cbw_and_segs, sec_chan_offset)
230 }
231 _ => Bandwidth::Cbw20,
233 };
234
235 Channel::new(primary, cbw, rx_primary_channel.band)
236}
237
238pub fn derive_wide_channel_bandwidth(
246 vht_cbw_and_segs: Option<(ie::VhtChannelBandwidth, u8, u8)>,
247 sec_chan_offset: ie::SecChanOffset,
248) -> Bandwidth {
249 use ie::VhtChannelBandwidth as Vcb;
250 match vht_cbw_and_segs {
251 Some((Vcb::CBW_80_160_80P80, _, 0)) => Bandwidth::Cbw80,
252 Some((Vcb::CBW_80_160_80P80, seg0, seg1)) if abs_sub(seg0, seg1) == 8 => Bandwidth::Cbw160,
253 Some((Vcb::CBW_80_160_80P80, seg0, seg1)) if abs_sub(seg0, seg1) > 16 => {
254 Bandwidth::Cbw80P80 { vht_secondary_80_channel: seg1 }
256 }
257 _ => match sec_chan_offset {
261 ie::SecChanOffset::SECONDARY_ABOVE => Bandwidth::Cbw40,
262 ie::SecChanOffset::SECONDARY_BELOW => Bandwidth::Cbw40Below,
263 ie::SecChanOffset::SECONDARY_NONE | _ => Bandwidth::Cbw20,
264 },
265 }
266}
267
268fn abs_sub(v1: u8, v2: u8) -> u8 {
269 if v2 >= v1 { v2 - v1 } else { v1 - v2 }
270}
271
272pub fn primary_channel_from_freq(freq: u32) -> Option<u8> {
276 if (2412..=2472).contains(&freq) && (freq - 2412) % 5 == 0 {
278 Some(((freq - 2407) / 5) as u8)
279 } else if freq == 2484 {
281 Some(14)
282 } else if (5180..=5720).contains(&freq) && (freq - 5180) % 20 == 0 {
284 Some(((freq - 5000) / 5) as u8)
285 } else if (5745..=5865).contains(&freq) && (freq - 5745) % 20 == 0 {
287 Some(((freq - 5000) / 5) as u8)
288 } else {
289 None
290 }
291}
292
293#[cfg(test)]
294mod tests {
295 use super::*;
296 use fidl_ieee80211::WlanBand::{FiveGhz, TwoGhz};
297
298 fn rx_channel(number: u8, band: fidl_ieee80211::WlanBand) -> fidl_ieee80211::ChannelNumber {
299 fidl_ieee80211::ChannelNumber { band, number }
300 }
301
302 #[test]
303 fn fmt_display() {
304 let mut c = Channel::new(100, Bandwidth::Cbw40, FiveGhz);
305 assert_eq!(format!("{}", c), "100+ (FiveGhz)");
306 c.bandwidth = Bandwidth::Cbw160;
307 assert_eq!(format!("{}", c), "100W (FiveGhz)");
308 c.bandwidth = Bandwidth::Cbw80P80 { vht_secondary_80_channel: 200 };
309 assert_eq!(format!("{}", c), "100+200P (FiveGhz)");
310 }
311
312 #[test]
313 fn test_band_start_freq() {
314 assert_eq!(
315 BASE_FREQ_2GHZ,
316 Channel::new(1, Bandwidth::Cbw20, TwoGhz).get_band_start_freq().unwrap()
317 );
318 assert_eq!(
319 BASE_FREQ_5GHZ,
320 Channel::new(100, Bandwidth::Cbw20, FiveGhz).get_band_start_freq().unwrap()
321 );
322 }
323
324 #[test]
325 fn test_get_center_chan_idx() {
326 assert!(Channel::new(1, Bandwidth::Cbw80, TwoGhz).get_center_chan_idx().is_err());
327 assert_eq!(
328 9,
329 Channel::new(11, Bandwidth::Cbw40Below, TwoGhz).get_center_chan_idx().unwrap()
330 );
331 assert_eq!(8, Channel::new(6, Bandwidth::Cbw40, TwoGhz).get_center_chan_idx().unwrap());
332 assert_eq!(36, Channel::new(36, Bandwidth::Cbw20, FiveGhz).get_center_chan_idx().unwrap());
333 assert_eq!(38, Channel::new(36, Bandwidth::Cbw40, FiveGhz).get_center_chan_idx().unwrap());
334 assert_eq!(42, Channel::new(36, Bandwidth::Cbw80, FiveGhz).get_center_chan_idx().unwrap());
335 assert_eq!(50, Channel::new(36, Bandwidth::Cbw160, FiveGhz).get_center_chan_idx().unwrap());
336 assert_eq!(
337 42,
338 Channel::new(36, Bandwidth::Cbw80P80 { vht_secondary_80_channel: 155 }, FiveGhz)
339 .get_center_chan_idx()
340 .unwrap()
341 );
342 }
343
344 #[test]
345 fn test_get_center_freq() {
346 assert_eq!(
347 2412 as MHz,
348 Channel::new(1, Bandwidth::Cbw20, TwoGhz).get_center_freq().unwrap()
349 );
350 assert_eq!(
351 2437 as MHz,
352 Channel::new(6, Bandwidth::Cbw20, TwoGhz).get_center_freq().unwrap()
353 );
354 assert_eq!(
355 2447 as MHz,
356 Channel::new(6, Bandwidth::Cbw40, TwoGhz).get_center_freq().unwrap()
357 );
358 assert_eq!(
359 2427 as MHz,
360 Channel::new(6, Bandwidth::Cbw40Below, TwoGhz).get_center_freq().unwrap()
361 );
362 assert_eq!(
363 5180 as MHz,
364 Channel::new(36, Bandwidth::Cbw20, FiveGhz).get_center_freq().unwrap()
365 );
366 assert_eq!(
367 5190 as MHz,
368 Channel::new(36, Bandwidth::Cbw40, FiveGhz).get_center_freq().unwrap()
369 );
370 assert_eq!(
371 5210 as MHz,
372 Channel::new(36, Bandwidth::Cbw80, FiveGhz).get_center_freq().unwrap()
373 );
374 assert_eq!(
375 5250 as MHz,
376 Channel::new(36, Bandwidth::Cbw160, FiveGhz).get_center_freq().unwrap()
377 );
378 assert_eq!(
379 5210 as MHz,
380 Channel::new(36, Bandwidth::Cbw80P80 { vht_secondary_80_channel: 155 }, FiveGhz)
381 .get_center_freq()
382 .unwrap()
383 );
384 }
385
386 #[test]
387 fn test_primarychannel_from_freq() {
388 assert_eq!(Some(1), primary_channel_from_freq(2412));
389 assert_eq!(None, primary_channel_from_freq(2413));
391 assert_eq!(Some(6), primary_channel_from_freq(2437));
392 assert_eq!(Some(13), primary_channel_from_freq(2472));
393 assert_eq!(Some(14), primary_channel_from_freq(2484));
394 assert_eq!(None, primary_channel_from_freq(5160));
396 assert_eq!(Some(36), primary_channel_from_freq(5180));
397 assert_eq!(None, primary_channel_from_freq(5190));
399 assert_eq!(Some(165), primary_channel_from_freq(5825));
400 assert_eq!(Some(173), primary_channel_from_freq(5865));
401 assert_eq!(None, primary_channel_from_freq(2400));
403 assert_eq!(None, primary_channel_from_freq(5000));
405 assert_eq!(None, primary_channel_from_freq(5885));
407 }
408
409 const RX_PRIMARY_CHAN: u8 = 11;
410 const RX_PRIMARY_CHAN_5GHZ: u8 = 36;
411 const HT_PRIMARY_CHAN: u8 = 48;
412
413 #[test]
414 fn test_derive_channel_basic() {
415 let channel = derive_channel(
416 fidl_ieee80211::ChannelNumber { number: RX_PRIMARY_CHAN, band: TwoGhz },
417 None,
418 None,
419 None,
420 );
421 assert_eq!(channel, Channel::new(RX_PRIMARY_CHAN, Bandwidth::Cbw20, TwoGhz));
422 }
423
424 #[test]
425 fn test_derive_channel_with_dsss_param() {
426 let channel = derive_channel(
427 fidl_ieee80211::ChannelNumber { number: RX_PRIMARY_CHAN, band: TwoGhz },
428 Some(6),
429 None,
430 None,
431 );
432 assert_eq!(channel, Channel::new(6, Bandwidth::Cbw20, TwoGhz));
433 }
434
435 #[test]
436 fn test_derive_channel_with_ht_20mhz() {
437 let expected_channel = Channel::new(HT_PRIMARY_CHAN, Bandwidth::Cbw20, FiveGhz);
438
439 let test_params = [
440 (ie::StaChanWidth::TWENTY_MHZ, ie::SecChanOffset::SECONDARY_NONE),
441 (ie::StaChanWidth::TWENTY_MHZ, ie::SecChanOffset::SECONDARY_ABOVE),
442 (ie::StaChanWidth::TWENTY_MHZ, ie::SecChanOffset::SECONDARY_BELOW),
443 (ie::StaChanWidth::ANY, ie::SecChanOffset::SECONDARY_NONE),
444 ];
445
446 for (ht_width, sec_chan_offset) in test_params.iter() {
447 let ht_op = ht_op(HT_PRIMARY_CHAN, *ht_width, *sec_chan_offset);
448 let channel = derive_channel(
449 fidl_ieee80211::ChannelNumber { number: RX_PRIMARY_CHAN_5GHZ, band: FiveGhz },
450 Some(RX_PRIMARY_CHAN_5GHZ),
451 Some(ht_op),
452 None,
453 );
454 assert_eq!(channel, expected_channel);
455 }
456 }
457
458 #[test]
459 fn test_derive_channel_with_ht_40mhz() {
460 let ht_op =
461 ht_op(HT_PRIMARY_CHAN, ie::StaChanWidth::ANY, ie::SecChanOffset::SECONDARY_ABOVE);
462 let channel = derive_channel(
463 fidl_ieee80211::ChannelNumber { number: RX_PRIMARY_CHAN_5GHZ, band: FiveGhz },
464 Some(RX_PRIMARY_CHAN_5GHZ),
465 Some(ht_op),
466 None,
467 );
468 assert_eq!(channel, Channel::new(HT_PRIMARY_CHAN, Bandwidth::Cbw40, FiveGhz));
469 }
470
471 #[test]
472 fn test_derive_channel_with_ht_40mhz_below() {
473 let ht_op =
474 ht_op(HT_PRIMARY_CHAN, ie::StaChanWidth::ANY, ie::SecChanOffset::SECONDARY_BELOW);
475 let channel = derive_channel(
476 fidl_ieee80211::ChannelNumber { number: RX_PRIMARY_CHAN_5GHZ, band: FiveGhz },
477 Some(RX_PRIMARY_CHAN_5GHZ),
478 Some(ht_op),
479 None,
480 );
481 assert_eq!(channel, Channel::new(HT_PRIMARY_CHAN, Bandwidth::Cbw40Below, FiveGhz));
482 }
483
484 #[test]
485 fn test_derive_channel_with_vht_80mhz() {
486 let ht_op =
487 ht_op(HT_PRIMARY_CHAN, ie::StaChanWidth::ANY, ie::SecChanOffset::SECONDARY_ABOVE);
488 let vht_op = vht_op(ie::VhtChannelBandwidth::CBW_80_160_80P80, 8, 0);
489 let channel = derive_channel(
490 fidl_ieee80211::ChannelNumber { number: RX_PRIMARY_CHAN_5GHZ, band: FiveGhz },
491 Some(RX_PRIMARY_CHAN_5GHZ),
492 Some(ht_op),
493 Some(vht_op),
494 );
495 assert_eq!(channel, Channel::new(HT_PRIMARY_CHAN, Bandwidth::Cbw80, FiveGhz));
496 }
497
498 #[test]
499 fn test_derive_channel_with_vht_160mhz() {
500 let ht_op =
501 ht_op(HT_PRIMARY_CHAN, ie::StaChanWidth::ANY, ie::SecChanOffset::SECONDARY_ABOVE);
502 let vht_op = vht_op(ie::VhtChannelBandwidth::CBW_80_160_80P80, 0, 8);
503 let channel = derive_channel(
504 fidl_ieee80211::ChannelNumber { number: RX_PRIMARY_CHAN_5GHZ, band: FiveGhz },
505 Some(RX_PRIMARY_CHAN_5GHZ),
506 Some(ht_op),
507 Some(vht_op),
508 );
509 assert_eq!(channel, Channel::new(HT_PRIMARY_CHAN, Bandwidth::Cbw160, FiveGhz));
510 }
511
512 #[test]
513 fn test_derive_channel_with_vht_80plus80mhz() {
514 let ht_op =
515 ht_op(HT_PRIMARY_CHAN, ie::StaChanWidth::ANY, ie::SecChanOffset::SECONDARY_ABOVE);
516 let vht_op = vht_op(ie::VhtChannelBandwidth::CBW_80_160_80P80, 18, 1);
517 let channel = derive_channel(
518 fidl_ieee80211::ChannelNumber { number: RX_PRIMARY_CHAN_5GHZ, band: FiveGhz },
519 Some(RX_PRIMARY_CHAN_5GHZ),
520 Some(ht_op),
521 Some(vht_op),
522 );
523 assert_eq!(
524 channel,
525 Channel::new(
526 HT_PRIMARY_CHAN,
527 Bandwidth::Cbw80P80 { vht_secondary_80_channel: 1 },
528 FiveGhz
529 )
530 );
531 }
532
533 #[test]
534 fn test_derive_channel_none() {
535 let channel = derive_channel(rx_channel(8, TwoGhz), None, None, None);
536 assert_eq!(channel, Channel::new(8, Bandwidth::Cbw20, TwoGhz));
537 }
538
539 #[test]
540 fn test_derive_channel_no_rx_primary() {
541 let channel = derive_channel(rx_channel(8, TwoGhz), Some(6), None, None);
542 assert_eq!(channel, Channel::new(6, Bandwidth::Cbw20, TwoGhz))
543 }
544
545 fn ht_op(
546 primary_channel: u8,
547 chan_width: ie::StaChanWidth,
548 offset: ie::SecChanOffset,
549 ) -> ie::HtOperation {
550 let ht_op_info =
551 ie::HtOpInfo::new().with_sta_chan_width(chan_width).with_secondary_chan_offset(offset);
552 ie::HtOperation { primary_channel, ht_op_info, basic_ht_mcs_set: ie::SupportedMcsSet(0) }
553 }
554
555 fn vht_op(vht_cbw: ie::VhtChannelBandwidth, seg0: u8, seg1: u8) -> ie::VhtOperation {
556 ie::VhtOperation {
557 vht_cbw,
558 center_freq_seg0: seg0,
559 center_freq_seg1: seg1,
560 basic_mcs_nss: ie::VhtMcsNssMap(0),
561 }
562 }
563}