1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3 * Copyright (C) 2022 - 2023 Intel Corporation
4 */
5 #include "mvm.h"
6 #include "time-event.h"
7
iwl_mvm_get_free_fw_link_id(struct iwl_mvm * mvm,struct iwl_mvm_vif * mvm_vif)8 static u32 iwl_mvm_get_free_fw_link_id(struct iwl_mvm *mvm,
9 struct iwl_mvm_vif *mvm_vif)
10 {
11 u32 link_id;
12
13 lockdep_assert_held(&mvm->mutex);
14
15 link_id = ffz(mvm->fw_link_ids_map);
16
17 /* this case can happen if there're deactivated but not removed links */
18 if (link_id > IWL_MVM_FW_MAX_LINK_ID)
19 return IWL_MVM_FW_LINK_ID_INVALID;
20
21 mvm->fw_link_ids_map |= BIT(link_id);
22 return link_id;
23 }
24
iwl_mvm_release_fw_link_id(struct iwl_mvm * mvm,u32 link_id)25 static void iwl_mvm_release_fw_link_id(struct iwl_mvm *mvm, u32 link_id)
26 {
27 lockdep_assert_held(&mvm->mutex);
28
29 if (!WARN_ON(link_id > IWL_MVM_FW_MAX_LINK_ID))
30 mvm->fw_link_ids_map &= ~BIT(link_id);
31 }
32
iwl_mvm_link_cmd_send(struct iwl_mvm * mvm,struct iwl_link_config_cmd * cmd,enum iwl_ctxt_action action)33 static int iwl_mvm_link_cmd_send(struct iwl_mvm *mvm,
34 struct iwl_link_config_cmd *cmd,
35 enum iwl_ctxt_action action)
36 {
37 int ret;
38
39 cmd->action = cpu_to_le32(action);
40 ret = iwl_mvm_send_cmd_pdu(mvm,
41 WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD), 0,
42 sizeof(*cmd), cmd);
43 if (ret)
44 IWL_ERR(mvm, "Failed to send LINK_CONFIG_CMD (action:%d): %d\n",
45 action, ret);
46 return ret;
47 }
48
iwl_mvm_add_link(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link_conf)49 int iwl_mvm_add_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
50 struct ieee80211_bss_conf *link_conf)
51 {
52 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
53 unsigned int link_id = link_conf->link_id;
54 struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id];
55 struct iwl_link_config_cmd cmd = {};
56
57 if (WARN_ON_ONCE(!link_info))
58 return -EINVAL;
59
60 if (link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID) {
61 link_info->fw_link_id = iwl_mvm_get_free_fw_link_id(mvm,
62 mvmvif);
63 if (link_info->fw_link_id >= ARRAY_SIZE(mvm->link_id_to_link_conf))
64 return -EINVAL;
65
66 rcu_assign_pointer(mvm->link_id_to_link_conf[link_info->fw_link_id],
67 link_conf);
68 }
69
70 /* Update SF - Disable if needed. if this fails, SF might still be on
71 * while many macs are bound, which is forbidden - so fail the binding.
72 */
73 if (iwl_mvm_sf_update(mvm, vif, false))
74 return -EINVAL;
75
76 cmd.link_id = cpu_to_le32(link_info->fw_link_id);
77 cmd.mac_id = cpu_to_le32(mvmvif->id);
78 cmd.spec_link_id = link_conf->link_id;
79 WARN_ON_ONCE(link_info->phy_ctxt);
80 cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID);
81
82 memcpy(cmd.local_link_addr, link_conf->addr, ETH_ALEN);
83
84 if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid)
85 memcpy(cmd.ibss_bssid_addr, link_conf->bssid, ETH_ALEN);
86
87 cmd.listen_lmac = cpu_to_le32(link_info->listen_lmac);
88
89 return iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_ADD);
90 }
91
iwl_mvm_link_changed(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link_conf,u32 changes,bool active)92 int iwl_mvm_link_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
93 struct ieee80211_bss_conf *link_conf,
94 u32 changes, bool active)
95 {
96 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
97 unsigned int link_id = link_conf->link_id;
98 struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id];
99 struct iwl_mvm_phy_ctxt *phyctxt;
100 struct iwl_link_config_cmd cmd = {};
101 u32 ht_flag, flags = 0, flags_mask = 0;
102 int ret;
103
104 if (WARN_ON_ONCE(!link_info ||
105 link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID))
106 return -EINVAL;
107
108 if (changes & LINK_CONTEXT_MODIFY_ACTIVE) {
109 /* When activating a link, phy context should be valid;
110 * when deactivating a link, it also should be valid since
111 * the link was active before. So, do nothing in this case.
112 * Since a link is added first with FW_CTXT_INVALID, then we
113 * can get here in case it's removed before it was activated.
114 */
115 if (!link_info->phy_ctxt)
116 return 0;
117
118 /* Catch early if driver tries to activate or deactivate a link
119 * twice.
120 */
121 WARN_ON_ONCE(active == link_info->active);
122
123 /* When deactivating a link session protection should
124 * be stopped
125 */
126 if (!active && vif->type == NL80211_IFTYPE_STATION)
127 iwl_mvm_stop_session_protection(mvm, vif);
128 }
129
130 cmd.link_id = cpu_to_le32(link_info->fw_link_id);
131
132 /* The phy_id, link address and listen_lmac can be modified only until
133 * the link becomes active, otherwise they will be ignored.
134 */
135 phyctxt = link_info->phy_ctxt;
136 if (phyctxt)
137 cmd.phy_id = cpu_to_le32(phyctxt->id);
138 else
139 cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID);
140 cmd.mac_id = cpu_to_le32(mvmvif->id);
141
142 memcpy(cmd.local_link_addr, link_conf->addr, ETH_ALEN);
143
144 cmd.active = cpu_to_le32(active);
145
146 if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid)
147 memcpy(cmd.ibss_bssid_addr, link_conf->bssid, ETH_ALEN);
148
149 iwl_mvm_set_fw_basic_rates(mvm, vif, link_conf,
150 &cmd.cck_rates, &cmd.ofdm_rates);
151
152 cmd.cck_short_preamble = cpu_to_le32(link_conf->use_short_preamble);
153 cmd.short_slot = cpu_to_le32(link_conf->use_short_slot);
154
155 /* The fw does not distinguish between ht and fat */
156 ht_flag = LINK_PROT_FLG_HT_PROT | LINK_PROT_FLG_FAT_PROT;
157 iwl_mvm_set_fw_protection_flags(mvm, vif, link_conf,
158 &cmd.protection_flags,
159 ht_flag, LINK_PROT_FLG_TGG_PROTECT);
160
161 iwl_mvm_set_fw_qos_params(mvm, vif, link_conf, cmd.ac,
162 &cmd.qos_flags);
163
164
165 cmd.bi = cpu_to_le32(link_conf->beacon_int);
166 cmd.dtim_interval = cpu_to_le32(link_conf->beacon_int *
167 link_conf->dtim_period);
168
169 if (!link_conf->he_support || iwlwifi_mod_params.disable_11ax ||
170 (vif->type == NL80211_IFTYPE_STATION && !vif->cfg.assoc)) {
171 changes &= ~LINK_CONTEXT_MODIFY_HE_PARAMS;
172 goto send_cmd;
173 }
174
175 cmd.htc_trig_based_pkt_ext = link_conf->htc_trig_based_pkt_ext;
176
177 if (link_conf->uora_exists) {
178 cmd.rand_alloc_ecwmin =
179 link_conf->uora_ocw_range & 0x7;
180 cmd.rand_alloc_ecwmax =
181 (link_conf->uora_ocw_range >> 3) & 0x7;
182 }
183
184 /* TODO how to set ndp_fdbk_buff_th_exp? */
185
186 if (iwl_mvm_set_fw_mu_edca_params(mvm, mvmvif->link[link_id],
187 &cmd.trig_based_txf[0])) {
188 flags |= LINK_FLG_MU_EDCA_CW;
189 flags_mask |= LINK_FLG_MU_EDCA_CW;
190 }
191
192 if (changes & LINK_CONTEXT_MODIFY_EHT_PARAMS) {
193 if (iwlwifi_mod_params.disable_11be ||
194 !link_conf->eht_support)
195 changes &= ~LINK_CONTEXT_MODIFY_EHT_PARAMS;
196 else
197 cmd.puncture_mask =
198 cpu_to_le16(link_conf->eht_puncturing);
199 }
200
201 cmd.bss_color = link_conf->he_bss_color.color;
202
203 if (!link_conf->he_bss_color.enabled) {
204 flags |= LINK_FLG_BSS_COLOR_DIS;
205 flags_mask |= LINK_FLG_BSS_COLOR_DIS;
206 }
207
208 cmd.frame_time_rts_th = cpu_to_le16(link_conf->frame_time_rts_th);
209
210 /* Block 26-tone RU OFDMA transmissions */
211 if (link_info->he_ru_2mhz_block) {
212 flags |= LINK_FLG_RU_2MHZ_BLOCK;
213 flags_mask |= LINK_FLG_RU_2MHZ_BLOCK;
214 }
215
216 if (link_conf->nontransmitted) {
217 ether_addr_copy(cmd.ref_bssid_addr,
218 link_conf->transmitter_bssid);
219 cmd.bssid_index = link_conf->bssid_index;
220 }
221
222 send_cmd:
223 cmd.modify_mask = cpu_to_le32(changes);
224 cmd.flags = cpu_to_le32(flags);
225 cmd.flags_mask = cpu_to_le32(flags_mask);
226 cmd.spec_link_id = link_conf->link_id;
227 cmd.listen_lmac = cpu_to_le32(link_info->listen_lmac);
228
229 ret = iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_MODIFY);
230 if (!ret && (changes & LINK_CONTEXT_MODIFY_ACTIVE))
231 link_info->active = active;
232
233 return ret;
234 }
235
iwl_mvm_remove_link(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link_conf)236 int iwl_mvm_remove_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
237 struct ieee80211_bss_conf *link_conf)
238 {
239 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
240 unsigned int link_id = link_conf->link_id;
241 struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id];
242 struct iwl_link_config_cmd cmd = {};
243 int ret;
244
245 if (WARN_ON(!link_info ||
246 link_info->fw_link_id >= ARRAY_SIZE(mvm->link_id_to_link_conf)))
247 return -EINVAL;
248
249 RCU_INIT_POINTER(mvm->link_id_to_link_conf[link_info->fw_link_id],
250 NULL);
251 cmd.link_id = cpu_to_le32(link_info->fw_link_id);
252 iwl_mvm_release_fw_link_id(mvm, link_info->fw_link_id);
253 link_info->fw_link_id = IWL_MVM_FW_LINK_ID_INVALID;
254 cmd.spec_link_id = link_conf->link_id;
255
256 ret = iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_REMOVE);
257
258 if (!ret)
259 if (iwl_mvm_sf_update(mvm, vif, true))
260 IWL_ERR(mvm, "Failed to update SF state\n");
261
262 return ret;
263 }
264
265 /* link should be deactivated before removal, so in most cases we need to
266 * perform these two operations together
267 */
iwl_mvm_disable_link(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link_conf)268 int iwl_mvm_disable_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
269 struct ieee80211_bss_conf *link_conf)
270 {
271 int ret;
272
273 ret = iwl_mvm_link_changed(mvm, vif, link_conf,
274 LINK_CONTEXT_MODIFY_ACTIVE, false);
275 if (ret)
276 return ret;
277
278 ret = iwl_mvm_remove_link(mvm, vif, link_conf);
279 if (ret)
280 return ret;
281
282 return ret;
283 }
284