diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/binding.c | 16 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/fw-api.h | 62 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/fw.c | 4 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/mac80211.c | 21 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/mvm.h | 7 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/ops.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/sf.c | 291 |
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 @@ | |||
1 | obj-$(CONFIG_IWLMVM) += iwlmvm.o | 1 | obj-$(CONFIG_IWLMVM) += iwlmvm.o |
2 | iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o | 2 | iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o |
3 | iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o | 3 | iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o sf.o |
4 | iwlmvm-y += scan.o time-event.o rs.o | 4 | iwlmvm-y += scan.o time-event.o rs.o |
5 | iwlmvm-y += power.o power_legacy.o bt-coex.o | 5 | iwlmvm-y += power.o power_legacy.o bt-coex.o |
6 | iwlmvm-y += led.o tt.o | 6 | iwlmvm-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 | ||
189 | int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif) | 196 | int 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 */ | ||
1371 | enum 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 */ | ||
1380 | enum 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 | */ | ||
1421 | struct 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 | ||
1212 | static 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 | |||
1203 | static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw, | 1223 | static 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); | |||
869 | void iwl_mvm_tt_exit(struct iwl_mvm *mvm); | 872 | void iwl_mvm_tt_exit(struct iwl_mvm *mvm); |
870 | void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state); | 873 | void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state); |
871 | 874 | ||
875 | /* smart fifo */ | ||
876 | int 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 */ | ||
66 | struct 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 | */ | ||
77 | static 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 | */ | ||
102 | static 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 | |||
125 | static 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 | |||
171 | static 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 | */ | ||
228 | int 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 | } | ||