summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi/mvm
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm')
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/Makefile2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/binding.c2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/bt-coex.c589
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/d3.c267
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/debugfs.c257
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h319
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h53
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-power.h66
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h8
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api.h110
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw.c37
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/led.c2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c99
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c136
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mvm.h60
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/nvm.c145
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/ops.c49
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c14
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/power.c117
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/quota.c5
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/rs.c50
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/rx.c2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/scan.c12
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sta.c112
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sta.h8
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/time-event.c50
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/time-event.h5
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/tx.c72
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/utils.c9
33 files changed, 2163 insertions, 502 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile
index 807b250ec39..2acc44b4098 100644
--- a/drivers/net/wireless/iwlwifi/mvm/Makefile
+++ b/drivers/net/wireless/iwlwifi/mvm/Makefile
@@ -2,7 +2,7 @@ obj-$(CONFIG_IWLMVM) += iwlmvm.o
iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o
iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o
iwlmvm-y += scan.o time-event.o rs.o
-iwlmvm-y += power.o
+iwlmvm-y += power.o bt-coex.o
iwlmvm-y += led.o
iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o
iwlmvm-$(CONFIG_PM_SLEEP) += d3.o
diff --git a/drivers/net/wireless/iwlwifi/mvm/binding.c b/drivers/net/wireless/iwlwifi/mvm/binding.c
index 73d24aacb90..93fd1457954 100644
--- a/drivers/net/wireless/iwlwifi/mvm/binding.c
+++ b/drivers/net/wireless/iwlwifi/mvm/binding.c
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
diff --git a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c
new file mode 100644
index 00000000000..810bfa5f6de
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c
@@ -0,0 +1,589 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <net/mac80211.h>
+
+#include "fw-api-bt-coex.h"
+#include "iwl-modparams.h"
+#include "mvm.h"
+#include "iwl-debug.h"
+
+#define EVENT_PRIO_ANT(_evt, _prio, _shrd_ant) \
+ [(_evt)] = (((_prio) << BT_COEX_PRIO_TBL_PRIO_POS) | \
+ ((_shrd_ant) << BT_COEX_PRIO_TBL_SHRD_ANT_POS))
+
+static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB1,
+ BT_COEX_PRIO_TBL_PRIO_BYPASS, 0),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB2,
+ BT_COEX_PRIO_TBL_PRIO_BYPASS, 1),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1,
+ BT_COEX_PRIO_TBL_PRIO_LOW, 0),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2,
+ BT_COEX_PRIO_TBL_PRIO_LOW, 1),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1,
+ BT_COEX_PRIO_TBL_PRIO_HIGH, 0),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2,
+ BT_COEX_PRIO_TBL_PRIO_HIGH, 1),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_DTIM,
+ BT_COEX_PRIO_TBL_DISABLED, 0),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN52,
+ BT_COEX_PRIO_TBL_PRIO_COEX_OFF, 0),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN24,
+ BT_COEX_PRIO_TBL_PRIO_COEX_ON, 0),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_IDLE,
+ BT_COEX_PRIO_TBL_PRIO_COEX_IDLE, 0),
+ 0, 0, 0, 0, 0, 0,
+};
+
+#undef EVENT_PRIO_ANT
+
+/* BT Antenna Coupling Threshold (dB) */
+#define IWL_BT_ANTENNA_COUPLING_THRESHOLD (35)
+#define IWL_BT_LOAD_FORCE_SISO_THRESHOLD (3)
+
+#define BT_ENABLE_REDUCED_TXPOWER_THRESHOLD (-62)
+#define BT_DISABLE_REDUCED_TXPOWER_THRESHOLD (-65)
+#define BT_REDUCED_TX_POWER_BIT BIT(7)
+
+static inline bool is_loose_coex(void)
+{
+ return iwlwifi_mod_params.ant_coupling >
+ IWL_BT_ANTENNA_COUPLING_THRESHOLD;
+}
+
+int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm)
+{
+ return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, CMD_SYNC,
+ sizeof(struct iwl_bt_coex_prio_tbl_cmd),
+ &iwl_bt_prio_tbl);
+}
+
+static int iwl_send_bt_env(struct iwl_mvm *mvm, u8 action, u8 type)
+{
+ struct iwl_bt_coex_prot_env_cmd env_cmd;
+ int ret;
+
+ env_cmd.action = action;
+ env_cmd.type = type;
+ ret = iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PROT_ENV, CMD_SYNC,
+ sizeof(env_cmd), &env_cmd);
+ if (ret)
+ IWL_ERR(mvm, "failed to send BT env command\n");
+ return ret;
+}
+
+enum iwl_bt_kill_msk {
+ BT_KILL_MSK_DEFAULT,
+ BT_KILL_MSK_SCO_HID_A2DP,
+ BT_KILL_MSK_REDUCED_TXPOW,
+ BT_KILL_MSK_MAX,
+};
+
+static const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX] = {
+ [BT_KILL_MSK_DEFAULT] = 0xffff0000,
+ [BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff,
+ [BT_KILL_MSK_REDUCED_TXPOW] = 0,
+};
+
+static const u32 iwl_bt_cts_kill_msk[BT_KILL_MSK_MAX] = {
+ [BT_KILL_MSK_DEFAULT] = 0xffff0000,
+ [BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff,
+ [BT_KILL_MSK_REDUCED_TXPOW] = 0,
+};
+
+#define IWL_BT_DEFAULT_BOOST (0xf0f0f0f0)
+
+/* Tight Coex */
+static const __le32 iwl_tight_lookup[BT_COEX_LUT_SIZE] = {
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaeaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xcc00ff28),
+ cpu_to_le32(0x0000aaaa),
+ cpu_to_le32(0xcc00aaaa),
+ cpu_to_le32(0x0000aaaa),
+ cpu_to_le32(0xc0004000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0xf0005000),
+ cpu_to_le32(0xf0005000),
+};
+
+/* Loose Coex */
+static const __le32 iwl_loose_lookup[BT_COEX_LUT_SIZE] = {
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaeaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xcc00ff28),
+ cpu_to_le32(0x0000aaaa),
+ cpu_to_le32(0xcc00aaaa),
+ cpu_to_le32(0x0000aaaa),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0xf0005000),
+ cpu_to_le32(0xf0005000),
+};
+
+/* Full concurrency */
+static const __le32 iwl_concurrent_lookup[BT_COEX_LUT_SIZE] = {
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000),
+};
+
+int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
+{
+ struct iwl_bt_coex_cmd cmd = {
+ .max_kill = 5,
+ .bt3_time_t7_value = 1,
+ .bt3_prio_sample_time = 2,
+ .bt3_timer_t2_value = 0xc,
+ };
+ int ret;
+
+ cmd.flags = iwlwifi_mod_params.bt_coex_active ?
+ BT_COEX_NW : BT_COEX_DISABLE;
+ cmd.flags |= BT_CH_PRIMARY_EN | BT_SYNC_2_BT_DISABLE;
+
+ cmd.valid_bit_msk = cpu_to_le16(BT_VALID_ENABLE |
+ BT_VALID_BT_PRIO_BOOST |
+ BT_VALID_MAX_KILL |
+ BT_VALID_3W_TMRS |
+ BT_VALID_KILL_ACK |
+ BT_VALID_KILL_CTS |
+ BT_VALID_REDUCED_TX_POWER |
+ BT_VALID_LUT);
+
+ if (is_loose_coex())
+ memcpy(&cmd.decision_lut, iwl_loose_lookup,
+ sizeof(iwl_tight_lookup));
+ else
+ memcpy(&cmd.decision_lut, iwl_tight_lookup,
+ sizeof(iwl_tight_lookup));
+
+ cmd.bt_prio_boost = cpu_to_le32(IWL_BT_DEFAULT_BOOST);
+ cmd.kill_ack_msk =
+ cpu_to_le32(iwl_bt_ack_kill_msk[BT_KILL_MSK_DEFAULT]);
+ cmd.kill_cts_msk =
+ cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]);
+
+ memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
+
+ /* go to CALIB state in internal BT-Coex state machine */
+ ret = iwl_send_bt_env(mvm, BT_COEX_ENV_OPEN,
+ BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
+ if (ret)
+ return ret;
+
+ ret = iwl_send_bt_env(mvm, BT_COEX_ENV_CLOSE,
+ BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
+ if (ret)
+ return ret;
+
+ return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_SYNC,
+ sizeof(cmd), &cmd);
+}
+
+static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
+ bool reduced_tx_power)
+{
+ enum iwl_bt_kill_msk bt_kill_msk;
+ struct iwl_bt_coex_cmd cmd = {};
+ struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (reduced_tx_power) {
+ /* Reduced Tx power has precedence on the type of the profile */
+ bt_kill_msk = BT_KILL_MSK_REDUCED_TXPOW;
+ } else {
+ /* Low latency BT profile is active: give higher prio to BT */
+ if (BT_MBOX_MSG(notif, 3, SCO_STATE) ||
+ BT_MBOX_MSG(notif, 3, A2DP_STATE) ||
+ BT_MBOX_MSG(notif, 3, SNIFF_STATE))
+ bt_kill_msk = BT_KILL_MSK_SCO_HID_A2DP;
+ else
+ bt_kill_msk = BT_KILL_MSK_DEFAULT;
+ }
+
+ IWL_DEBUG_COEX(mvm,
+ "Update kill_msk: %d - SCO %sactive A2DP %sactive SNIFF %sactive\n",
+ bt_kill_msk,
+ BT_MBOX_MSG(notif, 3, SCO_STATE) ? "" : "in",
+ BT_MBOX_MSG(notif, 3, A2DP_STATE) ? "" : "in",
+ BT_MBOX_MSG(notif, 3, SNIFF_STATE) ? "" : "in");
+
+ /* Don't send HCMD if there is no update */
+ if (bt_kill_msk == mvm->bt_kill_msk)
+ return 0;
+
+ mvm->bt_kill_msk = bt_kill_msk;
+ cmd.kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]);
+ cmd.kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]);
+ cmd.valid_bit_msk = cpu_to_le16(BT_VALID_KILL_ACK | BT_VALID_KILL_CTS);
+
+ IWL_DEBUG_COEX(mvm, "bt_kill_msk = %d\n", bt_kill_msk);
+ return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_SYNC,
+ sizeof(cmd), &cmd);
+}
+
+static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
+ bool enable)
+{
+ struct iwl_bt_coex_cmd cmd = {
+ .valid_bit_msk = cpu_to_le16(BT_VALID_REDUCED_TX_POWER),
+ .bt_reduced_tx_power = sta_id,
+ };
+ struct ieee80211_sta *sta;
+ struct iwl_mvm_sta *mvmsta;
+
+ /* This can happen if the station has been removed right now */
+ if (sta_id == IWL_MVM_STATION_COUNT)
+ return 0;
+
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+ lockdep_is_held(&mvm->mutex));
+ mvmsta = (void *)sta->drv_priv;
+
+ /* nothing to do */
+ if (mvmsta->bt_reduced_txpower == enable)
+ return 0;
+
+ if (enable)
+ cmd.bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT;
+
+ IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n",
+ enable ? "en" : "dis", sta_id);
+
+ mvmsta->bt_reduced_txpower = enable;
+
+ /* Send ASYNC since this can be sent from an atomic context */
+ return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_ASYNC,
+ sizeof(cmd), &cmd);
+}
+
+struct iwl_bt_iterator_data {
+ struct iwl_bt_coex_profile_notif *notif;
+ struct iwl_mvm *mvm;
+ u32 num_bss_ifaces;
+ bool reduced_tx_power;
+};
+
+static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_bt_iterator_data *data = _data;
+ struct iwl_mvm *mvm = data->mvm;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ enum ieee80211_smps_mode smps_mode;
+ enum ieee80211_band band;
+ int ave_rssi;
+
+ if (vif->type != NL80211_IFTYPE_STATION)
+ return;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(vif->chanctx_conf);
+ if (chanctx_conf && chanctx_conf->def.chan)
+ band = chanctx_conf->def.chan->band;
+ else
+ band = -1;
+ rcu_read_unlock();
+
+ smps_mode = IEEE80211_SMPS_AUTOMATIC;
+
+ if (band != IEEE80211_BAND_2GHZ) {
+ ieee80211_request_smps(vif, smps_mode);
+ return;
+ }
+
+ if (data->notif->bt_status)
+ smps_mode = IEEE80211_SMPS_DYNAMIC;
+
+ if (data->notif->bt_traffic_load >= IWL_BT_LOAD_FORCE_SISO_THRESHOLD)
+ smps_mode = IEEE80211_SMPS_STATIC;
+
+ IWL_DEBUG_COEX(data->mvm,
+ "mac %d: bt_status %d traffic_load %d smps_req %d\n",
+ mvmvif->id, data->notif->bt_status,
+ data->notif->bt_traffic_load, smps_mode);
+
+ ieee80211_request_smps(vif, smps_mode);
+
+ /* don't reduce the Tx power if in loose scheme */
+ if (is_loose_coex())
+ return;
+
+ data->num_bss_ifaces++;
+
+ /* reduced Txpower only if there are open BT connections, so ...*/
+ if (!BT_MBOX_MSG(data->notif, 3, OPEN_CON_2)) {
+ /* ... cancel reduced Tx power ... */
+ if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false))
+ IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
+ data->reduced_tx_power = false;
+
+ /* ... and there is no need to get reports on RSSI any more. */
+ ieee80211_disable_rssi_reports(vif);
+ return;
+ }
+
+ ave_rssi = ieee80211_ave_rssi(vif);
+
+ /* if the RSSI isn't valid, fake it is very low */
+ if (!ave_rssi)
+ ave_rssi = -100;
+ if (ave_rssi > BT_ENABLE_REDUCED_TXPOWER_THRESHOLD) {
+ if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true))
+ IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
+
+ /*
+ * bt_kill_msk can be BT_KILL_MSK_REDUCED_TXPOW only if all the
+ * BSS / P2P clients have rssi above threshold.
+ * We set the bt_kill_msk to BT_KILL_MSK_REDUCED_TXPOW before
+ * the iteration, if one interface's rssi isn't good enough,
+ * bt_kill_msk will be set to default values.
+ */
+ } else if (ave_rssi < BT_DISABLE_REDUCED_TXPOWER_THRESHOLD) {
+ if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false))
+ IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
+
+ /*
+ * One interface hasn't rssi above threshold, bt_kill_msk must
+ * be set to default values.
+ */
+ data->reduced_tx_power = false;
+ }
+
+ /* Begin to monitor the RSSI: it may influence the reduced Tx power */
+ ieee80211_enable_rssi_reports(vif, BT_DISABLE_REDUCED_TXPOWER_THRESHOLD,
+ BT_ENABLE_REDUCED_TXPOWER_THRESHOLD);
+}
+
+static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
+{
+ struct iwl_bt_iterator_data data = {
+ .mvm = mvm,
+ .notif = &mvm->last_bt_notif,
+ .reduced_tx_power = true,
+ };
+
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_bt_notif_iterator, &data);
+
+ /*
+ * If there are no BSS / P2P client interfaces, reduced Tx Power is
+ * irrelevant since it is based on the RSSI coming from the beacon.
+ * Use BT_KILL_MSK_DEFAULT in that case.
+ */
+ data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces;
+
+ if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power))
+ IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
+}
+
+/* upon association, the fw will send in BT Coex notification */
+int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *dev_cmd)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data;
+
+
+ IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n");
+ IWL_DEBUG_COEX(mvm, "\tBT %salive\n", notif->bt_status ? "" : "not ");
+ IWL_DEBUG_COEX(mvm, "\tBT open conn %d\n", notif->bt_open_conn);
+ IWL_DEBUG_COEX(mvm, "\tBT traffic load %d\n", notif->bt_traffic_load);
+ IWL_DEBUG_COEX(mvm, "\tBT agg traffic load %d\n",
+ notif->bt_agg_traffic_load);
+ IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance);
+
+ /* remember this notification for future use: rssi fluctuations */
+ memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif));
+
+ iwl_mvm_bt_coex_notif_handle(mvm);
+
+ /*
+ * This is an async handler for a notification, returning anything other
+ * than 0 doesn't make sense even if HCMD failed.
+ */
+ return 0;
+}
+
+static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
+ struct iwl_bt_iterator_data *data = _data;
+ struct iwl_mvm *mvm = data->mvm;
+
+ struct ieee80211_sta *sta;
+ struct iwl_mvm_sta *mvmsta;
+
+ if (vif->type != NL80211_IFTYPE_STATION ||
+ mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)
+ return;
+
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id],
+ lockdep_is_held(&mvm->mutex));
+ mvmsta = (void *)sta->drv_priv;
+
+ /*
+ * This interface doesn't support reduced Tx power (because of low
+ * RSSI probably), then set bt_kill_msk to default values.
+ */
+ if (!mvmsta->bt_reduced_txpower)
+ data->reduced_tx_power = false;
+ /* else - possibly leave it to BT_KILL_MSK_REDUCED_TXPOW */
+}
+
+void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ enum ieee80211_rssi_event rssi_event)
+{
+ struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
+ struct iwl_bt_iterator_data data = {
+ .mvm = mvm,
+ .reduced_tx_power = true,
+ };
+ int ret;
+
+ mutex_lock(&mvm->mutex);
+
+ /* Rssi update while not associated ?! */
+ if (WARN_ON_ONCE(mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT))
+ goto out_unlock;
+
+ /* No open connection - reports should be disabled */
+ if (!BT_MBOX_MSG(&mvm->last_bt_notif, 3, OPEN_CON_2))
+ goto out_unlock;
+
+ IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid,
+ rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW");
+
+ /*
+ * Check if rssi is good enough for reduced Tx power, but not in loose
+ * scheme.
+ */
+ if (rssi_event == RSSI_EVENT_LOW || is_loose_coex())
+ ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
+ false);
+ else
+ ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true);
+
+ if (ret)
+ IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n");
+
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_bt_rssi_iterator, &data);
+
+ /*
+ * If there are no BSS / P2P client interfaces, reduced Tx Power is
+ * irrelevant since it is based on the RSSI coming from the beacon.
+ * Use BT_KILL_MSK_DEFAULT in that case.
+ */
+ data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces;
+
+ if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power))
+ IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
+
+ out_unlock:
+ mutex_unlock(&mvm->mutex);
+}
+
+void iwl_mvm_bt_coex_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ enum ieee80211_band band;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(vif->chanctx_conf);
+ if (chanctx_conf && chanctx_conf->def.chan)
+ band = chanctx_conf->def.chan->band;
+ else
+ band = -1;
+ rcu_read_unlock();
+
+ /* if we are in 2GHz we will get a notification from the fw */
+ if (band == IEEE80211_BAND_2GHZ)
+ return;
+
+ /* else, we can remove all the constraints */
+ memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
+
+ iwl_mvm_bt_coex_notif_handle(mvm);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c
index 994c8c263dc..16bbdcc8627 100644
--- a/drivers/net/wireless/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/iwlwifi/mvm/d3.c
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
@@ -62,8 +62,10 @@
*****************************************************************************/
#include <linux/etherdevice.h>
+#include <linux/ip.h>
#include <net/cfg80211.h>
#include <net/ipv6.h>
+#include <net/tcp.h>
#include "iwl-modparams.h"
#include "fw-api.h"
#include "mvm.h"
@@ -402,6 +404,233 @@ static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
sizeof(cmd), &cmd);
}
+enum iwl_mvm_tcp_packet_type {
+ MVM_TCP_TX_SYN,
+ MVM_TCP_RX_SYNACK,
+ MVM_TCP_TX_DATA,
+ MVM_TCP_RX_ACK,
+ MVM_TCP_RX_WAKE,
+ MVM_TCP_TX_FIN,
+};
+
+static __le16 pseudo_hdr_check(int len, __be32 saddr, __be32 daddr)
+{
+ __sum16 check = tcp_v4_check(len, saddr, daddr, 0);
+ return cpu_to_le16(be16_to_cpu((__force __be16)check));
+}
+
+static void iwl_mvm_build_tcp_packet(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct cfg80211_wowlan_tcp *tcp,
+ void *_pkt, u8 *mask,
+ __le16 *pseudo_hdr_csum,
+ enum iwl_mvm_tcp_packet_type ptype)
+{
+ struct {
+ struct ethhdr eth;
+ struct iphdr ip;
+ struct tcphdr tcp;
+ u8 data[];
+ } __packed *pkt = _pkt;
+ u16 ip_tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr);
+ int i;
+
+ pkt->eth.h_proto = cpu_to_be16(ETH_P_IP),
+ pkt->ip.version = 4;
+ pkt->ip.ihl = 5;
+ pkt->ip.protocol = IPPROTO_TCP;
+
+ switch (ptype) {
+ case MVM_TCP_TX_SYN:
+ case MVM_TCP_TX_DATA:
+ case MVM_TCP_TX_FIN:
+ memcpy(pkt->eth.h_dest, tcp->dst_mac, ETH_ALEN);
+ memcpy(pkt->eth.h_source, vif->addr, ETH_ALEN);
+ pkt->ip.ttl = 128;
+ pkt->ip.saddr = tcp->src;
+ pkt->ip.daddr = tcp->dst;
+ pkt->tcp.source = cpu_to_be16(tcp->src_port);
+ pkt->tcp.dest = cpu_to_be16(tcp->dst_port);
+ /* overwritten for TX SYN later */
+ pkt->tcp.doff = sizeof(struct tcphdr) / 4;
+ pkt->tcp.window = cpu_to_be16(65000);
+ break;
+ case MVM_TCP_RX_SYNACK:
+ case MVM_TCP_RX_ACK:
+ case MVM_TCP_RX_WAKE:
+ memcpy(pkt->eth.h_dest, vif->addr, ETH_ALEN);
+ memcpy(pkt->eth.h_source, tcp->dst_mac, ETH_ALEN);
+ pkt->ip.saddr = tcp->dst;
+ pkt->ip.daddr = tcp->src;
+ pkt->tcp.source = cpu_to_be16(tcp->dst_port);
+ pkt->tcp.dest = cpu_to_be16(tcp->src_port);
+ break;
+ default:
+ WARN_ON(1);
+ return;
+ }
+
+ switch (ptype) {
+ case MVM_TCP_TX_SYN:
+ /* firmware assumes 8 option bytes - 8 NOPs for now */
+ memset(pkt->data, 0x01, 8);
+ ip_tot_len += 8;
+ pkt->tcp.doff = (sizeof(struct tcphdr) + 8) / 4;
+ pkt->tcp.syn = 1;
+ break;
+ case MVM_TCP_TX_DATA:
+ ip_tot_len += tcp->payload_len;
+ memcpy(pkt->data, tcp->payload, tcp->payload_len);
+ pkt->tcp.psh = 1;
+ pkt->tcp.ack = 1;
+ break;
+ case MVM_TCP_TX_FIN:
+ pkt->tcp.fin = 1;
+ pkt->tcp.ack = 1;
+ break;
+ case MVM_TCP_RX_SYNACK:
+ pkt->tcp.syn = 1;
+ pkt->tcp.ack = 1;
+ break;
+ case MVM_TCP_RX_ACK:
+ pkt->tcp.ack = 1;
+ break;
+ case MVM_TCP_RX_WAKE:
+ ip_tot_len += tcp->wake_len;
+ pkt->tcp.psh = 1;
+ pkt->tcp.ack = 1;
+ memcpy(pkt->data, tcp->wake_data, tcp->wake_len);
+ break;
+ }
+
+ switch (ptype) {
+ case MVM_TCP_TX_SYN:
+ case MVM_TCP_TX_DATA:
+ case MVM_TCP_TX_FIN:
+ pkt->ip.tot_len = cpu_to_be16(ip_tot_len);
+ pkt->ip.check = ip_fast_csum(&pkt->ip, pkt->ip.ihl);
+ break;
+ case MVM_TCP_RX_WAKE:
+ for (i = 0; i < DIV_ROUND_UP(tcp->wake_len, 8); i++) {
+ u8 tmp = tcp->wake_mask[i];
+ mask[i + 6] |= tmp << 6;
+ if (i + 1 < DIV_ROUND_UP(tcp->wake_len, 8))
+ mask[i + 7] = tmp >> 2;
+ }
+ /* fall through for ethernet/IP/TCP headers mask */
+ case MVM_TCP_RX_SYNACK:
+ case MVM_TCP_RX_ACK:
+ mask[0] = 0xff; /* match ethernet */
+ /*
+ * match ethernet, ip.version, ip.ihl
+ * the ip.ihl half byte is really masked out by firmware
+ */
+ mask[1] = 0x7f;
+ mask[2] = 0x80; /* match ip.protocol */
+ mask[3] = 0xfc; /* match ip.saddr, ip.daddr */
+ mask[4] = 0x3f; /* match ip.daddr, tcp.source, tcp.dest */
+ mask[5] = 0x80; /* match tcp flags */
+ /* leave rest (0 or set for MVM_TCP_RX_WAKE) */
+ break;
+ };
+
+ *pseudo_hdr_csum = pseudo_hdr_check(ip_tot_len - sizeof(struct iphdr),
+ pkt->ip.saddr, pkt->ip.daddr);
+}
+
+static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct cfg80211_wowlan_tcp *tcp)
+{
+ struct iwl_wowlan_remote_wake_config *cfg;
+ struct iwl_host_cmd cmd = {
+ .id = REMOTE_WAKE_CONFIG_CMD,
+ .len = { sizeof(*cfg), },
+ .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+ .flags = CMD_SYNC,
+ };
+ int ret;
+
+ if (!tcp)
+ return 0;
+
+ cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+ if (!cfg)
+ return -ENOMEM;
+ cmd.data[0] = cfg;
+
+ cfg->max_syn_retries = 10;
+ cfg->max_data_retries = 10;
+ cfg->tcp_syn_ack_timeout = 1; /* seconds */
+ cfg->tcp_ack_timeout = 1; /* seconds */
+
+ /* SYN (TX) */
+ iwl_mvm_build_tcp_packet(
+ mvm, vif, tcp, cfg->syn_tx.data, NULL,
+ &cfg->syn_tx.info.tcp_pseudo_header_checksum,
+ MVM_TCP_TX_SYN);
+ cfg->syn_tx.info.tcp_payload_length = 0;
+
+ /* SYN/ACK (RX) */
+ iwl_mvm_build_tcp_packet(
+ mvm, vif, tcp, cfg->synack_rx.data, cfg->synack_rx.rx_mask,
+ &cfg->synack_rx.info.tcp_pseudo_header_checksum,
+ MVM_TCP_RX_SYNACK);
+ cfg->synack_rx.info.tcp_payload_length = 0;
+
+ /* KEEPALIVE/ACK (TX) */
+ iwl_mvm_build_tcp_packet(
+ mvm, vif, tcp, cfg->keepalive_tx.data, NULL,
+ &cfg->keepalive_tx.info.tcp_pseudo_header_checksum,
+ MVM_TCP_TX_DATA);
+ cfg->keepalive_tx.info.tcp_payload_length =
+ cpu_to_le16(tcp->payload_len);
+ cfg->sequence_number_offset = tcp->payload_seq.offset;
+ /* length must be 0..4, the field is little endian */
+ cfg->sequence_number_length = tcp->payload_seq.len;
+ cfg->initial_sequence_number = cpu_to_le32(tcp->payload_seq.start);
+ cfg->keepalive_interval = cpu_to_le16(tcp->data_interval);
+ if (tcp->payload_tok.len) {
+ cfg->token_offset = tcp->payload_tok.offset;
+ cfg->token_length = tcp->payload_tok.len;
+ cfg->num_tokens =
+ cpu_to_le16(tcp->tokens_size % tcp->payload_tok.len);
+ memcpy(cfg->tokens, tcp->payload_tok.token_stream,
+ tcp->tokens_size);
+ } else {
+ /* set tokens to max value to almost never run out */
+ cfg->num_tokens = cpu_to_le16(65535);
+ }
+
+ /* ACK (RX) */
+ iwl_mvm_build_tcp_packet(
+ mvm, vif, tcp, cfg->keepalive_ack_rx.data,
+ cfg->keepalive_ack_rx.rx_mask,
+ &cfg->keepalive_ack_rx.info.tcp_pseudo_header_checksum,
+ MVM_TCP_RX_ACK);
+ cfg->keepalive_ack_rx.info.tcp_payload_length = 0;
+
+ /* WAKEUP (RX) */
+ iwl_mvm_build_tcp_packet(
+ mvm, vif, tcp, cfg->wake_rx.data, cfg->wake_rx.rx_mask,
+ &cfg->wake_rx.info.tcp_pseudo_header_checksum,
+ MVM_TCP_RX_WAKE);
+ cfg->wake_rx.info.tcp_payload_length =
+ cpu_to_le16(tcp->wake_len);
+
+ /* FIN */
+ iwl_mvm_build_tcp_packet(
+ mvm, vif, tcp, cfg->fin_tx.data, NULL,
+ &cfg->fin_tx.info.tcp_pseudo_header_checksum,
+ MVM_TCP_TX_FIN);
+ cfg->fin_tx.info.tcp_payload_length = 0;
+
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+ kfree(cfg);
+
+ return ret;
+}
+
struct iwl_d3_iter_data {
struct iwl_mvm *mvm;
struct ieee80211_vif *vif;
@@ -540,7 +769,14 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
struct iwl_wowlan_config_cmd wowlan_config_cmd = {};
struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {};
struct iwl_wowlan_tkip_params_cmd tkip_cmd = {};
- struct iwl_d3_manager_config d3_cfg_cmd = {};
+ struct iwl_d3_manager_config d3_cfg_cmd = {
+ /*
+ * Program the minimum sleep time to 10 seconds, as many
+ * platforms have issues processing a wakeup signal while
+ * still being in the process of suspending.
+ */
+ .min_sleep_time = cpu_to_le32(10 * 1000 * 1000),
+ };
struct wowlan_key_data key_data = {
.use_rsc_tsc = false,
.tkip = &tkip_cmd,
@@ -637,9 +873,21 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
cpu_to_le32(IWL_WOWLAN_WAKEUP_PATTERN_MATCH);
if (wowlan->rfkill_release)
- d3_cfg_cmd.wakeup_flags |=
+ wowlan_config_cmd.wakeup_filter |=
cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT);
+ if (wowlan->tcp) {
+ /*
+ * Set the "link change" (really "link lost") flag as well
+ * since that implies losing the TCP connection.
+ */
+ wowlan_config_cmd.wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS |
+ IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE |
+ IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET |
+ IWL_WOWLAN_WAKEUP_LINK_CHANGE);
+ }
+
iwl_mvm_cancel_scan(mvm);
iwl_trans_stop_device(mvm->trans);
@@ -755,6 +1003,10 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
if (ret)
goto out;
+ ret = iwl_mvm_send_remote_wake_cfg(mvm, vif, wowlan->tcp);
+ if (ret)
+ goto out;
+
/* must be last -- this switches firmware state */
ret = iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, CMD_SYNC,
sizeof(d3_cfg_cmd), &d3_cfg_cmd);
@@ -874,6 +1126,15 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
if (reasons & IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE)
wakeup.four_way_handshake = true;
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_LINK_LOSS)
+ wakeup.tcp_connlost = true;
+
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_SIGNATURE_TABLE)
+ wakeup.tcp_nomoretokens = true;
+
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET)
+ wakeup.tcp_match = true;
+
if (status->wake_packet_bufsize) {
int pktsize = le32_to_cpu(status->wake_packet_bufsize);
int pktlen = le32_to_cpu(status->wake_packet_length);
diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c
index c1bdb558212..2053dccefcd 100644
--- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c
+++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
@@ -69,12 +69,6 @@ struct iwl_dbgfs_mvm_ctx {
struct ieee80211_vif *vif;
};
-static int iwl_dbgfs_open_file_generic(struct inode *inode, struct file *file)
-{
- file->private_data = inode->i_private;
- return 0;
-}
-
static ssize_t iwl_dbgfs_tx_flush_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
@@ -306,10 +300,191 @@ static ssize_t iwl_dbgfs_power_down_d3_allow_write(struct file *file,
return count;
}
+static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_vif *vif = file->private_data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->dbgfs_data;
+ u8 ap_sta_id;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ char buf[512];
+ int bufsz = sizeof(buf);
+ int pos = 0;
+ int i;
+
+ mutex_lock(&mvm->mutex);
+
+ ap_sta_id = mvmvif->ap_sta_id;
+
+ pos += scnprintf(buf+pos, bufsz-pos, "mac id/color: %d / %d\n",
+ mvmvif->id, mvmvif->color);
+ pos += scnprintf(buf+pos, bufsz-pos, "bssid: %pM\n",
+ vif->bss_conf.bssid);
+ pos += scnprintf(buf+pos, bufsz-pos, "QoS:\n");
+ for (i = 0; i < ARRAY_SIZE(mvmvif->queue_params); i++) {
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "\t%d: txop:%d - cw_min:%d - cw_max = %d - aifs = %d upasd = %d\n",
+ i, mvmvif->queue_params[i].txop,
+ mvmvif->queue_params[i].cw_min,
+ mvmvif->queue_params[i].cw_max,
+ mvmvif->queue_params[i].aifs,
+ mvmvif->queue_params[i].uapsd);
+ }
+
+ if (vif->type == NL80211_IFTYPE_STATION &&
+ ap_sta_id != IWL_MVM_STATION_COUNT) {
+ struct ieee80211_sta *sta;
+ struct iwl_mvm_sta *mvm_sta;
+
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[ap_sta_id],
+ lockdep_is_held(&mvm->mutex));
+ mvm_sta = (void *)sta->drv_priv;
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "ap_sta_id %d - reduced Tx power %d\n",
+ ap_sta_id, mvm_sta->bt_reduced_txpower);
+ }
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(vif->chanctx_conf);
+ if (chanctx_conf) {
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "idle rx chains %d, active rx chains: %d\n",
+ chanctx_conf->rx_chains_static,
+ chanctx_conf->rx_chains_dynamic);
+ }
+ rcu_read_unlock();
+
+ mutex_unlock(&mvm->mutex);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+#define BT_MBOX_MSG(_notif, _num, _field) \
+ ((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\
+ >> BT_MBOX##_num##_##_field##_POS)
+
+
+#define BT_MBOX_PRINT(_num, _field, _end) \
+ pos += scnprintf(buf + pos, bufsz - pos, \
+ "\t%s: %d%s", \
+ #_field, \
+ BT_MBOX_MSG(notif, _num, _field), \
+ true ? "\n" : ", ");
+
+static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif;
+ char *buf;
+ int ret, pos = 0, bufsz = sizeof(char) * 1024;
+
+ buf = kmalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ mutex_lock(&mvm->mutex);
+
+ pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw0:\n");
+
+ BT_MBOX_PRINT(0, LE_SLAVE_LAT, false);
+ BT_MBOX_PRINT(0, LE_PROF1, false);
+ BT_MBOX_PRINT(0, LE_PROF2, false);
+ BT_MBOX_PRINT(0, LE_PROF_OTHER, false);
+ BT_MBOX_PRINT(0, CHL_SEQ_N, false);
+ BT_MBOX_PRINT(0, INBAND_S, false);
+ BT_MBOX_PRINT(0, LE_MIN_RSSI, false);
+ BT_MBOX_PRINT(0, LE_SCAN, false);
+ BT_MBOX_PRINT(0, LE_ADV, false);
+ BT_MBOX_PRINT(0, LE_MAX_TX_POWER, false);
+ BT_MBOX_PRINT(0, OPEN_CON_1, true);
+
+ pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw1:\n");
+
+ BT_MBOX_PRINT(1, BR_MAX_TX_POWER, false);
+ BT_MBOX_PRINT(1, IP_SR, false);
+ BT_MBOX_PRINT(1, LE_MSTR, false);
+ BT_MBOX_PRINT(1, AGGR_TRFC_LD, false);
+ BT_MBOX_PRINT(1, MSG_TYPE, false);
+ BT_MBOX_PRINT(1, SSN, true);
+
+ pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw2:\n");
+
+ BT_MBOX_PRINT(2, SNIFF_ACT, false);
+ BT_MBOX_PRINT(2, PAG, false);
+ BT_MBOX_PRINT(2, INQUIRY, false);
+ BT_MBOX_PRINT(2, CONN, false);
+ BT_MBOX_PRINT(2, SNIFF_INTERVAL, false);
+ BT_MBOX_PRINT(2, DISC, false);
+ BT_MBOX_PRINT(2, SCO_TX_ACT, false);
+ BT_MBOX_PRINT(2, SCO_RX_ACT, false);
+ BT_MBOX_PRINT(2, ESCO_RE_TX, false);
+ BT_MBOX_PRINT(2, SCO_DURATION, true);
+
+ pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw3:\n");
+
+ BT_MBOX_PRINT(3, SCO_STATE, false);
+ BT_MBOX_PRINT(3, SNIFF_STATE, false);
+ BT_MBOX_PRINT(3, A2DP_STATE, false);
+ BT_MBOX_PRINT(3, ACL_STATE, false);
+ BT_MBOX_PRINT(3, MSTR_STATE, false);
+ BT_MBOX_PRINT(3, OBX_STATE, false);
+ BT_MBOX_PRINT(3, OPEN_CON_2, false);
+ BT_MBOX_PRINT(3, TRAFFIC_LOAD, false);
+ BT_MBOX_PRINT(3, CHL_SEQN_LSB, false);
+ BT_MBOX_PRINT(3, INBAND_P, false);
+ BT_MBOX_PRINT(3, MSG_TYPE_2, false);
+ BT_MBOX_PRINT(3, SSN_2, false);
+ BT_MBOX_PRINT(3, UPDATE_REQUEST, true);
+
+ pos += scnprintf(buf+pos, bufsz-pos, "bt_status = %d\n",
+ notif->bt_status);
+ pos += scnprintf(buf+pos, bufsz-pos, "bt_open_conn = %d\n",
+ notif->bt_open_conn);
+ pos += scnprintf(buf+pos, bufsz-pos, "bt_traffic_load = %d\n",
+ notif->bt_traffic_load);
+ pos += scnprintf(buf+pos, bufsz-pos, "bt_agg_traffic_load = %d\n",
+ notif->bt_agg_traffic_load);
+ pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n",
+ notif->bt_ci_compliance);
+
+ mutex_unlock(&mvm->mutex);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+
+ return ret;
+}
+#undef BT_MBOX_PRINT
+
+static ssize_t iwl_dbgfs_fw_restart_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ bool restart_fw = iwlwifi_mod_params.restart_fw;
+ int ret;
+
+ iwlwifi_mod_params.restart_fw = true;
+
+ mutex_lock(&mvm->mutex);
+
+ /* take the return value to make compiler happy - it will fail anyway */
+ ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_ERROR, CMD_SYNC, 0, NULL);
+
+ mutex_unlock(&mvm->mutex);
+
+ iwlwifi_mod_params.restart_fw = restart_fw;
+
+ return count;
+}
+
#define MVM_DEBUGFS_READ_FILE_OPS(name) \
static const struct file_operations iwl_dbgfs_##name##_ops = { \
.read = iwl_dbgfs_##name##_read, \
- .open = iwl_dbgfs_open_file_generic, \
+ .open = simple_open, \
.llseek = generic_file_llseek, \
}
@@ -317,14 +492,14 @@ static const struct file_operations iwl_dbgfs_##name##_ops = { \
static const struct file_operations iwl_dbgfs_##name##_ops = { \
.write = iwl_dbgfs_##name##_write, \
.read = iwl_dbgfs_##name##_read, \
- .open = iwl_dbgfs_open_file_generic, \
+ .open = simple_open, \
.llseek = generic_file_llseek, \
};
#define MVM_DEBUGFS_WRITE_FILE_OPS(name) \
static const struct file_operations iwl_dbgfs_##name##_ops = { \
.write = iwl_dbgfs_##name##_write, \
- .open = iwl_dbgfs_open_file_generic, \
+ .open = simple_open, \
.llseek = generic_file_llseek, \
};
@@ -345,8 +520,13 @@ MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush);
MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram);
MVM_DEBUGFS_READ_FILE_OPS(stations);
+MVM_DEBUGFS_READ_FILE_OPS(bt_notif);
MVM_DEBUGFS_WRITE_FILE_OPS(power_down_allow);
MVM_DEBUGFS_WRITE_FILE_OPS(power_down_d3_allow);
+MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart);
+
+/* Interface specific debugfs entries */
+MVM_DEBUGFS_READ_FILE_OPS(mac_params);
int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
{
@@ -358,8 +538,10 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
MVM_DEBUGFS_ADD_FILE(sta_drain, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(sram, mvm->debugfs_dir, S_IWUSR | S_IRUSR);
MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, S_IRUSR);
MVM_DEBUGFS_ADD_FILE(power_down_allow, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(power_down_d3_allow, mvm->debugfs_dir, S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR);
/*
* Create a symlink with mac80211. It will be removed when mac80211
@@ -376,3 +558,58 @@ err:
IWL_ERR(mvm, "Can't create the mvm debugfs directory\n");
return -ENOMEM;
}
+
+void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ struct dentry *dbgfs_dir = vif->debugfs_dir;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ char buf[100];
+
+ if (!dbgfs_dir)
+ return;
+
+ mvmvif->dbgfs_dir = debugfs_create_dir("iwlmvm", dbgfs_dir);
+ mvmvif->dbgfs_data = mvm;
+
+ if (!mvmvif->dbgfs_dir) {
+ IWL_ERR(mvm, "Failed to create debugfs directory under %s\n",
+ dbgfs_dir->d_name.name);
+ return;
+ }
+
+ MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir,
+ S_IRUSR);
+
+ /*
+ * Create symlink for convenience pointing to interface specific
+ * debugfs entries for the driver. For example, under
+ * /sys/kernel/debug/iwlwifi/0000\:02\:00.0/iwlmvm/
+ * find
+ * netdev:wlan0 -> ../../../ieee80211/phy0/netdev:wlan0/iwlmvm/
+ */
+ snprintf(buf, 100, "../../../%s/%s/%s/%s",
+ dbgfs_dir->d_parent->d_parent->d_name.name,
+ dbgfs_dir->d_parent->d_name.name,
+ dbgfs_dir->d_name.name,
+ mvmvif->dbgfs_dir->d_name.name);
+
+ mvmvif->dbgfs_slink = debugfs_create_symlink(dbgfs_dir->d_name.name,
+ mvm->debugfs_dir, buf);
+ if (!mvmvif->dbgfs_slink)
+ IWL_ERR(mvm, "Can't create debugfs symbolic link under %s\n",
+ dbgfs_dir->d_name.name);
+ return;
+err:
+ IWL_ERR(mvm, "Can't create debugfs entity\n");
+}
+
+void iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ debugfs_remove(mvmvif->dbgfs_slink);
+ mvmvif->dbgfs_slink = NULL;
+
+ debugfs_remove_recursive(mvmvif->dbgfs_dir);
+ mvmvif->dbgfs_dir = NULL;
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h
new file mode 100644
index 00000000000..05c61d6f384
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h
@@ -0,0 +1,319 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __fw_api_bt_coex_h__
+#define __fw_api_bt_coex_h__
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+
+#define BITS(nb) (BIT(nb) - 1)
+
+/**
+ * enum iwl_bt_coex_flags - flags for BT_COEX command
+ * @BT_CH_PRIMARY_EN:
+ * @BT_CH_SECONDARY_EN:
+ * @BT_NOTIF_COEX_OFF:
+ * @BT_COEX_MODE_POS:
+ * @BT_COEX_MODE_MSK:
+ * @BT_COEX_DISABLE:
+ * @BT_COEX_2W:
+ * @BT_COEX_3W:
+ * @BT_COEX_NW:
+ * @BT_USE_DEFAULTS:
+ * @BT_SYNC_2_BT_DISABLE:
+ * @BT_COEX_CORUNNING_TBL_EN:
+ */
+enum iwl_bt_coex_flags {
+ BT_CH_PRIMARY_EN = BIT(0),
+ BT_CH_SECONDARY_EN = BIT(1),
+ BT_NOTIF_COEX_OFF = BIT(2),
+ BT_COEX_MODE_POS = 3,
+ BT_COEX_MODE_MSK = BITS(3) << BT_COEX_MODE_POS,
+ BT_COEX_DISABLE = 0x0 << BT_COEX_MODE_POS,
+ BT_COEX_2W = 0x1 << BT_COEX_MODE_POS,
+ BT_COEX_3W = 0x2 << BT_COEX_MODE_POS,
+ BT_COEX_NW = 0x3 << BT_COEX_MODE_POS,
+ BT_USE_DEFAULTS = BIT(6),
+ BT_SYNC_2_BT_DISABLE = BIT(7),
+ /*
+ * For future use - when the flags will be enlarged
+ * BT_COEX_CORUNNING_TBL_EN = BIT(8),
+ */
+};
+
+/*
+ * indicates what has changed in the BT_COEX command.
+ */
+enum iwl_bt_coex_valid_bit_msk {
+ BT_VALID_ENABLE = BIT(0),
+ BT_VALID_BT_PRIO_BOOST = BIT(1),
+ BT_VALID_MAX_KILL = BIT(2),
+ BT_VALID_3W_TMRS = BIT(3),
+ BT_VALID_KILL_ACK = BIT(4),
+ BT_VALID_KILL_CTS = BIT(5),
+ BT_VALID_REDUCED_TX_POWER = BIT(6),
+ BT_VALID_LUT = BIT(7),
+ BT_VALID_WIFI_RX_SW_PRIO_BOOST = BIT(8),
+ BT_VALID_WIFI_TX_SW_PRIO_BOOST = BIT(9),
+ BT_VALID_MULTI_PRIO_LUT = BIT(10),
+ BT_VALID_TRM_KICK_FILTER = BIT(11),
+ BT_VALID_CORUN_LUT_20 = BIT(12),
+ BT_VALID_CORUN_LUT_40 = BIT(13),
+ BT_VALID_ANT_ISOLATION = BIT(14),
+ BT_VALID_ANT_ISOLATION_THRS = BIT(15),
+ /*
+ * For future use - when the valid flags will be enlarged
+ * BT_VALID_TXTX_DELTA_FREQ_THRS = BIT(16),
+ * BT_VALID_TXRX_MAX_FREQ_0 = BIT(17),
+ */
+};
+
+/**
+ * enum iwl_bt_reduced_tx_power - allows to reduce txpower for WiFi frames.
+ * @BT_REDUCED_TX_POWER_CTL: reduce Tx power for control frames
+ * @BT_REDUCED_TX_POWER_DATA: reduce Tx power for data frames
+ *
+ * This mechanism allows to have BT and WiFi run concurrently. Since WiFi
+ * reduces its Tx power, it can work along with BT, hence reducing the amount
+ * of WiFi frames being killed by BT.
+ */
+enum iwl_bt_reduced_tx_power {
+ BT_REDUCED_TX_POWER_CTL = BIT(0),
+ BT_REDUCED_TX_POWER_DATA = BIT(1),
+};
+
+#define BT_COEX_LUT_SIZE (12)
+
+/**
+ * struct iwl_bt_coex_cmd - bt coex configuration command
+ * @flags:&enum iwl_bt_coex_flags
+ * @lead_time:
+ * @max_kill:
+ * @bt3_time_t7_value:
+ * @kill_ack_msk:
+ * @kill_cts_msk:
+ * @bt3_prio_sample_time:
+ * @bt3_timer_t2_value:
+ * @bt4_reaction_time:
+ * @decision_lut[12]:
+ * @bt_reduced_tx_power: enum %iwl_bt_reduced_tx_power
+ * @valid_bit_msk: enum %iwl_bt_coex_valid_bit_msk
+ * @bt_prio_boost: values for PTA boost register
+ * @wifi_tx_prio_boost: SW boost of wifi tx priority
+ * @wifi_rx_prio_boost: SW boost of wifi rx priority
+ *
+ * The structure is used for the BT_COEX command.
+ */
+struct iwl_bt_coex_cmd {
+ u8 flags;
+ u8 lead_time;
+ u8 max_kill;
+ u8 bt3_time_t7_value;
+ __le32 kill_ack_msk;
+ __le32 kill_cts_msk;
+ u8 bt3_prio_sample_time;
+ u8 bt3_timer_t2_value;
+ __le16 bt4_reaction_time;
+ __le32 decision_lut[BT_COEX_LUT_SIZE];
+ u8 bt_reduced_tx_power;
+ u8 reserved;
+ __le16 valid_bit_msk;
+ __le32 bt_prio_boost;
+ u8 reserved2;
+ u8 wifi_tx_prio_boost;
+ __le16 wifi_rx_prio_boost;
+} __packed; /* BT_COEX_CMD_API_S_VER_3 */
+
+#define BT_MBOX(n_dw, _msg, _pos, _nbits) \
+ BT_MBOX##n_dw##_##_msg##_POS = (_pos), \
+ BT_MBOX##n_dw##_##_msg = BITS(_nbits) << BT_MBOX##n_dw##_##_msg##_POS
+
+enum iwl_bt_mxbox_dw0 {
+ BT_MBOX(0, LE_SLAVE_LAT, 0, 3),
+ BT_MBOX(0, LE_PROF1, 3, 1),
+ BT_MBOX(0, LE_PROF2, 4, 1),
+ BT_MBOX(0, LE_PROF_OTHER, 5, 1),
+ BT_MBOX(0, CHL_SEQ_N, 8, 4),
+ BT_MBOX(0, INBAND_S, 13, 1),
+ BT_MBOX(0, LE_MIN_RSSI, 16, 4),
+ BT_MBOX(0, LE_SCAN, 20, 1),
+ BT_MBOX(0, LE_ADV, 21, 1),
+ BT_MBOX(0, LE_MAX_TX_POWER, 24, 4),
+ BT_MBOX(0, OPEN_CON_1, 28, 2),
+};
+
+enum iwl_bt_mxbox_dw1 {
+ BT_MBOX(1, BR_MAX_TX_POWER, 0, 4),
+ BT_MBOX(1, IP_SR, 4, 1),
+ BT_MBOX(1, LE_MSTR, 5, 1),
+ BT_MBOX(1, AGGR_TRFC_LD, 8, 6),
+ BT_MBOX(1, MSG_TYPE, 16, 3),
+ BT_MBOX(1, SSN, 19, 2),
+};
+
+enum iwl_bt_mxbox_dw2 {
+ BT_MBOX(2, SNIFF_ACT, 0, 3),
+ BT_MBOX(2, PAG, 3, 1),
+ BT_MBOX(2, INQUIRY, 4, 1),
+ BT_MBOX(2, CONN, 5, 1),
+ BT_MBOX(2, SNIFF_INTERVAL, 8, 5),
+ BT_MBOX(2, DISC, 13, 1),
+ BT_MBOX(2, SCO_TX_ACT, 16, 2),
+ BT_MBOX(2, SCO_RX_ACT, 18, 2),
+ BT_MBOX(2, ESCO_RE_TX, 20, 2),
+ BT_MBOX(2, SCO_DURATION, 24, 6),
+};
+
+enum iwl_bt_mxbox_dw3 {
+ BT_MBOX(3, SCO_STATE, 0, 1),
+ BT_MBOX(3, SNIFF_STATE, 1, 1),
+ BT_MBOX(3, A2DP_STATE, 2, 1),
+ BT_MBOX(3, ACL_STATE, 3, 1),
+ BT_MBOX(3, MSTR_STATE, 4, 1),
+ BT_MBOX(3, OBX_STATE, 5, 1),
+ BT_MBOX(3, OPEN_CON_2, 8, 2),
+ BT_MBOX(3, TRAFFIC_LOAD, 10, 2),
+ BT_MBOX(3, CHL_SEQN_LSB, 12, 1),
+ BT_MBOX(3, INBAND_P, 13, 1),
+ BT_MBOX(3, MSG_TYPE_2, 16, 3),
+ BT_MBOX(3, SSN_2, 19, 2),
+ BT_MBOX(3, UPDATE_REQUEST, 21, 1),
+};
+
+#define BT_MBOX_MSG(_notif, _num, _field) \
+ ((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\
+ >> BT_MBOX##_num##_##_field##_POS)
+
+/**
+ * struct iwl_bt_coex_profile_notif - notification about BT coex
+ * @mbox_msg: message from BT to WiFi
+ * @:bt_status: 0 - off, 1 - on
+ * @:bt_open_conn: number of BT connections open
+ * @:bt_traffic_load: load of BT traffic
+ * @:bt_agg_traffic_load: aggregated load of BT traffic
+ * @:bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant
+ */
+struct iwl_bt_coex_profile_notif {
+ __le32 mbox_msg[4];
+ u8 bt_status;
+ u8 bt_open_conn;
+ u8 bt_traffic_load;
+ u8 bt_agg_traffic_load;
+ u8 bt_ci_compliance;
+ u8 reserved[3];
+} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_2 */
+
+enum iwl_bt_coex_prio_table_event {
+ BT_COEX_PRIO_TBL_EVT_INIT_CALIB1 = 0,
+ BT_COEX_PRIO_TBL_EVT_INIT_CALIB2 = 1,
+ BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1 = 2,
+ BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2 = 3,
+ BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1 = 4,
+ BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2 = 5,
+ BT_COEX_PRIO_TBL_EVT_DTIM = 6,
+ BT_COEX_PRIO_TBL_EVT_SCAN52 = 7,
+ BT_COEX_PRIO_TBL_EVT_SCAN24 = 8,
+ BT_COEX_PRIO_TBL_EVT_IDLE = 9,
+ BT_COEX_PRIO_TBL_EVT_MAX = 16,
+}; /* BT_COEX_PRIO_TABLE_EVENTS_API_E_VER_1 */
+
+enum iwl_bt_coex_prio_table_prio {
+ BT_COEX_PRIO_TBL_DISABLED = 0,
+ BT_COEX_PRIO_TBL_PRIO_LOW = 1,
+ BT_COEX_PRIO_TBL_PRIO_HIGH = 2,
+ BT_COEX_PRIO_TBL_PRIO_BYPASS = 3,
+ BT_COEX_PRIO_TBL_PRIO_COEX_OFF = 4,
+ BT_COEX_PRIO_TBL_PRIO_COEX_ON = 5,
+ BT_COEX_PRIO_TBL_PRIO_COEX_IDLE = 6,
+ BT_COEX_PRIO_TBL_MAX = 8,
+}; /* BT_COEX_PRIO_TABLE_PRIORITIES_API_E_VER_1 */
+
+#define BT_COEX_PRIO_TBL_SHRD_ANT_POS (0)
+#define BT_COEX_PRIO_TBL_PRIO_POS (1)
+#define BT_COEX_PRIO_TBL_RESERVED_POS (4)
+
+/**
+ * struct iwl_bt_coex_prio_tbl_cmd - priority table for BT coex
+ * @prio_tbl:
+ */
+struct iwl_bt_coex_prio_tbl_cmd {
+ u8 prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX];
+} __packed;
+
+enum iwl_bt_coex_env_action {
+ BT_COEX_ENV_CLOSE = 0,
+ BT_COEX_ENV_OPEN = 1,
+}; /* BT_COEX_PROT_ENV_ACTION_API_E_VER_1 */
+
+/**
+ * struct iwl_bt_coex_prot_env_cmd - BT Protection Envelope
+ * @action: enum %iwl_bt_coex_env_action
+ * @type: enum %iwl_bt_coex_prio_table_event
+ */
+struct iwl_bt_coex_prot_env_cmd {
+ u8 action; /* 0 = closed, 1 = open */
+ u8 type; /* 0 .. 15 */
+ u8 reserved[2];
+} __packed;
+
+#endif /* __fw_api_bt_coex_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
index cf6f9a02fb7..51e015d1dfb 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
@@ -258,7 +258,7 @@ enum iwl_wowlan_wakeup_reason {
IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE = BIT(8),
IWL_WOWLAN_WAKEUP_BY_REM_WAKE_LINK_LOSS = BIT(9),
IWL_WOWLAN_WAKEUP_BY_REM_WAKE_SIGNATURE_TABLE = BIT(10),
- IWL_WOWLAN_WAKEUP_BY_REM_WAKE_TCP_EXTERNAL = BIT(11),
+ /* BIT(11) reserved */
IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET = BIT(12),
}; /* WOWLAN_WAKE_UP_REASON_API_E_VER_2 */
@@ -277,6 +277,55 @@ struct iwl_wowlan_status {
u8 wake_packet[]; /* can be truncated from _length to _bufsize */
} __packed; /* WOWLAN_STATUSES_API_S_VER_4 */
+#define IWL_WOWLAN_TCP_MAX_PACKET_LEN 64
+#define IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN 128
+#define IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS 2048
+
+struct iwl_tcp_packet_info {
+ __le16 tcp_pseudo_header_checksum;
+ __le16 tcp_payload_length;
+} __packed; /* TCP_PACKET_INFO_API_S_VER_2 */
+
+struct iwl_tcp_packet {
+ struct iwl_tcp_packet_info info;
+ u8 rx_mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8];
+ u8 data[IWL_WOWLAN_TCP_MAX_PACKET_LEN];
+} __packed; /* TCP_PROTOCOL_PACKET_API_S_VER_1 */
+
+struct iwl_remote_wake_packet {
+ struct iwl_tcp_packet_info info;
+ u8 rx_mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8];
+ u8 data[IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN];
+} __packed; /* TCP_PROTOCOL_PACKET_API_S_VER_1 */
+
+struct iwl_wowlan_remote_wake_config {
+ __le32 connection_max_time; /* unused */
+ /* TCP_PROTOCOL_CONFIG_API_S_VER_1 */
+ u8 max_syn_retries;
+ u8 max_data_retries;
+ u8 tcp_syn_ack_timeout;
+ u8 tcp_ack_timeout;
+
+ struct iwl_tcp_packet syn_tx;
+ struct iwl_tcp_packet synack_rx;
+ struct iwl_tcp_packet keepalive_ack_rx;
+ struct iwl_tcp_packet fin_tx;
+
+ struct iwl_remote_wake_packet keepalive_tx;
+ struct iwl_remote_wake_packet wake_rx;
+
+ /* REMOTE_WAKE_OFFSET_INFO_API_S_VER_1 */
+ u8 sequence_number_offset;
+ u8 sequence_number_length;
+ u8 token_offset;
+ u8 token_length;
+ /* REMOTE_WAKE_PROTOCOL_PARAMS_API_S_VER_1 */
+ __le32 initial_sequence_number;
+ __le16 keepalive_interval;
+ __le16 num_tokens;
+ u8 tokens[IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS];
+} __packed; /* REMOTE_WAKE_CONFIG_API_S_VER_2 */
+
/* TODO: NetDetect API */
#endif /* __fw_api_d3_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h
index ae39b7dfda7..d68640ea41d 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
index be36b7604b7..81fe45f46be 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
@@ -68,73 +68,53 @@
/**
* enum iwl_scan_flags - masks for power table command flags
+ * @POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off
+ * receiver and transmitter. '0' - does not allow.
* @POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK: '0' Driver disables power management,
* '1' Driver enables PM (use rest of parameters)
- * @POWER_FLAGS_SLEEP_OVER_DTIM_MSK: '0' PM have to walk up every DTIM,
+ * @POWER_FLAGS_SKIP_OVER_DTIM_MSK: '0' PM have to walk up every DTIM,
* '1' PM could sleep over DTIM till listen Interval.
- * @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable.
- * @POWER_FLAGS_SNOOZE_ENA_MSK: Enable snoozing only if uAPSD is enabled and all
- * access categories are both delivery and trigger enabled.
- * @POWER_FLAGS_BT_SCO_ENA: Enable BT SCO coex only if uAPSD and
- * PBW Snoozing enabled
* @POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask
+ * @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable.
*/
enum iwl_power_flags {
- POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK = BIT(0),
- POWER_FLAGS_SLEEP_OVER_DTIM_MSK = BIT(1),
- POWER_FLAGS_LPRX_ENA_MSK = BIT(2),
- POWER_FLAGS_SNOOZE_ENA_MSK = BIT(3),
- POWER_FLAGS_BT_SCO_ENA = BIT(4),
- POWER_FLAGS_ADVANCE_PM_ENA_MSK = BIT(5)
+ POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0),
+ POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK = BIT(1),
+ POWER_FLAGS_SKIP_OVER_DTIM_MSK = BIT(2),
+ POWER_FLAGS_ADVANCE_PM_ENA_MSK = BIT(9),
+ POWER_FLAGS_LPRX_ENA_MSK = BIT(11),
};
+#define IWL_POWER_VEC_SIZE 5
+
/**
* struct iwl_powertable_cmd - Power Table Command
* POWER_TABLE_CMD = 0x77 (command, has simple generic response)
*
- * @id_and_color: MAC contex identifier
- * @action: Action on context - no action, add new,
- * modify existent, remove
* @flags: Power table command flags from POWER_FLAGS_*
* @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec.
- * Minimum allowed:- 3 * DTIM
+ * Minimum allowed:- 3 * DTIM. Keep alive period must be
+ * set regardless of power scheme or current power state.
+ * FW use this value also when PM is disabled.
* @rx_data_timeout: Minimum time (usec) from last Rx packet for AM to
* PSM transition - legacy PM
* @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to
* PSM transition - legacy PM
- * @rx_data_timeout_uapsd: Minimum time (usec) from last Rx packet for AM to
- * PSM transition - uAPSD
- * @tx_data_timeout_uapsd: Minimum time (usec) from last Tx packet for AM to
- * PSM transition - uAPSD
+ * @sleep_interval: not in use
+ * @keep_alive_beacons: not in use
* @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled.
* Default: 80dbm
- * @num_skip_dtim: Number of DTIMs to skip if Skip over DTIM flag is set
- * @snooze_interval: TBD
- * @snooze_window: TBD
- * @snooze_step: TBD
- * @qndp_tid: TBD
- * @uapsd_ac_flags: TBD
- * @uapsd_max_sp: TBD
*/
struct iwl_powertable_cmd {
- /* COMMON_INDEX_HDR_API_S_VER_1 */
- __le32 id_and_color;
- __le32 action;
+ /* PM_POWER_TABLE_CMD_API_S_VER_5 */
__le16 flags;
- u8 reserved;
- __le16 keep_alive_seconds;
+ u8 keep_alive_seconds;
+ u8 debug_flags;
__le32 rx_data_timeout;
__le32 tx_data_timeout;
- __le32 rx_data_timeout_uapsd;
- __le32 tx_data_timeout_uapsd;
- u8 lprx_rssi_threshold;
- u8 num_skip_dtim;
- __le16 snooze_interval;
- __le16 snooze_window;
- u8 snooze_step;
- u8 qndp_tid;
- u8 uapsd_ac_flags;
- u8 uapsd_max_sp;
+ __le32 sleep_interval[IWL_POWER_VEC_SIZE];
+ __le32 keep_alive_beacons;
+ __le32 lprx_rssi_threshold;
} __packed;
#endif
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h
index aa3474d0823..fdd33bc0a59 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
index 670ac8f95e2..b60d1415172 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h
index 0acb53dda22..a30691a8a85 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
index 2677914bf0a..007a93b25bd 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
@@ -537,6 +537,12 @@ struct iwl_mac_beacon_cmd {
struct ieee80211_hdr frame[0];
} __packed;
+struct iwl_beacon_notif {
+ struct iwl_mvm_tx_resp beacon_notify_hdr;
+ __le64 tsf;
+ __le32 ibss_mgr_status;
+} __packed;
+
/**
* enum iwl_dump_control - dump (flush) control flags
* @DUMP_TX_FIFO_FLUSH: Dump MSDUs until the the FIFO is empty
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
index 2adb61f103f..c6384555aab 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
@@ -70,6 +70,7 @@
#include "fw-api-mac.h"
#include "fw-api-power.h"
#include "fw-api-d3.h"
+#include "fw-api-bt-coex.h"
/* queue and FIFO numbers by usage */
enum {
@@ -150,8 +151,10 @@ enum {
SET_CALIB_DEFAULT_CMD = 0x8e,
+ BEACON_NOTIFICATION = 0x90,
BEACON_TEMPLATE_CMD = 0x91,
TX_ANT_CONFIGURATION_CMD = 0x98,
+ BT_CONFIG = 0x9b,
STATISTICS_NOTIFICATION = 0x9d,
/* RF-KILL commands and notifications */
@@ -162,9 +165,16 @@ enum {
REPLY_RX_MPDU_CMD = 0xc1,
BA_NOTIF = 0xc5,
+ /* BT Coex */
+ BT_COEX_PRIO_TABLE = 0xcc,
+ BT_COEX_PROT_ENV = 0xcd,
+ BT_PROFILE_NOTIFICATION = 0xce,
+
REPLY_DEBUG_CMD = 0xf0,
DEBUG_LOG_MSG = 0xf7,
+ MCAST_FILTER_CMD = 0xd0,
+
/* D3 commands/notifications */
D3_CONFIG_CMD = 0xd3,
PROT_OFFLOAD_CONFIG_CMD = 0xd4,
@@ -271,38 +281,7 @@ enum {
NVM_ACCESS_TARGET_EEPROM = 2,
};
-/**
- * struct iwl_nvm_access_cmd_ver1 - Request the device to send the NVM.
- * @op_code: 0 - read, 1 - write.
- * @target: NVM_ACCESS_TARGET_*. should be 0 for read.
- * @cache_refresh: 0 - None, 1- NVM.
- * @offset: offset in the nvm data.
- * @length: of the chunk.
- * @data: empty on read, the NVM chunk on write
- */
-struct iwl_nvm_access_cmd_ver1 {
- u8 op_code;
- u8 target;
- u8 cache_refresh;
- u8 reserved;
- __le16 offset;
- __le16 length;
- u8 data[];
-} __packed; /* NVM_ACCESS_CMD_API_S_VER_1 */
-
-/**
- * struct iwl_nvm_access_resp_ver1 - response to NVM_ACCESS_CMD
- * @offset: the offset in the nvm data
- * @length: of the chunk
- * @data: the nvm chunk on when NVM_ACCESS_CMD was read, nothing on write
- */
-struct iwl_nvm_access_resp_ver1 {
- __le16 offset;
- __le16 length;
- u8 data[];
-} __packed; /* NVM_ACCESS_CMD_RESP_API_S_VER_1 */
-
-/* Section types for NVM_ACCESS_CMD version 2 */
+/* Section types for NVM_ACCESS_CMD */
enum {
NVM_SECTION_TYPE_HW = 0,
NVM_SECTION_TYPE_SW,
@@ -323,7 +302,7 @@ enum {
* @length: in bytes, to read/write
* @data: if write operation, the data to write. On read its empty
*/
-struct iwl_nvm_access_cmd_ver2 {
+struct iwl_nvm_access_cmd {
u8 op_code;
u8 target;
__le16 type;
@@ -340,7 +319,7 @@ struct iwl_nvm_access_cmd_ver2 {
* @status: 0 for success, fail otherwise
* @data: if read operation, the data returned. Empty on write.
*/
-struct iwl_nvm_access_resp_ver2 {
+struct iwl_nvm_access_resp {
__le16 offset;
__le16 length;
__le16 type;
@@ -503,15 +482,34 @@ enum {
TE_DEP_TSF = 2,
TE_EVENT_SOCIOPATHIC = 4,
}; /* MAC_EVENT_DEPENDENCY_POLICY_API_E_VER_2 */
-
-/* When to send Time Event notifications and to whom (internal = FW) */
+/*
+ * Supported Time event notifications configuration.
+ * A notification (both event and fragment) includes a status indicating weather
+ * the FW was able to schedule the event or not. For fragment start/end
+ * notification the status is always success. There is no start/end fragment
+ * notification for monolithic events.
+ *
+ * @TE_NOTIF_NONE: no notifications
+ * @TE_NOTIF_HOST_EVENT_START: request/receive notification on event start
+ * @TE_NOTIF_HOST_EVENT_END:request/receive notification on event end
+ * @TE_NOTIF_INTERNAL_EVENT_START: internal FW use
+ * @TE_NOTIF_INTERNAL_EVENT_END: internal FW use.
+ * @TE_NOTIF_HOST_FRAG_START: request/receive notification on frag start
+ * @TE_NOTIF_HOST_FRAG_END:request/receive notification on frag end
+ * @TE_NOTIF_INTERNAL_FRAG_START: internal FW use.
+ * @TE_NOTIF_INTERNAL_FRAG_END: internal FW use.
+ */
enum {
TE_NOTIF_NONE = 0,
- TE_NOTIF_HOST_START = 0x1,
- TE_NOTIF_HOST_END = 0x2,
- TE_NOTIF_INTERNAL_START = 0x4,
- TE_NOTIF_INTERNAL_END = 0x8
-}; /* MAC_EVENT_ACTION_API_E_VER_1 */
+ TE_NOTIF_HOST_EVENT_START = 0x1,
+ TE_NOTIF_HOST_EVENT_END = 0x2,
+ TE_NOTIF_INTERNAL_EVENT_START = 0x4,
+ TE_NOTIF_INTERNAL_EVENT_END = 0x8,
+ TE_NOTIF_HOST_FRAG_START = 0x10,
+ TE_NOTIF_HOST_FRAG_END = 0x20,
+ TE_NOTIF_INTERNAL_FRAG_START = 0x40,
+ TE_NOTIF_INTERNAL_FRAG_END = 0x80
+}; /* MAC_EVENT_ACTION_API_E_VER_2 */
/*
* @TE_FRAG_NONE: fragmentation of the time event is NOT allowed.
@@ -794,6 +792,7 @@ struct iwl_phy_context_cmd {
* @byte_count: frame's byte-count
* @frame_time: frame's time on the air, based on byte count and frame rate
* calculation
+ * @mac_active_msk: what MACs were active when the frame was received
*
* Before each Rx, the device sends this data. It contains PHY information
* about the reception of the packet.
@@ -811,7 +810,7 @@ struct iwl_rx_phy_info {
__le32 non_cfg_phy[IWL_RX_INFO_PHY_CNT];
__le32 rate_n_flags;
__le32 byte_count;
- __le16 reserved2;
+ __le16 mac_active_msk;
__le16 frame_time;
} __packed;
@@ -951,4 +950,29 @@ struct iwl_set_calib_default_cmd {
u8 data[0];
} __packed; /* PHY_CALIB_OVERRIDE_VALUES_S */
+#define MAX_PORT_ID_NUM 2
+
+/**
+ * struct iwl_mcast_filter_cmd - configure multicast filter.
+ * @filter_own: Set 1 to filter out multicast packets sent by station itself
+ * @port_id: Multicast MAC addresses array specifier. This is a strange way
+ * to identify network interface adopted in host-device IF.
+ * It is used by FW as index in array of addresses. This array has
+ * MAX_PORT_ID_NUM members.
+ * @count: Number of MAC addresses in the array
+ * @pass_all: Set 1 to pass all multicast packets.
+ * @bssid: current association BSSID.
+ * @addr_list: Place holder for array of MAC addresses.
+ * IMPORTANT: add padding if necessary to ensure DWORD alignment.
+ */
+struct iwl_mcast_filter_cmd {
+ u8 filter_own;
+ u8 port_id;
+ u8 count;
+ u8 pass_all;
+ u8 bssid[6];
+ u8 reserved[2];
+ u8 addr_list[0];
+} __packed; /* MCAST_FILTERING_CMD_API_S_VER_1 */
+
#endif /* __fw_api_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c
index 500f818dba0..e18c92dd60e 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/iwlwifi/mvm/fw.c
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
@@ -114,7 +114,7 @@ static int iwl_send_tx_ant_cfg(struct iwl_mvm *mvm, u8 valid_tx_ant)
.valid = cpu_to_le32(valid_tx_ant),
};
- IWL_DEBUG_HC(mvm, "select valid tx ant: %u\n", valid_tx_ant);
+ IWL_DEBUG_FW(mvm, "select valid tx ant: %u\n", valid_tx_ant);
return iwl_mvm_send_cmd_pdu(mvm, TX_ANT_CONFIGURATION_CMD, CMD_SYNC,
sizeof(tx_ant_cmd), &tx_ant_cmd);
}
@@ -134,9 +134,10 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr);
alive_data->valid = le16_to_cpu(palive->status) == IWL_ALIVE_STATUS_OK;
- IWL_DEBUG_FW(mvm, "Alive ucode status 0x%04x revision 0x%01X 0x%01X\n",
+ IWL_DEBUG_FW(mvm,
+ "Alive ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
le16_to_cpu(palive->status), palive->ver_type,
- palive->ver_subtype);
+ palive->ver_subtype, palive->flags);
return true;
}
@@ -309,6 +310,10 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
goto error;
}
+ ret = iwl_send_bt_prio_tbl(mvm);
+ if (ret)
+ goto error;
+
if (read_nvm) {
/* Read nvm */
ret = iwl_nvm_init(mvm);
@@ -322,16 +327,14 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
WARN_ON(ret);
/* Send TX valid antennas before triggering calibrations */
- ret = iwl_send_tx_ant_cfg(mvm, mvm->nvm_data->valid_tx_ant);
+ ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw));
if (ret)
goto error;
- /* WkP doesn't have all calibrations, need to set default values */
- if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
- ret = iwl_set_default_calibrations(mvm);
- if (ret)
- goto error;
- }
+ /* need to set default values */
+ ret = iwl_set_default_calibrations(mvm);
+ if (ret)
+ goto error;
/*
* Send phy configurations command to init uCode
@@ -410,7 +413,15 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
goto error;
}
- ret = iwl_send_tx_ant_cfg(mvm, mvm->nvm_data->valid_tx_ant);
+ ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw));
+ if (ret)
+ goto error;
+
+ ret = iwl_send_bt_prio_tbl(mvm);
+ if (ret)
+ goto error;
+
+ ret = iwl_send_bt_init_conf(mvm);
if (ret)
goto error;
@@ -456,7 +467,7 @@ int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm)
goto error;
}
- ret = iwl_send_tx_ant_cfg(mvm, mvm->nvm_data->valid_tx_ant);
+ ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw));
if (ret)
goto error;
diff --git a/drivers/net/wireless/iwlwifi/mvm/led.c b/drivers/net/wireless/iwlwifi/mvm/led.c
index 011906e73a0..2269a9e5cc6 100644
--- a/drivers/net/wireless/iwlwifi/mvm/led.c
+++ b/drivers/net/wireless/iwlwifi/mvm/led.c
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
index 341dbc0237e..b2cc3d98e0f 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
@@ -196,7 +196,7 @@ u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm,
u32 qmask, ac;
if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
- return BIT(IWL_OFFCHANNEL_QUEUE);
+ return BIT(IWL_MVM_OFFCHANNEL_QUEUE);
qmask = (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE) ?
BIT(vif->cab_queue) : 0;
@@ -553,9 +553,9 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
if (vif->bss_conf.qos)
cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA);
+ /* Don't use cts to self as the fw doesn't support it currently. */
if (vif->bss_conf.use_cts_prot)
- cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT |
- MAC_PROT_FLG_SELF_CTS_EN);
+ cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT);
/*
* I think that we should enable these 2 flags regardless the HT PROT
@@ -586,10 +586,12 @@ static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm,
*/
static void iwl_mvm_mac_ctxt_cmd_fill_sta(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
- struct iwl_mac_data_sta *ctxt_sta)
+ struct iwl_mac_data_sta *ctxt_sta,
+ bool force_assoc_off)
{
/* We need the dtim_period to set the MAC as associated */
- if (vif->bss_conf.assoc && vif->bss_conf.dtim_period) {
+ if (vif->bss_conf.assoc && vif->bss_conf.dtim_period &&
+ !force_assoc_off) {
u32 dtim_offs;
/*
@@ -651,8 +653,16 @@ static int iwl_mvm_mac_ctxt_cmd_station(struct iwl_mvm *mvm,
/* Fill the common data for all mac context types */
iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
+ /* Allow beacons to pass through as long as we are not associated,or we
+ * do not have dtim period information */
+ if (!vif->bss_conf.assoc || !vif->bss_conf.dtim_period)
+ cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON);
+ else
+ cmd.filter_flags &= ~cpu_to_le32(MAC_FILTER_IN_BEACON);
+
/* Fill the data specific for station mode */
- iwl_mvm_mac_ctxt_cmd_fill_sta(mvm, vif, &cmd.sta);
+ iwl_mvm_mac_ctxt_cmd_fill_sta(mvm, vif, &cmd.sta,
+ action == FW_CTXT_ACTION_ADD);
return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
}
@@ -662,6 +672,7 @@ static int iwl_mvm_mac_ctxt_cmd_p2p_client(struct iwl_mvm *mvm,
u32 action)
{
struct iwl_mac_ctx_cmd cmd = {};
+ struct ieee80211_p2p_noa_attr *noa = &vif->bss_conf.p2p_noa_attr;
WARN_ON(vif->type != NL80211_IFTYPE_STATION || !vif->p2p);
@@ -669,9 +680,11 @@ static int iwl_mvm_mac_ctxt_cmd_p2p_client(struct iwl_mvm *mvm,
iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
/* Fill the data specific for station mode */
- iwl_mvm_mac_ctxt_cmd_fill_sta(mvm, vif, &cmd.p2p_sta.sta);
+ iwl_mvm_mac_ctxt_cmd_fill_sta(mvm, vif, &cmd.p2p_sta.sta,
+ action == FW_CTXT_ACTION_ADD);
- cmd.p2p_sta.ctwin = cpu_to_le32(vif->bss_conf.p2p_ctwindow);
+ cmd.p2p_sta.ctwin = cpu_to_le32(noa->oppps_ctwindow &
+ IEEE80211_P2P_OPPPS_CTWINDOW_MASK);
return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
}
@@ -685,7 +698,12 @@ static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm,
WARN_ON(vif->type != NL80211_IFTYPE_MONITOR);
iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
- /* No other data to be filled */
+
+ cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROMISC |
+ MAC_FILTER_IN_CONTROL_AND_MGMT |
+ MAC_FILTER_IN_BEACON |
+ MAC_FILTER_IN_PROBE_REQUEST);
+
return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
}
@@ -714,7 +732,9 @@ static int iwl_mvm_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm,
iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
cmd.protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT);
- cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROMISC);
+
+ /* Override the filter flags to accept only probe requests */
+ cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST);
/*
* This flag should be set to true when the P2P Device is
@@ -789,7 +809,7 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
TX_CMD_FLG_TSF);
mvm->mgmt_last_antenna_idx =
- iwl_mvm_next_antenna(mvm, mvm->nvm_data->valid_tx_ant,
+ iwl_mvm_next_antenna(mvm, iwl_fw_valid_tx_ant(mvm->fw),
mvm->mgmt_last_antenna_idx);
beacon_cmd.tx.rate_n_flags =
@@ -846,10 +866,10 @@ int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
*/
static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
- struct iwl_mac_data_ap *ctxt_ap)
+ struct iwl_mac_data_ap *ctxt_ap,
+ bool add)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- u32 curr_dev_time;
ctxt_ap->bi = cpu_to_le32(vif->bss_conf.beacon_int);
ctxt_ap->bi_reciprocal =
@@ -861,10 +881,19 @@ static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm,
vif->bss_conf.dtim_period));
ctxt_ap->mcast_qid = cpu_to_le32(vif->cab_queue);
- curr_dev_time = iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG);
- ctxt_ap->beacon_time = cpu_to_le32(curr_dev_time);
- ctxt_ap->beacon_tsf = cpu_to_le64(curr_dev_time);
+ /*
+ * Only read the system time when the MAC is being added, when we
+ * just modify the MAC then we should keep the time -- the firmware
+ * can otherwise have a "jumping" TBTT.
+ */
+ if (add)
+ mvmvif->ap_beacon_time =
+ iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG);
+
+ ctxt_ap->beacon_time = cpu_to_le32(mvmvif->ap_beacon_time);
+
+ ctxt_ap->beacon_tsf = 0; /* unused */
/* TODO: Assume that the beacon id == mac context id */
ctxt_ap->beacon_template = cpu_to_le32(mvmvif->id);
@@ -881,8 +910,12 @@ static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm,
/* Fill the common data for all mac context types */
iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
+ /* Also enable probe requests to pass */
+ cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST);
+
/* Fill the data specific for ap mode */
- iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.ap);
+ iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.ap,
+ action == FW_CTXT_ACTION_ADD);
return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
}
@@ -892,6 +925,7 @@ static int iwl_mvm_mac_ctxt_cmd_go(struct iwl_mvm *mvm,
u32 action)
{
struct iwl_mac_ctx_cmd cmd = {};
+ struct ieee80211_p2p_noa_attr *noa = &vif->bss_conf.p2p_noa_attr;
WARN_ON(vif->type != NL80211_IFTYPE_AP || !vif->p2p);
@@ -899,10 +933,14 @@ static int iwl_mvm_mac_ctxt_cmd_go(struct iwl_mvm *mvm,
iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
/* Fill the data specific for GO mode */
- iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.go.ap);
+ iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.go.ap,
+ action == FW_CTXT_ACTION_ADD);
- cmd.go.ctwin = cpu_to_le32(vif->bss_conf.p2p_ctwindow);
- cmd.go.opp_ps_enabled = cpu_to_le32(!!vif->bss_conf.p2p_oppps);
+ cmd.go.ctwin = cpu_to_le32(noa->oppps_ctwindow &
+ IEEE80211_P2P_OPPPS_CTWINDOW_MASK);
+ cmd.go.opp_ps_enabled =
+ cpu_to_le32(!!(noa->oppps_ctwindow &
+ IEEE80211_P2P_OPPPS_ENABLE_BIT));
return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
}
@@ -990,3 +1028,22 @@ int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
mvmvif->uploaded = false;
return 0;
}
+
+int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *cmd)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_beacon_notif *beacon = (void *)pkt->data;
+ u16 status __maybe_unused =
+ le16_to_cpu(beacon->beacon_notify_hdr.status.status);
+ u32 rate __maybe_unused =
+ le32_to_cpu(beacon->beacon_notify_hdr.initial_rate);
+
+ IWL_DEBUG_RX(mvm, "beacon status %#x retries:%d tsf:0x%16llX rate:%d\n",
+ status & TX_STATUS_MSK,
+ beacon->beacon_notify_hdr.failure_frame,
+ le64_to_cpu(beacon->tsf),
+ rate);
+ return 0;
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index 7e169b085af..a5eb8c82f16 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
@@ -65,7 +65,9 @@
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
+#include <linux/ip.h>
#include <net/mac80211.h>
+#include <net/tcp.h>
#include "iwl-op-mode.h"
#include "iwl-io.h"
@@ -102,10 +104,33 @@ static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = {
},
};
+#ifdef CONFIG_PM_SLEEP
+static const struct nl80211_wowlan_tcp_data_token_feature
+iwl_mvm_wowlan_tcp_token_feature = {
+ .min_len = 0,
+ .max_len = 255,
+ .bufsize = IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS,
+};
+
+static const struct wiphy_wowlan_tcp_support iwl_mvm_wowlan_tcp_support = {
+ .tok = &iwl_mvm_wowlan_tcp_token_feature,
+ .data_payload_max = IWL_WOWLAN_TCP_MAX_PACKET_LEN -
+ sizeof(struct ethhdr) -
+ sizeof(struct iphdr) -
+ sizeof(struct tcphdr),
+ .data_interval_max = 65535, /* __le16 in API */
+ .wake_payload_max = IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN -
+ sizeof(struct ethhdr) -
+ sizeof(struct iphdr) -
+ sizeof(struct tcphdr),
+ .seq = true,
+};
+#endif
+
int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
{
struct ieee80211_hw *hw = mvm->hw;
- int num_mac, ret;
+ int num_mac, ret, i;
/* Tell mac80211 our characteristics */
hw->flags = IEEE80211_HW_SIGNAL_DBM |
@@ -118,8 +143,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
IEEE80211_HW_AMPDU_AGGREGATION |
IEEE80211_HW_TIMING_BEACON_ONLY;
- hw->queues = IWL_FIRST_AMPDU_QUEUE;
- hw->offchannel_tx_hw_queue = IWL_OFFCHANNEL_QUEUE;
+ hw->queues = IWL_MVM_FIRST_AGG_QUEUE;
+ hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE;
hw->rate_control_algorithm = "iwl-mvm-rs";
/*
@@ -149,18 +174,22 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->wiphy->n_iface_combinations =
ARRAY_SIZE(iwl_mvm_iface_combinations);
- hw->wiphy->max_remain_on_channel_duration = 500;
+ hw->wiphy->max_remain_on_channel_duration = 10000;
hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
/* Extract MAC address */
memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN);
hw->wiphy->addresses = mvm->addresses;
hw->wiphy->n_addresses = 1;
- num_mac = mvm->nvm_data->n_hw_addrs;
- if (num_mac > 1) {
- memcpy(mvm->addresses[1].addr, mvm->addresses[0].addr,
+
+ /* Extract additional MAC addresses if available */
+ num_mac = (mvm->nvm_data->n_hw_addrs > 1) ?
+ min(IWL_MVM_MAX_ADDRESSES, mvm->nvm_data->n_hw_addrs) : 1;
+
+ for (i = 1; i < num_mac; i++) {
+ memcpy(mvm->addresses[i].addr, mvm->addresses[i-1].addr,
ETH_ALEN);
- mvm->addresses[1].addr[5]++;
+ mvm->addresses[i].addr[5]++;
hw->wiphy->n_addresses++;
}
@@ -178,7 +207,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->wiphy->hw_version = mvm->trans->hw_id;
- if (iwlwifi_mod_params.power_save)
+ if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
else
hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
@@ -206,6 +235,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->wiphy->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS;
hw->wiphy->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN;
hw->wiphy->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN;
+ hw->wiphy->wowlan.tcp = &iwl_mvm_wowlan_tcp_support;
}
#endif
@@ -227,7 +257,7 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
goto drop;
}
- if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_OFFCHANNEL_QUEUE &&
+ if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE &&
!test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status))
goto drop;
@@ -273,12 +303,18 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, 0, false);
break;
case IEEE80211_AMPDU_TX_START:
+ if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG) {
+ ret = -EINVAL;
+ break;
+ }
ret = iwl_mvm_sta_tx_agg_start(mvm, vif, sta, tid, ssn);
break;
case IEEE80211_AMPDU_TX_STOP_CONT:
+ ret = iwl_mvm_sta_tx_agg_stop(mvm, vif, sta, tid);
+ break;
case IEEE80211_AMPDU_TX_STOP_FLUSH:
case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
- ret = iwl_mvm_sta_tx_agg_stop(mvm, vif, sta, tid);
+ ret = iwl_mvm_sta_tx_agg_flush(mvm, vif, sta, tid);
break;
case IEEE80211_AMPDU_TX_OPERATIONAL:
ret = iwl_mvm_sta_tx_agg_oper(mvm, vif, sta, tid, buf_size);
@@ -466,11 +502,15 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
/*
* TODO: remove this temporary code.
* Currently MVM FW supports power management only on single MAC.
- * Iterate and disable PM on all active interfaces.
+ * If new interface added, disable PM on existing interface.
+ * P2P device is a special case, since it is handled by FW similary to
+ * scan. If P2P deviced is added, PM remains enabled on existing
+ * interface.
* Note: the method below does not count the new interface being added
* at this moment.
*/
- mvm->vif_count++;
+ if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
+ mvm->vif_count++;
if (mvm->vif_count > 1) {
IWL_DEBUG_MAC80211(mvm,
"Disable power on existing interfaces\n");
@@ -526,6 +566,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
mvm->p2p_device_vif = vif;
}
+ iwl_mvm_vif_dbgfs_register(mvm, vif);
goto out_unlock;
out_unbind:
@@ -539,10 +580,11 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
/*
* TODO: remove this temporary code.
* Currently MVM FW supports power management only on single MAC.
- * Check if only one additional interface remains after rereasing
+ * Check if only one additional interface remains after releasing
* current one. Update power mode on the remaining interface.
*/
- mvm->vif_count--;
+ if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
+ mvm->vif_count--;
IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n",
mvm->vif_count);
if (mvm->vif_count == 1) {
@@ -604,6 +646,8 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
mutex_lock(&mvm->mutex);
+ iwl_mvm_vif_dbgfs_clean(mvm, vif);
+
/*
* For AP/GO interface, the tear down of the resources allocated to the
* interface is be handled as part of the stop_ap flow.
@@ -627,7 +671,7 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
* Check if only one additional interface remains after removing
* current one. Update power mode on the remaining interface.
*/
- if (mvm->vif_count)
+ if (mvm->vif_count && vif->type != NL80211_IFTYPE_P2P_DEVICE)
mvm->vif_count--;
IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n",
mvm->vif_count);
@@ -657,6 +701,20 @@ static void iwl_mvm_configure_filter(struct ieee80211_hw *hw,
*total_flags = 0;
}
+static int iwl_mvm_configure_mcast_filter(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mcast_filter_cmd mcast_filter_cmd = {
+ .pass_all = 1,
+ };
+
+ memcpy(mcast_filter_cmd.bssid, vif->bss_conf.bssid, ETH_ALEN);
+
+ return iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_SYNC,
+ sizeof(mcast_filter_cmd),
+ &mcast_filter_cmd);
+}
+
static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf,
@@ -677,6 +735,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
IWL_ERR(mvm, "failed to update quotas\n");
return;
}
+ iwl_mvm_bt_coex_vif_assoc(mvm, vif);
+ iwl_mvm_configure_mcast_filter(mvm, vif);
} else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
/* remove AP station now that the MAC is unassoc */
ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id);
@@ -886,7 +946,7 @@ static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
switch (cmd) {
case STA_NOTIFY_SLEEP:
- if (atomic_read(&mvmsta->pending_frames) > 0)
+ if (atomic_read(&mvm->pending_frames[mvmsta->sta_id]) > 0)
ieee80211_sta_block_awake(hw, sta, true);
/*
* The fw updates the STA to be asleep. Tx packets on the Tx
@@ -895,7 +955,7 @@ static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
*/
break;
case STA_NOTIFY_AWAKE:
- if (WARN_ON(mvmsta->sta_id == IWL_INVALID_STATION))
+ if (WARN_ON(mvmsta->sta_id == IWL_MVM_STATION_COUNT))
break;
iwl_mvm_sta_modify_ps_wake(mvm, sta);
break;
@@ -1051,6 +1111,13 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
switch (cmd) {
case SET_KEY:
+ if (vif->type == NL80211_IFTYPE_AP && !sta) {
+ /* GTK on AP interface is a TX-only key, return 0 */
+ ret = 0;
+ key->hw_key_idx = STA_KEY_IDX_INVALID;
+ break;
+ }
+
IWL_DEBUG_MAC80211(mvm, "set hwcrypto key\n");
ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, false);
if (ret) {
@@ -1059,11 +1126,17 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
* can't add key for RX, but we don't need it
* in the device for TX so still return 0
*/
+ key->hw_key_idx = STA_KEY_IDX_INVALID;
ret = 0;
}
break;
case DISABLE_KEY:
+ if (key->hw_key_idx == STA_KEY_IDX_INVALID) {
+ ret = 0;
+ break;
+ }
+
IWL_DEBUG_MAC80211(mvm, "disable hwcrypto key\n");
ret = iwl_mvm_remove_sta_key(mvm, vif, sta, key);
break;
@@ -1090,7 +1163,8 @@ static void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw,
static int iwl_mvm_roc(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_channel *channel,
- int duration)
+ int duration,
+ enum ieee80211_roc_type type)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct cfg80211_chan_def chandef;
@@ -1101,8 +1175,8 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw,
return -EINVAL;
}
- IWL_DEBUG_MAC80211(mvm, "enter (%d, %d)\n", channel->hw_value,
- duration);
+ IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value,
+ duration, type);
mutex_lock(&mvm->mutex);
@@ -1111,7 +1185,7 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw,
&chandef, 1, 1);
/* Schedule the time events */
- ret = iwl_mvm_start_p2p_roc(mvm, vif, duration);
+ ret = iwl_mvm_start_p2p_roc(mvm, vif, duration, type);
mutex_unlock(&mvm->mutex);
IWL_DEBUG_MAC80211(mvm, "leave\n");
@@ -1215,6 +1289,7 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
* will handle quota settings.
*/
if (vif->type == NL80211_IFTYPE_MONITOR) {
+ mvmvif->monitor_active = true;
ret = iwl_mvm_update_quotas(mvm, vif);
if (ret)
goto out_remove_binding;
@@ -1245,15 +1320,16 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
if (vif->type == NL80211_IFTYPE_AP)
goto out_unlock;
- iwl_mvm_binding_remove_vif(mvm, vif);
switch (vif->type) {
case NL80211_IFTYPE_MONITOR:
- iwl_mvm_update_quotas(mvm, vif);
+ mvmvif->monitor_active = false;
+ iwl_mvm_update_quotas(mvm, NULL);
break;
default:
break;
}
+ iwl_mvm_binding_remove_vif(mvm, vif);
out_unlock:
mvmvif->phy_ctxt = NULL;
mutex_unlock(&mvm->mutex);
@@ -1274,6 +1350,15 @@ static int iwl_mvm_set_tim(struct ieee80211_hw *hw,
return iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm_sta->vif);
}
+static void iwl_mvm_mac_rssi_callback(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum ieee80211_rssi_event rssi_event)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ iwl_mvm_bt_rssi_event(mvm, vif, rssi_event);
+}
+
struct ieee80211_ops iwl_mvm_hw_ops = {
.tx = iwl_mvm_mac_tx,
.ampdu_action = iwl_mvm_mac_ampdu_action,
@@ -1297,6 +1382,7 @@ struct ieee80211_ops iwl_mvm_hw_ops = {
.update_tkip_key = iwl_mvm_mac_update_tkip_key,
.remain_on_channel = iwl_mvm_roc,
.cancel_remain_on_channel = iwl_mvm_cancel_roc,
+ .rssi_callback = iwl_mvm_mac_rssi_callback,
.add_chanctx = iwl_mvm_add_chanctx,
.remove_chanctx = iwl_mvm_remove_chanctx,
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
index bdae700c769..9f46b23801b 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
@@ -79,7 +79,7 @@
#include "fw-api.h"
#define IWL_INVALID_MAC80211_QUEUE 0xff
-#define IWL_MVM_MAX_ADDRESSES 2
+#define IWL_MVM_MAX_ADDRESSES 5
/* RSSI offset for WkP */
#define IWL_RSSI_OFFSET 50
@@ -90,10 +90,6 @@ enum iwl_mvm_tx_fifo {
IWL_MVM_TX_FIFO_VO,
};
-/* Placeholder */
-#define IWL_OFFCHANNEL_QUEUE 8
-#define IWL_FIRST_AMPDU_QUEUE 11
-
extern struct ieee80211_ops iwl_mvm_hw_ops;
/**
* struct iwl_mvm_mod_params - module parameters for iwlmvm
@@ -161,6 +157,8 @@ enum iwl_power_scheme {
* @uploaded: indicates the MAC context has been added to the device
* @ap_active: indicates that ap context is configured, and that the interface
* should get quota etc.
+ * @monitor_active: indicates that monitor context is configured, and that the
+ * interface should get quota etc.
* @queue_params: QoS params for this MAC
* @bcast_sta: station used for broadcast packets. Used by the following
* vifs: P2P_DEVICE, GO and AP.
@@ -173,6 +171,9 @@ struct iwl_mvm_vif {
bool uploaded;
bool ap_active;
+ bool monitor_active;
+
+ u32 ap_beacon_time;
enum iwl_tsf_id tsf_id;
@@ -211,6 +212,7 @@ struct iwl_mvm_vif {
#ifdef CONFIG_IWLWIFI_DEBUGFS
struct dentry *dbgfs_dir;
+ struct dentry *dbgfs_slink;
void *dbgfs_data;
#endif
};
@@ -279,10 +281,7 @@ struct iwl_mvm {
atomic_t queue_stop_count[IWL_MAX_HW_QUEUES];
struct iwl_nvm_data *nvm_data;
- /* eeprom blob for debugfs/testmode */
- u8 *eeprom_blob;
- size_t eeprom_blob_size;
- /* NVM sections for 7000 family */
+ /* NVM sections */
struct iwl_nvm_section nvm_sections[NVM_NUM_OF_SECTIONS];
/* EEPROM MAC addresses */
@@ -293,6 +292,7 @@ struct iwl_mvm {
struct ieee80211_sta __rcu *fw_id_to_mac_id[IWL_MVM_STATION_COUNT];
struct work_struct sta_drained_wk;
unsigned long sta_drained[BITS_TO_LONGS(IWL_MVM_STATION_COUNT)];
+ atomic_t pending_frames[IWL_MVM_STATION_COUNT];
/* configured by mac80211 */
u32 rts_threshold;
@@ -323,6 +323,13 @@ struct iwl_mvm {
* can hold 16 keys at most. Reflect this fact.
*/
unsigned long fw_key_table[BITS_TO_LONGS(STA_KEY_MAX_NUM)];
+
+ /*
+ * This counter of created interfaces is referenced only in conjunction
+ * with FW limitation related to power management. Currently PM is
+ * supported only on a single interface.
+ * IMPORTANT: this variable counts all interfaces except P2P device.
+ */
u8 vif_count;
struct led_classdev led;
@@ -332,6 +339,10 @@ struct iwl_mvm {
#ifdef CONFIG_PM_SLEEP
int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen;
#endif
+
+ /* BT-Coex */
+ u8 bt_kill_msk;
+ struct iwl_bt_coex_profile_notif last_bt_notif;
};
/* Extract MVM priv from op_mode and _hw */
@@ -445,6 +456,9 @@ u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm,
struct ieee80211_vif *vif);
int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
struct ieee80211_vif *vif);
+int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *cmd);
/* Bindings */
int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
@@ -466,16 +480,22 @@ void iwl_mvm_cancel_scan(struct iwl_mvm *mvm);
/* MVM debugfs */
#ifdef CONFIG_IWLWIFI_DEBUGFS
int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir);
-int iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct dentry *dbgfs_dir);
-void iwl_power_get_params(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct iwl_powertable_cmd *cmd);
+void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+void iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
#else
static inline int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm,
struct dentry *dbgfs_dir)
{
return 0;
}
+static inline void
+iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+}
+static inline void
+iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+}
#endif /* CONFIG_IWLWIFI_DEBUGFS */
/* rate scaling */
@@ -485,6 +505,8 @@ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
/* power managment */
int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct iwl_powertable_cmd *cmd);
int iwl_mvm_leds_init(struct iwl_mvm *mvm);
void iwl_mvm_leds_exit(struct iwl_mvm *mvm);
@@ -502,4 +524,14 @@ void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw,
void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, int idx);
+/* BT Coex */
+int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm);
+int iwl_send_bt_init_conf(struct iwl_mvm *mvm);
+int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *cmd);
+void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ enum ieee80211_rssi_event rssi_event);
+void iwl_mvm_bt_coex_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+
#endif /* __IWL_MVM_H__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c
index 20016bcbdea..b8ec02f89ac 100644
--- a/drivers/net/wireless/iwlwifi/mvm/nvm.c
+++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
@@ -74,26 +74,11 @@ static const int nvm_to_read[] = {
NVM_SECTION_TYPE_PRODUCTION,
};
-/* used to simplify the shared operations on NCM_ACCESS_CMD versions */
-union iwl_nvm_access_cmd {
- struct iwl_nvm_access_cmd_ver1 ver1;
- struct iwl_nvm_access_cmd_ver2 ver2;
-};
-union iwl_nvm_access_resp {
- struct iwl_nvm_access_resp_ver1 ver1;
- struct iwl_nvm_access_resp_ver2 ver2;
-};
-
-static inline void iwl_nvm_fill_read_ver1(struct iwl_nvm_access_cmd_ver1 *cmd,
- u16 offset, u16 length)
-{
- cmd->offset = cpu_to_le16(offset);
- cmd->length = cpu_to_le16(length);
- cmd->cache_refresh = 1;
-}
+/* Default NVM size to read */
+#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024);
-static inline void iwl_nvm_fill_read_ver2(struct iwl_nvm_access_cmd_ver2 *cmd,
- u16 offset, u16 length, u16 section)
+static inline void iwl_nvm_fill_read(struct iwl_nvm_access_cmd *cmd,
+ u16 offset, u16 length, u16 section)
{
cmd->offset = cpu_to_le16(offset);
cmd->length = cpu_to_le16(length);
@@ -103,8 +88,8 @@ static inline void iwl_nvm_fill_read_ver2(struct iwl_nvm_access_cmd_ver2 *cmd,
static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section,
u16 offset, u16 length, u8 *data)
{
- union iwl_nvm_access_cmd nvm_access_cmd;
- union iwl_nvm_access_resp *nvm_resp;
+ struct iwl_nvm_access_cmd nvm_access_cmd = {};
+ struct iwl_nvm_access_resp *nvm_resp;
struct iwl_rx_packet *pkt;
struct iwl_host_cmd cmd = {
.id = NVM_ACCESS_CMD,
@@ -114,18 +99,8 @@ static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section,
int ret, bytes_read, offset_read;
u8 *resp_data;
- memset(&nvm_access_cmd, 0, sizeof(nvm_access_cmd));
-
- /* TODO: not sure family should be the decider, maybe FW version? */
- if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
- iwl_nvm_fill_read_ver2(&(nvm_access_cmd.ver2),
- offset, length, section);
- cmd.len[0] = sizeof(struct iwl_nvm_access_cmd_ver2);
- } else {
- iwl_nvm_fill_read_ver1(&(nvm_access_cmd.ver1),
- offset, length);
- cmd.len[0] = sizeof(struct iwl_nvm_access_cmd_ver1);
- }
+ iwl_nvm_fill_read(&nvm_access_cmd, offset, length, section);
+ cmd.len[0] = sizeof(struct iwl_nvm_access_cmd);
ret = iwl_mvm_send_cmd(mvm, &cmd);
if (ret)
@@ -141,17 +116,10 @@ static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section,
/* Extract NVM response */
nvm_resp = (void *)pkt->data;
- if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
- ret = le16_to_cpu(nvm_resp->ver2.status);
- bytes_read = le16_to_cpu(nvm_resp->ver2.length);
- offset_read = le16_to_cpu(nvm_resp->ver2.offset);
- resp_data = nvm_resp->ver2.data;
- } else {
- ret = le16_to_cpu(nvm_resp->ver1.length) <= 0;
- bytes_read = le16_to_cpu(nvm_resp->ver1.length);
- offset_read = le16_to_cpu(nvm_resp->ver1.offset);
- resp_data = nvm_resp->ver1.data;
- }
+ ret = le16_to_cpu(nvm_resp->status);
+ bytes_read = le16_to_cpu(nvm_resp->length);
+ offset_read = le16_to_cpu(nvm_resp->offset);
+ resp_data = nvm_resp->data;
if (ret) {
IWL_ERR(mvm,
"NVM access command failed with status %d (device: %s)\n",
@@ -191,17 +159,10 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,
{
u16 length, offset = 0;
int ret;
- bool old_eeprom = mvm->cfg->device_family != IWL_DEVICE_FAMILY_7000;
- length = (iwlwifi_mod_params.amsdu_size_8K ? (8 * 1024) : (4 * 1024))
- - sizeof(union iwl_nvm_access_cmd)
- - sizeof(struct iwl_rx_packet);
- /*
- * if length is greater than EEPROM size, truncate it because uCode
- * doesn't check it by itself, and exit the loop when reached.
- */
- if (old_eeprom && length > mvm->cfg->base_params->eeprom_size)
- length = mvm->cfg->base_params->eeprom_size;
+ /* Set nvm section read length */
+ length = IWL_NVM_DEFAULT_CHUNK_SIZE;
+
ret = length;
/* Read the NVM until exhausted (reading less than requested) */
@@ -214,8 +175,6 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,
return ret;
}
offset += ret;
- if (old_eeprom && offset == mvm->cfg->base_params->eeprom_size)
- break;
}
IWL_INFO(mvm, "NVM section %d read completed\n", section);
@@ -249,63 +208,31 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
int ret, i, section;
u8 *nvm_buffer, *temp;
- if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
- /* TODO: find correct NVM max size for a section */
- nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size,
- GFP_KERNEL);
- if (!nvm_buffer)
- return -ENOMEM;
- for (i = 0; i < ARRAY_SIZE(nvm_to_read); i++) {
- section = nvm_to_read[i];
- /* we override the constness for initial read */
- ret = iwl_nvm_read_section(mvm, section, nvm_buffer);
- if (ret < 0)
- break;
- temp = kmemdup(nvm_buffer, ret, GFP_KERNEL);
- if (!temp) {
- ret = -ENOMEM;
- break;
- }
- mvm->nvm_sections[section].data = temp;
- mvm->nvm_sections[section].length = ret;
- }
- kfree(nvm_buffer);
+ /* TODO: find correct NVM max size for a section */
+ nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size,
+ GFP_KERNEL);
+ if (!nvm_buffer)
+ return -ENOMEM;
+ for (i = 0; i < ARRAY_SIZE(nvm_to_read); i++) {
+ section = nvm_to_read[i];
+ /* we override the constness for initial read */
+ ret = iwl_nvm_read_section(mvm, section, nvm_buffer);
if (ret < 0)
- return ret;
- } else {
- /* allocate eeprom */
- mvm->eeprom_blob_size = mvm->cfg->base_params->eeprom_size;
- IWL_DEBUG_EEPROM(mvm->trans->dev, "NVM size = %zd\n",
- mvm->eeprom_blob_size);
- mvm->eeprom_blob = kzalloc(mvm->eeprom_blob_size, GFP_KERNEL);
- if (!mvm->eeprom_blob)
- return -ENOMEM;
-
- ret = iwl_nvm_read_section(mvm, 0, mvm->eeprom_blob);
- if (ret != mvm->eeprom_blob_size) {
- IWL_ERR(mvm, "Read partial NVM %d/%zd\n",
- ret, mvm->eeprom_blob_size);
- kfree(mvm->eeprom_blob);
- mvm->eeprom_blob = NULL;
- return -EINVAL;
+ break;
+ temp = kmemdup(nvm_buffer, ret, GFP_KERNEL);
+ if (!temp) {
+ ret = -ENOMEM;
+ break;
}
+ mvm->nvm_sections[section].data = temp;
+ mvm->nvm_sections[section].length = ret;
}
+ kfree(nvm_buffer);
+ if (ret < 0)
+ return ret;
ret = 0;
- if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000)
- mvm->nvm_data = iwl_parse_nvm_sections(mvm);
- else
- mvm->nvm_data =
- iwl_parse_eeprom_data(mvm->trans->dev,
- mvm->cfg,
- mvm->eeprom_blob,
- mvm->eeprom_blob_size);
-
- if (!mvm->nvm_data) {
- kfree(mvm->eeprom_blob);
- mvm->eeprom_blob = NULL;
- ret = -ENOMEM;
- }
+ mvm->nvm_data = iwl_parse_nvm_sections(mvm);
return ret;
}
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c
index d0f9c1e0475..b29c31a4159 100644
--- a/drivers/net/wireless/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/iwlwifi/mvm/ops.c
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
@@ -143,21 +143,12 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode)
u8 radio_cfg_type, radio_cfg_step, radio_cfg_dash;
u32 reg_val = 0;
- /*
- * We can't upload the correct value to the INIT image
- * as we don't have nvm_data by that time.
- *
- * TODO: Figure out what we should do here
- */
- if (mvm->nvm_data) {
- radio_cfg_type = mvm->nvm_data->radio_cfg_type;
- radio_cfg_step = mvm->nvm_data->radio_cfg_step;
- radio_cfg_dash = mvm->nvm_data->radio_cfg_dash;
- } else {
- radio_cfg_type = 0;
- radio_cfg_step = 0;
- radio_cfg_dash = 0;
- }
+ radio_cfg_type = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_TYPE) >>
+ FW_PHY_CFG_RADIO_TYPE_POS;
+ radio_cfg_step = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_STEP) >>
+ FW_PHY_CFG_RADIO_STEP_POS;
+ radio_cfg_dash = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_DASH) >>
+ FW_PHY_CFG_RADIO_DASH_POS;
/* SKU control */
reg_val |= CSR_HW_REV_STEP(mvm->trans->hw_rev) <<
@@ -175,7 +166,6 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode)
/* silicon bits */
reg_val |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI;
- reg_val |= CSR_HW_IF_CONFIG_REG_BIT_MAC_SI;
iwl_trans_set_bits_mask(mvm->trans, CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH |
@@ -230,6 +220,9 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false),
RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, false),
+ RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true),
+ RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, false),
+
RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false),
RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false),
@@ -274,6 +267,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
CMD(WEP_KEY),
CMD(REPLY_RX_PHY_CMD),
CMD(REPLY_RX_MPDU_CMD),
+ CMD(BEACON_NOTIFICATION),
CMD(BEACON_TEMPLATE_CMD),
CMD(STATISTICS_NOTIFICATION),
CMD(TX_ANT_CONFIGURATION_CMD),
@@ -293,6 +287,12 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
CMD(NET_DETECT_PROFILES_CMD),
CMD(NET_DETECT_HOTSPOTS_CMD),
CMD(NET_DETECT_HOTSPOTS_QUERY_CMD),
+ CMD(CARD_STATE_NOTIFICATION),
+ CMD(BT_COEX_PRIO_TABLE),
+ CMD(BT_COEX_PROT_ENV),
+ CMD(BT_PROFILE_NOTIFICATION),
+ CMD(BT_CONFIG),
+ CMD(MCAST_FILTER_CMD),
};
#undef CMD
@@ -312,16 +312,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
};
int err, scan_size;
- switch (cfg->device_family) {
- case IWL_DEVICE_FAMILY_6030:
- case IWL_DEVICE_FAMILY_6005:
- case IWL_DEVICE_FAMILY_7000:
- break;
- default:
- IWL_ERR(trans, "Trying to load mvm on an unsupported device\n");
- return NULL;
- }
-
/********************************
* 1. Allocating and configuring HW data
********************************/
@@ -363,8 +353,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds);
trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K;
- /* TODO: this should really be a TLV */
- if (cfg->device_family == IWL_DEVICE_FAMILY_7000)
+ if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DW_BC_TABLE)
trans_cfg.bc_table_dword = true;
if (!iwlwifi_mod_params.wd_disable)
@@ -438,7 +427,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
out_free:
iwl_phy_db_free(mvm->phy_db);
kfree(mvm->scan_cmd);
- kfree(mvm->eeprom_blob);
iwl_trans_stop_hw(trans, true);
ieee80211_free_hw(mvm->hw);
return NULL;
@@ -460,7 +448,6 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
iwl_phy_db_free(mvm->phy_db);
mvm->phy_db = NULL;
- kfree(mvm->eeprom_blob);
iwl_free_nvm_data(mvm->nvm_data);
for (i = 0; i < NVM_NUM_OF_SECTIONS; i++)
kfree(mvm->nvm_sections[i].data);
diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
index b428448f8dd..a28a1d1f23e 100644
--- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
+++ b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
@@ -142,7 +142,7 @@ static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm,
struct cfg80211_chan_def *chandef,
u8 chains_static, u8 chains_dynamic)
{
- u8 valid_rx_chains, active_cnt, idle_cnt;
+ u8 active_cnt, idle_cnt;
/* Set the channel info data */
cmd->ci.band = (chandef->chan->band == IEEE80211_BAND_2GHZ ?
@@ -153,22 +153,16 @@ static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm,
cmd->ci.ctrl_pos = iwl_mvm_get_ctrl_pos(chandef);
/* Set rx the chains */
-
- /* TODO:
- * Need to add on chain noise calibration limitations, and
- * BT coex considerations.
- */
- valid_rx_chains = mvm->nvm_data->valid_rx_ant;
idle_cnt = chains_static;
active_cnt = chains_dynamic;
- cmd->rxchain_info = cpu_to_le32(valid_rx_chains <<
+ cmd->rxchain_info = cpu_to_le32(iwl_fw_valid_rx_ant(mvm->fw) <<
PHY_RX_CHAIN_VALID_POS);
cmd->rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS);
cmd->rxchain_info |= cpu_to_le32(active_cnt <<
PHY_RX_CHAIN_MIMO_CNT_POS);
- cmd->txchain_info = cpu_to_le32(mvm->nvm_data->valid_tx_ant);
+ cmd->txchain_info = cpu_to_le32(iwl_fw_valid_tx_ant(mvm->fw));
}
/*
diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c
index 5a92a497879..ed77e437aac 100644
--- a/drivers/net/wireless/iwlwifi/mvm/power.c
+++ b/drivers/net/wireless/iwlwifi/mvm/power.c
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
@@ -75,23 +75,48 @@
#define POWER_KEEP_ALIVE_PERIOD_SEC 25
-static void iwl_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct iwl_powertable_cmd *cmd)
+static void iwl_mvm_power_log(struct iwl_mvm *mvm,
+ struct iwl_powertable_cmd *cmd)
+{
+ IWL_DEBUG_POWER(mvm,
+ "Sending power table command for power level %d, flags = 0x%X\n",
+ iwlmvm_mod_params.power_scheme,
+ le16_to_cpu(cmd->flags));
+ IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n", cmd->keep_alive_seconds);
+
+ if (cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
+ IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n",
+ le32_to_cpu(cmd->rx_data_timeout));
+ IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n",
+ le32_to_cpu(cmd->tx_data_timeout));
+ IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n",
+ cmd->lprx_rssi_threshold);
+ }
+}
+
+void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct iwl_powertable_cmd *cmd)
{
struct ieee80211_hw *hw = mvm->hw;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_channel *chan;
int dtimper, dtimper_msec;
int keep_alive;
bool radar_detect = false;
- cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
- mvmvif->color));
- cmd->action = cpu_to_le32(FW_CTXT_ACTION_MODIFY);
+ /*
+ * Regardless of power management state the driver must set
+ * keep alive period. FW will use it for sending keep alive NDPs
+ * immediately after association.
+ */
+ cmd->keep_alive_seconds = POWER_KEEP_ALIVE_PERIOD_SEC;
- if ((!vif->bss_conf.ps) ||
- (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM))
+ if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
+ return;
+
+ cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
+
+ if (!vif->bss_conf.ps)
return;
cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
@@ -110,63 +135,29 @@ static void iwl_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
/* Check skip over DTIM conditions */
if (!radar_detect && (dtimper <= 10) &&
- (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP)) {
- cmd->flags |= cpu_to_le16(POWER_FLAGS_SLEEP_OVER_DTIM_MSK);
- cmd->num_skip_dtim = 2;
- }
+ (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP))
+ cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
/* Check that keep alive period is at least 3 * DTIM */
dtimper_msec = dtimper * vif->bss_conf.beacon_int;
keep_alive = max_t(int, 3 * dtimper_msec,
- MSEC_PER_SEC * POWER_KEEP_ALIVE_PERIOD_SEC);
+ MSEC_PER_SEC * cmd->keep_alive_seconds);
keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
+ cmd->keep_alive_seconds = keep_alive;
- cmd->keep_alive_seconds = cpu_to_le16(keep_alive);
-
- if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP) {
- /* TODO: Also for D3 (device sleep / WoWLAN) */
- cmd->rx_data_timeout = cpu_to_le32(10);
- cmd->tx_data_timeout = cpu_to_le32(10);
- } else {
- cmd->rx_data_timeout = cpu_to_le32(50);
- cmd->tx_data_timeout = cpu_to_le32(50);
- }
+ cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
+ cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
}
int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
struct iwl_powertable_cmd cmd = {};
- if (!iwlwifi_mod_params.power_save) {
- IWL_DEBUG_POWER(mvm, "Power management is not allowed\n");
- return 0;
- }
-
if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
return 0;
- iwl_power_build_cmd(mvm, vif, &cmd);
-
- IWL_DEBUG_POWER(mvm,
- "Sending power table command on mac id 0x%X for power level %d, flags = 0x%X\n",
- cmd.id_and_color, iwlmvm_mod_params.power_scheme,
- le16_to_cpu(cmd.flags));
-
- if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
- IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n",
- le16_to_cpu(cmd.keep_alive_seconds));
- IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n",
- le32_to_cpu(cmd.rx_data_timeout));
- IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n",
- le32_to_cpu(cmd.tx_data_timeout));
- IWL_DEBUG_POWER(mvm, "Rx timeout (uAPSD) = %u usec\n",
- le32_to_cpu(cmd.rx_data_timeout_uapsd));
- IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n",
- le32_to_cpu(cmd.tx_data_timeout_uapsd));
- IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n",
- cmd.lprx_rssi_threshold);
- IWL_DEBUG_POWER(mvm, "DTIMs to skip = %u\n", cmd.num_skip_dtim);
- }
+ iwl_mvm_power_build_cmd(mvm, vif, &cmd);
+ iwl_mvm_power_log(mvm, &cmd);
return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
sizeof(cmd), &cmd);
@@ -175,33 +166,15 @@ int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
struct iwl_powertable_cmd cmd = {};
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- if (!iwlwifi_mod_params.power_save) {
- IWL_DEBUG_POWER(mvm, "Power management is not allowed\n");
- return 0;
- }
if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
return 0;
- cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
- mvmvif->color));
- cmd.action = cpu_to_le32(FW_CTXT_ACTION_MODIFY);
+ if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
+ cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
- IWL_DEBUG_POWER(mvm,
- "Sending power table command on mac id 0x%X for power level %d, flags = 0x%X\n",
- cmd.id_and_color, iwlmvm_mod_params.power_scheme,
- le16_to_cpu(cmd.flags));
+ iwl_mvm_power_log(mvm, &cmd);
return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_ASYNC,
sizeof(cmd), &cmd);
}
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-void iwl_power_get_params(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct iwl_powertable_cmd *cmd)
-{
- iwl_power_build_cmd(mvm, vif, cmd);
-}
-#endif /* CONFIG_IWLWIFI_DEBUGFS */
diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c
index 92562846814..a1e3e923ea3 100644
--- a/drivers/net/wireless/iwlwifi/mvm/quota.c
+++ b/drivers/net/wireless/iwlwifi/mvm/quota.c
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
@@ -114,7 +114,8 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
data->n_interfaces[id]++;
break;
case NL80211_IFTYPE_MONITOR:
- data->n_interfaces[id]++;
+ if (mvmvif->monitor_active)
+ data->n_interfaces[id]++;
break;
case NL80211_IFTYPE_P2P_DEVICE:
break;
diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c
index 56b636d9ab3..55334d542e2 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rs.c
+++ b/drivers/net/wireless/iwlwifi/mvm/rs.c
@@ -680,12 +680,14 @@ static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags,
*/
static bool rs_use_green(struct ieee80211_sta *sta)
{
- struct iwl_mvm_sta *sta_priv = (void *)sta->drv_priv;
-
- bool use_green = !(sta_priv->vif->bss_conf.ht_operation_mode &
- IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
-
- return (sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && use_green;
+ /*
+ * There's a bug somewhere in this code that causes the
+ * scaling to get stuck because GF+SGI can't be combined
+ * in SISO rates. Until we find that bug, disable GF, it
+ * has only limited benefit and we still interoperate with
+ * GF APs since we can always receive GF transmissions.
+ */
+ return false;
}
/**
@@ -791,7 +793,7 @@ static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta,
if (num_of_ant(tbl->ant_type) > 1)
tbl->ant_type =
- first_antenna(mvm->nvm_data->valid_tx_ant);
+ first_antenna(iwl_fw_valid_tx_ant(mvm->fw));
tbl->is_ht40 = 0;
tbl->is_SGI = 0;
@@ -1233,7 +1235,7 @@ static int rs_switch_to_mimo2(struct iwl_mvm *mvm,
return -1;
/* Need both Tx chains/antennas to support MIMO */
- if (num_of_ant(mvm->nvm_data->valid_tx_ant) < 2)
+ if (num_of_ant(iwl_fw_valid_tx_ant(mvm->fw)) < 2)
return -1;
IWL_DEBUG_RATE(mvm, "LQ: try to switch to MIMO2\n");
@@ -1285,7 +1287,7 @@ static int rs_switch_to_mimo3(struct iwl_mvm *mvm,
return -1;
/* Need both Tx chains/antennas to support MIMO */
- if (num_of_ant(mvm->nvm_data->valid_tx_ant) < 3)
+ if (num_of_ant(iwl_fw_valid_tx_ant(mvm->fw)) < 3)
return -1;
IWL_DEBUG_RATE(mvm, "LQ: try to switch to MIMO3\n");
@@ -1379,7 +1381,7 @@ static int rs_move_legacy_other(struct iwl_mvm *mvm,
u32 sz = (sizeof(struct iwl_scale_tbl_info) -
(sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
u8 start_action;
- u8 valid_tx_ant = mvm->nvm_data->valid_tx_ant;
+ u8 valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw);
u8 tx_chains_num = num_of_ant(valid_tx_ant);
int ret;
u8 update_search_tbl_counter = 0;
@@ -1512,7 +1514,7 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm,
u32 sz = (sizeof(struct iwl_scale_tbl_info) -
(sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
u8 start_action;
- u8 valid_tx_ant = mvm->nvm_data->valid_tx_ant;
+ u8 valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw);
u8 tx_chains_num = num_of_ant(valid_tx_ant);
u8 update_search_tbl_counter = 0;
int ret;
@@ -1647,7 +1649,7 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm,
u32 sz = (sizeof(struct iwl_scale_tbl_info) -
(sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
u8 start_action;
- u8 valid_tx_ant = mvm->nvm_data->valid_tx_ant;
+ u8 valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw);
u8 tx_chains_num = num_of_ant(valid_tx_ant);
u8 update_search_tbl_counter = 0;
int ret;
@@ -1784,7 +1786,7 @@ static int rs_move_mimo3_to_other(struct iwl_mvm *mvm,
u32 sz = (sizeof(struct iwl_scale_tbl_info) -
(sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
u8 start_action;
- u8 valid_tx_ant = mvm->nvm_data->valid_tx_ant;
+ u8 valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw);
u8 tx_chains_num = num_of_ant(valid_tx_ant);
int ret;
u8 update_search_tbl_counter = 0;
@@ -2447,7 +2449,7 @@ static void rs_initialize_lq(struct iwl_mvm *mvm,
i = lq_sta->last_txrate_idx;
- valid_tx_ant = mvm->nvm_data->valid_tx_ant;
+ valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw);
if (!lq_sta->search_better_tbl)
active_tbl = lq_sta->active_tbl;
@@ -2637,15 +2639,15 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
/* These values will be overridden later */
lq_sta->lq.single_stream_ant_msk =
- first_antenna(mvm->nvm_data->valid_tx_ant);
+ first_antenna(iwl_fw_valid_tx_ant(mvm->fw));
lq_sta->lq.dual_stream_ant_msk =
- mvm->nvm_data->valid_tx_ant &
- ~first_antenna(mvm->nvm_data->valid_tx_ant);
+ iwl_fw_valid_tx_ant(mvm->fw) &
+ ~first_antenna(iwl_fw_valid_tx_ant(mvm->fw));
if (!lq_sta->lq.dual_stream_ant_msk) {
lq_sta->lq.dual_stream_ant_msk = ANT_AB;
- } else if (num_of_ant(mvm->nvm_data->valid_tx_ant) == 2) {
+ } else if (num_of_ant(iwl_fw_valid_tx_ant(mvm->fw)) == 2) {
lq_sta->lq.dual_stream_ant_msk =
- mvm->nvm_data->valid_tx_ant;
+ iwl_fw_valid_tx_ant(mvm->fw);
}
/* as default allow aggregation for all tids */
@@ -2706,7 +2708,7 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm,
index++;
repeat_rate--;
if (mvm)
- valid_tx_ant = mvm->nvm_data->valid_tx_ant;
+ valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw);
/* Fill rest of rate table */
while (index < LINK_QUAL_MAX_RETRY_NUM) {
@@ -2811,7 +2813,7 @@ static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
u8 ant_sel_tx;
mvm = lq_sta->drv;
- valid_tx_ant = mvm->nvm_data->valid_tx_ant;
+ valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw);
if (lq_sta->dbg_fixed_rate) {
ant_sel_tx =
((lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK)
@@ -2882,9 +2884,9 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
desc += sprintf(buff+desc, "fixed rate 0x%X\n",
lq_sta->dbg_fixed_rate);
desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n",
- (mvm->nvm_data->valid_tx_ant & ANT_A) ? "ANT_A," : "",
- (mvm->nvm_data->valid_tx_ant & ANT_B) ? "ANT_B," : "",
- (mvm->nvm_data->valid_tx_ant & ANT_C) ? "ANT_C" : "");
+ (iwl_fw_valid_tx_ant(mvm->fw) & ANT_A) ? "ANT_A," : "",
+ (iwl_fw_valid_tx_ant(mvm->fw) & ANT_B) ? "ANT_B," : "",
+ (iwl_fw_valid_tx_ant(mvm->fw) & ANT_C) ? "ANT_C" : "");
desc += sprintf(buff+desc, "lq type %s\n",
(is_legacy(tbl->lq_type)) ? "legacy" : "HT");
if (is_Ht(tbl->lq_type)) {
diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c
index b0b190d0ec2..4dfc21a3e83 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rx.c
+++ b/drivers/net/wireless/iwlwifi/mvm/rx.c
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c
index 9b21b92aa8d..2476e43799d 100644
--- a/drivers/net/wireless/iwlwifi/mvm/scan.c
+++ b/drivers/net/wireless/iwlwifi/mvm/scan.c
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
@@ -74,7 +74,7 @@
static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm)
{
u16 rx_chain;
- u8 rx_ant = mvm->nvm_data->valid_rx_ant;
+ u8 rx_ant = iwl_fw_valid_rx_ant(mvm->fw);
rx_chain = rx_ant << PHY_RX_CHAIN_VALID_POS;
rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS;
@@ -115,7 +115,7 @@ iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum ieee80211_band band,
u32 tx_ant;
mvm->scan_last_antenna_idx =
- iwl_mvm_next_antenna(mvm, mvm->nvm_data->valid_tx_ant,
+ iwl_mvm_next_antenna(mvm, iwl_fw_valid_tx_ant(mvm->fw),
mvm->scan_last_antenna_idx);
tx_ant = BIT(mvm->scan_last_antenna_idx) << RATE_MCS_ANT_POS;
@@ -298,6 +298,12 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
else
cmd->type = cpu_to_le32(SCAN_TYPE_FORCED);
+ /*
+ * TODO: This is a WA due to a bug in the FW AUX framework that does not
+ * properly handle time events that fail to be scheduled
+ */
+ cmd->type = cpu_to_le32(SCAN_TYPE_FORCED);
+
cmd->repeats = cpu_to_le32(1);
/*
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c
index 274f44e2ef6..5c664ed5440 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.c
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.c
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
@@ -101,8 +101,55 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
}
add_sta_cmd.add_modify = update ? 1 : 0;
- /* STA_FLG_FAT_EN_MSK ? */
- /* STA_FLG_MIMO_EN_MSK ? */
+ add_sta_cmd.station_flags_msk |= cpu_to_le32(STA_FLG_FAT_EN_MSK |
+ STA_FLG_MIMO_EN_MSK);
+
+ switch (sta->bandwidth) {
+ case IEEE80211_STA_RX_BW_160:
+ add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_160MHZ);
+ /* fall through */
+ case IEEE80211_STA_RX_BW_80:
+ add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_80MHZ);
+ /* fall through */
+ case IEEE80211_STA_RX_BW_40:
+ add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_40MHZ);
+ /* fall through */
+ case IEEE80211_STA_RX_BW_20:
+ if (sta->ht_cap.ht_supported)
+ add_sta_cmd.station_flags |=
+ cpu_to_le32(STA_FLG_FAT_EN_20MHZ);
+ break;
+ }
+
+ switch (sta->rx_nss) {
+ case 1:
+ add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_SISO);
+ break;
+ case 2:
+ add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_MIMO2);
+ break;
+ case 3 ... 8:
+ add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_MIMO3);
+ break;
+ }
+
+ switch (sta->smps_mode) {
+ case IEEE80211_SMPS_AUTOMATIC:
+ case IEEE80211_SMPS_NUM_MODES:
+ WARN_ON(1);
+ break;
+ case IEEE80211_SMPS_STATIC:
+ /* override NSS */
+ add_sta_cmd.station_flags &= ~cpu_to_le32(STA_FLG_MIMO_EN_MSK);
+ add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_SISO);
+ break;
+ case IEEE80211_SMPS_DYNAMIC:
+ add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_RTS_MIMO_PROT);
+ break;
+ case IEEE80211_SMPS_OFF:
+ /* nothing */
+ break;
+ }
if (sta->ht_cap.ht_supported) {
add_sta_cmd.station_flags_msk |=
@@ -172,7 +219,7 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
mvm_sta->max_agg_bufsize = LINK_QUAL_AGG_FRAME_LIMIT_DEF;
/* HW restart, don't assume the memory has been zeroed */
- atomic_set(&mvm_sta->pending_frames, 0);
+ atomic_set(&mvm->pending_frames[sta_id], 0);
mvm_sta->tid_disable_agg = 0;
mvm_sta->tfd_queue_msk = 0;
for (i = 0; i < IEEE80211_NUM_ACS; i++)
@@ -340,6 +387,9 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
if (vif->type == NL80211_IFTYPE_STATION &&
mvmvif->ap_sta_id == mvm_sta->sta_id) {
+ /* flush its queues here since we are freeing mvm_sta */
+ ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, true);
+
/*
* Put a non-NULL since the fw station isn't removed.
* It will be removed after the MAC will be set as
@@ -348,9 +398,6 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id],
ERR_PTR(-EINVAL));
- /* flush its queues here since we are freeing mvm_sta */
- ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, true);
-
/* if we are associated - we can't remove the AP STA now */
if (vif->bss_conf.assoc)
return ret;
@@ -360,14 +407,21 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
}
/*
+ * Make sure that the tx response code sees the station as -EBUSY and
+ * calls the drain worker.
+ */
+ spin_lock_bh(&mvm_sta->lock);
+ /*
* There are frames pending on the AC queues for this station.
* We need to wait until all the frames are drained...
*/
- if (atomic_read(&mvm_sta->pending_frames)) {
- ret = iwl_mvm_drain_sta(mvm, mvm_sta, true);
+ if (atomic_read(&mvm->pending_frames[mvm_sta->sta_id])) {
rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id],
ERR_PTR(-EBUSY));
+ spin_unlock_bh(&mvm_sta->lock);
+ ret = iwl_mvm_drain_sta(mvm, mvm_sta, true);
} else {
+ spin_unlock_bh(&mvm_sta->lock);
ret = iwl_mvm_rm_sta_common(mvm, mvm_sta->sta_id);
rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id], NULL);
}
@@ -686,7 +740,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
spin_lock_bh(&mvmsta->lock);
tid_data = &mvmsta->tid_data[tid];
- tid_data->ssn = SEQ_TO_SN(tid_data->seq_number);
+ tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
tid_data->txq_id = txq_id;
*ssn = tid_data->ssn;
@@ -789,7 +843,7 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
switch (tid_data->state) {
case IWL_AGG_ON:
- tid_data->ssn = SEQ_TO_SN(tid_data->seq_number);
+ tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
IWL_DEBUG_TX_QUEUES(mvm,
"ssn = %d, next_recl = %d\n",
@@ -834,6 +888,34 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
return err;
}
+int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u16 tid)
+{
+ struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
+ struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
+ u16 txq_id;
+
+ /*
+ * First set the agg state to OFF to avoid calling
+ * ieee80211_stop_tx_ba_cb in iwl_mvm_check_ratid_empty.
+ */
+ spin_lock_bh(&mvmsta->lock);
+ txq_id = tid_data->txq_id;
+ IWL_DEBUG_TX_QUEUES(mvm, "Flush AGG: sta %d tid %d q %d state %d\n",
+ mvmsta->sta_id, tid, txq_id, tid_data->state);
+ tid_data->state = IWL_AGG_OFF;
+ spin_unlock_bh(&mvmsta->lock);
+
+ if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), true))
+ IWL_ERR(mvm, "Couldn't flush the AGG queue\n");
+
+ iwl_trans_txq_disable(mvm->trans, tid_data->txq_id);
+ mvm->queue_to_mac80211[tid_data->txq_id] =
+ IWL_INVALID_MAC80211_QUEUE;
+
+ return 0;
+}
+
static int iwl_mvm_set_fw_key_idx(struct iwl_mvm *mvm)
{
int i;
@@ -870,7 +952,7 @@ static u8 iwl_mvm_get_key_sta_id(struct ieee80211_vif *vif,
mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT)
return mvmvif->ap_sta_id;
- return IWL_INVALID_STATION;
+ return IWL_MVM_STATION_COUNT;
}
static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
@@ -1018,7 +1100,7 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
/* Get the station id from the mvm local station table */
sta_id = iwl_mvm_get_key_sta_id(vif, sta);
- if (sta_id == IWL_INVALID_STATION) {
+ if (sta_id == IWL_MVM_STATION_COUNT) {
IWL_ERR(mvm, "Failed to find station id\n");
return -EINVAL;
}
@@ -1113,7 +1195,7 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
return -ENOENT;
}
- if (sta_id == IWL_INVALID_STATION) {
+ if (sta_id == IWL_MVM_STATION_COUNT) {
IWL_DEBUG_WEP(mvm, "station non-existent, early return.\n");
return 0;
}
@@ -1179,7 +1261,7 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,
struct iwl_mvm_sta *mvm_sta;
u8 sta_id = iwl_mvm_get_key_sta_id(vif, sta);
- if (WARN_ON_ONCE(sta_id == IWL_INVALID_STATION))
+ if (WARN_ON_ONCE(sta_id == IWL_MVM_STATION_COUNT))
return;
rcu_read_lock();
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h
index 896f88ac814..a4ddce77aaa 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.h
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.h
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
@@ -271,9 +271,9 @@ struct iwl_mvm_tid_data {
* @tid_disable_agg: bitmap: if bit(tid) is set, the fw won't send ampdus for
* tid.
* @max_agg_bufsize: the maximal size of the AGG buffer for this station
+ * @bt_reduced_txpower: is reduced tx power enabled for this station
* @lock: lock to protect the whole struct. Since %tid_data is access from Tx
* and from Tx response flow, it needs a spinlock.
- * @pending_frames: number of frames for this STA on the shared Tx queues.
* @tid_data: per tid data. Look at %iwl_mvm_tid_data.
*
* When mac80211 creates a station it reserves some space (hw->sta_data_size)
@@ -287,8 +287,8 @@ struct iwl_mvm_sta {
u32 mac_id_n_color;
u16 tid_disable_agg;
u8 max_agg_bufsize;
+ bool bt_reduced_txpower;
spinlock_t lock;
- atomic_t pending_frames;
struct iwl_mvm_tid_data tid_data[IWL_MAX_TID_COUNT];
struct iwl_lq_sta lq_sta;
struct ieee80211_vif *vif;
@@ -348,6 +348,8 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, u16 tid, u8 buf_size);
int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, u16 tid);
+int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u16 tid);
int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm);
int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta,
diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c
index e437e02c714..ad9bbca9921 100644
--- a/drivers/net/wireless/iwlwifi/mvm/time-event.c
+++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
@@ -76,14 +76,12 @@
#define TU_TO_JIFFIES(_tu) (usecs_to_jiffies((_tu) * 1024))
#define MSEC_TO_TU(_msec) (_msec*1000/1024)
-/* For ROC use a TE type which has priority high enough to be scheduled when
- * there is a concurrent BSS or GO/AP. Currently, use a TE type that has
- * priority similar to the TE priority used for action scans by the FW.
- * TODO: This needs to be changed, based on the reason for the ROC, i.e., use
- * TE_P2P_DEVICE_DISCOVERABLE for remain on channel without mgmt skb, and use
- * TE_P2P_DEVICE_ACTION_SCAN
+/*
+ * For the high priority TE use a time event type that has similar priority to
+ * the FW's action scan priority.
*/
-#define IWL_MVM_ROC_TE_TYPE TE_P2P_DEVICE_ACTION_SCAN
+#define IWL_MVM_ROC_TE_TYPE_NORMAL TE_P2P_DEVICE_DISCOVERABLE
+#define IWL_MVM_ROC_TE_TYPE_MGMT_TX TE_P2P_CLIENT_ASSOC
void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,
struct iwl_mvm_time_event_data *te_data)
@@ -116,7 +114,7 @@ void iwl_mvm_roc_done_wk(struct work_struct *wk)
* issue as it will have to complete before the next command is
* executed, and a new time event means a new command.
*/
- iwl_mvm_flush_tx_path(mvm, BIT(IWL_OFFCHANNEL_QUEUE), false);
+ iwl_mvm_flush_tx_path(mvm, BIT(IWL_MVM_OFFCHANNEL_QUEUE), false);
}
static void iwl_mvm_roc_finished(struct iwl_mvm *mvm)
@@ -168,7 +166,7 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
WARN_ONCE(!le32_to_cpu(notif->status),
"Failed to schedule time event\n");
- if (le32_to_cpu(notif->action) == TE_NOTIF_HOST_END) {
+ if (le32_to_cpu(notif->action) & TE_NOTIF_HOST_EVENT_END) {
IWL_DEBUG_TE(mvm,
"TE ended - current time %lu, estimated end %lu\n",
jiffies, te_data->end_jiffies);
@@ -191,7 +189,7 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
}
iwl_mvm_te_clear_data(mvm, te_data);
- } else if (le32_to_cpu(notif->action) == TE_NOTIF_HOST_START) {
+ } else if (le32_to_cpu(notif->action) & TE_NOTIF_HOST_EVENT_START) {
te_data->running = true;
te_data->end_jiffies = jiffies +
TU_TO_JIFFIES(te_data->duration);
@@ -370,7 +368,8 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm,
time_cmd.interval_reciprocal = cpu_to_le32(iwl_mvm_reciprocal(1));
time_cmd.duration = cpu_to_le32(duration);
time_cmd.repeat = cpu_to_le32(1);
- time_cmd.notify = cpu_to_le32(TE_NOTIF_HOST_START | TE_NOTIF_HOST_END);
+ time_cmd.notify = cpu_to_le32(TE_NOTIF_HOST_EVENT_START |
+ TE_NOTIF_HOST_EVENT_END);
iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
}
@@ -438,7 +437,7 @@ void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm,
}
int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- int duration)
+ int duration, enum ieee80211_roc_type type)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
@@ -459,27 +458,36 @@ int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
time_cmd.id_and_color =
cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
- time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE);
+
+ switch (type) {
+ case IEEE80211_ROC_TYPE_NORMAL:
+ time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_NORMAL);
+ break;
+ case IEEE80211_ROC_TYPE_MGMT_TX:
+ time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_MGMT_TX);
+ break;
+ default:
+ WARN_ONCE(1, "Got an invalid ROC type\n");
+ return -EINVAL;
+ }
time_cmd.apply_time = cpu_to_le32(0);
time_cmd.dep_policy = cpu_to_le32(TE_INDEPENDENT);
time_cmd.is_present = cpu_to_le32(1);
-
time_cmd.interval = cpu_to_le32(1);
/*
- * IWL_MVM_ROC_TE_TYPE can have lower priority than other events
+ * The P2P Device TEs can have lower priority than other events
* that are being scheduled by the driver/fw, and thus it might not be
- * scheduled. To improve the chances of it being scheduled, allow it to
- * be fragmented.
- * In addition, for the same reasons, allow to delay the scheduling of
- * the time event.
+ * scheduled. To improve the chances of it being scheduled, allow them
+ * to be fragmented, and in addition allow them to be delayed.
*/
time_cmd.max_frags = cpu_to_le32(MSEC_TO_TU(duration)/20);
time_cmd.max_delay = cpu_to_le32(MSEC_TO_TU(duration/2));
time_cmd.duration = cpu_to_le32(MSEC_TO_TU(duration));
time_cmd.repeat = cpu_to_le32(1);
- time_cmd.notify = cpu_to_le32(TE_NOTIF_HOST_START | TE_NOTIF_HOST_END);
+ time_cmd.notify = cpu_to_le32(TE_NOTIF_HOST_EVENT_START |
+ TE_NOTIF_HOST_EVENT_END);
return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
}
diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.h b/drivers/net/wireless/iwlwifi/mvm/time-event.h
index 64fb57a5ab4..f86c51065ed 100644
--- a/drivers/net/wireless/iwlwifi/mvm/time-event.h
+++ b/drivers/net/wireless/iwlwifi/mvm/time-event.h
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
@@ -162,6 +162,7 @@ int iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm,
* that the vif type is NL80211_IFTYPE_P2P_DEVICE
* @duration: the requested duration in millisecond for the fw to be on the
* channel that is bound to the vif.
+ * @type: the remain on channel request type
*
* This function can be used to issue a remain on channel session,
* which means that the fw will stay in the channel for the request %duration
@@ -172,7 +173,7 @@ int iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm,
* another notification to the driver.
*/
int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- int duration);
+ int duration, enum ieee80211_roc_type type);
/**
* iwl_mvm_stop_p2p_roc - stop remain on channel for p2p device functionlity
diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c
index 6645efe5c03..f212f16502f 100644
--- a/drivers/net/wireless/iwlwifi/mvm/tx.c
+++ b/drivers/net/wireless/iwlwifi/mvm/tx.c
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
@@ -205,7 +205,7 @@ static void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm,
rate_plcp = iwl_mvm_mac80211_idx_to_hwrate(rate_idx);
mvm->mgmt_last_antenna_idx =
- iwl_mvm_next_antenna(mvm, mvm->nvm_data->valid_tx_ant,
+ iwl_mvm_next_antenna(mvm, iwl_fw_valid_tx_ant(mvm->fw),
mvm->mgmt_last_antenna_idx);
rate_flags = BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS;
@@ -365,7 +365,7 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
if (WARN_ON_ONCE(!mvmsta))
return -1;
- if (WARN_ON_ONCE(mvmsta->sta_id == IWL_INVALID_STATION))
+ if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_STATION_COUNT))
return -1;
dev_cmd = iwl_mvm_set_tx_params(mvm, skb, sta, mvmsta->sta_id);
@@ -416,9 +416,8 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
spin_unlock(&mvmsta->lock);
- if (mvmsta->vif->type == NL80211_IFTYPE_AP &&
- txq_id < IWL_FIRST_AMPDU_QUEUE)
- atomic_inc(&mvmsta->pending_frames);
+ if (txq_id < IWL_MVM_FIRST_AGG_QUEUE)
+ atomic_inc(&mvm->pending_frames[mvmsta->sta_id]);
return 0;
@@ -606,7 +605,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
info);
/* Single frame failure in an AMPDU queue => send BAR */
- if (txq_id >= IWL_FIRST_AMPDU_QUEUE &&
+ if (txq_id >= IWL_MVM_FIRST_AGG_QUEUE &&
!(info->flags & IEEE80211_TX_STAT_ACK))
info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
@@ -619,7 +618,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
ieee80211_tx_status_ni(mvm->hw, skb);
}
- if (txq_id >= IWL_FIRST_AMPDU_QUEUE) {
+ if (txq_id >= IWL_MVM_FIRST_AGG_QUEUE) {
/* If this is an aggregation queue, we use the ssn since:
* ssn = wifi seq_num % 256.
* The seq_ctl is the sequence control of the packet to which
@@ -637,14 +636,16 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
next_reclaimed = ssn;
} else {
/* The next packet to be reclaimed is the one after this one */
- next_reclaimed = SEQ_TO_SN(seq_ctl + 0x10);
+ next_reclaimed = IEEE80211_SEQ_TO_SN(seq_ctl + 0x10);
}
IWL_DEBUG_TX_REPLY(mvm,
- "TXQ %d status %s (0x%08x)\n\t\t\t\tinitial_rate 0x%x "
- "retries %d, idx=%d ssn=%d next_reclaimed=0x%x seq_ctl=0x%x\n",
- txq_id, iwl_mvm_get_tx_fail_reason(status),
- status, le32_to_cpu(tx_resp->initial_rate),
+ "TXQ %d status %s (0x%08x)\n",
+ txq_id, iwl_mvm_get_tx_fail_reason(status), status);
+
+ IWL_DEBUG_TX_REPLY(mvm,
+ "\t\t\t\tinitial_rate 0x%x retries %d, idx=%d ssn=%d next_reclaimed=0x%x seq_ctl=0x%x\n",
+ le32_to_cpu(tx_resp->initial_rate),
tx_resp->failure_frame, SEQ_TO_INDEX(sequence),
ssn, next_reclaimed, seq_ctl);
@@ -678,16 +679,41 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
/*
* If the txq is not an AMPDU queue, there is no chance we freed
* several skbs. Check that out...
- * If there are no pending frames for this STA, notify mac80211 that
- * this station can go to sleep in its STA table.
*/
- if (txq_id < IWL_FIRST_AMPDU_QUEUE && mvmsta &&
- !WARN_ON(skb_freed > 1) &&
- mvmsta->vif->type == NL80211_IFTYPE_AP &&
- atomic_sub_and_test(skb_freed, &mvmsta->pending_frames)) {
- ieee80211_sta_block_awake(mvm->hw, sta, false);
- set_bit(sta_id, mvm->sta_drained);
- schedule_work(&mvm->sta_drained_wk);
+ if (txq_id < IWL_MVM_FIRST_AGG_QUEUE && !WARN_ON(skb_freed > 1) &&
+ atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id])) {
+ if (mvmsta) {
+ /*
+ * If there are no pending frames for this STA, notify
+ * mac80211 that this station can go to sleep in its
+ * STA table.
+ */
+ if (mvmsta->vif->type == NL80211_IFTYPE_AP)
+ ieee80211_sta_block_awake(mvm->hw, sta, false);
+ /*
+ * We might very well have taken mvmsta pointer while
+ * the station was being removed. The remove flow might
+ * have seen a pending_frame (because we didn't take
+ * the lock) even if now the queues are drained. So make
+ * really sure now that this the station is not being
+ * removed. If it is, run the drain worker to remove it.
+ */
+ spin_lock_bh(&mvmsta->lock);
+ sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+ if (IS_ERR_OR_NULL(sta)) {
+ /*
+ * Station disappeared in the meantime:
+ * so we are draining.
+ */
+ set_bit(sta_id, mvm->sta_drained);
+ schedule_work(&mvm->sta_drained_wk);
+ }
+ spin_unlock_bh(&mvmsta->lock);
+ } else if (!mvmsta) {
+ /* Tx response without STA, so we are draining */
+ set_bit(sta_id, mvm->sta_drained);
+ schedule_work(&mvm->sta_drained_wk);
+ }
}
rcu_read_unlock();
@@ -750,7 +776,7 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm,
u16 sequence = le16_to_cpu(pkt->hdr.sequence);
struct ieee80211_sta *sta;
- if (WARN_ON_ONCE(SEQ_TO_QUEUE(sequence) < IWL_FIRST_AMPDU_QUEUE))
+ if (WARN_ON_ONCE(SEQ_TO_QUEUE(sequence) < IWL_MVM_FIRST_AGG_QUEUE))
return;
if (WARN_ON_ONCE(tid == IWL_TID_NON_QOS))
diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c
index 000e842c2ed..687b34e387a 100644
--- a/drivers/net/wireless/iwlwifi/mvm/utils.c
+++ b/drivers/net/wireless/iwlwifi/mvm/utils.c
@@ -22,7 +22,7 @@
* USA
*
* The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
@@ -253,8 +253,9 @@ int iwl_mvm_rx_fw_error(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
u8 first_antenna(u8 mask)
{
BUILD_BUG_ON(ANT_A != BIT(0)); /* using ffs is wrong if not */
- WARN_ON_ONCE(!mask); /* ffs will return 0 if mask is zeroed */
- return (u8)(BIT(ffs(mask)));
+ if (WARN_ON_ONCE(!mask)) /* ffs will return 0 if mask is zeroed */
+ return BIT(0);
+ return BIT(ffs(mask) - 1);
}
/*
@@ -462,7 +463,7 @@ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
.data = { lq, },
};
- if (WARN_ON(lq->sta_id == IWL_INVALID_STATION))
+ if (WARN_ON(lq->sta_id == IWL_MVM_STATION_COUNT))
return -EINVAL;
if (WARN_ON(init && (cmd.flags & CMD_ASYNC)))