xref: /freebsd/sys/contrib/dev/iwlwifi/fw/rs.c (revision a4128aad8503277614f2d214011ef60a19447b83)
1bfcc09ddSBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2bfcc09ddSBjoern A. Zeeb /*
3*9af1bba4SBjoern A. Zeeb  * Copyright (C) 2021-2022 Intel Corporation
4bfcc09ddSBjoern A. Zeeb  */
5bfcc09ddSBjoern A. Zeeb 
6bfcc09ddSBjoern A. Zeeb #include <net/mac80211.h>
7bfcc09ddSBjoern A. Zeeb #include "fw/api/rs.h"
8bfcc09ddSBjoern A. Zeeb #include "iwl-drv.h"
9bfcc09ddSBjoern A. Zeeb #include "iwl-config.h"
10bfcc09ddSBjoern A. Zeeb 
11bfcc09ddSBjoern A. Zeeb #define IWL_DECLARE_RATE_INFO(r) \
12bfcc09ddSBjoern A. Zeeb 	[IWL_RATE_##r##M_INDEX] = IWL_RATE_##r##M_PLCP
13bfcc09ddSBjoern A. Zeeb 
14bfcc09ddSBjoern A. Zeeb /*
15bfcc09ddSBjoern A. Zeeb  * Translate from fw_rate_index (IWL_RATE_XXM_INDEX) to PLCP
16bfcc09ddSBjoern A. Zeeb  * */
17bfcc09ddSBjoern A. Zeeb static const u8 fw_rate_idx_to_plcp[IWL_RATE_COUNT] = {
18bfcc09ddSBjoern A. Zeeb 	IWL_DECLARE_RATE_INFO(1),
19bfcc09ddSBjoern A. Zeeb 	IWL_DECLARE_RATE_INFO(2),
20bfcc09ddSBjoern A. Zeeb 	IWL_DECLARE_RATE_INFO(5),
21bfcc09ddSBjoern A. Zeeb 	IWL_DECLARE_RATE_INFO(11),
22bfcc09ddSBjoern A. Zeeb 	IWL_DECLARE_RATE_INFO(6),
23bfcc09ddSBjoern A. Zeeb 	IWL_DECLARE_RATE_INFO(9),
24bfcc09ddSBjoern A. Zeeb 	IWL_DECLARE_RATE_INFO(12),
25bfcc09ddSBjoern A. Zeeb 	IWL_DECLARE_RATE_INFO(18),
26bfcc09ddSBjoern A. Zeeb 	IWL_DECLARE_RATE_INFO(24),
27bfcc09ddSBjoern A. Zeeb 	IWL_DECLARE_RATE_INFO(36),
28bfcc09ddSBjoern A. Zeeb 	IWL_DECLARE_RATE_INFO(48),
29bfcc09ddSBjoern A. Zeeb 	IWL_DECLARE_RATE_INFO(54),
30bfcc09ddSBjoern A. Zeeb };
31bfcc09ddSBjoern A. Zeeb 
32bfcc09ddSBjoern A. Zeeb /* mbps, mcs */
33bfcc09ddSBjoern A. Zeeb static const struct iwl_rate_mcs_info rate_mcs[IWL_RATE_COUNT] = {
34bfcc09ddSBjoern A. Zeeb 	{  "1", "BPSK DSSS"},
35bfcc09ddSBjoern A. Zeeb 	{  "2", "QPSK DSSS"},
36bfcc09ddSBjoern A. Zeeb 	{"5.5", "BPSK CCK"},
37bfcc09ddSBjoern A. Zeeb 	{ "11", "QPSK CCK"},
38bfcc09ddSBjoern A. Zeeb 	{  "6", "BPSK 1/2"},
39bfcc09ddSBjoern A. Zeeb 	{  "9", "BPSK 1/2"},
40bfcc09ddSBjoern A. Zeeb 	{ "12", "QPSK 1/2"},
41bfcc09ddSBjoern A. Zeeb 	{ "18", "QPSK 3/4"},
42bfcc09ddSBjoern A. Zeeb 	{ "24", "16QAM 1/2"},
43bfcc09ddSBjoern A. Zeeb 	{ "36", "16QAM 3/4"},
44bfcc09ddSBjoern A. Zeeb 	{ "48", "64QAM 2/3"},
45bfcc09ddSBjoern A. Zeeb 	{ "54", "64QAM 3/4"},
46bfcc09ddSBjoern A. Zeeb 	{ "60", "64QAM 5/6"},
47bfcc09ddSBjoern A. Zeeb };
48bfcc09ddSBjoern A. Zeeb 
49bfcc09ddSBjoern A. Zeeb static const char * const ant_name[] = {
50bfcc09ddSBjoern A. Zeeb 	[ANT_NONE] = "None",
51bfcc09ddSBjoern A. Zeeb 	[ANT_A]    = "A",
52bfcc09ddSBjoern A. Zeeb 	[ANT_B]    = "B",
53bfcc09ddSBjoern A. Zeeb 	[ANT_AB]   = "AB",
54bfcc09ddSBjoern A. Zeeb };
55bfcc09ddSBjoern A. Zeeb 
56bfcc09ddSBjoern A. Zeeb static const char * const pretty_bw[] = {
57bfcc09ddSBjoern A. Zeeb 	"20Mhz",
58bfcc09ddSBjoern A. Zeeb 	"40Mhz",
59bfcc09ddSBjoern A. Zeeb 	"80Mhz",
60bfcc09ddSBjoern A. Zeeb 	"160 Mhz",
61bfcc09ddSBjoern A. Zeeb 	"320Mhz",
62bfcc09ddSBjoern A. Zeeb };
63bfcc09ddSBjoern A. Zeeb 
64bfcc09ddSBjoern A. Zeeb u8 iwl_fw_rate_idx_to_plcp(int idx)
65bfcc09ddSBjoern A. Zeeb {
66bfcc09ddSBjoern A. Zeeb 	return fw_rate_idx_to_plcp[idx];
67bfcc09ddSBjoern A. Zeeb }
68bfcc09ddSBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_fw_rate_idx_to_plcp);
69bfcc09ddSBjoern A. Zeeb 
70bfcc09ddSBjoern A. Zeeb const struct iwl_rate_mcs_info *iwl_rate_mcs(int idx)
71bfcc09ddSBjoern A. Zeeb {
72bfcc09ddSBjoern A. Zeeb 	return &rate_mcs[idx];
73bfcc09ddSBjoern A. Zeeb }
74bfcc09ddSBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_rate_mcs);
75bfcc09ddSBjoern A. Zeeb 
76bfcc09ddSBjoern A. Zeeb const char *iwl_rs_pretty_ant(u8 ant)
77bfcc09ddSBjoern A. Zeeb {
78bfcc09ddSBjoern A. Zeeb 	if (ant >= ARRAY_SIZE(ant_name))
79bfcc09ddSBjoern A. Zeeb 		return "UNKNOWN";
80bfcc09ddSBjoern A. Zeeb 
81bfcc09ddSBjoern A. Zeeb 	return ant_name[ant];
82bfcc09ddSBjoern A. Zeeb }
83bfcc09ddSBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_rs_pretty_ant);
84bfcc09ddSBjoern A. Zeeb 
85bfcc09ddSBjoern A. Zeeb const char *iwl_rs_pretty_bw(int bw)
86bfcc09ddSBjoern A. Zeeb {
87bfcc09ddSBjoern A. Zeeb 	if (bw >= ARRAY_SIZE(pretty_bw))
88bfcc09ddSBjoern A. Zeeb 		return "unknown bw";
89bfcc09ddSBjoern A. Zeeb 
90bfcc09ddSBjoern A. Zeeb 	return pretty_bw[bw];
91bfcc09ddSBjoern A. Zeeb }
92bfcc09ddSBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_rs_pretty_bw);
93bfcc09ddSBjoern A. Zeeb 
94fac1f593SBjoern A. Zeeb static u32 iwl_legacy_rate_to_fw_idx(u32 rate_n_flags)
95fac1f593SBjoern A. Zeeb {
96fac1f593SBjoern A. Zeeb 	int rate = rate_n_flags & RATE_LEGACY_RATE_MSK_V1;
97fac1f593SBjoern A. Zeeb 	int idx;
98fac1f593SBjoern A. Zeeb 	bool ofdm = !(rate_n_flags & RATE_MCS_CCK_MSK_V1);
99fac1f593SBjoern A. Zeeb 	int offset = ofdm ? IWL_FIRST_OFDM_RATE : 0;
100fac1f593SBjoern A. Zeeb 	int last = ofdm ? IWL_RATE_COUNT_LEGACY : IWL_FIRST_OFDM_RATE;
101fac1f593SBjoern A. Zeeb 
102fac1f593SBjoern A. Zeeb 	for (idx = offset; idx < last; idx++)
103fac1f593SBjoern A. Zeeb 		if (iwl_fw_rate_idx_to_plcp(idx) == rate)
104fac1f593SBjoern A. Zeeb 			return idx - offset;
105fac1f593SBjoern A. Zeeb 	return IWL_RATE_INVALID;
106fac1f593SBjoern A. Zeeb }
107fac1f593SBjoern A. Zeeb 
108bfcc09ddSBjoern A. Zeeb u32 iwl_new_rate_from_v1(u32 rate_v1)
109bfcc09ddSBjoern A. Zeeb {
110bfcc09ddSBjoern A. Zeeb 	u32 rate_v2 = 0;
111bfcc09ddSBjoern A. Zeeb 	u32 dup = 0;
112bfcc09ddSBjoern A. Zeeb 
113bfcc09ddSBjoern A. Zeeb 	if (rate_v1 == 0)
114bfcc09ddSBjoern A. Zeeb 		return rate_v1;
115bfcc09ddSBjoern A. Zeeb 	/* convert rate */
116bfcc09ddSBjoern A. Zeeb 	if (rate_v1 & RATE_MCS_HT_MSK_V1) {
117bfcc09ddSBjoern A. Zeeb 		u32 nss = 0;
118bfcc09ddSBjoern A. Zeeb 
119bfcc09ddSBjoern A. Zeeb 		rate_v2 |= RATE_MCS_HT_MSK;
120bfcc09ddSBjoern A. Zeeb 		rate_v2 |=
121bfcc09ddSBjoern A. Zeeb 			rate_v1 & RATE_HT_MCS_RATE_CODE_MSK_V1;
122bfcc09ddSBjoern A. Zeeb 		nss = (rate_v1 & RATE_HT_MCS_MIMO2_MSK) >>
123bfcc09ddSBjoern A. Zeeb 			RATE_HT_MCS_NSS_POS_V1;
124bfcc09ddSBjoern A. Zeeb 		rate_v2 |= nss << RATE_MCS_NSS_POS;
125bfcc09ddSBjoern A. Zeeb 	} else if (rate_v1 & RATE_MCS_VHT_MSK_V1 ||
126bfcc09ddSBjoern A. Zeeb 		   rate_v1 & RATE_MCS_HE_MSK_V1) {
127bfcc09ddSBjoern A. Zeeb 		rate_v2 |= rate_v1 & RATE_VHT_MCS_RATE_CODE_MSK;
128bfcc09ddSBjoern A. Zeeb 
129*9af1bba4SBjoern A. Zeeb 		rate_v2 |= rate_v1 & RATE_MCS_NSS_MSK;
130bfcc09ddSBjoern A. Zeeb 
131bfcc09ddSBjoern A. Zeeb 		if (rate_v1 & RATE_MCS_HE_MSK_V1) {
132bfcc09ddSBjoern A. Zeeb 			u32 he_type_bits = rate_v1 & RATE_MCS_HE_TYPE_MSK_V1;
133bfcc09ddSBjoern A. Zeeb 			u32 he_type = he_type_bits >> RATE_MCS_HE_TYPE_POS_V1;
134bfcc09ddSBjoern A. Zeeb 			u32 he_106t = (rate_v1 & RATE_MCS_HE_106T_MSK_V1) >>
135bfcc09ddSBjoern A. Zeeb 				RATE_MCS_HE_106T_POS_V1;
136bfcc09ddSBjoern A. Zeeb 			u32 he_gi_ltf = (rate_v1 & RATE_MCS_HE_GI_LTF_MSK_V1) >>
137bfcc09ddSBjoern A. Zeeb 				RATE_MCS_HE_GI_LTF_POS;
138bfcc09ddSBjoern A. Zeeb 
139bfcc09ddSBjoern A. Zeeb 			if ((he_type_bits == RATE_MCS_HE_TYPE_SU ||
140bfcc09ddSBjoern A. Zeeb 			     he_type_bits == RATE_MCS_HE_TYPE_EXT_SU) &&
141bfcc09ddSBjoern A. Zeeb 			    he_gi_ltf == RATE_MCS_HE_SU_4_LTF)
142bfcc09ddSBjoern A. Zeeb 				/* the new rate have an additional bit to
143bfcc09ddSBjoern A. Zeeb 				 * represent the value 4 rather then using SGI
144bfcc09ddSBjoern A. Zeeb 				 * bit for this purpose - as it was done in the old
145bfcc09ddSBjoern A. Zeeb 				 * rate */
146bfcc09ddSBjoern A. Zeeb 				he_gi_ltf += (rate_v1 & RATE_MCS_SGI_MSK_V1) >>
147bfcc09ddSBjoern A. Zeeb 					RATE_MCS_SGI_POS_V1;
148bfcc09ddSBjoern A. Zeeb 
149bfcc09ddSBjoern A. Zeeb 			rate_v2 |= he_gi_ltf << RATE_MCS_HE_GI_LTF_POS;
150bfcc09ddSBjoern A. Zeeb 			rate_v2 |= he_type << RATE_MCS_HE_TYPE_POS;
151bfcc09ddSBjoern A. Zeeb 			rate_v2 |= he_106t << RATE_MCS_HE_106T_POS;
152bfcc09ddSBjoern A. Zeeb 			rate_v2 |= rate_v1 & RATE_HE_DUAL_CARRIER_MODE_MSK;
153bfcc09ddSBjoern A. Zeeb 			rate_v2 |= RATE_MCS_HE_MSK;
154bfcc09ddSBjoern A. Zeeb 		} else {
155bfcc09ddSBjoern A. Zeeb 			rate_v2 |= RATE_MCS_VHT_MSK;
156bfcc09ddSBjoern A. Zeeb 		}
157bfcc09ddSBjoern A. Zeeb 	/* if legacy format */
158bfcc09ddSBjoern A. Zeeb 	} else {
159bfcc09ddSBjoern A. Zeeb 		u32 legacy_rate = iwl_legacy_rate_to_fw_idx(rate_v1);
160bfcc09ddSBjoern A. Zeeb 
161fac1f593SBjoern A. Zeeb 		if (WARN_ON_ONCE(legacy_rate == IWL_RATE_INVALID))
162fac1f593SBjoern A. Zeeb 			legacy_rate = (rate_v1 & RATE_MCS_CCK_MSK_V1) ?
163fac1f593SBjoern A. Zeeb 				IWL_FIRST_CCK_RATE : IWL_FIRST_OFDM_RATE;
164fac1f593SBjoern A. Zeeb 
165bfcc09ddSBjoern A. Zeeb 		rate_v2 |= legacy_rate;
166bfcc09ddSBjoern A. Zeeb 		if (!(rate_v1 & RATE_MCS_CCK_MSK_V1))
167bfcc09ddSBjoern A. Zeeb 			rate_v2 |= RATE_MCS_LEGACY_OFDM_MSK;
168bfcc09ddSBjoern A. Zeeb 	}
169bfcc09ddSBjoern A. Zeeb 
170bfcc09ddSBjoern A. Zeeb 	/* convert flags */
171bfcc09ddSBjoern A. Zeeb 	if (rate_v1 & RATE_MCS_LDPC_MSK_V1)
172bfcc09ddSBjoern A. Zeeb 		rate_v2 |= RATE_MCS_LDPC_MSK;
173bfcc09ddSBjoern A. Zeeb 	rate_v2 |= (rate_v1 & RATE_MCS_CHAN_WIDTH_MSK_V1) |
174bfcc09ddSBjoern A. Zeeb 		(rate_v1 & RATE_MCS_ANT_AB_MSK) |
175bfcc09ddSBjoern A. Zeeb 		(rate_v1 & RATE_MCS_STBC_MSK) |
176bfcc09ddSBjoern A. Zeeb 		(rate_v1 & RATE_MCS_BF_MSK);
177bfcc09ddSBjoern A. Zeeb 
178bfcc09ddSBjoern A. Zeeb 	dup = (rate_v1 & RATE_MCS_DUP_MSK_V1) >> RATE_MCS_DUP_POS_V1;
179bfcc09ddSBjoern A. Zeeb 	if (dup) {
180bfcc09ddSBjoern A. Zeeb 		rate_v2 |= RATE_MCS_DUP_MSK;
181bfcc09ddSBjoern A. Zeeb 		rate_v2 |= dup << RATE_MCS_CHAN_WIDTH_POS;
182bfcc09ddSBjoern A. Zeeb 	}
183bfcc09ddSBjoern A. Zeeb 
184bfcc09ddSBjoern A. Zeeb 	if ((!(rate_v1 & RATE_MCS_HE_MSK_V1)) &&
185bfcc09ddSBjoern A. Zeeb 	    (rate_v1 & RATE_MCS_SGI_MSK_V1))
186bfcc09ddSBjoern A. Zeeb 		rate_v2 |= RATE_MCS_SGI_MSK;
187bfcc09ddSBjoern A. Zeeb 
188bfcc09ddSBjoern A. Zeeb 	return rate_v2;
189bfcc09ddSBjoern A. Zeeb }
190bfcc09ddSBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_new_rate_from_v1);
191bfcc09ddSBjoern A. Zeeb 
192bfcc09ddSBjoern A. Zeeb int rs_pretty_print_rate(char *buf, int bufsz, const u32 rate)
193bfcc09ddSBjoern A. Zeeb {
194bfcc09ddSBjoern A. Zeeb 	char *type;
195bfcc09ddSBjoern A. Zeeb 	u8 mcs = 0, nss = 0;
196bfcc09ddSBjoern A. Zeeb 	u8 ant = (rate & RATE_MCS_ANT_AB_MSK) >> RATE_MCS_ANT_POS;
197bfcc09ddSBjoern A. Zeeb 	u32 bw = (rate & RATE_MCS_CHAN_WIDTH_MSK) >>
198bfcc09ddSBjoern A. Zeeb 		RATE_MCS_CHAN_WIDTH_POS;
199bfcc09ddSBjoern A. Zeeb 	u32 format = rate & RATE_MCS_MOD_TYPE_MSK;
200bfcc09ddSBjoern A. Zeeb 	bool sgi;
201bfcc09ddSBjoern A. Zeeb 
202bfcc09ddSBjoern A. Zeeb 	if (format == RATE_MCS_CCK_MSK ||
203bfcc09ddSBjoern A. Zeeb 	    format == RATE_MCS_LEGACY_OFDM_MSK) {
204bfcc09ddSBjoern A. Zeeb 		int legacy_rate = rate & RATE_LEGACY_RATE_MSK;
205bfcc09ddSBjoern A. Zeeb 		int index = format == RATE_MCS_CCK_MSK ?
206bfcc09ddSBjoern A. Zeeb 			legacy_rate :
207bfcc09ddSBjoern A. Zeeb 			legacy_rate + IWL_FIRST_OFDM_RATE;
208bfcc09ddSBjoern A. Zeeb 
209bfcc09ddSBjoern A. Zeeb 		return scnprintf(buf, bufsz, "Legacy | ANT: %s Rate: %s Mbps",
210bfcc09ddSBjoern A. Zeeb 				 iwl_rs_pretty_ant(ant),
211bfcc09ddSBjoern A. Zeeb 				 iwl_rate_mcs(index)->mbps);
212bfcc09ddSBjoern A. Zeeb 	}
213bfcc09ddSBjoern A. Zeeb 
214bfcc09ddSBjoern A. Zeeb 	if (format ==  RATE_MCS_VHT_MSK)
215bfcc09ddSBjoern A. Zeeb 		type = "VHT";
216bfcc09ddSBjoern A. Zeeb 	else if (format ==  RATE_MCS_HT_MSK)
217bfcc09ddSBjoern A. Zeeb 		type = "HT";
218bfcc09ddSBjoern A. Zeeb 	else if (format == RATE_MCS_HE_MSK)
219bfcc09ddSBjoern A. Zeeb 		type = "HE";
220*9af1bba4SBjoern A. Zeeb 	else if (format == RATE_MCS_EHT_MSK)
221*9af1bba4SBjoern A. Zeeb 		type = "EHT";
222bfcc09ddSBjoern A. Zeeb 	else
223bfcc09ddSBjoern A. Zeeb 		type = "Unknown"; /* shouldn't happen */
224bfcc09ddSBjoern A. Zeeb 
225bfcc09ddSBjoern A. Zeeb 	mcs = format == RATE_MCS_HT_MSK ?
226bfcc09ddSBjoern A. Zeeb 		RATE_HT_MCS_INDEX(rate) :
227bfcc09ddSBjoern A. Zeeb 		rate & RATE_MCS_CODE_MSK;
228bfcc09ddSBjoern A. Zeeb 	nss = ((rate & RATE_MCS_NSS_MSK)
229bfcc09ddSBjoern A. Zeeb 	       >> RATE_MCS_NSS_POS) + 1;
230bfcc09ddSBjoern A. Zeeb 	sgi = format == RATE_MCS_HE_MSK ?
231bfcc09ddSBjoern A. Zeeb 		iwl_he_is_sgi(rate) :
232bfcc09ddSBjoern A. Zeeb 		rate & RATE_MCS_SGI_MSK;
233bfcc09ddSBjoern A. Zeeb 
234bfcc09ddSBjoern A. Zeeb 	return scnprintf(buf, bufsz,
235bfcc09ddSBjoern A. Zeeb 			 "0x%x: %s | ANT: %s BW: %s MCS: %d NSS: %d %s%s%s%s%s",
236bfcc09ddSBjoern A. Zeeb 			 rate, type, iwl_rs_pretty_ant(ant), iwl_rs_pretty_bw(bw), mcs, nss,
237bfcc09ddSBjoern A. Zeeb 			 (sgi) ? "SGI " : "NGI ",
238bfcc09ddSBjoern A. Zeeb 			 (rate & RATE_MCS_STBC_MSK) ? "STBC " : "",
239bfcc09ddSBjoern A. Zeeb 			 (rate & RATE_MCS_LDPC_MSK) ? "LDPC " : "",
240bfcc09ddSBjoern A. Zeeb 			 (rate & RATE_HE_DUAL_CARRIER_MODE_MSK) ? "DCM " : "",
241bfcc09ddSBjoern A. Zeeb 			 (rate & RATE_MCS_BF_MSK) ? "BF " : "");
242bfcc09ddSBjoern A. Zeeb }
243bfcc09ddSBjoern A. Zeeb IWL_EXPORT_SYMBOL(rs_pretty_print_rate);
244bfcc09ddSBjoern A. Zeeb 
245bfcc09ddSBjoern A. Zeeb bool iwl_he_is_sgi(u32 rate_n_flags)
246bfcc09ddSBjoern A. Zeeb {
247bfcc09ddSBjoern A. Zeeb 	u32 type = rate_n_flags & RATE_MCS_HE_TYPE_MSK;
248bfcc09ddSBjoern A. Zeeb 	u32 ltf_gi = rate_n_flags & RATE_MCS_HE_GI_LTF_MSK;
249bfcc09ddSBjoern A. Zeeb 
250bfcc09ddSBjoern A. Zeeb 	if (type == RATE_MCS_HE_TYPE_SU ||
251bfcc09ddSBjoern A. Zeeb 	    type == RATE_MCS_HE_TYPE_EXT_SU)
252bfcc09ddSBjoern A. Zeeb 		return ltf_gi == RATE_MCS_HE_SU_4_LTF_08_GI;
253bfcc09ddSBjoern A. Zeeb 	return false;
254bfcc09ddSBjoern A. Zeeb }
255bfcc09ddSBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_he_is_sgi);
256bfcc09ddSBjoern A. Zeeb 
257