Merge tag 'imx-fixes-6.4' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo...
[platform/kernel/linux-rpi.git] / drivers / net / wireless / intel / iwlwifi / mvm / link.c
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
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
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
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
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         struct iwl_mvm_phy_ctxt *phyctxt;
57
58         if (WARN_ON_ONCE(!link_info))
59                 return -EINVAL;
60
61         if (link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID) {
62                 link_info->fw_link_id = iwl_mvm_get_free_fw_link_id(mvm,
63                                                                     mvmvif);
64                 if (link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID)
65                         return -EINVAL;
66         }
67
68         /* Update SF - Disable if needed. if this fails, SF might still be on
69          * while many macs are bound, which is forbidden - so fail the binding.
70          */
71         if (iwl_mvm_sf_update(mvm, vif, false))
72                 return -EINVAL;
73
74         cmd.link_id = cpu_to_le32(link_info->fw_link_id);
75         cmd.mac_id = cpu_to_le32(mvmvif->id);
76         /* P2P-Device already has a valid PHY context during add */
77         phyctxt = link_info->phy_ctxt;
78         if (phyctxt)
79                 cmd.phy_id = cpu_to_le32(phyctxt->id);
80         else
81                 cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID);
82
83         memcpy(cmd.local_link_addr, link_conf->addr, ETH_ALEN);
84
85         if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid)
86                 memcpy(cmd.ibss_bssid_addr, link_conf->bssid, ETH_ALEN);
87
88         return iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_ADD);
89 }
90
91 int iwl_mvm_link_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
92                          struct ieee80211_bss_conf *link_conf,
93                          u32 changes, bool active)
94 {
95         struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
96         unsigned int link_id = link_conf->link_id;
97         struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id];
98         struct iwl_mvm_phy_ctxt *phyctxt;
99         struct iwl_link_config_cmd cmd = {};
100         u32 ht_flag, flags = 0, flags_mask = 0;
101         int ret;
102
103         if (WARN_ON_ONCE(!link_info ||
104                          link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID))
105                 return -EINVAL;
106
107         if (changes & LINK_CONTEXT_MODIFY_ACTIVE) {
108                 /* When activating a link, phy context should be valid;
109                  * when deactivating a link, it also should be valid since
110                  * the link was active before. So, do nothing in this case.
111                  * Since a link is added first with FW_CTXT_INVALID, then we
112                  * can get here in case it's removed before it was activated.
113                  */
114                 if (!link_info->phy_ctxt)
115                         return 0;
116
117                 /* check there aren't too many active links */
118                 if (!link_info->active && active) {
119                         int i, count = 0;
120
121                         /* link with phy_ctxt is active in FW */
122                         for_each_mvm_vif_valid_link(mvmvif, i)
123                                 if (mvmvif->link[i]->phy_ctxt)
124                                         count++;
125
126                         if (vif->type == NL80211_IFTYPE_AP) {
127                                 if (count > mvm->fw->ucode_capa.num_beacons)
128                                         return -EOPNOTSUPP;
129                         /* this should be per HW or such */
130                         } else if (count >= IWL_MVM_FW_MAX_ACTIVE_LINKS_NUM) {
131                                 return -EOPNOTSUPP;
132                         }
133                 }
134
135                 /* Catch early if driver tries to activate or deactivate a link
136                  * twice.
137                  */
138                 WARN_ON_ONCE(active == link_info->active);
139
140                 /* When deactivating a link session protection should
141                  * be stopped
142                  */
143                 if (!active && vif->type == NL80211_IFTYPE_STATION)
144                         iwl_mvm_stop_session_protection(mvm, vif);
145         }
146
147         cmd.link_id = cpu_to_le32(link_info->fw_link_id);
148
149         /* The phy_id, link address and listen_lmac can be modified only until
150          * the link becomes active, otherwise they will be ignored.
151          */
152         phyctxt = link_info->phy_ctxt;
153         if (phyctxt)
154                 cmd.phy_id = cpu_to_le32(phyctxt->id);
155         else
156                 cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID);
157         cmd.mac_id = cpu_to_le32(mvmvif->id);
158
159         memcpy(cmd.local_link_addr, link_conf->addr, ETH_ALEN);
160
161         cmd.active = cpu_to_le32(active);
162
163         if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid)
164                 memcpy(cmd.ibss_bssid_addr, link_conf->bssid, ETH_ALEN);
165
166         /* TODO: set a value to cmd.listen_lmac when system requiremens
167          * will define it
168          */
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[0],
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,
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 (link_conf->eht_puncturing && !iwlwifi_mod_params.disable_11be)
214                 cmd.puncture_mask = cpu_to_le16(link_conf->eht_puncturing);
215         else
216                 /* This flag can be set only if the MAC has eht support */
217                 changes &= ~LINK_CONTEXT_MODIFY_EHT_PARAMS;
218
219         cmd.bss_color = link_conf->he_bss_color.color;
220
221         if (!link_conf->he_bss_color.enabled) {
222                 flags |= LINK_FLG_BSS_COLOR_DIS;
223                 flags_mask |= LINK_FLG_BSS_COLOR_DIS;
224         }
225
226         cmd.frame_time_rts_th = cpu_to_le16(link_conf->frame_time_rts_th);
227
228         /* Block 26-tone RU OFDMA transmissions */
229         if (link_info->he_ru_2mhz_block) {
230                 flags |= LINK_FLG_RU_2MHZ_BLOCK;
231                 flags_mask |= LINK_FLG_RU_2MHZ_BLOCK;
232         }
233
234         if (link_conf->nontransmitted) {
235                 ether_addr_copy(cmd.ref_bssid_addr,
236                                 link_conf->transmitter_bssid);
237                 cmd.bssid_index = link_conf->bssid_index;
238         }
239
240 send_cmd:
241         cmd.modify_mask = cpu_to_le32(changes);
242         cmd.flags = cpu_to_le32(flags);
243         cmd.flags_mask = cpu_to_le32(flags_mask);
244
245         ret = iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_MODIFY);
246         if (!ret && (changes & LINK_CONTEXT_MODIFY_ACTIVE))
247                 link_info->active = active;
248
249         return ret;
250 }
251
252 int iwl_mvm_remove_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
253                         struct ieee80211_bss_conf *link_conf)
254 {
255         struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
256         unsigned int link_id = link_conf->link_id;
257         struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id];
258         struct iwl_link_config_cmd cmd = {};
259         int ret;
260
261         if (WARN_ON(!link_info ||
262                     link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID))
263                 return -EINVAL;
264
265         cmd.link_id = cpu_to_le32(link_info->fw_link_id);
266         iwl_mvm_release_fw_link_id(mvm, link_info->fw_link_id);
267         link_info->fw_link_id = IWL_MVM_FW_LINK_ID_INVALID;
268
269         ret = iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_REMOVE);
270
271         if (!ret)
272                 if (iwl_mvm_sf_update(mvm, vif, true))
273                         IWL_ERR(mvm, "Failed to update SF state\n");
274
275         return ret;
276 }
277
278 /* link should be deactivated before removal, so in most cases we need to
279  * perform these two operations together
280  */
281 int iwl_mvm_disable_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
282                          struct ieee80211_bss_conf *link_conf)
283 {
284         int ret;
285
286         ret = iwl_mvm_link_changed(mvm, vif, link_conf,
287                                    LINK_CONTEXT_MODIFY_ACTIVE, false);
288         if (ret)
289                 return ret;
290
291         ret = iwl_mvm_remove_link(mvm, vif, link_conf);
292         if (ret)
293                 return ret;
294
295         return ret;
296 }