Loading...
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
8static 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
25static 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
33static 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
49int 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
92int 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
222send_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
236int 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 /* mac80211 thought we have the link, but it was never configured */
246 if (WARN_ON(!link_info ||
247 link_info->fw_link_id >= ARRAY_SIZE(mvm->link_id_to_link_conf)))
248 return 0;
249
250 RCU_INIT_POINTER(mvm->link_id_to_link_conf[link_info->fw_link_id],
251 NULL);
252 cmd.link_id = cpu_to_le32(link_info->fw_link_id);
253 iwl_mvm_release_fw_link_id(mvm, link_info->fw_link_id);
254 link_info->fw_link_id = IWL_MVM_FW_LINK_ID_INVALID;
255 cmd.spec_link_id = link_conf->link_id;
256 cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID);
257
258 ret = iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_REMOVE);
259
260 if (!ret)
261 if (iwl_mvm_sf_update(mvm, vif, true))
262 IWL_ERR(mvm, "Failed to update SF state\n");
263
264 return ret;
265}
266
267/* link should be deactivated before removal, so in most cases we need to
268 * perform these two operations together
269 */
270int iwl_mvm_disable_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
271 struct ieee80211_bss_conf *link_conf)
272{
273 int ret;
274
275 ret = iwl_mvm_link_changed(mvm, vif, link_conf,
276 LINK_CONTEXT_MODIFY_ACTIVE, false);
277 if (ret)
278 return ret;
279
280 ret = iwl_mvm_remove_link(mvm, vif, link_conf);
281 if (ret)
282 return ret;
283
284 return ret;
285}
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2/*
3 * Copyright (C) 2022 - 2024 Intel Corporation
4 */
5#include "mvm.h"
6#include "time-event.h"
7
8static 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
25static 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
33static 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
49int iwl_mvm_set_link_mapping(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 struct iwl_mvm_vif_link_info *link_info =
54 mvmvif->link[link_conf->link_id];
55
56 if (link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID) {
57 link_info->fw_link_id = iwl_mvm_get_free_fw_link_id(mvm,
58 mvmvif);
59 if (link_info->fw_link_id >=
60 ARRAY_SIZE(mvm->link_id_to_link_conf))
61 return -EINVAL;
62
63 rcu_assign_pointer(mvm->link_id_to_link_conf[link_info->fw_link_id],
64 link_conf);
65 }
66
67 return 0;
68}
69
70int iwl_mvm_add_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
71 struct ieee80211_bss_conf *link_conf)
72{
73 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
74 unsigned int link_id = link_conf->link_id;
75 struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id];
76 struct iwl_link_config_cmd cmd = {};
77 unsigned int cmd_id = WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD);
78 u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 1);
79 int ret;
80
81 if (WARN_ON_ONCE(!link_info))
82 return -EINVAL;
83
84 ret = iwl_mvm_set_link_mapping(mvm, vif, link_conf);
85 if (ret)
86 return ret;
87
88 /* Update SF - Disable if needed. if this fails, SF might still be on
89 * while many macs are bound, which is forbidden - so fail the binding.
90 */
91 if (iwl_mvm_sf_update(mvm, vif, false))
92 return -EINVAL;
93
94 cmd.link_id = cpu_to_le32(link_info->fw_link_id);
95 cmd.mac_id = cpu_to_le32(mvmvif->id);
96 cmd.spec_link_id = link_conf->link_id;
97 WARN_ON_ONCE(link_info->phy_ctxt);
98 cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID);
99
100 memcpy(cmd.local_link_addr, link_conf->addr, ETH_ALEN);
101
102 if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid)
103 memcpy(cmd.ibss_bssid_addr, link_conf->bssid, ETH_ALEN);
104
105 if (cmd_ver < 2)
106 cmd.listen_lmac = cpu_to_le32(link_info->listen_lmac);
107
108 return iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_ADD);
109}
110
111int iwl_mvm_link_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
112 struct ieee80211_bss_conf *link_conf,
113 u32 changes, bool active)
114{
115 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
116 unsigned int link_id = link_conf->link_id;
117 struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id];
118 struct iwl_mvm_phy_ctxt *phyctxt;
119 struct iwl_link_config_cmd cmd = {};
120 u32 ht_flag, flags = 0, flags_mask = 0;
121 int ret;
122 unsigned int cmd_id = WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD);
123 u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 1);
124
125 if (WARN_ON_ONCE(!link_info ||
126 link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID))
127 return -EINVAL;
128
129 if (changes & LINK_CONTEXT_MODIFY_ACTIVE) {
130 /* When activating a link, phy context should be valid;
131 * when deactivating a link, it also should be valid since
132 * the link was active before. So, do nothing in this case.
133 * Since a link is added first with FW_CTXT_INVALID, then we
134 * can get here in case it's removed before it was activated.
135 */
136 if (!link_info->phy_ctxt)
137 return 0;
138
139 /* Catch early if driver tries to activate or deactivate a link
140 * twice.
141 */
142 WARN_ON_ONCE(active == link_info->active);
143
144 /* When deactivating a link session protection should
145 * be stopped
146 */
147 if (!active && vif->type == NL80211_IFTYPE_STATION)
148 iwl_mvm_stop_session_protection(mvm, vif);
149 }
150
151 cmd.link_id = cpu_to_le32(link_info->fw_link_id);
152
153 /* The phy_id, link address and listen_lmac can be modified only until
154 * the link becomes active, otherwise they will be ignored.
155 */
156 phyctxt = link_info->phy_ctxt;
157 if (phyctxt)
158 cmd.phy_id = cpu_to_le32(phyctxt->id);
159 else
160 cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID);
161 cmd.mac_id = cpu_to_le32(mvmvif->id);
162
163 memcpy(cmd.local_link_addr, link_conf->addr, ETH_ALEN);
164
165 cmd.active = cpu_to_le32(active);
166
167 if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid)
168 memcpy(cmd.ibss_bssid_addr, link_conf->bssid, ETH_ALEN);
169
170 iwl_mvm_set_fw_basic_rates(mvm, vif, link_conf,
171 &cmd.cck_rates, &cmd.ofdm_rates);
172
173 cmd.cck_short_preamble = cpu_to_le32(link_conf->use_short_preamble);
174 cmd.short_slot = cpu_to_le32(link_conf->use_short_slot);
175
176 /* The fw does not distinguish between ht and fat */
177 ht_flag = LINK_PROT_FLG_HT_PROT | LINK_PROT_FLG_FAT_PROT;
178 iwl_mvm_set_fw_protection_flags(mvm, vif, link_conf,
179 &cmd.protection_flags,
180 ht_flag, LINK_PROT_FLG_TGG_PROTECT);
181
182 iwl_mvm_set_fw_qos_params(mvm, vif, link_conf, cmd.ac,
183 &cmd.qos_flags);
184
185
186 cmd.bi = cpu_to_le32(link_conf->beacon_int);
187 cmd.dtim_interval = cpu_to_le32(link_conf->beacon_int *
188 link_conf->dtim_period);
189
190 if (!link_conf->he_support || iwlwifi_mod_params.disable_11ax ||
191 (vif->type == NL80211_IFTYPE_STATION && !vif->cfg.assoc)) {
192 changes &= ~LINK_CONTEXT_MODIFY_HE_PARAMS;
193 goto send_cmd;
194 }
195
196 cmd.htc_trig_based_pkt_ext = link_conf->htc_trig_based_pkt_ext;
197
198 if (link_conf->uora_exists) {
199 cmd.rand_alloc_ecwmin =
200 link_conf->uora_ocw_range & 0x7;
201 cmd.rand_alloc_ecwmax =
202 (link_conf->uora_ocw_range >> 3) & 0x7;
203 }
204
205 /* TODO how to set ndp_fdbk_buff_th_exp? */
206
207 if (iwl_mvm_set_fw_mu_edca_params(mvm, mvmvif->link[link_id],
208 &cmd.trig_based_txf[0])) {
209 flags |= LINK_FLG_MU_EDCA_CW;
210 flags_mask |= LINK_FLG_MU_EDCA_CW;
211 }
212
213 if (changes & LINK_CONTEXT_MODIFY_EHT_PARAMS) {
214 struct ieee80211_chanctx_conf *ctx;
215 struct cfg80211_chan_def *def = NULL;
216
217 rcu_read_lock();
218 ctx = rcu_dereference(link_conf->chanctx_conf);
219 if (ctx)
220 def = iwl_mvm_chanctx_def(mvm, ctx);
221
222 if (iwlwifi_mod_params.disable_11be ||
223 !link_conf->eht_support || !def ||
224 iwl_fw_lookup_cmd_ver(mvm->fw, PHY_CONTEXT_CMD, 1) >= 6)
225 changes &= ~LINK_CONTEXT_MODIFY_EHT_PARAMS;
226 else
227 cmd.puncture_mask = cpu_to_le16(def->punctured);
228 rcu_read_unlock();
229 }
230
231 cmd.bss_color = link_conf->he_bss_color.color;
232
233 if (!link_conf->he_bss_color.enabled) {
234 flags |= LINK_FLG_BSS_COLOR_DIS;
235 flags_mask |= LINK_FLG_BSS_COLOR_DIS;
236 }
237
238 cmd.frame_time_rts_th = cpu_to_le16(link_conf->frame_time_rts_th);
239
240 /* Block 26-tone RU OFDMA transmissions */
241 if (link_info->he_ru_2mhz_block) {
242 flags |= LINK_FLG_RU_2MHZ_BLOCK;
243 flags_mask |= LINK_FLG_RU_2MHZ_BLOCK;
244 }
245
246 if (link_conf->nontransmitted) {
247 ether_addr_copy(cmd.ref_bssid_addr,
248 link_conf->transmitter_bssid);
249 cmd.bssid_index = link_conf->bssid_index;
250 }
251
252send_cmd:
253 cmd.modify_mask = cpu_to_le32(changes);
254 cmd.flags = cpu_to_le32(flags);
255 cmd.flags_mask = cpu_to_le32(flags_mask);
256 cmd.spec_link_id = link_conf->link_id;
257 if (cmd_ver < 2)
258 cmd.listen_lmac = cpu_to_le32(link_info->listen_lmac);
259
260 ret = iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_MODIFY);
261 if (!ret && (changes & LINK_CONTEXT_MODIFY_ACTIVE))
262 link_info->active = active;
263
264 return ret;
265}
266
267int iwl_mvm_unset_link_mapping(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
268 struct ieee80211_bss_conf *link_conf)
269{
270 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
271 struct iwl_mvm_vif_link_info *link_info =
272 mvmvif->link[link_conf->link_id];
273
274 /* mac80211 thought we have the link, but it was never configured */
275 if (WARN_ON(!link_info ||
276 link_info->fw_link_id >=
277 ARRAY_SIZE(mvm->link_id_to_link_conf)))
278 return -EINVAL;
279
280 RCU_INIT_POINTER(mvm->link_id_to_link_conf[link_info->fw_link_id],
281 NULL);
282 iwl_mvm_release_fw_link_id(mvm, link_info->fw_link_id);
283 return 0;
284}
285
286int iwl_mvm_remove_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
287 struct ieee80211_bss_conf *link_conf)
288{
289 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
290 unsigned int link_id = link_conf->link_id;
291 struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id];
292 struct iwl_link_config_cmd cmd = {};
293 int ret;
294
295 ret = iwl_mvm_unset_link_mapping(mvm, vif, link_conf);
296 if (ret)
297 return 0;
298
299 cmd.link_id = cpu_to_le32(link_info->fw_link_id);
300 link_info->fw_link_id = IWL_MVM_FW_LINK_ID_INVALID;
301 cmd.spec_link_id = link_conf->link_id;
302 cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID);
303
304 ret = iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_REMOVE);
305
306 if (!ret)
307 if (iwl_mvm_sf_update(mvm, vif, true))
308 IWL_ERR(mvm, "Failed to update SF state\n");
309
310 return ret;
311}
312
313/* link should be deactivated before removal, so in most cases we need to
314 * perform these two operations together
315 */
316int iwl_mvm_disable_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
317 struct ieee80211_bss_conf *link_conf)
318{
319 int ret;
320
321 ret = iwl_mvm_link_changed(mvm, vif, link_conf,
322 LINK_CONTEXT_MODIFY_ACTIVE, false);
323 if (ret)
324 return ret;
325
326 ret = iwl_mvm_remove_link(mvm, vif, link_conf);
327 if (ret)
328 return ret;
329
330 return ret;
331}