diff options
author | John W. Linville <linville@tuxdriver.com> | 2013-03-08 15:53:47 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2013-03-08 15:53:47 -0500 |
commit | cd309ab39351cfd5d0a310bd3d0eb76d0b31f66d (patch) | |
tree | 0a9bccc45ca1a02597ab35caca8c2fe92889f0b2 /drivers | |
parent | 3d5c203272b25c4391397247cdb0059a04fccddf (diff) | |
parent | 25b9ea5c797b5d78f8ceced9ad9c7a7daf0db19c (diff) | |
download | blackbird-op-linux-cd309ab39351cfd5d0a310bd3d0eb76d0b31f66d.tar.gz blackbird-op-linux-cd309ab39351cfd5d0a310bd3d0eb76d0b31f66d.zip |
Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi-next
Diffstat (limited to 'drivers')
85 files changed, 2000 insertions, 696 deletions
diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig index ba319cba3f1e..56c2040a955b 100644 --- a/drivers/net/wireless/iwlwifi/Kconfig +++ b/drivers/net/wireless/iwlwifi/Kconfig @@ -6,7 +6,6 @@ config IWLWIFI select LEDS_CLASS select LEDS_TRIGGERS select MAC80211_LEDS - select IWLDVM ---help--- Select to build the driver supporting the: @@ -45,6 +44,7 @@ config IWLWIFI config IWLDVM tristate "Intel Wireless WiFi DVM Firmware support" depends on IWLWIFI + default IWLWIFI help This is the driver supporting the DVM firmware which is currently the only firmware available for existing devices. @@ -58,6 +58,15 @@ config IWLMVM Say yes if you have such a device. +# don't call it _MODULE -- will confuse Kconfig/fixdep/... +config IWLWIFI_OPMODE_MODULAR + bool + default y if IWLDVM=m + default y if IWLMVM=m + +comment "WARNING: iwlwifi is useless without IWLDVM or IWLMVM" + depends on IWLWIFI && IWLDVM=n && IWLMVM=n + menu "Debugging Options" depends on IWLWIFI diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile index 6c7800044a04..3b5613ea458b 100644 --- a/drivers/net/wireless/iwlwifi/Makefile +++ b/drivers/net/wireless/iwlwifi/Makefile @@ -7,8 +7,7 @@ iwlwifi-objs += iwl-notif-wait.o iwlwifi-objs += iwl-eeprom-read.o iwl-eeprom-parse.o iwlwifi-objs += iwl-phy-db.o iwl-nvm-parse.o iwlwifi-objs += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o -iwlwifi-objs += pcie/1000.o pcie/2000.o pcie/5000.o pcie/6000.o -iwlwifi-objs += pcie/7000.o +iwlwifi-objs += iwl-1000.o iwl-2000.o iwl-5000.o iwl-6000.o iwl-7000.o iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += iwl-test.o diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h index 41ec27cb6efe..019d433900ef 100644 --- a/drivers/net/wireless/iwlwifi/dvm/agn.h +++ b/drivers/net/wireless/iwlwifi/dvm/agn.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/dvm/calib.c b/drivers/net/wireless/iwlwifi/dvm/calib.c index 6468de8634b0..d6c4cf2ad7c5 100644 --- a/drivers/net/wireless/iwlwifi/dvm/calib.c +++ b/drivers/net/wireless/iwlwifi/dvm/calib.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/dvm/calib.h b/drivers/net/wireless/iwlwifi/dvm/calib.h index 65e920cab2b7..cfddde194940 100644 --- a/drivers/net/wireless/iwlwifi/dvm/calib.h +++ b/drivers/net/wireless/iwlwifi/dvm/calib.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/dvm/commands.h b/drivers/net/wireless/iwlwifi/dvm/commands.h index 84e2c0fcfef6..95ca026ecc9d 100644 --- a/drivers/net/wireless/iwlwifi/dvm/commands.h +++ b/drivers/net/wireless/iwlwifi/dvm/commands.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -1526,6 +1526,7 @@ struct iwl_compressed_ba_resp { __le16 scd_ssn; u8 txed; /* number of frames sent */ u8 txed_2_done; /* number of frames acked */ + __le16 reserved1; } __packed; /* diff --git a/drivers/net/wireless/iwlwifi/dvm/debugfs.c b/drivers/net/wireless/iwlwifi/dvm/debugfs.c index 20806cae11b7..7b8178be119f 100644 --- a/drivers/net/wireless/iwlwifi/dvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/dvm/debugfs.c @@ -19,7 +19,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -2324,6 +2324,28 @@ static ssize_t iwl_dbgfs_calib_disabled_write(struct file *file, return count; } +static ssize_t iwl_dbgfs_fw_restart_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + bool restart_fw = iwlwifi_mod_params.restart_fw; + int ret; + + iwlwifi_mod_params.restart_fw = true; + + mutex_lock(&priv->mutex); + + /* take the return value to make compiler happy - it will fail anyway */ + ret = iwl_dvm_send_cmd_pdu(priv, REPLY_ERROR, CMD_SYNC, 0, NULL); + + mutex_unlock(&priv->mutex); + + iwlwifi_mod_params.restart_fw = restart_fw; + + return count; +} + DEBUGFS_READ_FILE_OPS(ucode_rx_stats); DEBUGFS_READ_FILE_OPS(ucode_tx_stats); DEBUGFS_READ_FILE_OPS(ucode_general_stats); @@ -2343,6 +2365,7 @@ DEBUGFS_READ_FILE_OPS(bt_traffic); DEBUGFS_READ_WRITE_FILE_OPS(protection_mode); DEBUGFS_READ_FILE_OPS(reply_tx_error); DEBUGFS_WRITE_FILE_OPS(echo_test); +DEBUGFS_WRITE_FILE_OPS(fw_restart); #ifdef CONFIG_IWLWIFI_DEBUG DEBUGFS_READ_WRITE_FILE_OPS(log_event); #endif @@ -2400,6 +2423,7 @@ int iwl_dbgfs_register(struct iwl_priv *priv, struct dentry *dbgfs_dir) DEBUGFS_ADD_FILE(rxon_flags, dir_debug, S_IWUSR); DEBUGFS_ADD_FILE(rxon_filter_flags, dir_debug, S_IWUSR); DEBUGFS_ADD_FILE(echo_test, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(fw_restart, dir_debug, S_IWUSR); #ifdef CONFIG_IWLWIFI_DEBUG DEBUGFS_ADD_FILE(log_event, dir_debug, S_IWUSR | S_IRUSR); #endif diff --git a/drivers/net/wireless/iwlwifi/dvm/lib.c b/drivers/net/wireless/iwlwifi/dvm/lib.c index 86ea5f4c3939..cddf77c36b36 100644 --- a/drivers/net/wireless/iwlwifi/dvm/lib.c +++ b/drivers/net/wireless/iwlwifi/dvm/lib.c @@ -19,7 +19,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/dvm/scan.c b/drivers/net/wireless/iwlwifi/dvm/scan.c index 3a4aa5239c45..d69b55866714 100644 --- a/drivers/net/wireless/iwlwifi/dvm/scan.c +++ b/drivers/net/wireless/iwlwifi/dvm/scan.c @@ -19,7 +19,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/dvm/sta.c b/drivers/net/wireless/iwlwifi/dvm/sta.c index 94ef33838bc6..b775769f8322 100644 --- a/drivers/net/wireless/iwlwifi/dvm/sta.c +++ b/drivers/net/wireless/iwlwifi/dvm/sta.c @@ -151,7 +151,7 @@ int iwl_send_add_sta(struct iwl_priv *priv, sta_id, sta->sta.addr, flags & CMD_ASYNC ? "a" : ""); if (!(flags & CMD_ASYNC)) { - cmd.flags |= CMD_WANT_SKB | CMD_WANT_HCMD; + cmd.flags |= CMD_WANT_SKB; might_sleep(); } diff --git a/drivers/net/wireless/iwlwifi/dvm/testmode.c b/drivers/net/wireless/iwlwifi/dvm/testmode.c index dc6f965a123a..b89b9d9b9969 100644 --- a/drivers/net/wireless/iwlwifi/dvm/testmode.c +++ b/drivers/net/wireless/iwlwifi/dvm/testmode.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index d499a0366fa6..cc1e0c1a6f48 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -19,7 +19,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c index 736fe9bb140e..166019afc2d0 100644 --- a/drivers/net/wireless/iwlwifi/dvm/ucode.c +++ b/drivers/net/wireless/iwlwifi/dvm/ucode.c @@ -19,7 +19,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/pcie/1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c index ff3389757281..c080ae3070b2 100644 --- a/drivers/net/wireless/iwlwifi/pcie/1000.c +++ b/drivers/net/wireless/iwlwifi/iwl-1000.c @@ -29,7 +29,6 @@ #include "iwl-config.h" #include "iwl-csr.h" #include "iwl-agn-hw.h" -#include "cfg.h" /* Highest firmware API version supported */ #define IWL1000_UCODE_API_MAX 5 diff --git a/drivers/net/wireless/iwlwifi/pcie/2000.c b/drivers/net/wireless/iwlwifi/iwl-2000.c index e7de33128b16..a6ddd2f9fba0 100644 --- a/drivers/net/wireless/iwlwifi/pcie/2000.c +++ b/drivers/net/wireless/iwlwifi/iwl-2000.c @@ -28,7 +28,6 @@ #include <linux/stringify.h> #include "iwl-config.h" #include "iwl-agn-hw.h" -#include "cfg.h" #include "dvm/commands.h" /* needed for BT for now */ /* Highest firmware API version supported */ diff --git a/drivers/net/wireless/iwlwifi/pcie/5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 5096f7c96ab6..403f3f224bf6 100644 --- a/drivers/net/wireless/iwlwifi/pcie/5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -29,7 +29,6 @@ #include "iwl-config.h" #include "iwl-agn-hw.h" #include "iwl-csr.h" -#include "cfg.h" /* Highest firmware API version supported */ #define IWL5000_UCODE_API_MAX 5 diff --git a/drivers/net/wireless/iwlwifi/pcie/6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c index 801ff49796dd..b5ab8d1bcac0 100644 --- a/drivers/net/wireless/iwlwifi/pcie/6000.c +++ b/drivers/net/wireless/iwlwifi/iwl-6000.c @@ -28,7 +28,6 @@ #include <linux/stringify.h> #include "iwl-config.h" #include "iwl-agn-hw.h" -#include "cfg.h" #include "dvm/commands.h" /* needed for BT for now */ /* Highest firmware API version supported */ diff --git a/drivers/net/wireless/iwlwifi/pcie/7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c index 6e35b2b72332..50263e87fe15 100644 --- a/drivers/net/wireless/iwlwifi/pcie/7000.c +++ b/drivers/net/wireless/iwlwifi/iwl-7000.c @@ -1,34 +1,70 @@ /****************************************************************************** * - * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. * - * 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 + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. + * 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 + * 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. + * 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 - 2013 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * *****************************************************************************/ #include <linux/module.h> #include <linux/stringify.h> #include "iwl-config.h" #include "iwl-agn-hw.h" -#include "cfg.h" /* Highest firmware API version supported */ #define IWL7260_UCODE_API_MAX 6 @@ -70,7 +106,6 @@ static const struct iwl_base_params iwl7000_base_params = { }; static const struct iwl_ht_params iwl7000_ht_params = { - .ht_greenfield_support = true, .use_rts_for_aggregation = true, /* use rts/cts protection */ .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), }; diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-hw.h b/drivers/net/wireless/iwlwifi/iwl-agn-hw.h index e9975c54c276..6d73f943cefa 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-hw.h +++ b/drivers/net/wireless/iwlwifi/iwl-agn-hw.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index 743b48343358..c38aa8f77554 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -275,4 +275,51 @@ struct iwl_cfg { const bool temp_offset_v2; }; +/* + * This list declares the config structures for all devices. + */ +extern const struct iwl_cfg iwl5300_agn_cfg; +extern const struct iwl_cfg iwl5100_agn_cfg; +extern const struct iwl_cfg iwl5350_agn_cfg; +extern const struct iwl_cfg iwl5100_bgn_cfg; +extern const struct iwl_cfg iwl5100_abg_cfg; +extern const struct iwl_cfg iwl5150_agn_cfg; +extern const struct iwl_cfg iwl5150_abg_cfg; +extern const struct iwl_cfg iwl6005_2agn_cfg; +extern const struct iwl_cfg iwl6005_2abg_cfg; +extern const struct iwl_cfg iwl6005_2bg_cfg; +extern const struct iwl_cfg iwl6005_2agn_sff_cfg; +extern const struct iwl_cfg iwl6005_2agn_d_cfg; +extern const struct iwl_cfg iwl6005_2agn_mow1_cfg; +extern const struct iwl_cfg iwl6005_2agn_mow2_cfg; +extern const struct iwl_cfg iwl1030_bgn_cfg; +extern const struct iwl_cfg iwl1030_bg_cfg; +extern const struct iwl_cfg iwl6030_2agn_cfg; +extern const struct iwl_cfg iwl6030_2abg_cfg; +extern const struct iwl_cfg iwl6030_2bgn_cfg; +extern const struct iwl_cfg iwl6030_2bg_cfg; +extern const struct iwl_cfg iwl6000i_2agn_cfg; +extern const struct iwl_cfg iwl6000i_2abg_cfg; +extern const struct iwl_cfg iwl6000i_2bg_cfg; +extern const struct iwl_cfg iwl6000_3agn_cfg; +extern const struct iwl_cfg iwl6050_2agn_cfg; +extern const struct iwl_cfg iwl6050_2abg_cfg; +extern const struct iwl_cfg iwl6150_bgn_cfg; +extern const struct iwl_cfg iwl6150_bg_cfg; +extern const struct iwl_cfg iwl1000_bgn_cfg; +extern const struct iwl_cfg iwl1000_bg_cfg; +extern const struct iwl_cfg iwl100_bgn_cfg; +extern const struct iwl_cfg iwl100_bg_cfg; +extern const struct iwl_cfg iwl130_bgn_cfg; +extern const struct iwl_cfg iwl130_bg_cfg; +extern const struct iwl_cfg iwl2000_2bgn_cfg; +extern const struct iwl_cfg iwl2000_2bgn_d_cfg; +extern const struct iwl_cfg iwl2030_2bgn_cfg; +extern const struct iwl_cfg iwl6035_2agn_cfg; +extern const struct iwl_cfg iwl105_bgn_cfg; +extern const struct iwl_cfg iwl105_bgn_d_cfg; +extern const struct iwl_cfg iwl135_bgn_cfg; +extern const struct iwl_cfg iwl7260_2ac_cfg; +extern const struct iwl_cfg iwl3160_ac_cfg; + #endif /* __IWL_CONFIG_H__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h index df3463a38704..20e845d4da04 100644 --- a/drivers/net/wireless/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/iwlwifi/iwl-csr.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.c b/drivers/net/wireless/iwlwifi/iwl-debug.c index 87535a67de76..8a44f594528d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debug.c +++ b/drivers/net/wireless/iwlwifi/iwl-debug.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -66,6 +66,7 @@ #include <linux/device.h> #include <linux/interrupt.h> #include <linux/export.h> +#include "iwl-drv.h" #include "iwl-debug.h" #include "iwl-devtrace.h" @@ -85,11 +86,11 @@ void __iwl_ ##fn(struct device *dev, const char *fmt, ...) \ } __iwl_fn(warn) -EXPORT_SYMBOL_GPL(__iwl_warn); +IWL_EXPORT_SYMBOL(__iwl_warn); __iwl_fn(info) -EXPORT_SYMBOL_GPL(__iwl_info); +IWL_EXPORT_SYMBOL(__iwl_info); __iwl_fn(crit) -EXPORT_SYMBOL_GPL(__iwl_crit); +IWL_EXPORT_SYMBOL(__iwl_crit); void __iwl_err(struct device *dev, bool rfkill_prefix, bool trace_only, const char *fmt, ...) @@ -110,7 +111,7 @@ void __iwl_err(struct device *dev, bool rfkill_prefix, bool trace_only, trace_iwlwifi_err(&vaf); va_end(args); } -EXPORT_SYMBOL_GPL(__iwl_err); +IWL_EXPORT_SYMBOL(__iwl_err); #if defined(CONFIG_IWLWIFI_DEBUG) || defined(CONFIG_IWLWIFI_DEVICE_TRACING) void __iwl_dbg(struct device *dev, @@ -133,5 +134,5 @@ void __iwl_dbg(struct device *dev, trace_iwlwifi_dbg(level, in_interrupt(), function, &vaf); va_end(args); } -EXPORT_SYMBOL_GPL(__iwl_dbg); +IWL_EXPORT_SYMBOL(__iwl_dbg); #endif diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/iwlwifi/iwl-devtrace.h index 9a0f45ec9e01..4491c1c72cc7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace.h +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.h @@ -298,7 +298,7 @@ TRACE_EVENT(iwlwifi_dbg, MAX_MSG_LEN, vaf->fmt, *vaf->va) >= MAX_MSG_LEN); ), - TP_printk("%s", (char *)__get_dynamic_array(msg)) + TP_printk("%s", __get_str(msg)) ); #undef TRACE_SYSTEM @@ -349,25 +349,23 @@ TRACE_EVENT(iwlwifi_dev_rx_data, TRACE_EVENT(iwlwifi_dev_hcmd, TP_PROTO(const struct device *dev, struct iwl_host_cmd *cmd, u16 total_size, - const void *hdr, size_t hdr_len), - TP_ARGS(dev, cmd, total_size, hdr, hdr_len), + struct iwl_cmd_header *hdr), + TP_ARGS(dev, cmd, total_size, hdr), TP_STRUCT__entry( DEV_ENTRY __dynamic_array(u8, hcmd, total_size) __field(u32, flags) ), TP_fast_assign( - int i, offset = hdr_len; + int i, offset = sizeof(*hdr); DEV_ASSIGN; __entry->flags = cmd->flags; - memcpy(__get_dynamic_array(hcmd), hdr, hdr_len); + memcpy(__get_dynamic_array(hcmd), hdr, sizeof(*hdr)); - for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { + for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { if (!cmd->len[i]) continue; - if (!(cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY)) - continue; memcpy((u8 *)__get_dynamic_array(hcmd) + offset, cmd->data[i], cmd->len[i]); offset += cmd->len[i]; diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index 6f228bb2b844..3ce4e9d5082d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -1102,8 +1102,7 @@ void iwl_drv_stop(struct iwl_drv *drv) /* shared module parameters */ struct iwl_mod_params iwlwifi_mod_params = { - .amsdu_size_8K = 1, - .restart_fw = 1, + .restart_fw = true, .plcp_check = true, .bt_coex_active = true, .power_level = IWL_POWER_INDEX_1, @@ -1112,7 +1111,7 @@ struct iwl_mod_params iwlwifi_mod_params = { .wd_disable = true, /* the rest are 0 by default */ }; -EXPORT_SYMBOL_GPL(iwlwifi_mod_params); +IWL_EXPORT_SYMBOL(iwlwifi_mod_params); int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops) { @@ -1136,7 +1135,7 @@ int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops) mutex_unlock(&iwlwifi_opmode_table_mtx); return -EIO; } -EXPORT_SYMBOL_GPL(iwl_opmode_register); +IWL_EXPORT_SYMBOL(iwl_opmode_register); void iwl_opmode_deregister(const char *name) { @@ -1158,7 +1157,7 @@ void iwl_opmode_deregister(const char *name) } mutex_unlock(&iwlwifi_opmode_table_mtx); } -EXPORT_SYMBOL_GPL(iwl_opmode_deregister); +IWL_EXPORT_SYMBOL(iwl_opmode_deregister); static int __init iwl_drv_init(void) { @@ -1207,9 +1206,9 @@ MODULE_PARM_DESC(11n_disable, "disable 11n functionality, bitmap: 1: full, 2: agg TX, 4: agg RX"); module_param_named(amsdu_size_8K, iwlwifi_mod_params.amsdu_size_8K, int, S_IRUGO); -MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size"); -module_param_named(fw_restart, iwlwifi_mod_params.restart_fw, int, S_IRUGO); -MODULE_PARM_DESC(fw_restart, "restart firmware in case of error"); +MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size (default 0)"); +module_param_named(fw_restart, iwlwifi_mod_params.restart_fw, bool, S_IRUGO); +MODULE_PARM_DESC(fw_restart, "restart firmware in case of error (default true)"); module_param_named(antenna_coupling, iwlwifi_mod_params.ant_coupling, int, S_IRUGO); diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.h b/drivers/net/wireless/iwlwifi/iwl-drv.h index 594a5c71b272..7d1450916308 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.h +++ b/drivers/net/wireless/iwlwifi/iwl-drv.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -63,6 +63,8 @@ #ifndef __iwl_drv_h__ #define __iwl_drv_h__ +#include <linux/module.h> + /* for all modules */ #define DRV_NAME "iwlwifi" #define IWLWIFI_VERSION "in-tree:" @@ -123,4 +125,17 @@ struct iwl_drv *iwl_drv_start(struct iwl_trans *trans, */ void iwl_drv_stop(struct iwl_drv *drv); +/* + * exported symbol management + * + * The driver can be split into multiple modules, in which case some symbols + * must be exported for the sub-modules. However, if it's not split and + * everything is built-in, then we can avoid that. + */ +#ifdef CONFIG_IWLWIFI_OPMODE_MODULAR +#define IWL_EXPORT_SYMBOL(sym) EXPORT_SYMBOL_GPL(sym) +#else +#define IWL_EXPORT_SYMBOL(sym) +#endif + #endif /* __iwl_drv_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c index 034f2ff4f43d..600c9fdd7f71 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -62,6 +62,7 @@ #include <linux/types.h> #include <linux/slab.h> #include <linux/export.h> +#include "iwl-drv.h" #include "iwl-modparams.h" #include "iwl-eeprom-parse.h" @@ -749,7 +750,7 @@ void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg, } ht_info->ht_supported = true; - ht_info->cap = 0; + ht_info->cap = IEEE80211_HT_CAP_DSSSCCK40; if (iwlwifi_mod_params.amsdu_size_8K) ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; @@ -909,7 +910,7 @@ iwl_parse_eeprom_data(struct device *dev, const struct iwl_cfg *cfg, kfree(data); return NULL; } -EXPORT_SYMBOL_GPL(iwl_parse_eeprom_data); +IWL_EXPORT_SYMBOL(iwl_parse_eeprom_data); /* helper functions */ int iwl_nvm_check_version(struct iwl_nvm_data *data, @@ -928,4 +929,4 @@ int iwl_nvm_check_version(struct iwl_nvm_data *data, data->calib_version, trans->cfg->nvm_calib_ver); return -EINVAL; } -EXPORT_SYMBOL_GPL(iwl_nvm_check_version); +IWL_EXPORT_SYMBOL(iwl_nvm_check_version); diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h index 683fe6a8c58f..37f115390b19 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c index ef4806f27cf8..e5f2e362ab0b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -63,6 +63,7 @@ #include <linux/slab.h> #include <linux/export.h> +#include "iwl-drv.h" #include "iwl-debug.h" #include "iwl-eeprom-read.h" #include "iwl-io.h" @@ -460,4 +461,4 @@ int iwl_read_eeprom(struct iwl_trans *trans, u8 **eeprom, size_t *eeprom_size) return ret; } -EXPORT_SYMBOL_GPL(iwl_read_eeprom); +IWL_EXPORT_SYMBOL(iwl_read_eeprom); diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.h b/drivers/net/wireless/iwlwifi/iwl-eeprom-read.h index b2588c5cbf93..8e941f8bd7d6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.h +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-read.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/iwl-fh.h b/drivers/net/wireless/iwlwifi/iwl-fh.h index f5592fb3b1ed..484d318245fb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fh.h +++ b/drivers/net/wireless/iwlwifi/iwl-fh.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h index 90873eca35f7..8b6c6fd95ed0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index b545178e46e3..435618574240 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -73,12 +73,14 @@ * treats good CRC threshold as a boolean * @IWL_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w). * @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P. + * @IWL_UCODE_TLV_FLAGS_DW_BC_TABLE: The SCD byte count table is in DWORDS */ enum iwl_ucode_tlv_flag { IWL_UCODE_TLV_FLAGS_PAN = BIT(0), IWL_UCODE_TLV_FLAGS_NEWSCAN = BIT(1), IWL_UCODE_TLV_FLAGS_MFP = BIT(2), IWL_UCODE_TLV_FLAGS_P2P = BIT(3), + IWL_UCODE_TLV_FLAGS_DW_BC_TABLE = BIT(4), }; /* The default calibrate table size if not specified by firmware file */ diff --git a/drivers/net/wireless/iwlwifi/iwl-io.c b/drivers/net/wireless/iwlwifi/iwl-io.c index 276410d82de4..305c81f2c2b4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/iwlwifi/iwl-io.c @@ -29,6 +29,7 @@ #include <linux/device.h> #include <linux/export.h> +#include "iwl-drv.h" #include "iwl-io.h" #include "iwl-csr.h" #include "iwl-debug.h" @@ -49,7 +50,7 @@ int iwl_poll_bit(struct iwl_trans *trans, u32 addr, return -ETIMEDOUT; } -EXPORT_SYMBOL_GPL(iwl_poll_bit); +IWL_EXPORT_SYMBOL(iwl_poll_bit); u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg) { @@ -62,7 +63,7 @@ u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg) return value; } -EXPORT_SYMBOL_GPL(iwl_read_direct32); +IWL_EXPORT_SYMBOL(iwl_read_direct32); void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value) { @@ -73,7 +74,7 @@ void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value) iwl_trans_release_nic_access(trans, &flags); } } -EXPORT_SYMBOL_GPL(iwl_write_direct32); +IWL_EXPORT_SYMBOL(iwl_write_direct32); int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask, int timeout) @@ -89,7 +90,7 @@ int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask, return -ETIMEDOUT; } -EXPORT_SYMBOL_GPL(iwl_poll_direct_bit); +IWL_EXPORT_SYMBOL(iwl_poll_direct_bit); static inline u32 __iwl_read_prph(struct iwl_trans *trans, u32 ofs) { @@ -115,7 +116,7 @@ u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs) } return val; } -EXPORT_SYMBOL_GPL(iwl_read_prph); +IWL_EXPORT_SYMBOL(iwl_read_prph); void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val) { @@ -126,7 +127,7 @@ void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val) iwl_trans_release_nic_access(trans, &flags); } } -EXPORT_SYMBOL_GPL(iwl_write_prph); +IWL_EXPORT_SYMBOL(iwl_write_prph); void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask) { @@ -138,7 +139,7 @@ void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask) iwl_trans_release_nic_access(trans, &flags); } } -EXPORT_SYMBOL_GPL(iwl_set_bits_prph); +IWL_EXPORT_SYMBOL(iwl_set_bits_prph); void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs, u32 bits, u32 mask) @@ -151,7 +152,7 @@ void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs, iwl_trans_release_nic_access(trans, &flags); } } -EXPORT_SYMBOL_GPL(iwl_set_bits_mask_prph); +IWL_EXPORT_SYMBOL(iwl_set_bits_mask_prph); void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask) { @@ -164,4 +165,4 @@ void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask) iwl_trans_release_nic_access(trans, &flags); } } -EXPORT_SYMBOL_GPL(iwl_clear_bits_prph); +IWL_EXPORT_SYMBOL(iwl_clear_bits_prph); diff --git a/drivers/net/wireless/iwlwifi/iwl-modparams.h b/drivers/net/wireless/iwlwifi/iwl-modparams.h index e5e3a79eae2f..3cc39ffe8ba5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/iwlwifi/iwl-modparams.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -91,7 +91,7 @@ enum iwl_power_level { * @sw_crypto: using hardware encryption, default = 0 * @disable_11n: disable 11n capabilities, default = 0, * use IWL_DISABLE_HT_* constants - * @amsdu_size_8K: enable 8K amsdu size, default = 1 + * @amsdu_size_8K: enable 8K amsdu size, default = 0 * @restart_fw: restart firmware, default = 1 * @plcp_check: enable plcp health check, default = true * @wd_disable: enable stuck queue check, default = 0 @@ -109,7 +109,7 @@ struct iwl_mod_params { int sw_crypto; unsigned int disable_11n; int amsdu_size_8K; - int restart_fw; + bool restart_fw; bool plcp_check; int wd_disable; bool bt_coex_active; diff --git a/drivers/net/wireless/iwlwifi/iwl-notif-wait.c b/drivers/net/wireless/iwlwifi/iwl-notif-wait.c index c3affbc62cdf..940b8a9d5285 100644 --- a/drivers/net/wireless/iwlwifi/iwl-notif-wait.c +++ b/drivers/net/wireless/iwlwifi/iwl-notif-wait.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -63,6 +63,7 @@ #include <linux/sched.h> #include <linux/export.h> +#include "iwl-drv.h" #include "iwl-notif-wait.h" @@ -72,7 +73,7 @@ void iwl_notification_wait_init(struct iwl_notif_wait_data *notif_wait) INIT_LIST_HEAD(¬if_wait->notif_waits); init_waitqueue_head(¬if_wait->notif_waitq); } -EXPORT_SYMBOL_GPL(iwl_notification_wait_init); +IWL_EXPORT_SYMBOL(iwl_notification_wait_init); void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_wait, struct iwl_rx_packet *pkt) @@ -117,7 +118,7 @@ void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_wait, if (triggered) wake_up_all(¬if_wait->notif_waitq); } -EXPORT_SYMBOL_GPL(iwl_notification_wait_notify); +IWL_EXPORT_SYMBOL(iwl_notification_wait_notify); void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_wait) { @@ -130,7 +131,7 @@ void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_wait) wake_up_all(¬if_wait->notif_waitq); } -EXPORT_SYMBOL_GPL(iwl_abort_notification_waits); +IWL_EXPORT_SYMBOL(iwl_abort_notification_waits); void iwl_init_notification_wait(struct iwl_notif_wait_data *notif_wait, @@ -154,7 +155,7 @@ iwl_init_notification_wait(struct iwl_notif_wait_data *notif_wait, list_add(&wait_entry->list, ¬if_wait->notif_waits); spin_unlock_bh(¬if_wait->notif_wait_lock); } -EXPORT_SYMBOL_GPL(iwl_init_notification_wait); +IWL_EXPORT_SYMBOL(iwl_init_notification_wait); int iwl_wait_notification(struct iwl_notif_wait_data *notif_wait, struct iwl_notification_wait *wait_entry, @@ -178,7 +179,7 @@ int iwl_wait_notification(struct iwl_notif_wait_data *notif_wait, return -ETIMEDOUT; return 0; } -EXPORT_SYMBOL_GPL(iwl_wait_notification); +IWL_EXPORT_SYMBOL(iwl_wait_notification); void iwl_remove_notification(struct iwl_notif_wait_data *notif_wait, struct iwl_notification_wait *wait_entry) @@ -187,4 +188,4 @@ void iwl_remove_notification(struct iwl_notif_wait_data *notif_wait, list_del(&wait_entry->list); spin_unlock_bh(¬if_wait->notif_wait_lock); } -EXPORT_SYMBOL_GPL(iwl_remove_notification); +IWL_EXPORT_SYMBOL(iwl_remove_notification); diff --git a/drivers/net/wireless/iwlwifi/iwl-notif-wait.h b/drivers/net/wireless/iwlwifi/iwl-notif-wait.h index c2ce764463a3..2e2f1c8c99f9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-notif-wait.h +++ b/drivers/net/wireless/iwlwifi/iwl-notif-wait.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index a70213bdb83c..6199a0a597a6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -62,6 +62,7 @@ #include <linux/types.h> #include <linux/slab.h> #include <linux/export.h> +#include "iwl-drv.h" #include "iwl-modparams.h" #include "iwl-nvm-parse.h" @@ -149,6 +150,8 @@ static struct ieee80211_rate iwl_cfg80211_rates[] = { * @NVM_CHANNEL_DFS: dynamic freq selection candidate * @NVM_CHANNEL_WIDE: 20 MHz channel okay (?) * @NVM_CHANNEL_40MHZ: 40 MHz channel okay (?) + * @NVM_CHANNEL_80MHZ: 80 MHz channel okay (?) + * @NVM_CHANNEL_160MHZ: 160 MHz channel okay (?) */ enum iwl_nvm_channel_flags { NVM_CHANNEL_VALID = BIT(0), @@ -158,6 +161,8 @@ enum iwl_nvm_channel_flags { NVM_CHANNEL_DFS = BIT(7), NVM_CHANNEL_WIDE = BIT(8), NVM_CHANNEL_40MHZ = BIT(9), + NVM_CHANNEL_80MHZ = BIT(10), + NVM_CHANNEL_160MHZ = BIT(11), }; #define CHECK_AND_PRINT_I(x) \ @@ -210,6 +215,10 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, else channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; } + if (!(ch_flags & NVM_CHANNEL_80MHZ)) + channel->flags |= IEEE80211_CHAN_NO_80MHZ; + if (!(ch_flags & NVM_CHANNEL_160MHZ)) + channel->flags |= IEEE80211_CHAN_NO_160MHZ; if (!(ch_flags & NVM_CHANNEL_IBSS)) channel->flags |= IEEE80211_CHAN_NO_IBSS; @@ -245,6 +254,43 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, return n_channels; } +static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, + struct iwl_nvm_data *data, + struct ieee80211_sta_vht_cap *vht_cap) +{ + /* For now, assume new devices with NVM are VHT capable */ + + vht_cap->vht_supported = true; + + vht_cap->cap = IEEE80211_VHT_CAP_SHORT_GI_80 | + IEEE80211_VHT_CAP_RXSTBC_1 | + IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | + 7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; + + if (iwlwifi_mod_params.amsdu_size_8K) + vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991; + + vht_cap->vht_mcs.rx_mcs_map = + cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 14); + + if (data->valid_rx_ant == 1 || cfg->rx_with_siso_diversity) { + vht_cap->cap |= IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN | + IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN; + /* this works because NOT_SUPPORTED == 3 */ + vht_cap->vht_mcs.rx_mcs_map |= + cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << 2); + } + + vht_cap->vht_mcs.tx_mcs_map = vht_cap->vht_mcs.rx_mcs_map; +} + static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, struct iwl_nvm_data *data, const __le16 *nvm_sw) { @@ -268,6 +314,7 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, n_used += iwl_init_sband_channels(data, sband, n_channels, IEEE80211_BAND_5GHZ); iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ); + iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap); if (n_channels != n_used) IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n", @@ -343,4 +390,4 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, return data; } -EXPORT_SYMBOL_GPL(iwl_parse_nvm_data); +IWL_EXPORT_SYMBOL(iwl_parse_nvm_data); diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h index b2692bd287fa..e57fb989661e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/iwlwifi/iwl-op-mode.h index 4a680019e117..98c7aa7346da 100644 --- a/drivers/net/wireless/iwlwifi/iwl-op-mode.h +++ b/drivers/net/wireless/iwlwifi/iwl-op-mode.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.c b/drivers/net/wireless/iwlwifi/iwl-phy-db.c index 14fc8d39fc28..25745daa0d5d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-phy-db.c +++ b/drivers/net/wireless/iwlwifi/iwl-phy-db.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -65,6 +65,7 @@ #include <linux/string.h> #include <linux/export.h> +#include "iwl-drv.h" #include "iwl-phy-db.h" #include "iwl-debug.h" #include "iwl-op-mode.h" @@ -136,12 +137,6 @@ struct iwl_calib_res_notif_phy_db { u8 data[]; } __packed; -#define IWL_PHY_DB_STATIC_PIC cpu_to_le32(0x21436587) -static inline void iwl_phy_db_test_pic(__le32 pic) -{ - WARN_ON(IWL_PHY_DB_STATIC_PIC != pic); -} - struct iwl_phy_db *iwl_phy_db_init(struct iwl_trans *trans) { struct iwl_phy_db *phy_db = kzalloc(sizeof(struct iwl_phy_db), @@ -155,7 +150,7 @@ struct iwl_phy_db *iwl_phy_db_init(struct iwl_trans *trans) /* TODO: add default values of the phy db. */ return phy_db; } -EXPORT_SYMBOL(iwl_phy_db_init); +IWL_EXPORT_SYMBOL(iwl_phy_db_init); /* * get phy db section: returns a pointer to a phy db section specified by @@ -221,7 +216,7 @@ void iwl_phy_db_free(struct iwl_phy_db *phy_db) kfree(phy_db); } -EXPORT_SYMBOL(iwl_phy_db_free); +IWL_EXPORT_SYMBOL(iwl_phy_db_free); int iwl_phy_db_set_section(struct iwl_phy_db *phy_db, struct iwl_rx_packet *pkt, gfp_t alloc_ctx) @@ -260,18 +255,13 @@ int iwl_phy_db_set_section(struct iwl_phy_db *phy_db, struct iwl_rx_packet *pkt, (size - CHANNEL_NUM_SIZE) / phy_db->channel_num; } - /* Test PIC */ - if (type != IWL_PHY_DB_CFG) - iwl_phy_db_test_pic(*(((__le32 *)phy_db_notif->data) + - (size / sizeof(__le32)) - 1)); - IWL_DEBUG_INFO(phy_db->trans, "%s(%d): [PHYDB]SET: Type %d , Size: %d\n", __func__, __LINE__, type, size); return 0; } -EXPORT_SYMBOL(iwl_phy_db_set_section); +IWL_EXPORT_SYMBOL(iwl_phy_db_set_section); static int is_valid_channel(u16 ch_id) { @@ -372,11 +362,6 @@ int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db, *size = entry->size; } - /* Test PIC */ - if (type != IWL_PHY_DB_CFG) - iwl_phy_db_test_pic(*(((__le32 *)*data) + - (*size / sizeof(__le32)) - 1)); - IWL_DEBUG_INFO(phy_db->trans, "%s(%d): [PHYDB] GET: Type %d , Size: %d\n", __func__, __LINE__, type, *size); @@ -511,4 +496,4 @@ int iwl_send_phy_db_data(struct iwl_phy_db *phy_db) "Finished sending phy db non channel data\n"); return 0; } -EXPORT_SYMBOL(iwl_send_phy_db_data); +IWL_EXPORT_SYMBOL(iwl_send_phy_db_data); diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.h b/drivers/net/wireless/iwlwifi/iwl-phy-db.h index d0e43d96ab38..ce983af79644 100644 --- a/drivers/net/wireless/iwlwifi/iwl-phy-db.h +++ b/drivers/net/wireless/iwlwifi/iwl-phy-db.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index f76e9cad7757..386f2a7c87cb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/iwl-test.c b/drivers/net/wireless/iwlwifi/iwl-test.c index ce0c67b425ee..efff2986b5b4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-test.c +++ b/drivers/net/wireless/iwlwifi/iwl-test.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -64,6 +64,7 @@ #include <linux/export.h> #include <net/netlink.h> +#include "iwl-drv.h" #include "iwl-io.h" #include "iwl-fh.h" #include "iwl-prph.h" @@ -653,7 +654,7 @@ int iwl_test_parse(struct iwl_test *tst, struct nlattr **tb, } return 0; } -EXPORT_SYMBOL_GPL(iwl_test_parse); +IWL_EXPORT_SYMBOL(iwl_test_parse); /* * Handle test commands. @@ -715,7 +716,7 @@ int iwl_test_handle_cmd(struct iwl_test *tst, struct nlattr **tb) } return result; } -EXPORT_SYMBOL_GPL(iwl_test_handle_cmd); +IWL_EXPORT_SYMBOL(iwl_test_handle_cmd); static int iwl_test_trace_dump(struct iwl_test *tst, struct sk_buff *skb, struct netlink_callback *cb) @@ -803,7 +804,7 @@ int iwl_test_dump(struct iwl_test *tst, u32 cmd, struct sk_buff *skb, } return result; } -EXPORT_SYMBOL_GPL(iwl_test_dump); +IWL_EXPORT_SYMBOL(iwl_test_dump); /* * Multicast a spontaneous messages from the device to the user space. @@ -849,4 +850,4 @@ void iwl_test_rx(struct iwl_test *tst, struct iwl_rx_cmd_buffer *rxb) if (tst->notify) iwl_test_send_rx(tst, rxb); } -EXPORT_SYMBOL_GPL(iwl_test_rx); +IWL_EXPORT_SYMBOL(iwl_test_rx); diff --git a/drivers/net/wireless/iwlwifi/iwl-test.h b/drivers/net/wireless/iwlwifi/iwl-test.h index 7fbf4d717caa..8fbd21704840 100644 --- a/drivers/net/wireless/iwlwifi/iwl-test.h +++ b/drivers/net/wireless/iwlwifi/iwl-test.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/iwl-testmode.h b/drivers/net/wireless/iwlwifi/iwl-testmode.h index a963f45c6849..98f48a9afc98 100644 --- a/drivers/net/wireless/iwlwifi/iwl-testmode.h +++ b/drivers/net/wireless/iwlwifi/iwl-testmode.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 00bdc5b00af3..7f9c254292a8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -183,19 +183,13 @@ struct iwl_rx_packet { * @CMD_ASYNC: Return right away and don't want for the response * @CMD_WANT_SKB: valid only with CMD_SYNC. The caller needs the buffer of the * response. The caller needs to call iwl_free_resp when done. - * @CMD_WANT_HCMD: The caller needs to get the HCMD that was sent in the - * response handler. Chunks flagged by %IWL_HCMD_DFL_NOCOPY won't be - * copied. The pointer passed to the response handler is in the transport - * ownership and don't need to be freed by the op_mode. This also means - * that the pointer is invalidated after the op_mode's handler returns. * @CMD_ON_DEMAND: This command is sent by the test mode pipe. */ enum CMD_MODE { CMD_SYNC = 0, CMD_ASYNC = BIT(0), CMD_WANT_SKB = BIT(1), - CMD_WANT_HCMD = BIT(2), - CMD_ON_DEMAND = BIT(3), + CMD_ON_DEMAND = BIT(2), }; #define DEF_CMD_PAYLOAD_SIZE 320 @@ -214,7 +208,11 @@ struct iwl_device_cmd { #define TFD_MAX_PAYLOAD_SIZE (sizeof(struct iwl_device_cmd)) -#define IWL_MAX_CMD_TFDS 2 +/* + * number of transfer buffers (fragments) per transmit frame descriptor; + * this is just the driver's idea, the hardware supports 20 + */ +#define IWL_MAX_CMD_TBS_PER_TFD 2 /** * struct iwl_hcmd_dataflag - flag for each one of the chunks of the command @@ -251,15 +249,15 @@ enum iwl_hcmd_dataflag { * @id: id of the host command */ struct iwl_host_cmd { - const void *data[IWL_MAX_CMD_TFDS]; + const void *data[IWL_MAX_CMD_TBS_PER_TFD]; struct iwl_rx_packet *resp_pkt; unsigned long _rx_page_addr; u32 _rx_page_order; int handler_status; u32 flags; - u16 len[IWL_MAX_CMD_TFDS]; - u8 dataflags[IWL_MAX_CMD_TFDS]; + u16 len[IWL_MAX_CMD_TBS_PER_TFD]; + u8 dataflags[IWL_MAX_CMD_TBS_PER_TFD]; u8 id; }; diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile index 807b250ec396..2acc44b40986 100644 --- a/drivers/net/wireless/iwlwifi/mvm/Makefile +++ b/drivers/net/wireless/iwlwifi/mvm/Makefile @@ -2,7 +2,7 @@ obj-$(CONFIG_IWLMVM) += iwlmvm.o iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o iwlmvm-y += scan.o time-event.o rs.o -iwlmvm-y += power.o +iwlmvm-y += power.o bt-coex.o iwlmvm-y += led.o iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o iwlmvm-$(CONFIG_PM_SLEEP) += d3.o diff --git a/drivers/net/wireless/iwlwifi/mvm/binding.c b/drivers/net/wireless/iwlwifi/mvm/binding.c index 73d24aacb90a..93fd1457954b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/binding.c +++ b/drivers/net/wireless/iwlwifi/mvm/binding.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c new file mode 100644 index 000000000000..47954deb6493 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c @@ -0,0 +1,347 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2013 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2013 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include "fw-api-bt-coex.h" +#include "iwl-modparams.h" +#include "mvm.h" +#include "iwl-debug.h" + +#define EVENT_PRIO_ANT(_evt, _prio, _shrd_ant) \ + [(_evt)] = (((_prio) << BT_COEX_PRIO_TBL_PRIO_POS) | \ + ((_shrd_ant) << BT_COEX_PRIO_TBL_SHRD_ANT_POS)) + +static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = { + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB1, + BT_COEX_PRIO_TBL_PRIO_BYPASS, 0), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB2, + BT_COEX_PRIO_TBL_PRIO_BYPASS, 1), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1, + BT_COEX_PRIO_TBL_PRIO_LOW, 0), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2, + BT_COEX_PRIO_TBL_PRIO_LOW, 1), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1, + BT_COEX_PRIO_TBL_PRIO_HIGH, 0), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2, + BT_COEX_PRIO_TBL_PRIO_HIGH, 1), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_DTIM, + BT_COEX_PRIO_TBL_DISABLED, 0), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN52, + BT_COEX_PRIO_TBL_PRIO_COEX_OFF, 0), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN24, + BT_COEX_PRIO_TBL_PRIO_COEX_ON, 0), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_IDLE, + BT_COEX_PRIO_TBL_PRIO_COEX_IDLE, 0), + 0, 0, 0, 0, 0, 0, +}; + +#undef EVENT_PRIO_ANT + +int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm) +{ + return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, CMD_SYNC, + sizeof(struct iwl_bt_coex_prio_tbl_cmd), + &iwl_bt_prio_tbl); +} + +static int iwl_send_bt_env(struct iwl_mvm *mvm, u8 action, u8 type) +{ + struct iwl_bt_coex_prot_env_cmd env_cmd; + int ret; + + env_cmd.action = action; + env_cmd.type = type; + ret = iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PROT_ENV, CMD_SYNC, + sizeof(env_cmd), &env_cmd); + if (ret) + IWL_ERR(mvm, "failed to send BT env command\n"); + return ret; +} + +enum iwl_bt_kill_msk { + BT_KILL_MSK_DEFAULT, + BT_KILL_MSK_SCO_HID_A2DP, + BT_KILL_MSK_REDUCED_TXPOW, + BT_KILL_MSK_MAX, +}; + +static const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX] = { + 0xffffffff, + 0xfffffc00, + 0, +}; + +static const u32 iwl_bt_cts_kill_msk[BT_KILL_MSK_MAX] = { + 0xffffffff, + 0xfffffc00, + 0, +}; + +#define IWL_BT_DEFAULT_BOOST (0xf0f0f0f0) + +/* Tight Coex */ +static const __le32 iwl_tight_lookup[BT_COEX_LUT_SIZE] = { + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaeaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xcc00ff28), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xcc00aaaa), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xc0004000), + cpu_to_le32(0x00000000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xf0005000), +}; + +/* Loose Coex */ +static const __le32 iwl_loose_lookup[BT_COEX_LUT_SIZE] = { + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaeaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xcc00ff28), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xcc00aaaa), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xf0005000), +}; + +/* Full concurrency */ +static const __le32 iwl_concurrent_lookup[BT_COEX_LUT_SIZE] = { + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), +}; + +/* BT Antenna Coupling Threshold (dB) */ +#define IWL_BT_ANTENNA_COUPLING_THRESHOLD (35) + +int iwl_send_bt_init_conf(struct iwl_mvm *mvm) +{ + struct iwl_bt_coex_cmd cmd = { + .max_kill = 5, + .bt3_time_t7_value = 1, + .bt3_prio_sample_time = 2, + .bt3_timer_t2_value = 0xc, + }; + int ret; + + cmd.flags = iwlwifi_mod_params.bt_coex_active ? + BT_COEX_NW : BT_COEX_DISABLE; + cmd.flags |= iwlwifi_mod_params.bt_ch_announce ? + BT_CH_PRIMARY_EN | BT_CH_SECONDARY_EN : 0; + cmd.flags |= BT_SYNC_2_BT_DISABLE; + + cmd.valid_bit_msk = cpu_to_le16(BT_VALID_ENABLE | + BT_VALID_BT_PRIO_BOOST | + BT_VALID_MAX_KILL | + BT_VALID_3W_TMRS | + BT_VALID_KILL_ACK | + BT_VALID_KILL_CTS | + BT_VALID_REDUCED_TX_POWER | + BT_VALID_LUT); + + if (iwlwifi_mod_params.ant_coupling > IWL_BT_ANTENNA_COUPLING_THRESHOLD) + memcpy(&cmd.decision_lut, iwl_loose_lookup, + sizeof(iwl_tight_lookup)); + else + memcpy(&cmd.decision_lut, iwl_tight_lookup, + sizeof(iwl_tight_lookup)); + + cmd.bt_prio_boost = cpu_to_le32(IWL_BT_DEFAULT_BOOST); + cmd.kill_ack_msk = + cpu_to_le32(iwl_bt_ack_kill_msk[BT_KILL_MSK_DEFAULT]); + cmd.kill_cts_msk = + cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]); + + /* go to CALIB state in internal BT-Coex state machine */ + ret = iwl_send_bt_env(mvm, BT_COEX_ENV_OPEN, + BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); + if (ret) + return ret; + + ret = iwl_send_bt_env(mvm, BT_COEX_ENV_CLOSE, + BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); + if (ret) + return ret; + + return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_SYNC, + sizeof(cmd), &cmd); +} + +struct iwl_bt_notif_iterator_data { + struct iwl_mvm *mvm; + struct iwl_bt_coex_profile_notif *notif; +}; + +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_notif_iterator_data *data = _data; + struct ieee80211_chanctx_conf *chanctx_conf; + enum ieee80211_smps_mode smps_mode; + enum ieee80211_band band; + + if (vif->type != NL80211_IFTYPE_STATION) + return; + + rcu_read_lock(); + chanctx_conf = rcu_dereference(vif->chanctx_conf); + if (chanctx_conf && chanctx_conf->def.chan) + band = chanctx_conf->def.chan->band; + else + band = -1; + rcu_read_unlock(); + + if (band != IEEE80211_BAND_2GHZ) + return; + + smps_mode = IEEE80211_SMPS_AUTOMATIC; + + if (data->notif->bt_status) + smps_mode = IEEE80211_SMPS_DYNAMIC; + + if (data->notif->bt_traffic_load) + smps_mode = IEEE80211_SMPS_STATIC; + + IWL_DEBUG_COEX(data->mvm, + "mac %d: bt_status %d traffic_load %d smps_req %d\n", + mvmvif->id, data->notif->bt_status, + data->notif->bt_traffic_load, smps_mode); + + ieee80211_request_smps(vif, smps_mode); +} + +int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *dev_cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data; + struct iwl_bt_notif_iterator_data data = { + .mvm = mvm, + .notif = notif, + }; + struct iwl_bt_coex_cmd cmd = {}; + enum iwl_bt_kill_msk bt_kill_msk; + + IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n"); + IWL_DEBUG_COEX(mvm, "\tBT %salive\n", notif->bt_status ? "" : "not "); + IWL_DEBUG_COEX(mvm, "\tBT open conn %d\n", notif->bt_open_conn); + IWL_DEBUG_COEX(mvm, "\tBT traffic load %d\n", notif->bt_traffic_load); + IWL_DEBUG_COEX(mvm, "\tBT agg traffic load %d\n", + notif->bt_agg_traffic_load); + IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance); + + /* remember this notification for future use: rssi fluctuations */ + memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif)); + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_bt_notif_iterator, &data); + + /* Low latency BT profile is active: give higher prio to BT */ + if (BT_MBOX_MSG(notif, 3, SCO_STATE) || + BT_MBOX_MSG(notif, 3, A2DP_STATE) || + BT_MBOX_MSG(notif, 3, SNIFF_STATE)) + bt_kill_msk = BT_KILL_MSK_SCO_HID_A2DP; + else + bt_kill_msk = BT_KILL_MSK_DEFAULT; + + /* Don't send HCMD if there is no update */ + if (bt_kill_msk == mvm->bt_kill_msk) + return 0; + + IWL_DEBUG_COEX(mvm, + "Udpate kill_msk: %d\n\t SCO %sactive A2DP %sactive SNIFF %sactive\n", + bt_kill_msk, + BT_MBOX_MSG(notif, 3, SCO_STATE) ? "" : "in", + BT_MBOX_MSG(notif, 3, A2DP_STATE) ? "" : "in", + BT_MBOX_MSG(notif, 3, SNIFF_STATE) ? "" : "in"); + + mvm->bt_kill_msk = bt_kill_msk; + cmd.kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]); + cmd.kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]); + + cmd.valid_bit_msk = cpu_to_le16(BT_VALID_KILL_ACK | BT_VALID_KILL_CTS); + + if (iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_SYNC, sizeof(cmd), &cmd)) + IWL_ERR(mvm, "Failed to sent BT Coex CMD\n"); + + /* This handler is ASYNC */ + return 0; +} diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index c64d864799cd..d4578cefe445 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -61,8 +61,11 @@ * *****************************************************************************/ +#include <linux/etherdevice.h> +#include <linux/ip.h> #include <net/cfg80211.h> #include <net/ipv6.h> +#include <net/tcp.h> #include "iwl-modparams.h" #include "fw-api.h" #include "mvm.h" @@ -192,6 +195,11 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw, 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; } @@ -304,9 +312,13 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw, */ if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) { key->hw_key_idx = 0; + mvm->ptk_ivlen = key->iv_len; + mvm->ptk_icvlen = key->icv_len; } else { data->gtk_key_idx++; key->hw_key_idx = data->gtk_key_idx; + mvm->gtk_ivlen = key->iv_len; + mvm->gtk_icvlen = key->icv_len; } ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, true); @@ -392,6 +404,233 @@ static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, sizeof(cmd), &cmd); } +enum iwl_mvm_tcp_packet_type { + MVM_TCP_TX_SYN, + MVM_TCP_RX_SYNACK, + MVM_TCP_TX_DATA, + MVM_TCP_RX_ACK, + MVM_TCP_RX_WAKE, + MVM_TCP_TX_FIN, +}; + +static __le16 pseudo_hdr_check(int len, __be32 saddr, __be32 daddr) +{ + __sum16 check = tcp_v4_check(len, saddr, daddr, 0); + return cpu_to_le16(be16_to_cpu((__force __be16)check)); +} + +static void iwl_mvm_build_tcp_packet(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct cfg80211_wowlan_tcp *tcp, + void *_pkt, u8 *mask, + __le16 *pseudo_hdr_csum, + enum iwl_mvm_tcp_packet_type ptype) +{ + struct { + struct ethhdr eth; + struct iphdr ip; + struct tcphdr tcp; + u8 data[]; + } __packed *pkt = _pkt; + u16 ip_tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr); + int i; + + pkt->eth.h_proto = cpu_to_be16(ETH_P_IP), + pkt->ip.version = 4; + pkt->ip.ihl = 5; + pkt->ip.protocol = IPPROTO_TCP; + + switch (ptype) { + case MVM_TCP_TX_SYN: + case MVM_TCP_TX_DATA: + case MVM_TCP_TX_FIN: + memcpy(pkt->eth.h_dest, tcp->dst_mac, ETH_ALEN); + memcpy(pkt->eth.h_source, vif->addr, ETH_ALEN); + pkt->ip.ttl = 128; + pkt->ip.saddr = tcp->src; + pkt->ip.daddr = tcp->dst; + pkt->tcp.source = cpu_to_be16(tcp->src_port); + pkt->tcp.dest = cpu_to_be16(tcp->dst_port); + /* overwritten for TX SYN later */ + pkt->tcp.doff = sizeof(struct tcphdr) / 4; + pkt->tcp.window = cpu_to_be16(65000); + break; + case MVM_TCP_RX_SYNACK: + case MVM_TCP_RX_ACK: + case MVM_TCP_RX_WAKE: + memcpy(pkt->eth.h_dest, vif->addr, ETH_ALEN); + memcpy(pkt->eth.h_source, tcp->dst_mac, ETH_ALEN); + pkt->ip.saddr = tcp->dst; + pkt->ip.daddr = tcp->src; + pkt->tcp.source = cpu_to_be16(tcp->dst_port); + pkt->tcp.dest = cpu_to_be16(tcp->src_port); + break; + default: + WARN_ON(1); + return; + } + + switch (ptype) { + case MVM_TCP_TX_SYN: + /* firmware assumes 8 option bytes - 8 NOPs for now */ + memset(pkt->data, 0x01, 8); + ip_tot_len += 8; + pkt->tcp.doff = (sizeof(struct tcphdr) + 8) / 4; + pkt->tcp.syn = 1; + break; + case MVM_TCP_TX_DATA: + ip_tot_len += tcp->payload_len; + memcpy(pkt->data, tcp->payload, tcp->payload_len); + pkt->tcp.psh = 1; + pkt->tcp.ack = 1; + break; + case MVM_TCP_TX_FIN: + pkt->tcp.fin = 1; + pkt->tcp.ack = 1; + break; + case MVM_TCP_RX_SYNACK: + pkt->tcp.syn = 1; + pkt->tcp.ack = 1; + break; + case MVM_TCP_RX_ACK: + pkt->tcp.ack = 1; + break; + case MVM_TCP_RX_WAKE: + ip_tot_len += tcp->wake_len; + pkt->tcp.psh = 1; + pkt->tcp.ack = 1; + memcpy(pkt->data, tcp->wake_data, tcp->wake_len); + break; + } + + switch (ptype) { + case MVM_TCP_TX_SYN: + case MVM_TCP_TX_DATA: + case MVM_TCP_TX_FIN: + pkt->ip.tot_len = cpu_to_be16(ip_tot_len); + pkt->ip.check = ip_fast_csum(&pkt->ip, pkt->ip.ihl); + break; + case MVM_TCP_RX_WAKE: + for (i = 0; i < DIV_ROUND_UP(tcp->wake_len, 8); i++) { + u8 tmp = tcp->wake_mask[i]; + mask[i + 6] |= tmp << 6; + if (i + 1 < DIV_ROUND_UP(tcp->wake_len, 8)) + mask[i + 7] = tmp >> 2; + } + /* fall through for ethernet/IP/TCP headers mask */ + case MVM_TCP_RX_SYNACK: + case MVM_TCP_RX_ACK: + mask[0] = 0xff; /* match ethernet */ + /* + * match ethernet, ip.version, ip.ihl + * the ip.ihl half byte is really masked out by firmware + */ + mask[1] = 0x7f; + mask[2] = 0x80; /* match ip.protocol */ + mask[3] = 0xfc; /* match ip.saddr, ip.daddr */ + mask[4] = 0x3f; /* match ip.daddr, tcp.source, tcp.dest */ + mask[5] = 0x80; /* match tcp flags */ + /* leave rest (0 or set for MVM_TCP_RX_WAKE) */ + break; + }; + + *pseudo_hdr_csum = pseudo_hdr_check(ip_tot_len - sizeof(struct iphdr), + pkt->ip.saddr, pkt->ip.daddr); +} + +static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct cfg80211_wowlan_tcp *tcp) +{ + struct iwl_wowlan_remote_wake_config *cfg; + struct iwl_host_cmd cmd = { + .id = REMOTE_WAKE_CONFIG_CMD, + .len = { sizeof(*cfg), }, + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, + .flags = CMD_SYNC, + }; + int ret; + + if (!tcp) + return 0; + + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) + return -ENOMEM; + cmd.data[0] = cfg; + + cfg->max_syn_retries = 10; + cfg->max_data_retries = 10; + cfg->tcp_syn_ack_timeout = 1; /* seconds */ + cfg->tcp_ack_timeout = 1; /* seconds */ + + /* SYN (TX) */ + iwl_mvm_build_tcp_packet( + mvm, vif, tcp, cfg->syn_tx.data, NULL, + &cfg->syn_tx.info.tcp_pseudo_header_checksum, + MVM_TCP_TX_SYN); + cfg->syn_tx.info.tcp_payload_length = 0; + + /* SYN/ACK (RX) */ + iwl_mvm_build_tcp_packet( + mvm, vif, tcp, cfg->synack_rx.data, cfg->synack_rx.rx_mask, + &cfg->synack_rx.info.tcp_pseudo_header_checksum, + MVM_TCP_RX_SYNACK); + cfg->synack_rx.info.tcp_payload_length = 0; + + /* KEEPALIVE/ACK (TX) */ + iwl_mvm_build_tcp_packet( + mvm, vif, tcp, cfg->keepalive_tx.data, NULL, + &cfg->keepalive_tx.info.tcp_pseudo_header_checksum, + MVM_TCP_TX_DATA); + cfg->keepalive_tx.info.tcp_payload_length = + cpu_to_le16(tcp->payload_len); + cfg->sequence_number_offset = tcp->payload_seq.offset; + /* length must be 0..4, the field is little endian */ + cfg->sequence_number_length = tcp->payload_seq.len; + cfg->initial_sequence_number = cpu_to_le32(tcp->payload_seq.start); + cfg->keepalive_interval = cpu_to_le16(tcp->data_interval); + if (tcp->payload_tok.len) { + cfg->token_offset = tcp->payload_tok.offset; + cfg->token_length = tcp->payload_tok.len; + cfg->num_tokens = + cpu_to_le16(tcp->tokens_size % tcp->payload_tok.len); + memcpy(cfg->tokens, tcp->payload_tok.token_stream, + tcp->tokens_size); + } else { + /* set tokens to max value to almost never run out */ + cfg->num_tokens = cpu_to_le16(65535); + } + + /* ACK (RX) */ + iwl_mvm_build_tcp_packet( + mvm, vif, tcp, cfg->keepalive_ack_rx.data, + cfg->keepalive_ack_rx.rx_mask, + &cfg->keepalive_ack_rx.info.tcp_pseudo_header_checksum, + MVM_TCP_RX_ACK); + cfg->keepalive_ack_rx.info.tcp_payload_length = 0; + + /* WAKEUP (RX) */ + iwl_mvm_build_tcp_packet( + mvm, vif, tcp, cfg->wake_rx.data, cfg->wake_rx.rx_mask, + &cfg->wake_rx.info.tcp_pseudo_header_checksum, + MVM_TCP_RX_WAKE); + cfg->wake_rx.info.tcp_payload_length = + cpu_to_le16(tcp->wake_len); + + /* FIN */ + iwl_mvm_build_tcp_packet( + mvm, vif, tcp, cfg->fin_tx.data, NULL, + &cfg->fin_tx.info.tcp_pseudo_header_checksum, + MVM_TCP_TX_FIN); + cfg->fin_tx.info.tcp_payload_length = 0; + + ret = iwl_mvm_send_cmd(mvm, &cmd); + kfree(cfg); + + return ret; +} + struct iwl_d3_iter_data { struct iwl_mvm *mvm; struct ieee80211_vif *vif; @@ -630,6 +869,22 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) d3_cfg_cmd.wakeup_flags |= cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT); + if (wowlan->tcp) { + /* + * The firmware currently doesn't really look at these, only + * the IWL_WOWLAN_WAKEUP_LINK_CHANGE bit. We have to set that + * reason bit since losing the connection to the AP implies + * losing the TCP connection. + * Set the flags anyway as long as they exist, in case this + * will be changed in the firmware. + */ + wowlan_config_cmd.wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS | + IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE | + IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET | + IWL_WOWLAN_WAKEUP_LINK_CHANGE); + } + iwl_mvm_cancel_scan(mvm); iwl_trans_stop_device(mvm->trans); @@ -649,6 +904,11 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) /* We reprogram keys and shouldn't allocate new key indices */ memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table)); + mvm->ptk_ivlen = 0; + mvm->ptk_icvlen = 0; + mvm->ptk_ivlen = 0; + mvm->ptk_icvlen = 0; + /* * The D3 firmware still hardcodes the AP station ID for the * BSS we're associated with as 0. As a result, we have to move @@ -740,6 +1000,10 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) if (ret) goto out; + ret = iwl_mvm_send_remote_wake_cfg(mvm, vif, wowlan->tcp); + if (ret) + goto out; + /* must be last -- this switches firmware state */ ret = iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, CMD_SYNC, sizeof(d3_cfg_cmd), &d3_cfg_cmd); @@ -783,7 +1047,6 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, struct iwl_wowlan_status *status; u32 reasons; int ret, len; - bool pkt8023 = false; struct sk_buff *pkt = NULL; iwl_trans_read_mem_bytes(mvm->trans, base, @@ -824,7 +1087,8 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, status = (void *)cmd.resp_pkt->data; if (len - sizeof(struct iwl_cmd_header) != - sizeof(*status) + le32_to_cpu(status->wake_packet_bufsize)) { + sizeof(*status) + + ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4)) { IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); goto out; } @@ -836,61 +1100,105 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, goto report; } - if (reasons & IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET) { + if (reasons & IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET) wakeup.magic_pkt = true; - pkt8023 = true; - } - if (reasons & IWL_WOWLAN_WAKEUP_BY_PATTERN) { + if (reasons & IWL_WOWLAN_WAKEUP_BY_PATTERN) wakeup.pattern_idx = le16_to_cpu(status->pattern_number); - pkt8023 = true; - } 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) { + if (reasons & IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE) wakeup.gtk_rekey_failure = true; - pkt8023 = true; - } - if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED) { + if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED) wakeup.rfkill_release = true; - pkt8023 = true; - } - if (reasons & IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST) { + if (reasons & IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST) wakeup.eap_identity_req = true; - pkt8023 = true; - } - if (reasons & IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE) { + if (reasons & IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE) wakeup.four_way_handshake = true; - pkt8023 = 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) { - u32 pktsize = le32_to_cpu(status->wake_packet_bufsize); - u32 pktlen = le32_to_cpu(status->wake_packet_length); + int pktsize = le32_to_cpu(status->wake_packet_bufsize); + int pktlen = le32_to_cpu(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 */ - if (pkt8023) { pkt = alloc_skb(pktsize, GFP_KERNEL); if (!pkt) goto report; - memcpy(skb_put(pkt, pktsize), status->wake_packet, - pktsize); + + memcpy(skb_put(pkt, hdrlen), pktdata, hdrlen); + pktdata += hdrlen; + pktsize -= hdrlen; + + if (ieee80211_has_protected(hdr->frame_control)) { + 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 - (pktlen - pktsize); + 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; + wakeup.packet_len = pktlen - truncated; wakeup.packet_80211 = true; } } diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index c1bdb5582126..b080b4ba5458 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -69,12 +69,6 @@ struct iwl_dbgfs_mvm_ctx { struct ieee80211_vif *vif; }; -static int iwl_dbgfs_open_file_generic(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - static ssize_t iwl_dbgfs_tx_flush_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) @@ -306,10 +300,130 @@ static ssize_t iwl_dbgfs_power_down_d3_allow_write(struct file *file, return 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 ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif; + char *buf; + int ret, pos = 0, bufsz = sizeof(char) * 1024; + + buf = kmalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&mvm->mutex); + + pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw0:\n"); + + BT_MBOX_PRINT(0, LE_SLAVE_LAT, false); + BT_MBOX_PRINT(0, LE_PROF1, false); + BT_MBOX_PRINT(0, LE_PROF2, false); + BT_MBOX_PRINT(0, LE_PROF_OTHER, false); + BT_MBOX_PRINT(0, CHL_SEQ_N, false); + BT_MBOX_PRINT(0, INBAND_S, false); + BT_MBOX_PRINT(0, LE_MIN_RSSI, false); + BT_MBOX_PRINT(0, LE_SCAN, false); + BT_MBOX_PRINT(0, LE_ADV, false); + BT_MBOX_PRINT(0, LE_MAX_TX_POWER, false); + BT_MBOX_PRINT(0, OPEN_CON_1, true); + + pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw1:\n"); + + BT_MBOX_PRINT(1, BR_MAX_TX_POWER, false); + BT_MBOX_PRINT(1, IP_SR, false); + BT_MBOX_PRINT(1, LE_MSTR, false); + BT_MBOX_PRINT(1, AGGR_TRFC_LD, false); + BT_MBOX_PRINT(1, MSG_TYPE, false); + BT_MBOX_PRINT(1, SSN, true); + + pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw2:\n"); + + BT_MBOX_PRINT(2, SNIFF_ACT, false); + BT_MBOX_PRINT(2, PAG, false); + BT_MBOX_PRINT(2, INQUIRY, false); + BT_MBOX_PRINT(2, CONN, false); + BT_MBOX_PRINT(2, SNIFF_INTERVAL, false); + BT_MBOX_PRINT(2, DISC, false); + BT_MBOX_PRINT(2, SCO_TX_ACT, false); + BT_MBOX_PRINT(2, SCO_RX_ACT, false); + BT_MBOX_PRINT(2, ESCO_RE_TX, false); + BT_MBOX_PRINT(2, SCO_DURATION, true); + + pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw3:\n"); + + BT_MBOX_PRINT(3, SCO_STATE, false); + BT_MBOX_PRINT(3, SNIFF_STATE, false); + BT_MBOX_PRINT(3, A2DP_STATE, false); + BT_MBOX_PRINT(3, ACL_STATE, false); + BT_MBOX_PRINT(3, MSTR_STATE, false); + BT_MBOX_PRINT(3, OBX_STATE, false); + BT_MBOX_PRINT(3, OPEN_CON_2, false); + BT_MBOX_PRINT(3, TRAFFIC_LOAD, false); + BT_MBOX_PRINT(3, CHL_SEQN_LSB, false); + BT_MBOX_PRINT(3, INBAND_P, false); + BT_MBOX_PRINT(3, MSG_TYPE_2, false); + BT_MBOX_PRINT(3, SSN_2, false); + BT_MBOX_PRINT(3, UPDATE_REQUEST, true); + + pos += scnprintf(buf+pos, bufsz-pos, "bt_status = %d\n", + notif->bt_status); + pos += scnprintf(buf+pos, bufsz-pos, "bt_open_conn = %d\n", + notif->bt_open_conn); + pos += scnprintf(buf+pos, bufsz-pos, "bt_traffic_load = %d\n", + notif->bt_traffic_load); + pos += scnprintf(buf+pos, bufsz-pos, "bt_agg_traffic_load = %d\n", + notif->bt_agg_traffic_load); + pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n", + notif->bt_ci_compliance); + + mutex_unlock(&mvm->mutex); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + + return ret; +} +#undef BT_MBOX_PRINT + +static ssize_t iwl_dbgfs_fw_restart_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + bool restart_fw = iwlwifi_mod_params.restart_fw; + int ret; + + iwlwifi_mod_params.restart_fw = true; + + mutex_lock(&mvm->mutex); + + /* take the return value to make compiler happy - it will fail anyway */ + ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_ERROR, CMD_SYNC, 0, NULL); + + mutex_unlock(&mvm->mutex); + + iwlwifi_mod_params.restart_fw = restart_fw; + + return count; +} + #define MVM_DEBUGFS_READ_FILE_OPS(name) \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .read = iwl_dbgfs_##name##_read, \ - .open = iwl_dbgfs_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ } @@ -317,14 +431,14 @@ static const struct file_operations iwl_dbgfs_##name##_ops = { \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .write = iwl_dbgfs_##name##_write, \ .read = iwl_dbgfs_##name##_read, \ - .open = iwl_dbgfs_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ }; #define MVM_DEBUGFS_WRITE_FILE_OPS(name) \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .write = iwl_dbgfs_##name##_write, \ - .open = iwl_dbgfs_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ }; @@ -345,8 +459,10 @@ MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush); MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain); MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram); MVM_DEBUGFS_READ_FILE_OPS(stations); +MVM_DEBUGFS_READ_FILE_OPS(bt_notif); MVM_DEBUGFS_WRITE_FILE_OPS(power_down_allow); MVM_DEBUGFS_WRITE_FILE_OPS(power_down_d3_allow); +MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart); int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) { @@ -358,8 +474,10 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) MVM_DEBUGFS_ADD_FILE(sta_drain, mvm->debugfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(sram, mvm->debugfs_dir, S_IWUSR | S_IRUSR); MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, S_IRUSR); + MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, S_IRUSR); MVM_DEBUGFS_ADD_FILE(power_down_allow, mvm->debugfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(power_down_d3_allow, mvm->debugfs_dir, S_IWUSR); + MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR); /* * Create a symlink with mac80211. It will be removed when mac80211 diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h new file mode 100644 index 000000000000..05c61d6f384e --- /dev/null +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h @@ -0,0 +1,319 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2013 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2013 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __fw_api_bt_coex_h__ +#define __fw_api_bt_coex_h__ + +#include <linux/types.h> +#include <linux/bitops.h> + +#define BITS(nb) (BIT(nb) - 1) + +/** + * enum iwl_bt_coex_flags - flags for BT_COEX command + * @BT_CH_PRIMARY_EN: + * @BT_CH_SECONDARY_EN: + * @BT_NOTIF_COEX_OFF: + * @BT_COEX_MODE_POS: + * @BT_COEX_MODE_MSK: + * @BT_COEX_DISABLE: + * @BT_COEX_2W: + * @BT_COEX_3W: + * @BT_COEX_NW: + * @BT_USE_DEFAULTS: + * @BT_SYNC_2_BT_DISABLE: + * @BT_COEX_CORUNNING_TBL_EN: + */ +enum iwl_bt_coex_flags { + BT_CH_PRIMARY_EN = BIT(0), + BT_CH_SECONDARY_EN = BIT(1), + BT_NOTIF_COEX_OFF = BIT(2), + BT_COEX_MODE_POS = 3, + BT_COEX_MODE_MSK = BITS(3) << BT_COEX_MODE_POS, + BT_COEX_DISABLE = 0x0 << BT_COEX_MODE_POS, + BT_COEX_2W = 0x1 << BT_COEX_MODE_POS, + BT_COEX_3W = 0x2 << BT_COEX_MODE_POS, + BT_COEX_NW = 0x3 << BT_COEX_MODE_POS, + BT_USE_DEFAULTS = BIT(6), + BT_SYNC_2_BT_DISABLE = BIT(7), + /* + * For future use - when the flags will be enlarged + * BT_COEX_CORUNNING_TBL_EN = BIT(8), + */ +}; + +/* + * indicates what has changed in the BT_COEX command. + */ +enum iwl_bt_coex_valid_bit_msk { + BT_VALID_ENABLE = BIT(0), + BT_VALID_BT_PRIO_BOOST = BIT(1), + BT_VALID_MAX_KILL = BIT(2), + BT_VALID_3W_TMRS = BIT(3), + BT_VALID_KILL_ACK = BIT(4), + BT_VALID_KILL_CTS = BIT(5), + BT_VALID_REDUCED_TX_POWER = BIT(6), + BT_VALID_LUT = BIT(7), + BT_VALID_WIFI_RX_SW_PRIO_BOOST = BIT(8), + BT_VALID_WIFI_TX_SW_PRIO_BOOST = BIT(9), + BT_VALID_MULTI_PRIO_LUT = BIT(10), + BT_VALID_TRM_KICK_FILTER = BIT(11), + BT_VALID_CORUN_LUT_20 = BIT(12), + BT_VALID_CORUN_LUT_40 = BIT(13), + BT_VALID_ANT_ISOLATION = BIT(14), + BT_VALID_ANT_ISOLATION_THRS = BIT(15), + /* + * For future use - when the valid flags will be enlarged + * BT_VALID_TXTX_DELTA_FREQ_THRS = BIT(16), + * BT_VALID_TXRX_MAX_FREQ_0 = BIT(17), + */ +}; + +/** + * enum iwl_bt_reduced_tx_power - allows to reduce txpower for WiFi frames. + * @BT_REDUCED_TX_POWER_CTL: reduce Tx power for control frames + * @BT_REDUCED_TX_POWER_DATA: reduce Tx power for data frames + * + * This mechanism allows to have BT and WiFi run concurrently. Since WiFi + * reduces its Tx power, it can work along with BT, hence reducing the amount + * of WiFi frames being killed by BT. + */ +enum iwl_bt_reduced_tx_power { + BT_REDUCED_TX_POWER_CTL = BIT(0), + BT_REDUCED_TX_POWER_DATA = BIT(1), +}; + +#define BT_COEX_LUT_SIZE (12) + +/** + * struct iwl_bt_coex_cmd - bt coex configuration command + * @flags:&enum iwl_bt_coex_flags + * @lead_time: + * @max_kill: + * @bt3_time_t7_value: + * @kill_ack_msk: + * @kill_cts_msk: + * @bt3_prio_sample_time: + * @bt3_timer_t2_value: + * @bt4_reaction_time: + * @decision_lut[12]: + * @bt_reduced_tx_power: enum %iwl_bt_reduced_tx_power + * @valid_bit_msk: enum %iwl_bt_coex_valid_bit_msk + * @bt_prio_boost: values for PTA boost register + * @wifi_tx_prio_boost: SW boost of wifi tx priority + * @wifi_rx_prio_boost: SW boost of wifi rx priority + * + * The structure is used for the BT_COEX command. + */ +struct iwl_bt_coex_cmd { + u8 flags; + u8 lead_time; + u8 max_kill; + u8 bt3_time_t7_value; + __le32 kill_ack_msk; + __le32 kill_cts_msk; + u8 bt3_prio_sample_time; + u8 bt3_timer_t2_value; + __le16 bt4_reaction_time; + __le32 decision_lut[BT_COEX_LUT_SIZE]; + u8 bt_reduced_tx_power; + u8 reserved; + __le16 valid_bit_msk; + __le32 bt_prio_boost; + u8 reserved2; + u8 wifi_tx_prio_boost; + __le16 wifi_rx_prio_boost; +} __packed; /* BT_COEX_CMD_API_S_VER_3 */ + +#define BT_MBOX(n_dw, _msg, _pos, _nbits) \ + BT_MBOX##n_dw##_##_msg##_POS = (_pos), \ + BT_MBOX##n_dw##_##_msg = BITS(_nbits) << BT_MBOX##n_dw##_##_msg##_POS + +enum iwl_bt_mxbox_dw0 { + BT_MBOX(0, LE_SLAVE_LAT, 0, 3), + BT_MBOX(0, LE_PROF1, 3, 1), + BT_MBOX(0, LE_PROF2, 4, 1), + BT_MBOX(0, LE_PROF_OTHER, 5, 1), + BT_MBOX(0, CHL_SEQ_N, 8, 4), + BT_MBOX(0, INBAND_S, 13, 1), + BT_MBOX(0, LE_MIN_RSSI, 16, 4), + BT_MBOX(0, LE_SCAN, 20, 1), + BT_MBOX(0, LE_ADV, 21, 1), + BT_MBOX(0, LE_MAX_TX_POWER, 24, 4), + BT_MBOX(0, OPEN_CON_1, 28, 2), +}; + +enum iwl_bt_mxbox_dw1 { + BT_MBOX(1, BR_MAX_TX_POWER, 0, 4), + BT_MBOX(1, IP_SR, 4, 1), + BT_MBOX(1, LE_MSTR, 5, 1), + BT_MBOX(1, AGGR_TRFC_LD, 8, 6), + BT_MBOX(1, MSG_TYPE, 16, 3), + BT_MBOX(1, SSN, 19, 2), +}; + +enum iwl_bt_mxbox_dw2 { + BT_MBOX(2, SNIFF_ACT, 0, 3), + BT_MBOX(2, PAG, 3, 1), + BT_MBOX(2, INQUIRY, 4, 1), + BT_MBOX(2, CONN, 5, 1), + BT_MBOX(2, SNIFF_INTERVAL, 8, 5), + BT_MBOX(2, DISC, 13, 1), + BT_MBOX(2, SCO_TX_ACT, 16, 2), + BT_MBOX(2, SCO_RX_ACT, 18, 2), + BT_MBOX(2, ESCO_RE_TX, 20, 2), + BT_MBOX(2, SCO_DURATION, 24, 6), +}; + +enum iwl_bt_mxbox_dw3 { + BT_MBOX(3, SCO_STATE, 0, 1), + BT_MBOX(3, SNIFF_STATE, 1, 1), + BT_MBOX(3, A2DP_STATE, 2, 1), + BT_MBOX(3, ACL_STATE, 3, 1), + BT_MBOX(3, MSTR_STATE, 4, 1), + BT_MBOX(3, OBX_STATE, 5, 1), + BT_MBOX(3, OPEN_CON_2, 8, 2), + BT_MBOX(3, TRAFFIC_LOAD, 10, 2), + BT_MBOX(3, CHL_SEQN_LSB, 12, 1), + BT_MBOX(3, INBAND_P, 13, 1), + BT_MBOX(3, MSG_TYPE_2, 16, 3), + BT_MBOX(3, SSN_2, 19, 2), + BT_MBOX(3, UPDATE_REQUEST, 21, 1), +}; + +#define BT_MBOX_MSG(_notif, _num, _field) \ + ((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\ + >> BT_MBOX##_num##_##_field##_POS) + +/** + * struct iwl_bt_coex_profile_notif - notification about BT coex + * @mbox_msg: message from BT to WiFi + * @:bt_status: 0 - off, 1 - on + * @:bt_open_conn: number of BT connections open + * @:bt_traffic_load: load of BT traffic + * @:bt_agg_traffic_load: aggregated load of BT traffic + * @:bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant + */ +struct iwl_bt_coex_profile_notif { + __le32 mbox_msg[4]; + u8 bt_status; + u8 bt_open_conn; + u8 bt_traffic_load; + u8 bt_agg_traffic_load; + u8 bt_ci_compliance; + u8 reserved[3]; +} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_2 */ + +enum iwl_bt_coex_prio_table_event { + BT_COEX_PRIO_TBL_EVT_INIT_CALIB1 = 0, + BT_COEX_PRIO_TBL_EVT_INIT_CALIB2 = 1, + BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1 = 2, + BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2 = 3, + BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1 = 4, + BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2 = 5, + BT_COEX_PRIO_TBL_EVT_DTIM = 6, + BT_COEX_PRIO_TBL_EVT_SCAN52 = 7, + BT_COEX_PRIO_TBL_EVT_SCAN24 = 8, + BT_COEX_PRIO_TBL_EVT_IDLE = 9, + BT_COEX_PRIO_TBL_EVT_MAX = 16, +}; /* BT_COEX_PRIO_TABLE_EVENTS_API_E_VER_1 */ + +enum iwl_bt_coex_prio_table_prio { + BT_COEX_PRIO_TBL_DISABLED = 0, + BT_COEX_PRIO_TBL_PRIO_LOW = 1, + BT_COEX_PRIO_TBL_PRIO_HIGH = 2, + BT_COEX_PRIO_TBL_PRIO_BYPASS = 3, + BT_COEX_PRIO_TBL_PRIO_COEX_OFF = 4, + BT_COEX_PRIO_TBL_PRIO_COEX_ON = 5, + BT_COEX_PRIO_TBL_PRIO_COEX_IDLE = 6, + BT_COEX_PRIO_TBL_MAX = 8, +}; /* BT_COEX_PRIO_TABLE_PRIORITIES_API_E_VER_1 */ + +#define BT_COEX_PRIO_TBL_SHRD_ANT_POS (0) +#define BT_COEX_PRIO_TBL_PRIO_POS (1) +#define BT_COEX_PRIO_TBL_RESERVED_POS (4) + +/** + * struct iwl_bt_coex_prio_tbl_cmd - priority table for BT coex + * @prio_tbl: + */ +struct iwl_bt_coex_prio_tbl_cmd { + u8 prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX]; +} __packed; + +enum iwl_bt_coex_env_action { + BT_COEX_ENV_CLOSE = 0, + BT_COEX_ENV_OPEN = 1, +}; /* BT_COEX_PROT_ENV_ACTION_API_E_VER_1 */ + +/** + * struct iwl_bt_coex_prot_env_cmd - BT Protection Envelope + * @action: enum %iwl_bt_coex_env_action + * @type: enum %iwl_bt_coex_prio_table_event + */ +struct iwl_bt_coex_prot_env_cmd { + u8 action; /* 0 = closed, 1 = open */ + u8 type; /* 0 .. 15 */ + u8 reserved[2]; +} __packed; + +#endif /* __fw_api_bt_coex_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h index cf6f9a02fb74..51e015d1dfb2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -258,7 +258,7 @@ enum iwl_wowlan_wakeup_reason { IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE = BIT(8), IWL_WOWLAN_WAKEUP_BY_REM_WAKE_LINK_LOSS = BIT(9), IWL_WOWLAN_WAKEUP_BY_REM_WAKE_SIGNATURE_TABLE = BIT(10), - IWL_WOWLAN_WAKEUP_BY_REM_WAKE_TCP_EXTERNAL = BIT(11), + /* BIT(11) reserved */ IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET = BIT(12), }; /* WOWLAN_WAKE_UP_REASON_API_E_VER_2 */ @@ -277,6 +277,55 @@ struct iwl_wowlan_status { u8 wake_packet[]; /* can be truncated from _length to _bufsize */ } __packed; /* WOWLAN_STATUSES_API_S_VER_4 */ +#define IWL_WOWLAN_TCP_MAX_PACKET_LEN 64 +#define IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN 128 +#define IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS 2048 + +struct iwl_tcp_packet_info { + __le16 tcp_pseudo_header_checksum; + __le16 tcp_payload_length; +} __packed; /* TCP_PACKET_INFO_API_S_VER_2 */ + +struct iwl_tcp_packet { + struct iwl_tcp_packet_info info; + u8 rx_mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8]; + u8 data[IWL_WOWLAN_TCP_MAX_PACKET_LEN]; +} __packed; /* TCP_PROTOCOL_PACKET_API_S_VER_1 */ + +struct iwl_remote_wake_packet { + struct iwl_tcp_packet_info info; + u8 rx_mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8]; + u8 data[IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN]; +} __packed; /* TCP_PROTOCOL_PACKET_API_S_VER_1 */ + +struct iwl_wowlan_remote_wake_config { + __le32 connection_max_time; /* unused */ + /* TCP_PROTOCOL_CONFIG_API_S_VER_1 */ + u8 max_syn_retries; + u8 max_data_retries; + u8 tcp_syn_ack_timeout; + u8 tcp_ack_timeout; + + struct iwl_tcp_packet syn_tx; + struct iwl_tcp_packet synack_rx; + struct iwl_tcp_packet keepalive_ack_rx; + struct iwl_tcp_packet fin_tx; + + struct iwl_remote_wake_packet keepalive_tx; + struct iwl_remote_wake_packet wake_rx; + + /* REMOTE_WAKE_OFFSET_INFO_API_S_VER_1 */ + u8 sequence_number_offset; + u8 sequence_number_length; + u8 token_offset; + u8 token_length; + /* REMOTE_WAKE_PROTOCOL_PARAMS_API_S_VER_1 */ + __le32 initial_sequence_number; + __le16 keepalive_interval; + __le16 num_tokens; + u8 tokens[IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS]; +} __packed; /* REMOTE_WAKE_CONFIG_API_S_VER_2 */ + /* TODO: NetDetect API */ #endif /* __fw_api_d3_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h index ae39b7dfda7b..d68640ea41d4 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h index be36b7604b7f..127051891e9b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h index aa3474d08231..fdd33bc0a594 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h index 670ac8f95e26..b60d14151721 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h index 0acb53dda22d..a30691a8a85b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h index 2677914bf0a6..6d53850c5448 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index 23eebda848b0..f8d7e88234e4 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -70,6 +70,7 @@ #include "fw-api-mac.h" #include "fw-api-power.h" #include "fw-api-d3.h" +#include "fw-api-bt-coex.h" /* queue and FIFO numbers by usage */ enum { @@ -152,6 +153,7 @@ enum { BEACON_TEMPLATE_CMD = 0x91, TX_ANT_CONFIGURATION_CMD = 0x98, + BT_CONFIG = 0x9b, STATISTICS_NOTIFICATION = 0x9d, /* RF-KILL commands and notifications */ @@ -162,6 +164,11 @@ enum { REPLY_RX_MPDU_CMD = 0xc1, BA_NOTIF = 0xc5, + /* BT Coex */ + BT_COEX_PRIO_TABLE = 0xcc, + BT_COEX_PROT_ENV = 0xcd, + BT_PROFILE_NOTIFICATION = 0xce, + REPLY_DEBUG_CMD = 0xf0, DEBUG_LOG_MSG = 0xf7, @@ -762,18 +769,20 @@ struct iwl_phy_context_cmd { #define IWL_RX_INFO_PHY_CNT 8 #define IWL_RX_INFO_AGC_IDX 1 #define IWL_RX_INFO_RSSI_AB_IDX 2 -#define IWL_RX_INFO_RSSI_C_IDX 3 -#define IWL_OFDM_AGC_DB_MSK 0xfe00 -#define IWL_OFDM_AGC_DB_POS 9 +#define IWL_OFDM_AGC_A_MSK 0x0000007f +#define IWL_OFDM_AGC_A_POS 0 +#define IWL_OFDM_AGC_B_MSK 0x00003f80 +#define IWL_OFDM_AGC_B_POS 7 +#define IWL_OFDM_AGC_CODE_MSK 0x3fe00000 +#define IWL_OFDM_AGC_CODE_POS 20 #define IWL_OFDM_RSSI_INBAND_A_MSK 0x00ff -#define IWL_OFDM_RSSI_ALLBAND_A_MSK 0xff00 #define IWL_OFDM_RSSI_A_POS 0 +#define IWL_OFDM_RSSI_ALLBAND_A_MSK 0xff00 +#define IWL_OFDM_RSSI_ALLBAND_A_POS 8 #define IWL_OFDM_RSSI_INBAND_B_MSK 0xff0000 -#define IWL_OFDM_RSSI_ALLBAND_B_MSK 0xff000000 #define IWL_OFDM_RSSI_B_POS 16 -#define IWL_OFDM_RSSI_INBAND_C_MSK 0x00ff -#define IWL_OFDM_RSSI_ALLBAND_C_MSK 0xff00 -#define IWL_OFDM_RSSI_C_POS 0 +#define IWL_OFDM_RSSI_ALLBAND_B_MSK 0xff000000 +#define IWL_OFDM_RSSI_ALLBAND_B_POS 24 /** * struct iwl_rx_phy_info - phy info @@ -792,6 +801,7 @@ struct iwl_phy_context_cmd { * @byte_count: frame's byte-count * @frame_time: frame's time on the air, based on byte count and frame rate * calculation + * @mac_active_msk: what MACs were active when the frame was received * * Before each Rx, the device sends this data. It contains PHY information * about the reception of the packet. @@ -809,7 +819,7 @@ struct iwl_rx_phy_info { __le32 non_cfg_phy[IWL_RX_INFO_PHY_CNT]; __le32 rate_n_flags; __le32 byte_count; - __le16 reserved2; + __le16 mac_active_msk; __le16 frame_time; } __packed; diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index d3d959db03a9..1006b3204e7b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -79,17 +79,8 @@ #define UCODE_VALID_OK cpu_to_le32(0x1) /* Default calibration values for WkP - set to INIT image w/o running */ -static const u8 wkp_calib_values_bb_filter[] = { 0xbf, 0x00, 0x5f, 0x00, 0x2f, - 0x00, 0x18, 0x00 }; -static const u8 wkp_calib_values_rx_dc[] = { 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, - 0x7f, 0x7f, 0x7f }; -static const u8 wkp_calib_values_tx_lo[] = { 0x00, 0x00, 0x00, 0x00 }; -static const u8 wkp_calib_values_tx_iq[] = { 0xff, 0x00, 0xff, 0x00, 0x00, - 0x00 }; -static const u8 wkp_calib_values_rx_iq[] = { 0xff, 0x00, 0x00, 0x00 }; static const u8 wkp_calib_values_rx_iq_skew[] = { 0x00, 0x00, 0x01, 0x00 }; static const u8 wkp_calib_values_tx_iq_skew[] = { 0x01, 0x00, 0x00, 0x00 }; -static const u8 wkp_calib_values_xtal[] = { 0xd2, 0xd2 }; struct iwl_calib_default_data { u16 size; @@ -99,12 +90,7 @@ struct iwl_calib_default_data { #define CALIB_SIZE_N_DATA(_buf) {.size = sizeof(_buf), .data = &_buf} static const struct iwl_calib_default_data wkp_calib_default_data[12] = { - [5] = CALIB_SIZE_N_DATA(wkp_calib_values_rx_dc), - [6] = CALIB_SIZE_N_DATA(wkp_calib_values_bb_filter), - [7] = CALIB_SIZE_N_DATA(wkp_calib_values_tx_lo), - [8] = CALIB_SIZE_N_DATA(wkp_calib_values_tx_iq), [9] = CALIB_SIZE_N_DATA(wkp_calib_values_tx_iq_skew), - [10] = CALIB_SIZE_N_DATA(wkp_calib_values_rx_iq), [11] = CALIB_SIZE_N_DATA(wkp_calib_values_rx_iq_skew), }; @@ -241,20 +227,6 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, return 0; } -#define IWL_HW_REV_ID_RAINBOW 0x2 -#define IWL_PROJ_TYPE_LHP 0x5 - -static u32 iwl_mvm_build_phy_cfg(struct iwl_mvm *mvm) -{ - struct iwl_nvm_data *data = mvm->nvm_data; - /* Temp calls to static definitions, will be changed to CSR calls */ - u8 hw_rev_id = IWL_HW_REV_ID_RAINBOW; - u8 project_type = IWL_PROJ_TYPE_LHP; - - return data->radio_cfg_dash | (data->radio_cfg_step << 2) | - (hw_rev_id << 4) | ((project_type & 0x7f) << 6) | - (data->valid_tx_ant << 16) | (data->valid_rx_ant << 20); -} static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm) { @@ -262,7 +234,7 @@ static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm) enum iwl_ucode_type ucode_type = mvm->cur_ucode; /* Set parameters */ - phy_cfg_cmd.phy_cfg = cpu_to_le32(iwl_mvm_build_phy_cfg(mvm)); + phy_cfg_cmd.phy_cfg = cpu_to_le32(mvm->fw->phy_config); phy_cfg_cmd.calib_control.event_trigger = mvm->fw->default_calib[ucode_type].event_trigger; phy_cfg_cmd.calib_control.flow_trigger = @@ -275,103 +247,6 @@ static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm) sizeof(phy_cfg_cmd), &phy_cfg_cmd); } -/* Starting with the new PHY DB implementation - New calibs are enabled */ -/* Value - 0x405e7 */ -#define IWL_CALIB_DEFAULT_FLOW_INIT (IWL_CALIB_CFG_XTAL_IDX |\ - IWL_CALIB_CFG_TEMPERATURE_IDX |\ - IWL_CALIB_CFG_VOLTAGE_READ_IDX |\ - IWL_CALIB_CFG_DC_IDX |\ - IWL_CALIB_CFG_BB_FILTER_IDX |\ - IWL_CALIB_CFG_LO_LEAKAGE_IDX |\ - IWL_CALIB_CFG_TX_IQ_IDX |\ - IWL_CALIB_CFG_RX_IQ_IDX |\ - IWL_CALIB_CFG_AGC_IDX) - -#define IWL_CALIB_DEFAULT_EVENT_INIT 0x0 - -/* Value 0x41567 */ -#define IWL_CALIB_DEFAULT_FLOW_RUN (IWL_CALIB_CFG_XTAL_IDX |\ - IWL_CALIB_CFG_TEMPERATURE_IDX |\ - IWL_CALIB_CFG_VOLTAGE_READ_IDX |\ - IWL_CALIB_CFG_BB_FILTER_IDX |\ - IWL_CALIB_CFG_DC_IDX |\ - IWL_CALIB_CFG_TX_IQ_IDX |\ - IWL_CALIB_CFG_RX_IQ_IDX |\ - IWL_CALIB_CFG_SENSITIVITY_IDX |\ - IWL_CALIB_CFG_AGC_IDX) - -#define IWL_CALIB_DEFAULT_EVENT_RUN (IWL_CALIB_CFG_XTAL_IDX |\ - IWL_CALIB_CFG_TEMPERATURE_IDX |\ - IWL_CALIB_CFG_VOLTAGE_READ_IDX |\ - IWL_CALIB_CFG_TX_PWR_IDX |\ - IWL_CALIB_CFG_DC_IDX |\ - IWL_CALIB_CFG_TX_IQ_IDX |\ - IWL_CALIB_CFG_SENSITIVITY_IDX) - -/* - * Sets the calibrations trigger values that will be sent to the FW for runtime - * and init calibrations. - * The ones given in the FW TLV are not correct. - */ -static void iwl_set_default_calib_trigger(struct iwl_mvm *mvm) -{ - struct iwl_tlv_calib_ctrl default_calib; - - /* - * WkP FW TLV calib bits are wrong, overwrite them. - * This defines the dynamic calibrations which are implemented in the - * uCode both for init(flow) calculation and event driven calibs. - */ - - /* Init Image */ - default_calib.event_trigger = cpu_to_le32(IWL_CALIB_DEFAULT_EVENT_INIT); - default_calib.flow_trigger = cpu_to_le32(IWL_CALIB_DEFAULT_FLOW_INIT); - - if (default_calib.event_trigger != - mvm->fw->default_calib[IWL_UCODE_INIT].event_trigger) - IWL_ERR(mvm, - "Updating the event calib for INIT image: 0x%x -> 0x%x\n", - mvm->fw->default_calib[IWL_UCODE_INIT].event_trigger, - default_calib.event_trigger); - if (default_calib.flow_trigger != - mvm->fw->default_calib[IWL_UCODE_INIT].flow_trigger) - IWL_ERR(mvm, - "Updating the flow calib for INIT image: 0x%x -> 0x%x\n", - mvm->fw->default_calib[IWL_UCODE_INIT].flow_trigger, - default_calib.flow_trigger); - - memcpy((void *)&mvm->fw->default_calib[IWL_UCODE_INIT], - &default_calib, sizeof(struct iwl_tlv_calib_ctrl)); - IWL_ERR(mvm, - "Setting uCode init calibrations event 0x%x, trigger 0x%x\n", - default_calib.event_trigger, - default_calib.flow_trigger); - - /* Run time image */ - default_calib.event_trigger = cpu_to_le32(IWL_CALIB_DEFAULT_EVENT_RUN); - default_calib.flow_trigger = cpu_to_le32(IWL_CALIB_DEFAULT_FLOW_RUN); - - if (default_calib.event_trigger != - mvm->fw->default_calib[IWL_UCODE_REGULAR].event_trigger) - IWL_ERR(mvm, - "Updating the event calib for RT image: 0x%x -> 0x%x\n", - mvm->fw->default_calib[IWL_UCODE_REGULAR].event_trigger, - default_calib.event_trigger); - if (default_calib.flow_trigger != - mvm->fw->default_calib[IWL_UCODE_REGULAR].flow_trigger) - IWL_ERR(mvm, - "Updating the flow calib for RT image: 0x%x -> 0x%x\n", - mvm->fw->default_calib[IWL_UCODE_REGULAR].flow_trigger, - default_calib.flow_trigger); - - memcpy((void *)&mvm->fw->default_calib[IWL_UCODE_REGULAR], - &default_calib, sizeof(struct iwl_tlv_calib_ctrl)); - IWL_ERR(mvm, - "Setting uCode runtime calibs event 0x%x, trigger 0x%x\n", - default_calib.event_trigger, - default_calib.flow_trigger); -} - static int iwl_set_default_calibrations(struct iwl_mvm *mvm) { u8 cmd_raw[16]; /* holds the variable size commands */ @@ -434,6 +309,10 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) goto error; } + ret = iwl_send_bt_prio_tbl(mvm); + if (ret) + goto error; + if (read_nvm) { /* Read nvm */ ret = iwl_nvm_init(mvm); @@ -446,8 +325,10 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans); WARN_ON(ret); - /* Override the calibrations from TLV and the const of fw */ - iwl_set_default_calib_trigger(mvm); + /* Send TX valid antennas before triggering calibrations */ + ret = iwl_send_tx_ant_cfg(mvm, mvm->nvm_data->valid_tx_ant); + if (ret) + goto error; /* WkP doesn't have all calibrations, need to set default values */ if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) { @@ -537,6 +418,14 @@ int iwl_mvm_up(struct iwl_mvm *mvm) if (ret) goto error; + ret = iwl_send_bt_prio_tbl(mvm); + if (ret) + goto error; + + ret = iwl_send_bt_init_conf(mvm); + if (ret) + goto error; + /* Send phy db control command and then phy db calibration*/ ret = iwl_send_phy_db_data(mvm->phy_db); if (ret) diff --git a/drivers/net/wireless/iwlwifi/mvm/led.c b/drivers/net/wireless/iwlwifi/mvm/led.c index 011906e73a05..2269a9e5cc67 100644 --- a/drivers/net/wireless/iwlwifi/mvm/led.c +++ b/drivers/net/wireless/iwlwifi/mvm/led.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index 341dbc0237ea..2779235daa35 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -553,9 +553,9 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, if (vif->bss_conf.qos) cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA); + /* Don't use cts to self as the fw doesn't support it currently. */ if (vif->bss_conf.use_cts_prot) - cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT | - MAC_PROT_FLG_SELF_CTS_EN); + cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT); /* * I think that we should enable these 2 flags regardless the HT PROT @@ -651,6 +651,13 @@ static int iwl_mvm_mac_ctxt_cmd_station(struct iwl_mvm *mvm, /* Fill the common data for all mac context types */ iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action); + /* Allow beacons to pass through as long as we are not associated,or we + * do not have dtim period information */ + if (!vif->bss_conf.assoc || !vif->bss_conf.dtim_period) + cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON); + else + cmd.filter_flags &= ~cpu_to_le32(MAC_FILTER_IN_BEACON); + /* Fill the data specific for station mode */ iwl_mvm_mac_ctxt_cmd_fill_sta(mvm, vif, &cmd.sta); @@ -714,7 +721,9 @@ static int iwl_mvm_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm, iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action); cmd.protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT); - cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROMISC); + + /* Override the filter flags to accept only probe requests */ + cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); /* * This flag should be set to true when the P2P Device is @@ -846,10 +855,10 @@ int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm, */ static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_mac_data_ap *ctxt_ap) + struct iwl_mac_data_ap *ctxt_ap, + bool add) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - u32 curr_dev_time; ctxt_ap->bi = cpu_to_le32(vif->bss_conf.beacon_int); ctxt_ap->bi_reciprocal = @@ -861,10 +870,19 @@ static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm, vif->bss_conf.dtim_period)); ctxt_ap->mcast_qid = cpu_to_le32(vif->cab_queue); - curr_dev_time = iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG); - ctxt_ap->beacon_time = cpu_to_le32(curr_dev_time); - ctxt_ap->beacon_tsf = cpu_to_le64(curr_dev_time); + /* + * Only read the system time when the MAC is being added, when we + * just modify the MAC then we should keep the time -- the firmware + * can otherwise have a "jumping" TBTT. + */ + if (add) + mvmvif->ap_beacon_time = + iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG); + + ctxt_ap->beacon_time = cpu_to_le32(mvmvif->ap_beacon_time); + + ctxt_ap->beacon_tsf = 0; /* unused */ /* TODO: Assume that the beacon id == mac context id */ ctxt_ap->beacon_template = cpu_to_le32(mvmvif->id); @@ -881,8 +899,12 @@ static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm, /* Fill the common data for all mac context types */ iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action); + /* Also enable probe requests to pass */ + cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); + /* Fill the data specific for ap mode */ - iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.ap); + iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.ap, + action == FW_CTXT_ACTION_ADD); return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); } @@ -899,7 +921,8 @@ static int iwl_mvm_mac_ctxt_cmd_go(struct iwl_mvm *mvm, iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action); /* Fill the data specific for GO mode */ - iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.go.ap); + iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.go.ap, + action == FW_CTXT_ACTION_ADD); cmd.go.ctwin = cpu_to_le32(vif->bss_conf.p2p_ctwindow); cmd.go.opp_ps_enabled = cpu_to_le32(!!vif->bss_conf.p2p_oppps); diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 23460f4a4c75..14dd5ee9a01e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -65,7 +65,9 @@ #include <linux/skbuff.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> +#include <linux/ip.h> #include <net/mac80211.h> +#include <net/tcp.h> #include "iwl-op-mode.h" #include "iwl-io.h" @@ -102,10 +104,33 @@ static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = { }, }; +#ifdef CONFIG_PM_SLEEP +static const struct nl80211_wowlan_tcp_data_token_feature +iwl_mvm_wowlan_tcp_token_feature = { + .min_len = 0, + .max_len = 255, + .bufsize = IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS, +}; + +static const struct wiphy_wowlan_tcp_support iwl_mvm_wowlan_tcp_support = { + .tok = &iwl_mvm_wowlan_tcp_token_feature, + .data_payload_max = IWL_WOWLAN_TCP_MAX_PACKET_LEN - + sizeof(struct ethhdr) - + sizeof(struct iphdr) - + sizeof(struct tcphdr), + .data_interval_max = 65535, /* __le16 in API */ + .wake_payload_max = IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN - + sizeof(struct ethhdr) - + sizeof(struct iphdr) - + sizeof(struct tcphdr), + .seq = true, +}; +#endif + int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) { struct ieee80211_hw *hw = mvm->hw; - int num_mac, ret; + int num_mac, ret, i; /* Tell mac80211 our characteristics */ hw->flags = IEEE80211_HW_SIGNAL_DBM | @@ -156,11 +181,15 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN); hw->wiphy->addresses = mvm->addresses; hw->wiphy->n_addresses = 1; - num_mac = mvm->nvm_data->n_hw_addrs; - if (num_mac > 1) { - memcpy(mvm->addresses[1].addr, mvm->addresses[0].addr, + + /* Extract additional MAC addresses if available */ + num_mac = (mvm->nvm_data->n_hw_addrs > 1) ? + min(IWL_MVM_MAX_ADDRESSES, mvm->nvm_data->n_hw_addrs) : 1; + + for (i = 1; i < num_mac; i++) { + memcpy(mvm->addresses[i].addr, mvm->addresses[i-1].addr, ETH_ALEN); - mvm->addresses[1].addr[5]++; + mvm->addresses[i].addr[5]++; hw->wiphy->n_addresses++; } @@ -206,6 +235,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->wiphy->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS; hw->wiphy->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN; hw->wiphy->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN; + hw->wiphy->wowlan.tcp = &iwl_mvm_wowlan_tcp_support; } #endif @@ -273,12 +303,18 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, 0, false); break; case IEEE80211_AMPDU_TX_START: + if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG) { + ret = -EINVAL; + break; + } ret = iwl_mvm_sta_tx_agg_start(mvm, vif, sta, tid, ssn); break; case IEEE80211_AMPDU_TX_STOP_CONT: + ret = iwl_mvm_sta_tx_agg_stop(mvm, vif, sta, tid); + break; case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: - ret = iwl_mvm_sta_tx_agg_stop(mvm, vif, sta, tid); + ret = iwl_mvm_sta_tx_agg_flush(mvm, vif, sta, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: ret = iwl_mvm_sta_tx_agg_oper(mvm, vif, sta, tid, buf_size); @@ -557,11 +593,9 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, return ret; } -static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) +static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) { - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); u32 tfd_msk = 0, ac; for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) @@ -594,12 +628,21 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, */ 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); /* * For AP/GO interface, the tear down of the resources allocated to the - * interface should be handled as part of the bss_info_changed flow. + * interface is be handled as part of the stop_ap flow. */ if (vif->type == NL80211_IFTYPE_AP) { iwl_mvm_dealloc_int_sta(mvm, &mvmvif->bcast_sta); @@ -763,6 +806,8 @@ static void iwl_mvm_stop_ap(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); mvmvif->ap_active = false; diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 4e339ccfa800..203eb85e03d3 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -79,8 +79,9 @@ #include "fw-api.h" #define IWL_INVALID_MAC80211_QUEUE 0xff -#define IWL_MVM_MAX_ADDRESSES 2 -#define IWL_RSSI_OFFSET 44 +#define IWL_MVM_MAX_ADDRESSES 5 +/* RSSI offset for WkP */ +#define IWL_RSSI_OFFSET 50 enum iwl_mvm_tx_fifo { IWL_MVM_TX_FIFO_BK = 0, @@ -173,6 +174,8 @@ struct iwl_mvm_vif { bool uploaded; bool ap_active; + u32 ap_beacon_time; + enum iwl_tsf_id tsf_id; /* @@ -327,6 +330,14 @@ struct iwl_mvm { struct led_classdev led; struct ieee80211_vif *p2p_device_vif; + +#ifdef CONFIG_PM_SLEEP + int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen; +#endif + + /* BT-Coex */ + u8 bt_kill_msk; + struct iwl_bt_coex_profile_notif last_bt_notif; }; /* Extract MVM priv from op_mode and _hw */ @@ -497,4 +508,11 @@ void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw, void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int idx); +/* BT Coex */ +int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm); +int iwl_send_bt_init_conf(struct iwl_mvm *mvm); +int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); + #endif /* __IWL_MVM_H__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index 20016bcbdeab..93e3d0f174cc 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -74,6 +74,9 @@ static const int nvm_to_read[] = { NVM_SECTION_TYPE_PRODUCTION, }; +/* Default NVM size to read */ +#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024); + /* used to simplify the shared operations on NCM_ACCESS_CMD versions */ union iwl_nvm_access_cmd { struct iwl_nvm_access_cmd_ver1 ver1; @@ -193,9 +196,9 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section, int ret; bool old_eeprom = mvm->cfg->device_family != IWL_DEVICE_FAMILY_7000; - length = (iwlwifi_mod_params.amsdu_size_8K ? (8 * 1024) : (4 * 1024)) - - sizeof(union iwl_nvm_access_cmd) - - sizeof(struct iwl_rx_packet); + /* Set nvm section read length */ + length = IWL_NVM_DEFAULT_CHUNK_SIZE; + /* * if length is greater than EEPROM size, truncate it because uCode * doesn't check it by itself, and exit the loop when reached. diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index aa59adf87db3..828bdddd07e9 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -230,6 +230,8 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false), RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, false), + RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true), + RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false), RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false), @@ -293,6 +295,11 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = { CMD(NET_DETECT_PROFILES_CMD), CMD(NET_DETECT_HOTSPOTS_CMD), CMD(NET_DETECT_HOTSPOTS_QUERY_CMD), + CMD(CARD_STATE_NOTIFICATION), + CMD(BT_COEX_PRIO_TABLE), + CMD(BT_COEX_PROT_ENV), + CMD(BT_PROFILE_NOTIFICATION), + CMD(BT_CONFIG), }; #undef CMD @@ -363,8 +370,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K; - /* TODO: this should really be a TLV */ - if (cfg->device_family == IWL_DEVICE_FAMILY_7000) + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DW_BC_TABLE) trans_cfg.bc_table_dword = true; if (!iwlwifi_mod_params.wd_disable) @@ -624,12 +630,8 @@ static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) ieee80211_free_txskb(mvm->hw, skb); } -static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode) +static void iwl_mvm_nic_restart(struct iwl_mvm *mvm) { - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - - iwl_mvm_dump_nic_error_log(mvm); - iwl_abort_notification_waits(&mvm->notif_wait); /* @@ -663,9 +665,21 @@ static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode) } } +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); +} + 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); } static const struct iwl_op_mode_ops iwl_mvm_ops = { diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c index b428448f8ddf..0d537e035ef0 100644 --- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c index 5a92a4978795..efb9a6f3faac 100644 --- a/drivers/net/wireless/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/iwlwifi/mvm/power.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c index 925628468146..df85c49dc599 100644 --- a/drivers/net/wireless/iwlwifi/mvm/quota.c +++ b/drivers/net/wireless/iwlwifi/mvm/quota.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 56b636d9ab30..a01a6612677e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -680,12 +680,14 @@ static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags, */ static bool rs_use_green(struct ieee80211_sta *sta) { - struct iwl_mvm_sta *sta_priv = (void *)sta->drv_priv; - - bool use_green = !(sta_priv->vif->bss_conf.ht_operation_mode & - IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); - - return (sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && use_green; + /* + * There's a bug somewhere in this code that causes the + * scaling to get stuck because GF+SGI can't be combined + * in SISO rates. Until we find that bug, disable GF, it + * has only limited benefit and we still interoperate with + * GF APs since we can always receive GF transmissions. + */ + return false; } /** diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c index 3f40ab05bbd8..4dfc21a3e83e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/iwlwifi/mvm/rx.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -131,33 +131,42 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm, static int iwl_mvm_calc_rssi(struct iwl_mvm *mvm, struct iwl_rx_phy_info *phy_info) { - u32 rssi_a, rssi_b, rssi_c, max_rssi, agc_db; + int rssi_a, rssi_b, rssi_a_dbm, rssi_b_dbm, max_rssi_dbm; + int rssi_all_band_a, rssi_all_band_b; + u32 agc_a, agc_b, max_agc; u32 val; - /* Find max rssi among 3 possible receivers. + /* Find max rssi among 2 possible receivers. * These values are measured by the Digital Signal Processor (DSP). * They should stay fairly constant even as the signal strength varies, * if the radio's Automatic Gain Control (AGC) is working right. * AGC value (see below) will provide the "interesting" info. */ + val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_AGC_IDX]); + agc_a = (val & IWL_OFDM_AGC_A_MSK) >> IWL_OFDM_AGC_A_POS; + agc_b = (val & IWL_OFDM_AGC_B_MSK) >> IWL_OFDM_AGC_B_POS; + max_agc = max_t(u32, agc_a, agc_b); + val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_RSSI_AB_IDX]); rssi_a = (val & IWL_OFDM_RSSI_INBAND_A_MSK) >> IWL_OFDM_RSSI_A_POS; rssi_b = (val & IWL_OFDM_RSSI_INBAND_B_MSK) >> IWL_OFDM_RSSI_B_POS; - val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_RSSI_C_IDX]); - rssi_c = (val & IWL_OFDM_RSSI_INBAND_C_MSK) >> IWL_OFDM_RSSI_C_POS; - - val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_AGC_IDX]); - agc_db = (val & IWL_OFDM_AGC_DB_MSK) >> IWL_OFDM_AGC_DB_POS; + rssi_all_band_a = (val & IWL_OFDM_RSSI_ALLBAND_A_MSK) >> + IWL_OFDM_RSSI_ALLBAND_A_POS; + rssi_all_band_b = (val & IWL_OFDM_RSSI_ALLBAND_B_MSK) >> + IWL_OFDM_RSSI_ALLBAND_B_POS; - max_rssi = max_t(u32, rssi_a, rssi_b); - max_rssi = max_t(u32, max_rssi, rssi_c); + /* + * dBm = rssi dB - agc dB - constant. + * Higher AGC (higher radio gain) means lower signal. + */ + rssi_a_dbm = rssi_a - IWL_RSSI_OFFSET - agc_a; + rssi_b_dbm = rssi_b - IWL_RSSI_OFFSET - agc_b; + max_rssi_dbm = max_t(int, rssi_a_dbm, rssi_b_dbm); - IWL_DEBUG_STATS(mvm, "Rssi In A %d B %d C %d Max %d AGC dB %d\n", - rssi_a, rssi_b, rssi_c, max_rssi, agc_db); + IWL_DEBUG_STATS(mvm, "Rssi In A %d B %d Max %d AGCA %d AGCB %d\n", + rssi_a_dbm, rssi_b_dbm, max_rssi_dbm, agc_a, agc_b); - /* dBm = max_rssi dB - agc dB - constant. - * Higher AGC (higher radio gain) means lower signal. */ - return max_rssi - agc_db - IWL_RSSI_OFFSET; + return max_rssi_dbm; } /* diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 9b21b92aa8d1..0d3c76b29242 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 52aecf20d0df..4d872d69577f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -101,8 +101,55 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, } add_sta_cmd.add_modify = update ? 1 : 0; - /* STA_FLG_FAT_EN_MSK ? */ - /* STA_FLG_MIMO_EN_MSK ? */ + add_sta_cmd.station_flags_msk |= cpu_to_le32(STA_FLG_FAT_EN_MSK | + STA_FLG_MIMO_EN_MSK); + + switch (sta->bandwidth) { + case IEEE80211_STA_RX_BW_160: + add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_160MHZ); + /* fall through */ + case IEEE80211_STA_RX_BW_80: + add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_80MHZ); + /* fall through */ + case IEEE80211_STA_RX_BW_40: + add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_40MHZ); + /* fall through */ + case IEEE80211_STA_RX_BW_20: + if (sta->ht_cap.ht_supported) + add_sta_cmd.station_flags |= + cpu_to_le32(STA_FLG_FAT_EN_20MHZ); + break; + } + + switch (sta->rx_nss) { + case 1: + add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_SISO); + break; + case 2: + add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_MIMO2); + break; + case 3 ... 8: + add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_MIMO3); + break; + } + + switch (sta->smps_mode) { + case IEEE80211_SMPS_AUTOMATIC: + case IEEE80211_SMPS_NUM_MODES: + WARN_ON(1); + break; + case IEEE80211_SMPS_STATIC: + /* override NSS */ + add_sta_cmd.station_flags &= ~cpu_to_le32(STA_FLG_MIMO_EN_MSK); + add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_SISO); + break; + case IEEE80211_SMPS_DYNAMIC: + add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_RTS_MIMO_PROT); + break; + case IEEE80211_SMPS_OFF: + /* nothing */ + break; + } if (sta->ht_cap.ht_supported) { add_sta_cmd.station_flags_msk |= @@ -340,6 +387,9 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm, if (vif->type == NL80211_IFTYPE_STATION && mvmvif->ap_sta_id == mvm_sta->sta_id) { + /* flush its queues here since we are freeing mvm_sta */ + ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, true); + /* * Put a non-NULL since the fw station isn't removed. * It will be removed after the MAC will be set as @@ -348,9 +398,6 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm, rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id], ERR_PTR(-EINVAL)); - /* flush its queues here since we are freeing mvm_sta */ - ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, true); - /* if we are associated - we can't remove the AP STA now */ if (vif->bss_conf.assoc) return ret; @@ -770,6 +817,16 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 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; @@ -824,6 +881,34 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, return err; } +int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid) +{ + struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; + u16 txq_id; + + /* + * First set the agg state to OFF to avoid calling + * ieee80211_stop_tx_ba_cb in iwl_mvm_check_ratid_empty. + */ + spin_lock_bh(&mvmsta->lock); + txq_id = tid_data->txq_id; + IWL_DEBUG_TX_QUEUES(mvm, "Flush AGG: sta %d tid %d q %d state %d\n", + mvmsta->sta_id, tid, txq_id, tid_data->state); + tid_data->state = IWL_AGG_OFF; + spin_unlock_bh(&mvmsta->lock); + + if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), true)) + IWL_ERR(mvm, "Couldn't flush the AGG queue\n"); + + iwl_trans_txq_disable(mvm->trans, tid_data->txq_id); + mvm->queue_to_mac80211[tid_data->txq_id] = + IWL_INVALID_MAC80211_QUEUE; + + return 0; +} + static int iwl_mvm_set_fw_key_idx(struct iwl_mvm *mvm) { int i; diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h index 896f88ac8145..b0352df981e4 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/sta.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -348,6 +348,8 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid, u8 buf_size); int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid); +int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid); int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm); int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta, diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c index e437e02c7149..c2c7f5176027 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.h b/drivers/net/wireless/iwlwifi/mvm/time-event.h index 64fb57a5ab43..b36424eda361 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.h +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.h @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index 56df249b215e..0556d5e16f4e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -607,12 +607,8 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, /* Single frame failure in an AMPDU queue => send BAR */ if (txq_id >= IWL_FIRST_AMPDU_QUEUE && - !(info->flags & IEEE80211_TX_STAT_ACK)) { - /* there must be only one skb in the skb_list */ - WARN_ON_ONCE(skb_freed > 1 || - !skb_queue_empty(&skbs)); + !(info->flags & IEEE80211_TX_STAT_ACK)) info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; - } /* W/A FW bug: seq_ctl is wrong when the queue is flushed */ if (status == TX_STATUS_FAIL_FIFO_FLUSHED) { diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index 000e842c2edd..e308ad93aa9e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> diff --git a/drivers/net/wireless/iwlwifi/pcie/cfg.h b/drivers/net/wireless/iwlwifi/pcie/cfg.h deleted file mode 100644 index c6f8e83c3551..000000000000 --- a/drivers/net/wireless/iwlwifi/pcie/cfg.h +++ /dev/null @@ -1,115 +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) 2007 - 2013 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * 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) 2005 - 2013 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#ifndef __iwl_pci_h__ -#define __iwl_pci_h__ - - -/* - * This file declares the config structures for all devices. - */ - -extern const struct iwl_cfg iwl5300_agn_cfg; -extern const struct iwl_cfg iwl5100_agn_cfg; -extern const struct iwl_cfg iwl5350_agn_cfg; -extern const struct iwl_cfg iwl5100_bgn_cfg; -extern const struct iwl_cfg iwl5100_abg_cfg; -extern const struct iwl_cfg iwl5150_agn_cfg; -extern const struct iwl_cfg iwl5150_abg_cfg; -extern const struct iwl_cfg iwl6005_2agn_cfg; -extern const struct iwl_cfg iwl6005_2abg_cfg; -extern const struct iwl_cfg iwl6005_2bg_cfg; -extern const struct iwl_cfg iwl6005_2agn_sff_cfg; -extern const struct iwl_cfg iwl6005_2agn_d_cfg; -extern const struct iwl_cfg iwl6005_2agn_mow1_cfg; -extern const struct iwl_cfg iwl6005_2agn_mow2_cfg; -extern const struct iwl_cfg iwl1030_bgn_cfg; -extern const struct iwl_cfg iwl1030_bg_cfg; -extern const struct iwl_cfg iwl6030_2agn_cfg; -extern const struct iwl_cfg iwl6030_2abg_cfg; -extern const struct iwl_cfg iwl6030_2bgn_cfg; -extern const struct iwl_cfg iwl6030_2bg_cfg; -extern const struct iwl_cfg iwl6000i_2agn_cfg; -extern const struct iwl_cfg iwl6000i_2abg_cfg; -extern const struct iwl_cfg iwl6000i_2bg_cfg; -extern const struct iwl_cfg iwl6000_3agn_cfg; -extern const struct iwl_cfg iwl6050_2agn_cfg; -extern const struct iwl_cfg iwl6050_2abg_cfg; -extern const struct iwl_cfg iwl6150_bgn_cfg; -extern const struct iwl_cfg iwl6150_bg_cfg; -extern const struct iwl_cfg iwl1000_bgn_cfg; -extern const struct iwl_cfg iwl1000_bg_cfg; -extern const struct iwl_cfg iwl100_bgn_cfg; -extern const struct iwl_cfg iwl100_bg_cfg; -extern const struct iwl_cfg iwl130_bgn_cfg; -extern const struct iwl_cfg iwl130_bg_cfg; -extern const struct iwl_cfg iwl2000_2bgn_cfg; -extern const struct iwl_cfg iwl2000_2bgn_d_cfg; -extern const struct iwl_cfg iwl2030_2bgn_cfg; -extern const struct iwl_cfg iwl6035_2agn_cfg; -extern const struct iwl_cfg iwl105_bgn_cfg; -extern const struct iwl_cfg iwl105_bgn_d_cfg; -extern const struct iwl_cfg iwl135_bgn_cfg; -extern const struct iwl_cfg iwl7260_2ac_cfg; -extern const struct iwl_cfg iwl3160_ac_cfg; - -#endif /* __iwl_pci_h__ */ diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c index 7bc0fb9128dd..46ca91f77c9c 100644 --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -69,8 +69,6 @@ #include "iwl-trans.h" #include "iwl-drv.h" - -#include "cfg.h" #include "internal.h" #define IWL_PCI_DEVICE(dev, subdev, cfg) \ diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index aa2a39a637dd..148843e7f34f 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -137,10 +137,6 @@ static inline int iwl_queue_dec_wrap(int index, int n_bd) struct iwl_cmd_meta { /* only for SYNC commands, iff the reply skb is wanted */ struct iwl_host_cmd *source; - - DEFINE_DMA_UNMAP_ADDR(mapping); - DEFINE_DMA_UNMAP_LEN(len); - u32 flags; }; @@ -182,19 +178,39 @@ struct iwl_queue { #define TFD_TX_CMD_SLOTS 256 #define TFD_CMD_SLOTS 32 +/* + * The FH will write back to the first TB only, so we need + * to copy some data into the buffer regardless of whether + * it should be mapped or not. This indicates how big the + * first TB must be to include the scratch buffer. Since + * the scratch is 4 bytes at offset 12, it's 16 now. If we + * make it bigger then allocations will be bigger and copy + * slower, so that's probably not useful. + */ +#define IWL_HCMD_SCRATCHBUF_SIZE 16 + struct iwl_pcie_txq_entry { struct iwl_device_cmd *cmd; - struct iwl_device_cmd *copy_cmd; struct sk_buff *skb; /* buffer to free after command completes */ const void *free_buf; struct iwl_cmd_meta meta; }; +struct iwl_pcie_txq_scratch_buf { + struct iwl_cmd_header hdr; + u8 buf[8]; + __le32 scratch; +}; + /** * struct iwl_txq - Tx Queue for DMA * @q: generic Rx/Tx queue descriptor * @tfds: transmit frame descriptors (DMA memory) + * @scratchbufs: start of command headers, including scratch buffers, for + * the writeback -- this is DMA memory and an array holding one buffer + * for each command on the queue + * @scratchbufs_dma: DMA address for the scratchbufs start * @entries: transmit entries (driver state) * @lock: queue lock * @stuck_timer: timer that fires if queue gets stuck @@ -208,6 +224,8 @@ struct iwl_pcie_txq_entry { struct iwl_txq { struct iwl_queue q; struct iwl_tfd *tfds; + struct iwl_pcie_txq_scratch_buf *scratchbufs; + dma_addr_t scratchbufs_dma; struct iwl_pcie_txq_entry *entries; spinlock_t lock; struct timer_list stuck_timer; @@ -216,6 +234,13 @@ struct iwl_txq { u8 active; }; +static inline dma_addr_t +iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx) +{ + return txq->scratchbufs_dma + + sizeof(struct iwl_pcie_txq_scratch_buf) * idx; +} + /** * struct iwl_trans_pcie - PCIe transport specific data * @rxq: all the RX queue data diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index b0ae06d2456f..567e67ad1f61 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -637,22 +637,14 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, index = SEQ_TO_INDEX(sequence); cmd_index = get_cmd_index(&txq->q, index); - if (reclaim) { - struct iwl_pcie_txq_entry *ent; - ent = &txq->entries[cmd_index]; - cmd = ent->copy_cmd; - WARN_ON_ONCE(!cmd && ent->meta.flags & CMD_WANT_HCMD); - } else { + if (reclaim) + cmd = txq->entries[cmd_index].cmd; + else cmd = NULL; - } err = iwl_op_mode_rx(trans->op_mode, &rxcb, cmd); if (reclaim) { - /* The original command isn't needed any more */ - kfree(txq->entries[cmd_index].copy_cmd); - txq->entries[cmd_index].copy_cmd = NULL; - /* nor is the duplicated part of the command */ kfree(txq->entries[cmd_index].free_buf); txq->entries[cmd_index].free_buf = NULL; } diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 17bedc50e753..6649e377e9cd 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -22,7 +22,7 @@ * USA * * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. + * in the file called COPYING. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> @@ -715,7 +715,8 @@ static u32 iwl_trans_pcie_read32(struct iwl_trans *trans, u32 ofs) static u32 iwl_trans_pcie_read_prph(struct iwl_trans *trans, u32 reg) { - iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_RADDR, reg | (3 << 24)); + iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_RADDR, + ((reg & 0x000FFFFF) | (3 << 24))); return iwl_trans_pcie_read32(trans, HBUS_TARG_PRPH_RDAT); } @@ -723,7 +724,7 @@ static void iwl_trans_pcie_write_prph(struct iwl_trans *trans, u32 addr, u32 val) { iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WADDR, - ((addr & 0x0000FFFF) | (3 << 24))); + ((addr & 0x000FFFFF) | (3 << 24))); iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WDAT, val); } @@ -1370,28 +1371,11 @@ static ssize_t iwl_dbgfs_fh_reg_read(struct file *file, return ret; } -static ssize_t iwl_dbgfs_fw_restart_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_trans *trans = file->private_data; - - if (!trans->op_mode) - return -EAGAIN; - - local_bh_disable(); - iwl_op_mode_nic_error(trans->op_mode); - local_bh_enable(); - - return count; -} - DEBUGFS_READ_WRITE_FILE_OPS(interrupt); DEBUGFS_READ_FILE_OPS(fh_reg); DEBUGFS_READ_FILE_OPS(rx_queue); DEBUGFS_READ_FILE_OPS(tx_queue); DEBUGFS_WRITE_FILE_OPS(csr); -DEBUGFS_WRITE_FILE_OPS(fw_restart); /* * Create the debugfs files and directories @@ -1405,7 +1389,6 @@ static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, DEBUGFS_ADD_FILE(interrupt, dir, S_IWUSR | S_IRUSR); DEBUGFS_ADD_FILE(csr, dir, S_IWUSR); DEBUGFS_ADD_FILE(fh_reg, dir, S_IRUSR); - DEBUGFS_ADD_FILE(fw_restart, dir, S_IWUSR); return 0; err: diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index ad7441dfa6fb..84f4634634bc 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -191,12 +191,9 @@ static void iwl_pcie_txq_stuck_timer(unsigned long data) } for (i = q->read_ptr; i != q->write_ptr; - i = iwl_queue_inc_wrap(i, q->n_bd)) { - struct iwl_tx_cmd *tx_cmd = - (struct iwl_tx_cmd *)txq->entries[i].cmd->payload; + i = iwl_queue_inc_wrap(i, q->n_bd)) IWL_ERR(trans, "scratch %d = 0x%08x\n", i, - get_unaligned_le32(&tx_cmd->scratch)); - } + le32_to_cpu(txq->scratchbufs[i].scratch)); iwl_op_mode_nic_error(trans->op_mode); } @@ -367,8 +364,8 @@ static inline u8 iwl_pcie_tfd_get_num_tbs(struct iwl_tfd *tfd) } static void iwl_pcie_tfd_unmap(struct iwl_trans *trans, - struct iwl_cmd_meta *meta, struct iwl_tfd *tfd, - enum dma_data_direction dma_dir) + struct iwl_cmd_meta *meta, + struct iwl_tfd *tfd) { int i; int num_tbs; @@ -382,17 +379,12 @@ static void iwl_pcie_tfd_unmap(struct iwl_trans *trans, return; } - /* Unmap tx_cmd */ - if (num_tbs) - dma_unmap_single(trans->dev, - dma_unmap_addr(meta, mapping), - dma_unmap_len(meta, len), - DMA_BIDIRECTIONAL); + /* first TB is never freed - it's the scratchbuf data */ - /* Unmap chunks, if any. */ for (i = 1; i < num_tbs; i++) dma_unmap_single(trans->dev, iwl_pcie_tfd_tb_get_addr(tfd, i), - iwl_pcie_tfd_tb_get_len(tfd, i), dma_dir); + iwl_pcie_tfd_tb_get_len(tfd, i), + DMA_TO_DEVICE); tfd->num_tbs = 0; } @@ -406,8 +398,7 @@ static void iwl_pcie_tfd_unmap(struct iwl_trans *trans, * Does NOT advance any TFD circular buffer read/write indexes * Does NOT free the TFD itself (which is within circular buffer) */ -static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq, - enum dma_data_direction dma_dir) +static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq) { struct iwl_tfd *tfd_tmp = txq->tfds; @@ -418,8 +409,7 @@ static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq, lockdep_assert_held(&txq->lock); /* We have only q->n_window txq->entries, but we use q->n_bd tfds */ - iwl_pcie_tfd_unmap(trans, &txq->entries[idx].meta, &tfd_tmp[rd_ptr], - dma_dir); + iwl_pcie_tfd_unmap(trans, &txq->entries[idx].meta, &tfd_tmp[rd_ptr]); /* free SKB */ if (txq->entries) { @@ -479,6 +469,7 @@ static int iwl_pcie_txq_alloc(struct iwl_trans *trans, { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); size_t tfd_sz = sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX; + size_t scratchbuf_sz; int i; if (WARN_ON(txq->entries || txq->tfds)) @@ -514,9 +505,25 @@ static int iwl_pcie_txq_alloc(struct iwl_trans *trans, IWL_ERR(trans, "dma_alloc_coherent(%zd) failed\n", tfd_sz); goto error; } + + BUILD_BUG_ON(IWL_HCMD_SCRATCHBUF_SIZE != sizeof(*txq->scratchbufs)); + BUILD_BUG_ON(offsetof(struct iwl_pcie_txq_scratch_buf, scratch) != + sizeof(struct iwl_cmd_header) + + offsetof(struct iwl_tx_cmd, scratch)); + + scratchbuf_sz = sizeof(*txq->scratchbufs) * slots_num; + + txq->scratchbufs = dma_alloc_coherent(trans->dev, scratchbuf_sz, + &txq->scratchbufs_dma, + GFP_KERNEL); + if (!txq->scratchbufs) + goto err_free_tfds; + txq->q.id = txq_id; return 0; +err_free_tfds: + dma_free_coherent(trans->dev, tfd_sz, txq->tfds, txq->q.dma_addr); error: if (txq->entries && txq_id == trans_pcie->cmd_queue) for (i = 0; i < slots_num; i++) @@ -565,22 +572,13 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_txq *txq = &trans_pcie->txq[txq_id]; struct iwl_queue *q = &txq->q; - enum dma_data_direction dma_dir; if (!q->n_bd) return; - /* In the command queue, all the TBs are mapped as BIDI - * so unmap them as such. - */ - if (txq_id == trans_pcie->cmd_queue) - dma_dir = DMA_BIDIRECTIONAL; - else - dma_dir = DMA_TO_DEVICE; - spin_lock_bh(&txq->lock); while (q->write_ptr != q->read_ptr) { - iwl_pcie_txq_free_tfd(trans, txq, dma_dir); + iwl_pcie_txq_free_tfd(trans, txq); q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd); } spin_unlock_bh(&txq->lock); @@ -610,7 +608,6 @@ static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id) if (txq_id == trans_pcie->cmd_queue) for (i = 0; i < txq->q.n_window; i++) { kfree(txq->entries[i].cmd); - kfree(txq->entries[i].copy_cmd); kfree(txq->entries[i].free_buf); } @@ -619,6 +616,10 @@ static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id) dma_free_coherent(dev, sizeof(struct iwl_tfd) * txq->q.n_bd, txq->tfds, txq->q.dma_addr); txq->q.dma_addr = 0; + + dma_free_coherent(dev, + sizeof(*txq->scratchbufs) * txq->q.n_window, + txq->scratchbufs, txq->scratchbufs_dma); } kfree(txq->entries); @@ -962,7 +963,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, iwl_pcie_txq_inval_byte_cnt_tbl(trans, txq); - iwl_pcie_txq_free_tfd(trans, txq, DMA_TO_DEVICE); + iwl_pcie_txq_free_tfd(trans, txq); } iwl_pcie_txq_progress(trans_pcie, txq); @@ -1152,20 +1153,37 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, void *dup_buf = NULL; dma_addr_t phys_addr; int idx; - u16 copy_size, cmd_size; + u16 copy_size, cmd_size, scratch_size; bool had_nocopy = false; int i; u32 cmd_pos; + const u8 *cmddata[IWL_MAX_CMD_TBS_PER_TFD]; + u16 cmdlen[IWL_MAX_CMD_TBS_PER_TFD]; copy_size = sizeof(out_cmd->hdr); cmd_size = sizeof(out_cmd->hdr); /* need one for the header if the first is NOCOPY */ - BUILD_BUG_ON(IWL_MAX_CMD_TFDS > IWL_NUM_OF_TBS - 1); + BUILD_BUG_ON(IWL_MAX_CMD_TBS_PER_TFD > IWL_NUM_OF_TBS - 1); + + for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { + cmddata[i] = cmd->data[i]; + cmdlen[i] = cmd->len[i]; - for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { if (!cmd->len[i]) continue; + + /* need at least IWL_HCMD_SCRATCHBUF_SIZE copied */ + if (copy_size < IWL_HCMD_SCRATCHBUF_SIZE) { + int copy = IWL_HCMD_SCRATCHBUF_SIZE - copy_size; + + if (copy > cmdlen[i]) + copy = cmdlen[i]; + cmdlen[i] -= copy; + cmddata[i] += copy; + copy_size += copy; + } + if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) { had_nocopy = true; if (WARN_ON(cmd->dataflags[i] & IWL_HCMD_DFL_DUP)) { @@ -1185,7 +1203,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, goto free_dup_buf; } - dup_buf = kmemdup(cmd->data[i], cmd->len[i], + dup_buf = kmemdup(cmddata[i], cmdlen[i], GFP_ATOMIC); if (!dup_buf) return -ENOMEM; @@ -1195,7 +1213,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, idx = -EINVAL; goto free_dup_buf; } - copy_size += cmd->len[i]; + copy_size += cmdlen[i]; } cmd_size += cmd->len[i]; } @@ -1242,30 +1260,30 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, /* and copy the data that needs to be copied */ cmd_pos = offsetof(struct iwl_device_cmd, payload); - for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { - if (!cmd->len[i]) + copy_size = sizeof(out_cmd->hdr); + for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { + int copy = 0; + + if (!cmd->len) continue; - if (cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY | - IWL_HCMD_DFL_DUP)) - break; - memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], cmd->len[i]); - cmd_pos += cmd->len[i]; - } - WARN_ON_ONCE(txq->entries[idx].copy_cmd); + /* need at least IWL_HCMD_SCRATCHBUF_SIZE copied */ + if (copy_size < IWL_HCMD_SCRATCHBUF_SIZE) { + copy = IWL_HCMD_SCRATCHBUF_SIZE - copy_size; - /* - * since out_cmd will be the source address of the FH, it will write - * the retry count there. So when the user needs to receivce the HCMD - * that corresponds to the response in the response handler, it needs - * to set CMD_WANT_HCMD. - */ - if (cmd->flags & CMD_WANT_HCMD) { - txq->entries[idx].copy_cmd = - kmemdup(out_cmd, cmd_pos, GFP_ATOMIC); - if (unlikely(!txq->entries[idx].copy_cmd)) { - idx = -ENOMEM; - goto out; + if (copy > cmd->len[i]) + copy = cmd->len[i]; + } + + /* copy everything if not nocopy/dup */ + if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY | + IWL_HCMD_DFL_DUP))) + copy = cmd->len[i]; + + if (copy) { + memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy); + cmd_pos += copy; + copy_size += copy; } } @@ -1275,22 +1293,35 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence), cmd_size, q->write_ptr, idx, trans_pcie->cmd_queue); - phys_addr = dma_map_single(trans->dev, &out_cmd->hdr, copy_size, - DMA_BIDIRECTIONAL); - if (unlikely(dma_mapping_error(trans->dev, phys_addr))) { - idx = -ENOMEM; - goto out; - } - - dma_unmap_addr_set(out_meta, mapping, phys_addr); - dma_unmap_len_set(out_meta, len, copy_size); + /* start the TFD with the scratchbuf */ + scratch_size = min_t(int, copy_size, IWL_HCMD_SCRATCHBUF_SIZE); + memcpy(&txq->scratchbufs[q->write_ptr], &out_cmd->hdr, scratch_size); + iwl_pcie_txq_build_tfd(trans, txq, + iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr), + scratch_size, 1); + + /* map first command fragment, if any remains */ + if (copy_size > scratch_size) { + phys_addr = dma_map_single(trans->dev, + ((u8 *)&out_cmd->hdr) + scratch_size, + copy_size - scratch_size, + DMA_TO_DEVICE); + if (dma_mapping_error(trans->dev, phys_addr)) { + iwl_pcie_tfd_unmap(trans, out_meta, + &txq->tfds[q->write_ptr]); + idx = -ENOMEM; + goto out; + } - iwl_pcie_txq_build_tfd(trans, txq, phys_addr, copy_size, 1); + iwl_pcie_txq_build_tfd(trans, txq, phys_addr, + copy_size - scratch_size, 0); + } - for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { - const void *data = cmd->data[i]; + /* map the remaining (adjusted) nocopy/dup fragments */ + for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { + const void *data = cmddata[i]; - if (!cmd->len[i]) + if (!cmdlen[i]) continue; if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY | IWL_HCMD_DFL_DUP))) @@ -1298,16 +1329,15 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP) data = dup_buf; phys_addr = dma_map_single(trans->dev, (void *)data, - cmd->len[i], DMA_BIDIRECTIONAL); + cmdlen[i], DMA_TO_DEVICE); if (dma_mapping_error(trans->dev, phys_addr)) { iwl_pcie_tfd_unmap(trans, out_meta, - &txq->tfds[q->write_ptr], - DMA_BIDIRECTIONAL); + &txq->tfds[q->write_ptr]); idx = -ENOMEM; goto out; } - iwl_pcie_txq_build_tfd(trans, txq, phys_addr, cmd->len[i], 0); + iwl_pcie_txq_build_tfd(trans, txq, phys_addr, cmdlen[i], 0); } out_meta->flags = cmd->flags; @@ -1317,8 +1347,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, txq->need_update = 1; - trace_iwlwifi_dev_hcmd(trans->dev, cmd, cmd_size, - &out_cmd->hdr, copy_size); + trace_iwlwifi_dev_hcmd(trans->dev, cmd, cmd_size, &out_cmd->hdr); /* start timer if queue currently empty */ if (q->read_ptr == q->write_ptr && trans_pcie->wd_timeout) @@ -1377,7 +1406,7 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, cmd = txq->entries[cmd_index].cmd; meta = &txq->entries[cmd_index].meta; - iwl_pcie_tfd_unmap(trans, meta, &txq->tfds[index], DMA_BIDIRECTIONAL); + iwl_pcie_tfd_unmap(trans, meta, &txq->tfds[index]); /* Input error checking is done when commands are added to queue. */ if (meta->flags & CMD_WANT_SKB) { @@ -1556,10 +1585,9 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_cmd_meta *out_meta; struct iwl_txq *txq; struct iwl_queue *q; - dma_addr_t phys_addr = 0; - dma_addr_t txcmd_phys; - dma_addr_t scratch_phys; - u16 len, firstlen, secondlen; + dma_addr_t tb0_phys, tb1_phys, scratch_phys; + void *tb1_addr; + u16 len, tb1_len, tb2_len; u8 wait_write_ptr = 0; __le16 fc = hdr->frame_control; u8 hdr_len = ieee80211_hdrlen(fc); @@ -1597,85 +1625,80 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | INDEX_TO_SEQ(q->write_ptr))); + tb0_phys = iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr); + scratch_phys = tb0_phys + sizeof(struct iwl_cmd_header) + + offsetof(struct iwl_tx_cmd, scratch); + + tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); + tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys); + /* Set up first empty entry in queue's array of Tx/cmd buffers */ out_meta = &txq->entries[q->write_ptr].meta; /* - * Use the first empty entry in this queue's command buffer array - * to contain the Tx command and MAC header concatenated together - * (payload data will be in another buffer). - * Size of this varies, due to varying MAC header length. - * If end is not dword aligned, we'll have 2 extra bytes at the end - * of the MAC header (device reads on dword boundaries). - * We'll tell device about this padding later. + * The second TB (tb1) points to the remainder of the TX command + * and the 802.11 header - dword aligned size + * (This calculation modifies the TX command, so do it before the + * setup of the first TB) */ - len = sizeof(struct iwl_tx_cmd) + - sizeof(struct iwl_cmd_header) + hdr_len; - firstlen = (len + 3) & ~3; + len = sizeof(struct iwl_tx_cmd) + sizeof(struct iwl_cmd_header) + + hdr_len - IWL_HCMD_SCRATCHBUF_SIZE; + tb1_len = (len + 3) & ~3; /* Tell NIC about any 2-byte padding after MAC header */ - if (firstlen != len) + if (tb1_len != len) tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK; - /* Physical address of this Tx command's header (not MAC header!), - * within command buffer array. */ - txcmd_phys = dma_map_single(trans->dev, - &dev_cmd->hdr, firstlen, - DMA_BIDIRECTIONAL); - if (unlikely(dma_mapping_error(trans->dev, txcmd_phys))) - goto out_err; - dma_unmap_addr_set(out_meta, mapping, txcmd_phys); - dma_unmap_len_set(out_meta, len, firstlen); + /* The first TB points to the scratchbuf data - min_copy bytes */ + memcpy(&txq->scratchbufs[q->write_ptr], &dev_cmd->hdr, + IWL_HCMD_SCRATCHBUF_SIZE); + iwl_pcie_txq_build_tfd(trans, txq, tb0_phys, + IWL_HCMD_SCRATCHBUF_SIZE, 1); - if (!ieee80211_has_morefrags(fc)) { - txq->need_update = 1; - } else { - wait_write_ptr = 1; - txq->need_update = 0; - } + /* there must be data left over for TB1 or this code must be changed */ + BUILD_BUG_ON(sizeof(struct iwl_tx_cmd) < IWL_HCMD_SCRATCHBUF_SIZE); + + /* map the data for TB1 */ + tb1_addr = ((u8 *)&dev_cmd->hdr) + IWL_HCMD_SCRATCHBUF_SIZE; + tb1_phys = dma_map_single(trans->dev, tb1_addr, tb1_len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(trans->dev, tb1_phys))) + goto out_err; + iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, 0); - /* Set up TFD's 2nd entry to point directly to remainder of skb, - * if any (802.11 null frames have no payload). */ - secondlen = skb->len - hdr_len; - if (secondlen > 0) { - phys_addr = dma_map_single(trans->dev, skb->data + hdr_len, - secondlen, DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(trans->dev, phys_addr))) { - dma_unmap_single(trans->dev, - dma_unmap_addr(out_meta, mapping), - dma_unmap_len(out_meta, len), - DMA_BIDIRECTIONAL); + /* + * Set up TFD's third entry to point directly to remainder + * of skb, if any (802.11 null frames have no payload). + */ + tb2_len = skb->len - hdr_len; + if (tb2_len > 0) { + dma_addr_t tb2_phys = dma_map_single(trans->dev, + skb->data + hdr_len, + tb2_len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(trans->dev, tb2_phys))) { + iwl_pcie_tfd_unmap(trans, out_meta, + &txq->tfds[q->write_ptr]); goto out_err; } + iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, 0); } - /* Attach buffers to TFD */ - iwl_pcie_txq_build_tfd(trans, txq, txcmd_phys, firstlen, 1); - if (secondlen > 0) - iwl_pcie_txq_build_tfd(trans, txq, phys_addr, secondlen, 0); - - scratch_phys = txcmd_phys + sizeof(struct iwl_cmd_header) + - offsetof(struct iwl_tx_cmd, scratch); - - /* take back ownership of DMA buffer to enable update */ - dma_sync_single_for_cpu(trans->dev, txcmd_phys, firstlen, - DMA_BIDIRECTIONAL); - tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); - tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys); - /* Set up entry for this TFD in Tx byte-count array */ iwl_pcie_txq_update_byte_cnt_tbl(trans, txq, le16_to_cpu(tx_cmd->len)); - dma_sync_single_for_device(trans->dev, txcmd_phys, firstlen, - DMA_BIDIRECTIONAL); - trace_iwlwifi_dev_tx(trans->dev, skb, &txq->tfds[txq->q.write_ptr], sizeof(struct iwl_tfd), - &dev_cmd->hdr, firstlen, - skb->data + hdr_len, secondlen); + &dev_cmd->hdr, IWL_HCMD_SCRATCHBUF_SIZE + tb1_len, + skb->data + hdr_len, tb2_len); trace_iwlwifi_dev_tx_data(trans->dev, skb, - skb->data + hdr_len, secondlen); + skb->data + hdr_len, tb2_len); + + if (!ieee80211_has_morefrags(fc)) { + txq->need_update = 1; + } else { + wait_write_ptr = 1; + txq->need_update = 0; + } /* start timer if queue currently empty */ if (txq->need_update && q->read_ptr == q->write_ptr && |