aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/Makefile2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/binding.c16
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api.h62
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw.c4
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c21
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mvm.h7
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/ops.c1
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sf.c291
8 files changed, 402 insertions, 2 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile
index 285d8c7486f5..3ea47ab27337 100644
--- a/drivers/net/wireless/iwlwifi/mvm/Makefile
+++ b/drivers/net/wireless/iwlwifi/mvm/Makefile
@@ -1,6 +1,6 @@
1obj-$(CONFIG_IWLMVM) += iwlmvm.o 1obj-$(CONFIG_IWLMVM) += iwlmvm.o
2iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o 2iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o
3iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o 3iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o sf.o
4iwlmvm-y += scan.o time-event.o rs.o 4iwlmvm-y += scan.o time-event.o rs.o
5iwlmvm-y += power.o power_legacy.o bt-coex.o 5iwlmvm-y += power.o power_legacy.o bt-coex.o
6iwlmvm-y += led.o tt.o 6iwlmvm-y += led.o tt.o
diff --git a/drivers/net/wireless/iwlwifi/mvm/binding.c b/drivers/net/wireless/iwlwifi/mvm/binding.c
index 93fd1457954b..57d3eed86efa 100644
--- a/drivers/net/wireless/iwlwifi/mvm/binding.c
+++ b/drivers/net/wireless/iwlwifi/mvm/binding.c
@@ -183,15 +183,29 @@ int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
183 if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) 183 if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
184 return -EINVAL; 184 return -EINVAL;
185 185
186 /*
187 * Update SF - Disable if needed. if this fails, SF might still be on
188 * while many macs are bound, which is forbidden - so fail the binding.
189 */
190 if (iwl_mvm_sf_update(mvm, vif, false))
191 return -EINVAL;
192
186 return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, true); 193 return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, true);
187} 194}
188 195
189int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 196int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
190{ 197{
191 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 198 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
199 int ret;
192 200
193 if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) 201 if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
194 return -EINVAL; 202 return -EINVAL;
195 203
196 return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false); 204 ret = iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false);
205
206 if (!ret)
207 if (iwl_mvm_sf_update(mvm, vif, true))
208 IWL_ERR(mvm, "Failed to update SF state\n");
209
210 return ret;
197} 211}
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
index bad5a552dd8d..3af413386ad9 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
@@ -183,6 +183,7 @@ enum {
183 BT_PROFILE_NOTIFICATION = 0xce, 183 BT_PROFILE_NOTIFICATION = 0xce,
184 BT_COEX_CI = 0x5d, 184 BT_COEX_CI = 0x5d,
185 185
186 REPLY_SF_CFG_CMD = 0xd1,
186 REPLY_BEACON_FILTERING_CMD = 0xd2, 187 REPLY_BEACON_FILTERING_CMD = 0xd2,
187 188
188 REPLY_DEBUG_CMD = 0xf0, 189 REPLY_DEBUG_CMD = 0xf0,
@@ -1363,4 +1364,65 @@ struct iwl_notif_statistics { /* STATISTICS_NTFY_API_S_VER_8 */
1363 struct mvm_statistics_general general; 1364 struct mvm_statistics_general general;
1364} __packed; 1365} __packed;
1365 1366
1367/***********************************
1368 * Smart Fifo API
1369 ***********************************/
1370/* Smart Fifo state */
1371enum iwl_sf_state {
1372 SF_LONG_DELAY_ON = 0, /* should never be called by driver */
1373 SF_FULL_ON,
1374 SF_UNINIT,
1375 SF_INIT_OFF,
1376 SF_HW_NUM_STATES
1377};
1378
1379/* Smart Fifo possible scenario */
1380enum iwl_sf_scenario {
1381 SF_SCENARIO_SINGLE_UNICAST,
1382 SF_SCENARIO_AGG_UNICAST,
1383 SF_SCENARIO_MULTICAST,
1384 SF_SCENARIO_BA_RESP,
1385 SF_SCENARIO_TX_RESP,
1386 SF_NUM_SCENARIO
1387};
1388
1389#define SF_TRANSIENT_STATES_NUMBER 2 /* SF_LONG_DELAY_ON and SF_FULL_ON */
1390#define SF_NUM_TIMEOUT_TYPES 2 /* Aging timer and Idle timer */
1391
1392/* smart FIFO default values */
1393#define SF_W_MARK_SISO 4096
1394#define SF_W_MARK_MIMO2 8192
1395#define SF_W_MARK_MIMO3 6144
1396#define SF_W_MARK_LEGACY 4096
1397#define SF_W_MARK_SCAN 4096
1398
1399/* SF Scenarios timers for FULL_ON state (aligned to 32 uSec) */
1400#define SF_SINGLE_UNICAST_IDLE_TIMER 320 /* 300 uSec */
1401#define SF_SINGLE_UNICAST_AGING_TIMER 2016 /* 2 mSec */
1402#define SF_AGG_UNICAST_IDLE_TIMER 320 /* 300 uSec */
1403#define SF_AGG_UNICAST_AGING_TIMER 2016 /* 2 mSec */
1404#define SF_MCAST_IDLE_TIMER 2016 /* 2 mSec */
1405#define SF_MCAST_AGING_TIMER 10016 /* 10 mSec */
1406#define SF_BA_IDLE_TIMER 320 /* 300 uSec */
1407#define SF_BA_AGING_TIMER 2016 /* 2 mSec */
1408#define SF_TX_RE_IDLE_TIMER 320 /* 300 uSec */
1409#define SF_TX_RE_AGING_TIMER 2016 /* 2 mSec */
1410
1411#define SF_LONG_DELAY_AGING_TIMER 1000000 /* 1 Sec */
1412
1413/**
1414 * Smart Fifo configuration command.
1415 * @state: smart fifo state, types listed in iwl_sf_sate.
1416 * @watermark: Minimum allowed availabe free space in RXF for transient state.
1417 * @long_delay_timeouts: aging and idle timer values for each scenario
1418 * in long delay state.
1419 * @full_on_timeouts: timer values for each scenario in full on state.
1420 */
1421struct iwl_sf_cfg_cmd {
1422 enum iwl_sf_state state;
1423 __le32 watermark[SF_TRANSIENT_STATES_NUMBER];
1424 __le32 long_delay_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES];
1425 __le32 full_on_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES];
1426} __packed; /* SF_CFG_API_S_VER_2 */
1427
1366#endif /* __fw_api_h__ */ 1428#endif /* __fw_api_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c
index 70e5297646b2..b2bfa732a594 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/iwlwifi/mvm/fw.c
@@ -386,6 +386,10 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
386 goto error; 386 goto error;
387 } 387 }
388 388
389 ret = iwl_mvm_sf_update(mvm, NULL, false);
390 if (ret)
391 IWL_ERR(mvm, "Failed to initialize Smart Fifo\n");
392
389 ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw)); 393 ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw));
390 if (ret) 394 if (ret)
391 goto error; 395 goto error;
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index afc4419be46d..f0f8b2c7ebef 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -850,7 +850,16 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
850 iwl_mvm_protect_session(mvm, vif, dur, dur, 850 iwl_mvm_protect_session(mvm, vif, dur, dur,
851 5 * dur); 851 5 * dur);
852 } 852 }
853
854 iwl_mvm_sf_update(mvm, vif, false);
853 } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) { 855 } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
856 /*
857 * If update fails - SF might be running in associated
858 * mode while disassociated - which is forbidden.
859 */
860 WARN_ONCE(iwl_mvm_sf_update(mvm, vif, false),
861 "Failed to update SF upon disassociation\n");
862
854 /* remove AP station now that the MAC is unassoc */ 863 /* remove AP station now that the MAC is unassoc */
855 ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id); 864 ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id);
856 if (ret) 865 if (ret)
@@ -1200,6 +1209,17 @@ static int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1200 return 0; 1209 return 0;
1201} 1210}
1202 1211
1212static void iwl_mvm_sta_rc_update(struct ieee80211_hw *hw,
1213 struct ieee80211_vif *vif,
1214 struct ieee80211_sta *sta, u32 changed)
1215{
1216 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
1217
1218 if (vif->type == NL80211_IFTYPE_STATION &&
1219 changed & IEEE80211_RC_NSS_CHANGED)
1220 iwl_mvm_sf_update(mvm, vif, false);
1221}
1222
1203static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw, 1223static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw,
1204 struct ieee80211_vif *vif, u16 ac, 1224 struct ieee80211_vif *vif, u16 ac,
1205 const struct ieee80211_tx_queue_params *params) 1225 const struct ieee80211_tx_queue_params *params)
@@ -1765,6 +1785,7 @@ struct ieee80211_ops iwl_mvm_hw_ops = {
1765 .sta_notify = iwl_mvm_mac_sta_notify, 1785 .sta_notify = iwl_mvm_mac_sta_notify,
1766 .allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames, 1786 .allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames,
1767 .set_rts_threshold = iwl_mvm_mac_set_rts_threshold, 1787 .set_rts_threshold = iwl_mvm_mac_set_rts_threshold,
1788 .sta_rc_update = iwl_mvm_sta_rc_update,
1768 .conf_tx = iwl_mvm_mac_conf_tx, 1789 .conf_tx = iwl_mvm_mac_conf_tx,
1769 .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx, 1790 .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx,
1770 .sched_scan_start = iwl_mvm_mac_sched_scan_start, 1791 .sched_scan_start = iwl_mvm_mac_sched_scan_start,
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
index 7dc57cfe5803..bd072c7955be 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -489,6 +489,9 @@ struct iwl_mvm {
489 u8 scan_last_antenna_idx; /* to toggle TX between antennas */ 489 u8 scan_last_antenna_idx; /* to toggle TX between antennas */
490 u8 mgmt_last_antenna_idx; 490 u8 mgmt_last_antenna_idx;
491 491
492 /* last smart fifo state that was successfully sent to firmware */
493 enum iwl_sf_state sf_state;
494
492#ifdef CONFIG_IWLWIFI_DEBUGFS 495#ifdef CONFIG_IWLWIFI_DEBUGFS
493 struct dentry *debugfs_dir; 496 struct dentry *debugfs_dir;
494 u32 dbgfs_sram_offset, dbgfs_sram_len; 497 u32 dbgfs_sram_offset, dbgfs_sram_len;
@@ -869,4 +872,8 @@ void iwl_mvm_tt_initialize(struct iwl_mvm *mvm);
869void iwl_mvm_tt_exit(struct iwl_mvm *mvm); 872void iwl_mvm_tt_exit(struct iwl_mvm *mvm);
870void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state); 873void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state);
871 874
875/* smart fifo */
876int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
877 bool added_vif);
878
872#endif /* __IWL_MVM_H__ */ 879#endif /* __IWL_MVM_H__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c
index d86083c6f445..cae6123d25c3 100644
--- a/drivers/net/wireless/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/iwlwifi/mvm/ops.c
@@ -359,6 +359,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
359 mvm->aux_queue = 11; 359 mvm->aux_queue = 11;
360 mvm->first_agg_queue = 12; 360 mvm->first_agg_queue = 12;
361 } 361 }
362 mvm->sf_state = SF_UNINIT;
362 363
363 mutex_init(&mvm->mutex); 364 mutex_init(&mvm->mutex);
364 spin_lock_init(&mvm->async_handlers_lock); 365 spin_lock_init(&mvm->async_handlers_lock);
diff --git a/drivers/net/wireless/iwlwifi/mvm/sf.c b/drivers/net/wireless/iwlwifi/mvm/sf.c
new file mode 100644
index 000000000000..97bb3c3e75ce
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/sf.c
@@ -0,0 +1,291 @@
1/******************************************************************************
2 *
3 * This file is provided under a dual BSD/GPLv2 license. When using or
4 * redistributing this file, you may do so under either license.
5 *
6 * GPL LICENSE SUMMARY
7 *
8 * Copyright(c) 2013 Intel Corporation. All rights reserved.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of version 2 of the GNU General Public License as
12 * published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
22 * USA
23 *
24 * The full GNU General Public License is included in this distribution
25 * in the file called COPYING.
26 *
27 * Contact Information:
28 * Intel Linux Wireless <ilw@linux.intel.com>
29 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30 *
31 * BSD LICENSE
32 *
33 * Copyright(c) 2013 Intel Corporation. All rights reserved.
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 *
40 * * Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * * Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in
44 * the documentation and/or other materials provided with the
45 * distribution.
46 * * Neither the name Intel Corporation nor the names of its
47 * contributors may be used to endorse or promote products derived
48 * from this software without specific prior written permission.
49 *
50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
51 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
52 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
53 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
54 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
55 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
56 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
57 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
58 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
59 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
60 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61 *
62 *****************************************************************************/
63#include "mvm.h"
64
65/* For counting bound interfaces */
66struct iwl_mvm_active_iface_iterator_data {
67 struct ieee80211_vif *ignore_vif;
68 u8 sta_vif_ap_sta_id;
69 enum iwl_sf_state sta_vif_state;
70 int num_active_macs;
71};
72
73/*
74 * Count bound interfaces which are not p2p, besides data->ignore_vif.
75 * data->station_vif will point to one bound vif of type station, if exists.
76 */
77static void iwl_mvm_bound_iface_iterator(void *_data, u8 *mac,
78 struct ieee80211_vif *vif)
79{
80 struct iwl_mvm_active_iface_iterator_data *data = _data;
81 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
82
83 if (vif == data->ignore_vif || !mvmvif->phy_ctxt ||
84 vif->type == NL80211_IFTYPE_P2P_DEVICE)
85 return;
86
87 data->num_active_macs++;
88
89 if (vif->type == NL80211_IFTYPE_STATION) {
90 data->sta_vif_ap_sta_id = mvmvif->ap_sta_id;
91 if (vif->bss_conf.assoc)
92 data->sta_vif_state = SF_FULL_ON;
93 else
94 data->sta_vif_state = SF_INIT_OFF;
95 }
96}
97
98/*
99 * Aging and idle timeouts for the different possible scenarios
100 * in SF_FULL_ON state.
101 */
102static const __le32 sf_full_timeout[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = {
103 {
104 cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER),
105 cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER)
106 },
107 {
108 cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER),
109 cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER)
110 },
111 {
112 cpu_to_le32(SF_MCAST_AGING_TIMER),
113 cpu_to_le32(SF_MCAST_IDLE_TIMER)
114 },
115 {
116 cpu_to_le32(SF_BA_AGING_TIMER),
117 cpu_to_le32(SF_BA_IDLE_TIMER)
118 },
119 {
120 cpu_to_le32(SF_TX_RE_AGING_TIMER),
121 cpu_to_le32(SF_TX_RE_IDLE_TIMER)
122 },
123};
124
125static void iwl_mvm_fill_sf_command(struct iwl_sf_cfg_cmd *sf_cmd,
126 struct ieee80211_sta *sta)
127{
128 int i, j, watermark;
129
130 sf_cmd->watermark[SF_LONG_DELAY_ON] = cpu_to_le32(SF_W_MARK_SCAN);
131
132 /*
133 * If we are in association flow - check antenna configuration
134 * capabilities of the AP station, and choose the watermark accordingly.
135 */
136 if (sta) {
137 if (sta->ht_cap.ht_supported || sta->vht_cap.vht_supported) {
138 switch (sta->rx_nss) {
139 case 1:
140 watermark = SF_W_MARK_SISO;
141 break;
142 case 2:
143 watermark = SF_W_MARK_MIMO2;
144 break;
145 default:
146 watermark = SF_W_MARK_MIMO3;
147 break;
148 }
149 } else {
150 watermark = SF_W_MARK_LEGACY;
151 }
152 /* default watermark value for unassociated mode. */
153 } else {
154 watermark = SF_W_MARK_MIMO2;
155 }
156 sf_cmd->watermark[SF_FULL_ON] = cpu_to_le32(watermark);
157
158 for (i = 0; i < SF_NUM_SCENARIO; i++) {
159 for (j = 0; j < SF_NUM_TIMEOUT_TYPES; j++) {
160 sf_cmd->long_delay_timeouts[i][j] =
161 cpu_to_le32(SF_LONG_DELAY_AGING_TIMER);
162 }
163 }
164 BUILD_BUG_ON(sizeof(sf_full_timeout) !=
165 sizeof(__le32) * SF_NUM_SCENARIO * SF_NUM_TIMEOUT_TYPES);
166
167 memcpy(sf_cmd->full_on_timeouts, sf_full_timeout,
168 sizeof(sf_full_timeout));
169}
170
171static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id,
172 enum iwl_sf_state new_state)
173{
174 struct iwl_sf_cfg_cmd sf_cmd = {
175 .state = new_state,
176 };
177 struct ieee80211_sta *sta;
178 int ret = 0;
179
180 /*
181 * If an associated AP sta changed its antenna configuration, the state
182 * will remain FULL_ON but SF parameters need to be reconsidered.
183 */
184 if (new_state != SF_FULL_ON && mvm->sf_state == new_state)
185 return 0;
186
187 switch (new_state) {
188 case SF_UNINIT:
189 break;
190 case SF_FULL_ON:
191 if (sta_id == IWL_MVM_STATION_COUNT) {
192 IWL_ERR(mvm,
193 "No station: Cannot switch SF to FULL_ON\n");
194 return -EINVAL;
195 }
196 rcu_read_lock();
197 sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
198 if (IS_ERR_OR_NULL(sta)) {
199 IWL_ERR(mvm, "Invalid station id\n");
200 rcu_read_unlock();
201 return -EINVAL;
202 }
203 iwl_mvm_fill_sf_command(&sf_cmd, sta);
204 rcu_read_unlock();
205 break;
206 case SF_INIT_OFF:
207 iwl_mvm_fill_sf_command(&sf_cmd, NULL);
208 break;
209 default:
210 WARN_ONCE(1, "Invalid state: %d. not sending Smart Fifo cmd\n",
211 new_state);
212 return -EINVAL;
213 }
214
215 ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_SF_CFG_CMD, CMD_ASYNC,
216 sizeof(sf_cmd), &sf_cmd);
217 if (!ret)
218 mvm->sf_state = new_state;
219
220 return ret;
221}
222
223/*
224 * Update Smart fifo:
225 * Count bound interfaces that are not to be removed, ignoring p2p devices,
226 * and set new state accordingly.
227 */
228int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif,
229 bool remove_vif)
230{
231 enum iwl_sf_state new_state;
232 u8 sta_id = IWL_MVM_STATION_COUNT;
233 struct iwl_mvm_vif *mvmvif = NULL;
234 struct iwl_mvm_active_iface_iterator_data data = {
235 .ignore_vif = changed_vif,
236 .sta_vif_state = SF_UNINIT,
237 .sta_vif_ap_sta_id = IWL_MVM_STATION_COUNT,
238 };
239
240 if (IWL_UCODE_API(mvm->fw->ucode_ver) < 8)
241 return 0;
242
243 /*
244 * Ignore the call if we are in HW Restart flow, or if the handled
245 * vif is a p2p device.
246 */
247 if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) ||
248 (changed_vif && changed_vif->type == NL80211_IFTYPE_P2P_DEVICE))
249 return 0;
250
251 ieee80211_iterate_active_interfaces_atomic(mvm->hw,
252 IEEE80211_IFACE_ITER_NORMAL,
253 iwl_mvm_bound_iface_iterator,
254 &data);
255
256 /* If changed_vif exists and is not to be removed, add to the count */
257 if (changed_vif && !remove_vif)
258 data.num_active_macs++;
259
260 switch (data.num_active_macs) {
261 case 0:
262 /* If there are no active macs - change state to SF_INIT_OFF */
263 new_state = SF_INIT_OFF;
264 break;
265 case 1:
266 if (remove_vif) {
267 /* The one active mac left is of type station
268 * and we filled the relevant data during iteration
269 */
270 new_state = data.sta_vif_state;
271 sta_id = data.sta_vif_ap_sta_id;
272 } else {
273 if (WARN_ON(!changed_vif))
274 return -EINVAL;
275 if (changed_vif->type != NL80211_IFTYPE_STATION) {
276 new_state = SF_UNINIT;
277 } else if (changed_vif->bss_conf.assoc) {
278 mvmvif = iwl_mvm_vif_from_mac80211(changed_vif);
279 sta_id = mvmvif->ap_sta_id;
280 new_state = SF_FULL_ON;
281 } else {
282 new_state = SF_INIT_OFF;
283 }
284 }
285 break;
286 default:
287 /* If there are multiple active macs - change to SF_UNINIT */
288 new_state = SF_UNINIT;
289 }
290 return iwl_mvm_sf_config(mvm, sta_id, new_state);
291}