diff options
author | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2016-03-25 03:53:42 -0300 |
---|---|---|
committer | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2016-03-25 03:53:42 -0300 |
commit | 03dd4cb26d967f9588437b0fc9cc0e8353322bb7 (patch) | |
tree | fa581f6dc1c0596391690d1f67eceef3af8246dc /drivers/net/wireless/iwlwifi/mvm | |
parent | d4e493caf788ef44982e131ff9c786546904d934 (diff) |
Linux-libre 4.5-gnu
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm')
48 files changed, 0 insertions, 41367 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile deleted file mode 100644 index 8c2c3d13b..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -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 sf.o -iwlmvm-y += scan.o time-event.o rs.o -iwlmvm-y += power.o coex.o coex_legacy.o -iwlmvm-y += tt.o offloading.o tdls.o -iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o -iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o -iwlmvm-y += tof.o -iwlmvm-$(CONFIG_PM_SLEEP) += d3.o - -ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../ diff --git a/drivers/net/wireless/iwlwifi/mvm/binding.c b/drivers/net/wireless/iwlwifi/mvm/binding.c deleted file mode 100644 index a1376539d..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/binding.c +++ /dev/null @@ -1,211 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 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) 2012 - 2014 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.h" -#include "mvm.h" - -struct iwl_mvm_iface_iterator_data { - struct ieee80211_vif *ignore_vif; - int idx; - - struct iwl_mvm_phy_ctxt *phyctxt; - - u16 ids[MAX_MACS_IN_BINDING]; - u16 colors[MAX_MACS_IN_BINDING]; -}; - -static int iwl_mvm_binding_cmd(struct iwl_mvm *mvm, u32 action, - struct iwl_mvm_iface_iterator_data *data) -{ - struct iwl_binding_cmd cmd; - struct iwl_mvm_phy_ctxt *phyctxt = data->phyctxt; - int i, ret; - u32 status; - - memset(&cmd, 0, sizeof(cmd)); - - cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(phyctxt->id, - phyctxt->color)); - cmd.action = cpu_to_le32(action); - cmd.phy = cpu_to_le32(FW_CMD_ID_AND_COLOR(phyctxt->id, - phyctxt->color)); - - for (i = 0; i < MAX_MACS_IN_BINDING; i++) - cmd.macs[i] = cpu_to_le32(FW_CTXT_INVALID); - for (i = 0; i < data->idx; i++) - cmd.macs[i] = cpu_to_le32(FW_CMD_ID_AND_COLOR(data->ids[i], - data->colors[i])); - - status = 0; - ret = iwl_mvm_send_cmd_pdu_status(mvm, BINDING_CONTEXT_CMD, - sizeof(cmd), &cmd, &status); - if (ret) { - IWL_ERR(mvm, "Failed to send binding (action:%d): %d\n", - action, ret); - return ret; - } - - if (status) { - IWL_ERR(mvm, "Binding command failed: %u\n", status); - ret = -EIO; - } - - return ret; -} - -static void iwl_mvm_iface_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_iface_iterator_data *data = _data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (vif == data->ignore_vif) - return; - - if (mvmvif->phy_ctxt != data->phyctxt) - return; - - if (WARN_ON_ONCE(data->idx >= MAX_MACS_IN_BINDING)) - return; - - data->ids[data->idx] = mvmvif->id; - data->colors[data->idx] = mvmvif->color; - data->idx++; -} - -static int iwl_mvm_binding_update(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_mvm_phy_ctxt *phyctxt, - bool add) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_iface_iterator_data data = { - .ignore_vif = vif, - .phyctxt = phyctxt, - }; - u32 action = FW_CTXT_ACTION_MODIFY; - - lockdep_assert_held(&mvm->mutex); - - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_iface_iterator, - &data); - - /* - * If there are no other interfaces yet we - * need to create a new binding. - */ - if (data.idx == 0) { - if (add) - action = FW_CTXT_ACTION_ADD; - else - action = FW_CTXT_ACTION_REMOVE; - } - - if (add) { - if (WARN_ON_ONCE(data.idx >= MAX_MACS_IN_BINDING)) - return -EINVAL; - - data.ids[data.idx] = mvmvif->id; - data.colors[data.idx] = mvmvif->color; - data.idx++; - } - - return iwl_mvm_binding_cmd(mvm, action, &data); -} - -int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) - return -EINVAL; - - /* - * Update SF - Disable if needed. if this fails, SF might still be on - * while many macs are bound, which is forbidden - so fail the binding. - */ - if (iwl_mvm_sf_update(mvm, vif, false)) - return -EINVAL; - - return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, true); -} - -int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; - - if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) - return -EINVAL; - - ret = iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false); - - if (!ret) - if (iwl_mvm_sf_update(mvm, vif, true)) - IWL_ERR(mvm, "Failed to update SF state\n"); - - return ret; -} diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c deleted file mode 100644 index e290ac67d..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/coex.c +++ /dev/null @@ -1,1005 +0,0 @@ -/****************************************************************************** - * - * 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 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * 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 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * 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 <linux/ieee80211.h> -#include <linux/etherdevice.h> -#include <net/mac80211.h> - -#include "fw-api-coex.h" -#include "iwl-modparams.h" -#include "mvm.h" -#include "iwl-debug.h" - -/* 20MHz / 40MHz below / 40Mhz above*/ -static const __le64 iwl_ci_mask[][3] = { - /* dummy entry for channel 0 */ - {cpu_to_le64(0), cpu_to_le64(0), cpu_to_le64(0)}, - { - cpu_to_le64(0x0000001FFFULL), - cpu_to_le64(0x0ULL), - cpu_to_le64(0x00007FFFFFULL), - }, - { - cpu_to_le64(0x000000FFFFULL), - cpu_to_le64(0x0ULL), - cpu_to_le64(0x0003FFFFFFULL), - }, - { - cpu_to_le64(0x000003FFFCULL), - cpu_to_le64(0x0ULL), - cpu_to_le64(0x000FFFFFFCULL), - }, - { - cpu_to_le64(0x00001FFFE0ULL), - cpu_to_le64(0x0ULL), - cpu_to_le64(0x007FFFFFE0ULL), - }, - { - cpu_to_le64(0x00007FFF80ULL), - cpu_to_le64(0x00007FFFFFULL), - cpu_to_le64(0x01FFFFFF80ULL), - }, - { - cpu_to_le64(0x0003FFFC00ULL), - cpu_to_le64(0x0003FFFFFFULL), - cpu_to_le64(0x0FFFFFFC00ULL), - }, - { - cpu_to_le64(0x000FFFF000ULL), - cpu_to_le64(0x000FFFFFFCULL), - cpu_to_le64(0x3FFFFFF000ULL), - }, - { - cpu_to_le64(0x007FFF8000ULL), - cpu_to_le64(0x007FFFFFE0ULL), - cpu_to_le64(0xFFFFFF8000ULL), - }, - { - cpu_to_le64(0x01FFFE0000ULL), - cpu_to_le64(0x01FFFFFF80ULL), - cpu_to_le64(0xFFFFFE0000ULL), - }, - { - cpu_to_le64(0x0FFFF00000ULL), - cpu_to_le64(0x0FFFFFFC00ULL), - cpu_to_le64(0x0ULL), - }, - { - cpu_to_le64(0x3FFFC00000ULL), - cpu_to_le64(0x3FFFFFF000ULL), - cpu_to_le64(0x0) - }, - { - cpu_to_le64(0xFFFE000000ULL), - cpu_to_le64(0xFFFFFF8000ULL), - cpu_to_le64(0x0) - }, - { - cpu_to_le64(0xFFF8000000ULL), - cpu_to_le64(0xFFFFFE0000ULL), - cpu_to_le64(0x0) - }, - { - cpu_to_le64(0xFFC0000000ULL), - cpu_to_le64(0x0ULL), - cpu_to_le64(0x0ULL) - }, -}; - -struct corunning_block_luts { - u8 range; - __le32 lut20[BT_COEX_CORUN_LUT_SIZE]; -}; - -/* - * Ranges for the antenna coupling calibration / co-running block LUT: - * LUT0: [ 0, 12[ - * LUT1: [12, 20[ - * LUT2: [20, 21[ - * LUT3: [21, 23[ - * LUT4: [23, 27[ - * LUT5: [27, 30[ - * LUT6: [30, 32[ - * LUT7: [32, 33[ - * LUT8: [33, - [ - */ -static const struct corunning_block_luts antenna_coupling_ranges[] = { - { - .range = 0, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 12, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 20, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 21, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 23, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 27, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 30, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 32, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 33, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, -}; - -static enum iwl_bt_coex_lut_type -iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif) -{ - struct ieee80211_chanctx_conf *chanctx_conf; - enum iwl_bt_coex_lut_type ret; - u16 phy_ctx_id; - u32 primary_ch_phy_id, secondary_ch_phy_id; - - /* - * Checking that we hold mvm->mutex is a good idea, but the rate - * control can't acquire the mutex since it runs in Tx path. - * So this is racy in that case, but in the worst case, the AMPDU - * size limit will be wrong for a short time which is not a big - * issue. - */ - - rcu_read_lock(); - - chanctx_conf = rcu_dereference(vif->chanctx_conf); - - if (!chanctx_conf || - chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) { - rcu_read_unlock(); - return BT_COEX_INVALID_LUT; - } - - ret = BT_COEX_TX_DIS_LUT; - - if (mvm->cfg->bt_shared_single_ant) { - rcu_read_unlock(); - return ret; - } - - phy_ctx_id = *((u16 *)chanctx_conf->drv_priv); - primary_ch_phy_id = le32_to_cpu(mvm->last_bt_ci_cmd.primary_ch_phy_id); - secondary_ch_phy_id = - le32_to_cpu(mvm->last_bt_ci_cmd.secondary_ch_phy_id); - - if (primary_ch_phy_id == phy_ctx_id) - ret = le32_to_cpu(mvm->last_bt_notif.primary_ch_lut); - else if (secondary_ch_phy_id == phy_ctx_id) - ret = le32_to_cpu(mvm->last_bt_notif.secondary_ch_lut); - /* else - default = TX TX disallowed */ - - rcu_read_unlock(); - - return ret; -} - -int iwl_send_bt_init_conf(struct iwl_mvm *mvm) -{ - struct iwl_bt_coex_cmd bt_cmd = {}; - u32 mode; - - if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT)) - return iwl_send_bt_init_conf_old(mvm); - - lockdep_assert_held(&mvm->mutex); - - if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) { - switch (mvm->bt_force_ant_mode) { - case BT_FORCE_ANT_BT: - mode = BT_COEX_BT; - break; - case BT_FORCE_ANT_WIFI: - mode = BT_COEX_WIFI; - break; - default: - WARN_ON(1); - mode = 0; - } - - bt_cmd.mode = cpu_to_le32(mode); - goto send_cmd; - } - - mode = iwlwifi_mod_params.bt_coex_active ? BT_COEX_NW : BT_COEX_DISABLE; - bt_cmd.mode = cpu_to_le32(mode); - - if (IWL_MVM_BT_COEX_SYNC2SCO) - bt_cmd.enabled_modules |= - cpu_to_le32(BT_COEX_SYNC2SCO_ENABLED); - - if (iwl_mvm_bt_is_plcr_supported(mvm)) - bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_CORUN_ENABLED); - - if (IWL_MVM_BT_COEX_MPLUT) { - bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_MPLUT_ENABLED); - bt_cmd.enabled_modules |= - cpu_to_le32(BT_COEX_MPLUT_BOOST_ENABLED); - } - - bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_HIGH_BAND_RET); - -send_cmd: - memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif)); - memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd)); - - return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, 0, sizeof(bt_cmd), &bt_cmd); -} - -static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, - bool enable) -{ - struct iwl_bt_coex_reduced_txp_update_cmd cmd = {}; - struct iwl_mvm_sta *mvmsta; - u32 value; - int ret; - - mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id); - if (!mvmsta) - return 0; - - /* nothing to do */ - if (mvmsta->bt_reduced_txpower == enable) - return 0; - - value = mvmsta->sta_id; - - if (enable) - value |= BT_REDUCED_TX_POWER_BIT; - - IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n", - enable ? "en" : "dis", sta_id); - - cmd.reduced_txp = cpu_to_le32(value); - mvmsta->bt_reduced_txpower = enable; - - ret = iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_REDUCED_TXP, CMD_ASYNC, - sizeof(cmd), &cmd); - - return ret; -} - -struct iwl_bt_iterator_data { - struct iwl_bt_coex_profile_notif *notif; - struct iwl_mvm *mvm; - struct ieee80211_chanctx_conf *primary; - struct ieee80211_chanctx_conf *secondary; - bool primary_ll; -}; - -static inline -void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool enable, int rssi) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - mvmvif->bf_data.last_bt_coex_event = rssi; - mvmvif->bf_data.bt_coex_max_thold = - enable ? -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH : 0; - mvmvif->bf_data.bt_coex_min_thold = - enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0; -} - -/* must be called under rcu_read_lock */ -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; - /* default smps_mode is AUTOMATIC - only used for client modes */ - enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC; - u32 bt_activity_grading; - int ave_rssi; - - lockdep_assert_held(&mvm->mutex); - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - break; - case NL80211_IFTYPE_AP: - if (!mvmvif->ap_ibss_active) - return; - break; - default: - return; - } - - chanctx_conf = rcu_dereference(vif->chanctx_conf); - - /* If channel context is invalid or not on 2.4GHz .. */ - if ((!chanctx_conf || - chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ)) { - if (vif->type == NL80211_IFTYPE_STATION) { - /* ... relax constraints and disable rssi events */ - iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, - smps_mode); - iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, - false); - iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); - } - return; - } - - bt_activity_grading = le32_to_cpu(data->notif->bt_activity_grading); - if (bt_activity_grading >= BT_HIGH_TRAFFIC) - smps_mode = IEEE80211_SMPS_STATIC; - else if (bt_activity_grading >= BT_LOW_TRAFFIC) - smps_mode = IEEE80211_SMPS_DYNAMIC; - - /* relax SMPS constraints for next association */ - if (!vif->bss_conf.assoc) - smps_mode = IEEE80211_SMPS_AUTOMATIC; - - if (mvmvif->phy_ctxt && - IWL_COEX_IS_RRC_ON(mvm->last_bt_notif.ttc_rrc_status, - mvmvif->phy_ctxt->id)) - smps_mode = IEEE80211_SMPS_AUTOMATIC; - - IWL_DEBUG_COEX(data->mvm, - "mac %d: bt_activity_grading %d smps_req %d\n", - mvmvif->id, bt_activity_grading, smps_mode); - - if (vif->type == NL80211_IFTYPE_STATION) - iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, - smps_mode); - - /* low latency is always primary */ - if (iwl_mvm_vif_low_latency(mvmvif)) { - data->primary_ll = true; - - data->secondary = data->primary; - data->primary = chanctx_conf; - } - - if (vif->type == NL80211_IFTYPE_AP) { - if (!mvmvif->ap_ibss_active) - return; - - if (chanctx_conf == data->primary) - return; - - if (!data->primary_ll) { - /* - * downgrade the current primary no matter what its - * type is. - */ - data->secondary = data->primary; - data->primary = chanctx_conf; - } else { - /* there is low latency vif - we will be secondary */ - data->secondary = chanctx_conf; - } - return; - } - - /* - * STA / P2P Client, try to be primary if first vif. If we are in low - * latency mode, we are already in primary and just don't do much - */ - if (!data->primary || data->primary == chanctx_conf) - data->primary = chanctx_conf; - else if (!data->secondary) - /* if secondary is not NULL, it might be a GO */ - data->secondary = chanctx_conf; - - /* - * don't reduce the Tx power if one of these is true: - * we are in LOOSE - * single share antenna product - * BT is active - * we are associated - */ - if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT || - mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc || - le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF) { - iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false); - iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); - return; - } - - /* try to get the avg rssi from fw */ - ave_rssi = mvmvif->bf_data.ave_beacon_signal; - - /* if the RSSI isn't valid, fake it is very low */ - if (!ave_rssi) - ave_rssi = -100; - if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) { - if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true)) - IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); - } else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) { - if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false)) - IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); - } - - /* Begin to monitor the RSSI: it may influence the reduced Tx power */ - iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, true, ave_rssi); -} - -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, - }; - struct iwl_bt_coex_ci_cmd cmd = {}; - u8 ci_bw_idx; - - /* Ignore updates if we are in force mode */ - if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) - return; - - rcu_read_lock(); - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_bt_notif_iterator, &data); - - if (data.primary) { - struct ieee80211_chanctx_conf *chan = data.primary; - if (WARN_ON(!chan->def.chan)) { - rcu_read_unlock(); - return; - } - - if (chan->def.width < NL80211_CHAN_WIDTH_40) { - ci_bw_idx = 0; - } else { - if (chan->def.center_freq1 > - chan->def.chan->center_freq) - ci_bw_idx = 2; - else - ci_bw_idx = 1; - } - - cmd.bt_primary_ci = - iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; - cmd.primary_ch_phy_id = - cpu_to_le32(*((u16 *)data.primary->drv_priv)); - } - - if (data.secondary) { - struct ieee80211_chanctx_conf *chan = data.secondary; - if (WARN_ON(!data.secondary->def.chan)) { - rcu_read_unlock(); - return; - } - - if (chan->def.width < NL80211_CHAN_WIDTH_40) { - ci_bw_idx = 0; - } else { - if (chan->def.center_freq1 > - chan->def.chan->center_freq) - ci_bw_idx = 2; - else - ci_bw_idx = 1; - } - - cmd.bt_secondary_ci = - iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; - cmd.secondary_ch_phy_id = - cpu_to_le32(*((u16 *)data.secondary->drv_priv)); - } - - rcu_read_unlock(); - - /* Don't spam the fw with the same command over and over */ - if (memcmp(&cmd, &mvm->last_bt_ci_cmd, sizeof(cmd))) { - if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, 0, - sizeof(cmd), &cmd)) - IWL_ERR(mvm, "Failed to send BT_CI cmd\n"); - memcpy(&mvm->last_bt_ci_cmd, &cmd, sizeof(cmd)); - } -} - -void iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data; - - if (!fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_BT_COEX_SPLIT)) { - iwl_mvm_rx_bt_coex_notif_old(mvm, rxb); - return; - } - - IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n"); - IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance); - IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n", - le32_to_cpu(notif->primary_ch_lut)); - IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n", - le32_to_cpu(notif->secondary_ch_lut)); - IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n", - le32_to_cpu(notif->bt_activity_grading)); - - /* 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); -} - -void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - enum ieee80211_rssi_event_data rssi_event) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; - - if (!fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_BT_COEX_SPLIT)) { - iwl_mvm_bt_rssi_event_old(mvm, vif, rssi_event); - return; - } - - lockdep_assert_held(&mvm->mutex); - - /* Ignore updates if we are in force mode */ - if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) - return; - - /* - * Rssi update while not associated - can happen since the statistics - * are handled asynchronously - */ - if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) - return; - - /* No BT - reports should be disabled */ - if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF) - return; - - 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 || mvm->cfg->bt_shared_single_ant || - iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT) - 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"); -} - -#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) -#define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT (1200) - -u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, - struct ieee80211_sta *sta) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); - struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt; - enum iwl_bt_coex_lut_type lut_type; - - if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT)) - return iwl_mvm_coex_agg_time_limit_old(mvm, sta); - - if (IWL_COEX_IS_TTC_ON(mvm->last_bt_notif.ttc_rrc_status, phy_ctxt->id)) - return LINK_QUAL_AGG_TIME_LIMIT_DEF; - - if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < - BT_HIGH_TRAFFIC) - return LINK_QUAL_AGG_TIME_LIMIT_DEF; - - lut_type = iwl_get_coex_type(mvm, mvmsta->vif); - - if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT) - return LINK_QUAL_AGG_TIME_LIMIT_DEF; - - /* tight coex, high bt traffic, reduce AGG time limit */ - return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT; -} - -bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, - struct ieee80211_sta *sta) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); - struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt; - enum iwl_bt_coex_lut_type lut_type; - - if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT)) - return iwl_mvm_bt_coex_is_mimo_allowed_old(mvm, sta); - - if (IWL_COEX_IS_TTC_ON(mvm->last_bt_notif.ttc_rrc_status, phy_ctxt->id)) - return true; - - if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < - BT_HIGH_TRAFFIC) - return true; - - /* - * In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas - * since BT is already killed. - * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while - * we Tx. - * When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO. - */ - lut_type = iwl_get_coex_type(mvm, mvmsta->vif); - return lut_type != BT_COEX_LOOSE_LUT; -} - -bool iwl_mvm_bt_coex_is_ant_avail(struct iwl_mvm *mvm, u8 ant) -{ - /* there is no other antenna, shared antenna is always available */ - if (mvm->cfg->bt_shared_single_ant) - return true; - - if (ant & mvm->cfg->non_shared_ant) - return true; - - if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT)) - return iwl_mvm_bt_coex_is_shared_ant_avail_old(mvm); - - return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < - BT_HIGH_TRAFFIC; -} - -bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm) -{ - /* there is no other antenna, shared antenna is always available */ - if (mvm->cfg->bt_shared_single_ant) - return true; - - if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT)) - return iwl_mvm_bt_coex_is_shared_ant_avail_old(mvm); - - return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC; -} - -bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, - enum ieee80211_band band) -{ - u32 bt_activity = le32_to_cpu(mvm->last_bt_notif.bt_activity_grading); - - if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT)) - return iwl_mvm_bt_coex_is_tpc_allowed_old(mvm, band); - - if (band != IEEE80211_BAND_2GHZ) - return false; - - return bt_activity >= BT_LOW_TRAFFIC; -} - -u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, - struct ieee80211_tx_info *info, u8 ac) -{ - __le16 fc = hdr->frame_control; - - if (info->band != IEEE80211_BAND_2GHZ) - return 0; - - if (unlikely(mvm->bt_tx_prio)) - return mvm->bt_tx_prio - 1; - - /* High prio packet (wrt. BT coex) if it is EAPOL, MCAST or MGMT */ - if (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO || - is_multicast_ether_addr(hdr->addr1) || - ieee80211_is_ctl(fc) || ieee80211_is_mgmt(fc) || - ieee80211_is_nullfunc(fc) || ieee80211_is_qos_nullfunc(fc)) - return 3; - - switch (ac) { - case IEEE80211_AC_BE: - return 1; - case IEEE80211_AC_VO: - return 3; - case IEEE80211_AC_VI: - return 2; - default: - break; - } - - return 0; -} - -void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm) -{ - if (!fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_BT_COEX_SPLIT)) { - iwl_mvm_bt_coex_vif_change_old(mvm); - return; - } - - iwl_mvm_bt_coex_notif_handle(mvm); -} - -void iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - u32 ant_isolation = le32_to_cpup((void *)pkt->data); - struct iwl_bt_coex_corun_lut_update_cmd cmd = {}; - u8 __maybe_unused lower_bound, upper_bound; - u8 lut; - - if (!fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_BT_COEX_SPLIT)) { - iwl_mvm_rx_ant_coupling_notif_old(mvm, rxb); - return; - } - - if (!iwl_mvm_bt_is_plcr_supported(mvm)) - return; - - lockdep_assert_held(&mvm->mutex); - - /* Ignore updates if we are in force mode */ - if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) - return; - - if (ant_isolation == mvm->last_ant_isol) - return; - - for (lut = 0; lut < ARRAY_SIZE(antenna_coupling_ranges) - 1; lut++) - if (ant_isolation < antenna_coupling_ranges[lut + 1].range) - break; - - lower_bound = antenna_coupling_ranges[lut].range; - - if (lut < ARRAY_SIZE(antenna_coupling_ranges) - 1) - upper_bound = antenna_coupling_ranges[lut + 1].range; - else - upper_bound = antenna_coupling_ranges[lut].range; - - IWL_DEBUG_COEX(mvm, "Antenna isolation=%d in range [%d,%d[, lut=%d\n", - ant_isolation, lower_bound, upper_bound, lut); - - mvm->last_ant_isol = ant_isolation; - - if (mvm->last_corun_lut == lut) - return; - - mvm->last_corun_lut = lut; - - /* For the moment, use the same LUT for 20GHz and 40GHz */ - memcpy(&cmd.corun_lut20, antenna_coupling_ranges[lut].lut20, - sizeof(cmd.corun_lut20)); - - memcpy(&cmd.corun_lut40, antenna_coupling_ranges[lut].lut20, - sizeof(cmd.corun_lut40)); - - if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_CORUN_LUT, 0, - sizeof(cmd), &cmd)) - IWL_ERR(mvm, - "failed to send BT_COEX_UPDATE_CORUN_LUT command\n"); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c deleted file mode 100644 index 61c07b05f..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c +++ /dev/null @@ -1,1315 +0,0 @@ -/****************************************************************************** - * - * 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 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * 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 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * 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 <linux/ieee80211.h> -#include <linux/etherdevice.h> -#include <net/mac80211.h> - -#include "fw-api-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 - -static int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm) -{ - if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) - return 0; - - return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, 0, - sizeof(struct iwl_bt_coex_prio_tbl_cmd), - &iwl_bt_prio_tbl); -} - -static const __le32 iwl_bt_prio_boost[BT_COEX_BOOST_SIZE] = { - cpu_to_le32(0xf0f0f0f0), /* 50% */ - cpu_to_le32(0xc0c0c0c0), /* 25% */ - cpu_to_le32(0xfcfcfcfc), /* 75% */ - cpu_to_le32(0xfefefefe), /* 87.5% */ -}; - -static const __le32 iwl_single_shared_ant[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = { - { - cpu_to_le32(0x40000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x44000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x40000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x44000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0xc0004000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xc0004000), - cpu_to_le32(0xf0005000), - }, - { - cpu_to_le32(0x40000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x44000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x40000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x44000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0xc0004000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xc0004000), - cpu_to_le32(0xf0005000), - }, - { - cpu_to_le32(0x40000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x44000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x40000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x44000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0xc0004000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xc0004000), - cpu_to_le32(0xf0005000), - }, -}; - -static const __le32 iwl_combined_lookup[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = { - { - /* Tight */ - 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(0x00004000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xf0005000), - }, - { - /* Loose */ - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - 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), - }, - { - /* Tx Tx disabled */ - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xeeaaaaaa), - 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(0xc0004000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xf0005000), - }, -}; - -/* 20MHz / 40MHz below / 40Mhz above*/ -static const __le64 iwl_ci_mask[][3] = { - /* dummy entry for channel 0 */ - {cpu_to_le64(0), cpu_to_le64(0), cpu_to_le64(0)}, - { - cpu_to_le64(0x0000001FFFULL), - cpu_to_le64(0x0ULL), - cpu_to_le64(0x00007FFFFFULL), - }, - { - cpu_to_le64(0x000000FFFFULL), - cpu_to_le64(0x0ULL), - cpu_to_le64(0x0003FFFFFFULL), - }, - { - cpu_to_le64(0x000003FFFCULL), - cpu_to_le64(0x0ULL), - cpu_to_le64(0x000FFFFFFCULL), - }, - { - cpu_to_le64(0x00001FFFE0ULL), - cpu_to_le64(0x0ULL), - cpu_to_le64(0x007FFFFFE0ULL), - }, - { - cpu_to_le64(0x00007FFF80ULL), - cpu_to_le64(0x00007FFFFFULL), - cpu_to_le64(0x01FFFFFF80ULL), - }, - { - cpu_to_le64(0x0003FFFC00ULL), - cpu_to_le64(0x0003FFFFFFULL), - cpu_to_le64(0x0FFFFFFC00ULL), - }, - { - cpu_to_le64(0x000FFFF000ULL), - cpu_to_le64(0x000FFFFFFCULL), - cpu_to_le64(0x3FFFFFF000ULL), - }, - { - cpu_to_le64(0x007FFF8000ULL), - cpu_to_le64(0x007FFFFFE0ULL), - cpu_to_le64(0xFFFFFF8000ULL), - }, - { - cpu_to_le64(0x01FFFE0000ULL), - cpu_to_le64(0x01FFFFFF80ULL), - cpu_to_le64(0xFFFFFE0000ULL), - }, - { - cpu_to_le64(0x0FFFF00000ULL), - cpu_to_le64(0x0FFFFFFC00ULL), - cpu_to_le64(0x0ULL), - }, - { - cpu_to_le64(0x3FFFC00000ULL), - cpu_to_le64(0x3FFFFFF000ULL), - cpu_to_le64(0x0) - }, - { - cpu_to_le64(0xFFFE000000ULL), - cpu_to_le64(0xFFFFFF8000ULL), - cpu_to_le64(0x0) - }, - { - cpu_to_le64(0xFFF8000000ULL), - cpu_to_le64(0xFFFFFE0000ULL), - cpu_to_le64(0x0) - }, - { - cpu_to_le64(0xFFC0000000ULL), - cpu_to_le64(0x0ULL), - cpu_to_le64(0x0ULL) - }, -}; - -enum iwl_bt_kill_msk { - BT_KILL_MSK_DEFAULT, - BT_KILL_MSK_NEVER, - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_MAX, -}; - -static const u32 iwl_bt_ctl_kill_msk[BT_KILL_MSK_MAX] = { - [BT_KILL_MSK_DEFAULT] = 0xfffffc00, - [BT_KILL_MSK_NEVER] = 0xffffffff, - [BT_KILL_MSK_ALWAYS] = 0, -}; - -static const u8 iwl_bt_cts_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = { - { - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_ALWAYS, - }, - { - BT_KILL_MSK_NEVER, - BT_KILL_MSK_NEVER, - BT_KILL_MSK_NEVER, - }, - { - BT_KILL_MSK_NEVER, - BT_KILL_MSK_NEVER, - BT_KILL_MSK_NEVER, - }, - { - BT_KILL_MSK_DEFAULT, - BT_KILL_MSK_NEVER, - BT_KILL_MSK_DEFAULT, - }, -}; - -static const u8 iwl_bt_ack_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = { - { - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_ALWAYS, - }, - { - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_ALWAYS, - }, - { - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_ALWAYS, - }, - { - BT_KILL_MSK_DEFAULT, - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_DEFAULT, - }, -}; - -struct corunning_block_luts { - u8 range; - __le32 lut20[BT_COEX_CORUN_LUT_SIZE]; -}; - -/* - * Ranges for the antenna coupling calibration / co-running block LUT: - * LUT0: [ 0, 12[ - * LUT1: [12, 20[ - * LUT2: [20, 21[ - * LUT3: [21, 23[ - * LUT4: [23, 27[ - * LUT5: [27, 30[ - * LUT6: [30, 32[ - * LUT7: [32, 33[ - * LUT8: [33, - [ - */ -static const struct corunning_block_luts antenna_coupling_ranges[] = { - { - .range = 0, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 12, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 20, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 21, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 23, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 27, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 30, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 32, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 33, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, -}; - -static enum iwl_bt_coex_lut_type -iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif) -{ - struct ieee80211_chanctx_conf *chanctx_conf; - enum iwl_bt_coex_lut_type ret; - u16 phy_ctx_id; - - /* - * Checking that we hold mvm->mutex is a good idea, but the rate - * control can't acquire the mutex since it runs in Tx path. - * So this is racy in that case, but in the worst case, the AMPDU - * size limit will be wrong for a short time which is not a big - * issue. - */ - - rcu_read_lock(); - - chanctx_conf = rcu_dereference(vif->chanctx_conf); - - if (!chanctx_conf || - chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) { - rcu_read_unlock(); - return BT_COEX_INVALID_LUT; - } - - ret = BT_COEX_TX_DIS_LUT; - - if (mvm->cfg->bt_shared_single_ant) { - rcu_read_unlock(); - return ret; - } - - phy_ctx_id = *((u16 *)chanctx_conf->drv_priv); - - if (mvm->last_bt_ci_cmd_old.primary_ch_phy_id == phy_ctx_id) - ret = le32_to_cpu(mvm->last_bt_notif_old.primary_ch_lut); - else if (mvm->last_bt_ci_cmd_old.secondary_ch_phy_id == phy_ctx_id) - ret = le32_to_cpu(mvm->last_bt_notif_old.secondary_ch_lut); - /* else - default = TX TX disallowed */ - - rcu_read_unlock(); - - return ret; -} - -int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm) -{ - struct iwl_bt_coex_cmd_old *bt_cmd; - struct iwl_host_cmd cmd = { - .id = BT_CONFIG, - .len = { sizeof(*bt_cmd), }, - .dataflags = { IWL_HCMD_DFL_NOCOPY, }, - }; - int ret; - u32 flags; - - ret = iwl_send_bt_prio_tbl(mvm); - if (ret) - return ret; - - bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL); - if (!bt_cmd) - return -ENOMEM; - cmd.data[0] = bt_cmd; - - lockdep_assert_held(&mvm->mutex); - - if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) { - switch (mvm->bt_force_ant_mode) { - case BT_FORCE_ANT_AUTO: - flags = BT_COEX_AUTO_OLD; - break; - case BT_FORCE_ANT_BT: - flags = BT_COEX_BT_OLD; - break; - case BT_FORCE_ANT_WIFI: - flags = BT_COEX_WIFI_OLD; - break; - default: - WARN_ON(1); - flags = 0; - } - - bt_cmd->flags = cpu_to_le32(flags); - bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE); - goto send_cmd; - } - - bt_cmd->max_kill = 5; - bt_cmd->bt4_antenna_isolation_thr = - IWL_MVM_BT_COEX_ANTENNA_COUPLING_THRS; - bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling; - bt_cmd->bt4_tx_tx_delta_freq_thr = 15; - bt_cmd->bt4_tx_rx_max_freq0 = 15; - bt_cmd->override_primary_lut = BT_COEX_INVALID_LUT; - bt_cmd->override_secondary_lut = BT_COEX_INVALID_LUT; - - flags = iwlwifi_mod_params.bt_coex_active ? - BT_COEX_NW_OLD : BT_COEX_DISABLE_OLD; - bt_cmd->flags = cpu_to_le32(flags); - - bt_cmd->valid_bit_msk = cpu_to_le32(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 | - BT_VALID_WIFI_RX_SW_PRIO_BOOST | - BT_VALID_WIFI_TX_SW_PRIO_BOOST | - BT_VALID_ANT_ISOLATION | - BT_VALID_ANT_ISOLATION_THRS | - BT_VALID_TXTX_DELTA_FREQ_THRS | - BT_VALID_TXRX_MAX_FREQ_0 | - BT_VALID_SYNC_TO_SCO | - BT_VALID_TTC | - BT_VALID_RRC); - - if (IWL_MVM_BT_COEX_SYNC2SCO) - bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO); - - if (iwl_mvm_bt_is_plcr_supported(mvm)) { - bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_CORUN_LUT_20 | - BT_VALID_CORUN_LUT_40); - bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING); - } - - if (IWL_MVM_BT_COEX_MPLUT) { - bt_cmd->flags |= cpu_to_le32(BT_COEX_MPLUT); - bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_MULTI_PRIO_LUT); - } - - if (IWL_MVM_BT_COEX_TTC) - bt_cmd->flags |= cpu_to_le32(BT_COEX_TTC); - - if (iwl_mvm_bt_is_rrc_supported(mvm)) - bt_cmd->flags |= cpu_to_le32(BT_COEX_RRC); - - if (mvm->cfg->bt_shared_single_ant) - memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant, - sizeof(iwl_single_shared_ant)); - else - memcpy(&bt_cmd->decision_lut, iwl_combined_lookup, - sizeof(iwl_combined_lookup)); - - /* Take first Co-running block LUT to get started */ - memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[0].lut20, - sizeof(bt_cmd->bt4_corun_lut20)); - memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[0].lut20, - sizeof(bt_cmd->bt4_corun_lut40)); - - memcpy(&bt_cmd->bt_prio_boost, iwl_bt_prio_boost, - sizeof(iwl_bt_prio_boost)); - bt_cmd->bt4_multiprio_lut[0] = cpu_to_le32(IWL_MVM_BT_COEX_MPLUT_REG0); - bt_cmd->bt4_multiprio_lut[1] = cpu_to_le32(IWL_MVM_BT_COEX_MPLUT_REG1); - -send_cmd: - memset(&mvm->last_bt_notif_old, 0, sizeof(mvm->last_bt_notif_old)); - memset(&mvm->last_bt_ci_cmd_old, 0, sizeof(mvm->last_bt_ci_cmd_old)); - - ret = iwl_mvm_send_cmd(mvm, &cmd); - - kfree(bt_cmd); - return ret; -} - -static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm) -{ - struct iwl_bt_coex_profile_notif_old *notif = &mvm->last_bt_notif_old; - u32 primary_lut = le32_to_cpu(notif->primary_ch_lut); - u32 ag = le32_to_cpu(notif->bt_activity_grading); - struct iwl_bt_coex_cmd_old *bt_cmd; - u8 ack_kill_msk, cts_kill_msk; - struct iwl_host_cmd cmd = { - .id = BT_CONFIG, - .data[0] = &bt_cmd, - .len = { sizeof(*bt_cmd), }, - .dataflags = { IWL_HCMD_DFL_NOCOPY, }, - }; - int ret = 0; - - lockdep_assert_held(&mvm->mutex); - - ack_kill_msk = iwl_bt_ack_kill_msk[ag][primary_lut]; - cts_kill_msk = iwl_bt_cts_kill_msk[ag][primary_lut]; - - if (mvm->bt_ack_kill_msk[0] == ack_kill_msk && - mvm->bt_cts_kill_msk[0] == cts_kill_msk) - return 0; - - mvm->bt_ack_kill_msk[0] = ack_kill_msk; - mvm->bt_cts_kill_msk[0] = cts_kill_msk; - - bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL); - if (!bt_cmd) - return -ENOMEM; - cmd.data[0] = bt_cmd; - bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD); - - bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ctl_kill_msk[ack_kill_msk]); - bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_ctl_kill_msk[cts_kill_msk]); - bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE | - BT_VALID_KILL_ACK | - BT_VALID_KILL_CTS); - - ret = iwl_mvm_send_cmd(mvm, &cmd); - - kfree(bt_cmd); - return ret; -} - -static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, - bool enable) -{ - struct iwl_bt_coex_cmd_old *bt_cmd; - /* Send ASYNC since this can be sent from an atomic context */ - struct iwl_host_cmd cmd = { - .id = BT_CONFIG, - .len = { sizeof(*bt_cmd), }, - .dataflags = { IWL_HCMD_DFL_DUP, }, - .flags = CMD_ASYNC, - }; - struct iwl_mvm_sta *mvmsta; - int ret; - - mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id); - if (!mvmsta) - return 0; - - /* nothing to do */ - if (mvmsta->bt_reduced_txpower == enable) - return 0; - - bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_ATOMIC); - if (!bt_cmd) - return -ENOMEM; - cmd.data[0] = bt_cmd; - bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD); - - bt_cmd->valid_bit_msk = - cpu_to_le32(BT_VALID_ENABLE | BT_VALID_REDUCED_TX_POWER); - bt_cmd->bt_reduced_tx_power = sta_id; - - if (enable) - bt_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; - - ret = iwl_mvm_send_cmd(mvm, &cmd); - - kfree(bt_cmd); - return ret; -} - -struct iwl_bt_iterator_data { - struct iwl_bt_coex_profile_notif_old *notif; - struct iwl_mvm *mvm; - struct ieee80211_chanctx_conf *primary; - struct ieee80211_chanctx_conf *secondary; - bool primary_ll; -}; - -static inline -void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool enable, int rssi) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - mvmvif->bf_data.last_bt_coex_event = rssi; - mvmvif->bf_data.bt_coex_max_thold = - enable ? -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH : 0; - mvmvif->bf_data.bt_coex_min_thold = - enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0; -} - -/* must be called under rcu_read_lock */ -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; - u32 bt_activity_grading; - int ave_rssi; - - lockdep_assert_held(&mvm->mutex); - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - /* default smps_mode for BSS / P2P client is AUTOMATIC */ - smps_mode = IEEE80211_SMPS_AUTOMATIC; - break; - case NL80211_IFTYPE_AP: - if (!mvmvif->ap_ibss_active) - return; - break; - default: - return; - } - - chanctx_conf = rcu_dereference(vif->chanctx_conf); - - /* If channel context is invalid or not on 2.4GHz .. */ - if ((!chanctx_conf || - chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ)) { - if (vif->type == NL80211_IFTYPE_STATION) { - /* ... relax constraints and disable rssi events */ - iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, - smps_mode); - iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, - false); - iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); - } - return; - } - - bt_activity_grading = le32_to_cpu(data->notif->bt_activity_grading); - if (bt_activity_grading >= BT_HIGH_TRAFFIC) - smps_mode = IEEE80211_SMPS_STATIC; - else if (bt_activity_grading >= BT_LOW_TRAFFIC) - smps_mode = vif->type == NL80211_IFTYPE_AP ? - IEEE80211_SMPS_OFF : - IEEE80211_SMPS_DYNAMIC; - - /* relax SMPS contraints for next association */ - if (!vif->bss_conf.assoc) - smps_mode = IEEE80211_SMPS_AUTOMATIC; - - if (mvmvif->phy_ctxt && - data->notif->rrc_enabled & BIT(mvmvif->phy_ctxt->id)) - smps_mode = IEEE80211_SMPS_AUTOMATIC; - - IWL_DEBUG_COEX(data->mvm, - "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n", - mvmvif->id, data->notif->bt_status, bt_activity_grading, - smps_mode); - - if (vif->type == NL80211_IFTYPE_STATION) - iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, - smps_mode); - - /* low latency is always primary */ - if (iwl_mvm_vif_low_latency(mvmvif)) { - data->primary_ll = true; - - data->secondary = data->primary; - data->primary = chanctx_conf; - } - - if (vif->type == NL80211_IFTYPE_AP) { - if (!mvmvif->ap_ibss_active) - return; - - if (chanctx_conf == data->primary) - return; - - if (!data->primary_ll) { - /* - * downgrade the current primary no matter what its - * type is. - */ - data->secondary = data->primary; - data->primary = chanctx_conf; - } else { - /* there is low latency vif - we will be secondary */ - data->secondary = chanctx_conf; - } - return; - } - - /* - * STA / P2P Client, try to be primary if first vif. If we are in low - * latency mode, we are already in primary and just don't do much - */ - if (!data->primary || data->primary == chanctx_conf) - data->primary = chanctx_conf; - else if (!data->secondary) - /* if secondary is not NULL, it might be a GO */ - data->secondary = chanctx_conf; - - /* - * don't reduce the Tx power if one of these is true: - * we are in LOOSE - * single share antenna product - * BT is active - * we are associated - */ - if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT || - mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc || - !data->notif->bt_status) { - iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false); - iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); - return; - } - - /* try to get the avg rssi from fw */ - ave_rssi = mvmvif->bf_data.ave_beacon_signal; - - /* if the RSSI isn't valid, fake it is very low */ - if (!ave_rssi) - ave_rssi = -100; - if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) { - if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true)) - IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); - } else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) { - if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false)) - IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); - } - - /* Begin to monitor the RSSI: it may influence the reduced Tx power */ - iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, true, ave_rssi); -} - -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_old, - }; - struct iwl_bt_coex_ci_cmd_old cmd = {}; - u8 ci_bw_idx; - - /* Ignore updates if we are in force mode */ - if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) - return; - - rcu_read_lock(); - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_bt_notif_iterator, &data); - - if (data.primary) { - struct ieee80211_chanctx_conf *chan = data.primary; - - if (WARN_ON(!chan->def.chan)) { - rcu_read_unlock(); - return; - } - - if (chan->def.width < NL80211_CHAN_WIDTH_40) { - ci_bw_idx = 0; - cmd.co_run_bw_primary = 0; - } else { - cmd.co_run_bw_primary = 1; - if (chan->def.center_freq1 > - chan->def.chan->center_freq) - ci_bw_idx = 2; - else - ci_bw_idx = 1; - } - - cmd.bt_primary_ci = - iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; - cmd.primary_ch_phy_id = *((u16 *)data.primary->drv_priv); - } - - if (data.secondary) { - struct ieee80211_chanctx_conf *chan = data.secondary; - - if (WARN_ON(!data.secondary->def.chan)) { - rcu_read_unlock(); - return; - } - - if (chan->def.width < NL80211_CHAN_WIDTH_40) { - ci_bw_idx = 0; - cmd.co_run_bw_secondary = 0; - } else { - cmd.co_run_bw_secondary = 1; - if (chan->def.center_freq1 > - chan->def.chan->center_freq) - ci_bw_idx = 2; - else - ci_bw_idx = 1; - } - - cmd.bt_secondary_ci = - iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; - cmd.secondary_ch_phy_id = *((u16 *)data.secondary->drv_priv); - } - - rcu_read_unlock(); - - /* Don't spam the fw with the same command over and over */ - if (memcmp(&cmd, &mvm->last_bt_ci_cmd_old, sizeof(cmd))) { - if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, 0, - sizeof(cmd), &cmd)) - IWL_ERR(mvm, "Failed to send BT_CI cmd\n"); - memcpy(&mvm->last_bt_ci_cmd_old, &cmd, sizeof(cmd)); - } - - if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm)) - IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n"); -} - -void iwl_mvm_rx_bt_coex_notif_old(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_bt_coex_profile_notif_old *notif = (void *)pkt->data; - - IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n"); - IWL_DEBUG_COEX(mvm, "\tBT status: %s\n", - notif->bt_status ? "ON" : "OFF"); - IWL_DEBUG_COEX(mvm, "\tBT open conn %d\n", notif->bt_open_conn); - IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance); - IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n", - le32_to_cpu(notif->primary_ch_lut)); - IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n", - le32_to_cpu(notif->secondary_ch_lut)); - IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n", - le32_to_cpu(notif->bt_activity_grading)); - IWL_DEBUG_COEX(mvm, "\tBT agg traffic load %d\n", - notif->bt_agg_traffic_load); - - /* remember this notification for future use: rssi fluctuations */ - memcpy(&mvm->last_bt_notif_old, notif, sizeof(mvm->last_bt_notif_old)); - - iwl_mvm_bt_coex_notif_handle(mvm); -} - -static void iwl_mvm_bt_rssi_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_sta *sta; - struct iwl_mvm_sta *mvmsta; - - struct ieee80211_chanctx_conf *chanctx_conf; - - rcu_read_lock(); - chanctx_conf = rcu_dereference(vif->chanctx_conf); - /* If channel context is invalid or not on 2.4GHz - don't count it */ - if (!chanctx_conf || - chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) { - rcu_read_unlock(); - return; - } - rcu_read_unlock(); - - 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)); - - /* This can happen if the station has been removed right now */ - if (IS_ERR_OR_NULL(sta)) - return; - - mvmsta = iwl_mvm_sta_from_mac80211(sta); -} - -void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - enum ieee80211_rssi_event_data rssi_event) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_bt_iterator_data data = { - .mvm = mvm, - }; - int ret; - - lockdep_assert_held(&mvm->mutex); - - /* Ignore updates if we are in force mode */ - if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) - return; - - /* - * Rssi update while not associated - can happen since the statistics - * are handled asynchronously - */ - if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) - return; - - /* No BT - reports should be disabled */ - if (!mvm->last_bt_notif_old.bt_status) - return; - - 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 || mvm->cfg->bt_shared_single_ant || - iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT) - 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 (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm)) - IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n"); -} - -#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) -#define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT (1200) - -u16 iwl_mvm_coex_agg_time_limit_old(struct iwl_mvm *mvm, - struct ieee80211_sta *sta) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - enum iwl_bt_coex_lut_type lut_type; - - if (le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading) < - BT_HIGH_TRAFFIC) - return LINK_QUAL_AGG_TIME_LIMIT_DEF; - - if (mvm->last_bt_notif_old.ttc_enabled) - return LINK_QUAL_AGG_TIME_LIMIT_DEF; - - lut_type = iwl_get_coex_type(mvm, mvmsta->vif); - - if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT) - return LINK_QUAL_AGG_TIME_LIMIT_DEF; - - /* tight coex, high bt traffic, reduce AGG time limit */ - return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT; -} - -bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm, - struct ieee80211_sta *sta) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - enum iwl_bt_coex_lut_type lut_type; - - if (mvm->last_bt_notif_old.ttc_enabled) - return true; - - if (le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading) < - BT_HIGH_TRAFFIC) - return true; - - /* - * In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas - * since BT is already killed. - * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while - * we Tx. - * When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO. - */ - lut_type = iwl_get_coex_type(mvm, mvmsta->vif); - return lut_type != BT_COEX_LOOSE_LUT; -} - -bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm) -{ - u32 ag = le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading); - return ag < BT_HIGH_TRAFFIC; -} - -bool iwl_mvm_bt_coex_is_tpc_allowed_old(struct iwl_mvm *mvm, - enum ieee80211_band band) -{ - u32 bt_activity = - le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading); - - if (band != IEEE80211_BAND_2GHZ) - return false; - - return bt_activity >= BT_LOW_TRAFFIC; -} - -void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm) -{ - iwl_mvm_bt_coex_notif_handle(mvm); -} - -void iwl_mvm_rx_ant_coupling_notif_old(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - u32 ant_isolation = le32_to_cpup((void *)pkt->data); - u8 __maybe_unused lower_bound, upper_bound; - u8 lut; - - struct iwl_bt_coex_cmd_old *bt_cmd; - struct iwl_host_cmd cmd = { - .id = BT_CONFIG, - .len = { sizeof(*bt_cmd), }, - .dataflags = { IWL_HCMD_DFL_NOCOPY, }, - }; - - if (!iwl_mvm_bt_is_plcr_supported(mvm)) - return; - - lockdep_assert_held(&mvm->mutex); - - /* Ignore updates if we are in force mode */ - if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) - return; - - if (ant_isolation == mvm->last_ant_isol) - return; - - for (lut = 0; lut < ARRAY_SIZE(antenna_coupling_ranges) - 1; lut++) - if (ant_isolation < antenna_coupling_ranges[lut + 1].range) - break; - - lower_bound = antenna_coupling_ranges[lut].range; - - if (lut < ARRAY_SIZE(antenna_coupling_ranges) - 1) - upper_bound = antenna_coupling_ranges[lut + 1].range; - else - upper_bound = antenna_coupling_ranges[lut].range; - - IWL_DEBUG_COEX(mvm, "Antenna isolation=%d in range [%d,%d[, lut=%d\n", - ant_isolation, lower_bound, upper_bound, lut); - - mvm->last_ant_isol = ant_isolation; - - if (mvm->last_corun_lut == lut) - return; - - mvm->last_corun_lut = lut; - - bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL); - if (!bt_cmd) - return; - cmd.data[0] = bt_cmd; - - bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD); - bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE | - BT_VALID_CORUN_LUT_20 | - BT_VALID_CORUN_LUT_40); - - /* For the moment, use the same LUT for 20GHz and 40GHz */ - memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[lut].lut20, - sizeof(bt_cmd->bt4_corun_lut20)); - - memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[lut].lut20, - sizeof(bt_cmd->bt4_corun_lut40)); - - if (iwl_mvm_send_cmd(mvm, &cmd)) - IWL_ERR(mvm, "failed to send BT_CONFIG command\n"); - - kfree(bt_cmd); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/constants.h b/drivers/net/wireless/iwlwifi/mvm/constants.h deleted file mode 100644 index 5c21231e1..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/constants.h +++ /dev/null @@ -1,139 +0,0 @@ -/****************************************************************************** - * - * 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 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * 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 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * 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 __MVM_CONSTANTS_H -#define __MVM_CONSTANTS_H - -#include <linux/ieee80211.h> - -#define IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT (100 * USEC_PER_MSEC) -#define IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT (100 * USEC_PER_MSEC) -#define IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT (10 * USEC_PER_MSEC) -#define IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT (10 * USEC_PER_MSEC) -#define IWL_MVM_SHORT_PS_TX_DATA_TIMEOUT (2 * 1024) /* defined in TU */ -#define IWL_MVM_SHORT_PS_RX_DATA_TIMEOUT (40 * 1024) /* defined in TU */ -#define IWL_MVM_P2P_LOWLATENCY_PS_ENABLE 0 -#define IWL_MVM_UAPSD_RX_DATA_TIMEOUT (50 * USEC_PER_MSEC) -#define IWL_MVM_UAPSD_TX_DATA_TIMEOUT (50 * USEC_PER_MSEC) -#define IWL_MVM_UAPSD_QUEUES (IEEE80211_WMM_IE_STA_QOSINFO_AC_VO |\ - IEEE80211_WMM_IE_STA_QOSINFO_AC_VI |\ - IEEE80211_WMM_IE_STA_QOSINFO_AC_BK |\ - IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) -#define IWL_MVM_PS_HEAVY_TX_THLD_PACKETS 20 -#define IWL_MVM_PS_HEAVY_RX_THLD_PACKETS 8 -#define IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS 30 -#define IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS 20 -#define IWL_MVM_PS_HEAVY_TX_THLD_PERCENT 50 -#define IWL_MVM_PS_HEAVY_RX_THLD_PERCENT 50 -#define IWL_MVM_PS_SNOOZE_INTERVAL 25 -#define IWL_MVM_PS_SNOOZE_WINDOW 50 -#define IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW 25 -#define IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT 64 -#define IWL_MVM_BT_COEX_EN_RED_TXP_THRESH 62 -#define IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH 65 -#define IWL_MVM_BT_COEX_SYNC2SCO 1 -#define IWL_MVM_BT_COEX_CORUNNING 0 -#define IWL_MVM_BT_COEX_MPLUT 1 -#define IWL_MVM_BT_COEX_RRC 1 -#define IWL_MVM_BT_COEX_TTC 1 -#define IWL_MVM_BT_COEX_MPLUT_REG0 0x22002200 -#define IWL_MVM_BT_COEX_MPLUT_REG1 0x11118451 -#define IWL_MVM_BT_COEX_ANTENNA_COUPLING_THRS 30 -#define IWL_MVM_FW_MCAST_FILTER_PASS_ALL 0 -#define IWL_MVM_FW_BCAST_FILTER_PASS_ALL 0 -#define IWL_MVM_QUOTA_THRESHOLD 4 -#define IWL_MVM_RS_RSSI_BASED_INIT_RATE 0 -#define IWL_MVM_RS_80_20_FAR_RANGE_TWEAK 1 -#define IWL_MVM_TOF_IS_RESPONDER 0 -#define IWL_MVM_RS_NUM_TRY_BEFORE_ANT_TOGGLE 1 -#define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE 2 -#define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE_TW 1 -#define IWL_MVM_RS_INITIAL_MIMO_NUM_RATES 3 -#define IWL_MVM_RS_INITIAL_SISO_NUM_RATES 3 -#define IWL_MVM_RS_INITIAL_LEGACY_NUM_RATES 2 -#define IWL_MVM_RS_INITIAL_LEGACY_RETRIES 2 -#define IWL_MVM_RS_SECONDARY_LEGACY_RETRIES 1 -#define IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES 16 -#define IWL_MVM_RS_SECONDARY_SISO_NUM_RATES 3 -#define IWL_MVM_RS_SECONDARY_SISO_RETRIES 1 -#define IWL_MVM_RS_RATE_MIN_FAILURE_TH 3 -#define IWL_MVM_RS_RATE_MIN_SUCCESS_TH 8 -#define IWL_MVM_RS_STAY_IN_COLUMN_TIMEOUT 5 /* Seconds */ -#define IWL_MVM_RS_IDLE_TIMEOUT 5 /* Seconds */ -#define IWL_MVM_RS_MISSED_RATE_MAX 15 -#define IWL_MVM_RS_LEGACY_FAILURE_LIMIT 160 -#define IWL_MVM_RS_LEGACY_SUCCESS_LIMIT 480 -#define IWL_MVM_RS_LEGACY_TABLE_COUNT 160 -#define IWL_MVM_RS_NON_LEGACY_FAILURE_LIMIT 400 -#define IWL_MVM_RS_NON_LEGACY_SUCCESS_LIMIT 4500 -#define IWL_MVM_RS_NON_LEGACY_TABLE_COUNT 1500 -#define IWL_MVM_RS_SR_FORCE_DECREASE 15 /* percent */ -#define IWL_MVM_RS_SR_NO_DECREASE 85 /* percent */ -#define IWL_MVM_RS_AGG_TIME_LIMIT 4000 /* 4 msecs. valid 100-8000 */ -#define IWL_MVM_RS_AGG_DISABLE_START 3 -#define IWL_MVM_RS_TPC_SR_FORCE_INCREASE 75 /* percent */ -#define IWL_MVM_RS_TPC_SR_NO_INCREASE 85 /* percent */ -#define IWL_MVM_RS_TPC_TX_POWER_STEP 3 - -#endif /* __MVM_CONSTANTS_H */ diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c deleted file mode 100644 index 29ae58ebf..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ /dev/null @@ -1,2100 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * 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 <linux/etherdevice.h> -#include <linux/ip.h> -#include <linux/fs.h> -#include <net/cfg80211.h> -#include <net/ipv6.h> -#include <net/tcp.h> -#include <net/addrconf.h> -#include "iwl-modparams.h" -#include "fw-api.h" -#include "mvm.h" - -void iwl_mvm_set_rekey_data(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_gtk_rekey_data *data) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (iwlwifi_mod_params.sw_crypto) - return; - - mutex_lock(&mvm->mutex); - - memcpy(mvmvif->rekey_data.kek, data->kek, NL80211_KEK_LEN); - memcpy(mvmvif->rekey_data.kck, data->kck, NL80211_KCK_LEN); - mvmvif->rekey_data.replay_ctr = - cpu_to_le64(be64_to_cpup((__be64 *)&data->replay_ctr)); - mvmvif->rekey_data.valid = true; - - mutex_unlock(&mvm->mutex); -} - -#if IS_ENABLED(CONFIG_IPV6) -void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct inet6_dev *idev) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct inet6_ifaddr *ifa; - int idx = 0; - - read_lock_bh(&idev->lock); - list_for_each_entry(ifa, &idev->addr_list, if_list) { - mvmvif->target_ipv6_addrs[idx] = ifa->addr; - idx++; - if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX) - break; - } - read_unlock_bh(&idev->lock); - - mvmvif->num_target_ipv6_addrs = idx; -} -#endif - -void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, int idx) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - mvmvif->tx_key_idx = idx; -} - -static void iwl_mvm_convert_p1k(u16 *p1k, __le16 *out) -{ - int i; - - for (i = 0; i < IWL_P1K_SIZE; i++) - out[i] = cpu_to_le16(p1k[i]); -} - -struct wowlan_key_data { - struct iwl_wowlan_rsc_tsc_params_cmd *rsc_tsc; - struct iwl_wowlan_tkip_params_cmd *tkip; - bool error, use_rsc_tsc, use_tkip; - int wep_key_idx; -}; - -static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *key, - void *_data) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct wowlan_key_data *data = _data; - struct aes_sc *aes_sc, *aes_tx_sc = NULL; - struct tkip_sc *tkip_sc, *tkip_tx_sc = NULL; - struct iwl_p1k_cache *rx_p1ks; - u8 *rx_mic_key; - struct ieee80211_key_seq seq; - u32 cur_rx_iv32 = 0; - u16 p1k[IWL_P1K_SIZE]; - int ret, i; - - mutex_lock(&mvm->mutex); - - switch (key->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: { /* hack it for now */ - struct { - struct iwl_mvm_wep_key_cmd wep_key_cmd; - struct iwl_mvm_wep_key wep_key; - } __packed wkc = { - .wep_key_cmd.mac_id_n_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, - mvmvif->color)), - .wep_key_cmd.num_keys = 1, - /* firmware sets STA_KEY_FLG_WEP_13BYTES */ - .wep_key_cmd.decryption_type = STA_KEY_FLG_WEP, - .wep_key.key_index = key->keyidx, - .wep_key.key_size = key->keylen, - }; - - /* - * This will fail -- the key functions don't set support - * pairwise WEP keys. However, that's better than silently - * failing WoWLAN. Or maybe not? - */ - if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) - break; - - memcpy(&wkc.wep_key.key[3], key->key, key->keylen); - if (key->keyidx == mvmvif->tx_key_idx) { - /* TX key must be at offset 0 */ - wkc.wep_key.key_offset = 0; - } else { - /* others start at 1 */ - data->wep_key_idx++; - wkc.wep_key.key_offset = data->wep_key_idx; - } - - ret = iwl_mvm_send_cmd_pdu(mvm, WEP_KEY, 0, sizeof(wkc), &wkc); - data->error = ret != 0; - - mvm->ptk_ivlen = key->iv_len; - mvm->ptk_icvlen = key->icv_len; - mvm->gtk_ivlen = key->iv_len; - mvm->gtk_icvlen = key->icv_len; - - /* don't upload key again */ - goto out_unlock; - } - default: - data->error = true; - goto out_unlock; - case WLAN_CIPHER_SUITE_AES_CMAC: - /* - * Ignore CMAC keys -- the WoWLAN firmware doesn't support them - * but we also shouldn't abort suspend due to that. It does have - * support for the IGTK key renewal, but doesn't really use the - * IGTK for anything. This means we could spuriously wake up or - * be deauthenticated, but that was considered acceptable. - */ - goto out_unlock; - case WLAN_CIPHER_SUITE_TKIP: - if (sta) { - tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc; - tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc; - - rx_p1ks = data->tkip->rx_uni; - - ieee80211_get_key_tx_seq(key, &seq); - tkip_tx_sc->iv16 = cpu_to_le16(seq.tkip.iv16); - tkip_tx_sc->iv32 = cpu_to_le32(seq.tkip.iv32); - - ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k); - iwl_mvm_convert_p1k(p1k, data->tkip->tx.p1k); - - memcpy(data->tkip->mic_keys.tx, - &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY], - IWL_MIC_KEY_SIZE); - - rx_mic_key = data->tkip->mic_keys.rx_unicast; - } else { - tkip_sc = - data->rsc_tsc->all_tsc_rsc.tkip.multicast_rsc; - rx_p1ks = data->tkip->rx_multi; - rx_mic_key = data->tkip->mic_keys.rx_mcast; - } - - /* - * For non-QoS this relies on the fact that both the uCode and - * mac80211 use TID 0 (as they need to to avoid replay attacks) - * for checking the IV in the frames. - */ - for (i = 0; i < IWL_NUM_RSC; i++) { - ieee80211_get_key_rx_seq(key, i, &seq); - tkip_sc[i].iv16 = cpu_to_le16(seq.tkip.iv16); - tkip_sc[i].iv32 = cpu_to_le32(seq.tkip.iv32); - /* wrapping isn't allowed, AP must rekey */ - if (seq.tkip.iv32 > cur_rx_iv32) - cur_rx_iv32 = seq.tkip.iv32; - } - - ieee80211_get_tkip_rx_p1k(key, vif->bss_conf.bssid, - cur_rx_iv32, p1k); - iwl_mvm_convert_p1k(p1k, rx_p1ks[0].p1k); - ieee80211_get_tkip_rx_p1k(key, vif->bss_conf.bssid, - cur_rx_iv32 + 1, p1k); - iwl_mvm_convert_p1k(p1k, rx_p1ks[1].p1k); - - memcpy(rx_mic_key, - &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY], - IWL_MIC_KEY_SIZE); - - data->use_tkip = true; - data->use_rsc_tsc = true; - break; - case WLAN_CIPHER_SUITE_CCMP: - if (sta) { - u64 pn64; - - aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc; - aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc; - - pn64 = atomic64_read(&key->tx_pn); - aes_tx_sc->pn = cpu_to_le64(pn64); - } else { - aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc; - } - - /* - * For non-QoS this relies on the fact that both the uCode and - * mac80211 use TID 0 for checking the IV in the frames. - */ - for (i = 0; i < IWL_NUM_RSC; i++) { - u8 *pn = seq.ccmp.pn; - - ieee80211_get_key_rx_seq(key, i, &seq); - aes_sc[i].pn = cpu_to_le64((u64)pn[5] | - ((u64)pn[4] << 8) | - ((u64)pn[3] << 16) | - ((u64)pn[2] << 24) | - ((u64)pn[1] << 32) | - ((u64)pn[0] << 40)); - } - data->use_rsc_tsc = true; - break; - } - - /* - * The D3 firmware hardcodes the key offset 0 as the key it uses - * to transmit packets to the AP, i.e. the PTK. - */ - if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) { - mvm->ptk_ivlen = key->iv_len; - mvm->ptk_icvlen = key->icv_len; - ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, 0); - } else { - /* - * firmware only supports TSC/RSC for a single key, - * so if there are multiple keep overwriting them - * with new ones -- this relies on mac80211 doing - * list_add_tail(). - */ - mvm->gtk_ivlen = key->iv_len; - mvm->gtk_icvlen = key->icv_len; - ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, 1); - } - - data->error = ret != 0; -out_unlock: - mutex_unlock(&mvm->mutex); -} - -static int iwl_mvm_send_patterns(struct iwl_mvm *mvm, - struct cfg80211_wowlan *wowlan) -{ - struct iwl_wowlan_patterns_cmd *pattern_cmd; - struct iwl_host_cmd cmd = { - .id = WOWLAN_PATTERNS, - .dataflags[0] = IWL_HCMD_DFL_NOCOPY, - }; - int i, err; - - if (!wowlan->n_patterns) - return 0; - - cmd.len[0] = sizeof(*pattern_cmd) + - wowlan->n_patterns * sizeof(struct iwl_wowlan_pattern); - - pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL); - if (!pattern_cmd) - return -ENOMEM; - - pattern_cmd->n_patterns = cpu_to_le32(wowlan->n_patterns); - - for (i = 0; i < wowlan->n_patterns; i++) { - int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); - - memcpy(&pattern_cmd->patterns[i].mask, - wowlan->patterns[i].mask, mask_len); - memcpy(&pattern_cmd->patterns[i].pattern, - wowlan->patterns[i].pattern, - wowlan->patterns[i].pattern_len); - pattern_cmd->patterns[i].mask_size = mask_len; - pattern_cmd->patterns[i].pattern_size = - wowlan->patterns[i].pattern_len; - } - - cmd.data[0] = pattern_cmd; - err = iwl_mvm_send_cmd(mvm, &cmd); - kfree(pattern_cmd); - return err; -} - -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 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, }, - }; - 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( - 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( - 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( - 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( - 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( - 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( - 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; -} - -static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_sta *ap_sta) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct ieee80211_chanctx_conf *ctx; - u8 chains_static, chains_dynamic; - struct cfg80211_chan_def chandef; - int ret, i; - struct iwl_binding_cmd binding_cmd = {}; - struct iwl_time_quota_cmd quota_cmd = {}; - u32 status; - - /* add back the PHY */ - if (WARN_ON(!mvmvif->phy_ctxt)) - return -EINVAL; - - rcu_read_lock(); - ctx = rcu_dereference(vif->chanctx_conf); - if (WARN_ON(!ctx)) { - rcu_read_unlock(); - return -EINVAL; - } - chandef = ctx->def; - chains_static = ctx->rx_chains_static; - chains_dynamic = ctx->rx_chains_dynamic; - rcu_read_unlock(); - - ret = iwl_mvm_phy_ctxt_add(mvm, mvmvif->phy_ctxt, &chandef, - chains_static, chains_dynamic); - if (ret) - return ret; - - /* add back the MAC */ - mvmvif->uploaded = false; - - if (WARN_ON(!vif->bss_conf.assoc)) - return -EINVAL; - - ret = iwl_mvm_mac_ctxt_add(mvm, vif); - if (ret) - return ret; - - /* add back binding - XXX refactor? */ - binding_cmd.id_and_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id, - mvmvif->phy_ctxt->color)); - binding_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD); - binding_cmd.phy = - cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id, - mvmvif->phy_ctxt->color)); - binding_cmd.macs[0] = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, - mvmvif->color)); - for (i = 1; i < MAX_MACS_IN_BINDING; i++) - binding_cmd.macs[i] = cpu_to_le32(FW_CTXT_INVALID); - - status = 0; - ret = iwl_mvm_send_cmd_pdu_status(mvm, BINDING_CONTEXT_CMD, - sizeof(binding_cmd), &binding_cmd, - &status); - if (ret) { - IWL_ERR(mvm, "Failed to add binding: %d\n", ret); - return ret; - } - - if (status) { - IWL_ERR(mvm, "Binding command failed: %u\n", status); - return -EIO; - } - - ret = iwl_mvm_sta_send_to_fw(mvm, ap_sta, false); - if (ret) - return ret; - rcu_assign_pointer(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id], ap_sta); - - ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); - if (ret) - return ret; - - /* and some quota */ - quota_cmd.quotas[0].id_and_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id, - mvmvif->phy_ctxt->color)); - quota_cmd.quotas[0].quota = cpu_to_le32(IWL_MVM_MAX_QUOTA); - quota_cmd.quotas[0].max_duration = cpu_to_le32(IWL_MVM_MAX_QUOTA); - - for (i = 1; i < MAX_BINDINGS; i++) - quota_cmd.quotas[i].id_and_color = cpu_to_le32(FW_CTXT_INVALID); - - ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0, - sizeof(quota_cmd), "a_cmd); - if (ret) - IWL_ERR(mvm, "Failed to send quota: %d\n", ret); - - if (iwl_mvm_is_lar_supported(mvm) && iwl_mvm_init_fw_regd(mvm)) - IWL_ERR(mvm, "Failed to initialize D3 LAR information\n"); - - return 0; -} - -static int iwl_mvm_get_last_nonqos_seq(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_nonqos_seq_query_cmd query_cmd = { - .get_set_flag = cpu_to_le32(IWL_NONQOS_SEQ_GET), - .mac_id_n_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, - mvmvif->color)), - }; - struct iwl_host_cmd cmd = { - .id = NON_QOS_TX_COUNTER_CMD, - .flags = CMD_WANT_SKB, - }; - int err; - u32 size; - - cmd.data[0] = &query_cmd; - cmd.len[0] = sizeof(query_cmd); - - err = iwl_mvm_send_cmd(mvm, &cmd); - if (err) - return err; - - size = iwl_rx_packet_payload_len(cmd.resp_pkt); - if (size < sizeof(__le16)) { - err = -EINVAL; - } else { - err = le16_to_cpup((__le16 *)cmd.resp_pkt->data); - /* firmware returns next, not last-used seqno */ - err = (u16) (err - 0x10); - } - - iwl_free_resp(&cmd); - return err; -} - -void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_nonqos_seq_query_cmd query_cmd = { - .get_set_flag = cpu_to_le32(IWL_NONQOS_SEQ_SET), - .mac_id_n_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, - mvmvif->color)), - .value = cpu_to_le16(mvmvif->seqno), - }; - - /* return if called during restart, not resume from D3 */ - if (!mvmvif->seqno_valid) - return; - - mvmvif->seqno_valid = false; - - if (iwl_mvm_send_cmd_pdu(mvm, NON_QOS_TX_COUNTER_CMD, 0, - sizeof(query_cmd), &query_cmd)) - IWL_ERR(mvm, "failed to set non-QoS seqno\n"); -} - -static int iwl_mvm_switch_to_d3(struct iwl_mvm *mvm) -{ - iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_REGULAR, true); - - iwl_trans_stop_device(mvm->trans); - - /* - * Set the HW restart bit -- this is mostly true as we're - * going to load new firmware and reprogram that, though - * the reprogramming is going to be manual to avoid adding - * all the MACs that aren't support. - * We don't have to clear up everything though because the - * reprogramming is manual. When we resume, we'll actually - * go through a proper restart sequence again to switch - * back to the runtime firmware image. - */ - set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); - - mvm->ptk_ivlen = 0; - mvm->ptk_icvlen = 0; - mvm->ptk_ivlen = 0; - mvm->ptk_icvlen = 0; - - return iwl_mvm_load_d3_fw(mvm); -} - -static int -iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm, - struct cfg80211_wowlan *wowlan, - struct iwl_wowlan_config_cmd *wowlan_config_cmd, - struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif, - struct ieee80211_sta *ap_sta) -{ - int ret; - struct iwl_mvm_sta *mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta); - - /* TODO: wowlan_config_cmd->wowlan_ba_teardown_tids */ - - wowlan_config_cmd->is_11n_connection = - ap_sta->ht_cap.ht_supported; - - /* Query the last used seqno and set it */ - ret = iwl_mvm_get_last_nonqos_seq(mvm, vif); - if (ret < 0) - return ret; - - wowlan_config_cmd->non_qos_seq = cpu_to_le16(ret); - - iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, wowlan_config_cmd); - - if (wowlan->disconnect) - wowlan_config_cmd->wakeup_filter |= - cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS | - IWL_WOWLAN_WAKEUP_LINK_CHANGE); - if (wowlan->magic_pkt) - wowlan_config_cmd->wakeup_filter |= - cpu_to_le32(IWL_WOWLAN_WAKEUP_MAGIC_PACKET); - if (wowlan->gtk_rekey_failure) - wowlan_config_cmd->wakeup_filter |= - cpu_to_le32(IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL); - if (wowlan->eap_identity_req) - wowlan_config_cmd->wakeup_filter |= - cpu_to_le32(IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ); - if (wowlan->four_way_handshake) - wowlan_config_cmd->wakeup_filter |= - cpu_to_le32(IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE); - if (wowlan->n_patterns) - wowlan_config_cmd->wakeup_filter |= - cpu_to_le32(IWL_WOWLAN_WAKEUP_PATTERN_MATCH); - - if (wowlan->rfkill_release) - 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); - } - - return 0; -} - -static int -iwl_mvm_wowlan_config(struct iwl_mvm *mvm, - struct cfg80211_wowlan *wowlan, - struct iwl_wowlan_config_cmd *wowlan_config_cmd, - struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif, - struct ieee80211_sta *ap_sta) -{ - struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {}; - struct iwl_wowlan_tkip_params_cmd tkip_cmd = {}; - struct wowlan_key_data key_data = { - .use_rsc_tsc = false, - .tkip = &tkip_cmd, - .use_tkip = false, - }; - int ret; - - ret = iwl_mvm_switch_to_d3(mvm); - if (ret) - return ret; - - ret = iwl_mvm_d3_reprogram(mvm, vif, ap_sta); - if (ret) - return ret; - - key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL); - if (!key_data.rsc_tsc) - return -ENOMEM; - - if (!iwlwifi_mod_params.sw_crypto) { - /* - * This needs to be unlocked due to lock ordering - * constraints. Since we're in the suspend path - * that isn't really a problem though. - */ - mutex_unlock(&mvm->mutex); - ieee80211_iter_keys(mvm->hw, vif, - iwl_mvm_wowlan_program_keys, - &key_data); - mutex_lock(&mvm->mutex); - if (key_data.error) { - ret = -EIO; - goto out; - } - - if (key_data.use_rsc_tsc) { - struct iwl_host_cmd rsc_tsc_cmd = { - .id = WOWLAN_TSC_RSC_PARAM, - .data[0] = key_data.rsc_tsc, - .dataflags[0] = IWL_HCMD_DFL_NOCOPY, - .len[0] = sizeof(*key_data.rsc_tsc), - }; - - ret = iwl_mvm_send_cmd(mvm, &rsc_tsc_cmd); - if (ret) - goto out; - } - - if (key_data.use_tkip) { - ret = iwl_mvm_send_cmd_pdu(mvm, - WOWLAN_TKIP_PARAM, - 0, sizeof(tkip_cmd), - &tkip_cmd); - if (ret) - goto out; - } - - if (mvmvif->rekey_data.valid) { - memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd)); - memcpy(kek_kck_cmd.kck, mvmvif->rekey_data.kck, - NL80211_KCK_LEN); - kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN); - memcpy(kek_kck_cmd.kek, mvmvif->rekey_data.kek, - NL80211_KEK_LEN); - kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN); - kek_kck_cmd.replay_ctr = mvmvif->rekey_data.replay_ctr; - - ret = iwl_mvm_send_cmd_pdu(mvm, - WOWLAN_KEK_KCK_MATERIAL, 0, - sizeof(kek_kck_cmd), - &kek_kck_cmd); - if (ret) - goto out; - } - } - - ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0, - sizeof(*wowlan_config_cmd), - wowlan_config_cmd); - if (ret) - goto out; - - ret = iwl_mvm_send_patterns(mvm, wowlan); - if (ret) - goto out; - - ret = iwl_mvm_send_proto_offload(mvm, vif, false, 0); - if (ret) - goto out; - - ret = iwl_mvm_send_remote_wake_cfg(mvm, vif, wowlan->tcp); - -out: - kfree(key_data.rsc_tsc); - return ret; -} - -static int -iwl_mvm_netdetect_config(struct iwl_mvm *mvm, - struct cfg80211_wowlan *wowlan, - struct cfg80211_sched_scan_request *nd_config, - struct ieee80211_vif *vif) -{ - struct iwl_wowlan_config_cmd wowlan_config_cmd = {}; - int ret; - - ret = iwl_mvm_switch_to_d3(mvm); - if (ret) - return ret; - - /* rfkill release can be either for wowlan or netdetect */ - if (wowlan->rfkill_release) - wowlan_config_cmd.wakeup_filter |= - cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT); - - ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0, - sizeof(wowlan_config_cmd), - &wowlan_config_cmd); - if (ret) - return ret; - - ret = iwl_mvm_sched_scan_start(mvm, vif, nd_config, &mvm->nd_ies, - IWL_MVM_SCAN_NETDETECT); - if (ret) - return ret; - - if (WARN_ON(mvm->nd_match_sets || mvm->nd_channels)) - return -EBUSY; - - /* save the sched scan matchsets... */ - if (nd_config->n_match_sets) { - mvm->nd_match_sets = kmemdup(nd_config->match_sets, - sizeof(*nd_config->match_sets) * - nd_config->n_match_sets, - GFP_KERNEL); - if (mvm->nd_match_sets) - mvm->n_nd_match_sets = nd_config->n_match_sets; - } - - /* ...and the sched scan channels for later reporting */ - mvm->nd_channels = kmemdup(nd_config->channels, - sizeof(*nd_config->channels) * - nd_config->n_channels, - GFP_KERNEL); - if (mvm->nd_channels) - mvm->n_nd_channels = nd_config->n_channels; - - return 0; -} - -static void iwl_mvm_free_nd(struct iwl_mvm *mvm) -{ - kfree(mvm->nd_match_sets); - mvm->nd_match_sets = NULL; - mvm->n_nd_match_sets = 0; - kfree(mvm->nd_channels); - mvm->nd_channels = NULL; - mvm->n_nd_channels = 0; -} - -static int __iwl_mvm_suspend(struct ieee80211_hw *hw, - struct cfg80211_wowlan *wowlan, - bool test) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct ieee80211_vif *vif = NULL; - struct iwl_mvm_vif *mvmvif = NULL; - struct ieee80211_sta *ap_sta = NULL; - struct iwl_d3_manager_config d3_cfg_cmd_data = { - /* - * 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 iwl_host_cmd d3_cfg_cmd = { - .id = D3_CONFIG_CMD, - .flags = CMD_WANT_SKB, - .data[0] = &d3_cfg_cmd_data, - .len[0] = sizeof(d3_cfg_cmd_data), - }; - int ret; - int len __maybe_unused; - - if (!wowlan) { - /* - * mac80211 shouldn't get here, but for D3 test - * it doesn't warrant a warning - */ - WARN_ON(!test); - return -EINVAL; - } - - mutex_lock(&mvm->mutex); - - vif = iwl_mvm_get_bss_vif(mvm); - if (IS_ERR_OR_NULL(vif)) { - ret = 1; - goto out_noreset; - } - - mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) { - /* if we're not associated, this must be netdetect */ - if (!wowlan->nd_config && !mvm->nd_config) { - ret = 1; - goto out_noreset; - } - - ret = iwl_mvm_netdetect_config( - mvm, wowlan, wowlan->nd_config ?: mvm->nd_config, vif); - if (ret) - goto out; - - mvm->net_detect = true; - } else { - struct iwl_wowlan_config_cmd wowlan_config_cmd = {}; - - ap_sta = rcu_dereference_protected( - mvm->fw_id_to_mac_id[mvmvif->ap_sta_id], - lockdep_is_held(&mvm->mutex)); - if (IS_ERR_OR_NULL(ap_sta)) { - ret = -EINVAL; - goto out_noreset; - } - - ret = iwl_mvm_get_wowlan_config(mvm, wowlan, &wowlan_config_cmd, - vif, mvmvif, ap_sta); - if (ret) - goto out_noreset; - ret = iwl_mvm_wowlan_config(mvm, wowlan, &wowlan_config_cmd, - vif, mvmvif, ap_sta); - if (ret) - goto out; - - mvm->net_detect = false; - } - - ret = iwl_mvm_power_update_device(mvm); - if (ret) - goto out; - - ret = iwl_mvm_power_update_mac(mvm); - if (ret) - goto out; - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (mvm->d3_wake_sysassert) - d3_cfg_cmd_data.wakeup_flags |= - cpu_to_le32(IWL_WAKEUP_D3_CONFIG_FW_ERROR); -#endif - - /* must be last -- this switches firmware state */ - ret = iwl_mvm_send_cmd(mvm, &d3_cfg_cmd); - if (ret) - goto out; -#ifdef CONFIG_IWLWIFI_DEBUGFS - len = iwl_rx_packet_payload_len(d3_cfg_cmd.resp_pkt); - if (len >= sizeof(u32)) { - mvm->d3_test_pme_ptr = - le32_to_cpup((__le32 *)d3_cfg_cmd.resp_pkt->data); - } -#endif - iwl_free_resp(&d3_cfg_cmd); - - clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); - - iwl_trans_d3_suspend(mvm->trans, test); - out: - if (ret < 0) { - iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); - ieee80211_restart_hw(mvm->hw); - iwl_mvm_free_nd(mvm); - } - out_noreset: - mutex_unlock(&mvm->mutex); - - return ret; -} - -static int iwl_mvm_enter_d0i3_sync(struct iwl_mvm *mvm) -{ - struct iwl_notification_wait wait_d3; - static const u16 d3_notif[] = { D3_CONFIG_CMD }; - int ret; - - iwl_init_notification_wait(&mvm->notif_wait, &wait_d3, - d3_notif, ARRAY_SIZE(d3_notif), - NULL, NULL); - - ret = iwl_mvm_enter_d0i3(mvm->hw->priv); - if (ret) - goto remove_notif; - - ret = iwl_wait_notification(&mvm->notif_wait, &wait_d3, HZ); - WARN_ON_ONCE(ret); - return ret; - -remove_notif: - iwl_remove_notification(&mvm->notif_wait, &wait_d3); - return ret; -} - -int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int ret; - - /* make sure the d0i3 exit work is not pending */ - flush_work(&mvm->d0i3_exit_work); - - ret = iwl_trans_suspend(mvm->trans); - if (ret) - return ret; - - mvm->trans->wowlan_d0i3 = wowlan->any; - if (mvm->trans->wowlan_d0i3) { - /* 'any' trigger means d0i3 usage */ - if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) { - ret = iwl_mvm_enter_d0i3_sync(mvm); - - if (ret) - return ret; - } - - mutex_lock(&mvm->d0i3_suspend_mutex); - __set_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags); - mutex_unlock(&mvm->d0i3_suspend_mutex); - - iwl_trans_d3_suspend(mvm->trans, false); - - return 0; - } - - return __iwl_mvm_suspend(hw, wowlan, false); -} - -/* converted data from the different status responses */ -struct iwl_wowlan_status_data { - u16 pattern_number; - u16 qos_seq_ctr[8]; - u32 wakeup_reasons; - u32 wake_packet_length; - u32 wake_packet_bufsize; - const u8 *wake_packet; -}; - -static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_wowlan_status_data *status) -{ - struct sk_buff *pkt = NULL; - struct cfg80211_wowlan_wakeup wakeup = { - .pattern_idx = -1, - }; - struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup; - u32 reasons = status->wakeup_reasons; - - if (reasons == IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) { - wakeup_report = NULL; - goto report; - } - - if (reasons & IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET) - wakeup.magic_pkt = true; - - if (reasons & IWL_WOWLAN_WAKEUP_BY_PATTERN) - wakeup.pattern_idx = - status->pattern_number; - - if (reasons & (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | - IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH)) - wakeup.disconnect = true; - - if (reasons & IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE) - wakeup.gtk_rekey_failure = true; - - if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED) - wakeup.rfkill_release = true; - - if (reasons & IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST) - wakeup.eap_identity_req = true; - - 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 = status->wake_packet_bufsize; - int pktlen = status->wake_packet_length; - const u8 *pktdata = status->wake_packet; - struct ieee80211_hdr *hdr = (void *)pktdata; - int truncated = pktlen - pktsize; - - /* this would be a firmware bug */ - if (WARN_ON_ONCE(truncated < 0)) - truncated = 0; - - if (ieee80211_is_data(hdr->frame_control)) { - int hdrlen = ieee80211_hdrlen(hdr->frame_control); - int ivlen = 0, icvlen = 4; /* also FCS */ - - pkt = alloc_skb(pktsize, GFP_KERNEL); - if (!pkt) - goto report; - - memcpy(skb_put(pkt, hdrlen), pktdata, hdrlen); - pktdata += hdrlen; - pktsize -= hdrlen; - - if (ieee80211_has_protected(hdr->frame_control)) { - /* - * This is unlocked and using gtk_i(c)vlen, - * but since everything is under RTNL still - * that's not really a problem - changing - * it would be difficult. - */ - if (is_multicast_ether_addr(hdr->addr1)) { - ivlen = mvm->gtk_ivlen; - icvlen += mvm->gtk_icvlen; - } else { - ivlen = mvm->ptk_ivlen; - icvlen += mvm->ptk_icvlen; - } - } - - /* if truncated, FCS/ICV is (partially) gone */ - if (truncated >= icvlen) { - icvlen = 0; - truncated -= icvlen; - } else { - icvlen -= truncated; - truncated = 0; - } - - pktsize -= ivlen + icvlen; - pktdata += ivlen; - - memcpy(skb_put(pkt, pktsize), pktdata, pktsize); - - if (ieee80211_data_to_8023(pkt, vif->addr, vif->type)) - goto report; - wakeup.packet = pkt->data; - wakeup.packet_present_len = pkt->len; - wakeup.packet_len = pkt->len - truncated; - wakeup.packet_80211 = false; - } else { - int fcslen = 4; - - if (truncated >= 4) { - truncated -= 4; - fcslen = 0; - } else { - fcslen -= truncated; - truncated = 0; - } - pktsize -= fcslen; - wakeup.packet = status->wake_packet; - wakeup.packet_present_len = pktsize; - wakeup.packet_len = pktlen - truncated; - wakeup.packet_80211 = true; - } - } - - report: - ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL); - kfree_skb(pkt); -} - -static void iwl_mvm_aes_sc_to_seq(struct aes_sc *sc, - struct ieee80211_key_seq *seq) -{ - u64 pn; - - pn = le64_to_cpu(sc->pn); - seq->ccmp.pn[0] = pn >> 40; - seq->ccmp.pn[1] = pn >> 32; - seq->ccmp.pn[2] = pn >> 24; - seq->ccmp.pn[3] = pn >> 16; - seq->ccmp.pn[4] = pn >> 8; - seq->ccmp.pn[5] = pn; -} - -static void iwl_mvm_tkip_sc_to_seq(struct tkip_sc *sc, - struct ieee80211_key_seq *seq) -{ - seq->tkip.iv32 = le32_to_cpu(sc->iv32); - seq->tkip.iv16 = le16_to_cpu(sc->iv16); -} - -static void iwl_mvm_set_aes_rx_seq(struct aes_sc *scs, - struct ieee80211_key_conf *key) -{ - int tid; - - BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS); - - for (tid = 0; tid < IWL_NUM_RSC; tid++) { - struct ieee80211_key_seq seq = {}; - - iwl_mvm_aes_sc_to_seq(&scs[tid], &seq); - ieee80211_set_key_rx_seq(key, tid, &seq); - } -} - -static void iwl_mvm_set_tkip_rx_seq(struct tkip_sc *scs, - struct ieee80211_key_conf *key) -{ - int tid; - - BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS); - - for (tid = 0; tid < IWL_NUM_RSC; tid++) { - struct ieee80211_key_seq seq = {}; - - iwl_mvm_tkip_sc_to_seq(&scs[tid], &seq); - ieee80211_set_key_rx_seq(key, tid, &seq); - } -} - -static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key, - struct iwl_wowlan_status *status) -{ - union iwl_all_tsc_rsc *rsc = &status->gtk.rsc.all_tsc_rsc; - - switch (key->cipher) { - case WLAN_CIPHER_SUITE_CCMP: - iwl_mvm_set_aes_rx_seq(rsc->aes.multicast_rsc, key); - break; - case WLAN_CIPHER_SUITE_TKIP: - iwl_mvm_set_tkip_rx_seq(rsc->tkip.multicast_rsc, key); - break; - default: - WARN_ON(1); - } -} - -struct iwl_mvm_d3_gtk_iter_data { - struct iwl_wowlan_status *status; - void *last_gtk; - u32 cipher; - bool find_phase, unhandled_cipher; - int num_keys; -}; - -static void iwl_mvm_d3_update_gtks(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *key, - void *_data) -{ - struct iwl_mvm_d3_gtk_iter_data *data = _data; - - if (data->unhandled_cipher) - return; - - switch (key->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - /* ignore WEP completely, nothing to do */ - return; - case WLAN_CIPHER_SUITE_CCMP: - case WLAN_CIPHER_SUITE_TKIP: - /* we support these */ - break; - default: - /* everything else (even CMAC for MFP) - disconnect from AP */ - data->unhandled_cipher = true; - return; - } - - data->num_keys++; - - /* - * pairwise key - update sequence counters only; - * note that this assumes no TDLS sessions are active - */ - if (sta) { - struct ieee80211_key_seq seq = {}; - union iwl_all_tsc_rsc *sc = &data->status->gtk.rsc.all_tsc_rsc; - - if (data->find_phase) - return; - - switch (key->cipher) { - case WLAN_CIPHER_SUITE_CCMP: - iwl_mvm_set_aes_rx_seq(sc->aes.unicast_rsc, key); - atomic64_set(&key->tx_pn, le64_to_cpu(sc->aes.tsc.pn)); - break; - case WLAN_CIPHER_SUITE_TKIP: - iwl_mvm_tkip_sc_to_seq(&sc->tkip.tsc, &seq); - iwl_mvm_set_tkip_rx_seq(sc->tkip.unicast_rsc, key); - ieee80211_set_key_tx_seq(key, &seq); - break; - } - - /* that's it for this key */ - return; - } - - if (data->find_phase) { - data->last_gtk = key; - data->cipher = key->cipher; - return; - } - - if (data->status->num_of_gtk_rekeys) - ieee80211_remove_key(key); - else if (data->last_gtk == key) - iwl_mvm_set_key_rx_seq(key, data->status); -} - -static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_wowlan_status *status) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_d3_gtk_iter_data gtkdata = { - .status = status, - }; - u32 disconnection_reasons = - IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | - IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH; - - if (!status || !vif->bss_conf.bssid) - return false; - - if (le32_to_cpu(status->wakeup_reasons) & disconnection_reasons) - return false; - - /* find last GTK that we used initially, if any */ - gtkdata.find_phase = true; - ieee80211_iter_keys(mvm->hw, vif, - iwl_mvm_d3_update_gtks, >kdata); - /* not trying to keep connections with MFP/unhandled ciphers */ - if (gtkdata.unhandled_cipher) - return false; - if (!gtkdata.num_keys) - goto out; - if (!gtkdata.last_gtk) - return false; - - /* - * invalidate all other GTKs that might still exist and update - * the one that we used - */ - gtkdata.find_phase = false; - ieee80211_iter_keys(mvm->hw, vif, - iwl_mvm_d3_update_gtks, >kdata); - - if (status->num_of_gtk_rekeys) { - struct ieee80211_key_conf *key; - struct { - struct ieee80211_key_conf conf; - u8 key[32]; - } conf = { - .conf.cipher = gtkdata.cipher, - .conf.keyidx = status->gtk.key_index, - }; - - switch (gtkdata.cipher) { - case WLAN_CIPHER_SUITE_CCMP: - conf.conf.keylen = WLAN_KEY_LEN_CCMP; - memcpy(conf.conf.key, status->gtk.decrypt_key, - WLAN_KEY_LEN_CCMP); - break; - case WLAN_CIPHER_SUITE_TKIP: - conf.conf.keylen = WLAN_KEY_LEN_TKIP; - memcpy(conf.conf.key, status->gtk.decrypt_key, 16); - /* leave TX MIC key zeroed, we don't use it anyway */ - memcpy(conf.conf.key + - NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY, - status->gtk.tkip_mic_key, 8); - break; - } - - key = ieee80211_gtk_rekey_add(vif, &conf.conf); - if (IS_ERR(key)) - return false; - iwl_mvm_set_key_rx_seq(key, status); - } - - if (status->num_of_gtk_rekeys) { - __be64 replay_ctr = - cpu_to_be64(le64_to_cpu(status->replay_ctr)); - ieee80211_gtk_rekey_notify(vif, vif->bss_conf.bssid, - (void *)&replay_ctr, GFP_KERNEL); - } - -out: - mvmvif->seqno_valid = true; - /* +0x10 because the set API expects next-to-use, not last-used */ - mvmvif->seqno = le16_to_cpu(status->non_qos_seq_ctr) + 0x10; - - return true; -} - -static struct iwl_wowlan_status * -iwl_mvm_get_wakeup_status(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - u32 base = mvm->error_event_table; - struct error_table_start { - /* cf. struct iwl_error_event_table */ - u32 valid; - u32 error_id; - } err_info; - struct iwl_host_cmd cmd = { - .id = WOWLAN_GET_STATUSES, - .flags = CMD_WANT_SKB, - }; - struct iwl_wowlan_status *status, *fw_status; - int ret, len, status_size; - - iwl_trans_read_mem_bytes(mvm->trans, base, - &err_info, sizeof(err_info)); - - if (err_info.valid) { - IWL_INFO(mvm, "error table is valid (%d) with error (%d)\n", - err_info.valid, err_info.error_id); - if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN) { - struct cfg80211_wowlan_wakeup wakeup = { - .rfkill_release = true, - }; - ieee80211_report_wowlan_wakeup(vif, &wakeup, - GFP_KERNEL); - } - return ERR_PTR(-EIO); - } - - /* only for tracing for now */ - ret = iwl_mvm_send_cmd_pdu(mvm, OFFLOADS_QUERY_CMD, 0, 0, NULL); - if (ret) - IWL_ERR(mvm, "failed to query offload statistics (%d)\n", ret); - - ret = iwl_mvm_send_cmd(mvm, &cmd); - if (ret) { - IWL_ERR(mvm, "failed to query status (%d)\n", ret); - return ERR_PTR(ret); - } - - /* RF-kill already asserted again... */ - if (!cmd.resp_pkt) { - fw_status = ERR_PTR(-ERFKILL); - goto out_free_resp; - } - - status_size = sizeof(*fw_status); - - len = iwl_rx_packet_payload_len(cmd.resp_pkt); - if (len < status_size) { - IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); - fw_status = ERR_PTR(-EIO); - goto out_free_resp; - } - - status = (void *)cmd.resp_pkt->data; - if (len != (status_size + - ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4))) { - IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); - fw_status = ERR_PTR(-EIO); - goto out_free_resp; - } - - fw_status = kmemdup(status, len, GFP_KERNEL); - -out_free_resp: - iwl_free_resp(&cmd); - return fw_status; -} - -/* releases the MVM mutex */ -static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct iwl_wowlan_status_data status; - struct iwl_wowlan_status *fw_status; - int i; - bool keep; - struct ieee80211_sta *ap_sta; - struct iwl_mvm_sta *mvm_ap_sta; - - fw_status = iwl_mvm_get_wakeup_status(mvm, vif); - if (IS_ERR_OR_NULL(fw_status)) - goto out_unlock; - - status.pattern_number = le16_to_cpu(fw_status->pattern_number); - for (i = 0; i < 8; i++) - status.qos_seq_ctr[i] = - le16_to_cpu(fw_status->qos_seq_ctr[i]); - status.wakeup_reasons = le32_to_cpu(fw_status->wakeup_reasons); - status.wake_packet_length = - le32_to_cpu(fw_status->wake_packet_length); - status.wake_packet_bufsize = - le32_to_cpu(fw_status->wake_packet_bufsize); - status.wake_packet = fw_status->wake_packet; - - /* still at hard-coded place 0 for D3 image */ - ap_sta = rcu_dereference_protected( - mvm->fw_id_to_mac_id[0], - lockdep_is_held(&mvm->mutex)); - if (IS_ERR_OR_NULL(ap_sta)) - goto out_free; - - mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta); - for (i = 0; i < IWL_MAX_TID_COUNT; i++) { - u16 seq = status.qos_seq_ctr[i]; - /* firmware stores last-used value, we store next value */ - seq += 0x10; - mvm_ap_sta->tid_data[i].seq_number = seq; - } - - /* now we have all the data we need, unlock to avoid mac80211 issues */ - mutex_unlock(&mvm->mutex); - - iwl_mvm_report_wakeup_reasons(mvm, vif, &status); - - keep = iwl_mvm_setup_connection_keep(mvm, vif, fw_status); - - kfree(fw_status); - return keep; - -out_free: - kfree(fw_status); -out_unlock: - mutex_unlock(&mvm->mutex); - return false; -} - -struct iwl_mvm_nd_query_results { - u32 matched_profiles; - struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES]; -}; - -static int -iwl_mvm_netdetect_query_results(struct iwl_mvm *mvm, - struct iwl_mvm_nd_query_results *results) -{ - struct iwl_scan_offload_profiles_query *query; - struct iwl_host_cmd cmd = { - .id = SCAN_OFFLOAD_PROFILES_QUERY_CMD, - .flags = CMD_WANT_SKB, - }; - int ret, len; - - ret = iwl_mvm_send_cmd(mvm, &cmd); - if (ret) { - IWL_ERR(mvm, "failed to query matched profiles (%d)\n", ret); - return ret; - } - - /* RF-kill already asserted again... */ - if (!cmd.resp_pkt) { - ret = -ERFKILL; - goto out_free_resp; - } - - len = iwl_rx_packet_payload_len(cmd.resp_pkt); - if (len < sizeof(*query)) { - IWL_ERR(mvm, "Invalid scan offload profiles query response!\n"); - ret = -EIO; - goto out_free_resp; - } - - query = (void *)cmd.resp_pkt->data; - - results->matched_profiles = le32_to_cpu(query->matched_profiles); - memcpy(results->matches, query->matches, sizeof(results->matches)); - -#ifdef CONFIG_IWLWIFI_DEBUGFS - mvm->last_netdetect_scans = le32_to_cpu(query->n_scans_done); -#endif - -out_free_resp: - iwl_free_resp(&cmd); - return ret; -} - -static void iwl_mvm_query_netdetect_reasons(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct cfg80211_wowlan_nd_info *net_detect = NULL; - struct cfg80211_wowlan_wakeup wakeup = { - .pattern_idx = -1, - }; - struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup; - struct iwl_mvm_nd_query_results query; - struct iwl_wowlan_status *fw_status; - unsigned long matched_profiles; - u32 reasons = 0; - int i, j, n_matches, ret; - - fw_status = iwl_mvm_get_wakeup_status(mvm, vif); - if (!IS_ERR_OR_NULL(fw_status)) { - reasons = le32_to_cpu(fw_status->wakeup_reasons); - kfree(fw_status); - } - - if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED) - wakeup.rfkill_release = true; - - if (reasons != IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) - goto out; - - ret = iwl_mvm_netdetect_query_results(mvm, &query); - if (ret || !query.matched_profiles) { - wakeup_report = NULL; - goto out; - } - - matched_profiles = query.matched_profiles; - if (mvm->n_nd_match_sets) { - n_matches = hweight_long(matched_profiles); - } else { - IWL_ERR(mvm, "no net detect match information available\n"); - n_matches = 0; - } - - net_detect = kzalloc(sizeof(*net_detect) + - (n_matches * sizeof(net_detect->matches[0])), - GFP_KERNEL); - if (!net_detect || !n_matches) - goto out_report_nd; - - for_each_set_bit(i, &matched_profiles, mvm->n_nd_match_sets) { - struct iwl_scan_offload_profile_match *fw_match; - struct cfg80211_wowlan_nd_match *match; - int idx, n_channels = 0; - - fw_match = &query.matches[i]; - - for (j = 0; j < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN; j++) - n_channels += hweight8(fw_match->matching_channels[j]); - - match = kzalloc(sizeof(*match) + - (n_channels * sizeof(*match->channels)), - GFP_KERNEL); - if (!match) - goto out_report_nd; - - net_detect->matches[net_detect->n_matches++] = match; - - /* We inverted the order of the SSIDs in the scan - * request, so invert the index here. - */ - idx = mvm->n_nd_match_sets - i - 1; - match->ssid.ssid_len = mvm->nd_match_sets[idx].ssid.ssid_len; - memcpy(match->ssid.ssid, mvm->nd_match_sets[idx].ssid.ssid, - match->ssid.ssid_len); - - if (mvm->n_nd_channels < n_channels) - continue; - - for (j = 0; j < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN * 8; j++) - if (fw_match->matching_channels[j / 8] & (BIT(j % 8))) - match->channels[match->n_channels++] = - mvm->nd_channels[j]->center_freq; - } - -out_report_nd: - wakeup.net_detect = net_detect; -out: - iwl_mvm_free_nd(mvm); - - mutex_unlock(&mvm->mutex); - ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL); - - if (net_detect) { - for (i = 0; i < net_detect->n_matches; i++) - kfree(net_detect->matches[i]); - kfree(net_detect); - } -} - -static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm) -{ -#ifdef CONFIG_IWLWIFI_DEBUGFS - const struct fw_img *img = &mvm->fw->img[IWL_UCODE_WOWLAN]; - u32 len = img->sec[IWL_UCODE_SECTION_DATA].len; - u32 offs = img->sec[IWL_UCODE_SECTION_DATA].offset; - - if (!mvm->store_d3_resume_sram) - return; - - if (!mvm->d3_resume_sram) { - mvm->d3_resume_sram = kzalloc(len, GFP_KERNEL); - if (!mvm->d3_resume_sram) - return; - } - - iwl_trans_read_mem_bytes(mvm->trans, offs, mvm->d3_resume_sram, len); -#endif -} - -static void iwl_mvm_d3_disconnect_iter(void *data, u8 *mac, - struct ieee80211_vif *vif) -{ - /* skip the one we keep connection on */ - if (data == vif) - return; - - if (vif->type == NL80211_IFTYPE_STATION) - ieee80211_resume_disconnect(vif); -} - -static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) -{ - struct ieee80211_vif *vif = NULL; - int ret; - enum iwl_d3_status d3_status; - bool keep = false; - - mutex_lock(&mvm->mutex); - - /* get the BSS vif pointer again */ - vif = iwl_mvm_get_bss_vif(mvm); - if (IS_ERR_OR_NULL(vif)) - goto err; - - ret = iwl_trans_d3_resume(mvm->trans, &d3_status, test); - if (ret) - goto err; - - if (d3_status != IWL_D3_STATUS_ALIVE) { - IWL_INFO(mvm, "Device was reset during suspend\n"); - goto err; - } - - /* query SRAM first in case we want event logging */ - iwl_mvm_read_d3_sram(mvm); - - /* - * Query the current location and source from the D3 firmware so we - * can play it back when we re-intiailize the D0 firmware - */ - iwl_mvm_update_changed_regdom(mvm); - - if (mvm->net_detect) { - iwl_mvm_query_netdetect_reasons(mvm, vif); - /* has unlocked the mutex, so skip that */ - goto out; - } else { - keep = iwl_mvm_query_wakeup_reasons(mvm, vif); -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (keep) - mvm->keep_vif = vif; -#endif - /* has unlocked the mutex, so skip that */ - goto out_iterate; - } - -err: - iwl_mvm_free_nd(mvm); - mutex_unlock(&mvm->mutex); - -out_iterate: - if (!test) - ieee80211_iterate_active_interfaces_rtnl(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_d3_disconnect_iter, keep ? vif : NULL); - -out: - /* return 1 to reconfigure the device */ - set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); - set_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status); - - /* We always return 1, which causes mac80211 to do a reconfig - * with IEEE80211_RECONFIG_TYPE_RESTART. This type of - * reconfig calls iwl_mvm_restart_complete(), where we unref - * the IWL_MVM_REF_UCODE_DOWN, so we need to take the - * reference here. - */ - iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); - return 1; -} - -static int iwl_mvm_resume_d3(struct iwl_mvm *mvm) -{ - iwl_trans_resume(mvm->trans); - - return __iwl_mvm_resume(mvm, false); -} - -static int iwl_mvm_resume_d0i3(struct iwl_mvm *mvm) -{ - bool exit_now; - enum iwl_d3_status d3_status; - - iwl_trans_d3_resume(mvm->trans, &d3_status, false); - - /* - * make sure to clear D0I3_DEFER_WAKEUP before - * calling iwl_trans_resume(), which might wait - * for d0i3 exit completion. - */ - mutex_lock(&mvm->d0i3_suspend_mutex); - __clear_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags); - exit_now = __test_and_clear_bit(D0I3_PENDING_WAKEUP, - &mvm->d0i3_suspend_flags); - mutex_unlock(&mvm->d0i3_suspend_mutex); - if (exit_now) { - IWL_DEBUG_RPM(mvm, "Run deferred d0i3 exit\n"); - _iwl_mvm_exit_d0i3(mvm); - } - - iwl_trans_resume(mvm->trans); - - if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) { - int ret = iwl_mvm_exit_d0i3(mvm->hw->priv); - - if (ret) - return ret; - /* - * d0i3 exit will be deferred until reconfig_complete. - * make sure there we are out of d0i3. - */ - } - return 0; -} - -int iwl_mvm_resume(struct ieee80211_hw *hw) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - /* 'any' trigger means d0i3 was used */ - if (hw->wiphy->wowlan_config->any) - return iwl_mvm_resume_d0i3(mvm); - else - return iwl_mvm_resume_d3(mvm); -} - -void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - device_set_wakeup_enable(mvm->trans->dev, enabled); -} - -#ifdef CONFIG_IWLWIFI_DEBUGFS -static int iwl_mvm_d3_test_open(struct inode *inode, struct file *file) -{ - struct iwl_mvm *mvm = inode->i_private; - int err; - - if (mvm->d3_test_active) - return -EBUSY; - - file->private_data = inode->i_private; - - ieee80211_stop_queues(mvm->hw); - synchronize_net(); - - /* start pseudo D3 */ - rtnl_lock(); - err = __iwl_mvm_suspend(mvm->hw, mvm->hw->wiphy->wowlan_config, true); - rtnl_unlock(); - if (err > 0) - err = -EINVAL; - if (err) { - ieee80211_wake_queues(mvm->hw); - return err; - } - mvm->d3_test_active = true; - mvm->keep_vif = NULL; - return 0; -} - -static ssize_t iwl_mvm_d3_test_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - u32 pme_asserted; - - while (true) { - /* read pme_ptr if available */ - if (mvm->d3_test_pme_ptr) { - pme_asserted = iwl_trans_read_mem32(mvm->trans, - mvm->d3_test_pme_ptr); - if (pme_asserted) - break; - } - - if (msleep_interruptible(100)) - break; - } - - return 0; -} - -static void iwl_mvm_d3_test_disconn_work_iter(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - /* skip the one we keep connection on */ - if (_data == vif) - return; - - if (vif->type == NL80211_IFTYPE_STATION) - ieee80211_connection_loss(vif); -} - -static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file) -{ - struct iwl_mvm *mvm = inode->i_private; - int remaining_time = 10; - - mvm->d3_test_active = false; - rtnl_lock(); - __iwl_mvm_resume(mvm, true); - rtnl_unlock(); - iwl_abort_notification_waits(&mvm->notif_wait); - ieee80211_restart_hw(mvm->hw); - - /* wait for restart and disconnect all interfaces */ - while (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && - remaining_time > 0) { - remaining_time--; - msleep(1000); - } - - if (remaining_time == 0) - IWL_ERR(mvm, "Timed out waiting for HW restart to finish!\n"); - - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_d3_test_disconn_work_iter, mvm->keep_vif); - - ieee80211_wake_queues(mvm->hw); - - return 0; -} - -const struct file_operations iwl_dbgfs_d3_test_ops = { - .llseek = no_llseek, - .open = iwl_mvm_d3_test_open, - .read = iwl_mvm_d3_test_read, - .release = iwl_mvm_d3_test_release, -}; -#endif diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c deleted file mode 100644 index 7904b41a0..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c +++ /dev/null @@ -1,1483 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * 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 "mvm.h" -#include "fw-api-tof.h" -#include "debugfs.h" - -static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - enum iwl_dbgfs_pm_mask param, int val) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_dbgfs_pm *dbgfs_pm = &mvmvif->dbgfs_pm; - - dbgfs_pm->mask |= param; - - switch (param) { - case MVM_DEBUGFS_PM_KEEP_ALIVE: { - int dtimper = vif->bss_conf.dtim_period ?: 1; - int dtimper_msec = dtimper * vif->bss_conf.beacon_int; - - IWL_DEBUG_POWER(mvm, "debugfs: set keep_alive= %d sec\n", val); - if (val * MSEC_PER_SEC < 3 * dtimper_msec) - IWL_WARN(mvm, - "debugfs: keep alive period (%ld msec) is less than minimum required (%d msec)\n", - val * MSEC_PER_SEC, 3 * dtimper_msec); - dbgfs_pm->keep_alive_seconds = val; - break; - } - case MVM_DEBUGFS_PM_SKIP_OVER_DTIM: - IWL_DEBUG_POWER(mvm, "skip_over_dtim %s\n", - val ? "enabled" : "disabled"); - dbgfs_pm->skip_over_dtim = val; - break; - case MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS: - IWL_DEBUG_POWER(mvm, "skip_dtim_periods=%d\n", val); - dbgfs_pm->skip_dtim_periods = val; - break; - case MVM_DEBUGFS_PM_RX_DATA_TIMEOUT: - IWL_DEBUG_POWER(mvm, "rx_data_timeout=%d\n", val); - dbgfs_pm->rx_data_timeout = val; - break; - case MVM_DEBUGFS_PM_TX_DATA_TIMEOUT: - IWL_DEBUG_POWER(mvm, "tx_data_timeout=%d\n", val); - dbgfs_pm->tx_data_timeout = val; - break; - case MVM_DEBUGFS_PM_LPRX_ENA: - IWL_DEBUG_POWER(mvm, "lprx %s\n", val ? "enabled" : "disabled"); - dbgfs_pm->lprx_ena = val; - break; - case MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD: - IWL_DEBUG_POWER(mvm, "lprx_rssi_threshold=%d\n", val); - dbgfs_pm->lprx_rssi_threshold = val; - break; - case MVM_DEBUGFS_PM_SNOOZE_ENABLE: - IWL_DEBUG_POWER(mvm, "snooze_enable=%d\n", val); - dbgfs_pm->snooze_ena = val; - break; - case MVM_DEBUGFS_PM_UAPSD_MISBEHAVING: - IWL_DEBUG_POWER(mvm, "uapsd_misbehaving_enable=%d\n", val); - dbgfs_pm->uapsd_misbehaving = val; - break; - case MVM_DEBUGFS_PM_USE_PS_POLL: - IWL_DEBUG_POWER(mvm, "use_ps_poll=%d\n", val); - dbgfs_pm->use_ps_poll = val; - break; - } -} - -static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - enum iwl_dbgfs_pm_mask param; - int val, ret; - - if (!strncmp("keep_alive=", buf, 11)) { - if (sscanf(buf + 11, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_KEEP_ALIVE; - } else if (!strncmp("skip_over_dtim=", buf, 15)) { - if (sscanf(buf + 15, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_SKIP_OVER_DTIM; - } else if (!strncmp("skip_dtim_periods=", buf, 18)) { - if (sscanf(buf + 18, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS; - } else if (!strncmp("rx_data_timeout=", buf, 16)) { - if (sscanf(buf + 16, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_RX_DATA_TIMEOUT; - } else if (!strncmp("tx_data_timeout=", buf, 16)) { - if (sscanf(buf + 16, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT; - } else if (!strncmp("lprx=", buf, 5)) { - if (sscanf(buf + 5, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_LPRX_ENA; - } else if (!strncmp("lprx_rssi_threshold=", buf, 20)) { - if (sscanf(buf + 20, "%d", &val) != 1) - return -EINVAL; - if (val > POWER_LPRX_RSSI_THRESHOLD_MAX || val < - POWER_LPRX_RSSI_THRESHOLD_MIN) - return -EINVAL; - param = MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD; - } else if (!strncmp("snooze_enable=", buf, 14)) { - if (sscanf(buf + 14, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_SNOOZE_ENABLE; - } else if (!strncmp("uapsd_misbehaving=", buf, 18)) { - if (sscanf(buf + 18, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_UAPSD_MISBEHAVING; - } else if (!strncmp("use_ps_poll=", buf, 12)) { - if (sscanf(buf + 12, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_USE_PS_POLL; - } else { - return -EINVAL; - } - - mutex_lock(&mvm->mutex); - iwl_dbgfs_update_pm(mvm, vif, param, val); - ret = iwl_mvm_power_update_mac(mvm); - mutex_unlock(&mvm->mutex); - - return ret ?: count; -} - -static ssize_t iwl_dbgfs_tx_pwr_lmt_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_vif *vif = file->private_data; - char buf[64]; - int bufsz = sizeof(buf); - int pos; - - pos = scnprintf(buf, bufsz, "bss limit = %d\n", - vif->bss_conf.txpower); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_pm_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->mvm; - char buf[512]; - int bufsz = sizeof(buf); - int pos; - - pos = iwl_mvm_power_mac_dbgfs_read(mvm, vif, buf, bufsz); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -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->mvm; - 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; - - switch (ieee80211_vif_type_p2p(vif)) { - case NL80211_IFTYPE_ADHOC: - pos += scnprintf(buf+pos, bufsz-pos, "type: ibss\n"); - break; - case NL80211_IFTYPE_STATION: - pos += scnprintf(buf+pos, bufsz-pos, "type: bss\n"); - break; - case NL80211_IFTYPE_AP: - pos += scnprintf(buf+pos, bufsz-pos, "type: ap\n"); - break; - case NL80211_IFTYPE_P2P_CLIENT: - pos += scnprintf(buf+pos, bufsz-pos, "type: p2p client\n"); - break; - case NL80211_IFTYPE_P2P_GO: - pos += scnprintf(buf+pos, bufsz-pos, "type: p2p go\n"); - break; - case NL80211_IFTYPE_P2P_DEVICE: - pos += scnprintf(buf+pos, bufsz-pos, "type: p2p dev\n"); - break; - default: - break; - } - - 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; - - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[ap_sta_id], - lockdep_is_held(&mvm->mutex)); - if (!IS_ERR_OR_NULL(sta)) { - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - - 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); -} - -static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif, - enum iwl_dbgfs_bf_mask param, int value) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf; - - dbgfs_bf->mask |= param; - - switch (param) { - case MVM_DEBUGFS_BF_ENERGY_DELTA: - dbgfs_bf->bf_energy_delta = value; - break; - case MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA: - dbgfs_bf->bf_roaming_energy_delta = value; - break; - case MVM_DEBUGFS_BF_ROAMING_STATE: - dbgfs_bf->bf_roaming_state = value; - break; - case MVM_DEBUGFS_BF_TEMP_THRESHOLD: - dbgfs_bf->bf_temp_threshold = value; - break; - case MVM_DEBUGFS_BF_TEMP_FAST_FILTER: - dbgfs_bf->bf_temp_fast_filter = value; - break; - case MVM_DEBUGFS_BF_TEMP_SLOW_FILTER: - dbgfs_bf->bf_temp_slow_filter = value; - break; - case MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER: - dbgfs_bf->bf_enable_beacon_filter = value; - break; - case MVM_DEBUGFS_BF_DEBUG_FLAG: - dbgfs_bf->bf_debug_flag = value; - break; - case MVM_DEBUGFS_BF_ESCAPE_TIMER: - dbgfs_bf->bf_escape_timer = value; - break; - case MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT: - dbgfs_bf->ba_enable_beacon_abort = value; - break; - case MVM_DEBUGFS_BA_ESCAPE_TIMER: - dbgfs_bf->ba_escape_timer = value; - break; - } -} - -static ssize_t iwl_dbgfs_bf_params_write(struct ieee80211_vif *vif, char *buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - enum iwl_dbgfs_bf_mask param; - int value, ret = 0; - - if (!strncmp("bf_energy_delta=", buf, 16)) { - if (sscanf(buf+16, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_ENERGY_DELTA_MIN || - value > IWL_BF_ENERGY_DELTA_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_ENERGY_DELTA; - } else if (!strncmp("bf_roaming_energy_delta=", buf, 24)) { - if (sscanf(buf+24, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_ROAMING_ENERGY_DELTA_MIN || - value > IWL_BF_ROAMING_ENERGY_DELTA_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA; - } else if (!strncmp("bf_roaming_state=", buf, 17)) { - if (sscanf(buf+17, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_ROAMING_STATE_MIN || - value > IWL_BF_ROAMING_STATE_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_ROAMING_STATE; - } else if (!strncmp("bf_temp_threshold=", buf, 18)) { - if (sscanf(buf+18, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_TEMP_THRESHOLD_MIN || - value > IWL_BF_TEMP_THRESHOLD_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_TEMP_THRESHOLD; - } else if (!strncmp("bf_temp_fast_filter=", buf, 20)) { - if (sscanf(buf+20, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_TEMP_FAST_FILTER_MIN || - value > IWL_BF_TEMP_FAST_FILTER_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_TEMP_FAST_FILTER; - } else if (!strncmp("bf_temp_slow_filter=", buf, 20)) { - if (sscanf(buf+20, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_TEMP_SLOW_FILTER_MIN || - value > IWL_BF_TEMP_SLOW_FILTER_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_TEMP_SLOW_FILTER; - } else if (!strncmp("bf_enable_beacon_filter=", buf, 24)) { - if (sscanf(buf+24, "%d", &value) != 1) - return -EINVAL; - if (value < 0 || value > 1) - return -EINVAL; - param = MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER; - } else if (!strncmp("bf_debug_flag=", buf, 14)) { - if (sscanf(buf+14, "%d", &value) != 1) - return -EINVAL; - if (value < 0 || value > 1) - return -EINVAL; - param = MVM_DEBUGFS_BF_DEBUG_FLAG; - } else if (!strncmp("bf_escape_timer=", buf, 16)) { - if (sscanf(buf+16, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_ESCAPE_TIMER_MIN || - value > IWL_BF_ESCAPE_TIMER_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_ESCAPE_TIMER; - } else if (!strncmp("ba_escape_timer=", buf, 16)) { - if (sscanf(buf+16, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BA_ESCAPE_TIMER_MIN || - value > IWL_BA_ESCAPE_TIMER_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BA_ESCAPE_TIMER; - } else if (!strncmp("ba_enable_beacon_abort=", buf, 23)) { - if (sscanf(buf+23, "%d", &value) != 1) - return -EINVAL; - if (value < 0 || value > 1) - return -EINVAL; - param = MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT; - } else { - return -EINVAL; - } - - mutex_lock(&mvm->mutex); - iwl_dbgfs_update_bf(vif, param, value); - if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value) - ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); - else - ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0); - mutex_unlock(&mvm->mutex); - - return ret ?: count; -} - -static ssize_t iwl_dbgfs_bf_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); - char buf[256]; - int pos = 0; - const size_t bufsz = sizeof(buf); - struct iwl_beacon_filter_cmd cmd = { - IWL_BF_CMD_CONFIG_DEFAULTS, - .bf_enable_beacon_filter = - cpu_to_le32(IWL_BF_ENABLE_BEACON_FILTER_DEFAULT), - .ba_enable_beacon_abort = - cpu_to_le32(IWL_BA_ENABLE_BEACON_ABORT_DEFAULT), - }; - - iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); - if (mvmvif->bf_data.bf_enabled) - cmd.bf_enable_beacon_filter = cpu_to_le32(1); - else - cmd.bf_enable_beacon_filter = 0; - - pos += scnprintf(buf+pos, bufsz-pos, "bf_energy_delta = %d\n", - le32_to_cpu(cmd.bf_energy_delta)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_energy_delta = %d\n", - le32_to_cpu(cmd.bf_roaming_energy_delta)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_state = %d\n", - le32_to_cpu(cmd.bf_roaming_state)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_threshold = %d\n", - le32_to_cpu(cmd.bf_temp_threshold)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_fast_filter = %d\n", - le32_to_cpu(cmd.bf_temp_fast_filter)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_slow_filter = %d\n", - le32_to_cpu(cmd.bf_temp_slow_filter)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_enable_beacon_filter = %d\n", - le32_to_cpu(cmd.bf_enable_beacon_filter)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_debug_flag = %d\n", - le32_to_cpu(cmd.bf_debug_flag)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_escape_timer = %d\n", - le32_to_cpu(cmd.bf_escape_timer)); - pos += scnprintf(buf+pos, bufsz-pos, "ba_escape_timer = %d\n", - le32_to_cpu(cmd.ba_escape_timer)); - pos += scnprintf(buf+pos, bufsz-pos, "ba_enable_beacon_abort = %d\n", - le32_to_cpu(cmd.ba_enable_beacon_abort)); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static inline char *iwl_dbgfs_is_match(char *name, char *buf) -{ - int len = strlen(name); - - return !strncmp(name, buf, len) ? buf + len : NULL; -} - -static ssize_t iwl_dbgfs_tof_enable_write(struct ieee80211_vif *vif, - char *buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - u32 value; - int ret = -EINVAL; - char *data; - - mutex_lock(&mvm->mutex); - - data = iwl_dbgfs_is_match("tof_disabled=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.tof_cfg.tof_disabled = value; - goto out; - } - - data = iwl_dbgfs_is_match("one_sided_disabled=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.tof_cfg.one_sided_disabled = value; - goto out; - } - - data = iwl_dbgfs_is_match("is_debug_mode=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.tof_cfg.is_debug_mode = value; - goto out; - } - - data = iwl_dbgfs_is_match("is_buf=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.tof_cfg.is_buf_required = value; - goto out; - } - - data = iwl_dbgfs_is_match("send_tof_cfg=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0 && value) { - ret = iwl_mvm_tof_config_cmd(mvm); - goto out; - } - } - -out: - mutex_unlock(&mvm->mutex); - - return ret ?: count; -} - -static ssize_t iwl_dbgfs_tof_enable_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->mvm; - char buf[256]; - int pos = 0; - const size_t bufsz = sizeof(buf); - struct iwl_tof_config_cmd *cmd; - - cmd = &mvm->tof_data.tof_cfg; - - mutex_lock(&mvm->mutex); - - pos += scnprintf(buf + pos, bufsz - pos, "tof_disabled = %d\n", - cmd->tof_disabled); - pos += scnprintf(buf + pos, bufsz - pos, "one_sided_disabled = %d\n", - cmd->one_sided_disabled); - pos += scnprintf(buf + pos, bufsz - pos, "is_debug_mode = %d\n", - cmd->is_debug_mode); - pos += scnprintf(buf + pos, bufsz - pos, "is_buf_required = %d\n", - cmd->is_buf_required); - - mutex_unlock(&mvm->mutex); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_tof_responder_params_write(struct ieee80211_vif *vif, - char *buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - u32 value; - int ret = 0; - char *data; - - mutex_lock(&mvm->mutex); - - data = iwl_dbgfs_is_match("burst_period=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (!ret) - mvm->tof_data.responder_cfg.burst_period = - cpu_to_le16(value); - goto out; - } - - data = iwl_dbgfs_is_match("min_delta_ftm=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.min_delta_ftm = value; - goto out; - } - - data = iwl_dbgfs_is_match("burst_duration=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.burst_duration = value; - goto out; - } - - data = iwl_dbgfs_is_match("num_of_burst_exp=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.num_of_burst_exp = value; - goto out; - } - - data = iwl_dbgfs_is_match("abort_responder=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.abort_responder = value; - goto out; - } - - data = iwl_dbgfs_is_match("get_ch_est=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.get_ch_est = value; - goto out; - } - - data = iwl_dbgfs_is_match("recv_sta_req_params=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.recv_sta_req_params = value; - goto out; - } - - data = iwl_dbgfs_is_match("channel_num=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.channel_num = value; - goto out; - } - - data = iwl_dbgfs_is_match("bandwidth=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.bandwidth = value; - goto out; - } - - data = iwl_dbgfs_is_match("rate=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.rate = value; - goto out; - } - - data = iwl_dbgfs_is_match("bssid=", buf); - if (data) { - u8 *mac = mvm->tof_data.responder_cfg.bssid; - - if (!mac_pton(data, mac)) { - ret = -EINVAL; - goto out; - } - } - - data = iwl_dbgfs_is_match("tsf_timer_offset_msecs=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.tsf_timer_offset_msecs = - cpu_to_le16(value); - goto out; - } - - data = iwl_dbgfs_is_match("toa_offset=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.toa_offset = - cpu_to_le16(value); - goto out; - } - - data = iwl_dbgfs_is_match("center_freq=", buf); - if (data) { - struct iwl_tof_responder_config_cmd *cmd = - &mvm->tof_data.responder_cfg; - - ret = kstrtou32(data, 10, &value); - if (ret == 0 && value) { - enum ieee80211_band band = (cmd->channel_num <= 14) ? - IEEE80211_BAND_2GHZ : - IEEE80211_BAND_5GHZ; - struct ieee80211_channel chn = { - .band = band, - .center_freq = ieee80211_channel_to_frequency( - cmd->channel_num, band), - }; - struct cfg80211_chan_def chandef = { - .chan = &chn, - .center_freq1 = - ieee80211_channel_to_frequency(value, - band), - }; - - cmd->ctrl_ch_position = iwl_mvm_get_ctrl_pos(&chandef); - } - goto out; - } - - data = iwl_dbgfs_is_match("ftm_per_burst=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.ftm_per_burst = value; - goto out; - } - - data = iwl_dbgfs_is_match("ftm_resp_ts_avail=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.ftm_resp_ts_avail = value; - goto out; - } - - data = iwl_dbgfs_is_match("asap_mode=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.asap_mode = value; - goto out; - } - - data = iwl_dbgfs_is_match("send_responder_cfg=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0 && value) { - ret = iwl_mvm_tof_responder_cmd(mvm, vif); - goto out; - } - } - -out: - mutex_unlock(&mvm->mutex); - - return ret ?: count; -} - -static ssize_t iwl_dbgfs_tof_responder_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->mvm; - char buf[256]; - int pos = 0; - const size_t bufsz = sizeof(buf); - struct iwl_tof_responder_config_cmd *cmd; - - cmd = &mvm->tof_data.responder_cfg; - - mutex_lock(&mvm->mutex); - - pos += scnprintf(buf + pos, bufsz - pos, "burst_period = %d\n", - le16_to_cpu(cmd->burst_period)); - pos += scnprintf(buf + pos, bufsz - pos, "burst_duration = %d\n", - cmd->burst_duration); - pos += scnprintf(buf + pos, bufsz - pos, "bandwidth = %d\n", - cmd->bandwidth); - pos += scnprintf(buf + pos, bufsz - pos, "channel_num = %d\n", - cmd->channel_num); - pos += scnprintf(buf + pos, bufsz - pos, "ctrl_ch_position = 0x%x\n", - cmd->ctrl_ch_position); - pos += scnprintf(buf + pos, bufsz - pos, "bssid = %pM\n", - cmd->bssid); - pos += scnprintf(buf + pos, bufsz - pos, "min_delta_ftm = %d\n", - cmd->min_delta_ftm); - pos += scnprintf(buf + pos, bufsz - pos, "num_of_burst_exp = %d\n", - cmd->num_of_burst_exp); - pos += scnprintf(buf + pos, bufsz - pos, "rate = %d\n", cmd->rate); - pos += scnprintf(buf + pos, bufsz - pos, "abort_responder = %d\n", - cmd->abort_responder); - pos += scnprintf(buf + pos, bufsz - pos, "get_ch_est = %d\n", - cmd->get_ch_est); - pos += scnprintf(buf + pos, bufsz - pos, "recv_sta_req_params = %d\n", - cmd->recv_sta_req_params); - pos += scnprintf(buf + pos, bufsz - pos, "ftm_per_burst = %d\n", - cmd->ftm_per_burst); - pos += scnprintf(buf + pos, bufsz - pos, "ftm_resp_ts_avail = %d\n", - cmd->ftm_resp_ts_avail); - pos += scnprintf(buf + pos, bufsz - pos, "asap_mode = %d\n", - cmd->asap_mode); - pos += scnprintf(buf + pos, bufsz - pos, - "tsf_timer_offset_msecs = %d\n", - le16_to_cpu(cmd->tsf_timer_offset_msecs)); - pos += scnprintf(buf + pos, bufsz - pos, "toa_offset = %d\n", - le16_to_cpu(cmd->toa_offset)); - - mutex_unlock(&mvm->mutex); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_tof_range_request_write(struct ieee80211_vif *vif, - char *buf, size_t count, - loff_t *ppos) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - u32 value; - int ret = 0; - char *data; - - mutex_lock(&mvm->mutex); - - data = iwl_dbgfs_is_match("request_id=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.range_req.request_id = value; - goto out; - } - - data = iwl_dbgfs_is_match("initiator=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.range_req.initiator = value; - goto out; - } - - data = iwl_dbgfs_is_match("one_sided_los_disable=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.range_req.one_sided_los_disable = value; - goto out; - } - - data = iwl_dbgfs_is_match("req_timeout=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.range_req.req_timeout = value; - goto out; - } - - data = iwl_dbgfs_is_match("report_policy=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.range_req.report_policy = value; - goto out; - } - - data = iwl_dbgfs_is_match("macaddr_random=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.range_req.macaddr_random = value; - goto out; - } - - data = iwl_dbgfs_is_match("num_of_ap=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.range_req.num_of_ap = value; - goto out; - } - - data = iwl_dbgfs_is_match("macaddr_template=", buf); - if (data) { - u8 mac[ETH_ALEN]; - - if (!mac_pton(data, mac)) { - ret = -EINVAL; - goto out; - } - memcpy(mvm->tof_data.range_req.macaddr_template, mac, ETH_ALEN); - goto out; - } - - data = iwl_dbgfs_is_match("macaddr_mask=", buf); - if (data) { - u8 mac[ETH_ALEN]; - - if (!mac_pton(data, mac)) { - ret = -EINVAL; - goto out; - } - memcpy(mvm->tof_data.range_req.macaddr_mask, mac, ETH_ALEN); - goto out; - } - - data = iwl_dbgfs_is_match("ap=", buf); - if (data) { - struct iwl_tof_range_req_ap_entry ap = {}; - int size = sizeof(struct iwl_tof_range_req_ap_entry); - u16 burst_period; - u8 *mac = ap.bssid; - unsigned int i; - - if (sscanf(data, "%u %hhd %hhd %hhd" - "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx" - "%hhd %hhd %hd" - "%hhd %hhd %d" - "%hhx %hhd %hhd %hhd", - &i, &ap.channel_num, &ap.bandwidth, - &ap.ctrl_ch_position, - mac, mac + 1, mac + 2, mac + 3, mac + 4, mac + 5, - &ap.measure_type, &ap.num_of_bursts, - &burst_period, - &ap.samples_per_burst, &ap.retries_per_sample, - &ap.tsf_delta, &ap.location_req, &ap.asap_mode, - &ap.enable_dyn_ack, &ap.rssi) != 20) { - ret = -EINVAL; - goto out; - } - if (i >= IWL_MVM_TOF_MAX_APS) { - IWL_ERR(mvm, "Invalid AP index %d\n", i); - ret = -EINVAL; - goto out; - } - - ap.burst_period = cpu_to_le16(burst_period); - - memcpy(&mvm->tof_data.range_req.ap[i], &ap, size); - goto out; - } - - data = iwl_dbgfs_is_match("send_range_request=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0 && value) - ret = iwl_mvm_tof_range_request_cmd(mvm, vif); - goto out; - } - - ret = -EINVAL; -out: - mutex_unlock(&mvm->mutex); - return ret ?: count; -} - -static ssize_t iwl_dbgfs_tof_range_request_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->mvm; - char buf[512]; - int pos = 0; - const size_t bufsz = sizeof(buf); - struct iwl_tof_range_req_cmd *cmd; - int i; - - cmd = &mvm->tof_data.range_req; - - mutex_lock(&mvm->mutex); - - pos += scnprintf(buf + pos, bufsz - pos, "request_id= %d\n", - cmd->request_id); - pos += scnprintf(buf + pos, bufsz - pos, "initiator= %d\n", - cmd->initiator); - pos += scnprintf(buf + pos, bufsz - pos, "one_sided_los_disable = %d\n", - cmd->one_sided_los_disable); - pos += scnprintf(buf + pos, bufsz - pos, "req_timeout= %d\n", - cmd->req_timeout); - pos += scnprintf(buf + pos, bufsz - pos, "report_policy= %d\n", - cmd->report_policy); - pos += scnprintf(buf + pos, bufsz - pos, "macaddr_random= %d\n", - cmd->macaddr_random); - pos += scnprintf(buf + pos, bufsz - pos, "macaddr_template= %pM\n", - cmd->macaddr_template); - pos += scnprintf(buf + pos, bufsz - pos, "macaddr_mask= %pM\n", - cmd->macaddr_mask); - pos += scnprintf(buf + pos, bufsz - pos, "num_of_ap= %d\n", - cmd->num_of_ap); - for (i = 0; i < cmd->num_of_ap; i++) { - struct iwl_tof_range_req_ap_entry *ap = &cmd->ap[i]; - - pos += scnprintf(buf + pos, bufsz - pos, - "ap %.2d: channel_num=%hhd bw=%hhd" - " control=%hhd bssid=%pM type=%hhd" - " num_of_bursts=%hhd burst_period=%hd ftm=%hhd" - " retries=%hhd tsf_delta=%d" - " tsf_delta_direction=%hhd location_req=0x%hhx " - " asap=%hhd enable=%hhd rssi=%hhd\n", - i, ap->channel_num, ap->bandwidth, - ap->ctrl_ch_position, ap->bssid, - ap->measure_type, ap->num_of_bursts, - ap->burst_period, ap->samples_per_burst, - ap->retries_per_sample, ap->tsf_delta, - ap->tsf_delta_direction, - ap->location_req, ap->asap_mode, - ap->enable_dyn_ack, ap->rssi); - } - - mutex_unlock(&mvm->mutex); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_tof_range_req_ext_write(struct ieee80211_vif *vif, - char *buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - u32 value; - int ret = 0; - char *data; - - mutex_lock(&mvm->mutex); - - data = iwl_dbgfs_is_match("tsf_timer_offset_msec=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.range_req_ext.tsf_timer_offset_msec = - cpu_to_le16(value); - goto out; - } - - data = iwl_dbgfs_is_match("min_delta_ftm=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.range_req_ext.min_delta_ftm = value; - goto out; - } - - data = iwl_dbgfs_is_match("ftm_format_and_bw20M=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.range_req_ext.ftm_format_and_bw20M = - value; - goto out; - } - - data = iwl_dbgfs_is_match("ftm_format_and_bw40M=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.range_req_ext.ftm_format_and_bw40M = - value; - goto out; - } - - data = iwl_dbgfs_is_match("ftm_format_and_bw80M=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.range_req_ext.ftm_format_and_bw80M = - value; - goto out; - } - - data = iwl_dbgfs_is_match("send_range_req_ext=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0 && value) - ret = iwl_mvm_tof_range_request_ext_cmd(mvm, vif); - goto out; - } - - ret = -EINVAL; -out: - mutex_unlock(&mvm->mutex); - return ret ?: count; -} - -static ssize_t iwl_dbgfs_tof_range_req_ext_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->mvm; - char buf[256]; - int pos = 0; - const size_t bufsz = sizeof(buf); - struct iwl_tof_range_req_ext_cmd *cmd; - - cmd = &mvm->tof_data.range_req_ext; - - mutex_lock(&mvm->mutex); - - pos += scnprintf(buf + pos, bufsz - pos, - "tsf_timer_offset_msec = %hd\n", - cmd->tsf_timer_offset_msec); - pos += scnprintf(buf + pos, bufsz - pos, "min_delta_ftm = %hhd\n", - cmd->min_delta_ftm); - pos += scnprintf(buf + pos, bufsz - pos, - "ftm_format_and_bw20M = %hhd\n", - cmd->ftm_format_and_bw20M); - pos += scnprintf(buf + pos, bufsz - pos, - "ftm_format_and_bw40M = %hhd\n", - cmd->ftm_format_and_bw40M); - pos += scnprintf(buf + pos, bufsz - pos, - "ftm_format_and_bw80M = %hhd\n", - cmd->ftm_format_and_bw80M); - - mutex_unlock(&mvm->mutex); - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_tof_range_abort_write(struct ieee80211_vif *vif, - char *buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - u32 value; - int abort_id, ret = 0; - char *data; - - mutex_lock(&mvm->mutex); - - data = iwl_dbgfs_is_match("abort_id=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.last_abort_id = value; - goto out; - } - - data = iwl_dbgfs_is_match("send_range_abort=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0 && value) { - abort_id = mvm->tof_data.last_abort_id; - ret = iwl_mvm_tof_range_abort_cmd(mvm, abort_id); - goto out; - } - } - -out: - mutex_unlock(&mvm->mutex); - return ret ?: count; -} - -static ssize_t iwl_dbgfs_tof_range_abort_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->mvm; - char buf[32]; - int pos = 0; - const size_t bufsz = sizeof(buf); - int last_abort_id; - - mutex_lock(&mvm->mutex); - last_abort_id = mvm->tof_data.last_abort_id; - mutex_unlock(&mvm->mutex); - - pos += scnprintf(buf + pos, bufsz - pos, "last_abort_id = %d\n", - last_abort_id); - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_tof_range_response_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->mvm; - char *buf; - int pos = 0; - const size_t bufsz = sizeof(struct iwl_tof_range_rsp_ntfy) + 256; - struct iwl_tof_range_rsp_ntfy *cmd; - int i, ret; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - mutex_lock(&mvm->mutex); - cmd = &mvm->tof_data.range_resp; - - pos += scnprintf(buf + pos, bufsz - pos, "request_id = %d\n", - cmd->request_id); - pos += scnprintf(buf + pos, bufsz - pos, "status = %d\n", - cmd->request_status); - pos += scnprintf(buf + pos, bufsz - pos, "last_in_batch = %d\n", - cmd->last_in_batch); - pos += scnprintf(buf + pos, bufsz - pos, "num_of_aps = %d\n", - cmd->num_of_aps); - for (i = 0; i < cmd->num_of_aps; i++) { - struct iwl_tof_range_rsp_ap_entry_ntfy *ap = &cmd->ap[i]; - - pos += scnprintf(buf + pos, bufsz - pos, - "ap %.2d: bssid=%pM status=%hhd bw=%hhd" - " rtt=%d rtt_var=%d rtt_spread=%d" - " rssi=%hhd rssi_spread=%hhd" - " range=%d range_var=%d" - " time_stamp=%d\n", - i, ap->bssid, ap->measure_status, - ap->measure_bw, - ap->rtt, ap->rtt_variance, ap->rtt_spread, - ap->rssi, ap->rssi_spread, ap->range, - ap->range_variance, ap->timestamp); - } - mutex_unlock(&mvm->mutex); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_low_latency_write(struct ieee80211_vif *vif, char *buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - u8 value; - int ret; - - ret = kstrtou8(buf, 0, &value); - if (ret) - return ret; - if (value > 1) - return -EINVAL; - - mutex_lock(&mvm->mutex); - iwl_mvm_update_low_latency(mvm, vif, value); - mutex_unlock(&mvm->mutex); - - return count; -} - -static ssize_t iwl_dbgfs_low_latency_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); - char buf[2]; - - buf[0] = mvmvif->low_latency ? '1' : '0'; - buf[1] = '\n'; - return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf)); -} - -static ssize_t iwl_dbgfs_uapsd_misbehaving_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); - char buf[20]; - int len; - - len = sprintf(buf, "%pM\n", mvmvif->uapsd_misbehaving_bssid); - return simple_read_from_buffer(user_buf, count, ppos, buf, len); -} - -static ssize_t iwl_dbgfs_uapsd_misbehaving_write(struct ieee80211_vif *vif, - char *buf, size_t count, - loff_t *ppos) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - bool ret; - - mutex_lock(&mvm->mutex); - ret = mac_pton(buf, mvmvif->uapsd_misbehaving_bssid); - mutex_unlock(&mvm->mutex); - - return ret ? count : -EINVAL; -} - -static ssize_t iwl_dbgfs_rx_phyinfo_write(struct ieee80211_vif *vif, char *buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - struct ieee80211_chanctx_conf *chanctx_conf; - struct iwl_mvm_phy_ctxt *phy_ctxt; - u16 value; - int ret; - - ret = kstrtou16(buf, 0, &value); - if (ret) - return ret; - - mutex_lock(&mvm->mutex); - rcu_read_lock(); - - chanctx_conf = rcu_dereference(vif->chanctx_conf); - /* make sure the channel context is assigned */ - if (!chanctx_conf) { - rcu_read_unlock(); - mutex_unlock(&mvm->mutex); - return -EINVAL; - } - - phy_ctxt = &mvm->phy_ctxts[*(u16 *)chanctx_conf->drv_priv]; - rcu_read_unlock(); - - mvm->dbgfs_rx_phyinfo = value; - - ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chanctx_conf->min_def, - chanctx_conf->rx_chains_static, - chanctx_conf->rx_chains_dynamic); - mutex_unlock(&mvm->mutex); - - return ret ?: count; -} - -static ssize_t iwl_dbgfs_rx_phyinfo_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); - char buf[8]; - - snprintf(buf, sizeof(buf), "0x%04x\n", mvmvif->mvm->dbgfs_rx_phyinfo); - - return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf)); -} - -#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ - _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif) -#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ - _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif) -#define MVM_DEBUGFS_ADD_FILE_VIF(name, parent, mode) do { \ - if (!debugfs_create_file(#name, mode, parent, vif, \ - &iwl_dbgfs_##name##_ops)) \ - goto err; \ - } while (0) - -MVM_DEBUGFS_READ_FILE_OPS(mac_params); -MVM_DEBUGFS_READ_FILE_OPS(tx_pwr_lmt); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params, 32); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params, 256); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 10); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(uapsd_misbehaving, 20); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(rx_phyinfo, 10); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_enable, 32); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_range_request, 512); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_range_req_ext, 32); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_range_abort, 32); -MVM_DEBUGFS_READ_FILE_OPS(tof_range_response); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_responder_params, 32); - -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]; - - /* - * Check if debugfs directory already exist before creating it. - * This may happen when, for example, resetting hw or suspend-resume - */ - if (!dbgfs_dir || mvmvif->dbgfs_dir) - return; - - mvmvif->dbgfs_dir = debugfs_create_dir("iwlmvm", dbgfs_dir); - - if (!mvmvif->dbgfs_dir) { - IWL_ERR(mvm, "Failed to create debugfs directory under %s\n", - dbgfs_dir->d_name.name); - return; - } - - if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM && - ((vif->type == NL80211_IFTYPE_STATION && !vif->p2p) || - (vif->type == NL80211_IFTYPE_STATION && vif->p2p && - mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM))) - MVM_DEBUGFS_ADD_FILE_VIF(pm_params, mvmvif->dbgfs_dir, S_IWUSR | - S_IRUSR); - - MVM_DEBUGFS_ADD_FILE_VIF(tx_pwr_lmt, mvmvif->dbgfs_dir, S_IRUSR); - MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, S_IRUSR); - MVM_DEBUGFS_ADD_FILE_VIF(low_latency, mvmvif->dbgfs_dir, - S_IRUSR | S_IWUSR); - MVM_DEBUGFS_ADD_FILE_VIF(uapsd_misbehaving, mvmvif->dbgfs_dir, - S_IRUSR | S_IWUSR); - MVM_DEBUGFS_ADD_FILE_VIF(rx_phyinfo, mvmvif->dbgfs_dir, - S_IRUSR | S_IWUSR); - - if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p && - mvmvif == mvm->bf_allowed_vif) - MVM_DEBUGFS_ADD_FILE_VIF(bf_params, mvmvif->dbgfs_dir, - S_IRUSR | S_IWUSR); - - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT) && - !vif->p2p && (vif->type != NL80211_IFTYPE_P2P_DEVICE)) { - if (IWL_MVM_TOF_IS_RESPONDER && vif->type == NL80211_IFTYPE_AP) - MVM_DEBUGFS_ADD_FILE_VIF(tof_responder_params, - mvmvif->dbgfs_dir, - S_IRUSR | S_IWUSR); - - MVM_DEBUGFS_ADD_FILE_VIF(tof_range_request, mvmvif->dbgfs_dir, - S_IRUSR | S_IWUSR); - MVM_DEBUGFS_ADD_FILE_VIF(tof_range_req_ext, mvmvif->dbgfs_dir, - S_IRUSR | S_IWUSR); - MVM_DEBUGFS_ADD_FILE_VIF(tof_enable, mvmvif->dbgfs_dir, - S_IRUSR | S_IWUSR); - MVM_DEBUGFS_ADD_FILE_VIF(tof_range_abort, mvmvif->dbgfs_dir, - S_IRUSR | S_IWUSR); - MVM_DEBUGFS_ADD_FILE_VIF(tof_range_response, 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/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c deleted file mode 100644 index 05928fb40..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ /dev/null @@ -1,1516 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * 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 <linux/vmalloc.h> - -#include "mvm.h" -#include "sta.h" -#include "iwl-io.h" -#include "debugfs.h" -#include "iwl-fw-error-dump.h" - -static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - int ret; - u32 scd_q_msk; - - if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR) - return -EIO; - - if (sscanf(buf, "%x", &scd_q_msk) != 1) - return -EINVAL; - - IWL_ERR(mvm, "FLUSHING queues: scd_q_msk = 0x%x\n", scd_q_msk); - - mutex_lock(&mvm->mutex); - ret = iwl_mvm_flush_tx_path(mvm, scd_q_msk, 0) ? : count; - mutex_unlock(&mvm->mutex); - - return ret; -} - -static ssize_t iwl_dbgfs_sta_drain_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm_sta *mvmsta; - int sta_id, drain, ret; - - if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR) - return -EIO; - - if (sscanf(buf, "%d %d", &sta_id, &drain) != 2) - return -EINVAL; - if (sta_id < 0 || sta_id >= IWL_MVM_STATION_COUNT) - return -EINVAL; - if (drain < 0 || drain > 1) - return -EINVAL; - - mutex_lock(&mvm->mutex); - - mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id); - - if (!mvmsta) - ret = -ENOENT; - else - ret = iwl_mvm_drain_sta(mvm, mvmsta, drain) ? : count; - - mutex_unlock(&mvm->mutex); - - return ret; -} - -static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - const struct fw_img *img; - unsigned int ofs, len; - size_t ret; - u8 *ptr; - - if (!mvm->ucode_loaded) - return -EINVAL; - - /* default is to dump the entire data segment */ - img = &mvm->fw->img[mvm->cur_ucode]; - ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; - len = img->sec[IWL_UCODE_SECTION_DATA].len; - - if (mvm->dbgfs_sram_len) { - ofs = mvm->dbgfs_sram_offset; - len = mvm->dbgfs_sram_len; - } - - ptr = kzalloc(len, GFP_KERNEL); - if (!ptr) - return -ENOMEM; - - iwl_trans_read_mem_bytes(mvm->trans, ofs, ptr, len); - - ret = simple_read_from_buffer(user_buf, count, ppos, ptr, len); - - kfree(ptr); - - return ret; -} - -static ssize_t iwl_dbgfs_sram_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - const struct fw_img *img; - u32 offset, len; - u32 img_offset, img_len; - - if (!mvm->ucode_loaded) - return -EINVAL; - - img = &mvm->fw->img[mvm->cur_ucode]; - img_offset = img->sec[IWL_UCODE_SECTION_DATA].offset; - img_len = img->sec[IWL_UCODE_SECTION_DATA].len; - - if (sscanf(buf, "%x,%x", &offset, &len) == 2) { - if ((offset & 0x3) || (len & 0x3)) - return -EINVAL; - - if (offset + len > img_offset + img_len) - return -EINVAL; - - mvm->dbgfs_sram_offset = offset; - mvm->dbgfs_sram_len = len; - } else { - mvm->dbgfs_sram_offset = 0; - mvm->dbgfs_sram_len = 0; - } - - return count; -} - -static ssize_t iwl_dbgfs_set_nic_temperature_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - char buf[16]; - int pos; - - if (!mvm->temperature_test) - pos = scnprintf(buf , sizeof(buf), "disabled\n"); - else - pos = scnprintf(buf , sizeof(buf), "%d\n", mvm->temperature); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -/* - * Set NIC Temperature - * Cause the driver to ignore the actual NIC temperature reported by the FW - * Enable: any value between IWL_MVM_DEBUG_SET_TEMPERATURE_MIN - - * IWL_MVM_DEBUG_SET_TEMPERATURE_MAX - * Disable: IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE - */ -static ssize_t iwl_dbgfs_set_nic_temperature_write(struct iwl_mvm *mvm, - char *buf, size_t count, - loff_t *ppos) -{ - int temperature; - - if (!mvm->ucode_loaded && !mvm->temperature_test) - return -EIO; - - if (kstrtoint(buf, 10, &temperature)) - return -EINVAL; - /* not a legal temperature */ - if ((temperature > IWL_MVM_DEBUG_SET_TEMPERATURE_MAX && - temperature != IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE) || - temperature < IWL_MVM_DEBUG_SET_TEMPERATURE_MIN) - return -EINVAL; - - mutex_lock(&mvm->mutex); - if (temperature == IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE) { - if (!mvm->temperature_test) - goto out; - - mvm->temperature_test = false; - /* Since we can't read the temp while awake, just set - * it to zero until we get the next RX stats from the - * firmware. - */ - mvm->temperature = 0; - } else { - mvm->temperature_test = true; - mvm->temperature = temperature; - } - IWL_DEBUG_TEMP(mvm, "%sabling debug set temperature (temp = %d)\n", - mvm->temperature_test ? "En" : "Dis" , - mvm->temperature); - /* handle the temperature change */ - iwl_mvm_tt_handler(mvm); - -out: - mutex_unlock(&mvm->mutex); - - return count; -} - -static ssize_t iwl_dbgfs_nic_temp_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - char buf[16]; - int pos, temp; - - if (!mvm->ucode_loaded) - return -EIO; - - mutex_lock(&mvm->mutex); - temp = iwl_mvm_get_temp(mvm); - mutex_unlock(&mvm->mutex); - - if (temp < 0) - return temp; - - pos = scnprintf(buf , sizeof(buf), "%d\n", temp); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - struct ieee80211_sta *sta; - char buf[400]; - int i, pos = 0, bufsz = sizeof(buf); - - mutex_lock(&mvm->mutex); - - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { - pos += scnprintf(buf + pos, bufsz - pos, "%.2d: ", i); - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], - lockdep_is_held(&mvm->mutex)); - if (!sta) - pos += scnprintf(buf + pos, bufsz - pos, "N/A\n"); - else if (IS_ERR(sta)) - pos += scnprintf(buf + pos, bufsz - pos, "%ld\n", - PTR_ERR(sta)); - else - pos += scnprintf(buf + pos, bufsz - pos, "%pM\n", - sta->addr); - } - - mutex_unlock(&mvm->mutex); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_disable_power_off_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - char buf[64]; - int bufsz = sizeof(buf); - int pos = 0; - - pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off_d0=%d\n", - mvm->disable_power_off); - pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off_d3=%d\n", - mvm->disable_power_off_d3); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_disable_power_off_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - int ret, val; - - if (!mvm->ucode_loaded) - return -EIO; - - if (!strncmp("disable_power_off_d0=", buf, 21)) { - if (sscanf(buf + 21, "%d", &val) != 1) - return -EINVAL; - mvm->disable_power_off = val; - } else if (!strncmp("disable_power_off_d3=", buf, 21)) { - if (sscanf(buf + 21, "%d", &val) != 1) - return -EINVAL; - mvm->disable_power_off_d3 = val; - } else { - return -EINVAL; - } - - mutex_lock(&mvm->mutex); - ret = iwl_mvm_power_update_device(mvm); - mutex_unlock(&mvm->mutex); - - return ret ?: count; -} - -#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 -int iwl_mvm_coex_dump_mbox(struct iwl_bt_coex_profile_notif *notif, char *buf, - int pos, int bufsz) -{ - 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); - - return pos; -} - -static -int iwl_mvm_coex_dump_mbox_old(struct iwl_bt_coex_profile_notif_old *notif, - char *buf, int pos, int bufsz) -{ - 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); - - return pos; -} - -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; - char *buf; - int ret, pos = 0, bufsz = sizeof(char) * 1024; - - buf = kmalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - mutex_lock(&mvm->mutex); - - if (!fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_BT_COEX_SPLIT)) { - struct iwl_bt_coex_profile_notif_old *notif = - &mvm->last_bt_notif_old; - - pos += iwl_mvm_coex_dump_mbox_old(notif, buf, pos, bufsz); - - pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n", - notif->bt_ci_compliance); - pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n", - le32_to_cpu(notif->primary_ch_lut)); - pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n", - le32_to_cpu(notif->secondary_ch_lut)); - pos += scnprintf(buf+pos, - bufsz-pos, "bt_activity_grading = %d\n", - le32_to_cpu(notif->bt_activity_grading)); - pos += scnprintf(buf+pos, bufsz-pos, - "antenna isolation = %d CORUN LUT index = %d\n", - mvm->last_ant_isol, mvm->last_corun_lut); - } else { - struct iwl_bt_coex_profile_notif *notif = - &mvm->last_bt_notif; - - pos += iwl_mvm_coex_dump_mbox(notif, buf, pos, bufsz); - - pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n", - notif->bt_ci_compliance); - pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n", - le32_to_cpu(notif->primary_ch_lut)); - pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n", - le32_to_cpu(notif->secondary_ch_lut)); - pos += scnprintf(buf+pos, - bufsz-pos, "bt_activity_grading = %d\n", - le32_to_cpu(notif->bt_activity_grading)); - pos += scnprintf(buf+pos, bufsz-pos, - "antenna isolation = %d CORUN LUT index = %d\n", - mvm->last_ant_isol, mvm->last_corun_lut); - } - - 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_bt_cmd_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - char buf[256]; - int bufsz = sizeof(buf); - int pos = 0; - - mutex_lock(&mvm->mutex); - - if (!fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_BT_COEX_SPLIT)) { - struct iwl_bt_coex_ci_cmd_old *cmd = &mvm->last_bt_ci_cmd_old; - - pos += scnprintf(buf+pos, bufsz-pos, - "Channel inhibition CMD\n"); - pos += scnprintf(buf+pos, bufsz-pos, - "\tPrimary Channel Bitmap 0x%016llx\n", - le64_to_cpu(cmd->bt_primary_ci)); - pos += scnprintf(buf+pos, bufsz-pos, - "\tSecondary Channel Bitmap 0x%016llx\n", - le64_to_cpu(cmd->bt_secondary_ci)); - - pos += scnprintf(buf+pos, bufsz-pos, - "BT Configuration CMD - 0=default, 1=never, 2=always\n"); - pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill msk idx %d\n", - mvm->bt_ack_kill_msk[0]); - pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill msk idx %d\n", - mvm->bt_cts_kill_msk[0]); - - } else { - struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd; - - pos += scnprintf(buf+pos, bufsz-pos, - "Channel inhibition CMD\n"); - pos += scnprintf(buf+pos, bufsz-pos, - "\tPrimary Channel Bitmap 0x%016llx\n", - le64_to_cpu(cmd->bt_primary_ci)); - pos += scnprintf(buf+pos, bufsz-pos, - "\tSecondary Channel Bitmap 0x%016llx\n", - le64_to_cpu(cmd->bt_secondary_ci)); - } - - mutex_unlock(&mvm->mutex); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t -iwl_dbgfs_bt_tx_prio_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - u32 bt_tx_prio; - - if (sscanf(buf, "%u", &bt_tx_prio) != 1) - return -EINVAL; - if (bt_tx_prio > 4) - return -EINVAL; - - mvm->bt_tx_prio = bt_tx_prio; - - return count; -} - -static ssize_t -iwl_dbgfs_bt_force_ant_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - static const char * const modes_str[BT_FORCE_ANT_MAX] = { - [BT_FORCE_ANT_DIS] = "dis", - [BT_FORCE_ANT_AUTO] = "auto", - [BT_FORCE_ANT_BT] = "bt", - [BT_FORCE_ANT_WIFI] = "wifi", - }; - int ret, bt_force_ant_mode; - - for (bt_force_ant_mode = 0; - bt_force_ant_mode < ARRAY_SIZE(modes_str); - bt_force_ant_mode++) { - if (!strcmp(buf, modes_str[bt_force_ant_mode])) - break; - } - - if (bt_force_ant_mode >= ARRAY_SIZE(modes_str)) - return -EINVAL; - - ret = 0; - mutex_lock(&mvm->mutex); - if (mvm->bt_force_ant_mode == bt_force_ant_mode) - goto out; - - mvm->bt_force_ant_mode = bt_force_ant_mode; - IWL_DEBUG_COEX(mvm, "Force mode: %s\n", - modes_str[mvm->bt_force_ant_mode]); - ret = iwl_send_bt_init_conf(mvm); - -out: - mutex_unlock(&mvm->mutex); - return ret ?: count; -} - -#define PRINT_STATS_LE32(_struct, _memb) \ - pos += scnprintf(buf + pos, bufsz - pos, \ - fmt_table, #_memb, \ - le32_to_cpu(_struct->_memb)) - -static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file, - char __user *user_buf, size_t count, - loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - static const char *fmt_table = "\t%-30s %10u\n"; - static const char *fmt_header = "%-32s\n"; - int pos = 0; - char *buf; - int ret; - /* 43 is the size of each data line, 33 is the size of each header */ - size_t bufsz = - ((sizeof(struct mvm_statistics_rx) / sizeof(__le32)) * 43) + - (4 * 33) + 1; - - struct mvm_statistics_rx_phy *ofdm; - struct mvm_statistics_rx_phy *cck; - struct mvm_statistics_rx_non_phy *general; - struct mvm_statistics_rx_ht_phy *ht; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - mutex_lock(&mvm->mutex); - - ofdm = &mvm->rx_stats.ofdm; - cck = &mvm->rx_stats.cck; - general = &mvm->rx_stats.general; - ht = &mvm->rx_stats.ofdm_ht; - - pos += scnprintf(buf + pos, bufsz - pos, fmt_header, - "Statistics_Rx - OFDM"); - PRINT_STATS_LE32(ofdm, ina_cnt); - PRINT_STATS_LE32(ofdm, fina_cnt); - PRINT_STATS_LE32(ofdm, plcp_err); - PRINT_STATS_LE32(ofdm, crc32_err); - PRINT_STATS_LE32(ofdm, overrun_err); - PRINT_STATS_LE32(ofdm, early_overrun_err); - PRINT_STATS_LE32(ofdm, crc32_good); - PRINT_STATS_LE32(ofdm, false_alarm_cnt); - PRINT_STATS_LE32(ofdm, fina_sync_err_cnt); - PRINT_STATS_LE32(ofdm, sfd_timeout); - PRINT_STATS_LE32(ofdm, fina_timeout); - PRINT_STATS_LE32(ofdm, unresponded_rts); - PRINT_STATS_LE32(ofdm, rxe_frame_lmt_overrun); - PRINT_STATS_LE32(ofdm, sent_ack_cnt); - PRINT_STATS_LE32(ofdm, sent_cts_cnt); - PRINT_STATS_LE32(ofdm, sent_ba_rsp_cnt); - PRINT_STATS_LE32(ofdm, dsp_self_kill); - PRINT_STATS_LE32(ofdm, mh_format_err); - PRINT_STATS_LE32(ofdm, re_acq_main_rssi_sum); - PRINT_STATS_LE32(ofdm, reserved); - - pos += scnprintf(buf + pos, bufsz - pos, fmt_header, - "Statistics_Rx - CCK"); - PRINT_STATS_LE32(cck, ina_cnt); - PRINT_STATS_LE32(cck, fina_cnt); - PRINT_STATS_LE32(cck, plcp_err); - PRINT_STATS_LE32(cck, crc32_err); - PRINT_STATS_LE32(cck, overrun_err); - PRINT_STATS_LE32(cck, early_overrun_err); - PRINT_STATS_LE32(cck, crc32_good); - PRINT_STATS_LE32(cck, false_alarm_cnt); - PRINT_STATS_LE32(cck, fina_sync_err_cnt); - PRINT_STATS_LE32(cck, sfd_timeout); - PRINT_STATS_LE32(cck, fina_timeout); - PRINT_STATS_LE32(cck, unresponded_rts); - PRINT_STATS_LE32(cck, rxe_frame_lmt_overrun); - PRINT_STATS_LE32(cck, sent_ack_cnt); - PRINT_STATS_LE32(cck, sent_cts_cnt); - PRINT_STATS_LE32(cck, sent_ba_rsp_cnt); - PRINT_STATS_LE32(cck, dsp_self_kill); - PRINT_STATS_LE32(cck, mh_format_err); - PRINT_STATS_LE32(cck, re_acq_main_rssi_sum); - PRINT_STATS_LE32(cck, reserved); - - pos += scnprintf(buf + pos, bufsz - pos, fmt_header, - "Statistics_Rx - GENERAL"); - PRINT_STATS_LE32(general, bogus_cts); - PRINT_STATS_LE32(general, bogus_ack); - PRINT_STATS_LE32(general, non_bssid_frames); - PRINT_STATS_LE32(general, filtered_frames); - PRINT_STATS_LE32(general, non_channel_beacons); - PRINT_STATS_LE32(general, channel_beacons); - PRINT_STATS_LE32(general, num_missed_bcon); - PRINT_STATS_LE32(general, adc_rx_saturation_time); - PRINT_STATS_LE32(general, ina_detection_search_time); - PRINT_STATS_LE32(general, beacon_silence_rssi_a); - PRINT_STATS_LE32(general, beacon_silence_rssi_b); - PRINT_STATS_LE32(general, beacon_silence_rssi_c); - PRINT_STATS_LE32(general, interference_data_flag); - PRINT_STATS_LE32(general, channel_load); - PRINT_STATS_LE32(general, dsp_false_alarms); - PRINT_STATS_LE32(general, beacon_rssi_a); - PRINT_STATS_LE32(general, beacon_rssi_b); - PRINT_STATS_LE32(general, beacon_rssi_c); - PRINT_STATS_LE32(general, beacon_energy_a); - PRINT_STATS_LE32(general, beacon_energy_b); - PRINT_STATS_LE32(general, beacon_energy_c); - PRINT_STATS_LE32(general, num_bt_kills); - PRINT_STATS_LE32(general, mac_id); - PRINT_STATS_LE32(general, directed_data_mpdu); - - pos += scnprintf(buf + pos, bufsz - pos, fmt_header, - "Statistics_Rx - HT"); - PRINT_STATS_LE32(ht, plcp_err); - PRINT_STATS_LE32(ht, overrun_err); - PRINT_STATS_LE32(ht, early_overrun_err); - PRINT_STATS_LE32(ht, crc32_good); - PRINT_STATS_LE32(ht, crc32_err); - PRINT_STATS_LE32(ht, mh_format_err); - PRINT_STATS_LE32(ht, agg_crc32_good); - PRINT_STATS_LE32(ht, agg_mpdu_cnt); - PRINT_STATS_LE32(ht, agg_cnt); - PRINT_STATS_LE32(ht, unsupport_mcs); - - mutex_unlock(&mvm->mutex); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - - return ret; -} -#undef PRINT_STAT_LE32 - -static ssize_t iwl_dbgfs_frame_stats_read(struct iwl_mvm *mvm, - char __user *user_buf, size_t count, - loff_t *ppos, - struct iwl_mvm_frame_stats *stats) -{ - char *buff, *pos, *endpos; - int idx, i; - int ret; - static const size_t bufsz = 1024; - - buff = kmalloc(bufsz, GFP_KERNEL); - if (!buff) - return -ENOMEM; - - spin_lock_bh(&mvm->drv_stats_lock); - - pos = buff; - endpos = pos + bufsz; - - pos += scnprintf(pos, endpos - pos, - "Legacy/HT/VHT\t:\t%d/%d/%d\n", - stats->legacy_frames, - stats->ht_frames, - stats->vht_frames); - pos += scnprintf(pos, endpos - pos, "20/40/80\t:\t%d/%d/%d\n", - stats->bw_20_frames, - stats->bw_40_frames, - stats->bw_80_frames); - pos += scnprintf(pos, endpos - pos, "NGI/SGI\t\t:\t%d/%d\n", - stats->ngi_frames, - stats->sgi_frames); - pos += scnprintf(pos, endpos - pos, "SISO/MIMO2\t:\t%d/%d\n", - stats->siso_frames, - stats->mimo2_frames); - pos += scnprintf(pos, endpos - pos, "FAIL/SCSS\t:\t%d/%d\n", - stats->fail_frames, - stats->success_frames); - pos += scnprintf(pos, endpos - pos, "MPDUs agg\t:\t%d\n", - stats->agg_frames); - pos += scnprintf(pos, endpos - pos, "A-MPDUs\t\t:\t%d\n", - stats->ampdu_count); - pos += scnprintf(pos, endpos - pos, "Avg MPDUs/A-MPDU:\t%d\n", - stats->ampdu_count > 0 ? - (stats->agg_frames / stats->ampdu_count) : 0); - - pos += scnprintf(pos, endpos - pos, "Last Rates\n"); - - idx = stats->last_frame_idx - 1; - for (i = 0; i < ARRAY_SIZE(stats->last_rates); i++) { - idx = (idx + 1) % ARRAY_SIZE(stats->last_rates); - if (stats->last_rates[idx] == 0) - continue; - pos += scnprintf(pos, endpos - pos, "Rate[%d]: ", - (int)(ARRAY_SIZE(stats->last_rates) - i)); - pos += rs_pretty_print_rate(pos, stats->last_rates[idx]); - } - spin_unlock_bh(&mvm->drv_stats_lock); - - ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff); - kfree(buff); - - return ret; -} - -static ssize_t iwl_dbgfs_drv_rx_stats_read(struct file *file, - char __user *user_buf, size_t count, - loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - - return iwl_dbgfs_frame_stats_read(mvm, user_buf, count, ppos, - &mvm->drv_rx_stats); -} - -static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - int ret; - - mutex_lock(&mvm->mutex); - - /* allow one more restart that we're provoking here */ - if (mvm->restart_fw >= 0) - mvm->restart_fw++; - - /* take the return value to make compiler happy - it will fail anyway */ - ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_ERROR, 0, 0, NULL); - - mutex_unlock(&mvm->mutex); - - return count; -} - -static ssize_t iwl_dbgfs_fw_nmi_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - int ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_NMI); - if (ret) - return ret; - - iwl_force_nmi(mvm->trans); - - iwl_mvm_unref(mvm, IWL_MVM_REF_NMI); - - return count; -} - -static ssize_t -iwl_dbgfs_scan_ant_rxchain_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - int pos = 0; - char buf[32]; - const size_t bufsz = sizeof(buf); - - /* print which antennas were set for the scan command by the user */ - pos += scnprintf(buf + pos, bufsz - pos, "Antennas for scan: "); - if (mvm->scan_rx_ant & ANT_A) - pos += scnprintf(buf + pos, bufsz - pos, "A"); - if (mvm->scan_rx_ant & ANT_B) - pos += scnprintf(buf + pos, bufsz - pos, "B"); - if (mvm->scan_rx_ant & ANT_C) - pos += scnprintf(buf + pos, bufsz - pos, "C"); - pos += scnprintf(buf + pos, bufsz - pos, " (%hhx)\n", mvm->scan_rx_ant); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t -iwl_dbgfs_scan_ant_rxchain_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - u8 scan_rx_ant; - - if (sscanf(buf, "%hhx", &scan_rx_ant) != 1) - return -EINVAL; - if (scan_rx_ant > ANT_ABC) - return -EINVAL; - if (scan_rx_ant & ~(iwl_mvm_get_valid_rx_ant(mvm))) - return -EINVAL; - - if (mvm->scan_rx_ant != scan_rx_ant) { - mvm->scan_rx_ant = scan_rx_ant; - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_UMAC_SCAN)) - iwl_mvm_config_scan(mvm); - } - - return count; -} - -static ssize_t iwl_dbgfs_fw_dbg_conf_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - int conf; - char buf[8]; - const size_t bufsz = sizeof(buf); - int pos = 0; - - mutex_lock(&mvm->mutex); - conf = mvm->fw_dbg_conf; - mutex_unlock(&mvm->mutex); - - pos += scnprintf(buf + pos, bufsz - pos, "%d\n", conf); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_fw_dbg_conf_write(struct iwl_mvm *mvm, - char *buf, size_t count, - loff_t *ppos) -{ - unsigned int conf_id; - int ret; - - ret = kstrtouint(buf, 0, &conf_id); - if (ret) - return ret; - - if (WARN_ON(conf_id >= FW_DBG_CONF_MAX)) - return -EINVAL; - - mutex_lock(&mvm->mutex); - ret = iwl_mvm_start_fw_dbg_conf(mvm, conf_id); - mutex_unlock(&mvm->mutex); - - return ret ?: count; -} - -static ssize_t iwl_dbgfs_fw_dbg_collect_write(struct iwl_mvm *mvm, - char *buf, size_t count, - loff_t *ppos) -{ - int ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PRPH_WRITE); - - if (ret) - return ret; - - iwl_mvm_fw_dbg_collect(mvm, FW_DBG_TRIGGER_USER, NULL, 0, NULL); - - iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_WRITE); - - return count; -} - -#define ADD_TEXT(...) pos += scnprintf(buf + pos, bufsz - pos, __VA_ARGS__) -#ifdef CONFIG_IWLWIFI_BCAST_FILTERING -static ssize_t iwl_dbgfs_bcast_filters_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - struct iwl_bcast_filter_cmd cmd; - const struct iwl_fw_bcast_filter *filter; - char *buf; - int bufsz = 1024; - int i, j, pos = 0; - ssize_t ret; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - mutex_lock(&mvm->mutex); - if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) { - ADD_TEXT("None\n"); - mutex_unlock(&mvm->mutex); - goto out; - } - mutex_unlock(&mvm->mutex); - - for (i = 0; cmd.filters[i].attrs[0].mask; i++) { - filter = &cmd.filters[i]; - - ADD_TEXT("Filter [%d]:\n", i); - ADD_TEXT("\tDiscard=%d\n", filter->discard); - ADD_TEXT("\tFrame Type: %s\n", - filter->frame_type ? "IPv4" : "Generic"); - - for (j = 0; j < ARRAY_SIZE(filter->attrs); j++) { - const struct iwl_fw_bcast_filter_attr *attr; - - attr = &filter->attrs[j]; - if (!attr->mask) - break; - - ADD_TEXT("\tAttr [%d]: offset=%d (from %s), mask=0x%x, value=0x%x reserved=0x%x\n", - j, attr->offset, - attr->offset_type ? "IP End" : - "Payload Start", - be32_to_cpu(attr->mask), - be32_to_cpu(attr->val), - le16_to_cpu(attr->reserved1)); - } - } -out: - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_bcast_filters_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - int pos, next_pos; - struct iwl_fw_bcast_filter filter = {}; - struct iwl_bcast_filter_cmd cmd; - u32 filter_id, attr_id, mask, value; - int err = 0; - - if (sscanf(buf, "%d %hhi %hhi %n", &filter_id, &filter.discard, - &filter.frame_type, &pos) != 3) - return -EINVAL; - - if (filter_id >= ARRAY_SIZE(mvm->dbgfs_bcast_filtering.cmd.filters) || - filter.frame_type > BCAST_FILTER_FRAME_TYPE_IPV4) - return -EINVAL; - - for (attr_id = 0; attr_id < ARRAY_SIZE(filter.attrs); - attr_id++) { - struct iwl_fw_bcast_filter_attr *attr = - &filter.attrs[attr_id]; - - if (pos >= count) - break; - - if (sscanf(&buf[pos], "%hhi %hhi %i %i %n", - &attr->offset, &attr->offset_type, - &mask, &value, &next_pos) != 4) - return -EINVAL; - - attr->mask = cpu_to_be32(mask); - attr->val = cpu_to_be32(value); - if (mask) - filter.num_attrs++; - - pos += next_pos; - } - - mutex_lock(&mvm->mutex); - memcpy(&mvm->dbgfs_bcast_filtering.cmd.filters[filter_id], - &filter, sizeof(filter)); - - /* send updated bcast filtering configuration */ - if (mvm->dbgfs_bcast_filtering.override && - iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) - err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0, - sizeof(cmd), &cmd); - mutex_unlock(&mvm->mutex); - - return err ?: count; -} - -static ssize_t iwl_dbgfs_bcast_filters_macs_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - struct iwl_bcast_filter_cmd cmd; - char *buf; - int bufsz = 1024; - int i, pos = 0; - ssize_t ret; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - mutex_lock(&mvm->mutex); - if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) { - ADD_TEXT("None\n"); - mutex_unlock(&mvm->mutex); - goto out; - } - mutex_unlock(&mvm->mutex); - - for (i = 0; i < ARRAY_SIZE(cmd.macs); i++) { - const struct iwl_fw_bcast_mac *mac = &cmd.macs[i]; - - ADD_TEXT("Mac [%d]: discard=%d attached_filters=0x%x\n", - i, mac->default_discard, mac->attached_filters); - } -out: - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_bcast_filters_macs_write(struct iwl_mvm *mvm, - char *buf, size_t count, - loff_t *ppos) -{ - struct iwl_bcast_filter_cmd cmd; - struct iwl_fw_bcast_mac mac = {}; - u32 mac_id, attached_filters; - int err = 0; - - if (!mvm->bcast_filters) - return -ENOENT; - - if (sscanf(buf, "%d %hhi %i", &mac_id, &mac.default_discard, - &attached_filters) != 3) - return -EINVAL; - - if (mac_id >= ARRAY_SIZE(cmd.macs) || - mac.default_discard > 1 || - attached_filters >= BIT(ARRAY_SIZE(cmd.filters))) - return -EINVAL; - - mac.attached_filters = cpu_to_le16(attached_filters); - - mutex_lock(&mvm->mutex); - memcpy(&mvm->dbgfs_bcast_filtering.cmd.macs[mac_id], - &mac, sizeof(mac)); - - /* send updated bcast filtering configuration */ - if (mvm->dbgfs_bcast_filtering.override && - iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) - err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0, - sizeof(cmd), &cmd); - mutex_unlock(&mvm->mutex); - - return err ?: count; -} -#endif - -#ifdef CONFIG_PM_SLEEP -static ssize_t iwl_dbgfs_d3_sram_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - int store; - - if (sscanf(buf, "%d", &store) != 1) - return -EINVAL; - - mvm->store_d3_resume_sram = store; - - return count; -} - -static ssize_t iwl_dbgfs_d3_sram_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - const struct fw_img *img; - int ofs, len, pos = 0; - size_t bufsz, ret; - char *buf; - u8 *ptr = mvm->d3_resume_sram; - - img = &mvm->fw->img[IWL_UCODE_WOWLAN]; - len = img->sec[IWL_UCODE_SECTION_DATA].len; - - bufsz = len * 4 + 256; - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - pos += scnprintf(buf, bufsz, "D3 SRAM capture: %sabled\n", - mvm->store_d3_resume_sram ? "en" : "dis"); - - if (ptr) { - for (ofs = 0; ofs < len; ofs += 16) { - pos += scnprintf(buf + pos, bufsz - pos, - "0x%.4x %16ph\n", ofs, ptr + ofs); - } - } else { - pos += scnprintf(buf + pos, bufsz - pos, - "(no data captured)\n"); - } - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - - kfree(buf); - - return ret; -} -#endif - -#define PRINT_MVM_REF(ref) do { \ - if (mvm->refs[ref]) \ - pos += scnprintf(buf + pos, bufsz - pos, \ - "\t(0x%lx): %d %s\n", \ - BIT(ref), mvm->refs[ref], #ref); \ -} while (0) - -static ssize_t iwl_dbgfs_d0i3_refs_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - int i, pos = 0; - char buf[256]; - const size_t bufsz = sizeof(buf); - u32 refs = 0; - - for (i = 0; i < IWL_MVM_REF_COUNT; i++) - if (mvm->refs[i]) - refs |= BIT(i); - - pos += scnprintf(buf + pos, bufsz - pos, "taken mvm refs: 0x%x\n", - refs); - - PRINT_MVM_REF(IWL_MVM_REF_UCODE_DOWN); - PRINT_MVM_REF(IWL_MVM_REF_SCAN); - PRINT_MVM_REF(IWL_MVM_REF_ROC); - PRINT_MVM_REF(IWL_MVM_REF_ROC_AUX); - PRINT_MVM_REF(IWL_MVM_REF_P2P_CLIENT); - PRINT_MVM_REF(IWL_MVM_REF_AP_IBSS); - PRINT_MVM_REF(IWL_MVM_REF_USER); - PRINT_MVM_REF(IWL_MVM_REF_TX); - PRINT_MVM_REF(IWL_MVM_REF_TX_AGG); - PRINT_MVM_REF(IWL_MVM_REF_ADD_IF); - PRINT_MVM_REF(IWL_MVM_REF_START_AP); - PRINT_MVM_REF(IWL_MVM_REF_BSS_CHANGED); - PRINT_MVM_REF(IWL_MVM_REF_PREPARE_TX); - PRINT_MVM_REF(IWL_MVM_REF_PROTECT_TDLS); - PRINT_MVM_REF(IWL_MVM_REF_CHECK_CTKILL); - PRINT_MVM_REF(IWL_MVM_REF_PRPH_READ); - PRINT_MVM_REF(IWL_MVM_REF_PRPH_WRITE); - PRINT_MVM_REF(IWL_MVM_REF_NMI); - PRINT_MVM_REF(IWL_MVM_REF_TM_CMD); - PRINT_MVM_REF(IWL_MVM_REF_EXIT_WORK); - PRINT_MVM_REF(IWL_MVM_REF_PROTECT_CSA); - PRINT_MVM_REF(IWL_MVM_REF_FW_DBG_COLLECT); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_d0i3_refs_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - unsigned long value; - int ret; - bool taken; - - ret = kstrtoul(buf, 10, &value); - if (ret < 0) - return ret; - - mutex_lock(&mvm->mutex); - - taken = mvm->refs[IWL_MVM_REF_USER]; - if (value == 1 && !taken) - iwl_mvm_ref(mvm, IWL_MVM_REF_USER); - else if (value == 0 && taken) - iwl_mvm_unref(mvm, IWL_MVM_REF_USER); - else - ret = -EINVAL; - - mutex_unlock(&mvm->mutex); - - if (ret < 0) - return ret; - return count; -} - -#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ - _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm) -#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ - _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm) -#define MVM_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) do { \ - if (!debugfs_create_file(alias, mode, parent, mvm, \ - &iwl_dbgfs_##name##_ops)) \ - goto err; \ - } while (0) -#define MVM_DEBUGFS_ADD_FILE(name, parent, mode) \ - MVM_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) - -static ssize_t -iwl_dbgfs_prph_reg_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - int pos = 0; - char buf[32]; - const size_t bufsz = sizeof(buf); - int ret; - - if (!mvm->dbgfs_prph_reg_addr) - return -EINVAL; - - ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PRPH_READ); - if (ret) - return ret; - - pos += scnprintf(buf + pos, bufsz - pos, "Reg 0x%x: (0x%x)\n", - mvm->dbgfs_prph_reg_addr, - iwl_read_prph(mvm->trans, mvm->dbgfs_prph_reg_addr)); - - iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_READ); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t -iwl_dbgfs_prph_reg_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - u8 args; - u32 value; - int ret; - - args = sscanf(buf, "%i %i", &mvm->dbgfs_prph_reg_addr, &value); - /* if we only want to set the reg address - nothing more to do */ - if (args == 1) - goto out; - - /* otherwise, make sure we have both address and value */ - if (args != 2) - return -EINVAL; - - ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PRPH_WRITE); - if (ret) - return ret; - - iwl_write_prph(mvm->trans, mvm->dbgfs_prph_reg_addr, value); - - iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_WRITE); -out: - return count; -} - -static ssize_t -iwl_dbgfs_send_echo_cmd_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - int ret; - - mutex_lock(&mvm->mutex); - ret = iwl_mvm_send_cmd_pdu(mvm, ECHO_CMD, 0, 0, NULL); - mutex_unlock(&mvm->mutex); - - return ret ?: count; -} - -MVM_DEBUGFS_READ_WRITE_FILE_OPS(prph_reg, 64); - -/* Device wide debugfs entries */ -MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush, 16); -MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain, 8); -MVM_DEBUGFS_WRITE_FILE_OPS(send_echo_cmd, 8); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram, 64); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(set_nic_temperature, 64); -MVM_DEBUGFS_READ_FILE_OPS(nic_temp); -MVM_DEBUGFS_READ_FILE_OPS(stations); -MVM_DEBUGFS_READ_FILE_OPS(bt_notif); -MVM_DEBUGFS_READ_FILE_OPS(bt_cmd); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(disable_power_off, 64); -MVM_DEBUGFS_READ_FILE_OPS(fw_rx_stats); -MVM_DEBUGFS_READ_FILE_OPS(drv_rx_stats); -MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart, 10); -MVM_DEBUGFS_WRITE_FILE_OPS(fw_nmi, 10); -MVM_DEBUGFS_WRITE_FILE_OPS(bt_tx_prio, 10); -MVM_DEBUGFS_WRITE_FILE_OPS(bt_force_ant, 10); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg_conf, 8); -MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_collect, 8); - -#ifdef CONFIG_IWLWIFI_BCAST_FILTERING -MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters_macs, 256); -#endif - -#ifdef CONFIG_PM_SLEEP -MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram, 8); -#endif - -int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) -{ - struct dentry *bcast_dir __maybe_unused; - char buf[100]; - - spin_lock_init(&mvm->drv_stats_lock); - - mvm->debugfs_dir = dbgfs_dir; - - MVM_DEBUGFS_ADD_FILE(tx_flush, mvm->debugfs_dir, S_IWUSR); - 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(set_nic_temperature, mvm->debugfs_dir, - S_IWUSR | S_IRUSR); - MVM_DEBUGFS_ADD_FILE(nic_temp, dbgfs_dir, 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(bt_cmd, dbgfs_dir, S_IRUSR); - MVM_DEBUGFS_ADD_FILE(disable_power_off, mvm->debugfs_dir, - S_IRUSR | S_IWUSR); - MVM_DEBUGFS_ADD_FILE(fw_rx_stats, mvm->debugfs_dir, S_IRUSR); - MVM_DEBUGFS_ADD_FILE(drv_rx_stats, mvm->debugfs_dir, S_IRUSR); - MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR); - MVM_DEBUGFS_ADD_FILE(fw_nmi, mvm->debugfs_dir, S_IWUSR); - MVM_DEBUGFS_ADD_FILE(bt_tx_prio, mvm->debugfs_dir, S_IWUSR); - MVM_DEBUGFS_ADD_FILE(bt_force_ant, mvm->debugfs_dir, S_IWUSR); - MVM_DEBUGFS_ADD_FILE(scan_ant_rxchain, mvm->debugfs_dir, - S_IWUSR | S_IRUSR); - MVM_DEBUGFS_ADD_FILE(prph_reg, mvm->debugfs_dir, S_IWUSR | S_IRUSR); - MVM_DEBUGFS_ADD_FILE(d0i3_refs, mvm->debugfs_dir, S_IRUSR | S_IWUSR); - MVM_DEBUGFS_ADD_FILE(fw_dbg_conf, mvm->debugfs_dir, S_IRUSR | S_IWUSR); - MVM_DEBUGFS_ADD_FILE(fw_dbg_collect, mvm->debugfs_dir, S_IWUSR); - MVM_DEBUGFS_ADD_FILE(send_echo_cmd, mvm->debugfs_dir, S_IWUSR); - if (!debugfs_create_bool("enable_scan_iteration_notif", - S_IRUSR | S_IWUSR, - mvm->debugfs_dir, - &mvm->scan_iter_notif_enabled)) - goto err; - -#ifdef CONFIG_IWLWIFI_BCAST_FILTERING - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING) { - bcast_dir = debugfs_create_dir("bcast_filtering", - mvm->debugfs_dir); - if (!bcast_dir) - goto err; - - if (!debugfs_create_bool("override", S_IRUSR | S_IWUSR, - bcast_dir, - &mvm->dbgfs_bcast_filtering.override)) - goto err; - - MVM_DEBUGFS_ADD_FILE_ALIAS("filters", bcast_filters, - bcast_dir, S_IWUSR | S_IRUSR); - MVM_DEBUGFS_ADD_FILE_ALIAS("macs", bcast_filters_macs, - bcast_dir, S_IWUSR | S_IRUSR); - } -#endif - -#ifdef CONFIG_PM_SLEEP - MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, S_IRUSR | S_IWUSR); - MVM_DEBUGFS_ADD_FILE(d3_test, mvm->debugfs_dir, S_IRUSR); - if (!debugfs_create_bool("d3_wake_sysassert", S_IRUSR | S_IWUSR, - mvm->debugfs_dir, &mvm->d3_wake_sysassert)) - goto err; - if (!debugfs_create_u32("last_netdetect_scans", S_IRUSR, - mvm->debugfs_dir, &mvm->last_netdetect_scans)) - goto err; -#endif - - if (!debugfs_create_u8("low_latency_agg_frame_limit", S_IRUSR | S_IWUSR, - mvm->debugfs_dir, - &mvm->low_latency_agg_frame_limit)) - goto err; - if (!debugfs_create_u8("ps_disabled", S_IRUSR, - mvm->debugfs_dir, &mvm->ps_disabled)) - goto err; - if (!debugfs_create_blob("nvm_hw", S_IRUSR, - mvm->debugfs_dir, &mvm->nvm_hw_blob)) - goto err; - if (!debugfs_create_blob("nvm_sw", S_IRUSR, - mvm->debugfs_dir, &mvm->nvm_sw_blob)) - goto err; - if (!debugfs_create_blob("nvm_calib", S_IRUSR, - mvm->debugfs_dir, &mvm->nvm_calib_blob)) - goto err; - if (!debugfs_create_blob("nvm_prod", S_IRUSR, - mvm->debugfs_dir, &mvm->nvm_prod_blob)) - goto err; - if (!debugfs_create_blob("nvm_phy_sku", S_IRUSR, - mvm->debugfs_dir, &mvm->nvm_phy_sku_blob)) - goto err; - - /* - * Create a symlink with mac80211. It will be removed when mac80211 - * exists (before the opmode exists which removes the target.) - */ - snprintf(buf, 100, "../../%s/%s", - dbgfs_dir->d_parent->d_parent->d_name.name, - dbgfs_dir->d_parent->d_name.name); - if (!debugfs_create_symlink("iwlwifi", mvm->hw->wiphy->debugfsdir, buf)) - goto err; - - return 0; -err: - IWL_ERR(mvm, "Can't create the mvm debugfs directory\n"); - return -ENOMEM; -} diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.h b/drivers/net/wireless/iwlwifi/mvm/debugfs.h deleted file mode 100644 index 8c4190e7e..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.h +++ /dev/null @@ -1,103 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * 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. - * - *****************************************************************************/ - -#define MVM_DEBUGFS_READ_FILE_OPS(name) \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .read = iwl_dbgfs_##name##_read, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -} - -#define MVM_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \ -static ssize_t _iwl_dbgfs_##name##_write(struct file *file, \ - const char __user *user_buf, \ - size_t count, loff_t *ppos) \ -{ \ - argtype *arg = file->private_data; \ - char buf[buflen] = {}; \ - size_t buf_size = min(count, sizeof(buf) - 1); \ - \ - if (copy_from_user(buf, user_buf, buf_size)) \ - return -EFAULT; \ - \ - return iwl_dbgfs_##name##_write(arg, buf, buf_size, ppos); \ -} \ - -#define _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, buflen, argtype) \ -MVM_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .write = _iwl_dbgfs_##name##_write, \ - .read = iwl_dbgfs_##name##_read, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; - -#define _MVM_DEBUGFS_WRITE_FILE_OPS(name, buflen, argtype) \ -MVM_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .write = _iwl_dbgfs_##name##_write, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h deleted file mode 100644 index d398a6102..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h +++ /dev/null @@ -1,476 +0,0 @@ -/****************************************************************************** - * - * 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 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * 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 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * 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_COEX_MODE_POS: - * @BT_COEX_MODE_MSK: - * @BT_COEX_DISABLE_OLD: - * @BT_COEX_2W_OLD: - * @BT_COEX_3W_OLD: - * @BT_COEX_NW_OLD: - * @BT_COEX_AUTO_OLD: - * @BT_COEX_BT_OLD: Antenna is for BT (manufacuring tests) - * @BT_COEX_WIFI_OLD: Antenna is for BT (manufacuring tests) - * @BT_COEX_SYNC2SCO: - * @BT_COEX_CORUNNING: - * @BT_COEX_MPLUT: - * @BT_COEX_TTC: - * @BT_COEX_RRC: - * - * The COEX_MODE must be set for each command. Even if it is not changed. - */ -enum iwl_bt_coex_flags { - BT_COEX_MODE_POS = 3, - BT_COEX_MODE_MSK = BITS(3) << BT_COEX_MODE_POS, - BT_COEX_DISABLE_OLD = 0x0 << BT_COEX_MODE_POS, - BT_COEX_2W_OLD = 0x1 << BT_COEX_MODE_POS, - BT_COEX_3W_OLD = 0x2 << BT_COEX_MODE_POS, - BT_COEX_NW_OLD = 0x3 << BT_COEX_MODE_POS, - BT_COEX_AUTO_OLD = 0x5 << BT_COEX_MODE_POS, - BT_COEX_BT_OLD = 0x6 << BT_COEX_MODE_POS, - BT_COEX_WIFI_OLD = 0x7 << BT_COEX_MODE_POS, - BT_COEX_SYNC2SCO = BIT(7), - BT_COEX_CORUNNING = BIT(8), - BT_COEX_MPLUT = BIT(9), - BT_COEX_TTC = BIT(20), - BT_COEX_RRC = BIT(21), -}; - -/* - * indicates what has changed in the BT_COEX command. - * BT_VALID_ENABLE must be set for each command. Commands without this bit will - * discarded by the firmware - */ -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), - BT_VALID_TXTX_DELTA_FREQ_THRS = BIT(16), - BT_VALID_TXRX_MAX_FREQ_0 = BIT(17), - BT_VALID_SYNC_TO_SCO = BIT(18), - BT_VALID_TTC = BIT(20), - BT_VALID_RRC = BIT(21), -}; - -/** - * 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), -}; - -enum iwl_bt_coex_lut_type { - BT_COEX_TIGHT_LUT = 0, - BT_COEX_LOOSE_LUT, - BT_COEX_TX_DIS_LUT, - - BT_COEX_MAX_LUT, - BT_COEX_INVALID_LUT = 0xff, -}; /* BT_COEX_DECISION_LUT_INDEX_API_E_VER_1 */ - -#define BT_COEX_LUT_SIZE (12) -#define BT_COEX_CORUN_LUT_SIZE (32) -#define BT_COEX_MULTI_PRIO_LUT_SIZE (2) -#define BT_COEX_BOOST_SIZE (4) -#define BT_REDUCED_TX_POWER_BIT BIT(7) - -/** - * struct iwl_bt_coex_cmd_old - bt coex configuration command - * @flags:&enum iwl_bt_coex_flags - * @max_kill: - * @bt_reduced_tx_power: enum %iwl_bt_reduced_tx_power - * @override_primary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT - * should be set by default - * @override_secondary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT - * should be set by default - * @bt4_antenna_isolation: antenna isolation - * @bt4_antenna_isolation_thr: antenna threshold value - * @bt4_tx_tx_delta_freq_thr: TxTx delta frequency - * @bt4_tx_rx_max_freq0: TxRx max frequency - * @bt_prio_boost: BT priority boost registers - * @wifi_tx_prio_boost: SW boost of wifi tx priority - * @wifi_rx_prio_boost: SW boost of wifi rx priority - * @kill_ack_msk: kill ACK mask. 1 - Tx ACK, 0 - kill Tx of ACK. - * @kill_cts_msk: kill CTS mask. 1 - Tx CTS, 0 - kill Tx of CTS. - * @decision_lut: PTA decision LUT, per Prio-Ch - * @bt4_multiprio_lut: multi priority LUT configuration - * @bt4_corun_lut20: co-running 20 MHz LUT configuration - * @bt4_corun_lut40: co-running 40 MHz LUT configuration - * @valid_bit_msk: enum %iwl_bt_coex_valid_bit_msk - * - * The structure is used for the BT_COEX command. - */ -struct iwl_bt_coex_cmd_old { - __le32 flags; - u8 max_kill; - u8 bt_reduced_tx_power; - u8 override_primary_lut; - u8 override_secondary_lut; - - u8 bt4_antenna_isolation; - u8 bt4_antenna_isolation_thr; - u8 bt4_tx_tx_delta_freq_thr; - u8 bt4_tx_rx_max_freq0; - - __le32 bt_prio_boost[BT_COEX_BOOST_SIZE]; - __le32 wifi_tx_prio_boost; - __le32 wifi_rx_prio_boost; - __le32 kill_ack_msk; - __le32 kill_cts_msk; - - __le32 decision_lut[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE]; - __le32 bt4_multiprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE]; - __le32 bt4_corun_lut20[BT_COEX_CORUN_LUT_SIZE]; - __le32 bt4_corun_lut40[BT_COEX_CORUN_LUT_SIZE]; - - __le32 valid_bit_msk; -} __packed; /* BT_COEX_CMD_API_S_VER_5 */ - -enum iwl_bt_coex_mode { - BT_COEX_DISABLE = 0x0, - BT_COEX_NW = 0x1, - BT_COEX_BT = 0x2, - BT_COEX_WIFI = 0x3, -}; /* BT_COEX_MODES_E */ - -enum iwl_bt_coex_enabled_modules { - BT_COEX_MPLUT_ENABLED = BIT(0), - BT_COEX_MPLUT_BOOST_ENABLED = BIT(1), - BT_COEX_SYNC2SCO_ENABLED = BIT(2), - BT_COEX_CORUN_ENABLED = BIT(3), - BT_COEX_HIGH_BAND_RET = BIT(4), -}; /* BT_COEX_MODULES_ENABLE_E_VER_1 */ - -/** - * struct iwl_bt_coex_cmd - bt coex configuration command - * @mode: enum %iwl_bt_coex_mode - * @enabled_modules: enum %iwl_bt_coex_enabled_modules - * - * The structure is used for the BT_COEX command. - */ -struct iwl_bt_coex_cmd { - __le32 mode; - __le32 enabled_modules; -} __packed; /* BT_COEX_CMD_API_S_VER_6 */ - -/** - * struct iwl_bt_coex_corun_lut_update - bt coex update the corun lut - * @corun_lut20: co-running 20 MHz LUT configuration - * @corun_lut40: co-running 40 MHz LUT configuration - * - * The structure is used for the BT_COEX_UPDATE_CORUN_LUT command. - */ -struct iwl_bt_coex_corun_lut_update_cmd { - __le32 corun_lut20[BT_COEX_CORUN_LUT_SIZE]; - __le32 corun_lut40[BT_COEX_CORUN_LUT_SIZE]; -} __packed; /* BT_COEX_UPDATE_CORUN_LUT_API_S_VER_1 */ - -/** - * struct iwl_bt_coex_reduced_txp_update_cmd - * @reduced_txp: bit BT_REDUCED_TX_POWER_BIT to enable / disable, rest of the - * bits are the sta_id (value) - */ -struct iwl_bt_coex_reduced_txp_update_cmd { - __le32 reduced_txp; -} __packed; /* BT_COEX_UPDATE_REDUCED_TX_POWER_API_S_VER_1 */ - -/** - * struct iwl_bt_coex_ci_cmd - bt coex channel inhibition command - * @bt_primary_ci: - * @primary_ch_phy_id: - * @bt_secondary_ci: - * @secondary_ch_phy_id: - * - * Used for BT_COEX_CI command - */ -struct iwl_bt_coex_ci_cmd { - __le64 bt_primary_ci; - __le32 primary_ch_phy_id; - - __le64 bt_secondary_ci; - __le32 secondary_ch_phy_id; -} __packed; /* BT_CI_MSG_API_S_VER_2 */ - -#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) - -enum iwl_bt_activity_grading { - BT_OFF = 0, - BT_ON_NO_CONNECTION = 1, - BT_LOW_TRAFFIC = 2, - BT_HIGH_TRAFFIC = 3, - - BT_MAX_AG, -}; /* BT_COEX_BT_ACTIVITY_GRADING_API_E_VER_1 */ - -enum iwl_bt_ci_compliance { - BT_CI_COMPLIANCE_NONE = 0, - BT_CI_COMPLIANCE_PRIMARY = 1, - BT_CI_COMPLIANCE_SECONDARY = 2, - BT_CI_COMPLIANCE_BOTH = 3, -}; /* BT_COEX_CI_COMPLIENCE_E_VER_1 */ - -#define IWL_COEX_IS_TTC_ON(_ttc_rrc_status, _phy_id) \ - (_ttc_rrc_status & BIT(_phy_id)) - -#define IWL_COEX_IS_RRC_ON(_ttc_rrc_status, _phy_id) \ - ((_ttc_rrc_status >> 4) & BIT(_phy_id)) - -/** - * struct iwl_bt_coex_profile_notif - notification about BT coex - * @mbox_msg: message from BT to WiFi - * @msg_idx: the index of the message - * @bt_ci_compliance: enum %iwl_bt_ci_compliance - * @primary_ch_lut: LUT used for primary channel enum %iwl_bt_coex_lut_type - * @secondary_ch_lut: LUT used for secondary channel enume %iwl_bt_coex_lut_type - * @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading - * @ttc_rrc_status: is TTC or RRC enabled - one bit per PHY - */ -struct iwl_bt_coex_profile_notif { - __le32 mbox_msg[4]; - __le32 msg_idx; - __le32 bt_ci_compliance; - - __le32 primary_ch_lut; - __le32 secondary_ch_lut; - __le32 bt_activity_grading; - u8 ttc_rrc_status; - u8 reserved[3]; -} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_4 */ - -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; - -/** - * struct iwl_bt_coex_ci_cmd_old - bt coex channel inhibition command - * @bt_primary_ci: - * @bt_secondary_ci: - * @co_run_bw_primary: - * @co_run_bw_secondary: - * @primary_ch_phy_id: - * @secondary_ch_phy_id: - * - * Used for BT_COEX_CI command - */ -struct iwl_bt_coex_ci_cmd_old { - __le64 bt_primary_ci; - __le64 bt_secondary_ci; - - u8 co_run_bw_primary; - u8 co_run_bw_secondary; - u8 primary_ch_phy_id; - u8 secondary_ch_phy_id; -} __packed; /* BT_CI_MSG_API_S_VER_1 */ - -/** - * struct iwl_bt_coex_profile_notif_old - notification about BT coex - * @mbox_msg: message from BT to WiFi - * @msg_idx: the index of the message - * @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 - * @primary_ch_lut: LUT used for primary channel - * @secondary_ch_lut: LUT used for secondary channel - * @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading - */ -struct iwl_bt_coex_profile_notif_old { - __le32 mbox_msg[4]; - __le32 msg_idx; - u8 bt_status; - u8 bt_open_conn; - u8 bt_traffic_load; - u8 bt_agg_traffic_load; - u8 bt_ci_compliance; - u8 ttc_enabled; - u8 rrc_enabled; - u8 reserved; - - __le32 primary_ch_lut; - __le32 secondary_ch_lut; - __le32 bt_activity_grading; -} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_3 */ - -#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 deleted file mode 100644 index 20521bebb..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h +++ /dev/null @@ -1,425 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * 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_d3_h__ -#define __fw_api_d3_h__ - -/** - * enum iwl_d3_wakeup_flags - D3 manager wakeup flags - * @IWL_WAKEUP_D3_CONFIG_FW_ERROR: wake up on firmware sysassert - */ -enum iwl_d3_wakeup_flags { - IWL_WAKEUP_D3_CONFIG_FW_ERROR = BIT(0), -}; /* D3_MANAGER_WAKEUP_CONFIG_API_E_VER_3 */ - -/** - * struct iwl_d3_manager_config - D3 manager configuration command - * @min_sleep_time: minimum sleep time (in usec) - * @wakeup_flags: wakeup flags, see &enum iwl_d3_wakeup_flags - * @wakeup_host_timer: force wakeup after this many seconds - * - * The structure is used for the D3_CONFIG_CMD command. - */ -struct iwl_d3_manager_config { - __le32 min_sleep_time; - __le32 wakeup_flags; - __le32 wakeup_host_timer; -} __packed; /* D3_MANAGER_CONFIG_CMD_S_VER_4 */ - - -/* TODO: OFFLOADS_QUERY_API_S_VER_1 */ - -/** - * enum iwl_d3_proto_offloads - enabled protocol offloads - * @IWL_D3_PROTO_OFFLOAD_ARP: ARP data is enabled - * @IWL_D3_PROTO_OFFLOAD_NS: NS (Neighbor Solicitation) is enabled - */ -enum iwl_proto_offloads { - IWL_D3_PROTO_OFFLOAD_ARP = BIT(0), - IWL_D3_PROTO_OFFLOAD_NS = BIT(1), -}; - -#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1 2 -#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2 6 -#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L 12 -#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S 4 -#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX 12 - -#define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L 4 -#define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S 2 - -/** - * struct iwl_proto_offload_cmd_common - ARP/NS offload common part - * @enabled: enable flags - * @remote_ipv4_addr: remote address to answer to (or zero if all) - * @host_ipv4_addr: our IPv4 address to respond to queries for - * @arp_mac_addr: our MAC address for ARP responses - * @reserved: unused - */ -struct iwl_proto_offload_cmd_common { - __le32 enabled; - __be32 remote_ipv4_addr; - __be32 host_ipv4_addr; - u8 arp_mac_addr[ETH_ALEN]; - __le16 reserved; -} __packed; - -/** - * struct iwl_proto_offload_cmd_v1 - ARP/NS offload configuration - * @common: common/IPv4 configuration - * @remote_ipv6_addr: remote address to answer to (or zero if all) - * @solicited_node_ipv6_addr: broken -- solicited node address exists - * for each target address - * @target_ipv6_addr: our target addresses - * @ndp_mac_addr: neighbor solicitation response MAC address - */ -struct iwl_proto_offload_cmd_v1 { - struct iwl_proto_offload_cmd_common common; - u8 remote_ipv6_addr[16]; - u8 solicited_node_ipv6_addr[16]; - u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1][16]; - u8 ndp_mac_addr[ETH_ALEN]; - __le16 reserved2; -} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_1 */ - -/** - * struct iwl_proto_offload_cmd_v2 - ARP/NS offload configuration - * @common: common/IPv4 configuration - * @remote_ipv6_addr: remote address to answer to (or zero if all) - * @solicited_node_ipv6_addr: broken -- solicited node address exists - * for each target address - * @target_ipv6_addr: our target addresses - * @ndp_mac_addr: neighbor solicitation response MAC address - */ -struct iwl_proto_offload_cmd_v2 { - struct iwl_proto_offload_cmd_common common; - u8 remote_ipv6_addr[16]; - u8 solicited_node_ipv6_addr[16]; - u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2][16]; - u8 ndp_mac_addr[ETH_ALEN]; - u8 numValidIPv6Addresses; - u8 reserved2[3]; -} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_2 */ - -struct iwl_ns_config { - struct in6_addr source_ipv6_addr; - struct in6_addr dest_ipv6_addr; - u8 target_mac_addr[ETH_ALEN]; - __le16 reserved; -} __packed; /* NS_OFFLOAD_CONFIG */ - -struct iwl_targ_addr { - struct in6_addr addr; - __le32 config_num; -} __packed; /* TARGET_IPV6_ADDRESS */ - -/** - * struct iwl_proto_offload_cmd_v3_small - ARP/NS offload configuration - * @common: common/IPv4 configuration - * @target_ipv6_addr: target IPv6 addresses - * @ns_config: NS offload configurations - */ -struct iwl_proto_offload_cmd_v3_small { - struct iwl_proto_offload_cmd_common common; - __le32 num_valid_ipv6_addrs; - struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S]; - struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S]; -} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */ - -/** - * struct iwl_proto_offload_cmd_v3_large - ARP/NS offload configuration - * @common: common/IPv4 configuration - * @target_ipv6_addr: target IPv6 addresses - * @ns_config: NS offload configurations - */ -struct iwl_proto_offload_cmd_v3_large { - struct iwl_proto_offload_cmd_common common; - __le32 num_valid_ipv6_addrs; - struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L]; - struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L]; -} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */ - -/* - * WOWLAN_PATTERNS - */ -#define IWL_WOWLAN_MIN_PATTERN_LEN 16 -#define IWL_WOWLAN_MAX_PATTERN_LEN 128 - -struct iwl_wowlan_pattern { - u8 mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8]; - u8 pattern[IWL_WOWLAN_MAX_PATTERN_LEN]; - u8 mask_size; - u8 pattern_size; - __le16 reserved; -} __packed; /* WOWLAN_PATTERN_API_S_VER_1 */ - -#define IWL_WOWLAN_MAX_PATTERNS 20 - -struct iwl_wowlan_patterns_cmd { - __le32 n_patterns; - struct iwl_wowlan_pattern patterns[]; -} __packed; /* WOWLAN_PATTERN_ARRAY_API_S_VER_1 */ - -enum iwl_wowlan_wakeup_filters { - IWL_WOWLAN_WAKEUP_MAGIC_PACKET = BIT(0), - IWL_WOWLAN_WAKEUP_PATTERN_MATCH = BIT(1), - IWL_WOWLAN_WAKEUP_BEACON_MISS = BIT(2), - IWL_WOWLAN_WAKEUP_LINK_CHANGE = BIT(3), - IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL = BIT(4), - IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ = BIT(5), - IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE = BIT(6), - IWL_WOWLAN_WAKEUP_ENABLE_NET_DETECT = BIT(7), - IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT = BIT(8), - IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS = BIT(9), - IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE = BIT(10), - IWL_WOWLAN_WAKEUP_REMOTE_TCP_EXTERNAL = BIT(11), - IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET = BIT(12), - IWL_WOWLAN_WAKEUP_IOAC_MAGIC_PACKET = BIT(13), - IWL_WOWLAN_WAKEUP_HOST_TIMER = BIT(14), - IWL_WOWLAN_WAKEUP_RX_FRAME = BIT(15), - IWL_WOWLAN_WAKEUP_BCN_FILTERING = BIT(16), -}; /* WOWLAN_WAKEUP_FILTER_API_E_VER_4 */ - -struct iwl_wowlan_config_cmd { - __le32 wakeup_filter; - __le16 non_qos_seq; - __le16 qos_seq[8]; - u8 wowlan_ba_teardown_tids; - u8 is_11n_connection; - u8 offloading_tid; - u8 reserved[3]; -} __packed; /* WOWLAN_CONFIG_API_S_VER_3 */ - -/* - * WOWLAN_TSC_RSC_PARAMS - */ -#define IWL_NUM_RSC 16 - -struct tkip_sc { - __le16 iv16; - __le16 pad; - __le32 iv32; -} __packed; /* TKIP_SC_API_U_VER_1 */ - -struct iwl_tkip_rsc_tsc { - struct tkip_sc unicast_rsc[IWL_NUM_RSC]; - struct tkip_sc multicast_rsc[IWL_NUM_RSC]; - struct tkip_sc tsc; -} __packed; /* TKIP_TSC_RSC_API_S_VER_1 */ - -struct aes_sc { - __le64 pn; -} __packed; /* TKIP_AES_SC_API_U_VER_1 */ - -struct iwl_aes_rsc_tsc { - struct aes_sc unicast_rsc[IWL_NUM_RSC]; - struct aes_sc multicast_rsc[IWL_NUM_RSC]; - struct aes_sc tsc; -} __packed; /* AES_TSC_RSC_API_S_VER_1 */ - -union iwl_all_tsc_rsc { - struct iwl_tkip_rsc_tsc tkip; - struct iwl_aes_rsc_tsc aes; -}; /* ALL_TSC_RSC_API_S_VER_2 */ - -struct iwl_wowlan_rsc_tsc_params_cmd { - union iwl_all_tsc_rsc all_tsc_rsc; -} __packed; /* ALL_TSC_RSC_API_S_VER_2 */ - -#define IWL_MIC_KEY_SIZE 8 -struct iwl_mic_keys { - u8 tx[IWL_MIC_KEY_SIZE]; - u8 rx_unicast[IWL_MIC_KEY_SIZE]; - u8 rx_mcast[IWL_MIC_KEY_SIZE]; -} __packed; /* MIC_KEYS_API_S_VER_1 */ - -#define IWL_P1K_SIZE 5 -struct iwl_p1k_cache { - __le16 p1k[IWL_P1K_SIZE]; -} __packed; - -#define IWL_NUM_RX_P1K_CACHE 2 - -struct iwl_wowlan_tkip_params_cmd { - struct iwl_mic_keys mic_keys; - struct iwl_p1k_cache tx; - struct iwl_p1k_cache rx_uni[IWL_NUM_RX_P1K_CACHE]; - struct iwl_p1k_cache rx_multi[IWL_NUM_RX_P1K_CACHE]; -} __packed; /* WOWLAN_TKIP_SETTING_API_S_VER_1 */ - -#define IWL_KCK_MAX_SIZE 32 -#define IWL_KEK_MAX_SIZE 32 - -struct iwl_wowlan_kek_kck_material_cmd { - u8 kck[IWL_KCK_MAX_SIZE]; - u8 kek[IWL_KEK_MAX_SIZE]; - __le16 kck_len; - __le16 kek_len; - __le64 replay_ctr; -} __packed; /* KEK_KCK_MATERIAL_API_S_VER_2 */ - -#define RF_KILL_INDICATOR_FOR_WOWLAN 0x87 - -enum iwl_wowlan_rekey_status { - IWL_WOWLAN_REKEY_POST_REKEY = 0, - IWL_WOWLAN_REKEY_WHILE_REKEY = 1, -}; /* WOWLAN_REKEY_STATUS_API_E_VER_1 */ - -enum iwl_wowlan_wakeup_reason { - IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS = 0, - IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET = BIT(0), - IWL_WOWLAN_WAKEUP_BY_PATTERN = BIT(1), - IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON = BIT(2), - IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH = BIT(3), - IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE = BIT(4), - IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED = BIT(5), - IWL_WOWLAN_WAKEUP_BY_UCODE_ERROR = BIT(6), - IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST = BIT(7), - 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), - IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET = BIT(12), - IWL_WOWLAN_WAKEUP_BY_IOAC_MAGIC_PACKET = BIT(13), - IWL_WOWLAN_WAKEUP_BY_D3_WAKEUP_HOST_TIMER = BIT(14), - IWL_WOWLAN_WAKEUP_BY_RXFRAME_FILTERED_IN = BIT(15), - IWL_WOWLAN_WAKEUP_BY_BEACON_FILTERED_IN = BIT(16), - -}; /* WOWLAN_WAKE_UP_REASON_API_E_VER_2 */ - -struct iwl_wowlan_gtk_status { - u8 key_index; - u8 reserved[3]; - u8 decrypt_key[16]; - u8 tkip_mic_key[8]; - struct iwl_wowlan_rsc_tsc_params_cmd rsc; -} __packed; - -struct iwl_wowlan_status { - struct iwl_wowlan_gtk_status gtk; - __le64 replay_ctr; - __le16 pattern_number; - __le16 non_qos_seq_ctr; - __le16 qos_seq_ctr[8]; - __le32 wakeup_reasons; - __le32 num_of_gtk_rekeys; - __le32 transmitted_ndps; - __le32 received_beacons; - __le32 wake_packet_length; - __le32 wake_packet_bufsize; - u8 wake_packet[]; /* can be truncated from _length to _bufsize */ -} __packed; /* WOWLAN_STATUSES_API_S_VER_6 */ - -#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 deleted file mode 100644 index f3f3ee0a7..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h +++ /dev/null @@ -1,387 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 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) 2012 - 2014 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_mac_h__ -#define __fw_api_mac_h__ - -/* - * The first MAC indices (starting from 0) - * are available to the driver, AUX follows - */ -#define MAC_INDEX_AUX 4 -#define MAC_INDEX_MIN_DRIVER 0 -#define NUM_MAC_INDEX_DRIVER MAC_INDEX_AUX -#define NUM_MAC_INDEX (MAC_INDEX_AUX + 1) - -enum iwl_ac { - AC_BK, - AC_BE, - AC_VI, - AC_VO, - AC_NUM, -}; - -/** - * enum iwl_mac_protection_flags - MAC context flags - * @MAC_PROT_FLG_TGG_PROTECT: 11g protection when transmitting OFDM frames, - * this will require CCK RTS/CTS2self. - * RTS/CTS will protect full burst time. - * @MAC_PROT_FLG_HT_PROT: enable HT protection - * @MAC_PROT_FLG_FAT_PROT: protect 40 MHz transmissions - * @MAC_PROT_FLG_SELF_CTS_EN: allow CTS2self - */ -enum iwl_mac_protection_flags { - MAC_PROT_FLG_TGG_PROTECT = BIT(3), - MAC_PROT_FLG_HT_PROT = BIT(23), - MAC_PROT_FLG_FAT_PROT = BIT(24), - MAC_PROT_FLG_SELF_CTS_EN = BIT(30), -}; - -#define MAC_FLG_SHORT_SLOT BIT(4) -#define MAC_FLG_SHORT_PREAMBLE BIT(5) - -/** - * enum iwl_mac_types - Supported MAC types - * @FW_MAC_TYPE_FIRST: lowest supported MAC type - * @FW_MAC_TYPE_AUX: Auxiliary MAC (internal) - * @FW_MAC_TYPE_LISTENER: monitor MAC type (?) - * @FW_MAC_TYPE_PIBSS: Pseudo-IBSS - * @FW_MAC_TYPE_IBSS: IBSS - * @FW_MAC_TYPE_BSS_STA: BSS (managed) station - * @FW_MAC_TYPE_P2P_DEVICE: P2P Device - * @FW_MAC_TYPE_P2P_STA: P2P client - * @FW_MAC_TYPE_GO: P2P GO - * @FW_MAC_TYPE_TEST: ? - * @FW_MAC_TYPE_MAX: highest support MAC type - */ -enum iwl_mac_types { - FW_MAC_TYPE_FIRST = 1, - FW_MAC_TYPE_AUX = FW_MAC_TYPE_FIRST, - FW_MAC_TYPE_LISTENER, - FW_MAC_TYPE_PIBSS, - FW_MAC_TYPE_IBSS, - FW_MAC_TYPE_BSS_STA, - FW_MAC_TYPE_P2P_DEVICE, - FW_MAC_TYPE_P2P_STA, - FW_MAC_TYPE_GO, - FW_MAC_TYPE_TEST, - FW_MAC_TYPE_MAX = FW_MAC_TYPE_TEST -}; /* MAC_CONTEXT_TYPE_API_E_VER_1 */ - -/** - * enum iwl_tsf_id - TSF hw timer ID - * @TSF_ID_A: use TSF A - * @TSF_ID_B: use TSF B - * @TSF_ID_C: use TSF C - * @TSF_ID_D: use TSF D - * @NUM_TSF_IDS: number of TSF timers available - */ -enum iwl_tsf_id { - TSF_ID_A = 0, - TSF_ID_B = 1, - TSF_ID_C = 2, - TSF_ID_D = 3, - NUM_TSF_IDS = 4, -}; /* TSF_ID_API_E_VER_1 */ - -/** - * struct iwl_mac_data_ap - configuration data for AP MAC context - * @beacon_time: beacon transmit time in system time - * @beacon_tsf: beacon transmit time in TSF - * @bi: beacon interval in TU - * @bi_reciprocal: 2^32 / bi - * @dtim_interval: dtim transmit time in TU - * @dtim_reciprocal: 2^32 / dtim_interval - * @mcast_qid: queue ID for multicast traffic - * @beacon_template: beacon template ID - */ -struct iwl_mac_data_ap { - __le32 beacon_time; - __le64 beacon_tsf; - __le32 bi; - __le32 bi_reciprocal; - __le32 dtim_interval; - __le32 dtim_reciprocal; - __le32 mcast_qid; - __le32 beacon_template; -} __packed; /* AP_MAC_DATA_API_S_VER_1 */ - -/** - * struct iwl_mac_data_ibss - configuration data for IBSS MAC context - * @beacon_time: beacon transmit time in system time - * @beacon_tsf: beacon transmit time in TSF - * @bi: beacon interval in TU - * @bi_reciprocal: 2^32 / bi - * @beacon_template: beacon template ID - */ -struct iwl_mac_data_ibss { - __le32 beacon_time; - __le64 beacon_tsf; - __le32 bi; - __le32 bi_reciprocal; - __le32 beacon_template; -} __packed; /* IBSS_MAC_DATA_API_S_VER_1 */ - -/** - * struct iwl_mac_data_sta - configuration data for station MAC context - * @is_assoc: 1 for associated state, 0 otherwise - * @dtim_time: DTIM arrival time in system time - * @dtim_tsf: DTIM arrival time in TSF - * @bi: beacon interval in TU, applicable only when associated - * @bi_reciprocal: 2^32 / bi , applicable only when associated - * @dtim_interval: DTIM interval in TU, applicable only when associated - * @dtim_reciprocal: 2^32 / dtim_interval , applicable only when associated - * @listen_interval: in beacon intervals, applicable only when associated - * @assoc_id: unique ID assigned by the AP during association - */ -struct iwl_mac_data_sta { - __le32 is_assoc; - __le32 dtim_time; - __le64 dtim_tsf; - __le32 bi; - __le32 bi_reciprocal; - __le32 dtim_interval; - __le32 dtim_reciprocal; - __le32 listen_interval; - __le32 assoc_id; - __le32 assoc_beacon_arrive_time; -} __packed; /* STA_MAC_DATA_API_S_VER_1 */ - -/** - * struct iwl_mac_data_go - configuration data for P2P GO MAC context - * @ap: iwl_mac_data_ap struct with most config data - * @ctwin: client traffic window in TU (period after TBTT when GO is present). - * 0 indicates that there is no CT window. - * @opp_ps_enabled: indicate that opportunistic PS allowed - */ -struct iwl_mac_data_go { - struct iwl_mac_data_ap ap; - __le32 ctwin; - __le32 opp_ps_enabled; -} __packed; /* GO_MAC_DATA_API_S_VER_1 */ - -/** - * struct iwl_mac_data_p2p_sta - configuration data for P2P client MAC context - * @sta: iwl_mac_data_sta struct with most config data - * @ctwin: client traffic window in TU (period after TBTT when GO is present). - * 0 indicates that there is no CT window. - */ -struct iwl_mac_data_p2p_sta { - struct iwl_mac_data_sta sta; - __le32 ctwin; -} __packed; /* P2P_STA_MAC_DATA_API_S_VER_1 */ - -/** - * struct iwl_mac_data_pibss - Pseudo IBSS config data - * @stats_interval: interval in TU between statistics notifications to host. - */ -struct iwl_mac_data_pibss { - __le32 stats_interval; -} __packed; /* PIBSS_MAC_DATA_API_S_VER_1 */ - -/* - * struct iwl_mac_data_p2p_dev - configuration data for the P2P Device MAC - * context. - * @is_disc_extended: if set to true, P2P Device discoverability is enabled on - * other channels as well. This should be to true only in case that the - * device is discoverable and there is an active GO. Note that setting this - * field when not needed, will increase the number of interrupts and have - * effect on the platform power, as this setting opens the Rx filters on - * all macs. - */ -struct iwl_mac_data_p2p_dev { - __le32 is_disc_extended; -} __packed; /* _P2P_DEV_MAC_DATA_API_S_VER_1 */ - -/** - * enum iwl_mac_filter_flags - MAC context filter flags - * @MAC_FILTER_IN_PROMISC: accept all data frames - * @MAC_FILTER_IN_CONTROL_AND_MGMT: pass all management and - * control frames to the host - * @MAC_FILTER_ACCEPT_GRP: accept multicast frames - * @MAC_FILTER_DIS_DECRYPT: don't decrypt unicast frames - * @MAC_FILTER_DIS_GRP_DECRYPT: don't decrypt multicast frames - * @MAC_FILTER_IN_BEACON: transfer foreign BSS's beacons to host - * (in station mode when associated) - * @MAC_FILTER_OUT_BCAST: filter out all broadcast frames - * @MAC_FILTER_IN_CRC32: extract FCS and append it to frames - * @MAC_FILTER_IN_PROBE_REQUEST: pass probe requests to host - */ -enum iwl_mac_filter_flags { - MAC_FILTER_IN_PROMISC = BIT(0), - MAC_FILTER_IN_CONTROL_AND_MGMT = BIT(1), - MAC_FILTER_ACCEPT_GRP = BIT(2), - MAC_FILTER_DIS_DECRYPT = BIT(3), - MAC_FILTER_DIS_GRP_DECRYPT = BIT(4), - MAC_FILTER_IN_BEACON = BIT(6), - MAC_FILTER_OUT_BCAST = BIT(8), - MAC_FILTER_IN_CRC32 = BIT(11), - MAC_FILTER_IN_PROBE_REQUEST = BIT(12), -}; - -/** - * enum iwl_mac_qos_flags - QoS flags - * @MAC_QOS_FLG_UPDATE_EDCA: ? - * @MAC_QOS_FLG_TGN: HT is enabled - * @MAC_QOS_FLG_TXOP_TYPE: ? - * - */ -enum iwl_mac_qos_flags { - MAC_QOS_FLG_UPDATE_EDCA = BIT(0), - MAC_QOS_FLG_TGN = BIT(1), - MAC_QOS_FLG_TXOP_TYPE = BIT(4), -}; - -/** - * struct iwl_ac_qos - QOS timing params for MAC_CONTEXT_CMD - * @cw_min: Contention window, start value in numbers of slots. - * Should be a power-of-2, minus 1. Device's default is 0x0f. - * @cw_max: Contention window, max value in numbers of slots. - * Should be a power-of-2, minus 1. Device's default is 0x3f. - * @aifsn: Number of slots in Arbitration Interframe Space (before - * performing random backoff timing prior to Tx). Device default 1. - * @fifos_mask: FIFOs used by this MAC for this AC - * @edca_txop: Length of Tx opportunity, in uSecs. Device default is 0. - * - * One instance of this config struct for each of 4 EDCA access categories - * in struct iwl_qosparam_cmd. - * - * Device will automatically increase contention window by (2*CW) + 1 for each - * transmission retry. Device uses cw_max as a bit mask, ANDed with new CW - * value, to cap the CW value. - */ -struct iwl_ac_qos { - __le16 cw_min; - __le16 cw_max; - u8 aifsn; - u8 fifos_mask; - __le16 edca_txop; -} __packed; /* AC_QOS_API_S_VER_2 */ - -/** - * struct iwl_mac_ctx_cmd - command structure to configure MAC contexts - * ( MAC_CONTEXT_CMD = 0x28 ) - * @id_and_color: ID and color of the MAC - * @action: action to perform, one of FW_CTXT_ACTION_* - * @mac_type: one of FW_MAC_TYPE_* - * @tsd_id: TSF HW timer, one of TSF_ID_* - * @node_addr: MAC address - * @bssid_addr: BSSID - * @cck_rates: basic rates available for CCK - * @ofdm_rates: basic rates available for OFDM - * @protection_flags: combination of MAC_PROT_FLG_FLAG_* - * @cck_short_preamble: 0x20 for enabling short preamble, 0 otherwise - * @short_slot: 0x10 for enabling short slots, 0 otherwise - * @filter_flags: combination of MAC_FILTER_* - * @qos_flags: from MAC_QOS_FLG_* - * @ac: one iwl_mac_qos configuration for each AC - * @mac_specific: one of struct iwl_mac_data_*, according to mac_type - */ -struct iwl_mac_ctx_cmd { - /* COMMON_INDEX_HDR_API_S_VER_1 */ - __le32 id_and_color; - __le32 action; - /* MAC_CONTEXT_COMMON_DATA_API_S_VER_1 */ - __le32 mac_type; - __le32 tsf_id; - u8 node_addr[6]; - __le16 reserved_for_node_addr; - u8 bssid_addr[6]; - __le16 reserved_for_bssid_addr; - __le32 cck_rates; - __le32 ofdm_rates; - __le32 protection_flags; - __le32 cck_short_preamble; - __le32 short_slot; - __le32 filter_flags; - /* MAC_QOS_PARAM_API_S_VER_1 */ - __le32 qos_flags; - struct iwl_ac_qos ac[AC_NUM+1]; - /* MAC_CONTEXT_COMMON_DATA_API_S */ - union { - struct iwl_mac_data_ap ap; - struct iwl_mac_data_go go; - struct iwl_mac_data_sta sta; - struct iwl_mac_data_p2p_sta p2p_sta; - struct iwl_mac_data_p2p_dev p2p_dev; - struct iwl_mac_data_pibss pibss; - struct iwl_mac_data_ibss ibss; - }; -} __packed; /* MAC_CONTEXT_CMD_API_S_VER_1 */ - -static inline u32 iwl_mvm_reciprocal(u32 v) -{ - if (!v) - return 0; - return 0xFFFFFFFF / v; -} - -#define IWL_NONQOS_SEQ_GET 0x1 -#define IWL_NONQOS_SEQ_SET 0x2 -struct iwl_nonqos_seq_query_cmd { - __le32 get_set_flag; - __le32 mac_id_n_color; - __le16 value; - __le16 reserved; -} __packed; /* NON_QOS_TX_COUNTER_GET_SET_API_S_VER_1 */ - -#endif /* __fw_api_mac_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h deleted file mode 100644 index c8f3e2536..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h +++ /dev/null @@ -1,467 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH - * 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_power_h__ -#define __fw_api_power_h__ - -/* Power Management Commands, Responses, Notifications */ - -/** - * enum iwl_ltr_config_flags - masks for LTR config command flags - * @LTR_CFG_FLAG_FEATURE_ENABLE: Feature operational status - * @LTR_CFG_FLAG_HW_DIS_ON_SHADOW_REG_ACCESS: allow LTR change on shadow - * memory access - * @LTR_CFG_FLAG_HW_EN_SHRT_WR_THROUGH: allow LTR msg send on ANY LTR - * reg change - * @LTR_CFG_FLAG_HW_DIS_ON_D0_2_D3: allow LTR msg send on transition from - * D0 to D3 - * @LTR_CFG_FLAG_SW_SET_SHORT: fixed static short LTR register - * @LTR_CFG_FLAG_SW_SET_LONG: fixed static short LONG register - * @LTR_CFG_FLAG_DENIE_C10_ON_PD: allow going into C10 on PD - */ -enum iwl_ltr_config_flags { - LTR_CFG_FLAG_FEATURE_ENABLE = BIT(0), - LTR_CFG_FLAG_HW_DIS_ON_SHADOW_REG_ACCESS = BIT(1), - LTR_CFG_FLAG_HW_EN_SHRT_WR_THROUGH = BIT(2), - LTR_CFG_FLAG_HW_DIS_ON_D0_2_D3 = BIT(3), - LTR_CFG_FLAG_SW_SET_SHORT = BIT(4), - LTR_CFG_FLAG_SW_SET_LONG = BIT(5), - LTR_CFG_FLAG_DENIE_C10_ON_PD = BIT(6), -}; - -/** - * struct iwl_ltr_config_cmd_v1 - configures the LTR - * @flags: See %enum iwl_ltr_config_flags - */ -struct iwl_ltr_config_cmd_v1 { - __le32 flags; - __le32 static_long; - __le32 static_short; -} __packed; /* LTR_CAPABLE_API_S_VER_1 */ - -#define LTR_VALID_STATES_NUM 4 - -/** - * struct iwl_ltr_config_cmd - configures the LTR - * @flags: See %enum iwl_ltr_config_flags - * @static_long: - * @static_short: - * @ltr_cfg_values: - * @ltr_short_idle_timeout: - */ -struct iwl_ltr_config_cmd { - __le32 flags; - __le32 static_long; - __le32 static_short; - __le32 ltr_cfg_values[LTR_VALID_STATES_NUM]; - __le32 ltr_short_idle_timeout; -} __packed; /* LTR_CAPABLE_API_S_VER_2 */ - -/* Radio LP RX Energy Threshold measured in dBm */ -#define POWER_LPRX_RSSI_THRESHOLD 75 -#define POWER_LPRX_RSSI_THRESHOLD_MAX 94 -#define POWER_LPRX_RSSI_THRESHOLD_MIN 30 - -/** - * enum iwl_power_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_SKIP_OVER_DTIM_MSK: '0' PM have to walk up every DTIM, - * '1' PM could sleep over DTIM till listen Interval. - * @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. - * @POWER_FLAGS_AP_UAPSD_MISBEHAVING_ENA_MSK: AP/GO's uAPSD misbehaving - * detection enablement -*/ -enum iwl_power_flags { - 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_SNOOZE_ENA_MSK = BIT(5), - POWER_FLAGS_BT_SCO_ENA = BIT(8), - POWER_FLAGS_ADVANCE_PM_ENA_MSK = BIT(9), - POWER_FLAGS_LPRX_ENA_MSK = BIT(11), - POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK = BIT(12), -}; - -#define IWL_POWER_VEC_SIZE 5 - -/** - * struct iwl_powertable_cmd - legacy power command. Beside old API support this - * is used also with a new power API for device wide power settings. - * POWER_TABLE_CMD = 0x77 (command, has simple generic response) - * - * @flags: Power table command flags from POWER_FLAGS_* - * @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec. - * 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 - * @sleep_interval: not in use - * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag - * is set. For example, if it is required to skip over - * one DTIM, this value need to be set to 2 (DTIM periods). - * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled. - * Default: 80dbm - */ -struct iwl_powertable_cmd { - /* PM_POWER_TABLE_CMD_API_S_VER_6 */ - __le16 flags; - u8 keep_alive_seconds; - u8 debug_flags; - __le32 rx_data_timeout; - __le32 tx_data_timeout; - __le32 sleep_interval[IWL_POWER_VEC_SIZE]; - __le32 skip_dtim_periods; - __le32 lprx_rssi_threshold; -} __packed; - -/** - * enum iwl_device_power_flags - masks for device power command flags - * @DEVIC_POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off - * receiver and transmitter. '0' - does not allow. -*/ -enum iwl_device_power_flags { - DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0), -}; - -/** - * struct iwl_device_power_cmd - device wide power command. - * DEVICE_POWER_CMD = 0x77 (command, has simple generic response) - * - * @flags: Power table command flags from DEVICE_POWER_FLAGS_* - */ -struct iwl_device_power_cmd { - /* PM_POWER_TABLE_CMD_API_S_VER_6 */ - __le16 flags; - __le16 reserved; -} __packed; - -/** - * struct iwl_mac_power_cmd - New power command containing uAPSD support - * MAC_PM_POWER_TABLE = 0xA9 (command, has simple generic response) - * @id_and_color: MAC contex identifier - * @flags: Power table command flags from POWER_FLAGS_* - * @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec. - * 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 - * @sleep_interval: not in use - * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag - * is set. For example, if it is required to skip over - * one DTIM, this value need to be set to 2 (DTIM periods). - * @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 - * @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: Maximum time between attempts to retrieve buffered data - * from the AP [msec] - * @snooze_window: A window of time in which PBW snoozing insures that all - * packets received. It is also the minimum time from last - * received unicast RX packet, before client stops snoozing - * for data. [msec] - * @snooze_step: TBD - * @qndp_tid: TID client shall use for uAPSD QNDP triggers - * @uapsd_ac_flags: Set trigger-enabled and delivery-enabled indication for - * each corresponding AC. - * Use IEEE80211_WMM_IE_STA_QOSINFO_AC* for correct values. - * @uapsd_max_sp: Use IEEE80211_WMM_IE_STA_QOSINFO_SP_* for correct - * values. - * @heavy_tx_thld_packets: TX threshold measured in number of packets - * @heavy_rx_thld_packets: RX threshold measured in number of packets - * @heavy_tx_thld_percentage: TX threshold measured in load's percentage - * @heavy_rx_thld_percentage: RX threshold measured in load's percentage - * @limited_ps_threshold: -*/ -struct iwl_mac_power_cmd { - /* CONTEXT_DESC_API_T_VER_1 */ - __le32 id_and_color; - - /* CLIENT_PM_POWER_TABLE_S_VER_1 */ - __le16 flags; - __le16 keep_alive_seconds; - __le32 rx_data_timeout; - __le32 tx_data_timeout; - __le32 rx_data_timeout_uapsd; - __le32 tx_data_timeout_uapsd; - u8 lprx_rssi_threshold; - u8 skip_dtim_periods; - __le16 snooze_interval; - __le16 snooze_window; - u8 snooze_step; - u8 qndp_tid; - u8 uapsd_ac_flags; - u8 uapsd_max_sp; - u8 heavy_tx_thld_packets; - u8 heavy_rx_thld_packets; - u8 heavy_tx_thld_percentage; - u8 heavy_rx_thld_percentage; - u8 limited_ps_threshold; - u8 reserved; -} __packed; - -/* - * struct iwl_uapsd_misbehaving_ap_notif - FW sends this notification when - * associated AP is identified as improperly implementing uAPSD protocol. - * PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78 - * @sta_id: index of station in uCode's station table - associated AP ID in - * this context. - */ -struct iwl_uapsd_misbehaving_ap_notif { - __le32 sta_id; - u8 mac_id; - u8 reserved[3]; -} __packed; - -/** - * struct iwl_reduce_tx_power_cmd - TX power reduction command - * REDUCE_TX_POWER_CMD = 0x9f - * @flags: (reserved for future implementation) - * @mac_context_id: id of the mac ctx for which we are reducing TX power. - * @pwr_restriction: TX power restriction in dBms. - */ -struct iwl_reduce_tx_power_cmd { - u8 flags; - u8 mac_context_id; - __le16 pwr_restriction; -} __packed; /* TX_REDUCED_POWER_API_S_VER_1 */ - -enum iwl_dev_tx_power_cmd_mode { - IWL_TX_POWER_MODE_SET_MAC = 0, - IWL_TX_POWER_MODE_SET_DEVICE = 1, - IWL_TX_POWER_MODE_SET_CHAINS = 2, -}; /* TX_POWER_REDUCED_FLAGS_TYPE_API_E_VER_2 */; - -/** - * struct iwl_dev_tx_power_cmd_v2 - TX power reduction command - * @set_mode: see &enum iwl_dev_tx_power_cmd_mode - * @mac_context_id: id of the mac ctx for which we are reducing TX power. - * @pwr_restriction: TX power restriction in 1/8 dBms. - * @dev_24: device TX power restriction in 1/8 dBms - * @dev_52_low: device TX power restriction upper band - low - * @dev_52_high: device TX power restriction upper band - high - */ -struct iwl_dev_tx_power_cmd_v2 { - __le32 set_mode; - __le32 mac_context_id; - __le16 pwr_restriction; - __le16 dev_24; - __le16 dev_52_low; - __le16 dev_52_high; -} __packed; /* TX_REDUCED_POWER_API_S_VER_2 */ - -#define IWL_NUM_CHAIN_LIMITS 2 -#define IWL_NUM_SUB_BANDS 5 - -/** - * struct iwl_dev_tx_power_cmd - TX power reduction command - * @v2: version 2 of the command, embedded here for easier software handling - * @per_chain_restriction: per chain restrictions - */ -struct iwl_dev_tx_power_cmd { - /* v3 is just an extension of v2 - keep this here */ - struct iwl_dev_tx_power_cmd_v2 v2; - __le16 per_chain_restriction[IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS]; -} __packed; /* TX_REDUCED_POWER_API_S_VER_3 */ - -#define IWL_DEV_MAX_TX_POWER 0x7FFF - -/** - * struct iwl_beacon_filter_cmd - * REPLY_BEACON_FILTERING_CMD = 0xd2 (command) - * @id_and_color: MAC contex identifier - * @bf_energy_delta: Used for RSSI filtering, if in 'normal' state. Send beacon - * to driver if delta in Energy values calculated for this and last - * passed beacon is greater than this threshold. Zero value means that - * the Energy change is ignored for beacon filtering, and beacon will - * not be forced to be sent to driver regardless of this delta. Typical - * energy delta 5dB. - * @bf_roaming_energy_delta: Used for RSSI filtering, if in 'roaming' state. - * Send beacon to driver if delta in Energy values calculated for this - * and last passed beacon is greater than this threshold. Zero value - * means that the Energy change is ignored for beacon filtering while in - * Roaming state, typical energy delta 1dB. - * @bf_roaming_state: Used for RSSI filtering. If absolute Energy values - * calculated for current beacon is less than the threshold, use - * Roaming Energy Delta Threshold, otherwise use normal Energy Delta - * Threshold. Typical energy threshold is -72dBm. - * @bf_temp_threshold: This threshold determines the type of temperature - * filtering (Slow or Fast) that is selected (Units are in Celsuis): - * If the current temperature is above this threshold - Fast filter - * will be used, If the current temperature is below this threshold - - * Slow filter will be used. - * @bf_temp_fast_filter: Send Beacon to driver if delta in temperature values - * calculated for this and the last passed beacon is greater than this - * threshold. Zero value means that the temperature change is ignored for - * beacon filtering; beacons will not be forced to be sent to driver - * regardless of whether its temerature has been changed. - * @bf_temp_slow_filter: Send Beacon to driver if delta in temperature values - * calculated for this and the last passed beacon is greater than this - * threshold. Zero value means that the temperature change is ignored for - * beacon filtering; beacons will not be forced to be sent to driver - * regardless of whether its temerature has been changed. - * @bf_enable_beacon_filter: 1, beacon filtering is enabled; 0, disabled. - * @bf_filter_escape_timer: Send beacons to to driver if no beacons were passed - * for a specific period of time. Units: Beacons. - * @ba_escape_timer: Fully receive and parse beacon if no beacons were passed - * for a longer period of time then this escape-timeout. Units: Beacons. - * @ba_enable_beacon_abort: 1, beacon abort is enabled; 0, disabled. - */ -struct iwl_beacon_filter_cmd { - __le32 bf_energy_delta; - __le32 bf_roaming_energy_delta; - __le32 bf_roaming_state; - __le32 bf_temp_threshold; - __le32 bf_temp_fast_filter; - __le32 bf_temp_slow_filter; - __le32 bf_enable_beacon_filter; - __le32 bf_debug_flag; - __le32 bf_escape_timer; - __le32 ba_escape_timer; - __le32 ba_enable_beacon_abort; -} __packed; - -/* Beacon filtering and beacon abort */ -#define IWL_BF_ENERGY_DELTA_DEFAULT 5 -#define IWL_BF_ENERGY_DELTA_D0I3 20 -#define IWL_BF_ENERGY_DELTA_MAX 255 -#define IWL_BF_ENERGY_DELTA_MIN 0 - -#define IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT 1 -#define IWL_BF_ROAMING_ENERGY_DELTA_D0I3 20 -#define IWL_BF_ROAMING_ENERGY_DELTA_MAX 255 -#define IWL_BF_ROAMING_ENERGY_DELTA_MIN 0 - -#define IWL_BF_ROAMING_STATE_DEFAULT 72 -#define IWL_BF_ROAMING_STATE_D0I3 72 -#define IWL_BF_ROAMING_STATE_MAX 255 -#define IWL_BF_ROAMING_STATE_MIN 0 - -#define IWL_BF_TEMP_THRESHOLD_DEFAULT 112 -#define IWL_BF_TEMP_THRESHOLD_D0I3 112 -#define IWL_BF_TEMP_THRESHOLD_MAX 255 -#define IWL_BF_TEMP_THRESHOLD_MIN 0 - -#define IWL_BF_TEMP_FAST_FILTER_DEFAULT 1 -#define IWL_BF_TEMP_FAST_FILTER_D0I3 1 -#define IWL_BF_TEMP_FAST_FILTER_MAX 255 -#define IWL_BF_TEMP_FAST_FILTER_MIN 0 - -#define IWL_BF_TEMP_SLOW_FILTER_DEFAULT 5 -#define IWL_BF_TEMP_SLOW_FILTER_D0I3 20 -#define IWL_BF_TEMP_SLOW_FILTER_MAX 255 -#define IWL_BF_TEMP_SLOW_FILTER_MIN 0 - -#define IWL_BF_ENABLE_BEACON_FILTER_DEFAULT 1 - -#define IWL_BF_DEBUG_FLAG_DEFAULT 0 -#define IWL_BF_DEBUG_FLAG_D0I3 0 - -#define IWL_BF_ESCAPE_TIMER_DEFAULT 0 -#define IWL_BF_ESCAPE_TIMER_D0I3 0 -#define IWL_BF_ESCAPE_TIMER_MAX 1024 -#define IWL_BF_ESCAPE_TIMER_MIN 0 - -#define IWL_BA_ESCAPE_TIMER_DEFAULT 6 -#define IWL_BA_ESCAPE_TIMER_D0I3 6 -#define IWL_BA_ESCAPE_TIMER_D3 9 -#define IWL_BA_ESCAPE_TIMER_MAX 1024 -#define IWL_BA_ESCAPE_TIMER_MIN 0 - -#define IWL_BA_ENABLE_BEACON_ABORT_DEFAULT 1 - -#define IWL_BF_CMD_CONFIG(mode) \ - .bf_energy_delta = cpu_to_le32(IWL_BF_ENERGY_DELTA ## mode), \ - .bf_roaming_energy_delta = \ - cpu_to_le32(IWL_BF_ROAMING_ENERGY_DELTA ## mode), \ - .bf_roaming_state = cpu_to_le32(IWL_BF_ROAMING_STATE ## mode), \ - .bf_temp_threshold = cpu_to_le32(IWL_BF_TEMP_THRESHOLD ## mode), \ - .bf_temp_fast_filter = cpu_to_le32(IWL_BF_TEMP_FAST_FILTER ## mode), \ - .bf_temp_slow_filter = cpu_to_le32(IWL_BF_TEMP_SLOW_FILTER ## mode), \ - .bf_debug_flag = cpu_to_le32(IWL_BF_DEBUG_FLAG ## mode), \ - .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER ## mode), \ - .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER ## mode) - -#define IWL_BF_CMD_CONFIG_DEFAULTS IWL_BF_CMD_CONFIG(_DEFAULT) -#define IWL_BF_CMD_CONFIG_D0I3 IWL_BF_CMD_CONFIG(_D0I3) -#endif diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h deleted file mode 100644 index 0f1ea80a5..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h +++ /dev/null @@ -1,389 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 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) 2012 - 2014 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_rs_h__ -#define __fw_api_rs_h__ - -#include "fw-api-mac.h" - -/* - * These serve as indexes into - * struct iwl_rate_info fw_rate_idx_to_plcp[IWL_RATE_COUNT]; - * TODO: avoid overlap between legacy and HT rates - */ -enum { - IWL_RATE_1M_INDEX = 0, - IWL_FIRST_CCK_RATE = IWL_RATE_1M_INDEX, - IWL_RATE_2M_INDEX, - IWL_RATE_5M_INDEX, - IWL_RATE_11M_INDEX, - IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX, - IWL_RATE_6M_INDEX, - IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX, - IWL_RATE_MCS_0_INDEX = IWL_RATE_6M_INDEX, - IWL_FIRST_HT_RATE = IWL_RATE_MCS_0_INDEX, - IWL_FIRST_VHT_RATE = IWL_RATE_MCS_0_INDEX, - IWL_RATE_9M_INDEX, - IWL_RATE_12M_INDEX, - IWL_RATE_MCS_1_INDEX = IWL_RATE_12M_INDEX, - IWL_RATE_18M_INDEX, - IWL_RATE_MCS_2_INDEX = IWL_RATE_18M_INDEX, - IWL_RATE_24M_INDEX, - IWL_RATE_MCS_3_INDEX = IWL_RATE_24M_INDEX, - IWL_RATE_36M_INDEX, - IWL_RATE_MCS_4_INDEX = IWL_RATE_36M_INDEX, - IWL_RATE_48M_INDEX, - IWL_RATE_MCS_5_INDEX = IWL_RATE_48M_INDEX, - IWL_RATE_54M_INDEX, - IWL_RATE_MCS_6_INDEX = IWL_RATE_54M_INDEX, - IWL_LAST_NON_HT_RATE = IWL_RATE_54M_INDEX, - IWL_RATE_60M_INDEX, - IWL_RATE_MCS_7_INDEX = IWL_RATE_60M_INDEX, - IWL_LAST_HT_RATE = IWL_RATE_MCS_7_INDEX, - IWL_RATE_MCS_8_INDEX, - IWL_RATE_MCS_9_INDEX, - IWL_LAST_VHT_RATE = IWL_RATE_MCS_9_INDEX, - IWL_RATE_COUNT_LEGACY = IWL_LAST_NON_HT_RATE + 1, - IWL_RATE_COUNT = IWL_LAST_VHT_RATE + 1, -}; - -#define IWL_RATE_BIT_MSK(r) BIT(IWL_RATE_##r##M_INDEX) - -/* fw API values for legacy bit rates, both OFDM and CCK */ -enum { - IWL_RATE_6M_PLCP = 13, - IWL_RATE_9M_PLCP = 15, - IWL_RATE_12M_PLCP = 5, - IWL_RATE_18M_PLCP = 7, - IWL_RATE_24M_PLCP = 9, - IWL_RATE_36M_PLCP = 11, - IWL_RATE_48M_PLCP = 1, - IWL_RATE_54M_PLCP = 3, - IWL_RATE_1M_PLCP = 10, - IWL_RATE_2M_PLCP = 20, - IWL_RATE_5M_PLCP = 55, - IWL_RATE_11M_PLCP = 110, - IWL_RATE_INVM_PLCP = -1, -}; - -/* - * rate_n_flags bit fields - * - * The 32-bit value has different layouts in the low 8 bites depending on the - * format. There are three formats, HT, VHT and legacy (11abg, with subformats - * for CCK and OFDM). - * - * High-throughput (HT) rate format - * bit 8 is 1, bit 26 is 0, bit 9 is 0 (OFDM) - * Very High-throughput (VHT) rate format - * bit 8 is 0, bit 26 is 1, bit 9 is 0 (OFDM) - * Legacy OFDM rate format for bits 7:0 - * bit 8 is 0, bit 26 is 0, bit 9 is 0 (OFDM) - * Legacy CCK rate format for bits 7:0: - * bit 8 is 0, bit 26 is 0, bit 9 is 1 (CCK) - */ - -/* Bit 8: (1) HT format, (0) legacy or VHT format */ -#define RATE_MCS_HT_POS 8 -#define RATE_MCS_HT_MSK (1 << RATE_MCS_HT_POS) - -/* Bit 9: (1) CCK, (0) OFDM. HT (bit 8) must be "0" for this bit to be valid */ -#define RATE_MCS_CCK_POS 9 -#define RATE_MCS_CCK_MSK (1 << RATE_MCS_CCK_POS) - -/* Bit 26: (1) VHT format, (0) legacy format in bits 8:0 */ -#define RATE_MCS_VHT_POS 26 -#define RATE_MCS_VHT_MSK (1 << RATE_MCS_VHT_POS) - - -/* - * High-throughput (HT) rate format for bits 7:0 - * - * 2-0: MCS rate base - * 0) 6 Mbps - * 1) 12 Mbps - * 2) 18 Mbps - * 3) 24 Mbps - * 4) 36 Mbps - * 5) 48 Mbps - * 6) 54 Mbps - * 7) 60 Mbps - * 4-3: 0) Single stream (SISO) - * 1) Dual stream (MIMO) - * 2) Triple stream (MIMO) - * 5: Value of 0x20 in bits 7:0 indicates 6 Mbps HT40 duplicate data - * (bits 7-6 are zero) - * - * Together the low 5 bits work out to the MCS index because we don't - * support MCSes above 15/23, and 0-7 have one stream, 8-15 have two - * streams and 16-23 have three streams. We could also support MCS 32 - * which is the duplicate 20 MHz MCS (bit 5 set, all others zero.) - */ -#define RATE_HT_MCS_RATE_CODE_MSK 0x7 -#define RATE_HT_MCS_NSS_POS 3 -#define RATE_HT_MCS_NSS_MSK (3 << RATE_HT_MCS_NSS_POS) - -/* Bit 10: (1) Use Green Field preamble */ -#define RATE_HT_MCS_GF_POS 10 -#define RATE_HT_MCS_GF_MSK (1 << RATE_HT_MCS_GF_POS) - -#define RATE_HT_MCS_INDEX_MSK 0x3f - -/* - * Very High-throughput (VHT) rate format for bits 7:0 - * - * 3-0: VHT MCS (0-9) - * 5-4: number of streams - 1: - * 0) Single stream (SISO) - * 1) Dual stream (MIMO) - * 2) Triple stream (MIMO) - */ - -/* Bit 4-5: (0) SISO, (1) MIMO2 (2) MIMO3 */ -#define RATE_VHT_MCS_RATE_CODE_MSK 0xf -#define RATE_VHT_MCS_NSS_POS 4 -#define RATE_VHT_MCS_NSS_MSK (3 << RATE_VHT_MCS_NSS_POS) - -/* - * Legacy OFDM rate format for bits 7:0 - * - * 3-0: 0xD) 6 Mbps - * 0xF) 9 Mbps - * 0x5) 12 Mbps - * 0x7) 18 Mbps - * 0x9) 24 Mbps - * 0xB) 36 Mbps - * 0x1) 48 Mbps - * 0x3) 54 Mbps - * (bits 7-4 are 0) - * - * Legacy CCK rate format for bits 7:0: - * bit 8 is 0, bit 26 is 0, bit 9 is 1 (CCK): - * - * 6-0: 10) 1 Mbps - * 20) 2 Mbps - * 55) 5.5 Mbps - * 110) 11 Mbps - * (bit 7 is 0) - */ -#define RATE_LEGACY_RATE_MSK 0xff - - -/* - * Bit 11-12: (0) 20MHz, (1) 40MHz, (2) 80MHz, (3) 160MHz - * 0 and 1 are valid for HT and VHT, 2 and 3 only for VHT - */ -#define RATE_MCS_CHAN_WIDTH_POS 11 -#define RATE_MCS_CHAN_WIDTH_MSK (3 << RATE_MCS_CHAN_WIDTH_POS) -#define RATE_MCS_CHAN_WIDTH_20 (0 << RATE_MCS_CHAN_WIDTH_POS) -#define RATE_MCS_CHAN_WIDTH_40 (1 << RATE_MCS_CHAN_WIDTH_POS) -#define RATE_MCS_CHAN_WIDTH_80 (2 << RATE_MCS_CHAN_WIDTH_POS) -#define RATE_MCS_CHAN_WIDTH_160 (3 << RATE_MCS_CHAN_WIDTH_POS) - -/* Bit 13: (1) Short guard interval (0.4 usec), (0) normal GI (0.8 usec) */ -#define RATE_MCS_SGI_POS 13 -#define RATE_MCS_SGI_MSK (1 << RATE_MCS_SGI_POS) - -/* Bit 14-16: Antenna selection (1) Ant A, (2) Ant B, (4) Ant C */ -#define RATE_MCS_ANT_POS 14 -#define RATE_MCS_ANT_A_MSK (1 << RATE_MCS_ANT_POS) -#define RATE_MCS_ANT_B_MSK (2 << RATE_MCS_ANT_POS) -#define RATE_MCS_ANT_C_MSK (4 << RATE_MCS_ANT_POS) -#define RATE_MCS_ANT_AB_MSK (RATE_MCS_ANT_A_MSK | \ - RATE_MCS_ANT_B_MSK) -#define RATE_MCS_ANT_ABC_MSK (RATE_MCS_ANT_AB_MSK | \ - RATE_MCS_ANT_C_MSK) -#define RATE_MCS_ANT_MSK RATE_MCS_ANT_ABC_MSK -#define RATE_MCS_ANT_NUM 3 - -/* Bit 17-18: (0) SS, (1) SS*2 */ -#define RATE_MCS_STBC_POS 17 -#define RATE_MCS_HT_STBC_MSK (3 << RATE_MCS_STBC_POS) -#define RATE_MCS_VHT_STBC_MSK (1 << RATE_MCS_STBC_POS) - -/* Bit 19: (0) Beamforming is off, (1) Beamforming is on */ -#define RATE_MCS_BF_POS 19 -#define RATE_MCS_BF_MSK (1 << RATE_MCS_BF_POS) - -/* Bit 20: (0) ZLF is off, (1) ZLF is on */ -#define RATE_MCS_ZLF_POS 20 -#define RATE_MCS_ZLF_MSK (1 << RATE_MCS_ZLF_POS) - -/* Bit 24-25: (0) 20MHz (no dup), (1) 2x20MHz, (2) 4x20MHz, 3 8x20MHz */ -#define RATE_MCS_DUP_POS 24 -#define RATE_MCS_DUP_MSK (3 << RATE_MCS_DUP_POS) - -/* Bit 27: (1) LDPC enabled, (0) LDPC disabled */ -#define RATE_MCS_LDPC_POS 27 -#define RATE_MCS_LDPC_MSK (1 << RATE_MCS_LDPC_POS) - - -/* Link Quality definitions */ - -/* # entries in rate scale table to support Tx retries */ -#define LQ_MAX_RETRY_NUM 16 - -/* Link quality command flags bit fields */ - -/* Bit 0: (0) Don't use RTS (1) Use RTS */ -#define LQ_FLAG_USE_RTS_POS 0 -#define LQ_FLAG_USE_RTS_MSK (1 << LQ_FLAG_USE_RTS_POS) - -/* Bit 1-3: LQ command color. Used to match responses to LQ commands */ -#define LQ_FLAG_COLOR_POS 1 -#define LQ_FLAG_COLOR_MSK (7 << LQ_FLAG_COLOR_POS) - -/* Bit 4-5: Tx RTS BW Signalling - * (0) No RTS BW signalling - * (1) Static BW signalling - * (2) Dynamic BW signalling - */ -#define LQ_FLAG_RTS_BW_SIG_POS 4 -#define LQ_FLAG_RTS_BW_SIG_NONE (0 << LQ_FLAG_RTS_BW_SIG_POS) -#define LQ_FLAG_RTS_BW_SIG_STATIC (1 << LQ_FLAG_RTS_BW_SIG_POS) -#define LQ_FLAG_RTS_BW_SIG_DYNAMIC (2 << LQ_FLAG_RTS_BW_SIG_POS) - -/* Bit 6: (0) No dynamic BW selection (1) Allow dynamic BW selection - * Dyanmic BW selection allows Tx with narrower BW then requested in rates - */ -#define LQ_FLAG_DYNAMIC_BW_POS 6 -#define LQ_FLAG_DYNAMIC_BW_MSK (1 << LQ_FLAG_DYNAMIC_BW_POS) - -/* Single Stream Tx Parameters (lq_cmd->ss_params) - * Flags to control a smart FW decision about whether BFER/STBC/SISO will be - * used for single stream Tx. - */ - -/* Bit 0-1: Max STBC streams allowed. Can be 0-3. - * (0) - No STBC allowed - * (1) - 2x1 STBC allowed (HT/VHT) - * (2) - 4x2 STBC allowed (HT/VHT) - * (3) - 3x2 STBC allowed (HT only) - * All our chips are at most 2 antennas so only (1) is valid for now. - */ -#define LQ_SS_STBC_ALLOWED_POS 0 -#define LQ_SS_STBC_ALLOWED_MSK (3 << LQ_SS_STBC_ALLOWED_MSK) - -/* 2x1 STBC is allowed */ -#define LQ_SS_STBC_1SS_ALLOWED (1 << LQ_SS_STBC_ALLOWED_POS) - -/* Bit 2: Beamformer (VHT only) is allowed */ -#define LQ_SS_BFER_ALLOWED_POS 2 -#define LQ_SS_BFER_ALLOWED (1 << LQ_SS_BFER_ALLOWED_POS) - -/* Bit 3: Force BFER or STBC for testing - * If this is set: - * If BFER is allowed then force the ucode to choose BFER else - * If STBC is allowed then force the ucode to choose STBC over SISO - */ -#define LQ_SS_FORCE_POS 3 -#define LQ_SS_FORCE (1 << LQ_SS_FORCE_POS) - -/* Bit 31: ss_params field is valid. Used for FW backward compatibility - * with other drivers which don't support the ss_params API yet - */ -#define LQ_SS_PARAMS_VALID_POS 31 -#define LQ_SS_PARAMS_VALID (1 << LQ_SS_PARAMS_VALID_POS) - -/** - * struct iwl_lq_cmd - link quality command - * @sta_id: station to update - * @control: not used - * @flags: combination of LQ_FLAG_* - * @mimo_delim: the first SISO index in rs_table, which separates MIMO - * and SISO rates - * @single_stream_ant_msk: best antenna for SISO (can be dual in CDD). - * Should be ANT_[ABC] - * @dual_stream_ant_msk: best antennas for MIMO, combination of ANT_[ABC] - * @initial_rate_index: first index from rs_table per AC category - * @agg_time_limit: aggregation max time threshold in usec/100, meaning - * value of 100 is one usec. Range is 100 to 8000 - * @agg_disable_start_th: try-count threshold for starting aggregation. - * If a frame has higher try-count, it should not be selected for - * starting an aggregation sequence. - * @agg_frame_cnt_limit: max frame count in an aggregation. - * 0: no limit - * 1: no aggregation (one frame per aggregation) - * 2 - 0x3f: maximal number of frames (up to 3f == 63) - * @rs_table: array of rates for each TX try, each is rate_n_flags, - * meaning it is a combination of RATE_MCS_* and IWL_RATE_*_PLCP - * @ss_params: single stream features. declare whether STBC or BFER are allowed. - */ -struct iwl_lq_cmd { - u8 sta_id; - u8 reduced_tpc; - u16 control; - /* LINK_QUAL_GENERAL_PARAMS_API_S_VER_1 */ - u8 flags; - u8 mimo_delim; - u8 single_stream_ant_msk; - u8 dual_stream_ant_msk; - u8 initial_rate_index[AC_NUM]; - /* LINK_QUAL_AGG_PARAMS_API_S_VER_1 */ - __le16 agg_time_limit; - u8 agg_disable_start_th; - u8 agg_frame_cnt_limit; - __le32 reserved2; - __le32 rs_table[LQ_MAX_RETRY_NUM]; - __le32 ss_params; -}; /* LINK_QUALITY_CMD_API_S_VER_1 */ -#endif /* __fw_api_rs_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-rx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-rx.h deleted file mode 100644 index 9b7e49d46..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-rx.h +++ /dev/null @@ -1,238 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH - * 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_rx_h__ -#define __fw_api_rx_h__ - -#define IWL_RX_INFO_PHY_CNT 8 -#define IWL_RX_INFO_ENERGY_ANT_ABC_IDX 1 -#define IWL_RX_INFO_ENERGY_ANT_A_MSK 0x000000ff -#define IWL_RX_INFO_ENERGY_ANT_B_MSK 0x0000ff00 -#define IWL_RX_INFO_ENERGY_ANT_C_MSK 0x00ff0000 -#define IWL_RX_INFO_ENERGY_ANT_A_POS 0 -#define IWL_RX_INFO_ENERGY_ANT_B_POS 8 -#define IWL_RX_INFO_ENERGY_ANT_C_POS 16 - -/** - * struct iwl_rx_phy_info - phy info - * (REPLY_RX_PHY_CMD = 0xc0) - * @non_cfg_phy_cnt: non configurable DSP phy data byte count - * @cfg_phy_cnt: configurable DSP phy data byte count - * @stat_id: configurable DSP phy data set ID - * @reserved1: - * @system_timestamp: GP2 at on air rise - * @timestamp: TSF at on air rise - * @beacon_time_stamp: beacon at on-air rise - * @phy_flags: general phy flags: band, modulation, ... - * @channel: channel number - * @non_cfg_phy_buf: for various implementations of non_cfg_phy - * @rate_n_flags: RATE_MCS_* - * @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. - */ -struct iwl_rx_phy_info { - u8 non_cfg_phy_cnt; - u8 cfg_phy_cnt; - u8 stat_id; - u8 reserved1; - __le32 system_timestamp; - __le64 timestamp; - __le32 beacon_time_stamp; - __le16 phy_flags; - __le16 channel; - __le32 non_cfg_phy[IWL_RX_INFO_PHY_CNT]; - __le32 rate_n_flags; - __le32 byte_count; - __le16 mac_active_msk; - __le16 frame_time; -} __packed; - -/* - * TCP offload Rx assist info - * - * bits 0:3 - reserved - * bits 4:7 - MIC CRC length - * bits 8:12 - MAC header length - * bit 13 - Padding indication - * bit 14 - A-AMSDU indication - * bit 15 - Offload enabled - */ -enum iwl_csum_rx_assist_info { - CSUM_RXA_RESERVED_MASK = 0x000f, - CSUM_RXA_MICSIZE_MASK = 0x00f0, - CSUM_RXA_HEADERLEN_MASK = 0x1f00, - CSUM_RXA_PADD = BIT(13), - CSUM_RXA_AMSDU = BIT(14), - CSUM_RXA_ENA = BIT(15) -}; - -/** - * struct iwl_rx_mpdu_res_start - phy info - * @assist: see CSUM_RX_ASSIST_ above - */ -struct iwl_rx_mpdu_res_start { - __le16 byte_count; - __le16 assist; -} __packed; /* _RX_MPDU_RES_START_API_S_VER_2 */ - -/** - * enum iwl_rx_phy_flags - to parse %iwl_rx_phy_info phy_flags - * @RX_RES_PHY_FLAGS_BAND_24: true if the packet was received on 2.4 band - * @RX_RES_PHY_FLAGS_MOD_CCK: - * @RX_RES_PHY_FLAGS_SHORT_PREAMBLE: true if packet's preamble was short - * @RX_RES_PHY_FLAGS_NARROW_BAND: - * @RX_RES_PHY_FLAGS_ANTENNA: antenna on which the packet was received - * @RX_RES_PHY_FLAGS_AGG: set if the packet was part of an A-MPDU - * @RX_RES_PHY_FLAGS_OFDM_HT: The frame was an HT frame - * @RX_RES_PHY_FLAGS_OFDM_GF: The frame used GF preamble - * @RX_RES_PHY_FLAGS_OFDM_VHT: The frame was a VHT frame - */ -enum iwl_rx_phy_flags { - RX_RES_PHY_FLAGS_BAND_24 = BIT(0), - RX_RES_PHY_FLAGS_MOD_CCK = BIT(1), - RX_RES_PHY_FLAGS_SHORT_PREAMBLE = BIT(2), - RX_RES_PHY_FLAGS_NARROW_BAND = BIT(3), - RX_RES_PHY_FLAGS_ANTENNA = (0x7 << 4), - RX_RES_PHY_FLAGS_ANTENNA_POS = 4, - RX_RES_PHY_FLAGS_AGG = BIT(7), - RX_RES_PHY_FLAGS_OFDM_HT = BIT(8), - RX_RES_PHY_FLAGS_OFDM_GF = BIT(9), - RX_RES_PHY_FLAGS_OFDM_VHT = BIT(10), -}; - -/** - * enum iwl_mvm_rx_status - written by fw for each Rx packet - * @RX_MPDU_RES_STATUS_CRC_OK: CRC is fine - * @RX_MPDU_RES_STATUS_OVERRUN_OK: there was no RXE overflow - * @RX_MPDU_RES_STATUS_SRC_STA_FOUND: - * @RX_MPDU_RES_STATUS_KEY_VALID: - * @RX_MPDU_RES_STATUS_KEY_PARAM_OK: - * @RX_MPDU_RES_STATUS_ICV_OK: ICV is fine, if not, the packet is destroyed - * @RX_MPDU_RES_STATUS_MIC_OK: used for CCM alg only. TKIP MIC is checked - * in the driver. - * @RX_MPDU_RES_STATUS_TTAK_OK: TTAK is fine - * @RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR: valid for alg = CCM_CMAC or - * alg = CCM only. Checks replay attack for 11w frames. Relevant only if - * %RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME is set. - * @RX_MPDU_RES_STATUS_SEC_NO_ENC: this frame is not encrypted - * @RX_MPDU_RES_STATUS_SEC_WEP_ENC: this frame is encrypted using WEP - * @RX_MPDU_RES_STATUS_SEC_CCM_ENC: this frame is encrypted using CCM - * @RX_MPDU_RES_STATUS_SEC_TKIP_ENC: this frame is encrypted using TKIP - * @RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC: this frame is encrypted using CCM_CMAC - * @RX_MPDU_RES_STATUS_SEC_ENC_ERR: this frame couldn't be decrypted - * @RX_MPDU_RES_STATUS_SEC_ENC_MSK: bitmask of the encryption algorithm - * @RX_MPDU_RES_STATUS_DEC_DONE: this frame has been successfully decrypted - * @RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP: - * @RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP: - * @RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT: - * @RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME: this frame is an 11w management frame - * @RX_MPDU_RES_STATUS_CSUM_DONE: checksum was done by the hw - * @RX_MPDU_RES_STATUS_CSUM_OK: checksum found no errors - * @RX_MPDU_RES_STATUS_HASH_INDEX_MSK: - * @RX_MPDU_RES_STATUS_STA_ID_MSK: - * @RX_MPDU_RES_STATUS_RRF_KILL: - * @RX_MPDU_RES_STATUS_FILTERING_MSK: - * @RX_MPDU_RES_STATUS2_FILTERING_MSK: - */ -enum iwl_mvm_rx_status { - RX_MPDU_RES_STATUS_CRC_OK = BIT(0), - RX_MPDU_RES_STATUS_OVERRUN_OK = BIT(1), - RX_MPDU_RES_STATUS_SRC_STA_FOUND = BIT(2), - RX_MPDU_RES_STATUS_KEY_VALID = BIT(3), - RX_MPDU_RES_STATUS_KEY_PARAM_OK = BIT(4), - RX_MPDU_RES_STATUS_ICV_OK = BIT(5), - RX_MPDU_RES_STATUS_MIC_OK = BIT(6), - RX_MPDU_RES_STATUS_TTAK_OK = BIT(7), - RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR = BIT(7), - RX_MPDU_RES_STATUS_SEC_NO_ENC = (0 << 8), - RX_MPDU_RES_STATUS_SEC_WEP_ENC = (1 << 8), - RX_MPDU_RES_STATUS_SEC_CCM_ENC = (2 << 8), - RX_MPDU_RES_STATUS_SEC_TKIP_ENC = (3 << 8), - RX_MPDU_RES_STATUS_SEC_EXT_ENC = (4 << 8), - RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC = (6 << 8), - RX_MPDU_RES_STATUS_SEC_ENC_ERR = (7 << 8), - RX_MPDU_RES_STATUS_SEC_ENC_MSK = (7 << 8), - RX_MPDU_RES_STATUS_DEC_DONE = BIT(11), - RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP = BIT(12), - RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP = BIT(13), - RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT = BIT(14), - RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME = BIT(15), - RX_MPDU_RES_STATUS_CSUM_DONE = BIT(16), - RX_MPDU_RES_STATUS_CSUM_OK = BIT(17), - RX_MPDU_RES_STATUS_HASH_INDEX_MSK = (0x3F0000), - RX_MPDU_RES_STATUS_STA_ID_MSK = (0x1f000000), - RX_MPDU_RES_STATUS_RRF_KILL = BIT(29), - RX_MPDU_RES_STATUS_FILTERING_MSK = (0xc00000), - RX_MPDU_RES_STATUS2_FILTERING_MSK = (0xc0000000), -}; - -#endif /* __fw_api_rx_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h deleted file mode 100644 index 3a657e4b6..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h +++ /dev/null @@ -1,730 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * 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_scan_h__ -#define __fw_api_scan_h__ - -#include "fw-api.h" - -/* Scan Commands, Responses, Notifications */ - -/* Max number of IEs for direct SSID scans in a command */ -#define PROBE_OPTION_MAX 20 - -/** - * struct iwl_ssid_ie - directed scan network information element - * - * Up to 20 of these may appear in REPLY_SCAN_CMD, - * selected by "type" bit field in struct iwl_scan_channel; - * each channel may select different ssids from among the 20 entries. - * SSID IEs get transmitted in reverse order of entry. - */ -struct iwl_ssid_ie { - u8 id; - u8 len; - u8 ssid[IEEE80211_MAX_SSID_LEN]; -} __packed; /* SCAN_DIRECT_SSID_IE_API_S_VER_1 */ - -/* scan offload */ -#define IWL_SCAN_MAX_BLACKLIST_LEN 64 -#define IWL_SCAN_SHORT_BLACKLIST_LEN 16 -#define IWL_SCAN_MAX_PROFILES 11 -#define SCAN_OFFLOAD_PROBE_REQ_SIZE 512 - -/* Default watchdog (in MS) for scheduled scan iteration */ -#define IWL_SCHED_SCAN_WATCHDOG cpu_to_le16(15000) - -#define IWL_GOOD_CRC_TH_DEFAULT cpu_to_le16(1) -#define CAN_ABORT_STATUS 1 - -#define IWL_FULL_SCAN_MULTIPLIER 5 -#define IWL_FAST_SCHED_SCAN_ITERATIONS 3 -#define IWL_MAX_SCHED_SCAN_PLANS 2 - -enum scan_framework_client { - SCAN_CLIENT_SCHED_SCAN = BIT(0), - SCAN_CLIENT_NETDETECT = BIT(1), - SCAN_CLIENT_ASSET_TRACKING = BIT(2), -}; - -/** - * iwl_scan_offload_blacklist - SCAN_OFFLOAD_BLACKLIST_S - * @ssid: MAC address to filter out - * @reported_rssi: AP rssi reported to the host - * @client_bitmap: clients ignore this entry - enum scan_framework_client - */ -struct iwl_scan_offload_blacklist { - u8 ssid[ETH_ALEN]; - u8 reported_rssi; - u8 client_bitmap; -} __packed; - -enum iwl_scan_offload_network_type { - IWL_NETWORK_TYPE_BSS = 1, - IWL_NETWORK_TYPE_IBSS = 2, - IWL_NETWORK_TYPE_ANY = 3, -}; - -enum iwl_scan_offload_band_selection { - IWL_SCAN_OFFLOAD_SELECT_2_4 = 0x4, - IWL_SCAN_OFFLOAD_SELECT_5_2 = 0x8, - IWL_SCAN_OFFLOAD_SELECT_ANY = 0xc, -}; - -/** - * iwl_scan_offload_profile - SCAN_OFFLOAD_PROFILE_S - * @ssid_index: index to ssid list in fixed part - * @unicast_cipher: encryption algorithm to match - bitmap - * @aut_alg: authentication algorithm to match - bitmap - * @network_type: enum iwl_scan_offload_network_type - * @band_selection: enum iwl_scan_offload_band_selection - * @client_bitmap: clients waiting for match - enum scan_framework_client - */ -struct iwl_scan_offload_profile { - u8 ssid_index; - u8 unicast_cipher; - u8 auth_alg; - u8 network_type; - u8 band_selection; - u8 client_bitmap; - u8 reserved[2]; -} __packed; - -/** - * iwl_scan_offload_profile_cfg - SCAN_OFFLOAD_PROFILES_CFG_API_S_VER_1 - * @blaclist: AP list to filter off from scan results - * @profiles: profiles to search for match - * @blacklist_len: length of blacklist - * @num_profiles: num of profiles in the list - * @match_notify: clients waiting for match found notification - * @pass_match: clients waiting for the results - * @active_clients: active clients bitmap - enum scan_framework_client - * @any_beacon_notify: clients waiting for match notification without match - */ -struct iwl_scan_offload_profile_cfg { - struct iwl_scan_offload_profile profiles[IWL_SCAN_MAX_PROFILES]; - u8 blacklist_len; - u8 num_profiles; - u8 match_notify; - u8 pass_match; - u8 active_clients; - u8 any_beacon_notify; - u8 reserved[2]; -} __packed; - -/** - * iwl_scan_schedule_lmac - schedule of scan offload - * @delay: delay between iterations, in seconds. - * @iterations: num of scan iterations - * @full_scan_mul: number of partial scans before each full scan - */ -struct iwl_scan_schedule_lmac { - __le16 delay; - u8 iterations; - u8 full_scan_mul; -} __packed; /* SCAN_SCHEDULE_API_S */ - -enum iwl_scan_offload_complete_status { - IWL_SCAN_OFFLOAD_COMPLETED = 1, - IWL_SCAN_OFFLOAD_ABORTED = 2, -}; - -enum iwl_scan_ebs_status { - IWL_SCAN_EBS_SUCCESS, - IWL_SCAN_EBS_FAILED, - IWL_SCAN_EBS_CHAN_NOT_FOUND, - IWL_SCAN_EBS_INACTIVE, -}; - -/** - * iwl_scan_req_tx_cmd - SCAN_REQ_TX_CMD_API_S - * @tx_flags: combination of TX_CMD_FLG_* - * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is - * cleared. Combination of RATE_MCS_* - * @sta_id: index of destination station in FW station table - * @reserved: for alignment and future use - */ -struct iwl_scan_req_tx_cmd { - __le32 tx_flags; - __le32 rate_n_flags; - u8 sta_id; - u8 reserved[3]; -} __packed; - -enum iwl_scan_channel_flags_lmac { - IWL_UNIFIED_SCAN_CHANNEL_FULL = BIT(27), - IWL_UNIFIED_SCAN_CHANNEL_PARTIAL = BIT(28), -}; - -/** - * iwl_scan_channel_cfg_lmac - SCAN_CHANNEL_CFG_S_VER2 - * @flags: bits 1-20: directed scan to i'th ssid - * other bits &enum iwl_scan_channel_flags_lmac - * @channel_number: channel number 1-13 etc - * @iter_count: scan iteration on this channel - * @iter_interval: interval in seconds between iterations on one channel - */ -struct iwl_scan_channel_cfg_lmac { - __le32 flags; - __le16 channel_num; - __le16 iter_count; - __le32 iter_interval; -} __packed; - -/* - * iwl_scan_probe_segment - PROBE_SEGMENT_API_S_VER_1 - * @offset: offset in the data block - * @len: length of the segment - */ -struct iwl_scan_probe_segment { - __le16 offset; - __le16 len; -} __packed; - -/* iwl_scan_probe_req - PROBE_REQUEST_FRAME_API_S_VER_2 - * @mac_header: first (and common) part of the probe - * @band_data: band specific data - * @common_data: last (and common) part of the probe - * @buf: raw data block - */ -struct iwl_scan_probe_req { - struct iwl_scan_probe_segment mac_header; - struct iwl_scan_probe_segment band_data[2]; - struct iwl_scan_probe_segment common_data; - u8 buf[SCAN_OFFLOAD_PROBE_REQ_SIZE]; -} __packed; - -enum iwl_scan_channel_flags { - IWL_SCAN_CHANNEL_FLAG_EBS = BIT(0), - IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE = BIT(1), - IWL_SCAN_CHANNEL_FLAG_CACHE_ADD = BIT(2), -}; - -/* iwl_scan_channel_opt - CHANNEL_OPTIMIZATION_API_S - * @flags: enum iwl_scan_channel_flags - * @non_ebs_ratio: defines the ratio of number of scan iterations where EBS is - * involved. - * 1 - EBS is disabled. - * 2 - every second scan will be full scan(and so on). - */ -struct iwl_scan_channel_opt { - __le16 flags; - __le16 non_ebs_ratio; -} __packed; - -/** - * iwl_mvm_lmac_scan_flags - * @IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL: pass all beacons and probe responses - * without filtering. - * @IWL_MVM_LMAC_SCAN_FLAG_PASSIVE: force passive scan on all channels - * @IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION: single channel scan - * @IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE: send iteration complete notification - * @IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS multiple SSID matching - * @IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED: all passive scans will be fragmented - * @IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED: insert WFA vendor-specific TPC report - * and DS parameter set IEs into probe requests. - * @IWL_MVM_LMAC_SCAN_FLAG_MATCH: Send match found notification on matches - */ -enum iwl_mvm_lmac_scan_flags { - IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL = BIT(0), - IWL_MVM_LMAC_SCAN_FLAG_PASSIVE = BIT(1), - IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION = BIT(2), - IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE = BIT(3), - IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS = BIT(4), - IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED = BIT(5), - IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED = BIT(6), - IWL_MVM_LMAC_SCAN_FLAG_MATCH = BIT(9), -}; - -enum iwl_scan_priority { - IWL_SCAN_PRIORITY_LOW, - IWL_SCAN_PRIORITY_MEDIUM, - IWL_SCAN_PRIORITY_HIGH, -}; - -enum iwl_scan_priority_ext { - IWL_SCAN_PRIORITY_EXT_0_LOWEST, - IWL_SCAN_PRIORITY_EXT_1, - IWL_SCAN_PRIORITY_EXT_2, - IWL_SCAN_PRIORITY_EXT_3, - IWL_SCAN_PRIORITY_EXT_4, - IWL_SCAN_PRIORITY_EXT_5, - IWL_SCAN_PRIORITY_EXT_6, - IWL_SCAN_PRIORITY_EXT_7_HIGHEST, -}; - -/** - * iwl_scan_req_lmac - SCAN_REQUEST_CMD_API_S_VER_1 - * @reserved1: for alignment and future use - * @channel_num: num of channels to scan - * @active-dwell: dwell time for active channels - * @passive-dwell: dwell time for passive channels - * @fragmented-dwell: dwell time for fragmented passive scan - * @reserved2: for alignment and future use - * @rx_chain_selct: PHY_RX_CHAIN_* flags - * @scan_flags: &enum iwl_mvm_lmac_scan_flags - * @max_out_time: max time (in TU) to be out of associated channel - * @suspend_time: pause scan this long (TUs) when returning to service channel - * @flags: RXON flags - * @filter_flags: RXON filter - * @tx_cmd: tx command for active scan; for 2GHz and for 5GHz - * @direct_scan: list of SSIDs for directed active scan - * @scan_prio: enum iwl_scan_priority - * @iter_num: number of scan iterations - * @delay: delay in seconds before first iteration - * @schedule: two scheduling plans. The first one is finite, the second one can - * be infinite. - * @channel_opt: channel optimization options, for full and partial scan - * @data: channel configuration and probe request packet. - */ -struct iwl_scan_req_lmac { - /* SCAN_REQUEST_FIXED_PART_API_S_VER_7 */ - __le32 reserved1; - u8 n_channels; - u8 active_dwell; - u8 passive_dwell; - u8 fragmented_dwell; - __le16 reserved2; - __le16 rx_chain_select; - __le32 scan_flags; - __le32 max_out_time; - __le32 suspend_time; - /* RX_ON_FLAGS_API_S_VER_1 */ - __le32 flags; - __le32 filter_flags; - struct iwl_scan_req_tx_cmd tx_cmd[2]; - struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX]; - __le32 scan_prio; - /* SCAN_REQ_PERIODIC_PARAMS_API_S */ - __le32 iter_num; - __le32 delay; - struct iwl_scan_schedule_lmac schedule[IWL_MAX_SCHED_SCAN_PLANS]; - struct iwl_scan_channel_opt channel_opt[2]; - u8 data[]; -} __packed; - -/** - * struct iwl_scan_results_notif - scan results for one channel - - * SCAN_RESULT_NTF_API_S_VER_3 - * @channel: which channel the results are from - * @band: 0 for 5.2 GHz, 1 for 2.4 GHz - * @probe_status: SCAN_PROBE_STATUS_*, indicates success of probe request - * @num_probe_not_sent: # of request that weren't sent due to not enough time - * @duration: duration spent in channel, in usecs - */ -struct iwl_scan_results_notif { - u8 channel; - u8 band; - u8 probe_status; - u8 num_probe_not_sent; - __le32 duration; -} __packed; - -/** - * struct iwl_lmac_scan_complete_notif - notifies end of scanning (all channels) - * SCAN_COMPLETE_NTF_API_S_VER_3 - * @scanned_channels: number of channels scanned (and number of valid results) - * @status: one of SCAN_COMP_STATUS_* - * @bt_status: BT on/off status - * @last_channel: last channel that was scanned - * @tsf_low: TSF timer (lower half) in usecs - * @tsf_high: TSF timer (higher half) in usecs - * @results: an array of scan results, only "scanned_channels" of them are valid - */ -struct iwl_lmac_scan_complete_notif { - u8 scanned_channels; - u8 status; - u8 bt_status; - u8 last_channel; - __le32 tsf_low; - __le32 tsf_high; - struct iwl_scan_results_notif results[]; -} __packed; - -/** - * iwl_scan_offload_complete - PERIODIC_SCAN_COMPLETE_NTF_API_S_VER_2 - * @last_schedule_line: last schedule line executed (fast or regular) - * @last_schedule_iteration: last scan iteration executed before scan abort - * @status: enum iwl_scan_offload_complete_status - * @ebs_status: EBS success status &enum iwl_scan_ebs_status - * @time_after_last_iter; time in seconds elapsed after last iteration - */ -struct iwl_periodic_scan_complete { - u8 last_schedule_line; - u8 last_schedule_iteration; - u8 status; - u8 ebs_status; - __le32 time_after_last_iter; - __le32 reserved; -} __packed; - -/* UMAC Scan API */ - -/* The maximum of either of these cannot exceed 8, because we use an - * 8-bit mask (see IWL_MVM_SCAN_MASK in mvm.h). - */ -#define IWL_MVM_MAX_UMAC_SCANS 8 -#define IWL_MVM_MAX_LMAC_SCANS 1 - -enum scan_config_flags { - SCAN_CONFIG_FLAG_ACTIVATE = BIT(0), - SCAN_CONFIG_FLAG_DEACTIVATE = BIT(1), - SCAN_CONFIG_FLAG_FORBID_CHUB_REQS = BIT(2), - SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS = BIT(3), - SCAN_CONFIG_FLAG_SET_TX_CHAINS = BIT(8), - SCAN_CONFIG_FLAG_SET_RX_CHAINS = BIT(9), - SCAN_CONFIG_FLAG_SET_AUX_STA_ID = BIT(10), - SCAN_CONFIG_FLAG_SET_ALL_TIMES = BIT(11), - SCAN_CONFIG_FLAG_SET_EFFECTIVE_TIMES = BIT(12), - SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS = BIT(13), - SCAN_CONFIG_FLAG_SET_LEGACY_RATES = BIT(14), - SCAN_CONFIG_FLAG_SET_MAC_ADDR = BIT(15), - SCAN_CONFIG_FLAG_SET_FRAGMENTED = BIT(16), - SCAN_CONFIG_FLAG_CLEAR_FRAGMENTED = BIT(17), - SCAN_CONFIG_FLAG_SET_CAM_MODE = BIT(18), - SCAN_CONFIG_FLAG_CLEAR_CAM_MODE = BIT(19), - SCAN_CONFIG_FLAG_SET_PROMISC_MODE = BIT(20), - SCAN_CONFIG_FLAG_CLEAR_PROMISC_MODE = BIT(21), - - /* Bits 26-31 are for num of channels in channel_array */ -#define SCAN_CONFIG_N_CHANNELS(n) ((n) << 26) -}; - -enum scan_config_rates { - /* OFDM basic rates */ - SCAN_CONFIG_RATE_6M = BIT(0), - SCAN_CONFIG_RATE_9M = BIT(1), - SCAN_CONFIG_RATE_12M = BIT(2), - SCAN_CONFIG_RATE_18M = BIT(3), - SCAN_CONFIG_RATE_24M = BIT(4), - SCAN_CONFIG_RATE_36M = BIT(5), - SCAN_CONFIG_RATE_48M = BIT(6), - SCAN_CONFIG_RATE_54M = BIT(7), - /* CCK basic rates */ - SCAN_CONFIG_RATE_1M = BIT(8), - SCAN_CONFIG_RATE_2M = BIT(9), - SCAN_CONFIG_RATE_5M = BIT(10), - SCAN_CONFIG_RATE_11M = BIT(11), - - /* Bits 16-27 are for supported rates */ -#define SCAN_CONFIG_SUPPORTED_RATE(rate) ((rate) << 16) -}; - -enum iwl_channel_flags { - IWL_CHANNEL_FLAG_EBS = BIT(0), - IWL_CHANNEL_FLAG_ACCURATE_EBS = BIT(1), - IWL_CHANNEL_FLAG_EBS_ADD = BIT(2), - IWL_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE = BIT(3), -}; - -/** - * struct iwl_scan_config - * @flags: enum scan_config_flags - * @tx_chains: valid_tx antenna - ANT_* definitions - * @rx_chains: valid_rx antenna - ANT_* definitions - * @legacy_rates: default legacy rates - enum scan_config_rates - * @out_of_channel_time: default max out of serving channel time - * @suspend_time: default max suspend time - * @dwell_active: default dwell time for active scan - * @dwell_passive: default dwell time for passive scan - * @dwell_fragmented: default dwell time for fragmented scan - * @reserved: for future use and alignment - * @mac_addr: default mac address to be used in probes - * @bcast_sta_id: the index of the station in the fw - * @channel_flags: default channel flags - enum iwl_channel_flags - * scan_config_channel_flag - * @channel_array: default supported channels - */ -struct iwl_scan_config { - __le32 flags; - __le32 tx_chains; - __le32 rx_chains; - __le32 legacy_rates; - __le32 out_of_channel_time; - __le32 suspend_time; - u8 dwell_active; - u8 dwell_passive; - u8 dwell_fragmented; - u8 reserved; - u8 mac_addr[ETH_ALEN]; - u8 bcast_sta_id; - u8 channel_flags; - u8 channel_array[]; -} __packed; /* SCAN_CONFIG_DB_CMD_API_S */ - -/** - * iwl_umac_scan_flags - *@IWL_UMAC_SCAN_FLAG_PREEMPTIVE: scan process triggered by this scan request - * can be preempted by other scan requests with higher priority. - * The low priority scan will be resumed when the higher proirity scan is - * completed. - *@IWL_UMAC_SCAN_FLAG_START_NOTIF: notification will be sent to the driver - * when scan starts. - */ -enum iwl_umac_scan_flags { - IWL_UMAC_SCAN_FLAG_PREEMPTIVE = BIT(0), - IWL_UMAC_SCAN_FLAG_START_NOTIF = BIT(1), -}; - -enum iwl_umac_scan_uid_offsets { - IWL_UMAC_SCAN_UID_TYPE_OFFSET = 0, - IWL_UMAC_SCAN_UID_SEQ_OFFSET = 8, -}; - -enum iwl_umac_scan_general_flags { - IWL_UMAC_SCAN_GEN_FLAGS_PERIODIC = BIT(0), - IWL_UMAC_SCAN_GEN_FLAGS_OVER_BT = BIT(1), - IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL = BIT(2), - IWL_UMAC_SCAN_GEN_FLAGS_PASSIVE = BIT(3), - IWL_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT = BIT(4), - IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE = BIT(5), - IWL_UMAC_SCAN_GEN_FLAGS_MULTIPLE_SSID = BIT(6), - IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED = BIT(7), - IWL_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED = BIT(8), - IWL_UMAC_SCAN_GEN_FLAGS_MATCH = BIT(9) -}; - -/** - * struct iwl_scan_channel_cfg_umac - * @flags: bitmap - 0-19: directed scan to i'th ssid. - * @channel_num: channel number 1-13 etc. - * @iter_count: repetition count for the channel. - * @iter_interval: interval between two scan iterations on one channel. - */ -struct iwl_scan_channel_cfg_umac { - __le32 flags; - u8 channel_num; - u8 iter_count; - __le16 iter_interval; -} __packed; /* SCAN_CHANNEL_CFG_S_VER2 */ - -/** - * struct iwl_scan_umac_schedule - * @interval: interval in seconds between scan iterations - * @iter_count: num of scan iterations for schedule plan, 0xff for infinite loop - * @reserved: for alignment and future use - */ -struct iwl_scan_umac_schedule { - __le16 interval; - u8 iter_count; - u8 reserved; -} __packed; /* SCAN_SCHED_PARAM_API_S_VER_1 */ - -/** - * struct iwl_scan_req_umac_tail - the rest of the UMAC scan request command - * parameters following channels configuration array. - * @schedule: two scheduling plans. - * @delay: delay in TUs before starting the first scan iteration - * @reserved: for future use and alignment - * @preq: probe request with IEs blocks - * @direct_scan: list of SSIDs for directed active scan - */ -struct iwl_scan_req_umac_tail { - /* SCAN_PERIODIC_PARAMS_API_S_VER_1 */ - struct iwl_scan_umac_schedule schedule[IWL_MAX_SCHED_SCAN_PLANS]; - __le16 delay; - __le16 reserved; - /* SCAN_PROBE_PARAMS_API_S_VER_1 */ - struct iwl_scan_probe_req preq; - struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX]; -} __packed; - -/** - * struct iwl_scan_req_umac - * @flags: &enum iwl_umac_scan_flags - * @uid: scan id, &enum iwl_umac_scan_uid_offsets - * @ooc_priority: out of channel priority - &enum iwl_scan_priority - * @general_flags: &enum iwl_umac_scan_general_flags - * @reserved1: for future use and alignment - * @active_dwell: dwell time for active scan - * @passive_dwell: dwell time for passive scan - * @fragmented_dwell: dwell time for fragmented passive scan - * @max_out_time: max out of serving channel time - * @suspend_time: max suspend time - * @scan_priority: scan internal prioritization &enum iwl_scan_priority - * @channel_flags: &enum iwl_scan_channel_flags - * @n_channels: num of channels in scan request - * @reserved2: for future use and alignment - * @data: &struct iwl_scan_channel_cfg_umac and - * &struct iwl_scan_req_umac_tail - */ -struct iwl_scan_req_umac { - __le32 flags; - __le32 uid; - __le32 ooc_priority; - /* SCAN_GENERAL_PARAMS_API_S_VER_1 */ - __le32 general_flags; - u8 reserved1; - u8 active_dwell; - u8 passive_dwell; - u8 fragmented_dwell; - __le32 max_out_time; - __le32 suspend_time; - __le32 scan_priority; - /* SCAN_CHANNEL_PARAMS_API_S_VER_1 */ - u8 channel_flags; - u8 n_channels; - __le16 reserved2; - u8 data[]; -} __packed; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_1 */ - -/** - * struct iwl_umac_scan_abort - * @uid: scan id, &enum iwl_umac_scan_uid_offsets - * @flags: reserved - */ -struct iwl_umac_scan_abort { - __le32 uid; - __le32 flags; -} __packed; /* SCAN_ABORT_CMD_UMAC_API_S_VER_1 */ - -/** - * struct iwl_umac_scan_complete - * @uid: scan id, &enum iwl_umac_scan_uid_offsets - * @last_schedule: last scheduling line - * @last_iter: last scan iteration number - * @scan status: &enum iwl_scan_offload_complete_status - * @ebs_status: &enum iwl_scan_ebs_status - * @time_from_last_iter: time elapsed from last iteration - * @reserved: for future use - */ -struct iwl_umac_scan_complete { - __le32 uid; - u8 last_schedule; - u8 last_iter; - u8 status; - u8 ebs_status; - __le32 time_from_last_iter; - __le32 reserved; -} __packed; /* SCAN_COMPLETE_NTF_UMAC_API_S_VER_1 */ - -#define SCAN_OFFLOAD_MATCHING_CHANNELS_LEN 5 -/** - * struct iwl_scan_offload_profile_match - match information - * @bssid: matched bssid - * @channel: channel where the match occurred - * @energy: - * @matching_feature: - * @matching_channels: bitmap of channels that matched, referencing - * the channels passed in tue scan offload request - */ -struct iwl_scan_offload_profile_match { - u8 bssid[ETH_ALEN]; - __le16 reserved; - u8 channel; - u8 energy; - u8 matching_feature; - u8 matching_channels[SCAN_OFFLOAD_MATCHING_CHANNELS_LEN]; -} __packed; /* SCAN_OFFLOAD_PROFILE_MATCH_RESULTS_S_VER_1 */ - -/** - * struct iwl_scan_offload_profiles_query - match results query response - * @matched_profiles: bitmap of matched profiles, referencing the - * matches passed in the scan offload request - * @last_scan_age: age of the last offloaded scan - * @n_scans_done: number of offloaded scans done - * @gp2_d0u: GP2 when D0U occurred - * @gp2_invoked: GP2 when scan offload was invoked - * @resume_while_scanning: not used - * @self_recovery: obsolete - * @reserved: reserved - * @matches: array of match information, one for each match - */ -struct iwl_scan_offload_profiles_query { - __le32 matched_profiles; - __le32 last_scan_age; - __le32 n_scans_done; - __le32 gp2_d0u; - __le32 gp2_invoked; - u8 resume_while_scanning; - u8 self_recovery; - __le16 reserved; - struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES]; -} __packed; /* SCAN_OFFLOAD_PROFILES_QUERY_RSP_S_VER_2 */ - -/** - * struct iwl_umac_scan_iter_complete_notif - notifies end of scanning iteration - * @uid: scan id, &enum iwl_umac_scan_uid_offsets - * @scanned_channels: number of channels scanned and number of valid elements in - * results array - * @status: one of SCAN_COMP_STATUS_* - * @bt_status: BT on/off status - * @last_channel: last channel that was scanned - * @tsf_low: TSF timer (lower half) in usecs - * @tsf_high: TSF timer (higher half) in usecs - * @results: array of scan results, only "scanned_channels" of them are valid - */ -struct iwl_umac_scan_iter_complete_notif { - __le32 uid; - u8 scanned_channels; - u8 status; - u8 bt_status; - u8 last_channel; - __le32 tsf_low; - __le32 tsf_high; - struct iwl_scan_results_notif results[]; -} __packed; /* SCAN_ITER_COMPLETE_NTF_UMAC_API_S_VER_1 */ - -#endif diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h deleted file mode 100644 index 493a8bdfb..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h +++ /dev/null @@ -1,414 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * 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_sta_h__ -#define __fw_api_sta_h__ - -/** - * enum iwl_sta_flags - flags for the ADD_STA host command - * @STA_FLG_REDUCED_TX_PWR_CTRL: - * @STA_FLG_REDUCED_TX_PWR_DATA: - * @STA_FLG_DISABLE_TX: set if TX should be disabled - * @STA_FLG_PS: set if STA is in Power Save - * @STA_FLG_INVALID: set if STA is invalid - * @STA_FLG_DLP_EN: Direct Link Protocol is enabled - * @STA_FLG_SET_ALL_KEYS: the current key applies to all key IDs - * @STA_FLG_DRAIN_FLOW: drain flow - * @STA_FLG_PAN: STA is for PAN interface - * @STA_FLG_CLASS_AUTH: - * @STA_FLG_CLASS_ASSOC: - * @STA_FLG_CLASS_MIMO_PROT: - * @STA_FLG_MAX_AGG_SIZE_MSK: maximal size for A-MPDU - * @STA_FLG_AGG_MPDU_DENS_MSK: maximal MPDU density for Tx aggregation - * @STA_FLG_FAT_EN_MSK: support for channel width (for Tx). This flag is - * initialised by driver and can be updated by fw upon reception of - * action frames that can change the channel width. When cleared the fw - * will send all the frames in 20MHz even when FAT channel is requested. - * @STA_FLG_MIMO_EN_MSK: support for MIMO. This flag is initialised by the - * driver and can be updated by fw upon reception of action frames. - * @STA_FLG_MFP_EN: Management Frame Protection - */ -enum iwl_sta_flags { - STA_FLG_REDUCED_TX_PWR_CTRL = BIT(3), - STA_FLG_REDUCED_TX_PWR_DATA = BIT(6), - - STA_FLG_DISABLE_TX = BIT(4), - - STA_FLG_PS = BIT(8), - STA_FLG_DRAIN_FLOW = BIT(12), - STA_FLG_PAN = BIT(13), - STA_FLG_CLASS_AUTH = BIT(14), - STA_FLG_CLASS_ASSOC = BIT(15), - STA_FLG_RTS_MIMO_PROT = BIT(17), - - STA_FLG_MAX_AGG_SIZE_SHIFT = 19, - STA_FLG_MAX_AGG_SIZE_8K = (0 << STA_FLG_MAX_AGG_SIZE_SHIFT), - STA_FLG_MAX_AGG_SIZE_16K = (1 << STA_FLG_MAX_AGG_SIZE_SHIFT), - STA_FLG_MAX_AGG_SIZE_32K = (2 << STA_FLG_MAX_AGG_SIZE_SHIFT), - STA_FLG_MAX_AGG_SIZE_64K = (3 << STA_FLG_MAX_AGG_SIZE_SHIFT), - STA_FLG_MAX_AGG_SIZE_128K = (4 << STA_FLG_MAX_AGG_SIZE_SHIFT), - STA_FLG_MAX_AGG_SIZE_256K = (5 << STA_FLG_MAX_AGG_SIZE_SHIFT), - STA_FLG_MAX_AGG_SIZE_512K = (6 << STA_FLG_MAX_AGG_SIZE_SHIFT), - STA_FLG_MAX_AGG_SIZE_1024K = (7 << STA_FLG_MAX_AGG_SIZE_SHIFT), - STA_FLG_MAX_AGG_SIZE_MSK = (7 << STA_FLG_MAX_AGG_SIZE_SHIFT), - - STA_FLG_AGG_MPDU_DENS_SHIFT = 23, - STA_FLG_AGG_MPDU_DENS_2US = (4 << STA_FLG_AGG_MPDU_DENS_SHIFT), - STA_FLG_AGG_MPDU_DENS_4US = (5 << STA_FLG_AGG_MPDU_DENS_SHIFT), - STA_FLG_AGG_MPDU_DENS_8US = (6 << STA_FLG_AGG_MPDU_DENS_SHIFT), - STA_FLG_AGG_MPDU_DENS_16US = (7 << STA_FLG_AGG_MPDU_DENS_SHIFT), - STA_FLG_AGG_MPDU_DENS_MSK = (7 << STA_FLG_AGG_MPDU_DENS_SHIFT), - - STA_FLG_FAT_EN_20MHZ = (0 << 26), - STA_FLG_FAT_EN_40MHZ = (1 << 26), - STA_FLG_FAT_EN_80MHZ = (2 << 26), - STA_FLG_FAT_EN_160MHZ = (3 << 26), - STA_FLG_FAT_EN_MSK = (3 << 26), - - STA_FLG_MIMO_EN_SISO = (0 << 28), - STA_FLG_MIMO_EN_MIMO2 = (1 << 28), - STA_FLG_MIMO_EN_MIMO3 = (2 << 28), - STA_FLG_MIMO_EN_MSK = (3 << 28), -}; - -/** - * enum iwl_sta_key_flag - key flags for the ADD_STA host command - * @STA_KEY_FLG_NO_ENC: no encryption - * @STA_KEY_FLG_WEP: WEP encryption algorithm - * @STA_KEY_FLG_CCM: CCMP encryption algorithm - * @STA_KEY_FLG_TKIP: TKIP encryption algorithm - * @STA_KEY_FLG_EXT: extended cipher algorithm (depends on the FW support) - * @STA_KEY_FLG_CMAC: CMAC encryption algorithm - * @STA_KEY_FLG_ENC_UNKNOWN: unknown encryption algorithm - * @STA_KEY_FLG_EN_MSK: mask for encryption algorithmi value - * @STA_KEY_FLG_WEP_KEY_MAP: wep is either a group key (0 - legacy WEP) or from - * station info array (1 - n 1X mode) - * @STA_KEY_FLG_KEYID_MSK: the index of the key - * @STA_KEY_NOT_VALID: key is invalid - * @STA_KEY_FLG_WEP_13BYTES: set for 13 bytes WEP key - * @STA_KEY_MULTICAST: set for multical key - * @STA_KEY_MFP: key is used for Management Frame Protection - */ -enum iwl_sta_key_flag { - STA_KEY_FLG_NO_ENC = (0 << 0), - STA_KEY_FLG_WEP = (1 << 0), - STA_KEY_FLG_CCM = (2 << 0), - STA_KEY_FLG_TKIP = (3 << 0), - STA_KEY_FLG_EXT = (4 << 0), - STA_KEY_FLG_CMAC = (6 << 0), - STA_KEY_FLG_ENC_UNKNOWN = (7 << 0), - STA_KEY_FLG_EN_MSK = (7 << 0), - - STA_KEY_FLG_WEP_KEY_MAP = BIT(3), - STA_KEY_FLG_KEYID_POS = 8, - STA_KEY_FLG_KEYID_MSK = (3 << STA_KEY_FLG_KEYID_POS), - STA_KEY_NOT_VALID = BIT(11), - STA_KEY_FLG_WEP_13BYTES = BIT(12), - STA_KEY_MULTICAST = BIT(14), - STA_KEY_MFP = BIT(15), -}; - -/** - * enum iwl_sta_modify_flag - indicate to the fw what flag are being changed - * @STA_MODIFY_KEY: this command modifies %key - * @STA_MODIFY_TID_DISABLE_TX: this command modifies %tid_disable_tx - * @STA_MODIFY_TX_RATE: unused - * @STA_MODIFY_ADD_BA_TID: this command modifies %add_immediate_ba_tid - * @STA_MODIFY_REMOVE_BA_TID: this command modifies %remove_immediate_ba_tid - * @STA_MODIFY_SLEEPING_STA_TX_COUNT: this command modifies %sleep_tx_count - * @STA_MODIFY_PROT_TH: - * @STA_MODIFY_QUEUES: modify the queues used by this station - */ -enum iwl_sta_modify_flag { - STA_MODIFY_KEY = BIT(0), - STA_MODIFY_TID_DISABLE_TX = BIT(1), - STA_MODIFY_TX_RATE = BIT(2), - STA_MODIFY_ADD_BA_TID = BIT(3), - STA_MODIFY_REMOVE_BA_TID = BIT(4), - STA_MODIFY_SLEEPING_STA_TX_COUNT = BIT(5), - STA_MODIFY_PROT_TH = BIT(6), - STA_MODIFY_QUEUES = BIT(7), -}; - -#define STA_MODE_MODIFY 1 - -/** - * enum iwl_sta_sleep_flag - type of sleep of the station - * @STA_SLEEP_STATE_AWAKE: - * @STA_SLEEP_STATE_PS_POLL: - * @STA_SLEEP_STATE_UAPSD: - * @STA_SLEEP_STATE_MOREDATA: set more-data bit on - * (last) released frame - */ -enum iwl_sta_sleep_flag { - STA_SLEEP_STATE_AWAKE = 0, - STA_SLEEP_STATE_PS_POLL = BIT(0), - STA_SLEEP_STATE_UAPSD = BIT(1), - STA_SLEEP_STATE_MOREDATA = BIT(2), -}; - -/* STA ID and color bits definitions */ -#define STA_ID_SEED (0x0f) -#define STA_ID_POS (0) -#define STA_ID_MSK (STA_ID_SEED << STA_ID_POS) - -#define STA_COLOR_SEED (0x7) -#define STA_COLOR_POS (4) -#define STA_COLOR_MSK (STA_COLOR_SEED << STA_COLOR_POS) - -#define STA_ID_N_COLOR_GET_COLOR(id_n_color) \ - (((id_n_color) & STA_COLOR_MSK) >> STA_COLOR_POS) -#define STA_ID_N_COLOR_GET_ID(id_n_color) \ - (((id_n_color) & STA_ID_MSK) >> STA_ID_POS) - -#define STA_KEY_MAX_NUM (16) -#define STA_KEY_IDX_INVALID (0xff) -#define STA_KEY_MAX_DATA_KEY_NUM (4) -#define IWL_MAX_GLOBAL_KEYS (4) -#define STA_KEY_LEN_WEP40 (5) -#define STA_KEY_LEN_WEP104 (13) - -/** - * struct iwl_mvm_keyinfo - key information - * @key_flags: type %iwl_sta_key_flag - * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection - * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx - * @key_offset: key offset in the fw's key table - * @key: 16-byte unicast decryption key - * @tx_secur_seq_cnt: initial RSC / PN needed for replay check - * @hw_tkip_mic_rx_key: byte: MIC Rx Key - used for TKIP only - * @hw_tkip_mic_tx_key: byte: MIC Tx Key - used for TKIP only - */ -struct iwl_mvm_keyinfo { - __le16 key_flags; - u8 tkip_rx_tsc_byte2; - u8 reserved1; - __le16 tkip_rx_ttak[5]; - u8 key_offset; - u8 reserved2; - u8 key[16]; - __le64 tx_secur_seq_cnt; - __le64 hw_tkip_mic_rx_key; - __le64 hw_tkip_mic_tx_key; -} __packed; - -/** - * struct iwl_mvm_add_sta_cmd - Add/modify a station in the fw's sta table. - * ( REPLY_ADD_STA = 0x18 ) - * @add_modify: 1: modify existing, 0: add new station - * @awake_acs: - * @tid_disable_tx: is tid BIT(tid) enabled for Tx. Clear BIT(x) to enable - * AMPDU for tid x. Set %STA_MODIFY_TID_DISABLE_TX to change this field. - * @mac_id_n_color: the Mac context this station belongs to - * @addr[ETH_ALEN]: station's MAC address - * @sta_id: index of station in uCode's station table - * @modify_mask: STA_MODIFY_*, selects which parameters to modify vs. leave - * alone. 1 - modify, 0 - don't change. - * @station_flags: look at %iwl_sta_flags - * @station_flags_msk: what of %station_flags have changed - * @add_immediate_ba_tid: tid for which to add block-ack support (Rx) - * Set %STA_MODIFY_ADD_BA_TID to use this field, and also set - * add_immediate_ba_ssn. - * @remove_immediate_ba_tid: tid for which to remove block-ack support (Rx) - * Set %STA_MODIFY_REMOVE_BA_TID to use this field - * @add_immediate_ba_ssn: ssn for the Rx block-ack session. Used together with - * add_immediate_ba_tid. - * @sleep_tx_count: number of packets to transmit to station even though it is - * asleep. Used to synchronise PS-poll and u-APSD responses while ucode - * keeps track of STA sleep state. - * @sleep_state_flags: Look at %iwl_sta_sleep_flag. - * @assoc_id: assoc_id to be sent in VHT PLCP (9-bit), for grp use 0, for AP - * mac-addr. - * @beamform_flags: beam forming controls - * @tfd_queue_msk: tfd queues used by this station - * - * The device contains an internal table of per-station information, with info - * on security keys, aggregation parameters, and Tx rates for initial Tx - * attempt and any retries (set by REPLY_TX_LINK_QUALITY_CMD). - * - * ADD_STA sets up the table entry for one station, either creating a new - * entry, or modifying a pre-existing one. - */ -struct iwl_mvm_add_sta_cmd { - u8 add_modify; - u8 awake_acs; - __le16 tid_disable_tx; - __le32 mac_id_n_color; - u8 addr[ETH_ALEN]; /* _STA_ID_MODIFY_INFO_API_S_VER_1 */ - __le16 reserved2; - u8 sta_id; - u8 modify_mask; - __le16 reserved3; - __le32 station_flags; - __le32 station_flags_msk; - u8 add_immediate_ba_tid; - u8 remove_immediate_ba_tid; - __le16 add_immediate_ba_ssn; - __le16 sleep_tx_count; - __le16 sleep_state_flags; - __le16 assoc_id; - __le16 beamform_flags; - __le32 tfd_queue_msk; -} __packed; /* ADD_STA_CMD_API_S_VER_7 */ - -/** - * struct iwl_mvm_add_sta_key_cmd - add/modify sta key - * ( REPLY_ADD_STA_KEY = 0x17 ) - * @sta_id: index of station in uCode's station table - * @key_offset: key offset in key storage - * @key_flags: type %iwl_sta_key_flag - * @key: key material data - * @key2: key material data - * @rx_secur_seq_cnt: RX security sequence counter for the key - * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection - * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx - */ -struct iwl_mvm_add_sta_key_cmd { - u8 sta_id; - u8 key_offset; - __le16 key_flags; - u8 key[16]; - u8 key2[16]; - u8 rx_secur_seq_cnt[16]; - u8 tkip_rx_tsc_byte2; - u8 reserved; - __le16 tkip_rx_ttak[5]; -} __packed; /* ADD_MODIFY_STA_KEY_API_S_VER_1 */ - -/** - * enum iwl_mvm_add_sta_rsp_status - status in the response to ADD_STA command - * @ADD_STA_SUCCESS: operation was executed successfully - * @ADD_STA_STATIONS_OVERLOAD: no room left in the fw's station table - * @ADD_STA_IMMEDIATE_BA_FAILURE: can't add Rx block ack session - * @ADD_STA_MODIFY_NON_EXISTING_STA: driver requested to modify a station that - * doesn't exist. - */ -enum iwl_mvm_add_sta_rsp_status { - ADD_STA_SUCCESS = 0x1, - ADD_STA_STATIONS_OVERLOAD = 0x2, - ADD_STA_IMMEDIATE_BA_FAILURE = 0x4, - ADD_STA_MODIFY_NON_EXISTING_STA = 0x8, -}; - -/** - * struct iwl_mvm_rm_sta_cmd - Add / modify a station in the fw's station table - * ( REMOVE_STA = 0x19 ) - * @sta_id: the station id of the station to be removed - */ -struct iwl_mvm_rm_sta_cmd { - u8 sta_id; - u8 reserved[3]; -} __packed; /* REMOVE_STA_CMD_API_S_VER_2 */ - -/** - * struct iwl_mvm_mgmt_mcast_key_cmd - * ( MGMT_MCAST_KEY = 0x1f ) - * @ctrl_flags: %iwl_sta_key_flag - * @IGTK: - * @K1: unused - * @K2: unused - * @sta_id: station ID that support IGTK - * @key_id: - * @receive_seq_cnt: initial RSC/PN needed for replay check - */ -struct iwl_mvm_mgmt_mcast_key_cmd { - __le32 ctrl_flags; - u8 IGTK[16]; - u8 K1[16]; - u8 K2[16]; - __le32 key_id; - __le32 sta_id; - __le64 receive_seq_cnt; -} __packed; /* SEC_MGMT_MULTICAST_KEY_CMD_API_S_VER_1 */ - -struct iwl_mvm_wep_key { - u8 key_index; - u8 key_offset; - __le16 reserved1; - u8 key_size; - u8 reserved2[3]; - u8 key[16]; -} __packed; - -struct iwl_mvm_wep_key_cmd { - __le32 mac_id_n_color; - u8 num_keys; - u8 decryption_type; - u8 flags; - u8 reserved; - struct iwl_mvm_wep_key wep_key[0]; -} __packed; /* SEC_CURR_WEP_KEY_CMD_API_S_VER_2 */ - -/** - * struct iwl_mvm_eosp_notification - EOSP notification from firmware - * @remain_frame_count: # of frames remaining, non-zero if SP was cut - * short by GO absence - * @sta_id: station ID - */ -struct iwl_mvm_eosp_notification { - __le32 remain_frame_count; - __le32 sta_id; -} __packed; /* UAPSD_EOSP_NTFY_API_S_VER_1 */ - -#endif /* __fw_api_sta_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h deleted file mode 100644 index 0c321f63e..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h +++ /dev/null @@ -1,284 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * 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_stats_h__ -#define __fw_api_stats_h__ -#include "fw-api-mac.h" - -struct mvm_statistics_dbg { - __le32 burst_check; - __le32 burst_count; - __le32 wait_for_silence_timeout_cnt; - __le32 reserved[3]; -} __packed; /* STATISTICS_DEBUG_API_S_VER_2 */ - -struct mvm_statistics_div { - __le32 tx_on_a; - __le32 tx_on_b; - __le32 exec_time; - __le32 probe_time; - __le32 rssi_ant; - __le32 reserved2; -} __packed; /* STATISTICS_SLOW_DIV_API_S_VER_2 */ - -struct mvm_statistics_rx_non_phy { - __le32 bogus_cts; /* CTS received when not expecting CTS */ - __le32 bogus_ack; /* ACK received when not expecting ACK */ - __le32 non_bssid_frames; /* number of frames with BSSID that - * doesn't belong to the STA BSSID */ - __le32 filtered_frames; /* count frames that were dumped in the - * filtering process */ - __le32 non_channel_beacons; /* beacons with our bss id but not on - * our serving channel */ - __le32 channel_beacons; /* beacons with our bss id and in our - * serving channel */ - __le32 num_missed_bcon; /* number of missed beacons */ - __le32 adc_rx_saturation_time; /* count in 0.8us units the time the - * ADC was in saturation */ - __le32 ina_detection_search_time;/* total time (in 0.8us) searched - * for INA */ - __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */ - __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */ - __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */ - __le32 interference_data_flag; /* flag for interference data - * availability. 1 when data is - * available. */ - __le32 channel_load; /* counts RX Enable time in uSec */ - __le32 dsp_false_alarms; /* DSP false alarm (both OFDM - * and CCK) counter */ - __le32 beacon_rssi_a; - __le32 beacon_rssi_b; - __le32 beacon_rssi_c; - __le32 beacon_energy_a; - __le32 beacon_energy_b; - __le32 beacon_energy_c; - __le32 num_bt_kills; - __le32 mac_id; - __le32 directed_data_mpdu; -} __packed; /* STATISTICS_RX_NON_PHY_API_S_VER_3 */ - -struct mvm_statistics_rx_phy { - __le32 ina_cnt; - __le32 fina_cnt; - __le32 plcp_err; - __le32 crc32_err; - __le32 overrun_err; - __le32 early_overrun_err; - __le32 crc32_good; - __le32 false_alarm_cnt; - __le32 fina_sync_err_cnt; - __le32 sfd_timeout; - __le32 fina_timeout; - __le32 unresponded_rts; - __le32 rxe_frame_lmt_overrun; - __le32 sent_ack_cnt; - __le32 sent_cts_cnt; - __le32 sent_ba_rsp_cnt; - __le32 dsp_self_kill; - __le32 mh_format_err; - __le32 re_acq_main_rssi_sum; - __le32 reserved; -} __packed; /* STATISTICS_RX_PHY_API_S_VER_2 */ - -struct mvm_statistics_rx_ht_phy { - __le32 plcp_err; - __le32 overrun_err; - __le32 early_overrun_err; - __le32 crc32_good; - __le32 crc32_err; - __le32 mh_format_err; - __le32 agg_crc32_good; - __le32 agg_mpdu_cnt; - __le32 agg_cnt; - __le32 unsupport_mcs; -} __packed; /* STATISTICS_HT_RX_PHY_API_S_VER_1 */ - -struct mvm_statistics_tx_non_phy { - __le32 preamble_cnt; - __le32 rx_detected_cnt; - __le32 bt_prio_defer_cnt; - __le32 bt_prio_kill_cnt; - __le32 few_bytes_cnt; - __le32 cts_timeout; - __le32 ack_timeout; - __le32 expected_ack_cnt; - __le32 actual_ack_cnt; - __le32 dump_msdu_cnt; - __le32 burst_abort_next_frame_mismatch_cnt; - __le32 burst_abort_missing_next_frame_cnt; - __le32 cts_timeout_collision; - __le32 ack_or_ba_timeout_collision; -} __packed; /* STATISTICS_TX_NON_PHY_API_S_VER_3 */ - -#define MAX_CHAINS 3 - -struct mvm_statistics_tx_non_phy_agg { - __le32 ba_timeout; - __le32 ba_reschedule_frames; - __le32 scd_query_agg_frame_cnt; - __le32 scd_query_no_agg; - __le32 scd_query_agg; - __le32 scd_query_mismatch; - __le32 frame_not_ready; - __le32 underrun; - __le32 bt_prio_kill; - __le32 rx_ba_rsp_cnt; - __s8 txpower[MAX_CHAINS]; - __s8 reserved; - __le32 reserved2; -} __packed; /* STATISTICS_TX_NON_PHY_AGG_API_S_VER_1 */ - -struct mvm_statistics_tx_channel_width { - __le32 ext_cca_narrow_ch20[1]; - __le32 ext_cca_narrow_ch40[2]; - __le32 ext_cca_narrow_ch80[3]; - __le32 ext_cca_narrow_ch160[4]; - __le32 last_tx_ch_width_indx; - __le32 rx_detected_per_ch_width[4]; - __le32 success_per_ch_width[4]; - __le32 fail_per_ch_width[4]; -}; /* STATISTICS_TX_CHANNEL_WIDTH_API_S_VER_1 */ - -struct mvm_statistics_tx { - struct mvm_statistics_tx_non_phy general; - struct mvm_statistics_tx_non_phy_agg agg; - struct mvm_statistics_tx_channel_width channel_width; -} __packed; /* STATISTICS_TX_API_S_VER_4 */ - - -struct mvm_statistics_bt_activity { - __le32 hi_priority_tx_req_cnt; - __le32 hi_priority_tx_denied_cnt; - __le32 lo_priority_tx_req_cnt; - __le32 lo_priority_tx_denied_cnt; - __le32 hi_priority_rx_req_cnt; - __le32 hi_priority_rx_denied_cnt; - __le32 lo_priority_rx_req_cnt; - __le32 lo_priority_rx_denied_cnt; -} __packed; /* STATISTICS_BT_ACTIVITY_API_S_VER_1 */ - -struct mvm_statistics_general_v8 { - __le32 radio_temperature; - __le32 radio_voltage; - struct mvm_statistics_dbg dbg; - __le32 sleep_time; - __le32 slots_out; - __le32 slots_idle; - __le32 ttl_timestamp; - struct mvm_statistics_div slow_div; - __le32 rx_enable_counter; - /* - * num_of_sos_states: - * count the number of times we have to re-tune - * in order to get out of bad PHY status - */ - __le32 num_of_sos_states; - __le32 beacon_filtered; - __le32 missed_beacons; - u8 beacon_filter_average_energy; - u8 beacon_filter_reason; - u8 beacon_filter_current_energy; - u8 beacon_filter_reserved; - __le32 beacon_filter_delta_time; - struct mvm_statistics_bt_activity bt_activity; - __le64 rx_time; - __le64 on_time_rf; - __le64 on_time_scan; - __le64 tx_time; - __le32 beacon_counter[NUM_MAC_INDEX]; - u8 beacon_average_energy[NUM_MAC_INDEX]; - u8 reserved[4 - (NUM_MAC_INDEX % 4)]; -} __packed; /* STATISTICS_GENERAL_API_S_VER_8 */ - -struct mvm_statistics_rx { - struct mvm_statistics_rx_phy ofdm; - struct mvm_statistics_rx_phy cck; - struct mvm_statistics_rx_non_phy general; - struct mvm_statistics_rx_ht_phy ofdm_ht; -} __packed; /* STATISTICS_RX_API_S_VER_3 */ - -/* - * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command) - * - * By default, uCode issues this notification after receiving a beacon - * while associated. To disable this behavior, set DISABLE_NOTIF flag in the - * STATISTICS_CMD (0x9c), below. - */ - -struct iwl_notif_statistics_v10 { - __le32 flag; - struct mvm_statistics_rx rx; - struct mvm_statistics_tx tx; - struct mvm_statistics_general_v8 general; -} __packed; /* STATISTICS_NTFY_API_S_VER_10 */ - -#define IWL_STATISTICS_FLG_CLEAR 0x1 -#define IWL_STATISTICS_FLG_DISABLE_NOTIF 0x2 - -struct iwl_statistics_cmd { - __le32 flags; -} __packed; /* STATISTICS_CMD_API_S_VER_1 */ - -#endif /* __fw_api_stats_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tof.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tof.h deleted file mode 100644 index eed6271d0..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tof.h +++ /dev/null @@ -1,386 +0,0 @@ -/****************************************************************************** - * - * 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) 2015 Intel Deutschland GmbH - * - * 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) 2015 Intel Deutschland GmbH - * 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_tof_h__ -#define __fw_api_tof_h__ - -#include "fw-api.h" - -/* ToF sub-group command IDs */ -enum iwl_mvm_tof_sub_grp_ids { - TOF_RANGE_REQ_CMD = 0x1, - TOF_CONFIG_CMD = 0x2, - TOF_RANGE_ABORT_CMD = 0x3, - TOF_RANGE_REQ_EXT_CMD = 0x4, - TOF_RESPONDER_CONFIG_CMD = 0x5, - TOF_NW_INITIATED_RES_SEND_CMD = 0x6, - TOF_NEIGHBOR_REPORT_REQ_CMD = 0x7, - TOF_NEIGHBOR_REPORT_RSP_NOTIF = 0xFC, - TOF_NW_INITIATED_REQ_RCVD_NOTIF = 0xFD, - TOF_RANGE_RESPONSE_NOTIF = 0xFE, - TOF_MCSI_DEBUG_NOTIF = 0xFB, -}; - -/** - * struct iwl_tof_config_cmd - ToF configuration - * @tof_disabled: 0 enabled, 1 - disabled - * @one_sided_disabled: 0 enabled, 1 - disabled - * @is_debug_mode: 1 debug mode, 0 - otherwise - * @is_buf_required: 1 channel estimation buffer required, 0 - otherwise - */ -struct iwl_tof_config_cmd { - __le32 sub_grp_cmd_id; - u8 tof_disabled; - u8 one_sided_disabled; - u8 is_debug_mode; - u8 is_buf_required; -} __packed; - -/** - * struct iwl_tof_responder_config_cmd - ToF AP mode (for debug) - * @burst_period: future use: (currently hard coded in the LMAC) - * The interval between two sequential bursts. - * @min_delta_ftm: future use: (currently hard coded in the LMAC) - * The minimum delay between two sequential FTM Responses - * in the same burst. - * @burst_duration: future use: (currently hard coded in the LMAC) - * The total time for all FTMs handshake in the same burst. - * Affect the time events duration in the LMAC. - * @num_of_burst_exp: future use: (currently hard coded in the LMAC) - * The number of bursts for the current ToF request. Affect - * the number of events allocations in the current iteration. - * @get_ch_est: for xVT only, NA for driver - * @abort_responder: when set to '1' - Responder will terminate its activity - * (all other fields in the command are ignored) - * @recv_sta_req_params: 1 - Responder will ignore the other Responder's - * params and use the recomended Initiator params. - * 0 - otherwise - * @channel_num: current AP Channel - * @bandwidth: current AP Bandwidth: 0 20MHz, 1 40MHz, 2 80MHz - * @rate: current AP rate - * @ctrl_ch_position: coding of the control channel position relative to - * the center frequency. - * 40MHz 0 below center, 1 above center - * 80MHz bits [0..1]: 0 the near 20MHz to the center, - * 1 the far 20MHz to the center - * bit[2] as above 40MHz - * @ftm_per_burst: FTMs per Burst - * @ftm_resp_ts_avail: '0' - we don't measure over the Initial FTM Response, - * '1' - we measure over the Initial FTM Response - * @asap_mode: ASAP / Non ASAP mode for the current WLS station - * @sta_id: index of the AP STA when in AP mode - * @tsf_timer_offset_msecs: The dictated time offset (mSec) from the AP's TSF - * @toa_offset: Artificial addition [0.1nsec] for the ToA - to be used for debug - * purposes, simulating station movement by adding various values - * to this field - * @bssid: Current AP BSSID - */ -struct iwl_tof_responder_config_cmd { - __le32 sub_grp_cmd_id; - __le16 burst_period; - u8 min_delta_ftm; - u8 burst_duration; - u8 num_of_burst_exp; - u8 get_ch_est; - u8 abort_responder; - u8 recv_sta_req_params; - u8 channel_num; - u8 bandwidth; - u8 rate; - u8 ctrl_ch_position; - u8 ftm_per_burst; - u8 ftm_resp_ts_avail; - u8 asap_mode; - u8 sta_id; - __le16 tsf_timer_offset_msecs; - __le16 toa_offset; - u8 bssid[ETH_ALEN]; -} __packed; - -/** - * struct iwl_tof_range_request_ext_cmd - extended range req for WLS - * @tsf_timer_offset_msec: the recommended time offset (mSec) from the AP's TSF - * @min_delta_ftm: Minimal time between two consecutive measurements, - * in units of 100us. 0 means no preference by station - * @ftm_format_and_bw20M: FTM Channel Spacing/Format for 20MHz: recommended - * value be sent to the AP - * @ftm_format_and_bw40M: FTM Channel Spacing/Format for 40MHz: recommended - * value to be sent to the AP - * @ftm_format_and_bw80M: FTM Channel Spacing/Format for 80MHz: recommended - * value to be sent to the AP - */ -struct iwl_tof_range_req_ext_cmd { - __le32 sub_grp_cmd_id; - __le16 tsf_timer_offset_msec; - __le16 reserved; - u8 min_delta_ftm; - u8 ftm_format_and_bw20M; - u8 ftm_format_and_bw40M; - u8 ftm_format_and_bw80M; -} __packed; - -#define IWL_MVM_TOF_MAX_APS 21 - -/** - * struct iwl_tof_range_req_ap_entry - AP configuration parameters - * @channel_num: Current AP Channel - * @bandwidth: Current AP Bandwidth: 0 20MHz, 1 40MHz, 2 80MHz - * @tsf_delta_direction: TSF relatively to the subject AP - * @ctrl_ch_position: Coding of the control channel position relative to the - * center frequency. - * 40MHz 0 below center, 1 above center - * 80MHz bits [0..1]: 0 the near 20MHz to the center, - * 1 the far 20MHz to the center - * bit[2] as above 40MHz - * @bssid: AP's bss id - * @measure_type: Measurement type: 0 - two sided, 1 - One sided - * @num_of_bursts: Recommended value to be sent to the AP. 2s Exponent of the - * number of measurement iterations (min 2^0 = 1, max 2^14) - * @burst_period: Recommended value to be sent to the AP. Measurement - * periodicity In units of 100ms. ignored if num_of_bursts = 0 - * @samples_per_burst: 2-sided: the number of FTMs pairs in single Burst (1-31) - * 1-sided: how many rts/cts pairs should be used per burst. - * @retries_per_sample: Max number of retries that the LMAC should send - * in case of no replies by the AP. - * @tsf_delta: TSF Delta in units of microseconds. - * The difference between the AP TSF and the device local clock. - * @location_req: Location Request Bit[0] LCI should be sent in the FTMR - * Bit[1] Civic should be sent in the FTMR - * @asap_mode: 0 - non asap mode, 1 - asap mode (not relevant for one sided) - * @enable_dyn_ack: Enable Dynamic ACK BW. - * 0 Initiator interact with regular AP - * 1 Initiator interact with Responder machine: need to send the - * Initiator Acks with HT 40MHz / 80MHz, since the Responder should - * use it for its ch est measurement (this flag will be set when we - * configure the opposite machine to be Responder). - * @rssi: Last received value - * leagal values: -128-0 (0x7f). above 0x0 indicating an invalid value. - */ -struct iwl_tof_range_req_ap_entry { - u8 channel_num; - u8 bandwidth; - u8 tsf_delta_direction; - u8 ctrl_ch_position; - u8 bssid[ETH_ALEN]; - u8 measure_type; - u8 num_of_bursts; - __le16 burst_period; - u8 samples_per_burst; - u8 retries_per_sample; - __le32 tsf_delta; - u8 location_req; - u8 asap_mode; - u8 enable_dyn_ack; - s8 rssi; -} __packed; - -/** - * enum iwl_tof_response_mode - * @IWL_MVM_TOF_RESPOSE_ASAP: report each AP measurement separately as soon as - * possible (not supported for this release) - * @IWL_MVM_TOF_RESPOSE_TIMEOUT: report all AP measurements as a batch upon - * timeout expiration - * @IWL_MVM_TOF_RESPOSE_COMPLETE: report all AP measurements as a batch at the - * earlier of: measurements completion / timeout - * expiration. - */ -enum iwl_tof_response_mode { - IWL_MVM_TOF_RESPOSE_ASAP = 1, - IWL_MVM_TOF_RESPOSE_TIMEOUT, - IWL_MVM_TOF_RESPOSE_COMPLETE, -}; - -/** - * struct iwl_tof_range_req_cmd - start measurement cmd - * @request_id: A Token incremented per request. The same Token will be - * sent back in the range response - * @initiator: 0- NW initiated, 1 - Client Initiated - * @one_sided_los_disable: '0'- run ML-Algo for both ToF/OneSided, - * '1' - run ML-Algo for ToF only - * @req_timeout: Requested timeout of the response in units of 100ms. - * This is equivalent to the session time configured to the - * LMAC in Initiator Request - * @report_policy: Supported partially for this release: For current release - - * the range report will be uploaded as a batch when ready or - * when the session is done (successfully / partially). - * one of iwl_tof_response_mode. - * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) - * @macaddr_random: '0' Use default source MAC address (i.e. p2_p), - * '1' Use MAC Address randomization according to the below - * @macaddr_mask: Bits set to 0 shall be copied from the MAC address template. - * Bits set to 1 shall be randomized by the UMAC - */ -struct iwl_tof_range_req_cmd { - __le32 sub_grp_cmd_id; - u8 request_id; - u8 initiator; - u8 one_sided_los_disable; - u8 req_timeout; - u8 report_policy; - u8 los_det_disable; - u8 num_of_ap; - u8 macaddr_random; - u8 macaddr_template[ETH_ALEN]; - u8 macaddr_mask[ETH_ALEN]; - struct iwl_tof_range_req_ap_entry ap[IWL_MVM_TOF_MAX_APS]; -} __packed; - -/** - * struct iwl_tof_gen_resp_cmd - generic ToF response - */ -struct iwl_tof_gen_resp_cmd { - __le32 sub_grp_cmd_id; - u8 data[]; -} __packed; - -/** - * struct iwl_tof_range_rsp_ap_entry_ntfy - AP parameters (response) - * @measure_status: current APs measurement status - * @measure_bw: Current AP Bandwidth: 0 20MHz, 1 40MHz, 2 80MHz - * @rtt: The Round Trip Time that took for the last measurement for - * current AP [nSec] - * @rtt_variance: The Variance of the RTT values measured for current AP - * @rtt_spread: The Difference between the maximum and the minimum RTT - * values measured for current AP in the current session [nsec] - * @rssi: RSSI as uploaded in the Channel Estimation notification - * @rssi_spread: The Difference between the maximum and the minimum RSSI values - * measured for current AP in the current session - * @range: Measured range [cm] - * @range_variance: Measured range variance [cm] - * @timestamp: The GP2 Clock [usec] where Channel Estimation notification was - * uploaded by the LMAC - */ -struct iwl_tof_range_rsp_ap_entry_ntfy { - u8 bssid[ETH_ALEN]; - u8 measure_status; - u8 measure_bw; - __le32 rtt; - __le32 rtt_variance; - __le32 rtt_spread; - s8 rssi; - u8 rssi_spread; - __le16 reserved; - __le32 range; - __le32 range_variance; - __le32 timestamp; -} __packed; - -/** - * struct iwl_tof_range_rsp_ntfy - - * @request_id: A Token ID of the corresponding Range request - * @request_status: status of current measurement session - * @last_in_batch: reprot policy (when not all responses are uploaded at once) - * @num_of_aps: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) - */ -struct iwl_tof_range_rsp_ntfy { - u8 request_id; - u8 request_status; - u8 last_in_batch; - u8 num_of_aps; - struct iwl_tof_range_rsp_ap_entry_ntfy ap[IWL_MVM_TOF_MAX_APS]; -} __packed; - -#define IWL_MVM_TOF_MCSI_BUF_SIZE (245) -/** - * struct iwl_tof_mcsi_notif - used for debug - * @token: token ID for the current session - * @role: '0' - initiator, '1' - responder - * @initiator_bssid: initiator machine - * @responder_bssid: responder machine - * @mcsi_buffer: debug data - */ -struct iwl_tof_mcsi_notif { - u8 token; - u8 role; - __le16 reserved; - u8 initiator_bssid[ETH_ALEN]; - u8 responder_bssid[ETH_ALEN]; - u8 mcsi_buffer[IWL_MVM_TOF_MCSI_BUF_SIZE * 4]; -} __packed; - -/** - * struct iwl_tof_neighbor_report_notif - * @bssid: BSSID of the AP which sent the report - * @request_token: same token as the corresponding request - * @status: - * @report_ie_len: the length of the response frame starting from the Element ID - * @data: the IEs - */ -struct iwl_tof_neighbor_report { - u8 bssid[ETH_ALEN]; - u8 request_token; - u8 status; - __le16 report_ie_len; - u8 data[]; -} __packed; - -/** - * struct iwl_tof_range_abort_cmd - * @request_id: corresponds to a range request - */ -struct iwl_tof_range_abort_cmd { - __le32 sub_grp_cmd_id; - u8 request_id; - u8 reserved[3]; -} __packed; - -#endif diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h deleted file mode 100644 index 853698ab8..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h +++ /dev/null @@ -1,646 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 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) 2012 - 2014 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_tx_h__ -#define __fw_api_tx_h__ - -/** - * enum iwl_tx_flags - bitmasks for tx_flags in TX command - * @TX_CMD_FLG_PROT_REQUIRE: use RTS or CTS-to-self to protect the frame - * @TX_CMD_FLG_WRITE_TX_POWER: update current tx power value in the mgmt frame - * @TX_CMD_FLG_ACK: expect ACK from receiving station - * @TX_CMD_FLG_STA_RATE: use RS table with initial index from the TX command. - * Otherwise, use rate_n_flags from the TX command - * @TX_CMD_FLG_BAR: this frame is a BA request, immediate BAR is expected - * Must set TX_CMD_FLG_ACK with this flag. - * @TX_CMD_FLG_VHT_NDPA: mark frame is NDPA for VHT beamformer sequence - * @TX_CMD_FLG_HT_NDPA: mark frame is NDPA for HT beamformer sequence - * @TX_CMD_FLG_CSI_FDBK2HOST: mark to send feedback to host (only if good CRC) - * @TX_CMD_FLG_BT_PRIO_POS: the position of the BT priority (bit 11 is ignored - * on old firmwares). - * @TX_CMD_FLG_BT_DIS: disable BT priority for this frame - * @TX_CMD_FLG_SEQ_CTL: set if FW should override the sequence control. - * Should be set for mgmt, non-QOS data, mcast, bcast and in scan command - * @TX_CMD_FLG_MORE_FRAG: this frame is non-last MPDU - * @TX_CMD_FLG_TSF: FW should calculate and insert TSF in the frame - * Should be set for beacons and probe responses - * @TX_CMD_FLG_CALIB: activate PA TX power calibrations - * @TX_CMD_FLG_KEEP_SEQ_CTL: if seq_ctl is set, don't increase inner seq count - * @TX_CMD_FLG_MH_PAD: driver inserted 2 byte padding after MAC header. - * Should be set for 26/30 length MAC headers - * @TX_CMD_FLG_RESP_TO_DRV: zero this if the response should go only to FW - * @TX_CMD_FLG_CCMP_AGG: this frame uses CCMP for aggregation acceleration - * @TX_CMD_FLG_TKIP_MIC_DONE: FW already performed TKIP MIC calculation - * @TX_CMD_FLG_DUR: disable duration overwriting used in PS-Poll Assoc-id - * @TX_CMD_FLG_FW_DROP: FW should mark frame to be dropped - * @TX_CMD_FLG_EXEC_PAPD: execute PAPD - * @TX_CMD_FLG_PAPD_TYPE: 0 for reference power, 1 for nominal power - * @TX_CMD_FLG_HCCA_CHUNK: mark start of TSPEC chunk - */ -enum iwl_tx_flags { - TX_CMD_FLG_PROT_REQUIRE = BIT(0), - TX_CMD_FLG_WRITE_TX_POWER = BIT(1), - TX_CMD_FLG_ACK = BIT(3), - TX_CMD_FLG_STA_RATE = BIT(4), - TX_CMD_FLG_BAR = BIT(6), - TX_CMD_FLG_TXOP_PROT = BIT(7), - TX_CMD_FLG_VHT_NDPA = BIT(8), - TX_CMD_FLG_HT_NDPA = BIT(9), - TX_CMD_FLG_CSI_FDBK2HOST = BIT(10), - TX_CMD_FLG_BT_PRIO_POS = 11, - TX_CMD_FLG_BT_DIS = BIT(12), - TX_CMD_FLG_SEQ_CTL = BIT(13), - TX_CMD_FLG_MORE_FRAG = BIT(14), - TX_CMD_FLG_TSF = BIT(16), - TX_CMD_FLG_CALIB = BIT(17), - TX_CMD_FLG_KEEP_SEQ_CTL = BIT(18), - TX_CMD_FLG_MH_PAD = BIT(20), - TX_CMD_FLG_RESP_TO_DRV = BIT(21), - TX_CMD_FLG_CCMP_AGG = BIT(22), - TX_CMD_FLG_TKIP_MIC_DONE = BIT(23), - TX_CMD_FLG_DUR = BIT(25), - TX_CMD_FLG_FW_DROP = BIT(26), - TX_CMD_FLG_EXEC_PAPD = BIT(27), - TX_CMD_FLG_PAPD_TYPE = BIT(28), - TX_CMD_FLG_HCCA_CHUNK = BIT(31) -}; /* TX_FLAGS_BITS_API_S_VER_1 */ - -/** - * enum iwl_tx_pm_timeouts - pm timeout values in TX command - * @PM_FRAME_NONE: no need to suspend sleep mode - * @PM_FRAME_MGMT: fw suspend sleep mode for 100TU - * @PM_FRAME_ASSOC: fw suspend sleep mode for 10sec - */ -enum iwl_tx_pm_timeouts { - PM_FRAME_NONE = 0, - PM_FRAME_MGMT = 2, - PM_FRAME_ASSOC = 3, -}; - -/* - * TX command security control - */ -#define TX_CMD_SEC_WEP 0x01 -#define TX_CMD_SEC_CCM 0x02 -#define TX_CMD_SEC_TKIP 0x03 -#define TX_CMD_SEC_EXT 0x04 -#define TX_CMD_SEC_MSK 0x07 -#define TX_CMD_SEC_WEP_KEY_IDX_POS 6 -#define TX_CMD_SEC_WEP_KEY_IDX_MSK 0xc0 -#define TX_CMD_SEC_KEY128 0x08 - -/* TODO: how does these values are OK with only 16 bit variable??? */ -/* - * TX command next frame info - * - * bits 0:2 - security control (TX_CMD_SEC_*) - * bit 3 - immediate ACK required - * bit 4 - rate is taken from STA table - * bit 5 - frame belongs to BA stream - * bit 6 - immediate BA response expected - * bit 7 - unused - * bits 8:15 - Station ID - * bits 16:31 - rate - */ -#define TX_CMD_NEXT_FRAME_ACK_MSK (0x8) -#define TX_CMD_NEXT_FRAME_STA_RATE_MSK (0x10) -#define TX_CMD_NEXT_FRAME_BA_MSK (0x20) -#define TX_CMD_NEXT_FRAME_IMM_BA_RSP_MSK (0x40) -#define TX_CMD_NEXT_FRAME_FLAGS_MSK (0xf8) -#define TX_CMD_NEXT_FRAME_STA_ID_MSK (0xff00) -#define TX_CMD_NEXT_FRAME_STA_ID_POS (8) -#define TX_CMD_NEXT_FRAME_RATE_MSK (0xffff0000) -#define TX_CMD_NEXT_FRAME_RATE_POS (16) - -/* - * TX command Frame life time in us - to be written in pm_frame_timeout - */ -#define TX_CMD_LIFE_TIME_INFINITE 0xFFFFFFFF -#define TX_CMD_LIFE_TIME_DEFAULT 2000000 /* 2000 ms*/ -#define TX_CMD_LIFE_TIME_PROBE_RESP 40000 /* 40 ms */ -#define TX_CMD_LIFE_TIME_EXPIRED_FRAME 0 - -/* - * TID for non QoS frames - to be written in tid_tspec - */ -#define IWL_TID_NON_QOS IWL_MAX_TID_COUNT - -/* - * Limits on the retransmissions - to be written in {data,rts}_retry_limit - */ -#define IWL_DEFAULT_TX_RETRY 15 -#define IWL_MGMT_DFAULT_RETRY_LIMIT 3 -#define IWL_RTS_DFAULT_RETRY_LIMIT 60 -#define IWL_BAR_DFAULT_RETRY_LIMIT 60 -#define IWL_LOW_RETRY_LIMIT 7 - -/* TODO: complete documentation for try_cnt and btkill_cnt */ -/** - * struct iwl_tx_cmd - TX command struct to FW - * ( TX_CMD = 0x1c ) - * @len: in bytes of the payload, see below for details - * @tx_flags: combination of TX_CMD_FLG_* - * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is - * cleared. Combination of RATE_MCS_* - * @sta_id: index of destination station in FW station table - * @sec_ctl: security control, TX_CMD_SEC_* - * @initial_rate_index: index into the the rate table for initial TX attempt. - * Applied if TX_CMD_FLG_STA_RATE_MSK is set, normally 0 for data frames. - * @key: security key - * @next_frame_flags: TX_CMD_SEC_* and TX_CMD_NEXT_FRAME_* - * @life_time: frame life time (usecs??) - * @dram_lsb_ptr: Physical address of scratch area in the command (try_cnt + - * btkill_cnd + reserved), first 32 bits. "0" disables usage. - * @dram_msb_ptr: upper bits of the scratch physical address - * @rts_retry_limit: max attempts for RTS - * @data_retry_limit: max attempts to send the data packet - * @tid_spec: TID/tspec - * @pm_frame_timeout: PM TX frame timeout - * - * The byte count (both len and next_frame_len) includes MAC header - * (24/26/30/32 bytes) - * + 2 bytes pad if 26/30 header size - * + 8 byte IV for CCM or TKIP (not used for WEP) - * + Data payload - * + 8-byte MIC (not used for CCM/WEP) - * It does not include post-MAC padding, i.e., - * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes. - * Range of len: 14-2342 bytes. - * - * After the struct fields the MAC header is placed, plus any padding, - * and then the actial payload. - */ -struct iwl_tx_cmd { - __le16 len; - __le16 next_frame_len; - __le32 tx_flags; - struct { - u8 try_cnt; - u8 btkill_cnt; - __le16 reserved; - } scratch; /* DRAM_SCRATCH_API_U_VER_1 */ - __le32 rate_n_flags; - u8 sta_id; - u8 sec_ctl; - u8 initial_rate_index; - u8 reserved2; - u8 key[16]; - __le32 reserved3; - __le32 life_time; - __le32 dram_lsb_ptr; - u8 dram_msb_ptr; - u8 rts_retry_limit; - u8 data_retry_limit; - u8 tid_tspec; - __le16 pm_frame_timeout; - __le16 reserved4; - u8 payload[0]; - struct ieee80211_hdr hdr[0]; -} __packed; /* TX_CMD_API_S_VER_3 */ - -/* - * TX response related data - */ - -/* - * enum iwl_tx_status - status that is returned by the fw after attempts to Tx - * @TX_STATUS_SUCCESS: - * @TX_STATUS_DIRECT_DONE: - * @TX_STATUS_POSTPONE_DELAY: - * @TX_STATUS_POSTPONE_FEW_BYTES: - * @TX_STATUS_POSTPONE_BT_PRIO: - * @TX_STATUS_POSTPONE_QUIET_PERIOD: - * @TX_STATUS_POSTPONE_CALC_TTAK: - * @TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY: - * @TX_STATUS_FAIL_SHORT_LIMIT: - * @TX_STATUS_FAIL_LONG_LIMIT: - * @TX_STATUS_FAIL_UNDERRUN: - * @TX_STATUS_FAIL_DRAIN_FLOW: - * @TX_STATUS_FAIL_RFKILL_FLUSH: - * @TX_STATUS_FAIL_LIFE_EXPIRE: - * @TX_STATUS_FAIL_DEST_PS: - * @TX_STATUS_FAIL_HOST_ABORTED: - * @TX_STATUS_FAIL_BT_RETRY: - * @TX_STATUS_FAIL_STA_INVALID: - * @TX_TATUS_FAIL_FRAG_DROPPED: - * @TX_STATUS_FAIL_TID_DISABLE: - * @TX_STATUS_FAIL_FIFO_FLUSHED: - * @TX_STATUS_FAIL_SMALL_CF_POLL: - * @TX_STATUS_FAIL_FW_DROP: - * @TX_STATUS_FAIL_STA_COLOR_MISMATCH: mismatch between color of Tx cmd and - * STA table - * @TX_FRAME_STATUS_INTERNAL_ABORT: - * @TX_MODE_MSK: - * @TX_MODE_NO_BURST: - * @TX_MODE_IN_BURST_SEQ: - * @TX_MODE_FIRST_IN_BURST: - * @TX_QUEUE_NUM_MSK: - * - * Valid only if frame_count =1 - * TODO: complete documentation - */ -enum iwl_tx_status { - TX_STATUS_MSK = 0x000000ff, - TX_STATUS_SUCCESS = 0x01, - TX_STATUS_DIRECT_DONE = 0x02, - /* postpone TX */ - TX_STATUS_POSTPONE_DELAY = 0x40, - TX_STATUS_POSTPONE_FEW_BYTES = 0x41, - TX_STATUS_POSTPONE_BT_PRIO = 0x42, - TX_STATUS_POSTPONE_QUIET_PERIOD = 0x43, - TX_STATUS_POSTPONE_CALC_TTAK = 0x44, - /* abort TX */ - TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY = 0x81, - TX_STATUS_FAIL_SHORT_LIMIT = 0x82, - TX_STATUS_FAIL_LONG_LIMIT = 0x83, - TX_STATUS_FAIL_UNDERRUN = 0x84, - TX_STATUS_FAIL_DRAIN_FLOW = 0x85, - TX_STATUS_FAIL_RFKILL_FLUSH = 0x86, - TX_STATUS_FAIL_LIFE_EXPIRE = 0x87, - TX_STATUS_FAIL_DEST_PS = 0x88, - TX_STATUS_FAIL_HOST_ABORTED = 0x89, - TX_STATUS_FAIL_BT_RETRY = 0x8a, - TX_STATUS_FAIL_STA_INVALID = 0x8b, - TX_STATUS_FAIL_FRAG_DROPPED = 0x8c, - TX_STATUS_FAIL_TID_DISABLE = 0x8d, - TX_STATUS_FAIL_FIFO_FLUSHED = 0x8e, - TX_STATUS_FAIL_SMALL_CF_POLL = 0x8f, - TX_STATUS_FAIL_FW_DROP = 0x90, - TX_STATUS_FAIL_STA_COLOR_MISMATCH = 0x91, - TX_STATUS_INTERNAL_ABORT = 0x92, - TX_MODE_MSK = 0x00000f00, - TX_MODE_NO_BURST = 0x00000000, - TX_MODE_IN_BURST_SEQ = 0x00000100, - TX_MODE_FIRST_IN_BURST = 0x00000200, - TX_QUEUE_NUM_MSK = 0x0001f000, - TX_NARROW_BW_MSK = 0x00060000, - TX_NARROW_BW_1DIV2 = 0x00020000, - TX_NARROW_BW_1DIV4 = 0x00040000, - TX_NARROW_BW_1DIV8 = 0x00060000, -}; - -/* - * enum iwl_tx_agg_status - TX aggregation status - * @AGG_TX_STATE_STATUS_MSK: - * @AGG_TX_STATE_TRANSMITTED: - * @AGG_TX_STATE_UNDERRUN: - * @AGG_TX_STATE_BT_PRIO: - * @AGG_TX_STATE_FEW_BYTES: - * @AGG_TX_STATE_ABORT: - * @AGG_TX_STATE_LAST_SENT_TTL: - * @AGG_TX_STATE_LAST_SENT_TRY_CNT: - * @AGG_TX_STATE_LAST_SENT_BT_KILL: - * @AGG_TX_STATE_SCD_QUERY: - * @AGG_TX_STATE_TEST_BAD_CRC32: - * @AGG_TX_STATE_RESPONSE: - * @AGG_TX_STATE_DUMP_TX: - * @AGG_TX_STATE_DELAY_TX: - * @AGG_TX_STATE_TRY_CNT_MSK: Retry count for 1st frame in aggregation (retries - * occur if tx failed for this frame when it was a member of a previous - * aggregation block). If rate scaling is used, retry count indicates the - * rate table entry used for all frames in the new agg. - *@ AGG_TX_STATE_SEQ_NUM_MSK: Command ID and sequence number of Tx command for - * this frame - * - * TODO: complete documentation - */ -enum iwl_tx_agg_status { - AGG_TX_STATE_STATUS_MSK = 0x00fff, - AGG_TX_STATE_TRANSMITTED = 0x000, - AGG_TX_STATE_UNDERRUN = 0x001, - AGG_TX_STATE_BT_PRIO = 0x002, - AGG_TX_STATE_FEW_BYTES = 0x004, - AGG_TX_STATE_ABORT = 0x008, - AGG_TX_STATE_LAST_SENT_TTL = 0x010, - AGG_TX_STATE_LAST_SENT_TRY_CNT = 0x020, - AGG_TX_STATE_LAST_SENT_BT_KILL = 0x040, - AGG_TX_STATE_SCD_QUERY = 0x080, - AGG_TX_STATE_TEST_BAD_CRC32 = 0x0100, - AGG_TX_STATE_RESPONSE = 0x1ff, - AGG_TX_STATE_DUMP_TX = 0x200, - AGG_TX_STATE_DELAY_TX = 0x400, - AGG_TX_STATE_TRY_CNT_POS = 12, - AGG_TX_STATE_TRY_CNT_MSK = 0xf << AGG_TX_STATE_TRY_CNT_POS, -}; - -#define AGG_TX_STATE_LAST_SENT_MSK (AGG_TX_STATE_LAST_SENT_TTL| \ - AGG_TX_STATE_LAST_SENT_TRY_CNT| \ - AGG_TX_STATE_LAST_SENT_BT_KILL) - -/* - * The mask below describes a status where we are absolutely sure that the MPDU - * wasn't sent. For BA/Underrun we cannot be that sure. All we know that we've - * written the bytes to the TXE, but we know nothing about what the DSP did. - */ -#define AGG_TX_STAT_FRAME_NOT_SENT (AGG_TX_STATE_FEW_BYTES | \ - AGG_TX_STATE_ABORT | \ - AGG_TX_STATE_SCD_QUERY) - -/* - * REPLY_TX = 0x1c (response) - * - * This response may be in one of two slightly different formats, indicated - * by the frame_count field: - * - * 1) No aggregation (frame_count == 1). This reports Tx results for a single - * frame. Multiple attempts, at various bit rates, may have been made for - * this frame. - * - * 2) Aggregation (frame_count > 1). This reports Tx results for two or more - * frames that used block-acknowledge. All frames were transmitted at - * same rate. Rate scaling may have been used if first frame in this new - * agg block failed in previous agg block(s). - * - * Note that, for aggregation, ACK (block-ack) status is not delivered - * here; block-ack has not been received by the time the device records - * this status. - * This status relates to reasons the tx might have been blocked or aborted - * within the device, rather than whether it was received successfully by - * the destination station. - */ - -/** - * struct agg_tx_status - per packet TX aggregation status - * @status: enum iwl_tx_agg_status - * @sequence: Sequence # for this frame's Tx cmd (not SSN!) - */ -struct agg_tx_status { - __le16 status; - __le16 sequence; -} __packed; - -/* - * definitions for initial rate index field - * bits [3:0] initial rate index - * bits [6:4] rate table color, used for the initial rate - * bit-7 invalid rate indication - */ -#define TX_RES_INIT_RATE_INDEX_MSK 0x0f -#define TX_RES_RATE_TABLE_COLOR_MSK 0x70 -#define TX_RES_INV_RATE_INDEX_MSK 0x80 - -#define IWL_MVM_TX_RES_GET_TID(_ra_tid) ((_ra_tid) & 0x0f) -#define IWL_MVM_TX_RES_GET_RA(_ra_tid) ((_ra_tid) >> 4) - -/** - * struct iwl_mvm_tx_resp - notifies that fw is TXing a packet - * ( REPLY_TX = 0x1c ) - * @frame_count: 1 no aggregation, >1 aggregation - * @bt_kill_count: num of times blocked by bluetooth (unused for agg) - * @failure_rts: num of failures due to unsuccessful RTS - * @failure_frame: num failures due to no ACK (unused for agg) - * @initial_rate: for non-agg: rate of the successful Tx. For agg: rate of the - * Tx of all the batch. RATE_MCS_* - * @wireless_media_time: for non-agg: RTS + CTS + frame tx attempts time + ACK. - * for agg: RTS + CTS + aggregation tx time + block-ack time. - * in usec. - * @pa_status: tx power info - * @pa_integ_res_a: tx power info - * @pa_integ_res_b: tx power info - * @pa_integ_res_c: tx power info - * @measurement_req_id: tx power info - * @tfd_info: TFD information set by the FH - * @seq_ctl: sequence control from the Tx cmd - * @byte_cnt: byte count from the Tx cmd - * @tlc_info: TLC rate info - * @ra_tid: bits [3:0] = ra, bits [7:4] = tid - * @frame_ctrl: frame control - * @status: for non-agg: frame status TX_STATUS_* - * for agg: status of 1st frame, AGG_TX_STATE_*; other frame status fields - * follow this one, up to frame_count. - * - * After the array of statuses comes the SSN of the SCD. Look at - * %iwl_mvm_get_scd_ssn for more details. - */ -struct iwl_mvm_tx_resp { - u8 frame_count; - u8 bt_kill_count; - u8 failure_rts; - u8 failure_frame; - __le32 initial_rate; - __le16 wireless_media_time; - - u8 pa_status; - u8 pa_integ_res_a[3]; - u8 pa_integ_res_b[3]; - u8 pa_integ_res_c[3]; - __le16 measurement_req_id; - u8 reduced_tpc; - u8 reserved; - - __le32 tfd_info; - __le16 seq_ctl; - __le16 byte_cnt; - u8 tlc_info; - u8 ra_tid; - __le16 frame_ctrl; - - struct agg_tx_status status; -} __packed; /* TX_RSP_API_S_VER_3 */ - -/** - * struct iwl_mvm_ba_notif - notifies about reception of BA - * ( BA_NOTIF = 0xc5 ) - * @sta_addr_lo32: lower 32 bits of the MAC address - * @sta_addr_hi16: upper 16 bits of the MAC address - * @sta_id: Index of recipient (BA-sending) station in fw's station table - * @tid: tid of the session - * @seq_ctl: - * @bitmap: the bitmap of the BA notification as seen in the air - * @scd_flow: the tx queue this BA relates to - * @scd_ssn: the index of the last contiguously sent packet - * @txed: number of Txed frames in this batch - * @txed_2_done: number of Acked frames in this batch - */ -struct iwl_mvm_ba_notif { - __le32 sta_addr_lo32; - __le16 sta_addr_hi16; - __le16 reserved; - - u8 sta_id; - u8 tid; - __le16 seq_ctl; - __le64 bitmap; - __le16 scd_flow; - __le16 scd_ssn; - u8 txed; - u8 txed_2_done; - __le16 reserved1; -} __packed; - -/* - * struct iwl_mac_beacon_cmd - beacon template command - * @tx: the tx commands associated with the beacon frame - * @template_id: currently equal to the mac context id of the coresponding - * mac. - * @tim_idx: the offset of the tim IE in the beacon - * @tim_size: the length of the tim IE - * @frame: the template of the beacon frame - */ -struct iwl_mac_beacon_cmd { - struct iwl_tx_cmd tx; - __le32 template_id; - __le32 tim_idx; - __le32 tim_size; - 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; - -/** - * struct iwl_extended_beacon_notif - notifies about beacon transmission - * @beacon_notify_hdr: tx response command associated with the beacon - * @tsf: last beacon tsf - * @ibss_mgr_status: whether IBSS is manager - * @gp2: last beacon time in gp2 - */ -struct iwl_extended_beacon_notif { - struct iwl_mvm_tx_resp beacon_notify_hdr; - __le64 tsf; - __le32 ibss_mgr_status; - __le32 gp2; -} __packed; /* BEACON_NTFY_API_S_VER_5 */ - -/** - * enum iwl_dump_control - dump (flush) control flags - * @DUMP_TX_FIFO_FLUSH: Dump MSDUs until the the FIFO is empty - * and the TFD queues are empty. - */ -enum iwl_dump_control { - DUMP_TX_FIFO_FLUSH = BIT(1), -}; - -/** - * struct iwl_tx_path_flush_cmd -- queue/FIFO flush command - * @queues_ctl: bitmap of queues to flush - * @flush_ctl: control flags - * @reserved: reserved - */ -struct iwl_tx_path_flush_cmd { - __le32 queues_ctl; - __le16 flush_ctl; - __le16 reserved; -} __packed; /* TX_PATH_FLUSH_CMD_API_S_VER_1 */ - -/** - * iwl_mvm_get_scd_ssn - returns the SSN of the SCD - * @tx_resp: the Tx response from the fw (agg or non-agg) - * - * When the fw sends an AMPDU, it fetches the MPDUs one after the other. Since - * it can't know that everything will go well until the end of the AMPDU, it - * can't know in advance the number of MPDUs that will be sent in the current - * batch. This is why it writes the agg Tx response while it fetches the MPDUs. - * Hence, it can't know in advance what the SSN of the SCD will be at the end - * of the batch. This is why the SSN of the SCD is written at the end of the - * whole struct at a variable offset. This function knows how to cope with the - * variable offset and returns the SSN of the SCD. - */ -static inline u32 iwl_mvm_get_scd_ssn(struct iwl_mvm_tx_resp *tx_resp) -{ - return le32_to_cpup((__le32 *)&tx_resp->status + - tx_resp->frame_count) & 0xfff; -} - -/** - * struct iwl_scd_txq_cfg_cmd - New txq hw scheduler config command - * @token: - * @sta_id: station id - * @tid: - * @scd_queue: scheduler queue to confiug - * @enable: 1 queue enable, 0 queue disable - * @aggregate: 1 aggregated queue, 0 otherwise - * @tx_fifo: %enum iwl_mvm_tx_fifo - * @window: BA window size - * @ssn: SSN for the BA agreement - */ -struct iwl_scd_txq_cfg_cmd { - u8 token; - u8 sta_id; - u8 tid; - u8 scd_queue; - u8 enable; - u8 aggregate; - u8 tx_fifo; - u8 window; - __le16 ssn; - __le16 reserved; -} __packed; /* SCD_QUEUE_CFG_CMD_API_S_VER_1 */ - -/** - * struct iwl_scd_txq_cfg_rsp - * @token: taken from the command - * @sta_id: station id from the command - * @tid: tid from the command - * @scd_queue: scd_queue from the command - */ -struct iwl_scd_txq_cfg_rsp { - u8 token; - u8 sta_id; - u8 tid; - u8 scd_queue; -} __packed; /* SCD_QUEUE_CFG_RSP_API_S_VER_1 */ - -#endif /* __fw_api_tx_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h deleted file mode 100644 index 181590fbd..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ /dev/null @@ -1,1773 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * 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_h__ -#define __fw_api_h__ - -#include "fw-api-rs.h" -#include "fw-api-rx.h" -#include "fw-api-tx.h" -#include "fw-api-sta.h" -#include "fw-api-mac.h" -#include "fw-api-power.h" -#include "fw-api-d3.h" -#include "fw-api-coex.h" -#include "fw-api-scan.h" -#include "fw-api-stats.h" -#include "fw-api-tof.h" - -/* Tx queue numbers */ -enum { - IWL_MVM_OFFCHANNEL_QUEUE = 8, - IWL_MVM_CMD_QUEUE = 9, -}; - -enum iwl_mvm_tx_fifo { - IWL_MVM_TX_FIFO_BK = 0, - IWL_MVM_TX_FIFO_BE, - IWL_MVM_TX_FIFO_VI, - IWL_MVM_TX_FIFO_VO, - IWL_MVM_TX_FIFO_MCAST = 5, - IWL_MVM_TX_FIFO_CMD = 7, -}; - -#define IWL_MVM_STATION_COUNT 16 - -#define IWL_MVM_TDLS_STA_COUNT 4 - -/* commands */ -enum { - MVM_ALIVE = 0x1, - REPLY_ERROR = 0x2, - ECHO_CMD = 0x3, - - INIT_COMPLETE_NOTIF = 0x4, - - /* PHY context commands */ - PHY_CONTEXT_CMD = 0x8, - DBG_CFG = 0x9, - ANTENNA_COUPLING_NOTIFICATION = 0xa, - - /* UMAC scan commands */ - SCAN_ITERATION_COMPLETE_UMAC = 0xb5, - SCAN_CFG_CMD = 0xc, - SCAN_REQ_UMAC = 0xd, - SCAN_ABORT_UMAC = 0xe, - SCAN_COMPLETE_UMAC = 0xf, - - /* station table */ - ADD_STA_KEY = 0x17, - ADD_STA = 0x18, - REMOVE_STA = 0x19, - - /* paging get item */ - FW_GET_ITEM_CMD = 0x1a, - - /* TX */ - TX_CMD = 0x1c, - TXPATH_FLUSH = 0x1e, - MGMT_MCAST_KEY = 0x1f, - - /* scheduler config */ - SCD_QUEUE_CFG = 0x1d, - - /* global key */ - WEP_KEY = 0x20, - - /* Memory */ - SHARED_MEM_CFG = 0x25, - - /* TDLS */ - TDLS_CHANNEL_SWITCH_CMD = 0x27, - TDLS_CHANNEL_SWITCH_NOTIFICATION = 0xaa, - TDLS_CONFIG_CMD = 0xa7, - - /* MAC and Binding commands */ - MAC_CONTEXT_CMD = 0x28, - TIME_EVENT_CMD = 0x29, /* both CMD and response */ - TIME_EVENT_NOTIFICATION = 0x2a, - BINDING_CONTEXT_CMD = 0x2b, - TIME_QUOTA_CMD = 0x2c, - NON_QOS_TX_COUNTER_CMD = 0x2d, - - LQ_CMD = 0x4e, - - /* paging block to FW cpu2 */ - FW_PAGING_BLOCK_CMD = 0x4f, - - /* Scan offload */ - SCAN_OFFLOAD_REQUEST_CMD = 0x51, - SCAN_OFFLOAD_ABORT_CMD = 0x52, - HOT_SPOT_CMD = 0x53, - SCAN_OFFLOAD_COMPLETE = 0x6D, - SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E, - SCAN_OFFLOAD_CONFIG_CMD = 0x6f, - MATCH_FOUND_NOTIFICATION = 0xd9, - SCAN_ITERATION_COMPLETE = 0xe7, - - /* Phy */ - PHY_CONFIGURATION_CMD = 0x6a, - CALIB_RES_NOTIF_PHY_DB = 0x6b, - /* PHY_DB_CMD = 0x6c, */ - - /* ToF - 802.11mc FTM */ - TOF_CMD = 0x10, - TOF_NOTIFICATION = 0x11, - - /* Power - legacy power table command */ - POWER_TABLE_CMD = 0x77, - PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78, - LTR_CONFIG = 0xee, - - /* Thermal Throttling*/ - REPLY_THERMAL_MNG_BACKOFF = 0x7e, - - /* Set/Get DC2DC frequency tune */ - DC2DC_CONFIG_CMD = 0x83, - - /* NVM */ - NVM_ACCESS_CMD = 0x88, - - SET_CALIB_DEFAULT_CMD = 0x8e, - - BEACON_NOTIFICATION = 0x90, - BEACON_TEMPLATE_CMD = 0x91, - TX_ANT_CONFIGURATION_CMD = 0x98, - STATISTICS_CMD = 0x9c, - STATISTICS_NOTIFICATION = 0x9d, - EOSP_NOTIFICATION = 0x9e, - REDUCE_TX_POWER_CMD = 0x9f, - - /* RF-KILL commands and notifications */ - CARD_STATE_CMD = 0xa0, - CARD_STATE_NOTIFICATION = 0xa1, - - MISSED_BEACONS_NOTIFICATION = 0xa2, - - /* Power - new power table command */ - MAC_PM_POWER_TABLE = 0xa9, - - MFUART_LOAD_NOTIFICATION = 0xb1, - - REPLY_RX_PHY_CMD = 0xc0, - REPLY_RX_MPDU_CMD = 0xc1, - BA_NOTIF = 0xc5, - - /* Location Aware Regulatory */ - MCC_UPDATE_CMD = 0xc8, - MCC_CHUB_UPDATE_CMD = 0xc9, - - MARKER_CMD = 0xcb, - - /* BT Coex */ - BT_COEX_PRIO_TABLE = 0xcc, - BT_COEX_PROT_ENV = 0xcd, - BT_PROFILE_NOTIFICATION = 0xce, - BT_CONFIG = 0x9b, - BT_COEX_UPDATE_SW_BOOST = 0x5a, - BT_COEX_UPDATE_CORUN_LUT = 0x5b, - BT_COEX_UPDATE_REDUCED_TXP = 0x5c, - BT_COEX_CI = 0x5d, - - REPLY_SF_CFG_CMD = 0xd1, - REPLY_BEACON_FILTERING_CMD = 0xd2, - - /* DTS measurements */ - CMD_DTS_MEASUREMENT_TRIGGER = 0xdc, - DTS_MEASUREMENT_NOTIFICATION = 0xdd, - - REPLY_DEBUG_CMD = 0xf0, - DEBUG_LOG_MSG = 0xf7, - - BCAST_FILTER_CMD = 0xcf, - MCAST_FILTER_CMD = 0xd0, - - /* D3 commands/notifications */ - D3_CONFIG_CMD = 0xd3, - PROT_OFFLOAD_CONFIG_CMD = 0xd4, - OFFLOADS_QUERY_CMD = 0xd5, - REMOTE_WAKE_CONFIG_CMD = 0xd6, - D0I3_END_CMD = 0xed, - - /* for WoWLAN in particular */ - WOWLAN_PATTERNS = 0xe0, - WOWLAN_CONFIGURATION = 0xe1, - WOWLAN_TSC_RSC_PARAM = 0xe2, - WOWLAN_TKIP_PARAM = 0xe3, - WOWLAN_KEK_KCK_MATERIAL = 0xe4, - WOWLAN_GET_STATUSES = 0xe5, - WOWLAN_TX_POWER_PER_DB = 0xe6, - - /* and for NetDetect */ - SCAN_OFFLOAD_PROFILES_QUERY_CMD = 0x56, - SCAN_OFFLOAD_HOTSPOTS_CONFIG_CMD = 0x58, - SCAN_OFFLOAD_HOTSPOTS_QUERY_CMD = 0x59, - - REPLY_MAX = 0xff, -}; - -enum iwl_phy_ops_subcmd_ids { - CMD_DTS_MEASUREMENT_TRIGGER_WIDE = 0x0, - DTS_MEASUREMENT_NOTIF_WIDE = 0xFF, -}; - -/* command groups */ -enum { - PHY_OPS_GROUP = 0x4, -}; - -/** - * struct iwl_cmd_response - generic response struct for most commands - * @status: status of the command asked, changes for each one - */ -struct iwl_cmd_response { - __le32 status; -}; - -/* - * struct iwl_tx_ant_cfg_cmd - * @valid: valid antenna configuration - */ -struct iwl_tx_ant_cfg_cmd { - __le32 valid; -} __packed; - -/* - * Calibration control struct. - * Sent as part of the phy configuration command. - * @flow_trigger: bitmap for which calibrations to perform according to - * flow triggers. - * @event_trigger: bitmap for which calibrations to perform according to - * event triggers. - */ -struct iwl_calib_ctrl { - __le32 flow_trigger; - __le32 event_trigger; -} __packed; - -/* This enum defines the bitmap of various calibrations to enable in both - * init ucode and runtime ucode through CALIBRATION_CFG_CMD. - */ -enum iwl_calib_cfg { - IWL_CALIB_CFG_XTAL_IDX = BIT(0), - IWL_CALIB_CFG_TEMPERATURE_IDX = BIT(1), - IWL_CALIB_CFG_VOLTAGE_READ_IDX = BIT(2), - IWL_CALIB_CFG_PAPD_IDX = BIT(3), - IWL_CALIB_CFG_TX_PWR_IDX = BIT(4), - IWL_CALIB_CFG_DC_IDX = BIT(5), - IWL_CALIB_CFG_BB_FILTER_IDX = BIT(6), - IWL_CALIB_CFG_LO_LEAKAGE_IDX = BIT(7), - IWL_CALIB_CFG_TX_IQ_IDX = BIT(8), - IWL_CALIB_CFG_TX_IQ_SKEW_IDX = BIT(9), - IWL_CALIB_CFG_RX_IQ_IDX = BIT(10), - IWL_CALIB_CFG_RX_IQ_SKEW_IDX = BIT(11), - IWL_CALIB_CFG_SENSITIVITY_IDX = BIT(12), - IWL_CALIB_CFG_CHAIN_NOISE_IDX = BIT(13), - IWL_CALIB_CFG_DISCONNECTED_ANT_IDX = BIT(14), - IWL_CALIB_CFG_ANT_COUPLING_IDX = BIT(15), - IWL_CALIB_CFG_DAC_IDX = BIT(16), - IWL_CALIB_CFG_ABS_IDX = BIT(17), - IWL_CALIB_CFG_AGC_IDX = BIT(18), -}; - -/* - * Phy configuration command. - */ -struct iwl_phy_cfg_cmd { - __le32 phy_cfg; - struct iwl_calib_ctrl calib_control; -} __packed; - -#define PHY_CFG_RADIO_TYPE (BIT(0) | BIT(1)) -#define PHY_CFG_RADIO_STEP (BIT(2) | BIT(3)) -#define PHY_CFG_RADIO_DASH (BIT(4) | BIT(5)) -#define PHY_CFG_PRODUCT_NUMBER (BIT(6) | BIT(7)) -#define PHY_CFG_TX_CHAIN_A BIT(8) -#define PHY_CFG_TX_CHAIN_B BIT(9) -#define PHY_CFG_TX_CHAIN_C BIT(10) -#define PHY_CFG_RX_CHAIN_A BIT(12) -#define PHY_CFG_RX_CHAIN_B BIT(13) -#define PHY_CFG_RX_CHAIN_C BIT(14) - - -/* Target of the NVM_ACCESS_CMD */ -enum { - NVM_ACCESS_TARGET_CACHE = 0, - NVM_ACCESS_TARGET_OTP = 1, - NVM_ACCESS_TARGET_EEPROM = 2, -}; - -/* Section types for NVM_ACCESS_CMD */ -enum { - NVM_SECTION_TYPE_SW = 1, - NVM_SECTION_TYPE_REGULATORY = 3, - NVM_SECTION_TYPE_CALIBRATION = 4, - NVM_SECTION_TYPE_PRODUCTION = 5, - NVM_SECTION_TYPE_MAC_OVERRIDE = 11, - NVM_SECTION_TYPE_PHY_SKU = 12, - NVM_MAX_NUM_SECTIONS = 13, -}; - -/** - * struct iwl_nvm_access_cmd_ver2 - Request the device to send an NVM section - * @op_code: 0 - read, 1 - write - * @target: NVM_ACCESS_TARGET_* - * @type: NVM_SECTION_TYPE_* - * @offset: offset in bytes into the section - * @length: in bytes, to read/write - * @data: if write operation, the data to write. On read its empty - */ -struct iwl_nvm_access_cmd { - u8 op_code; - u8 target; - __le16 type; - __le16 offset; - __le16 length; - u8 data[]; -} __packed; /* NVM_ACCESS_CMD_API_S_VER_2 */ - -#define NUM_OF_FW_PAGING_BLOCKS 33 /* 32 for data and 1 block for CSS */ - -/* - * struct iwl_fw_paging_cmd - paging layout - * - * (FW_PAGING_BLOCK_CMD = 0x4f) - * - * Send to FW the paging layout in the driver. - * - * @flags: various flags for the command - * @block_size: the block size in powers of 2 - * @block_num: number of blocks specified in the command. - * @device_phy_addr: virtual addresses from device side -*/ -struct iwl_fw_paging_cmd { - __le32 flags; - __le32 block_size; - __le32 block_num; - __le32 device_phy_addr[NUM_OF_FW_PAGING_BLOCKS]; -} __packed; /* FW_PAGING_BLOCK_CMD_API_S_VER_1 */ - -/* - * Fw items ID's - * - * @IWL_FW_ITEM_ID_PAGING: Address of the pages that the FW will upload - * download - */ -enum iwl_fw_item_id { - IWL_FW_ITEM_ID_PAGING = 3, -}; - -/* - * struct iwl_fw_get_item_cmd - get an item from the fw - */ -struct iwl_fw_get_item_cmd { - __le32 item_id; -} __packed; /* FW_GET_ITEM_CMD_API_S_VER_1 */ - -struct iwl_fw_get_item_resp { - __le32 item_id; - __le32 item_byte_cnt; - __le32 item_val; -} __packed; /* FW_GET_ITEM_RSP_S_VER_1 */ - -/** - * struct iwl_nvm_access_resp_ver2 - response to NVM_ACCESS_CMD - * @offset: offset in bytes into the section - * @length: in bytes, either how much was written or read - * @type: NVM_SECTION_TYPE_* - * @status: 0 for success, fail otherwise - * @data: if read operation, the data returned. Empty on write. - */ -struct iwl_nvm_access_resp { - __le16 offset; - __le16 length; - __le16 type; - __le16 status; - u8 data[]; -} __packed; /* NVM_ACCESS_CMD_RESP_API_S_VER_2 */ - -/* MVM_ALIVE 0x1 */ - -/* alive response is_valid values */ -#define ALIVE_RESP_UCODE_OK BIT(0) -#define ALIVE_RESP_RFKILL BIT(1) - -/* alive response ver_type values */ -enum { - FW_TYPE_HW = 0, - FW_TYPE_PROT = 1, - FW_TYPE_AP = 2, - FW_TYPE_WOWLAN = 3, - FW_TYPE_TIMING = 4, - FW_TYPE_WIPAN = 5 -}; - -/* alive response ver_subtype values */ -enum { - FW_SUBTYPE_FULL_FEATURE = 0, - FW_SUBTYPE_BOOTSRAP = 1, /* Not valid */ - FW_SUBTYPE_REDUCED = 2, - FW_SUBTYPE_ALIVE_ONLY = 3, - FW_SUBTYPE_WOWLAN = 4, - FW_SUBTYPE_AP_SUBTYPE = 5, - FW_SUBTYPE_WIPAN = 6, - FW_SUBTYPE_INITIALIZE = 9 -}; - -#define IWL_ALIVE_STATUS_ERR 0xDEAD -#define IWL_ALIVE_STATUS_OK 0xCAFE - -#define IWL_ALIVE_FLG_RFKILL BIT(0) - -struct mvm_alive_resp_ver1 { - __le16 status; - __le16 flags; - u8 ucode_minor; - u8 ucode_major; - __le16 id; - u8 api_minor; - u8 api_major; - u8 ver_subtype; - u8 ver_type; - u8 mac; - u8 opt; - __le16 reserved2; - __le32 timestamp; - __le32 error_event_table_ptr; /* SRAM address for error log */ - __le32 log_event_table_ptr; /* SRAM address for event log */ - __le32 cpu_register_ptr; - __le32 dbgm_config_ptr; - __le32 alive_counter_ptr; - __le32 scd_base_ptr; /* SRAM address for SCD */ -} __packed; /* ALIVE_RES_API_S_VER_1 */ - -struct mvm_alive_resp_ver2 { - __le16 status; - __le16 flags; - u8 ucode_minor; - u8 ucode_major; - __le16 id; - u8 api_minor; - u8 api_major; - u8 ver_subtype; - u8 ver_type; - u8 mac; - u8 opt; - __le16 reserved2; - __le32 timestamp; - __le32 error_event_table_ptr; /* SRAM address for error log */ - __le32 log_event_table_ptr; /* SRAM address for LMAC event log */ - __le32 cpu_register_ptr; - __le32 dbgm_config_ptr; - __le32 alive_counter_ptr; - __le32 scd_base_ptr; /* SRAM address for SCD */ - __le32 st_fwrd_addr; /* pointer to Store and forward */ - __le32 st_fwrd_size; - u8 umac_minor; /* UMAC version: minor */ - u8 umac_major; /* UMAC version: major */ - __le16 umac_id; /* UMAC version: id */ - __le32 error_info_addr; /* SRAM address for UMAC error log */ - __le32 dbg_print_buff_addr; -} __packed; /* ALIVE_RES_API_S_VER_2 */ - -struct mvm_alive_resp { - __le16 status; - __le16 flags; - __le32 ucode_minor; - __le32 ucode_major; - u8 ver_subtype; - u8 ver_type; - u8 mac; - u8 opt; - __le32 timestamp; - __le32 error_event_table_ptr; /* SRAM address for error log */ - __le32 log_event_table_ptr; /* SRAM address for LMAC event log */ - __le32 cpu_register_ptr; - __le32 dbgm_config_ptr; - __le32 alive_counter_ptr; - __le32 scd_base_ptr; /* SRAM address for SCD */ - __le32 st_fwrd_addr; /* pointer to Store and forward */ - __le32 st_fwrd_size; - __le32 umac_minor; /* UMAC version: minor */ - __le32 umac_major; /* UMAC version: major */ - __le32 error_info_addr; /* SRAM address for UMAC error log */ - __le32 dbg_print_buff_addr; -} __packed; /* ALIVE_RES_API_S_VER_3 */ - -/* Error response/notification */ -enum { - FW_ERR_UNKNOWN_CMD = 0x0, - FW_ERR_INVALID_CMD_PARAM = 0x1, - FW_ERR_SERVICE = 0x2, - FW_ERR_ARC_MEMORY = 0x3, - FW_ERR_ARC_CODE = 0x4, - FW_ERR_WATCH_DOG = 0x5, - FW_ERR_WEP_GRP_KEY_INDX = 0x10, - FW_ERR_WEP_KEY_SIZE = 0x11, - FW_ERR_OBSOLETE_FUNC = 0x12, - FW_ERR_UNEXPECTED = 0xFE, - FW_ERR_FATAL = 0xFF -}; - -/** - * struct iwl_error_resp - FW error indication - * ( REPLY_ERROR = 0x2 ) - * @error_type: one of FW_ERR_* - * @cmd_id: the command ID for which the error occured - * @bad_cmd_seq_num: sequence number of the erroneous command - * @error_service: which service created the error, applicable only if - * error_type = 2, otherwise 0 - * @timestamp: TSF in usecs. - */ -struct iwl_error_resp { - __le32 error_type; - u8 cmd_id; - u8 reserved1; - __le16 bad_cmd_seq_num; - __le32 error_service; - __le64 timestamp; -} __packed; - - -/* Common PHY, MAC and Bindings definitions */ - -#define MAX_MACS_IN_BINDING (3) -#define MAX_BINDINGS (4) -#define AUX_BINDING_INDEX (3) -#define MAX_PHYS (4) - -/* Used to extract ID and color from the context dword */ -#define FW_CTXT_ID_POS (0) -#define FW_CTXT_ID_MSK (0xff << FW_CTXT_ID_POS) -#define FW_CTXT_COLOR_POS (8) -#define FW_CTXT_COLOR_MSK (0xff << FW_CTXT_COLOR_POS) -#define FW_CTXT_INVALID (0xffffffff) - -#define FW_CMD_ID_AND_COLOR(_id, _color) ((_id << FW_CTXT_ID_POS) |\ - (_color << FW_CTXT_COLOR_POS)) - -/* Possible actions on PHYs, MACs and Bindings */ -enum { - FW_CTXT_ACTION_STUB = 0, - FW_CTXT_ACTION_ADD, - FW_CTXT_ACTION_MODIFY, - FW_CTXT_ACTION_REMOVE, - FW_CTXT_ACTION_NUM -}; /* COMMON_CONTEXT_ACTION_API_E_VER_1 */ - -/* Time Events */ - -/* Time Event types, according to MAC type */ -enum iwl_time_event_type { - /* BSS Station Events */ - TE_BSS_STA_AGGRESSIVE_ASSOC, - TE_BSS_STA_ASSOC, - TE_BSS_EAP_DHCP_PROT, - TE_BSS_QUIET_PERIOD, - - /* P2P Device Events */ - TE_P2P_DEVICE_DISCOVERABLE, - TE_P2P_DEVICE_LISTEN, - TE_P2P_DEVICE_ACTION_SCAN, - TE_P2P_DEVICE_FULL_SCAN, - - /* P2P Client Events */ - TE_P2P_CLIENT_AGGRESSIVE_ASSOC, - TE_P2P_CLIENT_ASSOC, - TE_P2P_CLIENT_QUIET_PERIOD, - - /* P2P GO Events */ - TE_P2P_GO_ASSOC_PROT, - TE_P2P_GO_REPETITIVE_NOA, - TE_P2P_GO_CT_WINDOW, - - /* WiDi Sync Events */ - TE_WIDI_TX_SYNC, - - /* Channel Switch NoA */ - TE_CHANNEL_SWITCH_PERIOD, - - TE_MAX -}; /* MAC_EVENT_TYPE_API_E_VER_1 */ - - - -/* Time event - defines for command API v1 */ - -/* - * @TE_V1_FRAG_NONE: fragmentation of the time event is NOT allowed. - * @TE_V1_FRAG_SINGLE: fragmentation of the time event is allowed, but only - * the first fragment is scheduled. - * @TE_V1_FRAG_DUAL: fragmentation of the time event is allowed, but only - * the first 2 fragments are scheduled. - * @TE_V1_FRAG_ENDLESS: fragmentation of the time event is allowed, and any - * number of fragments are valid. - * - * Other than the constant defined above, specifying a fragmentation value 'x' - * means that the event can be fragmented but only the first 'x' will be - * scheduled. - */ -enum { - TE_V1_FRAG_NONE = 0, - TE_V1_FRAG_SINGLE = 1, - TE_V1_FRAG_DUAL = 2, - TE_V1_FRAG_ENDLESS = 0xffffffff -}; - -/* If a Time Event can be fragmented, this is the max number of fragments */ -#define TE_V1_FRAG_MAX_MSK 0x0fffffff -/* Repeat the time event endlessly (until removed) */ -#define TE_V1_REPEAT_ENDLESS 0xffffffff -/* If a Time Event has bounded repetitions, this is the maximal value */ -#define TE_V1_REPEAT_MAX_MSK_V1 0x0fffffff - -/* Time Event dependencies: none, on another TE, or in a specific time */ -enum { - TE_V1_INDEPENDENT = 0, - TE_V1_DEP_OTHER = BIT(0), - TE_V1_DEP_TSF = BIT(1), - TE_V1_EVENT_SOCIOPATHIC = BIT(2), -}; /* MAC_EVENT_DEPENDENCY_POLICY_API_E_VER_2 */ - -/* - * @TE_V1_NOTIF_NONE: no notifications - * @TE_V1_NOTIF_HOST_EVENT_START: request/receive notification on event start - * @TE_V1_NOTIF_HOST_EVENT_END:request/receive notification on event end - * @TE_V1_NOTIF_INTERNAL_EVENT_START: internal FW use - * @TE_V1_NOTIF_INTERNAL_EVENT_END: internal FW use. - * @TE_V1_NOTIF_HOST_FRAG_START: request/receive notification on frag start - * @TE_V1_NOTIF_HOST_FRAG_END:request/receive notification on frag end - * @TE_V1_NOTIF_INTERNAL_FRAG_START: internal FW use. - * @TE_V1_NOTIF_INTERNAL_FRAG_END: internal FW use. - * - * 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. - */ -enum { - TE_V1_NOTIF_NONE = 0, - TE_V1_NOTIF_HOST_EVENT_START = BIT(0), - TE_V1_NOTIF_HOST_EVENT_END = BIT(1), - TE_V1_NOTIF_INTERNAL_EVENT_START = BIT(2), - TE_V1_NOTIF_INTERNAL_EVENT_END = BIT(3), - TE_V1_NOTIF_HOST_FRAG_START = BIT(4), - TE_V1_NOTIF_HOST_FRAG_END = BIT(5), - TE_V1_NOTIF_INTERNAL_FRAG_START = BIT(6), - TE_V1_NOTIF_INTERNAL_FRAG_END = BIT(7), -}; /* MAC_EVENT_ACTION_API_E_VER_2 */ - -/* Time event - defines for command API */ - -/* - * @TE_V2_FRAG_NONE: fragmentation of the time event is NOT allowed. - * @TE_V2_FRAG_SINGLE: fragmentation of the time event is allowed, but only - * the first fragment is scheduled. - * @TE_V2_FRAG_DUAL: fragmentation of the time event is allowed, but only - * the first 2 fragments are scheduled. - * @TE_V2_FRAG_ENDLESS: fragmentation of the time event is allowed, and any - * number of fragments are valid. - * - * Other than the constant defined above, specifying a fragmentation value 'x' - * means that the event can be fragmented but only the first 'x' will be - * scheduled. - */ -enum { - TE_V2_FRAG_NONE = 0, - TE_V2_FRAG_SINGLE = 1, - TE_V2_FRAG_DUAL = 2, - TE_V2_FRAG_MAX = 0xfe, - TE_V2_FRAG_ENDLESS = 0xff -}; - -/* Repeat the time event endlessly (until removed) */ -#define TE_V2_REPEAT_ENDLESS 0xff -/* If a Time Event has bounded repetitions, this is the maximal value */ -#define TE_V2_REPEAT_MAX 0xfe - -#define TE_V2_PLACEMENT_POS 12 -#define TE_V2_ABSENCE_POS 15 - -/* Time event policy values - * 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_V2_DEFAULT_POLICY: independent, social, present, unoticable - * @TE_V2_NOTIF_HOST_EVENT_START: request/receive notification on event start - * @TE_V2_NOTIF_HOST_EVENT_END:request/receive notification on event end - * @TE_V2_NOTIF_INTERNAL_EVENT_START: internal FW use - * @TE_V2_NOTIF_INTERNAL_EVENT_END: internal FW use. - * @TE_V2_NOTIF_HOST_FRAG_START: request/receive notification on frag start - * @TE_V2_NOTIF_HOST_FRAG_END:request/receive notification on frag end - * @TE_V2_NOTIF_INTERNAL_FRAG_START: internal FW use. - * @TE_V2_NOTIF_INTERNAL_FRAG_END: internal FW use. - * @TE_V2_DEP_OTHER: depends on another time event - * @TE_V2_DEP_TSF: depends on a specific time - * @TE_V2_EVENT_SOCIOPATHIC: can't co-exist with other events of tha same MAC - * @TE_V2_ABSENCE: are we present or absent during the Time Event. - */ -enum { - TE_V2_DEFAULT_POLICY = 0x0, - - /* notifications (event start/stop, fragment start/stop) */ - TE_V2_NOTIF_HOST_EVENT_START = BIT(0), - TE_V2_NOTIF_HOST_EVENT_END = BIT(1), - TE_V2_NOTIF_INTERNAL_EVENT_START = BIT(2), - TE_V2_NOTIF_INTERNAL_EVENT_END = BIT(3), - - TE_V2_NOTIF_HOST_FRAG_START = BIT(4), - TE_V2_NOTIF_HOST_FRAG_END = BIT(5), - TE_V2_NOTIF_INTERNAL_FRAG_START = BIT(6), - TE_V2_NOTIF_INTERNAL_FRAG_END = BIT(7), - T2_V2_START_IMMEDIATELY = BIT(11), - - TE_V2_NOTIF_MSK = 0xff, - - /* placement characteristics */ - TE_V2_DEP_OTHER = BIT(TE_V2_PLACEMENT_POS), - TE_V2_DEP_TSF = BIT(TE_V2_PLACEMENT_POS + 1), - TE_V2_EVENT_SOCIOPATHIC = BIT(TE_V2_PLACEMENT_POS + 2), - - /* are we present or absent during the Time Event. */ - TE_V2_ABSENCE = BIT(TE_V2_ABSENCE_POS), -}; - -/** - * struct iwl_time_event_cmd_api - configuring Time Events - * with struct MAC_TIME_EVENT_DATA_API_S_VER_2 (see also - * with version 1. determined by IWL_UCODE_TLV_FLAGS) - * ( TIME_EVENT_CMD = 0x29 ) - * @id_and_color: ID and color of the relevant MAC - * @action: action to perform, one of FW_CTXT_ACTION_* - * @id: this field has two meanings, depending on the action: - * If the action is ADD, then it means the type of event to add. - * For all other actions it is the unique event ID assigned when the - * event was added by the FW. - * @apply_time: When to start the Time Event (in GP2) - * @max_delay: maximum delay to event's start (apply time), in TU - * @depends_on: the unique ID of the event we depend on (if any) - * @interval: interval between repetitions, in TU - * @duration: duration of event in TU - * @repeat: how many repetitions to do, can be TE_REPEAT_ENDLESS - * @max_frags: maximal number of fragments the Time Event can be divided to - * @policy: defines whether uCode shall notify the host or other uCode modules - * on event and/or fragment start and/or end - * using one of TE_INDEPENDENT, TE_DEP_OTHER, TE_DEP_TSF - * TE_EVENT_SOCIOPATHIC - * using TE_ABSENCE and using TE_NOTIF_* - */ -struct iwl_time_event_cmd { - /* COMMON_INDEX_HDR_API_S_VER_1 */ - __le32 id_and_color; - __le32 action; - __le32 id; - /* MAC_TIME_EVENT_DATA_API_S_VER_2 */ - __le32 apply_time; - __le32 max_delay; - __le32 depends_on; - __le32 interval; - __le32 duration; - u8 repeat; - u8 max_frags; - __le16 policy; -} __packed; /* MAC_TIME_EVENT_CMD_API_S_VER_2 */ - -/** - * struct iwl_time_event_resp - response structure to iwl_time_event_cmd - * @status: bit 0 indicates success, all others specify errors - * @id: the Time Event type - * @unique_id: the unique ID assigned (in ADD) or given (others) to the TE - * @id_and_color: ID and color of the relevant MAC - */ -struct iwl_time_event_resp { - __le32 status; - __le32 id; - __le32 unique_id; - __le32 id_and_color; -} __packed; /* MAC_TIME_EVENT_RSP_API_S_VER_1 */ - -/** - * struct iwl_time_event_notif - notifications of time event start/stop - * ( TIME_EVENT_NOTIFICATION = 0x2a ) - * @timestamp: action timestamp in GP2 - * @session_id: session's unique id - * @unique_id: unique id of the Time Event itself - * @id_and_color: ID and color of the relevant MAC - * @action: one of TE_NOTIF_START or TE_NOTIF_END - * @status: true if scheduled, false otherwise (not executed) - */ -struct iwl_time_event_notif { - __le32 timestamp; - __le32 session_id; - __le32 unique_id; - __le32 id_and_color; - __le32 action; - __le32 status; -} __packed; /* MAC_TIME_EVENT_NTFY_API_S_VER_1 */ - - -/* Bindings and Time Quota */ - -/** - * struct iwl_binding_cmd - configuring bindings - * ( BINDING_CONTEXT_CMD = 0x2b ) - * @id_and_color: ID and color of the relevant Binding - * @action: action to perform, one of FW_CTXT_ACTION_* - * @macs: array of MAC id and colors which belong to the binding - * @phy: PHY id and color which belongs to the binding - */ -struct iwl_binding_cmd { - /* COMMON_INDEX_HDR_API_S_VER_1 */ - __le32 id_and_color; - __le32 action; - /* BINDING_DATA_API_S_VER_1 */ - __le32 macs[MAX_MACS_IN_BINDING]; - __le32 phy; -} __packed; /* BINDING_CMD_API_S_VER_1 */ - -/* The maximal number of fragments in the FW's schedule session */ -#define IWL_MVM_MAX_QUOTA 128 - -/** - * struct iwl_time_quota_data - configuration of time quota per binding - * @id_and_color: ID and color of the relevant Binding - * @quota: absolute time quota in TU. The scheduler will try to divide the - * remainig quota (after Time Events) according to this quota. - * @max_duration: max uninterrupted context duration in TU - */ -struct iwl_time_quota_data { - __le32 id_and_color; - __le32 quota; - __le32 max_duration; -} __packed; /* TIME_QUOTA_DATA_API_S_VER_1 */ - -/** - * struct iwl_time_quota_cmd - configuration of time quota between bindings - * ( TIME_QUOTA_CMD = 0x2c ) - * @quotas: allocations per binding - */ -struct iwl_time_quota_cmd { - struct iwl_time_quota_data quotas[MAX_BINDINGS]; -} __packed; /* TIME_QUOTA_ALLOCATION_CMD_API_S_VER_1 */ - - -/* PHY context */ - -/* Supported bands */ -#define PHY_BAND_5 (0) -#define PHY_BAND_24 (1) - -/* Supported channel width, vary if there is VHT support */ -#define PHY_VHT_CHANNEL_MODE20 (0x0) -#define PHY_VHT_CHANNEL_MODE40 (0x1) -#define PHY_VHT_CHANNEL_MODE80 (0x2) -#define PHY_VHT_CHANNEL_MODE160 (0x3) - -/* - * Control channel position: - * For legacy set bit means upper channel, otherwise lower. - * For VHT - bit-2 marks if the control is lower/upper relative to center-freq - * bits-1:0 mark the distance from the center freq. for 20Mhz, offset is 0. - * center_freq - * | - * 40Mhz |_______|_______| - * 80Mhz |_______|_______|_______|_______| - * 160Mhz |_______|_______|_______|_______|_______|_______|_______|_______| - * code 011 010 001 000 | 100 101 110 111 - */ -#define PHY_VHT_CTRL_POS_1_BELOW (0x0) -#define PHY_VHT_CTRL_POS_2_BELOW (0x1) -#define PHY_VHT_CTRL_POS_3_BELOW (0x2) -#define PHY_VHT_CTRL_POS_4_BELOW (0x3) -#define PHY_VHT_CTRL_POS_1_ABOVE (0x4) -#define PHY_VHT_CTRL_POS_2_ABOVE (0x5) -#define PHY_VHT_CTRL_POS_3_ABOVE (0x6) -#define PHY_VHT_CTRL_POS_4_ABOVE (0x7) - -/* - * @band: PHY_BAND_* - * @channel: channel number - * @width: PHY_[VHT|LEGACY]_CHANNEL_* - * @ctrl channel: PHY_[VHT|LEGACY]_CTRL_* - */ -struct iwl_fw_channel_info { - u8 band; - u8 channel; - u8 width; - u8 ctrl_pos; -} __packed; - -#define PHY_RX_CHAIN_DRIVER_FORCE_POS (0) -#define PHY_RX_CHAIN_DRIVER_FORCE_MSK \ - (0x1 << PHY_RX_CHAIN_DRIVER_FORCE_POS) -#define PHY_RX_CHAIN_VALID_POS (1) -#define PHY_RX_CHAIN_VALID_MSK \ - (0x7 << PHY_RX_CHAIN_VALID_POS) -#define PHY_RX_CHAIN_FORCE_SEL_POS (4) -#define PHY_RX_CHAIN_FORCE_SEL_MSK \ - (0x7 << PHY_RX_CHAIN_FORCE_SEL_POS) -#define PHY_RX_CHAIN_FORCE_MIMO_SEL_POS (7) -#define PHY_RX_CHAIN_FORCE_MIMO_SEL_MSK \ - (0x7 << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS) -#define PHY_RX_CHAIN_CNT_POS (10) -#define PHY_RX_CHAIN_CNT_MSK \ - (0x3 << PHY_RX_CHAIN_CNT_POS) -#define PHY_RX_CHAIN_MIMO_CNT_POS (12) -#define PHY_RX_CHAIN_MIMO_CNT_MSK \ - (0x3 << PHY_RX_CHAIN_MIMO_CNT_POS) -#define PHY_RX_CHAIN_MIMO_FORCE_POS (14) -#define PHY_RX_CHAIN_MIMO_FORCE_MSK \ - (0x1 << PHY_RX_CHAIN_MIMO_FORCE_POS) - -/* TODO: fix the value, make it depend on firmware at runtime? */ -#define NUM_PHY_CTX 3 - -/* TODO: complete missing documentation */ -/** - * struct iwl_phy_context_cmd - config of the PHY context - * ( PHY_CONTEXT_CMD = 0x8 ) - * @id_and_color: ID and color of the relevant Binding - * @action: action to perform, one of FW_CTXT_ACTION_* - * @apply_time: 0 means immediate apply and context switch. - * other value means apply new params after X usecs - * @tx_param_color: ??? - * @channel_info: - * @txchain_info: ??? - * @rxchain_info: ??? - * @acquisition_data: ??? - * @dsp_cfg_flags: set to 0 - */ -struct iwl_phy_context_cmd { - /* COMMON_INDEX_HDR_API_S_VER_1 */ - __le32 id_and_color; - __le32 action; - /* PHY_CONTEXT_DATA_API_S_VER_1 */ - __le32 apply_time; - __le32 tx_param_color; - struct iwl_fw_channel_info ci; - __le32 txchain_info; - __le32 rxchain_info; - __le32 acquisition_data; - __le32 dsp_cfg_flags; -} __packed; /* PHY_CONTEXT_CMD_API_VER_1 */ - -/* - * Aux ROC command - * - * Command requests the firmware to create a time event for a certain duration - * and remain on the given channel. This is done by using the Aux framework in - * the FW. - * The command was first used for Hot Spot issues - but can be used regardless - * to Hot Spot. - * - * ( HOT_SPOT_CMD 0x53 ) - * - * @id_and_color: ID and color of the MAC - * @action: action to perform, one of FW_CTXT_ACTION_* - * @event_unique_id: If the action FW_CTXT_ACTION_REMOVE then the - * event_unique_id should be the id of the time event assigned by ucode. - * Otherwise ignore the event_unique_id. - * @sta_id_and_color: station id and color, resumed during "Remain On Channel" - * activity. - * @channel_info: channel info - * @node_addr: Our MAC Address - * @reserved: reserved for alignment - * @apply_time: GP2 value to start (should always be the current GP2 value) - * @apply_time_max_delay: Maximum apply time delay value in TU. Defines max - * time by which start of the event is allowed to be postponed. - * @duration: event duration in TU To calculate event duration: - * timeEventDuration = min(duration, remainingQuota) - */ -struct iwl_hs20_roc_req { - /* COMMON_INDEX_HDR_API_S_VER_1 hdr */ - __le32 id_and_color; - __le32 action; - __le32 event_unique_id; - __le32 sta_id_and_color; - struct iwl_fw_channel_info channel_info; - u8 node_addr[ETH_ALEN]; - __le16 reserved; - __le32 apply_time; - __le32 apply_time_max_delay; - __le32 duration; -} __packed; /* HOT_SPOT_CMD_API_S_VER_1 */ - -/* - * values for AUX ROC result values - */ -enum iwl_mvm_hot_spot { - HOT_SPOT_RSP_STATUS_OK, - HOT_SPOT_RSP_STATUS_TOO_MANY_EVENTS, - HOT_SPOT_MAX_NUM_OF_SESSIONS, -}; - -/* - * Aux ROC command response - * - * In response to iwl_hs20_roc_req the FW sends this command to notify the - * driver the uid of the timevent. - * - * ( HOT_SPOT_CMD 0x53 ) - * - * @event_unique_id: Unique ID of time event assigned by ucode - * @status: Return status 0 is success, all the rest used for specific errors - */ -struct iwl_hs20_roc_res { - __le32 event_unique_id; - __le32 status; -} __packed; /* HOT_SPOT_RSP_API_S_VER_1 */ - -/** - * struct iwl_radio_version_notif - information on the radio version - * ( RADIO_VERSION_NOTIFICATION = 0x68 ) - * @radio_flavor: - * @radio_step: - * @radio_dash: - */ -struct iwl_radio_version_notif { - __le32 radio_flavor; - __le32 radio_step; - __le32 radio_dash; -} __packed; /* RADIO_VERSION_NOTOFICATION_S_VER_1 */ - -enum iwl_card_state_flags { - CARD_ENABLED = 0x00, - HW_CARD_DISABLED = 0x01, - SW_CARD_DISABLED = 0x02, - CT_KILL_CARD_DISABLED = 0x04, - HALT_CARD_DISABLED = 0x08, - CARD_DISABLED_MSK = 0x0f, - CARD_IS_RX_ON = 0x10, -}; - -/** - * struct iwl_radio_version_notif - information on the radio version - * ( CARD_STATE_NOTIFICATION = 0xa1 ) - * @flags: %iwl_card_state_flags - */ -struct iwl_card_state_notif { - __le32 flags; -} __packed; /* CARD_STATE_NTFY_API_S_VER_1 */ - -/** - * struct iwl_missed_beacons_notif - information on missed beacons - * ( MISSED_BEACONS_NOTIFICATION = 0xa2 ) - * @mac_id: interface ID - * @consec_missed_beacons_since_last_rx: number of consecutive missed - * beacons since last RX. - * @consec_missed_beacons: number of consecutive missed beacons - * @num_expected_beacons: - * @num_recvd_beacons: - */ -struct iwl_missed_beacons_notif { - __le32 mac_id; - __le32 consec_missed_beacons_since_last_rx; - __le32 consec_missed_beacons; - __le32 num_expected_beacons; - __le32 num_recvd_beacons; -} __packed; /* MISSED_BEACON_NTFY_API_S_VER_3 */ - -/** - * struct iwl_mfuart_load_notif - mfuart image version & status - * ( MFUART_LOAD_NOTIFICATION = 0xb1 ) - * @installed_ver: installed image version - * @external_ver: external image version - * @status: MFUART loading status - * @duration: MFUART loading time -*/ -struct iwl_mfuart_load_notif { - __le32 installed_ver; - __le32 external_ver; - __le32 status; - __le32 duration; -} __packed; /*MFU_LOADER_NTFY_API_S_VER_1*/ - -/** - * struct iwl_set_calib_default_cmd - set default value for calibration. - * ( SET_CALIB_DEFAULT_CMD = 0x8e ) - * @calib_index: the calibration to set value for - * @length: of data - * @data: the value to set for the calibration result - */ -struct iwl_set_calib_default_cmd { - __le16 calib_index; - __le16 length; - u8 data[0]; -} __packed; /* PHY_CALIB_OVERRIDE_VALUES_S */ - -#define MAX_PORT_ID_NUM 2 -#define MAX_MCAST_FILTERING_ADDRESSES 256 - -/** - * 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 */ - -#define MAX_BCAST_FILTERS 8 -#define MAX_BCAST_FILTER_ATTRS 2 - -/** - * enum iwl_mvm_bcast_filter_attr_offset - written by fw for each Rx packet - * @BCAST_FILTER_OFFSET_PAYLOAD_START: offset is from payload start. - * @BCAST_FILTER_OFFSET_IP_END: offset is from ip header end (i.e. - * start of ip payload). - */ -enum iwl_mvm_bcast_filter_attr_offset { - BCAST_FILTER_OFFSET_PAYLOAD_START = 0, - BCAST_FILTER_OFFSET_IP_END = 1, -}; - -/** - * struct iwl_fw_bcast_filter_attr - broadcast filter attribute - * @offset_type: &enum iwl_mvm_bcast_filter_attr_offset. - * @offset: starting offset of this pattern. - * @val: value to match - big endian (MSB is the first - * byte to match from offset pos). - * @mask: mask to match (big endian). - */ -struct iwl_fw_bcast_filter_attr { - u8 offset_type; - u8 offset; - __le16 reserved1; - __be32 val; - __be32 mask; -} __packed; /* BCAST_FILTER_ATT_S_VER_1 */ - -/** - * enum iwl_mvm_bcast_filter_frame_type - filter frame type - * @BCAST_FILTER_FRAME_TYPE_ALL: consider all frames. - * @BCAST_FILTER_FRAME_TYPE_IPV4: consider only ipv4 frames - */ -enum iwl_mvm_bcast_filter_frame_type { - BCAST_FILTER_FRAME_TYPE_ALL = 0, - BCAST_FILTER_FRAME_TYPE_IPV4 = 1, -}; - -/** - * struct iwl_fw_bcast_filter - broadcast filter - * @discard: discard frame (1) or let it pass (0). - * @frame_type: &enum iwl_mvm_bcast_filter_frame_type. - * @num_attrs: number of valid attributes in this filter. - * @attrs: attributes of this filter. a filter is considered matched - * only when all its attributes are matched (i.e. AND relationship) - */ -struct iwl_fw_bcast_filter { - u8 discard; - u8 frame_type; - u8 num_attrs; - u8 reserved1; - struct iwl_fw_bcast_filter_attr attrs[MAX_BCAST_FILTER_ATTRS]; -} __packed; /* BCAST_FILTER_S_VER_1 */ - -/** - * struct iwl_fw_bcast_mac - per-mac broadcast filtering configuration. - * @default_discard: default action for this mac (discard (1) / pass (0)). - * @attached_filters: bitmap of relevant filters for this mac. - */ -struct iwl_fw_bcast_mac { - u8 default_discard; - u8 reserved1; - __le16 attached_filters; -} __packed; /* BCAST_MAC_CONTEXT_S_VER_1 */ - -/** - * struct iwl_bcast_filter_cmd - broadcast filtering configuration - * @disable: enable (0) / disable (1) - * @max_bcast_filters: max number of filters (MAX_BCAST_FILTERS) - * @max_macs: max number of macs (NUM_MAC_INDEX_DRIVER) - * @filters: broadcast filters - * @macs: broadcast filtering configuration per-mac - */ -struct iwl_bcast_filter_cmd { - u8 disable; - u8 max_bcast_filters; - u8 max_macs; - u8 reserved1; - struct iwl_fw_bcast_filter filters[MAX_BCAST_FILTERS]; - struct iwl_fw_bcast_mac macs[NUM_MAC_INDEX_DRIVER]; -} __packed; /* BCAST_FILTERING_HCMD_API_S_VER_1 */ - -/* - * enum iwl_mvm_marker_id - maker ids - * - * The ids for different type of markers to insert into the usniffer logs - */ -enum iwl_mvm_marker_id { - MARKER_ID_TX_FRAME_LATENCY = 1, -}; /* MARKER_ID_API_E_VER_1 */ - -/** - * struct iwl_mvm_marker - mark info into the usniffer logs - * - * (MARKER_CMD = 0xcb) - * - * Mark the UTC time stamp into the usniffer logs together with additional - * metadata, so the usniffer output can be parsed. - * In the command response the ucode will return the GP2 time. - * - * @dw_len: The amount of dwords following this byte including this byte. - * @marker_id: A unique marker id (iwl_mvm_marker_id). - * @reserved: reserved. - * @timestamp: in milliseconds since 1970-01-01 00:00:00 UTC - * @metadata: additional meta data that will be written to the unsiffer log - */ -struct iwl_mvm_marker { - u8 dwLen; - u8 markerId; - __le16 reserved; - __le64 timestamp; - __le32 metadata[0]; -} __packed; /* MARKER_API_S_VER_1 */ - -/* - * enum iwl_dc2dc_config_id - flag ids - * - * Ids of dc2dc configuration flags - */ -enum iwl_dc2dc_config_id { - DCDC_LOW_POWER_MODE_MSK_SET = 0x1, /* not used */ - DCDC_FREQ_TUNE_SET = 0x2, -}; /* MARKER_ID_API_E_VER_1 */ - -/** - * struct iwl_dc2dc_config_cmd - configure dc2dc values - * - * (DC2DC_CONFIG_CMD = 0x83) - * - * Set/Get & configure dc2dc values. - * The command always returns the current dc2dc values. - * - * @flags: set/get dc2dc - * @enable_low_power_mode: not used. - * @dc2dc_freq_tune0: frequency divider - digital domain - * @dc2dc_freq_tune1: frequency divider - analog domain - */ -struct iwl_dc2dc_config_cmd { - __le32 flags; - __le32 enable_low_power_mode; /* not used */ - __le32 dc2dc_freq_tune0; - __le32 dc2dc_freq_tune1; -} __packed; /* DC2DC_CONFIG_CMD_API_S_VER_1 */ - -/** - * struct iwl_dc2dc_config_resp - response for iwl_dc2dc_config_cmd - * - * Current dc2dc values returned by the FW. - * - * @dc2dc_freq_tune0: frequency divider - digital domain - * @dc2dc_freq_tune1: frequency divider - analog domain - */ -struct iwl_dc2dc_config_resp { - __le32 dc2dc_freq_tune0; - __le32 dc2dc_freq_tune1; -} __packed; /* DC2DC_CONFIG_RESP_API_S_VER_1 */ - -/*********************************** - * Smart Fifo API - ***********************************/ -/* Smart Fifo state */ -enum iwl_sf_state { - SF_LONG_DELAY_ON = 0, /* should never be called by driver */ - SF_FULL_ON, - SF_UNINIT, - SF_INIT_OFF, - SF_HW_NUM_STATES -}; - -/* Smart Fifo possible scenario */ -enum iwl_sf_scenario { - SF_SCENARIO_SINGLE_UNICAST, - SF_SCENARIO_AGG_UNICAST, - SF_SCENARIO_MULTICAST, - SF_SCENARIO_BA_RESP, - SF_SCENARIO_TX_RESP, - SF_NUM_SCENARIO -}; - -#define SF_TRANSIENT_STATES_NUMBER 2 /* SF_LONG_DELAY_ON and SF_FULL_ON */ -#define SF_NUM_TIMEOUT_TYPES 2 /* Aging timer and Idle timer */ - -/* smart FIFO default values */ -#define SF_W_MARK_SISO 6144 -#define SF_W_MARK_MIMO2 8192 -#define SF_W_MARK_MIMO3 6144 -#define SF_W_MARK_LEGACY 4096 -#define SF_W_MARK_SCAN 4096 - -/* SF Scenarios timers for default configuration (aligned to 32 uSec) */ -#define SF_SINGLE_UNICAST_IDLE_TIMER_DEF 160 /* 150 uSec */ -#define SF_SINGLE_UNICAST_AGING_TIMER_DEF 400 /* 0.4 mSec */ -#define SF_AGG_UNICAST_IDLE_TIMER_DEF 160 /* 150 uSec */ -#define SF_AGG_UNICAST_AGING_TIMER_DEF 400 /* 0.4 mSec */ -#define SF_MCAST_IDLE_TIMER_DEF 160 /* 150 mSec */ -#define SF_MCAST_AGING_TIMER_DEF 400 /* 0.4 mSec */ -#define SF_BA_IDLE_TIMER_DEF 160 /* 150 uSec */ -#define SF_BA_AGING_TIMER_DEF 400 /* 0.4 mSec */ -#define SF_TX_RE_IDLE_TIMER_DEF 160 /* 150 uSec */ -#define SF_TX_RE_AGING_TIMER_DEF 400 /* 0.4 mSec */ - -/* SF Scenarios timers for BSS MAC configuration (aligned to 32 uSec) */ -#define SF_SINGLE_UNICAST_IDLE_TIMER 320 /* 300 uSec */ -#define SF_SINGLE_UNICAST_AGING_TIMER 2016 /* 2 mSec */ -#define SF_AGG_UNICAST_IDLE_TIMER 320 /* 300 uSec */ -#define SF_AGG_UNICAST_AGING_TIMER 2016 /* 2 mSec */ -#define SF_MCAST_IDLE_TIMER 2016 /* 2 mSec */ -#define SF_MCAST_AGING_TIMER 10016 /* 10 mSec */ -#define SF_BA_IDLE_TIMER 320 /* 300 uSec */ -#define SF_BA_AGING_TIMER 2016 /* 2 mSec */ -#define SF_TX_RE_IDLE_TIMER 320 /* 300 uSec */ -#define SF_TX_RE_AGING_TIMER 2016 /* 2 mSec */ - -#define SF_LONG_DELAY_AGING_TIMER 1000000 /* 1 Sec */ - -#define SF_CFG_DUMMY_NOTIF_OFF BIT(16) - -/** - * Smart Fifo configuration command. - * @state: smart fifo state, types listed in enum %iwl_sf_sate. - * @watermark: Minimum allowed availabe free space in RXF for transient state. - * @long_delay_timeouts: aging and idle timer values for each scenario - * in long delay state. - * @full_on_timeouts: timer values for each scenario in full on state. - */ -struct iwl_sf_cfg_cmd { - __le32 state; - __le32 watermark[SF_TRANSIENT_STATES_NUMBER]; - __le32 long_delay_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES]; - __le32 full_on_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES]; -} __packed; /* SF_CFG_API_S_VER_2 */ - -/*********************************** - * Location Aware Regulatory (LAR) API - MCC updates - ***********************************/ - -/** - * struct iwl_mcc_update_cmd - Request the device to update geographic - * regulatory profile according to the given MCC (Mobile Country Code). - * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain. - * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the - * MCC in the cmd response will be the relevant MCC in the NVM. - * @mcc: given mobile country code - * @source_id: the source from where we got the MCC, see iwl_mcc_source - * @reserved: reserved for alignment - */ -struct iwl_mcc_update_cmd { - __le16 mcc; - u8 source_id; - u8 reserved; -} __packed; /* LAR_UPDATE_MCC_CMD_API_S */ - -/** - * iwl_mcc_update_resp - response to MCC_UPDATE_CMD. - * Contains the new channel control profile map, if changed, and the new MCC - * (mobile country code). - * The new MCC may be different than what was requested in MCC_UPDATE_CMD. - * @status: see &enum iwl_mcc_update_status - * @mcc: the new applied MCC - * @cap: capabilities for all channels which matches the MCC - * @source_id: the MCC source, see iwl_mcc_source - * @n_channels: number of channels in @channels_data (may be 14, 39, 50 or 51 - * channels, depending on platform) - * @channels: channel control data map, DWORD for each channel. Only the first - * 16bits are used. - */ -struct iwl_mcc_update_resp { - __le32 status; - __le16 mcc; - u8 cap; - u8 source_id; - __le32 n_channels; - __le32 channels[0]; -} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S */ - -/** - * struct iwl_mcc_chub_notif - chub notifies of mcc change - * (MCC_CHUB_UPDATE_CMD = 0xc9) - * The Chub (Communication Hub, CommsHUB) is a HW component that connects to - * the cellular and connectivity cores that gets updates of the mcc, and - * notifies the ucode directly of any mcc change. - * The ucode requests the driver to request the device to update geographic - * regulatory profile according to the given MCC (Mobile Country Code). - * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain. - * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the - * MCC in the cmd response will be the relevant MCC in the NVM. - * @mcc: given mobile country code - * @source_id: identity of the change originator, see iwl_mcc_source - * @reserved1: reserved for alignment - */ -struct iwl_mcc_chub_notif { - u16 mcc; - u8 source_id; - u8 reserved1; -} __packed; /* LAR_MCC_NOTIFY_S */ - -enum iwl_mcc_update_status { - MCC_RESP_NEW_CHAN_PROFILE, - MCC_RESP_SAME_CHAN_PROFILE, - MCC_RESP_INVALID, - MCC_RESP_NVM_DISABLED, - MCC_RESP_ILLEGAL, - MCC_RESP_LOW_PRIORITY, -}; - -enum iwl_mcc_source { - MCC_SOURCE_OLD_FW = 0, - MCC_SOURCE_ME = 1, - MCC_SOURCE_BIOS = 2, - MCC_SOURCE_3G_LTE_HOST = 3, - MCC_SOURCE_3G_LTE_DEVICE = 4, - MCC_SOURCE_WIFI = 5, - MCC_SOURCE_RESERVED = 6, - MCC_SOURCE_DEFAULT = 7, - MCC_SOURCE_UNINITIALIZED = 8, - MCC_SOURCE_GET_CURRENT = 0x10 -}; - -/* DTS measurements */ - -enum iwl_dts_measurement_flags { - DTS_TRIGGER_CMD_FLAGS_TEMP = BIT(0), - DTS_TRIGGER_CMD_FLAGS_VOLT = BIT(1), -}; - -/** - * iwl_dts_measurement_cmd - request DTS temperature and/or voltage measurements - * - * @flags: indicates which measurements we want as specified in &enum - * iwl_dts_measurement_flags - */ -struct iwl_dts_measurement_cmd { - __le32 flags; -} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_CMD_S */ - -/** -* enum iwl_dts_control_measurement_mode - DTS measurement type -* @DTS_AUTOMATIC: Automatic mode (full SW control). Provide temperature read -* back (latest value. Not waiting for new value). Use automatic -* SW DTS configuration. -* @DTS_REQUEST_READ: Request DTS read. Configure DTS with manual settings, -* trigger DTS reading and provide read back temperature read -* when available. -* @DTS_OVER_WRITE: over-write the DTS temperatures in the SW until next read -* @DTS_DIRECT_WITHOUT_MEASURE: DTS returns its latest temperature result, -* without measurement trigger. -*/ -enum iwl_dts_control_measurement_mode { - DTS_AUTOMATIC = 0, - DTS_REQUEST_READ = 1, - DTS_OVER_WRITE = 2, - DTS_DIRECT_WITHOUT_MEASURE = 3, -}; - -/** -* enum iwl_dts_used - DTS to use or used for measurement in the DTS request -* @DTS_USE_TOP: Top -* @DTS_USE_CHAIN_A: chain A -* @DTS_USE_CHAIN_B: chain B -* @DTS_USE_CHAIN_C: chain C -* @XTAL_TEMPERATURE - read temperature from xtal -*/ -enum iwl_dts_used { - DTS_USE_TOP = 0, - DTS_USE_CHAIN_A = 1, - DTS_USE_CHAIN_B = 2, - DTS_USE_CHAIN_C = 3, - XTAL_TEMPERATURE = 4, -}; - -/** -* enum iwl_dts_bit_mode - bit-mode to use in DTS request read mode -* @DTS_BIT6_MODE: bit 6 mode -* @DTS_BIT8_MODE: bit 8 mode -*/ -enum iwl_dts_bit_mode { - DTS_BIT6_MODE = 0, - DTS_BIT8_MODE = 1, -}; - -/** - * iwl_ext_dts_measurement_cmd - request extended DTS temperature measurements - * @control_mode: see &enum iwl_dts_control_measurement_mode - * @temperature: used when over write DTS mode is selected - * @sensor: set temperature sensor to use. See &enum iwl_dts_used - * @avg_factor: average factor to DTS in request DTS read mode - * @bit_mode: value defines the DTS bit mode to use. See &enum iwl_dts_bit_mode - * @step_duration: step duration for the DTS - */ -struct iwl_ext_dts_measurement_cmd { - __le32 control_mode; - __le32 temperature; - __le32 sensor; - __le32 avg_factor; - __le32 bit_mode; - __le32 step_duration; -} __packed; /* XVT_FW_DTS_CONTROL_MEASUREMENT_REQUEST_API_S */ - -/** - * iwl_dts_measurement_notif - notification received with the measurements - * - * @temp: the measured temperature - * @voltage: the measured voltage - */ -struct iwl_dts_measurement_notif { - __le32 temp; - __le32 voltage; -} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_NTFY_S */ - -/*********************************** - * TDLS API - ***********************************/ - -/* Type of TDLS request */ -enum iwl_tdls_channel_switch_type { - TDLS_SEND_CHAN_SW_REQ = 0, - TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH, - TDLS_MOVE_CH, -}; /* TDLS_STA_CHANNEL_SWITCH_CMD_TYPE_API_E_VER_1 */ - -/** - * Switch timing sub-element in a TDLS channel-switch command - * @frame_timestamp: GP2 timestamp of channel-switch request/response packet - * received from peer - * @max_offchan_duration: What amount of microseconds out of a DTIM is given - * to the TDLS off-channel communication. For instance if the DTIM is - * 200TU and the TDLS peer is to be given 25% of the time, the value - * given will be 50TU, or 50 * 1024 if translated into microseconds. - * @switch_time: switch time the peer sent in its channel switch timing IE - * @switch_timout: switch timeout the peer sent in its channel switch timing IE - */ -struct iwl_tdls_channel_switch_timing { - __le32 frame_timestamp; /* GP2 time of peer packet Rx */ - __le32 max_offchan_duration; /* given in micro-seconds */ - __le32 switch_time; /* given in micro-seconds */ - __le32 switch_timeout; /* given in micro-seconds */ -} __packed; /* TDLS_STA_CHANNEL_SWITCH_TIMING_DATA_API_S_VER_1 */ - -#define IWL_TDLS_CH_SW_FRAME_MAX_SIZE 200 - -/** - * TDLS channel switch frame template - * - * A template representing a TDLS channel-switch request or response frame - * - * @switch_time_offset: offset to the channel switch timing IE in the template - * @tx_cmd: Tx parameters for the frame - * @data: frame data - */ -struct iwl_tdls_channel_switch_frame { - __le32 switch_time_offset; - struct iwl_tx_cmd tx_cmd; - u8 data[IWL_TDLS_CH_SW_FRAME_MAX_SIZE]; -} __packed; /* TDLS_STA_CHANNEL_SWITCH_FRAME_API_S_VER_1 */ - -/** - * TDLS channel switch command - * - * The command is sent to initiate a channel switch and also in response to - * incoming TDLS channel-switch request/response packets from remote peers. - * - * @switch_type: see &enum iwl_tdls_channel_switch_type - * @peer_sta_id: station id of TDLS peer - * @ci: channel we switch to - * @timing: timing related data for command - * @frame: channel-switch request/response template, depending to switch_type - */ -struct iwl_tdls_channel_switch_cmd { - u8 switch_type; - __le32 peer_sta_id; - struct iwl_fw_channel_info ci; - struct iwl_tdls_channel_switch_timing timing; - struct iwl_tdls_channel_switch_frame frame; -} __packed; /* TDLS_STA_CHANNEL_SWITCH_CMD_API_S_VER_1 */ - -/** - * TDLS channel switch start notification - * - * @status: non-zero on success - * @offchannel_duration: duration given in microseconds - * @sta_id: peer currently performing the channel-switch with - */ -struct iwl_tdls_channel_switch_notif { - __le32 status; - __le32 offchannel_duration; - __le32 sta_id; -} __packed; /* TDLS_STA_CHANNEL_SWITCH_NTFY_API_S_VER_1 */ - -/** - * TDLS station info - * - * @sta_id: station id of the TDLS peer - * @tx_to_peer_tid: TID reserved vs. the peer for FW based Tx - * @tx_to_peer_ssn: initial SSN the FW should use for Tx on its TID vs the peer - * @is_initiator: 1 if the peer is the TDLS link initiator, 0 otherwise - */ -struct iwl_tdls_sta_info { - u8 sta_id; - u8 tx_to_peer_tid; - __le16 tx_to_peer_ssn; - __le32 is_initiator; -} __packed; /* TDLS_STA_INFO_VER_1 */ - -/** - * TDLS basic config command - * - * @id_and_color: MAC id and color being configured - * @tdls_peer_count: amount of currently connected TDLS peers - * @tx_to_ap_tid: TID reverved vs. the AP for FW based Tx - * @tx_to_ap_ssn: initial SSN the FW should use for Tx on its TID vs. the AP - * @sta_info: per-station info. Only the first tdls_peer_count entries are set - * @pti_req_data_offset: offset of network-level data for the PTI template - * @pti_req_tx_cmd: Tx parameters for PTI request template - * @pti_req_template: PTI request template data - */ -struct iwl_tdls_config_cmd { - __le32 id_and_color; /* mac id and color */ - u8 tdls_peer_count; - u8 tx_to_ap_tid; - __le16 tx_to_ap_ssn; - struct iwl_tdls_sta_info sta_info[IWL_MVM_TDLS_STA_COUNT]; - - __le32 pti_req_data_offset; - struct iwl_tx_cmd pti_req_tx_cmd; - u8 pti_req_template[0]; -} __packed; /* TDLS_CONFIG_CMD_API_S_VER_1 */ - -/** - * TDLS per-station config information from FW - * - * @sta_id: station id of the TDLS peer - * @tx_to_peer_last_seq: last sequence number used by FW during FW-based Tx to - * the peer - */ -struct iwl_tdls_config_sta_info_res { - __le16 sta_id; - __le16 tx_to_peer_last_seq; -} __packed; /* TDLS_STA_INFO_RSP_VER_1 */ - -/** - * TDLS config information from FW - * - * @tx_to_ap_last_seq: last sequence number used by FW during FW-based Tx to AP - * @sta_info: per-station TDLS config information - */ -struct iwl_tdls_config_res { - __le32 tx_to_ap_last_seq; - struct iwl_tdls_config_sta_info_res sta_info[IWL_MVM_TDLS_STA_COUNT]; -} __packed; /* TDLS_CONFIG_RSP_API_S_VER_1 */ - -#define TX_FIFO_MAX_NUM 8 -#define RX_FIFO_MAX_NUM 2 - -/** - * Shared memory configuration information from the FW - * - * @shared_mem_addr: shared memory addr (pre 8000 HW set to 0x0 as MARBH is not - * accessible) - * @shared_mem_size: shared memory size - * @sample_buff_addr: internal sample (mon/adc) buff addr (pre 8000 HW set to - * 0x0 as accessible only via DBGM RDAT) - * @sample_buff_size: internal sample buff size - * @txfifo_addr: start addr of TXF0 (excluding the context table 0.5KB), (pre - * 8000 HW set to 0x0 as not accessible) - * @txfifo_size: size of TXF0 ... TXF7 - * @rxfifo_size: RXF1, RXF2 sizes. If there is no RXF2, it'll have a value of 0 - * @page_buff_addr: used by UMAC and performance debug (page miss analysis), - * when paging is not supported this should be 0 - * @page_buff_size: size of %page_buff_addr - */ -struct iwl_shared_mem_cfg { - __le32 shared_mem_addr; - __le32 shared_mem_size; - __le32 sample_buff_addr; - __le32 sample_buff_size; - __le32 txfifo_addr; - __le32 txfifo_size[TX_FIFO_MAX_NUM]; - __le32 rxfifo_size[RX_FIFO_MAX_NUM]; - __le32 page_buff_addr; - __le32 page_buff_size; -} __packed; /* SHARED_MEM_ALLOC_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 deleted file mode 100644 index d906fa13b..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ /dev/null @@ -1,1166 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * 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 "iwl-trans.h" -#include "iwl-op-mode.h" -#include "iwl-fw.h" -#include "iwl-debug.h" -#include "iwl-csr.h" /* for iwl_mvm_rx_card_state_notif */ -#include "iwl-io.h" /* for iwl_mvm_rx_card_state_notif */ -#include "iwl-prph.h" -#include "iwl-eeprom-parse.h" - -#include "mvm.h" -#include "iwl-phy-db.h" - -#define MVM_UCODE_ALIVE_TIMEOUT HZ -#define MVM_UCODE_CALIB_TIMEOUT (2*HZ) - -#define UCODE_VALID_OK cpu_to_le32(0x1) - -struct iwl_mvm_alive_data { - bool valid; - u32 scd_base_addr; -}; - -static inline const struct fw_img * -iwl_get_ucode_image(struct iwl_mvm *mvm, enum iwl_ucode_type ucode_type) -{ - if (ucode_type >= IWL_UCODE_TYPE_MAX) - return NULL; - - return &mvm->fw->img[ucode_type]; -} - -static int iwl_send_tx_ant_cfg(struct iwl_mvm *mvm, u8 valid_tx_ant) -{ - struct iwl_tx_ant_cfg_cmd tx_ant_cmd = { - .valid = cpu_to_le32(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, 0, - sizeof(tx_ant_cmd), &tx_ant_cmd); -} - -static void iwl_free_fw_paging(struct iwl_mvm *mvm) -{ - int i; - - if (!mvm->fw_paging_db[0].fw_paging_block) - return; - - for (i = 0; i < NUM_OF_FW_PAGING_BLOCKS; i++) { - if (!mvm->fw_paging_db[i].fw_paging_block) { - IWL_DEBUG_FW(mvm, - "Paging: block %d already freed, continue to next page\n", - i); - - continue; - } - - __free_pages(mvm->fw_paging_db[i].fw_paging_block, - get_order(mvm->fw_paging_db[i].fw_paging_size)); - } - kfree(mvm->trans->paging_download_buf); - memset(mvm->fw_paging_db, 0, sizeof(mvm->fw_paging_db)); -} - -static int iwl_fill_paging_mem(struct iwl_mvm *mvm, const struct fw_img *image) -{ - int sec_idx, idx; - u32 offset = 0; - - /* - * find where is the paging image start point: - * if CPU2 exist and it's in paging format, then the image looks like: - * CPU1 sections (2 or more) - * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between CPU1 to CPU2 - * CPU2 sections (not paged) - * PAGING_SEPARATOR_SECTION delimiter - separate between CPU2 - * non paged to CPU2 paging sec - * CPU2 paging CSS - * CPU2 paging image (including instruction and data) - */ - for (sec_idx = 0; sec_idx < IWL_UCODE_SECTION_MAX; sec_idx++) { - if (image->sec[sec_idx].offset == PAGING_SEPARATOR_SECTION) { - sec_idx++; - break; - } - } - - if (sec_idx >= IWL_UCODE_SECTION_MAX) { - IWL_ERR(mvm, "driver didn't find paging image\n"); - iwl_free_fw_paging(mvm); - return -EINVAL; - } - - /* copy the CSS block to the dram */ - IWL_DEBUG_FW(mvm, "Paging: load paging CSS to FW, sec = %d\n", - sec_idx); - - memcpy(page_address(mvm->fw_paging_db[0].fw_paging_block), - image->sec[sec_idx].data, - mvm->fw_paging_db[0].fw_paging_size); - - IWL_DEBUG_FW(mvm, - "Paging: copied %d CSS bytes to first block\n", - mvm->fw_paging_db[0].fw_paging_size); - - sec_idx++; - - /* - * copy the paging blocks to the dram - * loop index start from 1 since that CSS block already copied to dram - * and CSS index is 0. - * loop stop at num_of_paging_blk since that last block is not full. - */ - for (idx = 1; idx < mvm->num_of_paging_blk; idx++) { - memcpy(page_address(mvm->fw_paging_db[idx].fw_paging_block), - image->sec[sec_idx].data + offset, - mvm->fw_paging_db[idx].fw_paging_size); - - IWL_DEBUG_FW(mvm, - "Paging: copied %d paging bytes to block %d\n", - mvm->fw_paging_db[idx].fw_paging_size, - idx); - - offset += mvm->fw_paging_db[idx].fw_paging_size; - } - - /* copy the last paging block */ - if (mvm->num_of_pages_in_last_blk > 0) { - memcpy(page_address(mvm->fw_paging_db[idx].fw_paging_block), - image->sec[sec_idx].data + offset, - FW_PAGING_SIZE * mvm->num_of_pages_in_last_blk); - - IWL_DEBUG_FW(mvm, - "Paging: copied %d pages in the last block %d\n", - mvm->num_of_pages_in_last_blk, idx); - } - - return 0; -} - -static int iwl_alloc_fw_paging_mem(struct iwl_mvm *mvm, - const struct fw_img *image) -{ - struct page *block; - dma_addr_t phys = 0; - int blk_idx = 0; - int order, num_of_pages; - int dma_enabled; - - if (mvm->fw_paging_db[0].fw_paging_block) - return 0; - - dma_enabled = is_device_dma_capable(mvm->trans->dev); - - /* ensure BLOCK_2_EXP_SIZE is power of 2 of PAGING_BLOCK_SIZE */ - BUILD_BUG_ON(BIT(BLOCK_2_EXP_SIZE) != PAGING_BLOCK_SIZE); - - num_of_pages = image->paging_mem_size / FW_PAGING_SIZE; - mvm->num_of_paging_blk = ((num_of_pages - 1) / - NUM_OF_PAGE_PER_GROUP) + 1; - - mvm->num_of_pages_in_last_blk = - num_of_pages - - NUM_OF_PAGE_PER_GROUP * (mvm->num_of_paging_blk - 1); - - IWL_DEBUG_FW(mvm, - "Paging: allocating mem for %d paging blocks, each block holds 8 pages, last block holds %d pages\n", - mvm->num_of_paging_blk, - mvm->num_of_pages_in_last_blk); - - /* allocate block of 4Kbytes for paging CSS */ - order = get_order(FW_PAGING_SIZE); - block = alloc_pages(GFP_KERNEL, order); - if (!block) { - /* free all the previous pages since we failed */ - iwl_free_fw_paging(mvm); - return -ENOMEM; - } - - mvm->fw_paging_db[blk_idx].fw_paging_block = block; - mvm->fw_paging_db[blk_idx].fw_paging_size = FW_PAGING_SIZE; - - if (dma_enabled) { - phys = dma_map_page(mvm->trans->dev, block, 0, - PAGE_SIZE << order, DMA_BIDIRECTIONAL); - if (dma_mapping_error(mvm->trans->dev, phys)) { - /* - * free the previous pages and the current one since - * we failed to map_page. - */ - iwl_free_fw_paging(mvm); - return -ENOMEM; - } - mvm->fw_paging_db[blk_idx].fw_paging_phys = phys; - } else { - mvm->fw_paging_db[blk_idx].fw_paging_phys = PAGING_ADDR_SIG | - blk_idx << BLOCK_2_EXP_SIZE; - } - - IWL_DEBUG_FW(mvm, - "Paging: allocated 4K(CSS) bytes (order %d) for firmware paging.\n", - order); - - /* - * allocate blocks in dram. - * since that CSS allocated in fw_paging_db[0] loop start from index 1 - */ - for (blk_idx = 1; blk_idx < mvm->num_of_paging_blk + 1; blk_idx++) { - /* allocate block of PAGING_BLOCK_SIZE (32K) */ - order = get_order(PAGING_BLOCK_SIZE); - block = alloc_pages(GFP_KERNEL, order); - if (!block) { - /* free all the previous pages since we failed */ - iwl_free_fw_paging(mvm); - return -ENOMEM; - } - - mvm->fw_paging_db[blk_idx].fw_paging_block = block; - mvm->fw_paging_db[blk_idx].fw_paging_size = PAGING_BLOCK_SIZE; - - if (dma_enabled) { - phys = dma_map_page(mvm->trans->dev, block, 0, - PAGE_SIZE << order, - DMA_BIDIRECTIONAL); - if (dma_mapping_error(mvm->trans->dev, phys)) { - /* - * free the previous pages and the current one - * since we failed to map_page. - */ - iwl_free_fw_paging(mvm); - return -ENOMEM; - } - mvm->fw_paging_db[blk_idx].fw_paging_phys = phys; - } else { - mvm->fw_paging_db[blk_idx].fw_paging_phys = - PAGING_ADDR_SIG | - blk_idx << BLOCK_2_EXP_SIZE; - } - - IWL_DEBUG_FW(mvm, - "Paging: allocated 32K bytes (order %d) for firmware paging.\n", - order); - } - - return 0; -} - -static int iwl_save_fw_paging(struct iwl_mvm *mvm, - const struct fw_img *fw) -{ - int ret; - - ret = iwl_alloc_fw_paging_mem(mvm, fw); - if (ret) - return ret; - - return iwl_fill_paging_mem(mvm, fw); -} - -/* send paging cmd to FW in case CPU2 has paging image */ -static int iwl_send_paging_cmd(struct iwl_mvm *mvm, const struct fw_img *fw) -{ - int blk_idx; - __le32 dev_phy_addr; - struct iwl_fw_paging_cmd fw_paging_cmd = { - .flags = - cpu_to_le32(PAGING_CMD_IS_SECURED | - PAGING_CMD_IS_ENABLED | - (mvm->num_of_pages_in_last_blk << - PAGING_CMD_NUM_OF_PAGES_IN_LAST_GRP_POS)), - .block_size = cpu_to_le32(BLOCK_2_EXP_SIZE), - .block_num = cpu_to_le32(mvm->num_of_paging_blk), - }; - - /* loop for for all paging blocks + CSS block */ - for (blk_idx = 0; blk_idx < mvm->num_of_paging_blk + 1; blk_idx++) { - dev_phy_addr = - cpu_to_le32(mvm->fw_paging_db[blk_idx].fw_paging_phys >> - PAGE_2_EXP_SIZE); - fw_paging_cmd.device_phy_addr[blk_idx] = dev_phy_addr; - } - - return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(FW_PAGING_BLOCK_CMD, - IWL_ALWAYS_LONG_GROUP, 0), - 0, sizeof(fw_paging_cmd), &fw_paging_cmd); -} - -/* - * Send paging item cmd to FW in case CPU2 has paging image - */ -static int iwl_trans_get_paging_item(struct iwl_mvm *mvm) -{ - int ret; - struct iwl_fw_get_item_cmd fw_get_item_cmd = { - .item_id = cpu_to_le32(IWL_FW_ITEM_ID_PAGING), - }; - - struct iwl_fw_get_item_resp *item_resp; - struct iwl_host_cmd cmd = { - .id = iwl_cmd_id(FW_GET_ITEM_CMD, IWL_ALWAYS_LONG_GROUP, 0), - .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL, - .data = { &fw_get_item_cmd, }, - }; - - cmd.len[0] = sizeof(struct iwl_fw_get_item_cmd); - - ret = iwl_mvm_send_cmd(mvm, &cmd); - if (ret) { - IWL_ERR(mvm, - "Paging: Failed to send FW_GET_ITEM_CMD cmd (err = %d)\n", - ret); - return ret; - } - - item_resp = (void *)((struct iwl_rx_packet *)cmd.resp_pkt)->data; - if (item_resp->item_id != cpu_to_le32(IWL_FW_ITEM_ID_PAGING)) { - IWL_ERR(mvm, - "Paging: got wrong item in FW_GET_ITEM_CMD resp (item_id = %u)\n", - le32_to_cpu(item_resp->item_id)); - ret = -EIO; - goto exit; - } - - mvm->trans->paging_download_buf = kzalloc(MAX_PAGING_IMAGE_SIZE, - GFP_KERNEL); - if (!mvm->trans->paging_download_buf) { - ret = -ENOMEM; - goto exit; - } - mvm->trans->paging_req_addr = le32_to_cpu(item_resp->item_val); - mvm->trans->paging_db = mvm->fw_paging_db; - IWL_DEBUG_FW(mvm, - "Paging: got paging request address (paging_req_addr 0x%08x)\n", - mvm->trans->paging_req_addr); - -exit: - iwl_free_resp(&cmd); - - return ret; -} - -static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, - struct iwl_rx_packet *pkt, void *data) -{ - struct iwl_mvm *mvm = - container_of(notif_wait, struct iwl_mvm, notif_wait); - struct iwl_mvm_alive_data *alive_data = data; - struct mvm_alive_resp_ver1 *palive1; - struct mvm_alive_resp_ver2 *palive2; - struct mvm_alive_resp *palive; - - if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive1)) { - palive1 = (void *)pkt->data; - - mvm->support_umac_log = false; - mvm->error_event_table = - le32_to_cpu(palive1->error_event_table_ptr); - mvm->log_event_table = - le32_to_cpu(palive1->log_event_table_ptr); - alive_data->scd_base_addr = le32_to_cpu(palive1->scd_base_ptr); - - alive_data->valid = le16_to_cpu(palive1->status) == - IWL_ALIVE_STATUS_OK; - IWL_DEBUG_FW(mvm, - "Alive VER1 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n", - le16_to_cpu(palive1->status), palive1->ver_type, - palive1->ver_subtype, palive1->flags); - } else if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive2)) { - palive2 = (void *)pkt->data; - - mvm->error_event_table = - le32_to_cpu(palive2->error_event_table_ptr); - mvm->log_event_table = - le32_to_cpu(palive2->log_event_table_ptr); - alive_data->scd_base_addr = le32_to_cpu(palive2->scd_base_ptr); - mvm->umac_error_event_table = - le32_to_cpu(palive2->error_info_addr); - mvm->sf_space.addr = le32_to_cpu(palive2->st_fwrd_addr); - mvm->sf_space.size = le32_to_cpu(palive2->st_fwrd_size); - - alive_data->valid = le16_to_cpu(palive2->status) == - IWL_ALIVE_STATUS_OK; - if (mvm->umac_error_event_table) - mvm->support_umac_log = true; - - IWL_DEBUG_FW(mvm, - "Alive VER2 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n", - le16_to_cpu(palive2->status), palive2->ver_type, - palive2->ver_subtype, palive2->flags); - - IWL_DEBUG_FW(mvm, - "UMAC version: Major - 0x%x, Minor - 0x%x\n", - palive2->umac_major, palive2->umac_minor); - } else if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive)) { - palive = (void *)pkt->data; - - mvm->error_event_table = - le32_to_cpu(palive->error_event_table_ptr); - mvm->log_event_table = - le32_to_cpu(palive->log_event_table_ptr); - alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr); - mvm->umac_error_event_table = - le32_to_cpu(palive->error_info_addr); - mvm->sf_space.addr = le32_to_cpu(palive->st_fwrd_addr); - mvm->sf_space.size = le32_to_cpu(palive->st_fwrd_size); - - alive_data->valid = le16_to_cpu(palive->status) == - IWL_ALIVE_STATUS_OK; - if (mvm->umac_error_event_table) - mvm->support_umac_log = true; - - IWL_DEBUG_FW(mvm, - "Alive VER3 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->flags); - - IWL_DEBUG_FW(mvm, - "UMAC version: Major - 0x%x, Minor - 0x%x\n", - le32_to_cpu(palive->umac_major), - le32_to_cpu(palive->umac_minor)); - } - - return true; -} - -static bool iwl_wait_phy_db_entry(struct iwl_notif_wait_data *notif_wait, - struct iwl_rx_packet *pkt, void *data) -{ - struct iwl_phy_db *phy_db = data; - - if (pkt->hdr.cmd != CALIB_RES_NOTIF_PHY_DB) { - WARN_ON(pkt->hdr.cmd != INIT_COMPLETE_NOTIF); - return true; - } - - WARN_ON(iwl_phy_db_set_section(phy_db, pkt, GFP_ATOMIC)); - - return false; -} - -static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, - enum iwl_ucode_type ucode_type) -{ - struct iwl_notification_wait alive_wait; - struct iwl_mvm_alive_data alive_data; - const struct fw_img *fw; - int ret, i; - enum iwl_ucode_type old_type = mvm->cur_ucode; - static const u16 alive_cmd[] = { MVM_ALIVE }; - struct iwl_sf_region st_fwrd_space; - - if (ucode_type == IWL_UCODE_REGULAR && - iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_START_FROM_ALIVE)) - fw = iwl_get_ucode_image(mvm, IWL_UCODE_REGULAR_USNIFFER); - else - fw = iwl_get_ucode_image(mvm, ucode_type); - if (WARN_ON(!fw)) - return -EINVAL; - mvm->cur_ucode = ucode_type; - mvm->ucode_loaded = false; - - iwl_init_notification_wait(&mvm->notif_wait, &alive_wait, - alive_cmd, ARRAY_SIZE(alive_cmd), - iwl_alive_fn, &alive_data); - - ret = iwl_trans_start_fw(mvm->trans, fw, ucode_type == IWL_UCODE_INIT); - if (ret) { - mvm->cur_ucode = old_type; - iwl_remove_notification(&mvm->notif_wait, &alive_wait); - return ret; - } - - /* - * Some things may run in the background now, but we - * just wait for the ALIVE notification here. - */ - ret = iwl_wait_notification(&mvm->notif_wait, &alive_wait, - MVM_UCODE_ALIVE_TIMEOUT); - if (ret) { - if (mvm->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) - IWL_ERR(mvm, - "SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n", - iwl_read_prph(mvm->trans, SB_CPU_1_STATUS), - iwl_read_prph(mvm->trans, SB_CPU_2_STATUS)); - mvm->cur_ucode = old_type; - return ret; - } - - if (!alive_data.valid) { - IWL_ERR(mvm, "Loaded ucode is not valid!\n"); - mvm->cur_ucode = old_type; - return -EIO; - } - - /* - * update the sdio allocation according to the pointer we get in the - * alive notification. - */ - st_fwrd_space.addr = mvm->sf_space.addr; - st_fwrd_space.size = mvm->sf_space.size; - ret = iwl_trans_update_sf(mvm->trans, &st_fwrd_space); - if (ret) { - IWL_ERR(mvm, "Failed to update SF size. ret %d\n", ret); - return ret; - } - - iwl_trans_fw_alive(mvm->trans, alive_data.scd_base_addr); - - /* - * configure and operate fw paging mechanism. - * driver configures the paging flow only once, CPU2 paging image - * included in the IWL_UCODE_INIT image. - */ - if (fw->paging_mem_size) { - /* - * When dma is not enabled, the driver needs to copy / write - * the downloaded / uploaded page to / from the smem. - * This gets the location of the place were the pages are - * stored. - */ - if (!is_device_dma_capable(mvm->trans->dev)) { - ret = iwl_trans_get_paging_item(mvm); - if (ret) { - IWL_ERR(mvm, "failed to get FW paging item\n"); - return ret; - } - } - - ret = iwl_save_fw_paging(mvm, fw); - if (ret) { - IWL_ERR(mvm, "failed to save the FW paging image\n"); - return ret; - } - - ret = iwl_send_paging_cmd(mvm, fw); - if (ret) { - IWL_ERR(mvm, "failed to send the paging cmd\n"); - iwl_free_fw_paging(mvm); - return ret; - } - } - - /* - * Note: all the queues are enabled as part of the interface - * initialization, but in firmware restart scenarios they - * could be stopped, so wake them up. In firmware restart, - * mac80211 will have the queues stopped as well until the - * reconfiguration completes. During normal startup, they - * will be empty. - */ - - memset(&mvm->queue_info, 0, sizeof(mvm->queue_info)); - mvm->queue_info[IWL_MVM_CMD_QUEUE].hw_queue_refcount = 1; - - for (i = 0; i < IEEE80211_MAX_QUEUES; i++) - atomic_set(&mvm->mac80211_queue_stop_count[i], 0); - - mvm->ucode_loaded = true; - - return 0; -} - -static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm) -{ - struct iwl_phy_cfg_cmd phy_cfg_cmd; - enum iwl_ucode_type ucode_type = mvm->cur_ucode; - - /* Set parameters */ - phy_cfg_cmd.phy_cfg = cpu_to_le32(iwl_mvm_get_phy_config(mvm)); - phy_cfg_cmd.calib_control.event_trigger = - mvm->fw->default_calib[ucode_type].event_trigger; - phy_cfg_cmd.calib_control.flow_trigger = - mvm->fw->default_calib[ucode_type].flow_trigger; - - IWL_DEBUG_INFO(mvm, "Sending Phy CFG command: 0x%x\n", - phy_cfg_cmd.phy_cfg); - - return iwl_mvm_send_cmd_pdu(mvm, PHY_CONFIGURATION_CMD, 0, - sizeof(phy_cfg_cmd), &phy_cfg_cmd); -} - -int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) -{ - struct iwl_notification_wait calib_wait; - static const u16 init_complete[] = { - INIT_COMPLETE_NOTIF, - CALIB_RES_NOTIF_PHY_DB - }; - int ret; - - lockdep_assert_held(&mvm->mutex); - - if (WARN_ON_ONCE(mvm->calibrating)) - return 0; - - iwl_init_notification_wait(&mvm->notif_wait, - &calib_wait, - init_complete, - ARRAY_SIZE(init_complete), - iwl_wait_phy_db_entry, - mvm->phy_db); - - /* Will also start the device */ - ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_INIT); - if (ret) { - IWL_ERR(mvm, "Failed to start INIT ucode: %d\n", ret); - goto error; - } - - ret = iwl_send_bt_init_conf(mvm); - if (ret) - goto error; - - /* Read the NVM only at driver load time, no need to do this twice */ - if (read_nvm) { - /* Read nvm */ - ret = iwl_nvm_init(mvm, true); - if (ret) { - IWL_ERR(mvm, "Failed to read NVM: %d\n", ret); - goto error; - } - } - - /* In case we read the NVM from external file, load it to the NIC */ - if (mvm->nvm_file_name) - iwl_mvm_load_nvm_to_nic(mvm); - - ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans); - WARN_ON(ret); - - /* - * abort after reading the nvm in case RF Kill is on, we will complete - * the init seq later when RF kill will switch to off - */ - if (iwl_mvm_is_radio_hw_killed(mvm)) { - IWL_DEBUG_RF_KILL(mvm, - "jump over all phy activities due to RF kill\n"); - iwl_remove_notification(&mvm->notif_wait, &calib_wait); - ret = 1; - goto out; - } - - mvm->calibrating = true; - - /* Send TX valid antennas before triggering calibrations */ - ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm)); - if (ret) - goto error; - - /* - * Send phy configurations command to init uCode - * to start the 16.0 uCode init image internal calibrations. - */ - ret = iwl_send_phy_cfg_cmd(mvm); - if (ret) { - IWL_ERR(mvm, "Failed to run INIT calibrations: %d\n", - ret); - goto error; - } - - /* - * Some things may run in the background now, but we - * just wait for the calibration complete notification. - */ - ret = iwl_wait_notification(&mvm->notif_wait, &calib_wait, - MVM_UCODE_CALIB_TIMEOUT); - - if (ret && iwl_mvm_is_radio_hw_killed(mvm)) { - IWL_DEBUG_RF_KILL(mvm, "RFKILL while calibrating.\n"); - ret = 1; - } - goto out; - -error: - iwl_remove_notification(&mvm->notif_wait, &calib_wait); -out: - mvm->calibrating = false; - if (iwlmvm_mod_params.init_dbg && !mvm->nvm_data) { - /* we want to debug INIT and we have no NVM - fake */ - mvm->nvm_data = kzalloc(sizeof(struct iwl_nvm_data) + - sizeof(struct ieee80211_channel) + - sizeof(struct ieee80211_rate), - GFP_KERNEL); - if (!mvm->nvm_data) - return -ENOMEM; - mvm->nvm_data->bands[0].channels = mvm->nvm_data->channels; - mvm->nvm_data->bands[0].n_channels = 1; - mvm->nvm_data->bands[0].n_bitrates = 1; - mvm->nvm_data->bands[0].bitrates = - (void *)mvm->nvm_data->channels + 1; - mvm->nvm_data->bands[0].bitrates->hw_value = 10; - } - - return ret; -} - -static void iwl_mvm_get_shared_mem_conf(struct iwl_mvm *mvm) -{ - struct iwl_host_cmd cmd = { - .id = SHARED_MEM_CFG, - .flags = CMD_WANT_SKB, - .data = { NULL, }, - .len = { 0, }, - }; - struct iwl_rx_packet *pkt; - struct iwl_shared_mem_cfg *mem_cfg; - u32 i; - - lockdep_assert_held(&mvm->mutex); - - if (WARN_ON(iwl_mvm_send_cmd(mvm, &cmd))) - return; - - pkt = cmd.resp_pkt; - mem_cfg = (void *)pkt->data; - - mvm->shared_mem_cfg.shared_mem_addr = - le32_to_cpu(mem_cfg->shared_mem_addr); - mvm->shared_mem_cfg.shared_mem_size = - le32_to_cpu(mem_cfg->shared_mem_size); - mvm->shared_mem_cfg.sample_buff_addr = - le32_to_cpu(mem_cfg->sample_buff_addr); - mvm->shared_mem_cfg.sample_buff_size = - le32_to_cpu(mem_cfg->sample_buff_size); - mvm->shared_mem_cfg.txfifo_addr = le32_to_cpu(mem_cfg->txfifo_addr); - for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++) - mvm->shared_mem_cfg.txfifo_size[i] = - le32_to_cpu(mem_cfg->txfifo_size[i]); - for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++) - mvm->shared_mem_cfg.rxfifo_size[i] = - le32_to_cpu(mem_cfg->rxfifo_size[i]); - mvm->shared_mem_cfg.page_buff_addr = - le32_to_cpu(mem_cfg->page_buff_addr); - mvm->shared_mem_cfg.page_buff_size = - le32_to_cpu(mem_cfg->page_buff_size); - IWL_DEBUG_INFO(mvm, "SHARED MEM CFG: got memory offsets/sizes\n"); - - iwl_free_resp(&cmd); -} - -int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, - struct iwl_mvm_dump_desc *desc, - struct iwl_fw_dbg_trigger_tlv *trigger) -{ - unsigned int delay = 0; - - if (trigger) - delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay)); - - if (test_and_set_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status)) - return -EBUSY; - - if (WARN_ON(mvm->fw_dump_desc)) - iwl_mvm_free_fw_dump_desc(mvm); - - IWL_WARN(mvm, "Collecting data: trigger %d fired.\n", - le32_to_cpu(desc->trig_desc.type)); - - mvm->fw_dump_desc = desc; - mvm->fw_dump_trig = trigger; - - queue_delayed_work(system_wq, &mvm->fw_dump_wk, delay); - - return 0; -} - -int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig, - const char *str, size_t len, - struct iwl_fw_dbg_trigger_tlv *trigger) -{ - struct iwl_mvm_dump_desc *desc; - - desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC); - if (!desc) - return -ENOMEM; - - desc->len = len; - desc->trig_desc.type = cpu_to_le32(trig); - memcpy(desc->trig_desc.data, str, len); - - return iwl_mvm_fw_dbg_collect_desc(mvm, desc, trigger); -} - -int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm, - struct iwl_fw_dbg_trigger_tlv *trigger, - const char *fmt, ...) -{ - u16 occurrences = le16_to_cpu(trigger->occurrences); - int ret, len = 0; - char buf[64]; - - if (!occurrences) - return 0; - - if (fmt) { - va_list ap; - - buf[sizeof(buf) - 1] = '\0'; - - va_start(ap, fmt); - vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - - /* check for truncation */ - if (WARN_ON_ONCE(buf[sizeof(buf) - 1])) - buf[sizeof(buf) - 1] = '\0'; - - len = strlen(buf) + 1; - } - - ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), buf, len, - trigger); - - if (ret) - return ret; - - trigger->occurrences = cpu_to_le16(occurrences - 1); - return 0; -} - -static inline void iwl_mvm_restart_early_start(struct iwl_mvm *mvm) -{ - if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) - iwl_clear_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100); - else - iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 1); -} - -int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id) -{ - u8 *ptr; - int ret; - int i; - - if (WARN_ONCE(conf_id >= ARRAY_SIZE(mvm->fw->dbg_conf_tlv), - "Invalid configuration %d\n", conf_id)) - return -EINVAL; - - /* EARLY START - firmware's configuration is hard coded */ - if ((!mvm->fw->dbg_conf_tlv[conf_id] || - !mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds) && - conf_id == FW_DBG_START_FROM_ALIVE) { - iwl_mvm_restart_early_start(mvm); - return 0; - } - - if (!mvm->fw->dbg_conf_tlv[conf_id]) - return -EINVAL; - - if (mvm->fw_dbg_conf != FW_DBG_INVALID) - IWL_WARN(mvm, "FW already configured (%d) - re-configuring\n", - mvm->fw_dbg_conf); - - /* Send all HCMDs for configuring the FW debug */ - ptr = (void *)&mvm->fw->dbg_conf_tlv[conf_id]->hcmd; - for (i = 0; i < mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds; i++) { - struct iwl_fw_dbg_conf_hcmd *cmd = (void *)ptr; - - ret = iwl_mvm_send_cmd_pdu(mvm, cmd->id, 0, - le16_to_cpu(cmd->len), cmd->data); - if (ret) - return ret; - - ptr += sizeof(*cmd); - ptr += le16_to_cpu(cmd->len); - } - - mvm->fw_dbg_conf = conf_id; - return ret; -} - -static int iwl_mvm_config_ltr(struct iwl_mvm *mvm) -{ - struct iwl_ltr_config_cmd cmd = { - .flags = cpu_to_le32(LTR_CFG_FLAG_FEATURE_ENABLE), - }; - - if (!mvm->trans->ltr_enabled) - return 0; - - return iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0, - sizeof(cmd), &cmd); -} - -int iwl_mvm_up(struct iwl_mvm *mvm) -{ - int ret, i; - struct ieee80211_channel *chan; - struct cfg80211_chan_def chandef; - - lockdep_assert_held(&mvm->mutex); - - ret = iwl_trans_start_hw(mvm->trans); - if (ret) - return ret; - - /* - * If we haven't completed the run of the init ucode during - * module loading, load init ucode now - * (for example, if we were in RFKILL) - */ - ret = iwl_run_init_mvm_ucode(mvm, false); - if (ret && !iwlmvm_mod_params.init_dbg) { - IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", ret); - /* this can't happen */ - if (WARN_ON(ret > 0)) - ret = -ERFKILL; - goto error; - } - if (!iwlmvm_mod_params.init_dbg) { - /* - * Stop and start the transport without entering low power - * mode. This will save the state of other components on the - * device that are triggered by the INIT firwmare (MFUART). - */ - _iwl_trans_stop_device(mvm->trans, false); - ret = _iwl_trans_start_hw(mvm->trans, false); - if (ret) - goto error; - } - - if (iwlmvm_mod_params.init_dbg) - return 0; - - ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR); - if (ret) { - IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret); - goto error; - } - - iwl_mvm_get_shared_mem_conf(mvm); - - ret = iwl_mvm_sf_update(mvm, NULL, false); - if (ret) - IWL_ERR(mvm, "Failed to initialize Smart Fifo\n"); - - mvm->fw_dbg_conf = FW_DBG_INVALID; - /* if we have a destination, assume EARLY START */ - if (mvm->fw->dbg_dest_tlv) - mvm->fw_dbg_conf = FW_DBG_START_FROM_ALIVE; - iwl_mvm_start_fw_dbg_conf(mvm, FW_DBG_START_FROM_ALIVE); - - ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm)); - if (ret) - goto error; - - ret = iwl_send_bt_init_conf(mvm); - if (ret) - goto error; - - /* Send phy db control command and then phy db calibration*/ - ret = iwl_send_phy_db_data(mvm->phy_db); - if (ret) - goto error; - - ret = iwl_send_phy_cfg_cmd(mvm); - if (ret) - goto error; - - /* init the fw <-> mac80211 STA mapping */ - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) - RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL); - - mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT; - - /* reset quota debouncing buffer - 0xff will yield invalid data */ - memset(&mvm->last_quota_cmd, 0xff, sizeof(mvm->last_quota_cmd)); - - /* Add auxiliary station for scanning */ - ret = iwl_mvm_add_aux_sta(mvm); - if (ret) - goto error; - - /* Add all the PHY contexts */ - chan = &mvm->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0]; - cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); - for (i = 0; i < NUM_PHY_CTX; i++) { - /* - * The channel used here isn't relevant as it's - * going to be overwritten in the other flows. - * For now use the first channel we have. - */ - ret = iwl_mvm_phy_ctxt_add(mvm, &mvm->phy_ctxts[i], - &chandef, 1, 1); - if (ret) - goto error; - } - - /* Initialize tx backoffs to the minimal possible */ - iwl_mvm_tt_tx_backoff(mvm, 0); - - WARN_ON(iwl_mvm_config_ltr(mvm)); - - ret = iwl_mvm_power_update_device(mvm); - if (ret) - goto error; - - /* - * RTNL is not taken during Ct-kill, but we don't need to scan/Tx - * anyway, so don't init MCC. - */ - if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) { - ret = iwl_mvm_init_mcc(mvm); - if (ret) - goto error; - } - - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { - ret = iwl_mvm_config_scan(mvm); - if (ret) - goto error; - } - - if (iwl_mvm_is_csum_supported(mvm) && - mvm->cfg->features & NETIF_F_RXCSUM) - iwl_trans_write_prph(mvm->trans, RX_EN_CSUM, 0x3); - - /* allow FW/transport low power modes if not during restart */ - if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) - iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN); - - IWL_DEBUG_INFO(mvm, "RT uCode started.\n"); - return 0; - error: - iwl_trans_stop_device(mvm->trans); - return ret; -} - -int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm) -{ - int ret, i; - - lockdep_assert_held(&mvm->mutex); - - ret = iwl_trans_start_hw(mvm->trans); - if (ret) - return ret; - - ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_WOWLAN); - if (ret) { - IWL_ERR(mvm, "Failed to start WoWLAN firmware: %d\n", ret); - goto error; - } - - ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm)); - if (ret) - goto error; - - /* Send phy db control command and then phy db calibration*/ - ret = iwl_send_phy_db_data(mvm->phy_db); - if (ret) - goto error; - - ret = iwl_send_phy_cfg_cmd(mvm); - if (ret) - goto error; - - /* init the fw <-> mac80211 STA mapping */ - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) - RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL); - - /* Add auxiliary station for scanning */ - ret = iwl_mvm_add_aux_sta(mvm); - if (ret) - goto error; - - return 0; - error: - iwl_trans_stop_device(mvm->trans); - return ret; -} - -void iwl_mvm_rx_card_state_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_card_state_notif *card_state_notif = (void *)pkt->data; - u32 flags = le32_to_cpu(card_state_notif->flags); - - IWL_DEBUG_RF_KILL(mvm, "Card state received: HW:%s SW:%s CT:%s\n", - (flags & HW_CARD_DISABLED) ? "Kill" : "On", - (flags & SW_CARD_DISABLED) ? "Kill" : "On", - (flags & CT_KILL_CARD_DISABLED) ? - "Reached" : "Not reached"); -} - -void iwl_mvm_rx_mfuart_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_mfuart_load_notif *mfuart_notif = (void *)pkt->data; - - IWL_DEBUG_INFO(mvm, - "MFUART: installed ver: 0x%08x, external ver: 0x%08x, status: 0x%08x, duration: 0x%08x\n", - le32_to_cpu(mfuart_notif->installed_ver), - le32_to_cpu(mfuart_notif->external_ver), - le32_to_cpu(mfuart_notif->status), - le32_to_cpu(mfuart_notif->duration)); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/led.c b/drivers/net/wireless/iwlwifi/mvm/led.c deleted file mode 100644 index e3b3cf4db..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/led.c +++ /dev/null @@ -1,136 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 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) 2012 - 2014 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 <linux/leds.h> -#include "iwl-io.h" -#include "iwl-csr.h" -#include "mvm.h" - -/* Set led register on */ -static void iwl_mvm_led_enable(struct iwl_mvm *mvm) -{ - iwl_write32(mvm->trans, CSR_LED_REG, CSR_LED_REG_TURN_ON); -} - -/* Set led register off */ -static void iwl_mvm_led_disable(struct iwl_mvm *mvm) -{ - iwl_write32(mvm->trans, CSR_LED_REG, CSR_LED_REG_TURN_OFF); -} - -static void iwl_led_brightness_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct iwl_mvm *mvm = container_of(led_cdev, struct iwl_mvm, led); - if (brightness > 0) - iwl_mvm_led_enable(mvm); - else - iwl_mvm_led_disable(mvm); -} - -int iwl_mvm_leds_init(struct iwl_mvm *mvm) -{ - int mode = iwlwifi_mod_params.led_mode; - int ret; - - switch (mode) { - case IWL_LED_BLINK: - IWL_ERR(mvm, "Blink led mode not supported, used default\n"); - case IWL_LED_DEFAULT: - case IWL_LED_RF_STATE: - mode = IWL_LED_RF_STATE; - break; - case IWL_LED_DISABLE: - IWL_INFO(mvm, "Led disabled\n"); - return 0; - default: - return -EINVAL; - } - - mvm->led.name = kasprintf(GFP_KERNEL, "%s-led", - wiphy_name(mvm->hw->wiphy)); - mvm->led.brightness_set = iwl_led_brightness_set; - mvm->led.max_brightness = 1; - - if (mode == IWL_LED_RF_STATE) - mvm->led.default_trigger = - ieee80211_get_radio_led_name(mvm->hw); - - ret = led_classdev_register(mvm->trans->dev, &mvm->led); - if (ret) { - kfree(mvm->led.name); - IWL_INFO(mvm, "Failed to enable led\n"); - return ret; - } - - return 0; -} - -void iwl_mvm_leds_exit(struct iwl_mvm *mvm) -{ - if (iwlwifi_mod_params.led_mode == IWL_LED_DISABLE) - return; - - led_classdev_unregister(&mvm->led); - kfree(mvm->led.name); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c deleted file mode 100644 index ad7ad720d..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ /dev/null @@ -1,1452 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH - * 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 <linux/etherdevice.h> -#include <net/mac80211.h> -#include "iwl-io.h" -#include "iwl-prph.h" -#include "fw-api.h" -#include "mvm.h" -#include "time-event.h" - -const u8 iwl_mvm_ac_to_tx_fifo[] = { - IWL_MVM_TX_FIFO_VO, - IWL_MVM_TX_FIFO_VI, - IWL_MVM_TX_FIFO_BE, - IWL_MVM_TX_FIFO_BK, -}; - -struct iwl_mvm_mac_iface_iterator_data { - struct iwl_mvm *mvm; - struct ieee80211_vif *vif; - unsigned long available_mac_ids[BITS_TO_LONGS(NUM_MAC_INDEX_DRIVER)]; - unsigned long available_tsf_ids[BITS_TO_LONGS(NUM_TSF_IDS)]; - enum iwl_tsf_id preferred_tsf; - bool found_vif; -}; - -struct iwl_mvm_hw_queues_iface_iterator_data { - struct ieee80211_vif *exclude_vif; - unsigned long used_hw_queues; -}; - -static void iwl_mvm_mac_tsf_id_iter(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_mac_iface_iterator_data *data = _data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - u16 min_bi; - - /* Skip the interface for which we are trying to assign a tsf_id */ - if (vif == data->vif) - return; - - /* - * The TSF is a hardware/firmware resource, there are 4 and - * the driver should assign and free them as needed. However, - * there are cases where 2 MACs should share the same TSF ID - * for the purpose of clock sync, an optimization to avoid - * clock drift causing overlapping TBTTs/DTIMs for a GO and - * client in the system. - * - * The firmware will decide according to the MAC type which - * will be the master and slave. Clients that need to sync - * with a remote station will be the master, and an AP or GO - * will be the slave. - * - * Depending on the new interface type it can be slaved to - * or become the master of an existing interface. - */ - switch (data->vif->type) { - case NL80211_IFTYPE_STATION: - /* - * The new interface is a client, so if the one we're iterating - * is an AP, and the beacon interval of the AP is a multiple or - * divisor of the beacon interval of the client, the same TSF - * should be used to avoid drift between the new client and - * existing AP. The existing AP will get drift updates from the - * new client context in this case. - */ - if (vif->type != NL80211_IFTYPE_AP || - data->preferred_tsf != NUM_TSF_IDS || - !test_bit(mvmvif->tsf_id, data->available_tsf_ids)) - break; - - min_bi = min(data->vif->bss_conf.beacon_int, - vif->bss_conf.beacon_int); - - if (!min_bi) - break; - - if ((data->vif->bss_conf.beacon_int - - vif->bss_conf.beacon_int) % min_bi == 0) { - data->preferred_tsf = mvmvif->tsf_id; - return; - } - break; - - case NL80211_IFTYPE_AP: - /* - * The new interface is AP/GO, so if its beacon interval is a - * multiple or a divisor of the beacon interval of an existing - * interface, it should get drift updates from an existing - * client or use the same TSF as an existing GO. There's no - * drift between TSFs internally but if they used different - * TSFs then a new client MAC could update one of them and - * cause drift that way. - */ - if ((vif->type != NL80211_IFTYPE_AP && - vif->type != NL80211_IFTYPE_STATION) || - data->preferred_tsf != NUM_TSF_IDS || - !test_bit(mvmvif->tsf_id, data->available_tsf_ids)) - break; - - min_bi = min(data->vif->bss_conf.beacon_int, - vif->bss_conf.beacon_int); - - if (!min_bi) - break; - - if ((data->vif->bss_conf.beacon_int - - vif->bss_conf.beacon_int) % min_bi == 0) { - data->preferred_tsf = mvmvif->tsf_id; - return; - } - break; - default: - /* - * For all other interface types there's no need to - * take drift into account. Either they're exclusive - * like IBSS and monitor, or we don't care much about - * their TSF (like P2P Device), but we won't be able - * to share the TSF resource. - */ - break; - } - - /* - * Unless we exited above, we can't share the TSF resource - * that the virtual interface we're iterating over is using - * with the new one, so clear the available bit and if this - * was the preferred one, reset that as well. - */ - __clear_bit(mvmvif->tsf_id, data->available_tsf_ids); - - if (data->preferred_tsf == mvmvif->tsf_id) - data->preferred_tsf = NUM_TSF_IDS; -} - -/* - * Get the mask of the queues used by the vif - */ -u32 iwl_mvm_mac_get_queues_mask(struct ieee80211_vif *vif) -{ - u32 qmask = 0, ac; - - if (vif->type == NL80211_IFTYPE_P2P_DEVICE) - return BIT(IWL_MVM_OFFCHANNEL_QUEUE); - - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { - if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE) - qmask |= BIT(vif->hw_queue[ac]); - } - - if (vif->type == NL80211_IFTYPE_AP) - qmask |= BIT(vif->cab_queue); - - return qmask; -} - -static void iwl_mvm_iface_hw_queues_iter(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_hw_queues_iface_iterator_data *data = _data; - - /* exclude the given vif */ - if (vif == data->exclude_vif) - return; - - data->used_hw_queues |= iwl_mvm_mac_get_queues_mask(vif); -} - -static void iwl_mvm_mac_sta_hw_queues_iter(void *_data, - struct ieee80211_sta *sta) -{ - struct iwl_mvm_hw_queues_iface_iterator_data *data = _data; - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - - /* Mark the queues used by the sta */ - data->used_hw_queues |= mvmsta->tfd_queue_msk; -} - -unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm, - struct ieee80211_vif *exclude_vif) -{ - u8 sta_id; - struct iwl_mvm_hw_queues_iface_iterator_data data = { - .exclude_vif = exclude_vif, - .used_hw_queues = - BIT(IWL_MVM_OFFCHANNEL_QUEUE) | - BIT(mvm->aux_queue) | - BIT(IWL_MVM_CMD_QUEUE), - }; - - lockdep_assert_held(&mvm->mutex); - - /* mark all VIF used hw queues */ - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, - iwl_mvm_iface_hw_queues_iter, &data); - - /* don't assign the same hw queues as TDLS stations */ - ieee80211_iterate_stations_atomic(mvm->hw, - iwl_mvm_mac_sta_hw_queues_iter, - &data); - - /* - * Some TDLS stations may be removed but are in the process of being - * drained. Don't touch their queues. - */ - for_each_set_bit(sta_id, mvm->sta_drained, IWL_MVM_STATION_COUNT) - data.used_hw_queues |= mvm->tfd_drained[sta_id]; - - return data.used_hw_queues; -} - -static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_mac_iface_iterator_data *data = _data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - /* Iterator may already find the interface being added -- skip it */ - if (vif == data->vif) { - data->found_vif = true; - return; - } - - /* Mark MAC IDs as used by clearing the available bit, and - * (below) mark TSFs as used if their existing use is not - * compatible with the new interface type. - * No locking or atomic bit operations are needed since the - * data is on the stack of the caller function. - */ - __clear_bit(mvmvif->id, data->available_mac_ids); - - /* find a suitable tsf_id */ - iwl_mvm_mac_tsf_id_iter(_data, mac, vif); -} - -void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_mac_iface_iterator_data data = { - .mvm = mvm, - .vif = vif, - .available_tsf_ids = { (1 << NUM_TSF_IDS) - 1 }, - /* no preference yet */ - .preferred_tsf = NUM_TSF_IDS, - }; - - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, - iwl_mvm_mac_tsf_id_iter, &data); - - if (data.preferred_tsf != NUM_TSF_IDS) - mvmvif->tsf_id = data.preferred_tsf; - else if (!test_bit(mvmvif->tsf_id, data.available_tsf_ids)) - mvmvif->tsf_id = find_first_bit(data.available_tsf_ids, - NUM_TSF_IDS); -} - -static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_mac_iface_iterator_data data = { - .mvm = mvm, - .vif = vif, - .available_mac_ids = { (1 << NUM_MAC_INDEX_DRIVER) - 1 }, - .available_tsf_ids = { (1 << NUM_TSF_IDS) - 1 }, - /* no preference yet */ - .preferred_tsf = NUM_TSF_IDS, - .found_vif = false, - }; - u32 ac; - int ret, i; - unsigned long used_hw_queues; - - /* - * Allocate a MAC ID and a TSF for this MAC, along with the queues - * and other resources. - */ - - /* - * Before the iterator, we start with all MAC IDs and TSFs available. - * - * During iteration, all MAC IDs are cleared that are in use by other - * virtual interfaces, and all TSF IDs are cleared that can't be used - * by this new virtual interface because they're used by an interface - * that can't share it with the new one. - * At the same time, we check if there's a preferred TSF in the case - * that we should share it with another interface. - */ - - /* Currently, MAC ID 0 should be used only for the managed/IBSS vif */ - switch (vif->type) { - case NL80211_IFTYPE_ADHOC: - break; - case NL80211_IFTYPE_STATION: - if (!vif->p2p) - break; - /* fall through */ - default: - __clear_bit(0, data.available_mac_ids); - } - - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, - iwl_mvm_mac_iface_iterator, &data); - - used_hw_queues = iwl_mvm_get_used_hw_queues(mvm, vif); - - /* - * In the case we're getting here during resume, it's similar to - * firmware restart, and with RESUME_ALL the iterator will find - * the vif being added already. - * We don't want to reassign any IDs in either case since doing - * so would probably assign different IDs (as interfaces aren't - * necessarily added in the same order), but the old IDs were - * preserved anyway, so skip ID assignment for both resume and - * recovery. - */ - if (data.found_vif) - return 0; - - /* Therefore, in recovery, we can't get here */ - if (WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))) - return -EBUSY; - - mvmvif->id = find_first_bit(data.available_mac_ids, - NUM_MAC_INDEX_DRIVER); - if (mvmvif->id == NUM_MAC_INDEX_DRIVER) { - IWL_ERR(mvm, "Failed to init MAC context - no free ID!\n"); - ret = -EIO; - goto exit_fail; - } - - if (data.preferred_tsf != NUM_TSF_IDS) - mvmvif->tsf_id = data.preferred_tsf; - else - mvmvif->tsf_id = find_first_bit(data.available_tsf_ids, - NUM_TSF_IDS); - if (mvmvif->tsf_id == NUM_TSF_IDS) { - IWL_ERR(mvm, "Failed to init MAC context - no free TSF!\n"); - ret = -EIO; - goto exit_fail; - } - - mvmvif->color = 0; - - INIT_LIST_HEAD(&mvmvif->time_event_data.list); - mvmvif->time_event_data.id = TE_MAX; - - /* No need to allocate data queues to P2P Device MAC.*/ - if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) - vif->hw_queue[ac] = IEEE80211_INVAL_HW_QUEUE; - - return 0; - } - - /* Find available queues, and allocate them to the ACs */ - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { - u8 queue = find_first_zero_bit(&used_hw_queues, - mvm->first_agg_queue); - - if (queue >= mvm->first_agg_queue) { - IWL_ERR(mvm, "Failed to allocate queue\n"); - ret = -EIO; - goto exit_fail; - } - - __set_bit(queue, &used_hw_queues); - vif->hw_queue[ac] = queue; - } - - /* Allocate the CAB queue for softAP and GO interfaces */ - if (vif->type == NL80211_IFTYPE_AP) { - u8 queue = find_first_zero_bit(&used_hw_queues, - mvm->first_agg_queue); - - if (queue >= mvm->first_agg_queue) { - IWL_ERR(mvm, "Failed to allocate cab queue\n"); - ret = -EIO; - goto exit_fail; - } - - vif->cab_queue = queue; - } else { - vif->cab_queue = IEEE80211_INVAL_HW_QUEUE; - } - - mvmvif->bcast_sta.sta_id = IWL_MVM_STATION_COUNT; - mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; - - for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) - mvmvif->smps_requests[i] = IEEE80211_SMPS_AUTOMATIC; - - return 0; - -exit_fail: - memset(mvmvif, 0, sizeof(struct iwl_mvm_vif)); - memset(vif->hw_queue, IEEE80211_INVAL_HW_QUEUE, sizeof(vif->hw_queue)); - vif->cab_queue = IEEE80211_INVAL_HW_QUEUE; - return ret; -} - -int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - unsigned int wdg_timeout = - iwl_mvm_get_wd_timeout(mvm, vif, false, false); - u32 ac; - int ret; - - lockdep_assert_held(&mvm->mutex); - - ret = iwl_mvm_mac_ctxt_allocate_resources(mvm, vif); - if (ret) - return ret; - - switch (vif->type) { - case NL80211_IFTYPE_P2P_DEVICE: - iwl_mvm_enable_ac_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE, - IWL_MVM_OFFCHANNEL_QUEUE, - IWL_MVM_TX_FIFO_VO, 0, wdg_timeout); - break; - case NL80211_IFTYPE_AP: - iwl_mvm_enable_ac_txq(mvm, vif->cab_queue, vif->cab_queue, - IWL_MVM_TX_FIFO_MCAST, 0, wdg_timeout); - /* fall through */ - default: - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) - iwl_mvm_enable_ac_txq(mvm, vif->hw_queue[ac], - vif->hw_queue[ac], - iwl_mvm_ac_to_tx_fifo[ac], 0, - wdg_timeout); - break; - } - - return 0; -} - -void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - int ac; - - lockdep_assert_held(&mvm->mutex); - - switch (vif->type) { - case NL80211_IFTYPE_P2P_DEVICE: - iwl_mvm_disable_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE, - IWL_MVM_OFFCHANNEL_QUEUE, IWL_MAX_TID_COUNT, - 0); - break; - case NL80211_IFTYPE_AP: - iwl_mvm_disable_txq(mvm, vif->cab_queue, vif->cab_queue, - IWL_MAX_TID_COUNT, 0); - /* fall through */ - default: - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) - iwl_mvm_disable_txq(mvm, vif->hw_queue[ac], - vif->hw_queue[ac], - IWL_MAX_TID_COUNT, 0); - } -} - -static void iwl_mvm_ack_rates(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - enum ieee80211_band band, - u8 *cck_rates, u8 *ofdm_rates) -{ - struct ieee80211_supported_band *sband; - unsigned long basic = vif->bss_conf.basic_rates; - int lowest_present_ofdm = 100; - int lowest_present_cck = 100; - u8 cck = 0; - u8 ofdm = 0; - int i; - - sband = mvm->hw->wiphy->bands[band]; - - for_each_set_bit(i, &basic, BITS_PER_LONG) { - int hw = sband->bitrates[i].hw_value; - if (hw >= IWL_FIRST_OFDM_RATE) { - ofdm |= BIT(hw - IWL_FIRST_OFDM_RATE); - if (lowest_present_ofdm > hw) - lowest_present_ofdm = hw; - } else { - BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0); - - cck |= BIT(hw); - if (lowest_present_cck > hw) - lowest_present_cck = hw; - } - } - - /* - * Now we've got the basic rates as bitmaps in the ofdm and cck - * variables. This isn't sufficient though, as there might not - * be all the right rates in the bitmap. E.g. if the only basic - * rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps - * and 6 Mbps because the 802.11-2007 standard says in 9.6: - * - * [...] a STA responding to a received frame shall transmit - * its Control Response frame [...] at the highest rate in the - * BSSBasicRateSet parameter that is less than or equal to the - * rate of the immediately previous frame in the frame exchange - * sequence ([...]) and that is of the same modulation class - * ([...]) as the received frame. If no rate contained in the - * BSSBasicRateSet parameter meets these conditions, then the - * control frame sent in response to a received frame shall be - * transmitted at the highest mandatory rate of the PHY that is - * less than or equal to the rate of the received frame, and - * that is of the same modulation class as the received frame. - * - * As a consequence, we need to add all mandatory rates that are - * lower than all of the basic rates to these bitmaps. - */ - - if (IWL_RATE_24M_INDEX < lowest_present_ofdm) - ofdm |= IWL_RATE_BIT_MSK(24) >> IWL_FIRST_OFDM_RATE; - if (IWL_RATE_12M_INDEX < lowest_present_ofdm) - ofdm |= IWL_RATE_BIT_MSK(12) >> IWL_FIRST_OFDM_RATE; - /* 6M already there or needed so always add */ - ofdm |= IWL_RATE_BIT_MSK(6) >> IWL_FIRST_OFDM_RATE; - - /* - * CCK is a bit more complex with DSSS vs. HR/DSSS vs. ERP. - * Note, however: - * - if no CCK rates are basic, it must be ERP since there must - * be some basic rates at all, so they're OFDM => ERP PHY - * (or we're in 5 GHz, and the cck bitmap will never be used) - * - if 11M is a basic rate, it must be ERP as well, so add 5.5M - * - if 5.5M is basic, 1M and 2M are mandatory - * - if 2M is basic, 1M is mandatory - * - if 1M is basic, that's the only valid ACK rate. - * As a consequence, it's not as complicated as it sounds, just add - * any lower rates to the ACK rate bitmap. - */ - if (IWL_RATE_11M_INDEX < lowest_present_cck) - cck |= IWL_RATE_BIT_MSK(11) >> IWL_FIRST_CCK_RATE; - if (IWL_RATE_5M_INDEX < lowest_present_cck) - cck |= IWL_RATE_BIT_MSK(5) >> IWL_FIRST_CCK_RATE; - if (IWL_RATE_2M_INDEX < lowest_present_cck) - cck |= IWL_RATE_BIT_MSK(2) >> IWL_FIRST_CCK_RATE; - /* 1M already there or needed so always add */ - cck |= IWL_RATE_BIT_MSK(1) >> IWL_FIRST_CCK_RATE; - - *cck_rates = cck; - *ofdm_rates = ofdm; -} - -static void iwl_mvm_mac_ctxt_set_ht_flags(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_mac_ctx_cmd *cmd) -{ - /* for both sta and ap, ht_operation_mode hold the protection_mode */ - u8 protection_mode = vif->bss_conf.ht_operation_mode & - IEEE80211_HT_OP_MODE_PROTECTION; - /* The fw does not distinguish between ht and fat */ - u32 ht_flag = MAC_PROT_FLG_HT_PROT | MAC_PROT_FLG_FAT_PROT; - - IWL_DEBUG_RATE(mvm, "protection mode set to %d\n", protection_mode); - /* - * See section 9.23.3.1 of IEEE 80211-2012. - * Nongreenfield HT STAs Present is not supported. - */ - switch (protection_mode) { - case IEEE80211_HT_OP_MODE_PROTECTION_NONE: - break; - case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER: - case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: - cmd->protection_flags |= cpu_to_le32(ht_flag); - break; - case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: - /* Protect when channel wider than 20MHz */ - if (vif->bss_conf.chandef.width > NL80211_CHAN_WIDTH_20) - cmd->protection_flags |= cpu_to_le32(ht_flag); - break; - default: - IWL_ERR(mvm, "Illegal protection mode %d\n", - protection_mode); - break; - } -} - -static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_mac_ctx_cmd *cmd, - const u8 *bssid_override, - u32 action) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct ieee80211_chanctx_conf *chanctx; - bool ht_enabled = !!(vif->bss_conf.ht_operation_mode & - IEEE80211_HT_OP_MODE_PROTECTION); - u8 cck_ack_rates, ofdm_ack_rates; - const u8 *bssid = bssid_override ?: vif->bss_conf.bssid; - int i; - - cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, - mvmvif->color)); - cmd->action = cpu_to_le32(action); - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - if (vif->p2p) - cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_P2P_STA); - else - cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_BSS_STA); - break; - case NL80211_IFTYPE_AP: - cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_GO); - break; - case NL80211_IFTYPE_MONITOR: - cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_LISTENER); - break; - case NL80211_IFTYPE_P2P_DEVICE: - cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_P2P_DEVICE); - break; - case NL80211_IFTYPE_ADHOC: - cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_IBSS); - break; - default: - WARN_ON_ONCE(1); - } - - cmd->tsf_id = cpu_to_le32(mvmvif->tsf_id); - - memcpy(cmd->node_addr, vif->addr, ETH_ALEN); - - if (bssid) - memcpy(cmd->bssid_addr, bssid, ETH_ALEN); - else - eth_broadcast_addr(cmd->bssid_addr); - - rcu_read_lock(); - chanctx = rcu_dereference(vif->chanctx_conf); - iwl_mvm_ack_rates(mvm, vif, chanctx ? chanctx->def.chan->band - : IEEE80211_BAND_2GHZ, - &cck_ack_rates, &ofdm_ack_rates); - rcu_read_unlock(); - - cmd->cck_rates = cpu_to_le32((u32)cck_ack_rates); - cmd->ofdm_rates = cpu_to_le32((u32)ofdm_ack_rates); - - cmd->cck_short_preamble = - cpu_to_le32(vif->bss_conf.use_short_preamble ? - MAC_FLG_SHORT_PREAMBLE : 0); - cmd->short_slot = - cpu_to_le32(vif->bss_conf.use_short_slot ? - MAC_FLG_SHORT_SLOT : 0); - - for (i = 0; i < IEEE80211_NUM_ACS; i++) { - u8 txf = iwl_mvm_ac_to_tx_fifo[i]; - - cmd->ac[txf].cw_min = - cpu_to_le16(mvmvif->queue_params[i].cw_min); - cmd->ac[txf].cw_max = - cpu_to_le16(mvmvif->queue_params[i].cw_max); - cmd->ac[txf].edca_txop = - cpu_to_le16(mvmvif->queue_params[i].txop * 32); - cmd->ac[txf].aifsn = mvmvif->queue_params[i].aifs; - cmd->ac[txf].fifos_mask = BIT(txf); - } - - /* in AP mode, the MCAST FIFO takes the EDCA params from VO */ - if (vif->type == NL80211_IFTYPE_AP) - cmd->ac[IWL_MVM_TX_FIFO_VO].fifos_mask |= - BIT(IWL_MVM_TX_FIFO_MCAST); - - if (vif->bss_conf.qos) - cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA); - - if (vif->bss_conf.use_cts_prot) - cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT); - - IWL_DEBUG_RATE(mvm, "use_cts_prot %d, ht_operation_mode %d\n", - vif->bss_conf.use_cts_prot, - vif->bss_conf.ht_operation_mode); - if (vif->bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) - cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_TGN); - if (ht_enabled) - iwl_mvm_mac_ctxt_set_ht_flags(mvm, vif, cmd); - - cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP); -} - -static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm, - struct iwl_mac_ctx_cmd *cmd) -{ - int ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, 0, - sizeof(*cmd), cmd); - if (ret) - IWL_ERR(mvm, "Failed to send MAC context (action:%d): %d\n", - le32_to_cpu(cmd->action), ret); - return ret; -} - -static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 action, bool force_assoc_off, - const u8 *bssid_override) -{ - struct iwl_mac_ctx_cmd cmd = {}; - struct iwl_mac_data_sta *ctxt_sta; - - WARN_ON(vif->type != NL80211_IFTYPE_STATION); - - /* Fill the common data for all mac context types */ - iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, bssid_override, action); - - if (vif->p2p) { - struct ieee80211_p2p_noa_attr *noa = - &vif->bss_conf.p2p_noa_attr; - - cmd.p2p_sta.ctwin = cpu_to_le32(noa->oppps_ctwindow & - IEEE80211_P2P_OPPPS_CTWINDOW_MASK); - ctxt_sta = &cmd.p2p_sta.sta; - } else { - ctxt_sta = &cmd.sta; - } - - /* We need the dtim_period to set the MAC as associated */ - if (vif->bss_conf.assoc && vif->bss_conf.dtim_period && - !force_assoc_off) { - u32 dtim_offs; - - /* - * The DTIM count counts down, so when it is N that means N - * more beacon intervals happen until the DTIM TBTT. Therefore - * add this to the current time. If that ends up being in the - * future, the firmware will handle it. - * - * Also note that the system_timestamp (which we get here as - * "sync_device_ts") and TSF timestamp aren't at exactly the - * same offset in the frame -- the TSF is at the first symbol - * of the TSF, the system timestamp is at signal acquisition - * time. This means there's an offset between them of at most - * a few hundred microseconds (24 * 8 bits + PLCP time gives - * 384us in the longest case), this is currently not relevant - * as the firmware wakes up around 2ms before the TBTT. - */ - dtim_offs = vif->bss_conf.sync_dtim_count * - vif->bss_conf.beacon_int; - /* convert TU to usecs */ - dtim_offs *= 1024; - - ctxt_sta->dtim_tsf = - cpu_to_le64(vif->bss_conf.sync_tsf + dtim_offs); - ctxt_sta->dtim_time = - cpu_to_le32(vif->bss_conf.sync_device_ts + dtim_offs); - - IWL_DEBUG_INFO(mvm, "DTIM TBTT is 0x%llx/0x%x, offset %d\n", - le64_to_cpu(ctxt_sta->dtim_tsf), - le32_to_cpu(ctxt_sta->dtim_time), - dtim_offs); - - ctxt_sta->is_assoc = cpu_to_le32(1); - } else { - ctxt_sta->is_assoc = cpu_to_le32(0); - - /* Allow beacons to pass through as long as we are not - * associated, or we do not have dtim period information. - */ - cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON); - } - - ctxt_sta->bi = cpu_to_le32(vif->bss_conf.beacon_int); - ctxt_sta->bi_reciprocal = - cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int)); - ctxt_sta->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int * - vif->bss_conf.dtim_period); - ctxt_sta->dtim_reciprocal = - cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int * - vif->bss_conf.dtim_period)); - - ctxt_sta->listen_interval = cpu_to_le32(mvm->hw->conf.listen_interval); - ctxt_sta->assoc_id = cpu_to_le32(vif->bss_conf.aid); - - if (vif->probe_req_reg && vif->bss_conf.assoc && vif->p2p) - cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); - - return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); -} - -static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 action) -{ - struct iwl_mac_ctx_cmd cmd = {}; - - WARN_ON(vif->type != NL80211_IFTYPE_MONITOR); - - iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); - - 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 | - MAC_FILTER_IN_CRC32); - ieee80211_hw_set(mvm->hw, RX_INCLUDES_FCS); - - return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); -} - -static int iwl_mvm_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 action) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mac_ctx_cmd cmd = {}; - - WARN_ON(vif->type != NL80211_IFTYPE_ADHOC); - - iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); - - cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_BEACON | - MAC_FILTER_IN_PROBE_REQUEST); - - /* cmd.ibss.beacon_time/cmd.ibss.beacon_tsf are curently ignored */ - cmd.ibss.bi = cpu_to_le32(vif->bss_conf.beacon_int); - cmd.ibss.bi_reciprocal = - cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int)); - - /* TODO: Assumes that the beacon id == mac context id */ - cmd.ibss.beacon_template = cpu_to_le32(mvmvif->id); - - return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); -} - -struct iwl_mvm_go_iterator_data { - bool go_active; -}; - -static void iwl_mvm_go_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) -{ - struct iwl_mvm_go_iterator_data *data = _data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (vif->type == NL80211_IFTYPE_AP && vif->p2p && - mvmvif->ap_ibss_active) - data->go_active = true; -} - -static int iwl_mvm_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 action) -{ - struct iwl_mac_ctx_cmd cmd = {}; - struct iwl_mvm_go_iterator_data data = {}; - - WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE); - - iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); - - cmd.protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT); - - /* 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 - * discoverable and there is at least another active P2P GO. Settings - * this flag will allow the P2P Device to be discoverable on other - * channels in addition to its listen channel. - * Note that this flag should not be set in other cases as it opens the - * Rx filters on all MAC and increases the number of interrupts. - */ - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, - iwl_mvm_go_iterator, &data); - - cmd.p2p_dev.is_disc_extended = cpu_to_le32(data.go_active ? 1 : 0); - return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); -} - -static void iwl_mvm_mac_ctxt_set_tim(struct iwl_mvm *mvm, - struct iwl_mac_beacon_cmd *beacon_cmd, - u8 *beacon, u32 frame_size) -{ - u32 tim_idx; - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon; - - /* The index is relative to frame start but we start looking at the - * variable-length part of the beacon. */ - tim_idx = mgmt->u.beacon.variable - beacon; - - /* Parse variable-length elements of beacon to find WLAN_EID_TIM */ - while ((tim_idx < (frame_size - 2)) && - (beacon[tim_idx] != WLAN_EID_TIM)) - tim_idx += beacon[tim_idx+1] + 2; - - /* If TIM field was found, set variables */ - if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) { - beacon_cmd->tim_idx = cpu_to_le32(tim_idx); - beacon_cmd->tim_size = cpu_to_le32((u32)beacon[tim_idx+1]); - } else { - IWL_WARN(mvm, "Unable to find TIM Element in beacon\n"); - } -} - -static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct sk_buff *beacon) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_host_cmd cmd = { - .id = BEACON_TEMPLATE_CMD, - .flags = CMD_ASYNC, - }; - struct iwl_mac_beacon_cmd beacon_cmd = {}; - struct ieee80211_tx_info *info; - u32 beacon_skb_len; - u32 rate, tx_flags; - - if (WARN_ON(!beacon)) - return -EINVAL; - - beacon_skb_len = beacon->len; - - /* TODO: for now the beacon template id is set to be the mac context id. - * Might be better to handle it as another resource ... */ - beacon_cmd.template_id = cpu_to_le32((u32)mvmvif->id); - info = IEEE80211_SKB_CB(beacon); - - /* Set up TX command fields */ - beacon_cmd.tx.len = cpu_to_le16((u16)beacon_skb_len); - beacon_cmd.tx.sta_id = mvmvif->bcast_sta.sta_id; - beacon_cmd.tx.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE); - tx_flags = TX_CMD_FLG_SEQ_CTL | TX_CMD_FLG_TSF; - tx_flags |= - iwl_mvm_bt_coex_tx_prio(mvm, (void *)beacon->data, info, 0) << - TX_CMD_FLG_BT_PRIO_POS; - beacon_cmd.tx.tx_flags = cpu_to_le32(tx_flags); - - mvm->mgmt_last_antenna_idx = - iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm), - mvm->mgmt_last_antenna_idx); - - beacon_cmd.tx.rate_n_flags = - cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) << - RATE_MCS_ANT_POS); - - if (info->band == IEEE80211_BAND_5GHZ || vif->p2p) { - rate = IWL_FIRST_OFDM_RATE; - } else { - rate = IWL_FIRST_CCK_RATE; - beacon_cmd.tx.rate_n_flags |= cpu_to_le32(RATE_MCS_CCK_MSK); - } - beacon_cmd.tx.rate_n_flags |= - cpu_to_le32(iwl_mvm_mac80211_idx_to_hwrate(rate)); - - /* Set up TX beacon command fields */ - if (vif->type == NL80211_IFTYPE_AP) - iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd, - beacon->data, - beacon_skb_len); - - /* Submit command */ - cmd.len[0] = sizeof(beacon_cmd); - cmd.data[0] = &beacon_cmd; - cmd.dataflags[0] = 0; - cmd.len[1] = beacon_skb_len; - cmd.data[1] = beacon->data; - cmd.dataflags[1] = IWL_HCMD_DFL_DUP; - - return iwl_mvm_send_cmd(mvm, &cmd); -} - -/* The beacon template for the AP/GO/IBSS has changed and needs update */ -int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct sk_buff *beacon; - int ret; - - WARN_ON(vif->type != NL80211_IFTYPE_AP && - vif->type != NL80211_IFTYPE_ADHOC); - - beacon = ieee80211_beacon_get_template(mvm->hw, vif, NULL); - if (!beacon) - return -ENOMEM; - - ret = iwl_mvm_mac_ctxt_send_beacon(mvm, vif, beacon); - dev_kfree_skb(beacon); - return ret; -} - -struct iwl_mvm_mac_ap_iterator_data { - struct iwl_mvm *mvm; - struct ieee80211_vif *vif; - u32 beacon_device_ts; - u16 beacon_int; -}; - -/* Find the beacon_device_ts and beacon_int for a managed interface */ -static void iwl_mvm_mac_ap_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_mac_ap_iterator_data *data = _data; - - if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc) - return; - - /* Station client has higher priority over P2P client*/ - if (vif->p2p && data->beacon_device_ts) - return; - - data->beacon_device_ts = vif->bss_conf.sync_device_ts; - data->beacon_int = vif->bss_conf.beacon_int; -} - -/* - * Fill the specific data for mac context of type AP of P2P GO - */ -static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_mac_data_ap *ctxt_ap, - bool add) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_mac_ap_iterator_data data = { - .mvm = mvm, - .vif = vif, - .beacon_device_ts = 0 - }; - - ctxt_ap->bi = cpu_to_le32(vif->bss_conf.beacon_int); - ctxt_ap->bi_reciprocal = - cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int)); - ctxt_ap->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int * - vif->bss_conf.dtim_period); - ctxt_ap->dtim_reciprocal = - cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int * - vif->bss_conf.dtim_period)); - - ctxt_ap->mcast_qid = cpu_to_le32(vif->cab_queue); - - /* - * Only set the beacon 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) { - /* - * If there is a station/P2P client interface which is - * associated, set the AP's TBTT far enough from the station's - * TBTT. Otherwise, set it to the current system time - */ - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, - iwl_mvm_mac_ap_iterator, &data); - - if (data.beacon_device_ts) { - u32 rand = (prandom_u32() % (64 - 36)) + 36; - mvmvif->ap_beacon_time = data.beacon_device_ts + - ieee80211_tu_to_usec(data.beacon_int * rand / - 100); - } else { - 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); -} - -static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 action) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mac_ctx_cmd cmd = {}; - - WARN_ON(vif->type != NL80211_IFTYPE_AP || vif->p2p); - - /* Fill the common data for all mac context types */ - iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); - - /* - * pass probe requests and beacons from other APs (needed - * for ht protection); when there're no any associated station - * don't ask FW to pass beacons to prevent unnecessary wake-ups. - */ - cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); - if (mvmvif->ap_assoc_sta_count) { - cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON); - IWL_DEBUG_HC(mvm, "Asking FW to pass beacons\n"); - } else { - IWL_DEBUG_HC(mvm, "No need to receive beacons\n"); - } - - /* Fill the data specific for ap mode */ - 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); -} - -static int iwl_mvm_mac_ctxt_cmd_go(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - 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); - - /* Fill the common data for all mac context types */ - iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); - - /* - * pass probe requests and beacons from other APs (needed - * for ht protection) - */ - cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST | - MAC_FILTER_IN_BEACON); - - /* Fill the data specific for GO mode */ - iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.go.ap, - action == FW_CTXT_ACTION_ADD); - - 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); -} - -static int iwl_mvm_mac_ctx_send(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - u32 action, bool force_assoc_off, - const u8 *bssid_override) -{ - switch (vif->type) { - case NL80211_IFTYPE_STATION: - return iwl_mvm_mac_ctxt_cmd_sta(mvm, vif, action, - force_assoc_off, - bssid_override); - break; - case NL80211_IFTYPE_AP: - if (!vif->p2p) - return iwl_mvm_mac_ctxt_cmd_ap(mvm, vif, action); - else - return iwl_mvm_mac_ctxt_cmd_go(mvm, vif, action); - break; - case NL80211_IFTYPE_MONITOR: - return iwl_mvm_mac_ctxt_cmd_listener(mvm, vif, action); - case NL80211_IFTYPE_P2P_DEVICE: - return iwl_mvm_mac_ctxt_cmd_p2p_device(mvm, vif, action); - case NL80211_IFTYPE_ADHOC: - return iwl_mvm_mac_ctxt_cmd_ibss(mvm, vif, action); - default: - break; - } - - return -EOPNOTSUPP; -} - -int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; - - if (WARN_ONCE(mvmvif->uploaded, "Adding active MAC %pM/%d\n", - vif->addr, ieee80211_vif_type_p2p(vif))) - return -EIO; - - ret = iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD, - true, NULL); - if (ret) - return ret; - - /* will only do anything at resume from D3 time */ - iwl_mvm_set_last_nonqos_seq(mvm, vif); - - mvmvif->uploaded = true; - return 0; -} - -int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - bool force_assoc_off, const u8 *bssid_override) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (WARN_ONCE(!mvmvif->uploaded, "Changing inactive MAC %pM/%d\n", - vif->addr, ieee80211_vif_type_p2p(vif))) - return -EIO; - - return iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY, - force_assoc_off, bssid_override); -} - -int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mac_ctx_cmd cmd; - int ret; - - if (WARN_ONCE(!mvmvif->uploaded, "Removing inactive MAC %pM/%d\n", - vif->addr, ieee80211_vif_type_p2p(vif))) - return -EIO; - - memset(&cmd, 0, sizeof(cmd)); - - cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, - mvmvif->color)); - cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE); - - ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, 0, - sizeof(cmd), &cmd); - if (ret) { - IWL_ERR(mvm, "Failed to remove MAC context: %d\n", ret); - return ret; - } - - mvmvif->uploaded = false; - - if (vif->type == NL80211_IFTYPE_MONITOR) - __clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, mvm->hw->flags); - - return 0; -} - -static void iwl_mvm_csa_count_down(struct iwl_mvm *mvm, - struct ieee80211_vif *csa_vif, u32 gp2, - bool tx_success) -{ - struct iwl_mvm_vif *mvmvif = - iwl_mvm_vif_from_mac80211(csa_vif); - - /* Don't start to countdown from a failed beacon */ - if (!tx_success && !mvmvif->csa_countdown) - return; - - mvmvif->csa_countdown = true; - - if (!ieee80211_csa_is_complete(csa_vif)) { - int c = ieee80211_csa_update_counter(csa_vif); - - iwl_mvm_mac_ctxt_beacon_changed(mvm, csa_vif); - if (csa_vif->p2p && - !iwl_mvm_te_scheduled(&mvmvif->time_event_data) && gp2 && - tx_success) { - u32 rel_time = (c + 1) * - csa_vif->bss_conf.beacon_int - - IWL_MVM_CHANNEL_SWITCH_TIME_GO; - u32 apply_time = gp2 + rel_time * 1024; - - iwl_mvm_schedule_csa_period(mvm, csa_vif, - IWL_MVM_CHANNEL_SWITCH_TIME_GO - - IWL_MVM_CHANNEL_SWITCH_MARGIN, - apply_time); - } - } else if (!iwl_mvm_te_scheduled(&mvmvif->time_event_data)) { - /* we don't have CSA NoA scheduled yet, switch now */ - ieee80211_csa_finish(csa_vif); - RCU_INIT_POINTER(mvm->csa_vif, NULL); - } -} - -void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_extended_beacon_notif *beacon = (void *)pkt->data; - struct iwl_mvm_tx_resp *beacon_notify_hdr; - struct ieee80211_vif *csa_vif; - struct ieee80211_vif *tx_blocked_vif; - u16 status; - - lockdep_assert_held(&mvm->mutex); - - beacon_notify_hdr = &beacon->beacon_notify_hdr; - mvm->ap_last_beacon_gp2 = le32_to_cpu(beacon->gp2); - - status = le16_to_cpu(beacon_notify_hdr->status.status) & TX_STATUS_MSK; - IWL_DEBUG_RX(mvm, - "beacon status %#x retries:%d tsf:0x%16llX gp2:0x%X rate:%d\n", - status, beacon_notify_hdr->failure_frame, - le64_to_cpu(beacon->tsf), - mvm->ap_last_beacon_gp2, - le32_to_cpu(beacon_notify_hdr->initial_rate)); - - csa_vif = rcu_dereference_protected(mvm->csa_vif, - lockdep_is_held(&mvm->mutex)); - if (unlikely(csa_vif && csa_vif->csa_active)) - iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2, - (status == TX_STATUS_SUCCESS)); - - tx_blocked_vif = rcu_dereference_protected(mvm->csa_tx_blocked_vif, - lockdep_is_held(&mvm->mutex)); - if (unlikely(tx_blocked_vif)) { - struct iwl_mvm_vif *mvmvif = - iwl_mvm_vif_from_mac80211(tx_blocked_vif); - - /* - * The channel switch is started and we have blocked the - * stations. If this is the first beacon (the timeout wasn't - * set), set the unblock timeout, otherwise countdown - */ - if (!mvm->csa_tx_block_bcn_timeout) - mvm->csa_tx_block_bcn_timeout = - IWL_MVM_CS_UNBLOCK_TX_TIMEOUT; - else - mvm->csa_tx_block_bcn_timeout--; - - /* Check if the timeout is expired, and unblock tx */ - if (mvm->csa_tx_block_bcn_timeout == 0) { - iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, false); - RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL); - } - } -} - -static void iwl_mvm_beacon_loss_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_missed_beacons_notif *missed_beacons = _data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - struct iwl_fw_dbg_trigger_missed_bcon *bcon_trig; - struct iwl_fw_dbg_trigger_tlv *trigger; - u32 stop_trig_missed_bcon, stop_trig_missed_bcon_since_rx; - u32 rx_missed_bcon, rx_missed_bcon_since_rx; - - if (mvmvif->id != (u16)le32_to_cpu(missed_beacons->mac_id)) - return; - - rx_missed_bcon = le32_to_cpu(missed_beacons->consec_missed_beacons); - rx_missed_bcon_since_rx = - le32_to_cpu(missed_beacons->consec_missed_beacons_since_last_rx); - /* - * TODO: the threshold should be adjusted based on latency conditions, - * and/or in case of a CS flow on one of the other AP vifs. - */ - if (le32_to_cpu(missed_beacons->consec_missed_beacons_since_last_rx) > - IWL_MVM_MISSED_BEACONS_THRESHOLD) - ieee80211_beacon_loss(vif); - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, - FW_DBG_TRIGGER_MISSED_BEACONS)) - return; - - trigger = iwl_fw_dbg_get_trigger(mvm->fw, - FW_DBG_TRIGGER_MISSED_BEACONS); - bcon_trig = (void *)trigger->data; - stop_trig_missed_bcon = le32_to_cpu(bcon_trig->stop_consec_missed_bcon); - stop_trig_missed_bcon_since_rx = - le32_to_cpu(bcon_trig->stop_consec_missed_bcon_since_rx); - - /* TODO: implement start trigger */ - - if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger)) - return; - - if (rx_missed_bcon_since_rx >= stop_trig_missed_bcon_since_rx || - rx_missed_bcon >= stop_trig_missed_bcon) - iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL); -} - -void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_missed_beacons_notif *mb = (void *)pkt->data; - - IWL_DEBUG_INFO(mvm, - "missed bcn mac_id=%u, consecutive=%u (%u, %u, %u)\n", - le32_to_cpu(mb->mac_id), - le32_to_cpu(mb->consec_missed_beacons), - le32_to_cpu(mb->consec_missed_beacons_since_last_rx), - le32_to_cpu(mb->num_recvd_beacons), - le32_to_cpu(mb->num_expected_beacons)); - - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_beacon_loss_iterator, - mb); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c deleted file mode 100644 index e88afac51..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ /dev/null @@ -1,4265 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * 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 <linux/kernel.h> -#include <linux/slab.h> -#include <linux/skbuff.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/ip.h> -#include <linux/if_arp.h> -#include <linux/devcoredump.h> -#include <net/mac80211.h> -#include <net/ieee80211_radiotap.h> -#include <net/tcp.h> - -#include "iwl-op-mode.h" -#include "iwl-io.h" -#include "mvm.h" -#include "sta.h" -#include "time-event.h" -#include "iwl-eeprom-parse.h" -#include "iwl-phy-db.h" -#include "testmode.h" -#include "iwl-fw-error-dump.h" -#include "iwl-prph.h" -#include "iwl-csr.h" -#include "iwl-nvm-parse.h" - -static const struct ieee80211_iface_limit iwl_mvm_limits[] = { - { - .max = 1, - .types = BIT(NL80211_IFTYPE_STATION), - }, - { - .max = 1, - .types = BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_P2P_GO), - }, - { - .max = 1, - .types = BIT(NL80211_IFTYPE_P2P_DEVICE), - }, -}; - -static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = { - { - .num_different_channels = 2, - .max_interfaces = 3, - .limits = iwl_mvm_limits, - .n_limits = ARRAY_SIZE(iwl_mvm_limits), - }, -}; - -#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 - -#ifdef CONFIG_IWLWIFI_BCAST_FILTERING -/* - * Use the reserved field to indicate magic values. - * these values will only be used internally by the driver, - * and won't make it to the fw (reserved will be 0). - * BC_FILTER_MAGIC_IP - configure the val of this attribute to - * be the vif's ip address. in case there is not a single - * ip address (0, or more than 1), this attribute will - * be skipped. - * BC_FILTER_MAGIC_MAC - set the val of this attribute to - * the LSB bytes of the vif's mac address - */ -enum { - BC_FILTER_MAGIC_NONE = 0, - BC_FILTER_MAGIC_IP, - BC_FILTER_MAGIC_MAC, -}; - -static const struct iwl_fw_bcast_filter iwl_mvm_default_bcast_filters[] = { - { - /* arp */ - .discard = 0, - .frame_type = BCAST_FILTER_FRAME_TYPE_ALL, - .attrs = { - { - /* frame type - arp, hw type - ethernet */ - .offset_type = - BCAST_FILTER_OFFSET_PAYLOAD_START, - .offset = sizeof(rfc1042_header), - .val = cpu_to_be32(0x08060001), - .mask = cpu_to_be32(0xffffffff), - }, - { - /* arp dest ip */ - .offset_type = - BCAST_FILTER_OFFSET_PAYLOAD_START, - .offset = sizeof(rfc1042_header) + 2 + - sizeof(struct arphdr) + - ETH_ALEN + sizeof(__be32) + - ETH_ALEN, - .mask = cpu_to_be32(0xffffffff), - /* mark it as special field */ - .reserved1 = cpu_to_le16(BC_FILTER_MAGIC_IP), - }, - }, - }, - { - /* dhcp offer bcast */ - .discard = 0, - .frame_type = BCAST_FILTER_FRAME_TYPE_IPV4, - .attrs = { - { - /* udp dest port - 68 (bootp client)*/ - .offset_type = BCAST_FILTER_OFFSET_IP_END, - .offset = offsetof(struct udphdr, dest), - .val = cpu_to_be32(0x00440000), - .mask = cpu_to_be32(0xffff0000), - }, - { - /* dhcp - lsb bytes of client hw address */ - .offset_type = BCAST_FILTER_OFFSET_IP_END, - .offset = 38, - .mask = cpu_to_be32(0xffffffff), - /* mark it as special field */ - .reserved1 = cpu_to_le16(BC_FILTER_MAGIC_MAC), - }, - }, - }, - /* last filter must be empty */ - {}, -}; -#endif - -void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type) -{ - if (!iwl_mvm_is_d0i3_supported(mvm)) - return; - - IWL_DEBUG_RPM(mvm, "Take mvm reference - type %d\n", ref_type); - spin_lock_bh(&mvm->refs_lock); - mvm->refs[ref_type]++; - spin_unlock_bh(&mvm->refs_lock); - iwl_trans_ref(mvm->trans); -} - -void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type) -{ - if (!iwl_mvm_is_d0i3_supported(mvm)) - return; - - IWL_DEBUG_RPM(mvm, "Leave mvm reference - type %d\n", ref_type); - spin_lock_bh(&mvm->refs_lock); - WARN_ON(!mvm->refs[ref_type]--); - spin_unlock_bh(&mvm->refs_lock); - iwl_trans_unref(mvm->trans); -} - -static void iwl_mvm_unref_all_except(struct iwl_mvm *mvm, - enum iwl_mvm_ref_type except_ref) -{ - int i, j; - - if (!iwl_mvm_is_d0i3_supported(mvm)) - return; - - spin_lock_bh(&mvm->refs_lock); - for (i = 0; i < IWL_MVM_REF_COUNT; i++) { - if (except_ref == i || !mvm->refs[i]) - continue; - - IWL_DEBUG_RPM(mvm, "Cleanup: remove mvm ref type %d (%d)\n", - i, mvm->refs[i]); - for (j = 0; j < mvm->refs[i]; j++) - iwl_trans_unref(mvm->trans); - mvm->refs[i] = 0; - } - spin_unlock_bh(&mvm->refs_lock); -} - -bool iwl_mvm_ref_taken(struct iwl_mvm *mvm) -{ - int i; - bool taken = false; - - if (!iwl_mvm_is_d0i3_supported(mvm)) - return true; - - spin_lock_bh(&mvm->refs_lock); - for (i = 0; i < IWL_MVM_REF_COUNT; i++) { - if (mvm->refs[i]) { - taken = true; - break; - } - } - spin_unlock_bh(&mvm->refs_lock); - - return taken; -} - -int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type) -{ - iwl_mvm_ref(mvm, ref_type); - - if (!wait_event_timeout(mvm->d0i3_exit_waitq, - !test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status), - HZ)) { - WARN_ON_ONCE(1); - iwl_mvm_unref(mvm, ref_type); - return -EIO; - } - - return 0; -} - -static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm) -{ - int i; - - memset(mvm->phy_ctxts, 0, sizeof(mvm->phy_ctxts)); - for (i = 0; i < NUM_PHY_CTX; i++) { - mvm->phy_ctxts[i].id = i; - mvm->phy_ctxts[i].ref = 0; - } -} - -struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy, - const char *alpha2, - enum iwl_mcc_source src_id, - bool *changed) -{ - struct ieee80211_regdomain *regd = NULL; - struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mcc_update_resp *resp; - - IWL_DEBUG_LAR(mvm, "Getting regdomain data for %s from FW\n", alpha2); - - lockdep_assert_held(&mvm->mutex); - - resp = iwl_mvm_update_mcc(mvm, alpha2, src_id); - if (IS_ERR_OR_NULL(resp)) { - IWL_DEBUG_LAR(mvm, "Could not get update from FW %d\n", - PTR_ERR_OR_ZERO(resp)); - goto out; - } - - if (changed) - *changed = (resp->status == MCC_RESP_NEW_CHAN_PROFILE); - - regd = iwl_parse_nvm_mcc_info(mvm->trans->dev, mvm->cfg, - __le32_to_cpu(resp->n_channels), - resp->channels, - __le16_to_cpu(resp->mcc)); - /* Store the return source id */ - src_id = resp->source_id; - kfree(resp); - if (IS_ERR_OR_NULL(regd)) { - IWL_DEBUG_LAR(mvm, "Could not get parse update from FW %d\n", - PTR_ERR_OR_ZERO(regd)); - goto out; - } - - IWL_DEBUG_LAR(mvm, "setting alpha2 from FW to %s (0x%x, 0x%x) src=%d\n", - regd->alpha2, regd->alpha2[0], regd->alpha2[1], src_id); - mvm->lar_regdom_set = true; - mvm->mcc_src = src_id; - -out: - return regd; -} - -void iwl_mvm_update_changed_regdom(struct iwl_mvm *mvm) -{ - bool changed; - struct ieee80211_regdomain *regd; - - if (!iwl_mvm_is_lar_supported(mvm)) - return; - - regd = iwl_mvm_get_current_regdomain(mvm, &changed); - if (!IS_ERR_OR_NULL(regd)) { - /* only update the regulatory core if changed */ - if (changed) - regulatory_set_wiphy_regd(mvm->hw->wiphy, regd); - - kfree(regd); - } -} - -struct ieee80211_regdomain *iwl_mvm_get_current_regdomain(struct iwl_mvm *mvm, - bool *changed) -{ - return iwl_mvm_get_regdomain(mvm->hw->wiphy, "ZZ", - iwl_mvm_is_wifi_mcc_supported(mvm) ? - MCC_SOURCE_GET_CURRENT : - MCC_SOURCE_OLD_FW, changed); -} - -int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm) -{ - enum iwl_mcc_source used_src; - struct ieee80211_regdomain *regd; - int ret; - bool changed; - const struct ieee80211_regdomain *r = - rtnl_dereference(mvm->hw->wiphy->regd); - - if (!r) - return -ENOENT; - - /* save the last source in case we overwrite it below */ - used_src = mvm->mcc_src; - if (iwl_mvm_is_wifi_mcc_supported(mvm)) { - /* Notify the firmware we support wifi location updates */ - regd = iwl_mvm_get_current_regdomain(mvm, NULL); - if (!IS_ERR_OR_NULL(regd)) - kfree(regd); - } - - /* Now set our last stored MCC and source */ - regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, r->alpha2, used_src, - &changed); - if (IS_ERR_OR_NULL(regd)) - return -EIO; - - /* update cfg80211 if the regdomain was changed */ - if (changed) - ret = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd); - else - ret = 0; - - kfree(regd); - return ret; -} - -int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) -{ - struct ieee80211_hw *hw = mvm->hw; - int num_mac, ret, i; - static const u32 mvm_ciphers[] = { - WLAN_CIPHER_SUITE_WEP40, - WLAN_CIPHER_SUITE_WEP104, - WLAN_CIPHER_SUITE_TKIP, - WLAN_CIPHER_SUITE_CCMP, - }; - - /* Tell mac80211 our characteristics */ - ieee80211_hw_set(hw, SIGNAL_DBM); - ieee80211_hw_set(hw, SPECTRUM_MGMT); - ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); - ieee80211_hw_set(hw, QUEUE_CONTROL); - ieee80211_hw_set(hw, WANT_MONITOR_VIF); - ieee80211_hw_set(hw, SUPPORTS_PS); - ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); - ieee80211_hw_set(hw, AMPDU_AGGREGATION); - ieee80211_hw_set(hw, TIMING_BEACON_ONLY); - ieee80211_hw_set(hw, CONNECTION_MONITOR); - ieee80211_hw_set(hw, CHANCTX_STA_CSA); - ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); - ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS); - - hw->queues = mvm->first_agg_queue; - hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE; - hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FEC | - IEEE80211_RADIOTAP_MCS_HAVE_STBC; - hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC | - IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED; - hw->rate_control_algorithm = "iwl-mvm-rs"; - hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES; - hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; - - BUILD_BUG_ON(ARRAY_SIZE(mvm->ciphers) < ARRAY_SIZE(mvm_ciphers) + 2); - memcpy(mvm->ciphers, mvm_ciphers, sizeof(mvm_ciphers)); - hw->wiphy->n_cipher_suites = ARRAY_SIZE(mvm_ciphers); - hw->wiphy->cipher_suites = mvm->ciphers; - - /* - * Enable 11w if advertised by firmware and software crypto - * is not enabled (as the firmware will interpret some mgmt - * packets, so enabling it with software crypto isn't safe) - */ - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_MFP && - !iwlwifi_mod_params.sw_crypto) { - ieee80211_hw_set(hw, MFP_CAPABLE); - mvm->ciphers[hw->wiphy->n_cipher_suites] = - WLAN_CIPHER_SUITE_AES_CMAC; - hw->wiphy->n_cipher_suites++; - } - - /* currently FW API supports only one optional cipher scheme */ - if (mvm->fw->cs[0].cipher) { - mvm->hw->n_cipher_schemes = 1; - mvm->hw->cipher_schemes = &mvm->fw->cs[0]; - mvm->ciphers[hw->wiphy->n_cipher_suites] = - mvm->fw->cs[0].cipher; - hw->wiphy->n_cipher_suites++; - } - - ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS); - hw->wiphy->features |= - NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR | - NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR | - NL80211_FEATURE_ND_RANDOM_MAC_ADDR; - - hw->sta_data_size = sizeof(struct iwl_mvm_sta); - hw->vif_data_size = sizeof(struct iwl_mvm_vif); - hw->chanctx_data_size = sizeof(u16); - - hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_P2P_DEVICE) | - BIT(NL80211_IFTYPE_ADHOC); - - hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; - hw->wiphy->regulatory_flags |= REGULATORY_ENABLE_RELAX_NO_IR; - if (iwl_mvm_is_lar_supported(mvm)) - hw->wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED; - else - hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | - REGULATORY_DISABLE_BEACON_HINTS; - - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD) - hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; - - hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; - - hw->wiphy->iface_combinations = iwl_mvm_iface_combinations; - hw->wiphy->n_iface_combinations = - ARRAY_SIZE(iwl_mvm_iface_combinations); - - hw->wiphy->max_remain_on_channel_duration = 10000; - hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; - /* we can compensate an offset of up to 3 channels = 15 MHz */ - hw->wiphy->max_adj_channel_rssi_comp = 3 * 5; - - /* 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; - - /* 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[i].addr[5]++; - hw->wiphy->n_addresses++; - } - - iwl_mvm_reset_phy_ctxts(mvm); - - hw->wiphy->max_scan_ie_len = iwl_mvm_max_scan_ie_len(mvm); - - hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX; - - BUILD_BUG_ON(IWL_MVM_SCAN_STOPPING_MASK & IWL_MVM_SCAN_MASK); - BUILD_BUG_ON(IWL_MVM_MAX_UMAC_SCANS > HWEIGHT32(IWL_MVM_SCAN_MASK) || - IWL_MVM_MAX_LMAC_SCANS > HWEIGHT32(IWL_MVM_SCAN_MASK)); - - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) - mvm->max_scans = IWL_MVM_MAX_UMAC_SCANS; - else - mvm->max_scans = IWL_MVM_MAX_LMAC_SCANS; - - if (mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels) - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = - &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ]; - if (mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels) { - hw->wiphy->bands[IEEE80211_BAND_5GHZ] = - &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ]; - - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_BEAMFORMER) && - fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_LQ_SS_PARAMS)) - hw->wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap.cap |= - IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE; - } - - hw->wiphy->hw_version = mvm->trans->hw_id; - - 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; - - hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; - hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX; - hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES; - /* we create the 802.11 header and zero length SSID IE. */ - hw->wiphy->max_sched_scan_ie_len = - SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2; - hw->wiphy->max_sched_scan_plans = IWL_MAX_SCHED_SCAN_PLANS; - hw->wiphy->max_sched_scan_plan_interval = U16_MAX; - - /* - * the firmware uses u8 for num of iterations, but 0xff is saved for - * infinite loop, so the maximum number of iterations is actually 254. - */ - hw->wiphy->max_sched_scan_plan_iterations = 254; - - hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN | - NL80211_FEATURE_LOW_PRIORITY_SCAN | - NL80211_FEATURE_P2P_GO_OPPPS | - NL80211_FEATURE_DYNAMIC_SMPS | - NL80211_FEATURE_STATIC_SMPS | - NL80211_FEATURE_SUPPORTS_WMM_ADMISSION; - - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT)) - hw->wiphy->features |= NL80211_FEATURE_TX_POWER_INSERTION; - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT)) - hw->wiphy->features |= NL80211_FEATURE_QUIET; - - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT)) - hw->wiphy->features |= - NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES; - - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT)) - hw->wiphy->features |= NL80211_FEATURE_WFA_TPC_IE_IN_PROBES; - - mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; - -#ifdef CONFIG_PM_SLEEP - if (iwl_mvm_is_d0i3_supported(mvm) && - device_can_wakeup(mvm->trans->dev)) { - mvm->wowlan.flags = WIPHY_WOWLAN_ANY; - hw->wiphy->wowlan = &mvm->wowlan; - } - - if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len && - mvm->trans->ops->d3_suspend && - mvm->trans->ops->d3_resume && - device_can_wakeup(mvm->trans->dev)) { - mvm->wowlan.flags |= WIPHY_WOWLAN_MAGIC_PKT | - WIPHY_WOWLAN_DISCONNECT | - WIPHY_WOWLAN_EAP_IDENTITY_REQ | - WIPHY_WOWLAN_RFKILL_RELEASE | - WIPHY_WOWLAN_NET_DETECT; - if (!iwlwifi_mod_params.sw_crypto) - mvm->wowlan.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | - WIPHY_WOWLAN_GTK_REKEY_FAILURE | - WIPHY_WOWLAN_4WAY_HANDSHAKE; - - mvm->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS; - mvm->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN; - mvm->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN; - mvm->wowlan.max_nd_match_sets = IWL_SCAN_MAX_PROFILES; - mvm->wowlan.tcp = &iwl_mvm_wowlan_tcp_support; - hw->wiphy->wowlan = &mvm->wowlan; - } -#endif - -#ifdef CONFIG_IWLWIFI_BCAST_FILTERING - /* assign default bcast filtering configuration */ - mvm->bcast_filters = iwl_mvm_default_bcast_filters; -#endif - - ret = iwl_mvm_leds_init(mvm); - if (ret) - return ret; - - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_TDLS_SUPPORT)) { - IWL_DEBUG_TDLS(mvm, "TDLS supported\n"); - hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; - ieee80211_hw_set(hw, TDLS_WIDER_BW); - } - - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH)) { - IWL_DEBUG_TDLS(mvm, "TDLS channel switch supported\n"); - hw->wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH; - } - - hw->netdev_features |= mvm->cfg->features; - if (!iwl_mvm_is_csum_supported(mvm)) - hw->netdev_features &= ~NETIF_F_RXCSUM; - - ret = ieee80211_register_hw(mvm->hw); - if (ret) - iwl_mvm_leds_exit(mvm); - - return ret; -} - -static bool iwl_mvm_defer_tx(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct sk_buff *skb) -{ - struct iwl_mvm_sta *mvmsta; - bool defer = false; - - /* - * double check the IN_D0I3 flag both before and after - * taking the spinlock, in order to prevent taking - * the spinlock when not needed. - */ - if (likely(!test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status))) - return false; - - spin_lock(&mvm->d0i3_tx_lock); - /* - * testing the flag again ensures the skb dequeue - * loop (on d0i3 exit) hasn't run yet. - */ - if (!test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status)) - goto out; - - mvmsta = iwl_mvm_sta_from_mac80211(sta); - if (mvmsta->sta_id == IWL_MVM_STATION_COUNT || - mvmsta->sta_id != mvm->d0i3_ap_sta_id) - goto out; - - __skb_queue_tail(&mvm->d0i3_tx, skb); - ieee80211_stop_queues(mvm->hw); - - /* trigger wakeup */ - iwl_mvm_ref(mvm, IWL_MVM_REF_TX); - iwl_mvm_unref(mvm, IWL_MVM_REF_TX); - - defer = true; -out: - spin_unlock(&mvm->d0i3_tx_lock); - return defer; -} - -static void iwl_mvm_mac_tx(struct ieee80211_hw *hw, - struct ieee80211_tx_control *control, - struct sk_buff *skb) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct ieee80211_sta *sta = control->sta; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_hdr *hdr = (void *)skb->data; - - if (iwl_mvm_is_radio_killed(mvm)) { - IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n"); - goto drop; - } - - if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE && - !test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status) && - !test_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status)) - goto drop; - - /* treat non-bufferable MMPDUs as broadcast if sta is sleeping */ - if (unlikely(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER && - ieee80211_is_mgmt(hdr->frame_control) && - !ieee80211_is_deauth(hdr->frame_control) && - !ieee80211_is_disassoc(hdr->frame_control) && - !ieee80211_is_action(hdr->frame_control))) - sta = NULL; - - if (sta) { - if (iwl_mvm_defer_tx(mvm, sta, skb)) - return; - if (iwl_mvm_tx_skb(mvm, skb, sta)) - goto drop; - return; - } - - if (iwl_mvm_tx_skb_non_sta(mvm, skb)) - goto drop; - return; - drop: - ieee80211_free_txskb(hw, skb); -} - -static inline bool iwl_enable_rx_ampdu(const struct iwl_cfg *cfg) -{ - if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG) - return false; - return true; -} - -static inline bool iwl_enable_tx_ampdu(const struct iwl_cfg *cfg) -{ - if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG) - return false; - if (iwlwifi_mod_params.disable_11n & IWL_ENABLE_HT_TXAGG) - return true; - - /* enabled by default */ - return true; -} - -#define CHECK_BA_TRIGGER(_mvm, _trig, _tid_bm, _tid, _fmt...) \ - do { \ - if (!(le16_to_cpu(_tid_bm) & BIT(_tid))) \ - break; \ - iwl_mvm_fw_dbg_collect_trig(_mvm, _trig, _fmt); \ - } while (0) - -static void -iwl_mvm_ampdu_check_trigger(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid, u16 rx_ba_ssn, - enum ieee80211_ampdu_mlme_action action) -{ - struct iwl_fw_dbg_trigger_tlv *trig; - struct iwl_fw_dbg_trigger_ba *ba_trig; - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA)) - return; - - trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA); - ba_trig = (void *)trig->data; - - if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig)) - return; - - switch (action) { - case IEEE80211_AMPDU_TX_OPERATIONAL: { - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; - - CHECK_BA_TRIGGER(mvm, trig, ba_trig->tx_ba_start, tid, - "TX AGG START: MAC %pM tid %d ssn %d\n", - sta->addr, tid, tid_data->ssn); - break; - } - case IEEE80211_AMPDU_TX_STOP_CONT: - CHECK_BA_TRIGGER(mvm, trig, ba_trig->tx_ba_stop, tid, - "TX AGG STOP: MAC %pM tid %d\n", - sta->addr, tid); - break; - case IEEE80211_AMPDU_RX_START: - CHECK_BA_TRIGGER(mvm, trig, ba_trig->rx_ba_start, tid, - "RX AGG START: MAC %pM tid %d ssn %d\n", - sta->addr, tid, rx_ba_ssn); - break; - case IEEE80211_AMPDU_RX_STOP: - CHECK_BA_TRIGGER(mvm, trig, ba_trig->rx_ba_stop, tid, - "RX AGG STOP: MAC %pM tid %d\n", - sta->addr, tid); - break; - default: - break; - } -} - -static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, - u16 *ssn, u8 buf_size, bool amsdu) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int ret; - bool tx_agg_ref = false; - - IWL_DEBUG_HT(mvm, "A-MPDU action on addr %pM tid %d: action %d\n", - sta->addr, tid, action); - - if (!(mvm->nvm_data->sku_cap_11n_enable)) - return -EACCES; - - /* return from D0i3 before starting a new Tx aggregation */ - switch (action) { - case IEEE80211_AMPDU_TX_START: - case IEEE80211_AMPDU_TX_STOP_CONT: - case IEEE80211_AMPDU_TX_STOP_FLUSH: - case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: - case IEEE80211_AMPDU_TX_OPERATIONAL: - /* - * for tx start, wait synchronously until D0i3 exit to - * get the correct sequence number for the tid. - * additionally, some other ampdu actions use direct - * target access, which is not handled automatically - * by the trans layer (unlike commands), so wait for - * d0i3 exit in these cases as well. - */ - ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_TX_AGG); - if (ret) - return ret; - - tx_agg_ref = true; - break; - default: - break; - } - - mutex_lock(&mvm->mutex); - - switch (action) { - case IEEE80211_AMPDU_RX_START: - if (!iwl_enable_rx_ampdu(mvm->cfg)) { - ret = -EINVAL; - break; - } - ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, *ssn, true); - break; - case IEEE80211_AMPDU_RX_STOP: - ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, 0, false); - break; - case IEEE80211_AMPDU_TX_START: - if (!iwl_enable_tx_ampdu(mvm->cfg)) { - 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_flush(mvm, vif, sta, tid); - break; - case IEEE80211_AMPDU_TX_OPERATIONAL: - ret = iwl_mvm_sta_tx_agg_oper(mvm, vif, sta, tid, buf_size); - break; - default: - WARN_ON_ONCE(1); - ret = -EINVAL; - break; - } - - if (!ret) { - u16 rx_ba_ssn = 0; - - if (action == IEEE80211_AMPDU_RX_START) - rx_ba_ssn = *ssn; - - iwl_mvm_ampdu_check_trigger(mvm, vif, sta, tid, - rx_ba_ssn, action); - } - mutex_unlock(&mvm->mutex); - - /* - * If the tid is marked as started, we won't use it for offloaded - * traffic on the next D0i3 entry. It's safe to unref. - */ - if (tx_agg_ref) - iwl_mvm_unref(mvm, IWL_MVM_REF_TX_AGG); - - return ret; -} - -static void iwl_mvm_cleanup_iterator(void *data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - mvmvif->uploaded = false; - mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; - - spin_lock_bh(&mvm->time_event_lock); - iwl_mvm_te_clear_data(mvm, &mvmvif->time_event_data); - spin_unlock_bh(&mvm->time_event_lock); - - mvmvif->phy_ctxt = NULL; - memset(&mvmvif->bf_data, 0, sizeof(mvmvif->bf_data)); -} - -static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count, - const void *data, size_t datalen) -{ - const struct iwl_mvm_dump_ptrs *dump_ptrs = data; - ssize_t bytes_read; - ssize_t bytes_read_trans; - - if (offset < dump_ptrs->op_mode_len) { - bytes_read = min_t(ssize_t, count, - dump_ptrs->op_mode_len - offset); - memcpy(buffer, (u8 *)dump_ptrs->op_mode_ptr + offset, - bytes_read); - offset += bytes_read; - count -= bytes_read; - - if (count == 0) - return bytes_read; - } else { - bytes_read = 0; - } - - if (!dump_ptrs->trans_ptr) - return bytes_read; - - offset -= dump_ptrs->op_mode_len; - bytes_read_trans = min_t(ssize_t, count, - dump_ptrs->trans_ptr->len - offset); - memcpy(buffer + bytes_read, - (u8 *)dump_ptrs->trans_ptr->data + offset, - bytes_read_trans); - - return bytes_read + bytes_read_trans; -} - -static void iwl_mvm_free_coredump(const void *data) -{ - const struct iwl_mvm_dump_ptrs *fw_error_dump = data; - - vfree(fw_error_dump->op_mode_ptr); - vfree(fw_error_dump->trans_ptr); - kfree(fw_error_dump); -} - -static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, - struct iwl_fw_error_dump_data **dump_data) -{ - struct iwl_fw_error_dump_fifo *fifo_hdr; - u32 *fifo_data; - u32 fifo_len; - unsigned long flags; - int i, j; - - if (!iwl_trans_grab_nic_access(mvm->trans, false, &flags)) - return; - - /* Pull RXF data from all RXFs */ - for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++) { - /* - * Keep aside the additional offset that might be needed for - * next RXF - */ - u32 offset_diff = RXF_DIFF_FROM_PREV * i; - - fifo_hdr = (void *)(*dump_data)->data; - fifo_data = (void *)fifo_hdr->data; - fifo_len = mvm->shared_mem_cfg.rxfifo_size[i]; - - /* No need to try to read the data if the length is 0 */ - if (fifo_len == 0) - continue; - - /* Add a TLV for the RXF */ - (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); - (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); - - fifo_hdr->fifo_num = cpu_to_le32(i); - fifo_hdr->available_bytes = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_RD_D_SPACE + - offset_diff)); - fifo_hdr->wr_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_RD_WR_PTR + - offset_diff)); - fifo_hdr->rd_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_RD_RD_PTR + - offset_diff)); - fifo_hdr->fence_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_RD_FENCE_PTR + - offset_diff)); - fifo_hdr->fence_mode = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_SET_FENCE_MODE + - offset_diff)); - - /* Lock fence */ - iwl_trans_write_prph(mvm->trans, - RXF_SET_FENCE_MODE + offset_diff, 0x1); - /* Set fence pointer to the same place like WR pointer */ - iwl_trans_write_prph(mvm->trans, - RXF_LD_WR2FENCE + offset_diff, 0x1); - /* Set fence offset */ - iwl_trans_write_prph(mvm->trans, - RXF_LD_FENCE_OFFSET_ADDR + offset_diff, - 0x0); - - /* Read FIFO */ - fifo_len /= sizeof(u32); /* Size in DWORDS */ - for (j = 0; j < fifo_len; j++) - fifo_data[j] = iwl_trans_read_prph(mvm->trans, - RXF_FIFO_RD_FENCE_INC + - offset_diff); - *dump_data = iwl_fw_error_next_data(*dump_data); - } - - /* Pull TXF data from all TXFs */ - for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++) { - /* Mark the number of TXF we're pulling now */ - iwl_trans_write_prph(mvm->trans, TXF_LARC_NUM, i); - - fifo_hdr = (void *)(*dump_data)->data; - fifo_data = (void *)fifo_hdr->data; - fifo_len = mvm->shared_mem_cfg.txfifo_size[i]; - - /* No need to try to read the data if the length is 0 */ - if (fifo_len == 0) - continue; - - /* Add a TLV for the FIFO */ - (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF); - (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); - - fifo_hdr->fifo_num = cpu_to_le32(i); - fifo_hdr->available_bytes = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_FIFO_ITEM_CNT)); - fifo_hdr->wr_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_WR_PTR)); - fifo_hdr->rd_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_RD_PTR)); - fifo_hdr->fence_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_FENCE_PTR)); - fifo_hdr->fence_mode = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_LOCK_FENCE)); - - /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */ - iwl_trans_write_prph(mvm->trans, TXF_READ_MODIFY_ADDR, - TXF_WR_PTR); - - /* Dummy-read to advance the read pointer to the head */ - iwl_trans_read_prph(mvm->trans, TXF_READ_MODIFY_DATA); - - /* Read FIFO */ - fifo_len /= sizeof(u32); /* Size in DWORDS */ - for (j = 0; j < fifo_len; j++) - fifo_data[j] = iwl_trans_read_prph(mvm->trans, - TXF_READ_MODIFY_DATA); - *dump_data = iwl_fw_error_next_data(*dump_data); - } - - iwl_trans_release_nic_access(mvm->trans, &flags); -} - -void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm) -{ - if (mvm->fw_dump_desc == &iwl_mvm_dump_desc_assert || - !mvm->fw_dump_desc) - return; - - kfree(mvm->fw_dump_desc); - mvm->fw_dump_desc = NULL; -} - -#define IWL8260_ICCM_OFFSET 0x44000 /* Only for B-step */ -#define IWL8260_ICCM_LEN 0xC000 /* Only for B-step */ - -void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) -{ - struct iwl_fw_error_dump_file *dump_file; - struct iwl_fw_error_dump_data *dump_data; - struct iwl_fw_error_dump_info *dump_info; - struct iwl_fw_error_dump_mem *dump_mem; - struct iwl_fw_error_dump_trigger_desc *dump_trig; - struct iwl_mvm_dump_ptrs *fw_error_dump; - u32 sram_len, sram_ofs; - u32 file_len, fifo_data_len = 0; - u32 smem_len = mvm->cfg->smem_len; - u32 sram2_len = mvm->cfg->dccm2_len; - bool monitor_dump_only = false; - - lockdep_assert_held(&mvm->mutex); - - /* there's no point in fw dump if the bus is dead */ - if (test_bit(STATUS_TRANS_DEAD, &mvm->trans->status)) { - IWL_ERR(mvm, "Skip fw error dump since bus is dead\n"); - return; - } - - if (mvm->fw_dump_trig && - mvm->fw_dump_trig->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY) - monitor_dump_only = true; - - fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL); - if (!fw_error_dump) - return; - - /* SRAM - include stack CCM if driver knows the values for it */ - if (!mvm->cfg->dccm_offset || !mvm->cfg->dccm_len) { - const struct fw_img *img; - - img = &mvm->fw->img[mvm->cur_ucode]; - sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; - sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; - } else { - sram_ofs = mvm->cfg->dccm_offset; - sram_len = mvm->cfg->dccm_len; - } - - /* reading RXF/TXF sizes */ - if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) { - struct iwl_mvm_shared_mem_cfg *mem_cfg = &mvm->shared_mem_cfg; - int i; - - fifo_data_len = 0; - - /* Count RXF size */ - for (i = 0; i < ARRAY_SIZE(mem_cfg->rxfifo_size); i++) { - if (!mem_cfg->rxfifo_size[i]) - continue; - - /* Add header info */ - fifo_data_len += mem_cfg->rxfifo_size[i] + - sizeof(*dump_data) + - sizeof(struct iwl_fw_error_dump_fifo); - } - - for (i = 0; i < ARRAY_SIZE(mem_cfg->txfifo_size); i++) { - if (!mem_cfg->txfifo_size[i]) - continue; - - /* Add header info */ - fifo_data_len += mem_cfg->txfifo_size[i] + - sizeof(*dump_data) + - sizeof(struct iwl_fw_error_dump_fifo); - } - } - - file_len = sizeof(*dump_file) + - sizeof(*dump_data) * 2 + - sram_len + sizeof(*dump_mem) + - fifo_data_len + - sizeof(*dump_info); - - /* Make room for the SMEM, if it exists */ - if (smem_len) - file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len; - - /* Make room for the secondary SRAM, if it exists */ - if (sram2_len) - file_len += sizeof(*dump_data) + sizeof(*dump_mem) + sram2_len; - - /* Make room for fw's virtual image pages, if it exists */ - if (mvm->fw->img[mvm->cur_ucode].paging_mem_size) - file_len += mvm->num_of_paging_blk * - (sizeof(*dump_data) + - sizeof(struct iwl_fw_error_dump_paging) + - PAGING_BLOCK_SIZE); - - /* If we only want a monitor dump, reset the file length */ - if (monitor_dump_only) { - file_len = sizeof(*dump_file) + sizeof(*dump_data) + - sizeof(*dump_info); - } - - /* - * In 8000 HW family B-step include the ICCM (which resides separately) - */ - if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 && - CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) - file_len += sizeof(*dump_data) + sizeof(*dump_mem) + - IWL8260_ICCM_LEN; - - if (mvm->fw_dump_desc) - file_len += sizeof(*dump_data) + sizeof(*dump_trig) + - mvm->fw_dump_desc->len; - - dump_file = vzalloc(file_len); - if (!dump_file) { - kfree(fw_error_dump); - iwl_mvm_free_fw_dump_desc(mvm); - return; - } - - fw_error_dump->op_mode_ptr = dump_file; - - dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER); - dump_data = (void *)dump_file->data; - - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO); - dump_data->len = cpu_to_le32(sizeof(*dump_info)); - dump_info = (void *) dump_data->data; - dump_info->device_family = - mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ? - cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) : - cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8); - dump_info->hw_step = cpu_to_le32(CSR_HW_REV_STEP(mvm->trans->hw_rev)); - memcpy(dump_info->fw_human_readable, mvm->fw->human_readable, - sizeof(dump_info->fw_human_readable)); - strncpy(dump_info->dev_human_readable, mvm->cfg->name, - sizeof(dump_info->dev_human_readable)); - strncpy(dump_info->bus_human_readable, mvm->dev->bus->name, - sizeof(dump_info->bus_human_readable)); - - dump_data = iwl_fw_error_next_data(dump_data); - /* We only dump the FIFOs if the FW is in error state */ - if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) - iwl_mvm_dump_fifos(mvm, &dump_data); - - if (mvm->fw_dump_desc) { - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO); - dump_data->len = cpu_to_le32(sizeof(*dump_trig) + - mvm->fw_dump_desc->len); - dump_trig = (void *)dump_data->data; - memcpy(dump_trig, &mvm->fw_dump_desc->trig_desc, - sizeof(*dump_trig) + mvm->fw_dump_desc->len); - - /* now we can free this copy */ - iwl_mvm_free_fw_dump_desc(mvm); - dump_data = iwl_fw_error_next_data(dump_data); - } - - /* In case we only want monitor dump, skip to dump trasport data */ - if (monitor_dump_only) - goto dump_trans_data; - - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); - dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem)); - dump_mem = (void *)dump_data->data; - dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); - dump_mem->offset = cpu_to_le32(sram_ofs); - iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_mem->data, - sram_len); - - if (smem_len) { - dump_data = iwl_fw_error_next_data(dump_data); - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); - dump_data->len = cpu_to_le32(smem_len + sizeof(*dump_mem)); - dump_mem = (void *)dump_data->data; - dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM); - dump_mem->offset = cpu_to_le32(mvm->cfg->smem_offset); - iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->smem_offset, - dump_mem->data, smem_len); - } - - if (sram2_len) { - dump_data = iwl_fw_error_next_data(dump_data); - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); - dump_data->len = cpu_to_le32(sram2_len + sizeof(*dump_mem)); - dump_mem = (void *)dump_data->data; - dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); - dump_mem->offset = cpu_to_le32(mvm->cfg->dccm2_offset); - iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->dccm2_offset, - dump_mem->data, sram2_len); - } - - if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 && - CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) { - dump_data = iwl_fw_error_next_data(dump_data); - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); - dump_data->len = cpu_to_le32(IWL8260_ICCM_LEN + - sizeof(*dump_mem)); - dump_mem = (void *)dump_data->data; - dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); - dump_mem->offset = cpu_to_le32(IWL8260_ICCM_OFFSET); - iwl_trans_read_mem_bytes(mvm->trans, IWL8260_ICCM_OFFSET, - dump_mem->data, IWL8260_ICCM_LEN); - } - - /* Dump fw's virtual image */ - if (mvm->fw->img[mvm->cur_ucode].paging_mem_size) { - u32 i; - - for (i = 1; i < mvm->num_of_paging_blk + 1; i++) { - struct iwl_fw_error_dump_paging *paging; - struct page *pages = - mvm->fw_paging_db[i].fw_paging_block; - - dump_data = iwl_fw_error_next_data(dump_data); - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING); - dump_data->len = cpu_to_le32(sizeof(*paging) + - PAGING_BLOCK_SIZE); - paging = (void *)dump_data->data; - paging->index = cpu_to_le32(i); - memcpy(paging->data, page_address(pages), - PAGING_BLOCK_SIZE); - } - } - -dump_trans_data: - fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans, - mvm->fw_dump_trig); - fw_error_dump->op_mode_len = file_len; - if (fw_error_dump->trans_ptr) - file_len += fw_error_dump->trans_ptr->len; - dump_file->file_len = cpu_to_le32(file_len); - - dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0, - GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump); - - mvm->fw_dump_trig = NULL; - clear_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status); -} - -struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert = { - .trig_desc = { - .type = cpu_to_le32(FW_DBG_TRIGGER_FW_ASSERT), - }, -}; - -static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) -{ - /* clear the D3 reconfig, we only need it to avoid dumping a - * firmware coredump on reconfiguration, we shouldn't do that - * on D3->D0 transition - */ - if (!test_and_clear_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status)) { - mvm->fw_dump_desc = &iwl_mvm_dump_desc_assert; - iwl_mvm_fw_error_dump(mvm); - } - - /* cleanup all stale references (scan, roc), but keep the - * ucode_down ref until reconfig is complete - */ - iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN); - - iwl_trans_stop_device(mvm->trans); - - mvm->scan_status = 0; - mvm->ps_disabled = false; - mvm->calibrating = false; - - /* just in case one was running */ - ieee80211_remain_on_channel_expired(mvm->hw); - - /* - * cleanup all interfaces, even inactive ones, as some might have - * gone down during the HW restart - */ - ieee80211_iterate_interfaces(mvm->hw, 0, iwl_mvm_cleanup_iterator, mvm); - - mvm->p2p_device_vif = NULL; - mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; - - iwl_mvm_reset_phy_ctxts(mvm); - memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained)); - memset(mvm->tfd_drained, 0, sizeof(mvm->tfd_drained)); - memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif)); - memset(&mvm->last_bt_notif_old, 0, sizeof(mvm->last_bt_notif_old)); - memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd)); - memset(&mvm->last_bt_ci_cmd_old, 0, sizeof(mvm->last_bt_ci_cmd_old)); - memset(&mvm->bt_ack_kill_msk, 0, sizeof(mvm->bt_ack_kill_msk)); - memset(&mvm->bt_cts_kill_msk, 0, sizeof(mvm->bt_cts_kill_msk)); - - ieee80211_wake_queues(mvm->hw); - - /* clear any stale d0i3 state */ - clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status); - - mvm->vif_count = 0; - mvm->rx_ba_sessions = 0; - mvm->fw_dbg_conf = FW_DBG_INVALID; - - /* keep statistics ticking */ - iwl_mvm_accu_radio_stats(mvm); -} - -int __iwl_mvm_mac_start(struct iwl_mvm *mvm) -{ - int ret; - - lockdep_assert_held(&mvm->mutex); - - /* Clean up some internal and mac80211 state on restart */ - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) - iwl_mvm_restart_cleanup(mvm); - - ret = iwl_mvm_up(mvm); - - if (ret && test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { - /* Something went wrong - we need to finish some cleanup - * that normally iwl_mvm_mac_restart_complete() below - * would do. - */ - clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); - iwl_mvm_d0i3_enable_tx(mvm, NULL); - } - - return ret; -} - -static int iwl_mvm_mac_start(struct ieee80211_hw *hw) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int ret; - - /* Some hw restart cleanups must not hold the mutex */ - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { - /* - * Make sure we are out of d0i3. This is needed - * to make sure the reference accounting is correct - * (and there is no stale d0i3_exit_work). - */ - wait_event_timeout(mvm->d0i3_exit_waitq, - !test_bit(IWL_MVM_STATUS_IN_D0I3, - &mvm->status), - HZ); - } - - mutex_lock(&mvm->mutex); - ret = __iwl_mvm_mac_start(mvm); - mutex_unlock(&mvm->mutex); - - return ret; -} - -static void iwl_mvm_restart_complete(struct iwl_mvm *mvm) -{ - int ret; - - mutex_lock(&mvm->mutex); - - clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); - iwl_mvm_d0i3_enable_tx(mvm, NULL); - ret = iwl_mvm_update_quotas(mvm, true, NULL); - if (ret) - IWL_ERR(mvm, "Failed to update quotas after restart (%d)\n", - ret); - - /* allow transport/FW low power modes */ - iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN); - - /* - * If we have TDLS peers, remove them. We don't know the last seqno/PN - * of packets the FW sent out, so we must reconnect. - */ - iwl_mvm_teardown_tdls_peers(mvm); - - mutex_unlock(&mvm->mutex); -} - -static void iwl_mvm_resume_complete(struct iwl_mvm *mvm) -{ - if (!iwl_mvm_is_d0i3_supported(mvm)) - return; - - if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) - if (!wait_event_timeout(mvm->d0i3_exit_waitq, - !test_bit(IWL_MVM_STATUS_IN_D0I3, - &mvm->status), - HZ)) - WARN_ONCE(1, "D0i3 exit on resume timed out\n"); -} - -static void -iwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw, - enum ieee80211_reconfig_type reconfig_type) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - switch (reconfig_type) { - case IEEE80211_RECONFIG_TYPE_RESTART: - iwl_mvm_restart_complete(mvm); - break; - case IEEE80211_RECONFIG_TYPE_SUSPEND: - iwl_mvm_resume_complete(mvm); - break; - } -} - -void __iwl_mvm_mac_stop(struct iwl_mvm *mvm) -{ - lockdep_assert_held(&mvm->mutex); - - /* firmware counters are obviously reset now, but we shouldn't - * partially track so also clear the fw_reset_accu counters. - */ - memset(&mvm->accu_radio_stats, 0, sizeof(mvm->accu_radio_stats)); - - /* - * Disallow low power states when the FW is down by taking - * the UCODE_DOWN ref. in case of ongoing hw restart the - * ref is already taken, so don't take it again. - */ - if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) - iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); - - /* async_handlers_wk is now blocked */ - - /* - * The work item could be running or queued if the - * ROC time event stops just as we get here. - */ - flush_work(&mvm->roc_done_wk); - - iwl_trans_stop_device(mvm->trans); - - iwl_mvm_async_handlers_purge(mvm); - /* async_handlers_list is empty and will stay empty: HW is stopped */ - - /* the fw is stopped, the aux sta is dead: clean up driver state */ - iwl_mvm_del_aux_sta(mvm); - - /* - * Clear IN_HW_RESTART flag when stopping the hw (as restart_complete() - * won't be called in this case). - * But make sure to cleanup interfaces that have gone down before/during - * HW restart was requested. - */ - if (test_and_clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) - ieee80211_iterate_interfaces(mvm->hw, 0, - iwl_mvm_cleanup_iterator, mvm); - - /* We shouldn't have any UIDs still set. Loop over all the UIDs to - * make sure there's nothing left there and warn if any is found. - */ - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { - int i; - - for (i = 0; i < mvm->max_scans; i++) { - if (WARN_ONCE(mvm->scan_uid_status[i], - "UMAC scan UID %d status was not cleaned\n", - i)) - mvm->scan_uid_status[i] = 0; - } - } - - mvm->ucode_loaded = false; -} - -static void iwl_mvm_mac_stop(struct ieee80211_hw *hw) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - flush_work(&mvm->d0i3_exit_work); - flush_work(&mvm->async_handlers_wk); - cancel_delayed_work_sync(&mvm->fw_dump_wk); - iwl_mvm_free_fw_dump_desc(mvm); - - mutex_lock(&mvm->mutex); - __iwl_mvm_mac_stop(mvm); - mutex_unlock(&mvm->mutex); - - /* - * The worker might have been waiting for the mutex, let it run and - * discover that its list is now empty. - */ - cancel_work_sync(&mvm->async_handlers_wk); -} - -static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm) -{ - u16 i; - - lockdep_assert_held(&mvm->mutex); - - for (i = 0; i < NUM_PHY_CTX; i++) - if (!mvm->phy_ctxts[i].ref) - return &mvm->phy_ctxts[i]; - - IWL_ERR(mvm, "No available PHY context\n"); - return NULL; -} - -static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - s16 tx_power) -{ - struct iwl_dev_tx_power_cmd cmd = { - .v2.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_MAC), - .v2.mac_context_id = - cpu_to_le32(iwl_mvm_vif_from_mac80211(vif)->id), - .v2.pwr_restriction = cpu_to_le16(8 * tx_power), - }; - int len = sizeof(cmd); - - if (tx_power == IWL_DEFAULT_MAX_TX_POWER) - cmd.v2.pwr_restriction = cpu_to_le16(IWL_DEV_MAX_TX_POWER); - - if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_TX_POWER_CHAIN)) - len = sizeof(cmd.v2); - - return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, len, &cmd); -} - -static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; - - mvmvif->mvm = mvm; - - /* - * make sure D0i3 exit is completed, otherwise a target access - * during tx queue configuration could be done when still in - * D0i3 state. - */ - ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_ADD_IF); - if (ret) - return ret; - - /* - * Not much to do here. The stack will not allow interface - * types or combinations that we didn't advertise, so we - * don't really have to check the types. - */ - - mutex_lock(&mvm->mutex); - - /* make sure that beacon statistics don't go backwards with FW reset */ - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) - mvmvif->beacon_stats.accu_num_beacons += - mvmvif->beacon_stats.num_beacons; - - /* Allocate resources for the MAC context, and add it to the fw */ - ret = iwl_mvm_mac_ctxt_init(mvm, vif); - if (ret) - goto out_unlock; - - /* Counting number of interfaces is needed for legacy PM */ - if (vif->type != NL80211_IFTYPE_P2P_DEVICE) - mvm->vif_count++; - - /* - * The AP binding flow can be done only after the beacon - * template is configured (which happens only in the mac80211 - * start_ap() flow), and adding the broadcast station can happen - * only after the binding. - * In addition, since modifying the MAC before adding a bcast - * station is not allowed by the FW, delay the adding of MAC context to - * the point where we can also add the bcast station. - * In short: there's not much we can do at this point, other than - * allocating resources :) - */ - if (vif->type == NL80211_IFTYPE_AP || - vif->type == NL80211_IFTYPE_ADHOC) { - ret = iwl_mvm_alloc_bcast_sta(mvm, vif); - if (ret) { - IWL_ERR(mvm, "Failed to allocate bcast sta\n"); - goto out_release; - } - - iwl_mvm_vif_dbgfs_register(mvm, vif); - goto out_unlock; - } - - mvmvif->features |= hw->netdev_features; - - ret = iwl_mvm_mac_ctxt_add(mvm, vif); - if (ret) - goto out_release; - - ret = iwl_mvm_power_update_mac(mvm); - if (ret) - goto out_remove_mac; - - /* beacon filtering */ - ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); - if (ret) - goto out_remove_mac; - - if (!mvm->bf_allowed_vif && - vif->type == NL80211_IFTYPE_STATION && !vif->p2p) { - mvm->bf_allowed_vif = mvmvif; - vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | - IEEE80211_VIF_SUPPORTS_CQM_RSSI; - } - - /* - * P2P_DEVICE interface does not have a channel context assigned to it, - * so a dedicated PHY context is allocated to it and the corresponding - * MAC context is bound to it at this stage. - */ - if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { - - mvmvif->phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); - if (!mvmvif->phy_ctxt) { - ret = -ENOSPC; - goto out_free_bf; - } - - iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt); - ret = iwl_mvm_binding_add_vif(mvm, vif); - if (ret) - goto out_unref_phy; - - ret = iwl_mvm_add_bcast_sta(mvm, vif); - if (ret) - goto out_unbind; - - /* Save a pointer to p2p device vif, so it can later be used to - * update the p2p device MAC when a GO is started/stopped */ - mvm->p2p_device_vif = vif; - } - - iwl_mvm_vif_dbgfs_register(mvm, vif); - goto out_unlock; - - out_unbind: - iwl_mvm_binding_remove_vif(mvm, vif); - out_unref_phy: - iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); - out_free_bf: - if (mvm->bf_allowed_vif == mvmvif) { - mvm->bf_allowed_vif = NULL; - vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER | - IEEE80211_VIF_SUPPORTS_CQM_RSSI); - } - out_remove_mac: - mvmvif->phy_ctxt = NULL; - iwl_mvm_mac_ctxt_remove(mvm, vif); - out_release: - if (vif->type != NL80211_IFTYPE_P2P_DEVICE) - mvm->vif_count--; - - iwl_mvm_mac_ctxt_release(mvm, vif); - out_unlock: - mutex_unlock(&mvm->mutex); - - iwl_mvm_unref(mvm, IWL_MVM_REF_ADD_IF); - - return ret; -} - -static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - u32 tfd_msk = iwl_mvm_mac_get_queues_mask(vif); - - if (tfd_msk) { - /* - * mac80211 first removes all the stations of the vif and - * then removes the vif. When it removes a station it also - * flushes the AMPDU session. So by now, all the AMPDU sessions - * of all the stations of this vif are closed, and the queues - * of these AMPDU sessions are properly closed. - * We still need to take care of the shared queues of the vif. - * Flush them here. - */ - mutex_lock(&mvm->mutex); - iwl_mvm_flush_tx_path(mvm, tfd_msk, 0); - mutex_unlock(&mvm->mutex); - - /* - * There are transports that buffer a few frames in the host. - * For these, the flush above isn't enough since while we were - * flushing, the transport might have sent more frames to the - * device. To solve this, wait here until the transport is - * empty. Technically, this could have replaced the flush - * above, but flush is much faster than draining. So flush - * first, and drain to make sure we have no frames in the - * transport anymore. - * If a station still had frames on the shared queues, it is - * already marked as draining, so to complete the draining, we - * just need to wait until the transport is empty. - */ - iwl_trans_wait_tx_queue_empty(mvm->trans, tfd_msk); - } - - if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { - /* - * Flush the ROC worker which will flush the OFFCHANNEL queue. - * We assume here that all the packets sent to the OFFCHANNEL - * queue are sent in ROC session. - */ - flush_work(&mvm->roc_done_wk); - } else { - /* - * By now, all the AC queues are empty. The AGG queues are - * empty too. We already got all the Tx responses for all the - * packets in the queues. The drain work can have been - * triggered. Flush it. - */ - flush_work(&mvm->sta_drained_wk); - } -} - -static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - iwl_mvm_prepare_mac_removal(mvm, vif); - - mutex_lock(&mvm->mutex); - - if (mvm->bf_allowed_vif == mvmvif) { - mvm->bf_allowed_vif = NULL; - vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER | - IEEE80211_VIF_SUPPORTS_CQM_RSSI); - } - - 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. - */ - if (vif->type == NL80211_IFTYPE_AP || - vif->type == NL80211_IFTYPE_ADHOC) { -#ifdef CONFIG_NL80211_TESTMODE - if (vif == mvm->noa_vif) { - mvm->noa_vif = NULL; - mvm->noa_duration = 0; - } -#endif - iwl_mvm_dealloc_bcast_sta(mvm, vif); - goto out_release; - } - - if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { - mvm->p2p_device_vif = NULL; - iwl_mvm_rm_bcast_sta(mvm, vif); - iwl_mvm_binding_remove_vif(mvm, vif); - iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); - mvmvif->phy_ctxt = NULL; - } - - if (mvm->vif_count && vif->type != NL80211_IFTYPE_P2P_DEVICE) - mvm->vif_count--; - - iwl_mvm_power_update_mac(mvm); - iwl_mvm_mac_ctxt_remove(mvm, vif); - -out_release: - iwl_mvm_mac_ctxt_release(mvm, vif); - mutex_unlock(&mvm->mutex); -} - -static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed) -{ - return 0; -} - -struct iwl_mvm_mc_iter_data { - struct iwl_mvm *mvm; - int port_id; -}; - -static void iwl_mvm_mc_iface_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_mc_iter_data *data = _data; - struct iwl_mvm *mvm = data->mvm; - struct iwl_mcast_filter_cmd *cmd = mvm->mcast_filter_cmd; - int ret, len; - - /* if we don't have free ports, mcast frames will be dropped */ - if (WARN_ON_ONCE(data->port_id >= MAX_PORT_ID_NUM)) - return; - - if (vif->type != NL80211_IFTYPE_STATION || - !vif->bss_conf.assoc) - return; - - cmd->port_id = data->port_id++; - memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN); - len = roundup(sizeof(*cmd) + cmd->count * ETH_ALEN, 4); - - ret = iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_ASYNC, len, cmd); - if (ret) - IWL_ERR(mvm, "mcast filter cmd error. ret=%d\n", ret); -} - -static void iwl_mvm_recalc_multicast(struct iwl_mvm *mvm) -{ - struct iwl_mvm_mc_iter_data iter_data = { - .mvm = mvm, - }; - - lockdep_assert_held(&mvm->mutex); - - if (WARN_ON_ONCE(!mvm->mcast_filter_cmd)) - return; - - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_mc_iface_iterator, &iter_data); -} - -static u64 iwl_mvm_prepare_multicast(struct ieee80211_hw *hw, - struct netdev_hw_addr_list *mc_list) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mcast_filter_cmd *cmd; - struct netdev_hw_addr *addr; - int addr_count; - bool pass_all; - int len; - - addr_count = netdev_hw_addr_list_count(mc_list); - pass_all = addr_count > MAX_MCAST_FILTERING_ADDRESSES || - IWL_MVM_FW_MCAST_FILTER_PASS_ALL; - if (pass_all) - addr_count = 0; - - len = roundup(sizeof(*cmd) + addr_count * ETH_ALEN, 4); - cmd = kzalloc(len, GFP_ATOMIC); - if (!cmd) - return 0; - - if (pass_all) { - cmd->pass_all = 1; - return (u64)(unsigned long)cmd; - } - - netdev_hw_addr_list_for_each(addr, mc_list) { - IWL_DEBUG_MAC80211(mvm, "mcast addr (%d): %pM\n", - cmd->count, addr->addr); - memcpy(&cmd->addr_list[cmd->count * ETH_ALEN], - addr->addr, ETH_ALEN); - cmd->count++; - } - - return (u64)(unsigned long)cmd; -} - -static void iwl_mvm_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, - u64 multicast) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mcast_filter_cmd *cmd = (void *)(unsigned long)multicast; - - mutex_lock(&mvm->mutex); - - /* replace previous configuration */ - kfree(mvm->mcast_filter_cmd); - mvm->mcast_filter_cmd = cmd; - - if (!cmd) - goto out; - - iwl_mvm_recalc_multicast(mvm); -out: - mutex_unlock(&mvm->mutex); - *total_flags = 0; -} - -static void iwl_mvm_config_iface_filter(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - unsigned int filter_flags, - unsigned int changed_flags) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - /* We support only filter for probe requests */ - if (!(changed_flags & FIF_PROBE_REQ)) - return; - - /* Supported only for p2p client interfaces */ - if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc || - !vif->p2p) - return; - - mutex_lock(&mvm->mutex); - iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); - mutex_unlock(&mvm->mutex); -} - -#ifdef CONFIG_IWLWIFI_BCAST_FILTERING -struct iwl_bcast_iter_data { - struct iwl_mvm *mvm; - struct iwl_bcast_filter_cmd *cmd; - u8 current_filter; -}; - -static void -iwl_mvm_set_bcast_filter(struct ieee80211_vif *vif, - const struct iwl_fw_bcast_filter *in_filter, - struct iwl_fw_bcast_filter *out_filter) -{ - struct iwl_fw_bcast_filter_attr *attr; - int i; - - memcpy(out_filter, in_filter, sizeof(*out_filter)); - - for (i = 0; i < ARRAY_SIZE(out_filter->attrs); i++) { - attr = &out_filter->attrs[i]; - - if (!attr->mask) - break; - - switch (attr->reserved1) { - case cpu_to_le16(BC_FILTER_MAGIC_IP): - if (vif->bss_conf.arp_addr_cnt != 1) { - attr->mask = 0; - continue; - } - - attr->val = vif->bss_conf.arp_addr_list[0]; - break; - case cpu_to_le16(BC_FILTER_MAGIC_MAC): - attr->val = *(__be32 *)&vif->addr[2]; - break; - default: - break; - } - attr->reserved1 = 0; - out_filter->num_attrs++; - } -} - -static void iwl_mvm_bcast_filter_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_bcast_iter_data *data = _data; - struct iwl_mvm *mvm = data->mvm; - struct iwl_bcast_filter_cmd *cmd = data->cmd; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_fw_bcast_mac *bcast_mac; - int i; - - if (WARN_ON(mvmvif->id >= ARRAY_SIZE(cmd->macs))) - return; - - bcast_mac = &cmd->macs[mvmvif->id]; - - /* - * enable filtering only for associated stations, but not for P2P - * Clients - */ - if (vif->type != NL80211_IFTYPE_STATION || vif->p2p || - !vif->bss_conf.assoc) - return; - - bcast_mac->default_discard = 1; - - /* copy all configured filters */ - for (i = 0; mvm->bcast_filters[i].attrs[0].mask; i++) { - /* - * Make sure we don't exceed our filters limit. - * if there is still a valid filter to be configured, - * be on the safe side and just allow bcast for this mac. - */ - if (WARN_ON_ONCE(data->current_filter >= - ARRAY_SIZE(cmd->filters))) { - bcast_mac->default_discard = 0; - bcast_mac->attached_filters = 0; - break; - } - - iwl_mvm_set_bcast_filter(vif, - &mvm->bcast_filters[i], - &cmd->filters[data->current_filter]); - - /* skip current filter if it contains no attributes */ - if (!cmd->filters[data->current_filter].num_attrs) - continue; - - /* attach the filter to current mac */ - bcast_mac->attached_filters |= - cpu_to_le16(BIT(data->current_filter)); - - data->current_filter++; - } -} - -bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm, - struct iwl_bcast_filter_cmd *cmd) -{ - struct iwl_bcast_iter_data iter_data = { - .mvm = mvm, - .cmd = cmd, - }; - - if (IWL_MVM_FW_BCAST_FILTER_PASS_ALL) - return false; - - memset(cmd, 0, sizeof(*cmd)); - cmd->max_bcast_filters = ARRAY_SIZE(cmd->filters); - cmd->max_macs = ARRAY_SIZE(cmd->macs); - -#ifdef CONFIG_IWLWIFI_DEBUGFS - /* use debugfs filters/macs if override is configured */ - if (mvm->dbgfs_bcast_filtering.override) { - memcpy(cmd->filters, &mvm->dbgfs_bcast_filtering.cmd.filters, - sizeof(cmd->filters)); - memcpy(cmd->macs, &mvm->dbgfs_bcast_filtering.cmd.macs, - sizeof(cmd->macs)); - return true; - } -#endif - - /* if no filters are configured, do nothing */ - if (!mvm->bcast_filters) - return false; - - /* configure and attach these filters for each associated sta vif */ - ieee80211_iterate_active_interfaces( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_bcast_filter_iterator, &iter_data); - - return true; -} -static int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct iwl_bcast_filter_cmd cmd; - - if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING)) - return 0; - - if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) - return 0; - - return iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0, - sizeof(cmd), &cmd); -} -#else -static inline int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - return 0; -} -#endif - -static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *bss_conf, - u32 changes) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; - - /* - * Re-calculate the tsf id, as the master-slave relations depend on the - * beacon interval, which was not known when the station interface was - * added. - */ - if (changes & BSS_CHANGED_ASSOC && bss_conf->assoc) - iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif); - - /* - * If we're not associated yet, take the (new) BSSID before associating - * so the firmware knows. If we're already associated, then use the old - * BSSID here, and we'll send a cleared one later in the CHANGED_ASSOC - * branch for disassociation below. - */ - if (changes & BSS_CHANGED_BSSID && !mvmvif->associated) - memcpy(mvmvif->bssid, bss_conf->bssid, ETH_ALEN); - - ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, mvmvif->bssid); - if (ret) - IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); - - /* after sending it once, adopt mac80211 data */ - memcpy(mvmvif->bssid, bss_conf->bssid, ETH_ALEN); - mvmvif->associated = bss_conf->assoc; - - if (changes & BSS_CHANGED_ASSOC) { - if (bss_conf->assoc) { - /* clear statistics to get clean beacon counter */ - iwl_mvm_request_statistics(mvm, true); - memset(&mvmvif->beacon_stats, 0, - sizeof(mvmvif->beacon_stats)); - - /* add quota for this interface */ - ret = iwl_mvm_update_quotas(mvm, true, NULL); - if (ret) { - IWL_ERR(mvm, "failed to update quotas\n"); - return; - } - - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, - &mvm->status)) { - /* - * If we're restarting then the firmware will - * obviously have lost synchronisation with - * the AP. It will attempt to synchronise by - * itself, but we can make it more reliable by - * scheduling a session protection time event. - * - * The firmware needs to receive a beacon to - * catch up with synchronisation, use 110% of - * the beacon interval. - * - * Set a large maximum delay to allow for more - * than a single interface. - */ - u32 dur = (11 * vif->bss_conf.beacon_int) / 10; - iwl_mvm_protect_session(mvm, vif, dur, dur, - 5 * dur, false); - } - - iwl_mvm_sf_update(mvm, vif, false); - iwl_mvm_power_vif_assoc(mvm, vif); - if (vif->p2p) { - iwl_mvm_ref(mvm, IWL_MVM_REF_P2P_CLIENT); - iwl_mvm_update_smps(mvm, vif, - IWL_MVM_SMPS_REQ_PROT, - IEEE80211_SMPS_DYNAMIC); - } - } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) { - /* - * If update fails - SF might be running in associated - * mode while disassociated - which is forbidden. - */ - WARN_ONCE(iwl_mvm_sf_update(mvm, vif, false), - "Failed to update SF upon disassociation\n"); - - /* remove AP station now that the MAC is unassoc */ - ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id); - if (ret) - IWL_ERR(mvm, "failed to remove AP station\n"); - - if (mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id) - mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; - mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; - /* remove quota for this interface */ - ret = iwl_mvm_update_quotas(mvm, false, NULL); - if (ret) - IWL_ERR(mvm, "failed to update quotas\n"); - - if (vif->p2p) - iwl_mvm_unref(mvm, IWL_MVM_REF_P2P_CLIENT); - - /* this will take the cleared BSSID from bss_conf */ - ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); - if (ret) - IWL_ERR(mvm, - "failed to update MAC %pM (clear after unassoc)\n", - vif->addr); - } - - iwl_mvm_recalc_multicast(mvm); - iwl_mvm_configure_bcast_filter(mvm, vif); - - /* reset rssi values */ - mvmvif->bf_data.ave_beacon_signal = 0; - - iwl_mvm_bt_coex_vif_change(mvm); - iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, - IEEE80211_SMPS_AUTOMATIC); - } else if (changes & BSS_CHANGED_BEACON_INFO) { - /* - * We received a beacon _after_ association so - * remove the session protection. - */ - iwl_mvm_remove_time_event(mvm, mvmvif, - &mvmvif->time_event_data); - } - - if (changes & BSS_CHANGED_BEACON_INFO) { - iwl_mvm_sf_update(mvm, vif, false); - WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); - } - - if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS | BSS_CHANGED_QOS)) { - ret = iwl_mvm_power_update_mac(mvm); - if (ret) - IWL_ERR(mvm, "failed to update power mode\n"); - } - - if (changes & BSS_CHANGED_TXPOWER) { - IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d\n", - bss_conf->txpower); - iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower); - } - - if (changes & BSS_CHANGED_CQM) { - IWL_DEBUG_MAC80211(mvm, "cqm info_changed\n"); - /* reset cqm events tracking */ - mvmvif->bf_data.last_cqm_event = 0; - if (mvmvif->bf_data.bf_enabled) { - ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0); - if (ret) - IWL_ERR(mvm, - "failed to update CQM thresholds\n"); - } - } - - if (changes & BSS_CHANGED_ARP_FILTER) { - IWL_DEBUG_MAC80211(mvm, "arp filter changed\n"); - iwl_mvm_configure_bcast_filter(mvm, vif); - } -} - -static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; - - /* - * iwl_mvm_mac_ctxt_add() might read directly from the device - * (the system time), so make sure it is available. - */ - ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_START_AP); - if (ret) - return ret; - - mutex_lock(&mvm->mutex); - - /* Send the beacon template */ - ret = iwl_mvm_mac_ctxt_beacon_changed(mvm, vif); - if (ret) - goto out_unlock; - - /* - * Re-calculate the tsf id, as the master-slave relations depend on the - * beacon interval, which was not known when the AP interface was added. - */ - if (vif->type == NL80211_IFTYPE_AP) - iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif); - - mvmvif->ap_assoc_sta_count = 0; - - /* Add the mac context */ - ret = iwl_mvm_mac_ctxt_add(mvm, vif); - if (ret) - goto out_unlock; - - /* Perform the binding */ - ret = iwl_mvm_binding_add_vif(mvm, vif); - if (ret) - goto out_remove; - - /* Send the bcast station. At this stage the TBTT and DTIM time events - * are added and applied to the scheduler */ - ret = iwl_mvm_send_add_bcast_sta(mvm, vif); - if (ret) - goto out_unbind; - - /* must be set before quota calculations */ - mvmvif->ap_ibss_active = true; - - /* power updated needs to be done before quotas */ - iwl_mvm_power_update_mac(mvm); - - ret = iwl_mvm_update_quotas(mvm, false, NULL); - if (ret) - goto out_quota_failed; - - /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */ - if (vif->p2p && mvm->p2p_device_vif) - iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false, NULL); - - iwl_mvm_ref(mvm, IWL_MVM_REF_AP_IBSS); - - iwl_mvm_bt_coex_vif_change(mvm); - - /* we don't support TDLS during DCM */ - if (iwl_mvm_phy_ctx_count(mvm) > 1) - iwl_mvm_teardown_tdls_peers(mvm); - - goto out_unlock; - -out_quota_failed: - iwl_mvm_power_update_mac(mvm); - mvmvif->ap_ibss_active = false; - iwl_mvm_send_rm_bcast_sta(mvm, vif); -out_unbind: - iwl_mvm_binding_remove_vif(mvm, vif); -out_remove: - iwl_mvm_mac_ctxt_remove(mvm, vif); -out_unlock: - mutex_unlock(&mvm->mutex); - iwl_mvm_unref(mvm, IWL_MVM_REF_START_AP); - return ret; -} - -static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - iwl_mvm_prepare_mac_removal(mvm, vif); - - mutex_lock(&mvm->mutex); - - /* Handle AP stop while in CSA */ - if (rcu_access_pointer(mvm->csa_vif) == vif) { - iwl_mvm_remove_time_event(mvm, mvmvif, - &mvmvif->time_event_data); - RCU_INIT_POINTER(mvm->csa_vif, NULL); - mvmvif->csa_countdown = false; - } - - if (rcu_access_pointer(mvm->csa_tx_blocked_vif) == vif) { - RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL); - mvm->csa_tx_block_bcn_timeout = 0; - } - - mvmvif->ap_ibss_active = false; - mvm->ap_last_beacon_gp2 = 0; - - iwl_mvm_bt_coex_vif_change(mvm); - - iwl_mvm_unref(mvm, IWL_MVM_REF_AP_IBSS); - - /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */ - if (vif->p2p && mvm->p2p_device_vif) - iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false, NULL); - - iwl_mvm_update_quotas(mvm, false, NULL); - iwl_mvm_send_rm_bcast_sta(mvm, vif); - iwl_mvm_binding_remove_vif(mvm, vif); - - iwl_mvm_power_update_mac(mvm); - - iwl_mvm_mac_ctxt_remove(mvm, vif); - - mutex_unlock(&mvm->mutex); -} - -static void -iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *bss_conf, - u32 changes) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - /* Changes will be applied when the AP/IBSS is started */ - if (!mvmvif->ap_ibss_active) - return; - - if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT | - BSS_CHANGED_BANDWIDTH | BSS_CHANGED_QOS) && - iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL)) - IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); - - /* Need to send a new beacon template to the FW */ - if (changes & BSS_CHANGED_BEACON && - iwl_mvm_mac_ctxt_beacon_changed(mvm, vif)) - IWL_WARN(mvm, "Failed updating beacon data\n"); - - if (changes & BSS_CHANGED_TXPOWER) { - IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d\n", - bss_conf->txpower); - iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower); - } - -} - -static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *bss_conf, - u32 changes) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - /* - * iwl_mvm_bss_info_changed_station() might call - * iwl_mvm_protect_session(), which reads directly from - * the device (the system time), so make sure it is available. - */ - if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_BSS_CHANGED)) - return; - - mutex_lock(&mvm->mutex); - - if (changes & BSS_CHANGED_IDLE && !bss_conf->idle) - iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, true); - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - iwl_mvm_bss_info_changed_station(mvm, vif, bss_conf, changes); - break; - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_ADHOC: - iwl_mvm_bss_info_changed_ap_ibss(mvm, vif, bss_conf, changes); - break; - default: - /* shouldn't happen */ - WARN_ON_ONCE(1); - } - - mutex_unlock(&mvm->mutex); - iwl_mvm_unref(mvm, IWL_MVM_REF_BSS_CHANGED); -} - -static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_scan_request *hw_req) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int ret; - - if (hw_req->req.n_channels == 0 || - hw_req->req.n_channels > mvm->fw->ucode_capa.n_scan_channels) - return -EINVAL; - - mutex_lock(&mvm->mutex); - ret = iwl_mvm_reg_scan_start(mvm, vif, &hw_req->req, &hw_req->ies); - mutex_unlock(&mvm->mutex); - - return ret; -} - -static void iwl_mvm_mac_cancel_hw_scan(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - mutex_lock(&mvm->mutex); - - /* Due to a race condition, it's possible that mac80211 asks - * us to stop a hw_scan when it's already stopped. This can - * happen, for instance, if we stopped the scan ourselves, - * called ieee80211_scan_completed() and the userspace called - * cancel scan scan before ieee80211_scan_work() could run. - * To handle that, simply return if the scan is not running. - */ - if (mvm->scan_status & IWL_MVM_SCAN_REGULAR) - iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_REGULAR, true); - - mutex_unlock(&mvm->mutex); -} - -static void -iwl_mvm_mac_allow_buffered_frames(struct ieee80211_hw *hw, - struct ieee80211_sta *sta, u16 tids, - int num_frames, - enum ieee80211_frame_release_type reason, - bool more_data) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - /* Called when we need to transmit (a) frame(s) from mac80211 */ - - iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames, - tids, more_data, false); -} - -static void -iwl_mvm_mac_release_buffered_frames(struct ieee80211_hw *hw, - struct ieee80211_sta *sta, u16 tids, - int num_frames, - enum ieee80211_frame_release_type reason, - bool more_data) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - /* Called when we need to transmit (a) frame(s) from agg queue */ - - iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames, - tids, more_data, true); -} - -static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum sta_notify_cmd cmd, - struct ieee80211_sta *sta) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - unsigned long txqs = 0, tids = 0; - int tid; - - spin_lock_bh(&mvmsta->lock); - for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { - struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; - - if (tid_data->state != IWL_AGG_ON && - tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA) - continue; - - __set_bit(tid_data->txq_id, &txqs); - - if (iwl_mvm_tid_queued(tid_data) == 0) - continue; - - __set_bit(tid, &tids); - } - - switch (cmd) { - case STA_NOTIFY_SLEEP: - if (atomic_read(&mvm->pending_frames[mvmsta->sta_id]) > 0) - ieee80211_sta_block_awake(hw, sta, true); - - for_each_set_bit(tid, &tids, IWL_MAX_TID_COUNT) - ieee80211_sta_set_buffered(sta, tid, true); - - if (txqs) - iwl_trans_freeze_txq_timer(mvm->trans, txqs, true); - /* - * The fw updates the STA to be asleep. Tx packets on the Tx - * queues to this station will not be transmitted. The fw will - * send a Tx response with TX_STATUS_FAIL_DEST_PS. - */ - break; - case STA_NOTIFY_AWAKE: - if (WARN_ON(mvmsta->sta_id == IWL_MVM_STATION_COUNT)) - break; - - if (txqs) - iwl_trans_freeze_txq_timer(mvm->trans, txqs, false); - iwl_mvm_sta_modify_ps_wake(mvm, sta); - break; - default: - break; - } - spin_unlock_bh(&mvmsta->lock); -} - -static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - - /* - * This is called before mac80211 does RCU synchronisation, - * so here we already invalidate our internal RCU-protected - * station pointer. The rest of the code will thus no longer - * be able to find the station this way, and we don't rely - * on further RCU synchronisation after the sta_state() - * callback deleted the station. - */ - mutex_lock(&mvm->mutex); - if (sta == rcu_access_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id])) - rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id], - ERR_PTR(-ENOENT)); - - if (mvm_sta->vif->type == NL80211_IFTYPE_AP) { - mvmvif->ap_assoc_sta_count--; - iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); - } - - mutex_unlock(&mvm->mutex); -} - -static void iwl_mvm_check_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - const u8 *bssid) -{ - if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT)) - return; - - if (iwlwifi_mod_params.uapsd_disable) { - vif->driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD; - return; - } - - vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; -} - -static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - enum ieee80211_sta_state old_state, - enum ieee80211_sta_state new_state) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; - - IWL_DEBUG_MAC80211(mvm, "station %pM state change %d->%d\n", - sta->addr, old_state, new_state); - - /* this would be a mac80211 bug ... but don't crash */ - if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) - return -EINVAL; - - /* if a STA is being removed, reuse its ID */ - flush_work(&mvm->sta_drained_wk); - - mutex_lock(&mvm->mutex); - if (old_state == IEEE80211_STA_NOTEXIST && - new_state == IEEE80211_STA_NONE) { - /* - * Firmware bug - it'll crash if the beacon interval is less - * than 16. We can't avoid connecting at all, so refuse the - * station state change, this will cause mac80211 to abandon - * attempts to connect to this AP, and eventually wpa_s will - * blacklist the AP... - */ - if (vif->type == NL80211_IFTYPE_STATION && - vif->bss_conf.beacon_int < 16) { - IWL_ERR(mvm, - "AP %pM beacon interval is %d, refusing due to firmware bug!\n", - sta->addr, vif->bss_conf.beacon_int); - ret = -EINVAL; - goto out_unlock; - } - - if (sta->tdls && - (vif->p2p || - iwl_mvm_tdls_sta_count(mvm, NULL) == - IWL_MVM_TDLS_STA_COUNT || - iwl_mvm_phy_ctx_count(mvm) > 1)) { - IWL_DEBUG_MAC80211(mvm, "refusing TDLS sta\n"); - ret = -EBUSY; - goto out_unlock; - } - - ret = iwl_mvm_add_sta(mvm, vif, sta); - if (sta->tdls && ret == 0) - iwl_mvm_recalc_tdls_state(mvm, vif, true); - } else if (old_state == IEEE80211_STA_NONE && - new_state == IEEE80211_STA_AUTH) { - /* - * EBS may be disabled due to previous failures reported by FW. - * Reset EBS status here assuming environment has been changed. - */ - mvm->last_ebs_successful = true; - iwl_mvm_check_uapsd(mvm, vif, sta->addr); - ret = 0; - } else if (old_state == IEEE80211_STA_AUTH && - new_state == IEEE80211_STA_ASSOC) { - ret = iwl_mvm_update_sta(mvm, vif, sta); - if (ret == 0) - iwl_mvm_rs_rate_init(mvm, sta, - mvmvif->phy_ctxt->channel->band, - true); - } else if (old_state == IEEE80211_STA_ASSOC && - new_state == IEEE80211_STA_AUTHORIZED) { - - /* we don't support TDLS during DCM */ - if (iwl_mvm_phy_ctx_count(mvm) > 1) - iwl_mvm_teardown_tdls_peers(mvm); - - /* enable beacon filtering */ - WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); - ret = 0; - } else if (old_state == IEEE80211_STA_AUTHORIZED && - new_state == IEEE80211_STA_ASSOC) { - /* disable beacon filtering */ - WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif, 0)); - ret = 0; - } else if (old_state == IEEE80211_STA_ASSOC && - new_state == IEEE80211_STA_AUTH) { - ret = 0; - } else if (old_state == IEEE80211_STA_AUTH && - new_state == IEEE80211_STA_NONE) { - ret = 0; - } else if (old_state == IEEE80211_STA_NONE && - new_state == IEEE80211_STA_NOTEXIST) { - ret = iwl_mvm_rm_sta(mvm, vif, sta); - if (sta->tdls) - iwl_mvm_recalc_tdls_state(mvm, vif, false); - } else { - ret = -EIO; - } - out_unlock: - mutex_unlock(&mvm->mutex); - - if (sta->tdls && ret == 0) { - if (old_state == IEEE80211_STA_NOTEXIST && - new_state == IEEE80211_STA_NONE) - ieee80211_reserve_tid(sta, IWL_MVM_TDLS_FW_TID); - else if (old_state == IEEE80211_STA_NONE && - new_state == IEEE80211_STA_NOTEXIST) - ieee80211_unreserve_tid(sta, IWL_MVM_TDLS_FW_TID); - } - - return ret; -} - -static int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - mvm->rts_threshold = value; - - return 0; -} - -static void iwl_mvm_sta_rc_update(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u32 changed) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - if (vif->type == NL80211_IFTYPE_STATION && - changed & IEEE80211_RC_NSS_CHANGED) - iwl_mvm_sf_update(mvm, vif, false); -} - -static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u16 ac, - const struct ieee80211_tx_queue_params *params) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - mvmvif->queue_params[ac] = *params; - - /* - * No need to update right away, we'll get BSS_CHANGED_QOS - * The exception is P2P_DEVICE interface which needs immediate update. - */ - if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { - int ret; - - mutex_lock(&mvm->mutex); - ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); - mutex_unlock(&mvm->mutex); - return ret; - } - return 0; -} - -static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - u32 duration = min(IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS, - 200 + vif->bss_conf.beacon_int); - u32 min_duration = min(IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS, - 100 + vif->bss_conf.beacon_int); - - if (WARN_ON_ONCE(vif->bss_conf.assoc)) - return; - - /* - * iwl_mvm_protect_session() reads directly from the device - * (the system time), so make sure it is available. - */ - if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PREPARE_TX)) - return; - - mutex_lock(&mvm->mutex); - /* Try really hard to protect the session and hear a beacon */ - iwl_mvm_protect_session(mvm, vif, duration, min_duration, 500, false); - mutex_unlock(&mvm->mutex); - - iwl_mvm_unref(mvm, IWL_MVM_REF_PREPARE_TX); -} - -static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_sched_scan_request *req, - struct ieee80211_scan_ies *ies) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - int ret; - - mutex_lock(&mvm->mutex); - - if (!vif->bss_conf.idle) { - ret = -EBUSY; - goto out; - } - - ret = iwl_mvm_sched_scan_start(mvm, vif, req, ies, IWL_MVM_SCAN_SCHED); - -out: - mutex_unlock(&mvm->mutex); - return ret; -} - -static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int ret; - - mutex_lock(&mvm->mutex); - - /* Due to a race condition, it's possible that mac80211 asks - * us to stop a sched_scan when it's already stopped. This - * can happen, for instance, if we stopped the scan ourselves, - * called ieee80211_sched_scan_stopped() and the userspace called - * stop sched scan scan before ieee80211_sched_scan_stopped_work() - * could run. To handle this, simply return if the scan is - * not running. - */ - if (!(mvm->scan_status & IWL_MVM_SCAN_SCHED)) { - mutex_unlock(&mvm->mutex); - return 0; - } - - ret = iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, false); - mutex_unlock(&mvm->mutex); - iwl_mvm_wait_for_async_handlers(mvm); - - return ret; -} - -static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, - enum set_key_cmd cmd, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *key) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int ret; - u8 key_offset; - - if (iwlwifi_mod_params.sw_crypto) { - IWL_DEBUG_MAC80211(mvm, "leave - hwcrypto disabled\n"); - return -EOPNOTSUPP; - } - - switch (key->cipher) { - case WLAN_CIPHER_SUITE_TKIP: - key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - break; - case WLAN_CIPHER_SUITE_CCMP: - key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; - break; - case WLAN_CIPHER_SUITE_AES_CMAC: - WARN_ON_ONCE(!ieee80211_hw_check(hw, MFP_CAPABLE)); - break; - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - /* For non-client mode, only use WEP keys for TX as we probably - * don't have a station yet anyway and would then have to keep - * track of the keys, linking them to each of the clients/peers - * as they appear. For now, don't do that, for performance WEP - * offload doesn't really matter much, but we need it for some - * other offload features in client mode. - */ - if (vif->type != NL80211_IFTYPE_STATION) - return 0; - break; - default: - /* currently FW supports only one optional cipher scheme */ - if (hw->n_cipher_schemes && - hw->cipher_schemes->cipher == key->cipher) - key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; - else - return -EOPNOTSUPP; - } - - mutex_lock(&mvm->mutex); - - switch (cmd) { - case SET_KEY: - if ((vif->type == NL80211_IFTYPE_ADHOC || - vif->type == NL80211_IFTYPE_AP) && !sta) { - /* - * GTK on AP interface is a TX-only key, return 0; - * on IBSS they're per-station and because we're lazy - * we don't support them for RX, so do the same. - */ - ret = 0; - key->hw_key_idx = STA_KEY_IDX_INVALID; - break; - } - - /* During FW restart, in order to restore the state as it was, - * don't try to reprogram keys we previously failed for. - */ - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && - key->hw_key_idx == STA_KEY_IDX_INVALID) { - IWL_DEBUG_MAC80211(mvm, - "skip invalid idx key programming during restart\n"); - ret = 0; - break; - } - - /* in HW restart reuse the index, otherwise request a new one */ - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) - key_offset = key->hw_key_idx; - else - key_offset = STA_KEY_IDX_INVALID; - - IWL_DEBUG_MAC80211(mvm, "set hwcrypto key\n"); - ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, key_offset); - if (ret) { - IWL_WARN(mvm, "set key failed\n"); - /* - * 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; - default: - ret = -EINVAL; - } - - mutex_unlock(&mvm->mutex); - return ret; -} - -static void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_key_conf *keyconf, - struct ieee80211_sta *sta, - u32 iv32, u16 *phase1key) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - if (keyconf->hw_key_idx == STA_KEY_IDX_INVALID) - return; - - iwl_mvm_update_tkip_key(mvm, vif, keyconf, sta, iv32, phase1key); -} - - -static bool iwl_mvm_rx_aux_roc(struct iwl_notif_wait_data *notif_wait, - struct iwl_rx_packet *pkt, void *data) -{ - struct iwl_mvm *mvm = - container_of(notif_wait, struct iwl_mvm, notif_wait); - struct iwl_hs20_roc_res *resp; - int resp_len = iwl_rx_packet_payload_len(pkt); - struct iwl_mvm_time_event_data *te_data = data; - - if (WARN_ON(pkt->hdr.cmd != HOT_SPOT_CMD)) - return true; - - if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { - IWL_ERR(mvm, "Invalid HOT_SPOT_CMD response\n"); - return true; - } - - resp = (void *)pkt->data; - - IWL_DEBUG_TE(mvm, - "Aux ROC: Recieved response from ucode: status=%d uid=%d\n", - resp->status, resp->event_unique_id); - - te_data->uid = le32_to_cpu(resp->event_unique_id); - IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n", - te_data->uid); - - spin_lock_bh(&mvm->time_event_lock); - list_add_tail(&te_data->list, &mvm->aux_roc_te_list); - spin_unlock_bh(&mvm->time_event_lock); - - return true; -} - -#define AUX_ROC_MAX_DELAY_ON_CHANNEL 200 -static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm, - struct ieee80211_channel *channel, - struct ieee80211_vif *vif, - int duration) -{ - int res, time_reg = DEVICE_SYSTEM_TIME_REG; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_time_event_data *te_data = &mvmvif->hs_time_event_data; - static const u16 time_event_response[] = { HOT_SPOT_CMD }; - struct iwl_notification_wait wait_time_event; - struct iwl_hs20_roc_req aux_roc_req = { - .action = cpu_to_le32(FW_CTXT_ACTION_ADD), - .id_and_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(MAC_INDEX_AUX, 0)), - .sta_id_and_color = cpu_to_le32(mvm->aux_sta.sta_id), - /* Set the channel info data */ - .channel_info.band = (channel->band == IEEE80211_BAND_2GHZ) ? - PHY_BAND_24 : PHY_BAND_5, - .channel_info.channel = channel->hw_value, - .channel_info.width = PHY_VHT_CHANNEL_MODE20, - /* Set the time and duration */ - .apply_time = cpu_to_le32(iwl_read_prph(mvm->trans, time_reg)), - .apply_time_max_delay = - cpu_to_le32(MSEC_TO_TU(AUX_ROC_MAX_DELAY_ON_CHANNEL)), - .duration = cpu_to_le32(MSEC_TO_TU(duration)), - }; - - /* Set the node address */ - memcpy(aux_roc_req.node_addr, vif->addr, ETH_ALEN); - - lockdep_assert_held(&mvm->mutex); - - spin_lock_bh(&mvm->time_event_lock); - - if (WARN_ON(te_data->id == HOT_SPOT_CMD)) { - spin_unlock_bh(&mvm->time_event_lock); - return -EIO; - } - - te_data->vif = vif; - te_data->duration = duration; - te_data->id = HOT_SPOT_CMD; - - spin_unlock_bh(&mvm->time_event_lock); - - /* - * Use a notification wait, which really just processes the - * command response and doesn't wait for anything, in order - * to be able to process the response and get the UID inside - * the RX path. Using CMD_WANT_SKB doesn't work because it - * stores the buffer and then wakes up this thread, by which - * time another notification (that the time event started) - * might already be processed unsuccessfully. - */ - iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event, - time_event_response, - ARRAY_SIZE(time_event_response), - iwl_mvm_rx_aux_roc, te_data); - - res = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0, sizeof(aux_roc_req), - &aux_roc_req); - - if (res) { - IWL_ERR(mvm, "Couldn't send HOT_SPOT_CMD: %d\n", res); - iwl_remove_notification(&mvm->notif_wait, &wait_time_event); - goto out_clear_te; - } - - /* No need to wait for anything, so just pass 1 (0 isn't valid) */ - res = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1); - /* should never fail */ - WARN_ON_ONCE(res); - - if (res) { - out_clear_te: - spin_lock_bh(&mvm->time_event_lock); - iwl_mvm_te_clear_data(mvm, te_data); - spin_unlock_bh(&mvm->time_event_lock); - } - - return res; -} - -static int iwl_mvm_roc(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_channel *channel, - int duration, - enum ieee80211_roc_type type) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct cfg80211_chan_def chandef; - struct iwl_mvm_phy_ctxt *phy_ctxt; - int ret, i; - - IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value, - duration, type); - - flush_work(&mvm->roc_done_wk); - - mutex_lock(&mvm->mutex); - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT)) { - /* Use aux roc framework (HS20) */ - ret = iwl_mvm_send_aux_roc_cmd(mvm, channel, - vif, duration); - goto out_unlock; - } - IWL_ERR(mvm, "hotspot not supported\n"); - ret = -EINVAL; - goto out_unlock; - case NL80211_IFTYPE_P2P_DEVICE: - /* handle below */ - break; - default: - IWL_ERR(mvm, "vif isn't P2P_DEVICE: %d\n", vif->type); - ret = -EINVAL; - goto out_unlock; - } - - for (i = 0; i < NUM_PHY_CTX; i++) { - phy_ctxt = &mvm->phy_ctxts[i]; - if (phy_ctxt->ref == 0 || mvmvif->phy_ctxt == phy_ctxt) - continue; - - if (phy_ctxt->ref && channel == phy_ctxt->channel) { - /* - * Unbind the P2P_DEVICE from the current PHY context, - * and if the PHY context is not used remove it. - */ - ret = iwl_mvm_binding_remove_vif(mvm, vif); - if (WARN(ret, "Failed unbinding P2P_DEVICE\n")) - goto out_unlock; - - iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); - - /* Bind the P2P_DEVICE to the current PHY Context */ - mvmvif->phy_ctxt = phy_ctxt; - - ret = iwl_mvm_binding_add_vif(mvm, vif); - if (WARN(ret, "Failed binding P2P_DEVICE\n")) - goto out_unlock; - - iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt); - goto schedule_time_event; - } - } - - /* Need to update the PHY context only if the ROC channel changed */ - if (channel == mvmvif->phy_ctxt->channel) - goto schedule_time_event; - - cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT); - - /* - * Change the PHY context configuration as it is currently referenced - * only by the P2P Device MAC - */ - if (mvmvif->phy_ctxt->ref == 1) { - ret = iwl_mvm_phy_ctxt_changed(mvm, mvmvif->phy_ctxt, - &chandef, 1, 1); - if (ret) - goto out_unlock; - } else { - /* - * The PHY context is shared with other MACs. Need to remove the - * P2P Device from the binding, allocate an new PHY context and - * create a new binding - */ - phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); - if (!phy_ctxt) { - ret = -ENOSPC; - goto out_unlock; - } - - ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chandef, - 1, 1); - if (ret) { - IWL_ERR(mvm, "Failed to change PHY context\n"); - goto out_unlock; - } - - /* Unbind the P2P_DEVICE from the current PHY context */ - ret = iwl_mvm_binding_remove_vif(mvm, vif); - if (WARN(ret, "Failed unbinding P2P_DEVICE\n")) - goto out_unlock; - - iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); - - /* Bind the P2P_DEVICE to the new allocated PHY context */ - mvmvif->phy_ctxt = phy_ctxt; - - ret = iwl_mvm_binding_add_vif(mvm, vif); - if (WARN(ret, "Failed binding P2P_DEVICE\n")) - goto out_unlock; - - iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt); - } - -schedule_time_event: - /* Schedule the time events */ - ret = iwl_mvm_start_p2p_roc(mvm, vif, duration, type); - -out_unlock: - mutex_unlock(&mvm->mutex); - IWL_DEBUG_MAC80211(mvm, "leave\n"); - return ret; -} - -static int iwl_mvm_cancel_roc(struct ieee80211_hw *hw) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - IWL_DEBUG_MAC80211(mvm, "enter\n"); - - mutex_lock(&mvm->mutex); - iwl_mvm_stop_roc(mvm); - mutex_unlock(&mvm->mutex); - - IWL_DEBUG_MAC80211(mvm, "leave\n"); - return 0; -} - -static int __iwl_mvm_add_chanctx(struct iwl_mvm *mvm, - struct ieee80211_chanctx_conf *ctx) -{ - u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; - struct iwl_mvm_phy_ctxt *phy_ctxt; - int ret; - - lockdep_assert_held(&mvm->mutex); - - IWL_DEBUG_MAC80211(mvm, "Add channel context\n"); - - phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); - if (!phy_ctxt) { - ret = -ENOSPC; - goto out; - } - - ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def, - ctx->rx_chains_static, - ctx->rx_chains_dynamic); - if (ret) { - IWL_ERR(mvm, "Failed to add PHY context\n"); - goto out; - } - - iwl_mvm_phy_ctxt_ref(mvm, phy_ctxt); - *phy_ctxt_id = phy_ctxt->id; -out: - return ret; -} - -static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw, - struct ieee80211_chanctx_conf *ctx) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int ret; - - mutex_lock(&mvm->mutex); - ret = __iwl_mvm_add_chanctx(mvm, ctx); - mutex_unlock(&mvm->mutex); - - return ret; -} - -static void __iwl_mvm_remove_chanctx(struct iwl_mvm *mvm, - struct ieee80211_chanctx_conf *ctx) -{ - u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; - struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; - - lockdep_assert_held(&mvm->mutex); - - iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt); -} - -static void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw, - struct ieee80211_chanctx_conf *ctx) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - mutex_lock(&mvm->mutex); - __iwl_mvm_remove_chanctx(mvm, ctx); - mutex_unlock(&mvm->mutex); -} - -static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw, - struct ieee80211_chanctx_conf *ctx, - u32 changed) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; - struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; - - if (WARN_ONCE((phy_ctxt->ref > 1) && - (changed & ~(IEEE80211_CHANCTX_CHANGE_WIDTH | - IEEE80211_CHANCTX_CHANGE_RX_CHAINS | - IEEE80211_CHANCTX_CHANGE_RADAR | - IEEE80211_CHANCTX_CHANGE_MIN_WIDTH)), - "Cannot change PHY. Ref=%d, changed=0x%X\n", - phy_ctxt->ref, changed)) - return; - - mutex_lock(&mvm->mutex); - iwl_mvm_bt_coex_vif_change(mvm); - iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def, - ctx->rx_chains_static, - ctx->rx_chains_dynamic); - mutex_unlock(&mvm->mutex); -} - -static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_chanctx_conf *ctx, - bool switching_chanctx) -{ - u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; - struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; - - lockdep_assert_held(&mvm->mutex); - - mvmvif->phy_ctxt = phy_ctxt; - - switch (vif->type) { - case NL80211_IFTYPE_AP: - /* only needed if we're switching chanctx (i.e. during CSA) */ - if (switching_chanctx) { - mvmvif->ap_ibss_active = true; - break; - } - case NL80211_IFTYPE_ADHOC: - /* - * The AP binding flow is handled as part of the start_ap flow - * (in bss_info_changed), similarly for IBSS. - */ - ret = 0; - goto out; - case NL80211_IFTYPE_STATION: - break; - case NL80211_IFTYPE_MONITOR: - /* always disable PS when a monitor interface is active */ - mvmvif->ps_disabled = true; - break; - default: - ret = -EINVAL; - goto out; - } - - ret = iwl_mvm_binding_add_vif(mvm, vif); - if (ret) - goto out; - - /* - * Power state must be updated before quotas, - * otherwise fw will complain. - */ - iwl_mvm_power_update_mac(mvm); - - /* Setting the quota at this stage is only required for monitor - * interfaces. For the other types, the bss_info changed flow - * will handle quota settings. - */ - if (vif->type == NL80211_IFTYPE_MONITOR) { - mvmvif->monitor_active = true; - ret = iwl_mvm_update_quotas(mvm, false, NULL); - if (ret) - goto out_remove_binding; - } - - /* Handle binding during CSA */ - if (vif->type == NL80211_IFTYPE_AP) { - iwl_mvm_update_quotas(mvm, false, NULL); - iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); - } - - if (switching_chanctx && vif->type == NL80211_IFTYPE_STATION) { - u32 duration = 2 * vif->bss_conf.beacon_int; - - /* iwl_mvm_protect_session() reads directly from the - * device (the system time), so make sure it is - * available. - */ - ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PROTECT_CSA); - if (ret) - goto out_remove_binding; - - /* Protect the session to make sure we hear the first - * beacon on the new channel. - */ - iwl_mvm_protect_session(mvm, vif, duration, duration, - vif->bss_conf.beacon_int / 2, - true); - - iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_CSA); - - iwl_mvm_update_quotas(mvm, false, NULL); - } - - goto out; - -out_remove_binding: - iwl_mvm_binding_remove_vif(mvm, vif); - iwl_mvm_power_update_mac(mvm); -out: - if (ret) - mvmvif->phy_ctxt = NULL; - return ret; -} -static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_chanctx_conf *ctx) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int ret; - - mutex_lock(&mvm->mutex); - ret = __iwl_mvm_assign_vif_chanctx(mvm, vif, ctx, false); - mutex_unlock(&mvm->mutex); - - return ret; -} - -static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_chanctx_conf *ctx, - bool switching_chanctx) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct ieee80211_vif *disabled_vif = NULL; - - lockdep_assert_held(&mvm->mutex); - - iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data); - - switch (vif->type) { - case NL80211_IFTYPE_ADHOC: - goto out; - case NL80211_IFTYPE_MONITOR: - mvmvif->monitor_active = false; - mvmvif->ps_disabled = false; - break; - case NL80211_IFTYPE_AP: - /* This part is triggered only during CSA */ - if (!switching_chanctx || !mvmvif->ap_ibss_active) - goto out; - - mvmvif->csa_countdown = false; - - /* Set CS bit on all the stations */ - iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, true); - - /* Save blocked iface, the timeout is set on the next beacon */ - rcu_assign_pointer(mvm->csa_tx_blocked_vif, vif); - - mvmvif->ap_ibss_active = false; - break; - case NL80211_IFTYPE_STATION: - if (!switching_chanctx) - break; - - disabled_vif = vif; - - iwl_mvm_mac_ctxt_changed(mvm, vif, true, NULL); - break; - default: - break; - } - - iwl_mvm_update_quotas(mvm, false, disabled_vif); - iwl_mvm_binding_remove_vif(mvm, vif); - -out: - mvmvif->phy_ctxt = NULL; - iwl_mvm_power_update_mac(mvm); -} - -static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_chanctx_conf *ctx) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - mutex_lock(&mvm->mutex); - __iwl_mvm_unassign_vif_chanctx(mvm, vif, ctx, false); - mutex_unlock(&mvm->mutex); -} - -static int -iwl_mvm_switch_vif_chanctx_swap(struct iwl_mvm *mvm, - struct ieee80211_vif_chanctx_switch *vifs) -{ - int ret; - - mutex_lock(&mvm->mutex); - __iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true); - __iwl_mvm_remove_chanctx(mvm, vifs[0].old_ctx); - - ret = __iwl_mvm_add_chanctx(mvm, vifs[0].new_ctx); - if (ret) { - IWL_ERR(mvm, "failed to add new_ctx during channel switch\n"); - goto out_reassign; - } - - ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx, - true); - if (ret) { - IWL_ERR(mvm, - "failed to assign new_ctx during channel switch\n"); - goto out_remove; - } - - /* we don't support TDLS during DCM - can be caused by channel switch */ - if (iwl_mvm_phy_ctx_count(mvm) > 1) - iwl_mvm_teardown_tdls_peers(mvm); - - goto out; - -out_remove: - __iwl_mvm_remove_chanctx(mvm, vifs[0].new_ctx); - -out_reassign: - if (__iwl_mvm_add_chanctx(mvm, vifs[0].old_ctx)) { - IWL_ERR(mvm, "failed to add old_ctx back after failure.\n"); - goto out_restart; - } - - if (__iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, - true)) { - IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n"); - goto out_restart; - } - - goto out; - -out_restart: - /* things keep failing, better restart the hw */ - iwl_mvm_nic_restart(mvm, false); - -out: - mutex_unlock(&mvm->mutex); - - return ret; -} - -static int -iwl_mvm_switch_vif_chanctx_reassign(struct iwl_mvm *mvm, - struct ieee80211_vif_chanctx_switch *vifs) -{ - int ret; - - mutex_lock(&mvm->mutex); - __iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true); - - ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx, - true); - if (ret) { - IWL_ERR(mvm, - "failed to assign new_ctx during channel switch\n"); - goto out_reassign; - } - - goto out; - -out_reassign: - if (__iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, - true)) { - IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n"); - goto out_restart; - } - - goto out; - -out_restart: - /* things keep failing, better restart the hw */ - iwl_mvm_nic_restart(mvm, false); - -out: - mutex_unlock(&mvm->mutex); - - return ret; -} - -static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw, - struct ieee80211_vif_chanctx_switch *vifs, - int n_vifs, - enum ieee80211_chanctx_switch_mode mode) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int ret; - - /* we only support a single-vif right now */ - if (n_vifs > 1) - return -EOPNOTSUPP; - - switch (mode) { - case CHANCTX_SWMODE_SWAP_CONTEXTS: - ret = iwl_mvm_switch_vif_chanctx_swap(mvm, vifs); - break; - case CHANCTX_SWMODE_REASSIGN_VIF: - ret = iwl_mvm_switch_vif_chanctx_reassign(mvm, vifs); - break; - default: - ret = -EOPNOTSUPP; - break; - } - - return ret; -} - -static int iwl_mvm_set_tim(struct ieee80211_hw *hw, - struct ieee80211_sta *sta, - bool set) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - - if (!mvm_sta || !mvm_sta->vif) { - IWL_ERR(mvm, "Station is not associated to a vif\n"); - return -EINVAL; - } - - return iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm_sta->vif); -} - -#ifdef CONFIG_NL80211_TESTMODE -static const struct nla_policy iwl_mvm_tm_policy[IWL_MVM_TM_ATTR_MAX + 1] = { - [IWL_MVM_TM_ATTR_CMD] = { .type = NLA_U32 }, - [IWL_MVM_TM_ATTR_NOA_DURATION] = { .type = NLA_U32 }, - [IWL_MVM_TM_ATTR_BEACON_FILTER_STATE] = { .type = NLA_U32 }, -}; - -static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - void *data, int len) -{ - struct nlattr *tb[IWL_MVM_TM_ATTR_MAX + 1]; - int err; - u32 noa_duration; - - err = nla_parse(tb, IWL_MVM_TM_ATTR_MAX, data, len, iwl_mvm_tm_policy); - if (err) - return err; - - if (!tb[IWL_MVM_TM_ATTR_CMD]) - return -EINVAL; - - switch (nla_get_u32(tb[IWL_MVM_TM_ATTR_CMD])) { - case IWL_MVM_TM_CMD_SET_NOA: - if (!vif || vif->type != NL80211_IFTYPE_AP || !vif->p2p || - !vif->bss_conf.enable_beacon || - !tb[IWL_MVM_TM_ATTR_NOA_DURATION]) - return -EINVAL; - - noa_duration = nla_get_u32(tb[IWL_MVM_TM_ATTR_NOA_DURATION]); - if (noa_duration >= vif->bss_conf.beacon_int) - return -EINVAL; - - mvm->noa_duration = noa_duration; - mvm->noa_vif = vif; - - return iwl_mvm_update_quotas(mvm, false, NULL); - case IWL_MVM_TM_CMD_SET_BEACON_FILTER: - /* must be associated client vif - ignore authorized */ - if (!vif || vif->type != NL80211_IFTYPE_STATION || - !vif->bss_conf.assoc || !vif->bss_conf.dtim_period || - !tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE]) - return -EINVAL; - - if (nla_get_u32(tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE])) - return iwl_mvm_enable_beacon_filter(mvm, vif, 0); - return iwl_mvm_disable_beacon_filter(mvm, vif, 0); - } - - return -EOPNOTSUPP; -} - -static int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - void *data, int len) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int err; - - mutex_lock(&mvm->mutex); - err = __iwl_mvm_mac_testmode_cmd(mvm, vif, data, len); - mutex_unlock(&mvm->mutex); - - return err; -} -#endif - -static void iwl_mvm_channel_switch(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_channel_switch *chsw) -{ - /* By implementing this operation, we prevent mac80211 from - * starting its own channel switch timer, so that we can call - * ieee80211_chswitch_done() ourselves at the right time - * (which is when the absence time event starts). - */ - - IWL_DEBUG_MAC80211(IWL_MAC80211_GET_MVM(hw), - "dummy channel switch op\n"); -} - -static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_channel_switch *chsw) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct ieee80211_vif *csa_vif; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - u32 apply_time; - int ret; - - mutex_lock(&mvm->mutex); - - mvmvif->csa_failed = false; - - IWL_DEBUG_MAC80211(mvm, "pre CSA to freq %d\n", - chsw->chandef.center_freq1); - - iwl_fw_dbg_trigger_simple_stop(mvm, vif, FW_DBG_TRIGGER_CHANNEL_SWITCH); - - switch (vif->type) { - case NL80211_IFTYPE_AP: - csa_vif = - rcu_dereference_protected(mvm->csa_vif, - lockdep_is_held(&mvm->mutex)); - if (WARN_ONCE(csa_vif && csa_vif->csa_active, - "Another CSA is already in progress")) { - ret = -EBUSY; - goto out_unlock; - } - - rcu_assign_pointer(mvm->csa_vif, vif); - - if (WARN_ONCE(mvmvif->csa_countdown, - "Previous CSA countdown didn't complete")) { - ret = -EBUSY; - goto out_unlock; - } - - break; - case NL80211_IFTYPE_STATION: - /* Schedule the time event to a bit before beacon 1, - * to make sure we're in the new channel when the - * GO/AP arrives. - */ - apply_time = chsw->device_timestamp + - ((vif->bss_conf.beacon_int * (chsw->count - 1) - - IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT) * 1024); - - if (chsw->block_tx) - iwl_mvm_csa_client_absent(mvm, vif); - - iwl_mvm_schedule_csa_period(mvm, vif, vif->bss_conf.beacon_int, - apply_time); - if (mvmvif->bf_data.bf_enabled) { - ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); - if (ret) - goto out_unlock; - } - - break; - default: - break; - } - - mvmvif->ps_disabled = true; - - ret = iwl_mvm_power_update_ps(mvm); - if (ret) - goto out_unlock; - - /* we won't be on this channel any longer */ - iwl_mvm_teardown_tdls_peers(mvm); - -out_unlock: - mutex_unlock(&mvm->mutex); - - return ret; -} - -static int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int ret; - - mutex_lock(&mvm->mutex); - - if (mvmvif->csa_failed) { - mvmvif->csa_failed = false; - ret = -EIO; - goto out_unlock; - } - - if (vif->type == NL80211_IFTYPE_STATION) { - struct iwl_mvm_sta *mvmsta; - - mvmsta = iwl_mvm_sta_from_staid_protected(mvm, - mvmvif->ap_sta_id); - - if (WARN_ON(!mvmsta)) { - ret = -EIO; - goto out_unlock; - } - - iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false); - - iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); - - ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0); - if (ret) - goto out_unlock; - - iwl_mvm_stop_session_protection(mvm, vif); - } - - mvmvif->ps_disabled = false; - - ret = iwl_mvm_power_update_ps(mvm); - -out_unlock: - mutex_unlock(&mvm->mutex); - - return ret; -} - -static void iwl_mvm_mac_flush(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u32 queues, bool drop) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif; - struct iwl_mvm_sta *mvmsta; - struct ieee80211_sta *sta; - int i; - u32 msk = 0; - - if (!vif || vif->type != NL80211_IFTYPE_STATION) - return; - - mutex_lock(&mvm->mutex); - mvmvif = iwl_mvm_vif_from_mac80211(vif); - - /* flush the AP-station and all TDLS peers */ - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], - lockdep_is_held(&mvm->mutex)); - if (IS_ERR_OR_NULL(sta)) - continue; - - mvmsta = iwl_mvm_sta_from_mac80211(sta); - if (mvmsta->vif != vif) - continue; - - /* make sure only TDLS peers or the AP are flushed */ - WARN_ON(i != mvmvif->ap_sta_id && !sta->tdls); - - msk |= mvmsta->tfd_queue_msk; - } - - if (drop) { - if (iwl_mvm_flush_tx_path(mvm, msk, 0)) - IWL_ERR(mvm, "flush request fail\n"); - mutex_unlock(&mvm->mutex); - } else { - mutex_unlock(&mvm->mutex); - - /* this can take a while, and we may need/want other operations - * to succeed while doing this, so do it without the mutex held - */ - iwl_trans_wait_tx_queue_empty(mvm->trans, msk); - } -} - -static int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx, - struct survey_info *survey) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int ret; - - memset(survey, 0, sizeof(*survey)); - - /* only support global statistics right now */ - if (idx != 0) - return -ENOENT; - - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS)) - return -ENOENT; - - mutex_lock(&mvm->mutex); - - if (mvm->ucode_loaded) { - ret = iwl_mvm_request_statistics(mvm, false); - if (ret) - goto out; - } - - survey->filled = SURVEY_INFO_TIME | - SURVEY_INFO_TIME_RX | - SURVEY_INFO_TIME_TX | - SURVEY_INFO_TIME_SCAN; - survey->time = mvm->accu_radio_stats.on_time_rf + - mvm->radio_stats.on_time_rf; - do_div(survey->time, USEC_PER_MSEC); - - survey->time_rx = mvm->accu_radio_stats.rx_time + - mvm->radio_stats.rx_time; - do_div(survey->time_rx, USEC_PER_MSEC); - - survey->time_tx = mvm->accu_radio_stats.tx_time + - mvm->radio_stats.tx_time; - do_div(survey->time_tx, USEC_PER_MSEC); - - survey->time_scan = mvm->accu_radio_stats.on_time_scan + - mvm->radio_stats.on_time_scan; - do_div(survey->time_scan, USEC_PER_MSEC); - - ret = 0; - out: - mutex_unlock(&mvm->mutex); - return ret; -} - -static void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct station_info *sinfo) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS)) - return; - - /* if beacon filtering isn't on mac80211 does it anyway */ - if (!(vif->driver_flags & IEEE80211_VIF_BEACON_FILTER)) - return; - - if (!vif->bss_conf.assoc) - return; - - mutex_lock(&mvm->mutex); - - if (mvmvif->ap_sta_id != mvmsta->sta_id) - goto unlock; - - if (iwl_mvm_request_statistics(mvm, false)) - goto unlock; - - sinfo->rx_beacon = mvmvif->beacon_stats.num_beacons + - mvmvif->beacon_stats.accu_num_beacons; - sinfo->filled |= BIT(NL80211_STA_INFO_BEACON_RX); - if (mvmvif->beacon_stats.avg_signal) { - /* firmware only reports a value after RXing a few beacons */ - sinfo->rx_beacon_signal_avg = mvmvif->beacon_stats.avg_signal; - sinfo->filled |= BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG); - } - unlock: - mutex_unlock(&mvm->mutex); -} - -static void iwl_mvm_event_mlme_callback(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - const struct ieee80211_event *event) -{ -#define CHECK_MLME_TRIGGER(_mvm, _trig, _buf, _cnt, _fmt...) \ - do { \ - if ((_cnt) && --(_cnt)) \ - break; \ - iwl_mvm_fw_dbg_collect_trig(_mvm, _trig, _fmt);\ - } while (0) - - struct iwl_fw_dbg_trigger_tlv *trig; - struct iwl_fw_dbg_trigger_mlme *trig_mlme; - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_MLME)) - return; - - trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_MLME); - trig_mlme = (void *)trig->data; - if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig)) - return; - - if (event->u.mlme.data == ASSOC_EVENT) { - if (event->u.mlme.status == MLME_DENIED) - CHECK_MLME_TRIGGER(mvm, trig, buf, - trig_mlme->stop_assoc_denied, - "DENIED ASSOC: reason %d", - event->u.mlme.reason); - else if (event->u.mlme.status == MLME_TIMEOUT) - CHECK_MLME_TRIGGER(mvm, trig, buf, - trig_mlme->stop_assoc_timeout, - "ASSOC TIMEOUT"); - } else if (event->u.mlme.data == AUTH_EVENT) { - if (event->u.mlme.status == MLME_DENIED) - CHECK_MLME_TRIGGER(mvm, trig, buf, - trig_mlme->stop_auth_denied, - "DENIED AUTH: reason %d", - event->u.mlme.reason); - else if (event->u.mlme.status == MLME_TIMEOUT) - CHECK_MLME_TRIGGER(mvm, trig, buf, - trig_mlme->stop_auth_timeout, - "AUTH TIMEOUT"); - } else if (event->u.mlme.data == DEAUTH_RX_EVENT) { - CHECK_MLME_TRIGGER(mvm, trig, buf, - trig_mlme->stop_rx_deauth, - "DEAUTH RX %d", event->u.mlme.reason); - } else if (event->u.mlme.data == DEAUTH_TX_EVENT) { - CHECK_MLME_TRIGGER(mvm, trig, buf, - trig_mlme->stop_tx_deauth, - "DEAUTH TX %d", event->u.mlme.reason); - } -#undef CHECK_MLME_TRIGGER -} - -static void iwl_mvm_event_bar_rx_callback(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - const struct ieee80211_event *event) -{ - struct iwl_fw_dbg_trigger_tlv *trig; - struct iwl_fw_dbg_trigger_ba *ba_trig; - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA)) - return; - - trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA); - ba_trig = (void *)trig->data; - if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig)) - return; - - if (!(le16_to_cpu(ba_trig->rx_bar) & BIT(event->u.ba.tid))) - return; - - iwl_mvm_fw_dbg_collect_trig(mvm, trig, - "BAR received from %pM, tid %d, ssn %d", - event->u.ba.sta->addr, event->u.ba.tid, - event->u.ba.ssn); -} - -static void -iwl_mvm_event_frame_timeout_callback(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - const struct ieee80211_event *event) -{ - struct iwl_fw_dbg_trigger_tlv *trig; - struct iwl_fw_dbg_trigger_ba *ba_trig; - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA)) - return; - - trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA); - ba_trig = (void *)trig->data; - if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig)) - return; - - if (!(le16_to_cpu(ba_trig->frame_timeout) & BIT(event->u.ba.tid))) - return; - - iwl_mvm_fw_dbg_collect_trig(mvm, trig, - "Frame from %pM timed out, tid %d", - event->u.ba.sta->addr, event->u.ba.tid); -} - -static void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - const struct ieee80211_event *event) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - switch (event->type) { - case MLME_EVENT: - iwl_mvm_event_mlme_callback(mvm, vif, event); - break; - case BAR_RX_EVENT: - iwl_mvm_event_bar_rx_callback(mvm, vif, event); - break; - case BA_FRAME_TIMEOUT: - iwl_mvm_event_frame_timeout_callback(mvm, vif, event); - break; - default: - break; - } -} - -const struct ieee80211_ops iwl_mvm_hw_ops = { - .tx = iwl_mvm_mac_tx, - .ampdu_action = iwl_mvm_mac_ampdu_action, - .start = iwl_mvm_mac_start, - .reconfig_complete = iwl_mvm_mac_reconfig_complete, - .stop = iwl_mvm_mac_stop, - .add_interface = iwl_mvm_mac_add_interface, - .remove_interface = iwl_mvm_mac_remove_interface, - .config = iwl_mvm_mac_config, - .prepare_multicast = iwl_mvm_prepare_multicast, - .configure_filter = iwl_mvm_configure_filter, - .config_iface_filter = iwl_mvm_config_iface_filter, - .bss_info_changed = iwl_mvm_bss_info_changed, - .hw_scan = iwl_mvm_mac_hw_scan, - .cancel_hw_scan = iwl_mvm_mac_cancel_hw_scan, - .sta_pre_rcu_remove = iwl_mvm_sta_pre_rcu_remove, - .sta_state = iwl_mvm_mac_sta_state, - .sta_notify = iwl_mvm_mac_sta_notify, - .allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames, - .release_buffered_frames = iwl_mvm_mac_release_buffered_frames, - .set_rts_threshold = iwl_mvm_mac_set_rts_threshold, - .sta_rc_update = iwl_mvm_sta_rc_update, - .conf_tx = iwl_mvm_mac_conf_tx, - .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx, - .mgd_protect_tdls_discover = iwl_mvm_mac_mgd_protect_tdls_discover, - .flush = iwl_mvm_mac_flush, - .sched_scan_start = iwl_mvm_mac_sched_scan_start, - .sched_scan_stop = iwl_mvm_mac_sched_scan_stop, - .set_key = iwl_mvm_mac_set_key, - .update_tkip_key = iwl_mvm_mac_update_tkip_key, - .remain_on_channel = iwl_mvm_roc, - .cancel_remain_on_channel = iwl_mvm_cancel_roc, - .add_chanctx = iwl_mvm_add_chanctx, - .remove_chanctx = iwl_mvm_remove_chanctx, - .change_chanctx = iwl_mvm_change_chanctx, - .assign_vif_chanctx = iwl_mvm_assign_vif_chanctx, - .unassign_vif_chanctx = iwl_mvm_unassign_vif_chanctx, - .switch_vif_chanctx = iwl_mvm_switch_vif_chanctx, - - .start_ap = iwl_mvm_start_ap_ibss, - .stop_ap = iwl_mvm_stop_ap_ibss, - .join_ibss = iwl_mvm_start_ap_ibss, - .leave_ibss = iwl_mvm_stop_ap_ibss, - - .set_tim = iwl_mvm_set_tim, - - .channel_switch = iwl_mvm_channel_switch, - .pre_channel_switch = iwl_mvm_pre_channel_switch, - .post_channel_switch = iwl_mvm_post_channel_switch, - - .tdls_channel_switch = iwl_mvm_tdls_channel_switch, - .tdls_cancel_channel_switch = iwl_mvm_tdls_cancel_channel_switch, - .tdls_recv_channel_switch = iwl_mvm_tdls_recv_channel_switch, - - .event_callback = iwl_mvm_mac_event_callback, - - CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd) - -#ifdef CONFIG_PM_SLEEP - /* look at d3.c */ - .suspend = iwl_mvm_suspend, - .resume = iwl_mvm_resume, - .set_wakeup = iwl_mvm_set_wakeup, - .set_rekey_data = iwl_mvm_set_rekey_data, -#if IS_ENABLED(CONFIG_IPV6) - .ipv6_addr_change = iwl_mvm_ipv6_addr_change, -#endif - .set_default_unicast_key = iwl_mvm_set_default_unicast_key, -#endif - .get_survey = iwl_mvm_mac_get_survey, - .sta_statistics = iwl_mvm_mac_sta_statistics, -}; diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h deleted file mode 100644 index 4bde2d027..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ /dev/null @@ -1,1535 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * 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 __IWL_MVM_H__ -#define __IWL_MVM_H__ - -#include <linux/list.h> -#include <linux/spinlock.h> -#include <linux/leds.h> -#include <linux/in6.h> - -#include "iwl-op-mode.h" -#include "iwl-trans.h" -#include "iwl-notif-wait.h" -#include "iwl-eeprom-parse.h" -#include "iwl-fw-file.h" -#include "iwl-config.h" -#include "sta.h" -#include "fw-api.h" -#include "constants.h" -#include "tof.h" - -#define IWL_MVM_MAX_ADDRESSES 5 -/* RSSI offset for WkP */ -#define IWL_RSSI_OFFSET 50 -#define IWL_MVM_MISSED_BEACONS_THRESHOLD 8 -/* A TimeUnit is 1024 microsecond */ -#define MSEC_TO_TU(_msec) (_msec*1000/1024) - -/* For GO, this value represents the number of TUs before CSA "beacon - * 0" TBTT when the CSA time-event needs to be scheduled to start. It - * must be big enough to ensure that we switch in time. - */ -#define IWL_MVM_CHANNEL_SWITCH_TIME_GO 40 - -/* For client, this value represents the number of TUs before CSA - * "beacon 1" TBTT, instead. This is because we don't know when the - * GO/AP will be in the new channel, so we switch early enough. - */ -#define IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT 10 - -/* - * This value (in TUs) is used to fine tune the CSA NoA end time which should - * be just before "beacon 0" TBTT. - */ -#define IWL_MVM_CHANNEL_SWITCH_MARGIN 4 - -/* - * Number of beacons to transmit on a new channel until we unblock tx to - * the stations, even if we didn't identify them on a new channel - */ -#define IWL_MVM_CS_UNBLOCK_TX_TIMEOUT 3 - -extern const struct ieee80211_ops iwl_mvm_hw_ops; - -/** - * struct iwl_mvm_mod_params - module parameters for iwlmvm - * @init_dbg: if true, then the NIC won't be stopped if the INIT fw asserted. - * We will register to mac80211 to have testmode working. The NIC must not - * be up'ed after the INIT fw asserted. This is useful to be able to use - * proprietary tools over testmode to debug the INIT fw. - * @tfd_q_hang_detect: enabled the detection of hung transmit queues - * @power_scheme: one of enum iwl_power_scheme - */ -struct iwl_mvm_mod_params { - bool init_dbg; - bool tfd_q_hang_detect; - int power_scheme; -}; -extern struct iwl_mvm_mod_params iwlmvm_mod_params; - -/** - * struct iwl_mvm_dump_ptrs - set of pointers needed for the fw-error-dump - * - * @op_mode_ptr: pointer to the buffer coming from the mvm op_mode - * @trans_ptr: pointer to struct %iwl_trans_dump_data which contains the - * transport's data. - * @trans_len: length of the valid data in trans_ptr - * @op_mode_len: length of the valid data in op_mode_ptr - */ -struct iwl_mvm_dump_ptrs { - struct iwl_trans_dump_data *trans_ptr; - void *op_mode_ptr; - u32 op_mode_len; -}; - -/** - * struct iwl_mvm_dump_desc - describes the dump - * @len: length of trig_desc->data - * @trig_desc: the description of the dump - */ -struct iwl_mvm_dump_desc { - size_t len; - /* must be last */ - struct iwl_fw_error_dump_trigger_desc trig_desc; -}; - -extern struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert; - -struct iwl_mvm_phy_ctxt { - u16 id; - u16 color; - u32 ref; - - /* - * TODO: This should probably be removed. Currently here only for rate - * scaling algorithm - */ - struct ieee80211_channel *channel; -}; - -struct iwl_mvm_time_event_data { - struct ieee80211_vif *vif; - struct list_head list; - unsigned long end_jiffies; - u32 duration; - bool running; - u32 uid; - - /* - * The access to the 'id' field must be done when the - * mvm->time_event_lock is held, as it value is used to indicate - * if the te is in the time event list or not (when id == TE_MAX) - */ - u32 id; -}; - - /* Power management */ - -/** - * enum iwl_power_scheme - * @IWL_POWER_LEVEL_CAM - Continuously Active Mode - * @IWL_POWER_LEVEL_BPS - Balanced Power Save (default) - * @IWL_POWER_LEVEL_LP - Low Power - */ -enum iwl_power_scheme { - IWL_POWER_SCHEME_CAM = 1, - IWL_POWER_SCHEME_BPS, - IWL_POWER_SCHEME_LP -}; - -#define IWL_CONN_MAX_LISTEN_INTERVAL 10 -#define IWL_UAPSD_MAX_SP IEEE80211_WMM_IE_STA_QOSINFO_SP_2 - -#ifdef CONFIG_IWLWIFI_DEBUGFS -enum iwl_dbgfs_pm_mask { - MVM_DEBUGFS_PM_KEEP_ALIVE = BIT(0), - MVM_DEBUGFS_PM_SKIP_OVER_DTIM = BIT(1), - MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS = BIT(2), - MVM_DEBUGFS_PM_RX_DATA_TIMEOUT = BIT(3), - MVM_DEBUGFS_PM_TX_DATA_TIMEOUT = BIT(4), - MVM_DEBUGFS_PM_LPRX_ENA = BIT(6), - MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD = BIT(7), - MVM_DEBUGFS_PM_SNOOZE_ENABLE = BIT(8), - MVM_DEBUGFS_PM_UAPSD_MISBEHAVING = BIT(9), - MVM_DEBUGFS_PM_USE_PS_POLL = BIT(10), -}; - -struct iwl_dbgfs_pm { - u16 keep_alive_seconds; - u32 rx_data_timeout; - u32 tx_data_timeout; - bool skip_over_dtim; - u8 skip_dtim_periods; - bool lprx_ena; - u32 lprx_rssi_threshold; - bool snooze_ena; - bool uapsd_misbehaving; - bool use_ps_poll; - int mask; -}; - -/* beacon filtering */ - -enum iwl_dbgfs_bf_mask { - MVM_DEBUGFS_BF_ENERGY_DELTA = BIT(0), - MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA = BIT(1), - MVM_DEBUGFS_BF_ROAMING_STATE = BIT(2), - MVM_DEBUGFS_BF_TEMP_THRESHOLD = BIT(3), - MVM_DEBUGFS_BF_TEMP_FAST_FILTER = BIT(4), - MVM_DEBUGFS_BF_TEMP_SLOW_FILTER = BIT(5), - MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER = BIT(6), - MVM_DEBUGFS_BF_DEBUG_FLAG = BIT(7), - MVM_DEBUGFS_BF_ESCAPE_TIMER = BIT(8), - MVM_DEBUGFS_BA_ESCAPE_TIMER = BIT(9), - MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT = BIT(10), -}; - -struct iwl_dbgfs_bf { - u32 bf_energy_delta; - u32 bf_roaming_energy_delta; - u32 bf_roaming_state; - u32 bf_temp_threshold; - u32 bf_temp_fast_filter; - u32 bf_temp_slow_filter; - u32 bf_enable_beacon_filter; - u32 bf_debug_flag; - u32 bf_escape_timer; - u32 ba_escape_timer; - u32 ba_enable_beacon_abort; - int mask; -}; -#endif - -enum iwl_mvm_smps_type_request { - IWL_MVM_SMPS_REQ_BT_COEX, - IWL_MVM_SMPS_REQ_TT, - IWL_MVM_SMPS_REQ_PROT, - NUM_IWL_MVM_SMPS_REQ, -}; - -enum iwl_mvm_ref_type { - IWL_MVM_REF_UCODE_DOWN, - IWL_MVM_REF_SCAN, - IWL_MVM_REF_ROC, - IWL_MVM_REF_ROC_AUX, - IWL_MVM_REF_P2P_CLIENT, - IWL_MVM_REF_AP_IBSS, - IWL_MVM_REF_USER, - IWL_MVM_REF_TX, - IWL_MVM_REF_TX_AGG, - IWL_MVM_REF_ADD_IF, - IWL_MVM_REF_START_AP, - IWL_MVM_REF_BSS_CHANGED, - IWL_MVM_REF_PREPARE_TX, - IWL_MVM_REF_PROTECT_TDLS, - IWL_MVM_REF_CHECK_CTKILL, - IWL_MVM_REF_PRPH_READ, - IWL_MVM_REF_PRPH_WRITE, - IWL_MVM_REF_NMI, - IWL_MVM_REF_TM_CMD, - IWL_MVM_REF_EXIT_WORK, - IWL_MVM_REF_PROTECT_CSA, - IWL_MVM_REF_FW_DBG_COLLECT, - - /* update debugfs.c when changing this */ - - IWL_MVM_REF_COUNT, -}; - -enum iwl_bt_force_ant_mode { - BT_FORCE_ANT_DIS = 0, - BT_FORCE_ANT_AUTO, - BT_FORCE_ANT_BT, - BT_FORCE_ANT_WIFI, - - BT_FORCE_ANT_MAX, -}; - -/** -* struct iwl_mvm_vif_bf_data - beacon filtering related data -* @bf_enabled: indicates if beacon filtering is enabled -* @ba_enabled: indicated if beacon abort is enabled -* @ave_beacon_signal: average beacon signal -* @last_cqm_event: rssi of the last cqm event -* @bt_coex_min_thold: minimum threshold for BT coex -* @bt_coex_max_thold: maximum threshold for BT coex -* @last_bt_coex_event: rssi of the last BT coex event -*/ -struct iwl_mvm_vif_bf_data { - bool bf_enabled; - bool ba_enabled; - int ave_beacon_signal; - int last_cqm_event; - int bt_coex_min_thold; - int bt_coex_max_thold; - int last_bt_coex_event; -}; - -/** - * struct iwl_mvm_vif - data per Virtual Interface, it is a MAC context - * @id: between 0 and 3 - * @color: to solve races upon MAC addition and removal - * @ap_sta_id: the sta_id of the AP - valid only if VIF type is STA - * @bssid: BSSID for this (client) interface - * @associated: indicates that we're currently associated, used only for - * managing the firmware state in iwl_mvm_bss_info_changed_station() - * @ap_assoc_sta_count: count of stations associated to us - valid only - * if VIF type is AP - * @uploaded: indicates the MAC context has been added to the device - * @ap_ibss_active: indicates that AP/IBSS is configured and that the interface - * should get quota etc. - * @pm_enabled - Indicate if MAC power management is allowed - * @monitor_active: indicates that monitor context is configured, and that the - * interface should get quota etc. - * @low_latency: indicates that this interface is in low-latency mode - * (VMACLowLatencyMode) - * @ps_disabled: indicates that this interface requires PS to be disabled - * @queue_params: QoS params for this MAC - * @bcast_sta: station used for broadcast packets. Used by the following - * vifs: P2P_DEVICE, GO and AP. - * @beacon_skb: the skb used to hold the AP/GO beacon template - * @smps_requests: the SMPS requests of different parts of the driver, - * combined on update to yield the overall request to mac80211. - * @beacon_stats: beacon statistics, containing the # of received beacons, - * # of received beacons accumulated over FW restart, and the current - * average signal of beacons retrieved from the firmware - * @csa_failed: CSA failed to schedule time event, report an error later - * @features: hw features active for this vif - */ -struct iwl_mvm_vif { - struct iwl_mvm *mvm; - u16 id; - u16 color; - u8 ap_sta_id; - - u8 bssid[ETH_ALEN]; - bool associated; - u8 ap_assoc_sta_count; - - bool uploaded; - bool ap_ibss_active; - bool pm_enabled; - bool monitor_active; - bool low_latency; - bool ps_disabled; - struct iwl_mvm_vif_bf_data bf_data; - - struct { - u32 num_beacons, accu_num_beacons; - u8 avg_signal; - } beacon_stats; - - u32 ap_beacon_time; - - enum iwl_tsf_id tsf_id; - - /* - * QoS data from mac80211, need to store this here - * as mac80211 has a separate callback but we need - * to have the data for the MAC context - */ - struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS]; - struct iwl_mvm_time_event_data time_event_data; - struct iwl_mvm_time_event_data hs_time_event_data; - - struct iwl_mvm_int_sta bcast_sta; - - /* - * Assigned while mac80211 has the interface in a channel context, - * or, for P2P Device, while it exists. - */ - struct iwl_mvm_phy_ctxt *phy_ctxt; - -#ifdef CONFIG_PM_SLEEP - /* WoWLAN GTK rekey data */ - struct { - u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN]; - __le64 replay_ctr; - bool valid; - } rekey_data; - - int tx_key_idx; - - bool seqno_valid; - u16 seqno; -#endif - -#if IS_ENABLED(CONFIG_IPV6) - /* IPv6 addresses for WoWLAN */ - struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX]; - int num_target_ipv6_addrs; -#endif - -#ifdef CONFIG_IWLWIFI_DEBUGFS - struct dentry *dbgfs_dir; - struct dentry *dbgfs_slink; - struct iwl_dbgfs_pm dbgfs_pm; - struct iwl_dbgfs_bf dbgfs_bf; - struct iwl_mac_power_cmd mac_pwr_cmd; -#endif - - enum ieee80211_smps_mode smps_requests[NUM_IWL_MVM_SMPS_REQ]; - - /* FW identified misbehaving AP */ - u8 uapsd_misbehaving_bssid[ETH_ALEN]; - - /* Indicates that CSA countdown may be started */ - bool csa_countdown; - bool csa_failed; - - /* TCP Checksum Offload */ - netdev_features_t features; -}; - -static inline struct iwl_mvm_vif * -iwl_mvm_vif_from_mac80211(struct ieee80211_vif *vif) -{ - return (void *)vif->drv_priv; -} - -extern const u8 tid_to_mac80211_ac[]; - -#define IWL_MVM_SCAN_STOPPING_SHIFT 8 - -enum iwl_scan_status { - IWL_MVM_SCAN_REGULAR = BIT(0), - IWL_MVM_SCAN_SCHED = BIT(1), - IWL_MVM_SCAN_NETDETECT = BIT(2), - - IWL_MVM_SCAN_STOPPING_REGULAR = BIT(8), - IWL_MVM_SCAN_STOPPING_SCHED = BIT(9), - IWL_MVM_SCAN_STOPPING_NETDETECT = BIT(10), - - IWL_MVM_SCAN_REGULAR_MASK = IWL_MVM_SCAN_REGULAR | - IWL_MVM_SCAN_STOPPING_REGULAR, - IWL_MVM_SCAN_SCHED_MASK = IWL_MVM_SCAN_SCHED | - IWL_MVM_SCAN_STOPPING_SCHED, - IWL_MVM_SCAN_NETDETECT_MASK = IWL_MVM_SCAN_NETDETECT | - IWL_MVM_SCAN_STOPPING_NETDETECT, - - IWL_MVM_SCAN_STOPPING_MASK = 0xff << IWL_MVM_SCAN_STOPPING_SHIFT, - IWL_MVM_SCAN_MASK = 0xff, -}; - -/** - * struct iwl_nvm_section - describes an NVM section in memory. - * - * This struct holds an NVM section read from the NIC using NVM_ACCESS_CMD, - * and saved for later use by the driver. Not all NVM sections are saved - * this way, only the needed ones. - */ -struct iwl_nvm_section { - u16 length; - const u8 *data; -}; - -/** - * struct iwl_mvm_tt_mgnt - Thermal Throttling Management structure - * @ct_kill_exit: worker to exit thermal kill - * @dynamic_smps: Is thermal throttling enabled dynamic_smps? - * @tx_backoff: The current thremal throttling tx backoff in uSec. - * @min_backoff: The minimal tx backoff due to power restrictions - * @params: Parameters to configure the thermal throttling algorithm. - * @throttle: Is thermal throttling is active? - */ -struct iwl_mvm_tt_mgmt { - struct delayed_work ct_kill_exit; - bool dynamic_smps; - u32 tx_backoff; - u32 min_backoff; - struct iwl_tt_params params; - bool throttle; -}; - -#define IWL_MVM_NUM_LAST_FRAMES_UCODE_RATES 8 - -struct iwl_mvm_frame_stats { - u32 legacy_frames; - u32 ht_frames; - u32 vht_frames; - u32 bw_20_frames; - u32 bw_40_frames; - u32 bw_80_frames; - u32 bw_160_frames; - u32 sgi_frames; - u32 ngi_frames; - u32 siso_frames; - u32 mimo2_frames; - u32 agg_frames; - u32 ampdu_count; - u32 success_frames; - u32 fail_frames; - u32 last_rates[IWL_MVM_NUM_LAST_FRAMES_UCODE_RATES]; - int last_frame_idx; -}; - -enum { - D0I3_DEFER_WAKEUP, - D0I3_PENDING_WAKEUP, -}; - -#define IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE 0xff -#define IWL_MVM_DEBUG_SET_TEMPERATURE_MIN -100 -#define IWL_MVM_DEBUG_SET_TEMPERATURE_MAX 200 - -enum iwl_mvm_tdls_cs_state { - IWL_MVM_TDLS_SW_IDLE = 0, - IWL_MVM_TDLS_SW_REQ_SENT, - IWL_MVM_TDLS_SW_RESP_RCVD, - IWL_MVM_TDLS_SW_REQ_RCVD, - IWL_MVM_TDLS_SW_ACTIVE, -}; - -struct iwl_mvm_shared_mem_cfg { - u32 shared_mem_addr; - u32 shared_mem_size; - u32 sample_buff_addr; - u32 sample_buff_size; - u32 txfifo_addr; - u32 txfifo_size[TX_FIFO_MAX_NUM]; - u32 rxfifo_size[RX_FIFO_MAX_NUM]; - u32 page_buff_addr; - u32 page_buff_size; -}; - -struct iwl_mvm { - /* for logger access */ - struct device *dev; - - struct iwl_trans *trans; - const struct iwl_fw *fw; - const struct iwl_cfg *cfg; - struct iwl_phy_db *phy_db; - struct ieee80211_hw *hw; - - /* for protecting access to iwl_mvm */ - struct mutex mutex; - struct list_head async_handlers_list; - spinlock_t async_handlers_lock; - struct work_struct async_handlers_wk; - - struct work_struct roc_done_wk; - - unsigned long status; - - /* - * for beacon filtering - - * currently only one interface can be supported - */ - struct iwl_mvm_vif *bf_allowed_vif; - - enum iwl_ucode_type cur_ucode; - bool ucode_loaded; - bool calibrating; - u32 error_event_table; - u32 log_event_table; - u32 umac_error_event_table; - bool support_umac_log; - struct iwl_sf_region sf_space; - - u32 ampdu_ref; - - struct iwl_notif_wait_data notif_wait; - - struct mvm_statistics_rx rx_stats; - - struct { - u64 rx_time; - u64 tx_time; - u64 on_time_rf; - u64 on_time_scan; - } radio_stats, accu_radio_stats; - - struct { - /* Map to HW queue */ - u32 hw_queue_to_mac80211; - u8 hw_queue_refcount; - bool setup_reserved; - u16 tid_bitmap; /* Bitmap of the TIDs mapped to this queue */ - } queue_info[IWL_MAX_HW_QUEUES]; - spinlock_t queue_info_lock; /* For syncing queue mgmt operations */ - atomic_t mac80211_queue_stop_count[IEEE80211_MAX_QUEUES]; - - const char *nvm_file_name; - struct iwl_nvm_data *nvm_data; - /* NVM sections */ - struct iwl_nvm_section nvm_sections[NVM_MAX_NUM_SECTIONS]; - - /* Paging section */ - struct iwl_fw_paging fw_paging_db[NUM_OF_FW_PAGING_BLOCKS]; - u16 num_of_paging_blk; - u16 num_of_pages_in_last_blk; - - /* EEPROM MAC addresses */ - struct mac_address addresses[IWL_MVM_MAX_ADDRESSES]; - - /* data related to data path */ - struct iwl_rx_phy_info last_phy_info; - 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]; - u32 tfd_drained[IWL_MVM_STATION_COUNT]; - u8 rx_ba_sessions; - - /* configured by mac80211 */ - u32 rts_threshold; - - /* Scan status, cmd (pre-allocated) and auxiliary station */ - unsigned int scan_status; - void *scan_cmd; - struct iwl_mcast_filter_cmd *mcast_filter_cmd; - - /* max number of simultaneous scans the FW supports */ - unsigned int max_scans; - - /* UMAC scan tracking */ - u32 scan_uid_status[IWL_MVM_MAX_UMAC_SCANS]; - - /* rx chain antennas set through debugfs for the scan command */ - u8 scan_rx_ant; - -#ifdef CONFIG_IWLWIFI_BCAST_FILTERING - /* broadcast filters to configure for each associated station */ - const struct iwl_fw_bcast_filter *bcast_filters; -#ifdef CONFIG_IWLWIFI_DEBUGFS - struct { - bool override; - struct iwl_bcast_filter_cmd cmd; - } dbgfs_bcast_filtering; -#endif -#endif - - /* Internal station */ - struct iwl_mvm_int_sta aux_sta; - - bool last_ebs_successful; - - u8 scan_last_antenna_idx; /* to toggle TX between antennas */ - u8 mgmt_last_antenna_idx; - - /* last smart fifo state that was successfully sent to firmware */ - enum iwl_sf_state sf_state; - -#ifdef CONFIG_IWLWIFI_DEBUGFS - struct dentry *debugfs_dir; - u32 dbgfs_sram_offset, dbgfs_sram_len; - u32 dbgfs_prph_reg_addr; - bool disable_power_off; - bool disable_power_off_d3; - - bool scan_iter_notif_enabled; - - struct debugfs_blob_wrapper nvm_hw_blob; - struct debugfs_blob_wrapper nvm_sw_blob; - struct debugfs_blob_wrapper nvm_calib_blob; - struct debugfs_blob_wrapper nvm_prod_blob; - struct debugfs_blob_wrapper nvm_phy_sku_blob; - - struct iwl_mvm_frame_stats drv_rx_stats; - spinlock_t drv_stats_lock; - u16 dbgfs_rx_phyinfo; -#endif - - struct iwl_mvm_phy_ctxt phy_ctxts[NUM_PHY_CTX]; - - struct list_head time_event_list; - spinlock_t time_event_lock; - - /* - * A bitmap indicating the index of the key in use. The firmware - * can hold 16 keys at most. Reflect this fact. - */ - unsigned long fw_key_table[BITS_TO_LONGS(STA_KEY_MAX_NUM)]; - u8 fw_key_deleted[STA_KEY_MAX_NUM]; - - /* references taken by the driver and spinlock protecting them */ - spinlock_t refs_lock; - u8 refs[IWL_MVM_REF_COUNT]; - - u8 vif_count; - - /* -1 for always, 0 for never, >0 for that many times */ - s8 restart_fw; - u8 fw_dbg_conf; - struct delayed_work fw_dump_wk; - struct iwl_mvm_dump_desc *fw_dump_desc; - struct iwl_fw_dbg_trigger_tlv *fw_dump_trig; - -#ifdef CONFIG_IWLWIFI_LEDS - struct led_classdev led; -#endif - - struct ieee80211_vif *p2p_device_vif; - -#ifdef CONFIG_PM_SLEEP - struct wiphy_wowlan_support wowlan; - int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen; - - /* sched scan settings for net detect */ - struct cfg80211_sched_scan_request *nd_config; - struct ieee80211_scan_ies nd_ies; - struct cfg80211_match_set *nd_match_sets; - int n_nd_match_sets; - struct ieee80211_channel **nd_channels; - int n_nd_channels; - bool net_detect; -#ifdef CONFIG_IWLWIFI_DEBUGFS - bool d3_wake_sysassert; - bool d3_test_active; - bool store_d3_resume_sram; - void *d3_resume_sram; - u32 d3_test_pme_ptr; - struct ieee80211_vif *keep_vif; - u32 last_netdetect_scans; /* no. of scans in the last net-detect wake */ -#endif -#endif - - /* d0i3 */ - u8 d0i3_ap_sta_id; - bool d0i3_offloading; - struct work_struct d0i3_exit_work; - struct sk_buff_head d0i3_tx; - /* protect d0i3_suspend_flags */ - struct mutex d0i3_suspend_mutex; - unsigned long d0i3_suspend_flags; - /* sync d0i3_tx queue and IWL_MVM_STATUS_IN_D0I3 status flag */ - spinlock_t d0i3_tx_lock; - wait_queue_head_t d0i3_exit_waitq; - - /* BT-Coex */ - u8 bt_ack_kill_msk[NUM_PHY_CTX]; - u8 bt_cts_kill_msk[NUM_PHY_CTX]; - - struct iwl_bt_coex_profile_notif_old last_bt_notif_old; - struct iwl_bt_coex_ci_cmd_old last_bt_ci_cmd_old; - struct iwl_bt_coex_profile_notif last_bt_notif; - struct iwl_bt_coex_ci_cmd last_bt_ci_cmd; - - u32 last_ant_isol; - u8 last_corun_lut; - u8 bt_tx_prio; - enum iwl_bt_force_ant_mode bt_force_ant_mode; - - /* Aux ROC */ - struct list_head aux_roc_te_list; - - /* Thermal Throttling and CTkill */ - struct iwl_mvm_tt_mgmt thermal_throttle; - s32 temperature; /* Celsius */ - /* - * Debug option to set the NIC temperature. This option makes the - * driver think this is the actual NIC temperature, and ignore the - * real temperature that is received from the fw - */ - bool temperature_test; /* Debug test temperature is enabled */ - - struct iwl_time_quota_cmd last_quota_cmd; - -#ifdef CONFIG_NL80211_TESTMODE - u32 noa_duration; - struct ieee80211_vif *noa_vif; -#endif - - /* Tx queues */ - u8 aux_queue; - u8 first_agg_queue; - u8 last_agg_queue; - - /* Indicate if device power save is allowed */ - u8 ps_disabled; /* u8 instead of bool to ease debugfs_create_* usage */ - - struct ieee80211_vif __rcu *csa_vif; - struct ieee80211_vif __rcu *csa_tx_blocked_vif; - u8 csa_tx_block_bcn_timeout; - - /* system time of last beacon (for AP/GO interface) */ - u32 ap_last_beacon_gp2; - - bool lar_regdom_set; - enum iwl_mcc_source mcc_src; - - u8 low_latency_agg_frame_limit; - - /* TDLS channel switch data */ - struct { - struct delayed_work dwork; - enum iwl_mvm_tdls_cs_state state; - - /* - * Current cs sta - might be different from periodic cs peer - * station. Value is meaningless when the cs-state is idle. - */ - u8 cur_sta_id; - - /* TDLS periodic channel-switch peer */ - struct { - u8 sta_id; - u8 op_class; - bool initiator; /* are we the link initiator */ - struct cfg80211_chan_def chandef; - struct sk_buff *skb; /* ch sw template */ - u32 ch_sw_tm_ie; - - /* timestamp of last ch-sw request sent (GP2 time) */ - u32 sent_timestamp; - } peer; - } tdls_cs; - - struct iwl_mvm_shared_mem_cfg shared_mem_cfg; - - u32 ciphers[6]; - struct iwl_mvm_tof_data tof_data; -}; - -/* Extract MVM priv from op_mode and _hw */ -#define IWL_OP_MODE_GET_MVM(_iwl_op_mode) \ - ((struct iwl_mvm *)(_iwl_op_mode)->op_mode_specific) - -#define IWL_MAC80211_GET_MVM(_hw) \ - IWL_OP_MODE_GET_MVM((struct iwl_op_mode *)((_hw)->priv)) - -enum iwl_mvm_status { - IWL_MVM_STATUS_HW_RFKILL, - IWL_MVM_STATUS_HW_CTKILL, - IWL_MVM_STATUS_ROC_RUNNING, - IWL_MVM_STATUS_IN_HW_RESTART, - IWL_MVM_STATUS_IN_D0I3, - IWL_MVM_STATUS_ROC_AUX_RUNNING, - IWL_MVM_STATUS_D3_RECONFIG, - IWL_MVM_STATUS_DUMPING_FW_LOG, -}; - -static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm) -{ - return test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status) || - test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status); -} - -static inline bool iwl_mvm_is_radio_hw_killed(struct iwl_mvm *mvm) -{ - return test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status); -} - -/* Must be called with rcu_read_lock() held and it can only be - * released when mvmsta is not needed anymore. - */ -static inline struct iwl_mvm_sta * -iwl_mvm_sta_from_staid_rcu(struct iwl_mvm *mvm, u8 sta_id) -{ - struct ieee80211_sta *sta; - - if (sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id)) - return NULL; - - sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); - - /* This can happen if the station has been removed right now */ - if (IS_ERR_OR_NULL(sta)) - return NULL; - - return iwl_mvm_sta_from_mac80211(sta); -} - -static inline struct iwl_mvm_sta * -iwl_mvm_sta_from_staid_protected(struct iwl_mvm *mvm, u8 sta_id) -{ - struct ieee80211_sta *sta; - - if (sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id)) - return NULL; - - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], - lockdep_is_held(&mvm->mutex)); - - /* This can happen if the station has been removed right now */ - if (IS_ERR_OR_NULL(sta)) - return NULL; - - return iwl_mvm_sta_from_mac80211(sta); -} - -static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm) -{ - return mvm->trans->cfg->d0i3 && - mvm->trans->d0i3_mode != IWL_D0I3_MODE_OFF && - !iwlwifi_mod_params.d0i3_disable && - fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_D0I3_SUPPORT); -} - -static inline bool iwl_mvm_is_dqa_supported(struct iwl_mvm *mvm) -{ - return fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_DQA_SUPPORT); -} - -static inline bool iwl_mvm_is_lar_supported(struct iwl_mvm *mvm) -{ - bool nvm_lar = mvm->nvm_data->lar_enabled; - bool tlv_lar = fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_LAR_SUPPORT); - - if (iwlwifi_mod_params.lar_disable) - return false; - - /* - * Enable LAR only if it is supported by the FW (TLV) && - * enabled in the NVM - */ - if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000) - return nvm_lar && tlv_lar; - else - return tlv_lar; -} - -static inline bool iwl_mvm_is_wifi_mcc_supported(struct iwl_mvm *mvm) -{ - return fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_WIFI_MCC_UPDATE) || - fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC); -} - -static inline bool iwl_mvm_bt_is_plcr_supported(struct iwl_mvm *mvm) -{ - return fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_BT_COEX_PLCR) && - IWL_MVM_BT_COEX_CORUNNING; -} - -static inline bool iwl_mvm_bt_is_rrc_supported(struct iwl_mvm *mvm) -{ - return fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_BT_COEX_RRC) && - IWL_MVM_BT_COEX_RRC; -} - -static inline bool iwl_mvm_is_csum_supported(struct iwl_mvm *mvm) -{ - return fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_CSUM_SUPPORT); -} - -static inline bool iwl_mvm_has_new_rx_api(struct iwl_mvm *mvm) -{ - /* firmware flag isn't defined yet */ - return false; -} - -extern const u8 iwl_mvm_ac_to_tx_fifo[]; - -struct iwl_rate_info { - u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */ - u8 plcp_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */ - u8 plcp_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */ - u8 plcp_mimo3; /* uCode API: IWL_RATE_MIMO3_6M_PLCP, etc. */ - u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */ -}; - -void __iwl_mvm_mac_stop(struct iwl_mvm *mvm); -int __iwl_mvm_mac_start(struct iwl_mvm *mvm); - -/****************** - * MVM Methods - ******************/ -/* uCode */ -int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm); - -/* Utils */ -int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags, - enum ieee80211_band band); -void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags, - enum ieee80211_band band, - struct ieee80211_tx_rate *r); -u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx); -void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm); -u8 first_antenna(u8 mask); -u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx); - -/* Tx / Host Commands */ -int __must_check iwl_mvm_send_cmd(struct iwl_mvm *mvm, - struct iwl_host_cmd *cmd); -int __must_check iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u32 id, - u32 flags, u16 len, const void *data); -int __must_check iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, - struct iwl_host_cmd *cmd, - u32 *status); -int __must_check iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u32 id, - u16 len, const void *data, - u32 *status); -int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, - struct ieee80211_sta *sta); -int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb); -void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, - struct iwl_tx_cmd *tx_cmd, - struct ieee80211_tx_info *info, u8 sta_id); -void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd, - struct ieee80211_tx_info *info, - struct ieee80211_sta *sta, __le16 fc); -#ifdef CONFIG_IWLWIFI_DEBUG -const char *iwl_mvm_get_tx_fail_reason(u32 status); -#else -static inline const char *iwl_mvm_get_tx_fail_reason(u32 status) { return ""; } -#endif -int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, u32 flags); -void iwl_mvm_async_handlers_purge(struct iwl_mvm *mvm); - -static inline void iwl_mvm_set_tx_cmd_ccmp(struct ieee80211_tx_info *info, - struct iwl_tx_cmd *tx_cmd) -{ - struct ieee80211_key_conf *keyconf = info->control.hw_key; - - tx_cmd->sec_ctl = TX_CMD_SEC_CCM; - memcpy(tx_cmd->key, keyconf->key, keyconf->keylen); - if (info->flags & IEEE80211_TX_CTL_AMPDU) - tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_CCMP_AGG); -} - -static inline void iwl_mvm_wait_for_async_handlers(struct iwl_mvm *mvm) -{ - flush_work(&mvm->async_handlers_wk); -} - -/* Statistics */ -void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm, - struct iwl_rx_packet *pkt); -void iwl_mvm_rx_statistics(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -int iwl_mvm_request_statistics(struct iwl_mvm *mvm, bool clear); -void iwl_mvm_accu_radio_stats(struct iwl_mvm *mvm); - -/* NVM */ -int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic); -int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm); - -static inline u8 iwl_mvm_get_valid_tx_ant(struct iwl_mvm *mvm) -{ - return mvm->nvm_data && mvm->nvm_data->valid_tx_ant ? - mvm->fw->valid_tx_ant & mvm->nvm_data->valid_tx_ant : - mvm->fw->valid_tx_ant; -} - -static inline u8 iwl_mvm_get_valid_rx_ant(struct iwl_mvm *mvm) -{ - return mvm->nvm_data && mvm->nvm_data->valid_rx_ant ? - mvm->fw->valid_rx_ant & mvm->nvm_data->valid_rx_ant : - mvm->fw->valid_rx_ant; -} - -static inline u32 iwl_mvm_get_phy_config(struct iwl_mvm *mvm) -{ - u32 phy_config = ~(FW_PHY_CFG_TX_CHAIN | - FW_PHY_CFG_RX_CHAIN); - u32 valid_rx_ant = iwl_mvm_get_valid_rx_ant(mvm); - u32 valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm); - - phy_config |= valid_tx_ant << FW_PHY_CFG_TX_CHAIN_POS | - valid_rx_ant << FW_PHY_CFG_RX_CHAIN_POS; - - return mvm->fw->phy_config & phy_config; -} - -int iwl_mvm_up(struct iwl_mvm *mvm); -int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm); - -int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm); -bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm, - struct iwl_bcast_filter_cmd *cmd); - -/* - * FW notifications / CMD responses handlers - * Convention: iwl_mvm_rx_<NAME OF THE CMD> - */ -void iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, - struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_fw_error(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_card_state_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_mfuart_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_shared_mem_cfg_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); - -/* MVM PHY */ -int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, - struct cfg80211_chan_def *chandef, - u8 chains_static, u8 chains_dynamic); -int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, - struct cfg80211_chan_def *chandef, - u8 chains_static, u8 chains_dynamic); -void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, - struct iwl_mvm_phy_ctxt *ctxt); -void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, - struct iwl_mvm_phy_ctxt *ctxt); -int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm); -u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef); -u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef); - -/* MAC (virtual interface) programming */ -int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - bool force_assoc_off, const u8 *bssid_override); -int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -u32 iwl_mvm_mac_get_queues_mask(struct ieee80211_vif *vif); -int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm, - struct ieee80211_vif *vif); -void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm, - struct ieee80211_vif *vif); -unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm, - struct ieee80211_vif *exclude_vif); -/* Bindings */ -int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); - -/* Quota management */ -int iwl_mvm_update_quotas(struct iwl_mvm *mvm, bool force_upload, - struct ieee80211_vif *disabled_vif); - -/* Scanning */ -int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req, - struct ieee80211_scan_ies *ies); -int iwl_mvm_scan_size(struct iwl_mvm *mvm); -int iwl_mvm_scan_stop(struct iwl_mvm *mvm, int type, bool notify); -int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm); -void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm); - -/* Scheduled scan */ -void iwl_mvm_rx_lmac_scan_complete_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_lmac_scan_iter_complete_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct cfg80211_sched_scan_request *req, - struct ieee80211_scan_ies *ies, - int type); -void iwl_mvm_rx_scan_match_found(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); - -/* UMAC scan */ -int iwl_mvm_config_scan(struct iwl_mvm *mvm); -void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); - -/* MVM debugfs */ -#ifdef CONFIG_IWLWIFI_DEBUGFS -int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir); -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 */ -int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init); -void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg); -int rs_pretty_print_rate(char *buf, const u32 rate); -void rs_update_last_rssi(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct ieee80211_rx_status *rx_status); - -/* power management */ -int iwl_mvm_power_update_device(struct iwl_mvm *mvm); -int iwl_mvm_power_update_mac(struct iwl_mvm *mvm); -int iwl_mvm_power_update_ps(struct iwl_mvm *mvm); -int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - char *buf, int bufsz); - -void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -void iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); - -#ifdef CONFIG_IWLWIFI_LEDS -int iwl_mvm_leds_init(struct iwl_mvm *mvm); -void iwl_mvm_leds_exit(struct iwl_mvm *mvm); -#else -static inline int iwl_mvm_leds_init(struct iwl_mvm *mvm) -{ - return 0; -} -static inline void iwl_mvm_leds_exit(struct iwl_mvm *mvm) -{ -} -#endif - -/* D3 (WoWLAN, NetDetect) */ -int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); -int iwl_mvm_resume(struct ieee80211_hw *hw); -void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled); -void iwl_mvm_set_rekey_data(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_gtk_rekey_data *data); -void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct inet6_dev *idev); -void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, int idx); -extern const struct file_operations iwl_dbgfs_d3_test_ops; -#ifdef CONFIG_PM_SLEEP -void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, - struct ieee80211_vif *vif); -#else -static inline void -iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ -} -#endif -void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta, - struct iwl_wowlan_config_cmd *cmd); -int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool disable_offloading, - u32 cmd_flags); - -/* D0i3 */ -void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type); -void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type); -int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type); -bool iwl_mvm_ref_taken(struct iwl_mvm *mvm); -void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq); -int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode); -int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode); -int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm); - -/* BT Coex */ -int iwl_send_bt_init_conf(struct iwl_mvm *mvm); -void iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - enum ieee80211_rssi_event_data); -void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm); -u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, - struct ieee80211_sta *sta); -bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, - struct ieee80211_sta *sta); -bool iwl_mvm_bt_coex_is_ant_avail(struct iwl_mvm *mvm, u8 ant); -bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm); -bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, - enum ieee80211_band band); -u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, - struct ieee80211_tx_info *info, u8 ac); - -bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm); -void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm); -int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm); -void iwl_mvm_rx_bt_coex_notif_old(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - enum ieee80211_rssi_event_data); -u16 iwl_mvm_coex_agg_time_limit_old(struct iwl_mvm *mvm, - struct ieee80211_sta *sta); -bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm, - struct ieee80211_sta *sta); -bool iwl_mvm_bt_coex_is_tpc_allowed_old(struct iwl_mvm *mvm, - enum ieee80211_band band); -void iwl_mvm_rx_ant_coupling_notif_old(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); - -/* beacon filtering */ -#ifdef CONFIG_IWLWIFI_DEBUGFS -void -iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, - struct iwl_beacon_filter_cmd *cmd); -#else -static inline void -iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, - struct iwl_beacon_filter_cmd *cmd) -{} -#endif -int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool enable, u32 flags); -int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 flags); -int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 flags); -/* SMPS */ -void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - enum iwl_mvm_smps_type_request req_type, - enum ieee80211_smps_mode smps_request); -bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm); - -/* Low latency */ -int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - bool value); -/* get SystemLowLatencyMode - only needed for beacon threshold? */ -bool iwl_mvm_low_latency(struct iwl_mvm *mvm); -/* get VMACLowLatencyMode */ -static inline bool iwl_mvm_vif_low_latency(struct iwl_mvm_vif *mvmvif) -{ - /* - * should this consider associated/active/... state? - * - * Normally low-latency should only be active on interfaces - * that are active, but at least with debugfs it can also be - * enabled on interfaces that aren't active. However, when - * interface aren't active then they aren't added into the - * binding, so this has no real impact. For now, just return - * the current desired low-latency state. - */ - - return mvmvif->low_latency; -} - -/* hw scheduler queue config */ -void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, - u16 ssn, const struct iwl_trans_txq_scd_cfg *cfg, - unsigned int wdg_timeout); -/* - * Disable a TXQ. - * Note that in non-DQA mode the %mac80211_queue and %tid params are ignored. - */ -void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, - u8 tid, u8 flags); -int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 minq, u8 maxq); - -static inline -void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, - u8 fifo, u16 ssn, unsigned int wdg_timeout) -{ - struct iwl_trans_txq_scd_cfg cfg = { - .fifo = fifo, - .tid = IWL_MAX_TID_COUNT, - .aggregate = false, - .frame_limit = IWL_FRAME_LIMIT, - }; - - iwl_mvm_enable_txq(mvm, queue, mac80211_queue, ssn, &cfg, wdg_timeout); -} - -static inline void iwl_mvm_enable_agg_txq(struct iwl_mvm *mvm, int queue, - int mac80211_queue, int fifo, - int sta_id, int tid, int frame_limit, - u16 ssn, unsigned int wdg_timeout) -{ - struct iwl_trans_txq_scd_cfg cfg = { - .fifo = fifo, - .sta_id = sta_id, - .tid = tid, - .frame_limit = frame_limit, - .aggregate = true, - }; - - iwl_mvm_enable_txq(mvm, queue, mac80211_queue, ssn, &cfg, wdg_timeout); -} - -/* Thermal management and CT-kill */ -void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff); -void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp); -void iwl_mvm_temp_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_tt_handler(struct iwl_mvm *mvm); -void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff); -void iwl_mvm_tt_exit(struct iwl_mvm *mvm); -void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state); -int iwl_mvm_get_temp(struct iwl_mvm *mvm); - -/* Location Aware Regulatory */ -struct iwl_mcc_update_resp * -iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, - enum iwl_mcc_source src_id); -int iwl_mvm_init_mcc(struct iwl_mvm *mvm); -void iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy, - const char *alpha2, - enum iwl_mcc_source src_id, - bool *changed); -struct ieee80211_regdomain *iwl_mvm_get_current_regdomain(struct iwl_mvm *mvm, - bool *changed); -int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm); -void iwl_mvm_update_changed_regdom(struct iwl_mvm *mvm); - -/* smart fifo */ -int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - bool added_vif); - -/* TDLS */ - -/* - * We use TID 4 (VI) as a FW-used-only TID when TDLS connections are present. - * This TID is marked as used vs the AP and all connected TDLS peers. - */ -#define IWL_MVM_TDLS_FW_TID 4 - -int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm); -void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - bool sta_added); -void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw, - struct ieee80211_vif *vif); -int iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u8 oper_class, - struct cfg80211_chan_def *chandef, - struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie); -void iwl_mvm_tdls_recv_channel_switch(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_tdls_ch_sw_params *params); -void iwl_mvm_tdls_cancel_channel_switch(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta); -void iwl_mvm_rx_tdls_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_tdls_ch_switch_work(struct work_struct *work); - -struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm); - -void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error); -void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm); - -int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 id); -int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig, - const char *str, size_t len, - struct iwl_fw_dbg_trigger_tlv *trigger); -int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, - struct iwl_mvm_dump_desc *desc, - struct iwl_fw_dbg_trigger_tlv *trigger); -void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm); -int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm, - struct iwl_fw_dbg_trigger_tlv *trigger, - const char *fmt, ...) __printf(3, 4); -unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool tdls, bool cmd_q); -void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - const char *errmsg); -static inline bool -iwl_fw_dbg_trigger_vif_match(struct iwl_fw_dbg_trigger_tlv *trig, - struct ieee80211_vif *vif) -{ - u32 trig_vif = le32_to_cpu(trig->vif_type); - - return trig_vif == IWL_FW_DBG_CONF_VIF_ANY || vif->type == trig_vif; -} - -static inline bool -iwl_fw_dbg_trigger_stop_conf_match(struct iwl_mvm *mvm, - struct iwl_fw_dbg_trigger_tlv *trig) -{ - return ((trig->mode & IWL_FW_DBG_TRIGGER_STOP) && - (mvm->fw_dbg_conf == FW_DBG_INVALID || - (BIT(mvm->fw_dbg_conf) & le32_to_cpu(trig->stop_conf_ids)))); -} - -static inline bool -iwl_fw_dbg_trigger_check_stop(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_fw_dbg_trigger_tlv *trig) -{ - if (vif && !iwl_fw_dbg_trigger_vif_match(trig, vif)) - return false; - - return iwl_fw_dbg_trigger_stop_conf_match(mvm, trig); -} - -static inline void -iwl_fw_dbg_trigger_simple_stop(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - enum iwl_fw_dbg_trigger trig) -{ - struct iwl_fw_dbg_trigger_tlv *trigger; - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, trig)) - return; - - trigger = iwl_fw_dbg_get_trigger(mvm->fw, trig); - if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger)) - return; - - iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL); -} - -#endif /* __IWL_MVM_H__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c deleted file mode 100644 index a26970c02..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ /dev/null @@ -1,864 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * 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 <linux/firmware.h> -#include <linux/rtnetlink.h> -#include <linux/pci.h> -#include <linux/acpi.h> -#include "iwl-trans.h" -#include "iwl-csr.h" -#include "mvm.h" -#include "iwl-eeprom-parse.h" -#include "iwl-eeprom-read.h" -#include "iwl-nvm-parse.h" -#include "iwl-prph.h" - -/* Default NVM size to read */ -#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024) -#define IWL_MAX_NVM_SECTION_SIZE 0x1b58 -#define IWL_MAX_NVM_8000_SECTION_SIZE 0x1ffc - -#define NVM_WRITE_OPCODE 1 -#define NVM_READ_OPCODE 0 - -/* load nvm chunk response */ -enum { - READ_NVM_CHUNK_SUCCEED = 0, - READ_NVM_CHUNK_NOT_VALID_ADDRESS = 1 -}; - -/* - * prepare the NVM host command w/ the pointers to the nvm buffer - * and send it to fw - */ -static int iwl_nvm_write_chunk(struct iwl_mvm *mvm, u16 section, - u16 offset, u16 length, const u8 *data) -{ - struct iwl_nvm_access_cmd nvm_access_cmd = { - .offset = cpu_to_le16(offset), - .length = cpu_to_le16(length), - .type = cpu_to_le16(section), - .op_code = NVM_WRITE_OPCODE, - }; - struct iwl_host_cmd cmd = { - .id = NVM_ACCESS_CMD, - .len = { sizeof(struct iwl_nvm_access_cmd), length }, - .flags = CMD_SEND_IN_RFKILL, - .data = { &nvm_access_cmd, data }, - /* data may come from vmalloc, so use _DUP */ - .dataflags = { 0, IWL_HCMD_DFL_DUP }, - }; - - return iwl_mvm_send_cmd(mvm, &cmd); -} - -static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section, - u16 offset, u16 length, u8 *data) -{ - struct iwl_nvm_access_cmd nvm_access_cmd = { - .offset = cpu_to_le16(offset), - .length = cpu_to_le16(length), - .type = cpu_to_le16(section), - .op_code = NVM_READ_OPCODE, - }; - struct iwl_nvm_access_resp *nvm_resp; - struct iwl_rx_packet *pkt; - struct iwl_host_cmd cmd = { - .id = NVM_ACCESS_CMD, - .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL, - .data = { &nvm_access_cmd, }, - }; - int ret, bytes_read, offset_read; - u8 *resp_data; - - cmd.len[0] = sizeof(struct iwl_nvm_access_cmd); - - ret = iwl_mvm_send_cmd(mvm, &cmd); - if (ret) - return ret; - - pkt = cmd.resp_pkt; - - /* Extract NVM response */ - nvm_resp = (void *)pkt->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) { - if ((offset != 0) && - (ret == READ_NVM_CHUNK_NOT_VALID_ADDRESS)) { - /* - * meaning of NOT_VALID_ADDRESS: - * driver try to read chunk from address that is - * multiple of 2K and got an error since addr is empty. - * meaning of (offset != 0): driver already - * read valid data from another chunk so this case - * is not an error. - */ - IWL_DEBUG_EEPROM(mvm->trans->dev, - "NVM access command failed on offset 0x%x since that section size is multiple 2K\n", - offset); - ret = 0; - } else { - IWL_DEBUG_EEPROM(mvm->trans->dev, - "NVM access command failed with status %d (device: %s)\n", - ret, mvm->cfg->name); - ret = -EIO; - } - goto exit; - } - - if (offset_read != offset) { - IWL_ERR(mvm, "NVM ACCESS response with invalid offset %d\n", - offset_read); - ret = -EINVAL; - goto exit; - } - - /* Write data to NVM */ - memcpy(data + offset, resp_data, bytes_read); - ret = bytes_read; - -exit: - iwl_free_resp(&cmd); - return ret; -} - -static int iwl_nvm_write_section(struct iwl_mvm *mvm, u16 section, - const u8 *data, u16 length) -{ - int offset = 0; - - /* copy data in chunks of 2k (and remainder if any) */ - - while (offset < length) { - int chunk_size, ret; - - chunk_size = min(IWL_NVM_DEFAULT_CHUNK_SIZE, - length - offset); - - ret = iwl_nvm_write_chunk(mvm, section, offset, - chunk_size, data + offset); - if (ret < 0) - return ret; - - offset += chunk_size; - } - - return 0; -} - -/* - * Reads an NVM section completely. - * NICs prior to 7000 family doesn't have a real NVM, but just read - * section 0 which is the EEPROM. Because the EEPROM reading is unlimited - * by uCode, we need to manually check in this case that we don't - * overflow and try to read more than the EEPROM size. - * For 7000 family NICs, we supply the maximal size we can read, and - * the uCode fills the response with as much data as we can, - * without overflowing, so no check is needed. - */ -static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section, - u8 *data, u32 size_read) -{ - u16 length, offset = 0; - int ret; - - /* Set nvm section read length */ - length = IWL_NVM_DEFAULT_CHUNK_SIZE; - - ret = length; - - /* Read the NVM until exhausted (reading less than requested) */ - while (ret == length) { - /* Check no memory assumptions fail and cause an overflow */ - if ((size_read + offset + length) > - mvm->cfg->base_params->eeprom_size) { - IWL_ERR(mvm, "EEPROM size is too small for NVM\n"); - return -ENOBUFS; - } - - ret = iwl_nvm_read_chunk(mvm, section, offset, length, data); - if (ret < 0) { - IWL_DEBUG_EEPROM(mvm->trans->dev, - "Cannot read NVM from section %d offset %d, length %d\n", - section, offset, length); - return ret; - } - offset += ret; - } - - IWL_DEBUG_EEPROM(mvm->trans->dev, - "NVM section %d read completed\n", section); - return offset; -} - -static struct iwl_nvm_data * -iwl_parse_nvm_sections(struct iwl_mvm *mvm) -{ - struct iwl_nvm_section *sections = mvm->nvm_sections; - const __le16 *hw, *sw, *calib, *regulatory, *mac_override, *phy_sku; - bool lar_enabled; - u32 mac_addr0, mac_addr1; - - /* Checking for required sections */ - if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { - if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data || - !mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data) { - IWL_ERR(mvm, "Can't parse empty OTP/NVM sections\n"); - return NULL; - } - } else { - /* SW and REGULATORY sections are mandatory */ - if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data || - !mvm->nvm_sections[NVM_SECTION_TYPE_REGULATORY].data) { - IWL_ERR(mvm, - "Can't parse empty family 8000 OTP/NVM sections\n"); - return NULL; - } - /* MAC_OVERRIDE or at least HW section must exist */ - if (!mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data && - !mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data) { - IWL_ERR(mvm, - "Can't parse mac_address, empty sections\n"); - return NULL; - } - - /* PHY_SKU section is mandatory in B0 */ - if (!mvm->nvm_sections[NVM_SECTION_TYPE_PHY_SKU].data) { - IWL_ERR(mvm, - "Can't parse phy_sku in B0, empty sections\n"); - return NULL; - } - } - - if (WARN_ON(!mvm->cfg)) - return NULL; - - /* read the mac address from WFMP registers */ - mac_addr0 = iwl_trans_read_prph(mvm->trans, WFMP_MAC_ADDR_0); - mac_addr1 = iwl_trans_read_prph(mvm->trans, WFMP_MAC_ADDR_1); - - hw = (const __le16 *)sections[mvm->cfg->nvm_hw_section_num].data; - sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data; - calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data; - regulatory = (const __le16 *)sections[NVM_SECTION_TYPE_REGULATORY].data; - mac_override = - (const __le16 *)sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data; - phy_sku = (const __le16 *)sections[NVM_SECTION_TYPE_PHY_SKU].data; - - lar_enabled = !iwlwifi_mod_params.lar_disable && - fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_LAR_SUPPORT); - - return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib, - regulatory, mac_override, phy_sku, - mvm->fw->valid_tx_ant, mvm->fw->valid_rx_ant, - lar_enabled, mac_addr0, mac_addr1, - mvm->trans->hw_id); -} - -#define MAX_NVM_FILE_LEN 16384 - -/* - * Reads external NVM from a file into mvm->nvm_sections - * - * HOW TO CREATE THE NVM FILE FORMAT: - * ------------------------------ - * 1. create hex file, format: - * 3800 -> header - * 0000 -> header - * 5a40 -> data - * - * rev - 6 bit (word1) - * len - 10 bit (word1) - * id - 4 bit (word2) - * rsv - 12 bit (word2) - * - * 2. flip 8bits with 8 bits per line to get the right NVM file format - * - * 3. create binary file from the hex file - * - * 4. save as "iNVM_xxx.bin" under /lib/firmware - */ -static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm) -{ - int ret, section_size; - u16 section_id; - const struct firmware *fw_entry; - const struct { - __le16 word1; - __le16 word2; - u8 data[]; - } *file_sec; - const u8 *eof, *temp; - int max_section_size; - const __le32 *dword_buff; - -#define NVM_WORD1_LEN(x) (8 * (x & 0x03FF)) -#define NVM_WORD2_ID(x) (x >> 12) -#define NVM_WORD2_LEN_FAMILY_8000(x) (2 * ((x & 0xFF) << 8 | x >> 8)) -#define NVM_WORD1_ID_FAMILY_8000(x) (x >> 4) -#define NVM_HEADER_0 (0x2A504C54) -#define NVM_HEADER_1 (0x4E564D2A) -#define NVM_HEADER_SIZE (4 * sizeof(u32)) - - IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from external NVM\n"); - - /* Maximal size depends on HW family and step */ - if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) - max_section_size = IWL_MAX_NVM_SECTION_SIZE; - else - max_section_size = IWL_MAX_NVM_8000_SECTION_SIZE; - - /* - * Obtain NVM image via request_firmware. Since we already used - * reject_firmware_nowait() for the firmware binary load and only - * get here after that we assume the NVM request can be satisfied - * synchronously. - */ - ret = reject_firmware(&fw_entry, mvm->nvm_file_name, - mvm->trans->dev); - if (ret) { - IWL_ERR(mvm, "ERROR: %s isn't available %d\n", - mvm->nvm_file_name, ret); - return ret; - } - - IWL_INFO(mvm, "Loaded NVM file %s (%zu bytes)\n", - mvm->nvm_file_name, fw_entry->size); - - if (fw_entry->size > MAX_NVM_FILE_LEN) { - IWL_ERR(mvm, "NVM file too large\n"); - ret = -EINVAL; - goto out; - } - - eof = fw_entry->data + fw_entry->size; - dword_buff = (__le32 *)fw_entry->data; - - /* some NVM file will contain a header. - * The header is identified by 2 dwords header as follow: - * dword[0] = 0x2A504C54 - * dword[1] = 0x4E564D2A - * - * This header must be skipped when providing the NVM data to the FW. - */ - if (fw_entry->size > NVM_HEADER_SIZE && - dword_buff[0] == cpu_to_le32(NVM_HEADER_0) && - dword_buff[1] == cpu_to_le32(NVM_HEADER_1)) { - file_sec = (void *)(fw_entry->data + NVM_HEADER_SIZE); - IWL_INFO(mvm, "NVM Version %08X\n", le32_to_cpu(dword_buff[2])); - IWL_INFO(mvm, "NVM Manufacturing date %08X\n", - le32_to_cpu(dword_buff[3])); - - /* nvm file validation, dword_buff[2] holds the file version */ - if ((CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_C_STEP && - le32_to_cpu(dword_buff[2]) < 0xE4A) || - (CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP && - le32_to_cpu(dword_buff[2]) >= 0xE4A)) { - ret = -EFAULT; - goto out; - } - } else { - file_sec = (void *)fw_entry->data; - } - - while (true) { - if (file_sec->data > eof) { - IWL_ERR(mvm, - "ERROR - NVM file too short for section header\n"); - ret = -EINVAL; - break; - } - - /* check for EOF marker */ - if (!file_sec->word1 && !file_sec->word2) { - ret = 0; - break; - } - - if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { - section_size = - 2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1)); - section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2)); - } else { - section_size = 2 * NVM_WORD2_LEN_FAMILY_8000( - le16_to_cpu(file_sec->word2)); - section_id = NVM_WORD1_ID_FAMILY_8000( - le16_to_cpu(file_sec->word1)); - } - - if (section_size > max_section_size) { - IWL_ERR(mvm, "ERROR - section too large (%d)\n", - section_size); - ret = -EINVAL; - break; - } - - if (!section_size) { - IWL_ERR(mvm, "ERROR - section empty\n"); - ret = -EINVAL; - break; - } - - if (file_sec->data + section_size > eof) { - IWL_ERR(mvm, - "ERROR - NVM file too short for section (%d bytes)\n", - section_size); - ret = -EINVAL; - break; - } - - if (WARN(section_id >= NVM_MAX_NUM_SECTIONS, - "Invalid NVM section ID %d\n", section_id)) { - ret = -EINVAL; - break; - } - - temp = kmemdup(file_sec->data, section_size, GFP_KERNEL); - if (!temp) { - ret = -ENOMEM; - break; - } - kfree(mvm->nvm_sections[section_id].data); - mvm->nvm_sections[section_id].data = temp; - mvm->nvm_sections[section_id].length = section_size; - - /* advance to the next section */ - file_sec = (void *)(file_sec->data + section_size); - } -out: - release_firmware(fw_entry); - return ret; -} - -/* Loads the NVM data stored in mvm->nvm_sections into the NIC */ -int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm) -{ - int i, ret = 0; - struct iwl_nvm_section *sections = mvm->nvm_sections; - - IWL_DEBUG_EEPROM(mvm->trans->dev, "'Write to NVM\n"); - - for (i = 0; i < ARRAY_SIZE(mvm->nvm_sections); i++) { - if (!mvm->nvm_sections[i].data || !mvm->nvm_sections[i].length) - continue; - ret = iwl_nvm_write_section(mvm, i, sections[i].data, - sections[i].length); - if (ret < 0) { - IWL_ERR(mvm, "iwl_mvm_send_cmd failed: %d\n", ret); - break; - } - } - return ret; -} - -int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic) -{ - int ret, section; - u32 size_read = 0; - u8 *nvm_buffer, *temp; - const char *nvm_file_B = mvm->cfg->default_nvm_file_B_step; - const char *nvm_file_C = mvm->cfg->default_nvm_file_C_step; - - if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS)) - return -EINVAL; - - /* load NVM values from nic */ - if (read_nvm_from_nic) { - /* Read From FW NVM */ - IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n"); - - nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size, - GFP_KERNEL); - if (!nvm_buffer) - return -ENOMEM; - for (section = 0; section < NVM_MAX_NUM_SECTIONS; section++) { - /* we override the constness for initial read */ - ret = iwl_nvm_read_section(mvm, section, nvm_buffer, - size_read); - if (ret < 0) - continue; - size_read += ret; - temp = kmemdup(nvm_buffer, ret, GFP_KERNEL); - if (!temp) { - ret = -ENOMEM; - break; - } - mvm->nvm_sections[section].data = temp; - mvm->nvm_sections[section].length = ret; - -#ifdef CONFIG_IWLWIFI_DEBUGFS - switch (section) { - case NVM_SECTION_TYPE_SW: - mvm->nvm_sw_blob.data = temp; - mvm->nvm_sw_blob.size = ret; - break; - case NVM_SECTION_TYPE_CALIBRATION: - mvm->nvm_calib_blob.data = temp; - mvm->nvm_calib_blob.size = ret; - break; - case NVM_SECTION_TYPE_PRODUCTION: - mvm->nvm_prod_blob.data = temp; - mvm->nvm_prod_blob.size = ret; - break; - case NVM_SECTION_TYPE_PHY_SKU: - mvm->nvm_phy_sku_blob.data = temp; - mvm->nvm_phy_sku_blob.size = ret; - break; - default: - if (section == mvm->cfg->nvm_hw_section_num) { - mvm->nvm_hw_blob.data = temp; - mvm->nvm_hw_blob.size = ret; - break; - } - } -#endif - } - if (!size_read) - IWL_ERR(mvm, "OTP is blank\n"); - kfree(nvm_buffer); - } - - /* Only if PNVM selected in the mod param - load external NVM */ - if (mvm->nvm_file_name) { - /* read External NVM file from the mod param */ - ret = iwl_mvm_read_external_nvm(mvm); - if (ret) { - /* choose the nvm_file name according to the - * HW step - */ - if (CSR_HW_REV_STEP(mvm->trans->hw_rev) == - SILICON_B_STEP) - mvm->nvm_file_name = nvm_file_B; - else - mvm->nvm_file_name = nvm_file_C; - - if (ret == -EFAULT && mvm->nvm_file_name) { - /* in case nvm file was failed try again */ - ret = iwl_mvm_read_external_nvm(mvm); - if (ret) - return ret; - } else { - return ret; - } - } - } - - /* parse the relevant nvm sections */ - mvm->nvm_data = iwl_parse_nvm_sections(mvm); - if (!mvm->nvm_data) - return -ENODATA; - IWL_DEBUG_EEPROM(mvm->trans->dev, "nvm version = %x\n", - mvm->nvm_data->nvm_version); - - return 0; -} - -struct iwl_mcc_update_resp * -iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, - enum iwl_mcc_source src_id) -{ - struct iwl_mcc_update_cmd mcc_update_cmd = { - .mcc = cpu_to_le16(alpha2[0] << 8 | alpha2[1]), - .source_id = (u8)src_id, - }; - struct iwl_mcc_update_resp *mcc_resp, *resp_cp = NULL; - struct iwl_rx_packet *pkt; - struct iwl_host_cmd cmd = { - .id = MCC_UPDATE_CMD, - .flags = CMD_WANT_SKB, - .data = { &mcc_update_cmd }, - }; - - int ret; - u32 status; - int resp_len, n_channels; - u16 mcc; - - if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm))) - return ERR_PTR(-EOPNOTSUPP); - - cmd.len[0] = sizeof(struct iwl_mcc_update_cmd); - - IWL_DEBUG_LAR(mvm, "send MCC update to FW with '%c%c' src = %d\n", - alpha2[0], alpha2[1], src_id); - - ret = iwl_mvm_send_cmd(mvm, &cmd); - if (ret) - return ERR_PTR(ret); - - pkt = cmd.resp_pkt; - - /* Extract MCC response */ - mcc_resp = (void *)pkt->data; - status = le32_to_cpu(mcc_resp->status); - - mcc = le16_to_cpu(mcc_resp->mcc); - - /* W/A for a FW/NVM issue - returns 0x00 for the world domain */ - if (mcc == 0) { - mcc = 0x3030; /* "00" - world */ - mcc_resp->mcc = cpu_to_le16(mcc); - } - - n_channels = __le32_to_cpu(mcc_resp->n_channels); - IWL_DEBUG_LAR(mvm, - "MCC response status: 0x%x. new MCC: 0x%x ('%c%c') change: %d n_chans: %d\n", - status, mcc, mcc >> 8, mcc & 0xff, - !!(status == MCC_RESP_NEW_CHAN_PROFILE), n_channels); - - resp_len = sizeof(*mcc_resp) + n_channels * sizeof(__le32); - resp_cp = kmemdup(mcc_resp, resp_len, GFP_KERNEL); - if (!resp_cp) { - ret = -ENOMEM; - goto exit; - } - - ret = 0; -exit: - iwl_free_resp(&cmd); - if (ret) - return ERR_PTR(ret); - return resp_cp; -} - -#ifdef CONFIG_ACPI -#define WRD_METHOD "WRDD" -#define WRDD_WIFI (0x07) -#define WRDD_WIGIG (0x10) - -static u32 iwl_mvm_wrdd_get_mcc(struct iwl_mvm *mvm, union acpi_object *wrdd) -{ - union acpi_object *mcc_pkg, *domain_type, *mcc_value; - u32 i; - - if (wrdd->type != ACPI_TYPE_PACKAGE || - wrdd->package.count < 2 || - wrdd->package.elements[0].type != ACPI_TYPE_INTEGER || - wrdd->package.elements[0].integer.value != 0) { - IWL_DEBUG_LAR(mvm, "Unsupported wrdd structure\n"); - return 0; - } - - for (i = 1 ; i < wrdd->package.count ; ++i) { - mcc_pkg = &wrdd->package.elements[i]; - - if (mcc_pkg->type != ACPI_TYPE_PACKAGE || - mcc_pkg->package.count < 2 || - mcc_pkg->package.elements[0].type != ACPI_TYPE_INTEGER || - mcc_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) { - mcc_pkg = NULL; - continue; - } - - domain_type = &mcc_pkg->package.elements[0]; - if (domain_type->integer.value == WRDD_WIFI) - break; - - mcc_pkg = NULL; - } - - if (mcc_pkg) { - mcc_value = &mcc_pkg->package.elements[1]; - return mcc_value->integer.value; - } - - return 0; -} - -static int iwl_mvm_get_bios_mcc(struct iwl_mvm *mvm, char *mcc) -{ - acpi_handle root_handle; - acpi_handle handle; - struct acpi_buffer wrdd = {ACPI_ALLOCATE_BUFFER, NULL}; - acpi_status status; - u32 mcc_val; - struct pci_dev *pdev = to_pci_dev(mvm->dev); - - root_handle = ACPI_HANDLE(&pdev->dev); - if (!root_handle) { - IWL_DEBUG_LAR(mvm, - "Could not retrieve root port ACPI handle\n"); - return -ENOENT; - } - - /* Get the method's handle */ - status = acpi_get_handle(root_handle, (acpi_string)WRD_METHOD, &handle); - if (ACPI_FAILURE(status)) { - IWL_DEBUG_LAR(mvm, "WRD method not found\n"); - return -ENOENT; - } - - /* Call WRDD with no arguments */ - status = acpi_evaluate_object(handle, NULL, NULL, &wrdd); - if (ACPI_FAILURE(status)) { - IWL_DEBUG_LAR(mvm, "WRDC invocation failed (0x%x)\n", status); - return -ENOENT; - } - - mcc_val = iwl_mvm_wrdd_get_mcc(mvm, wrdd.pointer); - kfree(wrdd.pointer); - if (!mcc_val) - return -ENOENT; - - mcc[0] = (mcc_val >> 8) & 0xff; - mcc[1] = mcc_val & 0xff; - mcc[2] = '\0'; - return 0; -} -#else /* CONFIG_ACPI */ -static int iwl_mvm_get_bios_mcc(struct iwl_mvm *mvm, char *mcc) -{ - return -ENOENT; -} -#endif - -int iwl_mvm_init_mcc(struct iwl_mvm *mvm) -{ - bool tlv_lar; - bool nvm_lar; - int retval; - struct ieee80211_regdomain *regd; - char mcc[3]; - - if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000) { - tlv_lar = fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_LAR_SUPPORT); - nvm_lar = mvm->nvm_data->lar_enabled; - if (tlv_lar != nvm_lar) - IWL_INFO(mvm, - "Conflict between TLV & NVM regarding enabling LAR (TLV = %s NVM =%s)\n", - tlv_lar ? "enabled" : "disabled", - nvm_lar ? "enabled" : "disabled"); - } - - if (!iwl_mvm_is_lar_supported(mvm)) - return 0; - - /* - * try to replay the last set MCC to FW. If it doesn't exist, - * queue an update to cfg80211 to retrieve the default alpha2 from FW. - */ - retval = iwl_mvm_init_fw_regd(mvm); - if (retval != -ENOENT) - return retval; - - /* - * Driver regulatory hint for initial update, this also informs the - * firmware we support wifi location updates. - * Disallow scans that might crash the FW while the LAR regdomain - * is not set. - */ - mvm->lar_regdom_set = false; - - regd = iwl_mvm_get_current_regdomain(mvm, NULL); - if (IS_ERR_OR_NULL(regd)) - return -EIO; - - if (iwl_mvm_is_wifi_mcc_supported(mvm) && - !iwl_mvm_get_bios_mcc(mvm, mcc)) { - kfree(regd); - regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc, - MCC_SOURCE_BIOS, NULL); - if (IS_ERR_OR_NULL(regd)) - return -EIO; - } - - retval = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd); - kfree(regd); - return retval; -} - -void iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_mcc_chub_notif *notif = (void *)pkt->data; - enum iwl_mcc_source src; - char mcc[3]; - struct ieee80211_regdomain *regd; - - lockdep_assert_held(&mvm->mutex); - - if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm))) - return; - - mcc[0] = notif->mcc >> 8; - mcc[1] = notif->mcc & 0xff; - mcc[2] = '\0'; - src = notif->source_id; - - IWL_DEBUG_LAR(mvm, - "RX: received chub update mcc cmd (mcc '%s' src %d)\n", - mcc, src); - regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc, src, NULL); - if (IS_ERR_OR_NULL(regd)) - return; - - regulatory_set_wiphy_regd(mvm->hw->wiphy, regd); - kfree(regd); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/offloading.c b/drivers/net/wireless/iwlwifi/mvm/offloading.c deleted file mode 100644 index 68b0169c8..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/offloading.c +++ /dev/null @@ -1,217 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * 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/ipv6.h> -#include <net/addrconf.h> -#include "mvm.h" - -void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta, - struct iwl_wowlan_config_cmd *cmd) -{ - int i; - - /* - * For QoS counters, we store the one to use next, so subtract 0x10 - * since the uCode will add 0x10 *before* using the value while we - * increment after using the value (i.e. store the next value to use). - */ - for (i = 0; i < IWL_MAX_TID_COUNT; i++) { - u16 seq = mvm_ap_sta->tid_data[i].seq_number; - seq -= 0x10; - cmd->qos_seq[i] = cpu_to_le16(seq); - } -} - -int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool disable_offloading, - u32 cmd_flags) -{ - union { - struct iwl_proto_offload_cmd_v1 v1; - struct iwl_proto_offload_cmd_v2 v2; - struct iwl_proto_offload_cmd_v3_small v3s; - struct iwl_proto_offload_cmd_v3_large v3l; - } cmd = {}; - struct iwl_host_cmd hcmd = { - .id = PROT_OFFLOAD_CONFIG_CMD, - .flags = cmd_flags, - .data[0] = &cmd, - .dataflags[0] = IWL_HCMD_DFL_DUP, - }; - struct iwl_proto_offload_cmd_common *common; - u32 enabled = 0, size; - u32 capa_flags = mvm->fw->ucode_capa.flags; -#if IS_ENABLED(CONFIG_IPV6) - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int i; - - if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL || - capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) { - struct iwl_ns_config *nsc; - struct iwl_targ_addr *addrs; - int n_nsc, n_addrs; - int c; - - if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) { - nsc = cmd.v3s.ns_config; - n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S; - addrs = cmd.v3s.targ_addrs; - n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S; - } else { - nsc = cmd.v3l.ns_config; - n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L; - addrs = cmd.v3l.targ_addrs; - n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L; - } - - if (mvmvif->num_target_ipv6_addrs) - enabled |= IWL_D3_PROTO_OFFLOAD_NS; - - /* - * For each address we have (and that will fit) fill a target - * address struct and combine for NS offload structs with the - * solicited node addresses. - */ - for (i = 0, c = 0; - i < mvmvif->num_target_ipv6_addrs && - i < n_addrs && c < n_nsc; i++) { - struct in6_addr solicited_addr; - int j; - - addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i], - &solicited_addr); - for (j = 0; j < c; j++) - if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr, - &solicited_addr) == 0) - break; - if (j == c) - c++; - addrs[i].addr = mvmvif->target_ipv6_addrs[i]; - addrs[i].config_num = cpu_to_le32(j); - nsc[j].dest_ipv6_addr = solicited_addr; - memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN); - } - - if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) - cmd.v3s.num_valid_ipv6_addrs = cpu_to_le32(i); - else - cmd.v3l.num_valid_ipv6_addrs = cpu_to_le32(i); - } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { - if (mvmvif->num_target_ipv6_addrs) { - enabled |= IWL_D3_PROTO_OFFLOAD_NS; - memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN); - } - - BUILD_BUG_ON(sizeof(cmd.v2.target_ipv6_addr[0]) != - sizeof(mvmvif->target_ipv6_addrs[0])); - - for (i = 0; i < min(mvmvif->num_target_ipv6_addrs, - IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++) - memcpy(cmd.v2.target_ipv6_addr[i], - &mvmvif->target_ipv6_addrs[i], - sizeof(cmd.v2.target_ipv6_addr[i])); - } else { - if (mvmvif->num_target_ipv6_addrs) { - enabled |= IWL_D3_PROTO_OFFLOAD_NS; - memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN); - } - - BUILD_BUG_ON(sizeof(cmd.v1.target_ipv6_addr[0]) != - sizeof(mvmvif->target_ipv6_addrs[0])); - - for (i = 0; i < min(mvmvif->num_target_ipv6_addrs, - IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++) - memcpy(cmd.v1.target_ipv6_addr[i], - &mvmvif->target_ipv6_addrs[i], - sizeof(cmd.v1.target_ipv6_addr[i])); - } -#endif - - if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) { - common = &cmd.v3s.common; - size = sizeof(cmd.v3s); - } else if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) { - common = &cmd.v3l.common; - size = sizeof(cmd.v3l); - } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { - common = &cmd.v2.common; - size = sizeof(cmd.v2); - } else { - common = &cmd.v1.common; - size = sizeof(cmd.v1); - } - - if (vif->bss_conf.arp_addr_cnt) { - enabled |= IWL_D3_PROTO_OFFLOAD_ARP; - common->host_ipv4_addr = vif->bss_conf.arp_addr_list[0]; - memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN); - } - - if (!disable_offloading) - common->enabled = cpu_to_le32(enabled); - - hcmd.len[0] = size; - return iwl_mvm_send_cmd(mvm, &hcmd); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c deleted file mode 100644 index 13c97f665..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ /dev/null @@ -1,1434 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * 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 <linux/module.h> -#include <linux/vmalloc.h> -#include <net/mac80211.h> - -#include "iwl-notif-wait.h" -#include "iwl-trans.h" -#include "iwl-op-mode.h" -#include "iwl-fw.h" -#include "iwl-debug.h" -#include "iwl-drv.h" -#include "iwl-modparams.h" -#include "mvm.h" -#include "iwl-phy-db.h" -#include "iwl-eeprom-parse.h" -#include "iwl-csr.h" -#include "iwl-io.h" -#include "iwl-prph.h" -#include "rs.h" -#include "fw-api-scan.h" -#include "time-event.h" - -#define DRV_DESCRIPTION "The new Intel(R) wireless AGN driver for Linux" -MODULE_DESCRIPTION(DRV_DESCRIPTION); -MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); -MODULE_LICENSE("GPL"); - -static const struct iwl_op_mode_ops iwl_mvm_ops; -static const struct iwl_op_mode_ops iwl_mvm_ops_mq; - -struct iwl_mvm_mod_params iwlmvm_mod_params = { - .power_scheme = IWL_POWER_SCHEME_BPS, - .tfd_q_hang_detect = true - /* rest of fields are 0 by default */ -}; - -module_param_named(init_dbg, iwlmvm_mod_params.init_dbg, bool, S_IRUGO); -MODULE_PARM_DESC(init_dbg, - "set to true to debug an ASSERT in INIT fw (default: false"); -module_param_named(power_scheme, iwlmvm_mod_params.power_scheme, int, S_IRUGO); -MODULE_PARM_DESC(power_scheme, - "power management scheme: 1-active, 2-balanced, 3-low power, default: 2"); -module_param_named(tfd_q_hang_detect, iwlmvm_mod_params.tfd_q_hang_detect, - bool, S_IRUGO); -MODULE_PARM_DESC(tfd_q_hang_detect, - "TFD queues hang detection (default: true"); - -/* - * module init and exit functions - */ -static int __init iwl_mvm_init(void) -{ - int ret; - - ret = iwl_mvm_rate_control_register(); - if (ret) { - pr_err("Unable to register rate control algorithm: %d\n", ret); - return ret; - } - - ret = iwl_opmode_register("iwlmvm", &iwl_mvm_ops); - - if (ret) { - pr_err("Unable to register MVM op_mode: %d\n", ret); - iwl_mvm_rate_control_unregister(); - } - - return ret; -} -module_init(iwl_mvm_init); - -static void __exit iwl_mvm_exit(void) -{ - iwl_opmode_deregister("iwlmvm"); - iwl_mvm_rate_control_unregister(); -} -module_exit(iwl_mvm_exit); - -static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode) -{ - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - u8 radio_cfg_type, radio_cfg_step, radio_cfg_dash; - u32 reg_val = 0; - u32 phy_config = iwl_mvm_get_phy_config(mvm); - - radio_cfg_type = (phy_config & FW_PHY_CFG_RADIO_TYPE) >> - FW_PHY_CFG_RADIO_TYPE_POS; - radio_cfg_step = (phy_config & FW_PHY_CFG_RADIO_STEP) >> - FW_PHY_CFG_RADIO_STEP_POS; - radio_cfg_dash = (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) << - CSR_HW_IF_CONFIG_REG_POS_MAC_STEP; - reg_val |= CSR_HW_REV_DASH(mvm->trans->hw_rev) << - CSR_HW_IF_CONFIG_REG_POS_MAC_DASH; - - /* radio configuration */ - reg_val |= radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE; - reg_val |= radio_cfg_step << CSR_HW_IF_CONFIG_REG_POS_PHY_STEP; - reg_val |= radio_cfg_dash << CSR_HW_IF_CONFIG_REG_POS_PHY_DASH; - - WARN_ON((radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE) & - ~CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE); - - /* - * TODO: Bits 7-8 of CSR in 8000 HW family set the ADC sampling, and - * shouldn't be set to any non-zero value. The same is supposed to be - * true of the other HW, but unsetting them (such as the 7260) causes - * automatic tests to fail on seemingly unrelated errors. Need to - * further investigate this, but for now we'll separate cases. - */ - if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) - reg_val |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI; - - iwl_trans_set_bits_mask(mvm->trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH | - CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP | - CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE | - CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP | - CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH | - CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | - CSR_HW_IF_CONFIG_REG_BIT_MAC_SI, - reg_val); - - IWL_DEBUG_INFO(mvm, "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type, - radio_cfg_step, radio_cfg_dash); - - /* - * W/A : NIC is stuck in a reset state after Early PCIe power off - * (PCIe power is lost before PERST# is asserted), causing ME FW - * to lose ownership and not being able to obtain it back. - */ - if (!mvm->trans->cfg->apmg_not_supported) - iwl_set_bits_mask_prph(mvm->trans, APMG_PS_CTRL_REG, - APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, - ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); -} - -struct iwl_rx_handlers { - u16 cmd_id; - bool async; - void (*fn)(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); -}; - -#define RX_HANDLER(_cmd_id, _fn, _async) \ - { .cmd_id = _cmd_id , .fn = _fn , .async = _async } -#define RX_HANDLER_GRP(_grp, _cmd, _fn, _async) \ - { .cmd_id = WIDE_ID(_grp, _cmd), .fn = _fn, .async = _async } - -/* - * Handlers for fw notifications - * Convention: RX_HANDLER(CMD_NAME, iwl_mvm_rx_CMD_NAME - * This list should be in order of frequency for performance purposes. - * - * The handler can be SYNC - this means that it will be called in the Rx path - * which can't acquire mvm->mutex. If the handler needs to hold mvm->mutex (and - * only in this case!), it should be set as ASYNC. In that case, it will be - * called from a worker with mvm->mutex held. - */ -static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { - RX_HANDLER(TX_CMD, iwl_mvm_rx_tx_cmd, false), - RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, false), - - RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true), - RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, true), - RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, true), - RX_HANDLER(ANTENNA_COUPLING_NOTIFICATION, - iwl_mvm_rx_ant_coupling_notif, true), - - RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, false), - RX_HANDLER(MCC_CHUB_UPDATE_CMD, iwl_mvm_rx_chub_update_mcc, true), - - RX_HANDLER(EOSP_NOTIFICATION, iwl_mvm_rx_eosp_notif, false), - - RX_HANDLER(SCAN_ITERATION_COMPLETE, - iwl_mvm_rx_lmac_scan_iter_complete_notif, false), - RX_HANDLER(SCAN_OFFLOAD_COMPLETE, - iwl_mvm_rx_lmac_scan_complete_notif, true), - RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_scan_match_found, - false), - RX_HANDLER(SCAN_COMPLETE_UMAC, iwl_mvm_rx_umac_scan_complete_notif, - true), - RX_HANDLER(SCAN_ITERATION_COMPLETE_UMAC, - iwl_mvm_rx_umac_scan_iter_complete_notif, false), - - RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false), - - RX_HANDLER(MISSED_BEACONS_NOTIFICATION, iwl_mvm_rx_missed_beacons_notif, - false), - - RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, false), - RX_HANDLER(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION, - iwl_mvm_power_uapsd_misbehaving_ap_notif, false), - RX_HANDLER(DTS_MEASUREMENT_NOTIFICATION, iwl_mvm_temp_notif, true), - RX_HANDLER_GRP(PHY_OPS_GROUP, DTS_MEASUREMENT_NOTIF_WIDE, - iwl_mvm_temp_notif, true), - - RX_HANDLER(TDLS_CHANNEL_SWITCH_NOTIFICATION, iwl_mvm_rx_tdls_notif, - true), - RX_HANDLER(MFUART_LOAD_NOTIFICATION, iwl_mvm_rx_mfuart_notif, false), - RX_HANDLER(TOF_NOTIFICATION, iwl_mvm_tof_resp_handler, true), - -}; -#undef RX_HANDLER -#undef RX_HANDLER_GRP -#define CMD(x) [x] = #x - -static const char *const iwl_mvm_cmd_strings[REPLY_MAX + 1] = { - CMD(MVM_ALIVE), - CMD(REPLY_ERROR), - CMD(ECHO_CMD), - CMD(INIT_COMPLETE_NOTIF), - CMD(PHY_CONTEXT_CMD), - CMD(MGMT_MCAST_KEY), - CMD(TX_CMD), - CMD(TXPATH_FLUSH), - CMD(SHARED_MEM_CFG), - CMD(MAC_CONTEXT_CMD), - CMD(TIME_EVENT_CMD), - CMD(TIME_EVENT_NOTIFICATION), - CMD(BINDING_CONTEXT_CMD), - CMD(TIME_QUOTA_CMD), - CMD(NON_QOS_TX_COUNTER_CMD), - CMD(DC2DC_CONFIG_CMD), - CMD(NVM_ACCESS_CMD), - CMD(PHY_CONFIGURATION_CMD), - CMD(CALIB_RES_NOTIF_PHY_DB), - CMD(SET_CALIB_DEFAULT_CMD), - CMD(FW_PAGING_BLOCK_CMD), - CMD(ADD_STA_KEY), - CMD(ADD_STA), - CMD(FW_GET_ITEM_CMD), - CMD(REMOVE_STA), - CMD(LQ_CMD), - CMD(SCAN_OFFLOAD_CONFIG_CMD), - CMD(MATCH_FOUND_NOTIFICATION), - CMD(SCAN_OFFLOAD_REQUEST_CMD), - CMD(SCAN_OFFLOAD_ABORT_CMD), - CMD(HOT_SPOT_CMD), - CMD(SCAN_OFFLOAD_COMPLETE), - CMD(SCAN_OFFLOAD_UPDATE_PROFILES_CMD), - CMD(SCAN_ITERATION_COMPLETE), - CMD(POWER_TABLE_CMD), - CMD(WEP_KEY), - CMD(REPLY_RX_PHY_CMD), - CMD(REPLY_RX_MPDU_CMD), - CMD(BEACON_NOTIFICATION), - CMD(BEACON_TEMPLATE_CMD), - CMD(STATISTICS_CMD), - CMD(STATISTICS_NOTIFICATION), - CMD(EOSP_NOTIFICATION), - CMD(REDUCE_TX_POWER_CMD), - CMD(TX_ANT_CONFIGURATION_CMD), - CMD(D3_CONFIG_CMD), - CMD(D0I3_END_CMD), - CMD(PROT_OFFLOAD_CONFIG_CMD), - CMD(OFFLOADS_QUERY_CMD), - CMD(REMOTE_WAKE_CONFIG_CMD), - CMD(WOWLAN_PATTERNS), - CMD(WOWLAN_CONFIGURATION), - CMD(WOWLAN_TSC_RSC_PARAM), - CMD(WOWLAN_TKIP_PARAM), - CMD(WOWLAN_KEK_KCK_MATERIAL), - CMD(WOWLAN_GET_STATUSES), - CMD(WOWLAN_TX_POWER_PER_DB), - CMD(SCAN_OFFLOAD_PROFILES_QUERY_CMD), - CMD(SCAN_OFFLOAD_HOTSPOTS_CONFIG_CMD), - CMD(SCAN_OFFLOAD_HOTSPOTS_QUERY_CMD), - CMD(CARD_STATE_NOTIFICATION), - CMD(MISSED_BEACONS_NOTIFICATION), - CMD(BT_COEX_PRIO_TABLE), - CMD(BT_COEX_PROT_ENV), - CMD(BT_PROFILE_NOTIFICATION), - CMD(BT_CONFIG), - CMD(MCAST_FILTER_CMD), - CMD(BCAST_FILTER_CMD), - CMD(REPLY_SF_CFG_CMD), - CMD(REPLY_BEACON_FILTERING_CMD), - CMD(CMD_DTS_MEASUREMENT_TRIGGER), - CMD(DTS_MEASUREMENT_NOTIFICATION), - CMD(REPLY_THERMAL_MNG_BACKOFF), - CMD(MAC_PM_POWER_TABLE), - CMD(LTR_CONFIG), - CMD(BT_COEX_CI), - CMD(BT_COEX_UPDATE_SW_BOOST), - CMD(BT_COEX_UPDATE_CORUN_LUT), - CMD(BT_COEX_UPDATE_REDUCED_TXP), - CMD(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION), - CMD(ANTENNA_COUPLING_NOTIFICATION), - CMD(SCD_QUEUE_CFG), - CMD(SCAN_CFG_CMD), - CMD(SCAN_REQ_UMAC), - CMD(SCAN_ABORT_UMAC), - CMD(SCAN_COMPLETE_UMAC), - CMD(TDLS_CHANNEL_SWITCH_CMD), - CMD(TDLS_CHANNEL_SWITCH_NOTIFICATION), - CMD(TDLS_CONFIG_CMD), - CMD(MCC_UPDATE_CMD), - CMD(SCAN_ITERATION_COMPLETE_UMAC), -}; -#undef CMD - -/* this forward declaration can avoid to export the function */ -static void iwl_mvm_async_handlers_wk(struct work_struct *wk); -static void iwl_mvm_d0i3_exit_work(struct work_struct *wk); - -static u32 calc_min_backoff(struct iwl_trans *trans, const struct iwl_cfg *cfg) -{ - const struct iwl_pwr_tx_backoff *pwr_tx_backoff = cfg->pwr_tx_backoffs; - - if (!pwr_tx_backoff) - return 0; - - while (pwr_tx_backoff->pwr) { - if (trans->dflt_pwr_limit >= pwr_tx_backoff->pwr) - return pwr_tx_backoff->backoff; - - pwr_tx_backoff++; - } - - return 0; -} - -static void iwl_mvm_fw_error_dump_wk(struct work_struct *work); - -static struct iwl_op_mode * -iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, - const struct iwl_fw *fw, struct dentry *dbgfs_dir) -{ - struct ieee80211_hw *hw; - struct iwl_op_mode *op_mode; - struct iwl_mvm *mvm; - struct iwl_trans_config trans_cfg = {}; - static const u8 no_reclaim_cmds[] = { - TX_CMD, - }; - int err, scan_size; - u32 min_backoff; - - /* - * We use IWL_MVM_STATION_COUNT to check the validity of the station - * index all over the driver - check that its value corresponds to the - * array size. - */ - BUILD_BUG_ON(ARRAY_SIZE(mvm->fw_id_to_mac_id) != IWL_MVM_STATION_COUNT); - - /******************************** - * 1. Allocating and configuring HW data - ********************************/ - hw = ieee80211_alloc_hw(sizeof(struct iwl_op_mode) + - sizeof(struct iwl_mvm), - &iwl_mvm_hw_ops); - if (!hw) - return NULL; - - if (cfg->max_rx_agg_size) - hw->max_rx_aggregation_subframes = cfg->max_rx_agg_size; - - if (cfg->max_tx_agg_size) - hw->max_tx_aggregation_subframes = cfg->max_tx_agg_size; - - op_mode = hw->priv; - - mvm = IWL_OP_MODE_GET_MVM(op_mode); - mvm->dev = trans->dev; - mvm->trans = trans; - mvm->cfg = cfg; - mvm->fw = fw; - mvm->hw = hw; - - if (iwl_mvm_has_new_rx_api(mvm)) { - op_mode->ops = &iwl_mvm_ops_mq; - } else { - op_mode->ops = &iwl_mvm_ops; - - if (WARN_ON(trans->num_rx_queues > 1)) - goto out_free; - } - - mvm->restart_fw = iwlwifi_mod_params.restart_fw ? -1 : 0; - - mvm->aux_queue = 15; - mvm->first_agg_queue = 16; - mvm->last_agg_queue = mvm->cfg->base_params->num_of_queues - 1; - if (mvm->cfg->base_params->num_of_queues == 16) { - mvm->aux_queue = 11; - mvm->first_agg_queue = 12; - } - mvm->sf_state = SF_UNINIT; - mvm->low_latency_agg_frame_limit = 6; - mvm->cur_ucode = IWL_UCODE_INIT; - - mutex_init(&mvm->mutex); - mutex_init(&mvm->d0i3_suspend_mutex); - spin_lock_init(&mvm->async_handlers_lock); - INIT_LIST_HEAD(&mvm->time_event_list); - INIT_LIST_HEAD(&mvm->aux_roc_te_list); - INIT_LIST_HEAD(&mvm->async_handlers_list); - spin_lock_init(&mvm->time_event_lock); - spin_lock_init(&mvm->queue_info_lock); - - INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk); - INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk); - INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk); - INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work); - INIT_DELAYED_WORK(&mvm->fw_dump_wk, iwl_mvm_fw_error_dump_wk); - INIT_DELAYED_WORK(&mvm->tdls_cs.dwork, iwl_mvm_tdls_ch_switch_work); - - spin_lock_init(&mvm->d0i3_tx_lock); - spin_lock_init(&mvm->refs_lock); - skb_queue_head_init(&mvm->d0i3_tx); - init_waitqueue_head(&mvm->d0i3_exit_waitq); - - SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev); - - /* - * Populate the state variables that the transport layer needs - * to know about. - */ - trans_cfg.op_mode = op_mode; - trans_cfg.no_reclaim_cmds = no_reclaim_cmds; - trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); - trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K; - trans_cfg.wide_cmd_header = fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_WIDE_CMD_HDR); - - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DW_BC_TABLE) - trans_cfg.bc_table_dword = true; - - trans_cfg.command_names = iwl_mvm_cmd_strings; - - trans_cfg.cmd_queue = IWL_MVM_CMD_QUEUE; - trans_cfg.cmd_fifo = IWL_MVM_TX_FIFO_CMD; - trans_cfg.scd_set_active = true; - - trans_cfg.sdio_adma_addr = fw->sdio_adma_addr; - - /* Set a short watchdog for the command queue */ - trans_cfg.cmd_q_wdg_timeout = - iwl_mvm_get_wd_timeout(mvm, NULL, false, true); - - snprintf(mvm->hw->wiphy->fw_version, - sizeof(mvm->hw->wiphy->fw_version), - "%s", fw->fw_version); - - /* Configure transport layer */ - iwl_trans_configure(mvm->trans, &trans_cfg); - - trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD; - trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start); - trans->dbg_dest_tlv = mvm->fw->dbg_dest_tlv; - trans->dbg_dest_reg_num = mvm->fw->dbg_dest_reg_num; - memcpy(trans->dbg_conf_tlv, mvm->fw->dbg_conf_tlv, - sizeof(trans->dbg_conf_tlv)); - trans->dbg_trigger_tlv = mvm->fw->dbg_trigger_tlv; - - /* set up notification wait support */ - iwl_notification_wait_init(&mvm->notif_wait); - - /* Init phy db */ - mvm->phy_db = iwl_phy_db_init(trans); - if (!mvm->phy_db) { - IWL_ERR(mvm, "Cannot init phy_db\n"); - goto out_free; - } - - IWL_INFO(mvm, "Detected %s, REV=0x%X\n", - mvm->cfg->name, mvm->trans->hw_rev); - - min_backoff = calc_min_backoff(trans, cfg); - iwl_mvm_tt_initialize(mvm, min_backoff); - - if (iwlwifi_mod_params.nvm_file) - mvm->nvm_file_name = iwlwifi_mod_params.nvm_file; - else - IWL_DEBUG_EEPROM(mvm->trans->dev, - "working without external nvm file\n"); - - if (WARN(cfg->no_power_up_nic_in_init && !mvm->nvm_file_name, - "not allowing power-up and not having nvm_file\n")) - goto out_free; - - /* - * Even if nvm exists in the nvm_file driver should read again the nvm - * from the nic because there might be entries that exist in the OTP - * and not in the file. - * for nics with no_power_up_nic_in_init: rely completley on nvm_file - */ - if (cfg->no_power_up_nic_in_init && mvm->nvm_file_name) { - err = iwl_nvm_init(mvm, false); - if (err) - goto out_free; - } else { - err = iwl_trans_start_hw(mvm->trans); - if (err) - goto out_free; - - mutex_lock(&mvm->mutex); - err = iwl_run_init_mvm_ucode(mvm, true); - if (!err || !iwlmvm_mod_params.init_dbg) - iwl_trans_stop_device(trans); - mutex_unlock(&mvm->mutex); - /* returns 0 if successful, 1 if success but in rfkill */ - if (err < 0 && !iwlmvm_mod_params.init_dbg) { - IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err); - goto out_free; - } - } - - scan_size = iwl_mvm_scan_size(mvm); - - mvm->scan_cmd = kmalloc(scan_size, GFP_KERNEL); - if (!mvm->scan_cmd) - goto out_free; - - /* Set EBS as successful as long as not stated otherwise by the FW. */ - mvm->last_ebs_successful = true; - - err = iwl_mvm_mac_setup_register(mvm); - if (err) - goto out_free; - - err = iwl_mvm_dbgfs_register(mvm, dbgfs_dir); - if (err) - goto out_unregister; - - memset(&mvm->rx_stats, 0, sizeof(struct mvm_statistics_rx)); - - /* rpm starts with a taken ref. only set the appropriate bit here. */ - mvm->refs[IWL_MVM_REF_UCODE_DOWN] = 1; - - iwl_mvm_tof_init(mvm); - - return op_mode; - - out_unregister: - ieee80211_unregister_hw(mvm->hw); - iwl_mvm_leds_exit(mvm); - out_free: - flush_delayed_work(&mvm->fw_dump_wk); - iwl_phy_db_free(mvm->phy_db); - kfree(mvm->scan_cmd); - if (!cfg->no_power_up_nic_in_init || !mvm->nvm_file_name) - iwl_trans_op_mode_leave(trans); - ieee80211_free_hw(mvm->hw); - return NULL; -} - -static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) -{ - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - int i; - - iwl_mvm_leds_exit(mvm); - - iwl_mvm_tt_exit(mvm); - - ieee80211_unregister_hw(mvm->hw); - - kfree(mvm->scan_cmd); - kfree(mvm->mcast_filter_cmd); - mvm->mcast_filter_cmd = NULL; - -#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_IWLWIFI_DEBUGFS) - kfree(mvm->d3_resume_sram); - if (mvm->nd_config) { - kfree(mvm->nd_config->match_sets); - kfree(mvm->nd_config->scan_plans); - kfree(mvm->nd_config); - mvm->nd_config = NULL; - } -#endif - - iwl_trans_op_mode_leave(mvm->trans); - - iwl_phy_db_free(mvm->phy_db); - mvm->phy_db = NULL; - - iwl_free_nvm_data(mvm->nvm_data); - for (i = 0; i < NVM_MAX_NUM_SECTIONS; i++) - kfree(mvm->nvm_sections[i].data); - - iwl_mvm_tof_clean(mvm); - - ieee80211_free_hw(mvm->hw); -} - -struct iwl_async_handler_entry { - struct list_head list; - struct iwl_rx_cmd_buffer rxb; - void (*fn)(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); -}; - -void iwl_mvm_async_handlers_purge(struct iwl_mvm *mvm) -{ - struct iwl_async_handler_entry *entry, *tmp; - - spin_lock_bh(&mvm->async_handlers_lock); - list_for_each_entry_safe(entry, tmp, &mvm->async_handlers_list, list) { - iwl_free_rxb(&entry->rxb); - list_del(&entry->list); - kfree(entry); - } - spin_unlock_bh(&mvm->async_handlers_lock); -} - -static void iwl_mvm_async_handlers_wk(struct work_struct *wk) -{ - struct iwl_mvm *mvm = - container_of(wk, struct iwl_mvm, async_handlers_wk); - struct iwl_async_handler_entry *entry, *tmp; - struct list_head local_list; - - INIT_LIST_HEAD(&local_list); - - /* Ensure that we are not in stop flow (check iwl_mvm_mac_stop) */ - mutex_lock(&mvm->mutex); - - /* - * Sync with Rx path with a lock. Remove all the entries from this list, - * add them to a local one (lock free), and then handle them. - */ - spin_lock_bh(&mvm->async_handlers_lock); - list_splice_init(&mvm->async_handlers_list, &local_list); - spin_unlock_bh(&mvm->async_handlers_lock); - - list_for_each_entry_safe(entry, tmp, &local_list, list) { - entry->fn(mvm, &entry->rxb); - iwl_free_rxb(&entry->rxb); - list_del(&entry->list); - kfree(entry); - } - mutex_unlock(&mvm->mutex); -} - -static inline void iwl_mvm_rx_check_trigger(struct iwl_mvm *mvm, - struct iwl_rx_packet *pkt) -{ - struct iwl_fw_dbg_trigger_tlv *trig; - struct iwl_fw_dbg_trigger_cmd *cmds_trig; - int i; - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_FW_NOTIF)) - return; - - trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_FW_NOTIF); - cmds_trig = (void *)trig->data; - - if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig)) - return; - - for (i = 0; i < ARRAY_SIZE(cmds_trig->cmds); i++) { - /* don't collect on CMD 0 */ - if (!cmds_trig->cmds[i].cmd_id) - break; - - if (cmds_trig->cmds[i].cmd_id != pkt->hdr.cmd || - cmds_trig->cmds[i].group_id != pkt->hdr.group_id) - continue; - - iwl_mvm_fw_dbg_collect_trig(mvm, trig, - "CMD 0x%02x.%02x received", - pkt->hdr.group_id, pkt->hdr.cmd); - break; - } -} - -static void iwl_mvm_rx_common(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_rx_packet *pkt) -{ - int i; - - iwl_mvm_rx_check_trigger(mvm, pkt); - - /* - * Do the notification wait before RX handlers so - * even if the RX handler consumes the RXB we have - * access to it in the notification wait entry. - */ - iwl_notification_wait_notify(&mvm->notif_wait, pkt); - - for (i = 0; i < ARRAY_SIZE(iwl_mvm_rx_handlers); i++) { - const struct iwl_rx_handlers *rx_h = &iwl_mvm_rx_handlers[i]; - struct iwl_async_handler_entry *entry; - - if (rx_h->cmd_id != WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd)) - continue; - - if (!rx_h->async) { - rx_h->fn(mvm, rxb); - return; - } - - entry = kzalloc(sizeof(*entry), GFP_ATOMIC); - /* we can't do much... */ - if (!entry) - return; - - entry->rxb._page = rxb_steal_page(rxb); - entry->rxb._offset = rxb->_offset; - entry->rxb._rx_page_order = rxb->_rx_page_order; - entry->fn = rx_h->fn; - spin_lock(&mvm->async_handlers_lock); - list_add_tail(&entry->list, &mvm->async_handlers_list); - spin_unlock(&mvm->async_handlers_lock); - schedule_work(&mvm->async_handlers_wk); - break; - } -} - -static void iwl_mvm_rx(struct iwl_op_mode *op_mode, - struct napi_struct *napi, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - - if (likely(pkt->hdr.cmd == REPLY_RX_MPDU_CMD)) - iwl_mvm_rx_rx_mpdu(mvm, napi, rxb); - else if (pkt->hdr.cmd == REPLY_RX_PHY_CMD) - iwl_mvm_rx_rx_phy_cmd(mvm, rxb); - else - iwl_mvm_rx_common(mvm, rxb, pkt); -} - -static void iwl_mvm_rx_mq(struct iwl_op_mode *op_mode, - struct napi_struct *napi, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - - if (likely(pkt->hdr.cmd == REPLY_RX_MPDU_CMD)) - iwl_mvm_rx_rx_mpdu(mvm, napi, rxb); - else if (pkt->hdr.cmd == REPLY_RX_PHY_CMD) - iwl_mvm_rx_rx_phy_cmd(mvm, rxb); - else - iwl_mvm_rx_common(mvm, rxb, pkt); -} - -static void iwl_mvm_stop_sw_queue(struct iwl_op_mode *op_mode, int queue) -{ - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - unsigned long mq; - int q; - - spin_lock_bh(&mvm->queue_info_lock); - mq = mvm->queue_info[queue].hw_queue_to_mac80211; - spin_unlock_bh(&mvm->queue_info_lock); - - if (WARN_ON_ONCE(!mq)) - return; - - for_each_set_bit(q, &mq, IEEE80211_MAX_QUEUES) { - if (atomic_inc_return(&mvm->mac80211_queue_stop_count[q]) > 1) { - IWL_DEBUG_TX_QUEUES(mvm, - "queue %d (mac80211 %d) already stopped\n", - queue, q); - continue; - } - - ieee80211_stop_queue(mvm->hw, q); - } -} - -static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int queue) -{ - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - unsigned long mq; - int q; - - spin_lock_bh(&mvm->queue_info_lock); - mq = mvm->queue_info[queue].hw_queue_to_mac80211; - spin_unlock_bh(&mvm->queue_info_lock); - - if (WARN_ON_ONCE(!mq)) - return; - - for_each_set_bit(q, &mq, IEEE80211_MAX_QUEUES) { - if (atomic_dec_return(&mvm->mac80211_queue_stop_count[q]) > 0) { - IWL_DEBUG_TX_QUEUES(mvm, - "queue %d (mac80211 %d) still stopped\n", - queue, q); - continue; - } - - ieee80211_wake_queue(mvm->hw, q); - } -} - -void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state) -{ - if (state) - set_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status); - else - clear_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status); - - wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm)); -} - -static bool iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) -{ - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - bool calibrating = ACCESS_ONCE(mvm->calibrating); - - if (state) - set_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status); - else - clear_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status); - - wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm)); - - /* iwl_run_init_mvm_ucode is waiting for results, abort it */ - if (calibrating) - iwl_abort_notification_waits(&mvm->notif_wait); - - /* - * Stop the device if we run OPERATIONAL firmware or if we are in the - * middle of the calibrations. - */ - return state && (mvm->cur_ucode != IWL_UCODE_INIT || calibrating); -} - -static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) -{ - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - struct ieee80211_tx_info *info; - - info = IEEE80211_SKB_CB(skb); - iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]); - ieee80211_free_txskb(mvm->hw, skb); -} - -struct iwl_mvm_reprobe { - struct device *dev; - struct work_struct work; -}; - -static void iwl_mvm_reprobe_wk(struct work_struct *wk) -{ - struct iwl_mvm_reprobe *reprobe; - - reprobe = container_of(wk, struct iwl_mvm_reprobe, work); - if (device_reprobe(reprobe->dev)) - dev_err(reprobe->dev, "reprobe failed!\n"); - kfree(reprobe); - module_put(THIS_MODULE); -} - -static void iwl_mvm_fw_error_dump_wk(struct work_struct *work) -{ - struct iwl_mvm *mvm = - container_of(work, struct iwl_mvm, fw_dump_wk.work); - - if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_FW_DBG_COLLECT)) - return; - - mutex_lock(&mvm->mutex); - - /* stop recording */ - if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) { - iwl_set_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100); - } else { - iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 0); - /* wait before we collect the data till the DBGC stop */ - udelay(100); - } - - iwl_mvm_fw_error_dump(mvm); - - /* start recording again if the firmware is not crashed */ - WARN_ON_ONCE((!test_bit(STATUS_FW_ERROR, &mvm->trans->status)) && - mvm->fw->dbg_dest_tlv && - iwl_mvm_start_fw_dbg_conf(mvm, mvm->fw_dbg_conf)); - - mutex_unlock(&mvm->mutex); - - iwl_mvm_unref(mvm, IWL_MVM_REF_FW_DBG_COLLECT); -} - -void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error) -{ - iwl_abort_notification_waits(&mvm->notif_wait); - - /* - * This is a bit racy, but worst case we tell mac80211 about - * a stopped/aborted scan when that was already done which - * is not a problem. It is necessary to abort any os scan - * here because mac80211 requires having the scan cleared - * before restarting. - * We'll reset the scan_status to NONE in restart cleanup in - * the next start() call from mac80211. If restart isn't called - * (no fw restart) scan status will stay busy. - */ - iwl_mvm_report_scan_aborted(mvm); - - /* - * If we're restarting already, don't cycle restarts. - * If INIT fw asserted, it will likely fail again. - * If WoWLAN fw asserted, don't restart either, mac80211 - * can't recover this since we're already half suspended. - */ - if (!mvm->restart_fw && fw_error) { - iwl_mvm_fw_dbg_collect_desc(mvm, &iwl_mvm_dump_desc_assert, - NULL); - } else if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART, - &mvm->status)) { - struct iwl_mvm_reprobe *reprobe; - - IWL_ERR(mvm, - "Firmware error during reconfiguration - reprobe!\n"); - - /* - * get a module reference to avoid doing this while unloading - * anyway and to avoid scheduling a work with code that's - * being removed. - */ - if (!try_module_get(THIS_MODULE)) { - IWL_ERR(mvm, "Module is being unloaded - abort\n"); - return; - } - - reprobe = kzalloc(sizeof(*reprobe), GFP_ATOMIC); - if (!reprobe) { - module_put(THIS_MODULE); - return; - } - reprobe->dev = mvm->trans->dev; - INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk); - schedule_work(&reprobe->work); - } else if (mvm->cur_ucode == IWL_UCODE_REGULAR) { - /* don't let the transport/FW power down */ - iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); - - if (fw_error && mvm->restart_fw > 0) - mvm->restart_fw--; - ieee80211_restart_hw(mvm->hw); - } -} - -static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode) -{ - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - - iwl_mvm_dump_nic_error_log(mvm); - - iwl_mvm_nic_restart(mvm, true); -} - -static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode) -{ - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - - WARN_ON(1); - iwl_mvm_nic_restart(mvm, true); -} - -struct iwl_d0i3_iter_data { - struct iwl_mvm *mvm; - u8 ap_sta_id; - u8 vif_count; - u8 offloading_tid; - bool disable_offloading; -}; - -static bool iwl_mvm_disallow_offloading(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_d0i3_iter_data *iter_data) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct ieee80211_sta *ap_sta; - struct iwl_mvm_sta *mvmsta; - u32 available_tids = 0; - u8 tid; - - if (WARN_ON(vif->type != NL80211_IFTYPE_STATION || - mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)) - return false; - - ap_sta = rcu_dereference(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id]); - if (IS_ERR_OR_NULL(ap_sta)) - return false; - - mvmsta = iwl_mvm_sta_from_mac80211(ap_sta); - spin_lock_bh(&mvmsta->lock); - for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { - struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; - - /* - * in case of pending tx packets, don't use this tid - * for offloading in order to prevent reuse of the same - * qos seq counters. - */ - if (iwl_mvm_tid_queued(tid_data)) - continue; - - if (tid_data->state != IWL_AGG_OFF) - continue; - - available_tids |= BIT(tid); - } - spin_unlock_bh(&mvmsta->lock); - - /* - * disallow protocol offloading if we have no available tid - * (with no pending frames and no active aggregation, - * as we don't handle "holes" properly - the scheduler needs the - * frame's seq number and TFD index to match) - */ - if (!available_tids) - return true; - - /* for simplicity, just use the first available tid */ - iter_data->offloading_tid = ffs(available_tids) - 1; - return false; -} - -static void iwl_mvm_enter_d0i3_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_d0i3_iter_data *data = _data; - struct iwl_mvm *mvm = data->mvm; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE; - - IWL_DEBUG_RPM(mvm, "entering D0i3 - vif %pM\n", vif->addr); - if (vif->type != NL80211_IFTYPE_STATION || - !vif->bss_conf.assoc) - return; - - /* - * in case of pending tx packets or active aggregations, - * avoid offloading features in order to prevent reuse of - * the same qos seq counters. - */ - if (iwl_mvm_disallow_offloading(mvm, vif, data)) - data->disable_offloading = true; - - iwl_mvm_update_d0i3_power_mode(mvm, vif, true, flags); - iwl_mvm_send_proto_offload(mvm, vif, data->disable_offloading, flags); - - /* - * on init/association, mvm already configures POWER_TABLE_CMD - * and REPLY_MCAST_FILTER_CMD, so currently don't - * reconfigure them (we might want to use different - * params later on, though). - */ - data->ap_sta_id = mvmvif->ap_sta_id; - data->vif_count++; -} - -static void iwl_mvm_set_wowlan_data(struct iwl_mvm *mvm, - struct iwl_wowlan_config_cmd *cmd, - struct iwl_d0i3_iter_data *iter_data) -{ - struct ieee80211_sta *ap_sta; - struct iwl_mvm_sta *mvm_ap_sta; - - if (iter_data->ap_sta_id == IWL_MVM_STATION_COUNT) - return; - - rcu_read_lock(); - - ap_sta = rcu_dereference(mvm->fw_id_to_mac_id[iter_data->ap_sta_id]); - if (IS_ERR_OR_NULL(ap_sta)) - goto out; - - mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta); - cmd->is_11n_connection = ap_sta->ht_cap.ht_supported; - cmd->offloading_tid = iter_data->offloading_tid; - - /* - * The d0i3 uCode takes care of the nonqos counters, - * so configure only the qos seq ones. - */ - iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, cmd); -out: - rcu_read_unlock(); -} - -int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) -{ - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE; - int ret; - struct iwl_d0i3_iter_data d0i3_iter_data = { - .mvm = mvm, - }; - struct iwl_wowlan_config_cmd wowlan_config_cmd = { - .wakeup_filter = cpu_to_le32(IWL_WOWLAN_WAKEUP_RX_FRAME | - IWL_WOWLAN_WAKEUP_BEACON_MISS | - IWL_WOWLAN_WAKEUP_LINK_CHANGE | - IWL_WOWLAN_WAKEUP_BCN_FILTERING), - }; - struct iwl_d3_manager_config d3_cfg_cmd = { - .min_sleep_time = cpu_to_le32(1000), - .wakeup_flags = cpu_to_le32(IWL_WAKEUP_D3_CONFIG_FW_ERROR), - }; - - IWL_DEBUG_RPM(mvm, "MVM entering D0i3\n"); - - set_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status); - - /* - * iwl_mvm_ref_sync takes a reference before checking the flag. - * so by checking there is no held reference we prevent a state - * in which iwl_mvm_ref_sync continues successfully while we - * configure the firmware to enter d0i3 - */ - if (iwl_mvm_ref_taken(mvm)) { - IWL_DEBUG_RPM(mvm->trans, "abort d0i3 due to taken ref\n"); - clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status); - wake_up(&mvm->d0i3_exit_waitq); - return 1; - } - - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_enter_d0i3_iterator, - &d0i3_iter_data); - if (d0i3_iter_data.vif_count == 1) { - mvm->d0i3_ap_sta_id = d0i3_iter_data.ap_sta_id; - mvm->d0i3_offloading = !d0i3_iter_data.disable_offloading; - } else { - WARN_ON_ONCE(d0i3_iter_data.vif_count > 1); - mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; - mvm->d0i3_offloading = false; - } - - /* make sure we have no running tx while configuring the seqno */ - synchronize_net(); - - /* configure wowlan configuration only if needed */ - if (mvm->d0i3_ap_sta_id != IWL_MVM_STATION_COUNT) { - iwl_mvm_set_wowlan_data(mvm, &wowlan_config_cmd, - &d0i3_iter_data); - - ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, flags, - sizeof(wowlan_config_cmd), - &wowlan_config_cmd); - if (ret) - return ret; - } - - return iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, - flags | CMD_MAKE_TRANS_IDLE, - sizeof(d3_cfg_cmd), &d3_cfg_cmd); -} - -static void iwl_mvm_exit_d0i3_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = _data; - u32 flags = CMD_ASYNC | CMD_HIGH_PRIO; - - IWL_DEBUG_RPM(mvm, "exiting D0i3 - vif %pM\n", vif->addr); - if (vif->type != NL80211_IFTYPE_STATION || - !vif->bss_conf.assoc) - return; - - iwl_mvm_update_d0i3_power_mode(mvm, vif, false, flags); -} - -struct iwl_mvm_wakeup_reason_iter_data { - struct iwl_mvm *mvm; - u32 wakeup_reasons; -}; - -static void iwl_mvm_d0i3_wakeup_reason_iter(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_wakeup_reason_iter_data *data = _data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (vif->type == NL80211_IFTYPE_STATION && vif->bss_conf.assoc && - data->mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id) { - if (data->wakeup_reasons & - IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH) - iwl_mvm_connection_loss(data->mvm, vif, "D0i3"); - else - ieee80211_beacon_loss(vif); - } -} - -void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq) -{ - struct ieee80211_sta *sta = NULL; - struct iwl_mvm_sta *mvm_ap_sta; - int i; - bool wake_queues = false; - - lockdep_assert_held(&mvm->mutex); - - spin_lock_bh(&mvm->d0i3_tx_lock); - - if (mvm->d0i3_ap_sta_id == IWL_MVM_STATION_COUNT) - goto out; - - IWL_DEBUG_RPM(mvm, "re-enqueue packets\n"); - - /* get the sta in order to update seq numbers and re-enqueue skbs */ - sta = rcu_dereference_protected( - mvm->fw_id_to_mac_id[mvm->d0i3_ap_sta_id], - lockdep_is_held(&mvm->mutex)); - - if (IS_ERR_OR_NULL(sta)) { - sta = NULL; - goto out; - } - - if (mvm->d0i3_offloading && qos_seq) { - /* update qos seq numbers if offloading was enabled */ - mvm_ap_sta = iwl_mvm_sta_from_mac80211(sta); - for (i = 0; i < IWL_MAX_TID_COUNT; i++) { - u16 seq = le16_to_cpu(qos_seq[i]); - /* firmware stores last-used one, we store next one */ - seq += 0x10; - mvm_ap_sta->tid_data[i].seq_number = seq; - } - } -out: - /* re-enqueue (or drop) all packets */ - while (!skb_queue_empty(&mvm->d0i3_tx)) { - struct sk_buff *skb = __skb_dequeue(&mvm->d0i3_tx); - - if (!sta || iwl_mvm_tx_skb(mvm, skb, sta)) - ieee80211_free_txskb(mvm->hw, skb); - - /* if the skb_queue is not empty, we need to wake queues */ - wake_queues = true; - } - clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status); - wake_up(&mvm->d0i3_exit_waitq); - mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; - if (wake_queues) - ieee80211_wake_queues(mvm->hw); - - spin_unlock_bh(&mvm->d0i3_tx_lock); -} - -static void iwl_mvm_d0i3_exit_work(struct work_struct *wk) -{ - struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, d0i3_exit_work); - struct iwl_host_cmd get_status_cmd = { - .id = WOWLAN_GET_STATUSES, - .flags = CMD_HIGH_PRIO | CMD_WANT_SKB, - }; - struct iwl_wowlan_status *status; - int ret; - u32 handled_reasons, wakeup_reasons = 0; - __le16 *qos_seq = NULL; - - mutex_lock(&mvm->mutex); - ret = iwl_mvm_send_cmd(mvm, &get_status_cmd); - if (ret) - goto out; - - if (!get_status_cmd.resp_pkt) - goto out; - - status = (void *)get_status_cmd.resp_pkt->data; - wakeup_reasons = le32_to_cpu(status->wakeup_reasons); - qos_seq = status->qos_seq_ctr; - - IWL_DEBUG_RPM(mvm, "wakeup reasons: 0x%x\n", wakeup_reasons); - - handled_reasons = IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | - IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH; - if (wakeup_reasons & handled_reasons) { - struct iwl_mvm_wakeup_reason_iter_data data = { - .mvm = mvm, - .wakeup_reasons = wakeup_reasons, - }; - - ieee80211_iterate_active_interfaces( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_d0i3_wakeup_reason_iter, &data); - } -out: - iwl_mvm_d0i3_enable_tx(mvm, qos_seq); - - IWL_DEBUG_INFO(mvm, "d0i3 exit completed (wakeup reasons: 0x%x)\n", - wakeup_reasons); - - /* qos_seq might point inside resp_pkt, so free it only now */ - if (get_status_cmd.resp_pkt) - iwl_free_resp(&get_status_cmd); - - /* the FW might have updated the regdomain */ - iwl_mvm_update_changed_regdom(mvm); - - iwl_mvm_unref(mvm, IWL_MVM_REF_EXIT_WORK); - mutex_unlock(&mvm->mutex); -} - -int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm) -{ - u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE | - CMD_WAKE_UP_TRANS; - int ret; - - IWL_DEBUG_RPM(mvm, "MVM exiting D0i3\n"); - - mutex_lock(&mvm->d0i3_suspend_mutex); - if (test_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags)) { - IWL_DEBUG_RPM(mvm, "Deferring d0i3 exit until resume\n"); - __set_bit(D0I3_PENDING_WAKEUP, &mvm->d0i3_suspend_flags); - mutex_unlock(&mvm->d0i3_suspend_mutex); - return 0; - } - mutex_unlock(&mvm->d0i3_suspend_mutex); - - ret = iwl_mvm_send_cmd_pdu(mvm, D0I3_END_CMD, flags, 0, NULL); - if (ret) - goto out; - - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_exit_d0i3_iterator, - mvm); -out: - schedule_work(&mvm->d0i3_exit_work); - return ret; -} - -int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode) -{ - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - - iwl_mvm_ref(mvm, IWL_MVM_REF_EXIT_WORK); - return _iwl_mvm_exit_d0i3(mvm); -} - -#define IWL_MVM_COMMON_OPS \ - /* these could be differentiated */ \ - .queue_full = iwl_mvm_stop_sw_queue, \ - .queue_not_full = iwl_mvm_wake_sw_queue, \ - .hw_rf_kill = iwl_mvm_set_hw_rfkill_state, \ - .free_skb = iwl_mvm_free_skb, \ - .nic_error = iwl_mvm_nic_error, \ - .cmd_queue_full = iwl_mvm_cmd_queue_full, \ - .nic_config = iwl_mvm_nic_config, \ - .enter_d0i3 = iwl_mvm_enter_d0i3, \ - .exit_d0i3 = iwl_mvm_exit_d0i3, \ - /* as we only register one, these MUST be common! */ \ - .start = iwl_op_mode_mvm_start, \ - .stop = iwl_op_mode_mvm_stop - -static const struct iwl_op_mode_ops iwl_mvm_ops = { - IWL_MVM_COMMON_OPS, - .rx = iwl_mvm_rx, -}; - -static void iwl_mvm_rx_mq_rss(struct iwl_op_mode *op_mode, - struct napi_struct *napi, - struct iwl_rx_cmd_buffer *rxb, - unsigned int queue) -{ - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - - iwl_mvm_rx_rx_mpdu(mvm, napi, rxb); -} - -static const struct iwl_op_mode_ops iwl_mvm_ops_mq = { - IWL_MVM_COMMON_OPS, - .rx = iwl_mvm_rx_mq, - .rx_rss = iwl_mvm_rx_mq_rss, -}; diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c deleted file mode 100644 index e68a475e3..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c +++ /dev/null @@ -1,295 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * 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.h" -#include "mvm.h" - -/* Maps the driver specific channel width definition to the fw values */ -u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef) -{ - switch (chandef->width) { - case NL80211_CHAN_WIDTH_20_NOHT: - case NL80211_CHAN_WIDTH_20: - return PHY_VHT_CHANNEL_MODE20; - case NL80211_CHAN_WIDTH_40: - return PHY_VHT_CHANNEL_MODE40; - case NL80211_CHAN_WIDTH_80: - return PHY_VHT_CHANNEL_MODE80; - case NL80211_CHAN_WIDTH_160: - return PHY_VHT_CHANNEL_MODE160; - default: - WARN(1, "Invalid channel width=%u", chandef->width); - return PHY_VHT_CHANNEL_MODE20; - } -} - -/* - * Maps the driver specific control channel position (relative to the center - * freq) definitions to the the fw values - */ -u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef) -{ - switch (chandef->chan->center_freq - chandef->center_freq1) { - case -70: - return PHY_VHT_CTRL_POS_4_BELOW; - case -50: - return PHY_VHT_CTRL_POS_3_BELOW; - case -30: - return PHY_VHT_CTRL_POS_2_BELOW; - case -10: - return PHY_VHT_CTRL_POS_1_BELOW; - case 10: - return PHY_VHT_CTRL_POS_1_ABOVE; - case 30: - return PHY_VHT_CTRL_POS_2_ABOVE; - case 50: - return PHY_VHT_CTRL_POS_3_ABOVE; - case 70: - return PHY_VHT_CTRL_POS_4_ABOVE; - default: - WARN(1, "Invalid channel definition"); - case 0: - /* - * The FW is expected to check the control channel position only - * when in HT/VHT and the channel width is not 20MHz. Return - * this value as the default one. - */ - return PHY_VHT_CTRL_POS_1_BELOW; - } -} - -/* - * Construct the generic fields of the PHY context command - */ -static void iwl_mvm_phy_ctxt_cmd_hdr(struct iwl_mvm_phy_ctxt *ctxt, - struct iwl_phy_context_cmd *cmd, - u32 action, u32 apply_time) -{ - memset(cmd, 0, sizeof(struct iwl_phy_context_cmd)); - - cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(ctxt->id, - ctxt->color)); - cmd->action = cpu_to_le32(action); - cmd->apply_time = cpu_to_le32(apply_time); -} - -/* - * Add the phy configuration to the PHY context command - */ -static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm, - struct iwl_phy_context_cmd *cmd, - struct cfg80211_chan_def *chandef, - u8 chains_static, u8 chains_dynamic) -{ - u8 active_cnt, idle_cnt; - - /* Set the channel info data */ - cmd->ci.band = (chandef->chan->band == IEEE80211_BAND_2GHZ ? - PHY_BAND_24 : PHY_BAND_5); - - cmd->ci.channel = chandef->chan->hw_value; - cmd->ci.width = iwl_mvm_get_channel_width(chandef); - cmd->ci.ctrl_pos = iwl_mvm_get_ctrl_pos(chandef); - - /* Set rx the chains */ - idle_cnt = chains_static; - active_cnt = chains_dynamic; - - /* In scenarios where we only ever use a single-stream rates, - * i.e. legacy 11b/g/a associations, single-stream APs or even - * static SMPS, enable both chains to get diversity, improving - * the case where we're far enough from the AP that attenuation - * between the two antennas is sufficiently different to impact - * performance. - */ - if (active_cnt == 1 && iwl_mvm_rx_diversity_allowed(mvm)) { - idle_cnt = 2; - active_cnt = 2; - } - - cmd->rxchain_info = cpu_to_le32(iwl_mvm_get_valid_rx_ant(mvm) << - 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); -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (unlikely(mvm->dbgfs_rx_phyinfo)) - cmd->rxchain_info = cpu_to_le32(mvm->dbgfs_rx_phyinfo); -#endif - - cmd->txchain_info = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm)); -} - -/* - * Send a command to apply the current phy configuration. The command is send - * only if something in the configuration changed: in case that this is the - * first time that the phy configuration is applied or in case that the phy - * configuration changed from the previous apply. - */ -static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm, - struct iwl_mvm_phy_ctxt *ctxt, - struct cfg80211_chan_def *chandef, - u8 chains_static, u8 chains_dynamic, - u32 action, u32 apply_time) -{ - struct iwl_phy_context_cmd cmd; - int ret; - - /* Set the command header fields */ - iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, action, apply_time); - - /* Set the command data */ - iwl_mvm_phy_ctxt_cmd_data(mvm, &cmd, chandef, - chains_static, chains_dynamic); - - ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, 0, - sizeof(struct iwl_phy_context_cmd), - &cmd); - if (ret) - IWL_ERR(mvm, "PHY ctxt cmd error. ret=%d\n", ret); - return ret; -} - -/* - * Send a command to add a PHY context based on the current HW configuration. - */ -int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, - struct cfg80211_chan_def *chandef, - u8 chains_static, u8 chains_dynamic) -{ - WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && - ctxt->ref); - lockdep_assert_held(&mvm->mutex); - - ctxt->channel = chandef->chan; - - return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, - chains_static, chains_dynamic, - FW_CTXT_ACTION_ADD, 0); -} - -/* - * Update the number of references to the given PHY context. This is valid only - * in case the PHY context was already created, i.e., its reference count > 0. - */ -void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) -{ - lockdep_assert_held(&mvm->mutex); - ctxt->ref++; -} - -/* - * Send a command to modify the PHY context based on the current HW - * configuration. Note that the function does not check that the configuration - * changed. - */ -int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, - struct cfg80211_chan_def *chandef, - u8 chains_static, u8 chains_dynamic) -{ - lockdep_assert_held(&mvm->mutex); - - ctxt->channel = chandef->chan; - return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, - chains_static, chains_dynamic, - FW_CTXT_ACTION_MODIFY, 0); -} - -void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) -{ - lockdep_assert_held(&mvm->mutex); - - if (WARN_ON_ONCE(!ctxt)) - return; - - ctxt->ref--; -} - -static void iwl_mvm_binding_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - unsigned long *data = _data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (!mvmvif->phy_ctxt) - return; - - if (vif->type == NL80211_IFTYPE_STATION || - vif->type == NL80211_IFTYPE_AP) - __set_bit(mvmvif->phy_ctxt->id, data); -} - -int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm) -{ - unsigned long phy_ctxt_counter = 0; - - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_binding_iterator, - &phy_ctxt_counter); - - return hweight8(phy_ctxt_counter); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c deleted file mode 100644 index bed9696ee..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/power.c +++ /dev/null @@ -1,1040 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH - * 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 <linux/kernel.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/etherdevice.h> - -#include <net/mac80211.h> - -#include "iwl-debug.h" -#include "mvm.h" -#include "iwl-modparams.h" -#include "fw-api-power.h" - -#define POWER_KEEP_ALIVE_PERIOD_SEC 25 - -static -int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm, - struct iwl_beacon_filter_cmd *cmd, - u32 flags) -{ - IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n", - le32_to_cpu(cmd->ba_enable_beacon_abort)); - IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n", - le32_to_cpu(cmd->ba_escape_timer)); - IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n", - le32_to_cpu(cmd->bf_debug_flag)); - IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n", - le32_to_cpu(cmd->bf_enable_beacon_filter)); - IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n", - le32_to_cpu(cmd->bf_energy_delta)); - IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n", - le32_to_cpu(cmd->bf_escape_timer)); - IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n", - le32_to_cpu(cmd->bf_roaming_energy_delta)); - IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n", - le32_to_cpu(cmd->bf_roaming_state)); - IWL_DEBUG_POWER(mvm, "bf_temp_threshold is: %d\n", - le32_to_cpu(cmd->bf_temp_threshold)); - IWL_DEBUG_POWER(mvm, "bf_temp_fast_filter is: %d\n", - le32_to_cpu(cmd->bf_temp_fast_filter)); - IWL_DEBUG_POWER(mvm, "bf_temp_slow_filter is: %d\n", - le32_to_cpu(cmd->bf_temp_slow_filter)); - - return iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, flags, - sizeof(struct iwl_beacon_filter_cmd), cmd); -} - -static -void iwl_mvm_beacon_filter_set_cqm_params(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_beacon_filter_cmd *cmd, - bool d0i3) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (vif->bss_conf.cqm_rssi_thold && !d0i3) { - cmd->bf_energy_delta = - cpu_to_le32(vif->bss_conf.cqm_rssi_hyst); - /* fw uses an absolute value for this */ - cmd->bf_roaming_state = - cpu_to_le32(-vif->bss_conf.cqm_rssi_thold); - } - cmd->ba_enable_beacon_abort = cpu_to_le32(mvmvif->bf_data.ba_enabled); -} - -static void iwl_mvm_power_log(struct iwl_mvm *mvm, - struct iwl_mac_power_cmd *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)); - IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n", - le16_to_cpu(cmd->keep_alive_seconds)); - - if (!(cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK))) { - IWL_DEBUG_POWER(mvm, "Disable power management\n"); - return; - } - - 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)); - if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) - IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n", - cmd->skip_dtim_periods); - if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK)) - IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n", - cmd->lprx_rssi_threshold); - if (cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) { - IWL_DEBUG_POWER(mvm, "uAPSD enabled\n"); - IWL_DEBUG_POWER(mvm, "Rx timeout (uAPSD) = %u usec\n", - le32_to_cpu(cmd->rx_data_timeout_uapsd)); - IWL_DEBUG_POWER(mvm, "Tx timeout (uAPSD) = %u usec\n", - le32_to_cpu(cmd->tx_data_timeout_uapsd)); - IWL_DEBUG_POWER(mvm, "QNDP TID = %d\n", cmd->qndp_tid); - IWL_DEBUG_POWER(mvm, "ACs flags = 0x%x\n", cmd->uapsd_ac_flags); - IWL_DEBUG_POWER(mvm, "Max SP = %d\n", cmd->uapsd_max_sp); - } -} - -static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_mac_power_cmd *cmd) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - enum ieee80211_ac_numbers ac; - bool tid_found = false; - - for (ac = IEEE80211_AC_VO; ac <= IEEE80211_AC_BK; ac++) { - if (!mvmvif->queue_params[ac].uapsd) - continue; - - if (mvm->cur_ucode != IWL_UCODE_WOWLAN) - cmd->flags |= - cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK); - - cmd->uapsd_ac_flags |= BIT(ac); - - /* QNDP TID - the highest TID with no admission control */ - if (!tid_found && !mvmvif->queue_params[ac].acm) { - tid_found = true; - switch (ac) { - case IEEE80211_AC_VO: - cmd->qndp_tid = 6; - break; - case IEEE80211_AC_VI: - cmd->qndp_tid = 5; - break; - case IEEE80211_AC_BE: - cmd->qndp_tid = 0; - break; - case IEEE80211_AC_BK: - cmd->qndp_tid = 1; - break; - } - } - } - - if (!(cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) { -#ifdef CONFIG_IWLWIFI_DEBUGFS - /* set advanced pm flag with no uapsd ACs to enable ps-poll */ - if (mvmvif->dbgfs_pm.use_ps_poll) - cmd->flags |= - cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK); -#endif - return; - } - - cmd->flags |= cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK); - - if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) | - BIT(IEEE80211_AC_VI) | - BIT(IEEE80211_AC_BE) | - BIT(IEEE80211_AC_BK))) { - cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK); - cmd->snooze_interval = cpu_to_le16(IWL_MVM_PS_SNOOZE_INTERVAL); - cmd->snooze_window = (mvm->cur_ucode == IWL_UCODE_WOWLAN) ? - cpu_to_le16(IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW) : - cpu_to_le16(IWL_MVM_PS_SNOOZE_WINDOW); - } - - cmd->uapsd_max_sp = IWL_UAPSD_MAX_SP; - - if (mvm->cur_ucode == IWL_UCODE_WOWLAN || cmd->flags & - cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) { - cmd->rx_data_timeout_uapsd = - cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT); - cmd->tx_data_timeout_uapsd = - cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT); - } else { - cmd->rx_data_timeout_uapsd = - cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT); - cmd->tx_data_timeout_uapsd = - cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT); - } - - if (cmd->flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) { - cmd->heavy_tx_thld_packets = - IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS; - cmd->heavy_rx_thld_packets = - IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS; - } else { - cmd->heavy_tx_thld_packets = - IWL_MVM_PS_HEAVY_TX_THLD_PACKETS; - cmd->heavy_rx_thld_packets = - IWL_MVM_PS_HEAVY_RX_THLD_PACKETS; - } - cmd->heavy_tx_thld_percentage = - IWL_MVM_PS_HEAVY_TX_THLD_PERCENT; - cmd->heavy_rx_thld_percentage = - IWL_MVM_PS_HEAVY_RX_THLD_PERCENT; -} - -static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (!memcmp(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid, - ETH_ALEN)) - return false; - - if (vif->p2p && - !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD)) - return false; - /* - * Avoid using uAPSD if P2P client is associated to GO that uses - * opportunistic power save. This is due to current FW limitation. - */ - if (vif->p2p && - (vif->bss_conf.p2p_noa_attr.oppps_ctwindow & - IEEE80211_P2P_OPPPS_ENABLE_BIT)) - return false; - - /* - * Avoid using uAPSD if client is in DCM - - * low latency issue in Miracast - */ - if (iwl_mvm_phy_ctx_count(mvm) >= 2) - return false; - - return true; -} - -static bool iwl_mvm_power_is_radar(struct ieee80211_vif *vif) -{ - struct ieee80211_chanctx_conf *chanctx_conf; - struct ieee80211_channel *chan; - bool radar_detect = false; - - rcu_read_lock(); - chanctx_conf = rcu_dereference(vif->chanctx_conf); - WARN_ON(!chanctx_conf); - if (chanctx_conf) { - chan = chanctx_conf->def.chan; - radar_detect = chan->flags & IEEE80211_CHAN_RADAR; - } - rcu_read_unlock(); - - return radar_detect; -} - -static void iwl_mvm_power_config_skip_dtim(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_mac_power_cmd *cmd, - bool host_awake) -{ - int dtimper = vif->bss_conf.dtim_period ?: 1; - int skip; - - /* disable, in case we're supposed to override */ - cmd->skip_dtim_periods = 0; - cmd->flags &= ~cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); - - if (iwl_mvm_power_is_radar(vif)) - return; - - if (dtimper >= 10) - return; - - /* TODO: check that multicast wake lock is off */ - - if (host_awake) { - if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_LP) - return; - skip = 2; - } else { - int dtimper_tu = dtimper * vif->bss_conf.beacon_int; - - if (WARN_ON(!dtimper_tu)) - return; - /* configure skip over dtim up to 306TU - 314 msec */ - skip = max_t(u8, 1, 306 / dtimper_tu); - } - - /* the firmware really expects "look at every X DTIMs", so add 1 */ - cmd->skip_dtim_periods = 1 + skip; - cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); -} - -static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_mac_power_cmd *cmd, - bool host_awake) -{ - int dtimper, bi; - int keep_alive; - struct iwl_mvm_vif *mvmvif __maybe_unused = - iwl_mvm_vif_from_mac80211(vif); - - cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, - mvmvif->color)); - dtimper = vif->bss_conf.dtim_period; - bi = vif->bss_conf.beacon_int; - - /* - * Regardless of power management state the driver must set - * keep alive period. FW will use it for sending keep alive NDPs - * immediately after association. Check that keep alive period - * is at least 3 * DTIM - */ - keep_alive = DIV_ROUND_UP(ieee80211_tu_to_usec(3 * dtimper * bi), - USEC_PER_SEC); - keep_alive = max(keep_alive, POWER_KEEP_ALIVE_PERIOD_SEC); - cmd->keep_alive_seconds = cpu_to_le16(keep_alive); - - if (mvm->ps_disabled) - return; - - cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); - - if (!vif->bss_conf.ps || !mvmvif->pm_enabled) - return; - - if (iwl_mvm_vif_low_latency(mvmvif) && vif->p2p && - (!fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS) || - !IWL_MVM_P2P_LOWLATENCY_PS_ENABLE)) - return; - - cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK); - - if (vif->bss_conf.beacon_rate && - (vif->bss_conf.beacon_rate->bitrate == 10 || - vif->bss_conf.beacon_rate->bitrate == 60)) { - cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK); - cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD; - } - - iwl_mvm_power_config_skip_dtim(mvm, vif, cmd, host_awake); - - if (!host_awake) { - cmd->rx_data_timeout = - cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT); - cmd->tx_data_timeout = - cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT); - } else if (iwl_mvm_vif_low_latency(mvmvif) && vif->p2p && - fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS)) { - cmd->tx_data_timeout = - cpu_to_le32(IWL_MVM_SHORT_PS_TX_DATA_TIMEOUT); - cmd->rx_data_timeout = - cpu_to_le32(IWL_MVM_SHORT_PS_RX_DATA_TIMEOUT); - } else { - cmd->rx_data_timeout = - cpu_to_le32(IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT); - cmd->tx_data_timeout = - cpu_to_le32(IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT); - } - - if (iwl_mvm_power_allow_uapsd(mvm, vif)) - iwl_mvm_power_configure_uapsd(mvm, vif, cmd); - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE) - cmd->keep_alive_seconds = - cpu_to_le16(mvmvif->dbgfs_pm.keep_alive_seconds); - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) { - if (mvmvif->dbgfs_pm.skip_over_dtim) - cmd->flags |= - cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); - else - cmd->flags &= - cpu_to_le16(~POWER_FLAGS_SKIP_OVER_DTIM_MSK); - } - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_RX_DATA_TIMEOUT) - cmd->rx_data_timeout = - cpu_to_le32(mvmvif->dbgfs_pm.rx_data_timeout); - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_TX_DATA_TIMEOUT) - cmd->tx_data_timeout = - cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout); - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS) - cmd->skip_dtim_periods = mvmvif->dbgfs_pm.skip_dtim_periods; - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) { - if (mvmvif->dbgfs_pm.lprx_ena) - cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK); - else - cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK); - } - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD) - cmd->lprx_rssi_threshold = mvmvif->dbgfs_pm.lprx_rssi_threshold; - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SNOOZE_ENABLE) { - if (mvmvif->dbgfs_pm.snooze_ena) - cmd->flags |= - cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK); - else - cmd->flags &= - cpu_to_le16(~POWER_FLAGS_SNOOZE_ENA_MSK); - } - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_UAPSD_MISBEHAVING) { - u16 flag = POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK; - if (mvmvif->dbgfs_pm.uapsd_misbehaving) - cmd->flags |= cpu_to_le16(flag); - else - cmd->flags &= cpu_to_le16(flag); - } -#endif /* CONFIG_IWLWIFI_DEBUGFS */ -} - -static int iwl_mvm_power_send_cmd(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct iwl_mac_power_cmd cmd = {}; - - iwl_mvm_power_build_cmd(mvm, vif, &cmd, - mvm->cur_ucode != IWL_UCODE_WOWLAN); - iwl_mvm_power_log(mvm, &cmd); -#ifdef CONFIG_IWLWIFI_DEBUGFS - memcpy(&iwl_mvm_vif_from_mac80211(vif)->mac_pwr_cmd, &cmd, sizeof(cmd)); -#endif - - return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, 0, - sizeof(cmd), &cmd); -} - -int iwl_mvm_power_update_device(struct iwl_mvm *mvm) -{ - struct iwl_device_power_cmd cmd = { - .flags = 0, - }; - - if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) - mvm->ps_disabled = true; - - if (!mvm->ps_disabled) - cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK); - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if ((mvm->cur_ucode == IWL_UCODE_WOWLAN) ? mvm->disable_power_off_d3 : - mvm->disable_power_off) - cmd.flags &= - cpu_to_le16(~DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK); -#endif - IWL_DEBUG_POWER(mvm, - "Sending device power command with flags = 0x%X\n", - cmd.flags); - - return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, 0, sizeof(cmd), - &cmd); -} - -void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (memcmp(vif->bss_conf.bssid, mvmvif->uapsd_misbehaving_bssid, - ETH_ALEN)) - eth_zero_addr(mvmvif->uapsd_misbehaving_bssid); -} - -static void iwl_mvm_power_uapsd_misbehav_ap_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - u8 *ap_sta_id = _data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - /* The ap_sta_id is not expected to change during current association - * so no explicit protection is needed - */ - if (mvmvif->ap_sta_id == *ap_sta_id) - memcpy(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid, - ETH_ALEN); -} - -void iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_uapsd_misbehaving_ap_notif *notif = (void *)pkt->data; - u8 ap_sta_id = le32_to_cpu(notif->sta_id); - - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_power_uapsd_misbehav_ap_iterator, &ap_sta_id); -} - -struct iwl_power_vifs { - struct iwl_mvm *mvm; - struct ieee80211_vif *bf_vif; - struct ieee80211_vif *bss_vif; - struct ieee80211_vif *p2p_vif; - struct ieee80211_vif *ap_vif; - struct ieee80211_vif *monitor_vif; - bool p2p_active; - bool bss_active; - bool ap_active; - bool monitor_active; -}; - -static void iwl_mvm_power_disable_pm_iterator(void *_data, u8* mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - mvmvif->pm_enabled = false; -} - -static void iwl_mvm_power_ps_disabled_iterator(void *_data, u8* mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - bool *disable_ps = _data; - - if (mvmvif->phy_ctxt) - if (mvmvif->phy_ctxt->id < MAX_PHYS) - *disable_ps |= mvmvif->ps_disabled; -} - -static void iwl_mvm_power_get_vifs_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_power_vifs *power_iterator = _data; - - switch (ieee80211_vif_type_p2p(vif)) { - case NL80211_IFTYPE_P2P_DEVICE: - break; - - case NL80211_IFTYPE_P2P_GO: - case NL80211_IFTYPE_AP: - /* only a single MAC of the same type */ - WARN_ON(power_iterator->ap_vif); - power_iterator->ap_vif = vif; - if (mvmvif->phy_ctxt) - if (mvmvif->phy_ctxt->id < MAX_PHYS) - power_iterator->ap_active = true; - break; - - case NL80211_IFTYPE_MONITOR: - /* only a single MAC of the same type */ - WARN_ON(power_iterator->monitor_vif); - power_iterator->monitor_vif = vif; - if (mvmvif->phy_ctxt) - if (mvmvif->phy_ctxt->id < MAX_PHYS) - power_iterator->monitor_active = true; - break; - - case NL80211_IFTYPE_P2P_CLIENT: - /* only a single MAC of the same type */ - WARN_ON(power_iterator->p2p_vif); - power_iterator->p2p_vif = vif; - if (mvmvif->phy_ctxt) - if (mvmvif->phy_ctxt->id < MAX_PHYS) - power_iterator->p2p_active = true; - break; - - case NL80211_IFTYPE_STATION: - /* only a single MAC of the same type */ - WARN_ON(power_iterator->bss_vif); - power_iterator->bss_vif = vif; - if (mvmvif->phy_ctxt) - if (mvmvif->phy_ctxt->id < MAX_PHYS) - power_iterator->bss_active = true; - - if (mvmvif->bf_data.bf_enabled && - !WARN_ON(power_iterator->bf_vif)) - power_iterator->bf_vif = vif; - - break; - - default: - break; - } -} - -static void iwl_mvm_power_set_pm(struct iwl_mvm *mvm, - struct iwl_power_vifs *vifs) -{ - struct iwl_mvm_vif *bss_mvmvif = NULL; - struct iwl_mvm_vif *p2p_mvmvif = NULL; - struct iwl_mvm_vif *ap_mvmvif = NULL; - bool client_same_channel = false; - bool ap_same_channel = false; - - lockdep_assert_held(&mvm->mutex); - - /* set pm_enable to false */ - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_power_disable_pm_iterator, - NULL); - - if (vifs->bss_vif) - bss_mvmvif = iwl_mvm_vif_from_mac80211(vifs->bss_vif); - - if (vifs->p2p_vif) - p2p_mvmvif = iwl_mvm_vif_from_mac80211(vifs->p2p_vif); - - if (vifs->ap_vif) - ap_mvmvif = iwl_mvm_vif_from_mac80211(vifs->ap_vif); - - /* don't allow PM if any TDLS stations exist */ - if (iwl_mvm_tdls_sta_count(mvm, NULL)) - return; - - /* enable PM on bss if bss stand alone */ - if (vifs->bss_active && !vifs->p2p_active && !vifs->ap_active) { - bss_mvmvif->pm_enabled = true; - return; - } - - /* enable PM on p2p if p2p stand alone */ - if (vifs->p2p_active && !vifs->bss_active && !vifs->ap_active) { - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM) - p2p_mvmvif->pm_enabled = true; - return; - } - - if (vifs->bss_active && vifs->p2p_active) - client_same_channel = (bss_mvmvif->phy_ctxt->id == - p2p_mvmvif->phy_ctxt->id); - if (vifs->bss_active && vifs->ap_active) - ap_same_channel = (bss_mvmvif->phy_ctxt->id == - ap_mvmvif->phy_ctxt->id); - - /* clients are not stand alone: enable PM if DCM */ - if (!(client_same_channel || ap_same_channel) && - (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM)) { - if (vifs->bss_active) - bss_mvmvif->pm_enabled = true; - if (vifs->p2p_active && - (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM)) - p2p_mvmvif->pm_enabled = true; - return; - } - - /* - * There is only one channel in the system and there are only - * bss and p2p clients that share it - */ - if (client_same_channel && !vifs->ap_active && - (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM)) { - /* share same channel*/ - bss_mvmvif->pm_enabled = true; - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM) - p2p_mvmvif->pm_enabled = true; - } -} - -#ifdef CONFIG_IWLWIFI_DEBUGFS -int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, char *buf, - int bufsz) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mac_power_cmd cmd = {}; - int pos = 0; - - mutex_lock(&mvm->mutex); - memcpy(&cmd, &mvmvif->mac_pwr_cmd, sizeof(cmd)); - mutex_unlock(&mvm->mutex); - - pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n", - iwlmvm_mod_params.power_scheme); - pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n", - le16_to_cpu(cmd.flags)); - pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n", - le16_to_cpu(cmd.keep_alive_seconds)); - - if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK))) - return pos; - - pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n", - (cmd.flags & - cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ? 1 : 0); - pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n", - cmd.skip_dtim_periods); - if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) { - pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n", - le32_to_cpu(cmd.rx_data_timeout)); - pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n", - le32_to_cpu(cmd.tx_data_timeout)); - } - if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK)) - pos += scnprintf(buf+pos, bufsz-pos, - "lprx_rssi_threshold = %d\n", - cmd.lprx_rssi_threshold); - - if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) - return pos; - - pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout_uapsd = %d\n", - le32_to_cpu(cmd.rx_data_timeout_uapsd)); - pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout_uapsd = %d\n", - le32_to_cpu(cmd.tx_data_timeout_uapsd)); - pos += scnprintf(buf+pos, bufsz-pos, "qndp_tid = %d\n", cmd.qndp_tid); - pos += scnprintf(buf+pos, bufsz-pos, "uapsd_ac_flags = 0x%x\n", - cmd.uapsd_ac_flags); - pos += scnprintf(buf+pos, bufsz-pos, "uapsd_max_sp = %d\n", - cmd.uapsd_max_sp); - pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_packets = %d\n", - cmd.heavy_tx_thld_packets); - pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_packets = %d\n", - cmd.heavy_rx_thld_packets); - pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_percentage = %d\n", - cmd.heavy_tx_thld_percentage); - pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_percentage = %d\n", - cmd.heavy_rx_thld_percentage); - pos += scnprintf(buf+pos, bufsz-pos, "uapsd_misbehaving_enable = %d\n", - (cmd.flags & - cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK)) ? - 1 : 0); - - if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK))) - return pos; - - pos += scnprintf(buf+pos, bufsz-pos, "snooze_interval = %d\n", - cmd.snooze_interval); - pos += scnprintf(buf+pos, bufsz-pos, "snooze_window = %d\n", - cmd.snooze_window); - - return pos; -} - -void -iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, - struct iwl_beacon_filter_cmd *cmd) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf; - - if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ENERGY_DELTA) - cmd->bf_energy_delta = cpu_to_le32(dbgfs_bf->bf_energy_delta); - if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA) - cmd->bf_roaming_energy_delta = - cpu_to_le32(dbgfs_bf->bf_roaming_energy_delta); - if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_STATE) - cmd->bf_roaming_state = cpu_to_le32(dbgfs_bf->bf_roaming_state); - if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_THRESHOLD) - cmd->bf_temp_threshold = - cpu_to_le32(dbgfs_bf->bf_temp_threshold); - if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_FAST_FILTER) - cmd->bf_temp_fast_filter = - cpu_to_le32(dbgfs_bf->bf_temp_fast_filter); - if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_SLOW_FILTER) - cmd->bf_temp_slow_filter = - cpu_to_le32(dbgfs_bf->bf_temp_slow_filter); - if (dbgfs_bf->mask & MVM_DEBUGFS_BF_DEBUG_FLAG) - cmd->bf_debug_flag = cpu_to_le32(dbgfs_bf->bf_debug_flag); - if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ESCAPE_TIMER) - cmd->bf_escape_timer = cpu_to_le32(dbgfs_bf->bf_escape_timer); - if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ESCAPE_TIMER) - cmd->ba_escape_timer = cpu_to_le32(dbgfs_bf->ba_escape_timer); - if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT) - cmd->ba_enable_beacon_abort = - cpu_to_le32(dbgfs_bf->ba_enable_beacon_abort); -} -#endif - -static int _iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_beacon_filter_cmd *cmd, - u32 cmd_flags, - bool d0i3) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; - - if (mvmvif != mvm->bf_allowed_vif || !vif->bss_conf.dtim_period || - vif->type != NL80211_IFTYPE_STATION || vif->p2p) - return 0; - - iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, cmd, d0i3); - if (!d0i3) - iwl_mvm_beacon_filter_debugfs_parameters(vif, cmd); - ret = iwl_mvm_beacon_filter_send_cmd(mvm, cmd, cmd_flags); - - /* don't change bf_enabled in case of temporary d0i3 configuration */ - if (!ret && !d0i3) - mvmvif->bf_data.bf_enabled = true; - - return ret; -} - -int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 flags) -{ - struct iwl_beacon_filter_cmd cmd = { - IWL_BF_CMD_CONFIG_DEFAULTS, - .bf_enable_beacon_filter = cpu_to_le32(1), - }; - - return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, flags, false); -} - -static int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool enable) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_beacon_filter_cmd cmd = { - IWL_BF_CMD_CONFIG_DEFAULTS, - .bf_enable_beacon_filter = cpu_to_le32(1), - }; - - if (!mvmvif->bf_data.bf_enabled) - return 0; - - if (mvm->cur_ucode == IWL_UCODE_WOWLAN) - cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3); - - mvmvif->bf_data.ba_enabled = enable; - return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, 0, false); -} - -int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 flags) -{ - struct iwl_beacon_filter_cmd cmd = {}; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; - - if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) - return 0; - - ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd, flags); - - if (!ret) - mvmvif->bf_data.bf_enabled = false; - - return ret; -} - -static int iwl_mvm_power_set_ps(struct iwl_mvm *mvm) -{ - bool disable_ps; - int ret; - - /* disable PS if CAM */ - disable_ps = (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM); - /* ...or if any of the vifs require PS to be off */ - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_power_ps_disabled_iterator, - &disable_ps); - - /* update device power state if it has changed */ - if (mvm->ps_disabled != disable_ps) { - bool old_ps_disabled = mvm->ps_disabled; - - mvm->ps_disabled = disable_ps; - ret = iwl_mvm_power_update_device(mvm); - if (ret) { - mvm->ps_disabled = old_ps_disabled; - return ret; - } - } - - return 0; -} - -static int iwl_mvm_power_set_ba(struct iwl_mvm *mvm, - struct iwl_power_vifs *vifs) -{ - struct iwl_mvm_vif *mvmvif; - bool ba_enable; - - if (!vifs->bf_vif) - return 0; - - mvmvif = iwl_mvm_vif_from_mac80211(vifs->bf_vif); - - ba_enable = !(!mvmvif->pm_enabled || mvm->ps_disabled || - !vifs->bf_vif->bss_conf.ps || - iwl_mvm_vif_low_latency(mvmvif)); - - return iwl_mvm_update_beacon_abort(mvm, vifs->bf_vif, ba_enable); -} - -int iwl_mvm_power_update_ps(struct iwl_mvm *mvm) -{ - struct iwl_power_vifs vifs = { - .mvm = mvm, - }; - int ret; - - lockdep_assert_held(&mvm->mutex); - - /* get vifs info */ - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_power_get_vifs_iterator, &vifs); - - ret = iwl_mvm_power_set_ps(mvm); - if (ret) - return ret; - - return iwl_mvm_power_set_ba(mvm, &vifs); -} - -int iwl_mvm_power_update_mac(struct iwl_mvm *mvm) -{ - struct iwl_power_vifs vifs = { - .mvm = mvm, - }; - int ret; - - lockdep_assert_held(&mvm->mutex); - - /* get vifs info */ - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_power_get_vifs_iterator, &vifs); - - iwl_mvm_power_set_pm(mvm, &vifs); - - ret = iwl_mvm_power_set_ps(mvm); - if (ret) - return ret; - - if (vifs.bss_vif) { - ret = iwl_mvm_power_send_cmd(mvm, vifs.bss_vif); - if (ret) - return ret; - } - - if (vifs.p2p_vif) { - ret = iwl_mvm_power_send_cmd(mvm, vifs.p2p_vif); - if (ret) - return ret; - } - - return iwl_mvm_power_set_ba(mvm, &vifs); -} - -int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool enable, u32 flags) -{ - int ret; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mac_power_cmd cmd = {}; - - if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) - return 0; - - if (!vif->bss_conf.assoc) - return 0; - - iwl_mvm_power_build_cmd(mvm, vif, &cmd, !enable); - - iwl_mvm_power_log(mvm, &cmd); -#ifdef CONFIG_IWLWIFI_DEBUGFS - memcpy(&mvmvif->mac_pwr_cmd, &cmd, sizeof(cmd)); -#endif - ret = iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, flags, - sizeof(cmd), &cmd); - if (ret) - return ret; - - /* configure beacon filtering */ - if (mvmvif != mvm->bf_allowed_vif) - return 0; - - if (enable) { - struct iwl_beacon_filter_cmd cmd_bf = { - IWL_BF_CMD_CONFIG_D0I3, - .bf_enable_beacon_filter = cpu_to_le32(1), - }; - ret = _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd_bf, - flags, true); - } else { - if (mvmvif->bf_data.bf_enabled) - ret = iwl_mvm_enable_beacon_filter(mvm, vif, flags); - else - ret = iwl_mvm_disable_beacon_filter(mvm, vif, flags); - } - - return ret; -} diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c deleted file mode 100644 index 509a66d05..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/quota.c +++ /dev/null @@ -1,328 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * 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.h" -#include "mvm.h" - -#define QUOTA_100 IWL_MVM_MAX_QUOTA -#define QUOTA_LOWLAT_MIN ((QUOTA_100 * IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT) / 100) - -struct iwl_mvm_quota_iterator_data { - int n_interfaces[MAX_BINDINGS]; - int colors[MAX_BINDINGS]; - int low_latency[MAX_BINDINGS]; - int n_low_latency_bindings; - struct ieee80211_vif *disabled_vif; -}; - -static void iwl_mvm_quota_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_quota_iterator_data *data = _data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - u16 id; - - /* skip disabled interfaces here immediately */ - if (vif == data->disabled_vif) - return; - - if (!mvmvif->phy_ctxt) - return; - - /* currently, PHY ID == binding ID */ - id = mvmvif->phy_ctxt->id; - - /* need at least one binding per PHY */ - BUILD_BUG_ON(NUM_PHY_CTX > MAX_BINDINGS); - - if (WARN_ON_ONCE(id >= MAX_BINDINGS)) - return; - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - if (vif->bss_conf.assoc) - break; - return; - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_ADHOC: - if (mvmvif->ap_ibss_active) - break; - return; - case NL80211_IFTYPE_MONITOR: - if (mvmvif->monitor_active) - break; - return; - case NL80211_IFTYPE_P2P_DEVICE: - return; - default: - WARN_ON_ONCE(1); - return; - } - - if (data->colors[id] < 0) - data->colors[id] = mvmvif->phy_ctxt->color; - else - WARN_ON_ONCE(data->colors[id] != mvmvif->phy_ctxt->color); - - data->n_interfaces[id]++; - - if (iwl_mvm_vif_low_latency(mvmvif) && !data->low_latency[id]) { - data->n_low_latency_bindings++; - data->low_latency[id] = true; - } -} - -static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm, - struct iwl_time_quota_cmd *cmd) -{ -#ifdef CONFIG_NL80211_TESTMODE - struct iwl_mvm_vif *mvmvif; - int i, phy_id = -1, beacon_int = 0; - - if (!mvm->noa_duration || !mvm->noa_vif) - return; - - mvmvif = iwl_mvm_vif_from_mac80211(mvm->noa_vif); - if (!mvmvif->ap_ibss_active) - return; - - phy_id = mvmvif->phy_ctxt->id; - beacon_int = mvm->noa_vif->bss_conf.beacon_int; - - for (i = 0; i < MAX_BINDINGS; i++) { - u32 id_n_c = le32_to_cpu(cmd->quotas[i].id_and_color); - u32 id = (id_n_c & FW_CTXT_ID_MSK) >> FW_CTXT_ID_POS; - u32 quota = le32_to_cpu(cmd->quotas[i].quota); - - if (id != phy_id) - continue; - - quota *= (beacon_int - mvm->noa_duration); - quota /= beacon_int; - - IWL_DEBUG_QUOTA(mvm, "quota: adjust for NoA from %d to %d\n", - le32_to_cpu(cmd->quotas[i].quota), quota); - - cmd->quotas[i].quota = cpu_to_le32(quota); - } -#endif -} - -int iwl_mvm_update_quotas(struct iwl_mvm *mvm, - bool force_update, - struct ieee80211_vif *disabled_vif) -{ - struct iwl_time_quota_cmd cmd = {}; - int i, idx, err, num_active_macs, quota, quota_rem, n_non_lowlat; - struct iwl_mvm_quota_iterator_data data = { - .n_interfaces = {}, - .colors = { -1, -1, -1, -1 }, - .disabled_vif = disabled_vif, - }; - struct iwl_time_quota_cmd *last = &mvm->last_quota_cmd; - bool send = false; - - lockdep_assert_held(&mvm->mutex); - - /* update all upon completion */ - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) - return 0; - - /* iterator data above must match */ - BUILD_BUG_ON(MAX_BINDINGS != 4); - - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_quota_iterator, &data); - - /* - * The FW's scheduling session consists of - * IWL_MVM_MAX_QUOTA fragments. Divide these fragments - * equally between all the bindings that require quota - */ - num_active_macs = 0; - for (i = 0; i < MAX_BINDINGS; i++) { - cmd.quotas[i].id_and_color = cpu_to_le32(FW_CTXT_INVALID); - num_active_macs += data.n_interfaces[i]; - } - - n_non_lowlat = num_active_macs; - - if (data.n_low_latency_bindings == 1) { - for (i = 0; i < MAX_BINDINGS; i++) { - if (data.low_latency[i]) { - n_non_lowlat -= data.n_interfaces[i]; - break; - } - } - } - - if (data.n_low_latency_bindings == 1 && n_non_lowlat) { - /* - * Reserve quota for the low latency binding in case that - * there are several data bindings but only a single - * low latency one. Split the rest of the quota equally - * between the other data interfaces. - */ - quota = (QUOTA_100 - QUOTA_LOWLAT_MIN) / n_non_lowlat; - quota_rem = QUOTA_100 - n_non_lowlat * quota - - QUOTA_LOWLAT_MIN; - IWL_DEBUG_QUOTA(mvm, - "quota: low-latency binding active, remaining quota per other binding: %d\n", - quota); - } else if (num_active_macs) { - /* - * There are 0 or more than 1 low latency bindings, or all the - * data interfaces belong to the single low latency binding. - * Split the quota equally between the data interfaces. - */ - quota = QUOTA_100 / num_active_macs; - quota_rem = QUOTA_100 % num_active_macs; - IWL_DEBUG_QUOTA(mvm, - "quota: splitting evenly per binding: %d\n", - quota); - } else { - /* values don't really matter - won't be used */ - quota = 0; - quota_rem = 0; - } - - for (idx = 0, i = 0; i < MAX_BINDINGS; i++) { - if (data.colors[i] < 0) - continue; - - cmd.quotas[idx].id_and_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(i, data.colors[i])); - - if (data.n_interfaces[i] <= 0) - cmd.quotas[idx].quota = cpu_to_le32(0); - else if (data.n_low_latency_bindings == 1 && n_non_lowlat && - data.low_latency[i]) - /* - * There is more than one binding, but only one of the - * bindings is in low latency. For this case, allocate - * the minimal required quota for the low latency - * binding. - */ - cmd.quotas[idx].quota = cpu_to_le32(QUOTA_LOWLAT_MIN); - else - cmd.quotas[idx].quota = - cpu_to_le32(quota * data.n_interfaces[i]); - - WARN_ONCE(le32_to_cpu(cmd.quotas[idx].quota) > QUOTA_100, - "Binding=%d, quota=%u > max=%u\n", - idx, le32_to_cpu(cmd.quotas[idx].quota), QUOTA_100); - - cmd.quotas[idx].max_duration = cpu_to_le32(0); - - idx++; - } - - /* Give the remainder of the session to the first data binding */ - for (i = 0; i < MAX_BINDINGS; i++) { - if (le32_to_cpu(cmd.quotas[i].quota) != 0) { - le32_add_cpu(&cmd.quotas[i].quota, quota_rem); - IWL_DEBUG_QUOTA(mvm, - "quota: giving remainder of %d to binding %d\n", - quota_rem, i); - break; - } - } - - iwl_mvm_adjust_quota_for_noa(mvm, &cmd); - - /* check that we have non-zero quota for all valid bindings */ - for (i = 0; i < MAX_BINDINGS; i++) { - if (cmd.quotas[i].id_and_color != last->quotas[i].id_and_color) - send = true; - if (cmd.quotas[i].max_duration != last->quotas[i].max_duration) - send = true; - if (abs((int)le32_to_cpu(cmd.quotas[i].quota) - - (int)le32_to_cpu(last->quotas[i].quota)) - > IWL_MVM_QUOTA_THRESHOLD) - send = true; - if (cmd.quotas[i].id_and_color == cpu_to_le32(FW_CTXT_INVALID)) - continue; - WARN_ONCE(cmd.quotas[i].quota == 0, - "zero quota on binding %d\n", i); - } - - if (!send && !force_update) { - /* don't send a practically unchanged command, the firmware has - * to re-initialize a lot of state and that can have an adverse - * impact on it - */ - return 0; - } - - err = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0, sizeof(cmd), &cmd); - - if (err) - IWL_ERR(mvm, "Failed to send quota: %d\n", err); - else - mvm->last_quota_cmd = cmd; - return err; -} diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c deleted file mode 100644 index d1ad10391..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ /dev/null @@ -1,3983 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * 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 LICENSE. - * - * Contact Information: - * Intel Linux Wireless <ilw@linux.intel.com> - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ -#include <linux/kernel.h> -#include <linux/skbuff.h> -#include <linux/slab.h> -#include <net/mac80211.h> - -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/delay.h> - -#include <linux/workqueue.h> -#include "rs.h" -#include "fw-api.h" -#include "sta.h" -#include "iwl-op-mode.h" -#include "mvm.h" -#include "debugfs.h" - -#define RS_NAME "iwl-mvm-rs" - -#define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */ - -/* Calculations of success ratio are done in fixed point where 12800 is 100%. - * Use this macro when dealing with thresholds consts set as a percentage - */ -#define RS_PERCENT(x) (128 * x) - -static u8 rs_ht_to_legacy[] = { - [IWL_RATE_MCS_0_INDEX] = IWL_RATE_6M_INDEX, - [IWL_RATE_MCS_1_INDEX] = IWL_RATE_9M_INDEX, - [IWL_RATE_MCS_2_INDEX] = IWL_RATE_12M_INDEX, - [IWL_RATE_MCS_3_INDEX] = IWL_RATE_18M_INDEX, - [IWL_RATE_MCS_4_INDEX] = IWL_RATE_24M_INDEX, - [IWL_RATE_MCS_5_INDEX] = IWL_RATE_36M_INDEX, - [IWL_RATE_MCS_6_INDEX] = IWL_RATE_48M_INDEX, - [IWL_RATE_MCS_7_INDEX] = IWL_RATE_54M_INDEX, - [IWL_RATE_MCS_8_INDEX] = IWL_RATE_54M_INDEX, - [IWL_RATE_MCS_9_INDEX] = IWL_RATE_54M_INDEX, -}; - -static const u8 ant_toggle_lookup[] = { - [ANT_NONE] = ANT_NONE, - [ANT_A] = ANT_B, - [ANT_B] = ANT_C, - [ANT_AB] = ANT_BC, - [ANT_C] = ANT_A, - [ANT_AC] = ANT_AB, - [ANT_BC] = ANT_AC, - [ANT_ABC] = ANT_ABC, -}; - -#define IWL_DECLARE_RATE_INFO(r, s, rp, rn) \ - [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \ - IWL_RATE_HT_SISO_MCS_##s##_PLCP, \ - IWL_RATE_HT_MIMO2_MCS_##s##_PLCP, \ - IWL_RATE_VHT_SISO_MCS_##s##_PLCP, \ - IWL_RATE_VHT_MIMO2_MCS_##s##_PLCP,\ - IWL_RATE_##rp##M_INDEX, \ - IWL_RATE_##rn##M_INDEX } - -#define IWL_DECLARE_MCS_RATE(s) \ - [IWL_RATE_MCS_##s##_INDEX] = { IWL_RATE_INVM_PLCP, \ - IWL_RATE_HT_SISO_MCS_##s##_PLCP, \ - IWL_RATE_HT_MIMO2_MCS_##s##_PLCP, \ - IWL_RATE_VHT_SISO_MCS_##s##_PLCP, \ - IWL_RATE_VHT_MIMO2_MCS_##s##_PLCP, \ - IWL_RATE_INVM_INDEX, \ - IWL_RATE_INVM_INDEX } - -/* - * Parameter order: - * rate, ht rate, prev rate, next rate - * - * If there isn't a valid next or previous rate then INV is used which - * maps to IWL_RATE_INVALID - * - */ -static const struct iwl_rs_rate_info iwl_rates[IWL_RATE_COUNT] = { - IWL_DECLARE_RATE_INFO(1, INV, INV, 2), /* 1mbps */ - IWL_DECLARE_RATE_INFO(2, INV, 1, 5), /* 2mbps */ - IWL_DECLARE_RATE_INFO(5, INV, 2, 11), /*5.5mbps */ - IWL_DECLARE_RATE_INFO(11, INV, 9, 12), /* 11mbps */ - IWL_DECLARE_RATE_INFO(6, 0, 5, 11), /* 6mbps ; MCS 0 */ - IWL_DECLARE_RATE_INFO(9, INV, 6, 11), /* 9mbps */ - IWL_DECLARE_RATE_INFO(12, 1, 11, 18), /* 12mbps ; MCS 1 */ - IWL_DECLARE_RATE_INFO(18, 2, 12, 24), /* 18mbps ; MCS 2 */ - IWL_DECLARE_RATE_INFO(24, 3, 18, 36), /* 24mbps ; MCS 3 */ - IWL_DECLARE_RATE_INFO(36, 4, 24, 48), /* 36mbps ; MCS 4 */ - IWL_DECLARE_RATE_INFO(48, 5, 36, 54), /* 48mbps ; MCS 5 */ - IWL_DECLARE_RATE_INFO(54, 6, 48, INV), /* 54mbps ; MCS 6 */ - IWL_DECLARE_MCS_RATE(7), /* MCS 7 */ - IWL_DECLARE_MCS_RATE(8), /* MCS 8 */ - IWL_DECLARE_MCS_RATE(9), /* MCS 9 */ -}; - -enum rs_action { - RS_ACTION_STAY = 0, - RS_ACTION_DOWNSCALE = -1, - RS_ACTION_UPSCALE = 1, -}; - -enum rs_column_mode { - RS_INVALID = 0, - RS_LEGACY, - RS_SISO, - RS_MIMO2, -}; - -#define MAX_NEXT_COLUMNS 7 -#define MAX_COLUMN_CHECKS 3 - -struct rs_tx_column; - -typedef bool (*allow_column_func_t) (struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct rs_rate *rate, - const struct rs_tx_column *next_col); - -struct rs_tx_column { - enum rs_column_mode mode; - u8 ant; - bool sgi; - enum rs_column next_columns[MAX_NEXT_COLUMNS]; - allow_column_func_t checks[MAX_COLUMN_CHECKS]; -}; - -static bool rs_ant_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - struct rs_rate *rate, - const struct rs_tx_column *next_col) -{ - return iwl_mvm_bt_coex_is_ant_avail(mvm, next_col->ant); -} - -static bool rs_mimo_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - struct rs_rate *rate, - const struct rs_tx_column *next_col) -{ - struct iwl_mvm_sta *mvmsta; - struct iwl_mvm_vif *mvmvif; - - if (!sta->ht_cap.ht_supported) - return false; - - if (sta->smps_mode == IEEE80211_SMPS_STATIC) - return false; - - if (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) < 2) - return false; - - if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) - return false; - - mvmsta = iwl_mvm_sta_from_mac80211(sta); - mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); - - if (mvm->nvm_data->sku_cap_mimo_disabled) - return false; - - return true; -} - -static bool rs_siso_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - struct rs_rate *rate, - const struct rs_tx_column *next_col) -{ - if (!sta->ht_cap.ht_supported) - return false; - - return true; -} - -static bool rs_sgi_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - struct rs_rate *rate, - const struct rs_tx_column *next_col) -{ - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; - - if (is_ht20(rate) && (ht_cap->cap & - IEEE80211_HT_CAP_SGI_20)) - return true; - if (is_ht40(rate) && (ht_cap->cap & - IEEE80211_HT_CAP_SGI_40)) - return true; - if (is_ht80(rate) && (vht_cap->cap & - IEEE80211_VHT_CAP_SHORT_GI_80)) - return true; - - return false; -} - -static const struct rs_tx_column rs_tx_columns[] = { - [RS_COLUMN_LEGACY_ANT_A] = { - .mode = RS_LEGACY, - .ant = ANT_A, - .next_columns = { - RS_COLUMN_LEGACY_ANT_B, - RS_COLUMN_SISO_ANT_A, - RS_COLUMN_MIMO2, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - }, - .checks = { - rs_ant_allow, - }, - }, - [RS_COLUMN_LEGACY_ANT_B] = { - .mode = RS_LEGACY, - .ant = ANT_B, - .next_columns = { - RS_COLUMN_LEGACY_ANT_A, - RS_COLUMN_SISO_ANT_B, - RS_COLUMN_MIMO2, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - }, - .checks = { - rs_ant_allow, - }, - }, - [RS_COLUMN_SISO_ANT_A] = { - .mode = RS_SISO, - .ant = ANT_A, - .next_columns = { - RS_COLUMN_SISO_ANT_B, - RS_COLUMN_MIMO2, - RS_COLUMN_SISO_ANT_A_SGI, - RS_COLUMN_LEGACY_ANT_A, - RS_COLUMN_LEGACY_ANT_B, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - }, - .checks = { - rs_siso_allow, - rs_ant_allow, - }, - }, - [RS_COLUMN_SISO_ANT_B] = { - .mode = RS_SISO, - .ant = ANT_B, - .next_columns = { - RS_COLUMN_SISO_ANT_A, - RS_COLUMN_MIMO2, - RS_COLUMN_SISO_ANT_B_SGI, - RS_COLUMN_LEGACY_ANT_A, - RS_COLUMN_LEGACY_ANT_B, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - }, - .checks = { - rs_siso_allow, - rs_ant_allow, - }, - }, - [RS_COLUMN_SISO_ANT_A_SGI] = { - .mode = RS_SISO, - .ant = ANT_A, - .sgi = true, - .next_columns = { - RS_COLUMN_SISO_ANT_B_SGI, - RS_COLUMN_MIMO2_SGI, - RS_COLUMN_SISO_ANT_A, - RS_COLUMN_LEGACY_ANT_A, - RS_COLUMN_LEGACY_ANT_B, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - }, - .checks = { - rs_siso_allow, - rs_ant_allow, - rs_sgi_allow, - }, - }, - [RS_COLUMN_SISO_ANT_B_SGI] = { - .mode = RS_SISO, - .ant = ANT_B, - .sgi = true, - .next_columns = { - RS_COLUMN_SISO_ANT_A_SGI, - RS_COLUMN_MIMO2_SGI, - RS_COLUMN_SISO_ANT_B, - RS_COLUMN_LEGACY_ANT_A, - RS_COLUMN_LEGACY_ANT_B, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - }, - .checks = { - rs_siso_allow, - rs_ant_allow, - rs_sgi_allow, - }, - }, - [RS_COLUMN_MIMO2] = { - .mode = RS_MIMO2, - .ant = ANT_AB, - .next_columns = { - RS_COLUMN_SISO_ANT_A, - RS_COLUMN_MIMO2_SGI, - RS_COLUMN_LEGACY_ANT_A, - RS_COLUMN_LEGACY_ANT_B, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - }, - .checks = { - rs_mimo_allow, - }, - }, - [RS_COLUMN_MIMO2_SGI] = { - .mode = RS_MIMO2, - .ant = ANT_AB, - .sgi = true, - .next_columns = { - RS_COLUMN_SISO_ANT_A_SGI, - RS_COLUMN_MIMO2, - RS_COLUMN_LEGACY_ANT_A, - RS_COLUMN_LEGACY_ANT_B, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - }, - .checks = { - rs_mimo_allow, - rs_sgi_allow, - }, - }, -}; - -static inline u8 rs_extract_rate(u32 rate_n_flags) -{ - /* also works for HT because bits 7:6 are zero there */ - return (u8)(rate_n_flags & RATE_LEGACY_RATE_MSK); -} - -static int iwl_hwrate_to_plcp_idx(u32 rate_n_flags) -{ - int idx = 0; - - if (rate_n_flags & RATE_MCS_HT_MSK) { - idx = rate_n_flags & RATE_HT_MCS_RATE_CODE_MSK; - idx += IWL_RATE_MCS_0_INDEX; - - /* skip 9M not supported in HT*/ - if (idx >= IWL_RATE_9M_INDEX) - idx += 1; - if ((idx >= IWL_FIRST_HT_RATE) && (idx <= IWL_LAST_HT_RATE)) - return idx; - } else if (rate_n_flags & RATE_MCS_VHT_MSK) { - idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK; - idx += IWL_RATE_MCS_0_INDEX; - - /* skip 9M not supported in VHT*/ - if (idx >= IWL_RATE_9M_INDEX) - idx++; - if ((idx >= IWL_FIRST_VHT_RATE) && (idx <= IWL_LAST_VHT_RATE)) - return idx; - } else { - /* legacy rate format, search for match in table */ - - u8 legacy_rate = rs_extract_rate(rate_n_flags); - for (idx = 0; idx < ARRAY_SIZE(iwl_rates); idx++) - if (iwl_rates[idx].plcp == legacy_rate) - return idx; - } - - return IWL_RATE_INVALID; -} - -static void rs_rate_scale_perform(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, - int tid); -static void rs_fill_lq_cmd(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, - const struct rs_rate *initial_rate); -static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search); - -/** - * The following tables contain the expected throughput metrics for all rates - * - * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits - * - * where invalid entries are zeros. - * - * CCK rates are only valid in legacy table and will only be used in G - * (2.4 GHz) band. - */ - -static const u16 expected_tpt_legacy[IWL_RATE_COUNT] = { - 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0, 0, 0 -}; - -/* Expected TpT tables. 4 indexes: - * 0 - NGI, 1 - SGI, 2 - AGG+NGI, 3 - AGG+SGI - */ -static const u16 expected_tpt_siso_20MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 42, 0, 76, 102, 124, 159, 183, 193, 202, 216, 0}, - {0, 0, 0, 0, 46, 0, 82, 110, 132, 168, 192, 202, 210, 225, 0}, - {0, 0, 0, 0, 49, 0, 97, 145, 192, 285, 375, 420, 464, 551, 0}, - {0, 0, 0, 0, 54, 0, 108, 160, 213, 315, 415, 465, 513, 608, 0}, -}; - -static const u16 expected_tpt_siso_40MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257, 269, 275}, - {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264, 275, 280}, - {0, 0, 0, 0, 101, 0, 199, 295, 389, 570, 744, 828, 911, 1070, 1173}, - {0, 0, 0, 0, 112, 0, 220, 326, 429, 629, 819, 912, 1000, 1173, 1284}, -}; - -static const u16 expected_tpt_siso_80MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 130, 0, 191, 223, 244, 273, 288, 294, 298, 305, 308}, - {0, 0, 0, 0, 138, 0, 200, 231, 251, 279, 293, 298, 302, 308, 312}, - {0, 0, 0, 0, 217, 0, 429, 634, 834, 1220, 1585, 1760, 1931, 2258, 2466}, - {0, 0, 0, 0, 241, 0, 475, 701, 921, 1343, 1741, 1931, 2117, 2468, 2691}, -}; - -static const u16 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 74, 0, 123, 155, 179, 213, 235, 243, 250, 261, 0}, - {0, 0, 0, 0, 81, 0, 131, 164, 187, 221, 242, 250, 256, 267, 0}, - {0, 0, 0, 0, 98, 0, 193, 286, 375, 550, 718, 799, 878, 1032, 0}, - {0, 0, 0, 0, 109, 0, 214, 316, 414, 607, 790, 879, 965, 1132, 0}, -}; - -static const u16 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289, 296, 300}, - {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293, 300, 303}, - {0, 0, 0, 0, 200, 0, 390, 571, 741, 1067, 1365, 1505, 1640, 1894, 2053}, - {0, 0, 0, 0, 221, 0, 430, 630, 816, 1169, 1490, 1641, 1784, 2053, 2221}, -}; - -static const u16 expected_tpt_mimo2_80MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 182, 0, 240, 264, 278, 299, 308, 311, 313, 317, 319}, - {0, 0, 0, 0, 190, 0, 247, 269, 282, 302, 310, 313, 315, 319, 320}, - {0, 0, 0, 0, 428, 0, 833, 1215, 1577, 2254, 2863, 3147, 3418, 3913, 4219}, - {0, 0, 0, 0, 474, 0, 920, 1338, 1732, 2464, 3116, 3418, 3705, 4225, 4545}, -}; - -/* mbps, mcs */ -static const struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = { - { "1", "BPSK DSSS"}, - { "2", "QPSK DSSS"}, - {"5.5", "BPSK CCK"}, - { "11", "QPSK CCK"}, - { "6", "BPSK 1/2"}, - { "9", "BPSK 1/2"}, - { "12", "QPSK 1/2"}, - { "18", "QPSK 3/4"}, - { "24", "16QAM 1/2"}, - { "36", "16QAM 3/4"}, - { "48", "64QAM 2/3"}, - { "54", "64QAM 3/4"}, - { "60", "64QAM 5/6"}, -}; - -#define MCS_INDEX_PER_STREAM (8) - -static const char *rs_pretty_ant(u8 ant) -{ - static const char * const ant_name[] = { - [ANT_NONE] = "None", - [ANT_A] = "A", - [ANT_B] = "B", - [ANT_AB] = "AB", - [ANT_C] = "C", - [ANT_AC] = "AC", - [ANT_BC] = "BC", - [ANT_ABC] = "ABC", - }; - - if (ant > ANT_ABC) - return "UNKNOWN"; - - return ant_name[ant]; -} - -static const char *rs_pretty_lq_type(enum iwl_table_type type) -{ - static const char * const lq_types[] = { - [LQ_NONE] = "NONE", - [LQ_LEGACY_A] = "LEGACY_A", - [LQ_LEGACY_G] = "LEGACY_G", - [LQ_HT_SISO] = "HT SISO", - [LQ_HT_MIMO2] = "HT MIMO", - [LQ_VHT_SISO] = "VHT SISO", - [LQ_VHT_MIMO2] = "VHT MIMO", - }; - - if (type < LQ_NONE || type >= LQ_MAX) - return "UNKNOWN"; - - return lq_types[type]; -} - -static char *rs_pretty_rate(const struct rs_rate *rate) -{ - static char buf[40]; - static const char * const legacy_rates[] = { - [IWL_RATE_1M_INDEX] = "1M", - [IWL_RATE_2M_INDEX] = "2M", - [IWL_RATE_5M_INDEX] = "5.5M", - [IWL_RATE_11M_INDEX] = "11M", - [IWL_RATE_6M_INDEX] = "6M", - [IWL_RATE_9M_INDEX] = "9M", - [IWL_RATE_12M_INDEX] = "12M", - [IWL_RATE_18M_INDEX] = "18M", - [IWL_RATE_24M_INDEX] = "24M", - [IWL_RATE_36M_INDEX] = "36M", - [IWL_RATE_48M_INDEX] = "48M", - [IWL_RATE_54M_INDEX] = "54M", - }; - static const char *const ht_vht_rates[] = { - [IWL_RATE_MCS_0_INDEX] = "MCS0", - [IWL_RATE_MCS_1_INDEX] = "MCS1", - [IWL_RATE_MCS_2_INDEX] = "MCS2", - [IWL_RATE_MCS_3_INDEX] = "MCS3", - [IWL_RATE_MCS_4_INDEX] = "MCS4", - [IWL_RATE_MCS_5_INDEX] = "MCS5", - [IWL_RATE_MCS_6_INDEX] = "MCS6", - [IWL_RATE_MCS_7_INDEX] = "MCS7", - [IWL_RATE_MCS_8_INDEX] = "MCS8", - [IWL_RATE_MCS_9_INDEX] = "MCS9", - }; - const char *rate_str; - - if (is_type_legacy(rate->type)) - rate_str = legacy_rates[rate->index]; - else if (is_type_ht(rate->type) || is_type_vht(rate->type)) - rate_str = ht_vht_rates[rate->index]; - else - rate_str = "BAD_RATE"; - - sprintf(buf, "(%s|%s|%s)", rs_pretty_lq_type(rate->type), - rs_pretty_ant(rate->ant), rate_str); - return buf; -} - -static inline void rs_dump_rate(struct iwl_mvm *mvm, const struct rs_rate *rate, - const char *prefix) -{ - IWL_DEBUG_RATE(mvm, - "%s: %s BW: %d SGI: %d LDPC: %d STBC: %d\n", - prefix, rs_pretty_rate(rate), rate->bw, - rate->sgi, rate->ldpc, rate->stbc); -} - -static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window) -{ - window->data = 0; - window->success_counter = 0; - window->success_ratio = IWL_INVALID_VALUE; - window->counter = 0; - window->average_tpt = IWL_INVALID_VALUE; -} - -static void rs_rate_scale_clear_tbl_windows(struct iwl_mvm *mvm, - struct iwl_scale_tbl_info *tbl) -{ - int i; - - IWL_DEBUG_RATE(mvm, "Clearing up window stats\n"); - for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window(&tbl->win[i]); - - for (i = 0; i < ARRAY_SIZE(tbl->tpc_win); i++) - rs_rate_scale_clear_window(&tbl->tpc_win[i]); -} - -static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type) -{ - return (ant_type & valid_antenna) == ant_type; -} - -static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_data, u8 tid, - struct ieee80211_sta *sta) -{ - int ret = -EAGAIN; - - IWL_DEBUG_HT(mvm, "Starting Tx agg: STA: %pM tid: %d\n", - sta->addr, tid); - ret = ieee80211_start_tx_ba_session(sta, tid, 5000); - if (ret == -EAGAIN) { - /* - * driver and mac80211 is out of sync - * this might be cause by reloading firmware - * stop the tx ba session here - */ - IWL_ERR(mvm, "Fail start Tx agg on tid: %d\n", - tid); - ieee80211_stop_tx_ba_session(sta, tid); - } - return ret; -} - -static void rs_tl_turn_on_agg(struct iwl_mvm *mvm, u8 tid, - struct iwl_lq_sta *lq_data, - struct ieee80211_sta *sta) -{ - if (tid < IWL_MAX_TID_COUNT) - rs_tl_turn_on_agg_for_tid(mvm, lq_data, tid, sta); - else - IWL_ERR(mvm, "tid exceeds max TID count: %d/%d\n", - tid, IWL_MAX_TID_COUNT); -} - -static inline int get_num_of_ant_from_rate(u32 rate_n_flags) -{ - return !!(rate_n_flags & RATE_MCS_ANT_A_MSK) + - !!(rate_n_flags & RATE_MCS_ANT_B_MSK) + - !!(rate_n_flags & RATE_MCS_ANT_C_MSK); -} - -/* - * Static function to get the expected throughput from an iwl_scale_tbl_info - * that wraps a NULL pointer check - */ -static s32 get_expected_tpt(struct iwl_scale_tbl_info *tbl, int rs_index) -{ - if (tbl->expected_tpt) - return tbl->expected_tpt[rs_index]; - return 0; -} - -/** - * rs_collect_tx_data - Update the success/failure sliding window - * - * We keep a sliding window of the last 62 packets transmitted - * at this rate. window->data contains the bitmask of successful - * packets. - */ -static int _rs_collect_tx_data(struct iwl_mvm *mvm, - struct iwl_scale_tbl_info *tbl, - int scale_index, int attempts, int successes, - struct iwl_rate_scale_data *window) -{ - static const u64 mask = (((u64)1) << (IWL_RATE_MAX_WINDOW - 1)); - s32 fail_count, tpt; - - /* Get expected throughput */ - tpt = get_expected_tpt(tbl, scale_index); - - /* - * Keep track of only the latest 62 tx frame attempts in this rate's - * history window; anything older isn't really relevant any more. - * If we have filled up the sliding window, drop the oldest attempt; - * if the oldest attempt (highest bit in bitmap) shows "success", - * subtract "1" from the success counter (this is the main reason - * we keep these bitmaps!). - */ - while (attempts > 0) { - if (window->counter >= IWL_RATE_MAX_WINDOW) { - /* remove earliest */ - window->counter = IWL_RATE_MAX_WINDOW - 1; - - if (window->data & mask) { - window->data &= ~mask; - window->success_counter--; - } - } - - /* Increment frames-attempted counter */ - window->counter++; - - /* Shift bitmap by one frame to throw away oldest history */ - window->data <<= 1; - - /* Mark the most recent #successes attempts as successful */ - if (successes > 0) { - window->success_counter++; - window->data |= 0x1; - successes--; - } - - attempts--; - } - - /* Calculate current success ratio, avoid divide-by-0! */ - if (window->counter > 0) - window->success_ratio = 128 * (100 * window->success_counter) - / window->counter; - else - window->success_ratio = IWL_INVALID_VALUE; - - fail_count = window->counter - window->success_counter; - - /* Calculate average throughput, if we have enough history. */ - if ((fail_count >= IWL_MVM_RS_RATE_MIN_FAILURE_TH) || - (window->success_counter >= IWL_MVM_RS_RATE_MIN_SUCCESS_TH)) - window->average_tpt = (window->success_ratio * tpt + 64) / 128; - else - window->average_tpt = IWL_INVALID_VALUE; - - return 0; -} - -static int rs_collect_tx_data(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl, - int scale_index, int attempts, int successes, - u8 reduced_txp) -{ - struct iwl_rate_scale_data *window = NULL; - int ret; - - if (scale_index < 0 || scale_index >= IWL_RATE_COUNT) - return -EINVAL; - - if (tbl->column != RS_COLUMN_INVALID) { - struct lq_sta_pers *pers = &lq_sta->pers; - - pers->tx_stats[tbl->column][scale_index].total += attempts; - pers->tx_stats[tbl->column][scale_index].success += successes; - } - - /* Select window for current tx bit rate */ - window = &(tbl->win[scale_index]); - - ret = _rs_collect_tx_data(mvm, tbl, scale_index, attempts, successes, - window); - if (ret) - return ret; - - if (WARN_ON_ONCE(reduced_txp > TPC_MAX_REDUCTION)) - return -EINVAL; - - window = &tbl->tpc_win[reduced_txp]; - return _rs_collect_tx_data(mvm, tbl, scale_index, attempts, successes, - window); -} - -/* Convert rs_rate object into ucode rate bitmask */ -static u32 ucode_rate_from_rs_rate(struct iwl_mvm *mvm, - struct rs_rate *rate) -{ - u32 ucode_rate = 0; - int index = rate->index; - - ucode_rate |= ((rate->ant << RATE_MCS_ANT_POS) & - RATE_MCS_ANT_ABC_MSK); - - if (is_legacy(rate)) { - ucode_rate |= iwl_rates[index].plcp; - if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE) - ucode_rate |= RATE_MCS_CCK_MSK; - return ucode_rate; - } - - if (is_ht(rate)) { - if (index < IWL_FIRST_HT_RATE || index > IWL_LAST_HT_RATE) { - IWL_ERR(mvm, "Invalid HT rate index %d\n", index); - index = IWL_LAST_HT_RATE; - } - ucode_rate |= RATE_MCS_HT_MSK; - - if (is_ht_siso(rate)) - ucode_rate |= iwl_rates[index].plcp_ht_siso; - else if (is_ht_mimo2(rate)) - ucode_rate |= iwl_rates[index].plcp_ht_mimo2; - else - WARN_ON_ONCE(1); - } else if (is_vht(rate)) { - if (index < IWL_FIRST_VHT_RATE || index > IWL_LAST_VHT_RATE) { - IWL_ERR(mvm, "Invalid VHT rate index %d\n", index); - index = IWL_LAST_VHT_RATE; - } - ucode_rate |= RATE_MCS_VHT_MSK; - if (is_vht_siso(rate)) - ucode_rate |= iwl_rates[index].plcp_vht_siso; - else if (is_vht_mimo2(rate)) - ucode_rate |= iwl_rates[index].plcp_vht_mimo2; - else - WARN_ON_ONCE(1); - - } else { - IWL_ERR(mvm, "Invalid rate->type %d\n", rate->type); - } - - if (is_siso(rate) && rate->stbc) { - /* To enable STBC we need to set both a flag and ANT_AB */ - ucode_rate |= RATE_MCS_ANT_AB_MSK; - ucode_rate |= RATE_MCS_VHT_STBC_MSK; - } - - ucode_rate |= rate->bw; - if (rate->sgi) - ucode_rate |= RATE_MCS_SGI_MSK; - if (rate->ldpc) - ucode_rate |= RATE_MCS_LDPC_MSK; - - return ucode_rate; -} - -/* Convert a ucode rate into an rs_rate object */ -static int rs_rate_from_ucode_rate(const u32 ucode_rate, - enum ieee80211_band band, - struct rs_rate *rate) -{ - u32 ant_msk = ucode_rate & RATE_MCS_ANT_ABC_MSK; - u8 num_of_ant = get_num_of_ant_from_rate(ucode_rate); - u8 nss; - - memset(rate, 0, sizeof(*rate)); - rate->index = iwl_hwrate_to_plcp_idx(ucode_rate); - - if (rate->index == IWL_RATE_INVALID) - return -EINVAL; - - rate->ant = (ant_msk >> RATE_MCS_ANT_POS); - - /* Legacy */ - if (!(ucode_rate & RATE_MCS_HT_MSK) && - !(ucode_rate & RATE_MCS_VHT_MSK)) { - if (num_of_ant == 1) { - if (band == IEEE80211_BAND_5GHZ) - rate->type = LQ_LEGACY_A; - else - rate->type = LQ_LEGACY_G; - } - - return 0; - } - - /* HT or VHT */ - if (ucode_rate & RATE_MCS_SGI_MSK) - rate->sgi = true; - if (ucode_rate & RATE_MCS_LDPC_MSK) - rate->ldpc = true; - if (ucode_rate & RATE_MCS_VHT_STBC_MSK) - rate->stbc = true; - if (ucode_rate & RATE_MCS_BF_MSK) - rate->bfer = true; - - rate->bw = ucode_rate & RATE_MCS_CHAN_WIDTH_MSK; - - if (ucode_rate & RATE_MCS_HT_MSK) { - nss = ((ucode_rate & RATE_HT_MCS_NSS_MSK) >> - RATE_HT_MCS_NSS_POS) + 1; - - if (nss == 1) { - rate->type = LQ_HT_SISO; - WARN_ONCE(!rate->stbc && !rate->bfer && num_of_ant != 1, - "stbc %d bfer %d", - rate->stbc, rate->bfer); - } else if (nss == 2) { - rate->type = LQ_HT_MIMO2; - WARN_ON_ONCE(num_of_ant != 2); - } else { - WARN_ON_ONCE(1); - } - } else if (ucode_rate & RATE_MCS_VHT_MSK) { - nss = ((ucode_rate & RATE_VHT_MCS_NSS_MSK) >> - RATE_VHT_MCS_NSS_POS) + 1; - - if (nss == 1) { - rate->type = LQ_VHT_SISO; - WARN_ONCE(!rate->stbc && !rate->bfer && num_of_ant != 1, - "stbc %d bfer %d", - rate->stbc, rate->bfer); - } else if (nss == 2) { - rate->type = LQ_VHT_MIMO2; - WARN_ON_ONCE(num_of_ant != 2); - } else { - WARN_ON_ONCE(1); - } - } - - WARN_ON_ONCE(rate->bw == RATE_MCS_CHAN_WIDTH_160); - WARN_ON_ONCE(rate->bw == RATE_MCS_CHAN_WIDTH_80 && - !is_vht(rate)); - - return 0; -} - -/* switch to another antenna/antennas and return 1 */ -/* if no other valid antenna found, return 0 */ -static int rs_toggle_antenna(u32 valid_ant, struct rs_rate *rate) -{ - u8 new_ant_type; - - if (!rate->ant || rate->ant > ANT_ABC) - return 0; - - if (!rs_is_valid_ant(valid_ant, rate->ant)) - return 0; - - new_ant_type = ant_toggle_lookup[rate->ant]; - - while ((new_ant_type != rate->ant) && - !rs_is_valid_ant(valid_ant, new_ant_type)) - new_ant_type = ant_toggle_lookup[new_ant_type]; - - if (new_ant_type == rate->ant) - return 0; - - rate->ant = new_ant_type; - - return 1; -} - -static u16 rs_get_supported_rates(struct iwl_lq_sta *lq_sta, - struct rs_rate *rate) -{ - if (is_legacy(rate)) - return lq_sta->active_legacy_rate; - else if (is_siso(rate)) - return lq_sta->active_siso_rate; - else if (is_mimo2(rate)) - return lq_sta->active_mimo2_rate; - - WARN_ON_ONCE(1); - return 0; -} - -static u16 rs_get_adjacent_rate(struct iwl_mvm *mvm, u8 index, u16 rate_mask, - int rate_type) -{ - u8 high = IWL_RATE_INVALID; - u8 low = IWL_RATE_INVALID; - - /* 802.11A or ht walks to the next literal adjacent rate in - * the rate table */ - if (is_type_a_band(rate_type) || !is_type_legacy(rate_type)) { - int i; - u32 mask; - - /* Find the previous rate that is in the rate mask */ - i = index - 1; - for (mask = (1 << i); i >= 0; i--, mask >>= 1) { - if (rate_mask & mask) { - low = i; - break; - } - } - - /* Find the next rate that is in the rate mask */ - i = index + 1; - for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) { - if (rate_mask & mask) { - high = i; - break; - } - } - - return (high << 8) | low; - } - - low = index; - while (low != IWL_RATE_INVALID) { - low = iwl_rates[low].prev_rs; - if (low == IWL_RATE_INVALID) - break; - if (rate_mask & (1 << low)) - break; - } - - high = index; - while (high != IWL_RATE_INVALID) { - high = iwl_rates[high].next_rs; - if (high == IWL_RATE_INVALID) - break; - if (rate_mask & (1 << high)) - break; - } - - return (high << 8) | low; -} - -static inline bool rs_rate_supported(struct iwl_lq_sta *lq_sta, - struct rs_rate *rate) -{ - return BIT(rate->index) & rs_get_supported_rates(lq_sta, rate); -} - -/* Get the next supported lower rate in the current column. - * Return true if bottom rate in the current column was reached - */ -static bool rs_get_lower_rate_in_column(struct iwl_lq_sta *lq_sta, - struct rs_rate *rate) -{ - u8 low; - u16 high_low; - u16 rate_mask; - struct iwl_mvm *mvm = lq_sta->pers.drv; - - rate_mask = rs_get_supported_rates(lq_sta, rate); - high_low = rs_get_adjacent_rate(mvm, rate->index, rate_mask, - rate->type); - low = high_low & 0xff; - - /* Bottom rate of column reached */ - if (low == IWL_RATE_INVALID) - return true; - - rate->index = low; - return false; -} - -/* Get the next rate to use following a column downgrade */ -static void rs_get_lower_rate_down_column(struct iwl_lq_sta *lq_sta, - struct rs_rate *rate) -{ - struct iwl_mvm *mvm = lq_sta->pers.drv; - - if (is_legacy(rate)) { - /* No column to downgrade from Legacy */ - return; - } else if (is_siso(rate)) { - /* Downgrade to Legacy if we were in SISO */ - if (lq_sta->band == IEEE80211_BAND_5GHZ) - rate->type = LQ_LEGACY_A; - else - rate->type = LQ_LEGACY_G; - - rate->bw = RATE_MCS_CHAN_WIDTH_20; - - WARN_ON_ONCE(rate->index < IWL_RATE_MCS_0_INDEX || - rate->index > IWL_RATE_MCS_9_INDEX); - - rate->index = rs_ht_to_legacy[rate->index]; - rate->ldpc = false; - } else { - /* Downgrade to SISO with same MCS if in MIMO */ - rate->type = is_vht_mimo2(rate) ? - LQ_VHT_SISO : LQ_HT_SISO; - } - - if (num_of_ant(rate->ant) > 1) - rate->ant = first_antenna(iwl_mvm_get_valid_tx_ant(mvm)); - - /* Relevant in both switching to SISO or Legacy */ - rate->sgi = false; - - if (!rs_rate_supported(lq_sta, rate)) - rs_get_lower_rate_in_column(lq_sta, rate); -} - -/* Check if both rates are identical - * allow_ant_mismatch enables matching a SISO rate on ANT_A or ANT_B - * with a rate indicating STBC/BFER and ANT_AB. - */ -static inline bool rs_rate_equal(struct rs_rate *a, - struct rs_rate *b, - bool allow_ant_mismatch) - -{ - bool ant_match = (a->ant == b->ant) && (a->stbc == b->stbc) && - (a->bfer == b->bfer); - - if (allow_ant_mismatch) { - if (a->stbc || a->bfer) { - WARN_ONCE(a->ant != ANT_AB, "stbc %d bfer %d ant %d", - a->stbc, a->bfer, a->ant); - ant_match |= (b->ant == ANT_A || b->ant == ANT_B); - } else if (b->stbc || b->bfer) { - WARN_ONCE(b->ant != ANT_AB, "stbc %d bfer %d ant %d", - b->stbc, b->bfer, b->ant); - ant_match |= (a->ant == ANT_A || a->ant == ANT_B); - } - } - - return (a->type == b->type) && (a->bw == b->bw) && (a->sgi == b->sgi) && - (a->ldpc == b->ldpc) && (a->index == b->index) && ant_match; -} - -/* Check if both rates share the same column */ -static inline bool rs_rate_column_match(struct rs_rate *a, - struct rs_rate *b) -{ - bool ant_match; - - if (a->stbc || a->bfer) - ant_match = (b->ant == ANT_A || b->ant == ANT_B); - else - ant_match = (a->ant == b->ant); - - return (a->type == b->type) && (a->bw == b->bw) && (a->sgi == b->sgi) - && ant_match; -} - -static inline enum rs_column rs_get_column_from_rate(struct rs_rate *rate) -{ - if (is_legacy(rate)) { - if (rate->ant == ANT_A) - return RS_COLUMN_LEGACY_ANT_A; - - if (rate->ant == ANT_B) - return RS_COLUMN_LEGACY_ANT_B; - - goto err; - } - - if (is_siso(rate)) { - if (rate->ant == ANT_A || rate->stbc || rate->bfer) - return rate->sgi ? RS_COLUMN_SISO_ANT_A_SGI : - RS_COLUMN_SISO_ANT_A; - - if (rate->ant == ANT_B) - return rate->sgi ? RS_COLUMN_SISO_ANT_B_SGI : - RS_COLUMN_SISO_ANT_B; - - goto err; - } - - if (is_mimo(rate)) - return rate->sgi ? RS_COLUMN_MIMO2_SGI : RS_COLUMN_MIMO2; - -err: - return RS_COLUMN_INVALID; -} - -static u8 rs_get_tid(struct ieee80211_hdr *hdr) -{ - u8 tid = IWL_MAX_TID_COUNT; - - if (ieee80211_is_data_qos(hdr->frame_control)) { - u8 *qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & 0xf; - } - - if (unlikely(tid > IWL_MAX_TID_COUNT)) - tid = IWL_MAX_TID_COUNT; - - return tid; -} - -void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - int tid, struct ieee80211_tx_info *info) -{ - int legacy_success; - int retries; - int i; - struct iwl_lq_cmd *table; - u32 lq_hwrate; - struct rs_rate lq_rate, tx_resp_rate; - struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl; - u8 reduced_txp = (uintptr_t)info->status.status_driver_data[0]; - u32 tx_resp_hwrate = (uintptr_t)info->status.status_driver_data[1]; - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta; - bool allow_ant_mismatch = fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_LQ_SS_PARAMS); - - /* Treat uninitialized rate scaling data same as non-existing. */ - if (!lq_sta) { - IWL_DEBUG_RATE(mvm, "Station rate scaling not created yet.\n"); - return; - } else if (!lq_sta->pers.drv) { - IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n"); - return; - } - - /* This packet was aggregated but doesn't carry status info */ - if ((info->flags & IEEE80211_TX_CTL_AMPDU) && - !(info->flags & IEEE80211_TX_STAT_AMPDU)) - return; - - rs_rate_from_ucode_rate(tx_resp_hwrate, info->band, &tx_resp_rate); - -#ifdef CONFIG_MAC80211_DEBUGFS - /* Disable last tx check if we are debugging with fixed rate but - * update tx stats */ - if (lq_sta->pers.dbg_fixed_rate) { - int index = tx_resp_rate.index; - enum rs_column column; - int attempts, success; - - column = rs_get_column_from_rate(&tx_resp_rate); - if (WARN_ONCE(column == RS_COLUMN_INVALID, - "Can't map rate 0x%x to column", - tx_resp_hwrate)) - return; - - if (info->flags & IEEE80211_TX_STAT_AMPDU) { - attempts = info->status.ampdu_len; - success = info->status.ampdu_ack_len; - } else { - attempts = info->status.rates[0].count; - success = !!(info->flags & IEEE80211_TX_STAT_ACK); - } - - lq_sta->pers.tx_stats[column][index].total += attempts; - lq_sta->pers.tx_stats[column][index].success += success; - - IWL_DEBUG_RATE(mvm, "Fixed rate 0x%x success %d attempts %d\n", - tx_resp_hwrate, success, attempts); - return; - } -#endif - - if (time_after(jiffies, - (unsigned long)(lq_sta->last_tx + - (IWL_MVM_RS_IDLE_TIMEOUT * HZ)))) { - int t; - - IWL_DEBUG_RATE(mvm, "Tx idle for too long. reinit rs\n"); - for (t = 0; t < IWL_MAX_TID_COUNT; t++) - ieee80211_stop_tx_ba_session(sta, t); - - iwl_mvm_rs_rate_init(mvm, sta, info->band, false); - return; - } - lq_sta->last_tx = jiffies; - - /* Ignore this Tx frame response if its initial rate doesn't match - * that of latest Link Quality command. There may be stragglers - * from a previous Link Quality command, but we're no longer interested - * in those; they're either from the "active" mode while we're trying - * to check "search" mode, or a prior "search" mode after we've moved - * to a new "search" mode (which might become the new "active" mode). - */ - table = &lq_sta->lq; - lq_hwrate = le32_to_cpu(table->rs_table[0]); - rs_rate_from_ucode_rate(lq_hwrate, info->band, &lq_rate); - - /* Here we actually compare this rate to the latest LQ command */ - if (!rs_rate_equal(&tx_resp_rate, &lq_rate, allow_ant_mismatch)) { - IWL_DEBUG_RATE(mvm, - "initial tx resp rate 0x%x does not match 0x%x\n", - tx_resp_hwrate, lq_hwrate); - - /* - * Since rates mis-match, the last LQ command may have failed. - * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with - * ... driver. - */ - lq_sta->missed_rate_counter++; - if (lq_sta->missed_rate_counter > IWL_MVM_RS_MISSED_RATE_MAX) { - lq_sta->missed_rate_counter = 0; - IWL_DEBUG_RATE(mvm, - "Too many rates mismatch. Send sync LQ. rs_state %d\n", - lq_sta->rs_state); - iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false); - } - /* Regardless, ignore this status info for outdated rate */ - return; - } else - /* Rate did match, so reset the missed_rate_counter */ - lq_sta->missed_rate_counter = 0; - - if (!lq_sta->search_better_tbl) { - curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); - } else { - curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); - other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - } - - if (WARN_ON_ONCE(!rs_rate_column_match(&lq_rate, &curr_tbl->rate))) { - IWL_DEBUG_RATE(mvm, - "Neither active nor search matches tx rate\n"); - tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - rs_dump_rate(mvm, &tmp_tbl->rate, "ACTIVE"); - tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); - rs_dump_rate(mvm, &tmp_tbl->rate, "SEARCH"); - rs_dump_rate(mvm, &lq_rate, "ACTUAL"); - - /* - * no matching table found, let's by-pass the data collection - * and continue to perform rate scale to find the rate table - */ - rs_stay_in_table(lq_sta, true); - goto done; - } - - /* - * Updating the frame history depends on whether packets were - * aggregated. - * - * For aggregation, all packets were transmitted at the same rate, the - * first index into rate scale table. - */ - if (info->flags & IEEE80211_TX_STAT_AMPDU) { - /* ampdu_ack_len = 0 marks no BA was received. In this case - * treat it as a single frame loss as we don't want the success - * ratio to dip too quickly because a BA wasn't received - */ - if (info->status.ampdu_ack_len == 0) - info->status.ampdu_len = 1; - - rs_collect_tx_data(mvm, lq_sta, curr_tbl, lq_rate.index, - info->status.ampdu_len, - info->status.ampdu_ack_len, - reduced_txp); - - /* Update success/fail counts if not searching for new mode */ - if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) { - lq_sta->total_success += info->status.ampdu_ack_len; - lq_sta->total_failed += (info->status.ampdu_len - - info->status.ampdu_ack_len); - } - } else { - /* For legacy, update frame history with for each Tx retry. */ - retries = info->status.rates[0].count - 1; - /* HW doesn't send more than 15 retries */ - retries = min(retries, 15); - - /* The last transmission may have been successful */ - legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK); - /* Collect data for each rate used during failed TX attempts */ - for (i = 0; i <= retries; ++i) { - lq_hwrate = le32_to_cpu(table->rs_table[i]); - rs_rate_from_ucode_rate(lq_hwrate, info->band, - &lq_rate); - /* - * Only collect stats if retried rate is in the same RS - * table as active/search. - */ - if (rs_rate_column_match(&lq_rate, &curr_tbl->rate)) - tmp_tbl = curr_tbl; - else if (rs_rate_column_match(&lq_rate, - &other_tbl->rate)) - tmp_tbl = other_tbl; - else - continue; - - rs_collect_tx_data(mvm, lq_sta, tmp_tbl, lq_rate.index, - 1, i < retries ? 0 : legacy_success, - reduced_txp); - } - - /* Update success/fail counts if not searching for new mode */ - if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) { - lq_sta->total_success += legacy_success; - lq_sta->total_failed += retries + (1 - legacy_success); - } - } - /* The last TX rate is cached in lq_sta; it's set in if/else above */ - lq_sta->last_rate_n_flags = lq_hwrate; - IWL_DEBUG_RATE(mvm, "reduced txpower: %d\n", reduced_txp); -done: - /* See if there's a better rate or modulation mode to try. */ - if (sta->supp_rates[info->band]) - rs_rate_scale_perform(mvm, sta, lq_sta, tid); -} - -/* - * mac80211 sends us Tx status - */ -static void rs_mac80211_tx_status(void *mvm_r, - struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, void *priv_sta, - struct sk_buff *skb) -{ - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct iwl_op_mode *op_mode = (struct iwl_op_mode *)mvm_r; - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - - if (!iwl_mvm_sta_from_mac80211(sta)->vif) - return; - - if (!ieee80211_is_data(hdr->frame_control) || - info->flags & IEEE80211_TX_CTL_NO_ACK) - return; - - iwl_mvm_rs_tx_status(mvm, sta, rs_get_tid(hdr), info); -} - -/* - * Begin a period of staying with a selected modulation mode. - * Set "stay_in_tbl" flag to prevent any mode switches. - * Set frame tx success limits according to legacy vs. high-throughput, - * and reset overall (spanning all rates) tx success history statistics. - * These control how long we stay using same modulation mode before - * searching for a new mode. - */ -static void rs_set_stay_in_table(struct iwl_mvm *mvm, u8 is_legacy, - struct iwl_lq_sta *lq_sta) -{ - IWL_DEBUG_RATE(mvm, "Moving to RS_STATE_STAY_IN_COLUMN\n"); - lq_sta->rs_state = RS_STATE_STAY_IN_COLUMN; - if (is_legacy) { - lq_sta->table_count_limit = IWL_MVM_RS_LEGACY_TABLE_COUNT; - lq_sta->max_failure_limit = IWL_MVM_RS_LEGACY_FAILURE_LIMIT; - lq_sta->max_success_limit = IWL_MVM_RS_LEGACY_SUCCESS_LIMIT; - } else { - lq_sta->table_count_limit = IWL_MVM_RS_NON_LEGACY_TABLE_COUNT; - lq_sta->max_failure_limit = IWL_MVM_RS_NON_LEGACY_FAILURE_LIMIT; - lq_sta->max_success_limit = IWL_MVM_RS_NON_LEGACY_SUCCESS_LIMIT; - } - lq_sta->table_count = 0; - lq_sta->total_failed = 0; - lq_sta->total_success = 0; - lq_sta->flush_timer = jiffies; - lq_sta->visited_columns = 0; -} - -static inline int rs_get_max_rate_from_mask(unsigned long rate_mask) -{ - if (rate_mask) - return find_last_bit(&rate_mask, BITS_PER_LONG); - return IWL_RATE_INVALID; -} - -static int rs_get_max_allowed_rate(struct iwl_lq_sta *lq_sta, - const struct rs_tx_column *column) -{ - switch (column->mode) { - case RS_LEGACY: - return lq_sta->max_legacy_rate_idx; - case RS_SISO: - return lq_sta->max_siso_rate_idx; - case RS_MIMO2: - return lq_sta->max_mimo2_rate_idx; - default: - WARN_ON_ONCE(1); - } - - return lq_sta->max_legacy_rate_idx; -} - -static const u16 *rs_get_expected_tpt_table(struct iwl_lq_sta *lq_sta, - const struct rs_tx_column *column, - u32 bw) -{ - /* Used to choose among HT tables */ - const u16 (*ht_tbl_pointer)[IWL_RATE_COUNT]; - - if (WARN_ON_ONCE(column->mode != RS_LEGACY && - column->mode != RS_SISO && - column->mode != RS_MIMO2)) - return expected_tpt_legacy; - - /* Legacy rates have only one table */ - if (column->mode == RS_LEGACY) - return expected_tpt_legacy; - - ht_tbl_pointer = expected_tpt_mimo2_20MHz; - /* Choose among many HT tables depending on number of streams - * (SISO/MIMO2), channel width (20/40/80), SGI, and aggregation - * status */ - if (column->mode == RS_SISO) { - switch (bw) { - case RATE_MCS_CHAN_WIDTH_20: - ht_tbl_pointer = expected_tpt_siso_20MHz; - break; - case RATE_MCS_CHAN_WIDTH_40: - ht_tbl_pointer = expected_tpt_siso_40MHz; - break; - case RATE_MCS_CHAN_WIDTH_80: - ht_tbl_pointer = expected_tpt_siso_80MHz; - break; - default: - WARN_ON_ONCE(1); - } - } else if (column->mode == RS_MIMO2) { - switch (bw) { - case RATE_MCS_CHAN_WIDTH_20: - ht_tbl_pointer = expected_tpt_mimo2_20MHz; - break; - case RATE_MCS_CHAN_WIDTH_40: - ht_tbl_pointer = expected_tpt_mimo2_40MHz; - break; - case RATE_MCS_CHAN_WIDTH_80: - ht_tbl_pointer = expected_tpt_mimo2_80MHz; - break; - default: - WARN_ON_ONCE(1); - } - } else { - WARN_ON_ONCE(1); - } - - if (!column->sgi && !lq_sta->is_agg) /* Normal */ - return ht_tbl_pointer[0]; - else if (column->sgi && !lq_sta->is_agg) /* SGI */ - return ht_tbl_pointer[1]; - else if (!column->sgi && lq_sta->is_agg) /* AGG */ - return ht_tbl_pointer[2]; - else /* AGG+SGI */ - return ht_tbl_pointer[3]; -} - -static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl) -{ - struct rs_rate *rate = &tbl->rate; - const struct rs_tx_column *column = &rs_tx_columns[tbl->column]; - - tbl->expected_tpt = rs_get_expected_tpt_table(lq_sta, column, rate->bw); -} - -static s32 rs_get_best_rate(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl, /* "search" */ - unsigned long rate_mask, s8 index) -{ - struct iwl_scale_tbl_info *active_tbl = - &(lq_sta->lq_info[lq_sta->active_tbl]); - s32 success_ratio = active_tbl->win[index].success_ratio; - u16 expected_current_tpt = active_tbl->expected_tpt[index]; - const u16 *tpt_tbl = tbl->expected_tpt; - u16 high_low; - u32 target_tpt; - int rate_idx; - - if (success_ratio >= RS_PERCENT(IWL_MVM_RS_SR_NO_DECREASE)) { - target_tpt = 100 * expected_current_tpt; - IWL_DEBUG_RATE(mvm, - "SR %d high. Find rate exceeding EXPECTED_CURRENT %d\n", - success_ratio, target_tpt); - } else { - target_tpt = lq_sta->last_tpt; - IWL_DEBUG_RATE(mvm, - "SR %d not that good. Find rate exceeding ACTUAL_TPT %d\n", - success_ratio, target_tpt); - } - - rate_idx = find_first_bit(&rate_mask, BITS_PER_LONG); - - while (rate_idx != IWL_RATE_INVALID) { - if (target_tpt < (100 * tpt_tbl[rate_idx])) - break; - - high_low = rs_get_adjacent_rate(mvm, rate_idx, rate_mask, - tbl->rate.type); - - rate_idx = (high_low >> 8) & 0xff; - } - - IWL_DEBUG_RATE(mvm, "Best rate found %d target_tp %d expected_new %d\n", - rate_idx, target_tpt, - rate_idx != IWL_RATE_INVALID ? - 100 * tpt_tbl[rate_idx] : IWL_INVALID_VALUE); - - return rate_idx; -} - -static u32 rs_bw_from_sta_bw(struct ieee80211_sta *sta) -{ - if (sta->bandwidth >= IEEE80211_STA_RX_BW_80) - return RATE_MCS_CHAN_WIDTH_80; - else if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) - return RATE_MCS_CHAN_WIDTH_40; - - return RATE_MCS_CHAN_WIDTH_20; -} - -/* - * Check whether we should continue using same modulation mode, or - * begin search for a new mode, based on: - * 1) # tx successes or failures while using this mode - * 2) # times calling this function - * 3) elapsed time in this mode (not used, for now) - */ -static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) -{ - struct iwl_scale_tbl_info *tbl; - int active_tbl; - int flush_interval_passed = 0; - struct iwl_mvm *mvm; - - mvm = lq_sta->pers.drv; - active_tbl = lq_sta->active_tbl; - - tbl = &(lq_sta->lq_info[active_tbl]); - - /* If we've been disallowing search, see if we should now allow it */ - if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) { - /* Elapsed time using current modulation mode */ - if (lq_sta->flush_timer) - flush_interval_passed = - time_after(jiffies, - (unsigned long)(lq_sta->flush_timer + - (IWL_MVM_RS_STAY_IN_COLUMN_TIMEOUT * HZ))); - - /* - * Check if we should allow search for new modulation mode. - * If many frames have failed or succeeded, or we've used - * this same modulation for a long time, allow search, and - * reset history stats that keep track of whether we should - * allow a new search. Also (below) reset all bitmaps and - * stats in active history. - */ - if (force_search || - (lq_sta->total_failed > lq_sta->max_failure_limit) || - (lq_sta->total_success > lq_sta->max_success_limit) || - ((!lq_sta->search_better_tbl) && - (lq_sta->flush_timer) && (flush_interval_passed))) { - IWL_DEBUG_RATE(mvm, - "LQ: stay is expired %d %d %d\n", - lq_sta->total_failed, - lq_sta->total_success, - flush_interval_passed); - - /* Allow search for new mode */ - lq_sta->rs_state = RS_STATE_SEARCH_CYCLE_STARTED; - IWL_DEBUG_RATE(mvm, - "Moving to RS_STATE_SEARCH_CYCLE_STARTED\n"); - lq_sta->total_failed = 0; - lq_sta->total_success = 0; - lq_sta->flush_timer = 0; - /* mark the current column as visited */ - lq_sta->visited_columns = BIT(tbl->column); - /* - * Else if we've used this modulation mode enough repetitions - * (regardless of elapsed time or success/failure), reset - * history bitmaps and rate-specific stats for all rates in - * active table. - */ - } else { - lq_sta->table_count++; - if (lq_sta->table_count >= - lq_sta->table_count_limit) { - lq_sta->table_count = 0; - - IWL_DEBUG_RATE(mvm, - "LQ: stay in table clear win\n"); - rs_rate_scale_clear_tbl_windows(mvm, tbl); - } - } - - /* If transitioning to allow "search", reset all history - * bitmaps and stats in active table (this will become the new - * "search" table). */ - if (lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_STARTED) { - rs_rate_scale_clear_tbl_windows(mvm, tbl); - } - } -} - -/* - * setup rate table in uCode - */ -static void rs_update_rate_tbl(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl) -{ - rs_fill_lq_cmd(mvm, sta, lq_sta, &tbl->rate); - iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false); -} - -static bool rs_tweak_rate_tbl(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl, - enum rs_action scale_action) -{ - if (sta->bandwidth != IEEE80211_STA_RX_BW_80) - return false; - - if (!is_vht_siso(&tbl->rate)) - return false; - - if ((tbl->rate.bw == RATE_MCS_CHAN_WIDTH_80) && - (tbl->rate.index == IWL_RATE_MCS_0_INDEX) && - (scale_action == RS_ACTION_DOWNSCALE)) { - tbl->rate.bw = RATE_MCS_CHAN_WIDTH_20; - tbl->rate.index = IWL_RATE_MCS_4_INDEX; - IWL_DEBUG_RATE(mvm, "Switch 80Mhz SISO MCS0 -> 20Mhz MCS4\n"); - goto tweaked; - } - - /* Go back to 80Mhz MCS1 only if we've established that 20Mhz MCS5 is - * sustainable, i.e. we're past the test window. We can't go back - * if MCS5 is just tested as this will happen always after switching - * to 20Mhz MCS4 because the rate stats are cleared. - */ - if ((tbl->rate.bw == RATE_MCS_CHAN_WIDTH_20) && - (((tbl->rate.index == IWL_RATE_MCS_5_INDEX) && - (scale_action == RS_ACTION_STAY)) || - ((tbl->rate.index > IWL_RATE_MCS_5_INDEX) && - (scale_action == RS_ACTION_UPSCALE)))) { - tbl->rate.bw = RATE_MCS_CHAN_WIDTH_80; - tbl->rate.index = IWL_RATE_MCS_1_INDEX; - IWL_DEBUG_RATE(mvm, "Switch 20Mhz SISO MCS5 -> 80Mhz MCS1\n"); - goto tweaked; - } - - return false; - -tweaked: - rs_set_expected_tpt_table(lq_sta, tbl); - rs_rate_scale_clear_tbl_windows(mvm, tbl); - return true; -} - -static enum rs_column rs_get_next_column(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct ieee80211_sta *sta, - struct iwl_scale_tbl_info *tbl) -{ - int i, j, max_rate; - enum rs_column next_col_id; - const struct rs_tx_column *curr_col = &rs_tx_columns[tbl->column]; - const struct rs_tx_column *next_col; - allow_column_func_t allow_func; - u8 valid_ants = iwl_mvm_get_valid_tx_ant(mvm); - const u16 *expected_tpt_tbl; - u16 tpt, max_expected_tpt; - - for (i = 0; i < MAX_NEXT_COLUMNS; i++) { - next_col_id = curr_col->next_columns[i]; - - if (next_col_id == RS_COLUMN_INVALID) - continue; - - if (lq_sta->visited_columns & BIT(next_col_id)) { - IWL_DEBUG_RATE(mvm, "Skip already visited column %d\n", - next_col_id); - continue; - } - - next_col = &rs_tx_columns[next_col_id]; - - if (!rs_is_valid_ant(valid_ants, next_col->ant)) { - IWL_DEBUG_RATE(mvm, - "Skip column %d as ANT config isn't supported by chip. valid_ants 0x%x column ant 0x%x\n", - next_col_id, valid_ants, next_col->ant); - continue; - } - - for (j = 0; j < MAX_COLUMN_CHECKS; j++) { - allow_func = next_col->checks[j]; - if (allow_func && !allow_func(mvm, sta, &tbl->rate, - next_col)) - break; - } - - if (j != MAX_COLUMN_CHECKS) { - IWL_DEBUG_RATE(mvm, - "Skip column %d: not allowed (check %d failed)\n", - next_col_id, j); - - continue; - } - - tpt = lq_sta->last_tpt / 100; - expected_tpt_tbl = rs_get_expected_tpt_table(lq_sta, next_col, - rs_bw_from_sta_bw(sta)); - if (WARN_ON_ONCE(!expected_tpt_tbl)) - continue; - - max_rate = rs_get_max_allowed_rate(lq_sta, next_col); - if (max_rate == IWL_RATE_INVALID) { - IWL_DEBUG_RATE(mvm, - "Skip column %d: no rate is allowed in this column\n", - next_col_id); - continue; - } - - max_expected_tpt = expected_tpt_tbl[max_rate]; - if (tpt >= max_expected_tpt) { - IWL_DEBUG_RATE(mvm, - "Skip column %d: can't beat current TPT. Max expected %d current %d\n", - next_col_id, max_expected_tpt, tpt); - continue; - } - - IWL_DEBUG_RATE(mvm, - "Found potential column %d. Max expected %d current %d\n", - next_col_id, max_expected_tpt, tpt); - break; - } - - if (i == MAX_NEXT_COLUMNS) - return RS_COLUMN_INVALID; - - return next_col_id; -} - -static int rs_switch_to_column(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct ieee80211_sta *sta, - enum rs_column col_id) -{ - struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - struct iwl_scale_tbl_info *search_tbl = - &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - struct rs_rate *rate = &search_tbl->rate; - const struct rs_tx_column *column = &rs_tx_columns[col_id]; - const struct rs_tx_column *curr_column = &rs_tx_columns[tbl->column]; - u32 sz = (sizeof(struct iwl_scale_tbl_info) - - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); - unsigned long rate_mask = 0; - u32 rate_idx = 0; - - memcpy(search_tbl, tbl, sz); - - rate->sgi = column->sgi; - rate->ant = column->ant; - - if (column->mode == RS_LEGACY) { - if (lq_sta->band == IEEE80211_BAND_5GHZ) - rate->type = LQ_LEGACY_A; - else - rate->type = LQ_LEGACY_G; - - rate->bw = RATE_MCS_CHAN_WIDTH_20; - rate->ldpc = false; - rate_mask = lq_sta->active_legacy_rate; - } else if (column->mode == RS_SISO) { - rate->type = lq_sta->is_vht ? LQ_VHT_SISO : LQ_HT_SISO; - rate_mask = lq_sta->active_siso_rate; - } else if (column->mode == RS_MIMO2) { - rate->type = lq_sta->is_vht ? LQ_VHT_MIMO2 : LQ_HT_MIMO2; - rate_mask = lq_sta->active_mimo2_rate; - } else { - WARN_ON_ONCE("Bad column mode"); - } - - if (column->mode != RS_LEGACY) { - rate->bw = rs_bw_from_sta_bw(sta); - rate->ldpc = lq_sta->ldpc; - } - - search_tbl->column = col_id; - rs_set_expected_tpt_table(lq_sta, search_tbl); - - lq_sta->visited_columns |= BIT(col_id); - - /* Get the best matching rate if we're changing modes. e.g. - * SISO->MIMO, LEGACY->SISO, MIMO->SISO - */ - if (curr_column->mode != column->mode) { - rate_idx = rs_get_best_rate(mvm, lq_sta, search_tbl, - rate_mask, rate->index); - - if ((rate_idx == IWL_RATE_INVALID) || - !(BIT(rate_idx) & rate_mask)) { - IWL_DEBUG_RATE(mvm, - "can not switch with index %d" - " rate mask %lx\n", - rate_idx, rate_mask); - - goto err; - } - - rate->index = rate_idx; - } - - IWL_DEBUG_RATE(mvm, "Switched to column %d: Index %d\n", - col_id, rate->index); - - return 0; - -err: - rate->type = LQ_NONE; - return -1; -} - -static enum rs_action rs_get_rate_action(struct iwl_mvm *mvm, - struct iwl_scale_tbl_info *tbl, - s32 sr, int low, int high, - int current_tpt, - int low_tpt, int high_tpt) -{ - enum rs_action action = RS_ACTION_STAY; - - if ((sr <= RS_PERCENT(IWL_MVM_RS_SR_FORCE_DECREASE)) || - (current_tpt == 0)) { - IWL_DEBUG_RATE(mvm, - "Decrease rate because of low SR\n"); - return RS_ACTION_DOWNSCALE; - } - - if ((low_tpt == IWL_INVALID_VALUE) && - (high_tpt == IWL_INVALID_VALUE) && - (high != IWL_RATE_INVALID)) { - IWL_DEBUG_RATE(mvm, - "No data about high/low rates. Increase rate\n"); - return RS_ACTION_UPSCALE; - } - - if ((high_tpt == IWL_INVALID_VALUE) && - (high != IWL_RATE_INVALID) && - (low_tpt != IWL_INVALID_VALUE) && - (low_tpt < current_tpt)) { - IWL_DEBUG_RATE(mvm, - "No data about high rate and low rate is worse. Increase rate\n"); - return RS_ACTION_UPSCALE; - } - - if ((high_tpt != IWL_INVALID_VALUE) && - (high_tpt > current_tpt)) { - IWL_DEBUG_RATE(mvm, - "Higher rate is better. Increate rate\n"); - return RS_ACTION_UPSCALE; - } - - if ((low_tpt != IWL_INVALID_VALUE) && - (high_tpt != IWL_INVALID_VALUE) && - (low_tpt < current_tpt) && - (high_tpt < current_tpt)) { - IWL_DEBUG_RATE(mvm, - "Both high and low are worse. Maintain rate\n"); - return RS_ACTION_STAY; - } - - if ((low_tpt != IWL_INVALID_VALUE) && - (low_tpt > current_tpt)) { - IWL_DEBUG_RATE(mvm, - "Lower rate is better\n"); - action = RS_ACTION_DOWNSCALE; - goto out; - } - - if ((low_tpt == IWL_INVALID_VALUE) && - (low != IWL_RATE_INVALID)) { - IWL_DEBUG_RATE(mvm, - "No data about lower rate\n"); - action = RS_ACTION_DOWNSCALE; - goto out; - } - - IWL_DEBUG_RATE(mvm, "Maintain rate\n"); - -out: - if ((action == RS_ACTION_DOWNSCALE) && (low != IWL_RATE_INVALID)) { - if (sr >= RS_PERCENT(IWL_MVM_RS_SR_NO_DECREASE)) { - IWL_DEBUG_RATE(mvm, - "SR is above NO DECREASE. Avoid downscale\n"); - action = RS_ACTION_STAY; - } else if (current_tpt > (100 * tbl->expected_tpt[low])) { - IWL_DEBUG_RATE(mvm, - "Current TPT is higher than max expected in low rate. Avoid downscale\n"); - action = RS_ACTION_STAY; - } else { - IWL_DEBUG_RATE(mvm, "Decrease rate\n"); - } - } - - return action; -} - -static bool rs_stbc_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta) -{ - /* Our chip supports Tx STBC and the peer is an HT/VHT STA which - * supports STBC of at least 1*SS - */ - if (!lq_sta->stbc_capable) - return false; - - if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) - return false; - - return true; -} - -static void rs_get_adjacent_txp(struct iwl_mvm *mvm, int index, - int *weaker, int *stronger) -{ - *weaker = index + IWL_MVM_RS_TPC_TX_POWER_STEP; - if (*weaker > TPC_MAX_REDUCTION) - *weaker = TPC_INVALID; - - *stronger = index - IWL_MVM_RS_TPC_TX_POWER_STEP; - if (*stronger < 0) - *stronger = TPC_INVALID; -} - -static bool rs_tpc_allowed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct rs_rate *rate, enum ieee80211_band band) -{ - int index = rate->index; - bool cam = (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM); - bool sta_ps_disabled = (vif->type == NL80211_IFTYPE_STATION && - !vif->bss_conf.ps); - - IWL_DEBUG_RATE(mvm, "cam: %d sta_ps_disabled %d\n", - cam, sta_ps_disabled); - /* - * allow tpc only if power management is enabled, or bt coex - * activity grade allows it and we are on 2.4Ghz. - */ - if ((cam || sta_ps_disabled) && - !iwl_mvm_bt_coex_is_tpc_allowed(mvm, band)) - return false; - - IWL_DEBUG_RATE(mvm, "check rate, table type: %d\n", rate->type); - if (is_legacy(rate)) - return index == IWL_RATE_54M_INDEX; - if (is_ht(rate)) - return index == IWL_RATE_MCS_7_INDEX; - if (is_vht(rate)) - return index == IWL_RATE_MCS_7_INDEX || - index == IWL_RATE_MCS_8_INDEX || - index == IWL_RATE_MCS_9_INDEX; - - WARN_ON_ONCE(1); - return false; -} - -enum tpc_action { - TPC_ACTION_STAY, - TPC_ACTION_DECREASE, - TPC_ACTION_INCREASE, - TPC_ACTION_NO_RESTIRCTION, -}; - -static enum tpc_action rs_get_tpc_action(struct iwl_mvm *mvm, - s32 sr, int weak, int strong, - int current_tpt, - int weak_tpt, int strong_tpt) -{ - /* stay until we have valid tpt */ - if (current_tpt == IWL_INVALID_VALUE) { - IWL_DEBUG_RATE(mvm, "no current tpt. stay.\n"); - return TPC_ACTION_STAY; - } - - /* Too many failures, increase txp */ - if (sr <= RS_PERCENT(IWL_MVM_RS_TPC_SR_FORCE_INCREASE) || - current_tpt == 0) { - IWL_DEBUG_RATE(mvm, "increase txp because of weak SR\n"); - return TPC_ACTION_NO_RESTIRCTION; - } - - /* try decreasing first if applicable */ - if (weak != TPC_INVALID) { - if (weak_tpt == IWL_INVALID_VALUE && - (strong_tpt == IWL_INVALID_VALUE || - current_tpt >= strong_tpt)) { - IWL_DEBUG_RATE(mvm, - "no weak txp measurement. decrease txp\n"); - return TPC_ACTION_DECREASE; - } - - if (weak_tpt > current_tpt) { - IWL_DEBUG_RATE(mvm, - "lower txp has better tpt. decrease txp\n"); - return TPC_ACTION_DECREASE; - } - } - - /* next, increase if needed */ - if (sr < RS_PERCENT(IWL_MVM_RS_TPC_SR_NO_INCREASE) && - strong != TPC_INVALID) { - if (weak_tpt == IWL_INVALID_VALUE && - strong_tpt != IWL_INVALID_VALUE && - current_tpt < strong_tpt) { - IWL_DEBUG_RATE(mvm, - "higher txp has better tpt. increase txp\n"); - return TPC_ACTION_INCREASE; - } - - if (weak_tpt < current_tpt && - (strong_tpt == IWL_INVALID_VALUE || - strong_tpt > current_tpt)) { - IWL_DEBUG_RATE(mvm, - "lower txp has worse tpt. increase txp\n"); - return TPC_ACTION_INCREASE; - } - } - - IWL_DEBUG_RATE(mvm, "no need to increase or decrease txp - stay\n"); - return TPC_ACTION_STAY; -} - -static bool rs_tpc_perform(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl) -{ - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - struct ieee80211_vif *vif = mvm_sta->vif; - struct ieee80211_chanctx_conf *chanctx_conf; - enum ieee80211_band band; - struct iwl_rate_scale_data *window; - struct rs_rate *rate = &tbl->rate; - enum tpc_action action; - s32 sr; - u8 cur = lq_sta->lq.reduced_tpc; - int current_tpt; - int weak, strong; - int weak_tpt = IWL_INVALID_VALUE, strong_tpt = IWL_INVALID_VALUE; - -#ifdef CONFIG_MAC80211_DEBUGFS - if (lq_sta->pers.dbg_fixed_txp_reduction <= TPC_MAX_REDUCTION) { - IWL_DEBUG_RATE(mvm, "fixed tpc: %d\n", - lq_sta->pers.dbg_fixed_txp_reduction); - lq_sta->lq.reduced_tpc = lq_sta->pers.dbg_fixed_txp_reduction; - return cur != lq_sta->pers.dbg_fixed_txp_reduction; - } -#endif - - rcu_read_lock(); - chanctx_conf = rcu_dereference(vif->chanctx_conf); - if (WARN_ON(!chanctx_conf)) - band = IEEE80211_NUM_BANDS; - else - band = chanctx_conf->def.chan->band; - rcu_read_unlock(); - - if (!rs_tpc_allowed(mvm, vif, rate, band)) { - IWL_DEBUG_RATE(mvm, - "tpc is not allowed. remove txp restrictions\n"); - lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION; - return cur != TPC_NO_REDUCTION; - } - - rs_get_adjacent_txp(mvm, cur, &weak, &strong); - - /* Collect measured throughputs for current and adjacent rates */ - window = tbl->tpc_win; - sr = window[cur].success_ratio; - current_tpt = window[cur].average_tpt; - if (weak != TPC_INVALID) - weak_tpt = window[weak].average_tpt; - if (strong != TPC_INVALID) - strong_tpt = window[strong].average_tpt; - - IWL_DEBUG_RATE(mvm, - "(TPC: %d): cur_tpt %d SR %d weak %d strong %d weak_tpt %d strong_tpt %d\n", - cur, current_tpt, sr, weak, strong, - weak_tpt, strong_tpt); - - action = rs_get_tpc_action(mvm, sr, weak, strong, - current_tpt, weak_tpt, strong_tpt); - - /* override actions if we are on the edge */ - if (weak == TPC_INVALID && action == TPC_ACTION_DECREASE) { - IWL_DEBUG_RATE(mvm, "already in lowest txp, stay\n"); - action = TPC_ACTION_STAY; - } else if (strong == TPC_INVALID && - (action == TPC_ACTION_INCREASE || - action == TPC_ACTION_NO_RESTIRCTION)) { - IWL_DEBUG_RATE(mvm, "already in highest txp, stay\n"); - action = TPC_ACTION_STAY; - } - - switch (action) { - case TPC_ACTION_DECREASE: - lq_sta->lq.reduced_tpc = weak; - return true; - case TPC_ACTION_INCREASE: - lq_sta->lq.reduced_tpc = strong; - return true; - case TPC_ACTION_NO_RESTIRCTION: - lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION; - return true; - case TPC_ACTION_STAY: - /* do nothing */ - break; - } - return false; -} - -/* - * Do rate scaling and search for new modulation mode. - */ -static void rs_rate_scale_perform(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, - int tid) -{ - int low = IWL_RATE_INVALID; - int high = IWL_RATE_INVALID; - int index; - struct iwl_rate_scale_data *window = NULL; - int current_tpt = IWL_INVALID_VALUE; - int low_tpt = IWL_INVALID_VALUE; - int high_tpt = IWL_INVALID_VALUE; - u32 fail_count; - enum rs_action scale_action = RS_ACTION_STAY; - u16 rate_mask; - u8 update_lq = 0; - struct iwl_scale_tbl_info *tbl, *tbl1; - u8 active_tbl = 0; - u8 done_search = 0; - u16 high_low; - s32 sr; - u8 prev_agg = lq_sta->is_agg; - struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_tid_data *tid_data; - struct rs_rate *rate; - - lq_sta->is_agg = !!sta_priv->agg_tids; - - /* - * Select rate-scale / modulation-mode table to work with in - * the rest of this function: "search" if searching for better - * modulation mode, or "active" if doing rate scaling within a mode. - */ - if (!lq_sta->search_better_tbl) - active_tbl = lq_sta->active_tbl; - else - active_tbl = 1 - lq_sta->active_tbl; - - tbl = &(lq_sta->lq_info[active_tbl]); - rate = &tbl->rate; - - if (prev_agg != lq_sta->is_agg) { - IWL_DEBUG_RATE(mvm, - "Aggregation changed: prev %d current %d. Update expected TPT table\n", - prev_agg, lq_sta->is_agg); - rs_set_expected_tpt_table(lq_sta, tbl); - rs_rate_scale_clear_tbl_windows(mvm, tbl); - } - - /* current tx rate */ - index = rate->index; - - /* rates available for this association, and for modulation mode */ - rate_mask = rs_get_supported_rates(lq_sta, rate); - - if (!(BIT(index) & rate_mask)) { - IWL_ERR(mvm, "Current Rate is not valid\n"); - if (lq_sta->search_better_tbl) { - /* revert to active table if search table is not valid*/ - rate->type = LQ_NONE; - lq_sta->search_better_tbl = 0; - tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - rs_update_rate_tbl(mvm, sta, lq_sta, tbl); - } - return; - } - - /* Get expected throughput table and history window for current rate */ - if (!tbl->expected_tpt) { - IWL_ERR(mvm, "tbl->expected_tpt is NULL\n"); - return; - } - - /* TODO: handle rate_idx_mask and rate_idx_mcs_mask */ - window = &(tbl->win[index]); - - /* - * If there is not enough history to calculate actual average - * throughput, keep analyzing results of more tx frames, without - * changing rate or mode (bypass most of the rest of this function). - * Set up new rate table in uCode only if old rate is not supported - * in current association (use new rate found above). - */ - fail_count = window->counter - window->success_counter; - if ((fail_count < IWL_MVM_RS_RATE_MIN_FAILURE_TH) && - (window->success_counter < IWL_MVM_RS_RATE_MIN_SUCCESS_TH)) { - IWL_DEBUG_RATE(mvm, - "%s: Test Window: succ %d total %d\n", - rs_pretty_rate(rate), - window->success_counter, window->counter); - - /* Can't calculate this yet; not enough history */ - window->average_tpt = IWL_INVALID_VALUE; - - /* Should we stay with this modulation mode, - * or search for a new one? */ - rs_stay_in_table(lq_sta, false); - - return; - } - - /* If we are searching for better modulation mode, check success. */ - if (lq_sta->search_better_tbl) { - /* If good success, continue using the "search" mode; - * no need to send new link quality command, since we're - * continuing to use the setup that we've been trying. */ - if (window->average_tpt > lq_sta->last_tpt) { - IWL_DEBUG_RATE(mvm, - "SWITCHING TO NEW TABLE SR: %d " - "cur-tpt %d old-tpt %d\n", - window->success_ratio, - window->average_tpt, - lq_sta->last_tpt); - - /* Swap tables; "search" becomes "active" */ - lq_sta->active_tbl = active_tbl; - current_tpt = window->average_tpt; - /* Else poor success; go back to mode in "active" table */ - } else { - IWL_DEBUG_RATE(mvm, - "GOING BACK TO THE OLD TABLE: SR %d " - "cur-tpt %d old-tpt %d\n", - window->success_ratio, - window->average_tpt, - lq_sta->last_tpt); - - /* Nullify "search" table */ - rate->type = LQ_NONE; - - /* Revert to "active" table */ - active_tbl = lq_sta->active_tbl; - tbl = &(lq_sta->lq_info[active_tbl]); - - /* Revert to "active" rate and throughput info */ - index = tbl->rate.index; - current_tpt = lq_sta->last_tpt; - - /* Need to set up a new rate table in uCode */ - update_lq = 1; - } - - /* Either way, we've made a decision; modulation mode - * search is done, allow rate adjustment next time. */ - lq_sta->search_better_tbl = 0; - done_search = 1; /* Don't switch modes below! */ - goto lq_update; - } - - /* (Else) not in search of better modulation mode, try for better - * starting rate, while staying in this mode. */ - high_low = rs_get_adjacent_rate(mvm, index, rate_mask, rate->type); - low = high_low & 0xff; - high = (high_low >> 8) & 0xff; - - /* TODO: handle rate_idx_mask and rate_idx_mcs_mask */ - - sr = window->success_ratio; - - /* Collect measured throughputs for current and adjacent rates */ - current_tpt = window->average_tpt; - if (low != IWL_RATE_INVALID) - low_tpt = tbl->win[low].average_tpt; - if (high != IWL_RATE_INVALID) - high_tpt = tbl->win[high].average_tpt; - - IWL_DEBUG_RATE(mvm, - "%s: cur_tpt %d SR %d low %d high %d low_tpt %d high_tpt %d\n", - rs_pretty_rate(rate), current_tpt, sr, - low, high, low_tpt, high_tpt); - - scale_action = rs_get_rate_action(mvm, tbl, sr, low, high, - current_tpt, low_tpt, high_tpt); - - /* Force a search in case BT doesn't like us being in MIMO */ - if (is_mimo(rate) && - !iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) { - IWL_DEBUG_RATE(mvm, - "BT Coex forbids MIMO. Search for new config\n"); - rs_stay_in_table(lq_sta, true); - goto lq_update; - } - - switch (scale_action) { - case RS_ACTION_DOWNSCALE: - /* Decrease starting rate, update uCode's rate table */ - if (low != IWL_RATE_INVALID) { - update_lq = 1; - index = low; - } else { - IWL_DEBUG_RATE(mvm, - "At the bottom rate. Can't decrease\n"); - } - - break; - case RS_ACTION_UPSCALE: - /* Increase starting rate, update uCode's rate table */ - if (high != IWL_RATE_INVALID) { - update_lq = 1; - index = high; - } else { - IWL_DEBUG_RATE(mvm, - "At the top rate. Can't increase\n"); - } - - break; - case RS_ACTION_STAY: - /* No change */ - if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) - update_lq = rs_tpc_perform(mvm, sta, lq_sta, tbl); - break; - default: - break; - } - -lq_update: - /* Replace uCode's rate table for the destination station. */ - if (update_lq) { - tbl->rate.index = index; - if (IWL_MVM_RS_80_20_FAR_RANGE_TWEAK) - rs_tweak_rate_tbl(mvm, sta, lq_sta, tbl, scale_action); - rs_update_rate_tbl(mvm, sta, lq_sta, tbl); - } - - rs_stay_in_table(lq_sta, false); - - /* - * Search for new modulation mode if we're: - * 1) Not changing rates right now - * 2) Not just finishing up a search - * 3) Allowing a new search - */ - if (!update_lq && !done_search && - lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_STARTED - && window->counter) { - enum rs_column next_column; - - /* Save current throughput to compare with "search" throughput*/ - lq_sta->last_tpt = current_tpt; - - IWL_DEBUG_RATE(mvm, - "Start Search: update_lq %d done_search %d rs_state %d win->counter %d\n", - update_lq, done_search, lq_sta->rs_state, - window->counter); - - next_column = rs_get_next_column(mvm, lq_sta, sta, tbl); - if (next_column != RS_COLUMN_INVALID) { - int ret = rs_switch_to_column(mvm, lq_sta, sta, - next_column); - if (!ret) - lq_sta->search_better_tbl = 1; - } else { - IWL_DEBUG_RATE(mvm, - "No more columns to explore in search cycle. Go to RS_STATE_SEARCH_CYCLE_ENDED\n"); - lq_sta->rs_state = RS_STATE_SEARCH_CYCLE_ENDED; - } - - /* If new "search" mode was selected, set up in uCode table */ - if (lq_sta->search_better_tbl) { - /* Access the "search" table, clear its history. */ - tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - rs_rate_scale_clear_tbl_windows(mvm, tbl); - - /* Use new "search" start rate */ - index = tbl->rate.index; - - rs_dump_rate(mvm, &tbl->rate, - "Switch to SEARCH TABLE:"); - rs_update_rate_tbl(mvm, sta, lq_sta, tbl); - } else { - done_search = 1; - } - } - - if (done_search && lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_ENDED) { - /* If the "active" (non-search) mode was legacy, - * and we've tried switching antennas, - * but we haven't been able to try HT modes (not available), - * stay with best antenna legacy modulation for a while - * before next round of mode comparisons. */ - tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]); - if (is_legacy(&tbl1->rate)) { - IWL_DEBUG_RATE(mvm, "LQ: STAY in legacy table\n"); - - if (tid != IWL_MAX_TID_COUNT) { - tid_data = &sta_priv->tid_data[tid]; - if (tid_data->state != IWL_AGG_OFF) { - IWL_DEBUG_RATE(mvm, - "Stop aggregation on tid %d\n", - tid); - ieee80211_stop_tx_ba_session(sta, tid); - } - } - rs_set_stay_in_table(mvm, 1, lq_sta); - } else { - /* If we're in an HT mode, and all 3 mode switch actions - * have been tried and compared, stay in this best modulation - * mode for a while before next round of mode comparisons. */ - if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) && - (lq_sta->tx_agg_tid_en & (1 << tid)) && - (tid != IWL_MAX_TID_COUNT)) { - tid_data = &sta_priv->tid_data[tid]; - if (tid_data->state == IWL_AGG_OFF) { - IWL_DEBUG_RATE(mvm, - "try to aggregate tid %d\n", - tid); - rs_tl_turn_on_agg(mvm, tid, - lq_sta, sta); - } - } - rs_set_stay_in_table(mvm, 0, lq_sta); - } - } -} - -struct rs_init_rate_info { - s8 rssi; - u8 rate_idx; -}; - -static const struct rs_init_rate_info rs_optimal_rates_24ghz_legacy[] = { - { -60, IWL_RATE_54M_INDEX }, - { -64, IWL_RATE_48M_INDEX }, - { -68, IWL_RATE_36M_INDEX }, - { -80, IWL_RATE_24M_INDEX }, - { -84, IWL_RATE_18M_INDEX }, - { -85, IWL_RATE_12M_INDEX }, - { -86, IWL_RATE_11M_INDEX }, - { -88, IWL_RATE_5M_INDEX }, - { -90, IWL_RATE_2M_INDEX }, - { S8_MIN, IWL_RATE_1M_INDEX }, -}; - -static const struct rs_init_rate_info rs_optimal_rates_5ghz_legacy[] = { - { -60, IWL_RATE_54M_INDEX }, - { -64, IWL_RATE_48M_INDEX }, - { -72, IWL_RATE_36M_INDEX }, - { -80, IWL_RATE_24M_INDEX }, - { -84, IWL_RATE_18M_INDEX }, - { -85, IWL_RATE_12M_INDEX }, - { -87, IWL_RATE_9M_INDEX }, - { S8_MIN, IWL_RATE_6M_INDEX }, -}; - -static const struct rs_init_rate_info rs_optimal_rates_ht[] = { - { -60, IWL_RATE_MCS_7_INDEX }, - { -64, IWL_RATE_MCS_6_INDEX }, - { -68, IWL_RATE_MCS_5_INDEX }, - { -72, IWL_RATE_MCS_4_INDEX }, - { -80, IWL_RATE_MCS_3_INDEX }, - { -84, IWL_RATE_MCS_2_INDEX }, - { -85, IWL_RATE_MCS_1_INDEX }, - { S8_MIN, IWL_RATE_MCS_0_INDEX}, -}; - -static const struct rs_init_rate_info rs_optimal_rates_vht_20mhz[] = { - { -60, IWL_RATE_MCS_8_INDEX }, - { -64, IWL_RATE_MCS_7_INDEX }, - { -68, IWL_RATE_MCS_6_INDEX }, - { -72, IWL_RATE_MCS_5_INDEX }, - { -80, IWL_RATE_MCS_4_INDEX }, - { -84, IWL_RATE_MCS_3_INDEX }, - { -85, IWL_RATE_MCS_2_INDEX }, - { -87, IWL_RATE_MCS_1_INDEX }, - { S8_MIN, IWL_RATE_MCS_0_INDEX}, -}; - -static const struct rs_init_rate_info rs_optimal_rates_vht_40_80mhz[] = { - { -60, IWL_RATE_MCS_9_INDEX }, - { -64, IWL_RATE_MCS_8_INDEX }, - { -68, IWL_RATE_MCS_7_INDEX }, - { -72, IWL_RATE_MCS_6_INDEX }, - { -80, IWL_RATE_MCS_5_INDEX }, - { -84, IWL_RATE_MCS_4_INDEX }, - { -85, IWL_RATE_MCS_3_INDEX }, - { -87, IWL_RATE_MCS_2_INDEX }, - { -88, IWL_RATE_MCS_1_INDEX }, - { S8_MIN, IWL_RATE_MCS_0_INDEX }, -}; - -/* Init the optimal rate based on STA caps - * This combined with rssi is used to report the last tx rate - * to userspace when we haven't transmitted enough frames. - */ -static void rs_init_optimal_rate(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta) -{ - struct rs_rate *rate = &lq_sta->optimal_rate; - - if (lq_sta->max_mimo2_rate_idx != IWL_RATE_INVALID) - rate->type = lq_sta->is_vht ? LQ_VHT_MIMO2 : LQ_HT_MIMO2; - else if (lq_sta->max_siso_rate_idx != IWL_RATE_INVALID) - rate->type = lq_sta->is_vht ? LQ_VHT_SISO : LQ_HT_SISO; - else if (lq_sta->band == IEEE80211_BAND_5GHZ) - rate->type = LQ_LEGACY_A; - else - rate->type = LQ_LEGACY_G; - - rate->bw = rs_bw_from_sta_bw(sta); - rate->sgi = rs_sgi_allow(mvm, sta, rate, NULL); - - /* ANT/LDPC/STBC aren't relevant for the rate reported to userspace */ - - if (is_mimo(rate)) { - lq_sta->optimal_rate_mask = lq_sta->active_mimo2_rate; - } else if (is_siso(rate)) { - lq_sta->optimal_rate_mask = lq_sta->active_siso_rate; - } else { - lq_sta->optimal_rate_mask = lq_sta->active_legacy_rate; - - if (lq_sta->band == IEEE80211_BAND_5GHZ) { - lq_sta->optimal_rates = rs_optimal_rates_5ghz_legacy; - lq_sta->optimal_nentries = - ARRAY_SIZE(rs_optimal_rates_5ghz_legacy); - } else { - lq_sta->optimal_rates = rs_optimal_rates_24ghz_legacy; - lq_sta->optimal_nentries = - ARRAY_SIZE(rs_optimal_rates_24ghz_legacy); - } - } - - if (is_vht(rate)) { - if (rate->bw == RATE_MCS_CHAN_WIDTH_20) { - lq_sta->optimal_rates = rs_optimal_rates_vht_20mhz; - lq_sta->optimal_nentries = - ARRAY_SIZE(rs_optimal_rates_vht_20mhz); - } else { - lq_sta->optimal_rates = rs_optimal_rates_vht_40_80mhz; - lq_sta->optimal_nentries = - ARRAY_SIZE(rs_optimal_rates_vht_40_80mhz); - } - } else if (is_ht(rate)) { - lq_sta->optimal_rates = rs_optimal_rates_ht; - lq_sta->optimal_nentries = ARRAY_SIZE(rs_optimal_rates_ht); - } -} - -/* Compute the optimal rate index based on RSSI */ -static struct rs_rate *rs_get_optimal_rate(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta) -{ - struct rs_rate *rate = &lq_sta->optimal_rate; - int i; - - rate->index = find_first_bit(&lq_sta->optimal_rate_mask, - BITS_PER_LONG); - - for (i = 0; i < lq_sta->optimal_nentries; i++) { - int rate_idx = lq_sta->optimal_rates[i].rate_idx; - - if ((lq_sta->pers.last_rssi >= lq_sta->optimal_rates[i].rssi) && - (BIT(rate_idx) & lq_sta->optimal_rate_mask)) { - rate->index = rate_idx; - break; - } - } - - return rate; -} - -/* Choose an initial legacy rate and antenna to use based on the RSSI - * of last Rx - */ -static void rs_get_initial_rate(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - enum ieee80211_band band, - struct rs_rate *rate) -{ - int i, nentries; - s8 best_rssi = S8_MIN; - u8 best_ant = ANT_NONE; - u8 valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm); - const struct rs_init_rate_info *initial_rates; - - for (i = 0; i < ARRAY_SIZE(lq_sta->pers.chain_signal); i++) { - if (!(lq_sta->pers.chains & BIT(i))) - continue; - - if (lq_sta->pers.chain_signal[i] > best_rssi) { - best_rssi = lq_sta->pers.chain_signal[i]; - best_ant = BIT(i); - } - } - - IWL_DEBUG_RATE(mvm, "Best ANT: %s Best RSSI: %d\n", - rs_pretty_ant(best_ant), best_rssi); - - if (best_ant != ANT_A && best_ant != ANT_B) - rate->ant = first_antenna(valid_tx_ant); - else - rate->ant = best_ant; - - rate->sgi = false; - rate->ldpc = false; - rate->bw = RATE_MCS_CHAN_WIDTH_20; - - rate->index = find_first_bit(&lq_sta->active_legacy_rate, - BITS_PER_LONG); - - if (band == IEEE80211_BAND_5GHZ) { - rate->type = LQ_LEGACY_A; - initial_rates = rs_optimal_rates_5ghz_legacy; - nentries = ARRAY_SIZE(rs_optimal_rates_5ghz_legacy); - } else { - rate->type = LQ_LEGACY_G; - initial_rates = rs_optimal_rates_24ghz_legacy; - nentries = ARRAY_SIZE(rs_optimal_rates_24ghz_legacy); - } - - if (IWL_MVM_RS_RSSI_BASED_INIT_RATE) { - for (i = 0; i < nentries; i++) { - int rate_idx = initial_rates[i].rate_idx; - if ((best_rssi >= initial_rates[i].rssi) && - (BIT(rate_idx) & lq_sta->active_legacy_rate)) { - rate->index = rate_idx; - break; - } - } - } - - IWL_DEBUG_RATE(mvm, "rate_idx %d ANT %s\n", rate->index, - rs_pretty_ant(rate->ant)); -} - -/* Save info about RSSI of last Rx */ -void rs_update_last_rssi(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct ieee80211_rx_status *rx_status) -{ - int i; - - lq_sta->pers.chains = rx_status->chains; - lq_sta->pers.chain_signal[0] = rx_status->chain_signal[0]; - lq_sta->pers.chain_signal[1] = rx_status->chain_signal[1]; - lq_sta->pers.chain_signal[2] = rx_status->chain_signal[2]; - lq_sta->pers.last_rssi = S8_MIN; - - for (i = 0; i < ARRAY_SIZE(lq_sta->pers.chain_signal); i++) { - if (!(lq_sta->pers.chains & BIT(i))) - continue; - - if (lq_sta->pers.chain_signal[i] > lq_sta->pers.last_rssi) - lq_sta->pers.last_rssi = lq_sta->pers.chain_signal[i]; - } -} - -/** - * rs_initialize_lq - Initialize a station's hardware rate table - * - * The uCode's station table contains a table of fallback rates - * for automatic fallback during transmission. - * - * NOTE: This sets up a default set of values. These will be replaced later - * if the driver's iwl-agn-rs rate scaling algorithm is used, instead of - * rc80211_simple. - * - * NOTE: Run REPLY_ADD_STA command to set up station table entry, before - * calling this function (which runs REPLY_TX_LINK_QUALITY_CMD, - * which requires station table entry to exist). - */ -static void rs_initialize_lq(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, - enum ieee80211_band band, - bool init) -{ - struct iwl_scale_tbl_info *tbl; - struct rs_rate *rate; - u8 active_tbl = 0; - - if (!sta || !lq_sta) - return; - - if (!lq_sta->search_better_tbl) - active_tbl = lq_sta->active_tbl; - else - active_tbl = 1 - lq_sta->active_tbl; - - tbl = &(lq_sta->lq_info[active_tbl]); - rate = &tbl->rate; - - rs_get_initial_rate(mvm, lq_sta, band, rate); - rs_init_optimal_rate(mvm, sta, lq_sta); - - WARN_ON_ONCE(rate->ant != ANT_A && rate->ant != ANT_B); - if (rate->ant == ANT_A) - tbl->column = RS_COLUMN_LEGACY_ANT_A; - else - tbl->column = RS_COLUMN_LEGACY_ANT_B; - - rs_set_expected_tpt_table(lq_sta, tbl); - rs_fill_lq_cmd(mvm, sta, lq_sta, rate); - /* TODO restore station should remember the lq cmd */ - iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, init); -} - -static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta, - struct ieee80211_tx_rate_control *txrc) -{ - struct sk_buff *skb = txrc->skb; - struct iwl_op_mode *op_mode __maybe_unused = - (struct iwl_op_mode *)mvm_r; - struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode); - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct iwl_lq_sta *lq_sta = mvm_sta; - struct rs_rate *optimal_rate; - u32 last_ucode_rate; - - if (sta && !iwl_mvm_sta_from_mac80211(sta)->vif) { - /* if vif isn't initialized mvm doesn't know about - * this station, so don't do anything with the it - */ - sta = NULL; - mvm_sta = NULL; - } - - /* TODO: handle rate_idx_mask and rate_idx_mcs_mask */ - - /* Treat uninitialized rate scaling data same as non-existing. */ - if (lq_sta && !lq_sta->pers.drv) { - IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n"); - mvm_sta = NULL; - } - - /* Send management frames and NO_ACK data using lowest rate. */ - if (rate_control_send_low(sta, mvm_sta, txrc)) - return; - - iwl_mvm_hwrate_to_tx_rate(lq_sta->last_rate_n_flags, - info->band, &info->control.rates[0]); - info->control.rates[0].count = 1; - - /* Report the optimal rate based on rssi and STA caps if we haven't - * converged yet (too little traffic) or exploring other modulations - */ - if (lq_sta->rs_state != RS_STATE_STAY_IN_COLUMN) { - optimal_rate = rs_get_optimal_rate(mvm, lq_sta); - last_ucode_rate = ucode_rate_from_rs_rate(mvm, - optimal_rate); - iwl_mvm_hwrate_to_tx_rate(last_ucode_rate, info->band, - &txrc->reported_rate); - } -} - -static void *rs_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta, - gfp_t gfp) -{ - struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta); - struct iwl_op_mode *op_mode = (struct iwl_op_mode *)mvm_rate; - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - struct iwl_lq_sta *lq_sta = &sta_priv->lq_sta; - - IWL_DEBUG_RATE(mvm, "create station rate scale window\n"); - - lq_sta->pers.drv = mvm; -#ifdef CONFIG_MAC80211_DEBUGFS - lq_sta->pers.dbg_fixed_rate = 0; - lq_sta->pers.dbg_fixed_txp_reduction = TPC_INVALID; - lq_sta->pers.ss_force = RS_SS_FORCE_NONE; -#endif - lq_sta->pers.chains = 0; - memset(lq_sta->pers.chain_signal, 0, sizeof(lq_sta->pers.chain_signal)); - lq_sta->pers.last_rssi = S8_MIN; - - return &sta_priv->lq_sta; -} - -static int rs_vht_highest_rx_mcs_index(struct ieee80211_sta_vht_cap *vht_cap, - int nss) -{ - u16 rx_mcs = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) & - (0x3 << (2 * (nss - 1))); - rx_mcs >>= (2 * (nss - 1)); - - if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_7) - return IWL_RATE_MCS_7_INDEX; - else if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_8) - return IWL_RATE_MCS_8_INDEX; - else if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_9) - return IWL_RATE_MCS_9_INDEX; - - WARN_ON_ONCE(rx_mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED); - return -1; -} - -static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta, - struct ieee80211_sta_vht_cap *vht_cap, - struct iwl_lq_sta *lq_sta) -{ - int i; - int highest_mcs = rs_vht_highest_rx_mcs_index(vht_cap, 1); - - if (highest_mcs >= IWL_RATE_MCS_0_INDEX) { - for (i = IWL_RATE_MCS_0_INDEX; i <= highest_mcs; i++) { - if (i == IWL_RATE_9M_INDEX) - continue; - - /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */ - if (i == IWL_RATE_MCS_9_INDEX && - sta->bandwidth == IEEE80211_STA_RX_BW_20) - continue; - - lq_sta->active_siso_rate |= BIT(i); - } - } - - if (sta->rx_nss < 2) - return; - - highest_mcs = rs_vht_highest_rx_mcs_index(vht_cap, 2); - if (highest_mcs >= IWL_RATE_MCS_0_INDEX) { - for (i = IWL_RATE_MCS_0_INDEX; i <= highest_mcs; i++) { - if (i == IWL_RATE_9M_INDEX) - continue; - - /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */ - if (i == IWL_RATE_MCS_9_INDEX && - sta->bandwidth == IEEE80211_STA_RX_BW_20) - continue; - - lq_sta->active_mimo2_rate |= BIT(i); - } - } -} - -static void rs_ht_init(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, - struct ieee80211_sta_ht_cap *ht_cap) -{ - /* active_siso_rate mask includes 9 MBits (bit 5), - * and CCK (bits 0-3), supp_rates[] does not; - * shift to convert format, force 9 MBits off. - */ - lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1; - lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1; - lq_sta->active_siso_rate &= ~((u16)0x2); - lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE; - - lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1; - lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1; - lq_sta->active_mimo2_rate &= ~((u16)0x2); - lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE; - - if (mvm->cfg->ht_params->ldpc && - (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING)) - lq_sta->ldpc = true; - - if (mvm->cfg->ht_params->stbc && - (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && - (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC)) - lq_sta->stbc_capable = true; - - lq_sta->is_vht = false; -} - -static void rs_vht_init(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, - struct ieee80211_sta_vht_cap *vht_cap) -{ - rs_vht_set_enabled_rates(sta, vht_cap, lq_sta); - - if (mvm->cfg->ht_params->ldpc && - (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC)) - lq_sta->ldpc = true; - - if (mvm->cfg->ht_params->stbc && - (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && - (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK)) - lq_sta->stbc_capable = true; - - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_BEAMFORMER) && - (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && - (vht_cap->cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)) - lq_sta->bfer_capable = true; - - lq_sta->is_vht = true; -} - -#ifdef CONFIG_IWLWIFI_DEBUGFS -static void iwl_mvm_reset_frame_stats(struct iwl_mvm *mvm) -{ - spin_lock_bh(&mvm->drv_stats_lock); - memset(&mvm->drv_rx_stats, 0, sizeof(mvm->drv_rx_stats)); - spin_unlock_bh(&mvm->drv_stats_lock); -} - -void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg) -{ - u8 nss = 0, mcs = 0; - - spin_lock(&mvm->drv_stats_lock); - - if (agg) - mvm->drv_rx_stats.agg_frames++; - - mvm->drv_rx_stats.success_frames++; - - switch (rate & RATE_MCS_CHAN_WIDTH_MSK) { - case RATE_MCS_CHAN_WIDTH_20: - mvm->drv_rx_stats.bw_20_frames++; - break; - case RATE_MCS_CHAN_WIDTH_40: - mvm->drv_rx_stats.bw_40_frames++; - break; - case RATE_MCS_CHAN_WIDTH_80: - mvm->drv_rx_stats.bw_80_frames++; - break; - default: - WARN_ONCE(1, "bad BW. rate 0x%x", rate); - } - - if (rate & RATE_MCS_HT_MSK) { - mvm->drv_rx_stats.ht_frames++; - mcs = rate & RATE_HT_MCS_RATE_CODE_MSK; - nss = ((rate & RATE_HT_MCS_NSS_MSK) >> RATE_HT_MCS_NSS_POS) + 1; - } else if (rate & RATE_MCS_VHT_MSK) { - mvm->drv_rx_stats.vht_frames++; - mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK; - nss = ((rate & RATE_VHT_MCS_NSS_MSK) >> - RATE_VHT_MCS_NSS_POS) + 1; - } else { - mvm->drv_rx_stats.legacy_frames++; - } - - if (nss == 1) - mvm->drv_rx_stats.siso_frames++; - else if (nss == 2) - mvm->drv_rx_stats.mimo2_frames++; - - if (rate & RATE_MCS_SGI_MSK) - mvm->drv_rx_stats.sgi_frames++; - else - mvm->drv_rx_stats.ngi_frames++; - - mvm->drv_rx_stats.last_rates[mvm->drv_rx_stats.last_frame_idx] = rate; - mvm->drv_rx_stats.last_frame_idx = - (mvm->drv_rx_stats.last_frame_idx + 1) % - ARRAY_SIZE(mvm->drv_rx_stats.last_rates); - - spin_unlock(&mvm->drv_stats_lock); -} -#endif - -/* - * Called after adding a new station to initialize rate scaling - */ -void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - enum ieee80211_band band, bool init) -{ - int i, j; - struct ieee80211_hw *hw = mvm->hw; - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; - struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta); - struct iwl_lq_sta *lq_sta = &sta_priv->lq_sta; - struct ieee80211_supported_band *sband; - unsigned long supp; /* must be unsigned long for for_each_set_bit */ - - /* clear all non-persistent lq data */ - memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers)); - - sband = hw->wiphy->bands[band]; - - lq_sta->lq.sta_id = sta_priv->sta_id; - - for (j = 0; j < LQ_SIZE; j++) - rs_rate_scale_clear_tbl_windows(mvm, &lq_sta->lq_info[j]); - - lq_sta->flush_timer = 0; - lq_sta->last_tx = jiffies; - - IWL_DEBUG_RATE(mvm, - "LQ: *** rate scale station global init for station %d ***\n", - sta_priv->sta_id); - /* TODO: what is a good starting rate for STA? About middle? Maybe not - * the lowest or the highest rate.. Could consider using RSSI from - * previous packets? Need to have IEEE 802.1X auth succeed immediately - * after assoc.. */ - - lq_sta->missed_rate_counter = IWL_MVM_RS_MISSED_RATE_MAX; - lq_sta->band = sband->band; - /* - * active legacy rates as per supported rates bitmap - */ - supp = sta->supp_rates[sband->band]; - lq_sta->active_legacy_rate = 0; - for_each_set_bit(i, &supp, BITS_PER_LONG) - lq_sta->active_legacy_rate |= BIT(sband->bitrates[i].hw_value); - - /* TODO: should probably account for rx_highest for both HT/VHT */ - if (!vht_cap || !vht_cap->vht_supported) - rs_ht_init(mvm, sta, lq_sta, ht_cap); - else - rs_vht_init(mvm, sta, lq_sta, vht_cap); - - lq_sta->max_legacy_rate_idx = - rs_get_max_rate_from_mask(lq_sta->active_legacy_rate); - lq_sta->max_siso_rate_idx = - rs_get_max_rate_from_mask(lq_sta->active_siso_rate); - lq_sta->max_mimo2_rate_idx = - rs_get_max_rate_from_mask(lq_sta->active_mimo2_rate); - - IWL_DEBUG_RATE(mvm, - "LEGACY=%lX SISO=%lX MIMO2=%lX VHT=%d LDPC=%d STBC=%d BFER=%d\n", - lq_sta->active_legacy_rate, - lq_sta->active_siso_rate, - lq_sta->active_mimo2_rate, - lq_sta->is_vht, lq_sta->ldpc, lq_sta->stbc_capable, - lq_sta->bfer_capable); - IWL_DEBUG_RATE(mvm, "MAX RATE: LEGACY=%d SISO=%d MIMO2=%d\n", - lq_sta->max_legacy_rate_idx, - lq_sta->max_siso_rate_idx, - lq_sta->max_mimo2_rate_idx); - - /* These values will be overridden later */ - lq_sta->lq.single_stream_ant_msk = - first_antenna(iwl_mvm_get_valid_tx_ant(mvm)); - lq_sta->lq.dual_stream_ant_msk = ANT_AB; - - /* as default allow aggregation for all tids */ - lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID; - lq_sta->is_agg = 0; -#ifdef CONFIG_IWLWIFI_DEBUGFS - iwl_mvm_reset_frame_stats(mvm); -#endif - rs_initialize_lq(mvm, sta, lq_sta, band, init); -} - -static void rs_rate_update(void *mvm_r, - struct ieee80211_supported_band *sband, - struct cfg80211_chan_def *chandef, - struct ieee80211_sta *sta, void *priv_sta, - u32 changed) -{ - u8 tid; - struct iwl_op_mode *op_mode = - (struct iwl_op_mode *)mvm_r; - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - - if (!iwl_mvm_sta_from_mac80211(sta)->vif) - return; - - /* Stop any ongoing aggregations as rs starts off assuming no agg */ - for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) - ieee80211_stop_tx_ba_session(sta, tid); - - iwl_mvm_rs_rate_init(mvm, sta, sband->band, false); -} - -#ifdef CONFIG_MAC80211_DEBUGFS -static void rs_build_rates_table_from_fixed(struct iwl_mvm *mvm, - struct iwl_lq_cmd *lq_cmd, - enum ieee80211_band band, - u32 ucode_rate) -{ - struct rs_rate rate; - int i; - int num_rates = ARRAY_SIZE(lq_cmd->rs_table); - __le32 ucode_rate_le32 = cpu_to_le32(ucode_rate); - u8 ant = (ucode_rate & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS; - - for (i = 0; i < num_rates; i++) - lq_cmd->rs_table[i] = ucode_rate_le32; - - rs_rate_from_ucode_rate(ucode_rate, band, &rate); - - if (is_mimo(&rate)) - lq_cmd->mimo_delim = num_rates - 1; - else - lq_cmd->mimo_delim = 0; - - lq_cmd->reduced_tpc = 0; - - if (num_of_ant(ant) == 1) - lq_cmd->single_stream_ant_msk = ant; - - lq_cmd->agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_DEF; -} -#endif /* CONFIG_MAC80211_DEBUGFS */ - -static void rs_fill_rates_for_column(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct rs_rate *rate, - __le32 *rs_table, int *rs_table_index, - int num_rates, int num_retries, - u8 valid_tx_ant, bool toggle_ant) -{ - int i, j; - __le32 ucode_rate; - bool bottom_reached = false; - int prev_rate_idx = rate->index; - int end = LINK_QUAL_MAX_RETRY_NUM; - int index = *rs_table_index; - - for (i = 0; i < num_rates && index < end; i++) { - for (j = 0; j < num_retries && index < end; j++, index++) { - ucode_rate = cpu_to_le32(ucode_rate_from_rs_rate(mvm, - rate)); - rs_table[index] = ucode_rate; - if (toggle_ant) - rs_toggle_antenna(valid_tx_ant, rate); - } - - prev_rate_idx = rate->index; - bottom_reached = rs_get_lower_rate_in_column(lq_sta, rate); - if (bottom_reached && !is_legacy(rate)) - break; - } - - if (!bottom_reached && !is_legacy(rate)) - rate->index = prev_rate_idx; - - *rs_table_index = index; -} - -/* Building the rate table is non trivial. When we're in MIMO2/VHT/80Mhz/SGI - * column the rate table should look like this: - * - * rate[0] 0x400D019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI - * rate[1] 0x400D019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI - * rate[2] 0x400D018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI - * rate[3] 0x400D018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI - * rate[4] 0x400D017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI - * rate[5] 0x400D017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI - * rate[6] 0x4005007 VHT | ANT: A BW: 80Mhz MCS: 7 NSS: 1 NGI - * rate[7] 0x4009006 VHT | ANT: B BW: 80Mhz MCS: 6 NSS: 1 NGI - * rate[8] 0x4005005 VHT | ANT: A BW: 80Mhz MCS: 5 NSS: 1 NGI - * rate[9] 0x800B Legacy | ANT: B Rate: 36 Mbps - * rate[10] 0x4009 Legacy | ANT: A Rate: 24 Mbps - * rate[11] 0x8007 Legacy | ANT: B Rate: 18 Mbps - * rate[12] 0x4005 Legacy | ANT: A Rate: 12 Mbps - * rate[13] 0x800F Legacy | ANT: B Rate: 9 Mbps - * rate[14] 0x400D Legacy | ANT: A Rate: 6 Mbps - * rate[15] 0x800D Legacy | ANT: B Rate: 6 Mbps - */ -static void rs_build_rates_table(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, - const struct rs_rate *initial_rate) -{ - struct rs_rate rate; - int num_rates, num_retries, index = 0; - u8 valid_tx_ant = 0; - struct iwl_lq_cmd *lq_cmd = &lq_sta->lq; - bool toggle_ant = false; - - memcpy(&rate, initial_rate, sizeof(rate)); - - valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm); - - /* TODO: remove old API when min FW API hits 14 */ - if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_LQ_SS_PARAMS) && - rs_stbc_allow(mvm, sta, lq_sta)) - rate.stbc = true; - - if (is_siso(&rate)) { - num_rates = IWL_MVM_RS_INITIAL_SISO_NUM_RATES; - num_retries = IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE; - } else if (is_mimo(&rate)) { - num_rates = IWL_MVM_RS_INITIAL_MIMO_NUM_RATES; - num_retries = IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE; - } else { - num_rates = IWL_MVM_RS_INITIAL_LEGACY_NUM_RATES; - num_retries = IWL_MVM_RS_INITIAL_LEGACY_RETRIES; - toggle_ant = true; - } - - rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index, - num_rates, num_retries, valid_tx_ant, - toggle_ant); - - rs_get_lower_rate_down_column(lq_sta, &rate); - - if (is_siso(&rate)) { - num_rates = IWL_MVM_RS_SECONDARY_SISO_NUM_RATES; - num_retries = IWL_MVM_RS_SECONDARY_SISO_RETRIES; - lq_cmd->mimo_delim = index; - } else if (is_legacy(&rate)) { - num_rates = IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES; - num_retries = IWL_MVM_RS_SECONDARY_LEGACY_RETRIES; - } else { - WARN_ON_ONCE(1); - } - - toggle_ant = true; - - rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index, - num_rates, num_retries, valid_tx_ant, - toggle_ant); - - rs_get_lower_rate_down_column(lq_sta, &rate); - - num_rates = IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES; - num_retries = IWL_MVM_RS_SECONDARY_LEGACY_RETRIES; - - rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index, - num_rates, num_retries, valid_tx_ant, - toggle_ant); - -} - -struct rs_bfer_active_iter_data { - struct ieee80211_sta *exclude_sta; - struct iwl_mvm_sta *bfer_mvmsta; -}; - -static void rs_bfer_active_iter(void *_data, - struct ieee80211_sta *sta) -{ - struct rs_bfer_active_iter_data *data = _data; - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_lq_cmd *lq_cmd = &mvmsta->lq_sta.lq; - u32 ss_params = le32_to_cpu(lq_cmd->ss_params); - - if (sta == data->exclude_sta) - return; - - /* The current sta has BFER allowed */ - if (ss_params & LQ_SS_BFER_ALLOWED) { - WARN_ON_ONCE(data->bfer_mvmsta != NULL); - - data->bfer_mvmsta = mvmsta; - } -} - -static int rs_bfer_priority(struct iwl_mvm_sta *sta) -{ - int prio = -1; - enum nl80211_iftype viftype = ieee80211_vif_type_p2p(sta->vif); - - switch (viftype) { - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_P2P_GO: - prio = 3; - break; - case NL80211_IFTYPE_P2P_CLIENT: - prio = 2; - break; - case NL80211_IFTYPE_STATION: - prio = 1; - break; - default: - WARN_ONCE(true, "viftype %d sta_id %d", viftype, sta->sta_id); - prio = -1; - } - - return prio; -} - -/* Returns >0 if sta1 has a higher BFER priority compared to sta2 */ -static int rs_bfer_priority_cmp(struct iwl_mvm_sta *sta1, - struct iwl_mvm_sta *sta2) -{ - int prio1 = rs_bfer_priority(sta1); - int prio2 = rs_bfer_priority(sta2); - - if (prio1 > prio2) - return 1; - if (prio1 < prio2) - return -1; - return 0; -} - -static void rs_set_lq_ss_params(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, - const struct rs_rate *initial_rate) -{ - struct iwl_lq_cmd *lq_cmd = &lq_sta->lq; - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct rs_bfer_active_iter_data data = { - .exclude_sta = sta, - .bfer_mvmsta = NULL, - }; - struct iwl_mvm_sta *bfer_mvmsta = NULL; - u32 ss_params = LQ_SS_PARAMS_VALID; - - if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) - goto out; - -#ifdef CONFIG_MAC80211_DEBUGFS - /* Check if forcing the decision is configured. - * Note that SISO is forced by not allowing STBC or BFER - */ - if (lq_sta->pers.ss_force == RS_SS_FORCE_STBC) - ss_params |= (LQ_SS_STBC_1SS_ALLOWED | LQ_SS_FORCE); - else if (lq_sta->pers.ss_force == RS_SS_FORCE_BFER) - ss_params |= (LQ_SS_BFER_ALLOWED | LQ_SS_FORCE); - - if (lq_sta->pers.ss_force != RS_SS_FORCE_NONE) { - IWL_DEBUG_RATE(mvm, "Forcing single stream Tx decision %d\n", - lq_sta->pers.ss_force); - goto out; - } -#endif - - if (lq_sta->stbc_capable) - ss_params |= LQ_SS_STBC_1SS_ALLOWED; - - if (!lq_sta->bfer_capable) - goto out; - - ieee80211_iterate_stations_atomic(mvm->hw, - rs_bfer_active_iter, - &data); - bfer_mvmsta = data.bfer_mvmsta; - - /* This code is safe as it doesn't run concurrently for different - * stations. This is guaranteed by the fact that calls to - * ieee80211_tx_status wouldn't run concurrently for a single HW. - */ - if (!bfer_mvmsta) { - IWL_DEBUG_RATE(mvm, "No sta with BFER allowed found. Allow\n"); - - ss_params |= LQ_SS_BFER_ALLOWED; - goto out; - } - - IWL_DEBUG_RATE(mvm, "Found existing sta %d with BFER activated\n", - bfer_mvmsta->sta_id); - - /* Disallow BFER on another STA if active and we're a higher priority */ - if (rs_bfer_priority_cmp(mvmsta, bfer_mvmsta) > 0) { - struct iwl_lq_cmd *bfersta_lq_cmd = &bfer_mvmsta->lq_sta.lq; - u32 bfersta_ss_params = le32_to_cpu(bfersta_lq_cmd->ss_params); - - bfersta_ss_params &= ~LQ_SS_BFER_ALLOWED; - bfersta_lq_cmd->ss_params = cpu_to_le32(bfersta_ss_params); - iwl_mvm_send_lq_cmd(mvm, bfersta_lq_cmd, false); - - ss_params |= LQ_SS_BFER_ALLOWED; - IWL_DEBUG_RATE(mvm, - "Lower priority BFER sta found (%d). Switch BFER\n", - bfer_mvmsta->sta_id); - } -out: - lq_cmd->ss_params = cpu_to_le32(ss_params); -} - -static void rs_fill_lq_cmd(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, - const struct rs_rate *initial_rate) -{ - struct iwl_lq_cmd *lq_cmd = &lq_sta->lq; - struct iwl_mvm_sta *mvmsta; - struct iwl_mvm_vif *mvmvif; - - lq_cmd->agg_disable_start_th = IWL_MVM_RS_AGG_DISABLE_START; - lq_cmd->agg_time_limit = - cpu_to_le16(IWL_MVM_RS_AGG_TIME_LIMIT); - -#ifdef CONFIG_MAC80211_DEBUGFS - if (lq_sta->pers.dbg_fixed_rate) { - rs_build_rates_table_from_fixed(mvm, lq_cmd, - lq_sta->band, - lq_sta->pers.dbg_fixed_rate); - return; - } -#endif - if (WARN_ON_ONCE(!sta || !initial_rate)) - return; - - rs_build_rates_table(mvm, sta, lq_sta, initial_rate); - - if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_LQ_SS_PARAMS)) - rs_set_lq_ss_params(mvm, sta, lq_sta, initial_rate); - - mvmsta = iwl_mvm_sta_from_mac80211(sta); - mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); - - if (num_of_ant(initial_rate->ant) == 1) - lq_cmd->single_stream_ant_msk = initial_rate->ant; - - lq_cmd->agg_frame_cnt_limit = mvmsta->max_agg_bufsize; - - /* - * In case of low latency, tell the firmware to leave a frame in the - * Tx Fifo so that it can start a transaction in the same TxOP. This - * basically allows the firmware to send bursts. - */ - if (iwl_mvm_vif_low_latency(mvmvif)) { - lq_cmd->agg_frame_cnt_limit--; - - if (mvm->low_latency_agg_frame_limit) - lq_cmd->agg_frame_cnt_limit = - min(lq_cmd->agg_frame_cnt_limit, - mvm->low_latency_agg_frame_limit); - } - - if (mvmsta->vif->p2p) - lq_cmd->flags |= LQ_FLAG_USE_RTS_MSK; - - lq_cmd->agg_time_limit = - cpu_to_le16(iwl_mvm_coex_agg_time_limit(mvm, sta)); -} - -static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) -{ - return hw->priv; -} -/* rate scale requires free function to be implemented */ -static void rs_free(void *mvm_rate) -{ - return; -} - -static void rs_free_sta(void *mvm_r, struct ieee80211_sta *sta, - void *mvm_sta) -{ - struct iwl_op_mode *op_mode __maybe_unused = mvm_r; - struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode); - - IWL_DEBUG_RATE(mvm, "enter\n"); - IWL_DEBUG_RATE(mvm, "leave\n"); -} - -#ifdef CONFIG_MAC80211_DEBUGFS -int rs_pretty_print_rate(char *buf, const u32 rate) -{ - - char *type, *bw; - u8 mcs = 0, nss = 0; - u8 ant = (rate & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS; - - if (!(rate & RATE_MCS_HT_MSK) && - !(rate & RATE_MCS_VHT_MSK)) { - int index = iwl_hwrate_to_plcp_idx(rate); - - return sprintf(buf, "Legacy | ANT: %s Rate: %s Mbps\n", - rs_pretty_ant(ant), - index == IWL_RATE_INVALID ? "BAD" : - iwl_rate_mcs[index].mbps); - } - - if (rate & RATE_MCS_VHT_MSK) { - type = "VHT"; - mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK; - nss = ((rate & RATE_VHT_MCS_NSS_MSK) - >> RATE_VHT_MCS_NSS_POS) + 1; - } else if (rate & RATE_MCS_HT_MSK) { - type = "HT"; - mcs = rate & RATE_HT_MCS_INDEX_MSK; - } else { - type = "Unknown"; /* shouldn't happen */ - } - - switch (rate & RATE_MCS_CHAN_WIDTH_MSK) { - case RATE_MCS_CHAN_WIDTH_20: - bw = "20Mhz"; - break; - case RATE_MCS_CHAN_WIDTH_40: - bw = "40Mhz"; - break; - case RATE_MCS_CHAN_WIDTH_80: - bw = "80Mhz"; - break; - case RATE_MCS_CHAN_WIDTH_160: - bw = "160Mhz"; - break; - default: - bw = "BAD BW"; - } - - return sprintf(buf, "%s | ANT: %s BW: %s MCS: %d NSS: %d %s%s%s%s%s\n", - type, rs_pretty_ant(ant), bw, mcs, nss, - (rate & RATE_MCS_SGI_MSK) ? "SGI " : "NGI ", - (rate & RATE_MCS_HT_STBC_MSK) ? "STBC " : "", - (rate & RATE_MCS_LDPC_MSK) ? "LDPC " : "", - (rate & RATE_MCS_BF_MSK) ? "BF " : "", - (rate & RATE_MCS_ZLF_MSK) ? "ZLF " : ""); -} - -/** - * Program the device to use fixed rate for frame transmit - * This is for debugging/testing only - * once the device start use fixed rate, we need to reload the module - * to being back the normal operation. - */ -static void rs_program_fix_rate(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta) -{ - lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */ - lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ - lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ - - IWL_DEBUG_RATE(mvm, "sta_id %d rate 0x%X\n", - lq_sta->lq.sta_id, lq_sta->pers.dbg_fixed_rate); - - if (lq_sta->pers.dbg_fixed_rate) { - rs_fill_lq_cmd(mvm, NULL, lq_sta, NULL); - iwl_mvm_send_lq_cmd(lq_sta->pers.drv, &lq_sta->lq, false); - } -} - -static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) -{ - struct iwl_lq_sta *lq_sta = file->private_data; - struct iwl_mvm *mvm; - char buf[64]; - size_t buf_size; - u32 parsed_rate; - - mvm = lq_sta->pers.drv; - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - if (sscanf(buf, "%x", &parsed_rate) == 1) - lq_sta->pers.dbg_fixed_rate = parsed_rate; - else - lq_sta->pers.dbg_fixed_rate = 0; - - rs_program_fix_rate(mvm, lq_sta); - - return count; -} - -static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, - char __user *user_buf, size_t count, loff_t *ppos) -{ - char *buff; - int desc = 0; - int i = 0; - ssize_t ret; - - struct iwl_lq_sta *lq_sta = file->private_data; - struct iwl_mvm *mvm; - struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - struct rs_rate *rate = &tbl->rate; - u32 ss_params; - mvm = lq_sta->pers.drv; - buff = kmalloc(2048, GFP_KERNEL); - if (!buff) - return -ENOMEM; - - desc += sprintf(buff+desc, "sta_id %d\n", lq_sta->lq.sta_id); - desc += sprintf(buff+desc, "failed=%d success=%d rate=0%lX\n", - lq_sta->total_failed, lq_sta->total_success, - lq_sta->active_legacy_rate); - desc += sprintf(buff+desc, "fixed rate 0x%X\n", - lq_sta->pers.dbg_fixed_rate); - desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n", - (iwl_mvm_get_valid_tx_ant(mvm) & ANT_A) ? "ANT_A," : "", - (iwl_mvm_get_valid_tx_ant(mvm) & ANT_B) ? "ANT_B," : "", - (iwl_mvm_get_valid_tx_ant(mvm) & ANT_C) ? "ANT_C" : ""); - desc += sprintf(buff+desc, "lq type %s\n", - (is_legacy(rate)) ? "legacy" : - is_vht(rate) ? "VHT" : "HT"); - if (!is_legacy(rate)) { - desc += sprintf(buff + desc, " %s", - (is_siso(rate)) ? "SISO" : "MIMO2"); - desc += sprintf(buff + desc, " %s", - (is_ht20(rate)) ? "20MHz" : - (is_ht40(rate)) ? "40MHz" : - (is_ht80(rate)) ? "80Mhz" : "BAD BW"); - desc += sprintf(buff + desc, " %s %s %s\n", - (rate->sgi) ? "SGI" : "NGI", - (rate->ldpc) ? "LDPC" : "BCC", - (lq_sta->is_agg) ? "AGG on" : ""); - } - desc += sprintf(buff+desc, "last tx rate=0x%X\n", - lq_sta->last_rate_n_flags); - desc += sprintf(buff+desc, - "general: flags=0x%X mimo-d=%d s-ant=0x%x d-ant=0x%x\n", - lq_sta->lq.flags, - lq_sta->lq.mimo_delim, - lq_sta->lq.single_stream_ant_msk, - lq_sta->lq.dual_stream_ant_msk); - - desc += sprintf(buff+desc, - "agg: time_limit=%d dist_start_th=%d frame_cnt_limit=%d\n", - le16_to_cpu(lq_sta->lq.agg_time_limit), - lq_sta->lq.agg_disable_start_th, - lq_sta->lq.agg_frame_cnt_limit); - - desc += sprintf(buff+desc, "reduced tpc=%d\n", lq_sta->lq.reduced_tpc); - ss_params = le32_to_cpu(lq_sta->lq.ss_params); - desc += sprintf(buff+desc, "single stream params: %s%s%s%s\n", - (ss_params & LQ_SS_PARAMS_VALID) ? - "VALID" : "INVALID", - (ss_params & LQ_SS_BFER_ALLOWED) ? - ", BFER" : "", - (ss_params & LQ_SS_STBC_1SS_ALLOWED) ? - ", STBC" : "", - (ss_params & LQ_SS_FORCE) ? - ", FORCE" : ""); - desc += sprintf(buff+desc, - "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n", - lq_sta->lq.initial_rate_index[0], - lq_sta->lq.initial_rate_index[1], - lq_sta->lq.initial_rate_index[2], - lq_sta->lq.initial_rate_index[3]); - - for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { - u32 r = le32_to_cpu(lq_sta->lq.rs_table[i]); - - desc += sprintf(buff+desc, " rate[%d] 0x%X ", i, r); - desc += rs_pretty_print_rate(buff+desc, r); - } - - ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); - kfree(buff); - return ret; -} - -static const struct file_operations rs_sta_dbgfs_scale_table_ops = { - .write = rs_sta_dbgfs_scale_table_write, - .read = rs_sta_dbgfs_scale_table_read, - .open = simple_open, - .llseek = default_llseek, -}; -static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, - char __user *user_buf, size_t count, loff_t *ppos) -{ - char *buff; - int desc = 0; - int i, j; - ssize_t ret; - struct iwl_scale_tbl_info *tbl; - struct rs_rate *rate; - struct iwl_lq_sta *lq_sta = file->private_data; - - buff = kmalloc(1024, GFP_KERNEL); - if (!buff) - return -ENOMEM; - - for (i = 0; i < LQ_SIZE; i++) { - tbl = &(lq_sta->lq_info[i]); - rate = &tbl->rate; - desc += sprintf(buff+desc, - "%s type=%d SGI=%d BW=%s DUP=0\n" - "index=%d\n", - lq_sta->active_tbl == i ? "*" : "x", - rate->type, - rate->sgi, - is_ht20(rate) ? "20Mhz" : - is_ht40(rate) ? "40Mhz" : - is_ht80(rate) ? "80Mhz" : "ERR", - rate->index); - for (j = 0; j < IWL_RATE_COUNT; j++) { - desc += sprintf(buff+desc, - "counter=%d success=%d %%=%d\n", - tbl->win[j].counter, - tbl->win[j].success_counter, - tbl->win[j].success_ratio); - } - } - ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); - kfree(buff); - return ret; -} - -static const struct file_operations rs_sta_dbgfs_stats_table_ops = { - .read = rs_sta_dbgfs_stats_table_read, - .open = simple_open, - .llseek = default_llseek, -}; - -static ssize_t rs_sta_dbgfs_drv_tx_stats_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - static const char * const column_name[] = { - [RS_COLUMN_LEGACY_ANT_A] = "LEGACY_ANT_A", - [RS_COLUMN_LEGACY_ANT_B] = "LEGACY_ANT_B", - [RS_COLUMN_SISO_ANT_A] = "SISO_ANT_A", - [RS_COLUMN_SISO_ANT_B] = "SISO_ANT_B", - [RS_COLUMN_SISO_ANT_A_SGI] = "SISO_ANT_A_SGI", - [RS_COLUMN_SISO_ANT_B_SGI] = "SISO_ANT_B_SGI", - [RS_COLUMN_MIMO2] = "MIMO2", - [RS_COLUMN_MIMO2_SGI] = "MIMO2_SGI", - }; - - static const char * const rate_name[] = { - [IWL_RATE_1M_INDEX] = "1M", - [IWL_RATE_2M_INDEX] = "2M", - [IWL_RATE_5M_INDEX] = "5.5M", - [IWL_RATE_11M_INDEX] = "11M", - [IWL_RATE_6M_INDEX] = "6M|MCS0", - [IWL_RATE_9M_INDEX] = "9M", - [IWL_RATE_12M_INDEX] = "12M|MCS1", - [IWL_RATE_18M_INDEX] = "18M|MCS2", - [IWL_RATE_24M_INDEX] = "24M|MCS3", - [IWL_RATE_36M_INDEX] = "36M|MCS4", - [IWL_RATE_48M_INDEX] = "48M|MCS5", - [IWL_RATE_54M_INDEX] = "54M|MCS6", - [IWL_RATE_MCS_7_INDEX] = "MCS7", - [IWL_RATE_MCS_8_INDEX] = "MCS8", - [IWL_RATE_MCS_9_INDEX] = "MCS9", - }; - - char *buff, *pos, *endpos; - int col, rate; - ssize_t ret; - struct iwl_lq_sta *lq_sta = file->private_data; - struct rs_rate_stats *stats; - static const size_t bufsz = 1024; - - buff = kmalloc(bufsz, GFP_KERNEL); - if (!buff) - return -ENOMEM; - - pos = buff; - endpos = pos + bufsz; - - pos += scnprintf(pos, endpos - pos, "COLUMN,"); - for (rate = 0; rate < IWL_RATE_COUNT; rate++) - pos += scnprintf(pos, endpos - pos, "%s,", rate_name[rate]); - pos += scnprintf(pos, endpos - pos, "\n"); - - for (col = 0; col < RS_COLUMN_COUNT; col++) { - pos += scnprintf(pos, endpos - pos, - "%s,", column_name[col]); - - for (rate = 0; rate < IWL_RATE_COUNT; rate++) { - stats = &(lq_sta->pers.tx_stats[col][rate]); - pos += scnprintf(pos, endpos - pos, - "%llu/%llu,", - stats->success, - stats->total); - } - pos += scnprintf(pos, endpos - pos, "\n"); - } - - ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff); - kfree(buff); - return ret; -} - -static ssize_t rs_sta_dbgfs_drv_tx_stats_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_lq_sta *lq_sta = file->private_data; - memset(lq_sta->pers.tx_stats, 0, sizeof(lq_sta->pers.tx_stats)); - - return count; -} - -static const struct file_operations rs_sta_dbgfs_drv_tx_stats_ops = { - .read = rs_sta_dbgfs_drv_tx_stats_read, - .write = rs_sta_dbgfs_drv_tx_stats_write, - .open = simple_open, - .llseek = default_llseek, -}; - -static ssize_t iwl_dbgfs_ss_force_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_lq_sta *lq_sta = file->private_data; - char buf[12]; - int bufsz = sizeof(buf); - int pos = 0; - static const char * const ss_force_name[] = { - [RS_SS_FORCE_NONE] = "none", - [RS_SS_FORCE_STBC] = "stbc", - [RS_SS_FORCE_BFER] = "bfer", - [RS_SS_FORCE_SISO] = "siso", - }; - - pos += scnprintf(buf+pos, bufsz-pos, "%s\n", - ss_force_name[lq_sta->pers.ss_force]); - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_ss_force_write(struct iwl_lq_sta *lq_sta, char *buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = lq_sta->pers.drv; - int ret = 0; - - if (!strncmp("none", buf, 4)) { - lq_sta->pers.ss_force = RS_SS_FORCE_NONE; - } else if (!strncmp("siso", buf, 4)) { - lq_sta->pers.ss_force = RS_SS_FORCE_SISO; - } else if (!strncmp("stbc", buf, 4)) { - if (lq_sta->stbc_capable) { - lq_sta->pers.ss_force = RS_SS_FORCE_STBC; - } else { - IWL_ERR(mvm, - "can't force STBC. peer doesn't support\n"); - ret = -EINVAL; - } - } else if (!strncmp("bfer", buf, 4)) { - if (lq_sta->bfer_capable) { - lq_sta->pers.ss_force = RS_SS_FORCE_BFER; - } else { - IWL_ERR(mvm, - "can't force BFER. peer doesn't support\n"); - ret = -EINVAL; - } - } else { - IWL_ERR(mvm, "valid values none|siso|stbc|bfer\n"); - ret = -EINVAL; - } - return ret ?: count; -} - -#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ - _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_lq_sta) -#define MVM_DEBUGFS_ADD_FILE_RS(name, parent, mode) do { \ - if (!debugfs_create_file(#name, mode, parent, lq_sta, \ - &iwl_dbgfs_##name##_ops)) \ - goto err; \ - } while (0) - -MVM_DEBUGFS_READ_WRITE_FILE_OPS(ss_force, 32); - -static void rs_add_debugfs(void *mvm, void *priv_sta, struct dentry *dir) -{ - struct iwl_lq_sta *lq_sta = priv_sta; - struct iwl_mvm_sta *mvmsta; - - mvmsta = container_of(lq_sta, struct iwl_mvm_sta, lq_sta); - - if (!mvmsta->vif) - return; - - debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir, - lq_sta, &rs_sta_dbgfs_scale_table_ops); - debugfs_create_file("rate_stats_table", S_IRUSR, dir, - lq_sta, &rs_sta_dbgfs_stats_table_ops); - debugfs_create_file("drv_tx_stats", S_IRUSR | S_IWUSR, dir, - lq_sta, &rs_sta_dbgfs_drv_tx_stats_ops); - debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir, - &lq_sta->tx_agg_tid_en); - debugfs_create_u8("reduced_tpc", S_IRUSR | S_IWUSR, dir, - &lq_sta->pers.dbg_fixed_txp_reduction); - - MVM_DEBUGFS_ADD_FILE_RS(ss_force, dir, S_IRUSR | S_IWUSR); - return; -err: - IWL_ERR((struct iwl_mvm *)mvm, "Can't create debugfs entity\n"); -} - -static void rs_remove_debugfs(void *mvm, void *mvm_sta) -{ -} -#endif - -/* - * Initialization of rate scaling information is done by driver after - * the station is added. Since mac80211 calls this function before a - * station is added we ignore it. - */ -static void rs_rate_init_stub(void *mvm_r, - struct ieee80211_supported_band *sband, - struct cfg80211_chan_def *chandef, - struct ieee80211_sta *sta, void *mvm_sta) -{ -} - -static const struct rate_control_ops rs_mvm_ops = { - .name = RS_NAME, - .tx_status = rs_mac80211_tx_status, - .get_rate = rs_get_rate, - .rate_init = rs_rate_init_stub, - .alloc = rs_alloc, - .free = rs_free, - .alloc_sta = rs_alloc_sta, - .free_sta = rs_free_sta, - .rate_update = rs_rate_update, -#ifdef CONFIG_MAC80211_DEBUGFS - .add_sta_debugfs = rs_add_debugfs, - .remove_sta_debugfs = rs_remove_debugfs, -#endif -}; - -int iwl_mvm_rate_control_register(void) -{ - return ieee80211_rate_control_register(&rs_mvm_ops); -} - -void iwl_mvm_rate_control_unregister(void) -{ - ieee80211_rate_control_unregister(&rs_mvm_ops); -} - -/** - * iwl_mvm_tx_protection - Gets LQ command, change it to enable/disable - * Tx protection, according to this request and previous requests, - * and send the LQ command. - * @mvmsta: The station - * @enable: Enable Tx protection? - */ -int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, - bool enable) -{ - struct iwl_lq_cmd *lq = &mvmsta->lq_sta.lq; - - lockdep_assert_held(&mvm->mutex); - - if (enable) { - if (mvmsta->tx_protection == 0) - lq->flags |= LQ_FLAG_USE_RTS_MSK; - mvmsta->tx_protection++; - } else { - mvmsta->tx_protection--; - if (mvmsta->tx_protection == 0) - lq->flags &= ~LQ_FLAG_USE_RTS_MSK; - } - - return iwl_mvm_send_lq_cmd(mvm, lq, false); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.h b/drivers/net/wireless/iwlwifi/mvm/rs.h deleted file mode 100644 index 81314ad9e..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/rs.h +++ /dev/null @@ -1,392 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2015 Intel Mobile Communications GmbH - * - * 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 LICENSE. - * - * Contact Information: - * Intel Linux Wireless <ilw@linux.intel.com> - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#ifndef __rs_h__ -#define __rs_h__ - -#include <net/mac80211.h> - -#include "iwl-config.h" - -#include "fw-api.h" -#include "iwl-trans.h" - -struct iwl_rs_rate_info { - u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */ - u8 plcp_ht_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */ - u8 plcp_ht_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */ - u8 plcp_vht_siso; - u8 plcp_vht_mimo2; - u8 prev_rs; /* previous rate used in rs algo */ - u8 next_rs; /* next rate used in rs algo */ -}; - -#define IWL_RATE_60M_PLCP 3 - -enum { - IWL_RATE_INVM_INDEX = IWL_RATE_COUNT, - IWL_RATE_INVALID = IWL_RATE_COUNT, -}; - -#define LINK_QUAL_MAX_RETRY_NUM 16 - -enum { - IWL_RATE_6M_INDEX_TABLE = 0, - IWL_RATE_9M_INDEX_TABLE, - IWL_RATE_12M_INDEX_TABLE, - IWL_RATE_18M_INDEX_TABLE, - IWL_RATE_24M_INDEX_TABLE, - IWL_RATE_36M_INDEX_TABLE, - IWL_RATE_48M_INDEX_TABLE, - IWL_RATE_54M_INDEX_TABLE, - IWL_RATE_1M_INDEX_TABLE, - IWL_RATE_2M_INDEX_TABLE, - IWL_RATE_5M_INDEX_TABLE, - IWL_RATE_11M_INDEX_TABLE, - IWL_RATE_INVM_INDEX_TABLE = IWL_RATE_INVM_INDEX - 1, -}; - -/* #define vs. enum to keep from defaulting to 'large integer' */ -#define IWL_RATE_6M_MASK (1 << IWL_RATE_6M_INDEX) -#define IWL_RATE_9M_MASK (1 << IWL_RATE_9M_INDEX) -#define IWL_RATE_12M_MASK (1 << IWL_RATE_12M_INDEX) -#define IWL_RATE_18M_MASK (1 << IWL_RATE_18M_INDEX) -#define IWL_RATE_24M_MASK (1 << IWL_RATE_24M_INDEX) -#define IWL_RATE_36M_MASK (1 << IWL_RATE_36M_INDEX) -#define IWL_RATE_48M_MASK (1 << IWL_RATE_48M_INDEX) -#define IWL_RATE_54M_MASK (1 << IWL_RATE_54M_INDEX) -#define IWL_RATE_60M_MASK (1 << IWL_RATE_60M_INDEX) -#define IWL_RATE_1M_MASK (1 << IWL_RATE_1M_INDEX) -#define IWL_RATE_2M_MASK (1 << IWL_RATE_2M_INDEX) -#define IWL_RATE_5M_MASK (1 << IWL_RATE_5M_INDEX) -#define IWL_RATE_11M_MASK (1 << IWL_RATE_11M_INDEX) - - -/* uCode API values for HT/VHT bit rates */ -enum { - IWL_RATE_HT_SISO_MCS_0_PLCP = 0, - IWL_RATE_HT_SISO_MCS_1_PLCP = 1, - IWL_RATE_HT_SISO_MCS_2_PLCP = 2, - IWL_RATE_HT_SISO_MCS_3_PLCP = 3, - IWL_RATE_HT_SISO_MCS_4_PLCP = 4, - IWL_RATE_HT_SISO_MCS_5_PLCP = 5, - IWL_RATE_HT_SISO_MCS_6_PLCP = 6, - IWL_RATE_HT_SISO_MCS_7_PLCP = 7, - IWL_RATE_HT_MIMO2_MCS_0_PLCP = 0x8, - IWL_RATE_HT_MIMO2_MCS_1_PLCP = 0x9, - IWL_RATE_HT_MIMO2_MCS_2_PLCP = 0xA, - IWL_RATE_HT_MIMO2_MCS_3_PLCP = 0xB, - IWL_RATE_HT_MIMO2_MCS_4_PLCP = 0xC, - IWL_RATE_HT_MIMO2_MCS_5_PLCP = 0xD, - IWL_RATE_HT_MIMO2_MCS_6_PLCP = 0xE, - IWL_RATE_HT_MIMO2_MCS_7_PLCP = 0xF, - IWL_RATE_VHT_SISO_MCS_0_PLCP = 0, - IWL_RATE_VHT_SISO_MCS_1_PLCP = 1, - IWL_RATE_VHT_SISO_MCS_2_PLCP = 2, - IWL_RATE_VHT_SISO_MCS_3_PLCP = 3, - IWL_RATE_VHT_SISO_MCS_4_PLCP = 4, - IWL_RATE_VHT_SISO_MCS_5_PLCP = 5, - IWL_RATE_VHT_SISO_MCS_6_PLCP = 6, - IWL_RATE_VHT_SISO_MCS_7_PLCP = 7, - IWL_RATE_VHT_SISO_MCS_8_PLCP = 8, - IWL_RATE_VHT_SISO_MCS_9_PLCP = 9, - IWL_RATE_VHT_MIMO2_MCS_0_PLCP = 0x10, - IWL_RATE_VHT_MIMO2_MCS_1_PLCP = 0x11, - IWL_RATE_VHT_MIMO2_MCS_2_PLCP = 0x12, - IWL_RATE_VHT_MIMO2_MCS_3_PLCP = 0x13, - IWL_RATE_VHT_MIMO2_MCS_4_PLCP = 0x14, - IWL_RATE_VHT_MIMO2_MCS_5_PLCP = 0x15, - IWL_RATE_VHT_MIMO2_MCS_6_PLCP = 0x16, - IWL_RATE_VHT_MIMO2_MCS_7_PLCP = 0x17, - IWL_RATE_VHT_MIMO2_MCS_8_PLCP = 0x18, - IWL_RATE_VHT_MIMO2_MCS_9_PLCP = 0x19, - IWL_RATE_HT_SISO_MCS_INV_PLCP, - IWL_RATE_HT_MIMO2_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, - IWL_RATE_VHT_SISO_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, - IWL_RATE_VHT_MIMO2_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, - IWL_RATE_HT_SISO_MCS_8_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, - IWL_RATE_HT_SISO_MCS_9_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, - IWL_RATE_HT_MIMO2_MCS_8_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, - IWL_RATE_HT_MIMO2_MCS_9_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, -}; - -#define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1) - -#define IWL_INVALID_VALUE -1 - -#define TPC_MAX_REDUCTION 15 -#define TPC_NO_REDUCTION 0 -#define TPC_INVALID 0xff - -#define LINK_QUAL_AGG_FRAME_LIMIT_DEF (63) -#define LINK_QUAL_AGG_FRAME_LIMIT_MAX (63) -#define LINK_QUAL_AGG_FRAME_LIMIT_MIN (0) - -#define LQ_SIZE 2 /* 2 mode tables: "Active" and "Search" */ - -/* load per tid defines for A-MPDU activation */ -#define IWL_AGG_TPT_THREHOLD 0 -#define IWL_AGG_ALL_TID 0xff - -enum iwl_table_type { - LQ_NONE, - LQ_LEGACY_G, /* legacy types */ - LQ_LEGACY_A, - LQ_HT_SISO, /* HT types */ - LQ_HT_MIMO2, - LQ_VHT_SISO, /* VHT types */ - LQ_VHT_MIMO2, - LQ_MAX, -}; - -struct rs_rate { - int index; - enum iwl_table_type type; - u8 ant; - u32 bw; - bool sgi; - bool ldpc; - bool stbc; - bool bfer; -}; - - -#define is_type_legacy(type) (((type) == LQ_LEGACY_G) || \ - ((type) == LQ_LEGACY_A)) -#define is_type_ht_siso(type) ((type) == LQ_HT_SISO) -#define is_type_ht_mimo2(type) ((type) == LQ_HT_MIMO2) -#define is_type_vht_siso(type) ((type) == LQ_VHT_SISO) -#define is_type_vht_mimo2(type) ((type) == LQ_VHT_MIMO2) -#define is_type_siso(type) (is_type_ht_siso(type) || is_type_vht_siso(type)) -#define is_type_mimo2(type) (is_type_ht_mimo2(type) || is_type_vht_mimo2(type)) -#define is_type_mimo(type) (is_type_mimo2(type)) -#define is_type_ht(type) (is_type_ht_siso(type) || is_type_ht_mimo2(type)) -#define is_type_vht(type) (is_type_vht_siso(type) || is_type_vht_mimo2(type)) -#define is_type_a_band(type) ((type) == LQ_LEGACY_A) -#define is_type_g_band(type) ((type) == LQ_LEGACY_G) - -#define is_legacy(rate) is_type_legacy((rate)->type) -#define is_ht_siso(rate) is_type_ht_siso((rate)->type) -#define is_ht_mimo2(rate) is_type_ht_mimo2((rate)->type) -#define is_vht_siso(rate) is_type_vht_siso((rate)->type) -#define is_vht_mimo2(rate) is_type_vht_mimo2((rate)->type) -#define is_siso(rate) is_type_siso((rate)->type) -#define is_mimo2(rate) is_type_mimo2((rate)->type) -#define is_mimo(rate) is_type_mimo((rate)->type) -#define is_ht(rate) is_type_ht((rate)->type) -#define is_vht(rate) is_type_vht((rate)->type) -#define is_a_band(rate) is_type_a_band((rate)->type) -#define is_g_band(rate) is_type_g_band((rate)->type) - -#define is_ht20(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_20) -#define is_ht40(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_40) -#define is_ht80(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_80) - -#define IWL_MAX_MCS_DISPLAY_SIZE 12 - -struct iwl_rate_mcs_info { - char mbps[IWL_MAX_MCS_DISPLAY_SIZE]; - char mcs[IWL_MAX_MCS_DISPLAY_SIZE]; -}; - -/** - * struct iwl_rate_scale_data -- tx success history for one rate - */ -struct iwl_rate_scale_data { - u64 data; /* bitmap of successful frames */ - s32 success_counter; /* number of frames successful */ - s32 success_ratio; /* per-cent * 128 */ - s32 counter; /* number of frames attempted */ - s32 average_tpt; /* success ratio * expected throughput */ -}; - -/* Possible Tx columns - * Tx Column = a combo of legacy/siso/mimo x antenna x SGI - */ -enum rs_column { - RS_COLUMN_LEGACY_ANT_A = 0, - RS_COLUMN_LEGACY_ANT_B, - RS_COLUMN_SISO_ANT_A, - RS_COLUMN_SISO_ANT_B, - RS_COLUMN_SISO_ANT_A_SGI, - RS_COLUMN_SISO_ANT_B_SGI, - RS_COLUMN_MIMO2, - RS_COLUMN_MIMO2_SGI, - - RS_COLUMN_LAST = RS_COLUMN_MIMO2_SGI, - RS_COLUMN_COUNT = RS_COLUMN_LAST + 1, - RS_COLUMN_INVALID, -}; - -enum rs_ss_force_opt { - RS_SS_FORCE_NONE = 0, - RS_SS_FORCE_STBC, - RS_SS_FORCE_BFER, - RS_SS_FORCE_SISO, -}; - -/* Packet stats per rate */ -struct rs_rate_stats { - u64 success; - u64 total; -}; - -/** - * struct iwl_scale_tbl_info -- tx params and success history for all rates - * - * There are two of these in struct iwl_lq_sta, - * one for "active", and one for "search". - */ -struct iwl_scale_tbl_info { - struct rs_rate rate; - enum rs_column column; - const u16 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ - struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */ - /* per txpower-reduction history */ - struct iwl_rate_scale_data tpc_win[TPC_MAX_REDUCTION + 1]; -}; - -enum { - RS_STATE_SEARCH_CYCLE_STARTED, - RS_STATE_SEARCH_CYCLE_ENDED, - RS_STATE_STAY_IN_COLUMN, -}; - -/** - * struct iwl_lq_sta -- driver's rate scaling private structure - * - * Pointer to this gets passed back and forth between driver and mac80211. - */ -struct iwl_lq_sta { - u8 active_tbl; /* index of active table, range 0-1 */ - u8 rs_state; /* RS_STATE_* */ - u8 search_better_tbl; /* 1: currently trying alternate mode */ - s32 last_tpt; - - /* The following determine when to search for a new mode */ - u32 table_count_limit; - u32 max_failure_limit; /* # failed frames before new search */ - u32 max_success_limit; /* # successful frames before new search */ - u32 table_count; - u32 total_failed; /* total failed frames, any/all rates */ - u32 total_success; /* total successful frames, any/all rates */ - u64 flush_timer; /* time staying in mode before new search */ - - u32 visited_columns; /* Bitmask marking which Tx columns were - * explored during a search cycle - */ - u64 last_tx; - bool is_vht; - bool ldpc; /* LDPC Rx is supported by the STA */ - bool stbc_capable; /* Tx STBC is supported by chip and Rx by STA */ - bool bfer_capable; /* Remote supports beamformee and we BFer */ - - enum ieee80211_band band; - - /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */ - unsigned long active_legacy_rate; - unsigned long active_siso_rate; - unsigned long active_mimo2_rate; - - /* Highest rate per Tx mode */ - u8 max_legacy_rate_idx; - u8 max_siso_rate_idx; - u8 max_mimo2_rate_idx; - - /* Optimal rate based on RSSI and STA caps. - * Used only to reflect link speed to userspace. - */ - struct rs_rate optimal_rate; - unsigned long optimal_rate_mask; - const struct rs_init_rate_info *optimal_rates; - int optimal_nentries; - - u8 missed_rate_counter; - - struct iwl_lq_cmd lq; - struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */ - u8 tx_agg_tid_en; - - /* last tx rate_n_flags */ - u32 last_rate_n_flags; - /* packets destined for this STA are aggregated */ - u8 is_agg; - - /* tx power reduce for this sta */ - int tpc_reduce; - - /* persistent fields - initialized only once - keep last! */ - struct lq_sta_pers { -#ifdef CONFIG_MAC80211_DEBUGFS - u32 dbg_fixed_rate; - u8 dbg_fixed_txp_reduction; - - /* force STBC/BFER/SISO for testing */ - enum rs_ss_force_opt ss_force; -#endif - u8 chains; - s8 chain_signal[IEEE80211_MAX_CHAINS]; - s8 last_rssi; - struct rs_rate_stats tx_stats[RS_COLUMN_COUNT][IWL_RATE_COUNT]; - struct iwl_mvm *drv; - } pers; -}; - -/* Initialize station's rate scaling information after adding station */ -void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - enum ieee80211_band band, bool init); - -/* Notify RS about Tx status */ -void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - int tid, struct ieee80211_tx_info *info); - -/** - * iwl_rate_control_register - Register the rate control algorithm callbacks - * - * Since the rate control algorithm is hardware specific, there is no need - * or reason to place it as a stand alone module. The driver can call - * iwl_rate_control_register in order to register the rate control callbacks - * with the mac80211 subsystem. This should be performed prior to calling - * ieee80211_register_hw - * - */ -int iwl_mvm_rate_control_register(void); - -/** - * iwl_rate_control_unregister - Unregister the rate control callbacks - * - * This should be called after calling ieee80211_unregister_hw, but before - * the driver is unloaded. - */ -void iwl_mvm_rate_control_unregister(void); - -struct iwl_mvm_sta; - -int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, - bool enable); - -#endif /* __rs__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c deleted file mode 100644 index 5b58f5320..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/rx.c +++ /dev/null @@ -1,612 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * 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 <linux/skbuff.h> -#include "iwl-trans.h" -#include "mvm.h" -#include "fw-api.h" - -/* - * iwl_mvm_rx_rx_phy_cmd - REPLY_RX_PHY_CMD handler - * - * Copies the phy information in mvm->last_phy_info, it will be used when the - * actual data will come from the fw in the next packet. - */ -void iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - - memcpy(&mvm->last_phy_info, pkt->data, sizeof(mvm->last_phy_info)); - mvm->ampdu_ref++; - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (mvm->last_phy_info.phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) { - spin_lock(&mvm->drv_stats_lock); - mvm->drv_rx_stats.ampdu_count++; - spin_unlock(&mvm->drv_stats_lock); - } -#endif -} - -/* - * iwl_mvm_pass_packet_to_mac80211 - builds the packet for mac80211 - * - * Adds the rxb to a new skb and give it to mac80211 - */ -static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm, - struct napi_struct *napi, - struct sk_buff *skb, - struct ieee80211_hdr *hdr, u16 len, - u32 ampdu_status, u8 crypt_len, - struct iwl_rx_cmd_buffer *rxb) -{ - unsigned int hdrlen, fraglen; - - /* If frame is small enough to fit in skb->head, pull it completely. - * If not, only pull ieee80211_hdr (including crypto if present, and - * an additional 8 bytes for SNAP/ethertype, see below) so that - * splice() or TCP coalesce are more efficient. - * - * Since, in addition, ieee80211_data_to_8023() always pull in at - * least 8 bytes (possibly more for mesh) we can do the same here - * to save the cost of doing it later. That still doesn't pull in - * the actual IP header since the typical case has a SNAP header. - * If the latter changes (there are efforts in the standards group - * to do so) we should revisit this and ieee80211_data_to_8023(). - */ - hdrlen = (len <= skb_tailroom(skb)) ? len : - sizeof(*hdr) + crypt_len + 8; - - memcpy(skb_put(skb, hdrlen), hdr, hdrlen); - fraglen = len - hdrlen; - - if (fraglen) { - int offset = (void *)hdr + hdrlen - - rxb_addr(rxb) + rxb_offset(rxb); - - skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset, - fraglen, rxb->truesize); - } - - ieee80211_rx_napi(mvm->hw, skb, napi); -} - -/* - * iwl_mvm_get_signal_strength - use new rx PHY INFO API - * values are reported by the fw as positive values - need to negate - * to obtain their dBM. Account for missing antennas by replacing 0 - * values by -256dBm: practically 0 power and a non-feasible 8 bit value. - */ -static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm, - struct iwl_rx_phy_info *phy_info, - struct ieee80211_rx_status *rx_status) -{ - int energy_a, energy_b, energy_c, max_energy; - u32 val; - - val = - le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_ENERGY_ANT_ABC_IDX]); - energy_a = (val & IWL_RX_INFO_ENERGY_ANT_A_MSK) >> - IWL_RX_INFO_ENERGY_ANT_A_POS; - energy_a = energy_a ? -energy_a : S8_MIN; - energy_b = (val & IWL_RX_INFO_ENERGY_ANT_B_MSK) >> - IWL_RX_INFO_ENERGY_ANT_B_POS; - energy_b = energy_b ? -energy_b : S8_MIN; - energy_c = (val & IWL_RX_INFO_ENERGY_ANT_C_MSK) >> - IWL_RX_INFO_ENERGY_ANT_C_POS; - energy_c = energy_c ? -energy_c : S8_MIN; - max_energy = max(energy_a, energy_b); - max_energy = max(max_energy, energy_c); - - IWL_DEBUG_STATS(mvm, "energy In A %d B %d C %d , and max %d\n", - energy_a, energy_b, energy_c, max_energy); - - rx_status->signal = max_energy; - rx_status->chains = (le16_to_cpu(phy_info->phy_flags) & - RX_RES_PHY_FLAGS_ANTENNA) - >> RX_RES_PHY_FLAGS_ANTENNA_POS; - rx_status->chain_signal[0] = energy_a; - rx_status->chain_signal[1] = energy_b; - rx_status->chain_signal[2] = energy_c; -} - -/* - * iwl_mvm_set_mac80211_rx_flag - translate fw status to mac80211 format - * @mvm: the mvm object - * @hdr: 80211 header - * @stats: status in mac80211's format - * @rx_pkt_status: status coming from fw - * - * returns non 0 value if the packet should be dropped - */ -static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm, - struct ieee80211_hdr *hdr, - struct ieee80211_rx_status *stats, - u32 rx_pkt_status, - u8 *crypt_len) -{ - if (!ieee80211_has_protected(hdr->frame_control) || - (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) == - RX_MPDU_RES_STATUS_SEC_NO_ENC) - return 0; - - /* packet was encrypted with unknown alg */ - if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) == - RX_MPDU_RES_STATUS_SEC_ENC_ERR) - return 0; - - switch (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) { - case RX_MPDU_RES_STATUS_SEC_CCM_ENC: - /* alg is CCM: check MIC only */ - if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK)) - return -1; - - stats->flag |= RX_FLAG_DECRYPTED; - *crypt_len = IEEE80211_CCMP_HDR_LEN; - return 0; - - case RX_MPDU_RES_STATUS_SEC_TKIP_ENC: - /* Don't drop the frame and decrypt it in SW */ - if (!(rx_pkt_status & RX_MPDU_RES_STATUS_TTAK_OK)) - return 0; - *crypt_len = IEEE80211_TKIP_IV_LEN; - /* fall through if TTAK OK */ - - case RX_MPDU_RES_STATUS_SEC_WEP_ENC: - if (!(rx_pkt_status & RX_MPDU_RES_STATUS_ICV_OK)) - return -1; - - stats->flag |= RX_FLAG_DECRYPTED; - if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) == - RX_MPDU_RES_STATUS_SEC_WEP_ENC) - *crypt_len = IEEE80211_WEP_IV_LEN; - return 0; - - case RX_MPDU_RES_STATUS_SEC_EXT_ENC: - if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK)) - return -1; - stats->flag |= RX_FLAG_DECRYPTED; - return 0; - - default: - IWL_ERR(mvm, "Unhandled alg: 0x%x\n", rx_pkt_status); - } - - return 0; -} - -static void iwl_mvm_rx_csum(struct ieee80211_sta *sta, - struct sk_buff *skb, - u32 status) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); - - if (mvmvif->features & NETIF_F_RXCSUM && - status & RX_MPDU_RES_STATUS_CSUM_DONE && - status & RX_MPDU_RES_STATUS_CSUM_OK) - skb->ip_summed = CHECKSUM_UNNECESSARY; -} - -/* - * iwl_mvm_rx_rx_mpdu - REPLY_RX_MPDU_CMD handler - * - * Handles the actual data of the Rx packet from the fw - */ -void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, - struct iwl_rx_cmd_buffer *rxb) -{ - struct ieee80211_hdr *hdr; - struct ieee80211_rx_status *rx_status; - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_rx_phy_info *phy_info; - struct iwl_rx_mpdu_res_start *rx_res; - struct ieee80211_sta *sta; - struct sk_buff *skb; - u32 len; - u32 ampdu_status; - u32 rate_n_flags; - u32 rx_pkt_status; - u8 crypt_len = 0; - - phy_info = &mvm->last_phy_info; - rx_res = (struct iwl_rx_mpdu_res_start *)pkt->data; - hdr = (struct ieee80211_hdr *)(pkt->data + sizeof(*rx_res)); - len = le16_to_cpu(rx_res->byte_count); - rx_pkt_status = le32_to_cpup((__le32 *) - (pkt->data + sizeof(*rx_res) + len)); - - /* Dont use dev_alloc_skb(), we'll have enough headroom once - * ieee80211_hdr pulled. - */ - skb = alloc_skb(128, GFP_ATOMIC); - if (!skb) { - IWL_ERR(mvm, "alloc_skb failed\n"); - return; - } - - rx_status = IEEE80211_SKB_RXCB(skb); - - /* - * drop the packet if it has failed being decrypted by HW - */ - if (iwl_mvm_set_mac80211_rx_flag(mvm, hdr, rx_status, rx_pkt_status, - &crypt_len)) { - IWL_DEBUG_DROP(mvm, "Bad decryption results 0x%08x\n", - rx_pkt_status); - kfree_skb(skb); - return; - } - - /* - * Keep packets with CRC errors (and with overrun) for monitor mode - * (otherwise the firmware discards them) but mark them as bad. - */ - if (!(rx_pkt_status & RX_MPDU_RES_STATUS_CRC_OK) || - !(rx_pkt_status & RX_MPDU_RES_STATUS_OVERRUN_OK)) { - IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status); - rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; - } - - /* This will be used in several places later */ - rate_n_flags = le32_to_cpu(phy_info->rate_n_flags); - - /* rx_status carries information about the packet to mac80211 */ - rx_status->mactime = le64_to_cpu(phy_info->timestamp); - rx_status->device_timestamp = le32_to_cpu(phy_info->system_timestamp); - rx_status->band = - (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_BAND_24)) ? - IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; - rx_status->freq = - ieee80211_channel_to_frequency(le16_to_cpu(phy_info->channel), - rx_status->band); - /* - * TSF as indicated by the fw is at INA time, but mac80211 expects the - * TSF at the beginning of the MPDU. - */ - /*rx_status->flag |= RX_FLAG_MACTIME_MPDU;*/ - - iwl_mvm_get_signal_strength(mvm, phy_info, rx_status); - - IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status->signal, - (unsigned long long)rx_status->mactime); - - rcu_read_lock(); - /* - * We have tx blocked stations (with CS bit). If we heard frames from - * a blocked station on a new channel we can TX to it again. - */ - if (unlikely(mvm->csa_tx_block_bcn_timeout)) { - sta = ieee80211_find_sta( - rcu_dereference(mvm->csa_tx_blocked_vif), hdr->addr2); - if (sta) - iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false); - } - - /* This is fine since we don't support multiple AP interfaces */ - sta = ieee80211_find_sta_by_ifaddr(mvm->hw, hdr->addr2, NULL); - if (sta) { - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - - rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status); - - if (iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_RSSI) && - ieee80211_is_beacon(hdr->frame_control)) { - struct iwl_fw_dbg_trigger_tlv *trig; - struct iwl_fw_dbg_trigger_low_rssi *rssi_trig; - bool trig_check; - s32 rssi; - - trig = iwl_fw_dbg_get_trigger(mvm->fw, - FW_DBG_TRIGGER_RSSI); - rssi_trig = (void *)trig->data; - rssi = le32_to_cpu(rssi_trig->rssi); - - trig_check = - iwl_fw_dbg_trigger_check_stop(mvm, mvmsta->vif, - trig); - if (trig_check && rx_status->signal < rssi) - iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL); - } - } - - if (sta && ieee80211_is_data(hdr->frame_control)) - iwl_mvm_rx_csum(sta, skb, rx_pkt_status); - - rcu_read_unlock(); - - /* set the preamble flag if appropriate */ - if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_SHORT_PREAMBLE)) - rx_status->flag |= RX_FLAG_SHORTPRE; - - if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) { - /* - * We know which subframes of an A-MPDU belong - * together since we get a single PHY response - * from the firmware for all of them - */ - rx_status->flag |= RX_FLAG_AMPDU_DETAILS; - rx_status->ampdu_reference = mvm->ampdu_ref; - } - - /* Set up the HT phy flags */ - switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { - case RATE_MCS_CHAN_WIDTH_20: - break; - case RATE_MCS_CHAN_WIDTH_40: - rx_status->flag |= RX_FLAG_40MHZ; - break; - case RATE_MCS_CHAN_WIDTH_80: - rx_status->vht_flag |= RX_VHT_FLAG_80MHZ; - break; - case RATE_MCS_CHAN_WIDTH_160: - rx_status->vht_flag |= RX_VHT_FLAG_160MHZ; - break; - } - if (rate_n_flags & RATE_MCS_SGI_MSK) - rx_status->flag |= RX_FLAG_SHORT_GI; - if (rate_n_flags & RATE_HT_MCS_GF_MSK) - rx_status->flag |= RX_FLAG_HT_GF; - if (rate_n_flags & RATE_MCS_LDPC_MSK) - rx_status->flag |= RX_FLAG_LDPC; - if (rate_n_flags & RATE_MCS_HT_MSK) { - u8 stbc = (rate_n_flags & RATE_MCS_HT_STBC_MSK) >> - RATE_MCS_STBC_POS; - rx_status->flag |= RX_FLAG_HT; - rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK; - rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT; - } else if (rate_n_flags & RATE_MCS_VHT_MSK) { - u8 stbc = (rate_n_flags & RATE_MCS_VHT_STBC_MSK) >> - RATE_MCS_STBC_POS; - rx_status->vht_nss = - ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> - RATE_VHT_MCS_NSS_POS) + 1; - rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK; - rx_status->flag |= RX_FLAG_VHT; - rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT; - if (rate_n_flags & RATE_MCS_BF_MSK) - rx_status->vht_flag |= RX_VHT_FLAG_BF; - } else { - rx_status->rate_idx = - iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags, - rx_status->band); - } - -#ifdef CONFIG_IWLWIFI_DEBUGFS - iwl_mvm_update_frame_stats(mvm, rate_n_flags, - rx_status->flag & RX_FLAG_AMPDU_DETAILS); -#endif - iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, hdr, len, ampdu_status, - crypt_len, rxb); -} - -static void iwl_mvm_update_rx_statistics(struct iwl_mvm *mvm, - struct mvm_statistics_rx *rx_stats) -{ - lockdep_assert_held(&mvm->mutex); - - mvm->rx_stats = *rx_stats; -} - -struct iwl_mvm_stat_data { - struct iwl_mvm *mvm; - __le32 mac_id; - u8 beacon_filter_average_energy; - struct mvm_statistics_general_v8 *general; -}; - -static void iwl_mvm_stat_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_stat_data *data = _data; - struct iwl_mvm *mvm = data->mvm; - int sig = -data->beacon_filter_average_energy; - int last_event; - int thold = vif->bss_conf.cqm_rssi_thold; - int hyst = vif->bss_conf.cqm_rssi_hyst; - u16 id = le32_to_cpu(data->mac_id); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - /* This doesn't need the MAC ID check since it's not taking the - * data copied into the "data" struct, but rather the data from - * the notification directly. - */ - if (data->general) { - mvmvif->beacon_stats.num_beacons = - le32_to_cpu(data->general->beacon_counter[mvmvif->id]); - mvmvif->beacon_stats.avg_signal = - -data->general->beacon_average_energy[mvmvif->id]; - } - - if (mvmvif->id != id) - return; - - if (vif->type != NL80211_IFTYPE_STATION) - return; - - if (sig == 0) { - IWL_DEBUG_RX(mvm, "RSSI is 0 - skip signal based decision\n"); - return; - } - - mvmvif->bf_data.ave_beacon_signal = sig; - - /* BT Coex */ - if (mvmvif->bf_data.bt_coex_min_thold != - mvmvif->bf_data.bt_coex_max_thold) { - last_event = mvmvif->bf_data.last_bt_coex_event; - if (sig > mvmvif->bf_data.bt_coex_max_thold && - (last_event <= mvmvif->bf_data.bt_coex_min_thold || - last_event == 0)) { - mvmvif->bf_data.last_bt_coex_event = sig; - IWL_DEBUG_RX(mvm, "cqm_iterator bt coex high %d\n", - sig); - iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_HIGH); - } else if (sig < mvmvif->bf_data.bt_coex_min_thold && - (last_event >= mvmvif->bf_data.bt_coex_max_thold || - last_event == 0)) { - mvmvif->bf_data.last_bt_coex_event = sig; - IWL_DEBUG_RX(mvm, "cqm_iterator bt coex low %d\n", - sig); - iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_LOW); - } - } - - if (!(vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) - return; - - /* CQM Notification */ - last_event = mvmvif->bf_data.last_cqm_event; - if (thold && sig < thold && (last_event == 0 || - sig < last_event - hyst)) { - mvmvif->bf_data.last_cqm_event = sig; - IWL_DEBUG_RX(mvm, "cqm_iterator cqm low %d\n", - sig); - ieee80211_cqm_rssi_notify( - vif, - NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, - GFP_KERNEL); - } else if (sig > thold && - (last_event == 0 || sig > last_event + hyst)) { - mvmvif->bf_data.last_cqm_event = sig; - IWL_DEBUG_RX(mvm, "cqm_iterator cqm high %d\n", - sig); - ieee80211_cqm_rssi_notify( - vif, - NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, - GFP_KERNEL); - } -} - -static inline void -iwl_mvm_rx_stats_check_trigger(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) -{ - struct iwl_fw_dbg_trigger_tlv *trig; - struct iwl_fw_dbg_trigger_stats *trig_stats; - u32 trig_offset, trig_thold; - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_STATS)) - return; - - trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_STATS); - trig_stats = (void *)trig->data; - - if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig)) - return; - - trig_offset = le32_to_cpu(trig_stats->stop_offset); - trig_thold = le32_to_cpu(trig_stats->stop_threshold); - - if (WARN_ON_ONCE(trig_offset >= iwl_rx_packet_payload_len(pkt))) - return; - - if (le32_to_cpup((__le32 *) (pkt->data + trig_offset)) < trig_thold) - return; - - iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL); -} - -void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm, - struct iwl_rx_packet *pkt) -{ - struct iwl_notif_statistics_v10 *stats = (void *)&pkt->data; - struct iwl_mvm_stat_data data = { - .mvm = mvm, - }; - u32 temperature; - - if (iwl_rx_packet_payload_len(pkt) != sizeof(*stats)) - goto invalid; - - temperature = le32_to_cpu(stats->general.radio_temperature); - data.mac_id = stats->rx.general.mac_id; - data.beacon_filter_average_energy = - stats->general.beacon_filter_average_energy; - - iwl_mvm_update_rx_statistics(mvm, &stats->rx); - - mvm->radio_stats.rx_time = le64_to_cpu(stats->general.rx_time); - mvm->radio_stats.tx_time = le64_to_cpu(stats->general.tx_time); - mvm->radio_stats.on_time_rf = - le64_to_cpu(stats->general.on_time_rf); - mvm->radio_stats.on_time_scan = - le64_to_cpu(stats->general.on_time_scan); - - data.general = &stats->general; - - iwl_mvm_rx_stats_check_trigger(mvm, pkt); - - ieee80211_iterate_active_interfaces(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_stat_iterator, - &data); - return; - invalid: - IWL_ERR(mvm, "received invalid statistics size (%d)!\n", - iwl_rx_packet_payload_len(pkt)); -} - -void iwl_mvm_rx_statistics(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) -{ - iwl_mvm_handle_rx_statistics(mvm, rxb_addr(rxb)); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c deleted file mode 100644 index 8215d7405..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ /dev/null @@ -1,1556 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * 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 <linux/etherdevice.h> -#include <net/mac80211.h> - -#include "mvm.h" -#include "fw-api-scan.h" - -#define IWL_DENSE_EBS_SCAN_RATIO 5 -#define IWL_SPARSE_EBS_SCAN_RATIO 1 - -enum iwl_mvm_scan_type { - IWL_SCAN_TYPE_UNASSOC, - IWL_SCAN_TYPE_WILD, - IWL_SCAN_TYPE_MILD, - IWL_SCAN_TYPE_FRAGMENTED, -}; - -enum iwl_mvm_traffic_load { - IWL_MVM_TRAFFIC_LOW, - IWL_MVM_TRAFFIC_MEDIUM, - IWL_MVM_TRAFFIC_HIGH, -}; - -struct iwl_mvm_scan_timing_params { - u32 dwell_active; - u32 dwell_passive; - u32 dwell_fragmented; - u32 suspend_time; - u32 max_out_time; -}; - -static struct iwl_mvm_scan_timing_params scan_timing[] = { - [IWL_SCAN_TYPE_UNASSOC] = { - .dwell_active = 10, - .dwell_passive = 110, - .dwell_fragmented = 44, - .suspend_time = 0, - .max_out_time = 0, - }, - [IWL_SCAN_TYPE_WILD] = { - .dwell_active = 10, - .dwell_passive = 110, - .dwell_fragmented = 44, - .suspend_time = 30, - .max_out_time = 120, - }, - [IWL_SCAN_TYPE_MILD] = { - .dwell_active = 10, - .dwell_passive = 110, - .dwell_fragmented = 44, - .suspend_time = 120, - .max_out_time = 120, - }, - [IWL_SCAN_TYPE_FRAGMENTED] = { - .dwell_active = 10, - .dwell_passive = 110, - .dwell_fragmented = 44, - .suspend_time = 95, - .max_out_time = 44, - }, -}; - -struct iwl_mvm_scan_params { - enum iwl_mvm_scan_type type; - u32 n_channels; - u16 delay; - int n_ssids; - struct cfg80211_ssid *ssids; - struct ieee80211_channel **channels; - u32 flags; - u8 *mac_addr; - u8 *mac_addr_mask; - bool no_cck; - bool pass_all; - int n_match_sets; - struct iwl_scan_probe_req preq; - struct cfg80211_match_set *match_sets; - int n_scan_plans; - struct cfg80211_sched_scan_plan *scan_plans; -}; - -static u8 iwl_mvm_scan_rx_ant(struct iwl_mvm *mvm) -{ - if (mvm->scan_rx_ant != ANT_NONE) - return mvm->scan_rx_ant; - return iwl_mvm_get_valid_rx_ant(mvm); -} - -static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm) -{ - u16 rx_chain; - u8 rx_ant; - - rx_ant = iwl_mvm_scan_rx_ant(mvm); - rx_chain = rx_ant << PHY_RX_CHAIN_VALID_POS; - rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS; - rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_SEL_POS; - rx_chain |= 0x1 << PHY_RX_CHAIN_DRIVER_FORCE_POS; - return cpu_to_le16(rx_chain); -} - -static __le32 iwl_mvm_scan_rxon_flags(enum ieee80211_band band) -{ - if (band == IEEE80211_BAND_2GHZ) - return cpu_to_le32(PHY_BAND_24); - else - return cpu_to_le32(PHY_BAND_5); -} - -static inline __le32 -iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum ieee80211_band band, - bool no_cck) -{ - u32 tx_ant; - - mvm->scan_last_antenna_idx = - iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm), - mvm->scan_last_antenna_idx); - tx_ant = BIT(mvm->scan_last_antenna_idx) << RATE_MCS_ANT_POS; - - if (band == IEEE80211_BAND_2GHZ && !no_cck) - return cpu_to_le32(IWL_RATE_1M_PLCP | RATE_MCS_CCK_MSK | - tx_ant); - else - return cpu_to_le32(IWL_RATE_6M_PLCP | tx_ant); -} - -static void iwl_mvm_scan_condition_iterator(void *data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int *global_cnt = data; - - if (vif->type != NL80211_IFTYPE_P2P_DEVICE && mvmvif->phy_ctxt && - mvmvif->phy_ctxt->id < MAX_PHYS) - *global_cnt += 1; -} - -static enum iwl_mvm_traffic_load iwl_mvm_get_traffic_load(struct iwl_mvm *mvm) -{ - return IWL_MVM_TRAFFIC_LOW; -} - -static enum -iwl_mvm_scan_type iwl_mvm_get_scan_type(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_mvm_scan_params *params) -{ - int global_cnt = 0; - enum iwl_mvm_traffic_load load; - bool low_latency; - - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_scan_condition_iterator, - &global_cnt); - if (!global_cnt) - return IWL_SCAN_TYPE_UNASSOC; - - load = iwl_mvm_get_traffic_load(mvm); - low_latency = iwl_mvm_low_latency(mvm); - - if ((load == IWL_MVM_TRAFFIC_HIGH || low_latency) && - vif->type != NL80211_IFTYPE_P2P_DEVICE && - fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_FRAGMENTED_SCAN)) - return IWL_SCAN_TYPE_FRAGMENTED; - - if (load >= IWL_MVM_TRAFFIC_MEDIUM || low_latency) - return IWL_SCAN_TYPE_MILD; - - return IWL_SCAN_TYPE_WILD; -} - -static inline bool iwl_mvm_rrm_scan_needed(struct iwl_mvm *mvm) -{ - /* require rrm scan whenever the fw supports it */ - return fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT); -} - -static int iwl_mvm_max_scan_ie_fw_cmd_room(struct iwl_mvm *mvm) -{ - int max_probe_len; - - max_probe_len = SCAN_OFFLOAD_PROBE_REQ_SIZE; - - /* we create the 802.11 header and SSID element */ - max_probe_len -= 24 + 2; - - /* DS parameter set element is added on 2.4GHZ band if required */ - if (iwl_mvm_rrm_scan_needed(mvm)) - max_probe_len -= 3; - - return max_probe_len; -} - -int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm) -{ - int max_ie_len = iwl_mvm_max_scan_ie_fw_cmd_room(mvm); - - /* TODO: [BUG] This function should return the maximum allowed size of - * scan IEs, however the LMAC scan api contains both 2GHZ and 5GHZ IEs - * in the same command. So the correct implementation of this function - * is just iwl_mvm_max_scan_ie_fw_cmd_room() / 2. Currently the scan - * command has only 512 bytes and it would leave us with about 240 - * bytes for scan IEs, which is clearly not enough. So meanwhile - * we will report an incorrect value. This may result in a failure to - * issue a scan in unified_scan_lmac and unified_sched_scan_lmac - * functions with -ENOBUFS, if a large enough probe will be provided. - */ - return max_ie_len; -} - -static u8 *iwl_mvm_dump_channel_list(struct iwl_scan_results_notif *res, - int num_res, u8 *buf, size_t buf_size) -{ - int i; - u8 *pos = buf, *end = buf + buf_size; - - for (i = 0; pos < end && i < num_res; i++) - pos += snprintf(pos, end - pos, " %u", res[i].channel); - - /* terminate the string in case the buffer was too short */ - *(buf + buf_size - 1) = '\0'; - - return buf; -} - -void iwl_mvm_rx_lmac_scan_iter_complete_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_lmac_scan_complete_notif *notif = (void *)pkt->data; - u8 buf[256]; - - IWL_DEBUG_SCAN(mvm, - "Scan offload iteration complete: status=0x%x scanned channels=%d channels list: %s\n", - notif->status, notif->scanned_channels, - iwl_mvm_dump_channel_list(notif->results, - notif->scanned_channels, buf, - sizeof(buf))); -} - -void iwl_mvm_rx_scan_match_found(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n"); - ieee80211_sched_scan_results(mvm->hw); -} - -static const char *iwl_mvm_ebs_status_str(enum iwl_scan_ebs_status status) -{ - switch (status) { - case IWL_SCAN_EBS_SUCCESS: - return "successful"; - case IWL_SCAN_EBS_INACTIVE: - return "inactive"; - case IWL_SCAN_EBS_FAILED: - case IWL_SCAN_EBS_CHAN_NOT_FOUND: - default: - return "failed"; - } -} - -void iwl_mvm_rx_lmac_scan_complete_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_periodic_scan_complete *scan_notif = (void *)pkt->data; - bool aborted = (scan_notif->status == IWL_SCAN_OFFLOAD_ABORTED); - - /* scan status must be locked for proper checking */ - lockdep_assert_held(&mvm->mutex); - - /* We first check if we were stopping a scan, in which case we - * just clear the stopping flag. Then we check if it was a - * firmware initiated stop, in which case we need to inform - * mac80211. - * Note that we can have a stopping and a running scan - * simultaneously, but we can't have two different types of - * scans stopping or running at the same time (since LMAC - * doesn't support it). - */ - - if (mvm->scan_status & IWL_MVM_SCAN_STOPPING_SCHED) { - WARN_ON_ONCE(mvm->scan_status & IWL_MVM_SCAN_STOPPING_REGULAR); - - IWL_DEBUG_SCAN(mvm, "Scheduled scan %s, EBS status %s\n", - aborted ? "aborted" : "completed", - iwl_mvm_ebs_status_str(scan_notif->ebs_status)); - IWL_DEBUG_SCAN(mvm, - "Last line %d, Last iteration %d, Time after last iteration %d\n", - scan_notif->last_schedule_line, - scan_notif->last_schedule_iteration, - __le32_to_cpu(scan_notif->time_after_last_iter)); - - mvm->scan_status &= ~IWL_MVM_SCAN_STOPPING_SCHED; - } else if (mvm->scan_status & IWL_MVM_SCAN_STOPPING_REGULAR) { - IWL_DEBUG_SCAN(mvm, "Regular scan %s, EBS status %s\n", - aborted ? "aborted" : "completed", - iwl_mvm_ebs_status_str(scan_notif->ebs_status)); - - mvm->scan_status &= ~IWL_MVM_SCAN_STOPPING_REGULAR; - } else if (mvm->scan_status & IWL_MVM_SCAN_SCHED) { - WARN_ON_ONCE(mvm->scan_status & IWL_MVM_SCAN_REGULAR); - - IWL_DEBUG_SCAN(mvm, "Scheduled scan %s, EBS status %s\n", - aborted ? "aborted" : "completed", - iwl_mvm_ebs_status_str(scan_notif->ebs_status)); - IWL_DEBUG_SCAN(mvm, - "Last line %d, Last iteration %d, Time after last iteration %d (FW)\n", - scan_notif->last_schedule_line, - scan_notif->last_schedule_iteration, - __le32_to_cpu(scan_notif->time_after_last_iter)); - - mvm->scan_status &= ~IWL_MVM_SCAN_SCHED; - ieee80211_sched_scan_stopped(mvm->hw); - } else if (mvm->scan_status & IWL_MVM_SCAN_REGULAR) { - IWL_DEBUG_SCAN(mvm, "Regular scan %s, EBS status %s (FW)\n", - aborted ? "aborted" : "completed", - iwl_mvm_ebs_status_str(scan_notif->ebs_status)); - - mvm->scan_status &= ~IWL_MVM_SCAN_REGULAR; - ieee80211_scan_completed(mvm->hw, - scan_notif->status == IWL_SCAN_OFFLOAD_ABORTED); - iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); - } - - mvm->last_ebs_successful = - scan_notif->ebs_status == IWL_SCAN_EBS_SUCCESS || - scan_notif->ebs_status == IWL_SCAN_EBS_INACTIVE; -} - -static int iwl_ssid_exist(u8 *ssid, u8 ssid_len, struct iwl_ssid_ie *ssid_list) -{ - int i; - - for (i = 0; i < PROBE_OPTION_MAX; i++) { - if (!ssid_list[i].len) - break; - if (ssid_list[i].len == ssid_len && - !memcmp(ssid_list->ssid, ssid, ssid_len)) - return i; - } - return -1; -} - -/* We insert the SSIDs in an inverted order, because the FW will - * invert it back. - */ -static void iwl_scan_build_ssids(struct iwl_mvm_scan_params *params, - struct iwl_ssid_ie *ssids, - u32 *ssid_bitmap) -{ - int i, j; - int index; - - /* - * copy SSIDs from match list. - * iwl_config_sched_scan_profiles() uses the order of these ssids to - * config match list. - */ - for (i = 0, j = params->n_match_sets - 1; - j >= 0 && i < PROBE_OPTION_MAX; - i++, j--) { - /* skip empty SSID matchsets */ - if (!params->match_sets[j].ssid.ssid_len) - continue; - ssids[i].id = WLAN_EID_SSID; - ssids[i].len = params->match_sets[j].ssid.ssid_len; - memcpy(ssids[i].ssid, params->match_sets[j].ssid.ssid, - ssids[i].len); - } - - /* add SSIDs from scan SSID list */ - *ssid_bitmap = 0; - for (j = params->n_ssids - 1; - j >= 0 && i < PROBE_OPTION_MAX; - i++, j--) { - index = iwl_ssid_exist(params->ssids[j].ssid, - params->ssids[j].ssid_len, - ssids); - if (index < 0) { - ssids[i].id = WLAN_EID_SSID; - ssids[i].len = params->ssids[j].ssid_len; - memcpy(ssids[i].ssid, params->ssids[j].ssid, - ssids[i].len); - *ssid_bitmap |= BIT(i); - } else { - *ssid_bitmap |= BIT(index); - } - } -} - -static int -iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm, - struct cfg80211_sched_scan_request *req) -{ - struct iwl_scan_offload_profile *profile; - struct iwl_scan_offload_profile_cfg *profile_cfg; - struct iwl_scan_offload_blacklist *blacklist; - struct iwl_host_cmd cmd = { - .id = SCAN_OFFLOAD_UPDATE_PROFILES_CMD, - .len[1] = sizeof(*profile_cfg), - .dataflags[0] = IWL_HCMD_DFL_NOCOPY, - .dataflags[1] = IWL_HCMD_DFL_NOCOPY, - }; - int blacklist_len; - int i; - int ret; - - if (WARN_ON(req->n_match_sets > IWL_SCAN_MAX_PROFILES)) - return -EIO; - - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SHORT_BL) - blacklist_len = IWL_SCAN_SHORT_BLACKLIST_LEN; - else - blacklist_len = IWL_SCAN_MAX_BLACKLIST_LEN; - - blacklist = kzalloc(sizeof(*blacklist) * blacklist_len, GFP_KERNEL); - if (!blacklist) - return -ENOMEM; - - profile_cfg = kzalloc(sizeof(*profile_cfg), GFP_KERNEL); - if (!profile_cfg) { - ret = -ENOMEM; - goto free_blacklist; - } - - cmd.data[0] = blacklist; - cmd.len[0] = sizeof(*blacklist) * blacklist_len; - cmd.data[1] = profile_cfg; - - /* No blacklist configuration */ - - profile_cfg->num_profiles = req->n_match_sets; - profile_cfg->active_clients = SCAN_CLIENT_SCHED_SCAN; - profile_cfg->pass_match = SCAN_CLIENT_SCHED_SCAN; - profile_cfg->match_notify = SCAN_CLIENT_SCHED_SCAN; - if (!req->n_match_sets || !req->match_sets[0].ssid.ssid_len) - profile_cfg->any_beacon_notify = SCAN_CLIENT_SCHED_SCAN; - - for (i = 0; i < req->n_match_sets; i++) { - profile = &profile_cfg->profiles[i]; - profile->ssid_index = i; - /* Support any cipher and auth algorithm */ - profile->unicast_cipher = 0xff; - profile->auth_alg = 0xff; - profile->network_type = IWL_NETWORK_TYPE_ANY; - profile->band_selection = IWL_SCAN_OFFLOAD_SELECT_ANY; - profile->client_bitmap = SCAN_CLIENT_SCHED_SCAN; - } - - IWL_DEBUG_SCAN(mvm, "Sending scheduled scan profile config\n"); - - ret = iwl_mvm_send_cmd(mvm, &cmd); - kfree(profile_cfg); -free_blacklist: - kfree(blacklist); - - return ret; -} - -static bool iwl_mvm_scan_pass_all(struct iwl_mvm *mvm, - struct cfg80211_sched_scan_request *req) -{ - if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) { - IWL_DEBUG_SCAN(mvm, - "Sending scheduled scan with filtering, n_match_sets %d\n", - req->n_match_sets); - return false; - } - - IWL_DEBUG_SCAN(mvm, "Sending Scheduled scan without filtering\n"); - return true; -} - -static int iwl_mvm_lmac_scan_abort(struct iwl_mvm *mvm) -{ - int ret; - struct iwl_host_cmd cmd = { - .id = SCAN_OFFLOAD_ABORT_CMD, - }; - u32 status; - - ret = iwl_mvm_send_cmd_status(mvm, &cmd, &status); - if (ret) - return ret; - - if (status != CAN_ABORT_STATUS) { - /* - * The scan abort will return 1 for success or - * 2 for "failure". A failure condition can be - * due to simply not being in an active scan which - * can occur if we send the scan abort before the - * microcode has notified us that a scan is completed. - */ - IWL_DEBUG_SCAN(mvm, "SCAN OFFLOAD ABORT ret %d.\n", status); - ret = -ENOENT; - } - - return ret; -} - -static void iwl_mvm_scan_fill_tx_cmd(struct iwl_mvm *mvm, - struct iwl_scan_req_tx_cmd *tx_cmd, - bool no_cck) -{ - tx_cmd[0].tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL | - TX_CMD_FLG_BT_DIS); - tx_cmd[0].rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm, - IEEE80211_BAND_2GHZ, - no_cck); - tx_cmd[0].sta_id = mvm->aux_sta.sta_id; - - tx_cmd[1].tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL | - TX_CMD_FLG_BT_DIS); - tx_cmd[1].rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm, - IEEE80211_BAND_5GHZ, - no_cck); - tx_cmd[1].sta_id = mvm->aux_sta.sta_id; -} - -static void -iwl_mvm_lmac_scan_cfg_channels(struct iwl_mvm *mvm, - struct ieee80211_channel **channels, - int n_channels, u32 ssid_bitmap, - struct iwl_scan_req_lmac *cmd) -{ - struct iwl_scan_channel_cfg_lmac *channel_cfg = (void *)&cmd->data; - int i; - - for (i = 0; i < n_channels; i++) { - channel_cfg[i].channel_num = - cpu_to_le16(channels[i]->hw_value); - channel_cfg[i].iter_count = cpu_to_le16(1); - channel_cfg[i].iter_interval = 0; - channel_cfg[i].flags = - cpu_to_le32(IWL_UNIFIED_SCAN_CHANNEL_PARTIAL | - ssid_bitmap); - } -} - -static u8 *iwl_mvm_copy_and_insert_ds_elem(struct iwl_mvm *mvm, const u8 *ies, - size_t len, u8 *const pos) -{ - static const u8 before_ds_params[] = { - WLAN_EID_SSID, - WLAN_EID_SUPP_RATES, - WLAN_EID_REQUEST, - WLAN_EID_EXT_SUPP_RATES, - }; - size_t offs; - u8 *newpos = pos; - - if (!iwl_mvm_rrm_scan_needed(mvm)) { - memcpy(newpos, ies, len); - return newpos + len; - } - - offs = ieee80211_ie_split(ies, len, - before_ds_params, - ARRAY_SIZE(before_ds_params), - 0); - - memcpy(newpos, ies, offs); - newpos += offs; - - /* Add a placeholder for DS Parameter Set element */ - *newpos++ = WLAN_EID_DS_PARAMS; - *newpos++ = 1; - *newpos++ = 0; - - memcpy(newpos, ies + offs, len - offs); - newpos += len - offs; - - return newpos; -} - -static void -iwl_mvm_build_scan_probe(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_scan_ies *ies, - struct iwl_mvm_scan_params *params) -{ - struct ieee80211_mgmt *frame = (void *)params->preq.buf; - u8 *pos, *newpos; - const u8 *mac_addr = params->flags & NL80211_SCAN_FLAG_RANDOM_ADDR ? - params->mac_addr : NULL; - - /* - * Unfortunately, right now the offload scan doesn't support randomising - * within the firmware, so until the firmware API is ready we implement - * it in the driver. This means that the scan iterations won't really be - * random, only when it's restarted, but at least that helps a bit. - */ - if (mac_addr) - get_random_mask_addr(frame->sa, mac_addr, - params->mac_addr_mask); - else - memcpy(frame->sa, vif->addr, ETH_ALEN); - - frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); - eth_broadcast_addr(frame->da); - eth_broadcast_addr(frame->bssid); - frame->seq_ctrl = 0; - - pos = frame->u.probe_req.variable; - *pos++ = WLAN_EID_SSID; - *pos++ = 0; - - params->preq.mac_header.offset = 0; - params->preq.mac_header.len = cpu_to_le16(24 + 2); - - /* Insert ds parameter set element on 2.4 GHz band */ - newpos = iwl_mvm_copy_and_insert_ds_elem(mvm, - ies->ies[IEEE80211_BAND_2GHZ], - ies->len[IEEE80211_BAND_2GHZ], - pos); - params->preq.band_data[0].offset = cpu_to_le16(pos - params->preq.buf); - params->preq.band_data[0].len = cpu_to_le16(newpos - pos); - pos = newpos; - - memcpy(pos, ies->ies[IEEE80211_BAND_5GHZ], - ies->len[IEEE80211_BAND_5GHZ]); - params->preq.band_data[1].offset = cpu_to_le16(pos - params->preq.buf); - params->preq.band_data[1].len = - cpu_to_le16(ies->len[IEEE80211_BAND_5GHZ]); - pos += ies->len[IEEE80211_BAND_5GHZ]; - - memcpy(pos, ies->common_ies, ies->common_ie_len); - params->preq.common_data.offset = cpu_to_le16(pos - params->preq.buf); - params->preq.common_data.len = cpu_to_le16(ies->common_ie_len); -} - -static __le32 iwl_mvm_scan_priority(struct iwl_mvm *mvm, - enum iwl_scan_priority_ext prio) -{ - if (fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_EXT_SCAN_PRIORITY)) - return cpu_to_le32(prio); - - if (prio <= IWL_SCAN_PRIORITY_EXT_2) - return cpu_to_le32(IWL_SCAN_PRIORITY_LOW); - - if (prio <= IWL_SCAN_PRIORITY_EXT_4) - return cpu_to_le32(IWL_SCAN_PRIORITY_MEDIUM); - - return cpu_to_le32(IWL_SCAN_PRIORITY_HIGH); -} - -static void iwl_mvm_scan_lmac_dwell(struct iwl_mvm *mvm, - struct iwl_scan_req_lmac *cmd, - struct iwl_mvm_scan_params *params) -{ - cmd->active_dwell = scan_timing[params->type].dwell_active; - cmd->passive_dwell = scan_timing[params->type].dwell_passive; - cmd->fragmented_dwell = scan_timing[params->type].dwell_fragmented; - cmd->max_out_time = cpu_to_le32(scan_timing[params->type].max_out_time); - cmd->suspend_time = cpu_to_le32(scan_timing[params->type].suspend_time); - cmd->scan_prio = iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_6); -} - -static inline bool iwl_mvm_scan_fits(struct iwl_mvm *mvm, int n_ssids, - struct ieee80211_scan_ies *ies, - int n_channels) -{ - return ((n_ssids <= PROBE_OPTION_MAX) && - (n_channels <= mvm->fw->ucode_capa.n_scan_channels) & - (ies->common_ie_len + - ies->len[NL80211_BAND_2GHZ] + - ies->len[NL80211_BAND_5GHZ] <= - iwl_mvm_max_scan_ie_fw_cmd_room(mvm))); -} - -static inline bool iwl_mvm_scan_use_ebs(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - const struct iwl_ucode_capabilities *capa = &mvm->fw->ucode_capa; - - /* We can only use EBS if: - * 1. the feature is supported; - * 2. the last EBS was successful; - * 3. if only single scan, the single scan EBS API is supported; - * 4. it's not a p2p find operation. - */ - return ((capa->flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT) && - mvm->last_ebs_successful && - vif->type != NL80211_IFTYPE_P2P_DEVICE); -} - -static int iwl_mvm_scan_lmac_flags(struct iwl_mvm *mvm, - struct iwl_mvm_scan_params *params) -{ - int flags = 0; - - if (params->n_ssids == 0) - flags |= IWL_MVM_LMAC_SCAN_FLAG_PASSIVE; - - if (params->n_ssids == 1 && params->ssids[0].ssid_len != 0) - flags |= IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION; - - if (params->type == IWL_SCAN_TYPE_FRAGMENTED) - flags |= IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED; - - if (iwl_mvm_rrm_scan_needed(mvm)) - flags |= IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED; - - if (params->pass_all) - flags |= IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL; - else - flags |= IWL_MVM_LMAC_SCAN_FLAG_MATCH; - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (mvm->scan_iter_notif_enabled) - flags |= IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE; -#endif - - return flags; -} - -static int iwl_mvm_scan_lmac(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_mvm_scan_params *params) -{ - struct iwl_scan_req_lmac *cmd = mvm->scan_cmd; - struct iwl_scan_probe_req *preq = - (void *)(cmd->data + sizeof(struct iwl_scan_channel_cfg_lmac) * - mvm->fw->ucode_capa.n_scan_channels); - u32 ssid_bitmap = 0; - int i; - - lockdep_assert_held(&mvm->mutex); - - memset(cmd, 0, ksize(cmd)); - - if (WARN_ON(params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS)) - return -EINVAL; - - iwl_mvm_scan_lmac_dwell(mvm, cmd, params); - - cmd->rx_chain_select = iwl_mvm_scan_rx_chain(mvm); - cmd->iter_num = cpu_to_le32(1); - cmd->n_channels = (u8)params->n_channels; - - cmd->delay = cpu_to_le32(params->delay); - - cmd->scan_flags = cpu_to_le32(iwl_mvm_scan_lmac_flags(mvm, params)); - - cmd->flags = iwl_mvm_scan_rxon_flags(params->channels[0]->band); - cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP | - MAC_FILTER_IN_BEACON); - iwl_mvm_scan_fill_tx_cmd(mvm, cmd->tx_cmd, params->no_cck); - iwl_scan_build_ssids(params, cmd->direct_scan, &ssid_bitmap); - - /* this API uses bits 1-20 instead of 0-19 */ - ssid_bitmap <<= 1; - - for (i = 0; i < params->n_scan_plans; i++) { - struct cfg80211_sched_scan_plan *scan_plan = - ¶ms->scan_plans[i]; - - cmd->schedule[i].delay = - cpu_to_le16(scan_plan->interval); - cmd->schedule[i].iterations = scan_plan->iterations; - cmd->schedule[i].full_scan_mul = 1; - } - - /* - * If the number of iterations of the last scan plan is set to - * zero, it should run infinitely. However, this is not always the case. - * For example, when regular scan is requested the driver sets one scan - * plan with one iteration. - */ - if (!cmd->schedule[i - 1].iterations) - cmd->schedule[i - 1].iterations = 0xff; - - if (iwl_mvm_scan_use_ebs(mvm, vif)) { - cmd->channel_opt[0].flags = - cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS | - IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | - IWL_SCAN_CHANNEL_FLAG_CACHE_ADD); - cmd->channel_opt[0].non_ebs_ratio = - cpu_to_le16(IWL_DENSE_EBS_SCAN_RATIO); - cmd->channel_opt[1].flags = - cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS | - IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | - IWL_SCAN_CHANNEL_FLAG_CACHE_ADD); - cmd->channel_opt[1].non_ebs_ratio = - cpu_to_le16(IWL_SPARSE_EBS_SCAN_RATIO); - } - - iwl_mvm_lmac_scan_cfg_channels(mvm, params->channels, - params->n_channels, ssid_bitmap, cmd); - - *preq = params->preq; - - return 0; -} - -static int rate_to_scan_rate_flag(unsigned int rate) -{ - static const int rate_to_scan_rate[IWL_RATE_COUNT] = { - [IWL_RATE_1M_INDEX] = SCAN_CONFIG_RATE_1M, - [IWL_RATE_2M_INDEX] = SCAN_CONFIG_RATE_2M, - [IWL_RATE_5M_INDEX] = SCAN_CONFIG_RATE_5M, - [IWL_RATE_11M_INDEX] = SCAN_CONFIG_RATE_11M, - [IWL_RATE_6M_INDEX] = SCAN_CONFIG_RATE_6M, - [IWL_RATE_9M_INDEX] = SCAN_CONFIG_RATE_9M, - [IWL_RATE_12M_INDEX] = SCAN_CONFIG_RATE_12M, - [IWL_RATE_18M_INDEX] = SCAN_CONFIG_RATE_18M, - [IWL_RATE_24M_INDEX] = SCAN_CONFIG_RATE_24M, - [IWL_RATE_36M_INDEX] = SCAN_CONFIG_RATE_36M, - [IWL_RATE_48M_INDEX] = SCAN_CONFIG_RATE_48M, - [IWL_RATE_54M_INDEX] = SCAN_CONFIG_RATE_54M, - }; - - return rate_to_scan_rate[rate]; -} - -static __le32 iwl_mvm_scan_config_rates(struct iwl_mvm *mvm) -{ - struct ieee80211_supported_band *band; - unsigned int rates = 0; - int i; - - band = &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ]; - for (i = 0; i < band->n_bitrates; i++) - rates |= rate_to_scan_rate_flag(band->bitrates[i].hw_value); - band = &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ]; - for (i = 0; i < band->n_bitrates; i++) - rates |= rate_to_scan_rate_flag(band->bitrates[i].hw_value); - - /* Set both basic rates and supported rates */ - rates |= SCAN_CONFIG_SUPPORTED_RATE(rates); - - return cpu_to_le32(rates); -} - -int iwl_mvm_config_scan(struct iwl_mvm *mvm) -{ - struct iwl_scan_config *scan_config; - struct ieee80211_supported_band *band; - int num_channels = - mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels + - mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels; - int ret, i, j = 0, cmd_size; - struct iwl_host_cmd cmd = { - .id = iwl_cmd_id(SCAN_CFG_CMD, IWL_ALWAYS_LONG_GROUP, 0), - }; - - if (WARN_ON(num_channels > mvm->fw->ucode_capa.n_scan_channels)) - return -ENOBUFS; - - cmd_size = sizeof(*scan_config) + mvm->fw->ucode_capa.n_scan_channels; - - scan_config = kzalloc(cmd_size, GFP_KERNEL); - if (!scan_config) - return -ENOMEM; - - scan_config->flags = cpu_to_le32(SCAN_CONFIG_FLAG_ACTIVATE | - SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS | - SCAN_CONFIG_FLAG_SET_TX_CHAINS | - SCAN_CONFIG_FLAG_SET_RX_CHAINS | - SCAN_CONFIG_FLAG_SET_ALL_TIMES | - SCAN_CONFIG_FLAG_SET_LEGACY_RATES | - SCAN_CONFIG_FLAG_SET_MAC_ADDR | - SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS| - SCAN_CONFIG_N_CHANNELS(num_channels)); - scan_config->tx_chains = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm)); - scan_config->rx_chains = cpu_to_le32(iwl_mvm_scan_rx_ant(mvm)); - scan_config->legacy_rates = iwl_mvm_scan_config_rates(mvm); - scan_config->out_of_channel_time = cpu_to_le32(170); - scan_config->suspend_time = cpu_to_le32(30); - scan_config->dwell_active = 20; - scan_config->dwell_passive = 110; - scan_config->dwell_fragmented = 20; - - memcpy(&scan_config->mac_addr, &mvm->addresses[0].addr, ETH_ALEN); - - scan_config->bcast_sta_id = mvm->aux_sta.sta_id; - scan_config->channel_flags = IWL_CHANNEL_FLAG_EBS | - IWL_CHANNEL_FLAG_ACCURATE_EBS | - IWL_CHANNEL_FLAG_EBS_ADD | - IWL_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE; - - band = &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ]; - for (i = 0; i < band->n_channels; i++, j++) - scan_config->channel_array[j] = band->channels[i].hw_value; - band = &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ]; - for (i = 0; i < band->n_channels; i++, j++) - scan_config->channel_array[j] = band->channels[i].hw_value; - - cmd.data[0] = scan_config; - cmd.len[0] = cmd_size; - cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; - - IWL_DEBUG_SCAN(mvm, "Sending UMAC scan config\n"); - - ret = iwl_mvm_send_cmd(mvm, &cmd); - - kfree(scan_config); - return ret; -} - -static int iwl_mvm_scan_uid_by_status(struct iwl_mvm *mvm, int status) -{ - int i; - - for (i = 0; i < mvm->max_scans; i++) - if (mvm->scan_uid_status[i] == status) - return i; - - return -ENOENT; -} - -static inline bool iwl_mvm_is_regular_scan(struct iwl_mvm_scan_params *params) -{ - return params->n_scan_plans == 1 && - params->scan_plans[0].iterations == 1; -} - -static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm, - struct iwl_scan_req_umac *cmd, - struct iwl_mvm_scan_params *params) -{ - cmd->active_dwell = scan_timing[params->type].dwell_active; - cmd->passive_dwell = scan_timing[params->type].dwell_passive; - cmd->fragmented_dwell = scan_timing[params->type].dwell_fragmented; - cmd->max_out_time = cpu_to_le32(scan_timing[params->type].max_out_time); - cmd->suspend_time = cpu_to_le32(scan_timing[params->type].suspend_time); - cmd->scan_priority = - iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_6); - - if (iwl_mvm_is_regular_scan(params)) - cmd->ooc_priority = - iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_6); - else - cmd->ooc_priority = - iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_2); -} - -static void -iwl_mvm_umac_scan_cfg_channels(struct iwl_mvm *mvm, - struct ieee80211_channel **channels, - int n_channels, u32 ssid_bitmap, - struct iwl_scan_req_umac *cmd) -{ - struct iwl_scan_channel_cfg_umac *channel_cfg = (void *)&cmd->data; - int i; - - for (i = 0; i < n_channels; i++) { - channel_cfg[i].flags = cpu_to_le32(ssid_bitmap); - channel_cfg[i].channel_num = channels[i]->hw_value; - channel_cfg[i].iter_count = 1; - channel_cfg[i].iter_interval = 0; - } -} - -static u32 iwl_mvm_scan_umac_flags(struct iwl_mvm *mvm, - struct iwl_mvm_scan_params *params) -{ - int flags = 0; - - if (params->n_ssids == 0) - flags = IWL_UMAC_SCAN_GEN_FLAGS_PASSIVE; - - if (params->n_ssids == 1 && params->ssids[0].ssid_len != 0) - flags |= IWL_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT; - - if (params->type == IWL_SCAN_TYPE_FRAGMENTED) - flags |= IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED; - - if (iwl_mvm_rrm_scan_needed(mvm)) - flags |= IWL_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED; - - if (params->pass_all) - flags |= IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL; - else - flags |= IWL_UMAC_SCAN_GEN_FLAGS_MATCH; - - if (!iwl_mvm_is_regular_scan(params)) - flags |= IWL_UMAC_SCAN_GEN_FLAGS_PERIODIC; - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (mvm->scan_iter_notif_enabled) - flags |= IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE; -#endif - return flags; -} - -static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_mvm_scan_params *params, - int type) -{ - struct iwl_scan_req_umac *cmd = mvm->scan_cmd; - struct iwl_scan_req_umac_tail *sec_part = (void *)&cmd->data + - sizeof(struct iwl_scan_channel_cfg_umac) * - mvm->fw->ucode_capa.n_scan_channels; - int uid, i; - u32 ssid_bitmap = 0; - - lockdep_assert_held(&mvm->mutex); - - if (WARN_ON(params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS)) - return -EINVAL; - - uid = iwl_mvm_scan_uid_by_status(mvm, 0); - if (uid < 0) - return uid; - - memset(cmd, 0, ksize(cmd)); - - iwl_mvm_scan_umac_dwell(mvm, cmd, params); - - mvm->scan_uid_status[uid] = type; - - cmd->uid = cpu_to_le32(uid); - cmd->general_flags = cpu_to_le32(iwl_mvm_scan_umac_flags(mvm, params)); - - if (type == IWL_MVM_SCAN_SCHED) - cmd->flags = cpu_to_le32(IWL_UMAC_SCAN_FLAG_PREEMPTIVE); - - if (iwl_mvm_scan_use_ebs(mvm, vif)) - cmd->channel_flags = IWL_SCAN_CHANNEL_FLAG_EBS | - IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | - IWL_SCAN_CHANNEL_FLAG_CACHE_ADD; - - cmd->n_channels = params->n_channels; - - iwl_scan_build_ssids(params, sec_part->direct_scan, &ssid_bitmap); - - iwl_mvm_umac_scan_cfg_channels(mvm, params->channels, - params->n_channels, ssid_bitmap, cmd); - - for (i = 0; i < params->n_scan_plans; i++) { - struct cfg80211_sched_scan_plan *scan_plan = - ¶ms->scan_plans[i]; - - sec_part->schedule[i].iter_count = scan_plan->iterations; - sec_part->schedule[i].interval = - cpu_to_le16(scan_plan->interval); - } - - /* - * If the number of iterations of the last scan plan is set to - * zero, it should run infinitely. However, this is not always the case. - * For example, when regular scan is requested the driver sets one scan - * plan with one iteration. - */ - if (!sec_part->schedule[i - 1].iter_count) - sec_part->schedule[i - 1].iter_count = 0xff; - - sec_part->delay = cpu_to_le16(params->delay); - sec_part->preq = params->preq; - - return 0; -} - -static int iwl_mvm_num_scans(struct iwl_mvm *mvm) -{ - return hweight32(mvm->scan_status & IWL_MVM_SCAN_MASK); -} - -static int iwl_mvm_check_running_scans(struct iwl_mvm *mvm, int type) -{ - /* This looks a bit arbitrary, but the idea is that if we run - * out of possible simultaneous scans and the userspace is - * trying to run a scan type that is already running, we - * return -EBUSY. But if the userspace wants to start a - * different type of scan, we stop the opposite type to make - * space for the new request. The reason is backwards - * compatibility with old wpa_supplicant that wouldn't stop a - * scheduled scan before starting a normal scan. - */ - - if (iwl_mvm_num_scans(mvm) < mvm->max_scans) - return 0; - - /* Use a switch, even though this is a bitmask, so that more - * than one bits set will fall in default and we will warn. - */ - switch (type) { - case IWL_MVM_SCAN_REGULAR: - if (mvm->scan_status & IWL_MVM_SCAN_REGULAR_MASK) - return -EBUSY; - return iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, true); - case IWL_MVM_SCAN_SCHED: - if (mvm->scan_status & IWL_MVM_SCAN_SCHED_MASK) - return -EBUSY; - iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_REGULAR, true); - case IWL_MVM_SCAN_NETDETECT: - /* No need to stop anything for net-detect since the - * firmware is restarted anyway. This way, any sched - * scans that were running will be restarted when we - * resume. - */ - return 0; - default: - WARN_ON(1); - break; - } - - return -EIO; -} - -int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req, - struct ieee80211_scan_ies *ies) -{ - struct iwl_host_cmd hcmd = { - .len = { iwl_mvm_scan_size(mvm), }, - .data = { mvm->scan_cmd, }, - .dataflags = { IWL_HCMD_DFL_NOCOPY, }, - }; - struct iwl_mvm_scan_params params = {}; - int ret; - struct cfg80211_sched_scan_plan scan_plan = { .iterations = 1 }; - - lockdep_assert_held(&mvm->mutex); - - if (iwl_mvm_is_lar_supported(mvm) && !mvm->lar_regdom_set) { - IWL_ERR(mvm, "scan while LAR regdomain is not set\n"); - return -EBUSY; - } - - ret = iwl_mvm_check_running_scans(mvm, IWL_MVM_SCAN_REGULAR); - if (ret) - return ret; - - /* we should have failed registration if scan_cmd was NULL */ - if (WARN_ON(!mvm->scan_cmd)) - return -ENOMEM; - - if (!iwl_mvm_scan_fits(mvm, req->n_ssids, ies, req->n_channels)) - return -ENOBUFS; - - params.n_ssids = req->n_ssids; - params.flags = req->flags; - params.n_channels = req->n_channels; - params.delay = 0; - params.ssids = req->ssids; - params.channels = req->channels; - params.mac_addr = req->mac_addr; - params.mac_addr_mask = req->mac_addr_mask; - params.no_cck = req->no_cck; - params.pass_all = true; - params.n_match_sets = 0; - params.match_sets = NULL; - - params.scan_plans = &scan_plan; - params.n_scan_plans = 1; - - params.type = iwl_mvm_get_scan_type(mvm, vif, ¶ms); - - iwl_mvm_build_scan_probe(mvm, vif, ies, ¶ms); - - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { - hcmd.id = iwl_cmd_id(SCAN_REQ_UMAC, IWL_ALWAYS_LONG_GROUP, 0); - ret = iwl_mvm_scan_umac(mvm, vif, ¶ms, - IWL_MVM_SCAN_REGULAR); - } else { - hcmd.id = SCAN_OFFLOAD_REQUEST_CMD; - ret = iwl_mvm_scan_lmac(mvm, vif, ¶ms); - } - - if (ret) - return ret; - - ret = iwl_mvm_send_cmd(mvm, &hcmd); - if (ret) { - /* If the scan failed, it usually means that the FW was unable - * to allocate the time events. Warn on it, but maybe we - * should try to send the command again with different params. - */ - IWL_ERR(mvm, "Scan failed! ret %d\n", ret); - return ret; - } - - IWL_DEBUG_SCAN(mvm, "Scan request was sent successfully\n"); - mvm->scan_status |= IWL_MVM_SCAN_REGULAR; - iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN); - - return 0; -} - -int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct cfg80211_sched_scan_request *req, - struct ieee80211_scan_ies *ies, - int type) -{ - struct iwl_host_cmd hcmd = { - .len = { iwl_mvm_scan_size(mvm), }, - .data = { mvm->scan_cmd, }, - .dataflags = { IWL_HCMD_DFL_NOCOPY, }, - }; - struct iwl_mvm_scan_params params = {}; - int ret; - - lockdep_assert_held(&mvm->mutex); - - if (iwl_mvm_is_lar_supported(mvm) && !mvm->lar_regdom_set) { - IWL_ERR(mvm, "sched-scan while LAR regdomain is not set\n"); - return -EBUSY; - } - - /* we don't support "match all" in the firmware */ - if (!req->n_match_sets) - return -EOPNOTSUPP; - - ret = iwl_mvm_check_running_scans(mvm, type); - if (ret) - return ret; - - /* we should have failed registration if scan_cmd was NULL */ - if (WARN_ON(!mvm->scan_cmd)) - return -ENOMEM; - - if (!iwl_mvm_scan_fits(mvm, req->n_ssids, ies, req->n_channels)) - return -ENOBUFS; - - params.n_ssids = req->n_ssids; - params.flags = req->flags; - params.n_channels = req->n_channels; - params.ssids = req->ssids; - params.channels = req->channels; - params.mac_addr = req->mac_addr; - params.mac_addr_mask = req->mac_addr_mask; - params.no_cck = false; - params.pass_all = iwl_mvm_scan_pass_all(mvm, req); - params.n_match_sets = req->n_match_sets; - params.match_sets = req->match_sets; - if (!req->n_scan_plans) - return -EINVAL; - - params.n_scan_plans = req->n_scan_plans; - params.scan_plans = req->scan_plans; - - params.type = iwl_mvm_get_scan_type(mvm, vif, ¶ms); - - /* In theory, LMAC scans can handle a 32-bit delay, but since - * waiting for over 18 hours to start the scan is a bit silly - * and to keep it aligned with UMAC scans (which only support - * 16-bit delays), trim it down to 16-bits. - */ - if (req->delay > U16_MAX) { - IWL_DEBUG_SCAN(mvm, - "delay value is > 16-bits, set to max possible\n"); - params.delay = U16_MAX; - } else { - params.delay = req->delay; - } - - ret = iwl_mvm_config_sched_scan_profiles(mvm, req); - if (ret) - return ret; - - iwl_mvm_build_scan_probe(mvm, vif, ies, ¶ms); - - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { - hcmd.id = iwl_cmd_id(SCAN_REQ_UMAC, IWL_ALWAYS_LONG_GROUP, 0); - ret = iwl_mvm_scan_umac(mvm, vif, ¶ms, IWL_MVM_SCAN_SCHED); - } else { - hcmd.id = SCAN_OFFLOAD_REQUEST_CMD; - ret = iwl_mvm_scan_lmac(mvm, vif, ¶ms); - } - - if (ret) - return ret; - - ret = iwl_mvm_send_cmd(mvm, &hcmd); - if (!ret) { - IWL_DEBUG_SCAN(mvm, - "Sched scan request was sent successfully\n"); - mvm->scan_status |= type; - } else { - /* If the scan failed, it usually means that the FW was unable - * to allocate the time events. Warn on it, but maybe we - * should try to send the command again with different params. - */ - IWL_ERR(mvm, "Sched scan failed! ret %d\n", ret); - } - - return ret; -} - -void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_umac_scan_complete *notif = (void *)pkt->data; - u32 uid = __le32_to_cpu(notif->uid); - bool aborted = (notif->status == IWL_SCAN_OFFLOAD_ABORTED); - - if (WARN_ON(!(mvm->scan_uid_status[uid] & mvm->scan_status))) - return; - - /* if the scan is already stopping, we don't need to notify mac80211 */ - if (mvm->scan_uid_status[uid] == IWL_MVM_SCAN_REGULAR) { - ieee80211_scan_completed(mvm->hw, aborted); - iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); - } else if (mvm->scan_uid_status[uid] == IWL_MVM_SCAN_SCHED) { - ieee80211_sched_scan_stopped(mvm->hw); - } - - mvm->scan_status &= ~mvm->scan_uid_status[uid]; - IWL_DEBUG_SCAN(mvm, - "Scan completed, uid %u type %u, status %s, EBS status %s\n", - uid, mvm->scan_uid_status[uid], - notif->status == IWL_SCAN_OFFLOAD_COMPLETED ? - "completed" : "aborted", - iwl_mvm_ebs_status_str(notif->ebs_status)); - IWL_DEBUG_SCAN(mvm, - "Last line %d, Last iteration %d, Time from last iteration %d\n", - notif->last_schedule, notif->last_iter, - __le32_to_cpu(notif->time_from_last_iter)); - - if (notif->ebs_status != IWL_SCAN_EBS_SUCCESS && - notif->ebs_status != IWL_SCAN_EBS_INACTIVE) - mvm->last_ebs_successful = false; - - mvm->scan_uid_status[uid] = 0; -} - -void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_umac_scan_iter_complete_notif *notif = (void *)pkt->data; - u8 buf[256]; - - IWL_DEBUG_SCAN(mvm, - "UMAC Scan iteration complete: status=0x%x scanned_channels=%d channels list: %s\n", - notif->status, notif->scanned_channels, - iwl_mvm_dump_channel_list(notif->results, - notif->scanned_channels, buf, - sizeof(buf))); -} - -static int iwl_mvm_umac_scan_abort(struct iwl_mvm *mvm, int type) -{ - struct iwl_umac_scan_abort cmd = {}; - int uid, ret; - - lockdep_assert_held(&mvm->mutex); - - /* We should always get a valid index here, because we already - * checked that this type of scan was running in the generic - * code. - */ - uid = iwl_mvm_scan_uid_by_status(mvm, type); - if (WARN_ON_ONCE(uid < 0)) - return uid; - - cmd.uid = cpu_to_le32(uid); - - IWL_DEBUG_SCAN(mvm, "Sending scan abort, uid %u\n", uid); - - ret = iwl_mvm_send_cmd_pdu(mvm, - iwl_cmd_id(SCAN_ABORT_UMAC, - IWL_ALWAYS_LONG_GROUP, 0), - 0, sizeof(cmd), &cmd); - if (!ret) - mvm->scan_uid_status[uid] = type << IWL_MVM_SCAN_STOPPING_SHIFT; - - return ret; -} - -static int iwl_mvm_scan_stop_wait(struct iwl_mvm *mvm, int type) -{ - struct iwl_notification_wait wait_scan_done; - static const u16 scan_done_notif[] = { SCAN_COMPLETE_UMAC, - SCAN_OFFLOAD_COMPLETE, }; - int ret; - - lockdep_assert_held(&mvm->mutex); - - iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_done, - scan_done_notif, - ARRAY_SIZE(scan_done_notif), - NULL, NULL); - - IWL_DEBUG_SCAN(mvm, "Preparing to stop scan, type %x\n", type); - - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) - ret = iwl_mvm_umac_scan_abort(mvm, type); - else - ret = iwl_mvm_lmac_scan_abort(mvm); - - if (ret) { - IWL_DEBUG_SCAN(mvm, "couldn't stop scan type %d\n", type); - iwl_remove_notification(&mvm->notif_wait, &wait_scan_done); - return ret; - } - - ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_done, 1 * HZ); - - return ret; -} - -int iwl_mvm_scan_size(struct iwl_mvm *mvm) -{ - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) - return sizeof(struct iwl_scan_req_umac) + - sizeof(struct iwl_scan_channel_cfg_umac) * - mvm->fw->ucode_capa.n_scan_channels + - sizeof(struct iwl_scan_req_umac_tail); - - return sizeof(struct iwl_scan_req_lmac) + - sizeof(struct iwl_scan_channel_cfg_lmac) * - mvm->fw->ucode_capa.n_scan_channels + - sizeof(struct iwl_scan_probe_req); -} - -/* - * This function is used in nic restart flow, to inform mac80211 about scans - * that was aborted by restart flow or by an assert. - */ -void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm) -{ - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { - int uid, i; - - uid = iwl_mvm_scan_uid_by_status(mvm, IWL_MVM_SCAN_REGULAR); - if (uid >= 0) { - ieee80211_scan_completed(mvm->hw, true); - mvm->scan_uid_status[uid] = 0; - } - uid = iwl_mvm_scan_uid_by_status(mvm, IWL_MVM_SCAN_SCHED); - if (uid >= 0 && !mvm->restart_fw) { - ieee80211_sched_scan_stopped(mvm->hw); - mvm->scan_uid_status[uid] = 0; - } - - /* We shouldn't have any UIDs still set. Loop over all the - * UIDs to make sure there's nothing left there and warn if - * any is found. - */ - for (i = 0; i < mvm->max_scans; i++) { - if (WARN_ONCE(mvm->scan_uid_status[i], - "UMAC scan UID %d status was not cleaned\n", - i)) - mvm->scan_uid_status[i] = 0; - } - } else { - if (mvm->scan_status & IWL_MVM_SCAN_REGULAR) - ieee80211_scan_completed(mvm->hw, true); - - /* Sched scan will be restarted by mac80211 in - * restart_hw, so do not report if FW is about to be - * restarted. - */ - if ((mvm->scan_status & IWL_MVM_SCAN_SCHED) && !mvm->restart_fw) - ieee80211_sched_scan_stopped(mvm->hw); - } -} - -int iwl_mvm_scan_stop(struct iwl_mvm *mvm, int type, bool notify) -{ - int ret; - - if (!(mvm->scan_status & type)) - return 0; - - if (iwl_mvm_is_radio_killed(mvm)) { - ret = 0; - goto out; - } - - ret = iwl_mvm_scan_stop_wait(mvm, type); - if (!ret) - mvm->scan_status |= type << IWL_MVM_SCAN_STOPPING_SHIFT; -out: - /* Clear the scan status so the next scan requests will - * succeed and mark the scan as stopping, so that the Rx - * handler doesn't do anything, as the scan was stopped from - * above. - */ - mvm->scan_status &= ~type; - - if (type == IWL_MVM_SCAN_REGULAR) { - /* Since the rx handler won't do anything now, we have - * to release the scan reference here. - */ - iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); - if (notify) - ieee80211_scan_completed(mvm->hw, true); - } else if (notify) { - ieee80211_sched_scan_stopped(mvm->hw); - } - - return ret; -} diff --git a/drivers/net/wireless/iwlwifi/mvm/sf.c b/drivers/net/wireless/iwlwifi/mvm/sf.c deleted file mode 100644 index b0f59fdd2..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/sf.c +++ /dev/null @@ -1,340 +0,0 @@ -/****************************************************************************** - * - * 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 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * 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 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * 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 "mvm.h" - -/* For counting bound interfaces */ -struct iwl_mvm_active_iface_iterator_data { - struct ieee80211_vif *ignore_vif; - u8 sta_vif_ap_sta_id; - enum iwl_sf_state sta_vif_state; - int num_active_macs; -}; - -/* - * Count bound interfaces which are not p2p, besides data->ignore_vif. - * data->station_vif will point to one bound vif of type station, if exists. - */ -static void iwl_mvm_bound_iface_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_active_iface_iterator_data *data = _data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (vif == data->ignore_vif || !mvmvif->phy_ctxt || - vif->type == NL80211_IFTYPE_P2P_DEVICE) - return; - - data->num_active_macs++; - - if (vif->type == NL80211_IFTYPE_STATION) { - data->sta_vif_ap_sta_id = mvmvif->ap_sta_id; - if (vif->bss_conf.assoc) - data->sta_vif_state = SF_FULL_ON; - else - data->sta_vif_state = SF_INIT_OFF; - } -} - -/* - * Aging and idle timeouts for the different possible scenarios - * in default configuration - */ -static const -__le32 sf_full_timeout_def[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = { - { - cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER_DEF), - cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER_DEF) - }, - { - cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER_DEF), - cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER_DEF) - }, - { - cpu_to_le32(SF_MCAST_AGING_TIMER_DEF), - cpu_to_le32(SF_MCAST_IDLE_TIMER_DEF) - }, - { - cpu_to_le32(SF_BA_AGING_TIMER_DEF), - cpu_to_le32(SF_BA_IDLE_TIMER_DEF) - }, - { - cpu_to_le32(SF_TX_RE_AGING_TIMER_DEF), - cpu_to_le32(SF_TX_RE_IDLE_TIMER_DEF) - }, -}; - -/* - * Aging and idle timeouts for the different possible scenarios - * in single BSS MAC configuration. - */ -static const __le32 sf_full_timeout[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = { - { - cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER), - cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER) - }, - { - cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER), - cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER) - }, - { - cpu_to_le32(SF_MCAST_AGING_TIMER), - cpu_to_le32(SF_MCAST_IDLE_TIMER) - }, - { - cpu_to_le32(SF_BA_AGING_TIMER), - cpu_to_le32(SF_BA_IDLE_TIMER) - }, - { - cpu_to_le32(SF_TX_RE_AGING_TIMER), - cpu_to_le32(SF_TX_RE_IDLE_TIMER) - }, -}; - -static void iwl_mvm_fill_sf_command(struct iwl_mvm *mvm, - struct iwl_sf_cfg_cmd *sf_cmd, - struct ieee80211_sta *sta) -{ - int i, j, watermark; - - sf_cmd->watermark[SF_LONG_DELAY_ON] = cpu_to_le32(SF_W_MARK_SCAN); - - /* - * If we are in association flow - check antenna configuration - * capabilities of the AP station, and choose the watermark accordingly. - */ - if (sta) { - if (sta->ht_cap.ht_supported || sta->vht_cap.vht_supported) { - switch (sta->rx_nss) { - case 1: - watermark = SF_W_MARK_SISO; - break; - case 2: - watermark = SF_W_MARK_MIMO2; - break; - default: - watermark = SF_W_MARK_MIMO3; - break; - } - } else { - watermark = SF_W_MARK_LEGACY; - } - /* default watermark value for unassociated mode. */ - } else { - watermark = SF_W_MARK_MIMO2; - } - sf_cmd->watermark[SF_FULL_ON] = cpu_to_le32(watermark); - - for (i = 0; i < SF_NUM_SCENARIO; i++) { - for (j = 0; j < SF_NUM_TIMEOUT_TYPES; j++) { - sf_cmd->long_delay_timeouts[i][j] = - cpu_to_le32(SF_LONG_DELAY_AGING_TIMER); - } - } - - if (sta || IWL_UCODE_API(mvm->fw->ucode_ver) < 13) { - BUILD_BUG_ON(sizeof(sf_full_timeout) != - sizeof(__le32) * SF_NUM_SCENARIO * - SF_NUM_TIMEOUT_TYPES); - - memcpy(sf_cmd->full_on_timeouts, sf_full_timeout, - sizeof(sf_full_timeout)); - } else { - BUILD_BUG_ON(sizeof(sf_full_timeout_def) != - sizeof(__le32) * SF_NUM_SCENARIO * - SF_NUM_TIMEOUT_TYPES); - - memcpy(sf_cmd->full_on_timeouts, sf_full_timeout_def, - sizeof(sf_full_timeout_def)); - } - -} - -static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id, - enum iwl_sf_state new_state) -{ - struct iwl_sf_cfg_cmd sf_cmd = { - .state = cpu_to_le32(SF_FULL_ON), - }; - struct ieee80211_sta *sta; - int ret = 0; - - if (IWL_UCODE_API(mvm->fw->ucode_ver) < 13) - sf_cmd.state = cpu_to_le32(new_state); - - if (mvm->cfg->disable_dummy_notification) - sf_cmd.state |= cpu_to_le32(SF_CFG_DUMMY_NOTIF_OFF); - - /* - * If an associated AP sta changed its antenna configuration, the state - * will remain FULL_ON but SF parameters need to be reconsidered. - */ - if (new_state != SF_FULL_ON && mvm->sf_state == new_state) - return 0; - - switch (new_state) { - case SF_UNINIT: - if (IWL_UCODE_API(mvm->fw->ucode_ver) >= 13) - iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL); - break; - case SF_FULL_ON: - if (sta_id == IWL_MVM_STATION_COUNT) { - IWL_ERR(mvm, - "No station: Cannot switch SF to FULL_ON\n"); - return -EINVAL; - } - rcu_read_lock(); - sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); - if (IS_ERR_OR_NULL(sta)) { - IWL_ERR(mvm, "Invalid station id\n"); - rcu_read_unlock(); - return -EINVAL; - } - iwl_mvm_fill_sf_command(mvm, &sf_cmd, sta); - rcu_read_unlock(); - break; - case SF_INIT_OFF: - iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL); - break; - default: - WARN_ONCE(1, "Invalid state: %d. not sending Smart Fifo cmd\n", - new_state); - return -EINVAL; - } - - ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_SF_CFG_CMD, CMD_ASYNC, - sizeof(sf_cmd), &sf_cmd); - if (!ret) - mvm->sf_state = new_state; - - return ret; -} - -/* - * Update Smart fifo: - * Count bound interfaces that are not to be removed, ignoring p2p devices, - * and set new state accordingly. - */ -int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif, - bool remove_vif) -{ - enum iwl_sf_state new_state; - u8 sta_id = IWL_MVM_STATION_COUNT; - struct iwl_mvm_vif *mvmvif = NULL; - struct iwl_mvm_active_iface_iterator_data data = { - .ignore_vif = changed_vif, - .sta_vif_state = SF_UNINIT, - .sta_vif_ap_sta_id = IWL_MVM_STATION_COUNT, - }; - - /* - * Ignore the call if we are in HW Restart flow, or if the handled - * vif is a p2p device. - */ - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) || - (changed_vif && changed_vif->type == NL80211_IFTYPE_P2P_DEVICE)) - return 0; - - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_bound_iface_iterator, - &data); - - /* If changed_vif exists and is not to be removed, add to the count */ - if (changed_vif && !remove_vif) - data.num_active_macs++; - - switch (data.num_active_macs) { - case 0: - /* If there are no active macs - change state to SF_INIT_OFF */ - new_state = SF_INIT_OFF; - break; - case 1: - if (remove_vif) { - /* The one active mac left is of type station - * and we filled the relevant data during iteration - */ - new_state = data.sta_vif_state; - sta_id = data.sta_vif_ap_sta_id; - } else { - if (WARN_ON(!changed_vif)) - return -EINVAL; - if (changed_vif->type != NL80211_IFTYPE_STATION) { - new_state = SF_UNINIT; - } else if (changed_vif->bss_conf.assoc && - changed_vif->bss_conf.dtim_period) { - mvmvif = iwl_mvm_vif_from_mac80211(changed_vif); - sta_id = mvmvif->ap_sta_id; - new_state = SF_FULL_ON; - } else { - new_state = SF_INIT_OFF; - } - } - break; - default: - /* If there are multiple active macs - change to SF_UNINIT */ - new_state = SF_UNINIT; - } - return iwl_mvm_sf_config(mvm, sta_id, new_state); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c deleted file mode 100644 index 2b976b110..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ /dev/null @@ -1,1819 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2015 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * 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) 2012 - 2015 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * 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 "mvm.h" -#include "sta.h" -#include "rs.h" - -static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm, - enum nl80211_iftype iftype) -{ - int sta_id; - u32 reserved_ids = 0; - - BUILD_BUG_ON(IWL_MVM_STATION_COUNT > 32); - WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)); - - lockdep_assert_held(&mvm->mutex); - - /* d0i3/d3 assumes the AP's sta_id (of sta vif) is 0. reserve it. */ - if (iftype != NL80211_IFTYPE_STATION) - reserved_ids = BIT(0); - - /* Don't take rcu_read_lock() since we are protected by mvm->mutex */ - for (sta_id = 0; sta_id < IWL_MVM_STATION_COUNT; sta_id++) { - if (BIT(sta_id) & reserved_ids) - continue; - - if (!rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], - lockdep_is_held(&mvm->mutex))) - return sta_id; - } - return IWL_MVM_STATION_COUNT; -} - -/* send station add/update command to firmware */ -int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - bool update) -{ - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_add_sta_cmd add_sta_cmd = { - .sta_id = mvm_sta->sta_id, - .mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color), - .add_modify = update ? 1 : 0, - .station_flags_msk = cpu_to_le32(STA_FLG_FAT_EN_MSK | - STA_FLG_MIMO_EN_MSK), - }; - int ret; - u32 status; - u32 agg_size = 0, mpdu_dens = 0; - - if (!update) { - add_sta_cmd.tfd_queue_msk = cpu_to_le32(mvm_sta->tfd_queue_msk); - memcpy(&add_sta_cmd.addr, sta->addr, ETH_ALEN); - } - - 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 |= - cpu_to_le32(STA_FLG_MAX_AGG_SIZE_MSK | - STA_FLG_AGG_MPDU_DENS_MSK); - - mpdu_dens = sta->ht_cap.ampdu_density; - } - - if (sta->vht_cap.vht_supported) { - agg_size = sta->vht_cap.cap & - IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; - agg_size >>= - IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; - } else if (sta->ht_cap.ht_supported) { - agg_size = sta->ht_cap.ampdu_factor; - } - - add_sta_cmd.station_flags |= - cpu_to_le32(agg_size << STA_FLG_MAX_AGG_SIZE_SHIFT); - add_sta_cmd.station_flags |= - cpu_to_le32(mpdu_dens << STA_FLG_AGG_MPDU_DENS_SHIFT); - - status = ADD_STA_SUCCESS; - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(add_sta_cmd), - &add_sta_cmd, &status); - if (ret) - return ret; - - switch (status) { - case ADD_STA_SUCCESS: - IWL_DEBUG_ASSOC(mvm, "ADD_STA PASSED\n"); - break; - default: - ret = -EIO; - IWL_ERR(mvm, "ADD_STA failed\n"); - break; - } - - return ret; -} - -static int iwl_mvm_tdls_sta_init(struct iwl_mvm *mvm, - struct ieee80211_sta *sta) -{ - unsigned long used_hw_queues; - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - unsigned int wdg_timeout = - iwl_mvm_get_wd_timeout(mvm, NULL, true, false); - u32 ac; - - lockdep_assert_held(&mvm->mutex); - - used_hw_queues = iwl_mvm_get_used_hw_queues(mvm, NULL); - - /* Find available queues, and allocate them to the ACs */ - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { - u8 queue = find_first_zero_bit(&used_hw_queues, - mvm->first_agg_queue); - - if (queue >= mvm->first_agg_queue) { - IWL_ERR(mvm, "Failed to allocate STA queue\n"); - return -EBUSY; - } - - __set_bit(queue, &used_hw_queues); - mvmsta->hw_queue[ac] = queue; - } - - /* Found a place for all queues - enable them */ - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { - iwl_mvm_enable_ac_txq(mvm, mvmsta->hw_queue[ac], - mvmsta->hw_queue[ac], - iwl_mvm_ac_to_tx_fifo[ac], 0, - wdg_timeout); - mvmsta->tfd_queue_msk |= BIT(mvmsta->hw_queue[ac]); - } - - return 0; -} - -static void iwl_mvm_tdls_sta_deinit(struct iwl_mvm *mvm, - struct ieee80211_sta *sta) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - unsigned long sta_msk; - int i; - - lockdep_assert_held(&mvm->mutex); - - /* disable the TDLS STA-specific queues */ - sta_msk = mvmsta->tfd_queue_msk; - for_each_set_bit(i, &sta_msk, sizeof(sta_msk) * BITS_PER_BYTE) - iwl_mvm_disable_txq(mvm, i, i, IWL_MAX_TID_COUNT, 0); -} - -int iwl_mvm_add_sta(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - int i, ret, sta_id; - - lockdep_assert_held(&mvm->mutex); - - if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) - sta_id = iwl_mvm_find_free_sta_id(mvm, - ieee80211_vif_type_p2p(vif)); - else - sta_id = mvm_sta->sta_id; - - if (sta_id == IWL_MVM_STATION_COUNT) - return -ENOSPC; - - if (vif->type == NL80211_IFTYPE_AP) { - mvmvif->ap_assoc_sta_count++; - iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); - } - - spin_lock_init(&mvm_sta->lock); - - mvm_sta->sta_id = sta_id; - mvm_sta->mac_id_n_color = FW_CMD_ID_AND_COLOR(mvmvif->id, - mvmvif->color); - mvm_sta->vif = vif; - mvm_sta->max_agg_bufsize = LINK_QUAL_AGG_FRAME_LIMIT_DEF; - mvm_sta->tx_protection = 0; - mvm_sta->tt_tx_protection = false; - - /* HW restart, don't assume the memory has been zeroed */ - atomic_set(&mvm->pending_frames[sta_id], 0); - mvm_sta->tid_disable_agg = 0xffff; /* No aggs at first */ - mvm_sta->tfd_queue_msk = 0; - - /* allocate new queues for a TDLS station */ - if (sta->tdls) { - ret = iwl_mvm_tdls_sta_init(mvm, sta); - if (ret) - return ret; - } else { - for (i = 0; i < IEEE80211_NUM_ACS; i++) - if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE) - mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]); - } - - /* for HW restart - reset everything but the sequence number */ - for (i = 0; i < IWL_MAX_TID_COUNT; i++) { - u16 seq = mvm_sta->tid_data[i].seq_number; - memset(&mvm_sta->tid_data[i], 0, sizeof(mvm_sta->tid_data[i])); - mvm_sta->tid_data[i].seq_number = seq; - } - mvm_sta->agg_tids = 0; - - ret = iwl_mvm_sta_send_to_fw(mvm, sta, false); - if (ret) - goto err; - - if (vif->type == NL80211_IFTYPE_STATION) { - if (!sta->tdls) { - WARN_ON(mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT); - mvmvif->ap_sta_id = sta_id; - } else { - WARN_ON(mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT); - } - } - - rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], sta); - - return 0; - -err: - iwl_mvm_tdls_sta_deinit(mvm, sta); - return ret; -} - -int iwl_mvm_update_sta(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - return iwl_mvm_sta_send_to_fw(mvm, sta, true); -} - -int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, - bool drain) -{ - struct iwl_mvm_add_sta_cmd cmd = {}; - int ret; - u32 status; - - lockdep_assert_held(&mvm->mutex); - - cmd.mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color); - cmd.sta_id = mvmsta->sta_id; - cmd.add_modify = STA_MODE_MODIFY; - cmd.station_flags = drain ? cpu_to_le32(STA_FLG_DRAIN_FLOW) : 0; - cmd.station_flags_msk = cpu_to_le32(STA_FLG_DRAIN_FLOW); - - status = ADD_STA_SUCCESS; - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), - &cmd, &status); - if (ret) - return ret; - - switch (status) { - case ADD_STA_SUCCESS: - IWL_DEBUG_INFO(mvm, "Frames for staid %d will drained in fw\n", - mvmsta->sta_id); - break; - default: - ret = -EIO; - IWL_ERR(mvm, "Couldn't drain frames for staid %d\n", - mvmsta->sta_id); - break; - } - - return ret; -} - -/* - * Remove a station from the FW table. Before sending the command to remove - * the station validate that the station is indeed known to the driver (sanity - * only). - */ -static int iwl_mvm_rm_sta_common(struct iwl_mvm *mvm, u8 sta_id) -{ - struct ieee80211_sta *sta; - struct iwl_mvm_rm_sta_cmd rm_sta_cmd = { - .sta_id = sta_id, - }; - int ret; - - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], - lockdep_is_held(&mvm->mutex)); - - /* Note: internal stations are marked as error values */ - if (!sta) { - IWL_ERR(mvm, "Invalid station id\n"); - return -EINVAL; - } - - ret = iwl_mvm_send_cmd_pdu(mvm, REMOVE_STA, 0, - sizeof(rm_sta_cmd), &rm_sta_cmd); - if (ret) { - IWL_ERR(mvm, "Failed to remove station. Id=%d\n", sta_id); - return ret; - } - - return 0; -} - -void iwl_mvm_sta_drained_wk(struct work_struct *wk) -{ - struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, sta_drained_wk); - u8 sta_id; - - /* - * The mutex is needed because of the SYNC cmd, but not only: if the - * work would run concurrently with iwl_mvm_rm_sta, it would run before - * iwl_mvm_rm_sta sets the station as busy, and exit. Then - * iwl_mvm_rm_sta would set the station as busy, and nobody will clean - * that later. - */ - mutex_lock(&mvm->mutex); - - for_each_set_bit(sta_id, mvm->sta_drained, IWL_MVM_STATION_COUNT) { - int ret; - struct ieee80211_sta *sta = - rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], - lockdep_is_held(&mvm->mutex)); - - /* - * This station is in use or RCU-removed; the latter happens in - * managed mode, where mac80211 removes the station before we - * can remove it from firmware (we can only do that after the - * MAC is marked unassociated), and possibly while the deauth - * frame to disconnect from the AP is still queued. Then, the - * station pointer is -ENOENT when the last skb is reclaimed. - */ - if (!IS_ERR(sta) || PTR_ERR(sta) == -ENOENT) - continue; - - if (PTR_ERR(sta) == -EINVAL) { - IWL_ERR(mvm, "Drained sta %d, but it is internal?\n", - sta_id); - continue; - } - - if (!sta) { - IWL_ERR(mvm, "Drained sta %d, but it was NULL?\n", - sta_id); - continue; - } - - WARN_ON(PTR_ERR(sta) != -EBUSY); - /* This station was removed and we waited until it got drained, - * we can now proceed and remove it. - */ - ret = iwl_mvm_rm_sta_common(mvm, sta_id); - if (ret) { - IWL_ERR(mvm, - "Couldn't remove sta %d after it was drained\n", - sta_id); - continue; - } - RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL); - clear_bit(sta_id, mvm->sta_drained); - - if (mvm->tfd_drained[sta_id]) { - unsigned long i, msk = mvm->tfd_drained[sta_id]; - - for_each_set_bit(i, &msk, sizeof(msk) * BITS_PER_BYTE) - iwl_mvm_disable_txq(mvm, i, i, - IWL_MAX_TID_COUNT, 0); - - mvm->tfd_drained[sta_id] = 0; - IWL_DEBUG_TDLS(mvm, "Drained sta %d, with queues %ld\n", - sta_id, msk); - } - } - - mutex_unlock(&mvm->mutex); -} - -int iwl_mvm_rm_sta(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - int ret; - - lockdep_assert_held(&mvm->mutex); - - if (vif->type == NL80211_IFTYPE_STATION && - mvmvif->ap_sta_id == mvm_sta->sta_id) { - ret = iwl_mvm_drain_sta(mvm, mvm_sta, true); - if (ret) - return ret; - /* flush its queues here since we are freeing mvm_sta */ - ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, 0); - if (ret) - return ret; - ret = iwl_trans_wait_tx_queue_empty(mvm->trans, - mvm_sta->tfd_queue_msk); - if (ret) - return ret; - ret = iwl_mvm_drain_sta(mvm, mvm_sta, false); - - /* if we are associated - we can't remove the AP STA now */ - if (vif->bss_conf.assoc) - return ret; - - /* unassoc - go ahead - remove the AP STA now */ - mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; - - /* clear d0i3_ap_sta_id if no longer relevant */ - if (mvm->d0i3_ap_sta_id == mvm_sta->sta_id) - mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; - } - - /* - * This shouldn't happen - the TDLS channel switch should be canceled - * before the STA is removed. - */ - if (WARN_ON_ONCE(mvm->tdls_cs.peer.sta_id == mvm_sta->sta_id)) { - mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT; - cancel_delayed_work(&mvm->tdls_cs.dwork); - } - - /* - * 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->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); - - /* disable TDLS sta queues on drain complete */ - if (sta->tdls) { - mvm->tfd_drained[mvm_sta->sta_id] = - mvm_sta->tfd_queue_msk; - IWL_DEBUG_TDLS(mvm, "Draining TDLS sta %d\n", - mvm_sta->sta_id); - } - - ret = iwl_mvm_drain_sta(mvm, mvm_sta, true); - } else { - spin_unlock_bh(&mvm_sta->lock); - - if (sta->tdls) - iwl_mvm_tdls_sta_deinit(mvm, sta); - - ret = iwl_mvm_rm_sta_common(mvm, mvm_sta->sta_id); - RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta->sta_id], NULL); - } - - return ret; -} - -int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u8 sta_id) -{ - int ret = iwl_mvm_rm_sta_common(mvm, sta_id); - - lockdep_assert_held(&mvm->mutex); - - RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL); - return ret; -} - -static int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, - struct iwl_mvm_int_sta *sta, - u32 qmask, enum nl80211_iftype iftype) -{ - if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { - sta->sta_id = iwl_mvm_find_free_sta_id(mvm, iftype); - if (WARN_ON_ONCE(sta->sta_id == IWL_MVM_STATION_COUNT)) - return -ENOSPC; - } - - sta->tfd_queue_msk = qmask; - - /* put a non-NULL value so iterating over the stations won't stop */ - rcu_assign_pointer(mvm->fw_id_to_mac_id[sta->sta_id], ERR_PTR(-EINVAL)); - return 0; -} - -static void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm, - struct iwl_mvm_int_sta *sta) -{ - RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta->sta_id], NULL); - memset(sta, 0, sizeof(struct iwl_mvm_int_sta)); - sta->sta_id = IWL_MVM_STATION_COUNT; -} - -static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm, - struct iwl_mvm_int_sta *sta, - const u8 *addr, - u16 mac_id, u16 color) -{ - struct iwl_mvm_add_sta_cmd cmd; - int ret; - u32 status; - - lockdep_assert_held(&mvm->mutex); - - memset(&cmd, 0, sizeof(cmd)); - cmd.sta_id = sta->sta_id; - cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id, - color)); - - cmd.tfd_queue_msk = cpu_to_le32(sta->tfd_queue_msk); - - if (addr) - memcpy(cmd.addr, addr, ETH_ALEN); - - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), - &cmd, &status); - if (ret) - return ret; - - switch (status) { - case ADD_STA_SUCCESS: - IWL_DEBUG_INFO(mvm, "Internal station added.\n"); - return 0; - default: - ret = -EIO; - IWL_ERR(mvm, "Add internal station failed, status=0x%x\n", - status); - break; - } - return ret; -} - -int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm) -{ - unsigned int wdg_timeout = iwlmvm_mod_params.tfd_q_hang_detect ? - mvm->cfg->base_params->wd_timeout : - IWL_WATCHDOG_DISABLED; - int ret; - - lockdep_assert_held(&mvm->mutex); - - /* Map Aux queue to fifo - needs to happen before adding Aux station */ - iwl_mvm_enable_ac_txq(mvm, mvm->aux_queue, mvm->aux_queue, - IWL_MVM_TX_FIFO_MCAST, 0, wdg_timeout); - - /* Allocate aux station and assign to it the aux queue */ - ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, BIT(mvm->aux_queue), - NL80211_IFTYPE_UNSPECIFIED); - if (ret) - return ret; - - ret = iwl_mvm_add_int_sta_common(mvm, &mvm->aux_sta, NULL, - MAC_INDEX_AUX, 0); - - if (ret) - iwl_mvm_dealloc_int_sta(mvm, &mvm->aux_sta); - return ret; -} - -void iwl_mvm_del_aux_sta(struct iwl_mvm *mvm) -{ - lockdep_assert_held(&mvm->mutex); - - iwl_mvm_dealloc_int_sta(mvm, &mvm->aux_sta); -} - -/* - * Send the add station command for the vif's broadcast station. - * Assumes that the station was already allocated. - * - * @mvm: the mvm component - * @vif: the interface to which the broadcast station is added - * @bsta: the broadcast station to add. - */ -int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_int_sta *bsta = &mvmvif->bcast_sta; - static const u8 _baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - const u8 *baddr = _baddr; - - lockdep_assert_held(&mvm->mutex); - - if (vif->type == NL80211_IFTYPE_ADHOC) - baddr = vif->bss_conf.bssid; - - if (WARN_ON_ONCE(bsta->sta_id == IWL_MVM_STATION_COUNT)) - return -ENOSPC; - - return iwl_mvm_add_int_sta_common(mvm, bsta, baddr, - mvmvif->id, mvmvif->color); -} - -/* Send the FW a request to remove the station from it's internal data - * structures, but DO NOT remove the entry from the local data structures. */ -int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; - - lockdep_assert_held(&mvm->mutex); - - ret = iwl_mvm_rm_sta_common(mvm, mvmvif->bcast_sta.sta_id); - if (ret) - IWL_WARN(mvm, "Failed sending remove station\n"); - return ret; -} - -int iwl_mvm_alloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - u32 qmask; - - lockdep_assert_held(&mvm->mutex); - - qmask = iwl_mvm_mac_get_queues_mask(vif); - - /* - * The firmware defines the TFD queue mask to only be relevant - * for *unicast* queues, so the multicast (CAB) queue shouldn't - * be included. - */ - if (vif->type == NL80211_IFTYPE_AP) - qmask &= ~BIT(vif->cab_queue); - - return iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta, qmask, - ieee80211_vif_type_p2p(vif)); -} - -/* Allocate a new station entry for the broadcast station to the given vif, - * and send it to the FW. - * Note that each P2P mac should have its own broadcast station. - * - * @mvm: the mvm component - * @vif: the interface to which the broadcast station is added - * @bsta: the broadcast station to add. */ -int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_int_sta *bsta = &mvmvif->bcast_sta; - int ret; - - lockdep_assert_held(&mvm->mutex); - - ret = iwl_mvm_alloc_bcast_sta(mvm, vif); - if (ret) - return ret; - - ret = iwl_mvm_send_add_bcast_sta(mvm, vif); - - if (ret) - iwl_mvm_dealloc_int_sta(mvm, bsta); - - return ret; -} - -void iwl_mvm_dealloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - iwl_mvm_dealloc_int_sta(mvm, &mvmvif->bcast_sta); -} - -/* - * Send the FW a request to remove the station from it's internal data - * structures, and in addition remove it from the local data structure. - */ -int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - int ret; - - lockdep_assert_held(&mvm->mutex); - - ret = iwl_mvm_send_rm_bcast_sta(mvm, vif); - - iwl_mvm_dealloc_bcast_sta(mvm, vif); - - return ret; -} - -#define IWL_MAX_RX_BA_SESSIONS 16 - -int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - int tid, u16 ssn, bool start) -{ - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_add_sta_cmd cmd = {}; - int ret; - u32 status; - - lockdep_assert_held(&mvm->mutex); - - if (start && mvm->rx_ba_sessions >= IWL_MAX_RX_BA_SESSIONS) { - IWL_WARN(mvm, "Not enough RX BA SESSIONS\n"); - return -ENOSPC; - } - - cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color); - cmd.sta_id = mvm_sta->sta_id; - cmd.add_modify = STA_MODE_MODIFY; - if (start) { - cmd.add_immediate_ba_tid = (u8) tid; - cmd.add_immediate_ba_ssn = cpu_to_le16(ssn); - } else { - cmd.remove_immediate_ba_tid = (u8) tid; - } - cmd.modify_mask = start ? STA_MODIFY_ADD_BA_TID : - STA_MODIFY_REMOVE_BA_TID; - - status = ADD_STA_SUCCESS; - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), - &cmd, &status); - if (ret) - return ret; - - switch (status) { - case ADD_STA_SUCCESS: - IWL_DEBUG_INFO(mvm, "RX BA Session %sed in fw\n", - start ? "start" : "stopp"); - break; - case ADD_STA_IMMEDIATE_BA_FAILURE: - IWL_WARN(mvm, "RX BA Session refused by fw\n"); - ret = -ENOSPC; - break; - default: - ret = -EIO; - IWL_ERR(mvm, "RX BA Session failed %sing, status 0x%x\n", - start ? "start" : "stopp", status); - break; - } - - if (!ret) { - if (start) - mvm->rx_ba_sessions++; - else if (mvm->rx_ba_sessions > 0) - /* check that restart flow didn't zero the counter */ - mvm->rx_ba_sessions--; - } - - return ret; -} - -static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - int tid, u8 queue, bool start) -{ - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_add_sta_cmd cmd = {}; - int ret; - u32 status; - - lockdep_assert_held(&mvm->mutex); - - if (start) { - mvm_sta->tfd_queue_msk |= BIT(queue); - mvm_sta->tid_disable_agg &= ~BIT(tid); - } else { - mvm_sta->tfd_queue_msk &= ~BIT(queue); - mvm_sta->tid_disable_agg |= BIT(tid); - } - - cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color); - cmd.sta_id = mvm_sta->sta_id; - cmd.add_modify = STA_MODE_MODIFY; - cmd.modify_mask = STA_MODIFY_QUEUES | STA_MODIFY_TID_DISABLE_TX; - cmd.tfd_queue_msk = cpu_to_le32(mvm_sta->tfd_queue_msk); - cmd.tid_disable_tx = cpu_to_le16(mvm_sta->tid_disable_agg); - - status = ADD_STA_SUCCESS; - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), - &cmd, &status); - if (ret) - return ret; - - switch (status) { - case ADD_STA_SUCCESS: - break; - default: - ret = -EIO; - IWL_ERR(mvm, "TX BA Session failed %sing, status 0x%x\n", - start ? "start" : "stopp", status); - break; - } - - return ret; -} - -const u8 tid_to_mac80211_ac[] = { - IEEE80211_AC_BE, - IEEE80211_AC_BK, - IEEE80211_AC_BK, - IEEE80211_AC_BE, - IEEE80211_AC_VI, - IEEE80211_AC_VI, - IEEE80211_AC_VO, - IEEE80211_AC_VO, -}; - -static const u8 tid_to_ucode_ac[] = { - AC_BE, - AC_BK, - AC_BK, - AC_BE, - AC_VI, - AC_VI, - AC_VO, - AC_VO, -}; - -int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid, u16 *ssn) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_tid_data *tid_data; - int txq_id; - int ret; - - if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) - return -EINVAL; - - if (mvmsta->tid_data[tid].state != IWL_AGG_OFF) { - IWL_ERR(mvm, "Start AGG when state is not IWL_AGG_OFF %d!\n", - mvmsta->tid_data[tid].state); - return -ENXIO; - } - - lockdep_assert_held(&mvm->mutex); - - spin_lock_bh(&mvmsta->lock); - - /* possible race condition - we entered D0i3 while starting agg */ - if (test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status)) { - spin_unlock_bh(&mvmsta->lock); - IWL_ERR(mvm, "Entered D0i3 while starting Tx agg\n"); - return -EIO; - } - - spin_lock_bh(&mvm->queue_info_lock); - - txq_id = iwl_mvm_find_free_queue(mvm, mvm->first_agg_queue, - mvm->last_agg_queue); - if (txq_id < 0) { - ret = txq_id; - spin_unlock_bh(&mvm->queue_info_lock); - IWL_ERR(mvm, "Failed to allocate agg queue\n"); - goto release_locks; - } - mvm->queue_info[txq_id].setup_reserved = true; - spin_unlock_bh(&mvm->queue_info_lock); - - tid_data = &mvmsta->tid_data[tid]; - tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); - tid_data->txq_id = txq_id; - *ssn = tid_data->ssn; - - IWL_DEBUG_TX_QUEUES(mvm, - "Start AGG: sta %d tid %d queue %d - ssn = %d, next_recl = %d\n", - mvmsta->sta_id, tid, txq_id, tid_data->ssn, - tid_data->next_reclaimed); - - if (tid_data->ssn == tid_data->next_reclaimed) { - tid_data->state = IWL_AGG_STARTING; - ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); - } else { - tid_data->state = IWL_EMPTYING_HW_QUEUE_ADDBA; - } - - ret = 0; - -release_locks: - spin_unlock_bh(&mvmsta->lock); - - return ret; -} - -int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid, u8 buf_size) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; - unsigned int wdg_timeout = - iwl_mvm_get_wd_timeout(mvm, vif, sta->tdls, false); - int queue, fifo, ret; - u16 ssn; - - BUILD_BUG_ON((sizeof(mvmsta->agg_tids) * BITS_PER_BYTE) - != IWL_MAX_TID_COUNT); - - buf_size = min_t(int, buf_size, LINK_QUAL_AGG_FRAME_LIMIT_DEF); - - spin_lock_bh(&mvmsta->lock); - ssn = tid_data->ssn; - queue = tid_data->txq_id; - tid_data->state = IWL_AGG_ON; - mvmsta->agg_tids |= BIT(tid); - tid_data->ssn = 0xffff; - spin_unlock_bh(&mvmsta->lock); - - fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]]; - - iwl_mvm_enable_agg_txq(mvm, queue, - vif->hw_queue[tid_to_mac80211_ac[tid]], fifo, - mvmsta->sta_id, tid, buf_size, ssn, wdg_timeout); - - ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true); - if (ret) - return -EIO; - - /* No need to mark as reserved */ - spin_lock_bh(&mvm->queue_info_lock); - mvm->queue_info[queue].setup_reserved = false; - spin_unlock_bh(&mvm->queue_info_lock); - - /* - * Even though in theory the peer could have different - * aggregation reorder buffer sizes for different sessions, - * our ucode doesn't allow for that and has a global limit - * for each station. Therefore, use the minimum of all the - * aggregation sessions and our default value. - */ - mvmsta->max_agg_bufsize = - min(mvmsta->max_agg_bufsize, buf_size); - mvmsta->lq_sta.lq.agg_frame_cnt_limit = mvmsta->max_agg_bufsize; - - IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n", - sta->addr, tid); - - return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, false); -} - -int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; - u16 txq_id; - int err; - - - /* - * If mac80211 is cleaning its state, then say that we finished since - * our state has been cleared anyway. - */ - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { - ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); - return 0; - } - - spin_lock_bh(&mvmsta->lock); - - txq_id = tid_data->txq_id; - - IWL_DEBUG_TX_QUEUES(mvm, "Stop AGG: sta %d tid %d q %d state %d\n", - mvmsta->sta_id, tid, txq_id, tid_data->state); - - mvmsta->agg_tids &= ~BIT(tid); - - /* No need to mark as reserved anymore */ - spin_lock_bh(&mvm->queue_info_lock); - mvm->queue_info[txq_id].setup_reserved = false; - spin_unlock_bh(&mvm->queue_info_lock); - - switch (tid_data->state) { - case IWL_AGG_ON: - tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); - - IWL_DEBUG_TX_QUEUES(mvm, - "ssn = %d, next_recl = %d\n", - tid_data->ssn, tid_data->next_reclaimed); - - /* There are still packets for this RA / TID in the HW */ - if (tid_data->ssn != tid_data->next_reclaimed) { - tid_data->state = IWL_EMPTYING_HW_QUEUE_DELBA; - err = 0; - break; - } - - tid_data->ssn = 0xffff; - tid_data->state = IWL_AGG_OFF; - spin_unlock_bh(&mvmsta->lock); - - ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); - - iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false); - - iwl_mvm_disable_txq(mvm, txq_id, - vif->hw_queue[tid_to_mac80211_ac[tid]], tid, - 0); - return 0; - case IWL_AGG_STARTING: - case IWL_EMPTYING_HW_QUEUE_ADDBA: - /* - * The agg session has been stopped before it was set up. This - * can happen when the AddBA timer times out for example. - */ - - /* No barriers since we are under mutex */ - lockdep_assert_held(&mvm->mutex); - - ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); - tid_data->state = IWL_AGG_OFF; - err = 0; - break; - default: - IWL_ERR(mvm, - "Stopping AGG while state not ON or starting for %d on %d (%d)\n", - mvmsta->sta_id, tid, tid_data->state); - IWL_ERR(mvm, - "\ttid_data->txq_id = %d\n", tid_data->txq_id); - err = -EINVAL; - } - - spin_unlock_bh(&mvmsta->lock); - - 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 = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; - u16 txq_id; - enum iwl_mvm_agg_state old_state; - - /* - * 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); - old_state = tid_data->state; - tid_data->state = IWL_AGG_OFF; - mvmsta->agg_tids &= ~BIT(tid); - spin_unlock_bh(&mvmsta->lock); - - /* No need to mark as reserved */ - spin_lock_bh(&mvm->queue_info_lock); - mvm->queue_info[txq_id].setup_reserved = false; - spin_unlock_bh(&mvm->queue_info_lock); - - if (old_state >= IWL_AGG_ON) { - iwl_mvm_drain_sta(mvm, mvmsta, true); - if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), 0)) - IWL_ERR(mvm, "Couldn't flush the AGG queue\n"); - iwl_trans_wait_tx_queue_empty(mvm->trans, - mvmsta->tfd_queue_msk); - iwl_mvm_drain_sta(mvm, mvmsta, false); - - iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false); - - iwl_mvm_disable_txq(mvm, tid_data->txq_id, - vif->hw_queue[tid_to_mac80211_ac[tid]], tid, - 0); - } - - return 0; -} - -static int iwl_mvm_set_fw_key_idx(struct iwl_mvm *mvm) -{ - int i, max = -1, max_offs = -1; - - lockdep_assert_held(&mvm->mutex); - - /* Pick the unused key offset with the highest 'deleted' - * counter. Every time a key is deleted, all the counters - * are incremented and the one that was just deleted is - * reset to zero. Thus, the highest counter is the one - * that was deleted longest ago. Pick that one. - */ - for (i = 0; i < STA_KEY_MAX_NUM; i++) { - if (test_bit(i, mvm->fw_key_table)) - continue; - if (mvm->fw_key_deleted[i] > max) { - max = mvm->fw_key_deleted[i]; - max_offs = i; - } - } - - if (max_offs < 0) - return STA_KEY_IDX_INVALID; - - __set_bit(max_offs, mvm->fw_key_table); - - return max_offs; -} - -static u8 iwl_mvm_get_key_sta_id(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (sta) { - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - - return mvm_sta->sta_id; - } - - /* - * The device expects GTKs for station interfaces to be - * installed as GTKs for the AP station. If we have no - * station ID, then use AP's station ID. - */ - if (vif->type == NL80211_IFTYPE_STATION && - mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) { - u8 sta_id = mvmvif->ap_sta_id; - - sta = rcu_dereference_check(mvm->fw_id_to_mac_id[sta_id], - lockdep_is_held(&mvm->mutex)); - /* - * It is possible that the 'sta' parameter is NULL, - * for example when a GTK is removed - the sta_id will then - * be the AP ID, and no station was passed by mac80211. - */ - if (IS_ERR_OR_NULL(sta)) - return IWL_MVM_STATION_COUNT; - - return sta_id; - } - - return IWL_MVM_STATION_COUNT; -} - -static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, - struct iwl_mvm_sta *mvm_sta, - struct ieee80211_key_conf *keyconf, bool mcast, - u32 tkip_iv32, u16 *tkip_p1k, u32 cmd_flags, - u8 key_offset) -{ - struct iwl_mvm_add_sta_key_cmd cmd = {}; - __le16 key_flags; - int ret; - u32 status; - u16 keyidx; - int i; - u8 sta_id = mvm_sta->sta_id; - - keyidx = (keyconf->keyidx << STA_KEY_FLG_KEYID_POS) & - STA_KEY_FLG_KEYID_MSK; - key_flags = cpu_to_le16(keyidx); - key_flags |= cpu_to_le16(STA_KEY_FLG_WEP_KEY_MAP); - - switch (keyconf->cipher) { - case WLAN_CIPHER_SUITE_TKIP: - key_flags |= cpu_to_le16(STA_KEY_FLG_TKIP); - cmd.tkip_rx_tsc_byte2 = tkip_iv32; - for (i = 0; i < 5; i++) - cmd.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]); - memcpy(cmd.key, keyconf->key, keyconf->keylen); - break; - case WLAN_CIPHER_SUITE_CCMP: - key_flags |= cpu_to_le16(STA_KEY_FLG_CCM); - memcpy(cmd.key, keyconf->key, keyconf->keylen); - break; - case WLAN_CIPHER_SUITE_WEP104: - key_flags |= cpu_to_le16(STA_KEY_FLG_WEP_13BYTES); - /* fall through */ - case WLAN_CIPHER_SUITE_WEP40: - key_flags |= cpu_to_le16(STA_KEY_FLG_WEP); - memcpy(cmd.key + 3, keyconf->key, keyconf->keylen); - break; - default: - key_flags |= cpu_to_le16(STA_KEY_FLG_EXT); - memcpy(cmd.key, keyconf->key, keyconf->keylen); - } - - if (mcast) - key_flags |= cpu_to_le16(STA_KEY_MULTICAST); - - cmd.key_offset = key_offset; - cmd.key_flags = key_flags; - cmd.sta_id = sta_id; - - status = ADD_STA_SUCCESS; - if (cmd_flags & CMD_ASYNC) - ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA_KEY, CMD_ASYNC, - sizeof(cmd), &cmd); - else - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd), - &cmd, &status); - - switch (status) { - case ADD_STA_SUCCESS: - IWL_DEBUG_WEP(mvm, "MODIFY_STA: set dynamic key passed\n"); - break; - default: - ret = -EIO; - IWL_ERR(mvm, "MODIFY_STA: set dynamic key failed\n"); - break; - } - - return ret; -} - -static int iwl_mvm_send_sta_igtk(struct iwl_mvm *mvm, - struct ieee80211_key_conf *keyconf, - u8 sta_id, bool remove_key) -{ - struct iwl_mvm_mgmt_mcast_key_cmd igtk_cmd = {}; - - /* verify the key details match the required command's expectations */ - if (WARN_ON((keyconf->cipher != WLAN_CIPHER_SUITE_AES_CMAC) || - (keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE) || - (keyconf->keyidx != 4 && keyconf->keyidx != 5))) - return -EINVAL; - - igtk_cmd.key_id = cpu_to_le32(keyconf->keyidx); - igtk_cmd.sta_id = cpu_to_le32(sta_id); - - if (remove_key) { - igtk_cmd.ctrl_flags |= cpu_to_le32(STA_KEY_NOT_VALID); - } else { - struct ieee80211_key_seq seq; - const u8 *pn; - - memcpy(igtk_cmd.IGTK, keyconf->key, keyconf->keylen); - ieee80211_get_key_rx_seq(keyconf, 0, &seq); - pn = seq.aes_cmac.pn; - igtk_cmd.receive_seq_cnt = cpu_to_le64(((u64) pn[5] << 0) | - ((u64) pn[4] << 8) | - ((u64) pn[3] << 16) | - ((u64) pn[2] << 24) | - ((u64) pn[1] << 32) | - ((u64) pn[0] << 40)); - } - - IWL_DEBUG_INFO(mvm, "%s igtk for sta %u\n", - remove_key ? "removing" : "installing", - igtk_cmd.sta_id); - - return iwl_mvm_send_cmd_pdu(mvm, MGMT_MCAST_KEY, 0, - sizeof(igtk_cmd), &igtk_cmd); -} - - -static inline u8 *iwl_mvm_get_mac_addr(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (sta) - return sta->addr; - - if (vif->type == NL80211_IFTYPE_STATION && - mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) { - u8 sta_id = mvmvif->ap_sta_id; - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], - lockdep_is_held(&mvm->mutex)); - return sta->addr; - } - - - return NULL; -} - -static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *keyconf, - u8 key_offset, - bool mcast) -{ - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - int ret; - const u8 *addr; - struct ieee80211_key_seq seq; - u16 p1k[5]; - - switch (keyconf->cipher) { - case WLAN_CIPHER_SUITE_TKIP: - addr = iwl_mvm_get_mac_addr(mvm, vif, sta); - /* get phase 1 key from mac80211 */ - ieee80211_get_key_rx_seq(keyconf, 0, &seq); - ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k); - ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast, - seq.tkip.iv32, p1k, 0, key_offset); - break; - case WLAN_CIPHER_SUITE_CCMP: - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast, - 0, NULL, 0, key_offset); - break; - default: - ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast, - 0, NULL, 0, key_offset); - } - - return ret; -} - -static int __iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, u8 sta_id, - struct ieee80211_key_conf *keyconf, - bool mcast) -{ - struct iwl_mvm_add_sta_key_cmd cmd = {}; - __le16 key_flags; - int ret; - u32 status; - - key_flags = cpu_to_le16((keyconf->keyidx << STA_KEY_FLG_KEYID_POS) & - STA_KEY_FLG_KEYID_MSK); - key_flags |= cpu_to_le16(STA_KEY_FLG_NO_ENC | STA_KEY_FLG_WEP_KEY_MAP); - key_flags |= cpu_to_le16(STA_KEY_NOT_VALID); - - if (mcast) - key_flags |= cpu_to_le16(STA_KEY_MULTICAST); - - cmd.key_flags = key_flags; - cmd.key_offset = keyconf->hw_key_idx; - cmd.sta_id = sta_id; - - status = ADD_STA_SUCCESS; - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd), - &cmd, &status); - - switch (status) { - case ADD_STA_SUCCESS: - IWL_DEBUG_WEP(mvm, "MODIFY_STA: remove sta key passed\n"); - break; - default: - ret = -EIO; - IWL_ERR(mvm, "MODIFY_STA: remove sta key failed\n"); - break; - } - - return ret; -} - -int iwl_mvm_set_sta_key(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *keyconf, - u8 key_offset) -{ - bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE); - u8 sta_id; - int ret; - static const u8 __maybe_unused zero_addr[ETH_ALEN] = {0}; - - lockdep_assert_held(&mvm->mutex); - - /* Get the station id from the mvm local station table */ - sta_id = iwl_mvm_get_key_sta_id(mvm, vif, sta); - if (sta_id == IWL_MVM_STATION_COUNT) { - IWL_ERR(mvm, "Failed to find station id\n"); - return -EINVAL; - } - - if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC) { - ret = iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, false); - goto end; - } - - /* - * It is possible that the 'sta' parameter is NULL, and thus - * there is a need to retrieve the sta from the local station table. - */ - if (!sta) { - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], - lockdep_is_held(&mvm->mutex)); - if (IS_ERR_OR_NULL(sta)) { - IWL_ERR(mvm, "Invalid station id\n"); - return -EINVAL; - } - } - - if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif)) - return -EINVAL; - - /* If the key_offset is not pre-assigned, we need to find a - * new offset to use. In normal cases, the offset is not - * pre-assigned, but during HW_RESTART we want to reuse the - * same indices, so we pass them when this function is called. - * - * In D3 entry, we need to hardcoded the indices (because the - * firmware hardcodes the PTK offset to 0). In this case, we - * need to make sure we don't overwrite the hw_key_idx in the - * keyconf structure, because otherwise we cannot configure - * the original ones back when resuming. - */ - if (key_offset == STA_KEY_IDX_INVALID) { - key_offset = iwl_mvm_set_fw_key_idx(mvm); - if (key_offset == STA_KEY_IDX_INVALID) - return -ENOSPC; - keyconf->hw_key_idx = key_offset; - } - - ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, key_offset, mcast); - if (ret) { - __clear_bit(keyconf->hw_key_idx, mvm->fw_key_table); - goto end; - } - - /* - * For WEP, the same key is used for multicast and unicast. Upload it - * again, using the same key offset, and now pointing the other one - * to the same key slot (offset). - * If this fails, remove the original as well. - */ - if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 || - keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) { - ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, - key_offset, !mcast); - if (ret) { - __clear_bit(keyconf->hw_key_idx, mvm->fw_key_table); - __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast); - } - } - -end: - IWL_DEBUG_WEP(mvm, "key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n", - keyconf->cipher, keyconf->keylen, keyconf->keyidx, - sta ? sta->addr : zero_addr, ret); - return ret; -} - -int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *keyconf) -{ - bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE); - u8 sta_id; - int ret, i; - - lockdep_assert_held(&mvm->mutex); - - /* Get the station id from the mvm local station table */ - sta_id = iwl_mvm_get_key_sta_id(mvm, vif, sta); - - IWL_DEBUG_WEP(mvm, "mvm remove dynamic key: idx=%d sta=%d\n", - keyconf->keyidx, sta_id); - - if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC) - return iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, true); - - if (!__test_and_clear_bit(keyconf->hw_key_idx, mvm->fw_key_table)) { - IWL_ERR(mvm, "offset %d not used in fw key table.\n", - keyconf->hw_key_idx); - return -ENOENT; - } - - /* track which key was deleted last */ - for (i = 0; i < STA_KEY_MAX_NUM; i++) { - if (mvm->fw_key_deleted[i] < U8_MAX) - mvm->fw_key_deleted[i]++; - } - mvm->fw_key_deleted[keyconf->hw_key_idx] = 0; - - if (sta_id == IWL_MVM_STATION_COUNT) { - IWL_DEBUG_WEP(mvm, "station non-existent, early return.\n"); - return 0; - } - - ret = __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast); - if (ret) - return ret; - - /* delete WEP key twice to get rid of (now useless) offset */ - if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 || - keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) - ret = __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, !mcast); - - return ret; -} - -void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_key_conf *keyconf, - struct ieee80211_sta *sta, u32 iv32, - u16 *phase1key) -{ - struct iwl_mvm_sta *mvm_sta; - u8 sta_id; - bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE); - - rcu_read_lock(); - - sta_id = iwl_mvm_get_key_sta_id(mvm, vif, sta); - if (WARN_ON_ONCE(sta_id == IWL_MVM_STATION_COUNT)) - goto unlock; - - if (!sta) { - sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); - if (WARN_ON(IS_ERR_OR_NULL(sta))) { - rcu_read_unlock(); - return; - } - } - - mvm_sta = iwl_mvm_sta_from_mac80211(sta); - iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast, - iv32, phase1key, CMD_ASYNC, keyconf->hw_key_idx); - - unlock: - rcu_read_unlock(); -} - -void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, - struct ieee80211_sta *sta) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_add_sta_cmd cmd = { - .add_modify = STA_MODE_MODIFY, - .sta_id = mvmsta->sta_id, - .station_flags_msk = cpu_to_le32(STA_FLG_PS), - .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color), - }; - int ret; - - ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd); - if (ret) - IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); -} - -void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - enum ieee80211_frame_release_type reason, - u16 cnt, u16 tids, bool more_data, - bool agg) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_add_sta_cmd cmd = { - .add_modify = STA_MODE_MODIFY, - .sta_id = mvmsta->sta_id, - .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT, - .sleep_tx_count = cpu_to_le16(cnt), - .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color), - }; - int tid, ret; - unsigned long _tids = tids; - - /* convert TIDs to ACs - we don't support TSPEC so that's OK - * Note that this field is reserved and unused by firmware not - * supporting GO uAPSD, so it's safe to always do this. - */ - for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT) - cmd.awake_acs |= BIT(tid_to_ucode_ac[tid]); - - /* If we're releasing frames from aggregation queues then check if the - * all queues combined that we're releasing frames from have - * - more frames than the service period, in which case more_data - * needs to be set - * - fewer than 'cnt' frames, in which case we need to adjust the - * firmware command (but do that unconditionally) - */ - if (agg) { - int remaining = cnt; - - spin_lock_bh(&mvmsta->lock); - for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT) { - struct iwl_mvm_tid_data *tid_data; - u16 n_queued; - - tid_data = &mvmsta->tid_data[tid]; - if (WARN(tid_data->state != IWL_AGG_ON && - tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA, - "TID %d state is %d\n", - tid, tid_data->state)) { - spin_unlock_bh(&mvmsta->lock); - ieee80211_sta_eosp(sta); - return; - } - - n_queued = iwl_mvm_tid_queued(tid_data); - if (n_queued > remaining) { - more_data = true; - remaining = 0; - break; - } - remaining -= n_queued; - } - spin_unlock_bh(&mvmsta->lock); - - cmd.sleep_tx_count = cpu_to_le16(cnt - remaining); - if (WARN_ON(cnt - remaining == 0)) { - ieee80211_sta_eosp(sta); - return; - } - } - - /* Note: this is ignored by firmware not supporting GO uAPSD */ - if (more_data) - cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_MOREDATA); - - if (reason == IEEE80211_FRAME_RELEASE_PSPOLL) { - mvmsta->next_status_eosp = true; - cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_PS_POLL); - } else { - cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_UAPSD); - } - - ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd); - if (ret) - IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); -} - -void iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_mvm_eosp_notification *notif = (void *)pkt->data; - struct ieee80211_sta *sta; - u32 sta_id = le32_to_cpu(notif->sta_id); - - if (WARN_ON_ONCE(sta_id >= IWL_MVM_STATION_COUNT)) - return; - - rcu_read_lock(); - sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); - if (!IS_ERR_OR_NULL(sta)) - ieee80211_sta_eosp(sta); - rcu_read_unlock(); -} - -void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm, - struct iwl_mvm_sta *mvmsta, bool disable) -{ - struct iwl_mvm_add_sta_cmd cmd = { - .add_modify = STA_MODE_MODIFY, - .sta_id = mvmsta->sta_id, - .station_flags = disable ? cpu_to_le32(STA_FLG_DISABLE_TX) : 0, - .station_flags_msk = cpu_to_le32(STA_FLG_DISABLE_TX), - .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color), - }; - int ret; - - ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd); - if (ret) - IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); -} - -void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - bool disable) -{ - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - - spin_lock_bh(&mvm_sta->lock); - - if (mvm_sta->disable_tx == disable) { - spin_unlock_bh(&mvm_sta->lock); - return; - } - - mvm_sta->disable_tx = disable; - - /* - * Tell mac80211 to start/stop queuing tx for this station, - * but don't stop queuing if there are still pending frames - * for this station. - */ - if (disable || !atomic_read(&mvm->pending_frames[mvm_sta->sta_id])) - ieee80211_sta_block_awake(mvm->hw, sta, disable); - - iwl_mvm_sta_modify_disable_tx(mvm, mvm_sta, disable); - - spin_unlock_bh(&mvm_sta->lock); -} - -void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm, - struct iwl_mvm_vif *mvmvif, - bool disable) -{ - struct ieee80211_sta *sta; - struct iwl_mvm_sta *mvm_sta; - int i; - - lockdep_assert_held(&mvm->mutex); - - /* Block/unblock all the stations of the given mvmvif */ - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], - lockdep_is_held(&mvm->mutex)); - if (IS_ERR_OR_NULL(sta)) - continue; - - mvm_sta = iwl_mvm_sta_from_mac80211(sta); - if (mvm_sta->mac_id_n_color != - FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)) - continue; - - iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, disable); - } -} - -void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_sta *mvmsta; - - rcu_read_lock(); - - mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, mvmvif->ap_sta_id); - - if (!WARN_ON(!mvmsta)) - iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, true); - - rcu_read_unlock(); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h deleted file mode 100644 index 0631cc0a6..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/sta.h +++ /dev/null @@ -1,426 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * 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 __sta_h__ -#define __sta_h__ - -#include <linux/spinlock.h> -#include <net/mac80211.h> -#include <linux/wait.h> - -#include "iwl-trans.h" /* for IWL_MAX_TID_COUNT */ -#include "fw-api.h" /* IWL_MVM_STATION_COUNT */ -#include "rs.h" - -struct iwl_mvm; -struct iwl_mvm_vif; - -/** - * DOC: station table - introduction - * - * The station table is a list of data structure that reprensent the stations. - * In STA/P2P client mode, the driver will hold one station for the AP/ GO. - * In GO/AP mode, the driver will have as many stations as associated clients. - * All these stations are reflected in the fw's station table. The driver - * keeps the fw's station table up to date with the ADD_STA command. Stations - * can be removed by the REMOVE_STA command. - * - * All the data related to a station is held in the structure %iwl_mvm_sta - * which is embed in the mac80211's %ieee80211_sta (in the drv_priv) area. - * This data includes the index of the station in the fw, per tid information - * (sequence numbers, Block-ack state machine, etc...). The stations are - * created and deleted by the %sta_state callback from %ieee80211_ops. - * - * The driver holds a map: %fw_id_to_mac_id that allows to fetch a - * %ieee80211_sta (and the %iwl_mvm_sta embedded into it) based on a fw - * station index. That way, the driver is able to get the tid related data in - * O(1) in time sensitive paths (Tx / Tx response / BA notification). These - * paths are triggered by the fw, and the driver needs to get a pointer to the - * %ieee80211 structure. This map helps to get that pointer quickly. - */ - -/** - * DOC: station table - locking - * - * As stated before, the station is created / deleted by mac80211's %sta_state - * callback from %ieee80211_ops which can sleep. The next paragraph explains - * the locking of a single stations, the next ones relates to the station - * table. - * - * The station holds the sequence number per tid. So this data needs to be - * accessed in the Tx path (which is softIRQ). It also holds the Block-Ack - * information (the state machine / and the logic that checks if the queues - * were drained), so it also needs to be accessible from the Tx response flow. - * In short, the station needs to be access from sleepable context as well as - * from tasklets, so the station itself needs a spinlock. - * - * The writers of %fw_id_to_mac_id map are serialized by the global mutex of - * the mvm op_mode. This is possible since %sta_state can sleep. - * The pointers in this map are RCU protected, hence we won't replace the - * station while we have Tx / Tx response / BA notification running. - * - * If a station is deleted while it still has packets in its A-MPDU queues, - * then the reclaim flow will notice that there is no station in the map for - * sta_id and it will dump the responses. - */ - -/** - * DOC: station table - internal stations - * - * The FW needs a few internal stations that are not reflected in - * mac80211, such as broadcast station in AP / GO mode, or AUX sta for - * scanning and P2P device (during the GO negotiation). - * For these kind of stations we have %iwl_mvm_int_sta struct which holds the - * data relevant for them from both %iwl_mvm_sta and %ieee80211_sta. - * Usually the data for these stations is static, so no locking is required, - * and no TID data as this is also not needed. - * One thing to note, is that these stations have an ID in the fw, but not - * in mac80211. In order to "reserve" them a sta_id in %fw_id_to_mac_id - * we fill ERR_PTR(EINVAL) in this mapping and all other dereferencing of - * pointers from this mapping need to check that the value is not error - * or NULL. - * - * Currently there is only one auxiliary station for scanning, initialized - * on init. - */ - -/** - * DOC: station table - AP Station in STA mode - * - * %iwl_mvm_vif includes the index of the AP station in the fw's STA table: - * %ap_sta_id. To get the point to the corresponding %ieee80211_sta, - * &fw_id_to_mac_id can be used. Due to the way the fw works, we must not remove - * the AP station from the fw before setting the MAC context as unassociated. - * Hence, %fw_id_to_mac_id[%ap_sta_id] will be NULLed when the AP station is - * removed by mac80211, but the station won't be removed in the fw until the - * VIF is set as unassociated. Then, %ap_sta_id will be invalidated. - */ - -/** - * DOC: station table - Drain vs. Flush - * - * Flush means that all the frames in the SCD queue are dumped regardless the - * station to which they were sent. We do that when we disassociate and before - * we remove the STA of the AP. The flush can be done synchronously against the - * fw. - * Drain means that the fw will drop all the frames sent to a specific station. - * This is useful when a client (if we are IBSS / GO or AP) disassociates. In - * that case, we need to drain all the frames for that client from the AC queues - * that are shared with the other clients. Only then, we can remove the STA in - * the fw. In order to do so, we track the non-AMPDU packets for each station. - * If mac80211 removes a STA and if it still has non-AMPDU packets pending in - * the queues, we mark this station as %EBUSY in %fw_id_to_mac_id, and drop all - * the frames for this STA (%iwl_mvm_rm_sta). When the last frame is dropped - * (we know about it with its Tx response), we remove the station in fw and set - * it as %NULL in %fw_id_to_mac_id: this is the purpose of - * %iwl_mvm_sta_drained_wk. - */ - -/** - * DOC: station table - fw restart - * - * When the fw asserts, or we have any other issue that requires to reset the - * driver, we require mac80211 to reconfigure the driver. Since the private - * data of the stations is embed in mac80211's %ieee80211_sta, that data will - * not be zeroed and needs to be reinitialized manually. - * %IWL_MVM_STATUS_IN_HW_RESTART is set during restart and that will hint us - * that we must not allocate a new sta_id but reuse the previous one. This - * means that the stations being re-added after the reset will have the same - * place in the fw as before the reset. We do need to zero the %fw_id_to_mac_id - * map, since the stations aren't in the fw any more. Internal stations that - * are not added by mac80211 will be re-added in the init flow that is called - * after the restart: mac80211 call's %iwl_mvm_mac_start which calls to - * %iwl_mvm_up. - */ - -/** - * DOC: AP mode - PS - * - * When a station is asleep, the fw will set it as "asleep". All frames on - * shared queues (i.e. non-aggregation queues) to that station will be dropped - * by the fw (%TX_STATUS_FAIL_DEST_PS failure code). - * - * AMPDUs are in a separate queue that is stopped by the fw. We just need to - * let mac80211 know when there are frames in these queues so that it can - * properly handle trigger frames. - * - * When a trigger frame is received, mac80211 tells the driver to send frames - * from the AMPDU queues or sends frames to non-aggregation queues itself, - * depending on which ACs are delivery-enabled and what TID has frames to - * transmit. Note that mac80211 has all the knowledge since all the non-agg - * frames are buffered / filtered, and the driver tells mac80211 about agg - * frames). The driver needs to tell the fw to let frames out even if the - * station is asleep. This is done by %iwl_mvm_sta_modify_sleep_tx_count. - * - * When we receive a frame from that station with PM bit unset, the driver - * needs to let the fw know that this station isn't asleep any more. This is - * done by %iwl_mvm_sta_modify_ps_wake in response to mac80211 signaling the - * station's wakeup. - * - * For a GO, the Service Period might be cut short due to an absence period - * of the GO. In this (and all other cases) the firmware notifies us with the - * EOSP_NOTIFICATION, and we notify mac80211 of that. Further frames that we - * already sent to the device will be rejected again. - * - * See also "AP support for powersaving clients" in mac80211.h. - */ - -/** - * enum iwl_mvm_agg_state - * - * The state machine of the BA agreement establishment / tear down. - * These states relate to a specific RA / TID. - * - * @IWL_AGG_OFF: aggregation is not used - * @IWL_AGG_STARTING: aggregation are starting (between start and oper) - * @IWL_AGG_ON: aggregation session is up - * @IWL_EMPTYING_HW_QUEUE_ADDBA: establishing a BA session - waiting for the - * HW queue to be empty from packets for this RA /TID. - * @IWL_EMPTYING_HW_QUEUE_DELBA: tearing down a BA session - waiting for the - * HW queue to be empty from packets for this RA /TID. - */ -enum iwl_mvm_agg_state { - IWL_AGG_OFF = 0, - IWL_AGG_STARTING, - IWL_AGG_ON, - IWL_EMPTYING_HW_QUEUE_ADDBA, - IWL_EMPTYING_HW_QUEUE_DELBA, -}; - -/** - * struct iwl_mvm_tid_data - holds the states for each RA / TID - * @seq_number: the next WiFi sequence number to use - * @next_reclaimed: the WiFi sequence number of the next packet to be acked. - * This is basically (last acked packet++). - * @rate_n_flags: Rate at which Tx was attempted. Holds the data between the - * Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA). - * @reduced_tpc: Reduced tx power. Holds the data between the - * Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA). - * @state: state of the BA agreement establishment / tear down. - * @txq_id: Tx queue used by the BA session - * @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or - * the first packet to be sent in legacy HW queue in Tx AGG stop flow. - * Basically when next_reclaimed reaches ssn, we can tell mac80211 that - * we are ready to finish the Tx AGG stop / start flow. - * @tx_time: medium time consumed by this A-MPDU - */ -struct iwl_mvm_tid_data { - u16 seq_number; - u16 next_reclaimed; - /* The rest is Tx AGG related */ - u32 rate_n_flags; - u8 reduced_tpc; - enum iwl_mvm_agg_state state; - u16 txq_id; - u16 ssn; - u16 tx_time; -}; - -static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data) -{ - return ieee80211_sn_sub(IEEE80211_SEQ_TO_SN(tid_data->seq_number), - tid_data->next_reclaimed); -} - -/** - * struct iwl_mvm_sta - representation of a station in the driver - * @sta_id: the index of the station in the fw (will be replaced by id_n_color) - * @tfd_queue_msk: the tfd queues used by the station - * @hw_queue: per-AC mapping of the TFD queues used by station - * @mac_id_n_color: the MAC context this station is linked to - * @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 - * @next_status_eosp: the next reclaimed packet is a PS-Poll response and - * we need to signal the EOSP - * @lock: lock to protect the whole struct. Since %tid_data is access from Tx - * and from Tx response flow, it needs a spinlock. - * @tid_data: per tid data. Look at %iwl_mvm_tid_data. - * @tx_protection: reference counter for controlling the Tx protection. - * @tt_tx_protection: is thermal throttling enable Tx protection? - * @disable_tx: is tx to this STA disabled? - * @agg_tids: bitmap of tids whose status is operational aggregated (IWL_AGG_ON) - * - * When mac80211 creates a station it reserves some space (hw->sta_data_size) - * in the structure for use by driver. This structure is placed in that - * space. - * - */ -struct iwl_mvm_sta { - u32 sta_id; - u32 tfd_queue_msk; - u8 hw_queue[IEEE80211_NUM_ACS]; - u32 mac_id_n_color; - u16 tid_disable_agg; - u8 max_agg_bufsize; - bool bt_reduced_txpower; - bool next_status_eosp; - spinlock_t lock; - struct iwl_mvm_tid_data tid_data[IWL_MAX_TID_COUNT]; - struct iwl_lq_sta lq_sta; - struct ieee80211_vif *vif; - - /* Temporary, until the new TLC will control the Tx protection */ - s8 tx_protection; - bool tt_tx_protection; - - bool disable_tx; - u8 agg_tids; -}; - -static inline struct iwl_mvm_sta * -iwl_mvm_sta_from_mac80211(struct ieee80211_sta *sta) -{ - return (void *)sta->drv_priv; -} - -/** - * struct iwl_mvm_int_sta - representation of an internal station (auxiliary or - * broadcast) - * @sta_id: the index of the station in the fw (will be replaced by id_n_color) - * @tfd_queue_msk: the tfd queues used by the station - */ -struct iwl_mvm_int_sta { - u32 sta_id; - u32 tfd_queue_msk; -}; - -int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - bool update); -int iwl_mvm_add_sta(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta); -int iwl_mvm_update_sta(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta); -int iwl_mvm_rm_sta(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta); -int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u8 sta_id); -int iwl_mvm_set_sta_key(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *keyconf, - u8 key_offset); -int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *keyconf); - -void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_key_conf *keyconf, - struct ieee80211_sta *sta, u32 iv32, - u16 *phase1key); - -void iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); - -/* AMPDU */ -int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - int tid, u16 ssn, bool start); -int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid, u16 *ssn); -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); -void iwl_mvm_del_aux_sta(struct iwl_mvm *mvm); - -int iwl_mvm_alloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -void iwl_mvm_dealloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); - -void iwl_mvm_sta_drained_wk(struct work_struct *wk); -void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, - struct ieee80211_sta *sta); -void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - enum ieee80211_frame_release_type reason, - u16 cnt, u16 tids, bool more_data, - bool agg); -int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, - bool drain); -void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm, - struct iwl_mvm_sta *mvmsta, bool disable); -void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - bool disable); -void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm, - struct iwl_mvm_vif *mvmvif, - bool disable); -void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif); - -#endif /* __sta_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/tdls.c b/drivers/net/wireless/iwlwifi/mvm/tdls.c deleted file mode 100644 index fe2fa5650..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/tdls.c +++ /dev/null @@ -1,732 +0,0 @@ -/****************************************************************************** - * - * 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) 2014 Intel Mobile Communications GmbH - * - * 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) 2014 Intel Mobile Communications GmbH - * 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 <linux/etherdevice.h> -#include "mvm.h" -#include "time-event.h" -#include "iwl-io.h" -#include "iwl-prph.h" - -#define TU_TO_US(x) (x * 1024) -#define TU_TO_MS(x) (TU_TO_US(x) / 1000) - -void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm) -{ - struct ieee80211_sta *sta; - struct iwl_mvm_sta *mvmsta; - int i; - - lockdep_assert_held(&mvm->mutex); - - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], - lockdep_is_held(&mvm->mutex)); - if (!sta || IS_ERR(sta) || !sta->tdls) - continue; - - mvmsta = iwl_mvm_sta_from_mac80211(sta); - ieee80211_tdls_oper_request(mvmsta->vif, sta->addr, - NL80211_TDLS_TEARDOWN, - WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, - GFP_KERNEL); - } -} - -int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct ieee80211_sta *sta; - struct iwl_mvm_sta *mvmsta; - int count = 0; - int i; - - lockdep_assert_held(&mvm->mutex); - - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], - lockdep_is_held(&mvm->mutex)); - if (!sta || IS_ERR(sta) || !sta->tdls) - continue; - - if (vif) { - mvmsta = iwl_mvm_sta_from_mac80211(sta); - if (mvmsta->vif != vif) - continue; - } - - count++; - } - - return count; -} - -static void iwl_mvm_tdls_config(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_rx_packet *pkt; - struct iwl_tdls_config_res *resp; - struct iwl_tdls_config_cmd tdls_cfg_cmd = {}; - struct iwl_host_cmd cmd = { - .id = TDLS_CONFIG_CMD, - .flags = CMD_WANT_SKB, - .data = { &tdls_cfg_cmd, }, - .len = { sizeof(struct iwl_tdls_config_cmd), }, - }; - struct ieee80211_sta *sta; - int ret, i, cnt; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - lockdep_assert_held(&mvm->mutex); - - tdls_cfg_cmd.id_and_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); - tdls_cfg_cmd.tx_to_ap_tid = IWL_MVM_TDLS_FW_TID; - tdls_cfg_cmd.tx_to_ap_ssn = cpu_to_le16(0); /* not used for now */ - - /* for now the Tx cmd is empty and unused */ - - /* populate TDLS peer data */ - cnt = 0; - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], - lockdep_is_held(&mvm->mutex)); - if (IS_ERR_OR_NULL(sta) || !sta->tdls) - continue; - - tdls_cfg_cmd.sta_info[cnt].sta_id = i; - tdls_cfg_cmd.sta_info[cnt].tx_to_peer_tid = - IWL_MVM_TDLS_FW_TID; - tdls_cfg_cmd.sta_info[cnt].tx_to_peer_ssn = cpu_to_le16(0); - tdls_cfg_cmd.sta_info[cnt].is_initiator = - cpu_to_le32(sta->tdls_initiator ? 1 : 0); - - cnt++; - } - - tdls_cfg_cmd.tdls_peer_count = cnt; - IWL_DEBUG_TDLS(mvm, "send TDLS config to FW for %d peers\n", cnt); - - ret = iwl_mvm_send_cmd(mvm, &cmd); - if (WARN_ON_ONCE(ret)) - return; - - pkt = cmd.resp_pkt; - - WARN_ON_ONCE(iwl_rx_packet_payload_len(pkt) != sizeof(*resp)); - - /* we don't really care about the response at this point */ - - iwl_free_resp(&cmd); -} - -void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - bool sta_added) -{ - int tdls_sta_cnt = iwl_mvm_tdls_sta_count(mvm, vif); - - /* when the first peer joins, send a power update first */ - if (tdls_sta_cnt == 1 && sta_added) - iwl_mvm_power_update_mac(mvm); - - /* configure the FW with TDLS peer info */ - iwl_mvm_tdls_config(mvm, vif); - - /* when the last peer leaves, send a power update last */ - if (tdls_sta_cnt == 0 && !sta_added) - iwl_mvm_power_update_mac(mvm); -} - -void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - u32 duration = 2 * vif->bss_conf.dtim_period * vif->bss_conf.beacon_int; - - /* - * iwl_mvm_protect_session() reads directly from the device - * (the system time), so make sure it is available. - */ - if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PROTECT_TDLS)) - return; - - mutex_lock(&mvm->mutex); - /* Protect the session to hear the TDLS setup response on the channel */ - iwl_mvm_protect_session(mvm, vif, duration, duration, 100, true); - mutex_unlock(&mvm->mutex); - - iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_TDLS); -} - -static const char * -iwl_mvm_tdls_cs_state_str(enum iwl_mvm_tdls_cs_state state) -{ - switch (state) { - case IWL_MVM_TDLS_SW_IDLE: - return "IDLE"; - case IWL_MVM_TDLS_SW_REQ_SENT: - return "REQ SENT"; - case IWL_MVM_TDLS_SW_RESP_RCVD: - return "RESP RECEIVED"; - case IWL_MVM_TDLS_SW_REQ_RCVD: - return "REQ RECEIVED"; - case IWL_MVM_TDLS_SW_ACTIVE: - return "ACTIVE"; - } - - return NULL; -} - -static void iwl_mvm_tdls_update_cs_state(struct iwl_mvm *mvm, - enum iwl_mvm_tdls_cs_state state) -{ - if (mvm->tdls_cs.state == state) - return; - - IWL_DEBUG_TDLS(mvm, "TDLS channel switch state: %s -> %s\n", - iwl_mvm_tdls_cs_state_str(mvm->tdls_cs.state), - iwl_mvm_tdls_cs_state_str(state)); - mvm->tdls_cs.state = state; - - /* we only send requests to our switching peer - update sent time */ - if (state == IWL_MVM_TDLS_SW_REQ_SENT) - mvm->tdls_cs.peer.sent_timestamp = - iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG); - - if (state == IWL_MVM_TDLS_SW_IDLE) - mvm->tdls_cs.cur_sta_id = IWL_MVM_STATION_COUNT; -} - -void iwl_mvm_rx_tdls_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_tdls_channel_switch_notif *notif = (void *)pkt->data; - struct ieee80211_sta *sta; - unsigned int delay; - struct iwl_mvm_sta *mvmsta; - struct ieee80211_vif *vif; - u32 sta_id = le32_to_cpu(notif->sta_id); - - lockdep_assert_held(&mvm->mutex); - - /* can fail sometimes */ - if (!le32_to_cpu(notif->status)) { - iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE); - return; - } - - if (WARN_ON(sta_id >= IWL_MVM_STATION_COUNT)) - return; - - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], - lockdep_is_held(&mvm->mutex)); - /* the station may not be here, but if it is, it must be a TDLS peer */ - if (IS_ERR_OR_NULL(sta) || WARN_ON(!sta->tdls)) - return; - - mvmsta = iwl_mvm_sta_from_mac80211(sta); - vif = mvmsta->vif; - - /* - * Update state and possibly switch again after this is over (DTIM). - * Also convert TU to msec. - */ - delay = TU_TO_MS(vif->bss_conf.dtim_period * vif->bss_conf.beacon_int); - mod_delayed_work(system_wq, &mvm->tdls_cs.dwork, - msecs_to_jiffies(delay)); - - iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_ACTIVE); -} - -static int -iwl_mvm_tdls_check_action(struct iwl_mvm *mvm, - enum iwl_tdls_channel_switch_type type, - const u8 *peer, bool peer_initiator, u32 timestamp) -{ - bool same_peer = false; - int ret = 0; - - /* get the existing peer if it's there */ - if (mvm->tdls_cs.state != IWL_MVM_TDLS_SW_IDLE && - mvm->tdls_cs.cur_sta_id != IWL_MVM_STATION_COUNT) { - struct ieee80211_sta *sta = rcu_dereference_protected( - mvm->fw_id_to_mac_id[mvm->tdls_cs.cur_sta_id], - lockdep_is_held(&mvm->mutex)); - if (!IS_ERR_OR_NULL(sta)) - same_peer = ether_addr_equal(peer, sta->addr); - } - - switch (mvm->tdls_cs.state) { - case IWL_MVM_TDLS_SW_IDLE: - /* - * might be spurious packet from the peer after the switch is - * already done - */ - if (type == TDLS_MOVE_CH) - ret = -EINVAL; - break; - case IWL_MVM_TDLS_SW_REQ_SENT: - /* only allow requests from the same peer */ - if (!same_peer) - ret = -EBUSY; - else if (type == TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH && - !peer_initiator) - /* - * We received a ch-switch request while an outgoing - * one is pending. Allow it if the peer is the link - * initiator. - */ - ret = -EBUSY; - else if (type == TDLS_SEND_CHAN_SW_REQ) - /* wait for idle before sending another request */ - ret = -EBUSY; - else if (timestamp <= mvm->tdls_cs.peer.sent_timestamp) - /* we got a stale response - ignore it */ - ret = -EINVAL; - break; - case IWL_MVM_TDLS_SW_RESP_RCVD: - /* - * we are waiting for the FW to give an "active" notification, - * so ignore requests in the meantime - */ - ret = -EBUSY; - break; - case IWL_MVM_TDLS_SW_REQ_RCVD: - /* as above, allow the link initiator to proceed */ - if (type == TDLS_SEND_CHAN_SW_REQ) { - if (!same_peer) - ret = -EBUSY; - else if (peer_initiator) /* they are the initiator */ - ret = -EBUSY; - } else if (type == TDLS_MOVE_CH) { - ret = -EINVAL; - } - break; - case IWL_MVM_TDLS_SW_ACTIVE: - /* - * the only valid request when active is a request to return - * to the base channel by the current off-channel peer - */ - if (type != TDLS_MOVE_CH || !same_peer) - ret = -EBUSY; - break; - } - - if (ret) - IWL_DEBUG_TDLS(mvm, - "Invalid TDLS action %d state %d peer %pM same_peer %d initiator %d\n", - type, mvm->tdls_cs.state, peer, same_peer, - peer_initiator); - - return ret; -} - -static int -iwl_mvm_tdls_config_channel_switch(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - enum iwl_tdls_channel_switch_type type, - const u8 *peer, bool peer_initiator, - u8 oper_class, - struct cfg80211_chan_def *chandef, - u32 timestamp, u16 switch_time, - u16 switch_timeout, struct sk_buff *skb, - u32 ch_sw_tm_ie) -{ - struct ieee80211_sta *sta; - struct iwl_mvm_sta *mvmsta; - struct ieee80211_tx_info *info; - struct ieee80211_hdr *hdr; - struct iwl_tdls_channel_switch_cmd cmd = {0}; - int ret; - - lockdep_assert_held(&mvm->mutex); - - ret = iwl_mvm_tdls_check_action(mvm, type, peer, peer_initiator, - timestamp); - if (ret) - return ret; - - if (!skb || WARN_ON(skb->len > IWL_TDLS_CH_SW_FRAME_MAX_SIZE)) { - ret = -EINVAL; - goto out; - } - - cmd.switch_type = type; - cmd.timing.frame_timestamp = cpu_to_le32(timestamp); - cmd.timing.switch_time = cpu_to_le32(switch_time); - cmd.timing.switch_timeout = cpu_to_le32(switch_timeout); - - rcu_read_lock(); - sta = ieee80211_find_sta(vif, peer); - if (!sta) { - rcu_read_unlock(); - ret = -ENOENT; - goto out; - } - mvmsta = iwl_mvm_sta_from_mac80211(sta); - cmd.peer_sta_id = cpu_to_le32(mvmsta->sta_id); - - if (!chandef) { - if (mvm->tdls_cs.state == IWL_MVM_TDLS_SW_REQ_SENT && - mvm->tdls_cs.peer.chandef.chan) { - /* actually moving to the channel */ - chandef = &mvm->tdls_cs.peer.chandef; - } else if (mvm->tdls_cs.state == IWL_MVM_TDLS_SW_ACTIVE && - type == TDLS_MOVE_CH) { - /* we need to return to base channel */ - struct ieee80211_chanctx_conf *chanctx = - rcu_dereference(vif->chanctx_conf); - - if (WARN_ON_ONCE(!chanctx)) { - rcu_read_unlock(); - goto out; - } - - chandef = &chanctx->def; - } - } - - if (chandef) { - cmd.ci.band = (chandef->chan->band == IEEE80211_BAND_2GHZ ? - PHY_BAND_24 : PHY_BAND_5); - cmd.ci.channel = chandef->chan->hw_value; - cmd.ci.width = iwl_mvm_get_channel_width(chandef); - cmd.ci.ctrl_pos = iwl_mvm_get_ctrl_pos(chandef); - } - - /* keep quota calculation simple for now - 50% of DTIM for TDLS */ - cmd.timing.max_offchan_duration = - cpu_to_le32(TU_TO_US(vif->bss_conf.dtim_period * - vif->bss_conf.beacon_int) / 2); - - /* Switch time is the first element in the switch-timing IE. */ - cmd.frame.switch_time_offset = cpu_to_le32(ch_sw_tm_ie + 2); - - info = IEEE80211_SKB_CB(skb); - hdr = (void *)skb->data; - if (info->control.hw_key) { - if (info->control.hw_key->cipher != WLAN_CIPHER_SUITE_CCMP) { - rcu_read_unlock(); - ret = -EINVAL; - goto out; - } - iwl_mvm_set_tx_cmd_ccmp(info, &cmd.frame.tx_cmd); - } - - iwl_mvm_set_tx_cmd(mvm, skb, &cmd.frame.tx_cmd, info, - mvmsta->sta_id); - - iwl_mvm_set_tx_cmd_rate(mvm, &cmd.frame.tx_cmd, info, sta, - hdr->frame_control); - rcu_read_unlock(); - - memcpy(cmd.frame.data, skb->data, skb->len); - - ret = iwl_mvm_send_cmd_pdu(mvm, TDLS_CHANNEL_SWITCH_CMD, 0, - sizeof(cmd), &cmd); - if (ret) { - IWL_ERR(mvm, "Failed to send TDLS_CHANNEL_SWITCH cmd: %d\n", - ret); - goto out; - } - - /* channel switch has started, update state */ - if (type != TDLS_MOVE_CH) { - mvm->tdls_cs.cur_sta_id = mvmsta->sta_id; - iwl_mvm_tdls_update_cs_state(mvm, - type == TDLS_SEND_CHAN_SW_REQ ? - IWL_MVM_TDLS_SW_REQ_SENT : - IWL_MVM_TDLS_SW_REQ_RCVD); - } else { - iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_RESP_RCVD); - } - -out: - - /* channel switch failed - we are idle */ - if (ret) - iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE); - - return ret; -} - -void iwl_mvm_tdls_ch_switch_work(struct work_struct *work) -{ - struct iwl_mvm *mvm; - struct ieee80211_sta *sta; - struct iwl_mvm_sta *mvmsta; - struct ieee80211_vif *vif; - unsigned int delay; - int ret; - - mvm = container_of(work, struct iwl_mvm, tdls_cs.dwork.work); - mutex_lock(&mvm->mutex); - - /* called after an active channel switch has finished or timed-out */ - iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE); - - /* station might be gone, in that case do nothing */ - if (mvm->tdls_cs.peer.sta_id == IWL_MVM_STATION_COUNT) - goto out; - - sta = rcu_dereference_protected( - mvm->fw_id_to_mac_id[mvm->tdls_cs.peer.sta_id], - lockdep_is_held(&mvm->mutex)); - /* the station may not be here, but if it is, it must be a TDLS peer */ - if (!sta || IS_ERR(sta) || WARN_ON(!sta->tdls)) - goto out; - - mvmsta = iwl_mvm_sta_from_mac80211(sta); - vif = mvmsta->vif; - ret = iwl_mvm_tdls_config_channel_switch(mvm, vif, - TDLS_SEND_CHAN_SW_REQ, - sta->addr, - mvm->tdls_cs.peer.initiator, - mvm->tdls_cs.peer.op_class, - &mvm->tdls_cs.peer.chandef, - 0, 0, 0, - mvm->tdls_cs.peer.skb, - mvm->tdls_cs.peer.ch_sw_tm_ie); - if (ret) - IWL_ERR(mvm, "Not sending TDLS channel switch: %d\n", ret); - - /* retry after a DTIM if we failed sending now */ - delay = TU_TO_MS(vif->bss_conf.dtim_period * vif->bss_conf.beacon_int); - queue_delayed_work(system_wq, &mvm->tdls_cs.dwork, - msecs_to_jiffies(delay)); -out: - mutex_unlock(&mvm->mutex); -} - -int -iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u8 oper_class, - struct cfg80211_chan_def *chandef, - struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_sta *mvmsta; - unsigned int delay; - int ret; - - mutex_lock(&mvm->mutex); - - IWL_DEBUG_TDLS(mvm, "TDLS channel switch with %pM ch %d width %d\n", - sta->addr, chandef->chan->center_freq, chandef->width); - - /* we only support a single peer for channel switching */ - if (mvm->tdls_cs.peer.sta_id != IWL_MVM_STATION_COUNT) { - IWL_DEBUG_TDLS(mvm, - "Existing peer. Can't start switch with %pM\n", - sta->addr); - ret = -EBUSY; - goto out; - } - - ret = iwl_mvm_tdls_config_channel_switch(mvm, vif, - TDLS_SEND_CHAN_SW_REQ, - sta->addr, sta->tdls_initiator, - oper_class, chandef, 0, 0, 0, - tmpl_skb, ch_sw_tm_ie); - if (ret) - goto out; - - /* - * Mark the peer as "in tdls switch" for this vif. We only allow a - * single such peer per vif. - */ - mvm->tdls_cs.peer.skb = skb_copy(tmpl_skb, GFP_KERNEL); - if (!mvm->tdls_cs.peer.skb) { - ret = -ENOMEM; - goto out; - } - - mvmsta = iwl_mvm_sta_from_mac80211(sta); - mvm->tdls_cs.peer.sta_id = mvmsta->sta_id; - mvm->tdls_cs.peer.chandef = *chandef; - mvm->tdls_cs.peer.initiator = sta->tdls_initiator; - mvm->tdls_cs.peer.op_class = oper_class; - mvm->tdls_cs.peer.ch_sw_tm_ie = ch_sw_tm_ie; - - /* - * Wait for 2 DTIM periods before attempting the next switch. The next - * switch will be made sooner if the current one completes before that. - */ - delay = 2 * TU_TO_MS(vif->bss_conf.dtim_period * - vif->bss_conf.beacon_int); - mod_delayed_work(system_wq, &mvm->tdls_cs.dwork, - msecs_to_jiffies(delay)); - -out: - mutex_unlock(&mvm->mutex); - return ret; -} - -void iwl_mvm_tdls_cancel_channel_switch(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct ieee80211_sta *cur_sta; - bool wait_for_phy = false; - - mutex_lock(&mvm->mutex); - - IWL_DEBUG_TDLS(mvm, "TDLS cancel channel switch with %pM\n", sta->addr); - - /* we only support a single peer for channel switching */ - if (mvm->tdls_cs.peer.sta_id == IWL_MVM_STATION_COUNT) { - IWL_DEBUG_TDLS(mvm, "No ch switch peer - %pM\n", sta->addr); - goto out; - } - - cur_sta = rcu_dereference_protected( - mvm->fw_id_to_mac_id[mvm->tdls_cs.peer.sta_id], - lockdep_is_held(&mvm->mutex)); - /* make sure it's the same peer */ - if (cur_sta != sta) - goto out; - - /* - * If we're currently in a switch because of the now canceled peer, - * wait a DTIM here to make sure the phy is back on the base channel. - * We can't otherwise force it. - */ - if (mvm->tdls_cs.cur_sta_id == mvm->tdls_cs.peer.sta_id && - mvm->tdls_cs.state != IWL_MVM_TDLS_SW_IDLE) - wait_for_phy = true; - - mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT; - dev_kfree_skb(mvm->tdls_cs.peer.skb); - mvm->tdls_cs.peer.skb = NULL; - -out: - mutex_unlock(&mvm->mutex); - - /* make sure the phy is on the base channel */ - if (wait_for_phy) - msleep(TU_TO_MS(vif->bss_conf.dtim_period * - vif->bss_conf.beacon_int)); - - /* flush the channel switch state */ - flush_delayed_work(&mvm->tdls_cs.dwork); - - IWL_DEBUG_TDLS(mvm, "TDLS ending channel switch with %pM\n", sta->addr); -} - -void -iwl_mvm_tdls_recv_channel_switch(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_tdls_ch_sw_params *params) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - enum iwl_tdls_channel_switch_type type; - unsigned int delay; - const char *action_str = - params->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ? - "REQ" : "RESP"; - - mutex_lock(&mvm->mutex); - - IWL_DEBUG_TDLS(mvm, - "Received TDLS ch switch action %s from %pM status %d\n", - action_str, params->sta->addr, params->status); - - /* - * we got a non-zero status from a peer we were switching to - move to - * the idle state and retry again later - */ - if (params->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE && - params->status != 0 && - mvm->tdls_cs.state == IWL_MVM_TDLS_SW_REQ_SENT && - mvm->tdls_cs.cur_sta_id != IWL_MVM_STATION_COUNT) { - struct ieee80211_sta *cur_sta; - - /* make sure it's the same peer */ - cur_sta = rcu_dereference_protected( - mvm->fw_id_to_mac_id[mvm->tdls_cs.cur_sta_id], - lockdep_is_held(&mvm->mutex)); - if (cur_sta == params->sta) { - iwl_mvm_tdls_update_cs_state(mvm, - IWL_MVM_TDLS_SW_IDLE); - goto retry; - } - } - - type = (params->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST) ? - TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH : TDLS_MOVE_CH; - - iwl_mvm_tdls_config_channel_switch(mvm, vif, type, params->sta->addr, - params->sta->tdls_initiator, 0, - params->chandef, params->timestamp, - params->switch_time, - params->switch_timeout, - params->tmpl_skb, - params->ch_sw_tm_ie); - -retry: - /* register a timeout in case we don't succeed in switching */ - delay = vif->bss_conf.dtim_period * vif->bss_conf.beacon_int * - 1024 / 1000; - mod_delayed_work(system_wq, &mvm->tdls_cs.dwork, - msecs_to_jiffies(delay)); - mutex_unlock(&mvm->mutex); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/testmode.h b/drivers/net/wireless/iwlwifi/mvm/testmode.h deleted file mode 100644 index 79ab6beb6..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/testmode.h +++ /dev/null @@ -1,97 +0,0 @@ -/****************************************************************************** - * - * 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 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * 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 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * 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 __IWL_MVM_TESTMODE_H__ -#define __IWL_MVM_TESTMODE_H__ - -/** - * enum iwl_mvm_testmode_attrs - testmode attributes inside NL80211_ATTR_TESTDATA - * @IWL_MVM_TM_ATTR_UNSPEC: (invalid attribute) - * @IWL_MVM_TM_ATTR_CMD: sub command, see &enum iwl_mvm_testmode_commands (u32) - * @IWL_MVM_TM_ATTR_NOA_DURATION: requested NoA duration (u32) - * @IWL_MVM_TM_ATTR_BEACON_FILTER_STATE: beacon filter state (0 or 1, u32) - */ -enum iwl_mvm_testmode_attrs { - IWL_MVM_TM_ATTR_UNSPEC, - IWL_MVM_TM_ATTR_CMD, - IWL_MVM_TM_ATTR_NOA_DURATION, - IWL_MVM_TM_ATTR_BEACON_FILTER_STATE, - - /* keep last */ - NUM_IWL_MVM_TM_ATTRS, - IWL_MVM_TM_ATTR_MAX = NUM_IWL_MVM_TM_ATTRS - 1, -}; - -/** - * enum iwl_mvm_testmode_commands - MVM testmode commands - * @IWL_MVM_TM_CMD_SET_NOA: set NoA on GO vif for testing - * @IWL_MVM_TM_CMD_SET_BEACON_FILTER: turn beacon filtering off/on - */ -enum iwl_mvm_testmode_commands { - IWL_MVM_TM_CMD_SET_NOA, - IWL_MVM_TM_CMD_SET_BEACON_FILTER, -}; - -#endif /* __IWL_MVM_TESTMODE_H__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c deleted file mode 100644 index 7530eb230..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.c +++ /dev/null @@ -1,872 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * 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 <linux/jiffies.h> -#include <net/mac80211.h> - -#include "iwl-notif-wait.h" -#include "iwl-trans.h" -#include "fw-api.h" -#include "time-event.h" -#include "mvm.h" -#include "iwl-io.h" -#include "iwl-prph.h" - -/* - * 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_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) -{ - lockdep_assert_held(&mvm->time_event_lock); - - if (!te_data->vif) - return; - - list_del(&te_data->list); - te_data->running = false; - te_data->uid = 0; - te_data->id = TE_MAX; - te_data->vif = NULL; -} - -void iwl_mvm_roc_done_wk(struct work_struct *wk) -{ - struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, roc_done_wk); - u32 queues = 0; - - /* - * Clear the ROC_RUNNING /ROC_AUX_RUNNING status bit. - * This will cause the TX path to drop offchannel transmissions. - * That would also be done by mac80211, but it is racy, in particular - * in the case that the time event actually completed in the firmware - * (which is handled in iwl_mvm_te_handle_notif). - */ - if (test_and_clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status)) { - queues |= BIT(IWL_MVM_OFFCHANNEL_QUEUE); - iwl_mvm_unref(mvm, IWL_MVM_REF_ROC); - } - if (test_and_clear_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status)) { - queues |= BIT(mvm->aux_queue); - iwl_mvm_unref(mvm, IWL_MVM_REF_ROC_AUX); - } - - synchronize_net(); - - /* - * Flush the offchannel queue -- this is called when the time - * event finishes or is canceled, so that frames queued for it - * won't get stuck on the queue and be transmitted in the next - * time event. - * We have to send the command asynchronously since this cannot - * be under the mutex for locking reasons, but that's not an - * 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, queues, CMD_ASYNC); -} - -static void iwl_mvm_roc_finished(struct iwl_mvm *mvm) -{ - /* - * Of course, our status bit is just as racy as mac80211, so in - * addition, fire off the work struct which will drop all frames - * from the hardware queues that made it through the race. First - * it will of course synchronize the TX path to make sure that - * any *new* TX will be rejected. - */ - schedule_work(&mvm->roc_done_wk); -} - -static void iwl_mvm_csa_noa_start(struct iwl_mvm *mvm) -{ - struct ieee80211_vif *csa_vif; - - rcu_read_lock(); - - csa_vif = rcu_dereference(mvm->csa_vif); - if (!csa_vif || !csa_vif->csa_active) - goto out_unlock; - - IWL_DEBUG_TE(mvm, "CSA NOA started\n"); - - /* - * CSA NoA is started but we still have beacons to - * transmit on the current channel. - * So we just do nothing here and the switch - * will be performed on the last TBTT. - */ - if (!ieee80211_csa_is_complete(csa_vif)) { - IWL_WARN(mvm, "CSA NOA started too early\n"); - goto out_unlock; - } - - ieee80211_csa_finish(csa_vif); - - rcu_read_unlock(); - - RCU_INIT_POINTER(mvm->csa_vif, NULL); - - return; - -out_unlock: - rcu_read_unlock(); -} - -static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - const char *errmsg) -{ - if (vif->type != NL80211_IFTYPE_STATION) - return false; - if (vif->bss_conf.assoc && vif->bss_conf.dtim_period) - return false; - if (errmsg) - IWL_ERR(mvm, "%s\n", errmsg); - - iwl_mvm_connection_loss(mvm, vif, errmsg); - return true; -} - -static void -iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm, - struct iwl_mvm_time_event_data *te_data, - struct iwl_time_event_notif *notif) -{ - struct ieee80211_vif *vif = te_data->vif; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (!notif->status) - IWL_DEBUG_TE(mvm, "CSA time event failed to start\n"); - - switch (te_data->vif->type) { - case NL80211_IFTYPE_AP: - if (!notif->status) - mvmvif->csa_failed = true; - iwl_mvm_csa_noa_start(mvm); - break; - case NL80211_IFTYPE_STATION: - if (!notif->status) { - iwl_mvm_connection_loss(mvm, vif, - "CSA TE failed to start"); - break; - } - iwl_mvm_csa_client_absent(mvm, te_data->vif); - ieee80211_chswitch_done(te_data->vif, true); - break; - default: - /* should never happen */ - WARN_ON_ONCE(1); - break; - } - - /* we don't need it anymore */ - iwl_mvm_te_clear_data(mvm, te_data); -} - -static void iwl_mvm_te_check_trigger(struct iwl_mvm *mvm, - struct iwl_time_event_notif *notif, - struct iwl_mvm_time_event_data *te_data) -{ - struct iwl_fw_dbg_trigger_tlv *trig; - struct iwl_fw_dbg_trigger_time_event *te_trig; - int i; - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TIME_EVENT)) - return; - - trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TIME_EVENT); - te_trig = (void *)trig->data; - - if (!iwl_fw_dbg_trigger_check_stop(mvm, te_data->vif, trig)) - return; - - for (i = 0; i < ARRAY_SIZE(te_trig->time_events); i++) { - u32 trig_te_id = le32_to_cpu(te_trig->time_events[i].id); - u32 trig_action_bitmap = - le32_to_cpu(te_trig->time_events[i].action_bitmap); - u32 trig_status_bitmap = - le32_to_cpu(te_trig->time_events[i].status_bitmap); - - if (trig_te_id != te_data->id || - !(trig_action_bitmap & le32_to_cpu(notif->action)) || - !(trig_status_bitmap & BIT(le32_to_cpu(notif->status)))) - continue; - - iwl_mvm_fw_dbg_collect_trig(mvm, trig, - "Time event %d Action 0x%x received status: %d", - te_data->id, - le32_to_cpu(notif->action), - le32_to_cpu(notif->status)); - break; - } -} - -/* - * Handles a FW notification for an event that is known to the driver. - * - * @mvm: the mvm component - * @te_data: the time event data - * @notif: the notification data corresponding the time event data. - */ -static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm, - struct iwl_mvm_time_event_data *te_data, - struct iwl_time_event_notif *notif) -{ - lockdep_assert_held(&mvm->time_event_lock); - - IWL_DEBUG_TE(mvm, "Handle time event notif - UID = 0x%x action %d\n", - le32_to_cpu(notif->unique_id), - le32_to_cpu(notif->action)); - - iwl_mvm_te_check_trigger(mvm, notif, te_data); - - /* - * The FW sends the start/end time event notifications even for events - * that it fails to schedule. This is indicated in the status field of - * the notification. This happens in cases that the scheduler cannot - * find a schedule that can handle the event (for example requesting a - * P2P Device discoveribility, while there are other higher priority - * events in the system). - */ - if (!le32_to_cpu(notif->status)) { - const char *msg; - - if (notif->action & cpu_to_le32(TE_V2_NOTIF_HOST_EVENT_START)) - msg = "Time Event start notification failure"; - else - msg = "Time Event end notification failure"; - - IWL_DEBUG_TE(mvm, "%s\n", msg); - - if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, msg)) { - iwl_mvm_te_clear_data(mvm, te_data); - return; - } - } - - if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_END) { - IWL_DEBUG_TE(mvm, - "TE ended - current time %lu, estimated end %lu\n", - jiffies, te_data->end_jiffies); - - switch (te_data->vif->type) { - case NL80211_IFTYPE_P2P_DEVICE: - ieee80211_remain_on_channel_expired(mvm->hw); - iwl_mvm_roc_finished(mvm); - break; - case NL80211_IFTYPE_STATION: - /* - * By now, we should have finished association - * and know the dtim period. - */ - iwl_mvm_te_check_disconnect(mvm, te_data->vif, - "No association and the time event is over already..."); - break; - default: - break; - } - - iwl_mvm_te_clear_data(mvm, te_data); - } else if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_START) { - te_data->running = true; - te_data->end_jiffies = TU_TO_EXP_TIME(te_data->duration); - - if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { - set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status); - iwl_mvm_ref(mvm, IWL_MVM_REF_ROC); - ieee80211_ready_on_channel(mvm->hw); - } else if (te_data->id == TE_CHANNEL_SWITCH_PERIOD) { - iwl_mvm_te_handle_notify_csa(mvm, te_data, notif); - } - } else { - IWL_WARN(mvm, "Got TE with unknown action\n"); - } -} - -/* - * Handle A Aux ROC time event - */ -static int iwl_mvm_aux_roc_te_handle_notif(struct iwl_mvm *mvm, - struct iwl_time_event_notif *notif) -{ - struct iwl_mvm_time_event_data *te_data, *tmp; - bool aux_roc_te = false; - - list_for_each_entry_safe(te_data, tmp, &mvm->aux_roc_te_list, list) { - if (le32_to_cpu(notif->unique_id) == te_data->uid) { - aux_roc_te = true; - break; - } - } - if (!aux_roc_te) /* Not a Aux ROC time event */ - return -EINVAL; - - iwl_mvm_te_check_trigger(mvm, notif, te_data); - - if (!le32_to_cpu(notif->status)) { - IWL_DEBUG_TE(mvm, - "ERROR: Aux ROC Time Event %s notification failure\n", - (le32_to_cpu(notif->action) & - TE_V2_NOTIF_HOST_EVENT_START) ? "start" : "end"); - return -EINVAL; - } - - IWL_DEBUG_TE(mvm, - "Aux ROC time event notification - UID = 0x%x action %d\n", - le32_to_cpu(notif->unique_id), - le32_to_cpu(notif->action)); - - if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_END) { - /* End TE, notify mac80211 */ - ieee80211_remain_on_channel_expired(mvm->hw); - iwl_mvm_roc_finished(mvm); /* flush aux queue */ - list_del(&te_data->list); /* remove from list */ - te_data->running = false; - te_data->vif = NULL; - te_data->uid = 0; - te_data->id = TE_MAX; - } else if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_START) { - set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status); - te_data->running = true; - iwl_mvm_ref(mvm, IWL_MVM_REF_ROC_AUX); - ieee80211_ready_on_channel(mvm->hw); /* Start TE */ - } else { - IWL_DEBUG_TE(mvm, - "ERROR: Unknown Aux ROC Time Event (action = %d)\n", - le32_to_cpu(notif->action)); - return -EINVAL; - } - - return 0; -} - -/* - * The Rx handler for time event notifications - */ -void iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_time_event_notif *notif = (void *)pkt->data; - struct iwl_mvm_time_event_data *te_data, *tmp; - - IWL_DEBUG_TE(mvm, "Time event notification - UID = 0x%x action %d\n", - le32_to_cpu(notif->unique_id), - le32_to_cpu(notif->action)); - - spin_lock_bh(&mvm->time_event_lock); - /* This time event is triggered for Aux ROC request */ - if (!iwl_mvm_aux_roc_te_handle_notif(mvm, notif)) - goto unlock; - - list_for_each_entry_safe(te_data, tmp, &mvm->time_event_list, list) { - if (le32_to_cpu(notif->unique_id) == te_data->uid) - iwl_mvm_te_handle_notif(mvm, te_data, notif); - } -unlock: - spin_unlock_bh(&mvm->time_event_lock); -} - -static bool iwl_mvm_te_notif(struct iwl_notif_wait_data *notif_wait, - struct iwl_rx_packet *pkt, void *data) -{ - struct iwl_mvm *mvm = - container_of(notif_wait, struct iwl_mvm, notif_wait); - struct iwl_mvm_time_event_data *te_data = data; - struct iwl_time_event_notif *resp; - int resp_len = iwl_rx_packet_payload_len(pkt); - - if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_NOTIFICATION)) - return true; - - if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { - IWL_ERR(mvm, "Invalid TIME_EVENT_NOTIFICATION response\n"); - return true; - } - - resp = (void *)pkt->data; - - /* te_data->uid is already set in the TIME_EVENT_CMD response */ - if (le32_to_cpu(resp->unique_id) != te_data->uid) - return false; - - IWL_DEBUG_TE(mvm, "TIME_EVENT_NOTIFICATION response - UID = 0x%x\n", - te_data->uid); - if (!resp->status) - IWL_ERR(mvm, - "TIME_EVENT_NOTIFICATION received but not executed\n"); - - return true; -} - -static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait, - struct iwl_rx_packet *pkt, void *data) -{ - struct iwl_mvm *mvm = - container_of(notif_wait, struct iwl_mvm, notif_wait); - struct iwl_mvm_time_event_data *te_data = data; - struct iwl_time_event_resp *resp; - int resp_len = iwl_rx_packet_payload_len(pkt); - - if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_CMD)) - return true; - - if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { - IWL_ERR(mvm, "Invalid TIME_EVENT_CMD response\n"); - return true; - } - - resp = (void *)pkt->data; - - /* we should never get a response to another TIME_EVENT_CMD here */ - if (WARN_ON_ONCE(le32_to_cpu(resp->id) != te_data->id)) - return false; - - te_data->uid = le32_to_cpu(resp->unique_id); - IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n", - te_data->uid); - return true; -} - -static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_mvm_time_event_data *te_data, - struct iwl_time_event_cmd *te_cmd) -{ - static const u16 time_event_response[] = { TIME_EVENT_CMD }; - struct iwl_notification_wait wait_time_event; - int ret; - - lockdep_assert_held(&mvm->mutex); - - IWL_DEBUG_TE(mvm, "Add new TE, duration %d TU\n", - le32_to_cpu(te_cmd->duration)); - - spin_lock_bh(&mvm->time_event_lock); - if (WARN_ON(te_data->id != TE_MAX)) { - spin_unlock_bh(&mvm->time_event_lock); - return -EIO; - } - te_data->vif = vif; - te_data->duration = le32_to_cpu(te_cmd->duration); - te_data->id = le32_to_cpu(te_cmd->id); - list_add_tail(&te_data->list, &mvm->time_event_list); - spin_unlock_bh(&mvm->time_event_lock); - - /* - * Use a notification wait, which really just processes the - * command response and doesn't wait for anything, in order - * to be able to process the response and get the UID inside - * the RX path. Using CMD_WANT_SKB doesn't work because it - * stores the buffer and then wakes up this thread, by which - * time another notification (that the time event started) - * might already be processed unsuccessfully. - */ - iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event, - time_event_response, - ARRAY_SIZE(time_event_response), - iwl_mvm_time_event_response, te_data); - - ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0, - sizeof(*te_cmd), te_cmd); - if (ret) { - IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret); - iwl_remove_notification(&mvm->notif_wait, &wait_time_event); - goto out_clear_te; - } - - /* No need to wait for anything, so just pass 1 (0 isn't valid) */ - ret = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1); - /* should never fail */ - WARN_ON_ONCE(ret); - - if (ret) { - out_clear_te: - spin_lock_bh(&mvm->time_event_lock); - iwl_mvm_te_clear_data(mvm, te_data); - spin_unlock_bh(&mvm->time_event_lock); - } - return ret; -} - -void iwl_mvm_protect_session(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 duration, u32 min_duration, - u32 max_delay, bool wait_for_notif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; - const u16 te_notif_response[] = { TIME_EVENT_NOTIFICATION }; - struct iwl_notification_wait wait_te_notif; - struct iwl_time_event_cmd time_cmd = {}; - - lockdep_assert_held(&mvm->mutex); - - if (te_data->running && - time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) { - IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n", - jiffies_to_msecs(te_data->end_jiffies - jiffies)); - return; - } - - if (te_data->running) { - IWL_DEBUG_TE(mvm, "extend 0x%x: only %u ms left\n", - te_data->uid, - jiffies_to_msecs(te_data->end_jiffies - jiffies)); - /* - * we don't have enough time - * cancel the current TE and issue a new one - * Of course it would be better to remove the old one only - * when the new one is added, but we don't care if we are off - * channel for a bit. All we need to do, is not to return - * before we actually begin to be on the channel. - */ - iwl_mvm_stop_session_protection(mvm, 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(TE_BSS_STA_AGGRESSIVE_ASSOC); - - time_cmd.apply_time = cpu_to_le32(0); - - time_cmd.max_frags = TE_V2_FRAG_NONE; - time_cmd.max_delay = cpu_to_le32(max_delay); - /* TODO: why do we need to interval = bi if it is not periodic? */ - time_cmd.interval = cpu_to_le32(1); - time_cmd.duration = cpu_to_le32(duration); - time_cmd.repeat = 1; - time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START | - TE_V2_NOTIF_HOST_EVENT_END | - T2_V2_START_IMMEDIATELY); - - if (!wait_for_notif) { - iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); - return; - } - - /* - * Create notification_wait for the TIME_EVENT_NOTIFICATION to use - * right after we send the time event - */ - iwl_init_notification_wait(&mvm->notif_wait, &wait_te_notif, - te_notif_response, - ARRAY_SIZE(te_notif_response), - iwl_mvm_te_notif, te_data); - - /* If TE was sent OK - wait for the notification that started */ - if (iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd)) { - IWL_ERR(mvm, "Failed to add TE to protect session\n"); - iwl_remove_notification(&mvm->notif_wait, &wait_te_notif); - } else if (iwl_wait_notification(&mvm->notif_wait, &wait_te_notif, - TU_TO_JIFFIES(max_delay))) { - IWL_ERR(mvm, "Failed to protect session until TE\n"); - } -} - -static bool __iwl_mvm_remove_time_event(struct iwl_mvm *mvm, - struct iwl_mvm_time_event_data *te_data, - u32 *uid) -{ - u32 id; - - /* - * It is possible that by the time we got to this point the time - * event was already removed. - */ - spin_lock_bh(&mvm->time_event_lock); - - /* Save time event uid before clearing its data */ - *uid = te_data->uid; - id = te_data->id; - - /* - * The clear_data function handles time events that were already removed - */ - iwl_mvm_te_clear_data(mvm, te_data); - spin_unlock_bh(&mvm->time_event_lock); - - /* - * It is possible that by the time we try to remove it, the time event - * has already ended and removed. In such a case there is no need to - * send a removal command. - */ - if (id == TE_MAX) { - IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", *uid); - return false; - } - - return true; -} - -/* - * Explicit request to remove a aux roc time event. The removal of a time - * event needs to be synchronized with the flow of a time event's end - * notification, which also removes the time event from the op mode - * data structures. - */ -static void iwl_mvm_remove_aux_roc_te(struct iwl_mvm *mvm, - struct iwl_mvm_vif *mvmvif, - struct iwl_mvm_time_event_data *te_data) -{ - struct iwl_hs20_roc_req aux_cmd = {}; - u32 uid; - int ret; - - if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid)) - return; - - aux_cmd.event_unique_id = cpu_to_le32(uid); - aux_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE); - aux_cmd.id_and_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); - IWL_DEBUG_TE(mvm, "Removing BSS AUX ROC TE 0x%x\n", - le32_to_cpu(aux_cmd.event_unique_id)); - ret = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0, - sizeof(aux_cmd), &aux_cmd); - - if (WARN_ON(ret)) - return; -} - -/* - * Explicit request to remove a time event. The removal of a time event needs to - * be synchronized with the flow of a time event's end notification, which also - * removes the time event from the op mode data structures. - */ -void iwl_mvm_remove_time_event(struct iwl_mvm *mvm, - struct iwl_mvm_vif *mvmvif, - struct iwl_mvm_time_event_data *te_data) -{ - struct iwl_time_event_cmd time_cmd = {}; - u32 uid; - int ret; - - if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid)) - return; - - /* When we remove a TE, the UID is to be set in the id field */ - time_cmd.id = cpu_to_le32(uid); - time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE); - time_cmd.id_and_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); - - IWL_DEBUG_TE(mvm, "Removing TE 0x%x\n", le32_to_cpu(time_cmd.id)); - ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0, - sizeof(time_cmd), &time_cmd); - if (WARN_ON(ret)) - return; -} - -void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; - - lockdep_assert_held(&mvm->mutex); - iwl_mvm_remove_time_event(mvm, mvmvif, te_data); -} - -int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - 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; - struct iwl_time_event_cmd time_cmd = {}; - - lockdep_assert_held(&mvm->mutex); - if (te_data->running) { - IWL_WARN(mvm, "P2P_DEVICE remain on channel already running\n"); - return -EBUSY; - } - - /* - * Flush the done work, just in case it's still pending, so that - * the work it does can complete and we can accept new frames. - */ - flush_work(&mvm->roc_done_wk); - - 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)); - - 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.interval = cpu_to_le32(1); - - /* - * 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 them - * to be fragmented, and in addition allow them to be delayed. - */ - time_cmd.max_frags = min(MSEC_TO_TU(duration)/50, TE_V2_FRAG_ENDLESS); - 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 = 1; - time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START | - TE_V2_NOTIF_HOST_EVENT_END | - T2_V2_START_IMMEDIATELY); - - return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); -} - -void iwl_mvm_stop_roc(struct iwl_mvm *mvm) -{ - struct iwl_mvm_vif *mvmvif = NULL; - struct iwl_mvm_time_event_data *te_data; - bool is_p2p = false; - - lockdep_assert_held(&mvm->mutex); - - spin_lock_bh(&mvm->time_event_lock); - - /* - * Iterate over the list of time events and find the time event that is - * associated with a P2P_DEVICE interface. - * This assumes that a P2P_DEVICE interface can have only a single time - * event at any given time and this time event coresponds to a ROC - * request - */ - list_for_each_entry(te_data, &mvm->time_event_list, list) { - if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { - mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); - is_p2p = true; - goto remove_te; - } - } - - /* There can only be at most one AUX ROC time event, we just use the - * list to simplify/unify code. Remove it if it exists. - */ - te_data = list_first_entry_or_null(&mvm->aux_roc_te_list, - struct iwl_mvm_time_event_data, - list); - if (te_data) - mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); - -remove_te: - spin_unlock_bh(&mvm->time_event_lock); - - if (!mvmvif) { - IWL_WARN(mvm, "No remain on channel event\n"); - return; - } - - if (is_p2p) - iwl_mvm_remove_time_event(mvm, mvmvif, te_data); - else - iwl_mvm_remove_aux_roc_te(mvm, mvmvif, te_data); - - iwl_mvm_roc_finished(mvm); -} - -int iwl_mvm_schedule_csa_period(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 duration, u32 apply_time) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; - struct iwl_time_event_cmd time_cmd = {}; - - lockdep_assert_held(&mvm->mutex); - - if (te_data->running) { - IWL_DEBUG_TE(mvm, "CS period is already scheduled\n"); - return -EBUSY; - } - - 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(TE_CHANNEL_SWITCH_PERIOD); - time_cmd.apply_time = cpu_to_le32(apply_time); - time_cmd.max_frags = TE_V2_FRAG_NONE; - time_cmd.duration = cpu_to_le32(duration); - time_cmd.repeat = 1; - time_cmd.interval = cpu_to_le32(1); - time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START | - TE_V2_ABSENCE); - - 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 deleted file mode 100644 index cbdf8e52a..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.h +++ /dev/null @@ -1,249 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * 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 __time_event_h__ -#define __time_event_h__ - -#include "fw-api.h" - -#include "mvm.h" - -/** - * DOC: Time Events - what is it? - * - * Time Events are a fw feature that allows the driver to control the presence - * of the device on the channel. Since the fw supports multiple channels - * concurrently, the fw may choose to jump to another channel at any time. - * In order to make sure that the fw is on a specific channel at a certain time - * and for a certain duration, the driver needs to issue a time event. - * - * The simplest example is for BSS association. The driver issues a time event, - * waits for it to start, and only then tells mac80211 that we can start the - * association. This way, we make sure that the association will be done - * smoothly and won't be interrupted by channel switch decided within the fw. - */ - - /** - * DOC: The flow against the fw - * - * When the driver needs to make sure we are in a certain channel, at a certain - * time and for a certain duration, it sends a Time Event. The flow against the - * fw goes like this: - * 1) Driver sends a TIME_EVENT_CMD to the fw - * 2) Driver gets the response for that command. This response contains the - * Unique ID (UID) of the event. - * 3) The fw sends notification when the event starts. - * - * Of course the API provides various options that allow to cover parameters - * of the flow. - * What is the duration of the event? - * What is the start time of the event? - * Is there an end-time for the event? - * How much can the event be delayed? - * Can the event be split? - * If yes what is the maximal number of chunks? - * etc... - */ - -/** - * DOC: Abstraction to the driver - * - * In order to simplify the use of time events to the rest of the driver, - * we abstract the use of time events. This component provides the functions - * needed by the driver. - */ - -#define IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS 500 -#define IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS 400 - -/** - * iwl_mvm_protect_session - start / extend the session protection. - * @mvm: the mvm component - * @vif: the virtual interface for which the session is issued - * @duration: the duration of the session in TU. - * @min_duration: will start a new session if the current session will end - * in less than min_duration. - * @max_delay: maximum delay before starting the time event (in TU) - * @wait_for_notif: true if it is required that a time event notification be - * waited for (that the time event has been scheduled before returning) - * - * This function can be used to start a session protection which means that the - * fw will stay on the channel for %duration_ms milliseconds. This function - * can block (sleep) until the session starts. This function can also be used - * to extend a currently running session. - * This function is meant to be used for BSS association for example, where we - * want to make sure that the fw stays on the channel during the association. - */ -void iwl_mvm_protect_session(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 duration, u32 min_duration, - u32 max_delay, bool wait_for_notif); - -/** - * iwl_mvm_stop_session_protection - cancel the session protection. - * @mvm: the mvm component - * @vif: the virtual interface for which the session is issued - * - * This functions cancels the session protection which is an act of good - * citizenship. If it is not needed any more it should be canceled because - * the other bindings wait for the medium during that time. - * This funtions doesn't sleep. - */ -void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm, - struct ieee80211_vif *vif); - -/* - * iwl_mvm_rx_time_event_notif - handles %TIME_EVENT_NOTIFICATION. - */ -void iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); - -/** - * iwl_mvm_start_p2p_roc - start remain on channel for p2p device functionality - * @mvm: the mvm component - * @vif: the virtual interface for which the roc is requested. It is assumed - * 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 - * milliseconds. The function is async, meaning that it only issues the ROC - * request but does not wait for it to start. Once the FW is ready to serve the - * ROC request, it will issue a notification to the driver that it is on the - * requested channel. Once the FW completes the ROC request it will issue - * another notification to the driver. - */ -int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - int duration, enum ieee80211_roc_type type); - -/** - * iwl_mvm_stop_roc - stop remain on channel functionality - * @mvm: the mvm component - * - * This function can be used to cancel an ongoing ROC session. - * The function is async, it will instruct the FW to stop serving the ROC - * session, but will not wait for the actual stopping of the session. - */ -void iwl_mvm_stop_roc(struct iwl_mvm *mvm); - -/** - * iwl_mvm_remove_time_event - general function to clean up of time event - * @mvm: the mvm component - * @vif: the vif to which the time event belongs - * @te_data: the time event data that corresponds to that time event - * - * This function can be used to cancel a time event regardless its type. - * It is useful for cleaning up time events running before removing an - * interface. - */ -void iwl_mvm_remove_time_event(struct iwl_mvm *mvm, - struct iwl_mvm_vif *mvmvif, - struct iwl_mvm_time_event_data *te_data); - -/** - * iwl_mvm_te_clear_data - remove time event from list - * @mvm: the mvm component - * @te_data: the time event data to remove - * - * This function is mostly internal, it is made available here only - * for firmware restart purposes. - */ -void iwl_mvm_te_clear_data(struct iwl_mvm *mvm, - struct iwl_mvm_time_event_data *te_data); - -void iwl_mvm_roc_done_wk(struct work_struct *wk); - -/** - * iwl_mvm_schedule_csa_period - request channel switch absence period - * @mvm: the mvm component - * @vif: the virtual interface for which the channel switch is issued - * @duration: the duration of the NoA in TU. - * @apply_time: NoA start time in GP2. - * - * This function is used to schedule NoA time event and is used to perform - * the channel switch flow. - */ -int iwl_mvm_schedule_csa_period(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 duration, u32 apply_time); - -/** - * iwl_mvm_te_scheduled - check if the fw received the TE cmd - * @te_data: the time event data that corresponds to that time event - * - * This function returns true iff this TE is added to the fw. - */ -static inline bool -iwl_mvm_te_scheduled(struct iwl_mvm_time_event_data *te_data) -{ - if (!te_data) - return false; - - return !!te_data->uid; -} - -#endif /* __time_event_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/tof.c b/drivers/net/wireless/iwlwifi/mvm/tof.c deleted file mode 100644 index 4007f1d42..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/tof.c +++ /dev/null @@ -1,306 +0,0 @@ -/****************************************************************************** - * - * 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) 2015 Intel Deutschland GmbH - * - * 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) 2015 Intel Deutschland GmbH - * 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 "mvm.h" -#include "fw-api-tof.h" - -#define IWL_MVM_TOF_RANGE_REQ_MAX_ID 256 - -void iwl_mvm_tof_init(struct iwl_mvm *mvm) -{ - struct iwl_mvm_tof_data *tof_data = &mvm->tof_data; - - if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT)) - return; - - memset(tof_data, 0, sizeof(*tof_data)); - - tof_data->tof_cfg.sub_grp_cmd_id = cpu_to_le32(TOF_CONFIG_CMD); - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (IWL_MVM_TOF_IS_RESPONDER) { - tof_data->responder_cfg.sub_grp_cmd_id = - cpu_to_le32(TOF_RESPONDER_CONFIG_CMD); - tof_data->responder_cfg.sta_id = IWL_MVM_STATION_COUNT; - } -#endif - - tof_data->range_req.sub_grp_cmd_id = cpu_to_le32(TOF_RANGE_REQ_CMD); - tof_data->range_req.req_timeout = 1; - tof_data->range_req.initiator = 1; - tof_data->range_req.report_policy = 3; - - tof_data->range_req_ext.sub_grp_cmd_id = - cpu_to_le32(TOF_RANGE_REQ_EXT_CMD); - - mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID; -} - -void iwl_mvm_tof_clean(struct iwl_mvm *mvm) -{ - struct iwl_mvm_tof_data *tof_data = &mvm->tof_data; - - if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT)) - return; - - memset(tof_data, 0, sizeof(*tof_data)); - mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID; -} - -static void iwl_tof_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - bool *enabled = _data; - - /* non bss vif exists */ - if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) - *enabled = false; -} - -int iwl_mvm_tof_config_cmd(struct iwl_mvm *mvm) -{ - struct iwl_tof_config_cmd *cmd = &mvm->tof_data.tof_cfg; - bool enabled; - - lockdep_assert_held(&mvm->mutex); - - if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT)) - return -EINVAL; - - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_tof_iterator, &enabled); - if (!enabled) { - IWL_DEBUG_INFO(mvm, "ToF is not supported (non bss vif)\n"); - return -EINVAL; - } - - mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID; - return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD, - IWL_ALWAYS_LONG_GROUP, 0), - 0, sizeof(*cmd), cmd); -} - -int iwl_mvm_tof_range_abort_cmd(struct iwl_mvm *mvm, u8 id) -{ - struct iwl_tof_range_abort_cmd cmd = { - .sub_grp_cmd_id = cpu_to_le32(TOF_RANGE_ABORT_CMD), - .request_id = id, - }; - - lockdep_assert_held(&mvm->mutex); - - if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT)) - return -EINVAL; - - if (id != mvm->tof_data.active_range_request) { - IWL_ERR(mvm, "Invalid range request id %d (active %d)\n", - id, mvm->tof_data.active_range_request); - return -EINVAL; - } - - /* after abort is sent there's no active request anymore */ - mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID; - - return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD, - IWL_ALWAYS_LONG_GROUP, 0), - 0, sizeof(cmd), &cmd); -} - -#ifdef CONFIG_IWLWIFI_DEBUGFS -int iwl_mvm_tof_responder_cmd(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct iwl_tof_responder_config_cmd *cmd = &mvm->tof_data.responder_cfg; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - lockdep_assert_held(&mvm->mutex); - - if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT)) - return -EINVAL; - - if (vif->p2p || vif->type != NL80211_IFTYPE_AP || - !mvmvif->ap_ibss_active) { - IWL_ERR(mvm, "Cannot start responder, not in AP mode\n"); - return -EIO; - } - - cmd->sta_id = mvmvif->bcast_sta.sta_id; - memcpy(cmd->bssid, vif->addr, ETH_ALEN); - return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD, - IWL_ALWAYS_LONG_GROUP, 0), - 0, sizeof(*cmd), cmd); -} -#endif - -int iwl_mvm_tof_range_request_cmd(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct iwl_host_cmd cmd = { - .id = iwl_cmd_id(TOF_CMD, IWL_ALWAYS_LONG_GROUP, 0), - .len = { sizeof(mvm->tof_data.range_req), }, - /* no copy because of the command size */ - .dataflags = { IWL_HCMD_DFL_NOCOPY, }, - }; - - lockdep_assert_held(&mvm->mutex); - - if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT)) - return -EINVAL; - - if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) { - IWL_ERR(mvm, "Cannot send range request, not STA mode\n"); - return -EIO; - } - - /* nesting of range requests is not supported in FW */ - if (mvm->tof_data.active_range_request != - IWL_MVM_TOF_RANGE_REQ_MAX_ID) { - IWL_ERR(mvm, "Cannot send range req, already active req %d\n", - mvm->tof_data.active_range_request); - return -EIO; - } - - mvm->tof_data.active_range_request = mvm->tof_data.range_req.request_id; - - cmd.data[0] = &mvm->tof_data.range_req; - return iwl_mvm_send_cmd(mvm, &cmd); -} - -int iwl_mvm_tof_range_request_ext_cmd(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - lockdep_assert_held(&mvm->mutex); - - if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT)) - return -EINVAL; - - if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) { - IWL_ERR(mvm, "Cannot send ext range req, not in STA mode\n"); - return -EIO; - } - - return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD, - IWL_ALWAYS_LONG_GROUP, 0), - 0, sizeof(mvm->tof_data.range_req_ext), - &mvm->tof_data.range_req_ext); -} - -static int iwl_mvm_tof_range_resp(struct iwl_mvm *mvm, void *data) -{ - struct iwl_tof_range_rsp_ntfy *resp = (void *)data; - - if (resp->request_id != mvm->tof_data.active_range_request) { - IWL_ERR(mvm, "Request id mismatch, got %d, active %d\n", - resp->request_id, mvm->tof_data.active_range_request); - return -EIO; - } - - memcpy(&mvm->tof_data.range_resp, resp, - sizeof(struct iwl_tof_range_rsp_ntfy)); - mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID; - - return 0; -} - -static int iwl_mvm_tof_mcsi_notif(struct iwl_mvm *mvm, void *data) -{ - struct iwl_tof_mcsi_notif *resp = (struct iwl_tof_mcsi_notif *)data; - - IWL_DEBUG_INFO(mvm, "MCSI notification, token %d\n", resp->token); - return 0; -} - -static int iwl_mvm_tof_nb_report_notif(struct iwl_mvm *mvm, void *data) -{ - struct iwl_tof_neighbor_report *report = - (struct iwl_tof_neighbor_report *)data; - - IWL_DEBUG_INFO(mvm, "NB report, bssid %pM, token %d, status 0x%x\n", - report->bssid, report->request_token, report->status); - return 0; -} - -void iwl_mvm_tof_resp_handler(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_tof_gen_resp_cmd *resp = (void *)pkt->data; - - lockdep_assert_held(&mvm->mutex); - - switch (le32_to_cpu(resp->sub_grp_cmd_id)) { - case TOF_RANGE_RESPONSE_NOTIF: - iwl_mvm_tof_range_resp(mvm, resp->data); - break; - case TOF_MCSI_DEBUG_NOTIF: - iwl_mvm_tof_mcsi_notif(mvm, resp->data); - break; - case TOF_NEIGHBOR_REPORT_RSP_NOTIF: - iwl_mvm_tof_nb_report_notif(mvm, resp->data); - break; - default: - IWL_ERR(mvm, "Unknown sub-group command 0x%x\n", - resp->sub_grp_cmd_id); - break; - } -} diff --git a/drivers/net/wireless/iwlwifi/mvm/tof.h b/drivers/net/wireless/iwlwifi/mvm/tof.h deleted file mode 100644 index 9beebc33c..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/tof.h +++ /dev/null @@ -1,94 +0,0 @@ -/****************************************************************************** - * - * 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) 2015 Intel Deutschland GmbH - * - * 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) 2015 Intel Deutschland GmbH - * 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 __tof_h__ -#define __tof_h__ - -#include "fw-api-tof.h" - -struct iwl_mvm_tof_data { - struct iwl_tof_config_cmd tof_cfg; - struct iwl_tof_range_req_cmd range_req; - struct iwl_tof_range_req_ext_cmd range_req_ext; -#ifdef CONFIG_IWLWIFI_DEBUGFS - struct iwl_tof_responder_config_cmd responder_cfg; -#endif - struct iwl_tof_range_rsp_ntfy range_resp; - u8 last_abort_id; - u16 active_range_request; -}; - -void iwl_mvm_tof_init(struct iwl_mvm *mvm); -void iwl_mvm_tof_clean(struct iwl_mvm *mvm); -int iwl_mvm_tof_config_cmd(struct iwl_mvm *mvm); -int iwl_mvm_tof_range_abort_cmd(struct iwl_mvm *mvm, u8 id); -int iwl_mvm_tof_range_request_cmd(struct iwl_mvm *mvm, - struct ieee80211_vif *vif); -void iwl_mvm_tof_resp_handler(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -int iwl_mvm_tof_range_request_ext_cmd(struct iwl_mvm *mvm, - struct ieee80211_vif *vif); -#ifdef CONFIG_IWLWIFI_DEBUGFS -int iwl_mvm_tof_responder_cmd(struct iwl_mvm *mvm, - struct ieee80211_vif *vif); -#endif -#endif /* __tof_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/tt.c b/drivers/net/wireless/iwlwifi/mvm/tt.c deleted file mode 100644 index cadfc0460..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/tt.c +++ /dev/null @@ -1,460 +0,0 @@ -/****************************************************************************** - * - * 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 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH - * 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 "mvm.h" - -#define IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT HZ - -static void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm) -{ - struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; - u32 duration = tt->params.ct_kill_duration; - - if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) - return; - - IWL_ERR(mvm, "Enter CT Kill\n"); - iwl_mvm_set_hw_ctkill_state(mvm, true); - - tt->throttle = false; - tt->dynamic_smps = false; - - /* Don't schedule an exit work if we're in test mode, since - * the temperature will not change unless we manually set it - * again (or disable testing). - */ - if (!mvm->temperature_test) - schedule_delayed_work(&tt->ct_kill_exit, - round_jiffies_relative(duration * HZ)); -} - -static void iwl_mvm_exit_ctkill(struct iwl_mvm *mvm) -{ - if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) - return; - - IWL_ERR(mvm, "Exit CT Kill\n"); - iwl_mvm_set_hw_ctkill_state(mvm, false); -} - -void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp) -{ - /* ignore the notification if we are in test mode */ - if (mvm->temperature_test) - return; - - if (mvm->temperature == temp) - return; - - mvm->temperature = temp; - iwl_mvm_tt_handler(mvm); -} - -static int iwl_mvm_temp_notif_parse(struct iwl_mvm *mvm, - struct iwl_rx_packet *pkt) -{ - struct iwl_dts_measurement_notif *notif; - int len = iwl_rx_packet_payload_len(pkt); - int temp; - - if (WARN_ON_ONCE(len != sizeof(*notif))) { - IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n"); - return -EINVAL; - } - - notif = (void *)pkt->data; - - temp = le32_to_cpu(notif->temp); - - /* shouldn't be negative, but since it's s32, make sure it isn't */ - if (WARN_ON_ONCE(temp < 0)) - temp = 0; - - IWL_DEBUG_TEMP(mvm, "DTS_MEASUREMENT_NOTIFICATION - %d\n", temp); - - return temp; -} - -static bool iwl_mvm_temp_notif_wait(struct iwl_notif_wait_data *notif_wait, - struct iwl_rx_packet *pkt, void *data) -{ - struct iwl_mvm *mvm = - container_of(notif_wait, struct iwl_mvm, notif_wait); - int *temp = data; - int ret; - - ret = iwl_mvm_temp_notif_parse(mvm, pkt); - if (ret < 0) - return true; - - *temp = ret; - - return true; -} - -void iwl_mvm_temp_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - int temp; - - /* the notification is handled synchronously in ctkill, so skip here */ - if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) - return; - - temp = iwl_mvm_temp_notif_parse(mvm, pkt); - if (temp < 0) - return; - - iwl_mvm_tt_temp_changed(mvm, temp); -} - -static int iwl_mvm_get_temp_cmd(struct iwl_mvm *mvm) -{ - struct iwl_dts_measurement_cmd cmd = { - .flags = cpu_to_le32(DTS_TRIGGER_CMD_FLAGS_TEMP), - }; - struct iwl_ext_dts_measurement_cmd extcmd = { - .control_mode = cpu_to_le32(DTS_AUTOMATIC), - }; - u32 cmdid; - - if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_WIDE_CMD_HDR)) - cmdid = iwl_cmd_id(CMD_DTS_MEASUREMENT_TRIGGER_WIDE, - PHY_OPS_GROUP, 0); - else - cmdid = CMD_DTS_MEASUREMENT_TRIGGER; - - if (!fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE)) - return iwl_mvm_send_cmd_pdu(mvm, cmdid, 0, sizeof(cmd), &cmd); - - return iwl_mvm_send_cmd_pdu(mvm, cmdid, 0, sizeof(extcmd), &extcmd); -} - -int iwl_mvm_get_temp(struct iwl_mvm *mvm) -{ - struct iwl_notification_wait wait_temp_notif; - static u16 temp_notif[] = { WIDE_ID(PHY_OPS_GROUP, - DTS_MEASUREMENT_NOTIF_WIDE) }; - int ret, temp; - - if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_WIDE_CMD_HDR)) - temp_notif[0] = DTS_MEASUREMENT_NOTIFICATION; - - lockdep_assert_held(&mvm->mutex); - - iwl_init_notification_wait(&mvm->notif_wait, &wait_temp_notif, - temp_notif, ARRAY_SIZE(temp_notif), - iwl_mvm_temp_notif_wait, &temp); - - ret = iwl_mvm_get_temp_cmd(mvm); - if (ret) { - IWL_ERR(mvm, "Failed to get the temperature (err=%d)\n", ret); - iwl_remove_notification(&mvm->notif_wait, &wait_temp_notif); - return ret; - } - - ret = iwl_wait_notification(&mvm->notif_wait, &wait_temp_notif, - IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT); - if (ret) { - IWL_ERR(mvm, "Getting the temperature timed out\n"); - return ret; - } - - return temp; -} - -static void check_exit_ctkill(struct work_struct *work) -{ - struct iwl_mvm_tt_mgmt *tt; - struct iwl_mvm *mvm; - u32 duration; - s32 temp; - - tt = container_of(work, struct iwl_mvm_tt_mgmt, ct_kill_exit.work); - mvm = container_of(tt, struct iwl_mvm, thermal_throttle); - - duration = tt->params.ct_kill_duration; - - mutex_lock(&mvm->mutex); - - if (__iwl_mvm_mac_start(mvm)) - goto reschedule; - - /* make sure the device is available for direct read/writes */ - if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_CHECK_CTKILL)) { - __iwl_mvm_mac_stop(mvm); - goto reschedule; - } - - temp = iwl_mvm_get_temp(mvm); - - iwl_mvm_unref(mvm, IWL_MVM_REF_CHECK_CTKILL); - - __iwl_mvm_mac_stop(mvm); - - if (temp < 0) - goto reschedule; - - IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", temp); - - if (temp <= tt->params.ct_kill_exit) { - mutex_unlock(&mvm->mutex); - iwl_mvm_exit_ctkill(mvm); - return; - } - -reschedule: - mutex_unlock(&mvm->mutex); - schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit, - round_jiffies(duration * HZ)); -} - -static void iwl_mvm_tt_smps_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = _data; - enum ieee80211_smps_mode smps_mode; - - lockdep_assert_held(&mvm->mutex); - - if (mvm->thermal_throttle.dynamic_smps) - smps_mode = IEEE80211_SMPS_DYNAMIC; - else - smps_mode = IEEE80211_SMPS_AUTOMATIC; - - if (vif->type != NL80211_IFTYPE_STATION) - return; - - iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, smps_mode); -} - -static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable) -{ - struct ieee80211_sta *sta; - struct iwl_mvm_sta *mvmsta; - int i, err; - - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], - lockdep_is_held(&mvm->mutex)); - if (IS_ERR_OR_NULL(sta)) - continue; - mvmsta = iwl_mvm_sta_from_mac80211(sta); - if (enable == mvmsta->tt_tx_protection) - continue; - err = iwl_mvm_tx_protection(mvm, mvmsta, enable); - if (err) { - IWL_ERR(mvm, "Failed to %s Tx protection\n", - enable ? "enable" : "disable"); - } else { - IWL_DEBUG_TEMP(mvm, "%s Tx protection\n", - enable ? "Enable" : "Disable"); - mvmsta->tt_tx_protection = enable; - } - } -} - -void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff) -{ - struct iwl_host_cmd cmd = { - .id = REPLY_THERMAL_MNG_BACKOFF, - .len = { sizeof(u32), }, - .data = { &backoff, }, - }; - - backoff = max(backoff, mvm->thermal_throttle.min_backoff); - - if (iwl_mvm_send_cmd(mvm, &cmd) == 0) { - IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n", - backoff); - mvm->thermal_throttle.tx_backoff = backoff; - } else { - IWL_ERR(mvm, "Failed to change Thermal Tx backoff\n"); - } -} - -void iwl_mvm_tt_handler(struct iwl_mvm *mvm) -{ - struct iwl_tt_params *params = &mvm->thermal_throttle.params; - struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; - s32 temperature = mvm->temperature; - bool throttle_enable = false; - int i; - u32 tx_backoff; - - IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", mvm->temperature); - - if (params->support_ct_kill && temperature >= params->ct_kill_entry) { - iwl_mvm_enter_ctkill(mvm); - return; - } - - if (params->support_ct_kill && - temperature <= params->ct_kill_exit) { - iwl_mvm_exit_ctkill(mvm); - return; - } - - if (params->support_dynamic_smps) { - if (!tt->dynamic_smps && - temperature >= params->dynamic_smps_entry) { - IWL_DEBUG_TEMP(mvm, "Enable dynamic SMPS\n"); - tt->dynamic_smps = true; - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_tt_smps_iterator, mvm); - throttle_enable = true; - } else if (tt->dynamic_smps && - temperature <= params->dynamic_smps_exit) { - IWL_DEBUG_TEMP(mvm, "Disable dynamic SMPS\n"); - tt->dynamic_smps = false; - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_tt_smps_iterator, mvm); - } - } - - if (params->support_tx_protection) { - if (temperature >= params->tx_protection_entry) { - iwl_mvm_tt_tx_protection(mvm, true); - throttle_enable = true; - } else if (temperature <= params->tx_protection_exit) { - iwl_mvm_tt_tx_protection(mvm, false); - } - } - - if (params->support_tx_backoff) { - tx_backoff = tt->min_backoff; - for (i = 0; i < TT_TX_BACKOFF_SIZE; i++) { - if (temperature < params->tx_backoff[i].temperature) - break; - tx_backoff = max(tt->min_backoff, - params->tx_backoff[i].backoff); - } - if (tx_backoff != tt->min_backoff) - throttle_enable = true; - if (tt->tx_backoff != tx_backoff) - iwl_mvm_tt_tx_backoff(mvm, tx_backoff); - } - - if (!tt->throttle && throttle_enable) { - IWL_WARN(mvm, - "Due to high temperature thermal throttling initiated\n"); - tt->throttle = true; - } else if (tt->throttle && !tt->dynamic_smps && - tt->tx_backoff == tt->min_backoff && - temperature <= params->tx_protection_exit) { - IWL_WARN(mvm, - "Temperature is back to normal thermal throttling stopped\n"); - tt->throttle = false; - } -} - -static const struct iwl_tt_params iwl_mvm_default_tt_params = { - .ct_kill_entry = 118, - .ct_kill_exit = 96, - .ct_kill_duration = 5, - .dynamic_smps_entry = 114, - .dynamic_smps_exit = 110, - .tx_protection_entry = 114, - .tx_protection_exit = 108, - .tx_backoff = { - {.temperature = 112, .backoff = 200}, - {.temperature = 113, .backoff = 600}, - {.temperature = 114, .backoff = 1200}, - {.temperature = 115, .backoff = 2000}, - {.temperature = 116, .backoff = 4000}, - {.temperature = 117, .backoff = 10000}, - }, - .support_ct_kill = true, - .support_dynamic_smps = true, - .support_tx_protection = true, - .support_tx_backoff = true, -}; - -void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff) -{ - struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; - - IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n"); - - if (mvm->cfg->thermal_params) - tt->params = *mvm->cfg->thermal_params; - else - tt->params = iwl_mvm_default_tt_params; - - tt->throttle = false; - tt->dynamic_smps = false; - tt->min_backoff = min_backoff; - INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill); -} - -void iwl_mvm_tt_exit(struct iwl_mvm *mvm) -{ - cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit); - IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n"); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c deleted file mode 100644 index c652a66be..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ /dev/null @@ -1,1115 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * 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 <linux/ieee80211.h> -#include <linux/etherdevice.h> - -#include "iwl-trans.h" -#include "iwl-eeprom-parse.h" -#include "mvm.h" -#include "sta.h" - -static void -iwl_mvm_bar_check_trigger(struct iwl_mvm *mvm, const u8 *addr, - u16 tid, u16 ssn) -{ - struct iwl_fw_dbg_trigger_tlv *trig; - struct iwl_fw_dbg_trigger_ba *ba_trig; - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA)) - return; - - trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA); - ba_trig = (void *)trig->data; - - if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig)) - return; - - if (!(le16_to_cpu(ba_trig->tx_bar) & BIT(tid))) - return; - - iwl_mvm_fw_dbg_collect_trig(mvm, trig, - "BAR sent to %pM, tid %d, ssn %d", - addr, tid, ssn); -} - -/* - * Sets most of the Tx cmd's fields - */ -void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, - struct iwl_tx_cmd *tx_cmd, - struct ieee80211_tx_info *info, u8 sta_id) -{ - struct ieee80211_hdr *hdr = (void *)skb->data; - __le16 fc = hdr->frame_control; - u32 tx_flags = le32_to_cpu(tx_cmd->tx_flags); - u32 len = skb->len + FCS_LEN; - u8 ac; - - if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) - tx_flags |= TX_CMD_FLG_ACK; - else - tx_flags &= ~TX_CMD_FLG_ACK; - - if (ieee80211_is_probe_resp(fc)) - tx_flags |= TX_CMD_FLG_TSF; - - if (ieee80211_has_morefrags(fc)) - tx_flags |= TX_CMD_FLG_MORE_FRAG; - - if (ieee80211_is_data_qos(fc)) { - u8 *qc = ieee80211_get_qos_ctl(hdr); - tx_cmd->tid_tspec = qc[0] & 0xf; - tx_flags &= ~TX_CMD_FLG_SEQ_CTL; - } else if (ieee80211_is_back_req(fc)) { - struct ieee80211_bar *bar = (void *)skb->data; - u16 control = le16_to_cpu(bar->control); - u16 ssn = le16_to_cpu(bar->start_seq_num); - - tx_flags |= TX_CMD_FLG_ACK | TX_CMD_FLG_BAR; - tx_cmd->tid_tspec = (control & - IEEE80211_BAR_CTRL_TID_INFO_MASK) >> - IEEE80211_BAR_CTRL_TID_INFO_SHIFT; - WARN_ON_ONCE(tx_cmd->tid_tspec >= IWL_MAX_TID_COUNT); - iwl_mvm_bar_check_trigger(mvm, bar->ra, tx_cmd->tid_tspec, - ssn); - } else { - tx_cmd->tid_tspec = IWL_TID_NON_QOS; - if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) - tx_flags |= TX_CMD_FLG_SEQ_CTL; - else - tx_flags &= ~TX_CMD_FLG_SEQ_CTL; - } - - /* Default to 0 (BE) when tid_spec is set to IWL_TID_NON_QOS */ - if (tx_cmd->tid_tspec < IWL_MAX_TID_COUNT) - ac = tid_to_mac80211_ac[tx_cmd->tid_tspec]; - else - ac = tid_to_mac80211_ac[0]; - - tx_flags |= iwl_mvm_bt_coex_tx_prio(mvm, hdr, info, ac) << - TX_CMD_FLG_BT_PRIO_POS; - - if (ieee80211_is_mgmt(fc)) { - if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) - tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_ASSOC); - else if (ieee80211_is_action(fc)) - tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_NONE); - else - tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_MGMT); - - /* The spec allows Action frames in A-MPDU, we don't support - * it - */ - WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU); - } else if (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO) { - tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_MGMT); - } else { - tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_NONE); - } - - if (ieee80211_is_data(fc) && len > mvm->rts_threshold && - !is_multicast_ether_addr(ieee80211_get_DA(hdr))) - tx_flags |= TX_CMD_FLG_PROT_REQUIRE; - - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT) && - ieee80211_action_contains_tpc(skb)) - tx_flags |= TX_CMD_FLG_WRITE_TX_POWER; - - tx_cmd->tx_flags = cpu_to_le32(tx_flags); - /* Total # bytes to be transmitted */ - tx_cmd->len = cpu_to_le16((u16)skb->len); - tx_cmd->next_frame_len = 0; - tx_cmd->life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE); - tx_cmd->sta_id = sta_id; -} - -/* - * Sets the fields in the Tx cmd that are rate related - */ -void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd, - struct ieee80211_tx_info *info, - struct ieee80211_sta *sta, __le16 fc) -{ - u32 rate_flags; - int rate_idx; - u8 rate_plcp; - - /* Set retry limit on RTS packets */ - tx_cmd->rts_retry_limit = IWL_RTS_DFAULT_RETRY_LIMIT; - - /* Set retry limit on DATA packets and Probe Responses*/ - if (ieee80211_is_probe_resp(fc)) { - tx_cmd->data_retry_limit = IWL_MGMT_DFAULT_RETRY_LIMIT; - tx_cmd->rts_retry_limit = - min(tx_cmd->data_retry_limit, tx_cmd->rts_retry_limit); - } else if (ieee80211_is_back_req(fc)) { - tx_cmd->data_retry_limit = IWL_BAR_DFAULT_RETRY_LIMIT; - } else { - tx_cmd->data_retry_limit = IWL_DEFAULT_TX_RETRY; - } - - /* - * for data packets, rate info comes from the table inside the fw. This - * table is controlled by LINK_QUALITY commands - */ - - if (ieee80211_is_data(fc) && sta) { - tx_cmd->initial_rate_index = 0; - tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_STA_RATE); - return; - } else if (ieee80211_is_back_req(fc)) { - tx_cmd->tx_flags |= - cpu_to_le32(TX_CMD_FLG_ACK | TX_CMD_FLG_BAR); - } - - /* HT rate doesn't make sense for a non data frame */ - WARN_ONCE(info->control.rates[0].flags & IEEE80211_TX_RC_MCS, - "Got an HT rate (flags:0x%x/mcs:%d) for a non data frame (fc:0x%x)\n", - info->control.rates[0].flags, - info->control.rates[0].idx, - le16_to_cpu(fc)); - - rate_idx = info->control.rates[0].idx; - /* if the rate isn't a well known legacy rate, take the lowest one */ - if (rate_idx < 0 || rate_idx > IWL_RATE_COUNT_LEGACY) - rate_idx = rate_lowest_index( - &mvm->nvm_data->bands[info->band], sta); - - /* For 5 GHZ band, remap mac80211 rate indices into driver indices */ - if (info->band == IEEE80211_BAND_5GHZ) - rate_idx += IWL_FIRST_OFDM_RATE; - - /* For 2.4 GHZ band, check that there is no need to remap */ - BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0); - - /* Get PLCP rate for tx_cmd->rate_n_flags */ - rate_plcp = iwl_mvm_mac80211_idx_to_hwrate(rate_idx); - - mvm->mgmt_last_antenna_idx = - iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm), - mvm->mgmt_last_antenna_idx); - - if (info->band == IEEE80211_BAND_2GHZ && - !iwl_mvm_bt_coex_is_shared_ant_avail(mvm)) - rate_flags = mvm->cfg->non_shared_ant << RATE_MCS_ANT_POS; - else - rate_flags = - BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS; - - /* Set CCK flag as needed */ - if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE)) - rate_flags |= RATE_MCS_CCK_MSK; - - /* Set the rate in the TX cmd */ - tx_cmd->rate_n_flags = cpu_to_le32((u32)rate_plcp | rate_flags); -} - -/* - * Sets the fields in the Tx cmd that are crypto related - */ -static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm, - struct ieee80211_tx_info *info, - struct iwl_tx_cmd *tx_cmd, - struct sk_buff *skb_frag, - int hdrlen) -{ - struct ieee80211_key_conf *keyconf = info->control.hw_key; - u8 *crypto_hdr = skb_frag->data + hdrlen; - u64 pn; - - switch (keyconf->cipher) { - case WLAN_CIPHER_SUITE_CCMP: - case WLAN_CIPHER_SUITE_CCMP_256: - iwl_mvm_set_tx_cmd_ccmp(info, tx_cmd); - pn = atomic64_inc_return(&keyconf->tx_pn); - crypto_hdr[0] = pn; - crypto_hdr[2] = 0; - crypto_hdr[3] = 0x20 | (keyconf->keyidx << 6); - crypto_hdr[1] = pn >> 8; - crypto_hdr[4] = pn >> 16; - crypto_hdr[5] = pn >> 24; - crypto_hdr[6] = pn >> 32; - crypto_hdr[7] = pn >> 40; - break; - - case WLAN_CIPHER_SUITE_TKIP: - tx_cmd->sec_ctl = TX_CMD_SEC_TKIP; - ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key); - break; - - case WLAN_CIPHER_SUITE_WEP104: - tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; - /* fall through */ - case WLAN_CIPHER_SUITE_WEP40: - tx_cmd->sec_ctl |= TX_CMD_SEC_WEP | - ((keyconf->keyidx << TX_CMD_SEC_WEP_KEY_IDX_POS) & - TX_CMD_SEC_WEP_KEY_IDX_MSK); - - memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen); - break; - default: - tx_cmd->sec_ctl |= TX_CMD_SEC_EXT; - } -} - -/* - * Allocates and sets the Tx cmd the driver data pointers in the skb - */ -static struct iwl_device_cmd * -iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb, - int hdrlen, struct ieee80211_sta *sta, u8 sta_id) -{ - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct iwl_device_cmd *dev_cmd; - struct iwl_tx_cmd *tx_cmd; - - dev_cmd = iwl_trans_alloc_tx_cmd(mvm->trans); - - if (unlikely(!dev_cmd)) - return NULL; - - memset(dev_cmd, 0, sizeof(*dev_cmd)); - dev_cmd->hdr.cmd = TX_CMD; - tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; - - if (info->control.hw_key) - iwl_mvm_set_tx_cmd_crypto(mvm, info, tx_cmd, skb, hdrlen); - - iwl_mvm_set_tx_cmd(mvm, skb, tx_cmd, info, sta_id); - - iwl_mvm_set_tx_cmd_rate(mvm, tx_cmd, info, sta, hdr->frame_control); - - memset(&info->status, 0, sizeof(info->status)); - - info->driver_data[0] = NULL; - info->driver_data[1] = dev_cmd; - - return dev_cmd; -} - -int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) -{ - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct iwl_device_cmd *dev_cmd; - struct iwl_tx_cmd *tx_cmd; - u8 sta_id; - int hdrlen = ieee80211_hdrlen(hdr->frame_control); - - if (WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU)) - return -1; - - if (WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM && - (!info->control.vif || - info->hw_queue != info->control.vif->cab_queue))) - return -1; - - /* - * IWL_MVM_OFFCHANNEL_QUEUE is used for ROC packets that can be used - * in 2 different types of vifs, P2P & STATION. P2P uses the offchannel - * queue. STATION (HS2.0) uses the auxiliary context of the FW, - * and hence needs to be sent on the aux queue - */ - if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE && - info->control.vif->type == NL80211_IFTYPE_STATION) - IEEE80211_SKB_CB(skb)->hw_queue = mvm->aux_queue; - - /* - * If the interface on which the frame is sent is the P2P_DEVICE - * or an AP/GO interface use the broadcast station associated - * with it; otherwise if the interface is a managed interface - * use the AP station associated with it for multicast traffic - * (this is not possible for unicast packets as a TLDS discovery - * response are sent without a station entry); otherwise use the - * AUX station. - */ - sta_id = mvm->aux_sta.sta_id; - if (info->control.vif) { - struct iwl_mvm_vif *mvmvif = - iwl_mvm_vif_from_mac80211(info->control.vif); - - if (info->control.vif->type == NL80211_IFTYPE_P2P_DEVICE || - info->control.vif->type == NL80211_IFTYPE_AP) - sta_id = mvmvif->bcast_sta.sta_id; - else if (info->control.vif->type == NL80211_IFTYPE_STATION && - is_multicast_ether_addr(hdr->addr1)) { - u8 ap_sta_id = ACCESS_ONCE(mvmvif->ap_sta_id); - - if (ap_sta_id != IWL_MVM_STATION_COUNT) - sta_id = ap_sta_id; - } - } - - IWL_DEBUG_TX(mvm, "station Id %d, queue=%d\n", sta_id, info->hw_queue); - - dev_cmd = iwl_mvm_set_tx_params(mvm, skb, hdrlen, NULL, sta_id); - if (!dev_cmd) - return -1; - - /* From now on, we cannot access info->control */ - tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; - - /* Copy MAC header from skb into command buffer */ - memcpy(tx_cmd->hdr, hdr, hdrlen); - - if (iwl_trans_tx(mvm->trans, skb, dev_cmd, info->hw_queue)) { - iwl_trans_free_tx_cmd(mvm->trans, dev_cmd); - return -1; - } - - return 0; -} - -/* - * Sets the fields in the Tx cmd that are crypto related - */ -int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, - struct ieee80211_sta *sta) -{ - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct iwl_mvm_sta *mvmsta; - struct iwl_device_cmd *dev_cmd; - struct iwl_tx_cmd *tx_cmd; - __le16 fc; - u16 seq_number = 0; - u8 tid = IWL_MAX_TID_COUNT; - u8 txq_id = info->hw_queue; - bool is_data_qos = false, is_ampdu = false; - int hdrlen; - - mvmsta = iwl_mvm_sta_from_mac80211(sta); - fc = hdr->frame_control; - hdrlen = ieee80211_hdrlen(fc); - - if (WARN_ON_ONCE(!mvmsta)) - return -1; - - if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_STATION_COUNT)) - return -1; - - dev_cmd = iwl_mvm_set_tx_params(mvm, skb, hdrlen, sta, mvmsta->sta_id); - if (!dev_cmd) - goto drop; - - tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; - /* From now on, we cannot access info->control */ - - /* - * we handle that entirely ourselves -- for uAPSD the firmware - * will always send a notification, and for PS-Poll responses - * we'll notify mac80211 when getting frame status - */ - info->flags &= ~IEEE80211_TX_STATUS_EOSP; - - spin_lock(&mvmsta->lock); - - if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) { - u8 *qc = NULL; - qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; - if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) - goto drop_unlock_sta; - - seq_number = mvmsta->tid_data[tid].seq_number; - seq_number &= IEEE80211_SCTL_SEQ; - hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); - hdr->seq_ctrl |= cpu_to_le16(seq_number); - is_data_qos = true; - is_ampdu = info->flags & IEEE80211_TX_CTL_AMPDU; - } - - /* Copy MAC header from skb into command buffer */ - memcpy(tx_cmd->hdr, hdr, hdrlen); - - WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM); - - if (sta->tdls) { - /* default to TID 0 for non-QoS packets */ - u8 tdls_tid = tid == IWL_MAX_TID_COUNT ? 0 : tid; - - txq_id = mvmsta->hw_queue[tid_to_mac80211_ac[tdls_tid]]; - } - - if (is_ampdu) { - if (WARN_ON_ONCE(mvmsta->tid_data[tid].state != IWL_AGG_ON)) - goto drop_unlock_sta; - txq_id = mvmsta->tid_data[tid].txq_id; - } - - IWL_DEBUG_TX(mvm, "TX to [%d|%d] Q:%d - seq: 0x%x\n", mvmsta->sta_id, - tid, txq_id, IEEE80211_SEQ_TO_SN(seq_number)); - - if (iwl_trans_tx(mvm->trans, skb, dev_cmd, txq_id)) - goto drop_unlock_sta; - - if (is_data_qos && !ieee80211_has_morefrags(fc)) - mvmsta->tid_data[tid].seq_number = seq_number + 0x10; - - spin_unlock(&mvmsta->lock); - - if (txq_id < mvm->first_agg_queue) - atomic_inc(&mvm->pending_frames[mvmsta->sta_id]); - - return 0; - -drop_unlock_sta: - iwl_trans_free_tx_cmd(mvm->trans, dev_cmd); - spin_unlock(&mvmsta->lock); -drop: - return -1; -} - -static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, u8 tid) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; - struct ieee80211_vif *vif = mvmsta->vif; - - lockdep_assert_held(&mvmsta->lock); - - if ((tid_data->state == IWL_AGG_ON || - tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA) && - iwl_mvm_tid_queued(tid_data) == 0) { - /* - * Now that this aggregation queue is empty tell mac80211 so it - * knows we no longer have frames buffered for the station on - * this TID (for the TIM bitmap calculation.) - */ - ieee80211_sta_set_buffered(sta, tid, false); - } - - if (tid_data->ssn != tid_data->next_reclaimed) - return; - - switch (tid_data->state) { - case IWL_EMPTYING_HW_QUEUE_ADDBA: - IWL_DEBUG_TX_QUEUES(mvm, - "Can continue addBA flow ssn = next_recl = %d\n", - tid_data->next_reclaimed); - tid_data->state = IWL_AGG_STARTING; - ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); - break; - - case IWL_EMPTYING_HW_QUEUE_DELBA: - IWL_DEBUG_TX_QUEUES(mvm, - "Can continue DELBA flow ssn = next_recl = %d\n", - tid_data->next_reclaimed); - iwl_mvm_disable_txq(mvm, tid_data->txq_id, - vif->hw_queue[tid_to_mac80211_ac[tid]], tid, - CMD_ASYNC); - tid_data->state = IWL_AGG_OFF; - ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); - break; - - default: - break; - } -} - -#ifdef CONFIG_IWLWIFI_DEBUG -const char *iwl_mvm_get_tx_fail_reason(u32 status) -{ -#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x -#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x - - switch (status & TX_STATUS_MSK) { - case TX_STATUS_SUCCESS: - return "SUCCESS"; - TX_STATUS_POSTPONE(DELAY); - TX_STATUS_POSTPONE(FEW_BYTES); - TX_STATUS_POSTPONE(BT_PRIO); - TX_STATUS_POSTPONE(QUIET_PERIOD); - TX_STATUS_POSTPONE(CALC_TTAK); - TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY); - TX_STATUS_FAIL(SHORT_LIMIT); - TX_STATUS_FAIL(LONG_LIMIT); - TX_STATUS_FAIL(UNDERRUN); - TX_STATUS_FAIL(DRAIN_FLOW); - TX_STATUS_FAIL(RFKILL_FLUSH); - TX_STATUS_FAIL(LIFE_EXPIRE); - TX_STATUS_FAIL(DEST_PS); - TX_STATUS_FAIL(HOST_ABORTED); - TX_STATUS_FAIL(BT_RETRY); - TX_STATUS_FAIL(STA_INVALID); - TX_STATUS_FAIL(FRAG_DROPPED); - TX_STATUS_FAIL(TID_DISABLE); - TX_STATUS_FAIL(FIFO_FLUSHED); - TX_STATUS_FAIL(SMALL_CF_POLL); - TX_STATUS_FAIL(FW_DROP); - TX_STATUS_FAIL(STA_COLOR_MISMATCH); - } - - return "UNKNOWN"; - -#undef TX_STATUS_FAIL -#undef TX_STATUS_POSTPONE -} -#endif /* CONFIG_IWLWIFI_DEBUG */ - -void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags, - enum ieee80211_band band, - struct ieee80211_tx_rate *r) -{ - if (rate_n_flags & RATE_HT_MCS_GF_MSK) - r->flags |= IEEE80211_TX_RC_GREEN_FIELD; - switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { - case RATE_MCS_CHAN_WIDTH_20: - break; - case RATE_MCS_CHAN_WIDTH_40: - r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; - break; - case RATE_MCS_CHAN_WIDTH_80: - r->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH; - break; - case RATE_MCS_CHAN_WIDTH_160: - r->flags |= IEEE80211_TX_RC_160_MHZ_WIDTH; - break; - } - if (rate_n_flags & RATE_MCS_SGI_MSK) - r->flags |= IEEE80211_TX_RC_SHORT_GI; - if (rate_n_flags & RATE_MCS_HT_MSK) { - r->flags |= IEEE80211_TX_RC_MCS; - r->idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK; - } else if (rate_n_flags & RATE_MCS_VHT_MSK) { - ieee80211_rate_set_vht( - r, rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK, - ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> - RATE_VHT_MCS_NSS_POS) + 1); - r->flags |= IEEE80211_TX_RC_VHT_MCS; - } else { - r->idx = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags, - band); - } -} - -/** - * translate ucode response to mac80211 tx status control values - */ -static void iwl_mvm_hwrate_to_tx_status(u32 rate_n_flags, - struct ieee80211_tx_info *info) -{ - struct ieee80211_tx_rate *r = &info->status.rates[0]; - - info->status.antenna = - ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); - iwl_mvm_hwrate_to_tx_rate(rate_n_flags, info->band, r); -} - -static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, - struct iwl_rx_packet *pkt) -{ - struct ieee80211_sta *sta; - u16 sequence = le16_to_cpu(pkt->hdr.sequence); - int txq_id = SEQ_TO_QUEUE(sequence); - struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data; - int sta_id = IWL_MVM_TX_RES_GET_RA(tx_resp->ra_tid); - int tid = IWL_MVM_TX_RES_GET_TID(tx_resp->ra_tid); - u32 status = le16_to_cpu(tx_resp->status.status); - u16 ssn = iwl_mvm_get_scd_ssn(tx_resp); - struct iwl_mvm_sta *mvmsta; - struct sk_buff_head skbs; - u8 skb_freed = 0; - u16 next_reclaimed, seq_ctl; - - __skb_queue_head_init(&skbs); - - seq_ctl = le16_to_cpu(tx_resp->seq_ctl); - - /* we can free until ssn % q.n_bd not inclusive */ - iwl_trans_reclaim(mvm->trans, txq_id, ssn, &skbs); - - while (!skb_queue_empty(&skbs)) { - struct sk_buff *skb = __skb_dequeue(&skbs); - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - - skb_freed++; - - iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]); - - memset(&info->status, 0, sizeof(info->status)); - - info->flags &= ~IEEE80211_TX_CTL_AMPDU; - - /* inform mac80211 about what happened with the frame */ - switch (status & TX_STATUS_MSK) { - case TX_STATUS_SUCCESS: - case TX_STATUS_DIRECT_DONE: - info->flags |= IEEE80211_TX_STAT_ACK; - break; - case TX_STATUS_FAIL_DEST_PS: - info->flags |= IEEE80211_TX_STAT_TX_FILTERED; - break; - default: - break; - } - - info->status.rates[0].count = tx_resp->failure_frame + 1; - iwl_mvm_hwrate_to_tx_status(le32_to_cpu(tx_resp->initial_rate), - info); - info->status.status_driver_data[1] = - (void *)(uintptr_t)le32_to_cpu(tx_resp->initial_rate); - - /* Single frame failure in an AMPDU queue => send BAR */ - if (txq_id >= mvm->first_agg_queue && - !(info->flags & IEEE80211_TX_STAT_ACK) && - !(info->flags & IEEE80211_TX_STAT_TX_FILTERED)) - info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; - - /* W/A FW bug: seq_ctl is wrong when the status isn't success */ - if (status != TX_STATUS_SUCCESS) { - struct ieee80211_hdr *hdr = (void *)skb->data; - seq_ctl = le16_to_cpu(hdr->seq_ctrl); - } - - /* - * TODO: this is not accurate if we are freeing more than one - * packet. - */ - info->status.tx_time = - le16_to_cpu(tx_resp->wireless_media_time); - BUILD_BUG_ON(ARRAY_SIZE(info->status.status_driver_data) < 1); - info->status.status_driver_data[0] = - (void *)(uintptr_t)tx_resp->reduced_tpc; - - ieee80211_tx_status(mvm->hw, skb); - } - - if (txq_id >= 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 - * this Tx response relates. But if there is a hole in the - * bitmap of the BA we received, this Tx response may allow to - * reclaim the hole and all the subsequent packets that were - * already acked. In that case, seq_ctl != ssn, and the next - * packet to be reclaimed will be ssn and not seq_ctl. In that - * case, several packets will be reclaimed even if - * frame_count = 1. - * - * The ssn is the index (% 256) of the latest packet that has - * treated (acked / dropped) + 1. - */ - next_reclaimed = ssn; - } else { - /* The next packet to be reclaimed is the one after this one */ - next_reclaimed = IEEE80211_SEQ_TO_SN(seq_ctl + 0x10); - } - - IWL_DEBUG_TX_REPLY(mvm, - "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); - - rcu_read_lock(); - - sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); - /* - * sta can't be NULL otherwise it'd mean that the sta has been freed in - * the firmware while we still have packets for it in the Tx queues. - */ - if (WARN_ON_ONCE(!sta)) - goto out; - - if (!IS_ERR(sta)) { - mvmsta = iwl_mvm_sta_from_mac80211(sta); - - if (tid != IWL_TID_NON_QOS) { - struct iwl_mvm_tid_data *tid_data = - &mvmsta->tid_data[tid]; - - spin_lock_bh(&mvmsta->lock); - tid_data->next_reclaimed = next_reclaimed; - IWL_DEBUG_TX_REPLY(mvm, "Next reclaimed packet:%d\n", - next_reclaimed); - iwl_mvm_check_ratid_empty(mvm, sta, tid); - spin_unlock_bh(&mvmsta->lock); - } - - if (mvmsta->next_status_eosp) { - mvmsta->next_status_eosp = false; - ieee80211_sta_eosp(sta); - } - } else { - mvmsta = NULL; - } - - /* - * If the txq is not an AMPDU queue, there is no chance we freed - * several skbs. Check that out... - */ - if (txq_id >= mvm->first_agg_queue) - goto out; - - /* We can't free more than one frame at once on a shared queue */ - WARN_ON(skb_freed > 1); - - /* If we have still frames for this STA nothing to do here */ - if (!atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id])) - goto out; - - if (mvmsta && mvmsta->vif->type == NL80211_IFTYPE_AP) { - - /* - * If there are no pending frames for this STA and - * the tx to this station is not disabled, notify - * mac80211 that this station can now wake up in its - * STA table. - * If mvmsta is not NULL, sta is valid. - */ - - spin_lock_bh(&mvmsta->lock); - - if (!mvmsta->disable_tx) - ieee80211_sta_block_awake(mvm->hw, sta, false); - - spin_unlock_bh(&mvmsta->lock); - } - - if (PTR_ERR(sta) == -EBUSY || PTR_ERR(sta) == -ENOENT) { - /* - * We are draining and this was the last packet - pre_rcu_remove - * has been called already. We might be after the - * synchronize_net already. - * Don't rely on iwl_mvm_rm_sta to see the empty Tx queues. - */ - set_bit(sta_id, mvm->sta_drained); - schedule_work(&mvm->sta_drained_wk); - } - -out: - rcu_read_unlock(); -} - -#ifdef CONFIG_IWLWIFI_DEBUG -#define AGG_TX_STATE_(x) case AGG_TX_STATE_ ## x: return #x -static const char *iwl_get_agg_tx_status(u16 status) -{ - switch (status & AGG_TX_STATE_STATUS_MSK) { - AGG_TX_STATE_(TRANSMITTED); - AGG_TX_STATE_(UNDERRUN); - AGG_TX_STATE_(BT_PRIO); - AGG_TX_STATE_(FEW_BYTES); - AGG_TX_STATE_(ABORT); - AGG_TX_STATE_(LAST_SENT_TTL); - AGG_TX_STATE_(LAST_SENT_TRY_CNT); - AGG_TX_STATE_(LAST_SENT_BT_KILL); - AGG_TX_STATE_(SCD_QUERY); - AGG_TX_STATE_(TEST_BAD_CRC32); - AGG_TX_STATE_(RESPONSE); - AGG_TX_STATE_(DUMP_TX); - AGG_TX_STATE_(DELAY_TX); - } - - return "UNKNOWN"; -} - -static void iwl_mvm_rx_tx_cmd_agg_dbg(struct iwl_mvm *mvm, - struct iwl_rx_packet *pkt) -{ - struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data; - struct agg_tx_status *frame_status = &tx_resp->status; - int i; - - for (i = 0; i < tx_resp->frame_count; i++) { - u16 fstatus = le16_to_cpu(frame_status[i].status); - - IWL_DEBUG_TX_REPLY(mvm, - "status %s (0x%04x), try-count (%d) seq (0x%x)\n", - iwl_get_agg_tx_status(fstatus), - fstatus & AGG_TX_STATE_STATUS_MSK, - (fstatus & AGG_TX_STATE_TRY_CNT_MSK) >> - AGG_TX_STATE_TRY_CNT_POS, - le16_to_cpu(frame_status[i].sequence)); - } -} -#else -static void iwl_mvm_rx_tx_cmd_agg_dbg(struct iwl_mvm *mvm, - struct iwl_rx_packet *pkt) -{} -#endif /* CONFIG_IWLWIFI_DEBUG */ - -static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm, - struct iwl_rx_packet *pkt) -{ - struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data; - int sta_id = IWL_MVM_TX_RES_GET_RA(tx_resp->ra_tid); - int tid = IWL_MVM_TX_RES_GET_TID(tx_resp->ra_tid); - u16 sequence = le16_to_cpu(pkt->hdr.sequence); - struct ieee80211_sta *sta; - - if (WARN_ON_ONCE(SEQ_TO_QUEUE(sequence) < mvm->first_agg_queue)) - return; - - if (WARN_ON_ONCE(tid == IWL_TID_NON_QOS)) - return; - - iwl_mvm_rx_tx_cmd_agg_dbg(mvm, pkt); - - rcu_read_lock(); - - sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); - - if (!WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) { - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - mvmsta->tid_data[tid].rate_n_flags = - le32_to_cpu(tx_resp->initial_rate); - mvmsta->tid_data[tid].reduced_tpc = tx_resp->reduced_tpc; - mvmsta->tid_data[tid].tx_time = - le16_to_cpu(tx_resp->wireless_media_time); - } - - rcu_read_unlock(); -} - -void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data; - - if (tx_resp->frame_count == 1) - iwl_mvm_rx_tx_cmd_single(mvm, pkt); - else - iwl_mvm_rx_tx_cmd_agg(mvm, pkt); -} - -static void iwl_mvm_tx_info_from_ba_notif(struct ieee80211_tx_info *info, - struct iwl_mvm_ba_notif *ba_notif, - struct iwl_mvm_tid_data *tid_data) -{ - info->flags |= IEEE80211_TX_STAT_AMPDU; - info->status.ampdu_ack_len = ba_notif->txed_2_done; - info->status.ampdu_len = ba_notif->txed; - iwl_mvm_hwrate_to_tx_status(tid_data->rate_n_flags, - info); - /* TODO: not accounted if the whole A-MPDU failed */ - info->status.tx_time = tid_data->tx_time; - info->status.status_driver_data[0] = - (void *)(uintptr_t)tid_data->reduced_tpc; - info->status.status_driver_data[1] = - (void *)(uintptr_t)tid_data->rate_n_flags; -} - -void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_mvm_ba_notif *ba_notif = (void *)pkt->data; - struct sk_buff_head reclaimed_skbs; - struct iwl_mvm_tid_data *tid_data; - struct ieee80211_sta *sta; - struct iwl_mvm_sta *mvmsta; - struct sk_buff *skb; - int sta_id, tid, freed; - /* "flow" corresponds to Tx queue */ - u16 scd_flow = le16_to_cpu(ba_notif->scd_flow); - /* "ssn" is start of block-ack Tx window, corresponds to index - * (in Tx queue's circular buffer) of first TFD/frame in window */ - u16 ba_resp_scd_ssn = le16_to_cpu(ba_notif->scd_ssn); - - sta_id = ba_notif->sta_id; - tid = ba_notif->tid; - - if (WARN_ONCE(sta_id >= IWL_MVM_STATION_COUNT || - tid >= IWL_MAX_TID_COUNT, - "sta_id %d tid %d", sta_id, tid)) - return; - - rcu_read_lock(); - - sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); - - /* Reclaiming frames for a station that has been deleted ? */ - if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) { - rcu_read_unlock(); - return; - } - - mvmsta = iwl_mvm_sta_from_mac80211(sta); - tid_data = &mvmsta->tid_data[tid]; - - if (tid_data->txq_id != scd_flow) { - IWL_ERR(mvm, - "invalid BA notification: Q %d, tid %d, flow %d\n", - tid_data->txq_id, tid, scd_flow); - rcu_read_unlock(); - return; - } - - spin_lock_bh(&mvmsta->lock); - - __skb_queue_head_init(&reclaimed_skbs); - - /* - * Release all TFDs before the SSN, i.e. all TFDs in front of - * block-ack window (we assume that they've been successfully - * transmitted ... if not, it's too late anyway). - */ - iwl_trans_reclaim(mvm->trans, scd_flow, ba_resp_scd_ssn, - &reclaimed_skbs); - - IWL_DEBUG_TX_REPLY(mvm, - "BA_NOTIFICATION Received from %pM, sta_id = %d\n", - (u8 *)&ba_notif->sta_addr_lo32, - ba_notif->sta_id); - IWL_DEBUG_TX_REPLY(mvm, - "TID = %d, SeqCtl = %d, bitmap = 0x%llx, scd_flow = %d, scd_ssn = %d sent:%d, acked:%d\n", - ba_notif->tid, le16_to_cpu(ba_notif->seq_ctl), - (unsigned long long)le64_to_cpu(ba_notif->bitmap), - scd_flow, ba_resp_scd_ssn, ba_notif->txed, - ba_notif->txed_2_done); - - tid_data->next_reclaimed = ba_resp_scd_ssn; - - iwl_mvm_check_ratid_empty(mvm, sta, tid); - - freed = 0; - - skb_queue_walk(&reclaimed_skbs, skb) { - struct ieee80211_hdr *hdr = (void *)skb->data; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - - if (ieee80211_is_data_qos(hdr->frame_control)) - freed++; - else - WARN_ON_ONCE(1); - - iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]); - - memset(&info->status, 0, sizeof(info->status)); - /* Packet was transmitted successfully, failures come as single - * frames because before failing a frame the firmware transmits - * it without aggregation at least once. - */ - info->flags |= IEEE80211_TX_STAT_ACK; - - /* this is the first skb we deliver in this batch */ - /* put the rate scaling data there */ - if (freed == 1) - iwl_mvm_tx_info_from_ba_notif(info, ba_notif, tid_data); - } - - spin_unlock_bh(&mvmsta->lock); - - /* We got a BA notif with 0 acked or scd_ssn didn't progress which is - * possible (i.e. first MPDU in the aggregation wasn't acked) - * Still it's important to update RS about sent vs. acked. - */ - if (skb_queue_empty(&reclaimed_skbs)) { - struct ieee80211_tx_info ba_info = {}; - struct ieee80211_chanctx_conf *chanctx_conf = NULL; - - if (mvmsta->vif) - chanctx_conf = - rcu_dereference(mvmsta->vif->chanctx_conf); - - if (WARN_ON_ONCE(!chanctx_conf)) - goto out; - - ba_info.band = chanctx_conf->def.chan->band; - iwl_mvm_tx_info_from_ba_notif(&ba_info, ba_notif, tid_data); - - IWL_DEBUG_TX_REPLY(mvm, "No reclaim. Update rs directly\n"); - iwl_mvm_rs_tx_status(mvm, sta, tid, &ba_info); - } - -out: - rcu_read_unlock(); - - while (!skb_queue_empty(&reclaimed_skbs)) { - skb = __skb_dequeue(&reclaimed_skbs); - ieee80211_tx_status(mvm->hw, skb); - } -} - -/* - * Note that there are transports that buffer frames before they reach - * the firmware. This means that after flush_tx_path is called, the - * queue might not be empty. The race-free way to handle this is to: - * 1) set the station as draining - * 2) flush the Tx path - * 3) wait for the transport queues to be empty - */ -int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, u32 flags) -{ - int ret; - struct iwl_tx_path_flush_cmd flush_cmd = { - .queues_ctl = cpu_to_le32(tfd_msk), - .flush_ctl = cpu_to_le16(DUMP_TX_FIFO_FLUSH), - }; - - ret = iwl_mvm_send_cmd_pdu(mvm, TXPATH_FLUSH, flags, - sizeof(flush_cmd), &flush_cmd); - if (ret) - IWL_ERR(mvm, "Failed to send flush command (%d)\n", ret); - return ret; -} diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c deleted file mode 100644 index ad0f16909..000000000 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ /dev/null @@ -1,1083 +0,0 @@ -/****************************************************************************** - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright (C) 2015 Intel Deutschland GmbH - * - * 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) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * 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 "iwl-debug.h" -#include "iwl-io.h" -#include "iwl-prph.h" - -#include "mvm.h" -#include "fw-api-rs.h" - -/* - * Will return 0 even if the cmd failed when RFKILL is asserted unless - * CMD_WANT_SKB is set in cmd->flags. - */ -int iwl_mvm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd) -{ - int ret; - -#if defined(CONFIG_IWLWIFI_DEBUGFS) && defined(CONFIG_PM_SLEEP) - if (WARN_ON(mvm->d3_test_active)) - return -EIO; -#endif - - /* - * Synchronous commands from this op-mode must hold - * the mutex, this ensures we don't try to send two - * (or more) synchronous commands at a time. - */ - if (!(cmd->flags & CMD_ASYNC)) - lockdep_assert_held(&mvm->mutex); - - ret = iwl_trans_send_cmd(mvm->trans, cmd); - - /* - * If the caller wants the SKB, then don't hide any problems, the - * caller might access the response buffer which will be NULL if - * the command failed. - */ - if (cmd->flags & CMD_WANT_SKB) - return ret; - - /* Silently ignore failures if RFKILL is asserted */ - if (!ret || ret == -ERFKILL) - return 0; - return ret; -} - -int iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u32 id, - u32 flags, u16 len, const void *data) -{ - struct iwl_host_cmd cmd = { - .id = id, - .len = { len, }, - .data = { data, }, - .flags = flags, - }; - - return iwl_mvm_send_cmd(mvm, &cmd); -} - -/* - * We assume that the caller set the status to the success value - */ -int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd, - u32 *status) -{ - struct iwl_rx_packet *pkt; - struct iwl_cmd_response *resp; - int ret, resp_len; - - lockdep_assert_held(&mvm->mutex); - -#if defined(CONFIG_IWLWIFI_DEBUGFS) && defined(CONFIG_PM_SLEEP) - if (WARN_ON(mvm->d3_test_active)) - return -EIO; -#endif - - /* - * Only synchronous commands can wait for status, - * we use WANT_SKB so the caller can't. - */ - if (WARN_ONCE(cmd->flags & (CMD_ASYNC | CMD_WANT_SKB), - "cmd flags %x", cmd->flags)) - return -EINVAL; - - cmd->flags |= CMD_WANT_SKB; - - ret = iwl_trans_send_cmd(mvm->trans, cmd); - if (ret == -ERFKILL) { - /* - * The command failed because of RFKILL, don't update - * the status, leave it as success and return 0. - */ - return 0; - } else if (ret) { - return ret; - } - - pkt = cmd->resp_pkt; - /* Can happen if RFKILL is asserted */ - if (!pkt) { - ret = 0; - goto out_free_resp; - } - - resp_len = iwl_rx_packet_payload_len(pkt); - if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { - ret = -EIO; - goto out_free_resp; - } - - resp = (void *)pkt->data; - *status = le32_to_cpu(resp->status); - out_free_resp: - iwl_free_resp(cmd); - return ret; -} - -/* - * We assume that the caller set the status to the sucess value - */ -int iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u32 id, u16 len, - const void *data, u32 *status) -{ - struct iwl_host_cmd cmd = { - .id = id, - .len = { len, }, - .data = { data, }, - }; - - return iwl_mvm_send_cmd_status(mvm, &cmd, status); -} - -#define IWL_DECLARE_RATE_INFO(r) \ - [IWL_RATE_##r##M_INDEX] = IWL_RATE_##r##M_PLCP - -/* - * Translate from fw_rate_index (IWL_RATE_XXM_INDEX) to PLCP - */ -static const u8 fw_rate_idx_to_plcp[IWL_RATE_COUNT] = { - IWL_DECLARE_RATE_INFO(1), - IWL_DECLARE_RATE_INFO(2), - IWL_DECLARE_RATE_INFO(5), - IWL_DECLARE_RATE_INFO(11), - IWL_DECLARE_RATE_INFO(6), - IWL_DECLARE_RATE_INFO(9), - IWL_DECLARE_RATE_INFO(12), - IWL_DECLARE_RATE_INFO(18), - IWL_DECLARE_RATE_INFO(24), - IWL_DECLARE_RATE_INFO(36), - IWL_DECLARE_RATE_INFO(48), - IWL_DECLARE_RATE_INFO(54), -}; - -int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags, - enum ieee80211_band band) -{ - int rate = rate_n_flags & RATE_LEGACY_RATE_MSK; - int idx; - int band_offset = 0; - - /* Legacy rate format, search for match in table */ - if (band == IEEE80211_BAND_5GHZ) - band_offset = IWL_FIRST_OFDM_RATE; - for (idx = band_offset; idx < IWL_RATE_COUNT_LEGACY; idx++) - if (fw_rate_idx_to_plcp[idx] == rate) - return idx - band_offset; - - return -1; -} - -u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx) -{ - /* Get PLCP rate for tx_cmd->rate_n_flags */ - return fw_rate_idx_to_plcp[rate_idx]; -} - -void iwl_mvm_rx_fw_error(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_error_resp *err_resp = (void *)pkt->data; - - IWL_ERR(mvm, "FW Error notification: type 0x%08X cmd_id 0x%02X\n", - le32_to_cpu(err_resp->error_type), err_resp->cmd_id); - IWL_ERR(mvm, "FW Error notification: seq 0x%04X service 0x%08X\n", - le16_to_cpu(err_resp->bad_cmd_seq_num), - le32_to_cpu(err_resp->error_service)); - IWL_ERR(mvm, "FW Error notification: timestamp 0x%16llX\n", - le64_to_cpu(err_resp->timestamp)); -} - -/* - * Returns the first antenna as ANT_[ABC], as defined in iwl-config.h. - * The parameter should also be a combination of ANT_[ABC]. - */ -u8 first_antenna(u8 mask) -{ - BUILD_BUG_ON(ANT_A != BIT(0)); /* using ffs is wrong if not */ - if (WARN_ON_ONCE(!mask)) /* ffs will return 0 if mask is zeroed */ - return BIT(0); - return BIT(ffs(mask) - 1); -} - -/* - * Toggles between TX antennas to send the probe request on. - * Receives the bitmask of valid TX antennas and the *index* used - * for the last TX, and returns the next valid *index* to use. - * In order to set it in the tx_cmd, must do BIT(idx). - */ -u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx) -{ - u8 ind = last_idx; - int i; - - for (i = 0; i < RATE_MCS_ANT_NUM; i++) { - ind = (ind + 1) % RATE_MCS_ANT_NUM; - if (valid & BIT(ind)) - return ind; - } - - WARN_ONCE(1, "Failed to toggle between antennas 0x%x", valid); - return last_idx; -} - -static const struct { - const char *name; - u8 num; -} advanced_lookup[] = { - { "NMI_INTERRUPT_WDG", 0x34 }, - { "SYSASSERT", 0x35 }, - { "UCODE_VERSION_MISMATCH", 0x37 }, - { "BAD_COMMAND", 0x38 }, - { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C }, - { "FATAL_ERROR", 0x3D }, - { "NMI_TRM_HW_ERR", 0x46 }, - { "NMI_INTERRUPT_TRM", 0x4C }, - { "NMI_INTERRUPT_BREAK_POINT", 0x54 }, - { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C }, - { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 }, - { "NMI_INTERRUPT_HOST", 0x66 }, - { "NMI_INTERRUPT_ACTION_PT", 0x7C }, - { "NMI_INTERRUPT_UNKNOWN", 0x84 }, - { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 }, - { "ADVANCED_SYSASSERT", 0 }, -}; - -static const char *desc_lookup(u32 num) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(advanced_lookup) - 1; i++) - if (advanced_lookup[i].num == num) - return advanced_lookup[i].name; - - /* No entry matches 'num', so it is the last: ADVANCED_SYSASSERT */ - return advanced_lookup[i].name; -} - -/* - * Note: This structure is read from the device with IO accesses, - * and the reading already does the endian conversion. As it is - * read with u32-sized accesses, any members with a different size - * need to be ordered correctly though! - */ -struct iwl_error_event_table_v1 { - u32 valid; /* (nonzero) valid, (0) log is empty */ - u32 error_id; /* type of error */ - u32 pc; /* program counter */ - u32 blink1; /* branch link */ - u32 blink2; /* branch link */ - u32 ilink1; /* interrupt link */ - u32 ilink2; /* interrupt link */ - u32 data1; /* error-specific data */ - u32 data2; /* error-specific data */ - u32 data3; /* error-specific data */ - u32 bcon_time; /* beacon timer */ - u32 tsf_low; /* network timestamp function timer */ - u32 tsf_hi; /* network timestamp function timer */ - u32 gp1; /* GP1 timer register */ - u32 gp2; /* GP2 timer register */ - u32 gp3; /* GP3 timer register */ - u32 ucode_ver; /* uCode version */ - u32 hw_ver; /* HW Silicon version */ - u32 brd_ver; /* HW board version */ - u32 log_pc; /* log program counter */ - u32 frame_ptr; /* frame pointer */ - u32 stack_ptr; /* stack pointer */ - u32 hcmd; /* last host command header */ - u32 isr0; /* isr status register LMPM_NIC_ISR0: - * rxtx_flag */ - u32 isr1; /* isr status register LMPM_NIC_ISR1: - * host_flag */ - u32 isr2; /* isr status register LMPM_NIC_ISR2: - * enc_flag */ - u32 isr3; /* isr status register LMPM_NIC_ISR3: - * time_flag */ - u32 isr4; /* isr status register LMPM_NIC_ISR4: - * wico interrupt */ - u32 isr_pref; /* isr status register LMPM_NIC_PREF_STAT */ - u32 wait_event; /* wait event() caller address */ - u32 l2p_control; /* L2pControlField */ - u32 l2p_duration; /* L2pDurationField */ - u32 l2p_mhvalid; /* L2pMhValidBits */ - u32 l2p_addr_match; /* L2pAddrMatchStat */ - u32 lmpm_pmg_sel; /* indicate which clocks are turned on - * (LMPM_PMG_SEL) */ - u32 u_timestamp; /* indicate when the date and time of the - * compilation */ - u32 flow_handler; /* FH read/write pointers, RX credit */ -} __packed /* LOG_ERROR_TABLE_API_S_VER_1 */; - -struct iwl_error_event_table { - u32 valid; /* (nonzero) valid, (0) log is empty */ - u32 error_id; /* type of error */ - u32 pc; /* program counter */ - u32 blink1; /* branch link */ - u32 blink2; /* branch link */ - u32 ilink1; /* interrupt link */ - u32 ilink2; /* interrupt link */ - u32 data1; /* error-specific data */ - u32 data2; /* error-specific data */ - u32 data3; /* error-specific data */ - u32 bcon_time; /* beacon timer */ - u32 tsf_low; /* network timestamp function timer */ - u32 tsf_hi; /* network timestamp function timer */ - u32 gp1; /* GP1 timer register */ - u32 gp2; /* GP2 timer register */ - u32 gp3; /* GP3 timer register */ - u32 major; /* uCode version major */ - u32 minor; /* uCode version minor */ - u32 hw_ver; /* HW Silicon version */ - u32 brd_ver; /* HW board version */ - u32 log_pc; /* log program counter */ - u32 frame_ptr; /* frame pointer */ - u32 stack_ptr; /* stack pointer */ - u32 hcmd; /* last host command header */ - u32 isr0; /* isr status register LMPM_NIC_ISR0: - * rxtx_flag */ - u32 isr1; /* isr status register LMPM_NIC_ISR1: - * host_flag */ - u32 isr2; /* isr status register LMPM_NIC_ISR2: - * enc_flag */ - u32 isr3; /* isr status register LMPM_NIC_ISR3: - * time_flag */ - u32 isr4; /* isr status register LMPM_NIC_ISR4: - * wico interrupt */ - u32 isr_pref; /* isr status register LMPM_NIC_PREF_STAT */ - u32 wait_event; /* wait event() caller address */ - u32 l2p_control; /* L2pControlField */ - u32 l2p_duration; /* L2pDurationField */ - u32 l2p_mhvalid; /* L2pMhValidBits */ - u32 l2p_addr_match; /* L2pAddrMatchStat */ - u32 lmpm_pmg_sel; /* indicate which clocks are turned on - * (LMPM_PMG_SEL) */ - u32 u_timestamp; /* indicate when the date and time of the - * compilation */ - u32 flow_handler; /* FH read/write pointers, RX credit */ -} __packed /* LOG_ERROR_TABLE_API_S_VER_2 */; - -/* - * UMAC error struct - relevant starting from family 8000 chip. - * Note: This structure is read from the device with IO accesses, - * and the reading already does the endian conversion. As it is - * read with u32-sized accesses, any members with a different size - * need to be ordered correctly though! - */ -struct iwl_umac_error_event_table { - u32 valid; /* (nonzero) valid, (0) log is empty */ - u32 error_id; /* type of error */ - u32 blink1; /* branch link */ - u32 blink2; /* branch link */ - u32 ilink1; /* interrupt link */ - u32 ilink2; /* interrupt link */ - u32 data1; /* error-specific data */ - u32 data2; /* error-specific data */ - u32 data3; /* error-specific data */ - u32 umac_major; - u32 umac_minor; - u32 frame_pointer; /* core register 27*/ - u32 stack_pointer; /* core register 28 */ - u32 cmd_header; /* latest host cmd sent to UMAC */ - u32 nic_isr_pref; /* ISR status register */ -} __packed; - -#define ERROR_START_OFFSET (1 * sizeof(u32)) -#define ERROR_ELEM_SIZE (7 * sizeof(u32)) - -static void iwl_mvm_dump_umac_error_log(struct iwl_mvm *mvm) -{ - struct iwl_trans *trans = mvm->trans; - struct iwl_umac_error_event_table table; - u32 base; - - base = mvm->umac_error_event_table; - - if (base < 0x800000) { - IWL_ERR(mvm, - "Not valid error log pointer 0x%08X for %s uCode\n", - base, - (mvm->cur_ucode == IWL_UCODE_INIT) - ? "Init" : "RT"); - return; - } - - iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table)); - - if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { - IWL_ERR(trans, "Start IWL Error Log Dump:\n"); - IWL_ERR(trans, "Status: 0x%08lX, count: %d\n", - mvm->status, table.valid); - } - - IWL_ERR(mvm, "0x%08X | %s\n", table.error_id, - desc_lookup(table.error_id)); - IWL_ERR(mvm, "0x%08X | umac branchlink1\n", table.blink1); - IWL_ERR(mvm, "0x%08X | umac branchlink2\n", table.blink2); - IWL_ERR(mvm, "0x%08X | umac interruptlink1\n", table.ilink1); - IWL_ERR(mvm, "0x%08X | umac interruptlink2\n", table.ilink2); - IWL_ERR(mvm, "0x%08X | umac data1\n", table.data1); - IWL_ERR(mvm, "0x%08X | umac data2\n", table.data2); - IWL_ERR(mvm, "0x%08X | umac data3\n", table.data3); - IWL_ERR(mvm, "0x%08X | umac major\n", table.umac_major); - IWL_ERR(mvm, "0x%08X | umac minor\n", table.umac_minor); - IWL_ERR(mvm, "0x%08X | frame pointer\n", table.frame_pointer); - IWL_ERR(mvm, "0x%08X | stack pointer\n", table.stack_pointer); - IWL_ERR(mvm, "0x%08X | last host cmd\n", table.cmd_header); - IWL_ERR(mvm, "0x%08X | isr status reg\n", table.nic_isr_pref); -} - -static void iwl_mvm_dump_nic_error_log_old(struct iwl_mvm *mvm) -{ - struct iwl_trans *trans = mvm->trans; - struct iwl_error_event_table_v1 table; - u32 base; - - base = mvm->error_event_table; - if (mvm->cur_ucode == IWL_UCODE_INIT) { - if (!base) - base = mvm->fw->init_errlog_ptr; - } else { - if (!base) - base = mvm->fw->inst_errlog_ptr; - } - - if (base < 0x800000) { - IWL_ERR(mvm, - "Not valid error log pointer 0x%08X for %s uCode\n", - base, - (mvm->cur_ucode == IWL_UCODE_INIT) - ? "Init" : "RT"); - return; - } - - iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table)); - - if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { - IWL_ERR(trans, "Start IWL Error Log Dump:\n"); - IWL_ERR(trans, "Status: 0x%08lX, count: %d\n", - mvm->status, table.valid); - } - - /* Do not change this output - scripts rely on it */ - - IWL_ERR(mvm, "Loaded firmware version: %s\n", mvm->fw->fw_version); - - trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low, - table.data1, table.data2, table.data3, - table.blink1, table.blink2, table.ilink1, - table.ilink2, table.bcon_time, table.gp1, - table.gp2, table.gp3, table.ucode_ver, 0, - table.hw_ver, table.brd_ver); - IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id, - desc_lookup(table.error_id)); - IWL_ERR(mvm, "0x%08X | uPc\n", table.pc); - IWL_ERR(mvm, "0x%08X | branchlink1\n", table.blink1); - IWL_ERR(mvm, "0x%08X | branchlink2\n", table.blink2); - IWL_ERR(mvm, "0x%08X | interruptlink1\n", table.ilink1); - IWL_ERR(mvm, "0x%08X | interruptlink2\n", table.ilink2); - IWL_ERR(mvm, "0x%08X | data1\n", table.data1); - IWL_ERR(mvm, "0x%08X | data2\n", table.data2); - IWL_ERR(mvm, "0x%08X | data3\n", table.data3); - IWL_ERR(mvm, "0x%08X | beacon time\n", table.bcon_time); - IWL_ERR(mvm, "0x%08X | tsf low\n", table.tsf_low); - IWL_ERR(mvm, "0x%08X | tsf hi\n", table.tsf_hi); - IWL_ERR(mvm, "0x%08X | time gp1\n", table.gp1); - IWL_ERR(mvm, "0x%08X | time gp2\n", table.gp2); - IWL_ERR(mvm, "0x%08X | time gp3\n", table.gp3); - IWL_ERR(mvm, "0x%08X | uCode version\n", table.ucode_ver); - IWL_ERR(mvm, "0x%08X | hw version\n", table.hw_ver); - IWL_ERR(mvm, "0x%08X | board version\n", table.brd_ver); - IWL_ERR(mvm, "0x%08X | hcmd\n", table.hcmd); - IWL_ERR(mvm, "0x%08X | isr0\n", table.isr0); - IWL_ERR(mvm, "0x%08X | isr1\n", table.isr1); - IWL_ERR(mvm, "0x%08X | isr2\n", table.isr2); - IWL_ERR(mvm, "0x%08X | isr3\n", table.isr3); - IWL_ERR(mvm, "0x%08X | isr4\n", table.isr4); - IWL_ERR(mvm, "0x%08X | isr_pref\n", table.isr_pref); - IWL_ERR(mvm, "0x%08X | wait_event\n", table.wait_event); - IWL_ERR(mvm, "0x%08X | l2p_control\n", table.l2p_control); - IWL_ERR(mvm, "0x%08X | l2p_duration\n", table.l2p_duration); - IWL_ERR(mvm, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid); - IWL_ERR(mvm, "0x%08X | l2p_addr_match\n", table.l2p_addr_match); - IWL_ERR(mvm, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel); - IWL_ERR(mvm, "0x%08X | timestamp\n", table.u_timestamp); - IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler); - - if (mvm->support_umac_log) - iwl_mvm_dump_umac_error_log(mvm); -} - -void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) -{ - struct iwl_trans *trans = mvm->trans; - struct iwl_error_event_table table; - u32 base; - - if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_NEW_VERSION)) { - iwl_mvm_dump_nic_error_log_old(mvm); - return; - } - - base = mvm->error_event_table; - if (mvm->cur_ucode == IWL_UCODE_INIT) { - if (!base) - base = mvm->fw->init_errlog_ptr; - } else { - if (!base) - base = mvm->fw->inst_errlog_ptr; - } - - if (base < 0x800000) { - IWL_ERR(mvm, - "Not valid error log pointer 0x%08X for %s uCode\n", - base, - (mvm->cur_ucode == IWL_UCODE_INIT) - ? "Init" : "RT"); - return; - } - - iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table)); - - if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { - IWL_ERR(trans, "Start IWL Error Log Dump:\n"); - IWL_ERR(trans, "Status: 0x%08lX, count: %d\n", - mvm->status, table.valid); - } - - /* Do not change this output - scripts rely on it */ - - IWL_ERR(mvm, "Loaded firmware version: %s\n", mvm->fw->fw_version); - - trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low, - table.data1, table.data2, table.data3, - table.blink1, table.blink2, table.ilink1, - table.ilink2, table.bcon_time, table.gp1, - table.gp2, table.gp3, table.major, - table.minor, table.hw_ver, table.brd_ver); - IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id, - desc_lookup(table.error_id)); - IWL_ERR(mvm, "0x%08X | uPc\n", table.pc); - IWL_ERR(mvm, "0x%08X | branchlink1\n", table.blink1); - IWL_ERR(mvm, "0x%08X | branchlink2\n", table.blink2); - IWL_ERR(mvm, "0x%08X | interruptlink1\n", table.ilink1); - IWL_ERR(mvm, "0x%08X | interruptlink2\n", table.ilink2); - IWL_ERR(mvm, "0x%08X | data1\n", table.data1); - IWL_ERR(mvm, "0x%08X | data2\n", table.data2); - IWL_ERR(mvm, "0x%08X | data3\n", table.data3); - IWL_ERR(mvm, "0x%08X | beacon time\n", table.bcon_time); - IWL_ERR(mvm, "0x%08X | tsf low\n", table.tsf_low); - IWL_ERR(mvm, "0x%08X | tsf hi\n", table.tsf_hi); - IWL_ERR(mvm, "0x%08X | time gp1\n", table.gp1); - IWL_ERR(mvm, "0x%08X | time gp2\n", table.gp2); - IWL_ERR(mvm, "0x%08X | time gp3\n", table.gp3); - IWL_ERR(mvm, "0x%08X | uCode version major\n", table.major); - IWL_ERR(mvm, "0x%08X | uCode version minor\n", table.minor); - IWL_ERR(mvm, "0x%08X | hw version\n", table.hw_ver); - IWL_ERR(mvm, "0x%08X | board version\n", table.brd_ver); - IWL_ERR(mvm, "0x%08X | hcmd\n", table.hcmd); - IWL_ERR(mvm, "0x%08X | isr0\n", table.isr0); - IWL_ERR(mvm, "0x%08X | isr1\n", table.isr1); - IWL_ERR(mvm, "0x%08X | isr2\n", table.isr2); - IWL_ERR(mvm, "0x%08X | isr3\n", table.isr3); - IWL_ERR(mvm, "0x%08X | isr4\n", table.isr4); - IWL_ERR(mvm, "0x%08X | isr_pref\n", table.isr_pref); - IWL_ERR(mvm, "0x%08X | wait_event\n", table.wait_event); - IWL_ERR(mvm, "0x%08X | l2p_control\n", table.l2p_control); - IWL_ERR(mvm, "0x%08X | l2p_duration\n", table.l2p_duration); - IWL_ERR(mvm, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid); - IWL_ERR(mvm, "0x%08X | l2p_addr_match\n", table.l2p_addr_match); - IWL_ERR(mvm, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel); - IWL_ERR(mvm, "0x%08X | timestamp\n", table.u_timestamp); - IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler); - - if (mvm->support_umac_log) - iwl_mvm_dump_umac_error_log(mvm); -} - -int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 minq, u8 maxq) -{ - int i; - - lockdep_assert_held(&mvm->queue_info_lock); - - for (i = minq; i <= maxq; i++) - if (mvm->queue_info[i].hw_queue_refcount == 0 && - !mvm->queue_info[i].setup_reserved) - return i; - - return -ENOSPC; -} - -void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, - u16 ssn, const struct iwl_trans_txq_scd_cfg *cfg, - unsigned int wdg_timeout) -{ - bool enable_queue = true; - - spin_lock_bh(&mvm->queue_info_lock); - - /* Make sure this TID isn't already enabled */ - if (mvm->queue_info[queue].tid_bitmap & BIT(cfg->tid)) { - spin_unlock_bh(&mvm->queue_info_lock); - IWL_ERR(mvm, "Trying to enable TXQ with existing TID %d\n", - cfg->tid); - return; - } - - /* Update mappings and refcounts */ - mvm->queue_info[queue].hw_queue_to_mac80211 |= BIT(mac80211_queue); - mvm->queue_info[queue].hw_queue_refcount++; - if (mvm->queue_info[queue].hw_queue_refcount > 1) - enable_queue = false; - mvm->queue_info[queue].tid_bitmap |= BIT(cfg->tid); - - IWL_DEBUG_TX_QUEUES(mvm, - "Enabling TXQ #%d refcount=%d (mac80211 map:0x%x)\n", - queue, mvm->queue_info[queue].hw_queue_refcount, - mvm->queue_info[queue].hw_queue_to_mac80211); - - spin_unlock_bh(&mvm->queue_info_lock); - - /* Send the enabling command if we need to */ - if (enable_queue) { - struct iwl_scd_txq_cfg_cmd cmd = { - .scd_queue = queue, - .enable = 1, - .window = cfg->frame_limit, - .sta_id = cfg->sta_id, - .ssn = cpu_to_le16(ssn), - .tx_fifo = cfg->fifo, - .aggregate = cfg->aggregate, - .tid = cfg->tid, - }; - - iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn, NULL, - wdg_timeout); - WARN(iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, sizeof(cmd), - &cmd), - "Failed to configure queue %d on FIFO %d\n", queue, - cfg->fifo); - } -} - -void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, - u8 tid, u8 flags) -{ - struct iwl_scd_txq_cfg_cmd cmd = { - .scd_queue = queue, - .enable = 0, - }; - bool remove_mac_queue = true; - int ret; - - spin_lock_bh(&mvm->queue_info_lock); - - if (WARN_ON(mvm->queue_info[queue].hw_queue_refcount == 0)) { - spin_unlock_bh(&mvm->queue_info_lock); - return; - } - - mvm->queue_info[queue].tid_bitmap &= ~BIT(tid); - - /* - * If there is another TID with the same AC - don't remove the MAC queue - * from the mapping - */ - if (tid < IWL_MAX_TID_COUNT) { - unsigned long tid_bitmap = - mvm->queue_info[queue].tid_bitmap; - int ac = tid_to_mac80211_ac[tid]; - int i; - - for_each_set_bit(i, &tid_bitmap, IWL_MAX_TID_COUNT) { - if (tid_to_mac80211_ac[i] == ac) - remove_mac_queue = false; - } - } - - if (remove_mac_queue) - mvm->queue_info[queue].hw_queue_to_mac80211 &= - ~BIT(mac80211_queue); - mvm->queue_info[queue].hw_queue_refcount--; - - cmd.enable = mvm->queue_info[queue].hw_queue_refcount ? 1 : 0; - - IWL_DEBUG_TX_QUEUES(mvm, - "Disabling TXQ #%d refcount=%d (mac80211 map:0x%x)\n", - queue, - mvm->queue_info[queue].hw_queue_refcount, - mvm->queue_info[queue].hw_queue_to_mac80211); - - /* If the queue is still enabled - nothing left to do in this func */ - if (cmd.enable) { - spin_unlock_bh(&mvm->queue_info_lock); - return; - } - - /* Make sure queue info is correct even though we overwrite it */ - WARN(mvm->queue_info[queue].hw_queue_refcount || - mvm->queue_info[queue].tid_bitmap || - mvm->queue_info[queue].hw_queue_to_mac80211, - "TXQ #%d info out-of-sync - refcount=%d, mac map=0x%x, tid=0x%x\n", - queue, mvm->queue_info[queue].hw_queue_refcount, - mvm->queue_info[queue].hw_queue_to_mac80211, - mvm->queue_info[queue].tid_bitmap); - - /* If we are here - the queue is freed and we can zero out these vals */ - mvm->queue_info[queue].hw_queue_refcount = 0; - mvm->queue_info[queue].tid_bitmap = 0; - mvm->queue_info[queue].hw_queue_to_mac80211 = 0; - - spin_unlock_bh(&mvm->queue_info_lock); - - iwl_trans_txq_disable(mvm->trans, queue, false); - ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, flags, - sizeof(cmd), &cmd); - if (ret) - IWL_ERR(mvm, "Failed to disable queue %d (ret=%d)\n", - queue, ret); -} - -/** - * iwl_mvm_send_lq_cmd() - Send link quality command - * @init: This command is sent as part of station initialization right - * after station has been added. - * - * The link quality command is sent as the last step of station creation. - * This is the special case in which init is set and we call a callback in - * this case to clear the state indicating that station creation is in - * progress. - */ -int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init) -{ - struct iwl_host_cmd cmd = { - .id = LQ_CMD, - .len = { sizeof(struct iwl_lq_cmd), }, - .flags = init ? 0 : CMD_ASYNC, - .data = { lq, }, - }; - - if (WARN_ON(lq->sta_id == IWL_MVM_STATION_COUNT)) - return -EINVAL; - - return iwl_mvm_send_cmd(mvm, &cmd); -} - -/** - * iwl_mvm_update_smps - Get a request to change the SMPS mode - * @req_type: The part of the driver who call for a change. - * @smps_requests: The request to change the SMPS mode. - * - * Get a requst to change the SMPS mode, - * and change it according to all other requests in the driver. - */ -void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - enum iwl_mvm_smps_type_request req_type, - enum ieee80211_smps_mode smps_request) -{ - struct iwl_mvm_vif *mvmvif; - enum ieee80211_smps_mode smps_mode; - int i; - - lockdep_assert_held(&mvm->mutex); - - /* SMPS is irrelevant for NICs that don't have at least 2 RX antenna */ - if (num_of_ant(iwl_mvm_get_valid_rx_ant(mvm)) == 1) - return; - - if (vif->type == NL80211_IFTYPE_AP) - smps_mode = IEEE80211_SMPS_OFF; - else - smps_mode = IEEE80211_SMPS_AUTOMATIC; - - mvmvif = iwl_mvm_vif_from_mac80211(vif); - mvmvif->smps_requests[req_type] = smps_request; - for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) { - if (mvmvif->smps_requests[i] == IEEE80211_SMPS_STATIC) { - smps_mode = IEEE80211_SMPS_STATIC; - break; - } - if (mvmvif->smps_requests[i] == IEEE80211_SMPS_DYNAMIC) - smps_mode = IEEE80211_SMPS_DYNAMIC; - } - - ieee80211_request_smps(vif, smps_mode); -} - -int iwl_mvm_request_statistics(struct iwl_mvm *mvm, bool clear) -{ - struct iwl_statistics_cmd scmd = { - .flags = clear ? cpu_to_le32(IWL_STATISTICS_FLG_CLEAR) : 0, - }; - struct iwl_host_cmd cmd = { - .id = STATISTICS_CMD, - .len[0] = sizeof(scmd), - .data[0] = &scmd, - .flags = CMD_WANT_SKB, - }; - int ret; - - ret = iwl_mvm_send_cmd(mvm, &cmd); - if (ret) - return ret; - - iwl_mvm_handle_rx_statistics(mvm, cmd.resp_pkt); - iwl_free_resp(&cmd); - - if (clear) - iwl_mvm_accu_radio_stats(mvm); - - return 0; -} - -void iwl_mvm_accu_radio_stats(struct iwl_mvm *mvm) -{ - mvm->accu_radio_stats.rx_time += mvm->radio_stats.rx_time; - mvm->accu_radio_stats.tx_time += mvm->radio_stats.tx_time; - mvm->accu_radio_stats.on_time_rf += mvm->radio_stats.on_time_rf; - mvm->accu_radio_stats.on_time_scan += mvm->radio_stats.on_time_scan; -} - -static void iwl_mvm_diversity_iter(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - bool *result = _data; - int i; - - for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) { - if (mvmvif->smps_requests[i] == IEEE80211_SMPS_STATIC || - mvmvif->smps_requests[i] == IEEE80211_SMPS_DYNAMIC) - *result = false; - } -} - -bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm) -{ - bool result = true; - - lockdep_assert_held(&mvm->mutex); - - if (num_of_ant(iwl_mvm_get_valid_rx_ant(mvm)) == 1) - return false; - - if (mvm->cfg->rx_with_siso_diversity) - return false; - - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_diversity_iter, &result); - - return result; -} - -int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - bool value) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int res; - - lockdep_assert_held(&mvm->mutex); - - if (mvmvif->low_latency == value) - return 0; - - mvmvif->low_latency = value; - - res = iwl_mvm_update_quotas(mvm, false, NULL); - if (res) - return res; - - iwl_mvm_bt_coex_vif_change(mvm); - - return iwl_mvm_power_update_mac(mvm); -} - -static void iwl_mvm_ll_iter(void *_data, u8 *mac, struct ieee80211_vif *vif) -{ - bool *result = _data; - - if (iwl_mvm_vif_low_latency(iwl_mvm_vif_from_mac80211(vif))) - *result = true; -} - -bool iwl_mvm_low_latency(struct iwl_mvm *mvm) -{ - bool result = false; - - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_ll_iter, &result); - - return result; -} - -struct iwl_bss_iter_data { - struct ieee80211_vif *vif; - bool error; -}; - -static void iwl_mvm_bss_iface_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_bss_iter_data *data = _data; - - if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) - return; - - if (data->vif) { - data->error = true; - return; - } - - data->vif = vif; -} - -struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm) -{ - struct iwl_bss_iter_data bss_iter_data = {}; - - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_bss_iface_iterator, &bss_iter_data); - - if (bss_iter_data.error) { - IWL_ERR(mvm, "More than one managed interface active!\n"); - return ERR_PTR(-EINVAL); - } - - return bss_iter_data.vif; -} - -unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool tdls, bool cmd_q) -{ - struct iwl_fw_dbg_trigger_tlv *trigger; - struct iwl_fw_dbg_trigger_txq_timer *txq_timer; - unsigned int default_timeout = - cmd_q ? IWL_DEF_WD_TIMEOUT : mvm->cfg->base_params->wd_timeout; - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TXQ_TIMERS)) - return iwlmvm_mod_params.tfd_q_hang_detect ? - default_timeout : IWL_WATCHDOG_DISABLED; - - trigger = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TXQ_TIMERS); - txq_timer = (void *)trigger->data; - - if (tdls) - return le32_to_cpu(txq_timer->tdls); - - if (cmd_q) - return le32_to_cpu(txq_timer->command_queue); - - if (WARN_ON(!vif)) - return default_timeout; - - switch (ieee80211_vif_type_p2p(vif)) { - case NL80211_IFTYPE_ADHOC: - return le32_to_cpu(txq_timer->ibss); - case NL80211_IFTYPE_STATION: - return le32_to_cpu(txq_timer->bss); - case NL80211_IFTYPE_AP: - return le32_to_cpu(txq_timer->softap); - case NL80211_IFTYPE_P2P_CLIENT: - return le32_to_cpu(txq_timer->p2p_client); - case NL80211_IFTYPE_P2P_GO: - return le32_to_cpu(txq_timer->p2p_go); - case NL80211_IFTYPE_P2P_DEVICE: - return le32_to_cpu(txq_timer->p2p_device); - default: - WARN_ON(1); - return mvm->cfg->base_params->wd_timeout; - } -} - -void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - const char *errmsg) -{ - struct iwl_fw_dbg_trigger_tlv *trig; - struct iwl_fw_dbg_trigger_mlme *trig_mlme; - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_MLME)) - goto out; - - trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_MLME); - trig_mlme = (void *)trig->data; - if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig)) - goto out; - - if (trig_mlme->stop_connection_loss && - --trig_mlme->stop_connection_loss) - goto out; - - iwl_mvm_fw_dbg_collect_trig(mvm, trig, "%s", errmsg); - -out: - ieee80211_connection_loss(vif); -} |