1use crate::common::mac::WlanGi;
6use crate::probe_sequence::{ProbeEntry, ProbeSequence};
7use ieee80211::{MacAddr, MacAddrBytes};
8use log::{debug, error};
9use std::collections::{HashMap, HashSet, hash_map};
10use std::time::Duration;
11use wlan_common::ie::{HtCapabilities, RxMcsBitmask, SupportedRate};
12use wlan_common::mac::FrameControl;
13use wlan_common::tx_vector::{
14 ERP_NUM_TX_VECTOR, ERP_START_IDX, HT_NUM_MCS, HT_NUM_UNIQUE_MCS, TxVecIdx, TxVector,
15};
16use {
17 fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211,
18 fidl_fuchsia_wlan_minstrel as fidl_minstrel, fidl_fuchsia_wlan_softmac as fidl_softmac,
19};
20
21const ASSOC_CHAN_WIDTH: fidl_ieee80211::ChannelBandwidth = fidl_ieee80211::ChannelBandwidth::Cbw20;
23
24const MCS_MASK_0_31: u128 = 0xFFFFFFFF;
25const MINSTREL_FRAME_LENGTH: u32 = 1400; const MINSTREL_EXP_WEIGHT: f32 = 0.75; const MINSTREL_PROBABILITY_THRESHOLD: f32 = 0.9; const PROBE_INTERVAL: u8 = 16; const MAX_SLOW_PROBE: u64 = 2; const DEAD_PROBE_CYCLE_COUNT: u8 = 32; type TxStatsMap = HashMap<TxVecIdx, TxStats>;
37
38struct TxStats {
39 tx_vector_idx: TxVecIdx,
40 perfect_tx_time: Duration, success_cur: u64, attempts_cur: u64, moving_avg_probability: f32, expected_throughput: f32, success_total: u64, attempts_total: u64, probe_cycles_skipped: u8, probes_total: u64,
49}
50
51impl TxStats {
52 fn new(tx_vector_idx: TxVecIdx) -> Self {
53 Self {
54 tx_vector_idx,
55 perfect_tx_time: Duration::ZERO,
56 success_cur: 0,
57 attempts_cur: 0,
58 moving_avg_probability: 1.0 - MINSTREL_PROBABILITY_THRESHOLD,
60 expected_throughput: 0.0,
61 success_total: 0,
62 attempts_total: 0,
63 probe_cycles_skipped: 0,
64 probes_total: 0,
65 }
66 }
67
68 fn phy_type_strictly_preferred_over(&self, other: &TxStats) -> bool {
69 self.tx_vector_idx.is_ht() && !other.tx_vector_idx.is_ht()
74 }
75
76 fn better_for_expected_throughput_than(&self, other: &TxStats) -> bool {
77 (self.expected_throughput, self.moving_avg_probability)
78 > (other.expected_throughput, other.moving_avg_probability)
79 }
80
81 fn better_for_reliable_transmission_than(&self, other: &TxStats) -> bool {
82 if self.moving_avg_probability > MINSTREL_PROBABILITY_THRESHOLD
83 && other.moving_avg_probability > MINSTREL_PROBABILITY_THRESHOLD
84 {
85 self.expected_throughput > other.expected_throughput
87 } else {
88 self.moving_avg_probability > other.moving_avg_probability
89 }
90 }
91}
92
93impl From<&TxStats> for fidl_minstrel::StatsEntry {
94 fn from(stats: &TxStats) -> fidl_minstrel::StatsEntry {
95 fidl_minstrel::StatsEntry {
96 tx_vector_idx: *stats.tx_vector_idx,
97 tx_vec_desc: format!("{}", stats.tx_vector_idx),
98 success_cur: stats.success_cur,
99 attempts_cur: stats.attempts_cur,
100 probability: stats.moving_avg_probability,
101 cur_tp: stats.expected_throughput,
102 success_total: stats.success_total,
103 attempts_total: stats.attempts_total,
104 probes_total: stats.probes_total,
105 probe_cycles_skipped: stats.probe_cycles_skipped,
106 }
107 }
108}
109
110struct Peer {
111 addr: MacAddr,
112 tx_stats_map: TxStatsMap,
113 erp_rates: HashSet<TxVecIdx>,
114 highest_erp_rate: Option<TxVecIdx>, best_erp_for_reliability: Option<TxVecIdx>, best_expected_throughput: Option<TxVecIdx>, best_for_reliability: Option<TxVecIdx>, num_probe_cycles_done: u64,
121 num_pkt_until_next_probe: u8,
122 probes_total: u64,
123 probe_entry: ProbeEntry,
124}
125
126impl Peer {
127 fn from_assoc_cfg(assoc_cfg: &fidl_softmac::WlanAssociationConfig) -> Result<Self, zx::Status> {
128 let mut peer = Self {
129 addr: match assoc_cfg.bssid {
130 None => {
131 error!("Cannot create Peer without a BSSID.");
132 return Err(zx::Status::INTERNAL);
133 }
134 Some(bssid) => bssid.into(),
135 },
136 num_pkt_until_next_probe: PROBE_INTERVAL - 1,
137 tx_stats_map: Default::default(),
138 erp_rates: Default::default(),
139 highest_erp_rate: Default::default(),
140 best_erp_for_reliability: Default::default(),
141 best_expected_throughput: Default::default(),
142 best_for_reliability: Default::default(),
143 num_probe_cycles_done: Default::default(),
144 probes_total: Default::default(),
145 probe_entry: Default::default(),
146 };
147
148 if let Some(ht_cap) = assoc_cfg.ht_cap {
149 let mut ht_cap = HtCapabilities::from(ht_cap.clone());
150
151 let mut cap_info = ht_cap.ht_cap_info;
153 cap_info.set_short_gi_20(false);
154 cap_info.set_short_gi_40(false);
155 ht_cap.ht_cap_info = cap_info;
156
157 let mcs_set = ht_cap.mcs_set;
158 if (mcs_set.rx_mcs().0 & MCS_MASK_0_31) == 0 {
159 error!("Invalid AssocCtx. HT supported but no valid MCS: {:?}", mcs_set);
160 return Err(zx::Status::INTERNAL);
161 } else {
162 peer.add_ht(&ht_cap);
163 }
164 }
165
166 if let Some(rates) = &assoc_cfg.rates {
167 peer.erp_rates = peer.add_supported_erp(&rates[..]);
168 peer.highest_erp_rate = peer.erp_rates.iter().cloned().max();
169 }
170 debug!("tx_stats_map populated. size: {}", peer.tx_stats_map.len());
171 if peer.tx_stats_map.is_empty() {
172 error!("No usable rates for peer {}", &peer.addr);
173 return Err(zx::Status::INTERNAL);
174 }
175
176 Ok(peer)
177 }
178
179 fn handle_tx_result_report(&mut self, tx_result: &fidl_common::WlanTxResult) {
180 let mut last_attempted_idx = None;
181 for status_entry in &tx_result.tx_result_entry[..] {
182 let idx = match TxVecIdx::new(status_entry.tx_vector_idx) {
183 Some(idx) => idx,
184 None => break,
185 };
186 last_attempted_idx.replace(idx);
187 let stats = match self.tx_stats_map.entry(idx) {
189 hash_map::Entry::Occupied(val) => Some(val.into_mut()),
190 hash_map::Entry::Vacant(vacant) => {
191 idx.to_erp_rate().map(|rate| vacant.insert(erp_idx_stats(idx, rate)))
192 }
193 };
194 match stats {
195 Some(stats) => stats.attempts_cur += status_entry.attempts as u64,
196 None => {
197 last_attempted_idx.take();
198 debug!("error: Invalid TxVecIdx: {:?}", idx)
199 }
200 }
201 }
202 if let Some(idx) = last_attempted_idx {
203 if tx_result.result_code == fidl_common::WlanTxResultCode::Success {
204 self.tx_stats_map.get_mut(&idx).unwrap().success_cur += 1;
206 }
207 }
208 }
209
210 fn add_ht(&mut self, ht_cap: &HtCapabilities) {
211 let mut max_size = HT_NUM_MCS + ERP_NUM_TX_VECTOR; let cap_info = ht_cap.ht_cap_info;
213 let sgi_20 = cap_info.short_gi_20();
214 let sgi_40 = cap_info.short_gi_40();
215 if sgi_20 {
216 max_size += HT_NUM_MCS;
217 }
218 if ASSOC_CHAN_WIDTH == fidl_ieee80211::ChannelBandwidth::Cbw40 {
219 max_size += HT_NUM_MCS;
220 if sgi_40 {
221 max_size += HT_NUM_MCS;
222 }
223 }
224
225 debug!("max_size is {}", max_size);
226 self.tx_stats_map.reserve(max_size as usize);
227 let mcs_set = ht_cap.mcs_set;
228 self.add_supported_ht(
229 fidl_ieee80211::ChannelBandwidth::Cbw20,
230 WlanGi::G_800NS,
231 mcs_set.rx_mcs(),
232 );
233 if sgi_20 {
234 self.add_supported_ht(
235 fidl_ieee80211::ChannelBandwidth::Cbw20,
236 WlanGi::G_400NS,
237 mcs_set.rx_mcs(),
238 );
239 }
240 if ASSOC_CHAN_WIDTH == fidl_ieee80211::ChannelBandwidth::Cbw40 {
241 self.add_supported_ht(
242 fidl_ieee80211::ChannelBandwidth::Cbw40,
243 WlanGi::G_800NS,
244 mcs_set.rx_mcs(),
245 );
246 if sgi_40 {
247 self.add_supported_ht(
248 fidl_ieee80211::ChannelBandwidth::Cbw40,
249 WlanGi::G_400NS,
250 mcs_set.rx_mcs(),
251 );
252 }
253 }
254
255 debug!("TxStatsMap size: {}", self.tx_stats_map.len());
256 }
257
258 fn add_supported_ht(
261 &mut self,
262 channel_bandwidth: fidl_ieee80211::ChannelBandwidth,
263 gi: WlanGi,
264 mcs_set: RxMcsBitmask,
265 ) {
266 let mut tx_stats_added = 0;
267 for mcs_idx in 0..HT_NUM_MCS {
268 if mcs_set.support(mcs_idx) {
269 let tx_vector =
270 TxVector::new(fidl_common::WlanPhyType::Ht, gi, channel_bandwidth, mcs_idx)
271 .expect("Should be a valid TxVector");
272 let tx_vector_idx = tx_vector.to_idx();
273 let perfect_tx_time = tx_time_ht(channel_bandwidth, gi, mcs_idx);
274 let tx_stats = TxStats { perfect_tx_time, ..TxStats::new(tx_vector_idx) };
275 self.tx_stats_map.insert(tx_vector_idx, tx_stats);
276 tx_stats_added += 1;
277 }
278 }
279 debug!(
280 "{} HTs added with channel_bandwidth={:?}, gi={:?}",
281 tx_stats_added, channel_bandwidth, gi
282 );
283 }
284
285 fn add_supported_erp(&mut self, rates: &[u8]) -> HashSet<TxVecIdx> {
286 let mut tx_stats_added = 0;
287 let basic_rates: HashSet<TxVecIdx> = rates
288 .iter()
289 .filter_map(|rate| {
290 let rate = SupportedRate(*rate);
291 let tx_vector = match TxVector::from_supported_rate(&rate) {
292 Ok(tx_vector) => Some(tx_vector),
293 Err(e) => {
294 error!("Could not create tx vector from supported rate: {}", e);
295 None
296 }
297 }?;
298 if tx_vector.phy() != fidl_common::WlanPhyType::Erp {
299 return None;
300 }
301 let tx_vector_idx = tx_vector.to_idx();
302 self.tx_stats_map.insert(tx_vector_idx, erp_idx_stats(tx_vector_idx, rate));
303 tx_stats_added += 1;
304 if rate.basic() { Some(tx_vector_idx) } else { None }
305 })
306 .collect();
307 debug!("{} ERP added.", tx_stats_added);
308 if basic_rates.is_empty() {
309 vec![TxVecIdx::new(ERP_START_IDX).unwrap()].into_iter().collect()
310 } else {
311 basic_rates
312 }
313 }
314
315 fn update_stats(&mut self) {
316 for tx_stats in self.tx_stats_map.values_mut() {
318 if tx_stats.attempts_cur != 0 {
319 let probability = tx_stats.success_cur as f32 / tx_stats.attempts_cur as f32;
320 if tx_stats.attempts_total == 0 {
321 tx_stats.moving_avg_probability = probability;
322 } else {
323 tx_stats.moving_avg_probability = tx_stats.moving_avg_probability
324 * MINSTREL_EXP_WEIGHT
325 + probability * (1.0 - MINSTREL_EXP_WEIGHT);
326 }
327 tx_stats.attempts_total += tx_stats.attempts_cur;
328 tx_stats.success_total += tx_stats.success_cur;
329 tx_stats.attempts_cur = 0;
330 tx_stats.success_cur = 0;
331 tx_stats.probe_cycles_skipped = 0;
332 } else {
333 tx_stats.probe_cycles_skipped = tx_stats.probe_cycles_skipped.saturating_add(1);
334 }
335 const NANOS_PER_SECOND: f32 = 1e9;
336 tx_stats.expected_throughput = NANOS_PER_SECOND
338 / tx_stats.perfect_tx_time.as_nanos() as f32
339 * tx_stats.moving_avg_probability;
340 }
341
342 let arbitrary_rate = match self.tx_stats_map.iter().next() {
344 Some((tx_vec_idx, _)) => *tx_vec_idx,
345 None => return, };
347
348 let mut best_expected_throughput = arbitrary_rate;
350 let mut best_for_reliability = arbitrary_rate;
351 let mut best_erp_for_reliability = self.highest_erp_rate;
352 for (tx_vector_idx, tx_stats) in &self.tx_stats_map {
353 let best_throughput_stats = self.tx_stats_map.get(&best_expected_throughput).unwrap();
356 let best_reliability_stats = self.tx_stats_map.get(&best_for_reliability).unwrap();
357
358 if (!tx_unlikely(tx_stats)
361 && tx_stats.phy_type_strictly_preferred_over(best_throughput_stats))
362 || (tx_stats.better_for_expected_throughput_than(best_throughput_stats)
363 && !(!tx_unlikely(best_throughput_stats)
364 && best_throughput_stats.phy_type_strictly_preferred_over(tx_stats)))
365 {
366 best_expected_throughput = *tx_vector_idx;
367 }
368 if (!tx_unlikely(tx_stats)
371 && tx_stats.phy_type_strictly_preferred_over(best_reliability_stats))
372 || (tx_stats.better_for_reliable_transmission_than(best_reliability_stats)
373 && !(!tx_unlikely(best_reliability_stats)
374 && best_reliability_stats.phy_type_strictly_preferred_over(tx_stats)))
375 {
376 best_for_reliability = *tx_vector_idx;
377 }
378 if let Some(best_erp_for_reliability) = best_erp_for_reliability.as_mut() {
379 let best_erp_reliability_stats =
380 self.tx_stats_map.get(best_erp_for_reliability).unwrap();
381 if self.erp_rates.contains(tx_vector_idx)
382 && tx_stats.better_for_reliable_transmission_than(best_erp_reliability_stats)
383 {
384 *best_erp_for_reliability = *tx_vector_idx;
385 }
386 }
387 }
388 self.best_expected_throughput = Some(best_expected_throughput);
389 self.best_for_reliability = Some(best_for_reliability);
390 self.best_erp_for_reliability = best_erp_for_reliability;
391 }
392
393 fn get_tx_vector_idx(
394 &mut self,
395 needs_reliability: bool,
396 probe_sequence: &ProbeSequence,
397 ) -> Option<TxVecIdx> {
398 if needs_reliability {
399 self.best_for_reliability
400 } else if self.num_pkt_until_next_probe > 0 {
401 self.num_pkt_until_next_probe -= 1;
402 self.best_expected_throughput
403 } else {
404 self.num_pkt_until_next_probe = PROBE_INTERVAL - 1;
405 self.get_next_probe(probe_sequence)
406 }
407 }
408
409 fn get_next_probe(&mut self, probe_sequence: &ProbeSequence) -> Option<TxVecIdx> {
410 let slow_probe_cutoff = self.tx_stats_map.get(&self.best_for_reliability?)?.perfect_tx_time;
413 if self.tx_stats_map.len() == 1 {
414 return self.best_expected_throughput;
415 }
416 for _ in 0..self.tx_stats_map.len() {
418 let probe_idx = self.next_supported_probe_idx(probe_sequence);
419 let tx_stats = self.tx_stats_map.get_mut(&probe_idx).unwrap();
420 if Some(probe_idx) == self.best_erp_for_reliability.or(self.highest_erp_rate)
423 || Some(probe_idx) == self.best_expected_throughput.or(self.best_for_reliability)
424 || tx_stats.attempts_cur > self.num_probe_cycles_done
426 || (tx_stats.perfect_tx_time > slow_probe_cutoff
428 && tx_stats.attempts_cur >= MAX_SLOW_PROBE)
429 || (tx_unlikely(tx_stats)
431 && (tx_stats.probe_cycles_skipped < DEAD_PROBE_CYCLE_COUNT
432 || tx_stats.attempts_cur > 0))
433 {
434 continue;
435 }
436 self.probes_total += 1;
437 tx_stats.probes_total += 1;
438 return Some(probe_idx);
439 }
440 return self.best_expected_throughput;
441 }
442
443 fn next_supported_probe_idx(&mut self, probe_sequence: &ProbeSequence) -> TxVecIdx {
445 assert!(
446 !self.tx_stats_map.is_empty(),
447 "Cannot call next_supported_probe_idx with empty tx_stats_map"
448 );
449
450 loop {
451 let idx = probe_sequence.next(&mut self.probe_entry);
452 if self.probe_entry.cycle_complete() {
453 self.num_probe_cycles_done += 1;
454 }
455 if self.tx_stats_map.contains_key(&idx) {
456 return idx;
457 }
458 }
459 }
460}
461
462fn tx_unlikely(tx_stats: &TxStats) -> bool {
463 tx_stats.moving_avg_probability < 1.0 - MINSTREL_PROBABILITY_THRESHOLD
464}
465
466pub trait TimerManager {
467 fn schedule(&mut self, from_now: Duration);
468 fn cancel(&mut self);
469}
470
471pub struct MinstrelRateSelector<T: TimerManager> {
479 timer_manager: T,
480 update_interval: Duration,
481 probe_sequence: ProbeSequence,
482 peer_map: HashMap<MacAddr, Peer>,
483 outdated_peers: HashSet<MacAddr>,
484}
485
486impl<T: TimerManager> MinstrelRateSelector<T> {
487 pub fn new(timer_manager: T, update_interval: Duration, probe_sequence: ProbeSequence) -> Self {
488 Self {
489 timer_manager,
490 update_interval,
491 probe_sequence,
492 peer_map: Default::default(),
493 outdated_peers: Default::default(),
494 }
495 }
496
497 pub fn add_peer(
498 &mut self,
499 assoc_cfg: &fidl_softmac::WlanAssociationConfig,
500 ) -> Result<(), zx::Status> {
501 let bssid: MacAddr = match assoc_cfg.bssid {
502 None => {
503 error!("Attempted to add peer with no BSSID.");
504 return Err(zx::Status::INTERNAL);
505 }
506 Some(bssid) => bssid.into(),
507 };
508 if self.peer_map.contains_key(&bssid) {
509 error!("Attempted to add peer {} twice.", &bssid);
510 } else {
511 let mut peer = Peer::from_assoc_cfg(assoc_cfg)?;
512 if self.peer_map.is_empty() {
513 self.timer_manager.schedule(self.update_interval);
514 }
515 peer.update_stats();
516 self.peer_map.insert(bssid, peer);
517 }
518 Ok(())
519 }
520
521 pub fn remove_peer(&mut self, addr: &MacAddr) {
522 self.outdated_peers.remove(addr);
523 match self.peer_map.remove(addr) {
524 Some(_) => debug!("Peer {} removed.", addr),
525 None => debug!("Cannot remove peer {}, not found.", addr),
526 }
527 if self.peer_map.is_empty() {
528 self.timer_manager.cancel();
529 }
530 }
531
532 pub fn handle_tx_result_report(&mut self, tx_result: &fidl_common::WlanTxResult) {
533 let peer_addr: MacAddr = tx_result.peer_addr.into();
534 match self.peer_map.get_mut(&peer_addr) {
535 Some(peer) => {
536 peer.handle_tx_result_report(tx_result);
537 self.outdated_peers.insert(peer_addr);
538 }
539 None => {
540 debug!("Peer {} received tx status report after it was removed.", peer_addr);
541 }
542 }
543 }
544
545 fn update_stats(&mut self) {
546 for outdated_peer in self.outdated_peers.drain() {
547 self.peer_map.get_mut(&outdated_peer).map(|peer| peer.update_stats());
548 }
549 }
550
551 pub fn handle_timeout(&mut self) {
552 self.timer_manager.schedule(self.update_interval);
554 self.update_stats();
555 }
556
557 pub fn get_tx_vector_idx(
558 &mut self,
559 frame_control: &FrameControl,
560 peer_addr: &MacAddr,
561 flags: fidl_softmac::WlanTxInfoFlags,
562 ) -> Option<TxVecIdx> {
563 match self.peer_map.get_mut(peer_addr) {
564 None => TxVecIdx::new(ERP_START_IDX + ERP_NUM_TX_VECTOR as u16 - 1),
565 Some(peer) => {
566 if frame_control.is_data() {
567 let needs_reliability =
568 flags.contains(fidl_softmac::WlanTxInfoFlags::FAVOR_RELIABILITY);
569 peer.get_tx_vector_idx(needs_reliability, &self.probe_sequence)
570 } else {
571 peer.best_erp_for_reliability
572 }
573 }
574 }
575 }
576
577 pub fn get_fidl_peers(&self) -> fidl_minstrel::Peers {
578 fidl_minstrel::Peers {
579 addrs: self.peer_map.iter().map(|(peer, _)| peer.to_array()).collect(),
580 }
581 }
582
583 pub fn get_fidl_peer_stats(
584 &self,
585 peer_addr: &MacAddr,
586 ) -> Result<fidl_minstrel::Peer, zx::Status> {
587 let peer = self.peer_map.get(peer_addr).ok_or(zx::Status::NOT_FOUND)?;
588 Ok(fidl_minstrel::Peer {
589 addr: peer_addr.to_array(),
590 max_tp: tx_vec_idx_opt_to_u16(&peer.best_expected_throughput),
591 max_probability: tx_vec_idx_opt_to_u16(&peer.best_for_reliability),
592 basic_highest: tx_vec_idx_opt_to_u16(&peer.highest_erp_rate),
593 basic_max_probability: tx_vec_idx_opt_to_u16(&peer.best_erp_for_reliability),
594 probes: peer.probes_total,
595 entries: peer.tx_stats_map.iter().map(|(_, entry)| entry.into()).collect(),
596 })
597 }
598
599 pub fn is_active(&self) -> bool {
600 !self.peer_map.is_empty()
601 }
602}
603
604fn tx_vec_idx_opt_to_u16(tx_vec_idx: &Option<TxVecIdx>) -> u16 {
605 match tx_vec_idx {
606 Some(idx) => **idx,
607 None => 0,
608 }
609}
610
611fn tx_time_ht(
612 channel_bandwidth: fidl_ieee80211::ChannelBandwidth,
613 gi: WlanGi,
614 relative_mcs_idx: u8,
615) -> Duration {
616 header_tx_time_ht() + payload_tx_time_ht(channel_bandwidth, gi, relative_mcs_idx)
617}
618
619fn header_tx_time_ht() -> Duration {
620 Duration::ZERO
622}
623
624fn payload_tx_time_ht(
630 channel_bandwidth: fidl_ieee80211::ChannelBandwidth,
631 gi: WlanGi,
632 mcs_idx: u8,
633) -> Duration {
634 const BITS_PER_SYMBOL_LIST: [u16; HT_NUM_UNIQUE_MCS as usize + 2] =
637 [26, 52, 78, 104, 156, 208, 234, 260, 312, 347];
638 const DATA_SUB_CARRIERS_20: u16 = 52;
641 const DATA_SUB_CARRIERS_40: u16 = 108;
643 let nss = 1 + mcs_idx / HT_NUM_UNIQUE_MCS;
646 let relative_mcs_idx = mcs_idx % HT_NUM_UNIQUE_MCS;
647 let bits_per_symbol = if channel_bandwidth == fidl_ieee80211::ChannelBandwidth::Cbw40 {
648 BITS_PER_SYMBOL_LIST[relative_mcs_idx as usize] * DATA_SUB_CARRIERS_40
649 / DATA_SUB_CARRIERS_20
650 } else {
651 BITS_PER_SYMBOL_LIST[relative_mcs_idx as usize]
652 };
653
654 const TX_TIME_PER_SYMBOL_GI_800: Duration = Duration::from_nanos(4000);
655 const TX_TIME_PER_SYMBOL_GI_400: Duration = Duration::from_nanos(3600);
656 const TX_TIME_PADDING_GI_400: Duration = Duration::from_nanos(800);
657
658 match gi {
660 WlanGi::G_400NS => {
661 TX_TIME_PADDING_GI_400
662 + (TX_TIME_PER_SYMBOL_GI_400 * 8 * MINSTREL_FRAME_LENGTH)
663 / (nss as u32 * bits_per_symbol as u32)
664 }
665 WlanGi::G_800NS => {
666 (TX_TIME_PER_SYMBOL_GI_800 * 8 * MINSTREL_FRAME_LENGTH)
667 / (nss as u32 * bits_per_symbol as u32)
668 }
669 _ => panic!("payload_tx_time_ht is invalid for non-ht phy"),
670 }
671}
672
673fn tx_time_erp(rate: &SupportedRate) -> Duration {
674 header_tx_time_erp() + payload_tx_time_erp(rate)
675}
676
677fn header_tx_time_erp() -> Duration {
678 Duration::ZERO
680}
681
682fn payload_tx_time_erp(rate: &SupportedRate) -> Duration {
683 let bits_per_symbol = rate.rate() * 2;
686 const TX_TIME_PER_SYMBOL: Duration = Duration::from_nanos(4000);
687 TX_TIME_PER_SYMBOL * 8 * MINSTREL_FRAME_LENGTH / bits_per_symbol as u32
688}
689
690fn erp_idx_stats(tx_vector_idx: TxVecIdx, rate: SupportedRate) -> TxStats {
691 let perfect_tx_time = tx_time_erp(&rate);
692 TxStats { perfect_tx_time, ..TxStats::new(tx_vector_idx) }
693}
694
695#[cfg(test)]
696mod tests {
697 use super::*;
698 use fidl_fuchsia_wlan_common as fidl_common;
699 use fuchsia_sync::Mutex;
700 use std::sync::{Arc, LazyLock};
701 use wlan_common::ie::{ChanWidthSet, HtCapabilityInfo};
702 use wlan_common::mac::FrameType;
703 use wlan_common::tx_vector::HT_START_IDX;
704
705 struct MockTimerManager {
706 scheduled: Arc<Mutex<Option<Duration>>>,
707 }
708
709 impl TimerManager for MockTimerManager {
710 fn schedule(&mut self, from_now: Duration) {
711 let mut scheduled = self.scheduled.lock();
712 scheduled.replace(from_now);
713 }
714 fn cancel(&mut self) {
715 let mut scheduled = self.scheduled.lock();
716 scheduled.take();
717 }
718 }
719
720 fn mock_minstrel() -> (MinstrelRateSelector<MockTimerManager>, Arc<Mutex<Option<Duration>>>) {
721 let timer = Arc::new(Mutex::new(None));
722 let timer_manager = MockTimerManager { scheduled: timer.clone() };
723 let update_interval = Duration::from_micros(100);
724 let probe_sequence = ProbeSequence::sequential();
725 (MinstrelRateSelector::new(timer_manager, update_interval, probe_sequence), timer)
726 }
727
728 static TEST_MAC_ADDR: LazyLock<MacAddr> =
729 LazyLock::new(|| MacAddr::from([50, 53, 51, 56, 55, 52]));
730
731 const BASIC_RATE_BIT: u8 = 0b10000000;
732
733 fn ht_assoc_cfg() -> fidl_softmac::WlanAssociationConfig {
734 let mut ht_cap = wlan_common::ie::fake_ht_capabilities();
735 let mut ht_cap_info = HtCapabilityInfo(0);
736 ht_cap_info.set_short_gi_40(true);
737 ht_cap_info.set_short_gi_20(true);
738 ht_cap_info.set_chan_width_set(ChanWidthSet::TWENTY_FORTY);
739 ht_cap.ht_cap_info = ht_cap_info;
740 ht_cap.mcs_set.0 = 0xffff; fidl_softmac::WlanAssociationConfig {
743 bssid: Some(TEST_MAC_ADDR.to_array()),
744 aid: Some(42),
745 listen_interval: Some(0),
746 channel: Some(fidl_ieee80211::WlanChannel {
747 primary: 149,
748 cbw: fidl_ieee80211::ChannelBandwidth::Cbw40,
749 secondary80: 0,
750 }),
751 qos: Some(true),
752 wmm_params: None,
753 rates: Some(vec![
754 2,
755 4,
756 11,
757 22,
758 12 | BASIC_RATE_BIT,
759 18,
760 24,
761 36,
762 48,
763 72,
764 96,
765 108 | BASIC_RATE_BIT,
766 ]),
767 capability_info: Some(0),
768 ht_cap: Some(ht_cap.into()),
769 ht_op: None,
770 vht_cap: None,
771 vht_op: None,
772 ..Default::default()
773 }
774 }
775
776 #[test]
777 fn peer_from_assoc_cfg() {
778 let assoc_cfg = ht_assoc_cfg();
779 let peer = Peer::from_assoc_cfg(&assoc_cfg)
780 .expect("Failed to convert WlanAssociationConfig into Peer.");
781 assert_eq!(peer.addr, assoc_cfg.bssid.unwrap().into());
782 assert_eq!(peer.tx_stats_map.len(), 24);
783 let mut peer_rates = peer
784 .tx_stats_map
785 .keys()
786 .into_iter()
787 .map(|tx_vector_idx| **tx_vector_idx)
788 .collect::<Vec<u16>>();
789 peer_rates.sort();
790 assert_eq!(
791 peer_rates,
792 vec![
793 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 129, 130, 131, 132, 133, 134, 135, 136, ]
796 );
797 let mut peer_erp_rates =
798 peer.erp_rates.iter().map(|tx_vector_idx| **tx_vector_idx).collect::<Vec<u16>>();
799 peer_erp_rates.sort();
800 let expected_basic_rate_1 =
801 TxVector::from_supported_rate(&SupportedRate(12 | BASIC_RATE_BIT)).unwrap().to_idx();
802 let expected_basic_rate_2 =
803 TxVector::from_supported_rate(&SupportedRate(108 | BASIC_RATE_BIT)).unwrap().to_idx();
804 assert_eq!(peer_erp_rates, vec![*expected_basic_rate_1, *expected_basic_rate_2]);
805 assert_eq!(peer.highest_erp_rate, TxVecIdx::new(136));
806 }
807
808 #[test]
809 fn add_peer() {
810 let (mut minstrel, timer) = mock_minstrel();
811 assert!(timer.lock().is_none()); minstrel.add_peer(&ht_assoc_cfg()).expect("Failed to add peer.");
813 assert!(timer.lock().is_some()); let peers = minstrel.get_fidl_peers();
816 assert_eq!(peers.addrs.len(), 1);
817
818 let peer_addr: MacAddr = {
819 let mut peer_addr = [0u8; 6];
820 peer_addr.copy_from_slice(&peers.addrs[0][..]);
821 peer_addr.into()
822 };
823 let peer_stats =
824 minstrel.get_fidl_peer_stats(&peer_addr).expect("Failed to get peer stats");
825 assert_eq!(&peer_stats.addr, TEST_MAC_ADDR.as_array());
826 assert_eq!(peer_stats.entries.len(), 24);
828 assert_eq!(peer_stats.max_tp, 16); assert_eq!(peer_stats.basic_highest, ERP_START_IDX + ERP_NUM_TX_VECTOR as u16 - 1);
830 assert_eq!(peer_stats.basic_max_probability, ERP_START_IDX + ERP_NUM_TX_VECTOR as u16 - 1);
831 }
832
833 #[test]
834 fn remove_peer() {
835 let (mut minstrel, timer) = mock_minstrel();
836 minstrel.add_peer(&ht_assoc_cfg()).expect("Failed to add peer.");
837 assert_eq!(minstrel.get_fidl_peers().addrs.len(), 1);
838 assert!(timer.lock().is_some()); minstrel.remove_peer(&TEST_MAC_ADDR);
841 assert!(timer.lock().is_none()); assert!(minstrel.get_fidl_peers().addrs.is_empty());
844 assert_eq!(minstrel.get_fidl_peer_stats(&TEST_MAC_ADDR), Err(zx::Status::NOT_FOUND));
845 }
846
847 #[test]
848 fn remove_second_peer() {
849 let (mut minstrel, timer) = mock_minstrel();
850 minstrel.add_peer(&ht_assoc_cfg()).expect("Failed to add peer.");
851 let mut peer2 = ht_assoc_cfg();
852 peer2.bssid = Some([11, 12, 13, 14, 15, 16]);
853 minstrel.add_peer(&peer2).expect("Failed to add peer.");
854 assert_eq!(minstrel.get_fidl_peers().addrs.len(), 2);
855 assert!(timer.lock().is_some()); minstrel.remove_peer(&TEST_MAC_ADDR);
858 assert_eq!(minstrel.get_fidl_peers().addrs.len(), 1);
859 assert!(timer.lock().is_some()); assert_eq!(minstrel.get_fidl_peer_stats(&TEST_MAC_ADDR), Err(zx::Status::NOT_FOUND));
862 assert!(minstrel.get_fidl_peer_stats(&peer2.bssid.unwrap().into()).is_ok());
863 }
864
865 fn make_tx_result(entries: Vec<(u16, u8)>, success: bool) -> fidl_common::WlanTxResult {
867 assert!(entries.len() <= 8);
868 let mut tx_result_entry =
869 [fidl_common::WlanTxResultEntry { tx_vector_idx: 0, attempts: 0 }; 8];
870 tx_result_entry[0..entries.len()].copy_from_slice(
871 &entries
872 .into_iter()
873 .map(|(tx_vector_idx, attempts)| fidl_common::WlanTxResultEntry {
874 tx_vector_idx,
875 attempts,
876 })
877 .collect::<Vec<fidl_common::WlanTxResultEntry>>()[..],
878 );
879 let result_code = if success {
880 fidl_common::WlanTxResultCode::Success
881 } else {
882 fidl_common::WlanTxResultCode::Failed
883 };
884 fidl_common::WlanTxResult {
885 tx_result_entry,
886 peer_addr: TEST_MAC_ADDR.to_array(),
887 result_code,
888 }
889 }
890
891 #[test]
892 fn handle_tx_result_reports() {
893 let tx_result = make_tx_result(vec![(16, 1), (15, 1), (14, 1), (13, 1)], true);
895
896 let (mut minstrel, _timer) = mock_minstrel();
897 minstrel.add_peer(&ht_assoc_cfg()).expect("Failed to add peer.");
898 minstrel.handle_tx_result_report(&tx_result);
899
900 let peer_stats =
902 minstrel.get_fidl_peer_stats(&TEST_MAC_ADDR).expect("Failed to get peer stats");
903 assert_eq!(peer_stats.max_tp, 16);
904
905 minstrel.handle_timeout();
906 let peer_stats =
907 minstrel.get_fidl_peer_stats(&TEST_MAC_ADDR).expect("Failed to get peer stats");
908 assert_eq!(peer_stats.max_tp, 13);
909 assert_eq!(peer_stats.max_probability, 13);
910
911 let tx_result = make_tx_result(vec![(13, 1), (9, 1)], true);
912
913 for _ in 0..10 {
914 minstrel.handle_tx_result_report(&tx_result);
915 minstrel.handle_timeout();
916 let peer_stats =
917 minstrel.get_fidl_peer_stats(&TEST_MAC_ADDR).expect("Failed to get peer stats");
918 assert_eq!(peer_stats.max_probability, 9);
919 }
920 let peer_stats =
922 minstrel.get_fidl_peer_stats(&TEST_MAC_ADDR).expect("Failed to get peer stats");
923 assert_eq!(peer_stats.max_probability, 9);
924 assert_eq!(peer_stats.max_tp, 9);
925 }
926
927 #[test]
928 fn ht_rates_preferred() {
929 let ht_tx_result_failed = make_tx_result(
930 vec![
931 (HT_START_IDX + 15, 1),
932 (HT_START_IDX + 14, 1),
933 (HT_START_IDX + 13, 1),
934 (HT_START_IDX + 12, 1),
935 (HT_START_IDX + 11, 1),
936 (HT_START_IDX + 10, 1),
937 (HT_START_IDX + 9, 1),
938 (HT_START_IDX + 8, 1),
939 ],
940 false,
941 );
942 let ht_tx_result_success = make_tx_result(
943 vec![
944 (HT_START_IDX + 7, 1),
945 (HT_START_IDX + 6, 1),
946 (HT_START_IDX + 5, 1),
947 (HT_START_IDX + 4, 1),
948 (HT_START_IDX + 3, 1),
949 (HT_START_IDX + 2, 1),
950 (HT_START_IDX + 1, 1),
951 (HT_START_IDX + 0, 9),
954 ],
955 true,
956 );
957 let erp_tx_result_success =
959 make_tx_result(vec![(ERP_START_IDX + ERP_NUM_TX_VECTOR as u16 - 1, 1)], true);
960
961 let (mut minstrel, _timer) = mock_minstrel();
962 minstrel.add_peer(&ht_assoc_cfg()).expect("Failed to add peer.");
963 minstrel.handle_tx_result_report(&ht_tx_result_failed);
964 minstrel.handle_tx_result_report(&ht_tx_result_success);
965 minstrel.handle_tx_result_report(&erp_tx_result_success);
966 minstrel.handle_timeout();
967
968 let peer_stats =
969 minstrel.get_fidl_peer_stats(&TEST_MAC_ADDR).expect("Failed to get peer stats");
970 assert_eq!(peer_stats.max_probability, HT_START_IDX);
972 assert_eq!(peer_stats.max_tp, HT_START_IDX);
973 }
974
975 #[test]
976 fn add_missing_rates() {
977 let (mut minstrel, _timer) = mock_minstrel();
978 let mut assoc_cfg = ht_assoc_cfg();
979 let reduced_supported_rates = vec![2, 4, 11, 22, 12, 18, 24, 36, 48, 72];
981 assoc_cfg.rates = Some(reduced_supported_rates);
982 minstrel.add_peer(&assoc_cfg).expect("Failed to add peer.");
983
984 let rate_108 = ERP_START_IDX + ERP_NUM_TX_VECTOR as u16 - 1; let rate_72 = ERP_START_IDX + ERP_NUM_TX_VECTOR as u16 - 3;
986
987 let peer_stats =
989 minstrel.get_fidl_peer_stats(&TEST_MAC_ADDR).expect("Failed to get peer stats");
990 assert!(!peer_stats.entries.iter().any(|entry| entry.tx_vector_idx == rate_108));
991
992 let tx_result = make_tx_result(vec![(rate_108, 1), (rate_72, 1)], true);
994 minstrel.handle_tx_result_report(&tx_result);
995 let peer_stats =
997 minstrel.get_fidl_peer_stats(&TEST_MAC_ADDR).expect("Failed to get peer stats");
998 assert!(peer_stats.entries.iter().any(|entry| entry.tx_vector_idx == rate_108));
999 }
1000
1001 #[track_caller]
1002 fn expect_probe_order(
1003 minstrel: &mut MinstrelRateSelector<MockTimerManager>,
1004 expected_probes: &[u16],
1005 ) {
1006 let mut probes_iter = expected_probes.iter();
1007 let max_tp =
1008 minstrel.get_fidl_peer_stats(&TEST_MAC_ADDR).expect("Failed to get peer stats").max_tp;
1009 let mut fc = FrameControl(0);
1010 fc.set_frame_type(FrameType::DATA);
1011 let flags = fidl_softmac::WlanTxInfoFlags::empty();
1012
1013 for i in 0..(PROBE_INTERVAL as usize * expected_probes.len()) {
1014 let tx_vec_idx = minstrel.get_tx_vector_idx(&fc, &TEST_MAC_ADDR, flags);
1015 if i % PROBE_INTERVAL as usize == PROBE_INTERVAL as usize - 1 {
1016 assert_eq!(*tx_vec_idx.unwrap(), *probes_iter.next().unwrap());
1018 } else {
1019 assert_eq!(*tx_vec_idx.unwrap(), max_tp);
1020 }
1021 }
1022 }
1023
1024 #[test]
1025 fn expected_probe_order() {
1026 let (mut minstrel, _timer) = mock_minstrel();
1027 minstrel.add_peer(&ht_assoc_cfg()).expect("Failed to add peer.");
1028
1029 const EXPECTED_PROBES: [u16; 22] = [
1032 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 129, 130, 131, 132, 133, 134, 135, ];
1035
1036 expect_probe_order(&mut minstrel, &EXPECTED_PROBES[..]);
1037 }
1038
1039 #[test]
1040 fn skip_seen_probes() {
1041 let (mut minstrel, _timer) = mock_minstrel();
1042 minstrel.add_peer(&ht_assoc_cfg()).expect("Failed to add peer.");
1043 let tx_result = make_tx_result(vec![(16, 1), (15, 1), (14, 1)], true);
1044 minstrel.handle_tx_result_report(&tx_result);
1045 let tx_result = make_tx_result(vec![(13, 1)], true);
1046 minstrel.handle_tx_result_report(&tx_result);
1047
1048 const UPDATED_PROBES: [u16; 19] = [
1050 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 129, 130, 131, 132,
1051 133, 134, 135, ];
1053
1054 expect_probe_order(&mut minstrel, &UPDATED_PROBES[..]);
1055
1056 minstrel.handle_timeout();
1058
1059 const UPDATED_PROBES_2: [u16; 20] = [
1062 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 129, 130, 131, 132,
1063 133, 134, 135, ];
1065
1066 expect_probe_order(&mut minstrel, &UPDATED_PROBES_2[..]);
1067 }
1068
1069 #[test]
1070 fn dead_probe_cycle_count() {
1071 let (mut minstrel, _timer) = mock_minstrel();
1072 minstrel.add_peer(&ht_assoc_cfg()).expect("Failed to add peer.");
1073 let tx_result = make_tx_result(vec![(16, 1), (15, 1), (14, 1)], true);
1074 minstrel.handle_tx_result_report(&tx_result);
1075 minstrel.handle_timeout();
1076
1077 const EXPECTED_PROBES: [u16; 20] = [
1079 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 129, 130, 131, 132,
1080 133, 134, 135, ];
1082 expect_probe_order(&mut minstrel, &EXPECTED_PROBES[..]);
1083
1084 for _ in 0..DEAD_PROBE_CYCLE_COUNT as usize {
1085 minstrel.outdated_peers.insert(*TEST_MAC_ADDR);
1087 minstrel.handle_timeout();
1088 }
1089
1090 const EXPECT_DEAD_PROBES: [u16; 22] = [
1092 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 129, 130, 131, 132,
1093 133, 134, 135, ];
1095 expect_probe_order(&mut minstrel, &EXPECT_DEAD_PROBES[..]);
1096 }
1097
1098 #[test]
1099 fn max_slow_probe() {
1100 let (mut minstrel, _timer) = mock_minstrel();
1101 minstrel.add_peer(&ht_assoc_cfg()).expect("Failed to add peer.");
1102 minstrel.handle_tx_result_report(&make_tx_result(vec![(16, 1)], true));
1104 minstrel.handle_timeout();
1105
1106 const EXPECTED_PROBES: [u16; 22] = [
1107 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 129, 130, 131, 132,
1108 133, 134, 135, ];
1110 for _ in 0..MAX_SLOW_PROBE {
1111 expect_probe_order(&mut minstrel, &EXPECTED_PROBES[..]);
1113 minstrel.handle_tx_result_report(&make_tx_result(vec![(1, 1)], true));
1114 }
1115
1116 const NEW_EXPECTED_PROBES: [u16; 21] = [
1118 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 129, 130,
1119 131, 132, 133, 134, 135, ];
1121 expect_probe_order(&mut minstrel, &NEW_EXPECTED_PROBES[..]);
1122
1123 minstrel.handle_timeout();
1125 expect_probe_order(&mut minstrel, &EXPECTED_PROBES[..]);
1126 }
1127
1128 #[track_caller]
1129 fn assert_data_rate(
1130 channel_bandwidth: fidl_ieee80211::ChannelBandwidth,
1131 gi: WlanGi,
1132 relative_mcs_idx: u8,
1133 expected_mbit_per_second: f64,
1134 ) {
1135 let tx_time = tx_time_ht(channel_bandwidth, gi, relative_mcs_idx);
1136 const BYTES_PER_MBIT: f64 = 125000.0;
1137 let mut expected_tx_time =
1138 (MINSTREL_FRAME_LENGTH as f64 / BYTES_PER_MBIT) / expected_mbit_per_second;
1139 if gi == WlanGi::G_400NS {
1140 expected_tx_time += Duration::from_nanos(800).as_secs_f64();
1142 }
1143 let actual_tx_time = tx_time.as_secs_f64();
1144 let ratio = expected_tx_time / actual_tx_time;
1145 assert!(ratio < 1.01 && ratio > 0.99);
1146 }
1147
1148 #[test]
1149 fn tx_time_ht_approx_values_cbw20() {
1150 assert_data_rate(fidl_ieee80211::ChannelBandwidth::Cbw20, WlanGi::G_800NS, 0, 6.5);
1152 assert_data_rate(fidl_ieee80211::ChannelBandwidth::Cbw20, WlanGi::G_400NS, 0, 7.2);
1153 assert_data_rate(fidl_ieee80211::ChannelBandwidth::Cbw20, WlanGi::G_800NS, 8, 13.0);
1154 assert_data_rate(fidl_ieee80211::ChannelBandwidth::Cbw20, WlanGi::G_400NS, 8, 14.4);
1155 assert_data_rate(fidl_ieee80211::ChannelBandwidth::Cbw20, WlanGi::G_800NS, 31, 260.0);
1156 assert_data_rate(fidl_ieee80211::ChannelBandwidth::Cbw20, WlanGi::G_400NS, 31, 288.9);
1157 }
1158
1159 #[test]
1160 fn tx_time_ht_approx_values_cbw40() {
1161 assert_data_rate(fidl_ieee80211::ChannelBandwidth::Cbw40, WlanGi::G_800NS, 0, 13.5);
1163 assert_data_rate(fidl_ieee80211::ChannelBandwidth::Cbw40, WlanGi::G_400NS, 0, 15.0);
1164 assert_data_rate(fidl_ieee80211::ChannelBandwidth::Cbw40, WlanGi::G_800NS, 8, 27.0);
1165 assert_data_rate(fidl_ieee80211::ChannelBandwidth::Cbw40, WlanGi::G_400NS, 8, 30.0);
1166 assert_data_rate(fidl_ieee80211::ChannelBandwidth::Cbw40, WlanGi::G_800NS, 31, 540.0);
1167 assert_data_rate(fidl_ieee80211::ChannelBandwidth::Cbw40, WlanGi::G_400NS, 31, 600.0);
1168 }
1169}