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