Commit | Line | Data |
---|---|---|
9f66a397 GG |
1 | /****************************************************************************** |
2 | * | |
3 | * This file is provided under a dual BSD/GPLv2 license. When using or | |
4 | * redistributing this file, you may do so under either license. | |
5 | * | |
6 | * GPL LICENSE SUMMARY | |
7 | * | |
8 | * Copyright(c) 2017 Intel Deutschland GmbH | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of version 2 of the GNU General Public License as | |
12 | * published by the Free Software Foundation. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, but | |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * General Public License for more details. | |
18 | * | |
19 | * The full GNU General Public License is included in this distribution | |
20 | * in the file called COPYING. | |
21 | * | |
22 | * Contact Information: | |
23 | * Intel Linux Wireless <linuxwifi@intel.com> | |
24 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 | |
25 | * | |
26 | * BSD LICENSE | |
27 | * | |
28 | * Copyright(c) 2017 Intel Deutschland GmbH | |
29 | * All rights reserved. | |
30 | * | |
31 | * Redistribution and use in source and binary forms, with or without | |
32 | * modification, are permitted provided that the following conditions | |
33 | * are met: | |
34 | * | |
35 | * * Redistributions of source code must retain the above copyright | |
36 | * notice, this list of conditions and the following disclaimer. | |
37 | * * Redistributions in binary form must reproduce the above copyright | |
38 | * notice, this list of conditions and the following disclaimer in | |
39 | * the documentation and/or other materials provided with the | |
40 | * distribution. | |
41 | * * Neither the name Intel Corporation nor the names of its | |
42 | * contributors may be used to endorse or promote products derived | |
43 | * from this software without specific prior written permission. | |
44 | * | |
45 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
46 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
47 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
48 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
49 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
50 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
51 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
52 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
53 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
54 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
55 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
56 | * | |
57 | *****************************************************************************/ | |
58 | #include "rs.h" | |
59 | #include "fw-api.h" | |
60 | #include "sta.h" | |
61 | #include "iwl-op-mode.h" | |
62 | #include "mvm.h" | |
63 | ||
110b32f0 GG |
64 | static u8 rs_fw_bw_from_sta_bw(struct ieee80211_sta *sta) |
65 | { | |
66 | switch (sta->bandwidth) { | |
67 | case IEEE80211_STA_RX_BW_160: | |
68 | return IWL_TLC_MNG_MAX_CH_WIDTH_160MHZ; | |
69 | case IEEE80211_STA_RX_BW_80: | |
70 | return IWL_TLC_MNG_MAX_CH_WIDTH_80MHZ; | |
71 | case IEEE80211_STA_RX_BW_40: | |
72 | return IWL_TLC_MNG_MAX_CH_WIDTH_40MHZ; | |
73 | case IEEE80211_STA_RX_BW_20: | |
74 | default: | |
75 | return IWL_TLC_MNG_MAX_CH_WIDTH_20MHZ; | |
76 | } | |
77 | } | |
78 | ||
79 | static u8 rs_fw_set_active_chains(u8 chains) | |
80 | { | |
81 | u8 fw_chains = 0; | |
82 | ||
83 | if (chains & ANT_A) | |
84 | fw_chains |= IWL_TLC_MNG_CHAIN_A_MSK; | |
85 | if (chains & ANT_B) | |
86 | fw_chains |= IWL_TLC_MNG_CHAIN_B_MSK; | |
87 | if (chains & ANT_C) | |
88 | fw_chains |= IWL_TLC_MNG_CHAIN_C_MSK; | |
89 | ||
90 | return fw_chains; | |
91 | } | |
92 | ||
93 | static u8 rs_fw_sgi_cw_support(struct ieee80211_sta *sta) | |
94 | { | |
95 | struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; | |
96 | struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; | |
97 | u8 supp = 0; | |
98 | ||
99 | if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20) | |
100 | supp |= IWL_TLC_MNG_SGI_20MHZ_MSK; | |
101 | if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40) | |
102 | supp |= IWL_TLC_MNG_SGI_40MHZ_MSK; | |
103 | if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80) | |
104 | supp |= IWL_TLC_MNG_SGI_80MHZ_MSK; | |
105 | if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_160) | |
106 | supp |= IWL_TLC_MNG_SGI_160MHZ_MSK; | |
107 | ||
108 | return supp; | |
109 | } | |
110 | ||
111 | static u16 rs_fw_set_config_flags(struct iwl_mvm *mvm, | |
112 | struct ieee80211_sta *sta) | |
113 | { | |
114 | struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; | |
115 | struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; | |
116 | bool vht_ena = vht_cap && vht_cap->vht_supported; | |
117 | u16 flags = IWL_TLC_MNG_CFG_FLAGS_CCK_MSK | | |
118 | IWL_TLC_MNG_CFG_FLAGS_DCM_MSK | | |
119 | IWL_TLC_MNG_CFG_FLAGS_DD_MSK; | |
120 | ||
121 | if (mvm->cfg->ht_params->stbc && | |
122 | (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && | |
123 | ((ht_cap && (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC)) || | |
124 | (vht_ena && (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK)))) | |
125 | flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK; | |
126 | ||
127 | if (mvm->cfg->ht_params->ldpc && | |
128 | ((ht_cap && (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING)) || | |
129 | (vht_ena && (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC)))) | |
130 | flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; | |
131 | ||
132 | if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_BEAMFORMER) && | |
133 | (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && | |
134 | (vht_cap->cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)) | |
135 | flags |= IWL_TLC_MNG_CFG_FLAGS_BF_MSK; | |
136 | ||
137 | return flags; | |
138 | } | |
139 | ||
140 | static | |
141 | int rs_fw_vht_highest_rx_mcs_index(struct ieee80211_sta_vht_cap *vht_cap, | |
142 | int nss) | |
143 | { | |
144 | u16 rx_mcs = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) & | |
145 | (0x3 << (2 * (nss - 1))); | |
146 | rx_mcs >>= (2 * (nss - 1)); | |
147 | ||
148 | switch (rx_mcs) { | |
149 | case IEEE80211_VHT_MCS_SUPPORT_0_7: | |
150 | return IWL_TLC_MNG_HT_RATE_MCS7; | |
151 | case IEEE80211_VHT_MCS_SUPPORT_0_8: | |
152 | return IWL_TLC_MNG_HT_RATE_MCS8; | |
153 | case IEEE80211_VHT_MCS_SUPPORT_0_9: | |
154 | return IWL_TLC_MNG_HT_RATE_MCS9; | |
155 | default: | |
156 | WARN_ON_ONCE(1); | |
157 | break; | |
158 | } | |
159 | ||
160 | return 0; | |
161 | } | |
162 | ||
163 | static void rs_fw_vht_set_enabled_rates(struct ieee80211_sta *sta, | |
164 | struct ieee80211_sta_vht_cap *vht_cap, | |
165 | struct iwl_tlc_config_cmd *cmd) | |
166 | { | |
167 | u16 supp; | |
168 | int i, highest_mcs; | |
169 | ||
170 | for (i = 0; i < sta->rx_nss; i++) { | |
171 | if (i == MAX_RS_ANT_NUM) | |
172 | break; | |
173 | ||
174 | highest_mcs = rs_fw_vht_highest_rx_mcs_index(vht_cap, i + 1); | |
175 | if (!highest_mcs) | |
176 | continue; | |
177 | ||
178 | supp = BIT(highest_mcs + 1) - 1; | |
179 | if (sta->bandwidth == IEEE80211_STA_RX_BW_20) | |
180 | supp &= ~BIT(IWL_TLC_MNG_HT_RATE_MCS9); | |
181 | ||
182 | cmd->ht_supp_rates[i] = cpu_to_le16(supp); | |
183 | } | |
184 | } | |
185 | ||
186 | static void rs_fw_set_supp_rates(struct ieee80211_sta *sta, | |
187 | struct ieee80211_supported_band *sband, | |
188 | struct iwl_tlc_config_cmd *cmd) | |
189 | { | |
190 | int i; | |
191 | unsigned long tmp; | |
192 | unsigned long supp; /* must be unsigned long for for_each_set_bit */ | |
193 | struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; | |
194 | struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; | |
195 | ||
196 | /* non HT rates */ | |
197 | supp = 0; | |
198 | tmp = sta->supp_rates[sband->band]; | |
199 | for_each_set_bit(i, &tmp, BITS_PER_LONG) | |
200 | supp |= BIT(sband->bitrates[i].hw_value); | |
201 | ||
202 | cmd->non_ht_supp_rates = cpu_to_le16(supp); | |
203 | cmd->mode = IWL_TLC_MNG_MODE_NON_HT; | |
204 | ||
205 | /* HT/VHT rates */ | |
206 | if (vht_cap && vht_cap->vht_supported) { | |
207 | cmd->mode = IWL_TLC_MNG_MODE_VHT; | |
208 | rs_fw_vht_set_enabled_rates(sta, vht_cap, cmd); | |
209 | } else if (ht_cap && ht_cap->ht_supported) { | |
210 | cmd->mode = IWL_TLC_MNG_MODE_HT; | |
211 | cmd->ht_supp_rates[0] = cpu_to_le16(ht_cap->mcs.rx_mask[0]); | |
212 | cmd->ht_supp_rates[1] = cpu_to_le16(ht_cap->mcs.rx_mask[1]); | |
213 | } | |
214 | } | |
215 | ||
46d372af GG |
216 | static void rs_fw_tlc_mng_notif_req_config(struct iwl_mvm *mvm, u8 sta_id) |
217 | { | |
218 | u32 cmd_id = iwl_cmd_id(TLC_MNG_NOTIF_REQ_CMD, DATA_PATH_GROUP, 0); | |
219 | struct iwl_tlc_notif_req_config_cmd cfg_cmd = { | |
220 | .sta_id = sta_id, | |
221 | .flags = cpu_to_le16(IWL_TLC_NOTIF_INIT_RATE_MSK), | |
222 | .interval = cpu_to_le16(IWL_TLC_NOTIF_REQ_INTERVAL), | |
223 | }; | |
224 | int ret; | |
225 | ||
226 | ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cfg_cmd), &cfg_cmd); | |
227 | if (ret) | |
228 | IWL_ERR(mvm, "Failed to send TLC notif request (%d)\n", ret); | |
229 | } | |
230 | ||
231 | void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) | |
232 | { | |
233 | struct iwl_tlc_update_notif *notif; | |
234 | struct iwl_mvm_sta *mvmsta; | |
235 | struct iwl_lq_sta_rs_fw *lq_sta; | |
236 | ||
237 | notif = (void *)pkt->data; | |
238 | mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, notif->sta_id); | |
239 | ||
240 | if (!mvmsta) { | |
241 | IWL_ERR(mvm, "Invalid sta id (%d) in FW TLC notification\n", | |
242 | notif->sta_id); | |
243 | return; | |
244 | } | |
245 | ||
246 | lq_sta = &mvmsta->lq_sta.rs_fw; | |
247 | ||
248 | if (le16_to_cpu(notif->flags) & IWL_TLC_NOTIF_INIT_RATE_MSK) { | |
249 | lq_sta->last_rate_n_flags = | |
250 | le32_to_cpu(notif->values[IWL_TLC_NOTIF_INIT_RATE_POS]); | |
251 | IWL_DEBUG_RATE(mvm, "new rate_n_flags: 0x%X\n", | |
252 | lq_sta->last_rate_n_flags); | |
253 | } | |
254 | } | |
255 | ||
9f66a397 GG |
256 | void rs_fw_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, |
257 | enum nl80211_band band) | |
258 | { | |
110b32f0 GG |
259 | struct ieee80211_hw *hw = mvm->hw; |
260 | struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); | |
261 | struct iwl_lq_sta_rs_fw *lq_sta = &mvmsta->lq_sta.rs_fw; | |
262 | u32 cmd_id = iwl_cmd_id(TLC_MNG_CONFIG_CMD, DATA_PATH_GROUP, 0); | |
263 | struct ieee80211_supported_band *sband; | |
264 | struct iwl_tlc_config_cmd cfg_cmd = { | |
265 | .sta_id = mvmsta->sta_id, | |
266 | .max_supp_ch_width = rs_fw_bw_from_sta_bw(sta), | |
267 | .flags = cpu_to_le16(rs_fw_set_config_flags(mvm, sta)), | |
268 | .chains = rs_fw_set_active_chains(iwl_mvm_get_valid_tx_ant(mvm)), | |
269 | .max_supp_ss = sta->rx_nss, | |
270 | .max_ampdu_cnt = cpu_to_le32(mvmsta->max_agg_bufsize), | |
271 | .sgi_ch_width_supp = rs_fw_sgi_cw_support(sta), | |
272 | }; | |
273 | int ret; | |
274 | ||
275 | memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers)); | |
276 | ||
277 | #ifdef CONFIG_IWLWIFI_DEBUGFS | |
278 | iwl_mvm_reset_frame_stats(mvm); | |
279 | #endif | |
280 | sband = hw->wiphy->bands[band]; | |
281 | rs_fw_set_supp_rates(sta, sband, &cfg_cmd); | |
282 | ||
283 | ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cfg_cmd), &cfg_cmd); | |
284 | if (ret) | |
285 | IWL_ERR(mvm, "Failed to send rate scale config (%d)\n", ret); | |
46d372af GG |
286 | |
287 | rs_fw_tlc_mng_notif_req_config(mvm, cfg_cmd.sta_id); | |
110b32f0 GG |
288 | } |
289 | ||
290 | int rs_fw_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, | |
291 | bool enable) | |
292 | { | |
293 | /* TODO: need to introduce a new FW cmd since LQ cmd is not relevant */ | |
294 | IWL_DEBUG_RATE(mvm, "tx protection - not implemented yet.\n"); | |
295 | return 0; | |
9f66a397 GG |
296 | } |
297 | ||
298 | void iwl_mvm_rs_add_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta) | |
299 | { | |
300 | struct iwl_lq_sta_rs_fw *lq_sta = &mvmsta->lq_sta.rs_fw; | |
301 | ||
302 | IWL_DEBUG_RATE(mvm, "create station rate scale window\n"); | |
303 | ||
304 | lq_sta->pers.drv = mvm; | |
305 | lq_sta->pers.sta_id = mvmsta->sta_id; | |
306 | lq_sta->pers.chains = 0; | |
307 | memset(lq_sta->pers.chain_signal, 0, sizeof(lq_sta->pers.chain_signal)); | |
308 | lq_sta->pers.last_rssi = S8_MIN; | |
309 | lq_sta->last_rate_n_flags = 0; | |
310 | ||
59365b9e | 311 | #ifdef CONFIG_MAC80211_DEBUGFS |
9f66a397 GG |
312 | lq_sta->pers.dbg_fixed_rate = 0; |
313 | #endif | |
314 | } |