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