xref: /OK3568_Linux_fs/kernel/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmwifi_rates.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  * Common [OS-independent] rate management
3  * 802.11 Networking Adapter Device Driver.
4  *
5  * Broadcom Proprietary and Confidential. Copyright (C) 2020,
6  * All Rights Reserved.
7  *
8  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom;
9  * the contents of this file may not be disclosed to third parties,
10  * copied or duplicated in any form, in whole or in part, without
11  * the prior written permission of Broadcom.
12  *
13  *
14  * <<Broadcom-WL-IPTag/Proprietary:>>
15  */
16 
17 #include <typedefs.h>
18 #ifdef BCMDRIVER
19 #include <osl.h>
20 #else
21 #include <assert.h>
22 #ifndef ASSERT
23 #define ASSERT(e)	assert(e)
24 #endif
25 #ifndef ASSERT_FP
26 #define ASSERT_FP(e)	assert(e)
27 #endif
28 #endif /* BCMDRIVER */
29 #include <802.11.h>
30 #include <802.11ax.h>
31 #include <bcmutils.h>
32 
33 #include <bcmwifi_rspec.h>
34 #include <bcmwifi_rates.h>
35 
36 /* TODO: Consolidate rate utility functions from wlc_rate.c and bcmwifi_monitor.c
37  * into here if they're shared by non wl layer as well...
38  */
39 
40 /* ============================================ */
41 /* Moved from wlc_rate.c                        */
42 /* ============================================ */
43 
44 /* HE mcs info */
45 struct ieee_80211_mcs_rate_info {
46 	uint8 constellation_bits;
47 	uint8 coding_q;
48 	uint8 coding_d;
49 	uint8 dcm_capable;	/* 1 if dcm capable */
50 };
51 
52 static const struct ieee_80211_mcs_rate_info wlc_mcs_info[] = {
53 	{ 1, 1, 2, 1 }, /* MCS  0: MOD: BPSK,   CR 1/2, dcm capable */
54 	{ 2, 1, 2, 1 }, /* MCS  1: MOD: QPSK,   CR 1/2, dcm capable */
55 	{ 2, 3, 4, 0 }, /* MCS  2: MOD: QPSK,   CR 3/4, NOT dcm capable */
56 	{ 4, 1, 2, 1 }, /* MCS  3: MOD: 16QAM,  CR 1/2, dcm capable */
57 	{ 4, 3, 4, 1 }, /* MCS  4: MOD: 16QAM,  CR 3/4, dcm capable */
58 	{ 6, 2, 3, 0 }, /* MCS  5: MOD: 64QAM,  CR 2/3, NOT dcm capable */
59 	{ 6, 3, 4, 0 }, /* MCS  6: MOD: 64QAM,  CR 3/4, NOT dcm capable */
60 	{ 6, 5, 6, 0 }, /* MCS  7: MOD: 64QAM,  CR 5/6, NOT dcm capable */
61 	{ 8, 3, 4, 0 }, /* MCS  8: MOD: 256QAM, CR 3/4, NOT dcm capable */
62 	{ 8, 5, 6, 0 }, /* MCS  9: MOD: 256QAM, CR 5/6, NOT dcm capable */
63 	{ 10, 3, 4, 0 }, /* MCS 10: MOD: 1024QAM, CR 3/4, NOT dcm capable */
64 	{ 10, 5, 6, 0 }, /* MCS 11: MOD: 1024QAM, CR 5/6, NOT dcm capable */
65 #ifdef WL11BE
66 	/* TODO: for now EHT shares this table with HE,
67 	 * create a new table if needed once we know more
68 	 * about EHT rate calculation...
69 	 */
70 	{ 12, 3, 4, 0 }, /* MCS 12: MOD: 4096QAM, CR 3/4, NOT dcm capable */
71 	{ 12, 5, 6, 0 }, /* MCS 13: MOD: 4096QAM, CR 5/6, NOT dcm capable */
72 #endif
73 };
74 
75 /* Nsd values Draft0.4 Table 26.63 onwards */
76 static const uint wlc_he_nsd[] = {
77 	234,	/* BW20 */
78 	468,	/* BW40 */
79 	980,	/* BW80 */
80 	1960,	/* BW160 */
81 #ifdef WL11BE
82 	/* TODO: for now EHT shares this table with HE,
83 	 * create a new table if needed once we know more
84 	 * about EHT rate calculation...
85 	 */
86 	2940,	/* BW240 */
87 	3920	/* BW320 */
88 #endif
89 };
90 
91 /* Nsd values Draft3.3 Table 28-15 */
92 static const uint wlc_he_ru_nsd[] = {
93 	24,	/* 26T */
94 	48,	/* 52T */
95 	102,	/* 106T */
96 	234,	/* 242T/BW20 */
97 	468,	/* 484T/BW40 */
98 	980,	/* 996T/BW80 */
99 	1960,	/* 2*996T/BW160 */
100 #ifdef WL11BE
101 	/* TODO: for now EHT shares this table with HE,
102 	 * create a new table if needed once we know more
103 	 * about EHT rate calculation...
104 	 */
105 	2940,	/* 3*996T/BW240 */
106 	3920	/* 4*996T/BW320 */
107 #endif
108 };
109 
110 #define HE_RU_TO_NSD(ru_idx)	\
111 	(ru_idx < ARRAYSIZE(wlc_he_ru_nsd)) ? \
112 	wlc_he_ru_nsd[ru_idx] : 0
113 
114 /* sym_len = 12.8 us. For calculation purpose, *10 */
115 #define HE_SYM_LEN_FACTOR		(128)
116 
117 /* GI values = 0.8 , 1.6 or 3.2 us. For calculation purpose, *10 */
118 #define HE_GI_800us_FACTOR		(8)
119 #define HE_GI_1600us_FACTOR		(16)
120 #define HE_GI_3200us_FACTOR		(32)
121 
122 /* To avoid ROM invalidation use the old macro as is... */
123 #ifdef WL11BE
124 #define HE_BW_TO_NSD(bwi) \
125 	((bwi) > 0u && (bwi) <= ARRAYSIZE(wlc_he_nsd)) ? \
126 	wlc_he_nsd[(bwi) - 1u] : 0u
127 #else
128 #define HE_BW_TO_NSD(bwi) \
129 	((bwi) > 0 && ((bwi) << WL_RSPEC_BW_SHIFT) <= WL_RSPEC_BW_160MHZ) ? \
130 	wlc_he_nsd[(bwi)-1] : 0
131 #endif /* WL11BE */
132 
133 #define ksps		250 /* kilo symbols per sec, 4 us sym */
134 
135 #ifdef WL11BE
136 /* Table "wlc_nsd" is derived from HT and VHT #defines below, but extended for HE
137  * for rate calculation purpose at a given NSS and bandwidth combination.
138  *
139  * It should and can only be used in where it wants to know the relative rate in kbps
140  * for a different NSS and bandwidth combination at a given mcs e.g. in fallback rate
141  * search. It shouldn not and can not be used in where it calculates the absolute rate
142  * i.e. the result doesn't agree with what the spec says otherwise.
143  *
144  * See Std 802.11-2016 "Table 21-61 VHT-MCSs for optional 160 MHz and 80+80 MHz, NSS = 8"
145  * for VHT, and P802.11ax/D6.0 "Table 27-111 HE-MCSs for 2x996-tone RU, NSS = 8" for HE,
146  * for 160Mhz bandwidth for resulting rate comparison.
147  *
148  * It's again extended for EHT 240/320Mhz bandwidth, for the same purpose.
149  */
150 static const uint16 wlc_nsd[] = {
151 	52,	/* 20MHz */
152 	108,	/* 40MHz */
153 	234,	/* 80Mhz */
154 	468,	/* 160MHz */
155 	702,	/* 240MHz */
156 	936,	/* 320MHz */
157 };
158 
159 #define BW_TO_NSD(bwi) \
160 	((bwi) > 0u && (bwi) <= ARRAYSIZE(wlc_nsd)) ? \
161 	wlc_nsd[(bwi) - 1u] : 0u
162 
163 static uint
wf_nsd2ndbps(uint mcs,uint nss,uint nsd,bool dcm)164 wf_nsd2ndbps(uint mcs, uint nss, uint nsd, bool dcm)
165 {
166 	uint Ndbps;
167 
168 	/* multiply number of spatial streams,
169 	 * bits per number from the constellation,
170 	 * and coding quotient
171 	 */
172 	Ndbps = nsd * nss *
173 		wlc_mcs_info[mcs].coding_q * wlc_mcs_info[mcs].constellation_bits;
174 
175 	/* adjust for the coding rate divisor */
176 	Ndbps = Ndbps / wlc_mcs_info[mcs].coding_d;
177 
178 	/* take care of dcm: dcm divides R by 2. If not dcm mcs, ignore */
179 	if (dcm) {
180 		if (wlc_mcs_info[mcs].dcm_capable) {
181 			Ndbps >>= 1u;
182 		}
183 	}
184 
185 	return Ndbps;
186 }
187 #else
188 /* for HT and VHT? */
189 #define Nsd_20MHz	52
190 #define Nsd_40MHz	108
191 #define Nsd_80MHz	234
192 #define Nsd_160MHz	468
193 #endif /* WL11BE */
194 
195 uint
wf_he_mcs_to_Ndbps(uint mcs,uint nss,uint bw,bool dcm)196 wf_he_mcs_to_Ndbps(uint mcs, uint nss, uint bw, bool dcm)
197 {
198 	uint Nsd;
199 	uint Ndbps;
200 
201 	/* find the number of complex numbers per symbol */
202 	Nsd = HE_BW_TO_NSD(bw >> WL_RSPEC_BW_SHIFT);
203 
204 #ifdef WL11BE
205 	Ndbps = wf_nsd2ndbps(mcs, nss, Nsd, dcm);
206 #else
207 	/* multiply number of spatial streams,
208 	 * bits per number from the constellation,
209 	 * and coding quotient
210 	 */
211 	Ndbps = Nsd * nss *
212 		wlc_mcs_info[mcs].coding_q * wlc_mcs_info[mcs].constellation_bits;
213 
214 	/* adjust for the coding rate divisor */
215 	Ndbps = Ndbps / wlc_mcs_info[mcs].coding_d;
216 
217 	/* take care of dcm: dcm divides R by 2. If not dcm mcs, ignore */
218 	if (dcm) {
219 		if (wlc_mcs_info[mcs].dcm_capable) {
220 			Ndbps >>= 1;
221 		}
222 	}
223 #endif /* WL11BE */
224 
225 	return Ndbps;
226 }
227 
228 uint32
wf_he_mcs_ru_to_ndbps(uint8 mcs,uint8 nss,bool dcm,uint8 ru_index)229 wf_he_mcs_ru_to_ndbps(uint8 mcs, uint8 nss, bool dcm, uint8 ru_index)
230 {
231 	uint32 nsd;
232 	uint32 ndbps;
233 
234 	/* find the number of complex numbers per symbol */
235 	nsd = HE_RU_TO_NSD(ru_index);
236 
237 #ifdef WL11BE
238 	ndbps = wf_nsd2ndbps(mcs, nss, nsd, dcm);
239 #else
240 	/* multiply number of spatial streams,
241 	 * bits per number from the constellation,
242 	 * and coding quotient
243 	 * Ndbps = Nss x Nsd x (Nbpscs x R) x (DCM/2)
244 	 */
245 	ndbps = nsd * nss *
246 		wlc_mcs_info[mcs].coding_q * wlc_mcs_info[mcs].constellation_bits;
247 
248 	/* adjust for the coding rate divisor */
249 	ndbps = ndbps / wlc_mcs_info[mcs].coding_d;
250 
251 	/* take care of dcm: dcm divides R by 2. If not dcm mcs, ignore */
252 	if (dcm && wlc_mcs_info[mcs].dcm_capable) {
253 		ndbps >>= 1;
254 	}
255 #endif /* WL11BE */
256 	return ndbps;
257 }
258 
259 /**
260  * Returns the rate in [Kbps] units for a caller supplied MCS/bandwidth/Nss/Sgi/dcm combination.
261  *     'mcs' : a *single* spatial stream MCS (11ax)
262  * formula as per http:
263  *	WLAN&preview=/323036249/344457953/11ax_rate_table.xlsx
264  * Symbol length = 12.8 usec [given as sym_len/10 below]
265  * GI value = 0.8 or 1.6 or 3.2 usec [given as GI_value/10 below]
266  * rate (Kbps) = (Nsd * Nbpscs * nss * (coding_q/coding_d) * 1000) / ((sym_len/10) + (GI_value/10))
267  *	 Note that, for calculation purpose, following is used. [to be careful with overflows]
268  * rate (Kbps) = (Nsd * Nbpscs * nss * (coding_q/coding_d) * 1000) / ((sym_len + GI_value) / 10)
269  * rate (Kbps) = (Nsd * Nbpscs * nss * (coding_q/coding_d) * 1000) / (sym_len + GI_value) * 10
270  */
271 uint
wf_he_mcs_to_rate(uint mcs,uint nss,uint bw,uint gi,bool dcm)272 wf_he_mcs_to_rate(uint mcs, uint nss, uint bw, uint gi, bool dcm)
273 {
274 	uint rate;
275 	uint rate_deno;
276 
277 	rate = HE_BW_TO_NSD(bw >> WL_RSPEC_BW_SHIFT);
278 
279 #ifdef WL11BE
280 	rate = wf_nsd2ndbps(mcs, nss, rate, dcm);
281 #else
282 	/* Nbpscs: multiply by bits per number from the constellation in use */
283 	rate = rate * wlc_mcs_info[mcs].constellation_bits;
284 
285 	/* Nss: adjust for the number of spatial streams */
286 	rate = rate * nss;
287 
288 	/* R: adjust for the coding rate given as a quotient and divisor */
289 	rate = (rate * wlc_mcs_info[mcs].coding_q) / wlc_mcs_info[mcs].coding_d;
290 
291 	/* take care of dcm: dcm divides R by 2. If not dcm mcs, ignore */
292 	if (dcm) {
293 		if (wlc_mcs_info[mcs].dcm_capable) {
294 			rate >>= 1;
295 		}
296 	}
297 #endif /* WL11BE */
298 
299 	/* add sym len factor */
300 	rate_deno = HE_SYM_LEN_FACTOR;
301 
302 	/* get GI for denominator */
303 	if (HE_IS_GI_3_2us(gi)) {
304 		rate_deno += HE_GI_3200us_FACTOR;
305 	} else if (HE_IS_GI_1_6us(gi)) {
306 		rate_deno += HE_GI_1600us_FACTOR;
307 	} else {
308 		/* assuming HE_GI_0_8us */
309 		rate_deno += HE_GI_800us_FACTOR;
310 	}
311 
312 	/* as per above formula */
313 	rate *= 1000;	/* factor of 10. *100 to accommodate 2 places */
314 	rate /= rate_deno;
315 	rate *= 10; /* *100 was already done above. Splitting is done to avoid overflow. */
316 
317 	return rate;
318 }
319 
320 uint
wf_mcs_to_Ndbps(uint mcs,uint nss,uint bw)321 wf_mcs_to_Ndbps(uint mcs, uint nss, uint bw)
322 {
323 	uint Nsd;
324 	uint Ndbps;
325 
326 	/* This calculation works for 11n HT and 11ac VHT if the HT mcs values
327 	 * are decomposed into a base MCS = MCS % 8, and Nss = 1 + MCS / 8.
328 	 * That is, HT MCS 23 is a base MCS = 7, Nss = 3
329 	 */
330 
331 	/* find the number of complex numbers per symbol */
332 #ifdef WL11BE
333 	Nsd = BW_TO_NSD(bw >> WL_RSPEC_BW_SHIFT);
334 
335 	Ndbps = wf_nsd2ndbps(mcs, nss, Nsd, FALSE);
336 #else
337 	if (bw == WL_RSPEC_BW_20MHZ) {
338 		Nsd = Nsd_20MHz;
339 	} else if (bw == WL_RSPEC_BW_40MHZ) {
340 		Nsd = Nsd_40MHz;
341 	} else if (bw == WL_RSPEC_BW_80MHZ) {
342 		Nsd = Nsd_80MHz;
343 	} else if (bw == WL_RSPEC_BW_160MHZ) {
344 		Nsd = Nsd_160MHz;
345 	} else {
346 		Nsd = 0;
347 	}
348 
349 	/* multiply number of spatial streams,
350 	 * bits per number from the constellation,
351 	 * and coding quotient
352 	 */
353 	Ndbps = Nsd * nss *
354 		wlc_mcs_info[mcs].coding_q * wlc_mcs_info[mcs].constellation_bits;
355 
356 	/* adjust for the coding rate divisor */
357 	Ndbps = Ndbps / wlc_mcs_info[mcs].coding_d;
358 #endif /* WL11BE */
359 
360 	return Ndbps;
361 }
362 
363 /**
364  * Returns the rate in [Kbps] units for a caller supplied MCS/bandwidth/Nss/Sgi combination.
365  *     'mcs' : a *single* spatial stream MCS (11n or 11ac)
366  */
367 uint
wf_mcs_to_rate(uint mcs,uint nss,uint bw,int sgi)368 wf_mcs_to_rate(uint mcs, uint nss, uint bw, int sgi)
369 {
370 	uint rate;
371 
372 	if (mcs == 32) {
373 		/* just return fixed values for mcs32 instead of trying to parametrize */
374 		rate = (sgi == 0) ? 6000 : 6778;
375 	} else {
376 		/* This calculation works for 11n HT, 11ac VHT and 11ax HE if the HT mcs values
377 		 * are decomposed into a base MCS = MCS % 8, and Nss = 1 + MCS / 8.
378 		 * That is, HT MCS 23 is a base MCS = 7, Nss = 3
379 		 */
380 
381 #if defined(WLPROPRIETARY_11N_RATES)
382 		switch (mcs) {
383 		case 87:
384 			mcs = 8; /* MCS  8: MOD: 256QAM, CR 3/4 */
385 			break;
386 		case 88:
387 			mcs = 9; /* MCS  9: MOD: 256QAM, CR 5/6 */
388 			break;
389 		default:
390 			break;
391 		}
392 #endif /* WLPROPRIETARY_11N_RATES */
393 
394 #ifdef WL11BE
395 		rate = wf_mcs_to_Ndbps(mcs, nss, bw);
396 #else
397 		/* find the number of complex numbers per symbol */
398 		if (RSPEC_IS20MHZ(bw)) {
399 			/* 4360 TODO: eliminate Phy const in rspec bw, then just compare
400 			 * as in 80 and 160 case below instead of RSPEC_IS20MHZ(bw)
401 			 */
402 			rate = Nsd_20MHz;
403 		} else if (RSPEC_IS40MHZ(bw)) {
404 			/* 4360 TODO: eliminate Phy const in rspec bw, then just compare
405 			 * as in 80 and 160 case below instead of RSPEC_IS40MHZ(bw)
406 			 */
407 			rate = Nsd_40MHz;
408 		} else if (bw == WL_RSPEC_BW_80MHZ) {
409 			rate = Nsd_80MHz;
410 		} else if (bw == WL_RSPEC_BW_160MHZ) {
411 			rate = Nsd_160MHz;
412 		} else {
413 			rate = 0;
414 		}
415 
416 		/* multiply by bits per number from the constellation in use */
417 		rate = rate * wlc_mcs_info[mcs].constellation_bits;
418 
419 		/* adjust for the number of spatial streams */
420 		rate = rate * nss;
421 
422 		/* adjust for the coding rate given as a quotient and divisor */
423 		rate = (rate * wlc_mcs_info[mcs].coding_q) / wlc_mcs_info[mcs].coding_d;
424 #endif /* WL11BE */
425 
426 		/* multiply by Kilo symbols per sec to get Kbps */
427 		rate = rate * ksps;
428 
429 		/* adjust the symbols per sec for SGI
430 		 * symbol duration is 4 us without SGI, and 3.6 us with SGI,
431 		 * so ratio is 10 / 9
432 		 */
433 		if (sgi) {
434 			/* add 4 for rounding of division by 9 */
435 			rate = ((rate * 10) + 4) / 9;
436 		}
437 	}
438 
439 	return rate;
440 } /* wf_mcs_to_rate */
441 
442 /* This function needs update to handle MU frame PLCP as well (MCS is conveyed via VHT-SIGB
443  * field in case of MU frames). Currently this support needs to be added in uCode to communicate
444  * MCS information for an MU frame
445  *
446  *	For VHT frame:
447  *		bit 0-3 mcs index
448  *		bit 6-4 nsts for VHT
449  *		bit 7:	 1 for VHT
450  *	Note: bit 7 is used to indicate to the rate sel the mcs is a non HT mcs!
451  *
452  * Essentially it's the NSS:MCS portions of the rspec
453  */
454 uint8
wf_vht_plcp_to_rate(uint8 * plcp)455 wf_vht_plcp_to_rate(uint8 *plcp)
456 {
457 	uint8 rate, gid;
458 	uint nss;
459 	uint32 plcp0 = plcp[0] + (plcp[1] << 8); /* don't need plcp[2] */
460 
461 	gid = (plcp0 & VHT_SIGA1_GID_MASK) >> VHT_SIGA1_GID_SHIFT;
462 	if (gid > VHT_SIGA1_GID_TO_AP && gid < VHT_SIGA1_GID_NOT_TO_AP) {
463 		/* for MU packet we hacked Signal Tail field in VHT-SIG-A2 to save nss and mcs,
464 		 * copy from murate in d11 rx header.
465 		 * nss = bit 18:19 (for 11ac 2 bits to indicate maximum 4 nss)
466 		 * mcs = 20:23
467 		 */
468 		rate = (plcp[5] & 0xF0) >> 4;
469 		nss = ((plcp[5] & 0x0C) >> 2) + 1;
470 	} else {
471 		rate = (plcp[3] >> VHT_SIGA2_MCS_SHIFT);
472 		nss = ((plcp0 & VHT_SIGA1_NSTS_SHIFT_MASK_USER0) >>
473 			VHT_SIGA1_NSTS_SHIFT) + 1;
474 		if (plcp0 & VHT_SIGA1_STBC)
475 			nss = nss >> 1;
476 	}
477 	rate |= ((nss << WL_RSPEC_VHT_NSS_SHIFT) | WF_NON_HT_MCS);
478 
479 	return rate;
480 }
481 
482 /**
483  * Function for computing NSS:MCS from HE SU PLCP or
484  * MCS:LTF-GI from HE MU PLCP
485  *
486  * based on rev3.10 :
487  * https://docs.google.com/spreadsheets/d/
488  * 1eP6ZCRrtnF924ds1R-XmbcH0IdQ0WNJpS1-FHmWeb9g/edit#gid=1492656555
489  *
490  *	For HE SU frame:
491  *		bit 0-3 mcs index
492  *		bit 6-4 nsts for HE
493  *		bit 7:	 1 for HE
494  *	Note: bit 7 is used to indicate to the rate sel the mcs is a non HT mcs!
495  *	Essentially it's the NSS:MCS portions of the rspec
496  *
497  *	For HE MU frame:
498  *		bit 0-3 mcs index
499  *		bit 4-5 LTF-GI value
500  *		bit 6 STBC
501  *	Essentially it's the MCS and LTF-GI portion of the rspec
502  */
503 /* Macros to be used for calculating rate from PLCP */
504 #define HE_SU_PLCP2RATE_MCS_MASK	0x0F
505 #define HE_SU_PLCP2RATE_MCS_SHIFT	0
506 #define HE_SU_PLCP2RATE_NSS_MASK	0x70
507 #define HE_SU_PLCP2RATE_NSS_SHIFT	4
508 #define HE_MU_PLCP2RATE_LTF_GI_MASK	0x30
509 #define HE_MU_PLCP2RATE_LTF_GI_SHIFT	4
510 #define HE_MU_PLCP2RATE_STBC_MASK	0x40
511 #define HE_MU_PLCP2RATE_STBC_SHIFT	6
512 
513 uint8
wf_he_plcp_to_rate(uint8 * plcp,bool is_mu)514 wf_he_plcp_to_rate(uint8 *plcp, bool is_mu)
515 {
516 	uint8 rate = 0;
517 	uint8 nss = 0;
518 	uint32 plcp0 = 0;
519 	uint32 plcp1 = 0;
520 	uint8 he_ltf_gi;
521 	uint8 stbc;
522 
523 	ASSERT(plcp);
524 
525 	BCM_REFERENCE(nss);
526 	BCM_REFERENCE(he_ltf_gi);
527 
528 	plcp0 = ((plcp[3] << 24) | (plcp[2] << 16) | (plcp[1] << 8) | plcp[0]);
529 	plcp1 = ((plcp[5] << 8) | plcp[4]);
530 
531 	if (!is_mu) {
532 		/* For SU frames return rate in MCS:NSS format */
533 		rate = ((plcp0 & HE_SU_RE_SIGA_MCS_MASK) >> HE_SU_RE_SIGA_MCS_SHIFT);
534 		nss = ((plcp0 & HE_SU_RE_SIGA_NSTS_MASK) >> HE_SU_RE_SIGA_NSTS_SHIFT) + 1;
535 		rate |= ((nss << HE_SU_PLCP2RATE_NSS_SHIFT) | WF_NON_HT_MCS);
536 	} else {
537 		/* For MU frames return rate in MCS:LTF-GI format */
538 		rate = (plcp0 & HE_MU_SIGA_SIGB_MCS_MASK) >> HE_MU_SIGA_SIGB_MCS_SHIFT;
539 		he_ltf_gi = (plcp0 & HE_MU_SIGA_GI_LTF_MASK) >> HE_MU_SIGA_GI_LTF_SHIFT;
540 		stbc = (plcp1 & HE_MU_SIGA_STBC_MASK) >> HE_MU_SIGA_STBC_SHIFT;
541 
542 		/* LTF-GI shall take the same position as NSS */
543 		rate |= (he_ltf_gi << HE_MU_PLCP2RATE_LTF_GI_SHIFT);
544 
545 		/* STBC needs to be filled in bit 6 */
546 		rate |= (stbc << HE_MU_PLCP2RATE_STBC_SHIFT);
547 	}
548 
549 	return rate;
550 }
551 
552 /**
553  * Function for computing NSS:MCS from EHT SU PLCP or
554  * MCS:LTF-GI from EHT MU PLCP
555  *
556  * TODO: add link to the HW spec.
557  * FIXME: do we really need to support mu?
558  */
559 uint8
wf_eht_plcp_to_rate(uint8 * plcp,bool is_mu)560 wf_eht_plcp_to_rate(uint8 *plcp, bool is_mu)
561 {
562 	BCM_REFERENCE(plcp);
563 	BCM_REFERENCE(is_mu);
564 	ASSERT(!"wf_eht_plcp_to_rate: not implemented!");
565 	return 0;
566 }
567 
568 /* ============================================ */
569 /* Moved from wlc_rate_def.c                    */
570 /* ============================================ */
571 
572 /**
573  * Some functions require a single stream MCS as an input parameter. Given an MCS, this function
574  * returns the single spatial stream MCS equivalent.
575  */
576 uint8
wf_get_single_stream_mcs(uint mcs)577 wf_get_single_stream_mcs(uint mcs)
578 {
579 	if (mcs < 32) {
580 		return mcs % 8;
581 	}
582 	switch (mcs) {
583 	case 32:
584 		return 32;
585 	case 87:
586 	case 99:
587 	case 101:
588 		return 87;	/* MCS 87: SS 1, MOD: 256QAM, CR 3/4 */
589 	default:
590 		return 88;	/* MCS 88: SS 1, MOD: 256QAM, CR 5/6 */
591 	}
592 }
593 
594 /* ============================================ */
595 /* Moved from wlc_phy_iovar.c                   */
596 /* ============================================ */
597 
598 const uint8 plcp_ofdm_rate_tbl[] = {
599 	DOT11_RATE_48M, /* 8: 48Mbps */
600 	DOT11_RATE_24M, /* 9: 24Mbps */
601 	DOT11_RATE_12M, /* A: 12Mbps */
602 	DOT11_RATE_6M,  /* B:  6Mbps */
603 	DOT11_RATE_54M, /* C: 54Mbps */
604 	DOT11_RATE_36M, /* D: 36Mbps */
605 	DOT11_RATE_18M, /* E: 18Mbps */
606 	DOT11_RATE_9M   /* F:  9Mbps */
607 };
608