1use crate::common::mac::WlanGi;
6use crate::probe_sequence::{ProbeEntry, ProbeSequence};
7use ieee80211::{MacAddr, MacAddrBytes};
8use log::{debug, error};
9use std::collections::{hash_map, HashMap, HashSet};
10use std::time::Duration;
11use wlan_common::ie::{HtCapabilities, RxMcsBitmask, SupportedRate};
12use wlan_common::mac::FrameControl;
13use wlan_common::tx_vector::{
14 TxVecIdx, TxVector, ERP_NUM_TX_VECTOR, ERP_START_IDX, HT_NUM_MCS, HT_NUM_UNIQUE_MCS,
15};
16use {
17 fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_minstrel as fidl_minstrel,
18 fidl_fuchsia_wlan_softmac as fidl_softmac,
19};
20
21const ASSOC_CHAN_WIDTH: fidl_common::ChannelBandwidth = fidl_common::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_common::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_common::ChannelBandwidth::Cbw20,
230 WlanGi::G_800NS,
231 mcs_set.rx_mcs(),
232 );
233 if sgi_20 {
234 self.add_supported_ht(
235 fidl_common::ChannelBandwidth::Cbw20,
236 WlanGi::G_400NS,
237 mcs_set.rx_mcs(),
238 );
239 }
240 if ASSOC_CHAN_WIDTH == fidl_common::ChannelBandwidth::Cbw40 {
241 self.add_supported_ht(
242 fidl_common::ChannelBandwidth::Cbw40,
243 WlanGi::G_800NS,
244 mcs_set.rx_mcs(),
245 );
246 if sgi_40 {
247 self.add_supported_ht(
248 fidl_common::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_common::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() {
305 Some(tx_vector_idx)
306 } else {
307 None
308 }
309 })
310 .collect();
311 debug!("{} ERP added.", tx_stats_added);
312 if basic_rates.is_empty() {
313 vec![TxVecIdx::new(ERP_START_IDX).unwrap()].into_iter().collect()
314 } else {
315 basic_rates
316 }
317 }
318
319 fn update_stats(&mut self) {
320 for tx_stats in self.tx_stats_map.values_mut() {
322 if tx_stats.attempts_cur != 0 {
323 let probability = tx_stats.success_cur as f32 / tx_stats.attempts_cur as f32;
324 if tx_stats.attempts_total == 0 {
325 tx_stats.moving_avg_probability = probability;
326 } else {
327 tx_stats.moving_avg_probability = tx_stats.moving_avg_probability
328 * MINSTREL_EXP_WEIGHT
329 + probability * (1.0 - MINSTREL_EXP_WEIGHT);
330 }
331 tx_stats.attempts_total += tx_stats.attempts_cur;
332 tx_stats.success_total += tx_stats.success_cur;
333 tx_stats.attempts_cur = 0;
334 tx_stats.success_cur = 0;
335 tx_stats.probe_cycles_skipped = 0;
336 } else {
337 tx_stats.probe_cycles_skipped = tx_stats.probe_cycles_skipped.saturating_add(1);
338 }
339 const NANOS_PER_SECOND: f32 = 1e9;
340 tx_stats.expected_throughput = NANOS_PER_SECOND
342 / tx_stats.perfect_tx_time.as_nanos() as f32
343 * tx_stats.moving_avg_probability;
344 }
345
346 let arbitrary_rate = match self.tx_stats_map.iter().next() {
348 Some((tx_vec_idx, _)) => *tx_vec_idx,
349 None => return, };
351
352 let mut best_expected_throughput = arbitrary_rate;
354 let mut best_for_reliability = arbitrary_rate;
355 let mut best_erp_for_reliability = self.highest_erp_rate;
356 for (tx_vector_idx, tx_stats) in &self.tx_stats_map {
357 let best_throughput_stats = self.tx_stats_map.get(&best_expected_throughput).unwrap();
360 let best_reliability_stats = self.tx_stats_map.get(&best_for_reliability).unwrap();
361
362 if (!tx_unlikely(tx_stats)
365 && tx_stats.phy_type_strictly_preferred_over(best_throughput_stats))
366 || (tx_stats.better_for_expected_throughput_than(best_throughput_stats)
367 && !(!tx_unlikely(best_throughput_stats)
368 && best_throughput_stats.phy_type_strictly_preferred_over(tx_stats)))
369 {
370 best_expected_throughput = *tx_vector_idx;
371 }
372 if (!tx_unlikely(tx_stats)
375 && tx_stats.phy_type_strictly_preferred_over(best_reliability_stats))
376 || (tx_stats.better_for_reliable_transmission_than(best_reliability_stats)
377 && !(!tx_unlikely(best_reliability_stats)
378 && best_reliability_stats.phy_type_strictly_preferred_over(tx_stats)))
379 {
380 best_for_reliability = *tx_vector_idx;
381 }
382 if let Some(best_erp_for_reliability) = best_erp_for_reliability.as_mut() {
383 let best_erp_reliability_stats =
384 self.tx_stats_map.get(best_erp_for_reliability).unwrap();
385 if self.erp_rates.contains(tx_vector_idx)
386 && tx_stats.better_for_reliable_transmission_than(best_erp_reliability_stats)
387 {
388 *best_erp_for_reliability = *tx_vector_idx;
389 }
390 }
391 }
392 self.best_expected_throughput = Some(best_expected_throughput);
393 self.best_for_reliability = Some(best_for_reliability);
394 self.best_erp_for_reliability = best_erp_for_reliability;
395 }
396
397 fn get_tx_vector_idx(
398 &mut self,
399 needs_reliability: bool,
400 probe_sequence: &ProbeSequence,
401 ) -> Option<TxVecIdx> {
402 if needs_reliability {
403 self.best_for_reliability
404 } else if self.num_pkt_until_next_probe > 0 {
405 self.num_pkt_until_next_probe -= 1;
406 self.best_expected_throughput
407 } else {
408 self.num_pkt_until_next_probe = PROBE_INTERVAL - 1;
409 self.get_next_probe(probe_sequence)
410 }
411 }
412
413 fn get_next_probe(&mut self, probe_sequence: &ProbeSequence) -> Option<TxVecIdx> {
414 let slow_probe_cutoff = self.tx_stats_map.get(&self.best_for_reliability?)?.perfect_tx_time;
417 if self.tx_stats_map.len() == 1 {
418 return self.best_expected_throughput;
419 }
420 for _ in 0..self.tx_stats_map.len() {
422 let probe_idx = self.next_supported_probe_idx(probe_sequence);
423 let tx_stats = self.tx_stats_map.get_mut(&probe_idx).unwrap();
424 if Some(probe_idx) == self.best_erp_for_reliability.or(self.highest_erp_rate)
427 || Some(probe_idx) == self.best_expected_throughput.or(self.best_for_reliability)
428 || tx_stats.attempts_cur > self.num_probe_cycles_done
430 || (tx_stats.perfect_tx_time > slow_probe_cutoff
432 && tx_stats.attempts_cur >= MAX_SLOW_PROBE)
433 || (tx_unlikely(tx_stats)
435 && (tx_stats.probe_cycles_skipped < DEAD_PROBE_CYCLE_COUNT
436 || tx_stats.attempts_cur > 0))
437 {
438 continue;
439 }
440 self.probes_total += 1;
441 tx_stats.probes_total += 1;
442 return Some(probe_idx);
443 }
444 return self.best_expected_throughput;
445 }
446
447 fn next_supported_probe_idx(&mut self, probe_sequence: &ProbeSequence) -> TxVecIdx {
449 assert!(
450 !self.tx_stats_map.is_empty(),
451 "Cannot call next_supported_probe_idx with empty tx_stats_map"
452 );
453
454 loop {
455 let idx = probe_sequence.next(&mut self.probe_entry);
456 if self.probe_entry.cycle_complete() {
457 self.num_probe_cycles_done += 1;
458 }
459 if self.tx_stats_map.contains_key(&idx) {
460 return idx;
461 }
462 }
463 }
464}
465
466fn tx_unlikely(tx_stats: &TxStats) -> bool {
467 tx_stats.moving_avg_probability < 1.0 - MINSTREL_PROBABILITY_THRESHOLD
468}
469
470pub trait TimerManager {
471 fn schedule(&mut self, from_now: Duration);
472 fn cancel(&mut self);
473}
474
475pub struct MinstrelRateSelector<T: TimerManager> {
483 timer_manager: T,
484 update_interval: Duration,
485 probe_sequence: ProbeSequence,
486 peer_map: HashMap<MacAddr, Peer>,
487 outdated_peers: HashSet<MacAddr>,
488}
489
490impl<T: TimerManager> MinstrelRateSelector<T> {
491 pub fn new(timer_manager: T, update_interval: Duration, probe_sequence: ProbeSequence) -> Self {
492 Self {
493 timer_manager,
494 update_interval,
495 probe_sequence,
496 peer_map: Default::default(),
497 outdated_peers: Default::default(),
498 }
499 }
500
501 pub fn add_peer(
502 &mut self,
503 assoc_cfg: &fidl_softmac::WlanAssociationConfig,
504 ) -> Result<(), zx::Status> {
505 let bssid: MacAddr = match assoc_cfg.bssid {
506 None => {
507 error!("Attempted to add peer with no BSSID.");
508 return Err(zx::Status::INTERNAL);
509 }
510 Some(bssid) => bssid.into(),
511 };
512 if self.peer_map.contains_key(&bssid) {
513 error!("Attempted to add peer {} twice.", &bssid);
514 } else {
515 let mut peer = Peer::from_assoc_cfg(assoc_cfg)?;
516 if self.peer_map.is_empty() {
517 self.timer_manager.schedule(self.update_interval);
518 }
519 peer.update_stats();
520 self.peer_map.insert(bssid, peer);
521 }
522 Ok(())
523 }
524
525 pub fn remove_peer(&mut self, addr: &MacAddr) {
526 self.outdated_peers.remove(addr);
527 match self.peer_map.remove(addr) {
528 Some(_) => debug!("Peer {} removed.", addr),
529 None => debug!("Cannot remove peer {}, not found.", addr),
530 }
531 if self.peer_map.is_empty() {
532 self.timer_manager.cancel();
533 }
534 }
535
536 pub fn handle_tx_result_report(&mut self, tx_result: &fidl_common::WlanTxResult) {
537 let peer_addr: MacAddr = tx_result.peer_addr.into();
538 match self.peer_map.get_mut(&peer_addr) {
539 Some(peer) => {
540 peer.handle_tx_result_report(tx_result);
541 self.outdated_peers.insert(peer_addr);
542 }
543 None => {
544 debug!("Peer {} received tx status report after it was removed.", peer_addr);
545 }
546 }
547 }
548
549 fn update_stats(&mut self) {
550 for outdated_peer in self.outdated_peers.drain() {
551 self.peer_map.get_mut(&outdated_peer).map(|peer| peer.update_stats());
552 }
553 }
554
555 pub fn handle_timeout(&mut self) {
556 self.timer_manager.schedule(self.update_interval);
558 self.update_stats();
559 }
560
561 pub fn get_tx_vector_idx(
562 &mut self,
563 frame_control: &FrameControl,
564 peer_addr: &MacAddr,
565 flags: fidl_softmac::WlanTxInfoFlags,
566 ) -> Option<TxVecIdx> {
567 match self.peer_map.get_mut(peer_addr) {
568 None => TxVecIdx::new(ERP_START_IDX + ERP_NUM_TX_VECTOR as u16 - 1),
569 Some(peer) => {
570 if frame_control.is_data() {
571 let needs_reliability =
572 flags.contains(fidl_softmac::WlanTxInfoFlags::FAVOR_RELIABILITY);
573 peer.get_tx_vector_idx(needs_reliability, &self.probe_sequence)
574 } else {
575 peer.best_erp_for_reliability
576 }
577 }
578 }
579 }
580
581 pub fn get_fidl_peers(&self) -> fidl_minstrel::Peers {
582 fidl_minstrel::Peers {
583 addrs: self.peer_map.iter().map(|(peer, _)| peer.to_array()).collect(),
584 }
585 }
586
587 pub fn get_fidl_peer_stats(
588 &self,
589 peer_addr: &MacAddr,
590 ) -> Result<fidl_minstrel::Peer, zx::Status> {
591 let peer = self.peer_map.get(peer_addr).ok_or(zx::Status::NOT_FOUND)?;
592 Ok(fidl_minstrel::Peer {
593 addr: peer_addr.to_array(),
594 max_tp: tx_vec_idx_opt_to_u16(&peer.best_expected_throughput),
595 max_probability: tx_vec_idx_opt_to_u16(&peer.best_for_reliability),
596 basic_highest: tx_vec_idx_opt_to_u16(&peer.highest_erp_rate),
597 basic_max_probability: tx_vec_idx_opt_to_u16(&peer.best_erp_for_reliability),
598 probes: peer.probes_total,
599 entries: peer.tx_stats_map.iter().map(|(_, entry)| entry.into()).collect(),
600 })
601 }
602
603 pub fn is_active(&self) -> bool {
604 !self.peer_map.is_empty()
605 }
606}
607
608fn tx_vec_idx_opt_to_u16(tx_vec_idx: &Option<TxVecIdx>) -> u16 {
609 match tx_vec_idx {
610 Some(idx) => **idx,
611 None => 0,
612 }
613}
614
615fn tx_time_ht(
616 channel_bandwidth: fidl_common::ChannelBandwidth,
617 gi: WlanGi,
618 relative_mcs_idx: u8,
619) -> Duration {
620 header_tx_time_ht() + payload_tx_time_ht(channel_bandwidth, gi, relative_mcs_idx)
621}
622
623fn header_tx_time_ht() -> Duration {
624 Duration::ZERO
626}
627
628fn payload_tx_time_ht(
634 channel_bandwidth: fidl_common::ChannelBandwidth,
635 gi: WlanGi,
636 mcs_idx: u8,
637) -> Duration {
638 const BITS_PER_SYMBOL_LIST: [u16; HT_NUM_UNIQUE_MCS as usize + 2] =
641 [26, 52, 78, 104, 156, 208, 234, 260, 312, 347];
642 const DATA_SUB_CARRIERS_20: u16 = 52;
645 const DATA_SUB_CARRIERS_40: u16 = 108;
647 let nss = 1 + mcs_idx / HT_NUM_UNIQUE_MCS;
650 let relative_mcs_idx = mcs_idx % HT_NUM_UNIQUE_MCS;
651 let bits_per_symbol = if channel_bandwidth == fidl_common::ChannelBandwidth::Cbw40 {
652 BITS_PER_SYMBOL_LIST[relative_mcs_idx as usize] * DATA_SUB_CARRIERS_40
653 / DATA_SUB_CARRIERS_20
654 } else {
655 BITS_PER_SYMBOL_LIST[relative_mcs_idx as usize]
656 };
657
658 const TX_TIME_PER_SYMBOL_GI_800: Duration = Duration::from_nanos(4000);
659 const TX_TIME_PER_SYMBOL_GI_400: Duration = Duration::from_nanos(3600);
660 const TX_TIME_PADDING_GI_400: Duration = Duration::from_nanos(800);
661
662 match gi {
664 WlanGi::G_400NS => {
665 TX_TIME_PADDING_GI_400
666 + (TX_TIME_PER_SYMBOL_GI_400 * 8 * MINSTREL_FRAME_LENGTH)
667 / (nss as u32 * bits_per_symbol as u32)
668 }
669 WlanGi::G_800NS => {
670 (TX_TIME_PER_SYMBOL_GI_800 * 8 * MINSTREL_FRAME_LENGTH)
671 / (nss as u32 * bits_per_symbol as u32)
672 }
673 _ => panic!("payload_tx_time_ht is invalid for non-ht phy"),
674 }
675}
676
677fn tx_time_erp(rate: &SupportedRate) -> Duration {
678 header_tx_time_erp() + payload_tx_time_erp(rate)
679}
680
681fn header_tx_time_erp() -> Duration {
682 Duration::ZERO
684}
685
686fn payload_tx_time_erp(rate: &SupportedRate) -> Duration {
687 let bits_per_symbol = rate.rate() * 2;
690 const TX_TIME_PER_SYMBOL: Duration = Duration::from_nanos(4000);
691 TX_TIME_PER_SYMBOL * 8 * MINSTREL_FRAME_LENGTH / bits_per_symbol as u32
692}
693
694fn erp_idx_stats(tx_vector_idx: TxVecIdx, rate: SupportedRate) -> TxStats {
695 let perfect_tx_time = tx_time_erp(&rate);
696 TxStats { perfect_tx_time, ..TxStats::new(tx_vector_idx) }
697}
698
699#[cfg(test)]
700mod tests {
701 use super::*;
702 use fidl_fuchsia_wlan_common as fidl_common;
703 use lazy_static::lazy_static;
704 use std::sync::{Arc, Mutex};
705 use wlan_common::ie::{ChanWidthSet, HtCapabilityInfo};
706 use wlan_common::mac::FrameType;
707 use wlan_common::tx_vector::HT_START_IDX;
708
709 struct MockTimerManager {
710 scheduled: Arc<Mutex<Option<Duration>>>,
711 }
712
713 impl TimerManager for MockTimerManager {
714 fn schedule(&mut self, from_now: Duration) {
715 let mut scheduled = self.scheduled.lock().unwrap();
716 scheduled.replace(from_now);
717 }
718 fn cancel(&mut self) {
719 let mut scheduled = self.scheduled.lock().unwrap();
720 scheduled.take();
721 }
722 }
723
724 fn mock_minstrel() -> (MinstrelRateSelector<MockTimerManager>, Arc<Mutex<Option<Duration>>>) {
725 let timer = Arc::new(Mutex::new(None));
726 let timer_manager = MockTimerManager { scheduled: timer.clone() };
727 let update_interval = Duration::from_micros(100);
728 let probe_sequence = ProbeSequence::sequential();
729 (MinstrelRateSelector::new(timer_manager, update_interval, probe_sequence), timer)
730 }
731
732 lazy_static! {
733 static ref TEST_MAC_ADDR: MacAddr = MacAddr::from([50, 53, 51, 56, 55, 52]);
734 }
735
736 const BASIC_RATE_BIT: u8 = 0b10000000;
737
738 fn ht_assoc_cfg() -> fidl_softmac::WlanAssociationConfig {
739 let mut ht_cap = wlan_common::ie::fake_ht_capabilities();
740 let mut ht_cap_info = HtCapabilityInfo(0);
741 ht_cap_info.set_short_gi_40(true);
742 ht_cap_info.set_short_gi_20(true);
743 ht_cap_info.set_chan_width_set(ChanWidthSet::TWENTY_FORTY);
744 ht_cap.ht_cap_info = ht_cap_info;
745 ht_cap.mcs_set.0 = 0xffff; fidl_softmac::WlanAssociationConfig {
748 bssid: Some(TEST_MAC_ADDR.to_array()),
749 aid: Some(42),
750 listen_interval: Some(0),
751 channel: Some(fidl_common::WlanChannel {
752 primary: 149,
753 cbw: fidl_common::ChannelBandwidth::Cbw40,
754 secondary80: 0,
755 }),
756 qos: Some(true),
757 wmm_params: None,
758 rates: Some(vec![
759 2,
760 4,
761 11,
762 22,
763 12 | BASIC_RATE_BIT,
764 18,
765 24,
766 36,
767 48,
768 72,
769 96,
770 108 | BASIC_RATE_BIT,
771 ]),
772 capability_info: Some(0),
773 ht_cap: Some(ht_cap.into()),
774 ht_op: None,
775 vht_cap: None,
776 vht_op: None,
777 ..Default::default()
778 }
779 }
780
781 #[test]
782 fn peer_from_assoc_cfg() {
783 let assoc_cfg = ht_assoc_cfg();
784 let peer = Peer::from_assoc_cfg(&assoc_cfg)
785 .expect("Failed to convert WlanAssociationConfig into Peer.");
786 assert_eq!(peer.addr, assoc_cfg.bssid.unwrap().into());
787 assert_eq!(peer.tx_stats_map.len(), 24);
788 let mut peer_rates = peer
789 .tx_stats_map
790 .keys()
791 .into_iter()
792 .map(|tx_vector_idx| **tx_vector_idx)
793 .collect::<Vec<u16>>();
794 peer_rates.sort();
795 assert_eq!(
796 peer_rates,
797 vec![
798 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 129, 130, 131, 132, 133, 134, 135, 136, ]
801 );
802 let mut peer_erp_rates =
803 peer.erp_rates.iter().map(|tx_vector_idx| **tx_vector_idx).collect::<Vec<u16>>();
804 peer_erp_rates.sort();
805 let expected_basic_rate_1 =
806 TxVector::from_supported_rate(&SupportedRate(12 | BASIC_RATE_BIT)).unwrap().to_idx();
807 let expected_basic_rate_2 =
808 TxVector::from_supported_rate(&SupportedRate(108 | BASIC_RATE_BIT)).unwrap().to_idx();
809 assert_eq!(peer_erp_rates, vec![*expected_basic_rate_1, *expected_basic_rate_2]);
810 assert_eq!(peer.highest_erp_rate, TxVecIdx::new(136));
811 }
812
813 #[test]
814 fn add_peer() {
815 let (mut minstrel, timer) = mock_minstrel();
816 assert!(timer.lock().unwrap().is_none()); minstrel.add_peer(&ht_assoc_cfg()).expect("Failed to add peer.");
818 assert!(timer.lock().unwrap().is_some()); let peers = minstrel.get_fidl_peers();
821 assert_eq!(peers.addrs.len(), 1);
822
823 let peer_addr: MacAddr = {
824 let mut peer_addr = [0u8; 6];
825 peer_addr.copy_from_slice(&peers.addrs[0][..]);
826 peer_addr.into()
827 };
828 let peer_stats =
829 minstrel.get_fidl_peer_stats(&peer_addr).expect("Failed to get peer stats");
830 assert_eq!(&peer_stats.addr, TEST_MAC_ADDR.as_array());
831 assert_eq!(peer_stats.entries.len(), 24);
833 assert_eq!(peer_stats.max_tp, 16); assert_eq!(peer_stats.basic_highest, ERP_START_IDX + ERP_NUM_TX_VECTOR as u16 - 1);
835 assert_eq!(peer_stats.basic_max_probability, ERP_START_IDX + ERP_NUM_TX_VECTOR as u16 - 1);
836 }
837
838 #[test]
839 fn remove_peer() {
840 let (mut minstrel, timer) = mock_minstrel();
841 minstrel.add_peer(&ht_assoc_cfg()).expect("Failed to add peer.");
842 assert_eq!(minstrel.get_fidl_peers().addrs.len(), 1);
843 assert!(timer.lock().unwrap().is_some()); minstrel.remove_peer(&TEST_MAC_ADDR);
846 assert!(timer.lock().unwrap().is_none()); assert!(minstrel.get_fidl_peers().addrs.is_empty());
849 assert_eq!(minstrel.get_fidl_peer_stats(&TEST_MAC_ADDR), Err(zx::Status::NOT_FOUND));
850 }
851
852 #[test]
853 fn remove_second_peer() {
854 let (mut minstrel, timer) = mock_minstrel();
855 minstrel.add_peer(&ht_assoc_cfg()).expect("Failed to add peer.");
856 let mut peer2 = ht_assoc_cfg();
857 peer2.bssid = Some([11, 12, 13, 14, 15, 16]);
858 minstrel.add_peer(&peer2).expect("Failed to add peer.");
859 assert_eq!(minstrel.get_fidl_peers().addrs.len(), 2);
860 assert!(timer.lock().unwrap().is_some()); minstrel.remove_peer(&TEST_MAC_ADDR);
863 assert_eq!(minstrel.get_fidl_peers().addrs.len(), 1);
864 assert!(timer.lock().unwrap().is_some()); assert_eq!(minstrel.get_fidl_peer_stats(&TEST_MAC_ADDR), Err(zx::Status::NOT_FOUND));
867 assert!(minstrel.get_fidl_peer_stats(&peer2.bssid.unwrap().into()).is_ok());
868 }
869
870 fn make_tx_result(entries: Vec<(u16, u8)>, success: bool) -> fidl_common::WlanTxResult {
872 assert!(entries.len() <= 8);
873 let mut tx_result_entry =
874 [fidl_common::WlanTxResultEntry { tx_vector_idx: 0, attempts: 0 }; 8];
875 tx_result_entry[0..entries.len()].copy_from_slice(
876 &entries
877 .into_iter()
878 .map(|(tx_vector_idx, attempts)| fidl_common::WlanTxResultEntry {
879 tx_vector_idx,
880 attempts,
881 })
882 .collect::<Vec<fidl_common::WlanTxResultEntry>>()[..],
883 );
884 let result_code = if success {
885 fidl_common::WlanTxResultCode::Success
886 } else {
887 fidl_common::WlanTxResultCode::Failed
888 };
889 fidl_common::WlanTxResult {
890 tx_result_entry,
891 peer_addr: TEST_MAC_ADDR.to_array(),
892 result_code,
893 }
894 }
895
896 #[test]
897 fn handle_tx_result_reports() {
898 let tx_result = make_tx_result(vec![(16, 1), (15, 1), (14, 1), (13, 1)], true);
900
901 let (mut minstrel, _timer) = mock_minstrel();
902 minstrel.add_peer(&ht_assoc_cfg()).expect("Failed to add peer.");
903 minstrel.handle_tx_result_report(&tx_result);
904
905 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, 16);
909
910 minstrel.handle_timeout();
911 let peer_stats =
912 minstrel.get_fidl_peer_stats(&TEST_MAC_ADDR).expect("Failed to get peer stats");
913 assert_eq!(peer_stats.max_tp, 13);
914 assert_eq!(peer_stats.max_probability, 13);
915
916 let tx_result = make_tx_result(vec![(13, 1), (9, 1)], true);
917
918 for _ in 0..10 {
919 minstrel.handle_tx_result_report(&tx_result);
920 minstrel.handle_timeout();
921 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 }
925 let peer_stats =
927 minstrel.get_fidl_peer_stats(&TEST_MAC_ADDR).expect("Failed to get peer stats");
928 assert_eq!(peer_stats.max_probability, 9);
929 assert_eq!(peer_stats.max_tp, 9);
930 }
931
932 #[test]
933 fn ht_rates_preferred() {
934 let ht_tx_result_failed = make_tx_result(
935 vec![
936 (HT_START_IDX + 15, 1),
937 (HT_START_IDX + 14, 1),
938 (HT_START_IDX + 13, 1),
939 (HT_START_IDX + 12, 1),
940 (HT_START_IDX + 11, 1),
941 (HT_START_IDX + 10, 1),
942 (HT_START_IDX + 9, 1),
943 (HT_START_IDX + 8, 1),
944 ],
945 false,
946 );
947 let ht_tx_result_success = make_tx_result(
948 vec![
949 (HT_START_IDX + 7, 1),
950 (HT_START_IDX + 6, 1),
951 (HT_START_IDX + 5, 1),
952 (HT_START_IDX + 4, 1),
953 (HT_START_IDX + 3, 1),
954 (HT_START_IDX + 2, 1),
955 (HT_START_IDX + 1, 1),
956 (HT_START_IDX + 0, 9),
959 ],
960 true,
961 );
962 let erp_tx_result_success =
964 make_tx_result(vec![(ERP_START_IDX + ERP_NUM_TX_VECTOR as u16 - 1, 1)], true);
965
966 let (mut minstrel, _timer) = mock_minstrel();
967 minstrel.add_peer(&ht_assoc_cfg()).expect("Failed to add peer.");
968 minstrel.handle_tx_result_report(&ht_tx_result_failed);
969 minstrel.handle_tx_result_report(&ht_tx_result_success);
970 minstrel.handle_tx_result_report(&erp_tx_result_success);
971 minstrel.handle_timeout();
972
973 let peer_stats =
974 minstrel.get_fidl_peer_stats(&TEST_MAC_ADDR).expect("Failed to get peer stats");
975 assert_eq!(peer_stats.max_probability, HT_START_IDX);
977 assert_eq!(peer_stats.max_tp, HT_START_IDX);
978 }
979
980 #[test]
981 fn add_missing_rates() {
982 let (mut minstrel, _timer) = mock_minstrel();
983 let mut assoc_cfg = ht_assoc_cfg();
984 let reduced_supported_rates = vec![2, 4, 11, 22, 12, 18, 24, 36, 48, 72];
986 assoc_cfg.rates = Some(reduced_supported_rates);
987 minstrel.add_peer(&assoc_cfg).expect("Failed to add peer.");
988
989 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;
991
992 let peer_stats =
994 minstrel.get_fidl_peer_stats(&TEST_MAC_ADDR).expect("Failed to get peer stats");
995 assert!(!peer_stats.entries.iter().any(|entry| entry.tx_vector_idx == rate_108));
996
997 let tx_result = make_tx_result(vec![(rate_108, 1), (rate_72, 1)], true);
999 minstrel.handle_tx_result_report(&tx_result);
1000 let peer_stats =
1002 minstrel.get_fidl_peer_stats(&TEST_MAC_ADDR).expect("Failed to get peer stats");
1003 assert!(peer_stats.entries.iter().any(|entry| entry.tx_vector_idx == rate_108));
1004 }
1005
1006 #[track_caller]
1007 fn expect_probe_order(
1008 minstrel: &mut MinstrelRateSelector<MockTimerManager>,
1009 expected_probes: &[u16],
1010 ) {
1011 let mut probes_iter = expected_probes.iter();
1012 let max_tp =
1013 minstrel.get_fidl_peer_stats(&TEST_MAC_ADDR).expect("Failed to get peer stats").max_tp;
1014 let mut fc = FrameControl(0);
1015 fc.set_frame_type(FrameType::DATA);
1016 let flags = fidl_softmac::WlanTxInfoFlags::empty();
1017
1018 for i in 0..(PROBE_INTERVAL as usize * expected_probes.len()) {
1019 let tx_vec_idx = minstrel.get_tx_vector_idx(&fc, &TEST_MAC_ADDR, flags);
1020 if i % PROBE_INTERVAL as usize == PROBE_INTERVAL as usize - 1 {
1021 assert_eq!(*tx_vec_idx.unwrap(), *probes_iter.next().unwrap());
1023 } else {
1024 assert_eq!(*tx_vec_idx.unwrap(), max_tp);
1025 }
1026 }
1027 }
1028
1029 #[test]
1030 fn expected_probe_order() {
1031 let (mut minstrel, _timer) = mock_minstrel();
1032 minstrel.add_peer(&ht_assoc_cfg()).expect("Failed to add peer.");
1033
1034 const EXPECTED_PROBES: [u16; 22] = [
1037 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 129, 130, 131, 132, 133, 134, 135, ];
1040
1041 expect_probe_order(&mut minstrel, &EXPECTED_PROBES[..]);
1042 }
1043
1044 #[test]
1045 fn skip_seen_probes() {
1046 let (mut minstrel, _timer) = mock_minstrel();
1047 minstrel.add_peer(&ht_assoc_cfg()).expect("Failed to add peer.");
1048 let tx_result = make_tx_result(vec![(16, 1), (15, 1), (14, 1)], true);
1049 minstrel.handle_tx_result_report(&tx_result);
1050 let tx_result = make_tx_result(vec![(13, 1)], true);
1051 minstrel.handle_tx_result_report(&tx_result);
1052
1053 const UPDATED_PROBES: [u16; 19] = [
1055 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 129, 130, 131, 132,
1056 133, 134, 135, ];
1058
1059 expect_probe_order(&mut minstrel, &UPDATED_PROBES[..]);
1060
1061 minstrel.handle_timeout();
1063
1064 const UPDATED_PROBES_2: [u16; 20] = [
1067 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 129, 130, 131, 132,
1068 133, 134, 135, ];
1070
1071 expect_probe_order(&mut minstrel, &UPDATED_PROBES_2[..]);
1072 }
1073
1074 #[test]
1075 fn dead_probe_cycle_count() {
1076 let (mut minstrel, _timer) = mock_minstrel();
1077 minstrel.add_peer(&ht_assoc_cfg()).expect("Failed to add peer.");
1078 let tx_result = make_tx_result(vec![(16, 1), (15, 1), (14, 1)], true);
1079 minstrel.handle_tx_result_report(&tx_result);
1080 minstrel.handle_timeout();
1081
1082 const EXPECTED_PROBES: [u16; 20] = [
1084 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 129, 130, 131, 132,
1085 133, 134, 135, ];
1087 expect_probe_order(&mut minstrel, &EXPECTED_PROBES[..]);
1088
1089 for _ in 0..DEAD_PROBE_CYCLE_COUNT as usize {
1090 minstrel.outdated_peers.insert(*TEST_MAC_ADDR);
1092 minstrel.handle_timeout();
1093 }
1094
1095 const EXPECT_DEAD_PROBES: [u16; 22] = [
1097 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 129, 130, 131, 132,
1098 133, 134, 135, ];
1100 expect_probe_order(&mut minstrel, &EXPECT_DEAD_PROBES[..]);
1101 }
1102
1103 #[test]
1104 fn max_slow_probe() {
1105 let (mut minstrel, _timer) = mock_minstrel();
1106 minstrel.add_peer(&ht_assoc_cfg()).expect("Failed to add peer.");
1107 minstrel.handle_tx_result_report(&make_tx_result(vec![(16, 1)], true));
1109 minstrel.handle_timeout();
1110
1111 const EXPECTED_PROBES: [u16; 22] = [
1112 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 129, 130, 131, 132,
1113 133, 134, 135, ];
1115 for _ in 0..MAX_SLOW_PROBE {
1116 expect_probe_order(&mut minstrel, &EXPECTED_PROBES[..]);
1118 minstrel.handle_tx_result_report(&make_tx_result(vec![(1, 1)], true));
1119 }
1120
1121 const NEW_EXPECTED_PROBES: [u16; 21] = [
1123 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 129, 130,
1124 131, 132, 133, 134, 135, ];
1126 expect_probe_order(&mut minstrel, &NEW_EXPECTED_PROBES[..]);
1127
1128 minstrel.handle_timeout();
1130 expect_probe_order(&mut minstrel, &EXPECTED_PROBES[..]);
1131 }
1132
1133 #[track_caller]
1134 fn assert_data_rate(
1135 channel_bandwidth: fidl_common::ChannelBandwidth,
1136 gi: WlanGi,
1137 relative_mcs_idx: u8,
1138 expected_mbit_per_second: f64,
1139 ) {
1140 let tx_time = tx_time_ht(channel_bandwidth, gi, relative_mcs_idx);
1141 const BYTES_PER_MBIT: f64 = 125000.0;
1142 let mut expected_tx_time =
1143 (MINSTREL_FRAME_LENGTH as f64 / BYTES_PER_MBIT) / expected_mbit_per_second;
1144 if gi == WlanGi::G_400NS {
1145 expected_tx_time += Duration::from_nanos(800).as_secs_f64();
1147 }
1148 let actual_tx_time = tx_time.as_secs_f64();
1149 let ratio = expected_tx_time / actual_tx_time;
1150 assert!(ratio < 1.01 && ratio > 0.99);
1151 }
1152
1153 #[test]
1154 fn tx_time_ht_approx_values_cbw20() {
1155 assert_data_rate(fidl_common::ChannelBandwidth::Cbw20, WlanGi::G_800NS, 0, 6.5);
1157 assert_data_rate(fidl_common::ChannelBandwidth::Cbw20, WlanGi::G_400NS, 0, 7.2);
1158 assert_data_rate(fidl_common::ChannelBandwidth::Cbw20, WlanGi::G_800NS, 8, 13.0);
1159 assert_data_rate(fidl_common::ChannelBandwidth::Cbw20, WlanGi::G_400NS, 8, 14.4);
1160 assert_data_rate(fidl_common::ChannelBandwidth::Cbw20, WlanGi::G_800NS, 31, 260.0);
1161 assert_data_rate(fidl_common::ChannelBandwidth::Cbw20, WlanGi::G_400NS, 31, 288.9);
1162 }
1163
1164 #[test]
1165 fn tx_time_ht_approx_values_cbw40() {
1166 assert_data_rate(fidl_common::ChannelBandwidth::Cbw40, WlanGi::G_800NS, 0, 13.5);
1168 assert_data_rate(fidl_common::ChannelBandwidth::Cbw40, WlanGi::G_400NS, 0, 15.0);
1169 assert_data_rate(fidl_common::ChannelBandwidth::Cbw40, WlanGi::G_800NS, 8, 27.0);
1170 assert_data_rate(fidl_common::ChannelBandwidth::Cbw40, WlanGi::G_400NS, 8, 30.0);
1171 assert_data_rate(fidl_common::ChannelBandwidth::Cbw40, WlanGi::G_800NS, 31, 540.0);
1172 assert_data_rate(fidl_common::ChannelBandwidth::Cbw40, WlanGi::G_400NS, 31, 600.0);
1173 }
1174}