1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
45use crate::mac::{Aid, MAX_AID};
67const TIM_BITMAP_LEN: usize = (MAX_AID as usize + 1) / 8;
89/// Represents a complete (that is, not partial) traffic indication bitmap. Consists of 2008 bits
10/// (i.e. 251 bytes).
11pub struct TrafficIndicationMap([u8; TIM_BITMAP_LEN]);
1213impl TrafficIndicationMap {
14pub fn new() -> Self {
15Self([0; TIM_BITMAP_LEN])
16 }
1718/// Sets a given AID to have buffered traffic (or not) in the traffic indication map.
19 ///
20 /// There is no special handling for AID 0 (i.e. for group traffic), we do not set it as the TIM
21 /// header will indicate it, as per IEEE Std 802.11-2016, 9.4.2.6: The bit numbered 0 in the
22 /// traffic indication virtual bitmap need not be included in the Partial Virtual Bitmap field
23 /// even if that bit is set.
24pub fn set_traffic_buffered(&mut self, aid: Aid, buffered: bool) {
25let octet = aid as usize / 8;
26let bit_offset = aid as usize % 8;
27self.0[octet] = (self.0[octet] & !(1 << bit_offset)) | ((buffered as u8) << bit_offset)
28 }
2930/// IEEE Std 802.11-2016, 9.4.2.6: N1 is the largest even number such that bits numbered 1 to
31 /// (N1 * 8) - 1 in the traffic indication virtual bitmap are all 0.
32fn n1(&self) -> usize {
33// TODO(https://fxbug.dev/42116033): Consider using u64 and CTZ instead of checking all the bytes individually.
34for (i, b) in self.0.iter().enumerate() {
35if *b != 0 {
36// Round down to the nearest even number.
37return i & !0b1;
38 }
39 }
4041// IEEE Std 802.11-2016, 9.4.2.6: In the event that all bits other than bit 0 in the traffic
42 // indication virtual bitmap are 0, [...], the Bitmap Offset subfield is 0, [...].
430
44}
4546/// IEEE Std 802.11-2016, 9.4.2.6: N2 is the smallest number such that bits numbered
47 /// (N2 + 1) * 8 to 2007 in the traffic indication virtual bitmap are all 0.
48fn n2(&self) -> usize {
49// TODO(https://fxbug.dev/42116033): Consider using u64 and CLZ instead of checking all the bytes individually.
50for (i, b) in self.0.iter().enumerate().rev() {
51if *b != 0 {
52return i;
53 }
54 }
55560
57}
5859/// Creates a view into TrafficIndicationMap suitable for use in a TIM IE.
60pub fn make_partial_virtual_bitmap(&self) -> (u8, &[u8]) {
61// Invariant: n1 <= n2. This is guaranteed as n1 scans from the front and n2 scans from the
62 // back, so the range of n1..n2 is always non-decreasing.
63let n1 = self.n1();
64let n2 = self.n2();
6566// IEEE Std 802.11-2016, 9.4.2.6: In the event that all bits other than bit 0 in the traffic
67 // indication virtual bitmap are 0, the Partial Virtual Bitmap field is encoded as a single
68 // octet equal to 0, [...].
69 //
70 // We always have at least one item in the bitmap here, even if there are no items in the
71 // traffic indication bitmap: both n1 and n2 will be 0, which will take the first octet from
72 // the bitmap (which will always be 0).
73((n1 / 2) as u8, &self.0[n1..n2 + 1])
74 }
75}
7677pub fn is_traffic_buffered(offset: u8, bitmap: &[u8], aid: Aid) -> bool {
78// IEEE 802.11-2016 Std, 9.4.2.6: When dot11MultiBSSIDActivated is false [...] In this case, the
79 // Bitmap Offset subfield value contains the number N1/2.
80let n1 = offset as usize * 2;
81let octet = aid as usize / 8;
8283let carries_aid = n1 <= octet && octet < bitmap.len() + n1;
84 carries_aid && bitmap[octet - n1] & (1 << (aid as usize % 8)) != 0
85}
8687#[cfg(test)]
88mod tests {
89use super::*;
9091#[test]
92fn zero_offset() {
93let bitmap = &[0b0010010][..];
9495assert!(!is_traffic_buffered(0, bitmap, 0));
96assert!(is_traffic_buffered(0, bitmap, 1));
97assert!(!is_traffic_buffered(0, bitmap, 2));
98assert!(!is_traffic_buffered(0, bitmap, 3));
99assert!(is_traffic_buffered(0, bitmap, 4));
100assert!(!is_traffic_buffered(0, bitmap, 5));
101assert!(!is_traffic_buffered(0, bitmap, 6));
102assert!(!is_traffic_buffered(0, bitmap, 7));
103assert!(!is_traffic_buffered(0, bitmap, 100));
104 }
105106#[test]
107fn with_offset() {
108let bitmap = &[0b0010010][..];
109110// Offset of 1 means "skip 16 bits"
111assert!(!is_traffic_buffered(1, bitmap, 15));
112assert!(!is_traffic_buffered(1, bitmap, 16));
113assert!(is_traffic_buffered(1, bitmap, 17));
114assert!(!is_traffic_buffered(1, bitmap, 18));
115assert!(!is_traffic_buffered(1, bitmap, 19));
116assert!(is_traffic_buffered(1, bitmap, 20));
117assert!(!is_traffic_buffered(1, bitmap, 21));
118assert!(!is_traffic_buffered(1, bitmap, 22));
119assert!(!is_traffic_buffered(1, bitmap, 100));
120 }
121122#[test]
123fn traffic_indication_map_set_traffic_buffered() {
124let mut tim = TrafficIndicationMap::new();
125 tim.set_traffic_buffered(12, true);
126let mut expected_bitmap = [0; TIM_BITMAP_LEN];
127 expected_bitmap[1] = 0b00010000;
128assert_eq!(&tim.0[..], &expected_bitmap[..]);
129assert_eq!(tim.n1(), 0);
130assert_eq!(tim.n2(), 1);
131 }
132133#[test]
134fn traffic_indication_map_set_traffic_buffered_multiple_octets() {
135let mut tim = TrafficIndicationMap::new();
136 tim.set_traffic_buffered(12, true);
137 tim.set_traffic_buffered(35, true);
138let mut expected_bitmap = [0; TIM_BITMAP_LEN];
139 expected_bitmap[1] = 0b00010000;
140 expected_bitmap[4] = 0b00001000;
141assert_eq!(&tim.0[..], &expected_bitmap[..]);
142assert_eq!(tim.n1(), 0);
143assert_eq!(tim.n2(), 4);
144 }
145146#[test]
147fn from_partial_virtual_bitmap() {
148let mut tim = TrafficIndicationMap::new();
149 tim.set_traffic_buffered(12, true);
150let (offset, bitmap) = tim.make_partial_virtual_bitmap();
151assert_eq!(offset, 0);
152assert_eq!(bitmap, &[0b00000000, 0b00010000]);
153assert!(is_traffic_buffered(offset, bitmap, 12));
154 }
155156#[test]
157fn from_partial_virtual_bitmap_bigger_offset() {
158let mut tim = TrafficIndicationMap::new();
159 tim.set_traffic_buffered(49, true);
160let (offset, bitmap) = tim.make_partial_virtual_bitmap();
161assert_eq!(offset, 3);
162assert_eq!(bitmap, &[0b00000010]);
163assert!(is_traffic_buffered(offset, bitmap, 49));
164 }
165166#[test]
167fn from_partial_virtual_bitmap_multiple_octets() {
168let mut tim = TrafficIndicationMap::new();
169 tim.set_traffic_buffered(12, true);
170 tim.set_traffic_buffered(35, true);
171let (offset, bitmap) = tim.make_partial_virtual_bitmap();
172assert_eq!(offset, 0);
173assert_eq!(bitmap, &[0b00000000, 0b00010000, 0b00000000, 0b00000000, 0b00001000]);
174assert!(is_traffic_buffered(offset, bitmap, 12));
175assert!(is_traffic_buffered(offset, bitmap, 35));
176 }
177178#[test]
179fn from_partial_virtual_bitmap_no_traffic() {
180let tim = TrafficIndicationMap::new();
181let (offset, bitmap) = tim.make_partial_virtual_bitmap();
182assert_eq!(offset, 0);
183assert_eq!(bitmap, &[0b00000000]);
184 }
185}