26952 lines
790 KiB
Diff
26952 lines
790 KiB
Diff
diff --git a/MAINTAINERS b/MAINTAINERS
|
|
index d3a0684..547e8b3 100644
|
|
--- a/MAINTAINERS
|
|
+++ b/MAINTAINERS
|
|
@@ -2051,6 +2051,16 @@ L: http://lists.sourceforge.net/mailman/listinfo/ipw2100-devel
|
|
W: http://ipw2200.sourceforge.net
|
|
S: Supported
|
|
|
|
+INTEL WIRELESS WIFI LINK (iwlwifi)
|
|
+P: Zhu Yi
|
|
+M: yi.zhu@intel.com
|
|
+L: linux-wireless@vger.kernel.org
|
|
+L: ipw3945-devel@lists.sourceforge.net
|
|
+L: http://lists.sourceforge.net/mailman/listinfo/ipw3945-devel
|
|
+W: http://intellinuxwireless.org
|
|
+T: git git://intellinuxwireless.org/repos/iwlwifi
|
|
+S: Supported
|
|
+
|
|
IOC3 DRIVER
|
|
P: Ralf Baechle
|
|
M: ralf@linux-mips.org
|
|
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
|
|
index ae27af0..4b7c656 100644
|
|
--- a/drivers/net/wireless/Kconfig
|
|
+++ b/drivers/net/wireless/Kconfig
|
|
@@ -558,6 +558,135 @@ config RTL8187
|
|
|
|
Thanks to Realtek for their support!
|
|
|
|
+config IWLWIFI
|
|
+ bool "Intel Wireless WiFi Link Drivers"
|
|
+ depends on PCI && MAC80211 && WLAN_80211 && EXPERIMENTAL
|
|
+ select FW_LOADER
|
|
+ default n
|
|
+ ---help---
|
|
+ Select to enable drivers based on the iwlwifi project. This
|
|
+ project provides a common foundation for Intel's wireless
|
|
+ drivers designed to use the mac80211 subsystem.
|
|
+
|
|
+ See <file:Documentation/networking/README.iwlwifi> for
|
|
+ information on the capabilities currently enabled in this
|
|
+ driver and for tips for debugging issues and problems.
|
|
+
|
|
+config IWLWIFI_DEBUG
|
|
+ bool "Enable full debugging output in iwlwifi drivers"
|
|
+ depends on IWLWIFI
|
|
+ default y
|
|
+ ---help---
|
|
+ This option will enable debug tracing output for the iwlwifi
|
|
+ drivers.
|
|
+
|
|
+ This will result in the kernel module being ~100k larger. You can
|
|
+ control which debug output is sent to the kernel log by setting the
|
|
+ value in
|
|
+
|
|
+ /sys/bus/pci/drivers/${DRIVER}/debug_level
|
|
+
|
|
+ This entry will only exist if this option is enabled.
|
|
+
|
|
+ To set a value, simply echo an 8-byte hex value to the same file:
|
|
+
|
|
+ % echo 0x43fff > /sys/bus/pci/drivers/${DRIVER}/debug_level
|
|
+
|
|
+ You can find the list of debug mask values in:
|
|
+ drivers/net/wireless/mac80211/iwlwifi/iwl-debug.h
|
|
+
|
|
+ If this is your first time using this driver, you should say Y here
|
|
+ as the debug information can assist others in helping you resolve
|
|
+ any problems you may encounter.
|
|
+
|
|
+config IWLWIFI_SENSITIVITY
|
|
+ bool "Enable Sensitivity Calibration in iwlwifi drivers"
|
|
+ depends on IWLWIFI
|
|
+ default y
|
|
+ ---help---
|
|
+ This option will enable sensitivity calibration for the iwlwifi
|
|
+ drivers.
|
|
+
|
|
+config IWLWIFI_SPECTRUM_MEASUREMENT
|
|
+ bool "Enable Spectrum Measurement in iwlwifi drivers"
|
|
+ depends on IWLWIFI
|
|
+ default y
|
|
+ ---help---
|
|
+ This option will enable spectrum measurement for the iwlwifi drivers.
|
|
+
|
|
+config IWLWIFI_QOS
|
|
+ bool "Enable Wireless QoS in iwlwifi drivers"
|
|
+ depends on IWLWIFI
|
|
+ default y
|
|
+ ---help---
|
|
+ This option will enable wireless quality of service (QoS) for the
|
|
+ iwlwifi drivers.
|
|
+
|
|
+config IWLWIFI_HT
|
|
+ bool "Enable 802.11n HT features in iwlwifi drivers"
|
|
+ depends on EXPERIMENTAL
|
|
+ depends on IWLWIFI && MAC80211_HT
|
|
+ default n
|
|
+ ---help---
|
|
+ This option enables IEEE 802.11n High Throughput features
|
|
+ for the iwlwifi drivers.
|
|
+
|
|
+config IWL4965
|
|
+ tristate "Intel Wireless WiFi 4965AGN"
|
|
+ depends on m && IWLWIFI && EXPERIMENTAL
|
|
+ default m
|
|
+ ---help---
|
|
+ Select to build the driver supporting the:
|
|
+
|
|
+ Intel Wireless WiFi Link 4965AGN
|
|
+
|
|
+ This driver uses the kernel's mac80211 subsystem.
|
|
+
|
|
+ See <file:Documentation/networking/README.iwlwifi> for
|
|
+ information on the capabilities currently enabled in this
|
|
+ driver and for tips for debugging any issues or problems.
|
|
+
|
|
+ In order to use this driver, you will need a microcode (uCode)
|
|
+ image for it. You can obtain the microcode from:
|
|
+
|
|
+ <http://intellinuxwireless.org/>.
|
|
+
|
|
+ See the above referenced README.iwlwifi for information on where
|
|
+ to install the microcode images.
|
|
+
|
|
+ If you want to compile the driver as a module ( = code which can be
|
|
+ inserted in and remvoed from the running kernel whenever you want),
|
|
+ say M here and read <file:Documentation/modules.txt>. The module
|
|
+ will be called iwl4965.ko.
|
|
+
|
|
+config IWL3945
|
|
+ tristate "Intel PRO/Wireless 3945ABG/BG Network Connection"
|
|
+ depends on m && IWLWIFI && EXPERIMENTAL
|
|
+ default m
|
|
+ ---help---
|
|
+ Select to build the driver supporting the:
|
|
+
|
|
+ Intel PRO/Wireless 3945ABG/BG Network Connection
|
|
+
|
|
+ This driver uses the kernel's mac80211 subsystem.
|
|
+
|
|
+ See <file:Documentation/networking/README.iwlwifi> for
|
|
+ information on the capabilities currently enabled in this
|
|
+ driver and for tips for debugging any issues or problems.
|
|
+
|
|
+ In order to use this driver, you will need a microcode (uCode)
|
|
+ image for it. You can obtain the microcode from:
|
|
+
|
|
+ <http://intellinuxwireless.org/>.
|
|
+
|
|
+ See the above referenced README.iwlwifi for information on where
|
|
+ to install the microcode images.
|
|
+
|
|
+ If you want to compile the driver as a module ( = code which can be
|
|
+ inserted in and remvoed from the running kernel whenever you want),
|
|
+ say M here and read <file:Documentation/modules.txt>. The module
|
|
+ will be called iwl3945.ko.
|
|
+
|
|
source "drivers/net/wireless/hostap/Kconfig"
|
|
source "drivers/net/wireless/bcm43xx/Kconfig"
|
|
source "drivers/net/wireless/zd1211rw/Kconfig"
|
|
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
|
|
index ef35bc6..1245b5c 100644
|
|
--- a/drivers/net/wireless/Makefile
|
|
+++ b/drivers/net/wireless/Makefile
|
|
@@ -47,3 +47,32 @@ obj-$(CONFIG_LIBERTAS_USB) += libertas/
|
|
|
|
rtl8187-objs := rtl8187_dev.o rtl8187_rtl8225.o
|
|
obj-$(CONFIG_RTL8187) += rtl8187.o
|
|
+
|
|
+# NOTE: We use common code from iwl-base.c to build driver
|
|
+# specific binaries based on the #define IWL -- the target
|
|
+# setup below creates a specific driver target from iwl-base.c
|
|
+#
|
|
+# NOTE2: iwl-base-XXXX.o has -D"KBUILD_MODNAME=KBUILD_STR(...)" in order to
|
|
+# prevent the following kbuild error:
|
|
+# include/linux/pci.h:603: error: `KBUILD_MODNAME' undeclared (first \
|
|
+# use in this function)
|
|
+#
|
|
+# -jpk
|
|
+
|
|
+obj-$(CONFIG_IWL3945) += iwl3945.o
|
|
+iwl3945-objs = iwl-base-3945.o iwl-3945.o iwl-3945-rs.o
|
|
+CFLAGS_iwl-3945.o = -DIWL=3945
|
|
+CFLAGS_iwl-3945-rs.o = -DIWL=3945
|
|
+CFLAGS_iwl-base-3945.o = -DIWL=3945 -D"KBUILD_MODNAME=KBUILD_STR(iwl3945)"
|
|
+$(obj)/iwl-base-3945.o: $(src)/iwl-base.c FORCE
|
|
+ $(call cmd,force_checksrc)
|
|
+ $(call if_changed_rule,cc_o_c)
|
|
+
|
|
+obj-$(CONFIG_IWL4965) += iwl4965.o
|
|
+iwl4965-objs = iwl-base-4965.o iwl-4965.o iwl-4965-rs.o
|
|
+CFLAGS_iwl-4965.o = -DIWL=4965
|
|
+CFLAGS_iwl-4965-rs.o = -DIWL=4965
|
|
+CFLAGS_iwl-base-4965.o = -DIWL=4965 -D"KBUILD_MODNAME=KBUILD_STR(iwl4965)"
|
|
+$(obj)/iwl-base-4965.o: $(src)/iwl-base.c FORCE
|
|
+ $(call cmd,force_checksrc)
|
|
+ $(call if_changed_rule,cc_o_c)
|
|
diff --git a/drivers/net/wireless/iwl-3945-hw.h b/drivers/net/wireless/iwl-3945-hw.h
|
|
new file mode 100644
|
|
index 0000000..1b5e72f
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/iwl-3945-hw.h
|
|
@@ -0,0 +1,118 @@
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * 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) 2005 - 2007 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 Geeral 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:
|
|
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
|
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
+ *
|
|
+ * BSD LICENSE
|
|
+ *
|
|
+ * Copyright(c) 2005 - 2007 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_3945_hw__
|
|
+#define __iwl_3945_hw__
|
|
+
|
|
+#define IWL_RX_BUF_SIZE 3000
|
|
+/* card static random access memory (SRAM) for processor data and instructs */
|
|
+#define ALM_RTC_INST_UPPER_BOUND (0x014000)
|
|
+#define ALM_RTC_DATA_UPPER_BOUND (0x808000)
|
|
+
|
|
+#define ALM_RTC_INST_SIZE (ALM_RTC_INST_UPPER_BOUND - RTC_INST_LOWER_BOUND)
|
|
+#define ALM_RTC_DATA_SIZE (ALM_RTC_DATA_UPPER_BOUND - RTC_DATA_LOWER_BOUND)
|
|
+
|
|
+#define IWL_MAX_BSM_SIZE ALM_RTC_INST_SIZE
|
|
+#define IWL_MAX_INST_SIZE ALM_RTC_INST_SIZE
|
|
+#define IWL_MAX_DATA_SIZE ALM_RTC_DATA_SIZE
|
|
+#define IWL_MAX_NUM_QUEUES 8
|
|
+
|
|
+static inline int iwl_hw_valid_rtc_data_addr(u32 addr)
|
|
+{
|
|
+ return ((addr >= RTC_DATA_LOWER_BOUND)
|
|
+ && (addr < ALM_RTC_DATA_UPPER_BOUND));
|
|
+}
|
|
+
|
|
+/* Base physical address of iwl_shared is provided to FH_TSSR_CBB_BASE
|
|
+ * and &iwl_shared.rx_read_ptr[0] is provided to FH_RCSR_RPTR_ADDR(0) */
|
|
+struct iwl_shared {
|
|
+ __le32 tx_base_ptr[8];
|
|
+ __le32 rx_read_ptr[3];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct iwl_tfd_frame_data {
|
|
+ __le32 addr;
|
|
+ __le32 len;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct iwl_tfd_frame {
|
|
+ __le32 control_flags;
|
|
+ struct iwl_tfd_frame_data pa[4];
|
|
+ u8 reserved[28];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+static inline u8 iwl_hw_get_rate(__le16 rate_n_flags)
|
|
+{
|
|
+ return le16_to_cpu(rate_n_flags) & 0xFF;
|
|
+}
|
|
+
|
|
+static inline u16 iwl_hw_get_rate_n_flags(__le16 rate_n_flags)
|
|
+{
|
|
+ return le16_to_cpu(rate_n_flags);
|
|
+}
|
|
+
|
|
+static inline __le16 iwl_hw_set_rate_n_flags(u8 rate, u16 flags)
|
|
+{
|
|
+ return cpu_to_le16((u16)rate|flags);
|
|
+}
|
|
+#endif
|
|
diff --git a/drivers/net/wireless/iwl-3945-rs.c b/drivers/net/wireless/iwl-3945-rs.c
|
|
new file mode 100644
|
|
index 0000000..2eee6c5
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/iwl-3945-rs.c
|
|
@@ -0,0 +1,984 @@
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * Copyright(c) 2005 - 2007 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.
|
|
+ *
|
|
+ * Contact Information:
|
|
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
|
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/skbuff.h>
|
|
+#include <linux/wireless.h>
|
|
+#include <net/mac80211.h>
|
|
+#include <net/ieee80211.h>
|
|
+
|
|
+#include <linux/netdevice.h>
|
|
+#include <linux/etherdevice.h>
|
|
+#include <linux/delay.h>
|
|
+
|
|
+#include <linux/workqueue.h>
|
|
+
|
|
+#include <net/mac80211.h>
|
|
+#include <linux/wireless.h>
|
|
+
|
|
+#include "../net/mac80211/ieee80211_rate.h"
|
|
+
|
|
+#include "iwl-3945-rs.h"
|
|
+#include "iwlwifi.h"
|
|
+#include "iwl-helpers.h"
|
|
+
|
|
+#define RS_NAME "iwl-3945-rs"
|
|
+
|
|
+struct iwl_rate_scale_data {
|
|
+ u64 data;
|
|
+ s32 success_counter;
|
|
+ s32 success_ratio;
|
|
+ s32 counter;
|
|
+ s32 average_tpt;
|
|
+ unsigned long stamp;
|
|
+};
|
|
+
|
|
+struct iwl_rate_scale_priv {
|
|
+ spinlock_t lock;
|
|
+ s32 *expected_tpt;
|
|
+ unsigned long last_partial_flush;
|
|
+ unsigned long last_flush;
|
|
+ u8 flush_pending;
|
|
+ u32 flush_time;
|
|
+ u32 last_tx_packets;
|
|
+ u8 tgg;
|
|
+ u32 tx_packets;
|
|
+ u8 start_rate;
|
|
+ u8 ibss_sta_added;
|
|
+ struct timer_list rate_scale_flush;
|
|
+ struct iwl_rate_scale_data win[IWL_RATE_COUNT];
|
|
+};
|
|
+
|
|
+static s32 iwl_expected_tpt_g[IWL_RATE_COUNT] = {
|
|
+ 0, 0, 76, 104, 130, 168, 191, 202, 7, 13, 35, 58
|
|
+};
|
|
+
|
|
+static s32 iwl_expected_tpt_g_prot[IWL_RATE_COUNT] = {
|
|
+ 0, 0, 0, 80, 93, 113, 123, 125, 7, 13, 35, 58
|
|
+};
|
|
+
|
|
+static s32 iwl_expected_tpt_a[IWL_RATE_COUNT] = {
|
|
+ 40, 57, 72, 98, 121, 154, 177, 186, 0, 0, 0, 0
|
|
+};
|
|
+
|
|
+static s32 iwl_expected_tpt_b[IWL_RATE_COUNT] = {
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 7, 13, 35, 58
|
|
+};
|
|
+
|
|
+struct iwl_tpt_entry {
|
|
+ s8 min_rssi;
|
|
+ u8 index;
|
|
+};
|
|
+
|
|
+static struct iwl_tpt_entry iwl_tpt_table_a[] = {
|
|
+ {-60, IWL_RATE_54M_INDEX},
|
|
+ {-64, IWL_RATE_48M_INDEX},
|
|
+ {-72, IWL_RATE_36M_INDEX},
|
|
+ {-80, IWL_RATE_24M_INDEX},
|
|
+ {-84, IWL_RATE_18M_INDEX},
|
|
+ {-85, IWL_RATE_12M_INDEX},
|
|
+ {-87, IWL_RATE_9M_INDEX},
|
|
+ {-89, IWL_RATE_6M_INDEX}
|
|
+};
|
|
+
|
|
+static struct iwl_tpt_entry iwl_tpt_table_b[] = {
|
|
+ {-86, IWL_RATE_11M_INDEX},
|
|
+ {-88, IWL_RATE_5M_INDEX},
|
|
+ {-90, IWL_RATE_2M_INDEX},
|
|
+ {-92, IWL_RATE_1M_INDEX}
|
|
+
|
|
+};
|
|
+
|
|
+static struct iwl_tpt_entry iwl_tpt_table_g[] = {
|
|
+ {-60, IWL_RATE_54M_INDEX},
|
|
+ {-64, IWL_RATE_48M_INDEX},
|
|
+ {-68, IWL_RATE_36M_INDEX},
|
|
+ {-80, IWL_RATE_24M_INDEX},
|
|
+ {-84, IWL_RATE_18M_INDEX},
|
|
+ {-85, IWL_RATE_12M_INDEX},
|
|
+ {-86, IWL_RATE_11M_INDEX},
|
|
+ {-88, IWL_RATE_5M_INDEX},
|
|
+ {-90, IWL_RATE_2M_INDEX},
|
|
+ {-92, IWL_RATE_1M_INDEX}
|
|
+};
|
|
+
|
|
+#define IWL_RATE_MAX_WINDOW 62
|
|
+#define IWL_RATE_FLUSH (3*HZ/10)
|
|
+#define IWL_RATE_WIN_FLUSH (HZ/2)
|
|
+#define IWL_RATE_HIGH_TH 11520
|
|
+#define IWL_RATE_MIN_FAILURE_TH 8
|
|
+#define IWL_RATE_MIN_SUCCESS_TH 8
|
|
+#define IWL_RATE_DECREASE_TH 1920
|
|
+
|
|
+static u8 iwl_get_rate_index_by_rssi(s32 rssi, u8 mode)
|
|
+{
|
|
+ u32 index = 0;
|
|
+ u32 table_size = 0;
|
|
+ struct iwl_tpt_entry *tpt_table = NULL;
|
|
+
|
|
+ if ((rssi < IWL_MIN_RSSI_VAL) || (rssi > IWL_MAX_RSSI_VAL))
|
|
+ rssi = IWL_MIN_RSSI_VAL;
|
|
+
|
|
+ switch (mode) {
|
|
+ case MODE_IEEE80211G:
|
|
+ tpt_table = iwl_tpt_table_g;
|
|
+ table_size = ARRAY_SIZE(iwl_tpt_table_g);
|
|
+ break;
|
|
+
|
|
+ case MODE_IEEE80211A:
|
|
+ tpt_table = iwl_tpt_table_a;
|
|
+ table_size = ARRAY_SIZE(iwl_tpt_table_a);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ case MODE_IEEE80211B:
|
|
+ tpt_table = iwl_tpt_table_b;
|
|
+ table_size = ARRAY_SIZE(iwl_tpt_table_b);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ while ((index < table_size) && (rssi < tpt_table[index].min_rssi))
|
|
+ index++;
|
|
+
|
|
+ index = min(index, (table_size - 1));
|
|
+
|
|
+ return tpt_table[index].index;
|
|
+}
|
|
+
|
|
+static void iwl_clear_window(struct iwl_rate_scale_data *window)
|
|
+{
|
|
+ window->data = 0;
|
|
+ window->success_counter = 0;
|
|
+ window->success_ratio = IWL_INVALID_VALUE;
|
|
+ window->counter = 0;
|
|
+ window->average_tpt = IWL_INVALID_VALUE;
|
|
+ window->stamp = 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_rate_scale_flush_windows - flush out the rate scale windows
|
|
+ *
|
|
+ * Returns the number of windows that have gathered data but were
|
|
+ * not flushed. If there were any that were not flushed, then
|
|
+ * reschedule the rate flushing routine.
|
|
+ */
|
|
+static int iwl_rate_scale_flush_windows(struct iwl_rate_scale_priv *rs_priv)
|
|
+{
|
|
+ int unflushed = 0;
|
|
+ int i;
|
|
+ unsigned long flags;
|
|
+
|
|
+ /*
|
|
+ * For each rate, if we have collected data on that rate
|
|
+ * and it has been more than IWL_RATE_WIN_FLUSH
|
|
+ * since we flushed, clear out the gathered statistics
|
|
+ */
|
|
+ for (i = 0; i < IWL_RATE_COUNT; i++) {
|
|
+ if (!rs_priv->win[i].counter)
|
|
+ continue;
|
|
+
|
|
+ spin_lock_irqsave(&rs_priv->lock, flags);
|
|
+ if (time_after(jiffies, rs_priv->win[i].stamp +
|
|
+ IWL_RATE_WIN_FLUSH)) {
|
|
+ IWL_DEBUG_RATE("flushing %d samples of rate "
|
|
+ "index %d\n",
|
|
+ rs_priv->win[i].counter, i);
|
|
+ iwl_clear_window(&rs_priv->win[i]);
|
|
+ } else
|
|
+ unflushed++;
|
|
+ spin_unlock_irqrestore(&rs_priv->lock, flags);
|
|
+ }
|
|
+
|
|
+ return unflushed;
|
|
+}
|
|
+
|
|
+#define IWL_RATE_FLUSH_MAX 5000 /* msec */
|
|
+#define IWL_RATE_FLUSH_MIN 50 /* msec */
|
|
+
|
|
+static void iwl_bg_rate_scale_flush(unsigned long data)
|
|
+{
|
|
+ struct iwl_rate_scale_priv *rs_priv = (void *)data;
|
|
+ int unflushed = 0;
|
|
+ unsigned long flags;
|
|
+ u32 packet_count, duration, pps;
|
|
+
|
|
+ IWL_DEBUG_RATE("enter\n");
|
|
+
|
|
+ unflushed = iwl_rate_scale_flush_windows(rs_priv);
|
|
+
|
|
+ spin_lock_irqsave(&rs_priv->lock, flags);
|
|
+
|
|
+ rs_priv->flush_pending = 0;
|
|
+
|
|
+ /* Number of packets Rx'd since last time this timer ran */
|
|
+ packet_count = (rs_priv->tx_packets - rs_priv->last_tx_packets) + 1;
|
|
+
|
|
+ rs_priv->last_tx_packets = rs_priv->tx_packets + 1;
|
|
+
|
|
+ if (unflushed) {
|
|
+ duration =
|
|
+ jiffies_to_msecs(jiffies - rs_priv->last_partial_flush);
|
|
+/* duration = jiffies_to_msecs(rs_priv->flush_time); */
|
|
+
|
|
+ IWL_DEBUG_RATE("Tx'd %d packets in %dms\n",
|
|
+ packet_count, duration);
|
|
+
|
|
+ /* Determine packets per second */
|
|
+ if (duration)
|
|
+ pps = (packet_count * 1000) / duration;
|
|
+ else
|
|
+ pps = 0;
|
|
+
|
|
+ if (pps) {
|
|
+ duration = IWL_RATE_FLUSH_MAX / pps;
|
|
+ if (duration < IWL_RATE_FLUSH_MIN)
|
|
+ duration = IWL_RATE_FLUSH_MIN;
|
|
+ } else
|
|
+ duration = IWL_RATE_FLUSH_MAX;
|
|
+
|
|
+ rs_priv->flush_time = msecs_to_jiffies(duration);
|
|
+
|
|
+ IWL_DEBUG_RATE("new flush period: %d msec ave %d\n",
|
|
+ duration, packet_count);
|
|
+
|
|
+ mod_timer(&rs_priv->rate_scale_flush, jiffies +
|
|
+ rs_priv->flush_time);
|
|
+
|
|
+ rs_priv->last_partial_flush = jiffies;
|
|
+ }
|
|
+
|
|
+ /* If there weren't any unflushed entries, we don't schedule the timer
|
|
+ * to run again */
|
|
+
|
|
+ rs_priv->last_flush = jiffies;
|
|
+
|
|
+ spin_unlock_irqrestore(&rs_priv->lock, flags);
|
|
+
|
|
+ IWL_DEBUG_RATE("leave\n");
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_collect_tx_data - Update the success/failure sliding window
|
|
+ *
|
|
+ * We keep a sliding window of the last 64 packets transmitted
|
|
+ * at this rate. window->data contains the bitmask of successful
|
|
+ * packets.
|
|
+ */
|
|
+static void iwl_collect_tx_data(struct iwl_rate_scale_priv *rs_priv,
|
|
+ struct iwl_rate_scale_data *window,
|
|
+ int success, int retries)
|
|
+{
|
|
+ unsigned long flags;
|
|
+
|
|
+ if (!retries) {
|
|
+ IWL_DEBUG_RATE("leave: retries == 0 -- should be at "
|
|
+ "least 1\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ while (retries--) {
|
|
+ spin_lock_irqsave(&rs_priv->lock, flags);
|
|
+
|
|
+ /* If we have filled up the window then subtract one from the
|
|
+ * success counter if the high-bit is counting toward
|
|
+ * success */
|
|
+ if (window->counter == IWL_RATE_MAX_WINDOW) {
|
|
+ if (window->data & (1ULL << (IWL_RATE_MAX_WINDOW - 1)))
|
|
+ window->success_counter--;
|
|
+ } else
|
|
+ window->counter++;
|
|
+
|
|
+ /* Slide the window to the left one bit */
|
|
+ window->data = (window->data << 1);
|
|
+
|
|
+ /* If this packet was a success then set the low bit high */
|
|
+ if (success) {
|
|
+ window->success_counter++;
|
|
+ window->data |= 1;
|
|
+ }
|
|
+
|
|
+ /* window->counter can't be 0 -- it is either >0 or
|
|
+ * IWL_RATE_MAX_WINDOW */
|
|
+ window->success_ratio = 12800 * window->success_counter /
|
|
+ window->counter;
|
|
+
|
|
+ /* Tag this window as having been updated */
|
|
+ window->stamp = jiffies;
|
|
+
|
|
+ spin_unlock_irqrestore(&rs_priv->lock, flags);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void rs_rate_init(void *priv_rate, void *priv_sta,
|
|
+ struct ieee80211_local *local, struct sta_info *sta)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ IWL_DEBUG_RATE("enter\n");
|
|
+
|
|
+ /* TODO: what is a good starting rate for STA? About middle? Maybe not
|
|
+ * the lowest or the highest rate.. Could consider using RSSI from
|
|
+ * previous packets? Need to have IEEE 802.1X auth succeed immediately
|
|
+ * after assoc.. */
|
|
+
|
|
+ for (i = IWL_RATE_COUNT - 1; i >= 0; i--) {
|
|
+ if (sta->supp_rates & (1 << i)) {
|
|
+ sta->txrate = i;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ sta->last_txrate = sta->txrate;
|
|
+
|
|
+ IWL_DEBUG_RATE("leave\n");
|
|
+}
|
|
+
|
|
+static void *rs_alloc(struct ieee80211_local *local)
|
|
+{
|
|
+ IWL_DEBUG_RATE("enter\n");
|
|
+ IWL_DEBUG_RATE("leave\n");
|
|
+ return local->hw.priv;
|
|
+}
|
|
+
|
|
+static void rs_free(void *data)
|
|
+{
|
|
+ IWL_DEBUG_RATE("enter\n");
|
|
+ IWL_DEBUG_RATE("leave\n");
|
|
+}
|
|
+
|
|
+static void *rs_alloc_sta(void *priv, gfp_t gfp)
|
|
+{
|
|
+ struct iwl_rate_scale_priv *rs_priv;
|
|
+ int i;
|
|
+
|
|
+ IWL_DEBUG_RATE("enter\n");
|
|
+
|
|
+ rs_priv = kzalloc(sizeof(struct iwl_rate_scale_priv), gfp);
|
|
+ if (!rs_priv) {
|
|
+ IWL_DEBUG_RATE("leave: ENOMEM\n");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ spin_lock_init(&rs_priv->lock);
|
|
+
|
|
+ rs_priv->start_rate = IWL_RATE_INVALID;
|
|
+
|
|
+ /* default to just 802.11b */
|
|
+ rs_priv->expected_tpt = iwl_expected_tpt_b;
|
|
+
|
|
+ rs_priv->last_partial_flush = jiffies;
|
|
+ rs_priv->last_flush = jiffies;
|
|
+ rs_priv->flush_time = IWL_RATE_FLUSH;
|
|
+ rs_priv->last_tx_packets = 0;
|
|
+ rs_priv->ibss_sta_added = 0;
|
|
+
|
|
+ init_timer(&rs_priv->rate_scale_flush);
|
|
+ rs_priv->rate_scale_flush.data = (unsigned long)rs_priv;
|
|
+ rs_priv->rate_scale_flush.function = &iwl_bg_rate_scale_flush;
|
|
+
|
|
+ for (i = 0; i < IWL_RATE_COUNT; i++)
|
|
+ iwl_clear_window(&rs_priv->win[i]);
|
|
+
|
|
+ IWL_DEBUG_RATE("leave\n");
|
|
+
|
|
+ return rs_priv;
|
|
+}
|
|
+
|
|
+static void rs_free_sta(void *priv, void *priv_sta)
|
|
+{
|
|
+ struct iwl_rate_scale_priv *rs_priv = priv_sta;
|
|
+
|
|
+ IWL_DEBUG_RATE("enter\n");
|
|
+ del_timer_sync(&rs_priv->rate_scale_flush);
|
|
+ kfree(rs_priv);
|
|
+ IWL_DEBUG_RATE("leave\n");
|
|
+}
|
|
+
|
|
+static void rs_clear(void *priv)
|
|
+{
|
|
+ IWL_DEBUG_RATE("NOP\n");
|
|
+}
|
|
+
|
|
+/**
|
|
+ * rs_tx_status - Update rate control values based on Tx results
|
|
+ *
|
|
+ * NOTE: Uses iwl_priv->retry_rate for the # of retries attempted by
|
|
+ * the hardware for each rate.
|
|
+ */
|
|
+static void rs_tx_status(void *priv_rate,
|
|
+ struct net_device *dev,
|
|
+ struct sk_buff *skb,
|
|
+ struct ieee80211_tx_status *tx_resp)
|
|
+{
|
|
+ u8 retries, current_count;
|
|
+ int scale_rate_index, first_index, last_index;
|
|
+ unsigned long flags;
|
|
+ struct sta_info *sta;
|
|
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
|
+ struct iwl_priv *priv = (struct iwl_priv *)priv_rate;
|
|
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
+ struct iwl_rate_scale_priv *rs_priv;
|
|
+
|
|
+ IWL_DEBUG_RATE("enter\n");
|
|
+
|
|
+ retries = tx_resp->retry_count;
|
|
+
|
|
+ first_index = tx_resp->control.tx_rate;
|
|
+ if ((first_index < 0) || (first_index >= IWL_RATE_COUNT)) {
|
|
+ IWL_DEBUG_RATE("leave: Rate out of bounds: %0x for %d\n",
|
|
+ tx_resp->control.tx_rate, first_index);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ sta = sta_info_get(local, hdr->addr1);
|
|
+ if (!sta || !sta->rate_ctrl_priv) {
|
|
+ if (sta)
|
|
+ sta_info_put(sta);
|
|
+ IWL_DEBUG_RATE("leave: No STA priv data to update!\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ rs_priv = (void *)sta->rate_ctrl_priv;
|
|
+
|
|
+ rs_priv->tx_packets++;
|
|
+
|
|
+ scale_rate_index = first_index;
|
|
+ last_index = first_index;
|
|
+
|
|
+ /*
|
|
+ * Update the window for each rate. We determine which rates
|
|
+ * were Tx'd based on the total number of retries vs. the number
|
|
+ * of retries configured for each rate -- currently set to the
|
|
+ * priv value 'retry_rate' vs. rate specific
|
|
+ *
|
|
+ * On exit from this while loop last_index indicates the rate
|
|
+ * at which the frame was finally transmitted (or failed if no
|
|
+ * ACK)
|
|
+ */
|
|
+ while (retries > 0) {
|
|
+ if (retries < priv->retry_rate) {
|
|
+ current_count = retries;
|
|
+ last_index = scale_rate_index;
|
|
+ } else {
|
|
+ current_count = priv->retry_rate;
|
|
+ last_index = iwl_get_prev_ieee_rate(scale_rate_index);
|
|
+ }
|
|
+
|
|
+ /* Update this rate accounting for as many retries
|
|
+ * as was used for it (per current_count) */
|
|
+ iwl_collect_tx_data(rs_priv,
|
|
+ &rs_priv->win[scale_rate_index],
|
|
+ 0, current_count);
|
|
+ IWL_DEBUG_RATE("Update rate %d for %d retries.\n",
|
|
+ scale_rate_index, current_count);
|
|
+
|
|
+ retries -= current_count;
|
|
+
|
|
+ if (retries)
|
|
+ scale_rate_index =
|
|
+ iwl_get_prev_ieee_rate(scale_rate_index);
|
|
+ }
|
|
+
|
|
+ /* Update the last index window with success/failure based on ACK */
|
|
+ IWL_DEBUG_RATE("Update rate %d with %s.\n",
|
|
+ last_index,
|
|
+ (tx_resp->flags & IEEE80211_TX_STATUS_ACK) ?
|
|
+ "success" : "failure");
|
|
+ iwl_collect_tx_data(rs_priv,
|
|
+ &rs_priv->win[last_index],
|
|
+ tx_resp->flags & IEEE80211_TX_STATUS_ACK, 1);
|
|
+
|
|
+ /* We updated the rate scale window -- if its been more than
|
|
+ * flush_time since the last run, schedule the flush
|
|
+ * again */
|
|
+ spin_lock_irqsave(&rs_priv->lock, flags);
|
|
+
|
|
+ if (!rs_priv->flush_pending &&
|
|
+ time_after(jiffies, rs_priv->last_partial_flush +
|
|
+ rs_priv->flush_time)) {
|
|
+
|
|
+ rs_priv->flush_pending = 1;
|
|
+ mod_timer(&rs_priv->rate_scale_flush,
|
|
+ jiffies + rs_priv->flush_time);
|
|
+ }
|
|
+
|
|
+ spin_unlock_irqrestore(&rs_priv->lock, flags);
|
|
+
|
|
+ sta_info_put(sta);
|
|
+
|
|
+ IWL_DEBUG_RATE("leave\n");
|
|
+
|
|
+ return;
|
|
+}
|
|
+
|
|
+static struct ieee80211_rate *iwl_get_lowest_rate(struct ieee80211_local
|
|
+ *local)
|
|
+{
|
|
+ struct ieee80211_hw_mode *mode = local->oper_hw_mode;
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < mode->num_rates; i++) {
|
|
+ struct ieee80211_rate *rate = &mode->rates[i];
|
|
+
|
|
+ if (rate->flags & IEEE80211_RATE_SUPPORTED)
|
|
+ return rate;
|
|
+ }
|
|
+
|
|
+ return &mode->rates[0];
|
|
+}
|
|
+
|
|
+static u16 iwl_get_adjacent_rate(struct iwl_rate_scale_priv *rs_priv,
|
|
+ u8 index, u16 rate_mask, int phymode)
|
|
+{
|
|
+ u8 high = IWL_RATE_INVALID;
|
|
+ u8 low = IWL_RATE_INVALID;
|
|
+
|
|
+ /* 802.11A walks to the next literal adjascent rate in
|
|
+ * the rate table */
|
|
+ if (unlikely(phymode == MODE_IEEE80211A)) {
|
|
+ int i;
|
|
+ u32 mask;
|
|
+
|
|
+ /* Find the previous rate that is in the rate mask */
|
|
+ i = index - 1;
|
|
+ for (mask = (1 << i); i >= 0; i--, mask >>= 1) {
|
|
+ if (rate_mask & mask) {
|
|
+ low = i;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Find the next rate that is in the rate mask */
|
|
+ i = index + 1;
|
|
+ for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) {
|
|
+ if (rate_mask & mask) {
|
|
+ high = i;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return (high << 8) | low;
|
|
+ }
|
|
+
|
|
+ low = index;
|
|
+ while (low != IWL_RATE_INVALID) {
|
|
+ if (rs_priv->tgg)
|
|
+ low = iwl_rates[low].prev_rs_tgg;
|
|
+ else
|
|
+ low = iwl_rates[low].prev_rs;
|
|
+ if (low == IWL_RATE_INVALID)
|
|
+ break;
|
|
+ if (rate_mask & (1 << low))
|
|
+ break;
|
|
+ IWL_DEBUG_RATE("Skipping masked lower rate: %d\n", low);
|
|
+ }
|
|
+
|
|
+ high = index;
|
|
+ while (high != IWL_RATE_INVALID) {
|
|
+ if (rs_priv->tgg)
|
|
+ high = iwl_rates[high].next_rs_tgg;
|
|
+ else
|
|
+ high = iwl_rates[high].next_rs;
|
|
+ if (high == IWL_RATE_INVALID)
|
|
+ break;
|
|
+ if (rate_mask & (1 << high))
|
|
+ break;
|
|
+ IWL_DEBUG_RATE("Skipping masked higher rate: %d\n", high);
|
|
+ }
|
|
+
|
|
+ return (high << 8) | low;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * rs_get_rate - find the rate for the requested packet
|
|
+ *
|
|
+ * Returns the ieee80211_rate structure allocated by the driver.
|
|
+ *
|
|
+ * The rate control algorithm has no internal mapping between hw_mode's
|
|
+ * rate ordering and the rate ordering used by the rate control algorithm.
|
|
+ *
|
|
+ * The rate control algorithm uses a single table of rates that goes across
|
|
+ * the entire A/B/G spectrum vs. being limited to just one particular
|
|
+ * hw_mode.
|
|
+ *
|
|
+ * As such, we can't convert the index obtained below into the hw_mode's
|
|
+ * rate table and must reference the driver allocated rate table
|
|
+ *
|
|
+ */
|
|
+static struct ieee80211_rate *rs_get_rate(void *priv_rate,
|
|
+ struct net_device *dev,
|
|
+ struct sk_buff *skb,
|
|
+ struct rate_control_extra *extra)
|
|
+{
|
|
+ u8 low = IWL_RATE_INVALID;
|
|
+ u8 high = IWL_RATE_INVALID;
|
|
+ u16 high_low;
|
|
+ int index;
|
|
+ struct iwl_rate_scale_priv *rs_priv;
|
|
+ struct iwl_rate_scale_data *window = NULL;
|
|
+ int current_tpt = IWL_INVALID_VALUE;
|
|
+ int low_tpt = IWL_INVALID_VALUE;
|
|
+ int high_tpt = IWL_INVALID_VALUE;
|
|
+ u32 fail_count;
|
|
+ s8 scale_action = 0;
|
|
+ unsigned long flags;
|
|
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
|
+ struct sta_info *sta;
|
|
+ u16 fc, rate_mask;
|
|
+ struct iwl_priv *priv = (struct iwl_priv *)priv_rate;
|
|
+
|
|
+ IWL_DEBUG_RATE("enter\n");
|
|
+
|
|
+ memset(extra, 0, sizeof(*extra));
|
|
+
|
|
+ fc = le16_to_cpu(hdr->frame_control);
|
|
+ if (((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA) ||
|
|
+ (is_multicast_ether_addr(hdr->addr1))) {
|
|
+ /* Send management frames and broadcast/multicast data using
|
|
+ * lowest rate. */
|
|
+ /* TODO: this could probably be improved.. */
|
|
+ IWL_DEBUG_RATE("leave: lowest rate (not data or is "
|
|
+ "multicast)\n");
|
|
+
|
|
+ return iwl_get_lowest_rate(local);
|
|
+ }
|
|
+
|
|
+ sta = sta_info_get(local, hdr->addr1);
|
|
+ if (!sta || !sta->rate_ctrl_priv) {
|
|
+ IWL_DEBUG_RATE("leave: No STA priv data to update!\n");
|
|
+ if (sta)
|
|
+ sta_info_put(sta);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ rate_mask = sta->supp_rates;
|
|
+ index = min(sta->txrate & 0xffff, IWL_RATE_COUNT - 1);
|
|
+
|
|
+ rs_priv = (void *)sta->rate_ctrl_priv;
|
|
+
|
|
+ if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) &&
|
|
+ !rs_priv->ibss_sta_added) {
|
|
+ u8 sta_id = iwl_hw_find_station(priv, hdr->addr1);
|
|
+
|
|
+ if (sta_id == IWL_INVALID_STATION) {
|
|
+ IWL_DEBUG_RATE("LQ: ADD station " MAC_FMT "\n",
|
|
+ MAC_ARG(hdr->addr1));
|
|
+ sta_id = iwl_add_station(priv,
|
|
+ hdr->addr1, 0, CMD_ASYNC);
|
|
+ }
|
|
+ if (sta_id != IWL_INVALID_STATION)
|
|
+ rs_priv->ibss_sta_added = 1;
|
|
+ }
|
|
+
|
|
+ spin_lock_irqsave(&rs_priv->lock, flags);
|
|
+
|
|
+ if (rs_priv->start_rate != IWL_RATE_INVALID) {
|
|
+ index = rs_priv->start_rate;
|
|
+ rs_priv->start_rate = IWL_RATE_INVALID;
|
|
+ }
|
|
+
|
|
+ window = &(rs_priv->win[index]);
|
|
+
|
|
+ fail_count = window->counter - window->success_counter;
|
|
+
|
|
+ if (((fail_count <= IWL_RATE_MIN_FAILURE_TH) &&
|
|
+ (window->success_counter < IWL_RATE_MIN_SUCCESS_TH))) {
|
|
+ window->average_tpt = IWL_INVALID_VALUE;
|
|
+ spin_unlock_irqrestore(&rs_priv->lock, flags);
|
|
+
|
|
+ IWL_DEBUG_RATE("Invalid average_tpt on rate %d: "
|
|
+ "counter: %d, success_counter: %d, "
|
|
+ "expected_tpt is %sNULL\n",
|
|
+ index,
|
|
+ window->counter,
|
|
+ window->success_counter,
|
|
+ rs_priv->expected_tpt ? "not " : "");
|
|
+ goto out;
|
|
+
|
|
+ }
|
|
+
|
|
+ window->average_tpt = ((window->success_ratio *
|
|
+ rs_priv->expected_tpt[index] + 64) / 128);
|
|
+ current_tpt = window->average_tpt;
|
|
+
|
|
+ high_low = iwl_get_adjacent_rate(rs_priv, index, rate_mask,
|
|
+ local->hw.conf.phymode);
|
|
+ low = high_low & 0xff;
|
|
+ high = (high_low >> 8) & 0xff;
|
|
+
|
|
+ if (low != IWL_RATE_INVALID)
|
|
+ low_tpt = rs_priv->win[low].average_tpt;
|
|
+
|
|
+ if (high != IWL_RATE_INVALID)
|
|
+ high_tpt = rs_priv->win[high].average_tpt;
|
|
+
|
|
+ spin_unlock_irqrestore(&rs_priv->lock, flags);
|
|
+
|
|
+ scale_action = 1;
|
|
+
|
|
+ if ((window->success_ratio < IWL_RATE_DECREASE_TH) || !current_tpt) {
|
|
+ IWL_DEBUG_RATE("decrease rate because of low success_ratio\n");
|
|
+ scale_action = -1;
|
|
+ } else if ((low_tpt == IWL_INVALID_VALUE) &&
|
|
+ (high_tpt == IWL_INVALID_VALUE))
|
|
+ scale_action = 1;
|
|
+ else if ((low_tpt != IWL_INVALID_VALUE) &&
|
|
+ (high_tpt != IWL_INVALID_VALUE)
|
|
+ && (low_tpt < current_tpt)
|
|
+ && (high_tpt < current_tpt)) {
|
|
+ IWL_DEBUG_RATE("No action -- low [%d] & high [%d] < "
|
|
+ "current_tpt [%d]\n",
|
|
+ low_tpt, high_tpt, current_tpt);
|
|
+ scale_action = 0;
|
|
+ } else {
|
|
+ if (high_tpt != IWL_INVALID_VALUE) {
|
|
+ if (high_tpt > current_tpt)
|
|
+ scale_action = 1;
|
|
+ else {
|
|
+ IWL_DEBUG_RATE
|
|
+ ("decrease rate because of high tpt\n");
|
|
+ scale_action = -1;
|
|
+ }
|
|
+ } else if (low_tpt != IWL_INVALID_VALUE) {
|
|
+ if (low_tpt > current_tpt) {
|
|
+ IWL_DEBUG_RATE
|
|
+ ("decrease rate because of low tpt\n");
|
|
+ scale_action = -1;
|
|
+ } else
|
|
+ scale_action = 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if ((window->success_ratio > IWL_RATE_HIGH_TH) ||
|
|
+ (current_tpt > window->average_tpt)) {
|
|
+ IWL_DEBUG_RATE("No action -- success_ratio [%d] > HIGH_TH or "
|
|
+ "current_tpt [%d] > average_tpt [%d]\n",
|
|
+ window->success_ratio,
|
|
+ current_tpt, window->average_tpt);
|
|
+ scale_action = 0;
|
|
+ }
|
|
+
|
|
+ switch (scale_action) {
|
|
+ case -1:
|
|
+ if (low != IWL_RATE_INVALID)
|
|
+ index = low;
|
|
+ break;
|
|
+
|
|
+ case 1:
|
|
+ if (high != IWL_RATE_INVALID)
|
|
+ index = high;
|
|
+
|
|
+ break;
|
|
+
|
|
+ case 0:
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_RATE("Selected %d (action %d) - low %d high %d\n",
|
|
+ index, scale_action, low, high);
|
|
+
|
|
+ out:
|
|
+
|
|
+ sta->last_txrate = index;
|
|
+ sta->txrate = sta->last_txrate;
|
|
+ sta_info_put(sta);
|
|
+
|
|
+ IWL_DEBUG_RATE("leave: %d\n", index);
|
|
+
|
|
+ return &priv->ieee_rates[index];
|
|
+}
|
|
+
|
|
+static struct rate_control_ops rs_ops = {
|
|
+ .module = NULL,
|
|
+ .name = RS_NAME,
|
|
+ .tx_status = rs_tx_status,
|
|
+ .get_rate = rs_get_rate,
|
|
+ .rate_init = rs_rate_init,
|
|
+ .clear = rs_clear,
|
|
+ .alloc = rs_alloc,
|
|
+ .free = rs_free,
|
|
+ .alloc_sta = rs_alloc_sta,
|
|
+ .free_sta = rs_free_sta,
|
|
+};
|
|
+
|
|
+int iwl_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id)
|
|
+{
|
|
+ struct ieee80211_local *local = hw_to_local(hw);
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
+ struct iwl_rate_scale_priv *rs_priv;
|
|
+ struct sta_info *sta;
|
|
+ unsigned long flags;
|
|
+ int count = 0, i;
|
|
+ u32 samples = 0, success = 0, good = 0;
|
|
+ unsigned long now = jiffies;
|
|
+ u32 max_time = 0;
|
|
+
|
|
+ sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr);
|
|
+ if (!sta || !sta->rate_ctrl_priv) {
|
|
+ if (sta) {
|
|
+ sta_info_put(sta);
|
|
+ IWL_DEBUG_RATE("leave - no private rate data!\n");
|
|
+ } else
|
|
+ IWL_DEBUG_RATE("leave - no station!\n");
|
|
+ return sprintf(buf, "station %d not found\n", sta_id);
|
|
+ }
|
|
+
|
|
+ rs_priv = (void *)sta->rate_ctrl_priv;
|
|
+ spin_lock_irqsave(&rs_priv->lock, flags);
|
|
+ i = IWL_RATE_54M_INDEX;
|
|
+ while (1) {
|
|
+ u64 mask;
|
|
+ int j;
|
|
+
|
|
+ count +=
|
|
+ sprintf(&buf[count], " %2dMbs: ", iwl_rates[i].ieee / 2);
|
|
+
|
|
+ mask = (1ULL << (IWL_RATE_MAX_WINDOW - 1));
|
|
+ for (j = 0; j < IWL_RATE_MAX_WINDOW; j++, mask >>= 1)
|
|
+ buf[count++] =
|
|
+ (rs_priv->win[i].data & mask) ? '1' : '0';
|
|
+
|
|
+ samples += rs_priv->win[i].counter;
|
|
+ good += rs_priv->win[i].success_counter;
|
|
+ success += rs_priv->win[i].success_counter * iwl_rates[i].ieee;
|
|
+
|
|
+ if (rs_priv->win[i].stamp) {
|
|
+ int delta =
|
|
+ jiffies_to_msecs(now - rs_priv->win[i].stamp);
|
|
+
|
|
+ if (delta > max_time)
|
|
+ max_time = delta;
|
|
+
|
|
+ count += sprintf(&buf[count], "%5dms\n", delta);
|
|
+ } else
|
|
+ buf[count++] = '\n';
|
|
+
|
|
+ j = iwl_get_prev_ieee_rate(i);
|
|
+ if (j == i)
|
|
+ break;
|
|
+ i = j;
|
|
+ }
|
|
+ spin_unlock_irqrestore(&rs_priv->lock, flags);
|
|
+ sta_info_put(sta);
|
|
+
|
|
+ /* Display the average rate of all samples taken.
|
|
+ *
|
|
+ * NOTE: We multiple # of samples by 2 since the IEEE measurement
|
|
+ * added from iwl_rates is actually 2X the rate */
|
|
+ if (samples)
|
|
+ count += sprintf(
|
|
+ &buf[count],
|
|
+ "\nAverage rate is %3d.%02dMbs over last %4dms\n"
|
|
+ "%3d%% success (%d good packets over %d tries)\n",
|
|
+ success / (2 * samples), (success * 5 / samples) % 10,
|
|
+ max_time, good * 100 / samples, good, samples);
|
|
+ else
|
|
+ count += sprintf(&buf[count], "\nAverage rate: 0Mbs\n");
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+void iwl_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id)
|
|
+{
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
+ s32 rssi = 0;
|
|
+ unsigned long flags;
|
|
+ struct ieee80211_local *local = hw_to_local(hw);
|
|
+ struct iwl_rate_scale_priv *rs_priv;
|
|
+ struct sta_info *sta;
|
|
+
|
|
+ IWL_DEBUG_RATE("enter\n");
|
|
+
|
|
+ if (!local->rate_ctrl->ops->name ||
|
|
+ strcmp(local->rate_ctrl->ops->name, RS_NAME)) {
|
|
+ IWL_WARNING("iwl-3945-rs not selected as rate control algo!\n");
|
|
+ IWL_DEBUG_RATE("leave - mac80211 picked the wrong RC algo.\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr);
|
|
+ if (!sta || !sta->rate_ctrl_priv) {
|
|
+ if (sta)
|
|
+ sta_info_put(sta);
|
|
+ IWL_DEBUG_RATE("leave - no private rate data!\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ rs_priv = (void *)sta->rate_ctrl_priv;
|
|
+
|
|
+ spin_lock_irqsave(&rs_priv->lock, flags);
|
|
+
|
|
+ rs_priv->tgg = 0;
|
|
+ switch (priv->phymode) {
|
|
+ case MODE_IEEE80211G:
|
|
+ if (priv->active_rxon.flags & RXON_FLG_TGG_PROTECT_MSK) {
|
|
+ rs_priv->tgg = 1;
|
|
+ rs_priv->expected_tpt = iwl_expected_tpt_g_prot;
|
|
+ } else
|
|
+ rs_priv->expected_tpt = iwl_expected_tpt_g;
|
|
+ break;
|
|
+
|
|
+ case MODE_IEEE80211A:
|
|
+ rs_priv->expected_tpt = iwl_expected_tpt_a;
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ IWL_WARNING("Invalid phymode. Defaulting to 802.11b\n");
|
|
+ case MODE_IEEE80211B:
|
|
+ rs_priv->expected_tpt = iwl_expected_tpt_b;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ sta_info_put(sta);
|
|
+ spin_unlock_irqrestore(&rs_priv->lock, flags);
|
|
+
|
|
+ rssi = priv->last_rx_rssi;
|
|
+ if (rssi == 0)
|
|
+ rssi = IWL_MIN_RSSI_VAL;
|
|
+
|
|
+ IWL_DEBUG(IWL_DL_INFO | IWL_DL_RATE, "Network RSSI: %d\n", rssi);
|
|
+
|
|
+ rs_priv->start_rate = iwl_get_rate_index_by_rssi(rssi, priv->phymode);
|
|
+
|
|
+ IWL_DEBUG_RATE("leave: rssi %d assign rate index: "
|
|
+ "%d (plcp 0x%x)\n", rssi, rs_priv->start_rate,
|
|
+ iwl_rates[rs_priv->start_rate].plcp);
|
|
+}
|
|
+
|
|
+void iwl_rate_control_register(struct ieee80211_hw *hw)
|
|
+{
|
|
+ ieee80211_rate_control_register(&rs_ops);
|
|
+}
|
|
+
|
|
+void iwl_rate_control_unregister(struct ieee80211_hw *hw)
|
|
+{
|
|
+ ieee80211_rate_control_unregister(&rs_ops);
|
|
+}
|
|
+
|
|
+
|
|
diff --git a/drivers/net/wireless/iwl-3945-rs.h b/drivers/net/wireless/iwl-3945-rs.h
|
|
new file mode 100644
|
|
index 0000000..70dcec4
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/iwl-3945-rs.h
|
|
@@ -0,0 +1,221 @@
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * Copyright(c) 2005 - 2007 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.
|
|
+ *
|
|
+ * Contact Information:
|
|
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
|
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+#ifndef __iwl_3945_rs_h__
|
|
+#define __iwl_3945_rs_h__
|
|
+
|
|
+struct iwl_rate_info {
|
|
+ u8 plcp;
|
|
+ u8 ieee;
|
|
+ u8 prev_ieee; /* previous rate in IEEE speeds */
|
|
+ u8 next_ieee; /* next rate in IEEE speeds */
|
|
+ u8 prev_rs; /* previous rate used in rs algo */
|
|
+ u8 next_rs; /* next rate used in rs algo */
|
|
+ u8 prev_rs_tgg; /* previous rate used in TGG rs algo */
|
|
+ u8 next_rs_tgg; /* next rate used in TGG rs algo */
|
|
+};
|
|
+
|
|
+enum {
|
|
+ IWL_RATE_6M_INDEX = 0,
|
|
+ IWL_RATE_9M_INDEX,
|
|
+ IWL_RATE_12M_INDEX,
|
|
+ IWL_RATE_18M_INDEX,
|
|
+ IWL_RATE_24M_INDEX,
|
|
+ IWL_RATE_36M_INDEX,
|
|
+ IWL_RATE_48M_INDEX,
|
|
+ IWL_RATE_54M_INDEX,
|
|
+ IWL_RATE_1M_INDEX,
|
|
+ IWL_RATE_2M_INDEX,
|
|
+ IWL_RATE_5M_INDEX,
|
|
+ IWL_RATE_11M_INDEX,
|
|
+ IWL_RATE_COUNT,
|
|
+ IWL_RATE_INVM_INDEX,
|
|
+ IWL_RATE_INVALID = IWL_RATE_INVM_INDEX
|
|
+};
|
|
+
|
|
+enum {
|
|
+ IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX,
|
|
+ IWL_LAST_OFDM_RATE = IWL_RATE_54M_INDEX,
|
|
+ IWL_FIRST_CCK_RATE = IWL_RATE_1M_INDEX,
|
|
+ IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX,
|
|
+};
|
|
+
|
|
+/* #define vs. enum to keep from defaulting to 'large integer' */
|
|
+#define IWL_RATE_6M_MASK (1<<IWL_RATE_6M_INDEX)
|
|
+#define IWL_RATE_9M_MASK (1<<IWL_RATE_9M_INDEX)
|
|
+#define IWL_RATE_12M_MASK (1<<IWL_RATE_12M_INDEX)
|
|
+#define IWL_RATE_18M_MASK (1<<IWL_RATE_18M_INDEX)
|
|
+#define IWL_RATE_24M_MASK (1<<IWL_RATE_24M_INDEX)
|
|
+#define IWL_RATE_36M_MASK (1<<IWL_RATE_36M_INDEX)
|
|
+#define IWL_RATE_48M_MASK (1<<IWL_RATE_48M_INDEX)
|
|
+#define IWL_RATE_54M_MASK (1<<IWL_RATE_54M_INDEX)
|
|
+#define IWL_RATE_1M_MASK (1<<IWL_RATE_1M_INDEX)
|
|
+#define IWL_RATE_2M_MASK (1<<IWL_RATE_2M_INDEX)
|
|
+#define IWL_RATE_5M_MASK (1<<IWL_RATE_5M_INDEX)
|
|
+#define IWL_RATE_11M_MASK (1<<IWL_RATE_11M_INDEX)
|
|
+
|
|
+enum {
|
|
+ IWL_RATE_6M_PLCP = 13,
|
|
+ IWL_RATE_9M_PLCP = 15,
|
|
+ IWL_RATE_12M_PLCP = 5,
|
|
+ IWL_RATE_18M_PLCP = 7,
|
|
+ IWL_RATE_24M_PLCP = 9,
|
|
+ IWL_RATE_36M_PLCP = 11,
|
|
+ IWL_RATE_48M_PLCP = 1,
|
|
+ IWL_RATE_54M_PLCP = 3,
|
|
+ IWL_RATE_1M_PLCP = 10,
|
|
+ IWL_RATE_2M_PLCP = 20,
|
|
+ IWL_RATE_5M_PLCP = 55,
|
|
+ IWL_RATE_11M_PLCP = 110,
|
|
+};
|
|
+
|
|
+enum {
|
|
+ IWL_RATE_6M_IEEE = 12,
|
|
+ IWL_RATE_9M_IEEE = 18,
|
|
+ IWL_RATE_12M_IEEE = 24,
|
|
+ IWL_RATE_18M_IEEE = 36,
|
|
+ IWL_RATE_24M_IEEE = 48,
|
|
+ IWL_RATE_36M_IEEE = 72,
|
|
+ IWL_RATE_48M_IEEE = 96,
|
|
+ IWL_RATE_54M_IEEE = 108,
|
|
+ IWL_RATE_1M_IEEE = 2,
|
|
+ IWL_RATE_2M_IEEE = 4,
|
|
+ IWL_RATE_5M_IEEE = 11,
|
|
+ IWL_RATE_11M_IEEE = 22,
|
|
+};
|
|
+
|
|
+#define IWL_CCK_BASIC_RATES_MASK \
|
|
+ (IWL_RATE_1M_MASK | \
|
|
+ IWL_RATE_2M_MASK)
|
|
+
|
|
+#define IWL_CCK_RATES_MASK \
|
|
+ (IWL_BASIC_RATES_MASK | \
|
|
+ IWL_RATE_5M_MASK | \
|
|
+ IWL_RATE_11M_MASK)
|
|
+
|
|
+#define IWL_OFDM_BASIC_RATES_MASK \
|
|
+ (IWL_RATE_6M_MASK | \
|
|
+ IWL_RATE_12M_MASK | \
|
|
+ IWL_RATE_24M_MASK)
|
|
+
|
|
+#define IWL_OFDM_RATES_MASK \
|
|
+ (IWL_OFDM_BASIC_RATES_MASK | \
|
|
+ IWL_RATE_9M_MASK | \
|
|
+ IWL_RATE_18M_MASK | \
|
|
+ IWL_RATE_36M_MASK | \
|
|
+ IWL_RATE_48M_MASK | \
|
|
+ IWL_RATE_54M_MASK)
|
|
+
|
|
+#define IWL_BASIC_RATES_MASK \
|
|
+ (IWL_OFDM_BASIC_RATES_MASK | \
|
|
+ IWL_CCK_BASIC_RATES_MASK)
|
|
+
|
|
+#define IWL_RATES_MASK ((1<<IWL_RATE_COUNT)-1)
|
|
+
|
|
+#define IWL_INVALID_VALUE -1
|
|
+
|
|
+#define IWL_MIN_RSSI_VAL -100
|
|
+#define IWL_MAX_RSSI_VAL 0
|
|
+
|
|
+extern const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT];
|
|
+
|
|
+static inline int iwl_rate_index_from_plcp(int plcp)
|
|
+{
|
|
+ int i = 0;
|
|
+ for (i = 0; i < IWL_RATE_COUNT; i++)
|
|
+ if (iwl_rates[i].plcp == plcp)
|
|
+ return i;
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static inline u8 iwl_rate_get_lowest_plcp(int rate_mask)
|
|
+{
|
|
+ u8 i;
|
|
+
|
|
+ for (i = IWL_RATE_1M_INDEX; i != IWL_RATE_INVALID;
|
|
+ i = iwl_rates[i].next_ieee) {
|
|
+ if (rate_mask & (1 << i))
|
|
+ return iwl_rates[i].plcp;
|
|
+ }
|
|
+
|
|
+ return IWL_RATE_INVALID;
|
|
+}
|
|
+
|
|
+static inline u8 iwl_get_prev_ieee_rate(u8 rate_index)
|
|
+{
|
|
+ u8 rate = iwl_rates[rate_index].prev_ieee;
|
|
+ if (rate == IWL_RATE_INVALID)
|
|
+ rate = rate_index;
|
|
+ return rate;
|
|
+}
|
|
+
|
|
+#if IWL == 3945
|
|
+/**
|
|
+ * iwl_fill_rs_info - Fill an output text buffer with the rate representation
|
|
+ *
|
|
+ * NOTE: This is provided as a quick mechanism for a user to visualize
|
|
+ * the performance of the rate control alogirthm and is not meant to be
|
|
+ * parsed software.
|
|
+ */
|
|
+extern int iwl_fill_rs_info(struct ieee80211_hw *, char *buf, u8 sta_id);
|
|
+
|
|
+/**
|
|
+ * iwl_rate_scale_init - Initialize the rate scale table based on assoc info
|
|
+ *
|
|
+ * The specific througput table used is based on the type of network
|
|
+ * the associated with, including A, B, G, and G w/ TGG protection
|
|
+ */
|
|
+extern void iwl_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id);
|
|
+
|
|
+/**
|
|
+ * iwl_rate_control_register - Register the rate control algorithm callbacks
|
|
+ *
|
|
+ * Since the rate control algorithm is hardware specific, there is no need
|
|
+ * or reason to place it as a stand alone module. The driver can call
|
|
+ * iwl_rate_control_register in order to register the rate control callbacks
|
|
+ * with the mac80211 subsystem. This should be performed prior to calling
|
|
+ * ieee80211_register_hw
|
|
+ *
|
|
+ */
|
|
+extern void iwl_rate_control_register(struct ieee80211_hw *hw);
|
|
+
|
|
+/**
|
|
+ * iwl_rate_control_unregister - Unregister the rate control callbacks
|
|
+ *
|
|
+ * This should be called after calling ieee80211_unregister_hw, but before
|
|
+ * the driver is unloaded.
|
|
+ */
|
|
+extern void iwl_rate_control_unregister(struct ieee80211_hw *hw);
|
|
+#else
|
|
+static inline int iwl_fill_rs_info(struct ieee80211_hw *hw, char *buf,
|
|
+ u8 sta_id)
|
|
+{ return -ENOTSUPP; }
|
|
+static inline void iwl_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id) {}
|
|
+static inline void iwl_rate_control_register(struct ieee80211_hw *hw) {}
|
|
+static inline void iwl_rate_control_unregister(struct ieee80211_hw *hw) {}
|
|
+#endif /* IWL == 3945 */
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/net/wireless/iwl-3945.c b/drivers/net/wireless/iwl-3945.c
|
|
new file mode 100644
|
|
index 0000000..bb75836
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/iwl-3945.c
|
|
@@ -0,0 +1,2286 @@
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * Copyright(c) 2003 - 2007 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.
|
|
+ *
|
|
+ * Contact Information:
|
|
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
|
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/version.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/pci.h>
|
|
+#include <linux/dma-mapping.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/skbuff.h>
|
|
+#include <linux/netdevice.h>
|
|
+#include <linux/wireless.h>
|
|
+#include <linux/firmware.h>
|
|
+#include <net/mac80211.h>
|
|
+
|
|
+#include <linux/etherdevice.h>
|
|
+#include <linux/delay.h>
|
|
+
|
|
+#include "iwlwifi.h"
|
|
+#include "iwl-helpers.h"
|
|
+#include "iwl-3945.h"
|
|
+#include "iwl-3945-rs.h"
|
|
+
|
|
+#define IWL_DECLARE_RATE_INFO(r, ip, in, rp, rn, pp, np) \
|
|
+ [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \
|
|
+ IWL_RATE_##r##M_IEEE, \
|
|
+ IWL_RATE_##ip##M_INDEX, \
|
|
+ IWL_RATE_##in##M_INDEX, \
|
|
+ IWL_RATE_##rp##M_INDEX, \
|
|
+ IWL_RATE_##rn##M_INDEX, \
|
|
+ IWL_RATE_##pp##M_INDEX, \
|
|
+ IWL_RATE_##np##M_INDEX }
|
|
+
|
|
+/*
|
|
+ * Parameter order:
|
|
+ * rate, prev rate, next rate, prev tgg rate, next tgg rate
|
|
+ *
|
|
+ * If there isn't a valid next or previous rate then INV is used which
|
|
+ * maps to IWL_RATE_INVALID
|
|
+ *
|
|
+ */
|
|
+const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT] = {
|
|
+ IWL_DECLARE_RATE_INFO(6, 5, 9, 5, 11, 5, 11), /* 6mbps */
|
|
+ IWL_DECLARE_RATE_INFO(9, 6, 11, 5, 11, 5, 11), /* 9mbps */
|
|
+ IWL_DECLARE_RATE_INFO(12, 11, 18, 11, 18, 11, 18), /* 12mbps */
|
|
+ IWL_DECLARE_RATE_INFO(18, 12, 24, 12, 24, 11, 24), /* 18mbps */
|
|
+ IWL_DECLARE_RATE_INFO(24, 18, 36, 18, 36, 18, 36), /* 24mbps */
|
|
+ IWL_DECLARE_RATE_INFO(36, 24, 48, 24, 48, 24, 48), /* 36mbps */
|
|
+ IWL_DECLARE_RATE_INFO(48, 36, 54, 36, 54, 36, 54), /* 48mbps */
|
|
+ IWL_DECLARE_RATE_INFO(54, 48, INV, 48, INV, 48, INV),/* 54mbps */
|
|
+ IWL_DECLARE_RATE_INFO(1, INV, 2, INV, 2, INV, 2), /* 1mbps */
|
|
+ IWL_DECLARE_RATE_INFO(2, 1, 5, 1, 5, 1, 5), /* 2mbps */
|
|
+ IWL_DECLARE_RATE_INFO(5, 2, 6, 2, 11, 2, 11), /*5.5mbps */
|
|
+ IWL_DECLARE_RATE_INFO(11, 9, 12, 5, 12, 5, 18), /* 11mbps */
|
|
+};
|
|
+
|
|
+/* 1 = enable the iwl_disable_events() function */
|
|
+#define IWL_EVT_DISABLE (0)
|
|
+#define IWL_EVT_DISABLE_SIZE (1532/32)
|
|
+
|
|
+/**
|
|
+ * iwl_disable_events - Disable selected events in uCode event log
|
|
+ *
|
|
+ * Disable an event by writing "1"s into "disable"
|
|
+ * bitmap in SRAM. Bit position corresponds to Event # (id/type).
|
|
+ * Default values of 0 enable uCode events to be logged.
|
|
+ * Use for only special debugging. This function is just a placeholder as-is,
|
|
+ * you'll need to provide the special bits! ...
|
|
+ * ... and set IWL_EVT_DISABLE to 1. */
|
|
+void iwl_disable_events(struct iwl_priv *priv)
|
|
+{
|
|
+ int rc;
|
|
+ int i;
|
|
+ u32 base; /* SRAM address of event log header */
|
|
+ u32 disable_ptr; /* SRAM address of event-disable bitmap array */
|
|
+ u32 array_size; /* # of u32 entries in array */
|
|
+ u32 evt_disable[IWL_EVT_DISABLE_SIZE] = {
|
|
+ 0x00000000, /* 31 - 0 Event id numbers */
|
|
+ 0x00000000, /* 63 - 32 */
|
|
+ 0x00000000, /* 95 - 64 */
|
|
+ 0x00000000, /* 127 - 96 */
|
|
+ 0x00000000, /* 159 - 128 */
|
|
+ 0x00000000, /* 191 - 160 */
|
|
+ 0x00000000, /* 223 - 192 */
|
|
+ 0x00000000, /* 255 - 224 */
|
|
+ 0x00000000, /* 287 - 256 */
|
|
+ 0x00000000, /* 319 - 288 */
|
|
+ 0x00000000, /* 351 - 320 */
|
|
+ 0x00000000, /* 383 - 352 */
|
|
+ 0x00000000, /* 415 - 384 */
|
|
+ 0x00000000, /* 447 - 416 */
|
|
+ 0x00000000, /* 479 - 448 */
|
|
+ 0x00000000, /* 511 - 480 */
|
|
+ 0x00000000, /* 543 - 512 */
|
|
+ 0x00000000, /* 575 - 544 */
|
|
+ 0x00000000, /* 607 - 576 */
|
|
+ 0x00000000, /* 639 - 608 */
|
|
+ 0x00000000, /* 671 - 640 */
|
|
+ 0x00000000, /* 703 - 672 */
|
|
+ 0x00000000, /* 735 - 704 */
|
|
+ 0x00000000, /* 767 - 736 */
|
|
+ 0x00000000, /* 799 - 768 */
|
|
+ 0x00000000, /* 831 - 800 */
|
|
+ 0x00000000, /* 863 - 832 */
|
|
+ 0x00000000, /* 895 - 864 */
|
|
+ 0x00000000, /* 927 - 896 */
|
|
+ 0x00000000, /* 959 - 928 */
|
|
+ 0x00000000, /* 991 - 960 */
|
|
+ 0x00000000, /* 1023 - 992 */
|
|
+ 0x00000000, /* 1055 - 1024 */
|
|
+ 0x00000000, /* 1087 - 1056 */
|
|
+ 0x00000000, /* 1119 - 1088 */
|
|
+ 0x00000000, /* 1151 - 1120 */
|
|
+ 0x00000000, /* 1183 - 1152 */
|
|
+ 0x00000000, /* 1215 - 1184 */
|
|
+ 0x00000000, /* 1247 - 1216 */
|
|
+ 0x00000000, /* 1279 - 1248 */
|
|
+ 0x00000000, /* 1311 - 1280 */
|
|
+ 0x00000000, /* 1343 - 1312 */
|
|
+ 0x00000000, /* 1375 - 1344 */
|
|
+ 0x00000000, /* 1407 - 1376 */
|
|
+ 0x00000000, /* 1439 - 1408 */
|
|
+ 0x00000000, /* 1471 - 1440 */
|
|
+ 0x00000000, /* 1503 - 1472 */
|
|
+ };
|
|
+
|
|
+ base = le32_to_cpu(priv->card_alive.log_event_table_ptr);
|
|
+ if (!iwl_hw_valid_rtc_data_addr(base)) {
|
|
+ IWL_ERROR("Invalid event log pointer 0x%08X\n", base);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc) {
|
|
+ IWL_WARNING("Can not read from adapter at this time.\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ disable_ptr = iwl_read_restricted_mem(priv, base + (4 * sizeof(u32)));
|
|
+ array_size = iwl_read_restricted_mem(priv, base + (5 * sizeof(u32)));
|
|
+ iwl_release_restricted_access(priv);
|
|
+
|
|
+ if (IWL_EVT_DISABLE && (array_size == IWL_EVT_DISABLE_SIZE)) {
|
|
+ IWL_DEBUG_INFO("Disabling selected uCode log events at 0x%x\n",
|
|
+ disable_ptr);
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ for (i = 0; i < IWL_EVT_DISABLE_SIZE; i++)
|
|
+ iwl_write_restricted_mem(priv,
|
|
+ disable_ptr +
|
|
+ (i * sizeof(u32)),
|
|
+ evt_disable[i]);
|
|
+
|
|
+ iwl_release_restricted_access(priv);
|
|
+ } else {
|
|
+ IWL_DEBUG_INFO("Selected uCode log events may be disabled\n");
|
|
+ IWL_DEBUG_INFO(" by writing \"1\"s into disable bitmap\n");
|
|
+ IWL_DEBUG_INFO(" in SRAM at 0x%x, size %d u32s\n",
|
|
+ disable_ptr, array_size);
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl3945_get_antenna_flags - Get antenna flags for RXON command
|
|
+ * @priv: eeprom and antenna fields are used to determine antenna flags
|
|
+ *
|
|
+ * priv->eeprom is used to determine if antenna AUX/MAIN are reversed
|
|
+ * priv->antenna specifies the antenna diversity mode:
|
|
+ *
|
|
+ * IWL_ANTENNA_DIVERISTY - NIC selects best antenna by itself
|
|
+ * IWL_ANTENNA_MAIN - Force MAIN antenna
|
|
+ * IWL_ANTENNA_AUX - Force AUX antenna
|
|
+ */
|
|
+__le32 iwl3945_get_antenna_flags(const struct iwl_priv *priv)
|
|
+{
|
|
+ switch (priv->antenna) {
|
|
+ case IWL_ANTENNA_DIVERSITY:
|
|
+ return 0;
|
|
+
|
|
+ case IWL_ANTENNA_MAIN:
|
|
+ if (priv->eeprom.antenna_switch_type)
|
|
+ return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_B_MSK;
|
|
+ return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_A_MSK;
|
|
+
|
|
+ case IWL_ANTENNA_AUX:
|
|
+ if (priv->eeprom.antenna_switch_type)
|
|
+ return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_A_MSK;
|
|
+ return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_B_MSK;
|
|
+ }
|
|
+
|
|
+ /* bad antenna selector value */
|
|
+ IWL_ERROR("Bad antenna selector value (0x%x)\n", priv->antenna);
|
|
+ return 0; /* "diversity" is default if error */
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+ *
|
|
+ * Intel PRO/Wireless 3945ABG/BG Network Connection
|
|
+ *
|
|
+ * RX handler implementations
|
|
+ *
|
|
+ * Used by iwl-base.c
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+void iwl_hw_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
|
|
+{
|
|
+ struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
|
|
+ IWL_DEBUG_RX("Statistics notification received (%d vs %d).\n",
|
|
+ (int)sizeof(struct iwl_notif_statistics),
|
|
+ le32_to_cpu(pkt->len));
|
|
+
|
|
+ memcpy(&priv->statistics, pkt->u.raw, sizeof(priv->statistics));
|
|
+
|
|
+ priv->last_statistics_time = jiffies;
|
|
+}
|
|
+
|
|
+static void iwl3945_handle_data_packet(struct iwl_priv *priv, int is_data,
|
|
+ struct iwl_rx_mem_buffer *rxb,
|
|
+ struct ieee80211_rx_status *stats,
|
|
+ u16 phy_flags)
|
|
+{
|
|
+ struct ieee80211_hdr *hdr;
|
|
+ struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
|
|
+ struct iwl_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt);
|
|
+ struct iwl_rx_frame_end *rx_end = IWL_RX_END(pkt);
|
|
+ short len = le16_to_cpu(rx_hdr->len);
|
|
+
|
|
+ /* We received data from the HW, so stop the watchdog */
|
|
+ if (unlikely((len + IWL_RX_FRAME_SIZE) > skb_tailroom(rxb->skb))) {
|
|
+ IWL_DEBUG_DROP("Corruption detected!\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* We only process data packets if the interface is open */
|
|
+ if (unlikely(!priv->is_open)) {
|
|
+ IWL_DEBUG_DROP_LIMIT
|
|
+ ("Dropping packet while interface is not open.\n");
|
|
+ return;
|
|
+ }
|
|
+ if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) {
|
|
+ if (iwl_param_hwcrypto)
|
|
+ iwl_set_decrypted_flag(priv, rxb->skb,
|
|
+ le32_to_cpu(rx_end->status),
|
|
+ stats);
|
|
+ iwl_handle_data_packet_monitor(priv, rxb, IWL_RX_DATA(pkt),
|
|
+ len, stats, phy_flags);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ skb_reserve(rxb->skb, (void *)rx_hdr->payload - (void *)pkt);
|
|
+ /* Set the size of the skb to the size of the frame */
|
|
+ skb_put(rxb->skb, le16_to_cpu(rx_hdr->len));
|
|
+
|
|
+ hdr = (void *)rxb->skb->data;
|
|
+
|
|
+ if (iwl_param_hwcrypto)
|
|
+ iwl_set_decrypted_flag(priv, rxb->skb,
|
|
+ le32_to_cpu(rx_end->status), stats);
|
|
+
|
|
+ ieee80211_rx_irqsafe(priv->hw, rxb->skb, stats);
|
|
+ rxb->skb = NULL;
|
|
+}
|
|
+
|
|
+static void iwl3945_rx_reply_rx(struct iwl_priv *priv,
|
|
+ struct iwl_rx_mem_buffer *rxb)
|
|
+{
|
|
+ struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
|
|
+ struct iwl_rx_frame_stats *rx_stats = IWL_RX_STATS(pkt);
|
|
+ struct iwl_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt);
|
|
+ struct iwl_rx_frame_end *rx_end = IWL_RX_END(pkt);
|
|
+ struct ieee80211_hdr *header;
|
|
+ u16 phy_flags = le16_to_cpu(rx_hdr->phy_flags);
|
|
+ u16 rx_stats_sig_avg = le16_to_cpu(rx_stats->sig_avg);
|
|
+ u16 rx_stats_noise_diff = le16_to_cpu(rx_stats->noise_diff);
|
|
+ struct ieee80211_rx_status stats = {
|
|
+ .mactime = le32_to_cpu(rx_end->beacon_timestamp),
|
|
+ .freq = ieee80211chan2mhz(le16_to_cpu(rx_hdr->channel)),
|
|
+ .channel = le16_to_cpu(rx_hdr->channel),
|
|
+ .phymode = (rx_hdr->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ?
|
|
+ MODE_IEEE80211G : MODE_IEEE80211A,
|
|
+ .antenna = 0,
|
|
+ .rate = rx_hdr->rate,
|
|
+ .flag = 0,
|
|
+ };
|
|
+ u8 network_packet;
|
|
+ int snr;
|
|
+
|
|
+ if ((unlikely(rx_stats->phy_count > 20))) {
|
|
+ IWL_DEBUG_DROP
|
|
+ ("dsp size out of range [0,20]: "
|
|
+ "%d/n", rx_stats->phy_count);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!(rx_end->status & RX_RES_STATUS_NO_CRC32_ERROR)
|
|
+ || !(rx_end->status & RX_RES_STATUS_NO_RXE_OVERFLOW)) {
|
|
+ IWL_DEBUG_RX("Bad CRC or FIFO: 0x%08X.\n", rx_end->status);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) {
|
|
+ iwl3945_handle_data_packet(priv, 1, rxb, &stats, phy_flags);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Convert 3945's rssi indicator to dBm */
|
|
+ stats.ssi = rx_stats->rssi - IWL_RSSI_OFFSET;
|
|
+
|
|
+ /* Set default noise value to -127 */
|
|
+ if (priv->last_rx_noise == 0)
|
|
+ priv->last_rx_noise = IWL_NOISE_MEAS_NOT_AVAILABLE;
|
|
+
|
|
+ /* 3945 provides noise info for OFDM frames only.
|
|
+ * sig_avg and noise_diff are measured by the 3945's digital signal
|
|
+ * processor (DSP), and indicate linear levels of signal level and
|
|
+ * distortion/noise within the packet preamble after
|
|
+ * automatic gain control (AGC). sig_avg should stay fairly
|
|
+ * constant if the radio's AGC is working well.
|
|
+ * Since these values are linear (not dB or dBm), linear
|
|
+ * signal-to-noise ratio (SNR) is (sig_avg / noise_diff).
|
|
+ * Convert linear SNR to dB SNR, then subtract that from rssi dBm
|
|
+ * to obtain noise level in dBm.
|
|
+ * Calculate stats.signal (quality indicator in %) based on SNR. */
|
|
+ if (rx_stats_noise_diff) {
|
|
+ snr = rx_stats_sig_avg / rx_stats_noise_diff;
|
|
+ stats.noise = stats.ssi - iwl_calc_db_from_ratio(snr);
|
|
+ stats.signal = iwl_calc_sig_qual(stats.ssi, stats.noise);
|
|
+
|
|
+ /* If noise info not available, calculate signal quality indicator (%)
|
|
+ * using just the dBm signal level. */
|
|
+ } else {
|
|
+ stats.noise = priv->last_rx_noise;
|
|
+ stats.signal = iwl_calc_sig_qual(stats.ssi, 0);
|
|
+ }
|
|
+
|
|
+
|
|
+ IWL_DEBUG_STATS("Rssi %d noise %d qual %d sig_avg %d noise_diff %d\n",
|
|
+ stats.ssi, stats.noise, stats.signal,
|
|
+ rx_stats_sig_avg, rx_stats_noise_diff);
|
|
+
|
|
+ stats.freq = ieee80211chan2mhz(stats.channel);
|
|
+
|
|
+ /* can be covered by iwl_report_frame() in most cases */
|
|
+/* IWL_DEBUG_RX("RX status: 0x%08X\n", rx_end->status); */
|
|
+
|
|
+ header = (struct ieee80211_hdr *)IWL_RX_DATA(pkt);
|
|
+
|
|
+ network_packet = iwl_is_network_packet(priv, header);
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+ if (iwl_debug_level & IWL_DL_STATS && net_ratelimit())
|
|
+ IWL_DEBUG_STATS
|
|
+ ("[%c] %d RSSI: %d Signal: %u, Noise: %u, Rate: %u\n",
|
|
+ network_packet ? '*' : ' ',
|
|
+ stats.channel, stats.ssi, stats.ssi,
|
|
+ stats.ssi, stats.rate);
|
|
+
|
|
+ if (iwl_debug_level & (IWL_DL_RX))
|
|
+ /* Set "1" to report good data frames in groups of 100 */
|
|
+ iwl_report_frame(priv, pkt, header, 1);
|
|
+#endif
|
|
+
|
|
+ if (network_packet) {
|
|
+ priv->last_beacon_time = le32_to_cpu(rx_end->beacon_timestamp);
|
|
+ priv->last_tsf = le64_to_cpu(rx_end->timestamp);
|
|
+ priv->last_rx_rssi = stats.ssi;
|
|
+ priv->last_rx_noise = stats.noise;
|
|
+ }
|
|
+
|
|
+ switch (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FTYPE) {
|
|
+ case IEEE80211_FTYPE_MGMT:
|
|
+ switch (le16_to_cpu(header->frame_control) &
|
|
+ IEEE80211_FCTL_STYPE) {
|
|
+ case IEEE80211_STYPE_PROBE_RESP:
|
|
+ case IEEE80211_STYPE_BEACON:{
|
|
+ /* If this is a beacon or probe response for
|
|
+ * our network then cache the beacon
|
|
+ * timestamp */
|
|
+ if ((((priv->iw_mode == IEEE80211_IF_TYPE_STA)
|
|
+ && !compare_ether_addr(header->addr2,
|
|
+ priv->bssid)) ||
|
|
+ ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS)
|
|
+ && !compare_ether_addr(header->addr3,
|
|
+ priv->bssid)))) {
|
|
+ struct ieee80211_mgmt *mgmt =
|
|
+ (struct ieee80211_mgmt *)header;
|
|
+ __le32 *pos;
|
|
+ pos =
|
|
+ (__le32 *) & mgmt->u.beacon.
|
|
+ timestamp;
|
|
+ priv->timestamp0 = le32_to_cpu(pos[0]);
|
|
+ priv->timestamp1 = le32_to_cpu(pos[1]);
|
|
+ priv->beacon_int = le16_to_cpu(
|
|
+ mgmt->u.beacon.beacon_int);
|
|
+ if (priv->call_post_assoc_from_beacon &&
|
|
+ (priv->iw_mode ==
|
|
+ IEEE80211_IF_TYPE_STA))
|
|
+ queue_work(priv->workqueue,
|
|
+ &priv->post_associate.work);
|
|
+
|
|
+ priv->call_post_assoc_from_beacon = 0;
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ case IEEE80211_STYPE_ACTION:
|
|
+ /* TODO: Parse 802.11h frames for CSA... */
|
|
+ break;
|
|
+
|
|
+ /*
|
|
+ * TODO: There is no callback function from upper
|
|
+ * stack to inform us when associated status. this
|
|
+ * work around to sniff assoc_resp management frame
|
|
+ * and finish the association process.
|
|
+ */
|
|
+ case IEEE80211_STYPE_ASSOC_RESP:
|
|
+ case IEEE80211_STYPE_REASSOC_RESP:{
|
|
+ struct ieee80211_mgmt *mgnt =
|
|
+ (struct ieee80211_mgmt *)header;
|
|
+ priv->assoc_id = (~((1 << 15) | (1 << 14)) &
|
|
+ le16_to_cpu(mgnt->u.
|
|
+ assoc_resp.aid));
|
|
+ priv->assoc_capability =
|
|
+ le16_to_cpu(mgnt->u.assoc_resp.capab_info);
|
|
+ if (priv->beacon_int)
|
|
+ queue_work(priv->workqueue,
|
|
+ &priv->post_associate.work);
|
|
+ else
|
|
+ priv->call_post_assoc_from_beacon = 1;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ case IEEE80211_STYPE_PROBE_REQ:{
|
|
+ if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS)
|
|
+ IWL_DEBUG_DROP
|
|
+ ("Dropping (non network): " MAC_FMT
|
|
+ ", " MAC_FMT ", " MAC_FMT "\n",
|
|
+ MAC_ARG(header->addr1),
|
|
+ MAC_ARG(header->addr2),
|
|
+ MAC_ARG(header->addr3));
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ iwl3945_handle_data_packet(priv, 0, rxb, &stats, phy_flags);
|
|
+ break;
|
|
+
|
|
+ case IEEE80211_FTYPE_CTL:
|
|
+ break;
|
|
+
|
|
+ case IEEE80211_FTYPE_DATA:
|
|
+ if (unlikely(is_duplicate_packet(priv, header)))
|
|
+ IWL_DEBUG_DROP("Dropping (dup): " MAC_FMT ", "
|
|
+ MAC_FMT ", " MAC_FMT "\n",
|
|
+ MAC_ARG(header->addr1),
|
|
+ MAC_ARG(header->addr2),
|
|
+ MAC_ARG(header->addr3));
|
|
+ else
|
|
+ iwl3945_handle_data_packet(priv, 1, rxb, &stats,
|
|
+ phy_flags);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+int iwl_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv, void *ptr,
|
|
+ dma_addr_t addr, u16 len)
|
|
+{
|
|
+ int count;
|
|
+ u32 pad;
|
|
+ struct iwl_tfd_frame *tfd = (struct iwl_tfd_frame *)ptr;
|
|
+
|
|
+ count = TFD_CTL_COUNT_GET(le32_to_cpu(tfd->control_flags));
|
|
+ pad = TFD_CTL_PAD_GET(le32_to_cpu(tfd->control_flags));
|
|
+
|
|
+ if ((count >= NUM_TFD_CHUNKS) || (count < 0)) {
|
|
+ IWL_ERROR("Error can not send more than %d chunks\n",
|
|
+ NUM_TFD_CHUNKS);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ tfd->pa[count].addr = cpu_to_le32(addr);
|
|
+ tfd->pa[count].len = cpu_to_le32(len);
|
|
+
|
|
+ count++;
|
|
+
|
|
+ tfd->control_flags = cpu_to_le32(TFD_CTL_COUNT_SET(count) |
|
|
+ TFD_CTL_PAD_SET(pad));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_hw_txq_free_tfd - Free one TFD, those at index [txq->q.last_used]
|
|
+ *
|
|
+ * Does NOT advance any indexes
|
|
+ */
|
|
+int iwl_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq)
|
|
+{
|
|
+ struct iwl_tfd_frame *bd_tmp = (struct iwl_tfd_frame *)&txq->bd[0];
|
|
+ struct iwl_tfd_frame *bd = &bd_tmp[txq->q.last_used];
|
|
+ struct pci_dev *dev = priv->pci_dev;
|
|
+ int i;
|
|
+ int counter;
|
|
+
|
|
+ /* classify bd */
|
|
+ if (txq->q.id == IWL_CMD_QUEUE_NUM)
|
|
+ /* nothing to cleanup after for host commands */
|
|
+ return 0;
|
|
+
|
|
+ /* sanity check */
|
|
+ counter = TFD_CTL_COUNT_GET(le32_to_cpu(bd->control_flags));
|
|
+ if (counter > NUM_TFD_CHUNKS) {
|
|
+ IWL_ERROR("Too many chunks: %i\n", counter);
|
|
+ /* @todo issue fatal error, it is quite serious situation */
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* unmap chunks if any */
|
|
+
|
|
+ for (i = 1; i < counter; i++) {
|
|
+ pci_unmap_single(dev, le32_to_cpu(bd->pa[i].addr),
|
|
+ le32_to_cpu(bd->pa[i].len), PCI_DMA_TODEVICE);
|
|
+ if (txq->txb[txq->q.last_used].skb[0]) {
|
|
+ struct sk_buff *skb = txq->txb[txq->q.last_used].skb[0];
|
|
+ if (txq->txb[txq->q.last_used].skb[0]) {
|
|
+ /* Can be called from interrupt context */
|
|
+ dev_kfree_skb_any(skb);
|
|
+ txq->txb[txq->q.last_used].skb[0] = NULL;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+u8 iwl_hw_find_station(struct iwl_priv *priv, const u8 *bssid)
|
|
+{
|
|
+ int i;
|
|
+ int ret = IWL_INVALID_STATION;
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&priv->sta_lock, flags);
|
|
+ for (i = IWL_STA_ID; i < (IWL_STA_ID + priv->num_stations); i++)
|
|
+ if ((priv->stations[i].used) &&
|
|
+ (!compare_ether_addr
|
|
+ (priv->stations[i].sta.sta.addr, bssid))) {
|
|
+ ret = i;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_INFO("can not find STA " MAC_FMT " (total %d)\n",
|
|
+ MAC_ARG(bssid), priv->num_stations);
|
|
+ out:
|
|
+ spin_unlock_irqrestore(&priv->sta_lock, flags);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_hw_build_tx_cmd_rate - Add rate portion to TX_CMD
|
|
+ *
|
|
+*/
|
|
+void iwl_hw_build_tx_cmd_rate(struct iwl_priv *priv,
|
|
+ struct iwl_cmd *cmd,
|
|
+ struct ieee80211_tx_control *ctrl,
|
|
+ struct ieee80211_hdr *hdr, int sta_id, int tx_id)
|
|
+{
|
|
+ unsigned long flags;
|
|
+ u16 rate_index = min(ctrl->tx_rate & 0xffff, IWL_RATE_COUNT - 1);
|
|
+ u16 rate_mask;
|
|
+ int rate;
|
|
+ u8 rts_retry_limit;
|
|
+ u8 data_retry_limit;
|
|
+ __le32 tx_flags;
|
|
+ u16 fc = le16_to_cpu(hdr->frame_control);
|
|
+
|
|
+ rate = iwl_rates[rate_index].plcp;
|
|
+ tx_flags = cmd->cmd.tx.tx_flags;
|
|
+
|
|
+ /* We need to figure out how to get the sta->supp_rates while
|
|
+ * in this running context; perhaps encoding into ctrl->tx_rate? */
|
|
+ rate_mask = IWL_RATES_MASK;
|
|
+
|
|
+ spin_lock_irqsave(&priv->sta_lock, flags);
|
|
+
|
|
+ priv->stations[sta_id].current_rate.rate_n_flags = rate;
|
|
+
|
|
+ if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) &&
|
|
+ (sta_id != IWL_BROADCAST_ID) && (sta_id != IWL_MULTICAST_ID))
|
|
+ priv->stations[IWL_STA_ID].current_rate.rate_n_flags = rate;
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->sta_lock, flags);
|
|
+
|
|
+ if (tx_id >= IWL_CMD_QUEUE_NUM)
|
|
+ rts_retry_limit = 3;
|
|
+ else
|
|
+ rts_retry_limit = 7;
|
|
+
|
|
+ if (ieee80211_is_probe_response(fc)) {
|
|
+ data_retry_limit = 3;
|
|
+ if (data_retry_limit < rts_retry_limit)
|
|
+ rts_retry_limit = data_retry_limit;
|
|
+ } else
|
|
+ data_retry_limit = IWL_DEFAULT_TX_RETRY;
|
|
+
|
|
+ if (priv->data_retry_limit != -1)
|
|
+ data_retry_limit = priv->data_retry_limit;
|
|
+
|
|
+ if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) {
|
|
+ switch (fc & IEEE80211_FCTL_STYPE) {
|
|
+ case IEEE80211_STYPE_AUTH:
|
|
+ case IEEE80211_STYPE_DEAUTH:
|
|
+ case IEEE80211_STYPE_ASSOC_REQ:
|
|
+ case IEEE80211_STYPE_REASSOC_REQ:
|
|
+ if (tx_flags & TX_CMD_FLG_RTS_MSK) {
|
|
+ tx_flags &= ~TX_CMD_FLG_RTS_MSK;
|
|
+ tx_flags |= TX_CMD_FLG_CTS_MSK;
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ cmd->cmd.tx.rts_retry_limit = rts_retry_limit;
|
|
+ cmd->cmd.tx.data_retry_limit = data_retry_limit;
|
|
+ cmd->cmd.tx.rate = rate;
|
|
+ cmd->cmd.tx.tx_flags = tx_flags;
|
|
+
|
|
+ /* OFDM */
|
|
+ cmd->cmd.tx.supp_rates[0] = rate_mask & IWL_OFDM_RATES_MASK;
|
|
+
|
|
+ /* CCK */
|
|
+ cmd->cmd.tx.supp_rates[1] = (rate_mask >> 8) & 0xF;
|
|
+
|
|
+ IWL_DEBUG_RATE("Tx sta id: %d, rate: %d (plcp), flags: 0x%4X "
|
|
+ "cck/ofdm mask: 0x%x/0x%x\n", sta_id,
|
|
+ cmd->cmd.tx.rate, le32_to_cpu(cmd->cmd.tx.tx_flags),
|
|
+ cmd->cmd.tx.supp_rates[1], cmd->cmd.tx.supp_rates[0]);
|
|
+}
|
|
+
|
|
+u8 iwl3945_sync_sta(struct iwl_priv *priv, int sta_id, u16 tx_rate, u8 flags)
|
|
+{
|
|
+ unsigned long flags_spin;
|
|
+ struct iwl_station_entry *station;
|
|
+
|
|
+ if (sta_id == IWL_INVALID_STATION)
|
|
+ return IWL_INVALID_STATION;
|
|
+
|
|
+ spin_lock_irqsave(&priv->sta_lock, flags_spin);
|
|
+ station = &priv->stations[sta_id];
|
|
+
|
|
+ station->sta.sta.modify_mask = STA_MODIFY_TX_RATE_MSK;
|
|
+ station->sta.rate_n_flags = cpu_to_le16(tx_rate);
|
|
+ station->current_rate.rate_n_flags = tx_rate;
|
|
+ station->sta.mode = STA_CONTROL_MODIFY_MSK;
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
|
|
+
|
|
+ iwl_send_add_station(priv, &station->sta, flags);
|
|
+ IWL_DEBUG_RATE("SCALE sync station %d to rate %d\n",
|
|
+ sta_id, tx_rate);
|
|
+ return sta_id;
|
|
+}
|
|
+
|
|
+void iwl_hw_card_show_info(struct iwl_priv *priv)
|
|
+{
|
|
+ IWL_DEBUG_INFO("3945ABG HW Version %u.%u.%u\n",
|
|
+ ((priv->eeprom.board_revision >> 8) & 0x0F),
|
|
+ ((priv->eeprom.board_revision >> 8) >> 4),
|
|
+ (priv->eeprom.board_revision & 0x00FF));
|
|
+
|
|
+ IWL_DEBUG_INFO("3945ABG PBA Number %.*s\n",
|
|
+ (int)sizeof(priv->eeprom.board_pba_number),
|
|
+ priv->eeprom.board_pba_number);
|
|
+
|
|
+ IWL_DEBUG_INFO("EEPROM_ANTENNA_SWITCH_TYPE is 0x%02X\n",
|
|
+ priv->eeprom.antenna_switch_type);
|
|
+}
|
|
+
|
|
+static int iwl3945_nic_set_pwr_src(struct iwl_priv *priv, int pwr_max)
|
|
+{
|
|
+ int rc;
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc) {
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ if (!pwr_max) {
|
|
+ u32 val;
|
|
+
|
|
+ rc = pci_read_config_dword(priv->pci_dev,
|
|
+ PCI_POWER_SOURCE, &val);
|
|
+ if (val & PCI_CFG_PMC_PME_FROM_D3COLD_SUPPORT) {
|
|
+ iwl_set_bits_mask_restricted_reg(priv, ALM_APMG_PS_CTL,
|
|
+ APMG_PS_CTRL_REG_VAL_POWER_SRC_VAUX,
|
|
+ ~APMG_PS_CTRL_REG_MSK_POWER_SRC);
|
|
+ iwl_release_restricted_access(priv);
|
|
+
|
|
+ iwl_poll_bit(priv, CSR_GPIO_IN,
|
|
+ CSR_GPIO_IN_VAL_VAUX_PWR_SRC,
|
|
+ CSR_GPIO_IN_BIT_AUX_POWER, 5000);
|
|
+ } else
|
|
+ iwl_release_restricted_access(priv);
|
|
+ } else {
|
|
+ iwl_set_bits_mask_restricted_reg(priv, ALM_APMG_PS_CTL,
|
|
+ APMG_PS_CTRL_REG_VAL_POWER_SRC_VMAIN,
|
|
+ ~APMG_PS_CTRL_REG_MSK_POWER_SRC);
|
|
+
|
|
+ iwl_release_restricted_access(priv);
|
|
+ iwl_poll_bit(priv, CSR_GPIO_IN, CSR_GPIO_IN_VAL_VMAIN_PWR_SRC,
|
|
+ CSR_GPIO_IN_BIT_AUX_POWER, 5000); /* uS */
|
|
+ }
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int iwl3945_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
|
|
+{
|
|
+ int rc;
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc) {
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ iwl_write_restricted(priv, FH_RCSR_RBD_BASE(0), rxq->dma_addr);
|
|
+ iwl_write_restricted(priv, FH_RCSR_RPTR_ADDR(0),
|
|
+ priv->hw_setting.shared_phys +
|
|
+ offsetof(struct iwl_shared, rx_read_ptr[0]));
|
|
+ iwl_write_restricted(priv, FH_RCSR_WPTR(0), 0);
|
|
+ iwl_write_restricted(priv, FH_RCSR_CONFIG(0),
|
|
+ ALM_FH_RCSR_RX_CONFIG_REG_VAL_DMA_CHNL_EN_ENABLE |
|
|
+ ALM_FH_RCSR_RX_CONFIG_REG_VAL_RDRBD_EN_ENABLE |
|
|
+ ALM_FH_RCSR_RX_CONFIG_REG_BIT_WR_STTS_EN |
|
|
+ ALM_FH_RCSR_RX_CONFIG_REG_VAL_MAX_FRAG_SIZE_128 |
|
|
+ (RX_QUEUE_SIZE_LOG << ALM_FH_RCSR_RX_CONFIG_REG_POS_RBDC_SIZE) |
|
|
+ ALM_FH_RCSR_RX_CONFIG_REG_VAL_IRQ_DEST_INT_HOST |
|
|
+ (1 << ALM_FH_RCSR_RX_CONFIG_REG_POS_IRQ_RBTH) |
|
|
+ ALM_FH_RCSR_RX_CONFIG_REG_VAL_MSG_MODE_FH);
|
|
+
|
|
+ /* fake read to flush all prev I/O */
|
|
+ iwl_read_restricted(priv, FH_RSSR_CTRL);
|
|
+
|
|
+ iwl_release_restricted_access(priv);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int iwl3945_tx_reset(struct iwl_priv *priv)
|
|
+{
|
|
+ int rc;
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc) {
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ /* bypass mode */
|
|
+ iwl_write_restricted_reg(priv, SCD_MODE_REG, 0x2);
|
|
+
|
|
+ /* RA 0 is active */
|
|
+ iwl_write_restricted_reg(priv, SCD_ARASTAT_REG, 0x01);
|
|
+
|
|
+ /* all 6 fifo are active */
|
|
+ iwl_write_restricted_reg(priv, SCD_TXFACT_REG, 0x3f);
|
|
+
|
|
+ iwl_write_restricted_reg(priv, SCD_SBYP_MODE_1_REG, 0x010000);
|
|
+ iwl_write_restricted_reg(priv, SCD_SBYP_MODE_2_REG, 0x030002);
|
|
+ iwl_write_restricted_reg(priv, SCD_TXF4MF_REG, 0x000004);
|
|
+ iwl_write_restricted_reg(priv, SCD_TXF5MF_REG, 0x000005);
|
|
+
|
|
+ iwl_write_restricted(priv, FH_TSSR_CBB_BASE,
|
|
+ priv->hw_setting.shared_phys);
|
|
+
|
|
+ iwl_write_restricted(priv, FH_TSSR_MSG_CONFIG,
|
|
+ ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TXPD_ON |
|
|
+ ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_TXPD_ON |
|
|
+ ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_MAX_FRAG_SIZE_128B |
|
|
+ ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TFD_ON |
|
|
+ ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_CBB_ON |
|
|
+ ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RSP_WAIT_TH |
|
|
+ ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_RSP_WAIT_TH);
|
|
+
|
|
+ iwl_release_restricted_access(priv);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl3945_txq_ctx_reset - Reset TX queue context
|
|
+ *
|
|
+ * Destroys all DMA structures and initialize them again
|
|
+ */
|
|
+static int iwl3945_txq_ctx_reset(struct iwl_priv *priv)
|
|
+{
|
|
+ int rc;
|
|
+ int txq_id, slots_num;
|
|
+
|
|
+ iwl_hw_txq_ctx_free(priv);
|
|
+
|
|
+ /* Tx CMD queue */
|
|
+ rc = iwl3945_tx_reset(priv);
|
|
+ if (rc)
|
|
+ goto error;
|
|
+
|
|
+ /* Tx queue(s) */
|
|
+ for (txq_id = 0; txq_id < TFD_QUEUE_MAX; txq_id++) {
|
|
+ slots_num = (txq_id == IWL_CMD_QUEUE_NUM) ?
|
|
+ TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS;
|
|
+ rc = iwl_tx_queue_init(priv, &priv->txq[txq_id], slots_num,
|
|
+ txq_id);
|
|
+ if (rc) {
|
|
+ IWL_ERROR("Tx %d queue init failed\n", txq_id);
|
|
+ goto error;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+
|
|
+ error:
|
|
+ iwl_hw_txq_ctx_free(priv);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+int iwl_hw_nic_init(struct iwl_priv *priv)
|
|
+{
|
|
+ u8 rev_id;
|
|
+ int rc;
|
|
+ unsigned long flags;
|
|
+ struct iwl_rx_queue *rxq = &priv->rxq;
|
|
+
|
|
+ iwl_power_init_handle(priv);
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ iwl_set_bit(priv, CSR_ANA_PLL_CFG, (1 << 24));
|
|
+ iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS,
|
|
+ CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX);
|
|
+
|
|
+ iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
|
|
+ rc = iwl_poll_bit(priv, CSR_GP_CNTRL,
|
|
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
|
|
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
|
|
+ if (rc < 0) {
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ IWL_DEBUG_INFO("Failed to init the card\n");
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc) {
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ return rc;
|
|
+ }
|
|
+ iwl_write_restricted_reg(priv, ALM_APMG_CLK_EN,
|
|
+ APMG_CLK_REG_VAL_DMA_CLK_RQT |
|
|
+ APMG_CLK_REG_VAL_BSM_CLK_RQT);
|
|
+ udelay(20);
|
|
+ iwl_set_bits_restricted_reg(priv, ALM_APMG_PCIDEV_STT,
|
|
+ APMG_DEV_STATE_REG_VAL_L1_ACTIVE_DISABLE);
|
|
+ iwl_release_restricted_access(priv);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ /* Determine HW type */
|
|
+ rc = pci_read_config_byte(priv->pci_dev, PCI_REVISION_ID, &rev_id);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+ IWL_DEBUG_INFO("HW Revision ID = 0x%X\n", rev_id);
|
|
+
|
|
+ iwl3945_nic_set_pwr_src(priv, 1);
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+
|
|
+ if (rev_id & PCI_CFG_REV_ID_BIT_RTP)
|
|
+ IWL_DEBUG_INFO("RTP type \n");
|
|
+ else if (rev_id & PCI_CFG_REV_ID_BIT_BASIC_SKU) {
|
|
+ IWL_DEBUG_INFO("ALM-MB type\n");
|
|
+ iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
|
|
+ CSR_HW_IF_CONFIG_REG_BIT_ALMAGOR_MB);
|
|
+ } else {
|
|
+ IWL_DEBUG_INFO("ALM-MM type\n");
|
|
+ iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
|
|
+ CSR_HW_IF_CONFIG_REG_BIT_ALMAGOR_MM);
|
|
+ }
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ /* Initialize the EEPROM */
|
|
+ rc = iwl_eeprom_init(priv);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ if (EEPROM_SKU_CAP_OP_MODE_MRC == priv->eeprom.sku_cap) {
|
|
+ IWL_DEBUG_INFO("SKU OP mode is mrc\n");
|
|
+ iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
|
|
+ CSR_HW_IF_CONFIG_REG_BIT_SKU_MRC);
|
|
+ } else
|
|
+ IWL_DEBUG_INFO("SKU OP mode is basic\n");
|
|
+
|
|
+ if ((priv->eeprom.board_revision & 0xF0) == 0xD0) {
|
|
+ IWL_DEBUG_INFO("3945ABG revision is 0x%X\n",
|
|
+ priv->eeprom.board_revision);
|
|
+ iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
|
|
+ CSR_HW_IF_CONFIG_REG_BIT_BOARD_TYPE);
|
|
+ } else {
|
|
+ IWL_DEBUG_INFO("3945ABG revision is 0x%X\n",
|
|
+ priv->eeprom.board_revision);
|
|
+ iwl_clear_bit(priv, CSR_HW_IF_CONFIG_REG,
|
|
+ CSR_HW_IF_CONFIG_REG_BIT_BOARD_TYPE);
|
|
+ }
|
|
+
|
|
+ if (priv->eeprom.almgor_m_version <= 1) {
|
|
+ iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
|
|
+ CSR_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A);
|
|
+ IWL_DEBUG_INFO("Card M type A version is 0x%X\n",
|
|
+ priv->eeprom.almgor_m_version);
|
|
+ } else {
|
|
+ IWL_DEBUG_INFO("Card M type B version is 0x%X\n",
|
|
+ priv->eeprom.almgor_m_version);
|
|
+ iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
|
|
+ CSR_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B);
|
|
+ }
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ if (priv->eeprom.sku_cap & EEPROM_SKU_CAP_SW_RF_KILL_ENABLE)
|
|
+ IWL_DEBUG_RF_KILL("SW RF KILL supported in EEPROM.\n");
|
|
+
|
|
+ if (priv->eeprom.sku_cap & EEPROM_SKU_CAP_HW_RF_KILL_ENABLE)
|
|
+ IWL_DEBUG_RF_KILL("HW RF KILL supported in EEPROM.\n");
|
|
+
|
|
+ /* Allocate the RX queue, or reset if it is already allocated */
|
|
+ if (!rxq->bd) {
|
|
+ rc = iwl_rx_queue_alloc(priv);
|
|
+ if (rc) {
|
|
+ IWL_ERROR("Unable to initialize Rx queue\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ } else
|
|
+ iwl_rx_queue_reset(priv, rxq);
|
|
+
|
|
+ iwl_rx_replenish(priv);
|
|
+
|
|
+ iwl3945_rx_init(priv, rxq);
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+
|
|
+ /* Look at using this instead:
|
|
+ rxq->need_update = 1;
|
|
+ iwl_rx_queue_update_write_ptr(priv, rxq);
|
|
+ */
|
|
+
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc) {
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ return rc;
|
|
+ }
|
|
+ iwl_write_restricted(priv, FH_RCSR_WPTR(0), rxq->write & ~7);
|
|
+ iwl_release_restricted_access(priv);
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ rc = iwl3945_txq_ctx_reset(priv);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ set_bit(STATUS_INIT, &priv->status);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_hw_txq_ctx_free - Free TXQ Context
|
|
+ *
|
|
+ * Destroy all TX DMA queues and structures
|
|
+ */
|
|
+void iwl_hw_txq_ctx_free(struct iwl_priv *priv)
|
|
+{
|
|
+ int txq_id;
|
|
+
|
|
+ /* Tx queues */
|
|
+ for (txq_id = 0; txq_id < TFD_QUEUE_MAX; txq_id++)
|
|
+ iwl_tx_queue_free(priv, &priv->txq[txq_id]);
|
|
+}
|
|
+
|
|
+void iwl_hw_txq_ctx_stop(struct iwl_priv *priv)
|
|
+{
|
|
+ int queue;
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ if (iwl_grab_restricted_access(priv)) {
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ iwl_hw_txq_ctx_free(priv);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* stop SCD */
|
|
+ iwl_write_restricted_reg(priv, SCD_MODE_REG, 0);
|
|
+
|
|
+ /* reset TFD queues */
|
|
+ for (queue = TFD_QUEUE_MIN; queue < TFD_QUEUE_MAX; queue++) {
|
|
+ iwl_write_restricted(priv, FH_TCSR_CONFIG(queue), 0x0);
|
|
+ iwl_poll_restricted_bit(priv, FH_TSSR_TX_STATUS,
|
|
+ ALM_FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(queue),
|
|
+ 1000);
|
|
+ }
|
|
+
|
|
+ iwl_release_restricted_access(priv);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ iwl_hw_txq_ctx_free(priv);
|
|
+}
|
|
+
|
|
+int iwl_hw_nic_stop_master(struct iwl_priv *priv)
|
|
+{
|
|
+ int rc = 0;
|
|
+ u32 reg_val;
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+
|
|
+ /* set stop master bit */
|
|
+ iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER);
|
|
+
|
|
+ reg_val = iwl_read32(priv, CSR_GP_CNTRL);
|
|
+
|
|
+ if (CSR_GP_CNTRL_REG_FLAG_MAC_POWER_SAVE ==
|
|
+ (reg_val & CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE))
|
|
+ IWL_DEBUG_INFO("Card in power save, master is already "
|
|
+ "stopped\n");
|
|
+ else {
|
|
+ rc = iwl_poll_bit(priv, CSR_RESET,
|
|
+ CSR_RESET_REG_FLAG_MASTER_DISABLED,
|
|
+ CSR_RESET_REG_FLAG_MASTER_DISABLED, 100);
|
|
+ if (rc < 0) {
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ return rc;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ IWL_DEBUG_INFO("stop master\n");
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+int iwl_hw_nic_reset(struct iwl_priv *priv)
|
|
+{
|
|
+ int rc;
|
|
+ unsigned long flags;
|
|
+
|
|
+ iwl_hw_nic_stop_master(priv);
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+
|
|
+ iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
|
|
+
|
|
+ rc = iwl_poll_bit(priv, CSR_GP_CNTRL,
|
|
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
|
|
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
|
|
+
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (!rc) {
|
|
+ iwl_write_restricted_reg(priv, APMG_CLK_CTRL_REG,
|
|
+ APMG_CLK_REG_VAL_BSM_CLK_RQT);
|
|
+
|
|
+ udelay(10);
|
|
+
|
|
+ iwl_set_bit(priv, CSR_GP_CNTRL,
|
|
+ CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
|
|
+
|
|
+ iwl_write_restricted_reg(priv, ALM_APMG_LARC_INT_MSK, 0x0);
|
|
+ iwl_write_restricted_reg(priv, ALM_APMG_LARC_INT, 0xFFFFFFFF);
|
|
+
|
|
+ /* enable DMA */
|
|
+ iwl_write_restricted_reg(priv, ALM_APMG_CLK_EN,
|
|
+ APMG_CLK_REG_VAL_DMA_CLK_RQT |
|
|
+ APMG_CLK_REG_VAL_BSM_CLK_RQT);
|
|
+ udelay(10);
|
|
+
|
|
+ iwl_set_bits_restricted_reg(priv, ALM_APMG_PS_CTL,
|
|
+ APMG_PS_CTRL_REG_VAL_ALM_R_RESET_REQ);
|
|
+ udelay(5);
|
|
+ iwl_clear_bits_restricted_reg(priv, ALM_APMG_PS_CTL,
|
|
+ APMG_PS_CTRL_REG_VAL_ALM_R_RESET_REQ);
|
|
+ iwl_release_restricted_access(priv);
|
|
+ }
|
|
+
|
|
+ /* Clear the 'host command active' bit... */
|
|
+ clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
|
|
+
|
|
+ wake_up_interruptible(&priv->wait_command_queue);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_hw_reg_adjust_power_by_temp - return index delta into power gain settings table
|
|
+ */
|
|
+static int iwl_hw_reg_adjust_power_by_temp(int new_reading, int old_reading)
|
|
+{
|
|
+ return (new_reading - old_reading) * (-11) / 100;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_hw_reg_temp_out_of_range - Keep temperature in sane range
|
|
+ */
|
|
+static inline int iwl_hw_reg_temp_out_of_range(int temperature)
|
|
+{
|
|
+ return (((temperature < -260) || (temperature > 25)) ? 1 : 0);
|
|
+}
|
|
+
|
|
+int iwl_hw_get_temperature(struct iwl_priv *priv)
|
|
+{
|
|
+ return iwl_read32(priv, CSR_UCODE_DRV_GP2);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_hw_reg_txpower_get_temperature - get current temperature by reading from NIC
|
|
+ */
|
|
+static int iwl_hw_reg_txpower_get_temperature(struct iwl_priv *priv)
|
|
+{
|
|
+ int temperature;
|
|
+
|
|
+ temperature = iwl_hw_get_temperature(priv);
|
|
+
|
|
+ /* driver's okay range is -260 to +25.
|
|
+ * human readable okay range is 0 to +285 */
|
|
+ IWL_DEBUG_INFO("Temperature: %d\n", temperature + IWL_TEMP_CONVERT);
|
|
+
|
|
+ /* handle insane temp reading */
|
|
+ if (iwl_hw_reg_temp_out_of_range(temperature)) {
|
|
+ IWL_ERROR("Error bad temperature value %d\n", temperature);
|
|
+
|
|
+ /* if really really hot(?),
|
|
+ * substitute the 3rd band/group's temp measured at factory */
|
|
+ if (priv->last_temperature > 100)
|
|
+ temperature = priv->eeprom.groups[2].temperature;
|
|
+ else /* else use most recent "sane" value from driver */
|
|
+ temperature = priv->last_temperature;
|
|
+ }
|
|
+
|
|
+ return temperature; /* raw, not "human readable" */
|
|
+}
|
|
+
|
|
+/* Adjust Txpower only if temperature variance is greater than threshold.
|
|
+ *
|
|
+ * Both are lower than older versions' 9 degrees */
|
|
+#define IWL_TEMPERATURE_LIMIT_TIMER 6
|
|
+
|
|
+/**
|
|
+ * is_temp_calib_needed - determines if new calibration is needed
|
|
+ *
|
|
+ * records new temperature in tx_mgr->temperature.
|
|
+ * replaces tx_mgr->last_temperature *only* if calib needed
|
|
+ * (assumes caller will actually do the calibration!). */
|
|
+static int is_temp_calib_needed(struct iwl_priv *priv)
|
|
+{
|
|
+ int temp_diff;
|
|
+
|
|
+ priv->temperature = iwl_hw_reg_txpower_get_temperature(priv);
|
|
+ temp_diff = priv->temperature - priv->last_temperature;
|
|
+
|
|
+ /* get absolute value */
|
|
+ if (temp_diff < 0) {
|
|
+ IWL_DEBUG_POWER("Getting cooler, delta %d,\n", temp_diff);
|
|
+ temp_diff = -temp_diff;
|
|
+ } else if (temp_diff == 0)
|
|
+ IWL_DEBUG_POWER("Same temp,\n");
|
|
+ else
|
|
+ IWL_DEBUG_POWER("Getting warmer, delta %d,\n", temp_diff);
|
|
+
|
|
+ /* if we don't need calibration, *don't* update last_temperature */
|
|
+ if (temp_diff < IWL_TEMPERATURE_LIMIT_TIMER) {
|
|
+ IWL_DEBUG_POWER("Timed thermal calib not needed\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_POWER("Timed thermal calib needed\n");
|
|
+
|
|
+ /* assume that caller will actually do calib ...
|
|
+ * update the "last temperature" value */
|
|
+ priv->last_temperature = priv->temperature;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+#define IWL_MAX_GAIN_ENTRIES 78
|
|
+#define IWL_CCK_FROM_OFDM_POWER_DIFF -5
|
|
+#define IWL_CCK_FROM_OFDM_INDEX_DIFF (10)
|
|
+
|
|
+/* radio and DSP power table, each step is 1/2 dB.
|
|
+ * 1st number is for RF analog gain, 2nd number is for DSP pre-DAC gain. */
|
|
+static struct iwl_tx_power power_gain_table[2][IWL_MAX_GAIN_ENTRIES] = {
|
|
+ {
|
|
+ {251, 127}, /* 2.4 GHz, highest power */
|
|
+ {251, 127},
|
|
+ {251, 127},
|
|
+ {251, 127},
|
|
+ {251, 125},
|
|
+ {251, 110},
|
|
+ {251, 105},
|
|
+ {251, 98},
|
|
+ {187, 125},
|
|
+ {187, 115},
|
|
+ {187, 108},
|
|
+ {187, 99},
|
|
+ {243, 119},
|
|
+ {243, 111},
|
|
+ {243, 105},
|
|
+ {243, 97},
|
|
+ {243, 92},
|
|
+ {211, 106},
|
|
+ {211, 100},
|
|
+ {179, 120},
|
|
+ {179, 113},
|
|
+ {179, 107},
|
|
+ {147, 125},
|
|
+ {147, 119},
|
|
+ {147, 112},
|
|
+ {147, 106},
|
|
+ {147, 101},
|
|
+ {147, 97},
|
|
+ {147, 91},
|
|
+ {115, 107},
|
|
+ {235, 121},
|
|
+ {235, 115},
|
|
+ {235, 109},
|
|
+ {203, 127},
|
|
+ {203, 121},
|
|
+ {203, 115},
|
|
+ {203, 108},
|
|
+ {203, 102},
|
|
+ {203, 96},
|
|
+ {203, 92},
|
|
+ {171, 110},
|
|
+ {171, 104},
|
|
+ {171, 98},
|
|
+ {139, 116},
|
|
+ {227, 125},
|
|
+ {227, 119},
|
|
+ {227, 113},
|
|
+ {227, 107},
|
|
+ {227, 101},
|
|
+ {227, 96},
|
|
+ {195, 113},
|
|
+ {195, 106},
|
|
+ {195, 102},
|
|
+ {195, 95},
|
|
+ {163, 113},
|
|
+ {163, 106},
|
|
+ {163, 102},
|
|
+ {163, 95},
|
|
+ {131, 113},
|
|
+ {131, 106},
|
|
+ {131, 102},
|
|
+ {131, 95},
|
|
+ {99, 113},
|
|
+ {99, 106},
|
|
+ {99, 102},
|
|
+ {99, 95},
|
|
+ {67, 113},
|
|
+ {67, 106},
|
|
+ {67, 102},
|
|
+ {67, 95},
|
|
+ {35, 113},
|
|
+ {35, 106},
|
|
+ {35, 102},
|
|
+ {35, 95},
|
|
+ {3, 113},
|
|
+ {3, 106},
|
|
+ {3, 102},
|
|
+ {3, 95} }, /* 2.4 GHz, lowest power */
|
|
+ {
|
|
+ {251, 127}, /* 5.x GHz, highest power */
|
|
+ {251, 120},
|
|
+ {251, 114},
|
|
+ {219, 119},
|
|
+ {219, 101},
|
|
+ {187, 113},
|
|
+ {187, 102},
|
|
+ {155, 114},
|
|
+ {155, 103},
|
|
+ {123, 117},
|
|
+ {123, 107},
|
|
+ {123, 99},
|
|
+ {123, 92},
|
|
+ {91, 108},
|
|
+ {59, 125},
|
|
+ {59, 118},
|
|
+ {59, 109},
|
|
+ {59, 102},
|
|
+ {59, 96},
|
|
+ {59, 90},
|
|
+ {27, 104},
|
|
+ {27, 98},
|
|
+ {27, 92},
|
|
+ {115, 118},
|
|
+ {115, 111},
|
|
+ {115, 104},
|
|
+ {83, 126},
|
|
+ {83, 121},
|
|
+ {83, 113},
|
|
+ {83, 105},
|
|
+ {83, 99},
|
|
+ {51, 118},
|
|
+ {51, 111},
|
|
+ {51, 104},
|
|
+ {51, 98},
|
|
+ {19, 116},
|
|
+ {19, 109},
|
|
+ {19, 102},
|
|
+ {19, 98},
|
|
+ {19, 93},
|
|
+ {171, 113},
|
|
+ {171, 107},
|
|
+ {171, 99},
|
|
+ {139, 120},
|
|
+ {139, 113},
|
|
+ {139, 107},
|
|
+ {139, 99},
|
|
+ {107, 120},
|
|
+ {107, 113},
|
|
+ {107, 107},
|
|
+ {107, 99},
|
|
+ {75, 120},
|
|
+ {75, 113},
|
|
+ {75, 107},
|
|
+ {75, 99},
|
|
+ {43, 120},
|
|
+ {43, 113},
|
|
+ {43, 107},
|
|
+ {43, 99},
|
|
+ {11, 120},
|
|
+ {11, 113},
|
|
+ {11, 107},
|
|
+ {11, 99},
|
|
+ {131, 107},
|
|
+ {131, 99},
|
|
+ {99, 120},
|
|
+ {99, 113},
|
|
+ {99, 107},
|
|
+ {99, 99},
|
|
+ {67, 120},
|
|
+ {67, 113},
|
|
+ {67, 107},
|
|
+ {67, 99},
|
|
+ {35, 120},
|
|
+ {35, 113},
|
|
+ {35, 107},
|
|
+ {35, 99},
|
|
+ {3, 120} } /* 5.x GHz, lowest power */
|
|
+};
|
|
+
|
|
+static inline u8 iwl_hw_reg_fix_power_index(int index)
|
|
+{
|
|
+ if (index < 0)
|
|
+ return 0;
|
|
+ if (index >= IWL_MAX_GAIN_ENTRIES)
|
|
+ return IWL_MAX_GAIN_ENTRIES - 1;
|
|
+ return (u8) index;
|
|
+}
|
|
+
|
|
+/* Kick off thermal recalibration check every 60 seconds */
|
|
+#define REG_RECALIB_PERIOD (60)
|
|
+
|
|
+/**
|
|
+ * iwl_hw_reg_set_scan_power - Set Tx power for scan probe requests
|
|
+ *
|
|
+ * Set (in our channel info database) the direct scan Tx power for 1 Mbit (CCK)
|
|
+ * or 6 Mbit (OFDM) rates.
|
|
+ */
|
|
+static void iwl_hw_reg_set_scan_power(struct iwl_priv *priv, u32 scan_tbl_index,
|
|
+ s32 rate_index, const s8 *clip_pwrs,
|
|
+ struct iwl_channel_info *ch_info,
|
|
+ int band_index)
|
|
+{
|
|
+ struct iwl_scan_power_info *scan_power_info;
|
|
+ s8 power;
|
|
+ u8 power_index;
|
|
+
|
|
+ scan_power_info = &ch_info->scan_pwr_info[scan_tbl_index];
|
|
+
|
|
+ /* use this channel group's 6Mbit clipping/saturation pwr,
|
|
+ * but cap at regulatory scan power restriction (set during init
|
|
+ * based on eeprom channel data) for this channel. */
|
|
+ power = min(ch_info->scan_power, clip_pwrs[IWL_RATE_6M_INDEX]);
|
|
+
|
|
+ /* further limit to user's max power preference.
|
|
+ * FIXME: Other spectrum management power limitations do not
|
|
+ * seem to apply?? */
|
|
+ power = min(power, priv->user_txpower_limit);
|
|
+ scan_power_info->requested_power = power;
|
|
+
|
|
+ /* find difference between new scan *power* and current "normal"
|
|
+ * Tx *power* for 6Mb. Use this difference (x2) to adjust the
|
|
+ * current "normal" temperature-compensated Tx power *index* for
|
|
+ * this rate (1Mb or 6Mb) to yield new temp-compensated scan power
|
|
+ * *index*. */
|
|
+ power_index = ch_info->power_info[rate_index].power_table_index
|
|
+ - (power - ch_info->power_info
|
|
+ [IWL_RATE_6M_INDEX].requested_power) * 2;
|
|
+
|
|
+ /* store reference index that we use when adjusting *all* scan
|
|
+ * powers. So we can accommodate user (all channel) or spectrum
|
|
+ * management (single channel) power changes "between" temperature
|
|
+ * feedback compensation procedures.
|
|
+ * don't force fit this reference index into gain table; it may be a
|
|
+ * negative number. This will help avoid errors when we're at
|
|
+ * the lower bounds (highest gains, for warmest temperatures)
|
|
+ * of the table. */
|
|
+
|
|
+ /* don't exceed table bounds for "real" setting */
|
|
+ power_index = iwl_hw_reg_fix_power_index(power_index);
|
|
+
|
|
+ scan_power_info->power_table_index = power_index;
|
|
+ scan_power_info->tpc.tx_gain =
|
|
+ power_gain_table[band_index][power_index].tx_gain;
|
|
+ scan_power_info->tpc.dsp_atten =
|
|
+ power_gain_table[band_index][power_index].dsp_atten;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_hw_reg_send_txpower - fill in Tx Power command with gain settings
|
|
+ *
|
|
+ * Configures power settings for all rates for the current channel,
|
|
+ * using values from channel info struct, and send to NIC
|
|
+ */
|
|
+int iwl_hw_reg_send_txpower(struct iwl_priv *priv)
|
|
+{
|
|
+ int rate_idx;
|
|
+ const struct iwl_channel_info *ch_info = NULL;
|
|
+ struct iwl_txpowertable_cmd txpower = {
|
|
+ .channel = priv->active_rxon.channel,
|
|
+ };
|
|
+
|
|
+ txpower.band = (priv->phymode == MODE_IEEE80211A) ? 0 : 1;
|
|
+ ch_info = iwl_get_channel_info(priv,
|
|
+ priv->phymode,
|
|
+ le16_to_cpu(priv->active_rxon.channel));
|
|
+ if (!ch_info) {
|
|
+ IWL_ERROR
|
|
+ ("Failed to get channel info for channel %d [%d]\n",
|
|
+ le16_to_cpu(priv->active_rxon.channel), priv->phymode);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (!is_channel_valid(ch_info)) {
|
|
+ IWL_DEBUG_POWER("Not calling TX_PWR_TABLE_CMD on "
|
|
+ "non-Tx channel.\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* fill cmd with power settings for all rates for current channel */
|
|
+ for (rate_idx = 0; rate_idx < IWL_RATE_COUNT; rate_idx++) {
|
|
+ txpower.power[rate_idx].tpc = ch_info->power_info[rate_idx].tpc;
|
|
+ txpower.power[rate_idx].rate = iwl_rates[rate_idx].plcp;
|
|
+
|
|
+ IWL_DEBUG_POWER("ch %d:%d rf %d dsp %3d rate code 0x%02x\n",
|
|
+ le16_to_cpu(txpower.channel),
|
|
+ txpower.band,
|
|
+ txpower.power[rate_idx].tpc.tx_gain,
|
|
+ txpower.power[rate_idx].tpc.dsp_atten,
|
|
+ txpower.power[rate_idx].rate);
|
|
+ }
|
|
+
|
|
+ return iwl_send_cmd_pdu(priv, REPLY_TX_PWR_TABLE_CMD,
|
|
+ sizeof(struct iwl_txpowertable_cmd), &txpower);
|
|
+
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_hw_reg_set_new_power - Configures power tables at new levels
|
|
+ * @ch_info: Channel to update. Uses power_info.requested_power.
|
|
+ *
|
|
+ * Replace requested_power and base_power_index ch_info fields for
|
|
+ * one channel.
|
|
+ *
|
|
+ * Called if user or spectrum management changes power preferences.
|
|
+ * Takes into account h/w and modulation limitations (clip power).
|
|
+ *
|
|
+ * This does *not* send anything to NIC, just sets up ch_info for one channel.
|
|
+ *
|
|
+ * NOTE: reg_compensate_for_temperature_dif() *must* be run after this to
|
|
+ * properly fill out the scan powers, and actual h/w gain settings,
|
|
+ * and send changes to NIC
|
|
+ */
|
|
+static int iwl_hw_reg_set_new_power(struct iwl_priv *priv,
|
|
+ struct iwl_channel_info *ch_info)
|
|
+{
|
|
+ struct iwl_channel_power_info *power_info;
|
|
+ int power_changed = 0;
|
|
+ int i;
|
|
+ const s8 *clip_pwrs;
|
|
+ int power;
|
|
+
|
|
+ /* Get this chnlgrp's rate-to-max/clip-powers table */
|
|
+ clip_pwrs = priv->clip_groups[ch_info->group_index].clip_powers;
|
|
+
|
|
+ /* Get this channel's rate-to-current-power settings table */
|
|
+ power_info = ch_info->power_info;
|
|
+
|
|
+ /* update OFDM Txpower settings */
|
|
+ for (i = IWL_FIRST_OFDM_RATE; i <= IWL_LAST_OFDM_RATE;
|
|
+ i++, ++power_info) {
|
|
+ int delta_idx;
|
|
+
|
|
+ /* limit new power to be no more than h/w capability */
|
|
+ power = min(ch_info->curr_txpow, clip_pwrs[i]);
|
|
+ if (power == power_info->requested_power)
|
|
+ continue;
|
|
+
|
|
+ /* find difference between old and new requested powers,
|
|
+ * update base (non-temp-compensated) power index */
|
|
+ delta_idx = (power - power_info->requested_power) * 2;
|
|
+ power_info->base_power_index -= delta_idx;
|
|
+
|
|
+ /* save new requested power value */
|
|
+ power_info->requested_power = power;
|
|
+
|
|
+ power_changed = 1;
|
|
+ }
|
|
+
|
|
+ /* update CCK Txpower settings, based on OFDM 12M setting ...
|
|
+ * ... all CCK power settings for a given channel are the *same*. */
|
|
+ if (power_changed) {
|
|
+ power =
|
|
+ ch_info->power_info[IWL_RATE_12M_INDEX].
|
|
+ requested_power + IWL_CCK_FROM_OFDM_POWER_DIFF;
|
|
+
|
|
+ /* do all CCK rates' iwl_channel_power_info structures */
|
|
+ for (i = IWL_FIRST_CCK_RATE; i <= IWL_LAST_CCK_RATE; i++) {
|
|
+ power_info->requested_power = power;
|
|
+ power_info->base_power_index =
|
|
+ ch_info->power_info[IWL_RATE_12M_INDEX].
|
|
+ base_power_index + IWL_CCK_FROM_OFDM_INDEX_DIFF;
|
|
+ ++power_info;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_hw_reg_get_ch_txpower_limit - returns new power limit for channel
|
|
+ *
|
|
+ * NOTE: Returned power limit may be less (but not more) than requested,
|
|
+ * based strictly on regulatory (eeprom and spectrum mgt) limitations
|
|
+ * (no consideration for h/w clipping limitations).
|
|
+ */
|
|
+static int iwl_hw_reg_get_ch_txpower_limit(struct iwl_channel_info *ch_info)
|
|
+{
|
|
+ s8 max_power;
|
|
+
|
|
+#if 0
|
|
+ /* if we're using TGd limits, use lower of TGd or EEPROM */
|
|
+ if (ch_info->tgd_data.max_power != 0)
|
|
+ max_power = min(ch_info->tgd_data.max_power,
|
|
+ ch_info->eeprom.max_power_avg);
|
|
+
|
|
+ /* else just use EEPROM limits */
|
|
+ else
|
|
+#endif
|
|
+ max_power = ch_info->eeprom.max_power_avg;
|
|
+
|
|
+ return min(max_power, ch_info->max_power_avg);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_hw_reg_comp_txpower_temp - Compensate for temperature
|
|
+ *
|
|
+ * Compensate txpower settings of *all* channels for temperature.
|
|
+ * This only accounts for the difference between current temperature
|
|
+ * and the factory calibration temperatures, and bases the new settings
|
|
+ * on the channel's base_power_index.
|
|
+ *
|
|
+ * If RxOn is "associated", this sends the new Txpower to NIC!
|
|
+ */
|
|
+static int iwl_hw_reg_comp_txpower_temp(struct iwl_priv *priv)
|
|
+{
|
|
+ struct iwl_channel_info *ch_info = NULL;
|
|
+ int delta_index;
|
|
+ const s8 *clip_pwrs; /* array of h/w max power levels for each rate */
|
|
+ u8 a_band;
|
|
+ u8 rate_index;
|
|
+ u8 scan_tbl_index;
|
|
+ u8 i;
|
|
+ int ref_temp;
|
|
+ int temperature = priv->temperature;
|
|
+
|
|
+ /* set up new Tx power info for each and every channel, 2.4 and 5.x */
|
|
+ for (i = 0; i < priv->channel_count; i++) {
|
|
+ ch_info = &priv->channel_info[i];
|
|
+ a_band = is_channel_a_band(ch_info);
|
|
+
|
|
+ /* Get this chnlgrp's factory calibration temperature */
|
|
+ ref_temp = (s16)priv->eeprom.groups[ch_info->group_index].
|
|
+ temperature;
|
|
+
|
|
+ /* get power index adjustment based on curr and factory
|
|
+ * temps */
|
|
+ delta_index = iwl_hw_reg_adjust_power_by_temp(temperature,
|
|
+ ref_temp);
|
|
+
|
|
+ /* set tx power value for all rates, OFDM and CCK */
|
|
+ for (rate_index = 0; rate_index < IWL_RATE_COUNT;
|
|
+ rate_index++) {
|
|
+ int power_idx =
|
|
+ ch_info->power_info[rate_index].base_power_index;
|
|
+
|
|
+ /* temperature compensate */
|
|
+ power_idx += delta_index;
|
|
+
|
|
+ /* stay within table range */
|
|
+ power_idx = iwl_hw_reg_fix_power_index(power_idx);
|
|
+ ch_info->power_info[rate_index].
|
|
+ power_table_index = (u8) power_idx;
|
|
+ ch_info->power_info[rate_index].tpc =
|
|
+ power_gain_table[a_band][power_idx];
|
|
+ }
|
|
+
|
|
+ /* Get this chnlgrp's rate-to-max/clip-powers table */
|
|
+ clip_pwrs = priv->clip_groups[ch_info->group_index].clip_powers;
|
|
+
|
|
+ /* set scan tx power, 1Mbit for CCK, 6Mbit for OFDM */
|
|
+ for (scan_tbl_index = 0;
|
|
+ scan_tbl_index < IWL_NUM_SCAN_RATES; scan_tbl_index++) {
|
|
+ s32 actual_index = (scan_tbl_index == 0) ?
|
|
+ IWL_RATE_1M_INDEX : IWL_RATE_6M_INDEX;
|
|
+ iwl_hw_reg_set_scan_power(priv, scan_tbl_index,
|
|
+ actual_index, clip_pwrs,
|
|
+ ch_info, a_band);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* send Txpower command for current channel to ucode */
|
|
+ return iwl_hw_reg_send_txpower(priv);
|
|
+}
|
|
+
|
|
+int iwl_hw_reg_set_txpower(struct iwl_priv *priv, s8 power)
|
|
+{
|
|
+ struct iwl_channel_info *ch_info;
|
|
+ s8 max_power;
|
|
+ u8 a_band;
|
|
+ u8 i;
|
|
+
|
|
+ if (priv->user_txpower_limit == power) {
|
|
+ IWL_DEBUG_POWER("Requested Tx power same as current "
|
|
+ "limit: %ddBm.\n", power);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_POWER("Setting upper limit clamp to %ddBm.\n", power);
|
|
+ priv->user_txpower_limit = power;
|
|
+
|
|
+ /* set up new Tx powers for each and every channel, 2.4 and 5.x */
|
|
+
|
|
+ for (i = 0; i < priv->channel_count; i++) {
|
|
+ ch_info = &priv->channel_info[i];
|
|
+ a_band = is_channel_a_band(ch_info);
|
|
+
|
|
+ /* find minimum power of all user and regulatory constraints
|
|
+ * (does not consider h/w clipping limitations) */
|
|
+ max_power = iwl_hw_reg_get_ch_txpower_limit(ch_info);
|
|
+ max_power = min(power, max_power);
|
|
+ if (max_power != ch_info->curr_txpow) {
|
|
+ ch_info->curr_txpow = max_power;
|
|
+
|
|
+ /* this considers the h/w clipping limitations */
|
|
+ iwl_hw_reg_set_new_power(priv, ch_info);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* update txpower settings for all channels,
|
|
+ * send to NIC if associated. */
|
|
+ is_temp_calib_needed(priv);
|
|
+ iwl_hw_reg_comp_txpower_temp(priv);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* will add 3945 channel switch cmd handling later */
|
|
+int iwl_hw_channel_switch(struct iwl_priv *priv, u16 channel)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl3945_reg_txpower_periodic - called when time to check our temperature.
|
|
+ *
|
|
+ * -- reset periodic timer
|
|
+ * -- see if temp has changed enough to warrant re-calibration ... if so:
|
|
+ * -- correct coeffs for temp (can reset temp timer)
|
|
+ * -- save this temp as "last",
|
|
+ * -- send new set of gain settings to NIC
|
|
+ * NOTE: This should continue working, even when we're not associated,
|
|
+ * so we can keep our internal table of scan powers current. */
|
|
+void iwl3945_reg_txpower_periodic(struct iwl_priv *priv)
|
|
+{
|
|
+ /* This will kick in the "brute force"
|
|
+ * iwl_hw_reg_comp_txpower_temp() below */
|
|
+ if (!is_temp_calib_needed(priv))
|
|
+ goto reschedule;
|
|
+
|
|
+ /* Set up a new set of temp-adjusted TxPowers, send to NIC.
|
|
+ * This is based *only* on current temperature,
|
|
+ * ignoring any previous power measurements */
|
|
+ iwl_hw_reg_comp_txpower_temp(priv);
|
|
+
|
|
+ reschedule:
|
|
+ queue_delayed_work(priv->workqueue,
|
|
+ &priv->thermal_periodic, REG_RECALIB_PERIOD * HZ);
|
|
+}
|
|
+
|
|
+void iwl3945_bg_reg_txpower_periodic(struct work_struct *work)
|
|
+{
|
|
+ struct iwl_priv *priv = container_of(work, struct iwl_priv,
|
|
+ thermal_periodic.work);
|
|
+
|
|
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
|
+ return;
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+ iwl3945_reg_txpower_periodic(priv);
|
|
+ mutex_unlock(&priv->mutex);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_hw_reg_get_ch_grp_index - find the channel-group index (0-4)
|
|
+ * for the channel.
|
|
+ *
|
|
+ * This function is used when initializing channel-info structs.
|
|
+ *
|
|
+ * NOTE: These channel groups do *NOT* match the bands above!
|
|
+ * These channel groups are based on factory-tested channels;
|
|
+ * on A-band, EEPROM's "group frequency" entries represent the top
|
|
+ * channel in each group 1-4. Group 5 All B/G channels are in group 0.
|
|
+ */
|
|
+static u16 iwl_hw_reg_get_ch_grp_index(struct iwl_priv *priv,
|
|
+ const struct iwl_channel_info *ch_info)
|
|
+{
|
|
+ struct iwl_eeprom_txpower_group *ch_grp = &priv->eeprom.groups[0];
|
|
+ u8 group;
|
|
+ u16 group_index = 0; /* based on factory calib frequencies */
|
|
+ u8 grp_channel;
|
|
+
|
|
+ /* Find the group index for the channel ... don't use index 1(?) */
|
|
+ if (is_channel_a_band(ch_info)) {
|
|
+ for (group = 1; group < 5; group++) {
|
|
+ grp_channel = ch_grp[group].group_channel;
|
|
+ if (ch_info->channel <= grp_channel) {
|
|
+ group_index = group;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ /* group 4 has a few channels *above* its factory cal freq */
|
|
+ if (group == 5)
|
|
+ group_index = 4;
|
|
+ } else
|
|
+ group_index = 0; /* 2.4 GHz, group 0 */
|
|
+
|
|
+ IWL_DEBUG_POWER("Chnl %d mapped to grp %d\n", ch_info->channel,
|
|
+ group_index);
|
|
+ return group_index;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_hw_reg_get_matched_power_index - Interpolate to get nominal index
|
|
+ *
|
|
+ * Interpolate to get nominal (i.e. at factory calibration temperature) index
|
|
+ * into radio/DSP gain settings table for requested power.
|
|
+ */
|
|
+static int iwl_hw_reg_get_matched_power_index(struct iwl_priv *priv,
|
|
+ s8 requested_power,
|
|
+ s32 setting_index, s32 *new_index)
|
|
+{
|
|
+ const struct iwl_eeprom_txpower_group *chnl_grp = NULL;
|
|
+ s32 index0, index1;
|
|
+ s32 power = 2 * requested_power;
|
|
+ s32 i;
|
|
+ const struct iwl_eeprom_txpower_sample *samples;
|
|
+ s32 gains0, gains1;
|
|
+ s32 res;
|
|
+ s32 denominator;
|
|
+
|
|
+ chnl_grp = &priv->eeprom.groups[setting_index];
|
|
+ samples = chnl_grp->samples;
|
|
+ for (i = 0; i < 5; i++) {
|
|
+ if (power == samples[i].power) {
|
|
+ *new_index = samples[i].gain_index;
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (power > samples[1].power) {
|
|
+ index0 = 0;
|
|
+ index1 = 1;
|
|
+ } else if (power > samples[2].power) {
|
|
+ index0 = 1;
|
|
+ index1 = 2;
|
|
+ } else if (power > samples[3].power) {
|
|
+ index0 = 2;
|
|
+ index1 = 3;
|
|
+ } else {
|
|
+ index0 = 3;
|
|
+ index1 = 4;
|
|
+ }
|
|
+
|
|
+ denominator = (s32) samples[index1].power - (s32) samples[index0].power;
|
|
+ if (denominator == 0)
|
|
+ return -EINVAL;
|
|
+ gains0 = (s32) samples[index0].gain_index * (1 << 19);
|
|
+ gains1 = (s32) samples[index1].gain_index * (1 << 19);
|
|
+ res = gains0 + (gains1 - gains0) *
|
|
+ ((s32) power - (s32) samples[index0].power) / denominator +
|
|
+ (1 << 18);
|
|
+ *new_index = res >> 19;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void iwl_hw_reg_init_channel_groups(struct iwl_priv *priv)
|
|
+{
|
|
+ u32 i;
|
|
+ s32 rate_index;
|
|
+ const struct iwl_eeprom_txpower_group *group;
|
|
+
|
|
+ IWL_DEBUG_POWER("Initializing factory calib info from EEPROM\n");
|
|
+
|
|
+ for (i = 0; i < IWL_NUM_TX_CALIB_GROUPS; i++) {
|
|
+ s8 *clip_pwrs; /* table of power levels for each rate */
|
|
+ s8 satur_pwr; /* saturation power for each chnl group */
|
|
+ group = &priv->eeprom.groups[i];
|
|
+
|
|
+ /* sanity check on factory saturation power value */
|
|
+ if (group->saturation_power < 40) {
|
|
+ IWL_WARNING("Error: saturation power is %d, "
|
|
+ "less than minimum expected 40\n",
|
|
+ group->saturation_power);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Derive requested power levels for each rate, based on
|
|
+ * hardware capabilities (saturation power for band).
|
|
+ * Basic value is 3dB down from saturation, with further
|
|
+ * power reductions for highest 3 data rates. These
|
|
+ * backoffs provide headroom for high rate modulation
|
|
+ * power peaks, without too much distortion (clipping).
|
|
+ */
|
|
+ /* we'll fill in this array with h/w max power levels */
|
|
+ clip_pwrs = (s8 *) priv->clip_groups[i].clip_powers;
|
|
+
|
|
+ /* divide factory saturation power by 2 to find -3dB level */
|
|
+ satur_pwr = (s8) (group->saturation_power >> 1);
|
|
+
|
|
+ /* fill in channel group's nominal powers for each rate */
|
|
+ for (rate_index = 0;
|
|
+ rate_index < IWL_RATE_COUNT; rate_index++, clip_pwrs++) {
|
|
+ switch (rate_index) {
|
|
+ case IWL_RATE_36M_INDEX:
|
|
+ if (i == 0) /* B/G */
|
|
+ *clip_pwrs = satur_pwr;
|
|
+ else /* A */
|
|
+ *clip_pwrs = satur_pwr - 5;
|
|
+ break;
|
|
+ case IWL_RATE_48M_INDEX:
|
|
+ if (i == 0)
|
|
+ *clip_pwrs = satur_pwr - 7;
|
|
+ else
|
|
+ *clip_pwrs = satur_pwr - 10;
|
|
+ break;
|
|
+ case IWL_RATE_54M_INDEX:
|
|
+ if (i == 0)
|
|
+ *clip_pwrs = satur_pwr - 9;
|
|
+ else
|
|
+ *clip_pwrs = satur_pwr - 12;
|
|
+ break;
|
|
+ default:
|
|
+ *clip_pwrs = satur_pwr;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl3945_txpower_set_from_eeprom - Set channel power info based on EEPROM
|
|
+ *
|
|
+ * Second pass (during init) to set up priv->channel_info
|
|
+ *
|
|
+ * Set up Tx-power settings in our channel info database for each VALID
|
|
+ * (for this geo/SKU) channel, at all Tx data rates, based on eeprom values
|
|
+ * and current temperature.
|
|
+ *
|
|
+ * Since this is based on current temperature (at init time), these values may
|
|
+ * not be valid for very long, but it gives us a starting/default point,
|
|
+ * and allows us to active (i.e. using Tx) scan.
|
|
+ *
|
|
+ * This does *not* write values to NIC, just sets up our internal table.
|
|
+ */
|
|
+int iwl3945_txpower_set_from_eeprom(struct iwl_priv *priv)
|
|
+{
|
|
+ struct iwl_channel_info *ch_info = NULL;
|
|
+ struct iwl_channel_power_info *pwr_info;
|
|
+ int delta_index;
|
|
+ u8 rate_index;
|
|
+ u8 scan_tbl_index;
|
|
+ const s8 *clip_pwrs; /* array of power levels for each rate */
|
|
+ u8 gain, dsp_atten;
|
|
+ s8 power;
|
|
+ u8 pwr_index, base_pwr_index, a_band;
|
|
+ u8 i;
|
|
+ int temperature;
|
|
+
|
|
+ /* save temperature reference,
|
|
+ * so we can determine next time to calibrate */
|
|
+ temperature = iwl_hw_reg_txpower_get_temperature(priv);
|
|
+ priv->last_temperature = temperature;
|
|
+
|
|
+ iwl_hw_reg_init_channel_groups(priv);
|
|
+
|
|
+ /* initialize Tx power info for each and every channel, 2.4 and 5.x */
|
|
+ for (i = 0, ch_info = priv->channel_info; i < priv->channel_count;
|
|
+ i++, ch_info++) {
|
|
+ a_band = is_channel_a_band(ch_info);
|
|
+ if (!is_channel_valid(ch_info))
|
|
+ continue;
|
|
+
|
|
+ /* find this channel's channel group (*not* "band") index */
|
|
+ ch_info->group_index =
|
|
+ iwl_hw_reg_get_ch_grp_index(priv, ch_info);
|
|
+
|
|
+ /* Get this chnlgrp's rate->max/clip-powers table */
|
|
+ clip_pwrs = priv->clip_groups[ch_info->group_index].clip_powers;
|
|
+
|
|
+ /* calculate power index *adjustment* value according to
|
|
+ * diff between current temperature and factory temperature */
|
|
+ delta_index = iwl_hw_reg_adjust_power_by_temp(temperature,
|
|
+ priv->eeprom.groups[ch_info->group_index].
|
|
+ temperature);
|
|
+
|
|
+ IWL_DEBUG_POWER("Delta index for channel %d: %d [%d]\n",
|
|
+ ch_info->channel, delta_index, temperature +
|
|
+ IWL_TEMP_CONVERT);
|
|
+
|
|
+ /* set tx power value for all OFDM rates */
|
|
+ for (rate_index = 0; rate_index < IWL_OFDM_RATES;
|
|
+ rate_index++) {
|
|
+ s32 power_idx;
|
|
+ int rc;
|
|
+
|
|
+ /* use channel group's clip-power table,
|
|
+ * but don't exceed channel's max power */
|
|
+ s8 pwr = min(ch_info->max_power_avg,
|
|
+ clip_pwrs[rate_index]);
|
|
+
|
|
+ pwr_info = &ch_info->power_info[rate_index];
|
|
+
|
|
+ /* get base (i.e. at factory-measured temperature)
|
|
+ * power table index for this rate's power */
|
|
+ rc = iwl_hw_reg_get_matched_power_index(priv, pwr,
|
|
+ ch_info->group_index,
|
|
+ &power_idx);
|
|
+ if (rc) {
|
|
+ IWL_ERROR("Invalid power index\n");
|
|
+ return rc;
|
|
+ }
|
|
+ pwr_info->base_power_index = (u8) power_idx;
|
|
+
|
|
+ /* temperature compensate */
|
|
+ power_idx += delta_index;
|
|
+
|
|
+ /* stay within range of gain table */
|
|
+ power_idx = iwl_hw_reg_fix_power_index(power_idx);
|
|
+
|
|
+ /* fill 1 OFDM rate's iwl_channel_power_info struct */
|
|
+ pwr_info->requested_power = pwr;
|
|
+ pwr_info->power_table_index = (u8) power_idx;
|
|
+ pwr_info->tpc.tx_gain =
|
|
+ power_gain_table[a_band][power_idx].tx_gain;
|
|
+ pwr_info->tpc.dsp_atten =
|
|
+ power_gain_table[a_band][power_idx].dsp_atten;
|
|
+ }
|
|
+
|
|
+ /* set tx power for CCK rates, based on OFDM 12 Mbit settings*/
|
|
+ pwr_info = &ch_info->power_info[IWL_RATE_12M_INDEX];
|
|
+ power = pwr_info->requested_power +
|
|
+ IWL_CCK_FROM_OFDM_POWER_DIFF;
|
|
+ pwr_index = pwr_info->power_table_index +
|
|
+ IWL_CCK_FROM_OFDM_INDEX_DIFF;
|
|
+ base_pwr_index = pwr_info->base_power_index +
|
|
+ IWL_CCK_FROM_OFDM_INDEX_DIFF;
|
|
+
|
|
+ /* stay within table range */
|
|
+ pwr_index = iwl_hw_reg_fix_power_index(pwr_index);
|
|
+ gain = power_gain_table[a_band][pwr_index].tx_gain;
|
|
+ dsp_atten = power_gain_table[a_band][pwr_index].dsp_atten;
|
|
+
|
|
+ /* fill each CCK rate's iwl_channel_power_info structure
|
|
+ * NOTE: All CCK-rate Txpwrs are the same for a given chnl!
|
|
+ * NOTE: CCK rates start at end of OFDM rates! */
|
|
+ for (rate_index = IWL_OFDM_RATES;
|
|
+ rate_index < IWL_RATE_COUNT; rate_index++) {
|
|
+ pwr_info = &ch_info->power_info[rate_index];
|
|
+ pwr_info->requested_power = power;
|
|
+ pwr_info->power_table_index = pwr_index;
|
|
+ pwr_info->base_power_index = base_pwr_index;
|
|
+ pwr_info->tpc.tx_gain = gain;
|
|
+ pwr_info->tpc.dsp_atten = dsp_atten;
|
|
+ }
|
|
+
|
|
+ /* set scan tx power, 1Mbit for CCK, 6Mbit for OFDM */
|
|
+ for (scan_tbl_index = 0;
|
|
+ scan_tbl_index < IWL_NUM_SCAN_RATES; scan_tbl_index++) {
|
|
+ s32 actual_index = (scan_tbl_index == 0) ?
|
|
+ IWL_RATE_1M_INDEX : IWL_RATE_6M_INDEX;
|
|
+ iwl_hw_reg_set_scan_power(priv, scan_tbl_index,
|
|
+ actual_index, clip_pwrs, ch_info, a_band);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int iwl_hw_rxq_stop(struct iwl_priv *priv)
|
|
+{
|
|
+ int rc;
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc) {
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ iwl_write_restricted(priv, FH_RCSR_CONFIG(0), 0);
|
|
+ rc = iwl_poll_restricted_bit(priv, FH_RSSR_STATUS, (1 << 24), 1000);
|
|
+ if (rc < 0)
|
|
+ IWL_ERROR("Can't stop Rx DMA.\n");
|
|
+
|
|
+ iwl_release_restricted_access(priv);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int iwl_hw_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq)
|
|
+{
|
|
+ int rc;
|
|
+ unsigned long flags;
|
|
+ int txq_id = txq->q.id;
|
|
+
|
|
+ struct iwl_shared *shared_data = priv->hw_setting.shared_virt;
|
|
+
|
|
+ shared_data->tx_base_ptr[txq_id] = cpu_to_le32((u32)txq->q.dma_addr);
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc) {
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ return rc;
|
|
+ }
|
|
+ iwl_write_restricted(priv, FH_CBCC_CTRL(txq_id), 0);
|
|
+ iwl_write_restricted(priv, FH_CBCC_BASE(txq_id), 0);
|
|
+
|
|
+ iwl_write_restricted(priv, FH_TCSR_CONFIG(txq_id),
|
|
+ ALM_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_NOINT |
|
|
+ ALM_FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_TXF |
|
|
+ ALM_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD |
|
|
+ ALM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL |
|
|
+ ALM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE);
|
|
+ iwl_release_restricted_access(priv);
|
|
+
|
|
+ /* fake read to flush all prev. writes */
|
|
+ iwl_read32(priv, FH_TSSR_CBB_BASE);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int iwl_hw_get_rx_read(struct iwl_priv *priv)
|
|
+{
|
|
+ struct iwl_shared *shared_data = priv->hw_setting.shared_virt;
|
|
+
|
|
+ return le32_to_cpu(shared_data->rx_read_ptr[0]);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl3945_init_hw_rate_table - Initialize the hardware rate fallback table
|
|
+ */
|
|
+int iwl3945_init_hw_rate_table(struct iwl_priv *priv)
|
|
+{
|
|
+ int rc, i;
|
|
+ struct iwl_rate_scaling_cmd rate_cmd = {
|
|
+ .reserved = {0, 0, 0},
|
|
+ };
|
|
+ struct iwl_rate_scaling_info *table = rate_cmd.table;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(iwl_rates); i++) {
|
|
+ table[i].rate_n_flags =
|
|
+ iwl_hw_set_rate_n_flags(iwl_rates[i].plcp, 0);
|
|
+ table[i].try_cnt = priv->retry_rate;
|
|
+ table[i].next_rate_index = iwl_get_prev_ieee_rate(i);
|
|
+ }
|
|
+
|
|
+ switch (priv->phymode) {
|
|
+ case MODE_IEEE80211A:
|
|
+ IWL_DEBUG_RATE("Select A mode rate scale\n");
|
|
+ /* If one of the following CCK rates is used,
|
|
+ * have it fall back to the 6M OFDM rate */
|
|
+ for (i = IWL_FIRST_CCK_RATE; i <= IWL_LAST_CCK_RATE; i++)
|
|
+ table[i].next_rate_index = IWL_FIRST_OFDM_RATE;
|
|
+
|
|
+ /* Don't fall back to CCK rates */
|
|
+ table[IWL_RATE_12M_INDEX].next_rate_index = IWL_RATE_9M_INDEX;
|
|
+
|
|
+ /* Don't drop out of OFDM rates */
|
|
+ table[IWL_FIRST_OFDM_RATE].next_rate_index =
|
|
+ IWL_FIRST_OFDM_RATE;
|
|
+ break;
|
|
+
|
|
+ case MODE_IEEE80211B:
|
|
+ IWL_DEBUG_RATE("Select B mode rate scale\n");
|
|
+ /* If an OFDM rate is used, have it fall back to the
|
|
+ * 1M CCK rates */
|
|
+ for (i = IWL_FIRST_OFDM_RATE; i <= IWL_LAST_OFDM_RATE; i++)
|
|
+ table[i].next_rate_index = IWL_FIRST_CCK_RATE;
|
|
+
|
|
+ /* CCK shouldn't fall back to OFDM... */
|
|
+ table[IWL_RATE_11M_INDEX].next_rate_index = IWL_RATE_5M_INDEX;
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ IWL_DEBUG_RATE("Select G mode rate scale\n");
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Update the rate scaling for control frame Tx */
|
|
+ rate_cmd.table_id = 0;
|
|
+ rc = iwl_send_cmd_pdu(priv, REPLY_RATE_SCALE, sizeof(rate_cmd),
|
|
+ &rate_cmd);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ /* Update the rate scaling for data frame Tx */
|
|
+ rate_cmd.table_id = 1;
|
|
+ return iwl_send_cmd_pdu(priv, REPLY_RATE_SCALE, sizeof(rate_cmd),
|
|
+ &rate_cmd);
|
|
+}
|
|
+
|
|
+int iwl_hw_set_hw_setting(struct iwl_priv *priv)
|
|
+{
|
|
+ memset((void *)&priv->hw_setting, 0,
|
|
+ sizeof(struct iwl_driver_hw_info));
|
|
+
|
|
+ priv->hw_setting.shared_virt =
|
|
+ pci_alloc_consistent(priv->pci_dev,
|
|
+ sizeof(struct iwl_shared),
|
|
+ &priv->hw_setting.shared_phys);
|
|
+
|
|
+ if (!priv->hw_setting.shared_virt) {
|
|
+ IWL_ERROR("failed to allocate pci memory\n");
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ priv->hw_setting.ac_queue_count = AC_NUM;
|
|
+ priv->hw_setting.rx_buffer_size = IWL_RX_BUF_SIZE;
|
|
+ priv->hw_setting.tx_cmd_len = sizeof(struct iwl_tx_cmd);
|
|
+ priv->hw_setting.max_rxq_size = RX_QUEUE_SIZE;
|
|
+ priv->hw_setting.max_rxq_log = RX_QUEUE_SIZE_LOG;
|
|
+ priv->hw_setting.cck_flag = 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+unsigned int iwl_hw_get_beacon_cmd(struct iwl_priv *priv,
|
|
+ struct iwl_frame *frame, u8 rate)
|
|
+{
|
|
+ struct iwl_tx_beacon_cmd *tx_beacon_cmd;
|
|
+ unsigned int frame_size;
|
|
+
|
|
+ tx_beacon_cmd = (struct iwl_tx_beacon_cmd *)&frame->u;
|
|
+ memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd));
|
|
+
|
|
+ tx_beacon_cmd->tx.sta_id = IWL_BROADCAST_ID;
|
|
+ tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
|
|
+
|
|
+ frame_size = iwl_fill_beacon_frame(priv,
|
|
+ tx_beacon_cmd->frame,
|
|
+ BROADCAST_ADDR,
|
|
+ sizeof(frame->u) - sizeof(*tx_beacon_cmd));
|
|
+
|
|
+ BUG_ON(frame_size > MAX_MPDU_SIZE);
|
|
+ tx_beacon_cmd->tx.len = cpu_to_le16((u16)frame_size);
|
|
+
|
|
+ tx_beacon_cmd->tx.rate = rate;
|
|
+ tx_beacon_cmd->tx.tx_flags = (TX_CMD_FLG_SEQ_CTL_MSK |
|
|
+ TX_CMD_FLG_TSF_MSK);
|
|
+
|
|
+ /* supp_rates[0] == OFDM */
|
|
+ tx_beacon_cmd->tx.supp_rates[0] = IWL_OFDM_BASIC_RATES_MASK;
|
|
+
|
|
+ /* supp_rates[1] == CCK
|
|
+ *
|
|
+ * NOTE: IWL_*_RATES_MASK are not in the order that supp_rates
|
|
+ * expects so we have to shift them around.
|
|
+ *
|
|
+ * supp_rates expects:
|
|
+ * CCK rates are bit0..3
|
|
+ *
|
|
+ * However IWL_*_RATES_MASK has:
|
|
+ * CCK rates are bit8..11
|
|
+ */
|
|
+ tx_beacon_cmd->tx.supp_rates[1] =
|
|
+ (IWL_CCK_BASIC_RATES_MASK >> 8) & 0xF;
|
|
+
|
|
+ return (sizeof(struct iwl_tx_beacon_cmd) + frame_size);
|
|
+}
|
|
+
|
|
+void iwl_hw_rx_handler_setup(struct iwl_priv *priv)
|
|
+{
|
|
+ priv->rx_handlers[REPLY_3945_RX] = iwl3945_rx_reply_rx;
|
|
+}
|
|
+
|
|
+void iwl_hw_setup_deferred_work(struct iwl_priv *priv)
|
|
+{
|
|
+ INIT_DELAYED_WORK(&priv->thermal_periodic,
|
|
+ iwl3945_bg_reg_txpower_periodic);
|
|
+}
|
|
+
|
|
+void iwl_hw_cancel_deferred_work(struct iwl_priv *priv)
|
|
+{
|
|
+ cancel_delayed_work(&priv->thermal_periodic);
|
|
+}
|
|
+
|
|
+struct pci_device_id iwl_hw_card_ids[] = {
|
|
+ {0x8086, 0x4222, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
|
+ {0x8086, 0x4227, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
|
+ {0}
|
|
+};
|
|
+
|
|
+inline int iwl_eeprom_aqcuire_semaphore(struct iwl_priv *priv)
|
|
+{
|
|
+ _iwl_clear_bit(priv, CSR_EEPROM_GP, CSR_EEPROM_GP_IF_OWNER_MSK);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids);
|
|
diff --git a/drivers/net/wireless/iwl-3945.h b/drivers/net/wireless/iwl-3945.h
|
|
new file mode 100644
|
|
index 0000000..0f4db4c
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/iwl-3945.h
|
|
@@ -0,0 +1,60 @@
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * Copyright(c) 2003 - 2007 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.
|
|
+ *
|
|
+ * Contact Information:
|
|
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
|
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+#ifndef __iwl_3945_h__
|
|
+#define __iwl_3945_h__
|
|
+
|
|
+#if IWL != 3945
|
|
+/*
|
|
+ * In non IWL == 3945 builds, these must build to nothing in order to allow
|
|
+ * the common code to not have several #if IWL == XXXX / #endif blocks
|
|
+ */
|
|
+static inline __le32 iwl3945_get_antenna_flags(const struct iwl_priv *priv)
|
|
+{ return 0; }
|
|
+static inline int iwl3945_init_hw_rate_table(struct iwl_priv *priv)
|
|
+{ return 0; }
|
|
+static inline void iwl3945_reg_txpower_periodic(struct iwl_priv *priv) {}
|
|
+static inline void iwl3945_bg_reg_txpower_periodic(struct work_struct *work)
|
|
+{}
|
|
+static inline int iwl3945_txpower_set_from_eeprom(struct iwl_priv *priv)
|
|
+{ return 0; }
|
|
+static inline u8 iwl3945_sync_sta(struct iwl_priv *priv, int sta_id,
|
|
+ u16 tx_rate, u8 flags) { return 0; }
|
|
+#else /* IWL == 3945 */
|
|
+/*
|
|
+ * Forward declare iwl-3945.c functions for iwl-base.c
|
|
+ */
|
|
+extern int iwl_eeprom_aqcuire_semaphore(struct iwl_priv *priv);
|
|
+extern __le32 iwl3945_get_antenna_flags(const struct iwl_priv *priv);
|
|
+extern int iwl3945_init_hw_rate_table(struct iwl_priv *priv);
|
|
+extern void iwl3945_reg_txpower_periodic(struct iwl_priv *priv);
|
|
+extern void iwl3945_bg_reg_txpower_periodic(struct work_struct *work);
|
|
+extern int iwl3945_txpower_set_from_eeprom(struct iwl_priv *priv);
|
|
+extern u8 iwl3945_sync_sta(struct iwl_priv *priv, int sta_id,
|
|
+ u16 tx_rate, u8 flags);
|
|
+#endif /* IWL == 3945 */
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/net/wireless/iwl-4965-hw.h b/drivers/net/wireless/iwl-4965-hw.h
|
|
new file mode 100644
|
|
index 0000000..858ec55
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/iwl-4965-hw.h
|
|
@@ -0,0 +1,835 @@
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * 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) 2005 - 2007 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 Geeral 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:
|
|
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
|
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
+ *
|
|
+ * BSD LICENSE
|
|
+ *
|
|
+ * Copyright(c) 2005 - 2007 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_4965_hw_h__
|
|
+#define __iwl_4965_hw_h__
|
|
+
|
|
+#define IWL_RX_BUF_SIZE (4 * 1024)
|
|
+#define IWL_MAX_BSM_SIZE BSM_SRAM_SIZE
|
|
+#define KDR_RTC_INST_UPPER_BOUND (0x018000)
|
|
+#define KDR_RTC_DATA_UPPER_BOUND (0x80A000)
|
|
+#define KDR_RTC_INST_SIZE (KDR_RTC_INST_UPPER_BOUND - RTC_INST_LOWER_BOUND)
|
|
+#define KDR_RTC_DATA_SIZE (KDR_RTC_DATA_UPPER_BOUND - RTC_DATA_LOWER_BOUND)
|
|
+
|
|
+#define IWL_MAX_INST_SIZE KDR_RTC_INST_SIZE
|
|
+#define IWL_MAX_DATA_SIZE KDR_RTC_DATA_SIZE
|
|
+
|
|
+static inline int iwl_hw_valid_rtc_data_addr(u32 addr)
|
|
+{
|
|
+ return ((addr >= RTC_DATA_LOWER_BOUND)
|
|
+ && (addr < KDR_RTC_DATA_UPPER_BOUND));
|
|
+}
|
|
+
|
|
+/********************* START TXPOWER *****************************************/
|
|
+enum {
|
|
+ HT_IE_EXT_CHANNEL_NONE = 0,
|
|
+ HT_IE_EXT_CHANNEL_ABOVE,
|
|
+ HT_IE_EXT_CHANNEL_INVALID,
|
|
+ HT_IE_EXT_CHANNEL_BELOW,
|
|
+ HT_IE_EXT_CHANNEL_MAX
|
|
+};
|
|
+
|
|
+enum {
|
|
+ CALIB_CH_GROUP_1 = 0,
|
|
+ CALIB_CH_GROUP_2 = 1,
|
|
+ CALIB_CH_GROUP_3 = 2,
|
|
+ CALIB_CH_GROUP_4 = 3,
|
|
+ CALIB_CH_GROUP_5 = 4,
|
|
+ CALIB_CH_GROUP_MAX
|
|
+};
|
|
+
|
|
+/* Temperature calibration offset is 3% 0C in Kelvin */
|
|
+#define TEMPERATURE_CALIB_KELVIN_OFFSET 8
|
|
+#define TEMPERATURE_CALIB_A_VAL 259
|
|
+
|
|
+#define IWL_TX_POWER_TEMPERATURE_MIN (263)
|
|
+#define IWL_TX_POWER_TEMPERATURE_MAX (410)
|
|
+
|
|
+#define IWL_TX_POWER_TEMPERATURE_OUT_OF_RANGE(t) \
|
|
+ (((t) < IWL_TX_POWER_TEMPERATURE_MIN) || \
|
|
+ ((t) > IWL_TX_POWER_TEMPERATURE_MAX))
|
|
+
|
|
+#define IWL_TX_POWER_ILLEGAL_TEMPERATURE (300)
|
|
+
|
|
+#define IWL_TX_POWER_TEMPERATURE_DIFFERENCE (2)
|
|
+
|
|
+#define IWL_TX_POWER_MIMO_REGULATORY_COMPENSATION (6)
|
|
+
|
|
+#define IWL_TX_POWER_TARGET_POWER_MIN (0) /* 0 dBm = 1 milliwatt */
|
|
+#define IWL_TX_POWER_TARGET_POWER_MAX (16) /* 16 dBm */
|
|
+
|
|
+/* timeout equivalent to 3 minutes */
|
|
+#define IWL_TX_POWER_TIMELIMIT_NOCALIB 1800000000
|
|
+
|
|
+#define IWL_TX_POWER_CCK_COMPENSATION (9)
|
|
+
|
|
+#define MIN_TX_GAIN_INDEX (0)
|
|
+#define MIN_TX_GAIN_INDEX_52GHZ_EXT (-9)
|
|
+#define MAX_TX_GAIN_INDEX_52GHZ (98)
|
|
+#define MIN_TX_GAIN_52GHZ (98)
|
|
+#define MAX_TX_GAIN_INDEX_24GHZ (98)
|
|
+#define MIN_TX_GAIN_24GHZ (98)
|
|
+#define MAX_TX_GAIN (0)
|
|
+#define MAX_TX_GAIN_52GHZ_EXT (-9)
|
|
+
|
|
+#define IWL_TX_POWER_DEFAULT_REGULATORY_24 (34)
|
|
+#define IWL_TX_POWER_DEFAULT_REGULATORY_52 (34)
|
|
+#define IWL_TX_POWER_REGULATORY_MIN (0)
|
|
+#define IWL_TX_POWER_REGULATORY_MAX (34)
|
|
+#define IWL_TX_POWER_DEFAULT_SATURATION_24 (38)
|
|
+#define IWL_TX_POWER_DEFAULT_SATURATION_52 (38)
|
|
+#define IWL_TX_POWER_SATURATION_MIN (20)
|
|
+#define IWL_TX_POWER_SATURATION_MAX (50)
|
|
+
|
|
+/* dv *0.4 = dt; so that 5 degrees temperature diff equals
|
|
+ * 12.5 in voltage diff */
|
|
+#define IWL_TX_TEMPERATURE_UPDATE_LIMIT 9
|
|
+
|
|
+#define IWL_INVALID_CHANNEL (0xffffffff)
|
|
+#define IWL_TX_POWER_REGITRY_BIT (2)
|
|
+
|
|
+#define MIN_IWL_TX_POWER_CALIB_DUR (100)
|
|
+#define IWL_CCK_FROM_OFDM_POWER_DIFF (-5)
|
|
+#define IWL_CCK_FROM_OFDM_INDEX_DIFF (9)
|
|
+
|
|
+/* Number of entries in the gain table */
|
|
+#define POWER_GAIN_NUM_ENTRIES 78
|
|
+#define TX_POW_MAX_SESSION_NUM 5
|
|
+/* timeout equivalent to 3 minutes */
|
|
+#define TX_IWL_TIMELIMIT_NOCALIB 1800000000
|
|
+
|
|
+/* Kedron TX_CALIB_STATES */
|
|
+#define IWL_TX_CALIB_STATE_SEND_TX 0x00000001
|
|
+#define IWL_TX_CALIB_WAIT_TX_RESPONSE 0x00000002
|
|
+#define IWL_TX_CALIB_ENABLED 0x00000004
|
|
+#define IWL_TX_CALIB_XVT_ON 0x00000008
|
|
+#define IWL_TX_CALIB_TEMPERATURE_CORRECT 0x00000010
|
|
+#define IWL_TX_CALIB_WORKING_WITH_XVT 0x00000020
|
|
+#define IWL_TX_CALIB_XVT_PERIODICAL 0x00000040
|
|
+
|
|
+#define NUM_IWL_TX_CALIB_SETTINS 5 /* Number of tx correction groups */
|
|
+
|
|
+#define IWL_MIN_POWER_IN_VP_TABLE 1 /* 0.5dBm multiplied by 2 */
|
|
+#define IWL_MAX_POWER_IN_VP_TABLE 40 /* 20dBm - multiplied by 2 (because
|
|
+ * entries are for each 0.5dBm) */
|
|
+#define IWL_STEP_IN_VP_TABLE 1 /* 0.5dB - multiplied by 2 */
|
|
+#define IWL_NUM_POINTS_IN_VPTABLE \
|
|
+ (1 + IWL_MAX_POWER_IN_VP_TABLE - IWL_MIN_POWER_IN_VP_TABLE)
|
|
+
|
|
+#define MIN_TX_GAIN_INDEX (0)
|
|
+#define MAX_TX_GAIN_INDEX_52GHZ (98)
|
|
+#define MIN_TX_GAIN_52GHZ (98)
|
|
+#define MAX_TX_GAIN_INDEX_24GHZ (98)
|
|
+#define MIN_TX_GAIN_24GHZ (98)
|
|
+#define MAX_TX_GAIN (0)
|
|
+
|
|
+/* First and last channels of all groups */
|
|
+#define CALIB_IWL_TX_ATTEN_GR1_FCH 34
|
|
+#define CALIB_IWL_TX_ATTEN_GR1_LCH 43
|
|
+#define CALIB_IWL_TX_ATTEN_GR2_FCH 44
|
|
+#define CALIB_IWL_TX_ATTEN_GR2_LCH 70
|
|
+#define CALIB_IWL_TX_ATTEN_GR3_FCH 71
|
|
+#define CALIB_IWL_TX_ATTEN_GR3_LCH 124
|
|
+#define CALIB_IWL_TX_ATTEN_GR4_FCH 125
|
|
+#define CALIB_IWL_TX_ATTEN_GR4_LCH 200
|
|
+#define CALIB_IWL_TX_ATTEN_GR5_FCH 1
|
|
+#define CALIB_IWL_TX_ATTEN_GR5_LCH 20
|
|
+
|
|
+
|
|
+union iwl_tx_power_dual_stream {
|
|
+ struct {
|
|
+ u8 radio_tx_gain[2];
|
|
+ u8 dsp_predis_atten[2];
|
|
+ } s;
|
|
+ u32 dw;
|
|
+};
|
|
+
|
|
+/********************* END TXPOWER *****************************************/
|
|
+
|
|
+/* HT flags */
|
|
+#define RXON_FLG_CONTROL_CHANNEL_LOCATION_POS (22)
|
|
+#define RXON_FLG_CONTROL_CHANNEL_LOCATION_MSK __constant_cpu_to_le32(0x1<<22)
|
|
+#define RXON_FLG_CONTROL_CHANNEL_LOC_LOW_MSK __constant_cpu_to_le32(0x0<<22)
|
|
+#define RXON_FLG_CONTROL_CHANNEL_LOC_HIGH_MSK __constant_cpu_to_le32(0x1<<22)
|
|
+
|
|
+#define RXON_FLG_HT_OPERATING_MODE_POS (23)
|
|
+
|
|
+#define RXON_FLG_HT_PROT_MSK __constant_cpu_to_le32(0x1<<23)
|
|
+#define RXON_FLG_FAT_PROT_MSK __constant_cpu_to_le32(0x2<<23)
|
|
+
|
|
+#define RXON_FLG_CHANNEL_MODE_POS (25)
|
|
+#define RXON_FLG_CHANNEL_MODE_MSK __constant_cpu_to_le32(0x3<<25)
|
|
+#define RXON_FLG_CHANNEL_MODE_LEGACY_MSK __constant_cpu_to_le32(0x0<<25)
|
|
+#define RXON_FLG_CHANNEL_MODE_PURE_40_MSK __constant_cpu_to_le32(0x1<<25)
|
|
+#define RXON_FLG_CHANNEL_MODE_MIXED_MSK __constant_cpu_to_le32(0x2<<25)
|
|
+
|
|
+#define RXON_RX_CHAIN_DRIVER_FORCE_MSK __constant_cpu_to_le16(0x1<<0)
|
|
+#define RXON_RX_CHAIN_VALID_MSK __constant_cpu_to_le16(0x7<<1)
|
|
+#define RXON_RX_CHAIN_VALID_POS (1)
|
|
+#define RXON_RX_CHAIN_FORCE_SEL_MSK __constant_cpu_to_le16(0x7<<4)
|
|
+#define RXON_RX_CHAIN_FORCE_SEL_POS (4)
|
|
+#define RXON_RX_CHAIN_FORCE_MIMO_SEL_MSK __constant_cpu_to_le16(0x7<<7)
|
|
+#define RXON_RX_CHAIN_FORCE_MIMO_SEL_POS (7)
|
|
+#define RXON_RX_CHAIN_CNT_MSK __constant_cpu_to_le16(0x3<<10)
|
|
+#define RXON_RX_CHAIN_CNT_POS (10)
|
|
+#define RXON_RX_CHAIN_MIMO_CNT_MSK __constant_cpu_to_le16(0x3<<12)
|
|
+#define RXON_RX_CHAIN_MIMO_CNT_POS (12)
|
|
+#define RXON_RX_CHAIN_MIMO_FORCE_MSK __constant_cpu_to_le16(0x1<<14)
|
|
+#define RXON_RX_CHAIN_MIMO_FORCE_POS (14)
|
|
+
|
|
+
|
|
+#define MCS_DUP_6M_PLCP 0x20
|
|
+
|
|
+/* OFDM HT rate masks */
|
|
+/* ***************************************** */
|
|
+#define R_MCS_6M_MSK 0x1
|
|
+#define R_MCS_12M_MSK 0x2
|
|
+#define R_MCS_18M_MSK 0x4
|
|
+#define R_MCS_24M_MSK 0x8
|
|
+#define R_MCS_36M_MSK 0x10
|
|
+#define R_MCS_48M_MSK 0x20
|
|
+#define R_MCS_54M_MSK 0x40
|
|
+#define R_MCS_60M_MSK 0x80
|
|
+#define R_MCS_12M_DUAL_MSK 0x100
|
|
+#define R_MCS_24M_DUAL_MSK 0x200
|
|
+#define R_MCS_36M_DUAL_MSK 0x400
|
|
+#define R_MCS_48M_DUAL_MSK 0x800
|
|
+
|
|
+#define is_legacy(tbl) (((tbl) == LQ_G) || ((tbl) == LQ_A))
|
|
+#define is_siso(tbl) (((tbl) == LQ_SISO))
|
|
+#define is_mimo(tbl) (((tbl) == LQ_MIMO))
|
|
+#define is_Ht(tbl) (is_siso(tbl) || is_mimo(tbl))
|
|
+#define is_a_band(tbl) (((tbl) == LQ_A))
|
|
+#define is_g_and(tbl) (((tbl) == LQ_G))
|
|
+
|
|
+/* Flow Handler Definitions */
|
|
+
|
|
+/**********************/
|
|
+/* Addresses */
|
|
+/**********************/
|
|
+
|
|
+#define FH_MEM_LOWER_BOUND (0x1000)
|
|
+#define FH_MEM_UPPER_BOUND (0x1EF0)
|
|
+
|
|
+#define IWL_FH_REGS_LOWER_BOUND (0x1000)
|
|
+#define IWL_FH_REGS_UPPER_BOUND (0x2000)
|
|
+
|
|
+/* TFDB Area - TFDs buffer table */
|
|
+#define FH_MEM_TFDB_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x000)
|
|
+#define FH_MEM_TFDB_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0x900)
|
|
+/* channels 0 - 8 */
|
|
+#define FH_MEM_TFDB_CHNL_BUF0(x) (FH_MEM_TFDB_LOWER_BOUND + (x) * 0x100)
|
|
+#define FH_MEM_TFDB_CHNL_BUF1(x) (FH_MEM_TFDB_LOWER_BOUND + 0x80 + (x) * 0x100)
|
|
+
|
|
+/* TFDIB Area - TFD Immediate Buffer */
|
|
+#define FH_MEM_TFDIB_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x900)
|
|
+#define FH_MEM_TFDIB_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0x958)
|
|
+/* channels 0 - 10 */
|
|
+#define FH_MEM_TFDIB_CHNL(x) (FH_MEM_TFDIB_LOWER_BOUND + (x) * 0x8)
|
|
+
|
|
+/* TFDIB registers used in Service Mode */
|
|
+#define FH_MEM_TFDIB_CHNL9_REG0 (FH_MEM_TFDIB_CHNL(9))
|
|
+#define FH_MEM_TFDIB_CHNL9_REG1 (FH_MEM_TFDIB_CHNL(9) + 4)
|
|
+#define FH_MEM_TFDIB_CHNL10_REG0 (FH_MEM_TFDIB_CHNL(10))
|
|
+#define FH_MEM_TFDIB_CHNL10_REG1 (FH_MEM_TFDIB_CHNL(10) + 4)
|
|
+
|
|
+/* Tx service channels */
|
|
+#define FH_MEM_TFDIB_DRAM_ADDR_LSB_MASK (0xFFFFFFFF)
|
|
+#define FH_MEM_TFDIB_DRAM_ADDR_MSB_MASK (0xF00000000)
|
|
+#define FH_MEM_TFDIB_TB_LENGTH_MASK (0x0001FFFF) /* bits 16:0 */
|
|
+
|
|
+#define FH_MEM_TFDIB_DRAM_ADDR_LSB_BITSHIFT (0)
|
|
+#define FH_MEM_TFDIB_DRAM_ADDR_MSB_BITSHIFT (32)
|
|
+#define FH_MEM_TFDIB_TB_LENGTH_BITSHIFT (0)
|
|
+
|
|
+#define FH_MEM_TFDIB_REG0_ADDR_MASK (0xFFFFFFFF)
|
|
+#define FH_MEM_TFDIB_REG1_ADDR_MASK (0xF0000000)
|
|
+#define FH_MEM_TFDIB_REG1_LENGTH_MASK (0x0001FFFF)
|
|
+
|
|
+#define FH_MEM_TFDIB_REG0_ADDR_BITSHIFT (0)
|
|
+#define FH_MEM_TFDIB_REG1_ADDR_BITSHIFT (28)
|
|
+#define FH_MEM_TFDIB_REG1_LENGTH_BITSHIFT (0)
|
|
+
|
|
+/* TRB Area - Transmit Request Buffers */
|
|
+#define FH_MEM_TRB_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x0958)
|
|
+#define FH_MEM_TRB_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0x0980)
|
|
+/* channels 0 - 8 */
|
|
+#define FH_MEM_TRB_CHNL(x) (FH_MEM_TRB_LOWER_BOUND + (x) * 0x4)
|
|
+
|
|
+#define IWL_FH_KW_MEM_ADDR_REG (FH_MEM_LOWER_BOUND + 0x97C)
|
|
+/* STAGB Area - Scheduler TAG Buffer */
|
|
+#define FH_MEM_STAGB_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x980)
|
|
+#define FH_MEM_STAGB_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0x9D0)
|
|
+/* channels 0 - 8 */
|
|
+#define FH_MEM_STAGB_0(x) (FH_MEM_STAGB_LOWER_BOUND + (x) * 0x8)
|
|
+#define FH_MEM_STAGB_1(x) (FH_MEM_STAGB_LOWER_BOUND + 0x4 + (x) * 0x8)
|
|
+
|
|
+/* Tx service channels */
|
|
+#define FH_MEM_SRAM_ADDR_9 (FH_MEM_STAGB_LOWER_BOUND + 0x048)
|
|
+#define FH_MEM_SRAM_ADDR_10 (FH_MEM_STAGB_LOWER_BOUND + 0x04C)
|
|
+
|
|
+#define FH_MEM_STAGB_SRAM_ADDR_MASK (0x00FFFFFF)
|
|
+
|
|
+/* CBBC Area - Circular buffers base address cache pointers table */
|
|
+#define FH_MEM_CBBC_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x9D0)
|
|
+#define FH_MEM_CBBC_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xA10)
|
|
+/* queues 0 - 15 */
|
|
+#define FH_MEM_CBBC_QUEUE(x) (FH_MEM_CBBC_LOWER_BOUND + (x) * 0x4)
|
|
+
|
|
+/* TAGR Area - TAG reconstruct table */
|
|
+#define FH_MEM_TAGR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xA10)
|
|
+#define FH_MEM_TAGR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xA70)
|
|
+
|
|
+/* TDBGR Area - Tx Debug Registers */
|
|
+#define FH_MEM_TDBGR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x0A70)
|
|
+#define FH_MEM_TDBGR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0x0B20)
|
|
+/* channels 0 - 10 */
|
|
+#define FH_MEM_TDBGR_CHNL(x) (FH_MEM_TDBGR_LOWER_BOUND + (x) * 0x10)
|
|
+
|
|
+#define FH_MEM_TDBGR_CHNL_REG_0(x) (FH_MEM_TDBGR_CHNL(x))
|
|
+#define FH_MEM_TDBGR_CHNL_REG_1(x) (FH_MEM_TDBGR_CHNL_REG_0(x) + 0x4)
|
|
+
|
|
+#define FH_MEM_TDBGR_CHNL_BYTES_TO_FIFO_MASK (0x000FFFFF)
|
|
+#define FH_MEM_TDBGR_CHNL_BYTES_TO_FIFO_BITSHIFT (0)
|
|
+
|
|
+/* RDBUF Area */
|
|
+#define FH_MEM_RDBUF_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xB80)
|
|
+#define FH_MEM_RDBUF_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xBC0)
|
|
+#define FH_MEM_RDBUF_CHNL0 (FH_MEM_RDBUF_LOWER_BOUND)
|
|
+
|
|
+/* RSCSR Area */
|
|
+#define FH_MEM_RSCSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xBC0)
|
|
+#define FH_MEM_RSCSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xC00)
|
|
+#define FH_MEM_RSCSR_CHNL0 (FH_MEM_RSCSR_LOWER_BOUND)
|
|
+#define FH_MEM_RSCSR_CHNL1 (FH_MEM_RSCSR_LOWER_BOUND + 0x020)
|
|
+
|
|
+/* RSCSR registers used in Normal mode*/
|
|
+#define FH_RSCSR_CHNL0_STTS_WPTR_REG (FH_MEM_RSCSR_CHNL0)
|
|
+#define FH_RSCSR_CHNL0_RBDCB_BASE_REG (FH_MEM_RSCSR_CHNL0 + 0x004)
|
|
+#define FH_RSCSR_CHNL0_RBDCB_WPTR_REG (FH_MEM_RSCSR_CHNL0 + 0x008)
|
|
+#define FH_RSCSR_CHNL0_RBDCB_RPTR_REG (FH_MEM_RSCSR_CHNL0 + 0x00c)
|
|
+
|
|
+#define FH_RSCSR_FRAME_SIZE_MASK (0x00003FFF) /* bits 0-13 */
|
|
+/* RSCSR registers used in Service mode*/
|
|
+#define FH_RSCSR_CHNL1_RB_WPTR_REG (FH_MEM_RSCSR_CHNL1)
|
|
+#define FH_RSCSR_CHNL1_RB_WPTR_OFFSET_REG (FH_MEM_RSCSR_CHNL1 + 0x004)
|
|
+#define FH_RSCSR_CHNL1_RB_CHUNK_NUM_REG (FH_MEM_RSCSR_CHNL1 + 0x008)
|
|
+#define FH_RSCSR_CHNL1_SRAM_ADDR_REG (FH_MEM_RSCSR_CHNL1 + 0x00C)
|
|
+
|
|
+/* RCSR Area - Registers address map */
|
|
+#define FH_MEM_RCSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xC00)
|
|
+#define FH_MEM_RCSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xCC0)
|
|
+#define FH_MEM_RCSR_CHNL0 (FH_MEM_RCSR_LOWER_BOUND)
|
|
+#define FH_MEM_RCSR_CHNL1 (FH_MEM_RCSR_LOWER_BOUND + 0x020)
|
|
+
|
|
+#define FH_MEM_RCSR_CHNL0_CONFIG_REG (FH_MEM_RCSR_CHNL0)
|
|
+#define FH_MEM_RCSR_CHNL0_CREDIT_REG (FH_MEM_RCSR_CHNL0 + 0x004)
|
|
+#define FH_MEM_RCSR_CHNL0_RBD_STTS_REG (FH_MEM_RCSR_CHNL0 + 0x008)
|
|
+#define FH_MEM_RCSR_CHNL0_RB_STTS_REG (FH_MEM_RCSR_CHNL0 + 0x00C)
|
|
+#define FH_MEM_RCSR_CHNL0_RXPD_STTS_REG (FH_MEM_RCSR_CHNL0 + 0x010)
|
|
+
|
|
+#define FH_MEM_RCSR_CHNL0_RBD_STTS_FRAME_RB_CNT_MASK (0x7FFFFFF0) /* bits4:30 */
|
|
+
|
|
+/* RCSR registers used in Service mode*/
|
|
+#define FH_MEM_RCSR_CHNL1_CONFIG_REG (FH_MEM_RCSR_CHNL1)
|
|
+#define FH_MEM_RCSR_CHNL1_RB_STTS_REG (FH_MEM_RCSR_CHNL1 + 0x00C)
|
|
+#define FH_MEM_RCSR_CHNL1_RX_PD_STTS_REG (FH_MEM_RCSR_CHNL1 + 0x010)
|
|
+
|
|
+/* RSSR Area - Rx shared ctrl & status registers */
|
|
+#define FH_MEM_RSSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xC40)
|
|
+#define FH_MEM_RSSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xD00)
|
|
+#define FH_MEM_RSSR_SHARED_CTRL_REG (FH_MEM_RSSR_LOWER_BOUND)
|
|
+#define FH_MEM_RSSR_RX_STATUS_REG (FH_MEM_RSSR_LOWER_BOUND + 0x004)
|
|
+#define FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV (FH_MEM_RSSR_LOWER_BOUND + 0x008)
|
|
+
|
|
+/* TCSR */
|
|
+#define IWL_FH_TCSR_LOWER_BOUND (IWL_FH_REGS_LOWER_BOUND + 0xD00)
|
|
+#define IWL_FH_TCSR_UPPER_BOUND (IWL_FH_REGS_LOWER_BOUND + 0xE60)
|
|
+
|
|
+#define IWL_FH_TCSR_CHNL_NUM (7)
|
|
+#define IWL_FH_TCSR_CHNL_TX_CONFIG_REG(_chnl) \
|
|
+ (IWL_FH_TCSR_LOWER_BOUND + 0x20 * _chnl)
|
|
+#define IWL_FH_TCSR_CHNL_TX_CREDIT_REG(_chnl) \
|
|
+ (IWL_FH_TCSR_LOWER_BOUND + 0x20 * _chnl + 0x4)
|
|
+#define IWL_FH_TCSR_CHNL_TX_BUF_STS_REG(_chnl) \
|
|
+ (IWL_FH_TCSR_LOWER_BOUND + 0x20 * _chnl + 0x8)
|
|
+
|
|
+/* TSSR Area - Tx shared status registers */
|
|
+/* TSSR */
|
|
+#define IWL_FH_TSSR_LOWER_BOUND (IWL_FH_REGS_LOWER_BOUND + 0xEA0)
|
|
+#define IWL_FH_TSSR_UPPER_BOUND (IWL_FH_REGS_LOWER_BOUND + 0xEC0)
|
|
+
|
|
+#define IWL_FH_TSSR_TX_MSG_CONFIG_REG (IWL_FH_TSSR_LOWER_BOUND + 0x008)
|
|
+#define IWL_FH_TSSR_TX_STATUS_REG (IWL_FH_TSSR_LOWER_BOUND + 0x010)
|
|
+
|
|
+#define IWL_FH_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TXPD_ON (0xFF000000)
|
|
+#define IWL_FH_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_TXPD_ON (0x00FF0000)
|
|
+
|
|
+#define IWL_FH_TSSR_TX_MSG_CONFIG_REG_VAL_MAX_FRAG_SIZE_64B (0x00000000)
|
|
+#define IWL_FH_TSSR_TX_MSG_CONFIG_REG_VAL_MAX_FRAG_SIZE_128B (0x00000400)
|
|
+#define IWL_FH_TSSR_TX_MSG_CONFIG_REG_VAL_MAX_FRAG_SIZE_256B (0x00000800)
|
|
+#define IWL_FH_TSSR_TX_MSG_CONFIG_REG_VAL_MAX_FRAG_SIZE_512B (0x00000C00)
|
|
+
|
|
+#define IWL_FH_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TFD_ON (0x00000100)
|
|
+#define IWL_FH_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_CBB_ON (0x00000080)
|
|
+
|
|
+#define IWL_FH_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RSP_WAIT_TH (0x00000020)
|
|
+#define IWL_FH_TSSR_TX_MSG_CONFIG_REG_VAL_RSP_WAIT_TH (0x00000005)
|
|
+
|
|
+#define IWL_FH_TSSR_TX_STATUS_REG_BIT_BUFS_EMPTY(_chnl) \
|
|
+ ((1 << (_chnl)) << 24)
|
|
+#define IWL_FH_TSSR_TX_STATUS_REG_BIT_NO_PEND_REQ(_chnl) \
|
|
+ ((1 << (_chnl)) << 16)
|
|
+
|
|
+#define IWL_FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(_chnl) \
|
|
+ (IWL_FH_TSSR_TX_STATUS_REG_BIT_BUFS_EMPTY(_chnl) | \
|
|
+ IWL_FH_TSSR_TX_STATUS_REG_BIT_NO_PEND_REQ(_chnl))
|
|
+
|
|
+/* SRVC */
|
|
+#define IWL_FH_SRVC_LOWER_BOUND (IWL_FH_REGS_LOWER_BOUND + 0x9C8)
|
|
+#define IWL_FH_SRVC_UPPER_BOUND (IWL_FH_REGS_LOWER_BOUND + 0x9D0)
|
|
+
|
|
+#define IWL_FH_SRVC_CHNL_SRAM_ADDR_REG(_chnl) \
|
|
+ (IWL_FH_SRVC_LOWER_BOUND + (_chnl - 9) * 0x4)
|
|
+
|
|
+/* TFDIB */
|
|
+#define IWL_FH_TFDIB_LOWER_BOUND (IWL_FH_REGS_LOWER_BOUND + 0x900)
|
|
+#define IWL_FH_TFDIB_UPPER_BOUND (IWL_FH_REGS_LOWER_BOUND + 0x958)
|
|
+
|
|
+#define IWL_FH_TFDIB_CTRL0_REG(_chnl) \
|
|
+ (IWL_FH_TFDIB_LOWER_BOUND + 0x8 * _chnl)
|
|
+#define IWL_FH_TFDIB_CTRL1_REG(_chnl) \
|
|
+ (IWL_FH_TFDIB_LOWER_BOUND + 0x8 * _chnl + 0x4)
|
|
+
|
|
+#define IWL_FH_SRVC_CHNL (9)
|
|
+#define IWL_FH_TFDIB_CTRL1_REG_POS_MSB (28)
|
|
+
|
|
+/* Debug Monitor Area */
|
|
+#define FH_MEM_DM_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xEE0)
|
|
+#define FH_MEM_DM_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xEF0)
|
|
+#define FH_MEM_DM_CONTROL_MASK_REG (FH_MEM_DM_LOWER_BOUND)
|
|
+#define FH_MEM_DM_CONTROL_START_REG (FH_MEM_DM_LOWER_BOUND + 0x004)
|
|
+#define FH_MEM_DM_CONTROL_STATUS_REG (FH_MEM_DM_LOWER_BOUND + 0x008)
|
|
+#define FH_MEM_DM_MONITOR_REG (FH_MEM_DM_LOWER_BOUND + 0x00C)
|
|
+
|
|
+#define FH_TB1_ADDR_LOW_MASK (0xFFFFFFFF) /* bits 31:0 */
|
|
+#define FH_TB1_ADDR_HIGH_MASK (0xF00000000) /* bits 35:32 */
|
|
+#define FH_TB2_ADDR_LOW_MASK (0x0000FFFF) /* bits 15:0 */
|
|
+#define FH_TB2_ADDR_HIGH_MASK (0xFFFFF0000) /* bits 35:16 */
|
|
+
|
|
+#define FH_TB1_ADDR_LOW_BITSHIFT (0)
|
|
+#define FH_TB1_ADDR_HIGH_BITSHIFT (32)
|
|
+#define FH_TB2_ADDR_LOW_BITSHIFT (0)
|
|
+#define FH_TB2_ADDR_HIGH_BITSHIFT (16)
|
|
+
|
|
+#define FH_TB1_LENGTH_MASK (0x00000FFF) /* bits 11:0 */
|
|
+#define FH_TB2_LENGTH_MASK (0x00000FFF) /* bits 11:0 */
|
|
+
|
|
+/* number of FH channels including 2 service mode */
|
|
+#define NUM_OF_FH_CHANNELS (10)
|
|
+
|
|
+/* ctrl field bitology */
|
|
+#define FH_TFD_CTRL_PADDING_MASK (0xC0000000) /* bits 31:30 */
|
|
+#define FH_TFD_CTRL_NUMTB_MASK (0x1F000000) /* bits 28:24 */
|
|
+
|
|
+#define FH_TFD_CTRL_PADDING_BITSHIFT (30)
|
|
+#define FH_TFD_CTRL_NUMTB_BITSHIFT (24)
|
|
+
|
|
+#define FH_TFD_GET_NUM_TBS(ctrl) \
|
|
+ ((ctrl & FH_TFD_CTRL_NUMTB_MASK) >> FH_TFD_CTRL_NUMTB_BITSHIFT)
|
|
+#define FH_TFD_GET_PADDING(ctrl) \
|
|
+ ((ctrl & FH_TFD_CTRL_PADDING_MASK) >> FH_TFD_CTRL_PADDING_BITSHIFT)
|
|
+
|
|
+/* TCSR: tx_config register values */
|
|
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_TXF (0x00000000)
|
|
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_DRIVER (0x00000001)
|
|
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_ARC (0x00000002)
|
|
+
|
|
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE_VAL (0x00000000)
|
|
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL (0x00000008)
|
|
+
|
|
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_NOINT (0x00000000)
|
|
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD (0x00100000)
|
|
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD (0x00200000)
|
|
+
|
|
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_NOINT (0x00000000)
|
|
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_ENDTFD (0x00400000)
|
|
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_IFTFD (0x00800000)
|
|
+
|
|
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE (0x00000000)
|
|
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE_EOF (0x40000000)
|
|
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE (0x80000000)
|
|
+
|
|
+#define IWL_FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_EMPTY (0x00000000)
|
|
+#define IWL_FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_WAIT (0x00002000)
|
|
+#define IWL_FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID (0x00000003)
|
|
+
|
|
+#define IWL_FH_TCSR_CHNL_TX_BUF_STS_REG_BIT_TFDB_WPTR (0x00000001)
|
|
+
|
|
+#define IWL_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM (20)
|
|
+#define IWL_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX (12)
|
|
+
|
|
+/* CBB table */
|
|
+#define FH_CBB_ADDR_MASK 0x0FFFFFFF /* bits 27:0 */
|
|
+#define FH_CBB_ADDR_BIT_SHIFT (8)
|
|
+
|
|
+/* RCSR: channel 0 rx_config register defines */
|
|
+#define FH_RCSR_CHNL0_RX_CONFIG_DMA_CHNL_EN_MASK (0xC0000000) /* bits 30-31 */
|
|
+#define FH_RCSR_CHNL0_RX_CONFIG_RBDBC_SIZE_MASK (0x00F00000) /* bits 20-23 */
|
|
+#define FH_RCSR_CHNL0_RX_CONFIG_RB_SIZE_MASK (0x00030000) /* bits 16-17 */
|
|
+#define FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MASK (0x00008000) /* bit 15 */
|
|
+#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_MASK (0x00001000) /* bit 12 */
|
|
+#define FH_RCSR_CHNL0_RX_CONFIG_RB_TIMEOUT_MASK (0x00000FF0) /* bit 4-11 */
|
|
+
|
|
+#define FH_RCSR_RX_CONFIG_RBDCB_SIZE_BITSHIFT (20)
|
|
+#define FH_RCSR_RX_CONFIG_RB_SIZE_BITSHIFT (16)
|
|
+
|
|
+#define FH_RCSR_GET_RDBC_SIZE(reg) \
|
|
+ ((reg & FH_RCSR_RX_CONFIG_RDBC_SIZE_MASK) >> \
|
|
+ FH_RCSR_RX_CONFIG_RDBC_SIZE_BITSHIFT)
|
|
+
|
|
+/* RCSR: channel 1 rx_config register defines */
|
|
+#define FH_RCSR_CHNL1_RX_CONFIG_DMA_CHNL_EN_MASK (0xC0000000) /* bits 30-31 */
|
|
+#define FH_RCSR_CHNL1_RX_CONFIG_IRQ_DEST_MASK (0x00003000) /* bits 12-13 */
|
|
+
|
|
+/* RCSR: rx_config register values */
|
|
+#define FH_RCSR_RX_CONFIG_CHNL_EN_PAUSE_VAL (0x00000000)
|
|
+#define FH_RCSR_RX_CONFIG_CHNL_EN_PAUSE_EOF_VAL (0x40000000)
|
|
+#define FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL (0x80000000)
|
|
+#define FH_RCSR_RX_CONFIG_SINGLE_FRAME_MODE (0x00008000)
|
|
+
|
|
+#define FH_RCSR_RX_CONFIG_RDRBD_DISABLE_VAL (0x00000000)
|
|
+#define FH_RCSR_RX_CONFIG_RDRBD_ENABLE_VAL (0x20000000)
|
|
+
|
|
+#define IWL_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K (0x00000000)
|
|
+
|
|
+/* RCSR channel 0 config register values */
|
|
+#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_NO_INT_VAL (0x00000000)
|
|
+#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL (0x00001000)
|
|
+
|
|
+/* RCSR channel 1 config register values */
|
|
+#define FH_RCSR_CHNL1_RX_CONFIG_IRQ_DEST_NO_INT_VAL (0x00000000)
|
|
+#define FH_RCSR_CHNL1_RX_CONFIG_IRQ_DEST_INT_HOST_VAL (0x00001000)
|
|
+#define FH_RCSR_CHNL1_RX_CONFIG_IRQ_DEST_INT_RTC_VAL (0x00002000)
|
|
+#define FH_RCSR_CHNL1_RX_CONFIG_IRQ_DEST_INT_HOST_RTC_VAL (0x00003000)
|
|
+
|
|
+/* RCSR: rb status register defines */
|
|
+#define FH_RCSR_RB_BYTE_TO_SEND_MASK (0x0001FFFF) /* bits 0-16 */
|
|
+
|
|
+/* RSCSR: defs used in normal mode */
|
|
+#define FH_RSCSR_CHNL0_RBDCB_WPTR_MASK (0x00000FFF) /* bits 0-11 */
|
|
+
|
|
+/* RSCSR: defs used in service mode */
|
|
+#define FH_RSCSR_CHNL1_SRAM_ADDR_MASK (0x00FFFFFF) /* bits 0-23 */
|
|
+#define FH_RSCSR_CHNL1_RB_WPTR_MASK (0x0FFFFFFF) /* bits 0-27 */
|
|
+#define FH_RSCSR_CHNL1_RB_WPTR_OFFSET_MASK (0x000000FF) /* bits 0-7 */
|
|
+
|
|
+/* RSSR: RX Enable Error IRQ to Driver register defines */
|
|
+#define FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV_NO_RBD (0x00400000) /* bit 22 */
|
|
+
|
|
+#define FH_DRAM2SRAM_DRAM_ADDR_HIGH_MASK (0xFFFFFFF00) /* bits 8-35 */
|
|
+#define FH_DRAM2SRAM_DRAM_ADDR_LOW_MASK (0x000000FF) /* bits 0-7 */
|
|
+
|
|
+#define FH_DRAM2SRAM_DRAM_ADDR_HIGH_BITSHIFT (8) /* bits 8-35 */
|
|
+
|
|
+/* RX DRAM status regs definitions */
|
|
+#define FH_RX_RB_NUM_MASK (0x00000FFF) /* bits 0-11 */
|
|
+#define FH_RX_FRAME_NUM_MASK (0x0FFF0000) /* bits 16-27 */
|
|
+
|
|
+#define FH_RX_RB_NUM_BITSHIFT (0)
|
|
+#define FH_RX_FRAME_NUM_BITSHIFT (16)
|
|
+
|
|
+#define SCD_WIN_SIZE 64
|
|
+#define SCD_FRAME_LIMIT 10
|
|
+
|
|
+/* memory mapped registers */
|
|
+#define SCD_START_OFFSET 0xa02c00
|
|
+
|
|
+#define SCD_SRAM_BASE_ADDR (SCD_START_OFFSET + 0x0)
|
|
+#define SCD_EMPTY_BITS (SCD_START_OFFSET + 0x4)
|
|
+#define SCD_DRAM_BASE_ADDR (SCD_START_OFFSET + 0x10)
|
|
+#define SCD_AIT (SCD_START_OFFSET + 0x18)
|
|
+#define SCD_TXFACT (SCD_START_OFFSET + 0x1c)
|
|
+#define SCD_QUEUE_WRPTR(x) (SCD_START_OFFSET + 0x24 + (x) * 4)
|
|
+#define SCD_QUEUE_RDPTR(x) (SCD_START_OFFSET + 0x64 + (x) * 4)
|
|
+#define SCD_SETQUEUENUM (SCD_START_OFFSET + 0xa4)
|
|
+#define SCD_SET_TXSTAT_TXED (SCD_START_OFFSET + 0xa8)
|
|
+#define SCD_SET_TXSTAT_DONE (SCD_START_OFFSET + 0xac)
|
|
+#define SCD_SET_TXSTAT_NOT_SCHD (SCD_START_OFFSET + 0xb0)
|
|
+#define SCD_DECREASE_CREDIT (SCD_START_OFFSET + 0xb4)
|
|
+#define SCD_DECREASE_SCREDIT (SCD_START_OFFSET + 0xb8)
|
|
+#define SCD_LOAD_CREDIT (SCD_START_OFFSET + 0xbc)
|
|
+#define SCD_LOAD_SCREDIT (SCD_START_OFFSET + 0xc0)
|
|
+#define SCD_BAR (SCD_START_OFFSET + 0xc4)
|
|
+#define SCD_BAR_DW0 (SCD_START_OFFSET + 0xc8)
|
|
+#define SCD_BAR_DW1 (SCD_START_OFFSET + 0xcc)
|
|
+#define SCD_QUEUECHAIN_SEL (SCD_START_OFFSET + 0xd0)
|
|
+#define SCD_QUERY_REQ (SCD_START_OFFSET + 0xd8)
|
|
+#define SCD_QUERY_RES (SCD_START_OFFSET + 0xdc)
|
|
+#define SCD_PENDING_FRAMES (SCD_START_OFFSET + 0xe0)
|
|
+#define SCD_INTERRUPT_MASK (SCD_START_OFFSET + 0xe4)
|
|
+#define SCD_INTERRUPT_THRESHOLD (SCD_START_OFFSET + 0xe8)
|
|
+#define SCD_QUERY_MIN_FRAME_SIZE (SCD_START_OFFSET + 0x100)
|
|
+#define SCD_QUEUE_STATUS_BITS(x) (SCD_START_OFFSET + 0x104 + (x) * 4)
|
|
+
|
|
+/* SRAM structures */
|
|
+#define SCD_CONTEXT_DATA_OFFSET 0x380
|
|
+#define SCD_TX_STTS_BITMAP_OFFSET 0x400
|
|
+#define SCD_TRANSLATE_TBL_OFFSET 0x500
|
|
+#define SCD_CONTEXT_QUEUE_OFFSET(x) (SCD_CONTEXT_DATA_OFFSET + ((x) * 8))
|
|
+#define SCD_TRANSLATE_TBL_OFFSET_QUEUE(x) \
|
|
+ ((SCD_TRANSLATE_TBL_OFFSET + ((x) * 2)) & 0xfffffffc)
|
|
+
|
|
+#define SCD_TXFACT_REG_TXFIFO_MASK(lo, hi) \
|
|
+ ((1<<(hi))|((1<<(hi))-(1<<(lo))))
|
|
+
|
|
+
|
|
+#define SCD_MODE_REG_BIT_SEARCH_MODE (1<<0)
|
|
+#define SCD_MODE_REG_BIT_SBYP_MODE (1<<1)
|
|
+
|
|
+#define SCD_TXFIFO_POS_TID (0)
|
|
+#define SCD_TXFIFO_POS_RA (4)
|
|
+#define SCD_QUEUE_STTS_REG_POS_ACTIVE (0)
|
|
+#define SCD_QUEUE_STTS_REG_POS_TXF (1)
|
|
+#define SCD_QUEUE_STTS_REG_POS_WSL (5)
|
|
+#define SCD_QUEUE_STTS_REG_POS_SCD_ACK (8)
|
|
+#define SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN (10)
|
|
+#define SCD_QUEUE_STTS_REG_MSK (0x0007FC00)
|
|
+
|
|
+#define SCD_QUEUE_RA_TID_MAP_RATID_MSK (0x01FF)
|
|
+
|
|
+#define SCD_QUEUE_CTX_REG1_WIN_SIZE_POS (0)
|
|
+#define SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK (0x0000007F)
|
|
+#define SCD_QUEUE_CTX_REG1_CREDIT_POS (8)
|
|
+#define SCD_QUEUE_CTX_REG1_CREDIT_MSK (0x00FFFF00)
|
|
+#define SCD_QUEUE_CTX_REG1_SUPER_CREDIT_POS (24)
|
|
+#define SCD_QUEUE_CTX_REG1_SUPER_CREDIT_MSK (0xFF000000)
|
|
+#define SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS (16)
|
|
+#define SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK (0x007F0000)
|
|
+
|
|
+#define CSR_HW_IF_CONFIG_REG_BIT_KEDRON_R (0x00000010)
|
|
+#define CSR_HW_IF_CONFIG_REG_MSK_BOARD_VER (0x00000C00)
|
|
+#define CSR_HW_IF_CONFIG_REG_BIT_MAC_SI (0x00000100)
|
|
+#define CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI (0x00000200)
|
|
+
|
|
+
|
|
+ /*IWL4965-END */
|
|
+
|
|
+#define IWL4965_BROADCAST_ID (31)
|
|
+
|
|
+#define RX_RES_PHY_CNT 14
|
|
+
|
|
+#define STATISTICS_FLG_CLEAR (0x1)
|
|
+#define STATISTICS_FLG_DISABLE_NOTIFICATION (0x2)
|
|
+
|
|
+#define STATISTICS_REPLY_FLG_CLEAR __constant_cpu_to_le32(0x1)
|
|
+#define STATISTICS_REPLY_FLG_BAND_24G_MSK __constant_cpu_to_le32(0x2)
|
|
+#define STATISTICS_REPLY_FLG_TGJ_NARROW_BAND_MSK __constant_cpu_to_le32(0x4)
|
|
+#define STATISTICS_REPLY_FLG_FAT_MODE_MSK __constant_cpu_to_le32(0x8)
|
|
+#define RX_PHY_FLAGS_ANTENNAE_OFFSET (4)
|
|
+#define RX_PHY_FLAGS_ANTENNAE_MASK (0x70)
|
|
+
|
|
+
|
|
+
|
|
+struct iwl4965_rx_phy_res {
|
|
+ u8 non_cfg_phy_cnt; /* non configurable DSP phy data byte count */
|
|
+ u8 cfg_phy_cnt; /* configurable DSP phy data byte count */
|
|
+ u8 stat_id; /* configurable DSP phy data set ID */
|
|
+ u8 reserved1;
|
|
+ __le64 timestamp; /* TSF at on air rise */
|
|
+ __le32 beacon_time_stamp; /* beacon at on-air rise */
|
|
+ __le16 phy_flags; /* general phy flags: band, modulation, ... */
|
|
+ __le16 channel; /* channel number */
|
|
+ __le16 non_cfg_phy[RX_RES_PHY_CNT]; /* upto 14 phy entries */
|
|
+ __le32 reserved2;
|
|
+ __le32 rate_n_flags;
|
|
+ __le16 byte_count; /* frame's byte-count */
|
|
+ __le16 reserved3;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct iwl4965_rx_mpdu_res_start {
|
|
+ __le16 byte_count;
|
|
+ __le16 reserved;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+static inline u8 iwl_hw_get_rate(__le32 rate_n_flags)
|
|
+{
|
|
+ return le32_to_cpu(rate_n_flags) & 0xFF;
|
|
+}
|
|
+static inline u16 iwl_hw_get_rate_n_flags(__le32 rate_n_flags)
|
|
+{
|
|
+ return le32_to_cpu(rate_n_flags) & 0xFFFF;
|
|
+}
|
|
+static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u16 flags)
|
|
+{
|
|
+ return cpu_to_le32(flags|(u16)rate);
|
|
+}
|
|
+
|
|
+#define IWL_AGC_DB_MASK (0x3f80) /* MASK(7,13) */
|
|
+#define IWL_AGC_DB_POS (7)
|
|
+/* Fixed (non-configurable) rx data from phy */
|
|
+struct iwl4965_rx_non_cfg_phy {
|
|
+ __le16 ant_selection; /* ant A bit 4, ant B bit 5, ant C bit 6 */
|
|
+ __le16 agc_info; /* agc code 0:6, agc dB 7:13, reserved 14:15 */
|
|
+ u8 rssi_info[6]; /* we use even entries, 0/2/4 for A/B/C rssi */
|
|
+ u8 pad[0];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct iwl_tfd_frame_data {
|
|
+ __le32 tb1_addr;
|
|
+
|
|
+ __le32 val1;
|
|
+ /* __le32 ptb1_32_35:4; */
|
|
+#define IWL_tb1_addr_hi_POS 0
|
|
+#define IWL_tb1_addr_hi_LEN 4
|
|
+#define IWL_tb1_addr_hi_SYM val1
|
|
+ /* __le32 tb_len1:12; */
|
|
+#define IWL_tb1_len_POS 4
|
|
+#define IWL_tb1_len_LEN 12
|
|
+#define IWL_tb1_len_SYM val1
|
|
+ /* __le32 ptb2_0_15:16; */
|
|
+#define IWL_tb2_addr_lo16_POS 16
|
|
+#define IWL_tb2_addr_lo16_LEN 16
|
|
+#define IWL_tb2_addr_lo16_SYM val1
|
|
+
|
|
+ __le32 val2;
|
|
+ /* __le32 ptb2_16_35:20; */
|
|
+#define IWL_tb2_addr_hi20_POS 0
|
|
+#define IWL_tb2_addr_hi20_LEN 20
|
|
+#define IWL_tb2_addr_hi20_SYM val2
|
|
+ /* __le32 tb_len2:12; */
|
|
+#define IWL_tb2_len_POS 20
|
|
+#define IWL_tb2_len_LEN 12
|
|
+#define IWL_tb2_len_SYM val2
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct iwl_tfd_frame {
|
|
+ __le32 val0;
|
|
+ /* __le32 rsvd1:24; */
|
|
+ /* __le32 num_tbs:5; */
|
|
+#define IWL_num_tbs_POS 24
|
|
+#define IWL_num_tbs_LEN 5
|
|
+#define IWL_num_tbs_SYM val0
|
|
+ /* __le32 rsvd2:1; */
|
|
+ /* __le32 padding:2; */
|
|
+ struct iwl_tfd_frame_data pa[10];
|
|
+ __le32 reserved;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+#define IWL4965_MAX_WIN_SIZE 64
|
|
+#define IWL4965_QUEUE_SIZE 256
|
|
+#define IWL4965_NUM_FIFOS 7
|
|
+#define IWL_MAX_NUM_QUEUES 16
|
|
+
|
|
+struct iwl4965_queue_byte_cnt_entry {
|
|
+ __le16 val;
|
|
+ /* __le16 byte_cnt:12; */
|
|
+#define IWL_byte_cnt_POS 0
|
|
+#define IWL_byte_cnt_LEN 12
|
|
+#define IWL_byte_cnt_SYM val
|
|
+ /* __le16 rsvd:4; */
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct iwl4965_sched_queue_byte_cnt_tbl {
|
|
+ struct iwl4965_queue_byte_cnt_entry tfd_offset[IWL4965_QUEUE_SIZE +
|
|
+ IWL4965_MAX_WIN_SIZE];
|
|
+ u8 dont_care[1024 -
|
|
+ (IWL4965_QUEUE_SIZE + IWL4965_MAX_WIN_SIZE) *
|
|
+ sizeof(__le16)];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/* Base physical address of iwl_shared is provided to SCD_DRAM_BASE_ADDR
|
|
+ * and &iwl_shared.val0 is provided to FH_RSCSR_CHNL0_STTS_WPTR_REG */
|
|
+struct iwl_shared {
|
|
+ struct iwl4965_sched_queue_byte_cnt_tbl
|
|
+ queues_byte_cnt_tbls[IWL_MAX_NUM_QUEUES];
|
|
+ __le32 val0;
|
|
+
|
|
+ /* __le32 rb_closed_stts_rb_num:12; */
|
|
+#define IWL_rb_closed_stts_rb_num_POS 0
|
|
+#define IWL_rb_closed_stts_rb_num_LEN 12
|
|
+#define IWL_rb_closed_stts_rb_num_SYM val0
|
|
+ /* __le32 rsrv1:4; */
|
|
+ /* __le32 rb_closed_stts_rx_frame_num:12; */
|
|
+#define IWL_rb_closed_stts_rx_frame_num_POS 16
|
|
+#define IWL_rb_closed_stts_rx_frame_num_LEN 12
|
|
+#define IWL_rb_closed_stts_rx_frame_num_SYM val0
|
|
+ /* __le32 rsrv2:4; */
|
|
+
|
|
+ __le32 val1;
|
|
+ /* __le32 frame_finished_stts_rb_num:12; */
|
|
+#define IWL_frame_finished_stts_rb_num_POS 0
|
|
+#define IWL_frame_finished_stts_rb_num_LEN 12
|
|
+#define IWL_frame_finished_stts_rb_num_SYM val1
|
|
+ /* __le32 rsrv3:4; */
|
|
+ /* __le32 frame_finished_stts_rx_frame_num:12; */
|
|
+#define IWL_frame_finished_stts_rx_frame_num_POS 16
|
|
+#define IWL_frame_finished_stts_rx_frame_num_LEN 12
|
|
+#define IWL_frame_finished_stts_rx_frame_num_SYM val1
|
|
+ /* __le32 rsrv4:4; */
|
|
+
|
|
+ __le32 padding1; /* so that allocation will be aligned to 16B */
|
|
+ __le32 padding2;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+#endif /* __iwl_4965_hw_h__ */
|
|
diff --git a/drivers/net/wireless/iwl-4965-rs.c b/drivers/net/wireless/iwl-4965-rs.c
|
|
new file mode 100644
|
|
index 0000000..a97914f
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/iwl-4965-rs.c
|
|
@@ -0,0 +1,2141 @@
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * Copyright(c) 2005 - 2007 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.
|
|
+ *
|
|
+ * Contact Information:
|
|
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
|
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/skbuff.h>
|
|
+#include <linux/wireless.h>
|
|
+#include <net/mac80211.h>
|
|
+#include <net/ieee80211.h>
|
|
+
|
|
+#include <linux/netdevice.h>
|
|
+#include <linux/etherdevice.h>
|
|
+#include <linux/delay.h>
|
|
+
|
|
+#include <linux/workqueue.h>
|
|
+
|
|
+#include <net/mac80211.h>
|
|
+#include <linux/wireless.h>
|
|
+
|
|
+#include "../net/mac80211/ieee80211_rate.h"
|
|
+
|
|
+#include "iwlwifi.h"
|
|
+#include "iwl-4965-rs.h"
|
|
+#include "iwl-helpers.h"
|
|
+
|
|
+#define RS_NAME "iwl-4965-rs"
|
|
+
|
|
+#define NUM_TRY_BEFORE_ANTENNA_TOGGLE 1
|
|
+#define IWL_NUMBER_TRY 1
|
|
+#define IWL_HT_NUMBER_TRY 3
|
|
+
|
|
+#define IWL_RATE_MAX_WINDOW 62
|
|
+#define IWL_RATE_HIGH_TH 10880
|
|
+#define IWL_RATE_MIN_FAILURE_TH 6
|
|
+#define IWL_RATE_MIN_SUCCESS_TH 8
|
|
+#define IWL_RATE_DECREASE_TH 1920
|
|
+#define IWL_RATE_INCREASE_TH 8960
|
|
+#define IWL_RATE_SCALE_FLUSH_INTVL (2*HZ) /*2 seconds */
|
|
+
|
|
+static u8 rs_ht_to_legacy[] = {
|
|
+ IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX,
|
|
+ IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX,
|
|
+ IWL_RATE_6M_INDEX,
|
|
+ IWL_RATE_6M_INDEX, IWL_RATE_9M_INDEX,
|
|
+ IWL_RATE_12M_INDEX, IWL_RATE_18M_INDEX,
|
|
+ IWL_RATE_24M_INDEX, IWL_RATE_36M_INDEX,
|
|
+ IWL_RATE_48M_INDEX, IWL_RATE_54M_INDEX
|
|
+};
|
|
+
|
|
+struct iwl_rate {
|
|
+ u32 rate_n_flags;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct iwl_rate_scale_data {
|
|
+ u64 data;
|
|
+ s32 success_counter;
|
|
+ s32 success_ratio;
|
|
+ s32 counter;
|
|
+ s32 average_tpt;
|
|
+ unsigned long stamp;
|
|
+};
|
|
+
|
|
+struct iwl_scale_tbl_info {
|
|
+ enum iwl_table_type lq_type;
|
|
+ enum iwl_antenna_type antenna_type;
|
|
+ u8 is_SGI;
|
|
+ u8 is_fat;
|
|
+ u8 is_dup;
|
|
+ s32 *expected_tpt;
|
|
+ u8 action;
|
|
+ struct iwl_rate current_rate;
|
|
+ struct iwl_rate_scale_data win[IWL_RATE_COUNT];
|
|
+};
|
|
+
|
|
+struct iwl_rate_scale_priv {
|
|
+ u8 active_tbl;
|
|
+ u8 enable_counter;
|
|
+ u8 stay_in_tbl;
|
|
+ u8 search_better_tbl;
|
|
+ s32 last_tpt;
|
|
+ u32 table_count_limit;
|
|
+ u32 max_failure_limit;
|
|
+ u32 max_success_limit;
|
|
+ u32 table_count;
|
|
+ u32 total_failed;
|
|
+ u32 total_success;
|
|
+ u8 action_counter;
|
|
+ u32 flush_timer;
|
|
+ u8 antenna;
|
|
+ u8 valid_antenna;
|
|
+ u8 is_green;
|
|
+ u8 is_dup;
|
|
+ u8 phymode;
|
|
+ u8 ibss_sta_added;
|
|
+ u16 active_rate;
|
|
+ u16 active_siso_rate;
|
|
+ u16 active_mimo_rate;
|
|
+ u16 active_rate_basic;
|
|
+ struct iwl_link_quality_cmd lq;
|
|
+ struct iwl_scale_tbl_info lq_info[LQ_SIZE];
|
|
+};
|
|
+
|
|
+static void rs_rate_scale_perform(struct iwl_priv *priv,
|
|
+ struct net_device *dev,
|
|
+ struct ieee80211_hdr *hdr,
|
|
+ struct sta_info *sta);
|
|
+static int rs_fill_link_cmd(struct iwl_rate_scale_priv *lq_data,
|
|
+ struct iwl_rate *tx_mcs,
|
|
+ struct iwl_link_quality_cmd *tbl,
|
|
+ struct sta_info *sta);
|
|
+
|
|
+
|
|
+static s32 expected_tpt_A[IWL_RATE_COUNT] = {
|
|
+ 0, 0, 0, 0, 40, 57, 72, 98, 121, 154, 177, 186, 186
|
|
+};
|
|
+
|
|
+static s32 expected_tpt_G[IWL_RATE_COUNT] = {
|
|
+ 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 186
|
|
+};
|
|
+
|
|
+static s32 expected_tpt_siso20MHz[IWL_RATE_COUNT] = {
|
|
+ 0, 0, 0, 0, 42, 42, 76, 102, 124, 159, 183, 193, 202
|
|
+};
|
|
+
|
|
+static s32 expected_tpt_siso20MHzSGI[IWL_RATE_COUNT] = {
|
|
+ 0, 0, 0, 0, 46, 46, 82, 110, 132, 168, 192, 202, 211
|
|
+};
|
|
+
|
|
+static s32 expected_tpt_mimo20MHz[IWL_RATE_COUNT] = {
|
|
+ 0, 0, 0, 0, 74, 74, 123, 155, 179, 214, 236, 244, 251
|
|
+};
|
|
+
|
|
+static s32 expected_tpt_mimo20MHzSGI[IWL_RATE_COUNT] = {
|
|
+ 0, 0, 0, 0, 81, 81, 131, 164, 188, 222, 243, 251, 257
|
|
+};
|
|
+
|
|
+static s32 expected_tpt_siso40MHz[IWL_RATE_COUNT] = {
|
|
+ 0, 0, 0, 0, 77, 77, 127, 160, 184, 220, 242, 250, 257
|
|
+};
|
|
+
|
|
+static s32 expected_tpt_siso40MHzSGI[IWL_RATE_COUNT] = {
|
|
+ 0, 0, 0, 0, 83, 83, 135, 169, 193, 229, 250, 257, 264
|
|
+};
|
|
+
|
|
+static s32 expected_tpt_mimo40MHz[IWL_RATE_COUNT] = {
|
|
+ 0, 0, 0, 0, 123, 123, 182, 214, 235, 264, 279, 285, 289
|
|
+};
|
|
+
|
|
+static s32 expected_tpt_mimo40MHzSGI[IWL_RATE_COUNT] = {
|
|
+ 0, 0, 0, 0, 131, 131, 191, 222, 242, 270, 284, 289, 293
|
|
+};
|
|
+
|
|
+static int iwl_lq_sync_callback(struct iwl_priv *priv,
|
|
+ struct iwl_cmd *cmd, struct sk_buff *skb)
|
|
+{
|
|
+ /*We didn't cache the SKB; let the caller free it */
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static inline u8 iwl_rate_get_rate(u32 rate_n_flags)
|
|
+{
|
|
+ return (u8)(rate_n_flags & 0xFF);
|
|
+}
|
|
+
|
|
+static int rs_send_lq_cmd(struct iwl_priv *priv,
|
|
+ struct iwl_link_quality_cmd *lq, u8 flags)
|
|
+{
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+ int i;
|
|
+#endif
|
|
+ int rc = -1;
|
|
+
|
|
+ struct iwl_host_cmd cmd = {
|
|
+ .id = REPLY_TX_LINK_QUALITY_CMD,
|
|
+ .len = sizeof(struct iwl_link_quality_cmd),
|
|
+ .meta.flags = flags,
|
|
+ .data = lq,
|
|
+ };
|
|
+
|
|
+ if ((lq->sta_id == 0xFF) &&
|
|
+ (priv->iw_mode == IEEE80211_IF_TYPE_IBSS))
|
|
+ return rc;
|
|
+
|
|
+ if (lq->sta_id == 0xFF)
|
|
+ lq->sta_id = IWL_AP_ID;
|
|
+
|
|
+ IWL_DEBUG_RATE("lq station id 0x%x\n", lq->sta_id);
|
|
+ IWL_DEBUG_RATE("lq dta 0x%X 0x%X\n",
|
|
+ lq->general_params.single_stream_ant_msk,
|
|
+ lq->general_params.dual_stream_ant_msk);
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+ for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++)
|
|
+ IWL_DEBUG_RATE("lq index %d 0x%X\n",
|
|
+ i, lq->rs_table[i].rate_n_flags);
|
|
+#endif
|
|
+
|
|
+ if (flags & CMD_ASYNC)
|
|
+ cmd.meta.u.callback = iwl_lq_sync_callback;
|
|
+ if (iwl_is_associated(priv) && priv->lq_mngr.lq_ready)
|
|
+ rc = iwl_send_cmd(priv, &cmd);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int rs_rate_scale_clear_window(struct iwl_rate_scale_data *window)
|
|
+{
|
|
+ window->data = 0;
|
|
+ window->success_counter = 0;
|
|
+ window->success_ratio = IWL_INVALID_VALUE;
|
|
+ window->counter = 0;
|
|
+ window->average_tpt = IWL_INVALID_VALUE;
|
|
+ window->stamp = 0;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int rs_collect_tx_data(struct iwl_rate_scale_data *windows,
|
|
+ int scale_index, s32 tpt, u32 status)
|
|
+{
|
|
+ int rc = 0;
|
|
+ struct iwl_rate_scale_data *window = NULL;
|
|
+ u64 mask;
|
|
+ u8 win_size = IWL_RATE_MAX_WINDOW;
|
|
+ s32 fail_count;
|
|
+
|
|
+ if (scale_index < 0)
|
|
+ return -1;
|
|
+
|
|
+ if (scale_index >= IWL_RATE_COUNT)
|
|
+ return -1;
|
|
+
|
|
+ window = &(windows[scale_index]);
|
|
+
|
|
+ if (window->counter >= win_size) {
|
|
+
|
|
+ window->counter = win_size - 1;
|
|
+ mask = 1;
|
|
+ mask = (mask << (win_size - 1));
|
|
+ if ((window->data & mask)) {
|
|
+ window->data &= ~mask;
|
|
+ window->success_counter = window->success_counter - 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ window->counter = window->counter + 1;
|
|
+ mask = window->data;
|
|
+ window->data = (mask << 1);
|
|
+ if (status != 0) {
|
|
+ window->success_counter = window->success_counter + 1;
|
|
+ window->data |= 0x1;
|
|
+ }
|
|
+
|
|
+ if (window->counter > 0)
|
|
+ window->success_ratio = 128 * (100 * window->success_counter)
|
|
+ / window->counter;
|
|
+ else
|
|
+ window->success_ratio = IWL_INVALID_VALUE;
|
|
+
|
|
+ fail_count = window->counter - window->success_counter;
|
|
+
|
|
+ if ((fail_count >= IWL_RATE_MIN_FAILURE_TH) ||
|
|
+ (window->success_counter >= IWL_RATE_MIN_SUCCESS_TH))
|
|
+ window->average_tpt = (window->success_ratio * tpt + 64) / 128;
|
|
+ else
|
|
+ window->average_tpt = IWL_INVALID_VALUE;
|
|
+
|
|
+ window->stamp = jiffies;
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+int static rs_mcs_from_tbl(struct iwl_rate *mcs_rate,
|
|
+ struct iwl_scale_tbl_info *tbl,
|
|
+ int index, u8 use_green)
|
|
+{
|
|
+ int rc = 0;
|
|
+
|
|
+ if (is_legacy(tbl->lq_type)) {
|
|
+ mcs_rate->rate_n_flags = iwl_rates[index].plcp;
|
|
+ if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE)
|
|
+ mcs_rate->rate_n_flags |= RATE_MCS_CCK_MSK;
|
|
+
|
|
+ } else if (is_siso(tbl->lq_type)) {
|
|
+ if (index > IWL_LAST_OFDM_RATE)
|
|
+ index = IWL_LAST_OFDM_RATE;
|
|
+ mcs_rate->rate_n_flags = iwl_rates[index].plcp_siso |
|
|
+ RATE_MCS_HT_MSK;
|
|
+ } else {
|
|
+ if (index > IWL_LAST_OFDM_RATE)
|
|
+ index = IWL_LAST_OFDM_RATE;
|
|
+ mcs_rate->rate_n_flags = iwl_rates[index].plcp_mimo |
|
|
+ RATE_MCS_HT_MSK;
|
|
+ }
|
|
+
|
|
+ switch (tbl->antenna_type) {
|
|
+ case ANT_BOTH:
|
|
+ mcs_rate->rate_n_flags |= RATE_MCS_ANT_AB_MSK;
|
|
+ break;
|
|
+ case ANT_MAIN:
|
|
+ mcs_rate->rate_n_flags |= RATE_MCS_ANT_A_MSK;
|
|
+ break;
|
|
+ case ANT_AUX:
|
|
+ mcs_rate->rate_n_flags |= RATE_MCS_ANT_B_MSK;
|
|
+ break;
|
|
+ case ANT_NONE:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (is_legacy(tbl->lq_type))
|
|
+ return rc;
|
|
+
|
|
+ if (tbl->is_fat) {
|
|
+ if (tbl->is_dup)
|
|
+ mcs_rate->rate_n_flags |= RATE_MCS_DUP_MSK;
|
|
+ else
|
|
+ mcs_rate->rate_n_flags |= RATE_MCS_FAT_MSK;
|
|
+ }
|
|
+ if (tbl->is_SGI)
|
|
+ mcs_rate->rate_n_flags |= RATE_MCS_SGI_MSK;
|
|
+
|
|
+ if (use_green) {
|
|
+ mcs_rate->rate_n_flags |= RATE_MCS_GF_MSK;
|
|
+ if (is_siso(tbl->lq_type))
|
|
+ mcs_rate->rate_n_flags &= ~RATE_MCS_SGI_MSK;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int rs_get_tbl_info_from_mcs(const struct iwl_rate *mcs_rate,
|
|
+ int phymode, struct iwl_scale_tbl_info *tbl,
|
|
+ int *rate_idx)
|
|
+{
|
|
+ int index;
|
|
+ u32 ant_msk;
|
|
+
|
|
+ index = iwl_rate_index_from_plcp(mcs_rate->rate_n_flags);
|
|
+
|
|
+ if (index == IWL_RATE_INVALID) {
|
|
+ *rate_idx = -1;
|
|
+ return -1;
|
|
+ }
|
|
+ tbl->is_SGI = 0;
|
|
+ tbl->is_fat = 0;
|
|
+ tbl->is_dup = 0;
|
|
+ tbl->antenna_type = ANT_BOTH;
|
|
+
|
|
+ if (!(mcs_rate->rate_n_flags & RATE_MCS_HT_MSK)) {
|
|
+ ant_msk = (mcs_rate->rate_n_flags & RATE_MCS_ANT_AB_MSK);
|
|
+
|
|
+ if (ant_msk == RATE_MCS_ANT_AB_MSK)
|
|
+ tbl->lq_type = LQ_NONE;
|
|
+ else {
|
|
+
|
|
+ if (phymode == MODE_IEEE80211A)
|
|
+ tbl->lq_type = LQ_A;
|
|
+ else
|
|
+ tbl->lq_type = LQ_G;
|
|
+
|
|
+ if (mcs_rate->rate_n_flags & RATE_MCS_ANT_A_MSK)
|
|
+ tbl->antenna_type = ANT_MAIN;
|
|
+ else
|
|
+ tbl->antenna_type = ANT_AUX;
|
|
+ }
|
|
+ *rate_idx = index;
|
|
+
|
|
+ } else if (iwl_rate_get_rate(mcs_rate->rate_n_flags)
|
|
+ <= IWL_RATE_SISO_60M_PLCP) {
|
|
+ tbl->lq_type = LQ_SISO;
|
|
+
|
|
+ ant_msk = (mcs_rate->rate_n_flags & RATE_MCS_ANT_AB_MSK);
|
|
+ if (ant_msk == RATE_MCS_ANT_AB_MSK)
|
|
+ tbl->lq_type = LQ_NONE;
|
|
+ else {
|
|
+ if (mcs_rate->rate_n_flags & RATE_MCS_ANT_A_MSK)
|
|
+ tbl->antenna_type = ANT_MAIN;
|
|
+ else
|
|
+ tbl->antenna_type = ANT_AUX;
|
|
+ }
|
|
+ if (mcs_rate->rate_n_flags & RATE_MCS_SGI_MSK)
|
|
+ tbl->is_SGI = 1;
|
|
+
|
|
+ if ((mcs_rate->rate_n_flags & RATE_MCS_FAT_MSK) ||
|
|
+ (mcs_rate->rate_n_flags & RATE_MCS_DUP_MSK))
|
|
+ tbl->is_fat = 1;
|
|
+
|
|
+ if (mcs_rate->rate_n_flags & RATE_MCS_DUP_MSK)
|
|
+ tbl->is_dup = 1;
|
|
+
|
|
+ *rate_idx = index;
|
|
+ } else {
|
|
+ tbl->lq_type = LQ_MIMO;
|
|
+ if (mcs_rate->rate_n_flags & RATE_MCS_SGI_MSK)
|
|
+ tbl->is_SGI = 1;
|
|
+
|
|
+ if ((mcs_rate->rate_n_flags & RATE_MCS_FAT_MSK) ||
|
|
+ (mcs_rate->rate_n_flags & RATE_MCS_DUP_MSK))
|
|
+ tbl->is_fat = 1;
|
|
+
|
|
+ if (mcs_rate->rate_n_flags & RATE_MCS_DUP_MSK)
|
|
+ tbl->is_dup = 1;
|
|
+ *rate_idx = index;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline void rs_toggle_antenna(struct iwl_rate *new_rate,
|
|
+ struct iwl_scale_tbl_info *tbl)
|
|
+{
|
|
+ if (tbl->antenna_type == ANT_AUX) {
|
|
+ tbl->antenna_type = ANT_MAIN;
|
|
+ new_rate->rate_n_flags &= ~RATE_MCS_ANT_B_MSK;
|
|
+ new_rate->rate_n_flags |= RATE_MCS_ANT_A_MSK;
|
|
+ } else {
|
|
+ tbl->antenna_type = ANT_AUX;
|
|
+ new_rate->rate_n_flags &= ~RATE_MCS_ANT_A_MSK;
|
|
+ new_rate->rate_n_flags |= RATE_MCS_ANT_B_MSK;
|
|
+ }
|
|
+}
|
|
+
|
|
+static inline s8 rs_use_green(struct iwl_priv *priv)
|
|
+{
|
|
+ s8 rc = 0;
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+ if (!priv->is_ht_enabled || !priv->current_assoc_ht.is_ht)
|
|
+ return 0;
|
|
+
|
|
+ if ((priv->current_assoc_ht.is_green_field) &&
|
|
+ !(priv->current_assoc_ht.operating_mode & 0x4))
|
|
+ rc = 1;
|
|
+#endif /*CONFIG_IWLWIFI_HT */
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * rs_get_supported_rates - get the available rates
|
|
+ *
|
|
+ * if management frame or broadcast frame only return
|
|
+ * basic available rates.
|
|
+ *
|
|
+ */
|
|
+static void rs_get_supported_rates(struct iwl_rate_scale_priv *lq_data,
|
|
+ struct ieee80211_hdr *hdr,
|
|
+ enum iwl_table_type rate_type,
|
|
+ u16 *data_rate)
|
|
+{
|
|
+ if (is_legacy(rate_type))
|
|
+ *data_rate = lq_data->active_rate;
|
|
+ else {
|
|
+ if (is_siso(rate_type))
|
|
+ *data_rate = lq_data->active_siso_rate;
|
|
+ else
|
|
+ *data_rate = lq_data->active_mimo_rate;
|
|
+ }
|
|
+
|
|
+ if (hdr && is_multicast_ether_addr(hdr->addr1) &&
|
|
+ lq_data->active_rate_basic)
|
|
+ *data_rate = lq_data->active_rate_basic;
|
|
+}
|
|
+
|
|
+static u16 rs_get_adjacent_rate(u8 index, u16 rate_mask, int rate_type)
|
|
+{
|
|
+ u8 high = IWL_RATE_INVALID;
|
|
+ u8 low = IWL_RATE_INVALID;
|
|
+
|
|
+ /* 802.11A or ht walks to the next literal adjascent rate in
|
|
+ * the rate table */
|
|
+ if (is_a_band(rate_type) || !is_legacy(rate_type)) {
|
|
+ int i;
|
|
+ u32 mask;
|
|
+
|
|
+ /* Find the previous rate that is in the rate mask */
|
|
+ i = index - 1;
|
|
+ for (mask = (1 << i); i >= 0; i--, mask >>= 1) {
|
|
+ if (rate_mask & mask) {
|
|
+ low = i;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Find the next rate that is in the rate mask */
|
|
+ i = index + 1;
|
|
+ for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) {
|
|
+ if (rate_mask & mask) {
|
|
+ high = i;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return (high << 8) | low;
|
|
+ }
|
|
+
|
|
+ low = index;
|
|
+ while (low != IWL_RATE_INVALID) {
|
|
+ low = iwl_rates[low].prev_rs;
|
|
+ if (low == IWL_RATE_INVALID)
|
|
+ break;
|
|
+ if (rate_mask & (1 << low))
|
|
+ break;
|
|
+ IWL_DEBUG_RATE("Skipping masked lower rate: %d\n", low);
|
|
+ }
|
|
+
|
|
+ high = index;
|
|
+ while (high != IWL_RATE_INVALID) {
|
|
+ high = iwl_rates[high].next_rs;
|
|
+ if (high == IWL_RATE_INVALID)
|
|
+ break;
|
|
+ if (rate_mask & (1 << high))
|
|
+ break;
|
|
+ IWL_DEBUG_RATE("Skipping masked higher rate: %d\n", high);
|
|
+ }
|
|
+
|
|
+ return (high << 8) | low;
|
|
+}
|
|
+
|
|
+static int rs_get_lower_rate(struct iwl_rate_scale_priv *lq_data,
|
|
+ struct iwl_scale_tbl_info *tbl, u8 scale_index,
|
|
+ u8 ht_possible, struct iwl_rate *mcs_rate,
|
|
+ struct sta_info *sta)
|
|
+{
|
|
+ u8 is_green = lq_data->is_green;
|
|
+ s32 low;
|
|
+ u16 rate_mask;
|
|
+ u16 high_low;
|
|
+ u8 switch_to_legacy = 0;
|
|
+
|
|
+ /* check if we need to switch from HT to legacy rates.
|
|
+ * assumption is that mandatory rates (1Mbps or 6Mbps)
|
|
+ * are always supported (spec demand) */
|
|
+ if (!is_legacy(tbl->lq_type) && (!ht_possible || !scale_index)) {
|
|
+ switch_to_legacy = 1;
|
|
+ scale_index = rs_ht_to_legacy[scale_index];
|
|
+ if (lq_data->phymode == MODE_IEEE80211A)
|
|
+ tbl->lq_type = LQ_A;
|
|
+ else
|
|
+ tbl->lq_type = LQ_G;
|
|
+
|
|
+ if ((tbl->antenna_type == ANT_BOTH) ||
|
|
+ (tbl->antenna_type == ANT_NONE))
|
|
+ tbl->antenna_type = ANT_MAIN;
|
|
+
|
|
+ tbl->is_fat = 0;
|
|
+ tbl->is_SGI = 0;
|
|
+ }
|
|
+
|
|
+ rs_get_supported_rates(lq_data, NULL, tbl->lq_type, &rate_mask);
|
|
+
|
|
+ /* mask with station rate restriction */
|
|
+ if (is_legacy(tbl->lq_type)) {
|
|
+ if (lq_data->phymode == (u8) MODE_IEEE80211A)
|
|
+ rate_mask = (u16)(rate_mask &
|
|
+ (sta->supp_rates << IWL_FIRST_OFDM_RATE));
|
|
+ else
|
|
+ rate_mask = (u16)(rate_mask & sta->supp_rates);
|
|
+ }
|
|
+
|
|
+ /* if we did switched from HT to legacy check current rate */
|
|
+ if ((switch_to_legacy) &&
|
|
+ (rate_mask & (1 << scale_index))) {
|
|
+ rs_mcs_from_tbl(mcs_rate, tbl, scale_index, is_green);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ high_low = rs_get_adjacent_rate(scale_index, rate_mask, tbl->lq_type);
|
|
+ low = high_low & 0xff;
|
|
+
|
|
+ if (low != IWL_RATE_INVALID)
|
|
+ rs_mcs_from_tbl(mcs_rate, tbl, low, is_green);
|
|
+ else
|
|
+ rs_mcs_from_tbl(mcs_rate, tbl, scale_index, is_green);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void rs_tx_status(void *priv_rate,
|
|
+ struct net_device *dev,
|
|
+ struct sk_buff *skb,
|
|
+ struct ieee80211_tx_status *tx_resp)
|
|
+{
|
|
+ int status;
|
|
+ u8 retries;
|
|
+ int rs_index, index = 0;
|
|
+ struct iwl_rate_scale_priv *lq;
|
|
+ struct iwl_link_quality_cmd *table;
|
|
+ struct sta_info *sta;
|
|
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
|
+ struct iwl_priv *priv = (struct iwl_priv *)priv_rate;
|
|
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
+ struct iwl_rate_scale_data *window = NULL;
|
|
+ struct iwl_rate_scale_data *search_win = NULL;
|
|
+ struct iwl_rate tx_mcs;
|
|
+ struct iwl_scale_tbl_info tbl_type;
|
|
+ struct iwl_scale_tbl_info *curr_tbl, *search_tbl;
|
|
+ u8 active_index = 0;
|
|
+ u16 fc = le16_to_cpu(hdr->frame_control);
|
|
+ s32 tpt = 0;
|
|
+
|
|
+ IWL_DEBUG_RATE("getting frame ack response, update rate "
|
|
+ "scale window\n");
|
|
+
|
|
+ if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1))
|
|
+ return;
|
|
+
|
|
+ retries = tx_resp->retry_count;
|
|
+
|
|
+ if (retries > 15)
|
|
+ retries = 15;
|
|
+
|
|
+
|
|
+ sta = sta_info_get(local, hdr->addr1);
|
|
+
|
|
+ if (!sta || !sta->rate_ctrl_priv) {
|
|
+ if (sta)
|
|
+ sta_info_put(sta);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ lq = (struct iwl_rate_scale_priv *)sta->rate_ctrl_priv;
|
|
+
|
|
+ if (!priv->lq_mngr.lq_ready)
|
|
+ return;
|
|
+
|
|
+ if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) && !lq->ibss_sta_added)
|
|
+ return;
|
|
+
|
|
+ table = &lq->lq;
|
|
+ active_index = lq->active_tbl;
|
|
+
|
|
+ lq->antenna = (lq->valid_antenna & local->hw.conf.antenna_sel_tx);
|
|
+ if (!lq->antenna)
|
|
+ lq->antenna = lq->valid_antenna;
|
|
+
|
|
+ lq->antenna = lq->valid_antenna;
|
|
+ curr_tbl = &(lq->lq_info[active_index]);
|
|
+ search_tbl = &(lq->lq_info[(1 - active_index)]);
|
|
+ window = (struct iwl_rate_scale_data *)
|
|
+ &(curr_tbl->win[0]);
|
|
+ search_win = (struct iwl_rate_scale_data *)
|
|
+ &(search_tbl->win[0]);
|
|
+
|
|
+ tx_mcs.rate_n_flags = tx_resp->control.tx_rate;
|
|
+
|
|
+ rs_get_tbl_info_from_mcs(&tx_mcs, priv->phymode,
|
|
+ &tbl_type, &rs_index);
|
|
+ if ((rs_index < 0) || (rs_index >= IWL_RATE_COUNT)) {
|
|
+ IWL_DEBUG_RATE("bad rate index at: %d rate 0x%X\n",
|
|
+ rs_index, tx_mcs.rate_n_flags);
|
|
+ sta_info_put(sta);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (retries &&
|
|
+ (tx_mcs.rate_n_flags !=
|
|
+ le32_to_cpu(table->rs_table[0].rate_n_flags))) {
|
|
+ IWL_DEBUG_RATE("initial rate does not match 0x%x 0x%x\n",
|
|
+ tx_mcs.rate_n_flags,
|
|
+ le32_to_cpu(table->rs_table[0].rate_n_flags));
|
|
+ sta_info_put(sta);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ while (retries) {
|
|
+ tx_mcs.rate_n_flags =
|
|
+ le32_to_cpu(table->rs_table[index].rate_n_flags);
|
|
+ rs_get_tbl_info_from_mcs(&tx_mcs, priv->phymode,
|
|
+ &tbl_type, &rs_index);
|
|
+
|
|
+ if ((tbl_type.lq_type == search_tbl->lq_type) &&
|
|
+ (tbl_type.antenna_type == search_tbl->antenna_type) &&
|
|
+ (tbl_type.is_SGI == search_tbl->is_SGI)) {
|
|
+ if (search_tbl->expected_tpt)
|
|
+ tpt = search_tbl->expected_tpt[rs_index];
|
|
+ else
|
|
+ tpt = 0;
|
|
+ rs_collect_tx_data(search_win,
|
|
+ rs_index, tpt, 0);
|
|
+ } else if ((tbl_type.lq_type == curr_tbl->lq_type) &&
|
|
+ (tbl_type.antenna_type == curr_tbl->antenna_type) &&
|
|
+ (tbl_type.is_SGI == curr_tbl->is_SGI)) {
|
|
+ if (curr_tbl->expected_tpt)
|
|
+ tpt = curr_tbl->expected_tpt[rs_index];
|
|
+ else
|
|
+ tpt = 0;
|
|
+ rs_collect_tx_data(window, rs_index, tpt, 0);
|
|
+ }
|
|
+ if (lq->stay_in_tbl)
|
|
+ lq->total_failed++;
|
|
+ --retries;
|
|
+ index++;
|
|
+
|
|
+ }
|
|
+
|
|
+ if (!tx_resp->retry_count)
|
|
+ tx_mcs.rate_n_flags = tx_resp->control.tx_rate;
|
|
+ else
|
|
+ tx_mcs.rate_n_flags =
|
|
+ le32_to_cpu(table->rs_table[index].rate_n_flags);
|
|
+
|
|
+ rs_get_tbl_info_from_mcs(&tx_mcs, priv->phymode,
|
|
+ &tbl_type, &rs_index);
|
|
+
|
|
+ if (tx_resp->flags & IEEE80211_TX_STATUS_ACK)
|
|
+ status = 1;
|
|
+ else
|
|
+ status = 0;
|
|
+
|
|
+ if ((tbl_type.lq_type == search_tbl->lq_type) &&
|
|
+ (tbl_type.antenna_type == search_tbl->antenna_type) &&
|
|
+ (tbl_type.is_SGI == search_tbl->is_SGI)) {
|
|
+ if (search_tbl->expected_tpt)
|
|
+ tpt = search_tbl->expected_tpt[rs_index];
|
|
+ else
|
|
+ tpt = 0;
|
|
+ rs_collect_tx_data(search_win,
|
|
+ rs_index, tpt, status);
|
|
+ } else if ((tbl_type.lq_type == curr_tbl->lq_type) &&
|
|
+ (tbl_type.antenna_type == curr_tbl->antenna_type) &&
|
|
+ (tbl_type.is_SGI == curr_tbl->is_SGI)) {
|
|
+ if (curr_tbl->expected_tpt)
|
|
+ tpt = curr_tbl->expected_tpt[rs_index];
|
|
+ else
|
|
+ tpt = 0;
|
|
+ rs_collect_tx_data(window, rs_index, tpt, status);
|
|
+ }
|
|
+
|
|
+ if (lq->stay_in_tbl) {
|
|
+ if (status)
|
|
+ lq->total_success++;
|
|
+ else
|
|
+ lq->total_failed++;
|
|
+ }
|
|
+
|
|
+ rs_rate_scale_perform(priv, dev, hdr, sta);
|
|
+ sta_info_put(sta);
|
|
+ return;
|
|
+}
|
|
+
|
|
+static u8 rs_is_ant_connected(u8 valid_antenna,
|
|
+ enum iwl_antenna_type antenna_type)
|
|
+{
|
|
+ if (antenna_type == ANT_AUX)
|
|
+ return ((valid_antenna & 0x2) ? 1:0);
|
|
+ else if (antenna_type == ANT_MAIN)
|
|
+ return ((valid_antenna & 0x1) ? 1:0);
|
|
+ else if (antenna_type == ANT_BOTH) {
|
|
+ if ((valid_antenna & 0x3) == 0x3)
|
|
+ return 1;
|
|
+ else
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static u8 rs_is_other_ant_connected(u8 valid_antenna,
|
|
+ enum iwl_antenna_type antenna_type)
|
|
+{
|
|
+ if (antenna_type == ANT_AUX)
|
|
+ return (rs_is_ant_connected(valid_antenna, ANT_MAIN));
|
|
+ else
|
|
+ return (rs_is_ant_connected(valid_antenna, ANT_AUX));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#define IWL_LEGACY_SWITCH_ANTENNA 0
|
|
+#define IWL_LECACY_SWITCH_SISO 1
|
|
+#define IWL_LEGACY_SWITCH_MIMO 2
|
|
+
|
|
+#define IWL_RS_GOOD_RATIO 12800
|
|
+
|
|
+#define IWL_ACTION_LIMIT 3
|
|
+#define IWL_LEGACY_FAILURE_LIMIT 160
|
|
+#define IWL_LEGACY_SUCCESS_LIMIT 480
|
|
+#define IWL_LEGACY_TABLE_COUNT 160
|
|
+
|
|
+#define IWL_NONE_LEGACY_FAILURE_LIMIT 400
|
|
+#define IWL_NONE_LEGACY_SUCCESS_LIMIT 4500
|
|
+#define IWL_NONE_LEGACY_TABLE_COUNT 1500
|
|
+
|
|
+#define IWL_RATE_SCALE_SWITCH (10880)
|
|
+
|
|
+static void rs_set_stay_in_table(u8 is_legacy,
|
|
+ struct iwl_rate_scale_priv *lq_data)
|
|
+{
|
|
+ IWL_DEBUG_HT("we are staying in the same table\n");
|
|
+ lq_data->stay_in_tbl = 1;
|
|
+ if (is_legacy) {
|
|
+ lq_data->table_count_limit = IWL_LEGACY_TABLE_COUNT;
|
|
+ lq_data->max_failure_limit = IWL_LEGACY_FAILURE_LIMIT;
|
|
+ lq_data->max_success_limit = IWL_LEGACY_TABLE_COUNT;
|
|
+ } else {
|
|
+ lq_data->table_count_limit = IWL_NONE_LEGACY_TABLE_COUNT;
|
|
+ lq_data->max_failure_limit = IWL_NONE_LEGACY_FAILURE_LIMIT;
|
|
+ lq_data->max_success_limit = IWL_NONE_LEGACY_SUCCESS_LIMIT;
|
|
+ }
|
|
+ lq_data->table_count = 0;
|
|
+ lq_data->total_failed = 0;
|
|
+ lq_data->total_success = 0;
|
|
+}
|
|
+
|
|
+static void rs_get_expected_tpt_table(struct iwl_rate_scale_priv *lq_data,
|
|
+ struct iwl_scale_tbl_info *tbl)
|
|
+{
|
|
+ if (is_legacy(tbl->lq_type)) {
|
|
+ if (!is_a_band(tbl->lq_type))
|
|
+ tbl->expected_tpt = expected_tpt_G;
|
|
+ else
|
|
+ tbl->expected_tpt = expected_tpt_A;
|
|
+ } else if (is_siso(tbl->lq_type)) {
|
|
+ if (tbl->is_fat && !lq_data->is_dup)
|
|
+ if (tbl->is_SGI)
|
|
+ tbl->expected_tpt = expected_tpt_siso40MHzSGI;
|
|
+ else
|
|
+ tbl->expected_tpt = expected_tpt_siso40MHz;
|
|
+ else if (tbl->is_SGI)
|
|
+ tbl->expected_tpt = expected_tpt_siso20MHzSGI;
|
|
+ else
|
|
+ tbl->expected_tpt = expected_tpt_siso20MHz;
|
|
+
|
|
+ } else if (is_mimo(tbl->lq_type)) {
|
|
+ if (tbl->is_fat && !lq_data->is_dup)
|
|
+ if (tbl->is_SGI)
|
|
+ tbl->expected_tpt = expected_tpt_mimo40MHzSGI;
|
|
+ else
|
|
+ tbl->expected_tpt = expected_tpt_mimo40MHz;
|
|
+ else if (tbl->is_SGI)
|
|
+ tbl->expected_tpt = expected_tpt_mimo20MHzSGI;
|
|
+ else
|
|
+ tbl->expected_tpt = expected_tpt_mimo20MHz;
|
|
+ } else
|
|
+ tbl->expected_tpt = expected_tpt_G;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+static s32 rs_get_best_rate(struct iwl_priv *priv,
|
|
+ struct iwl_rate_scale_priv *lq_data,
|
|
+ struct iwl_scale_tbl_info *tbl,
|
|
+ u16 rate_mask, s8 index, s8 rate)
|
|
+{
|
|
+ struct iwl_scale_tbl_info *active_tbl =
|
|
+ &(lq_data->lq_info[lq_data->active_tbl]);
|
|
+ s32 new_rate, high, low, start_hi;
|
|
+ s32 active_sr = active_tbl->win[index].success_ratio;
|
|
+ s32 *tpt_tbl = tbl->expected_tpt;
|
|
+ s32 active_tpt = active_tbl->expected_tpt[index];
|
|
+ u16 high_low;
|
|
+
|
|
+ new_rate = high = low = start_hi = IWL_RATE_INVALID;
|
|
+
|
|
+ for (; ;) {
|
|
+ high_low = rs_get_adjacent_rate(rate, rate_mask, tbl->lq_type);
|
|
+
|
|
+ low = high_low & 0xff;
|
|
+ high = (high_low >> 8) & 0xff;
|
|
+
|
|
+ if ((((100 * tpt_tbl[rate]) > lq_data->last_tpt) &&
|
|
+ ((active_sr > IWL_RATE_DECREASE_TH) &&
|
|
+ (active_sr <= IWL_RATE_HIGH_TH) &&
|
|
+ (tpt_tbl[rate] <= active_tpt))) ||
|
|
+ ((active_sr >= IWL_RATE_SCALE_SWITCH) &&
|
|
+ (tpt_tbl[rate] > active_tpt))) {
|
|
+
|
|
+ if (start_hi != IWL_RATE_INVALID) {
|
|
+ new_rate = start_hi;
|
|
+ break;
|
|
+ }
|
|
+ new_rate = rate;
|
|
+ if (low != IWL_RATE_INVALID)
|
|
+ rate = low;
|
|
+ else
|
|
+ break;
|
|
+ } else {
|
|
+ if (new_rate != IWL_RATE_INVALID)
|
|
+ break;
|
|
+ else if (high != IWL_RATE_INVALID) {
|
|
+ start_hi = high;
|
|
+ rate = high;
|
|
+ } else {
|
|
+ new_rate = rate;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return new_rate;
|
|
+}
|
|
+#endif /* CONFIG_IWLWIFI_HT */
|
|
+
|
|
+static inline u8 rs_is_both_ant_supp(u8 valid_antenna)
|
|
+{
|
|
+ return (rs_is_ant_connected(valid_antenna, ANT_BOTH));
|
|
+}
|
|
+
|
|
+static int rs_switch_to_mimo(struct iwl_priv *priv,
|
|
+ struct iwl_rate_scale_priv *lq_data,
|
|
+ struct iwl_scale_tbl_info *tbl, int index)
|
|
+{
|
|
+ int rc = -1;
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+ u16 rate_mask;
|
|
+ s32 rate;
|
|
+ s8 is_green = lq_data->is_green;
|
|
+
|
|
+ if (!priv->is_ht_enabled || !priv->current_assoc_ht.is_ht)
|
|
+ return -1;
|
|
+
|
|
+ IWL_DEBUG_HT("LQ: try to switch to MIMO\n");
|
|
+ tbl->lq_type = LQ_MIMO;
|
|
+ rs_get_supported_rates(lq_data, NULL, tbl->lq_type,
|
|
+ &rate_mask);
|
|
+
|
|
+ if (priv->current_assoc_ht.tx_mimo_ps_mode == IWL_MIMO_PS_STATIC)
|
|
+ return -1;
|
|
+
|
|
+ if (!rs_is_both_ant_supp(lq_data->antenna))
|
|
+ return -1;
|
|
+
|
|
+ rc = 0;
|
|
+ tbl->is_dup = lq_data->is_dup;
|
|
+ tbl->action = 0;
|
|
+ if (priv->current_channel_width == IWL_CHANNEL_WIDTH_40MHZ)
|
|
+ tbl->is_fat = 1;
|
|
+ else
|
|
+ tbl->is_fat = 0;
|
|
+
|
|
+ if (tbl->is_fat) {
|
|
+ if (priv->current_assoc_ht.sgf & HT_SHORT_GI_40MHZ_ONLY)
|
|
+ tbl->is_SGI = 1;
|
|
+ else
|
|
+ tbl->is_SGI = 0;
|
|
+ } else if (priv->current_assoc_ht.sgf & HT_SHORT_GI_20MHZ_ONLY)
|
|
+ tbl->is_SGI = 1;
|
|
+ else
|
|
+ tbl->is_SGI = 0;
|
|
+
|
|
+ rs_get_expected_tpt_table(lq_data, tbl);
|
|
+
|
|
+ rate = rs_get_best_rate(priv, lq_data, tbl, rate_mask, index, index);
|
|
+
|
|
+ IWL_DEBUG_HT("LQ: MIMO best rate %d mask %X\n", rate, rate_mask);
|
|
+ if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask))
|
|
+ return -1;
|
|
+ rs_mcs_from_tbl(&tbl->current_rate, tbl, rate, is_green);
|
|
+
|
|
+ IWL_DEBUG_HT("LQ: Switch to new mcs %X index is green %X\n",
|
|
+ tbl->current_rate.rate_n_flags, is_green);
|
|
+
|
|
+#endif /*CONFIG_IWLWIFI_HT */
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int rs_switch_to_siso(struct iwl_priv *priv,
|
|
+ struct iwl_rate_scale_priv *lq_data,
|
|
+ struct iwl_scale_tbl_info *tbl, int index)
|
|
+{
|
|
+ int rc = -1;
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+ u16 rate_mask;
|
|
+ u8 is_green = lq_data->is_green;
|
|
+ s32 rate;
|
|
+
|
|
+ IWL_DEBUG_HT("LQ: try to switch to SISO\n");
|
|
+ if (!priv->is_ht_enabled || !priv->current_assoc_ht.is_ht)
|
|
+ return -1;
|
|
+
|
|
+ rc = 0;
|
|
+ tbl->is_dup = lq_data->is_dup;
|
|
+ tbl->lq_type = LQ_SISO;
|
|
+ tbl->action = 0;
|
|
+ rs_get_supported_rates(lq_data, NULL, tbl->lq_type,
|
|
+ &rate_mask);
|
|
+
|
|
+ if (priv->current_channel_width == IWL_CHANNEL_WIDTH_40MHZ)
|
|
+ tbl->is_fat = 1;
|
|
+ else
|
|
+ tbl->is_fat = 0;
|
|
+
|
|
+ if (tbl->is_fat) {
|
|
+ if (priv->current_assoc_ht.sgf & HT_SHORT_GI_40MHZ_ONLY)
|
|
+ tbl->is_SGI = 1;
|
|
+ else
|
|
+ tbl->is_SGI = 0;
|
|
+ } else if (priv->current_assoc_ht.sgf & HT_SHORT_GI_20MHZ_ONLY)
|
|
+ tbl->is_SGI = 1;
|
|
+ else
|
|
+ tbl->is_SGI = 0;
|
|
+
|
|
+ if (is_green)
|
|
+ tbl->is_SGI = 0;
|
|
+
|
|
+ rs_get_expected_tpt_table(lq_data, tbl);
|
|
+ rate = rs_get_best_rate(priv, lq_data, tbl, rate_mask, index, index);
|
|
+
|
|
+ IWL_DEBUG_HT("LQ: get best rate %d mask %X\n", rate, rate_mask);
|
|
+ if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) {
|
|
+ IWL_DEBUG_HT("can not switch with index %d rate mask %x\n",
|
|
+ rate, rate_mask);
|
|
+ return -1;
|
|
+ }
|
|
+ rs_mcs_from_tbl(&tbl->current_rate, tbl, rate, is_green);
|
|
+ IWL_DEBUG_HT("LQ: Switch to new mcs %X index is green %X\n",
|
|
+ tbl->current_rate.rate_n_flags, is_green);
|
|
+
|
|
+#endif /*CONFIG_IWLWIFI_HT */
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int rs_move_legacy_other(struct iwl_priv *priv,
|
|
+ struct iwl_rate_scale_priv *lq_data,
|
|
+ int index)
|
|
+{
|
|
+ int rc = 0;
|
|
+ struct iwl_scale_tbl_info *tbl =
|
|
+ &(lq_data->lq_info[lq_data->active_tbl]);
|
|
+ struct iwl_scale_tbl_info *search_tbl =
|
|
+ &(lq_data->lq_info[(1 - lq_data->active_tbl)]);
|
|
+ struct iwl_rate_scale_data *window = &(tbl->win[index]);
|
|
+ u32 sz = (sizeof(struct iwl_scale_tbl_info) -
|
|
+ (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
|
|
+ u8 start_action = tbl->action;
|
|
+
|
|
+ for (; ;) {
|
|
+ switch (tbl->action) {
|
|
+ case IWL_LEGACY_SWITCH_ANTENNA:
|
|
+ IWL_DEBUG_HT("LQ Legacy switch Antenna\n");
|
|
+
|
|
+ search_tbl->lq_type = LQ_NONE;
|
|
+ lq_data->action_counter++;
|
|
+ if (window->success_ratio >= IWL_RS_GOOD_RATIO)
|
|
+ break;
|
|
+ if (!rs_is_other_ant_connected(lq_data->antenna,
|
|
+ tbl->antenna_type))
|
|
+ break;
|
|
+
|
|
+ memcpy(search_tbl, tbl, sz);
|
|
+
|
|
+ rs_toggle_antenna(&(search_tbl->current_rate),
|
|
+ search_tbl);
|
|
+ rs_get_expected_tpt_table(lq_data, search_tbl);
|
|
+ lq_data->search_better_tbl = 1;
|
|
+ goto out;
|
|
+
|
|
+ case IWL_LECACY_SWITCH_SISO:
|
|
+ IWL_DEBUG_HT("LQ: Legacy switch to SISO\n");
|
|
+ memcpy(search_tbl, tbl, sz);
|
|
+ search_tbl->lq_type = LQ_SISO;
|
|
+ search_tbl->is_SGI = 0;
|
|
+ search_tbl->is_fat = 0;
|
|
+ rc = rs_switch_to_siso(priv, lq_data, search_tbl,
|
|
+ index);
|
|
+ if (!rc) {
|
|
+ lq_data->search_better_tbl = 1;
|
|
+ lq_data->action_counter = 0;
|
|
+ }
|
|
+ if (!rc)
|
|
+ goto out;
|
|
+
|
|
+ break;
|
|
+ case IWL_LEGACY_SWITCH_MIMO:
|
|
+ IWL_DEBUG_HT("LQ: Legacy switch MIMO\n");
|
|
+ memcpy(search_tbl, tbl, sz);
|
|
+ search_tbl->lq_type = LQ_MIMO;
|
|
+ search_tbl->is_SGI = 0;
|
|
+ search_tbl->is_fat = 0;
|
|
+ search_tbl->antenna_type = ANT_BOTH;
|
|
+ rc = rs_switch_to_mimo(priv, lq_data, search_tbl,
|
|
+ index);
|
|
+ if (!rc) {
|
|
+ lq_data->search_better_tbl = 1;
|
|
+ lq_data->action_counter = 0;
|
|
+ }
|
|
+ if (!rc)
|
|
+ goto out;
|
|
+ break;
|
|
+ }
|
|
+ tbl->action++;
|
|
+ if (tbl->action > IWL_LEGACY_SWITCH_MIMO)
|
|
+ tbl->action = IWL_LEGACY_SWITCH_ANTENNA;
|
|
+
|
|
+ if (tbl->action == start_action)
|
|
+ break;
|
|
+
|
|
+ }
|
|
+ return 0;
|
|
+
|
|
+ out:
|
|
+ tbl->action++;
|
|
+ if (tbl->action > IWL_LEGACY_SWITCH_MIMO)
|
|
+ tbl->action = IWL_LEGACY_SWITCH_ANTENNA;
|
|
+ return 0;
|
|
+
|
|
+}
|
|
+
|
|
+#define IWL_SISO_SWITCH_ANTENNA 0
|
|
+#define IWL_SISO_SWITCH_MIMO 1
|
|
+#define IWL_SISO_SWITCH_GI 2
|
|
+
|
|
+static int rs_move_siso_to_other(struct iwl_priv *priv,
|
|
+ struct iwl_rate_scale_priv *lq_data,
|
|
+ int index)
|
|
+{
|
|
+ int rc = -1;
|
|
+ u8 is_green = lq_data->is_green;
|
|
+ struct iwl_scale_tbl_info *tbl =
|
|
+ &(lq_data->lq_info[lq_data->active_tbl]);
|
|
+ struct iwl_scale_tbl_info *search_tbl =
|
|
+ &(lq_data->lq_info[(1 - lq_data->active_tbl)]);
|
|
+ struct iwl_rate_scale_data *window = &(tbl->win[index]);
|
|
+ u32 sz = (sizeof(struct iwl_scale_tbl_info) -
|
|
+ (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
|
|
+ u8 start_action = tbl->action;
|
|
+
|
|
+ for (;;) {
|
|
+ lq_data->action_counter++;
|
|
+ switch (tbl->action) {
|
|
+ case IWL_SISO_SWITCH_ANTENNA:
|
|
+ IWL_DEBUG_HT("LQ: SISO SWITCH ANTENNA SISO\n");
|
|
+ search_tbl->lq_type = LQ_NONE;
|
|
+ if (window->success_ratio >= IWL_RS_GOOD_RATIO)
|
|
+ break;
|
|
+ if (!rs_is_other_ant_connected(lq_data->antenna,
|
|
+ tbl->antenna_type))
|
|
+ break;
|
|
+
|
|
+ memcpy(search_tbl, tbl, sz);
|
|
+ search_tbl->action = IWL_SISO_SWITCH_MIMO;
|
|
+ rs_toggle_antenna(&(search_tbl->current_rate),
|
|
+ search_tbl);
|
|
+ lq_data->search_better_tbl = 1;
|
|
+
|
|
+ goto out;
|
|
+
|
|
+ case IWL_SISO_SWITCH_MIMO:
|
|
+ IWL_DEBUG_HT("LQ: SISO SWITCH TO MIMO FROM SISO\n");
|
|
+ memcpy(search_tbl, tbl, sz);
|
|
+ search_tbl->lq_type = LQ_MIMO;
|
|
+ search_tbl->is_SGI = 0;
|
|
+ search_tbl->is_fat = 0;
|
|
+ search_tbl->antenna_type = ANT_BOTH;
|
|
+ rc = rs_switch_to_mimo(priv, lq_data, search_tbl,
|
|
+ index);
|
|
+ if (!rc)
|
|
+ lq_data->search_better_tbl = 1;
|
|
+
|
|
+ if (!rc)
|
|
+ goto out;
|
|
+ break;
|
|
+ case IWL_SISO_SWITCH_GI:
|
|
+ IWL_DEBUG_HT("LQ: SISO SWITCH TO GI\n");
|
|
+ memcpy(search_tbl, tbl, sz);
|
|
+ search_tbl->action = 0;
|
|
+ if (search_tbl->is_SGI)
|
|
+ search_tbl->is_SGI = 0;
|
|
+ else if (!is_green)
|
|
+ search_tbl->is_SGI = 1;
|
|
+ else
|
|
+ break;
|
|
+ lq_data->search_better_tbl = 1;
|
|
+ if ((tbl->lq_type == LQ_SISO) &&
|
|
+ (tbl->is_SGI)) {
|
|
+ s32 tpt = lq_data->last_tpt / 100;
|
|
+ if (((!tbl->is_fat) &&
|
|
+ (tpt >= expected_tpt_siso20MHz[index])) ||
|
|
+ ((tbl->is_fat) &&
|
|
+ (tpt >= expected_tpt_siso40MHz[index])))
|
|
+ lq_data->search_better_tbl = 0;
|
|
+ }
|
|
+ rs_get_expected_tpt_table(lq_data, search_tbl);
|
|
+ rs_mcs_from_tbl(&search_tbl->current_rate,
|
|
+ search_tbl, index, is_green);
|
|
+ goto out;
|
|
+ }
|
|
+ tbl->action++;
|
|
+ if (tbl->action > IWL_SISO_SWITCH_GI)
|
|
+ tbl->action = IWL_SISO_SWITCH_ANTENNA;
|
|
+
|
|
+ if (tbl->action == start_action)
|
|
+ break;
|
|
+ }
|
|
+ return 0;
|
|
+
|
|
+ out:
|
|
+ tbl->action++;
|
|
+ if (tbl->action > IWL_SISO_SWITCH_GI)
|
|
+ tbl->action = IWL_SISO_SWITCH_ANTENNA;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#define IWL_MIMO_SWITCH_ANTENNA_A 0
|
|
+#define IWL_MIMO_SWITCH_ANTENNA_B 1
|
|
+#define IWL_MIMO_SWITCH_GI 2
|
|
+
|
|
+static int rs_move_mimo_to_other(struct iwl_priv *priv,
|
|
+ struct iwl_rate_scale_priv *lq_data,
|
|
+ int index)
|
|
+{
|
|
+ int rc = -1;
|
|
+ s8 is_green = lq_data->is_green;
|
|
+ struct iwl_scale_tbl_info *tbl =
|
|
+ &(lq_data->lq_info[lq_data->active_tbl]);
|
|
+ struct iwl_scale_tbl_info *search_tbl =
|
|
+ &(lq_data->lq_info[(1 - lq_data->active_tbl)]);
|
|
+ u32 sz = (sizeof(struct iwl_scale_tbl_info) -
|
|
+ (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
|
|
+ u8 start_action = tbl->action;
|
|
+
|
|
+ for (;;) {
|
|
+ lq_data->action_counter++;
|
|
+ switch (tbl->action) {
|
|
+ case IWL_MIMO_SWITCH_ANTENNA_A:
|
|
+ case IWL_MIMO_SWITCH_ANTENNA_B:
|
|
+ IWL_DEBUG_HT("LQ: MIMO SWITCH TO SISO\n");
|
|
+ memcpy(search_tbl, tbl, sz);
|
|
+ search_tbl->lq_type = LQ_SISO;
|
|
+ search_tbl->is_SGI = 0;
|
|
+ search_tbl->is_fat = 0;
|
|
+ if (tbl->action == IWL_MIMO_SWITCH_ANTENNA_A)
|
|
+ search_tbl->antenna_type = ANT_MAIN;
|
|
+ else
|
|
+ search_tbl->antenna_type = ANT_AUX;
|
|
+
|
|
+ rc = rs_switch_to_siso(priv, lq_data, search_tbl,
|
|
+ index);
|
|
+ if (!rc) {
|
|
+ lq_data->search_better_tbl = 1;
|
|
+ goto out;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case IWL_MIMO_SWITCH_GI:
|
|
+ IWL_DEBUG_HT("LQ: MIMO SWITCH TO GI\n");
|
|
+ memcpy(search_tbl, tbl, sz);
|
|
+ search_tbl->lq_type = LQ_MIMO;
|
|
+ search_tbl->antenna_type = ANT_BOTH;
|
|
+ search_tbl->action = 0;
|
|
+ if (search_tbl->is_SGI)
|
|
+ search_tbl->is_SGI = 0;
|
|
+ else
|
|
+ search_tbl->is_SGI = 1;
|
|
+ lq_data->search_better_tbl = 1;
|
|
+ if ((tbl->lq_type == LQ_MIMO) &&
|
|
+ (tbl->is_SGI)) {
|
|
+ s32 tpt = lq_data->last_tpt / 100;
|
|
+ if (((!tbl->is_fat) &&
|
|
+ (tpt >= expected_tpt_mimo20MHz[index])) ||
|
|
+ ((tbl->is_fat) &&
|
|
+ (tpt >= expected_tpt_mimo40MHz[index])))
|
|
+ lq_data->search_better_tbl = 0;
|
|
+ }
|
|
+ rs_get_expected_tpt_table(lq_data, search_tbl);
|
|
+ rs_mcs_from_tbl(&search_tbl->current_rate,
|
|
+ search_tbl, index, is_green);
|
|
+ goto out;
|
|
+
|
|
+ }
|
|
+ tbl->action++;
|
|
+ if (tbl->action > IWL_MIMO_SWITCH_GI)
|
|
+ tbl->action = IWL_MIMO_SWITCH_ANTENNA_A;
|
|
+
|
|
+ if (tbl->action == start_action)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+ out:
|
|
+ tbl->action++;
|
|
+ if (tbl->action > IWL_MIMO_SWITCH_GI)
|
|
+ tbl->action = IWL_MIMO_SWITCH_ANTENNA_A;
|
|
+ return 0;
|
|
+
|
|
+}
|
|
+
|
|
+static void rs_stay_in_table(struct iwl_rate_scale_priv *lq_data)
|
|
+{
|
|
+ struct iwl_scale_tbl_info *tbl;
|
|
+ int i;
|
|
+ int active_tbl;
|
|
+ int flush_interval_passed = 0;
|
|
+
|
|
+ active_tbl = lq_data->active_tbl;
|
|
+
|
|
+ tbl = &(lq_data->lq_info[active_tbl]);
|
|
+
|
|
+ if (lq_data->stay_in_tbl) {
|
|
+
|
|
+ if (lq_data->flush_timer)
|
|
+ flush_interval_passed =
|
|
+ time_after(jiffies,
|
|
+ (unsigned long)(lq_data->flush_timer +
|
|
+ IWL_RATE_SCALE_FLUSH_INTVL));
|
|
+
|
|
+ flush_interval_passed = 0;
|
|
+ if ((lq_data->total_failed > lq_data->max_failure_limit) ||
|
|
+ (lq_data->total_success > lq_data->max_success_limit) ||
|
|
+ ((!lq_data->search_better_tbl) && (lq_data->flush_timer)
|
|
+ && (flush_interval_passed))) {
|
|
+ IWL_DEBUG_HT("LQ: stay is expired %d %d %d\n:",
|
|
+ lq_data->total_failed,
|
|
+ lq_data->total_success,
|
|
+ flush_interval_passed);
|
|
+ lq_data->stay_in_tbl = 0;
|
|
+ lq_data->total_failed = 0;
|
|
+ lq_data->total_success = 0;
|
|
+ lq_data->flush_timer = 0;
|
|
+ } else if (lq_data->table_count > 0) {
|
|
+ lq_data->table_count++;
|
|
+ if (lq_data->table_count >=
|
|
+ lq_data->table_count_limit) {
|
|
+ lq_data->table_count = 0;
|
|
+
|
|
+ IWL_DEBUG_HT("LQ: stay in table clear win\n");
|
|
+ for (i = 0; i < IWL_RATE_COUNT; i++)
|
|
+ rs_rate_scale_clear_window(
|
|
+ &(tbl->win[i]));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!lq_data->stay_in_tbl) {
|
|
+ for (i = 0; i < IWL_RATE_COUNT; i++)
|
|
+ rs_rate_scale_clear_window(&(tbl->win[i]));
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static void rs_rate_scale_perform(struct iwl_priv *priv,
|
|
+ struct net_device *dev,
|
|
+ struct ieee80211_hdr *hdr,
|
|
+ struct sta_info *sta)
|
|
+{
|
|
+ int low = IWL_RATE_INVALID;
|
|
+ int high = IWL_RATE_INVALID;
|
|
+ int index;
|
|
+ int i;
|
|
+ struct iwl_rate_scale_data *window = NULL;
|
|
+ int current_tpt = IWL_INVALID_VALUE;
|
|
+ int low_tpt = IWL_INVALID_VALUE;
|
|
+ int high_tpt = IWL_INVALID_VALUE;
|
|
+ u32 fail_count;
|
|
+ s8 scale_action = 0;
|
|
+ u16 fc, rate_mask;
|
|
+ u8 update_lq = 0;
|
|
+ struct iwl_rate_scale_priv *lq_data;
|
|
+ struct iwl_scale_tbl_info *tbl, *tbl1;
|
|
+ u16 rate_scale_index_msk = 0;
|
|
+ struct iwl_rate mcs_rate;
|
|
+ u8 is_green = 0;
|
|
+ u8 active_tbl = 0;
|
|
+ u8 done_search = 0;
|
|
+ u16 high_low;
|
|
+
|
|
+ IWL_DEBUG_RATE("rate scale calculate new rate for skb\n");
|
|
+
|
|
+ fc = le16_to_cpu(hdr->frame_control);
|
|
+ if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1)) {
|
|
+ /* Send management frames and broadcast/multicast data using
|
|
+ * lowest rate. */
|
|
+ /* TODO: this could probably be improved.. */
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!sta || !sta->rate_ctrl_priv)
|
|
+ return;
|
|
+
|
|
+ if (!priv->lq_mngr.lq_ready) {
|
|
+ IWL_DEBUG_RATE("still rate scaling not ready\n");
|
|
+ return;
|
|
+ }
|
|
+ lq_data = (struct iwl_rate_scale_priv *)sta->rate_ctrl_priv;
|
|
+
|
|
+ if (!lq_data->search_better_tbl)
|
|
+ active_tbl = lq_data->active_tbl;
|
|
+ else
|
|
+ active_tbl = 1 - lq_data->active_tbl;
|
|
+
|
|
+ tbl = &(lq_data->lq_info[active_tbl]);
|
|
+ is_green = lq_data->is_green;
|
|
+
|
|
+ index = sta->last_txrate;
|
|
+
|
|
+ IWL_DEBUG_RATE("Rate scale index %d for type %d\n", index,
|
|
+ tbl->lq_type);
|
|
+
|
|
+ rs_get_supported_rates(lq_data, hdr, tbl->lq_type,
|
|
+ &rate_mask);
|
|
+
|
|
+ IWL_DEBUG_RATE("mask 0x%04X \n", rate_mask);
|
|
+
|
|
+ /* mask with station rate restriction */
|
|
+ if (is_legacy(tbl->lq_type)) {
|
|
+ if (lq_data->phymode == (u8) MODE_IEEE80211A)
|
|
+ rate_scale_index_msk = (u16) (rate_mask &
|
|
+ (sta->supp_rates << IWL_FIRST_OFDM_RATE));
|
|
+ else
|
|
+ rate_scale_index_msk = (u16) (rate_mask &
|
|
+ sta->supp_rates);
|
|
+
|
|
+ } else
|
|
+ rate_scale_index_msk = rate_mask;
|
|
+
|
|
+ if (!rate_scale_index_msk)
|
|
+ rate_scale_index_msk = rate_mask;
|
|
+
|
|
+ if (index < 0 || !((1 << index) & rate_scale_index_msk)) {
|
|
+ index = IWL_INVALID_VALUE;
|
|
+ update_lq = 1;
|
|
+
|
|
+ /* get the lowest availabe rate */
|
|
+ for (i = 0; i <= IWL_RATE_COUNT; i++) {
|
|
+ if ((1 << i) & rate_scale_index_msk)
|
|
+ index = i;
|
|
+ }
|
|
+
|
|
+ if (index == IWL_INVALID_VALUE) {
|
|
+ IWL_WARNING("Can not find a suitable rate\n");
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!tbl->expected_tpt)
|
|
+ rs_get_expected_tpt_table(lq_data, tbl);
|
|
+
|
|
+ window = &(tbl->win[index]);
|
|
+
|
|
+ fail_count = window->counter - window->success_counter;
|
|
+ if (((fail_count < IWL_RATE_MIN_FAILURE_TH) &&
|
|
+ (window->success_counter < IWL_RATE_MIN_SUCCESS_TH))
|
|
+ || (tbl->expected_tpt == NULL)) {
|
|
+ IWL_DEBUG_RATE("LQ: still below TH succ %d total %d "
|
|
+ "for index %d\n",
|
|
+ window->success_counter, window->counter, index);
|
|
+ window->average_tpt = IWL_INVALID_VALUE;
|
|
+ rs_stay_in_table(lq_data);
|
|
+ if (update_lq) {
|
|
+ rs_mcs_from_tbl(&mcs_rate, tbl, index, is_green);
|
|
+ rs_fill_link_cmd(lq_data, &mcs_rate, &lq_data->lq, sta);
|
|
+ rs_send_lq_cmd(priv, &lq_data->lq, CMD_ASYNC);
|
|
+ }
|
|
+ goto out;
|
|
+
|
|
+ } else
|
|
+ window->average_tpt = ((window->success_ratio *
|
|
+ tbl->expected_tpt[index] + 64) / 128);
|
|
+
|
|
+ if (lq_data->search_better_tbl) {
|
|
+ int success_limit = IWL_RATE_SCALE_SWITCH;
|
|
+
|
|
+ if ((window->success_ratio > success_limit) ||
|
|
+ (window->average_tpt > lq_data->last_tpt)) {
|
|
+ if (!is_legacy(tbl->lq_type)) {
|
|
+ IWL_DEBUG_HT("LQ: we are switching to HT"
|
|
+ " rate suc %d current tpt %d"
|
|
+ " old tpt %d\n",
|
|
+ window->success_ratio,
|
|
+ window->average_tpt,
|
|
+ lq_data->last_tpt);
|
|
+ lq_data->enable_counter = 1;
|
|
+ }
|
|
+ lq_data->active_tbl = active_tbl;
|
|
+ current_tpt = window->average_tpt;
|
|
+ } else {
|
|
+ tbl->lq_type = LQ_NONE;
|
|
+ active_tbl = lq_data->active_tbl;
|
|
+ tbl = &(lq_data->lq_info[active_tbl]);
|
|
+
|
|
+ index = iwl_rate_index_from_plcp(
|
|
+ tbl->current_rate.rate_n_flags);
|
|
+
|
|
+ update_lq = 1;
|
|
+ current_tpt = lq_data->last_tpt;
|
|
+ IWL_DEBUG_HT("XXY GO BACK TO OLD TABLE\n");
|
|
+ }
|
|
+ lq_data->search_better_tbl = 0;
|
|
+ done_search = 1;
|
|
+ goto lq_update;
|
|
+ }
|
|
+
|
|
+ high_low = rs_get_adjacent_rate(index, rate_scale_index_msk,
|
|
+ tbl->lq_type);
|
|
+ low = high_low & 0xff;
|
|
+ high = (high_low >> 8) & 0xff;
|
|
+
|
|
+ current_tpt = window->average_tpt;
|
|
+
|
|
+ if (low != IWL_RATE_INVALID)
|
|
+ low_tpt = tbl->win[low].average_tpt;
|
|
+
|
|
+ if (high != IWL_RATE_INVALID)
|
|
+ high_tpt = tbl->win[high].average_tpt;
|
|
+
|
|
+
|
|
+ scale_action = 1;
|
|
+
|
|
+ if ((window->success_ratio <= IWL_RATE_DECREASE_TH) ||
|
|
+ (current_tpt == 0)) {
|
|
+ IWL_DEBUG_RATE("decrease rate because of low success_ratio\n");
|
|
+ scale_action = -1;
|
|
+ } else if ((low_tpt == IWL_INVALID_VALUE) &&
|
|
+ (high_tpt == IWL_INVALID_VALUE))
|
|
+ scale_action = 1;
|
|
+ else if ((low_tpt != IWL_INVALID_VALUE) &&
|
|
+ (high_tpt != IWL_INVALID_VALUE) &&
|
|
+ (low_tpt < current_tpt) &&
|
|
+ (high_tpt < current_tpt))
|
|
+ scale_action = 0;
|
|
+ else {
|
|
+ if (high_tpt != IWL_INVALID_VALUE) {
|
|
+ if (high_tpt > current_tpt)
|
|
+ scale_action = 1;
|
|
+ else {
|
|
+ IWL_DEBUG_RATE
|
|
+ ("decrease rate because of high tpt\n");
|
|
+ scale_action = -1;
|
|
+ }
|
|
+ } else if (low_tpt != IWL_INVALID_VALUE) {
|
|
+ if (low_tpt > current_tpt) {
|
|
+ IWL_DEBUG_RATE
|
|
+ ("decrease rate because of low tpt\n");
|
|
+ scale_action = -1;
|
|
+ } else
|
|
+ scale_action = 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (scale_action == -1) {
|
|
+ if ((low != IWL_RATE_INVALID) &&
|
|
+ ((window->success_ratio > IWL_RATE_HIGH_TH) ||
|
|
+ (current_tpt > (100 * tbl->expected_tpt[low]))))
|
|
+ scale_action = 0;
|
|
+ } else if ((scale_action == 1) &&
|
|
+ (window->success_ratio < IWL_RATE_INCREASE_TH))
|
|
+ scale_action = 0;
|
|
+
|
|
+ switch (scale_action) {
|
|
+ case -1:
|
|
+ if (low != IWL_RATE_INVALID) {
|
|
+ update_lq = 1;
|
|
+ index = low;
|
|
+ }
|
|
+ break;
|
|
+ case 1:
|
|
+ if (high != IWL_RATE_INVALID) {
|
|
+ update_lq = 1;
|
|
+ index = high;
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ case 0:
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_HT("choose rate scale index %d action %d low %d "
|
|
+ "high %d type %d\n",
|
|
+ index, scale_action, low, high, tbl->lq_type);
|
|
+
|
|
+ lq_update:
|
|
+ if (update_lq) {
|
|
+ rs_mcs_from_tbl(&mcs_rate, tbl, index, is_green);
|
|
+ rs_fill_link_cmd(lq_data, &mcs_rate, &lq_data->lq, sta);
|
|
+ rs_send_lq_cmd(priv, &lq_data->lq, CMD_ASYNC);
|
|
+ }
|
|
+ rs_stay_in_table(lq_data);
|
|
+
|
|
+ if (!update_lq && !done_search && !lq_data->stay_in_tbl) {
|
|
+ lq_data->last_tpt = current_tpt;
|
|
+
|
|
+ if (is_legacy(tbl->lq_type))
|
|
+ rs_move_legacy_other(priv, lq_data, index);
|
|
+ else if (is_siso(tbl->lq_type))
|
|
+ rs_move_siso_to_other(priv, lq_data, index);
|
|
+ else
|
|
+ rs_move_mimo_to_other(priv, lq_data, index);
|
|
+
|
|
+ if (lq_data->search_better_tbl) {
|
|
+ tbl = &(lq_data->lq_info[(1 - lq_data->active_tbl)]);
|
|
+ for (i = 0; i < IWL_RATE_COUNT; i++)
|
|
+ rs_rate_scale_clear_window(&(tbl->win[i]));
|
|
+
|
|
+ index = iwl_rate_index_from_plcp(
|
|
+ tbl->current_rate.rate_n_flags);
|
|
+
|
|
+ IWL_DEBUG_HT("Switch current mcs: %X index: %d\n",
|
|
+ tbl->current_rate.rate_n_flags, index);
|
|
+ rs_fill_link_cmd(lq_data, &tbl->current_rate,
|
|
+ &(lq_data->lq), sta);
|
|
+ rs_send_lq_cmd(priv, &lq_data->lq, CMD_ASYNC);
|
|
+ }
|
|
+ tbl1 = &(lq_data->lq_info[lq_data->active_tbl]);
|
|
+
|
|
+ if (is_legacy(tbl1->lq_type) &&
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+ !priv->current_assoc_ht.is_ht &&
|
|
+#endif
|
|
+ (lq_data->action_counter >= 1)) {
|
|
+ lq_data->action_counter = 0;
|
|
+ IWL_DEBUG_HT("LQ: STAY in legacy table\n");
|
|
+ rs_set_stay_in_table(1, lq_data);
|
|
+ }
|
|
+
|
|
+ if (lq_data->enable_counter &&
|
|
+ (lq_data->action_counter >= IWL_ACTION_LIMIT)) {
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+ if ((lq_data->last_tpt > TID_AGG_TPT_THREHOLD) &&
|
|
+ (priv->lq_mngr.agg_ctrl.auto_agg)) {
|
|
+ priv->lq_mngr.agg_ctrl.tid_retry =
|
|
+ TID_ALL_SPECIFIED;
|
|
+ schedule_work(&priv->agg_work);
|
|
+ }
|
|
+#endif /*CONFIG_IWLWIFI_HT_AGG */
|
|
+ lq_data->action_counter = 0;
|
|
+ rs_set_stay_in_table(0, lq_data);
|
|
+ }
|
|
+ } else {
|
|
+ if ((!update_lq) && (!done_search) && (!lq_data->flush_timer))
|
|
+ lq_data->flush_timer = jiffies;
|
|
+ }
|
|
+
|
|
+out:
|
|
+ rs_mcs_from_tbl(&tbl->current_rate, tbl, index, is_green);
|
|
+ i = index;
|
|
+ sta->last_txrate = i;
|
|
+
|
|
+ /* sta->txrate is an index to A mode rates which start
|
|
+ * at IWL_FIRST_OFDM_RATE
|
|
+ */
|
|
+ if (lq_data->phymode == (u8) MODE_IEEE80211A)
|
|
+ sta->txrate = i - IWL_FIRST_OFDM_RATE;
|
|
+
|
|
+ sta->antenna_sel_tx = tbl->lq_type;
|
|
+
|
|
+ return;
|
|
+}
|
|
+
|
|
+
|
|
+static void rs_initialize_lq(struct iwl_priv *priv,
|
|
+ struct sta_info *sta)
|
|
+{
|
|
+ int i;
|
|
+ struct iwl_rate_scale_priv *lq;
|
|
+ struct iwl_scale_tbl_info *tbl;
|
|
+ u8 active_tbl = 0;
|
|
+ int rate_idx;
|
|
+ u8 use_green = rs_use_green(priv);
|
|
+ struct iwl_rate mcs_rate;
|
|
+
|
|
+ if (!sta || !sta->rate_ctrl_priv)
|
|
+ goto out;
|
|
+
|
|
+ lq = (struct iwl_rate_scale_priv *)sta->rate_ctrl_priv;
|
|
+ i = sta->last_txrate;
|
|
+
|
|
+ if ((lq->lq.sta_id == 0xff) &&
|
|
+ (priv->iw_mode == IEEE80211_IF_TYPE_IBSS))
|
|
+ goto out;
|
|
+
|
|
+ if (!lq->search_better_tbl)
|
|
+ active_tbl = lq->active_tbl;
|
|
+ else
|
|
+ active_tbl = 1 - lq->active_tbl;
|
|
+
|
|
+ tbl = &(lq->lq_info[active_tbl]);
|
|
+
|
|
+ if ((i < 0) || (i >= IWL_RATE_COUNT))
|
|
+ i = 0;
|
|
+
|
|
+ mcs_rate.rate_n_flags = iwl_rates[i].plcp ;
|
|
+ mcs_rate.rate_n_flags |= RATE_MCS_ANT_B_MSK;
|
|
+ mcs_rate.rate_n_flags &= ~RATE_MCS_ANT_A_MSK;
|
|
+
|
|
+ if (i >= IWL_FIRST_CCK_RATE && i <= IWL_LAST_CCK_RATE)
|
|
+ mcs_rate.rate_n_flags |= RATE_MCS_CCK_MSK;
|
|
+
|
|
+ tbl->antenna_type = ANT_AUX;
|
|
+ rs_get_tbl_info_from_mcs(&mcs_rate, priv->phymode, tbl, &rate_idx);
|
|
+ if (!rs_is_ant_connected(priv->valid_antenna, tbl->antenna_type))
|
|
+ rs_toggle_antenna(&mcs_rate, tbl),
|
|
+
|
|
+ rs_mcs_from_tbl(&mcs_rate, tbl, rate_idx, use_green);
|
|
+ tbl->current_rate.rate_n_flags = mcs_rate.rate_n_flags;
|
|
+ rs_get_expected_tpt_table(lq, tbl);
|
|
+ rs_fill_link_cmd(lq, &mcs_rate, &(lq->lq), sta);
|
|
+ rs_send_lq_cmd(priv, &lq->lq, CMD_ASYNC);
|
|
+ out:
|
|
+ return;
|
|
+}
|
|
+
|
|
+static struct ieee80211_rate *rs_get_lowest_rate(struct ieee80211_local
|
|
+ *local)
|
|
+{
|
|
+ struct ieee80211_hw_mode *mode = local->oper_hw_mode;
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < mode->num_rates; i++) {
|
|
+ struct ieee80211_rate *rate = &mode->rates[i];
|
|
+
|
|
+ if (rate->flags & IEEE80211_RATE_SUPPORTED)
|
|
+ return rate;
|
|
+ }
|
|
+
|
|
+ return &mode->rates[0];
|
|
+}
|
|
+
|
|
+static struct ieee80211_rate *rs_get_rate(void *priv_rate,
|
|
+ struct net_device *dev,
|
|
+ struct sk_buff *skb,
|
|
+ struct rate_control_extra
|
|
+ *extra)
|
|
+{
|
|
+
|
|
+ int i;
|
|
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
|
+ struct sta_info *sta;
|
|
+ u16 fc;
|
|
+ struct iwl_priv *priv = (struct iwl_priv *)priv_rate;
|
|
+ struct iwl_rate_scale_priv *lq;
|
|
+
|
|
+ IWL_DEBUG_RATE("rate scale calculate new rate for skb\n");
|
|
+
|
|
+ memset(extra, 0, sizeof(*extra));
|
|
+
|
|
+ fc = le16_to_cpu(hdr->frame_control);
|
|
+ if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1)) {
|
|
+ /* Send management frames and broadcast/multicast data using
|
|
+ * lowest rate. */
|
|
+ /* TODO: this could probably be improved.. */
|
|
+ return rs_get_lowest_rate(local);
|
|
+ }
|
|
+
|
|
+ sta = sta_info_get(local, hdr->addr1);
|
|
+
|
|
+ if (!sta || !sta->rate_ctrl_priv) {
|
|
+ if (sta)
|
|
+ sta_info_put(sta);
|
|
+ return rs_get_lowest_rate(local);
|
|
+ }
|
|
+
|
|
+ lq = (struct iwl_rate_scale_priv *)sta->rate_ctrl_priv;
|
|
+ i = sta->last_txrate;
|
|
+
|
|
+ if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) && !lq->ibss_sta_added) {
|
|
+ u8 sta_id = iwl_hw_find_station(priv, hdr->addr1);
|
|
+
|
|
+ if (sta_id == IWL_INVALID_STATION) {
|
|
+ IWL_DEBUG_RATE("LQ: ADD station " MAC_FMT "\n",
|
|
+ MAC_ARG(hdr->addr1));
|
|
+ sta_id = iwl_add_station(priv,
|
|
+ hdr->addr1, 0, CMD_ASYNC);
|
|
+ }
|
|
+ if ((sta_id != IWL_INVALID_STATION)) {
|
|
+ lq->lq.sta_id = sta_id;
|
|
+ lq->lq.rs_table[0].rate_n_flags = 0;
|
|
+ lq->ibss_sta_added = 1;
|
|
+ rs_initialize_lq(priv, sta);
|
|
+ }
|
|
+ if (!lq->ibss_sta_added)
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ done:
|
|
+ sta_info_put(sta);
|
|
+ if ((i < 0) || (i > IWL_RATE_COUNT))
|
|
+ return rs_get_lowest_rate(local);
|
|
+
|
|
+ return &priv->ieee_rates[i];
|
|
+}
|
|
+
|
|
+static void *rs_alloc_sta(void *priv, gfp_t gfp)
|
|
+{
|
|
+ struct iwl_rate_scale_priv *crl;
|
|
+ int i, j;
|
|
+
|
|
+ IWL_DEBUG_RATE("create station rate scale window\n");
|
|
+
|
|
+ crl = kzalloc(sizeof(struct iwl_rate_scale_priv), gfp);
|
|
+
|
|
+ if (crl == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ memset(crl, 0, sizeof(struct iwl_rate_scale_priv));
|
|
+ crl->lq.sta_id = 0xff;
|
|
+
|
|
+ for (j = 0; j < LQ_SIZE; j++)
|
|
+ for (i = 0; i < IWL_RATE_COUNT; i++)
|
|
+ rs_rate_scale_clear_window(&(crl->lq_info[j].win[i]));
|
|
+
|
|
+ return crl;
|
|
+}
|
|
+
|
|
+static void rs_rate_init(void *priv_rate, void *priv_sta,
|
|
+ struct ieee80211_local *local,
|
|
+ struct sta_info *sta)
|
|
+{
|
|
+ int i, j;
|
|
+ struct ieee80211_hw_mode *mode = local->oper_hw_mode;
|
|
+ struct iwl_priv *priv = (struct iwl_priv *)priv_rate;
|
|
+ struct iwl_rate_scale_priv *crl = priv_sta;
|
|
+
|
|
+ memset(crl, 0, sizeof(struct iwl_rate_scale_priv));
|
|
+
|
|
+ crl->lq.sta_id = 0xff;
|
|
+ crl->flush_timer = 0;
|
|
+ sta->txrate = 3;
|
|
+ for (j = 0; j < LQ_SIZE; j++)
|
|
+ for (i = 0; i < IWL_RATE_COUNT; i++)
|
|
+ rs_rate_scale_clear_window(&(crl->lq_info[j].win[i]));
|
|
+
|
|
+ IWL_DEBUG_RATE("rate scale global init\n");
|
|
+ /* TODO: what is a good starting rate for STA? About middle? Maybe not
|
|
+ * the lowest or the highest rate.. Could consider using RSSI from
|
|
+ * previous packets? Need to have IEEE 802.1X auth succeed immediately
|
|
+ * after assoc.. */
|
|
+
|
|
+ crl->ibss_sta_added = 0;
|
|
+ if (priv->iw_mode == IEEE80211_IF_TYPE_AP) {
|
|
+ u8 sta_id = iwl_hw_find_station(priv, sta->addr);
|
|
+ /* for IBSS the call are from tasklet */
|
|
+ IWL_DEBUG_HT("LQ: ADD station " MAC_FMT " \n",
|
|
+ MAC_ARG(sta->addr));
|
|
+
|
|
+ if (sta_id == IWL_INVALID_STATION) {
|
|
+ IWL_DEBUG_RATE("LQ: ADD station " MAC_FMT "\n",
|
|
+ MAC_ARG(sta->addr));
|
|
+ sta_id = iwl_add_station(priv,
|
|
+ sta->addr, 0, CMD_ASYNC);
|
|
+ }
|
|
+ if ((sta_id != IWL_INVALID_STATION)) {
|
|
+ crl->lq.sta_id = sta_id;
|
|
+ crl->lq.rs_table[0].rate_n_flags = 0;
|
|
+ }
|
|
+ priv->lq_mngr.lq_ready = 1;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < mode->num_rates; i++) {
|
|
+ if ((sta->supp_rates & BIT(i)) &&
|
|
+ (mode->rates[i].flags & IEEE80211_RATE_SUPPORTED))
|
|
+ sta->txrate = i;
|
|
+ }
|
|
+ sta->last_txrate = sta->txrate;
|
|
+ /* For MODE_IEEE80211A mode cck rate are at end
|
|
+ * rate table
|
|
+ */
|
|
+ if (local->hw.conf.phymode == MODE_IEEE80211A)
|
|
+ sta->last_txrate += IWL_FIRST_OFDM_RATE;
|
|
+
|
|
+ crl->is_dup = priv->is_dup;
|
|
+ crl->valid_antenna = priv->valid_antenna;
|
|
+ crl->antenna = priv->antenna;
|
|
+ crl->is_green = rs_use_green(priv);
|
|
+ crl->active_rate = priv->active_rate;
|
|
+ crl->active_rate &= ~(0x1000);
|
|
+ crl->active_rate_basic = priv->active_rate_basic;
|
|
+ crl->phymode = priv->phymode;
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+ crl->active_siso_rate = (priv->current_assoc_ht.supp_rates[0] << 1);
|
|
+ crl->active_siso_rate |= (priv->current_assoc_ht.supp_rates[0] & 0x1);
|
|
+ crl->active_siso_rate &= ~((u16)0x2);
|
|
+ crl->active_siso_rate = crl->active_siso_rate << IWL_FIRST_OFDM_RATE;
|
|
+
|
|
+ crl->active_mimo_rate = (priv->current_assoc_ht.supp_rates[1] << 1);
|
|
+ crl->active_mimo_rate |= (priv->current_assoc_ht.supp_rates[1] & 0x1);
|
|
+ crl->active_mimo_rate &= ~((u16)0x2);
|
|
+ crl->active_mimo_rate = crl->active_mimo_rate << IWL_FIRST_OFDM_RATE;
|
|
+ IWL_DEBUG_HT("MIMO RATE 0x%X SISO MASK 0x%X\n", crl->active_siso_rate,
|
|
+ crl->active_mimo_rate);
|
|
+#endif /*CONFIG_IWLWIFI_HT*/
|
|
+
|
|
+ if (priv)
|
|
+ rs_initialize_lq(priv, sta);
|
|
+}
|
|
+
|
|
+static int rs_fill_link_cmd(struct iwl_rate_scale_priv *lq_data,
|
|
+ struct iwl_rate *tx_mcs,
|
|
+ struct iwl_link_quality_cmd *lq_cmd,
|
|
+ struct sta_info *sta)
|
|
+{
|
|
+ int index = 0;
|
|
+ int rc = 0;
|
|
+ int rate_idx;
|
|
+ u8 ant_toggle_count = 0;
|
|
+ u8 use_ht_possible = 1;
|
|
+ u8 repeat_cur_rate = 0;
|
|
+ struct iwl_rate new_rate;
|
|
+ struct iwl_scale_tbl_info tbl_type = { 0 };
|
|
+
|
|
+ rs_get_tbl_info_from_mcs(tx_mcs, lq_data->phymode,
|
|
+ &tbl_type, &rate_idx);
|
|
+
|
|
+ if (is_legacy(tbl_type.lq_type)) {
|
|
+ ant_toggle_count = 1;
|
|
+ repeat_cur_rate = IWL_NUMBER_TRY;
|
|
+ } else
|
|
+ repeat_cur_rate = IWL_HT_NUMBER_TRY;
|
|
+
|
|
+ lq_cmd->general_params.mimo_delimiter =
|
|
+ is_mimo(tbl_type.lq_type) ? 1 : 0;
|
|
+ lq_cmd->rs_table[index].rate_n_flags =
|
|
+ cpu_to_le32(tx_mcs->rate_n_flags);
|
|
+ new_rate.rate_n_flags = tx_mcs->rate_n_flags;
|
|
+
|
|
+ if (is_mimo(tbl_type.lq_type) || (tbl_type.antenna_type == ANT_MAIN))
|
|
+ lq_cmd->general_params.single_stream_ant_msk = 1;
|
|
+ else
|
|
+ lq_cmd->general_params.single_stream_ant_msk = 2;
|
|
+
|
|
+ index++;
|
|
+ repeat_cur_rate--;
|
|
+
|
|
+ while (index < LINK_QUAL_MAX_RETRY_NUM) {
|
|
+ while (repeat_cur_rate && (index < LINK_QUAL_MAX_RETRY_NUM)) {
|
|
+ if (is_legacy(tbl_type.lq_type)) {
|
|
+ if (ant_toggle_count <
|
|
+ NUM_TRY_BEFORE_ANTENNA_TOGGLE)
|
|
+ ant_toggle_count++;
|
|
+ else {
|
|
+ rs_toggle_antenna(&new_rate, &tbl_type);
|
|
+ ant_toggle_count = 1;
|
|
+ }
|
|
+ }
|
|
+ lq_cmd->rs_table[index].rate_n_flags =
|
|
+ cpu_to_le32(new_rate.rate_n_flags);
|
|
+ repeat_cur_rate--;
|
|
+ index++;
|
|
+ }
|
|
+
|
|
+ rs_get_tbl_info_from_mcs(&new_rate, lq_data->phymode, &tbl_type,
|
|
+ &rate_idx);
|
|
+
|
|
+ if (is_mimo(tbl_type.lq_type))
|
|
+ lq_cmd->general_params.mimo_delimiter = index;
|
|
+
|
|
+ rs_get_lower_rate(lq_data, &tbl_type, rate_idx,
|
|
+ use_ht_possible, &new_rate, sta);
|
|
+
|
|
+ if (is_legacy(tbl_type.lq_type)) {
|
|
+ if (ant_toggle_count < NUM_TRY_BEFORE_ANTENNA_TOGGLE)
|
|
+ ant_toggle_count++;
|
|
+ else {
|
|
+ rs_toggle_antenna(&new_rate, &tbl_type);
|
|
+ ant_toggle_count = 1;
|
|
+ }
|
|
+ repeat_cur_rate = IWL_NUMBER_TRY;
|
|
+ } else
|
|
+ repeat_cur_rate = IWL_HT_NUMBER_TRY;
|
|
+
|
|
+ use_ht_possible = 0;
|
|
+
|
|
+ lq_cmd->rs_table[index].rate_n_flags =
|
|
+ cpu_to_le32(new_rate.rate_n_flags);
|
|
+ /* lq_cmd->rs_table[index].rate_n_flags = 0x800d; */
|
|
+
|
|
+ index++;
|
|
+ repeat_cur_rate--;
|
|
+ }
|
|
+
|
|
+ /* lq_cmd->rs_table[0].rate_n_flags = 0x800d; */
|
|
+
|
|
+ lq_cmd->general_params.dual_stream_ant_msk = 3;
|
|
+ lq_cmd->agg_params.agg_dis_start_th = 3;
|
|
+ lq_cmd->agg_params.agg_time_limit = cpu_to_le16(4000);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static void *rs_alloc(struct ieee80211_local *local)
|
|
+{
|
|
+ IWL_DEBUG_RATE("enter\n");
|
|
+ IWL_DEBUG_RATE("leave\n");
|
|
+ return local->hw.priv;
|
|
+}
|
|
+
|
|
+static void rs_free(void *data)
|
|
+{
|
|
+ IWL_DEBUG_RATE("enter\n");
|
|
+ IWL_DEBUG_RATE("leave\n");
|
|
+}
|
|
+
|
|
+static void rs_clear(void *priv_rate)
|
|
+{
|
|
+ struct iwl_priv *priv = (struct iwl_priv *) priv_rate;
|
|
+
|
|
+ IWL_DEBUG_RATE("NOP\n");
|
|
+
|
|
+ priv->lq_mngr.lq_ready = 0;
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+ if (priv->lq_mngr.agg_ctrl.granted_ba)
|
|
+ iwl4965_turn_off_agg(priv, TID_ALL_SPECIFIED);
|
|
+#endif /*CONFIG_IWLWIFI_HT_AGG */
|
|
+#endif /* CONFIG_IWLWIFI_HT */
|
|
+}
|
|
+
|
|
+static void rs_free_sta(void *priv, void *priv_sta)
|
|
+{
|
|
+ struct iwl_rate_scale_priv *rs_priv = priv_sta;
|
|
+
|
|
+ IWL_DEBUG_RATE("enter\n");
|
|
+ kfree(rs_priv);
|
|
+ IWL_DEBUG_RATE("leave\n");
|
|
+}
|
|
+
|
|
+
|
|
+static struct rate_control_ops rs_ops = {
|
|
+ .module = NULL,
|
|
+ .name = RS_NAME,
|
|
+ .tx_status = rs_tx_status,
|
|
+ .get_rate = rs_get_rate,
|
|
+ .rate_init = rs_rate_init,
|
|
+ .clear = rs_clear,
|
|
+ .alloc = rs_alloc,
|
|
+ .free = rs_free,
|
|
+ .alloc_sta = rs_alloc_sta,
|
|
+ .free_sta = rs_free_sta,
|
|
+};
|
|
+
|
|
+int iwl_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id)
|
|
+{
|
|
+ struct ieee80211_local *local = hw_to_local(hw);
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
+ struct iwl_rate_scale_priv *rs_priv;
|
|
+ struct sta_info *sta;
|
|
+ int count = 0, i;
|
|
+ u32 samples = 0, success = 0, good = 0;
|
|
+ unsigned long now = jiffies;
|
|
+ u32 max_time = 0;
|
|
+ u8 lq_type, antenna;
|
|
+
|
|
+ sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr);
|
|
+ if (!sta || !sta->rate_ctrl_priv) {
|
|
+ if (sta) {
|
|
+ sta_info_put(sta);
|
|
+ IWL_DEBUG_RATE("leave - no private rate data!\n");
|
|
+ } else
|
|
+ IWL_DEBUG_RATE("leave - no station!\n");
|
|
+ return sprintf(buf, "station %d not found\n", sta_id);
|
|
+ }
|
|
+
|
|
+ rs_priv = (void *)sta->rate_ctrl_priv;
|
|
+
|
|
+ lq_type = rs_priv->lq_info[rs_priv->active_tbl].lq_type;
|
|
+ antenna = rs_priv->lq_info[rs_priv->active_tbl].antenna_type;
|
|
+
|
|
+ if (is_legacy(lq_type))
|
|
+ i = IWL_RATE_54M_INDEX;
|
|
+ else
|
|
+ i = IWL_RATE_60M_INDEX;
|
|
+ while (1) {
|
|
+ u64 mask;
|
|
+ int j;
|
|
+ int active = rs_priv->active_tbl;
|
|
+
|
|
+ count +=
|
|
+ sprintf(&buf[count], " %2dMbs: ", iwl_rates[i].ieee / 2);
|
|
+
|
|
+ mask = (1ULL << (IWL_RATE_MAX_WINDOW - 1));
|
|
+ for (j = 0; j < IWL_RATE_MAX_WINDOW; j++, mask >>= 1)
|
|
+ buf[count++] =
|
|
+ (rs_priv->lq_info[active].win[i].data & mask)
|
|
+ ? '1' : '0';
|
|
+
|
|
+ samples += rs_priv->lq_info[active].win[i].counter;
|
|
+ good += rs_priv->lq_info[active].win[i].success_counter;
|
|
+ success += rs_priv->lq_info[active].win[i].success_counter *
|
|
+ iwl_rates[i].ieee;
|
|
+
|
|
+ if (rs_priv->lq_info[active].win[i].stamp) {
|
|
+ int delta =
|
|
+ jiffies_to_msecs(now -
|
|
+ rs_priv->lq_info[active].win[i].stamp);
|
|
+
|
|
+ if (delta > max_time)
|
|
+ max_time = delta;
|
|
+
|
|
+ count += sprintf(&buf[count], "%5dms\n", delta);
|
|
+ } else
|
|
+ buf[count++] = '\n';
|
|
+
|
|
+ j = iwl_get_prev_ieee_rate(i);
|
|
+ if (j == i)
|
|
+ break;
|
|
+ i = j;
|
|
+ }
|
|
+ sta_info_put(sta);
|
|
+
|
|
+ /* Display the average rate of all samples taken.
|
|
+ *
|
|
+ * NOTE: We multiple # of samples by 2 since the IEEE measurement
|
|
+ * added from iwl_rates is actually 2X the rate */
|
|
+ if (samples)
|
|
+ count += sprintf(
|
|
+ &buf[count],
|
|
+ "\nAverage rate is %3d.%02dMbs over last %4dms\n"
|
|
+ "%3d%% success (%d good packets over %d tries)\n",
|
|
+ success / (2 * samples), (success * 5 / samples) % 10,
|
|
+ max_time, good * 100 / samples, good, samples);
|
|
+ else
|
|
+ count += sprintf(&buf[count], "\nAverage rate: 0Mbs\n");
|
|
+ count += sprintf(&buf[count], "\nrate scale type %d anntena %d \n",
|
|
+ lq_type, antenna);
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+void iwl_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id)
|
|
+{
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
+
|
|
+ priv->lq_mngr.lq_ready = 1;
|
|
+}
|
|
+
|
|
+void iwl_rate_control_register(struct ieee80211_hw *hw)
|
|
+{
|
|
+ ieee80211_rate_control_register(&rs_ops);
|
|
+}
|
|
+
|
|
+void iwl_rate_control_unregister(struct ieee80211_hw *hw)
|
|
+{
|
|
+ ieee80211_rate_control_unregister(&rs_ops);
|
|
+}
|
|
+
|
|
diff --git a/drivers/net/wireless/iwl-4965-rs.h b/drivers/net/wireless/iwl-4965-rs.h
|
|
new file mode 100644
|
|
index 0000000..aa9aae5
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/iwl-4965-rs.h
|
|
@@ -0,0 +1,286 @@
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * Copyright(c) 2003 - 2007 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.
|
|
+ *
|
|
+ * Contact Information:
|
|
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
|
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+#ifndef __iwl_4965_rs_h__
|
|
+#define __iwl_4965_rs_h__
|
|
+
|
|
+#include "iwl-hw.h"
|
|
+#include "iwl-4965.h"
|
|
+
|
|
+struct iwl_rate_info {
|
|
+ u8 plcp;
|
|
+ u8 plcp_siso;
|
|
+ u8 plcp_mimo;
|
|
+ u8 ieee;
|
|
+ u8 prev_ieee; /* previous rate in IEEE speeds */
|
|
+ u8 next_ieee; /* next rate in IEEE speeds */
|
|
+ u8 prev_rs; /* previous rate used in rs algo */
|
|
+ u8 next_rs; /* next rate used in rs algo */
|
|
+ u8 prev_rs_tgg; /* previous rate used in TGG rs algo */
|
|
+ u8 next_rs_tgg; /* next rate used in TGG rs algo */
|
|
+};
|
|
+
|
|
+enum {
|
|
+ IWL_RATE_1M_INDEX = 0,
|
|
+ IWL_RATE_2M_INDEX,
|
|
+ IWL_RATE_5M_INDEX,
|
|
+ IWL_RATE_11M_INDEX,
|
|
+ IWL_RATE_6M_INDEX,
|
|
+ IWL_RATE_9M_INDEX,
|
|
+ IWL_RATE_12M_INDEX,
|
|
+ IWL_RATE_18M_INDEX,
|
|
+ IWL_RATE_24M_INDEX,
|
|
+ IWL_RATE_36M_INDEX,
|
|
+ IWL_RATE_48M_INDEX,
|
|
+ IWL_RATE_54M_INDEX,
|
|
+ IWL_RATE_60M_INDEX,
|
|
+ IWL_RATE_COUNT,
|
|
+ IWL_RATE_INVM_INDEX = IWL_RATE_COUNT,
|
|
+ IWL_RATE_INVALID = IWL_RATE_INVM_INDEX
|
|
+};
|
|
+
|
|
+enum {
|
|
+ IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX,
|
|
+ IWL_LAST_OFDM_RATE = IWL_RATE_60M_INDEX,
|
|
+ IWL_FIRST_CCK_RATE = IWL_RATE_1M_INDEX,
|
|
+ IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX,
|
|
+};
|
|
+
|
|
+/* #define vs. enum to keep from defaulting to 'large integer' */
|
|
+#define IWL_RATE_6M_MASK (1<<IWL_RATE_6M_INDEX)
|
|
+#define IWL_RATE_9M_MASK (1<<IWL_RATE_9M_INDEX)
|
|
+#define IWL_RATE_12M_MASK (1<<IWL_RATE_12M_INDEX)
|
|
+#define IWL_RATE_18M_MASK (1<<IWL_RATE_18M_INDEX)
|
|
+#define IWL_RATE_24M_MASK (1<<IWL_RATE_24M_INDEX)
|
|
+#define IWL_RATE_36M_MASK (1<<IWL_RATE_36M_INDEX)
|
|
+#define IWL_RATE_48M_MASK (1<<IWL_RATE_48M_INDEX)
|
|
+#define IWL_RATE_54M_MASK (1<<IWL_RATE_54M_INDEX)
|
|
+#define IWL_RATE_60M_MASK (1<<IWL_RATE_60M_INDEX)
|
|
+#define IWL_RATE_1M_MASK (1<<IWL_RATE_1M_INDEX)
|
|
+#define IWL_RATE_2M_MASK (1<<IWL_RATE_2M_INDEX)
|
|
+#define IWL_RATE_5M_MASK (1<<IWL_RATE_5M_INDEX)
|
|
+#define IWL_RATE_11M_MASK (1<<IWL_RATE_11M_INDEX)
|
|
+
|
|
+enum {
|
|
+ IWL_RATE_6M_PLCP = 13,
|
|
+ IWL_RATE_9M_PLCP = 15,
|
|
+ IWL_RATE_12M_PLCP = 5,
|
|
+ IWL_RATE_18M_PLCP = 7,
|
|
+ IWL_RATE_24M_PLCP = 9,
|
|
+ IWL_RATE_36M_PLCP = 11,
|
|
+ IWL_RATE_48M_PLCP = 1,
|
|
+ IWL_RATE_54M_PLCP = 3,
|
|
+ IWL_RATE_60M_PLCP = 3,
|
|
+ IWL_RATE_1M_PLCP = 10,
|
|
+ IWL_RATE_2M_PLCP = 20,
|
|
+ IWL_RATE_5M_PLCP = 55,
|
|
+ IWL_RATE_11M_PLCP = 110,
|
|
+};
|
|
+
|
|
+/* OFDM HT rate plcp */
|
|
+enum {
|
|
+ IWL_RATE_SISO_6M_PLCP = 0,
|
|
+ IWL_RATE_SISO_12M_PLCP = 1,
|
|
+ IWL_RATE_SISO_18M_PLCP = 2,
|
|
+ IWL_RATE_SISO_24M_PLCP = 3,
|
|
+ IWL_RATE_SISO_36M_PLCP = 4,
|
|
+ IWL_RATE_SISO_48M_PLCP = 5,
|
|
+ IWL_RATE_SISO_54M_PLCP = 6,
|
|
+ IWL_RATE_SISO_60M_PLCP = 7,
|
|
+ IWL_RATE_MIMO_6M_PLCP = 0x8,
|
|
+ IWL_RATE_MIMO_12M_PLCP = 0x9,
|
|
+ IWL_RATE_MIMO_18M_PLCP = 0xa,
|
|
+ IWL_RATE_MIMO_24M_PLCP = 0xb,
|
|
+ IWL_RATE_MIMO_36M_PLCP = 0xc,
|
|
+ IWL_RATE_MIMO_48M_PLCP = 0xd,
|
|
+ IWL_RATE_MIMO_54M_PLCP = 0xe,
|
|
+ IWL_RATE_MIMO_60M_PLCP = 0xf,
|
|
+ IWL_RATE_SISO_INVM_PLCP,
|
|
+ IWL_RATE_MIMO_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP,
|
|
+};
|
|
+
|
|
+enum {
|
|
+ IWL_RATE_6M_IEEE = 12,
|
|
+ IWL_RATE_9M_IEEE = 18,
|
|
+ IWL_RATE_12M_IEEE = 24,
|
|
+ IWL_RATE_18M_IEEE = 36,
|
|
+ IWL_RATE_24M_IEEE = 48,
|
|
+ IWL_RATE_36M_IEEE = 72,
|
|
+ IWL_RATE_48M_IEEE = 96,
|
|
+ IWL_RATE_54M_IEEE = 108,
|
|
+ IWL_RATE_60M_IEEE = 120,
|
|
+ IWL_RATE_1M_IEEE = 2,
|
|
+ IWL_RATE_2M_IEEE = 4,
|
|
+ IWL_RATE_5M_IEEE = 11,
|
|
+ IWL_RATE_11M_IEEE = 22,
|
|
+};
|
|
+
|
|
+#define IWL_CCK_BASIC_RATES_MASK \
|
|
+ (IWL_RATE_1M_MASK | \
|
|
+ IWL_RATE_2M_MASK)
|
|
+
|
|
+#define IWL_CCK_RATES_MASK \
|
|
+ (IWL_BASIC_RATES_MASK | \
|
|
+ IWL_RATE_5M_MASK | \
|
|
+ IWL_RATE_11M_MASK)
|
|
+
|
|
+#define IWL_OFDM_BASIC_RATES_MASK \
|
|
+ (IWL_RATE_6M_MASK | \
|
|
+ IWL_RATE_12M_MASK | \
|
|
+ IWL_RATE_24M_MASK)
|
|
+
|
|
+#define IWL_OFDM_RATES_MASK \
|
|
+ (IWL_OFDM_BASIC_RATES_MASK | \
|
|
+ IWL_RATE_9M_MASK | \
|
|
+ IWL_RATE_18M_MASK | \
|
|
+ IWL_RATE_36M_MASK | \
|
|
+ IWL_RATE_48M_MASK | \
|
|
+ IWL_RATE_54M_MASK)
|
|
+
|
|
+#define IWL_BASIC_RATES_MASK \
|
|
+ (IWL_OFDM_BASIC_RATES_MASK | \
|
|
+ IWL_CCK_BASIC_RATES_MASK)
|
|
+
|
|
+#define IWL_RATES_MASK ((1<<IWL_RATE_COUNT)-1)
|
|
+
|
|
+#define IWL_INVALID_VALUE -1
|
|
+
|
|
+#define IWL_MIN_RSSI_VAL -100
|
|
+#define IWL_MAX_RSSI_VAL 0
|
|
+
|
|
+extern const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT];
|
|
+
|
|
+#define LQ_SIZE 2
|
|
+
|
|
+enum iwl_table_type {
|
|
+ LQ_NONE,
|
|
+ LQ_G,
|
|
+ LQ_A,
|
|
+ LQ_SISO,
|
|
+ LQ_MIMO,
|
|
+ LQ_MAX,
|
|
+};
|
|
+
|
|
+enum iwl_antenna_type {
|
|
+ ANT_NONE,
|
|
+ ANT_MAIN,
|
|
+ ANT_AUX,
|
|
+ ANT_BOTH,
|
|
+};
|
|
+
|
|
+static inline int iwl_rate_index_from_plcp(int plcp)
|
|
+{
|
|
+ int i = 0;
|
|
+
|
|
+ if (plcp & RATE_MCS_HT_MSK) {
|
|
+ i = (plcp & 0xff);
|
|
+
|
|
+ if (i >= IWL_RATE_MIMO_6M_PLCP)
|
|
+ i = i - IWL_RATE_MIMO_6M_PLCP;
|
|
+
|
|
+ i += IWL_FIRST_OFDM_RATE;
|
|
+ /* skip 9M not supported in ht*/
|
|
+ if (i >= IWL_RATE_9M_INDEX)
|
|
+ i += 1;
|
|
+ if ((i >= IWL_FIRST_OFDM_RATE) &&
|
|
+ (i <= IWL_LAST_OFDM_RATE))
|
|
+ return i;
|
|
+ } else {
|
|
+ for (i = 0; i < ARRAY_SIZE(iwl_rates); i++)
|
|
+ if (iwl_rates[i].plcp == (plcp &0xFF))
|
|
+ return i;
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static inline u8 iwl_rate_get_lowest_plcp(int rate_mask)
|
|
+{
|
|
+ u8 i;
|
|
+
|
|
+ for (i = IWL_RATE_1M_INDEX; i != IWL_RATE_INVALID;
|
|
+ i = iwl_rates[i].next_ieee) {
|
|
+ if (rate_mask & (1 << i))
|
|
+ return iwl_rates[i].plcp;
|
|
+ }
|
|
+
|
|
+ return IWL_RATE_INVALID;
|
|
+}
|
|
+
|
|
+static inline u8 iwl_get_prev_ieee_rate(u8 rate_index)
|
|
+{
|
|
+ u8 rate = iwl_rates[rate_index].prev_ieee;
|
|
+ if (rate == IWL_RATE_INVALID)
|
|
+ rate = rate_index;
|
|
+ return rate;
|
|
+}
|
|
+
|
|
+#if IWL == 4965
|
|
+/**
|
|
+ * iwl_fill_rs_info - Fill an output text buffer with the rate representation
|
|
+ *
|
|
+ * NOTE: This is provided as a quick mechanism for a user to visualize
|
|
+ * the performance of the rate control alogirthm and is not meant to be
|
|
+ * parsed software.
|
|
+ */
|
|
+extern int iwl_fill_rs_info(struct ieee80211_hw *, char *buf, u8 sta_id);
|
|
+
|
|
+/**
|
|
+ * iwl_rate_scale_init - Initialize the rate scale table based on assoc info
|
|
+ *
|
|
+ * The specific througput table used is based on the type of network
|
|
+ * the associated with, including A, B, G, and G w/ TGG protection
|
|
+ */
|
|
+extern void iwl_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id);
|
|
+
|
|
+/**
|
|
+ * iwl_rate_control_register - Register the rate control algorithm callbacks
|
|
+ *
|
|
+ * Since the rate control algorithm is hardware specific, there is no need
|
|
+ * or reason to place it as a stand alone module. The driver can call
|
|
+ * iwl_rate_control_register in order to register the rate control callbacks
|
|
+ * with the mac80211 subsystem. This should be performed prior to calling
|
|
+ * ieee80211_register_hw
|
|
+ *
|
|
+ */
|
|
+extern void iwl_rate_control_register(struct ieee80211_hw *hw);
|
|
+
|
|
+/**
|
|
+ * iwl_rate_control_unregister - Unregister the rate control callbacks
|
|
+ *
|
|
+ * This should be called after calling ieee80211_unregister_hw, but before
|
|
+ * the driver is unloaded.
|
|
+ */
|
|
+extern void iwl_rate_control_unregister(struct ieee80211_hw *hw);
|
|
+#else
|
|
+static inline int iwl_fill_rs_info(struct ieee80211_hw *hw, char *buf,
|
|
+ u8 sta_id)
|
|
+{ return -ENOTSUPP; }
|
|
+static inline void iwl_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id) {}
|
|
+static inline void iwl_rate_control_register(struct ieee80211_hw *hw) {}
|
|
+static inline void iwl_rate_control_unregister(struct ieee80211_hw *hw) {}
|
|
+#endif /* IWL == 4965 */
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/net/wireless/iwl-4965.c b/drivers/net/wireless/iwl-4965.c
|
|
new file mode 100644
|
|
index 0000000..a78cc8c
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/iwl-4965.c
|
|
@@ -0,0 +1,4763 @@
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * Copyright(c) 2003 - 2007 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.
|
|
+ *
|
|
+ * Contact Information:
|
|
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
|
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/version.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/pci.h>
|
|
+#include <linux/dma-mapping.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/skbuff.h>
|
|
+#include <linux/netdevice.h>
|
|
+#include <linux/wireless.h>
|
|
+#include <net/mac80211.h>
|
|
+#include <linux/netdevice.h>
|
|
+#include <linux/etherdevice.h>
|
|
+#include <linux/delay.h>
|
|
+
|
|
+#include "iwlwifi.h"
|
|
+#include "iwl-4965.h"
|
|
+#include "iwl-helpers.h"
|
|
+
|
|
+#define IWL_DECLARE_RATE_INFO(r, s, ip, in, rp, rn, pp, np) \
|
|
+ [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \
|
|
+ IWL_RATE_SISO_##s##M_PLCP, \
|
|
+ IWL_RATE_MIMO_##s##M_PLCP, \
|
|
+ IWL_RATE_##r##M_IEEE, \
|
|
+ IWL_RATE_##ip##M_INDEX, \
|
|
+ IWL_RATE_##in##M_INDEX, \
|
|
+ IWL_RATE_##rp##M_INDEX, \
|
|
+ IWL_RATE_##rn##M_INDEX, \
|
|
+ IWL_RATE_##pp##M_INDEX, \
|
|
+ IWL_RATE_##np##M_INDEX }
|
|
+
|
|
+/*
|
|
+ * Parameter order:
|
|
+ * rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate
|
|
+ *
|
|
+ * If there isn't a valid next or previous rate then INV is used which
|
|
+ * maps to IWL_RATE_INVALID
|
|
+ *
|
|
+ */
|
|
+const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT] = {
|
|
+ IWL_DECLARE_RATE_INFO(1, INV, INV, 2, INV, 2, INV, 2), /* 1mbps */
|
|
+ IWL_DECLARE_RATE_INFO(2, INV, 1, 5, 1, 5, 1, 5), /* 2mbps */
|
|
+ IWL_DECLARE_RATE_INFO(5, INV, 2, 6, 2, 11, 2, 11), /*5.5mbps */
|
|
+ IWL_DECLARE_RATE_INFO(11, INV, 9, 12, 9, 12, 5, 18), /* 11mbps */
|
|
+ IWL_DECLARE_RATE_INFO(6, 6, 5, 9, 5, 11, 5, 11), /* 6mbps */
|
|
+ IWL_DECLARE_RATE_INFO(9, 6, 6, 11, 6, 11, 5, 11), /* 9mbps */
|
|
+ IWL_DECLARE_RATE_INFO(12, 12, 11, 18, 11, 18, 11, 18), /* 12mbps */
|
|
+ IWL_DECLARE_RATE_INFO(18, 18, 12, 24, 12, 24, 11, 24), /* 18mbps */
|
|
+ IWL_DECLARE_RATE_INFO(24, 24, 18, 36, 18, 36, 18, 36), /* 24mbps */
|
|
+ IWL_DECLARE_RATE_INFO(36, 36, 24, 48, 24, 48, 24, 48), /* 36mbps */
|
|
+ IWL_DECLARE_RATE_INFO(48, 48, 36, 54, 36, 54, 36, 54), /* 48mbps */
|
|
+ IWL_DECLARE_RATE_INFO(54, 54, 48, INV, 48, INV, 48, INV),/* 54mbps */
|
|
+ IWL_DECLARE_RATE_INFO(60, 60, 48, INV, 48, INV, 48, INV),/* 60mbps */
|
|
+};
|
|
+
|
|
+static int is_fat_channel(__le32 rxon_flags)
|
|
+{
|
|
+ return (rxon_flags & RXON_FLG_CHANNEL_MODE_PURE_40_MSK) ||
|
|
+ (rxon_flags & RXON_FLG_CHANNEL_MODE_MIXED_MSK);
|
|
+}
|
|
+
|
|
+static u8 is_single_stream(struct iwl_priv *priv)
|
|
+{
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+ if (!priv->is_ht_enabled || !priv->current_assoc_ht.is_ht ||
|
|
+ (priv->active_rate_ht[1] == 0) ||
|
|
+ (priv->ps_mode == IWL_MIMO_PS_STATIC))
|
|
+ return 1;
|
|
+#else
|
|
+ return 1;
|
|
+#endif /*CONFIG_IWLWIFI_HT */
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Determine how many receiver/antenna chains to use.
|
|
+ * More provides better reception via diversity. Fewer saves power.
|
|
+ * MIMO (dual stream) requires at least 2, but works better with 3.
|
|
+ * This does not determine *which* chains to use, just how many.
|
|
+ */
|
|
+static int iwl4965_get_rx_chain_counter(struct iwl_priv *priv,
|
|
+ u8 *idle_state, u8 *rx_state)
|
|
+{
|
|
+ u8 is_single = is_single_stream(priv);
|
|
+ u8 is_cam = test_bit(STATUS_POWER_PMI, &priv->status) ? 0 : 1;
|
|
+
|
|
+ /* # of Rx chains to use when expecting MIMO. */
|
|
+ if (is_single || (!is_cam && (priv->ps_mode == IWL_MIMO_PS_STATIC)))
|
|
+ *rx_state = 2;
|
|
+ else
|
|
+ *rx_state = 3;
|
|
+
|
|
+ /* # Rx chains when idling and maybe trying to save power */
|
|
+ switch (priv->ps_mode) {
|
|
+ case IWL_MIMO_PS_STATIC:
|
|
+ case IWL_MIMO_PS_DYNAMIC:
|
|
+ *idle_state = (is_cam) ? 2 : 1;
|
|
+ break;
|
|
+ case IWL_MIMO_PS_NONE:
|
|
+ *idle_state = (is_cam) ? *rx_state : 1;
|
|
+ break;
|
|
+ default:
|
|
+ *idle_state = 1;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int iwl_hw_rxq_stop(struct iwl_priv *priv)
|
|
+{
|
|
+ int rc;
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc) {
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ /* stop HW */
|
|
+ iwl_write_restricted(priv, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0);
|
|
+ rc = iwl_poll_restricted_bit(priv, FH_MEM_RSSR_RX_STATUS_REG,
|
|
+ (1 << 24), 1000);
|
|
+ if (rc < 0)
|
|
+ IWL_ERROR("Can't stop Rx DMA.\n");
|
|
+
|
|
+ iwl_release_restricted_access(priv);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+u8 iwl_hw_find_station(struct iwl_priv *priv, const u8 *bssid)
|
|
+{
|
|
+ int i;
|
|
+ int start = 0;
|
|
+ int ret = IWL_INVALID_STATION;
|
|
+ unsigned long flags;
|
|
+
|
|
+ if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) ||
|
|
+ (priv->iw_mode == IEEE80211_IF_TYPE_AP))
|
|
+ start = IWL_STA_ID;
|
|
+
|
|
+ if (is_broadcast_ether_addr(bssid))
|
|
+ return IWL_BROADCAST_ID;
|
|
+
|
|
+ spin_lock_irqsave(&priv->sta_lock, flags);
|
|
+ for (i = start; i < (start + priv->num_stations); i++)
|
|
+ if ((priv->stations[i].used) &&
|
|
+ (!compare_ether_addr
|
|
+ (priv->stations[i].sta.sta.addr, bssid))) {
|
|
+ ret = i;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_ASSOC("can not find STA " MAC_FMT " total %d\n",
|
|
+ MAC_ARG(bssid), priv->num_stations);
|
|
+
|
|
+ out:
|
|
+ spin_unlock_irqrestore(&priv->sta_lock, flags);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int iwl4965_nic_set_pwr_src(struct iwl_priv *priv, int pwr_max)
|
|
+{
|
|
+ int rc = 0;
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc) {
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ if (!pwr_max) {
|
|
+ u32 val;
|
|
+
|
|
+ rc = pci_read_config_dword(priv->pci_dev, PCI_POWER_SOURCE,
|
|
+ &val);
|
|
+
|
|
+ if (val & PCI_CFG_PMC_PME_FROM_D3COLD_SUPPORT)
|
|
+ iwl_set_bits_mask_restricted_reg(
|
|
+ priv, ALM_APMG_PS_CTL,
|
|
+ APMG_PS_CTRL_REG_VAL_POWER_SRC_VAUX,
|
|
+ ~APMG_PS_CTRL_REG_MSK_POWER_SRC);
|
|
+ } else
|
|
+ iwl_set_bits_mask_restricted_reg(
|
|
+ priv, ALM_APMG_PS_CTL,
|
|
+ APMG_PS_CTRL_REG_VAL_POWER_SRC_VMAIN,
|
|
+ ~APMG_PS_CTRL_REG_MSK_POWER_SRC);
|
|
+
|
|
+ iwl_release_restricted_access(priv);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int iwl4965_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
|
|
+{
|
|
+ int rc;
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc) {
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ /* stop HW */
|
|
+ iwl_write_restricted(priv, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0);
|
|
+
|
|
+ iwl_write_restricted(priv, FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0);
|
|
+ iwl_write_restricted(priv, FH_RSCSR_CHNL0_RBDCB_BASE_REG,
|
|
+ rxq->dma_addr >> 8);
|
|
+
|
|
+ iwl_write_restricted(priv, FH_RSCSR_CHNL0_STTS_WPTR_REG,
|
|
+ (priv->hw_setting.shared_phys +
|
|
+ offsetof(struct iwl_shared, val0)) >> 4);
|
|
+
|
|
+ iwl_write_restricted(priv, FH_MEM_RCSR_CHNL0_CONFIG_REG,
|
|
+ FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL |
|
|
+ FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL |
|
|
+ IWL_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K |
|
|
+ /*0x10 << 4 | */
|
|
+ (RX_QUEUE_SIZE_LOG <<
|
|
+ FH_RCSR_RX_CONFIG_RBDCB_SIZE_BITSHIFT));
|
|
+
|
|
+ /*
|
|
+ * iwl_write32(priv,CSR_INT_COAL_REG,0);
|
|
+ */
|
|
+
|
|
+ iwl_release_restricted_access(priv);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int iwl4965_kw_init(struct iwl_priv *priv)
|
|
+{
|
|
+ unsigned long flags;
|
|
+ int rc;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc)
|
|
+ goto out;
|
|
+
|
|
+ iwl_write_restricted(priv, IWL_FH_KW_MEM_ADDR_REG,
|
|
+ priv->kw.dma_addr >> 4);
|
|
+ iwl_release_restricted_access(priv);
|
|
+out:
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int iwl4965_kw_alloc(struct iwl_priv *priv)
|
|
+{
|
|
+ struct pci_dev *dev = priv->pci_dev;
|
|
+ struct iwl_kw *kw = &priv->kw;
|
|
+
|
|
+ kw->size = IWL4965_KW_SIZE; /* TBW need set somewhere else */
|
|
+ kw->v_addr = pci_alloc_consistent(dev, kw->size, &kw->dma_addr);
|
|
+ if (!kw->v_addr)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#define CHECK_AND_PRINT(x) ((eeprom_ch->flags & EEPROM_CHANNEL_##x) \
|
|
+ ? # x " " : "")
|
|
+
|
|
+int iwl4965_set_fat_chan_info(struct iwl_priv *priv, int phymode, u16 channel,
|
|
+ const struct iwl_eeprom_channel *eeprom_ch,
|
|
+ u8 fat_extension_channel)
|
|
+{
|
|
+ struct iwl_channel_info *ch_info;
|
|
+
|
|
+ ch_info = (struct iwl_channel_info *)
|
|
+ iwl_get_channel_info(priv, phymode, channel);
|
|
+
|
|
+ if (!is_channel_valid(ch_info))
|
|
+ return -1;
|
|
+
|
|
+ IWL_DEBUG_INFO("FAT Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x"
|
|
+ " %ddBm): Ad-Hoc %ssupported\n",
|
|
+ ch_info->channel,
|
|
+ is_channel_a_band(ch_info) ?
|
|
+ "5.2" : "2.4",
|
|
+ CHECK_AND_PRINT(IBSS),
|
|
+ CHECK_AND_PRINT(ACTIVE),
|
|
+ CHECK_AND_PRINT(RADAR),
|
|
+ CHECK_AND_PRINT(WIDE),
|
|
+ CHECK_AND_PRINT(NARROW),
|
|
+ CHECK_AND_PRINT(DFS),
|
|
+ eeprom_ch->flags,
|
|
+ eeprom_ch->max_power_avg,
|
|
+ ((eeprom_ch->flags & EEPROM_CHANNEL_IBSS)
|
|
+ && !(eeprom_ch->flags & EEPROM_CHANNEL_RADAR)) ?
|
|
+ "" : "not ");
|
|
+
|
|
+ ch_info->fat_eeprom = *eeprom_ch;
|
|
+ ch_info->fat_max_power_avg = eeprom_ch->max_power_avg;
|
|
+ ch_info->fat_curr_txpow = eeprom_ch->max_power_avg;
|
|
+ ch_info->fat_min_power = 0;
|
|
+ ch_info->fat_scan_power = eeprom_ch->max_power_avg;
|
|
+ ch_info->fat_flags = eeprom_ch->flags;
|
|
+ ch_info->fat_extension_channel = fat_extension_channel;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void iwl4965_kw_free(struct iwl_priv *priv)
|
|
+{
|
|
+ struct pci_dev *dev = priv->pci_dev;
|
|
+ struct iwl_kw *kw = &priv->kw;
|
|
+
|
|
+ if (kw->v_addr) {
|
|
+ pci_free_consistent(dev, kw->size, kw->v_addr, kw->dma_addr);
|
|
+ memset(kw, 0, sizeof(*kw));
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl4965_txq_ctx_reset - Reset TX queue context
|
|
+ * Destroys all DMA structures and initialise them again
|
|
+ *
|
|
+ * @param priv
|
|
+ * @return error code
|
|
+ */
|
|
+static int iwl4965_txq_ctx_reset(struct iwl_priv *priv)
|
|
+{
|
|
+ int rc = 0;
|
|
+ int txq_id, slots_num;
|
|
+ unsigned long flags;
|
|
+
|
|
+ iwl4965_kw_free(priv);
|
|
+
|
|
+ iwl_hw_txq_ctx_free(priv);
|
|
+
|
|
+ /* Tx CMD queue */
|
|
+ rc = iwl4965_kw_alloc(priv);
|
|
+ if (rc) {
|
|
+ IWL_ERROR("Keep Warm allocation failed");
|
|
+ goto error_kw;
|
|
+ }
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (unlikely(rc)) {
|
|
+ IWL_ERROR("TX reset failed");
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ goto error_reset;
|
|
+ }
|
|
+
|
|
+ iwl_write_restricted_reg(priv, SCD_TXFACT, 0);
|
|
+ iwl_release_restricted_access(priv);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ rc = iwl4965_kw_init(priv);
|
|
+ if (rc) {
|
|
+ IWL_ERROR("kw_init failed\n");
|
|
+ goto error_reset;
|
|
+ }
|
|
+
|
|
+ /* Tx queue(s) */
|
|
+ for (txq_id = 0; txq_id < priv->hw_setting.max_txq_num; txq_id++) {
|
|
+ slots_num = (txq_id == IWL_CMD_QUEUE_NUM) ?
|
|
+ TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS;
|
|
+ rc = iwl_tx_queue_init(priv, &priv->txq[txq_id], slots_num,
|
|
+ txq_id);
|
|
+ if (rc) {
|
|
+ IWL_ERROR("Tx %d queue init failed\n", txq_id);
|
|
+ goto error;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+
|
|
+ error:
|
|
+ iwl_hw_txq_ctx_free(priv);
|
|
+ error_reset:
|
|
+ iwl4965_kw_free(priv);
|
|
+ error_kw:
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+int iwl_hw_nic_init(struct iwl_priv *priv)
|
|
+{
|
|
+ int rc;
|
|
+ unsigned long flags;
|
|
+ struct iwl_rx_queue *rxq = &priv->rxq;
|
|
+ u8 rev_id;
|
|
+ u32 val;
|
|
+ u8 val_link;
|
|
+
|
|
+ iwl_power_init_handle(priv);
|
|
+
|
|
+ /* nic_init */
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+
|
|
+ iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS,
|
|
+ CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER);
|
|
+
|
|
+ iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
|
|
+ rc = iwl_poll_bit(priv, CSR_GP_CNTRL,
|
|
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
|
|
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
|
|
+ if (rc < 0) {
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ IWL_DEBUG_INFO("Failed to init the card\n");
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc) {
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ iwl_read_restricted_reg(priv, APMG_CLK_CTRL_REG);
|
|
+
|
|
+ iwl_write_restricted_reg(priv, APMG_CLK_CTRL_REG,
|
|
+ APMG_CLK_REG_VAL_DMA_CLK_RQT |
|
|
+ APMG_CLK_REG_VAL_BSM_CLK_RQT);
|
|
+ iwl_read_restricted_reg(priv, APMG_CLK_CTRL_REG);
|
|
+
|
|
+ udelay(20);
|
|
+
|
|
+ iwl_set_bits_restricted_reg(priv, ALM_APMG_PCIDEV_STT,
|
|
+ APMG_DEV_STATE_REG_VAL_L1_ACTIVE_DISABLE);
|
|
+
|
|
+ iwl_release_restricted_access(priv);
|
|
+ iwl_write32(priv, CSR_INT_COALESCING, 512 / 32);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ /* Determine HW type */
|
|
+ rc = pci_read_config_byte(priv->pci_dev, PCI_REVISION_ID, &rev_id);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ IWL_DEBUG_INFO("HW Revision ID = 0x%X\n", rev_id);
|
|
+
|
|
+ iwl4965_nic_set_pwr_src(priv, 1);
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+
|
|
+ if ((rev_id & 0x80) == 0x80 && (rev_id & 0x7f) < 8) {
|
|
+ pci_read_config_dword(priv->pci_dev, PCI_REG_WUM8, &val);
|
|
+ /* Enable No Snoop field */
|
|
+ pci_write_config_dword(priv->pci_dev, PCI_REG_WUM8,
|
|
+ val & ~(1 << 11));
|
|
+ }
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ /* Read the EEPROM */
|
|
+ rc = iwl_eeprom_init(priv);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ if (priv->eeprom.calib_version < EEPROM_TX_POWER_VERSION_NEW) {
|
|
+ IWL_ERROR("Older EEPROM detected! Aborting.\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ pci_read_config_byte(priv->pci_dev, PCI_LINK_CTRL, &val_link);
|
|
+
|
|
+ /* disable L1 entry -- workaround for pre-B1 */
|
|
+ pci_write_config_byte(priv->pci_dev, PCI_LINK_CTRL, val_link & ~0x02);
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+
|
|
+ /* set CSR_HW_CONFIG_REG for uCode use */
|
|
+
|
|
+ iwl_set_bit(priv, CSR_SW_VER, CSR_HW_IF_CONFIG_REG_BIT_KEDRON_R |
|
|
+ CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI |
|
|
+ CSR_HW_IF_CONFIG_REG_BIT_MAC_SI);
|
|
+
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc < 0) {
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ IWL_DEBUG_INFO("Failed to init the card\n");
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ iwl_read_restricted_reg(priv, ALM_APMG_PS_CTL);
|
|
+ iwl_set_bits_restricted_reg(priv, ALM_APMG_PS_CTL,
|
|
+ APMG_PS_CTRL_REG_VAL_ALM_R_RESET_REQ);
|
|
+ udelay(5);
|
|
+ iwl_clear_bits_restricted_reg(priv, ALM_APMG_PS_CTL,
|
|
+ APMG_PS_CTRL_REG_VAL_ALM_R_RESET_REQ);
|
|
+
|
|
+ iwl_release_restricted_access(priv);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ iwl_hw_card_show_info(priv);
|
|
+
|
|
+ /* end nic_init */
|
|
+
|
|
+ /* Allocate the RX queue, or reset if it is already allocated */
|
|
+ if (!rxq->bd) {
|
|
+ rc = iwl_rx_queue_alloc(priv);
|
|
+ if (rc) {
|
|
+ IWL_ERROR("Unable to initialize Rx queue\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ } else
|
|
+ iwl_rx_queue_reset(priv, rxq);
|
|
+
|
|
+ iwl_rx_replenish(priv);
|
|
+
|
|
+ iwl4965_rx_init(priv, rxq);
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+
|
|
+ rxq->need_update = 1;
|
|
+ iwl_rx_queue_update_write_ptr(priv, rxq);
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ rc = iwl4965_txq_ctx_reset(priv);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ if (priv->eeprom.sku_cap & EEPROM_SKU_CAP_SW_RF_KILL_ENABLE)
|
|
+ IWL_DEBUG_RF_KILL("SW RF KILL supported in EEPROM.\n");
|
|
+
|
|
+ if (priv->eeprom.sku_cap & EEPROM_SKU_CAP_HW_RF_KILL_ENABLE)
|
|
+ IWL_DEBUG_RF_KILL("HW RF KILL supported in EEPROM.\n");
|
|
+
|
|
+ set_bit(STATUS_INIT, &priv->status);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int iwl_hw_nic_stop_master(struct iwl_priv *priv)
|
|
+{
|
|
+ int rc = 0;
|
|
+ u32 reg_val;
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+
|
|
+ /* set stop master bit */
|
|
+ iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER);
|
|
+
|
|
+ reg_val = iwl_read32(priv, CSR_GP_CNTRL);
|
|
+
|
|
+ if (CSR_GP_CNTRL_REG_FLAG_MAC_POWER_SAVE ==
|
|
+ (reg_val & CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE))
|
|
+ IWL_DEBUG_INFO("Card in power save, master is already "
|
|
+ "stopped\n");
|
|
+ else {
|
|
+ rc = iwl_poll_bit(priv, CSR_RESET,
|
|
+ CSR_RESET_REG_FLAG_MASTER_DISABLED,
|
|
+ CSR_RESET_REG_FLAG_MASTER_DISABLED, 100);
|
|
+ if (rc < 0) {
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ return rc;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ IWL_DEBUG_INFO("stop master\n");
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+void iwl_hw_txq_ctx_stop(struct iwl_priv *priv)
|
|
+{
|
|
+
|
|
+ int txq_id;
|
|
+ unsigned long flags;
|
|
+
|
|
+ /* reset TFD queues */
|
|
+ for (txq_id = 0; txq_id < priv->hw_setting.max_txq_num; txq_id++) {
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ if (iwl_grab_restricted_access(priv)) {
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ iwl_write_restricted(priv,
|
|
+ IWL_FH_TCSR_CHNL_TX_CONFIG_REG(txq_id),
|
|
+ 0x0);
|
|
+ iwl_poll_restricted_bit(priv, IWL_FH_TSSR_TX_STATUS_REG,
|
|
+ IWL_FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE
|
|
+ (txq_id), 200);
|
|
+ iwl_release_restricted_access(priv);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ }
|
|
+
|
|
+ iwl_hw_txq_ctx_free(priv);
|
|
+}
|
|
+
|
|
+int iwl_hw_nic_reset(struct iwl_priv *priv)
|
|
+{
|
|
+ int rc = 0;
|
|
+ unsigned long flags;
|
|
+
|
|
+ iwl_hw_nic_stop_master(priv);
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+
|
|
+ iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
|
|
+
|
|
+ udelay(10);
|
|
+
|
|
+ iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
|
|
+ rc = iwl_poll_bit(priv, CSR_RESET,
|
|
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
|
|
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25);
|
|
+
|
|
+ udelay(10);
|
|
+
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (!rc) {
|
|
+ iwl_write_restricted_reg(priv, ALM_APMG_CLK_EN,
|
|
+ APMG_CLK_REG_VAL_DMA_CLK_RQT |
|
|
+ APMG_CLK_REG_VAL_BSM_CLK_RQT);
|
|
+
|
|
+ udelay(10);
|
|
+
|
|
+ iwl_set_bits_restricted_reg(priv, ALM_APMG_PCIDEV_STT,
|
|
+ APMG_DEV_STATE_REG_VAL_L1_ACTIVE_DISABLE);
|
|
+
|
|
+ iwl_release_restricted_access(priv);
|
|
+ }
|
|
+
|
|
+ clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
|
|
+ wake_up_interruptible(&priv->wait_command_queue);
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ return rc;
|
|
+
|
|
+}
|
|
+
|
|
+#define REG_RECALIB_PERIOD (60)
|
|
+
|
|
+/**
|
|
+ * iwl4965_bg_statistics_periodic - Timer callback to queue statistics
|
|
+ *
|
|
+ * This callback is provided in order to queue the statistics_work
|
|
+ * in work_queue context (v. softirq)
|
|
+ *
|
|
+ * This timer function is continually reset to execute within
|
|
+ * REG_RECALIB_PERIOD seconds since the last STATISTICS_NOTIFICATION
|
|
+ * was received. We need to ensure we receive the statistics in order
|
|
+ * to update the temperature used for calibrating the TXPOWER. However,
|
|
+ * we can't send the statistics command from softirq context (which
|
|
+ * is the context which timers run at) so we have to queue off the
|
|
+ * statistics_work to actually send the command to the hardware.
|
|
+ */
|
|
+static void iwl4965_bg_statistics_periodic(unsigned long data)
|
|
+{
|
|
+ struct iwl_priv *priv = (struct iwl_priv *)data;
|
|
+
|
|
+ queue_work(priv->workqueue, &priv->statistics_work);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl4965_bg_statistics_work - Send the statistics request to the hardware.
|
|
+ *
|
|
+ * This is queued by iwl_bg_statistics_periodic.
|
|
+ */
|
|
+static void iwl4965_bg_statistics_work(struct work_struct *work)
|
|
+{
|
|
+ struct iwl_priv *priv = container_of(work, struct iwl_priv,
|
|
+ statistics_work);
|
|
+
|
|
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
|
+ return;
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+ iwl_send_statistics_request(priv);
|
|
+ mutex_unlock(&priv->mutex);
|
|
+}
|
|
+
|
|
+#define CT_LIMIT_CONST 259
|
|
+#define TM_CT_KILL_THRESHOLD 110
|
|
+
|
|
+void iwl4965_rf_kill_ct_config(struct iwl_priv *priv)
|
|
+{
|
|
+ struct iwl_ct_kill_config cmd;
|
|
+ u32 R1, R2, R3;
|
|
+ u32 temp_th;
|
|
+ u32 crit_temperature;
|
|
+ unsigned long flags;
|
|
+ int rc = 0;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR,
|
|
+ CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ if (priv->statistics.flag & STATISTICS_REPLY_FLG_FAT_MODE_MSK) {
|
|
+ R1 = (s32)le32_to_cpu(priv->card_alive_init.therm_r1[1]);
|
|
+ R2 = (s32)le32_to_cpu(priv->card_alive_init.therm_r2[1]);
|
|
+ R3 = (s32)le32_to_cpu(priv->card_alive_init.therm_r3[1]);
|
|
+ } else {
|
|
+ R1 = (s32)le32_to_cpu(priv->card_alive_init.therm_r1[0]);
|
|
+ R2 = (s32)le32_to_cpu(priv->card_alive_init.therm_r2[0]);
|
|
+ R3 = (s32)le32_to_cpu(priv->card_alive_init.therm_r3[0]);
|
|
+ }
|
|
+
|
|
+ temp_th = CELSIUS_TO_KELVIN(TM_CT_KILL_THRESHOLD);
|
|
+
|
|
+ crit_temperature = ((temp_th * (R3-R1))/CT_LIMIT_CONST) + R2;
|
|
+ cmd.critical_temperature_R = cpu_to_le32(crit_temperature);
|
|
+ rc = iwl_send_cmd_pdu(priv,
|
|
+ REPLY_CT_KILL_CONFIG_CMD, sizeof(cmd), &cmd);
|
|
+ if (rc)
|
|
+ IWL_ERROR("REPLY_CT_KILL_CONFIG_CMD failed\n");
|
|
+ else
|
|
+ IWL_DEBUG_INFO("REPLY_CT_KILL_CONFIG_CMD succeeded\n");
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_SENSITIVITY
|
|
+
|
|
+/* "false alarms" are signals that our DSP tries to lock onto,
|
|
+ * but then determines that they are either noise, or transmissions
|
|
+ * from a distant wireless network (also "noise", really) that get
|
|
+ * "stepped on" by stronger transmissions within our own network.
|
|
+ * This algorithm attempts to set a sensitivity level that is high
|
|
+ * enough to receive all of our own network traffic, but not so
|
|
+ * high that our DSP gets too busy trying to lock onto non-network
|
|
+ * activity/noise. */
|
|
+static int iwl4965_sens_energy_cck(struct iwl_priv *priv,
|
|
+ u32 norm_fa,
|
|
+ u32 rx_enable_time,
|
|
+ struct statistics_general_data *rx_info)
|
|
+{
|
|
+ u32 max_nrg_cck = 0;
|
|
+ int i = 0;
|
|
+ u8 max_silence_rssi = 0;
|
|
+ u32 silence_ref = 0;
|
|
+ u8 silence_rssi_a = 0;
|
|
+ u8 silence_rssi_b = 0;
|
|
+ u8 silence_rssi_c = 0;
|
|
+ u32 val;
|
|
+
|
|
+ /* "false_alarms" values below are cross-multiplications to assess the
|
|
+ * numbers of false alarms within the measured period of actual Rx
|
|
+ * (Rx is off when we're txing), vs the min/max expected false alarms
|
|
+ * (some should be expected if rx is sensitive enough) in a
|
|
+ * hypothetical listening period of 200 time units (TU), 204.8 msec:
|
|
+ *
|
|
+ * MIN_FA/fixed-time < false_alarms/actual-rx-time < MAX_FA/beacon-time
|
|
+ *
|
|
+ * */
|
|
+ u32 false_alarms = norm_fa * 200 * 1024;
|
|
+ u32 max_false_alarms = MAX_FA_CCK * rx_enable_time;
|
|
+ u32 min_false_alarms = MIN_FA_CCK * rx_enable_time;
|
|
+ struct iwl_sensitivity_data *data = NULL;
|
|
+
|
|
+ data = &(priv->sensitivity_data);
|
|
+
|
|
+ data->nrg_auto_corr_silence_diff = 0;
|
|
+
|
|
+ /* Find max silence rssi among all 3 receivers.
|
|
+ * This is background noise, which may include transmissions from other
|
|
+ * networks, measured during silence before our network's beacon */
|
|
+ silence_rssi_a = (u8)((rx_info->beacon_silence_rssi_a &
|
|
+ ALL_BAND_FILTER)>>8);
|
|
+ silence_rssi_b = (u8)((rx_info->beacon_silence_rssi_b &
|
|
+ ALL_BAND_FILTER)>>8);
|
|
+ silence_rssi_c = (u8)((rx_info->beacon_silence_rssi_c &
|
|
+ ALL_BAND_FILTER)>>8);
|
|
+
|
|
+ val = max(silence_rssi_b, silence_rssi_c);
|
|
+ max_silence_rssi = max(silence_rssi_a, (u8) val);
|
|
+
|
|
+ /* Store silence rssi in 20-beacon history table */
|
|
+ data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi;
|
|
+ data->nrg_silence_idx++;
|
|
+ if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L)
|
|
+ data->nrg_silence_idx = 0;
|
|
+
|
|
+ /* Find max silence rssi across 20 beacon history */
|
|
+ for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) {
|
|
+ val = data->nrg_silence_rssi[i];
|
|
+ silence_ref = max(silence_ref, val);
|
|
+ }
|
|
+ IWL_DEBUG_CALIB("silence a %u, b %u, c %u, 20-bcn max %u\n",
|
|
+ silence_rssi_a, silence_rssi_b, silence_rssi_c,
|
|
+ silence_ref);
|
|
+
|
|
+ /* Find max rx energy (min value!) among all 3 receivers,
|
|
+ * measured during beacon frame.
|
|
+ * Save it in 10-beacon history table. */
|
|
+ i = data->nrg_energy_idx;
|
|
+ val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c);
|
|
+ data->nrg_value[i] = min(rx_info->beacon_energy_a, val);
|
|
+
|
|
+ data->nrg_energy_idx++;
|
|
+ if (data->nrg_energy_idx >= 10)
|
|
+ data->nrg_energy_idx = 0;
|
|
+
|
|
+ /* Find min rx energy (max value) across 10 beacon history.
|
|
+ * This is the minimum signal level that we want to receive well.
|
|
+ * Add backoff (margin so we don't miss slightly lower energy frames).
|
|
+ * This establishes an upper bound (min value) for energy threshold. */
|
|
+ max_nrg_cck = data->nrg_value[0];
|
|
+ for (i = 1; i < 10; i++)
|
|
+ max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i]));
|
|
+ max_nrg_cck += 6;
|
|
+
|
|
+ IWL_DEBUG_CALIB("rx energy a %u, b %u, c %u, 10-bcn max/min %u\n",
|
|
+ rx_info->beacon_energy_a, rx_info->beacon_energy_b,
|
|
+ rx_info->beacon_energy_c, max_nrg_cck - 6);
|
|
+
|
|
+ /* Count number of consecutive beacons with fewer-than-desired
|
|
+ * false alarms. */
|
|
+ if (false_alarms < min_false_alarms)
|
|
+ data->num_in_cck_no_fa++;
|
|
+ else
|
|
+ data->num_in_cck_no_fa = 0;
|
|
+ IWL_DEBUG_CALIB("consecutive bcns with few false alarms = %u\n",
|
|
+ data->num_in_cck_no_fa);
|
|
+
|
|
+ /* If we got too many false alarms this time, reduce sensitivity */
|
|
+ if (false_alarms > max_false_alarms) {
|
|
+ IWL_DEBUG_CALIB("norm FA %u > max FA %u\n",
|
|
+ false_alarms, max_false_alarms);
|
|
+ IWL_DEBUG_CALIB("... reducing sensitivity\n");
|
|
+ data->nrg_curr_state = IWL_FA_TOO_MANY;
|
|
+
|
|
+ if (data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK) {
|
|
+ /* Store for "fewer than desired" on later beacon */
|
|
+ data->nrg_silence_ref = silence_ref;
|
|
+
|
|
+ /* increase energy threshold (reduce nrg value)
|
|
+ * to decrease sensitivity */
|
|
+ if (data->nrg_th_cck > (NRG_MAX_CCK + NRG_STEP_CCK))
|
|
+ data->nrg_th_cck = data->nrg_th_cck
|
|
+ - NRG_STEP_CCK;
|
|
+ }
|
|
+
|
|
+ /* increase auto_corr values to decrease sensitivity */
|
|
+ if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK)
|
|
+ data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1;
|
|
+ else {
|
|
+ val = data->auto_corr_cck + AUTO_CORR_STEP_CCK;
|
|
+ data->auto_corr_cck = min((u32)AUTO_CORR_MAX_CCK, val);
|
|
+ }
|
|
+ val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK;
|
|
+ data->auto_corr_cck_mrc = min((u32)AUTO_CORR_MAX_CCK_MRC, val);
|
|
+
|
|
+ /* Else if we got fewer than desired, increase sensitivity */
|
|
+ } else if (false_alarms < min_false_alarms) {
|
|
+ data->nrg_curr_state = IWL_FA_TOO_FEW;
|
|
+
|
|
+ /* Compare silence level with silence level for most recent
|
|
+ * healthy number or too many false alarms */
|
|
+ data->nrg_auto_corr_silence_diff = (s32)data->nrg_silence_ref -
|
|
+ (s32)silence_ref;
|
|
+
|
|
+ IWL_DEBUG_CALIB("norm FA %u < min FA %u, silence diff %d\n",
|
|
+ false_alarms, min_false_alarms,
|
|
+ data->nrg_auto_corr_silence_diff);
|
|
+
|
|
+ /* Increase value to increase sensitivity, but only if:
|
|
+ * 1a) previous beacon did *not* have *too many* false alarms
|
|
+ * 1b) AND there's a significant difference in Rx levels
|
|
+ * from a previous beacon with too many, or healthy # FAs
|
|
+ * OR 2) We've seen a lot of beacons (100) with too few
|
|
+ * false alarms */
|
|
+ if ((data->nrg_prev_state != IWL_FA_TOO_MANY) &&
|
|
+ ((data->nrg_auto_corr_silence_diff > NRG_DIFF) ||
|
|
+ (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) {
|
|
+
|
|
+ IWL_DEBUG_CALIB("... increasing sensitivity\n");
|
|
+ /* Increase nrg value to increase sensitivity */
|
|
+ val = data->nrg_th_cck + NRG_STEP_CCK;
|
|
+ data->nrg_th_cck = min((u32)NRG_MIN_CCK, val);
|
|
+
|
|
+ /* Decrease auto_corr values to increase sensitivity */
|
|
+ val = data->auto_corr_cck - AUTO_CORR_STEP_CCK;
|
|
+ data->auto_corr_cck = max((u32)AUTO_CORR_MIN_CCK, val);
|
|
+
|
|
+ val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK;
|
|
+ data->auto_corr_cck_mrc =
|
|
+ max((u32)AUTO_CORR_MIN_CCK_MRC, val);
|
|
+
|
|
+ } else
|
|
+ IWL_DEBUG_CALIB("... but not changing sensitivity\n");
|
|
+
|
|
+ /* Else we got a healthy number of false alarms, keep status quo */
|
|
+ } else {
|
|
+ IWL_DEBUG_CALIB(" FA in safe zone\n");
|
|
+ data->nrg_curr_state = IWL_FA_GOOD_RANGE;
|
|
+
|
|
+ /* Store for use in "fewer than desired" with later beacon */
|
|
+ data->nrg_silence_ref = silence_ref;
|
|
+
|
|
+ /* If previous beacon had too many false alarms,
|
|
+ * give it some extra margin by reducing sensitivity again
|
|
+ * (but don't go below measured energy of desired Rx) */
|
|
+ if (IWL_FA_TOO_MANY == data->nrg_prev_state) {
|
|
+ IWL_DEBUG_CALIB("... increasing margin\n");
|
|
+ data->nrg_th_cck -= NRG_MARGIN;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Make sure the energy threshold does not go above the measured
|
|
+ * energy of the desired Rx signals (reduced by backoff margin),
|
|
+ * or else we might start missing Rx frames.
|
|
+ * Lower value is higher energy, so we use max()!
|
|
+ */
|
|
+ data->nrg_th_cck = max(max_nrg_cck, data->nrg_th_cck);
|
|
+ IWL_DEBUG_CALIB("new nrg_th_cck %u\n", data->nrg_th_cck);
|
|
+
|
|
+ data->nrg_prev_state = data->nrg_curr_state;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static int iwl4965_sens_auto_corr_ofdm(struct iwl_priv *priv,
|
|
+ u32 norm_fa,
|
|
+ u32 rx_enable_time)
|
|
+{
|
|
+ u32 val;
|
|
+ u32 false_alarms = norm_fa * 200 * 1024;
|
|
+ u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time;
|
|
+ u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time;
|
|
+ struct iwl_sensitivity_data *data = NULL;
|
|
+
|
|
+ data = &(priv->sensitivity_data);
|
|
+
|
|
+ /* If we got too many false alarms this time, reduce sensitivity */
|
|
+ if (false_alarms > max_false_alarms) {
|
|
+
|
|
+ IWL_DEBUG_CALIB("norm FA %u > max FA %u)\n",
|
|
+ false_alarms, max_false_alarms);
|
|
+
|
|
+ val = data->auto_corr_ofdm + AUTO_CORR_STEP_OFDM;
|
|
+ data->auto_corr_ofdm =
|
|
+ min((u32)AUTO_CORR_MAX_OFDM, val);
|
|
+
|
|
+ val = data->auto_corr_ofdm_mrc + AUTO_CORR_STEP_OFDM;
|
|
+ data->auto_corr_ofdm_mrc =
|
|
+ min((u32)AUTO_CORR_MAX_OFDM_MRC, val);
|
|
+
|
|
+ val = data->auto_corr_ofdm_x1 + AUTO_CORR_STEP_OFDM;
|
|
+ data->auto_corr_ofdm_x1 =
|
|
+ min((u32)AUTO_CORR_MAX_OFDM_X1, val);
|
|
+
|
|
+ val = data->auto_corr_ofdm_mrc_x1 + AUTO_CORR_STEP_OFDM;
|
|
+ data->auto_corr_ofdm_mrc_x1 =
|
|
+ min((u32)AUTO_CORR_MAX_OFDM_MRC_X1, val);
|
|
+ }
|
|
+
|
|
+ /* Else if we got fewer than desired, increase sensitivity */
|
|
+ else if (false_alarms < min_false_alarms) {
|
|
+
|
|
+ IWL_DEBUG_CALIB("norm FA %u < min FA %u\n",
|
|
+ false_alarms, min_false_alarms);
|
|
+
|
|
+ val = data->auto_corr_ofdm - AUTO_CORR_STEP_OFDM;
|
|
+ data->auto_corr_ofdm =
|
|
+ max((u32)AUTO_CORR_MIN_OFDM, val);
|
|
+
|
|
+ val = data->auto_corr_ofdm_mrc - AUTO_CORR_STEP_OFDM;
|
|
+ data->auto_corr_ofdm_mrc =
|
|
+ max((u32)AUTO_CORR_MIN_OFDM_MRC, val);
|
|
+
|
|
+ val = data->auto_corr_ofdm_x1 - AUTO_CORR_STEP_OFDM;
|
|
+ data->auto_corr_ofdm_x1 =
|
|
+ max((u32)AUTO_CORR_MIN_OFDM_X1, val);
|
|
+
|
|
+ val = data->auto_corr_ofdm_mrc_x1 - AUTO_CORR_STEP_OFDM;
|
|
+ data->auto_corr_ofdm_mrc_x1 =
|
|
+ max((u32)AUTO_CORR_MIN_OFDM_MRC_X1, val);
|
|
+ }
|
|
+
|
|
+ else
|
|
+ IWL_DEBUG_CALIB("min FA %u < norm FA %u < max FA %u OK\n",
|
|
+ min_false_alarms, false_alarms, max_false_alarms);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int iwl_sensitivity_callback(struct iwl_priv *priv,
|
|
+ struct iwl_cmd *cmd, struct sk_buff *skb)
|
|
+{
|
|
+ /* We didn't cache the SKB; let the caller free it */
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */
|
|
+static int iwl4965_sensitivity_write(struct iwl_priv *priv, u8 flags)
|
|
+{
|
|
+ int rc = 0;
|
|
+ struct iwl_sensitivity_cmd cmd ;
|
|
+ struct iwl_sensitivity_data *data = NULL;
|
|
+ struct iwl_host_cmd cmd_out = {
|
|
+ .id = SENSITIVITY_CMD,
|
|
+ .len = sizeof(struct iwl_sensitivity_cmd),
|
|
+ .meta.flags = flags,
|
|
+ .data = &cmd,
|
|
+ };
|
|
+
|
|
+ data = &(priv->sensitivity_data);
|
|
+
|
|
+ memset(&cmd, 0, sizeof(cmd));
|
|
+
|
|
+ cmd.table[HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX] =
|
|
+ cpu_to_le16((u16)data->auto_corr_ofdm);
|
|
+ cmd.table[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX] =
|
|
+ cpu_to_le16((u16)data->auto_corr_ofdm_mrc);
|
|
+ cmd.table[HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX] =
|
|
+ cpu_to_le16((u16)data->auto_corr_ofdm_x1);
|
|
+ cmd.table[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX] =
|
|
+ cpu_to_le16((u16)data->auto_corr_ofdm_mrc_x1);
|
|
+
|
|
+ cmd.table[HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX] =
|
|
+ cpu_to_le16((u16)data->auto_corr_cck);
|
|
+ cmd.table[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX] =
|
|
+ cpu_to_le16((u16)data->auto_corr_cck_mrc);
|
|
+
|
|
+ cmd.table[HD_MIN_ENERGY_CCK_DET_INDEX] =
|
|
+ cpu_to_le16((u16)data->nrg_th_cck);
|
|
+ cmd.table[HD_MIN_ENERGY_OFDM_DET_INDEX] =
|
|
+ cpu_to_le16((u16)data->nrg_th_ofdm);
|
|
+
|
|
+ cmd.table[HD_BARKER_CORR_TH_ADD_MIN_INDEX] =
|
|
+ __constant_cpu_to_le16(190);
|
|
+ cmd.table[HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX] =
|
|
+ __constant_cpu_to_le16(390);
|
|
+ cmd.table[HD_OFDM_ENERGY_TH_IN_INDEX] =
|
|
+ __constant_cpu_to_le16(62);
|
|
+
|
|
+ IWL_DEBUG_CALIB("ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n",
|
|
+ data->auto_corr_ofdm, data->auto_corr_ofdm_mrc,
|
|
+ data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1,
|
|
+ data->nrg_th_ofdm);
|
|
+
|
|
+ IWL_DEBUG_CALIB("cck: ac %u mrc %u thresh %u\n",
|
|
+ data->auto_corr_cck, data->auto_corr_cck_mrc,
|
|
+ data->nrg_th_cck);
|
|
+
|
|
+ cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE;
|
|
+
|
|
+ if (flags & CMD_ASYNC)
|
|
+ cmd_out.meta.u.callback = iwl_sensitivity_callback;
|
|
+
|
|
+ /* Don't send command to uCode if nothing has changed */
|
|
+ if (!memcmp(&cmd.table[0], &(priv->sensitivity_tbl[0]),
|
|
+ sizeof(u16)*HD_TABLE_SIZE)) {
|
|
+ IWL_DEBUG_CALIB("No change in SENSITIVITY_CMD\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* Copy table for comparison next time */
|
|
+ memcpy(&(priv->sensitivity_tbl[0]), &(cmd.table[0]),
|
|
+ sizeof(u16)*HD_TABLE_SIZE);
|
|
+
|
|
+ rc = iwl_send_cmd(priv, &cmd_out);
|
|
+ if (!rc) {
|
|
+ IWL_DEBUG_CALIB("SENSITIVITY_CMD succeeded\n");
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void iwl4965_init_sensitivity(struct iwl_priv *priv, u8 flags, u8 force)
|
|
+{
|
|
+ int rc = 0;
|
|
+ int i;
|
|
+ struct iwl_sensitivity_data *data = NULL;
|
|
+
|
|
+ IWL_DEBUG_CALIB("Start iwl4965_init_sensitivity\n");
|
|
+
|
|
+ if (force)
|
|
+ memset(&(priv->sensitivity_tbl[0]), 0,
|
|
+ sizeof(u16)*HD_TABLE_SIZE);
|
|
+
|
|
+ /* Clear driver's sensitivity algo data */
|
|
+ data = &(priv->sensitivity_data);
|
|
+ memset(data, 0, sizeof(struct iwl_sensitivity_data));
|
|
+
|
|
+ data->num_in_cck_no_fa = 0;
|
|
+ data->nrg_curr_state = IWL_FA_TOO_MANY;
|
|
+ data->nrg_prev_state = IWL_FA_TOO_MANY;
|
|
+ data->nrg_silence_ref = 0;
|
|
+ data->nrg_silence_idx = 0;
|
|
+ data->nrg_energy_idx = 0;
|
|
+
|
|
+ for (i = 0; i < 10; i++)
|
|
+ data->nrg_value[i] = 0;
|
|
+
|
|
+ for (i = 0; i < NRG_NUM_PREV_STAT_L; i++)
|
|
+ data->nrg_silence_rssi[i] = 0;
|
|
+
|
|
+ data->auto_corr_ofdm = 90;
|
|
+ data->auto_corr_ofdm_mrc = 170;
|
|
+ data->auto_corr_ofdm_x1 = 105;
|
|
+ data->auto_corr_ofdm_mrc_x1 = 220;
|
|
+ data->auto_corr_cck = AUTO_CORR_CCK_MIN_VAL_DEF;
|
|
+ data->auto_corr_cck_mrc = 200;
|
|
+ data->nrg_th_cck = 100;
|
|
+ data->nrg_th_ofdm = 100;
|
|
+
|
|
+ data->last_bad_plcp_cnt_ofdm = 0;
|
|
+ data->last_fa_cnt_ofdm = 0;
|
|
+ data->last_bad_plcp_cnt_cck = 0;
|
|
+ data->last_fa_cnt_cck = 0;
|
|
+
|
|
+ /* Clear prior Sensitivity command data to force send to uCode */
|
|
+ if (force)
|
|
+ memset(&(priv->sensitivity_tbl[0]), 0,
|
|
+ sizeof(u16)*HD_TABLE_SIZE);
|
|
+
|
|
+ rc |= iwl4965_sensitivity_write(priv, flags);
|
|
+ IWL_DEBUG_CALIB("<<return 0x%X\n", rc);
|
|
+
|
|
+ return;
|
|
+}
|
|
+
|
|
+
|
|
+/* Reset differential Rx gains in NIC to prepare for chain noise calibration.
|
|
+ * Called after every association, but this runs only once!
|
|
+ * ... once chain noise is calibrated the first time, it's good forever. */
|
|
+void iwl4965_chain_noise_reset(struct iwl_priv *priv)
|
|
+{
|
|
+ struct iwl_chain_noise_data *data = NULL;
|
|
+ int rc = 0;
|
|
+
|
|
+ data = &(priv->chain_noise_data);
|
|
+ if ((data->state == IWL_CHAIN_NOISE_ALIVE) && iwl_is_associated(priv)) {
|
|
+ struct iwl_calibration_cmd cmd;
|
|
+
|
|
+ memset(&cmd, 0, sizeof(cmd));
|
|
+ cmd.opCode = PHY_CALIBRATE_DIFF_GAIN_CMD;
|
|
+ cmd.diff_gain_a = 0;
|
|
+ cmd.diff_gain_b = 0;
|
|
+ cmd.diff_gain_c = 0;
|
|
+ rc = iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
|
|
+ sizeof(cmd), &cmd);
|
|
+ msleep(4);
|
|
+ data->state = IWL_CHAIN_NOISE_ACCUMULATE;
|
|
+ IWL_DEBUG_CALIB("Run chain_noise_calibrate\n");
|
|
+ }
|
|
+ return;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Accumulate 20 beacons of signal and noise statistics for each of
|
|
+ * 3 receivers/antennas/rx-chains, then figure out:
|
|
+ * 1) Which antennas are connected.
|
|
+ * 2) Differential rx gain settings to balance the 3 receivers.
|
|
+ */
|
|
+static void iwl4965_noise_calibration(struct iwl_priv *priv,
|
|
+ struct iwl_notif_statistics *stat_resp)
|
|
+{
|
|
+ struct iwl_chain_noise_data *data = NULL;
|
|
+ int rc = 0;
|
|
+
|
|
+ u32 chain_noise_a;
|
|
+ u32 chain_noise_b;
|
|
+ u32 chain_noise_c;
|
|
+ u32 chain_sig_a;
|
|
+ u32 chain_sig_b;
|
|
+ u32 chain_sig_c;
|
|
+ u32 average_sig[NUM_RX_CHAINS] = {INITIALIZATION_VALUE};
|
|
+ u32 average_noise[NUM_RX_CHAINS] = {INITIALIZATION_VALUE};
|
|
+ u32 max_average_sig;
|
|
+ u16 max_average_sig_antenna_i;
|
|
+ u32 min_average_noise = MIN_AVERAGE_NOISE_MAX_VALUE;
|
|
+ u16 min_average_noise_antenna_i = INITIALIZATION_VALUE;
|
|
+ u16 i = 0;
|
|
+ u16 chan_num = INITIALIZATION_VALUE;
|
|
+ u32 band = INITIALIZATION_VALUE;
|
|
+ u32 active_chains = 0;
|
|
+ unsigned long flags;
|
|
+ struct statistics_rx_non_phy *rx_info = &(stat_resp->rx.general);
|
|
+
|
|
+ data = &(priv->chain_noise_data);
|
|
+
|
|
+ /* Accumulate just the first 20 beacons after the first association,
|
|
+ * then we're done forever. */
|
|
+ if (data->state != IWL_CHAIN_NOISE_ACCUMULATE) {
|
|
+ if (data->state == IWL_CHAIN_NOISE_ALIVE)
|
|
+ IWL_DEBUG_CALIB("Wait for noise calib reset\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) {
|
|
+ IWL_DEBUG_CALIB(" << Interference data unavailable\n");
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ band = (priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK) ? 0 : 1;
|
|
+ chan_num = le16_to_cpu(priv->staging_rxon.channel);
|
|
+
|
|
+ /* Make sure we accumulate data for just the associated channel
|
|
+ * (even if scanning). */
|
|
+ if ((chan_num != (le32_to_cpu(stat_resp->flag) >> 16)) ||
|
|
+ ((STATISTICS_REPLY_FLG_BAND_24G_MSK ==
|
|
+ (stat_resp->flag & STATISTICS_REPLY_FLG_BAND_24G_MSK)) && band)) {
|
|
+ IWL_DEBUG_CALIB("Stats not from chan=%d, band=%d\n",
|
|
+ chan_num, band);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Accumulate beacon statistics values across 20 beacons */
|
|
+ chain_noise_a = le32_to_cpu(rx_info->beacon_silence_rssi_a) &
|
|
+ IN_BAND_FILTER;
|
|
+ chain_noise_b = le32_to_cpu(rx_info->beacon_silence_rssi_b) &
|
|
+ IN_BAND_FILTER;
|
|
+ chain_noise_c = le32_to_cpu(rx_info->beacon_silence_rssi_c) &
|
|
+ IN_BAND_FILTER;
|
|
+
|
|
+ chain_sig_a = le32_to_cpu(rx_info->beacon_rssi_a) & IN_BAND_FILTER;
|
|
+ chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER;
|
|
+ chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER;
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ data->beacon_count++;
|
|
+
|
|
+ data->chain_noise_a = (chain_noise_a + data->chain_noise_a);
|
|
+ data->chain_noise_b = (chain_noise_b + data->chain_noise_b);
|
|
+ data->chain_noise_c = (chain_noise_c + data->chain_noise_c);
|
|
+
|
|
+ data->chain_signal_a = (chain_sig_a + data->chain_signal_a);
|
|
+ data->chain_signal_b = (chain_sig_b + data->chain_signal_b);
|
|
+ data->chain_signal_c = (chain_sig_c + data->chain_signal_c);
|
|
+
|
|
+ IWL_DEBUG_CALIB("chan=%d, band=%d, beacon=%d\n", chan_num, band,
|
|
+ data->beacon_count);
|
|
+ IWL_DEBUG_CALIB("chain_sig: a %d b %d c %d\n",
|
|
+ chain_sig_a, chain_sig_b, chain_sig_c);
|
|
+ IWL_DEBUG_CALIB("chain_noise: a %d b %d c %d\n",
|
|
+ chain_noise_a, chain_noise_b, chain_noise_c);
|
|
+
|
|
+ /* If this is the 20th beacon, determine:
|
|
+ * 1) Disconnected antennas (using signal strengths)
|
|
+ * 2) Differential gain (using silence noise) to balance receivers */
|
|
+ if (data->beacon_count == CAL_NUM_OF_BEACONS) {
|
|
+
|
|
+ /* Analyze signal for disconnected antenna */
|
|
+ average_sig[0] = (data->chain_signal_a) / CAL_NUM_OF_BEACONS;
|
|
+ average_sig[1] = (data->chain_signal_b) / CAL_NUM_OF_BEACONS;
|
|
+ average_sig[2] = (data->chain_signal_c) / CAL_NUM_OF_BEACONS;
|
|
+
|
|
+ if (average_sig[0] >= average_sig[1]) {
|
|
+ max_average_sig = average_sig[0];
|
|
+ max_average_sig_antenna_i = 0;
|
|
+ active_chains = (1 << max_average_sig_antenna_i);
|
|
+ } else {
|
|
+ max_average_sig = average_sig[1];
|
|
+ max_average_sig_antenna_i = 1;
|
|
+ active_chains = (1 << max_average_sig_antenna_i);
|
|
+ }
|
|
+
|
|
+ if (average_sig[2] >= max_average_sig) {
|
|
+ max_average_sig = average_sig[2];
|
|
+ max_average_sig_antenna_i = 2;
|
|
+ active_chains = (1 << max_average_sig_antenna_i);
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_CALIB("average_sig: a %d b %d c %d\n",
|
|
+ average_sig[0], average_sig[1], average_sig[2]);
|
|
+ IWL_DEBUG_CALIB("max_average_sig = %d, antenna %d\n",
|
|
+ max_average_sig, max_average_sig_antenna_i);
|
|
+
|
|
+ /* Compare signal strengths for all 3 receivers. */
|
|
+ for (i = 0; i < NUM_RX_CHAINS; i++) {
|
|
+ if (i != max_average_sig_antenna_i) {
|
|
+ s32 rssi_delta = (max_average_sig -
|
|
+ average_sig[i]);
|
|
+
|
|
+ /* If signal is very weak, compared with
|
|
+ * strongest, mark it as disconnected. */
|
|
+ if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS)
|
|
+ data->disconn_array[i] = 1;
|
|
+ else
|
|
+ active_chains |= (1 << i);
|
|
+ IWL_DEBUG_CALIB("i = %d rssiDelta = %d "
|
|
+ "disconn_array[i] = %d\n",
|
|
+ i, rssi_delta, data->disconn_array[i]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /*If both chains A & B are disconnected -
|
|
+ * connect B and leave A as is */
|
|
+ if (data->disconn_array[CHAIN_A] &&
|
|
+ data->disconn_array[CHAIN_B]) {
|
|
+ data->disconn_array[CHAIN_B] = 0;
|
|
+ active_chains |= (1 << CHAIN_B);
|
|
+ IWL_DEBUG_CALIB("both A & B chains are disconnected! "
|
|
+ "W/A - declare B as connected\n");
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_CALIB("active_chains (bitwise) = 0x%x\n",
|
|
+ active_chains);
|
|
+
|
|
+ /* Save for use within RXON, TX, SCAN commands, etc. */
|
|
+ priv->valid_antenna = active_chains;
|
|
+
|
|
+ /* Analyze noise for rx balance */
|
|
+ average_noise[0] = ((data->chain_noise_a)/CAL_NUM_OF_BEACONS);
|
|
+ average_noise[1] = ((data->chain_noise_b)/CAL_NUM_OF_BEACONS);
|
|
+ average_noise[2] = ((data->chain_noise_c)/CAL_NUM_OF_BEACONS);
|
|
+
|
|
+ for (i = 0; i < NUM_RX_CHAINS; i++) {
|
|
+ if (!(data->disconn_array[i]) &&
|
|
+ (average_noise[i] <= min_average_noise)) {
|
|
+ /* This means that chain i is active and has
|
|
+ * lower noise values so far: */
|
|
+ min_average_noise = average_noise[i];
|
|
+ min_average_noise_antenna_i = i;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ data->delta_gain_code[min_average_noise_antenna_i] = 0;
|
|
+
|
|
+ IWL_DEBUG_CALIB("average_noise: a %d b %d c %d\n",
|
|
+ average_noise[0], average_noise[1],
|
|
+ average_noise[2]);
|
|
+
|
|
+ IWL_DEBUG_CALIB("min_average_noise = %d, antenna %d\n",
|
|
+ min_average_noise, min_average_noise_antenna_i);
|
|
+
|
|
+ for (i = 0; i < NUM_RX_CHAINS; i++) {
|
|
+ s32 delta_g = 0;
|
|
+
|
|
+ if (!(data->disconn_array[i]) &&
|
|
+ (data->delta_gain_code[i] ==
|
|
+ CHAIN_NOISE_DELTA_GAIN_INIT_VAL)) {
|
|
+ delta_g = average_noise[i] - min_average_noise;
|
|
+ data->delta_gain_code[i] = (u8)((delta_g *
|
|
+ 10) / 15);
|
|
+ if (CHAIN_NOISE_MAX_DELTA_GAIN_CODE <
|
|
+ data->delta_gain_code[i])
|
|
+ data->delta_gain_code[i] =
|
|
+ CHAIN_NOISE_MAX_DELTA_GAIN_CODE;
|
|
+
|
|
+ data->delta_gain_code[i] =
|
|
+ (data->delta_gain_code[i] | (1 << 2));
|
|
+ } else
|
|
+ data->delta_gain_code[i] = 0;
|
|
+ }
|
|
+ IWL_DEBUG_CALIB("delta_gain_codes: a %d b %d c %d\n",
|
|
+ data->delta_gain_code[0],
|
|
+ data->delta_gain_code[1],
|
|
+ data->delta_gain_code[2]);
|
|
+
|
|
+ /* Differential gain gets sent to uCode only once */
|
|
+ if (!data->radio_write) {
|
|
+ struct iwl_calibration_cmd cmd;
|
|
+ data->radio_write = 1;
|
|
+
|
|
+ memset(&cmd, 0, sizeof(cmd));
|
|
+ cmd.opCode = PHY_CALIBRATE_DIFF_GAIN_CMD;
|
|
+ cmd.diff_gain_a = data->delta_gain_code[0];
|
|
+ cmd.diff_gain_b = data->delta_gain_code[1];
|
|
+ cmd.diff_gain_c = data->delta_gain_code[2];
|
|
+ rc = iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
|
|
+ sizeof(cmd), &cmd);
|
|
+ if (rc)
|
|
+ IWL_DEBUG_CALIB("fail sending cmd "
|
|
+ "REPLY_PHY_CALIBRATION_CMD \n");
|
|
+
|
|
+ /* TODO we might want recalculate
|
|
+ * rx_chain in rxon cmd */
|
|
+
|
|
+ /* Mark so we run this algo only once! */
|
|
+ data->state = IWL_CHAIN_NOISE_CALIBRATED;
|
|
+ }
|
|
+ data->chain_noise_a = 0;
|
|
+ data->chain_noise_b = 0;
|
|
+ data->chain_noise_c = 0;
|
|
+ data->chain_signal_a = 0;
|
|
+ data->chain_signal_b = 0;
|
|
+ data->chain_signal_c = 0;
|
|
+ data->beacon_count = 0;
|
|
+ }
|
|
+ return;
|
|
+}
|
|
+
|
|
+static void iwl4965_sensitivity_calibration(struct iwl_priv *priv,
|
|
+ struct iwl_notif_statistics *resp)
|
|
+{
|
|
+ int rc = 0;
|
|
+ u32 rx_enable_time;
|
|
+ u32 fa_cck;
|
|
+ u32 fa_ofdm;
|
|
+ u32 bad_plcp_cck;
|
|
+ u32 bad_plcp_ofdm;
|
|
+ u32 norm_fa_ofdm;
|
|
+ u32 norm_fa_cck;
|
|
+ struct iwl_sensitivity_data *data = NULL;
|
|
+ struct statistics_rx_non_phy *rx_info = &(resp->rx.general);
|
|
+ struct statistics_rx *statistics = &(resp->rx);
|
|
+ unsigned long flags;
|
|
+ struct statistics_general_data statis;
|
|
+
|
|
+ data = &(priv->sensitivity_data);
|
|
+
|
|
+ if (!iwl_is_associated(priv)) {
|
|
+ IWL_DEBUG_CALIB("<< - not associated\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) {
|
|
+ IWL_DEBUG_CALIB("<< invalid data.\n");
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Extract Statistics: */
|
|
+ rx_enable_time = le32_to_cpu(rx_info->channel_load);
|
|
+ fa_cck = le32_to_cpu(statistics->cck.false_alarm_cnt);
|
|
+ fa_ofdm = le32_to_cpu(statistics->ofdm.false_alarm_cnt);
|
|
+ bad_plcp_cck = le32_to_cpu(statistics->cck.plcp_err);
|
|
+ bad_plcp_ofdm = le32_to_cpu(statistics->ofdm.plcp_err);
|
|
+
|
|
+ statis.beacon_silence_rssi_a =
|
|
+ le32_to_cpu(statistics->general.beacon_silence_rssi_a);
|
|
+ statis.beacon_silence_rssi_b =
|
|
+ le32_to_cpu(statistics->general.beacon_silence_rssi_b);
|
|
+ statis.beacon_silence_rssi_c =
|
|
+ le32_to_cpu(statistics->general.beacon_silence_rssi_c);
|
|
+ statis.beacon_energy_a =
|
|
+ le32_to_cpu(statistics->general.beacon_energy_a);
|
|
+ statis.beacon_energy_b =
|
|
+ le32_to_cpu(statistics->general.beacon_energy_b);
|
|
+ statis.beacon_energy_c =
|
|
+ le32_to_cpu(statistics->general.beacon_energy_c);
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ IWL_DEBUG_CALIB("rx_enable_time = %u usecs\n", rx_enable_time);
|
|
+
|
|
+ if (!rx_enable_time) {
|
|
+ IWL_DEBUG_CALIB("<< RX Enable Time == 0! \n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* These statistics increase monotonically, and do not reset
|
|
+ * at each beacon. Calculate difference from last value, or just
|
|
+ * use the new statistics value if it has reset or wrapped around. */
|
|
+ if (data->last_bad_plcp_cnt_cck > bad_plcp_cck)
|
|
+ data->last_bad_plcp_cnt_cck = bad_plcp_cck;
|
|
+ else {
|
|
+ bad_plcp_cck -= data->last_bad_plcp_cnt_cck;
|
|
+ data->last_bad_plcp_cnt_cck += bad_plcp_cck;
|
|
+ }
|
|
+
|
|
+ if (data->last_bad_plcp_cnt_ofdm > bad_plcp_ofdm)
|
|
+ data->last_bad_plcp_cnt_ofdm = bad_plcp_ofdm;
|
|
+ else {
|
|
+ bad_plcp_ofdm -= data->last_bad_plcp_cnt_ofdm;
|
|
+ data->last_bad_plcp_cnt_ofdm += bad_plcp_ofdm;
|
|
+ }
|
|
+
|
|
+ if (data->last_fa_cnt_ofdm > fa_ofdm)
|
|
+ data->last_fa_cnt_ofdm = fa_ofdm;
|
|
+ else {
|
|
+ fa_ofdm -= data->last_fa_cnt_ofdm;
|
|
+ data->last_fa_cnt_ofdm += fa_ofdm;
|
|
+ }
|
|
+
|
|
+ if (data->last_fa_cnt_cck > fa_cck)
|
|
+ data->last_fa_cnt_cck = fa_cck;
|
|
+ else {
|
|
+ fa_cck -= data->last_fa_cnt_cck;
|
|
+ data->last_fa_cnt_cck += fa_cck;
|
|
+ }
|
|
+
|
|
+ /* Total aborted signal locks */
|
|
+ norm_fa_ofdm = fa_ofdm + bad_plcp_ofdm;
|
|
+ norm_fa_cck = fa_cck + bad_plcp_cck;
|
|
+
|
|
+ IWL_DEBUG_CALIB("cck: fa %u badp %u ofdm: fa %u badp %u\n", fa_cck,
|
|
+ bad_plcp_cck, fa_ofdm, bad_plcp_ofdm);
|
|
+
|
|
+ iwl4965_sens_auto_corr_ofdm(priv, norm_fa_ofdm, rx_enable_time);
|
|
+ iwl4965_sens_energy_cck(priv, norm_fa_cck, rx_enable_time, &statis);
|
|
+ rc |= iwl4965_sensitivity_write(priv, CMD_ASYNC);
|
|
+
|
|
+ return;
|
|
+}
|
|
+
|
|
+static void iwl4965_bg_sensitivity_work(struct work_struct *work)
|
|
+{
|
|
+ struct iwl_priv *priv = container_of(work, struct iwl_priv,
|
|
+ sensitivity_work);
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+
|
|
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status) ||
|
|
+ test_bit(STATUS_SCANNING, &priv->status)) {
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (priv->start_calib) {
|
|
+ iwl4965_noise_calibration(priv, &priv->statistics);
|
|
+
|
|
+ if (priv->sensitivity_data.state ==
|
|
+ IWL_SENS_CALIB_NEED_REINIT) {
|
|
+ iwl4965_init_sensitivity(priv, CMD_ASYNC, 0);
|
|
+ priv->sensitivity_data.state = IWL_SENS_CALIB_ALLOWED;
|
|
+ } else
|
|
+ iwl4965_sensitivity_calibration(priv,
|
|
+ &priv->statistics);
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ return;
|
|
+}
|
|
+#endif /*CONFIG_IWLWIFI_SENSITIVITY*/
|
|
+
|
|
+static void iwl4965_bg_txpower_work(struct work_struct *work)
|
|
+{
|
|
+ struct iwl_priv *priv = container_of(work, struct iwl_priv,
|
|
+ txpower_work);
|
|
+
|
|
+ /* If a scan happened to start before we got here
|
|
+ * then just return; the statistics notification will
|
|
+ * kick off another scheduled work to compensate for
|
|
+ * any temperature delta we missed here. */
|
|
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status) ||
|
|
+ test_bit(STATUS_SCANNING, &priv->status))
|
|
+ return;
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+
|
|
+ /* Regardless of if we are assocaited, we must reconfigure the
|
|
+ * TX power since frames can be sent on non-radar channels while
|
|
+ * not associated */
|
|
+ iwl_hw_reg_send_txpower(priv);
|
|
+
|
|
+ /* Update last_temperature to keep is_calib_needed from running
|
|
+ * when it isn't needed... */
|
|
+ priv->last_temperature = priv->temperature;
|
|
+
|
|
+ mutex_unlock(&priv->mutex);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Acquire priv->lock before calling this function !
|
|
+ */
|
|
+static void iwl4965_set_wr_ptrs(struct iwl_priv *priv, int txq_id, u32 index)
|
|
+{
|
|
+ iwl_write_restricted(priv, HBUS_TARG_WRPTR,
|
|
+ (index & 0xff) | (txq_id << 8));
|
|
+ iwl_write_restricted_reg(priv, SCD_QUEUE_RDPTR(txq_id), index);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Acquire priv->lock before calling this function !
|
|
+ */
|
|
+static void iwl4965_tx_queue_set_status(struct iwl_priv *priv,
|
|
+ struct iwl_tx_queue *txq,
|
|
+ int tx_fifo_id, int scd_retry)
|
|
+{
|
|
+ int txq_id = txq->q.id;
|
|
+ int active = test_bit(txq_id, &priv->txq_ctx_active_msk)?1:0;
|
|
+
|
|
+ iwl_write_restricted_reg(priv, SCD_QUEUE_STATUS_BITS(txq_id),
|
|
+ (active << SCD_QUEUE_STTS_REG_POS_ACTIVE) |
|
|
+ (tx_fifo_id << SCD_QUEUE_STTS_REG_POS_TXF) |
|
|
+ (scd_retry << SCD_QUEUE_STTS_REG_POS_WSL) |
|
|
+ (scd_retry << SCD_QUEUE_STTS_REG_POS_SCD_ACK) |
|
|
+ SCD_QUEUE_STTS_REG_MSK);
|
|
+
|
|
+ txq->sched_retry = scd_retry;
|
|
+
|
|
+ IWL_DEBUG_INFO("%s %s Queue %d on AC %d\n",
|
|
+ active ? "Activete" : "Deactivate",
|
|
+ scd_retry ? "BA" : "AC", txq_id, tx_fifo_id);
|
|
+}
|
|
+
|
|
+static const u16 default_ac_to_tx_fifo[] = {
|
|
+ IWL_TX_QUEUE_AC1, IWL_TX_QUEUE_AC0,
|
|
+ IWL_TX_QUEUE_AC2, IWL_TX_QUEUE_AC3,
|
|
+ IWL_TX_QUEUE_HCCA_1, IWL_TX_QUEUE_HCCA_2
|
|
+};
|
|
+
|
|
+
|
|
+static inline void iwl4965_txq_ctx_activate(struct iwl_priv *priv, int txq_id)
|
|
+{
|
|
+ set_bit(txq_id, &priv->txq_ctx_active_msk);
|
|
+}
|
|
+
|
|
+static inline void iwl4965_txq_ctx_deactivate(struct iwl_priv *priv, int txq_id)
|
|
+{
|
|
+ clear_bit(txq_id, &priv->txq_ctx_active_msk);
|
|
+}
|
|
+
|
|
+int iwl4965_alive_notify(struct iwl_priv *priv)
|
|
+{
|
|
+ u32 a;
|
|
+ int i = 0;
|
|
+ unsigned long flags;
|
|
+ int rc;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_SENSITIVITY
|
|
+ memset(&(priv->sensitivity_data), 0,
|
|
+ sizeof(struct iwl_sensitivity_data));
|
|
+ memset(&(priv->chain_noise_data), 0,
|
|
+ sizeof(struct iwl_chain_noise_data));
|
|
+ for (i = 0; i < NUM_RX_CHAINS; i++)
|
|
+ priv->chain_noise_data.delta_gain_code[i] =
|
|
+ CHAIN_NOISE_DELTA_GAIN_INIT_VAL;
|
|
+#endif /* CONFIG_IWLWIFI_SENSITIVITY*/
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc) {
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ priv->scd_base_addr = iwl_read_restricted_reg(priv, SCD_SRAM_BASE_ADDR);
|
|
+ a = priv->scd_base_addr + SCD_CONTEXT_DATA_OFFSET;
|
|
+ for (; a < priv->scd_base_addr + SCD_TX_STTS_BITMAP_OFFSET; a += 4)
|
|
+ iwl_write_restricted_mem(priv, a, 0);
|
|
+ for (; a < priv->scd_base_addr + SCD_TRANSLATE_TBL_OFFSET; a += 4)
|
|
+ iwl_write_restricted_mem(priv, a, 0);
|
|
+ for (; a < sizeof(u16) * priv->hw_setting.max_txq_num; a += 4)
|
|
+ iwl_write_restricted_mem(priv, a, 0);
|
|
+
|
|
+ iwl_write_restricted_reg(priv, SCD_DRAM_BASE_ADDR,
|
|
+ (priv->hw_setting.shared_phys +
|
|
+ offsetof(struct iwl_shared, queues_byte_cnt_tbls)) >> 10);
|
|
+ iwl_write_restricted_reg(priv, SCD_QUEUECHAIN_SEL, 0);
|
|
+
|
|
+ /* initiate the queues */
|
|
+ for (i = 0; i < priv->hw_setting.max_txq_num; i++) {
|
|
+ iwl_write_restricted_reg(priv, SCD_QUEUE_RDPTR(i), 0);
|
|
+ iwl_write_restricted(priv, HBUS_TARG_WRPTR, 0 | (i << 8));
|
|
+ iwl_write_restricted_mem(priv, priv->scd_base_addr +
|
|
+ SCD_CONTEXT_QUEUE_OFFSET(i),
|
|
+ (SCD_WIN_SIZE <<
|
|
+ SCD_QUEUE_CTX_REG1_WIN_SIZE_POS) &
|
|
+ SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK);
|
|
+ iwl_write_restricted_mem(priv, priv->scd_base_addr +
|
|
+ SCD_CONTEXT_QUEUE_OFFSET(i) +
|
|
+ sizeof(u32),
|
|
+ (SCD_FRAME_LIMIT <<
|
|
+ SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
|
|
+ SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK);
|
|
+
|
|
+ }
|
|
+ iwl_write_restricted_reg(priv, SCD_INTERRUPT_MASK,
|
|
+ (1 << priv->hw_setting.max_txq_num) - 1);
|
|
+
|
|
+ iwl_write_restricted_reg(priv, SCD_TXFACT,
|
|
+ SCD_TXFACT_REG_TXFIFO_MASK(0, 7));
|
|
+
|
|
+ iwl4965_set_wr_ptrs(priv, IWL_CMD_QUEUE_NUM, 0);
|
|
+ iwl4965_txq_ctx_activate(priv, IWL_CMD_QUEUE_NUM);
|
|
+ iwl4965_tx_queue_set_status(priv, &priv->txq[IWL_CMD_QUEUE_NUM],
|
|
+ IWL_CMD_FIFO_NUM, 0);
|
|
+ /* map qos queues to fifos one-to-one */
|
|
+ for (i = 0; i < ARRAY_SIZE(default_ac_to_tx_fifo); i++) {
|
|
+ int ac = default_ac_to_tx_fifo[i];
|
|
+ iwl4965_txq_ctx_activate(priv, ac);
|
|
+ iwl4965_tx_queue_set_status(priv, &priv->txq[ac], ac, 0);
|
|
+ }
|
|
+
|
|
+ iwl_release_restricted_access(priv);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int iwl_hw_set_hw_setting(struct iwl_priv *priv)
|
|
+{
|
|
+ priv->hw_setting.shared_virt =
|
|
+ pci_alloc_consistent(priv->pci_dev,
|
|
+ sizeof(struct iwl_shared),
|
|
+ &priv->hw_setting.shared_phys);
|
|
+
|
|
+ if (!priv->hw_setting.shared_virt)
|
|
+ return -1;
|
|
+
|
|
+ memset(priv->hw_setting.shared_virt, 0, sizeof(struct iwl_shared));
|
|
+
|
|
+ priv->hw_setting.max_txq_num = iwl_param_queues_num;
|
|
+ priv->hw_setting.ac_queue_count = AC_NUM;
|
|
+
|
|
+ priv->hw_setting.cck_flag = RATE_MCS_CCK_MSK;
|
|
+ priv->hw_setting.tx_cmd_len = sizeof(struct iwl_tx_cmd);
|
|
+ priv->hw_setting.max_rxq_size = RX_QUEUE_SIZE;
|
|
+ priv->hw_setting.max_rxq_log = RX_QUEUE_SIZE_LOG;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_hw_txq_ctx_free - Free TXQ Context
|
|
+ *
|
|
+ * Destroy all TX DMA queues and structures
|
|
+ */
|
|
+void iwl_hw_txq_ctx_free(struct iwl_priv *priv)
|
|
+{
|
|
+ int txq_id;
|
|
+
|
|
+ /* Tx queues */
|
|
+ for (txq_id = 0; txq_id < priv->hw_setting.max_txq_num; txq_id++)
|
|
+ iwl_tx_queue_free(priv, &priv->txq[txq_id]);
|
|
+
|
|
+ iwl4965_kw_free(priv);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_hw_txq_free_tfd - Free one TFD, those at index [txq->q.last_used]
|
|
+ *
|
|
+ * Does NOT advance any indexes
|
|
+ */
|
|
+int iwl_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq)
|
|
+{
|
|
+ struct iwl_tfd_frame *bd_tmp = (struct iwl_tfd_frame *)&txq->bd[0];
|
|
+ struct iwl_tfd_frame *bd = &bd_tmp[txq->q.last_used];
|
|
+ struct pci_dev *dev = priv->pci_dev;
|
|
+ int i;
|
|
+ int counter = 0;
|
|
+ int index, is_odd;
|
|
+
|
|
+ /* classify bd */
|
|
+ if (txq->q.id == IWL_CMD_QUEUE_NUM)
|
|
+ /* nothing to cleanup after for host commands */
|
|
+ return 0;
|
|
+
|
|
+ /* sanity check */
|
|
+ counter = IWL_GET_BITS(*bd, num_tbs);
|
|
+ if (counter > MAX_NUM_OF_TBS) {
|
|
+ IWL_ERROR("Too many chunks: %i\n", counter);
|
|
+ /* @todo issue fatal error, it is quite serious situation */
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* unmap chunks if any */
|
|
+
|
|
+ for (i = 0; i < counter; i++) {
|
|
+ index = i / 2;
|
|
+ is_odd = i & 0x1;
|
|
+
|
|
+ if (is_odd)
|
|
+ pci_unmap_single(
|
|
+ dev,
|
|
+ IWL_GET_BITS(bd->pa[index], tb2_addr_lo16) |
|
|
+ (IWL_GET_BITS(bd->pa[index],
|
|
+ tb2_addr_hi20) << 16),
|
|
+ IWL_GET_BITS(bd->pa[index], tb2_len),
|
|
+ PCI_DMA_TODEVICE);
|
|
+
|
|
+ else if (i > 0)
|
|
+ pci_unmap_single(dev,
|
|
+ le32_to_cpu(bd->pa[index].tb1_addr),
|
|
+ IWL_GET_BITS(bd->pa[index], tb1_len),
|
|
+ PCI_DMA_TODEVICE);
|
|
+
|
|
+ if (txq->txb[txq->q.last_used].skb[i]) {
|
|
+ struct sk_buff *skb = txq->txb[txq->q.last_used].skb[i];
|
|
+
|
|
+ dev_kfree_skb(skb);
|
|
+ txq->txb[txq->q.last_used].skb[i] = NULL;
|
|
+ }
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int iwl_hw_reg_set_txpower(struct iwl_priv *priv, s8 power)
|
|
+{
|
|
+ IWL_ERROR("TODO: Implement iwl_hw_reg_set_txpower!\n");
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+#define TX_POWER_IWL_ILLEGAL_VDET -100000
|
|
+#define TX_POWER_IWL_ILLEGAL_VOLTAGE -10000
|
|
+#define TX_POWER_IWL_CLOSED_LOOP_MIN_POWER 18
|
|
+#define TX_POWER_IWL_CLOSED_LOOP_MAX_POWER 34
|
|
+#define TX_POWER_IWL_VDET_SLOPE_BELOW_NOMINAL 17
|
|
+#define TX_POWER_IWL_VDET_SLOPE_ABOVE_NOMINAL 20
|
|
+#define TX_POWER_IWL_NOMINAL_POWER 26
|
|
+#define TX_POWER_IWL_CLOSED_LOOP_ITERATION_LIMIT 1
|
|
+#define TX_POWER_IWL_VOLTAGE_CODES_PER_03V 7
|
|
+#define TX_POWER_IWL_DEGREES_PER_VDET_CODE 11
|
|
+#define IWL_TX_POWER_MAX_NUM_PA_MEASUREMENTS 1
|
|
+#define IWL_TX_POWER_CCK_COMPENSATION_B_STEP (9)
|
|
+#define IWL_TX_POWER_CCK_COMPENSATION_C_STEP (5)
|
|
+
|
|
+static s32 iwl4965_math_div_round(s32 num, s32 denom, s32 *res)
|
|
+{
|
|
+ s32 sign = 1;
|
|
+
|
|
+ if (num < 0) {
|
|
+ sign = -sign;
|
|
+ num = -num;
|
|
+ }
|
|
+ if (denom < 0) {
|
|
+ sign = -sign;
|
|
+ denom = -denom;
|
|
+ }
|
|
+ *res = 1;
|
|
+ *res = ((num * 2 + denom) / (denom * 2)) * sign;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static s32 iwl4965_get_voltage_compensation(s32 eeprom_voltage,
|
|
+ s32 current_voltage)
|
|
+{
|
|
+ s32 comp = 0;
|
|
+
|
|
+ if ((TX_POWER_IWL_ILLEGAL_VOLTAGE == eeprom_voltage) ||
|
|
+ (TX_POWER_IWL_ILLEGAL_VOLTAGE == current_voltage))
|
|
+ return 0;
|
|
+
|
|
+ iwl4965_math_div_round(current_voltage - eeprom_voltage,
|
|
+ TX_POWER_IWL_VOLTAGE_CODES_PER_03V, &comp);
|
|
+
|
|
+ if (current_voltage > eeprom_voltage)
|
|
+ comp *= 2;
|
|
+ if ((comp < -2) || (comp > 2))
|
|
+ comp = 0;
|
|
+
|
|
+ return comp;
|
|
+}
|
|
+
|
|
+static const struct iwl_channel_info *
|
|
+iwl4965_get_channel_txpower_info(struct iwl_priv *priv, u8 phymode, u16 channel)
|
|
+{
|
|
+ const struct iwl_channel_info *ch_info;
|
|
+
|
|
+ ch_info = iwl_get_channel_info(priv, phymode, channel);
|
|
+
|
|
+ if (!is_channel_valid(ch_info))
|
|
+ return NULL;
|
|
+
|
|
+ return ch_info;
|
|
+}
|
|
+
|
|
+static s32 iwl4965_get_tx_atten_grp(u16 channel)
|
|
+{
|
|
+ if (channel >= CALIB_IWL_TX_ATTEN_GR5_FCH &&
|
|
+ channel <= CALIB_IWL_TX_ATTEN_GR5_LCH)
|
|
+ return CALIB_CH_GROUP_5;
|
|
+
|
|
+ if (channel >= CALIB_IWL_TX_ATTEN_GR1_FCH &&
|
|
+ channel <= CALIB_IWL_TX_ATTEN_GR1_LCH)
|
|
+ return CALIB_CH_GROUP_1;
|
|
+
|
|
+ if (channel >= CALIB_IWL_TX_ATTEN_GR2_FCH &&
|
|
+ channel <= CALIB_IWL_TX_ATTEN_GR2_LCH)
|
|
+ return CALIB_CH_GROUP_2;
|
|
+
|
|
+ if (channel >= CALIB_IWL_TX_ATTEN_GR3_FCH &&
|
|
+ channel <= CALIB_IWL_TX_ATTEN_GR3_LCH)
|
|
+ return CALIB_CH_GROUP_3;
|
|
+
|
|
+ if (channel >= CALIB_IWL_TX_ATTEN_GR4_FCH &&
|
|
+ channel <= CALIB_IWL_TX_ATTEN_GR4_LCH)
|
|
+ return CALIB_CH_GROUP_4;
|
|
+
|
|
+ IWL_ERROR("Can't find txatten group for channel %d.\n", channel);
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static u32 iwl4965_get_sub_band(const struct iwl_priv *priv, u32 channel)
|
|
+{
|
|
+ s32 b = -1;
|
|
+
|
|
+ for (b = 0; b < EEPROM_TX_POWER_BANDS; b++) {
|
|
+ if (priv->eeprom.calib_info.band_info[b].ch_from == 0)
|
|
+ continue;
|
|
+
|
|
+ if ((channel >= priv->eeprom.calib_info.band_info[b].ch_from)
|
|
+ && (channel <= priv->eeprom.calib_info.band_info[b].ch_to))
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return b;
|
|
+}
|
|
+
|
|
+static s32 iwl4965_interpolate_value(s32 x, s32 x1, s32 y1, s32 x2, s32 y2)
|
|
+{
|
|
+ s32 val;
|
|
+
|
|
+ if (x2 == x1)
|
|
+ return y1;
|
|
+ else {
|
|
+ iwl4965_math_div_round((x2 - x) * (y1 - y2), (x2 - x1), &val);
|
|
+ return val + y2;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int iwl4965_interpolate_chan(struct iwl_priv *priv, u32 channel,
|
|
+ struct iwl_eeprom_calib_ch_info *chan_info)
|
|
+{
|
|
+ s32 s = -1;
|
|
+ u32 c;
|
|
+ u32 m;
|
|
+ const struct iwl_eeprom_calib_measure *m1;
|
|
+ const struct iwl_eeprom_calib_measure *m2;
|
|
+ struct iwl_eeprom_calib_measure *omeas;
|
|
+ u32 ch_i1;
|
|
+ u32 ch_i2;
|
|
+
|
|
+ s = iwl4965_get_sub_band(priv, channel);
|
|
+ if (s >= EEPROM_TX_POWER_BANDS) {
|
|
+ IWL_ERROR("Tx Power can not find channel %d ", channel);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ ch_i1 = priv->eeprom.calib_info.band_info[s].ch1.ch_num;
|
|
+ ch_i2 = priv->eeprom.calib_info.band_info[s].ch2.ch_num;
|
|
+ chan_info->ch_num = (u8) channel;
|
|
+
|
|
+ IWL_DEBUG_TXPOWER("channel %d subband %d factory cal ch %d & %d\n",
|
|
+ channel, s, ch_i1, ch_i2);
|
|
+
|
|
+ for (c = 0; c < EEPROM_TX_POWER_TX_CHAINS; c++) {
|
|
+ for (m = 0; m < EEPROM_TX_POWER_MEASUREMENTS; m++) {
|
|
+ m1 = &(priv->eeprom.calib_info.band_info[s].ch1.
|
|
+ measurements[c][m]);
|
|
+ m2 = &(priv->eeprom.calib_info.band_info[s].ch2.
|
|
+ measurements[c][m]);
|
|
+ omeas = &(chan_info->measurements[c][m]);
|
|
+
|
|
+ omeas->actual_pow =
|
|
+ (u8) iwl4965_interpolate_value(channel, ch_i1,
|
|
+ m1->actual_pow,
|
|
+ ch_i2,
|
|
+ m2->actual_pow);
|
|
+ omeas->gain_idx =
|
|
+ (u8) iwl4965_interpolate_value(channel, ch_i1,
|
|
+ m1->gain_idx, ch_i2,
|
|
+ m2->gain_idx);
|
|
+ omeas->temperature =
|
|
+ (u8) iwl4965_interpolate_value(channel, ch_i1,
|
|
+ m1->temperature,
|
|
+ ch_i2,
|
|
+ m2->temperature);
|
|
+ omeas->pa_det =
|
|
+ (s8) iwl4965_interpolate_value(channel, ch_i1,
|
|
+ m1->pa_det, ch_i2,
|
|
+ m2->pa_det);
|
|
+
|
|
+ IWL_DEBUG_TXPOWER
|
|
+ ("chain %d meas %d AP1=%d AP2=%d AP=%d\n", c, m,
|
|
+ m1->actual_pow, m2->actual_pow, omeas->actual_pow);
|
|
+ IWL_DEBUG_TXPOWER
|
|
+ ("chain %d meas %d NI1=%d NI2=%d NI=%d\n", c, m,
|
|
+ m1->gain_idx, m2->gain_idx, omeas->gain_idx);
|
|
+ IWL_DEBUG_TXPOWER
|
|
+ ("chain %d meas %d PA1=%d PA2=%d PA=%d\n", c, m,
|
|
+ m1->pa_det, m2->pa_det, omeas->pa_det);
|
|
+ IWL_DEBUG_TXPOWER
|
|
+ ("chain %d meas %d T1=%d T2=%d T=%d\n", c, m,
|
|
+ m1->temperature, m2->temperature,
|
|
+ omeas->temperature);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* bit-rate-dependent table to prevent Tx distortion, in half-dB units,
|
|
+ * for OFDM 6, 12, 18, 24, 36, 48, 54, 60 MBit, and CCK all rates. */
|
|
+static s32 back_off_table[] = {
|
|
+ 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM SISO 20 MHz */
|
|
+ 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM MIMO 20 MHz */
|
|
+ 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM SISO 40 MHz */
|
|
+ 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM MIMO 40 MHz */
|
|
+ 10 /* CCK */
|
|
+};
|
|
+
|
|
+/* Thermal compensation values for txpower for various frequency ranges ...
|
|
+ * ratios from 3:1 to 4.5:1 of degrees (Celsius) per half-dB gain adjust */
|
|
+static struct iwl_txpower_comp_entry {
|
|
+ s32 degrees_per_05db_a;
|
|
+ s32 degrees_per_05db_a_denom;
|
|
+} tx_power_cmp_tble[CALIB_CH_GROUP_MAX] = {
|
|
+ {9, 2}, /* group 0 5.2, ch 34-43 */
|
|
+ {4, 1}, /* group 1 5.2, ch 44-70 */
|
|
+ {4, 1}, /* group 2 5.2, ch 71-124 */
|
|
+ {4, 1}, /* group 3 5.2, ch 125-200 */
|
|
+ {3, 1} /* group 4 2.4, ch all */
|
|
+};
|
|
+
|
|
+static s32 get_min_power_index(s32 rate_power_index, u32 band)
|
|
+{
|
|
+ if (!band) {
|
|
+ if ((rate_power_index & 7) <= 4)
|
|
+ return MIN_TX_GAIN_INDEX_52GHZ_EXT;
|
|
+ }
|
|
+ return MIN_TX_GAIN_INDEX;
|
|
+}
|
|
+
|
|
+struct gain_entry {
|
|
+ u8 dsp;
|
|
+ u8 radio;
|
|
+};
|
|
+
|
|
+static const struct gain_entry gain_table[2][108] = {
|
|
+ /* 5.2GHz power gain index table */
|
|
+ {
|
|
+ {123, 0x3F}, /* highest txpower */
|
|
+ {117, 0x3F},
|
|
+ {110, 0x3F},
|
|
+ {104, 0x3F},
|
|
+ {98, 0x3F},
|
|
+ {110, 0x3E},
|
|
+ {104, 0x3E},
|
|
+ {98, 0x3E},
|
|
+ {110, 0x3D},
|
|
+ {104, 0x3D},
|
|
+ {98, 0x3D},
|
|
+ {110, 0x3C},
|
|
+ {104, 0x3C},
|
|
+ {98, 0x3C},
|
|
+ {110, 0x3B},
|
|
+ {104, 0x3B},
|
|
+ {98, 0x3B},
|
|
+ {110, 0x3A},
|
|
+ {104, 0x3A},
|
|
+ {98, 0x3A},
|
|
+ {110, 0x39},
|
|
+ {104, 0x39},
|
|
+ {98, 0x39},
|
|
+ {110, 0x38},
|
|
+ {104, 0x38},
|
|
+ {98, 0x38},
|
|
+ {110, 0x37},
|
|
+ {104, 0x37},
|
|
+ {98, 0x37},
|
|
+ {110, 0x36},
|
|
+ {104, 0x36},
|
|
+ {98, 0x36},
|
|
+ {110, 0x35},
|
|
+ {104, 0x35},
|
|
+ {98, 0x35},
|
|
+ {110, 0x34},
|
|
+ {104, 0x34},
|
|
+ {98, 0x34},
|
|
+ {110, 0x33},
|
|
+ {104, 0x33},
|
|
+ {98, 0x33},
|
|
+ {110, 0x32},
|
|
+ {104, 0x32},
|
|
+ {98, 0x32},
|
|
+ {110, 0x31},
|
|
+ {104, 0x31},
|
|
+ {98, 0x31},
|
|
+ {110, 0x30},
|
|
+ {104, 0x30},
|
|
+ {98, 0x30},
|
|
+ {110, 0x25},
|
|
+ {104, 0x25},
|
|
+ {98, 0x25},
|
|
+ {110, 0x24},
|
|
+ {104, 0x24},
|
|
+ {98, 0x24},
|
|
+ {110, 0x23},
|
|
+ {104, 0x23},
|
|
+ {98, 0x23},
|
|
+ {110, 0x22},
|
|
+ {104, 0x18},
|
|
+ {98, 0x18},
|
|
+ {110, 0x17},
|
|
+ {104, 0x17},
|
|
+ {98, 0x17},
|
|
+ {110, 0x16},
|
|
+ {104, 0x16},
|
|
+ {98, 0x16},
|
|
+ {110, 0x15},
|
|
+ {104, 0x15},
|
|
+ {98, 0x15},
|
|
+ {110, 0x14},
|
|
+ {104, 0x14},
|
|
+ {98, 0x14},
|
|
+ {110, 0x13},
|
|
+ {104, 0x13},
|
|
+ {98, 0x13},
|
|
+ {110, 0x12},
|
|
+ {104, 0x08},
|
|
+ {98, 0x08},
|
|
+ {110, 0x07},
|
|
+ {104, 0x07},
|
|
+ {98, 0x07},
|
|
+ {110, 0x06},
|
|
+ {104, 0x06},
|
|
+ {98, 0x06},
|
|
+ {110, 0x05},
|
|
+ {104, 0x05},
|
|
+ {98, 0x05},
|
|
+ {110, 0x04},
|
|
+ {104, 0x04},
|
|
+ {98, 0x04},
|
|
+ {110, 0x03},
|
|
+ {104, 0x03},
|
|
+ {98, 0x03},
|
|
+ {110, 0x02},
|
|
+ {104, 0x02},
|
|
+ {98, 0x02},
|
|
+ {110, 0x01},
|
|
+ {104, 0x01},
|
|
+ {98, 0x01},
|
|
+ {110, 0x00},
|
|
+ {104, 0x00},
|
|
+ {98, 0x00},
|
|
+ {93, 0x00},
|
|
+ {88, 0x00},
|
|
+ {83, 0x00},
|
|
+ {78, 0x00},
|
|
+ },
|
|
+ /* 2.4GHz power gain index table */
|
|
+ {
|
|
+ {110, 0x3f}, /* highest txpower */
|
|
+ {104, 0x3f},
|
|
+ {98, 0x3f},
|
|
+ {110, 0x3e},
|
|
+ {104, 0x3e},
|
|
+ {98, 0x3e},
|
|
+ {110, 0x3d},
|
|
+ {104, 0x3d},
|
|
+ {98, 0x3d},
|
|
+ {110, 0x3c},
|
|
+ {104, 0x3c},
|
|
+ {98, 0x3c},
|
|
+ {110, 0x3b},
|
|
+ {104, 0x3b},
|
|
+ {98, 0x3b},
|
|
+ {110, 0x3a},
|
|
+ {104, 0x3a},
|
|
+ {98, 0x3a},
|
|
+ {110, 0x39},
|
|
+ {104, 0x39},
|
|
+ {98, 0x39},
|
|
+ {110, 0x38},
|
|
+ {104, 0x38},
|
|
+ {98, 0x38},
|
|
+ {110, 0x37},
|
|
+ {104, 0x37},
|
|
+ {98, 0x37},
|
|
+ {110, 0x36},
|
|
+ {104, 0x36},
|
|
+ {98, 0x36},
|
|
+ {110, 0x35},
|
|
+ {104, 0x35},
|
|
+ {98, 0x35},
|
|
+ {110, 0x34},
|
|
+ {104, 0x34},
|
|
+ {98, 0x34},
|
|
+ {110, 0x33},
|
|
+ {104, 0x33},
|
|
+ {98, 0x33},
|
|
+ {110, 0x32},
|
|
+ {104, 0x32},
|
|
+ {98, 0x32},
|
|
+ {110, 0x31},
|
|
+ {104, 0x31},
|
|
+ {98, 0x31},
|
|
+ {110, 0x30},
|
|
+ {104, 0x30},
|
|
+ {98, 0x30},
|
|
+ {110, 0x6},
|
|
+ {104, 0x6},
|
|
+ {98, 0x6},
|
|
+ {110, 0x5},
|
|
+ {104, 0x5},
|
|
+ {98, 0x5},
|
|
+ {110, 0x4},
|
|
+ {104, 0x4},
|
|
+ {98, 0x4},
|
|
+ {110, 0x3},
|
|
+ {104, 0x3},
|
|
+ {98, 0x3},
|
|
+ {110, 0x2},
|
|
+ {104, 0x2},
|
|
+ {98, 0x2},
|
|
+ {110, 0x1},
|
|
+ {104, 0x1},
|
|
+ {98, 0x1},
|
|
+ {110, 0x0},
|
|
+ {104, 0x0},
|
|
+ {98, 0x0},
|
|
+ {97, 0},
|
|
+ {96, 0},
|
|
+ {95, 0},
|
|
+ {94, 0},
|
|
+ {93, 0},
|
|
+ {92, 0},
|
|
+ {91, 0},
|
|
+ {90, 0},
|
|
+ {89, 0},
|
|
+ {88, 0},
|
|
+ {87, 0},
|
|
+ {86, 0},
|
|
+ {85, 0},
|
|
+ {84, 0},
|
|
+ {83, 0},
|
|
+ {82, 0},
|
|
+ {81, 0},
|
|
+ {80, 0},
|
|
+ {79, 0},
|
|
+ {78, 0},
|
|
+ {77, 0},
|
|
+ {76, 0},
|
|
+ {75, 0},
|
|
+ {74, 0},
|
|
+ {73, 0},
|
|
+ {72, 0},
|
|
+ {71, 0},
|
|
+ {70, 0},
|
|
+ {69, 0},
|
|
+ {68, 0},
|
|
+ {67, 0},
|
|
+ {66, 0},
|
|
+ {65, 0},
|
|
+ {64, 0},
|
|
+ {63, 0},
|
|
+ {62, 0},
|
|
+ {61, 0},
|
|
+ {60, 0},
|
|
+ {59, 0},
|
|
+ }
|
|
+};
|
|
+
|
|
+static int iwl4965_fill_txpower_tbl(struct iwl_priv *priv, u8 band, u16 channel,
|
|
+ u8 is_fat, u8 ctrl_chan_high,
|
|
+ struct iwl_tx_power_db *tx_power_tbl)
|
|
+{
|
|
+ u8 saturation_power;
|
|
+ s32 target_power;
|
|
+ s32 user_target_power;
|
|
+ s32 power_limit;
|
|
+ s32 current_temp;
|
|
+ s32 reg_limit;
|
|
+ s32 current_regulatory;
|
|
+ s32 txatten_grp = CALIB_CH_GROUP_MAX;
|
|
+ int i;
|
|
+ int c;
|
|
+ const struct iwl_channel_info *ch_info = NULL;
|
|
+ struct iwl_eeprom_calib_ch_info ch_eeprom_info;
|
|
+ const struct iwl_eeprom_calib_measure *measurement;
|
|
+ s16 voltage;
|
|
+ s32 init_voltage;
|
|
+ s32 voltage_compensation;
|
|
+ s32 degrees_per_05db_num;
|
|
+ s32 degrees_per_05db_denom;
|
|
+ s32 factory_temp;
|
|
+ s32 temperature_comp[2];
|
|
+ s32 factory_gain_index[2];
|
|
+ s32 factory_actual_pwr[2];
|
|
+ s32 power_index;
|
|
+
|
|
+ /* Sanity check requested level (dBm) */
|
|
+ if (priv->user_txpower_limit < IWL_TX_POWER_TARGET_POWER_MIN) {
|
|
+ IWL_WARNING("Requested user TXPOWER %d below limit.\n",
|
|
+ priv->user_txpower_limit);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ if (priv->user_txpower_limit > IWL_TX_POWER_TARGET_POWER_MAX) {
|
|
+ IWL_WARNING("Requested user TXPOWER %d above limit.\n",
|
|
+ priv->user_txpower_limit);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* user_txpower_limit is in dBm, convert to half-dBm (half-dB units
|
|
+ * are used for indexing into txpower table) */
|
|
+ user_target_power = 2 * priv->user_txpower_limit;
|
|
+
|
|
+ /* Get current (RXON) channel, band, width */
|
|
+ ch_info =
|
|
+ iwl4965_get_channel_txpower_info(priv, priv->phymode, channel);
|
|
+
|
|
+ IWL_DEBUG_TXPOWER("chan %d band %d is_fat %d\n", channel, band,
|
|
+ is_fat);
|
|
+
|
|
+ if (!ch_info)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* get txatten group, used to select 1) thermal txpower adjustment
|
|
+ * and 2) mimo txpower balance between Tx chains. */
|
|
+ txatten_grp = iwl4965_get_tx_atten_grp(channel);
|
|
+ if (txatten_grp < 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ IWL_DEBUG_TXPOWER("channel %d belongs to txatten group %d\n",
|
|
+ channel, txatten_grp);
|
|
+
|
|
+ if (is_fat) {
|
|
+ if (ctrl_chan_high)
|
|
+ channel -= 2;
|
|
+ else
|
|
+ channel += 2;
|
|
+ }
|
|
+
|
|
+ /* hardware txpower limits ...
|
|
+ * saturation (clipping distortion) txpowers are in half-dBm */
|
|
+ if (band)
|
|
+ saturation_power = priv->eeprom.calib_info.saturation_power24;
|
|
+ else
|
|
+ saturation_power = priv->eeprom.calib_info.saturation_power52;
|
|
+
|
|
+ if (saturation_power < IWL_TX_POWER_SATURATION_MIN ||
|
|
+ saturation_power > IWL_TX_POWER_SATURATION_MAX) {
|
|
+ if (band)
|
|
+ saturation_power = IWL_TX_POWER_DEFAULT_SATURATION_24;
|
|
+ else
|
|
+ saturation_power = IWL_TX_POWER_DEFAULT_SATURATION_52;
|
|
+ }
|
|
+
|
|
+ /* regulatory txpower limits ... reg_limit values are in half-dBm,
|
|
+ * max_power_avg values are in dBm, convert * 2 */
|
|
+ if (is_fat)
|
|
+ reg_limit = ch_info->fat_max_power_avg * 2;
|
|
+ else
|
|
+ reg_limit = ch_info->max_power_avg * 2;
|
|
+
|
|
+ if ((reg_limit < IWL_TX_POWER_REGULATORY_MIN) ||
|
|
+ (reg_limit > IWL_TX_POWER_REGULATORY_MAX)) {
|
|
+ if (band)
|
|
+ reg_limit = IWL_TX_POWER_DEFAULT_REGULATORY_24;
|
|
+ else
|
|
+ reg_limit = IWL_TX_POWER_DEFAULT_REGULATORY_52;
|
|
+ }
|
|
+
|
|
+ /* Interpolate txpower calibration values for this channel,
|
|
+ * based on factory calibration tests on spaced channels. */
|
|
+ iwl4965_interpolate_chan(priv, channel, &ch_eeprom_info);
|
|
+
|
|
+ /* calculate tx gain adjustment based on power supply voltage */
|
|
+ voltage = priv->eeprom.calib_info.voltage;
|
|
+ init_voltage = (s32)le32_to_cpu(priv->card_alive_init.voltage);
|
|
+ voltage_compensation =
|
|
+ iwl4965_get_voltage_compensation(voltage, init_voltage);
|
|
+
|
|
+ IWL_DEBUG_TXPOWER("curr volt %d eeprom volt %d volt comp %d\n",
|
|
+ init_voltage,
|
|
+ voltage, voltage_compensation);
|
|
+
|
|
+ /* get current temperature (Celsius) */
|
|
+ current_temp = max(priv->temperature, IWL_TX_POWER_TEMPERATURE_MIN);
|
|
+ current_temp = min(priv->temperature, IWL_TX_POWER_TEMPERATURE_MAX);
|
|
+ current_temp = KELVIN_TO_CELSIUS(current_temp);
|
|
+
|
|
+ /* select thermal txpower adjustment params, based on channel group
|
|
+ * (same frequency group used for mimo txatten adjustment) */
|
|
+ degrees_per_05db_num =
|
|
+ tx_power_cmp_tble[txatten_grp].degrees_per_05db_a;
|
|
+ degrees_per_05db_denom =
|
|
+ tx_power_cmp_tble[txatten_grp].degrees_per_05db_a_denom;
|
|
+
|
|
+ /* get per-chain txpower values from factory measurements */
|
|
+ for (c = 0; c < 2; c++) {
|
|
+ measurement = &ch_eeprom_info.measurements[c][1];
|
|
+
|
|
+ /* txgain adjustment (in half-dB steps) based on difference
|
|
+ * between factory and current temperature */
|
|
+ factory_temp = measurement->temperature;
|
|
+ iwl4965_math_div_round((current_temp - factory_temp) *
|
|
+ degrees_per_05db_denom,
|
|
+ degrees_per_05db_num,
|
|
+ &temperature_comp[c]);
|
|
+
|
|
+ factory_gain_index[c] = measurement->gain_idx;
|
|
+ factory_actual_pwr[c] = measurement->actual_pow;
|
|
+
|
|
+ IWL_DEBUG_TXPOWER("chain = %d\n", c);
|
|
+ IWL_DEBUG_TXPOWER("fctry tmp %d, "
|
|
+ "curr tmp %d, comp %d steps\n",
|
|
+ factory_temp, current_temp,
|
|
+ temperature_comp[c]);
|
|
+
|
|
+ IWL_DEBUG_TXPOWER("fctry idx %d, fctry pwr %d\n",
|
|
+ factory_gain_index[c],
|
|
+ factory_actual_pwr[c]);
|
|
+ }
|
|
+
|
|
+ /* for each of 33 bit-rates (including 1 for CCK) */
|
|
+ for (i = 0; i < POWER_TABLE_NUM_ENTRIES; i++) {
|
|
+ u8 is_mimo_rate;
|
|
+ union iwl_tx_power_dual_stream tx_power;
|
|
+
|
|
+ /* for mimo, reduce each chain's txpower by half
|
|
+ * (3dB, 6 steps), so total output power is regulatory
|
|
+ * compliant. */
|
|
+ if (i & 0x8) {
|
|
+ current_regulatory = reg_limit -
|
|
+ IWL_TX_POWER_MIMO_REGULATORY_COMPENSATION;
|
|
+ is_mimo_rate = 1;
|
|
+ } else {
|
|
+ current_regulatory = reg_limit;
|
|
+ is_mimo_rate = 0;
|
|
+ }
|
|
+
|
|
+ /* find txpower limit, either hardware or regulatory */
|
|
+ power_limit = saturation_power - back_off_table[i];
|
|
+ if (power_limit > current_regulatory)
|
|
+ power_limit = current_regulatory;
|
|
+
|
|
+ /* reduce user's txpower request if necessary
|
|
+ * for this rate on this channel */
|
|
+ target_power = user_target_power;
|
|
+ if (target_power > power_limit)
|
|
+ target_power = power_limit;
|
|
+
|
|
+ IWL_DEBUG_TXPOWER("rate %d sat %d reg %d usr %d tgt %d\n",
|
|
+ i, saturation_power - back_off_table[i],
|
|
+ current_regulatory, user_target_power,
|
|
+ target_power);
|
|
+
|
|
+ /* for each of 2 Tx chains (radio transmitters) */
|
|
+ for (c = 0; c < 2; c++) {
|
|
+ s32 atten_value;
|
|
+
|
|
+ if (is_mimo_rate)
|
|
+ atten_value =
|
|
+ (s32)le32_to_cpu(priv->card_alive_init.
|
|
+ tx_atten[txatten_grp][c]);
|
|
+ else
|
|
+ atten_value = 0;
|
|
+
|
|
+ /* calculate index; higher index means lower txpower */
|
|
+ power_index = (u8) (factory_gain_index[c] -
|
|
+ (target_power -
|
|
+ factory_actual_pwr[c]) -
|
|
+ temperature_comp[c] -
|
|
+ voltage_compensation +
|
|
+ atten_value);
|
|
+
|
|
+/* IWL_DEBUG_TXPOWER("calculated txpower index %d\n",
|
|
+ power_index); */
|
|
+
|
|
+ if (power_index < get_min_power_index(i, band))
|
|
+ power_index = get_min_power_index(i, band);
|
|
+
|
|
+ /* adjust 5 GHz index to support negative indexes */
|
|
+ if (!band)
|
|
+ power_index += 9;
|
|
+
|
|
+ /* CCK, rate 32, reduce txpower for CCK */
|
|
+ if (i == POWER_TABLE_CCK_ENTRY)
|
|
+ power_index +=
|
|
+ IWL_TX_POWER_CCK_COMPENSATION_C_STEP;
|
|
+
|
|
+ /* stay within the table! */
|
|
+ if (power_index > 107) {
|
|
+ IWL_WARNING("txpower index %d > 107\n",
|
|
+ power_index);
|
|
+ power_index = 107;
|
|
+ }
|
|
+ if (power_index < 0) {
|
|
+ IWL_WARNING("txpower index %d < 0\n",
|
|
+ power_index);
|
|
+ power_index = 0;
|
|
+ }
|
|
+
|
|
+ /* fill txpower command for this rate/chain */
|
|
+ tx_power.s.radio_tx_gain[c] =
|
|
+ gain_table[band][power_index].radio;
|
|
+ tx_power.s.dsp_predis_atten[c] =
|
|
+ gain_table[band][power_index].dsp;
|
|
+
|
|
+ IWL_DEBUG_TXPOWER("chain %d mimo %d index %d "
|
|
+ "gain 0x%02x dsp %d\n",
|
|
+ c, atten_value, power_index,
|
|
+ tx_power.s.radio_tx_gain[c],
|
|
+ tx_power.s.dsp_predis_atten[c]);
|
|
+ }/* for each chain */
|
|
+
|
|
+ tx_power_tbl->power_tbl[i].dw = cpu_to_le32(tx_power.dw);
|
|
+
|
|
+ }/* for each rate */
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_hw_reg_send_txpower - Configure the TXPOWER level user limit
|
|
+ *
|
|
+ * Uses the active RXON for channel, band, and characteristics (fat, high)
|
|
+ * The power limit is taken from priv->user_txpower_limit.
|
|
+ */
|
|
+int iwl_hw_reg_send_txpower(struct iwl_priv *priv)
|
|
+{
|
|
+ struct iwl_tx_power_table_cmd cmd = { 0 };
|
|
+ int rc = 0;
|
|
+ u8 band = 0;
|
|
+ u8 is_fat = 0;
|
|
+ u8 ctrl_chan_high = 0;
|
|
+
|
|
+ if (test_bit(STATUS_SCANNING, &priv->status)) {
|
|
+ /* If this gets hit a lot, switch it to a BUG() and catch
|
|
+ * the stack trace to find out who is calling this during
|
|
+ * a scan. */
|
|
+ IWL_WARNING("TX Power requested while scanning!\n");
|
|
+ return -EAGAIN;
|
|
+ }
|
|
+
|
|
+ band = ((priv->phymode == MODE_IEEE80211B) ||
|
|
+ (priv->phymode == MODE_IEEE80211G));
|
|
+
|
|
+ is_fat = is_fat_channel(priv->active_rxon.flags);
|
|
+
|
|
+ if (is_fat &&
|
|
+ (priv->active_rxon.flags & RXON_FLG_CONTROL_CHANNEL_LOC_HIGH_MSK))
|
|
+ ctrl_chan_high = 1;
|
|
+
|
|
+ cmd.band = band;
|
|
+ cmd.channel = priv->active_rxon.channel;
|
|
+ cmd.channel_normal_width = 0;
|
|
+
|
|
+ rc = iwl4965_fill_txpower_tbl(priv, band,
|
|
+ le16_to_cpu(priv->active_rxon.channel),
|
|
+ is_fat, ctrl_chan_high, &cmd.tx_power);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ rc = iwl_send_cmd_pdu(priv, REPLY_TX_PWR_TABLE_CMD, sizeof(cmd), &cmd);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+int iwl_hw_channel_switch(struct iwl_priv *priv, u16 channel)
|
|
+{
|
|
+ int rc;
|
|
+ u8 band = 0;
|
|
+ u8 is_fat = 0;
|
|
+ u8 ctrl_chan_high = 0;
|
|
+ struct iwl_channel_switch_cmd cmd = { 0 };
|
|
+ const struct iwl_channel_info *ch_info;
|
|
+
|
|
+ band = ((priv->phymode == MODE_IEEE80211B) ||
|
|
+ (priv->phymode == MODE_IEEE80211G));
|
|
+
|
|
+ ch_info = iwl_get_channel_info(priv, priv->phymode, channel);
|
|
+
|
|
+ is_fat = is_fat_channel(priv->staging_rxon.flags);
|
|
+
|
|
+ if (is_fat &&
|
|
+ (priv->active_rxon.flags & RXON_FLG_CONTROL_CHANNEL_LOC_HIGH_MSK))
|
|
+ ctrl_chan_high = 1;
|
|
+
|
|
+ cmd.band = band;
|
|
+ cmd.expect_beacon = 0;
|
|
+ cmd.channel = cpu_to_le16(channel);
|
|
+ cmd.rxon_flags = priv->active_rxon.flags;
|
|
+ cmd.rxon_filter_flags = priv->active_rxon.filter_flags;
|
|
+ cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time);
|
|
+ if (ch_info)
|
|
+ cmd.expect_beacon = is_channel_radar(ch_info);
|
|
+ else
|
|
+ cmd.expect_beacon = 1;
|
|
+
|
|
+ rc = iwl4965_fill_txpower_tbl(priv, band, channel, is_fat,
|
|
+ ctrl_chan_high, &cmd.tx_power);
|
|
+ if (rc) {
|
|
+ IWL_DEBUG_11H("error:%d fill txpower_tbl\n", rc);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ rc = iwl_send_cmd_pdu(priv, REPLY_CHANNEL_SWITCH, sizeof(cmd), &cmd);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+#define RTS_HCCA_RETRY_LIMIT 3
|
|
+#define RTS_DFAULT_RETRY_LIMIT 60
|
|
+
|
|
+void iwl_hw_build_tx_cmd_rate(struct iwl_priv *priv,
|
|
+ struct iwl_cmd *cmd,
|
|
+ struct ieee80211_tx_control *ctrl,
|
|
+ struct ieee80211_hdr *hdr, int sta_id,
|
|
+ int is_hcca)
|
|
+{
|
|
+ u8 rate;
|
|
+ u8 rts_retry_limit = 0;
|
|
+ u8 data_retry_limit = 0;
|
|
+ __le32 tx_flags;
|
|
+ u16 fc = le16_to_cpu(hdr->frame_control);
|
|
+
|
|
+ tx_flags = cmd->cmd.tx.tx_flags;
|
|
+
|
|
+ rate = iwl_rates[ctrl->tx_rate].plcp;
|
|
+
|
|
+ rts_retry_limit = (is_hcca) ?
|
|
+ RTS_HCCA_RETRY_LIMIT : RTS_DFAULT_RETRY_LIMIT;
|
|
+
|
|
+ if (ieee80211_is_probe_response(fc)) {
|
|
+ data_retry_limit = 3;
|
|
+ if (data_retry_limit < rts_retry_limit)
|
|
+ rts_retry_limit = data_retry_limit;
|
|
+ } else
|
|
+ data_retry_limit = IWL_DEFAULT_TX_RETRY;
|
|
+
|
|
+ if (priv->data_retry_limit != -1)
|
|
+ data_retry_limit = priv->data_retry_limit;
|
|
+
|
|
+ if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) {
|
|
+ switch (fc & IEEE80211_FCTL_STYPE) {
|
|
+ case IEEE80211_STYPE_AUTH:
|
|
+ case IEEE80211_STYPE_DEAUTH:
|
|
+ case IEEE80211_STYPE_ASSOC_REQ:
|
|
+ case IEEE80211_STYPE_REASSOC_REQ:
|
|
+ if (tx_flags & TX_CMD_FLG_RTS_MSK) {
|
|
+ tx_flags &= ~TX_CMD_FLG_RTS_MSK;
|
|
+ tx_flags |= TX_CMD_FLG_CTS_MSK;
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ cmd->cmd.tx.rts_retry_limit = rts_retry_limit;
|
|
+ cmd->cmd.tx.data_retry_limit = data_retry_limit;
|
|
+ cmd->cmd.tx.rate_n_flags = iwl_hw_set_rate_n_flags(rate, 0);
|
|
+ cmd->cmd.tx.tx_flags = tx_flags;
|
|
+}
|
|
+
|
|
+int iwl_hw_get_rx_read(struct iwl_priv *priv)
|
|
+{
|
|
+ struct iwl_shared *shared_data = priv->hw_setting.shared_virt;
|
|
+
|
|
+ return IWL_GET_BITS(*shared_data, rb_closed_stts_rb_num);
|
|
+}
|
|
+
|
|
+int iwl_hw_get_temperature(struct iwl_priv *priv)
|
|
+{
|
|
+ return priv->temperature;
|
|
+}
|
|
+
|
|
+unsigned int iwl_hw_get_beacon_cmd(struct iwl_priv *priv,
|
|
+ struct iwl_frame *frame, u8 rate)
|
|
+{
|
|
+ struct iwl_tx_beacon_cmd *tx_beacon_cmd;
|
|
+ unsigned int frame_size;
|
|
+
|
|
+ tx_beacon_cmd = &frame->u.beacon;
|
|
+ memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd));
|
|
+
|
|
+ tx_beacon_cmd->tx.sta_id = IWL_BROADCAST_ID;
|
|
+ tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
|
|
+
|
|
+ frame_size = iwl_fill_beacon_frame(priv,
|
|
+ tx_beacon_cmd->frame,
|
|
+ BROADCAST_ADDR,
|
|
+ sizeof(frame->u) - sizeof(*tx_beacon_cmd));
|
|
+
|
|
+ BUG_ON(frame_size > MAX_MPDU_SIZE);
|
|
+ tx_beacon_cmd->tx.len = cpu_to_le16((u16)frame_size);
|
|
+
|
|
+ if ((rate == IWL_RATE_1M_PLCP) || (rate >= IWL_RATE_2M_PLCP))
|
|
+ tx_beacon_cmd->tx.rate_n_flags =
|
|
+ iwl_hw_set_rate_n_flags(rate, RATE_MCS_CCK_MSK);
|
|
+ else
|
|
+ tx_beacon_cmd->tx.rate_n_flags =
|
|
+ iwl_hw_set_rate_n_flags(rate, 0);
|
|
+
|
|
+ tx_beacon_cmd->tx.tx_flags = (TX_CMD_FLG_SEQ_CTL_MSK |
|
|
+ TX_CMD_FLG_TSF_MSK | TX_CMD_FLG_STA_RATE_MSK);
|
|
+ return (sizeof(*tx_beacon_cmd) + frame_size);
|
|
+}
|
|
+
|
|
+int iwl_hw_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq)
|
|
+{
|
|
+ int rc;
|
|
+ unsigned long flags;
|
|
+ int txq_id = txq->q.id;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc) {
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ iwl_write_restricted(priv, FH_MEM_CBBC_QUEUE(txq_id),
|
|
+ txq->q.dma_addr >> 8);
|
|
+ iwl_write_restricted(
|
|
+ priv, IWL_FH_TCSR_CHNL_TX_CONFIG_REG(txq_id),
|
|
+ IWL_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
|
|
+ IWL_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL);
|
|
+ iwl_release_restricted_access(priv);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline u8 iwl4965_get_dma_hi_address(dma_addr_t addr)
|
|
+{
|
|
+ return sizeof(addr) > sizeof(u32) ? (addr >> 16) >> 16 : 0;
|
|
+}
|
|
+
|
|
+int iwl_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv, void *ptr,
|
|
+ dma_addr_t addr, u16 len)
|
|
+{
|
|
+ int index, is_odd;
|
|
+ struct iwl_tfd_frame *tfd = ptr;
|
|
+ u32 num_tbs = IWL_GET_BITS(*tfd, num_tbs);
|
|
+
|
|
+ if ((num_tbs >= MAX_NUM_OF_TBS) || (num_tbs < 0)) {
|
|
+ IWL_ERROR("Error can not send more than %d chunks\n",
|
|
+ MAX_NUM_OF_TBS);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ index = num_tbs / 2;
|
|
+ is_odd = num_tbs & 0x1;
|
|
+
|
|
+ if (!is_odd) {
|
|
+ tfd->pa[index].tb1_addr = cpu_to_le32(addr);
|
|
+ IWL_SET_BITS(tfd->pa[index], tb1_addr_hi,
|
|
+ iwl4965_get_dma_hi_address(addr));
|
|
+ IWL_SET_BITS(tfd->pa[index], tb1_len, len);
|
|
+ } else {
|
|
+ IWL_SET_BITS(tfd->pa[index], tb2_addr_lo16,
|
|
+ (u32) (addr & 0xffff));
|
|
+ IWL_SET_BITS(tfd->pa[index], tb2_addr_hi20, addr >> 16);
|
|
+ IWL_SET_BITS(tfd->pa[index], tb2_len, len);
|
|
+ }
|
|
+
|
|
+ IWL_SET_BITS(*tfd, num_tbs, num_tbs + 1);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void iwl_hw_card_show_info(struct iwl_priv *priv)
|
|
+{
|
|
+ u16 hw_version = priv->eeprom.board_revision_4965;
|
|
+
|
|
+ IWL_DEBUG_INFO("4965ABGN HW Version %u.%u.%u\n",
|
|
+ ((hw_version >> 8) & 0x0F),
|
|
+ ((hw_version >> 8) >> 4), (hw_version & 0x00FF));
|
|
+
|
|
+ IWL_DEBUG_INFO("4965ABGN PBA Number %.16s\n",
|
|
+ priv->eeprom.board_pba_number_4965);
|
|
+}
|
|
+
|
|
+#define IWL_TX_CRC_SIZE 4
|
|
+#define IWL_TX_DELIMITER_SIZE 4
|
|
+
|
|
+int iwl4965_tx_queue_update_wr_ptr(struct iwl_priv *priv,
|
|
+ struct iwl_tx_queue *txq, u16 byte_cnt)
|
|
+{
|
|
+ int len;
|
|
+ int txq_id = txq->q.id;
|
|
+ struct iwl_shared *shared_data = priv->hw_setting.shared_virt;
|
|
+
|
|
+ if (txq->need_update == 0)
|
|
+ return 0;
|
|
+
|
|
+ len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE;
|
|
+
|
|
+ IWL_SET_BITS16(shared_data->queues_byte_cnt_tbls[txq_id].
|
|
+ tfd_offset[txq->q.first_empty], byte_cnt, len);
|
|
+
|
|
+ if (txq->q.first_empty < IWL4965_MAX_WIN_SIZE)
|
|
+ IWL_SET_BITS16(shared_data->queues_byte_cnt_tbls[txq_id].
|
|
+ tfd_offset[IWL4965_QUEUE_SIZE + txq->q.first_empty],
|
|
+ byte_cnt, len);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#define IWL4965_LEGACY_SWITCH_ANTENNA 0
|
|
+#define IWL4965_LECACY_SWITCH_SISO 1
|
|
+#define IWL4965_LEGACY_SWITCH_MIMO 2
|
|
+
|
|
+#define IWL4965_GOOD_RATIO 12800
|
|
+
|
|
+#define IWL_ACTION_LIMIT 3
|
|
+#define IWL4965_LEGACY_FAILURE_LIMIT 160
|
|
+#define IWL4965_LEGACY_SUCCESS_LIMIT 480
|
|
+#define IWL4965_LEGACY_TABLE_COUNT 160
|
|
+
|
|
+#define IWL4965_NONE_LEGACY_FAILURE_LIMIT 400
|
|
+#define IWL4965_NONE_LEGACY_SUCCESS_LIMIT 4500
|
|
+#define IWL4965_NONE_LEGACY_TABLE_COUNT 1500
|
|
+
|
|
+#define IWL4965_RATE_SCALE_SWITCH (10880)
|
|
+
|
|
+/* Set up Rx receiver/antenna/chain usage in "staging" RXON image.
|
|
+ * This should not be used for scan command ... it puts data in wrong place. */
|
|
+void iwl4965_set_rxon_chain(struct iwl_priv *priv)
|
|
+{
|
|
+ u8 is_single = is_single_stream(priv);
|
|
+ u8 idle_state, rx_state;
|
|
+
|
|
+ priv->staging_rxon.rx_chain = 0;
|
|
+ rx_state = idle_state = 3;
|
|
+
|
|
+ /* Tell uCode which antennas are actually connected.
|
|
+ * Before first association, we assume all antennas are connected.
|
|
+ * Just after first association, iwl4965_noise_calibration()
|
|
+ * checks which antennas actually *are* connected. */
|
|
+ priv->staging_rxon.rx_chain |=
|
|
+ cpu_to_le16(priv->valid_antenna << RXON_RX_CHAIN_VALID_POS);
|
|
+
|
|
+ /* How many receivers should we use? */
|
|
+ iwl4965_get_rx_chain_counter(priv, &idle_state, &rx_state);
|
|
+ priv->staging_rxon.rx_chain |=
|
|
+ cpu_to_le16(rx_state << RXON_RX_CHAIN_MIMO_CNT_POS);
|
|
+ priv->staging_rxon.rx_chain |=
|
|
+ cpu_to_le16(idle_state << RXON_RX_CHAIN_CNT_POS);
|
|
+
|
|
+ if (!is_single && (rx_state >= 2) &&
|
|
+ !test_bit(STATUS_POWER_PMI, &priv->status))
|
|
+ priv->staging_rxon.rx_chain |= RXON_RX_CHAIN_MIMO_FORCE_MSK;
|
|
+ else
|
|
+ priv->staging_rxon.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK;
|
|
+
|
|
+ IWL_DEBUG_ASSOC("rx chain %X\n", priv->staging_rxon.rx_chain);
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+/*
|
|
+ get the traffic load value for tid
|
|
+*/
|
|
+static u32 iwl4965_tl_get_load(struct iwl_priv *priv, u8 tid)
|
|
+{
|
|
+ u32 load = 0;
|
|
+ u32 current_time = jiffies_to_msecs(jiffies);
|
|
+ u32 time_diff;
|
|
+ s32 index;
|
|
+ unsigned long flags;
|
|
+ struct iwl_traffic_load *tid_ptr = NULL;
|
|
+
|
|
+ if (tid >= TID_MAX_LOAD_COUNT)
|
|
+ return 0;
|
|
+
|
|
+ tid_ptr = &(priv->lq_mngr.agg_ctrl.traffic_load[tid]);
|
|
+
|
|
+ current_time -= current_time % TID_ROUND_VALUE;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lq_mngr.lock, flags);
|
|
+ if (!(tid_ptr->queue_count))
|
|
+ goto out;
|
|
+
|
|
+ time_diff = TIME_WRAP_AROUND(tid_ptr->time_stamp, current_time);
|
|
+ index = time_diff / TID_QUEUE_CELL_SPACING;
|
|
+
|
|
+ if (index >= TID_QUEUE_MAX_SIZE) {
|
|
+ u32 oldest_time = current_time - TID_MAX_TIME_DIFF;
|
|
+
|
|
+ while (tid_ptr->queue_count &&
|
|
+ (tid_ptr->time_stamp < oldest_time)) {
|
|
+ tid_ptr->total -= tid_ptr->packet_count[tid_ptr->head];
|
|
+ tid_ptr->packet_count[tid_ptr->head] = 0;
|
|
+ tid_ptr->time_stamp += TID_QUEUE_CELL_SPACING;
|
|
+ tid_ptr->queue_count--;
|
|
+ tid_ptr->head++;
|
|
+ if (tid_ptr->head >= TID_QUEUE_MAX_SIZE)
|
|
+ tid_ptr->head = 0;
|
|
+ }
|
|
+ }
|
|
+ load = tid_ptr->total;
|
|
+
|
|
+ out:
|
|
+ spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
|
|
+ return load;
|
|
+}
|
|
+
|
|
+/*
|
|
+ increment traffic load value for tid and also remove
|
|
+ any old values if passed the certian time period
|
|
+*/
|
|
+static void iwl4965_tl_add_packet(struct iwl_priv *priv, u8 tid)
|
|
+{
|
|
+ u32 current_time = jiffies_to_msecs(jiffies);
|
|
+ u32 time_diff;
|
|
+ s32 index;
|
|
+ unsigned long flags;
|
|
+ struct iwl_traffic_load *tid_ptr = NULL;
|
|
+
|
|
+ if (tid >= TID_MAX_LOAD_COUNT)
|
|
+ return;
|
|
+
|
|
+ tid_ptr = &(priv->lq_mngr.agg_ctrl.traffic_load[tid]);
|
|
+
|
|
+ current_time -= current_time % TID_ROUND_VALUE;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lq_mngr.lock, flags);
|
|
+ if (!(tid_ptr->queue_count)) {
|
|
+ tid_ptr->total = 1;
|
|
+ tid_ptr->time_stamp = current_time;
|
|
+ tid_ptr->queue_count = 1;
|
|
+ tid_ptr->head = 0;
|
|
+ tid_ptr->packet_count[0] = 1;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ time_diff = TIME_WRAP_AROUND(tid_ptr->time_stamp, current_time);
|
|
+ index = time_diff / TID_QUEUE_CELL_SPACING;
|
|
+
|
|
+ if (index >= TID_QUEUE_MAX_SIZE) {
|
|
+ u32 oldest_time = current_time - TID_MAX_TIME_DIFF;
|
|
+
|
|
+ while (tid_ptr->queue_count &&
|
|
+ (tid_ptr->time_stamp < oldest_time)) {
|
|
+ tid_ptr->total -= tid_ptr->packet_count[tid_ptr->head];
|
|
+ tid_ptr->packet_count[tid_ptr->head] = 0;
|
|
+ tid_ptr->time_stamp += TID_QUEUE_CELL_SPACING;
|
|
+ tid_ptr->queue_count--;
|
|
+ tid_ptr->head++;
|
|
+ if (tid_ptr->head >= TID_QUEUE_MAX_SIZE)
|
|
+ tid_ptr->head = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ index = (tid_ptr->head + index) % TID_QUEUE_MAX_SIZE;
|
|
+ tid_ptr->packet_count[index] = tid_ptr->packet_count[index] + 1;
|
|
+ tid_ptr->total = tid_ptr->total + 1;
|
|
+
|
|
+ if ((index + 1) > tid_ptr->queue_count)
|
|
+ tid_ptr->queue_count = index + 1;
|
|
+ out:
|
|
+ spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
|
|
+
|
|
+}
|
|
+
|
|
+#define MMAC_SCHED_MAX_NUMBER_OF_HT_BACK_FLOWS 7
|
|
+enum HT_STATUS {
|
|
+ BA_STATUS_FAILURE = 0,
|
|
+ BA_STATUS_INITIATOR_DELBA,
|
|
+ BA_STATUS_RECIPIENT_DELBA,
|
|
+ BA_STATUS_RENEW_ADDBA_REQUEST,
|
|
+ BA_STATUS_ACTIVE,
|
|
+};
|
|
+
|
|
+static u8 iwl4964_tl_ba_avail(struct iwl_priv *priv)
|
|
+{
|
|
+ int i;
|
|
+ struct iwl_lq_mngr *lq;
|
|
+ u8 count = 0;
|
|
+ u16 msk;
|
|
+
|
|
+ lq = (struct iwl_lq_mngr *)&(priv->lq_mngr);
|
|
+ for (i = 0; i < TID_MAX_LOAD_COUNT ; i++) {
|
|
+ msk = 1 << i;
|
|
+ if ((lq->agg_ctrl.granted_ba & msk) ||
|
|
+ (lq->agg_ctrl.wait_for_agg_status & msk))
|
|
+ count++;
|
|
+ }
|
|
+
|
|
+ if (count < MMAC_SCHED_MAX_NUMBER_OF_HT_BACK_FLOWS)
|
|
+ return 1;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void iwl4965_ba_status(struct iwl_priv *priv,
|
|
+ u8 tid, enum HT_STATUS status);
|
|
+
|
|
+static int iwl4965_perform_addba(struct iwl_priv *priv, u8 tid, u32 length,
|
|
+ u32 ba_timeout)
|
|
+{
|
|
+ int rc;
|
|
+
|
|
+ rc = ieee80211_start_BA_session(priv->hw, priv->bssid, tid);
|
|
+ if (rc)
|
|
+ iwl4965_ba_status(priv, tid, BA_STATUS_FAILURE);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int iwl4965_perform_delba(struct iwl_priv *priv, u8 tid)
|
|
+{
|
|
+ int rc;
|
|
+
|
|
+ rc = ieee80211_stop_BA_session(priv->hw, priv->bssid, tid);
|
|
+ if (rc)
|
|
+ iwl4965_ba_status(priv, tid, BA_STATUS_FAILURE);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static void iwl4965_turn_on_agg_for_tid(struct iwl_priv *priv,
|
|
+ struct iwl_lq_mngr *lq,
|
|
+ u8 auto_agg, u8 tid)
|
|
+{
|
|
+ u32 tid_msk = (1 << tid);
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lq_mngr.lock, flags);
|
|
+/*
|
|
+ if ((auto_agg) && (!lq->enable_counter)){
|
|
+ lq->agg_ctrl.next_retry = 0;
|
|
+ lq->agg_ctrl.tid_retry = 0;
|
|
+ spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
|
|
+ return;
|
|
+ }
|
|
+*/
|
|
+ if (!(lq->agg_ctrl.granted_ba & tid_msk) &&
|
|
+ (lq->agg_ctrl.requested_ba & tid_msk)) {
|
|
+ u8 available_queues;
|
|
+ u32 load;
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
|
|
+ available_queues = iwl4964_tl_ba_avail(priv);
|
|
+ load = iwl4965_tl_get_load(priv, tid);
|
|
+
|
|
+ spin_lock_irqsave(&priv->lq_mngr.lock, flags);
|
|
+ if (!available_queues) {
|
|
+ if (auto_agg)
|
|
+ lq->agg_ctrl.tid_retry |= tid_msk;
|
|
+ else {
|
|
+ lq->agg_ctrl.requested_ba &= ~tid_msk;
|
|
+ lq->agg_ctrl.wait_for_agg_status &= ~tid_msk;
|
|
+ }
|
|
+ } else if ((auto_agg) &&
|
|
+ ((load <= lq->agg_ctrl.tid_traffic_load_threshold) ||
|
|
+ ((lq->agg_ctrl.wait_for_agg_status & tid_msk))))
|
|
+ lq->agg_ctrl.tid_retry |= tid_msk;
|
|
+ else {
|
|
+ lq->agg_ctrl.wait_for_agg_status |= tid_msk;
|
|
+ spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
|
|
+ iwl4965_perform_addba(priv, tid, 0x40,
|
|
+ lq->agg_ctrl.ba_timeout);
|
|
+ spin_lock_irqsave(&priv->lq_mngr.lock, flags);
|
|
+ }
|
|
+ }
|
|
+ spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
|
|
+}
|
|
+
|
|
+static void iwl4965_turn_on_agg(struct iwl_priv *priv, u8 tid)
|
|
+{
|
|
+ struct iwl_lq_mngr *lq;
|
|
+ unsigned long flags;
|
|
+
|
|
+ lq = (struct iwl_lq_mngr *)&(priv->lq_mngr);
|
|
+
|
|
+ if ((tid < TID_MAX_LOAD_COUNT))
|
|
+ iwl4965_turn_on_agg_for_tid(priv, lq, lq->agg_ctrl.auto_agg,
|
|
+ tid);
|
|
+ else if (tid == TID_ALL_SPECIFIED) {
|
|
+ if (lq->agg_ctrl.requested_ba) {
|
|
+ for (tid = 0; tid < TID_MAX_LOAD_COUNT; tid++)
|
|
+ iwl4965_turn_on_agg_for_tid(priv, lq,
|
|
+ lq->agg_ctrl.auto_agg, tid);
|
|
+ } else {
|
|
+ spin_lock_irqsave(&priv->lq_mngr.lock, flags);
|
|
+ lq->agg_ctrl.tid_retry = 0;
|
|
+ lq->agg_ctrl.next_retry = 0;
|
|
+ spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
|
|
+ }
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+void iwl4965_turn_off_agg(struct iwl_priv *priv, u8 tid)
|
|
+{
|
|
+ u32 tid_msk;
|
|
+ struct iwl_lq_mngr *lq;
|
|
+ unsigned long flags;
|
|
+
|
|
+ lq = (struct iwl_lq_mngr *)&(priv->lq_mngr);
|
|
+
|
|
+ if ((tid < TID_MAX_LOAD_COUNT)) {
|
|
+ tid_msk = 1 << tid;
|
|
+ spin_lock_irqsave(&priv->lq_mngr.lock, flags);
|
|
+ lq->agg_ctrl.wait_for_agg_status |= tid_msk;
|
|
+ lq->agg_ctrl.requested_ba &= ~tid_msk;
|
|
+ spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
|
|
+ iwl4965_perform_delba(priv, tid);
|
|
+ } else if (tid == TID_ALL_SPECIFIED) {
|
|
+ spin_lock_irqsave(&priv->lq_mngr.lock, flags);
|
|
+ for (tid = 0; tid < TID_MAX_LOAD_COUNT; tid++) {
|
|
+ tid_msk = 1 << tid;
|
|
+ lq->agg_ctrl.wait_for_agg_status |= tid_msk;
|
|
+ spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
|
|
+ iwl4965_perform_delba(priv, tid);
|
|
+ spin_lock_irqsave(&priv->lq_mngr.lock, flags);
|
|
+ }
|
|
+ lq->agg_ctrl.requested_ba = 0;
|
|
+ spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void iwl4965_ba_status(struct iwl_priv *priv,
|
|
+ u8 tid, enum HT_STATUS status)
|
|
+{
|
|
+ struct iwl_lq_mngr *lq;
|
|
+ u32 tid_msk = (1 << tid);
|
|
+ unsigned long flags;
|
|
+
|
|
+ lq = (struct iwl_lq_mngr *)&(priv->lq_mngr);
|
|
+
|
|
+ if ((tid >= TID_MAX_LOAD_COUNT))
|
|
+ goto out;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lq_mngr.lock, flags);
|
|
+ switch (status) {
|
|
+ case BA_STATUS_ACTIVE:
|
|
+ if (!(lq->agg_ctrl.granted_ba & tid_msk))
|
|
+ lq->agg_ctrl.granted_ba |= tid_msk;
|
|
+ break;
|
|
+ default:
|
|
+ if ((lq->agg_ctrl.granted_ba & tid_msk))
|
|
+ lq->agg_ctrl.granted_ba &= ~tid_msk;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ lq->agg_ctrl.wait_for_agg_status &= ~tid_msk;
|
|
+ if (status != BA_STATUS_ACTIVE) {
|
|
+ if (lq->agg_ctrl.auto_agg) {
|
|
+ lq->agg_ctrl.tid_retry |= tid_msk;
|
|
+ lq->agg_ctrl.next_retry =
|
|
+ jiffies + msecs_to_jiffies(500);
|
|
+ } else
|
|
+ lq->agg_ctrl.requested_ba &= ~tid_msk;
|
|
+ }
|
|
+ spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
|
|
+ out:
|
|
+ return;
|
|
+}
|
|
+
|
|
+static void iwl4965_bg_agg_work(struct work_struct *work)
|
|
+{
|
|
+ struct iwl_priv *priv = container_of(work, struct iwl_priv,
|
|
+ agg_work);
|
|
+
|
|
+ u32 tid;
|
|
+ u32 retry_tid;
|
|
+ u32 tid_msk;
|
|
+ unsigned long flags;
|
|
+ struct iwl_lq_mngr *lq = (struct iwl_lq_mngr *)&(priv->lq_mngr);
|
|
+
|
|
+ spin_lock_irqsave(&priv->lq_mngr.lock, flags);
|
|
+ retry_tid = lq->agg_ctrl.tid_retry;
|
|
+ lq->agg_ctrl.tid_retry = 0;
|
|
+ spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
|
|
+
|
|
+ if (retry_tid == TID_ALL_SPECIFIED)
|
|
+ iwl4965_turn_on_agg(priv, TID_ALL_SPECIFIED);
|
|
+ else {
|
|
+ for (tid = 0; tid < TID_MAX_LOAD_COUNT; tid++) {
|
|
+ tid_msk = (1 << tid);
|
|
+ if (retry_tid & tid_msk)
|
|
+ iwl4965_turn_on_agg(priv, tid);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ spin_lock_irqsave(&priv->lq_mngr.lock, flags);
|
|
+ if (lq->agg_ctrl.tid_retry)
|
|
+ lq->agg_ctrl.next_retry = jiffies + msecs_to_jiffies(500);
|
|
+ spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
|
|
+ return;
|
|
+}
|
|
+#endif /*CONFIG_IWLWIFI_HT_AGG */
|
|
+#endif /* CONFIG_IWLWIFI_HT */
|
|
+
|
|
+int iwl4965_tx_cmd(struct iwl_priv *priv, struct iwl_cmd *out_cmd,
|
|
+ u8 sta_id, dma_addr_t txcmd_phys,
|
|
+ struct ieee80211_hdr *hdr, u8 hdr_len,
|
|
+ struct ieee80211_tx_control *ctrl, void *sta_in)
|
|
+{
|
|
+ struct iwl_tx_cmd cmd;
|
|
+ struct iwl_tx_cmd *tx = (struct iwl_tx_cmd *)&out_cmd->cmd.payload[0];
|
|
+ dma_addr_t scratch_phys;
|
|
+ u8 unicast = 0;
|
|
+ u8 is_data = 1;
|
|
+ u16 fc;
|
|
+ u16 rate_flags;
|
|
+ int rate_index = min(ctrl->tx_rate & 0xffff, IWL_RATE_COUNT - 1);
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+ __le16 *qc;
|
|
+#endif /*CONFIG_IWLWIFI_HT_AGG */
|
|
+#endif /* CONFIG_IWLWIFI_HT */
|
|
+
|
|
+ unicast = !is_multicast_ether_addr(hdr->addr1);
|
|
+
|
|
+ fc = le16_to_cpu(hdr->frame_control);
|
|
+ if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)
|
|
+ is_data = 0;
|
|
+
|
|
+ memcpy(&cmd, &(out_cmd->cmd.tx), sizeof(struct iwl_tx_cmd));
|
|
+ memset(tx, 0, sizeof(struct iwl_tx_cmd));
|
|
+ memcpy(tx->hdr, hdr, hdr_len);
|
|
+
|
|
+ tx->len = cmd.len;
|
|
+ tx->driver_txop = cmd.driver_txop;
|
|
+ tx->stop_time.life_time = cmd.stop_time.life_time;
|
|
+ tx->tx_flags = cmd.tx_flags;
|
|
+ tx->sta_id = cmd.sta_id;
|
|
+ tx->tid_tspec = cmd.tid_tspec;
|
|
+ tx->timeout.pm_frame_timeout = cmd.timeout.pm_frame_timeout;
|
|
+ tx->next_frame_len = cmd.next_frame_len;
|
|
+
|
|
+ tx->sec_ctl = cmd.sec_ctl;
|
|
+ memcpy(&(tx->key[0]), &(cmd.key[0]), 16);
|
|
+ tx->tx_flags = cmd.tx_flags;
|
|
+
|
|
+ tx->rts_retry_limit = cmd.rts_retry_limit;
|
|
+ tx->data_retry_limit = cmd.data_retry_limit;
|
|
+
|
|
+ scratch_phys = txcmd_phys + sizeof(struct iwl_cmd_header) +
|
|
+ offsetof(struct iwl_tx_cmd, scratch);
|
|
+ tx->dram_lsb_ptr = cpu_to_le32(scratch_phys);
|
|
+ tx->dram_msb_ptr = iwl4965_get_dma_hi_address(scratch_phys);
|
|
+
|
|
+ /* Hard coded to start at the highest retry fallback position
|
|
+ * until the 4965 specific rate control algorithm is tied in */
|
|
+ tx->initial_rate_index = LINK_QUAL_MAX_RETRY_NUM - 1;
|
|
+
|
|
+ /* Alternate between antenna A and B for successive frames */
|
|
+ if (priv->use_ant_b_for_management_frame) {
|
|
+ priv->use_ant_b_for_management_frame = 0;
|
|
+ rate_flags = RATE_MCS_ANT_B_MSK;
|
|
+ } else {
|
|
+ priv->use_ant_b_for_management_frame = 1;
|
|
+ rate_flags = RATE_MCS_ANT_A_MSK;
|
|
+ }
|
|
+
|
|
+ if (!unicast || !is_data) {
|
|
+ if ((rate_index >= IWL_FIRST_CCK_RATE) &&
|
|
+ (rate_index <= IWL_LAST_CCK_RATE))
|
|
+ rate_flags |= RATE_MCS_CCK_MSK;
|
|
+ } else {
|
|
+ tx->initial_rate_index = 0;
|
|
+ tx->tx_flags |= TX_CMD_FLG_STA_RATE_MSK;
|
|
+ }
|
|
+
|
|
+ tx->rate_n_flags = iwl_hw_set_rate_n_flags(iwl_rates[rate_index].plcp,
|
|
+ rate_flags);
|
|
+
|
|
+ if (ieee80211_is_probe_request(fc))
|
|
+ tx->tx_flags |= TX_CMD_FLG_TSF_MSK;
|
|
+ else if (ieee80211_is_back_request(fc))
|
|
+ tx->tx_flags |= TX_CMD_FLG_ACK_MSK |
|
|
+ TX_CMD_FLG_IMM_BA_RSP_MASK;
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+ qc = ieee80211_get_qos_ctrl(hdr);
|
|
+ if (qc &&
|
|
+ (priv->iw_mode != IEEE80211_IF_TYPE_IBSS)) {
|
|
+ u8 tid = 0;
|
|
+ tid = (u8) (le16_to_cpu(*qc) & 0xF);
|
|
+ if (tid < TID_MAX_LOAD_COUNT)
|
|
+ iwl4965_tl_add_packet(priv, tid);
|
|
+ }
|
|
+
|
|
+ if (priv->lq_mngr.agg_ctrl.next_retry &&
|
|
+ (time_after(priv->lq_mngr.agg_ctrl.next_retry, jiffies))) {
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lq_mngr.lock, flags);
|
|
+ priv->lq_mngr.agg_ctrl.next_retry = 0;
|
|
+ spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
|
|
+ schedule_work(&priv->agg_work);
|
|
+ }
|
|
+#endif
|
|
+#endif
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl4965_sign_extend - Sign extend a value using specified bit as sign-bit
|
|
+ *
|
|
+ * Example: sign_extend(9, 3) would return -7 as bit3 of 1001b is 1
|
|
+ * and bit0..2 is 001b which when sign extended to 1111111111111001b is -7.
|
|
+ *
|
|
+ * @param oper value to sign extend
|
|
+ * @param index 0 based bit index (0<=index<32) to sign bit
|
|
+ */
|
|
+static s32 iwl4965_sign_extend(u32 oper, int index)
|
|
+{
|
|
+ u32 bit;
|
|
+ u32 mask;
|
|
+
|
|
+ /* If the index is the MSB or higher then just return the
|
|
+ * operand cast to a signed value */
|
|
+ if (index > 30)
|
|
+ return oper;
|
|
+
|
|
+ bit = 1 << index;
|
|
+ mask = ~(bit - 1);
|
|
+
|
|
+ /* negative -- sign extend */
|
|
+ if (oper & bit)
|
|
+ return oper |= mask;
|
|
+
|
|
+ /* positive -- sign clear */
|
|
+ return oper &= ~mask;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl4965_get_temperature - return the calibrated temperature (in Kelvin)
|
|
+ * @statistics: Provides the temperature reading from the uCode
|
|
+ *
|
|
+ * A return of <0 indicates bogus data in the statistics
|
|
+ */
|
|
+int iwl4965_get_temperature(const struct iwl_priv *priv)
|
|
+{
|
|
+ s32 temperature;
|
|
+ s32 vt;
|
|
+ s32 R1, R2, R3;
|
|
+ u32 R4;
|
|
+
|
|
+ if (test_bit(STATUS_TEMPERATURE, &priv->status) &&
|
|
+ (priv->statistics.flag & STATISTICS_REPLY_FLG_FAT_MODE_MSK)) {
|
|
+ IWL_DEBUG_TEMP("Running FAT temperature calibration\n");
|
|
+ R1 = (s32)le32_to_cpu(priv->card_alive_init.therm_r1[1]);
|
|
+ R2 = (s32)le32_to_cpu(priv->card_alive_init.therm_r2[1]);
|
|
+ R3 = (s32)le32_to_cpu(priv->card_alive_init.therm_r3[1]);
|
|
+ R4 = le32_to_cpu(priv->card_alive_init.therm_r4[1]);
|
|
+ } else {
|
|
+ IWL_DEBUG_TEMP("Running temperature calibration\n");
|
|
+ R1 = (s32)le32_to_cpu(priv->card_alive_init.therm_r1[0]);
|
|
+ R2 = (s32)le32_to_cpu(priv->card_alive_init.therm_r2[0]);
|
|
+ R3 = (s32)le32_to_cpu(priv->card_alive_init.therm_r3[0]);
|
|
+ R4 = le32_to_cpu(priv->card_alive_init.therm_r4[0]);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Temperature is only 23 bits so sign extend out to 32
|
|
+ *
|
|
+ * NOTE If we haven't received a statistics notification yet
|
|
+ * with an updated temperature, use R4 provided to us in the
|
|
+ * ALIVE response. */
|
|
+ if (!test_bit(STATUS_TEMPERATURE, &priv->status))
|
|
+ vt = iwl4965_sign_extend(R4, 23);
|
|
+ else
|
|
+ vt = iwl4965_sign_extend(
|
|
+ le32_to_cpu(priv->statistics.general.temperature), 23);
|
|
+
|
|
+ IWL_DEBUG_TEMP("Calib values R[1-3]: %d %d %d R4: %d\n",
|
|
+ R1, R2, R3, vt);
|
|
+
|
|
+ if (R3 == R1) {
|
|
+ IWL_ERROR("Calibration conflict R1 == R3\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ /* Calculate temperature in degrees Kelvin, adjust by 97%.
|
|
+ * Add offset to center the adjustment around 0 degrees Centigrade. */
|
|
+ temperature = TEMPERATURE_CALIB_A_VAL * (vt - R2);
|
|
+ temperature /= (R3 - R1);
|
|
+ temperature = (temperature * 97) / 100 +
|
|
+ TEMPERATURE_CALIB_KELVIN_OFFSET;
|
|
+
|
|
+ IWL_DEBUG_TEMP("Calibrated temperature: %dK, %dC\n", temperature,
|
|
+ KELVIN_TO_CELSIUS(temperature));
|
|
+
|
|
+ return temperature;
|
|
+}
|
|
+
|
|
+/* Adjust Txpower only if temperature variance is greater than threshold. */
|
|
+#define IWL_TEMPERATURE_THRESHOLD 3
|
|
+
|
|
+/**
|
|
+ * iwl4965_is_temp_calib_needed - determines if new calibration is needed
|
|
+ *
|
|
+ * If the temperature changed has changed sufficiently, then a recalibration
|
|
+ * is needed.
|
|
+ *
|
|
+ * Assumes caller will replace priv->last_temperature once calibration
|
|
+ * executed.
|
|
+ */
|
|
+static int iwl4965_is_temp_calib_needed(struct iwl_priv *priv)
|
|
+{
|
|
+ int temp_diff;
|
|
+
|
|
+ if (!test_bit(STATUS_STATISTICS, &priv->status)) {
|
|
+ IWL_DEBUG_TEMP("Temperature not updated -- no statistics.\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ temp_diff = priv->temperature - priv->last_temperature;
|
|
+
|
|
+ /* get absolute value */
|
|
+ if (temp_diff < 0) {
|
|
+ IWL_DEBUG_POWER("Getting cooler, delta %d, \n", temp_diff);
|
|
+ temp_diff = -temp_diff;
|
|
+ } else if (temp_diff == 0)
|
|
+ IWL_DEBUG_POWER("Same temp, \n");
|
|
+ else
|
|
+ IWL_DEBUG_POWER("Getting warmer, delta %d, \n", temp_diff);
|
|
+
|
|
+ if (temp_diff < IWL_TEMPERATURE_THRESHOLD) {
|
|
+ IWL_DEBUG_POWER("Thermal txpower calib not needed\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_POWER("Thermal txpower calib needed\n");
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/* Calculate noise level, based on measurements during network silence just
|
|
+ * before arriving beacon. This measurement can be done only if we know
|
|
+ * exactly when to expect beacons, therefore only when we're associated. */
|
|
+static void iwl4965_rx_calc_noise(struct iwl_priv *priv)
|
|
+{
|
|
+ struct statistics_rx_non_phy *rx_info
|
|
+ = &(priv->statistics.rx.general);
|
|
+ int num_active_rx = 0;
|
|
+ int total_silence = 0;
|
|
+ int bcn_silence_a =
|
|
+ le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER;
|
|
+ int bcn_silence_b =
|
|
+ le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER;
|
|
+ int bcn_silence_c =
|
|
+ le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER;
|
|
+
|
|
+ if (bcn_silence_a) {
|
|
+ total_silence += bcn_silence_a;
|
|
+ num_active_rx++;
|
|
+ }
|
|
+ if (bcn_silence_b) {
|
|
+ total_silence += bcn_silence_b;
|
|
+ num_active_rx++;
|
|
+ }
|
|
+ if (bcn_silence_c) {
|
|
+ total_silence += bcn_silence_c;
|
|
+ num_active_rx++;
|
|
+ }
|
|
+
|
|
+ /* Average among active antennas */
|
|
+ if (num_active_rx)
|
|
+ priv->last_rx_noise = (total_silence / num_active_rx) - 107;
|
|
+ else
|
|
+ priv->last_rx_noise = IWL_NOISE_MEAS_NOT_AVAILABLE;
|
|
+
|
|
+ IWL_DEBUG_CALIB("inband silence a %u, b %u, c %u, dBm %d\n",
|
|
+ bcn_silence_a, bcn_silence_b, bcn_silence_c,
|
|
+ priv->last_rx_noise);
|
|
+}
|
|
+
|
|
+void iwl_hw_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
|
|
+{
|
|
+ struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
|
|
+ int change;
|
|
+ s32 temp;
|
|
+
|
|
+ IWL_DEBUG_RX("Statistics notification received (%d vs %d).\n",
|
|
+ (int)sizeof(priv->statistics), pkt->len);
|
|
+
|
|
+ change = ((priv->statistics.general.temperature !=
|
|
+ pkt->u.stats.general.temperature) ||
|
|
+ ((priv->statistics.flag &
|
|
+ STATISTICS_REPLY_FLG_FAT_MODE_MSK) !=
|
|
+ (pkt->u.stats.flag & STATISTICS_REPLY_FLG_FAT_MODE_MSK)));
|
|
+
|
|
+ memcpy(&priv->statistics, &pkt->u.stats, sizeof(priv->statistics));
|
|
+
|
|
+ set_bit(STATUS_STATISTICS, &priv->status);
|
|
+
|
|
+ /* Reschedule the statistics timer to occur in
|
|
+ * REG_RECALIB_PERIOD seconds to ensure we get a
|
|
+ * thermal update even if the uCode doesn't give
|
|
+ * us one */
|
|
+ mod_timer(&priv->statistics_periodic, jiffies +
|
|
+ msecs_to_jiffies(REG_RECALIB_PERIOD * 1000));
|
|
+
|
|
+ if (unlikely(!test_bit(STATUS_SCANNING, &priv->status)) &&
|
|
+ (pkt->hdr.cmd == STATISTICS_NOTIFICATION)) {
|
|
+ iwl4965_rx_calc_noise(priv);
|
|
+#ifdef CONFIG_IWLWIFI_SENSITIVITY
|
|
+ queue_work(priv->workqueue, &priv->sensitivity_work);
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ /* If the hardware hasn't reported a change in
|
|
+ * temperature then don't bother computing a
|
|
+ * calibrated temperature value */
|
|
+ if (!change)
|
|
+ return;
|
|
+
|
|
+ temp = iwl4965_get_temperature(priv);
|
|
+ if (temp < 0)
|
|
+ return;
|
|
+
|
|
+ if (priv->temperature != temp) {
|
|
+ if (priv->temperature)
|
|
+ IWL_DEBUG_TEMP("Temperature changed "
|
|
+ "from %dC to %dC\n",
|
|
+ KELVIN_TO_CELSIUS(priv->temperature),
|
|
+ KELVIN_TO_CELSIUS(temp));
|
|
+ else
|
|
+ IWL_DEBUG_TEMP("Temperature "
|
|
+ "initialized to %dC\n",
|
|
+ KELVIN_TO_CELSIUS(temp));
|
|
+ }
|
|
+
|
|
+ priv->temperature = temp;
|
|
+ set_bit(STATUS_TEMPERATURE, &priv->status);
|
|
+
|
|
+ if (unlikely(!test_bit(STATUS_SCANNING, &priv->status)) &&
|
|
+ iwl4965_is_temp_calib_needed(priv))
|
|
+ queue_work(priv->workqueue, &priv->txpower_work);
|
|
+}
|
|
+
|
|
+static void iwl4965_handle_data_packet(struct iwl_priv *priv, int is_data,
|
|
+ int include_phy,
|
|
+ struct iwl_rx_mem_buffer *rxb,
|
|
+ struct ieee80211_rx_status *stats)
|
|
+{
|
|
+ struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
|
|
+ struct iwl4965_rx_phy_res *rx_start = (include_phy) ?
|
|
+ (struct iwl4965_rx_phy_res *)&(pkt->u.raw[0]) : NULL;
|
|
+ struct ieee80211_hdr *hdr;
|
|
+ u16 len;
|
|
+ __le32 *rx_end;
|
|
+ unsigned int skblen;
|
|
+ u32 ampdu_status;
|
|
+
|
|
+ if (!include_phy && priv->last_phy_res[0])
|
|
+ rx_start = (struct iwl4965_rx_phy_res *)&priv->last_phy_res[1];
|
|
+
|
|
+ if (!rx_start) {
|
|
+ IWL_ERROR("MPDU frame without a PHY data\n");
|
|
+ return;
|
|
+ }
|
|
+ if (include_phy) {
|
|
+ hdr = (struct ieee80211_hdr *)((u8 *) & rx_start[1] +
|
|
+ rx_start->cfg_phy_cnt);
|
|
+
|
|
+ len = le16_to_cpu(rx_start->byte_count);
|
|
+
|
|
+ rx_end = (__le32 *) ((u8 *) & pkt->u.raw[0] +
|
|
+ sizeof(struct iwl4965_rx_phy_res) +
|
|
+ rx_start->cfg_phy_cnt + len);
|
|
+
|
|
+ } else {
|
|
+ struct iwl4965_rx_mpdu_res_start *amsdu =
|
|
+ (struct iwl4965_rx_mpdu_res_start *)pkt->u.raw;
|
|
+
|
|
+ hdr = (struct ieee80211_hdr *)(pkt->u.raw +
|
|
+ sizeof(struct iwl4965_rx_mpdu_res_start));
|
|
+ len = le16_to_cpu(amsdu->byte_count);
|
|
+ rx_start->byte_count = amsdu->byte_count;
|
|
+ rx_end = (__le32 *) (((u8 *) hdr) + len);
|
|
+ }
|
|
+ if (len > 2342 || len < 16) {
|
|
+ IWL_DEBUG_DROP("byte count out of range [16,2342]"
|
|
+ " : %d\n", len);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ampdu_status = le32_to_cpu(*rx_end);
|
|
+ skblen = ((u8 *) rx_end - (u8 *) & pkt->u.raw[0]) + sizeof(u32);
|
|
+
|
|
+ /* start from MAC */
|
|
+ skb_reserve(rxb->skb, (void *)hdr - (void *)pkt);
|
|
+ skb_put(rxb->skb, len); /* end where data ends */
|
|
+
|
|
+ /* We only process data packets if the interface is open */
|
|
+ if (unlikely(!priv->is_open)) {
|
|
+ IWL_DEBUG_DROP_LIMIT
|
|
+ ("Dropping packet while interface is not open.\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) {
|
|
+ if (iwl_param_hwcrypto)
|
|
+ iwl_set_decrypted_flag(priv, rxb->skb,
|
|
+ ampdu_status, stats);
|
|
+ iwl_handle_data_packet_monitor(priv, rxb, hdr, len, stats, 0);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ stats->flag = 0;
|
|
+ hdr = (struct ieee80211_hdr *)rxb->skb->data;
|
|
+
|
|
+ if (iwl_param_hwcrypto)
|
|
+ iwl_set_decrypted_flag(priv, rxb->skb, ampdu_status, stats);
|
|
+
|
|
+ ieee80211_rx_irqsafe(priv->hw, rxb->skb, stats);
|
|
+ priv->alloc_rxb_skb--;
|
|
+ rxb->skb = NULL;
|
|
+#ifdef LED
|
|
+ priv->led_packets += len;
|
|
+ iwl_setup_activity_timer(priv);
|
|
+#endif
|
|
+}
|
|
+
|
|
+/* Calc max signal level (dBm) among 3 possible receivers */
|
|
+static int iwl4965_calc_rssi(struct iwl4965_rx_phy_res *rx_resp)
|
|
+{
|
|
+ /* data from PHY/DSP regarding signal strength, etc.,
|
|
+ * contents are always there, not configurable by host. */
|
|
+ struct iwl4965_rx_non_cfg_phy *ncphy =
|
|
+ (struct iwl4965_rx_non_cfg_phy *)rx_resp->non_cfg_phy;
|
|
+ u32 agc = (le16_to_cpu(ncphy->agc_info) & IWL_AGC_DB_MASK)
|
|
+ >> IWL_AGC_DB_POS;
|
|
+
|
|
+ u32 valid_antennae =
|
|
+ (le16_to_cpu(rx_resp->phy_flags) & RX_PHY_FLAGS_ANTENNAE_MASK)
|
|
+ >> RX_PHY_FLAGS_ANTENNAE_OFFSET;
|
|
+ u8 max_rssi = 0;
|
|
+ u32 i;
|
|
+
|
|
+ /* Find max rssi among 3 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. */
|
|
+ for (i = 0; i < 3; i++)
|
|
+ if (valid_antennae & (1 << i))
|
|
+ max_rssi = max(ncphy->rssi_info[i << 1], max_rssi);
|
|
+
|
|
+ IWL_DEBUG_STATS("Rssi In A %d B %d C %d Max %d AGC dB %d\n",
|
|
+ ncphy->rssi_info[0], ncphy->rssi_info[2], ncphy->rssi_info[4],
|
|
+ max_rssi, agc);
|
|
+
|
|
+ /* dBm = max_rssi dB - agc dB - constant.
|
|
+ * Higher AGC (higher radio gain) means lower signal. */
|
|
+ return (max_rssi - agc - IWL_RSSI_OFFSET);
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+
|
|
+/* Parsed Information Elements */
|
|
+struct ieee802_11_elems {
|
|
+ u8 *ds_params;
|
|
+ u8 ds_params_len;
|
|
+ u8 *tim;
|
|
+ u8 tim_len;
|
|
+ u8 *ibss_params;
|
|
+ u8 ibss_params_len;
|
|
+ u8 *erp_info;
|
|
+ u8 erp_info_len;
|
|
+ u8 *ht_cap_param;
|
|
+ u8 ht_cap_param_len;
|
|
+ u8 *ht_extra_param;
|
|
+ u8 ht_extra_param_len;
|
|
+};
|
|
+
|
|
+static int parse_elems(u8 *start, size_t len, struct ieee802_11_elems *elems)
|
|
+{
|
|
+ size_t left = len;
|
|
+ u8 *pos = start;
|
|
+ int unknown = 0;
|
|
+
|
|
+ memset(elems, 0, sizeof(*elems));
|
|
+
|
|
+ while (left >= 2) {
|
|
+ u8 id, elen;
|
|
+
|
|
+ id = *pos++;
|
|
+ elen = *pos++;
|
|
+ left -= 2;
|
|
+
|
|
+ if (elen > left)
|
|
+ return -1;
|
|
+
|
|
+ switch (id) {
|
|
+ case WLAN_EID_DS_PARAMS:
|
|
+ elems->ds_params = pos;
|
|
+ elems->ds_params_len = elen;
|
|
+ break;
|
|
+ case WLAN_EID_TIM:
|
|
+ elems->tim = pos;
|
|
+ elems->tim_len = elen;
|
|
+ break;
|
|
+ case WLAN_EID_IBSS_PARAMS:
|
|
+ elems->ibss_params = pos;
|
|
+ elems->ibss_params_len = elen;
|
|
+ break;
|
|
+ case WLAN_EID_ERP_INFO:
|
|
+ elems->erp_info = pos;
|
|
+ elems->erp_info_len = elen;
|
|
+ break;
|
|
+ case WLAN_EID_HT_CAPABILITY:
|
|
+ elems->ht_cap_param = pos;
|
|
+ elems->ht_cap_param_len = elen;
|
|
+ break;
|
|
+ case WLAN_EID_HT_EXTRA_INFO:
|
|
+ elems->ht_extra_param = pos;
|
|
+ elems->ht_extra_param_len = elen;
|
|
+ break;
|
|
+ default:
|
|
+ unknown++;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ left -= elen;
|
|
+ pos += elen;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+#endif /* CONFIG_IWLWIFI_HT */
|
|
+
|
|
+static void iwl4965_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id)
|
|
+{
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&priv->sta_lock, flags);
|
|
+ priv->stations[sta_id].sta.station_flags &= ~STA_FLG_PWR_SAVE_MSK;
|
|
+ priv->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK;
|
|
+ priv->stations[sta_id].sta.sta.modify_mask = 0;
|
|
+ priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
|
|
+ spin_unlock_irqrestore(&priv->sta_lock, flags);
|
|
+
|
|
+ iwl_send_add_station(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
|
|
+}
|
|
+
|
|
+static void iwl4965_update_ps_mode(struct iwl_priv *priv, u16 ps_bit, u8 *addr)
|
|
+{
|
|
+ /* FIXME: need locking over ps_status ??? */
|
|
+ u8 sta_id = iwl_hw_find_station(priv, addr);
|
|
+
|
|
+ if (sta_id != IWL_INVALID_STATION) {
|
|
+ u8 sta_awake = priv->stations[sta_id].
|
|
+ ps_status == STA_PS_STATUS_WAKE;
|
|
+
|
|
+ if (sta_awake && ps_bit)
|
|
+ priv->stations[sta_id].ps_status = STA_PS_STATUS_SLEEP;
|
|
+ else if (!sta_awake && !ps_bit) {
|
|
+ iwl4965_sta_modify_ps_wake(priv, sta_id);
|
|
+ priv->stations[sta_id].ps_status = STA_PS_STATUS_WAKE;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Called for REPLY_4965_RX (legacy ABG frames), or
|
|
+ * REPLY_RX_MPDU_CMD (HT high-throughput N frames). */
|
|
+static void iwl4965_rx_reply_rx(struct iwl_priv *priv,
|
|
+ struct iwl_rx_mem_buffer *rxb)
|
|
+{
|
|
+ struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
|
|
+ /* Use phy data (Rx signal strength, etc.) contained within
|
|
+ * this rx packet for legacy frames,
|
|
+ * or phy data cached from REPLY_RX_PHY_CMD for HT frames. */
|
|
+ int include_phy = (pkt->hdr.cmd == REPLY_4965_RX);
|
|
+ struct iwl4965_rx_phy_res *rx_start = (include_phy) ?
|
|
+ (struct iwl4965_rx_phy_res *)&(pkt->u.raw[0]) :
|
|
+ (struct iwl4965_rx_phy_res *)&priv->last_phy_res[1];
|
|
+ __le32 *rx_end;
|
|
+ unsigned int len = 0;
|
|
+ struct ieee80211_hdr *header;
|
|
+ u16 fc;
|
|
+ struct ieee80211_rx_status stats = {
|
|
+ .mactime = le32_to_cpu(rx_start->beacon_time_stamp),
|
|
+ .channel = le16_to_cpu(rx_start->channel),
|
|
+ .phymode =
|
|
+ (rx_start->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ?
|
|
+ MODE_IEEE80211G : MODE_IEEE80211A,
|
|
+ .antenna = 0,
|
|
+ .rate = iwl_hw_get_rate(rx_start->rate_n_flags),
|
|
+ .flag = 0,
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+ .ordered = 0
|
|
+#endif /* CONFIG_IWLWIFI_HT_AGG */
|
|
+ };
|
|
+ u8 network_packet;
|
|
+
|
|
+ if ((unlikely(rx_start->cfg_phy_cnt > 20))) {
|
|
+ IWL_DEBUG_DROP
|
|
+ ("dsp size out of range [0,20]: "
|
|
+ "%d/n", rx_start->cfg_phy_cnt);
|
|
+ return;
|
|
+ }
|
|
+ if (!include_phy) {
|
|
+ if (priv->last_phy_res[0])
|
|
+ rx_start = (struct iwl4965_rx_phy_res *)
|
|
+ &priv->last_phy_res[1];
|
|
+ else
|
|
+ rx_start = NULL;
|
|
+ }
|
|
+
|
|
+ if (!rx_start) {
|
|
+ IWL_ERROR("MPDU frame without a PHY data\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (include_phy) {
|
|
+ header = (struct ieee80211_hdr *)((u8 *) & rx_start[1]
|
|
+ + rx_start->cfg_phy_cnt);
|
|
+
|
|
+ len = le16_to_cpu(rx_start->byte_count);
|
|
+ rx_end = (__le32 *) (pkt->u.raw + rx_start->cfg_phy_cnt +
|
|
+ sizeof(struct iwl4965_rx_phy_res) + len);
|
|
+ } else {
|
|
+ struct iwl4965_rx_mpdu_res_start *amsdu =
|
|
+ (struct iwl4965_rx_mpdu_res_start *)pkt->u.raw;
|
|
+
|
|
+ header = (void *)(pkt->u.raw +
|
|
+ sizeof(struct iwl4965_rx_mpdu_res_start));
|
|
+ len = le16_to_cpu(amsdu->byte_count);
|
|
+ rx_end = (__le32 *) (pkt->u.raw +
|
|
+ sizeof(struct iwl4965_rx_mpdu_res_start) + len);
|
|
+ }
|
|
+
|
|
+ if (!(*rx_end & RX_RES_STATUS_NO_CRC32_ERROR) ||
|
|
+ !(*rx_end & RX_RES_STATUS_NO_RXE_OVERFLOW)) {
|
|
+ IWL_DEBUG_RX("Bad CRC or FIFO: 0x%08X.\n",
|
|
+ le32_to_cpu(*rx_end));
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ priv->ucode_beacon_time = le32_to_cpu(rx_start->beacon_time_stamp);
|
|
+
|
|
+ stats.freq = ieee80211chan2mhz(stats.channel);
|
|
+
|
|
+ /* Find max signal strength (dBm) among 3 antenna/receiver chains */
|
|
+ stats.ssi = iwl4965_calc_rssi(rx_start);
|
|
+
|
|
+ /* Meaningful noise values are available only from beacon statistics,
|
|
+ * which are gathered only when associated, and indicate noise
|
|
+ * only for the associated network channel ...
|
|
+ * Ignore these noise values while scanning (other channels) */
|
|
+ if (iwl_is_associated(priv) &&
|
|
+ !test_bit(STATUS_SCANNING, &priv->status)) {
|
|
+ stats.noise = priv->last_rx_noise;
|
|
+ stats.signal = iwl_calc_sig_qual(stats.ssi, stats.noise);
|
|
+ } else {
|
|
+ stats.noise = IWL_NOISE_MEAS_NOT_AVAILABLE;
|
|
+ stats.signal = iwl_calc_sig_qual(stats.ssi, 0);
|
|
+ }
|
|
+
|
|
+ /* Reset beacon noise level if not associated. */
|
|
+ if (!iwl_is_associated(priv))
|
|
+ priv->last_rx_noise = IWL_NOISE_MEAS_NOT_AVAILABLE;
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+ /* TODO: Parts of iwl_report_frame are broken for 4965 */
|
|
+ if (iwl_debug_level & (IWL_DL_RX))
|
|
+ /* Set "1" to report good data frames in groups of 100 */
|
|
+ iwl_report_frame(priv, pkt, header, 1);
|
|
+
|
|
+ if (iwl_debug_level & (IWL_DL_RX | IWL_DL_STATS))
|
|
+ IWL_DEBUG_RX("Rssi %d, noise %d, qual %d, TSF %lu\n",
|
|
+ stats.ssi, stats.noise, stats.signal,
|
|
+ (long unsigned int)le64_to_cpu(rx_start->timestamp));
|
|
+#endif
|
|
+
|
|
+ network_packet = iwl_is_network_packet(priv, header);
|
|
+ if (network_packet) {
|
|
+ priv->last_rx_rssi = stats.ssi;
|
|
+ priv->last_beacon_time = priv->ucode_beacon_time;
|
|
+ priv->last_tsf = le64_to_cpu(rx_start->timestamp);
|
|
+ }
|
|
+
|
|
+ fc = le16_to_cpu(header->frame_control);
|
|
+ switch (fc & IEEE80211_FCTL_FTYPE) {
|
|
+ case IEEE80211_FTYPE_MGMT:
|
|
+
|
|
+ if (priv->iw_mode == IEEE80211_IF_TYPE_AP)
|
|
+ iwl4965_update_ps_mode(priv, fc & IEEE80211_FCTL_PM,
|
|
+ header->addr2);
|
|
+ switch (fc & IEEE80211_FCTL_STYPE) {
|
|
+ case IEEE80211_STYPE_PROBE_RESP:
|
|
+ case IEEE80211_STYPE_BEACON:
|
|
+ if ((priv->iw_mode == IEEE80211_IF_TYPE_STA &&
|
|
+ !compare_ether_addr(header->addr2, priv->bssid)) ||
|
|
+ (priv->iw_mode == IEEE80211_IF_TYPE_IBSS &&
|
|
+ !compare_ether_addr(header->addr3, priv->bssid))) {
|
|
+ struct ieee80211_mgmt *mgmt =
|
|
+ (struct ieee80211_mgmt *)header;
|
|
+ u64 timestamp =
|
|
+ le64_to_cpu(mgmt->u.beacon.timestamp);
|
|
+
|
|
+ priv->timestamp0 = timestamp & 0xFFFFFFFF;
|
|
+ priv->timestamp1 =
|
|
+ (timestamp >> 32) & 0xFFFFFFFF;
|
|
+ priv->beacon_int = le16_to_cpu(
|
|
+ mgmt->u.beacon.beacon_int);
|
|
+ if (priv->call_post_assoc_from_beacon &&
|
|
+ (priv->iw_mode == IEEE80211_IF_TYPE_STA)) {
|
|
+ priv->call_post_assoc_from_beacon = 0;
|
|
+ queue_work(priv->workqueue,
|
|
+ &priv->post_associate.work);
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case IEEE80211_STYPE_ACTION:
|
|
+ break;
|
|
+
|
|
+ /*
|
|
+ * TODO: There is no callback function from upper
|
|
+ * stack to inform us when associated status. this
|
|
+ * work around to sniff assoc_resp management frame
|
|
+ * and finish the association process.
|
|
+ */
|
|
+ case IEEE80211_STYPE_ASSOC_RESP:
|
|
+ case IEEE80211_STYPE_REASSOC_RESP:
|
|
+ if (network_packet && iwl_is_associated(priv)) {
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+ u8 *pos = NULL;
|
|
+ struct ieee802_11_elems elems;
|
|
+#endif /*CONFIG_IWLWIFI_HT */
|
|
+ struct ieee80211_mgmt *mgnt =
|
|
+ (struct ieee80211_mgmt *)header;
|
|
+
|
|
+ priv->assoc_id = (~((1 << 15) | (1 << 14))
|
|
+ & le16_to_cpu(mgnt->u.assoc_resp.aid));
|
|
+ priv->assoc_capability =
|
|
+ le16_to_cpu(
|
|
+ mgnt->u.assoc_resp.capab_info);
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+ pos = mgnt->u.assoc_resp.variable;
|
|
+ if (!parse_elems(pos,
|
|
+ len - (pos - (u8 *) mgnt),
|
|
+ &elems)) {
|
|
+ if (elems.ht_extra_param &&
|
|
+ elems.ht_cap_param)
|
|
+ break;
|
|
+ }
|
|
+#endif /*CONFIG_IWLWIFI_HT */
|
|
+ /* assoc_id is 0 no association */
|
|
+ if (!priv->assoc_id)
|
|
+ break;
|
|
+ if (priv->beacon_int)
|
|
+ queue_work(priv->workqueue,
|
|
+ &priv->post_associate.work);
|
|
+ else
|
|
+ priv->call_post_assoc_from_beacon = 1;
|
|
+ }
|
|
+
|
|
+ break;
|
|
+
|
|
+ case IEEE80211_STYPE_PROBE_REQ:
|
|
+ if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) &&
|
|
+ !iwl_is_associated(priv)) {
|
|
+ IWL_DEBUG_DROP("Dropping (non network): "
|
|
+ MAC_FMT ", " MAC_FMT ", "
|
|
+ MAC_FMT "\n",
|
|
+ MAC_ARG(header->addr1),
|
|
+ MAC_ARG(header->addr2),
|
|
+ MAC_ARG(header->addr3));
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ iwl4965_handle_data_packet(priv, 0, include_phy, rxb, &stats);
|
|
+ break;
|
|
+
|
|
+ case IEEE80211_FTYPE_CTL:
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+ switch (fc & IEEE80211_FCTL_STYPE) {
|
|
+ case IEEE80211_STYPE_BACK_REQ:
|
|
+ IWL_DEBUG_HT("IEEE80211_STYPE_BACK_REQ arrived\n");
|
|
+ iwl4965_handle_data_packet(priv, 0, include_phy,
|
|
+ rxb, &stats);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ break;
|
|
+
|
|
+ case IEEE80211_FTYPE_DATA:
|
|
+ if (priv->iw_mode == IEEE80211_IF_TYPE_AP)
|
|
+ iwl4965_update_ps_mode(priv, fc & IEEE80211_FCTL_PM,
|
|
+ header->addr2);
|
|
+
|
|
+ if (unlikely(!network_packet))
|
|
+ IWL_DEBUG_DROP("Dropping (non network): "
|
|
+ MAC_FMT ", " MAC_FMT ", "
|
|
+ MAC_FMT "\n",
|
|
+ MAC_ARG(header->addr1),
|
|
+ MAC_ARG(header->addr2),
|
|
+ MAC_ARG(header->addr3));
|
|
+ else if (unlikely(is_duplicate_packet(priv, header)))
|
|
+ IWL_DEBUG_DROP("Dropping (dup): " MAC_FMT ", "
|
|
+ MAC_FMT ", " MAC_FMT "\n",
|
|
+ MAC_ARG(header->addr1),
|
|
+ MAC_ARG(header->addr2),
|
|
+ MAC_ARG(header->addr3));
|
|
+ else
|
|
+ iwl4965_handle_data_packet(priv, 1, include_phy, rxb,
|
|
+ &stats);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Cache phy data (Rx signal strength, etc) for HT frame (REPLY_RX_PHY_CMD).
|
|
+ * This will be used later in iwl4965_rx_reply_rx() for REPLY_RX_MPDU_CMD. */
|
|
+static void iwl4965_rx_reply_rx_phy(struct iwl_priv *priv,
|
|
+ struct iwl_rx_mem_buffer *rxb)
|
|
+{
|
|
+ struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
|
|
+ priv->last_phy_res[0] = 1;
|
|
+ memcpy(&priv->last_phy_res[1], &(pkt->u.raw[0]),
|
|
+ sizeof(struct iwl4965_rx_phy_res));
|
|
+}
|
|
+
|
|
+static void iwl4965_rx_missed_beacon_notif(struct iwl_priv *priv,
|
|
+ struct iwl_rx_mem_buffer *rxb)
|
|
+
|
|
+{
|
|
+#ifdef CONFIG_IWLWIFI_SENSITIVITY
|
|
+ struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
|
|
+ struct iwl_missed_beacon_notif *missed_beacon;
|
|
+
|
|
+ missed_beacon = &pkt->u.missed_beacon;
|
|
+ if (le32_to_cpu(missed_beacon->consequtive_missed_beacons) > 5) {
|
|
+ IWL_DEBUG_CALIB("missed bcn cnsq %d totl %d rcd %d expctd %d\n",
|
|
+ le32_to_cpu(missed_beacon->consequtive_missed_beacons),
|
|
+ le32_to_cpu(missed_beacon->total_missed_becons),
|
|
+ le32_to_cpu(missed_beacon->num_recvd_beacons),
|
|
+ le32_to_cpu(missed_beacon->num_expected_beacons));
|
|
+ priv->sensitivity_data.state = IWL_SENS_CALIB_NEED_REINIT;
|
|
+ if (unlikely(!test_bit(STATUS_SCANNING, &priv->status)))
|
|
+ queue_work(priv->workqueue, &priv->sensitivity_work);
|
|
+ }
|
|
+#endif /*CONFIG_IWLWIFI_SENSITIVITY*/
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+
|
|
+static void iwl4965_set_tx_status(struct iwl_priv *priv, int txq_id, int idx,
|
|
+ u32 status, u32 retry_count, u32 rate)
|
|
+{
|
|
+ struct ieee80211_tx_status *tx_status =
|
|
+ &(priv->txq[txq_id].txb[idx].status);
|
|
+
|
|
+ tx_status->flags = status ? IEEE80211_TX_STATUS_ACK : 0;
|
|
+ tx_status->retry_count += retry_count;
|
|
+ tx_status->control.tx_rate = rate;
|
|
+}
|
|
+
|
|
+
|
|
+static void iwl_sta_modify_enable_tid_tx(struct iwl_priv *priv,
|
|
+ int sta_id, int tid)
|
|
+{
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&priv->sta_lock, flags);
|
|
+ priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX;
|
|
+ priv->stations[sta_id].sta.tid_disable_tx &= cpu_to_le16(~(1 << tid));
|
|
+ priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
|
|
+ spin_unlock_irqrestore(&priv->sta_lock, flags);
|
|
+
|
|
+ iwl_send_add_station(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
|
|
+}
|
|
+
|
|
+
|
|
+static int iwl4965_tx_status_reply_compressed_ba(struct iwl_priv *priv,
|
|
+ struct iwl_ht_agg *agg,
|
|
+ struct iwl_compressed_ba_resp*
|
|
+ ba_resp)
|
|
+
|
|
+{
|
|
+ int i, sh, ack;
|
|
+ u16 ba_seq_ctl = le16_to_cpu(ba_resp->ba_seq_ctl);
|
|
+ u32 bitmap0, bitmap1;
|
|
+ u32 resp_bitmap0 = le32_to_cpu(ba_resp->ba_bitmap0);
|
|
+ u32 resp_bitmap1 = le32_to_cpu(ba_resp->ba_bitmap1);
|
|
+
|
|
+ if (unlikely(!agg->wait_for_ba)) {
|
|
+ IWL_ERROR("Received BA when not expected\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ agg->wait_for_ba = 0;
|
|
+ IWL_DEBUG_TX_REPLY("BA %d %d\n", agg->start_idx, ba_resp->ba_seq_ctl);
|
|
+ sh = agg->start_idx - SEQ_TO_INDEX(ba_seq_ctl>>4);
|
|
+ if (sh < 0) /* tbw something is wrong with indeces */
|
|
+ sh += 0x100;
|
|
+
|
|
+ /* don't use 64 bits for now */
|
|
+ bitmap0 = resp_bitmap0 >> sh;
|
|
+ bitmap1 = resp_bitmap1 >> sh;
|
|
+ bitmap0 |= (resp_bitmap1 & ((1<<sh)|((1<<sh)-1))) << (32 - sh);
|
|
+
|
|
+ if (agg->frame_count > (64 - sh)) {
|
|
+ IWL_DEBUG_TX_REPLY("more frames than bitmap size");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ /* check for success or failure according to the
|
|
+ * transmitted bitmap and back bitmap */
|
|
+ bitmap0 &= agg->bitmap0;
|
|
+ bitmap1 &= agg->bitmap1;
|
|
+
|
|
+ for (i = 0; i < agg->frame_count ; i++) {
|
|
+ int idx = (agg->start_idx + i) & 0xff;
|
|
+ ack = bitmap0 & (1 << i);
|
|
+ IWL_DEBUG_TX_REPLY("%s ON i=%d idx=%d raw=%d\n",
|
|
+ ack? "ACK":"NACK", i, idx, agg->start_idx + i);
|
|
+ iwl4965_set_tx_status(priv, agg->txq_id, idx, ack, 1,
|
|
+ agg->rate_n_flags);
|
|
+
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_TX_REPLY("Bitmap %x%x\n", bitmap0, bitmap1);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int iwl_queue_dec_wrap(int index, int n_bd)
|
|
+{
|
|
+ return (index == 0) ? n_bd - 1 : index - 1;
|
|
+}
|
|
+
|
|
+static void iwl4965_rx_reply_compressed_ba(struct iwl_priv *priv,
|
|
+ struct iwl_rx_mem_buffer *rxb)
|
|
+{
|
|
+ struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
|
|
+ struct iwl_compressed_ba_resp *ba_resp = &pkt->u.compressed_ba;
|
|
+ int index;
|
|
+ struct iwl_tx_queue *txq = NULL;
|
|
+ struct iwl_ht_agg *agg;
|
|
+ u16 ba_resp_scd_flow = le16_to_cpu(ba_resp->scd_flow);
|
|
+ u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn);
|
|
+
|
|
+ if (ba_resp_scd_flow >= ARRAY_SIZE(priv->txq)) {
|
|
+ IWL_ERROR("BUG_ON scd_flow is bigger than number of queues");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ txq = &priv->txq[ba_resp_scd_flow];
|
|
+ agg = &priv->stations[ba_resp->sta_id].tid[ba_resp->tid].agg;
|
|
+ index = iwl_queue_dec_wrap(ba_resp_scd_ssn & 0xff, txq->q.n_bd);
|
|
+
|
|
+ /* TODO: Need to get this copy more sefely - now good for debug */
|
|
+/*
|
|
+ IWL_DEBUG_TX_REPLY("REPLY_COMPRESSED_BA [%d]Received from " MAC_FMT ",
|
|
+ sta_id = %d\n",
|
|
+ agg->wait_for_ba,
|
|
+ MAC_ARG((u8*) &ba_resp->sta_addr_lo32),
|
|
+ ba_resp->sta_id);
|
|
+ IWL_DEBUG_TX_REPLY("TID = %d, SeqCtl = %d, bitmap = 0x%X%X, scd_flow = "
|
|
+ "%d, scd_ssn = %d\n",
|
|
+ ba_resp->tid,
|
|
+ ba_resp->ba_seq_ctl,
|
|
+ ba_resp->ba_bitmap1,
|
|
+ ba_resp->ba_bitmap0,
|
|
+ ba_resp->scd_flow,
|
|
+ ba_resp->scd_ssn);
|
|
+ IWL_DEBUG_TX_REPLY("DAT start_idx = %d, bitmap = 0x%X%X \n",
|
|
+ agg->start_idx,
|
|
+ agg->bitmap1,
|
|
+ agg->bitmap0);
|
|
+*/
|
|
+ iwl4965_tx_status_reply_compressed_ba(priv, agg, ba_resp);
|
|
+ /* releases all the TFDs until the SSN */
|
|
+ if (txq->q.last_used != (ba_resp_scd_ssn & 0xff))
|
|
+ iwl_tx_queue_reclaim(priv, ba_resp_scd_flow, index);
|
|
+
|
|
+}
|
|
+
|
|
+
|
|
+#define STA_MODIFY_ADDBA_TID_MSK 0x08
|
|
+#define STA_MODIFY_DELBA_TID_MSK 0x10
|
|
+#define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid))
|
|
+
|
|
+static void iwl4965_tx_queue_stop_scheduler(struct iwl_priv *priv, u16 txq_id)
|
|
+{
|
|
+ iwl_write_restricted_reg(priv,
|
|
+ SCD_QUEUE_STATUS_BITS(txq_id),
|
|
+ (0 << SCD_QUEUE_STTS_REG_POS_ACTIVE)|
|
|
+ (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
|
|
+}
|
|
+
|
|
+static int iwl4965_tx_queue_set_q2ratid(struct iwl_priv *priv, u16 ra_tid,
|
|
+ u16 txq_id)
|
|
+{
|
|
+ u32 tbl_dw_addr;
|
|
+ u32 tbl_dw;
|
|
+ u16 scd_q2ratid;
|
|
+
|
|
+ scd_q2ratid = ra_tid & SCD_QUEUE_RA_TID_MAP_RATID_MSK;
|
|
+
|
|
+ tbl_dw_addr = priv->scd_base_addr +
|
|
+ SCD_TRANSLATE_TBL_OFFSET_QUEUE(txq_id);
|
|
+
|
|
+ tbl_dw = iwl_read_restricted_mem(priv, tbl_dw_addr);
|
|
+ ((u16 *)&tbl_dw)[txq_id & 0x1] = scd_q2ratid;
|
|
+
|
|
+ iwl_write_restricted_mem(priv, tbl_dw_addr, tbl_dw);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * txq_id must be greater than IWL_BACK_QUEUE_FIRST_ID
|
|
+ */
|
|
+static int iwl4965_tx_queue_agg_enable(struct iwl_priv *priv, int txq_id,
|
|
+ int tx_fifo, int sta_id, int tid,
|
|
+ u16 ssn_idx)
|
|
+{
|
|
+ unsigned long flags;
|
|
+ int rc;
|
|
+ u16 ra_tid;
|
|
+
|
|
+ if (IWL_BACK_QUEUE_FIRST_ID > txq_id)
|
|
+ IWL_WARNING("queue number too small: %d, must be > %d\n",
|
|
+ txq_id, IWL_BACK_QUEUE_FIRST_ID);
|
|
+
|
|
+ ra_tid = BUILD_RAxTID(sta_id, tid);
|
|
+
|
|
+ iwl_sta_modify_enable_tid_tx(priv, sta_id, tid);
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc) {
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ iwl4965_tx_queue_stop_scheduler(priv, txq_id);
|
|
+
|
|
+ iwl4965_tx_queue_set_q2ratid(priv, ra_tid, txq_id);
|
|
+
|
|
+
|
|
+ iwl_set_bits_restricted_reg(priv, SCD_QUEUECHAIN_SEL, (1<<txq_id));
|
|
+
|
|
+ priv->txq[txq_id].q.last_used = (ssn_idx & 0xff);
|
|
+ priv->txq[txq_id].q.first_empty = (ssn_idx & 0xff);
|
|
+
|
|
+ /* supposes that ssn_idx is valid (!= 0xFFF) */
|
|
+ iwl4965_set_wr_ptrs(priv, txq_id, ssn_idx);
|
|
+
|
|
+ iwl_write_restricted_mem(priv,
|
|
+ priv->scd_base_addr + SCD_CONTEXT_QUEUE_OFFSET(txq_id),
|
|
+ (SCD_WIN_SIZE << SCD_QUEUE_CTX_REG1_WIN_SIZE_POS) &
|
|
+ SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK);
|
|
+
|
|
+ iwl_write_restricted_mem(priv, priv->scd_base_addr +
|
|
+ SCD_CONTEXT_QUEUE_OFFSET(txq_id) + sizeof(u32),
|
|
+ (SCD_FRAME_LIMIT << SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS)
|
|
+ & SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK);
|
|
+
|
|
+ iwl_set_bits_restricted_reg(priv, SCD_INTERRUPT_MASK, (1 << txq_id));
|
|
+
|
|
+ iwl4965_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 1);
|
|
+
|
|
+ iwl_release_restricted_access(priv);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * txq_id must be greater than IWL_BACK_QUEUE_FIRST_ID
|
|
+ */
|
|
+static int iwl4965_tx_queue_agg_disable(struct iwl_priv *priv, u16 txq_id,
|
|
+ u16 ssn_idx, u8 tx_fifo)
|
|
+{
|
|
+ unsigned long flags;
|
|
+ int rc;
|
|
+
|
|
+ if (IWL_BACK_QUEUE_FIRST_ID > txq_id) {
|
|
+ IWL_WARNING("queue number too small: %d, must be > %d\n",
|
|
+ txq_id, IWL_BACK_QUEUE_FIRST_ID);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc) {
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ iwl4965_tx_queue_stop_scheduler(priv, txq_id);
|
|
+
|
|
+ iwl_clear_bits_restricted_reg(priv, SCD_QUEUECHAIN_SEL, (1 << txq_id));
|
|
+
|
|
+ priv->txq[txq_id].q.last_used = (ssn_idx & 0xff);
|
|
+ priv->txq[txq_id].q.first_empty = (ssn_idx & 0xff);
|
|
+ /* supposes that ssn_idx is valid (!= 0xFFF) */
|
|
+ iwl4965_set_wr_ptrs(priv, txq_id, ssn_idx);
|
|
+
|
|
+ iwl_clear_bits_restricted_reg(priv, SCD_INTERRUPT_MASK, (1 << txq_id));
|
|
+ iwl4965_txq_ctx_deactivate(priv, txq_id);
|
|
+ iwl4965_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 0);
|
|
+
|
|
+ iwl_release_restricted_access(priv);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#endif/* CONFIG_IWLWIFI_HT_AGG */
|
|
+#endif /* CONFIG_IWLWIFI_HT */
|
|
+/*
|
|
+ * RATE SCALE CODE
|
|
+ */
|
|
+int iwl4965_init_hw_rates(struct iwl_priv *priv, struct ieee80211_rate *rates)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * iwl4965_add_station - Initialize a station's hardware rate table
|
|
+ *
|
|
+ * The uCode contains a table of fallback rates and retries per rate
|
|
+ * for automatic fallback during transmission.
|
|
+ *
|
|
+ * NOTE: This initializes the table for a single retry per data rate
|
|
+ * which is not optimal. Setting up an intelligent retry per rate
|
|
+ * requires feedback from transmission, which isn't exposed through
|
|
+ * rc80211_simple which is what this driver is currently using.
|
|
+ *
|
|
+ */
|
|
+void iwl4965_add_station(struct iwl_priv *priv, const u8 *addr, int is_ap)
|
|
+{
|
|
+ int i, r;
|
|
+ struct iwl_link_quality_cmd link_cmd = {
|
|
+ .reserved1 = 0,
|
|
+ };
|
|
+ u16 rate_flags;
|
|
+
|
|
+ /* Set up the rate scaling to start at 54M and fallback
|
|
+ * all the way to 1M in IEEE order and then spin on IEEE */
|
|
+ if (is_ap)
|
|
+ r = IWL_RATE_54M_INDEX;
|
|
+ else if (priv->phymode == MODE_IEEE80211A)
|
|
+ r = IWL_RATE_6M_INDEX;
|
|
+ else
|
|
+ r = IWL_RATE_1M_INDEX;
|
|
+
|
|
+ for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
|
|
+ rate_flags = 0;
|
|
+ if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE)
|
|
+ rate_flags |= RATE_MCS_CCK_MSK;
|
|
+
|
|
+ rate_flags |= RATE_MCS_ANT_B_MSK;
|
|
+ rate_flags &= ~RATE_MCS_ANT_A_MSK;
|
|
+ link_cmd.rs_table[i].rate_n_flags =
|
|
+ iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags);
|
|
+ r = iwl_get_prev_ieee_rate(r);
|
|
+ }
|
|
+
|
|
+ link_cmd.general_params.single_stream_ant_msk = 2;
|
|
+ link_cmd.general_params.dual_stream_ant_msk = 3;
|
|
+ link_cmd.agg_params.agg_dis_start_th = 3;
|
|
+ link_cmd.agg_params.agg_time_limit = cpu_to_le16(4000);
|
|
+
|
|
+ /* Update the rate scaling for control frame Tx to AP */
|
|
+ link_cmd.sta_id = is_ap ? IWL_AP_ID : IWL_BROADCAST_ID;
|
|
+
|
|
+ iwl_send_cmd_pdu(priv, REPLY_TX_LINK_QUALITY_CMD, sizeof(link_cmd),
|
|
+ &link_cmd);
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+
|
|
+static u8 iwl_is_channel_extension(struct iwl_priv *priv, int phymode,
|
|
+ u16 channel, u8 extension_chan_offset)
|
|
+{
|
|
+ const struct iwl_channel_info *ch_info;
|
|
+
|
|
+ ch_info = iwl_get_channel_info(priv, phymode, channel);
|
|
+ if (!is_channel_valid(ch_info))
|
|
+ return 0;
|
|
+
|
|
+ if (extension_chan_offset == IWL_EXT_CHANNEL_OFFSET_AUTO)
|
|
+ return 0;
|
|
+
|
|
+ if ((ch_info->fat_extension_channel == extension_chan_offset) ||
|
|
+ (ch_info->fat_extension_channel == HT_IE_EXT_CHANNEL_MAX))
|
|
+ return 1;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static u8 iwl_is_fat_tx_allowed(struct iwl_priv *priv,
|
|
+ const struct sta_ht_info *ht_info)
|
|
+{
|
|
+
|
|
+ if (priv->channel_width != IWL_CHANNEL_WIDTH_40MHZ)
|
|
+ return 0;
|
|
+
|
|
+ if (ht_info->supported_chan_width != IWL_CHANNEL_WIDTH_40MHZ)
|
|
+ return 0;
|
|
+
|
|
+ if (ht_info->extension_chan_offset == IWL_EXT_CHANNEL_OFFSET_AUTO)
|
|
+ return 0;
|
|
+
|
|
+ /* no fat tx allowed on 2.4GHZ */
|
|
+ if (priv->phymode != MODE_IEEE80211A)
|
|
+ return 0;
|
|
+ return (iwl_is_channel_extension(priv, priv->phymode,
|
|
+ ht_info->control_channel,
|
|
+ ht_info->extension_chan_offset));
|
|
+}
|
|
+
|
|
+void iwl4965_set_rxon_ht(struct iwl_priv *priv, struct sta_ht_info *ht_info)
|
|
+{
|
|
+ struct iwl_rxon_cmd *rxon = &priv->staging_rxon;
|
|
+ u32 val;
|
|
+
|
|
+ if (!ht_info->is_ht)
|
|
+ return;
|
|
+
|
|
+ if (iwl_is_fat_tx_allowed(priv, ht_info))
|
|
+ rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED_MSK;
|
|
+ else
|
|
+ rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY_MSK;
|
|
+
|
|
+ if (le16_to_cpu(rxon->channel) != ht_info->control_channel) {
|
|
+ IWL_DEBUG_ASSOC("control diff than current %d %d\n",
|
|
+ le16_to_cpu(rxon->channel),
|
|
+ ht_info->control_channel);
|
|
+ rxon->channel = cpu_to_le16(ht_info->control_channel);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ rxon->flags &= ~RXON_FLG_CONTROL_CHANNEL_LOCATION_MSK;
|
|
+
|
|
+ switch (ht_info->extension_chan_offset) {
|
|
+ case IWL_EXT_CHANNEL_OFFSET_ABOVE:
|
|
+ rxon->flags |= RXON_FLG_CONTROL_CHANNEL_LOC_LOW_MSK;
|
|
+ break;
|
|
+ case IWL_EXT_CHANNEL_OFFSET_BELOW:
|
|
+ rxon->flags |= RXON_FLG_CONTROL_CHANNEL_LOC_HIGH_MSK;
|
|
+ break;
|
|
+ case IWL_EXT_CHANNEL_OFFSET_AUTO:
|
|
+ rxon->flags &= ~RXON_FLG_CHANNEL_MODE_MIXED_MSK;
|
|
+ break;
|
|
+ default:
|
|
+ rxon->flags &= ~RXON_FLG_CHANNEL_MODE_MIXED_MSK;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ val = ht_info->operating_mode;
|
|
+
|
|
+ rxon->flags |= cpu_to_le32(val << RXON_FLG_HT_OPERATING_MODE_POS);
|
|
+
|
|
+ priv->active_rate_ht[0] = ht_info->supp_rates[0];
|
|
+ priv->active_rate_ht[1] = ht_info->supp_rates[1];
|
|
+ iwl4965_set_rxon_chain(priv);
|
|
+
|
|
+ IWL_DEBUG_ASSOC("supported HT rate 0x%X %X "
|
|
+ "rxon flags 0x%X operation mode :0x%X "
|
|
+ "extension channel offset 0x%x "
|
|
+ "control chan %d\n",
|
|
+ priv->active_rate_ht[0], priv->active_rate_ht[1],
|
|
+ le32_to_cpu(rxon->flags), ht_info->operating_mode,
|
|
+ ht_info->extension_chan_offset,
|
|
+ ht_info->control_channel);
|
|
+ return;
|
|
+}
|
|
+
|
|
+void iwl4965_set_ht_add_station(struct iwl_priv *priv, u8 index)
|
|
+{
|
|
+ __le32 sta_flags;
|
|
+ struct sta_ht_info *ht_info = &priv->current_assoc_ht;
|
|
+
|
|
+ priv->current_channel_width = IWL_CHANNEL_WIDTH_20MHZ;
|
|
+ if (!ht_info->is_ht)
|
|
+ goto done;
|
|
+
|
|
+ sta_flags = priv->stations[index].sta.station_flags;
|
|
+
|
|
+ if (ht_info->tx_mimo_ps_mode == IWL_MIMO_PS_DYNAMIC)
|
|
+ sta_flags |= STA_FLG_RTS_MIMO_PROT_MSK;
|
|
+ else
|
|
+ sta_flags &= ~STA_FLG_RTS_MIMO_PROT_MSK;
|
|
+
|
|
+ sta_flags |= cpu_to_le32(
|
|
+ (u32)ht_info->ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS);
|
|
+
|
|
+ sta_flags |= cpu_to_le32(
|
|
+ (u32)ht_info->mpdu_density << STA_FLG_AGG_MPDU_DENSITY_POS);
|
|
+
|
|
+ sta_flags &= (~STA_FLG_FAT_EN_MSK);
|
|
+ ht_info->tx_chan_width = IWL_CHANNEL_WIDTH_20MHZ;
|
|
+ ht_info->chan_width_cap = IWL_CHANNEL_WIDTH_20MHZ;
|
|
+
|
|
+ if (iwl_is_fat_tx_allowed(priv, ht_info)) {
|
|
+ sta_flags |= STA_FLG_FAT_EN_MSK;
|
|
+ ht_info->chan_width_cap = IWL_CHANNEL_WIDTH_40MHZ;
|
|
+ if (ht_info->supported_chan_width == IWL_CHANNEL_WIDTH_40MHZ)
|
|
+ ht_info->tx_chan_width = IWL_CHANNEL_WIDTH_40MHZ;
|
|
+ }
|
|
+ priv->current_channel_width = ht_info->tx_chan_width;
|
|
+ priv->stations[index].sta.station_flags = sta_flags;
|
|
+ done:
|
|
+ return;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+
|
|
+static void iwl4965_sta_modify_add_ba_tid(struct iwl_priv *priv,
|
|
+ int sta_id, int tid, u16 ssn)
|
|
+{
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&priv->sta_lock, flags);
|
|
+ priv->stations[sta_id].sta.station_flags_msk = 0;
|
|
+ priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK;
|
|
+ priv->stations[sta_id].sta.add_immediate_ba_tid = (u8)tid;
|
|
+ priv->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn);
|
|
+ priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
|
|
+ spin_unlock_irqrestore(&priv->sta_lock, flags);
|
|
+
|
|
+ iwl_send_add_station(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
|
|
+}
|
|
+
|
|
+static void iwl4965_sta_modify_del_ba_tid(struct iwl_priv *priv,
|
|
+ int sta_id, int tid)
|
|
+{
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&priv->sta_lock, flags);
|
|
+ priv->stations[sta_id].sta.station_flags_msk = 0;
|
|
+ priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK;
|
|
+ priv->stations[sta_id].sta.remove_immediate_ba_tid = (u8)tid;
|
|
+ priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
|
|
+ spin_unlock_irqrestore(&priv->sta_lock, flags);
|
|
+
|
|
+ iwl_send_add_station(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
|
|
+}
|
|
+
|
|
+static const u16 default_tid_to_ac[] = {
|
|
+ IWL_TX_QUEUE_AC0,
|
|
+ IWL_TX_QUEUE_AC1,
|
|
+ IWL_TX_QUEUE_AC1,
|
|
+ IWL_TX_QUEUE_AC0,
|
|
+ IWL_TX_QUEUE_AC2,
|
|
+ IWL_TX_QUEUE_AC2,
|
|
+ IWL_TX_QUEUE_AC3,
|
|
+ IWL_TX_QUEUE_AC3,
|
|
+ IWL_TX_QUEUE_NONE,
|
|
+ IWL_TX_QUEUE_NONE,
|
|
+ IWL_TX_QUEUE_NONE,
|
|
+ IWL_TX_QUEUE_NONE,
|
|
+ IWL_TX_QUEUE_NONE,
|
|
+ IWL_TX_QUEUE_NONE,
|
|
+ IWL_TX_QUEUE_NONE,
|
|
+ IWL_TX_QUEUE_NONE,
|
|
+ IWL_TX_QUEUE_AC3
|
|
+};
|
|
+
|
|
+static int iwl_txq_ctx_activate_free(struct iwl_priv *priv)
|
|
+{
|
|
+ int txq_id;
|
|
+
|
|
+ for (txq_id = 0; txq_id < priv->hw_setting.max_txq_num; txq_id++)
|
|
+ if (!test_and_set_bit(txq_id, &priv->txq_ctx_active_msk))
|
|
+ return txq_id;
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+int iwl_mac_ht_tx_agg_start(struct ieee80211_hw *hw, u8 *da, u16 tid,
|
|
+ u16 *start_seq_num)
|
|
+{
|
|
+
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
+ int sta_id;
|
|
+ int tx_fifo;
|
|
+ int txq_id;
|
|
+ int ssn = -1;
|
|
+ unsigned long flags;
|
|
+ struct iwl_tid_data *tid_data;
|
|
+
|
|
+ if (likely(tid < ARRAY_SIZE(default_tid_to_ac)))
|
|
+ tx_fifo = default_tid_to_ac[tid];
|
|
+ else
|
|
+ return -EINVAL;
|
|
+
|
|
+ IWL_WARNING("iwl-AGG iwl_mac_ht_tx_agg_start on da=" MAC_FMT
|
|
+ " tid=%d\n", MAC_ARG(da), tid);
|
|
+
|
|
+ sta_id = iwl_hw_find_station(priv, da);
|
|
+ if (sta_id == IWL_INVALID_STATION)
|
|
+ return -ENXIO;
|
|
+
|
|
+ txq_id = iwl_txq_ctx_activate_free(priv);
|
|
+ if (txq_id == -1)
|
|
+ return -ENXIO;
|
|
+
|
|
+ spin_lock_irqsave(&priv->sta_lock, flags);
|
|
+ tid_data = &priv->stations[sta_id].tid[tid];
|
|
+ ssn = SEQ_TO_SN(tid_data->seq_number);
|
|
+ tid_data->agg.txq_id = txq_id;
|
|
+ spin_unlock_irqrestore(&priv->sta_lock, flags);
|
|
+
|
|
+ *start_seq_num = ssn;
|
|
+ iwl4965_ba_status(priv, tid, BA_STATUS_ACTIVE);
|
|
+ return iwl4965_tx_queue_agg_enable(priv, txq_id, tx_fifo,
|
|
+ sta_id, tid, ssn);
|
|
+}
|
|
+
|
|
+
|
|
+int iwl_mac_ht_tx_agg_stop(struct ieee80211_hw *hw, u8 *da, u16 tid,
|
|
+ int generator)
|
|
+{
|
|
+
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
+ int tx_fifo_id, txq_id, sta_id, ssn = -1;
|
|
+ struct iwl_tid_data *tid_data;
|
|
+ int rc;
|
|
+ if (!da) {
|
|
+ IWL_ERROR("%s: da = NULL\n", __func__);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (likely(tid < ARRAY_SIZE(default_tid_to_ac)))
|
|
+ tx_fifo_id = default_tid_to_ac[tid];
|
|
+ else
|
|
+ return -EINVAL;
|
|
+
|
|
+ sta_id = iwl_hw_find_station(priv, da);
|
|
+
|
|
+ if (sta_id == IWL_INVALID_STATION)
|
|
+ return -ENXIO;
|
|
+
|
|
+ tid_data = &priv->stations[sta_id].tid[tid];
|
|
+ ssn = (tid_data->seq_number & IEEE80211_SCTL_SEQ) >> 4;
|
|
+ txq_id = tid_data->agg.txq_id;
|
|
+
|
|
+ rc = iwl4965_tx_queue_agg_disable(priv, txq_id, ssn, tx_fifo_id);
|
|
+ /* FIXME: need more safe way to handle error condition */
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ iwl4965_ba_status(priv, tid, BA_STATUS_INITIATOR_DELBA);
|
|
+ IWL_DEBUG_INFO("iwl_mac_ht_tx_agg_stop on da=" MAC_FMT " tid=%d\n",
|
|
+ MAC_ARG(da), tid);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int iwl_mac_ht_rx_agg_start(struct ieee80211_hw *hw, u8 *da,
|
|
+ u16 tid, u16 start_seq_num)
|
|
+{
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
+ int sta_id;
|
|
+
|
|
+ IWL_WARNING("iwl-AGG iwl_mac_ht_rx_agg_start on da=" MAC_FMT
|
|
+ " tid=%d\n", MAC_ARG(da), tid);
|
|
+ sta_id = iwl_hw_find_station(priv, da);
|
|
+ iwl4965_sta_modify_add_ba_tid(priv, sta_id, tid, start_seq_num);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int iwl_mac_ht_rx_agg_stop(struct ieee80211_hw *hw, u8 *da,
|
|
+ u16 tid, int generator)
|
|
+{
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
+ int sta_id;
|
|
+
|
|
+ IWL_WARNING("iwl-AGG iwl_mac_ht_rx_agg_stop on da=" MAC_FMT " tid=%d\n",
|
|
+ MAC_ARG(da), tid);
|
|
+ sta_id = iwl_hw_find_station(priv, da);
|
|
+ iwl4965_sta_modify_del_ba_tid(priv, sta_id, tid);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#endif /* CONFIG_IWLWIFI_HT_AGG */
|
|
+#endif /* CONFIG_IWLWIFI_HT */
|
|
+
|
|
+/* Set up 4965-specific Rx frame reply handlers */
|
|
+void iwl_hw_rx_handler_setup(struct iwl_priv *priv)
|
|
+{
|
|
+ /* Legacy Rx frames */
|
|
+ priv->rx_handlers[REPLY_4965_RX] = iwl4965_rx_reply_rx;
|
|
+
|
|
+ /* High-throughput (HT) Rx frames */
|
|
+ priv->rx_handlers[REPLY_RX_PHY_CMD] = iwl4965_rx_reply_rx_phy;
|
|
+ priv->rx_handlers[REPLY_RX_MPDU_CMD] = iwl4965_rx_reply_rx;
|
|
+
|
|
+ priv->rx_handlers[MISSED_BEACONS_NOTIFICATION] =
|
|
+ iwl4965_rx_missed_beacon_notif;
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+ priv->rx_handlers[REPLY_COMPRESSED_BA] = iwl4965_rx_reply_compressed_ba;
|
|
+#endif /* CONFIG_IWLWIFI_AGG */
|
|
+#endif /* CONFIG_IWLWIFI */
|
|
+}
|
|
+
|
|
+void iwl_hw_setup_deferred_work(struct iwl_priv *priv)
|
|
+{
|
|
+ INIT_WORK(&priv->txpower_work, iwl4965_bg_txpower_work);
|
|
+ INIT_WORK(&priv->statistics_work, iwl4965_bg_statistics_work);
|
|
+#ifdef CONFIG_IWLWIFI_SENSITIVITY
|
|
+ INIT_WORK(&priv->sensitivity_work, iwl4965_bg_sensitivity_work);
|
|
+#endif
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+ INIT_WORK(&priv->agg_work, iwl4965_bg_agg_work);
|
|
+#endif /* CONFIG_IWLWIFI_AGG */
|
|
+#endif /* CONFIG_IWLWIFI_HT */
|
|
+ init_timer(&priv->statistics_periodic);
|
|
+ priv->statistics_periodic.data = (unsigned long)priv;
|
|
+ priv->statistics_periodic.function = iwl4965_bg_statistics_periodic;
|
|
+}
|
|
+
|
|
+void iwl_hw_cancel_deferred_work(struct iwl_priv *priv)
|
|
+{
|
|
+ del_timer_sync(&priv->statistics_periodic);
|
|
+
|
|
+ cancel_delayed_work(&priv->init_alive_start);
|
|
+}
|
|
+
|
|
+struct pci_device_id iwl_hw_card_ids[] = {
|
|
+ {0x8086, 0x4229, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
|
+ {0x8086, 0x4230, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
|
+ {0}
|
|
+};
|
|
+
|
|
+int iwl_eeprom_aqcuire_semaphore(struct iwl_priv *priv)
|
|
+{
|
|
+ u16 count;
|
|
+ int rc;
|
|
+
|
|
+ for (count = 0; count < EEPROM_SEM_RETRY_LIMIT; count++) {
|
|
+ iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
|
|
+ CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM);
|
|
+ rc = iwl_poll_bit(priv, CSR_HW_IF_CONFIG_REG,
|
|
+ CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM,
|
|
+ CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM,
|
|
+ EEPROM_SEM_TIMEOUT);
|
|
+ if (rc >= 0) {
|
|
+ IWL_DEBUG_IO("Aqcuired semaphore after %d tries.\n",
|
|
+ count+1);
|
|
+ return rc;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+inline void iwl_eeprom_release_semaphore(struct iwl_priv *priv)
|
|
+{
|
|
+ iwl_clear_bit(priv, CSR_HW_IF_CONFIG_REG,
|
|
+ CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM);
|
|
+}
|
|
+
|
|
+
|
|
+MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids);
|
|
diff --git a/drivers/net/wireless/iwl-4965.h b/drivers/net/wireless/iwl-4965.h
|
|
new file mode 100644
|
|
index 0000000..af5215f
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/iwl-4965.h
|
|
@@ -0,0 +1,367 @@
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * Copyright(c) 2003 - 2007 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.
|
|
+ *
|
|
+ * Contact Information:
|
|
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
|
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+#ifndef __iwl_4965_h__
|
|
+#define __iwl_4965_h__
|
|
+
|
|
+struct iwl_priv;
|
|
+struct sta_ht_info;
|
|
+
|
|
+#if IWL != 4965
|
|
+/*
|
|
+ * In non IWL == 4965 builds, these must build to nothing in order to allow
|
|
+ * the common code to not have several #if IWL == XXXX / #endif blocks
|
|
+ */
|
|
+static inline void iwl_eeprom_release_semaphore(struct iwl_priv *priv) {}
|
|
+
|
|
+static inline void iwl4965_add_station(struct iwl_priv *priv, const u8 *addr,
|
|
+ int is_ap) {}
|
|
+static inline void iwl4965_set_rxon_ht(struct iwl_priv *priv,
|
|
+ struct sta_ht_info *ht_info) {}
|
|
+
|
|
+static inline void iwl4965_set_rxon_chain(struct iwl_priv *priv) {}
|
|
+static inline int iwl4965_tx_cmd(struct iwl_priv *priv,
|
|
+ struct iwl_cmd *out_cmd,
|
|
+ u8 sta_id, dma_addr_t txcmd_phys,
|
|
+ struct ieee80211_hdr *hdr, u8 hdr_len,
|
|
+ struct ieee80211_tx_control *ctrl,
|
|
+ void *sta_in) { return 0; }
|
|
+static inline int iwl4965_tx_queue_update_wr_ptr(struct iwl_priv *priv,
|
|
+ struct iwl_tx_queue *txq,
|
|
+ u16 len) { return 0; }
|
|
+static inline int iwl4965_init_hw_rates(struct iwl_priv *priv,
|
|
+ struct ieee80211_rate *rates)
|
|
+{ return 0; }
|
|
+static inline int iwl4965_alive_notify(struct iwl_priv *priv) { return 0; }
|
|
+static inline void iwl4965_update_rate_scaling(struct iwl_priv *priv,
|
|
+ u8 mode) {}
|
|
+static inline void iwl4965_set_ht_add_station(struct iwl_priv *priv, u8 index)
|
|
+{}
|
|
+static inline void iwl4965_chain_noise_reset(struct iwl_priv *priv) {}
|
|
+static inline void iwl4965_init_sensitivity(struct iwl_priv *priv, u8 flags,
|
|
+ u8 force) {}
|
|
+static inline int iwl4965_set_fat_chan_info(struct iwl_priv *priv, int phymode,
|
|
+ u16 channel,
|
|
+ const struct iwl_eeprom_channel *eeprom_ch,
|
|
+ u8 fat_extension_channel) { return 0; }
|
|
+static inline void iwl4965_rf_kill_ct_config(struct iwl_priv *priv) {}
|
|
+#else /* IWL == 4965 */
|
|
+/*
|
|
+ * Forward declare iwl-4965.c functions for iwl-base.c
|
|
+ */
|
|
+extern int iwl_eeprom_aqcuire_semaphore(struct iwl_priv *priv);
|
|
+extern void iwl_eeprom_release_semaphore(struct iwl_priv *priv);
|
|
+
|
|
+extern int iwl4965_tx_queue_update_wr_ptr(struct iwl_priv *priv,
|
|
+ struct iwl_tx_queue *txq,
|
|
+ u16 byte_cnt);
|
|
+extern void iwl4965_add_station(struct iwl_priv *priv, const u8 *addr,
|
|
+ int is_ap);
|
|
+extern void iwl4965_set_rxon_ht(struct iwl_priv *priv,
|
|
+ struct sta_ht_info *ht_info);
|
|
+
|
|
+extern void iwl4965_set_rxon_chain(struct iwl_priv *priv);
|
|
+extern int iwl4965_tx_cmd(struct iwl_priv *priv, struct iwl_cmd *out_cmd,
|
|
+ u8 sta_id, dma_addr_t txcmd_phys,
|
|
+ struct ieee80211_hdr *hdr, u8 hdr_len,
|
|
+ struct ieee80211_tx_control *ctrl, void *sta_in);
|
|
+extern int iwl4965_init_hw_rates(struct iwl_priv *priv,
|
|
+ struct ieee80211_rate *rates);
|
|
+extern int iwl4965_alive_notify(struct iwl_priv *priv);
|
|
+extern void iwl4965_update_rate_scaling(struct iwl_priv *priv, u8 mode);
|
|
+extern void iwl4965_set_ht_add_station(struct iwl_priv *priv, u8 index);
|
|
+
|
|
+extern void iwl4965_chain_noise_reset(struct iwl_priv *priv);
|
|
+extern void iwl4965_init_sensitivity(struct iwl_priv *priv, u8 flags,
|
|
+ u8 force);
|
|
+extern int iwl4965_set_fat_chan_info(struct iwl_priv *priv, int phymode,
|
|
+ u16 channel,
|
|
+ const struct iwl_eeprom_channel *eeprom_ch,
|
|
+ u8 fat_extension_channel);
|
|
+extern void iwl4965_rf_kill_ct_config(struct iwl_priv *priv);
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+extern int iwl_mac_ht_tx_agg_start(struct ieee80211_hw *hw, u8 *da,
|
|
+ u16 tid, u16 *start_seq_num);
|
|
+extern int iwl_mac_ht_rx_agg_start(struct ieee80211_hw *hw, u8 *da,
|
|
+ u16 tid, u16 start_seq_num);
|
|
+extern int iwl_mac_ht_rx_agg_stop(struct ieee80211_hw *hw, u8 *da,
|
|
+ u16 tid, int generator);
|
|
+extern int iwl_mac_ht_tx_agg_stop(struct ieee80211_hw *hw, u8 *da,
|
|
+ u16 tid, int generator);
|
|
+extern void iwl4965_turn_off_agg(struct iwl_priv *priv, u8 tid);
|
|
+#endif /* CONFIG_IWLWIFI_HT_AGG */
|
|
+#endif /*CONFIG_IWLWIFI_HT */
|
|
+/* Structures, enum, and defines specific to the 4965 */
|
|
+
|
|
+#define IWL4965_KW_SIZE 0x1000 /*4k */
|
|
+
|
|
+struct iwl_kw {
|
|
+ dma_addr_t dma_addr;
|
|
+ void *v_addr;
|
|
+ size_t size;
|
|
+};
|
|
+
|
|
+#define TID_QUEUE_CELL_SPACING 50 /*mS */
|
|
+#define TID_QUEUE_MAX_SIZE 20
|
|
+#define TID_ROUND_VALUE 5 /* mS */
|
|
+#define TID_MAX_LOAD_COUNT 8
|
|
+
|
|
+#define TID_MAX_TIME_DIFF ((TID_QUEUE_MAX_SIZE - 1) * TID_QUEUE_CELL_SPACING)
|
|
+#define TIME_WRAP_AROUND(x, y) (((y) > (x)) ? (y) - (x) : (0-(x)) + (y))
|
|
+
|
|
+#define TID_ALL_ENABLED 0x7f
|
|
+#define TID_ALL_SPECIFIED 0xff
|
|
+#define TID_AGG_TPT_THREHOLD 0x0
|
|
+
|
|
+#define IWL_CHANNEL_WIDTH_20MHZ 0
|
|
+#define IWL_CHANNEL_WIDTH_40MHZ 1
|
|
+
|
|
+#define IWL_MIMO_PS_STATIC 0
|
|
+#define IWL_MIMO_PS_NONE 3
|
|
+#define IWL_MIMO_PS_DYNAMIC 1
|
|
+#define IWL_MIMO_PS_INVALID 2
|
|
+
|
|
+#define IWL_OPERATION_MODE_AUTO 0
|
|
+#define IWL_OPERATION_MODE_HT_ONLY 1
|
|
+#define IWL_OPERATION_MODE_MIXED 2
|
|
+#define IWL_OPERATION_MODE_20MHZ 3
|
|
+
|
|
+#define IWL_EXT_CHANNEL_OFFSET_AUTO 0
|
|
+#define IWL_EXT_CHANNEL_OFFSET_ABOVE 1
|
|
+#define IWL_EXT_CHANNEL_OFFSET_ 2
|
|
+#define IWL_EXT_CHANNEL_OFFSET_BELOW 3
|
|
+#define IWL_EXT_CHANNEL_OFFSET_MAX 4
|
|
+
|
|
+#define NRG_NUM_PREV_STAT_L 20
|
|
+#define NUM_RX_CHAINS (3)
|
|
+
|
|
+struct iwl_traffic_load {
|
|
+ unsigned long time_stamp;
|
|
+ u32 packet_count[TID_QUEUE_MAX_SIZE];
|
|
+ u8 queue_count;
|
|
+ u8 head;
|
|
+ u32 total;
|
|
+};
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+struct iwl_agg_control {
|
|
+ unsigned long next_retry;
|
|
+ u32 wait_for_agg_status;
|
|
+ u32 tid_retry;
|
|
+ u32 requested_ba;
|
|
+ u32 granted_ba;
|
|
+ u8 auto_agg;
|
|
+ u32 tid_traffic_load_threshold;
|
|
+ u32 ba_timeout;
|
|
+ struct iwl_traffic_load traffic_load[TID_MAX_LOAD_COUNT];
|
|
+};
|
|
+#endif /*CONFIG_IWLWIFI_HT_AGG */
|
|
+
|
|
+struct iwl_lq_mngr {
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+ struct iwl_agg_control agg_ctrl;
|
|
+#endif
|
|
+ spinlock_t lock;
|
|
+ s32 max_window_size;
|
|
+ s32 *expected_tpt;
|
|
+ u8 *next_higher_rate;
|
|
+ u8 *next_lower_rate;
|
|
+ unsigned long stamp;
|
|
+ unsigned long stamp_last;
|
|
+ u32 flush_time;
|
|
+ u32 tx_packets;
|
|
+ u8 lq_ready;
|
|
+};
|
|
+
|
|
+
|
|
+/* Sensitivity and chain noise calibration */
|
|
+#define INTERFERENCE_DATA_AVAILABLE __constant_cpu_to_le32(1)
|
|
+#define INITIALIZATION_VALUE 0xFFFF
|
|
+#define CAL_NUM_OF_BEACONS 20
|
|
+#define MAXIMUM_ALLOWED_PATHLOSS 15
|
|
+
|
|
+/* Param table within SENSITIVITY_CMD */
|
|
+#define HD_MIN_ENERGY_CCK_DET_INDEX (0)
|
|
+#define HD_MIN_ENERGY_OFDM_DET_INDEX (1)
|
|
+#define HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX (2)
|
|
+#define HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX (3)
|
|
+#define HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX (4)
|
|
+#define HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX (5)
|
|
+#define HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX (6)
|
|
+#define HD_BARKER_CORR_TH_ADD_MIN_INDEX (7)
|
|
+#define HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX (8)
|
|
+#define HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX (9)
|
|
+#define HD_OFDM_ENERGY_TH_IN_INDEX (10)
|
|
+
|
|
+#define SENSITIVITY_CMD_CONTROL_DEFAULT_TABLE __constant_cpu_to_le16(0)
|
|
+#define SENSITIVITY_CMD_CONTROL_WORK_TABLE __constant_cpu_to_le16(1)
|
|
+
|
|
+#define CHAIN_NOISE_MAX_DELTA_GAIN_CODE 3
|
|
+
|
|
+#define MAX_FA_OFDM 50
|
|
+#define MIN_FA_OFDM 5
|
|
+#define MAX_FA_CCK 50
|
|
+#define MIN_FA_CCK 5
|
|
+
|
|
+#define NRG_MIN_CCK 97
|
|
+#define NRG_MAX_CCK 0
|
|
+
|
|
+#define AUTO_CORR_MIN_OFDM 85
|
|
+#define AUTO_CORR_MIN_OFDM_MRC 170
|
|
+#define AUTO_CORR_MIN_OFDM_X1 105
|
|
+#define AUTO_CORR_MIN_OFDM_MRC_X1 220
|
|
+#define AUTO_CORR_MAX_OFDM 120
|
|
+#define AUTO_CORR_MAX_OFDM_MRC 210
|
|
+#define AUTO_CORR_MAX_OFDM_X1 140
|
|
+#define AUTO_CORR_MAX_OFDM_MRC_X1 270
|
|
+#define AUTO_CORR_STEP_OFDM 1
|
|
+
|
|
+#define AUTO_CORR_MIN_CCK (125)
|
|
+#define AUTO_CORR_MAX_CCK (200)
|
|
+#define AUTO_CORR_MIN_CCK_MRC 200
|
|
+#define AUTO_CORR_MAX_CCK_MRC 400
|
|
+#define AUTO_CORR_STEP_CCK 3
|
|
+#define AUTO_CORR_MAX_TH_CCK 160
|
|
+
|
|
+#define NRG_ALG 0
|
|
+#define AUTO_CORR_ALG 1
|
|
+#define NRG_DIFF 2
|
|
+#define NRG_STEP_CCK 2
|
|
+#define NRG_MARGIN 8
|
|
+#define MAX_NUMBER_CCK_NO_FA 100
|
|
+
|
|
+#define AUTO_CORR_CCK_MIN_VAL_DEF (125)
|
|
+
|
|
+#define CHAIN_A 0
|
|
+#define CHAIN_B 1
|
|
+#define CHAIN_C 2
|
|
+#define CHAIN_NOISE_DELTA_GAIN_INIT_VAL 4
|
|
+#define ALL_BAND_FILTER 0xFF00
|
|
+#define IN_BAND_FILTER 0xFF
|
|
+#define MIN_AVERAGE_NOISE_MAX_VALUE 0xFFFFFFFF
|
|
+
|
|
+enum iwl_false_alarm_state {
|
|
+ IWL_FA_TOO_MANY = 0,
|
|
+ IWL_FA_TOO_FEW = 1,
|
|
+ IWL_FA_GOOD_RANGE = 2,
|
|
+};
|
|
+
|
|
+enum iwl_chain_noise_state {
|
|
+ IWL_CHAIN_NOISE_ALIVE = 0, /* must be 0 */
|
|
+ IWL_CHAIN_NOISE_ACCUMULATE = 1,
|
|
+ IWL_CHAIN_NOISE_CALIBRATED = 2,
|
|
+};
|
|
+
|
|
+enum iwl_sensitivity_state {
|
|
+ IWL_SENS_CALIB_ALLOWED = 0,
|
|
+ IWL_SENS_CALIB_NEED_REINIT = 1,
|
|
+};
|
|
+
|
|
+enum iwl_calib_enabled_state {
|
|
+ IWL_CALIB_DISABLED = 0, /* must be 0 */
|
|
+ IWL_CALIB_ENABLED = 1,
|
|
+};
|
|
+
|
|
+struct statistics_general_data {
|
|
+ u32 beacon_silence_rssi_a;
|
|
+ u32 beacon_silence_rssi_b;
|
|
+ u32 beacon_silence_rssi_c;
|
|
+ u32 beacon_energy_a;
|
|
+ u32 beacon_energy_b;
|
|
+ u32 beacon_energy_c;
|
|
+};
|
|
+
|
|
+/* Sensitivity calib data */
|
|
+struct iwl_sensitivity_data {
|
|
+ u32 auto_corr_ofdm;
|
|
+ u32 auto_corr_ofdm_mrc;
|
|
+ u32 auto_corr_ofdm_x1;
|
|
+ u32 auto_corr_ofdm_mrc_x1;
|
|
+ u32 auto_corr_cck;
|
|
+ u32 auto_corr_cck_mrc;
|
|
+
|
|
+ u32 last_bad_plcp_cnt_ofdm;
|
|
+ u32 last_fa_cnt_ofdm;
|
|
+ u32 last_bad_plcp_cnt_cck;
|
|
+ u32 last_fa_cnt_cck;
|
|
+
|
|
+ u32 nrg_curr_state;
|
|
+ u32 nrg_prev_state;
|
|
+ u32 nrg_value[10];
|
|
+ u8 nrg_silence_rssi[NRG_NUM_PREV_STAT_L];
|
|
+ u32 nrg_silence_ref;
|
|
+ u32 nrg_energy_idx;
|
|
+ u32 nrg_silence_idx;
|
|
+ u32 nrg_th_cck;
|
|
+ s32 nrg_auto_corr_silence_diff;
|
|
+ u32 num_in_cck_no_fa;
|
|
+ u32 nrg_th_ofdm;
|
|
+
|
|
+ u8 state;
|
|
+};
|
|
+
|
|
+/* Chain noise (differential Rx gain) calib data */
|
|
+struct iwl_chain_noise_data {
|
|
+ u8 state;
|
|
+ u16 beacon_count;
|
|
+ u32 chain_noise_a;
|
|
+ u32 chain_noise_b;
|
|
+ u32 chain_noise_c;
|
|
+ u32 chain_signal_a;
|
|
+ u32 chain_signal_b;
|
|
+ u32 chain_signal_c;
|
|
+ u8 disconn_array[NUM_RX_CHAINS];
|
|
+ u8 delta_gain_code[NUM_RX_CHAINS];
|
|
+ u8 radio_write;
|
|
+};
|
|
+
|
|
+/* IWL4965 */
|
|
+#define RATE_MCS_CODE_MSK 0x7
|
|
+#define RATE_MCS_MIMO_POS 3
|
|
+#define RATE_MCS_MIMO_MSK 0x8
|
|
+#define RATE_MCS_HT_DUP_POS 5
|
|
+#define RATE_MCS_HT_DUP_MSK 0x20
|
|
+#define RATE_MCS_FLAGS_POS 8
|
|
+#define RATE_MCS_HT_POS 8
|
|
+#define RATE_MCS_HT_MSK 0x100
|
|
+#define RATE_MCS_CCK_POS 9
|
|
+#define RATE_MCS_CCK_MSK 0x200
|
|
+#define RATE_MCS_GF_POS 10
|
|
+#define RATE_MCS_GF_MSK 0x400
|
|
+
|
|
+#define RATE_MCS_FAT_POS 11
|
|
+#define RATE_MCS_FAT_MSK 0x800
|
|
+#define RATE_MCS_DUP_POS 12
|
|
+#define RATE_MCS_DUP_MSK 0x1000
|
|
+#define RATE_MCS_SGI_POS 13
|
|
+#define RATE_MCS_SGI_MSK 0x2000
|
|
+
|
|
+#define EEPROM_SEM_TIMEOUT 10
|
|
+#define EEPROM_SEM_RETRY_LIMIT 1000
|
|
+
|
|
+#endif /* IWL == 4965 */
|
|
+#endif /* __iwl_4965_h__ */
|
|
diff --git a/drivers/net/wireless/iwl-base.c b/drivers/net/wireless/iwl-base.c
|
|
new file mode 100644
|
|
index 0000000..4b80728
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/iwl-base.c
|
|
@@ -0,0 +1,9643 @@
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * Copyright(c) 2003 - 2007 Intel Corporation. All rights reserved.
|
|
+ *
|
|
+ * Portions of this file are derived from the ipw3945 project, as well
|
|
+ * as portions of the ieee80211 subsystem header files.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify it
|
|
+ * under the terms of version 2 of the GNU General Public License as
|
|
+ * published by the Free Software Foundation.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful, but WITHOUT
|
|
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
+ * more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License along with
|
|
+ * this program; if not, write to the Free Software Foundation, Inc.,
|
|
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
|
|
+ *
|
|
+ * The full GNU General Public License is included in this distribution in the
|
|
+ * file called LICENSE.
|
|
+ *
|
|
+ * Contact Information:
|
|
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
|
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+/*
|
|
+ * NOTE: This file (iwl-base.c) is used to build to multiple hardware targets
|
|
+ * by defining IWL to either 3945 or 4965. The Makefile used when building
|
|
+ * the base targets will create base-3945.o and base-4965.o
|
|
+ *
|
|
+ * The eventual goal is to move as many of the #if IWL / #endif blocks out of
|
|
+ * this file and into the hardware specific implementation files (iwl-XXXX.c)
|
|
+ * and leave only the common (non #ifdef sprinkled) code in this file
|
|
+ */
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/version.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/pci.h>
|
|
+#include <linux/dma-mapping.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/skbuff.h>
|
|
+#include <linux/netdevice.h>
|
|
+#include <linux/wireless.h>
|
|
+#include <linux/firmware.h>
|
|
+#include <linux/skbuff.h>
|
|
+#include <linux/netdevice.h>
|
|
+#include <linux/etherdevice.h>
|
|
+#include <linux/if_arp.h>
|
|
+
|
|
+#include <net/ieee80211_radiotap.h>
|
|
+#include <net/mac80211.h>
|
|
+
|
|
+#include <asm/div64.h>
|
|
+
|
|
+#include "iwlwifi.h"
|
|
+#include "iwl-helpers.h"
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+u32 iwl_debug_level;
|
|
+#endif
|
|
+
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * module boiler plate
|
|
+ *
|
|
+ ******************************************************************************/
|
|
+
|
|
+/* module parameters */
|
|
+int iwl_param_disable_hw_scan;
|
|
+int iwl_param_debug;
|
|
+int iwl_param_disable; /* def: enable radio */
|
|
+int iwl_param_antenna; /* def: 0 = both antennas (use diversity) */
|
|
+int iwl_param_hwcrypto; /* def: using software encryption */
|
|
+int iwl_param_qos_enable = 1;
|
|
+int iwl_param_queues_num = IWL_MAX_NUM_QUEUES;
|
|
+
|
|
+/*
|
|
+ * module name, copyright, version, etc.
|
|
+ * NOTE: DRV_NAME is defined in iwlwifi.h for use by iwl-debug.h and printk
|
|
+ */
|
|
+
|
|
+#if IWL == 3945
|
|
+#define DRV_DESCRIPTION \
|
|
+"Intel(R) PRO/Wireless 3945ABG/BG Network Connection driver for Linux"
|
|
+#elif IWL == 4965
|
|
+#define DRV_DESCRIPTION \
|
|
+"Intel(R) Wireless WiFi Link 4965AGN driver for Linux"
|
|
+#else
|
|
+BUILD_BUG()
|
|
+#endif
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+#define VD "d"
|
|
+#else
|
|
+#define VD
|
|
+#endif
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_SPECTRUM_MEASUREMENT
|
|
+#define VS "s"
|
|
+#else
|
|
+#define VS
|
|
+#endif
|
|
+
|
|
+#define IWLWIFI_VERSION "0.1.11k" VD VS
|
|
+#define DRV_COPYRIGHT "Copyright(c) 2003-2007 Intel Corporation"
|
|
+#define DRV_VERSION IWLWIFI_VERSION
|
|
+
|
|
+/* Change firmware file name, using "-" and incrementing number,
|
|
+ * *only* when uCode interface or architecture changes so that it
|
|
+ * is not compatible with earlier drivers.
|
|
+ * This number will also appear in << 8 position of 1st dword of uCode file */
|
|
+#define IWL3945_UCODE_API "-1"
|
|
+#define IWL4965_UCODE_API "-1"
|
|
+
|
|
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
|
|
+MODULE_VERSION(DRV_VERSION);
|
|
+MODULE_AUTHOR(DRV_COPYRIGHT);
|
|
+MODULE_LICENSE("GPL");
|
|
+
|
|
+__le16 *ieee80211_get_qos_ctrl(struct ieee80211_hdr *hdr)
|
|
+{
|
|
+ u16 fc = le16_to_cpu(hdr->frame_control);
|
|
+ int hdr_len = ieee80211_get_hdrlen(fc);
|
|
+
|
|
+ if ((fc & 0x00cc) == (IEEE80211_STYPE_QOS_DATA | IEEE80211_FTYPE_DATA))
|
|
+ return (__le16 *) ((u8 *) hdr + hdr_len - QOS_CONTROL_LEN);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static const struct ieee80211_hw_mode *iwl_get_hw_mode(
|
|
+ struct iwl_priv *priv, int mode)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < 3; i++)
|
|
+ if (priv->modes[i].mode == mode)
|
|
+ return &priv->modes[i];
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static int iwl_is_empty_essid(const char *essid, int essid_len)
|
|
+{
|
|
+ /* Single white space is for Linksys APs */
|
|
+ if (essid_len == 1 && essid[0] == ' ')
|
|
+ return 1;
|
|
+
|
|
+ /* Otherwise, if the entire essid is 0, we assume it is hidden */
|
|
+ while (essid_len) {
|
|
+ essid_len--;
|
|
+ if (essid[essid_len] != '\0')
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static const char *iwl_escape_essid(const char *essid, u8 essid_len)
|
|
+{
|
|
+ static char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
|
|
+ const char *s = essid;
|
|
+ char *d = escaped;
|
|
+
|
|
+ if (iwl_is_empty_essid(essid, essid_len)) {
|
|
+ memcpy(escaped, "<hidden>", sizeof("<hidden>"));
|
|
+ return escaped;
|
|
+ }
|
|
+
|
|
+ essid_len = min(essid_len, (u8) IW_ESSID_MAX_SIZE);
|
|
+ while (essid_len--) {
|
|
+ if (*s == '\0') {
|
|
+ *d++ = '\\';
|
|
+ *d++ = '0';
|
|
+ s++;
|
|
+ } else
|
|
+ *d++ = *s++;
|
|
+ }
|
|
+ *d = '\0';
|
|
+ return escaped;
|
|
+}
|
|
+
|
|
+static void iwl_print_hex_dump(int level, void *p, u32 len)
|
|
+{
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+ if (!(iwl_debug_level & level))
|
|
+ return;
|
|
+
|
|
+ print_hex_dump(KERN_DEBUG, "iwl data: ", DUMP_PREFIX_OFFSET, 16, 1,
|
|
+ p, len, 1);
|
|
+#endif
|
|
+}
|
|
+
|
|
+/*************** DMA-QUEUE-GENERAL-FUNCTIONS *****
|
|
+ * DMA services
|
|
+ *
|
|
+ * Theory of operation
|
|
+ *
|
|
+ * A queue is a circular buffers with 'Read' and 'Write' pointers.
|
|
+ * 2 empty entries always kept in the buffer to protect from overflow.
|
|
+ *
|
|
+ * For Tx queue, there are low mark and high mark limits. If, after queuing
|
|
+ * the packet for Tx, free space become < low mark, Tx queue stopped. When
|
|
+ * reclaiming packets (on 'tx done IRQ), if free space become > high mark,
|
|
+ * Tx queue resumed.
|
|
+ *
|
|
+ * The IPW operates with six queues, one receive queue in the device's
|
|
+ * sram, one transmit queue for sending commands to the device firmware,
|
|
+ * and four transmit queues for data.
|
|
+ *
|
|
+ * The four transmit queues allow for performing quality of service (qos)
|
|
+ * transmissions as per the 802.11 protocol. Currently Linux does not
|
|
+ * provide a mechanism to the user for utilizing prioritized queues, so
|
|
+ * we only utilize the first data transmit queue (queue1).
|
|
+ ***************************************************/
|
|
+
|
|
+static int iwl_queue_space(const struct iwl_queue *q)
|
|
+{
|
|
+ int s = q->last_used - q->first_empty;
|
|
+
|
|
+ if (q->last_used > q->first_empty)
|
|
+ s -= q->n_bd;
|
|
+
|
|
+ if (s <= 0)
|
|
+ s += q->n_window;
|
|
+ /* keep some reserve to not confuse empty and full situations */
|
|
+ s -= 2;
|
|
+ if (s < 0)
|
|
+ s = 0;
|
|
+ return s;
|
|
+}
|
|
+
|
|
+/* XXX: n_bd must be power-of-two size */
|
|
+static inline int iwl_queue_inc_wrap(int index, int n_bd)
|
|
+{
|
|
+ return ++index & (n_bd - 1);
|
|
+}
|
|
+
|
|
+/* XXX: n_bd must be power-of-two size */
|
|
+static inline int iwl_queue_dec_wrap(int index, int n_bd)
|
|
+{
|
|
+ return --index & (n_bd - 1);
|
|
+}
|
|
+
|
|
+static inline int x2_queue_used(const struct iwl_queue *q, int i)
|
|
+{
|
|
+ return q->first_empty > q->last_used ?
|
|
+ (i >= q->last_used && i < q->first_empty) :
|
|
+ !(i < q->last_used && i >= q->first_empty);
|
|
+}
|
|
+
|
|
+static inline u8 get_cmd_index(struct iwl_queue *q, u32 index, int is_huge)
|
|
+{
|
|
+ if (is_huge)
|
|
+ return q->n_window;
|
|
+
|
|
+ return index & (q->n_window - 1);
|
|
+}
|
|
+
|
|
+static int iwl_queue_init(struct iwl_priv *priv, struct iwl_queue *q,
|
|
+ int count, int slots_num, u32 id)
|
|
+{
|
|
+ q->n_bd = count;
|
|
+ q->n_window = slots_num;
|
|
+ q->id = id;
|
|
+
|
|
+ /* count must be power-of-two size, otherwise iwl_queue_inc_wrap
|
|
+ * and iwl_queue_dec_wrap are broken. */
|
|
+ BUG_ON(!is_power_of_2(count));
|
|
+
|
|
+ /* slots_num must be power-of-two size, otherwise
|
|
+ * get_cmd_index is broken. */
|
|
+ BUG_ON(!is_power_of_2(slots_num));
|
|
+
|
|
+ q->low_mark = q->n_window / 4;
|
|
+ if (q->low_mark < 4)
|
|
+ q->low_mark = 4;
|
|
+
|
|
+ q->high_mark = q->n_window / 8;
|
|
+ if (q->high_mark < 2)
|
|
+ q->high_mark = 2;
|
|
+
|
|
+ q->first_empty = q->last_used = 0;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int iwl_tx_queue_alloc(struct iwl_priv *priv,
|
|
+ struct iwl_tx_queue *txq, u32 id)
|
|
+{
|
|
+ struct pci_dev *dev = priv->pci_dev;
|
|
+
|
|
+ if (id != IWL_CMD_QUEUE_NUM) {
|
|
+ txq->txb = kmalloc(sizeof(txq->txb[0]) *
|
|
+ TFD_QUEUE_SIZE_MAX, GFP_KERNEL);
|
|
+ if (!txq->txb) {
|
|
+ IWL_ERROR("kmalloc for auxilary BD "
|
|
+ "structures failed\n");
|
|
+ goto error;
|
|
+ }
|
|
+ } else
|
|
+ txq->txb = NULL;
|
|
+
|
|
+ txq->bd = pci_alloc_consistent(dev,
|
|
+ sizeof(txq->bd[0]) * TFD_QUEUE_SIZE_MAX,
|
|
+ &txq->q.dma_addr);
|
|
+
|
|
+ if (!txq->bd) {
|
|
+ IWL_ERROR("pci_alloc_consistent(%zd) failed\n",
|
|
+ sizeof(txq->bd[0]) * TFD_QUEUE_SIZE_MAX);
|
|
+ goto error;
|
|
+ }
|
|
+ txq->q.id = id;
|
|
+
|
|
+ return 0;
|
|
+
|
|
+ error:
|
|
+ if (txq->txb) {
|
|
+ kfree(txq->txb);
|
|
+ txq->txb = NULL;
|
|
+ }
|
|
+
|
|
+ return -ENOMEM;
|
|
+}
|
|
+
|
|
+int iwl_tx_queue_init(struct iwl_priv *priv,
|
|
+ struct iwl_tx_queue *txq, int slots_num, u32 txq_id)
|
|
+{
|
|
+ struct pci_dev *dev = priv->pci_dev;
|
|
+ int len;
|
|
+ int rc = 0;
|
|
+
|
|
+ /* alocate command space + one big command for scan since scan
|
|
+ * command is very huge the system will not have two scan at the
|
|
+ * same time */
|
|
+ len = sizeof(struct iwl_cmd) * slots_num;
|
|
+ if (txq_id == IWL_CMD_QUEUE_NUM);
|
|
+ len += IWL_MAX_SCAN_SIZE;
|
|
+ txq->cmd = pci_alloc_consistent(dev, len, &txq->dma_addr_cmd);
|
|
+ if (!txq->cmd)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ rc = iwl_tx_queue_alloc(priv, txq, txq_id);
|
|
+ if (rc) {
|
|
+ pci_free_consistent(dev, len, txq->cmd, txq->dma_addr_cmd);
|
|
+
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ txq->need_update = 0;
|
|
+
|
|
+ /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise
|
|
+ * iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */
|
|
+ BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1));
|
|
+ iwl_queue_init(priv, &txq->q, TFD_QUEUE_SIZE_MAX, slots_num, txq_id);
|
|
+
|
|
+ iwl_hw_tx_queue_init(priv, txq);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_tx_queue_free - Deallocate DMA queue.
|
|
+ * @txq: Transmit queue to deallocate.
|
|
+ *
|
|
+ * Empty queue by removing and destroying all BD's.
|
|
+ * Free all buffers. txq itself is not freed.
|
|
+ *
|
|
+ */
|
|
+void iwl_tx_queue_free(struct iwl_priv *priv, struct iwl_tx_queue *txq)
|
|
+{
|
|
+ struct iwl_queue *q = &txq->q;
|
|
+ struct pci_dev *dev = priv->pci_dev;
|
|
+ int len;
|
|
+
|
|
+ if (q->n_bd == 0)
|
|
+ return;
|
|
+
|
|
+ /* first, empty all BD's */
|
|
+ for (; q->first_empty != q->last_used;
|
|
+ q->last_used = iwl_queue_inc_wrap(q->last_used, q->n_bd))
|
|
+ iwl_hw_txq_free_tfd(priv, txq);
|
|
+
|
|
+ len = sizeof(struct iwl_cmd) * q->n_window;
|
|
+ if (q->id == IWL_CMD_QUEUE_NUM);
|
|
+ len += IWL_MAX_SCAN_SIZE;
|
|
+
|
|
+ pci_free_consistent(dev, len, txq->cmd, txq->dma_addr_cmd);
|
|
+
|
|
+ /* free buffers belonging to queue itself */
|
|
+ if (txq->q.n_bd)
|
|
+ pci_free_consistent(dev, sizeof(struct iwl_tfd_frame) *
|
|
+ txq->q.n_bd, txq->bd, txq->q.dma_addr);
|
|
+
|
|
+ if (txq->txb) {
|
|
+ kfree(txq->txb);
|
|
+ txq->txb = NULL;
|
|
+ }
|
|
+
|
|
+ /* 0 fill whole structure */
|
|
+ memset(txq, 0, sizeof(*txq));
|
|
+}
|
|
+
|
|
+const u8 BROADCAST_ADDR[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
|
+
|
|
+/*************** STATION TABLE MANAGEMENT ****
|
|
+ *
|
|
+ * NOTE: This needs to be overhauled to better synchronize between
|
|
+ * how the iwl-4965.c is using iwl_hw_find_station vs. iwl-3945.c
|
|
+ *
|
|
+ * mac80211 should also be examined to determine if sta_info is duplicating
|
|
+ * the functionality provided here
|
|
+ */
|
|
+
|
|
+/**************************************************************/
|
|
+
|
|
+static u8 iwl_remove_station(struct iwl_priv *priv, const u8 *bssid, int is_ap)
|
|
+{
|
|
+ int index = IWL_INVALID_STATION;
|
|
+ int i;
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&priv->sta_lock, flags);
|
|
+
|
|
+ if (is_ap) {
|
|
+ index = IWL_AP_ID;
|
|
+ if ((priv->stations[index].used))
|
|
+ priv->stations[index].used = 0;
|
|
+ } else if (is_broadcast_ether_addr(bssid)) {
|
|
+ index = IWL_BROADCAST_ID;
|
|
+ if ((priv->stations[index].used))
|
|
+ priv->stations[index].used = 0;
|
|
+ } else {
|
|
+ for (i = IWL_STA_ID; i < priv->num_stations + IWL_STA_ID; i++) {
|
|
+ if (priv->stations[i].used &&
|
|
+ !compare_ether_addr(priv->stations[i].sta.sta.addr,
|
|
+ bssid)) {
|
|
+ index = i;
|
|
+ priv->stations[index].used = 0;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if (index != IWL_INVALID_STATION) {
|
|
+ if (priv->num_stations > 0)
|
|
+ priv->num_stations--;
|
|
+ }
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->sta_lock, flags);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void iwl_clear_stations_table(struct iwl_priv *priv)
|
|
+{
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&priv->sta_lock, flags);
|
|
+
|
|
+ priv->num_stations = 0;
|
|
+ memset(priv->stations, 0, sizeof(priv->stations));
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->sta_lock, flags);
|
|
+}
|
|
+
|
|
+u8 iwl_add_station(struct iwl_priv *priv, const u8 *bssid, int is_ap, u8 flags)
|
|
+{
|
|
+ int i = IWL_STATION_COUNT;
|
|
+ int index = IWL_INVALID_STATION;
|
|
+ struct iwl_station_entry *station;
|
|
+ unsigned long flags_spin;
|
|
+#if IWL == 3945
|
|
+ u8 rate;
|
|
+#endif
|
|
+ spin_lock_irqsave(&priv->sta_lock, flags_spin);
|
|
+ if (is_ap) {
|
|
+ index = IWL_AP_ID;
|
|
+ if (priv->stations[index].used &&
|
|
+ !compare_ether_addr(priv->stations[index].sta.sta.addr,
|
|
+ bssid))
|
|
+ goto done;
|
|
+ } else if (is_broadcast_ether_addr(bssid)) {
|
|
+ index = IWL_BROADCAST_ID;
|
|
+ if (priv->stations[index].used &&
|
|
+ !compare_ether_addr(priv->stations[index].sta.sta.addr,
|
|
+ bssid))
|
|
+ goto done;
|
|
+ } else {
|
|
+ for (i = IWL_STA_ID; i < priv->num_stations + IWL_STA_ID; i++) {
|
|
+ if (priv->stations[i].used &&
|
|
+ !compare_ether_addr(priv->stations[i].sta.sta.addr,
|
|
+ bssid))
|
|
+ goto done;
|
|
+
|
|
+ if (!priv->stations[i].used &&
|
|
+ index == IWL_INVALID_STATION)
|
|
+ index = i;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (index != IWL_INVALID_STATION)
|
|
+ i = index;
|
|
+
|
|
+ if (i == IWL_STATION_COUNT) {
|
|
+ index = IWL_INVALID_STATION;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_ASSOC("Adding STA ID %d: " MAC_FMT "\n", i, MAC_ARG(bssid));
|
|
+ station = &priv->stations[i];
|
|
+
|
|
+ station->used = 1;
|
|
+ memset(&station->sta, 0, sizeof(struct iwl_addsta_cmd));
|
|
+ memcpy(station->sta.sta.addr, bssid, ETH_ALEN);
|
|
+ station->sta.mode = 0;
|
|
+ station->sta.sta.sta_id = i;
|
|
+ station->sta.station_flags = 0;
|
|
+#if IWL == 3945
|
|
+ rate = (priv->phymode == MODE_IEEE80211A) ? IWL_RATE_6M_PLCP :
|
|
+ IWL_RATE_1M_PLCP | priv->hw_setting.cck_flag;
|
|
+
|
|
+ /* Turn on both antennas for the station... */
|
|
+ station->sta.rate_n_flags =
|
|
+ iwl_hw_set_rate_n_flags(rate, RATE_MCS_ANT_AB_MSK);
|
|
+
|
|
+ station->sta.station_flags |= STA_FLG_TX_RATE_MSK;
|
|
+
|
|
+ station->current_rate.rate_n_flags =
|
|
+ le16_to_cpu(station->sta.rate_n_flags);
|
|
+#endif
|
|
+
|
|
+#if IWL == 4965
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+ if (is_ap) {
|
|
+ iwl4965_set_ht_add_station(priv, i);
|
|
+ iwl4965_set_rxon_chain(priv);
|
|
+ }
|
|
+#endif /*CONFIG_IWLWIFI_HT*/
|
|
+#endif
|
|
+
|
|
+ priv->num_stations++;
|
|
+ spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
|
|
+ iwl_send_add_station(priv, &station->sta, flags);
|
|
+ return i;
|
|
+
|
|
+ done:
|
|
+ spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
|
|
+ return index;
|
|
+}
|
|
+
|
|
+/*************** DRIVER STATUS FUNCTIONS *****/
|
|
+
|
|
+static inline int iwl_is_ready(struct iwl_priv *priv)
|
|
+{
|
|
+ /* The adapter is 'ready' if READY and GEO_CONFIGURED bits are
|
|
+ * set but EXIT_PENDING is not */
|
|
+ return test_bit(STATUS_READY, &priv->status) &&
|
|
+ test_bit(STATUS_GEO_CONFIGURED, &priv->status) &&
|
|
+ !test_bit(STATUS_EXIT_PENDING, &priv->status);
|
|
+}
|
|
+
|
|
+static inline int iwl_is_alive(struct iwl_priv *priv)
|
|
+{
|
|
+ return test_bit(STATUS_ALIVE, &priv->status);
|
|
+}
|
|
+
|
|
+static inline int iwl_is_init(struct iwl_priv *priv)
|
|
+{
|
|
+ return test_bit(STATUS_INIT, &priv->status);
|
|
+}
|
|
+
|
|
+static inline int iwl_is_rfkill(struct iwl_priv *priv)
|
|
+{
|
|
+ return test_bit(STATUS_RF_KILL_HW, &priv->status) ||
|
|
+ test_bit(STATUS_RF_KILL_SW, &priv->status);
|
|
+}
|
|
+
|
|
+static inline int iwl_is_ready_rf(struct iwl_priv *priv)
|
|
+{
|
|
+
|
|
+ if (iwl_is_rfkill(priv))
|
|
+ return 0;
|
|
+
|
|
+ return iwl_is_ready(priv);
|
|
+}
|
|
+
|
|
+/*************** HOST COMMAND QUEUE FUNCTIONS *****/
|
|
+
|
|
+#define IWL_CMD(x) case x : return #x
|
|
+
|
|
+static const char *get_cmd_string(u8 cmd)
|
|
+{
|
|
+ switch (cmd) {
|
|
+ IWL_CMD(REPLY_ALIVE);
|
|
+ IWL_CMD(REPLY_ERROR);
|
|
+ IWL_CMD(REPLY_RXON);
|
|
+ IWL_CMD(REPLY_RXON_ASSOC);
|
|
+ IWL_CMD(REPLY_QOS_PARAM);
|
|
+ IWL_CMD(REPLY_RXON_TIMING);
|
|
+ IWL_CMD(REPLY_ADD_STA);
|
|
+ IWL_CMD(REPLY_REMOVE_STA);
|
|
+ IWL_CMD(REPLY_REMOVE_ALL_STA);
|
|
+#if IWL == 3945
|
|
+ IWL_CMD(REPLY_3945_RX);
|
|
+#endif
|
|
+ IWL_CMD(REPLY_TX);
|
|
+ IWL_CMD(REPLY_RATE_SCALE);
|
|
+ IWL_CMD(REPLY_LEDS_CMD);
|
|
+ IWL_CMD(REPLY_TX_LINK_QUALITY_CMD);
|
|
+ IWL_CMD(RADAR_NOTIFICATION);
|
|
+ IWL_CMD(REPLY_QUIET_CMD);
|
|
+ IWL_CMD(REPLY_CHANNEL_SWITCH);
|
|
+ IWL_CMD(CHANNEL_SWITCH_NOTIFICATION);
|
|
+ IWL_CMD(REPLY_SPECTRUM_MEASUREMENT_CMD);
|
|
+ IWL_CMD(SPECTRUM_MEASURE_NOTIFICATION);
|
|
+ IWL_CMD(POWER_TABLE_CMD);
|
|
+ IWL_CMD(PM_SLEEP_NOTIFICATION);
|
|
+ IWL_CMD(PM_DEBUG_STATISTIC_NOTIFIC);
|
|
+ IWL_CMD(REPLY_SCAN_CMD);
|
|
+ IWL_CMD(REPLY_SCAN_ABORT_CMD);
|
|
+ IWL_CMD(SCAN_START_NOTIFICATION);
|
|
+ IWL_CMD(SCAN_RESULTS_NOTIFICATION);
|
|
+ IWL_CMD(SCAN_COMPLETE_NOTIFICATION);
|
|
+ IWL_CMD(BEACON_NOTIFICATION);
|
|
+ IWL_CMD(REPLY_TX_BEACON);
|
|
+ IWL_CMD(WHO_IS_AWAKE_NOTIFICATION);
|
|
+ IWL_CMD(QUIET_NOTIFICATION);
|
|
+ IWL_CMD(REPLY_TX_PWR_TABLE_CMD);
|
|
+ IWL_CMD(MEASURE_ABORT_NOTIFICATION);
|
|
+ IWL_CMD(REPLY_BT_CONFIG);
|
|
+ IWL_CMD(REPLY_STATISTICS_CMD);
|
|
+ IWL_CMD(STATISTICS_NOTIFICATION);
|
|
+ IWL_CMD(REPLY_CARD_STATE_CMD);
|
|
+ IWL_CMD(CARD_STATE_NOTIFICATION);
|
|
+ IWL_CMD(MISSED_BEACONS_NOTIFICATION);
|
|
+#if IWL == 4965
|
|
+ IWL_CMD(REPLY_CT_KILL_CONFIG_CMD);
|
|
+ IWL_CMD(SENSITIVITY_CMD);
|
|
+ IWL_CMD(REPLY_PHY_CALIBRATION_CMD);
|
|
+ IWL_CMD(REPLY_RX_PHY_CMD);
|
|
+ IWL_CMD(REPLY_RX_MPDU_CMD);
|
|
+ IWL_CMD(REPLY_4965_RX);
|
|
+ IWL_CMD(REPLY_COMPRESSED_BA);
|
|
+#endif
|
|
+ default:
|
|
+ return "UNKNOWN";
|
|
+
|
|
+ }
|
|
+}
|
|
+
|
|
+#define HOST_COMPLETE_TIMEOUT (HZ / 2)
|
|
+
|
|
+/**
|
|
+ * iwl_enqueue_hcmd - enqueue a uCode command
|
|
+ * @priv: device private data point
|
|
+ * @cmd: a point to the ucode command structure
|
|
+ *
|
|
+ * The function returns < 0 values to indicate the operation is
|
|
+ * failed. On success, it turns the index (> 0) of command in the
|
|
+ * command queue.
|
|
+ */
|
|
+static int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
|
|
+{
|
|
+ struct iwl_tx_queue *txq = &priv->txq[IWL_CMD_QUEUE_NUM];
|
|
+ struct iwl_queue *q = &txq->q;
|
|
+ struct iwl_tfd_frame *tfd;
|
|
+ u32 *control_flags;
|
|
+ struct iwl_cmd *out_cmd;
|
|
+ u32 idx;
|
|
+ u16 fix_size = (u16)(cmd->meta.len + sizeof(out_cmd->hdr));
|
|
+ dma_addr_t phys_addr;
|
|
+#if IWL == 3945
|
|
+ int pad;
|
|
+ u16 count;
|
|
+#endif
|
|
+ int rc = 0;
|
|
+ unsigned long flags;
|
|
+
|
|
+ /* If any of the command structures end up being larger than
|
|
+ * the TFD_MAX_PAYLOAD_SIZE, and it sent as a 'small' command then
|
|
+ * we will need to increase the size of the TFD entries */
|
|
+ BUG_ON((fix_size > TFD_MAX_PAYLOAD_SIZE) &&
|
|
+ !(cmd->meta.flags & CMD_SIZE_HUGE));
|
|
+
|
|
+ if (iwl_queue_space(q) < ((cmd->meta.flags & CMD_ASYNC) ? 2 : 1)) {
|
|
+ IWL_ERROR("No space for Tx\n");
|
|
+ return -ENOSPC;
|
|
+ }
|
|
+
|
|
+ spin_lock_irqsave(&priv->hcmd_lock, flags);
|
|
+
|
|
+ tfd = &txq->bd[q->first_empty];
|
|
+ memset(tfd, 0, sizeof(*tfd));
|
|
+
|
|
+ control_flags = (u32 *) tfd;
|
|
+
|
|
+ idx = get_cmd_index(q, q->first_empty, cmd->meta.flags & CMD_SIZE_HUGE);
|
|
+ out_cmd = &txq->cmd[idx];
|
|
+
|
|
+ out_cmd->hdr.cmd = cmd->id;
|
|
+ memcpy(&out_cmd->meta, &cmd->meta, sizeof(cmd->meta));
|
|
+ memcpy(&out_cmd->cmd.payload, cmd->data, cmd->meta.len);
|
|
+
|
|
+ /* At this point, the out_cmd now has all of the incoming cmd
|
|
+ * information */
|
|
+
|
|
+ out_cmd->hdr.flags = 0;
|
|
+ out_cmd->hdr.sequence = cpu_to_le16(QUEUE_TO_SEQ(IWL_CMD_QUEUE_NUM) |
|
|
+ INDEX_TO_SEQ(q->first_empty));
|
|
+ if (out_cmd->meta.flags & CMD_SIZE_HUGE)
|
|
+ out_cmd->hdr.sequence |= cpu_to_le16(SEQ_HUGE_FRAME);
|
|
+
|
|
+ phys_addr = txq->dma_addr_cmd + sizeof(txq->cmd[0]) * idx +
|
|
+ offsetof(struct iwl_cmd, hdr);
|
|
+ iwl_hw_txq_attach_buf_to_tfd(priv, tfd, phys_addr, fix_size);
|
|
+
|
|
+#if IWL == 3945
|
|
+ pad = U32_PAD(out_cmd->meta.len);
|
|
+ count = TFD_CTL_COUNT_GET(*control_flags);
|
|
+ *control_flags = TFD_CTL_COUNT_SET(count) | TFD_CTL_PAD_SET(pad);
|
|
+#endif
|
|
+
|
|
+ IWL_DEBUG_HC("Sending command %s (#%x), seq: 0x%04X, "
|
|
+ "%d bytes at %d[%d]:%d\n",
|
|
+ get_cmd_string(out_cmd->hdr.cmd),
|
|
+ out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence),
|
|
+ fix_size, q->first_empty, idx, IWL_CMD_QUEUE_NUM);
|
|
+
|
|
+ txq->need_update = 1;
|
|
+#if IWL == 4965
|
|
+ rc = iwl4965_tx_queue_update_wr_ptr(priv, txq, 0);
|
|
+ q->first_empty = iwl_queue_inc_wrap(q->first_empty, q->n_bd);
|
|
+ iwl_tx_queue_update_write_ptr(priv, txq);
|
|
+#elif IWL == 3945
|
|
+ q->first_empty = iwl_queue_inc_wrap(q->first_empty, q->n_bd);
|
|
+ rc = iwl_tx_queue_update_write_ptr(priv, txq);
|
|
+#endif
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->hcmd_lock, flags);
|
|
+ return rc ? rc : idx;
|
|
+}
|
|
+
|
|
+static int __iwl_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
|
|
+{
|
|
+ int rc;
|
|
+
|
|
+ cmd->meta.len = cmd->len;
|
|
+
|
|
+ rc = iwl_enqueue_hcmd(priv, cmd);
|
|
+ if (rc < 0) {
|
|
+ IWL_ERROR("Error sending %s: iwl_enqueue_hcmd failed: %d\n",
|
|
+ get_cmd_string(cmd->id), rc);
|
|
+ return -ENOSPC;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+int iwl_send_cmd_async(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ BUG_ON(!(cmd->meta.flags & CMD_ASYNC));
|
|
+
|
|
+ /* An asynchronous command can not expect an SKB to be set. */
|
|
+ BUG_ON(cmd->meta.flags & CMD_WANT_SKB);
|
|
+
|
|
+ /* An asynchronous command MUST have a callback. */
|
|
+ BUG_ON(!cmd->meta.u.callback);
|
|
+
|
|
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
|
+ return -EBUSY;
|
|
+
|
|
+ ret = __iwl_send_cmd(priv, cmd);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int iwl_send_cmd_sync(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
|
|
+{
|
|
+ int cmd_idx;
|
|
+ int ret;
|
|
+ static atomic_t entry = ATOMIC_INIT(0); /* reentrance protection */
|
|
+
|
|
+ BUG_ON(cmd->meta.flags & CMD_ASYNC);
|
|
+
|
|
+ /* A synchronous command can not have a callback set. */
|
|
+ BUG_ON(cmd->meta.u.callback != NULL);
|
|
+
|
|
+ if (atomic_xchg(&entry, 1)) {
|
|
+ IWL_ERROR("Error sending %s: Already sending a host command\n",
|
|
+ get_cmd_string(cmd->id));
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ set_bit(STATUS_HCMD_ACTIVE, &priv->status);
|
|
+
|
|
+ if (cmd->meta.flags & CMD_WANT_SKB)
|
|
+ cmd->meta.source = &cmd->meta;
|
|
+
|
|
+ ret = __iwl_send_cmd(priv, cmd);
|
|
+ if (ret < 0)
|
|
+ goto out;
|
|
+
|
|
+ cmd_idx = ret;
|
|
+
|
|
+ ret = wait_event_interruptible_timeout(priv->wait_command_queue,
|
|
+ !test_bit(STATUS_HCMD_ACTIVE, &priv->status),
|
|
+ HOST_COMPLETE_TIMEOUT);
|
|
+ if (!ret) {
|
|
+ if (test_bit(STATUS_HCMD_ACTIVE, &priv->status)) {
|
|
+ IWL_ERROR("Error sending %s: time out after %dms.\n",
|
|
+ get_cmd_string(cmd->id),
|
|
+ jiffies_to_msecs(HOST_COMPLETE_TIMEOUT));
|
|
+
|
|
+ clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
|
|
+ ret = -ETIMEDOUT;
|
|
+ goto cancel;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (test_bit(STATUS_RF_KILL_HW, &priv->status)) {
|
|
+ IWL_DEBUG_INFO("Command %s aborted: RF KILL Switch\n",
|
|
+ get_cmd_string(cmd->id));
|
|
+ ret = -ECANCELED;
|
|
+ goto fail;
|
|
+ }
|
|
+ if (test_bit(STATUS_FW_ERROR, &priv->status)) {
|
|
+ IWL_DEBUG_INFO("Command %s failed: FW Error\n",
|
|
+ get_cmd_string(cmd->id));
|
|
+ ret = -EIO;
|
|
+ goto fail;
|
|
+ }
|
|
+ if ((cmd->meta.flags & CMD_WANT_SKB) && !cmd->meta.u.skb) {
|
|
+ IWL_ERROR("Error: Response NULL in '%s'\n",
|
|
+ get_cmd_string(cmd->id));
|
|
+ ret = -EIO;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ ret = 0;
|
|
+ goto out;
|
|
+
|
|
+cancel:
|
|
+ if (cmd->meta.flags & CMD_WANT_SKB) {
|
|
+ struct iwl_cmd *qcmd;
|
|
+
|
|
+ /* Cancel the CMD_WANT_SKB flag for the cmd in the
|
|
+ * TX cmd queue. Otherwise in case the cmd comes
|
|
+ * in later, it will possibly set an invalid
|
|
+ * address (cmd->meta.source). */
|
|
+ qcmd = &priv->txq[IWL_CMD_QUEUE_NUM].cmd[cmd_idx];
|
|
+ qcmd->meta.flags &= ~CMD_WANT_SKB;
|
|
+ }
|
|
+fail:
|
|
+ if (cmd->meta.u.skb) {
|
|
+ dev_kfree_skb_any(cmd->meta.u.skb);
|
|
+ cmd->meta.u.skb = NULL;
|
|
+ }
|
|
+out:
|
|
+ atomic_set(&entry, 0);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int iwl_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
|
|
+{
|
|
+ /* A command can not be asynchronous AND expect an SKB to be set. */
|
|
+ BUG_ON((cmd->meta.flags & CMD_ASYNC) &&
|
|
+ (cmd->meta.flags & CMD_WANT_SKB));
|
|
+
|
|
+ if (cmd->meta.flags & CMD_ASYNC)
|
|
+ return iwl_send_cmd_async(priv, cmd);
|
|
+
|
|
+ return iwl_send_cmd_sync(priv, cmd);
|
|
+}
|
|
+
|
|
+int iwl_send_cmd_pdu(struct iwl_priv *priv, u8 id, u16 len, const void *data)
|
|
+{
|
|
+ struct iwl_host_cmd cmd = {
|
|
+ .id = id,
|
|
+ .len = len,
|
|
+ .data = data,
|
|
+ };
|
|
+
|
|
+ return iwl_send_cmd_sync(priv, &cmd);
|
|
+}
|
|
+
|
|
+static int __must_check iwl_send_cmd_u32(struct iwl_priv *priv, u8 id, u32 val)
|
|
+{
|
|
+ struct iwl_host_cmd cmd = {
|
|
+ .id = id,
|
|
+ .len = sizeof(val),
|
|
+ .data = &val,
|
|
+ };
|
|
+
|
|
+ return iwl_send_cmd_sync(priv, &cmd);
|
|
+}
|
|
+
|
|
+int iwl_send_statistics_request(struct iwl_priv *priv)
|
|
+{
|
|
+ return iwl_send_cmd_u32(priv, REPLY_STATISTICS_CMD, 0);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_rxon_add_station - add station into station table.
|
|
+ *
|
|
+ * there is only one AP station with id= IWL_AP_ID
|
|
+ * NOTE: mutex must be held before calling the this fnction
|
|
+*/
|
|
+static int iwl_rxon_add_station(struct iwl_priv *priv,
|
|
+ const u8 *addr, int is_ap)
|
|
+{
|
|
+ u8 rc;
|
|
+
|
|
+ /* Remove this station if it happens to already exist */
|
|
+ iwl_remove_station(priv, addr, is_ap);
|
|
+
|
|
+ rc = iwl_add_station(priv, addr, is_ap, 0);
|
|
+
|
|
+ iwl4965_add_station(priv, addr, is_ap);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_set_rxon_channel - Set the phymode and channel values in staging RXON
|
|
+ * @phymode: MODE_IEEE80211A sets to 5.2GHz; all else set to 2.4GHz
|
|
+ * @channel: Any channel valid for the requested phymode
|
|
+
|
|
+ * In addition to setting the staging RXON, priv->phymode is also set.
|
|
+ *
|
|
+ * NOTE: Does not commit to the hardware; it sets appropriate bit fields
|
|
+ * in the staging RXON flag structure based on the phymode
|
|
+ */
|
|
+static int iwl_set_rxon_channel(struct iwl_priv *priv, u8 phymode, u16 channel)
|
|
+{
|
|
+ if (!iwl_get_channel_info(priv, phymode, channel)) {
|
|
+ IWL_DEBUG_INFO("Could not set channel to %d [%d]\n",
|
|
+ channel, phymode);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if ((le16_to_cpu(priv->staging_rxon.channel) == channel) &&
|
|
+ (priv->phymode == phymode))
|
|
+ return 0;
|
|
+
|
|
+ priv->staging_rxon.channel = cpu_to_le16(channel);
|
|
+ if (phymode == MODE_IEEE80211A)
|
|
+ priv->staging_rxon.flags &= ~RXON_FLG_BAND_24G_MSK;
|
|
+ else
|
|
+ priv->staging_rxon.flags |= RXON_FLG_BAND_24G_MSK;
|
|
+
|
|
+ priv->phymode = phymode;
|
|
+
|
|
+ IWL_DEBUG_INFO("Staging channel set to %d [%d]\n", channel, phymode);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_check_rxon_cmd - validate RXON structure is valid
|
|
+ *
|
|
+ * NOTE: This is really only useful during development and can eventually
|
|
+ * be #ifdef'd out once the driver is stable and folks aren't actively
|
|
+ * making changes
|
|
+ */
|
|
+static int iwl_check_rxon_cmd(struct iwl_rxon_cmd *rxon)
|
|
+{
|
|
+ int error = 0;
|
|
+ int counter = 1;
|
|
+
|
|
+ if (rxon->flags & RXON_FLG_BAND_24G_MSK) {
|
|
+ error |= le32_to_cpu(rxon->flags &
|
|
+ (RXON_FLG_TGJ_NARROW_BAND_MSK |
|
|
+ RXON_FLG_RADAR_DETECT_MSK));
|
|
+ if (error)
|
|
+ IWL_WARNING("check 24G fields %d | %d\n",
|
|
+ counter++, error);
|
|
+ } else {
|
|
+ error |= (rxon->flags & RXON_FLG_SHORT_SLOT_MSK) ?
|
|
+ 0 : le32_to_cpu(RXON_FLG_SHORT_SLOT_MSK);
|
|
+ if (error)
|
|
+ IWL_WARNING("check 52 fields %d | %d\n",
|
|
+ counter++, error);
|
|
+ error |= le32_to_cpu(rxon->flags & RXON_FLG_CCK_MSK);
|
|
+ if (error)
|
|
+ IWL_WARNING("check 52 CCK %d | %d\n",
|
|
+ counter++, error);
|
|
+ }
|
|
+ error |= (rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1;
|
|
+ if (error)
|
|
+ IWL_WARNING("check mac addr %d | %d\n", counter++, error);
|
|
+
|
|
+ /* make sure basic rates 6Mbps and 1Mbps are supported */
|
|
+ error |= (((rxon->ofdm_basic_rates & IWL_RATE_6M_MASK) == 0) &&
|
|
+ ((rxon->cck_basic_rates & IWL_RATE_1M_MASK) == 0));
|
|
+ if (error)
|
|
+ IWL_WARNING("check basic rate %d | %d\n", counter++, error);
|
|
+
|
|
+ error |= (le16_to_cpu(rxon->assoc_id) > 2007);
|
|
+ if (error)
|
|
+ IWL_WARNING("check assoc id %d | %d\n", counter++, error);
|
|
+
|
|
+ error |= ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK))
|
|
+ == (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK));
|
|
+ if (error)
|
|
+ IWL_WARNING("check CCK and short slot %d | %d\n",
|
|
+ counter++, error);
|
|
+
|
|
+ error |= ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK))
|
|
+ == (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK));
|
|
+ if (error)
|
|
+ IWL_WARNING("check CCK & auto detect %d | %d\n",
|
|
+ counter++, error);
|
|
+
|
|
+ error |= ((rxon->flags & (RXON_FLG_AUTO_DETECT_MSK |
|
|
+ RXON_FLG_TGG_PROTECT_MSK)) == RXON_FLG_TGG_PROTECT_MSK);
|
|
+ if (error)
|
|
+ IWL_WARNING("check TGG and auto detect %d | %d\n",
|
|
+ counter++, error);
|
|
+
|
|
+#if IWL == 3945
|
|
+ if ((rxon->flags & RXON_FLG_DIS_DIV_MSK))
|
|
+ error |= ((rxon->flags & (RXON_FLG_ANT_B_MSK |
|
|
+ RXON_FLG_ANT_A_MSK)) == 0);
|
|
+ if (error)
|
|
+ IWL_WARNING("check antenna %d %d\n", counter++, error);
|
|
+#endif
|
|
+ if (error)
|
|
+ IWL_WARNING("Tuning to channel %d\n",
|
|
+ le16_to_cpu(rxon->channel));
|
|
+
|
|
+ if (error) {
|
|
+ IWL_ERROR("Not a valid iwl_rxon_assoc_cmd field values\n");
|
|
+ return -1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_full_rxon_required - determine if RXON_ASSOC can be used in RXON commit
|
|
+ * @priv: staging_rxon is comapred to active_rxon
|
|
+ *
|
|
+ * If the RXON structure is changing sufficient to require a new
|
|
+ * tune or to clear and reset the RXON_FILTER_ASSOC_MSK then return 1
|
|
+ * to indicate a new tune is required.
|
|
+ */
|
|
+static int iwl_full_rxon_required(struct iwl_priv *priv)
|
|
+{
|
|
+
|
|
+ /* These items are only settable from the full RXON command */
|
|
+ if (!(priv->active_rxon.filter_flags & RXON_FILTER_ASSOC_MSK) ||
|
|
+ compare_ether_addr(priv->staging_rxon.bssid_addr,
|
|
+ priv->active_rxon.bssid_addr) ||
|
|
+ compare_ether_addr(priv->staging_rxon.node_addr,
|
|
+ priv->active_rxon.node_addr) ||
|
|
+ compare_ether_addr(priv->staging_rxon.wlap_bssid_addr,
|
|
+ priv->active_rxon.wlap_bssid_addr) ||
|
|
+ (priv->staging_rxon.dev_type != priv->active_rxon.dev_type) ||
|
|
+ (priv->staging_rxon.channel != priv->active_rxon.channel) ||
|
|
+ (priv->staging_rxon.air_propagation !=
|
|
+ priv->active_rxon.air_propagation) ||
|
|
+#if IWL == 4965
|
|
+ (priv->staging_rxon.ofdm_ht_single_stream_basic_rates !=
|
|
+ priv->active_rxon.ofdm_ht_single_stream_basic_rates) ||
|
|
+ (priv->staging_rxon.ofdm_ht_dual_stream_basic_rates !=
|
|
+ priv->active_rxon.ofdm_ht_dual_stream_basic_rates) ||
|
|
+ (priv->staging_rxon.rx_chain != priv->active_rxon.rx_chain) ||
|
|
+#endif
|
|
+ (priv->staging_rxon.assoc_id != priv->active_rxon.assoc_id))
|
|
+ return 1;
|
|
+
|
|
+ /* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can
|
|
+ * be updated with the RXON_ASSOC command -- however only some
|
|
+ * flag transitions are allowed using RXON_ASSOC */
|
|
+
|
|
+ /* Check if we are not switching bands */
|
|
+ if ((priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK) !=
|
|
+ (priv->active_rxon.flags & RXON_FLG_BAND_24G_MSK))
|
|
+ return 1;
|
|
+
|
|
+ /* Check if we are switching association toggle */
|
|
+ if ((priv->staging_rxon.filter_flags & RXON_FILTER_ASSOC_MSK) !=
|
|
+ (priv->active_rxon.filter_flags & RXON_FILTER_ASSOC_MSK))
|
|
+ return 1;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int iwl_send_rxon_assoc(struct iwl_priv *priv)
|
|
+{
|
|
+ int rc = 0;
|
|
+ struct iwl_rx_packet *res = NULL;
|
|
+ struct iwl_rxon_assoc_cmd rxon_assoc;
|
|
+ struct iwl_host_cmd cmd = {
|
|
+ .id = REPLY_RXON_ASSOC,
|
|
+ .len = sizeof(rxon_assoc),
|
|
+ .meta.flags = CMD_WANT_SKB,
|
|
+ .data = &rxon_assoc,
|
|
+ };
|
|
+ const struct iwl_rxon_cmd *rxon1 = &priv->staging_rxon;
|
|
+ const struct iwl_rxon_cmd *rxon2 = &priv->active_rxon;
|
|
+
|
|
+ if ((rxon1->flags == rxon2->flags) &&
|
|
+ (rxon1->filter_flags == rxon2->filter_flags) &&
|
|
+ (rxon1->cck_basic_rates == rxon2->cck_basic_rates) &&
|
|
+#if IWL == 4965
|
|
+ (rxon1->ofdm_ht_single_stream_basic_rates ==
|
|
+ rxon2->ofdm_ht_single_stream_basic_rates) &&
|
|
+ (rxon1->ofdm_ht_dual_stream_basic_rates ==
|
|
+ rxon2->ofdm_ht_dual_stream_basic_rates) &&
|
|
+ (rxon1->rx_chain == rxon2->rx_chain) &&
|
|
+#endif
|
|
+ (rxon1->ofdm_basic_rates == rxon2->ofdm_basic_rates)) {
|
|
+ IWL_DEBUG_INFO("Using current RXON_ASSOC. Not resending.\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ rxon_assoc.flags = priv->staging_rxon.flags;
|
|
+ rxon_assoc.filter_flags = priv->staging_rxon.filter_flags;
|
|
+ rxon_assoc.ofdm_basic_rates = priv->staging_rxon.ofdm_basic_rates;
|
|
+ rxon_assoc.cck_basic_rates = priv->staging_rxon.cck_basic_rates;
|
|
+ rxon_assoc.reserved = 0;
|
|
+#if IWL == 4965
|
|
+ rxon_assoc.ofdm_ht_single_stream_basic_rates =
|
|
+ priv->staging_rxon.ofdm_ht_single_stream_basic_rates;
|
|
+ rxon_assoc.ofdm_ht_dual_stream_basic_rates =
|
|
+ priv->staging_rxon.ofdm_ht_dual_stream_basic_rates;
|
|
+ rxon_assoc.rx_chain_select_flags = priv->staging_rxon.rx_chain;
|
|
+#endif
|
|
+
|
|
+ rc = iwl_send_cmd_sync(priv, &cmd);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ res = (struct iwl_rx_packet *)cmd.meta.u.skb->data;
|
|
+ if (res->hdr.flags & IWL_CMD_FAILED_MSK) {
|
|
+ IWL_ERROR("Bad return from REPLY_RXON_ASSOC command\n");
|
|
+ rc = -EIO;
|
|
+ }
|
|
+
|
|
+ priv->alloc_rxb_skb--;
|
|
+ dev_kfree_skb_any(cmd.meta.u.skb);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_commit_rxon - commit staging_rxon to hardware
|
|
+ *
|
|
+ * The RXON command in staging_rxon is commited to the hardware and
|
|
+ * the active_rxon structure is updated with the new data. This
|
|
+ * function correctly transitions out of the RXON_ASSOC_MSK state if
|
|
+ * a HW tune is required based on the RXON structure changes.
|
|
+ */
|
|
+static int iwl_commit_rxon(struct iwl_priv *priv)
|
|
+{
|
|
+ /* cast away the const for active_rxon in this function */
|
|
+ struct iwl_rxon_cmd *active_rxon = (void *)&priv->active_rxon;
|
|
+ int rc = 0;
|
|
+
|
|
+ if (!iwl_is_alive(priv))
|
|
+ return -1;
|
|
+
|
|
+ /* always get timestamp with Rx frame */
|
|
+ priv->staging_rxon.flags |= RXON_FLG_TSF2HOST_MSK;
|
|
+
|
|
+#if IWL == 3945
|
|
+ /* select antenna */
|
|
+ priv->staging_rxon.flags &=
|
|
+ ~(RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_SEL_MSK);
|
|
+ priv->staging_rxon.flags |= iwl3945_get_antenna_flags(priv);
|
|
+#endif
|
|
+
|
|
+ rc = iwl_check_rxon_cmd(&priv->staging_rxon);
|
|
+ if (rc) {
|
|
+ IWL_ERROR("Invalid RXON configuration. Not committing.\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* If we don't need to send a full RXON, we can use
|
|
+ * iwl_rxon_assoc_cmd which is used to reconfigure filter
|
|
+ * and other flags for the current radio configuration. */
|
|
+ if (!iwl_full_rxon_required(priv)) {
|
|
+ rc = iwl_send_rxon_assoc(priv);
|
|
+ if (rc) {
|
|
+ IWL_ERROR("Error setting RXON_ASSOC "
|
|
+ "configuration (%d).\n", rc);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon));
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+#if IWL == 4965
|
|
+#ifdef CONFIG_IWLWIFI_SENSITIVITY
|
|
+ priv->sensitivity_data.state = IWL_SENS_CALIB_NEED_REINIT;
|
|
+ if (!priv->error_recovering)
|
|
+ priv->start_calib = 0;
|
|
+
|
|
+ iwl4965_init_sensitivity(priv, CMD_ASYNC, 1);
|
|
+#endif /* CONFIG_IWLWIFI_SENSITIVITY */
|
|
+#endif /* IWL == 4965 */
|
|
+
|
|
+ /* If we are currently associated and the new config requires
|
|
+ * an RXON_ASSOC and the new config wants the associated mask enabled,
|
|
+ * we must clear the associated from the active configuration
|
|
+ * before we apply the new config */
|
|
+ if (iwl_is_associated(priv) &&
|
|
+ (priv->staging_rxon.filter_flags & RXON_FILTER_ASSOC_MSK)) {
|
|
+ IWL_DEBUG_INFO("Toggling associated bit on current RXON\n");
|
|
+ active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
|
|
+
|
|
+ rc = iwl_send_cmd_pdu(priv, REPLY_RXON,
|
|
+ sizeof(struct iwl_rxon_cmd),
|
|
+ &priv->active_rxon);
|
|
+
|
|
+ /* If the mask clearing failed then we set
|
|
+ * active_rxon back to what it was previously */
|
|
+ if (rc) {
|
|
+ active_rxon->filter_flags |= RXON_FILTER_ASSOC_MSK;
|
|
+ IWL_ERROR("Error clearing ASSOC_MSK on current "
|
|
+ "configuration (%d).\n", rc);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ /* The RXON bit toggling will have cleared out the
|
|
+ * station table in the uCode, so blank it in the driver
|
|
+ * as well */
|
|
+ iwl_clear_stations_table(priv);
|
|
+ } else if (priv->staging_rxon.filter_flags & RXON_FILTER_ASSOC_MSK) {
|
|
+ /* When switching from non-associated to associated, the
|
|
+ * uCode clears out the station table; so clear it in the
|
|
+ * driver as well */
|
|
+ iwl_clear_stations_table(priv);
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_INFO("Sending RXON\n"
|
|
+ "* with%s RXON_FILTER_ASSOC_MSK\n"
|
|
+ "* channel = %d\n"
|
|
+ "* bssid = " MAC_FMT "\n",
|
|
+ ((priv->staging_rxon.filter_flags &
|
|
+ RXON_FILTER_ASSOC_MSK) ? "" : "out"),
|
|
+ le16_to_cpu(priv->staging_rxon.channel),
|
|
+ MAC_ARG(priv->staging_rxon.bssid_addr));
|
|
+
|
|
+ /* Apply the new configuration */
|
|
+ rc = iwl_send_cmd_pdu(priv, REPLY_RXON,
|
|
+ sizeof(struct iwl_rxon_cmd), &priv->staging_rxon);
|
|
+ if (rc) {
|
|
+ IWL_ERROR("Error setting new configuration (%d).\n", rc);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+#if IWL == 4965
|
|
+#ifdef CONFIG_IWLWIFI_SENSITIVITY
|
|
+ if (!priv->error_recovering)
|
|
+ priv->start_calib = 0;
|
|
+
|
|
+ priv->sensitivity_data.state = IWL_SENS_CALIB_NEED_REINIT;
|
|
+ iwl4965_init_sensitivity(priv, CMD_ASYNC, 1);
|
|
+#endif /* CONFIG_IWLWIFI_SENSITIVITY */
|
|
+#endif /* IWL == 4965 */
|
|
+
|
|
+ memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon));
|
|
+
|
|
+ /* If we issue a new RXON command which required a tune then we must
|
|
+ * send a new TXPOWER command or we won't be able to Tx any frames */
|
|
+ rc = iwl_hw_reg_send_txpower(priv);
|
|
+ if (rc) {
|
|
+ IWL_ERROR("Error setting Tx power (%d).\n", rc);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ /* Add the broadcast address so we can send broadcast frames */
|
|
+ if (iwl_rxon_add_station(priv, BROADCAST_ADDR, 0) ==
|
|
+ IWL_INVALID_STATION) {
|
|
+ IWL_ERROR("Error adding BROADCAST address for transmit.\n");
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ /* If we have set the ASSOC_MSK and we are in BSS mode then
|
|
+ * add the IWL_AP_ID to the station rate table */
|
|
+ if (iwl_is_associated(priv) &&
|
|
+ (priv->iw_mode == IEEE80211_IF_TYPE_STA)) {
|
|
+ if (iwl_rxon_add_station(priv, priv->active_rxon.bssid_addr, 1)
|
|
+ == IWL_INVALID_STATION) {
|
|
+ IWL_ERROR("Error adding AP address for transmit.\n");
|
|
+ return -EIO;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Init the hardware's rate fallback order based on the
|
|
+ * phymode */
|
|
+ rc = iwl3945_init_hw_rate_table(priv);
|
|
+ if (rc) {
|
|
+ IWL_ERROR("Error setting HW rate table: %02X\n", rc);
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int iwl_send_bt_config(struct iwl_priv *priv)
|
|
+{
|
|
+ struct iwl_bt_cmd bt_cmd = {
|
|
+ .flags = 3,
|
|
+ .lead_time = 0xAA,
|
|
+ .max_kill = 1,
|
|
+ .kill_ack_mask = 0,
|
|
+ .kill_cts_mask = 0,
|
|
+ };
|
|
+
|
|
+ return iwl_send_cmd_pdu(priv, REPLY_BT_CONFIG,
|
|
+ sizeof(struct iwl_bt_cmd), &bt_cmd);
|
|
+}
|
|
+
|
|
+static int iwl_send_scan_abort(struct iwl_priv *priv)
|
|
+{
|
|
+ int rc = 0;
|
|
+ struct iwl_rx_packet *res;
|
|
+ struct iwl_host_cmd cmd = {
|
|
+ .id = REPLY_SCAN_ABORT_CMD,
|
|
+ .meta.flags = CMD_WANT_SKB,
|
|
+ };
|
|
+
|
|
+ /* If there isn't a scan actively going on in the hardware
|
|
+ * then we are in between scan bands and not actually
|
|
+ * actively scanning, so don't send the abort command */
|
|
+ if (!test_bit(STATUS_SCAN_HW, &priv->status)) {
|
|
+ clear_bit(STATUS_SCAN_ABORTING, &priv->status);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ rc = iwl_send_cmd_sync(priv, &cmd);
|
|
+ if (rc) {
|
|
+ clear_bit(STATUS_SCAN_ABORTING, &priv->status);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ res = (struct iwl_rx_packet *)cmd.meta.u.skb->data;
|
|
+ if (res->u.status != CAN_ABORT_STATUS) {
|
|
+ /* The scan abort will return 1 for success or
|
|
+ * 2 for "failure". A failure condition can be
|
|
+ * due to simply not being in an active scan which
|
|
+ * can occur if we send the scan abort before we
|
|
+ * the microcode has notified us that a scan is
|
|
+ * completed. */
|
|
+ IWL_DEBUG_INFO("SCAN_ABORT returned %d.\n", res->u.status);
|
|
+ clear_bit(STATUS_SCAN_ABORTING, &priv->status);
|
|
+ clear_bit(STATUS_SCAN_HW, &priv->status);
|
|
+ }
|
|
+
|
|
+ dev_kfree_skb_any(cmd.meta.u.skb);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int iwl_card_state_sync_callback(struct iwl_priv *priv,
|
|
+ struct iwl_cmd *cmd,
|
|
+ struct sk_buff *skb)
|
|
+{
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * CARD_STATE_CMD
|
|
+ *
|
|
+ * Use: Sets the internal card state to enable, disable, or halt
|
|
+ *
|
|
+ * When in the 'enable' state the card operates as normal.
|
|
+ * When in the 'disable' state, the card enters into a low power mode.
|
|
+ * When in the 'halt' state, the card is shut down and must be fully
|
|
+ * restarted to come back on.
|
|
+ */
|
|
+static int iwl_send_card_state(struct iwl_priv *priv, u32 flags, u8 meta_flag)
|
|
+{
|
|
+ struct iwl_host_cmd cmd = {
|
|
+ .id = REPLY_CARD_STATE_CMD,
|
|
+ .len = sizeof(u32),
|
|
+ .data = &flags,
|
|
+ .meta.flags = meta_flag,
|
|
+ };
|
|
+
|
|
+ if (meta_flag & CMD_ASYNC)
|
|
+ cmd.meta.u.callback = iwl_card_state_sync_callback;
|
|
+
|
|
+ return iwl_send_cmd(priv, &cmd);
|
|
+}
|
|
+
|
|
+static int iwl_add_sta_sync_callback(struct iwl_priv *priv,
|
|
+ struct iwl_cmd *cmd, struct sk_buff *skb)
|
|
+{
|
|
+ struct iwl_rx_packet *res = NULL;
|
|
+
|
|
+ if (!skb) {
|
|
+ IWL_ERROR("Error: Response NULL in REPLY_ADD_STA.\n");
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ res = (struct iwl_rx_packet *)skb->data;
|
|
+ if (res->hdr.flags & IWL_CMD_FAILED_MSK) {
|
|
+ IWL_ERROR("Bad return from REPLY_ADD_STA (0x%08X)\n",
|
|
+ res->hdr.flags);
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ switch (res->u.add_sta.status) {
|
|
+ case ADD_STA_SUCCESS_MSK:
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* We didn't cache the SKB; let the caller free it */
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+int iwl_send_add_station(struct iwl_priv *priv,
|
|
+ struct iwl_addsta_cmd *sta, u8 flags)
|
|
+{
|
|
+ struct iwl_rx_packet *res = NULL;
|
|
+ int rc = 0;
|
|
+ struct iwl_host_cmd cmd = {
|
|
+ .id = REPLY_ADD_STA,
|
|
+ .len = sizeof(struct iwl_addsta_cmd),
|
|
+ .meta.flags = flags,
|
|
+ .data = sta,
|
|
+ };
|
|
+
|
|
+ if (flags & CMD_ASYNC)
|
|
+ cmd.meta.u.callback = iwl_add_sta_sync_callback;
|
|
+ else
|
|
+ cmd.meta.flags |= CMD_WANT_SKB;
|
|
+
|
|
+ rc = iwl_send_cmd(priv, &cmd);
|
|
+
|
|
+ if (rc || (flags & CMD_ASYNC))
|
|
+ return rc;
|
|
+
|
|
+ res = (struct iwl_rx_packet *)cmd.meta.u.skb->data;
|
|
+ if (res->hdr.flags & IWL_CMD_FAILED_MSK) {
|
|
+ IWL_ERROR("Bad return from REPLY_ADD_STA (0x%08X)\n",
|
|
+ res->hdr.flags);
|
|
+ rc = -EIO;
|
|
+ }
|
|
+
|
|
+ if (rc == 0) {
|
|
+ switch (res->u.add_sta.status) {
|
|
+ case ADD_STA_SUCCESS_MSK:
|
|
+ IWL_DEBUG_INFO("REPLY_ADD_STA PASSED\n");
|
|
+ break;
|
|
+ default:
|
|
+ rc = -EIO;
|
|
+ IWL_WARNING("REPLY_ADD_STA failed\n");
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ priv->alloc_rxb_skb--;
|
|
+ dev_kfree_skb_any(cmd.meta.u.skb);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int iwl_update_sta_key_info(struct iwl_priv *priv,
|
|
+ struct ieee80211_key_conf *keyconf,
|
|
+ u8 sta_id)
|
|
+{
|
|
+ unsigned long flags;
|
|
+ __le16 key_flags = 0;
|
|
+
|
|
+ switch (keyconf->alg) {
|
|
+ case ALG_CCMP:
|
|
+ key_flags |= STA_KEY_FLG_CCMP;
|
|
+ key_flags |= cpu_to_le16(
|
|
+ keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
|
|
+ key_flags &= ~STA_KEY_FLG_INVALID;
|
|
+ break;
|
|
+ case ALG_TKIP:
|
|
+ case ALG_WEP:
|
|
+ return -EINVAL;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ spin_lock_irqsave(&priv->sta_lock, flags);
|
|
+ priv->stations[sta_id].keyinfo.alg = keyconf->alg;
|
|
+ priv->stations[sta_id].keyinfo.keylen = keyconf->keylen;
|
|
+ memcpy(priv->stations[sta_id].keyinfo.key, keyconf->key,
|
|
+ keyconf->keylen);
|
|
+
|
|
+ memcpy(priv->stations[sta_id].sta.key.key, keyconf->key,
|
|
+ keyconf->keylen);
|
|
+ priv->stations[sta_id].sta.key.key_flags = key_flags;
|
|
+ priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
|
|
+ priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->sta_lock, flags);
|
|
+
|
|
+ IWL_DEBUG_INFO("hwcrypto: modify ucode station key info\n");
|
|
+ iwl_send_add_station(priv, &priv->stations[sta_id].sta, 0);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void iwl_clear_free_frames(struct iwl_priv *priv)
|
|
+{
|
|
+ struct list_head *element;
|
|
+
|
|
+ IWL_DEBUG_INFO("%d frames on pre-allocated heap on clear.\n",
|
|
+ priv->frames_count);
|
|
+
|
|
+ while (!list_empty(&priv->free_frames)) {
|
|
+ element = priv->free_frames.next;
|
|
+ list_del(element);
|
|
+ kfree(list_entry(element, struct iwl_frame, list));
|
|
+ priv->frames_count--;
|
|
+ }
|
|
+
|
|
+ if (priv->frames_count) {
|
|
+ IWL_WARNING("%d frames still in use. Did we lose one?\n",
|
|
+ priv->frames_count);
|
|
+ priv->frames_count = 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+static struct iwl_frame *iwl_get_free_frame(struct iwl_priv *priv)
|
|
+{
|
|
+ struct iwl_frame *frame;
|
|
+ struct list_head *element;
|
|
+ if (list_empty(&priv->free_frames)) {
|
|
+ frame = kzalloc(sizeof(*frame), GFP_KERNEL);
|
|
+ if (!frame) {
|
|
+ IWL_ERROR("Could not allocate frame!\n");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ priv->frames_count++;
|
|
+ return frame;
|
|
+ }
|
|
+
|
|
+ element = priv->free_frames.next;
|
|
+ list_del(element);
|
|
+ return list_entry(element, struct iwl_frame, list);
|
|
+}
|
|
+
|
|
+static void iwl_free_frame(struct iwl_priv *priv, struct iwl_frame *frame)
|
|
+{
|
|
+ memset(frame, 0, sizeof(*frame));
|
|
+ list_add(&frame->list, &priv->free_frames);
|
|
+}
|
|
+
|
|
+unsigned int iwl_fill_beacon_frame(struct iwl_priv *priv,
|
|
+ struct ieee80211_hdr *hdr,
|
|
+ const u8 *dest, int left)
|
|
+{
|
|
+
|
|
+ if (!iwl_is_associated(priv) || !priv->ibss_beacon ||
|
|
+ ((priv->iw_mode != IEEE80211_IF_TYPE_IBSS) &&
|
|
+ (priv->iw_mode != IEEE80211_IF_TYPE_AP)))
|
|
+ return 0;
|
|
+
|
|
+ if (priv->ibss_beacon->len > left)
|
|
+ return 0;
|
|
+
|
|
+ memcpy(hdr, priv->ibss_beacon->data, priv->ibss_beacon->len);
|
|
+
|
|
+ return priv->ibss_beacon->len;
|
|
+}
|
|
+
|
|
+static int iwl_send_beacon_cmd(struct iwl_priv *priv)
|
|
+{
|
|
+ struct iwl_frame *frame;
|
|
+ unsigned int frame_size;
|
|
+ int rc;
|
|
+ u8 rate;
|
|
+
|
|
+ frame = iwl_get_free_frame(priv);
|
|
+
|
|
+ if (!frame) {
|
|
+ IWL_ERROR("Could not obtain free frame buffer for beacon "
|
|
+ "command.\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ if (!(priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK)) {
|
|
+ rate = iwl_rate_get_lowest_plcp(priv->active_rate_basic &
|
|
+ 0xFF0);
|
|
+ if (rate == IWL_INVALID_RATE)
|
|
+ rate = IWL_RATE_6M_PLCP;
|
|
+ } else {
|
|
+ rate = iwl_rate_get_lowest_plcp(priv->active_rate_basic & 0xF);
|
|
+ if (rate == IWL_INVALID_RATE)
|
|
+ rate = IWL_RATE_1M_PLCP;
|
|
+ }
|
|
+
|
|
+ frame_size = iwl_hw_get_beacon_cmd(priv, frame, rate);
|
|
+
|
|
+ rc = iwl_send_cmd_pdu(priv, REPLY_TX_BEACON, frame_size,
|
|
+ &frame->u.cmd[0]);
|
|
+
|
|
+ iwl_free_frame(priv, frame);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * EEPROM related functions
|
|
+ *
|
|
+ ******************************************************************************/
|
|
+
|
|
+static void get_eeprom_mac(struct iwl_priv *priv, u8 *mac)
|
|
+{
|
|
+ memcpy(mac, priv->eeprom.mac_address, 6);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_eeprom_init - read EEPROM contents
|
|
+ *
|
|
+ * Load the EEPROM from adapter into priv->eeprom
|
|
+ *
|
|
+ * NOTE: This routine uses the non-debug IO access functions.
|
|
+ */
|
|
+int iwl_eeprom_init(struct iwl_priv *priv)
|
|
+{
|
|
+ u16 *e = (u16 *)&priv->eeprom;
|
|
+ u32 gp = iwl_read32(priv, CSR_EEPROM_GP);
|
|
+ u32 r;
|
|
+ int sz = sizeof(priv->eeprom);
|
|
+ int rc;
|
|
+ int i;
|
|
+ u16 addr;
|
|
+
|
|
+ /* The EEPROM structure has several padding buffers within it
|
|
+ * and when adding new EEPROM maps is subject to programmer errors
|
|
+ * which may be very difficult to identify without explicitly
|
|
+ * checking the resulting size of the eeprom map. */
|
|
+ BUILD_BUG_ON(sizeof(priv->eeprom) != IWL_EEPROM_IMAGE_SIZE);
|
|
+
|
|
+ if ((gp & CSR_EEPROM_GP_VALID_MSK) == CSR_EEPROM_GP_BAD_SIGNATURE) {
|
|
+ IWL_ERROR("EEPROM not found, EEPROM_GP=0x%08x", gp);
|
|
+ return -ENOENT;
|
|
+ }
|
|
+
|
|
+ rc = iwl_eeprom_aqcuire_semaphore(priv);
|
|
+ if (rc < 0) {
|
|
+ IWL_ERROR("Failed to aqcuire EEPROM semaphore.\n");
|
|
+ return -ENOENT;
|
|
+ }
|
|
+
|
|
+ /* eeprom is an array of 16bit values */
|
|
+ for (addr = 0; addr < sz; addr += sizeof(u16)) {
|
|
+ _iwl_write32(priv, CSR_EEPROM_REG, addr << 1);
|
|
+ _iwl_clear_bit(priv, CSR_EEPROM_REG, CSR_EEPROM_REG_BIT_CMD);
|
|
+
|
|
+ for (i = 0; i < IWL_EEPROM_ACCESS_TIMEOUT;
|
|
+ i += IWL_EEPROM_ACCESS_DELAY) {
|
|
+ r = _iwl_read_restricted(priv, CSR_EEPROM_REG);
|
|
+ if (r & CSR_EEPROM_REG_READ_VALID_MSK)
|
|
+ break;
|
|
+ udelay(IWL_EEPROM_ACCESS_DELAY);
|
|
+ }
|
|
+
|
|
+ if (!(r & CSR_EEPROM_REG_READ_VALID_MSK)) {
|
|
+ IWL_ERROR("Time out reading EEPROM[%d]", addr);
|
|
+ rc = -ETIMEDOUT;
|
|
+ goto done;
|
|
+ }
|
|
+ e[addr / 2] = le16_to_cpu(r >> 16);
|
|
+ }
|
|
+ rc = 0;
|
|
+
|
|
+done:
|
|
+ iwl_eeprom_release_semaphore(priv);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * Misc. internal state and helper functions
|
|
+ *
|
|
+ ******************************************************************************/
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+
|
|
+/**
|
|
+ * iwl_report_frame - dump frame to syslog during debug sessions
|
|
+ *
|
|
+ * hack this function to show different aspects of received frames,
|
|
+ * including selective frame dumps.
|
|
+ * group100 parameter selects whether to show 1 out of 100 good frames.
|
|
+ *
|
|
+ * TODO: ieee80211_hdr stuff is common to 3945 and 4965, so frame type
|
|
+ * info output is okay, but some of this stuff (e.g. iwl_rx_frame_stats)
|
|
+ * is 3945-specific and gives bad output for 4965. Need to split the
|
|
+ * functionality, keep common stuff here.
|
|
+ */
|
|
+void iwl_report_frame(struct iwl_priv *priv,
|
|
+ struct iwl_rx_packet *pkt,
|
|
+ struct ieee80211_hdr *header, int group100)
|
|
+{
|
|
+ u32 to_us;
|
|
+ u32 print_summary = 0;
|
|
+ u32 print_dump = 0; /* set to 1 to dump all frames' contents */
|
|
+ u32 hundred = 0;
|
|
+ u32 dataframe = 0;
|
|
+ u16 fc;
|
|
+ u16 seq_ctl;
|
|
+ u16 channel;
|
|
+ u16 phy_flags;
|
|
+ int rate_sym;
|
|
+ u16 length;
|
|
+ u16 status;
|
|
+ u16 bcn_tmr;
|
|
+ u32 tsf_low;
|
|
+ u64 tsf;
|
|
+ u8 rssi;
|
|
+ u8 agc;
|
|
+ u16 sig_avg;
|
|
+ u16 noise_diff;
|
|
+ struct iwl_rx_frame_stats *rx_stats = IWL_RX_STATS(pkt);
|
|
+ struct iwl_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt);
|
|
+ struct iwl_rx_frame_end *rx_end = IWL_RX_END(pkt);
|
|
+ u8 *data = IWL_RX_DATA(pkt);
|
|
+
|
|
+ /* MAC header */
|
|
+ fc = le16_to_cpu(header->frame_control);
|
|
+ seq_ctl = le16_to_cpu(header->seq_ctrl);
|
|
+
|
|
+ /* metadata */
|
|
+ channel = le16_to_cpu(rx_hdr->channel);
|
|
+ phy_flags = le16_to_cpu(rx_hdr->phy_flags);
|
|
+ rate_sym = rx_hdr->rate;
|
|
+ length = le16_to_cpu(rx_hdr->len);
|
|
+
|
|
+ /* end-of-frame status and timestamp */
|
|
+ status = le32_to_cpu(rx_end->status);
|
|
+ bcn_tmr = le32_to_cpu(rx_end->beacon_timestamp);
|
|
+ tsf_low = le64_to_cpu(rx_end->timestamp) & 0x0ffffffff;
|
|
+ tsf = le64_to_cpu(rx_end->timestamp);
|
|
+
|
|
+ /* signal statistics */
|
|
+ rssi = rx_stats->rssi;
|
|
+ agc = rx_stats->agc;
|
|
+ sig_avg = le16_to_cpu(rx_stats->sig_avg);
|
|
+ noise_diff = le16_to_cpu(rx_stats->noise_diff);
|
|
+
|
|
+ to_us = !compare_ether_addr(header->addr1, priv->mac_addr);
|
|
+
|
|
+ /* if data frame is to us and all is good,
|
|
+ * (optionally) print summary for only 1 out of every 100 */
|
|
+ if (to_us && (fc & ~IEEE80211_FCTL_PROTECTED) ==
|
|
+ (IEEE80211_FCTL_FROMDS | IEEE80211_FTYPE_DATA)) {
|
|
+ dataframe = 1;
|
|
+ if (!group100)
|
|
+ print_summary = 1; /* print each frame */
|
|
+ else if (priv->framecnt_to_us < 100) {
|
|
+ priv->framecnt_to_us++;
|
|
+ print_summary = 0;
|
|
+ } else {
|
|
+ priv->framecnt_to_us = 0;
|
|
+ print_summary = 1;
|
|
+ hundred = 1;
|
|
+ }
|
|
+ } else {
|
|
+ /* print summary for all other frames */
|
|
+ print_summary = 1;
|
|
+ }
|
|
+
|
|
+ if (print_summary) {
|
|
+ char *title;
|
|
+ u32 rate;
|
|
+
|
|
+ if (hundred)
|
|
+ title = "100Frames";
|
|
+ else if (fc & IEEE80211_FCTL_RETRY)
|
|
+ title = "Retry";
|
|
+ else if (ieee80211_is_assoc_response(fc))
|
|
+ title = "AscRsp";
|
|
+ else if (ieee80211_is_reassoc_response(fc))
|
|
+ title = "RasRsp";
|
|
+ else if (ieee80211_is_probe_response(fc)) {
|
|
+ title = "PrbRsp";
|
|
+ print_dump = 1; /* dump frame contents */
|
|
+ } else if (ieee80211_is_beacon(fc)) {
|
|
+ title = "Beacon";
|
|
+ print_dump = 1; /* dump frame contents */
|
|
+ } else if (ieee80211_is_atim(fc))
|
|
+ title = "ATIM";
|
|
+ else if (ieee80211_is_auth(fc))
|
|
+ title = "Auth";
|
|
+ else if (ieee80211_is_deauth(fc))
|
|
+ title = "DeAuth";
|
|
+ else if (ieee80211_is_disassoc(fc))
|
|
+ title = "DisAssoc";
|
|
+ else
|
|
+ title = "Frame";
|
|
+
|
|
+ rate = iwl_rate_index_from_plcp(rate_sym);
|
|
+ if (rate == -1)
|
|
+ rate = 0;
|
|
+ else
|
|
+ rate = iwl_rates[rate].ieee / 2;
|
|
+
|
|
+ /* print frame summary.
|
|
+ * MAC addresses show just the last byte (for brevity),
|
|
+ * but you can hack it to show more, if you'd like to. */
|
|
+ if (dataframe)
|
|
+ IWL_DEBUG_RX("%s: mhd=0x%04x, dst=0x%02x, "
|
|
+ "len=%u, rssi=%d, chnl=%d, rate=%u, \n",
|
|
+ title, fc, header->addr1[5],
|
|
+ length, rssi, channel, rate);
|
|
+ else {
|
|
+ /* src/dst addresses assume managed mode */
|
|
+ IWL_DEBUG_RX("%s: 0x%04x, dst=0x%02x, "
|
|
+ "src=0x%02x, rssi=%u, tim=%lu usec, "
|
|
+ "phy=0x%02x, chnl=%d\n",
|
|
+ title, fc, header->addr1[5],
|
|
+ header->addr3[5], rssi,
|
|
+ tsf_low - priv->scan_start_tsf,
|
|
+ phy_flags, channel);
|
|
+ }
|
|
+ }
|
|
+ if (print_dump)
|
|
+ iwl_print_hex_dump(IWL_DL_RX, data, length);
|
|
+}
|
|
+#endif
|
|
+
|
|
+static void iwl_unset_hw_setting(struct iwl_priv *priv)
|
|
+{
|
|
+ if (priv->hw_setting.shared_virt)
|
|
+ pci_free_consistent(priv->pci_dev,
|
|
+ sizeof(struct iwl_shared),
|
|
+ priv->hw_setting.shared_virt,
|
|
+ priv->hw_setting.shared_phys);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_supported_rate_to_ie - fill in the supported rate in IE field
|
|
+ *
|
|
+ * return : set the bit for each supported rate insert in ie
|
|
+ */
|
|
+static u16 iwl_supported_rate_to_ie(u8 *ie, u16 supported_rate,
|
|
+ u16 basic_rate, int max_count)
|
|
+{
|
|
+ u16 ret_rates = 0, bit;
|
|
+ int i;
|
|
+ u8 *rates;
|
|
+
|
|
+ rates = &(ie[1]);
|
|
+
|
|
+ for (bit = 1, i = 0; i < IWL_RATE_COUNT; i++, bit <<= 1) {
|
|
+ if (bit & supported_rate) {
|
|
+ ret_rates |= bit;
|
|
+ rates[*ie] = iwl_rates[i].ieee |
|
|
+ ((bit & basic_rate) ? 0x80 : 0x00);
|
|
+ *ie = *ie + 1;
|
|
+ if (*ie >= max_count)
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return ret_rates;
|
|
+}
|
|
+
|
|
+#if IWL == 4965
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+void static iwl_set_ht_capab(struct ieee80211_hw *hw,
|
|
+ struct ieee80211_ht_capability *ht_cap,
|
|
+ u8 use_wide_chan);
|
|
+#endif
|
|
+#endif
|
|
+
|
|
+/**
|
|
+ * iwl_fill_probe_req - fill in all required fields and IE for probe request
|
|
+ */
|
|
+static u16 iwl_fill_probe_req(struct iwl_priv *priv,
|
|
+ struct ieee80211_mgmt *frame,
|
|
+ int left, int is_direct)
|
|
+{
|
|
+ int len = 0;
|
|
+ u8 *pos = NULL;
|
|
+ u16 ret_rates;
|
|
+
|
|
+ /* Make sure there is enough space for the probe request,
|
|
+ * two mandatory IEs and the data */
|
|
+ left -= 24;
|
|
+ if (left < 0)
|
|
+ return 0;
|
|
+ len += 24;
|
|
+
|
|
+ frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
|
|
+ memcpy(frame->da, BROADCAST_ADDR, ETH_ALEN);
|
|
+ memcpy(frame->sa, priv->mac_addr, ETH_ALEN);
|
|
+ memcpy(frame->bssid, BROADCAST_ADDR, ETH_ALEN);
|
|
+ frame->seq_ctrl = 0;
|
|
+
|
|
+ /* fill in our indirect SSID IE */
|
|
+ /* ...next IE... */
|
|
+
|
|
+ left -= 2;
|
|
+ if (left < 0)
|
|
+ return 0;
|
|
+ len += 2;
|
|
+ pos = &(frame->u.probe_req.variable[0]);
|
|
+ *pos++ = WLAN_EID_SSID;
|
|
+ *pos++ = 0;
|
|
+
|
|
+ /* fill in our direct SSID IE... */
|
|
+ if (is_direct) {
|
|
+ /* ...next IE... */
|
|
+ left -= 2 + priv->essid_len;
|
|
+ if (left < 0)
|
|
+ return 0;
|
|
+ /* ... fill it in... */
|
|
+ *pos++ = WLAN_EID_SSID;
|
|
+ *pos++ = priv->essid_len;
|
|
+ memcpy(pos, priv->essid, priv->essid_len);
|
|
+ pos += priv->essid_len;
|
|
+ len += 2 + priv->essid_len;
|
|
+ }
|
|
+
|
|
+ /* fill in supported rate */
|
|
+ /* ...next IE... */
|
|
+ left -= 2;
|
|
+ if (left < 0)
|
|
+ return 0;
|
|
+ /* ... fill it in... */
|
|
+ *pos++ = WLAN_EID_SUPP_RATES;
|
|
+ *pos = 0;
|
|
+ ret_rates = priv->active_rate = priv->rates_mask;
|
|
+ priv->active_rate_basic = priv->rates_mask & IWL_BASIC_RATES_MASK;
|
|
+
|
|
+ iwl_supported_rate_to_ie(pos, priv->active_rate,
|
|
+ priv->active_rate_basic, left);
|
|
+ len += 2 + *pos;
|
|
+ pos += (*pos) + 1;
|
|
+ ret_rates = ~ret_rates & priv->active_rate;
|
|
+
|
|
+ if (ret_rates == 0)
|
|
+ goto fill_end;
|
|
+
|
|
+ /* fill in supported extended rate */
|
|
+ /* ...next IE... */
|
|
+ left -= 2;
|
|
+ if (left < 0)
|
|
+ return 0;
|
|
+ /* ... fill it in... */
|
|
+ *pos++ = WLAN_EID_EXT_SUPP_RATES;
|
|
+ *pos = 0;
|
|
+ iwl_supported_rate_to_ie(pos, ret_rates, priv->active_rate_basic, left);
|
|
+ if (*pos > 0)
|
|
+ len += 2 + *pos;
|
|
+#if IWL == 4965
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+ if (is_direct && priv->is_ht_enabled) {
|
|
+ u8 use_wide_chan = 1;
|
|
+
|
|
+ if (priv->channel_width != IWL_CHANNEL_WIDTH_40MHZ)
|
|
+ use_wide_chan = 0;
|
|
+ pos += (*pos) + 1;
|
|
+ *pos++ = WLAN_EID_HT_CAPABILITY;
|
|
+ *pos++ = sizeof(struct ieee80211_ht_capability);
|
|
+ iwl_set_ht_capab(NULL, (struct ieee80211_ht_capability *)pos,
|
|
+ use_wide_chan);
|
|
+ len += 2 + sizeof(struct ieee80211_ht_capability);
|
|
+ }
|
|
+#endif /*CONFIG_IWLWIFI_HT */
|
|
+#endif
|
|
+
|
|
+ fill_end:
|
|
+ return (u16)len;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * QoS support
|
|
+*/
|
|
+#ifdef CONFIG_IWLWIFI_QOS
|
|
+static int iwl_send_qos_params_command(struct iwl_priv *priv,
|
|
+ struct iwl_qosparam_cmd *qos)
|
|
+{
|
|
+
|
|
+ return iwl_send_cmd_pdu(priv, REPLY_QOS_PARAM,
|
|
+ sizeof(struct iwl_qosparam_cmd), qos);
|
|
+}
|
|
+
|
|
+static void iwl_reset_qos(struct iwl_priv *priv)
|
|
+{
|
|
+ u16 cw_min = 15;
|
|
+ u16 cw_max = 1023;
|
|
+ u8 aifs = 2;
|
|
+ u8 is_legacy = 0;
|
|
+ unsigned long flags;
|
|
+ int i;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ priv->qos_data.qos_active = 0;
|
|
+
|
|
+ if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS) {
|
|
+ if (priv->qos_data.qos_enable)
|
|
+ priv->qos_data.qos_active = IPW_QOS_WMM;
|
|
+ if (!(priv->active_rate & 0xfff0)) {
|
|
+ cw_min = 31;
|
|
+ is_legacy = 1;
|
|
+ }
|
|
+ } else {
|
|
+ if (!(priv->staging_rxon.flags & RXON_FLG_SHORT_SLOT_MSK)) {
|
|
+ cw_min = 31;
|
|
+ is_legacy = 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (priv->qos_data.qos_active)
|
|
+ aifs = 3;
|
|
+
|
|
+ priv->qos_data.def_qos_parm.ac[0].cw_min = cpu_to_le16(cw_min);
|
|
+ priv->qos_data.def_qos_parm.ac[0].cw_max = cpu_to_le16(cw_max);
|
|
+ priv->qos_data.def_qos_parm.ac[0].aifsn = aifs;
|
|
+ priv->qos_data.def_qos_parm.ac[0].edca_txop = 0;
|
|
+ priv->qos_data.def_qos_parm.ac[0].reserved1 = 0;
|
|
+
|
|
+ if (priv->qos_data.qos_active) {
|
|
+ i = 1;
|
|
+ priv->qos_data.def_qos_parm.ac[i].cw_min = cpu_to_le16(cw_min);
|
|
+ priv->qos_data.def_qos_parm.ac[i].cw_max = cpu_to_le16(cw_max);
|
|
+ priv->qos_data.def_qos_parm.ac[i].aifsn = 7;
|
|
+ priv->qos_data.def_qos_parm.ac[i].edca_txop = 0;
|
|
+ priv->qos_data.def_qos_parm.ac[i].reserved1 = 0;
|
|
+
|
|
+ i = 2;
|
|
+ priv->qos_data.def_qos_parm.ac[i].cw_min =
|
|
+ cpu_to_le16((cw_min + 1) / 2 - 1);
|
|
+ priv->qos_data.def_qos_parm.ac[i].cw_max =
|
|
+ cpu_to_le16(cw_max);
|
|
+ priv->qos_data.def_qos_parm.ac[i].aifsn = 2;
|
|
+ if (is_legacy)
|
|
+ priv->qos_data.def_qos_parm.ac[i].edca_txop =
|
|
+ cpu_to_le16(6016);
|
|
+ else
|
|
+ priv->qos_data.def_qos_parm.ac[i].edca_txop =
|
|
+ cpu_to_le16(3008);
|
|
+ priv->qos_data.def_qos_parm.ac[i].reserved1 = 0;
|
|
+
|
|
+ i = 3;
|
|
+ priv->qos_data.def_qos_parm.ac[i].cw_min =
|
|
+ cpu_to_le16((cw_min + 1) / 4 - 1);
|
|
+ priv->qos_data.def_qos_parm.ac[i].cw_max =
|
|
+ cpu_to_le16((cw_max + 1) / 2 - 1);
|
|
+ priv->qos_data.def_qos_parm.ac[i].aifsn = 2;
|
|
+ priv->qos_data.def_qos_parm.ac[i].reserved1 = 0;
|
|
+ if (is_legacy)
|
|
+ priv->qos_data.def_qos_parm.ac[i].edca_txop =
|
|
+ cpu_to_le16(3264);
|
|
+ else
|
|
+ priv->qos_data.def_qos_parm.ac[i].edca_txop =
|
|
+ cpu_to_le16(1504);
|
|
+ } else {
|
|
+ for (i = 1; i < 4; i++) {
|
|
+ priv->qos_data.def_qos_parm.ac[i].cw_min =
|
|
+ cpu_to_le16(cw_min);
|
|
+ priv->qos_data.def_qos_parm.ac[i].cw_max =
|
|
+ cpu_to_le16(cw_max);
|
|
+ priv->qos_data.def_qos_parm.ac[i].aifsn = aifs;
|
|
+ priv->qos_data.def_qos_parm.ac[i].edca_txop = 0;
|
|
+ priv->qos_data.def_qos_parm.ac[i].reserved1 = 0;
|
|
+ }
|
|
+ }
|
|
+ IWL_DEBUG_QOS("set QoS to default \n");
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+}
|
|
+
|
|
+static void iwl_activate_qos(struct iwl_priv *priv, u8 force)
|
|
+{
|
|
+ unsigned long flags;
|
|
+
|
|
+ if (priv == NULL)
|
|
+ return;
|
|
+
|
|
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
|
+ return;
|
|
+
|
|
+ if (!priv->qos_data.qos_enable)
|
|
+ return;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ priv->qos_data.def_qos_parm.qos_flags = 0;
|
|
+
|
|
+ if (priv->qos_data.qos_cap.q_AP.queue_request &&
|
|
+ !priv->qos_data.qos_cap.q_AP.txop_request)
|
|
+ priv->qos_data.def_qos_parm.qos_flags |=
|
|
+ QOS_PARAM_FLG_TXOP_TYPE_MSK;
|
|
+
|
|
+ if (priv->qos_data.qos_active)
|
|
+ priv->qos_data.def_qos_parm.qos_flags |=
|
|
+ QOS_PARAM_FLG_UPDATE_EDCA_MSK;
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ if (force || iwl_is_associated(priv)) {
|
|
+ IWL_DEBUG_QOS("send QoS cmd with Qos active %d \n",
|
|
+ priv->qos_data.qos_active);
|
|
+
|
|
+ iwl_send_qos_params_command(priv,
|
|
+ &(priv->qos_data.def_qos_parm));
|
|
+ }
|
|
+}
|
|
+
|
|
+#endif /* CONFIG_IWLWIFI_QOS */
|
|
+/*
|
|
+ * Power management (not Tx power!) functions
|
|
+ */
|
|
+#define MSEC_TO_USEC 1024
|
|
+
|
|
+#if IWL == 3945
|
|
+#define NOSLP __constant_cpu_to_le32(0)
|
|
+#define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK
|
|
+#elif IWL == 4965
|
|
+#define NOSLP __constant_cpu_to_le16(0), 0, 0
|
|
+#define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK, 0, 0
|
|
+#endif
|
|
+#define SLP_TIMEOUT(T) __constant_cpu_to_le32((T) * MSEC_TO_USEC)
|
|
+#define SLP_VEC(X0, X1, X2, X3, X4) {__constant_cpu_to_le32(X0), \
|
|
+ __constant_cpu_to_le32(X1), \
|
|
+ __constant_cpu_to_le32(X2), \
|
|
+ __constant_cpu_to_le32(X3), \
|
|
+ __constant_cpu_to_le32(X4)}
|
|
+
|
|
+
|
|
+/* default power management (not Tx power) table values */
|
|
+/* for tim 0-10 */
|
|
+static struct iwl_power_vec_entry range_0[IWL_POWER_AC] = {
|
|
+ {{NOSLP, SLP_TIMEOUT(0), SLP_TIMEOUT(0), SLP_VEC(0, 0, 0, 0, 0)}, 0},
|
|
+ {{SLP, SLP_TIMEOUT(200), SLP_TIMEOUT(500), SLP_VEC(1, 2, 3, 4, 4)}, 0},
|
|
+ {{SLP, SLP_TIMEOUT(200), SLP_TIMEOUT(300), SLP_VEC(2, 4, 6, 7, 7)}, 0},
|
|
+ {{SLP, SLP_TIMEOUT(50), SLP_TIMEOUT(100), SLP_VEC(2, 6, 9, 9, 10)}, 0},
|
|
+ {{SLP, SLP_TIMEOUT(50), SLP_TIMEOUT(25), SLP_VEC(2, 7, 9, 9, 10)}, 1},
|
|
+ {{SLP, SLP_TIMEOUT(25), SLP_TIMEOUT(25), SLP_VEC(4, 7, 10, 10, 10)}, 1}
|
|
+};
|
|
+
|
|
+/* for tim > 10 */
|
|
+static struct iwl_power_vec_entry range_1[IWL_POWER_AC] = {
|
|
+ {{NOSLP, SLP_TIMEOUT(0), SLP_TIMEOUT(0), SLP_VEC(0, 0, 0, 0, 0)}, 0},
|
|
+ {{SLP, SLP_TIMEOUT(200), SLP_TIMEOUT(500),
|
|
+ SLP_VEC(1, 2, 3, 4, 0xFF)}, 0},
|
|
+ {{SLP, SLP_TIMEOUT(200), SLP_TIMEOUT(300),
|
|
+ SLP_VEC(2, 4, 6, 7, 0xFF)}, 0},
|
|
+ {{SLP, SLP_TIMEOUT(50), SLP_TIMEOUT(100),
|
|
+ SLP_VEC(2, 6, 9, 9, 0xFF)}, 0},
|
|
+ {{SLP, SLP_TIMEOUT(50), SLP_TIMEOUT(25), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0},
|
|
+ {{SLP, SLP_TIMEOUT(25), SLP_TIMEOUT(25),
|
|
+ SLP_VEC(4, 7, 10, 10, 0xFF)}, 0}
|
|
+};
|
|
+
|
|
+int iwl_power_init_handle(struct iwl_priv *priv)
|
|
+{
|
|
+ int rc = 0, i;
|
|
+ struct iwl_power_mgr *pow_data;
|
|
+ int size = sizeof(struct iwl_power_vec_entry) * IWL_POWER_AC;
|
|
+ u16 pci_pm;
|
|
+
|
|
+ IWL_DEBUG_POWER("Initialize power \n");
|
|
+
|
|
+ pow_data = &(priv->power_data);
|
|
+
|
|
+ memset(pow_data, 0, sizeof(*pow_data));
|
|
+
|
|
+ pow_data->active_index = IWL_POWER_RANGE_0;
|
|
+ pow_data->dtim_val = 0xffff;
|
|
+
|
|
+ memcpy(&pow_data->pwr_range_0[0], &range_0[0], size);
|
|
+ memcpy(&pow_data->pwr_range_1[0], &range_1[0], size);
|
|
+
|
|
+ rc = pci_read_config_word(priv->pci_dev, PCI_LINK_CTRL, &pci_pm);
|
|
+ if (rc != 0)
|
|
+ return 0;
|
|
+ else {
|
|
+ struct iwl_powertable_cmd *cmd;
|
|
+
|
|
+ IWL_DEBUG_POWER("adjust power command flags\n");
|
|
+
|
|
+ for (i = 0; i < IWL_POWER_AC; i++) {
|
|
+ cmd = &pow_data->pwr_range_0[i].cmd;
|
|
+
|
|
+ if (pci_pm & 0x1)
|
|
+ cmd->flags &= ~IWL_POWER_PCI_PM_MSK;
|
|
+ else
|
|
+ cmd->flags |= IWL_POWER_PCI_PM_MSK;
|
|
+ }
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int iwl_update_power_cmd(struct iwl_priv *priv,
|
|
+ struct iwl_powertable_cmd *cmd, u32 mode)
|
|
+{
|
|
+ int rc = 0, i;
|
|
+ u8 skip;
|
|
+ u32 max_sleep = 0;
|
|
+ struct iwl_power_vec_entry *range;
|
|
+ u8 period = 0;
|
|
+ struct iwl_power_mgr *pow_data;
|
|
+
|
|
+ if (mode > IWL_POWER_INDEX_5) {
|
|
+ IWL_DEBUG_POWER("Error invalid power mode \n");
|
|
+ return -1;
|
|
+ }
|
|
+ pow_data = &(priv->power_data);
|
|
+
|
|
+ if (pow_data->active_index == IWL_POWER_RANGE_0)
|
|
+ range = &pow_data->pwr_range_0[0];
|
|
+ else
|
|
+ range = &pow_data->pwr_range_1[1];
|
|
+
|
|
+ memcpy(cmd, &range[mode].cmd, sizeof(struct iwl_powertable_cmd));
|
|
+
|
|
+#ifdef IWL_MAC80211_DISABLE
|
|
+ if (priv->assoc_network != NULL) {
|
|
+ unsigned long flags;
|
|
+
|
|
+ period = priv->assoc_network->tim.tim_period;
|
|
+ }
|
|
+#endif /*IWL_MAC80211_DISABLE */
|
|
+ skip = range[mode].no_dtim;
|
|
+
|
|
+ if (period == 0) {
|
|
+ period = 1;
|
|
+ skip = 0;
|
|
+ }
|
|
+
|
|
+ if (skip == 0) {
|
|
+ max_sleep = period;
|
|
+ cmd->flags &= ~IWL_POWER_SLEEP_OVER_DTIM_MSK;
|
|
+ } else {
|
|
+ __le32 slp_itrvl = cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1];
|
|
+ max_sleep = (le32_to_cpu(slp_itrvl) / period) * period;
|
|
+ cmd->flags |= IWL_POWER_SLEEP_OVER_DTIM_MSK;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < IWL_POWER_VEC_SIZE; i++) {
|
|
+ if (le32_to_cpu(cmd->sleep_interval[i]) > max_sleep)
|
|
+ cmd->sleep_interval[i] = cpu_to_le32(max_sleep);
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_POWER("Flags value = 0x%08X\n", cmd->flags);
|
|
+ IWL_DEBUG_POWER("Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout));
|
|
+ IWL_DEBUG_POWER("Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout));
|
|
+ IWL_DEBUG_POWER("Sleep interval vector = { %d , %d , %d , %d , %d }\n",
|
|
+ le32_to_cpu(cmd->sleep_interval[0]),
|
|
+ le32_to_cpu(cmd->sleep_interval[1]),
|
|
+ le32_to_cpu(cmd->sleep_interval[2]),
|
|
+ le32_to_cpu(cmd->sleep_interval[3]),
|
|
+ le32_to_cpu(cmd->sleep_interval[4]));
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int iwl_send_power_mode(struct iwl_priv *priv, u32 mode)
|
|
+{
|
|
+ u32 final_mode = mode;
|
|
+ int rc;
|
|
+ struct iwl_powertable_cmd cmd;
|
|
+
|
|
+ /* If on battery, set to 3,
|
|
+ * if plugged into AC power, set to CAM ("continuosly aware mode"),
|
|
+ * else user level */
|
|
+ switch (mode) {
|
|
+ case IWL_POWER_BATTERY:
|
|
+ final_mode = IWL_POWER_INDEX_3;
|
|
+ break;
|
|
+ case IWL_POWER_AC:
|
|
+ final_mode = IWL_POWER_MODE_CAM;
|
|
+ break;
|
|
+ default:
|
|
+ final_mode = mode;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+#if IWL == 4965
|
|
+ cmd.keep_alive_beacons = 0;
|
|
+#endif
|
|
+
|
|
+ iwl_update_power_cmd(priv, &cmd, final_mode);
|
|
+
|
|
+ rc = iwl_send_cmd_pdu(priv, POWER_TABLE_CMD, sizeof(cmd), &cmd);
|
|
+
|
|
+ if (final_mode == IWL_POWER_MODE_CAM)
|
|
+ clear_bit(STATUS_POWER_PMI, &priv->status);
|
|
+ else
|
|
+ set_bit(STATUS_POWER_PMI, &priv->status);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+int iwl_is_network_packet(struct iwl_priv *priv, struct ieee80211_hdr *header)
|
|
+{
|
|
+ /* Filter incoming packets to determine if they are targeted toward
|
|
+ * this network, discarding packets coming from ourselves */
|
|
+ switch (priv->iw_mode) {
|
|
+ case IEEE80211_IF_TYPE_IBSS: /* Header: Dest. | Source | BSSID */
|
|
+ /* packets from our adapter are dropped (echo) */
|
|
+ if (!compare_ether_addr(header->addr2, priv->mac_addr))
|
|
+ return 0;
|
|
+ /* {broad,multi}cast packets to our IBSS go through */
|
|
+ if (is_multicast_ether_addr(header->addr1))
|
|
+ return !compare_ether_addr(header->addr3, priv->bssid);
|
|
+ /* packets to our adapter go through */
|
|
+ return !compare_ether_addr(header->addr1, priv->mac_addr);
|
|
+ case IEEE80211_IF_TYPE_STA: /* Header: Dest. | AP{BSSID} | Source */
|
|
+ /* packets from our adapter are dropped (echo) */
|
|
+ if (!compare_ether_addr(header->addr3, priv->mac_addr))
|
|
+ return 0;
|
|
+ /* {broad,multi}cast packets to our BSS go through */
|
|
+ if (is_multicast_ether_addr(header->addr1))
|
|
+ return !compare_ether_addr(header->addr2, priv->bssid);
|
|
+ /* packets to our adapter go through */
|
|
+ return !compare_ether_addr(header->addr1, priv->mac_addr);
|
|
+ }
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+#define TX_STATUS_ENTRY(x) case TX_STATUS_FAIL_ ## x: return #x
|
|
+
|
|
+const char *iwl_get_tx_fail_reason(u32 status)
|
|
+{
|
|
+ switch (status & TX_STATUS_MSK) {
|
|
+ case TX_STATUS_SUCCESS:
|
|
+ return "SUCCESS";
|
|
+ TX_STATUS_ENTRY(SHORT_LIMIT);
|
|
+ TX_STATUS_ENTRY(LONG_LIMIT);
|
|
+ TX_STATUS_ENTRY(FIFO_UNDERRUN);
|
|
+ TX_STATUS_ENTRY(MGMNT_ABORT);
|
|
+ TX_STATUS_ENTRY(NEXT_FRAG);
|
|
+ TX_STATUS_ENTRY(LIFE_EXPIRE);
|
|
+ TX_STATUS_ENTRY(DEST_PS);
|
|
+ TX_STATUS_ENTRY(ABORTED);
|
|
+ TX_STATUS_ENTRY(BT_RETRY);
|
|
+ TX_STATUS_ENTRY(STA_INVALID);
|
|
+ TX_STATUS_ENTRY(FRAG_DROPPED);
|
|
+ TX_STATUS_ENTRY(TID_DISABLE);
|
|
+ TX_STATUS_ENTRY(FRAME_FLUSHED);
|
|
+ TX_STATUS_ENTRY(INSUFFICIENT_CF_POLL);
|
|
+ TX_STATUS_ENTRY(TX_LOCKED);
|
|
+ TX_STATUS_ENTRY(NO_BEACON_ON_RADAR);
|
|
+ }
|
|
+
|
|
+ return "UNKNOWN";
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_scan_cancel - Cancel any currently executing HW scan
|
|
+ *
|
|
+ * NOTE: priv->mutex is not required before calling this function
|
|
+ */
|
|
+static int iwl_scan_cancel(struct iwl_priv *priv)
|
|
+{
|
|
+ if (!test_bit(STATUS_SCAN_HW, &priv->status)) {
|
|
+ clear_bit(STATUS_SCANNING, &priv->status);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (test_bit(STATUS_SCANNING, &priv->status)) {
|
|
+ if (!test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
|
|
+ IWL_DEBUG_SCAN("Queuing scan abort.\n");
|
|
+ set_bit(STATUS_SCAN_ABORTING, &priv->status);
|
|
+ queue_work(priv->workqueue, &priv->abort_scan);
|
|
+
|
|
+ } else
|
|
+ IWL_DEBUG_SCAN("Scan abort already in progress.\n");
|
|
+
|
|
+ return test_bit(STATUS_SCANNING, &priv->status);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_scan_cancel_timeout - Cancel any currently executing HW scan
|
|
+ * @ms: amount of time to wait (in milliseconds) for scan to abort
|
|
+ *
|
|
+ * NOTE: priv->mutex must be held before calling this function
|
|
+ */
|
|
+static int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms)
|
|
+{
|
|
+ unsigned long now = jiffies;
|
|
+ int ret;
|
|
+
|
|
+ ret = iwl_scan_cancel(priv);
|
|
+ if (ret && ms) {
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ while (!time_after(jiffies, now + msecs_to_jiffies(ms)) &&
|
|
+ test_bit(STATUS_SCANNING, &priv->status))
|
|
+ msleep(1);
|
|
+ mutex_lock(&priv->mutex);
|
|
+
|
|
+ return test_bit(STATUS_SCANNING, &priv->status);
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void iwl_sequence_reset(struct iwl_priv *priv)
|
|
+{
|
|
+ /* Reset ieee stats */
|
|
+
|
|
+ /* We don't reset the net_device_stats (ieee->stats) on
|
|
+ * re-association */
|
|
+
|
|
+ priv->last_seq_num = -1;
|
|
+ priv->last_frag_num = -1;
|
|
+ priv->last_packet_time = 0;
|
|
+
|
|
+ iwl_scan_cancel(priv);
|
|
+}
|
|
+
|
|
+#if IWL == 4965
|
|
+#define MAX_UCODE_BEACON_INTERVAL 4096
|
|
+#else
|
|
+#define MAX_UCODE_BEACON_INTERVAL 1024
|
|
+#endif
|
|
+#define INTEL_CONN_LISTEN_INTERVAL __constant_cpu_to_le16(0xA)
|
|
+
|
|
+static __le16 iwl_adjust_beacon_interval(u16 beacon_val)
|
|
+{
|
|
+ u16 new_val = 0;
|
|
+ u16 beacon_factor = 0;
|
|
+
|
|
+ beacon_factor =
|
|
+ (beacon_val + MAX_UCODE_BEACON_INTERVAL)
|
|
+ / MAX_UCODE_BEACON_INTERVAL;
|
|
+ new_val = beacon_val / beacon_factor;
|
|
+
|
|
+ return cpu_to_le16(new_val);
|
|
+}
|
|
+
|
|
+static void iwl_setup_rxon_timing(struct iwl_priv *priv)
|
|
+{
|
|
+ u64 interval_tm_unit;
|
|
+ u64 tsf, result;
|
|
+ unsigned long flags;
|
|
+ struct ieee80211_conf *conf = NULL;
|
|
+ u16 beacon_int = 0;
|
|
+
|
|
+ conf = ieee80211_get_hw_conf(priv->hw);
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ priv->rxon_timing.timestamp.dw[1] = cpu_to_le32(priv->timestamp1);
|
|
+ priv->rxon_timing.timestamp.dw[0] = cpu_to_le32(priv->timestamp0);
|
|
+
|
|
+ priv->rxon_timing.listen_interval = INTEL_CONN_LISTEN_INTERVAL;
|
|
+
|
|
+ tsf = priv->timestamp1;
|
|
+ tsf = ((tsf << 32) | priv->timestamp0);
|
|
+
|
|
+ beacon_int = priv->beacon_int;
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ if (priv->iw_mode == IEEE80211_IF_TYPE_STA) {
|
|
+ if (beacon_int == 0) {
|
|
+ priv->rxon_timing.beacon_interval = cpu_to_le16(100);
|
|
+ priv->rxon_timing.beacon_init_val = cpu_to_le32(102400);
|
|
+ } else {
|
|
+ priv->rxon_timing.beacon_interval =
|
|
+ cpu_to_le16(beacon_int);
|
|
+ priv->rxon_timing.beacon_interval =
|
|
+ iwl_adjust_beacon_interval(
|
|
+ le16_to_cpu(priv->rxon_timing.beacon_interval));
|
|
+ }
|
|
+
|
|
+ priv->rxon_timing.atim_window = 0;
|
|
+ } else {
|
|
+ priv->rxon_timing.beacon_interval =
|
|
+ iwl_adjust_beacon_interval(conf->beacon_int);
|
|
+ /* TODO: we need to get atim_window from upper stack
|
|
+ * for now we set to 0 */
|
|
+ priv->rxon_timing.atim_window = 0;
|
|
+ }
|
|
+
|
|
+ interval_tm_unit =
|
|
+ (le16_to_cpu(priv->rxon_timing.beacon_interval) * 1024);
|
|
+ result = do_div(tsf, interval_tm_unit);
|
|
+ priv->rxon_timing.beacon_init_val =
|
|
+ cpu_to_le32((u32) ((u64) interval_tm_unit - result));
|
|
+
|
|
+ IWL_DEBUG_ASSOC
|
|
+ ("beacon interval %d beacon timer %d beacon tim %d\n",
|
|
+ le16_to_cpu(priv->rxon_timing.beacon_interval),
|
|
+ le32_to_cpu(priv->rxon_timing.beacon_init_val),
|
|
+ le16_to_cpu(priv->rxon_timing.atim_window));
|
|
+}
|
|
+
|
|
+static int iwl_scan_initiate(struct iwl_priv *priv)
|
|
+{
|
|
+ if (priv->iw_mode == IEEE80211_IF_TYPE_AP) {
|
|
+ IWL_ERROR("APs don't scan.\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (!iwl_is_ready_rf(priv)) {
|
|
+ IWL_DEBUG_SCAN("Aborting scan due to not ready.\n");
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ if (test_bit(STATUS_SCANNING, &priv->status)) {
|
|
+ IWL_DEBUG_SCAN("Scan already in progress.\n");
|
|
+ return -EAGAIN;
|
|
+ }
|
|
+
|
|
+ if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
|
|
+ IWL_DEBUG_SCAN("Scan request while abort pending. "
|
|
+ "Queuing.\n");
|
|
+ return -EAGAIN;
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_INFO("Starting scan...\n");
|
|
+ priv->scan_bands = 2;
|
|
+ set_bit(STATUS_SCANNING, &priv->status);
|
|
+ priv->scan_start = jiffies;
|
|
+ priv->scan_pass_start = priv->scan_start;
|
|
+
|
|
+ queue_work(priv->workqueue, &priv->request_scan);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int iwl_set_rxon_hwcrypto(struct iwl_priv *priv, int hw_decrypt)
|
|
+{
|
|
+ struct iwl_rxon_cmd *rxon = &priv->staging_rxon;
|
|
+
|
|
+ if (hw_decrypt)
|
|
+ rxon->filter_flags &= ~RXON_FILTER_DIS_DECRYPT_MSK;
|
|
+ else
|
|
+ rxon->filter_flags |= RXON_FILTER_DIS_DECRYPT_MSK;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void iwl_set_flags_for_phymode(struct iwl_priv *priv, u8 phymode)
|
|
+{
|
|
+ if (phymode == MODE_IEEE80211A) {
|
|
+ priv->staging_rxon.flags &=
|
|
+ ~(RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK
|
|
+ | RXON_FLG_CCK_MSK);
|
|
+ priv->staging_rxon.flags |= RXON_FLG_SHORT_SLOT_MSK;
|
|
+ } else {
|
|
+ /* Copied from iwl_bg_post_associate() */
|
|
+ if (priv->assoc_capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
|
|
+ priv->staging_rxon.flags |= RXON_FLG_SHORT_SLOT_MSK;
|
|
+ else
|
|
+ priv->staging_rxon.flags &= ~RXON_FLG_SHORT_SLOT_MSK;
|
|
+
|
|
+ if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS)
|
|
+ priv->staging_rxon.flags &= ~RXON_FLG_SHORT_SLOT_MSK;
|
|
+
|
|
+ priv->staging_rxon.flags |= RXON_FLG_BAND_24G_MSK;
|
|
+ priv->staging_rxon.flags |= RXON_FLG_AUTO_DETECT_MSK;
|
|
+ priv->staging_rxon.flags &= ~RXON_FLG_CCK_MSK;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * initilize rxon structure with default values fromm eeprom
|
|
+ */
|
|
+static void iwl_connection_init_rx_config(struct iwl_priv *priv)
|
|
+{
|
|
+ const struct iwl_channel_info *ch_info;
|
|
+
|
|
+ memset(&priv->staging_rxon, 0, sizeof(priv->staging_rxon));
|
|
+
|
|
+ switch (priv->iw_mode) {
|
|
+ case IEEE80211_IF_TYPE_AP:
|
|
+ priv->staging_rxon.dev_type = RXON_DEV_TYPE_AP;
|
|
+ break;
|
|
+
|
|
+ case IEEE80211_IF_TYPE_STA:
|
|
+ priv->staging_rxon.dev_type = RXON_DEV_TYPE_ESS;
|
|
+ priv->staging_rxon.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK;
|
|
+ break;
|
|
+
|
|
+ case IEEE80211_IF_TYPE_IBSS:
|
|
+ priv->staging_rxon.dev_type = RXON_DEV_TYPE_IBSS;
|
|
+ priv->staging_rxon.flags = RXON_FLG_SHORT_PREAMBLE_MSK;
|
|
+ priv->staging_rxon.filter_flags = RXON_FILTER_BCON_AWARE_MSK |
|
|
+ RXON_FILTER_ACCEPT_GRP_MSK;
|
|
+ break;
|
|
+
|
|
+ case IEEE80211_IF_TYPE_MNTR:
|
|
+ priv->staging_rxon.dev_type = RXON_DEV_TYPE_SNIFFER;
|
|
+ priv->staging_rxon.filter_flags = RXON_FILTER_PROMISC_MSK |
|
|
+ RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_ACCEPT_GRP_MSK;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+#if 0
|
|
+ /* TODO: Figure out when short_preamble would be set and cache from
|
|
+ * that */
|
|
+ if (!hw_to_local(priv->hw)->short_preamble)
|
|
+ priv->staging_rxon.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK;
|
|
+ else
|
|
+ priv->staging_rxon.flags |= RXON_FLG_SHORT_PREAMBLE_MSK;
|
|
+#endif
|
|
+
|
|
+ ch_info = iwl_get_channel_info(priv, priv->phymode,
|
|
+ le16_to_cpu(priv->staging_rxon.channel));
|
|
+
|
|
+ if (!ch_info)
|
|
+ ch_info = &priv->channel_info[0];
|
|
+
|
|
+ /*
|
|
+ * in some case A channels are all non IBSS
|
|
+ * in this case force B/G channel
|
|
+ */
|
|
+ if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) &&
|
|
+ !(is_channel_ibss(ch_info)))
|
|
+ ch_info = &priv->channel_info[0];
|
|
+
|
|
+ priv->staging_rxon.channel = cpu_to_le16(ch_info->channel);
|
|
+ if (is_channel_a_band(ch_info))
|
|
+ priv->phymode = MODE_IEEE80211A;
|
|
+ else
|
|
+ priv->phymode = MODE_IEEE80211G;
|
|
+
|
|
+ iwl_set_flags_for_phymode(priv, priv->phymode);
|
|
+
|
|
+ priv->staging_rxon.ofdm_basic_rates =
|
|
+ (IWL_OFDM_RATES_MASK >> IWL_FIRST_OFDM_RATE) & 0xFF;
|
|
+ priv->staging_rxon.cck_basic_rates =
|
|
+ (IWL_CCK_RATES_MASK >> IWL_FIRST_CCK_RATE) & 0xF;
|
|
+
|
|
+#if IWL == 4965
|
|
+ priv->staging_rxon.flags |= RXON_FLG_CHANNEL_MODE_LEGACY_MSK;
|
|
+ memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN);
|
|
+ memcpy(priv->staging_rxon.wlap_bssid_addr, priv->mac_addr, ETH_ALEN);
|
|
+ priv->staging_rxon.ofdm_ht_single_stream_basic_rates = 0xff;
|
|
+ priv->staging_rxon.ofdm_ht_dual_stream_basic_rates = 0xff;
|
|
+ iwl4965_set_rxon_chain(priv);
|
|
+#endif
|
|
+}
|
|
+
|
|
+static int iwl_set_mode(struct iwl_priv *priv, int mode)
|
|
+{
|
|
+ if (!iwl_is_ready_rf(priv))
|
|
+ return -EAGAIN;
|
|
+
|
|
+ if (mode == IEEE80211_IF_TYPE_IBSS) {
|
|
+ const struct iwl_channel_info *ch_info;
|
|
+
|
|
+ ch_info = iwl_get_channel_info(priv,
|
|
+ priv->phymode,
|
|
+ le16_to_cpu(priv->staging_rxon.channel));
|
|
+
|
|
+ if (!ch_info || !is_channel_ibss(ch_info)) {
|
|
+ IWL_ERROR("channel %d not IBSS channel\n",
|
|
+ le16_to_cpu(priv->staging_rxon.channel));
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ cancel_delayed_work(&priv->scan_check);
|
|
+ if (iwl_scan_cancel_timeout(priv, 100)) {
|
|
+ IWL_WARNING("Aborted scan still in progress after 100ms\n");
|
|
+ IWL_DEBUG_MAC80211("leaving - scan abort failed.\n");
|
|
+ return -EAGAIN;
|
|
+ }
|
|
+
|
|
+ priv->iw_mode = mode;
|
|
+
|
|
+ iwl_connection_init_rx_config(priv);
|
|
+ memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN);
|
|
+
|
|
+ iwl_clear_stations_table(priv);
|
|
+
|
|
+ iwl_commit_rxon(priv);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void iwl_build_tx_cmd_hwcrypto(struct iwl_priv *priv,
|
|
+ struct ieee80211_tx_control *ctl,
|
|
+ struct iwl_cmd *cmd,
|
|
+ struct sk_buff *skb_frag,
|
|
+ int last_frag)
|
|
+{
|
|
+ struct iwl_hw_key *keyinfo = &priv->stations[ctl->key_idx].keyinfo;
|
|
+
|
|
+ switch (keyinfo->alg) {
|
|
+ case ALG_CCMP:
|
|
+ cmd->cmd.tx.sec_ctl = TX_CMD_SEC_CCM;
|
|
+
|
|
+ cmd->cmd.tx.hdr[0].frame_control |=
|
|
+ cpu_to_le16(IEEE80211_FCTL_PROTECTED);
|
|
+ /* XXX: ACK flag must be set for CCMP even if it
|
|
+ * is a multicast/broadcast packet, because CCMP
|
|
+ * group communication encrypted by GTK is
|
|
+ * actually done by the AP. */
|
|
+ cmd->cmd.tx.tx_flags |= TX_CMD_FLG_ACK_MSK;
|
|
+ memcpy(cmd->cmd.tx.key, keyinfo->key, keyinfo->keylen);
|
|
+ IWL_DEBUG_TX("tx_cmd with aes hwcrypto\n");
|
|
+ break;
|
|
+ case ALG_TKIP:
|
|
+#if 0
|
|
+ cmd->cmd.tx.sec_ctl = TX_CMD_SEC_TKIP;
|
|
+
|
|
+ if (last_frag)
|
|
+ memcpy(cmd->cmd.tx.tkip_mic.byte, skb_frag->tail - 8,
|
|
+ 8);
|
|
+ else
|
|
+ memset(cmd->cmd.tx.tkip_mic.byte, 0, 8);
|
|
+
|
|
+ cmd->cmd.tx.hdr[0].frame_control |=
|
|
+ cpu_to_le16(IEEE80211_FCTL_PROTECTED);
|
|
+ /* XXX: ACK flag must be set for CCMP even if it
|
|
+ * is a multicast/broadcast packet, because CCMP
|
|
+ * group communication encrypted by GTK is
|
|
+ * actually done by the AP. */
|
|
+ cmd->cmd.tx.tx_flags |= TX_CMD_FLG_ACK_MSK;
|
|
+#endif
|
|
+ break;
|
|
+ case ALG_WEP:
|
|
+ cmd->cmd.tx.sec_ctl = 1 | /* WEP */
|
|
+ (ctl->key_idx & 0x3) << 6;
|
|
+
|
|
+ if (keyinfo->keylen == 13)
|
|
+ cmd->cmd.tx.sec_ctl |= (1 << 3); /* 128-bit */
|
|
+
|
|
+ memcpy(&cmd->cmd.tx.key[3], keyinfo->key, keyinfo->keylen);
|
|
+
|
|
+ cmd->cmd.tx.hdr[0].frame_control |=
|
|
+ cpu_to_le16(IEEE80211_FCTL_PROTECTED);
|
|
+
|
|
+ IWL_DEBUG_TX("Configuring packet for WEP encryption "
|
|
+ "with key %d\n", ctl->key_idx);
|
|
+ break;
|
|
+
|
|
+ case ALG_NONE:
|
|
+ IWL_DEBUG_TX("Tx packet in the clear "
|
|
+ "(encrypt requested).\n");
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ printk(KERN_ERR "Unknown encode alg %d\n", keyinfo->alg);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+/*
|
|
+ * handle build REPLY_TX command notification.
|
|
+ */
|
|
+static void iwl_build_tx_cmd_basic(struct iwl_priv *priv,
|
|
+ struct iwl_cmd *cmd,
|
|
+ struct ieee80211_tx_control *ctrl,
|
|
+ struct ieee80211_hdr *hdr,
|
|
+ int is_unicast, u8 std_id)
|
|
+{
|
|
+ __le16 *qc;
|
|
+ u16 fc = le16_to_cpu(hdr->frame_control);
|
|
+ __le32 tx_flags = cmd->cmd.tx.tx_flags;
|
|
+
|
|
+ cmd->cmd.tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
|
|
+ if (!(ctrl->flags & IEEE80211_TXCTL_NO_ACK)) {
|
|
+ tx_flags |= TX_CMD_FLG_ACK_MSK;
|
|
+ if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)
|
|
+ tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
|
|
+ if (ieee80211_is_probe_response(fc) &&
|
|
+ !(le16_to_cpu(hdr->seq_ctrl) & 0xf))
|
|
+ tx_flags |= TX_CMD_FLG_TSF_MSK;
|
|
+ } else {
|
|
+ tx_flags &= (~TX_CMD_FLG_ACK_MSK);
|
|
+ tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
|
|
+ }
|
|
+
|
|
+ cmd->cmd.tx.sta_id = std_id;
|
|
+ if (ieee80211_get_morefrag(hdr))
|
|
+ tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK;
|
|
+
|
|
+ qc = ieee80211_get_qos_ctrl(hdr);
|
|
+ if (qc) {
|
|
+ cmd->cmd.tx.tid_tspec = (u8) (le16_to_cpu(*qc) & 0xf);
|
|
+ tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK;
|
|
+ } else
|
|
+ tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
|
|
+
|
|
+ if (ctrl->flags & IEEE80211_TXCTL_USE_RTS_CTS) {
|
|
+ tx_flags |= TX_CMD_FLG_RTS_MSK;
|
|
+ tx_flags &= ~TX_CMD_FLG_CTS_MSK;
|
|
+ } else if (ctrl->flags & IEEE80211_TXCTL_USE_CTS_PROTECT) {
|
|
+ tx_flags &= ~TX_CMD_FLG_RTS_MSK;
|
|
+ tx_flags |= TX_CMD_FLG_CTS_MSK;
|
|
+ }
|
|
+
|
|
+ if ((tx_flags & TX_CMD_FLG_RTS_MSK) || (tx_flags & TX_CMD_FLG_CTS_MSK))
|
|
+ tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK;
|
|
+
|
|
+ tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK);
|
|
+ if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) {
|
|
+ if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ ||
|
|
+ (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_REASSOC_REQ)
|
|
+ cmd->cmd.tx.timeout.pm_frame_timeout =
|
|
+ cpu_to_le16(3);
|
|
+ else
|
|
+ cmd->cmd.tx.timeout.pm_frame_timeout =
|
|
+ cpu_to_le16(2);
|
|
+ } else
|
|
+ cmd->cmd.tx.timeout.pm_frame_timeout = 0;
|
|
+
|
|
+ cmd->cmd.tx.driver_txop = 0;
|
|
+ cmd->cmd.tx.tx_flags = tx_flags;
|
|
+ cmd->cmd.tx.next_frame_len = 0;
|
|
+}
|
|
+
|
|
+static int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr)
|
|
+{
|
|
+ int sta_id;
|
|
+ u16 fc = le16_to_cpu(hdr->frame_control);
|
|
+
|
|
+ /* If this frame is broadcast or not data then use the broadcast
|
|
+ * station id */
|
|
+ if (((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA) ||
|
|
+ is_multicast_ether_addr(hdr->addr1))
|
|
+ return IWL_BROADCAST_ID;
|
|
+
|
|
+ switch (priv->iw_mode) {
|
|
+
|
|
+ /* If this frame is part of a BSS network (we're a station), then
|
|
+ * we use the AP's station id */
|
|
+ case IEEE80211_IF_TYPE_STA:
|
|
+ return IWL_AP_ID;
|
|
+
|
|
+ /* If we are an AP, then find the station, or use BCAST */
|
|
+ case IEEE80211_IF_TYPE_AP:
|
|
+ sta_id = iwl_hw_find_station(priv, hdr->addr1);
|
|
+ if (sta_id != IWL_INVALID_STATION)
|
|
+ return sta_id;
|
|
+ return IWL_BROADCAST_ID;
|
|
+
|
|
+ /* If this frame is part of a IBSS network, then we use the
|
|
+ * target specific station id */
|
|
+ case IEEE80211_IF_TYPE_IBSS:
|
|
+ sta_id = iwl_hw_find_station(priv, hdr->addr1);
|
|
+ if (sta_id != IWL_INVALID_STATION)
|
|
+ return sta_id;
|
|
+
|
|
+ sta_id = iwl_add_station(priv, hdr->addr1, 0, CMD_ASYNC);
|
|
+
|
|
+ if (sta_id != IWL_INVALID_STATION)
|
|
+ return sta_id;
|
|
+
|
|
+ IWL_DEBUG_DROP("Station " MAC_FMT " not in station map. "
|
|
+ "Defaulting to broadcast...\n",
|
|
+ MAC_ARG(hdr->addr1));
|
|
+ iwl_print_hex_dump(IWL_DL_DROP, (u8 *) hdr, sizeof(*hdr));
|
|
+ return IWL_BROADCAST_ID;
|
|
+
|
|
+ default:
|
|
+ IWL_WARNING("Unkown mode of operation: %d", priv->iw_mode);
|
|
+ return IWL_BROADCAST_ID;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * start REPLY_TX command process
|
|
+ */
|
|
+static int iwl_tx_skb(struct iwl_priv *priv,
|
|
+ struct sk_buff *skb, struct ieee80211_tx_control *ctl)
|
|
+{
|
|
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
|
+ struct iwl_tfd_frame *tfd;
|
|
+ u32 *control_flags;
|
|
+ int txq_id = ctl->queue;
|
|
+ struct iwl_tx_queue *txq = NULL;
|
|
+ struct iwl_queue *q = NULL;
|
|
+ dma_addr_t phys_addr;
|
|
+ dma_addr_t txcmd_phys;
|
|
+ struct iwl_cmd *out_cmd = NULL;
|
|
+ u16 len, idx, len_org;
|
|
+ u8 id, hdr_len, unicast;
|
|
+ u8 sta_id;
|
|
+ u16 seq_number = 0;
|
|
+ u16 fc;
|
|
+ __le16 *qc;
|
|
+ u8 wait_write_ptr = 0;
|
|
+ unsigned long flags;
|
|
+ int rc;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ if (iwl_is_rfkill(priv)) {
|
|
+ IWL_DEBUG_DROP("Dropping - RF KILL\n");
|
|
+ goto drop_unlock;
|
|
+ }
|
|
+
|
|
+ if (!priv->interface_id) {
|
|
+ IWL_DEBUG_DROP("Dropping - !priv->interface_id\n");
|
|
+ goto drop_unlock;
|
|
+ }
|
|
+
|
|
+ if ((ctl->tx_rate & 0xFF) == IWL_INVALID_RATE) {
|
|
+ IWL_ERROR("ERROR: No TX rate available.\n");
|
|
+ goto drop_unlock;
|
|
+ }
|
|
+
|
|
+ unicast = !is_multicast_ether_addr(hdr->addr1);
|
|
+ id = 0;
|
|
+
|
|
+ fc = le16_to_cpu(hdr->frame_control);
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+ if (ieee80211_is_auth(fc))
|
|
+ IWL_DEBUG_TX("Sending AUTH frame\n");
|
|
+ else if (ieee80211_is_assoc_request(fc))
|
|
+ IWL_DEBUG_TX("Sending ASSOC frame\n");
|
|
+ else if (ieee80211_is_reassoc_request(fc))
|
|
+ IWL_DEBUG_TX("Sending REASSOC frame\n");
|
|
+#endif
|
|
+
|
|
+ if (!iwl_is_associated(priv) &&
|
|
+ ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)) {
|
|
+ IWL_DEBUG_DROP("Dropping - !iwl_is_associated\n");
|
|
+ goto drop_unlock;
|
|
+ }
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ hdr_len = ieee80211_get_hdrlen(fc);
|
|
+ sta_id = iwl_get_sta_id(priv, hdr);
|
|
+ if (sta_id == IWL_INVALID_STATION) {
|
|
+ IWL_DEBUG_DROP("Dropping - INVALID STATION: " MAC_FMT "\n",
|
|
+ MAC_ARG(hdr->addr1));
|
|
+ goto drop;
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_RATE("station Id %d\n", sta_id);
|
|
+
|
|
+ qc = ieee80211_get_qos_ctrl(hdr);
|
|
+ if (qc) {
|
|
+ u8 tid = (u8)(le16_to_cpu(*qc) & 0xf);
|
|
+ seq_number = priv->stations[sta_id].tid[tid].seq_number &
|
|
+ IEEE80211_SCTL_SEQ;
|
|
+ hdr->seq_ctrl = cpu_to_le16(seq_number) |
|
|
+ (hdr->seq_ctrl &
|
|
+ __constant_cpu_to_le16(IEEE80211_SCTL_FRAG));
|
|
+ seq_number += 0x10;
|
|
+#if IWL == 4965
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+ /* aggregation is on for this <sta,tid> */
|
|
+ if (ctl->flags & IEEE80211_TXCTL_HT_MPDU_AGG)
|
|
+ txq_id = priv->stations[sta_id].tid[tid].agg.txq_id;
|
|
+#endif /* CONFIG_IWLWIFI_HT_AGG */
|
|
+#endif /* CONFIG_IWLWIFI_HT */
|
|
+#endif
|
|
+ }
|
|
+ txq = &priv->txq[txq_id];
|
|
+ q = &txq->q;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+
|
|
+ tfd = &txq->bd[q->first_empty];
|
|
+ memset(tfd, 0, sizeof(*tfd));
|
|
+ control_flags = (u32 *) tfd;
|
|
+ idx = get_cmd_index(q, q->first_empty, 0);
|
|
+
|
|
+ memset(&(txq->txb[q->first_empty]), 0, sizeof(struct iwl_tx_info));
|
|
+ txq->txb[q->first_empty].skb[0] = skb;
|
|
+ memcpy(&(txq->txb[q->first_empty].status.control),
|
|
+ ctl, sizeof(struct ieee80211_tx_control));
|
|
+ out_cmd = &txq->cmd[idx];
|
|
+ memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr));
|
|
+ memset(&out_cmd->cmd.tx, 0, sizeof(out_cmd->cmd.tx));
|
|
+ out_cmd->hdr.cmd = REPLY_TX;
|
|
+ out_cmd->hdr.sequence = cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) |
|
|
+ INDEX_TO_SEQ(q->first_empty)));
|
|
+ /* copy frags header */
|
|
+ memcpy(out_cmd->cmd.tx.hdr, hdr, hdr_len);
|
|
+
|
|
+ /* hdr = (struct ieee80211_hdr *)out_cmd->cmd.tx.hdr; */
|
|
+ len = priv->hw_setting.tx_cmd_len +
|
|
+ sizeof(struct iwl_cmd_header) + hdr_len;
|
|
+
|
|
+ len_org = len;
|
|
+ len = (len + 3) & ~3;
|
|
+
|
|
+ if (len_org != len)
|
|
+ len_org = 1;
|
|
+ else
|
|
+ len_org = 0;
|
|
+
|
|
+ txcmd_phys = txq->dma_addr_cmd + sizeof(struct iwl_cmd) * idx +
|
|
+ offsetof(struct iwl_cmd, hdr);
|
|
+
|
|
+ iwl_hw_txq_attach_buf_to_tfd(priv, tfd, txcmd_phys, len);
|
|
+
|
|
+ if (ctl->key_idx != -1)
|
|
+ iwl_build_tx_cmd_hwcrypto(priv, ctl, out_cmd, skb, 0);
|
|
+
|
|
+ /* 802.11 null functions have no payload... */
|
|
+ len = skb->len - hdr_len;
|
|
+ if (len) {
|
|
+ phys_addr = pci_map_single(priv->pci_dev, skb->data + hdr_len,
|
|
+ len, PCI_DMA_TODEVICE);
|
|
+ iwl_hw_txq_attach_buf_to_tfd(priv, tfd, phys_addr, len);
|
|
+ }
|
|
+
|
|
+#if IWL == 3945
|
|
+ /* If there is no payload, then only one TFD is used */
|
|
+ if (!len)
|
|
+ *control_flags = TFD_CTL_COUNT_SET(1);
|
|
+ else
|
|
+ *control_flags = TFD_CTL_COUNT_SET(2) |
|
|
+ TFD_CTL_PAD_SET(U32_PAD(len));
|
|
+#else
|
|
+ if (len_org)
|
|
+ out_cmd->cmd.tx.tx_flags |= TX_CMD_FLG_MH_PAD_MSK;
|
|
+#endif
|
|
+ len = (u16)skb->len;
|
|
+ out_cmd->cmd.tx.len = cpu_to_le16(len);
|
|
+
|
|
+ /* TODO need this for burst mode later on */
|
|
+ iwl_build_tx_cmd_basic(priv, out_cmd, ctl, hdr, unicast, sta_id);
|
|
+
|
|
+ /* set is_hcca to 0; it probably will never be implemented */
|
|
+ iwl_hw_build_tx_cmd_rate(priv, out_cmd, ctl, hdr, sta_id, 0);
|
|
+
|
|
+#if IWL == 4965
|
|
+ iwl4965_tx_cmd(priv, out_cmd, sta_id, txcmd_phys,
|
|
+ hdr, hdr_len, ctl, NULL);
|
|
+#elif IWL == 3945
|
|
+ out_cmd->cmd.tx.tx_flags &= ~TX_CMD_FLG_ANT_A_MSK;
|
|
+ out_cmd->cmd.tx.tx_flags &= ~TX_CMD_FLG_ANT_B_MSK;
|
|
+#endif
|
|
+
|
|
+ if (!ieee80211_get_morefrag(hdr)) {
|
|
+ txq->need_update = 1;
|
|
+ if (qc) {
|
|
+ u8 tid = (u8)(le16_to_cpu(*qc) & 0xf);
|
|
+ priv->stations[sta_id].tid[tid].seq_number = seq_number;
|
|
+ }
|
|
+ } else {
|
|
+ wait_write_ptr = 1;
|
|
+ txq->need_update = 0;
|
|
+ }
|
|
+
|
|
+ iwl_print_hex_dump(IWL_DL_TX, out_cmd->cmd.payload,
|
|
+ sizeof(out_cmd->cmd.tx));
|
|
+
|
|
+ iwl_print_hex_dump(IWL_DL_TX, (u8 *)out_cmd->cmd.tx.hdr,
|
|
+ ieee80211_get_hdrlen(fc));
|
|
+
|
|
+ iwl4965_tx_queue_update_wr_ptr(priv, txq, len);
|
|
+
|
|
+ q->first_empty = iwl_queue_inc_wrap(q->first_empty, q->n_bd);
|
|
+ rc = iwl_tx_queue_update_write_ptr(priv, txq);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ if ((iwl_queue_space(q) < q->high_mark)
|
|
+ && priv->mac80211_registered) {
|
|
+ if (wait_write_ptr) {
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ txq->need_update = 1;
|
|
+ iwl_tx_queue_update_write_ptr(priv, txq);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ }
|
|
+
|
|
+ ieee80211_stop_queue(priv->hw, ctl->queue);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+drop_unlock:
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+drop:
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static void iwl_set_rate(struct iwl_priv *priv)
|
|
+{
|
|
+ const struct ieee80211_hw_mode *hw = NULL;
|
|
+ struct ieee80211_rate *rate;
|
|
+ int i;
|
|
+
|
|
+ hw = iwl_get_hw_mode(priv, priv->phymode);
|
|
+
|
|
+ priv->active_rate = 0;
|
|
+ priv->active_rate_basic = 0;
|
|
+
|
|
+ IWL_DEBUG_RATE("Setting rates for 802.11%c\n",
|
|
+ hw->mode == MODE_IEEE80211A ?
|
|
+ 'a' : ((hw->mode == MODE_IEEE80211B) ? 'b' : 'g'));
|
|
+
|
|
+ for (i = 0; i < hw->num_rates; i++) {
|
|
+ rate = &(hw->rates[i]);
|
|
+ if ((rate->val < IWL_RATE_COUNT) &&
|
|
+ (rate->flags & IEEE80211_RATE_SUPPORTED)) {
|
|
+ IWL_DEBUG_RATE("Adding rate index %d (plcp %d)%s\n",
|
|
+ rate->val, iwl_rates[rate->val].plcp,
|
|
+ (rate->flags & IEEE80211_RATE_BASIC) ?
|
|
+ "*" : "");
|
|
+ priv->active_rate |= (1 << rate->val);
|
|
+ if (rate->flags & IEEE80211_RATE_BASIC)
|
|
+ priv->active_rate_basic |= (1 << rate->val);
|
|
+ } else
|
|
+ IWL_DEBUG_RATE("Not adding rate %d (plcp %d)\n",
|
|
+ rate->val, iwl_rates[rate->val].plcp);
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_RATE("Set active_rate = %0x, active_rate_basic = %0x\n",
|
|
+ priv->active_rate, priv->active_rate_basic);
|
|
+
|
|
+ /*
|
|
+ * If a basic rate is configured, then use it (adding IWL_RATE_1M_MASK)
|
|
+ * otherwise set it to the default of all CCK rates and 6, 12, 24 for
|
|
+ * OFDM
|
|
+ */
|
|
+ if (priv->active_rate_basic & IWL_CCK_BASIC_RATES_MASK)
|
|
+ priv->staging_rxon.cck_basic_rates =
|
|
+ ((priv->active_rate_basic &
|
|
+ IWL_CCK_RATES_MASK) >> IWL_FIRST_CCK_RATE) & 0xF;
|
|
+ else
|
|
+ priv->staging_rxon.cck_basic_rates =
|
|
+ (IWL_CCK_BASIC_RATES_MASK >> IWL_FIRST_CCK_RATE) & 0xF;
|
|
+
|
|
+ if (priv->active_rate_basic & IWL_OFDM_BASIC_RATES_MASK)
|
|
+ priv->staging_rxon.ofdm_basic_rates =
|
|
+ ((priv->active_rate_basic &
|
|
+ (IWL_OFDM_BASIC_RATES_MASK | IWL_RATE_6M_MASK)) >>
|
|
+ IWL_FIRST_OFDM_RATE) & 0xFF;
|
|
+ else
|
|
+ priv->staging_rxon.ofdm_basic_rates =
|
|
+ (IWL_OFDM_BASIC_RATES_MASK >> IWL_FIRST_OFDM_RATE) & 0xFF;
|
|
+}
|
|
+
|
|
+static void iwl_radio_kill_sw(struct iwl_priv *priv, int disable_radio)
|
|
+{
|
|
+ unsigned long flags;
|
|
+
|
|
+ if (disable_radio ? 1 : 0 ==
|
|
+ test_bit(STATUS_RF_KILL_SW, &priv->status) ? 1 : 0)
|
|
+ return;
|
|
+
|
|
+ IWL_DEBUG_RF_KILL("Manual SW RF KILL set to: RADIO %s\n",
|
|
+ disable_radio ? "OFF" : "ON");
|
|
+
|
|
+ if (disable_radio) {
|
|
+ iwl_scan_cancel(priv);
|
|
+ /* FIXME: This is a workaround for AP */
|
|
+ if (priv->iw_mode != IEEE80211_IF_TYPE_AP) {
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
|
|
+ CSR_UCODE_SW_BIT_RFKILL);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ iwl_send_card_state(priv, CARD_STATE_CMD_DISABLE, 0);
|
|
+ set_bit(STATUS_RF_KILL_SW, &priv->status);
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
|
|
+
|
|
+ clear_bit(STATUS_RF_KILL_SW, &priv->status);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ /* wake up ucode */
|
|
+ msleep(10);
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ iwl_read32(priv, CSR_UCODE_DRV_GP1);
|
|
+ if (!iwl_grab_restricted_access(priv))
|
|
+ iwl_release_restricted_access(priv);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ if (test_bit(STATUS_RF_KILL_HW, &priv->status)) {
|
|
+ IWL_DEBUG_RF_KILL("Can not turn radio back on - "
|
|
+ "disabled by HW switch\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ queue_work(priv->workqueue, &priv->restart);
|
|
+ return;
|
|
+}
|
|
+
|
|
+void iwl_set_decrypted_flag(struct iwl_priv *priv, struct sk_buff *skb,
|
|
+ u32 decrypt_res, struct ieee80211_rx_status *stats)
|
|
+{
|
|
+ u16 fc =
|
|
+ le16_to_cpu(((struct ieee80211_hdr *)skb->data)->frame_control);
|
|
+
|
|
+ if (priv->active_rxon.filter_flags & RXON_FILTER_DIS_DECRYPT_MSK)
|
|
+ return;
|
|
+
|
|
+ if (!(fc & IEEE80211_FCTL_PROTECTED))
|
|
+ return;
|
|
+
|
|
+ IWL_DEBUG_RX("decrypt_res:0x%x\n", decrypt_res);
|
|
+ switch (decrypt_res & RX_RES_STATUS_SEC_TYPE_MSK) {
|
|
+ case RX_RES_STATUS_SEC_TYPE_TKIP:
|
|
+ if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
|
|
+ RX_RES_STATUS_BAD_ICV_MIC)
|
|
+ stats->flag |= RX_FLAG_MMIC_ERROR;
|
|
+ case RX_RES_STATUS_SEC_TYPE_WEP:
|
|
+ case RX_RES_STATUS_SEC_TYPE_CCMP:
|
|
+ if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
|
|
+ RX_RES_STATUS_DECRYPT_OK) {
|
|
+ IWL_DEBUG_RX("hw decrypt successfully!!!\n");
|
|
+ stats->flag |= RX_FLAG_DECRYPTED;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+void iwl_handle_data_packet_monitor(struct iwl_priv *priv,
|
|
+ struct iwl_rx_mem_buffer *rxb,
|
|
+ void *data, short len,
|
|
+ struct ieee80211_rx_status *stats,
|
|
+ u16 phy_flags)
|
|
+{
|
|
+ struct iwl_rt_rx_hdr *iwl_rt;
|
|
+
|
|
+ /* First cache any information we need before we overwrite
|
|
+ * the information provided in the skb from the hardware */
|
|
+ s8 signal = stats->ssi;
|
|
+ s8 noise = 0;
|
|
+ int rate = stats->rate;
|
|
+ u64 tsf = stats->mactime;
|
|
+ __le16 phy_flags_hw = cpu_to_le16(phy_flags);
|
|
+
|
|
+ /* We received data from the HW, so stop the watchdog */
|
|
+ if (len > IWL_RX_BUF_SIZE - sizeof(*iwl_rt)) {
|
|
+ IWL_DEBUG_DROP("Dropping too large packet in monitor\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* copy the frame data to write after where the radiotap header goes */
|
|
+ iwl_rt = (void *)rxb->skb->data;
|
|
+ memmove(iwl_rt->payload, data, len);
|
|
+
|
|
+ iwl_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
|
|
+ iwl_rt->rt_hdr.it_pad = 0; /* always good to zero */
|
|
+
|
|
+ /* total header + data */
|
|
+ iwl_rt->rt_hdr.it_len = cpu_to_le16(sizeof(*iwl_rt));
|
|
+
|
|
+ /* Set the size of the skb to the size of the frame */
|
|
+ skb_put(rxb->skb, sizeof(*iwl_rt) + len);
|
|
+
|
|
+ /* Big bitfield of all the fields we provide in radiotap */
|
|
+ iwl_rt->rt_hdr.it_present =
|
|
+ cpu_to_le32((1 << IEEE80211_RADIOTAP_TSFT) |
|
|
+ (1 << IEEE80211_RADIOTAP_FLAGS) |
|
|
+ (1 << IEEE80211_RADIOTAP_RATE) |
|
|
+ (1 << IEEE80211_RADIOTAP_CHANNEL) |
|
|
+ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
|
|
+ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) |
|
|
+ (1 << IEEE80211_RADIOTAP_ANTENNA));
|
|
+
|
|
+ /* Zero the flags, we'll add to them as we go */
|
|
+ iwl_rt->rt_flags = 0;
|
|
+
|
|
+ iwl_rt->rt_tsf = cpu_to_le64(tsf);
|
|
+
|
|
+ /* Convert to dBm */
|
|
+ iwl_rt->rt_dbmsignal = signal;
|
|
+ iwl_rt->rt_dbmnoise = noise;
|
|
+
|
|
+ /* Convert the channel frequency and set the flags */
|
|
+ iwl_rt->rt_channelMHz = cpu_to_le16(stats->freq);
|
|
+ if (!(phy_flags_hw & RX_RES_PHY_FLAGS_BAND_24_MSK))
|
|
+ iwl_rt->rt_chbitmask =
|
|
+ cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ));
|
|
+ else if (phy_flags_hw & RX_RES_PHY_FLAGS_MOD_CCK_MSK)
|
|
+ iwl_rt->rt_chbitmask =
|
|
+ cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ));
|
|
+ else /* 802.11g */
|
|
+ iwl_rt->rt_chbitmask =
|
|
+ cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ));
|
|
+
|
|
+ rate = iwl_rate_index_from_plcp(rate);
|
|
+ if (rate == -1)
|
|
+ iwl_rt->rt_rate = 0;
|
|
+ else
|
|
+ iwl_rt->rt_rate = iwl_rates[rate].ieee;
|
|
+
|
|
+ /* antenna number */
|
|
+ iwl_rt->rt_antenna =
|
|
+ le16_to_cpu(phy_flags_hw & RX_RES_PHY_FLAGS_ANTENNA_MSK) >> 4;
|
|
+
|
|
+ /* set the preamble flag if we have it */
|
|
+ if (phy_flags_hw & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK)
|
|
+ iwl_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
|
|
+
|
|
+ IWL_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
|
|
+
|
|
+ stats->flag |= RX_FLAG_RADIOTAP;
|
|
+ ieee80211_rx_irqsafe(priv->hw, rxb->skb, stats);
|
|
+ rxb->skb = NULL;
|
|
+}
|
|
+
|
|
+
|
|
+#define IWL_PACKET_RETRY_TIME HZ
|
|
+
|
|
+int is_duplicate_packet(struct iwl_priv *priv, struct ieee80211_hdr *header)
|
|
+{
|
|
+ u16 sc = le16_to_cpu(header->seq_ctrl);
|
|
+ u16 seq = (sc & IEEE80211_SCTL_SEQ) >> 4;
|
|
+ u16 frag = sc & IEEE80211_SCTL_FRAG;
|
|
+ u16 *last_seq, *last_frag;
|
|
+ unsigned long *last_time;
|
|
+
|
|
+ switch (priv->iw_mode) {
|
|
+ case IEEE80211_IF_TYPE_IBSS:{
|
|
+ struct list_head *p;
|
|
+ struct iwl_ibss_seq *entry = NULL;
|
|
+ u8 *mac = header->addr2;
|
|
+ int index = mac[5] & (IWL_IBSS_MAC_HASH_SIZE - 1);
|
|
+
|
|
+ __list_for_each(p, &priv->ibss_mac_hash[index]) {
|
|
+ entry =
|
|
+ list_entry(p, struct iwl_ibss_seq, list);
|
|
+ if (!compare_ether_addr(entry->mac, mac))
|
|
+ break;
|
|
+ }
|
|
+ if (p == &priv->ibss_mac_hash[index]) {
|
|
+ entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
|
|
+ if (!entry) {
|
|
+ IWL_ERROR
|
|
+ ("Cannot malloc new mac entry\n");
|
|
+ return 0;
|
|
+ }
|
|
+ memcpy(entry->mac, mac, ETH_ALEN);
|
|
+ entry->seq_num = seq;
|
|
+ entry->frag_num = frag;
|
|
+ entry->packet_time = jiffies;
|
|
+ list_add(&entry->list,
|
|
+ &priv->ibss_mac_hash[index]);
|
|
+ return 0;
|
|
+ }
|
|
+ last_seq = &entry->seq_num;
|
|
+ last_frag = &entry->frag_num;
|
|
+ last_time = &entry->packet_time;
|
|
+ break;
|
|
+ }
|
|
+ case IEEE80211_IF_TYPE_STA:
|
|
+ last_seq = &priv->last_seq_num;
|
|
+ last_frag = &priv->last_frag_num;
|
|
+ last_time = &priv->last_packet_time;
|
|
+ break;
|
|
+ default:
|
|
+ return 0;
|
|
+ }
|
|
+ if ((*last_seq == seq) &&
|
|
+ time_after(*last_time + IWL_PACKET_RETRY_TIME, jiffies)) {
|
|
+ if (*last_frag == frag)
|
|
+ goto drop;
|
|
+ if (*last_frag + 1 != frag)
|
|
+ /* out-of-order fragment */
|
|
+ goto drop;
|
|
+ } else
|
|
+ *last_seq = seq;
|
|
+
|
|
+ *last_frag = frag;
|
|
+ *last_time = jiffies;
|
|
+ return 0;
|
|
+
|
|
+ drop:
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_SPECTRUM_MEASUREMENT
|
|
+
|
|
+#include "iwl-spectrum.h"
|
|
+
|
|
+#define BEACON_TIME_MASK_LOW 0x00FFFFFF
|
|
+#define BEACON_TIME_MASK_HIGH 0xFF000000
|
|
+#define TIME_UNIT 1024
|
|
+
|
|
+/*
|
|
+ * extended beacon time format
|
|
+ * time in usec will be changed into a 32-bit value in 8:24 format
|
|
+ * the high 1 byte is the beacon counts
|
|
+ * the lower 3 bytes is the time in usec within one beacon interval
|
|
+ */
|
|
+
|
|
+static u32 iwl_usecs_to_beacons(u32 usec, u32 beacon_interval)
|
|
+{
|
|
+ u32 quot;
|
|
+ u32 rem;
|
|
+ u32 interval = beacon_interval * 1024;
|
|
+
|
|
+ if (!interval || !usec)
|
|
+ return 0;
|
|
+
|
|
+ quot = (usec / interval) & (BEACON_TIME_MASK_HIGH >> 24);
|
|
+ rem = (usec % interval) & BEACON_TIME_MASK_LOW;
|
|
+
|
|
+ return (quot << 24) + rem;
|
|
+}
|
|
+
|
|
+/* base is usually what we get from ucode with each received frame,
|
|
+ * the same as HW timer counter counting down
|
|
+ */
|
|
+
|
|
+static __le32 iwl_add_beacon_time(u32 base, u32 addon, u32 beacon_interval)
|
|
+{
|
|
+ u32 base_low = base & BEACON_TIME_MASK_LOW;
|
|
+ u32 addon_low = addon & BEACON_TIME_MASK_LOW;
|
|
+ u32 interval = beacon_interval * TIME_UNIT;
|
|
+ u32 res = (base & BEACON_TIME_MASK_HIGH) +
|
|
+ (addon & BEACON_TIME_MASK_HIGH);
|
|
+
|
|
+ if (base_low > addon_low)
|
|
+ res += base_low - addon_low;
|
|
+ else if (base_low < addon_low) {
|
|
+ res += interval + base_low - addon_low;
|
|
+ res += (1 << 24);
|
|
+ } else
|
|
+ res += (1 << 24);
|
|
+
|
|
+ return cpu_to_le32(res);
|
|
+}
|
|
+
|
|
+static int iwl_get_measurement(struct iwl_priv *priv,
|
|
+ struct ieee80211_measurement_params *params,
|
|
+ u8 type)
|
|
+{
|
|
+ struct iwl_spectrum_cmd spectrum;
|
|
+ struct iwl_rx_packet *res;
|
|
+ struct iwl_host_cmd cmd = {
|
|
+ .id = REPLY_SPECTRUM_MEASUREMENT_CMD,
|
|
+ .data = (void *)&spectrum,
|
|
+ .meta.flags = CMD_WANT_SKB,
|
|
+ };
|
|
+ u32 add_time = le64_to_cpu(params->start_time);
|
|
+ int rc;
|
|
+ int spectrum_resp_status;
|
|
+ int duration = le16_to_cpu(params->duration);
|
|
+
|
|
+ if (iwl_is_associated(priv))
|
|
+ add_time =
|
|
+ iwl_usecs_to_beacons(
|
|
+ le64_to_cpu(params->start_time) - priv->last_tsf,
|
|
+ le16_to_cpu(priv->rxon_timing.beacon_interval));
|
|
+
|
|
+ memset(&spectrum, 0, sizeof(spectrum));
|
|
+
|
|
+ spectrum.channel_count = cpu_to_le16(1);
|
|
+ spectrum.flags =
|
|
+ RXON_FLG_TSF2HOST_MSK | RXON_FLG_ANT_A_MSK | RXON_FLG_DIS_DIV_MSK;
|
|
+ spectrum.filter_flags = MEASUREMENT_FILTER_FLAG;
|
|
+ cmd.len = sizeof(spectrum);
|
|
+ spectrum.len = cpu_to_le16(cmd.len - sizeof(spectrum.len));
|
|
+
|
|
+ if (iwl_is_associated(priv))
|
|
+ spectrum.start_time =
|
|
+ iwl_add_beacon_time(priv->last_beacon_time,
|
|
+ add_time,
|
|
+ le16_to_cpu(priv->rxon_timing.beacon_interval));
|
|
+ else
|
|
+ spectrum.start_time = 0;
|
|
+
|
|
+ spectrum.channels[0].duration = cpu_to_le32(duration * TIME_UNIT);
|
|
+ spectrum.channels[0].channel = params->channel;
|
|
+ spectrum.channels[0].type = type;
|
|
+ if (priv->active_rxon.flags & RXON_FLG_BAND_24G_MSK)
|
|
+ spectrum.flags |= RXON_FLG_BAND_24G_MSK |
|
|
+ RXON_FLG_AUTO_DETECT_MSK | RXON_FLG_TGG_PROTECT_MSK;
|
|
+
|
|
+ rc = iwl_send_cmd_sync(priv, &cmd);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ res = (struct iwl_rx_packet *)cmd.meta.u.skb->data;
|
|
+ if (res->hdr.flags & IWL_CMD_FAILED_MSK) {
|
|
+ IWL_ERROR("Bad return from REPLY_RX_ON_ASSOC command\n");
|
|
+ rc = -EIO;
|
|
+ }
|
|
+
|
|
+ spectrum_resp_status = le16_to_cpu(res->u.spectrum.status);
|
|
+ switch (spectrum_resp_status) {
|
|
+ case 0: /* Command will be handled */
|
|
+ if (res->u.spectrum.id != 0xff) {
|
|
+ IWL_DEBUG_INFO
|
|
+ ("Replaced existing measurement: %d\n",
|
|
+ res->u.spectrum.id);
|
|
+ priv->measurement_status &= ~MEASUREMENT_READY;
|
|
+ }
|
|
+ priv->measurement_status |= MEASUREMENT_ACTIVE;
|
|
+ rc = 0;
|
|
+ break;
|
|
+
|
|
+ case 1: /* Command will not be handled */
|
|
+ rc = -EAGAIN;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ dev_kfree_skb_any(cmd.meta.u.skb);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+#endif
|
|
+
|
|
+static void iwl_txstatus_to_ieee(struct iwl_priv *priv,
|
|
+ struct iwl_tx_info *tx_sta)
|
|
+{
|
|
+
|
|
+ tx_sta->status.ack_signal = 0;
|
|
+ tx_sta->status.excessive_retries = 0;
|
|
+ tx_sta->status.queue_length = 0;
|
|
+ tx_sta->status.queue_number = 0;
|
|
+
|
|
+ if (in_interrupt())
|
|
+ ieee80211_tx_status_irqsafe(priv->hw,
|
|
+ tx_sta->skb[0], &(tx_sta->status));
|
|
+ else
|
|
+ ieee80211_tx_status(priv->hw,
|
|
+ tx_sta->skb[0], &(tx_sta->status));
|
|
+
|
|
+ tx_sta->skb[0] = NULL;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_tx_queue_reclaim - Reclaim Tx queue entries no more used by NIC.
|
|
+ *
|
|
+ * When FW advances 'R' index, all entries between old and
|
|
+ * new 'R' index need to be reclaimed. As result, some free space
|
|
+ * forms. If there is enough free space (> low mark), wake Tx queue.
|
|
+ */
|
|
+int iwl_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index)
|
|
+{
|
|
+ struct iwl_tx_queue *txq = &priv->txq[txq_id];
|
|
+ struct iwl_queue *q = &txq->q;
|
|
+ int nfreed = 0;
|
|
+
|
|
+ if ((index >= q->n_bd) || (x2_queue_used(q, index) == 0)) {
|
|
+ IWL_ERROR("Read index for DMA queue txq id (%d), index %d, "
|
|
+ "is out of range [0-%d] %d %d.\n", txq_id,
|
|
+ index, q->n_bd, q->first_empty, q->last_used);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ for (index = iwl_queue_inc_wrap(index, q->n_bd);
|
|
+ q->last_used != index;
|
|
+ q->last_used = iwl_queue_inc_wrap(q->last_used, q->n_bd)) {
|
|
+ if (txq_id != IWL_CMD_QUEUE_NUM) {
|
|
+ iwl_txstatus_to_ieee(priv,
|
|
+ &(txq->txb[txq->q.last_used]));
|
|
+ iwl_hw_txq_free_tfd(priv, txq);
|
|
+ } else if (nfreed > 1) {
|
|
+ IWL_ERROR("HCMD skipped: index (%d) %d %d\n", index,
|
|
+ q->first_empty, q->last_used);
|
|
+ queue_work(priv->workqueue, &priv->restart);
|
|
+ }
|
|
+ nfreed++;
|
|
+ }
|
|
+
|
|
+ if (iwl_queue_space(q) > q->low_mark && (txq_id >= 0) &&
|
|
+ (txq_id != IWL_CMD_QUEUE_NUM) &&
|
|
+ priv->mac80211_registered)
|
|
+ ieee80211_wake_queue(priv->hw, txq_id);
|
|
+
|
|
+
|
|
+ return nfreed;
|
|
+}
|
|
+
|
|
+static int iwl_is_tx_success(u32 status)
|
|
+{
|
|
+#if IWL == 3945
|
|
+ return (status & 0xFF) == 0x1;
|
|
+#elif IWL == 4965
|
|
+ status &= TX_STATUS_MSK;
|
|
+ return (status == TX_STATUS_SUCCESS)
|
|
+ || (status == TX_STATUS_DIRECT_DONE);
|
|
+#endif
|
|
+}
|
|
+
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * Generic RX handler implementations
|
|
+ *
|
|
+ ******************************************************************************/
|
|
+#if IWL == 4965
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+
|
|
+static inline int iwl_get_ra_sta_id(struct iwl_priv *priv,
|
|
+ struct ieee80211_hdr *hdr)
|
|
+{
|
|
+ if (priv->iw_mode == IEEE80211_IF_TYPE_STA)
|
|
+ return IWL_AP_ID;
|
|
+ else {
|
|
+ u8 *da = ieee80211_get_DA(hdr);
|
|
+ return iwl_hw_find_station(priv, da);
|
|
+ }
|
|
+}
|
|
+
|
|
+static struct ieee80211_hdr *iwl_tx_queue_get_hdr(
|
|
+ struct iwl_priv *priv, int txq_id, int idx)
|
|
+{
|
|
+ if (priv->txq[txq_id].txb[idx].skb[0])
|
|
+ return (struct ieee80211_hdr *)priv->txq[txq_id].
|
|
+ txb[idx].skb[0]->data;
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static inline u32 iwl_get_scd_ssn(struct iwl_tx_resp *tx_resp)
|
|
+{
|
|
+ __le32 *scd_ssn = (__le32 *)((u32 *)&tx_resp->status +
|
|
+ tx_resp->frame_count);
|
|
+ return le32_to_cpu(*scd_ssn) & MAX_SN;
|
|
+
|
|
+}
|
|
+static int iwl4965_tx_status_reply_tx(struct iwl_priv *priv,
|
|
+ struct iwl_ht_agg *agg,
|
|
+ struct iwl_tx_resp *tx_resp,
|
|
+ u16 start_idx)
|
|
+{
|
|
+ u32 status;
|
|
+ __le32 *frame_status = &tx_resp->status;
|
|
+ struct ieee80211_tx_status *tx_status = NULL;
|
|
+ struct ieee80211_hdr *hdr = NULL;
|
|
+ int i, sh;
|
|
+ int txq_id, idx;
|
|
+ u16 seq;
|
|
+
|
|
+ if (agg->wait_for_ba)
|
|
+ IWL_DEBUG_TX_REPLY("got tx repsons w/o back\n");
|
|
+
|
|
+ agg->frame_count = tx_resp->frame_count;
|
|
+ agg->start_idx = start_idx;
|
|
+ agg->rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags);
|
|
+ agg->bitmap0 = agg->bitmap1 = 0;
|
|
+
|
|
+ if (agg->frame_count == 1) {
|
|
+ struct iwl_tx_queue *txq ;
|
|
+ status = le32_to_cpu(frame_status[0]);
|
|
+
|
|
+ txq_id = agg->txq_id;
|
|
+ txq = &priv->txq[txq_id];
|
|
+ /* FIXME: code repetition */
|
|
+ IWL_DEBUG_TX_REPLY("FrameCnt = %d, StartIdx=%d \n",
|
|
+ agg->frame_count, agg->start_idx);
|
|
+
|
|
+ tx_status = &(priv->txq[txq_id].txb[txq->q.last_used].status);
|
|
+ tx_status->retry_count = tx_resp->failure_frame;
|
|
+ tx_status->queue_number = status & 0xff;
|
|
+ tx_status->queue_length = tx_resp->bt_kill_count;
|
|
+ tx_status->queue_length |= tx_resp->failure_rts;
|
|
+
|
|
+ tx_status->flags = iwl_is_tx_success(status)?
|
|
+ IEEE80211_TX_STATUS_ACK : 0;
|
|
+ tx_status->control.tx_rate =
|
|
+ iwl_hw_get_rate_n_flags(tx_resp->rate_n_flags);
|
|
+ /* FIXME: code repetition end */
|
|
+
|
|
+ IWL_DEBUG_TX_REPLY("1 Frame 0x%x failure :%d\n",
|
|
+ status & 0xff, tx_resp->failure_frame);
|
|
+ IWL_DEBUG_TX_REPLY("Rate Info rate_n_flags=%x\n",
|
|
+ iwl_hw_get_rate_n_flags(tx_resp->rate_n_flags));
|
|
+
|
|
+ agg->wait_for_ba = 0;
|
|
+ } else {
|
|
+ u64 bitmap = 0;
|
|
+ int start = agg->start_idx;
|
|
+
|
|
+ for (i = 0; i < agg->frame_count; i++) {
|
|
+ u16 sc;
|
|
+ status = le32_to_cpu(frame_status[i]);
|
|
+ seq = status >> 16;
|
|
+ idx = SEQ_TO_INDEX(seq);
|
|
+ txq_id = SEQ_TO_QUEUE(seq);
|
|
+
|
|
+ if (status & (AGG_TX_STATE_FEW_BYTES_MSK |
|
|
+ AGG_TX_STATE_ABORT_MSK))
|
|
+ continue;
|
|
+
|
|
+ IWL_DEBUG_TX_REPLY("FrameCnt = %d, txq_id=%d idx=%d\n",
|
|
+ agg->frame_count, txq_id, idx);
|
|
+
|
|
+ hdr = iwl_tx_queue_get_hdr(priv, txq_id, idx);
|
|
+
|
|
+ sc = le16_to_cpu(hdr->seq_ctrl);
|
|
+ if (idx != (SEQ_TO_SN(sc) & 0xff)) {
|
|
+ IWL_ERROR("BUG_ON idx doesn't match seq control"
|
|
+ " idx=%d, seq_idx=%d, seq=%d\n",
|
|
+ idx, SEQ_TO_SN(sc),
|
|
+ hdr->seq_ctrl);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_TX_REPLY("AGG Frame i=%d idx %d seq=%d\n",
|
|
+ i, idx, SEQ_TO_SN(sc));
|
|
+
|
|
+ sh = idx - start;
|
|
+ if (sh > 64) {
|
|
+ sh = (start - idx) + 0xff;
|
|
+ bitmap = bitmap << sh;
|
|
+ sh = 0;
|
|
+ start = idx;
|
|
+ } else if (sh < -64)
|
|
+ sh = 0xff - (start - idx);
|
|
+ else if (sh < 0) {
|
|
+ sh = start - idx;
|
|
+ start = idx;
|
|
+ bitmap = bitmap << sh;
|
|
+ sh = 0;
|
|
+ }
|
|
+ bitmap |= (1 << sh);
|
|
+ IWL_DEBUG_TX_REPLY("start=%d bitmap=0x%x\n",
|
|
+ start, (u32)(bitmap & 0xFFFFFFFF));
|
|
+ }
|
|
+
|
|
+ agg->bitmap0 = bitmap & 0xFFFFFFFF;
|
|
+ agg->bitmap1 = bitmap >> 32;
|
|
+ agg->start_idx = start;
|
|
+ agg->rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags);
|
|
+ IWL_DEBUG_TX_REPLY("Frames %d start_idx=%d bitmap=0x%x\n",
|
|
+ agg->frame_count, agg->start_idx,
|
|
+ agg->bitmap0);
|
|
+
|
|
+ if (bitmap)
|
|
+ agg->wait_for_ba = 1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+#endif
|
|
+#endif
|
|
+#endif
|
|
+
|
|
+static void iwl_rx_reply_tx(struct iwl_priv *priv,
|
|
+ struct iwl_rx_mem_buffer *rxb)
|
|
+{
|
|
+ struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
|
|
+ u16 sequence = le16_to_cpu(pkt->hdr.sequence);
|
|
+ int txq_id = SEQ_TO_QUEUE(sequence);
|
|
+ int index = SEQ_TO_INDEX(sequence);
|
|
+ struct iwl_tx_queue *txq = &priv->txq[txq_id];
|
|
+ struct ieee80211_tx_status *tx_status;
|
|
+ struct iwl_tx_resp *tx_resp = (void *)&pkt->u.raw[0];
|
|
+ u32 status = le32_to_cpu(tx_resp->status);
|
|
+#if IWL == 4965
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+ int tid, sta_id;
|
|
+#endif
|
|
+#endif
|
|
+#endif
|
|
+
|
|
+ if ((index >= txq->q.n_bd) || (x2_queue_used(&txq->q, index) == 0)) {
|
|
+ IWL_ERROR("Read index for DMA queue txq_id (%d) index %d "
|
|
+ "is out of range [0-%d] %d %d\n", txq_id,
|
|
+ index, txq->q.n_bd, txq->q.first_empty,
|
|
+ txq->q.last_used);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+#if IWL == 4965
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+ if (txq->sched_retry) {
|
|
+ const u32 scd_ssn = iwl_get_scd_ssn(tx_resp);
|
|
+ struct ieee80211_hdr *hdr =
|
|
+ iwl_tx_queue_get_hdr(priv, txq_id, index);
|
|
+ struct iwl_ht_agg *agg = NULL;
|
|
+ __le16 *qc = ieee80211_get_qos_ctrl(hdr);
|
|
+
|
|
+ if (qc == NULL) {
|
|
+ IWL_ERROR("BUG_ON qc is null!!!!\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ tid = le16_to_cpu(*qc) & 0xf;
|
|
+
|
|
+ sta_id = iwl_get_ra_sta_id(priv, hdr);
|
|
+ if (unlikely(sta_id == IWL_INVALID_STATION)) {
|
|
+ IWL_ERROR("Station not known for\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ agg = &priv->stations[sta_id].tid[tid].agg;
|
|
+
|
|
+ iwl4965_tx_status_reply_tx(priv, agg, tx_resp, index);
|
|
+
|
|
+ if ((tx_resp->frame_count == 1) &&
|
|
+ !iwl_is_tx_success(status)) {
|
|
+ /* TODO: send BAR */
|
|
+ }
|
|
+
|
|
+ if ((txq->q.last_used != (scd_ssn & 0xff))) {
|
|
+ index = iwl_queue_dec_wrap(scd_ssn & 0xff, txq->q.n_bd);
|
|
+ IWL_DEBUG_TX_REPLY("Retry scheduler reclaim scd_ssn "
|
|
+ "%d index %d\n", scd_ssn , index);
|
|
+ iwl_tx_queue_reclaim(priv, txq_id, index);
|
|
+ }
|
|
+ } else {
|
|
+#endif /* CONFIG_IWLWIFI_HT_AGG */
|
|
+#endif /* CONFIG_IWLWIFI_HT */
|
|
+#endif /* 4965 */
|
|
+ tx_status = &(txq->txb[txq->q.last_used].status);
|
|
+
|
|
+ tx_status->retry_count = tx_resp->failure_frame;
|
|
+ tx_status->queue_number = status;
|
|
+ tx_status->queue_length = tx_resp->bt_kill_count;
|
|
+ tx_status->queue_length |= tx_resp->failure_rts;
|
|
+
|
|
+ tx_status->flags =
|
|
+ iwl_is_tx_success(status) ? IEEE80211_TX_STATUS_ACK : 0;
|
|
+
|
|
+#if IWL == 3945
|
|
+ tx_status->control.tx_rate = iwl_rate_index_from_plcp(tx_resp->rate);
|
|
+
|
|
+ IWL_DEBUG_TX("Tx queue %d Status %s (0x%08x) plcp rate %d retries %d\n",
|
|
+ txq_id, iwl_get_tx_fail_reason(status), status,
|
|
+ tx_resp->rate, tx_resp->failure_frame);
|
|
+#elif IWL == 4965
|
|
+ tx_status->control.tx_rate =
|
|
+ iwl_hw_get_rate_n_flags(tx_resp->rate_n_flags);
|
|
+
|
|
+ IWL_DEBUG_TX("Tx queue %d Status %s (0x%08x) rate_n_flags 0x%x "
|
|
+ "retries %d\n", txq_id, iwl_get_tx_fail_reason(status),
|
|
+ status, le32_to_cpu(tx_resp->rate_n_flags),
|
|
+ tx_resp->failure_frame);
|
|
+#endif
|
|
+
|
|
+ IWL_DEBUG_TX_REPLY("Tx queue reclaim %d\n", index);
|
|
+ if (index != -1)
|
|
+ iwl_tx_queue_reclaim(priv, txq_id, index);
|
|
+#if IWL == 4965
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+ }
|
|
+#endif /* CONFIG_IWLWIFI_HT_AGG */
|
|
+#endif /* CONFIG_IWLWIFI_HT */
|
|
+#endif /* 4965 */
|
|
+
|
|
+ if (iwl_check_bits(status, TX_ABORT_REQUIRED_MSK))
|
|
+ IWL_ERROR("TODO: Implement Tx ABORT REQUIRED!!!\n");
|
|
+}
|
|
+
|
|
+
|
|
+static void iwl_rx_reply_alive(struct iwl_priv *priv,
|
|
+ struct iwl_rx_mem_buffer *rxb)
|
|
+{
|
|
+ struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
|
|
+ struct iwl_alive_resp *palive;
|
|
+ struct delayed_work *pwork;
|
|
+
|
|
+ palive = &pkt->u.alive_frame;
|
|
+
|
|
+ IWL_DEBUG_INFO("Alive ucode status 0x%08X revision "
|
|
+ "0x%01X 0x%01X\n",
|
|
+ palive->is_valid, palive->ver_type,
|
|
+ palive->ver_subtype);
|
|
+
|
|
+ if (palive->ver_subtype == INITIALIZE_SUBTYPE) {
|
|
+ IWL_DEBUG_INFO("Initialization Alive received.\n");
|
|
+ memcpy(&priv->card_alive_init,
|
|
+ &pkt->u.alive_frame,
|
|
+ sizeof(struct iwl_init_alive_resp));
|
|
+ pwork = &priv->init_alive_start;
|
|
+ } else {
|
|
+ IWL_DEBUG_INFO("Runtime Alive received.\n");
|
|
+ memcpy(&priv->card_alive, &pkt->u.alive_frame,
|
|
+ sizeof(struct iwl_alive_resp));
|
|
+ pwork = &priv->alive_start;
|
|
+#if IWL == 3945
|
|
+ /* For debugging (selective disable not supported in 4965) */
|
|
+ iwl_disable_events(priv);
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ /* We delay the ALIVE response by 5ms to
|
|
+ * give the HW RF Kill time to activate... */
|
|
+ if (palive->is_valid == UCODE_VALID_OK)
|
|
+ queue_delayed_work(priv->workqueue, pwork,
|
|
+ msecs_to_jiffies(5));
|
|
+ else
|
|
+ IWL_WARNING("uCode did not respond OK.\n");
|
|
+}
|
|
+
|
|
+static void iwl_rx_reply_add_sta(struct iwl_priv *priv,
|
|
+ struct iwl_rx_mem_buffer *rxb)
|
|
+{
|
|
+ struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
|
|
+ IWL_DEBUG_RX("Received REPLY_ADD_STA: 0x%02X\n", pkt->u.status);
|
|
+ return;
|
|
+}
|
|
+
|
|
+static void iwl_rx_reply_error(struct iwl_priv *priv,
|
|
+ struct iwl_rx_mem_buffer *rxb)
|
|
+{
|
|
+ struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
|
|
+
|
|
+ IWL_ERROR("Error Reply type 0x%08X cmd %s (0x%02X) "
|
|
+ "seq 0x%04X ser 0x%08X\n",
|
|
+ le32_to_cpu(pkt->u.err_resp.error_type),
|
|
+ get_cmd_string(pkt->u.err_resp.cmd_id),
|
|
+ pkt->u.err_resp.cmd_id,
|
|
+ le16_to_cpu(pkt->u.err_resp.bad_cmd_seq_num),
|
|
+ le32_to_cpu(pkt->u.err_resp.error_info));
|
|
+}
|
|
+
|
|
+#define TX_STATUS_ENTRY(x) case TX_STATUS_FAIL_ ## x: return #x
|
|
+
|
|
+static void iwl_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
|
|
+{
|
|
+ struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
|
|
+ struct iwl_rxon_cmd *rxon = (void *)&priv->active_rxon;
|
|
+ struct iwl_csa_notification *csa = &(pkt->u.csa_notif);
|
|
+ IWL_DEBUG_11H("CSA notif: channel %d, status %d\n",
|
|
+ le16_to_cpu(csa->channel), le32_to_cpu(csa->status));
|
|
+ rxon->channel = csa->channel;
|
|
+ priv->staging_rxon.channel = csa->channel;
|
|
+}
|
|
+
|
|
+static void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv,
|
|
+ struct iwl_rx_mem_buffer *rxb)
|
|
+{
|
|
+#ifdef CONFIG_IWLWIFI_SPECTRUM_MEASUREMENT
|
|
+ struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
|
|
+ struct iwl_spectrum_notification *report = &(pkt->u.spectrum_notif);
|
|
+
|
|
+ if (!report->state) {
|
|
+ IWL_DEBUG(IWL_DL_11H | IWL_DL_INFO,
|
|
+ "Spectrum Measure Notification: Start\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ memcpy(&priv->measure_report, report, sizeof(*report));
|
|
+ priv->measurement_status |= MEASUREMENT_READY;
|
|
+#endif
|
|
+}
|
|
+
|
|
+static void iwl_rx_pm_sleep_notif(struct iwl_priv *priv,
|
|
+ struct iwl_rx_mem_buffer *rxb)
|
|
+{
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+ struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
|
|
+ struct iwl_sleep_notification *sleep = &(pkt->u.sleep_notif);
|
|
+ IWL_DEBUG_RX("sleep mode: %d, src: %d\n",
|
|
+ sleep->pm_sleep_mode, sleep->pm_wakeup_src);
|
|
+#endif
|
|
+}
|
|
+
|
|
+static void iwl_rx_pm_debug_statistics_notif(struct iwl_priv *priv,
|
|
+ struct iwl_rx_mem_buffer *rxb)
|
|
+{
|
|
+ struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
|
|
+ IWL_DEBUG_RADIO("Dumping %d bytes of unhandled "
|
|
+ "notification for %s:\n",
|
|
+ le32_to_cpu(pkt->len), get_cmd_string(pkt->hdr.cmd));
|
|
+ iwl_print_hex_dump(IWL_DL_RADIO, pkt->u.raw, le32_to_cpu(pkt->len));
|
|
+}
|
|
+
|
|
+static void iwl_bg_beacon_update(struct work_struct *work)
|
|
+{
|
|
+ struct iwl_priv *priv =
|
|
+ container_of(work, struct iwl_priv, beacon_update);
|
|
+ struct sk_buff *beacon;
|
|
+
|
|
+ /* Pull updated AP beacon from mac80211. will fail if not in AP mode */
|
|
+ beacon = ieee80211_beacon_get(priv->hw, priv->interface_id, NULL);
|
|
+
|
|
+ if (!beacon) {
|
|
+ IWL_ERROR("update beacon failed\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+ /* new beacon skb is allocated every time; dispose previous.*/
|
|
+ if (priv->ibss_beacon)
|
|
+ dev_kfree_skb(priv->ibss_beacon);
|
|
+
|
|
+ priv->ibss_beacon = beacon;
|
|
+ mutex_unlock(&priv->mutex);
|
|
+
|
|
+ iwl_send_beacon_cmd(priv);
|
|
+}
|
|
+
|
|
+static void iwl_rx_beacon_notif(struct iwl_priv *priv,
|
|
+ struct iwl_rx_mem_buffer *rxb)
|
|
+{
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+ struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
|
|
+ struct iwl_beacon_notif *beacon = &(pkt->u.beacon_status);
|
|
+#if IWL == 3945
|
|
+ u8 rate = beacon->beacon_notify_hdr.rate;
|
|
+#elif IWL == 4965
|
|
+ u8 rate = iwl_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags);
|
|
+#endif
|
|
+ IWL_DEBUG_RX("beacon status %x retries %d iss %d "
|
|
+ "tsf %d %d rate %d\n",
|
|
+ le32_to_cpu(beacon->beacon_notify_hdr.status) & TX_STATUS_MSK,
|
|
+ beacon->beacon_notify_hdr.failure_frame,
|
|
+ le32_to_cpu(beacon->ibss_mgr_status),
|
|
+ le32_to_cpu(beacon->high_tsf),
|
|
+ le32_to_cpu(beacon->low_tsf), rate);
|
|
+#endif
|
|
+
|
|
+ if ((priv->iw_mode == IEEE80211_IF_TYPE_AP) &&
|
|
+ (!test_bit(STATUS_EXIT_PENDING, &priv->status)))
|
|
+ queue_work(priv->workqueue, &priv->beacon_update);
|
|
+}
|
|
+
|
|
+/* Service response to REPLY_SCAN_CMD (0x80) */
|
|
+static void iwl_rx_reply_scan(struct iwl_priv *priv,
|
|
+ struct iwl_rx_mem_buffer *rxb)
|
|
+{
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+ struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
|
|
+ struct iwl_scanreq_notification *notif =
|
|
+ (struct iwl_scanreq_notification *)pkt->u.raw;
|
|
+
|
|
+ IWL_DEBUG_RX("Scan request status = 0x%x\n", notif->status);
|
|
+#endif
|
|
+}
|
|
+
|
|
+/* Service SCAN_START_NOTIFICATION (0x82) */
|
|
+static void iwl_rx_scan_start_notif(struct iwl_priv *priv,
|
|
+ struct iwl_rx_mem_buffer *rxb)
|
|
+{
|
|
+ struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
|
|
+ struct iwl_scanstart_notification *notif =
|
|
+ (struct iwl_scanstart_notification *)pkt->u.raw;
|
|
+ priv->scan_start_tsf = le32_to_cpu(notif->tsf_low);
|
|
+ IWL_DEBUG_SCAN("Scan start: "
|
|
+ "%d [802.11%s] "
|
|
+ "(TSF: 0x%08X:%08X) - %d (beacon timer %u)\n",
|
|
+ notif->channel,
|
|
+ notif->band ? "bg" : "a",
|
|
+ notif->tsf_high,
|
|
+ notif->tsf_low, notif->status, notif->beacon_timer);
|
|
+}
|
|
+
|
|
+/* Service SCAN_RESULTS_NOTIFICATION (0x83) */
|
|
+static void iwl_rx_scan_results_notif(struct iwl_priv *priv,
|
|
+ struct iwl_rx_mem_buffer *rxb)
|
|
+{
|
|
+ struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
|
|
+ struct iwl_scanresults_notification *notif =
|
|
+ (struct iwl_scanresults_notification *)pkt->u.raw;
|
|
+
|
|
+ IWL_DEBUG_SCAN("Scan ch.res: "
|
|
+ "%d [802.11%s] "
|
|
+ "(TSF: 0x%08X:%08X) - %d "
|
|
+ "elapsed=%lu usec (%dms since last)\n",
|
|
+ notif->channel,
|
|
+ notif->band ? "bg" : "a",
|
|
+ le32_to_cpu(notif->tsf_high),
|
|
+ le32_to_cpu(notif->tsf_low),
|
|
+ le32_to_cpu(notif->statistics[0]),
|
|
+ le32_to_cpu(notif->tsf_low) - priv->scan_start_tsf,
|
|
+ jiffies_to_msecs(elapsed_jiffies
|
|
+ (priv->last_scan_jiffies, jiffies)));
|
|
+
|
|
+ priv->last_scan_jiffies = jiffies;
|
|
+}
|
|
+
|
|
+/* Service SCAN_COMPLETE_NOTIFICATION (0x84) */
|
|
+static void iwl_rx_scan_complete_notif(struct iwl_priv *priv,
|
|
+ struct iwl_rx_mem_buffer *rxb)
|
|
+{
|
|
+ struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
|
|
+ struct iwl_scancomplete_notification *scan_notif = (void *)pkt->u.raw;
|
|
+
|
|
+ IWL_DEBUG_SCAN("Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n",
|
|
+ scan_notif->scanned_channels,
|
|
+ scan_notif->tsf_low,
|
|
+ scan_notif->tsf_high, scan_notif->status);
|
|
+
|
|
+ /* The HW is no longer scanning */
|
|
+ clear_bit(STATUS_SCAN_HW, &priv->status);
|
|
+
|
|
+ /* The scan completion notification came in, so kill that timer... */
|
|
+ cancel_delayed_work(&priv->scan_check);
|
|
+
|
|
+ IWL_DEBUG_INFO("Scan pass on %sGHz took %dms\n",
|
|
+ (priv->scan_bands == 2) ? "2.4" : "5.2",
|
|
+ jiffies_to_msecs(elapsed_jiffies
|
|
+ (priv->scan_pass_start, jiffies)));
|
|
+
|
|
+ /* Remove this scanned band from the list
|
|
+ * of pending bands to scan */
|
|
+ priv->scan_bands--;
|
|
+
|
|
+ /* If a request to abort was given, or the scan did not succeed
|
|
+ * then we reset the scan state machine and terminate,
|
|
+ * re-queuing another scan if one has been requested */
|
|
+ if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
|
|
+ IWL_DEBUG_INFO("Aborted scan completed.\n");
|
|
+ clear_bit(STATUS_SCAN_ABORTING, &priv->status);
|
|
+ } else {
|
|
+ /* If there are more bands on this scan pass reschedule */
|
|
+ if (priv->scan_bands > 0)
|
|
+ goto reschedule;
|
|
+ }
|
|
+
|
|
+ priv->last_scan_jiffies = jiffies;
|
|
+ IWL_DEBUG_INFO("Setting scan to off\n");
|
|
+
|
|
+ clear_bit(STATUS_SCANNING, &priv->status);
|
|
+
|
|
+ IWL_DEBUG_INFO("Scan took %dms\n",
|
|
+ jiffies_to_msecs(elapsed_jiffies(priv->scan_start, jiffies)));
|
|
+
|
|
+ queue_work(priv->workqueue, &priv->scan_completed);
|
|
+
|
|
+ return;
|
|
+
|
|
+reschedule:
|
|
+ priv->scan_pass_start = jiffies;
|
|
+ queue_work(priv->workqueue, &priv->request_scan);
|
|
+}
|
|
+
|
|
+/* Handle notification from uCode that card's power state is changing
|
|
+ * due to software, hardware, or critical temperature RFKILL */
|
|
+static void iwl_rx_card_state_notif(struct iwl_priv *priv,
|
|
+ struct iwl_rx_mem_buffer *rxb)
|
|
+{
|
|
+ struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
|
|
+ u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags);
|
|
+ unsigned long status = priv->status;
|
|
+ IWL_DEBUG_RF_KILL("Card state received: HW:%s SW:%s\n",
|
|
+ (flags & HW_CARD_DISABLED) ? "Kill" : "On",
|
|
+ (flags & SW_CARD_DISABLED) ? "Kill" : "On");
|
|
+#if IWL == 4965
|
|
+ if (flags & (SW_CARD_DISABLED | HW_CARD_DISABLED |
|
|
+ RF_CARD_DISABLED)) {
|
|
+
|
|
+ iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
|
|
+ CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
|
|
+
|
|
+ if (!iwl_grab_restricted_access(priv)) {
|
|
+ iwl_write_restricted(
|
|
+ priv, HBUS_TARG_MBX_C,
|
|
+ HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
|
|
+
|
|
+ iwl_release_restricted_access(priv);
|
|
+ }
|
|
+
|
|
+ if (!(flags & RXON_CARD_DISABLED)) {
|
|
+ iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR,
|
|
+ CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
|
|
+ if (!iwl_grab_restricted_access(priv)) {
|
|
+ iwl_write_restricted(
|
|
+ priv, HBUS_TARG_MBX_C,
|
|
+ HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
|
|
+
|
|
+ iwl_release_restricted_access(priv);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (flags & RF_CARD_DISABLED) {
|
|
+ iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
|
|
+ CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
|
|
+ iwl_read32(priv, CSR_UCODE_DRV_GP1);
|
|
+ if (!iwl_grab_restricted_access(priv))
|
|
+ iwl_release_restricted_access(priv);
|
|
+ }
|
|
+ }
|
|
+#else
|
|
+ iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
|
|
+ CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
|
|
+#endif
|
|
+ if (flags & HW_CARD_DISABLED)
|
|
+ set_bit(STATUS_RF_KILL_HW, &priv->status);
|
|
+ else
|
|
+ clear_bit(STATUS_RF_KILL_HW, &priv->status);
|
|
+
|
|
+
|
|
+ if (flags & SW_CARD_DISABLED)
|
|
+ set_bit(STATUS_RF_KILL_SW, &priv->status);
|
|
+ else
|
|
+ clear_bit(STATUS_RF_KILL_SW, &priv->status);
|
|
+
|
|
+#if IWL == 4965
|
|
+ if (!(flags & RXON_CARD_DISABLED))
|
|
+#endif
|
|
+ iwl_scan_cancel(priv);
|
|
+
|
|
+ if ((test_bit(STATUS_RF_KILL_HW, &status) !=
|
|
+ test_bit(STATUS_RF_KILL_HW, &priv->status)) ||
|
|
+ (test_bit(STATUS_RF_KILL_SW, &status) !=
|
|
+ test_bit(STATUS_RF_KILL_SW, &priv->status)))
|
|
+ queue_work(priv->workqueue, &priv->rf_kill);
|
|
+ else
|
|
+ wake_up_interruptible(&priv->wait_command_queue);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_setup_rx_handlers - Initialize Rx handler callbacks
|
|
+ *
|
|
+ * Setup the RX handlers for each of the reply types sent from the uCode
|
|
+ * to the host.
|
|
+ *
|
|
+ * This function chains into the hardware specific files for them to setup
|
|
+ * any hardware specific handlers as well.
|
|
+ */
|
|
+static void iwl_setup_rx_handlers(struct iwl_priv *priv)
|
|
+{
|
|
+ priv->rx_handlers[REPLY_ALIVE] = iwl_rx_reply_alive;
|
|
+ priv->rx_handlers[REPLY_ADD_STA] = iwl_rx_reply_add_sta;
|
|
+ priv->rx_handlers[REPLY_ERROR] = iwl_rx_reply_error;
|
|
+ priv->rx_handlers[CHANNEL_SWITCH_NOTIFICATION] = iwl_rx_csa;
|
|
+ priv->rx_handlers[SPECTRUM_MEASURE_NOTIFICATION] =
|
|
+ iwl_rx_spectrum_measure_notif;
|
|
+ priv->rx_handlers[PM_SLEEP_NOTIFICATION] = iwl_rx_pm_sleep_notif;
|
|
+ priv->rx_handlers[PM_DEBUG_STATISTIC_NOTIFIC] =
|
|
+ iwl_rx_pm_debug_statistics_notif;
|
|
+ priv->rx_handlers[BEACON_NOTIFICATION] = iwl_rx_beacon_notif;
|
|
+
|
|
+ /* NOTE: iwl_rx_statistics is different based on whether
|
|
+ * the build is for the 3945 or the 4965. See the
|
|
+ * corresponding implementation in iwl-XXXX.c
|
|
+ *
|
|
+ * The same handler is used for both the REPLY to a
|
|
+ * discrete statistics request from the host as well as
|
|
+ * for the periodic statistics notification from the uCode
|
|
+ */
|
|
+ priv->rx_handlers[REPLY_STATISTICS_CMD] = iwl_hw_rx_statistics;
|
|
+ priv->rx_handlers[STATISTICS_NOTIFICATION] = iwl_hw_rx_statistics;
|
|
+
|
|
+ priv->rx_handlers[REPLY_SCAN_CMD] = iwl_rx_reply_scan;
|
|
+ priv->rx_handlers[SCAN_START_NOTIFICATION] = iwl_rx_scan_start_notif;
|
|
+ priv->rx_handlers[SCAN_RESULTS_NOTIFICATION] =
|
|
+ iwl_rx_scan_results_notif;
|
|
+ priv->rx_handlers[SCAN_COMPLETE_NOTIFICATION] =
|
|
+ iwl_rx_scan_complete_notif;
|
|
+ priv->rx_handlers[CARD_STATE_NOTIFICATION] = iwl_rx_card_state_notif;
|
|
+ priv->rx_handlers[REPLY_TX] = iwl_rx_reply_tx;
|
|
+
|
|
+ /* Setup hardware specific Rx handlers */
|
|
+ iwl_hw_rx_handler_setup(priv);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_tx_cmd_complete - Pull unused buffers off the queue and reclaim them
|
|
+ * @rxb: Rx buffer to reclaim
|
|
+ *
|
|
+ * If an Rx buffer has an async callback associated with it the callback
|
|
+ * will be executed. The attached skb (if present) will only be freed
|
|
+ * if the callback returns 1
|
|
+ */
|
|
+static void iwl_tx_cmd_complete(struct iwl_priv *priv,
|
|
+ struct iwl_rx_mem_buffer *rxb)
|
|
+{
|
|
+ struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
|
|
+ u16 sequence = le16_to_cpu(pkt->hdr.sequence);
|
|
+ int txq_id = SEQ_TO_QUEUE(sequence);
|
|
+ int index = SEQ_TO_INDEX(sequence);
|
|
+ int huge = sequence & SEQ_HUGE_FRAME;
|
|
+ int cmd_index;
|
|
+ struct iwl_cmd *cmd;
|
|
+
|
|
+ /* If a Tx command is being handled and it isn't in the actual
|
|
+ * command queue then there a command routing bug has been introduced
|
|
+ * in the queue management code. */
|
|
+ if (txq_id != IWL_CMD_QUEUE_NUM)
|
|
+ IWL_ERROR("Error wrong command queue %d command id 0x%X\n",
|
|
+ txq_id, pkt->hdr.cmd);
|
|
+ BUG_ON(txq_id != IWL_CMD_QUEUE_NUM);
|
|
+
|
|
+ cmd_index = get_cmd_index(&priv->txq[IWL_CMD_QUEUE_NUM].q, index, huge);
|
|
+ cmd = &priv->txq[IWL_CMD_QUEUE_NUM].cmd[cmd_index];
|
|
+
|
|
+ /* Input error checking is done when commands are added to queue. */
|
|
+ if (cmd->meta.flags & CMD_WANT_SKB) {
|
|
+ cmd->meta.source->u.skb = rxb->skb;
|
|
+ rxb->skb = NULL;
|
|
+ } else if (cmd->meta.u.callback &&
|
|
+ !cmd->meta.u.callback(priv, cmd, rxb->skb))
|
|
+ rxb->skb = NULL;
|
|
+
|
|
+ iwl_tx_queue_reclaim(priv, txq_id, index);
|
|
+
|
|
+ if (!(cmd->meta.flags & CMD_ASYNC)) {
|
|
+ clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
|
|
+ wake_up_interruptible(&priv->wait_command_queue);
|
|
+ }
|
|
+}
|
|
+
|
|
+/************************** RX-FUNCTIONS ****************************/
|
|
+/*
|
|
+ * Rx theory of operation
|
|
+ *
|
|
+ * The host allocates 32 DMA target addresses and passes the host address
|
|
+ * to the firmware at register IWL_RFDS_TABLE_LOWER + N * RFD_SIZE where N is
|
|
+ * 0 to 31
|
|
+ *
|
|
+ * Rx Queue Indexes
|
|
+ * The host/firmware share two index registers for managing the Rx buffers.
|
|
+ *
|
|
+ * The READ index maps to the first position that the firmware may be writing
|
|
+ * to -- the driver can read up to (but not including) this position and get
|
|
+ * good data.
|
|
+ * The READ index is managed by the firmware once the card is enabled.
|
|
+ *
|
|
+ * The WRITE index maps to the last position the driver has read from -- the
|
|
+ * position preceding WRITE is the last slot the firmware can place a packet.
|
|
+ *
|
|
+ * The queue is empty (no good data) if WRITE = READ - 1, and is full if
|
|
+ * WRITE = READ.
|
|
+ *
|
|
+ * During initialization the host sets up the READ queue position to the first
|
|
+ * INDEX position, and WRITE to the last (READ - 1 wrapped)
|
|
+ *
|
|
+ * When the firmware places a packet in a buffer it will advance the READ index
|
|
+ * and fire the RX interrupt. The driver can then query the READ index and
|
|
+ * process as many packets as possible, moving the WRITE index forward as it
|
|
+ * resets the Rx queue buffers with new memory.
|
|
+ *
|
|
+ * The management in the driver is as follows:
|
|
+ * + A list of pre-allocated SKBs is stored in ipw->rxq->rx_free. When
|
|
+ * ipw->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled
|
|
+ * to replensish the ipw->rxq->rx_free.
|
|
+ * + In iwl_rx_replenish (scheduled) if 'processed' != 'read' then the
|
|
+ * ipw->rxq is replenished and the READ INDEX is updated (updating the
|
|
+ * 'processed' and 'read' driver indexes as well)
|
|
+ * + A received packet is processed and handed to the kernel network stack,
|
|
+ * detached from the ipw->rxq. The driver 'processed' index is updated.
|
|
+ * + The Host/Firmware ipw->rxq is replenished at tasklet time from the rx_free
|
|
+ * list. If there are no allocated buffers in ipw->rxq->rx_free, the READ
|
|
+ * INDEX is not incremented and ipw->status(RX_STALLED) is set. If there
|
|
+ * were enough free buffers and RX_STALLED is set it is cleared.
|
|
+ *
|
|
+ *
|
|
+ * Driver sequence:
|
|
+ *
|
|
+ * iwl_rx_queue_alloc() Allocates rx_free
|
|
+ * iwl_rx_replenish() Replenishes rx_free list from rx_used, and calls
|
|
+ * iwl_rx_queue_restock
|
|
+ * iwl_rx_queue_restock() Moves available buffers from rx_free into Rx
|
|
+ * queue, updates firmware pointers, and updates
|
|
+ * the WRITE index. If insufficient rx_free buffers
|
|
+ * are available, schedules iwl_rx_replenish
|
|
+ *
|
|
+ * -- enable interrupts --
|
|
+ * ISR - iwl_rx() Detach iwl_rx_mem_buffers from pool up to the
|
|
+ * READ INDEX, detaching the SKB from the pool.
|
|
+ * Moves the packet buffer from queue to rx_used.
|
|
+ * Calls iwl_rx_queue_restock to refill any empty
|
|
+ * slots.
|
|
+ * ...
|
|
+ *
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * iwl_rx_queue_space - Return number of free slots available in queue.
|
|
+ */
|
|
+static int iwl_rx_queue_space(const struct iwl_rx_queue *q)
|
|
+{
|
|
+ int s = q->read - q->write;
|
|
+ if (s <= 0)
|
|
+ s += RX_QUEUE_SIZE;
|
|
+ /* keep some buffer to not confuse full and empty queue */
|
|
+ s -= 2;
|
|
+ if (s < 0)
|
|
+ s = 0;
|
|
+ return s;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_rx_queue_update_write_ptr - Update the write pointer for the RX queue
|
|
+ *
|
|
+ * NOTE: This function has 3945 and 4965 specific code sections
|
|
+ * but is declared in base due to the majority of the
|
|
+ * implementation being the same (only a numeric constant is
|
|
+ * different)
|
|
+ *
|
|
+ */
|
|
+int iwl_rx_queue_update_write_ptr(struct iwl_priv *priv, struct iwl_rx_queue *q)
|
|
+{
|
|
+ u32 reg = 0;
|
|
+ int rc = 0;
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&q->lock, flags);
|
|
+
|
|
+ if (q->need_update == 0)
|
|
+ goto exit_unlock;
|
|
+
|
|
+ if (test_bit(STATUS_POWER_PMI, &priv->status)) {
|
|
+ reg = iwl_read32(priv, CSR_UCODE_DRV_GP1);
|
|
+
|
|
+ if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
|
|
+ iwl_set_bit(priv, CSR_GP_CNTRL,
|
|
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
|
|
+ goto exit_unlock;
|
|
+ }
|
|
+
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc)
|
|
+ goto exit_unlock;
|
|
+
|
|
+ iwl_write_restricted(priv, FH_RSCSR_CHNL0_WPTR,
|
|
+ q->write & ~0x7);
|
|
+ iwl_release_restricted_access(priv);
|
|
+ } else
|
|
+ iwl_write32(priv, FH_RSCSR_CHNL0_WPTR, q->write & ~0x7);
|
|
+
|
|
+
|
|
+ q->need_update = 0;
|
|
+
|
|
+ exit_unlock:
|
|
+ spin_unlock_irqrestore(&q->lock, flags);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_rx_queue_restock - refill RX queue from pre-allocated pool
|
|
+ *
|
|
+ * If there are slots in the RX queue that need to be restocked,
|
|
+ * and we have free pre-allocated buffers, fill the ranks as much
|
|
+ * as we can pulling from rx_free.
|
|
+ *
|
|
+ * This moves the 'write' index forward to catch up with 'processed', and
|
|
+ * also updates the memory address in the firmware to reference the new
|
|
+ * target buffer.
|
|
+ */
|
|
+int iwl_rx_queue_restock(struct iwl_priv *priv)
|
|
+{
|
|
+ struct iwl_rx_queue *rxq = &priv->rxq;
|
|
+ struct list_head *element;
|
|
+ struct iwl_rx_mem_buffer *rxb;
|
|
+ unsigned long flags;
|
|
+ int write, rc;
|
|
+
|
|
+ spin_lock_irqsave(&rxq->lock, flags);
|
|
+ write = rxq->write & ~0x7;
|
|
+ while ((iwl_rx_queue_space(rxq) > 0) && (rxq->free_count)) {
|
|
+ element = rxq->rx_free.next;
|
|
+ rxb = list_entry(element, struct iwl_rx_mem_buffer, list);
|
|
+ list_del(element);
|
|
+ rxq->bd[rxq->write] =
|
|
+ iwl_dma_addr2rbd_ptr(priv, rxb->dma_addr);
|
|
+ rxq->queue[rxq->write] = rxb;
|
|
+ rxq->write = (rxq->write + 1) & RX_QUEUE_MASK;
|
|
+ rxq->free_count--;
|
|
+ }
|
|
+ spin_unlock_irqrestore(&rxq->lock, flags);
|
|
+ /* If the pre-allocated buffer pool is dropping low, schedule to
|
|
+ * refill it */
|
|
+ if (rxq->free_count <= RX_LOW_WATERMARK)
|
|
+ queue_work(priv->workqueue, &priv->rx_replenish);
|
|
+
|
|
+
|
|
+ /* If we've added more space for the firmware to place data, tell it */
|
|
+ if ((write != (rxq->write & ~0x7))
|
|
+ || (abs(rxq->write - rxq->read) > 7)) {
|
|
+ spin_lock_irqsave(&rxq->lock, flags);
|
|
+ rxq->need_update = 1;
|
|
+ spin_unlock_irqrestore(&rxq->lock, flags);
|
|
+ rc = iwl_rx_queue_update_write_ptr(priv, rxq);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_rx_replensih - Move all used packet from rx_used to rx_free
|
|
+ *
|
|
+ * When moving to rx_free an SKB is allocated for the slot.
|
|
+ *
|
|
+ * Also restock the Rx queue via iwl_rx_queue_restock.
|
|
+ * This is called as a scheduled work item (except for during intialization)
|
|
+ */
|
|
+void iwl_rx_replenish(void *data)
|
|
+{
|
|
+ struct iwl_priv *priv = data;
|
|
+ struct iwl_rx_queue *rxq = &priv->rxq;
|
|
+ struct list_head *element;
|
|
+ struct iwl_rx_mem_buffer *rxb;
|
|
+ unsigned long flags;
|
|
+ spin_lock_irqsave(&rxq->lock, flags);
|
|
+ while (!list_empty(&rxq->rx_used)) {
|
|
+ element = rxq->rx_used.next;
|
|
+ rxb = list_entry(element, struct iwl_rx_mem_buffer, list);
|
|
+ rxb->skb =
|
|
+ alloc_skb(IWL_RX_BUF_SIZE, __GFP_NOWARN | GFP_ATOMIC);
|
|
+ if (!rxb->skb) {
|
|
+ if (net_ratelimit())
|
|
+ printk(KERN_CRIT DRV_NAME
|
|
+ ": Can not allocate SKB buffers\n");
|
|
+ /* We don't reschedule replenish work here -- we will
|
|
+ * call the restock method and if it still needs
|
|
+ * more buffers it will schedule replenish */
|
|
+ break;
|
|
+ }
|
|
+ priv->alloc_rxb_skb++;
|
|
+ list_del(element);
|
|
+ rxb->dma_addr =
|
|
+ pci_map_single(priv->pci_dev, rxb->skb->data,
|
|
+ IWL_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
|
|
+ list_add_tail(&rxb->list, &rxq->rx_free);
|
|
+ rxq->free_count++;
|
|
+ }
|
|
+ spin_unlock_irqrestore(&rxq->lock, flags);
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ iwl_rx_queue_restock(priv);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+}
|
|
+
|
|
+/* Assumes that the skb field of the buffers in 'pool' is kept accurate.
|
|
+ * If an SKB has been detached, the POOL needs to have it's SKB set to NULL
|
|
+ * This free routine walks the list of POOL entries and if SKB is set to
|
|
+ * non NULL it is unmapped and freed
|
|
+ */
|
|
+void iwl_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
|
|
+{
|
|
+ int i;
|
|
+ for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) {
|
|
+ if (rxq->pool[i].skb != NULL) {
|
|
+ pci_unmap_single(priv->pci_dev,
|
|
+ rxq->pool[i].dma_addr,
|
|
+ IWL_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
|
|
+ dev_kfree_skb(rxq->pool[i].skb);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ pci_free_consistent(priv->pci_dev, 4 * RX_QUEUE_SIZE, rxq->bd,
|
|
+ rxq->dma_addr);
|
|
+ rxq->bd = NULL;
|
|
+}
|
|
+
|
|
+int iwl_rx_queue_alloc(struct iwl_priv *priv)
|
|
+{
|
|
+ struct iwl_rx_queue *rxq = &priv->rxq;
|
|
+ struct pci_dev *dev = priv->pci_dev;
|
|
+ int i;
|
|
+
|
|
+ spin_lock_init(&rxq->lock);
|
|
+ INIT_LIST_HEAD(&rxq->rx_free);
|
|
+ INIT_LIST_HEAD(&rxq->rx_used);
|
|
+ rxq->bd = pci_alloc_consistent(dev, 4 * RX_QUEUE_SIZE, &rxq->dma_addr);
|
|
+ if (!rxq->bd)
|
|
+ return -ENOMEM;
|
|
+ /* Fill the rx_used queue with _all_ of the Rx buffers */
|
|
+ for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++)
|
|
+ list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
|
|
+ /* Set us so that we have processed and used all buffers, but have
|
|
+ * not restocked the Rx queue with fresh buffers */
|
|
+ rxq->read = rxq->write = 0;
|
|
+ rxq->free_count = 0;
|
|
+ rxq->need_update = 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void iwl_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
|
|
+{
|
|
+ unsigned long flags;
|
|
+ int i;
|
|
+ spin_lock_irqsave(&rxq->lock, flags);
|
|
+ INIT_LIST_HEAD(&rxq->rx_free);
|
|
+ INIT_LIST_HEAD(&rxq->rx_used);
|
|
+ /* Fill the rx_used queue with _all_ of the Rx buffers */
|
|
+ for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
|
|
+ /* In the reset function, these buffers may have been allocated
|
|
+ * to an SKB, so we need to unmap and free potential storage */
|
|
+ if (rxq->pool[i].skb != NULL) {
|
|
+ pci_unmap_single(priv->pci_dev,
|
|
+ rxq->pool[i].dma_addr,
|
|
+ IWL_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
|
|
+ priv->alloc_rxb_skb--;
|
|
+ dev_kfree_skb(rxq->pool[i].skb);
|
|
+ rxq->pool[i].skb = NULL;
|
|
+ }
|
|
+ list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
|
|
+ }
|
|
+
|
|
+ /* Set us so that we have processed and used all buffers, but have
|
|
+ * not restocked the Rx queue with fresh buffers */
|
|
+ rxq->read = rxq->write = 0;
|
|
+ rxq->free_count = 0;
|
|
+ spin_unlock_irqrestore(&rxq->lock, flags);
|
|
+}
|
|
+
|
|
+/* Convert linear signal-to-noise ratio into dB */
|
|
+static u8 ratio2dB[100] = {
|
|
+/* 0 1 2 3 4 5 6 7 8 9 */
|
|
+ 0, 0, 6, 10, 12, 14, 16, 17, 18, 19, /* 00 - 09 */
|
|
+ 20, 21, 22, 22, 23, 23, 24, 25, 26, 26, /* 10 - 19 */
|
|
+ 26, 26, 26, 27, 27, 28, 28, 28, 29, 29, /* 20 - 29 */
|
|
+ 29, 30, 30, 30, 31, 31, 31, 31, 32, 32, /* 30 - 39 */
|
|
+ 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, /* 40 - 49 */
|
|
+ 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, /* 50 - 59 */
|
|
+ 36, 36, 36, 36, 36, 36, 36, 37, 37, 37, /* 60 - 69 */
|
|
+ 37, 37, 37, 37, 37, 38, 38, 38, 38, 38, /* 70 - 79 */
|
|
+ 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, /* 80 - 89 */
|
|
+ 39, 39, 39, 39, 39, 40, 40, 40, 40, 40 /* 90 - 99 */
|
|
+};
|
|
+
|
|
+/* Calculates a relative dB value from a ratio of linear
|
|
+ * (i.e. not dB) signal levels.
|
|
+ * Conversion assumes that levels are voltages (20*log), not powers (10*log). */
|
|
+int iwl_calc_db_from_ratio(int sig_ratio)
|
|
+{
|
|
+ /* Anything above 1000:1 just report as 60 dB */
|
|
+ if (sig_ratio > 1000)
|
|
+ return 60;
|
|
+
|
|
+ /* Above 100:1, divide by 10 and use table,
|
|
+ * add 20 dB to make up for divide by 10 */
|
|
+ if (sig_ratio > 100)
|
|
+ return (20 + (int)ratio2dB[sig_ratio/10]);
|
|
+
|
|
+ /* We shouldn't see this */
|
|
+ if (sig_ratio < 1)
|
|
+ return 0;
|
|
+
|
|
+ /* Use table for ratios 1:1 - 99:1 */
|
|
+ return (int)ratio2dB[sig_ratio];
|
|
+}
|
|
+
|
|
+#define PERFECT_RSSI (-20) /* dBm */
|
|
+#define WORST_RSSI (-95) /* dBm */
|
|
+#define RSSI_RANGE (PERFECT_RSSI - WORST_RSSI)
|
|
+
|
|
+/* Calculate an indication of rx signal quality (a percentage, not dBm!).
|
|
+ * See http://www.ces.clemson.edu/linux/signal_quality.shtml for info
|
|
+ * about formulas used below. */
|
|
+int iwl_calc_sig_qual(int rssi_dbm, int noise_dbm)
|
|
+{
|
|
+ int sig_qual;
|
|
+ int degradation = PERFECT_RSSI - rssi_dbm;
|
|
+
|
|
+ /* If we get a noise measurement, use signal-to-noise ratio (SNR)
|
|
+ * as indicator; formula is (signal dbm - noise dbm).
|
|
+ * SNR at or above 40 is a great signal (100%).
|
|
+ * Below that, scale to fit SNR of 0 - 40 dB within 0 - 100% indicator.
|
|
+ * Weakest usable signal is usually 10 - 15 dB SNR. */
|
|
+ if (noise_dbm) {
|
|
+ if (rssi_dbm - noise_dbm >= 40)
|
|
+ return 100;
|
|
+ else if (rssi_dbm < noise_dbm)
|
|
+ return 0;
|
|
+ sig_qual = ((rssi_dbm - noise_dbm) * 5) / 2;
|
|
+
|
|
+ /* Else use just the signal level.
|
|
+ * This formula is a least squares fit of data points collected and
|
|
+ * compared with a reference system that had a percentage (%) display
|
|
+ * for signal quality. */
|
|
+ } else
|
|
+ sig_qual = (100 * (RSSI_RANGE * RSSI_RANGE) - degradation *
|
|
+ (15 * RSSI_RANGE + 62 * degradation)) /
|
|
+ (RSSI_RANGE * RSSI_RANGE);
|
|
+
|
|
+ if (sig_qual > 100)
|
|
+ sig_qual = 100;
|
|
+ else if (sig_qual < 1)
|
|
+ sig_qual = 0;
|
|
+
|
|
+ return sig_qual;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_rx_handle - Main entry function for receiving responses from the uCode
|
|
+ *
|
|
+ * Uses the priv->rx_handlers callback function array to invoke
|
|
+ * the appropriate handlers, including command responses,
|
|
+ * frame-received notifications, and other notifications.
|
|
+ */
|
|
+static void iwl_rx_handle(struct iwl_priv *priv)
|
|
+{
|
|
+ struct iwl_rx_mem_buffer *rxb;
|
|
+ struct iwl_rx_packet *pkt;
|
|
+ struct iwl_rx_queue *rxq = &priv->rxq;
|
|
+ u32 r, i;
|
|
+ int reclaim;
|
|
+ unsigned long flags;
|
|
+
|
|
+ r = iwl_hw_get_rx_read(priv);
|
|
+ i = rxq->read;
|
|
+
|
|
+ /* Rx interrupt, but nothing sent from uCode */
|
|
+ if (i == r)
|
|
+ IWL_DEBUG(IWL_DL_RX | IWL_DL_ISR, "r = %d, i = %d\n", r, i);
|
|
+
|
|
+ while (i != r) {
|
|
+ rxb = rxq->queue[i];
|
|
+
|
|
+ /* If an RXB doesn't have a queue slot associated with it
|
|
+ * then a bug has been introduced in the queue refilling
|
|
+ * routines -- catch it here */
|
|
+ BUG_ON(rxb == NULL);
|
|
+
|
|
+ rxq->queue[i] = NULL;
|
|
+
|
|
+ pci_dma_sync_single_for_cpu(priv->pci_dev, rxb->dma_addr,
|
|
+ IWL_RX_BUF_SIZE,
|
|
+ PCI_DMA_FROMDEVICE);
|
|
+ pkt = (struct iwl_rx_packet *)rxb->skb->data;
|
|
+
|
|
+ /* Reclaim a command buffer only if this packet is a response
|
|
+ * to a (driver-originated) command.
|
|
+ * If the packet (e.g. Rx frame) originated from uCode,
|
|
+ * there is no command buffer to reclaim.
|
|
+ * Ucode should set SEQ_RX_FRAME bit if ucode-originated,
|
|
+ * but apparently a few don't get set; catch them here. */
|
|
+ reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME) &&
|
|
+#if IWL == 4965
|
|
+ (pkt->hdr.cmd != REPLY_RX_PHY_CMD) &&
|
|
+ (pkt->hdr.cmd != REPLY_4965_RX) &&
|
|
+#endif
|
|
+ (pkt->hdr.cmd != STATISTICS_NOTIFICATION) &&
|
|
+ (pkt->hdr.cmd != REPLY_TX);
|
|
+
|
|
+ /* Based on type of command response or notification,
|
|
+ * handle those that need handling via function in
|
|
+ * rx_handlers table. See iwl_setup_rx_handlers() */
|
|
+ if (priv->rx_handlers[pkt->hdr.cmd]) {
|
|
+ IWL_DEBUG(IWL_DL_HOST_COMMAND | IWL_DL_RX | IWL_DL_ISR,
|
|
+ "r = %d, i = %d, %s, 0x%02x\n", r, i,
|
|
+ get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd);
|
|
+ priv->rx_handlers[pkt->hdr.cmd] (priv, rxb);
|
|
+ } else {
|
|
+ /* No handling needed */
|
|
+ IWL_DEBUG(IWL_DL_HOST_COMMAND | IWL_DL_RX | IWL_DL_ISR,
|
|
+ "r %d i %d No handler needed for %s, 0x%02x\n",
|
|
+ r, i, get_cmd_string(pkt->hdr.cmd),
|
|
+ pkt->hdr.cmd);
|
|
+ }
|
|
+
|
|
+ if (reclaim) {
|
|
+ /* Invoke any callbacks, transfer the skb to caller,
|
|
+ * and fire off the (possibly) blocking iwl_send_cmd()
|
|
+ * as we reclaim the driver command queue */
|
|
+ if (rxb && rxb->skb)
|
|
+ iwl_tx_cmd_complete(priv, rxb);
|
|
+ else
|
|
+ IWL_WARNING("Claim null rxb?\n");
|
|
+ }
|
|
+
|
|
+ /* For now we just don't re-use anything. We can tweak this
|
|
+ * later to try and re-use notification packets and SKBs that
|
|
+ * fail to Rx correctly */
|
|
+ if (rxb->skb != NULL) {
|
|
+ priv->alloc_rxb_skb--;
|
|
+ dev_kfree_skb_any(rxb->skb);
|
|
+ rxb->skb = NULL;
|
|
+ }
|
|
+
|
|
+ pci_unmap_single(priv->pci_dev, rxb->dma_addr,
|
|
+ IWL_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
|
|
+ spin_lock_irqsave(&rxq->lock, flags);
|
|
+ list_add_tail(&rxb->list, &priv->rxq.rx_used);
|
|
+ spin_unlock_irqrestore(&rxq->lock, flags);
|
|
+ i = (i + 1) & RX_QUEUE_MASK;
|
|
+ }
|
|
+
|
|
+ /* Backtrack one entry */
|
|
+ priv->rxq.read = i;
|
|
+ iwl_rx_queue_restock(priv);
|
|
+}
|
|
+
|
|
+int iwl_tx_queue_update_write_ptr(struct iwl_priv *priv,
|
|
+ struct iwl_tx_queue *txq)
|
|
+{
|
|
+ u32 reg = 0;
|
|
+ int rc = 0;
|
|
+ int txq_id = txq->q.id;
|
|
+
|
|
+ if (txq->need_update == 0)
|
|
+ return rc;
|
|
+
|
|
+ /* if we're trying to save power */
|
|
+ if (test_bit(STATUS_POWER_PMI, &priv->status)) {
|
|
+ /* wake up nic if it's powered down ...
|
|
+ * uCode will wake up, and interrupt us again, so next
|
|
+ * time we'll skip this part. */
|
|
+ reg = iwl_read32(priv, CSR_UCODE_DRV_GP1);
|
|
+
|
|
+ if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
|
|
+ IWL_DEBUG_INFO("Requesting wakeup, GP1 = 0x%x\n", reg);
|
|
+ iwl_set_bit(priv, CSR_GP_CNTRL,
|
|
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ /* restore this queue's parameters in nic hardware. */
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+ iwl_write_restricted(priv, HBUS_TARG_WRPTR,
|
|
+ txq->q.first_empty | (txq_id << 8));
|
|
+ iwl_release_restricted_access(priv);
|
|
+
|
|
+ /* else not in power-save mode, uCode will never sleep when we're
|
|
+ * trying to tx (during RFKILL, we're not trying to tx). */
|
|
+ } else
|
|
+ iwl_write32(priv, HBUS_TARG_WRPTR,
|
|
+ txq->q.first_empty | (txq_id << 8));
|
|
+
|
|
+ txq->need_update = 0;
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+static void iwl_print_rx_config_cmd(struct iwl_rxon_cmd *rxon)
|
|
+{
|
|
+ IWL_DEBUG_RADIO("RX CONFIG:\n");
|
|
+ iwl_print_hex_dump(IWL_DL_RADIO, (u8 *) rxon, sizeof(*rxon));
|
|
+ IWL_DEBUG_RADIO("u16 channel: 0x%x\n", le16_to_cpu(rxon->channel));
|
|
+ IWL_DEBUG_RADIO("u32 flags: 0x%08X\n", le32_to_cpu(rxon->flags));
|
|
+ IWL_DEBUG_RADIO("u32 filter_flags: 0x%08x\n",
|
|
+ le32_to_cpu(rxon->filter_flags));
|
|
+ IWL_DEBUG_RADIO("u8 dev_type: 0x%x\n", rxon->dev_type);
|
|
+ IWL_DEBUG_RADIO("u8 ofdm_basic_rates: 0x%02x\n",
|
|
+ rxon->ofdm_basic_rates);
|
|
+ IWL_DEBUG_RADIO("u8 cck_basic_rates: 0x%02x\n", rxon->cck_basic_rates);
|
|
+ IWL_DEBUG_RADIO("u8[6] node_addr: " MAC_FMT "\n",
|
|
+ MAC_ARG(rxon->node_addr));
|
|
+ IWL_DEBUG_RADIO("u8[6] bssid_addr: " MAC_FMT "\n",
|
|
+ MAC_ARG(rxon->bssid_addr));
|
|
+ IWL_DEBUG_RADIO("u16 assoc_id: 0x%x\n", le16_to_cpu(rxon->assoc_id));
|
|
+}
|
|
+#endif
|
|
+
|
|
+static void iwl_enable_interrupts(struct iwl_priv *priv)
|
|
+{
|
|
+ IWL_DEBUG_ISR("Enabling interrupts\n");
|
|
+ set_bit(STATUS_INT_ENABLED, &priv->status);
|
|
+ iwl_write32(priv, CSR_INT_MASK, CSR_INI_SET_MASK);
|
|
+}
|
|
+
|
|
+static inline void iwl_disable_interrupts(struct iwl_priv *priv)
|
|
+{
|
|
+ clear_bit(STATUS_INT_ENABLED, &priv->status);
|
|
+
|
|
+ /* disable interrupts from uCode/NIC to host */
|
|
+ iwl_write32(priv, CSR_INT_MASK, 0x00000000);
|
|
+
|
|
+ /* acknowledge/clear/reset any interrupts still pending
|
|
+ * from uCode or flow handler (Rx/Tx DMA) */
|
|
+ iwl_write32(priv, CSR_INT, 0xffffffff);
|
|
+ iwl_write32(priv, CSR_FH_INT_STATUS, 0xffffffff);
|
|
+ IWL_DEBUG_ISR("Disabled interrupts\n");
|
|
+}
|
|
+
|
|
+static const char *desc_lookup(int i)
|
|
+{
|
|
+ switch (i) {
|
|
+ case 1:
|
|
+ return "FAIL";
|
|
+ case 2:
|
|
+ return "BAD_PARAM";
|
|
+ case 3:
|
|
+ return "BAD_CHECKSUM";
|
|
+ case 4:
|
|
+ return "NMI_INTERRUPT";
|
|
+ case 5:
|
|
+ return "SYSASSERT";
|
|
+ case 6:
|
|
+ return "FATAL_ERROR";
|
|
+ }
|
|
+
|
|
+ return "UNKNOWN";
|
|
+}
|
|
+
|
|
+#define ERROR_START_OFFSET (1 * sizeof(u32))
|
|
+#define ERROR_ELEM_SIZE (7 * sizeof(u32))
|
|
+
|
|
+static void iwl_dump_nic_error_log(struct iwl_priv *priv)
|
|
+{
|
|
+#if IWL == 3945
|
|
+ u32 i;
|
|
+#else /* IWL == 4965 */
|
|
+ u32 data2, line;
|
|
+#endif
|
|
+ u32 desc, time, count, base, data1;
|
|
+ u32 blink1, blink2, ilink1, ilink2;
|
|
+ int rc;
|
|
+
|
|
+ base = le32_to_cpu(priv->card_alive.error_event_table_ptr);
|
|
+
|
|
+ if (!iwl_hw_valid_rtc_data_addr(base)) {
|
|
+ IWL_ERROR("Not valid error log pointer 0x%08X\n", base);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc) {
|
|
+ IWL_WARNING("Can not read from adapter at this time.\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ count = iwl_read_restricted_mem(priv, base);
|
|
+
|
|
+ if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) {
|
|
+ IWL_ERROR("Start IWL Error Log Dump:\n");
|
|
+ IWL_ERROR("Status: 0x%08lX, Config: %08X count: %d\n",
|
|
+ priv->status, priv->config, count);
|
|
+ }
|
|
+
|
|
+#if IWL == 3945
|
|
+ IWL_ERROR("Desc Time asrtPC blink2 "
|
|
+ "ilink1 nmiPC Line\n");
|
|
+ for (i = ERROR_START_OFFSET;
|
|
+ i < (count * ERROR_ELEM_SIZE) + ERROR_START_OFFSET;
|
|
+ i += ERROR_ELEM_SIZE) {
|
|
+ desc = iwl_read_restricted_mem(priv, base + i);
|
|
+ time =
|
|
+ iwl_read_restricted_mem(priv, base + i + 1 * sizeof(u32));
|
|
+ blink1 =
|
|
+ iwl_read_restricted_mem(priv, base + i + 2 * sizeof(u32));
|
|
+ blink2 =
|
|
+ iwl_read_restricted_mem(priv, base + i + 3 * sizeof(u32));
|
|
+ ilink1 =
|
|
+ iwl_read_restricted_mem(priv, base + i + 4 * sizeof(u32));
|
|
+ ilink2 =
|
|
+ iwl_read_restricted_mem(priv, base + i + 5 * sizeof(u32));
|
|
+ data1 =
|
|
+ iwl_read_restricted_mem(priv, base + i + 6 * sizeof(u32));
|
|
+
|
|
+ IWL_ERROR
|
|
+ ("%-13s (#%d) %010u 0x%05X 0x%05X 0x%05X 0x%05X %u\n\n",
|
|
+ desc_lookup(desc), desc, time, blink1, blink2,
|
|
+ ilink1, ilink2, data1);
|
|
+ }
|
|
+#else /* 4965 Error format */
|
|
+ desc = iwl_read_restricted_mem(priv, base + 1 * sizeof(u32));
|
|
+ blink1 = iwl_read_restricted_mem(priv, base + 3 * sizeof(u32));
|
|
+ blink2 = iwl_read_restricted_mem(priv, base + 4 * sizeof(u32));
|
|
+ ilink1 = iwl_read_restricted_mem(priv, base + 5 * sizeof(u32));
|
|
+ ilink2 = iwl_read_restricted_mem(priv, base + 6 * sizeof(u32));
|
|
+ data1 = iwl_read_restricted_mem(priv, base + 7 * sizeof(u32));
|
|
+ data2 = iwl_read_restricted_mem(priv, base + 8 * sizeof(u32));
|
|
+ line = iwl_read_restricted_mem(priv, base + 9 * sizeof(u32));
|
|
+ time = iwl_read_restricted_mem(priv, base + 11 * sizeof(u32));
|
|
+
|
|
+ IWL_ERROR("Desc Time "
|
|
+ "data1 data2 line\n");
|
|
+ IWL_ERROR
|
|
+ ("%-13s (#%d) %010u 0x%08X 0x%08X %u\n",
|
|
+ desc_lookup(desc), desc, time, data1, data2, line);
|
|
+ IWL_ERROR("blink1 blink2 ilink1 ilink2\n");
|
|
+ IWL_ERROR
|
|
+ ("0x%05X 0x%05X 0x%05X 0x%05X\n", blink1, blink2, ilink1, ilink2);
|
|
+
|
|
+#endif /* IWL 3945 */
|
|
+
|
|
+ iwl_release_restricted_access(priv);
|
|
+
|
|
+}
|
|
+
|
|
+#define EVENT_START_OFFSET (4 * sizeof(u32))
|
|
+
|
|
+/**
|
|
+ * iwl_print_event_log - Dump error event log to syslog
|
|
+ *
|
|
+ * NOTE: Must be called with iwl_grab_restricted_access() already obtained!
|
|
+ */
|
|
+static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
|
|
+ u32 num_events, u32 mode)
|
|
+{
|
|
+ u32 i;
|
|
+ u32 base; /* SRAM byte address of event log header */
|
|
+ u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */
|
|
+ u32 ptr; /* SRAM byte address of log data */
|
|
+ u32 ev, time, data; /* event log data */
|
|
+
|
|
+ if (num_events == 0)
|
|
+ return;
|
|
+
|
|
+ base = le32_to_cpu(priv->card_alive.log_event_table_ptr);
|
|
+
|
|
+ if (mode == 0)
|
|
+ event_size = 2 * sizeof(u32);
|
|
+ else
|
|
+ event_size = 3 * sizeof(u32);
|
|
+
|
|
+ ptr = base + EVENT_START_OFFSET + (start_idx * event_size);
|
|
+
|
|
+ /* "time" is actually "data" for mode 0 (no timestamp).
|
|
+ * place event id # at far right for easier visual parsing. */
|
|
+ for (i = 0; i < num_events; i++) {
|
|
+ ev = iwl_read_restricted_mem(priv, ptr);
|
|
+ ptr += sizeof(u32);
|
|
+ time = iwl_read_restricted_mem(priv, ptr);
|
|
+ ptr += sizeof(u32);
|
|
+ if (mode == 0)
|
|
+ IWL_ERROR("0x%08x\t%04u\n", time, ev); /* data, ev */
|
|
+ else {
|
|
+ data = iwl_read_restricted_mem(priv, ptr);
|
|
+ ptr += sizeof(u32);
|
|
+ IWL_ERROR("%010u\t0x%08x\t%04u\n", time, data, ev);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static void iwl_dump_nic_event_log(struct iwl_priv *priv)
|
|
+{
|
|
+ int rc;
|
|
+ u32 base; /* SRAM byte address of event log header */
|
|
+ u32 capacity; /* event log capacity in # entries */
|
|
+ u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */
|
|
+ u32 num_wraps; /* # times uCode wrapped to top of log */
|
|
+ u32 next_entry; /* index of next entry to be written by uCode */
|
|
+ u32 size; /* # entries that we'll print */
|
|
+
|
|
+ base = le32_to_cpu(priv->card_alive.log_event_table_ptr);
|
|
+ if (!iwl_hw_valid_rtc_data_addr(base)) {
|
|
+ IWL_ERROR("Invalid event log pointer 0x%08X\n", base);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc) {
|
|
+ IWL_WARNING("Can not read from adapter at this time.\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* event log header */
|
|
+ capacity = iwl_read_restricted_mem(priv, base);
|
|
+ mode = iwl_read_restricted_mem(priv, base + (1 * sizeof(u32)));
|
|
+ num_wraps = iwl_read_restricted_mem(priv, base + (2 * sizeof(u32)));
|
|
+ next_entry = iwl_read_restricted_mem(priv, base + (3 * sizeof(u32)));
|
|
+
|
|
+ size = num_wraps ? capacity : next_entry;
|
|
+
|
|
+ /* bail out if nothing in log */
|
|
+ if (size == 0) {
|
|
+ IWL_ERROR("Start IPW Event Log Dump: nothing in log\n");
|
|
+ iwl_release_restricted_access(priv);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ IWL_ERROR("Start IPW Event Log Dump: display count %d, wraps %d\n",
|
|
+ size, num_wraps);
|
|
+
|
|
+ /* if uCode has wrapped back to top of log, start at the oldest entry,
|
|
+ * i.e the next one that uCode would fill. */
|
|
+ if (num_wraps)
|
|
+ iwl_print_event_log(priv, next_entry,
|
|
+ capacity - next_entry, mode);
|
|
+
|
|
+ /* (then/else) start at top of log */
|
|
+ iwl_print_event_log(priv, 0, next_entry, mode);
|
|
+
|
|
+ iwl_release_restricted_access(priv);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_irq_handle_error - called for HW or SW error interrupt from card
|
|
+ */
|
|
+static void iwl_irq_handle_error(struct iwl_priv *priv)
|
|
+{
|
|
+ /* Set the FW error flag -- cleared on iwl_down */
|
|
+ set_bit(STATUS_FW_ERROR, &priv->status);
|
|
+
|
|
+ /* Cancel currently queued command. */
|
|
+ clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+ if (iwl_debug_level & IWL_DL_FW_ERRORS) {
|
|
+ iwl_dump_nic_error_log(priv);
|
|
+ iwl_dump_nic_event_log(priv);
|
|
+ iwl_print_rx_config_cmd(&priv->staging_rxon);
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ wake_up_interruptible(&priv->wait_command_queue);
|
|
+
|
|
+ /* Keep the restart process from trying to send host
|
|
+ * commands by clearing the INIT status bit */
|
|
+ clear_bit(STATUS_READY, &priv->status);
|
|
+
|
|
+ if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) {
|
|
+ IWL_DEBUG(IWL_DL_INFO | IWL_DL_FW_ERRORS,
|
|
+ "Restarting adapter due to uCode error.\n");
|
|
+
|
|
+ if (iwl_is_associated(priv)) {
|
|
+ memcpy(&priv->recovery_rxon, &priv->active_rxon,
|
|
+ sizeof(priv->recovery_rxon));
|
|
+ priv->error_recovering = 1;
|
|
+ }
|
|
+ queue_work(priv->workqueue, &priv->restart);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void iwl_error_recovery(struct iwl_priv *priv)
|
|
+{
|
|
+ unsigned long flags;
|
|
+
|
|
+ memcpy(&priv->staging_rxon, &priv->recovery_rxon,
|
|
+ sizeof(priv->staging_rxon));
|
|
+ priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
|
|
+ iwl_commit_rxon(priv);
|
|
+
|
|
+ iwl_rxon_add_station(priv, priv->bssid, 1);
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ priv->assoc_id = le16_to_cpu(priv->staging_rxon.assoc_id);
|
|
+ priv->error_recovering = 0;
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+}
|
|
+
|
|
+static void iwl_irq_tasklet(struct iwl_priv *priv)
|
|
+{
|
|
+ u32 inta, handled = 0;
|
|
+ u32 inta_fh;
|
|
+ unsigned long flags;
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+ u32 inta_mask;
|
|
+#endif
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+
|
|
+ /* Ack/clear/reset pending uCode interrupts.
|
|
+ * Note: Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS,
|
|
+ * and will clear only when CSR_FH_INT_STATUS gets cleared. */
|
|
+ inta = iwl_read32(priv, CSR_INT);
|
|
+ iwl_write32(priv, CSR_INT, inta);
|
|
+
|
|
+ /* Ack/clear/reset pending flow-handler (DMA) interrupts.
|
|
+ * Any new interrupts that happen after this, either while we're
|
|
+ * in this tasklet, or later, will show up in next ISR/tasklet. */
|
|
+ inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS);
|
|
+ iwl_write32(priv, CSR_FH_INT_STATUS, inta_fh);
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+ if (iwl_debug_level & IWL_DL_ISR) {
|
|
+ inta_mask = iwl_read32(priv, CSR_INT_MASK); /* just for debug */
|
|
+ IWL_DEBUG_ISR("inta 0x%08x, enabled 0x%08x, fh 0x%08x\n",
|
|
+ inta, inta_mask, inta_fh);
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ /* Since CSR_INT and CSR_FH_INT_STATUS reads and clears are not
|
|
+ * atomic, make sure that inta covers all the interrupts that
|
|
+ * we've discovered, even if FH interrupt came in just after
|
|
+ * reading CSR_INT. */
|
|
+ if (inta_fh & FH_INT_RX_MASK)
|
|
+ inta |= BIT_INT_FH_RX;
|
|
+ if (inta_fh & FH_INT_TX_MASK)
|
|
+ inta |= BIT_INT_FH_TX;
|
|
+
|
|
+ /* Now service all interrupt bits discovered above. */
|
|
+ if (inta & BIT_INT_ERR) {
|
|
+ IWL_ERROR("Microcode HW error detected. Restarting.\n");
|
|
+
|
|
+ /* Tell the device to stop sending interrupts */
|
|
+ iwl_disable_interrupts(priv);
|
|
+
|
|
+ iwl_irq_handle_error(priv);
|
|
+
|
|
+ handled |= BIT_INT_ERR;
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ return;
|
|
+ }
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+ if (iwl_debug_level & (IWL_DL_ISR)) {
|
|
+ /* NIC fires this, but we don't use it, redundant with WAKEUP */
|
|
+ if (inta & BIT_INT_MAC_CLK_ACTV)
|
|
+ IWL_DEBUG_ISR("Microcode started or stopped.\n");
|
|
+
|
|
+ /* Alive notification via Rx interrupt will do the real work */
|
|
+ if (inta & BIT_INT_ALIVE)
|
|
+ IWL_DEBUG_ISR("Alive interrupt\n");
|
|
+ }
|
|
+#endif
|
|
+ /* Safely ignore these bits for debug checks below */
|
|
+ inta &= ~(BIT_INT_MAC_CLK_ACTV | BIT_INT_ALIVE);
|
|
+
|
|
+ /* HW RF KILL switch toggled (4965 only) */
|
|
+ if (inta & BIT_INT_RF_KILL) {
|
|
+ int hw_rf_kill = 0;
|
|
+ if (!(iwl_read32(priv, CSR_GP_CNTRL) &
|
|
+ CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW))
|
|
+ hw_rf_kill = 1;
|
|
+
|
|
+ IWL_DEBUG(IWL_DL_INFO | IWL_DL_RF_KILL | IWL_DL_ISR,
|
|
+ "RF_KILL bit toggled to %s.\n",
|
|
+ hw_rf_kill ? "disable radio":"enable radio");
|
|
+
|
|
+ /* Queue restart only if RF_KILL switch was set to "kill"
|
|
+ * when we loaded driver, and is now set to "enable".
|
|
+ * After we're Alive, RF_KILL gets handled by
|
|
+ * iwl_rx_card_state_notif() */
|
|
+ if (!hw_rf_kill && !test_bit(STATUS_ALIVE, &priv->status))
|
|
+ queue_work(priv->workqueue, &priv->restart);
|
|
+
|
|
+ handled |= BIT_INT_RF_KILL;
|
|
+ }
|
|
+
|
|
+ /* Chip got too hot and stopped itself (4965 only) */
|
|
+ if (inta & BIT_INT_CT_KILL) {
|
|
+ IWL_ERROR("Microcode CT kill error detected.\n");
|
|
+ handled |= BIT_INT_CT_KILL;
|
|
+ }
|
|
+
|
|
+ /* Error detected by uCode */
|
|
+ if (inta & BIT_INT_SWERROR) {
|
|
+ IWL_ERROR("Microcode SW error detected. Restarting 0x%X.\n",
|
|
+ inta);
|
|
+ iwl_irq_handle_error(priv);
|
|
+ handled |= BIT_INT_SWERROR;
|
|
+ }
|
|
+
|
|
+ /* uCode wakes up after power-down sleep */
|
|
+ if (inta & BIT_INT_WAKEUP) {
|
|
+ IWL_DEBUG_ISR("Wakeup interrupt\n");
|
|
+ iwl_rx_queue_update_write_ptr(priv, &priv->rxq);
|
|
+ iwl_tx_queue_update_write_ptr(priv, &priv->txq[0]);
|
|
+ iwl_tx_queue_update_write_ptr(priv, &priv->txq[1]);
|
|
+ iwl_tx_queue_update_write_ptr(priv, &priv->txq[2]);
|
|
+ iwl_tx_queue_update_write_ptr(priv, &priv->txq[3]);
|
|
+ iwl_tx_queue_update_write_ptr(priv, &priv->txq[4]);
|
|
+ iwl_tx_queue_update_write_ptr(priv, &priv->txq[5]);
|
|
+
|
|
+ handled |= BIT_INT_WAKEUP;
|
|
+ }
|
|
+
|
|
+ /* All uCode command responses, including Tx command responses,
|
|
+ * Rx "responses" (frame-received notification), and other
|
|
+ * notifications from uCode come through here*/
|
|
+ if (inta & (BIT_INT_FH_RX | BIT_INT_SW_RX)) {
|
|
+ iwl_rx_handle(priv);
|
|
+ handled |= (BIT_INT_FH_RX | BIT_INT_SW_RX);
|
|
+ }
|
|
+
|
|
+ if (inta & BIT_INT_FH_TX) {
|
|
+ IWL_DEBUG_ISR("Tx interrupt\n");
|
|
+
|
|
+#if IWL == 3945
|
|
+ iwl_write32(priv, CSR_FH_INT_STATUS, (1 << 6));
|
|
+ if (!iwl_grab_restricted_access(priv)) {
|
|
+ iwl_write_restricted(priv,
|
|
+ FH_TCSR_CREDIT
|
|
+ (ALM_FH_SRVC_CHNL), 0x0);
|
|
+ iwl_release_restricted_access(priv);
|
|
+ }
|
|
+#endif /* IWL == 3945 */
|
|
+ handled |= BIT_INT_FH_TX;
|
|
+ }
|
|
+
|
|
+ if (inta & ~handled)
|
|
+ IWL_ERROR("Unhandled INTA bits 0x%08x\n", inta & ~handled);
|
|
+
|
|
+ if (inta & ~CSR_INI_SET_MASK) {
|
|
+ IWL_WARNING("Disabled INTA bits 0x%08x were pending\n",
|
|
+ inta & ~CSR_INI_SET_MASK);
|
|
+ IWL_WARNING(" with FH_INT = 0x%08x\n", inta_fh);
|
|
+ }
|
|
+
|
|
+ /* Re-enable all interrupts */
|
|
+ iwl_enable_interrupts(priv);
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+ if (iwl_debug_level & (IWL_DL_ISR)) {
|
|
+ inta = iwl_read32(priv, CSR_INT);
|
|
+ inta_mask = iwl_read32(priv, CSR_INT_MASK);
|
|
+ inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS);
|
|
+ IWL_DEBUG_ISR("End inta 0x%08x, enabled 0x%08x, fh 0x%08x, "
|
|
+ "flags 0x%08lx\n", inta, inta_mask, inta_fh, flags);
|
|
+ }
|
|
+#endif
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+}
|
|
+
|
|
+static irqreturn_t iwl_isr(int irq, void *data)
|
|
+{
|
|
+ struct iwl_priv *priv = data;
|
|
+ u32 inta, inta_mask;
|
|
+ u32 inta_fh;
|
|
+ if (!priv)
|
|
+ return IRQ_NONE;
|
|
+
|
|
+ spin_lock(&priv->lock);
|
|
+
|
|
+ /* Disable (but don't clear!) interrupts here to avoid
|
|
+ * back-to-back ISRs and sporadic interrupts from our NIC.
|
|
+ * If we have something to service, the tasklet will re-enable ints.
|
|
+ * If we *don't* have something, we'll re-enable before leaving here. */
|
|
+ inta_mask = iwl_read32(priv, CSR_INT_MASK); /* just for debug */
|
|
+ iwl_write32(priv, CSR_INT_MASK, 0x00000000);
|
|
+
|
|
+ /* Discover which interrupts are active/pending */
|
|
+ inta = iwl_read32(priv, CSR_INT);
|
|
+ inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS);
|
|
+
|
|
+ /* Ignore interrupt if there's nothing in NIC to service.
|
|
+ * This may be due to IRQ shared with another device,
|
|
+ * or due to sporadic interrupts thrown from our NIC. */
|
|
+ if (!inta && !inta_fh) {
|
|
+ IWL_DEBUG_ISR("Ignore interrupt, inta == 0, inta_fh == 0\n");
|
|
+ goto none;
|
|
+ }
|
|
+
|
|
+ if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) {
|
|
+ /* Hardware disappeared */
|
|
+ IWL_WARNING("HARDWARE GONE?? INTA == 0x%080x\n", inta);
|
|
+ goto none;
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_ISR("ISR inta 0x%08x, enabled 0x%08x, fh 0x%08x\n",
|
|
+ inta, inta_mask, inta_fh);
|
|
+
|
|
+ /* iwl_irq_tasklet() will service interrupts and re-enable them */
|
|
+ tasklet_schedule(&priv->irq_tasklet);
|
|
+ spin_unlock(&priv->lock);
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+
|
|
+ none:
|
|
+ /* re-enable interrupts here since we don't have anything to service. */
|
|
+ iwl_enable_interrupts(priv);
|
|
+ spin_unlock(&priv->lock);
|
|
+ return IRQ_NONE;
|
|
+}
|
|
+
|
|
+/************************** EEPROM BANDS ****************************
|
|
+ *
|
|
+ * The iwl_eeprom_band definitions below provide the mapping from the
|
|
+ * EEPROM contents to the specific channel number supported for each
|
|
+ * band.
|
|
+ *
|
|
+ * For example, iwl_priv->eeprom.band_3_channels[4] from the band_3
|
|
+ * definition below maps to physical channel 42 in the 5.2GHz spectrum.
|
|
+ * The specific geography and calibration information for that channel
|
|
+ * is contained in the eeprom map itself.
|
|
+ *
|
|
+ * During init, we copy the eeprom information and channel map
|
|
+ * information into priv->channel_info_24/52 and priv->channel_map_24/52
|
|
+ *
|
|
+ * channel_map_24/52 provides the index in the channel_info array for a
|
|
+ * given channel. We have to have two separate maps as there is channel
|
|
+ * overlap with the 2.4GHz and 5.2GHz spectrum as seen in band_1 and
|
|
+ * band_2
|
|
+ *
|
|
+ * A value of 0xff stored in the channel_map indicates that the channel
|
|
+ * is not supported by the hardware at all.
|
|
+ *
|
|
+ * A value of 0xfe in the channel_map indicates that the channel is not
|
|
+ * valid for Tx with the current hardware. This means that
|
|
+ * while the system can tune and receive on a given channel, it may not
|
|
+ * be able to associate or transmit any frames on that
|
|
+ * channel. There is no corresponding channel information for that
|
|
+ * entry.
|
|
+ *
|
|
+ *********************************************************************/
|
|
+
|
|
+/* 2.4 GHz */
|
|
+static const u8 iwl_eeprom_band_1[14] = {
|
|
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
|
|
+};
|
|
+
|
|
+/* 5.2 GHz bands */
|
|
+static const u8 iwl_eeprom_band_2[] = {
|
|
+ 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16
|
|
+};
|
|
+
|
|
+static const u8 iwl_eeprom_band_3[] = { /* 5205-5320MHz */
|
|
+ 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64
|
|
+};
|
|
+
|
|
+static const u8 iwl_eeprom_band_4[] = { /* 5500-5700MHz */
|
|
+ 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140
|
|
+};
|
|
+
|
|
+static const u8 iwl_eeprom_band_5[] = { /* 5725-5825MHz */
|
|
+ 145, 149, 153, 157, 161, 165
|
|
+};
|
|
+
|
|
+#if IWL == 4965
|
|
+static u8 iwl_eeprom_band_6[] = { /* 2.4 FAT channel */
|
|
+ 1, 2, 3, 4, 5, 6, 7
|
|
+};
|
|
+
|
|
+static u8 iwl_eeprom_band_7[] = { /* 5.2 FAT channel */
|
|
+ 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157
|
|
+};
|
|
+#endif
|
|
+
|
|
+static void iwl_init_band_reference(const struct iwl_priv *priv, int band,
|
|
+ int *eeprom_ch_count,
|
|
+ const struct iwl_eeprom_channel
|
|
+ **eeprom_ch_info,
|
|
+ const u8 **eeprom_ch_index)
|
|
+{
|
|
+ switch (band) {
|
|
+ case 1: /* 2.4GHz band */
|
|
+ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_1);
|
|
+ *eeprom_ch_info = priv->eeprom.band_1_channels;
|
|
+ *eeprom_ch_index = iwl_eeprom_band_1;
|
|
+ break;
|
|
+ case 2: /* 5.2GHz band */
|
|
+ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_2);
|
|
+ *eeprom_ch_info = priv->eeprom.band_2_channels;
|
|
+ *eeprom_ch_index = iwl_eeprom_band_2;
|
|
+ break;
|
|
+ case 3: /* 5.2GHz band */
|
|
+ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_3);
|
|
+ *eeprom_ch_info = priv->eeprom.band_3_channels;
|
|
+ *eeprom_ch_index = iwl_eeprom_band_3;
|
|
+ break;
|
|
+ case 4: /* 5.2GHz band */
|
|
+ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_4);
|
|
+ *eeprom_ch_info = priv->eeprom.band_4_channels;
|
|
+ *eeprom_ch_index = iwl_eeprom_band_4;
|
|
+ break;
|
|
+ case 5: /* 5.2GHz band */
|
|
+ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_5);
|
|
+ *eeprom_ch_info = priv->eeprom.band_5_channels;
|
|
+ *eeprom_ch_index = iwl_eeprom_band_5;
|
|
+ break;
|
|
+#if IWL == 4965
|
|
+ case 6:
|
|
+ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_6);
|
|
+ *eeprom_ch_info = priv->eeprom.band_24_channels;
|
|
+ *eeprom_ch_index = iwl_eeprom_band_6;
|
|
+ break;
|
|
+ case 7:
|
|
+ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_7);
|
|
+ *eeprom_ch_info = priv->eeprom.band_52_channels;
|
|
+ *eeprom_ch_index = iwl_eeprom_band_7;
|
|
+ break;
|
|
+#endif
|
|
+ default:
|
|
+ BUG();
|
|
+ return;
|
|
+ }
|
|
+}
|
|
+
|
|
+const struct iwl_channel_info *iwl_get_channel_info(const struct iwl_priv *priv,
|
|
+ int phymode, u16 channel)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ switch (phymode) {
|
|
+ case MODE_IEEE80211A:
|
|
+ for (i = 14; i < priv->channel_count; i++) {
|
|
+ if (priv->channel_info[i].channel == channel)
|
|
+ return &priv->channel_info[i];
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case MODE_IEEE80211B:
|
|
+ case MODE_IEEE80211G:
|
|
+ if (channel >= 1 && channel <= 14)
|
|
+ return &priv->channel_info[channel - 1];
|
|
+ break;
|
|
+
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+#define CHECK_AND_PRINT(x) ((eeprom_ch_info[ch].flags & EEPROM_CHANNEL_##x) \
|
|
+ ? # x " " : "")
|
|
+
|
|
+static int iwl_init_channel_map(struct iwl_priv *priv)
|
|
+{
|
|
+ int eeprom_ch_count = 0;
|
|
+ const u8 *eeprom_ch_index = NULL;
|
|
+ const struct iwl_eeprom_channel *eeprom_ch_info = NULL;
|
|
+ int band, ch;
|
|
+ struct iwl_channel_info *ch_info;
|
|
+
|
|
+ if (priv->channel_count) {
|
|
+ IWL_DEBUG_INFO("Channel map already initialized.\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (priv->eeprom.version < 0x2f) {
|
|
+ IWL_WARNING("Unsupported EEPROM version: 0x%04X\n",
|
|
+ priv->eeprom.version);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_INFO("Initializing regulatory info from EEPROM\n");
|
|
+
|
|
+ priv->channel_count =
|
|
+ ARRAY_SIZE(iwl_eeprom_band_1) +
|
|
+ ARRAY_SIZE(iwl_eeprom_band_2) +
|
|
+ ARRAY_SIZE(iwl_eeprom_band_3) +
|
|
+ ARRAY_SIZE(iwl_eeprom_band_4) +
|
|
+ ARRAY_SIZE(iwl_eeprom_band_5);
|
|
+
|
|
+ IWL_DEBUG_INFO("Parsing data for %d channels.\n", priv->channel_count);
|
|
+
|
|
+ priv->channel_info = kzalloc(sizeof(struct iwl_channel_info) *
|
|
+ priv->channel_count, GFP_KERNEL);
|
|
+ if (!priv->channel_info) {
|
|
+ IWL_ERROR("Could not allocate channel_info\n");
|
|
+ priv->channel_count = 0;
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ ch_info = priv->channel_info;
|
|
+
|
|
+ /* Loop through the 5 EEPROM bands adding them in order to the
|
|
+ * channel map we maintain (that contains additional information than
|
|
+ * what just in the EEPROM) */
|
|
+ for (band = 1; band <= 5; band++) {
|
|
+
|
|
+ iwl_init_band_reference(priv, band, &eeprom_ch_count,
|
|
+ &eeprom_ch_info, &eeprom_ch_index);
|
|
+
|
|
+ /* Loop through each band adding each of the channels */
|
|
+ for (ch = 0; ch < eeprom_ch_count; ch++) {
|
|
+ ch_info->channel = eeprom_ch_index[ch];
|
|
+ ch_info->phymode = (band == 1) ? MODE_IEEE80211B :
|
|
+ MODE_IEEE80211A;
|
|
+
|
|
+ /* permanently store EEPROM's channel regulatory flags
|
|
+ * and max power in channel info database. */
|
|
+ ch_info->eeprom = eeprom_ch_info[ch];
|
|
+
|
|
+ /* Copy the run-time flags so they are there even on
|
|
+ * invalid channels */
|
|
+ ch_info->flags = eeprom_ch_info[ch].flags;
|
|
+
|
|
+ if (!(is_channel_valid(ch_info))) {
|
|
+ IWL_DEBUG_INFO("Ch. %d Flags %x [%sGHz] - "
|
|
+ "No traffic\n",
|
|
+ ch_info->channel,
|
|
+ ch_info->flags,
|
|
+ is_channel_a_band(ch_info) ?
|
|
+ "5.2" : "2.4");
|
|
+ ch_info++;
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ /* Initialize regulatory-based run-time data */
|
|
+ ch_info->max_power_avg = ch_info->curr_txpow =
|
|
+ eeprom_ch_info[ch].max_power_avg;
|
|
+ ch_info->scan_power = eeprom_ch_info[ch].max_power_avg;
|
|
+ ch_info->min_power = 0;
|
|
+
|
|
+ IWL_DEBUG_INFO("Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x"
|
|
+ " %ddBm): Ad-Hoc %ssupported\n",
|
|
+ ch_info->channel,
|
|
+ is_channel_a_band(ch_info) ?
|
|
+ "5.2" : "2.4",
|
|
+ CHECK_AND_PRINT(IBSS),
|
|
+ CHECK_AND_PRINT(ACTIVE),
|
|
+ CHECK_AND_PRINT(RADAR),
|
|
+ CHECK_AND_PRINT(WIDE),
|
|
+ CHECK_AND_PRINT(NARROW),
|
|
+ CHECK_AND_PRINT(DFS),
|
|
+ eeprom_ch_info[ch].flags,
|
|
+ eeprom_ch_info[ch].max_power_avg,
|
|
+ ((eeprom_ch_info[ch].
|
|
+ flags & EEPROM_CHANNEL_IBSS)
|
|
+ && !(eeprom_ch_info[ch].
|
|
+ flags & EEPROM_CHANNEL_RADAR))
|
|
+ ? "" : "not ");
|
|
+
|
|
+ /* Set the user_txpower_limit to the highest power
|
|
+ * supported by any channel */
|
|
+ if (eeprom_ch_info[ch].max_power_avg >
|
|
+ priv->user_txpower_limit)
|
|
+ priv->user_txpower_limit =
|
|
+ eeprom_ch_info[ch].max_power_avg;
|
|
+
|
|
+ ch_info++;
|
|
+ }
|
|
+ }
|
|
+#if IWL == 4965
|
|
+ for (band = 6; band <= 7; band++) {
|
|
+ int phymode;
|
|
+ u8 fat_extension_chan;
|
|
+
|
|
+ iwl_init_band_reference(priv, band, &eeprom_ch_count,
|
|
+ &eeprom_ch_info, &eeprom_ch_index);
|
|
+
|
|
+ phymode = (band == 6) ? MODE_IEEE80211B : MODE_IEEE80211A;
|
|
+ /* Loop through each band adding each of the channels */
|
|
+ for (ch = 0; ch < eeprom_ch_count; ch++) {
|
|
+
|
|
+ if ((band == 6) &&
|
|
+ ((eeprom_ch_index[ch] == 5) ||
|
|
+ (eeprom_ch_index[ch] == 6) ||
|
|
+ (eeprom_ch_index[ch] == 7)))
|
|
+ fat_extension_chan = HT_IE_EXT_CHANNEL_MAX;
|
|
+ else
|
|
+ fat_extension_chan = HT_IE_EXT_CHANNEL_ABOVE;
|
|
+
|
|
+ iwl4965_set_fat_chan_info(priv, phymode,
|
|
+ eeprom_ch_index[ch],
|
|
+ &(eeprom_ch_info[ch]),
|
|
+ fat_extension_chan);
|
|
+
|
|
+ iwl4965_set_fat_chan_info(priv, phymode,
|
|
+ (eeprom_ch_index[ch] + 4),
|
|
+ &(eeprom_ch_info[ch]),
|
|
+ HT_IE_EXT_CHANNEL_BELOW);
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ if (iwl3945_txpower_set_from_eeprom(priv))
|
|
+ return -EIO;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after
|
|
+ * sending probe req. This should be set long enough to hear probe responses
|
|
+ * from more than one AP. */
|
|
+#define IWL_ACTIVE_DWELL_TIME_24 (20) /* all times in msec */
|
|
+#define IWL_ACTIVE_DWELL_TIME_52 (10)
|
|
+
|
|
+/* For faster active scanning, scan will move to the next channel if fewer than
|
|
+ * PLCP_QUIET_THRESH packets are heard on this channel within
|
|
+ * ACTIVE_QUIET_TIME after sending probe request. This shortens the dwell
|
|
+ * time if it's a quiet channel (nothing responded to our probe, and there's
|
|
+ * no other traffic).
|
|
+ * Disable "quiet" feature by setting PLCP_QUIET_THRESH to 0. */
|
|
+#define IWL_PLCP_QUIET_THRESH __constant_cpu_to_le16(1) /* packets */
|
|
+#define IWL_ACTIVE_QUIET_TIME __constant_cpu_to_le16(5) /* msec */
|
|
+
|
|
+/* For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel.
|
|
+ * Must be set longer than active dwell time.
|
|
+ * For the most reliable scan, set > AP beacon interval (typically 100msec). */
|
|
+#define IWL_PASSIVE_DWELL_TIME_24 (20) /* all times in msec */
|
|
+#define IWL_PASSIVE_DWELL_TIME_52 (10)
|
|
+#define IWL_PASSIVE_DWELL_BASE (100)
|
|
+#define IWL_CHANNEL_TUNE_TIME 5
|
|
+
|
|
+static inline u16 iwl_get_active_dwell_time(struct iwl_priv *priv, int phymode)
|
|
+{
|
|
+ if (phymode == MODE_IEEE80211A)
|
|
+ return IWL_ACTIVE_DWELL_TIME_52;
|
|
+ else
|
|
+ return IWL_ACTIVE_DWELL_TIME_24;
|
|
+}
|
|
+
|
|
+static u16 iwl_get_passive_dwell_time(struct iwl_priv *priv, int phymode)
|
|
+{
|
|
+ u16 active = iwl_get_active_dwell_time(priv, phymode);
|
|
+ u16 passive = (phymode != MODE_IEEE80211A) ?
|
|
+ IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_24 :
|
|
+ IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_52;
|
|
+
|
|
+ if (iwl_is_associated(priv)) {
|
|
+ /* If we're associated, we clamp the maximum passive
|
|
+ * dwell time to be 98% of the beacon interval (minus
|
|
+ * 2 * channel tune time) */
|
|
+ passive = priv->beacon_int;
|
|
+ if ((passive > IWL_PASSIVE_DWELL_BASE) || !passive)
|
|
+ passive = IWL_PASSIVE_DWELL_BASE;
|
|
+ passive = (passive * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2;
|
|
+ }
|
|
+
|
|
+ if (passive <= active)
|
|
+ passive = active + 1;
|
|
+
|
|
+ return passive;
|
|
+}
|
|
+
|
|
+static int iwl_get_channels_for_scan(struct iwl_priv *priv, int phymode,
|
|
+ u8 is_active, u8 direct_mask,
|
|
+ struct iwl_scan_channel *scan_ch)
|
|
+{
|
|
+ const struct ieee80211_channel *channels = NULL;
|
|
+ const struct ieee80211_hw_mode *hw_mode;
|
|
+ const struct iwl_channel_info *ch_info;
|
|
+ u16 passive_dwell = 0;
|
|
+ u16 active_dwell = 0;
|
|
+ int added, i;
|
|
+
|
|
+ hw_mode = iwl_get_hw_mode(priv, phymode);
|
|
+ if (!hw_mode)
|
|
+ return 0;
|
|
+
|
|
+ channels = hw_mode->channels;
|
|
+
|
|
+ active_dwell = iwl_get_active_dwell_time(priv, phymode);
|
|
+ passive_dwell = iwl_get_passive_dwell_time(priv, phymode);
|
|
+
|
|
+ for (i = 0, added = 0; i < hw_mode->num_channels; i++) {
|
|
+ if (channels[i].chan ==
|
|
+ le16_to_cpu(priv->active_rxon.channel)) {
|
|
+ if (iwl_is_associated(priv)) {
|
|
+ IWL_DEBUG_SCAN
|
|
+ ("Skipping current channel %d\n",
|
|
+ le16_to_cpu(priv->active_rxon.channel));
|
|
+ continue;
|
|
+ }
|
|
+ } else if (priv->only_active_channel)
|
|
+ continue;
|
|
+
|
|
+ scan_ch->channel = channels[i].chan;
|
|
+
|
|
+ ch_info = iwl_get_channel_info(priv, phymode, scan_ch->channel);
|
|
+ if (!is_channel_valid(ch_info)) {
|
|
+ IWL_DEBUG_SCAN("Channel %d is INVALID for this SKU.\n",
|
|
+ scan_ch->channel);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (!is_active || is_channel_passive(ch_info) ||
|
|
+ !(channels[i].flag & IEEE80211_CHAN_W_ACTIVE_SCAN))
|
|
+ scan_ch->type = 0; /* passive */
|
|
+ else
|
|
+ scan_ch->type = 1; /* active */
|
|
+
|
|
+ if (scan_ch->type & 1)
|
|
+ scan_ch->type |= (direct_mask << 1);
|
|
+
|
|
+ if (is_channel_narrow(ch_info))
|
|
+ scan_ch->type |= (1 << 7);
|
|
+
|
|
+ scan_ch->active_dwell = cpu_to_le16(active_dwell);
|
|
+ scan_ch->passive_dwell = cpu_to_le16(passive_dwell);
|
|
+
|
|
+ /* Set power levels to defaults */
|
|
+ scan_ch->tpc.dsp_atten = 110;
|
|
+ /* scan_pwr_info->tpc.dsp_atten; */
|
|
+
|
|
+ /*scan_pwr_info->tpc.tx_gain; */
|
|
+ if (phymode == MODE_IEEE80211A)
|
|
+ scan_ch->tpc.tx_gain = ((1 << 5) | (3 << 3)) | 3;
|
|
+ else {
|
|
+ scan_ch->tpc.tx_gain = ((1 << 5) | (5 << 3));
|
|
+ /* NOTE: if we were doing 6Mb OFDM for scans we'd use
|
|
+ * power level
|
|
+ scan_ch->tpc.tx_gain = ((1<<5) | (2 << 3)) | 3;
|
|
+ */
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_SCAN("Scanning %d [%s %d]\n",
|
|
+ scan_ch->channel,
|
|
+ (scan_ch->type & 1) ? "ACTIVE" : "PASSIVE",
|
|
+ (scan_ch->type & 1) ?
|
|
+ active_dwell : passive_dwell);
|
|
+
|
|
+ scan_ch++;
|
|
+ added++;
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_SCAN("total channels to scan %d \n", added);
|
|
+ return added;
|
|
+}
|
|
+
|
|
+static void iwl_reset_channel_flag(struct iwl_priv *priv)
|
|
+{
|
|
+ int i, j;
|
|
+ for (i = 0; i < 3; i++) {
|
|
+ struct ieee80211_hw_mode *hw_mode = (void *)&priv->modes[i];
|
|
+ for (j = 0; j < hw_mode->num_channels; j++)
|
|
+ hw_mode->channels[j].flag = hw_mode->channels[j].val;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void iwl_init_hw_rates(struct iwl_priv *priv,
|
|
+ struct ieee80211_rate *rates)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < IWL_RATE_COUNT; i++) {
|
|
+ rates[i].rate = iwl_rates[i].ieee * 5;
|
|
+ rates[i].val = i; /* Rate scaling will work on indexes */
|
|
+ rates[i].val2 = i;
|
|
+ rates[i].flags = IEEE80211_RATE_SUPPORTED;
|
|
+ /* Only OFDM have the bits-per-symbol set */
|
|
+ if ((i <= IWL_LAST_OFDM_RATE) && (i >= IWL_FIRST_OFDM_RATE))
|
|
+ rates[i].flags |= IEEE80211_RATE_OFDM;
|
|
+ else {
|
|
+ /*
|
|
+ * If CCK 1M then set rate flag to CCK else CCK_2
|
|
+ * which is CCK | PREAMBLE2
|
|
+ */
|
|
+ rates[i].flags |= (iwl_rates[i].plcp == 10) ?
|
|
+ IEEE80211_RATE_CCK : IEEE80211_RATE_CCK_2;
|
|
+ }
|
|
+
|
|
+ /* Set up which ones are basic rates... */
|
|
+ if (IWL_BASIC_RATES_MASK & (1 << i))
|
|
+ rates[i].flags |= IEEE80211_RATE_BASIC;
|
|
+ }
|
|
+
|
|
+#if IWL == 4965
|
|
+ iwl4965_init_hw_rates(priv, rates);
|
|
+#endif
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_init_geos - Initialize mac80211's geo/channel info based from eeprom
|
|
+ */
|
|
+static int iwl_init_geos(struct iwl_priv *priv)
|
|
+{
|
|
+ struct iwl_channel_info *ch;
|
|
+ struct ieee80211_hw_mode *modes;
|
|
+ struct ieee80211_channel *channels;
|
|
+ struct ieee80211_channel *geo_ch;
|
|
+ struct ieee80211_rate *rates;
|
|
+ int i = 0;
|
|
+#if IWL == 4965
|
|
+ enum {
|
|
+ A = 0,
|
|
+ B = 1,
|
|
+ G = 2,
|
|
+ A_11N = 3,
|
|
+ G_11N = 4,
|
|
+ };
|
|
+ int mode_count = 5;
|
|
+#else
|
|
+ enum {
|
|
+ A = 0,
|
|
+ B = 1,
|
|
+ G = 2,
|
|
+ };
|
|
+ int mode_count = 3;
|
|
+#endif
|
|
+
|
|
+ if (priv->modes) {
|
|
+ IWL_DEBUG_INFO("Geography modes already initialized.\n");
|
|
+ set_bit(STATUS_GEO_CONFIGURED, &priv->status);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ modes = kzalloc(sizeof(struct ieee80211_hw_mode) * mode_count,
|
|
+ GFP_KERNEL);
|
|
+ if (!modes)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ channels = kzalloc(sizeof(struct ieee80211_channel) *
|
|
+ priv->channel_count, GFP_KERNEL);
|
|
+ if (!channels) {
|
|
+ kfree(modes);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ rates = kzalloc((sizeof(struct ieee80211_rate) * (IWL_MAX_RATES + 1)),
|
|
+ GFP_KERNEL);
|
|
+ if (!rates) {
|
|
+ kfree(modes);
|
|
+ kfree(channels);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ /* 0 = 802.11a
|
|
+ * 1 = 802.11b
|
|
+ * 2 = 802.11g
|
|
+ */
|
|
+
|
|
+ /* 5.2GHz channels start after the 2.4GHz channels */
|
|
+ modes[A].mode = MODE_IEEE80211A;
|
|
+ modes[A].channels = &channels[ARRAY_SIZE(iwl_eeprom_band_1)];
|
|
+ modes[A].rates = rates;
|
|
+ modes[A].num_rates = 8; /* just OFDM */
|
|
+#if IWL == 4965
|
|
+ modes[A].rates = &rates[4];
|
|
+#endif
|
|
+ modes[A].num_channels = 0;
|
|
+
|
|
+ modes[B].mode = MODE_IEEE80211B;
|
|
+ modes[B].channels = channels;
|
|
+#if IWL == 3945
|
|
+ modes[B].rates = &rates[8];
|
|
+#elif IWL == 4965
|
|
+ modes[B].rates = rates;
|
|
+#endif
|
|
+ modes[B].num_rates = 4; /* just CCK */
|
|
+ modes[B].num_channels = 0;
|
|
+
|
|
+ modes[G].mode = MODE_IEEE80211G;
|
|
+ modes[G].channels = channels;
|
|
+ modes[G].rates = rates;
|
|
+ modes[G].num_rates = 12; /* OFDM & CCK */
|
|
+ modes[G].num_channels = 0;
|
|
+
|
|
+#if IWL == 4965
|
|
+ modes[G_11N].mode = MODE_IEEE80211G;
|
|
+ modes[G_11N].channels = channels;
|
|
+ modes[G_11N].num_rates = 13; /* OFDM & CCK */
|
|
+ modes[G_11N].rates = rates;
|
|
+ modes[G_11N].num_channels = 0;
|
|
+
|
|
+ modes[A_11N].mode = MODE_IEEE80211A;
|
|
+ modes[A_11N].channels = &channels[ARRAY_SIZE(iwl_eeprom_band_1)];
|
|
+ modes[A_11N].rates = &rates[4];
|
|
+ modes[A_11N].num_rates = 9; /* just OFDM */
|
|
+ modes[A_11N].num_channels = 0;
|
|
+#endif
|
|
+ priv->ieee_channels = channels;
|
|
+ priv->ieee_rates = rates;
|
|
+
|
|
+ iwl_init_hw_rates(priv, rates);
|
|
+
|
|
+ for (i = 0, geo_ch = channels; i < priv->channel_count; i++) {
|
|
+ ch = &priv->channel_info[i];
|
|
+
|
|
+ if (!is_channel_valid(ch)) {
|
|
+ IWL_DEBUG_INFO("Channel %d [%sGHz] is restricted -- "
|
|
+ "skipping.\n",
|
|
+ ch->channel, is_channel_a_band(ch) ?
|
|
+ "5.2" : "2.4");
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (is_channel_a_band(ch)) {
|
|
+ geo_ch = &modes[A].channels[modes[A].num_channels++];
|
|
+#if IWL == 4965
|
|
+ modes[A_11N].num_channels++;
|
|
+#endif
|
|
+ } else {
|
|
+ geo_ch = &modes[B].channels[modes[B].num_channels++];
|
|
+ modes[G].num_channels++;
|
|
+#if IWL == 4965
|
|
+ modes[G_11N].num_channels++;
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ geo_ch->freq = ieee80211chan2mhz(ch->channel);
|
|
+ geo_ch->chan = ch->channel;
|
|
+ geo_ch->power_level = ch->max_power_avg;
|
|
+ geo_ch->antenna_max = 0xff;
|
|
+
|
|
+ if (is_channel_valid(ch)) {
|
|
+ geo_ch->flag = IEEE80211_CHAN_W_SCAN;
|
|
+ if (ch->flags & EEPROM_CHANNEL_IBSS)
|
|
+ geo_ch->flag |= IEEE80211_CHAN_W_IBSS;
|
|
+
|
|
+ if (ch->flags & EEPROM_CHANNEL_ACTIVE)
|
|
+ geo_ch->flag |= IEEE80211_CHAN_W_ACTIVE_SCAN;
|
|
+
|
|
+ if (ch->flags & EEPROM_CHANNEL_RADAR)
|
|
+ geo_ch->flag |= IEEE80211_CHAN_W_RADAR_DETECT;
|
|
+
|
|
+ if (ch->max_power_avg > priv->max_channel_txpower_limit)
|
|
+ priv->max_channel_txpower_limit =
|
|
+ ch->max_power_avg;
|
|
+ }
|
|
+
|
|
+ geo_ch->val = geo_ch->flag;
|
|
+ }
|
|
+
|
|
+ if ((modes[A].num_channels == 0) && priv->is_abg) {
|
|
+ printk(KERN_INFO DRV_NAME
|
|
+ ": Incorrectly detected BG card as ABG. Please send "
|
|
+ "your PCI ID 0x%04X:0x%04X to maintainer.\n",
|
|
+ priv->pci_dev->device, priv->pci_dev->subsystem_device);
|
|
+ priv->is_abg = 0;
|
|
+ }
|
|
+
|
|
+ printk(KERN_INFO DRV_NAME
|
|
+ ": Tunable channels: %d 802.11bg, %d 802.11a channels\n",
|
|
+ modes[G].num_channels, modes[A].num_channels);
|
|
+
|
|
+ /*
|
|
+ * NOTE: We register these in preference of order -- the
|
|
+ * stack doesn't currently (as of 7.0.6 / Apr 24 '07) pick
|
|
+ * a phymode based on rates or AP capabilities but seems to
|
|
+ * configure it purely on if the channel being configured
|
|
+ * is supported by a mode -- and the first match is taken
|
|
+ */
|
|
+
|
|
+ if (modes[G].num_channels)
|
|
+ ieee80211_register_hwmode(priv->hw, &modes[G]);
|
|
+ if (modes[B].num_channels)
|
|
+ ieee80211_register_hwmode(priv->hw, &modes[B]);
|
|
+ if (modes[A].num_channels)
|
|
+ ieee80211_register_hwmode(priv->hw, &modes[A]);
|
|
+
|
|
+ priv->modes = modes;
|
|
+ set_bit(STATUS_GEO_CONFIGURED, &priv->status);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * uCode download functions
|
|
+ *
|
|
+ ******************************************************************************/
|
|
+
|
|
+static void iwl_dealloc_ucode_pci(struct iwl_priv *priv)
|
|
+{
|
|
+ if (priv->ucode_code.v_addr != NULL) {
|
|
+ pci_free_consistent(priv->pci_dev,
|
|
+ priv->ucode_code.len,
|
|
+ priv->ucode_code.v_addr,
|
|
+ priv->ucode_code.p_addr);
|
|
+ priv->ucode_code.v_addr = NULL;
|
|
+ }
|
|
+ if (priv->ucode_data.v_addr != NULL) {
|
|
+ pci_free_consistent(priv->pci_dev,
|
|
+ priv->ucode_data.len,
|
|
+ priv->ucode_data.v_addr,
|
|
+ priv->ucode_data.p_addr);
|
|
+ priv->ucode_data.v_addr = NULL;
|
|
+ }
|
|
+ if (priv->ucode_data_backup.v_addr != NULL) {
|
|
+ pci_free_consistent(priv->pci_dev,
|
|
+ priv->ucode_data_backup.len,
|
|
+ priv->ucode_data_backup.v_addr,
|
|
+ priv->ucode_data_backup.p_addr);
|
|
+ priv->ucode_data_backup.v_addr = NULL;
|
|
+ }
|
|
+ if (priv->ucode_init.v_addr != NULL) {
|
|
+ pci_free_consistent(priv->pci_dev,
|
|
+ priv->ucode_init.len,
|
|
+ priv->ucode_init.v_addr,
|
|
+ priv->ucode_init.p_addr);
|
|
+ priv->ucode_init.v_addr = NULL;
|
|
+ }
|
|
+ if (priv->ucode_init_data.v_addr != NULL) {
|
|
+ pci_free_consistent(priv->pci_dev,
|
|
+ priv->ucode_init_data.len,
|
|
+ priv->ucode_init_data.v_addr,
|
|
+ priv->ucode_init_data.p_addr);
|
|
+ priv->ucode_init_data.v_addr = NULL;
|
|
+ }
|
|
+ if (priv->ucode_boot.v_addr != NULL) {
|
|
+ pci_free_consistent(priv->pci_dev,
|
|
+ priv->ucode_boot.len,
|
|
+ priv->ucode_boot.v_addr,
|
|
+ priv->ucode_boot.p_addr);
|
|
+ priv->ucode_boot.v_addr = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_verify_inst_full - verify runtime uCode image in card vs. host,
|
|
+ * looking at all data.
|
|
+ */
|
|
+static int iwl_verify_inst_full(struct iwl_priv *priv, __le32 * image, u32 len)
|
|
+{
|
|
+ u32 val;
|
|
+ u32 save_len = len;
|
|
+ int rc = 0;
|
|
+ u32 errcnt;
|
|
+
|
|
+ IWL_DEBUG_INFO("ucode inst image size is %u\n", len);
|
|
+
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ iwl_write_restricted(priv, HBUS_TARG_MEM_RADDR, RTC_INST_LOWER_BOUND);
|
|
+
|
|
+ errcnt = 0;
|
|
+ for (; len > 0; len -= sizeof(u32), image++) {
|
|
+ /* read data comes through single port, auto-incr addr */
|
|
+ /* NOTE: Use the debugless read so we don't flood kernel log
|
|
+ * if IWL_DL_IO is set */
|
|
+ val = _iwl_read_restricted(priv, HBUS_TARG_MEM_RDAT);
|
|
+ if (val != le32_to_cpu(*image)) {
|
|
+ IWL_ERROR("uCode INST section is invalid at "
|
|
+ "offset 0x%x, is 0x%x, s/b 0x%x\n",
|
|
+ save_len - len, val, le32_to_cpu(*image));
|
|
+ rc = -EIO;
|
|
+ errcnt++;
|
|
+ if (errcnt >= 20)
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ iwl_release_restricted_access(priv);
|
|
+
|
|
+ if (!errcnt)
|
|
+ IWL_DEBUG_INFO
|
|
+ ("ucode image in INSTRUCTION memory is good\n");
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * iwl_verify_inst_sparse - verify runtime uCode image in card vs. host,
|
|
+ * using sample data 100 bytes apart. If these sample points are good,
|
|
+ * it's a pretty good bet that everything between them is good, too.
|
|
+ */
|
|
+static int iwl_verify_inst_sparse(struct iwl_priv *priv, __le32 *image, u32 len)
|
|
+{
|
|
+ u32 val;
|
|
+ int rc = 0;
|
|
+ u32 errcnt = 0;
|
|
+ u32 i;
|
|
+
|
|
+ IWL_DEBUG_INFO("ucode inst image size is %u\n", len);
|
|
+
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ for (i = 0; i < len; i += 100, image += 100/sizeof(u32)) {
|
|
+ /* read data comes through single port, auto-incr addr */
|
|
+ /* NOTE: Use the debugless read so we don't flood kernel log
|
|
+ * if IWL_DL_IO is set */
|
|
+ iwl_write_restricted(priv, HBUS_TARG_MEM_RADDR,
|
|
+ i + RTC_INST_LOWER_BOUND);
|
|
+ val = _iwl_read_restricted(priv, HBUS_TARG_MEM_RDAT);
|
|
+ if (val != le32_to_cpu(*image)) {
|
|
+#if 0 /* Enable this if you want to see details */
|
|
+ IWL_ERROR("uCode INST section is invalid at "
|
|
+ "offset 0x%x, is 0x%x, s/b 0x%x\n",
|
|
+ i, val, *image);
|
|
+#endif
|
|
+ rc = -EIO;
|
|
+ errcnt++;
|
|
+ if (errcnt >= 3)
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ iwl_release_restricted_access(priv);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * iwl_verify_ucode - determine which instruction image is in SRAM,
|
|
+ * and verify its contents
|
|
+ */
|
|
+static int iwl_verify_ucode(struct iwl_priv *priv)
|
|
+{
|
|
+ __le32 *image;
|
|
+ u32 len;
|
|
+ int rc = 0;
|
|
+
|
|
+ /* Try bootstrap */
|
|
+ image = (__le32 *)priv->ucode_boot.v_addr;
|
|
+ len = priv->ucode_boot.len;
|
|
+ rc = iwl_verify_inst_sparse(priv, image, len);
|
|
+ if (rc == 0) {
|
|
+ IWL_DEBUG_INFO("Bootstrap uCode is good in inst SRAM\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* Try initialize */
|
|
+ image = (__le32 *)priv->ucode_init.v_addr;
|
|
+ len = priv->ucode_init.len;
|
|
+ rc = iwl_verify_inst_sparse(priv, image, len);
|
|
+ if (rc == 0) {
|
|
+ IWL_DEBUG_INFO("Initialize uCode is good in inst SRAM\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* Try runtime/protocol */
|
|
+ image = (__le32 *)priv->ucode_code.v_addr;
|
|
+ len = priv->ucode_code.len;
|
|
+ rc = iwl_verify_inst_sparse(priv, image, len);
|
|
+ if (rc == 0) {
|
|
+ IWL_DEBUG_INFO("Runtime uCode is good in inst SRAM\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ IWL_ERROR("NO VALID UCODE IMAGE IN INSTRUCTION SRAM!!\n");
|
|
+
|
|
+ /* Show first several data entries in instruction SRAM.
|
|
+ * Selection of bootstrap image is arbitrary. */
|
|
+ image = (__le32 *)priv->ucode_boot.v_addr;
|
|
+ len = priv->ucode_boot.len;
|
|
+ rc = iwl_verify_inst_full(priv, image, len);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
+/* check contents of special bootstrap uCode SRAM */
|
|
+static int iwl_verify_bsm(struct iwl_priv *priv)
|
|
+{
|
|
+ __le32 *image = priv->ucode_boot.v_addr;
|
|
+ u32 len = priv->ucode_boot.len;
|
|
+ u32 reg;
|
|
+ u32 val;
|
|
+
|
|
+ IWL_DEBUG_INFO("Begin verify bsm\n");
|
|
+
|
|
+ /* verify BSM SRAM contents */
|
|
+ val = iwl_read_restricted_reg(priv, BSM_WR_DWCOUNT_REG);
|
|
+ for (reg = BSM_SRAM_LOWER_BOUND;
|
|
+ reg < BSM_SRAM_LOWER_BOUND + len;
|
|
+ reg += sizeof(u32), image ++) {
|
|
+ val = iwl_read_restricted_reg(priv, reg);
|
|
+ if (val != le32_to_cpu(*image)) {
|
|
+ IWL_ERROR("BSM uCode verification failed at "
|
|
+ "addr 0x%08X+%u (of %u), is 0x%x, s/b 0x%x\n",
|
|
+ BSM_SRAM_LOWER_BOUND,
|
|
+ reg - BSM_SRAM_LOWER_BOUND, len,
|
|
+ val, le32_to_cpu(*image));
|
|
+ return -EIO;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_INFO("BSM bootstrap uCode image OK\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_load_bsm - Load bootstrap instructions
|
|
+ *
|
|
+ * BSM operation:
|
|
+ *
|
|
+ * The Bootstrap State Machine (BSM) stores a short bootstrap uCode program
|
|
+ * in special SRAM that does not power down during RFKILL. When powering back
|
|
+ * up after power-saving sleeps (or during initial uCode load), the BSM loads
|
|
+ * the bootstrap program into the on-board processor, and starts it.
|
|
+ *
|
|
+ * The bootstrap program loads (via DMA) instructions and data for a new
|
|
+ * program from host DRAM locations indicated by the host driver in the
|
|
+ * BSM_DRAM_* registers. Once the new program is loaded, it starts
|
|
+ * automatically.
|
|
+ *
|
|
+ * When initializing the NIC, the host driver points the BSM to the
|
|
+ * "initialize" uCode image. This uCode sets up some internal data, then
|
|
+ * notifies host via "initialize alive" that it is complete.
|
|
+ *
|
|
+ * The host then replaces the BSM_DRAM_* pointer values to point to the
|
|
+ * normal runtime uCode instructions and a backup uCode data cache buffer
|
|
+ * (filled initially with starting data values for the on-board processor),
|
|
+ * then triggers the "initialize" uCode to load and launch the runtime uCode,
|
|
+ * which begins normal operation.
|
|
+ *
|
|
+ * When doing a power-save shutdown, runtime uCode saves data SRAM into
|
|
+ * the backup data cache in DRAM before SRAM is powered down.
|
|
+ *
|
|
+ * When powering back up, the BSM loads the bootstrap program. This reloads
|
|
+ * the runtime uCode instructions and the backup data cache into SRAM,
|
|
+ * and re-launches the runtime uCode from where it left off.
|
|
+ */
|
|
+static int iwl_load_bsm(struct iwl_priv *priv)
|
|
+{
|
|
+ __le32 *image = priv->ucode_boot.v_addr;
|
|
+ u32 len = priv->ucode_boot.len;
|
|
+ dma_addr_t pinst;
|
|
+ dma_addr_t pdata;
|
|
+ u32 inst_len;
|
|
+ u32 data_len;
|
|
+ int rc;
|
|
+ int i;
|
|
+ u32 done;
|
|
+ u32 reg_offset;
|
|
+
|
|
+ IWL_DEBUG_INFO("Begin load bsm\n");
|
|
+
|
|
+ /* make sure bootstrap program is no larger than BSM's SRAM size */
|
|
+ if (len > IWL_MAX_BSM_SIZE)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* Tell bootstrap uCode where to find the "Initialize" uCode
|
|
+ * in host DRAM ... bits 31:0 for 3945, bits 35:4 for 4965.
|
|
+ * NOTE: iwl_initialize_alive_start() will replace these values,
|
|
+ * after the "initialize" uCode has run, to point to
|
|
+ * runtime/protocol instructions and backup data cache. */
|
|
+#if IWL == 3945
|
|
+ pinst = priv->ucode_init.p_addr;
|
|
+ pdata = priv->ucode_init_data.p_addr;
|
|
+#elif IWL == 4965
|
|
+ pinst = priv->ucode_init.p_addr >> 4;
|
|
+ pdata = priv->ucode_init_data.p_addr >> 4;
|
|
+#endif
|
|
+ inst_len = priv->ucode_init.len;
|
|
+ data_len = priv->ucode_init_data.len;
|
|
+
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ iwl_write_restricted_reg(priv, BSM_DRAM_INST_PTR_REG, pinst);
|
|
+ iwl_write_restricted_reg(priv, BSM_DRAM_DATA_PTR_REG, pdata);
|
|
+ iwl_write_restricted_reg(priv, BSM_DRAM_INST_BYTECOUNT_REG, inst_len);
|
|
+ iwl_write_restricted_reg(priv, BSM_DRAM_DATA_BYTECOUNT_REG, data_len);
|
|
+
|
|
+ /* Fill BSM memory with bootstrap instructions */
|
|
+ for (reg_offset = BSM_SRAM_LOWER_BOUND;
|
|
+ reg_offset < BSM_SRAM_LOWER_BOUND + len;
|
|
+ reg_offset += sizeof(u32), image++)
|
|
+ _iwl_write_restricted_reg(priv, reg_offset,
|
|
+ le32_to_cpu(*image));
|
|
+
|
|
+ rc = iwl_verify_bsm(priv);
|
|
+ if (rc) {
|
|
+ iwl_release_restricted_access(priv);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ /* Tell BSM to copy from BSM SRAM into instruction SRAM, when asked */
|
|
+ iwl_write_restricted_reg(priv, BSM_WR_MEM_SRC_REG, 0x0);
|
|
+ iwl_write_restricted_reg(priv, BSM_WR_MEM_DST_REG,
|
|
+ RTC_INST_LOWER_BOUND);
|
|
+ iwl_write_restricted_reg(priv, BSM_WR_DWCOUNT_REG, len / sizeof(u32));
|
|
+
|
|
+ /* Load bootstrap code into instruction SRAM now,
|
|
+ * to prepare to load "initialize" uCode */
|
|
+ iwl_write_restricted_reg(priv, BSM_WR_CTRL_REG,
|
|
+ BSM_WR_CTRL_REG_BIT_START);
|
|
+
|
|
+ /* Wait for load of bootstrap uCode to finish */
|
|
+ for (i = 0; i < 100; i++) {
|
|
+ done = iwl_read_restricted_reg(priv, BSM_WR_CTRL_REG);
|
|
+ if (!(done & BSM_WR_CTRL_REG_BIT_START))
|
|
+ break;
|
|
+ udelay(10);
|
|
+ }
|
|
+ if (i < 100)
|
|
+ IWL_DEBUG_INFO("BSM write complete, poll %d iterations\n", i);
|
|
+ else {
|
|
+ IWL_ERROR("BSM write did not complete!\n");
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ /* Enable future boot loads whenever power management unit triggers it
|
|
+ * (e.g. when powering back up after power-save shutdown) */
|
|
+ iwl_write_restricted_reg(priv, BSM_WR_CTRL_REG,
|
|
+ BSM_WR_CTRL_REG_BIT_START_EN);
|
|
+
|
|
+ iwl_release_restricted_access(priv);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void iwl_nic_start(struct iwl_priv *priv)
|
|
+{
|
|
+ /* Remove all resets to allow NIC to operate */
|
|
+ iwl_write32(priv, CSR_RESET, 0);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_read_ucode - Read uCode images from disk file.
|
|
+ *
|
|
+ * Copy into buffers for card to fetch via bus-mastering
|
|
+ */
|
|
+static int iwl_read_ucode(struct iwl_priv *priv)
|
|
+{
|
|
+ struct iwl_ucode *ucode;
|
|
+ int rc = 0;
|
|
+ const struct firmware *ucode_raw;
|
|
+#if IWL == 3945
|
|
+ /* firmware file name contains uCode/driver compatibility version */
|
|
+ const char *name = "iwlwifi-3945" IWL3945_UCODE_API ".ucode";
|
|
+#elif IWL == 4965
|
|
+ const char *name = "iwlwifi-4965" IWL4965_UCODE_API ".ucode";
|
|
+#endif
|
|
+ u8 *src;
|
|
+ size_t len;
|
|
+ u32 ver, inst_size, data_size, init_size, init_data_size, boot_size;
|
|
+
|
|
+ /* Ask kernel firmware_class module to get the boot firmware off disk.
|
|
+ * request_firmware() is synchronous, file is in memory on return. */
|
|
+ rc = request_firmware(&ucode_raw, name, &priv->pci_dev->dev);
|
|
+ if (rc < 0) {
|
|
+ IWL_ERROR("%s firmware file req failed: Reason %d\n", name, rc);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_INFO("Got firmware '%s' file (%zd bytes) from disk\n",
|
|
+ name, ucode_raw->size);
|
|
+
|
|
+ /* Make sure that we got at least our header! */
|
|
+ if (ucode_raw->size < sizeof(*ucode)) {
|
|
+ IWL_ERROR("File size way too small!\n");
|
|
+ rc = -EINVAL;
|
|
+ goto err_release;
|
|
+ }
|
|
+
|
|
+ /* Data from ucode file: header followed by uCode images */
|
|
+ ucode = (void *)ucode_raw->data;
|
|
+
|
|
+ ver = le32_to_cpu(ucode->ver);
|
|
+ inst_size = le32_to_cpu(ucode->inst_size);
|
|
+ data_size = le32_to_cpu(ucode->data_size);
|
|
+ init_size = le32_to_cpu(ucode->init_size);
|
|
+ init_data_size = le32_to_cpu(ucode->init_data_size);
|
|
+ boot_size = le32_to_cpu(ucode->boot_size);
|
|
+
|
|
+ IWL_DEBUG_INFO("f/w package hdr ucode version = 0x%x\n", ver);
|
|
+ IWL_DEBUG_INFO("f/w package hdr runtime inst size = %u\n",
|
|
+ inst_size);
|
|
+ IWL_DEBUG_INFO("f/w package hdr runtime data size = %u\n",
|
|
+ data_size);
|
|
+ IWL_DEBUG_INFO("f/w package hdr init inst size = %u\n",
|
|
+ init_size);
|
|
+ IWL_DEBUG_INFO("f/w package hdr init data size = %u\n",
|
|
+ init_data_size);
|
|
+ IWL_DEBUG_INFO("f/w package hdr boot inst size = %u\n",
|
|
+ boot_size);
|
|
+
|
|
+ /* Verify size of file vs. image size info in file's header */
|
|
+ if (ucode_raw->size < sizeof(*ucode) +
|
|
+ inst_size + data_size + init_size +
|
|
+ init_data_size + boot_size) {
|
|
+
|
|
+ IWL_DEBUG_INFO("uCode file size %d too small\n",
|
|
+ (int)ucode_raw->size);
|
|
+ rc = -EINVAL;
|
|
+ goto err_release;
|
|
+ }
|
|
+
|
|
+ /* Verify that uCode images will fit in card's SRAM */
|
|
+ if (inst_size > IWL_MAX_INST_SIZE) {
|
|
+ IWL_DEBUG_INFO("uCode instr len %d too large to fit in card\n",
|
|
+ (int)inst_size);
|
|
+ rc = -EINVAL;
|
|
+ goto err_release;
|
|
+ }
|
|
+
|
|
+ if (data_size > IWL_MAX_DATA_SIZE) {
|
|
+ IWL_DEBUG_INFO("uCode data len %d too large to fit in card\n",
|
|
+ (int)data_size);
|
|
+ rc = -EINVAL;
|
|
+ goto err_release;
|
|
+ }
|
|
+ if (init_size > IWL_MAX_INST_SIZE) {
|
|
+ IWL_DEBUG_INFO
|
|
+ ("uCode init instr len %d too large to fit in card\n",
|
|
+ (int)init_size);
|
|
+ rc = -EINVAL;
|
|
+ goto err_release;
|
|
+ }
|
|
+ if (init_data_size > IWL_MAX_DATA_SIZE) {
|
|
+ IWL_DEBUG_INFO
|
|
+ ("uCode init data len %d too large to fit in card\n",
|
|
+ (int)init_data_size);
|
|
+ rc = -EINVAL;
|
|
+ goto err_release;
|
|
+ }
|
|
+ if (boot_size > IWL_MAX_BSM_SIZE) {
|
|
+ IWL_DEBUG_INFO
|
|
+ ("uCode boot instr len %d too large to fit in bsm\n",
|
|
+ (int)boot_size);
|
|
+ rc = -EINVAL;
|
|
+ goto err_release;
|
|
+ }
|
|
+
|
|
+ /* Allocate ucode buffers for card's bus-master loading ... */
|
|
+
|
|
+ /* Runtime instructions and 2 copies of data:
|
|
+ * 1) unmodified from disk
|
|
+ * 2) backup cache for save/restore during power-downs */
|
|
+ priv->ucode_code.len = inst_size;
|
|
+ priv->ucode_code.v_addr =
|
|
+ pci_alloc_consistent(priv->pci_dev,
|
|
+ priv->ucode_code.len,
|
|
+ &(priv->ucode_code.p_addr));
|
|
+
|
|
+ priv->ucode_data.len = data_size;
|
|
+ priv->ucode_data.v_addr =
|
|
+ pci_alloc_consistent(priv->pci_dev,
|
|
+ priv->ucode_data.len,
|
|
+ &(priv->ucode_data.p_addr));
|
|
+
|
|
+ priv->ucode_data_backup.len = data_size;
|
|
+ priv->ucode_data_backup.v_addr =
|
|
+ pci_alloc_consistent(priv->pci_dev,
|
|
+ priv->ucode_data_backup.len,
|
|
+ &(priv->ucode_data_backup.p_addr));
|
|
+
|
|
+
|
|
+ /* Initialization instructions and data */
|
|
+ priv->ucode_init.len = init_size;
|
|
+ priv->ucode_init.v_addr =
|
|
+ pci_alloc_consistent(priv->pci_dev,
|
|
+ priv->ucode_init.len,
|
|
+ &(priv->ucode_init.p_addr));
|
|
+
|
|
+ priv->ucode_init_data.len = init_data_size;
|
|
+ priv->ucode_init_data.v_addr =
|
|
+ pci_alloc_consistent(priv->pci_dev,
|
|
+ priv->ucode_init_data.len,
|
|
+ &(priv->ucode_init_data.p_addr));
|
|
+
|
|
+ /* Bootstrap (instructions only, no data) */
|
|
+ priv->ucode_boot.len = boot_size;
|
|
+ priv->ucode_boot.v_addr =
|
|
+ pci_alloc_consistent(priv->pci_dev,
|
|
+ priv->ucode_boot.len,
|
|
+ &(priv->ucode_boot.p_addr));
|
|
+
|
|
+ if (!priv->ucode_code.v_addr || !priv->ucode_data.v_addr ||
|
|
+ !priv->ucode_init.v_addr || !priv->ucode_init_data.v_addr ||
|
|
+ !priv->ucode_boot.v_addr || !priv->ucode_data_backup.v_addr)
|
|
+ goto err_pci_alloc;
|
|
+
|
|
+ /* Copy images into buffers for card's bus-master reads ... */
|
|
+
|
|
+ /* Runtime instructions (first block of data in file) */
|
|
+ src = &ucode->data[0];
|
|
+ len = priv->ucode_code.len;
|
|
+ IWL_DEBUG_INFO("Copying (but not loading) uCode instr len %d\n",
|
|
+ (int)len);
|
|
+ memcpy(priv->ucode_code.v_addr, src, len);
|
|
+ IWL_DEBUG_INFO("uCode instr buf vaddr = 0x%p, paddr = 0x%08x\n",
|
|
+ priv->ucode_code.v_addr, (u32)priv->ucode_code.p_addr);
|
|
+
|
|
+ /* Runtime data (2nd block)
|
|
+ * NOTE: Copy into backup buffer will be done in iwl_up() */
|
|
+ src = &ucode->data[inst_size];
|
|
+ len = priv->ucode_data.len;
|
|
+ IWL_DEBUG_INFO("Copying (but not loading) uCode data len %d\n",
|
|
+ (int)len);
|
|
+ memcpy(priv->ucode_data.v_addr, src, len);
|
|
+ memcpy(priv->ucode_data_backup.v_addr, src, len);
|
|
+
|
|
+ /* Initialization instructions (3rd block) */
|
|
+ if (init_size) {
|
|
+ src = &ucode->data[inst_size + data_size];
|
|
+ len = priv->ucode_init.len;
|
|
+ IWL_DEBUG_INFO("Copying (but not loading) init instr len %d\n",
|
|
+ (int)len);
|
|
+ memcpy(priv->ucode_init.v_addr, src, len);
|
|
+ }
|
|
+
|
|
+ /* Initialization data (4th block) */
|
|
+ if (init_data_size) {
|
|
+ src = &ucode->data[inst_size + data_size + init_size];
|
|
+ len = priv->ucode_init_data.len;
|
|
+ IWL_DEBUG_INFO("Copying (but not loading) init data len %d\n",
|
|
+ (int)len);
|
|
+ memcpy(priv->ucode_init_data.v_addr, src, len);
|
|
+ }
|
|
+
|
|
+ /* Bootstrap instructions (5th block) */
|
|
+ src = &ucode->data[inst_size + data_size + init_size + init_data_size];
|
|
+ len = priv->ucode_boot.len;
|
|
+ IWL_DEBUG_INFO("Copying (but not loading) boot instr len %d\n",
|
|
+ (int)len);
|
|
+ memcpy(priv->ucode_boot.v_addr, src, len);
|
|
+
|
|
+ /* We have our copies now, allow OS release its copies */
|
|
+ release_firmware(ucode_raw);
|
|
+ return 0;
|
|
+
|
|
+ err_pci_alloc:
|
|
+ IWL_ERROR("failed to allocate pci memory\n");
|
|
+ rc = -ENOMEM;
|
|
+ iwl_dealloc_ucode_pci(priv);
|
|
+
|
|
+ err_release:
|
|
+ release_firmware(ucode_raw);
|
|
+
|
|
+ error:
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * iwl_set_ucode_ptrs - Set uCode address location
|
|
+ *
|
|
+ * Tell initialization uCode where to find runtime uCode.
|
|
+ *
|
|
+ * BSM registers initially contain pointers to initialization uCode.
|
|
+ * We need to replace them to load runtime uCode inst and data,
|
|
+ * and to save runtime data when powering down.
|
|
+ */
|
|
+static int iwl_set_ucode_ptrs(struct iwl_priv *priv)
|
|
+{
|
|
+ dma_addr_t pinst;
|
|
+ dma_addr_t pdata;
|
|
+ int rc = 0;
|
|
+ unsigned long flags;
|
|
+
|
|
+#if IWL == 3945
|
|
+ /* bits 31:0 for 3945 */
|
|
+ pinst = priv->ucode_code.p_addr;
|
|
+ pdata = priv->ucode_data_backup.p_addr;
|
|
+#else
|
|
+ /* bits 35:4 for 4965 */
|
|
+ pinst = priv->ucode_code.p_addr >> 4;
|
|
+ pdata = priv->ucode_data_backup.p_addr >> 4;
|
|
+#endif
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc) {
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ /* Tell bootstrap uCode where to find image to load */
|
|
+ iwl_write_restricted_reg(priv, BSM_DRAM_INST_PTR_REG, pinst);
|
|
+ iwl_write_restricted_reg(priv, BSM_DRAM_DATA_PTR_REG, pdata);
|
|
+ iwl_write_restricted_reg(priv, BSM_DRAM_DATA_BYTECOUNT_REG,
|
|
+ priv->ucode_data.len);
|
|
+
|
|
+ /* Inst bytecount must be last to set up, bit 31 signals uCode
|
|
+ * that all new ptr/size info is in place */
|
|
+ iwl_write_restricted_reg(priv, BSM_DRAM_INST_BYTECOUNT_REG,
|
|
+ priv->ucode_code.len | BSM_DRAM_INST_LOAD);
|
|
+
|
|
+ iwl_release_restricted_access(priv);
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ IWL_DEBUG_INFO("Runtime uCode pointers are set.\n");
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_init_alive_start - Called after REPLY_ALIVE notification receieved
|
|
+ *
|
|
+ * Called after REPLY_ALIVE notification received from "initialize" uCode.
|
|
+ *
|
|
+ * The 4965 "initialize" ALIVE reply contains calibration data for:
|
|
+ * Voltage, temperature, and MIMO tx gain correction, now stored in priv
|
|
+ * (3945 does not contain this data).
|
|
+ *
|
|
+ * Tell "initialize" uCode to go ahead and load the runtime uCode.
|
|
+*/
|
|
+static void iwl_init_alive_start(struct iwl_priv *priv)
|
|
+{
|
|
+ /* Check alive response for "valid" sign from uCode */
|
|
+ if (priv->card_alive_init.is_valid != UCODE_VALID_OK) {
|
|
+ /* We had an error bringing up the hardware, so take it
|
|
+ * all the way back down so we can try again */
|
|
+ IWL_DEBUG_INFO("Initialize Alive failed.\n");
|
|
+ goto restart;
|
|
+ }
|
|
+
|
|
+ /* Bootstrap uCode has loaded initialize uCode ... verify inst image.
|
|
+ * This is a paranoid check, because we would not have gotten the
|
|
+ * "initialize" alive if code weren't properly loaded. */
|
|
+ if (iwl_verify_ucode(priv)) {
|
|
+ /* Runtime instruction load was bad;
|
|
+ * take it all the way back down so we can try again */
|
|
+ IWL_DEBUG_INFO("Bad \"initialize\" uCode load.\n");
|
|
+ goto restart;
|
|
+ }
|
|
+
|
|
+#if IWL == 4965
|
|
+ /* Calculate temperature */
|
|
+ priv->temperature = iwl4965_get_temperature(priv);
|
|
+#endif
|
|
+
|
|
+ /* Send pointers to protocol/runtime uCode image ... init code will
|
|
+ * load and launch runtime uCode, which will send us another "Alive"
|
|
+ * notification. */
|
|
+ IWL_DEBUG_INFO("Initialization Alive received.\n");
|
|
+ if (iwl_set_ucode_ptrs(priv)) {
|
|
+ /* Runtime instruction load won't happen;
|
|
+ * take it all the way back down so we can try again */
|
|
+ IWL_DEBUG_INFO("Couldn't set up uCode pointers.\n");
|
|
+ goto restart;
|
|
+ }
|
|
+ return;
|
|
+
|
|
+ restart:
|
|
+ queue_work(priv->workqueue, &priv->restart);
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * iwl_alive_start - called after REPLY_ALIVE notification received
|
|
+ * from protocol/runtime uCode (initialization uCode's
|
|
+ * Alive gets handled by iwl_init_alive_start()).
|
|
+ */
|
|
+static void iwl_alive_start(struct iwl_priv *priv)
|
|
+{
|
|
+ int rc = 0;
|
|
+#if IWL == 3945
|
|
+ int thermal_spin = 0;
|
|
+ u32 rfkill;
|
|
+#endif
|
|
+
|
|
+ IWL_DEBUG_INFO("Runtime Alive received.\n");
|
|
+
|
|
+ if (priv->card_alive.is_valid != UCODE_VALID_OK) {
|
|
+ /* We had an error bringing up the hardware, so take it
|
|
+ * all the way back down so we can try again */
|
|
+ IWL_DEBUG_INFO("Alive failed.\n");
|
|
+ goto restart;
|
|
+ }
|
|
+
|
|
+ /* Initialize uCode has loaded Runtime uCode ... verify inst image.
|
|
+ * This is a paranoid check, because we would not have gotten the
|
|
+ * "runtime" alive if code weren't properly loaded. */
|
|
+ if (iwl_verify_ucode(priv)) {
|
|
+ /* Runtime instruction load was bad;
|
|
+ * take it all the way back down so we can try again */
|
|
+ IWL_DEBUG_INFO("Bad runtime uCode load.\n");
|
|
+ goto restart;
|
|
+ }
|
|
+
|
|
+ iwl_clear_stations_table(priv);
|
|
+
|
|
+#if IWL == 4965
|
|
+ rc = iwl4965_alive_notify(priv);
|
|
+ if (rc) {
|
|
+ IWL_WARNING("Could not complete ALIVE transition [ntf]: %d\n",
|
|
+ rc);
|
|
+ goto restart;
|
|
+ }
|
|
+#elif IWL == 3945
|
|
+ rc = iwl_grab_restricted_access(priv);
|
|
+ if (rc) {
|
|
+ IWL_WARNING("Can not read rfkill status from adapter\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ rfkill = iwl_read_restricted_reg(priv, ALM_APMG_RFKILL);
|
|
+ IWL_DEBUG_INFO("RFKILL status: 0x%x\n", rfkill);
|
|
+ iwl_release_restricted_access(priv);
|
|
+
|
|
+ if (rfkill & 0x1) {
|
|
+ clear_bit(STATUS_RF_KILL_HW, &priv->status);
|
|
+ /* if rfkill is not on, then wait for thermal
|
|
+ * sensor in adapter to kick in */
|
|
+ while (iwl_hw_get_temperature(priv) == 0) {
|
|
+ thermal_spin++;
|
|
+ udelay(10);
|
|
+ }
|
|
+
|
|
+ if (thermal_spin)
|
|
+ IWL_DEBUG_INFO("Thermal calibration took %dus\n",
|
|
+ thermal_spin * 10);
|
|
+ } else
|
|
+ set_bit(STATUS_RF_KILL_HW, &priv->status);
|
|
+#endif
|
|
+
|
|
+ /* After the ALIVE response, we can process host commands */
|
|
+ set_bit(STATUS_ALIVE, &priv->status);
|
|
+
|
|
+ /* Clear out the uCode error bit if it is set */
|
|
+ clear_bit(STATUS_FW_ERROR, &priv->status);
|
|
+
|
|
+ rc = iwl_init_channel_map(priv);
|
|
+ if (rc) {
|
|
+ IWL_ERROR("initializing regulatory failed: %d\n", rc);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ iwl_init_geos(priv);
|
|
+
|
|
+ if (iwl_is_rfkill(priv))
|
|
+ return;
|
|
+
|
|
+ if (!priv->mac80211_registered) {
|
|
+ /* Unlock so any user space entry points can call back into
|
|
+ * the driver without a deadlock... */
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ iwl_rate_control_register(priv->hw);
|
|
+ rc = ieee80211_register_hw(priv->hw);
|
|
+ priv->hw->conf.beacon_int = 100;
|
|
+ mutex_lock(&priv->mutex);
|
|
+
|
|
+ if (rc) {
|
|
+ IWL_ERROR("Failed to register network "
|
|
+ "device (error %d)\n", rc);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ priv->mac80211_registered = 1;
|
|
+
|
|
+ iwl_reset_channel_flag(priv);
|
|
+ } else
|
|
+ ieee80211_start_queues(priv->hw);
|
|
+
|
|
+ priv->active_rate = priv->rates_mask;
|
|
+ priv->active_rate_basic = priv->rates_mask & IWL_BASIC_RATES_MASK;
|
|
+
|
|
+ iwl_send_power_mode(priv, IWL_POWER_LEVEL(priv->power_mode));
|
|
+
|
|
+ if (iwl_is_associated(priv)) {
|
|
+ struct iwl_rxon_cmd *active_rxon =
|
|
+ (struct iwl_rxon_cmd *)(&priv->active_rxon);
|
|
+
|
|
+ memcpy(&priv->staging_rxon, &priv->active_rxon,
|
|
+ sizeof(priv->staging_rxon));
|
|
+ active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
|
|
+ } else {
|
|
+ /* Initialize our rx_config data */
|
|
+ iwl_connection_init_rx_config(priv);
|
|
+ memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN);
|
|
+ }
|
|
+
|
|
+ /* Configure BT coexistence */
|
|
+ iwl_send_bt_config(priv);
|
|
+
|
|
+ /* Configure the adapter for unassociated operation */
|
|
+ iwl_commit_rxon(priv);
|
|
+
|
|
+ /* At this point, the NIC is initialized and operational */
|
|
+ priv->notif_missed_beacons = 0;
|
|
+ set_bit(STATUS_READY, &priv->status);
|
|
+
|
|
+ iwl3945_reg_txpower_periodic(priv);
|
|
+
|
|
+#if IWL == 4965
|
|
+ iwl4965_rf_kill_ct_config(priv);
|
|
+#endif
|
|
+ IWL_DEBUG_INFO("ALIVE processing complete.\n");
|
|
+
|
|
+ if (priv->error_recovering)
|
|
+ iwl_error_recovery(priv);
|
|
+
|
|
+ return;
|
|
+
|
|
+ restart:
|
|
+ queue_work(priv->workqueue, &priv->restart);
|
|
+}
|
|
+
|
|
+static void iwl_cancel_deferred_work(struct iwl_priv *priv);
|
|
+
|
|
+static void __iwl_down(struct iwl_priv *priv)
|
|
+{
|
|
+ unsigned long flags;
|
|
+ int exit_pending = test_bit(STATUS_EXIT_PENDING, &priv->status);
|
|
+ struct ieee80211_conf *conf = NULL;
|
|
+
|
|
+ IWL_WARNING("ipw going down \n");
|
|
+
|
|
+ conf = ieee80211_get_hw_conf(priv->hw);
|
|
+
|
|
+ if (!exit_pending)
|
|
+ set_bit(STATUS_EXIT_PENDING, &priv->status);
|
|
+
|
|
+ iwl_clear_stations_table(priv);
|
|
+
|
|
+ /* Unblock any waiting calls */
|
|
+ wake_up_interruptible_all(&priv->wait_command_queue);
|
|
+
|
|
+ iwl_cancel_deferred_work(priv);
|
|
+
|
|
+ /* Wipe out the EXIT_PENDING status bit if we are not actually
|
|
+ * exiting the module */
|
|
+ if (!exit_pending)
|
|
+ clear_bit(STATUS_EXIT_PENDING, &priv->status);
|
|
+
|
|
+ /* stop and reset the on-board processor */
|
|
+ iwl_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
|
|
+
|
|
+ /* tell the device to stop sending interrupts */
|
|
+ iwl_disable_interrupts(priv);
|
|
+
|
|
+ if (priv->mac80211_registered)
|
|
+ ieee80211_stop_queues(priv->hw);
|
|
+
|
|
+ /* If we have not previously called iwl_init() then
|
|
+ * clear all bits but the RF Kill and SUSPEND bits and return */
|
|
+ if (!iwl_is_init(priv)) {
|
|
+ priv->status = test_bit(STATUS_RF_KILL_HW, &priv->status) <<
|
|
+ STATUS_RF_KILL_HW |
|
|
+ test_bit(STATUS_RF_KILL_SW, &priv->status) <<
|
|
+ STATUS_RF_KILL_SW |
|
|
+ test_bit(STATUS_IN_SUSPEND, &priv->status) <<
|
|
+ STATUS_IN_SUSPEND;
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ /* ...otherwise clear out all the status bits but the RF Kill and
|
|
+ * SUSPEND bits and continue taking the NIC down. */
|
|
+ priv->status &= test_bit(STATUS_RF_KILL_HW, &priv->status) <<
|
|
+ STATUS_RF_KILL_HW |
|
|
+ test_bit(STATUS_RF_KILL_SW, &priv->status) <<
|
|
+ STATUS_RF_KILL_SW |
|
|
+ test_bit(STATUS_IN_SUSPEND, &priv->status) <<
|
|
+ STATUS_IN_SUSPEND |
|
|
+ test_bit(STATUS_FW_ERROR, &priv->status) <<
|
|
+ STATUS_FW_ERROR;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ iwl_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ iwl_hw_txq_ctx_stop(priv);
|
|
+ iwl_hw_rxq_stop(priv);
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ if (!iwl_grab_restricted_access(priv)) {
|
|
+ iwl_write_restricted_reg(priv, ALM_APMG_CLK_DIS,
|
|
+ APMG_CLK_REG_VAL_DMA_CLK_RQT);
|
|
+ iwl_release_restricted_access(priv);
|
|
+ }
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ udelay(5);
|
|
+
|
|
+ iwl_hw_nic_stop_master(priv);
|
|
+ iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
|
|
+ iwl_hw_nic_reset(priv);
|
|
+
|
|
+ exit:
|
|
+ memset(&priv->card_alive, 0, sizeof(struct iwl_alive_resp));
|
|
+
|
|
+ if (priv->ibss_beacon)
|
|
+ dev_kfree_skb(priv->ibss_beacon);
|
|
+ priv->ibss_beacon = NULL;
|
|
+
|
|
+ /* clear out any free frames */
|
|
+ iwl_clear_free_frames(priv);
|
|
+}
|
|
+
|
|
+static void iwl_down(struct iwl_priv *priv)
|
|
+{
|
|
+ mutex_lock(&priv->mutex);
|
|
+ __iwl_down(priv);
|
|
+ mutex_unlock(&priv->mutex);
|
|
+}
|
|
+
|
|
+#define MAX_HW_RESTARTS 5
|
|
+
|
|
+static int __iwl_up(struct iwl_priv *priv)
|
|
+{
|
|
+ int rc, i;
|
|
+
|
|
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
|
|
+ IWL_WARNING("Exit pending; will not bring the NIC up\n");
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ if (test_bit(STATUS_RF_KILL_SW, &priv->status)) {
|
|
+ IWL_WARNING("Radio disabled by SW RF kill (module "
|
|
+ "parameter)\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ iwl_write32(priv, CSR_INT, 0xFFFFFFFF);
|
|
+
|
|
+ rc = iwl_hw_nic_init(priv);
|
|
+ if (rc) {
|
|
+ IWL_ERROR("Unable to int nic\n");
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ /* make sure rfkill handshake bits are cleared */
|
|
+ iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
|
|
+ iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR,
|
|
+ CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
|
|
+
|
|
+ /* clear (again), then enable host interrupts */
|
|
+ iwl_write32(priv, CSR_INT, 0xFFFFFFFF);
|
|
+ iwl_enable_interrupts(priv);
|
|
+
|
|
+ /* really make sure rfkill handshake bits are cleared */
|
|
+ iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
|
|
+ iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
|
|
+
|
|
+ /* Copy original ucode data image from disk into backup cache.
|
|
+ * This will be used to initialize the on-board processor's
|
|
+ * data SRAM for a clean start when the runtime program first loads. */
|
|
+ memcpy(priv->ucode_data_backup.v_addr, priv->ucode_data.v_addr,
|
|
+ priv->ucode_data.len);
|
|
+
|
|
+#if IWL == 4965
|
|
+ {
|
|
+ u32 hw_rf_kill = 0;
|
|
+
|
|
+ /* If platform's RF_KILL switch is set to KILL,
|
|
+ * wait for BIT_INT_RF_KILL interrupt before loading uCode
|
|
+ * and getting things started */
|
|
+ if (!(iwl_read32(priv, CSR_GP_CNTRL) &
|
|
+ CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW))
|
|
+ hw_rf_kill = 1;
|
|
+
|
|
+ if (test_bit(STATUS_RF_KILL_HW, &priv->status) || hw_rf_kill) {
|
|
+ IWL_WARNING("Radio disabled by HW RF Kill switch\n");
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ for (i = 0; i < MAX_HW_RESTARTS; i++) {
|
|
+
|
|
+ iwl_clear_stations_table(priv);
|
|
+
|
|
+ /* load bootstrap state machine,
|
|
+ * load bootstrap program into processor's memory,
|
|
+ * prepare to load the "initialize" uCode */
|
|
+ rc = iwl_load_bsm(priv);
|
|
+
|
|
+ if (rc) {
|
|
+ IWL_ERROR("Unable to set up bootstrap uCode: %d\n", rc);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ /* start card; "initialize" will load runtime ucode */
|
|
+ iwl_nic_start(priv);
|
|
+
|
|
+ /* MAC Address location in EEPROM same for 3945/4965 */
|
|
+ get_eeprom_mac(priv, priv->mac_addr);
|
|
+ IWL_DEBUG_INFO("MAC address: " MAC_FMT "\n",
|
|
+ MAC_ARG(priv->mac_addr));
|
|
+
|
|
+ SET_IEEE80211_PERM_ADDR(priv->hw, priv->mac_addr);
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ set_bit(STATUS_EXIT_PENDING, &priv->status);
|
|
+ __iwl_down(priv);
|
|
+
|
|
+ /* tried to restart and config the device for as long as our
|
|
+ * patience could withstand */
|
|
+ IWL_ERROR("Unable to initialize device after %d attempts.\n", i);
|
|
+ return -EIO;
|
|
+}
|
|
+
|
|
+
|
|
+/*****************************************************************************
|
|
+ *
|
|
+ * Workqueue callbacks
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+static void iwl_bg_init_alive_start(struct work_struct *data)
|
|
+{
|
|
+ struct iwl_priv *priv =
|
|
+ container_of(data, struct iwl_priv, init_alive_start.work);
|
|
+
|
|
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
|
+ return;
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+ iwl_init_alive_start(priv);
|
|
+ mutex_unlock(&priv->mutex);
|
|
+}
|
|
+
|
|
+static void iwl_bg_alive_start(struct work_struct *data)
|
|
+{
|
|
+ struct iwl_priv *priv =
|
|
+ container_of(data, struct iwl_priv, alive_start.work);
|
|
+
|
|
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
|
+ return;
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+ iwl_alive_start(priv);
|
|
+ mutex_unlock(&priv->mutex);
|
|
+}
|
|
+
|
|
+static void iwl_bg_rf_kill(struct work_struct *work)
|
|
+{
|
|
+ struct iwl_priv *priv = container_of(work, struct iwl_priv, rf_kill);
|
|
+
|
|
+ wake_up_interruptible(&priv->wait_command_queue);
|
|
+
|
|
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
|
+ return;
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+
|
|
+ if (!iwl_is_rfkill(priv)) {
|
|
+ IWL_DEBUG(IWL_DL_INFO | IWL_DL_RF_KILL,
|
|
+ "HW and/or SW RF Kill no longer active, restarting "
|
|
+ "device\n");
|
|
+ if (!test_bit(STATUS_EXIT_PENDING, &priv->status))
|
|
+ queue_work(priv->workqueue, &priv->restart);
|
|
+ } else {
|
|
+
|
|
+ if (!test_bit(STATUS_RF_KILL_HW, &priv->status))
|
|
+ IWL_DEBUG_RF_KILL("Can not turn radio back on - "
|
|
+ "disabled by SW switch\n");
|
|
+ else
|
|
+ IWL_WARNING("Radio Frequency Kill Switch is On:\n"
|
|
+ "Kill switch must be turned off for "
|
|
+ "wireless networking to work.\n");
|
|
+ }
|
|
+ mutex_unlock(&priv->mutex);
|
|
+}
|
|
+
|
|
+#define IWL_SCAN_CHECK_WATCHDOG (7 * HZ)
|
|
+
|
|
+static void iwl_bg_scan_check(struct work_struct *data)
|
|
+{
|
|
+ struct iwl_priv *priv =
|
|
+ container_of(data, struct iwl_priv, scan_check.work);
|
|
+
|
|
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
|
+ return;
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+ if (test_bit(STATUS_SCANNING, &priv->status) ||
|
|
+ test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
|
|
+ IWL_DEBUG(IWL_DL_INFO | IWL_DL_SCAN,
|
|
+ "Scan completion watchdog resetting adapter (%dms)\n",
|
|
+ jiffies_to_msecs(IWL_SCAN_CHECK_WATCHDOG));
|
|
+ if (!test_bit(STATUS_EXIT_PENDING, &priv->status))
|
|
+ queue_work(priv->workqueue, &priv->restart);
|
|
+ }
|
|
+ mutex_unlock(&priv->mutex);
|
|
+}
|
|
+
|
|
+static void iwl_bg_request_scan(struct work_struct *data)
|
|
+{
|
|
+ struct iwl_priv *priv =
|
|
+ container_of(data, struct iwl_priv, request_scan);
|
|
+ struct iwl_host_cmd cmd = {
|
|
+ .id = REPLY_SCAN_CMD,
|
|
+ .len = sizeof(struct iwl_scan_cmd),
|
|
+ .meta.flags = CMD_SIZE_HUGE,
|
|
+ };
|
|
+ int rc = 0;
|
|
+ struct iwl_scan_cmd *scan;
|
|
+ struct ieee80211_conf *conf = NULL;
|
|
+ u8 direct_mask;
|
|
+ int phymode;
|
|
+
|
|
+ conf = ieee80211_get_hw_conf(priv->hw);
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+
|
|
+ if (!iwl_is_ready(priv)) {
|
|
+ IWL_WARNING("request scan called when driver not ready.\n");
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ /* Make sure the scan wasn't cancelled before this queued work
|
|
+ * was given the chance to run... */
|
|
+ if (!test_bit(STATUS_SCANNING, &priv->status))
|
|
+ goto done;
|
|
+
|
|
+ /* This should never be called or scheduled if there is currently
|
|
+ * a scan active in the hardware. */
|
|
+ if (test_bit(STATUS_SCAN_HW, &priv->status)) {
|
|
+ IWL_DEBUG_INFO("Multiple concurrent scan requests in parallel. "
|
|
+ "Ignoring second request.\n");
|
|
+ rc = -EIO;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
|
|
+ IWL_DEBUG_SCAN("Aborting scan due to device shutdown\n");
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
|
|
+ IWL_DEBUG_HC("Scan request while abort pending. Queuing.\n");
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (iwl_is_rfkill(priv)) {
|
|
+ IWL_DEBUG_HC("Aborting scan due to RF Kill activation\n");
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (!test_bit(STATUS_READY, &priv->status)) {
|
|
+ IWL_DEBUG_HC("Scan request while uninitialized. Queuing.\n");
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (!priv->scan_bands) {
|
|
+ IWL_DEBUG_HC("Aborting scan due to no requested bands\n");
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (!priv->scan) {
|
|
+ priv->scan = kmalloc(sizeof(struct iwl_scan_cmd) +
|
|
+ IWL_MAX_SCAN_SIZE, GFP_KERNEL);
|
|
+ if (!priv->scan) {
|
|
+ rc = -ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+ scan = priv->scan;
|
|
+ memset(scan, 0, sizeof(struct iwl_scan_cmd) + IWL_MAX_SCAN_SIZE);
|
|
+
|
|
+ scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH;
|
|
+ scan->quiet_time = IWL_ACTIVE_QUIET_TIME;
|
|
+
|
|
+ if (iwl_is_associated(priv)) {
|
|
+ u16 interval = 0;
|
|
+ u32 extra;
|
|
+ u32 suspend_time = 100;
|
|
+ u32 scan_suspend_time = 100;
|
|
+ unsigned long flags;
|
|
+
|
|
+ IWL_DEBUG_INFO("Scanning while associated...\n");
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ interval = priv->beacon_int;
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ scan->suspend_time = 0;
|
|
+ scan->max_out_time = cpu_to_le32(600 * 1024);
|
|
+ if (!interval)
|
|
+ interval = suspend_time;
|
|
+#if IWL == 3945
|
|
+ /*
|
|
+ * suspend time format:
|
|
+ * 0-19: beacon interval in usec (time before exec.)
|
|
+ * 20-23: 0
|
|
+ * 24-31: number of beacons (suspend between channels)
|
|
+ */
|
|
+
|
|
+ extra = (suspend_time / interval) << 24;
|
|
+ scan_suspend_time = 0xFF0FFFFF &
|
|
+ (extra | ((suspend_time % interval) * 1024));
|
|
+#else
|
|
+ extra = (suspend_time / interval) << 22;
|
|
+ scan_suspend_time = (extra |
|
|
+ ((suspend_time % interval) * 1024));
|
|
+#endif
|
|
+ scan->suspend_time = cpu_to_le32(scan_suspend_time);
|
|
+ IWL_DEBUG_SCAN("suspend_time 0x%X beacon interval %d\n",
|
|
+ scan_suspend_time, interval);
|
|
+ }
|
|
+
|
|
+ /* We should add the ability for user to lock to PASSIVE ONLY */
|
|
+ if (priv->one_direct_scan) {
|
|
+ IWL_DEBUG_SCAN
|
|
+ ("Kicking off one direct scan for '%s'\n",
|
|
+ iwl_escape_essid(priv->direct_ssid,
|
|
+ priv->direct_ssid_len));
|
|
+ scan->direct_scan[0].id = WLAN_EID_SSID;
|
|
+ scan->direct_scan[0].len = priv->direct_ssid_len;
|
|
+ memcpy(scan->direct_scan[0].ssid,
|
|
+ priv->direct_ssid, priv->direct_ssid_len);
|
|
+ direct_mask = 1;
|
|
+ } else if (!iwl_is_associated(priv)) {
|
|
+ scan->direct_scan[0].id = WLAN_EID_SSID;
|
|
+ scan->direct_scan[0].len = priv->essid_len;
|
|
+ memcpy(scan->direct_scan[0].ssid, priv->essid, priv->essid_len);
|
|
+ direct_mask = 1;
|
|
+ } else
|
|
+ direct_mask = 0;
|
|
+
|
|
+ /* We don't build a direct scan probe request; the uCode will do
|
|
+ * that based on the direct_mask added to each channel entry */
|
|
+ scan->tx_cmd.len = cpu_to_le16(
|
|
+ iwl_fill_probe_req(priv, (struct ieee80211_mgmt *)scan->data,
|
|
+ IWL_MAX_SCAN_SIZE - sizeof(scan), 0));
|
|
+ scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK;
|
|
+ scan->tx_cmd.sta_id = IWL_BROADCAST_ID;
|
|
+ scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
|
|
+
|
|
+ /* flags + rate selection */
|
|
+
|
|
+#if IWL == 4965
|
|
+ scan->tx_cmd.tx_flags |= cpu_to_le32(0x200);
|
|
+#endif
|
|
+
|
|
+ switch (priv->scan_bands) {
|
|
+ case 2:
|
|
+ scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK;
|
|
+#if IWL == 3945
|
|
+ scan->tx_cmd.rate = IWL_RATE_1M_PLCP;
|
|
+#elif IWL == 4965
|
|
+ scan->tx_cmd.rate_n_flags =
|
|
+ iwl_hw_set_rate_n_flags(IWL_RATE_1M_PLCP,
|
|
+ RATE_MCS_ANT_B_MSK|RATE_MCS_CCK_MSK);
|
|
+#endif
|
|
+
|
|
+ scan->good_CRC_th = 0;
|
|
+ phymode = MODE_IEEE80211G;
|
|
+ break;
|
|
+
|
|
+ case 1:
|
|
+#if IWL == 3945
|
|
+ scan->tx_cmd.rate = IWL_RATE_6M_PLCP;
|
|
+#elif IWL == 4965
|
|
+ scan->tx_cmd.rate_n_flags =
|
|
+ iwl_hw_set_rate_n_flags(IWL_RATE_6M_PLCP,
|
|
+ RATE_MCS_ANT_B_MSK);
|
|
+#endif
|
|
+ scan->good_CRC_th = IWL_GOOD_CRC_TH;
|
|
+ phymode = MODE_IEEE80211A;
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ IWL_WARNING("Invalid scan band count\n");
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ /* select Rx antennas/chains */
|
|
+#if IWL == 3945
|
|
+ scan->flags |= iwl3945_get_antenna_flags(priv);
|
|
+
|
|
+#elif IWL == 4965
|
|
+ /* Force use of chains B and C (0x6) for scan Rx.
|
|
+ * Avoid A (0x1) because of its off-channel reception on A-band.
|
|
+ * MIMO is not used here, but value is required to make uCode happy. */
|
|
+ scan->rx_chain = RXON_RX_CHAIN_DRIVER_FORCE_MSK |
|
|
+ cpu_to_le16((0x7 << RXON_RX_CHAIN_VALID_POS) |
|
|
+ (0x6 << RXON_RX_CHAIN_FORCE_SEL_POS) |
|
|
+ (0x7 << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS));
|
|
+#endif
|
|
+
|
|
+ if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR)
|
|
+ scan->filter_flags = RXON_FILTER_PROMISC_MSK;
|
|
+
|
|
+ if (direct_mask)
|
|
+ IWL_DEBUG_SCAN
|
|
+ ("Initiating direct scan for %s.\n",
|
|
+ iwl_escape_essid(priv->essid, priv->essid_len));
|
|
+ else
|
|
+ IWL_DEBUG_SCAN("Initiating indirect scan.\n");
|
|
+
|
|
+ scan->channel_count =
|
|
+ iwl_get_channels_for_scan(
|
|
+ priv, phymode, 1, /* active */
|
|
+ direct_mask,
|
|
+ (void *)&scan->data[le16_to_cpu(scan->tx_cmd.len)]);
|
|
+
|
|
+ cmd.len += le16_to_cpu(scan->tx_cmd.len) +
|
|
+ scan->channel_count * sizeof(struct iwl_scan_channel);
|
|
+ cmd.data = scan;
|
|
+ scan->len = cpu_to_le16(cmd.len);
|
|
+
|
|
+ set_bit(STATUS_SCAN_HW, &priv->status);
|
|
+ rc = iwl_send_cmd_sync(priv, &cmd);
|
|
+ if (rc)
|
|
+ goto done;
|
|
+
|
|
+ queue_delayed_work(priv->workqueue, &priv->scan_check,
|
|
+ IWL_SCAN_CHECK_WATCHDOG);
|
|
+
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ return;
|
|
+
|
|
+ done:
|
|
+ /* inform mac80211 sacn aborted */
|
|
+ queue_work(priv->workqueue, &priv->scan_completed);
|
|
+ mutex_unlock(&priv->mutex);
|
|
+}
|
|
+
|
|
+static void iwl_bg_up(struct work_struct *data)
|
|
+{
|
|
+ struct iwl_priv *priv = container_of(data, struct iwl_priv, up);
|
|
+
|
|
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
|
+ return;
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+ __iwl_up(priv);
|
|
+ mutex_unlock(&priv->mutex);
|
|
+}
|
|
+
|
|
+static void iwl_bg_restart(struct work_struct *data)
|
|
+{
|
|
+ struct iwl_priv *priv = container_of(data, struct iwl_priv, restart);
|
|
+
|
|
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
|
+ return;
|
|
+
|
|
+ iwl_down(priv);
|
|
+ queue_work(priv->workqueue, &priv->up);
|
|
+}
|
|
+
|
|
+static void iwl_bg_rx_replenish(struct work_struct *data)
|
|
+{
|
|
+ struct iwl_priv *priv =
|
|
+ container_of(data, struct iwl_priv, rx_replenish);
|
|
+
|
|
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
|
+ return;
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+ iwl_rx_replenish(priv);
|
|
+ mutex_unlock(&priv->mutex);
|
|
+}
|
|
+
|
|
+static void iwl_bg_post_associate(struct work_struct *data)
|
|
+{
|
|
+ struct iwl_priv *priv = container_of(data, struct iwl_priv,
|
|
+ post_associate.work);
|
|
+
|
|
+ int rc = 0;
|
|
+ struct ieee80211_conf *conf = NULL;
|
|
+
|
|
+ IWL_DEBUG_ASSOC("Associated as %d to: " MAC_FMT "\n",
|
|
+ priv->assoc_id, MAC_ARG(priv->active_rxon.bssid_addr));
|
|
+
|
|
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
|
+ return;
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+
|
|
+ conf = ieee80211_get_hw_conf(priv->hw);
|
|
+
|
|
+ priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
|
|
+ iwl_commit_rxon(priv);
|
|
+
|
|
+ memset(&priv->rxon_timing, 0, sizeof(struct iwl_rxon_time_cmd));
|
|
+ iwl_setup_rxon_timing(priv);
|
|
+ rc = iwl_send_cmd_pdu(priv, REPLY_RXON_TIMING,
|
|
+ sizeof(priv->rxon_timing), &priv->rxon_timing);
|
|
+ if (rc)
|
|
+ IWL_WARNING("REPLY_RXON_TIMING failed - "
|
|
+ "Attempting to continue.\n");
|
|
+
|
|
+ priv->staging_rxon.filter_flags |= RXON_FILTER_ASSOC_MSK;
|
|
+
|
|
+#if IWL == 4965
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+ if (priv->is_ht_enabled && priv->current_assoc_ht.is_ht)
|
|
+ iwl4965_set_rxon_ht(priv, &priv->current_assoc_ht);
|
|
+ else {
|
|
+ priv->active_rate_ht[0] = 0;
|
|
+ priv->active_rate_ht[1] = 0;
|
|
+ priv->current_channel_width = IWL_CHANNEL_WIDTH_20MHZ;
|
|
+ }
|
|
+#endif /* CONFIG_IWLWIFI_HT*/
|
|
+ iwl4965_set_rxon_chain(priv);
|
|
+#endif
|
|
+ priv->staging_rxon.assoc_id = cpu_to_le16(priv->assoc_id);
|
|
+
|
|
+ IWL_DEBUG_ASSOC("assoc id %d beacon interval %d\n",
|
|
+ priv->assoc_id, priv->beacon_int);
|
|
+
|
|
+ if (priv->assoc_capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
|
|
+ priv->staging_rxon.flags |= RXON_FLG_SHORT_PREAMBLE_MSK;
|
|
+ else
|
|
+ priv->staging_rxon.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK;
|
|
+
|
|
+ if (priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK) {
|
|
+ if (priv->assoc_capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
|
|
+ priv->staging_rxon.flags |= RXON_FLG_SHORT_SLOT_MSK;
|
|
+ else
|
|
+ priv->staging_rxon.flags &= ~RXON_FLG_SHORT_SLOT_MSK;
|
|
+
|
|
+ if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS)
|
|
+ priv->staging_rxon.flags &= ~RXON_FLG_SHORT_SLOT_MSK;
|
|
+
|
|
+ }
|
|
+
|
|
+ iwl_commit_rxon(priv);
|
|
+
|
|
+ switch (priv->iw_mode) {
|
|
+ case IEEE80211_IF_TYPE_STA:
|
|
+ iwl_rate_scale_init(priv->hw, IWL_AP_ID);
|
|
+ break;
|
|
+
|
|
+ case IEEE80211_IF_TYPE_IBSS:
|
|
+
|
|
+ /* clear out the station table */
|
|
+ iwl_clear_stations_table(priv);
|
|
+
|
|
+ iwl_rxon_add_station(priv, BROADCAST_ADDR, 0);
|
|
+ iwl_rxon_add_station(priv, priv->bssid, 0);
|
|
+ iwl3945_sync_sta(priv, IWL_STA_ID,
|
|
+ (priv->phymode == MODE_IEEE80211A)?
|
|
+ IWL_RATE_6M_PLCP : IWL_RATE_1M_PLCP,
|
|
+ CMD_ASYNC);
|
|
+ iwl_rate_scale_init(priv->hw, IWL_STA_ID);
|
|
+ iwl_send_beacon_cmd(priv);
|
|
+
|
|
+ break;
|
|
+
|
|
+ case IEEE80211_IF_TYPE_AP:
|
|
+
|
|
+ /* clear out the station table */
|
|
+ iwl_clear_stations_table(priv);
|
|
+
|
|
+ iwl_rxon_add_station(priv, BROADCAST_ADDR, 0);
|
|
+ iwl_send_beacon_cmd(priv);
|
|
+
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* FIXME: not sure why this doesn't work in AP mode */
|
|
+ if (priv->iw_mode != IEEE80211_IF_TYPE_AP)
|
|
+ iwl_sequence_reset(priv);
|
|
+
|
|
+#if IWL == 4965
|
|
+#ifdef CONFIG_IWLWIFI_SENSITIVITY
|
|
+ /* Enable Rx differential gain and sensitivity calibrations */
|
|
+ iwl4965_chain_noise_reset(priv);
|
|
+ priv->start_calib = 1;
|
|
+#endif /* CONFIG_IWLWIFI_SENSITIVITY */
|
|
+#endif /* IWL == 4965 */
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_QOS
|
|
+ iwl_activate_qos(priv, 0);
|
|
+#endif /* CONFIG_IWLWIFI_QOS */
|
|
+ mutex_unlock(&priv->mutex);
|
|
+}
|
|
+
|
|
+static void iwl_bg_abort_scan(struct work_struct *work)
|
|
+{
|
|
+ struct iwl_priv *priv = container_of(work, struct iwl_priv,
|
|
+ abort_scan);
|
|
+
|
|
+ if (!iwl_is_ready(priv))
|
|
+ return;
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+
|
|
+ set_bit(STATUS_SCAN_ABORTING, &priv->status);
|
|
+ iwl_send_scan_abort(priv);
|
|
+
|
|
+ mutex_unlock(&priv->mutex);
|
|
+}
|
|
+
|
|
+static void iwl_bg_scan_completed(struct work_struct *work)
|
|
+{
|
|
+ struct iwl_priv *priv =
|
|
+ container_of(work, struct iwl_priv, scan_completed);
|
|
+
|
|
+ IWL_DEBUG(IWL_DL_INFO | IWL_DL_SCAN, "SCAN complete scan\n");
|
|
+
|
|
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
|
+ return;
|
|
+
|
|
+ ieee80211_scan_completed(priv->hw);
|
|
+
|
|
+ /* Since setting the TXPOWER may have been deferred while
|
|
+ * performing the scan, fire one off */
|
|
+ mutex_lock(&priv->mutex);
|
|
+ iwl_hw_reg_send_txpower(priv);
|
|
+ mutex_unlock(&priv->mutex);
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+ *
|
|
+ * mac80211 entry point functions
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+static int iwl_mac_open(struct ieee80211_hw *hw)
|
|
+{
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
+
|
|
+ IWL_DEBUG_MAC80211("enter\n");
|
|
+
|
|
+ /* we should be verifying the device is ready to be opened */
|
|
+ mutex_lock(&priv->mutex);
|
|
+
|
|
+ priv->is_open = 1;
|
|
+
|
|
+ if (!iwl_is_rfkill(priv))
|
|
+ ieee80211_start_queues(priv->hw);
|
|
+
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ IWL_DEBUG_MAC80211("leave\n");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int iwl_mac_stop(struct ieee80211_hw *hw)
|
|
+{
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
+
|
|
+ IWL_DEBUG_MAC80211("enter\n");
|
|
+ priv->is_open = 0;
|
|
+ /*netif_stop_queue(dev); */
|
|
+ flush_workqueue(priv->workqueue);
|
|
+ IWL_DEBUG_MAC80211("leave\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int iwl_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb,
|
|
+ struct ieee80211_tx_control *ctl)
|
|
+{
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
+
|
|
+ IWL_DEBUG_MAC80211("enter\n");
|
|
+
|
|
+ if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) {
|
|
+ IWL_DEBUG_MAC80211("leave - monitor\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_TX("dev->xmit(%d bytes) at rate 0x%02x\n", skb->len,
|
|
+ ctl->tx_rate);
|
|
+
|
|
+ if (iwl_tx_skb(priv, skb, ctl))
|
|
+ dev_kfree_skb_any(skb);
|
|
+
|
|
+ IWL_DEBUG_MAC80211("leave\n");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int iwl_mac_add_interface(struct ieee80211_hw *hw,
|
|
+ struct ieee80211_if_init_conf *conf)
|
|
+{
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
+ unsigned long flags;
|
|
+
|
|
+ IWL_DEBUG_MAC80211("enter - id %d, type %d, MAC " MAC_FMT "\n",
|
|
+ conf->if_id, conf->type, MAC_ARG(conf->mac_addr));
|
|
+
|
|
+ if (priv->interface_id) {
|
|
+ IWL_DEBUG_MAC80211("leave - interface_id != 0\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ priv->interface_id = conf->if_id;
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+ iwl_set_mode(priv, conf->type);
|
|
+
|
|
+ IWL_DEBUG_MAC80211("leave\n");
|
|
+ mutex_unlock(&priv->mutex);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_mac_config - mac80211 config callback
|
|
+ *
|
|
+ * We ignore conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME since it seems to
|
|
+ * be set inappropriately and the driver currently sets the hardware up to
|
|
+ * use it whenever needed.
|
|
+ */
|
|
+static int iwl_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf)
|
|
+{
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
+ const struct iwl_channel_info *ch_info;
|
|
+ unsigned long flags;
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+ IWL_DEBUG_MAC80211("enter to channel %d\n", conf->channel);
|
|
+
|
|
+ if (!iwl_is_ready(priv)) {
|
|
+ IWL_DEBUG_MAC80211("leave - not ready\n");
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ /* TODO: Figure out how to get ieee80211_local->sta_scanning w/ only
|
|
+ * what is exposed through include/ declrations */
|
|
+ if (unlikely(!iwl_param_disable_hw_scan &&
|
|
+ test_bit(STATUS_SCANNING, &priv->status))) {
|
|
+ IWL_DEBUG_MAC80211("leave - scanning\n");
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+
|
|
+ ch_info = iwl_get_channel_info(priv, conf->phymode, conf->channel);
|
|
+ if (!is_channel_valid(ch_info)) {
|
|
+ IWL_DEBUG_SCAN("Channel %d [%d] is INVALID for this SKU.\n",
|
|
+ conf->channel, conf->phymode);
|
|
+ IWL_DEBUG_MAC80211("leave - invalid channel\n");
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+#if IWL == 4965
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+ /* if we are switching fron ht to 2.4 clear flags
|
|
+ * from any ht related info since 2.4 does not
|
|
+ * support ht */
|
|
+ if ((le16_to_cpu(priv->staging_rxon.channel) != conf->channel)
|
|
+#ifdef IEEE80211_CONF_CHANNEL_SWITCH
|
|
+ && !(conf->flags & IEEE80211_CONF_CHANNEL_SWITCH)
|
|
+#endif
|
|
+ )
|
|
+ priv->staging_rxon.flags = 0;
|
|
+#endif /* CONFIG_IWLWIFI_HT */
|
|
+#endif /* IWL == 4965 */
|
|
+
|
|
+ iwl_set_rxon_channel(priv, conf->phymode, conf->channel);
|
|
+
|
|
+ iwl_set_flags_for_phymode(priv, conf->phymode);
|
|
+
|
|
+ /* The list of supported rates and rate mask can be different
|
|
+ * for each phymode; since the phymode may have changed, reset
|
|
+ * the rate mask to what mac80211 lists */
|
|
+ iwl_set_rate(priv);
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+#ifdef IEEE80211_CONF_CHANNEL_SWITCH
|
|
+ if (conf->flags & IEEE80211_CONF_CHANNEL_SWITCH) {
|
|
+ iwl_hw_channel_switch(priv, conf->channel);
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ return 0;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ iwl_radio_kill_sw(priv, !conf->radio_enabled);
|
|
+
|
|
+ if (!conf->radio_enabled) {
|
|
+ IWL_DEBUG_MAC80211("leave - radio disabled\n");
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (iwl_is_rfkill(priv)) {
|
|
+ IWL_DEBUG_MAC80211("leave - RF kill\n");
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ iwl_set_rate(priv);
|
|
+
|
|
+ if (memcmp(&priv->active_rxon,
|
|
+ &priv->staging_rxon, sizeof(priv->staging_rxon)))
|
|
+ iwl_commit_rxon(priv);
|
|
+ else
|
|
+ IWL_DEBUG_INFO("No re-sending same RXON configuration.\n");
|
|
+
|
|
+ IWL_DEBUG_MAC80211("leave\n");
|
|
+
|
|
+ mutex_unlock(&priv->mutex);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void iwl_config_ap(struct iwl_priv *priv)
|
|
+{
|
|
+ int rc = 0;
|
|
+
|
|
+ if (priv->status & STATUS_EXIT_PENDING)
|
|
+ return;
|
|
+
|
|
+ /* The following should be done only at AP bring up */
|
|
+ if ((priv->active_rxon.filter_flags & RXON_FILTER_ASSOC_MSK) == 0) {
|
|
+
|
|
+ /* RXON - unassoc (to set timing command) */
|
|
+ priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
|
|
+ iwl_commit_rxon(priv);
|
|
+
|
|
+ /* RXON Timing */
|
|
+ memset(&priv->rxon_timing, 0, sizeof(struct iwl_rxon_time_cmd));
|
|
+ iwl_setup_rxon_timing(priv);
|
|
+ rc = iwl_send_cmd_pdu(priv, REPLY_RXON_TIMING,
|
|
+ sizeof(priv->rxon_timing), &priv->rxon_timing);
|
|
+ if (rc)
|
|
+ IWL_WARNING("REPLY_RXON_TIMING failed - "
|
|
+ "Attempting to continue.\n");
|
|
+
|
|
+ iwl4965_set_rxon_chain(priv);
|
|
+
|
|
+ /* FIXME: what should be the assoc_id for AP? */
|
|
+ priv->staging_rxon.assoc_id = cpu_to_le16(priv->assoc_id);
|
|
+ if (priv->assoc_capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
|
|
+ priv->staging_rxon.flags |=
|
|
+ RXON_FLG_SHORT_PREAMBLE_MSK;
|
|
+ else
|
|
+ priv->staging_rxon.flags &=
|
|
+ ~RXON_FLG_SHORT_PREAMBLE_MSK;
|
|
+
|
|
+ if (priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK) {
|
|
+ if (priv->assoc_capability &
|
|
+ WLAN_CAPABILITY_SHORT_SLOT_TIME)
|
|
+ priv->staging_rxon.flags |=
|
|
+ RXON_FLG_SHORT_SLOT_MSK;
|
|
+ else
|
|
+ priv->staging_rxon.flags &=
|
|
+ ~RXON_FLG_SHORT_SLOT_MSK;
|
|
+
|
|
+ if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS)
|
|
+ priv->staging_rxon.flags &=
|
|
+ ~RXON_FLG_SHORT_SLOT_MSK;
|
|
+ }
|
|
+ /* restore RXON assoc */
|
|
+ priv->staging_rxon.filter_flags |= RXON_FILTER_ASSOC_MSK;
|
|
+ iwl_commit_rxon(priv);
|
|
+ iwl_rxon_add_station(priv, BROADCAST_ADDR, 0);
|
|
+ iwl_send_beacon_cmd(priv);
|
|
+ } else
|
|
+ iwl_send_beacon_cmd(priv);
|
|
+
|
|
+ /* FIXME - we need to add code here to detect a totally new
|
|
+ * configuration, reset the AP, unassoc, rxon timing, assoc,
|
|
+ * clear sta table, add BCAST sta... */
|
|
+}
|
|
+
|
|
+static int iwl_mac_config_interface(struct ieee80211_hw *hw, int if_id,
|
|
+ struct ieee80211_if_conf *conf)
|
|
+{
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
+ unsigned long flags;
|
|
+ int rc;
|
|
+
|
|
+ if (conf == NULL)
|
|
+ return -EIO;
|
|
+
|
|
+ if ((priv->iw_mode == IEEE80211_IF_TYPE_AP) &&
|
|
+ (!conf->beacon || !conf->ssid_len)) {
|
|
+ IWL_DEBUG_MAC80211
|
|
+ ("Leaving in AP mode because HostAPD is not ready.\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+
|
|
+ IWL_DEBUG_MAC80211("enter: interface id %d\n", if_id);
|
|
+ if (conf->bssid)
|
|
+ IWL_DEBUG_MAC80211("bssid: " MAC_FMT "\n",
|
|
+ MAC_ARG(conf->bssid));
|
|
+
|
|
+ if (unlikely(test_bit(STATUS_SCANNING, &priv->status)) &&
|
|
+ !(priv->hw->flags & IEEE80211_HW_NO_PROBE_FILTERING)) {
|
|
+ IWL_DEBUG_MAC80211("leave - scanning\n");
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (priv->interface_id != if_id) {
|
|
+ IWL_DEBUG_MAC80211("leave - interface_id != if_id\n");
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (priv->iw_mode == IEEE80211_IF_TYPE_AP) {
|
|
+ if (!conf->bssid) {
|
|
+ conf->bssid = priv->mac_addr;
|
|
+ memcpy(priv->bssid, priv->mac_addr, ETH_ALEN);
|
|
+ IWL_DEBUG_MAC80211("bssid was set to: " MAC_FMT "\n",
|
|
+ MAC_ARG(conf->bssid));
|
|
+ }
|
|
+ if (priv->ibss_beacon)
|
|
+ dev_kfree_skb(priv->ibss_beacon);
|
|
+
|
|
+ priv->ibss_beacon = conf->beacon;
|
|
+ }
|
|
+
|
|
+ if (conf->bssid && !is_zero_ether_addr(conf->bssid) &&
|
|
+ !is_multicast_ether_addr(conf->bssid)) {
|
|
+ /* If there is currently a HW scan going on in the background
|
|
+ * then we need to cancel it else the RXON below will fail. */
|
|
+ if (iwl_scan_cancel_timeout(priv, 100)) {
|
|
+ IWL_WARNING("Aborted scan still in progress "
|
|
+ "after 100ms\n");
|
|
+ IWL_DEBUG_MAC80211("leaving - scan abort failed.\n");
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ return -EAGAIN;
|
|
+ }
|
|
+ memcpy(priv->staging_rxon.bssid_addr, conf->bssid, ETH_ALEN);
|
|
+
|
|
+ /* TODO: Audit driver for usage of these members and see
|
|
+ * if mac80211 deprecates them (priv->bssid looks like it
|
|
+ * shouldn't be there, but I haven't scanned the IBSS code
|
|
+ * to verify) - jpk */
|
|
+ memcpy(priv->bssid, conf->bssid, ETH_ALEN);
|
|
+
|
|
+ if (priv->iw_mode == IEEE80211_IF_TYPE_AP)
|
|
+ iwl_config_ap(priv);
|
|
+ else {
|
|
+ priv->staging_rxon.filter_flags |=
|
|
+ RXON_FILTER_ASSOC_MSK;
|
|
+ rc = iwl_commit_rxon(priv);
|
|
+ if ((priv->iw_mode == IEEE80211_IF_TYPE_STA) && rc)
|
|
+ iwl_rxon_add_station(
|
|
+ priv, priv->active_rxon.bssid_addr, 1);
|
|
+ }
|
|
+
|
|
+ } else {
|
|
+ priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
|
|
+ iwl_commit_rxon(priv);
|
|
+ }
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ if (!conf->ssid_len)
|
|
+ memset(priv->essid, 0, IW_ESSID_MAX_SIZE);
|
|
+ else
|
|
+ memcpy(priv->essid, conf->ssid, conf->ssid_len);
|
|
+
|
|
+ priv->essid_len = conf->ssid_len;
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ IWL_DEBUG_MAC80211("leave\n");
|
|
+ mutex_unlock(&priv->mutex);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void iwl_mac_remove_interface(struct ieee80211_hw *hw,
|
|
+ struct ieee80211_if_init_conf *conf)
|
|
+{
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
+
|
|
+ IWL_DEBUG_MAC80211("enter\n");
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+ if (priv->interface_id == conf->if_id) {
|
|
+ priv->interface_id = 0;
|
|
+ memset(priv->bssid, 0, ETH_ALEN);
|
|
+ memset(priv->essid, 0, IW_ESSID_MAX_SIZE);
|
|
+ priv->essid_len = 0;
|
|
+ }
|
|
+ mutex_unlock(&priv->mutex);
|
|
+
|
|
+ IWL_DEBUG_MAC80211("leave\n");
|
|
+
|
|
+}
|
|
+
|
|
+#define IWL_DELAY_NEXT_SCAN (HZ*2)
|
|
+static int iwl_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t len)
|
|
+{
|
|
+ int rc = 0;
|
|
+ unsigned long flags;
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
+
|
|
+ IWL_DEBUG_MAC80211("enter\n");
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+
|
|
+ if (!iwl_is_ready_rf(priv)) {
|
|
+ rc = -EIO;
|
|
+ IWL_DEBUG_MAC80211("leave - not ready or exit pending\n");
|
|
+ goto out_unlock;
|
|
+ }
|
|
+
|
|
+ if (priv->iw_mode == IEEE80211_IF_TYPE_AP) { /* APs don't scan */
|
|
+ rc = -EIO;
|
|
+ IWL_ERROR("ERROR: APs don't scan\n");
|
|
+ goto out_unlock;
|
|
+ }
|
|
+
|
|
+ /* if we just finished scan ask for delay */
|
|
+ if (priv->last_scan_jiffies &&
|
|
+ time_after(priv->last_scan_jiffies + IWL_DELAY_NEXT_SCAN,
|
|
+ jiffies)) {
|
|
+ rc = -EAGAIN;
|
|
+ goto out_unlock;
|
|
+ }
|
|
+ if (len) {
|
|
+ IWL_DEBUG_SCAN("direct scan for "
|
|
+ "%s [%d]\n ",
|
|
+ iwl_escape_essid(ssid, len), (int)len);
|
|
+
|
|
+ priv->one_direct_scan = 1;
|
|
+ priv->direct_ssid_len = (u8)
|
|
+ min((u8) len, (u8) IW_ESSID_MAX_SIZE);
|
|
+ memcpy(priv->direct_ssid, ssid, priv->direct_ssid_len);
|
|
+ }
|
|
+
|
|
+ rc = iwl_scan_initiate(priv);
|
|
+
|
|
+ IWL_DEBUG_MAC80211("leave\n");
|
|
+
|
|
+out_unlock:
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int iwl_mac_set_key(struct ieee80211_hw *hw, set_key_cmd cmd, u8 *addr,
|
|
+ struct ieee80211_key_conf *key, int aid)
|
|
+{
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
+ int rc = 0;
|
|
+ u8 sta_id;
|
|
+
|
|
+ IWL_DEBUG_MAC80211("enter\n");
|
|
+
|
|
+ if (!iwl_param_hwcrypto) {
|
|
+ IWL_DEBUG_MAC80211("leave - hwcrypto disabled\n");
|
|
+ return -EOPNOTSUPP;
|
|
+ }
|
|
+
|
|
+ sta_id = iwl_hw_find_station(priv, addr);
|
|
+ if (sta_id == IWL_INVALID_STATION) {
|
|
+ IWL_DEBUG_MAC80211("leave - " MAC_FMT " not in station map.\n",
|
|
+ MAC_ARG(addr));
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+
|
|
+ if (cmd == SET_KEY)
|
|
+ rc = iwl_update_sta_key_info(priv, key, sta_id);
|
|
+ else
|
|
+ rc = -EINVAL;
|
|
+
|
|
+ if (!rc) {
|
|
+ iwl_set_rxon_hwcrypto(priv, 1);
|
|
+ iwl_commit_rxon(priv);
|
|
+ key->flags &= ~IEEE80211_KEY_FORCE_SW_ENCRYPT;
|
|
+ key->hw_key_idx = sta_id;
|
|
+ IWL_DEBUG_MAC80211("set_key success, using hwcrypto\n");
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_MAC80211("leave\n");
|
|
+ mutex_unlock(&priv->mutex);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int iwl_mac_conf_tx(struct ieee80211_hw *hw, int queue,
|
|
+ const struct ieee80211_tx_queue_params *params)
|
|
+{
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
+#ifdef CONFIG_IWLWIFI_QOS
|
|
+ unsigned long flags;
|
|
+ int i;
|
|
+#endif /* CONFIG_IWL_QOS */
|
|
+
|
|
+ IWL_DEBUG_MAC80211("enter\n");
|
|
+
|
|
+ if (!iwl_is_ready_rf(priv)) {
|
|
+ IWL_DEBUG_MAC80211("leave - RF not ready\n");
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ if (queue >= AC_NUM) {
|
|
+ IWL_DEBUG_MAC80211("leave - queue >= AC_NUM %d\n", queue);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_QOS
|
|
+ if (!priv->qos_data.qos_enable) {
|
|
+ priv->qos_data.qos_active = 0;
|
|
+ IWL_DEBUG_MAC80211("leave - qos ! enabled\n");
|
|
+ return 0;
|
|
+ }
|
|
+ i = AC_NUM - 1 - queue;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+
|
|
+ priv->qos_data.def_qos_parm.ac[i].cw_min = (__le16)
|
|
+ cpu_to_le16(params->cw_min);
|
|
+ priv->qos_data.def_qos_parm.ac[i].cw_max = (__le16)
|
|
+ cpu_to_le16(params->cw_max);
|
|
+ priv->qos_data.def_qos_parm.ac[i].aifsn = (u8)
|
|
+ params->aifs;
|
|
+ priv->qos_data.def_qos_parm.ac[i].edca_txop = (__le16)
|
|
+ cpu_to_le16((params->burst_time * 100));
|
|
+
|
|
+ priv->qos_data.def_qos_parm.ac[i].reserved1 = 0;
|
|
+ priv->qos_data.qos_active = IPW_QOS_WMM;
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+
|
|
+ /* we wait for the last queue then call qos ucode command */
|
|
+ if (priv->assoc_id && iwl_is_associated(priv) && (i == (AC_NUM - 1)))
|
|
+ iwl_activate_qos(priv, 0);
|
|
+
|
|
+ mutex_unlock(&priv->mutex);
|
|
+
|
|
+#endif /*CONFIG_IWLWIFI_QOS */
|
|
+
|
|
+ IWL_DEBUG_MAC80211("leave\n");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int iwl_mac_get_tx_stats(struct ieee80211_hw *hw,
|
|
+ struct ieee80211_tx_queue_stats *stats)
|
|
+{
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
+ int i, avail;
|
|
+ struct iwl_tx_queue *txq;
|
|
+ struct iwl_queue *q;
|
|
+ unsigned long flags;
|
|
+
|
|
+ IWL_DEBUG_MAC80211("enter\n");
|
|
+
|
|
+ if (!iwl_is_ready_rf(priv)) {
|
|
+ IWL_DEBUG_MAC80211("leave - RF not ready\n");
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+
|
|
+ for (i = 0; i < AC_NUM; i++) {
|
|
+ txq = &priv->txq[i];
|
|
+ q = &txq->q;
|
|
+ avail = iwl_queue_space(q);
|
|
+
|
|
+ stats->data[i].len = q->n_window - avail;
|
|
+ stats->data[i].limit = q->n_window - q->high_mark;
|
|
+ stats->data[i].count = q->n_window;
|
|
+
|
|
+ }
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ IWL_DEBUG_MAC80211("leave\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int iwl_mac_get_stats(struct ieee80211_hw *hw,
|
|
+ struct ieee80211_low_level_stats *stats)
|
|
+{
|
|
+ IWL_DEBUG_MAC80211("enter\n");
|
|
+ IWL_DEBUG_MAC80211("leave\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static u64 iwl_mac_get_tsf(struct ieee80211_hw *hw)
|
|
+{
|
|
+ IWL_DEBUG_MAC80211("enter\n");
|
|
+ IWL_DEBUG_MAC80211("leave\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void iwl_mac_reset_tsf(struct ieee80211_hw *hw)
|
|
+{
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
+ unsigned long flags;
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+ IWL_DEBUG_MAC80211("enter\n");
|
|
+
|
|
+#if IWL == 4965
|
|
+ priv->lq_mngr.lq_ready = 0;
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ memset(&priv->current_assoc_ht, 0, sizeof(struct sta_ht_info));
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+/* if (priv->lq_mngr.agg_ctrl.granted_ba)
|
|
+ iwl4965_turn_off_agg(priv, TID_ALL_SPECIFIED);*/
|
|
+
|
|
+ memset(&(priv->lq_mngr.agg_ctrl), 0, sizeof(struct iwl_agg_control));
|
|
+ priv->lq_mngr.agg_ctrl.tid_traffic_load_threshold = 10;
|
|
+ priv->lq_mngr.agg_ctrl.ba_timeout = 5000;
|
|
+ priv->lq_mngr.agg_ctrl.auto_agg = 1;
|
|
+
|
|
+ if (priv->lq_mngr.agg_ctrl.auto_agg)
|
|
+ priv->lq_mngr.agg_ctrl.requested_ba = TID_ALL_ENABLED;
|
|
+#endif /*CONFIG_IWLWIFI_HT_AGG */
|
|
+#endif /* CONFIG_IWLWIFI_HT */
|
|
+#endif /* IWL == 4965 */
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_QOS
|
|
+ iwl_reset_qos(priv);
|
|
+#endif
|
|
+
|
|
+ cancel_delayed_work(&priv->post_associate);
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ priv->assoc_id = 0;
|
|
+ priv->assoc_capability = 0;
|
|
+ priv->call_post_assoc_from_beacon = 0;
|
|
+
|
|
+ /* new association get rid of ibss beacon skb */
|
|
+ if (priv->ibss_beacon)
|
|
+ dev_kfree_skb(priv->ibss_beacon);
|
|
+
|
|
+ priv->ibss_beacon = NULL;
|
|
+
|
|
+ priv->beacon_int = priv->hw->conf.beacon_int;
|
|
+ priv->timestamp1 = 0;
|
|
+ priv->timestamp0 = 0;
|
|
+ if ((priv->iw_mode == IEEE80211_IF_TYPE_STA))
|
|
+ priv->beacon_int = 0;
|
|
+
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ /* Per mac80211.h: This is only used in IBSS mode... */
|
|
+ if (priv->iw_mode != IEEE80211_IF_TYPE_IBSS) {
|
|
+ IWL_DEBUG_MAC80211("leave - not in IBSS\n");
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!iwl_is_ready_rf(priv)) {
|
|
+ IWL_DEBUG_MAC80211("leave - not ready\n");
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ priv->only_active_channel = 0;
|
|
+
|
|
+ iwl_set_rate(priv);
|
|
+
|
|
+ mutex_unlock(&priv->mutex);
|
|
+
|
|
+ IWL_DEBUG_MAC80211("leave\n");
|
|
+
|
|
+}
|
|
+
|
|
+static int iwl_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb,
|
|
+ struct ieee80211_tx_control *control)
|
|
+{
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
+ unsigned long flags;
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+ IWL_DEBUG_MAC80211("enter\n");
|
|
+
|
|
+ if (!iwl_is_ready_rf(priv)) {
|
|
+ IWL_DEBUG_MAC80211("leave - RF not ready\n");
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ if (priv->iw_mode != IEEE80211_IF_TYPE_IBSS) {
|
|
+ IWL_DEBUG_MAC80211("leave - not IBSS\n");
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+
|
|
+ if (priv->ibss_beacon)
|
|
+ dev_kfree_skb(priv->ibss_beacon);
|
|
+
|
|
+ priv->ibss_beacon = skb;
|
|
+
|
|
+ priv->assoc_id = 0;
|
|
+
|
|
+ IWL_DEBUG_MAC80211("leave\n");
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_QOS
|
|
+ iwl_reset_qos(priv);
|
|
+#endif
|
|
+
|
|
+ queue_work(priv->workqueue, &priv->post_associate.work);
|
|
+
|
|
+ mutex_unlock(&priv->mutex);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#if IWL == 4965
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+union ht_cap_info {
|
|
+ struct {
|
|
+ u16 advanced_coding_cap :1;
|
|
+ u16 supported_chan_width_set :1;
|
|
+ u16 mimo_power_save_mode :2;
|
|
+ u16 green_field :1;
|
|
+ u16 short_GI20 :1;
|
|
+ u16 short_GI40 :1;
|
|
+ u16 tx_stbc :1;
|
|
+ u16 rx_stbc :1;
|
|
+ u16 beam_forming :1;
|
|
+ u16 delayed_ba :1;
|
|
+ u16 maximal_amsdu_size :1;
|
|
+ u16 cck_mode_at_40MHz :1;
|
|
+ u16 psmp_support :1;
|
|
+ u16 stbc_ctrl_frame_support :1;
|
|
+ u16 sig_txop_protection_support :1;
|
|
+ };
|
|
+ u16 val;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+union ht_param_info{
|
|
+ struct {
|
|
+ u8 max_rx_ampdu_factor :2;
|
|
+ u8 mpdu_density :3;
|
|
+ u8 reserved :3;
|
|
+ };
|
|
+ u8 val;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+union ht_exra_param_info {
|
|
+ struct {
|
|
+ u8 ext_chan_offset :2;
|
|
+ u8 tx_chan_width :1;
|
|
+ u8 rifs_mode :1;
|
|
+ u8 controlled_access_only :1;
|
|
+ u8 service_interval_granularity :3;
|
|
+ };
|
|
+ u8 val;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+union ht_operation_mode{
|
|
+ struct {
|
|
+ u16 op_mode :2;
|
|
+ u16 non_GF :1;
|
|
+ u16 reserved :13;
|
|
+ };
|
|
+ u16 val;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+
|
|
+static int sta_ht_info_init(struct ieee80211_ht_capability *ht_cap,
|
|
+ struct ieee80211_ht_additional_info *ht_extra,
|
|
+ struct sta_ht_info *ht_info_ap,
|
|
+ struct sta_ht_info *ht_info)
|
|
+{
|
|
+ union ht_cap_info cap;
|
|
+ union ht_operation_mode op_mode;
|
|
+ union ht_param_info param_info;
|
|
+ union ht_exra_param_info extra_param_info;
|
|
+
|
|
+ IWL_DEBUG_MAC80211("enter: \n");
|
|
+
|
|
+ if (!ht_info) {
|
|
+ IWL_DEBUG_MAC80211("leave: ht_info is NULL\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (ht_cap) {
|
|
+ cap.val = (u16) le16_to_cpu(ht_cap->capabilities_info);
|
|
+ param_info.val = ht_cap->mac_ht_params_info;
|
|
+ ht_info->is_ht = 1;
|
|
+ if (cap.short_GI20)
|
|
+ ht_info->sgf |= 0x1;
|
|
+ if (cap.short_GI40)
|
|
+ ht_info->sgf |= 0x2;
|
|
+ ht_info->is_green_field = cap.green_field;
|
|
+ ht_info->max_amsdu_size = cap.maximal_amsdu_size;
|
|
+ ht_info->supported_chan_width = cap.supported_chan_width_set;
|
|
+ ht_info->tx_mimo_ps_mode = cap.mimo_power_save_mode;
|
|
+ memcpy(ht_info->supp_rates, ht_cap->supported_mcs_set, 16);
|
|
+
|
|
+ ht_info->ampdu_factor = param_info.max_rx_ampdu_factor;
|
|
+ ht_info->mpdu_density = param_info.mpdu_density;
|
|
+
|
|
+ IWL_DEBUG_MAC80211("SISO mask 0x%X MIMO mask 0x%X \n",
|
|
+ ht_cap->supported_mcs_set[0],
|
|
+ ht_cap->supported_mcs_set[1]);
|
|
+
|
|
+ if (ht_info_ap) {
|
|
+ ht_info->control_channel = ht_info_ap->control_channel;
|
|
+ ht_info->extension_chan_offset =
|
|
+ ht_info_ap->extension_chan_offset;
|
|
+ ht_info->tx_chan_width = ht_info_ap->tx_chan_width;
|
|
+ ht_info->operating_mode = ht_info_ap->operating_mode;
|
|
+ }
|
|
+
|
|
+ if (ht_extra) {
|
|
+ extra_param_info.val = ht_extra->ht_param;
|
|
+ ht_info->control_channel = ht_extra->control_chan;
|
|
+ ht_info->extension_chan_offset =
|
|
+ extra_param_info.ext_chan_offset;
|
|
+ ht_info->tx_chan_width = extra_param_info.tx_chan_width;
|
|
+ op_mode.val = (u16)
|
|
+ le16_to_cpu(ht_extra->operation_mode);
|
|
+ ht_info->operating_mode = op_mode.op_mode;
|
|
+ IWL_DEBUG_MAC80211("control channel %d\n",
|
|
+ ht_extra->control_chan);
|
|
+ }
|
|
+ } else
|
|
+ ht_info->is_ht = 0;
|
|
+
|
|
+ IWL_DEBUG_MAC80211("leave\n");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int iwl_mac_conf_ht(struct ieee80211_hw *hw,
|
|
+ struct ieee80211_ht_capability *ht_cap,
|
|
+ struct ieee80211_ht_additional_info *ht_extra)
|
|
+{
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
+ int rs;
|
|
+
|
|
+ IWL_DEBUG_MAC80211("enter: \n");
|
|
+
|
|
+ rs = sta_ht_info_init(ht_cap, ht_extra, NULL, &priv->current_assoc_ht);
|
|
+ iwl4965_set_rxon_chain(priv);
|
|
+
|
|
+ if (priv && priv->assoc_id &&
|
|
+ (priv->iw_mode == IEEE80211_IF_TYPE_STA)) {
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ if (priv->beacon_int)
|
|
+ queue_work(priv->workqueue, &priv->post_associate.work);
|
|
+ else
|
|
+ priv->call_post_assoc_from_beacon = 1;
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_MAC80211("leave: control channel %d\n",
|
|
+ ht_extra->control_chan);
|
|
+ return rs;
|
|
+
|
|
+}
|
|
+
|
|
+static void iwl_set_ht_capab(struct ieee80211_hw *hw,
|
|
+ struct ieee80211_ht_capability *ht_cap,
|
|
+ u8 use_wide_chan)
|
|
+{
|
|
+ union ht_cap_info cap;
|
|
+ union ht_param_info param_info;
|
|
+
|
|
+ memset(&cap, 0, sizeof(union ht_cap_info));
|
|
+ memset(¶m_info, 0, sizeof(union ht_param_info));
|
|
+
|
|
+ cap.maximal_amsdu_size = HT_IE_MAX_AMSDU_SIZE_4K;
|
|
+ cap.green_field = 1;
|
|
+ cap.short_GI20 = 1;
|
|
+ cap.short_GI40 = 1;
|
|
+ cap.supported_chan_width_set = use_wide_chan;
|
|
+ cap.mimo_power_save_mode = 0x3;
|
|
+
|
|
+ param_info.max_rx_ampdu_factor = CFG_HT_RX_AMPDU_FACTOR_DEF;
|
|
+ param_info.mpdu_density = CFG_HT_MPDU_DENSITY_DEF;
|
|
+ ht_cap->capabilities_info = (__le16) cpu_to_le16(cap.val);
|
|
+ ht_cap->mac_ht_params_info = (u8) param_info.val;
|
|
+
|
|
+ ht_cap->supported_mcs_set[0] = 0xff;
|
|
+ ht_cap->supported_mcs_set[1] = 0xff;
|
|
+ ht_cap->supported_mcs_set[4] =
|
|
+ (cap.supported_chan_width_set) ? 0x1: 0x0;
|
|
+}
|
|
+
|
|
+static void iwl_mac_get_ht_capab(struct ieee80211_hw *hw,
|
|
+ struct ieee80211_ht_capability *ht_cap)
|
|
+{
|
|
+ u8 use_wide_channel = 1;
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
+
|
|
+ IWL_DEBUG_MAC80211("enter: \n");
|
|
+ if (priv->channel_width != IWL_CHANNEL_WIDTH_40MHZ)
|
|
+ use_wide_channel = 0;
|
|
+
|
|
+ /* no fat tx allowed on 2.4GHZ */
|
|
+ if (priv->phymode != MODE_IEEE80211A)
|
|
+ use_wide_channel = 0;
|
|
+
|
|
+ iwl_set_ht_capab(hw, ht_cap, use_wide_channel);
|
|
+ IWL_DEBUG_MAC80211("leave: \n");
|
|
+}
|
|
+#endif /*CONFIG_IWLWIFI_HT*/
|
|
+#endif /*IWL == 4965*/
|
|
+/*****************************************************************************
|
|
+ *
|
|
+ * sysfs attributes
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+
|
|
+/*
|
|
+ * The following adds a new attribute to the sysfs representation
|
|
+ * of this device driver (i.e. a new file in /sys/bus/pci/drivers/ipw/)
|
|
+ * used for controlling the debug level.
|
|
+ *
|
|
+ * See the level definitions in ipw for details.
|
|
+ */
|
|
+
|
|
+static ssize_t show_debug_level(struct device_driver *d, char *buf)
|
|
+{
|
|
+ return sprintf(buf, "0x%08X\n", iwl_debug_level);
|
|
+}
|
|
+static ssize_t store_debug_level(struct device_driver *d,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ char *p = (char *)buf;
|
|
+ u32 val;
|
|
+
|
|
+ val = simple_strtoul(p, &p, 0);
|
|
+ if (p == buf)
|
|
+ printk(KERN_INFO DRV_NAME
|
|
+ ": %s is not in hex or decimal form.\n", buf);
|
|
+ else
|
|
+ iwl_debug_level = val;
|
|
+
|
|
+ return strnlen(buf, count);
|
|
+}
|
|
+
|
|
+static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO,
|
|
+ show_debug_level, store_debug_level);
|
|
+
|
|
+#endif /* CONFIG_IWLWIFI_DEBUG */
|
|
+
|
|
+static ssize_t show_rf_kill(struct device *d,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ /*
|
|
+ * 0 - RF kill not enabled
|
|
+ * 1 - SW based RF kill active (sysfs)
|
|
+ * 2 - HW based RF kill active
|
|
+ * 3 - Both HW and SW based RF kill active
|
|
+ */
|
|
+ struct iwl_priv *priv = (struct iwl_priv *)d->driver_data;
|
|
+ int val = (test_bit(STATUS_RF_KILL_SW, &priv->status) ? 0x1 : 0x0) |
|
|
+ (test_bit(STATUS_RF_KILL_HW, &priv->status) ? 0x2 : 0x0);
|
|
+
|
|
+ return sprintf(buf, "%i\n", val);
|
|
+}
|
|
+
|
|
+static ssize_t store_rf_kill(struct device *d,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ struct iwl_priv *priv = (struct iwl_priv *)d->driver_data;
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+ iwl_radio_kill_sw(priv, buf[0] == '1');
|
|
+ mutex_unlock(&priv->mutex);
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR(rf_kill, S_IWUSR | S_IRUGO, show_rf_kill, store_rf_kill);
|
|
+
|
|
+static ssize_t show_temperature(struct device *d,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct iwl_priv *priv = (struct iwl_priv *)d->driver_data;
|
|
+
|
|
+ if (!iwl_is_alive(priv))
|
|
+ return -EAGAIN;
|
|
+
|
|
+ return sprintf(buf, "%d\n", iwl_hw_get_temperature(priv));
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR(temperature, S_IRUGO, show_temperature, NULL);
|
|
+
|
|
+static ssize_t show_rs_window(struct device *d,
|
|
+ struct device_attribute *attr,
|
|
+ char *buf)
|
|
+{
|
|
+ struct iwl_priv *priv = d->driver_data;
|
|
+ return iwl_fill_rs_info(priv->hw, buf, IWL_AP_ID);
|
|
+}
|
|
+static DEVICE_ATTR(rs_window, S_IRUGO, show_rs_window, NULL);
|
|
+
|
|
+static ssize_t show_tx_power(struct device *d,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct iwl_priv *priv = (struct iwl_priv *)d->driver_data;
|
|
+ return sprintf(buf, "%d\n", priv->user_txpower_limit);
|
|
+}
|
|
+
|
|
+static ssize_t store_tx_power(struct device *d,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ struct iwl_priv *priv = (struct iwl_priv *)d->driver_data;
|
|
+ char *p = (char *)buf;
|
|
+ u32 val;
|
|
+
|
|
+ val = simple_strtoul(p, &p, 10);
|
|
+ if (p == buf)
|
|
+ printk(KERN_INFO DRV_NAME
|
|
+ ": %s is not in decimal form.\n", buf);
|
|
+ else
|
|
+ iwl_hw_reg_set_txpower(priv, val);
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, show_tx_power, store_tx_power);
|
|
+
|
|
+static ssize_t show_flags(struct device *d,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct iwl_priv *priv = (struct iwl_priv *)d->driver_data;
|
|
+
|
|
+ return sprintf(buf, "0x%04X\n", priv->active_rxon.flags);
|
|
+}
|
|
+
|
|
+static ssize_t store_flags(struct device *d,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ struct iwl_priv *priv = (struct iwl_priv *)d->driver_data;
|
|
+ u32 flags = simple_strtoul(buf, NULL, 0);
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+ if (le32_to_cpu(priv->staging_rxon.flags) != flags) {
|
|
+ /* Cancel any currently running scans... */
|
|
+ if (iwl_scan_cancel_timeout(priv, 100))
|
|
+ IWL_WARNING("Could not cancel scan.\n");
|
|
+ else {
|
|
+ IWL_DEBUG_INFO("Committing rxon.flags = 0x%04X\n",
|
|
+ flags);
|
|
+ priv->staging_rxon.flags = cpu_to_le32(flags);
|
|
+ iwl_commit_rxon(priv);
|
|
+ }
|
|
+ }
|
|
+ mutex_unlock(&priv->mutex);
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR(flags, S_IWUSR | S_IRUGO, show_flags, store_flags);
|
|
+
|
|
+static ssize_t show_filter_flags(struct device *d,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct iwl_priv *priv = (struct iwl_priv *)d->driver_data;
|
|
+
|
|
+ return sprintf(buf, "0x%04X\n",
|
|
+ le32_to_cpu(priv->active_rxon.filter_flags));
|
|
+}
|
|
+
|
|
+static ssize_t store_filter_flags(struct device *d,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ struct iwl_priv *priv = (struct iwl_priv *)d->driver_data;
|
|
+ u32 filter_flags = simple_strtoul(buf, NULL, 0);
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+ if (le32_to_cpu(priv->staging_rxon.filter_flags) != filter_flags) {
|
|
+ /* Cancel any currently running scans... */
|
|
+ if (iwl_scan_cancel_timeout(priv, 100))
|
|
+ IWL_WARNING("Could not cancel scan.\n");
|
|
+ else {
|
|
+ IWL_DEBUG_INFO("Committing rxon.filter_flags = "
|
|
+ "0x%04X\n", filter_flags);
|
|
+ priv->staging_rxon.filter_flags =
|
|
+ cpu_to_le32(filter_flags);
|
|
+ iwl_commit_rxon(priv);
|
|
+ }
|
|
+ }
|
|
+ mutex_unlock(&priv->mutex);
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR(filter_flags, S_IWUSR | S_IRUGO, show_filter_flags,
|
|
+ store_filter_flags);
|
|
+
|
|
+static ssize_t show_tune(struct device *d,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct iwl_priv *priv = (struct iwl_priv *)d->driver_data;
|
|
+
|
|
+ return sprintf(buf, "0x%04X\n",
|
|
+ (priv->phymode << 8) |
|
|
+ le16_to_cpu(priv->active_rxon.channel));
|
|
+}
|
|
+
|
|
+static void iwl_set_flags_for_phymode(struct iwl_priv *priv, u8 phymode);
|
|
+
|
|
+static ssize_t store_tune(struct device *d,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ struct iwl_priv *priv = (struct iwl_priv *)d->driver_data;
|
|
+ char *p = (char *)buf;
|
|
+ u16 tune = simple_strtoul(p, &p, 0);
|
|
+ u8 phymode = (tune >> 8) & 0xff;
|
|
+ u16 channel = tune & 0xff;
|
|
+
|
|
+ IWL_DEBUG_INFO("Tune request to:%d channel:%d\n", phymode, channel);
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+ if ((le16_to_cpu(priv->staging_rxon.channel) != channel) ||
|
|
+ (priv->phymode != phymode)) {
|
|
+ const struct iwl_channel_info *ch_info;
|
|
+
|
|
+ ch_info = iwl_get_channel_info(priv, phymode, channel);
|
|
+ if (!ch_info) {
|
|
+ IWL_WARNING("Requested invalid phymode/channel "
|
|
+ "combination: %d %d\n", phymode, channel);
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* Cancel any currently running scans... */
|
|
+ if (iwl_scan_cancel_timeout(priv, 100))
|
|
+ IWL_WARNING("Could not cancel scan.\n");
|
|
+ else {
|
|
+ IWL_DEBUG_INFO("Committing phymode and "
|
|
+ "rxon.channel = %d %d\n",
|
|
+ phymode, channel);
|
|
+
|
|
+ iwl_set_rxon_channel(priv, phymode, channel);
|
|
+ iwl_set_flags_for_phymode(priv, phymode);
|
|
+
|
|
+ iwl_set_rate(priv);
|
|
+ iwl_commit_rxon(priv);
|
|
+ }
|
|
+ }
|
|
+ mutex_unlock(&priv->mutex);
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR(tune, S_IWUSR | S_IRUGO, show_tune, store_tune);
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_SPECTRUM_MEASUREMENT
|
|
+
|
|
+static ssize_t show_measurement(struct device *d,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct iwl_priv *priv = dev_get_drvdata(d);
|
|
+ struct iwl_spectrum_notification measure_report;
|
|
+ u32 size = sizeof(measure_report), len = 0, ofs = 0;
|
|
+ u8 *data = (u8 *) & measure_report;
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ if (!(priv->measurement_status & MEASUREMENT_READY)) {
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+ return 0;
|
|
+ }
|
|
+ memcpy(&measure_report, &priv->measure_report, size);
|
|
+ priv->measurement_status = 0;
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ while (size && (PAGE_SIZE - len)) {
|
|
+ hex_dump_to_buffer(data + ofs, size, 16, 1, buf + len,
|
|
+ PAGE_SIZE - len, 1);
|
|
+ len = strlen(buf);
|
|
+ if (PAGE_SIZE - len)
|
|
+ buf[len++] = '\n';
|
|
+
|
|
+ ofs += 16;
|
|
+ size -= min(size, 16U);
|
|
+ }
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static ssize_t store_measurement(struct device *d,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ struct iwl_priv *priv = dev_get_drvdata(d);
|
|
+ struct ieee80211_measurement_params params = {
|
|
+ .channel = le16_to_cpu(priv->active_rxon.channel),
|
|
+ .start_time = cpu_to_le64(priv->last_tsf),
|
|
+ .duration = cpu_to_le16(1),
|
|
+ };
|
|
+ u8 type = IWL_MEASURE_BASIC;
|
|
+ u8 buffer[32];
|
|
+ u8 channel;
|
|
+
|
|
+ if (count) {
|
|
+ char *p = buffer;
|
|
+ strncpy(buffer, buf, min(sizeof(buffer), count));
|
|
+ channel = simple_strtoul(p, NULL, 0);
|
|
+ if (channel)
|
|
+ params.channel = channel;
|
|
+
|
|
+ p = buffer;
|
|
+ while (*p && *p != ' ')
|
|
+ p++;
|
|
+ if (*p)
|
|
+ type = simple_strtoul(p + 1, NULL, 0);
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_INFO("Invoking measurement of type %d on "
|
|
+ "channel %d (for '%s')\n", type, params.channel, buf);
|
|
+ iwl_get_measurement(priv, ¶ms, type);
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR(measurement, S_IRUSR | S_IWUSR,
|
|
+ show_measurement, store_measurement);
|
|
+#endif /* CONFIG_IWLWIFI_SPECTRUM_MEASUREMENT */
|
|
+#if IWL == 3945
|
|
+static ssize_t show_rate(struct device *d,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct iwl_priv *priv = dev_get_drvdata(d);
|
|
+ unsigned long flags;
|
|
+ int i;
|
|
+
|
|
+ spin_lock_irqsave(&priv->sta_lock, flags);
|
|
+ if (priv->iw_mode == IEEE80211_IF_TYPE_STA)
|
|
+ i = priv->stations[IWL_AP_ID].current_rate.s.rate;
|
|
+ else
|
|
+ i = priv->stations[IWL_STA_ID].current_rate.s.rate;
|
|
+ spin_unlock_irqrestore(&priv->sta_lock, flags);
|
|
+
|
|
+ i = iwl_rate_index_from_plcp(i);
|
|
+ if (i == -1)
|
|
+ return sprintf(buf, "0\n");
|
|
+
|
|
+ return sprintf(buf, "%d%s\n",
|
|
+ (iwl_rates[i].ieee >> 1),
|
|
+ (iwl_rates[i].ieee & 0x1) ? ".5" : "");
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR(rate, S_IRUSR, show_rate, NULL);
|
|
+#endif
|
|
+
|
|
+static ssize_t store_retry_rate(struct device *d,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ struct iwl_priv *priv = dev_get_drvdata(d);
|
|
+
|
|
+ priv->retry_rate = simple_strtoul(buf, NULL, 0);
|
|
+ if (priv->retry_rate <= 0)
|
|
+ priv->retry_rate = 1;
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static ssize_t show_retry_rate(struct device *d,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct iwl_priv *priv = dev_get_drvdata(d);
|
|
+ return sprintf(buf, "%d", priv->retry_rate);
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR(retry_rate, S_IWUSR | S_IRUSR, show_retry_rate,
|
|
+ store_retry_rate);
|
|
+
|
|
+static ssize_t store_power_level(struct device *d,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ struct iwl_priv *priv = dev_get_drvdata(d);
|
|
+ int rc;
|
|
+ int mode;
|
|
+
|
|
+ mode = simple_strtoul(buf, NULL, 0);
|
|
+ mutex_lock(&priv->mutex);
|
|
+
|
|
+ if (!iwl_is_ready(priv)) {
|
|
+ rc = -EAGAIN;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if ((mode < 1) || (mode > IWL_POWER_LIMIT) || (mode == IWL_POWER_AC))
|
|
+ mode = IWL_POWER_AC;
|
|
+ else
|
|
+ mode |= IWL_POWER_ENABLED;
|
|
+
|
|
+ if (mode != priv->power_mode) {
|
|
+ rc = iwl_send_power_mode(priv, IWL_POWER_LEVEL(mode));
|
|
+ if (rc) {
|
|
+ IWL_DEBUG_MAC80211("failed setting power mode.\n");
|
|
+ goto out;
|
|
+ }
|
|
+ priv->power_mode = mode;
|
|
+ }
|
|
+
|
|
+ rc = count;
|
|
+
|
|
+ out:
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+#define MAX_WX_STRING 80
|
|
+
|
|
+/* Values are in microsecond */
|
|
+static const s32 timeout_duration[] = {
|
|
+ 350000,
|
|
+ 250000,
|
|
+ 75000,
|
|
+ 37000,
|
|
+ 25000,
|
|
+};
|
|
+static const s32 period_duration[] = {
|
|
+ 400000,
|
|
+ 700000,
|
|
+ 1000000,
|
|
+ 1000000,
|
|
+ 1000000
|
|
+};
|
|
+
|
|
+static ssize_t show_power_level(struct device *d,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct iwl_priv *priv = dev_get_drvdata(d);
|
|
+ int level = IWL_POWER_LEVEL(priv->power_mode);
|
|
+ char *p = buf;
|
|
+
|
|
+ p += sprintf(p, "%d ", level);
|
|
+ switch (level) {
|
|
+ case IWL_POWER_MODE_CAM:
|
|
+ case IWL_POWER_AC:
|
|
+ p += sprintf(p, "(AC)");
|
|
+ break;
|
|
+ case IWL_POWER_BATTERY:
|
|
+ p += sprintf(p, "(BATTERY)");
|
|
+ break;
|
|
+ default:
|
|
+ p += sprintf(p,
|
|
+ "(Timeout %dms, Period %dms)",
|
|
+ timeout_duration[level - 1] / 1000,
|
|
+ period_duration[level - 1] / 1000);
|
|
+ }
|
|
+
|
|
+ if (!(priv->power_mode & IWL_POWER_ENABLED))
|
|
+ p += sprintf(p, " OFF\n");
|
|
+ else
|
|
+ p += sprintf(p, " \n");
|
|
+
|
|
+ return (p - buf + 1);
|
|
+
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR(power_level, S_IWUSR | S_IRUSR, show_power_level,
|
|
+ store_power_level);
|
|
+
|
|
+static ssize_t show_channels(struct device *d,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct iwl_priv *priv = dev_get_drvdata(d);
|
|
+ int len = 0, i;
|
|
+ struct ieee80211_channel *channels = NULL;
|
|
+ const struct ieee80211_hw_mode *hw_mode = NULL;
|
|
+ int count = 0;
|
|
+
|
|
+ if (!iwl_is_ready(priv))
|
|
+ return -EAGAIN;
|
|
+
|
|
+ hw_mode = iwl_get_hw_mode(priv, MODE_IEEE80211G);
|
|
+ if (!hw_mode)
|
|
+ hw_mode = iwl_get_hw_mode(priv, MODE_IEEE80211B);
|
|
+ if (hw_mode) {
|
|
+ channels = hw_mode->channels;
|
|
+ count = hw_mode->num_channels;
|
|
+ }
|
|
+
|
|
+ len +=
|
|
+ sprintf(&buf[len],
|
|
+ "Displaying %d channels in 2.4GHz band "
|
|
+ "(802.11bg):\n", count);
|
|
+
|
|
+ for (i = 0; i < count; i++)
|
|
+ len += sprintf(&buf[len], "%d: %ddBm: BSS%s%s, %s.\n",
|
|
+ channels[i].chan,
|
|
+ channels[i].power_level,
|
|
+ channels[i].
|
|
+ flag & IEEE80211_CHAN_W_RADAR_DETECT ?
|
|
+ " (IEEE 802.11h required)" : "",
|
|
+ (!(channels[i].flag & IEEE80211_CHAN_W_IBSS)
|
|
+ || (channels[i].
|
|
+ flag &
|
|
+ IEEE80211_CHAN_W_RADAR_DETECT)) ? "" :
|
|
+ ", IBSS",
|
|
+ channels[i].
|
|
+ flag & IEEE80211_CHAN_W_ACTIVE_SCAN ?
|
|
+ "active/passive" : "passive only");
|
|
+
|
|
+ hw_mode = iwl_get_hw_mode(priv, MODE_IEEE80211A);
|
|
+ if (hw_mode) {
|
|
+ channels = hw_mode->channels;
|
|
+ count = hw_mode->num_channels;
|
|
+ } else {
|
|
+ channels = NULL;
|
|
+ count = 0;
|
|
+ }
|
|
+
|
|
+ len += sprintf(&buf[len], "Displaying %d channels in 5.2GHz band "
|
|
+ "(802.11a):\n", count);
|
|
+
|
|
+ for (i = 0; i < count; i++)
|
|
+ len += sprintf(&buf[len], "%d: %ddBm: BSS%s%s, %s.\n",
|
|
+ channels[i].chan,
|
|
+ channels[i].power_level,
|
|
+ channels[i].
|
|
+ flag & IEEE80211_CHAN_W_RADAR_DETECT ?
|
|
+ " (IEEE 802.11h required)" : "",
|
|
+ (!(channels[i].flag & IEEE80211_CHAN_W_IBSS)
|
|
+ || (channels[i].
|
|
+ flag &
|
|
+ IEEE80211_CHAN_W_RADAR_DETECT)) ? "" :
|
|
+ ", IBSS",
|
|
+ channels[i].
|
|
+ flag & IEEE80211_CHAN_W_ACTIVE_SCAN ?
|
|
+ "active/passive" : "passive only");
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR(channels, S_IRUSR, show_channels, NULL);
|
|
+
|
|
+static ssize_t show_statistics(struct device *d,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct iwl_priv *priv = dev_get_drvdata(d);
|
|
+ u32 size = sizeof(struct iwl_notif_statistics);
|
|
+ u32 len = 0, ofs = 0;
|
|
+ u8 *data = (u8 *) & priv->statistics;
|
|
+ int rc = 0;
|
|
+
|
|
+ if (!iwl_is_alive(priv))
|
|
+ return -EAGAIN;
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+ rc = iwl_send_statistics_request(priv);
|
|
+ mutex_unlock(&priv->mutex);
|
|
+
|
|
+ if (rc) {
|
|
+ len = sprintf(buf,
|
|
+ "Error sending statistics request: 0x%08X\n", rc);
|
|
+ return len;
|
|
+ }
|
|
+
|
|
+ while (size && (PAGE_SIZE - len)) {
|
|
+ hex_dump_to_buffer(data + ofs, size, 16, 1, buf + len,
|
|
+ PAGE_SIZE - len, 1);
|
|
+ len = strlen(buf);
|
|
+ if (PAGE_SIZE - len)
|
|
+ buf[len++] = '\n';
|
|
+
|
|
+ ofs += 16;
|
|
+ size -= min(size, 16U);
|
|
+ }
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR(statistics, S_IRUGO, show_statistics, NULL);
|
|
+
|
|
+static ssize_t show_antenna(struct device *d,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct iwl_priv *priv = dev_get_drvdata(d);
|
|
+
|
|
+ if (!iwl_is_alive(priv))
|
|
+ return -EAGAIN;
|
|
+
|
|
+ return sprintf(buf, "%d\n", priv->antenna);
|
|
+}
|
|
+
|
|
+static ssize_t store_antenna(struct device *d,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ int ant;
|
|
+ struct iwl_priv *priv = dev_get_drvdata(d);
|
|
+
|
|
+ if (count == 0)
|
|
+ return 0;
|
|
+
|
|
+ if (sscanf(buf, "%1i", &ant) != 1) {
|
|
+ IWL_DEBUG_INFO("not in hex or decimal form.\n");
|
|
+ return count;
|
|
+ }
|
|
+
|
|
+ if ((ant >= 0) && (ant <= 2)) {
|
|
+ IWL_DEBUG_INFO("Setting antenna select to %d.\n", ant);
|
|
+ priv->antenna = (enum iwl_antenna)ant;
|
|
+ } else
|
|
+ IWL_DEBUG_INFO("Bad antenna select value %d.\n", ant);
|
|
+
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR(antenna, S_IWUSR | S_IRUGO, show_antenna, store_antenna);
|
|
+
|
|
+static ssize_t show_status(struct device *d,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct iwl_priv *priv = (struct iwl_priv *)d->driver_data;
|
|
+ if (!iwl_is_alive(priv))
|
|
+ return -EAGAIN;
|
|
+ return sprintf(buf, "0x%08x\n", (int)priv->status);
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
|
|
+
|
|
+static ssize_t dump_error_log(struct device *d,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ char *p = (char *)buf;
|
|
+
|
|
+ if (p[0] == '1')
|
|
+ iwl_dump_nic_error_log((struct iwl_priv *)d->driver_data);
|
|
+
|
|
+ return strnlen(buf, count);
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR(dump_errors, S_IWUSR, NULL, dump_error_log);
|
|
+
|
|
+static ssize_t dump_event_log(struct device *d,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ char *p = (char *)buf;
|
|
+
|
|
+ if (p[0] == '1')
|
|
+ iwl_dump_nic_event_log((struct iwl_priv *)d->driver_data);
|
|
+
|
|
+ return strnlen(buf, count);
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR(dump_events, S_IWUSR, NULL, dump_event_log);
|
|
+
|
|
+/*****************************************************************************
|
|
+ *
|
|
+ * driver setup and teardown
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+static void iwl_setup_deferred_work(struct iwl_priv *priv)
|
|
+{
|
|
+ priv->workqueue = create_workqueue(DRV_NAME);
|
|
+
|
|
+ init_waitqueue_head(&priv->wait_command_queue);
|
|
+
|
|
+ INIT_WORK(&priv->up, iwl_bg_up);
|
|
+ INIT_WORK(&priv->restart, iwl_bg_restart);
|
|
+ INIT_WORK(&priv->rx_replenish, iwl_bg_rx_replenish);
|
|
+ INIT_WORK(&priv->scan_completed, iwl_bg_scan_completed);
|
|
+ INIT_WORK(&priv->request_scan, iwl_bg_request_scan);
|
|
+ INIT_WORK(&priv->abort_scan, iwl_bg_abort_scan);
|
|
+ INIT_WORK(&priv->rf_kill, iwl_bg_rf_kill);
|
|
+ INIT_WORK(&priv->beacon_update, iwl_bg_beacon_update);
|
|
+ INIT_DELAYED_WORK(&priv->post_associate, iwl_bg_post_associate);
|
|
+ INIT_DELAYED_WORK(&priv->init_alive_start, iwl_bg_init_alive_start);
|
|
+ INIT_DELAYED_WORK(&priv->alive_start, iwl_bg_alive_start);
|
|
+ INIT_DELAYED_WORK(&priv->scan_check, iwl_bg_scan_check);
|
|
+
|
|
+ iwl_hw_setup_deferred_work(priv);
|
|
+
|
|
+ tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
|
|
+ iwl_irq_tasklet, (unsigned long)priv);
|
|
+}
|
|
+
|
|
+static void iwl_cancel_deferred_work(struct iwl_priv *priv)
|
|
+{
|
|
+ iwl_hw_cancel_deferred_work(priv);
|
|
+
|
|
+ cancel_delayed_work(&priv->scan_check);
|
|
+ cancel_delayed_work(&priv->alive_start);
|
|
+ cancel_delayed_work(&priv->post_associate);
|
|
+ cancel_work_sync(&priv->beacon_update);
|
|
+}
|
|
+
|
|
+static struct attribute *iwl_sysfs_entries[] = {
|
|
+ &dev_attr_antenna.attr,
|
|
+ &dev_attr_channels.attr,
|
|
+ &dev_attr_dump_errors.attr,
|
|
+ &dev_attr_dump_events.attr,
|
|
+ &dev_attr_flags.attr,
|
|
+ &dev_attr_filter_flags.attr,
|
|
+#ifdef CONFIG_IWLWIFI_SPECTRUM_MEASUREMENT
|
|
+ &dev_attr_measurement.attr,
|
|
+#endif
|
|
+ &dev_attr_power_level.attr,
|
|
+#if IWL == 3945
|
|
+ &dev_attr_rate.attr,
|
|
+#endif
|
|
+ &dev_attr_retry_rate.attr,
|
|
+ &dev_attr_rf_kill.attr,
|
|
+ &dev_attr_rs_window.attr,
|
|
+ &dev_attr_statistics.attr,
|
|
+ &dev_attr_status.attr,
|
|
+ &dev_attr_temperature.attr,
|
|
+ &dev_attr_tune.attr,
|
|
+ &dev_attr_tx_power.attr,
|
|
+
|
|
+ NULL
|
|
+};
|
|
+
|
|
+static struct attribute_group iwl_attribute_group = {
|
|
+ .name = NULL, /* put in device directory */
|
|
+ .attrs = iwl_sysfs_entries,
|
|
+};
|
|
+
|
|
+static struct ieee80211_ops iwl_hw_ops = {
|
|
+ .tx = iwl_mac_tx,
|
|
+ .open = iwl_mac_open,
|
|
+ .stop = iwl_mac_stop,
|
|
+ .add_interface = iwl_mac_add_interface,
|
|
+ .remove_interface = iwl_mac_remove_interface,
|
|
+ .config = iwl_mac_config,
|
|
+ .config_interface = iwl_mac_config_interface,
|
|
+ .set_key = iwl_mac_set_key,
|
|
+ .get_stats = iwl_mac_get_stats,
|
|
+ .get_tx_stats = iwl_mac_get_tx_stats,
|
|
+ .conf_tx = iwl_mac_conf_tx,
|
|
+ .get_tsf = iwl_mac_get_tsf,
|
|
+ .reset_tsf = iwl_mac_reset_tsf,
|
|
+ .beacon_update = iwl_mac_beacon_update,
|
|
+#if IWL == 4965
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+ .conf_ht = iwl_mac_conf_ht,
|
|
+ .get_ht_capab = iwl_mac_get_ht_capab,
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+ .ht_tx_agg_start = iwl_mac_ht_tx_agg_start,
|
|
+ .ht_tx_agg_stop = iwl_mac_ht_tx_agg_stop,
|
|
+ .ht_rx_agg_start = iwl_mac_ht_rx_agg_start,
|
|
+ .ht_rx_agg_stop = iwl_mac_ht_rx_agg_stop,
|
|
+#endif /* CONFIG_IWLWIFI_HT_AGG */
|
|
+#endif /* CONFIG_IWLWIFI_HT */
|
|
+#endif
|
|
+ .hw_scan = iwl_mac_hw_scan
|
|
+};
|
|
+
|
|
+static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|
+{
|
|
+ int err = 0;
|
|
+ u32 pci_id;
|
|
+ struct iwl_priv *priv;
|
|
+ struct ieee80211_hw *hw;
|
|
+ int i;
|
|
+
|
|
+ if (iwl_param_disable_hw_scan) {
|
|
+ IWL_DEBUG_INFO("Disabling hw_scan\n");
|
|
+ iwl_hw_ops.hw_scan = NULL;
|
|
+ }
|
|
+
|
|
+ if ((iwl_param_queues_num > IWL_MAX_NUM_QUEUES) ||
|
|
+ (iwl_param_queues_num < IWL_MIN_NUM_QUEUES)) {
|
|
+ IWL_ERROR("invalid queues_num, should be between %d and %d\n",
|
|
+ IWL_MIN_NUM_QUEUES, IWL_MAX_NUM_QUEUES);
|
|
+ err = -EINVAL;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* mac80211 allocates memory for this device instance, including
|
|
+ * space for this driver's private structure */
|
|
+ hw = ieee80211_alloc_hw(sizeof(struct iwl_priv), &iwl_hw_ops);
|
|
+ if (hw == NULL) {
|
|
+ IWL_ERROR("Can not allocate network device\n");
|
|
+ err = -ENOMEM;
|
|
+ goto out;
|
|
+ }
|
|
+ SET_IEEE80211_DEV(hw, &pdev->dev);
|
|
+
|
|
+ IWL_DEBUG_INFO("*** LOAD DRIVER ***\n");
|
|
+ priv = hw->priv;
|
|
+ priv->hw = hw;
|
|
+
|
|
+ priv->pci_dev = pdev;
|
|
+ priv->antenna = (enum iwl_antenna)iwl_param_antenna;
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+ iwl_debug_level = iwl_param_debug;
|
|
+ atomic_set(&priv->restrict_refcnt, 0);
|
|
+#endif
|
|
+ priv->retry_rate = 1;
|
|
+
|
|
+ priv->ibss_beacon = NULL;
|
|
+
|
|
+ /* Tell mac80211 and its clients (e.g. Wireless Extensions)
|
|
+ * the range of signal quality values that we'll provide.
|
|
+ * Negative values for level/noise indicate that we'll provide dBm.
|
|
+ * For WE, at least, non-0 values here *enable* display of values
|
|
+ * in app (iwconfig). */
|
|
+ hw->max_rssi = -20; /* signal level, negative indicates dBm */
|
|
+ hw->max_noise = -20; /* noise level, negative indicates dBm */
|
|
+ hw->max_signal = 100; /* link quality indication (%) */
|
|
+
|
|
+ /* Tell mac80211 our Tx characteristics */
|
|
+ hw->flags = IEEE80211_HW_WEP_INCLUDE_IV |
|
|
+ IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE;
|
|
+
|
|
+ hw->queues = 4;
|
|
+#if IWL == 4965
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+ hw->queues = 16;
|
|
+#endif /* CONFIG_IWLWIFI_HT_AGG */
|
|
+#endif /* CONFIG_IWLWIFI_HT */
|
|
+#endif /* 4965 */
|
|
+
|
|
+ spin_lock_init(&priv->lock);
|
|
+ spin_lock_init(&priv->power_data.lock);
|
|
+ spin_lock_init(&priv->sta_lock);
|
|
+ spin_lock_init(&priv->hcmd_lock);
|
|
+#if IWL == 4965
|
|
+ spin_lock_init(&priv->lq_mngr.lock);
|
|
+#endif
|
|
+
|
|
+ for (i = 0; i < IWL_IBSS_MAC_HASH_SIZE; i++)
|
|
+ INIT_LIST_HEAD(&priv->ibss_mac_hash[i]);
|
|
+
|
|
+ INIT_LIST_HEAD(&priv->free_frames);
|
|
+
|
|
+ mutex_init(&priv->mutex);
|
|
+ if (pci_enable_device(pdev)) {
|
|
+ err = -ENODEV;
|
|
+ goto out_ieee80211_free_hw;
|
|
+ }
|
|
+
|
|
+ pci_set_master(pdev);
|
|
+
|
|
+ iwl_clear_stations_table(priv);
|
|
+
|
|
+ priv->data_retry_limit = -1;
|
|
+ priv->ieee_channels = NULL;
|
|
+ priv->ieee_rates = NULL;
|
|
+ priv->phymode = -1;
|
|
+
|
|
+ err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
|
|
+ if (!err)
|
|
+ err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
|
|
+ if (err) {
|
|
+ printk(KERN_WARNING DRV_NAME ": No suitable DMA available.\n");
|
|
+ goto out_pci_disable_device;
|
|
+ }
|
|
+
|
|
+ pci_set_drvdata(pdev, priv);
|
|
+ err = pci_request_regions(pdev, DRV_NAME);
|
|
+ if (err)
|
|
+ goto out_pci_disable_device;
|
|
+ /* We disable the RETRY_TIMEOUT register (0x41) to keep
|
|
+ * PCI Tx retries from interfering with C3 CPU state */
|
|
+ pci_write_config_byte(pdev, 0x41, 0x00);
|
|
+ priv->hw_base = pci_iomap(pdev, 0, 0);
|
|
+ if (!priv->hw_base) {
|
|
+ err = -ENODEV;
|
|
+ goto out_pci_release_regions;
|
|
+ }
|
|
+
|
|
+ IWL_DEBUG_INFO("pci_resource_len = 0x%08llx\n",
|
|
+ (unsigned long long) pci_resource_len(pdev, 0));
|
|
+ IWL_DEBUG_INFO("pci_resource_base = %p\n", priv->hw_base);
|
|
+
|
|
+ /* Initialize module parameter values here */
|
|
+
|
|
+ if (iwl_param_disable) {
|
|
+ set_bit(STATUS_RF_KILL_SW, &priv->status);
|
|
+ IWL_DEBUG_INFO("Radio disabled.\n");
|
|
+ }
|
|
+
|
|
+ priv->iw_mode = IEEE80211_IF_TYPE_STA;
|
|
+
|
|
+ pci_id =
|
|
+ (priv->pci_dev->device << 16) | priv->pci_dev->subsystem_device;
|
|
+
|
|
+#if IWL == 4965
|
|
+ priv->ps_mode = 0;
|
|
+ priv->use_ant_b_for_management_frame = 1; /* start with ant B */
|
|
+ priv->is_ht_enabled = 1;
|
|
+ priv->channel_width = IWL_CHANNEL_WIDTH_40MHZ;
|
|
+ priv->valid_antenna = 0x7; /* assume all 3 connected */
|
|
+ priv->ps_mode = IWL_MIMO_PS_NONE;
|
|
+ priv->cck_power_index_compensation = iwl_read32(
|
|
+ priv, CSR_HW_REV_WA_REG);
|
|
+
|
|
+ iwl4965_set_rxon_chain(priv);
|
|
+
|
|
+ printk(KERN_INFO DRV_NAME
|
|
+ ": Detected Intel Wireless WiFi Link 4965AGN\n");
|
|
+#else
|
|
+ switch (pci_id) {
|
|
+ case 0x42221005: /* 0x4222 0x8086 0x1005 is BG SKU */
|
|
+ case 0x42221034: /* 0x4222 0x8086 0x1034 is BG SKU */
|
|
+ case 0x42271014: /* 0x4227 0x8086 0x1014 is BG SKU */
|
|
+ case 0x42221044: /* 0x4222 0x8086 0x1044 is BG SKU */
|
|
+ priv->is_abg = 0;
|
|
+ break;
|
|
+
|
|
+ /*
|
|
+ * Rest are assumed ABG SKU -- if this is not the
|
|
+ * case then the card will get the wrong 'Detected'
|
|
+ * line in the kernel log however the code that
|
|
+ * initializes the GEO table will detect no A-band
|
|
+ * channels and remove the is_abg mask.
|
|
+ */
|
|
+ default:
|
|
+ priv->is_abg = 1;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ printk(KERN_INFO DRV_NAME
|
|
+ ": Detected Intel PRO/Wireless 3945%sBG Network Connection\n",
|
|
+ priv->is_abg ? "A" : "");
|
|
+#endif
|
|
+
|
|
+ /* Device-specific setup */
|
|
+ if (iwl_hw_set_hw_setting(priv)) {
|
|
+ IWL_ERROR("failed to set hw settings\n");
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ goto out_iounmap;
|
|
+ }
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_QOS
|
|
+ if (iwl_param_qos_enable)
|
|
+ priv->qos_data.qos_enable = 1;
|
|
+ priv->qos_data.qos_active = 0;
|
|
+ priv->qos_data.qos_cap.val = 0;
|
|
+#endif /* CONFIG_IWLWIFI_QOS */
|
|
+
|
|
+ iwl_set_rxon_channel(priv, MODE_IEEE80211G, 6);
|
|
+ iwl_setup_deferred_work(priv);
|
|
+ iwl_setup_rx_handlers(priv);
|
|
+
|
|
+ priv->rates_mask = IWL_RATES_MASK;
|
|
+ /* If power management is turned on, default to AC mode */
|
|
+ priv->power_mode = IWL_POWER_AC;
|
|
+ priv->user_txpower_limit = IWL_DEFAULT_TX_POWER;
|
|
+
|
|
+ pci_enable_msi(pdev);
|
|
+
|
|
+ err = request_irq(pdev->irq, iwl_isr, IRQF_SHARED, DRV_NAME, priv);
|
|
+ if (err) {
|
|
+ IWL_ERROR("Error allocating IRQ %d\n", pdev->irq);
|
|
+ goto out_disable_msi;
|
|
+ }
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+
|
|
+ err = sysfs_create_group(&pdev->dev.kobj, &iwl_attribute_group);
|
|
+ if (err) {
|
|
+ IWL_ERROR("failed to create sysfs device attributes\n");
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ goto out_release_irq;
|
|
+ }
|
|
+
|
|
+ /* fetch ucode file from disk, alloc and copy to bus-master buffers ...
|
|
+ * ucode filename and max sizes are card-specific. */
|
|
+ err = iwl_read_ucode(priv);
|
|
+ if (err) {
|
|
+ IWL_ERROR("Could not read microcode: %d\n", err);
|
|
+ mutex_unlock(&priv->mutex);
|
|
+ goto out_pci_alloc;
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&priv->mutex);
|
|
+
|
|
+ IWL_DEBUG_INFO("Queing UP work.\n");
|
|
+
|
|
+ queue_work(priv->workqueue, &priv->up);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+ out_pci_alloc:
|
|
+ iwl_dealloc_ucode_pci(priv);
|
|
+
|
|
+ sysfs_remove_group(&pdev->dev.kobj, &iwl_attribute_group);
|
|
+
|
|
+ out_release_irq:
|
|
+ free_irq(pdev->irq, priv);
|
|
+
|
|
+ out_disable_msi:
|
|
+ pci_disable_msi(pdev);
|
|
+ destroy_workqueue(priv->workqueue);
|
|
+ priv->workqueue = NULL;
|
|
+ iwl_unset_hw_setting(priv);
|
|
+
|
|
+ out_iounmap:
|
|
+ pci_iounmap(pdev, priv->hw_base);
|
|
+ out_pci_release_regions:
|
|
+ pci_release_regions(pdev);
|
|
+ out_pci_disable_device:
|
|
+ pci_disable_device(pdev);
|
|
+ pci_set_drvdata(pdev, NULL);
|
|
+ out_ieee80211_free_hw:
|
|
+ ieee80211_free_hw(priv->hw);
|
|
+ out:
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static void iwl_pci_remove(struct pci_dev *pdev)
|
|
+{
|
|
+ struct iwl_priv *priv = pci_get_drvdata(pdev);
|
|
+ struct list_head *p, *q;
|
|
+ int i;
|
|
+
|
|
+ if (!priv)
|
|
+ return;
|
|
+
|
|
+ IWL_DEBUG_INFO("*** UNLOAD DRIVER ***\n");
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+ set_bit(STATUS_EXIT_PENDING, &priv->status);
|
|
+ __iwl_down(priv);
|
|
+ mutex_unlock(&priv->mutex);
|
|
+
|
|
+ /* Free MAC hash list for ADHOC */
|
|
+ for (i = 0; i < IWL_IBSS_MAC_HASH_SIZE; i++) {
|
|
+ list_for_each_safe(p, q, &priv->ibss_mac_hash[i]) {
|
|
+ list_del(p);
|
|
+ kfree(list_entry(p, struct iwl_ibss_seq, list));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ sysfs_remove_group(&pdev->dev.kobj, &iwl_attribute_group);
|
|
+
|
|
+ iwl_dealloc_ucode_pci(priv);
|
|
+
|
|
+ if (priv->rxq.bd)
|
|
+ iwl_rx_queue_free(priv, &priv->rxq);
|
|
+ iwl_hw_txq_ctx_free(priv);
|
|
+
|
|
+ iwl_unset_hw_setting(priv);
|
|
+ iwl_clear_stations_table(priv);
|
|
+
|
|
+ if (priv->mac80211_registered) {
|
|
+ ieee80211_unregister_hw(priv->hw);
|
|
+ iwl_rate_control_unregister(priv->hw);
|
|
+ }
|
|
+
|
|
+ /* ieee80211_unregister_hw calls iwl_mac_stop, which flushes
|
|
+ * priv->workqueue... so we can't take down the workqueue
|
|
+ * until now... */
|
|
+ destroy_workqueue(priv->workqueue);
|
|
+ priv->workqueue = NULL;
|
|
+
|
|
+ free_irq(pdev->irq, priv);
|
|
+ pci_disable_msi(pdev);
|
|
+ pci_iounmap(pdev, priv->hw_base);
|
|
+ pci_release_regions(pdev);
|
|
+ pci_disable_device(pdev);
|
|
+ pci_set_drvdata(pdev, NULL);
|
|
+
|
|
+ kfree(priv->channel_info);
|
|
+
|
|
+ kfree(priv->ieee_channels);
|
|
+ kfree(priv->ieee_rates);
|
|
+
|
|
+ if (priv->ibss_beacon)
|
|
+ dev_kfree_skb(priv->ibss_beacon);
|
|
+
|
|
+ ieee80211_free_hw(priv->hw);
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_PM
|
|
+
|
|
+static int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state)
|
|
+{
|
|
+ struct iwl_priv *priv = pci_get_drvdata(pdev);
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+
|
|
+ set_bit(STATUS_IN_SUSPEND, &priv->status);
|
|
+
|
|
+ /* Take down the device; powers it off, etc. */
|
|
+ __iwl_down(priv);
|
|
+
|
|
+ if (priv->mac80211_registered)
|
|
+ ieee80211_stop_queues(priv->hw);
|
|
+
|
|
+ pci_save_state(pdev);
|
|
+ pci_disable_device(pdev);
|
|
+ pci_set_power_state(pdev, PCI_D3hot);
|
|
+
|
|
+ mutex_unlock(&priv->mutex);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void iwl_resume(struct iwl_priv *priv)
|
|
+{
|
|
+ unsigned long flags;
|
|
+
|
|
+ /* The following it a temporary work around due to the
|
|
+ * suspend / resume not fully initializing the NIC correctly.
|
|
+ * Without all of the following, resume will not attempt to take
|
|
+ * down the NIC (it shouldn't really need to) and will just try
|
|
+ * and bring the NIC back up. However that fails during the
|
|
+ * ucode verification process. This then causes iwl_down to be
|
|
+ * called *after* iwl_hw_nic_init() has succeeded -- which
|
|
+ * then lets the next init sequence succeed. So, we've
|
|
+ * replicated all of that NIC init code here... */
|
|
+
|
|
+ iwl_write32(priv, CSR_INT, 0xFFFFFFFF);
|
|
+
|
|
+ iwl_hw_nic_init(priv);
|
|
+
|
|
+ iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
|
|
+ iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR,
|
|
+ CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
|
|
+ iwl_write32(priv, CSR_INT, 0xFFFFFFFF);
|
|
+ iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
|
|
+ iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
|
|
+
|
|
+ /* tell the device to stop sending interrupts */
|
|
+ iwl_disable_interrupts(priv);
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ iwl_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
|
|
+
|
|
+ if (!iwl_grab_restricted_access(priv)) {
|
|
+ iwl_write_restricted_reg(priv, ALM_APMG_CLK_DIS,
|
|
+ APMG_CLK_REG_VAL_DMA_CLK_RQT);
|
|
+ iwl_release_restricted_access(priv);
|
|
+ }
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ udelay(5);
|
|
+
|
|
+ iwl_hw_nic_reset(priv);
|
|
+
|
|
+ /* Bring the device back up */
|
|
+ clear_bit(STATUS_IN_SUSPEND, &priv->status);
|
|
+ queue_work(priv->workqueue, &priv->up);
|
|
+}
|
|
+
|
|
+static int iwl_pci_resume(struct pci_dev *pdev)
|
|
+{
|
|
+ struct iwl_priv *priv = pci_get_drvdata(pdev);
|
|
+ int err;
|
|
+
|
|
+ printk(KERN_INFO "Coming out of suspend...\n");
|
|
+
|
|
+ mutex_lock(&priv->mutex);
|
|
+
|
|
+ pci_set_power_state(pdev, PCI_D0);
|
|
+ err = pci_enable_device(pdev);
|
|
+ pci_restore_state(pdev);
|
|
+
|
|
+ /*
|
|
+ * Suspend/Resume resets the PCI configuration space, so we have to
|
|
+ * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries
|
|
+ * from interfering with C3 CPU state. pci_restore_state won't help
|
|
+ * here since it only restores the first 64 bytes pci config header.
|
|
+ */
|
|
+ pci_write_config_byte(pdev, 0x41, 0x00);
|
|
+
|
|
+ iwl_resume(priv);
|
|
+ mutex_unlock(&priv->mutex);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#endif /* CONFIG_PM */
|
|
+
|
|
+/*****************************************************************************
|
|
+ *
|
|
+ * driver and module entry point
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+static struct pci_driver iwl_driver = {
|
|
+ .name = DRV_NAME,
|
|
+ .id_table = iwl_hw_card_ids,
|
|
+ .probe = iwl_pci_probe,
|
|
+ .remove = __devexit_p(iwl_pci_remove),
|
|
+#ifdef CONFIG_PM
|
|
+ .suspend = iwl_pci_suspend,
|
|
+ .resume = iwl_pci_resume,
|
|
+#endif
|
|
+};
|
|
+
|
|
+static int __init iwl_init(void)
|
|
+{
|
|
+
|
|
+ int ret;
|
|
+ printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n");
|
|
+ printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n");
|
|
+ ret = pci_register_driver(&iwl_driver);
|
|
+ if (ret) {
|
|
+ IWL_ERROR("Unable to initialize PCI module\n");
|
|
+ return ret;
|
|
+ }
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+ ret = driver_create_file(&iwl_driver.driver, &driver_attr_debug_level);
|
|
+ if (ret) {
|
|
+ IWL_ERROR("Unable to create driver sysfs file\n");
|
|
+ pci_unregister_driver(&iwl_driver);
|
|
+ return ret;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void __exit iwl_exit(void)
|
|
+{
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+ driver_remove_file(&iwl_driver.driver, &driver_attr_debug_level);
|
|
+#endif
|
|
+ pci_unregister_driver(&iwl_driver);
|
|
+}
|
|
+
|
|
+module_param_named(antenna, iwl_param_antenna, int, 0444);
|
|
+MODULE_PARM_DESC(antenna, "select antenna (1=Main, 2=Aux, default 0 [both])");
|
|
+module_param_named(disable, iwl_param_disable, int, 0444);
|
|
+MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])");
|
|
+module_param_named(hwcrypto, iwl_param_hwcrypto, int, 0444);
|
|
+MODULE_PARM_DESC(hwcrypto,
|
|
+ "using hardware crypto engine (default 0 [software])\n");
|
|
+module_param_named(debug, iwl_param_debug, int, 0444);
|
|
+MODULE_PARM_DESC(debug, "debug output mask");
|
|
+module_param_named(disable_hw_scan, iwl_param_disable_hw_scan, int, 0444);
|
|
+MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 0)");
|
|
+
|
|
+module_param_named(queues_num, iwl_param_queues_num, int, 0444);
|
|
+MODULE_PARM_DESC(queues_num, "number of hw queues.");
|
|
+
|
|
+/* QoS */
|
|
+module_param_named(qos_enable, iwl_param_qos_enable, int, 0444);
|
|
+MODULE_PARM_DESC(qos_enable, "enable all QoS functionality");
|
|
+
|
|
+module_exit(iwl_exit);
|
|
+module_init(iwl_init);
|
|
diff --git a/drivers/net/wireless/iwl-channel.h b/drivers/net/wireless/iwl-channel.h
|
|
new file mode 100644
|
|
index 0000000..023c3f2
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/iwl-channel.h
|
|
@@ -0,0 +1,161 @@
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * Copyright(c) 2005 - 2007 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.
|
|
+ *
|
|
+ * Contact Information:
|
|
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
|
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+#ifndef __iwl_channel_h__
|
|
+#define __iwl_channel_h__
|
|
+
|
|
+#define IWL_NUM_SCAN_RATES (2)
|
|
+
|
|
+struct iwl_channel_tgd_info {
|
|
+ u8 type;
|
|
+ s8 max_power;
|
|
+};
|
|
+
|
|
+struct iwl_channel_tgh_info {
|
|
+ s64 last_radar_time;
|
|
+};
|
|
+
|
|
+/* current Tx power values to use, one for each rate for each channel.
|
|
+ * requested power is limited by:
|
|
+ * -- regulatory EEPROM limits for this channel
|
|
+ * -- hardware capabilities (clip-powers)
|
|
+ * -- spectrum management
|
|
+ * -- user preference (e.g. iwconfig)
|
|
+ * when requested power is set, base power index must also be set. */
|
|
+struct iwl_channel_power_info {
|
|
+ struct iwl_tx_power tpc; /* actual radio and DSP gain settings */
|
|
+ s8 power_table_index; /* actual (compenst'd) index into gain table */
|
|
+ s8 base_power_index; /* gain index for power at factory temp. */
|
|
+ s8 requested_power; /* power (dBm) requested for this chnl/rate */
|
|
+};
|
|
+
|
|
+/* current scan Tx power values to use, one for each scan rate for each
|
|
+ * channel. */
|
|
+struct iwl_scan_power_info {
|
|
+ struct iwl_tx_power tpc; /* actual radio and DSP gain settings */
|
|
+ s8 power_table_index; /* actual (compenst'd) index into gain table */
|
|
+ s8 requested_power; /* scan pwr (dBm) requested for chnl/rate */
|
|
+};
|
|
+
|
|
+/* Channel unlock period is 15 seconds. If no beacon or probe response
|
|
+ * has been received within 15 seconds on a locked channel then the channel
|
|
+ * remains locked. */
|
|
+#define TX_UNLOCK_PERIOD 15
|
|
+
|
|
+/* CSA lock period is 15 seconds. If a CSA has been received on a channel in
|
|
+ * the last 15 seconds, the channel is locked */
|
|
+#define CSA_LOCK_PERIOD 15
|
|
+/*
|
|
+ * One for each channel, holds all channel setup data
|
|
+ * Some of the fields (e.g. eeprom and flags/max_power_avg) are redundant
|
|
+ * with one another!
|
|
+ */
|
|
+#define IWL4965_MAX_RATE (33)
|
|
+
|
|
+struct iwl_channel_info {
|
|
+ struct iwl_channel_tgd_info tgd;
|
|
+ struct iwl_channel_tgh_info tgh;
|
|
+ struct iwl_eeprom_channel eeprom; /* EEPROM regulatory limit */
|
|
+ struct iwl_eeprom_channel fat_eeprom; /* EEPROM regulatory limit for
|
|
+ * FAT channel */
|
|
+
|
|
+ u8 channel; /* channel number */
|
|
+ u8 flags; /* flags copied from EEPROM */
|
|
+ s8 max_power_avg; /* (dBm) regul. eeprom, normal Tx, any rate */
|
|
+ s8 curr_txpow; /* (dBm) regulatory/spectrum/user (not h/w) */
|
|
+ s8 min_power; /* always 0 */
|
|
+ s8 scan_power; /* (dBm) regul. eeprom, direct scans, any rate */
|
|
+
|
|
+ u8 group_index; /* 0-4, maps channel to group1/2/3/4/5 */
|
|
+ u8 band_index; /* 0-4, maps channel to band1/2/3/4/5 */
|
|
+ u8 phymode; /* MODE_IEEE80211{A,B,G} */
|
|
+
|
|
+ /* Radio/DSP gain settings for each "normal" data Tx rate.
|
|
+ * These include, in addition to RF and DSP gain, a few fields for
|
|
+ * remembering/modifying gain settings (indexes). */
|
|
+ struct iwl_channel_power_info power_info[IWL4965_MAX_RATE];
|
|
+
|
|
+#if IWL == 4965
|
|
+ /* FAT channel info */
|
|
+ s8 fat_max_power_avg; /* (dBm) regul. eeprom, normal Tx, any rate */
|
|
+ s8 fat_curr_txpow; /* (dBm) regulatory/spectrum/user (not h/w) */
|
|
+ s8 fat_min_power; /* always 0 */
|
|
+ s8 fat_scan_power; /* (dBm) eeprom, direct scans, any rate */
|
|
+ u8 fat_flags; /* flags copied from EEPROM */
|
|
+ u8 fat_extension_channel;
|
|
+#endif
|
|
+
|
|
+ /* Radio/DSP gain settings for each scan rate, for directed scans. */
|
|
+ struct iwl_scan_power_info scan_pwr_info[IWL_NUM_SCAN_RATES];
|
|
+};
|
|
+
|
|
+struct iwl_clip_group {
|
|
+ /* maximum power level to prevent clipping for each rate, derived by
|
|
+ * us from this band's saturation power in EEPROM */
|
|
+ const s8 clip_powers[IWL_MAX_RATES];
|
|
+};
|
|
+
|
|
+static inline int is_channel_valid(const struct iwl_channel_info *ch_info)
|
|
+{
|
|
+ if (ch_info == NULL)
|
|
+ return 0;
|
|
+ return (ch_info->flags & EEPROM_CHANNEL_VALID) ? 1 : 0;
|
|
+}
|
|
+
|
|
+static inline int is_channel_narrow(const struct iwl_channel_info *ch_info)
|
|
+{
|
|
+ return (ch_info->flags & EEPROM_CHANNEL_NARROW) ? 1 : 0;
|
|
+}
|
|
+
|
|
+static inline int is_channel_radar(const struct iwl_channel_info *ch_info)
|
|
+{
|
|
+ return (ch_info->flags & EEPROM_CHANNEL_RADAR) ? 1 : 0;
|
|
+}
|
|
+
|
|
+static inline u8 is_channel_a_band(const struct iwl_channel_info *ch_info)
|
|
+{
|
|
+ return ch_info->phymode == MODE_IEEE80211A;
|
|
+}
|
|
+
|
|
+static inline u8 is_channel_bg_band(const struct iwl_channel_info *ch_info)
|
|
+{
|
|
+ return ((ch_info->phymode == MODE_IEEE80211B) ||
|
|
+ (ch_info->phymode == MODE_IEEE80211G));
|
|
+}
|
|
+
|
|
+static inline int is_channel_passive(const struct iwl_channel_info *ch)
|
|
+{
|
|
+ return (!(ch->flags & EEPROM_CHANNEL_ACTIVE)) ? 1 : 0;
|
|
+}
|
|
+
|
|
+static inline int is_channel_ibss(const struct iwl_channel_info *ch)
|
|
+{
|
|
+ return ((ch->flags & EEPROM_CHANNEL_IBSS)) ? 1 : 0;
|
|
+}
|
|
+
|
|
+extern const struct iwl_channel_info *iwl_get_channel_info(
|
|
+ const struct iwl_priv *priv, int phymode, u16 channel);
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/net/wireless/iwl-commands.h b/drivers/net/wireless/iwl-commands.h
|
|
new file mode 100644
|
|
index 0000000..7f39b03
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/iwl-commands.h
|
|
@@ -0,0 +1,1708 @@
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * 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) 2005 - 2007 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 Geeral 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:
|
|
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
|
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
+ *
|
|
+ * BSD LICENSE
|
|
+ *
|
|
+ * Copyright(c) 2005 - 2007 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_commands_h__
|
|
+#define __iwl_commands_h__
|
|
+
|
|
+enum {
|
|
+ REPLY_ALIVE = 0x1,
|
|
+ REPLY_ERROR = 0x2,
|
|
+
|
|
+ /* RXON and QOS commands */
|
|
+ REPLY_RXON = 0x10,
|
|
+ REPLY_RXON_ASSOC = 0x11,
|
|
+ REPLY_QOS_PARAM = 0x13,
|
|
+ REPLY_RXON_TIMING = 0x14,
|
|
+
|
|
+ /* Multi-Station support */
|
|
+ REPLY_ADD_STA = 0x18,
|
|
+ REPLY_REMOVE_STA = 0x19, /* not used */
|
|
+ REPLY_REMOVE_ALL_STA = 0x1a, /* not used */
|
|
+
|
|
+ /* RX, TX, LEDs */
|
|
+#if IWL == 3945
|
|
+ REPLY_3945_RX = 0x1b, /* 3945 only */
|
|
+#endif
|
|
+ REPLY_TX = 0x1c,
|
|
+ REPLY_RATE_SCALE = 0x47, /* 3945 only */
|
|
+ REPLY_LEDS_CMD = 0x48,
|
|
+ REPLY_TX_LINK_QUALITY_CMD = 0x4e, /* 4965 only */
|
|
+
|
|
+ /* 802.11h related */
|
|
+ RADAR_NOTIFICATION = 0x70, /* not used */
|
|
+ REPLY_QUIET_CMD = 0x71, /* not used */
|
|
+ REPLY_CHANNEL_SWITCH = 0x72,
|
|
+ CHANNEL_SWITCH_NOTIFICATION = 0x73,
|
|
+ REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74,
|
|
+ SPECTRUM_MEASURE_NOTIFICATION = 0x75,
|
|
+
|
|
+ /* Power Management */
|
|
+ POWER_TABLE_CMD = 0x77,
|
|
+ PM_SLEEP_NOTIFICATION = 0x7A,
|
|
+ PM_DEBUG_STATISTIC_NOTIFIC = 0x7B,
|
|
+
|
|
+ /* Scan commands and notifications */
|
|
+ REPLY_SCAN_CMD = 0x80,
|
|
+ REPLY_SCAN_ABORT_CMD = 0x81,
|
|
+ SCAN_START_NOTIFICATION = 0x82,
|
|
+ SCAN_RESULTS_NOTIFICATION = 0x83,
|
|
+ SCAN_COMPLETE_NOTIFICATION = 0x84,
|
|
+
|
|
+ /* IBSS/AP commands */
|
|
+ BEACON_NOTIFICATION = 0x90,
|
|
+ REPLY_TX_BEACON = 0x91,
|
|
+ WHO_IS_AWAKE_NOTIFICATION = 0x94, /* not used */
|
|
+
|
|
+ /* Miscellaneous commands */
|
|
+ QUIET_NOTIFICATION = 0x96, /* not used */
|
|
+ REPLY_TX_PWR_TABLE_CMD = 0x97,
|
|
+ MEASURE_ABORT_NOTIFICATION = 0x99, /* not used */
|
|
+
|
|
+ /* BT config command */
|
|
+ REPLY_BT_CONFIG = 0x9b,
|
|
+
|
|
+ /* 4965 Statistics */
|
|
+ REPLY_STATISTICS_CMD = 0x9c,
|
|
+ STATISTICS_NOTIFICATION = 0x9d,
|
|
+
|
|
+ /* RF-KILL commands and notifications */
|
|
+ REPLY_CARD_STATE_CMD = 0xa0,
|
|
+ CARD_STATE_NOTIFICATION = 0xa1,
|
|
+
|
|
+ /* Missed beacons notification */
|
|
+ MISSED_BEACONS_NOTIFICATION = 0xa2,
|
|
+
|
|
+#if IWL == 4965
|
|
+ REPLY_CT_KILL_CONFIG_CMD = 0xa4,
|
|
+ SENSITIVITY_CMD = 0xa8,
|
|
+ REPLY_PHY_CALIBRATION_CMD = 0xb0,
|
|
+ REPLY_RX_PHY_CMD = 0xc0,
|
|
+ REPLY_RX_MPDU_CMD = 0xc1,
|
|
+ REPLY_4965_RX = 0xc3,
|
|
+ REPLY_COMPRESSED_BA = 0xc5,
|
|
+#endif
|
|
+ REPLY_MAX = 0xff
|
|
+};
|
|
+
|
|
+/******************************************************************************
|
|
+ * (0)
|
|
+ * Header
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+#define IWL_CMD_FAILED_MSK 0x40
|
|
+
|
|
+struct iwl_cmd_header {
|
|
+ u8 cmd;
|
|
+ u8 flags;
|
|
+ /* We have 15 LSB to use as we please (MSB indicates
|
|
+ * a frame Rx'd from the HW). We encode the following
|
|
+ * information into the sequence field:
|
|
+ *
|
|
+ * 0:7 index in fifo
|
|
+ * 8:13 fifo selection
|
|
+ * 14:14 bit indicating if this packet references the 'extra'
|
|
+ * storage at the end of the memory queue
|
|
+ * 15:15 (Rx indication)
|
|
+ *
|
|
+ */
|
|
+ __le16 sequence;
|
|
+
|
|
+ /* command data follows immediately */
|
|
+ u8 data[0];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/******************************************************************************
|
|
+ * (0a)
|
|
+ * Alive and Error Commands & Responses:
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+#define UCODE_VALID_OK __constant_cpu_to_le32(0x1)
|
|
+#define INITIALIZE_SUBTYPE (9)
|
|
+
|
|
+/*
|
|
+ * REPLY_ALIVE = 0x1 (response only, not a command)
|
|
+ */
|
|
+struct iwl_alive_resp {
|
|
+ u8 ucode_minor;
|
|
+ u8 ucode_major;
|
|
+ __le16 reserved1;
|
|
+ u8 sw_rev[8];
|
|
+ u8 ver_type;
|
|
+ u8 ver_subtype;
|
|
+ __le16 reserved2;
|
|
+ __le32 log_event_table_ptr;
|
|
+ __le32 error_event_table_ptr;
|
|
+ __le32 timestamp;
|
|
+ __le32 is_valid;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct iwl_init_alive_resp {
|
|
+ u8 ucode_minor;
|
|
+ u8 ucode_major;
|
|
+ __le16 reserved1;
|
|
+ u8 sw_rev[8];
|
|
+ u8 ver_type;
|
|
+ u8 ver_subtype;
|
|
+ __le16 reserved2;
|
|
+ __le32 log_event_table_ptr;
|
|
+ __le32 error_event_table_ptr;
|
|
+ __le32 timestamp;
|
|
+ __le32 is_valid;
|
|
+
|
|
+#if IWL == 4965
|
|
+ /* calibration values from "initialize" uCode */
|
|
+ __le32 voltage; /* signed */
|
|
+ __le32 therm_r1[2]; /* signed 1st for normal, 2nd for FAT channel */
|
|
+ __le32 therm_r2[2]; /* signed */
|
|
+ __le32 therm_r3[2]; /* signed */
|
|
+ __le32 therm_r4[2]; /* signed */
|
|
+ __le32 tx_atten[5][2]; /* signed MIMO gain comp, 5 freq groups,
|
|
+ * 2 Tx chains */
|
|
+#endif
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+union tsf {
|
|
+ u8 byte[8];
|
|
+ __le16 word[4];
|
|
+ __le32 dw[2];
|
|
+};
|
|
+
|
|
+/*
|
|
+ * REPLY_ERROR = 0x2 (response only, not a command)
|
|
+ */
|
|
+struct iwl_error_resp {
|
|
+ __le32 error_type;
|
|
+ u8 cmd_id;
|
|
+ u8 reserved1;
|
|
+ __le16 bad_cmd_seq_num;
|
|
+#if IWL == 3945
|
|
+ __le16 reserved2;
|
|
+#endif
|
|
+ __le32 error_info;
|
|
+ union tsf timestamp;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/******************************************************************************
|
|
+ * (1)
|
|
+ * RXON Commands & Responses:
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+/*
|
|
+ * Rx config defines & structure
|
|
+ */
|
|
+/* rx_config device types */
|
|
+enum {
|
|
+ RXON_DEV_TYPE_AP = 1,
|
|
+ RXON_DEV_TYPE_ESS = 3,
|
|
+ RXON_DEV_TYPE_IBSS = 4,
|
|
+ RXON_DEV_TYPE_SNIFFER = 6,
|
|
+};
|
|
+
|
|
+/* rx_config flags */
|
|
+/* band & modulation selection */
|
|
+#define RXON_FLG_BAND_24G_MSK __constant_cpu_to_le32(1 << 0)
|
|
+#define RXON_FLG_CCK_MSK __constant_cpu_to_le32(1 << 1)
|
|
+/* auto detection enable */
|
|
+#define RXON_FLG_AUTO_DETECT_MSK __constant_cpu_to_le32(1 << 2)
|
|
+/* TGg protection when tx */
|
|
+#define RXON_FLG_TGG_PROTECT_MSK __constant_cpu_to_le32(1 << 3)
|
|
+/* cck short slot & preamble */
|
|
+#define RXON_FLG_SHORT_SLOT_MSK __constant_cpu_to_le32(1 << 4)
|
|
+#define RXON_FLG_SHORT_PREAMBLE_MSK __constant_cpu_to_le32(1 << 5)
|
|
+/* antenna selection */
|
|
+#define RXON_FLG_DIS_DIV_MSK __constant_cpu_to_le32(1 << 7)
|
|
+#define RXON_FLG_ANT_SEL_MSK __constant_cpu_to_le32(0x0f00)
|
|
+#define RXON_FLG_ANT_A_MSK __constant_cpu_to_le32(1 << 8)
|
|
+#define RXON_FLG_ANT_B_MSK __constant_cpu_to_le32(1 << 9)
|
|
+/* radar detection enable */
|
|
+#define RXON_FLG_RADAR_DETECT_MSK __constant_cpu_to_le32(1 << 12)
|
|
+#define RXON_FLG_TGJ_NARROW_BAND_MSK __constant_cpu_to_le32(1 << 13)
|
|
+/* rx response to host with 8-byte TSF
|
|
+* (according to ON_AIR deassertion) */
|
|
+#define RXON_FLG_TSF2HOST_MSK __constant_cpu_to_le32(1 << 15)
|
|
+
|
|
+/* rx_config filter flags */
|
|
+/* accept all data frames */
|
|
+#define RXON_FILTER_PROMISC_MSK __constant_cpu_to_le32(1 << 0)
|
|
+/* pass control & management to host */
|
|
+#define RXON_FILTER_CTL2HOST_MSK __constant_cpu_to_le32(1 << 1)
|
|
+/* accept multi-cast */
|
|
+#define RXON_FILTER_ACCEPT_GRP_MSK __constant_cpu_to_le32(1 << 2)
|
|
+/* don't decrypt uni-cast frames */
|
|
+#define RXON_FILTER_DIS_DECRYPT_MSK __constant_cpu_to_le32(1 << 3)
|
|
+/* don't decrypt multi-cast frames */
|
|
+#define RXON_FILTER_DIS_GRP_DECRYPT_MSK __constant_cpu_to_le32(1 << 4)
|
|
+/* STA is associated */
|
|
+#define RXON_FILTER_ASSOC_MSK __constant_cpu_to_le32(1 << 5)
|
|
+/* transfer to host non bssid beacons in associated state */
|
|
+#define RXON_FILTER_BCON_AWARE_MSK __constant_cpu_to_le32(1 << 6)
|
|
+
|
|
+/*
|
|
+ * REPLY_RXON = 0x10 (command, has simple generic response)
|
|
+ */
|
|
+struct iwl_rxon_cmd {
|
|
+ u8 node_addr[6];
|
|
+ __le16 reserved1;
|
|
+ u8 bssid_addr[6];
|
|
+ __le16 reserved2;
|
|
+ u8 wlap_bssid_addr[6];
|
|
+ __le16 reserved3;
|
|
+ u8 dev_type;
|
|
+ u8 air_propagation;
|
|
+#if IWL == 3945
|
|
+ __le16 reserved4;
|
|
+#elif IWL == 4965
|
|
+ __le16 rx_chain;
|
|
+#endif
|
|
+ u8 ofdm_basic_rates;
|
|
+ u8 cck_basic_rates;
|
|
+ __le16 assoc_id;
|
|
+ __le32 flags;
|
|
+ __le32 filter_flags;
|
|
+ __le16 channel;
|
|
+#if IWL == 3945
|
|
+ __le16 reserved5;
|
|
+#elif IWL == 4965
|
|
+ u8 ofdm_ht_single_stream_basic_rates;
|
|
+ u8 ofdm_ht_dual_stream_basic_rates;
|
|
+#endif
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * REPLY_RXON_ASSOC = 0x11 (command, has simple generic response)
|
|
+ */
|
|
+struct iwl_rxon_assoc_cmd {
|
|
+ __le32 flags;
|
|
+ __le32 filter_flags;
|
|
+ u8 ofdm_basic_rates;
|
|
+ u8 cck_basic_rates;
|
|
+#if IWL == 4965
|
|
+ u8 ofdm_ht_single_stream_basic_rates;
|
|
+ u8 ofdm_ht_dual_stream_basic_rates;
|
|
+ __le16 rx_chain_select_flags;
|
|
+#endif
|
|
+ __le16 reserved;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * REPLY_RXON_TIMING = 0x14 (command, has simple generic response)
|
|
+ */
|
|
+struct iwl_rxon_time_cmd {
|
|
+ union tsf timestamp;
|
|
+ __le16 beacon_interval;
|
|
+ __le16 atim_window;
|
|
+ __le32 beacon_init_val;
|
|
+ __le16 listen_interval;
|
|
+ __le16 reserved;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct iwl_tx_power {
|
|
+ u8 tx_gain; /* gain for analog radio */
|
|
+ u8 dsp_atten; /* gain for DSP */
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+#if IWL == 3945
|
|
+struct iwl_power_per_rate {
|
|
+ u8 rate; /* plcp */
|
|
+ struct iwl_tx_power tpc;
|
|
+ u8 reserved;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+#elif IWL == 4965
|
|
+#define POWER_TABLE_NUM_ENTRIES 33
|
|
+#define POWER_TABLE_NUM_HT_OFDM_ENTRIES 32
|
|
+#define POWER_TABLE_CCK_ENTRY 32
|
|
+struct tx_power_dual_stream {
|
|
+ __le32 dw;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct iwl_tx_power_db {
|
|
+ struct tx_power_dual_stream power_tbl[POWER_TABLE_NUM_ENTRIES];
|
|
+} __attribute__ ((packed));
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * REPLY_CHANNEL_SWITCH = 0x72 (command, has simple generic response)
|
|
+ */
|
|
+struct iwl_channel_switch_cmd {
|
|
+ u8 band;
|
|
+ u8 expect_beacon;
|
|
+ __le16 channel;
|
|
+ __le32 rxon_flags;
|
|
+ __le32 rxon_filter_flags;
|
|
+ __le32 switch_time;
|
|
+#if IWL == 3945
|
|
+ struct iwl_power_per_rate power[IWL_MAX_RATES];
|
|
+#elif IWL == 4965
|
|
+ struct iwl_tx_power_db tx_power;
|
|
+#endif
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * CHANNEL_SWITCH_NOTIFICATION = 0x73 (notification only, not a command)
|
|
+ */
|
|
+struct iwl_csa_notification {
|
|
+ __le16 band;
|
|
+ __le16 channel;
|
|
+ __le32 status; /* 0 - OK, 1 - fail */
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/******************************************************************************
|
|
+ * (2)
|
|
+ * Quality-of-Service (QOS) Commands & Responses:
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+struct iwl_ac_qos {
|
|
+ __le16 cw_min;
|
|
+ __le16 cw_max;
|
|
+ u8 aifsn;
|
|
+ u8 reserved1;
|
|
+ __le16 edca_txop;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/* QoS flags defines */
|
|
+#define QOS_PARAM_FLG_UPDATE_EDCA_MSK __constant_cpu_to_le32(0x01)
|
|
+#define QOS_PARAM_FLG_TGN_MSK __constant_cpu_to_le32(0x02)
|
|
+#define QOS_PARAM_FLG_TXOP_TYPE_MSK __constant_cpu_to_le32(0x10)
|
|
+
|
|
+/*
|
|
+ * TXFIFO Queue number defines
|
|
+ */
|
|
+/* number of Access categories (AC) (EDCA), queues 0..3 */
|
|
+#define AC_NUM 4
|
|
+
|
|
+/*
|
|
+ * REPLY_QOS_PARAM = 0x13 (command, has simple generic response)
|
|
+ */
|
|
+struct iwl_qosparam_cmd {
|
|
+ __le32 qos_flags;
|
|
+ struct iwl_ac_qos ac[AC_NUM];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/******************************************************************************
|
|
+ * (3)
|
|
+ * Add/Modify Stations Commands & Responses:
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+/*
|
|
+ * Multi station support
|
|
+ */
|
|
+#if IWL == 3945
|
|
+enum {
|
|
+ IWL_AP_ID = 0,
|
|
+ IWL_MULTICAST_ID,
|
|
+ IWL_STA_ID,
|
|
+ IWL_BROADCAST_ID = 24,
|
|
+ IWL_STATION_COUNT = 25,
|
|
+ IWL_INVALID_STATION
|
|
+};
|
|
+#elif IWL == 4965
|
|
+enum {
|
|
+ IWL_AP_ID = 0,
|
|
+ IWL_MULTICAST_ID,
|
|
+ IWL_STA_ID,
|
|
+ IWL_BROADCAST_ID = 31,
|
|
+ IWL_STATION_COUNT = 32,
|
|
+ IWL_INVALID_STATION
|
|
+};
|
|
+#endif
|
|
+
|
|
+#if IWL == 3945
|
|
+#define STA_FLG_TX_RATE_MSK __constant_cpu_to_le32(1<<2);
|
|
+#endif
|
|
+#define STA_FLG_PWR_SAVE_MSK __constant_cpu_to_le32(1<<8);
|
|
+
|
|
+#define STA_CONTROL_MODIFY_MSK 0x01
|
|
+
|
|
+/* key flags __le16*/
|
|
+#define STA_KEY_FLG_ENCRYPT_MSK __constant_cpu_to_le16(0x7)
|
|
+#define STA_KEY_FLG_NO_ENC __constant_cpu_to_le16(0x0)
|
|
+#define STA_KEY_FLG_WEP __constant_cpu_to_le16(0x1)
|
|
+#define STA_KEY_FLG_CCMP __constant_cpu_to_le16(0x2)
|
|
+#define STA_KEY_FLG_TKIP __constant_cpu_to_le16(0x3)
|
|
+
|
|
+#define STA_KEY_FLG_KEYID_POS 8
|
|
+#define STA_KEY_FLG_INVALID __constant_cpu_to_le16(0x0800)
|
|
+
|
|
+/* modify flags */
|
|
+enum {
|
|
+ STA_MODIFY_KEY_MASK = 0x01,
|
|
+ STA_MODIFY_TID_DISABLE_TX = 0x02,
|
|
+ STA_MODIFY_TX_RATE_MSK = 0x04
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Antenna masks:
|
|
+ * bit14:15 01 B inactive, A active
|
|
+ * 10 B active, A inactive
|
|
+ * 11 Both active
|
|
+ */
|
|
+#define RATE_MCS_ANT_A_POS 14
|
|
+#define RATE_MCS_ANT_B_POS 15
|
|
+#define RATE_MCS_ANT_A_MSK 0x4000
|
|
+#define RATE_MCS_ANT_B_MSK 0x8000
|
|
+#define RATE_MCS_ANT_AB_MSK 0xc000
|
|
+
|
|
+struct iwl_keyinfo {
|
|
+ __le16 key_flags;
|
|
+ u8 tkip_rx_tsc_byte2; /* TSC[2] for key mix ph1 detection */
|
|
+ u8 reserved1;
|
|
+ __le16 tkip_rx_ttak[5]; /* 10-byte unicast TKIP TTAK */
|
|
+ __le16 reserved2;
|
|
+ u8 key[16]; /* 16-byte unicast decryption key */
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct sta_id_modify {
|
|
+ u8 addr[ETH_ALEN];
|
|
+ __le16 reserved1;
|
|
+ u8 sta_id;
|
|
+ u8 modify_mask;
|
|
+ __le16 reserved2;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * REPLY_ADD_STA = 0x18 (command)
|
|
+ */
|
|
+struct iwl_addsta_cmd {
|
|
+ u8 mode;
|
|
+ u8 reserved[3];
|
|
+ struct sta_id_modify sta;
|
|
+ struct iwl_keyinfo key;
|
|
+ __le32 station_flags;
|
|
+ __le32 station_flags_msk;
|
|
+ __le16 tid_disable_tx;
|
|
+#if IWL == 3945
|
|
+ __le16 rate_n_flags;
|
|
+#else
|
|
+ __le16 reserved1;
|
|
+#endif
|
|
+ u8 add_immediate_ba_tid;
|
|
+ u8 remove_immediate_ba_tid;
|
|
+ __le16 add_immediate_ba_ssn;
|
|
+#if IWL == 4965
|
|
+ __le32 reserved2;
|
|
+#endif
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * REPLY_ADD_STA = 0x18 (response)
|
|
+ */
|
|
+struct iwl_add_sta_resp {
|
|
+ u8 status;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+#define ADD_STA_SUCCESS_MSK 0x1
|
|
+
|
|
+/******************************************************************************
|
|
+ * (4)
|
|
+ * Rx Responses:
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+struct iwl_rx_frame_stats {
|
|
+ u8 phy_count;
|
|
+ u8 id;
|
|
+ u8 rssi;
|
|
+ u8 agc;
|
|
+ __le16 sig_avg;
|
|
+ __le16 noise_diff;
|
|
+ u8 payload[0];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct iwl_rx_frame_hdr {
|
|
+ __le16 channel;
|
|
+ __le16 phy_flags;
|
|
+ u8 reserved1;
|
|
+ u8 rate;
|
|
+ __le16 len;
|
|
+ u8 payload[0];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+#define RX_RES_STATUS_NO_CRC32_ERROR __constant_cpu_to_le32(1 << 0)
|
|
+#define RX_RES_STATUS_NO_RXE_OVERFLOW __constant_cpu_to_le32(1 << 1)
|
|
+
|
|
+#define RX_RES_PHY_FLAGS_BAND_24_MSK __constant_cpu_to_le16(1 << 0)
|
|
+#define RX_RES_PHY_FLAGS_MOD_CCK_MSK __constant_cpu_to_le16(1 << 1)
|
|
+#define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK __constant_cpu_to_le16(1 << 2)
|
|
+#define RX_RES_PHY_FLAGS_NARROW_BAND_MSK __constant_cpu_to_le16(1 << 3)
|
|
+#define RX_RES_PHY_FLAGS_ANTENNA_MSK __constant_cpu_to_le16(0xf0)
|
|
+
|
|
+#define RX_RES_STATUS_SEC_TYPE_MSK (0x7 << 8)
|
|
+#define RX_RES_STATUS_SEC_TYPE_NONE (0x0 << 8)
|
|
+#define RX_RES_STATUS_SEC_TYPE_WEP (0x1 << 8)
|
|
+#define RX_RES_STATUS_SEC_TYPE_CCMP (0x2 << 8)
|
|
+#define RX_RES_STATUS_SEC_TYPE_TKIP (0x3 << 8)
|
|
+
|
|
+#define RX_RES_STATUS_DECRYPT_TYPE_MSK (0x3 << 11)
|
|
+#define RX_RES_STATUS_NOT_DECRYPT (0x0 << 11)
|
|
+#define RX_RES_STATUS_DECRYPT_OK (0x3 << 11)
|
|
+#define RX_RES_STATUS_BAD_ICV_MIC (0x1 << 11)
|
|
+#define RX_RES_STATUS_BAD_KEY_TTAK (0x2 << 11)
|
|
+
|
|
+struct iwl_rx_frame_end {
|
|
+ __le32 status;
|
|
+ __le64 timestamp;
|
|
+ __le32 beacon_timestamp;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * REPLY_3945_RX = 0x1b (response only, not a command)
|
|
+ *
|
|
+ * NOTE: DO NOT dereference from casts to this structure
|
|
+ * It is provided only for calculating minimum data set size.
|
|
+ * The actual offsets of the hdr and end are dynamic based on
|
|
+ * stats.phy_count
|
|
+ */
|
|
+struct iwl_rx_frame {
|
|
+ struct iwl_rx_frame_stats stats;
|
|
+ struct iwl_rx_frame_hdr hdr;
|
|
+ struct iwl_rx_frame_end end;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * REPLY_COMPRESSED_BA = 0xc5 (response only, not a command)
|
|
+ */
|
|
+struct iwl_compressed_ba_resp {
|
|
+ __le32 sta_addr_lo32;
|
|
+ __le16 sta_addr_hi16;
|
|
+ __le16 reserved;
|
|
+ u8 sta_id;
|
|
+ u8 tid;
|
|
+ __le16 ba_seq_ctl;
|
|
+ __le32 ba_bitmap0;
|
|
+ __le32 ba_bitmap1;
|
|
+ __le16 scd_flow;
|
|
+ __le16 scd_ssn;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/******************************************************************************
|
|
+ * (5)
|
|
+ * Tx Commands & Responses:
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+/* Tx flags */
|
|
+#define TX_CMD_FLG_RTS_MSK __constant_cpu_to_le32(1 << 1)
|
|
+#define TX_CMD_FLG_CTS_MSK __constant_cpu_to_le32(1 << 2)
|
|
+#define TX_CMD_FLG_ACK_MSK __constant_cpu_to_le32(1 << 3)
|
|
+#define TX_CMD_FLG_STA_RATE_MSK __constant_cpu_to_le32(1 << 4)
|
|
+#define TX_CMD_FLG_IMM_BA_RSP_MASK __constant_cpu_to_le32(1 << 6)
|
|
+#define TX_CMD_FLG_FULL_TXOP_PROT_MSK __constant_cpu_to_le32(1 << 7)
|
|
+#define TX_CMD_FLG_ANT_SEL_MSK __constant_cpu_to_le32(0xf00)
|
|
+#define TX_CMD_FLG_ANT_A_MSK __constant_cpu_to_le32(1 << 8)
|
|
+#define TX_CMD_FLG_ANT_B_MSK __constant_cpu_to_le32(1 << 9)
|
|
+
|
|
+/* ucode ignores BT priority for this frame */
|
|
+#define TX_CMD_FLG_BT_DIS_MSK __constant_cpu_to_le32(1 << 12)
|
|
+
|
|
+/* ucode overrides sequence control */
|
|
+#define TX_CMD_FLG_SEQ_CTL_MSK __constant_cpu_to_le32(1 << 13)
|
|
+
|
|
+/* signal that this frame is non-last MPDU */
|
|
+#define TX_CMD_FLG_MORE_FRAG_MSK __constant_cpu_to_le32(1 << 14)
|
|
+
|
|
+/* calculate TSF in outgoing frame */
|
|
+#define TX_CMD_FLG_TSF_MSK __constant_cpu_to_le32(1 << 16)
|
|
+
|
|
+/* activate TX calibration. */
|
|
+#define TX_CMD_FLG_CALIB_MSK __constant_cpu_to_le32(1 << 17)
|
|
+
|
|
+/* signals that 2 bytes pad was inserted
|
|
+ after the MAC header */
|
|
+#define TX_CMD_FLG_MH_PAD_MSK __constant_cpu_to_le32(1 << 20)
|
|
+
|
|
+/* HCCA-AP - disable duration overwriting. */
|
|
+#define TX_CMD_FLG_DUR_MSK __constant_cpu_to_le32(1 << 25)
|
|
+
|
|
+/*
|
|
+ * TX command security control
|
|
+ */
|
|
+#define TX_CMD_SEC_CCM 0x2
|
|
+#define TX_CMD_SEC_TKIP 0x3
|
|
+
|
|
+/*
|
|
+ * TX command Frame life time
|
|
+ */
|
|
+
|
|
+struct iwl_dram_scratch {
|
|
+ u8 try_cnt;
|
|
+ u8 bt_kill_cnt;
|
|
+ __le16 reserved;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * REPLY_TX = 0x1c (command)
|
|
+ */
|
|
+struct iwl_tx_cmd {
|
|
+ __le16 len;
|
|
+ __le16 next_frame_len;
|
|
+ __le32 tx_flags;
|
|
+#if IWL == 3945
|
|
+ u8 rate;
|
|
+ u8 sta_id;
|
|
+ u8 tid_tspec;
|
|
+#elif IWL == 4965
|
|
+ struct iwl_dram_scratch scratch;
|
|
+ __le32 rate_n_flags;
|
|
+ u8 sta_id;
|
|
+#endif
|
|
+ u8 sec_ctl;
|
|
+#if IWL == 4965
|
|
+ u8 initial_rate_index;
|
|
+ u8 reserved;
|
|
+#endif
|
|
+ u8 key[16];
|
|
+#if IWL == 3945
|
|
+ union {
|
|
+ u8 byte[8];
|
|
+ __le16 word[4];
|
|
+ __le32 dw[2];
|
|
+ } tkip_mic;
|
|
+ __le32 next_frame_info;
|
|
+#elif IWL == 4965
|
|
+ __le16 next_frame_flags;
|
|
+ __le16 reserved2;
|
|
+#endif
|
|
+ union {
|
|
+ __le32 life_time;
|
|
+ __le32 attempt;
|
|
+ } stop_time;
|
|
+#if IWL == 3945
|
|
+ u8 supp_rates[2];
|
|
+#elif IWL == 4965
|
|
+ __le32 dram_lsb_ptr;
|
|
+ u8 dram_msb_ptr;
|
|
+#endif
|
|
+ u8 rts_retry_limit; /*byte 50 */
|
|
+ u8 data_retry_limit; /*byte 51 */
|
|
+#if IWL == 4965
|
|
+ u8 tid_tspec;
|
|
+#endif
|
|
+ union {
|
|
+ __le16 pm_frame_timeout;
|
|
+ __le16 attempt_duration;
|
|
+ } timeout;
|
|
+ __le16 driver_txop;
|
|
+ u8 payload[0];
|
|
+ struct ieee80211_hdr hdr[0];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/* TX command response is sent after *all* transmission attempts.
|
|
+ *
|
|
+ * NOTES:
|
|
+ *
|
|
+ * TX_STATUS_FAIL_NEXT_FRAG
|
|
+ *
|
|
+ * If the fragment flag in the MAC header for the frame being transmitted
|
|
+ * is set and there is insufficient time to transmit the next frame, the
|
|
+ * TX status will be returned with 'TX_STATUS_FAIL_NEXT_FRAG'.
|
|
+ *
|
|
+ * TX_STATUS_FIFO_UNDERRUN
|
|
+ *
|
|
+ * Indicates the host did not provide bytes to the FIFO fast enough while
|
|
+ * a TX was in progress.
|
|
+ *
|
|
+ * TX_STATUS_FAIL_MGMNT_ABORT
|
|
+ *
|
|
+ * This status is only possible if the ABORT ON MGMT RX parameter was
|
|
+ * set to true with the TX command.
|
|
+ *
|
|
+ * If the MSB of the status parameter is set then an abort sequence is
|
|
+ * required. This sequence consists of the host activating the TX Abort
|
|
+ * control line, and then waiting for the TX Abort command response. This
|
|
+ * indicates that a the device is no longer in a transmit state, and that the
|
|
+ * command FIFO has been cleared. The host must then deactivate the TX Abort
|
|
+ * control line. Receiving is still allowed in this case.
|
|
+ */
|
|
+enum {
|
|
+ TX_STATUS_SUCCESS = 0x01,
|
|
+ TX_STATUS_DIRECT_DONE = 0x02,
|
|
+ TX_STATUS_FAIL_SHORT_LIMIT = 0x82,
|
|
+ TX_STATUS_FAIL_LONG_LIMIT = 0x83,
|
|
+ TX_STATUS_FAIL_FIFO_UNDERRUN = 0x84,
|
|
+ TX_STATUS_FAIL_MGMNT_ABORT = 0x85,
|
|
+ TX_STATUS_FAIL_NEXT_FRAG = 0x86,
|
|
+ TX_STATUS_FAIL_LIFE_EXPIRE = 0x87,
|
|
+ TX_STATUS_FAIL_DEST_PS = 0x88,
|
|
+ TX_STATUS_FAIL_ABORTED = 0x89,
|
|
+ TX_STATUS_FAIL_BT_RETRY = 0x8a,
|
|
+ TX_STATUS_FAIL_STA_INVALID = 0x8b,
|
|
+ TX_STATUS_FAIL_FRAG_DROPPED = 0x8c,
|
|
+ TX_STATUS_FAIL_TID_DISABLE = 0x8d,
|
|
+ TX_STATUS_FAIL_FRAME_FLUSHED = 0x8e,
|
|
+ TX_STATUS_FAIL_INSUFFICIENT_CF_POLL = 0x8f,
|
|
+ TX_STATUS_FAIL_TX_LOCKED = 0x90,
|
|
+ TX_STATUS_FAIL_NO_BEACON_ON_RADAR = 0x91,
|
|
+};
|
|
+
|
|
+#define TX_PACKET_MODE_REGULAR 0x0000
|
|
+#define TX_PACKET_MODE_BURST_SEQ 0x0100
|
|
+#define TX_PACKET_MODE_BURST_FIRST 0x0200
|
|
+
|
|
+enum {
|
|
+ TX_POWER_PA_NOT_ACTIVE = 0x0,
|
|
+};
|
|
+
|
|
+enum {
|
|
+ TX_STATUS_MSK = 0x000000ff, /* bits 0:7 */
|
|
+ TX_STATUS_DELAY_MSK = 0x00000040,
|
|
+ TX_STATUS_ABORT_MSK = 0x00000080,
|
|
+ TX_PACKET_MODE_MSK = 0x0000ff00, /* bits 8:15 */
|
|
+ TX_FIFO_NUMBER_MSK = 0x00070000, /* bits 16:18 */
|
|
+ TX_RESERVED = 0x00780000, /* bits 19:22 */
|
|
+ TX_POWER_PA_DETECT_MSK = 0x7f800000, /* bits 23:30 */
|
|
+ TX_ABORT_REQUIRED_MSK = 0x80000000, /* bits 31:31 */
|
|
+};
|
|
+
|
|
+/* *******************************
|
|
+ * TX aggregation state
|
|
+ ******************************* */
|
|
+
|
|
+enum {
|
|
+ AGG_TX_STATE_TRANSMITTED = 0x00,
|
|
+ AGG_TX_STATE_UNDERRUN_MSK = 0x01,
|
|
+ AGG_TX_STATE_BT_PRIO_MSK = 0x02,
|
|
+ AGG_TX_STATE_FEW_BYTES_MSK = 0x04,
|
|
+ AGG_TX_STATE_ABORT_MSK = 0x08,
|
|
+ AGG_TX_STATE_LAST_SENT_TTL_MSK = 0x10,
|
|
+ AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK = 0x20,
|
|
+ AGG_TX_STATE_LAST_SENT_BT_KILL_MSK = 0x40,
|
|
+ AGG_TX_STATE_SCD_QUERY_MSK = 0x80,
|
|
+ AGG_TX_STATE_TEST_BAD_CRC32_MSK = 0x100,
|
|
+ AGG_TX_STATE_RESPONSE_MSK = 0x1ff,
|
|
+ AGG_TX_STATE_DUMP_TX_MSK = 0x200,
|
|
+ AGG_TX_STATE_DELAY_TX_MSK = 0x400
|
|
+};
|
|
+
|
|
+#define AGG_TX_STATE_LAST_SENT_MSK \
|
|
+(AGG_TX_STATE_LAST_SENT_TTL_MSK | \
|
|
+ AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK | \
|
|
+ AGG_TX_STATE_LAST_SENT_BT_KILL_MSK)
|
|
+
|
|
+#define AGG_TX_STATE_TRY_CNT_POS 12
|
|
+#define AGG_TX_STATE_TRY_CNT_MSK 0xf000
|
|
+
|
|
+#define AGG_TX_STATE_SEQ_NUM_POS 16
|
|
+#define AGG_TX_STATE_SEQ_NUM_MSK 0xffff0000
|
|
+
|
|
+/*
|
|
+ * REPLY_TX = 0x1c (response)
|
|
+ */
|
|
+#if IWL == 4965
|
|
+struct iwl_tx_resp {
|
|
+ u8 frame_count; /* 1 no aggregation, >1 aggregation */
|
|
+ u8 bt_kill_count;
|
|
+ u8 failure_rts;
|
|
+ u8 failure_frame;
|
|
+ __le32 rate_n_flags;
|
|
+ __le16 wireless_media_time;
|
|
+ __le16 reserved;
|
|
+ __le32 pa_power1;
|
|
+ __le32 pa_power2;
|
|
+ __le32 status; /* TX status (for aggregation status of 1st frame) */
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+#elif IWL == 3945
|
|
+struct iwl_tx_resp {
|
|
+ u8 failure_rts;
|
|
+ u8 failure_frame;
|
|
+ u8 bt_kill_count;
|
|
+ u8 rate;
|
|
+ __le32 wireless_media_time;
|
|
+ __le32 status; /* TX status (for aggregation status of 1st frame) */
|
|
+} __attribute__ ((packed));
|
|
+#endif
|
|
+
|
|
+#if IWL == 3945
|
|
+/*
|
|
+ * REPLY_TX_PWR_TABLE_CMD = 0x97 (command, has simple generic response)
|
|
+ * 3945 Tx Power Table Command
|
|
+ */
|
|
+struct iwl_txpowertable_cmd {
|
|
+ u8 band;
|
|
+ u8 reserved;
|
|
+ __le16 channel;
|
|
+ struct iwl_power_per_rate power[IWL_MAX_RATES];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+#elif IWL == 4965
|
|
+/*
|
|
+ * REPLY_TX_PWR_TABLE_CMD = 0x97 (command, has simple generic response)
|
|
+ * 4965 Tx Power Table Command
|
|
+ */
|
|
+struct iwl_tx_power_table_cmd {
|
|
+ u8 band;
|
|
+ u8 channel_normal_width;
|
|
+ __le16 channel;
|
|
+ struct iwl_tx_power_db tx_power;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+#endif
|
|
+
|
|
+
|
|
+#if IWL == 3945
|
|
+struct iwl_rate_scaling_info {
|
|
+ __le16 rate_n_flags;
|
|
+ u8 try_cnt;
|
|
+ u8 next_rate_index;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/**
|
|
+ * struct iwl_rate_scaling_cmd - Rate Scaling Command & Response
|
|
+ *
|
|
+ * REPLY_RATE_SCALE = 0x47 (command, has simple generic response)
|
|
+ *
|
|
+ * NOTE: The table of rates passed to the uCode via the
|
|
+ * RATE_SCALE command sets up the corresponding order of
|
|
+ * rates used for all related commands, including rate
|
|
+ * masks, etc.
|
|
+ *
|
|
+ * For example, if you set 9MB (PLCP 0x0f) as the first
|
|
+ * rate in the rate table, the bit mask for that rate
|
|
+ * when passed through ofdm_basic_rates on the REPLY_RXON
|
|
+ * command would be bit 0 (1<<0)
|
|
+ */
|
|
+struct iwl_rate_scaling_cmd {
|
|
+ u8 table_id;
|
|
+ u8 reserved[3];
|
|
+ struct iwl_rate_scaling_info table[IWL_MAX_RATES];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+#elif IWL == 4965
|
|
+
|
|
+/*RS_NEW_API: only TLC_RTS remains and moved to bit 0 */
|
|
+#define LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK (1<<0)
|
|
+
|
|
+#define LINK_QUAL_AC_NUM AC_NUM
|
|
+#define LINK_QUAL_MAX_RETRY_NUM 16
|
|
+
|
|
+#define LINK_QUAL_ANT_A_MSK (1<<0)
|
|
+#define LINK_QUAL_ANT_B_MSK (1<<1)
|
|
+#define LINK_QUAL_ANT_MSK (LINK_QUAL_ANT_A_MSK|LINK_QUAL_ANT_B_MSK)
|
|
+
|
|
+struct iwl_link_qual_general_params {
|
|
+ u8 flags;
|
|
+ u8 mimo_delimiter;
|
|
+ u8 single_stream_ant_msk;
|
|
+ u8 dual_stream_ant_msk;
|
|
+ u8 start_rate_index[LINK_QUAL_AC_NUM];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct iwl_link_qual_agg_params {
|
|
+ __le16 agg_time_limit;
|
|
+ u8 agg_dis_start_th;
|
|
+ u8 agg_frame_cnt_limit;
|
|
+ __le32 reserved;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * REPLY_TX_LINK_QUALITY_CMD = 0x4e (command, has simple generic response)
|
|
+ */
|
|
+struct iwl_link_quality_cmd {
|
|
+ u8 sta_id;
|
|
+ u8 reserved1;
|
|
+ __le16 control;
|
|
+ struct iwl_link_qual_general_params general_params;
|
|
+ struct iwl_link_qual_agg_params agg_params;
|
|
+ struct {
|
|
+ __le32 rate_n_flags;
|
|
+ } rs_table[LINK_QUAL_MAX_RETRY_NUM];
|
|
+ __le32 reserved2;
|
|
+} __attribute__ ((packed));
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * REPLY_BT_CONFIG = 0x9b (command, has simple generic response)
|
|
+ */
|
|
+struct iwl_bt_cmd {
|
|
+ u8 flags;
|
|
+ u8 lead_time;
|
|
+ u8 max_kill;
|
|
+ u8 reserved;
|
|
+ __le32 kill_ack_mask;
|
|
+ __le32 kill_cts_mask;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/******************************************************************************
|
|
+ * (6)
|
|
+ * Spectrum Management (802.11h) Commands, Responses, Notifications:
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+/*
|
|
+ * Spectrum Management
|
|
+ */
|
|
+#define MEASUREMENT_FILTER_FLAG (RXON_FILTER_PROMISC_MSK | \
|
|
+ RXON_FILTER_CTL2HOST_MSK | \
|
|
+ RXON_FILTER_ACCEPT_GRP_MSK | \
|
|
+ RXON_FILTER_DIS_DECRYPT_MSK | \
|
|
+ RXON_FILTER_DIS_GRP_DECRYPT_MSK | \
|
|
+ RXON_FILTER_ASSOC_MSK | \
|
|
+ RXON_FILTER_BCON_AWARE_MSK)
|
|
+
|
|
+struct iwl_measure_channel {
|
|
+ __le32 duration; /* measurement duration in extended beacon
|
|
+ * format */
|
|
+ u8 channel; /* channel to measure */
|
|
+ u8 type; /* see enum iwl_measure_type */
|
|
+ __le16 reserved;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74 (command)
|
|
+ */
|
|
+struct iwl_spectrum_cmd {
|
|
+ __le16 len; /* number of bytes starting from token */
|
|
+ u8 token; /* token id */
|
|
+ u8 id; /* measurement id -- 0 or 1 */
|
|
+ u8 origin; /* 0 = TGh, 1 = other, 2 = TGk */
|
|
+ u8 periodic; /* 1 = periodic */
|
|
+ __le16 path_loss_timeout;
|
|
+ __le32 start_time; /* start time in extended beacon format */
|
|
+ __le32 reserved2;
|
|
+ __le32 flags; /* rxon flags */
|
|
+ __le32 filter_flags; /* rxon filter flags */
|
|
+ __le16 channel_count; /* minimum 1, maximum 10 */
|
|
+ __le16 reserved3;
|
|
+ struct iwl_measure_channel channels[10];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74 (response)
|
|
+ */
|
|
+struct iwl_spectrum_resp {
|
|
+ u8 token;
|
|
+ u8 id; /* id of the prior command replaced, or 0xff */
|
|
+ __le16 status; /* 0 - command will be handled
|
|
+ * 1 - cannot handle (conflicts with another
|
|
+ * measurement) */
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+enum iwl_measurement_state {
|
|
+ IWL_MEASUREMENT_START = 0,
|
|
+ IWL_MEASUREMENT_STOP = 1,
|
|
+};
|
|
+
|
|
+enum iwl_measurement_status {
|
|
+ IWL_MEASUREMENT_OK = 0,
|
|
+ IWL_MEASUREMENT_CONCURRENT = 1,
|
|
+ IWL_MEASUREMENT_CSA_CONFLICT = 2,
|
|
+ IWL_MEASUREMENT_TGH_CONFLICT = 3,
|
|
+ /* 4-5 reserved */
|
|
+ IWL_MEASUREMENT_STOPPED = 6,
|
|
+ IWL_MEASUREMENT_TIMEOUT = 7,
|
|
+ IWL_MEASUREMENT_PERIODIC_FAILED = 8,
|
|
+};
|
|
+
|
|
+#define NUM_ELEMENTS_IN_HISTOGRAM 8
|
|
+
|
|
+struct iwl_measurement_histogram {
|
|
+ __le32 ofdm[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 0.8usec counts */
|
|
+ __le32 cck[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 1usec counts */
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/* clear channel availability counters */
|
|
+struct iwl_measurement_cca_counters {
|
|
+ __le32 ofdm;
|
|
+ __le32 cck;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+enum iwl_measure_type {
|
|
+ IWL_MEASURE_BASIC = (1 << 0),
|
|
+ IWL_MEASURE_CHANNEL_LOAD = (1 << 1),
|
|
+ IWL_MEASURE_HISTOGRAM_RPI = (1 << 2),
|
|
+ IWL_MEASURE_HISTOGRAM_NOISE = (1 << 3),
|
|
+ IWL_MEASURE_FRAME = (1 << 4),
|
|
+ /* bits 5:6 are reserved */
|
|
+ IWL_MEASURE_IDLE = (1 << 7),
|
|
+};
|
|
+
|
|
+/*
|
|
+ * SPECTRUM_MEASURE_NOTIFICATION = 0x75 (notification only, not a command)
|
|
+ */
|
|
+struct iwl_spectrum_notification {
|
|
+ u8 id; /* measurement id -- 0 or 1 */
|
|
+ u8 token;
|
|
+ u8 channel_index; /* index in measurement channel list */
|
|
+ u8 state; /* 0 - start, 1 - stop */
|
|
+ __le32 start_time; /* lower 32-bits of TSF */
|
|
+ u8 band; /* 0 - 5.2GHz, 1 - 2.4GHz */
|
|
+ u8 channel;
|
|
+ u8 type; /* see enum iwl_measurement_type */
|
|
+ u8 reserved1;
|
|
+ /* NOTE: cca_ofdm, cca_cck, basic_type, and histogram are only only
|
|
+ * valid if applicable for measurement type requested. */
|
|
+ __le32 cca_ofdm; /* cca fraction time in 40Mhz clock periods */
|
|
+ __le32 cca_cck; /* cca fraction time in 44Mhz clock periods */
|
|
+ __le32 cca_time; /* channel load time in usecs */
|
|
+ u8 basic_type; /* 0 - bss, 1 - ofdm preamble, 2 -
|
|
+ * unidentified */
|
|
+ u8 reserved2[3];
|
|
+ struct iwl_measurement_histogram histogram;
|
|
+ __le32 stop_time; /* lower 32-bits of TSF */
|
|
+ __le32 status; /* see iwl_measurement_status */
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/******************************************************************************
|
|
+ * (7)
|
|
+ * Power Management Commands, Responses, Notifications:
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+/**
|
|
+ * struct iwl_powertable_cmd - Power Table Command
|
|
+ * @flags: See below:
|
|
+ *
|
|
+ * POWER_TABLE_CMD = 0x77 (command, has simple generic response)
|
|
+ *
|
|
+ * PM allow:
|
|
+ * bit 0 - '0' Driver not allow power management
|
|
+ * '1' Driver allow PM (use rest of parameters)
|
|
+ * uCode send sleep notifications:
|
|
+ * bit 1 - '0' Don't send sleep notification
|
|
+ * '1' send sleep notification (SEND_PM_NOTIFICATION)
|
|
+ * Sleep over DTIM
|
|
+ * bit 2 - '0' PM have to walk up every DTIM
|
|
+ * '1' PM could sleep over DTIM till listen Interval.
|
|
+ * PCI power managed
|
|
+ * bit 3 - '0' (PCI_LINK_CTRL & 0x1)
|
|
+ * '1' !(PCI_LINK_CTRL & 0x1)
|
|
+ * Force sleep Modes
|
|
+ * bit 31/30- '00' use both mac/xtal sleeps
|
|
+ * '01' force Mac sleep
|
|
+ * '10' force xtal sleep
|
|
+ * '11' Illegal set
|
|
+ *
|
|
+ * NOTE: if sleep_interval[SLEEP_INTRVL_TABLE_SIZE-1] > DTIM period then
|
|
+ * ucode assume sleep over DTIM is allowed and we don't need to wakeup
|
|
+ * for every DTIM.
|
|
+ */
|
|
+#define IWL_POWER_VEC_SIZE 5
|
|
+
|
|
+
|
|
+#if IWL == 3945
|
|
+
|
|
+#define IWL_POWER_DRIVER_ALLOW_SLEEP_MSK __constant_cpu_to_le32(1<<0)
|
|
+#define IWL_POWER_SLEEP_OVER_DTIM_MSK __constant_cpu_to_le32(1<<2)
|
|
+#define IWL_POWER_PCI_PM_MSK __constant_cpu_to_le32(1<<3)
|
|
+struct iwl_powertable_cmd {
|
|
+ __le32 flags;
|
|
+ __le32 rx_data_timeout;
|
|
+ __le32 tx_data_timeout;
|
|
+ __le32 sleep_interval[IWL_POWER_VEC_SIZE];
|
|
+} __attribute__((packed));
|
|
+
|
|
+#elif IWL == 4965
|
|
+
|
|
+#define IWL_POWER_DRIVER_ALLOW_SLEEP_MSK __constant_cpu_to_le16(1<<0)
|
|
+#define IWL_POWER_SLEEP_OVER_DTIM_MSK __constant_cpu_to_le16(1<<2)
|
|
+#define IWL_POWER_PCI_PM_MSK __constant_cpu_to_le16(1<<3)
|
|
+
|
|
+struct iwl_powertable_cmd {
|
|
+ __le16 flags;
|
|
+ u8 keep_alive_seconds;
|
|
+ u8 debug_flags;
|
|
+ __le32 rx_data_timeout;
|
|
+ __le32 tx_data_timeout;
|
|
+ __le32 sleep_interval[IWL_POWER_VEC_SIZE];
|
|
+ __le32 keep_alive_beacons;
|
|
+} __attribute__ ((packed));
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * PM_SLEEP_NOTIFICATION = 0x7A (notification only, not a command)
|
|
+ * 3945 and 4965 identical.
|
|
+ */
|
|
+struct iwl_sleep_notification {
|
|
+ u8 pm_sleep_mode;
|
|
+ u8 pm_wakeup_src;
|
|
+ __le16 reserved;
|
|
+ __le32 sleep_time;
|
|
+ __le32 tsf_low;
|
|
+ __le32 bcon_timer;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/* Sleep states. 3945 and 4965 identical. */
|
|
+enum {
|
|
+ IWL_PM_NO_SLEEP = 0,
|
|
+ IWL_PM_SLP_MAC = 1,
|
|
+ IWL_PM_SLP_FULL_MAC_UNASSOCIATE = 2,
|
|
+ IWL_PM_SLP_FULL_MAC_CARD_STATE = 3,
|
|
+ IWL_PM_SLP_PHY = 4,
|
|
+ IWL_PM_SLP_REPENT = 5,
|
|
+ IWL_PM_WAKEUP_BY_TIMER = 6,
|
|
+ IWL_PM_WAKEUP_BY_DRIVER = 7,
|
|
+ IWL_PM_WAKEUP_BY_RFKILL = 8,
|
|
+ /* 3 reserved */
|
|
+ IWL_PM_NUM_OF_MODES = 12,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * REPLY_CARD_STATE_CMD = 0xa0 (command, has simple generic response)
|
|
+ */
|
|
+#define CARD_STATE_CMD_DISABLE 0x00 /* Put card to sleep */
|
|
+#define CARD_STATE_CMD_ENABLE 0x01 /* Wake up card */
|
|
+#define CARD_STATE_CMD_HALT 0x02 /* Power down permanently */
|
|
+struct iwl_card_state_cmd {
|
|
+ __le32 status; /* CARD_STATE_CMD_* request new power state */
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * CARD_STATE_NOTIFICATION = 0xa1 (notification only, not a command)
|
|
+ */
|
|
+struct iwl_card_state_notif {
|
|
+ __le32 flags;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+#define HW_CARD_DISABLED 0x01
|
|
+#define SW_CARD_DISABLED 0x02
|
|
+#define RF_CARD_DISABLED 0x04
|
|
+#define RXON_CARD_DISABLED 0x10
|
|
+
|
|
+struct iwl_ct_kill_config {
|
|
+ __le32 reserved;
|
|
+ __le32 critical_temperature_M;
|
|
+ __le32 critical_temperature_R;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/******************************************************************************
|
|
+ * (8)
|
|
+ * Scan Commands, Responses, Notifications:
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+struct iwl_scan_channel {
|
|
+ /* type is defined as:
|
|
+ * 0:0 active (0 - passive)
|
|
+ * 1:4 SSID direct
|
|
+ * If 1 is set then corresponding SSID IE is transmitted in probe
|
|
+ * 5:7 reserved
|
|
+ */
|
|
+ u8 type;
|
|
+ u8 channel;
|
|
+ struct iwl_tx_power tpc;
|
|
+ __le16 active_dwell;
|
|
+ __le16 passive_dwell;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct iwl_ssid_ie {
|
|
+ u8 id;
|
|
+ u8 len;
|
|
+ u8 ssid[32];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+#define PROBE_OPTION_MAX 0x4
|
|
+#define TX_CMD_LIFE_TIME_INFINITE __constant_cpu_to_le32(0xFFFFFFFF)
|
|
+#define IWL_GOOD_CRC_TH __constant_cpu_to_le16(1)
|
|
+#define IWL_MAX_SCAN_SIZE 1024
|
|
+
|
|
+/*
|
|
+ * REPLY_SCAN_CMD = 0x80 (command)
|
|
+ */
|
|
+struct iwl_scan_cmd {
|
|
+ __le16 len;
|
|
+ u8 reserved0;
|
|
+ u8 channel_count;
|
|
+ __le16 quiet_time; /* dwell only this long on quiet chnl
|
|
+ * (active scan) */
|
|
+ __le16 quiet_plcp_th; /* quiet chnl is < this # pkts (typ. 1) */
|
|
+ __le16 good_CRC_th; /* passive -> active promotion threshold */
|
|
+#if IWL == 3945
|
|
+ __le16 reserved1;
|
|
+#elif IWL == 4965
|
|
+ __le16 rx_chain;
|
|
+#endif
|
|
+ __le32 max_out_time; /* max usec to be out of associated (service)
|
|
+ * chnl */
|
|
+ __le32 suspend_time; /* pause scan this long when returning to svc
|
|
+ * chnl.
|
|
+ * 3945 -- 31:24 # beacons, 19:0 additional usec,
|
|
+ * 4965 -- 31:22 # beacons, 21:0 additional usec.
|
|
+ */
|
|
+ __le32 flags;
|
|
+ __le32 filter_flags;
|
|
+
|
|
+ struct iwl_tx_cmd tx_cmd;
|
|
+ struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX];
|
|
+
|
|
+ u8 data[0];
|
|
+ /*
|
|
+ * The channels start after the probe request payload and are of type:
|
|
+ *
|
|
+ * struct iwl_scan_channel channels[0];
|
|
+ *
|
|
+ * NOTE: Only one band of channels can be scanned per pass. You
|
|
+ * can not mix 2.4GHz channels and 5.2GHz channels and must
|
|
+ * request a scan multiple times (not concurrently)
|
|
+ *
|
|
+ */
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/* Can abort will notify by complete notification with abort status. */
|
|
+#define CAN_ABORT_STATUS __constant_cpu_to_le32(0x1)
|
|
+/* complete notification statuses */
|
|
+#define ABORT_STATUS 0x2
|
|
+
|
|
+/*
|
|
+ * REPLY_SCAN_CMD = 0x80 (response)
|
|
+ */
|
|
+struct iwl_scanreq_notification {
|
|
+ __le32 status; /* 1: okay, 2: cannot fulfill request */
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * SCAN_START_NOTIFICATION = 0x82 (notification only, not a command)
|
|
+ */
|
|
+struct iwl_scanstart_notification {
|
|
+ __le32 tsf_low;
|
|
+ __le32 tsf_high;
|
|
+ __le32 beacon_timer;
|
|
+ u8 channel;
|
|
+ u8 band;
|
|
+ u8 reserved[2];
|
|
+ __le32 status;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+#define SCAN_OWNER_STATUS 0x1;
|
|
+#define MEASURE_OWNER_STATUS 0x2;
|
|
+
|
|
+#define NUMBER_OF_STATISTICS 1 /* first __le32 is good CRC */
|
|
+/*
|
|
+ * SCAN_RESULTS_NOTIFICATION = 0x83 (notification only, not a command)
|
|
+ */
|
|
+struct iwl_scanresults_notification {
|
|
+ u8 channel;
|
|
+ u8 band;
|
|
+ u8 reserved[2];
|
|
+ __le32 tsf_low;
|
|
+ __le32 tsf_high;
|
|
+ __le32 statistics[NUMBER_OF_STATISTICS];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * SCAN_COMPLETE_NOTIFICATION = 0x84 (notification only, not a command)
|
|
+ */
|
|
+struct iwl_scancomplete_notification {
|
|
+ u8 scanned_channels;
|
|
+ u8 status;
|
|
+ u8 reserved;
|
|
+ u8 last_channel;
|
|
+ __le32 tsf_low;
|
|
+ __le32 tsf_high;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+
|
|
+/******************************************************************************
|
|
+ * (9)
|
|
+ * IBSS/AP Commands and Notifications:
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+/*
|
|
+ * BEACON_NOTIFICATION = 0x90 (notification only, not a command)
|
|
+ */
|
|
+struct iwl_beacon_notif {
|
|
+ struct iwl_tx_resp beacon_notify_hdr;
|
|
+ __le32 low_tsf;
|
|
+ __le32 high_tsf;
|
|
+ __le32 ibss_mgr_status;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * REPLY_TX_BEACON = 0x91 (command, has simple generic response)
|
|
+ */
|
|
+struct iwl_tx_beacon_cmd {
|
|
+ struct iwl_tx_cmd tx;
|
|
+ __le16 tim_idx;
|
|
+ u8 tim_size;
|
|
+ u8 reserved1;
|
|
+ struct ieee80211_hdr frame[0]; /* beacon frame */
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/******************************************************************************
|
|
+ * (10)
|
|
+ * Statistics Commands and Notifications:
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+#define IWL_TEMP_CONVERT 260
|
|
+
|
|
+#define SUP_RATE_11A_MAX_NUM_CHANNELS 8
|
|
+#define SUP_RATE_11B_MAX_NUM_CHANNELS 4
|
|
+#define SUP_RATE_11G_MAX_NUM_CHANNELS 12
|
|
+
|
|
+/* Used for passing to driver number of successes and failures per rate */
|
|
+struct rate_histogram {
|
|
+ union {
|
|
+ __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS];
|
|
+ __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS];
|
|
+ __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS];
|
|
+ } success;
|
|
+ union {
|
|
+ __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS];
|
|
+ __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS];
|
|
+ __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS];
|
|
+ } failed;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/* statistics command response */
|
|
+
|
|
+struct statistics_rx_phy {
|
|
+ __le32 ina_cnt;
|
|
+ __le32 fina_cnt;
|
|
+ __le32 plcp_err;
|
|
+ __le32 crc32_err;
|
|
+ __le32 overrun_err;
|
|
+ __le32 early_overrun_err;
|
|
+ __le32 crc32_good;
|
|
+ __le32 false_alarm_cnt;
|
|
+ __le32 fina_sync_err_cnt;
|
|
+ __le32 sfd_timeout;
|
|
+ __le32 fina_timeout;
|
|
+ __le32 unresponded_rts;
|
|
+ __le32 rxe_frame_limit_overrun;
|
|
+ __le32 sent_ack_cnt;
|
|
+ __le32 sent_cts_cnt;
|
|
+#if IWL == 4965
|
|
+ __le32 sent_ba_rsp_cnt;
|
|
+ __le32 dsp_self_kill;
|
|
+ __le32 mh_format_err;
|
|
+ __le32 re_acq_main_rssi_sum;
|
|
+ __le32 reserved3;
|
|
+#endif
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+#if IWL == 4965
|
|
+struct statistics_rx_ht_phy {
|
|
+ __le32 plcp_err;
|
|
+ __le32 overrun_err;
|
|
+ __le32 early_overrun_err;
|
|
+ __le32 crc32_good;
|
|
+ __le32 crc32_err;
|
|
+ __le32 mh_format_err;
|
|
+ __le32 agg_crc32_good;
|
|
+ __le32 agg_mpdu_cnt;
|
|
+ __le32 agg_cnt;
|
|
+ __le32 reserved2;
|
|
+} __attribute__ ((packed));
|
|
+#endif
|
|
+
|
|
+struct statistics_rx_non_phy {
|
|
+ __le32 bogus_cts; /* CTS received when not expecting CTS */
|
|
+ __le32 bogus_ack; /* ACK received when not expecting ACK */
|
|
+ __le32 non_bssid_frames; /* number of frames with BSSID that
|
|
+ * doesn't belong to the STA BSSID */
|
|
+ __le32 filtered_frames; /* count frames that were dumped in the
|
|
+ * filtering process */
|
|
+ __le32 non_channel_beacons; /* beacons with our bss id but not on
|
|
+ * our serving channel */
|
|
+#if IWL == 4965
|
|
+ __le32 channel_beacons; /* beacons with our bss id and in our
|
|
+ * serving channel */
|
|
+ __le32 num_missed_bcon; /* number of missed beacons */
|
|
+ __le32 adc_rx_saturation_time; /* count in 0.8us units the time the
|
|
+ * ADC was in saturation */
|
|
+ __le32 ina_detection_search_time;/* total time (in 0.8us) searched
|
|
+ * for INA */
|
|
+ __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */
|
|
+ __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */
|
|
+ __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */
|
|
+ __le32 interference_data_flag; /* flag for interference data
|
|
+ * availability. 1 when data is
|
|
+ * available. */
|
|
+ __le32 channel_load; /* counts RX Enable time */
|
|
+ __le32 dsp_false_alarms; /* DSP false alarm (both OFDM
|
|
+ * and CCK) counter */
|
|
+ __le32 beacon_rssi_a;
|
|
+ __le32 beacon_rssi_b;
|
|
+ __le32 beacon_rssi_c;
|
|
+ __le32 beacon_energy_a;
|
|
+ __le32 beacon_energy_b;
|
|
+ __le32 beacon_energy_c;
|
|
+#endif
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct statistics_rx {
|
|
+ struct statistics_rx_phy ofdm;
|
|
+ struct statistics_rx_phy cck;
|
|
+ struct statistics_rx_non_phy general;
|
|
+#if IWL == 4965
|
|
+ struct statistics_rx_ht_phy ofdm_ht;
|
|
+#endif
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+#if IWL == 4965
|
|
+struct statistics_tx_non_phy_agg {
|
|
+ __le32 ba_timeout;
|
|
+ __le32 ba_reschedule_frames;
|
|
+ __le32 scd_query_agg_frame_cnt;
|
|
+ __le32 scd_query_no_agg;
|
|
+ __le32 scd_query_agg;
|
|
+ __le32 scd_query_mismatch;
|
|
+ __le32 frame_not_ready;
|
|
+ __le32 underrun;
|
|
+ __le32 bt_prio_kill;
|
|
+ __le32 rx_ba_rsp_cnt;
|
|
+ __le32 reserved2;
|
|
+ __le32 reserved3;
|
|
+} __attribute__ ((packed));
|
|
+#endif
|
|
+
|
|
+struct statistics_tx {
|
|
+ __le32 preamble_cnt;
|
|
+ __le32 rx_detected_cnt;
|
|
+ __le32 bt_prio_defer_cnt;
|
|
+ __le32 bt_prio_kill_cnt;
|
|
+ __le32 few_bytes_cnt;
|
|
+ __le32 cts_timeout;
|
|
+ __le32 ack_timeout;
|
|
+ __le32 expected_ack_cnt;
|
|
+ __le32 actual_ack_cnt;
|
|
+#if IWL == 4965
|
|
+ __le32 dump_msdu_cnt;
|
|
+ __le32 burst_abort_next_frame_mismatch_cnt;
|
|
+ __le32 burst_abort_missing_next_frame_cnt;
|
|
+ __le32 cts_timeout_collision;
|
|
+ __le32 ack_or_ba_timeout_collision;
|
|
+ struct statistics_tx_non_phy_agg agg;
|
|
+#endif
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct statistics_dbg {
|
|
+ __le32 burst_check;
|
|
+ __le32 burst_count;
|
|
+ __le32 reserved[4];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct statistics_div {
|
|
+ __le32 tx_on_a;
|
|
+ __le32 tx_on_b;
|
|
+ __le32 exec_time;
|
|
+ __le32 probe_time;
|
|
+#if IWL == 4965
|
|
+ __le32 reserved1;
|
|
+ __le32 reserved2;
|
|
+#endif
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct statistics_general {
|
|
+ __le32 temperature;
|
|
+#if IWL == 4965
|
|
+ __le32 temperature_m;
|
|
+#endif
|
|
+ struct statistics_dbg dbg;
|
|
+ __le32 sleep_time;
|
|
+ __le32 slots_out;
|
|
+ __le32 slots_idle;
|
|
+ __le32 ttl_timestamp;
|
|
+ struct statistics_div div;
|
|
+#if IWL == 4965
|
|
+ __le32 rx_enable_counter;
|
|
+ __le32 reserved1;
|
|
+ __le32 reserved2;
|
|
+ __le32 reserved3;
|
|
+#endif
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * REPLY_STATISTICS_CMD = 0x9c,
|
|
+ * 3945 and 4965 identical.
|
|
+ *
|
|
+ * This command triggers an immediate response containing uCode statistics.
|
|
+ * The response is in the same format as STATISTICS_NOTIFICATION 0x9d, below.
|
|
+ *
|
|
+ * If the CLEAR_STATS configuration flag is set, uCode will clear its
|
|
+ * internal copy of the statistics (counters) after issuing the response.
|
|
+ * This flag does not affect STATISTICS_NOTIFICATIONs after beacons (see below).
|
|
+ *
|
|
+ * If the DISABLE_NOTIF configuration flag is set, uCode will not issue
|
|
+ * STATISTICS_NOTIFICATIONs after received beacons (see below). This flag
|
|
+ * does not affect the response to the REPLY_STATISTICS_CMD 0x9c itself.
|
|
+ */
|
|
+#define IWL_STATS_CONF_CLEAR_STATS __constant_cpu_to_le32(0x1) /* see above */
|
|
+#define IWL_STATS_CONF_DISABLE_NOTIF __constant_cpu_to_le32(0x2)/* see above */
|
|
+struct iwl_statistics_cmd {
|
|
+ __le32 configuration_flags; /* IWL_STATS_CONF_* */
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command)
|
|
+ *
|
|
+ * By default, uCode issues this notification after receiving a beacon
|
|
+ * while associated. To disable this behavior, set DISABLE_NOTIF flag in the
|
|
+ * REPLY_STATISTICS_CMD 0x9c, above.
|
|
+ *
|
|
+ * Statistics counters continue to increment beacon after beacon, but are
|
|
+ * cleared when changing channels or when driver issues REPLY_STATISTICS_CMD
|
|
+ * 0x9c with CLEAR_STATS bit set (see above).
|
|
+ *
|
|
+ * uCode also issues this notification during scans. uCode clears statistics
|
|
+ * appropriately so that each notification contains statistics for only the
|
|
+ * one channel that has just been scanned.
|
|
+ */
|
|
+struct iwl_notif_statistics {
|
|
+ __le32 flag;
|
|
+ struct statistics_rx rx;
|
|
+ struct statistics_tx tx;
|
|
+ struct statistics_general general;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+
|
|
+/*
|
|
+ * MISSED_BEACONS_NOTIFICATION = 0xa2 (notification only, not a command)
|
|
+ */
|
|
+/* if ucode missed CONSECUTIVE_MISSED_BCONS_TH beacons in a row,
|
|
+ * then this notification will be sent. */
|
|
+#define CONSECUTIVE_MISSED_BCONS_TH 20
|
|
+
|
|
+struct iwl_missed_beacon_notif {
|
|
+ __le32 consequtive_missed_beacons;
|
|
+ __le32 total_missed_becons;
|
|
+ __le32 num_expected_beacons;
|
|
+ __le32 num_recvd_beacons;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/******************************************************************************
|
|
+ * (11)
|
|
+ * Rx Calibration Commands:
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+#define PHY_CALIBRATE_DIFF_GAIN_CMD (7)
|
|
+#define HD_TABLE_SIZE (11)
|
|
+
|
|
+struct iwl_sensitivity_cmd {
|
|
+ __le16 control;
|
|
+ __le16 table[HD_TABLE_SIZE];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct iwl_calibration_cmd {
|
|
+ u8 opCode;
|
|
+ u8 flags;
|
|
+ __le16 reserved;
|
|
+ s8 diff_gain_a;
|
|
+ s8 diff_gain_b;
|
|
+ s8 diff_gain_c;
|
|
+ u8 reserved1;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/******************************************************************************
|
|
+ * (12)
|
|
+ * Miscellaneous Commands:
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+/*
|
|
+ * LEDs Command & Response
|
|
+ * REPLY_LEDS_CMD = 0x48 (command, has simple generic response)
|
|
+ *
|
|
+ * For each of 3 possible LEDs (Activity/Link/Tech, selected by "id" field),
|
|
+ * this command turns it on or off, or sets up a periodic blinking cycle.
|
|
+ */
|
|
+struct iwl_led_cmd {
|
|
+ __le32 interval; /* "interval" in uSec */
|
|
+ u8 id; /* 1: Activity, 2: Link, 3: Tech */
|
|
+ u8 off; /* # intervals off while blinking;
|
|
+ * "0", with >0 "on" value, turns LED on */
|
|
+ u8 on; /* # intervals on while blinking;
|
|
+ * "0", regardless of "off", turns LED off */
|
|
+ u8 reserved;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/******************************************************************************
|
|
+ * (13)
|
|
+ * Union of all expected notifications/responses:
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+struct iwl_rx_packet {
|
|
+ __le32 len;
|
|
+ struct iwl_cmd_header hdr;
|
|
+ union {
|
|
+ struct iwl_alive_resp alive_frame;
|
|
+ struct iwl_rx_frame rx_frame;
|
|
+ struct iwl_tx_resp tx_resp;
|
|
+ struct iwl_spectrum_notification spectrum_notif;
|
|
+ struct iwl_csa_notification csa_notif;
|
|
+ struct iwl_error_resp err_resp;
|
|
+ struct iwl_card_state_notif card_state_notif;
|
|
+ struct iwl_beacon_notif beacon_status;
|
|
+ struct iwl_add_sta_resp add_sta;
|
|
+ struct iwl_sleep_notification sleep_notif;
|
|
+ struct iwl_spectrum_resp spectrum;
|
|
+ struct iwl_notif_statistics stats;
|
|
+#if IWL == 4965
|
|
+ struct iwl_compressed_ba_resp compressed_ba;
|
|
+ struct iwl_missed_beacon_notif missed_beacon;
|
|
+#endif
|
|
+ __le32 status;
|
|
+ u8 raw[0];
|
|
+ } u;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+#define IWL_RX_FRAME_SIZE (4 + sizeof(struct iwl_rx_frame))
|
|
+
|
|
+#endif /* __iwl_commands_h__ */
|
|
diff --git a/drivers/net/wireless/iwl-debug.h b/drivers/net/wireless/iwl-debug.h
|
|
new file mode 100644
|
|
index 0000000..0ebc4f7
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/iwl-debug.h
|
|
@@ -0,0 +1,149 @@
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * Copyright(c) 2003 - 2007 Intel Corporation. All rights reserved.
|
|
+ *
|
|
+ * Portions of this file are derived from the ipw3945 project.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify it
|
|
+ * under the terms of version 2 of the GNU General Public License as
|
|
+ * published by the Free Software Foundation.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful, but WITHOUT
|
|
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
+ * more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License along with
|
|
+ * this program; if not, write to the Free Software Foundation, Inc.,
|
|
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
|
|
+ *
|
|
+ * The full GNU General Public License is included in this distribution in the
|
|
+ * file called LICENSE.
|
|
+ *
|
|
+ * Contact Information:
|
|
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
|
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+#ifndef __iwl_debug_h__
|
|
+#define __iwl_debug_h__
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+extern u32 iwl_debug_level;
|
|
+#define IWL_DEBUG(level, fmt, args...) \
|
|
+do { if (iwl_debug_level & (level)) \
|
|
+ printk(KERN_ERR DRV_NAME": %c %s " fmt, \
|
|
+ in_interrupt() ? 'I' : 'U', __FUNCTION__ , ## args); } while (0)
|
|
+
|
|
+#define IWL_DEBUG_LIMIT(level, fmt, args...) \
|
|
+do { if ((iwl_debug_level & (level)) && net_ratelimit()) \
|
|
+ printk(KERN_ERR DRV_NAME": %c %s " fmt, \
|
|
+ in_interrupt() ? 'I' : 'U', __FUNCTION__ , ## args); } while (0)
|
|
+#else
|
|
+static inline void IWL_DEBUG(int level, const char *fmt, ...)
|
|
+{
|
|
+}
|
|
+static inline void IWL_DEBUG_LIMIT(int level, const char *fmt, ...)
|
|
+{
|
|
+}
|
|
+#endif /* CONFIG_IWLWIFI_DEBUG */
|
|
+
|
|
+/*
|
|
+ * To use the debug system;
|
|
+ *
|
|
+ * If you are defining a new debug classification, simply add it to the #define
|
|
+ * list here in the form of:
|
|
+ *
|
|
+ * #define IWL_DL_xxxx VALUE
|
|
+ *
|
|
+ * shifting value to the left one bit from the previous entry. xxxx should be
|
|
+ * the name of the classification (for example, WEP)
|
|
+ *
|
|
+ * You then need to either add a IWL_xxxx_DEBUG() macro definition for your
|
|
+ * classification, or use IWL_DEBUG(IWL_DL_xxxx, ...) whenever you want
|
|
+ * to send output to that classification.
|
|
+ *
|
|
+ * To add your debug level to the list of levels seen when you perform
|
|
+ *
|
|
+ * % cat /proc/net/ipw/debug_level
|
|
+ *
|
|
+ * you simply need to add your entry to the iwl_debug_levels array.
|
|
+ *
|
|
+ * If you do not see debug_level in /proc/net/ipw then you do not have
|
|
+ * CONFIG_IWLWIFI_DEBUG defined in your kernel configuration
|
|
+ *
|
|
+ */
|
|
+
|
|
+#define IWL_DL_INFO (1<<0)
|
|
+#define IWL_DL_MAC80211 (1<<1)
|
|
+#define IWL_DL_HOST_COMMAND (1<<2)
|
|
+#define IWL_DL_STATE (1<<3)
|
|
+
|
|
+#define IWL_DL_RADIO (1<<7)
|
|
+#define IWL_DL_POWER (1<<8)
|
|
+#define IWL_DL_TEMP (1<<9)
|
|
+
|
|
+#define IWL_DL_NOTIF (1<<10)
|
|
+#define IWL_DL_SCAN (1<<11)
|
|
+#define IWL_DL_ASSOC (1<<12)
|
|
+#define IWL_DL_DROP (1<<13)
|
|
+
|
|
+#define IWL_DL_TXPOWER (1<<14)
|
|
+
|
|
+#define IWL_DL_AP (1<<15)
|
|
+
|
|
+#define IWL_DL_FW (1<<16)
|
|
+#define IWL_DL_RF_KILL (1<<17)
|
|
+#define IWL_DL_FW_ERRORS (1<<18)
|
|
+
|
|
+#define IWL_DL_LED (1<<19)
|
|
+
|
|
+#define IWL_DL_RATE (1<<20)
|
|
+
|
|
+#define IWL_DL_CALIB (1<<21)
|
|
+#define IWL_DL_WEP (1<<22)
|
|
+#define IWL_DL_TX (1<<23)
|
|
+#define IWL_DL_RX (1<<24)
|
|
+#define IWL_DL_ISR (1<<25)
|
|
+#define IWL_DL_HT (1<<26)
|
|
+#define IWL_DL_IO (1<<27)
|
|
+#define IWL_DL_11H (1<<28)
|
|
+
|
|
+#define IWL_DL_STATS (1<<29)
|
|
+#define IWL_DL_TX_REPLY (1<<30)
|
|
+#define IWL_DL_QOS (1<<31)
|
|
+
|
|
+#define IWL_ERROR(f, a...) printk(KERN_ERR DRV_NAME ": " f, ## a)
|
|
+#define IWL_WARNING(f, a...) printk(KERN_WARNING DRV_NAME ": " f, ## a)
|
|
+#define IWL_DEBUG_INFO(f, a...) IWL_DEBUG(IWL_DL_INFO, f, ## a)
|
|
+
|
|
+#define IWL_DEBUG_MAC80211(f, a...) IWL_DEBUG(IWL_DL_MAC80211, f, ## a)
|
|
+#define IWL_DEBUG_TEMP(f, a...) IWL_DEBUG(IWL_DL_TEMP, f, ## a)
|
|
+#define IWL_DEBUG_SCAN(f, a...) IWL_DEBUG(IWL_DL_SCAN, f, ## a)
|
|
+#define IWL_DEBUG_RX(f, a...) IWL_DEBUG(IWL_DL_RX, f, ## a)
|
|
+#define IWL_DEBUG_TX(f, a...) IWL_DEBUG(IWL_DL_TX, f, ## a)
|
|
+#define IWL_DEBUG_ISR(f, a...) IWL_DEBUG(IWL_DL_ISR, f, ## a)
|
|
+#define IWL_DEBUG_LED(f, a...) IWL_DEBUG(IWL_DL_LED, f, ## a)
|
|
+#define IWL_DEBUG_WEP(f, a...) IWL_DEBUG(IWL_DL_WEP, f, ## a)
|
|
+#define IWL_DEBUG_HC(f, a...) IWL_DEBUG(IWL_DL_HOST_COMMAND, f, ## a)
|
|
+#define IWL_DEBUG_CALIB(f, a...) IWL_DEBUG(IWL_DL_CALIB, f, ## a)
|
|
+#define IWL_DEBUG_FW(f, a...) IWL_DEBUG(IWL_DL_FW, f, ## a)
|
|
+#define IWL_DEBUG_RF_KILL(f, a...) IWL_DEBUG(IWL_DL_RF_KILL, f, ## a)
|
|
+#define IWL_DEBUG_DROP(f, a...) IWL_DEBUG(IWL_DL_DROP, f, ## a)
|
|
+#define IWL_DEBUG_DROP_LIMIT(f, a...) IWL_DEBUG_LIMIT(IWL_DL_DROP, f, ## a)
|
|
+#define IWL_DEBUG_AP(f, a...) IWL_DEBUG(IWL_DL_AP, f, ## a)
|
|
+#define IWL_DEBUG_TXPOWER(f, a...) IWL_DEBUG(IWL_DL_TXPOWER, f, ## a)
|
|
+#define IWL_DEBUG_IO(f, a...) IWL_DEBUG(IWL_DL_IO, f, ## a)
|
|
+#define IWL_DEBUG_RATE(f, a...) IWL_DEBUG(IWL_DL_RATE, f, ## a)
|
|
+#define IWL_DEBUG_NOTIF(f, a...) IWL_DEBUG(IWL_DL_NOTIF, f, ## a)
|
|
+#define IWL_DEBUG_ASSOC(f, a...) IWL_DEBUG(IWL_DL_ASSOC | IWL_DL_INFO, f, ## a)
|
|
+#define IWL_DEBUG_HT(f, a...) IWL_DEBUG(IWL_DL_HT, f, ## a)
|
|
+#define IWL_DEBUG_STATS(f, a...) IWL_DEBUG(IWL_DL_STATS, f, ## a)
|
|
+#define IWL_DEBUG_TX_REPLY(f, a...) IWL_DEBUG(IWL_DL_TX_REPLY, f, ## a)
|
|
+#define IWL_DEBUG_QOS(f, a...) IWL_DEBUG(IWL_DL_QOS, f, ## a)
|
|
+#define IWL_DEBUG_RADIO(f, a...) IWL_DEBUG(IWL_DL_RADIO, f, ## a)
|
|
+#define IWL_DEBUG_POWER(f, a...) IWL_DEBUG(IWL_DL_POWER, f, ## a)
|
|
+#define IWL_DEBUG_11H(f, a...) IWL_DEBUG(IWL_DL_11H, f, ## a)
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/net/wireless/iwl-eeprom.h b/drivers/net/wireless/iwl-eeprom.h
|
|
new file mode 100644
|
|
index 0000000..e473c97
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/iwl-eeprom.h
|
|
@@ -0,0 +1,336 @@
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * 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) 2005 - 2007 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 Geeral 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:
|
|
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
|
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
+ *
|
|
+ * BSD LICENSE
|
|
+ *
|
|
+ * Copyright(c) 2005 - 2007 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_eeprom_h__
|
|
+#define __iwl_eeprom_h__
|
|
+
|
|
+/*
|
|
+ * This file defines EEPROM related constants, enums, and inline functions.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#define IWL_EEPROM_ACCESS_TIMEOUT 5000 /* uSec */
|
|
+#define IWL_EEPROM_ACCESS_DELAY 10 /* uSec */
|
|
+/* EEPROM field values */
|
|
+#define ANTENNA_SWITCH_NORMAL 0
|
|
+#define ANTENNA_SWITCH_INVERSE 1
|
|
+
|
|
+enum {
|
|
+ EEPROM_CHANNEL_VALID = (1 << 0), /* usable for this SKU/geo */
|
|
+ EEPROM_CHANNEL_IBSS = (1 << 1), /* usable as an IBSS channel */
|
|
+ /* Bit 2 Reserved */
|
|
+ EEPROM_CHANNEL_ACTIVE = (1 << 3), /* active scanning allowed */
|
|
+ EEPROM_CHANNEL_RADAR = (1 << 4), /* radar detection required */
|
|
+ EEPROM_CHANNEL_WIDE = (1 << 5),
|
|
+ EEPROM_CHANNEL_NARROW = (1 << 6),
|
|
+ EEPROM_CHANNEL_DFS = (1 << 7), /* dynamic freq selection candidate */
|
|
+};
|
|
+
|
|
+/* EEPROM field lengths */
|
|
+#define EEPROM_BOARD_PBA_NUMBER_LENGTH 11
|
|
+
|
|
+/* EEPROM field lengths */
|
|
+#define EEPROM_BOARD_PBA_NUMBER_LENGTH 11
|
|
+#define EEPROM_REGULATORY_SKU_ID_LENGTH 4
|
|
+#define EEPROM_REGULATORY_BAND1_CHANNELS_LENGTH 14
|
|
+#define EEPROM_REGULATORY_BAND2_CHANNELS_LENGTH 13
|
|
+#define EEPROM_REGULATORY_BAND3_CHANNELS_LENGTH 12
|
|
+#define EEPROM_REGULATORY_BAND4_CHANNELS_LENGTH 11
|
|
+#define EEPROM_REGULATORY_BAND5_CHANNELS_LENGTH 6
|
|
+
|
|
+#if IWL == 3945
|
|
+#define EEPROM_REGULATORY_CHANNELS_LENGTH ( \
|
|
+ EEPROM_REGULATORY_BAND1_CHANNELS_LENGTH + \
|
|
+ EEPROM_REGULATORY_BAND2_CHANNELS_LENGTH + \
|
|
+ EEPROM_REGULATORY_BAND3_CHANNELS_LENGTH + \
|
|
+ EEPROM_REGULATORY_BAND4_CHANNELS_LENGTH + \
|
|
+ EEPROM_REGULATORY_BAND5_CHANNELS_LENGTH)
|
|
+#elif IWL == 4965
|
|
+#define EEPROM_REGULATORY_BAND_24_FAT_CHANNELS_LENGTH 7
|
|
+#define EEPROM_REGULATORY_BAND_52_FAT_CHANNELS_LENGTH 11
|
|
+#define EEPROM_REGULATORY_CHANNELS_LENGTH ( \
|
|
+ EEPROM_REGULATORY_BAND1_CHANNELS_LENGTH + \
|
|
+ EEPROM_REGULATORY_BAND2_CHANNELS_LENGTH + \
|
|
+ EEPROM_REGULATORY_BAND3_CHANNELS_LENGTH + \
|
|
+ EEPROM_REGULATORY_BAND4_CHANNELS_LENGTH + \
|
|
+ EEPROM_REGULATORY_BAND5_CHANNELS_LENGTH + \
|
|
+ EEPROM_REGULATORY_BAND_24_FAT_CHANNELS_LENGTH + \
|
|
+ EEPROM_REGULATORY_BAND_52_FAT_CHANNELS_LENGTH)
|
|
+#endif
|
|
+
|
|
+#define EEPROM_REGULATORY_NUMBER_OF_BANDS 5
|
|
+
|
|
+/* SKU Capabilities */
|
|
+#define EEPROM_SKU_CAP_SW_RF_KILL_ENABLE (1 << 0)
|
|
+#define EEPROM_SKU_CAP_HW_RF_KILL_ENABLE (1 << 1)
|
|
+#define EEPROM_SKU_CAP_OP_MODE_MRC (1 << 7)
|
|
+
|
|
+/* *regulatory* channel data from eeprom, one for each channel */
|
|
+struct iwl_eeprom_channel {
|
|
+ u8 flags; /* flags copied from EEPROM */
|
|
+ s8 max_power_avg; /* max power (dBm) on this chnl, limit 31 */
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * Mapping of a Tx power level, at factory calibration temperature,
|
|
+ * to a radio/DSP gain table index.
|
|
+ * One for each of 5 "sample" power levels in each band.
|
|
+ * v_det is measured at the factory, using the 3945's built-in power amplifier
|
|
+ * (PA) output voltage detector. This same detector is used during Tx of
|
|
+ * long packets in normal operation to provide feedback as to proper output
|
|
+ * level.
|
|
+ * Data copied from EEPROM.
|
|
+ */
|
|
+struct iwl_eeprom_txpower_sample {
|
|
+ u8 gain_index; /* index into power (gain) setup table ... */
|
|
+ s8 power; /* ... for this pwr level for this chnl group */
|
|
+ u16 v_det; /* PA output voltage */
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * Mappings of Tx power levels -> nominal radio/DSP gain table indexes.
|
|
+ * One for each channel group (a.k.a. "band") (1 for BG, 4 for A).
|
|
+ * Tx power setup code interpolates between the 5 "sample" power levels
|
|
+ * to determine the nominal setup for a requested power level.
|
|
+ * Data copied from EEPROM.
|
|
+ * DO NOT ALTER THIS STRUCTURE!!!
|
|
+ */
|
|
+struct iwl_eeprom_txpower_group {
|
|
+ struct iwl_eeprom_txpower_sample samples[5]; /* 5 power levels */
|
|
+ s32 a, b, c, d, e; /* coefficients for voltage->power
|
|
+ * formula (signed) */
|
|
+ s32 Fa, Fb, Fc, Fd, Fe; /* these modify coeffs based on
|
|
+ * frequency (signed) */
|
|
+ s8 saturation_power; /* highest power possible by h/w in this
|
|
+ * band */
|
|
+ u8 group_channel; /* "representative" channel # in this band */
|
|
+ s16 temperature; /* h/w temperature at factory calib this band
|
|
+ * (signed) */
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * Temperature-based Tx-power compensation data, not band-specific.
|
|
+ * These coefficients are use to modify a/b/c/d/e coeffs based on
|
|
+ * difference between current temperature and factory calib temperature.
|
|
+ * Data copied from EEPROM.
|
|
+ */
|
|
+struct iwl_eeprom_temperature_corr {
|
|
+ u32 Ta;
|
|
+ u32 Tb;
|
|
+ u32 Tc;
|
|
+ u32 Td;
|
|
+ u32 Te;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+#if IWL == 4965
|
|
+#define EEPROM_TX_POWER_TX_CHAINS (2)
|
|
+#define EEPROM_TX_POWER_BANDS (8)
|
|
+#define EEPROM_TX_POWER_MEASUREMENTS (3)
|
|
+#define EEPROM_TX_POWER_VERSION (2)
|
|
+#define EEPROM_TX_POWER_VERSION_NEW (5)
|
|
+
|
|
+struct iwl_eeprom_calib_measure {
|
|
+ u8 temperature;
|
|
+ u8 gain_idx;
|
|
+ u8 actual_pow;
|
|
+ s8 pa_det;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct iwl_eeprom_calib_ch_info {
|
|
+ u8 ch_num;
|
|
+ struct iwl_eeprom_calib_measure measurements[EEPROM_TX_POWER_TX_CHAINS]
|
|
+ [EEPROM_TX_POWER_MEASUREMENTS];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct iwl_eeprom_calib_subband_info {
|
|
+ u8 ch_from;
|
|
+ u8 ch_to;
|
|
+ struct iwl_eeprom_calib_ch_info ch1;
|
|
+ struct iwl_eeprom_calib_ch_info ch2;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct iwl_eeprom_calib_info {
|
|
+ u8 saturation_power24;
|
|
+ u8 saturation_power52;
|
|
+ s16 voltage; /* signed */
|
|
+ struct iwl_eeprom_calib_subband_info band_info[EEPROM_TX_POWER_BANDS];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+#endif
|
|
+
|
|
+struct iwl_eeprom {
|
|
+ u8 reserved0[16];
|
|
+#define EEPROM_DEVICE_ID (2*0x08) /* 2 bytes */
|
|
+ u16 device_id; /* abs.ofs: 16 */
|
|
+ u8 reserved1[2];
|
|
+#define EEPROM_PMC (2*0x0A) /* 2 bytes */
|
|
+ u16 pmc; /* abs.ofs: 20 */
|
|
+ u8 reserved2[20];
|
|
+#define EEPROM_MAC_ADDRESS (2*0x15) /* 6 bytes */
|
|
+ u8 mac_address[6]; /* abs.ofs: 42 */
|
|
+ u8 reserved3[58];
|
|
+#define EEPROM_BOARD_REVISION (2*0x35) /* 2 bytes */
|
|
+ u16 board_revision; /* abs.ofs: 106 */
|
|
+ u8 reserved4[11];
|
|
+#define EEPROM_BOARD_PBA_NUMBER (2*0x3B+1) /* 9 bytes */
|
|
+ u8 board_pba_number[9]; /* abs.ofs: 119 */
|
|
+ u8 reserved5[8];
|
|
+#define EEPROM_VERSION (2*0x44) /* 2 bytes */
|
|
+ u16 version; /* abs.ofs: 136 */
|
|
+#define EEPROM_SKU_CAP (2*0x45) /* 1 bytes */
|
|
+ u8 sku_cap; /* abs.ofs: 138 */
|
|
+#define EEPROM_LEDS_MODE (2*0x45+1) /* 1 bytes */
|
|
+ u8 leds_mode; /* abs.ofs: 139 */
|
|
+#define EEPROM_OEM_MODE (2*0x46) /* 2 bytes */
|
|
+ u16 oem_mode;
|
|
+#define EEPROM_WOWLAN_MODE (2*0x47) /* 2 bytes */
|
|
+ u16 wowlan_mode; /* abs.ofs: 142 */
|
|
+#define EEPROM_LEDS_TIME_INTERVAL (2*0x48) /* 2 bytes */
|
|
+ u16 leds_time_interval; /* abs.ofs: 144 */
|
|
+#define EEPROM_LEDS_OFF_TIME (2*0x49) /* 1 bytes */
|
|
+ u8 leds_off_time; /* abs.ofs: 146 */
|
|
+#define EEPROM_LEDS_ON_TIME (2*0x49+1) /* 1 bytes */
|
|
+ u8 leds_on_time; /* abs.ofs: 147 */
|
|
+#define EEPROM_ALMGOR_M_VERSION (2*0x4A) /* 1 bytes */
|
|
+ u8 almgor_m_version; /* abs.ofs: 148 */
|
|
+#define EEPROM_ANTENNA_SWITCH_TYPE (2*0x4A+1) /* 1 bytes */
|
|
+ u8 antenna_switch_type; /* abs.ofs: 149 */
|
|
+#if IWL == 3945
|
|
+ u8 reserved6[42];
|
|
+#else
|
|
+ u8 reserved6[8];
|
|
+#define EEPROM_4965_BOARD_REVISION (2*0x4F) /* 2 bytes */
|
|
+ u16 board_revision_4965; /* abs.ofs: 158 */
|
|
+ u8 reserved7[13];
|
|
+#define EEPROM_4965_BOARD_PBA (2*0x56+1) /* 9 bytes */
|
|
+ u8 board_pba_number_4965[9]; /* abs.ofs: 173 */
|
|
+ u8 reserved8[10];
|
|
+#endif
|
|
+#define EEPROM_REGULATORY_SKU_ID (2*0x60) /* 4 bytes */
|
|
+ u8 sku_id[4]; /* abs.ofs: 192 */
|
|
+#define EEPROM_REGULATORY_BAND_1 (2*0x62) /* 2 bytes */
|
|
+ u16 band_1_count; /* abs.ofs: 196 */
|
|
+#define EEPROM_REGULATORY_BAND_1_CHANNELS (2*0x63) /* 28 bytes */
|
|
+ struct iwl_eeprom_channel band_1_channels[14]; /* abs.ofs: 196 */
|
|
+#define EEPROM_REGULATORY_BAND_2 (2*0x71) /* 2 bytes */
|
|
+ u16 band_2_count; /* abs.ofs: 226 */
|
|
+#define EEPROM_REGULATORY_BAND_2_CHANNELS (2*0x72) /* 26 bytes */
|
|
+ struct iwl_eeprom_channel band_2_channels[13]; /* abs.ofs: 228 */
|
|
+#define EEPROM_REGULATORY_BAND_3 (2*0x7F) /* 2 bytes */
|
|
+ u16 band_3_count; /* abs.ofs: 254 */
|
|
+#define EEPROM_REGULATORY_BAND_3_CHANNELS (2*0x80) /* 24 bytes */
|
|
+ struct iwl_eeprom_channel band_3_channels[12]; /* abs.ofs: 256 */
|
|
+#define EEPROM_REGULATORY_BAND_4 (2*0x8C) /* 2 bytes */
|
|
+ u16 band_4_count; /* abs.ofs: 280 */
|
|
+#define EEPROM_REGULATORY_BAND_4_CHANNELS (2*0x8D) /* 22 bytes */
|
|
+ struct iwl_eeprom_channel band_4_channels[11]; /* abs.ofs: 282 */
|
|
+#define EEPROM_REGULATORY_BAND_5 (2*0x98) /* 2 bytes */
|
|
+ u16 band_5_count; /* abs.ofs: 304 */
|
|
+#define EEPROM_REGULATORY_BAND_5_CHANNELS (2*0x99) /* 12 bytes */
|
|
+ struct iwl_eeprom_channel band_5_channels[6]; /* abs.ofs: 306 */
|
|
+
|
|
+/* From here on out the EEPROM diverges between the 4965 and the 3945 */
|
|
+#if IWL == 3945
|
|
+
|
|
+ u8 reserved9[194];
|
|
+
|
|
+#define EEPROM_TXPOWER_CALIB_GROUP0 0x200
|
|
+#define EEPROM_TXPOWER_CALIB_GROUP1 0x240
|
|
+#define EEPROM_TXPOWER_CALIB_GROUP2 0x280
|
|
+#define EEPROM_TXPOWER_CALIB_GROUP3 0x2c0
|
|
+#define EEPROM_TXPOWER_CALIB_GROUP4 0x300
|
|
+#define IWL_NUM_TX_CALIB_GROUPS 5
|
|
+ struct iwl_eeprom_txpower_group groups[IWL_NUM_TX_CALIB_GROUPS];
|
|
+/* abs.ofs: 512 */
|
|
+#define EEPROM_CALIB_TEMPERATURE_CORRECT 0x340
|
|
+ struct iwl_eeprom_temperature_corr corrections; /* abs.ofs: 832 */
|
|
+ u8 reserved16[172]; /* fill out to full 1024 byte block */
|
|
+
|
|
+/* 4965AGN adds fat channel support */
|
|
+#elif IWL == 4965
|
|
+
|
|
+ u8 reserved10[2];
|
|
+#define EEPROM_REGULATORY_BAND_24_FAT_CHANNELS (2*0xA0) /* 14 bytes */
|
|
+ struct iwl_eeprom_channel band_24_channels[7]; /* abs.ofs: 320 */
|
|
+ u8 reserved11[2];
|
|
+#define EEPROM_REGULATORY_BAND_52_FAT_CHANNELS (2*0xA8) /* 22 bytes */
|
|
+ struct iwl_eeprom_channel band_52_channels[11]; /* abs.ofs: 336 */
|
|
+ u8 reserved12[6];
|
|
+#define EEPROM_CALIB_VERSION_OFFSET (2*0xB6) /* 2 bytes */
|
|
+ u16 calib_version; /* abs.ofs: 364 */
|
|
+ u8 reserved13[2];
|
|
+#define EEPROM_SATURATION_POWER_OFFSET (2*0xB8) /* 2 bytes */
|
|
+ u16 satruation_power; /* abs.ofs: 368 */
|
|
+ u8 reserved14[94];
|
|
+#define EEPROM_IWL_CALIB_TXPOWER_OFFSET (2*0xE8) /* 48 bytes */
|
|
+ struct iwl_eeprom_calib_info calib_info; /* abs.ofs: 464 */
|
|
+
|
|
+ u8 reserved16[140]; /* fill out to full 1024 byte block */
|
|
+
|
|
+#endif
|
|
+
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+#define IWL_EEPROM_IMAGE_SIZE 1024
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/net/wireless/iwl-helpers.h b/drivers/net/wireless/iwl-helpers.h
|
|
new file mode 100644
|
|
index 0000000..e2a8d95
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/iwl-helpers.h
|
|
@@ -0,0 +1,255 @@
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * Copyright(c) 2003 - 2007 Intel Corporation. All rights reserved.
|
|
+ *
|
|
+ * Portions of this file are derived from the ipw3945 project, as well
|
|
+ * as portions of the ieee80211 subsystem header files.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify it
|
|
+ * under the terms of version 2 of the GNU General Public License as
|
|
+ * published by the Free Software Foundation.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful, but WITHOUT
|
|
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
+ * more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License along with
|
|
+ * this program; if not, write to the Free Software Foundation, Inc.,
|
|
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
|
|
+ *
|
|
+ * The full GNU General Public License is included in this distribution in the
|
|
+ * file called LICENSE.
|
|
+ *
|
|
+ * Contact Information:
|
|
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
|
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+#ifndef __iwl_helpers_h__
|
|
+#define __iwl_helpers_h__
|
|
+
|
|
+#include <linux/ctype.h>
|
|
+
|
|
+/*
|
|
+ * The structures defined by the hardware/uCode interface
|
|
+ * have bit-wise operations. For each bit-field there is
|
|
+ * a data symbol in the structure, the start bit position
|
|
+ * and the length of the bit-field.
|
|
+ *
|
|
+ * iwl_get_bits and iwl_set_bits will return or set the
|
|
+ * appropriate bits on a 32-bit value.
|
|
+ *
|
|
+ * IWL_GET_BITS and IWL_SET_BITS use symbol expansion to
|
|
+ * expand out to the appropriate call to iwl_get_bits
|
|
+ * and iwl_set_bits without having to reference all of the
|
|
+ * numerical constants and defines provided in the hardware
|
|
+ * definition
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * iwl_get_bits - Extract a hardware bit-field value
|
|
+ * @src: source hardware value (__le32)
|
|
+ * @pos: bit-position (0-based) of first bit of value
|
|
+ * @len: length of bit-field
|
|
+ *
|
|
+ * iwl_get_bits will return the bit-field in cpu endian ordering.
|
|
+ *
|
|
+ * NOTE: If used from IWL_GET_BITS then pos and len are compile-constants and
|
|
+ * will collapse to minimal code by the compiler.
|
|
+ */
|
|
+static inline u32 iwl_get_bits(__le32 src, u8 pos, u8 len)
|
|
+{
|
|
+ u32 tmp = le32_to_cpu(src);
|
|
+
|
|
+ tmp >>= pos;
|
|
+ tmp &= (1UL << len) - 1;
|
|
+ return tmp;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_set_bits - Set a hardware bit-field value
|
|
+ * @dst: Address of __le32 hardware value
|
|
+ * @pos: bit-position (0-based) of first bit of value
|
|
+ * @len: length of bit-field
|
|
+ * @val: cpu endian value to encode into the bit-field
|
|
+ *
|
|
+ * iwl_set_bits will encode val into dst, masked to be len bits long at bit
|
|
+ * position pos.
|
|
+ *
|
|
+ * NOTE: If used IWL_SET_BITS pos and len will be compile-constants and
|
|
+ * will collapse to minimal code by the compiler.
|
|
+ */
|
|
+static inline void iwl_set_bits(__le32 *dst, u8 pos, u8 len, int val)
|
|
+{
|
|
+ u32 tmp = le32_to_cpu(*dst);
|
|
+
|
|
+ tmp &= ~(((1UL << len) - 1) << pos);
|
|
+ tmp |= (val & ((1UL << len) - 1)) << pos;
|
|
+ *dst = cpu_to_le32(tmp);
|
|
+}
|
|
+
|
|
+static inline void iwl_set_bits16(__le16 *dst, u8 pos, u8 len, int val)
|
|
+{
|
|
+ u16 tmp = le16_to_cpu(*dst);
|
|
+
|
|
+ tmp &= ~((1UL << (pos + len)) - (1UL << pos));
|
|
+ tmp |= (val & ((1UL << len) - 1)) << pos;
|
|
+ *dst = cpu_to_le16(tmp);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * The bit-field definitions in iwl-xxxx-hw.h are in the form of:
|
|
+ *
|
|
+ * struct example {
|
|
+ * __le32 val1;
|
|
+ * #define IWL_name_POS 8
|
|
+ * #define IWL_name_LEN 4
|
|
+ * #define IWL_name_SYM val1
|
|
+ * };
|
|
+ *
|
|
+ * The IWL_SET_BITS and IWL_GET_BITS macros are provided to allow the driver
|
|
+ * to call:
|
|
+ *
|
|
+ * struct example bar;
|
|
+ * u32 val = IWL_GET_BITS(bar, name);
|
|
+ * val = val * 2;
|
|
+ * IWL_SET_BITS(bar, name, val);
|
|
+ *
|
|
+ * All cpu / host ordering, masking, and shifts are performed by the macros
|
|
+ * and iwl_{get,set}_bits.
|
|
+ *
|
|
+ */
|
|
+#define IWL_SET_BITS(s, sym, v) \
|
|
+ iwl_set_bits(&(s).IWL_ ## sym ## _SYM, IWL_ ## sym ## _POS, \
|
|
+ IWL_ ## sym ## _LEN, (v))
|
|
+
|
|
+#define IWL_SET_BITS16(s, sym, v) \
|
|
+ iwl_set_bits16(&(s).IWL_ ## sym ## _SYM, IWL_ ## sym ## _POS, \
|
|
+ IWL_ ## sym ## _LEN, (v))
|
|
+
|
|
+#define IWL_GET_BITS(s, sym) \
|
|
+ iwl_get_bits((s).IWL_ ## sym ## _SYM, IWL_ ## sym ## _POS, \
|
|
+ IWL_ ## sym ## _LEN)
|
|
+
|
|
+
|
|
+#define KELVIN_TO_CELSIUS(x) ((x)-273)
|
|
+#define CELSIUS_TO_KELVIN(x) ((x)+273)
|
|
+
|
|
+#define IEEE80211_CHAN_W_RADAR_DETECT 0x00000010
|
|
+
|
|
+static inline struct ieee80211_conf *ieee80211_get_hw_conf(
|
|
+ struct ieee80211_hw *hw)
|
|
+{
|
|
+ return &hw->conf;
|
|
+}
|
|
+
|
|
+#define QOS_CONTROL_LEN 2
|
|
+
|
|
+#define IEEE80211_STYPE_BACK_REQ 0x0080
|
|
+#define IEEE80211_STYPE_BACK 0x0090
|
|
+
|
|
+
|
|
+static inline int ieee80211_is_management(u16 fc)
|
|
+{
|
|
+ return (fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT;
|
|
+}
|
|
+
|
|
+static inline int ieee80211_is_control(u16 fc)
|
|
+{
|
|
+ return (fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL;
|
|
+}
|
|
+
|
|
+static inline int ieee80211_is_data(u16 fc)
|
|
+{
|
|
+ return (fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA;
|
|
+}
|
|
+
|
|
+static inline int ieee80211_is_back_request(u16 fc)
|
|
+{
|
|
+ return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL) &&
|
|
+ ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BACK_REQ);
|
|
+}
|
|
+
|
|
+static inline int ieee80211_is_probe_response(u16 fc)
|
|
+{
|
|
+ return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) &&
|
|
+ ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP);
|
|
+}
|
|
+
|
|
+static inline int ieee80211_is_probe_request(u16 fc)
|
|
+{
|
|
+ return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) &&
|
|
+ ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_REQ);
|
|
+}
|
|
+
|
|
+static inline int ieee80211_is_beacon(u16 fc)
|
|
+{
|
|
+ return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) &&
|
|
+ ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON);
|
|
+}
|
|
+
|
|
+static inline int ieee80211_is_atim(u16 fc)
|
|
+{
|
|
+ return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) &&
|
|
+ ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ATIM);
|
|
+}
|
|
+
|
|
+static inline int ieee80211_is_assoc_request(u16 fc)
|
|
+{
|
|
+ return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) &&
|
|
+ ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ);
|
|
+}
|
|
+
|
|
+static inline int ieee80211_is_assoc_response(u16 fc)
|
|
+{
|
|
+ return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) &&
|
|
+ ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_RESP);
|
|
+}
|
|
+
|
|
+static inline int ieee80211_is_auth(u16 fc)
|
|
+{
|
|
+ return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) &&
|
|
+ ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ);
|
|
+}
|
|
+
|
|
+static inline int ieee80211_is_deauth(u16 fc)
|
|
+{
|
|
+ return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) &&
|
|
+ ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ);
|
|
+}
|
|
+
|
|
+static inline int ieee80211_is_disassoc(u16 fc)
|
|
+{
|
|
+ return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) &&
|
|
+ ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ);
|
|
+}
|
|
+
|
|
+static inline int ieee80211_is_reassoc_request(u16 fc)
|
|
+{
|
|
+ return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) &&
|
|
+ ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_REASSOC_REQ);
|
|
+}
|
|
+
|
|
+static inline int ieee80211_is_reassoc_response(u16 fc)
|
|
+{
|
|
+ return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) &&
|
|
+ ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_REASSOC_RESP);
|
|
+}
|
|
+
|
|
+static inline int iwl_check_bits(unsigned long field, unsigned long mask)
|
|
+{
|
|
+ return ((field & mask) == mask) ? 1 : 0;
|
|
+}
|
|
+
|
|
+static inline unsigned long elapsed_jiffies(unsigned long start,
|
|
+ unsigned long end)
|
|
+{
|
|
+ if (end > start)
|
|
+ return end - start;
|
|
+
|
|
+ return end + (MAX_JIFFY_OFFSET - start);
|
|
+}
|
|
+
|
|
+#endif /* __iwl_helpers_h__ */
|
|
diff --git a/drivers/net/wireless/iwl-hw.h b/drivers/net/wireless/iwl-hw.h
|
|
new file mode 100644
|
|
index 0000000..3743cdb
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/iwl-hw.h
|
|
@@ -0,0 +1,717 @@
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * 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) 2005 - 2007 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 Geeral 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:
|
|
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
|
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
+ *
|
|
+ * BSD LICENSE
|
|
+ *
|
|
+ * Copyright(c) 2005 - 2007 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 __iwlwifi_hw_h__
|
|
+#define __iwlwifi_hw_h__
|
|
+
|
|
+/*
|
|
+ * This file defines hardware constants common to 3945 and 4965.
|
|
+ *
|
|
+ * Device-specific constants are defined in iwl-3945-hw.h and iwl-4965-hw.h,
|
|
+ * although this file contains a few definitions for which the .c
|
|
+ * implementation is the same for 3945 and 4965, except for the value of
|
|
+ * a constant.
|
|
+ *
|
|
+ * uCode API constants are defined in iwl-commands.h.
|
|
+ *
|
|
+ * NOTE: DO NOT PUT OS IMPLEMENTATION-SPECIFIC DECLARATIONS HERE
|
|
+ *
|
|
+ * The iwl-*hw.h (and files they include) files should remain OS/driver
|
|
+ * implementation independent, declaring only the hardware interface.
|
|
+ */
|
|
+
|
|
+/* uCode queue management definitions */
|
|
+#define IWL_CMD_QUEUE_NUM 4
|
|
+#define IWL_CMD_FIFO_NUM 4
|
|
+#define IWL_BACK_QUEUE_FIRST_ID 7
|
|
+
|
|
+/* Tx rates */
|
|
+#define IWL_CCK_RATES 4
|
|
+#define IWL_OFDM_RATES 8
|
|
+
|
|
+#if IWL == 3945
|
|
+#define IWL_HT_RATES 0
|
|
+#elif IWL == 4965
|
|
+#define IWL_HT_RATES 16
|
|
+#endif
|
|
+
|
|
+#define IWL_MAX_RATES (IWL_CCK_RATES+IWL_OFDM_RATES+IWL_HT_RATES)
|
|
+
|
|
+/* Time constants */
|
|
+#define SHORT_SLOT_TIME 9
|
|
+#define LONG_SLOT_TIME 20
|
|
+
|
|
+/* RSSI to dBm */
|
|
+#if IWL == 3945
|
|
+#define IWL_RSSI_OFFSET 95
|
|
+#elif IWL == 4965
|
|
+#define IWL_RSSI_OFFSET 44
|
|
+#endif
|
|
+
|
|
+#include "iwl-eeprom.h"
|
|
+#include "iwl-commands.h"
|
|
+
|
|
+#define PCI_LINK_CTRL 0x0F0
|
|
+#define PCI_POWER_SOURCE 0x0C8
|
|
+#define PCI_REG_WUM8 0x0E8
|
|
+
|
|
+/* register and values */
|
|
+/* base */
|
|
+#define CSR_BASE (0x0)
|
|
+#define HBUS_BASE (0x400)
|
|
+#define FH_BASE (0x800)
|
|
+
|
|
+#define HBUS_TARG_MBX_C (HBUS_BASE+0x030)
|
|
+
|
|
+/*=== CSR (control and status registers) ===*/
|
|
+
|
|
+#define CSR_SW_VER (CSR_BASE+0x000)
|
|
+#define CSR_HW_IF_CONFIG_REG (CSR_BASE+0x000) /* hardware interface config */
|
|
+#define CSR_INT_COALESCING (CSR_BASE+0x004) /* accum ints, 32-usec units */
|
|
+#define CSR_INT (CSR_BASE+0x008) /* host interrupt status/ack */
|
|
+#define CSR_INT_MASK (CSR_BASE+0x00c) /* host interrupt enable */
|
|
+#define CSR_FH_INT_STATUS (CSR_BASE+0x010) /* busmaster int status/ack*/
|
|
+#define CSR_GPIO_IN (CSR_BASE+0x018) /* read external chip pins */
|
|
+#define CSR_RESET (CSR_BASE+0x020) /* busmaster enable, NMI, etc*/
|
|
+#define CSR_GP_CNTRL (CSR_BASE+0x024)
|
|
+/* 0x028 - reserved */
|
|
+#define CSR_EEPROM_REG (CSR_BASE+0x02c)
|
|
+#define CSR_EEPROM_REG_READ_VALID_MSK 0x00000001
|
|
+#define CSR_EEPROM_REG_BIT_CMD 0x00000002
|
|
+
|
|
+#define CSR_EEPROM_GP (CSR_BASE+0x030)
|
|
+#define CSR_EEPROM_GP_VALID_MSK 0x00000006
|
|
+#define CSR_EEPROM_GP_BAD_SIGNATURE 0x00000000
|
|
+#define CSR_EEPROM_GP_IF_OWNER_MSK 0x00000180
|
|
+
|
|
+#define CSR_UCODE_DRV_GP1 (CSR_BASE+0x054)
|
|
+#define CSR_UCODE_DRV_GP1_SET (CSR_BASE+0x058)
|
|
+#define CSR_UCODE_DRV_GP1_CLR (CSR_BASE+0x05c)
|
|
+#define CSR_UCODE_DRV_GP2 (CSR_BASE+0x060)
|
|
+#define CSR_GIO_CHICKEN_BITS (CSR_BASE+0x100)
|
|
+#define CSR_ANA_PLL_CFG (CSR_BASE+0x20c)
|
|
+#define CSR_HW_REV_WA_REG (CSR_BASE+0x22C)
|
|
+
|
|
+/**
|
|
+ * BSM (Bootstrap State Machine)
|
|
+ *
|
|
+ * The Bootstrap State Machine (BSM) stores a short bootstrap uCode program
|
|
+ * in special SRAM that does not power down when the embedded control
|
|
+ * processor is sleeping (e.g. for periodic power-saving shutdowns of radio).
|
|
+ *
|
|
+ * When powering back up after sleeps (or during initial uCode load), the BSM
|
|
+ * internally loads the short bootstrap program from the special SRAM into the
|
|
+ * embedded processor's instruction SRAM, and starts the processor so it runs
|
|
+ * the bootstrap program.
|
|
+ *
|
|
+ * This bootstrap program loads (via PCI busmaster DMA) instructions and data
|
|
+ * images for a uCode program from host DRAM locations. The host driver
|
|
+ * indicates DRAM locations and sizes for instruction and data images via the
|
|
+ * four BSM_DRAM_* registers. Once the bootstrap program loads the new program,
|
|
+ * the new program starts automatically.
|
|
+ *
|
|
+ * The uCode used for open-source drivers includes two programs:
|
|
+ *
|
|
+ * 1) Initialization -- performs hardware calibration and sets up some
|
|
+ * internal data, then notifies host via "initialize alive" notification
|
|
+ * (struct iwl_init_alive_resp) that it has completed all of its work.
|
|
+ * After signal from host, it then loads and starts the runtime program.
|
|
+ * The initialization program must be used when initially setting up the
|
|
+ * NIC after loading the driver.
|
|
+ *
|
|
+ * 2) Runtime/Protocol -- performs all normal runtime operations. This
|
|
+ * notifies host via "alive" notification (struct iwl_alive_resp) that it
|
|
+ * is ready to be used.
|
|
+ *
|
|
+ * When initializing the NIC, the host driver does the following procedure:
|
|
+ *
|
|
+ * 1) Load bootstrap program (instructions only, no data image for bootstrap)
|
|
+ * into bootstrap memory. Use dword writes starting at BSM_SRAM_LOWER_BOUND
|
|
+ *
|
|
+ * 2) Point (via BSM_DRAM_*) to the "initialize" uCode data and instruction
|
|
+ * images in host DRAM.
|
|
+ *
|
|
+ * 3) Set up BSM to copy from BSM SRAM into uCode instruction SRAM when asked:
|
|
+ * BSM_WR_MEM_SRC_REG = 0
|
|
+ * BSM_WR_MEM_DST_REG = RTC_INST_LOWER_BOUND
|
|
+ * BSM_WR_MEM_DWCOUNT_REG = # dwords in bootstrap instruction image
|
|
+ *
|
|
+ * 4) Load bootstrap into instruction SRAM:
|
|
+ * BSM_WR_CTRL_REG = BSM_WR_CTRL_REG_BIT_START
|
|
+ *
|
|
+ * 5) Wait for load completion:
|
|
+ * Poll BSM_WR_CTRL_REG for BSM_WR_CTRL_REG_BIT_START = 0
|
|
+ *
|
|
+ * 6) Enable future boot loads whenever NIC's power management triggers it:
|
|
+ * BSM_WR_CTRL_REG = BSM_WR_CTRL_REG_BIT_START_EN
|
|
+ *
|
|
+ * 7) Start the NIC by removing all reset bits:
|
|
+ * CSR_RESET = 0
|
|
+ *
|
|
+ * The bootstrap uCode (already in instruction SRAM) loads initialization
|
|
+ * uCode. Initialization uCode performs data initialization, sends
|
|
+ * "initialize alive" notification to host, and waits for a signal from
|
|
+ * host to load runtime code.
|
|
+ *
|
|
+ * 4) Point (via BSM_DRAM_*) to the "runtime" uCode data and instruction
|
|
+ * images in host DRAM. The last register loaded must be the instruction
|
|
+ * bytecount register ("1" in MSbit tells initialization uCode to load
|
|
+ * the runtime uCode):
|
|
+ * BSM_DRAM_INST_BYTECOUNT_REG = bytecount | BSM_DRAM_INST_LOAD
|
|
+ *
|
|
+ * 5) Wait for "alive" notification, then issue normal runtime commands.
|
|
+ *
|
|
+ * Data caching during power-downs:
|
|
+ *
|
|
+ * Just before the embedded controller powers down (e.g for automatic
|
|
+ * power-saving modes, or for RFKILL), uCode stores (via PCI busmaster DMA)
|
|
+ * a current snapshot of the embedded processor's data SRAM into host DRAM.
|
|
+ * This caches the data while the embedded processor's memory is powered down.
|
|
+ * Location and size are controlled by BSM_DRAM_DATA_* registers.
|
|
+ *
|
|
+ * NOTE: Instruction SRAM does not need to be saved, since that doesn't
|
|
+ * change during operation; the original image (from uCode distribution
|
|
+ * file) can be used for reload.
|
|
+ *
|
|
+ * When powering back up, the BSM loads the bootstrap program. Bootstrap looks
|
|
+ * at the BSM_DRAM_* registers, which now point to the runtime instruction
|
|
+ * image and the cached (modified) runtime data (*not* the initialization
|
|
+ * uCode). Bootstrap reloads these runtime images into SRAM, and restarts the
|
|
+ * uCode from where it left off before the power-down.
|
|
+ *
|
|
+ * NOTE: Initialization uCode does *not* run as part of the save/restore
|
|
+ * procedure.
|
|
+ *
|
|
+ * This save/restore method is mostly for autonomous power management during
|
|
+ * normal operation (result of POWER_TABLE_CMD). Platform suspend/resume and
|
|
+ * RFKILL should use complete restarts (with total re-initialization) of uCode,
|
|
+ * allowing total shutdown (including BSM memory).
|
|
+ *
|
|
+ * Note that, during normal operation, the host DRAM that held the initial
|
|
+ * startup data for the runtime code is now being used as a backup data cache
|
|
+ * for modified data! If you need to completely re-initialize the NIC, make
|
|
+ * sure that you use the runtime data image from the uCode distribution file,
|
|
+ * not the modified/saved runtime data. You may want to store a separate
|
|
+ * "clean" runtime data image in DRAM to avoid disk reads of distribution file.
|
|
+ */
|
|
+
|
|
+/* BSM bit fields */
|
|
+#define BSM_WR_CTRL_REG_BIT_START (0x80000000) /* start boot load now */
|
|
+#define BSM_WR_CTRL_REG_BIT_START_EN (0x40000000) /* enable boot after pwrup*/
|
|
+#define BSM_DRAM_INST_LOAD (0x80000000) /* start program load now */
|
|
+
|
|
+/* BSM addresses */
|
|
+#define BSM_BASE (CSR_BASE + 0x3400)
|
|
+
|
|
+#define BSM_WR_CTRL_REG (BSM_BASE + 0x000) /* ctl and status */
|
|
+#define BSM_WR_MEM_SRC_REG (BSM_BASE + 0x004) /* source in BSM mem */
|
|
+#define BSM_WR_MEM_DST_REG (BSM_BASE + 0x008) /* dest in SRAM mem */
|
|
+#define BSM_WR_DWCOUNT_REG (BSM_BASE + 0x00C) /* bytes */
|
|
+#define BSM_WR_STATUS_REG (BSM_BASE + 0x010) /* bit 0: 1 == done */
|
|
+
|
|
+/*
|
|
+ * Pointers and size regs for bootstrap load and data SRAM save/restore.
|
|
+ * NOTE: 3945 pointers use bits 31:0 of DRAM address.
|
|
+ * 4965 pointers use bits 35:4 of DRAM address.
|
|
+ */
|
|
+#define BSM_DRAM_INST_PTR_REG (BSM_BASE + 0x090)
|
|
+#define BSM_DRAM_INST_BYTECOUNT_REG (BSM_BASE + 0x094)
|
|
+#define BSM_DRAM_DATA_PTR_REG (BSM_BASE + 0x098)
|
|
+#define BSM_DRAM_DATA_BYTECOUNT_REG (BSM_BASE + 0x09C)
|
|
+
|
|
+/*
|
|
+ * BSM special memory, stays powered on during power-save sleeps.
|
|
+ * Read/write, address range from LOWER_BOUND to (LOWER_BOUND + SIZE -1)
|
|
+ */
|
|
+#define BSM_SRAM_LOWER_BOUND (CSR_BASE + 0x3800)
|
|
+#define BSM_SRAM_SIZE (1024) /* bytes */
|
|
+
|
|
+
|
|
+/* SCD (Scheduler) */
|
|
+#define SCD_BASE (CSR_BASE + 0x2E00)
|
|
+
|
|
+#define SCD_MODE_REG (SCD_BASE + 0x000)
|
|
+#define SCD_ARASTAT_REG (SCD_BASE + 0x004)
|
|
+#define SCD_TXFACT_REG (SCD_BASE + 0x010)
|
|
+#define SCD_TXF4MF_REG (SCD_BASE + 0x014)
|
|
+#define SCD_TXF5MF_REG (SCD_BASE + 0x020)
|
|
+#define SCD_SBYP_MODE_1_REG (SCD_BASE + 0x02C)
|
|
+#define SCD_SBYP_MODE_2_REG (SCD_BASE + 0x030)
|
|
+
|
|
+/*=== HBUS (Host-side Bus) ===*/
|
|
+
|
|
+#define HBUS_TARG_MEM_RADDR (HBUS_BASE+0x00c)
|
|
+#define HBUS_TARG_MEM_WADDR (HBUS_BASE+0x010)
|
|
+#define HBUS_TARG_MEM_WDAT (HBUS_BASE+0x018)
|
|
+#define HBUS_TARG_MEM_RDAT (HBUS_BASE+0x01c)
|
|
+#define HBUS_TARG_PRPH_WADDR (HBUS_BASE+0x044)
|
|
+#define HBUS_TARG_PRPH_RADDR (HBUS_BASE+0x048)
|
|
+#define HBUS_TARG_PRPH_WDAT (HBUS_BASE+0x04c)
|
|
+#define HBUS_TARG_PRPH_RDAT (HBUS_BASE+0x050)
|
|
+#define HBUS_TARG_WRPTR (HBUS_BASE+0x060)
|
|
+
|
|
+/*=== FH (data Flow Handler) ===*/
|
|
+
|
|
+#define FH_CBCC_TABLE (FH_BASE+0x140)
|
|
+#define FH_TFDB_TABLE (FH_BASE+0x180)
|
|
+#define FH_RCSR_TABLE (FH_BASE+0x400)
|
|
+#define FH_RSSR_TABLE (FH_BASE+0x4c0)
|
|
+#define FH_TCSR_TABLE (FH_BASE+0x500)
|
|
+#define FH_TSSR_TABLE (FH_BASE+0x680)
|
|
+
|
|
+/* TFDB (Transmit Frame Buffer Descriptor) */
|
|
+#define FH_TFDB(_channel, buf) \
|
|
+ (FH_TFDB_TABLE+((_channel)*2+(buf))*0x28)
|
|
+#define ALM_FH_TFDB_CHNL_BUF_CTRL_REG(_channel) \
|
|
+ (FH_TFDB_TABLE + 0x50 * _channel)
|
|
+/* CBCC _channel is [0,2] */
|
|
+#define FH_CBCC(_channel) (FH_CBCC_TABLE+(_channel)*0x8)
|
|
+#define FH_CBCC_CTRL(_channel) (FH_CBCC(_channel)+0x00)
|
|
+#define FH_CBCC_BASE(_channel) (FH_CBCC(_channel)+0x04)
|
|
+
|
|
+/* RCSR _channel is [0,2] */
|
|
+#define FH_RCSR(_channel) (FH_RCSR_TABLE+(_channel)*0x40)
|
|
+#define FH_RCSR_CONFIG(_channel) (FH_RCSR(_channel)+0x00)
|
|
+#define FH_RCSR_RBD_BASE(_channel) (FH_RCSR(_channel)+0x04)
|
|
+#define FH_RCSR_WPTR(_channel) (FH_RCSR(_channel)+0x20)
|
|
+#define FH_RCSR_RPTR_ADDR(_channel) (FH_RCSR(_channel)+0x24)
|
|
+
|
|
+#if IWL == 3945
|
|
+#define FH_RSCSR_CHNL0_WPTR (FH_RCSR_WPTR(0))
|
|
+#elif IWL == 4965
|
|
+#define FH_RSCSR_CHNL0_WPTR (FH_RSCSR_CHNL0_RBDCB_WPTR_REG)
|
|
+#endif
|
|
+
|
|
+/* RSSR */
|
|
+#define FH_RSSR_CTRL (FH_RSSR_TABLE+0x000)
|
|
+#define FH_RSSR_STATUS (FH_RSSR_TABLE+0x004)
|
|
+/* TCSR */
|
|
+#define FH_TCSR(_channel) (FH_TCSR_TABLE+(_channel)*0x20)
|
|
+#define FH_TCSR_CONFIG(_channel) (FH_TCSR(_channel)+0x00)
|
|
+#define FH_TCSR_CREDIT(_channel) (FH_TCSR(_channel)+0x04)
|
|
+#define FH_TCSR_BUFF_STTS(_channel) (FH_TCSR(_channel)+0x08)
|
|
+/* TSSR */
|
|
+#define FH_TSSR_CBB_BASE (FH_TSSR_TABLE+0x000)
|
|
+#define FH_TSSR_MSG_CONFIG (FH_TSSR_TABLE+0x008)
|
|
+#define FH_TSSR_TX_STATUS (FH_TSSR_TABLE+0x010)
|
|
+/* 18 - reserved */
|
|
+
|
|
+/* card static random access memory (SRAM) for processor data and instructs */
|
|
+#define RTC_INST_LOWER_BOUND (0x000000)
|
|
+#define RTC_DATA_LOWER_BOUND (0x800000)
|
|
+
|
|
+/* HW I/F configuration */
|
|
+#define CSR_HW_IF_CONFIG_REG_BIT_ALMAGOR_MB (0x00000100)
|
|
+#define CSR_HW_IF_CONFIG_REG_BIT_ALMAGOR_MM (0x00000200)
|
|
+#define CSR_HW_IF_CONFIG_REG_BIT_SKU_MRC (0x00000400)
|
|
+#define CSR_HW_IF_CONFIG_REG_BIT_BOARD_TYPE (0x00000800)
|
|
+#define CSR_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A (0x00000000)
|
|
+#define CSR_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B (0x00001000)
|
|
+#define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000)
|
|
+
|
|
+#define CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP (0x00000001)
|
|
+#define CSR_UCODE_SW_BIT_RFKILL (0x00000002)
|
|
+#define CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED (0x00000004)
|
|
+#define CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT (0x00000008)
|
|
+
|
|
+#define CSR_GPIO_IN_BIT_AUX_POWER (0x00000200)
|
|
+#define CSR_GPIO_IN_VAL_VAUX_PWR_SRC (0x00000000)
|
|
+#define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX (0x00800000)
|
|
+#define CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER (0x20000000)
|
|
+#define CSR_GPIO_IN_VAL_VMAIN_PWR_SRC CSR_GPIO_IN_BIT_AUX_POWER
|
|
+
|
|
+#define PCI_CFG_PMC_PME_FROM_D3COLD_SUPPORT (0x80000000)
|
|
+
|
|
+/* interrupt flags in INTA, set by uCode or hardware (e.g. dma),
|
|
+ * acknowledged (reset) by host writing "1" to flagged bits. */
|
|
+#define BIT_INT_FH_RX (1<<31) /* Rx DMA, cmd responses, FH_INT[17:16] */
|
|
+#define BIT_INT_ERR (1<<29) /* DMA hardware error FH_INT[31] */
|
|
+#define BIT_INT_FH_TX (1<<27) /* Tx DMA FH_INT[1:0] */
|
|
+#define BIT_INT_MAC_CLK_ACTV (1<<26) /* NIC controller's clock toggled on/off */
|
|
+#define BIT_INT_SWERROR (1<<25) /* uCode error */
|
|
+#define BIT_INT_RF_KILL (1<<7) /* HW RFKILL switch GP_CNTRL[27] toggled */
|
|
+#define BIT_INT_CT_KILL (1<<6) /* Critical temp (chip too hot) rfkill */
|
|
+#define BIT_INT_SW_RX (1<<3) /* Rx, command responses, 3945 */
|
|
+#define BIT_INT_WAKEUP (1<<1) /* NIC controller waking up (pwr mgmt) */
|
|
+#define BIT_INT_ALIVE (1<<0) /* uCode interrupts once it initializes */
|
|
+
|
|
+#define CSR_INI_SET_MASK (BIT_INT_FH_RX | \
|
|
+ BIT_INT_ERR | \
|
|
+ BIT_INT_FH_TX | \
|
|
+ BIT_INT_SWERROR | \
|
|
+ BIT_INT_RF_KILL | \
|
|
+ BIT_INT_SW_RX | \
|
|
+ BIT_INT_WAKEUP | \
|
|
+ BIT_INT_ALIVE)
|
|
+
|
|
+/* interrupt flags in FH (flow handler) (PCI busmaster DMA) */
|
|
+#define BIT_FH_INT_ERR (1<<31) /* Error */
|
|
+#define BIT_FH_INT_HI_PRIOR (1<<30) /* High priority Rx, bypass coalescing */
|
|
+#define BIT_FH_INT_RX_CHNL2 (1<<18) /* Rx channel 2 (3945 only) */
|
|
+#define BIT_FH_INT_RX_CHNL1 (1<<17) /* Rx channel 1 */
|
|
+#define BIT_FH_INT_RX_CHNL0 (1<<16) /* Rx channel 0 */
|
|
+#define BIT_FH_INT_TX_CHNL6 (1<<6) /* Tx channel 6 (3945 only) */
|
|
+#define BIT_FH_INT_TX_CHNL1 (1<<1) /* Tx channel 1 */
|
|
+#define BIT_FH_INT_TX_CHNL0 (1<<0) /* Tx channel 0 */
|
|
+
|
|
+#define FH_INT_RX_MASK (BIT_FH_INT_HI_PRIOR | \
|
|
+ BIT_FH_INT_RX_CHNL2 | \
|
|
+ BIT_FH_INT_RX_CHNL1 | \
|
|
+ BIT_FH_INT_RX_CHNL0)
|
|
+
|
|
+#define FH_INT_TX_MASK (BIT_FH_INT_TX_CHNL6 | \
|
|
+ BIT_FH_INT_TX_CHNL1 | \
|
|
+ BIT_FH_INT_TX_CHNL0 )
|
|
+
|
|
+/* RESET */
|
|
+#define CSR_RESET_REG_FLAG_NEVO_RESET (0x00000001)
|
|
+#define CSR_RESET_REG_FLAG_FORCE_NMI (0x00000002)
|
|
+#define CSR_RESET_REG_FLAG_SW_RESET (0x00000080)
|
|
+#define CSR_RESET_REG_FLAG_MASTER_DISABLED (0x00000100)
|
|
+#define CSR_RESET_REG_FLAG_STOP_MASTER (0x00000200)
|
|
+
|
|
+/* GP (general purpose) CONTROL */
|
|
+#define CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY (0x00000001)
|
|
+#define CSR_GP_CNTRL_REG_FLAG_INIT_DONE (0x00000004)
|
|
+#define CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ (0x00000008)
|
|
+#define CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP (0x00000010)
|
|
+
|
|
+#define CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN (0x00000001)
|
|
+
|
|
+#define CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE (0x07000000)
|
|
+#define CSR_GP_CNTRL_REG_FLAG_MAC_POWER_SAVE (0x04000000)
|
|
+#define CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW (0x08000000)
|
|
+
|
|
+/* APMG (power management) constants */
|
|
+#define APMG_CLK_CTRL_REG (0x003000)
|
|
+#define ALM_APMG_CLK_EN (0x003004)
|
|
+#define ALM_APMG_CLK_DIS (0x003008)
|
|
+#define ALM_APMG_PS_CTL (0x00300c)
|
|
+#define ALM_APMG_PCIDEV_STT (0x003010)
|
|
+#define ALM_APMG_RFKILL (0x003014)
|
|
+#define ALM_APMG_LARC_INT (0x00301c)
|
|
+#define ALM_APMG_LARC_INT_MSK (0x003020)
|
|
+
|
|
+#define APMG_CLK_REG_VAL_DMA_CLK_RQT (0x00000200)
|
|
+#define APMG_CLK_REG_VAL_BSM_CLK_RQT (0x00000800)
|
|
+
|
|
+#define APMG_PS_CTRL_REG_VAL_ALM_R_RESET_REQ (0x04000000)
|
|
+
|
|
+#define APMG_DEV_STATE_REG_VAL_L1_ACTIVE_DISABLE (0x00000800)
|
|
+
|
|
+#define APMG_PS_CTRL_REG_MSK_POWER_SRC (0x03000000)
|
|
+#define APMG_PS_CTRL_REG_VAL_POWER_SRC_VMAIN (0x00000000)
|
|
+#define APMG_PS_CTRL_REG_VAL_POWER_SRC_VAUX (0x01000000)
|
|
+
|
|
+/* DBM */
|
|
+
|
|
+#define ALM_FH_SRVC_CHNL (6)
|
|
+
|
|
+#define ALM_FH_RCSR_RX_CONFIG_REG_POS_RBDC_SIZE (20)
|
|
+#define ALM_FH_RCSR_RX_CONFIG_REG_POS_IRQ_RBTH (4)
|
|
+
|
|
+#define ALM_FH_RCSR_RX_CONFIG_REG_BIT_WR_STTS_EN (0x08000000)
|
|
+
|
|
+#define ALM_FH_RCSR_RX_CONFIG_REG_VAL_DMA_CHNL_EN_ENABLE (0x80000000)
|
|
+
|
|
+#define ALM_FH_RCSR_RX_CONFIG_REG_VAL_RDRBD_EN_ENABLE (0x20000000)
|
|
+
|
|
+#define ALM_FH_RCSR_RX_CONFIG_REG_VAL_MAX_FRAG_SIZE_128 (0x01000000)
|
|
+
|
|
+#define ALM_FH_RCSR_RX_CONFIG_REG_VAL_IRQ_DEST_INT_HOST (0x00001000)
|
|
+
|
|
+#define ALM_FH_RCSR_RX_CONFIG_REG_VAL_MSG_MODE_FH (0x00000000)
|
|
+
|
|
+#define ALM_FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_TXF (0x00000000)
|
|
+#define ALM_FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_DRIVER (0x00000001)
|
|
+
|
|
+#define ALM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE_VAL (0x00000000)
|
|
+#define ALM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL (0x00000008)
|
|
+
|
|
+#define ALM_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD (0x00200000)
|
|
+
|
|
+#define ALM_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_NOINT (0x00000000)
|
|
+
|
|
+#define ALM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE (0x00000000)
|
|
+#define ALM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE (0x80000000)
|
|
+
|
|
+#define ALM_FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID (0x00004000)
|
|
+
|
|
+#define ALM_FH_TCSR_CHNL_TX_BUF_STS_REG_BIT_TFDB_WPTR (0x00000001)
|
|
+
|
|
+#define ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TXPD_ON (0xFF000000)
|
|
+#define ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_TXPD_ON (0x00FF0000)
|
|
+
|
|
+#define ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_MAX_FRAG_SIZE_128B (0x00000400)
|
|
+
|
|
+#define ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TFD_ON (0x00000100)
|
|
+#define ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_CBB_ON (0x00000080)
|
|
+
|
|
+#define ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RSP_WAIT_TH (0x00000020)
|
|
+#define ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_RSP_WAIT_TH (0x00000005)
|
|
+
|
|
+#define ALM_TB_MAX_BYTES_COUNT (0xFFF0)
|
|
+
|
|
+#define ALM_FH_TSSR_TX_STATUS_REG_BIT_BUFS_EMPTY(_channel) \
|
|
+ ((1LU << _channel) << 24)
|
|
+#define ALM_FH_TSSR_TX_STATUS_REG_BIT_NO_PEND_REQ(_channel) \
|
|
+ ((1LU << _channel) << 16)
|
|
+
|
|
+#define ALM_FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(_channel) \
|
|
+ (ALM_FH_TSSR_TX_STATUS_REG_BIT_BUFS_EMPTY(_channel) | \
|
|
+ ALM_FH_TSSR_TX_STATUS_REG_BIT_NO_PEND_REQ(_channel))
|
|
+#define PCI_CFG_REV_ID_BIT_BASIC_SKU (0x40) /* bit 6 */
|
|
+#define PCI_CFG_REV_ID_BIT_RTP (0x80) /* bit 7 */
|
|
+
|
|
+#define HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED (0x00000004)
|
|
+
|
|
+#define TFD_QUEUE_MIN 0
|
|
+#define TFD_QUEUE_MAX 6
|
|
+#define TFD_QUEUE_SIZE_MAX (256)
|
|
+
|
|
+/* spectrum and channel data structures */
|
|
+#define IWL_NUM_SCAN_RATES (2)
|
|
+
|
|
+#define IWL_SCAN_FLAG_24GHZ (1<<0)
|
|
+#define IWL_SCAN_FLAG_52GHZ (1<<1)
|
|
+#define IWL_SCAN_FLAG_ACTIVE (1<<2)
|
|
+#define IWL_SCAN_FLAG_DIRECT (1<<3)
|
|
+
|
|
+#define IWL_MAX_CMD_SIZE 1024
|
|
+
|
|
+/* LEDs mode */
|
|
+
|
|
+#define IWL_DEFAULT_TX_RETRY 15
|
|
+#define IWL_MAX_TX_RETRY 16
|
|
+
|
|
+/*********************************************/
|
|
+
|
|
+#define RFD_SIZE 4
|
|
+#define NUM_TFD_CHUNKS 4
|
|
+
|
|
+#define RX_QUEUE_SIZE 256
|
|
+#define RX_QUEUE_MASK 255
|
|
+#define RX_QUEUE_SIZE_LOG 8
|
|
+
|
|
+/*
|
|
+ * TX Queue Flag Definitions
|
|
+ */
|
|
+
|
|
+/* abort attempt if mgmt frame is rx'd */
|
|
+
|
|
+/* require CTS */
|
|
+
|
|
+/* use short preamble */
|
|
+#define DCT_FLAG_LONG_PREAMBLE 0x00
|
|
+#define DCT_FLAG_SHORT_PREAMBLE 0x04
|
|
+
|
|
+/* RTS/CTS first */
|
|
+
|
|
+/* don't calculate duration field */
|
|
+
|
|
+/* even if MAC WEP set (allows pre-encrypt) */
|
|
+#define IWL_
|
|
+/* overwrite TSF field */
|
|
+
|
|
+/* ACK rx is expected to follow */
|
|
+#define DCT_FLAG_ACK_REQD 0x80
|
|
+
|
|
+#define IWL_MB_DISASSOCIATE_THRESHOLD_DEFAULT 24
|
|
+#define IWL_MB_ROAMING_THRESHOLD_DEFAULT 8
|
|
+#define IWL_REAL_RATE_RX_PACKET_THRESHOLD 300
|
|
+
|
|
+/* QoS definitions */
|
|
+
|
|
+#define CW_MIN_OFDM 15
|
|
+#define CW_MAX_OFDM 1023
|
|
+#define CW_MIN_CCK 31
|
|
+#define CW_MAX_CCK 1023
|
|
+
|
|
+#define QOS_TX0_CW_MIN_OFDM CW_MIN_OFDM
|
|
+#define QOS_TX1_CW_MIN_OFDM CW_MIN_OFDM
|
|
+#define QOS_TX2_CW_MIN_OFDM ((CW_MIN_OFDM + 1) / 2 - 1)
|
|
+#define QOS_TX3_CW_MIN_OFDM ((CW_MIN_OFDM + 1) / 4 - 1)
|
|
+
|
|
+#define QOS_TX0_CW_MIN_CCK CW_MIN_CCK
|
|
+#define QOS_TX1_CW_MIN_CCK CW_MIN_CCK
|
|
+#define QOS_TX2_CW_MIN_CCK ((CW_MIN_CCK + 1) / 2 - 1)
|
|
+#define QOS_TX3_CW_MIN_CCK ((CW_MIN_CCK + 1) / 4 - 1)
|
|
+
|
|
+#define QOS_TX0_CW_MAX_OFDM CW_MAX_OFDM
|
|
+#define QOS_TX1_CW_MAX_OFDM CW_MAX_OFDM
|
|
+#define QOS_TX2_CW_MAX_OFDM CW_MIN_OFDM
|
|
+#define QOS_TX3_CW_MAX_OFDM ((CW_MIN_OFDM + 1) / 2 - 1)
|
|
+
|
|
+#define QOS_TX0_CW_MAX_CCK CW_MAX_CCK
|
|
+#define QOS_TX1_CW_MAX_CCK CW_MAX_CCK
|
|
+#define QOS_TX2_CW_MAX_CCK CW_MIN_CCK
|
|
+#define QOS_TX3_CW_MAX_CCK ((CW_MIN_CCK + 1) / 2 - 1)
|
|
+
|
|
+#define QOS_TX0_AIFS 3
|
|
+#define QOS_TX1_AIFS 7
|
|
+#define QOS_TX2_AIFS 2
|
|
+#define QOS_TX3_AIFS 2
|
|
+
|
|
+#define QOS_TX0_ACM 0
|
|
+#define QOS_TX1_ACM 0
|
|
+#define QOS_TX2_ACM 0
|
|
+#define QOS_TX3_ACM 0
|
|
+
|
|
+#define QOS_TX0_TXOP_LIMIT_CCK 0
|
|
+#define QOS_TX1_TXOP_LIMIT_CCK 0
|
|
+#define QOS_TX2_TXOP_LIMIT_CCK 6016
|
|
+#define QOS_TX3_TXOP_LIMIT_CCK 3264
|
|
+
|
|
+#define QOS_TX0_TXOP_LIMIT_OFDM 0
|
|
+#define QOS_TX1_TXOP_LIMIT_OFDM 0
|
|
+#define QOS_TX2_TXOP_LIMIT_OFDM 3008
|
|
+#define QOS_TX3_TXOP_LIMIT_OFDM 1504
|
|
+
|
|
+#define DEF_TX0_CW_MIN_OFDM CW_MIN_OFDM
|
|
+#define DEF_TX1_CW_MIN_OFDM CW_MIN_OFDM
|
|
+#define DEF_TX2_CW_MIN_OFDM CW_MIN_OFDM
|
|
+#define DEF_TX3_CW_MIN_OFDM CW_MIN_OFDM
|
|
+
|
|
+#define DEF_TX0_CW_MIN_CCK CW_MIN_CCK
|
|
+#define DEF_TX1_CW_MIN_CCK CW_MIN_CCK
|
|
+#define DEF_TX2_CW_MIN_CCK CW_MIN_CCK
|
|
+#define DEF_TX3_CW_MIN_CCK CW_MIN_CCK
|
|
+
|
|
+#define DEF_TX0_CW_MAX_OFDM CW_MAX_OFDM
|
|
+#define DEF_TX1_CW_MAX_OFDM CW_MAX_OFDM
|
|
+#define DEF_TX2_CW_MAX_OFDM CW_MAX_OFDM
|
|
+#define DEF_TX3_CW_MAX_OFDM CW_MAX_OFDM
|
|
+
|
|
+#define DEF_TX0_CW_MAX_CCK CW_MAX_CCK
|
|
+#define DEF_TX1_CW_MAX_CCK CW_MAX_CCK
|
|
+#define DEF_TX2_CW_MAX_CCK CW_MAX_CCK
|
|
+#define DEF_TX3_CW_MAX_CCK CW_MAX_CCK
|
|
+
|
|
+#define DEF_TX0_AIFS (2)
|
|
+#define DEF_TX1_AIFS (2)
|
|
+#define DEF_TX2_AIFS (2)
|
|
+#define DEF_TX3_AIFS (2)
|
|
+
|
|
+#define DEF_TX0_ACM 0
|
|
+#define DEF_TX1_ACM 0
|
|
+#define DEF_TX2_ACM 0
|
|
+#define DEF_TX3_ACM 0
|
|
+
|
|
+#define DEF_TX0_TXOP_LIMIT_CCK 0
|
|
+#define DEF_TX1_TXOP_LIMIT_CCK 0
|
|
+#define DEF_TX2_TXOP_LIMIT_CCK 0
|
|
+#define DEF_TX3_TXOP_LIMIT_CCK 0
|
|
+
|
|
+#define DEF_TX0_TXOP_LIMIT_OFDM 0
|
|
+#define DEF_TX1_TXOP_LIMIT_OFDM 0
|
|
+#define DEF_TX2_TXOP_LIMIT_OFDM 0
|
|
+#define DEF_TX3_TXOP_LIMIT_OFDM 0
|
|
+
|
|
+#define QOS_QOS_SETS 3
|
|
+#define QOS_PARAM_SET_ACTIVE 0
|
|
+#define QOS_PARAM_SET_DEF_CCK 1
|
|
+#define QOS_PARAM_SET_DEF_OFDM 2
|
|
+
|
|
+#define CTRL_QOS_NO_ACK (0x0020)
|
|
+#define DCT_FLAG_EXT_QOS_ENABLED (0x10)
|
|
+
|
|
+#define IWL_TX_QUEUE_AC0 0
|
|
+#define IWL_TX_QUEUE_AC1 1
|
|
+#define IWL_TX_QUEUE_AC2 2
|
|
+#define IWL_TX_QUEUE_AC3 3
|
|
+#define IWL_TX_QUEUE_HCCA_1 5
|
|
+#define IWL_TX_QUEUE_HCCA_2 6
|
|
+
|
|
+#define U32_PAD(n) ((4-(n))&0x3)
|
|
+
|
|
+#define AC_BE_TID_MASK 0x9 /* TID 0 and 3 */
|
|
+#define AC_BK_TID_MASK 0x6 /* TID 1 and 2 */
|
|
+
|
|
+/*
|
|
+ * Generic queue structure
|
|
+ *
|
|
+ * Contains common data for Rx and Tx queues
|
|
+ */
|
|
+#define TFD_CTL_COUNT_SET(n) (n<<24)
|
|
+#define TFD_CTL_COUNT_GET(ctl) ((ctl>>24) & 7)
|
|
+#define TFD_CTL_PAD_SET(n) (n<<28)
|
|
+#define TFD_CTL_PAD_GET(ctl) (ctl>>28)
|
|
+
|
|
+#define TFD_TX_CMD_SLOTS 256
|
|
+#define TFD_CMD_SLOTS 32
|
|
+
|
|
+#define TFD_MAX_PAYLOAD_SIZE (sizeof(struct iwl_cmd) - \
|
|
+ sizeof(struct iwl_cmd_meta))
|
|
+
|
|
+/*
|
|
+ * RX related structures and functions
|
|
+ */
|
|
+#define RX_FREE_BUFFERS 64
|
|
+#define RX_LOW_WATERMARK 8
|
|
+
|
|
+#if IWL == 3945
|
|
+#include "iwl-3945-hw.h"
|
|
+#elif IWL == 4965
|
|
+#include "iwl-4965-hw.h"
|
|
+#endif
|
|
+
|
|
+#endif /* __iwlwifi_hw_h__ */
|
|
diff --git a/drivers/net/wireless/iwl-io.h b/drivers/net/wireless/iwl-io.h
|
|
new file mode 100644
|
|
index 0000000..f293702
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/iwl-io.h
|
|
@@ -0,0 +1,485 @@
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * Copyright(c) 2003 - 2007 Intel Corporation. All rights reserved.
|
|
+ *
|
|
+ * Portions of this file are derived from the ipw3945 project.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify it
|
|
+ * under the terms of version 2 of the GNU General Public License as
|
|
+ * published by the Free Software Foundation.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful, but WITHOUT
|
|
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
+ * more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License along with
|
|
+ * this program; if not, write to the Free Software Foundation, Inc.,
|
|
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
|
|
+ *
|
|
+ * The full GNU General Public License is included in this distribution in the
|
|
+ * file called LICENSE.
|
|
+ *
|
|
+ * Contact Information:
|
|
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
|
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+#ifndef __iwl_io_h__
|
|
+#define __iwl_io_h__
|
|
+
|
|
+#include <linux/io.h>
|
|
+
|
|
+#include "iwl-debug.h"
|
|
+
|
|
+/*
|
|
+ * IO, register, and NIC memory access functions
|
|
+ *
|
|
+ * NOTE on naming convention and macro usage for these
|
|
+ *
|
|
+ * A single _ prefix before a an access function means that no state
|
|
+ * check or debug information is printed when that function is called.
|
|
+ *
|
|
+ * A double __ prefix before an access function means that state is checked
|
|
+ * (in the case of *restricted calls) and the current line number is printed
|
|
+ * in addition to any other debug output.
|
|
+ *
|
|
+ * The non-prefixed name is the #define that maps the caller into a
|
|
+ * #define that provides the caller's __LINE__ to the double prefix version.
|
|
+ *
|
|
+ * If you wish to call the function without any debug or state checking,
|
|
+ * you should use the single _ prefix version (as is used by dependent IO
|
|
+ * routines, for example _iwl_read_restricted calls the non-check version of
|
|
+ * _iwl_read32.)
|
|
+ *
|
|
+ * These declarations are *extremely* useful in quickly isolating code deltas
|
|
+ * which result in misconfiguring of the hardware I/O. In combination with
|
|
+ * git-bisect and the IO debug level you can quickly determine the specific
|
|
+ * commit which breaks the IO sequence to the hardware.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#define _iwl_write32(ipw, ofs, val) writel((val), (ipw)->hw_base + (ofs))
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+static inline void __iwl_write32(const char *f, u32 l, struct iwl_priv *ipw,
|
|
+ u32 ofs, u32 val)
|
|
+{
|
|
+ IWL_DEBUG_IO("write_direct32(0x%08X, 0x%08X) - %s %d\n",
|
|
+ (u32) (ofs), (u32) (val), f, l);
|
|
+ _iwl_write32(ipw, ofs, val);
|
|
+}
|
|
+#define iwl_write32(ipw, ofs, val) \
|
|
+ __iwl_write32(__FILE__, __LINE__, ipw, ofs, val)
|
|
+#else
|
|
+#define iwl_write32(ipw, ofs, val) _iwl_write32(ipw, ofs, val)
|
|
+#endif
|
|
+
|
|
+#define _iwl_read32(ipw, ofs) readl((ipw)->hw_base + (ofs))
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+static inline u32 __iwl_read32(char *f, u32 l, struct iwl_priv *ipw, u32 ofs)
|
|
+{
|
|
+ IWL_DEBUG_IO("read_direct32(0x%08X) - %s %d\n", ofs, f, l);
|
|
+ return _iwl_read32(ipw, ofs);
|
|
+}
|
|
+#define iwl_read32(ipw, ofs) __iwl_read32(__FILE__, __LINE__, ipw, ofs)
|
|
+#else
|
|
+#define iwl_read32(p, o) _iwl_read32(p, o)
|
|
+#endif
|
|
+
|
|
+static inline int _iwl_poll_bit(struct iwl_priv *priv, u32 addr,
|
|
+ u32 bits, u32 mask, int timeout)
|
|
+{
|
|
+ int i = 0;
|
|
+
|
|
+ do {
|
|
+ if ((_iwl_read32(priv, addr) & mask) == (bits & mask))
|
|
+ return i;
|
|
+ mdelay(10);
|
|
+ i += 10;
|
|
+ } while (i < timeout);
|
|
+
|
|
+ return -ETIMEDOUT;
|
|
+}
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+static inline int __iwl_poll_bit(const char *f, u32 l,
|
|
+ struct iwl_priv *priv, u32 addr,
|
|
+ u32 bits, u32 mask, int timeout)
|
|
+{
|
|
+ int rc = _iwl_poll_bit(priv, addr, bits, mask, timeout);
|
|
+ if (unlikely(rc == -ETIMEDOUT))
|
|
+ IWL_DEBUG_IO
|
|
+ ("poll_bit(0x%08X, 0x%08X, 0x%08X) - timedout - %s %d\n",
|
|
+ addr, bits, mask, f, l);
|
|
+ else
|
|
+ IWL_DEBUG_IO
|
|
+ ("poll_bit(0x%08X, 0x%08X, 0x%08X) = 0x%08X - %s %d\n",
|
|
+ addr, bits, mask, rc, f, l);
|
|
+ return rc;
|
|
+}
|
|
+#define iwl_poll_bit(ipw, addr, bits, mask, timeout) \
|
|
+ __iwl_poll_bit(__FILE__, __LINE__, ipw, addr, bits, mask, timeout)
|
|
+#else
|
|
+#define iwl_poll_bit(p, a, b, m, t) _iwl_poll_bit(p, a, b, m, t)
|
|
+#endif
|
|
+
|
|
+static inline void _iwl_set_bit(struct iwl_priv *priv, u32 reg, u32 mask)
|
|
+{
|
|
+ _iwl_write32(priv, reg, _iwl_read32(priv, reg) | mask);
|
|
+}
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+static inline void __iwl_set_bit(const char *f, u32 l,
|
|
+ struct iwl_priv *priv, u32 reg, u32 mask)
|
|
+{
|
|
+ u32 val = _iwl_read32(priv, reg) | mask;
|
|
+ IWL_DEBUG_IO("set_bit(0x%08X, 0x%08X) = 0x%08X\n", reg, mask, val);
|
|
+ _iwl_write32(priv, reg, val);
|
|
+}
|
|
+#define iwl_set_bit(p, r, m) __iwl_set_bit(__FILE__, __LINE__, p, r, m)
|
|
+#else
|
|
+#define iwl_set_bit(p, r, m) _iwl_set_bit(p, r, m)
|
|
+#endif
|
|
+
|
|
+static inline void _iwl_clear_bit(struct iwl_priv *priv, u32 reg, u32 mask)
|
|
+{
|
|
+ _iwl_write32(priv, reg, _iwl_read32(priv, reg) & ~mask);
|
|
+}
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+static inline void __iwl_clear_bit(const char *f, u32 l,
|
|
+ struct iwl_priv *priv, u32 reg, u32 mask)
|
|
+{
|
|
+ u32 val = _iwl_read32(priv, reg) & ~mask;
|
|
+ IWL_DEBUG_IO("clear_bit(0x%08X, 0x%08X) = 0x%08X\n", reg, mask, val);
|
|
+ _iwl_write32(priv, reg, val);
|
|
+}
|
|
+#define iwl_clear_bit(p, r, m) __iwl_clear_bit(__FILE__, __LINE__, p, r, m)
|
|
+#else
|
|
+#define iwl_clear_bit(p, r, m) _iwl_clear_bit(p, r, m)
|
|
+#endif
|
|
+
|
|
+static inline int _iwl_grab_restricted_access(struct iwl_priv *priv)
|
|
+{
|
|
+ int rc;
|
|
+ u32 gp_ctl;
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+ if (atomic_read(&priv->restrict_refcnt))
|
|
+ return 0;
|
|
+#endif
|
|
+ if (test_bit(STATUS_RF_KILL_HW, &priv->status) ||
|
|
+ test_bit(STATUS_RF_KILL_SW, &priv->status)) {
|
|
+ IWL_WARNING("WARNING: Requesting MAC access during RFKILL "
|
|
+ "wakes up NIC\n");
|
|
+
|
|
+ /* 10 msec allows time for NIC to complete its data save */
|
|
+ gp_ctl = _iwl_read32(priv, CSR_GP_CNTRL);
|
|
+ if (gp_ctl & CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY) {
|
|
+ IWL_DEBUG_RF_KILL("Wait for complete power-down, "
|
|
+ "gpctl = 0x%08x\n", gp_ctl);
|
|
+ mdelay(10);
|
|
+ } else
|
|
+ IWL_DEBUG_RF_KILL("power-down complete, "
|
|
+ "gpctl = 0x%08x\n", gp_ctl);
|
|
+ }
|
|
+
|
|
+ /* this bit wakes up the NIC */
|
|
+ _iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
|
|
+ rc = _iwl_poll_bit(priv, CSR_GP_CNTRL,
|
|
+ CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
|
|
+ (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
|
|
+ CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 50);
|
|
+ if (rc < 0) {
|
|
+ IWL_ERROR("MAC is in deep sleep!\n");
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+ atomic_inc(&priv->restrict_refcnt);
|
|
+#endif
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+static inline int __iwl_grab_restricted_access(const char *f, u32 l,
|
|
+ struct iwl_priv *priv)
|
|
+{
|
|
+ if (atomic_read(&priv->restrict_refcnt))
|
|
+ IWL_DEBUG_INFO("Grabbing access while already held at "
|
|
+ "line %d.\n", l);
|
|
+
|
|
+ IWL_DEBUG_IO("grabbing restricted access - %s %d\n", f, l);
|
|
+
|
|
+ return _iwl_grab_restricted_access(priv);
|
|
+}
|
|
+#define iwl_grab_restricted_access(priv) \
|
|
+ __iwl_grab_restricted_access(__FILE__, __LINE__, priv)
|
|
+#else
|
|
+#define iwl_grab_restricted_access(priv) \
|
|
+ _iwl_grab_restricted_access(priv)
|
|
+#endif
|
|
+
|
|
+static inline void _iwl_release_restricted_access(struct iwl_priv *priv)
|
|
+{
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+ if (atomic_dec_and_test(&priv->restrict_refcnt))
|
|
+#endif
|
|
+ _iwl_clear_bit(priv, CSR_GP_CNTRL,
|
|
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
|
|
+}
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+static inline void __iwl_release_restricted_access(const char *f, u32 l,
|
|
+ struct iwl_priv *priv)
|
|
+{
|
|
+ if (atomic_read(&priv->restrict_refcnt) <= 0)
|
|
+ IWL_ERROR("Release unheld restricted access at line %d.\n", l);
|
|
+
|
|
+ IWL_DEBUG_IO("releasing restricted access - %s %d\n", f, l);
|
|
+ _iwl_release_restricted_access(priv);
|
|
+}
|
|
+#define iwl_release_restricted_access(priv) \
|
|
+ __iwl_release_restricted_access(__FILE__, __LINE__, priv)
|
|
+#else
|
|
+#define iwl_release_restricted_access(priv) \
|
|
+ _iwl_release_restricted_access(priv)
|
|
+#endif
|
|
+
|
|
+static inline u32 _iwl_read_restricted(struct iwl_priv *priv, u32 reg)
|
|
+{
|
|
+ return _iwl_read32(priv, reg);
|
|
+}
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+static inline u32 __iwl_read_restricted(const char *f, u32 l,
|
|
+ struct iwl_priv *priv, u32 reg)
|
|
+{
|
|
+ u32 value = _iwl_read_restricted(priv, reg);
|
|
+ if (!atomic_read(&priv->restrict_refcnt))
|
|
+ IWL_ERROR("Unrestricted access from %s %d\n", f, l);
|
|
+ IWL_DEBUG_IO("read_restricted(0x%4X) = 0x%08x - %s %d \n", reg, value,
|
|
+ f, l);
|
|
+ return value;
|
|
+}
|
|
+#define iwl_read_restricted(priv, reg) \
|
|
+ __iwl_read_restricted(__FILE__, __LINE__, priv, reg)
|
|
+#else
|
|
+#define iwl_read_restricted _iwl_read_restricted
|
|
+#endif
|
|
+
|
|
+static inline void _iwl_write_restricted(struct iwl_priv *priv,
|
|
+ u32 reg, u32 value)
|
|
+{
|
|
+ _iwl_write32(priv, reg, value);
|
|
+}
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+static void __iwl_write_restricted(u32 line,
|
|
+ struct iwl_priv *priv, u32 reg, u32 value)
|
|
+{
|
|
+ if (!atomic_read(&priv->restrict_refcnt))
|
|
+ IWL_ERROR("Unrestricted access from line %d\n", line);
|
|
+ _iwl_write_restricted(priv, reg, value);
|
|
+}
|
|
+#define iwl_write_restricted(priv, reg, value) \
|
|
+ __iwl_write_restricted(__LINE__, priv, reg, value)
|
|
+#else
|
|
+#define iwl_write_restricted _iwl_write_restricted
|
|
+#endif
|
|
+
|
|
+static inline void iwl_write_buffer_restricted(struct iwl_priv *priv,
|
|
+ u32 reg, u32 len, u32 *values)
|
|
+{
|
|
+ u32 count = sizeof(u32);
|
|
+
|
|
+ if ((priv != NULL) && (values != NULL)) {
|
|
+ for (; 0 < len; len -= count, reg += count, values++)
|
|
+ _iwl_write_restricted(priv, reg, *values);
|
|
+ }
|
|
+}
|
|
+
|
|
+static inline int _iwl_poll_restricted_bit(struct iwl_priv *priv,
|
|
+ u32 addr, u32 mask, int timeout)
|
|
+{
|
|
+ int i = 0;
|
|
+
|
|
+ do {
|
|
+ if ((_iwl_read_restricted(priv, addr) & mask) == mask)
|
|
+ return i;
|
|
+ mdelay(10);
|
|
+ i += 10;
|
|
+ } while (i < timeout);
|
|
+
|
|
+ return -ETIMEDOUT;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+static inline int __iwl_poll_restricted_bit(const char *f, u32 l,
|
|
+ struct iwl_priv *priv,
|
|
+ u32 addr, u32 mask, int timeout)
|
|
+{
|
|
+ int rc = _iwl_poll_restricted_bit(priv, addr, mask, timeout);
|
|
+
|
|
+ if (unlikely(rc == -ETIMEDOUT))
|
|
+ IWL_DEBUG_IO("poll_restricted_bit(0x%08X, 0x%08X) - "
|
|
+ "timedout - %s %d\n", addr, mask, f, l);
|
|
+ else
|
|
+ IWL_DEBUG_IO("poll_restricted_bit(0x%08X, 0x%08X) = 0x%08X "
|
|
+ "- %s %d\n", addr, mask, rc, f, l);
|
|
+ return rc;
|
|
+}
|
|
+#define iwl_poll_restricted_bit(ipw, addr, mask, timeout) \
|
|
+ __iwl_poll_restricted_bit(__FILE__, __LINE__, ipw, addr, mask, timeout)
|
|
+#else
|
|
+#define iwl_poll_restricted_bit _iwl_poll_restricted_bit
|
|
+#endif
|
|
+
|
|
+static inline u32 _iwl_read_restricted_reg(struct iwl_priv *priv, u32 reg)
|
|
+{
|
|
+ _iwl_write_restricted(priv, HBUS_TARG_PRPH_RADDR, reg | (3 << 24));
|
|
+ return _iwl_read_restricted(priv, HBUS_TARG_PRPH_RDAT);
|
|
+}
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+static inline u32 __iwl_read_restricted_reg(u32 line,
|
|
+ struct iwl_priv *priv, u32 reg)
|
|
+{
|
|
+ if (!atomic_read(&priv->restrict_refcnt))
|
|
+ IWL_ERROR("Unrestricted access from line %d\n", line);
|
|
+ return _iwl_read_restricted_reg(priv, reg);
|
|
+}
|
|
+
|
|
+#define iwl_read_restricted_reg(priv, reg) \
|
|
+ __iwl_read_restricted_reg(__LINE__, priv, reg)
|
|
+#else
|
|
+#define iwl_read_restricted_reg _iwl_read_restricted_reg
|
|
+#endif
|
|
+
|
|
+static inline void _iwl_write_restricted_reg(struct iwl_priv *priv,
|
|
+ u32 addr, u32 val)
|
|
+{
|
|
+ _iwl_write_restricted(priv, HBUS_TARG_PRPH_WADDR,
|
|
+ ((addr & 0x0000FFFF) | (3 << 24)));
|
|
+ _iwl_write_restricted(priv, HBUS_TARG_PRPH_WDAT, val);
|
|
+}
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+static inline void __iwl_write_restricted_reg(u32 line,
|
|
+ struct iwl_priv *priv,
|
|
+ u32 addr, u32 val)
|
|
+{
|
|
+ if (!atomic_read(&priv->restrict_refcnt))
|
|
+ IWL_ERROR("Unrestricted access from line %d\n", line);
|
|
+ _iwl_write_restricted_reg(priv, addr, val);
|
|
+}
|
|
+
|
|
+#define iwl_write_restricted_reg(priv, addr, val) \
|
|
+ __iwl_write_restricted_reg(__LINE__, priv, addr, val);
|
|
+#else
|
|
+#define iwl_write_restricted_reg _iwl_write_restricted_reg
|
|
+#endif
|
|
+
|
|
+#define _iwl_set_bits_restricted_reg(priv, reg, mask) \
|
|
+ _iwl_write_restricted_reg(priv, reg, \
|
|
+ (_iwl_read_restricted_reg(priv, reg) | mask))
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+static inline void __iwl_set_bits_restricted_reg(u32 line, struct iwl_priv
|
|
+ *priv, u32 reg, u32 mask)
|
|
+{
|
|
+ if (!atomic_read(&priv->restrict_refcnt))
|
|
+ IWL_ERROR("Unrestricted access from line %d\n", line);
|
|
+ _iwl_set_bits_restricted_reg(priv, reg, mask);
|
|
+}
|
|
+#define iwl_set_bits_restricted_reg(priv, reg, mask) \
|
|
+ __iwl_set_bits_restricted_reg(__LINE__, priv, reg, mask)
|
|
+#else
|
|
+#define iwl_set_bits_restricted_reg _iwl_set_bits_restricted_reg
|
|
+#endif
|
|
+
|
|
+#define _iwl_set_bits_mask_restricted_reg(priv, reg, bits, mask) \
|
|
+ _iwl_write_restricted_reg( \
|
|
+ priv, reg, ((_iwl_read_restricted_reg(priv, reg) & mask) | bits))
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+static inline void __iwl_set_bits_mask_restricted_reg(u32 line,
|
|
+ struct iwl_priv *priv, u32 reg, u32 bits, u32 mask)
|
|
+{
|
|
+ if (!atomic_read(&priv->restrict_refcnt))
|
|
+ IWL_ERROR("Unrestricted access from line %d\n", line);
|
|
+ _iwl_set_bits_mask_restricted_reg(priv, reg, bits, mask);
|
|
+}
|
|
+
|
|
+#define iwl_set_bits_mask_restricted_reg(priv, reg, bits, mask) \
|
|
+ __iwl_set_bits_mask_restricted_reg(__LINE__, priv, reg, bits, mask)
|
|
+#else
|
|
+#define iwl_set_bits_mask_restricted_reg _iwl_set_bits_mask_restricted_reg
|
|
+#endif
|
|
+
|
|
+static inline void iwl_clear_bits_restricted_reg(struct iwl_priv
|
|
+ *priv, u32 reg, u32 mask)
|
|
+{
|
|
+ u32 val = _iwl_read_restricted_reg(priv, reg);
|
|
+ _iwl_write_restricted_reg(priv, reg, (val & ~mask));
|
|
+}
|
|
+
|
|
+static inline u32 iwl_read_restricted_mem(struct iwl_priv *priv, u32 addr)
|
|
+{
|
|
+ iwl_write_restricted(priv, HBUS_TARG_MEM_RADDR, addr);
|
|
+ return iwl_read_restricted(priv, HBUS_TARG_MEM_RDAT);
|
|
+}
|
|
+
|
|
+static inline void iwl_write_restricted_mem(struct iwl_priv *priv, u32 addr,
|
|
+ u32 val)
|
|
+{
|
|
+ iwl_write_restricted(priv, HBUS_TARG_MEM_WADDR, addr);
|
|
+ iwl_write_restricted(priv, HBUS_TARG_MEM_WDAT, val);
|
|
+}
|
|
+
|
|
+static inline void iwl_write_restricted_mems(struct iwl_priv *priv, u32 addr,
|
|
+ u32 len, u32 *values)
|
|
+{
|
|
+ iwl_write_restricted(priv, HBUS_TARG_MEM_WADDR, addr);
|
|
+ for (; 0 < len; len -= sizeof(u32), values++)
|
|
+ iwl_write_restricted(priv, HBUS_TARG_MEM_WDAT, *values);
|
|
+}
|
|
+
|
|
+static inline void iwl_write_restricted_regs(struct iwl_priv *priv, u32 reg,
|
|
+ u32 len, u8 *values)
|
|
+{
|
|
+ u32 reg_offset = reg;
|
|
+ u32 aligment = reg & 0x3;
|
|
+
|
|
+ /* write any non-dword-aligned stuff at the beginning */
|
|
+ if (len < sizeof(u32)) {
|
|
+ if ((aligment + len) <= sizeof(u32)) {
|
|
+ u8 size;
|
|
+ u32 value = 0;
|
|
+ size = len - 1;
|
|
+ memcpy(&value, values, len);
|
|
+ reg_offset = (reg_offset & 0x0000FFFF);
|
|
+
|
|
+ _iwl_write_restricted(priv,
|
|
+ HBUS_TARG_PRPH_WADDR,
|
|
+ (reg_offset | (size << 24)));
|
|
+ _iwl_write_restricted(priv, HBUS_TARG_PRPH_WDAT,
|
|
+ value);
|
|
+ }
|
|
+
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* now write all the dword-aligned stuff */
|
|
+ for (; reg_offset < (reg + len);
|
|
+ reg_offset += sizeof(u32), values += sizeof(u32))
|
|
+ _iwl_write_restricted_reg(priv, reg_offset, *((u32 *) values));
|
|
+}
|
|
+
|
|
+/**
|
|
+ * iwl_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer pointer.
|
|
+ *
|
|
+ * NOTE: This function has 3945 and 4965 specific code paths in it.
|
|
+ */
|
|
+static inline __le32 iwl_dma_addr2rbd_ptr(struct iwl_priv *priv,
|
|
+ dma_addr_t dma_addr)
|
|
+{
|
|
+#if IWL == 3945
|
|
+ return cpu_to_le32((u32)dma_addr);
|
|
+#elif IWL == 4965
|
|
+ return cpu_to_le32((u32)(dma_addr >> 8));
|
|
+#endif
|
|
+}
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/net/wireless/iwl-priv.h b/drivers/net/wireless/iwl-priv.h
|
|
new file mode 100644
|
|
index 0000000..c9cb11e
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/iwl-priv.h
|
|
@@ -0,0 +1,307 @@
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * Copyright(c) 2003 - 2007 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.
|
|
+ *
|
|
+ * Contact Information:
|
|
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
|
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+#ifndef __iwl_priv_h__
|
|
+#define __iwl_priv_h__
|
|
+
|
|
+#include <linux/workqueue.h>
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_SPECTRUM_MEASUREMENT
|
|
+
|
|
+enum {
|
|
+ MEASUREMENT_READY = (1 << 0),
|
|
+ MEASUREMENT_ACTIVE = (1 << 1),
|
|
+};
|
|
+
|
|
+#endif
|
|
+
|
|
+struct iwl_priv {
|
|
+
|
|
+ /* ieee device used by generic ieee processing code */
|
|
+ struct ieee80211_hw *hw;
|
|
+ struct ieee80211_channel *ieee_channels;
|
|
+ struct ieee80211_rate *ieee_rates;
|
|
+
|
|
+ /* temporary frame storage list */
|
|
+ struct list_head free_frames;
|
|
+ int frames_count;
|
|
+
|
|
+ u8 phymode;
|
|
+ int alloc_rxb_skb;
|
|
+
|
|
+ void (*rx_handlers[REPLY_MAX])(struct iwl_priv *priv,
|
|
+ struct iwl_rx_mem_buffer *rxb);
|
|
+
|
|
+ const struct ieee80211_hw_mode *modes;
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_SPECTRUM_MEASUREMENT
|
|
+ /* spectrum measurement report caching */
|
|
+ struct iwl_spectrum_notification measure_report;
|
|
+ u8 measurement_status;
|
|
+#endif
|
|
+ /* ucode beacon time */
|
|
+ u32 ucode_beacon_time;
|
|
+
|
|
+ /* we allocate array of iwl_channel_info for NIC's valid channels.
|
|
+ * Access via channel # using indirect index array */
|
|
+ struct iwl_channel_info *channel_info; /* channel info array */
|
|
+ u8 channel_count; /* # of channels */
|
|
+
|
|
+ /* each calibration channel group in the EEPROM has a derived
|
|
+ * clip setting for each rate. */
|
|
+ const struct iwl_clip_group clip_groups[5];
|
|
+
|
|
+ /* thermal calibration */
|
|
+ s32 temperature; /* degrees Kelvin */
|
|
+ s32 last_temperature;
|
|
+
|
|
+ /* Scan related variables */
|
|
+ unsigned long last_scan_jiffies;
|
|
+ unsigned long scan_start;
|
|
+ unsigned long scan_pass_start;
|
|
+ unsigned long scan_start_tsf;
|
|
+ int scan_bands;
|
|
+ int one_direct_scan;
|
|
+ u8 direct_ssid_len;
|
|
+ u8 direct_ssid[IW_ESSID_MAX_SIZE];
|
|
+ struct iwl_scan_cmd *scan;
|
|
+ u8 only_active_channel;
|
|
+
|
|
+ /* spinlock */
|
|
+ spinlock_t lock; /* protect general shared data */
|
|
+ spinlock_t hcmd_lock; /* protect hcmd */
|
|
+ struct mutex mutex;
|
|
+
|
|
+ /* basic pci-network driver stuff */
|
|
+ struct pci_dev *pci_dev;
|
|
+
|
|
+ /* pci hardware address support */
|
|
+ void __iomem *hw_base;
|
|
+
|
|
+ /* uCode images, save to reload in case of failure */
|
|
+ struct fw_image_desc ucode_code; /* runtime inst */
|
|
+ struct fw_image_desc ucode_data; /* runtime data original */
|
|
+ struct fw_image_desc ucode_data_backup; /* runtime data save/restore */
|
|
+ struct fw_image_desc ucode_init; /* initialization inst */
|
|
+ struct fw_image_desc ucode_init_data; /* initialization data */
|
|
+ struct fw_image_desc ucode_boot; /* bootstrap inst */
|
|
+
|
|
+
|
|
+ struct iwl_rxon_time_cmd rxon_timing;
|
|
+
|
|
+ /* We declare this const so it can only be
|
|
+ * changed via explicit cast within the
|
|
+ * routines that actually update the physical
|
|
+ * hardware */
|
|
+ const struct iwl_rxon_cmd active_rxon;
|
|
+ struct iwl_rxon_cmd staging_rxon;
|
|
+
|
|
+ int error_recovering;
|
|
+ struct iwl_rxon_cmd recovery_rxon;
|
|
+
|
|
+ /* 1st responses from initialize and runtime uCode images.
|
|
+ * 4965's initialize alive response contains some calibration data. */
|
|
+ struct iwl_init_alive_resp card_alive_init;
|
|
+ struct iwl_alive_resp card_alive;
|
|
+
|
|
+#ifdef LED
|
|
+ /* LED related variables */
|
|
+ struct iwl_activity_blink activity;
|
|
+ unsigned long led_packets;
|
|
+ int led_state;
|
|
+#endif
|
|
+
|
|
+ u16 active_rate;
|
|
+ u16 active_rate_basic;
|
|
+
|
|
+ u8 call_post_assoc_from_beacon;
|
|
+#if IWL == 4965
|
|
+ u8 use_ant_b_for_management_frame; /* Tx antenna selection */
|
|
+ /* HT variables */
|
|
+ u8 is_dup;
|
|
+ u8 is_ht_enabled;
|
|
+ u8 channel_width; /* 0=20MHZ, 1=40MHZ */
|
|
+ u8 current_channel_width;
|
|
+ u8 valid_antenna; /* Bit mask of antennas actually connected */
|
|
+#ifdef CONFIG_IWLWIFI_SENSITIVITY
|
|
+ struct iwl_sensitivity_data sensitivity_data;
|
|
+ struct iwl_chain_noise_data chain_noise_data;
|
|
+ u8 start_calib;
|
|
+ __le16 sensitivity_tbl[HD_TABLE_SIZE];
|
|
+#endif /*CONFIG_IWLWIFI_SENSITIVITY*/
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+ struct sta_ht_info current_assoc_ht;
|
|
+#endif
|
|
+ u8 active_rate_ht[2];
|
|
+ u8 last_phy_res[100];
|
|
+
|
|
+ /* Rate scaling data */
|
|
+ struct iwl_lq_mngr lq_mngr;
|
|
+#endif
|
|
+
|
|
+ /* Rate scaling data */
|
|
+ s8 data_retry_limit;
|
|
+ u8 retry_rate;
|
|
+
|
|
+ wait_queue_head_t wait_command_queue;
|
|
+
|
|
+ int activity_timer_active;
|
|
+
|
|
+ /* Rx and Tx DMA processing queues */
|
|
+ struct iwl_rx_queue rxq;
|
|
+ struct iwl_tx_queue txq[IWL_MAX_NUM_QUEUES];
|
|
+#if IWL == 4965
|
|
+ unsigned long txq_ctx_active_msk;
|
|
+ struct iwl_kw kw; /* keep warm address */
|
|
+ u32 scd_base_addr; /* scheduler sram base address */
|
|
+#endif
|
|
+
|
|
+ unsigned long status;
|
|
+ u32 config;
|
|
+
|
|
+ int last_rx_rssi; /* From Rx packet statisitics */
|
|
+ int last_rx_noise; /* From beacon statistics */
|
|
+
|
|
+ struct iwl_power_mgr power_data;
|
|
+
|
|
+ struct iwl_notif_statistics statistics;
|
|
+ unsigned long last_statistics_time;
|
|
+
|
|
+ /* context information */
|
|
+ u8 essid[IW_ESSID_MAX_SIZE];
|
|
+ u8 essid_len;
|
|
+ u16 rates_mask;
|
|
+
|
|
+ u32 power_mode;
|
|
+ u32 antenna;
|
|
+ u8 bssid[ETH_ALEN];
|
|
+ u16 rts_threshold;
|
|
+ u8 mac_addr[ETH_ALEN];
|
|
+
|
|
+ /*station table variables */
|
|
+ spinlock_t sta_lock;
|
|
+ u8 num_stations;
|
|
+ struct iwl_station_entry stations[IWL_STATION_COUNT];
|
|
+
|
|
+ /* Indication if ieee80211_ops->open has been called */
|
|
+ int is_open;
|
|
+
|
|
+ u8 mac80211_registered;
|
|
+ int is_abg;
|
|
+
|
|
+ u32 notif_missed_beacons;
|
|
+
|
|
+ /* Rx'd packet timing information */
|
|
+ u32 last_beacon_time;
|
|
+ u64 last_tsf;
|
|
+
|
|
+ /* Duplicate packet detection */
|
|
+ u16 last_seq_num;
|
|
+ u16 last_frag_num;
|
|
+ unsigned long last_packet_time;
|
|
+ struct list_head ibss_mac_hash[IWL_IBSS_MAC_HASH_SIZE];
|
|
+
|
|
+ /* eeprom */
|
|
+ struct iwl_eeprom eeprom;
|
|
+
|
|
+ int iw_mode;
|
|
+
|
|
+ struct sk_buff *ibss_beacon;
|
|
+
|
|
+ /* Last Rx'd beacon timestamp */
|
|
+ u32 timestamp0;
|
|
+ u32 timestamp1;
|
|
+ u16 beacon_int;
|
|
+ struct iwl_driver_hw_info hw_setting;
|
|
+ int interface_id;
|
|
+
|
|
+ /* Current association information needed to configure the
|
|
+ * hardware */
|
|
+ u16 assoc_id;
|
|
+ u16 assoc_capability;
|
|
+ u8 ps_mode;
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_QOS
|
|
+ struct iwl_qos_info qos_data;
|
|
+#endif /*CONFIG_IWLWIFI_QOS */
|
|
+
|
|
+ struct workqueue_struct *workqueue;
|
|
+
|
|
+ struct work_struct up;
|
|
+ struct work_struct restart;
|
|
+ struct work_struct calibrated_work;
|
|
+ struct work_struct scan_completed;
|
|
+ struct work_struct rx_replenish;
|
|
+ struct work_struct rf_kill;
|
|
+ struct work_struct abort_scan;
|
|
+ struct work_struct update_link_led;
|
|
+ struct work_struct auth_work;
|
|
+ struct work_struct report_work;
|
|
+ struct work_struct request_scan;
|
|
+ struct work_struct beacon_update;
|
|
+
|
|
+ struct tasklet_struct irq_tasklet;
|
|
+
|
|
+ struct delayed_work init_alive_start;
|
|
+ struct delayed_work alive_start;
|
|
+ struct delayed_work activity_timer;
|
|
+ struct delayed_work thermal_periodic;
|
|
+ struct delayed_work gather_stats;
|
|
+ struct delayed_work scan_check;
|
|
+ struct delayed_work post_associate;
|
|
+
|
|
+#define IWL_DEFAULT_TX_POWER 0x0F
|
|
+ s8 user_txpower_limit;
|
|
+ s8 max_channel_txpower_limit;
|
|
+ u32 cck_power_index_compensation;
|
|
+
|
|
+#ifdef CONFIG_PM
|
|
+ u32 pm_state[16];
|
|
+#endif
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+ /* debugging info */
|
|
+ u32 framecnt_to_us;
|
|
+ atomic_t restrict_refcnt;
|
|
+#endif
|
|
+
|
|
+#if IWL == 4965
|
|
+ struct work_struct txpower_work;
|
|
+#ifdef CONFIG_IWLWIFI_SENSITIVITY
|
|
+ struct work_struct sensitivity_work;
|
|
+#endif
|
|
+ struct work_struct statistics_work;
|
|
+ struct timer_list statistics_periodic;
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+ struct work_struct agg_work;
|
|
+#endif
|
|
+
|
|
+#endif /* 4965 */
|
|
+}; /*iwl_priv */
|
|
+
|
|
+#endif /* __iwl_priv_h__ */
|
|
diff --git a/drivers/net/wireless/iwl-spectrum.h b/drivers/net/wireless/iwl-spectrum.h
|
|
new file mode 100644
|
|
index 0000000..b576ff2
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/iwl-spectrum.h
|
|
@@ -0,0 +1,91 @@
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * Copyright(c) 2003 - 2007 Intel Corporation. All rights reserved.
|
|
+ *
|
|
+ * Portions of this file are derived from the ieee80211 subsystem header files.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify it
|
|
+ * under the terms of version 2 of the GNU General Public License as
|
|
+ * published by the Free Software Foundation.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful, but WITHOUT
|
|
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
+ * more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License along with
|
|
+ * this program; if not, write to the Free Software Foundation, Inc.,
|
|
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
|
|
+ *
|
|
+ * The full GNU General Public License is included in this distribution in the
|
|
+ * file called LICENSE.
|
|
+ *
|
|
+ * Contact Information:
|
|
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
|
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+#ifndef __iwl_spectrum_h__
|
|
+#define __iwl_spectrum_h__
|
|
+enum { /* ieee80211_basic_report.map */
|
|
+ IEEE80211_BASIC_MAP_BSS = (1 << 0),
|
|
+ IEEE80211_BASIC_MAP_OFDM = (1 << 1),
|
|
+ IEEE80211_BASIC_MAP_UNIDENTIFIED = (1 << 2),
|
|
+ IEEE80211_BASIC_MAP_RADAR = (1 << 3),
|
|
+ IEEE80211_BASIC_MAP_UNMEASURED = (1 << 4),
|
|
+ /* Bits 5-7 are reserved */
|
|
+
|
|
+};
|
|
+struct ieee80211_basic_report {
|
|
+ u8 channel;
|
|
+ __le64 start_time;
|
|
+ __le16 duration;
|
|
+ u8 map;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+enum { /* ieee80211_measurement_request.mode */
|
|
+ /* Bit 0 is reserved */
|
|
+ IEEE80211_MEASUREMENT_ENABLE = (1 << 1),
|
|
+ IEEE80211_MEASUREMENT_REQUEST = (1 << 2),
|
|
+ IEEE80211_MEASUREMENT_REPORT = (1 << 3),
|
|
+ /* Bits 4-7 are reserved */
|
|
+};
|
|
+
|
|
+enum {
|
|
+ IEEE80211_REPORT_BASIC = 0, /* required */
|
|
+ IEEE80211_REPORT_CCA = 1, /* optional */
|
|
+ IEEE80211_REPORT_RPI = 2, /* optional */
|
|
+ /* 3-255 reserved */
|
|
+};
|
|
+
|
|
+struct ieee80211_measurement_params {
|
|
+ u8 channel;
|
|
+ __le64 start_time;
|
|
+ __le16 duration;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct ieee80211_info_element {
|
|
+ u8 id;
|
|
+ u8 len;
|
|
+ u8 data[0];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct ieee80211_measurement_request {
|
|
+ struct ieee80211_info_element ie;
|
|
+ u8 token;
|
|
+ u8 mode;
|
|
+ u8 type;
|
|
+ struct ieee80211_measurement_params params[0];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct ieee80211_measurement_report {
|
|
+ struct ieee80211_info_element ie;
|
|
+ u8 token;
|
|
+ u8 mode;
|
|
+ u8 type;
|
|
+ union {
|
|
+ struct ieee80211_basic_report basic[0];
|
|
+ } u;
|
|
+} __attribute__ ((packed));
|
|
+#endif
|
|
diff --git a/drivers/net/wireless/iwlwifi.h b/drivers/net/wireless/iwlwifi.h
|
|
new file mode 100644
|
|
index 0000000..fcf0197
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/iwlwifi.h
|
|
@@ -0,0 +1,714 @@
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * Copyright(c) 2003 - 2007 Intel Corporation. All rights reserved.
|
|
+ *
|
|
+ * Portions of this file are derived from the ipw3945 project, as well
|
|
+ * as portions of the ieee80211 subsystem header files.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify it
|
|
+ * under the terms of version 2 of the GNU General Public License as
|
|
+ * published by the Free Software Foundation.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful, but WITHOUT
|
|
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
+ * more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License along with
|
|
+ * this program; if not, write to the Free Software Foundation, Inc.,
|
|
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
|
|
+ *
|
|
+ * The full GNU General Public License is included in this distribution in the
|
|
+ * file called LICENSE.
|
|
+ *
|
|
+ * Contact Information:
|
|
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
|
|
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+#ifndef __iwlwifi_h__
|
|
+#define __iwlwifi_h__
|
|
+
|
|
+#include <linux/pci.h> /* for struct pci_device_id */
|
|
+#include <linux/kernel.h>
|
|
+#include <net/ieee80211_radiotap.h>
|
|
+
|
|
+struct iwl_priv;
|
|
+
|
|
+/* Hardware specific file defines the PCI IDs table for that hardware module */
|
|
+extern struct pci_device_id iwl_hw_card_ids[];
|
|
+
|
|
+#if IWL == 3945
|
|
+#define DRV_NAME "iwl3945"
|
|
+#elif IWL == 4965
|
|
+#define DRV_NAME "iwl4965"
|
|
+#endif
|
|
+
|
|
+#include "iwl-hw.h"
|
|
+
|
|
+/*
|
|
+ * Driver implementation data structures, constants, inline
|
|
+ * functions
|
|
+ *
|
|
+ * NOTE: DO NOT PUT HARDWARE/UCODE SPECIFIC DECLRATIONS HERE
|
|
+ *
|
|
+ * Hardware specific declrations go into iwl-*hw.h
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "iwl-debug.h"
|
|
+
|
|
+/* Default noise level to report when noise measurement is not available.
|
|
+ * This may be because we're:
|
|
+ * 1) Not associated (4965, no beacon statistics being sent to driver)
|
|
+ * 2) Scanning (noise measurement does not apply to associated channel)
|
|
+ * 3) Receiving CCK (3945 delivers noise info only for OFDM frames)
|
|
+ * Use default noise value of -127 ... this is below the range of measurable
|
|
+ * Rx dBm for either 3945 or 4965, so it can indicate "unmeasurable" to user.
|
|
+ * Also, -127 works better than 0 when averaging frames with/without
|
|
+ * noise info (e.g. averaging might be done in app); measured dBm values are
|
|
+ * always negative ... using a negative value as the default keeps all
|
|
+ * averages within an s8's (used in some apps) range of negative values. */
|
|
+#define IWL_NOISE_MEAS_NOT_AVAILABLE (-127)
|
|
+
|
|
+/* Module parameters accessible from iwl-*.c */
|
|
+extern int iwl_param_disable_hw_scan;
|
|
+extern int iwl_param_debug;
|
|
+extern int iwl_param_mode;
|
|
+extern int iwl_param_disable;
|
|
+extern int iwl_param_antenna;
|
|
+extern int iwl_param_hwcrypto;
|
|
+extern int iwl_param_qos_enable;
|
|
+extern int iwl_param_queues_num;
|
|
+
|
|
+enum iwl_antenna {
|
|
+ IWL_ANTENNA_DIVERSITY,
|
|
+ IWL_ANTENNA_MAIN,
|
|
+ IWL_ANTENNA_AUX
|
|
+};
|
|
+
|
|
+/*
|
|
+ * RTS threshold here is total size [2347] minus 4 FCS bytes
|
|
+ * Per spec:
|
|
+ * a value of 0 means RTS on all data/management packets
|
|
+ * a value > max MSDU size means no RTS
|
|
+ * else RTS for data/management frames where MPDU is larger
|
|
+ * than RTS value.
|
|
+ */
|
|
+#define DEFAULT_RTS_THRESHOLD 2347U
|
|
+#define MIN_RTS_THRESHOLD 0U
|
|
+#define MAX_RTS_THRESHOLD 2347U
|
|
+#define MAX_MSDU_SIZE 2304U
|
|
+#define MAX_MPDU_SIZE 2346U
|
|
+#define DEFAULT_BEACON_INTERVAL 100U
|
|
+#define DEFAULT_SHORT_RETRY_LIMIT 7U
|
|
+#define DEFAULT_LONG_RETRY_LIMIT 4U
|
|
+
|
|
+struct iwl_rx_mem_buffer {
|
|
+ dma_addr_t dma_addr;
|
|
+ struct sk_buff *skb;
|
|
+ struct list_head list;
|
|
+};
|
|
+
|
|
+struct iwl_rt_rx_hdr {
|
|
+ struct ieee80211_radiotap_header rt_hdr;
|
|
+ __le64 rt_tsf; /* TSF */
|
|
+ u8 rt_flags; /* radiotap packet flags */
|
|
+ u8 rt_rate; /* rate in 500kb/s */
|
|
+ __le16 rt_channelMHz; /* channel in MHz */
|
|
+ __le16 rt_chbitmask; /* channel bitfield */
|
|
+ s8 rt_dbmsignal; /* signal in dBm, kluged to signed */
|
|
+ s8 rt_dbmnoise;
|
|
+ u8 rt_antenna; /* antenna number */
|
|
+ u8 payload[0]; /* payload... */
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct iwl_rt_tx_hdr {
|
|
+ struct ieee80211_radiotap_header rt_hdr;
|
|
+ u8 rt_rate; /* rate in 500kb/s */
|
|
+ __le16 rt_channel; /* channel in mHz */
|
|
+ __le16 rt_chbitmask; /* channel bitfield */
|
|
+ s8 rt_dbmsignal; /* signal in dBm, kluged to signed */
|
|
+ u8 rt_antenna; /* antenna number */
|
|
+ u8 payload[0]; /* payload... */
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * Generic queue structure
|
|
+ *
|
|
+ * Contains common data for Rx and Tx queues
|
|
+ */
|
|
+struct iwl_queue {
|
|
+ int n_bd; /* number of BDs in this queue */
|
|
+ int first_empty; /* 1-st empty entry (index) host_w*/
|
|
+ int last_used; /* last used entry (index) host_r*/
|
|
+ dma_addr_t dma_addr; /* physical addr for BD's */
|
|
+ int n_window; /* safe queue window */
|
|
+ u32 id;
|
|
+ int low_mark; /* low watermark, resume queue if free
|
|
+ * space more than this */
|
|
+ int high_mark; /* high watermark, stop queue if free
|
|
+ * space less than this */
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+#define MAX_NUM_OF_TBS (20)
|
|
+
|
|
+struct iwl_tx_info {
|
|
+ struct ieee80211_tx_status status;
|
|
+ struct sk_buff *skb[MAX_NUM_OF_TBS];
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct iwl_tx_queue - Tx Queue for DMA
|
|
+ * @need_update: need to update read/write index
|
|
+ * @shed_retry: queue is HT AGG enabled
|
|
+ *
|
|
+ * Queue consists of circular buffer of BD's and required locking structures.
|
|
+ */
|
|
+struct iwl_tx_queue {
|
|
+ struct iwl_queue q;
|
|
+ struct iwl_tfd_frame *bd;
|
|
+ struct iwl_cmd *cmd;
|
|
+ dma_addr_t dma_addr_cmd;
|
|
+ struct iwl_tx_info *txb;
|
|
+ int need_update;
|
|
+ int sched_retry;
|
|
+ int active;
|
|
+};
|
|
+
|
|
+#include "iwl-channel.h"
|
|
+
|
|
+#if IWL == 3945
|
|
+#include "iwl-3945-rs.h"
|
|
+#else
|
|
+#include "iwl-4965-rs.h"
|
|
+#endif
|
|
+
|
|
+#define IWL_TX_QUEUE_AC0 0
|
|
+#define IWL_TX_QUEUE_AC1 1
|
|
+#define IWL_TX_QUEUE_AC2 2
|
|
+#define IWL_TX_QUEUE_AC3 3
|
|
+#define IWL_TX_QUEUE_HCCA_1 5
|
|
+#define IWL_TX_QUEUE_HCCA_2 6
|
|
+#define IWL_TX_QUEUE_NONE 7
|
|
+#define IWL_MIN_NUM_QUEUES 4
|
|
+
|
|
+/* Power management (not Tx power) structures */
|
|
+
|
|
+struct iwl_power_vec_entry {
|
|
+ struct iwl_powertable_cmd cmd;
|
|
+ u8 no_dtim;
|
|
+};
|
|
+#define IWL_POWER_RANGE_0 (0)
|
|
+#define IWL_POWER_RANGE_1 (1)
|
|
+
|
|
+#define IWL_POWER_MODE_CAM 0x00 /* Continuously Aware Mode, always on */
|
|
+#define IWL_POWER_INDEX_3 0x03
|
|
+#define IWL_POWER_INDEX_5 0x05
|
|
+#define IWL_POWER_AC 0x06
|
|
+#define IWL_POWER_BATTERY 0x07
|
|
+#define IWL_POWER_LIMIT 0x07
|
|
+#define IWL_POWER_MASK 0x0F
|
|
+#define IWL_POWER_ENABLED 0x10
|
|
+#define IWL_POWER_LEVEL(x) ((x) & IWL_POWER_MASK)
|
|
+
|
|
+struct iwl_power_mgr {
|
|
+ spinlock_t lock;
|
|
+ struct iwl_power_vec_entry pwr_range_0[IWL_POWER_AC];
|
|
+ struct iwl_power_vec_entry pwr_range_1[IWL_POWER_AC];
|
|
+ u8 active_index;
|
|
+ u32 dtim_val;
|
|
+};
|
|
+
|
|
+#define IEEE80211_DATA_LEN 2304
|
|
+#define IEEE80211_4ADDR_LEN 30
|
|
+#define IEEE80211_HLEN (IEEE80211_4ADDR_LEN)
|
|
+#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN)
|
|
+
|
|
+struct iwl_frame {
|
|
+ union {
|
|
+ struct ieee80211_hdr frame;
|
|
+ struct iwl_tx_beacon_cmd beacon;
|
|
+ u8 raw[IEEE80211_FRAME_LEN];
|
|
+ u8 cmd[360];
|
|
+ } u;
|
|
+ struct list_head list;
|
|
+};
|
|
+
|
|
+#define SEQ_TO_QUEUE(x) ((x >> 8) & 0xbf)
|
|
+#define QUEUE_TO_SEQ(x) ((x & 0xbf) << 8)
|
|
+#define SEQ_TO_INDEX(x) (x & 0xff)
|
|
+#define INDEX_TO_SEQ(x) (x & 0xff)
|
|
+#define SEQ_HUGE_FRAME (0x4000)
|
|
+#define SEQ_RX_FRAME __constant_cpu_to_le16(0x8000)
|
|
+#define SEQ_TO_SN(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4)
|
|
+#define SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ)
|
|
+#define MAX_SN ((IEEE80211_SCTL_SEQ) >> 4)
|
|
+
|
|
+enum {
|
|
+ /* CMD_SIZE_NORMAL = 0, */
|
|
+ CMD_SIZE_HUGE = (1 << 0),
|
|
+ /* CMD_SYNC = 0, */
|
|
+ CMD_ASYNC = (1 << 1),
|
|
+ /* CMD_NO_SKB = 0, */
|
|
+ CMD_WANT_SKB = (1 << 2),
|
|
+};
|
|
+
|
|
+struct iwl_cmd;
|
|
+struct iwl_priv;
|
|
+
|
|
+struct iwl_cmd_meta {
|
|
+ struct iwl_cmd_meta *source;
|
|
+ union {
|
|
+ struct sk_buff *skb;
|
|
+ int (*callback)(struct iwl_priv *priv,
|
|
+ struct iwl_cmd *cmd, struct sk_buff *skb);
|
|
+ } __attribute__ ((packed)) u;
|
|
+
|
|
+ u16 len;
|
|
+
|
|
+ /* The CMD_SIZE_HUGE flag bit indicates that the command
|
|
+ * structure is stored at the end of the shared queue memory. */
|
|
+ u8 flags;
|
|
+
|
|
+ u8 token;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct iwl_cmd {
|
|
+ struct iwl_cmd_meta meta;
|
|
+ struct iwl_cmd_header hdr;
|
|
+ union {
|
|
+ struct iwl_addsta_cmd addsta;
|
|
+ struct iwl_led_cmd led;
|
|
+ u32 flags;
|
|
+ u8 val8;
|
|
+ u16 val16;
|
|
+ u32 val32;
|
|
+ struct iwl_bt_cmd bt;
|
|
+ struct iwl_rxon_time_cmd rxon_time;
|
|
+ struct iwl_powertable_cmd powertable;
|
|
+ struct iwl_qosparam_cmd qosparam;
|
|
+ struct iwl_tx_cmd tx;
|
|
+ struct iwl_tx_beacon_cmd tx_beacon;
|
|
+ struct iwl_rxon_assoc_cmd rxon_assoc;
|
|
+ u8 *indirect;
|
|
+ u8 payload[360];
|
|
+ } __attribute__ ((packed)) cmd;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct iwl_host_cmd {
|
|
+ u8 id;
|
|
+ u16 len;
|
|
+ struct iwl_cmd_meta meta;
|
|
+ const void *data;
|
|
+};
|
|
+
|
|
+#define TFD_MAX_PAYLOAD_SIZE (sizeof(struct iwl_cmd) - \
|
|
+ sizeof(struct iwl_cmd_meta))
|
|
+
|
|
+/*
|
|
+ * RX related structures and functions
|
|
+ */
|
|
+#define RX_FREE_BUFFERS 64
|
|
+#define RX_LOW_WATERMARK 8
|
|
+
|
|
+#define SUP_RATE_11A_MAX_NUM_CHANNELS 8
|
|
+#define SUP_RATE_11B_MAX_NUM_CHANNELS 4
|
|
+#define SUP_RATE_11G_MAX_NUM_CHANNELS 12
|
|
+
|
|
+/**
|
|
+ * struct iwl_rx_queue - Rx queue
|
|
+ * @processed: Internal index to last handled Rx packet
|
|
+ * @read: Shared index to newest available Rx buffer
|
|
+ * @write: Shared index to oldest written Rx packet
|
|
+ * @free_count: Number of pre-allocated buffers in rx_free
|
|
+ * @rx_free: list of free SKBs for use
|
|
+ * @rx_used: List of Rx buffers with no SKB
|
|
+ * @need_update: flag to indicate we need to update read/write index
|
|
+ *
|
|
+ * NOTE: rx_free and rx_used are used as a FIFO for iwl_rx_mem_buffers
|
|
+ */
|
|
+struct iwl_rx_queue {
|
|
+ __le32 *bd;
|
|
+ dma_addr_t dma_addr;
|
|
+ struct iwl_rx_mem_buffer pool[RX_QUEUE_SIZE + RX_FREE_BUFFERS];
|
|
+ struct iwl_rx_mem_buffer *queue[RX_QUEUE_SIZE];
|
|
+ u32 processed;
|
|
+ u32 read;
|
|
+ u32 write;
|
|
+ u32 free_count;
|
|
+ struct list_head rx_free;
|
|
+ struct list_head rx_used;
|
|
+ int need_update;
|
|
+ spinlock_t lock;
|
|
+};
|
|
+
|
|
+#define IWL_SUPPORTED_RATES_IE_LEN 8
|
|
+
|
|
+#define SCAN_INTERVAL 100
|
|
+
|
|
+#define MAX_A_CHANNELS 252
|
|
+#define MIN_A_CHANNELS 7
|
|
+
|
|
+#define MAX_B_CHANNELS 14
|
|
+#define MIN_B_CHANNELS 1
|
|
+
|
|
+#define STATUS_HCMD_ACTIVE 0 /* host command in progress */
|
|
+#define STATUS_INT_ENABLED 1
|
|
+#define STATUS_RF_KILL_HW 2
|
|
+#define STATUS_RF_KILL_SW 3
|
|
+#define STATUS_INIT 4
|
|
+#define STATUS_ALIVE 5
|
|
+#define STATUS_READY 6
|
|
+#define STATUS_TEMPERATURE 7
|
|
+#define STATUS_GEO_CONFIGURED 8
|
|
+#define STATUS_EXIT_PENDING 9
|
|
+#define STATUS_IN_SUSPEND 10
|
|
+#define STATUS_STATISTICS 11
|
|
+#define STATUS_SCANNING 12
|
|
+#define STATUS_SCAN_ABORTING 13
|
|
+#define STATUS_SCAN_HW 14
|
|
+#define STATUS_POWER_PMI 15
|
|
+#define STATUS_FW_ERROR 16
|
|
+
|
|
+/*TODO need to support adding adhoc station MAX_STATION should be 25 */
|
|
+#define IWL_INVALID_STATION (0xff)
|
|
+
|
|
+#define MAX_TID_COUNT 9
|
|
+
|
|
+#define IWL_INVALID_RATE 0xFF
|
|
+#define IWL_INVALID_VALUE -1
|
|
+
|
|
+#if IWL == 4965
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+struct iwl_ht_agg {
|
|
+ u16 txq_id;
|
|
+ u16 frame_count;
|
|
+ u16 wait_for_ba;
|
|
+ u16 start_idx;
|
|
+ u32 bitmap0;
|
|
+ u32 bitmap1;
|
|
+ u32 rate_n_flags;
|
|
+};
|
|
+#endif /* CONFIG_IWLWIFI_HT_AGG */
|
|
+#endif /* CONFIG_IWLWIFI_HT */
|
|
+#endif
|
|
+
|
|
+struct iwl_tid_data {
|
|
+ u16 seq_number;
|
|
+#if IWL == 4965
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+#ifdef CONFIG_IWLWIFI_HT_AGG
|
|
+ struct iwl_ht_agg agg;
|
|
+#endif /* CONFIG_IWLWIFI_HT_AGG */
|
|
+#endif /* CONFIG_IWLWIFI_HT */
|
|
+#endif
|
|
+};
|
|
+
|
|
+struct iwl_hw_key {
|
|
+ ieee80211_key_alg alg;
|
|
+ int keylen;
|
|
+ u8 key[32];
|
|
+};
|
|
+
|
|
+union iwl_ht_rate_supp {
|
|
+ u16 rates;
|
|
+ struct {
|
|
+ u8 siso_rate;
|
|
+ u8 mimo_rate;
|
|
+ };
|
|
+};
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_HT
|
|
+#define CFG_HT_RX_AMPDU_FACTOR_DEF (0x3)
|
|
+#define HT_IE_MAX_AMSDU_SIZE_4K (0)
|
|
+#define CFG_HT_MPDU_DENSITY_2USEC (0x5)
|
|
+#define CFG_HT_MPDU_DENSITY_DEF CFG_HT_MPDU_DENSITY_2USEC
|
|
+
|
|
+struct sta_ht_info {
|
|
+ u8 is_ht;
|
|
+ u16 rx_mimo_ps_mode;
|
|
+ u16 tx_mimo_ps_mode;
|
|
+ u16 control_channel;
|
|
+ u8 max_amsdu_size;
|
|
+ u8 ampdu_factor;
|
|
+ u8 mpdu_density;
|
|
+ u8 operating_mode;
|
|
+ u8 supported_chan_width;
|
|
+ u8 extension_chan_offset;
|
|
+ u8 is_green_field;
|
|
+ u8 sgf;
|
|
+ u8 supp_rates[16];
|
|
+ u8 tx_chan_width;
|
|
+ u8 chan_width_cap;
|
|
+};
|
|
+#endif /*CONFIG_IWLWIFI_HT */
|
|
+
|
|
+#ifdef CONFIG_IWLWIFI_QOS
|
|
+
|
|
+#define IPW_QOS_NONE 0
|
|
+#define IPW_QOS_11H 1
|
|
+#define IPW_QOS_WMM 2
|
|
+
|
|
+union iwl_qos_capabity {
|
|
+ struct {
|
|
+ u8 edca_count:4; /* bit 0-3 */
|
|
+ u8 q_ack:1; /* bit 4 */
|
|
+ u8 queue_request:1; /* bit 5 */
|
|
+ u8 txop_request:1; /* bit 6 */
|
|
+ u8 reserved:1; /* bit 7 */
|
|
+ } q_AP;
|
|
+ struct {
|
|
+ u8 acvo_APSD:1; /* bit 0 */
|
|
+ u8 acvi_APSD:1; /* bit 1 */
|
|
+ u8 ac_bk_APSD:1; /* bit 2 */
|
|
+ u8 ac_be_APSD:1; /* bit 3 */
|
|
+ u8 q_ack:1; /* bit 4 */
|
|
+ u8 max_len:2; /* bit 5-6 */
|
|
+ u8 more_data_ack:1; /* bit 7 */
|
|
+ } q_STA;
|
|
+ u8 val;
|
|
+};
|
|
+
|
|
+/* QoS sturctures */
|
|
+struct iwl_qos_info {
|
|
+ int qos_enable;
|
|
+ int qos_active;
|
|
+ union iwl_qos_capabity qos_cap;
|
|
+ struct iwl_qosparam_cmd def_qos_parm;
|
|
+};
|
|
+#endif /*CONFIG_IWLWIFI_QOS */
|
|
+
|
|
+#define STA_PS_STATUS_WAKE 0
|
|
+#define STA_PS_STATUS_SLEEP 1
|
|
+
|
|
+struct iwl_station_entry {
|
|
+ struct iwl_addsta_cmd sta;
|
|
+ struct iwl_tid_data tid[MAX_TID_COUNT];
|
|
+#if IWL == 3945
|
|
+ union {
|
|
+ struct {
|
|
+ u8 rate;
|
|
+ u8 flags;
|
|
+ } s;
|
|
+ u16 rate_n_flags;
|
|
+ } current_rate;
|
|
+#endif
|
|
+ u8 used;
|
|
+ u8 ps_status;
|
|
+ struct iwl_hw_key keyinfo;
|
|
+};
|
|
+
|
|
+/* one for each uCode image (inst/data, boot/init/runtime) */
|
|
+struct fw_image_desc {
|
|
+ void *v_addr; /* access by driver */
|
|
+ dma_addr_t p_addr; /* access by card's busmaster DMA */
|
|
+ u32 len; /* bytes */
|
|
+};
|
|
+
|
|
+/* uCode file layout */
|
|
+struct iwl_ucode {
|
|
+ __le32 ver; /* major/minor/subminor */
|
|
+ __le32 inst_size; /* bytes of runtime instructions */
|
|
+ __le32 data_size; /* bytes of runtime data */
|
|
+ __le32 init_size; /* bytes of initialization instructions */
|
|
+ __le32 init_data_size; /* bytes of initialization data */
|
|
+ __le32 boot_size; /* bytes of bootstrap instructions */
|
|
+ u8 data[0]; /* data in same order as "size" elements */
|
|
+};
|
|
+
|
|
+#define IWL_IBSS_MAC_HASH_SIZE 32
|
|
+
|
|
+struct iwl_ibss_seq {
|
|
+ u8 mac[ETH_ALEN];
|
|
+ u16 seq_num;
|
|
+ u16 frag_num;
|
|
+ unsigned long packet_time;
|
|
+ struct list_head list;
|
|
+};
|
|
+
|
|
+struct iwl_driver_hw_info {
|
|
+ u16 max_txq_num;
|
|
+ u16 ac_queue_count;
|
|
+ u32 rx_buffer_size;
|
|
+ u16 tx_cmd_len;
|
|
+ u16 max_rxq_size;
|
|
+ u16 max_rxq_log;
|
|
+ u32 cck_flag;
|
|
+ void *shared_virt;
|
|
+ dma_addr_t shared_phys;
|
|
+};
|
|
+
|
|
+
|
|
+#define STA_FLG_RTS_MIMO_PROT_MSK __constant_cpu_to_le32(1 << 17)
|
|
+#define STA_FLG_AGG_MPDU_8US_MSK __constant_cpu_to_le32(1 << 18)
|
|
+#define STA_FLG_MAX_AGG_SIZE_POS (19)
|
|
+#define STA_FLG_MAX_AGG_SIZE_MSK __constant_cpu_to_le32(3 << 19)
|
|
+#define STA_FLG_FAT_EN_MSK __constant_cpu_to_le32(1 << 21)
|
|
+#define STA_FLG_MIMO_DIS_MSK __constant_cpu_to_le32(1 << 22)
|
|
+#define STA_FLG_AGG_MPDU_DENSITY_POS (23)
|
|
+#define STA_FLG_AGG_MPDU_DENSITY_MSK __constant_cpu_to_le32(7 << 23)
|
|
+#define HT_SHORT_GI_20MHZ_ONLY (1 << 0)
|
|
+#define HT_SHORT_GI_40MHZ_ONLY (1 << 1)
|
|
+
|
|
+#include "iwl-3945.h"
|
|
+#include "iwl-4965.h"
|
|
+
|
|
+#include "iwl-priv.h"
|
|
+
|
|
+/* Requires full declaration of iwl_priv before including */
|
|
+#include "iwl-io.h"
|
|
+
|
|
+#define IWL_RX_HDR(x) ((struct iwl_rx_frame_hdr *)(\
|
|
+ x->u.rx_frame.stats.payload + \
|
|
+ x->u.rx_frame.stats.phy_count))
|
|
+#define IWL_RX_END(x) ((struct iwl_rx_frame_end *)(\
|
|
+ IWL_RX_HDR(x)->payload + \
|
|
+ le16_to_cpu(IWL_RX_HDR(x)->len)))
|
|
+#define IWL_RX_STATS(x) (&x->u.rx_frame.stats)
|
|
+#define IWL_RX_DATA(x) (IWL_RX_HDR(x)->payload)
|
|
+
|
|
+
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * Functions implemented in iwl-base.c which are forward declared here
|
|
+ * for use by iwl-*.c
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+struct iwl_addsta_cmd;
|
|
+extern int iwl_send_add_station(struct iwl_priv *priv,
|
|
+ struct iwl_addsta_cmd *sta, u8 flags);
|
|
+extern const char *iwl_get_tx_fail_reason(u32 status);
|
|
+extern u8 iwl_add_station(struct iwl_priv *priv, const u8 *bssid,
|
|
+ int is_ap, u8 flags);
|
|
+extern int iwl_is_network_packet(struct iwl_priv *priv,
|
|
+ struct ieee80211_hdr *header);
|
|
+extern int iwl_power_init_handle(struct iwl_priv *priv);
|
|
+extern int iwl_eeprom_init(struct iwl_priv *priv);
|
|
+#ifdef CONFIG_IWLWIFI_DEBUG
|
|
+extern void iwl_report_frame(struct iwl_priv *priv,
|
|
+ struct iwl_rx_packet *pkt,
|
|
+ struct ieee80211_hdr *header, int group100);
|
|
+#else
|
|
+static inline void iwl_report_frame(struct iwl_priv *priv,
|
|
+ struct iwl_rx_packet *pkt,
|
|
+ struct ieee80211_hdr *header,
|
|
+ int group100) {}
|
|
+#endif
|
|
+extern int iwl_tx_queue_update_write_ptr(struct iwl_priv *priv,
|
|
+ struct iwl_tx_queue *txq);
|
|
+extern void iwl_handle_data_packet_monitor(struct iwl_priv *priv,
|
|
+ struct iwl_rx_mem_buffer *rxb,
|
|
+ void *data, short len,
|
|
+ struct ieee80211_rx_status *stats,
|
|
+ u16 phy_flags);
|
|
+extern int is_duplicate_packet(struct iwl_priv *priv, struct ieee80211_hdr
|
|
+ *header);
|
|
+extern void iwl_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rxq);
|
|
+extern int iwl_rx_queue_alloc(struct iwl_priv *priv);
|
|
+extern void iwl_rx_queue_reset(struct iwl_priv *priv,
|
|
+ struct iwl_rx_queue *rxq);
|
|
+extern int iwl_calc_db_from_ratio(int sig_ratio);
|
|
+extern int iwl_calc_sig_qual(int rssi_dbm, int noise_dbm);
|
|
+extern int iwl_tx_queue_init(struct iwl_priv *priv,
|
|
+ struct iwl_tx_queue *txq, int count, u32 id);
|
|
+extern int iwl_rx_queue_restock(struct iwl_priv *priv);
|
|
+extern void iwl_rx_replenish(void *data);
|
|
+extern void iwl_tx_queue_free(struct iwl_priv *priv, struct iwl_tx_queue *txq);
|
|
+extern int iwl_send_cmd_pdu(struct iwl_priv *priv, u8 id, u16 len,
|
|
+ const void *data);
|
|
+extern int __must_check iwl_send_cmd_async(struct iwl_priv *priv,
|
|
+ struct iwl_host_cmd *cmd);
|
|
+extern int __must_check iwl_send_cmd_sync(struct iwl_priv *priv,
|
|
+ struct iwl_host_cmd *cmd);
|
|
+extern int __must_check iwl_send_cmd(struct iwl_priv *priv,
|
|
+ struct iwl_host_cmd *cmd);
|
|
+extern unsigned int iwl_fill_beacon_frame(struct iwl_priv *priv,
|
|
+ struct ieee80211_hdr *hdr,
|
|
+ const u8 *dest, int left);
|
|
+extern int iwl_rx_queue_update_write_ptr(struct iwl_priv *priv,
|
|
+ struct iwl_rx_queue *q);
|
|
+extern int iwl_send_statistics_request(struct iwl_priv *priv);
|
|
+extern void iwl_set_decrypted_flag(struct iwl_priv *priv, struct sk_buff *skb,
|
|
+ u32 decrypt_res,
|
|
+ struct ieee80211_rx_status *stats);
|
|
+extern __le16 *ieee80211_get_qos_ctrl(struct ieee80211_hdr *hdr);
|
|
+
|
|
+extern const u8 BROADCAST_ADDR[ETH_ALEN];
|
|
+
|
|
+/*
|
|
+ * Currently used by ipw-3945-rs... look at restructuring so that it doesn't
|
|
+ * call this... todo... fix that.
|
|
+*/
|
|
+extern u8 iwl_sync_station(struct iwl_priv *priv, int sta_id,
|
|
+ u16 tx_rate, u8 flags);
|
|
+
|
|
+static inline int iwl_is_associated(struct iwl_priv *priv)
|
|
+{
|
|
+ return (priv->active_rxon.filter_flags & RXON_FILTER_ASSOC_MSK) ?
|
|
+ 1 : 0;
|
|
+}
|
|
+
|
|
+/******************************************************************************
|
|
+ *
|
|
+ * Functions implemented in iwl-[34]*.c which are forward declared here
|
|
+ * for use by iwl-base.c
|
|
+ *
|
|
+ * NOTE: The implementation of these functions are hardware specific
|
|
+ * which is why they are in the hardware specific files (vs. iwl-base.c)
|
|
+ *
|
|
+ * Naming convention --
|
|
+ * iwl_ <-- Its part of iwlwifi (should be changed to iwl_)
|
|
+ * iwl_hw_ <-- Hardware specific (implemented in iwl-XXXX.c by all HW)
|
|
+ * iwlXXXX_ <-- Hardware specific (implemented in iwl-XXXX.c for XXXX)
|
|
+ * iwl_bg_ <-- Called from work queue context
|
|
+ * iwl_mac_ <-- mac80211 callback
|
|
+ *
|
|
+ ****************************************************************************/
|
|
+extern void iwl_hw_rx_handler_setup(struct iwl_priv *priv);
|
|
+extern void iwl_hw_setup_deferred_work(struct iwl_priv *priv);
|
|
+extern void iwl_hw_cancel_deferred_work(struct iwl_priv *priv);
|
|
+extern int iwl_hw_rxq_stop(struct iwl_priv *priv);
|
|
+extern int iwl_hw_set_hw_setting(struct iwl_priv *priv);
|
|
+extern int iwl_hw_nic_init(struct iwl_priv *priv);
|
|
+extern void iwl_hw_card_show_info(struct iwl_priv *priv);
|
|
+extern int iwl_hw_nic_stop_master(struct iwl_priv *priv);
|
|
+extern void iwl_hw_txq_ctx_free(struct iwl_priv *priv);
|
|
+extern void iwl_hw_txq_ctx_stop(struct iwl_priv *priv);
|
|
+extern int iwl_hw_nic_reset(struct iwl_priv *priv);
|
|
+extern int iwl_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv, void *tfd,
|
|
+ dma_addr_t addr, u16 len);
|
|
+extern int iwl_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq);
|
|
+extern int iwl_hw_get_temperature(struct iwl_priv *priv);
|
|
+extern int iwl_hw_tx_queue_init(struct iwl_priv *priv,
|
|
+ struct iwl_tx_queue *txq);
|
|
+extern unsigned int iwl_hw_get_beacon_cmd(struct iwl_priv *priv,
|
|
+ struct iwl_frame *frame, u8 rate);
|
|
+extern int iwl_hw_get_rx_read(struct iwl_priv *priv);
|
|
+extern void iwl_hw_build_tx_cmd_rate(struct iwl_priv *priv,
|
|
+ struct iwl_cmd *cmd,
|
|
+ struct ieee80211_tx_control *ctrl,
|
|
+ struct ieee80211_hdr *hdr,
|
|
+ int sta_id, int tx_id);
|
|
+extern int iwl_hw_reg_send_txpower(struct iwl_priv *priv);
|
|
+extern int iwl_hw_reg_set_txpower(struct iwl_priv *priv, s8 power);
|
|
+extern void iwl_hw_rx_statistics(struct iwl_priv *priv,
|
|
+ struct iwl_rx_mem_buffer *rxb);
|
|
+extern void iwl_disable_events(struct iwl_priv *priv);
|
|
+extern int iwl4965_get_temperature(const struct iwl_priv *priv);
|
|
+
|
|
+/**
|
|
+ * iwl_hw_find_station - Find station id for a given BSSID
|
|
+ * @bssid: MAC address of station ID to find
|
|
+ *
|
|
+ * NOTE: This should not be hardware specific but the code has
|
|
+ * not yet been merged into a single common layer for managing the
|
|
+ * station tables.
|
|
+ */
|
|
+extern u8 iwl_hw_find_station(struct iwl_priv *priv, const u8 *bssid);
|
|
+
|
|
+extern int iwl_hw_channel_switch(struct iwl_priv *priv, u16 channel);
|
|
+extern int iwl_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index);
|
|
+#endif
|