1006c152aSMiri Korenblit // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2006c152aSMiri Korenblit /*
338e72100SJohannes Berg * Copyright (C) 2022-2023 Intel Corporation
4006c152aSMiri Korenblit */
5006c152aSMiri Korenblit #include "mvm.h"
687f7e243SMiri Korenblit #include "time-sync.h"
766a588bfSJohannes Berg #include "sta.h"
866a588bfSJohannes Berg
iwl_mvm_sta_fw_id_mask(struct iwl_mvm * mvm,struct ieee80211_sta * sta,int filter_link_id)966a588bfSJohannes Berg u32 iwl_mvm_sta_fw_id_mask(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
1066a588bfSJohannes Berg int filter_link_id)
1166a588bfSJohannes Berg {
12adde9190SJohannes Berg struct ieee80211_link_sta *link_sta;
1366a588bfSJohannes Berg struct iwl_mvm_sta *mvmsta;
14adde9190SJohannes Berg struct ieee80211_vif *vif;
1566a588bfSJohannes Berg unsigned int link_id;
1666a588bfSJohannes Berg u32 result = 0;
1766a588bfSJohannes Berg
1866a588bfSJohannes Berg if (!sta)
1966a588bfSJohannes Berg return 0;
2066a588bfSJohannes Berg
2166a588bfSJohannes Berg mvmsta = iwl_mvm_sta_from_mac80211(sta);
22adde9190SJohannes Berg vif = mvmsta->vif;
2366a588bfSJohannes Berg
2466a588bfSJohannes Berg /* it's easy when the STA is not an MLD */
2566a588bfSJohannes Berg if (!sta->valid_links)
2666a588bfSJohannes Berg return BIT(mvmsta->deflink.sta_id);
2766a588bfSJohannes Berg
2866a588bfSJohannes Berg /* but if it is an MLD, get the mask of all the FW STAs it has ... */
29adde9190SJohannes Berg for_each_sta_active_link(vif, sta, link_sta, link_id) {
30adde9190SJohannes Berg struct iwl_mvm_link_sta *mvm_link_sta;
3166a588bfSJohannes Berg
3266a588bfSJohannes Berg /* unless we have a specific link in mind */
3366a588bfSJohannes Berg if (filter_link_id >= 0 && link_id != filter_link_id)
3466a588bfSJohannes Berg continue;
3566a588bfSJohannes Berg
36adde9190SJohannes Berg mvm_link_sta =
3766a588bfSJohannes Berg rcu_dereference_check(mvmsta->link[link_id],
3866a588bfSJohannes Berg lockdep_is_held(&mvm->mutex));
39c078f2b4SJohannes Berg if (!mvm_link_sta)
4066a588bfSJohannes Berg continue;
4166a588bfSJohannes Berg
42adde9190SJohannes Berg result |= BIT(mvm_link_sta->sta_id);
4366a588bfSJohannes Berg }
4466a588bfSJohannes Berg
4566a588bfSJohannes Berg return result;
4666a588bfSJohannes Berg }
47006c152aSMiri Korenblit
iwl_mvm_mld_send_sta_cmd(struct iwl_mvm * mvm,struct iwl_mvm_sta_cfg_cmd * cmd)48006c152aSMiri Korenblit static int iwl_mvm_mld_send_sta_cmd(struct iwl_mvm *mvm,
49006c152aSMiri Korenblit struct iwl_mvm_sta_cfg_cmd *cmd)
50006c152aSMiri Korenblit {
51006c152aSMiri Korenblit int ret = iwl_mvm_send_cmd_pdu(mvm,
52006c152aSMiri Korenblit WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD),
53006c152aSMiri Korenblit 0, sizeof(*cmd), cmd);
54006c152aSMiri Korenblit if (ret)
55006c152aSMiri Korenblit IWL_ERR(mvm, "STA_CONFIG_CMD send failed, ret=0x%x\n", ret);
56006c152aSMiri Korenblit return ret;
57006c152aSMiri Korenblit }
58006c152aSMiri Korenblit
59006c152aSMiri Korenblit /*
60006c152aSMiri Korenblit * Add an internal station to the FW table
61006c152aSMiri Korenblit */
iwl_mvm_mld_add_int_sta_to_fw(struct iwl_mvm * mvm,struct iwl_mvm_int_sta * sta,const u8 * addr,int link_id)62006c152aSMiri Korenblit static int iwl_mvm_mld_add_int_sta_to_fw(struct iwl_mvm *mvm,
63006c152aSMiri Korenblit struct iwl_mvm_int_sta *sta,
642152662dSJohannes Berg const u8 *addr, int link_id)
65006c152aSMiri Korenblit {
66006c152aSMiri Korenblit struct iwl_mvm_sta_cfg_cmd cmd;
67006c152aSMiri Korenblit
68006c152aSMiri Korenblit lockdep_assert_held(&mvm->mutex);
69006c152aSMiri Korenblit
70006c152aSMiri Korenblit memset(&cmd, 0, sizeof(cmd));
71006c152aSMiri Korenblit cmd.sta_id = cpu_to_le32((u8)sta->sta_id);
72006c152aSMiri Korenblit
732152662dSJohannes Berg cmd.link_id = cpu_to_le32(link_id);
74006c152aSMiri Korenblit
75006c152aSMiri Korenblit cmd.station_type = cpu_to_le32(sta->type);
76006c152aSMiri Korenblit
774c8d5c8dSJohannes Berg if (fw_has_capa(&mvm->fw->ucode_capa,
784c8d5c8dSJohannes Berg IWL_UCODE_TLV_CAPA_STA_EXP_MFP_SUPPORT) &&
794c8d5c8dSJohannes Berg sta->type == STATION_TYPE_BCAST_MGMT)
804c8d5c8dSJohannes Berg cmd.mfp = cpu_to_le32(1);
814c8d5c8dSJohannes Berg
82006c152aSMiri Korenblit if (addr) {
83006c152aSMiri Korenblit memcpy(cmd.peer_mld_address, addr, ETH_ALEN);
84006c152aSMiri Korenblit memcpy(cmd.peer_link_address, addr, ETH_ALEN);
85006c152aSMiri Korenblit }
86006c152aSMiri Korenblit
87006c152aSMiri Korenblit return iwl_mvm_mld_send_sta_cmd(mvm, &cmd);
88006c152aSMiri Korenblit }
89006c152aSMiri Korenblit
90006c152aSMiri Korenblit /*
91006c152aSMiri Korenblit * Remove a station from the FW table. Before sending the command to remove
92006c152aSMiri Korenblit * the station validate that the station is indeed known to the driver (sanity
93006c152aSMiri Korenblit * only).
94006c152aSMiri Korenblit */
iwl_mvm_mld_rm_sta_from_fw(struct iwl_mvm * mvm,u32 sta_id)95006c152aSMiri Korenblit static int iwl_mvm_mld_rm_sta_from_fw(struct iwl_mvm *mvm, u32 sta_id)
96006c152aSMiri Korenblit {
97006c152aSMiri Korenblit struct iwl_mvm_remove_sta_cmd rm_sta_cmd = {
98006c152aSMiri Korenblit .sta_id = cpu_to_le32(sta_id),
99006c152aSMiri Korenblit };
100006c152aSMiri Korenblit int ret;
101006c152aSMiri Korenblit
102006c152aSMiri Korenblit /* Note: internal stations are marked as error values */
10357974a55SGregory Greenman if (!rcu_access_pointer(mvm->fw_id_to_mac_id[sta_id])) {
10457974a55SGregory Greenman IWL_ERR(mvm, "Invalid station id %d\n", sta_id);
105006c152aSMiri Korenblit return -EINVAL;
106006c152aSMiri Korenblit }
107006c152aSMiri Korenblit
108006c152aSMiri Korenblit ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, STA_REMOVE_CMD),
109006c152aSMiri Korenblit 0, sizeof(rm_sta_cmd), &rm_sta_cmd);
110006c152aSMiri Korenblit if (ret) {
111006c152aSMiri Korenblit IWL_ERR(mvm, "Failed to remove station. Id=%d\n", sta_id);
112006c152aSMiri Korenblit return ret;
113006c152aSMiri Korenblit }
114006c152aSMiri Korenblit
115006c152aSMiri Korenblit return 0;
116006c152aSMiri Korenblit }
117006c152aSMiri Korenblit
iwl_mvm_add_aux_sta_to_fw(struct iwl_mvm * mvm,struct iwl_mvm_int_sta * sta,u32 lmac_id)118fe8b2ad3SMiri Korenblit static int iwl_mvm_add_aux_sta_to_fw(struct iwl_mvm *mvm,
119fe8b2ad3SMiri Korenblit struct iwl_mvm_int_sta *sta,
120fe8b2ad3SMiri Korenblit u32 lmac_id)
121fe8b2ad3SMiri Korenblit {
122fe8b2ad3SMiri Korenblit int ret;
123fe8b2ad3SMiri Korenblit
124fe8b2ad3SMiri Korenblit struct iwl_mvm_aux_sta_cmd cmd = {
125fe8b2ad3SMiri Korenblit .sta_id = cpu_to_le32(sta->sta_id),
126fe8b2ad3SMiri Korenblit .lmac_id = cpu_to_le32(lmac_id),
127fe8b2ad3SMiri Korenblit };
128fe8b2ad3SMiri Korenblit
129fe8b2ad3SMiri Korenblit ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, AUX_STA_CMD),
130fe8b2ad3SMiri Korenblit 0, sizeof(cmd), &cmd);
131fe8b2ad3SMiri Korenblit if (ret)
132fe8b2ad3SMiri Korenblit IWL_ERR(mvm, "Failed to send AUX_STA_CMD\n");
133fe8b2ad3SMiri Korenblit return ret;
134fe8b2ad3SMiri Korenblit }
135fe8b2ad3SMiri Korenblit
136006c152aSMiri Korenblit /*
137006c152aSMiri Korenblit * Adds an internal sta to the FW table with its queues
138006c152aSMiri Korenblit */
iwl_mvm_mld_add_int_sta_with_queue(struct iwl_mvm * mvm,struct iwl_mvm_int_sta * sta,const u8 * addr,int link_id,u16 * queue,u8 tid,unsigned int * _wdg_timeout)1390945f976SAvraham Stern int iwl_mvm_mld_add_int_sta_with_queue(struct iwl_mvm *mvm,
140006c152aSMiri Korenblit struct iwl_mvm_int_sta *sta,
1412152662dSJohannes Berg const u8 *addr, int link_id,
142006c152aSMiri Korenblit u16 *queue, u8 tid,
143006c152aSMiri Korenblit unsigned int *_wdg_timeout)
144006c152aSMiri Korenblit {
145006c152aSMiri Korenblit int ret, txq;
146006c152aSMiri Korenblit unsigned int wdg_timeout = _wdg_timeout ? *_wdg_timeout :
147006c152aSMiri Korenblit mvm->trans->trans_cfg->base_params->wd_timeout;
148006c152aSMiri Korenblit
149006c152aSMiri Korenblit if (WARN_ON_ONCE(sta->sta_id == IWL_MVM_INVALID_STA))
150006c152aSMiri Korenblit return -ENOSPC;
151006c152aSMiri Korenblit
152fe8b2ad3SMiri Korenblit if (sta->type == STATION_TYPE_AUX)
1532152662dSJohannes Berg ret = iwl_mvm_add_aux_sta_to_fw(mvm, sta, link_id);
154fe8b2ad3SMiri Korenblit else
1552152662dSJohannes Berg ret = iwl_mvm_mld_add_int_sta_to_fw(mvm, sta, addr, link_id);
156006c152aSMiri Korenblit if (ret)
157006c152aSMiri Korenblit return ret;
158006c152aSMiri Korenblit
159006c152aSMiri Korenblit /*
160006c152aSMiri Korenblit * For 22000 firmware and on we cannot add queue to a station unknown
161006c152aSMiri Korenblit * to firmware so enable queue here - after the station was added
162006c152aSMiri Korenblit */
163006c152aSMiri Korenblit txq = iwl_mvm_tvqm_enable_txq(mvm, NULL, sta->sta_id, tid,
164006c152aSMiri Korenblit wdg_timeout);
165006c152aSMiri Korenblit if (txq < 0) {
166006c152aSMiri Korenblit iwl_mvm_mld_rm_sta_from_fw(mvm, sta->sta_id);
167006c152aSMiri Korenblit return txq;
168006c152aSMiri Korenblit }
169006c152aSMiri Korenblit *queue = txq;
170006c152aSMiri Korenblit
171006c152aSMiri Korenblit return 0;
172006c152aSMiri Korenblit }
173006c152aSMiri Korenblit
174006c152aSMiri Korenblit /*
175006c152aSMiri Korenblit * Adds a new int sta: allocate it in the driver, add it to the FW table,
176006c152aSMiri Korenblit * and add its queues.
177006c152aSMiri Korenblit */
iwl_mvm_mld_add_int_sta(struct iwl_mvm * mvm,struct iwl_mvm_int_sta * int_sta,u16 * queue,enum nl80211_iftype iftype,enum iwl_fw_sta_type sta_type,int link_id,const u8 * addr,u8 tid,unsigned int * wdg_timeout)178006c152aSMiri Korenblit static int iwl_mvm_mld_add_int_sta(struct iwl_mvm *mvm,
179006c152aSMiri Korenblit struct iwl_mvm_int_sta *int_sta, u16 *queue,
180006c152aSMiri Korenblit enum nl80211_iftype iftype,
181006c152aSMiri Korenblit enum iwl_fw_sta_type sta_type,
1822152662dSJohannes Berg int link_id, const u8 *addr, u8 tid,
183006c152aSMiri Korenblit unsigned int *wdg_timeout)
184006c152aSMiri Korenblit {
185006c152aSMiri Korenblit int ret;
186006c152aSMiri Korenblit
187006c152aSMiri Korenblit lockdep_assert_held(&mvm->mutex);
188006c152aSMiri Korenblit
189006c152aSMiri Korenblit /* qmask argument is not used in the new tx api, send a don't care */
190006c152aSMiri Korenblit ret = iwl_mvm_allocate_int_sta(mvm, int_sta, 0, iftype,
191006c152aSMiri Korenblit sta_type);
192006c152aSMiri Korenblit if (ret)
193006c152aSMiri Korenblit return ret;
194006c152aSMiri Korenblit
1952152662dSJohannes Berg ret = iwl_mvm_mld_add_int_sta_with_queue(mvm, int_sta, addr, link_id,
196006c152aSMiri Korenblit queue, tid, wdg_timeout);
197006c152aSMiri Korenblit if (ret) {
198006c152aSMiri Korenblit iwl_mvm_dealloc_int_sta(mvm, int_sta);
199006c152aSMiri Korenblit return ret;
200006c152aSMiri Korenblit }
201006c152aSMiri Korenblit
202006c152aSMiri Korenblit return 0;
203006c152aSMiri Korenblit }
204006c152aSMiri Korenblit
205006c152aSMiri Korenblit /* Allocate a new station entry for the broadcast station to the given vif,
206006c152aSMiri Korenblit * and send it to the FW.
207006c152aSMiri Korenblit * Note that each P2P mac should have its own broadcast station.
208006c152aSMiri Korenblit */
iwl_mvm_mld_add_bcast_sta(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link_conf)20962e0ccb2SGregory Greenman int iwl_mvm_mld_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
21062e0ccb2SGregory Greenman struct ieee80211_bss_conf *link_conf)
211006c152aSMiri Korenblit {
212006c152aSMiri Korenblit struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
21362e0ccb2SGregory Greenman struct iwl_mvm_vif_link_info *mvm_link =
21462e0ccb2SGregory Greenman mvmvif->link[link_conf->link_id];
21562e0ccb2SGregory Greenman struct iwl_mvm_int_sta *bsta = &mvm_link->bcast_sta;
216006c152aSMiri Korenblit static const u8 _baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
217006c152aSMiri Korenblit const u8 *baddr = _baddr;
218006c152aSMiri Korenblit unsigned int wdg_timeout =
219006c152aSMiri Korenblit iwl_mvm_get_wd_timeout(mvm, vif, false, false);
220006c152aSMiri Korenblit u16 *queue;
221006c152aSMiri Korenblit
222006c152aSMiri Korenblit lockdep_assert_held(&mvm->mutex);
223006c152aSMiri Korenblit
224006c152aSMiri Korenblit if (vif->type == NL80211_IFTYPE_ADHOC)
22562e0ccb2SGregory Greenman baddr = link_conf->bssid;
226006c152aSMiri Korenblit
227006c152aSMiri Korenblit if (vif->type == NL80211_IFTYPE_AP ||
228006c152aSMiri Korenblit vif->type == NL80211_IFTYPE_ADHOC) {
229de50140bSJohannes Berg queue = &mvm_link->mgmt_queue;
230006c152aSMiri Korenblit } else if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
231006c152aSMiri Korenblit queue = &mvm->p2p_dev_queue;
232006c152aSMiri Korenblit } else {
233006c152aSMiri Korenblit WARN(1, "Missing required TXQ for adding bcast STA\n");
234006c152aSMiri Korenblit return -EINVAL;
235006c152aSMiri Korenblit }
236006c152aSMiri Korenblit
237006c152aSMiri Korenblit return iwl_mvm_mld_add_int_sta(mvm, bsta, queue,
238006c152aSMiri Korenblit ieee80211_vif_type_p2p(vif),
239006c152aSMiri Korenblit STATION_TYPE_BCAST_MGMT,
240d6f6b0d8SGregory Greenman mvm_link->fw_link_id, baddr,
241d6f6b0d8SGregory Greenman IWL_MAX_TID_COUNT, &wdg_timeout);
242006c152aSMiri Korenblit }
243006c152aSMiri Korenblit
244f947b62cSMiri Korenblit /* Allocate a new station entry for the broadcast station to the given vif,
245f947b62cSMiri Korenblit * and send it to the FW.
246f947b62cSMiri Korenblit * Note that each AP/GO mac should have its own multicast station.
247f947b62cSMiri Korenblit */
iwl_mvm_mld_add_mcast_sta(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link_conf)24862e0ccb2SGregory Greenman int iwl_mvm_mld_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
24962e0ccb2SGregory Greenman struct ieee80211_bss_conf *link_conf)
250f947b62cSMiri Korenblit {
251f947b62cSMiri Korenblit struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
25262e0ccb2SGregory Greenman struct iwl_mvm_vif_link_info *mvm_link =
25362e0ccb2SGregory Greenman mvmvif->link[link_conf->link_id];
25462e0ccb2SGregory Greenman struct iwl_mvm_int_sta *msta = &mvm_link->mcast_sta;
255f947b62cSMiri Korenblit static const u8 _maddr[] = {0x03, 0x00, 0x00, 0x00, 0x00, 0x00};
256f947b62cSMiri Korenblit const u8 *maddr = _maddr;
257f947b62cSMiri Korenblit unsigned int timeout = iwl_mvm_get_wd_timeout(mvm, vif, false, false);
258f947b62cSMiri Korenblit
259f947b62cSMiri Korenblit lockdep_assert_held(&mvm->mutex);
260f947b62cSMiri Korenblit
261f947b62cSMiri Korenblit if (WARN_ON(vif->type != NL80211_IFTYPE_AP &&
262f947b62cSMiri Korenblit vif->type != NL80211_IFTYPE_ADHOC))
263f947b62cSMiri Korenblit return -EOPNOTSUPP;
264f947b62cSMiri Korenblit
265f947b62cSMiri Korenblit /* In IBSS, ieee80211_check_queues() sets the cab_queue to be
266f947b62cSMiri Korenblit * invalid, so make sure we use the queue we want.
267f947b62cSMiri Korenblit * Note that this is done here as we want to avoid making DQA
268f947b62cSMiri Korenblit * changes in mac80211 layer.
269f947b62cSMiri Korenblit */
270f947b62cSMiri Korenblit if (vif->type == NL80211_IFTYPE_ADHOC)
27162e0ccb2SGregory Greenman mvm_link->cab_queue = IWL_MVM_DQA_GCAST_QUEUE;
272f947b62cSMiri Korenblit
27362e0ccb2SGregory Greenman return iwl_mvm_mld_add_int_sta(mvm, msta, &mvm_link->cab_queue,
274f947b62cSMiri Korenblit vif->type, STATION_TYPE_MCAST,
275d6f6b0d8SGregory Greenman mvm_link->fw_link_id, maddr, 0,
276d6f6b0d8SGregory Greenman &timeout);
277f947b62cSMiri Korenblit }
278f947b62cSMiri Korenblit
279006c152aSMiri Korenblit /* Allocate a new station entry for the sniffer station to the given vif,
280006c152aSMiri Korenblit * and send it to the FW.
281006c152aSMiri Korenblit */
iwl_mvm_mld_add_snif_sta(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link_conf)28262e0ccb2SGregory Greenman int iwl_mvm_mld_add_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
28362e0ccb2SGregory Greenman struct ieee80211_bss_conf *link_conf)
284006c152aSMiri Korenblit {
285006c152aSMiri Korenblit struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
286d6f6b0d8SGregory Greenman struct iwl_mvm_vif_link_info *mvm_link =
287d6f6b0d8SGregory Greenman mvmvif->link[link_conf->link_id];
288006c152aSMiri Korenblit
289006c152aSMiri Korenblit lockdep_assert_held(&mvm->mutex);
290006c152aSMiri Korenblit
291006c152aSMiri Korenblit return iwl_mvm_mld_add_int_sta(mvm, &mvm->snif_sta, &mvm->snif_queue,
292006c152aSMiri Korenblit vif->type, STATION_TYPE_BCAST_MGMT,
293d6f6b0d8SGregory Greenman mvm_link->fw_link_id, NULL,
294d6f6b0d8SGregory Greenman IWL_MAX_TID_COUNT, NULL);
295006c152aSMiri Korenblit }
296006c152aSMiri Korenblit
iwl_mvm_mld_add_aux_sta(struct iwl_mvm * mvm,u32 lmac_id)297fe8b2ad3SMiri Korenblit int iwl_mvm_mld_add_aux_sta(struct iwl_mvm *mvm, u32 lmac_id)
298fe8b2ad3SMiri Korenblit {
299fe8b2ad3SMiri Korenblit lockdep_assert_held(&mvm->mutex);
300fe8b2ad3SMiri Korenblit
3012152662dSJohannes Berg /* In CDB NICs we need to specify which lmac to use for aux activity;
3022152662dSJohannes Berg * use the link_id argument place to send lmac_id to the function.
303fe8b2ad3SMiri Korenblit */
304fe8b2ad3SMiri Korenblit return iwl_mvm_mld_add_int_sta(mvm, &mvm->aux_sta, &mvm->aux_queue,
305fe8b2ad3SMiri Korenblit NL80211_IFTYPE_UNSPECIFIED,
306fe8b2ad3SMiri Korenblit STATION_TYPE_AUX, lmac_id, NULL,
307fe8b2ad3SMiri Korenblit IWL_MAX_TID_COUNT, NULL);
308fe8b2ad3SMiri Korenblit }
309fe8b2ad3SMiri Korenblit
iwl_mvm_mld_disable_txq(struct iwl_mvm * mvm,u32 sta_mask,u16 * queueptr,u8 tid)3106f2c5f38SJohannes Berg static int iwl_mvm_mld_disable_txq(struct iwl_mvm *mvm, u32 sta_mask,
311006c152aSMiri Korenblit u16 *queueptr, u8 tid)
312006c152aSMiri Korenblit {
313006c152aSMiri Korenblit int queue = *queueptr;
314006c152aSMiri Korenblit int ret = 0;
315006c152aSMiri Korenblit
316925c6a40SJohannes Berg if (tid == IWL_MAX_TID_COUNT)
317925c6a40SJohannes Berg tid = IWL_MGMT_TID;
318925c6a40SJohannes Berg
319006c152aSMiri Korenblit if (mvm->sta_remove_requires_queue_remove) {
320006c152aSMiri Korenblit u32 cmd_id = WIDE_ID(DATA_PATH_GROUP,
321006c152aSMiri Korenblit SCD_QUEUE_CONFIG_CMD);
322006c152aSMiri Korenblit struct iwl_scd_queue_cfg_cmd remove_cmd = {
323006c152aSMiri Korenblit .operation = cpu_to_le32(IWL_SCD_QUEUE_REMOVE),
324006c152aSMiri Korenblit .u.remove.tid = cpu_to_le32(tid),
3256f2c5f38SJohannes Berg .u.remove.sta_mask = cpu_to_le32(sta_mask),
326006c152aSMiri Korenblit };
327006c152aSMiri Korenblit
328006c152aSMiri Korenblit ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0,
329006c152aSMiri Korenblit sizeof(remove_cmd),
330006c152aSMiri Korenblit &remove_cmd);
331006c152aSMiri Korenblit }
332006c152aSMiri Korenblit
333006c152aSMiri Korenblit iwl_trans_txq_free(mvm->trans, queue);
334006c152aSMiri Korenblit *queueptr = IWL_MVM_INVALID_QUEUE;
335006c152aSMiri Korenblit
336006c152aSMiri Korenblit return ret;
337006c152aSMiri Korenblit }
338006c152aSMiri Korenblit
339006c152aSMiri Korenblit /* Removes a sta from the FW table, disable its queues, and dealloc it
340006c152aSMiri Korenblit */
iwl_mvm_mld_rm_int_sta(struct iwl_mvm * mvm,struct iwl_mvm_int_sta * int_sta,bool flush,u8 tid,u16 * queuptr)341006c152aSMiri Korenblit static int iwl_mvm_mld_rm_int_sta(struct iwl_mvm *mvm,
342006c152aSMiri Korenblit struct iwl_mvm_int_sta *int_sta,
343006c152aSMiri Korenblit bool flush, u8 tid, u16 *queuptr)
344006c152aSMiri Korenblit {
345006c152aSMiri Korenblit int ret;
346006c152aSMiri Korenblit
347006c152aSMiri Korenblit lockdep_assert_held(&mvm->mutex);
348006c152aSMiri Korenblit
349006c152aSMiri Korenblit if (WARN_ON_ONCE(int_sta->sta_id == IWL_MVM_INVALID_STA))
350006c152aSMiri Korenblit return -EINVAL;
351006c152aSMiri Korenblit
352006c152aSMiri Korenblit if (flush)
353d43701c5SJohannes Berg iwl_mvm_flush_sta(mvm, int_sta->sta_id, int_sta->tfd_queue_msk);
354006c152aSMiri Korenblit
3556f2c5f38SJohannes Berg iwl_mvm_mld_disable_txq(mvm, BIT(int_sta->sta_id), queuptr, tid);
356006c152aSMiri Korenblit
357006c152aSMiri Korenblit ret = iwl_mvm_mld_rm_sta_from_fw(mvm, int_sta->sta_id);
358006c152aSMiri Korenblit if (ret)
359006c152aSMiri Korenblit IWL_WARN(mvm, "Failed sending remove station\n");
360006c152aSMiri Korenblit
361006c152aSMiri Korenblit iwl_mvm_dealloc_int_sta(mvm, int_sta);
362006c152aSMiri Korenblit
363006c152aSMiri Korenblit return ret;
364006c152aSMiri Korenblit }
365006c152aSMiri Korenblit
iwl_mvm_mld_rm_bcast_sta(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link_conf)36662e0ccb2SGregory Greenman int iwl_mvm_mld_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
36762e0ccb2SGregory Greenman struct ieee80211_bss_conf *link_conf)
368006c152aSMiri Korenblit {
369006c152aSMiri Korenblit struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
37062e0ccb2SGregory Greenman struct iwl_mvm_vif_link_info *link = mvmvif->link[link_conf->link_id];
371006c152aSMiri Korenblit u16 *queueptr;
372006c152aSMiri Korenblit
373006c152aSMiri Korenblit lockdep_assert_held(&mvm->mutex);
374006c152aSMiri Korenblit
37538e72100SJohannes Berg if (WARN_ON(!link))
37638e72100SJohannes Berg return -EIO;
37738e72100SJohannes Berg
378006c152aSMiri Korenblit switch (vif->type) {
379006c152aSMiri Korenblit case NL80211_IFTYPE_AP:
380006c152aSMiri Korenblit case NL80211_IFTYPE_ADHOC:
381de50140bSJohannes Berg queueptr = &link->mgmt_queue;
382006c152aSMiri Korenblit break;
383006c152aSMiri Korenblit case NL80211_IFTYPE_P2P_DEVICE:
384006c152aSMiri Korenblit queueptr = &mvm->p2p_dev_queue;
385006c152aSMiri Korenblit break;
386006c152aSMiri Korenblit default:
387006c152aSMiri Korenblit WARN(1, "Can't free bcast queue on vif type %d\n",
388006c152aSMiri Korenblit vif->type);
389006c152aSMiri Korenblit return -EINVAL;
390006c152aSMiri Korenblit }
391006c152aSMiri Korenblit
39262e0ccb2SGregory Greenman return iwl_mvm_mld_rm_int_sta(mvm, &link->bcast_sta,
39362e0ccb2SGregory Greenman true, IWL_MAX_TID_COUNT, queueptr);
394006c152aSMiri Korenblit }
395006c152aSMiri Korenblit
396f947b62cSMiri Korenblit /* Send the FW a request to remove the station from it's internal data
397f947b62cSMiri Korenblit * structures, and in addition remove it from the local data structure.
398f947b62cSMiri Korenblit */
iwl_mvm_mld_rm_mcast_sta(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link_conf)39962e0ccb2SGregory Greenman int iwl_mvm_mld_rm_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
40062e0ccb2SGregory Greenman struct ieee80211_bss_conf *link_conf)
401f947b62cSMiri Korenblit {
402f947b62cSMiri Korenblit struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
40362e0ccb2SGregory Greenman struct iwl_mvm_vif_link_info *link = mvmvif->link[link_conf->link_id];
404f947b62cSMiri Korenblit
405f947b62cSMiri Korenblit lockdep_assert_held(&mvm->mutex);
406f947b62cSMiri Korenblit
40738e72100SJohannes Berg if (WARN_ON(!link))
40838e72100SJohannes Berg return -EIO;
40938e72100SJohannes Berg
41062e0ccb2SGregory Greenman return iwl_mvm_mld_rm_int_sta(mvm, &link->mcast_sta, true, 0,
41162e0ccb2SGregory Greenman &link->cab_queue);
412f947b62cSMiri Korenblit }
413f947b62cSMiri Korenblit
iwl_mvm_mld_rm_snif_sta(struct iwl_mvm * mvm,struct ieee80211_vif * vif)414006c152aSMiri Korenblit int iwl_mvm_mld_rm_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
415006c152aSMiri Korenblit {
416006c152aSMiri Korenblit lockdep_assert_held(&mvm->mutex);
417006c152aSMiri Korenblit
418006c152aSMiri Korenblit return iwl_mvm_mld_rm_int_sta(mvm, &mvm->snif_sta, false,
419006c152aSMiri Korenblit IWL_MAX_TID_COUNT, &mvm->snif_queue);
420006c152aSMiri Korenblit }
4216f71e90eSMiri Korenblit
iwl_mvm_mld_rm_aux_sta(struct iwl_mvm * mvm)422fe8b2ad3SMiri Korenblit int iwl_mvm_mld_rm_aux_sta(struct iwl_mvm *mvm)
423fe8b2ad3SMiri Korenblit {
424fe8b2ad3SMiri Korenblit lockdep_assert_held(&mvm->mutex);
425fe8b2ad3SMiri Korenblit
426fe8b2ad3SMiri Korenblit return iwl_mvm_mld_rm_int_sta(mvm, &mvm->aux_sta, false,
427fe8b2ad3SMiri Korenblit IWL_MAX_TID_COUNT, &mvm->aux_queue);
428fe8b2ad3SMiri Korenblit }
429fe8b2ad3SMiri Korenblit
43087f7e243SMiri Korenblit /* send a cfg sta command to add/update a sta in firmware */
iwl_mvm_mld_cfg_sta(struct iwl_mvm * mvm,struct ieee80211_sta * sta,struct ieee80211_vif * vif,struct ieee80211_link_sta * link_sta,struct ieee80211_bss_conf * link_conf,struct iwl_mvm_link_sta * mvm_link_sta)43187f7e243SMiri Korenblit static int iwl_mvm_mld_cfg_sta(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
43257974a55SGregory Greenman struct ieee80211_vif *vif,
43357974a55SGregory Greenman struct ieee80211_link_sta *link_sta,
43457974a55SGregory Greenman struct ieee80211_bss_conf *link_conf,
43557974a55SGregory Greenman struct iwl_mvm_link_sta *mvm_link_sta)
43687f7e243SMiri Korenblit {
43787f7e243SMiri Korenblit struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
438d6f6b0d8SGregory Greenman struct iwl_mvm_vif *mvm_vif = iwl_mvm_vif_from_mac80211(vif);
43957974a55SGregory Greenman struct iwl_mvm_vif_link_info *link_info =
44057974a55SGregory Greenman mvm_vif->link[link_conf->link_id];
44187f7e243SMiri Korenblit struct iwl_mvm_sta_cfg_cmd cmd = {
44257974a55SGregory Greenman .sta_id = cpu_to_le32(mvm_link_sta->sta_id),
44387f7e243SMiri Korenblit .station_type = cpu_to_le32(mvm_sta->sta_type),
44487f7e243SMiri Korenblit };
44587f7e243SMiri Korenblit u32 agg_size = 0, mpdu_dens = 0;
44687f7e243SMiri Korenblit
447d6f6b0d8SGregory Greenman /* when adding sta, link should exist in FW */
448d6f6b0d8SGregory Greenman if (WARN_ON(link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID))
449d6f6b0d8SGregory Greenman return -EINVAL;
450d6f6b0d8SGregory Greenman
451d6f6b0d8SGregory Greenman cmd.link_id = cpu_to_le32(link_info->fw_link_id);
452d6f6b0d8SGregory Greenman
45387f7e243SMiri Korenblit memcpy(&cmd.peer_mld_address, sta->addr, ETH_ALEN);
45429df2a64SShaul Triebitz memcpy(&cmd.peer_link_address, link_sta->addr, ETH_ALEN);
45587f7e243SMiri Korenblit
45687f7e243SMiri Korenblit if (mvm_sta->sta_state >= IEEE80211_STA_ASSOC)
45787f7e243SMiri Korenblit cmd.assoc_id = cpu_to_le32(sta->aid);
45887f7e243SMiri Korenblit
4594c8d5c8dSJohannes Berg if (fw_has_capa(&mvm->fw->ucode_capa,
4604c8d5c8dSJohannes Berg IWL_UCODE_TLV_CAPA_STA_EXP_MFP_SUPPORT) &&
4614c8d5c8dSJohannes Berg (sta->mfp || mvm_sta->sta_state < IEEE80211_STA_AUTHORIZED))
4624c8d5c8dSJohannes Berg cmd.mfp = cpu_to_le32(1);
4634c8d5c8dSJohannes Berg
46457974a55SGregory Greenman switch (link_sta->rx_nss) {
46587f7e243SMiri Korenblit case 1:
46687f7e243SMiri Korenblit cmd.mimo = cpu_to_le32(0);
46787f7e243SMiri Korenblit break;
46887f7e243SMiri Korenblit case 2 ... 8:
46987f7e243SMiri Korenblit cmd.mimo = cpu_to_le32(1);
47087f7e243SMiri Korenblit break;
47187f7e243SMiri Korenblit }
47287f7e243SMiri Korenblit
47387f7e243SMiri Korenblit switch (sta->deflink.smps_mode) {
47487f7e243SMiri Korenblit case IEEE80211_SMPS_AUTOMATIC:
47587f7e243SMiri Korenblit case IEEE80211_SMPS_NUM_MODES:
47687f7e243SMiri Korenblit WARN_ON(1);
47787f7e243SMiri Korenblit break;
47887f7e243SMiri Korenblit case IEEE80211_SMPS_STATIC:
47987f7e243SMiri Korenblit /* override NSS */
48087f7e243SMiri Korenblit cmd.mimo = cpu_to_le32(0);
48187f7e243SMiri Korenblit break;
48287f7e243SMiri Korenblit case IEEE80211_SMPS_DYNAMIC:
48387f7e243SMiri Korenblit cmd.mimo_protection = cpu_to_le32(1);
48487f7e243SMiri Korenblit break;
48587f7e243SMiri Korenblit case IEEE80211_SMPS_OFF:
48687f7e243SMiri Korenblit /* nothing */
48787f7e243SMiri Korenblit break;
48887f7e243SMiri Korenblit }
48987f7e243SMiri Korenblit
49057974a55SGregory Greenman mpdu_dens = iwl_mvm_get_sta_ampdu_dens(link_sta, link_conf, &agg_size);
49187f7e243SMiri Korenblit cmd.tx_ampdu_spacing = cpu_to_le32(mpdu_dens);
49287f7e243SMiri Korenblit cmd.tx_ampdu_max_size = cpu_to_le32(agg_size);
49387f7e243SMiri Korenblit
49487f7e243SMiri Korenblit if (sta->wme) {
49587f7e243SMiri Korenblit cmd.sp_length =
49687f7e243SMiri Korenblit cpu_to_le32(sta->max_sp ? sta->max_sp * 2 : 128);
49787f7e243SMiri Korenblit cmd.uapsd_acs = cpu_to_le32(iwl_mvm_get_sta_uapsd_acs(sta));
49887f7e243SMiri Korenblit }
49987f7e243SMiri Korenblit
50057974a55SGregory Greenman if (link_sta->he_cap.has_he) {
50187f7e243SMiri Korenblit cmd.trig_rnd_alloc =
50257974a55SGregory Greenman cpu_to_le32(link_conf->uora_exists ? 1 : 0);
50387f7e243SMiri Korenblit
50487f7e243SMiri Korenblit /* PPE Thresholds */
50557974a55SGregory Greenman iwl_mvm_set_sta_pkt_ext(mvm, link_sta, &cmd.pkt_ext);
50687f7e243SMiri Korenblit
50787f7e243SMiri Korenblit /* HTC flags */
50857974a55SGregory Greenman cmd.htc_flags = iwl_mvm_get_sta_htc_flags(sta, link_sta);
50987f7e243SMiri Korenblit
51057974a55SGregory Greenman if (link_sta->he_cap.he_cap_elem.mac_cap_info[2] &
51187f7e243SMiri Korenblit IEEE80211_HE_MAC_CAP2_ACK_EN)
51287f7e243SMiri Korenblit cmd.ack_enabled = cpu_to_le32(1);
51387f7e243SMiri Korenblit }
51487f7e243SMiri Korenblit
51587f7e243SMiri Korenblit return iwl_mvm_mld_send_sta_cmd(mvm, &cmd);
51687f7e243SMiri Korenblit }
51787f7e243SMiri Korenblit
iwl_mvm_mld_free_sta_link(struct iwl_mvm * mvm,struct iwl_mvm_sta * mvm_sta,struct iwl_mvm_link_sta * mvm_sta_link,unsigned int link_id,bool is_in_fw)518e25fae98SBenjamin Berg void iwl_mvm_mld_free_sta_link(struct iwl_mvm *mvm,
519881d0548SJohannes Berg struct iwl_mvm_sta *mvm_sta,
520881d0548SJohannes Berg struct iwl_mvm_link_sta *mvm_sta_link,
521881d0548SJohannes Berg unsigned int link_id,
522881d0548SJohannes Berg bool is_in_fw)
523881d0548SJohannes Berg {
524881d0548SJohannes Berg RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta_link->sta_id],
525881d0548SJohannes Berg is_in_fw ? ERR_PTR(-EINVAL) : NULL);
526881d0548SJohannes Berg RCU_INIT_POINTER(mvm->fw_id_to_link_sta[mvm_sta_link->sta_id], NULL);
527881d0548SJohannes Berg RCU_INIT_POINTER(mvm_sta->link[link_id], NULL);
528881d0548SJohannes Berg
529881d0548SJohannes Berg if (mvm_sta_link != &mvm_sta->deflink)
530881d0548SJohannes Berg kfree_rcu(mvm_sta_link, rcu_head);
531881d0548SJohannes Berg }
532881d0548SJohannes Berg
iwl_mvm_mld_sta_rm_all_sta_links(struct iwl_mvm * mvm,struct iwl_mvm_sta * mvm_sta)53357974a55SGregory Greenman static void iwl_mvm_mld_sta_rm_all_sta_links(struct iwl_mvm *mvm,
53457974a55SGregory Greenman struct iwl_mvm_sta *mvm_sta)
53557974a55SGregory Greenman {
53657974a55SGregory Greenman unsigned int link_id;
53757974a55SGregory Greenman
53857974a55SGregory Greenman for (link_id = 0; link_id < ARRAY_SIZE(mvm_sta->link); link_id++) {
53957974a55SGregory Greenman struct iwl_mvm_link_sta *link =
54057974a55SGregory Greenman rcu_dereference_protected(mvm_sta->link[link_id],
54157974a55SGregory Greenman lockdep_is_held(&mvm->mutex));
54257974a55SGregory Greenman
54357974a55SGregory Greenman if (!link)
54457974a55SGregory Greenman continue;
54557974a55SGregory Greenman
546881d0548SJohannes Berg iwl_mvm_mld_free_sta_link(mvm, mvm_sta, link, link_id, false);
54757974a55SGregory Greenman }
54857974a55SGregory Greenman }
54957974a55SGregory Greenman
iwl_mvm_mld_alloc_sta_link(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_sta * sta,unsigned int link_id)5506e4198d3SShaul Triebitz static int iwl_mvm_mld_alloc_sta_link(struct iwl_mvm *mvm,
5516e4198d3SShaul Triebitz struct ieee80211_vif *vif,
5526e4198d3SShaul Triebitz struct ieee80211_sta *sta,
5536e4198d3SShaul Triebitz unsigned int link_id)
5546e4198d3SShaul Triebitz {
555b8a85a1dSJohannes Berg struct ieee80211_link_sta *link_sta =
5560d504ca1SJohannes Berg link_sta_dereference_protected(sta, link_id);
5576e4198d3SShaul Triebitz struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
5586e4198d3SShaul Triebitz struct iwl_mvm_link_sta *link;
5596e4198d3SShaul Triebitz u32 sta_id = iwl_mvm_find_free_sta_id(mvm,
5606e4198d3SShaul Triebitz ieee80211_vif_type_p2p(vif));
5616e4198d3SShaul Triebitz
5626e4198d3SShaul Triebitz if (sta_id == IWL_MVM_INVALID_STA)
5636e4198d3SShaul Triebitz return -ENOSPC;
5646e4198d3SShaul Triebitz
5656e4198d3SShaul Triebitz if (rcu_access_pointer(sta->link[link_id]) == &sta->deflink) {
5666e4198d3SShaul Triebitz link = &mvm_sta->deflink;
5676e4198d3SShaul Triebitz } else {
5686e4198d3SShaul Triebitz link = kzalloc(sizeof(*link), GFP_KERNEL);
5696e4198d3SShaul Triebitz if (!link)
5706e4198d3SShaul Triebitz return -ENOMEM;
5716e4198d3SShaul Triebitz }
5726e4198d3SShaul Triebitz
5736e4198d3SShaul Triebitz link->sta_id = sta_id;
5746e4198d3SShaul Triebitz rcu_assign_pointer(mvm_sta->link[link_id], link);
5756e4198d3SShaul Triebitz rcu_assign_pointer(mvm->fw_id_to_mac_id[link->sta_id], sta);
576b8a85a1dSJohannes Berg rcu_assign_pointer(mvm->fw_id_to_link_sta[link->sta_id],
577b8a85a1dSJohannes Berg link_sta);
5786e4198d3SShaul Triebitz
5796e4198d3SShaul Triebitz return 0;
5806e4198d3SShaul Triebitz }
5816e4198d3SShaul Triebitz
58257974a55SGregory Greenman /* allocate all the links of a sta, called when the station is first added */
iwl_mvm_mld_alloc_sta_links(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_sta * sta)58357974a55SGregory Greenman static int iwl_mvm_mld_alloc_sta_links(struct iwl_mvm *mvm,
58457974a55SGregory Greenman struct ieee80211_vif *vif,
58587f7e243SMiri Korenblit struct ieee80211_sta *sta)
58687f7e243SMiri Korenblit {
58787f7e243SMiri Korenblit struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
58829caa342SJohannes Berg struct ieee80211_link_sta *link_sta;
58957974a55SGregory Greenman unsigned int link_id;
59057974a55SGregory Greenman int ret;
59187f7e243SMiri Korenblit
59287f7e243SMiri Korenblit lockdep_assert_held(&mvm->mutex);
59387f7e243SMiri Korenblit
59429caa342SJohannes Berg for_each_sta_active_link(vif, sta, link_sta, link_id) {
59529caa342SJohannes Berg if (WARN_ON(mvm_sta->link[link_id]))
59657974a55SGregory Greenman continue;
59757974a55SGregory Greenman
5986e4198d3SShaul Triebitz ret = iwl_mvm_mld_alloc_sta_link(mvm, vif, sta, link_id);
5996e4198d3SShaul Triebitz if (ret)
60057974a55SGregory Greenman goto err;
60157974a55SGregory Greenman }
60257974a55SGregory Greenman
60357974a55SGregory Greenman return 0;
60457974a55SGregory Greenman
60557974a55SGregory Greenman err:
60657974a55SGregory Greenman iwl_mvm_mld_sta_rm_all_sta_links(mvm, mvm_sta);
60757974a55SGregory Greenman return ret;
60857974a55SGregory Greenman }
60957974a55SGregory Greenman
iwl_mvm_mld_set_ap_sta_id(struct ieee80211_sta * sta,struct iwl_mvm_vif_link_info * vif_link,struct iwl_mvm_link_sta * sta_link)61057974a55SGregory Greenman static void iwl_mvm_mld_set_ap_sta_id(struct ieee80211_sta *sta,
61157974a55SGregory Greenman struct iwl_mvm_vif_link_info *vif_link,
61257974a55SGregory Greenman struct iwl_mvm_link_sta *sta_link)
61357974a55SGregory Greenman {
61457974a55SGregory Greenman if (!sta->tdls) {
61557974a55SGregory Greenman WARN_ON(vif_link->ap_sta_id != IWL_MVM_INVALID_STA);
61657974a55SGregory Greenman vif_link->ap_sta_id = sta_link->sta_id;
61757974a55SGregory Greenman } else {
61857974a55SGregory Greenman WARN_ON(vif_link->ap_sta_id == IWL_MVM_INVALID_STA);
61957974a55SGregory Greenman }
62057974a55SGregory Greenman }
62157974a55SGregory Greenman
62257974a55SGregory Greenman /* FIXME: consider waiting for mac80211 to add the STA instead of allocating
62357974a55SGregory Greenman * queues here
62457974a55SGregory Greenman */
iwl_mvm_alloc_sta_after_restart(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_sta * sta)62557974a55SGregory Greenman static int iwl_mvm_alloc_sta_after_restart(struct iwl_mvm *mvm,
62657974a55SGregory Greenman struct ieee80211_vif *vif,
62757974a55SGregory Greenman struct ieee80211_sta *sta)
62857974a55SGregory Greenman {
62957974a55SGregory Greenman struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
63057974a55SGregory Greenman struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
63157974a55SGregory Greenman struct ieee80211_link_sta *link_sta;
63257974a55SGregory Greenman unsigned int link_id;
633164a52d4SJohannes Berg /* no active link found */
634164a52d4SJohannes Berg int ret = -EINVAL;
635164a52d4SJohannes Berg int sta_id;
63657974a55SGregory Greenman
63757974a55SGregory Greenman /* First add an empty station since allocating a queue requires
63857974a55SGregory Greenman * a valid station. Since we need a link_id to allocate a station,
63957974a55SGregory Greenman * pick up the first valid one.
64057974a55SGregory Greenman */
64157974a55SGregory Greenman for_each_sta_active_link(vif, sta, link_sta, link_id) {
64257974a55SGregory Greenman struct iwl_mvm_vif_link_info *mvm_link;
64357974a55SGregory Greenman struct ieee80211_bss_conf *link_conf =
64457974a55SGregory Greenman link_conf_dereference_protected(vif, link_id);
64557974a55SGregory Greenman struct iwl_mvm_link_sta *mvm_link_sta =
64657974a55SGregory Greenman rcu_dereference_protected(mvm_sta->link[link_id],
64757974a55SGregory Greenman lockdep_is_held(&mvm->mutex));
64857974a55SGregory Greenman
64957974a55SGregory Greenman if (!link_conf)
65057974a55SGregory Greenman continue;
65157974a55SGregory Greenman
65257974a55SGregory Greenman mvm_link = mvmvif->link[link_conf->link_id];
65357974a55SGregory Greenman
65457974a55SGregory Greenman if (!mvm_link || !mvm_link_sta)
65557974a55SGregory Greenman continue;
65657974a55SGregory Greenman
65757974a55SGregory Greenman sta_id = mvm_link_sta->sta_id;
658164a52d4SJohannes Berg ret = iwl_mvm_mld_cfg_sta(mvm, sta, vif, link_sta,
659164a52d4SJohannes Berg link_conf, mvm_link_sta);
66057974a55SGregory Greenman if (ret)
66157974a55SGregory Greenman return ret;
66257974a55SGregory Greenman
66357974a55SGregory Greenman rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], sta);
664b8a85a1dSJohannes Berg rcu_assign_pointer(mvm->fw_id_to_link_sta[sta_id], link_sta);
665164a52d4SJohannes Berg ret = 0;
66657974a55SGregory Greenman }
66757974a55SGregory Greenman
668164a52d4SJohannes Berg iwl_mvm_realloc_queues_after_restart(mvm, sta);
669164a52d4SJohannes Berg
670164a52d4SJohannes Berg return ret;
67157974a55SGregory Greenman }
67257974a55SGregory Greenman
iwl_mvm_mld_add_sta(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_sta * sta)67357974a55SGregory Greenman int iwl_mvm_mld_add_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
67457974a55SGregory Greenman struct ieee80211_sta *sta)
67557974a55SGregory Greenman {
67657974a55SGregory Greenman struct iwl_mvm_vif *mvm_vif = iwl_mvm_vif_from_mac80211(vif);
67757974a55SGregory Greenman struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
67857974a55SGregory Greenman unsigned long link_sta_added_to_fw = 0;
67957974a55SGregory Greenman struct ieee80211_link_sta *link_sta;
68057974a55SGregory Greenman int ret = 0;
68157974a55SGregory Greenman unsigned int link_id;
68257974a55SGregory Greenman
68357974a55SGregory Greenman lockdep_assert_held(&mvm->mutex);
68457974a55SGregory Greenman
68557974a55SGregory Greenman if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
68657974a55SGregory Greenman ret = iwl_mvm_mld_alloc_sta_links(mvm, vif, sta);
68757974a55SGregory Greenman if (ret)
68857974a55SGregory Greenman return ret;
68987f7e243SMiri Korenblit
69087f7e243SMiri Korenblit spin_lock_init(&mvm_sta->lock);
69187f7e243SMiri Korenblit
69257974a55SGregory Greenman ret = iwl_mvm_sta_init(mvm, vif, sta, IWL_MVM_INVALID_STA,
69387f7e243SMiri Korenblit STATION_TYPE_PEER);
6949e949dfdSJohannes Berg } else {
6959e949dfdSJohannes Berg ret = iwl_mvm_alloc_sta_after_restart(mvm, vif, sta);
6969e949dfdSJohannes Berg }
6979e949dfdSJohannes Berg
69887f7e243SMiri Korenblit if (ret)
69957974a55SGregory Greenman goto err;
70087f7e243SMiri Korenblit
70157974a55SGregory Greenman /* at this stage sta link pointers are already allocated */
70257974a55SGregory Greenman ret = iwl_mvm_mld_update_sta(mvm, vif, sta);
70387f7e243SMiri Korenblit
70457974a55SGregory Greenman for_each_sta_active_link(vif, sta, link_sta, link_id) {
70557974a55SGregory Greenman struct ieee80211_bss_conf *link_conf =
7060d504ca1SJohannes Berg link_conf_dereference_protected(vif, link_id);
70757974a55SGregory Greenman struct iwl_mvm_link_sta *mvm_link_sta =
70857974a55SGregory Greenman rcu_dereference_protected(mvm_sta->link[link_id],
70957974a55SGregory Greenman lockdep_is_held(&mvm->mutex));
71057974a55SGregory Greenman
711cf3b66ecSDan Carpenter if (WARN_ON(!link_conf || !mvm_link_sta)) {
712cf3b66ecSDan Carpenter ret = -EINVAL;
71357974a55SGregory Greenman goto err;
714cf3b66ecSDan Carpenter }
71557974a55SGregory Greenman
71657974a55SGregory Greenman ret = iwl_mvm_mld_cfg_sta(mvm, sta, vif, link_sta, link_conf,
71757974a55SGregory Greenman mvm_link_sta);
71857974a55SGregory Greenman if (ret)
71957974a55SGregory Greenman goto err;
72057974a55SGregory Greenman
72157974a55SGregory Greenman link_sta_added_to_fw |= BIT(link_id);
72257974a55SGregory Greenman
72357974a55SGregory Greenman if (vif->type == NL80211_IFTYPE_STATION)
72457974a55SGregory Greenman iwl_mvm_mld_set_ap_sta_id(sta, mvm_vif->link[link_id],
72557974a55SGregory Greenman mvm_link_sta);
72657974a55SGregory Greenman }
72787f7e243SMiri Korenblit
72887f7e243SMiri Korenblit return 0;
72957974a55SGregory Greenman
73057974a55SGregory Greenman err:
73157974a55SGregory Greenman /* remove all already allocated stations in FW */
73257974a55SGregory Greenman for_each_set_bit(link_id, &link_sta_added_to_fw,
73357974a55SGregory Greenman IEEE80211_MLD_MAX_NUM_LINKS) {
73457974a55SGregory Greenman struct iwl_mvm_link_sta *mvm_link_sta =
73557974a55SGregory Greenman rcu_dereference_protected(mvm_sta->link[link_id],
73657974a55SGregory Greenman lockdep_is_held(&mvm->mutex));
73757974a55SGregory Greenman
73857974a55SGregory Greenman iwl_mvm_mld_rm_sta_from_fw(mvm, mvm_link_sta->sta_id);
73957974a55SGregory Greenman }
74057974a55SGregory Greenman
74157974a55SGregory Greenman /* free all sta resources in the driver */
74257974a55SGregory Greenman iwl_mvm_mld_sta_rm_all_sta_links(mvm, mvm_sta);
74357974a55SGregory Greenman return ret;
74487f7e243SMiri Korenblit }
74587f7e243SMiri Korenblit
iwl_mvm_mld_update_sta(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_sta * sta)74687f7e243SMiri Korenblit int iwl_mvm_mld_update_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
74787f7e243SMiri Korenblit struct ieee80211_sta *sta)
74887f7e243SMiri Korenblit {
74957974a55SGregory Greenman struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
75057974a55SGregory Greenman struct ieee80211_link_sta *link_sta;
75157974a55SGregory Greenman unsigned int link_id;
752e234c362SMukesh Sisodiya int ret = -EINVAL;
75357974a55SGregory Greenman
75487f7e243SMiri Korenblit lockdep_assert_held(&mvm->mutex);
75587f7e243SMiri Korenblit
75657974a55SGregory Greenman for_each_sta_active_link(vif, sta, link_sta, link_id) {
75757974a55SGregory Greenman struct ieee80211_bss_conf *link_conf =
7580d504ca1SJohannes Berg link_conf_dereference_protected(vif, link_id);
75957974a55SGregory Greenman struct iwl_mvm_link_sta *mvm_link_sta =
76057974a55SGregory Greenman rcu_dereference_protected(mvm_sta->link[link_id],
76157974a55SGregory Greenman lockdep_is_held(&mvm->mutex));
76257974a55SGregory Greenman
76357974a55SGregory Greenman if (WARN_ON(!link_conf || !mvm_link_sta))
76457974a55SGregory Greenman return -EINVAL;
76557974a55SGregory Greenman
76657974a55SGregory Greenman ret = iwl_mvm_mld_cfg_sta(mvm, sta, vif, link_sta, link_conf,
76757974a55SGregory Greenman mvm_link_sta);
76857974a55SGregory Greenman
76957974a55SGregory Greenman if (ret) {
77057974a55SGregory Greenman IWL_ERR(mvm, "Failed to update sta link %d\n", link_id);
77157974a55SGregory Greenman break;
77257974a55SGregory Greenman }
77357974a55SGregory Greenman }
77457974a55SGregory Greenman
77557974a55SGregory Greenman return ret;
77687f7e243SMiri Korenblit }
77787f7e243SMiri Korenblit
iwl_mvm_mld_disable_sta_queues(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_sta * sta)77887f7e243SMiri Korenblit static void iwl_mvm_mld_disable_sta_queues(struct iwl_mvm *mvm,
77987f7e243SMiri Korenblit struct ieee80211_vif *vif,
78087f7e243SMiri Korenblit struct ieee80211_sta *sta)
78187f7e243SMiri Korenblit {
78287f7e243SMiri Korenblit struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
7836f2c5f38SJohannes Berg u32 sta_mask = iwl_mvm_sta_fw_id_mask(mvm, sta, -1);
78487f7e243SMiri Korenblit int i;
78587f7e243SMiri Korenblit
78687f7e243SMiri Korenblit lockdep_assert_held(&mvm->mutex);
78787f7e243SMiri Korenblit
78887f7e243SMiri Korenblit for (i = 0; i < ARRAY_SIZE(mvm_sta->tid_data); i++) {
78987f7e243SMiri Korenblit if (mvm_sta->tid_data[i].txq_id == IWL_MVM_INVALID_QUEUE)
79087f7e243SMiri Korenblit continue;
79187f7e243SMiri Korenblit
7926f2c5f38SJohannes Berg iwl_mvm_mld_disable_txq(mvm, sta_mask,
79375700ee1SMiri Korenblit &mvm_sta->tid_data[i].txq_id, i);
79487f7e243SMiri Korenblit mvm_sta->tid_data[i].txq_id = IWL_MVM_INVALID_QUEUE;
79587f7e243SMiri Korenblit }
79687f7e243SMiri Korenblit
79787f7e243SMiri Korenblit for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
79887f7e243SMiri Korenblit struct iwl_mvm_txq *mvmtxq =
79987f7e243SMiri Korenblit iwl_mvm_txq_from_mac80211(sta->txq[i]);
80087f7e243SMiri Korenblit
80187f7e243SMiri Korenblit mvmtxq->txq_id = IWL_MVM_INVALID_QUEUE;
80287f7e243SMiri Korenblit }
80387f7e243SMiri Korenblit }
80487f7e243SMiri Korenblit
iwl_mvm_mld_rm_sta(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_sta * sta)80587f7e243SMiri Korenblit int iwl_mvm_mld_rm_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
80687f7e243SMiri Korenblit struct ieee80211_sta *sta)
80787f7e243SMiri Korenblit {
80887f7e243SMiri Korenblit struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
80957974a55SGregory Greenman struct ieee80211_link_sta *link_sta;
81057974a55SGregory Greenman unsigned int link_id;
81187f7e243SMiri Korenblit int ret;
81287f7e243SMiri Korenblit
81387f7e243SMiri Korenblit lockdep_assert_held(&mvm->mutex);
81487f7e243SMiri Korenblit
81587f7e243SMiri Korenblit /* flush its queues here since we are freeing mvm_sta */
81657974a55SGregory Greenman for_each_sta_active_link(vif, sta, link_sta, link_id) {
81757974a55SGregory Greenman struct iwl_mvm_link_sta *mvm_link_sta =
81857974a55SGregory Greenman rcu_dereference_protected(mvm_sta->link[link_id],
81957974a55SGregory Greenman lockdep_is_held(&mvm->mutex));
82057974a55SGregory Greenman
82157974a55SGregory Greenman if (WARN_ON(!mvm_link_sta))
82257974a55SGregory Greenman return -EINVAL;
82357974a55SGregory Greenman
82457974a55SGregory Greenman ret = iwl_mvm_flush_sta_tids(mvm, mvm_link_sta->sta_id,
82557974a55SGregory Greenman 0xffff);
82687f7e243SMiri Korenblit if (ret)
82787f7e243SMiri Korenblit return ret;
82857974a55SGregory Greenman }
82957974a55SGregory Greenman
83087f7e243SMiri Korenblit ret = iwl_mvm_wait_sta_queues_empty(mvm, mvm_sta);
83187f7e243SMiri Korenblit if (ret)
83287f7e243SMiri Korenblit return ret;
83387f7e243SMiri Korenblit
83487f7e243SMiri Korenblit iwl_mvm_mld_disable_sta_queues(mvm, vif, sta);
83587f7e243SMiri Korenblit
83657974a55SGregory Greenman for_each_sta_active_link(vif, sta, link_sta, link_id) {
83757974a55SGregory Greenman struct iwl_mvm_link_sta *mvm_link_sta =
83857974a55SGregory Greenman rcu_dereference_protected(mvm_sta->link[link_id],
83957974a55SGregory Greenman lockdep_is_held(&mvm->mutex));
840881d0548SJohannes Berg bool stay_in_fw;
84157974a55SGregory Greenman
842a6ef8a88SJohannes Berg stay_in_fw = iwl_mvm_sta_del(mvm, vif, sta, link_sta, &ret);
843881d0548SJohannes Berg if (ret)
844881d0548SJohannes Berg break;
84587f7e243SMiri Korenblit
846881d0548SJohannes Berg if (!stay_in_fw)
847881d0548SJohannes Berg ret = iwl_mvm_mld_rm_sta_from_fw(mvm,
848881d0548SJohannes Berg mvm_link_sta->sta_id);
849881d0548SJohannes Berg
850881d0548SJohannes Berg iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_link_sta,
851881d0548SJohannes Berg link_id, stay_in_fw);
85257974a55SGregory Greenman }
85357974a55SGregory Greenman
85487f7e243SMiri Korenblit return ret;
85587f7e243SMiri Korenblit }
85687f7e243SMiri Korenblit
iwl_mvm_mld_rm_sta_id(struct iwl_mvm * mvm,u8 sta_id)857881d0548SJohannes Berg int iwl_mvm_mld_rm_sta_id(struct iwl_mvm *mvm, u8 sta_id)
858660eba5aSMiri Korenblit {
85994f80a8eSBenjamin Berg int ret;
860660eba5aSMiri Korenblit
861660eba5aSMiri Korenblit lockdep_assert_held(&mvm->mutex);
862660eba5aSMiri Korenblit
86394f80a8eSBenjamin Berg if (WARN_ON(sta_id == IWL_MVM_INVALID_STA))
86494f80a8eSBenjamin Berg return 0;
86594f80a8eSBenjamin Berg
86694f80a8eSBenjamin Berg ret = iwl_mvm_mld_rm_sta_from_fw(mvm, sta_id);
86794f80a8eSBenjamin Berg
868660eba5aSMiri Korenblit RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL);
869b8a85a1dSJohannes Berg RCU_INIT_POINTER(mvm->fw_id_to_link_sta[sta_id], NULL);
870660eba5aSMiri Korenblit return ret;
871660eba5aSMiri Korenblit }
872660eba5aSMiri Korenblit
iwl_mvm_mld_sta_modify_disable_tx(struct iwl_mvm * mvm,struct iwl_mvm_sta * mvmsta,bool disable)87356f4f12bSMiri Korenblit void iwl_mvm_mld_sta_modify_disable_tx(struct iwl_mvm *mvm,
87456f4f12bSMiri Korenblit struct iwl_mvm_sta *mvmsta,
8756f71e90eSMiri Korenblit bool disable)
8766f71e90eSMiri Korenblit {
8776f71e90eSMiri Korenblit struct iwl_mvm_sta_disable_tx_cmd cmd;
8786f71e90eSMiri Korenblit int ret;
8796f71e90eSMiri Korenblit
88056f4f12bSMiri Korenblit cmd.sta_id = cpu_to_le32(mvmsta->deflink.sta_id);
8816f71e90eSMiri Korenblit cmd.disable = cpu_to_le32(disable);
8826f71e90eSMiri Korenblit
8836f71e90eSMiri Korenblit ret = iwl_mvm_send_cmd_pdu(mvm,
8846f71e90eSMiri Korenblit WIDE_ID(MAC_CONF_GROUP, STA_DISABLE_TX_CMD),
8856f71e90eSMiri Korenblit CMD_ASYNC, sizeof(cmd), &cmd);
8866f71e90eSMiri Korenblit if (ret)
8876f71e90eSMiri Korenblit IWL_ERR(mvm,
8886f71e90eSMiri Korenblit "Failed to send STA_DISABLE_TX_CMD command (%d)\n",
8896f71e90eSMiri Korenblit ret);
89056f4f12bSMiri Korenblit }
89156f4f12bSMiri Korenblit
iwl_mvm_mld_sta_modify_disable_tx_ap(struct iwl_mvm * mvm,struct ieee80211_sta * sta,bool disable)89256f4f12bSMiri Korenblit void iwl_mvm_mld_sta_modify_disable_tx_ap(struct iwl_mvm *mvm,
89356f4f12bSMiri Korenblit struct ieee80211_sta *sta,
89456f4f12bSMiri Korenblit bool disable)
89556f4f12bSMiri Korenblit {
89656f4f12bSMiri Korenblit struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
89756f4f12bSMiri Korenblit
89856f4f12bSMiri Korenblit spin_lock_bh(&mvm_sta->lock);
89956f4f12bSMiri Korenblit
90056f4f12bSMiri Korenblit if (mvm_sta->disable_tx == disable) {
90156f4f12bSMiri Korenblit spin_unlock_bh(&mvm_sta->lock);
90256f4f12bSMiri Korenblit return;
90356f4f12bSMiri Korenblit }
90456f4f12bSMiri Korenblit
90556f4f12bSMiri Korenblit iwl_mvm_mld_sta_modify_disable_tx(mvm, mvm_sta, disable);
9066f71e90eSMiri Korenblit
9076f71e90eSMiri Korenblit spin_unlock_bh(&mvm_sta->lock);
9086f71e90eSMiri Korenblit }
9096f71e90eSMiri Korenblit
iwl_mvm_mld_modify_all_sta_disable_tx(struct iwl_mvm * mvm,struct iwl_mvm_vif * mvmvif,bool disable)9106f71e90eSMiri Korenblit void iwl_mvm_mld_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
9116f71e90eSMiri Korenblit struct iwl_mvm_vif *mvmvif,
9126f71e90eSMiri Korenblit bool disable)
9136f71e90eSMiri Korenblit {
9146f71e90eSMiri Korenblit struct ieee80211_sta *sta;
9156f71e90eSMiri Korenblit struct iwl_mvm_sta *mvm_sta;
9166f71e90eSMiri Korenblit int i;
9176f71e90eSMiri Korenblit
9186f71e90eSMiri Korenblit rcu_read_lock();
9196f71e90eSMiri Korenblit
9206f71e90eSMiri Korenblit /* Block/unblock all the stations of the given mvmvif */
9216f71e90eSMiri Korenblit for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {
9226f71e90eSMiri Korenblit sta = rcu_dereference(mvm->fw_id_to_mac_id[i]);
9236f71e90eSMiri Korenblit if (IS_ERR_OR_NULL(sta))
9246f71e90eSMiri Korenblit continue;
9256f71e90eSMiri Korenblit
9266f71e90eSMiri Korenblit mvm_sta = iwl_mvm_sta_from_mac80211(sta);
9276f71e90eSMiri Korenblit if (mvm_sta->mac_id_n_color !=
9286f71e90eSMiri Korenblit FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color))
9296f71e90eSMiri Korenblit continue;
9306f71e90eSMiri Korenblit
93156f4f12bSMiri Korenblit iwl_mvm_mld_sta_modify_disable_tx(mvm, mvm_sta, disable);
9326f71e90eSMiri Korenblit }
9336f71e90eSMiri Korenblit
9346f71e90eSMiri Korenblit rcu_read_unlock();
9356f71e90eSMiri Korenblit }
9366e4198d3SShaul Triebitz
iwl_mvm_mld_update_sta_queues(struct iwl_mvm * mvm,struct ieee80211_sta * sta,u32 old_sta_mask,u32 new_sta_mask)9377a243c6bSJohannes Berg static int iwl_mvm_mld_update_sta_queues(struct iwl_mvm *mvm,
9387a243c6bSJohannes Berg struct ieee80211_sta *sta,
9396e4198d3SShaul Triebitz u32 old_sta_mask,
9406e4198d3SShaul Triebitz u32 new_sta_mask)
9416e4198d3SShaul Triebitz {
9427a243c6bSJohannes Berg struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
9436e4198d3SShaul Triebitz struct iwl_scd_queue_cfg_cmd cmd = {
9446e4198d3SShaul Triebitz .operation = cpu_to_le32(IWL_SCD_QUEUE_MODIFY),
9456e4198d3SShaul Triebitz .u.modify.old_sta_mask = cpu_to_le32(old_sta_mask),
9466e4198d3SShaul Triebitz .u.modify.new_sta_mask = cpu_to_le32(new_sta_mask),
9476e4198d3SShaul Triebitz };
9486e4198d3SShaul Triebitz struct iwl_host_cmd hcmd = {
9496e4198d3SShaul Triebitz .id = WIDE_ID(DATA_PATH_GROUP, SCD_QUEUE_CONFIG_CMD),
9506e4198d3SShaul Triebitz .len[0] = sizeof(cmd),
9516e4198d3SShaul Triebitz .data[0] = &cmd
9526e4198d3SShaul Triebitz };
9536e4198d3SShaul Triebitz int tid;
9546e4198d3SShaul Triebitz int ret;
9556e4198d3SShaul Triebitz
9566e4198d3SShaul Triebitz lockdep_assert_held(&mvm->mutex);
9576e4198d3SShaul Triebitz
9586e4198d3SShaul Triebitz for (tid = 0; tid <= IWL_MAX_TID_COUNT; tid++) {
9596e4198d3SShaul Triebitz struct iwl_mvm_tid_data *tid_data = &mvm_sta->tid_data[tid];
9606e4198d3SShaul Triebitz int txq_id = tid_data->txq_id;
9616e4198d3SShaul Triebitz
9626e4198d3SShaul Triebitz if (txq_id == IWL_MVM_INVALID_QUEUE)
9636e4198d3SShaul Triebitz continue;
9646e4198d3SShaul Triebitz
9656e4198d3SShaul Triebitz if (tid == IWL_MAX_TID_COUNT)
9666e4198d3SShaul Triebitz cmd.u.modify.tid = cpu_to_le32(IWL_MGMT_TID);
9676e4198d3SShaul Triebitz else
9686e4198d3SShaul Triebitz cmd.u.modify.tid = cpu_to_le32(tid);
9696e4198d3SShaul Triebitz
9706e4198d3SShaul Triebitz ret = iwl_mvm_send_cmd(mvm, &hcmd);
9716e4198d3SShaul Triebitz if (ret)
9726e4198d3SShaul Triebitz return ret;
9736e4198d3SShaul Triebitz }
9746e4198d3SShaul Triebitz
9756e4198d3SShaul Triebitz return 0;
9766e4198d3SShaul Triebitz }
9776e4198d3SShaul Triebitz
iwl_mvm_mld_update_sta_baids(struct iwl_mvm * mvm,u32 old_sta_mask,u32 new_sta_mask)9787a243c6bSJohannes Berg static int iwl_mvm_mld_update_sta_baids(struct iwl_mvm *mvm,
9797a243c6bSJohannes Berg u32 old_sta_mask,
9807a243c6bSJohannes Berg u32 new_sta_mask)
9817a243c6bSJohannes Berg {
9827a243c6bSJohannes Berg struct iwl_rx_baid_cfg_cmd cmd = {
9837a243c6bSJohannes Berg .action = cpu_to_le32(IWL_RX_BAID_ACTION_MODIFY),
9847a243c6bSJohannes Berg .modify.old_sta_id_mask = cpu_to_le32(old_sta_mask),
9857a243c6bSJohannes Berg .modify.new_sta_id_mask = cpu_to_le32(new_sta_mask),
9867a243c6bSJohannes Berg };
9877a243c6bSJohannes Berg u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, RX_BAID_ALLOCATION_CONFIG_CMD);
9887a243c6bSJohannes Berg int baid;
9897a243c6bSJohannes Berg
9907a243c6bSJohannes Berg BUILD_BUG_ON(sizeof(struct iwl_rx_baid_cfg_resp) != sizeof(baid));
9917a243c6bSJohannes Berg
9927a243c6bSJohannes Berg for (baid = 0; baid < ARRAY_SIZE(mvm->baid_map); baid++) {
9937a243c6bSJohannes Berg struct iwl_mvm_baid_data *data;
9947a243c6bSJohannes Berg int ret;
9957a243c6bSJohannes Berg
9967a243c6bSJohannes Berg data = rcu_dereference_protected(mvm->baid_map[baid],
9977a243c6bSJohannes Berg lockdep_is_held(&mvm->mutex));
9987a243c6bSJohannes Berg if (!data)
9997a243c6bSJohannes Berg continue;
10007a243c6bSJohannes Berg
10017a243c6bSJohannes Berg if (!(data->sta_mask & old_sta_mask))
10027a243c6bSJohannes Berg continue;
10037a243c6bSJohannes Berg
10047a243c6bSJohannes Berg WARN_ONCE(data->sta_mask != old_sta_mask,
10057a243c6bSJohannes Berg "BAID data for %d corrupted - expected 0x%x found 0x%x\n",
10067a243c6bSJohannes Berg baid, old_sta_mask, data->sta_mask);
10077a243c6bSJohannes Berg
10087a243c6bSJohannes Berg cmd.modify.tid = cpu_to_le32(data->tid);
10097a243c6bSJohannes Berg
1010*ef227372SJohannes Berg ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, CMD_SEND_IN_RFKILL,
1011*ef227372SJohannes Berg sizeof(cmd), &cmd);
10127a243c6bSJohannes Berg data->sta_mask = new_sta_mask;
10137a243c6bSJohannes Berg if (ret)
10147a243c6bSJohannes Berg return ret;
10157a243c6bSJohannes Berg }
10167a243c6bSJohannes Berg
10177a243c6bSJohannes Berg return 0;
10187a243c6bSJohannes Berg }
10197a243c6bSJohannes Berg
iwl_mvm_mld_update_sta_resources(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_sta * sta,u32 old_sta_mask,u32 new_sta_mask)10207a243c6bSJohannes Berg static int iwl_mvm_mld_update_sta_resources(struct iwl_mvm *mvm,
10218642ddb2SJohannes Berg struct ieee80211_vif *vif,
10227a243c6bSJohannes Berg struct ieee80211_sta *sta,
10237a243c6bSJohannes Berg u32 old_sta_mask,
10247a243c6bSJohannes Berg u32 new_sta_mask)
10257a243c6bSJohannes Berg {
10267a243c6bSJohannes Berg int ret;
10277a243c6bSJohannes Berg
10287a243c6bSJohannes Berg ret = iwl_mvm_mld_update_sta_queues(mvm, sta,
10297a243c6bSJohannes Berg old_sta_mask,
10307a243c6bSJohannes Berg new_sta_mask);
10317a243c6bSJohannes Berg if (ret)
10327a243c6bSJohannes Berg return ret;
10337a243c6bSJohannes Berg
10348642ddb2SJohannes Berg ret = iwl_mvm_mld_update_sta_keys(mvm, vif, sta,
10358642ddb2SJohannes Berg old_sta_mask,
10368642ddb2SJohannes Berg new_sta_mask);
10378642ddb2SJohannes Berg if (ret)
10388642ddb2SJohannes Berg return ret;
10398642ddb2SJohannes Berg
10407a243c6bSJohannes Berg return iwl_mvm_mld_update_sta_baids(mvm, old_sta_mask, new_sta_mask);
10417a243c6bSJohannes Berg }
10427a243c6bSJohannes Berg
iwl_mvm_mld_update_sta_links(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_sta * sta,u16 old_links,u16 new_links)10436e4198d3SShaul Triebitz int iwl_mvm_mld_update_sta_links(struct iwl_mvm *mvm,
10446e4198d3SShaul Triebitz struct ieee80211_vif *vif,
10456e4198d3SShaul Triebitz struct ieee80211_sta *sta,
10466e4198d3SShaul Triebitz u16 old_links, u16 new_links)
10476e4198d3SShaul Triebitz {
10486e4198d3SShaul Triebitz struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
10496e4198d3SShaul Triebitz struct iwl_mvm_vif *mvm_vif = iwl_mvm_vif_from_mac80211(vif);
10506e4198d3SShaul Triebitz struct iwl_mvm_link_sta *mvm_sta_link;
10516e4198d3SShaul Triebitz struct iwl_mvm_vif_link_info *mvm_vif_link;
10526e4198d3SShaul Triebitz unsigned long links_to_add = ~old_links & new_links;
10536e4198d3SShaul Triebitz unsigned long links_to_rem = old_links & ~new_links;
10546e4198d3SShaul Triebitz unsigned long old_links_long = old_links;
10556e4198d3SShaul Triebitz u32 current_sta_mask = 0, sta_mask_added = 0, sta_mask_to_rem = 0;
10566e4198d3SShaul Triebitz unsigned long link_sta_added_to_fw = 0, link_sta_allocated = 0;
10576e4198d3SShaul Triebitz unsigned int link_id;
10586e4198d3SShaul Triebitz int ret;
10596e4198d3SShaul Triebitz
10606e4198d3SShaul Triebitz lockdep_assert_held(&mvm->mutex);
10616e4198d3SShaul Triebitz
10626e4198d3SShaul Triebitz for_each_set_bit(link_id, &old_links_long,
10636e4198d3SShaul Triebitz IEEE80211_MLD_MAX_NUM_LINKS) {
10646e4198d3SShaul Triebitz mvm_sta_link =
10656e4198d3SShaul Triebitz rcu_dereference_protected(mvm_sta->link[link_id],
10666e4198d3SShaul Triebitz lockdep_is_held(&mvm->mutex));
10676e4198d3SShaul Triebitz
10686e4198d3SShaul Triebitz if (WARN_ON(!mvm_sta_link)) {
10696e4198d3SShaul Triebitz ret = -EINVAL;
10706e4198d3SShaul Triebitz goto err;
10716e4198d3SShaul Triebitz }
10726e4198d3SShaul Triebitz
10736e4198d3SShaul Triebitz current_sta_mask |= BIT(mvm_sta_link->sta_id);
10746e4198d3SShaul Triebitz if (links_to_rem & BIT(link_id))
10756e4198d3SShaul Triebitz sta_mask_to_rem |= BIT(mvm_sta_link->sta_id);
10766e4198d3SShaul Triebitz }
10776e4198d3SShaul Triebitz
10786e4198d3SShaul Triebitz if (sta_mask_to_rem) {
10798642ddb2SJohannes Berg ret = iwl_mvm_mld_update_sta_resources(mvm, vif, sta,
10806e4198d3SShaul Triebitz current_sta_mask,
10817a243c6bSJohannes Berg current_sta_mask &
10827a243c6bSJohannes Berg ~sta_mask_to_rem);
10836e4198d3SShaul Triebitz if (WARN_ON(ret))
10846e4198d3SShaul Triebitz goto err;
10856e4198d3SShaul Triebitz
10866e4198d3SShaul Triebitz current_sta_mask &= ~sta_mask_to_rem;
10876e4198d3SShaul Triebitz }
10886e4198d3SShaul Triebitz
10896e4198d3SShaul Triebitz for_each_set_bit(link_id, &links_to_rem, IEEE80211_MLD_MAX_NUM_LINKS) {
10906e4198d3SShaul Triebitz mvm_sta_link =
10916e4198d3SShaul Triebitz rcu_dereference_protected(mvm_sta->link[link_id],
10926e4198d3SShaul Triebitz lockdep_is_held(&mvm->mutex));
10936e4198d3SShaul Triebitz mvm_vif_link = mvm_vif->link[link_id];
10946e4198d3SShaul Triebitz
10956e4198d3SShaul Triebitz if (WARN_ON(!mvm_sta_link || !mvm_vif_link)) {
10966e4198d3SShaul Triebitz ret = -EINVAL;
10976e4198d3SShaul Triebitz goto err;
10986e4198d3SShaul Triebitz }
10996e4198d3SShaul Triebitz
11006e4198d3SShaul Triebitz ret = iwl_mvm_mld_rm_sta_from_fw(mvm, mvm_sta_link->sta_id);
11016e4198d3SShaul Triebitz if (WARN_ON(ret))
11026e4198d3SShaul Triebitz goto err;
11036e4198d3SShaul Triebitz
11046e4198d3SShaul Triebitz if (vif->type == NL80211_IFTYPE_STATION)
11056e4198d3SShaul Triebitz mvm_vif_link->ap_sta_id = IWL_MVM_INVALID_STA;
11066e4198d3SShaul Triebitz
1107881d0548SJohannes Berg iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_sta_link, link_id,
1108881d0548SJohannes Berg false);
11096e4198d3SShaul Triebitz }
11106e4198d3SShaul Triebitz
11116e4198d3SShaul Triebitz for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) {
11126e4198d3SShaul Triebitz struct ieee80211_bss_conf *link_conf =
11130d504ca1SJohannes Berg link_conf_dereference_protected(vif, link_id);
11146e4198d3SShaul Triebitz struct ieee80211_link_sta *link_sta =
11150d504ca1SJohannes Berg link_sta_dereference_protected(sta, link_id);
11166e4198d3SShaul Triebitz mvm_vif_link = mvm_vif->link[link_id];
11176e4198d3SShaul Triebitz
11186e4198d3SShaul Triebitz if (WARN_ON(!mvm_vif_link || !link_conf || !link_sta ||
11196e4198d3SShaul Triebitz mvm_sta->link[link_id])) {
11206e4198d3SShaul Triebitz ret = -EINVAL;
11216e4198d3SShaul Triebitz goto err;
11226e4198d3SShaul Triebitz }
11236e4198d3SShaul Triebitz
11246e4198d3SShaul Triebitz ret = iwl_mvm_mld_alloc_sta_link(mvm, vif, sta, link_id);
11256e4198d3SShaul Triebitz if (WARN_ON(ret))
11266e4198d3SShaul Triebitz goto err;
11276e4198d3SShaul Triebitz
112824dc33ccSBenjamin Berg link_sta->agg.max_rc_amsdu_len = 1;
112924dc33ccSBenjamin Berg ieee80211_sta_recalc_aggregates(sta);
113024dc33ccSBenjamin Berg
11316e4198d3SShaul Triebitz mvm_sta_link =
11326e4198d3SShaul Triebitz rcu_dereference_protected(mvm_sta->link[link_id],
11336e4198d3SShaul Triebitz lockdep_is_held(&mvm->mutex));
11346e4198d3SShaul Triebitz
11356e4198d3SShaul Triebitz if (WARN_ON(!mvm_sta_link)) {
11366e4198d3SShaul Triebitz ret = -EINVAL;
11376e4198d3SShaul Triebitz goto err;
11386e4198d3SShaul Triebitz }
11396e4198d3SShaul Triebitz
11406e4198d3SShaul Triebitz if (vif->type == NL80211_IFTYPE_STATION)
11416e4198d3SShaul Triebitz iwl_mvm_mld_set_ap_sta_id(sta, mvm_vif_link,
11426e4198d3SShaul Triebitz mvm_sta_link);
11436e4198d3SShaul Triebitz
11446e4198d3SShaul Triebitz link_sta_allocated |= BIT(link_id);
11456e4198d3SShaul Triebitz
11466e4198d3SShaul Triebitz sta_mask_added |= BIT(mvm_sta_link->sta_id);
11476e4198d3SShaul Triebitz
11486e4198d3SShaul Triebitz ret = iwl_mvm_mld_cfg_sta(mvm, sta, vif, link_sta, link_conf,
11496e4198d3SShaul Triebitz mvm_sta_link);
11506e4198d3SShaul Triebitz if (WARN_ON(ret))
11516e4198d3SShaul Triebitz goto err;
11526e4198d3SShaul Triebitz
11536e4198d3SShaul Triebitz link_sta_added_to_fw |= BIT(link_id);
11549371ac0dSJohannes Berg
11559371ac0dSJohannes Berg iwl_mvm_rs_add_sta_link(mvm, mvm_sta_link);
11566e4198d3SShaul Triebitz }
11576e4198d3SShaul Triebitz
11586e4198d3SShaul Triebitz if (sta_mask_added) {
11598642ddb2SJohannes Berg ret = iwl_mvm_mld_update_sta_resources(mvm, vif, sta,
11606e4198d3SShaul Triebitz current_sta_mask,
11617a243c6bSJohannes Berg current_sta_mask |
11627a243c6bSJohannes Berg sta_mask_added);
11636e4198d3SShaul Triebitz if (WARN_ON(ret))
11646e4198d3SShaul Triebitz goto err;
11656e4198d3SShaul Triebitz }
11666e4198d3SShaul Triebitz
11676e4198d3SShaul Triebitz return 0;
11686e4198d3SShaul Triebitz
11696e4198d3SShaul Triebitz err:
11706e4198d3SShaul Triebitz /* remove all already allocated stations in FW */
11716e4198d3SShaul Triebitz for_each_set_bit(link_id, &link_sta_added_to_fw,
11726e4198d3SShaul Triebitz IEEE80211_MLD_MAX_NUM_LINKS) {
11736e4198d3SShaul Triebitz mvm_sta_link =
11746e4198d3SShaul Triebitz rcu_dereference_protected(mvm_sta->link[link_id],
11756e4198d3SShaul Triebitz lockdep_is_held(&mvm->mutex));
11766e4198d3SShaul Triebitz
11776e4198d3SShaul Triebitz iwl_mvm_mld_rm_sta_from_fw(mvm, mvm_sta_link->sta_id);
11786e4198d3SShaul Triebitz }
11796e4198d3SShaul Triebitz
11806e4198d3SShaul Triebitz /* remove all already allocated station links in driver */
11816e4198d3SShaul Triebitz for_each_set_bit(link_id, &link_sta_allocated,
11826e4198d3SShaul Triebitz IEEE80211_MLD_MAX_NUM_LINKS) {
11836e4198d3SShaul Triebitz mvm_sta_link =
11846e4198d3SShaul Triebitz rcu_dereference_protected(mvm_sta->link[link_id],
11856e4198d3SShaul Triebitz lockdep_is_held(&mvm->mutex));
11866e4198d3SShaul Triebitz
1187881d0548SJohannes Berg iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_sta_link, link_id,
1188881d0548SJohannes Berg false);
11896e4198d3SShaul Triebitz }
11906e4198d3SShaul Triebitz
11916e4198d3SShaul Triebitz return ret;
11926e4198d3SShaul Triebitz }
1193