diff --git a/debian/changelog b/debian/changelog index 38b40cc46..f4f6990b9 100644 --- a/debian/changelog +++ b/debian/changelog @@ -21,12 +21,13 @@ linux-2.6 (2.6.23~rc4-1~experimental.1) UNRELEASED; urgency=low * linux-image bugscript add cmdline. * [amd64, i386, ia64]: Enable DMIID. * Enable TCG_TPM various userspace accesses it. (closes: #439020) + * Add and enable IWLWIFI. [ Martin Michlmayr ] * [mips] Add a bcm1480 PCI build fix. * [mips] Add a bcm1480 serial build fix. - -- maximilian attems Fri, 31 Aug 2007 12:04:28 +0200 + -- maximilian attems Fri, 31 Aug 2007 12:21:00 +0200 linux-2.6 (2.6.22-2) UNRELEASED; urgency=low diff --git a/debian/config/config b/debian/config/config index 0bb8c6e21..10762f454 100644 --- a/debian/config/config +++ b/debian/config/config @@ -1592,6 +1592,7 @@ CONFIG_AIRO=m CONFIG_NORTEL_HERMES=m CONFIG_PCMCIA_SPECTRUM=m CONFIG_RTL8187=m +CONFIG_IWLWIFI=y CONFIG_HOSTAP=m CONFIG_HOSTAP_FIRMWARE=y # CONFIG_HOSTAP_FIRMWARE_NVRAM is not set diff --git a/debian/patches/features/all/v5-Add-iwlwifi-wireless-drivers.patch b/debian/patches/features/all/v5-Add-iwlwifi-wireless-drivers.patch new file mode 100644 index 000000000..94d2e4b38 --- /dev/null +++ b/debian/patches/features/all/v5-Add-iwlwifi-wireless-drivers.patch @@ -0,0 +1,26951 @@ +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 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 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: ++ ++ . ++ ++ 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 . 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 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: ++ ++ . ++ ++ 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 . 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 ++ * 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 ++ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 ++ * ++ *****************************************************************************/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++#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 ++ * 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< ++ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 ++ * ++ *****************************************************************************/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#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 ++ * 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 ++ * 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 ++ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 ++ * ++ *****************************************************************************/ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++#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 ++ * 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_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 ++ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 ++ * ++ *****************************************************************************/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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("<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<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[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 ++ * 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 ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++#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, "", sizeof("")); ++ 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 */ ++ 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 ++ * 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 ++ * 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 ++ * 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 ++ * 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 ++ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 ++ * ++ *****************************************************************************/ ++ ++#ifndef __iwl_helpers_h__ ++#define __iwl_helpers_h__ ++ ++#include ++ ++/* ++ * 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 ++ * 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 ++ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 ++ * ++ *****************************************************************************/ ++ ++#ifndef __iwl_io_h__ ++#define __iwl_io_h__ ++ ++#include ++ ++#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 ++ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 ++ * ++ *****************************************************************************/ ++ ++#ifndef __iwl_priv_h__ ++#define __iwl_priv_h__ ++ ++#include ++ ++#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 ++ * 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 ++ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 ++ * ++ *****************************************************************************/ ++ ++#ifndef __iwlwifi_h__ ++#define __iwlwifi_h__ ++ ++#include /* for struct pci_device_id */ ++#include ++#include ++ ++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 diff --git a/debian/patches/series/1~experimental.1 b/debian/patches/series/1~experimental.1 index 63f4904ec..a64b8e86c 100644 --- a/debian/patches/series/1~experimental.1 +++ b/debian/patches/series/1~experimental.1 @@ -39,3 +39,4 @@ + bugfix/arm/disable-netxen_nic.patch + bugfix/arm/disable-chelsio_t3.patch + bugfix/arm/disable-video_bt848.patch ++ features/all/v5-Add-iwlwifi-wireless-drivers.patch