1use crate::ie::SupportedRate;
6use crate::mac::WlanGi;
7use anyhow::{Error, bail};
8use fidl_fuchsia_wlan_common as fidl_common;
9use fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211;
10use fidl_fuchsia_wlan_softmac as fidl_softmac;
11
12pub const HT_NUM_MCS: u8 = 32; pub const HT_NUM_UNIQUE_MCS: u8 = 8;
14pub const ERP_NUM_TX_VECTOR: u8 = 8;
15
16const INVALID_TX_VECTOR_IDX: u16 = fidl_common::WLAN_TX_VECTOR_IDX_INVALID;
17
18const HT_NUM_GI: u8 = 2;
19const HT_NUM_CBW: u8 = 2;
20const HT_NUM_TX_VECTOR: u8 = HT_NUM_GI * HT_NUM_CBW * HT_NUM_MCS;
21
22const DSSS_CCK_NUM_TX_VECTOR: u8 = 4;
23
24pub const START_IDX: u16 = 1 + INVALID_TX_VECTOR_IDX;
25pub const HT_START_IDX: u16 = START_IDX;
26pub const ERP_START_IDX: u16 = HT_START_IDX + HT_NUM_TX_VECTOR as u16;
27pub const DSSS_CCK_START_IDX: u16 = ERP_START_IDX + ERP_NUM_TX_VECTOR as u16;
28pub const MAX_VALID_IDX: u16 = DSSS_CCK_START_IDX + DSSS_CCK_NUM_TX_VECTOR as u16 - 1;
29
30#[derive(PartialEq, Debug)]
46pub struct TxVector {
68 phy: fidl_ieee80211::WlanPhyType,
69 gi: WlanGi,
70 cbw: fidl_ieee80211::ChannelBandwidth,
71 nss: u8, mcs_idx: u8,
76}
77
78impl TxVector {
79 pub fn new(
80 phy: fidl_ieee80211::WlanPhyType,
81 gi: WlanGi,
82 cbw: fidl_ieee80211::ChannelBandwidth,
83 mcs_idx: u8,
84 ) -> Result<Self, Error> {
85 let supported_mcs = match phy {
86 fidl_ieee80211::WlanPhyType::Dsss => mcs_idx == 0 || mcs_idx == 1,
87 fidl_ieee80211::WlanPhyType::Hr => mcs_idx == 2 || mcs_idx == 3,
88 fidl_ieee80211::WlanPhyType::Ht => {
89 match gi {
90 WlanGi::G_800NS | WlanGi::G_400NS => (),
91 other => bail!("Unsupported GI for HT PHY: {:?}", other),
92 }
93 match cbw {
94 fidl_ieee80211::ChannelBandwidth::Cbw20
95 | fidl_ieee80211::ChannelBandwidth::Cbw40
96 | fidl_ieee80211::ChannelBandwidth::Cbw40Below => (),
97 other => bail!("Unsupported CBW for HT PHY: {:?}", other),
98 }
99 mcs_idx < HT_NUM_MCS
100 }
101 fidl_ieee80211::WlanPhyType::Erp => mcs_idx < ERP_NUM_TX_VECTOR,
102 other => bail!("Unsupported phy type: {:?}", other),
103 };
104 if supported_mcs {
105 let nss = match phy {
106 fidl_ieee80211::WlanPhyType::Ht => 1 + mcs_idx / HT_NUM_UNIQUE_MCS,
107 _ => 1,
109 };
110 Ok(Self { phy, gi, cbw, nss, mcs_idx })
111 } else {
112 bail!("Unsupported MCS {:?} for phy type {:?}", mcs_idx, phy);
113 }
114 }
115
116 pub fn phy(&self) -> fidl_ieee80211::WlanPhyType {
117 self.phy
118 }
119
120 pub fn from_supported_rate(erp_rate: &SupportedRate) -> Result<Self, Error> {
121 let (phy, mcs_idx) = match erp_rate.rate() {
122 2 => (fidl_ieee80211::WlanPhyType::Dsss, 0),
123 4 => (fidl_ieee80211::WlanPhyType::Dsss, 1),
124 11 => (fidl_ieee80211::WlanPhyType::Hr, 2),
125 22 => (fidl_ieee80211::WlanPhyType::Hr, 3),
126 12 => (fidl_ieee80211::WlanPhyType::Erp, 0),
127 18 => (fidl_ieee80211::WlanPhyType::Erp, 1),
128 24 => (fidl_ieee80211::WlanPhyType::Erp, 2),
129 36 => (fidl_ieee80211::WlanPhyType::Erp, 3),
130 48 => (fidl_ieee80211::WlanPhyType::Erp, 4),
131 72 => (fidl_ieee80211::WlanPhyType::Erp, 5),
132 96 => (fidl_ieee80211::WlanPhyType::Erp, 6),
133 108 => (fidl_ieee80211::WlanPhyType::Erp, 7),
134 other_rate => {
135 bail!("Invalid rate {} * 0.5 Mbps for 802.11a/b/g.", other_rate);
136 }
137 };
138 Self::new(phy, WlanGi::G_800NS, fidl_ieee80211::ChannelBandwidth::Cbw20, mcs_idx)
139 }
140
141 pub fn from_idx(idx: TxVecIdx) -> Self {
145 let phy = idx.to_phy();
146 match phy {
147 fidl_ieee80211::WlanPhyType::Ht => {
148 let group_idx = (*idx - HT_START_IDX) / HT_NUM_MCS as u16;
149 let gi = match (group_idx / HT_NUM_CBW as u16) % HT_NUM_GI as u16 {
150 1 => WlanGi::G_400NS,
151 _ => WlanGi::G_800NS,
152 };
153 let cbw = match group_idx % HT_NUM_CBW as u16 {
154 0 => fidl_ieee80211::ChannelBandwidth::Cbw20,
155 _ => fidl_ieee80211::ChannelBandwidth::Cbw40,
156 };
157 let mcs_idx = ((*idx - HT_START_IDX) % HT_NUM_MCS as u16) as u8;
158 Self::new(phy, gi, cbw, mcs_idx).unwrap()
159 }
160 fidl_ieee80211::WlanPhyType::Erp => Self::new(
161 phy,
162 WlanGi::G_800NS,
163 fidl_ieee80211::ChannelBandwidth::Cbw20,
164 (*idx - ERP_START_IDX) as u8,
165 )
166 .unwrap(),
167 fidl_ieee80211::WlanPhyType::Dsss | fidl_ieee80211::WlanPhyType::Hr => Self::new(
168 phy,
169 WlanGi::G_800NS,
170 fidl_ieee80211::ChannelBandwidth::Cbw20,
171 (*idx - DSSS_CCK_START_IDX) as u8,
172 )
173 .unwrap(),
174 _ => unreachable!(),
175 }
176 }
177
178 pub fn to_idx(&self) -> TxVecIdx {
179 match self.phy {
180 fidl_ieee80211::WlanPhyType::Ht => {
181 let group_idx = match self.gi {
182 WlanGi::G_400NS => HT_NUM_CBW as u16,
183 _ => 0,
184 } + match self.cbw {
185 fidl_ieee80211::ChannelBandwidth::Cbw40
186 | fidl_ieee80211::ChannelBandwidth::Cbw40Below => 1,
187 _ => 0,
188 };
189 TxVecIdx::new(HT_START_IDX + group_idx * HT_NUM_MCS as u16 + self.mcs_idx as u16)
190 .unwrap()
191 }
192 fidl_ieee80211::WlanPhyType::Erp => {
193 TxVecIdx::new(ERP_START_IDX + self.mcs_idx as u16).unwrap()
194 }
195 fidl_ieee80211::WlanPhyType::Hr | fidl_ieee80211::WlanPhyType::Dsss => {
196 TxVecIdx::new(DSSS_CCK_START_IDX + self.mcs_idx as u16).unwrap()
197 }
198 _ => unreachable!(),
199 }
200 }
201
202 pub fn to_fidl_tx_info(
203 &self,
204 tx_flags: fidl_softmac::WlanTxInfoFlags,
205 minstrel_enabled: bool,
206 ) -> fidl_softmac::WlanTxInfo {
207 fidl_softmac::WlanTxInfo {
208 tx_flags: tx_flags.bits(),
209 valid_fields: (fidl_softmac::WlanTxInfoValid::CHANNEL_BANDWIDTH
210 | fidl_softmac::WlanTxInfoValid::PHY
211 | fidl_softmac::WlanTxInfoValid::MCS
212 | if minstrel_enabled {
213 fidl_softmac::WlanTxInfoValid::TX_VECTOR_IDX
214 } else {
215 fidl_softmac::WlanTxInfoValid::empty()
216 })
217 .bits(),
218 tx_vector_idx: self.to_idx().0,
219 phy: self.phy,
220 channel_bandwidth: self.cbw,
221 mcs: self.mcs_idx,
222 }
223 }
224}
225
226#[derive(Hash, PartialEq, Eq, Debug, Copy, Clone, Ord, PartialOrd)]
227pub struct TxVecIdx(u16);
228impl std::ops::Deref for TxVecIdx {
229 type Target = u16;
230 fn deref(&self) -> &u16 {
231 &self.0
232 }
233}
234
235impl TxVecIdx {
236 pub fn new(value: u16) -> Option<Self> {
237 if INVALID_TX_VECTOR_IDX < value && value <= MAX_VALID_IDX {
238 Some(Self(value))
239 } else {
240 None
241 }
242 }
243
244 pub fn to_erp_rate(&self) -> Option<SupportedRate> {
247 const ERP_RATE_LIST: [u8; ERP_NUM_TX_VECTOR as usize] = [12, 18, 24, 36, 48, 72, 96, 108];
248 if self.is_erp() {
249 Some(SupportedRate(ERP_RATE_LIST[(self.0 - ERP_START_IDX) as usize]))
250 } else {
251 None
252 }
253 }
254
255 pub fn to_phy(&self) -> fidl_ieee80211::WlanPhyType {
256 match self.0 {
257 idx if idx < HT_START_IDX + HT_NUM_TX_VECTOR as u16 => fidl_ieee80211::WlanPhyType::Ht,
258 idx if idx < ERP_START_IDX + ERP_NUM_TX_VECTOR as u16 => {
259 fidl_ieee80211::WlanPhyType::Erp
260 }
261 idx if idx < DSSS_CCK_START_IDX + 2 => fidl_ieee80211::WlanPhyType::Dsss,
262 idx if idx < DSSS_CCK_START_IDX + DSSS_CCK_NUM_TX_VECTOR as u16 => {
263 fidl_ieee80211::WlanPhyType::Hr
264 }
265 _ => panic!("TxVecIdx has invalid value"),
268 }
269 }
270
271 pub fn is_ht(&self) -> bool {
272 HT_START_IDX <= self.0 && self.0 < HT_START_IDX + HT_NUM_TX_VECTOR as u16
273 }
274
275 pub fn is_erp(&self) -> bool {
276 ERP_START_IDX <= self.0 && self.0 < ERP_START_IDX + ERP_NUM_TX_VECTOR as u16
277 }
278}
279
280impl std::fmt::Display for TxVecIdx {
281 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
282 let tx_vector = TxVector::from_idx(*self);
283 write!(f, "TxVecIdx {:3}: {:?}", self.0, tx_vector)
284 }
285}
286
287#[cfg(test)]
288mod tests {
289 use super::*;
290
291 #[test]
292 fn valid_tx_vector_idxs() {
293 for idx in INVALID_TX_VECTOR_IDX + 1..=MAX_VALID_IDX {
294 let idx = TxVecIdx::new(idx).expect("Could not make TxVecIdx from valid index");
295 idx.to_phy(); }
297 assert!(
298 TxVecIdx::new(INVALID_TX_VECTOR_IDX).is_none(),
299 "Should not be able to construct invalid tx vector idx"
300 );
301 assert!(
302 TxVecIdx::new(MAX_VALID_IDX + 1).is_none(),
303 "Should not be able to construct invalid tx vector idx"
304 );
305 }
306
307 #[test]
308 fn erp_rates() {
309 for idx in INVALID_TX_VECTOR_IDX + 1..=MAX_VALID_IDX {
310 let idx = TxVecIdx::new(idx).expect("Could not make TxVecIdx from valid index");
311 assert_eq!(idx.is_erp(), idx.to_erp_rate().is_some());
312 }
313 }
314
315 #[test]
316 fn phy_types() {
317 for idx in INVALID_TX_VECTOR_IDX + 1..=MAX_VALID_IDX {
318 let idx = TxVecIdx::new(idx).expect("Could not make TxVecIdx from valid index");
319 if idx.is_erp() {
320 assert_eq!(idx.to_phy(), fidl_ieee80211::WlanPhyType::Erp);
321 } else if idx.is_ht() {
322 assert_eq!(idx.to_phy(), fidl_ieee80211::WlanPhyType::Ht);
323 } else {
324 assert!(
325 idx.to_phy() == fidl_ieee80211::WlanPhyType::Dsss
326 || idx.to_phy() == fidl_ieee80211::WlanPhyType::Hr
327 );
328 }
329 }
330 }
331
332 #[test]
333 fn to_and_from_idx() {
334 for idx in INVALID_TX_VECTOR_IDX + 1..=MAX_VALID_IDX {
335 let idx = TxVecIdx::new(idx).expect("Could not make TxVecIdx from valid index");
336 let tx_vector = TxVector::from_idx(idx);
337 assert_eq!(idx, tx_vector.to_idx());
338 }
339 }
340
341 #[test]
342 fn ht_and_erp_phy_types() {
343 for idx in INVALID_TX_VECTOR_IDX + 1..=MAX_VALID_IDX {
344 let idx = TxVecIdx::new(idx).expect("Could not make TxVecIdx from valid index");
345 let tx_vector = TxVector::from_idx(idx);
346 if idx.is_erp() {
347 assert_eq!(tx_vector.phy(), fidl_ieee80211::WlanPhyType::Erp);
348 } else if idx.is_ht() {
349 assert_eq!(tx_vector.phy(), fidl_ieee80211::WlanPhyType::Ht);
350 }
351 }
352 }
353
354 #[test]
355 fn from_erp_rates() {
356 for idx in INVALID_TX_VECTOR_IDX + 1..=MAX_VALID_IDX {
357 let idx = TxVecIdx::new(idx).expect("Could not make TxVecIdx from valid index");
358 if idx.is_erp() {
359 let erp_rate = idx.to_erp_rate().unwrap();
360 let tx_vector = TxVector::from_supported_rate(&erp_rate)
361 .expect("Could not make TxVector from ERP rate.");
362 assert_eq!(idx, tx_vector.to_idx());
363 }
364 }
365 }
366}