From: John W. Linville <linville@redhat.com> Date: Tue, 18 Dec 2007 17:44:29 -0500 Subject: [net] iwl4965 updates Message-id: 20071218224428.GG11328@redhat.com O-Subject: [RHEL5.2 PATCH 4/4] iwl4965 updates Bugzilla: 252981 This superbly monstrous patch updates the iwlwifi driver to match what is in wireless-2.6 as of today. It specifically addresses the following bugzillas: BZ252981 BZ253027 BZ253067 BZ256001 Lot of testing by me on Dell m4300 laptop equipped w/ iwl4965 hardware. Acked-by: "David S. Miller" <davem@redhat.com> diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 2a05163..9d6f9d4 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -44,4 +44,5 @@ obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o obj-$(CONFIG_USB_ZD1201) += zd1201.o -obj-$(CONFIG_IWLWIFI) += iwlwifi/ +obj-$(CONFIG_IWL3945) += iwlwifi/ +obj-$(CONFIG_IWL4965) += iwlwifi/ diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig index 295acf2..93ee596 100644 --- a/drivers/net/wireless/iwlwifi/Kconfig +++ b/drivers/net/wireless/iwlwifi/Kconfig @@ -1,30 +1,72 @@ -config IWLWIFI - bool "Intel Wireless WiFi Link Drivers" +config IWL4965 + tristate "Intel Wireless WiFi 4965AGN" depends on PCI && MAC80211 && NET_RADIO && EXPERIMENTAL select FW_LOADER - default n ---help--- - Select to enable drivers based on the iwlwifi project. This - project provides a common foundation for Intel's wireless - drivers designed to use the mac80211 subsystem. - - See <file:Documentation/networking/README.iwlwifi> for - information on the capabilities currently enabled in this - driver and for tips for debugging issues and problems. - -config IWLWIFI_DEBUG - bool "Enable full debugging output in iwlwifi drivers" - depends on IWLWIFI - default y + Select to build the driver supporting the: + + Intel Wireless WiFi Link 4965AGN + + This driver uses the kernel's mac80211 subsystem. + + See <file:Documentation/networking/README.iwlwifi> for + information on the capabilities currently enabled in this + driver and for tips for debugging any issues or problems. + + In order to use this driver, you will need a microcode (uCode) + image for it. You can obtain the microcode from: + + <http://intellinuxwireless.org/>. + + See the above referenced README.iwlwifi for information on where + to install the microcode images. + + If you want to compile the driver as a module ( = code which can be + inserted in and remvoed from the running kernel whenever you want), + say M here and read <file:Documentation/kbuild/modules.txt>. The + module will be called iwl4965.ko. + +config IWL4965_QOS + bool "Enable Wireless QoS in iwl4965 driver" + depends on IWL4965 + ---help--- + This option will enable wireless quality of service (QoS) for the + iw4965 driver. + +config IWL4965_HT + bool "Enable 802.11n HT features in iwl4965 driver" + depends on EXPERIMENTAL + depends on IWL4965 && IWL4965_QOS + depends on n + ---help--- + This option enables IEEE 802.11n High Throughput features + for the iwl4965 driver. + +config IWL4965_SPECTRUM_MEASUREMENT + bool "Enable Spectrum Measurement in iw4965 driver" + depends on IWL4965 ---help--- - This option will enable debug tracing output for the iwlwifi - drivers. + This option will enable spectrum measurement for the iwl4965 driver. - 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 +config IWL4965_SENSITIVITY + bool "Enable Sensitivity Calibration in iwl4965 driver" + depends on IWL4965 + ---help--- + This option will enable sensitivity calibration for the iwl4965 + driver. + +config IWL4965_DEBUG + bool "Enable full debugging output in iwl4965 driver" + depends on IWL4965 + ---help--- + This option will enable debug tracing output for the iwl4965 + driver. + + 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 + /sys/bus/pci/drivers/${DRIVER}/debug_level This entry will only exist if this option is enabled. @@ -33,64 +75,76 @@ config IWLWIFI_DEBUG % 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 + drivers/net/wireless/iwlwifi/iwl-4965-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 IWL4965 - tristate "Intel Wireless WiFi 4965AGN" - depends on m && IWLWIFI && EXPERIMENTAL - default m +config IWL3945 + tristate "Intel PRO/Wireless 3945ABG/BG Network Connection" + depends on PCI && MAC80211 && NET_RADIO && EXPERIMENTAL + select FW_LOADER ---help--- Select to build the driver supporting the: - Intel Wireless WiFi Link 4965AGN + Intel PRO/Wireless 3945ABG/BG Network Connection This driver uses the kernel's mac80211 subsystem. - See <file:Documentation/networking/README.iwlwifi> for - information on the capabilities currently enabled in this + See <file:Documentation/networking/README.iwlwifi> for + information on the capabilities currently enabled in this driver and for tips for debugging any issues or problems. In order to use this driver, you will need a microcode (uCode) image for it. You can obtain the microcode from: - <http://intellinuxwireless.org/>. + <http://intellinuxwireless.org/>. - See the above referenced README.iwlwifi for information on where + See the above referenced README.iwlwifi for information on where to install the microcode images. If you want to compile the driver as a module ( = code which can be inserted in and remvoed from the running kernel whenever you want), - say M here and read <file:Documentation/modules.txt>. The module - will be called iwl4965.ko. + say M here and read <file:Documentation/kbuild/modules.txt>. The + module will be called iwl3945.ko. -config IWL3945 - tristate "Intel PRO/Wireless 3945ABG/BG Network Connection" - depends on m && IWLWIFI && EXPERIMENTAL - default m +config IWL3945_QOS + bool "Enable Wireless QoS in iwl3945 driver" + depends on IWL3945 ---help--- - Select to build the driver supporting the: + This option will enable wireless quality of service (QoS) for the + iwl3945 driver. - Intel PRO/Wireless 3945ABG/BG Network Connection +config IWL3945_SPECTRUM_MEASUREMENT + bool "Enable Spectrum Measurement in iwl3945 drivers" + depends on IWL3945 + ---help--- + This option will enable spectrum measurement for the iwl3945 driver. - This driver uses the kernel's mac80211 subsystem. +config IWL3945_DEBUG + bool "Enable full debugging output in iwl3945 driver" + depends on IWL3945 + ---help--- + This option will enable debug tracing output for the iwl3945 + driver. - See <file:Documentation/networking/README.iwlwifi> for - information on the capabilities currently enabled in this - driver and for tips for debugging any issues or problems. + 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 - In order to use this driver, you will need a microcode (uCode) - image for it. You can obtain the microcode from: + /sys/bus/pci/drivers/${DRIVER}/debug_level - <http://intellinuxwireless.org/>. + This entry will only exist if this option is enabled. - See the above referenced README.iwlwifi for information on where - to install the microcode images. + 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/iwlwifi/iwl-3945-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. - If you want to compile the driver as a module ( = code which can be - inserted in and remvoed from the running kernel whenever you want), - say M here and read <file:Documentation/modules.txt>. The module - will be called iwl3945.ko. diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile index 8c0bdc7..3bbd383 100644 --- a/drivers/net/wireless/iwlwifi/Makefile +++ b/drivers/net/wireless/iwlwifi/Makefile @@ -1,29 +1,5 @@ -# NOTE: We use common code from base.c to build driver -# specific binaries based on the #define IWL -- the target -# setup below creates a specific driver target from base.c -# -# NOTE2: 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 = base-3945.o iwl-3945.o iwl-3945-rs.o -CFLAGS_iwl-3945.o = -DIWL=3945 -CFLAGS_iwl-3945-rs.o = -DIWL=3945 -CFLAGS_base-3945.o = -DIWL=3945 -D"KBUILD_MODNAME=KBUILD_STR(iwl3945)" -$(obj)/base-3945.o: $(src)/base.c FORCE - $(call cmd,force_checksrc) - $(call if_changed_rule,cc_o_c) +iwl3945-objs = iwl3945-base.o iwl-3945.o iwl-3945-rs.o obj-$(CONFIG_IWL4965) += iwl4965.o -iwl4965-objs = base-4965.o iwl-4965.o iwl-4965-rs.o -CFLAGS_iwl-4965.o = -DIWL=4965 -CFLAGS_iwl-4965-rs.o = -DIWL=4965 -CFLAGS_base-4965.o = -DIWL=4965 -D"KBUILD_MODNAME=KBUILD_STR(iwl4965)" -$(obj)/base-4965.o: $(src)/base.c FORCE - $(call cmd,force_checksrc) - $(call if_changed_rule,cc_o_c) - +iwl4965-objs = iwl4965-base.o iwl-4965.o iwl-4965-rs.o diff --git a/drivers/net/wireless/iwlwifi/base.c b/drivers/net/wireless/iwlwifi/base.c index 4f7fe12..e69de29 100644 --- a/drivers/net/wireless/iwlwifi/base.c +++ b/drivers/net/wireless/iwlwifi/base.c @@ -1,9046 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2007 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * James P. Ketrenos <ipw2100-admin@linux.intel.com> - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -/* - * NOTE: This file (base.c) is used to build to multiple hardware targets - * by defining IWL to either 3945 or 4965. The Makefile used when building - * the base targets will create base-3945.o and base-4965.o - * - * The eventual goal is to move as many of the #if IWL / #endif blocks out of - * this file and into the hardware specific implementation files (iwl-XXXX.c) - * and leave only the common (non #ifdef sprinkled) code in this file - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/version.h> -#include <linux/init.h> -#include <linux/pci.h> -#include <linux/dma-mapping.h> -#include <linux/delay.h> -#include <linux/skbuff.h> -#include <linux/netdevice.h> -#include <linux/wireless.h> -#include <linux/firmware.h> -#include <linux/skbuff.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/if_arp.h> - -#include <net/ieee80211_radiotap.h> -#include <net/mac80211.h> - -#include <asm/div64.h> - -#include "iwlwifi.h" -#include "iwl-helpers.h" - -#ifdef CONFIG_IWLWIFI_DEBUG -u32 iwl_debug_level; -#endif - -/****************************************************************************** - * - * module boiler plate - * - ******************************************************************************/ - -/* module parameters */ -int param_disable_hw_scan = 0; -int param_debug = 0; -int param_disable = 0; /* def: enable radio */ -int param_antenna = 0; /* def: 0 = both antennas (use diversity) */ -int param_hwcrypto = 0; /* def: using software encryption */ -int param_qos_enable = 0; - -/* - * 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_MEASUREMENY -#define VS "s" -#else -#define VS -#endif - -#define IWLWIFI_VERSION "1.0.0k" VD VS -#define DRV_COPYRIGHT "Copyright(c) 2003-2007 Intel Corporation" -#define DRV_VERSION IWLWIFI_VERSION - -MODULE_DESCRIPTION(DRV_DESCRIPTION); -MODULE_VERSION(DRV_VERSION); -MODULE_AUTHOR(DRV_COPYRIGHT); -MODULE_LICENSE("GPL"); - -/*************** 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; -} - -static inline int iwl_queue_inc_wrap(int index, int n_bd) -{ - return (++index == n_bd) ? 0 : index; -} - -static inline int iwl_queue_dec_wrap(int index, int n_bd) -{ - return (index == 0) ? n_bd - 1 : index - 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_next_cmd_index(struct iwl_queue *q, u32 index, int is_huge) -{ - if (is_huge) - return q->n_window; - - return (u8) (index % q->n_window); -} - -static int iwl_queue_init(struct iwl_priv *priv, struct iwl_queue *q, - int count, int size, u32 id) -{ - q->n_bd = count; - q->n_window = size; - q->id = id; - - 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, int count, 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_ATOMIC); - 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(struct iwl_tfd_frame) * TFD_QUEUE_SIZE_MAX, - &txq->q.dma_addr); - - txq->q.element_size = sizeof(struct iwl_tfd_frame); - if (!txq->bd) { - IWL_ERROR("pci_alloc_consistent(%zd) failed\n", - sizeof(txq->bd[0]) * count); - 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 count, 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) * count; - 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, count, txq_id); - if (rc) { - pci_free_consistent(dev, len, txq->cmd, txq->dma_addr_cmd); - - return -ENOMEM; - } - - txq->need_update = 0; - iwl_queue_init(priv, &txq->q, TFD_QUEUE_SIZE_MAX, count, 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_tx_queue_free_tfd(priv, txq); - } - - len = sizeof(txq->cmd[0]) * 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, - IWL_STATION_COUNT * sizeof(struct iwl_station_entry)); - 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; - - 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; - station->current_rate.s.rate = IWL_RATE_1M_PLCP; - 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; - - /* todoG do we need this - * all TID's disabled - * priv->stations[i].sta.tid_disable_tx = 0xffff; - */ - if ((priv->phymode == MODE_IEEE80211A) || - (priv->phymode == MODE_ATHEROS_TURBO)) - station->sta.tx_rate.rate_n_flags = IWL_RATE_6M_PLCP; - else - station->sta.tx_rate.rate_n_flags = IWL_RATE_1M_PLCP | - priv->hw_setting.cck_flag; - - /* Turn on both antennas for the station... */ - station->sta.tx_rate.rate_n_flags |= RATE_MCS_ANT_AB_MSK; - - /* - * priv->stations[i].sta.tx_rate.rate_n_flags |= RATE_MCS_ANT_B_MSK; - * priv->stations[i].sta.tx_rate.rate_n_flags &= ~RATE_MCS_ANT_A_MSK; - */ - - station->sta.station_flags |= STA_MODIFY_TX_RATE_MSK; -#if IWL == 4965 -#ifdef CONFIG_IWLWIFI_HT - if (is_ap) { - iwl4965_set_ht_add_station(priv, i, 0); - iwl4965_set_rxon_chain(priv); - } -#endif /*CONFIG_IWLWIFI_HT*/ -#endif - station->current_rate.rate_n_flags = station->sta.tx_rate.rate_n_flags; - - 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; -} - -/* iwl_hw_find_station is implemented in iwl-3945 and iwl-4965 for now... */ - -u8 iwl_sync_station(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_CONTROL_MODIFY_MSK; - station->sta.tx_rate.rate_n_flags = 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; -} - -/*************** 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 ((priv->status & (STATUS_READY | - STATUS_GEO_CONFIGURED | - STATUS_EXIT_PENDING)) == - (STATUS_READY | STATUS_GEO_CONFIGURED)) ? 1 : 0; -} - -static inline int iwl_is_alive(struct iwl_priv *priv) -{ - return (priv->status & STATUS_ALIVE) ? 1 : 0; -} - -static inline int iwl_is_init(struct iwl_priv *priv) -{ - return (priv->status & STATUS_INIT) ? 1 : 0; -} - -static inline int iwl_is_ready_rf(struct iwl_priv *priv) -{ - - if (priv->status & STATUS_RF_KILL_MASK) - 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(SCAN_START_NOTIFICATION); - IWL_CMD(SCAN_RESULTS_NOTIFICATION); - IWL_CMD(SCAN_COMPLETE_NOTIFICATION); - IWL_CMD(STATISTICS_NOTIFICATION); - IWL_CMD(REPLY_ALIVE); - IWL_CMD(REPLY_ERROR); - IWL_CMD(REPLY_RXON_ASSOC); - IWL_CMD(REPLY_RXON); - IWL_CMD(REPLY_QOS_PARAM); - IWL_CMD(REPLY_RXON_TIMING); - IWL_CMD(REPLY_ADD_STA); - IWL_CMD(REPLY_TX); - IWL_CMD(REPLY_BCON); - IWL_CMD(REPLY_RATE_SCALE); - IWL_CMD(REPLY_LEDS_CMD); - IWL_CMD(REPLY_SCAN_ABORT_CMD); - IWL_CMD(REPLY_TX_BEACON); - IWL_CMD(REPLY_BT_CONFIG); - IWL_CMD(REPLY_SCAN_CMD); - IWL_CMD(REPLY_TX_PWR_TABLE_CMD); - IWL_CMD(REPLY_STATISTICS_CMD); - IWL_CMD(REPLY_CARD_STATE_CMD); - IWL_CMD(REPLY_TX_LINK_QUALITY_CMD); -#if IWL == 3945 - IWL_CMD(REPLY_3945_RX); -#elif IWL == 4965 - IWL_CMD(MISSED_BEACONS_NOTIFICATION_TH_CMD); - IWL_CMD(REPLY_CT_KILL_CONFIG_CMD); - IWL_CMD(SENSITIVITY_CMD); - IWL_CMD(REPLY_RX_MPDU_CMD); - IWL_CMD(REPLY_RX_PHY_CMD); - IWL_CMD(REPLY_4965_RX); -#endif - case POWER_TABLE_CMD: - return "POWER_TABLE_CMD"; - default: - return "UNKNOWN"; - - } -} - -#define HOST_COMPLETE_TIMEOUT (HZ / 2) - -static inline int is_cmd_sync(struct iwl_host_cmd *cmd) -{ - return !(cmd->meta.flags & CMD_ASYNC); -} - -static inline int is_cmd_small(struct iwl_host_cmd *cmd) -{ - return !(cmd->meta.flags & CMD_SIZE_HUGE); -} - -static inline int cmd_needs_lock(struct iwl_host_cmd *cmd) -{ - return !(cmd->meta.flags & CMD_NO_LOCK); -} - -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; - u8 *tfd; - u32 *control_flags; - struct iwl_cmd *out_cmd; - u32 idx = 0; - u16 fix_size = (u16) (cmd->meta.len + sizeof(out_cmd->hdr)); - dma_addr_t phys_addr; -#if IWL == 3945 - int pad; - u16 count; -#elif IWL == 4965 - int rc; -#endif - - /* 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) - && is_cmd_small(cmd)); - if (iwl_queue_space(q) < (is_cmd_sync(cmd) ? 1 : 2)) { - IWL_ERROR("No space for Tx\n"); - return -ENOSPC; - } - tfd = &txq->bd[q->first_empty * q->element_size]; - memset(tfd, 0, q->element_size); - - control_flags = (u32 *) tfd; - - idx = get_next_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 = 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 |= SEQ_HUGE_FRAME; - - phys_addr = txq->dma_addr_cmd + sizeof(txq->cmd[0]) * idx + - offsetof(struct iwl_cmd, hdr); - iwl_hw_tx_queue_attach_buffer_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, 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); - return rc; -#elif IWL == 3945 - q->first_empty = iwl_queue_inc_wrap(q->first_empty, q->n_bd); - - return iwl_tx_queue_update_write_ptr(priv, txq); -#endif -} - -int iwl_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) -{ - int rc; - unsigned long flags = 0; - - /* If this is an asynchronous command, and we are in a shutdown - * process then don't let it start */ - if (!is_cmd_sync(cmd) && (priv->status & STATUS_EXIT_PENDING)) - return -EBUSY; - - /* - * The following checks are meant to catch programming API misuse - * and not run-time failures due to timing, resource constraint, etc. - */ - - /* A command can not be asynchronous AND expect an SKB to be set */ - if ((cmd->meta.flags & CMD_ASYNC) && (cmd->meta.flags & CMD_WANT_SKB)) { - IWL_ERROR("ASYNC && WANT_SKB\n"); - return -EINVAL; - } - - /* The skb/callback union must be NULL if an SKB is requested */ - if (cmd->meta.u.skb && (cmd->meta.flags & CMD_WANT_SKB)) { - IWL_ERROR("skb != null && WANT_SKB\n"); - return -EINVAL; - } - - /* A command can not be synchronous AND have a callback set */ - if (is_cmd_sync(cmd) && cmd->meta.u.callback) { - IWL_ERROR("callback != null && SYNC\n"); - return -EINVAL; - } - - /* An asynchronous command MUST have a callback */ - if ((cmd->meta.flags & CMD_ASYNC) && !cmd->meta.u.callback) { - IWL_ERROR("callback == null && ASYNC\n"); - return -EINVAL; - } - - /* A command can not be synchronous AND not use locks */ - if (is_cmd_sync(cmd) && (cmd->meta.flags & CMD_NO_LOCK)) { - IWL_ERROR("SYNC && NO_LOCK\n"); - return -EINVAL; - } - - if (cmd_needs_lock(cmd)) - spin_lock_irqsave(&priv->lock, flags); - - if (is_cmd_sync(cmd) && (priv->status & STATUS_HCMD_ACTIVE)) { - IWL_ERROR("Error sending %s: " - "Already sending a host command\n", - get_cmd_string(cmd->id)); - if (cmd_needs_lock(cmd)) - spin_unlock_irqrestore(&priv->lock, flags); - return -EBUSY; - } - - if (is_cmd_sync(cmd)) - priv->status |= STATUS_HCMD_ACTIVE; - - /* When the SKB is provided in the tasklet, it needs - * a backpointer to the originating caller so it can - * actually copy the skb there */ - if (cmd->meta.flags & CMD_WANT_SKB) { - cmd->meta.source = &cmd->meta; - cmd->meta.magic = CMD_VAR_MAGIC; - } - - cmd->meta.len = cmd->len; - - rc = iwl_enqueue_hcmd(priv, cmd); - if (rc) { - if (is_cmd_sync(cmd)) - priv->status &= ~STATUS_HCMD_ACTIVE; - if (cmd_needs_lock(cmd)) - spin_unlock_irqrestore(&priv->lock, flags); - - IWL_ERROR("Error sending %s: " - "iwl_queue_tx_hcmd failed: %d\n", - get_cmd_string(cmd->id), rc); - - return -ENOSPC; - } - if (cmd_needs_lock(cmd)) - spin_unlock_irqrestore(&priv->lock, flags); - - if (is_cmd_sync(cmd)) { - rc = wait_event_interruptible_timeout(priv->wait_command_queue, - !(priv->status & - STATUS_HCMD_ACTIVE), - HOST_COMPLETE_TIMEOUT); - if (rc == 0) { - if (cmd_needs_lock(cmd)) - spin_lock_irqsave(&priv->lock, flags); - - if (priv->status & STATUS_HCMD_ACTIVE) { - IWL_ERROR("Error sending %s: " - "time out after %dms.\n", - get_cmd_string(cmd->id), - jiffies_to_msecs - (HOST_COMPLETE_TIMEOUT)); - priv->status &= ~STATUS_HCMD_ACTIVE; - if ((cmd->meta.flags & CMD_WANT_SKB) - && cmd->meta.u.skb) { - dev_kfree_skb_any(cmd->meta.u.skb); - cmd->meta.u.skb = NULL; - } - - if (cmd_needs_lock(cmd)) - spin_unlock_irqrestore( - &priv->lock, flags); - cmd->meta.magic = 0; - return -ETIMEDOUT; - } - - if (cmd_needs_lock(cmd)) - spin_unlock_irqrestore(&priv->lock, flags); - } - } - - if (priv->status & STATUS_RF_KILL_HW) { - if ((cmd->meta.flags & CMD_WANT_SKB) - && cmd->meta.u.skb) { - dev_kfree_skb_any(cmd->meta.u.skb); - cmd->meta.u.skb = NULL; - } - - IWL_DEBUG_INFO("Command %s aborted: RF KILL Switch\n", - get_cmd_string(cmd->id)); - - return -ECANCELED; - } - - if (priv->status & STATUS_FW_ERROR) { - if ((cmd->meta.flags & CMD_WANT_SKB) - && cmd->meta.u.skb) { - dev_kfree_skb_any(cmd->meta.u.skb); - cmd->meta.u.skb = NULL; - } - - IWL_DEBUG_INFO("Command %s failed: FW Error\n", - get_cmd_string(cmd->id)); - - return -EIO; - } - - if ((cmd->meta.flags & CMD_WANT_SKB) && !cmd->meta.u.skb) { - IWL_ERROR("Error: Response NULL in '%s'\n", - get_cmd_string(cmd->id)); - return -EIO; - } - - return 0; -} - -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(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(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 i; - - /* Remove this station if it happens to already exist */ - iwl_remove_station(priv, addr, is_ap); - - i = iwl_add_station(priv, addr, is_ap, 0); - - iwl4965_add_station(priv, addr, is_ap); - - return i; -} - -/** - * 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, u8 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 ((priv->staging_rxon.channel == channel) && - (priv->phymode == phymode)) - return 0; - - priv->staging_rxon.channel = channel; - if ((phymode == MODE_IEEE80211A) || - (phymode == MODE_ATHEROS_TURBO)) - 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 |= (rxon->flags & RXON_FLG_TGJ_NARROW_BAND_MSK); - error |= (rxon->flags & 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) != - RXON_FLG_SHORT_SLOT_MSK); - if (error) - IWL_WARNING("check 52 fields %d | %d\n", - counter++, error); - error |= (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 |= (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 %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 - ("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 (iwl_check_bits(priv->staging_rxon.flags, RXON_FLG_BAND_24G_MSK) != - iwl_check_bits(priv->active_rxon.flags, RXON_FLG_BAND_24G_MSK)) - return 1; - - /* Check if we are switching association toggle */ - if (iwl_check_bits(priv->staging_rxon.filter_flags, - RXON_FILTER_ASSOC_MSK) != - iwl_check_bits(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(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; - } - - /* station table will be caleared */ - priv->assoc_station_added = 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"), - 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; - } - priv->assoc_station_added = 1; - } - - /* 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 (!(priv->status & STATUS_SCAN_HW)) { - priv->status &= ~STATUS_SCAN_ABORTING; - return 0; - } - - rc = iwl_send_cmd(priv, &cmd); - if (rc) { - priv->status &= ~STATUS_SCAN_ABORTING; - 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); - priv->status &= ~(STATUS_SCAN_ABORTING | STATUS_SCAN_HW); - } - - 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; - u16 key_flags = 0; - - switch (keyconf->alg) { - case ALG_CCMP: - key_flags |= STA_KEY_FLG_CCMP; - key_flags |= 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_ATOMIC); - 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); -} - -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; - int frame_size, rc; - u16 rate; - - frame = iwl_get_free_frame(priv); - - if (!frame) { - IWL_ERROR("Coult 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 <= 0) - rate = IWL_RATE_6M_PLCP; - } else { - rate = iwl_rate_get_lowest_plcp(priv->active_rate_basic & 0xF); - if (rate <= 0) - 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 r; - int to; - u32 gp = iwl_read32(priv, CSR_EEPROM_GP); - u16 sz = sizeof(priv->eeprom); - int rc; - 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; - } -#if IWL == 3945 - _iwl_clear_bit(priv, CSR_EEPROM_GP, CSR_EEPROM_GP_OWNER); -#endif - - for (addr = 0, r = 0; addr < sz; addr += 2) { - _iwl_write32(priv, CSR_EEPROM_REG, addr << 1); - _iwl_clear_bit(priv, CSR_EEPROM_REG, 0x00000002); - rc = _iwl_grab_restricted_access(priv); - if (rc) - return rc; - - for (to = 0; to < 10; to++) { - r = _iwl_read_restricted(priv, CSR_EEPROM_REG); - if (r & 1) - break; - udelay(5); - } - - _iwl_release_restricted_access(priv); - - if (!(r & 1)) { - IWL_ERROR("Time out reading EEPROM[%d]", addr); - return -ETIMEDOUT; - } - - e[addr / 2] = le16_to_cpu(r >> 16); - } - - return 0; -} - -/****************************************************************************** - * - * 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; - - /* these are declared without "=" to avoid compiler warnings if we - * don't use them in the debug messages below */ - u16 frame_ctl; - 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 */ - frame_ctl = 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 && (frame_ctl & ~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 (frame_ctl & IEEE80211_FCTL_RETRY) - title = "Retry"; - else if (ieee80211_is_assoc_response(frame_ctl)) - title = "AscRsp"; - else if (ieee80211_is_reassoc_response(frame_ctl)) - title = "RasRsp"; - else if (ieee80211_is_probe_response(frame_ctl)) { - title = "PrbRsp"; - print_dump = 1; /* dump frame contents */ - } else if (ieee80211_is_beacon(frame_ctl)) { - title = "Beacon"; - print_dump = 1; /* dump frame contents */ - } else if (ieee80211_is_atim(frame_ctl)) - title = "ATIM"; - else if (ieee80211_is_auth(frame_ctl)) - title = "Auth"; - else if (ieee80211_is_deauth(frame_ctl)) - title = "DeAuth"; - else if (ieee80211_is_disassoc(frame_ctl)) - 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, frame_ctl, 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, frame_ctl, header->addr1[5], - header->addr3[5], rssi, - tsf_low - priv->scan_start_tsf, - phy_flags, channel); - } - } - if (print_dump) - printk_buf(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 d_get_ht_capab (struct ieee80211_hw *hw, - struct ieee80211_ht_capability *ht_cap_param); -#endif -#endif - -/** - * iwl_fill_probe_req - fill in all required fields and IE for probe request - */ -static int 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) { - pos += (*pos) + 1; - *pos++ = WLAN_EID_HT_CAPABILITY; - *pos++ = sizeof(struct ieee80211_ht_capability); - d_get_ht_capab(NULL, (struct ieee80211_ht_capability *)pos); - len += 2 + sizeof(struct ieee80211_ht_capability); - } -#endif /*CONFIG_IWLWIFI_HT */ -#endif - - fill_end: - return len; -} - -/* - * Power management (not Tx power!) functions - */ -#define MSEC_TO_USEC 1024 - -#if IWL == 3945 -#define NOSLP 0 -#define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK -#elif IWL == 4965 -#define NOSLP 0, 0, 0 -#define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK, 0, 0 -#endif - -/* default power management (not Tx power) table values */ -/* for tim 0-10 */ -static struct iwl_power_vec_entry range_0[IWL_POWER_AC] = { - {{NOSLP, 0 * MSEC_TO_USEC, 0 * MSEC_TO_USEC, {0, 0, 0, 0, 0}}, 0}, - {{SLP, 200 * MSEC_TO_USEC, 500 * MSEC_TO_USEC, {1, 2, 3, 4, 4}}, 0}, - {{SLP, 200 * MSEC_TO_USEC, 300 * MSEC_TO_USEC, {2, 4, 6, 7, 7}}, 0}, - {{SLP, 50 * MSEC_TO_USEC, 100 * MSEC_TO_USEC, {2, 6, 9, 9, 10}}, 0}, - {{SLP, 50 * MSEC_TO_USEC, 25 * MSEC_TO_USEC, {2, 7, 9, 9, 10}}, 1}, - {{SLP, 25 * MSEC_TO_USEC, 25 * MSEC_TO_USEC, {4, 7, 10, 10, 10}}, 1} -}; - -/* for tim > 10 */ -static struct iwl_power_vec_entry range_1[IWL_POWER_AC] = { - {{NOSLP, 0 * MSEC_TO_USEC, 0 * MSEC_TO_USEC, {0, 0, 0, 0, 0}}, 0}, - {{SLP, 200 * MSEC_TO_USEC, 500 * MSEC_TO_USEC, {1, 2, 3, 4, 0xFF}}, 0}, - {{SLP, 200 * MSEC_TO_USEC, 300 * MSEC_TO_USEC, {2, 4, 6, 7, 0xFF}}, 0}, - {{SLP, 50 * MSEC_TO_USEC, 100 * MSEC_TO_USEC, {2, 6, 9, 9, 0xFF}}, 0}, - {{SLP, 50 * MSEC_TO_USEC, 25 * MSEC_TO_USEC, {2, 7, 9, 9, 0xFF}}, 0}, - {{SLP, 25 * MSEC_TO_USEC, 25 * MSEC_TO_USEC, {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 { - max_sleep = (cmd->sleep_interval[IWL_POWER_TABLE_SIZE - 1] / - period) * period; - cmd->flags |= IWL_POWER_SLEEP_OVER_DTIM_MSK; - } - - for (i = 0; i < IWL_POWER_TABLE_SIZE; i++) { - if (cmd->sleep_interval[i] > max_sleep) - cmd->sleep_interval[i] = max_sleep; - } - - IWL_DEBUG_POWER("Flags value = 0x%08X\n", cmd->flags); - IWL_DEBUG_POWER("Tx timeout = %u\n", cmd->tx_data_timeout); - IWL_DEBUG_POWER("Rx timeout = %u\n", cmd->rx_data_timeout); - IWL_DEBUG_POWER - ("Sleep interval vector = { %d , %d , %d , %d , %d }\n", - cmd->sleep_interval[0], cmd->sleep_interval[1], - cmd->sleep_interval[2], cmd->sleep_interval[3], - cmd->sleep_interval[4]); - - return rc; -} - -static int iwl_send_power_mode(struct iwl_priv *priv, u32 mode) -{ - u32 final_mode = mode; - int rc; - unsigned long flags; - 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); - - spin_lock_irqsave(&priv->lock, flags); - - if (final_mode == IWL_POWER_MODE_CAM) { - priv->status &= ~STATUS_POWER_PMI; - } else { - priv->status |= STATUS_POWER_PMI; - } - - spin_unlock_irqrestore(&priv->lock, flags); - 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 - * @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(struct iwl_priv *priv, unsigned long ms) -{ - unsigned long now = jiffies; - - if (!(priv->status & STATUS_SCAN_HW)) { - priv->status &= ~STATUS_SCANNING; - return 0; - } - - if (priv->status & STATUS_SCAN_PENDING) { - IWL_DEBUG_SCAN("Canceling pending scan request.\n"); - priv->status &= ~STATUS_SCAN_PENDING; - } - - if (priv->status & STATUS_SCANNING) { - if (!(priv->status & STATUS_SCAN_ABORTING)) { - IWL_DEBUG_SCAN("Queuing scan abort.\n"); - priv->status |= STATUS_SCAN_ABORTING; - queue_work(priv->workqueue, &priv->abort_scan); - - } else { - IWL_DEBUG_SCAN("Scan abort already in progress.\n"); - } - - mutex_unlock(&priv->mutex); - if (ms) - while (!time_after(jiffies, - now + msecs_to_jiffies(ms)) && - priv->status & STATUS_SCANNING) - msleep(1); - - mutex_lock(&priv->mutex); - } - - return (priv->status & STATUS_SCANNING); -} - -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, 0); -} - -#if IWL == 4965 -#define MAX_UCODE_BEACON_INTERVAL 4096 -#else -#define MAX_UCODE_BEACON_INTERVAL 1024 -#endif -#define INTEL_CONN_LISTEN_INTERVAL 0xA - -static u16 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 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] = priv->timestamp1; - priv->rxon_timing.timestamp.dw[0] = 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 = 100; - priv->rxon_timing.beacon_init_val = 102400; - } else { - priv->rxon_timing.beacon_interval = beacon_int; - priv->rxon_timing.beacon_interval = - iwl_adjust_beacon_interval( - 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 = (priv->rxon_timing.beacon_interval * 1024); - result = do_div(tsf, interval_tm_unit); - priv->rxon_timing.beacon_init_val = - (u32) ((u64) interval_tm_unit - result); - - IWL_DEBUG_ASSOC - ("beacon interval %d beacon timer %d beacon tim %d\n", - priv->rxon_timing.beacon_interval, - priv->rxon_timing.beacon_init_val, 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 (priv->status & STATUS_SCANNING) { - IWL_DEBUG_SCAN("Scan already in progress.\n"); - return -EAGAIN; - } - - if (priv->status & STATUS_SCAN_ABORTING) { - IWL_DEBUG_SCAN("Scan request while abort pending. " - "Queuing.\n"); - return -EAGAIN; - } - - IWL_DEBUG_INFO("Starting scan...\n"); - priv->scan_bands = 2; - priv->status |= STATUS_SCANNING; - 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) || - (phymode == MODE_ATHEROS_TURBO)) { - 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, - 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 = 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, - priv->staging_rxon.channel); - - if (!ch_info || !is_channel_ibss(ch_info)) { - IWL_ERROR("channel %d not IBSS channel\n", - priv->staging_rxon.channel); - return -EINVAL; - } - } - - cancel_delayed_work(&priv->scan_check); - priv->status &= ~STATUS_SCAN_PENDING; - if (iwl_scan_cancel(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) -{ - u32 tx_flags; - u16 fc = le16_to_cpu(hdr->frame_control); - u16 *qc; - - tx_flags = cmd->cmd.tx.tx_flags; - - cmd->cmd.tx.stop_time.life_time = 0xFFFFFFFF; - if (!(ctrl->flags & IEEE80211_TXCTL_NO_ACK)) { - tx_flags |= TX_CMD_FLG_ACK_MSK; - if (WLAN_FC_GET_TYPE(fc) == 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) (*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 (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_MGMT) { - if (((WLAN_FC_GET_STYPE(fc)) == IEEE80211_STYPE_ASSOC_REQ) || - ((WLAN_FC_GET_STYPE(fc)) == IEEE80211_STYPE_REASSOC_REQ)) - cmd->cmd.tx.timeout.pm_frame_timeout = 3; - else - cmd->cmd.tx.timeout.pm_frame_timeout = 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 ((WLAN_FC_GET_TYPE(fc) != IEEE80211_FTYPE_DATA) || - is_multicast_ether_addr(hdr->addr1)) - return IWL_BROADCAST_ID; - - /* If this frame is part of a BSS network (we're a station), then - * we use the AP's station id */ - if (priv->iw_mode == IEEE80211_IF_TYPE_STA) - return IWL_AP_ID; - - /* If this frame is part of a IBSS network, then we use the - * target specific station id */ - if (priv->iw_mode == 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 | CMD_NO_LOCK)); - - 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)); - printk_buf(IWL_DL_DROP, (u8 *) hdr, sizeof(*hdr)); - return IWL_BROADCAST_ID; - } - - /* Otherwise we default to the broadcast station id */ - 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; - u8 *tfd; - u32 *control_flags; - int txq_id = ctl->queue; - struct iwl_tx_queue *txq = &priv->txq[txq_id]; - struct iwl_queue *q = &txq->q; - 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; - u16 *qc; - u8 wait_write_ptr = 0; - unsigned long flags; - int rc; - - spin_lock_irqsave(&priv->lock, flags); - if (priv->status & STATUS_RF_KILL_MASK) { - IWL_DEBUG_DROP("Dropping - RF KILL\n"); - goto drop; - } - - if (!priv->interface_id) { - IWL_DEBUG_DROP("Dropping - !priv->interface_id\n"); - goto drop; - } - - if ((ctl->tx_rate & 0xFF) == IWL_INVALID_RATE) { - IWL_ERROR("ERROR: No TX rate available.\n"); - goto drop; - } - - 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) && - (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA)) { - IWL_DEBUG_DROP("Dropping - !iwl_is_associated\n"); - goto drop; - } - - 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)); - spin_lock_irqsave(&priv->lock, flags); - goto drop; - } - - IWL_DEBUG_RATE("station Id %d\n", sta_id); - - qc = ieee80211_get_qos_ctrl(hdr); - if (qc) { - u8 tid = (u8)(*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 & IEEE80211_SCTL_FRAG); - seq_number += 0x10; - } - - spin_lock_irqsave(&priv->lock, flags); - tfd = (u8 *) (&txq->bd[q->first_empty * q->element_size]); - memset(tfd, 0, q->element_size); - control_flags = (u32 *) tfd; - idx = get_next_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 = 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_tx_queue_attach_buffer_to_tfd(priv, tfd, txcmd_phys, - cpu_to_le16(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 = cpu_to_le32(pci_map_single(priv->pci_dev, - skb->data + hdr_len, - len, PCI_DMA_TODEVICE)); - iwl_hw_tx_queue_attach_buffer_to_tfd(priv, tfd, phys_addr, - cpu_to_le16(len)); - } - - out_cmd->cmd.tx.len = skb->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 - - /* todoG 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); - - len = out_cmd->cmd.tx.len; - -#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)(*qc & 0xf); - priv->stations[sta_id].tid[tid].seq_number = - seq_number; - } - } else { - wait_write_ptr = 1; - txq->need_update = 0; - } - - printk_buf(IWL_DL_TX, out_cmd->cmd.payload, sizeof(out_cmd->cmd.tx)); - - printk_buf(IWL_DL_TX, (u8 *) out_cmd->cmd.tx.hdr, - ieee80211_get_hdrlen(out_cmd->cmd.tx.hdr->frame_control)); - - 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: - spin_unlock_irqrestore(&priv->lock, flags); - - 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) || - (hw->mode == MODE_ATHEROS_TURBO)) ? - '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) == - ((priv->status & STATUS_RF_KILL_SW) ? 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, 0); - /* 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); - priv->status |= STATUS_RF_KILL_SW; - } - return; - } - - spin_lock_irqsave(&priv->lock, flags); - iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); - - priv->status &= ~STATUS_RF_KILL_SW; - 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 (priv->status & STATUS_RF_KILL_HW) { - 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; - u16 channel = stats->channel; - int rate = stats->rate; - u64 tsf = stats->mactime; - - /* 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 data and set the flags */ - iwl_rt->rt_channel = cpu_to_le16(channel); - if (!(phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK)) { - iwl_rt->rt_chbitmask = - cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ)); - } else if (phy_flags & 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 = (phy_flags & RX_RES_PHY_FLAGS_ANTENNA_MSK) >> 4; - - /* set the preamble flag if we have it */ - if (phy_flags & 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 = WLAN_GET_SEQ_SEQ(sc); - u16 frag = WLAN_GET_SEQ_FRAG(sc); - 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; - - __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) % 0x100; - 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 u32 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 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 = params->start_time; - int rc; - - if (iwl_is_associated(priv)) - add_time = - iwl_usecs_to_beacons(params->start_time - priv->last_tsf, - priv->rxon_timing.beacon_interval); - - memset(&spectrum, 0, sizeof(spectrum)); - - spectrum.channel_count = 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 = cmd.len - sizeof(spectrum.len); - - if (iwl_is_associated(priv)) - spectrum.start_time = - iwl_add_beacon_time(priv->last_beacon_time, - add_time, - priv->rxon_timing.beacon_interval); - else - spectrum.start_time = params->start_time; - - spectrum.channels[0].duration = params->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(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; - } - - switch (res->u.spectrum.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. - */ -static 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; - u8 is_next = 0; - int used; - if ((index >= q->n_bd) || (x2_queue_used(q, index) == 0)) { - IWL_ERROR("Read index for DMA queue (%d) is out of " - "range [0-%d) %d %d\n", - index, q->n_bd, q->first_empty, q->last_used); - goto done; - } - index = iwl_queue_inc_wrap(index, q->n_bd); - for (; q->last_used != index; - q->last_used = iwl_queue_inc_wrap(q->last_used, q->n_bd)) { - if (is_next) { - IWL_WARNING("command skipped\n"); - queue_work(priv->workqueue, &priv->restart); - } - if (txq_id != IWL_CMD_QUEUE_NUM) { - iwl_txstatus_to_ieee(priv, - &(txq->txb[txq->q.last_used])); - iwl_hw_tx_queue_free_tfd(priv, txq); - } else - is_next = 1; - } - done: - 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); - - used = q->first_empty - q->last_used; - if (used < 0) - used += q->n_window; - return used; -} - -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 - * - ******************************************************************************/ - -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 = 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 *status; - struct iwl_tx_resp *resp = (void *)&pkt->u.raw[0]; - - if ((index >= txq->q.n_bd) || (x2_queue_used(&txq->q, index) == 0)) { - IWL_ERROR("Read index for DMA queue (%d) " - "is out of range [0-%d) %d %d\n", - index, txq->q.n_bd, txq->q.first_empty, - txq->q.last_used); - return; - } - - status = &(txq->txb[txq->q.last_used].status); - - status->retry_count = resp->failure_frame; - status->queue_number = resp->status; - status->queue_length = resp->bt_kill_count; - status->queue_length |= resp->failure_rts; - - status->flags = - iwl_is_tx_success(resp->status) ? IEEE80211_TX_STATUS_ACK : 0; - -#if IWL == 3945 - - status->control.tx_rate = iwl_rate_index_from_plcp(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(resp->status), - resp->status, resp->rate, resp->failure_frame); - -#elif IWL == 4965 - - status->control.tx_rate = resp->rate.s.rate | (resp->rate.s.flags << 8); - - IWL_DEBUG_TX("Tx queue %d Status %s (0x%08x) plcp rate %d retries %d\n", - txq_id, iwl_get_tx_fail_reason(resp->status), - resp->status, resp->rate.s.rate, 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_check_bits(resp->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 work_struct *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; - u32 err_type = pkt->u.err_resp.error_type; - u8 cmd_id = pkt->u.err_resp.cmd_id; - u16 seq = pkt->u.err_resp.bad_cmd_seq_num; - u32 ser = pkt->u.err_resp.error_info; - IWL_ERROR("Error Reply type 0x%08X cmd %s (0x%02X) " - "seq 0x%04X ser 0x%08X\n", - err_type, get_cmd_string(cmd_id), cmd_id, seq, ser); - return; -} - -#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", - csa->channel, csa->status); - rxon->channel = priv->staging_rxon.channel = cpu_to_le16(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)); - printk_buf(IWL_DL_RADIO, pkt->u.raw, le32_to_cpu(pkt->len)); -} - -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 = beacon->beacon_notify_hdr.rate.s.rate; -#endif - IWL_DEBUG_RX("beacon status %x retries %d iss %d " - "tsf %d %d rate %d\n", - beacon->beacon_notify_hdr.status & TX_STATUS_MSK, - beacon->beacon_notify_hdr.failure_frame, - beacon->ibss_mgr_status, - beacon->high_tsf, beacon->low_tsf, rate); -#endif -} - -static void iwl_rx_reply_scan(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) -{ - 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); -} - -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 = 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); -} - -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", - notif->tsf_high, - notif->tsf_low, - notif->statistics[0], - notif->tsf_low - priv->scan_start_tsf, - jiffies_to_msecs(elapsed_jiffies - (priv->last_scan_jiffies, jiffies))); - - priv->last_scan_jiffies = jiffies; -} - -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 = - (struct iwl_scancomplete_notification *)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 */ - priv->status &= ~STATUS_SCAN_HW; - - /* 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 (priv->status & STATUS_SCAN_ABORTING) { - IWL_DEBUG_INFO("Aborted scan completed.\n"); - priv->status &= ~STATUS_SCAN_ABORTING; - } 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"); - - priv->status &= ~STATUS_SCANNING; - - 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); - u32 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) - priv->status |= STATUS_RF_KILL_HW; - else - priv->status &= ~STATUS_RF_KILL_HW; - - - if (flags & SW_CARD_DISABLED) - priv->status |= STATUS_RF_KILL_SW; - else - priv->status &= ~STATUS_RF_KILL_SW; - -#if IWL == 4965 - if (!(flags & RXON_CARD_DISABLED)) - iwl_scan_cancel(priv, 0); -#else - iwl_scan_cancel(priv, 0); -#endif - - if (((status & STATUS_RF_KILL_HW) != (priv->status & STATUS_RF_KILL_HW)) - || ((status & STATUS_RF_KILL_SW) != - (priv->status & STATUS_RF_KILL_SW))) - 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; - int txq_id = SEQ_TO_QUEUE(pkt->hdr.sequence); - int index = SEQ_TO_INDEX(pkt->hdr.sequence); - int is_huge = (pkt->hdr.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. */ - WARN_ON(txq_id != IWL_CMD_QUEUE_NUM); - if (txq_id != IWL_CMD_QUEUE_NUM) { - IWL_ERROR("Kernel BUG! Please report below info:\n"); - IWL_ERROR("Error wrong command queue %d command id 0x%X\n", - txq_id, pkt->hdr.cmd); - } - - cmd_index = get_next_cmd_index(&priv->txq[IWL_CMD_QUEUE_NUM].q, index, - is_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) { - /* FIXME: we use cmd->meta.magic to indicate the - * memory cmd->meta.source points to is still valid or - * not at this point since caller may pass a local - * variable to us and returned before we get here. - * In this case, caller must ensure the ->magic field - * is set correctly to indicate the availability of the - * pointer cmd->meta.source. */ - if (cmd->meta.source->magic == CMD_VAR_MAGIC) { - cmd->meta.source->u.skb = rxb->skb; - cmd->meta.source->magic = 0; - 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)) { - priv->status &= ~STATUS_HCMD_ACTIVE; - 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 (priv->status & STATUS_POWER_PMI) { - 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 counter = 0, 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); - ((u32 *) rxq->bd)[rxq->write] = - iwl_dma_addr2rbd_ptr(priv, rxb->dma_addr); - rxq->queue[rxq->write] = rxb; - rxq->write = (rxq->write + 1) % RX_QUEUE_SIZE; - rxq->free_count--; - counter++; - } - 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); - - counter = iwl_rx_queue_space(rxq); - /* If we've added more space for the firmware to place data, tell it */ - if ((write != (rxq->write & ~0x7) || (rxq->write < write)) - || (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, u8 do_lock) -{ - 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 = 0; - - 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); - - if (do_lock) - spin_lock_irqsave(&priv->lock, flags); - iwl_rx_queue_restock(priv); - if (do_lock) - 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 */ -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 response and 802.11 - * frame availability. - */ -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; - - 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; - - /* need to reclaim cmd buffer(s) */ - reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME) && -#if IWL == 4965 - (pkt->hdr.cmd != REPLY_RX_PHY_CMD) && -#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]) { - priv->rx_handlers[pkt->hdr.cmd] (priv, rxb); - IWL_DEBUG(IWL_DL_RX | IWL_DL_ISR, - "r = %d, i = %d, rx_handler %s\n", r, i, - get_cmd_string(pkt->hdr.cmd)); - } else { - /* No handling needed */ - IWL_DEBUG_HC("UNHANDLED - #0x%02x %s\n", - pkt->hdr.cmd, - get_cmd_string(pkt->hdr.cmd)); - } - - if (reclaim) { - /* Invoke any callbacks, transfer the skb to - * caller, and fire off the (possibly) blocking - * iwl_send_cmd() via as we reclaim the 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_SIZE; - } - - /* Backtrack one entry */ - priv->rxq.read = i; - - /* is a lot of queue space refill up right away - * so ucode wont assert */ - if (iwl_rx_queue_space(rxq) > RX_SPACE_HIGH_MARK) - iwl_rx_replenish(priv, 0); - else - 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 (priv->status & STATUS_POWER_PMI) { - /* 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"); - printk_buf(IWL_DL_RADIO, (u8 *) rxon, sizeof(*rxon)); - IWL_DEBUG_RADIO("u16 channel: 0x%x\n", rxon->channel); - IWL_DEBUG_RADIO("u32 flags: 0x%08X " BIT_FMT32 "\n", - rxon->flags, BIT_ARG32(rxon->flags)); - IWL_DEBUG_RADIO("u32 filter_flags: 0x%08x " BIT_FMT32 "\n", - rxon->filter_flags, BIT_ARG32(rxon->filter_flags)); - IWL_DEBUG_RADIO("u8 dev_type: 0x%x\n", rxon->dev_type); - IWL_DEBUG_RADIO("u8 ofdm_basic_rates: 0x%02x " BIT_FMT8 "\n", - rxon->ofdm_basic_rates, - BIT_ARG8(rxon->ofdm_basic_rates)); - IWL_DEBUG_RADIO("u8 cck_basic_rates: 0x%02x " BIT_FMT8 "\n", - rxon->cck_basic_rates, BIT_ARG8(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", rxon->assoc_id); -} - -#endif - -static void iwl_enable_interrupts(struct iwl_priv *priv) -{ - IWL_DEBUG_ISR("Enabling interrupts\n"); - priv->status |= STATUS_INT_ENABLED; - iwl_write32(priv, CSR_INT_MASK, CSR_INI_SET_MASK); -} - -static inline void iwl_disable_interrupts(struct iwl_priv *priv) -{ - priv->status &= ~STATUS_INT_ENABLED; - - /* 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 = priv->card_alive.error_event_table_ptr; - - if (!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%08X, 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 = 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 = priv->card_alive.log_event_table_ptr; - if (!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 */ - priv->status |= STATUS_FW_ERROR; - - /* Cancel currently queued command. */ - priv->status &= ~STATUS_HCMD_ACTIVE; - - IWL_WARNING("RX write index: %d read index %d\n", - priv->rxq.write, priv->rxq.read); - -#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 */ - priv->status &= ~STATUS_READY; - if (!(priv->status & STATUS_EXIT_PENDING)) { - 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 = 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, inta_mask, handled = 0; - u32 inta_fh; - unsigned long flags; - - 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); - - 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); - - /* 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; - } - - 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 && !(priv->status & STATUS_ALIVE)) - queue_work(priv->workqueue, &priv->restart); - - handled |= BIT_INT_RF_KILL; - } - - if (inta & BIT_INT_CT_KILL) { - IWL_ERROR("Microcode CT kill error detected.\n"); - handled |= BIT_INT_CT_KILL; - } - - 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; - } - - 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; - } - - /* Alive notification via Rx interrupt will do the real work */ - if (inta & BIT_INT_ALIVE) { - IWL_DEBUG_ISR("Alive interrupt\n"); - handled |= BIT_INT_ALIVE; - } - - /* 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 pt_regs *regs) -{ - 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 == 0xa5a5a5a5) - || (inta == 0x5a5a5a5a)) { - /* 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 = GLOBAL_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 = GLOBAL_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 = GLOBAL_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 = GLOBAL_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 = GLOBAL_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, int channel) -{ - int i; - - switch (phymode) { - case MODE_ATHEROS_TURBO: - 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: - case MODE_ATHEROS_TURBOG: - 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 = - GLOBAL_ARRAY_SIZE(iwl_eeprom_band_1) + - GLOBAL_ARRAY_SIZE(iwl_eeprom_band_2) + - GLOBAL_ARRAY_SIZE(iwl_eeprom_band_3) + - GLOBAL_ARRAY_SIZE(iwl_eeprom_band_4) + - GLOBAL_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 Tx\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(" BIT_FMT8 - " %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), - BIT_ARG8(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 (1) /* packets */ -#define IWL_ACTIVE_QUIET_TIME (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) || - (phymode == MODE_ATHEROS_TURBO)) - 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) && - (phymode != MODE_ATHEROS_TURBO)) ? - 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 = active_dwell; - scan_ch->passive_dwell = 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) || - (phymode == MODE_ATHEROS_TURBO)) { - 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"); - priv->status |= STATUS_GEO_CONFIGURED; - return 0; - } - - modes = kzalloc(sizeof(struct ieee80211_hw_mode) * mode_count, - GFP_ATOMIC); - if (!modes) - return -ENOMEM; - - channels = kzalloc(sizeof(struct ieee80211_channel) * - priv->channel_count, GFP_ATOMIC); - if (!channels) { - kfree(modes); - return -ENOMEM; - } - - rates = kzalloc((sizeof(struct ieee80211_rate) * (IWL_MAX_RATES + 1)), - GFP_ATOMIC); - 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[GLOBAL_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_ATHEROS_TURBOG; - 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_ATHEROS_TURBO; - modes[A_11N].channels = &channels[GLOBAL_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_WARNING("Channel %d [%sGHz] is Tx only -- " - "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; - priv->status |= STATUS_GEO_CONFIGURED; - - 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) -{ - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - - /* Remove all resets to allow NIC to operate */ - iwl_write32(priv, CSR_RESET, 0); - - spin_unlock_irqrestore(&priv->lock, flags); -} - -/** - * 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 - const char *name = "iwlwifi-3945.ucode"; /* firmware file name */ -#elif IWL == 4965 - const char *name = "iwlwifi-4965.ucode"; /* firmware file name */ -#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 call, file is in memory on return. - * TODO: Would it be more polite to use asynchronous - * request_firmware_nowait()?? If so, put request back into - * iwl_pci_probe(), and rest of this function would serve as - * the callback for request_firmware_nowait(). Also need to - * make sure everything waits for this callback to complete! */ - 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 | 0x80000000); - - 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 != 1) { - /* 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 != 1) { - /* 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) { - priv->status &= ~STATUS_RF_KILL_HW; - /* 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 - priv->status |= STATUS_RF_KILL_HW; -#endif - - /* After the ALIVE response, we can process host commands */ - priv->status |= STATUS_ALIVE; - - /* Clear out the uCode error bit if it is set */ - priv->status &= ~STATUS_FW_ERROR; - - rc = iwl_init_channel_map(priv); - if (rc) { - IWL_ERROR("initializing regulatory failed: %d\n", rc); - return; - } - - iwl_init_geos(priv); - - if (priv->status & STATUS_RF_KILL_MASK) - 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(); - 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)); - - /* Initialize our rx_config data */ - iwl_connection_init_rx_config(priv); - memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN); - - 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; - } - - /* 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; - priv->status |= STATUS_READY; - - 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); - -void iwl_down(struct iwl_priv *priv) -{ - unsigned long flags; - int exit_pending = priv->status & STATUS_EXIT_PENDING; - struct ieee80211_conf *conf = NULL; - - IWL_WARNING("ipw going down \n"); - - conf = ieee80211_get_hw_conf(priv->hw); - - priv->status |= STATUS_EXIT_PENDING; - - 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) - priv->status &= ~STATUS_EXIT_PENDING; - - /* 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 &= (STATUS_RF_KILL_MASK | 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 &= - (STATUS_RF_KILL_MASK | STATUS_IN_SUSPEND | 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); - - spin_lock_irqsave(&priv->lock, flags); - iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); - spin_unlock_irqrestore(&priv->lock, flags); - - 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); -} - -#define MAX_HW_RESTARTS 5 - -static int iwl_up(struct iwl_priv *priv) -{ - int rc, i; - u32 hw_rf_kill = 0; - - if (priv->status & STATUS_EXIT_PENDING) { - IWL_WARNING("Exit pending; will not bring the NIC up\n"); - return -EIO; - } - - if (priv->status & STATUS_RF_KILL_SW) { - 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 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 ((priv->status & STATUS_RF_KILL_HW) || hw_rf_kill) { - IWL_WARNING("Radio disabled by HW RF Kill switch\n"); - return 0; - } - - 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; - } - - priv->status |= STATUS_EXIT_PENDING; - 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(void *p) -{ - struct iwl_priv *priv = p; - - if (priv->status & STATUS_EXIT_PENDING) - return; - - mutex_lock(&priv->mutex); - iwl_init_alive_start(priv); - mutex_unlock(&priv->mutex); -} - -static void iwl_bg_alive_start(void *p) -{ - struct iwl_priv *priv = p; - - if (priv->status & STATUS_EXIT_PENDING) - return; - - mutex_lock(&priv->mutex); - iwl_alive_start(priv); - mutex_unlock(&priv->mutex); -} - -static void iwl_bg_rf_kill(void *p) -{ - struct iwl_priv *priv = p; - - wake_up_interruptible(&priv->wait_command_queue); - - if (priv->status & STATUS_EXIT_PENDING) - return; - - mutex_lock(&priv->mutex); - - if (!(priv->status & STATUS_RF_KILL_MASK)) { - IWL_DEBUG(IWL_DL_INFO | IWL_DL_RF_KILL, - "HW and/or SW RF Kill no longer active, restarting " - "device\n"); - if (!(priv->status & STATUS_EXIT_PENDING)) - queue_work(priv->workqueue, &priv->restart); - } else { - - if (!(priv->status & STATUS_RF_KILL_HW)) - 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(void *p) -{ - struct iwl_priv *priv = p; - - if (priv->status & STATUS_EXIT_PENDING) - return; - - mutex_lock(&priv->mutex); - if (priv->status & (STATUS_SCANNING | STATUS_SCAN_ABORTING)) { - IWL_DEBUG(IWL_DL_INFO | IWL_DL_SCAN, - "Scan completion watchdog resetting " - "adapter (%dms).\n", - jiffies_to_msecs(IWL_SCAN_CHECK_WATCHDOG)); - if (!(priv->status & STATUS_EXIT_PENDING)) - queue_work(priv->workqueue, &priv->restart); - } - mutex_unlock(&priv->mutex); -} - -static void iwl_bg_request_scan(void *p) -{ - struct iwl_priv *priv = p; - 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 (!(priv->status & STATUS_SCANNING)) - goto done; - - /* This should never be called or scheduled if there is currently - * a scan active in the hardware. */ - if (priv->status & STATUS_SCAN_HW) { - IWL_DEBUG_INFO - ("Multiple concurrent scan requests in parallel. " - "Ignoring second request.\n"); - rc = -EIO; - goto done; - } - - if (priv->status & STATUS_EXIT_PENDING) { - IWL_DEBUG_SCAN("Aborting scan due to device shutdown\n"); - goto done; - } - - if (priv->status & STATUS_SCAN_ABORTING) { - IWL_DEBUG_HC("Scan request while abort pending. Queuing.\n"); - goto done; - } - - if (priv->status & STATUS_RF_KILL_MASK) { - IWL_DEBUG_HC("Aborting scan due to RF Kill activation\n"); - goto done; - } - - if (!(priv->status & STATUS_READY)) { - 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_ATOMIC); - 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; - 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 = 600 * 1024; - if (interval) { -#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)); - IWL_DEBUG_SCAN("suspend_time 0x%X beacon interval %d\n", - scan->suspend_time, interval); -#endif - } - } - - /* 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 = - 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 |= 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.s.rate = IWL_RATE_1M_PLCP; - scan->tx_cmd.rate.s.flags = 0x80 | 0x2; -#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.s.rate = IWL_RATE_6M_PLCP; - scan->tx_cmd.rate.s.flags = 0x80; -#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 | - (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[scan->tx_cmd.len]); - - cmd.len += scan->tx_cmd.len + - scan->channel_count * sizeof(struct iwl_scan_channel); - cmd.data = scan; - scan->len = cmd.len; - - priv->status |= STATUS_SCAN_HW; - rc = iwl_send_cmd(priv, &cmd); - if (rc) - goto done; - - queue_delayed_work(priv->workqueue, &priv->scan_check, - IWL_SCAN_CHECK_WATCHDOG); - - priv->status &= ~STATUS_SCAN_PENDING; - - 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(void *p) -{ - struct iwl_priv *priv = p; - - if (priv->status & STATUS_EXIT_PENDING) - return; - - mutex_lock(&priv->mutex); - iwl_up(priv); - mutex_unlock(&priv->mutex); -} - -static void iwl_bg_restart(void *p) -{ - struct iwl_priv *priv = p; - - if (priv->status & STATUS_EXIT_PENDING) - return; - - mutex_lock(&priv->mutex); - iwl_down(priv); - mutex_unlock(&priv->mutex); - - queue_work(priv->workqueue, &priv->up); -} - -static void iwl_bg_rx_replenish(void *p) -{ - struct iwl_priv *priv = p; - - if (priv->status & STATUS_EXIT_PENDING) - return; - - mutex_lock(&priv->mutex); - iwl_rx_replenish(priv, 1); - mutex_unlock(&priv->mutex); -} - -static void iwl_bg_post_associate(void *p) -{ - struct iwl_priv *priv = p; - - 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 (priv->status & STATUS_EXIT_PENDING) - 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 = 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_sync_station(priv, IWL_AP_ID, - ((priv->phymode == MODE_IEEE80211A) || - (priv->phymode == MODE_ATHEROS_TURBO)) ? - IWL_RATE_6M_PLCP : IWL_RATE_1M_PLCP, - CMD_ASYNC); - 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); - - iwl_sync_station(priv, IWL_STA_ID, - ((priv->phymode == MODE_IEEE80211A) || - (priv->phymode == MODE_ATHEROS_TURBO)) ? - 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 */ - - mutex_unlock(&priv->mutex); -} - -static void iwl_bg_abort_scan(void *p) -{ - struct iwl_priv *priv = p; - - if (!iwl_is_ready(priv)) - return; - - mutex_lock(&priv->mutex); - priv->status &= ~STATUS_SCAN_PENDING; - priv->status |= STATUS_SCAN_ABORTING; - - iwl_send_scan_abort(priv); - - mutex_unlock(&priv->mutex); -} - -static void iwl_bg_scan_completed(void *p) -{ - struct iwl_priv *priv = p; - - IWL_DEBUG(IWL_DL_INFO | IWL_DL_SCAN, "SCAN complete scan\n"); - - if (priv->status & STATUS_EXIT_PENDING) - 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 d_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 (!(priv->status & STATUS_RF_KILL_MASK)) - ieee80211_start_queues(priv->hw); - - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211("leave\n"); - return 0; -} - -static int d_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 d_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 d_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; -} - -/** - * d_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 d_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 - (!param_disable_hw_scan && (priv->status & STATUS_SCANNING))) { - 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 (is_channel_bg_band(ch_info) && - (priv->staging_rxon.channel != conf->channel)) - 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); - - 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 (priv->status & STATUS_RF_KILL_MASK) { - 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 int d_config_interface(struct ieee80211_hw *hw, - int if_id, struct ieee80211_if_conf *conf) -{ - struct iwl_priv *priv = hw->priv; - unsigned long flags; - - 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(priv->status & STATUS_SCANNING) && - !(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(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); - priv->staging_rxon.filter_flags |= RXON_FILTER_ASSOC_MSK; - - /* 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); - - iwl_commit_rxon(priv); - if (priv->iw_mode != IEEE80211_IF_TYPE_AP) - iwl_rxon_add_station( - priv, priv->active_rxon.bssid_addr, 1); - - if (priv->iw_mode == IEEE80211_IF_TYPE_AP) { - /* FIXME: The unlock here is a patch. the Locks - * should be moved out of iwl_bg_post_associate */ - mutex_unlock(&priv->mutex); - iwl_bg_post_associate(&priv->post_associate); - mutex_lock(&priv->mutex); - } - } 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 d_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 d_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 d_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 (!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 &= (u32) - (~IEEE80211_KEY_FORCE_SW_ENCRYPT); - key->hw_key_idx = sta_id; - /* TODO do we need below */ - /* - * conf->sw_encrypt = 0; - * conf->sw_decrypt = 0; - */ - IWL_DEBUG_MAC80211("set_key success, using hwcrypto\n"); - } - - IWL_DEBUG_MAC80211("leave\n"); - mutex_unlock(&priv->mutex); - - return rc; -} - -static int d_conf_tx(struct ieee80211_hw *hw, int queue, - const struct ieee80211_tx_queue_params *params) -{ - struct iwl_priv *priv = hw->priv; - - 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\n"); - return 0; - } - IWL_DEBUG_MAC80211("leave\n"); - return 0; -} - -static int d_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 d_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 d_get_tsf(struct ieee80211_hw *hw) -{ - IWL_DEBUG_MAC80211("enter\n"); - IWL_DEBUG_MAC80211("leave\n"); - - return 0; -} - -static void d_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 -#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); -#endif /* CONFIG_IWLWIFI_HT */ -#endif /* IWL == 4965 */ - - 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; - priv->assoc_station_added = 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 d_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); - - queue_work(priv->workqueue, &priv->post_associate); - - 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 d_conf_ht(struct ieee80211_hw *hw, - struct ieee80211_ht_capability *ht_cap_param, - struct ieee80211_ht_additional_info *ht_extra_param) -{ - struct iwl_priv *priv = hw->priv; - struct sta_ht_info *ht_info = &priv->current_assoc_ht; - 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"); - cap.val = (u16) le16_to_cpu(ht_cap_param->capabilities_info); - op_mode.val = (u16) le16_to_cpu(ht_extra_param->operation_mode); - param_info.val = ht_cap_param->mac_ht_params_info; - extra_param_info.val = ht_extra_param->ht_param; - - ht_info->is_ht = 1; - ht_info->ampdu_factor = param_info.max_rx_ampdu_factor; - ht_info->control_chan = ht_extra_param->control_chan; - ht_info->is_green_field = cap.green_field; - if (cap.short_GI20) - ht_info->sgf |= 0x1; - if (cap.short_GI40) - ht_info->sgf |= 0x2; - ht_info->operating_mode = op_mode.op_mode; - ht_info->mpdu_density = param_info.mpdu_density; - ht_info->max_amsdu_size = cap.maximal_amsdu_size; - ht_info->supported_chan_width = cap.supported_chan_width_set; - ht_info->extension_chan_offset = extra_param_info.ext_chan_offset; - ht_info->tx_mimo_ps_mode = cap.mimo_power_save_mode; - - memcpy(ht_info->supp_rates, ht_cap_param->supported_mcs_set, 16); - - 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); - - 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_param->control_chan); - return 0; -} - -static void d_get_ht_capab(struct ieee80211_hw *hw, - struct ieee80211_ht_capability *ht_cap_param) -{ - union ht_cap_info cap; - union ht_param_info param_info; - - IWL_DEBUG_MAC80211("enter: \n"); - 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 = 1; - 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_param->capabilities_info = (__le16) cpu_to_le16(cap.val); - ht_cap_param->mac_ht_params_info = (u8) param_info.val; - - ht_cap_param->supported_mcs_set[0] = 0xff; - ht_cap_param->supported_mcs_set[1] = 0xff; - ht_cap_param->supported_mcs_set[4] = - (cap.supported_chan_width_set) ? 0x1: 0x0; - - IWL_DEBUG_MAC80211("leave: \n"); -} - - - -#endif /*CONFIG_IWLWIFI_HT*/ - -#ifdef CONFIG_IWLWIFI_HT_AGG - -static int kdrn_txq_ctx_activate_free(struct iwl_priv *priv); -static int d_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 ipw_tid_data *tid_data; - - if (likely(tid < ARRAY_SIZE(default_tid_to_ac))) - tx_fifo = default_tid_to_ac[tid]; - else - return -EINVAL; - - IWL_DEBUG_MAC80211("iwl-AGG d_ht_tx_agg_start on da=" MAC_FMT " tid=" - "%d\n", MAC_ARG(da), tid); - - sta_id = ipw_find_station(priv, da); - if (sta_id == IPW_INVALID_STATION) - return -ENXIO; - - txq_id = kdrn_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->txq_id = txq_id; - spin_unlock_irqrestore(&priv->sta_lock, flags); - - *start_seq_num = ssn; - return kdrn_tx_queue_agg_enable(priv, txq_id, tx_fifo, sta_id, tid, - ssn); -} - - -static int d_ht_tx_agg_stop(struct ieee80211_hw *hw, - u8 *da, u16 tid, int generator) -{ - - struct iwl_priv *priv = hw->priv; - int tx_fifo_id, sta_id, ssn = -1; - 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 = ipw_find_station(priv, da); - - if (sta_id == IPW_INVALID_STATION) - return -ENXIO; - - ssn = (priv->stations[sta_id].tid[tid].seq_number & - IEEE80211_SCTL_SEQ) >> 4; - - kdrn_tx_queue_agg_disable(priv, 7, ssn, tx_fifo_id); - IWL_DEBUG_MAC80211("iwl-AGG d_ht_tx_agg_stop on da=" MAC_FMT " tid=" - "%d\n", MAC_ARG(da), tid); - - return 0; -} - -#endif /*CONFIG_IWLWIFI_HT_AGG*/ -#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 = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) | - ((priv->status & STATUS_RF_KILL_HW) ? 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; - u16 flags = simple_strtoul(buf, NULL, 0); - - mutex_lock(&priv->mutex); - if (priv->staging_rxon.flags != flags) { - /* Cancel any currently running scans... */ - if (iwl_scan_cancel(priv, 100)) { - IWL_WARNING("Could not cancel scan.\n"); - } else { - IWL_DEBUG_INFO("Committing rxon.flags = 0x%04X\n", - flags); - priv->staging_rxon.flags = 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", 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; - u16 filter_flags = simple_strtoul(buf, NULL, 0); - - mutex_lock(&priv->mutex); - if (priv->staging_rxon.filter_flags != filter_flags) { - /* Cancel any currently running scans... */ - if (iwl_scan_cancel(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 = 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) | 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; - u8 channel = tune & 0xff; - - IWL_DEBUG_INFO("Tune request to:%d channel:%d\n", phymode, channel); - - mutex_lock(&priv->mutex); - if ((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(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)) { - len += - snprint_line(&buf[len], PAGE_SIZE - len, - &data[ofs], min(size, 16U), ofs); - 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 = priv->active_rxon.channel, - .start_time = priv->last_tsf, - .duration = 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 */ - -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_IBSS) - i = priv->stations[IWL_STA_ID].current_rate.s.rate; - else - i = priv->stations[IWL_AP_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 % 2) ? ".5" : ""); -} - -static DEVICE_ATTR(rate, S_IRUSR, show_rate, NULL); - -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)) { - len += - snprint_line(&buf[len], PAGE_SIZE - len, - &data[ofs], min(size, 16U), ofs); - 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, priv); - INIT_WORK(&priv->restart, iwl_bg_restart, priv); - INIT_WORK(&priv->rx_replenish, iwl_bg_rx_replenish, priv); - INIT_WORK(&priv->scan_completed, iwl_bg_scan_completed, priv); - INIT_WORK(&priv->request_scan, iwl_bg_request_scan, priv); - INIT_WORK(&priv->abort_scan, iwl_bg_abort_scan, priv); - INIT_WORK(&priv->rf_kill, iwl_bg_rf_kill, priv); - INIT_WORK(&priv->post_associate, iwl_bg_post_associate, priv); - INIT_WORK(&priv->init_alive_start, iwl_bg_init_alive_start, priv); - INIT_WORK(&priv->alive_start, iwl_bg_alive_start, priv); - INIT_WORK(&priv->scan_check, iwl_bg_scan_check, priv); - - 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); -} - -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, - &dev_attr_rate.attr, - &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 = d_tx, - .open = d_open, - .stop = d_stop, - .add_interface = d_add_interface, - .remove_interface = d_remove_interface, - .config = d_config, - .config_interface = d_config_interface, - .set_key = d_set_key, - .get_stats = d_get_stats, - .get_tx_stats = d_get_tx_stats, - .conf_tx = d_conf_tx, - .get_tsf = d_get_tsf, - .reset_tsf = d_reset_tsf, - .beacon_update = d_beacon_update, -#if IWL == 4965 -#ifdef CONFIG_IWLWIFI_HT - .conf_ht = d_conf_ht, - .get_ht_capab = d_get_ht_capab, -#ifdef CONFIG_IWLWIFI_HT_AGG - .ht_tx_agg_start = d_ht_tx_agg_start, - .ht_tx_agg_stop = d_ht_tx_agg_stop, -#endif /* CONFIG_IWLWIFI_HT_AGG */ -#endif /* CONFIG_IWLWIFI_HT */ -#endif - .hw_scan = d_hw_scan - -}; - -static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - int err = 0; - void __iomem *base; - u32 length; - u32 pci_id; - struct iwl_priv *priv; - struct ieee80211_hw *hw; - int i; - - if (param_disable_hw_scan) { - IWL_DEBUG_INFO("Disabling hw_scan\n"); - iwl_hw_ops.hw_scan = NULL; - } - - /* 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)param_antenna; -#ifdef CONFIG_IWLWIFI_DEBUG - iwl_debug_level = param_debug; -#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; - - spin_lock_init(&priv->lock); - spin_lock_init(&priv->power_data.lock); - spin_lock_init(&priv->sta_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); - - memset(&(priv->txq[0]), 0, - sizeof(struct iwl_tx_queue) * IWL_MAX_NUM_QUEUES); - memset(&priv->card_alive, 0, sizeof(struct iwl_alive_resp)); - 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); - - length = pci_resource_len(pdev, 0); - priv->hw_len = length; - base = ioremap_nocache(pci_resource_start(pdev, 0), length); - if (!base) { - err = -ENODEV; - goto out_pci_release_regions; - } - - priv->hw_base = base; - IWL_DEBUG_INFO("pci_resource_len = 0x%08x\n", length); - IWL_DEBUG_INFO("pci_resource_base = %p\n", base); - - /* Initialize module parameter values here */ - - if (param_disable) { - priv->status |= STATUS_RF_KILL_SW; - 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; - } - - 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; - 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_destroy_workqueue; - } - - 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_destroy_workqueue: - destroy_workqueue(priv->workqueue); - priv->workqueue = NULL; - iwl_unset_hw_setting(priv); - - out_iounmap: - iounmap(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); - - priv->status |= STATUS_EXIT_PENDING; - - 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(); - } - - /* ieee80211_unregister_hw calls d_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); - iounmap(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); - - priv->status |= STATUS_IN_SUSPEND; - - /* 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); - spin_unlock_irqrestore(&priv->lock, flags); - - 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_reset(priv); - - /* Bring the device back up */ - priv->status &= ~STATUS_IN_SUSPEND; - 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, param_antenna, int, 0444); -MODULE_PARM_DESC(antenna, "select antenna (1=Main, 2=Aux, default 0 [both])"); -module_param_named(disable, param_disable, int, 0444); -MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])"); -module_param_named(hwcrypto, param_hwcrypto, int, 0444); -MODULE_PARM_DESC(hwcrypto, - "using hardware crypto engine (default 0 [software])\n"); -module_param_named(debug, param_debug, int, 0444); -MODULE_PARM_DESC(debug, "debug output mask"); -module_param_named(disable_hw_scan, param_disable_hw_scan, int, 0444); -MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 0)"); - -/* QoS */ -module_param_named(qos_enable, 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/iwlwifi/iwl-3945-commands.h b/drivers/net/wireless/iwlwifi/iwl-3945-commands.h new file mode 100644 index 0000000..3cac2c8 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-3945-commands.h @@ -0,0 +1,1639 @@ +/****************************************************************************** + * + * 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 General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * James P. Ketrenos <ipw2100-admin@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2007 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +/* + * Please use this file (iwl-3945-commands.h) only for uCode API definitions. + * Please use iwl-3945-hw.h for hardware-related definitions. + * Please use iwl-3945.h for driver implementation definitions. + */ + +#ifndef __iwl_3945_commands_h__ +#define __iwl_3945_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 */ + REPLY_3945_RX = 0x1b, /* 3945 only */ + 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 */ + + /* Bluetooth device coexistance config command */ + REPLY_BT_CONFIG = 0x9b, + + /* 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, + + REPLY_MAX = 0xff +}; + +/****************************************************************************** + * (0) + * Commonly used structures and definitions: + * Command header, txpower + * + *****************************************************************************/ + +/* iwl3945_cmd_header flags value */ +#define IWL_CMD_FAILED_MSK 0x40 + +/** + * struct iwl3945_cmd_header + * + * This header format appears in the beginning of each command sent from the + * driver, and each response/notification received from uCode. + */ +struct iwl3945_cmd_header { + u8 cmd; /* Command ID: REPLY_RXON, etc. */ + u8 flags; /* IWL_CMD_* */ + /* + * The driver sets up the sequence number to values of its chosing. + * uCode does not use this value, but passes it back to the driver + * when sending the response to each driver-originated command, so + * the driver can match the response to the command. Since the values + * don't get used by uCode, the driver may set up an arbitrary format. + * + * There is one exception: uCode sets bit 15 when it originates + * the response/notification, i.e. when the response/notification + * is not a direct response to a command sent by the driver. For + * example, uCode issues REPLY_3945_RX when it sends a received frame + * to the driver; it is not a direct response to any driver command. + * + * The Linux driver uses the following format: + * + * 0:7 index/position within Tx queue + * 8:13 Tx queue selection + * 14:14 driver sets this to indicate command is in the 'huge' + * storage at the end of the command buffers, i.e. scan cmd + * 15:15 uCode sets this in uCode-originated response/notification + */ + __le16 sequence; + + /* command or response/notification data follows immediately */ + u8 data[0]; +} __attribute__ ((packed)); + +/** + * struct iwl3945_tx_power + * + * Used in REPLY_TX_PWR_TABLE_CMD, REPLY_SCAN_CMD, REPLY_CHANNEL_SWITCH + * + * Each entry contains two values: + * 1) DSP gain (or sometimes called DSP attenuation). This is a fine-grained + * linear value that multiplies the output of the digital signal processor, + * before being sent to the analog radio. + * 2) Radio gain. This sets the analog gain of the radio Tx path. + * It is a coarser setting, and behaves in a logarithmic (dB) fashion. + * + * Driver obtains values from struct iwl3945_tx_power power_gain_table[][]. + */ +struct iwl3945_tx_power { + u8 tx_gain; /* gain for analog radio */ + u8 dsp_atten; /* gain for DSP */ +} __attribute__ ((packed)); + +/** + * struct iwl3945_power_per_rate + * + * Used in REPLY_TX_PWR_TABLE_CMD, REPLY_CHANNEL_SWITCH + */ +struct iwl3945_power_per_rate { + u8 rate; /* plcp */ + struct iwl3945_tx_power tpc; + u8 reserved; +} __attribute__ ((packed)); + +/****************************************************************************** + * (0a) + * Alive and Error Commands & Responses: + * + *****************************************************************************/ + +#define UCODE_VALID_OK __constant_cpu_to_le32(0x1) +#define INITIALIZE_SUBTYPE (9) + +/* + * ("Initialize") REPLY_ALIVE = 0x1 (response only, not a command) + * + * uCode issues this "initialize alive" notification once the initialization + * uCode image has completed its work, and is ready to load the runtime image. + * This is the *first* "alive" notification that the driver will receive after + * rebooting uCode; the "initialize" alive is indicated by subtype field == 9. + * + * See comments documenting "BSM" (bootstrap state machine). + */ +struct iwl3945_init_alive_resp { + u8 ucode_minor; + u8 ucode_major; + __le16 reserved1; + u8 sw_rev[8]; + u8 ver_type; + u8 ver_subtype; /* "9" for initialize alive */ + __le16 reserved2; + __le32 log_event_table_ptr; + __le32 error_event_table_ptr; + __le32 timestamp; + __le32 is_valid; +} __attribute__ ((packed)); + + +/** + * REPLY_ALIVE = 0x1 (response only, not a command) + * + * uCode issues this "alive" notification once the runtime image is ready + * to receive commands from the driver. This is the *second* "alive" + * notification that the driver will receive after rebooting uCode; + * this "alive" is indicated by subtype field != 9. + * + * See comments documenting "BSM" (bootstrap state machine). + * + * This response includes two pointers to structures within the device's + * data SRAM (access via HBUS_TARG_MEM_* regs) that are useful for debugging: + * + * 1) log_event_table_ptr indicates base of the event log. This traces + * a 256-entry history of uCode execution within a circular buffer. + * + * 2) error_event_table_ptr indicates base of the error log. This contains + * information about any uCode error that occurs. + * + * The Linux driver can print both logs to the system log when a uCode error + * occurs. + */ +struct iwl3945_alive_resp { + u8 ucode_minor; + u8 ucode_major; + __le16 reserved1; + u8 sw_rev[8]; + u8 ver_type; + u8 ver_subtype; /* not "9" for runtime alive */ + __le16 reserved2; + __le32 log_event_table_ptr; /* SRAM address for event log */ + __le32 error_event_table_ptr; /* SRAM address for error log */ + __le32 timestamp; + __le32 is_valid; +} __attribute__ ((packed)); + +union tsf { + u8 byte[8]; + __le16 word[4]; + __le32 dw[2]; +}; + +/* + * REPLY_ERROR = 0x2 (response only, not a command) + */ +struct iwl3945_error_resp { + __le32 error_type; + u8 cmd_id; + u8 reserved1; + __le16 bad_cmd_seq_num; + __le16 reserved2; + __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) + * + * RXON tunes the radio tuner to a service channel, and sets up a number + * of parameters that are used primarily for Rx, but also for Tx operations. + * + * NOTE: When tuning to a new channel, driver must set the + * RXON_FILTER_ASSOC_MSK to 0. This will clear station-dependent + * info within the device, including the station tables, tx retry + * rate tables, and txpower tables. Driver must build a new station + * table and txpower table before transmitting anything on the RXON + * channel. + * + * NOTE: All RXONs wipe clean the internal txpower table. Driver must + * issue a new REPLY_TX_PWR_TABLE_CMD after each REPLY_RXON (0x10), + * regardless of whether RXON_FILTER_ASSOC_MSK is set. + */ +struct iwl3945_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; + __le16 reserved4; + u8 ofdm_basic_rates; + u8 cck_basic_rates; + __le16 assoc_id; + __le32 flags; + __le32 filter_flags; + __le16 channel; + __le16 reserved5; +} __attribute__ ((packed)); + +/* + * REPLY_RXON_ASSOC = 0x11 (command, has simple generic response) + */ +struct iwl3945_rxon_assoc_cmd { + __le32 flags; + __le32 filter_flags; + u8 ofdm_basic_rates; + u8 cck_basic_rates; + __le16 reserved; +} __attribute__ ((packed)); + +/* + * REPLY_RXON_TIMING = 0x14 (command, has simple generic response) + */ +struct iwl3945_rxon_time_cmd { + union tsf timestamp; + __le16 beacon_interval; + __le16 atim_window; + __le32 beacon_init_val; + __le16 listen_interval; + __le16 reserved; +} __attribute__ ((packed)); + +/* + * REPLY_CHANNEL_SWITCH = 0x72 (command, has simple generic response) + */ +struct iwl3945_channel_switch_cmd { + u8 band; + u8 expect_beacon; + __le16 channel; + __le32 rxon_flags; + __le32 rxon_filter_flags; + __le32 switch_time; + struct iwl3945_power_per_rate power[IWL_MAX_RATES]; +} __attribute__ ((packed)); + +/* + * CHANNEL_SWITCH_NOTIFICATION = 0x73 (notification only, not a command) + */ +struct iwl3945_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 -- QOS timing params for REPLY_QOS_PARAM + * One for each of 4 EDCA access categories in struct iwl_qosparam_cmd + * + * @cw_min: Contention window, start value in numbers of slots. + * Should be a power-of-2, minus 1. Device's default is 0x0f. + * @cw_max: Contention window, max value in numbers of slots. + * Should be a power-of-2, minus 1. Device's default is 0x3f. + * @aifsn: Number of slots in Arbitration Interframe Space (before + * performing random backoff timing prior to Tx). Device default 1. + * @edca_txop: Length of Tx opportunity, in uSecs. Device default is 0. + * + * Device will automatically increase contention window by (2*CW) + 1 for each + * transmission retry. Device uses cw_max as a bit mask, ANDed with new CW + * value, to cap the CW value. + */ +struct iwl3945_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) + +/* Number of Access Categories (AC) (EDCA), queues 0..3 */ +#define AC_NUM 4 + +/* + * REPLY_QOS_PARAM = 0x13 (command, has simple generic response) + * + * This command sets up timings for each of the 4 prioritized EDCA Tx FIFOs + * 0: Background, 1: Best Effort, 2: Video, 3: Voice. + */ +struct iwl3945_qosparam_cmd { + __le32 qos_flags; + struct iwl3945_ac_qos ac[AC_NUM]; +} __attribute__ ((packed)); + +/****************************************************************************** + * (3) + * Add/Modify Stations Commands & Responses: + * + *****************************************************************************/ +/* + * Multi station support + */ + +/* Special, dedicated locations within device's station table */ +#define IWL_AP_ID 0 +#define IWL_MULTICAST_ID 1 +#define IWL_STA_ID 2 +#define IWL3945_BROADCAST_ID 24 +#define IWL3945_STATION_COUNT 25 + +#define IWL_STATION_COUNT 32 /* MAX(3945,4965)*/ +#define IWL_INVALID_STATION 255 + +#define STA_FLG_TX_RATE_MSK __constant_cpu_to_le32(1<<2); +#define STA_FLG_PWR_SAVE_MSK __constant_cpu_to_le32(1<<8); + +/* Use in mode field. 1: modify existing entry, 0: add new station entry */ +#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) + +/* Flags indicate whether to modify vs. don't change various station params */ +#define STA_MODIFY_KEY_MASK 0x01 +#define STA_MODIFY_TID_DISABLE_TX 0x02 +#define 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 iwl3945_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 + * @addr[ETH_ALEN]: station's MAC address + * @sta_id: index of station in uCode's station table + * @modify_mask: STA_MODIFY_*, 1: modify, 0: don't change + * + * Driver selects unused table index when adding new station, + * or the index to a pre-existing station entry when modifying that station. + * Some indexes have special purposes (IWL_AP_ID, index 0, is for AP). + * + * modify_mask flags select which parameters to modify vs. leave alone. + */ +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) + * + * The device contains an internal table of per-station information, + * with info on security keys, aggregation parameters, and Tx rates for + * initial Tx attempt and any retries (4965 uses REPLY_TX_LINK_QUALITY_CMD, + * 3945 uses REPLY_RATE_SCALE to set up rate tables). + * + * REPLY_ADD_STA sets up the table entry for one station, either creating + * a new entry, or modifying a pre-existing one. + * + * NOTE: RXON command (without "associated" bit set) wipes the station table + * clean. Moving into RF_KILL state does this also. Driver must set up + * new station table before transmitting anything on the RXON channel + * (except active scans or active measurements; those commands carry + * their own txpower/rate setup data). + * + * When getting started on a new channel, driver must set up the + * IWL_BROADCAST_ID entry (last entry in the table). For a client + * station in a BSS, once an AP is selected, driver sets up the AP STA + * in the IWL_AP_ID entry (1st entry in the table). BROADCAST and AP + * are all that are needed for a BSS client station. If the device is + * used as AP, or in an IBSS network, driver must set up station table + * entries for all STAs in network, starting with index IWL_STA_ID. + */ +struct iwl3945_addsta_cmd { + u8 mode; /* 1: modify existing, 0: add new station */ + u8 reserved[3]; + struct sta_id_modify sta; + struct iwl3945_keyinfo key; + __le32 station_flags; /* STA_FLG_* */ + __le32 station_flags_msk; /* STA_FLG_* */ + + /* bit field to disable (1) or enable (0) Tx for Traffic ID (TID) + * corresponding to bit (e.g. bit 5 controls TID 5). + * Set modify_mask bit STA_MODIFY_TID_DISABLE_TX to use this field. */ + __le16 tid_disable_tx; + + __le16 rate_n_flags; + + /* TID for which to add block-ack support. + * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ + u8 add_immediate_ba_tid; + + /* TID for which to remove block-ack support. + * Set modify_mask bit STA_MODIFY_DELBA_TID_MSK to use this field. */ + u8 remove_immediate_ba_tid; + + /* Starting Sequence Number for added block-ack support. + * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ + __le16 add_immediate_ba_ssn; +} __attribute__ ((packed)); + +#define ADD_STA_SUCCESS_MSK 0x1 +#define ADD_STA_NO_ROOM_IN_TABLE 0x2 +#define ADD_STA_NO_BLOCK_ACK_RESOURCE 0x4 +/* + * REPLY_ADD_STA = 0x18 (response) + */ +struct iwl3945_add_sta_resp { + u8 status; /* ADD_STA_* */ +} __attribute__ ((packed)); + + +/****************************************************************************** + * (4) + * Rx Responses: + * + *****************************************************************************/ + +struct iwl3945_rx_frame_stats { + u8 phy_count; + u8 id; + u8 rssi; + u8 agc; + __le16 sig_avg; + __le16 noise_diff; + u8 payload[0]; +} __attribute__ ((packed)); + +struct iwl3945_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 iwl3945_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 iwl3945_rx_frame { + struct iwl3945_rx_frame_stats stats; + struct iwl3945_rx_frame_hdr hdr; + struct iwl3945_rx_frame_end end; +} __attribute__ ((packed)); + +/* Fixed (non-configurable) rx data from phy */ +#define RX_PHY_FLAGS_ANTENNAE_OFFSET (4) +#define RX_PHY_FLAGS_ANTENNAE_MASK (0x70) +#define IWL_AGC_DB_MASK (0x3f80) /* MASK(7,13) */ +#define IWL_AGC_DB_POS (7) +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)); + +/* + * REPLY_4965_RX = 0xc3 (response only, not a command) + * Used only for legacy (non 11n) frames. + */ +#define RX_RES_PHY_CNT 14 +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)); + + +/****************************************************************************** + * (5) + * Tx Commands & Responses: + * + * Driver must place each REPLY_TX command into one of the prioritized Tx + * queues in host DRAM, shared between driver and device. When the device's + * Tx scheduler and uCode are preparing to transmit, the device pulls the + * Tx command over the PCI bus via one of the device's Tx DMA channels, + * to fill an internal FIFO from which data will be transmitted. + * + * uCode handles all timing and protocol related to control frames + * (RTS/CTS/ACK), based on flags in the Tx command. + * + * uCode handles retrying Tx when an ACK is expected but not received. + * This includes trying lower data rates than the one requested in the Tx + * command, as set up by the REPLY_RATE_SCALE (for 3945) or + * REPLY_TX_LINK_QUALITY_CMD (4965). + * + * Driver sets up transmit power for various rates via REPLY_TX_PWR_TABLE_CMD. + * This command must be executed after every RXON command, before Tx can occur. + *****************************************************************************/ + +/* REPLY_TX Tx flags field */ + +/* 1: Use Request-To-Send protocol before this frame. + * Mutually exclusive vs. TX_CMD_FLG_CTS_MSK. */ +#define TX_CMD_FLG_RTS_MSK __constant_cpu_to_le32(1 << 1) + +/* 1: Transmit Clear-To-Send to self before this frame. + * Driver should set this for AUTH/DEAUTH/ASSOC-REQ/REASSOC mgmnt frames. + * Mutually exclusive vs. TX_CMD_FLG_RTS_MSK. */ +#define TX_CMD_FLG_CTS_MSK __constant_cpu_to_le32(1 << 2) + +/* 1: Expect ACK from receiving station + * 0: Don't expect ACK (MAC header's duration field s/b 0) + * Set this for unicast frames, but not broadcast/multicast. */ +#define TX_CMD_FLG_ACK_MSK __constant_cpu_to_le32(1 << 3) + +/* 1: Use rate scale table (see REPLY_TX_LINK_QUALITY_CMD). + * Tx command's initial_rate_index indicates first rate to try; + * uCode walks through table for additional Tx attempts. + * 0: Use Tx rate/MCS from Tx command's rate_n_flags field. + * This rate will be used for all Tx attempts; it will not be scaled. */ +#define TX_CMD_FLG_STA_RATE_MSK __constant_cpu_to_le32(1 << 4) + +/* 1: Expect immediate block-ack. + * Set when Txing a block-ack request frame. Also set TX_CMD_FLG_ACK_MSK. */ +#define TX_CMD_FLG_IMM_BA_RSP_MASK __constant_cpu_to_le32(1 << 6) + +/* 1: Frame requires full Tx-Op protection. + * Set this if either RTS or CTS Tx Flag gets set. */ +#define TX_CMD_FLG_FULL_TXOP_PROT_MSK __constant_cpu_to_le32(1 << 7) + +/* Tx antenna selection field; used only for 3945, reserved (0) for 4965. + * Set field to "0" to allow 3945 uCode to select antenna (normal usage). */ +#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) + +/* 1: Ignore Bluetooth priority for this frame. + * 0: Delay Tx until Bluetooth device is done (normal usage). */ +#define TX_CMD_FLG_BT_DIS_MSK __constant_cpu_to_le32(1 << 12) + +/* 1: uCode overrides sequence control field in MAC header. + * 0: Driver provides sequence control field in MAC header. + * Set this for management frames, non-QOS data frames, non-unicast frames, + * and also in Tx command embedded in REPLY_SCAN_CMD for active scans. */ +#define TX_CMD_FLG_SEQ_CTL_MSK __constant_cpu_to_le32(1 << 13) + +/* 1: This frame is non-last MPDU; more fragments are coming. + * 0: Last fragment, or not using fragmentation. */ +#define TX_CMD_FLG_MORE_FRAG_MSK __constant_cpu_to_le32(1 << 14) + +/* 1: uCode calculates and inserts Timestamp Function (TSF) in outgoing frame. + * 0: No TSF required in outgoing frame. + * Set this for transmitting beacons and probe responses. */ +#define TX_CMD_FLG_TSF_MSK __constant_cpu_to_le32(1 << 16) + +/* 1: Driver inserted 2 bytes pad after the MAC header, for (required) dword + * alignment of frame's payload data field. + * 0: No pad + * Set this for MAC headers with 26 or 30 bytes, i.e. those with QOS or ADDR4 + * field (but not both). Driver must align frame data (i.e. data following + * MAC header) to DWORD boundary. */ +#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_WEP 0x01 +#define TX_CMD_SEC_CCM 0x02 +#define TX_CMD_SEC_TKIP 0x03 +#define TX_CMD_SEC_MSK 0x03 +#define TX_CMD_SEC_SHIFT 6 +#define TX_CMD_SEC_KEY128 0x08 + +/* + * REPLY_TX = 0x1c (command) + */ +struct iwl3945_tx_cmd { + /* + * MPDU byte count: + * MAC header (24/26/30/32 bytes) + 2 bytes pad if 26/30 header size, + * + 8 byte IV for CCM or TKIP (not used for WEP) + * + Data payload + * + 8-byte MIC (not used for CCM/WEP) + * NOTE: Does not include Tx command bytes, post-MAC pad bytes, + * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.i + * Range: 14-2342 bytes. + */ + __le16 len; + + /* + * MPDU or MSDU byte count for next frame. + * Used for fragmentation and bursting, but not 11n aggregation. + * Same as "len", but for next frame. Set to 0 if not applicable. + */ + __le16 next_frame_len; + + __le32 tx_flags; /* TX_CMD_FLG_* */ + + u8 rate; + + /* Index of recipient station in uCode's station table */ + u8 sta_id; + u8 tid_tspec; + u8 sec_ctl; + u8 key[16]; + union { + u8 byte[8]; + __le16 word[4]; + __le32 dw[2]; + } tkip_mic; + __le32 next_frame_info; + union { + __le32 life_time; + __le32 attempt; + } stop_time; + u8 supp_rates[2]; + u8 rts_retry_limit; /*byte 50 */ + u8 data_retry_limit; /*byte 51 */ + union { + __le16 pm_frame_timeout; + __le16 attempt_duration; + } timeout; + + /* + * Duration of EDCA burst Tx Opportunity, in 32-usec units. + * Set this if txop time is not specified by HCCA protocol (e.g. by AP). + */ + __le16 driver_txop; + + /* + * MAC header goes here, followed by 2 bytes padding if MAC header + * length is 26 or 30 bytes, followed by payload data + */ + 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 */ +}; + +/* + * REPLY_TX = 0x1c (response) + */ +struct iwl3945_tx_resp { + u8 failure_rts; + u8 failure_frame; + u8 bt_kill_count; + u8 rate; + __le32 wireless_media_time; + __le32 status; /* TX status */ +} __attribute__ ((packed)); + +/* + * REPLY_TX_PWR_TABLE_CMD = 0x97 (command, has simple generic response) + */ +struct iwl3945_txpowertable_cmd { + u8 band; /* 0: 5 GHz, 1: 2.4 GHz */ + u8 reserved; + __le16 channel; + struct iwl3945_power_per_rate power[IWL_MAX_RATES]; +} __attribute__ ((packed)); + +struct iwl3945_rate_scaling_info { + __le16 rate_n_flags; + u8 try_cnt; + u8 next_rate_index; +} __attribute__ ((packed)); + +/** + * struct iwl3945_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 iwl3945_rate_scaling_cmd { + u8 table_id; + u8 reserved[3]; + struct iwl3945_rate_scaling_info table[IWL_MAX_RATES]; +} __attribute__ ((packed)); + +/* + * REPLY_BT_CONFIG = 0x9b (command, has simple generic response) + */ +struct iwl3945_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 iwl3945_measure_channel { + __le32 duration; /* measurement duration in extended beacon + * format */ + u8 channel; /* channel to measure */ + u8 type; /* see enum iwl3945_measure_type */ + __le16 reserved; +} __attribute__ ((packed)); + +/* + * REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74 (command) + */ +struct iwl3945_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 iwl3945_measure_channel channels[10]; +} __attribute__ ((packed)); + +/* + * REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74 (response) + */ +struct iwl3945_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 iwl3945_measurement_state { + IWL_MEASUREMENT_START = 0, + IWL_MEASUREMENT_STOP = 1, +}; + +enum iwl3945_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 iwl3945_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 iwl3945_measurement_cca_counters { + __le32 ofdm; + __le32 cck; +} __attribute__ ((packed)); + +enum iwl3945_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 iwl3945_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 iwl3945_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 iwl3945_measurement_histogram histogram; + __le32 stop_time; /* lower 32-bits of TSF */ + __le32 status; /* see iwl3945_measurement_status */ +} __attribute__ ((packed)); + +/****************************************************************************** + * (7) + * Power Management Commands, Responses, Notifications: + * + *****************************************************************************/ + +/** + * struct iwl3945_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 + +#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 iwl3945_powertable_cmd { + __le32 flags; + __le32 rx_data_timeout; + __le32 tx_data_timeout; + __le32 sleep_interval[IWL_POWER_VEC_SIZE]; +} __attribute__((packed)); + +/* + * PM_SLEEP_NOTIFICATION = 0x7A (notification only, not a command) + * 3945 and 4965 identical. + */ +struct iwl3945_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 iwl3945_card_state_cmd { + __le32 status; /* CARD_STATE_CMD_* request new power state */ +} __attribute__ ((packed)); + +/* + * CARD_STATE_NOTIFICATION = 0xa1 (notification only, not a command) + */ +struct iwl3945_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 iwl3945_ct_kill_config { + __le32 reserved; + __le32 critical_temperature_M; + __le32 critical_temperature_R; +} __attribute__ ((packed)); + +/****************************************************************************** + * (8) + * Scan Commands, Responses, Notifications: + * + *****************************************************************************/ + +struct iwl3945_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 iwl3945_tx_power tpc; + __le16 active_dwell; + __le16 passive_dwell; +} __attribute__ ((packed)); + +struct iwl3945_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 iwl3945_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 */ + __le16 reserved1; + __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 iwl3945_tx_cmd tx_cmd; + struct iwl3945_ssid_ie direct_scan[PROBE_OPTION_MAX]; + + u8 data[0]; + /* + * The channels start after the probe request payload and are of type: + * + * struct iwl3945_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 iwl3945_scanreq_notification { + __le32 status; /* 1: okay, 2: cannot fulfill request */ +} __attribute__ ((packed)); + +/* + * SCAN_START_NOTIFICATION = 0x82 (notification only, not a command) + */ +struct iwl3945_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 iwl3945_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 iwl3945_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 iwl3945_beacon_notif { + struct iwl3945_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 iwl3945_tx_beacon_cmd { + struct iwl3945_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; +} __attribute__ ((packed)); + +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 */ +} __attribute__ ((packed)); + +struct statistics_rx { + struct statistics_rx_phy ofdm; + struct statistics_rx_phy cck; + struct statistics_rx_non_phy general; +} __attribute__ ((packed)); + +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; +} __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; +} __attribute__ ((packed)); + +struct statistics_general { + __le32 temperature; + struct statistics_dbg dbg; + __le32 sleep_time; + __le32 slots_out; + __le32 slots_idle; + __le32 ttl_timestamp; + struct statistics_div div; +} __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 iwl3945_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. + */ +#define STATISTICS_REPLY_FLG_BAND_24G_MSK __constant_cpu_to_le32(0x2) +#define STATISTICS_REPLY_FLG_FAT_MODE_MSK __constant_cpu_to_le32(0x8) +struct iwl3945_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 iwl3945_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 iwl3945_sensitivity_cmd { + __le16 control; + __le16 table[HD_TABLE_SIZE]; +} __attribute__ ((packed)); + +struct iwl3945_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 iwl3945_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 iwl3945_rx_packet { + __le32 len; + struct iwl3945_cmd_header hdr; + union { + struct iwl3945_alive_resp alive_frame; + struct iwl3945_rx_frame rx_frame; + struct iwl3945_tx_resp tx_resp; + struct iwl3945_spectrum_notification spectrum_notif; + struct iwl3945_csa_notification csa_notif; + struct iwl3945_error_resp err_resp; + struct iwl3945_card_state_notif card_state_notif; + struct iwl3945_beacon_notif beacon_status; + struct iwl3945_add_sta_resp add_sta; + struct iwl3945_sleep_notification sleep_notif; + struct iwl3945_spectrum_resp spectrum; + struct iwl3945_notif_statistics stats; + __le32 status; + u8 raw[0]; + } u; +} __attribute__ ((packed)); + +#define IWL_RX_FRAME_SIZE (4 + sizeof(struct iwl3945_rx_frame)) + +#endif /* __iwl3945_3945_commands_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-debug.h b/drivers/net/wireless/iwlwifi/iwl-3945-debug.h new file mode 100644 index 0000000..ebf0168 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-3945-debug.h @@ -0,0 +1,152 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2007 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * James P. Ketrenos <ipw2100-admin@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#ifndef __iwl3945_debug_h__ +#define __iwl3945_debug_h__ + +#ifdef CONFIG_IWL3945_DEBUG +extern u32 iwl3945_debug_level; +#define IWL_DEBUG(level, fmt, args...) \ +do { if (iwl3945_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 ((iwl3945_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_IWL3945_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/iwl/debug_level + * + * you simply need to add your entry to the iwl3945_debug_levels array. + * + * If you do not see debug_level in /proc/net/iwl then you do not have + * CONFIG_IWL3945_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_RATE_LIMIT(f, a...) IWL_DEBUG_LIMIT(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_ASSOC_LIMIT(f, a...) \ + IWL_DEBUG_LIMIT(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/iwlwifi/iwl-3945-hw.h b/drivers/net/wireless/iwlwifi/iwl-3945-hw.h index b127450..3020e5d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-hw.h +++ b/drivers/net/wireless/iwlwifi/iwl-3945-hw.h @@ -8,7 +8,7 @@ * 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 + * 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 @@ -60,31 +60,647 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ +/* + * Please use this file (iwl-3945-hw.h) only for hardware-related definitions. + * Please use iwl-3945-commands.h for uCode API definitions. + * Please use iwl-3945.h for driver implementation definitions. + */ #ifndef __iwl_3945_hw__ #define __iwl_3945_hw__ -#define IWL_RX_BUF_SIZE 3000 -#define IWL_MAX_BSM_SIZE ALM_RTC_INST_SIZE +/* + * uCode queue management definitions ... + * Queue #4 is the command queue for 3945 and 4965. + */ +#define IWL_CMD_QUEUE_NUM 4 + +/* Tx rates */ +#define IWL_CCK_RATES 4 +#define IWL_OFDM_RATES 8 +#define IWL_HT_RATES 0 +#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 */ +#define IWL_RSSI_OFFSET 95 + +/* + * EEPROM related constants, enums, and structures. + */ + +/* + * EEPROM access time values: + * + * Driver initiates EEPROM read by writing byte address << 1 to CSR_EEPROM_REG, + * then clearing (with subsequent read/modify/write) CSR_EEPROM_REG bit + * CSR_EEPROM_REG_BIT_CMD (0x2). + * Driver then polls CSR_EEPROM_REG for CSR_EEPROM_REG_READ_VALID_MSK (0x1). + * When polling, wait 10 uSec between polling loops, up to a maximum 5000 uSec. + * Driver reads 16-bit value from bits 31-16 of CSR_EEPROM_REG. + */ +#define IWL_EEPROM_ACCESS_TIMEOUT 5000 /* uSec */ +#define IWL_EEPROM_ACCESS_DELAY 10 /* uSec */ + +/* + * Regulatory channel usage flags in EEPROM struct iwl_eeprom_channel.flags. + * + * IBSS and/or AP operation is allowed *only* on those channels with + * (VALID && IBSS && ACTIVE && !RADAR). This restriction is in place because + * RADAR detection is not supported by the 3945 driver, but is a + * requirement for establishing a new network for legal operation on channels + * requiring RADAR detection or restricting ACTIVE scanning. + * + * NOTE: "WIDE" flag indicates that 20 MHz channel is supported; + * 3945 does not support FAT 40 MHz-wide channels. + * + * NOTE: Using a channel inappropriately will result in a uCode error! + */ +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), /* 20 MHz channel okay */ + EEPROM_CHANNEL_NARROW = (1 << 6), /* 10 MHz channel (not used) */ + EEPROM_CHANNEL_DFS = (1 << 7), /* dynamic freq selection candidate */ +}; + +/* 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 iwl3945_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. + * DO NOT ALTER THIS STRUCTURE!!! + */ +struct iwl3945_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 iwl3945_eeprom_txpower_group { + struct iwl3945_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 iwl3945_eeprom_temperature_corr { + u32 Ta; + u32 Tb; + u32 Tc; + u32 Td; + u32 Te; +} __attribute__ ((packed)); + +/* + * EEPROM map + */ +struct iwl3945_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 */ + u8 reserved6[42]; +#define EEPROM_REGULATORY_SKU_ID (2*0x60) /* 4 bytes */ + u8 sku_id[4]; /* abs.ofs: 192 */ + +/* + * Per-channel regulatory data. + * + * Each channel that *might* be supported by 3945 or 4965 has a fixed location + * in EEPROM containing EEPROM_CHANNEL_* usage flags (LSB) and max regulatory + * txpower (MSB). + * + * Entries immediately below are for 20 MHz channel width. FAT (40 MHz) + * channels (only for 4965, not supported by 3945) appear later in the EEPROM. + * + * 2.4 GHz channels 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 + */ +#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 iwl3945_eeprom_channel band_1_channels[14]; /* abs.ofs: 196 */ + +/* + * 4.9 GHz channels 183, 184, 185, 187, 188, 189, 192, 196, + * 5.0 GHz channels 7, 8, 11, 12, 16 + * (4915-5080MHz) (none of these is ever supported) + */ +#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 iwl3945_eeprom_channel band_2_channels[13]; /* abs.ofs: 228 */ + +/* + * 5.2 GHz channels 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 + * (5170-5320MHz) + */ +#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 iwl3945_eeprom_channel band_3_channels[12]; /* abs.ofs: 256 */ + +/* + * 5.5 GHz channels 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 + * (5500-5700MHz) + */ +#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 iwl3945_eeprom_channel band_4_channels[11]; /* abs.ofs: 282 */ + +/* + * 5.7 GHz channels 145, 149, 153, 157, 161, 165 + * (5725-5825MHz) + */ +#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 iwl3945_eeprom_channel band_5_channels[6]; /* abs.ofs: 306 */ + + u8 reserved9[194]; + +/* + * 3945 Txpower calibration data. + */ +#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 iwl3945_eeprom_txpower_group groups[IWL_NUM_TX_CALIB_GROUPS]; +/* abs.ofs: 512 */ +#define EEPROM_CALIB_TEMPERATURE_CORRECT 0x340 + struct iwl3945_eeprom_temperature_corr corrections; /* abs.ofs: 832 */ + u8 reserved16[172]; /* fill out to full 1024 byte block */ +} __attribute__ ((packed)); + +#define IWL_EEPROM_IMAGE_SIZE 1024 + +/* End of EEPROM */ + + +#include "iwl-3945-commands.h" + +#define PCI_LINK_CTRL 0x0F0 +#define PCI_POWER_SOURCE 0x0C8 +#define PCI_REG_WUM8 0x0E8 +#define PCI_CFG_PMC_PME_FROM_D3COLD_SUPPORT (0x80000000) + +/*=== CSR (control and status registers) ===*/ +#define CSR_BASE (0x000) + +#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) + +/* + * Hardware revision info + * Bit fields: + * 31-8: Reserved + * 7-4: Type of device: 0x0 = 4965, 0xd = 3945 + * 3-2: Revision step: 0 = A, 1 = B, 2 = C, 3 = D + * 1-0: "Dash" value, as in A-1, etc. + */ +#define CSR_HW_REV (CSR_BASE+0x028) + +/* EEPROM reads */ +#define CSR_EEPROM_REG (CSR_BASE+0x02c) +#define CSR_EEPROM_GP (CSR_BASE+0x030) +#define CSR_GP_UCODE (CSR_BASE+0x044) +#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) + +/* Analog phase-lock-loop configuration (3945 only) + * Set bit 24. */ +#define CSR_ANA_PLL_CFG (CSR_BASE+0x20c) + +/* Bits for CSR_HW_IF_CONFIG_REG */ +#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) + +/* interrupt flags in INTA, set by uCode or hardware (e.g. dma), + * acknowledged (reset) by host writing "1" to flagged bits. */ +#define CSR_INT_BIT_FH_RX (1<<31) /* Rx DMA, cmd responses, FH_INT[17:16] */ +#define CSR_INT_BIT_HW_ERR (1<<29) /* DMA hardware error FH_INT[31] */ +#define CSR_INT_BIT_DNLD (1<<28) /* uCode Download */ +#define CSR_INT_BIT_FH_TX (1<<27) /* Tx DMA FH_INT[1:0] */ +#define CSR_INT_BIT_MAC_CLK_ACTV (1<<26) /* NIC controller's clock toggled on/off */ +#define CSR_INT_BIT_SW_ERR (1<<25) /* uCode error */ +#define CSR_INT_BIT_RF_KILL (1<<7) /* HW RFKILL switch GP_CNTRL[27] toggled */ +#define CSR_INT_BIT_CT_KILL (1<<6) /* Critical temp (chip too hot) rfkill */ +#define CSR_INT_BIT_SW_RX (1<<3) /* Rx, command responses, 3945 */ +#define CSR_INT_BIT_WAKEUP (1<<1) /* NIC controller waking up (pwr mgmt) */ +#define CSR_INT_BIT_ALIVE (1<<0) /* uCode interrupts once it initializes */ + +#define CSR_INI_SET_MASK (CSR_INT_BIT_FH_RX | \ + CSR_INT_BIT_HW_ERR | \ + CSR_INT_BIT_FH_TX | \ + CSR_INT_BIT_SW_ERR | \ + CSR_INT_BIT_RF_KILL | \ + CSR_INT_BIT_SW_RX | \ + CSR_INT_BIT_WAKEUP | \ + CSR_INT_BIT_ALIVE) + +/* interrupt flags in FH (flow handler) (PCI busmaster DMA) */ +#define CSR_FH_INT_BIT_ERR (1<<31) /* Error */ +#define CSR_FH_INT_BIT_HI_PRIOR (1<<30) /* High priority Rx, bypass coalescing */ +#define CSR_FH_INT_BIT_RX_CHNL2 (1<<18) /* Rx channel 2 (3945 only) */ +#define CSR_FH_INT_BIT_RX_CHNL1 (1<<17) /* Rx channel 1 */ +#define CSR_FH_INT_BIT_RX_CHNL0 (1<<16) /* Rx channel 0 */ +#define CSR_FH_INT_BIT_TX_CHNL6 (1<<6) /* Tx channel 6 (3945 only) */ +#define CSR_FH_INT_BIT_TX_CHNL1 (1<<1) /* Tx channel 1 */ +#define CSR_FH_INT_BIT_TX_CHNL0 (1<<0) /* Tx channel 0 */ + +#define CSR_FH_INT_RX_MASK (CSR_FH_INT_BIT_HI_PRIOR | \ + CSR_FH_INT_BIT_RX_CHNL2 | \ + CSR_FH_INT_BIT_RX_CHNL1 | \ + CSR_FH_INT_BIT_RX_CHNL0) + +#define CSR_FH_INT_TX_MASK (CSR_FH_INT_BIT_TX_CHNL6 | \ + CSR_FH_INT_BIT_TX_CHNL1 | \ + CSR_FH_INT_BIT_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) + + +/* EEPROM REG */ +#define CSR_EEPROM_REG_READ_VALID_MSK (0x00000001) +#define CSR_EEPROM_REG_BIT_CMD (0x00000002) + +/* EEPROM GP */ +#define CSR_EEPROM_GP_VALID_MSK (0x00000006) +#define CSR_EEPROM_GP_BAD_SIGNATURE (0x00000000) +#define CSR_EEPROM_GP_IF_OWNER_MSK (0x00000180) + +/* UCODE DRV GP */ +#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) + +/* GPIO */ +#define CSR_GPIO_IN_BIT_AUX_POWER (0x00000200) +#define CSR_GPIO_IN_VAL_VAUX_PWR_SRC (0x00000000) +#define CSR_GPIO_IN_VAL_VMAIN_PWR_SRC CSR_GPIO_IN_BIT_AUX_POWER + +/* GI Chicken Bits */ +#define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX (0x00800000) +#define CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER (0x20000000) + +/* CSR_ANA_PLL_CFG */ +#define CSR_ANA_PLL_CFG_SH (0x00880300) + +/*=== HBUS (Host-side Bus) ===*/ +#define HBUS_BASE (0x400) + +/* + * Registers for accessing device's internal SRAM memory (e.g. SCD SRAM + * structures, error log, event log, verifying uCode load). + * First write to address register, then read from or write to data register + * to complete the job. Once the address register is set up, accesses to + * data registers auto-increment the address by one dword. + * Bit usage for address registers (read or write): + * 0-31: memory address within device + */ +#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) + +/* + * Registers for accessing device's internal peripheral registers + * (e.g. SCD, BSM, etc.). First write to address register, + * then read from or write to data register to complete the job. + * Bit usage for address registers (read or write): + * 0-15: register address (offset) within device + * 24-25: (# bytes - 1) to read or write (e.g. 3 for dword) + */ +#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) + +/* + * Per-Tx-queue write pointer (index, really!) (3945 and 4965). + * Indicates index to next TFD that driver will fill (1 past latest filled). + * Bit usage: + * 0-7: queue write index + * 11-8: queue selector + */ +#define HBUS_TARG_WRPTR (HBUS_BASE+0x060) + +/* SCD (3945 Tx Frame 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) + +/*=== FH (data Flow Handler) ===*/ +#define FH_BASE (0x800) + +#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) + +#define FH_RSCSR_CHNL0_WPTR (FH_RCSR_WPTR(0)) + +/* 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) + + +/* 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 TFD_QUEUE_MIN 0 +#define TFD_QUEUE_MAX 6 +#define TFD_QUEUE_SIZE_MAX (256) + +#define IWL_NUM_SCAN_RATES (2) + +#define IWL_DEFAULT_TX_RETRY 15 + +/*********************************************/ + +#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 + +#define U32_PAD(n) ((4-(n))&0x3) + +#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 iwl3945_cmd) - \ + sizeof(struct iwl3945_cmd_meta)) + +/* + * RX related structures and functions + */ +#define RX_FREE_BUFFERS 64 +#define RX_LOW_WATERMARK 8 + +/* Sizes and addresses for instruction and data memory (SRAM) in + * 3945's embedded processor. Driver access is via HBUS_TARG_MEM_* regs. */ +#define RTC_INST_LOWER_BOUND (0x000000) +#define ALM_RTC_INST_UPPER_BOUND (0x014000) + +#define RTC_DATA_LOWER_BOUND (0x800000) +#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_INST_SIZE ALM_RTC_INST_SIZE #define IWL_MAX_DATA_SIZE ALM_RTC_DATA_SIZE -/* 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 { +/* Size of uCode instruction memory in bootstrap state machine */ +#define IWL_MAX_BSM_SIZE ALM_RTC_INST_SIZE + +#define IWL_MAX_NUM_QUEUES 8 + +static inline int iwl3945_hw_valid_rtc_data_addr(u32 addr) +{ + return (addr >= RTC_DATA_LOWER_BOUND) && + (addr < ALM_RTC_DATA_UPPER_BOUND); +} + +/* Base physical address of iwl3945_shared is provided to FH_TSSR_CBB_BASE + * and &iwl3945_shared.rx_read_ptr[0] is provided to FH_RCSR_RPTR_ADDR(0) */ +struct iwl3945_shared { __le32 tx_base_ptr[8]; __le32 rx_read_ptr[3]; } __attribute__ ((packed)); -struct iwl_tfd_frame_data { +struct iwl3945_tfd_frame_data { __le32 addr; __le32 len; } __attribute__ ((packed)); -struct iwl_tfd_frame { +struct iwl3945_tfd_frame { __le32 control_flags; - struct iwl_tfd_frame_data pa[4]; + struct iwl3945_tfd_frame_data pa[4]; u8 reserved[28]; } __attribute__ ((packed)); +static inline u8 iwl3945_hw_get_rate(__le16 rate_n_flags) +{ + return le16_to_cpu(rate_n_flags) & 0xFF; +} + +static inline u16 iwl3945_hw_get_rate_n_flags(__le16 rate_n_flags) +{ + return le16_to_cpu(rate_n_flags); +} + +static inline __le16 iwl3945_hw_set_rate_n_flags(u8 rate, u16 flags) +{ + return cpu_to_le16((u16)rate|flags); +} #endif diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-io.h b/drivers/net/wireless/iwlwifi/iwl-3945-io.h new file mode 100644 index 0000000..75e20d0 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-3945-io.h @@ -0,0 +1,431 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2007 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * James P. Ketrenos <ipw2100-admin@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#ifndef __iwl3945_io_h__ +#define __iwl3945_io_h__ + +#include <linux/io.h> + +#include "iwl-3945-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 + * 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 _iwl3945_read_direct32 calls the non-check version of + * _iwl3945_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 _iwl3945_write32(iwl, ofs, val) writel((val), (iwl)->hw_base + (ofs)) +#ifdef CONFIG_IWL3945_DEBUG +static inline void __iwl3945_write32(const char *f, u32 l, struct iwl3945_priv *iwl, + u32 ofs, u32 val) +{ + IWL_DEBUG_IO("write32(0x%08X, 0x%08X) - %s %d\n", ofs, val, f, l); + _iwl3945_write32(iwl, ofs, val); +} +#define iwl3945_write32(iwl, ofs, val) \ + __iwl3945_write32(__FILE__, __LINE__, iwl, ofs, val) +#else +#define iwl3945_write32(iwl, ofs, val) _iwl3945_write32(iwl, ofs, val) +#endif + +#define _iwl3945_read32(iwl, ofs) readl((iwl)->hw_base + (ofs)) +#ifdef CONFIG_IWL3945_DEBUG +static inline u32 __iwl3945_read32(char *f, u32 l, struct iwl3945_priv *iwl, u32 ofs) +{ + IWL_DEBUG_IO("read_direct32(0x%08X) - %s %d\n", ofs, f, l); + return _iwl3945_read32(iwl, ofs); +} +#define iwl3945_read32(iwl, ofs) __iwl3945_read32(__FILE__, __LINE__, iwl, ofs) +#else +#define iwl3945_read32(p, o) _iwl3945_read32(p, o) +#endif + +static inline int _iwl3945_poll_bit(struct iwl3945_priv *priv, u32 addr, + u32 bits, u32 mask, int timeout) +{ + int i = 0; + + do { + if ((_iwl3945_read32(priv, addr) & mask) == (bits & mask)) + return i; + mdelay(10); + i += 10; + } while (i < timeout); + + return -ETIMEDOUT; +} +#ifdef CONFIG_IWL3945_DEBUG +static inline int __iwl3945_poll_bit(const char *f, u32 l, + struct iwl3945_priv *priv, u32 addr, + u32 bits, u32 mask, int timeout) +{ + int ret = _iwl3945_poll_bit(priv, addr, bits, mask, timeout); + if (unlikely(ret == -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, ret, f, l); + return ret; +} +#define iwl3945_poll_bit(iwl, addr, bits, mask, timeout) \ + __iwl3945_poll_bit(__FILE__, __LINE__, iwl, addr, bits, mask, timeout) +#else +#define iwl3945_poll_bit(p, a, b, m, t) _iwl3945_poll_bit(p, a, b, m, t) +#endif + +static inline void _iwl3945_set_bit(struct iwl3945_priv *priv, u32 reg, u32 mask) +{ + _iwl3945_write32(priv, reg, _iwl3945_read32(priv, reg) | mask); +} +#ifdef CONFIG_IWL3945_DEBUG +static inline void __iwl3945_set_bit(const char *f, u32 l, + struct iwl3945_priv *priv, u32 reg, u32 mask) +{ + u32 val = _iwl3945_read32(priv, reg) | mask; + IWL_DEBUG_IO("set_bit(0x%08X, 0x%08X) = 0x%08X\n", reg, mask, val); + _iwl3945_write32(priv, reg, val); +} +#define iwl3945_set_bit(p, r, m) __iwl3945_set_bit(__FILE__, __LINE__, p, r, m) +#else +#define iwl3945_set_bit(p, r, m) _iwl3945_set_bit(p, r, m) +#endif + +static inline void _iwl3945_clear_bit(struct iwl3945_priv *priv, u32 reg, u32 mask) +{ + _iwl3945_write32(priv, reg, _iwl3945_read32(priv, reg) & ~mask); +} +#ifdef CONFIG_IWL3945_DEBUG +static inline void __iwl3945_clear_bit(const char *f, u32 l, + struct iwl3945_priv *priv, u32 reg, u32 mask) +{ + u32 val = _iwl3945_read32(priv, reg) & ~mask; + IWL_DEBUG_IO("clear_bit(0x%08X, 0x%08X) = 0x%08X\n", reg, mask, val); + _iwl3945_write32(priv, reg, val); +} +#define iwl3945_clear_bit(p, r, m) __iwl3945_clear_bit(__FILE__, __LINE__, p, r, m) +#else +#define iwl3945_clear_bit(p, r, m) _iwl3945_clear_bit(p, r, m) +#endif + +static inline int _iwl3945_grab_nic_access(struct iwl3945_priv *priv) +{ + int ret; + u32 gp_ctl; + +#ifdef CONFIG_IWL3945_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 = _iwl3945_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 */ + _iwl3945_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + ret = _iwl3945_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 (ret < 0) { + IWL_ERROR("MAC is in deep sleep!\n"); + return -EIO; + } + +#ifdef CONFIG_IWL3945_DEBUG + atomic_inc(&priv->restrict_refcnt); +#endif + return 0; +} + +#ifdef CONFIG_IWL3945_DEBUG +static inline int __iwl3945_grab_nic_access(const char *f, u32 l, + struct iwl3945_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 nic access - %s %d\n", f, l); + return _iwl3945_grab_nic_access(priv); +} +#define iwl3945_grab_nic_access(priv) \ + __iwl3945_grab_nic_access(__FILE__, __LINE__, priv) +#else +#define iwl3945_grab_nic_access(priv) \ + _iwl3945_grab_nic_access(priv) +#endif + +static inline void _iwl3945_release_nic_access(struct iwl3945_priv *priv) +{ +#ifdef CONFIG_IWL3945_DEBUG + if (atomic_dec_and_test(&priv->restrict_refcnt)) +#endif + _iwl3945_clear_bit(priv, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); +} +#ifdef CONFIG_IWL3945_DEBUG +static inline void __iwl3945_release_nic_access(const char *f, u32 l, + struct iwl3945_priv *priv) +{ + if (atomic_read(&priv->restrict_refcnt) <= 0) + IWL_ERROR("Release unheld nic access at line %d.\n", l); + + IWL_DEBUG_IO("releasing nic access - %s %d\n", f, l); + _iwl3945_release_nic_access(priv); +} +#define iwl3945_release_nic_access(priv) \ + __iwl3945_release_nic_access(__FILE__, __LINE__, priv) +#else +#define iwl3945_release_nic_access(priv) \ + _iwl3945_release_nic_access(priv) +#endif + +static inline u32 _iwl3945_read_direct32(struct iwl3945_priv *priv, u32 reg) +{ + return _iwl3945_read32(priv, reg); +} +#ifdef CONFIG_IWL3945_DEBUG +static inline u32 __iwl3945_read_direct32(const char *f, u32 l, + struct iwl3945_priv *priv, u32 reg) +{ + u32 value = _iwl3945_read_direct32(priv, reg); + if (!atomic_read(&priv->restrict_refcnt)) + IWL_ERROR("Nic access not held from %s %d\n", f, l); + IWL_DEBUG_IO("read_direct32(0x%4X) = 0x%08x - %s %d \n", reg, value, + f, l); + return value; +} +#define iwl3945_read_direct32(priv, reg) \ + __iwl3945_read_direct32(__FILE__, __LINE__, priv, reg) +#else +#define iwl3945_read_direct32 _iwl3945_read_direct32 +#endif + +static inline void _iwl3945_write_direct32(struct iwl3945_priv *priv, + u32 reg, u32 value) +{ + _iwl3945_write32(priv, reg, value); +} +#ifdef CONFIG_IWL3945_DEBUG +static void __iwl3945_write_direct32(u32 line, + struct iwl3945_priv *priv, u32 reg, u32 value) +{ + if (!atomic_read(&priv->restrict_refcnt)) + IWL_ERROR("Nic access not held from line %d\n", line); + _iwl3945_write_direct32(priv, reg, value); +} +#define iwl3945_write_direct32(priv, reg, value) \ + __iwl3945_write_direct32(__LINE__, priv, reg, value) +#else +#define iwl3945_write_direct32 _iwl3945_write_direct32 +#endif + +static inline void iwl3945_write_reg_buf(struct iwl3945_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++) + _iwl3945_write_direct32(priv, reg, *values); + } +} + +static inline int _iwl3945_poll_direct_bit(struct iwl3945_priv *priv, + u32 addr, u32 mask, int timeout) +{ + int i = 0; + + do { + if ((_iwl3945_read_direct32(priv, addr) & mask) == mask) + return i; + mdelay(10); + i += 10; + } while (i < timeout); + + return -ETIMEDOUT; +} + +#ifdef CONFIG_IWL3945_DEBUG +static inline int __iwl3945_poll_direct_bit(const char *f, u32 l, + struct iwl3945_priv *priv, + u32 addr, u32 mask, int timeout) +{ + int ret = _iwl3945_poll_direct_bit(priv, addr, mask, timeout); + + if (unlikely(ret == -ETIMEDOUT)) + IWL_DEBUG_IO("poll_direct_bit(0x%08X, 0x%08X) - " + "timedout - %s %d\n", addr, mask, f, l); + else + IWL_DEBUG_IO("poll_direct_bit(0x%08X, 0x%08X) = 0x%08X " + "- %s %d\n", addr, mask, ret, f, l); + return ret; +} +#define iwl3945_poll_direct_bit(iwl, addr, mask, timeout) \ + __iwl3945_poll_direct_bit(__FILE__, __LINE__, iwl, addr, mask, timeout) +#else +#define iwl3945_poll_direct_bit _iwl3945_poll_direct_bit +#endif + +static inline u32 _iwl3945_read_prph(struct iwl3945_priv *priv, u32 reg) +{ + _iwl3945_write_direct32(priv, HBUS_TARG_PRPH_RADDR, reg | (3 << 24)); + return _iwl3945_read_direct32(priv, HBUS_TARG_PRPH_RDAT); +} +#ifdef CONFIG_IWL3945_DEBUG +static inline u32 __iwl3945_read_prph(u32 line, struct iwl3945_priv *priv, u32 reg) +{ + if (!atomic_read(&priv->restrict_refcnt)) + IWL_ERROR("Nic access not held from line %d\n", line); + return _iwl3945_read_prph(priv, reg); +} + +#define iwl3945_read_prph(priv, reg) \ + __iwl3945_read_prph(__LINE__, priv, reg) +#else +#define iwl3945_read_prph _iwl3945_read_prph +#endif + +static inline void _iwl3945_write_prph(struct iwl3945_priv *priv, + u32 addr, u32 val) +{ + _iwl3945_write_direct32(priv, HBUS_TARG_PRPH_WADDR, + ((addr & 0x0000FFFF) | (3 << 24))); + _iwl3945_write_direct32(priv, HBUS_TARG_PRPH_WDAT, val); +} +#ifdef CONFIG_IWL3945_DEBUG +static inline void __iwl3945_write_prph(u32 line, struct iwl3945_priv *priv, + u32 addr, u32 val) +{ + if (!atomic_read(&priv->restrict_refcnt)) + IWL_ERROR("Nic access from line %d\n", line); + _iwl3945_write_prph(priv, addr, val); +} + +#define iwl3945_write_prph(priv, addr, val) \ + __iwl3945_write_prph(__LINE__, priv, addr, val); +#else +#define iwl3945_write_prph _iwl3945_write_prph +#endif + +#define _iwl3945_set_bits_prph(priv, reg, mask) \ + _iwl3945_write_prph(priv, reg, (_iwl3945_read_prph(priv, reg) | mask)) +#ifdef CONFIG_IWL3945_DEBUG +static inline void __iwl3945_set_bits_prph(u32 line, struct iwl3945_priv *priv, + u32 reg, u32 mask) +{ + if (!atomic_read(&priv->restrict_refcnt)) + IWL_ERROR("Nic access not held from line %d\n", line); + + _iwl3945_set_bits_prph(priv, reg, mask); +} +#define iwl3945_set_bits_prph(priv, reg, mask) \ + __iwl3945_set_bits_prph(__LINE__, priv, reg, mask) +#else +#define iwl3945_set_bits_prph _iwl3945_set_bits_prph +#endif + +#define _iwl3945_set_bits_mask_prph(priv, reg, bits, mask) \ + _iwl3945_write_prph(priv, reg, ((_iwl3945_read_prph(priv, reg) & mask) | bits)) + +#ifdef CONFIG_IWL3945_DEBUG +static inline void __iwl3945_set_bits_mask_prph(u32 line, + struct iwl3945_priv *priv, u32 reg, u32 bits, u32 mask) +{ + if (!atomic_read(&priv->restrict_refcnt)) + IWL_ERROR("Nic access not held from line %d\n", line); + _iwl3945_set_bits_mask_prph(priv, reg, bits, mask); +} +#define iwl3945_set_bits_mask_prph(priv, reg, bits, mask) \ + __iwl3945_set_bits_mask_prph(__LINE__, priv, reg, bits, mask) +#else +#define iwl3945_set_bits_mask_prph _iwl3945_set_bits_mask_prph +#endif + +static inline void iwl3945_clear_bits_prph(struct iwl3945_priv + *priv, u32 reg, u32 mask) +{ + u32 val = _iwl3945_read_prph(priv, reg); + _iwl3945_write_prph(priv, reg, (val & ~mask)); +} + +static inline u32 iwl3945_read_targ_mem(struct iwl3945_priv *priv, u32 addr) +{ + iwl3945_write_direct32(priv, HBUS_TARG_MEM_RADDR, addr); + return iwl3945_read_direct32(priv, HBUS_TARG_MEM_RDAT); +} + +static inline void iwl3945_write_targ_mem(struct iwl3945_priv *priv, u32 addr, u32 val) +{ + iwl3945_write_direct32(priv, HBUS_TARG_MEM_WADDR, addr); + iwl3945_write_direct32(priv, HBUS_TARG_MEM_WDAT, val); +} + +static inline void iwl3945_write_targ_mem_buf(struct iwl3945_priv *priv, u32 addr, + u32 len, u32 *values) +{ + iwl3945_write_direct32(priv, HBUS_TARG_MEM_WADDR, addr); + for (; 0 < len; len -= sizeof(u32), values++) + iwl3945_write_direct32(priv, HBUS_TARG_MEM_WDAT, *values); +} +#endif diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c index 2bffb11..adcd106 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c @@ -37,18 +37,13 @@ #include <linux/workqueue.h> -#include <net/mac80211.h> -#include <linux/wireless.h> - #include "../net/mac80211/ieee80211_rate.h" -#include "iwl-3945-rs.h" -#include "iwlwifi.h" -#include "iwl-helpers.h" +#include "iwl-3945.h" #define RS_NAME "iwl-3945-rs" -struct iwl_rate_scale_data { +struct iwl3945_rate_scale_data { u64 data; s32 success_counter; s32 success_ratio; @@ -57,43 +52,44 @@ struct iwl_rate_scale_data { unsigned long stamp; }; -struct iwl_rate_scale_priv { +struct iwl3945_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 tgg; + u8 flush_pending; u8 start_rate; + u8 ibss_sta_added; struct timer_list rate_scale_flush; - struct iwl_rate_scale_data win[IWL_RATE_COUNT]; + struct iwl3945_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 iwl3945_expected_tpt_g[IWL_RATE_COUNT] = { + 7, 13, 35, 58, 0, 0, 76, 104, 130, 168, 191, 202 }; -static s32 iwl_expected_tpt_g_prot[IWL_RATE_COUNT] = { - 0, 0, 0, 80, 93, 113, 123, 125, 7, 13, 35, 58 +static s32 iwl3945_expected_tpt_g_prot[IWL_RATE_COUNT] = { + 7, 13, 35, 58, 0, 0, 0, 80, 93, 113, 123, 125 }; -static s32 iwl_expected_tpt_a[IWL_RATE_COUNT] = { - 40, 57, 72, 98, 121, 154, 177, 186, 0, 0, 0, 0 +static s32 iwl3945_expected_tpt_a[IWL_RATE_COUNT] = { + 0, 0, 0, 0, 40, 57, 72, 98, 121, 154, 177, 186 }; -static s32 iwl_expected_tpt_b[IWL_RATE_COUNT] = { - 0, 0, 0, 0, 0, 0, 0, 0, 7, 13, 35, 58 +static s32 iwl3945_expected_tpt_b[IWL_RATE_COUNT] = { + 7, 13, 35, 58, 0, 0, 0, 0, 0, 0, 0, 0 }; -struct iwl_tpt_entry { +struct iwl3945_tpt_entry { s8 min_rssi; u8 index; }; -static struct iwl_tpt_entry iwl_tpt_table_a[] = { +static struct iwl3945_tpt_entry iwl3945_tpt_table_a[] = { {-60, IWL_RATE_54M_INDEX}, {-64, IWL_RATE_48M_INDEX}, {-72, IWL_RATE_36M_INDEX}, @@ -104,7 +100,7 @@ static struct iwl_tpt_entry iwl_tpt_table_a[] = { {-89, IWL_RATE_6M_INDEX} }; -static struct iwl_tpt_entry iwl_tpt_table_b[] = { +static struct iwl3945_tpt_entry iwl3945_tpt_table_b[] = { {-86, IWL_RATE_11M_INDEX}, {-88, IWL_RATE_5M_INDEX}, {-90, IWL_RATE_2M_INDEX}, @@ -112,7 +108,7 @@ static struct iwl_tpt_entry iwl_tpt_table_b[] = { }; -static struct iwl_tpt_entry iwl_tpt_table_g[] = { +static struct iwl3945_tpt_entry iwl3945_tpt_table_g[] = { {-60, IWL_RATE_54M_INDEX}, {-64, IWL_RATE_48M_INDEX}, {-68, IWL_RATE_36M_INDEX}, @@ -133,30 +129,30 @@ static struct iwl_tpt_entry iwl_tpt_table_g[] = { #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) +static u8 iwl3945_get_rate_index_by_rssi(s32 rssi, u8 mode) { u32 index = 0; u32 table_size = 0; - struct iwl_tpt_entry *tpt_table = NULL; + struct iwl3945_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 = GLOBAL_ARRAY_SIZE(iwl_tpt_table_g); + tpt_table = iwl3945_tpt_table_g; + table_size = ARRAY_SIZE(iwl3945_tpt_table_g); break; case MODE_IEEE80211A: - tpt_table = iwl_tpt_table_a; - table_size = GLOBAL_ARRAY_SIZE(iwl_tpt_table_a); + tpt_table = iwl3945_tpt_table_a; + table_size = ARRAY_SIZE(iwl3945_tpt_table_a); break; default: case MODE_IEEE80211B: - tpt_table = iwl_tpt_table_b; - table_size = GLOBAL_ARRAY_SIZE(iwl_tpt_table_b); + tpt_table = iwl3945_tpt_table_b; + table_size = ARRAY_SIZE(iwl3945_tpt_table_b); break; } @@ -168,7 +164,7 @@ static u8 iwl_get_rate_index_by_rssi(s32 rssi, u8 mode) return tpt_table[index].index; } -static void iwl_clear_window(struct iwl_rate_scale_data *window) +static void iwl3945_clear_window(struct iwl3945_rate_scale_data *window) { window->data = 0; window->success_counter = 0; @@ -179,13 +175,13 @@ static void iwl_clear_window(struct iwl_rate_scale_data *window) } /** - * iwl_rate_scale_flush_windows - flush out the rate scale windows + * iwl3945_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) +static int iwl3945_rate_scale_flush_windows(struct iwl3945_rate_scale_priv *rs_priv) { int unflushed = 0; int i; @@ -206,7 +202,7 @@ static int iwl_rate_scale_flush_windows(struct iwl_rate_scale_priv *rs_priv) IWL_DEBUG_RATE("flushing %d samples of rate " "index %d\n", rs_priv->win[i].counter, i); - iwl_clear_window(&rs_priv->win[i]); + iwl3945_clear_window(&rs_priv->win[i]); } else unflushed++; spin_unlock_irqrestore(&rs_priv->lock, flags); @@ -218,16 +214,16 @@ static int iwl_rate_scale_flush_windows(struct iwl_rate_scale_priv *rs_priv) #define IWL_RATE_FLUSH_MAX 5000 /* msec */ #define IWL_RATE_FLUSH_MIN 50 /* msec */ -static void iwl_bg_rate_scale_flush(unsigned long data) +static void iwl3945_bg_rate_scale_flush(unsigned long data) { - struct iwl_rate_scale_priv *rs_priv = (void *)data; + struct iwl3945_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); + unflushed = iwl3945_rate_scale_flush_windows(rs_priv); spin_lock_irqsave(&rs_priv->lock, flags); @@ -281,21 +277,20 @@ static void iwl_bg_rate_scale_flush(unsigned long data) } /** - * iwl_collect_tx_data - Update the success/failure sliding window + * iwl3945_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, +static void iwl3945_collect_tx_data(struct iwl3945_rate_scale_priv *rs_priv, + struct iwl3945_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"); + IWL_DEBUG_RATE("leave: retries == 0 -- should be at least 1\n"); return; } @@ -353,30 +348,37 @@ static void rs_rate_init(void *priv_rate, void *priv_sta, sta->last_txrate = sta->txrate; + /* For MODE_IEEE80211A mode it start at IWL_FIRST_OFDM_RATE */ + if (local->hw.conf.phymode == MODE_IEEE80211A) + sta->last_txrate += IWL_FIRST_OFDM_RATE; + 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) +/* rate scale requires free function to be implemented */ +static void rs_free(void *priv) { - IWL_DEBUG_RATE("enter\n"); - IWL_DEBUG_RATE("leave\n"); + return; +} +static void rs_clear(void *priv) +{ + return; } + static void *rs_alloc_sta(void *priv, gfp_t gfp) { - struct iwl_rate_scale_priv *rs_priv; + struct iwl3945_rate_scale_priv *rs_priv; int i; IWL_DEBUG_RATE("enter\n"); - rs_priv = kzalloc(sizeof(struct iwl_rate_scale_priv), gfp); + rs_priv = kzalloc(sizeof(struct iwl3945_rate_scale_priv), gfp); if (!rs_priv) { IWL_DEBUG_RATE("leave: ENOMEM\n"); return NULL; @@ -387,19 +389,20 @@ static void *rs_alloc_sta(void *priv, gfp_t gfp) rs_priv->start_rate = IWL_RATE_INVALID; /* default to just 802.11b */ - rs_priv->expected_tpt = iwl_expected_tpt_b; + rs_priv->expected_tpt = iwl3945_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; + rs_priv->rate_scale_flush.function = &iwl3945_bg_rate_scale_flush; for (i = 0; i < IWL_RATE_COUNT; i++) - iwl_clear_window(&rs_priv->win[i]); + iwl3945_clear_window(&rs_priv->win[i]); IWL_DEBUG_RATE("leave\n"); @@ -408,7 +411,7 @@ static void *rs_alloc_sta(void *priv, gfp_t gfp) static void rs_free_sta(void *priv, void *priv_sta) { - struct iwl_rate_scale_priv *rs_priv = priv_sta; + struct iwl3945_rate_scale_priv *rs_priv = priv_sta; IWL_DEBUG_RATE("enter\n"); del_timer_sync(&rs_priv->rate_scale_flush); @@ -416,15 +419,37 @@ static void rs_free_sta(void *priv, void *priv_sta) IWL_DEBUG_RATE("leave\n"); } -static void rs_clear(void *priv) + +/* + * get ieee prev rate from rate scale table. + * for A and B mode we need to overright prev + * value + */ +static int rs_adjust_next_rate(struct iwl3945_priv *priv, int rate) { - IWL_DEBUG_RATE("NOP\n"); -} + int next_rate = iwl3945_get_prev_ieee_rate(rate); + + switch (priv->phymode) { + case MODE_IEEE80211A: + if (rate == IWL_RATE_12M_INDEX) + next_rate = IWL_RATE_9M_INDEX; + else if (rate == IWL_RATE_6M_INDEX) + next_rate = IWL_RATE_6M_INDEX; + break; + case MODE_IEEE80211B: + if (rate == IWL_RATE_11M_INDEX_TABLE) + next_rate = IWL_RATE_5M_INDEX_TABLE; + break; + default: + break; + } + return next_rate; +} /** * rs_tx_status - Update rate control values based on Tx results * - * NOTE: Uses iwl_priv->retry_rate for the # of retries attempted by + * NOTE: Uses iwl3945_priv->retry_rate for the # of retries attempted by * the hardware for each rate. */ static void rs_tx_status(void *priv_rate, @@ -437,9 +462,9 @@ static void rs_tx_status(void *priv_rate, 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 iwl3945_priv *priv = (struct iwl3945_priv *)priv_rate; struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - struct iwl_rate_scale_priv *rs_priv; + struct iwl3945_rate_scale_priv *rs_priv; IWL_DEBUG_RATE("enter\n"); @@ -483,12 +508,13 @@ static void rs_tx_status(void *priv_rate, last_index = scale_rate_index; } else { current_count = priv->retry_rate; - last_index = iwl_get_prev_ieee_rate(scale_rate_index); + last_index = rs_adjust_next_rate(priv, + 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, + iwl3945_collect_tx_data(rs_priv, &rs_priv->win[scale_rate_index], 0, current_count); IWL_DEBUG_RATE("Update rate %d for %d retries.\n", @@ -498,15 +524,16 @@ static void rs_tx_status(void *priv_rate, if (retries) scale_rate_index = - iwl_get_prev_ieee_rate(scale_rate_index); + rs_adjust_next_rate(priv, 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, + iwl3945_collect_tx_data(rs_priv, &rs_priv->win[last_index], tx_resp->flags & IEEE80211_TX_STATUS_ACK, 1); @@ -533,7 +560,7 @@ static void rs_tx_status(void *priv_rate, return; } -static struct ieee80211_rate *iwl_get_lowest_rate(struct ieee80211_local +static struct ieee80211_rate *iwl3945_get_lowest_rate(struct ieee80211_local *local) { struct ieee80211_hw_mode *mode = local->oper_hw_mode; @@ -549,13 +576,13 @@ static struct ieee80211_rate *iwl_get_lowest_rate(struct ieee80211_local return &mode->rates[0]; } -static u16 iwl_get_adjacent_rate(struct iwl_rate_scale_priv *rs_priv, +static u16 iwl3945_get_adjacent_rate(struct iwl3945_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 + /* 802.11A walks to the next literal adjacent rate in * the rate table */ if (unlikely(phymode == MODE_IEEE80211A)) { int i; @@ -585,9 +612,9 @@ static u16 iwl_get_adjacent_rate(struct iwl_rate_scale_priv *rs_priv, low = index; while (low != IWL_RATE_INVALID) { if (rs_priv->tgg) - low = iwl_rates[low].prev_rs_tgg; + low = iwl3945_rates[low].prev_rs_tgg; else - low = iwl_rates[low].prev_rs; + low = iwl3945_rates[low].prev_rs; if (low == IWL_RATE_INVALID) break; if (rate_mask & (1 << low)) @@ -598,9 +625,9 @@ static u16 iwl_get_adjacent_rate(struct iwl_rate_scale_priv *rs_priv, high = index; while (high != IWL_RATE_INVALID) { if (rs_priv->tgg) - high = iwl_rates[high].next_rs_tgg; + high = iwl3945_rates[high].next_rs_tgg; else - high = iwl_rates[high].next_rs; + high = iwl3945_rates[high].next_rs; if (high == IWL_RATE_INVALID) break; if (rate_mask & (1 << high)) @@ -636,8 +663,8 @@ static struct ieee80211_rate *rs_get_rate(void *priv_rate, u8 high = IWL_RATE_INVALID; u16 high_low; int index; - struct iwl_rate_scale_priv *rs_priv; - struct iwl_rate_scale_data *window = NULL; + struct iwl3945_rate_scale_priv *rs_priv; + struct iwl3945_rate_scale_data *window = NULL; int current_tpt = IWL_INVALID_VALUE; int low_tpt = IWL_INVALID_VALUE; int high_tpt = IWL_INVALID_VALUE; @@ -648,7 +675,8 @@ static struct ieee80211_rate *rs_get_rate(void *priv_rate, 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; + struct iwl3945_priv *priv = (struct iwl3945_priv *)priv_rate; + DECLARE_MAC_BUF(mac); IWL_DEBUG_RATE("enter\n"); @@ -663,7 +691,7 @@ static struct ieee80211_rate *rs_get_rate(void *priv_rate, IWL_DEBUG_RATE("leave: lowest rate (not data or is " "multicast)\n"); - return iwl_get_lowest_rate(local); + return iwl3945_get_lowest_rate(local); } sta = sta_info_get(local, hdr->addr1); @@ -675,10 +703,27 @@ static struct ieee80211_rate *rs_get_rate(void *priv_rate, } rate_mask = sta->supp_rates; - index = min(sta->txrate & 0xffff, IWL_RATE_COUNT - 1); + index = min(sta->last_txrate & 0xffff, IWL_RATE_COUNT - 1); + + if (priv->phymode == (u8) MODE_IEEE80211A) + rate_mask = rate_mask << IWL_FIRST_OFDM_RATE; rs_priv = (void *)sta->rate_ctrl_priv; + if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) && + !rs_priv->ibss_sta_added) { + u8 sta_id = iwl3945_hw_find_station(priv, hdr->addr1); + + if (sta_id == IWL_INVALID_STATION) { + IWL_DEBUG_RATE("LQ: ADD station %s\n", + print_mac(mac, hdr->addr1)); + sta_id = iwl3945_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) { @@ -710,7 +755,7 @@ static struct ieee80211_rate *rs_get_rate(void *priv_rate, rs_priv->expected_tpt[index] + 64) / 128); current_tpt = window->average_tpt; - high_low = iwl_get_adjacent_rate(rs_priv, index, rate_mask, + high_low = iwl3945_get_adjacent_rate(rs_priv, index, rate_mask, local->hw.conf.phymode); low = high_low & 0xff; high = (high_low >> 8) & 0xff; @@ -729,9 +774,9 @@ static struct ieee80211_rate *rs_get_rate(void *priv_rate, 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)) { + (high_tpt == IWL_INVALID_VALUE)) scale_action = 1; - } else if ((low_tpt != IWL_INVALID_VALUE) && + else if ((low_tpt != IWL_INVALID_VALUE) && (high_tpt != IWL_INVALID_VALUE) && (low_tpt < current_tpt) && (high_tpt < current_tpt)) { @@ -790,7 +835,11 @@ static struct ieee80211_rate *rs_get_rate(void *priv_rate, out: sta->last_txrate = index; - sta->txrate = sta->last_txrate; + if (priv->phymode == (u8) MODE_IEEE80211A) + sta->txrate = sta->last_txrate - IWL_FIRST_OFDM_RATE; + else + sta->txrate = sta->last_txrate; + sta_info_put(sta); IWL_DEBUG_RATE("leave: %d\n", index); @@ -811,11 +860,11 @@ static struct rate_control_ops rs_ops = { .free_sta = rs_free_sta, }; -int iwl_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id) +int iwl3945_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 iwl3945_priv *priv = hw->priv; + struct iwl3945_rate_scale_priv *rs_priv; struct sta_info *sta; unsigned long flags; int count = 0, i; @@ -841,7 +890,7 @@ int iwl_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id) int j; count += - sprintf(&buf[count], " %2dMbs: ", iwl_rates[i].ieee / 2); + sprintf(&buf[count], " %2dMbs: ", iwl3945_rates[i].ieee / 2); mask = (1ULL << (IWL_RATE_MAX_WINDOW - 1)); for (j = 0; j < IWL_RATE_MAX_WINDOW; j++, mask >>= 1) @@ -850,7 +899,7 @@ int iwl_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id) samples += rs_priv->win[i].counter; good += rs_priv->win[i].success_counter; - success += rs_priv->win[i].success_counter * iwl_rates[i].ieee; + success += rs_priv->win[i].success_counter * iwl3945_rates[i].ieee; if (rs_priv->win[i].stamp) { int delta = @@ -863,7 +912,7 @@ int iwl_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id) } else buf[count++] = '\n'; - j = iwl_get_prev_ieee_rate(i); + j = iwl3945_get_prev_ieee_rate(i); if (j == i) break; i = j; @@ -874,7 +923,7 @@ int iwl_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id) /* 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 */ + * added from iwl3945_rates is actually 2X the rate */ if (samples) count += sprintf( &buf[count], @@ -888,21 +937,20 @@ int iwl_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id) return count; } -void iwl_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id) +void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id) { - struct iwl_priv *priv = hw->priv; + struct iwl3945_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 iwl3945_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 " - "aglo!\n"); + IWL_WARNING("iwl-3945-rs not selected as rate control algo!\n"); IWL_DEBUG_RATE("leave - mac80211 picked the wrong RC algo.\n"); return; } @@ -924,19 +972,19 @@ void iwl_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id) 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; + rs_priv->expected_tpt = iwl3945_expected_tpt_g_prot; } else - rs_priv->expected_tpt = iwl_expected_tpt_g; + rs_priv->expected_tpt = iwl3945_expected_tpt_g; break; case MODE_IEEE80211A: - rs_priv->expected_tpt = iwl_expected_tpt_a; + rs_priv->expected_tpt = iwl3945_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; + rs_priv->expected_tpt = iwl3945_expected_tpt_b; break; } @@ -949,19 +997,19 @@ void iwl_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id) 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); + rs_priv->start_rate = iwl3945_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); + iwl3945_rates[rs_priv->start_rate].plcp); } -void iwl_rate_control_register(void) +void iwl3945_rate_control_register(struct ieee80211_hw *hw) { ieee80211_rate_control_register(&rs_ops); } -void iwl_rate_control_unregister(void) +void iwl3945_rate_control_unregister(struct ieee80211_hw *hw) { ieee80211_rate_control_unregister(&rs_ops); } diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-rs.h b/drivers/net/wireless/iwlwifi/iwl-3945-rs.h index 25bdbcb..e21a5ba 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-rs.h +++ b/drivers/net/wireless/iwlwifi/iwl-3945-rs.h @@ -27,19 +27,29 @@ #ifndef __iwl_3945_rs_h__ #define __iwl_3945_rs_h__ -struct iwl_rate_info { - u8 plcp; - u8 ieee; +struct iwl3945_rate_info { + u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */ + u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */ 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 */ + u8 table_rs_index; /* index in rate scale table cmd */ + u8 prev_table_rs; /* prev in rate table cmd */ }; +/* + * These serve as indexes into + * struct iwl3945_rate_info iwl3945_rates[IWL_RATE_COUNT]; + */ enum { - IWL_RATE_6M_INDEX = 0, + 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, @@ -47,16 +57,28 @@ enum { 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_RATE_6M_INDEX_TABLE = 0, + IWL_RATE_9M_INDEX_TABLE, + IWL_RATE_12M_INDEX_TABLE, + IWL_RATE_18M_INDEX_TABLE, + IWL_RATE_24M_INDEX_TABLE, + IWL_RATE_36M_INDEX_TABLE, + IWL_RATE_48M_INDEX_TABLE, + IWL_RATE_54M_INDEX_TABLE, + IWL_RATE_1M_INDEX_TABLE, + IWL_RATE_2M_INDEX_TABLE, + IWL_RATE_5M_INDEX_TABLE, + IWL_RATE_11M_INDEX_TABLE, + IWL_RATE_INVM_INDEX_TABLE = IWL_RATE_INVM_INDEX, +}; + +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, @@ -77,6 +99,7 @@ enum { #define IWL_RATE_5M_MASK (1<<IWL_RATE_5M_INDEX) #define IWL_RATE_11M_MASK (1<<IWL_RATE_11M_INDEX) +/* 3945 uCode API values for (legacy) bit rates, both OFDM and CCK */ enum { IWL_RATE_6M_PLCP = 13, IWL_RATE_9M_PLCP = 15, @@ -92,6 +115,7 @@ enum { IWL_RATE_11M_PLCP = 110, }; +/* MAC header values for bit rates */ enum { IWL_RATE_6M_IEEE = 12, IWL_RATE_9M_IEEE = 18, @@ -140,82 +164,52 @@ enum { #define IWL_MIN_RSSI_VAL -100 #define IWL_MAX_RSSI_VAL 0 -extern const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT]; - -static inline int iwl_rate_index_from_plcp(int plcp) -{ - int i = 0; - for (i = 0; i < IWL_RATE_COUNT; i++) - if (iwl_rates[i].plcp == plcp) - return i; - return -1; -} +extern const struct iwl3945_rate_info iwl3945_rates[IWL_RATE_COUNT]; -static inline u8 iwl_rate_get_lowest_plcp(int rate_mask) +static inline u8 iwl3945_get_prev_ieee_rate(u8 rate_index) { - u8 i; + u8 rate = iwl3945_rates[rate_index].prev_ieee; - for (i = IWL_RATE_1M_INDEX; i != IWL_RATE_INVALID; - i = iwl_rates[i].next_ieee) { - if (rate_mask & (1 << i)) - return iwl_rates[i].plcp; - } - - return IWL_RATE_INVALID; -} - -static inline u8 iwl_get_prev_ieee_rate(u8 rate_index) -{ - u8 rate = iwl_rates[rate_index].prev_ieee; if (rate == IWL_RATE_INVALID) rate = rate_index; return rate; } -#if IWL == 3945 /** - * iwl_fill_rs_info - Fill an output text buffer with the rate representation + * iwl3945_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 + * the performance of the rate control algorithm and is not meant to be * parsed software. */ -extern int iwl_fill_rs_info(struct ieee80211_hw *, char *buf, u8 sta_id); +extern int iwl3945_fill_rs_info(struct ieee80211_hw *, char *buf, u8 sta_id); /** - * iwl_rate_scale_init - Initialize the rate scale table based on assoc info + * iwl3945_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 specific throughput 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); +extern void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id); /** - * iwl_rate_control_register - Register the rate control algorithm callbacks + * iwl3945_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 + * iwl3945_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(void); +extern void iwl3945_rate_control_register(struct ieee80211_hw *hw); /** - * iwl_rate_control_unregister - Unregister the rate control callbacks + * iwl3945_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(void); -#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(void) {} -static inline void iwl_rate_control_unregister(void) {} -#endif /* IWL == 3945 */ +extern void iwl3945_rate_control_unregister(struct ieee80211_hw *hw); #endif diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index 4325ff0..53e7927 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -38,11 +38,9 @@ #include <net/mac80211.h> #include <linux/etherdevice.h> -#include <linux/delay.h> -#include "iwlwifi.h" -#include "iwl-helpers.h" #include "iwl-3945.h" +#include "iwl-helpers.h" #include "iwl-3945-rs.h" #define IWL_DECLARE_RATE_INFO(r, ip, in, rp, rn, pp, np) \ @@ -53,7 +51,9 @@ IWL_RATE_##rp##M_INDEX, \ IWL_RATE_##rn##M_INDEX, \ IWL_RATE_##pp##M_INDEX, \ - IWL_RATE_##np##M_INDEX } + IWL_RATE_##np##M_INDEX, \ + IWL_RATE_##r##M_INDEX_TABLE, \ + IWL_RATE_##ip##M_INDEX_TABLE } /* * Parameter order: @@ -63,7 +63,11 @@ * maps to IWL_RATE_INVALID * */ -const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT] = { +const struct iwl3945_rate_info iwl3945_rates[IWL_RATE_COUNT] = { + 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 */ 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 */ @@ -72,18 +76,14 @@ const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT] = { 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 */ +/* 1 = enable the iwl3945_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 + * iwl3945_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). @@ -91,9 +91,9 @@ const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT] = { * 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) +void iwl3945_disable_events(struct iwl3945_priv *priv) { - int rc; + int ret; int i; u32 base; /* SRAM address of event log header */ u32 disable_ptr; /* SRAM address of event-disable bitmap array */ @@ -148,33 +148,32 @@ void iwl_disable_events(struct iwl_priv *priv) 0x00000000, /* 1503 - 1472 */ }; - base = priv->card_alive.log_event_table_ptr; - if (!VALID_RTC_DATA_ADDR(base)) { + base = le32_to_cpu(priv->card_alive.log_event_table_ptr); + if (!iwl3945_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) { + ret = iwl3945_grab_nic_access(priv); + if (ret) { 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); + disable_ptr = iwl3945_read_targ_mem(priv, base + (4 * sizeof(u32))); + array_size = iwl3945_read_targ_mem(priv, base + (5 * sizeof(u32))); + iwl3945_release_nic_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); + ret = iwl3945_grab_nic_access(priv); + for (i = 0; i < IWL_EVT_DISABLE_SIZE; i++) + iwl3945_write_targ_mem(priv, + disable_ptr + (i * sizeof(u32)), + evt_disable[i]); + + iwl3945_release_nic_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"); @@ -195,7 +194,7 @@ void iwl_disable_events(struct iwl_priv *priv) * IWL_ANTENNA_MAIN - Force MAIN antenna * IWL_ANTENNA_AUX - Force AUX antenna */ -int iwl3945_get_antenna_flags(const struct iwl_priv *priv) +__le32 iwl3945_get_antenna_flags(const struct iwl3945_priv *priv) { switch (priv->antenna) { case IWL_ANTENNA_DIVERSITY: @@ -223,15 +222,15 @@ int iwl3945_get_antenna_flags(const struct iwl_priv *priv) * * RX handler implementations * - * Used by base.c + * Used by iwl-base.c * *****************************************************************************/ -void iwl_hw_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) +void iwl3945_hw_rx_statistics(struct iwl3945_priv *priv, struct iwl3945_rx_mem_buffer *rxb) { - struct iwl_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl3945_rx_packet *pkt = (void *)rxb->skb->data; IWL_DEBUG_RX("Statistics notification received (%d vs %d).\n", - (int)sizeof(struct iwl_notif_statistics), + (int)sizeof(struct iwl3945_notif_statistics), le32_to_cpu(pkt->len)); memcpy(&priv->statistics, pkt->u.raw, sizeof(priv->statistics)); @@ -239,15 +238,15 @@ void iwl_hw_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) priv->last_statistics_time = jiffies; } -static void iwl3945_handle_data_packet(struct iwl_priv *priv, int is_data, - struct iwl_rx_mem_buffer *rxb, +static void iwl3945_handle_data_packet(struct iwl3945_priv *priv, int is_data, + struct iwl3945_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); + struct iwl3945_rx_packet *pkt = (struct iwl3945_rx_packet *)rxb->skb->data; + struct iwl3945_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt); + struct iwl3945_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 */ @@ -258,16 +257,16 @@ static void iwl3945_handle_data_packet(struct iwl_priv *priv, int is_data, /* We only process data packets if the interface is open */ if (unlikely(!priv->is_open)) { - IWL_DEBUG_DROP + IWL_DEBUG_DROP_LIMIT ("Dropping packet while interface is not open.\n"); return; } if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) { - if (param_hwcrypto) - iwl_set_decrypted_flag(priv, rxb->skb, + if (iwl3945_param_hwcrypto) + iwl3945_set_decrypted_flag(priv, rxb->skb, le32_to_cpu(rx_end->status), stats); - iwl_handle_data_packet_monitor(priv, rxb, IWL_RX_DATA(pkt), + iwl3945_handle_data_packet_monitor(priv, rxb, IWL_RX_DATA(pkt), len, stats, phy_flags); return; } @@ -278,25 +277,29 @@ static void iwl3945_handle_data_packet(struct iwl_priv *priv, int is_data, hdr = (void *)rxb->skb->data; - if (param_hwcrypto) - iwl_set_decrypted_flag(priv, rxb->skb, + if (iwl3945_param_hwcrypto) + iwl3945_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) +#define IWL_DELAY_NEXT_SCAN_AFTER_ASSOC (HZ*6) + +static void iwl3945_rx_reply_rx(struct iwl3945_priv *priv, + struct iwl3945_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 iwl3945_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl3945_rx_frame_stats *rx_stats = IWL_RX_STATS(pkt); + struct iwl3945_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt); + struct iwl3945_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), + .mactime = le64_to_cpu(rx_end->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) ? @@ -305,7 +308,6 @@ static void iwl3945_rx_reply_rx(struct iwl_priv *priv, .rate = rx_hdr->rate, .flag = 0, }; - u8 network_packet; int snr; @@ -316,9 +318,8 @@ static void iwl3945_rx_reply_rx(struct iwl_priv *priv, return; } - if (!(le32_to_cpu(rx_end->status) & RX_RES_STATUS_NO_CRC32_ERROR) - || !(le32_to_cpu(rx_end->status) & - RX_RES_STATUS_NO_RXE_OVERFLOW)) { + 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; } @@ -331,12 +332,9 @@ static void iwl3945_rx_reply_rx(struct iwl_priv *priv, /* Convert 3945's rssi indicator to dBm */ stats.ssi = rx_stats->rssi - IWL_RSSI_OFFSET; - /* Set default noise value to -127 ... this works better than 0 when - * averaging frames with/without noise info; measured values are - * always negative ... using a negative value as the default value - * keeps all averages within an s8's range of negative values. */ + /* Set default noise value to -127 */ if (priv->last_rx_noise == 0) - priv->last_rx_noise = -127; + 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 @@ -349,43 +347,43 @@ static void iwl3945_rx_reply_rx(struct iwl_priv *priv, * 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 (rx_stats_noise_diff) { + snr = rx_stats_sig_avg / rx_stats_noise_diff; + stats.noise = stats.ssi - iwl3945_calc_db_from_ratio(snr); + stats.signal = iwl3945_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); + stats.signal = iwl3945_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); + rx_stats_sig_avg, rx_stats_noise_diff); stats.freq = ieee80211chan2mhz(stats.channel); - /* can be covered by iwl_report_frame() in most cases */ + /* can be covered by iwl3945_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); + network_packet = iwl3945_is_network_packet(priv, header); -#ifdef CONFIG_IWLWIFI_DEBUG - if (iwl_debug_level & IWL_DL_STATS && net_ratelimit()) +#ifdef CONFIG_IWL3945_DEBUG + if (iwl3945_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)) + if (iwl3945_debug_level & (IWL_DL_RX)) /* Set "1" to report good data frames in groups of 100 */ - iwl_report_frame(priv, pkt, header, 1); + iwl3945_report_frame(priv, pkt, header, 1); #endif if (network_packet) { @@ -395,9 +393,10 @@ static void iwl3945_rx_reply_rx(struct iwl_priv *priv, priv->last_rx_noise = stats.noise; } - switch (WLAN_FC_GET_TYPE(le16_to_cpu(header->frame_control))) { + switch (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FTYPE) { case IEEE80211_FTYPE_MGMT: - switch (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_control))) { + 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 @@ -415,7 +414,7 @@ static void iwl3945_rx_reply_rx(struct iwl_priv *priv, pos = (__le32 *) & mgmt->u.beacon. timestamp; - priv->timestamp0 = le64_to_cpu(pos[0]); + 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); @@ -445,6 +444,13 @@ static void iwl3945_rx_reply_rx(struct iwl_priv *priv, case IEEE80211_STYPE_REASSOC_RESP:{ struct ieee80211_mgmt *mgnt = (struct ieee80211_mgmt *)header; + + /* We have just associated, give some + * time for the 4-way handshake if + * any. Don't start scan too early. */ + priv->next_scan_jiffies = jiffies + + IWL_DELAY_NEXT_SCAN_AFTER_ASSOC; + priv->assoc_id = (~((1 << 15) | (1 << 14)) & le16_to_cpu(mgnt->u. assoc_resp.aid)); @@ -459,13 +465,16 @@ static void iwl3945_rx_reply_rx(struct iwl_priv *priv, } case IEEE80211_STYPE_PROBE_REQ:{ + DECLARE_MAC_BUF(mac1); + DECLARE_MAC_BUF(mac2); + DECLARE_MAC_BUF(mac3); 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)); + ("Dropping (non network): %s" + ", %s, %s\n", + print_mac(mac1, header->addr1), + print_mac(mac2, header->addr2), + print_mac(mac3, header->addr3)); return; } } @@ -476,26 +485,30 @@ static void iwl3945_rx_reply_rx(struct iwl_priv *priv, 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)); + case IEEE80211_FTYPE_DATA: { + DECLARE_MAC_BUF(mac1); + DECLARE_MAC_BUF(mac2); + DECLARE_MAC_BUF(mac3); + + if (unlikely(iwl3945_is_duplicate_packet(priv, header))) + IWL_DEBUG_DROP("Dropping (dup): %s, %s, %s\n", + print_mac(mac1, header->addr1), + print_mac(mac2, header->addr2), + print_mac(mac3, header->addr3)); else iwl3945_handle_data_packet(priv, 1, rxb, &stats, phy_flags); break; } + } } -int iwl_hw_tx_queue_attach_buffer_to_tfd(struct iwl_priv *priv, - void *ptr, dma_addr_t addr, u16 len) +int iwl3945_hw_txq_attach_buf_to_tfd(struct iwl3945_priv *priv, void *ptr, + dma_addr_t addr, u16 len) { int count; u32 pad; - struct iwl_tfd_frame *tfd = (struct iwl_tfd_frame *)ptr; + struct iwl3945_tfd_frame *tfd = (struct iwl3945_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)); @@ -518,14 +531,14 @@ int iwl_hw_tx_queue_attach_buffer_to_tfd(struct iwl_priv *priv, } /** - * iwl_hw_tx_queue_free_tfd - Free one TFD, those at index [txq->q.last_used] + * iwl3945_hw_txq_free_tfd - Free one TFD, those at index [txq->q.read_ptr] * * Does NOT advance any indexes */ -int iwl_hw_tx_queue_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq) +int iwl3945_hw_txq_free_tfd(struct iwl3945_priv *priv, struct iwl3945_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 iwl3945_tfd_frame *bd_tmp = (struct iwl3945_tfd_frame *)&txq->bd[0]; + struct iwl3945_tfd_frame *bd = &bd_tmp[txq->q.read_ptr]; struct pci_dev *dev = priv->pci_dev; int i; int counter; @@ -548,46 +561,47 @@ int iwl_hw_tx_queue_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq) 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]) { + if (txq->txb[txq->q.read_ptr].skb[0]) { + struct sk_buff *skb = txq->txb[txq->q.read_ptr].skb[0]; + if (txq->txb[txq->q.read_ptr].skb[0]) { /* Can be called from interrupt context */ dev_kfree_skb_any(skb); - txq->txb[txq->q.last_used].skb[0] = NULL; + txq->txb[txq->q.read_ptr].skb[0] = NULL; } } } return 0; } -u8 iwl_hw_find_station(struct iwl_priv * priv, const u8 * bssid) +u8 iwl3945_hw_find_station(struct iwl3945_priv *priv, const u8 *addr) { int i; int ret = IWL_INVALID_STATION; unsigned long flags; + DECLARE_MAC_BUF(mac); spin_lock_irqsave(&priv->sta_lock, flags); - for (i = IWL_STA_ID; i < (IWL_STA_ID + priv->num_stations); i++) + for (i = IWL_STA_ID; i < priv->hw_setting.max_stations; i++) if ((priv->stations[i].used) && (!compare_ether_addr - (priv->stations[i].sta.sta.addr, bssid))) { + (priv->stations[i].sta.sta.addr, addr))) { ret = i; goto out; } - IWL_DEBUG_INFO("can not find STA " MAC_FMT " (total %d)\n", - MAC_ARG(bssid), priv->num_stations); + IWL_DEBUG_INFO("can not find STA %s (total %d)\n", + print_mac(mac, addr), 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 + * iwl3945_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, +void iwl3945_hw_build_tx_cmd_rate(struct iwl3945_priv *priv, + struct iwl3945_cmd *cmd, struct ieee80211_tx_control *ctrl, struct ieee80211_hdr *hdr, int sta_id, int tx_id) { @@ -597,10 +611,10 @@ void iwl_hw_build_tx_cmd_rate(struct iwl_priv *priv, int rate; u8 rts_retry_limit; u8 data_retry_limit; - u32 tx_flags; + __le32 tx_flags; u16 fc = le16_to_cpu(hdr->frame_control); - rate = iwl_rates[rate_index].plcp; + rate = iwl3945_rates[rate_index].plcp; tx_flags = cmd->cmd.tx.tx_flags; /* We need to figure out how to get the sta->supp_rates while @@ -612,7 +626,8 @@ void iwl_hw_build_tx_cmd_rate(struct iwl_priv *priv, 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)) + (sta_id != IWL3945_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); @@ -632,8 +647,8 @@ void iwl_hw_build_tx_cmd_rate(struct iwl_priv *priv, if (priv->data_retry_limit != -1) data_retry_limit = priv->data_retry_limit; - if (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_MGMT) { - switch (WLAN_FC_GET_STYPE(fc)) { + 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: @@ -654,41 +669,49 @@ void iwl_hw_build_tx_cmd_rate(struct iwl_priv *priv, cmd->cmd.tx.tx_flags = tx_flags; /* OFDM */ - cmd->cmd.tx.supp_rates[0] = rate_mask & IWL_OFDM_RATES_MASK; + cmd->cmd.tx.supp_rates[0] = + ((rate_mask & IWL_OFDM_RATES_MASK) >> IWL_FIRST_OFDM_RATE) & 0xFF; /* CCK */ - cmd->cmd.tx.supp_rates[1] = (rate_mask >> 8) & 0xF; + cmd->cmd.tx.supp_rates[1] = (rate_mask & 0xF); - IWL_DEBUG_RATE("Tx sta id: %d, rate: %d (plcp), flags: " BIT_FMT16 " " - "cck/ofdm mask: 0x%x/0x%x\n", - sta_id, - cmd->cmd.tx.rate, - BIT_ARG16(cmd->cmd.tx.tx_flags), + 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]); } -void iwl_hw_card_show_info(struct iwl_priv *priv) +u8 iwl3945_sync_sta(struct iwl3945_priv *priv, int sta_id, u16 tx_rate, u8 flags) { - 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)); + unsigned long flags_spin; + struct iwl3945_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]; - IWL_DEBUG_INFO("3945ABG PBA Number %.*s\n", - (int)sizeof(priv->eeprom.board_pba_number), - priv->eeprom.board_pba_number); + 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; - IWL_DEBUG_INFO("EEPROM_ANTENNA_SWITCH_TYPE is 0x%02X\n", - priv->eeprom.antenna_switch_type); + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + + iwl3945_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; } -static int iwl3945_nic_set_pwr_src(struct iwl_priv *priv, int pwr_max) +static int iwl3945_nic_set_pwr_src(struct iwl3945_priv *priv, int pwr_max) { int rc; unsigned long flags; spin_lock_irqsave(&priv->lock, flags); - rc = iwl_grab_restricted_access(priv); + rc = iwl3945_grab_nic_access(priv); if (rc) { spin_unlock_irqrestore(&priv->lock, flags); return rc; @@ -696,28 +719,27 @@ static int iwl3945_nic_set_pwr_src(struct iwl_priv *priv, int pwr_max) if (!pwr_max) { u32 val; - rc = pci_read_config_dword(priv->pci_dev, 0x0C8, &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); + iwl3945_set_bits_mask_prph(priv, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_PWR_SRC_VAUX, + ~APMG_PS_CTRL_MSK_PWR_SRC); + iwl3945_release_nic_access(priv); - iwl_poll_bit(priv, CSR_GPIO_IN, + iwl3945_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); - + iwl3945_release_nic_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); + iwl3945_set_bits_mask_prph(priv, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, + ~APMG_PS_CTRL_MSK_PWR_SRC); - iwl_release_restricted_access(priv); - iwl_poll_bit(priv, CSR_GPIO_IN, CSR_GPIO_IN_VAL_VMAIN_PWR_SRC, + iwl3945_release_nic_access(priv); + iwl3945_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); @@ -725,76 +747,72 @@ static int iwl3945_nic_set_pwr_src(struct iwl_priv *priv, int pwr_max) return rc; } -static int iwl3945_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq) +static int iwl3945_rx_init(struct iwl3945_priv *priv, struct iwl3945_rx_queue *rxq) { int rc; unsigned long flags; spin_lock_irqsave(&priv->lock, flags); - rc = iwl_grab_restricted_access(priv); + rc = iwl3945_grab_nic_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), + iwl3945_write_direct32(priv, FH_RCSR_RBD_BASE(0), rxq->dma_addr); + iwl3945_write_direct32(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), + offsetof(struct iwl3945_shared, rx_read_ptr[0])); + iwl3945_write_direct32(priv, FH_RCSR_WPTR(0), 0); + iwl3945_write_direct32(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) | + (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); + iwl3945_read_direct32(priv, FH_RSSR_CTRL); + iwl3945_release_nic_access(priv); spin_unlock_irqrestore(&priv->lock, flags); return 0; } -static int iwl3945_tx_reset(struct iwl_priv *priv) +static int iwl3945_tx_reset(struct iwl3945_priv *priv) { int rc; unsigned long flags; spin_lock_irqsave(&priv->lock, flags); - rc = iwl_grab_restricted_access(priv); + rc = iwl3945_grab_nic_access(priv); if (rc) { spin_unlock_irqrestore(&priv->lock, flags); return rc; } /* bypass mode */ - iwl_write_restricted_reg(priv, SCD_MODE_REG, 0x2); + iwl3945_write_prph(priv, ALM_SCD_MODE_REG, 0x2); /* RA 0 is active */ - iwl_write_restricted_reg(priv, SCD_ARASTAT_REG, 0x01); + iwl3945_write_prph(priv, ALM_SCD_ARASTAT_REG, 0x01); /* all 6 fifo are active */ - iwl_write_restricted_reg(priv, SCD_TXFACT_REG, 0x3f); + iwl3945_write_prph(priv, ALM_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); + iwl3945_write_prph(priv, ALM_SCD_SBYP_MODE_1_REG, 0x010000); + iwl3945_write_prph(priv, ALM_SCD_SBYP_MODE_2_REG, 0x030002); + iwl3945_write_prph(priv, ALM_SCD_TXF4MF_REG, 0x000004); + iwl3945_write_prph(priv, ALM_SCD_TXF5MF_REG, 0x000005); - iwl_write_restricted(priv, FH_TSSR_CBB_BASE, + iwl3945_write_direct32(priv, FH_TSSR_CBB_BASE, priv->hw_setting.shared_phys); - iwl_write_restricted( - priv, FH_TSSR_MSG_CONFIG, + iwl3945_write_direct32(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 | @@ -803,8 +821,7 @@ static int iwl3945_tx_reset(struct iwl_priv *priv) 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); - + iwl3945_release_nic_access(priv); spin_unlock_irqrestore(&priv->lock, flags); return 0; @@ -815,12 +832,12 @@ static int iwl3945_tx_reset(struct iwl_priv *priv) * * Destroys all DMA structures and initialize them again */ -static int iwl3945_txq_ctx_reset(struct iwl_priv *priv) +static int iwl3945_txq_ctx_reset(struct iwl3945_priv *priv) { int rc; - int i, num_slots; + int txq_id, slots_num; - iwl_hw_txq_ctx_free(priv); + iwl3945_hw_txq_ctx_free(priv); /* Tx CMD queue */ rc = iwl3945_tx_reset(priv); @@ -828,13 +845,13 @@ static int iwl3945_txq_ctx_reset(struct iwl_priv *priv) goto error; /* Tx queue(s) */ - for (i = 0; i < TFD_QUEUE_MAX; i++) { - num_slots = - (i == IWL_CMD_QUEUE_NUM) ? TFD_CMD_SLOTS : - TFD_TX_CMD_SLOTS; - rc = iwl_tx_queue_init(priv, &priv->txq[i], num_slots, i); + 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 = iwl3945_tx_queue_init(priv, &priv->txq[txq_id], slots_num, + txq_id); if (rc) { - IWL_ERROR("Tx %d queue init failed\n", i); + IWL_ERROR("Tx %d queue init failed\n", txq_id); goto error; } } @@ -842,26 +859,26 @@ static int iwl3945_txq_ctx_reset(struct iwl_priv *priv) return rc; error: - iwl_hw_txq_ctx_free(priv); + iwl3945_hw_txq_ctx_free(priv); return rc; } -int iwl_hw_nic_init(struct iwl_priv *priv) +int iwl3945_hw_nic_init(struct iwl3945_priv *priv) { u8 rev_id; int rc; unsigned long flags; - struct iwl_rx_queue *rxq = &priv->rxq; + struct iwl3945_rx_queue *rxq = &priv->rxq; - iwl_power_init_handle(priv); + iwl3945_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, + iwl3945_set_bit(priv, CSR_ANA_PLL_CFG, (1 << 24)); + iwl3945_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, + iwl3945_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + rc = iwl3945_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) { @@ -870,18 +887,18 @@ int iwl_hw_nic_init(struct iwl_priv *priv) return rc; } - rc = iwl_grab_restricted_access(priv); + rc = iwl3945_grab_nic_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); + iwl3945_write_prph(priv, APMG_CLK_EN_REG, + APMG_CLK_VAL_DMA_CLK_RQT | + APMG_CLK_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); + iwl3945_set_bits_prph(priv, APMG_PCIDEV_STT_REG, + APMG_PCIDEV_STT_VAL_L1_ACT_DIS); + iwl3945_release_nic_access(priv); spin_unlock_irqrestore(&priv->lock, flags); /* Determine HW type */ @@ -897,51 +914,50 @@ int iwl_hw_nic_init(struct iwl_priv *priv) 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, + iwl3945_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, + iwl3945_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); + rc = iwl3945_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, + iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BIT_SKU_MRC); - } else { + } 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, + iwl3945_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, + iwl3945_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, + iwl3945_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, + iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B); } spin_unlock_irqrestore(&priv->lock, flags); @@ -954,33 +970,32 @@ int iwl_hw_nic_init(struct iwl_priv *priv) /* Allocate the RX queue, or reset if it is already allocated */ if (!rxq->bd) { - rc = iwl_rx_queue_alloc(priv); + rc = iwl3945_rx_queue_alloc(priv); if (rc) { IWL_ERROR("Unable to initialize Rx queue\n"); return -ENOMEM; } } else - iwl_rx_queue_reset(priv, rxq); + iwl3945_rx_queue_reset(priv, rxq); - iwl_rx_replenish(priv, 1); + iwl3945_rx_replenish(priv); iwl3945_rx_init(priv, rxq); spin_lock_irqsave(&priv->lock, flags); -/* - * Look at using this instead ::: + /* Look at using this instead: rxq->need_update = 1; - iwl_rx_queue_update_write_ptr(priv, rxq); -*/ + iwl3945_rx_queue_update_write_ptr(priv, rxq); + */ - rc = iwl_grab_restricted_access(priv); + rc = iwl3945_grab_nic_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); + iwl3945_write_direct32(priv, FH_RCSR_WPTR(0), rxq->write & ~7); + iwl3945_release_nic_access(priv); spin_unlock_irqrestore(&priv->lock, flags); @@ -988,55 +1003,55 @@ int iwl_hw_nic_init(struct iwl_priv *priv) if (rc) return rc; - priv->status |= STATUS_INIT; + set_bit(STATUS_INIT, &priv->status); return 0; } /** - * iwl_hw_txq_ctx_free - Free TXQ Context + * iwl3945_hw_txq_ctx_free - Free TXQ Context * * Destroy all TX DMA queues and structures */ -void iwl_hw_txq_ctx_free(struct iwl_priv *priv) +void iwl3945_hw_txq_ctx_free(struct iwl3945_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]); + iwl3945_tx_queue_free(priv, &priv->txq[txq_id]); } -void iwl_hw_txq_ctx_stop(struct iwl_priv *priv) +void iwl3945_hw_txq_ctx_stop(struct iwl3945_priv *priv) { int queue; unsigned long flags; spin_lock_irqsave(&priv->lock, flags); - if (iwl_grab_restricted_access(priv)) { + if (iwl3945_grab_nic_access(priv)) { spin_unlock_irqrestore(&priv->lock, flags); - iwl_hw_txq_ctx_free(priv); + iwl3945_hw_txq_ctx_free(priv); return; } /* stop SCD */ - iwl_write_restricted_reg(priv, SCD_MODE_REG, 0); + iwl3945_write_prph(priv, ALM_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); + iwl3945_write_direct32(priv, FH_TCSR_CONFIG(queue), 0x0); + iwl3945_poll_direct_bit(priv, FH_TSSR_TX_STATUS, + ALM_FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(queue), + 1000); } - iwl_release_restricted_access(priv); + iwl3945_release_nic_access(priv); spin_unlock_irqrestore(&priv->lock, flags); - iwl_hw_txq_ctx_free(priv); + iwl3945_hw_txq_ctx_free(priv); } -int iwl_hw_nic_stop_master(struct iwl_priv *priv) +int iwl3945_hw_nic_stop_master(struct iwl3945_priv *priv) { int rc = 0; u32 reg_val; @@ -1045,17 +1060,16 @@ int iwl_hw_nic_stop_master(struct iwl_priv *priv) spin_lock_irqsave(&priv->lock, flags); /* set stop master bit */ - iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER); + iwl3945_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER); - reg_val = iwl_read32(priv, CSR_GP_CNTRL); + reg_val = iwl3945_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, + (reg_val & CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE)) + IWL_DEBUG_INFO("Card in power save, master is already " + "stopped\n"); + else { + rc = iwl3945_poll_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_MASTER_DISABLED, CSR_RESET_REG_FLAG_MASTER_DISABLED, 100); if (rc < 0) { @@ -1070,52 +1084,51 @@ int iwl_hw_nic_stop_master(struct iwl_priv *priv) return rc; } -int iwl_hw_nic_reset(struct iwl_priv *priv) +int iwl3945_hw_nic_reset(struct iwl3945_priv *priv) { int rc; unsigned long flags; - iwl_hw_nic_stop_master(priv); + iwl3945_hw_nic_stop_master(priv); spin_lock_irqsave(&priv->lock, flags); - iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); + iwl3945_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); - rc = iwl_poll_bit(priv, CSR_GP_CNTRL, + rc = iwl3945_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); + rc = iwl3945_grab_nic_access(priv); + if (!rc) { + iwl3945_write_prph(priv, APMG_CLK_CTRL_REG, + APMG_CLK_VAL_BSM_CLK_RQT); udelay(10); - iwl_set_bit(priv, CSR_GP_CNTRL, + iwl3945_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); + iwl3945_write_prph(priv, APMG_RTC_INT_MSK_REG, 0x0); + iwl3945_write_prph(priv, APMG_RTC_INT_STT_REG, + 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); + iwl3945_write_prph(priv, APMG_CLK_EN_REG, + APMG_CLK_VAL_DMA_CLK_RQT | + APMG_CLK_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); + iwl3945_set_bits_prph(priv, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_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); + iwl3945_clear_bits_prph(priv, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_RESET_REQ); + iwl3945_release_nic_access(priv); } /* Clear the 'host command active' bit... */ - priv->status &= ~STATUS_HCMD_ACTIVE; + clear_bit(STATUS_HCMD_ACTIVE, &priv->status); wake_up_interruptible(&priv->wait_command_queue); spin_unlock_irqrestore(&priv->lock, flags); @@ -1124,48 +1137,49 @@ int iwl_hw_nic_reset(struct iwl_priv *priv) } /** - * reg_adjust_power_by_temp - return index delta into power gain settings table - */ -static int reg_adjust_power_by_temp(int new_reading, int old_reading) + * iwl3945_hw_reg_adjust_power_by_temp + * return index delta into power gain settings table +*/ +static int iwl3945_hw_reg_adjust_power_by_temp(int new_reading, int old_reading) { return (new_reading - old_reading) * (-11) / 100; } /** - * reg_temp_out_of_range - Keep temperature in sane range + * iwl3945_hw_reg_temp_out_of_range - Keep temperature in sane range */ -static inline int reg_temp_out_of_range(int temperature) +static inline int iwl3945_hw_reg_temp_out_of_range(int temperature) { return (((temperature < -260) || (temperature > 25)) ? 1 : 0); } -int iwl_hw_get_temperature(struct iwl_priv *priv) +int iwl3945_hw_get_temperature(struct iwl3945_priv *priv) { - return iwl_read32(priv, CSR_UCODE_DRV_GP2); + return iwl3945_read32(priv, CSR_UCODE_DRV_GP2); } /** - * reg_txpower_get_temperature - get current temperature by reading from NIC - */ -static int reg_txpower_get_temperature(struct iwl_priv *priv) + * iwl3945_hw_reg_txpower_get_temperature + * get the current temperature by reading from NIC +*/ +static int iwl3945_hw_reg_txpower_get_temperature(struct iwl3945_priv *priv) { int temperature; - temperature = iwl_hw_get_temperature(priv); + temperature = iwl3945_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 (reg_temp_out_of_range(temperature)) { + if (iwl3945_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 = - (s16)le16_to_cpu(priv->eeprom.groups[2].temperature); + temperature = priv->eeprom.groups[2].temperature; else /* else use most recent "sane" value from driver */ temperature = priv->last_temperature; } @@ -1184,11 +1198,11 @@ static int reg_txpower_get_temperature(struct iwl_priv *priv) * 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) +static int is_temp_calib_needed(struct iwl3945_priv *priv) { int temp_diff; - priv->temperature = reg_txpower_get_temperature(priv); + priv->temperature = iwl3945_hw_reg_txpower_get_temperature(priv); temp_diff = priv->temperature - priv->last_temperature; /* get absolute value */ @@ -1220,7 +1234,7 @@ static int is_temp_calib_needed(struct iwl_priv *priv) /* 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] = { +static struct iwl3945_tx_power power_gain_table[2][IWL_MAX_GAIN_ENTRIES] = { { {251, 127}, /* 2.4 GHz, highest power */ {251, 127}, @@ -1299,7 +1313,7 @@ static struct iwl_tx_power power_gain_table[2][IWL_MAX_GAIN_ENTRIES] = { {3, 113}, {3, 106}, {3, 102}, - {3, 95}}, /* 2.4 GHz, lowest power */ + {3, 95} }, /* 2.4 GHz, lowest power */ { {251, 127}, /* 5.x GHz, highest power */ {251, 120}, @@ -1378,10 +1392,10 @@ static struct iwl_tx_power power_gain_table[2][IWL_MAX_GAIN_ENTRIES] = { {35, 113}, {35, 107}, {35, 99}, - {3, 120}} /* 5.x GHz, lowest power */ + {3, 120} } /* 5.x GHz, lowest power */ }; -static inline u8 reg_fix_power_index(int index) +static inline u8 iwl3945_hw_reg_fix_power_index(int index) { if (index < 0) return 0; @@ -1394,17 +1408,17 @@ static inline u8 reg_fix_power_index(int index) #define REG_RECALIB_PERIOD (60) /** - * reg_set_scan_power - Set Tx power for scan probe requests + * iwl3945_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 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, +static void iwl3945_hw_reg_set_scan_power(struct iwl3945_priv *priv, u32 scan_tbl_index, + s32 rate_index, const s8 *clip_pwrs, + struct iwl3945_channel_info *ch_info, int band_index) { - struct iwl_scan_power_info *scan_power_info; + struct iwl3945_scan_power_info *scan_power_info; s8 power; u8 power_index; @@ -1413,7 +1427,7 @@ static void reg_set_scan_power(struct iwl_priv *priv, u32 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]); + power = min(ch_info->scan_power, clip_pwrs[IWL_RATE_6M_INDEX_TABLE]); /* further limit to user's max power preference. * FIXME: Other spectrum management power limitations do not @@ -1428,12 +1442,7 @@ static void reg_set_scan_power(struct iwl_priv *priv, u32 scan_tbl_index, * *index*. */ power_index = ch_info->power_info[rate_index].power_table_index - (power - ch_info->power_info - [IWL_RATE_6M_INDEX].requested_power) * 2; - -#if 0 - IWL_DEBUG_POWER("chnl %d scan power index %d\n", - ch_info->channel, power_index); -#endif + [IWL_RATE_6M_INDEX_TABLE].requested_power) * 2; /* store reference index that we use when adjusting *all* scan * powers. So we can accommodate user (all channel) or spectrum @@ -1445,7 +1454,7 @@ static void reg_set_scan_power(struct iwl_priv *priv, u32 scan_tbl_index, * of the table. */ /* don't exceed table bounds for "real" setting */ - power_index = reg_fix_power_index(power_index); + power_index = iwl3945_hw_reg_fix_power_index(power_index); scan_power_info->power_table_index = power_index; scan_power_info->tpc.tx_gain = @@ -1455,21 +1464,21 @@ static void reg_set_scan_power(struct iwl_priv *priv, u32 scan_tbl_index, } /** - * iwl_hw_reg_send_txpower - fill in Tx Power command with gain settings + * iwl3945_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 iwl3945_hw_reg_send_txpower(struct iwl3945_priv *priv) { - int rate_idx; - const struct iwl_channel_info *ch_info = NULL; - struct iwl_txpowertable_cmd txpower = { + int rate_idx, i; + const struct iwl3945_channel_info *ch_info = NULL; + struct iwl3945_txpowertable_cmd txpower = { .channel = priv->active_rxon.channel, }; txpower.band = (priv->phymode == MODE_IEEE80211A) ? 0 : 1; - ch_info = iwl_get_channel_info(priv, + ch_info = iwl3945_get_channel_info(priv, priv->phymode, le16_to_cpu(priv->active_rxon.channel)); if (!ch_info) { @@ -1486,25 +1495,41 @@ int iwl_hw_reg_send_txpower(struct iwl_priv *priv) } /* 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; + /* Fill OFDM rate */ + for (rate_idx = IWL_FIRST_OFDM_RATE, i = 0; + rate_idx <= IWL_LAST_OFDM_RATE; rate_idx++, i++) { + + txpower.power[i].tpc = ch_info->power_info[i].tpc; + txpower.power[i].rate = iwl3945_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[i].tpc.tx_gain, + txpower.power[i].tpc.dsp_atten, + txpower.power[i].rate); + } + /* Fill CCK rates */ + for (rate_idx = IWL_FIRST_CCK_RATE; + rate_idx <= IWL_LAST_CCK_RATE; rate_idx++, i++) { + txpower.power[i].tpc = ch_info->power_info[i].tpc; + txpower.power[i].rate = iwl3945_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); + txpower.power[i].tpc.tx_gain, + txpower.power[i].tpc.dsp_atten, + txpower.power[i].rate); } - return iwl_send_cmd_pdu(priv, REPLY_TX_PWR_TABLE_CMD, - sizeof(struct iwl_txpowertable_cmd), &txpower); + return iwl3945_send_cmd_pdu(priv, REPLY_TX_PWR_TABLE_CMD, + sizeof(struct iwl3945_txpowertable_cmd), &txpower); } /** - * reg_set_new_power - Configures power tables at new levels + * iwl3945_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 @@ -1515,14 +1540,14 @@ int iwl_hw_reg_send_txpower(struct iwl_priv *priv) * * 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 + * 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 reg_set_new_power(struct iwl_priv *priv, - struct iwl_channel_info *ch_info) +static int iwl3945_hw_reg_set_new_power(struct iwl3945_priv *priv, + struct iwl3945_channel_info *ch_info) { - struct iwl_channel_power_info *power_info; + struct iwl3945_channel_power_info *power_info; int power_changed = 0; int i; const s8 *clip_pwrs; @@ -1535,7 +1560,7 @@ static int reg_set_new_power(struct iwl_priv *priv, power_info = ch_info->power_info; /* update OFDM Txpower settings */ - for (i = IWL_FIRST_OFDM_RATE; i <= IWL_LAST_OFDM_RATE; + for (i = IWL_RATE_6M_INDEX_TABLE; i <= IWL_RATE_54M_INDEX_TABLE; i++, ++power_info) { int delta_idx; @@ -1559,14 +1584,14 @@ static int reg_set_new_power(struct iwl_priv *priv, * ... all CCK power settings for a given channel are the *same*. */ if (power_changed) { power = - ch_info->power_info[IWL_RATE_12M_INDEX]. + ch_info->power_info[IWL_RATE_12M_INDEX_TABLE]. 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++) { + /* do all CCK rates' iwl3945_channel_power_info structures */ + for (i = IWL_RATE_1M_INDEX_TABLE; i <= IWL_RATE_11M_INDEX_TABLE; i++) { power_info->requested_power = power; power_info->base_power_index = - ch_info->power_info[IWL_RATE_12M_INDEX]. + ch_info->power_info[IWL_RATE_12M_INDEX_TABLE]. base_power_index + IWL_CCK_FROM_OFDM_INDEX_DIFF; ++power_info; } @@ -1576,12 +1601,13 @@ static int reg_set_new_power(struct iwl_priv *priv, } /** - * reg_get_channel_txpower_limit - returns new power limit for channel + * iwl3945_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 reg_get_channel_txpower_limit(struct iwl_channel_info *ch_info) + * 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 iwl3945_hw_reg_get_ch_txpower_limit(struct iwl3945_channel_info *ch_info) { s8 max_power; @@ -1595,11 +1621,12 @@ static int reg_get_channel_txpower_limit(struct iwl_channel_info *ch_info) else #endif max_power = ch_info->eeprom.max_power_avg; + return min(max_power, ch_info->max_power_avg); } /** - * reg_txpower_compensate_for_temperature_dif - Compensate for temperature + * iwl3945_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 @@ -1608,9 +1635,9 @@ static int reg_get_channel_txpower_limit(struct iwl_channel_info *ch_info) * * If RxOn is "associated", this sends the new Txpower to NIC! */ -static int reg_txpower_compensate_for_temperature_dif(struct iwl_priv *priv) +static int iwl3945_hw_reg_comp_txpower_temp(struct iwl3945_priv *priv) { - struct iwl_channel_info *ch_info = NULL; + struct iwl3945_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; @@ -1631,7 +1658,8 @@ static int reg_txpower_compensate_for_temperature_dif(struct iwl_priv *priv) /* get power index adjustment based on curr and factory * temps */ - delta_index = reg_adjust_power_by_temp(temperature, ref_temp); + delta_index = iwl3945_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; @@ -1643,7 +1671,7 @@ static int reg_txpower_compensate_for_temperature_dif(struct iwl_priv *priv) power_idx += delta_index; /* stay within table range */ - power_idx = reg_fix_power_index(power_idx); + power_idx = iwl3945_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 = @@ -1657,22 +1685,21 @@ static int reg_txpower_compensate_for_temperature_dif(struct iwl_priv *priv) 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; - reg_set_scan_power(priv, scan_tbl_index, + IWL_RATE_1M_INDEX_TABLE : IWL_RATE_6M_INDEX_TABLE; + iwl3945_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); + return iwl3945_hw_reg_send_txpower(priv); } -int iwl_hw_reg_set_txpower(struct iwl_priv *priv, s8 power) +int iwl3945_hw_reg_set_txpower(struct iwl3945_priv *priv, s8 power) { - struct iwl_channel_info *ch_info; + struct iwl3945_channel_info *ch_info; s8 max_power; - u8 channel; u8 a_band; u8 i; @@ -1690,28 +1717,33 @@ int iwl_hw_reg_set_txpower(struct iwl_priv *priv, s8 power) for (i = 0; i < priv->channel_count; i++) { ch_info = &priv->channel_info[i]; a_band = is_channel_a_band(ch_info); - channel = ch_info->channel; /* find minimum power of all user and regulatory constraints * (does not consider h/w clipping limitations) */ - max_power = reg_get_channel_txpower_limit(ch_info); + max_power = iwl3945_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 */ - reg_set_new_power(priv, ch_info); + iwl3945_hw_reg_set_new_power(priv, ch_info); } } /* update txpower settings for all channels, * send to NIC if associated. */ is_temp_calib_needed(priv); - reg_txpower_compensate_for_temperature_dif(priv); + iwl3945_hw_reg_comp_txpower_temp(priv); return 0; } +/* will add 3945 channel switch cmd handling later */ +int iwl3945_hw_channel_switch(struct iwl3945_priv *priv, u16 channel) +{ + return 0; +} + /** * iwl3945_reg_txpower_periodic - called when time to check our temperature. * @@ -1722,28 +1754,28 @@ int iwl_hw_reg_set_txpower(struct iwl_priv *priv, s8 power) * -- 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) +void iwl3945_reg_txpower_periodic(struct iwl3945_priv *priv) { /* This will kick in the "brute force" - * reg_txpower_compensate_for_temperature_dif() below */ + * iwl3945_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 */ - reg_txpower_compensate_for_temperature_dif(priv); + iwl3945_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(void *p) +static void iwl3945_bg_reg_txpower_periodic(void *p) { - struct iwl_priv *priv = p; + struct iwl3945_priv *priv = p; - if (priv->status & STATUS_EXIT_PENDING) + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; mutex_lock(&priv->mutex); @@ -1751,17 +1783,21 @@ void iwl3945_bg_reg_txpower_periodic(void *p) mutex_unlock(&priv->mutex); } -/* reg_get_chnl_grp_index - find the channel-group index (0-4) for the channel. +/** + * iwl3945_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. * - * ... 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 reg_get_chnl_grp_index(struct iwl_priv *priv, - const struct iwl_channel_info *ch_info) + * 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 iwl3945_hw_reg_get_ch_grp_index(struct iwl3945_priv *priv, + const struct iwl3945_channel_info *ch_info) { - struct iwl_eeprom_txpower_group *ch_grp = &priv->eeprom.groups[0]; + struct iwl3945_eeprom_txpower_group *ch_grp = &priv->eeprom.groups[0]; u8 group; u16 group_index = 0; /* based on factory calib frequencies */ u8 grp_channel; @@ -1787,20 +1823,20 @@ static u16 reg_get_chnl_grp_index(struct iwl_priv *priv, } /** - * reg_get_matched_power_index - Interpolate to get nominal index + * iwl3945_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 reg_get_matched_power_index(struct iwl_priv *priv, +static int iwl3945_hw_reg_get_matched_power_index(struct iwl3945_priv *priv, s8 requested_power, - s32 setting_index, s32 * new_index) + s32 setting_index, s32 *new_index) { - const struct iwl_eeprom_txpower_group *chnl_grp = NULL; + const struct iwl3945_eeprom_txpower_group *chnl_grp = NULL; s32 index0, index1; s32 power = 2 * requested_power; s32 i; - const struct iwl_eeprom_txpower_sample *samples; + const struct iwl3945_eeprom_txpower_sample *samples; s32 gains0, gains1; s32 res; s32 denominator; @@ -1840,11 +1876,11 @@ static int reg_get_matched_power_index(struct iwl_priv *priv, return 0; } -static void reg_init_channel_groups(struct iwl_priv *priv) +static void iwl3945_hw_reg_init_channel_groups(struct iwl3945_priv *priv) { u32 i; s32 rate_index; - const struct iwl_eeprom_txpower_group *group; + const struct iwl3945_eeprom_txpower_group *group; IWL_DEBUG_POWER("Initializing factory calib info from EEPROM\n"); @@ -1879,19 +1915,19 @@ static void reg_init_channel_groups(struct iwl_priv *priv) for (rate_index = 0; rate_index < IWL_RATE_COUNT; rate_index++, clip_pwrs++) { switch (rate_index) { - case IWL_RATE_36M_INDEX: + case IWL_RATE_36M_INDEX_TABLE: if (i == 0) /* B/G */ *clip_pwrs = satur_pwr; else /* A */ *clip_pwrs = satur_pwr - 5; break; - case IWL_RATE_48M_INDEX: + case IWL_RATE_48M_INDEX_TABLE: if (i == 0) *clip_pwrs = satur_pwr - 7; else *clip_pwrs = satur_pwr - 10; break; - case IWL_RATE_54M_INDEX: + case IWL_RATE_54M_INDEX_TABLE: if (i == 0) *clip_pwrs = satur_pwr - 9; else @@ -1920,10 +1956,10 @@ static void reg_init_channel_groups(struct iwl_priv *priv) * * This does *not* write values to NIC, just sets up our internal table. */ -int iwl3945_txpower_set_from_eeprom(struct iwl_priv *priv) +int iwl3945_txpower_set_from_eeprom(struct iwl3945_priv *priv) { - struct iwl_channel_info *ch_info = NULL; - struct iwl_channel_power_info *pwr_info; + struct iwl3945_channel_info *ch_info = NULL; + struct iwl3945_channel_power_info *pwr_info; int delta_index; u8 rate_index; u8 scan_tbl_index; @@ -1936,10 +1972,10 @@ int iwl3945_txpower_set_from_eeprom(struct iwl_priv *priv) /* save temperature reference, * so we can determine next time to calibrate */ - temperature = reg_txpower_get_temperature(priv); + temperature = iwl3945_hw_reg_txpower_get_temperature(priv); priv->last_temperature = temperature; - reg_init_channel_groups(priv); + iwl3945_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; @@ -1949,17 +1985,17 @@ int iwl3945_txpower_set_from_eeprom(struct iwl_priv *priv) continue; /* find this channel's channel group (*not* "band") index */ - ch_info->group_index = reg_get_chnl_grp_index(priv, ch_info); + ch_info->group_index = + iwl3945_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 = reg_adjust_power_by_temp( - temperature, - (s16)le16_to_cpu( - priv->eeprom.groups[ch_info->group_index].temperature)); + delta_index = iwl3945_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 + @@ -1980,7 +2016,7 @@ int iwl3945_txpower_set_from_eeprom(struct iwl_priv *priv) /* get base (i.e. at factory-measured temperature) * power table index for this rate's power */ - rc = reg_get_matched_power_index(priv, pwr, + rc = iwl3945_hw_reg_get_matched_power_index(priv, pwr, ch_info->group_index, &power_idx); if (rc) { @@ -1993,9 +2029,9 @@ int iwl3945_txpower_set_from_eeprom(struct iwl_priv *priv) power_idx += delta_index; /* stay within range of gain table */ - power_idx = reg_fix_power_index(power_idx); + power_idx = iwl3945_hw_reg_fix_power_index(power_idx); - /* fill 1 OFDM rate's iwl_channel_power_info struct */ + /* fill 1 OFDM rate's iwl3945_channel_power_info struct */ pwr_info->requested_power = pwr; pwr_info->power_table_index = (u8) power_idx; pwr_info->tpc.tx_gain = @@ -2005,25 +2041,25 @@ int iwl3945_txpower_set_from_eeprom(struct iwl_priv *priv) } /* 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; + pwr_info = &ch_info->power_info[IWL_RATE_12M_INDEX_TABLE]; + 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 = reg_fix_power_index(pwr_index); + pwr_index = iwl3945_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 + /* fill each CCK rate's iwl3945_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]; + for (rate_index = 0; + rate_index < IWL_CCK_RATES; rate_index++) { + pwr_info = &ch_info->power_info[rate_index+IWL_OFDM_RATES]; pwr_info->requested_power = power; pwr_info->power_table_index = pwr_index; pwr_info->base_power_index = base_pwr_index; @@ -2035,99 +2071,98 @@ int iwl3945_txpower_set_from_eeprom(struct iwl_priv *priv) 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; - reg_set_scan_power(priv, scan_tbl_index, - actual_index, clip_pwrs, - ch_info, a_band); + IWL_RATE_1M_INDEX_TABLE : IWL_RATE_6M_INDEX_TABLE; + iwl3945_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 iwl3945_hw_rxq_stop(struct iwl3945_priv *priv) { int rc; unsigned long flags; spin_lock_irqsave(&priv->lock, flags); - rc = iwl_grab_restricted_access(priv); + rc = iwl3945_grab_nic_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); + iwl3945_write_direct32(priv, FH_RCSR_CONFIG(0), 0); + rc = iwl3945_poll_direct_bit(priv, FH_RSSR_STATUS, (1 << 24), 1000); if (rc < 0) IWL_ERROR("Can't stop Rx DMA.\n"); - iwl_release_restricted_access(priv); + iwl3945_release_nic_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 iwl3945_hw_tx_queue_init(struct iwl3945_priv *priv, struct iwl3945_tx_queue *txq) { int rc; unsigned long flags; int txq_id = txq->q.id; - struct iwl_shared *shared_data = priv->hw_setting.shared_virt; + struct iwl3945_shared *shared_data = priv->hw_setting.shared_virt; - shared_data->tx_base_ptr[txq_id] = (u32) txq->q.dma_addr; - - txq->q.element_size = sizeof(struct iwl_tfd_frame); + 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); + rc = iwl3945_grab_nic_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); + iwl3945_write_direct32(priv, FH_CBCC_CTRL(txq_id), 0); + iwl3945_write_direct32(priv, FH_CBCC_BASE(txq_id), 0); - iwl_write_restricted( - priv, FH_TCSR_CONFIG(txq_id), + iwl3945_write_direct32(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); + iwl3945_release_nic_access(priv); /* fake read to flush all prev. writes */ - iwl_read32(priv, FH_TSSR_CBB_BASE); - + iwl3945_read32(priv, FH_TSSR_CBB_BASE); spin_unlock_irqrestore(&priv->lock, flags); + return 0; } -int iwl_hw_get_rx_read(struct iwl_priv *priv) +int iwl3945_hw_get_rx_read(struct iwl3945_priv *priv) { - struct iwl_shared *shared_data = - (struct iwl_shared *)priv->hw_setting.shared_virt; + struct iwl3945_shared *shared_data = priv->hw_setting.shared_virt; - return shared_data->rx_read_ptr[0]; + 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 iwl3945_init_hw_rate_table(struct iwl3945_priv *priv) { - int rc, i; - struct iwl_rate_scaling_cmd rate_cmd = { + int rc, i, index, prev_index; + struct iwl3945_rate_scaling_cmd rate_cmd = { .reserved = {0, 0, 0}, }; - struct iwl_rate_scaling_info *table = rate_cmd.table; + struct iwl3945_rate_scaling_info *table = rate_cmd.table; + + for (i = 0; i < ARRAY_SIZE(iwl3945_rates); i++) { + index = iwl3945_rates[i].table_rs_index; - for (i = 0; i < GLOBAL_ARRAY_SIZE(iwl_rates); i++) { - table[i].rate_n_flags = iwl_rates[i].plcp; - table[i].try_cnt = priv->retry_rate; - table[i].next_rate_index = iwl_get_prev_ieee_rate(i); + table[index].rate_n_flags = + iwl3945_hw_set_rate_n_flags(iwl3945_rates[i].plcp, 0); + table[index].try_cnt = priv->retry_rate; + prev_index = iwl3945_get_prev_ieee_rate(i); + table[index].next_rate_index = iwl3945_rates[prev_index].table_rs_index; } switch (priv->phymode) { @@ -2135,26 +2170,26 @@ int iwl3945_init_hw_rate_table(struct iwl_priv *priv) 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; + for (i = IWL_RATE_1M_INDEX_TABLE; i <= IWL_RATE_11M_INDEX_TABLE; i++) + table[i].next_rate_index = iwl3945_rates[IWL_FIRST_OFDM_RATE].table_rs_index; /* Don't fall back to CCK rates */ - table[IWL_RATE_12M_INDEX].next_rate_index = IWL_RATE_9M_INDEX; + table[IWL_RATE_12M_INDEX_TABLE].next_rate_index = IWL_RATE_9M_INDEX_TABLE; /* Don't drop out of OFDM rates */ - table[IWL_FIRST_OFDM_RATE].next_rate_index = - IWL_FIRST_OFDM_RATE; + table[IWL_RATE_6M_INDEX_TABLE].next_rate_index = + iwl3945_rates[IWL_FIRST_OFDM_RATE].table_rs_index; 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; + for (i = IWL_RATE_6M_INDEX_TABLE; i <= IWL_RATE_54M_INDEX_TABLE; i++) + table[i].next_rate_index = iwl3945_rates[IWL_FIRST_CCK_RATE].table_rs_index; /* CCK shouldn't fall back to OFDM... */ - table[IWL_RATE_11M_INDEX].next_rate_index = IWL_RATE_5M_INDEX; + table[IWL_RATE_11M_INDEX_TABLE].next_rate_index = IWL_RATE_5M_INDEX_TABLE; break; default: @@ -2164,25 +2199,26 @@ int iwl3945_init_hw_rate_table(struct iwl_priv *priv) /* 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), + rc = iwl3945_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), + return iwl3945_send_cmd_pdu(priv, REPLY_RATE_SCALE, sizeof(rate_cmd), &rate_cmd); } -int iwl_hw_set_hw_setting(struct iwl_priv *priv) +/* Called when initializing driver */ +int iwl3945_hw_set_hw_setting(struct iwl3945_priv *priv) { memset((void *)&priv->hw_setting, 0, - sizeof(struct iwl_driver_hw_info)); + sizeof(struct iwl3945_driver_hw_info)); priv->hw_setting.shared_virt = pci_alloc_consistent(priv->pci_dev, - sizeof(struct iwl_shared), + sizeof(struct iwl3945_shared), &priv->hw_setting.shared_phys); if (!priv->hw_setting.shared_virt) { @@ -2192,77 +2228,84 @@ int iwl_hw_set_hw_setting(struct iwl_priv *priv) } 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.rx_buf_size = IWL_RX_BUF_SIZE; + priv->hw_setting.max_pkt_size = 2342; + priv->hw_setting.tx_cmd_len = sizeof(struct iwl3945_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; + priv->hw_setting.max_stations = IWL3945_STATION_COUNT; + priv->hw_setting.bcast_sta_id = IWL3945_BROADCAST_ID; return 0; } -int iwl_hw_get_beacon_cmd(struct iwl_priv *priv, - struct iwl_frame *frame, u16 rate) +unsigned int iwl3945_hw_get_beacon_cmd(struct iwl3945_priv *priv, + struct iwl3945_frame *frame, u8 rate) { - struct iwl_tx_beacon_cmd *tx_beacon_cmd; - int frame_size; + struct iwl3945_tx_beacon_cmd *tx_beacon_cmd; + unsigned int frame_size; - tx_beacon_cmd = (struct iwl_tx_beacon_cmd *)&frame->u; + tx_beacon_cmd = (struct iwl3945_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 = 0xFFFFFFFF; + tx_beacon_cmd->tx.sta_id = IWL3945_BROADCAST_ID; + tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; - frame_size = iwl_fill_beacon_frame(priv, + frame_size = iwl3945_fill_beacon_frame(priv, tx_beacon_cmd->frame, - BROADCAST_ADDR, + iwl3945_broadcast_addr, sizeof(frame->u) - sizeof(*tx_beacon_cmd)); - tx_beacon_cmd->tx.len = frame_size; + 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 - */ + /* supp_rates[0] == OFDM start at IWL_FIRST_OFDM_RATE*/ + tx_beacon_cmd->tx.supp_rates[0] = + (IWL_OFDM_BASIC_RATES_MASK >> IWL_FIRST_OFDM_RATE) & 0xFF; + tx_beacon_cmd->tx.supp_rates[1] = - (IWL_CCK_BASIC_RATES_MASK >> 8) & 0xF; + (IWL_CCK_BASIC_RATES_MASK & 0xF); - return (sizeof(struct iwl_tx_beacon_cmd) + frame_size); + return (sizeof(struct iwl3945_tx_beacon_cmd) + frame_size); } -void iwl_hw_rx_handler_setup(struct iwl_priv *priv) +void iwl3945_hw_rx_handler_setup(struct iwl3945_priv *priv) { priv->rx_handlers[REPLY_3945_RX] = iwl3945_rx_reply_rx; } -void iwl_hw_setup_deferred_work(struct iwl_priv *priv) +void iwl3945_hw_setup_deferred_work(struct iwl3945_priv *priv) { INIT_WORK(&priv->thermal_periodic, - iwl3945_bg_reg_txpower_periodic, priv); + iwl3945_bg_reg_txpower_periodic, priv); } -void iwl_hw_cancel_deferred_work(struct iwl_priv *priv) +void iwl3945_hw_cancel_deferred_work(struct iwl3945_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}, +struct pci_device_id iwl3945_hw_card_ids[] = { + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4222)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4227)}, {0} }; -MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids); +/* + * Clear the OWNER_MSK, to establish driver (instead of uCode running on + * embedded controller) as EEPROM reader; each read is a series of pulses + * to/from the EEPROM chip, not a single event, so even reads could conflict + * if they weren't arbitrated by some ownership mechanism. Here, the driver + * simply claims ownership, which should be safe when this function is called + * (i.e. before loading uCode!). + */ +inline int iwl3945_eeprom_acquire_semaphore(struct iwl3945_priv *priv) +{ + _iwl3945_clear_bit(priv, CSR_EEPROM_GP, CSR_EEPROM_GP_IF_OWNER_MSK); + return 0; +} + +MODULE_DEVICE_TABLE(pci, iwl3945_hw_card_ids); diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.h b/drivers/net/wireless/iwlwifi/iwl-3945.h index f99a49b..5ac46c9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.h +++ b/drivers/net/wireless/iwlwifi/iwl-3945.h @@ -23,33 +23,967 @@ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ +/* + * Please use this file (iwl-3945.h) for driver implementation definitions. + * Please use iwl-3945-commands.h for uCode API definitions. + * Please use iwl-3945-hw.h for hardware-related definitions. + */ #ifndef __iwl_3945_h__ #define __iwl_3945_h__ -#if IWL != 3945 +#include <linux/pci.h> /* for struct pci_device_id */ +#include <linux/kernel.h> +#include <net/ieee80211_radiotap.h> + +/* Hardware specific file defines the PCI IDs table for that hardware module */ +extern struct pci_device_id iwl3945_hw_card_ids[]; + +#define DRV_NAME "iwl3945" +#include "iwl-3945-hw.h" +#include "iwl-prph.h" +#include "iwl-3945-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 iwl3945_param_hwcrypto; +extern int iwl3945_param_queues_num; + +enum iwl3945_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 IWL_RX_BUF_SIZE 3000U +#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 iwl3945_rx_mem_buffer { + dma_addr_t dma_addr; + struct sk_buff *skb; + struct list_head list; +}; + +struct iwl3945_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 iwl3945_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 iwl3945_queue { + int n_bd; /* number of BDs in this queue */ + int write_ptr; /* 1-st empty entry (index) host_w*/ + int read_ptr; /* 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) + +/* One for each TFD */ +struct iwl3945_tx_info { + struct ieee80211_tx_status status; + struct sk_buff *skb[MAX_NUM_OF_TBS]; +}; + +/** + * struct iwl3945_tx_queue - Tx Queue for DMA + * @q: generic Rx/Tx queue descriptor + * @bd: base of circular buffer of TFDs + * @cmd: array of command/Tx buffers + * @dma_addr_cmd: physical address of cmd/tx buffer array + * @txb: array of per-TFD driver data + * @need_update: indicates need to update read/write index + * + * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame + * descriptors) and required locking structures. + */ +struct iwl3945_tx_queue { + struct iwl3945_queue q; + struct iwl3945_tfd_frame *bd; + struct iwl3945_cmd *cmd; + dma_addr_t dma_addr_cmd; + struct iwl3945_tx_info *txb; + int need_update; + int active; +}; + +#define IWL_NUM_SCAN_RATES (2) + +struct iwl3945_channel_tgd_info { + u8 type; + s8 max_power; +}; + +struct iwl3945_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 iwl3945_channel_power_info { + struct iwl3945_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 iwl3945_scan_power_info { + struct iwl3945_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 */ +}; + +/* + * 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 iwl3945_channel_info { + struct iwl3945_channel_tgd_info tgd; + struct iwl3945_channel_tgh_info tgh; + struct iwl3945_eeprom_channel eeprom; /* EEPROM regulatory limit */ + struct iwl3945_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 iwl3945_channel_power_info power_info[IWL4965_MAX_RATE]; + + /* Radio/DSP gain settings for each scan rate, for directed scans. */ + struct iwl3945_scan_power_info scan_pwr_info[IWL_NUM_SCAN_RATES]; +}; + +struct iwl3945_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]; +}; + +#include "iwl-3945-rs.h" + +#define IWL_TX_FIFO_AC0 0 +#define IWL_TX_FIFO_AC1 1 +#define IWL_TX_FIFO_AC2 2 +#define IWL_TX_FIFO_AC3 3 +#define IWL_TX_FIFO_HCCA_1 5 +#define IWL_TX_FIFO_HCCA_2 6 +#define IWL_TX_FIFO_NONE 7 + +/* Minimum number of queues. MAX_NUM is defined in hw specific files */ +#define IWL_MIN_NUM_QUEUES 4 + +/* Power management (not Tx power) structures */ + +struct iwl3945_power_vec_entry { + struct iwl3945_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 iwl3945_power_mgr { + spinlock_t lock; + struct iwl3945_power_vec_entry pwr_range_0[IWL_POWER_AC]; + struct iwl3945_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 iwl3945_frame { + union { + struct ieee80211_hdr frame; + struct iwl3945_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 iwl3945_cmd; +struct iwl3945_priv; + +struct iwl3945_cmd_meta { + struct iwl3945_cmd_meta *source; + union { + struct sk_buff *skb; + int (*callback)(struct iwl3945_priv *priv, + struct iwl3945_cmd *cmd, struct sk_buff *skb); + } __attribute__ ((packed)) u; + + /* The CMD_SIZE_HUGE flag bit indicates that the command + * structure is stored at the end of the shared queue memory. */ + u32 flags; + +} __attribute__ ((packed)); + +/** + * struct iwl3945_cmd + * + * For allocation of the command and tx queues, this establishes the overall + * size of the largest command we send to uCode, except for a scan command + * (which is relatively huge; space is allocated separately). + */ +struct iwl3945_cmd { + struct iwl3945_cmd_meta meta; + struct iwl3945_cmd_header hdr; + union { + struct iwl3945_addsta_cmd addsta; + struct iwl3945_led_cmd led; + u32 flags; + u8 val8; + u16 val16; + u32 val32; + struct iwl3945_bt_cmd bt; + struct iwl3945_rxon_time_cmd rxon_time; + struct iwl3945_powertable_cmd powertable; + struct iwl3945_qosparam_cmd qosparam; + struct iwl3945_tx_cmd tx; + struct iwl3945_tx_beacon_cmd tx_beacon; + struct iwl3945_rxon_assoc_cmd rxon_assoc; + u8 *indirect; + u8 payload[360]; + } __attribute__ ((packed)) cmd; +} __attribute__ ((packed)); + +struct iwl3945_host_cmd { + u8 id; + u16 len; + struct iwl3945_cmd_meta meta; + const void *data; +}; + +#define TFD_MAX_PAYLOAD_SIZE (sizeof(struct iwl3945_cmd) - \ + sizeof(struct iwl3945_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 iwl3945_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 iwl3945_rx_mem_buffers + */ +struct iwl3945_rx_queue { + __le32 *bd; + dma_addr_t dma_addr; + struct iwl3945_rx_mem_buffer pool[RX_QUEUE_SIZE + RX_FREE_BUFFERS]; + struct iwl3945_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 + +#define MAX_TID_COUNT 9 + +#define IWL_INVALID_RATE 0xFF +#define IWL_INVALID_VALUE -1 + +struct iwl3945_tid_data { + u16 seq_number; +}; + +struct iwl3945_hw_key { + enum ieee80211_key_alg alg; + int keylen; + u8 key[32]; +}; + +union iwl3945_ht_rate_supp { + u16 rates; + struct { + u8 siso_rate; + u8 mimo_rate; + }; +}; + +#ifdef CONFIG_IWL3945_QOS + +union iwl3945_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 structures */ +struct iwl3945_qos_info { + int qos_enable; + int qos_active; + union iwl3945_qos_capabity qos_cap; + struct iwl3945_qosparam_cmd def_qos_parm; +}; +#endif /*CONFIG_IWL3945_QOS */ + +#define STA_PS_STATUS_WAKE 0 +#define STA_PS_STATUS_SLEEP 1 + +struct iwl3945_station_entry { + struct iwl3945_addsta_cmd sta; + struct iwl3945_tid_data tid[MAX_TID_COUNT]; + union { + struct { + u8 rate; + u8 flags; + } s; + u16 rate_n_flags; + } current_rate; + u8 used; + u8 ps_status; + struct iwl3945_hw_key keyinfo; +}; + +/* one for each uCode image (inst/data, boot/init/runtime) */ +struct fw_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 iwl3945_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 iwl3945_ibss_seq { + u8 mac[ETH_ALEN]; + u16 seq_num; + u16 frag_num; + unsigned long packet_time; + struct list_head list; +}; + +/** + * struct iwl4965_driver_hw_info + * @max_txq_num: Max # Tx queues supported + * @ac_queue_count: # Tx queues for EDCA Access Categories (AC) + * @tx_cmd_len: Size of Tx command (but not including frame itself) + * @max_rxq_size: Max # Rx frames in Rx queue (must be power-of-2) + * @rx_buf_size: + * @max_pkt_size: + * @max_rxq_log: Log-base-2 of max_rxq_size + * @max_stations: + * @bcast_sta_id: + * @shared_virt: Pointer to driver/uCode shared Tx Byte Counts and Rx status + * @shared_phys: Physical Pointer to Tx Byte Counts and Rx status + */ +struct iwl3945_driver_hw_info { + u16 max_txq_num; + u16 ac_queue_count; + u16 tx_cmd_len; + u16 max_rxq_size; + u32 rx_buf_size; + u32 max_pkt_size; + u16 max_rxq_log; + u8 max_stations; + u8 bcast_sta_id; + void *shared_virt; + dma_addr_t shared_phys; +}; + +#define IWL_RX_HDR(x) ((struct iwl3945_rx_frame_hdr *)(\ + x->u.rx_frame.stats.payload + \ + x->u.rx_frame.stats.phy_count)) +#define IWL_RX_END(x) ((struct iwl3945_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 iwl3945_addsta_cmd; +extern int iwl3945_send_add_station(struct iwl3945_priv *priv, + struct iwl3945_addsta_cmd *sta, u8 flags); +extern u8 iwl3945_add_station(struct iwl3945_priv *priv, const u8 *bssid, + int is_ap, u8 flags); +extern int iwl3945_is_network_packet(struct iwl3945_priv *priv, + struct ieee80211_hdr *header); +extern int iwl3945_power_init_handle(struct iwl3945_priv *priv); +extern int iwl3945_eeprom_init(struct iwl3945_priv *priv); +#ifdef CONFIG_IWL3945_DEBUG +extern void iwl3945_report_frame(struct iwl3945_priv *priv, + struct iwl3945_rx_packet *pkt, + struct ieee80211_hdr *header, int group100); +#else +static inline void iwl3945_report_frame(struct iwl3945_priv *priv, + struct iwl3945_rx_packet *pkt, + struct ieee80211_hdr *header, + int group100) {} +#endif +extern void iwl3945_handle_data_packet_monitor(struct iwl3945_priv *priv, + struct iwl3945_rx_mem_buffer *rxb, + void *data, short len, + struct ieee80211_rx_status *stats, + u16 phy_flags); +extern int iwl3945_is_duplicate_packet(struct iwl3945_priv *priv, + struct ieee80211_hdr *header); +extern int iwl3945_rx_queue_alloc(struct iwl3945_priv *priv); +extern void iwl3945_rx_queue_reset(struct iwl3945_priv *priv, + struct iwl3945_rx_queue *rxq); +extern int iwl3945_calc_db_from_ratio(int sig_ratio); +extern int iwl3945_calc_sig_qual(int rssi_dbm, int noise_dbm); +extern int iwl3945_tx_queue_init(struct iwl3945_priv *priv, + struct iwl3945_tx_queue *txq, int count, u32 id); +extern void iwl3945_rx_replenish(void *data); +extern void iwl3945_tx_queue_free(struct iwl3945_priv *priv, struct iwl3945_tx_queue *txq); +extern int iwl3945_send_cmd_pdu(struct iwl3945_priv *priv, u8 id, u16 len, + const void *data); +extern int __must_check iwl3945_send_cmd(struct iwl3945_priv *priv, + struct iwl3945_host_cmd *cmd); +extern unsigned int iwl3945_fill_beacon_frame(struct iwl3945_priv *priv, + struct ieee80211_hdr *hdr, + const u8 *dest, int left); +extern int iwl3945_rx_queue_update_write_ptr(struct iwl3945_priv *priv, + struct iwl3945_rx_queue *q); +extern int iwl3945_send_statistics_request(struct iwl3945_priv *priv); +extern void iwl3945_set_decrypted_flag(struct iwl3945_priv *priv, struct sk_buff *skb, + u32 decrypt_res, + struct ieee80211_rx_status *stats); +extern const u8 iwl3945_broadcast_addr[ETH_ALEN]; + /* - * 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 int 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(void *p) -{} -static inline int iwl3945_txpower_set_from_eeprom(struct iwl_priv *priv) -{ return 0; } -#else /* IWL == 3945 */ + * Currently used by iwl-3945-rs... look at restructuring so that it doesn't + * call this... todo... fix that. +*/ +extern u8 iwl3945_sync_station(struct iwl3945_priv *priv, int sta_id, + u16 tx_rate, u8 flags); + +/****************************************************************************** + * + * 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 -- + * iwl3945_ <-- Its part of iwlwifi (should be changed to iwl3945_) + * iwl3945_hw_ <-- Hardware specific (implemented in iwl-XXXX.c by all HW) + * iwlXXXX_ <-- Hardware specific (implemented in iwl-XXXX.c for XXXX) + * iwl3945_bg_ <-- Called from work queue context + * iwl3945_mac_ <-- mac80211 callback + * + ****************************************************************************/ +extern void iwl3945_hw_rx_handler_setup(struct iwl3945_priv *priv); +extern void iwl3945_hw_setup_deferred_work(struct iwl3945_priv *priv); +extern void iwl3945_hw_cancel_deferred_work(struct iwl3945_priv *priv); +extern int iwl3945_hw_rxq_stop(struct iwl3945_priv *priv); +extern int iwl3945_hw_set_hw_setting(struct iwl3945_priv *priv); +extern int iwl3945_hw_nic_init(struct iwl3945_priv *priv); +extern int iwl3945_hw_nic_stop_master(struct iwl3945_priv *priv); +extern void iwl3945_hw_txq_ctx_free(struct iwl3945_priv *priv); +extern void iwl3945_hw_txq_ctx_stop(struct iwl3945_priv *priv); +extern int iwl3945_hw_nic_reset(struct iwl3945_priv *priv); +extern int iwl3945_hw_txq_attach_buf_to_tfd(struct iwl3945_priv *priv, void *tfd, + dma_addr_t addr, u16 len); +extern int iwl3945_hw_txq_free_tfd(struct iwl3945_priv *priv, struct iwl3945_tx_queue *txq); +extern int iwl3945_hw_get_temperature(struct iwl3945_priv *priv); +extern int iwl3945_hw_tx_queue_init(struct iwl3945_priv *priv, + struct iwl3945_tx_queue *txq); +extern unsigned int iwl3945_hw_get_beacon_cmd(struct iwl3945_priv *priv, + struct iwl3945_frame *frame, u8 rate); +extern int iwl3945_hw_get_rx_read(struct iwl3945_priv *priv); +extern void iwl3945_hw_build_tx_cmd_rate(struct iwl3945_priv *priv, + struct iwl3945_cmd *cmd, + struct ieee80211_tx_control *ctrl, + struct ieee80211_hdr *hdr, + int sta_id, int tx_id); +extern int iwl3945_hw_reg_send_txpower(struct iwl3945_priv *priv); +extern int iwl3945_hw_reg_set_txpower(struct iwl3945_priv *priv, s8 power); +extern void iwl3945_hw_rx_statistics(struct iwl3945_priv *priv, + struct iwl3945_rx_mem_buffer *rxb); +extern void iwl3945_disable_events(struct iwl3945_priv *priv); +extern int iwl4965_get_temperature(const struct iwl3945_priv *priv); + +/** + * iwl3945_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 iwl3945_hw_find_station(struct iwl3945_priv *priv, const u8 *bssid); + +extern int iwl3945_hw_channel_switch(struct iwl3945_priv *priv, u16 channel); + /* - * Forward declare iwl-3945.c functions for base.c - */ -extern int 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(void *p); -extern int iwl3945_txpower_set_from_eeprom(struct iwl_priv *priv); -#endif /* IWL == 3945 */ + * Forward declare iwl-3945.c functions for iwl-base.c + */ +extern int iwl3945_eeprom_acquire_semaphore(struct iwl3945_priv *priv); +extern __le32 iwl3945_get_antenna_flags(const struct iwl3945_priv *priv); +extern int iwl3945_init_hw_rate_table(struct iwl3945_priv *priv); +extern void iwl3945_reg_txpower_periodic(struct iwl3945_priv *priv); +extern int iwl3945_txpower_set_from_eeprom(struct iwl3945_priv *priv); +extern u8 iwl3945_sync_sta(struct iwl3945_priv *priv, int sta_id, + u16 tx_rate, u8 flags); + + +#ifdef CONFIG_IWL3945_SPECTRUM_MEASUREMENT + +enum { + MEASUREMENT_READY = (1 << 0), + MEASUREMENT_ACTIVE = (1 << 1), +}; + +#endif + +struct iwl3945_priv { + + /* ieee device used by generic ieee processing code */ + struct ieee80211_hw *hw; + struct ieee80211_channel *ieee_channels; + struct ieee80211_rate *ieee_rates; + struct ieee80211_conf *cache_conf; + + /* temporary frame storage list */ + struct list_head free_frames; + int frames_count; + + u8 phymode; + int alloc_rxb_skb; + + void (*rx_handlers[REPLY_MAX])(struct iwl3945_priv *priv, + struct iwl3945_rx_mem_buffer *rxb); + + const struct ieee80211_hw_mode *modes; + +#ifdef CONFIG_IWL3945_SPECTRUM_MEASUREMENT + /* spectrum measurement report caching */ + struct iwl3945_spectrum_notification measure_report; + u8 measurement_status; +#endif + /* ucode beacon time */ + u32 ucode_beacon_time; + + /* we allocate array of iwl3945_channel_info for NIC's valid channels. + * Access via channel # using indirect index array */ + struct iwl3945_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 iwl3945_clip_group clip_groups[5]; + + /* thermal calibration */ + s32 temperature; /* degrees Kelvin */ + s32 last_temperature; + + /* Scan related variables */ + unsigned long last_scan_jiffies; + unsigned long next_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 iwl3945_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_desc ucode_code; /* runtime inst */ + struct fw_desc ucode_data; /* runtime data original */ + struct fw_desc ucode_data_backup; /* runtime data save/restore */ + struct fw_desc ucode_init; /* initialization inst */ + struct fw_desc ucode_init_data; /* initialization data */ + struct fw_desc ucode_boot; /* bootstrap inst */ + + + struct iwl3945_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 iwl3945_rxon_cmd active_rxon; + struct iwl3945_rxon_cmd staging_rxon; + + int error_recovering; + struct iwl3945_rxon_cmd recovery_rxon; + + /* 1st responses from initialize and runtime uCode images. + * 4965's initialize alive response contains some calibration data. */ + struct iwl3945_init_alive_resp card_alive_init; + struct iwl3945_alive_resp card_alive; + +#ifdef LED + /* LED related variables */ + struct iwl3945_activity_blink activity; + unsigned long led_packets; + int led_state; +#endif + + u16 active_rate; + u16 active_rate_basic; + + u8 call_post_assoc_from_beacon; + u8 assoc_station_added; + /* 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 iwl3945_rx_queue rxq; + struct iwl3945_tx_queue txq[IWL_MAX_NUM_QUEUES]; + + unsigned long status; + u32 config; + + int last_rx_rssi; /* From Rx packet statisitics */ + int last_rx_noise; /* From beacon statistics */ + + struct iwl3945_power_mgr power_data; + + struct iwl3945_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; + int num_stations; + struct iwl3945_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; + + /* Hash table for finding stations in IBSS network */ + struct list_head ibss_mac_hash[IWL_IBSS_MAC_HASH_SIZE]; + + /* eeprom */ + struct iwl3945_eeprom eeprom; + + int iw_mode; + + struct sk_buff *ibss_beacon; + + /* Last Rx'd beacon timestamp */ + u32 timestamp0; + u32 timestamp1; + u16 beacon_int; + struct iwl3945_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_IWL3945_QOS + struct iwl3945_qos_info qos_data; +#endif /*CONFIG_IWL3945_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 work_struct init_alive_start; + struct work_struct alive_start; + struct work_struct activity_timer; + struct work_struct thermal_periodic; + struct work_struct gather_stats; + struct work_struct scan_check; + struct work_struct post_associate; + +#define IWL_DEFAULT_TX_POWER 0x0F + s8 user_txpower_limit; + s8 max_channel_txpower_limit; + +#ifdef CONFIG_PM + u32 pm_state[16]; +#endif + +#ifdef CONFIG_IWL3945_DEBUG + /* debugging info */ + u32 framecnt_to_us; + atomic_t restrict_refcnt; +#endif +}; /*iwl3945_priv */ + +static inline int iwl3945_is_associated(struct iwl3945_priv *priv) +{ + return (priv->active_rxon.filter_flags & RXON_FILTER_ASSOC_MSK) ? 1 : 0; +} + +static inline int is_channel_valid(const struct iwl3945_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 iwl3945_channel_info *ch_info) +{ + return (ch_info->flags & EEPROM_CHANNEL_NARROW) ? 1 : 0; +} + +static inline int is_channel_radar(const struct iwl3945_channel_info *ch_info) +{ + return (ch_info->flags & EEPROM_CHANNEL_RADAR) ? 1 : 0; +} + +static inline u8 is_channel_a_band(const struct iwl3945_channel_info *ch_info) +{ + return ch_info->phymode == MODE_IEEE80211A; +} + +static inline u8 is_channel_bg_band(const struct iwl3945_channel_info *ch_info) +{ + return ((ch_info->phymode == MODE_IEEE80211B) || + (ch_info->phymode == MODE_IEEE80211G)); +} + +static inline int is_channel_passive(const struct iwl3945_channel_info *ch) +{ + return (!(ch->flags & EEPROM_CHANNEL_ACTIVE)) ? 1 : 0; +} + +static inline int is_channel_ibss(const struct iwl3945_channel_info *ch) +{ + return ((ch->flags & EEPROM_CHANNEL_IBSS)) ? 1 : 0; +} + +extern const struct iwl3945_channel_info *iwl3945_get_channel_info( + const struct iwl3945_priv *priv, int phymode, u16 channel); + +/* Requires full declaration of iwl3945_priv before including */ +#include "iwl-3945-io.h" #endif diff --git a/drivers/net/wireless/iwlwifi/iwl-4965-commands.h b/drivers/net/wireless/iwlwifi/iwl-4965-commands.h new file mode 100644 index 0000000..7988c75 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-4965-commands.h @@ -0,0 +1,2562 @@ +/****************************************************************************** + * + * 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 General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * James P. Ketrenos <ipw2100-admin@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2007 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +/* + * Please use this file (iwl-4965-commands.h) only for uCode API definitions. + * Please use iwl-4965-hw.h for hardware-related definitions. + * Please use iwl-4965.h for driver implementation definitions. + */ + +#ifndef __iwl4965_commands_h__ +#define __iwl4965_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 */ + 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 */ + + /* Bluetooth device coexistance config command */ + REPLY_BT_CONFIG = 0x9b, + + /* 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, + + 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, + REPLY_MAX = 0xff +}; + +/****************************************************************************** + * (0) + * Commonly used structures and definitions: + * Command header, rate_n_flags, txpower + * + *****************************************************************************/ + +/* iwl4965_cmd_header flags value */ +#define IWL_CMD_FAILED_MSK 0x40 + +/** + * struct iwl4965_cmd_header + * + * This header format appears in the beginning of each command sent from the + * driver, and each response/notification received from uCode. + */ +struct iwl4965_cmd_header { + u8 cmd; /* Command ID: REPLY_RXON, etc. */ + u8 flags; /* IWL_CMD_* */ + /* + * The driver sets up the sequence number to values of its chosing. + * uCode does not use this value, but passes it back to the driver + * when sending the response to each driver-originated command, so + * the driver can match the response to the command. Since the values + * don't get used by uCode, the driver may set up an arbitrary format. + * + * There is one exception: uCode sets bit 15 when it originates + * the response/notification, i.e. when the response/notification + * is not a direct response to a command sent by the driver. For + * example, uCode issues REPLY_3945_RX when it sends a received frame + * to the driver; it is not a direct response to any driver command. + * + * The Linux driver uses the following format: + * + * 0:7 index/position within Tx queue + * 8:13 Tx queue selection + * 14:14 driver sets this to indicate command is in the 'huge' + * storage at the end of the command buffers, i.e. scan cmd + * 15:15 uCode sets this in uCode-originated response/notification + */ + __le16 sequence; + + /* command or response/notification data follows immediately */ + u8 data[0]; +} __attribute__ ((packed)); + +/** + * 4965 rate_n_flags bit fields + * + * rate_n_flags format is used in following 4965 commands: + * REPLY_4965_RX (response only) + * REPLY_TX (both command and response) + * REPLY_TX_LINK_QUALITY_CMD + * + * High-throughput (HT) rate format for bits 7:0 (bit 8 must be "1"): + * 2-0: 0) 6 Mbps + * 1) 12 Mbps + * 2) 18 Mbps + * 3) 24 Mbps + * 4) 36 Mbps + * 5) 48 Mbps + * 6) 54 Mbps + * 7) 60 Mbps + * + * 3: 0) Single stream (SISO) + * 1) Dual stream (MIMO) + * + * 5: Value of 0x20 in bits 7:0 indicates 6 Mbps FAT duplicate data + * + * Legacy OFDM rate format for bits 7:0 (bit 8 must be "0", bit 9 "0"): + * 3-0: 0xD) 6 Mbps + * 0xF) 9 Mbps + * 0x5) 12 Mbps + * 0x7) 18 Mbps + * 0x9) 24 Mbps + * 0xB) 36 Mbps + * 0x1) 48 Mbps + * 0x3) 54 Mbps + * + * Legacy CCK rate format for bits 7:0 (bit 8 must be "0", bit 9 "1"): + * 3-0: 10) 1 Mbps + * 20) 2 Mbps + * 55) 5.5 Mbps + * 110) 11 Mbps + */ +#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 + +/* Bit 8: (1) HT format, (0) legacy format in bits 7:0 */ +#define RATE_MCS_FLAGS_POS 8 +#define RATE_MCS_HT_POS 8 +#define RATE_MCS_HT_MSK 0x100 + +/* Bit 9: (1) CCK, (0) OFDM. HT (bit 8) must be "0" for this bit to be valid */ +#define RATE_MCS_CCK_POS 9 +#define RATE_MCS_CCK_MSK 0x200 + +/* Bit 10: (1) Use Green Field preamble */ +#define RATE_MCS_GF_POS 10 +#define RATE_MCS_GF_MSK 0x400 + +/* Bit 11: (1) Use 40Mhz FAT chnl width, (0) use 20 MHz legacy chnl width */ +#define RATE_MCS_FAT_POS 11 +#define RATE_MCS_FAT_MSK 0x800 + +/* Bit 12: (1) Duplicate data on both 20MHz chnls. FAT (bit 11) must be set. */ +#define RATE_MCS_DUP_POS 12 +#define RATE_MCS_DUP_MSK 0x1000 + +/* Bit 13: (1) Short guard interval (0.4 usec), (0) normal GI (0.8 usec) */ +#define RATE_MCS_SGI_POS 13 +#define RATE_MCS_SGI_MSK 0x2000 + +/** + * rate_n_flags Tx antenna masks (4965 has 2 transmitters): + * 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 iwl4965_tx_power - txpower format used in REPLY_SCAN_CMD + * + * Scan uses only one transmitter, so only one analog/dsp gain pair is needed. + */ +struct iwl4965_tx_power { + u8 tx_gain; /* gain for analog radio */ + u8 dsp_atten; /* gain for DSP */ +} __attribute__ ((packed)); + +#define POWER_TABLE_NUM_ENTRIES 33 +#define POWER_TABLE_NUM_HT_OFDM_ENTRIES 32 +#define POWER_TABLE_CCK_ENTRY 32 + +/** + * union iwl4965_tx_power_dual_stream + * + * Host format used for REPLY_TX_PWR_TABLE_CMD, REPLY_CHANNEL_SWITCH + * Use __le32 version (struct tx_power_dual_stream) when building command. + * + * Driver provides radio gain and DSP attenuation settings to device in pairs, + * one value for each transmitter chain. The first value is for transmitter A, + * second for transmitter B. + * + * For SISO bit rates, both values in a pair should be identical. + * For MIMO rates, one value may be different from the other, + * in order to balance the Tx output between the two transmitters. + * + * See more details in doc for TXPOWER in iwl-4965-hw.h. + */ +union iwl4965_tx_power_dual_stream { + struct { + u8 radio_tx_gain[2]; + u8 dsp_predis_atten[2]; + } s; + u32 dw; +}; + +/** + * struct tx_power_dual_stream + * + * Table entries in REPLY_TX_PWR_TABLE_CMD, REPLY_CHANNEL_SWITCH + * + * Same format as iwl_tx_power_dual_stream, but __le32 + */ +struct tx_power_dual_stream { + __le32 dw; +} __attribute__ ((packed)); + +/** + * struct iwl4965_tx_power_db + * + * Entire table within REPLY_TX_PWR_TABLE_CMD, REPLY_CHANNEL_SWITCH + */ +struct iwl4965_tx_power_db { + struct tx_power_dual_stream power_tbl[POWER_TABLE_NUM_ENTRIES]; +} __attribute__ ((packed)); + + +/****************************************************************************** + * (0a) + * Alive and Error Commands & Responses: + * + *****************************************************************************/ + +#define UCODE_VALID_OK __constant_cpu_to_le32(0x1) +#define INITIALIZE_SUBTYPE (9) + +/* + * ("Initialize") REPLY_ALIVE = 0x1 (response only, not a command) + * + * uCode issues this "initialize alive" notification once the initialization + * uCode image has completed its work, and is ready to load the runtime image. + * This is the *first* "alive" notification that the driver will receive after + * rebooting uCode; the "initialize" alive is indicated by subtype field == 9. + * + * See comments documenting "BSM" (bootstrap state machine). + * + * For 4965, this notification contains important calibration data for + * calculating txpower settings: + * + * 1) Power supply voltage indication. The voltage sensor outputs higher + * values for lower voltage, and vice versa. + * + * 2) Temperature measurement parameters, for each of two channel widths + * (20 MHz and 40 MHz) supported by the radios. Temperature sensing + * is done via one of the receiver chains, and channel width influences + * the results. + * + * 3) Tx gain compensation to balance 4965's 2 Tx chains for MIMO operation, + * for each of 5 frequency ranges. + */ +struct iwl4965_init_alive_resp { + u8 ucode_minor; + u8 ucode_major; + __le16 reserved1; + u8 sw_rev[8]; + u8 ver_type; + u8 ver_subtype; /* "9" for initialize alive */ + __le16 reserved2; + __le32 log_event_table_ptr; + __le32 error_event_table_ptr; + __le32 timestamp; + __le32 is_valid; + + /* calibration values from "initialize" uCode */ + __le32 voltage; /* signed, higher value is lower voltage */ + __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 */ +} __attribute__ ((packed)); + + +/** + * REPLY_ALIVE = 0x1 (response only, not a command) + * + * uCode issues this "alive" notification once the runtime image is ready + * to receive commands from the driver. This is the *second* "alive" + * notification that the driver will receive after rebooting uCode; + * this "alive" is indicated by subtype field != 9. + * + * See comments documenting "BSM" (bootstrap state machine). + * + * This response includes two pointers to structures within the device's + * data SRAM (access via HBUS_TARG_MEM_* regs) that are useful for debugging: + * + * 1) log_event_table_ptr indicates base of the event log. This traces + * a 256-entry history of uCode execution within a circular buffer. + * Its header format is: + * + * __le32 log_size; log capacity (in number of entries) + * __le32 type; (1) timestamp with each entry, (0) no timestamp + * __le32 wraps; # times uCode has wrapped to top of circular buffer + * __le32 write_index; next circular buffer entry that uCode would fill + * + * The header is followed by the circular buffer of log entries. Entries + * with timestamps have the following format: + * + * __le32 event_id; range 0 - 1500 + * __le32 timestamp; low 32 bits of TSF (of network, if associated) + * __le32 data; event_id-specific data value + * + * Entries without timestamps contain only event_id and data. + * + * 2) error_event_table_ptr indicates base of the error log. This contains + * information about any uCode error that occurs. For 4965, the format + * of the error log is: + * + * __le32 valid; (nonzero) valid, (0) log is empty + * __le32 error_id; type of error + * __le32 pc; program counter + * __le32 blink1; branch link + * __le32 blink2; branch link + * __le32 ilink1; interrupt link + * __le32 ilink2; interrupt link + * __le32 data1; error-specific data + * __le32 data2; error-specific data + * __le32 line; source code line of error + * __le32 bcon_time; beacon timer + * __le32 tsf_low; network timestamp function timer + * __le32 tsf_hi; network timestamp function timer + * + * The Linux driver can print both logs to the system log when a uCode error + * occurs. + */ +struct iwl4965_alive_resp { + u8 ucode_minor; + u8 ucode_major; + __le16 reserved1; + u8 sw_rev[8]; + u8 ver_type; + u8 ver_subtype; /* not "9" for runtime alive */ + __le16 reserved2; + __le32 log_event_table_ptr; /* SRAM address for event log */ + __le32 error_event_table_ptr; /* SRAM address for error log */ + __le32 timestamp; + __le32 is_valid; +} __attribute__ ((packed)); + + +union tsf { + u8 byte[8]; + __le16 word[4]; + __le32 dw[2]; +}; + +/* + * REPLY_ERROR = 0x2 (response only, not a command) + */ +struct iwl4965_error_resp { + __le32 error_type; + u8 cmd_id; + u8 reserved1; + __le16 bad_cmd_seq_num; + __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, +}; + + +#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) + +/* 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) + + +/* HT flags */ +#define RXON_FLG_CTRL_CHANNEL_LOC_POS (22) +#define RXON_FLG_CTRL_CHANNEL_LOC_HI_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_PURE_40_MSK __constant_cpu_to_le32(0x1<<25) +#define RXON_FLG_CHANNEL_MODE_MIXED_MSK __constant_cpu_to_le32(0x2<<25) + +/* 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) + * + * RXON tunes the radio tuner to a service channel, and sets up a number + * of parameters that are used primarily for Rx, but also for Tx operations. + * + * NOTE: When tuning to a new channel, driver must set the + * RXON_FILTER_ASSOC_MSK to 0. This will clear station-dependent + * info within the device, including the station tables, tx retry + * rate tables, and txpower tables. Driver must build a new station + * table and txpower table before transmitting anything on the RXON + * channel. + * + * NOTE: All RXONs wipe clean the internal txpower table. Driver must + * issue a new REPLY_TX_PWR_TABLE_CMD after each REPLY_RXON (0x10), + * regardless of whether RXON_FILTER_ASSOC_MSK is set. + */ +struct iwl4965_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; + __le16 rx_chain; + u8 ofdm_basic_rates; + u8 cck_basic_rates; + __le16 assoc_id; + __le32 flags; + __le32 filter_flags; + __le16 channel; + u8 ofdm_ht_single_stream_basic_rates; + u8 ofdm_ht_dual_stream_basic_rates; +} __attribute__ ((packed)); + +/* + * REPLY_RXON_ASSOC = 0x11 (command, has simple generic response) + */ +struct iwl4965_rxon_assoc_cmd { + __le32 flags; + __le32 filter_flags; + u8 ofdm_basic_rates; + u8 cck_basic_rates; + u8 ofdm_ht_single_stream_basic_rates; + u8 ofdm_ht_dual_stream_basic_rates; + __le16 rx_chain_select_flags; + __le16 reserved; +} __attribute__ ((packed)); + +/* + * REPLY_RXON_TIMING = 0x14 (command, has simple generic response) + */ +struct iwl4965_rxon_time_cmd { + union tsf timestamp; + __le16 beacon_interval; + __le16 atim_window; + __le32 beacon_init_val; + __le16 listen_interval; + __le16 reserved; +} __attribute__ ((packed)); + +/* + * REPLY_CHANNEL_SWITCH = 0x72 (command, has simple generic response) + */ +struct iwl4965_channel_switch_cmd { + u8 band; + u8 expect_beacon; + __le16 channel; + __le32 rxon_flags; + __le32 rxon_filter_flags; + __le32 switch_time; + struct iwl4965_tx_power_db tx_power; +} __attribute__ ((packed)); + +/* + * CHANNEL_SWITCH_NOTIFICATION = 0x73 (notification only, not a command) + */ +struct iwl4965_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 -- QOS timing params for REPLY_QOS_PARAM + * One for each of 4 EDCA access categories in struct iwl_qosparam_cmd + * + * @cw_min: Contention window, start value in numbers of slots. + * Should be a power-of-2, minus 1. Device's default is 0x0f. + * @cw_max: Contention window, max value in numbers of slots. + * Should be a power-of-2, minus 1. Device's default is 0x3f. + * @aifsn: Number of slots in Arbitration Interframe Space (before + * performing random backoff timing prior to Tx). Device default 1. + * @edca_txop: Length of Tx opportunity, in uSecs. Device default is 0. + * + * Device will automatically increase contention window by (2*CW) + 1 for each + * transmission retry. Device uses cw_max as a bit mask, ANDed with new CW + * value, to cap the CW value. + */ +struct iwl4965_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) + +/* Number of Access Categories (AC) (EDCA), queues 0..3 */ +#define AC_NUM 4 + +/* + * REPLY_QOS_PARAM = 0x13 (command, has simple generic response) + * + * This command sets up timings for each of the 4 prioritized EDCA Tx FIFOs + * 0: Background, 1: Best Effort, 2: Video, 3: Voice. + */ +struct iwl4965_qosparam_cmd { + __le32 qos_flags; + struct iwl4965_ac_qos ac[AC_NUM]; +} __attribute__ ((packed)); + +/****************************************************************************** + * (3) + * Add/Modify Stations Commands & Responses: + * + *****************************************************************************/ +/* + * Multi station support + */ + +/* Special, dedicated locations within device's station table */ +#define IWL_AP_ID 0 +#define IWL_MULTICAST_ID 1 +#define IWL_STA_ID 2 +#define IWL4965_BROADCAST_ID 31 +#define IWL4965_STATION_COUNT 32 + +#define IWL_STATION_COUNT 32 /* MAX(3945,4965)*/ +#define IWL_INVALID_STATION 255 + +#define STA_FLG_PWR_SAVE_MSK __constant_cpu_to_le32(1<<8); +#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) + +/* Use in mode field. 1: modify existing entry, 0: add new station entry */ +#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) + +/* Flags indicate whether to modify vs. don't change various station params */ +#define STA_MODIFY_KEY_MASK 0x01 +#define STA_MODIFY_TID_DISABLE_TX 0x02 +#define STA_MODIFY_TX_RATE_MSK 0x04 +#define STA_MODIFY_ADDBA_TID_MSK 0x08 +#define STA_MODIFY_DELBA_TID_MSK 0x10 + +/* Receiver address (actually, Rx station's index into station table), + * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */ +#define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid)) + +struct iwl4965_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 + * @addr[ETH_ALEN]: station's MAC address + * @sta_id: index of station in uCode's station table + * @modify_mask: STA_MODIFY_*, 1: modify, 0: don't change + * + * Driver selects unused table index when adding new station, + * or the index to a pre-existing station entry when modifying that station. + * Some indexes have special purposes (IWL_AP_ID, index 0, is for AP). + * + * modify_mask flags select which parameters to modify vs. leave alone. + */ +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) + * + * The device contains an internal table of per-station information, + * with info on security keys, aggregation parameters, and Tx rates for + * initial Tx attempt and any retries (4965 uses REPLY_TX_LINK_QUALITY_CMD, + * 3945 uses REPLY_RATE_SCALE to set up rate tables). + * + * REPLY_ADD_STA sets up the table entry for one station, either creating + * a new entry, or modifying a pre-existing one. + * + * NOTE: RXON command (without "associated" bit set) wipes the station table + * clean. Moving into RF_KILL state does this also. Driver must set up + * new station table before transmitting anything on the RXON channel + * (except active scans or active measurements; those commands carry + * their own txpower/rate setup data). + * + * When getting started on a new channel, driver must set up the + * IWL_BROADCAST_ID entry (last entry in the table). For a client + * station in a BSS, once an AP is selected, driver sets up the AP STA + * in the IWL_AP_ID entry (1st entry in the table). BROADCAST and AP + * are all that are needed for a BSS client station. If the device is + * used as AP, or in an IBSS network, driver must set up station table + * entries for all STAs in network, starting with index IWL_STA_ID. + */ +struct iwl4965_addsta_cmd { + u8 mode; /* 1: modify existing, 0: add new station */ + u8 reserved[3]; + struct sta_id_modify sta; + struct iwl4965_keyinfo key; + __le32 station_flags; /* STA_FLG_* */ + __le32 station_flags_msk; /* STA_FLG_* */ + + /* bit field to disable (1) or enable (0) Tx for Traffic ID (TID) + * corresponding to bit (e.g. bit 5 controls TID 5). + * Set modify_mask bit STA_MODIFY_TID_DISABLE_TX to use this field. */ + __le16 tid_disable_tx; + + __le16 reserved1; + + /* TID for which to add block-ack support. + * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ + u8 add_immediate_ba_tid; + + /* TID for which to remove block-ack support. + * Set modify_mask bit STA_MODIFY_DELBA_TID_MSK to use this field. */ + u8 remove_immediate_ba_tid; + + /* Starting Sequence Number for added block-ack support. + * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ + __le16 add_immediate_ba_ssn; + + __le32 reserved2; +} __attribute__ ((packed)); + +#define ADD_STA_SUCCESS_MSK 0x1 +#define ADD_STA_NO_ROOM_IN_TABLE 0x2 +#define ADD_STA_NO_BLOCK_ACK_RESOURCE 0x4 +#define ADD_STA_MODIFY_NON_EXIST_STA 0x8 +/* + * REPLY_ADD_STA = 0x18 (response) + */ +struct iwl4965_add_sta_resp { + u8 status; /* ADD_STA_* */ +} __attribute__ ((packed)); + + +/****************************************************************************** + * (4) + * Rx Responses: + * + *****************************************************************************/ + +struct iwl4965_rx_frame_stats { + u8 phy_count; + u8 id; + u8 rssi; + u8 agc; + __le16 sig_avg; + __le16 noise_diff; + u8 payload[0]; +} __attribute__ ((packed)); + +struct iwl4965_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 iwl4965_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 iwl4965_rx_frame { + struct iwl4965_rx_frame_stats stats; + struct iwl4965_rx_frame_hdr hdr; + struct iwl4965_rx_frame_end end; +} __attribute__ ((packed)); + +/* Fixed (non-configurable) rx data from phy */ +#define RX_PHY_FLAGS_ANTENNAE_OFFSET (4) +#define RX_PHY_FLAGS_ANTENNAE_MASK (0x70) +#define IWL_AGC_DB_MASK (0x3f80) /* MASK(7,13) */ +#define IWL_AGC_DB_POS (7) +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)); + +/* + * REPLY_4965_RX = 0xc3 (response only, not a command) + * Used only for legacy (non 11n) frames. + */ +#define RX_RES_PHY_CNT 14 +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; /* RATE_MCS_* */ + __le16 byte_count; /* frame's byte-count */ + __le16 reserved3; +} __attribute__ ((packed)); + +struct iwl4965_rx_mpdu_res_start { + __le16 byte_count; + __le16 reserved; +} __attribute__ ((packed)); + + +/****************************************************************************** + * (5) + * Tx Commands & Responses: + * + * Driver must place each REPLY_TX command into one of the prioritized Tx + * queues in host DRAM, shared between driver and device (see comments for + * SCD registers and Tx/Rx Queues). When the device's Tx scheduler and uCode + * are preparing to transmit, the device pulls the Tx command over the PCI + * bus via one of the device's Tx DMA channels, to fill an internal FIFO + * from which data will be transmitted. + * + * uCode handles all timing and protocol related to control frames + * (RTS/CTS/ACK), based on flags in the Tx command. uCode and Tx scheduler + * handle reception of block-acks; uCode updates the host driver via + * REPLY_COMPRESSED_BA (4965). + * + * uCode handles retrying Tx when an ACK is expected but not received. + * This includes trying lower data rates than the one requested in the Tx + * command, as set up by the REPLY_RATE_SCALE (for 3945) or + * REPLY_TX_LINK_QUALITY_CMD (4965). + * + * Driver sets up transmit power for various rates via REPLY_TX_PWR_TABLE_CMD. + * This command must be executed after every RXON command, before Tx can occur. + *****************************************************************************/ + +/* REPLY_TX Tx flags field */ + +/* 1: Use Request-To-Send protocol before this frame. + * Mutually exclusive vs. TX_CMD_FLG_CTS_MSK. */ +#define TX_CMD_FLG_RTS_MSK __constant_cpu_to_le32(1 << 1) + +/* 1: Transmit Clear-To-Send to self before this frame. + * Driver should set this for AUTH/DEAUTH/ASSOC-REQ/REASSOC mgmnt frames. + * Mutually exclusive vs. TX_CMD_FLG_RTS_MSK. */ +#define TX_CMD_FLG_CTS_MSK __constant_cpu_to_le32(1 << 2) + +/* 1: Expect ACK from receiving station + * 0: Don't expect ACK (MAC header's duration field s/b 0) + * Set this for unicast frames, but not broadcast/multicast. */ +#define TX_CMD_FLG_ACK_MSK __constant_cpu_to_le32(1 << 3) + +/* For 4965: + * 1: Use rate scale table (see REPLY_TX_LINK_QUALITY_CMD). + * Tx command's initial_rate_index indicates first rate to try; + * uCode walks through table for additional Tx attempts. + * 0: Use Tx rate/MCS from Tx command's rate_n_flags field. + * This rate will be used for all Tx attempts; it will not be scaled. */ +#define TX_CMD_FLG_STA_RATE_MSK __constant_cpu_to_le32(1 << 4) + +/* 1: Expect immediate block-ack. + * Set when Txing a block-ack request frame. Also set TX_CMD_FLG_ACK_MSK. */ +#define TX_CMD_FLG_IMM_BA_RSP_MASK __constant_cpu_to_le32(1 << 6) + +/* 1: Frame requires full Tx-Op protection. + * Set this if either RTS or CTS Tx Flag gets set. */ +#define TX_CMD_FLG_FULL_TXOP_PROT_MSK __constant_cpu_to_le32(1 << 7) + +/* Tx antenna selection field; used only for 3945, reserved (0) for 4965. + * Set field to "0" to allow 3945 uCode to select antenna (normal usage). */ +#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) + +/* 1: Ignore Bluetooth priority for this frame. + * 0: Delay Tx until Bluetooth device is done (normal usage). */ +#define TX_CMD_FLG_BT_DIS_MSK __constant_cpu_to_le32(1 << 12) + +/* 1: uCode overrides sequence control field in MAC header. + * 0: Driver provides sequence control field in MAC header. + * Set this for management frames, non-QOS data frames, non-unicast frames, + * and also in Tx command embedded in REPLY_SCAN_CMD for active scans. */ +#define TX_CMD_FLG_SEQ_CTL_MSK __constant_cpu_to_le32(1 << 13) + +/* 1: This frame is non-last MPDU; more fragments are coming. + * 0: Last fragment, or not using fragmentation. */ +#define TX_CMD_FLG_MORE_FRAG_MSK __constant_cpu_to_le32(1 << 14) + +/* 1: uCode calculates and inserts Timestamp Function (TSF) in outgoing frame. + * 0: No TSF required in outgoing frame. + * Set this for transmitting beacons and probe responses. */ +#define TX_CMD_FLG_TSF_MSK __constant_cpu_to_le32(1 << 16) + +/* 1: Driver inserted 2 bytes pad after the MAC header, for (required) dword + * alignment of frame's payload data field. + * 0: No pad + * Set this for MAC headers with 26 or 30 bytes, i.e. those with QOS or ADDR4 + * field (but not both). Driver must align frame data (i.e. data following + * MAC header) to DWORD boundary. */ +#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_WEP 0x01 +#define TX_CMD_SEC_CCM 0x02 +#define TX_CMD_SEC_TKIP 0x03 +#define TX_CMD_SEC_MSK 0x03 +#define TX_CMD_SEC_SHIFT 6 +#define TX_CMD_SEC_KEY128 0x08 + +/* + * 4965 uCode updates these Tx attempt count values in host DRAM. + * Used for managing Tx retries when expecting block-acks. + * Driver should set these fields to 0. + */ +struct iwl4965_dram_scratch { + u8 try_cnt; /* Tx attempts */ + u8 bt_kill_cnt; /* Tx attempts blocked by Bluetooth device */ + __le16 reserved; +} __attribute__ ((packed)); + +/* + * REPLY_TX = 0x1c (command) + */ +struct iwl4965_tx_cmd { + /* + * MPDU byte count: + * MAC header (24/26/30/32 bytes) + 2 bytes pad if 26/30 header size, + * + 8 byte IV for CCM or TKIP (not used for WEP) + * + Data payload + * + 8-byte MIC (not used for CCM/WEP) + * NOTE: Does not include Tx command bytes, post-MAC pad bytes, + * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.i + * Range: 14-2342 bytes. + */ + __le16 len; + + /* + * MPDU or MSDU byte count for next frame. + * Used for fragmentation and bursting, but not 11n aggregation. + * Same as "len", but for next frame. Set to 0 if not applicable. + */ + __le16 next_frame_len; + + __le32 tx_flags; /* TX_CMD_FLG_* */ + + /* 4965's uCode may modify this field of the Tx command (in host DRAM!). + * Driver must also set dram_lsb_ptr and dram_msb_ptr in this cmd. */ + struct iwl4965_dram_scratch scratch; + + /* Rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is cleared. */ + __le32 rate_n_flags; /* RATE_MCS_* */ + + /* Index of destination station in uCode's station table */ + u8 sta_id; + + /* Type of security encryption: CCM or TKIP */ + u8 sec_ctl; /* TX_CMD_SEC_* */ + + /* + * Index into rate table (see REPLY_TX_LINK_QUALITY_CMD) for initial + * Tx attempt, if TX_CMD_FLG_STA_RATE_MSK is set. Normally "0" for + * data frames, this field may be used to selectively reduce initial + * rate (via non-0 value) for special frames (e.g. management), while + * still supporting rate scaling for all frames. + */ + u8 initial_rate_index; + u8 reserved; + u8 key[16]; + __le16 next_frame_flags; + __le16 reserved2; + union { + __le32 life_time; + __le32 attempt; + } stop_time; + + /* Host DRAM physical address pointer to "scratch" in this command. + * Must be dword aligned. "0" in dram_lsb_ptr disables usage. */ + __le32 dram_lsb_ptr; + u8 dram_msb_ptr; + + u8 rts_retry_limit; /*byte 50 */ + u8 data_retry_limit; /*byte 51 */ + u8 tid_tspec; + union { + __le16 pm_frame_timeout; + __le16 attempt_duration; + } timeout; + + /* + * Duration of EDCA burst Tx Opportunity, in 32-usec units. + * Set this if txop time is not specified by HCCA protocol (e.g. by AP). + */ + __le16 driver_txop; + + /* + * MAC header goes here, followed by 2 bytes padding if MAC header + * length is 26 or 30 bytes, followed by payload data + */ + 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 status + ******************************* */ + +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) + +/* # tx attempts for first frame in aggregation */ +#define AGG_TX_STATE_TRY_CNT_POS 12 +#define AGG_TX_STATE_TRY_CNT_MSK 0xf000 + +/* Command ID and sequence number of Tx command for this frame */ +#define AGG_TX_STATE_SEQ_NUM_POS 16 +#define AGG_TX_STATE_SEQ_NUM_MSK 0xffff0000 + +/* + * REPLY_TX = 0x1c (response) + * + * This response may be in one of two slightly different formats, indicated + * by the frame_count field: + * + * 1) No aggregation (frame_count == 1). This reports Tx results for + * a single frame. Multiple attempts, at various bit rates, may have + * been made for this frame. + * + * 2) Aggregation (frame_count > 1). This reports Tx results for + * 2 or more frames that used block-acknowledge. All frames were + * transmitted at same rate. Rate scaling may have been used if first + * frame in this new agg block failed in previous agg block(s). + * + * Note that, for aggregation, ACK (block-ack) status is not delivered here; + * block-ack has not been received by the time the 4965 records this status. + * This status relates to reasons the tx might have been blocked or aborted + * within the sending station (this 4965), rather than whether it was + * received successfully by the destination station. + */ +struct iwl4965_tx_resp { + u8 frame_count; /* 1 no aggregation, >1 aggregation */ + u8 bt_kill_count; /* # blocked by bluetooth (unused for agg) */ + u8 failure_rts; /* # failures due to unsuccessful RTS */ + u8 failure_frame; /* # failures due to no ACK (unused for agg) */ + + /* For non-agg: Rate at which frame was successful. + * For agg: Rate at which all frames were transmitted. */ + __le32 rate_n_flags; /* RATE_MCS_* */ + + /* For non-agg: RTS + CTS + frame tx attempts time + ACK. + * For agg: RTS + CTS + aggregation tx time + block-ack time. */ + __le16 wireless_media_time; /* uSecs */ + + __le16 reserved; + __le32 pa_power1; /* RF power amplifier measurement (not used) */ + __le32 pa_power2; + + /* + * For non-agg: frame status TX_STATUS_* + * For agg: status of 1st frame, AGG_TX_STATE_*; other frame status + * fields follow this one, up to frame_count. + * Bit fields: + * 11- 0: AGG_TX_STATE_* status code + * 15-12: Retry count for 1st frame in aggregation (retries + * occur if tx failed for this frame when it was a + * member of a previous aggregation block). If rate + * scaling is used, retry count indicates the rate + * table entry used for all frames in the new agg. + * 31-16: Sequence # for this frame's Tx cmd (not SSN!) + */ + __le32 status; /* TX status (for aggregation status of 1st frame) */ +} __attribute__ ((packed)); + +/* + * REPLY_COMPRESSED_BA = 0xc5 (response only, not a command) + * + * Reports Block-Acknowledge from recipient station + */ +struct iwl4965_compressed_ba_resp { + __le32 sta_addr_lo32; + __le16 sta_addr_hi16; + __le16 reserved; + + /* Index of recipient (BA-sending) station in uCode's station table */ + u8 sta_id; + u8 tid; + __le16 ba_seq_ctl; + __le32 ba_bitmap0; + __le32 ba_bitmap1; + __le16 scd_flow; + __le16 scd_ssn; +} __attribute__ ((packed)); + +/* + * REPLY_TX_PWR_TABLE_CMD = 0x97 (command, has simple generic response) + * + * See details under "TXPOWER" in iwl-4965-hw.h. + */ +struct iwl4965_txpowertable_cmd { + u8 band; /* 0: 5 GHz, 1: 2.4 GHz */ + u8 reserved; + __le16 channel; + struct iwl4965_tx_power_db tx_power; +} __attribute__ ((packed)); + +/*RS_NEW_API: only TLC_RTS remains and moved to bit 0 */ +#define LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK (1<<0) + +/* # of EDCA prioritized tx fifos */ +#define LINK_QUAL_AC_NUM AC_NUM + +/* # entries in rate scale table to support Tx retries */ +#define LINK_QUAL_MAX_RETRY_NUM 16 + +/* Tx antenna selection values */ +#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 iwl4965_link_qual_general_params + * + * Used in REPLY_TX_LINK_QUALITY_CMD + */ +struct iwl4965_link_qual_general_params { + u8 flags; + + /* No entries at or above this (driver chosen) index contain MIMO */ + u8 mimo_delimiter; + + /* Best single antenna to use for single stream (legacy, SISO). */ + u8 single_stream_ant_msk; /* LINK_QUAL_ANT_* */ + + /* Best antennas to use for MIMO (unused for 4965, assumes both). */ + u8 dual_stream_ant_msk; /* LINK_QUAL_ANT_* */ + + /* + * If driver needs to use different initial rates for different + * EDCA QOS access categories (as implemented by tx fifos 0-3), + * this table will set that up, by indicating the indexes in the + * rs_table[LINK_QUAL_MAX_RETRY_NUM] rate table at which to start. + * Otherwise, driver should set all entries to 0. + * + * Entry usage: + * 0 = Background, 1 = Best Effort (normal), 2 = Video, 3 = Voice + * TX FIFOs above 3 use same value (typically 0) as TX FIFO 3. + */ + u8 start_rate_index[LINK_QUAL_AC_NUM]; +} __attribute__ ((packed)); + +/** + * struct iwl4965_link_qual_agg_params + * + * Used in REPLY_TX_LINK_QUALITY_CMD + */ +struct iwl4965_link_qual_agg_params { + + /* Maximum number of uSec in aggregation. + * Driver should set this to 4000 (4 milliseconds). */ + __le16 agg_time_limit; + + /* + * Number of Tx retries allowed for a frame, before that frame will + * no longer be considered for the start of an aggregation sequence + * (scheduler will then try to tx it as single frame). + * Driver should set this to 3. + */ + u8 agg_dis_start_th; + + /* + * Maximum number of frames in aggregation. + * 0 = no limit (default). 1 = no aggregation. + * Other values = max # frames in aggregation. + */ + u8 agg_frame_cnt_limit; + + __le32 reserved; +} __attribute__ ((packed)); + +/* + * REPLY_TX_LINK_QUALITY_CMD = 0x4e (command, has simple generic response) + * + * For 4965 only; 3945 uses REPLY_RATE_SCALE. + * + * Each station in the 4965's internal station table has its own table of 16 + * Tx rates and modulation modes (e.g. legacy/SISO/MIMO) for retrying Tx when + * an ACK is not received. This command replaces the entire table for + * one station. + * + * NOTE: Station must already be in 4965's station table. Use REPLY_ADD_STA. + * + * The rate scaling procedures described below work well. Of course, other + * procedures are possible, and may work better for particular environments. + * + * + * FILLING THE RATE TABLE + * + * Given a particular initial rate and mode, as determined by the rate + * scaling algorithm described below, the Linux driver uses the following + * formula to fill the rs_table[LINK_QUAL_MAX_RETRY_NUM] rate table in the + * Link Quality command: + * + * + * 1) If using High-throughput (HT) (SISO or MIMO) initial rate: + * a) Use this same initial rate for first 3 entries. + * b) Find next lower available rate using same mode (SISO or MIMO), + * use for next 3 entries. If no lower rate available, switch to + * legacy mode (no FAT channel, no MIMO, no short guard interval). + * c) If using MIMO, set command's mimo_delimiter to number of entries + * using MIMO (3 or 6). + * d) After trying 2 HT rates, switch to legacy mode (no FAT channel, + * no MIMO, no short guard interval), at the next lower bit rate + * (e.g. if second HT bit rate was 54, try 48 legacy), and follow + * legacy procedure for remaining table entries. + * + * 2) If using legacy initial rate: + * a) Use the initial rate for only one entry. + * b) For each following entry, reduce the rate to next lower available + * rate, until reaching the lowest available rate. + * c) When reducing rate, also switch antenna selection. + * d) Once lowest available rate is reached, repeat this rate until + * rate table is filled (16 entries), switching antenna each entry. + * + * + * ACCUMULATING HISTORY + * + * The rate scaling algorithm for 4965, as implemented in Linux driver, uses + * two sets of frame Tx success history: One for the current/active modulation + * mode, and one for a speculative/search mode that is being attempted. If the + * speculative mode turns out to be more effective (i.e. actual transfer + * rate is better), then the driver continues to use the speculative mode + * as the new current active mode. + * + * Each history set contains, separately for each possible rate, data for a + * sliding window of the 62 most recent tx attempts at that rate. The data + * includes a shifting bitmap of success(1)/failure(0), and sums of successful + * and attempted frames, from which the driver can additionally calculate a + * success ratio (success / attempted) and number of failures + * (attempted - success), and control the size of the window (attempted). + * The driver uses the bit map to remove successes from the success sum, as + * the oldest tx attempts fall out of the window. + * + * When the 4965 makes multiple tx attempts for a given frame, each attempt + * might be at a different rate, and have different modulation characteristics + * (e.g. antenna, fat channel, short guard interval), as set up in the rate + * scaling table in the Link Quality command. The driver must determine + * which rate table entry was used for each tx attempt, to determine which + * rate-specific history to update, and record only those attempts that + * match the modulation characteristics of the history set. + * + * When using block-ack (aggregation), all frames are transmitted at the same + * rate, since there is no per-attempt acknowledgement from the destination + * station. The Tx response struct iwl_tx_resp indicates the Tx rate in + * rate_n_flags field. After receiving a block-ack, the driver can update + * history for the entire block all at once. + * + * + * FINDING BEST STARTING RATE: + * + * When working with a selected initial modulation mode (see below), the + * driver attempts to find a best initial rate. The initial rate is the + * first entry in the Link Quality command's rate table. + * + * 1) Calculate actual throughput (success ratio * expected throughput, see + * table below) for current initial rate. Do this only if enough frames + * have been attempted to make the value meaningful: at least 6 failed + * tx attempts, or at least 8 successes. If not enough, don't try rate + * scaling yet. + * + * 2) Find available rates adjacent to current initial rate. Available means: + * a) supported by hardware && + * b) supported by association && + * c) within any constraints selected by user + * + * 3) Gather measured throughputs for adjacent rates. These might not have + * enough history to calculate a throughput. That's okay, we might try + * using one of them anyway! + * + * 4) Try decreasing rate if, for current rate: + * a) success ratio is < 15% || + * b) lower adjacent rate has better measured throughput || + * c) higher adjacent rate has worse throughput, and lower is unmeasured + * + * As a sanity check, if decrease was determined above, leave rate + * unchanged if: + * a) lower rate unavailable + * b) success ratio at current rate > 85% (very good) + * c) current measured throughput is better than expected throughput + * of lower rate (under perfect 100% tx conditions, see table below) + * + * 5) Try increasing rate if, for current rate: + * a) success ratio is < 15% || + * b) both adjacent rates' throughputs are unmeasured (try it!) || + * b) higher adjacent rate has better measured throughput || + * c) lower adjacent rate has worse throughput, and higher is unmeasured + * + * As a sanity check, if increase was determined above, leave rate + * unchanged if: + * a) success ratio at current rate < 70%. This is not particularly + * good performance; higher rate is sure to have poorer success. + * + * 6) Re-evaluate the rate after each tx frame. If working with block- + * acknowledge, history and statistics may be calculated for the entire + * block (including prior history that fits within the history windows), + * before re-evaluation. + * + * FINDING BEST STARTING MODULATION MODE: + * + * After working with a modulation mode for a "while" (and doing rate scaling), + * the driver searches for a new initial mode in an attempt to improve + * throughput. The "while" is measured by numbers of attempted frames: + * + * For legacy mode, search for new mode after: + * 480 successful frames, or 160 failed frames + * For high-throughput modes (SISO or MIMO), search for new mode after: + * 4500 successful frames, or 400 failed frames + * + * Mode switch possibilities are (3 for each mode): + * + * For legacy: + * Change antenna, try SISO (if HT association), try MIMO (if HT association) + * For SISO: + * Change antenna, try MIMO, try shortened guard interval (SGI) + * For MIMO: + * Try SISO antenna A, SISO antenna B, try shortened guard interval (SGI) + * + * When trying a new mode, use the same bit rate as the old/current mode when + * trying antenna switches and shortened guard interval. When switching to + * SISO from MIMO or legacy, or to MIMO from SISO or legacy, use a rate + * for which the expected throughput (under perfect conditions) is about the + * same or slightly better than the actual measured throughput delivered by + * the old/current mode. + * + * Actual throughput can be estimated by multiplying the expected throughput + * by the success ratio (successful / attempted tx frames). Frame size is + * not considered in this calculation; it assumes that frame size will average + * out to be fairly consistent over several samples. The following are + * metric values for expected throughput assuming 100% success ratio. + * Only G band has support for CCK rates: + * + * RATE: 1 2 5 11 6 9 12 18 24 36 48 54 60 + * + * G: 7 13 35 58 40 57 72 98 121 154 177 186 186 + * A: 0 0 0 0 40 57 72 98 121 154 177 186 186 + * SISO 20MHz: 0 0 0 0 42 42 76 102 124 159 183 193 202 + * SGI SISO 20MHz: 0 0 0 0 46 46 82 110 132 168 192 202 211 + * MIMO 20MHz: 0 0 0 0 74 74 123 155 179 214 236 244 251 + * SGI MIMO 20MHz: 0 0 0 0 81 81 131 164 188 222 243 251 257 + * SISO 40MHz: 0 0 0 0 77 77 127 160 184 220 242 250 257 + * SGI SISO 40MHz: 0 0 0 0 83 83 135 169 193 229 250 257 264 + * MIMO 40MHz: 0 0 0 0 123 123 182 214 235 264 279 285 289 + * SGI MIMO 40MHz: 0 0 0 0 131 131 191 222 242 270 284 289 293 + * + * After the new mode has been tried for a short while (minimum of 6 failed + * frames or 8 successful frames), compare success ratio and actual throughput + * estimate of the new mode with the old. If either is better with the new + * mode, continue to use the new mode. + * + * Continue comparing modes until all 3 possibilities have been tried. + * If moving from legacy to HT, try all 3 possibilities from the new HT + * mode. After trying all 3, a best mode is found. Continue to use this mode + * for the longer "while" described above (e.g. 480 successful frames for + * legacy), and then repeat the search process. + * + */ +struct iwl4965_link_quality_cmd { + + /* Index of destination/recipient station in uCode's station table */ + u8 sta_id; + u8 reserved1; + __le16 control; /* not used */ + struct iwl4965_link_qual_general_params general_params; + struct iwl4965_link_qual_agg_params agg_params; + + /* + * Rate info; when using rate-scaling, Tx command's initial_rate_index + * specifies 1st Tx rate attempted, via index into this table. + * 4965 works its way through table when retrying Tx. + */ + struct { + __le32 rate_n_flags; /* RATE_MCS_*, IWL_RATE_* */ + } rs_table[LINK_QUAL_MAX_RETRY_NUM]; + __le32 reserved2; +} __attribute__ ((packed)); + +/* + * REPLY_BT_CONFIG = 0x9b (command, has simple generic response) + */ +struct iwl4965_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 iwl4965_measure_channel { + __le32 duration; /* measurement duration in extended beacon + * format */ + u8 channel; /* channel to measure */ + u8 type; /* see enum iwl4965_measure_type */ + __le16 reserved; +} __attribute__ ((packed)); + +/* + * REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74 (command) + */ +struct iwl4965_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 iwl4965_measure_channel channels[10]; +} __attribute__ ((packed)); + +/* + * REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74 (response) + */ +struct iwl4965_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 iwl4965_measurement_state { + IWL_MEASUREMENT_START = 0, + IWL_MEASUREMENT_STOP = 1, +}; + +enum iwl4965_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 iwl4965_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 iwl4965_measurement_cca_counters { + __le32 ofdm; + __le32 cck; +} __attribute__ ((packed)); + +enum iwl4965_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 iwl4965_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 iwl4965_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 iwl4965_measurement_histogram histogram; + __le32 stop_time; /* lower 32-bits of TSF */ + __le32 status; /* see iwl4965_measurement_status */ +} __attribute__ ((packed)); + +/****************************************************************************** + * (7) + * Power Management Commands, Responses, Notifications: + * + *****************************************************************************/ + +/** + * struct iwl4965_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 + +#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 iwl4965_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)); + +/* + * PM_SLEEP_NOTIFICATION = 0x7A (notification only, not a command) + * 3945 and 4965 identical. + */ +struct iwl4965_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 iwl4965_card_state_cmd { + __le32 status; /* CARD_STATE_CMD_* request new power state */ +} __attribute__ ((packed)); + +/* + * CARD_STATE_NOTIFICATION = 0xa1 (notification only, not a command) + */ +struct iwl4965_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 iwl4965_ct_kill_config { + __le32 reserved; + __le32 critical_temperature_M; + __le32 critical_temperature_R; +} __attribute__ ((packed)); + +/****************************************************************************** + * (8) + * Scan Commands, Responses, Notifications: + * + *****************************************************************************/ + +struct iwl4965_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 iwl4965_tx_power tpc; + __le16 active_dwell; + __le16 passive_dwell; +} __attribute__ ((packed)); + +struct iwl4965_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 iwl4965_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 */ + __le16 rx_chain; + __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 iwl4965_tx_cmd tx_cmd; + struct iwl4965_ssid_ie direct_scan[PROBE_OPTION_MAX]; + + u8 data[0]; + /* + * The channels start after the probe request payload and are of type: + * + * struct iwl4965_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 iwl4965_scanreq_notification { + __le32 status; /* 1: okay, 2: cannot fulfill request */ +} __attribute__ ((packed)); + +/* + * SCAN_START_NOTIFICATION = 0x82 (notification only, not a command) + */ +struct iwl4965_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 iwl4965_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 iwl4965_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 iwl4965_beacon_notif { + struct iwl4965_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 iwl4965_tx_beacon_cmd { + struct iwl4965_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; + __le32 sent_ba_rsp_cnt; + __le32 dsp_self_kill; + __le32 mh_format_err; + __le32 re_acq_main_rssi_sum; + __le32 reserved3; +} __attribute__ ((packed)); + +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)); + +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 */ + __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; +} __attribute__ ((packed)); + +struct statistics_rx { + struct statistics_rx_phy ofdm; + struct statistics_rx_phy cck; + struct statistics_rx_non_phy general; + struct statistics_rx_ht_phy ofdm_ht; +} __attribute__ ((packed)); + +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)); + +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; + __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; +} __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; + __le32 reserved1; + __le32 reserved2; +} __attribute__ ((packed)); + +struct statistics_general { + __le32 temperature; + __le32 temperature_m; + struct statistics_dbg dbg; + __le32 sleep_time; + __le32 slots_out; + __le32 slots_idle; + __le32 ttl_timestamp; + struct statistics_div div; + __le32 rx_enable_counter; + __le32 reserved1; + __le32 reserved2; + __le32 reserved3; +} __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 iwl4965_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. + */ +#define STATISTICS_REPLY_FLG_BAND_24G_MSK __constant_cpu_to_le32(0x2) +#define STATISTICS_REPLY_FLG_FAT_MODE_MSK __constant_cpu_to_le32(0x8) +struct iwl4965_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 iwl4965_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: + * + * With the uCode used for open source drivers, most Tx calibration (except + * for Tx Power) and most Rx calibration is done by uCode during the + * "initialize" phase of uCode boot. Driver must calibrate only: + * + * 1) Tx power (depends on temperature), described elsewhere + * 2) Receiver gain balance (optimize MIMO, and detect disconnected antennas) + * 3) Receiver sensitivity (to optimize signal detection) + * + *****************************************************************************/ + +/** + * SENSITIVITY_CMD = 0xa8 (command, has simple generic response) + * + * This command sets up the Rx signal detector for a sensitivity level that + * is high enough to lock onto all signals within the associated network, + * but low enough to ignore signals that are below a certain threshold, so as + * not to have too many "false alarms". False alarms are signals that the + * Rx DSP tries to lock onto, but then discards after determining that they + * are noise. + * + * The optimum number of false alarms is between 5 and 50 per 200 TUs + * (200 * 1024 uSecs, i.e. 204.8 milliseconds) of actual Rx time (i.e. + * time listening, not transmitting). Driver must adjust sensitivity so that + * the ratio of actual false alarms to actual Rx time falls within this range. + * + * While associated, uCode delivers STATISTICS_NOTIFICATIONs after each + * received beacon. These provide information to the driver to analyze the + * sensitivity. Don't analyze statistics that come in from scanning, or any + * other non-associated-network source. Pertinent statistics include: + * + * From "general" statistics (struct statistics_rx_non_phy): + * + * (beacon_energy_[abc] & 0x0FF00) >> 8 (unsigned, higher value is lower level) + * Measure of energy of desired signal. Used for establishing a level + * below which the device does not detect signals. + * + * (beacon_silence_rssi_[abc] & 0x0FF00) >> 8 (unsigned, units in dB) + * Measure of background noise in silent period after beacon. + * + * channel_load + * uSecs of actual Rx time during beacon period (varies according to + * how much time was spent transmitting). + * + * From "cck" and "ofdm" statistics (struct statistics_rx_phy), separately: + * + * false_alarm_cnt + * Signal locks abandoned early (before phy-level header). + * + * plcp_err + * Signal locks abandoned late (during phy-level header). + * + * NOTE: Both false_alarm_cnt and plcp_err increment monotonically from + * beacon to beacon, i.e. each value is an accumulation of all errors + * before and including the latest beacon. Values will wrap around to 0 + * after counting up to 2^32 - 1. Driver must differentiate vs. + * previous beacon's values to determine # false alarms in the current + * beacon period. + * + * Total number of false alarms = false_alarms + plcp_errs + * + * For OFDM, adjust the following table entries in struct iwl_sensitivity_cmd + * (notice that the start points for OFDM are at or close to settings for + * maximum sensitivity): + * + * START / MIN / MAX + * HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX 90 / 85 / 120 + * HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX 170 / 170 / 210 + * HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX 105 / 105 / 140 + * HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX 220 / 220 / 270 + * + * If actual rate of OFDM false alarms (+ plcp_errors) is too high + * (greater than 50 for each 204.8 msecs listening), reduce sensitivity + * by *adding* 1 to all 4 of the table entries above, up to the max for + * each entry. Conversely, if false alarm rate is too low (less than 5 + * for each 204.8 msecs listening), *subtract* 1 from each entry to + * increase sensitivity. + * + * For CCK sensitivity, keep track of the following: + * + * 1). 20-beacon history of maximum background noise, indicated by + * (beacon_silence_rssi_[abc] & 0x0FF00), units in dB, across the + * 3 receivers. For any given beacon, the "silence reference" is + * the maximum of last 60 samples (20 beacons * 3 receivers). + * + * 2). 10-beacon history of strongest signal level, as indicated + * by (beacon_energy_[abc] & 0x0FF00) >> 8, across the 3 receivers, + * i.e. the strength of the signal through the best receiver at the + * moment. These measurements are "upside down", with lower values + * for stronger signals, so max energy will be *minimum* value. + * + * Then for any given beacon, the driver must determine the *weakest* + * of the strongest signals; this is the minimum level that needs to be + * successfully detected, when using the best receiver at the moment. + * "Max cck energy" is the maximum (higher value means lower energy!) + * of the last 10 minima. Once this is determined, driver must add + * a little margin by adding "6" to it. + * + * 3). Number of consecutive beacon periods with too few false alarms. + * Reset this to 0 at the first beacon period that falls within the + * "good" range (5 to 50 false alarms per 204.8 milliseconds rx). + * + * Then, adjust the following CCK table entries in struct iwl_sensitivity_cmd + * (notice that the start points for CCK are at maximum sensitivity): + * + * START / MIN / MAX + * HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX 125 / 125 / 200 + * HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX 200 / 200 / 400 + * HD_MIN_ENERGY_CCK_DET_INDEX 100 / 0 / 100 + * + * If actual rate of CCK false alarms (+ plcp_errors) is too high + * (greater than 50 for each 204.8 msecs listening), method for reducing + * sensitivity is: + * + * 1) *Add* 3 to value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX, + * up to max 400. + * + * 2) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX is < 160, + * sensitivity has been reduced a significant amount; bring it up to + * a moderate 161. Otherwise, *add* 3, up to max 200. + * + * 3) a) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX is > 160, + * sensitivity has been reduced only a moderate or small amount; + * *subtract* 2 from value in HD_MIN_ENERGY_CCK_DET_INDEX, + * down to min 0. Otherwise (if gain has been significantly reduced), + * don't change the HD_MIN_ENERGY_CCK_DET_INDEX value. + * + * b) Save a snapshot of the "silence reference". + * + * If actual rate of CCK false alarms (+ plcp_errors) is too low + * (less than 5 for each 204.8 msecs listening), method for increasing + * sensitivity is used only if: + * + * 1a) Previous beacon did not have too many false alarms + * 1b) AND difference between previous "silence reference" and current + * "silence reference" (prev - current) is 2 or more, + * OR 2) 100 or more consecutive beacon periods have had rate of + * less than 5 false alarms per 204.8 milliseconds rx time. + * + * Method for increasing sensitivity: + * + * 1) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX, + * down to min 125. + * + * 2) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX, + * down to min 200. + * + * 3) *Add* 2 to value in HD_MIN_ENERGY_CCK_DET_INDEX, up to max 100. + * + * If actual rate of CCK false alarms (+ plcp_errors) is within good range + * (between 5 and 50 for each 204.8 msecs listening): + * + * 1) Save a snapshot of the silence reference. + * + * 2) If previous beacon had too many CCK false alarms (+ plcp_errors), + * give some extra margin to energy threshold by *subtracting* 8 + * from value in HD_MIN_ENERGY_CCK_DET_INDEX. + * + * For all cases (too few, too many, good range), make sure that the CCK + * detection threshold (energy) is below the energy level for robust + * detection over the past 10 beacon periods, the "Max cck energy". + * Lower values mean higher energy; this means making sure that the value + * in HD_MIN_ENERGY_CCK_DET_INDEX is at or *above* "Max cck energy". + * + * Driver should set the following entries to fixed values: + * + * HD_MIN_ENERGY_OFDM_DET_INDEX 100 + * HD_BARKER_CORR_TH_ADD_MIN_INDEX 190 + * HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX 390 + * HD_OFDM_ENERGY_TH_IN_INDEX 62 + */ + +/* + * Table entries in SENSITIVITY_CMD (struct iwl4965_sensitivity_cmd) + */ +#define HD_TABLE_SIZE (11) /* number of entries */ +#define HD_MIN_ENERGY_CCK_DET_INDEX (0) /* table indexes */ +#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) + +/* Control field in struct iwl4965_sensitivity_cmd */ +#define SENSITIVITY_CMD_CONTROL_DEFAULT_TABLE __constant_cpu_to_le16(0) +#define SENSITIVITY_CMD_CONTROL_WORK_TABLE __constant_cpu_to_le16(1) + +/** + * struct iwl4965_sensitivity_cmd + * @control: (1) updates working table, (0) updates default table + * @table: energy threshold values, use HD_* as index into table + * + * Always use "1" in "control" to update uCode's working table and DSP. + */ +struct iwl4965_sensitivity_cmd { + __le16 control; /* always use "1" */ + __le16 table[HD_TABLE_SIZE]; /* use HD_* as index */ +} __attribute__ ((packed)); + + +/** + * REPLY_PHY_CALIBRATION_CMD = 0xb0 (command, has simple generic response) + * + * This command sets the relative gains of 4965's 3 radio receiver chains. + * + * After the first association, driver should accumulate signal and noise + * statistics from the STATISTICS_NOTIFICATIONs that follow the first 20 + * beacons from the associated network (don't collect statistics that come + * in from scanning, or any other non-network source). + * + * DISCONNECTED ANTENNA: + * + * Driver should determine which antennas are actually connected, by comparing + * average beacon signal levels for the 3 Rx chains. Accumulate (add) the + * following values over 20 beacons, one accumulator for each of the chains + * a/b/c, from struct statistics_rx_non_phy: + * + * beacon_rssi_[abc] & 0x0FF (unsigned, units in dB) + * + * Find the strongest signal from among a/b/c. Compare the other two to the + * strongest. If any signal is more than 15 dB (times 20, unless you + * divide the accumulated values by 20) below the strongest, the driver + * considers that antenna to be disconnected, and should not try to use that + * antenna/chain for Rx or Tx. If both A and B seem to be disconnected, + * driver should declare the stronger one as connected, and attempt to use it + * (A and B are the only 2 Tx chains!). + * + * + * RX BALANCE: + * + * Driver should balance the 3 receivers (but just the ones that are connected + * to antennas, see above) for gain, by comparing the average signal levels + * detected during the silence after each beacon (background noise). + * Accumulate (add) the following values over 20 beacons, one accumulator for + * each of the chains a/b/c, from struct statistics_rx_non_phy: + * + * beacon_silence_rssi_[abc] & 0x0FF (unsigned, units in dB) + * + * Find the weakest background noise level from among a/b/c. This Rx chain + * will be the reference, with 0 gain adjustment. Attenuate other channels by + * finding noise difference: + * + * (accum_noise[i] - accum_noise[reference]) / 30 + * + * The "30" adjusts the dB in the 20 accumulated samples to units of 1.5 dB. + * For use in diff_gain_[abc] fields of struct iwl_calibration_cmd, the + * driver should limit the difference results to a range of 0-3 (0-4.5 dB), + * and set bit 2 to indicate "reduce gain". The value for the reference + * (weakest) chain should be "0". + * + * diff_gain_[abc] bit fields: + * 2: (1) reduce gain, (0) increase gain + * 1-0: amount of gain, units of 1.5 dB + */ + +/* "Differential Gain" opcode used in REPLY_PHY_CALIBRATION_CMD. */ +#define PHY_CALIBRATE_DIFF_GAIN_CMD (7) + +struct iwl4965_calibration_cmd { + u8 opCode; /* PHY_CALIBRATE_DIFF_GAIN_CMD (7) */ + u8 flags; /* not used */ + __le16 reserved; + s8 diff_gain_a; /* see above */ + 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 iwl4965_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 iwl4965_rx_packet { + __le32 len; + struct iwl4965_cmd_header hdr; + union { + struct iwl4965_alive_resp alive_frame; + struct iwl4965_rx_frame rx_frame; + struct iwl4965_tx_resp tx_resp; + struct iwl4965_spectrum_notification spectrum_notif; + struct iwl4965_csa_notification csa_notif; + struct iwl4965_error_resp err_resp; + struct iwl4965_card_state_notif card_state_notif; + struct iwl4965_beacon_notif beacon_status; + struct iwl4965_add_sta_resp add_sta; + struct iwl4965_sleep_notification sleep_notif; + struct iwl4965_spectrum_resp spectrum; + struct iwl4965_notif_statistics stats; + struct iwl4965_compressed_ba_resp compressed_ba; + struct iwl4965_missed_beacon_notif missed_beacon; + __le32 status; + u8 raw[0]; + } u; +} __attribute__ ((packed)); + +#define IWL_RX_FRAME_SIZE (4 + sizeof(struct iwl4965_rx_frame)) + +#endif /* __iwl4965_commands_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-4965-debug.h b/drivers/net/wireless/iwlwifi/iwl-4965-debug.h new file mode 100644 index 0000000..00bc1fa --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-4965-debug.h @@ -0,0 +1,152 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2007 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * James P. Ketrenos <ipw2100-admin@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#ifndef __iwl4965_debug_h__ +#define __iwl4965_debug_h__ + +#ifdef CONFIG_IWL4965_DEBUG +extern u32 iwl4965_debug_level; +#define IWL_DEBUG(level, fmt, args...) \ +do { if (iwl4965_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 ((iwl4965_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_IWL4965_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/iwl/debug_level + * + * you simply need to add your entry to the iwl4965_debug_levels array. + * + * If you do not see debug_level in /proc/net/iwl then you do not have + * CONFIG_IWL4965_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_RATE_LIMIT(f, a...) IWL_DEBUG_LIMIT(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_ASSOC_LIMIT(f, a...) \ + IWL_DEBUG_LIMIT(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/iwlwifi/iwl-4965-hw.h b/drivers/net/wireless/iwlwifi/iwl-4965-hw.h index 5e958a7..7e7d6e4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965-hw.h +++ b/drivers/net/wireless/iwlwifi/iwl-4965-hw.h @@ -8,7 +8,7 @@ * 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 + * 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 @@ -60,39 +60,618 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ +/* + * Please use this file (iwl-4965-hw.h) only for hardware-related definitions. + * Use iwl-4965-commands.h for uCode API definitions. + * Use iwl-4965.h for driver implementation definitions. + */ #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 IWL_MAX_INST_SIZE (96 * 1024) -#define IWL_MAX_DATA_SIZE (40 * 1024) - -/********************* START TXPOWER *****************************************/ +/* + * uCode queue management definitions ... + * Queue #4 is the command queue for 3945 and 4965; map it to Tx FIFO chnl 4. + * The first queue used for block-ack aggregation is #7 (4965 only). + * All block-ack aggregation queues should map to Tx DMA/FIFO channel 7. + */ +#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 +#define IWL_HT_RATES 16 +#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 */ +#define IWL_RSSI_OFFSET 44 + +/* + * EEPROM related constants, enums, and structures. + */ + +/* + * EEPROM access time values: + * + * Driver initiates EEPROM read by writing byte address << 1 to CSR_EEPROM_REG, + * then clearing (with subsequent read/modify/write) CSR_EEPROM_REG bit + * CSR_EEPROM_REG_BIT_CMD (0x2). + * Driver then polls CSR_EEPROM_REG for CSR_EEPROM_REG_READ_VALID_MSK (0x1). + * When polling, wait 10 uSec between polling loops, up to a maximum 5000 uSec. + * Driver reads 16-bit value from bits 31-16 of CSR_EEPROM_REG. + */ +#define IWL_EEPROM_ACCESS_TIMEOUT 5000 /* uSec */ +#define IWL_EEPROM_ACCESS_DELAY 10 /* uSec */ + +/* + * Regulatory channel usage flags in EEPROM struct iwl4965_eeprom_channel.flags. + * + * IBSS and/or AP operation is allowed *only* on those channels with + * (VALID && IBSS && ACTIVE && !RADAR). This restriction is in place because + * RADAR detection is not supported by the 4965 driver, but is a + * requirement for establishing a new network for legal operation on channels + * requiring RADAR detection or restricting ACTIVE scanning. + * + * NOTE: "WIDE" flag does not indicate anything about "FAT" 40 MHz channels. + * It only indicates that 20 MHz channel use is supported; FAT channel + * usage is indicated by a separate set of regulatory flags for each + * FAT channel pair. + * + * NOTE: Using a channel inappropriately will result in a uCode error! + */ 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 + 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), /* 20 MHz channel okay */ + EEPROM_CHANNEL_NARROW = (1 << 6), /* 10 MHz channel (not used) */ + EEPROM_CHANNEL_DFS = (1 << 7), /* dynamic freq selection candidate */ }; -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 -}; +/* SKU Capabilities */ +#define EEPROM_SKU_CAP_SW_RF_KILL_ENABLE (1 << 0) +#define EEPROM_SKU_CAP_HW_RF_KILL_ENABLE (1 << 1) + +/* *regulatory* channel data format in eeprom, one for each channel. + * There are separate entries for FAT (40 MHz) vs. normal (20 MHz) channels. */ +struct iwl4965_eeprom_channel { + u8 flags; /* EEPROM_CHANNEL_* flags copied from EEPROM */ + s8 max_power_avg; /* max power (dBm) on this chnl, limit 31 */ +} __attribute__ ((packed)); + +/* 4965 has two radio transmitters (and 3 radio receivers) */ +#define EEPROM_TX_POWER_TX_CHAINS (2) + +/* 4965 has room for up to 8 sets of txpower calibration data */ +#define EEPROM_TX_POWER_BANDS (8) + +/* 4965 factory calibration measures txpower gain settings for + * each of 3 target output levels */ +#define EEPROM_TX_POWER_MEASUREMENTS (3) + +/* 4965 driver does not work with txpower calibration version < 5. + * Look for this in calib_version member of struct iwl4965_eeprom. */ +#define EEPROM_TX_POWER_VERSION_NEW (5) + + +/* + * 4965 factory calibration data for one txpower level, on one channel, + * measured on one of the 2 tx chains (radio transmitter and associated + * antenna). EEPROM contains: + * + * 1) Temperature (degrees Celsius) of device when measurement was made. + * + * 2) Gain table index used to achieve the target measurement power. + * This refers to the "well-known" gain tables (see iwl-4965-hw.h). + * + * 3) Actual measured output power, in half-dBm ("34" = 17 dBm). + * + * 4) RF power amplifier detector level measurement (not used). + */ +struct iwl4965_eeprom_calib_measure { + u8 temperature; /* Device temperature (Celsius) */ + u8 gain_idx; /* Index into gain table */ + u8 actual_pow; /* Measured RF output power, half-dBm */ + s8 pa_det; /* Power amp detector level (not used) */ +} __attribute__ ((packed)); + + +/* + * 4965 measurement set for one channel. EEPROM contains: + * + * 1) Channel number measured + * + * 2) Measurements for each of 3 power levels for each of 2 radio transmitters + * (a.k.a. "tx chains") (6 measurements altogether) + */ +struct iwl4965_eeprom_calib_ch_info { + u8 ch_num; + struct iwl4965_eeprom_calib_measure measurements[EEPROM_TX_POWER_TX_CHAINS] + [EEPROM_TX_POWER_MEASUREMENTS]; +} __attribute__ ((packed)); + +/* + * 4965 txpower subband info. + * + * For each frequency subband, EEPROM contains the following: + * + * 1) First and last channels within range of the subband. "0" values + * indicate that this sample set is not being used. + * + * 2) Sample measurement sets for 2 channels close to the range endpoints. + */ +struct iwl4965_eeprom_calib_subband_info { + u8 ch_from; /* channel number of lowest channel in subband */ + u8 ch_to; /* channel number of highest channel in subband */ + struct iwl4965_eeprom_calib_ch_info ch1; + struct iwl4965_eeprom_calib_ch_info ch2; +} __attribute__ ((packed)); + + +/* + * 4965 txpower calibration info. EEPROM contains: + * + * 1) Factory-measured saturation power levels (maximum levels at which + * tx power amplifier can output a signal without too much distortion). + * There is one level for 2.4 GHz band and one for 5 GHz band. These + * values apply to all channels within each of the bands. + * + * 2) Factory-measured power supply voltage level. This is assumed to be + * constant (i.e. same value applies to all channels/bands) while the + * factory measurements are being made. + * + * 3) Up to 8 sets of factory-measured txpower calibration values. + * These are for different frequency ranges, since txpower gain + * characteristics of the analog radio circuitry vary with frequency. + * + * Not all sets need to be filled with data; + * struct iwl4965_eeprom_calib_subband_info contains range of channels + * (0 if unused) for each set of data. + */ +struct iwl4965_eeprom_calib_info { + u8 saturation_power24; /* half-dBm (e.g. "34" = 17 dBm) */ + u8 saturation_power52; /* half-dBm */ + s16 voltage; /* signed */ + struct iwl4965_eeprom_calib_subband_info band_info[EEPROM_TX_POWER_BANDS]; +} __attribute__ ((packed)); + + +/* + * 4965 EEPROM map + */ +struct iwl4965_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 */ + 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]; +#define EEPROM_REGULATORY_SKU_ID (2*0x60) /* 4 bytes */ + u8 sku_id[4]; /* abs.ofs: 192 */ + +/* + * Per-channel regulatory data. + * + * Each channel that *might* be supported by 3945 or 4965 has a fixed location + * in EEPROM containing EEPROM_CHANNEL_* usage flags (LSB) and max regulatory + * txpower (MSB). + * + * Entries immediately below are for 20 MHz channel width. FAT (40 MHz) + * channels (only for 4965, not supported by 3945) appear later in the EEPROM. + * + * 2.4 GHz channels 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 + */ +#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 iwl4965_eeprom_channel band_1_channels[14]; /* abs.ofs: 196 */ + +/* + * 4.9 GHz channels 183, 184, 185, 187, 188, 189, 192, 196, + * 5.0 GHz channels 7, 8, 11, 12, 16 + * (4915-5080MHz) (none of these is ever supported) + */ +#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 iwl4965_eeprom_channel band_2_channels[13]; /* abs.ofs: 228 */ + +/* + * 5.2 GHz channels 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 + * (5170-5320MHz) + */ +#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 iwl4965_eeprom_channel band_3_channels[12]; /* abs.ofs: 256 */ + +/* + * 5.5 GHz channels 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 + * (5500-5700MHz) + */ +#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 iwl4965_eeprom_channel band_4_channels[11]; /* abs.ofs: 282 */ + +/* + * 5.7 GHz channels 145, 149, 153, 157, 161, 165 + * (5725-5825MHz) + */ +#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 iwl4965_eeprom_channel band_5_channels[6]; /* abs.ofs: 306 */ + + u8 reserved10[2]; + + +/* + * 2.4 GHz FAT channels 1 (5), 2 (6), 3 (7), 4 (8), 5 (9), 6 (10), 7 (11) + * + * The channel listed is the center of the lower 20 MHz half of the channel. + * The overall center frequency is actually 2 channels (10 MHz) above that, + * and the upper half of each FAT channel is centered 4 channels (20 MHz) away + * from the lower half; e.g. the upper half of FAT channel 1 is channel 5, + * and the overall FAT channel width centers on channel 3. + * + * NOTE: The RXON command uses 20 MHz channel numbers to specify the + * control channel to which to tune. RXON also specifies whether the + * control channel is the upper or lower half of a FAT channel. + * + * NOTE: 4965 does not support FAT channels on 2.4 GHz. + */ +#define EEPROM_REGULATORY_BAND_24_FAT_CHANNELS (2*0xA0) /* 14 bytes */ + struct iwl4965_eeprom_channel band_24_channels[7]; /* abs.ofs: 320 */ + u8 reserved11[2]; + +/* + * 5.2 GHz FAT channels 36 (40), 44 (48), 52 (56), 60 (64), + * 100 (104), 108 (112), 116 (120), 124 (128), 132 (136), 149 (153), 157 (161) + */ +#define EEPROM_REGULATORY_BAND_52_FAT_CHANNELS (2*0xA8) /* 22 bytes */ + struct iwl4965_eeprom_channel band_52_channels[11]; /* abs.ofs: 336 */ + u8 reserved12[6]; + +/* + * 4965 driver requires txpower calibration format version 5 or greater. + * Driver does not work with txpower calibration version < 5. + * This value is simply a 16-bit number, no major/minor versions here. + */ +#define EEPROM_CALIB_VERSION_OFFSET (2*0xB6) /* 2 bytes */ + u16 calib_version; /* abs.ofs: 364 */ + u8 reserved13[2]; + u8 reserved14[96]; /* abs.ofs: 368 */ + +/* + * 4965 Txpower calibration data. + */ +#define EEPROM_IWL_CALIB_TXPOWER_OFFSET (2*0xE8) /* 48 bytes */ + struct iwl4965_eeprom_calib_info calib_info; /* abs.ofs: 464 */ + + u8 reserved16[140]; /* fill out to full 1024 byte block */ -#define POWER_TABLE_NUM_HT_OFDM_ENTRIES (32) -/* Temperature calibration offset is 3% 0C in Kelvin */ +} __attribute__ ((packed)); + +#define IWL_EEPROM_IMAGE_SIZE 1024 + +/* End of EEPROM */ + +#include "iwl-4965-commands.h" + +#define PCI_LINK_CTRL 0x0F0 +#define PCI_POWER_SOURCE 0x0C8 +#define PCI_REG_WUM8 0x0E8 +#define PCI_CFG_PMC_PME_FROM_D3COLD_SUPPORT (0x80000000) + +/*=== CSR (control and status registers) ===*/ +#define CSR_BASE (0x000) + +#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) + +/* + * Hardware revision info + * Bit fields: + * 31-8: Reserved + * 7-4: Type of device: 0x0 = 4965, 0xd = 3945 + * 3-2: Revision step: 0 = A, 1 = B, 2 = C, 3 = D + * 1-0: "Dash" value, as in A-1, etc. + * + * NOTE: Revision step affects calculation of CCK txpower for 4965. + */ +#define CSR_HW_REV (CSR_BASE+0x028) + +/* EEPROM reads */ +#define CSR_EEPROM_REG (CSR_BASE+0x02c) +#define CSR_EEPROM_GP (CSR_BASE+0x030) +#define CSR_GP_UCODE (CSR_BASE+0x044) +#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) + +/* + * Indicates hardware rev, to determine CCK backoff for txpower calculation. + * Bit fields: + * 3-2: 0 = A, 1 = B, 2 = C, 3 = D step + */ +#define CSR_HW_REV_WA_REG (CSR_BASE+0x22C) + +/* Hardware interface configuration bits */ +#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) +#define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000) + +/* interrupt flags in INTA, set by uCode or hardware (e.g. dma), + * acknowledged (reset) by host writing "1" to flagged bits. */ +#define CSR_INT_BIT_FH_RX (1<<31) /* Rx DMA, cmd responses, FH_INT[17:16] */ +#define CSR_INT_BIT_HW_ERR (1<<29) /* DMA hardware error FH_INT[31] */ +#define CSR_INT_BIT_DNLD (1<<28) /* uCode Download */ +#define CSR_INT_BIT_FH_TX (1<<27) /* Tx DMA FH_INT[1:0] */ +#define CSR_INT_BIT_MAC_CLK_ACTV (1<<26) /* NIC controller's clock toggled on/off */ +#define CSR_INT_BIT_SW_ERR (1<<25) /* uCode error */ +#define CSR_INT_BIT_RF_KILL (1<<7) /* HW RFKILL switch GP_CNTRL[27] toggled */ +#define CSR_INT_BIT_CT_KILL (1<<6) /* Critical temp (chip too hot) rfkill */ +#define CSR_INT_BIT_SW_RX (1<<3) /* Rx, command responses, 3945 */ +#define CSR_INT_BIT_WAKEUP (1<<1) /* NIC controller waking up (pwr mgmt) */ +#define CSR_INT_BIT_ALIVE (1<<0) /* uCode interrupts once it initializes */ + +#define CSR_INI_SET_MASK (CSR_INT_BIT_FH_RX | \ + CSR_INT_BIT_HW_ERR | \ + CSR_INT_BIT_FH_TX | \ + CSR_INT_BIT_SW_ERR | \ + CSR_INT_BIT_RF_KILL | \ + CSR_INT_BIT_SW_RX | \ + CSR_INT_BIT_WAKEUP | \ + CSR_INT_BIT_ALIVE) + +/* interrupt flags in FH (flow handler) (PCI busmaster DMA) */ +#define CSR_FH_INT_BIT_ERR (1<<31) /* Error */ +#define CSR_FH_INT_BIT_HI_PRIOR (1<<30) /* High priority Rx, bypass coalescing */ +#define CSR_FH_INT_BIT_RX_CHNL1 (1<<17) /* Rx channel 1 */ +#define CSR_FH_INT_BIT_RX_CHNL0 (1<<16) /* Rx channel 0 */ +#define CSR_FH_INT_BIT_TX_CHNL1 (1<<1) /* Tx channel 1 */ +#define CSR_FH_INT_BIT_TX_CHNL0 (1<<0) /* Tx channel 0 */ + +#define CSR_FH_INT_RX_MASK (CSR_FH_INT_BIT_HI_PRIOR | \ + CSR_FH_INT_BIT_RX_CHNL1 | \ + CSR_FH_INT_BIT_RX_CHNL0) + +#define CSR_FH_INT_TX_MASK (CSR_FH_INT_BIT_TX_CHNL1 | \ + CSR_FH_INT_BIT_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) + + +/* EEPROM REG */ +#define CSR_EEPROM_REG_READ_VALID_MSK (0x00000001) +#define CSR_EEPROM_REG_BIT_CMD (0x00000002) + +/* EEPROM GP */ +#define CSR_EEPROM_GP_VALID_MSK (0x00000006) +#define CSR_EEPROM_GP_BAD_SIGNATURE (0x00000000) +#define CSR_EEPROM_GP_IF_OWNER_MSK (0x00000180) + +/* UCODE DRV GP */ +#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) + +/* GPIO */ +#define CSR_GPIO_IN_BIT_AUX_POWER (0x00000200) +#define CSR_GPIO_IN_VAL_VAUX_PWR_SRC (0x00000000) +#define CSR_GPIO_IN_VAL_VMAIN_PWR_SRC CSR_GPIO_IN_BIT_AUX_POWER + +/* GI Chicken Bits */ +#define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX (0x00800000) +#define CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER (0x20000000) + +/*=== HBUS (Host-side Bus) ===*/ +#define HBUS_BASE (0x400) + +/* + * Registers for accessing device's internal SRAM memory (e.g. SCD SRAM + * structures, error log, event log, verifying uCode load). + * First write to address register, then read from or write to data register + * to complete the job. Once the address register is set up, accesses to + * data registers auto-increment the address by one dword. + * Bit usage for address registers (read or write): + * 0-31: memory address within device + */ +#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) + +/* + * Registers for accessing device's internal peripheral registers + * (e.g. SCD, BSM, etc.). First write to address register, + * then read from or write to data register to complete the job. + * Bit usage for address registers (read or write): + * 0-15: register address (offset) within device + * 24-25: (# bytes - 1) to read or write (e.g. 3 for dword) + */ +#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) + +/* + * Per-Tx-queue write pointer (index, really!) (3945 and 4965). + * Driver sets this to indicate index to next TFD that driver will fill + * (1 past latest filled). + * Bit usage: + * 0-7: queue write index (0-255) + * 11-8: queue selector (0-15) + */ +#define HBUS_TARG_WRPTR (HBUS_BASE+0x060) + +#define HBUS_TARG_MBX_C (HBUS_BASE+0x030) + +#define HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED (0x00000004) + +#define TFD_QUEUE_SIZE_MAX (256) + +#define IWL_NUM_SCAN_RATES (2) + +#define IWL_DEFAULT_TX_RETRY 15 + +#define RX_QUEUE_SIZE 256 +#define RX_QUEUE_MASK 255 +#define RX_QUEUE_SIZE_LOG 8 + +#define TFD_TX_CMD_SLOTS 256 +#define TFD_CMD_SLOTS 32 + +#define TFD_MAX_PAYLOAD_SIZE (sizeof(struct iwl4965_cmd) - \ + sizeof(struct iwl4965_cmd_meta)) + +/* + * RX related structures and functions + */ +#define RX_FREE_BUFFERS 64 +#define RX_LOW_WATERMARK 8 + +/* Size of one Rx buffer in host DRAM */ +#define IWL_RX_BUF_SIZE_4K (4 * 1024) +#define IWL_RX_BUF_SIZE_8K (8 * 1024) + +/* Sizes and addresses for instruction and data memory (SRAM) in + * 4965's embedded processor. Driver access is via HBUS_TARG_MEM_* regs. */ +#define RTC_INST_LOWER_BOUND (0x000000) +#define KDR_RTC_INST_UPPER_BOUND (0x018000) + +#define RTC_DATA_LOWER_BOUND (0x800000) +#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 + +/* Size of uCode instruction memory in bootstrap state machine */ +#define IWL_MAX_BSM_SIZE BSM_SRAM_SIZE + +static inline int iwl4965_hw_valid_rtc_data_addr(u32 addr) +{ + return (addr >= RTC_DATA_LOWER_BOUND) && + (addr < KDR_RTC_DATA_UPPER_BOUND); +} + +/********************* START TEMPERATURE *************************************/ + +/** + * 4965 temperature calculation. + * + * The driver must calculate the device temperature before calculating + * a txpower setting (amplifier gain is temperature dependent). The + * calculation uses 4 measurements, 3 of which (R1, R2, R3) are calibration + * values used for the life of the driver, and one of which (R4) is the + * real-time temperature indicator. + * + * uCode provides all 4 values to the driver via the "initialize alive" + * notification (see struct iwl4965_init_alive_resp). After the runtime uCode + * image loads, uCode updates the R4 value via statistics notifications + * (see STATISTICS_NOTIFICATION), which occur after each received beacon + * when associated, or can be requested via REPLY_STATISTICS_CMD. + * + * NOTE: uCode provides the R4 value as a 23-bit signed value. Driver + * must sign-extend to 32 bits before applying formula below. + * + * Formula: + * + * degrees Kelvin = ((97 * 259 * (R4 - R2) / (R3 - R1)) / 100) + 8 + * + * NOTE: The basic formula is 259 * (R4-R2) / (R3-R1). The 97/100 is + * an additional correction, which should be centered around 0 degrees + * Celsius (273 degrees Kelvin). The 8 (3 percent of 273) compensates for + * centering the 97/100 correction around 0 degrees K. + * + * Add 273 to Kelvin value to find degrees Celsius, for comparing current + * temperature with factory-measured temperatures when calculating txpower + * settings. + */ #define TEMPERATURE_CALIB_KELVIN_OFFSET 8 #define TEMPERATURE_CALIB_A_VAL 259 +/* Limit range of calculated temperature to be between these Kelvin values */ #define IWL_TX_POWER_TEMPERATURE_MIN (263) #define IWL_TX_POWER_TEMPERATURE_MAX (410) @@ -100,383 +679,875 @@ enum { (((t) < IWL_TX_POWER_TEMPERATURE_MIN) || \ ((t) > IWL_TX_POWER_TEMPERATURE_MAX)) -#define IWL_TX_POWER_ILLEGAL_TEMPERATURE (300) +/********************* END TEMPERATURE ***************************************/ -#define IWL_TX_POWER_TEMPERATURE_DIFFERENCE (2) +/********************* START TXPOWER *****************************************/ -#define IWL_TX_POWER_MIMO_REGULATORY_COMPENSATION (6) +/** + * 4965 txpower calculations rely on information from three sources: + * + * 1) EEPROM + * 2) "initialize" alive notification + * 3) statistics notifications + * + * EEPROM data consists of: + * + * 1) Regulatory information (max txpower and channel usage flags) is provided + * separately for each channel that can possibly supported by 4965. + * 40 MHz wide (.11n fat) channels are listed separately from 20 MHz + * (legacy) channels. + * + * See struct iwl4965_eeprom_channel for format, and struct iwl4965_eeprom + * for locations in EEPROM. + * + * 2) Factory txpower calibration information is provided separately for + * sub-bands of contiguous channels. 2.4GHz has just one sub-band, + * but 5 GHz has several sub-bands. + * + * In addition, per-band (2.4 and 5 Ghz) saturation txpowers are provided. + * + * See struct iwl4965_eeprom_calib_info (and the tree of structures + * contained within it) for format, and struct iwl4965_eeprom for + * locations in EEPROM. + * + * "Initialization alive" notification (see struct iwl4965_init_alive_resp) + * consists of: + * + * 1) Temperature calculation parameters. + * + * 2) Power supply voltage measurement. + * + * 3) Tx gain compensation to balance 2 transmitters for MIMO use. + * + * Statistics notifications deliver: + * + * 1) Current values for temperature param R4. + */ +/** + * To calculate a txpower setting for a given desired target txpower, channel, + * modulation bit rate, and transmitter chain (4965 has 2 transmitters to + * support MIMO and transmit diversity), driver must do the following: + * + * 1) Compare desired txpower vs. (EEPROM) regulatory limit for this channel. + * Do not exceed regulatory limit; reduce target txpower if necessary. + * + * If setting up txpowers for MIMO rates (rate indexes 8-15, 24-31), + * 2 transmitters will be used simultaneously; driver must reduce the + * regulatory limit by 3 dB (half-power) for each transmitter, so the + * combined total output of the 2 transmitters is within regulatory limits. + * + * + * 2) Compare target txpower vs. (EEPROM) saturation txpower *reduced by + * backoff for this bit rate*. Do not exceed (saturation - backoff[rate]); + * reduce target txpower if necessary. + * + * Backoff values below are in 1/2 dB units (equivalent to steps in + * txpower gain tables): + * + * OFDM 6 - 36 MBit: 10 steps (5 dB) + * OFDM 48 MBit: 15 steps (7.5 dB) + * OFDM 54 MBit: 17 steps (8.5 dB) + * OFDM 60 MBit: 20 steps (10 dB) + * CCK all rates: 10 steps (5 dB) + * + * Backoff values apply to saturation txpower on a per-transmitter basis; + * when using MIMO (2 transmitters), each transmitter uses the same + * saturation level provided in EEPROM, and the same backoff values; + * no reduction (such as with regulatory txpower limits) is required. + * + * Saturation and Backoff values apply equally to 20 Mhz (legacy) channel + * widths and 40 Mhz (.11n fat) channel widths; there is no separate + * factory measurement for fat channels. + * + * The result of this step is the final target txpower. The rest of + * the steps figure out the proper settings for the device to achieve + * that target txpower. + * + * + * 3) Determine (EEPROM) calibration subband for the target channel, by + * comparing against first and last channels in each subband + * (see struct iwl4965_eeprom_calib_subband_info). + * + * + * 4) Linearly interpolate (EEPROM) factory calibration measurement sets, + * referencing the 2 factory-measured (sample) channels within the subband. + * + * Interpolation is based on difference between target channel's frequency + * and the sample channels' frequencies. Since channel numbers are based + * on frequency (5 MHz between each channel number), this is equivalent + * to interpolating based on channel number differences. + * + * Note that the sample channels may or may not be the channels at the + * edges of the subband. The target channel may be "outside" of the + * span of the sampled channels. + * + * Driver may choose the pair (for 2 Tx chains) of measurements (see + * struct iwl4965_eeprom_calib_ch_info) for which the actual measured + * txpower comes closest to the desired txpower. Usually, though, + * the middle set of measurements is closest to the regulatory limits, + * and is therefore a good choice for all txpower calculations (this + * assumes that high accuracy is needed for maximizing legal txpower, + * while lower txpower configurations do not need as much accuracy). + * + * Driver should interpolate both members of the chosen measurement pair, + * i.e. for both Tx chains (radio transmitters), unless the driver knows + * that only one of the chains will be used (e.g. only one tx antenna + * connected, but this should be unusual). The rate scaling algorithm + * switches antennas to find best performance, so both Tx chains will + * be used (although only one at a time) even for non-MIMO transmissions. + * + * Driver should interpolate factory values for temperature, gain table + * index, and actual power. The power amplifier detector values are + * not used by the driver. + * + * Sanity check: If the target channel happens to be one of the sample + * channels, the results should agree with the sample channel's + * measurements! + * + * + * 5) Find difference between desired txpower and (interpolated) + * factory-measured txpower. Using (interpolated) factory gain table index + * (shown elsewhere) as a starting point, adjust this index lower to + * increase txpower, or higher to decrease txpower, until the target + * txpower is reached. Each step in the gain table is 1/2 dB. + * + * For example, if factory measured txpower is 16 dBm, and target txpower + * is 13 dBm, add 6 steps to the factory gain index to reduce txpower + * by 3 dB. + * + * + * 6) Find difference between current device temperature and (interpolated) + * factory-measured temperature for sub-band. Factory values are in + * degrees Celsius. To calculate current temperature, see comments for + * "4965 temperature calculation". + * + * If current temperature is higher than factory temperature, driver must + * increase gain (lower gain table index), and vice versa. + * + * Temperature affects gain differently for different channels: + * + * 2.4 GHz all channels: 3.5 degrees per half-dB step + * 5 GHz channels 34-43: 4.5 degrees per half-dB step + * 5 GHz channels >= 44: 4.0 degrees per half-dB step + * + * NOTE: Temperature can increase rapidly when transmitting, especially + * with heavy traffic at high txpowers. Driver should update + * temperature calculations often under these conditions to + * maintain strong txpower in the face of rising temperature. + * + * + * 7) Find difference between current power supply voltage indicator + * (from "initialize alive") and factory-measured power supply voltage + * indicator (EEPROM). + * + * If the current voltage is higher (indicator is lower) than factory + * voltage, gain should be reduced (gain table index increased) by: + * + * (eeprom - current) / 7 + * + * If the current voltage is lower (indicator is higher) than factory + * voltage, gain should be increased (gain table index decreased) by: + * + * 2 * (current - eeprom) / 7 + * + * If number of index steps in either direction turns out to be > 2, + * something is wrong ... just use 0. + * + * NOTE: Voltage compensation is independent of band/channel. + * + * NOTE: "Initialize" uCode measures current voltage, which is assumed + * to be constant after this initial measurement. Voltage + * compensation for txpower (number of steps in gain table) + * may be calculated once and used until the next uCode bootload. + * + * + * 8) If setting up txpowers for MIMO rates (rate indexes 8-15, 24-31), + * adjust txpower for each transmitter chain, so txpower is balanced + * between the two chains. There are 5 pairs of tx_atten[group][chain] + * values in "initialize alive", one pair for each of 5 channel ranges: + * + * Group 0: 5 GHz channel 34-43 + * Group 1: 5 GHz channel 44-70 + * Group 2: 5 GHz channel 71-124 + * Group 3: 5 GHz channel 125-200 + * Group 4: 2.4 GHz all channels + * + * Add the tx_atten[group][chain] value to the index for the target chain. + * The values are signed, but are in pairs of 0 and a non-negative number, + * so as to reduce gain (if necessary) of the "hotter" channel. This + * avoids any need to double-check for regulatory compliance after + * this step. + * + * + * 9) If setting up for a CCK rate, lower the gain by adding a CCK compensation + * value to the index: + * + * Hardware rev B: 9 steps (4.5 dB) + * Hardware rev C: 5 steps (2.5 dB) + * + * Hardware rev for 4965 can be determined by reading CSR_HW_REV_WA_REG, + * bits [3:2], 1 = B, 2 = C. + * + * NOTE: This compensation is in addition to any saturation backoff that + * might have been applied in an earlier step. + * + * + * 10) Select the gain table, based on band (2.4 vs 5 GHz). + * + * Limit the adjusted index to stay within the table! + * + * + * 11) Read gain table entries for DSP and radio gain, place into appropriate + * location(s) in command (struct iwl4965_txpowertable_cmd). + */ + +/* Limit range of txpower output target to be between these values */ #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) +/** + * When MIMO is used (2 transmitters operating simultaneously), driver should + * limit each transmitter to deliver a max of 3 dB below the regulatory limit + * for the device. That is, use half power for each transmitter, so total + * txpower is within regulatory limits. + * + * The value "6" represents number of steps in gain table to reduce power 3 dB. + * Each step is 1/2 dB. + */ +#define IWL_TX_POWER_MIMO_REGULATORY_COMPENSATION (6) +/** + * CCK gain compensation. + * + * When calculating txpowers for CCK, after making sure that the target power + * is within regulatory and saturation limits, driver must additionally + * back off gain by adding these values to the gain table index. + * + * Hardware rev for 4965 can be determined by reading CSR_HW_REV_WA_REG, + * bits [3:2], 1 = B, 2 = C. + */ +#define IWL_TX_POWER_CCK_COMPENSATION_B_STEP (9) +#define IWL_TX_POWER_CCK_COMPENSATION_C_STEP (5) + +/* + * 4965 power supply voltage compensation for txpower + */ +#define TX_POWER_IWL_VOLTAGE_CODES_PER_03V (7) + +/** + * Gain tables. + * + * The following tables contain pair of values for setting txpower, i.e. + * gain settings for the output of the device's digital signal processor (DSP), + * and for the analog gain structure of the transmitter. + * + * Each entry in the gain tables represents a step of 1/2 dB. Note that these + * are *relative* steps, not indications of absolute output power. Output + * power varies with temperature, voltage, and channel frequency, and also + * requires consideration of average power (to satisfy regulatory constraints), + * and peak power (to avoid distortion of the output signal). + * + * Each entry contains two values: + * 1) DSP gain (or sometimes called DSP attenuation). This is a fine-grained + * linear value that multiplies the output of the digital signal processor, + * before being sent to the analog radio. + * 2) Radio gain. This sets the analog gain of the radio Tx path. + * It is a coarser setting, and behaves in a logarithmic (dB) fashion. + * + * EEPROM contains factory calibration data for txpower. This maps actual + * measured txpower levels to gain settings in the "well known" tables + * below ("well-known" means here that both factory calibration *and* the + * driver work with the same table). + * + * There are separate tables for 2.4 GHz and 5 GHz bands. The 5 GHz table + * has an extension (into negative indexes), in case the driver needs to + * boost power setting for high device temperatures (higher than would be + * present during factory calibration). A 5 Ghz EEPROM index of "40" + * corresponds to the 49th entry in the table used by the driver. + */ +#define MIN_TX_GAIN_INDEX (0) /* highest gain, lowest idx, 2.4 */ +#define MIN_TX_GAIN_INDEX_52GHZ_EXT (-9) /* highest gain, lowest idx, 5 */ + +/** + * 2.4 GHz gain table + * + * Index Dsp gain Radio gain + * 0 110 0x3f (highest gain) + * 1 104 0x3f + * 2 98 0x3f + * 3 110 0x3e + * 4 104 0x3e + * 5 98 0x3e + * 6 110 0x3d + * 7 104 0x3d + * 8 98 0x3d + * 9 110 0x3c + * 10 104 0x3c + * 11 98 0x3c + * 12 110 0x3b + * 13 104 0x3b + * 14 98 0x3b + * 15 110 0x3a + * 16 104 0x3a + * 17 98 0x3a + * 18 110 0x39 + * 19 104 0x39 + * 20 98 0x39 + * 21 110 0x38 + * 22 104 0x38 + * 23 98 0x38 + * 24 110 0x37 + * 25 104 0x37 + * 26 98 0x37 + * 27 110 0x36 + * 28 104 0x36 + * 29 98 0x36 + * 30 110 0x35 + * 31 104 0x35 + * 32 98 0x35 + * 33 110 0x34 + * 34 104 0x34 + * 35 98 0x34 + * 36 110 0x33 + * 37 104 0x33 + * 38 98 0x33 + * 39 110 0x32 + * 40 104 0x32 + * 41 98 0x32 + * 42 110 0x31 + * 43 104 0x31 + * 44 98 0x31 + * 45 110 0x30 + * 46 104 0x30 + * 47 98 0x30 + * 48 110 0x6 + * 49 104 0x6 + * 50 98 0x6 + * 51 110 0x5 + * 52 104 0x5 + * 53 98 0x5 + * 54 110 0x4 + * 55 104 0x4 + * 56 98 0x4 + * 57 110 0x3 + * 58 104 0x3 + * 59 98 0x3 + * 60 110 0x2 + * 61 104 0x2 + * 62 98 0x2 + * 63 110 0x1 + * 64 104 0x1 + * 65 98 0x1 + * 66 110 0x0 + * 67 104 0x0 + * 68 98 0x0 + * 69 97 0 + * 70 96 0 + * 71 95 0 + * 72 94 0 + * 73 93 0 + * 74 92 0 + * 75 91 0 + * 76 90 0 + * 77 89 0 + * 78 88 0 + * 79 87 0 + * 80 86 0 + * 81 85 0 + * 82 84 0 + * 83 83 0 + * 84 82 0 + * 85 81 0 + * 86 80 0 + * 87 79 0 + * 88 78 0 + * 89 77 0 + * 90 76 0 + * 91 75 0 + * 92 74 0 + * 93 73 0 + * 94 72 0 + * 95 71 0 + * 96 70 0 + * 97 69 0 + * 98 68 0 + */ + +/** + * 5 GHz gain table + * + * Index Dsp gain Radio gain + * -9 123 0x3F (highest gain) + * -8 117 0x3F + * -7 110 0x3F + * -6 104 0x3F + * -5 98 0x3F + * -4 110 0x3E + * -3 104 0x3E + * -2 98 0x3E + * -1 110 0x3D + * 0 104 0x3D + * 1 98 0x3D + * 2 110 0x3C + * 3 104 0x3C + * 4 98 0x3C + * 5 110 0x3B + * 6 104 0x3B + * 7 98 0x3B + * 8 110 0x3A + * 9 104 0x3A + * 10 98 0x3A + * 11 110 0x39 + * 12 104 0x39 + * 13 98 0x39 + * 14 110 0x38 + * 15 104 0x38 + * 16 98 0x38 + * 17 110 0x37 + * 18 104 0x37 + * 19 98 0x37 + * 20 110 0x36 + * 21 104 0x36 + * 22 98 0x36 + * 23 110 0x35 + * 24 104 0x35 + * 25 98 0x35 + * 26 110 0x34 + * 27 104 0x34 + * 28 98 0x34 + * 29 110 0x33 + * 30 104 0x33 + * 31 98 0x33 + * 32 110 0x32 + * 33 104 0x32 + * 34 98 0x32 + * 35 110 0x31 + * 36 104 0x31 + * 37 98 0x31 + * 38 110 0x30 + * 39 104 0x30 + * 40 98 0x30 + * 41 110 0x25 + * 42 104 0x25 + * 43 98 0x25 + * 44 110 0x24 + * 45 104 0x24 + * 46 98 0x24 + * 47 110 0x23 + * 48 104 0x23 + * 49 98 0x23 + * 50 110 0x22 + * 51 104 0x18 + * 52 98 0x18 + * 53 110 0x17 + * 54 104 0x17 + * 55 98 0x17 + * 56 110 0x16 + * 57 104 0x16 + * 58 98 0x16 + * 59 110 0x15 + * 60 104 0x15 + * 61 98 0x15 + * 62 110 0x14 + * 63 104 0x14 + * 64 98 0x14 + * 65 110 0x13 + * 66 104 0x13 + * 67 98 0x13 + * 68 110 0x12 + * 69 104 0x08 + * 70 98 0x08 + * 71 110 0x07 + * 72 104 0x07 + * 73 98 0x07 + * 74 110 0x06 + * 75 104 0x06 + * 76 98 0x06 + * 77 110 0x05 + * 78 104 0x05 + * 79 98 0x05 + * 80 110 0x04 + * 81 104 0x04 + * 82 98 0x04 + * 83 110 0x03 + * 84 104 0x03 + * 85 98 0x03 + * 86 110 0x02 + * 87 104 0x02 + * 88 98 0x02 + * 89 110 0x01 + * 90 104 0x01 + * 91 98 0x01 + * 92 110 0x00 + * 93 104 0x00 + * 94 98 0x00 + * 95 93 0x00 + * 96 88 0x00 + * 97 83 0x00 + * 98 78 0x00 + */ + + +/** + * Sanity checks and default values for EEPROM regulatory levels. + * If EEPROM values fall outside MIN/MAX range, use default values. + * + * Regulatory limits refer to the maximum average txpower allowed by + * regulatory agencies in the geographies in which the device is meant + * to be operated. These limits are SKU-specific (i.e. geography-specific), + * and channel-specific; each channel has an individual regulatory limit + * listed in the EEPROM. + * + * Units are in half-dBm (i.e. "34" means 17 dBm). + */ #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) + +/** + * Sanity checks and default values for EEPROM saturation levels. + * If EEPROM values fall outside MIN/MAX range, use default values. + * + * Saturation is the highest level that the output power amplifier can produce + * without significant clipping distortion. This is a "peak" power level. + * Different types of modulation (i.e. various "rates", and OFDM vs. CCK) + * require differing amounts of backoff, relative to their average power output, + * in order to avoid clipping distortion. + * + * Driver must make sure that it is violating neither the saturation limit, + * nor the regulatory limit, when calculating Tx power settings for various + * rates. + * + * Units are in half-dBm (i.e. "38" means 19 dBm). + */ #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 */ +/** + * Channel groups used for Tx Attenuation calibration (MIMO tx channel balance) + * and thermal Txpower calibration. + * + * When calculating txpower, driver must compensate for current device + * temperature; higher temperature requires higher gain. Driver must calculate + * current temperature (see "4965 temperature calculation"), then compare vs. + * factory calibration temperature in EEPROM; if current temperature is higher + * than factory temperature, driver must *increase* gain by proportions shown + * in table below. If current temperature is lower than factory, driver must + * *decrease* gain. + * + * Different frequency ranges require different compensation, as shown below. + */ +/* Group 0, 5.2 GHz ch 34-43: 4.5 degrees per 1/2 dB. */ #define CALIB_IWL_TX_ATTEN_GR1_FCH 34 #define CALIB_IWL_TX_ATTEN_GR1_LCH 43 + +/* Group 1, 5.3 GHz ch 44-70: 4.0 degrees per 1/2 dB. */ #define CALIB_IWL_TX_ATTEN_GR2_FCH 44 #define CALIB_IWL_TX_ATTEN_GR2_LCH 70 + +/* Group 2, 5.5 GHz ch 71-124: 4.0 degrees per 1/2 dB. */ #define CALIB_IWL_TX_ATTEN_GR3_FCH 71 #define CALIB_IWL_TX_ATTEN_GR3_LCH 124 + +/* Group 3, 5.7 GHz ch 125-200: 4.0 degrees per 1/2 dB. */ #define CALIB_IWL_TX_ATTEN_GR4_FCH 125 #define CALIB_IWL_TX_ATTEN_GR4_LCH 200 + +/* Group 4, 2.4 GHz all channels: 3.5 degrees per 1/2 dB. */ #define CALIB_IWL_TX_ATTEN_GR5_FCH 1 #define CALIB_IWL_TX_ATTEN_GR5_LCH 20 -struct tx_power_dual_stream { - __le16 ramon_tx_gain; - __le16 dsp_predis_atten; -} __attribute__ ((packed)); - -union tx_power_dual_stream_u { - struct tx_power_dual_stream s; - __le32 dw; -} __attribute__ ((packed)); - -struct iwl_tx_power_db { - union tx_power_dual_stream_u - ht_ofdm_power[POWER_TABLE_NUM_HT_OFDM_ENTRIES]; - union tx_power_dual_stream_u legacy_cck_power; - -} __attribute__ ((packed)); - -struct iwl_tx_power_table_cmd { - u8 band; - u8 channel_normal_width; - __le16 channel; - struct iwl_tx_power_db tx_power; -} __attribute__ ((packed)); +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 +}; /********************* END TXPOWER *****************************************/ -/* HT flags */ -#define RXON_FLG_CONTROL_CHANNEL_LOCATION_MSK 0x400000 -#define RXON_FLG_CONTROL_CHANNEL_LOC_LOW_MSK 0x000000 -#define RXON_FLG_CONTROL_CHANNEL_LOC_HIGH_MSK 0x400000 - -#define RXON_FLG_HT_OPERATING_MODE_POS (23) -/*yshevet - bug fix */ -#define RXON_FLG_HT_PROT_MSK 0x800000 -#define RXON_FLG_FAT_PROT_MSK 0x1000000 - -#define RXON_FLG_CHANNEL_MODE_POS (25) -#define RXON_FLG_CHANNEL_MODE_MSK 0x06000000 -#define RXON_FLG_CHANNEL_MODE_LEGACY_MSK 0x00000000 -#define RXON_FLG_CHANNEL_MODE_PURE_40_MSK 0x02000000 -#define RXON_FLG_CHANNEL_MODE_MIXED_MSK 0x04000000 - -#define RXON_RX_CHAIN_DRIVER_FORCE_MSK (0x1<<0) -#define RXON_RX_CHAIN_VALID_MSK (0x7<<1) -#define RXON_RX_CHAIN_VALID_POS (1) -#define RXON_RX_CHAIN_FORCE_SEL_MSK (0x7<<4) -#define RXON_RX_CHAIN_FORCE_SEL_POS (4) -#define RXON_RX_CHAIN_FORCE_MIMO_SEL_MSK (0x7<<7) -#define RXON_RX_CHAIN_FORCE_MIMO_SEL_POS (7) -#define RXON_RX_CHAIN_CNT_MSK (0x3<<10) -#define RXON_RX_CHAIN_CNT_POS (10) -#define RXON_RX_CHAIN_MIMO_CNT_MSK (0x3<<12) -#define RXON_RX_CHAIN_MIMO_CNT_POS (12) -#define RXON_RX_CHAIN_MIMO_FORCE_MSK (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)) - -/*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)); - -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 iwl_rate rate_scale_table[LINK_QUAL_MAX_RETRY_NUM]; - __le32 reserved2; -} __attribute__ ((packed)); - -#define STA_FLG_PWR_SAVE_MSK 0x100 - +/****************************/ /* Flow Handler Definitions */ +/****************************/ -/**********************/ -/* Addresses */ -/**********************/ - +/** + * This I/O area is directly read/writable by driver (e.g. Linux uses writel()) + * Addresses are offsets from device's PCI hardware base address. + */ #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) - +/** + * Keep-Warm (KW) buffer base address. + * + * Driver must allocate a 4KByte buffer that is used by 4965 for keeping the + * host DRAM powered on (via dummy accesses to DRAM) to maintain low-latency + * DRAM access when 4965 is Txing or Rxing. The dummy accesses prevent host + * from going into a power-savings mode that would cause higher DRAM latency, + * and possible data over/under-runs, before all Tx/Rx is complete. + * + * Driver loads IWL_FH_KW_MEM_ADDR_REG with the physical address (bits 35:4) + * of the buffer, which must be 4K aligned. Once this is set up, the 4965 + * automatically invokes keep-warm accesses when normal accesses might not + * be sufficient to maintain fast DRAM response. + * + * Bit fields: + * 31-0: Keep-warm buffer physical base address [35:4], must be 4K aligned + */ #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 */ +/** + * TFD Circular Buffers Base (CBBC) addresses + * + * 4965 has 16 base pointer registers, one for each of 16 host-DRAM-resident + * circular buffers (CBs/queues) containing Transmit Frame Descriptors (TFDs) + * (see struct iwl_tfd_frame). These 16 pointer registers are offset by 0x04 + * bytes from one another. Each TFD circular buffer in DRAM must be 256-byte + * aligned (address bits 0-7 must be 0). + * + * Bit fields in each pointer register: + * 27-0: TFD CB physical base address [35:8], must be 256-byte aligned + */ #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) +/* Find TFD CB base pointer for given queue (range 0-15). */ +#define FH_MEM_CBBC_QUEUE(x) (FH_MEM_CBBC_LOWER_BOUND + (x) * 0x4) -/* 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 */ +/** + * Rx SRAM Control and Status Registers (RSCSR) + * + * These registers provide handshake between driver and 4965 for the Rx queue + * (this queue handles *all* command responses, notifications, Rx data, etc. + * sent from 4965 uCode to host driver). Unlike Tx, there is only one Rx + * queue, and only one Rx DMA/FIFO channel. Also unlike Tx, which can + * concatenate up to 20 DRAM buffers to form a Tx frame, each Receive Buffer + * Descriptor (RBD) points to only one Rx Buffer (RB); there is a 1:1 + * mapping between RBDs and RBs. + * + * Driver must allocate host DRAM memory for the following, and set the + * physical address of each into 4965 registers: + * + * 1) Receive Buffer Descriptor (RBD) circular buffer (CB), typically with 256 + * entries (although any power of 2, up to 4096, is selectable by driver). + * Each entry (1 dword) points to a receive buffer (RB) of consistent size + * (typically 4K, although 8K or 16K are also selectable by driver). + * Driver sets up RB size and number of RBDs in the CB via Rx config + * register FH_MEM_RCSR_CHNL0_CONFIG_REG. + * + * Bit fields within one RBD: + * 27-0: Receive Buffer physical address bits [35:8], 256-byte aligned + * + * Driver sets physical address [35:8] of base of RBD circular buffer + * into FH_RSCSR_CHNL0_RBDCB_BASE_REG [27:0]. + * + * 2) Rx status buffer, 8 bytes, in which 4965 indicates which Rx Buffers + * (RBs) have been filled, via a "write pointer", actually the index of + * the RB's corresponding RBD within the circular buffer. Driver sets + * physical address [35:4] into FH_RSCSR_CHNL0_STTS_WPTR_REG [31:0]. + * + * Bit fields in lower dword of Rx status buffer (upper dword not used + * by driver; see struct iwl4965_shared, val0): + * 31-12: Not used by driver + * 11- 0: Index of last filled Rx buffer descriptor + * (4965 writes, driver reads this value) + * + * As the driver prepares Receive Buffers (RBs) for 4965 to fill, driver must + * enter pointers to these RBs into contiguous RBD circular buffer entries, + * and update the 4965's "write" index register, FH_RSCSR_CHNL0_RBDCB_WPTR_REG. + * + * This "write" index corresponds to the *next* RBD that the driver will make + * available, i.e. one RBD past the tail of the ready-to-fill RBDs within + * the circular buffer. This value should initially be 0 (before preparing any + * RBs), should be 8 after preparing the first 8 RBs (for example), and must + * wrap back to 0 at the end of the circular buffer (but don't wrap before + * "read" index has advanced past 1! See below). + * NOTE: 4965 EXPECTS THE WRITE INDEX TO BE INCREMENTED IN MULTIPLES OF 8. + * + * As the 4965 fills RBs (referenced from contiguous RBDs within the circular + * buffer), it updates the Rx status buffer in host DRAM, 2) described above, + * to tell the driver the index of the latest filled RBD. The driver must + * read this "read" index from DRAM after receiving an Rx interrupt from 4965. + * + * The driver must also internally keep track of a third index, which is the + * next RBD to process. When receiving an Rx interrupt, driver should process + * all filled but unprocessed RBs up to, but not including, the RB + * corresponding to the "read" index. For example, if "read" index becomes "1", + * driver may process the RB pointed to by RBD 0. Depending on volume of + * traffic, there may be many RBs to process. + * + * If read index == write index, 4965 thinks there is no room to put new data. + * Due to this, the maximum number of filled RBs is 255, instead of 256. To + * be safe, make sure that there is a gap of at least 2 RBDs between "write" + * and "read" indexes; that is, make sure that there are no more than 254 + * buffers waiting to be filled. + */ #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*/ +/** + * Physical base address of 8-byte Rx Status buffer. + * Bit fields: + * 31-0: Rx status buffer physical base address [35:4], must 16-byte aligned. + */ #define FH_RSCSR_CHNL0_STTS_WPTR_REG (FH_MEM_RSCSR_CHNL0) + +/** + * Physical base address of Rx Buffer Descriptor Circular Buffer. + * Bit fields: + * 27-0: RBD CD physical base address [35:8], must be 256-byte aligned. + */ #define FH_RSCSR_CHNL0_RBDCB_BASE_REG (FH_MEM_RSCSR_CHNL0 + 0x004) + +/** + * Rx write pointer (index, really!). + * Bit fields: + * 11-0: Index of driver's most recent prepared-to-be-filled RBD, + 1. + * NOTE: For 256-entry circular buffer, use only bits [7:0]. + */ #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_CHNL0_WPTR (FH_RSCSR_CHNL0_RBDCB_WPTR_REG) -#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 */ +/** + * Rx Config/Status Registers (RCSR) + * Rx Config Reg for channel 0 (only channel used) + * + * Driver must initialize FH_MEM_RCSR_CHNL0_CONFIG_REG as follows for + * normal operation (see bit fields). + * + * Clearing FH_MEM_RCSR_CHNL0_CONFIG_REG to 0 turns off Rx DMA. + * Driver should poll FH_MEM_RSSR_RX_STATUS_REG for + * FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (bit 24) before continuing. + * + * Bit fields: + * 31-30: Rx DMA channel enable: '00' off/pause, '01' pause at end of frame, + * '10' operate normally + * 29-24: reserved + * 23-20: # RBDs in circular buffer = 2^value; use "8" for 256 RBDs (normal), + * min "5" for 32 RBDs, max "12" for 4096 RBDs. + * 19-18: reserved + * 17-16: size of each receive buffer; '00' 4K (normal), '01' 8K, + * '10' 12K, '11' 16K. + * 15-14: reserved + * 13-12: IRQ destination; '00' none, '01' host driver (normal operation) + * 11- 4: timeout for closing Rx buffer and interrupting host (units 32 usec) + * typical value 0x10 (about 1/2 msec) + * 3- 0: reserved + */ #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 */ +#define FH_RCSR_CHNL0_RX_CONFIG_RB_TIMEOUT_MASK (0x00000FF0) /* bit 4-11 */ +#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_MASK (0x00001000) /* bit 12 */ +#define FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MASK (0x00008000) /* bit 15 */ +#define FH_RCSR_CHNL0_RX_CONFIG_RB_SIZE_MASK (0x00030000) /* bits 16-17 */ +#define FH_RCSR_CHNL0_RX_CONFIG_RBDBC_SIZE_MASK (0x00F00000) /* bits 20-23 */ +#define FH_RCSR_CHNL0_RX_CONFIG_DMA_CHNL_EN_MASK (0xC0000000) /* bits 30-31 */ + +#define FH_RCSR_RX_CONFIG_RBDCB_SIZE_BITSHIFT (20) +#define FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_BITSHIFT (4) +#define RX_RB_TIMEOUT (0x10) -/* 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) +#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_REG_VAL_RB_SIZE_4K (0x00000000) +#define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K (0x00010000) +#define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_12K (0x00020000) +#define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_16K (0x00030000) -/* RSSR Area - Rx shared ctrl & status registers */ +#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_NO_INT_VAL (0x00000000) +#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL (0x00001000) + + +/** + * Rx Shared Status Registers (RSSR) + * + * After stopping Rx DMA channel (writing 0 to FH_MEM_RCSR_CHNL0_CONFIG_REG), + * driver must poll FH_MEM_RSSR_RX_STATUS_REG until Rx channel is idle. + * + * Bit fields: + * 24: 1 = Channel 0 is idle + * + * FH_MEM_RSSR_SHARED_CTRL_REG and FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV contain + * default values that should not be altered by the driver. + */ #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 FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (0x01000000) + -#define IWL_FH_TCSR_CHNL_NUM (7) +/** + * Transmit DMA Channel Control/Status Registers (TCSR) + * + * 4965 has one configuration register for each of 8 Tx DMA/FIFO channels + * supported in hardware (don't confuse these with the 16 Tx queues in DRAM, + * which feed the DMA/FIFO channels); config regs are separated by 0x20 bytes. + * + * To use a Tx DMA channel, driver must initialize its + * IWL_FH_TCSR_CHNL_TX_CONFIG_REG(chnl) with: + * + * IWL_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | + * IWL_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL + * + * All other bits should be 0. + * + * Bit fields: + * 31-30: Tx DMA channel enable: '00' off/pause, '01' pause at end of frame, + * '10' operate normally + * 29- 4: Reserved, set to "0" + * 3: Enable internal DMA requests (1, normal operation), disable (0) + * 2- 0: Reserved, set to "0" + */ +#define IWL_FH_TCSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xD00) +#define IWL_FH_TCSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xE60) + +/* Find Control/Status reg for given Tx DMA/FIFO channel */ #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_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_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_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_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) +/** + * Tx Shared Status Registers (TSSR) + * + * After stopping Tx DMA channel (writing 0 to + * IWL_FH_TCSR_CHNL_TX_CONFIG_REG(chnl)), driver must poll + * IWL_FH_TSSR_TX_STATUS_REG until selected Tx channel is idle + * (channel's buffers empty | no pending requests). + * + * Bit fields: + * 31-24: 1 = Channel buffers empty (channel 7:0) + * 23-16: 1 = No pending requests (channel 7:0) + */ +#define IWL_FH_TSSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xEA0) +#define IWL_FH_TSSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xEC0) -#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 (IWL_FH_TSSR_LOWER_BOUND + 0x010) #define IWL_FH_TSSR_TX_STATUS_REG_BIT_BUFS_EMPTY(_chnl) \ ((1 << (_chnl)) << 24) @@ -487,327 +1558,347 @@ struct iwl_link_quality_cmd { (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) +/********************* START TX SCHEDULER *************************************/ +/** + * 4965 Tx Scheduler + * + * The Tx Scheduler selects the next frame to be transmitted, chosing TFDs + * (Transmit Frame Descriptors) from up to 16 circular Tx queues resident in + * host DRAM. It steers each frame's Tx command (which contains the frame + * data) into one of up to 7 prioritized Tx DMA FIFO channels within the + * device. A queue maps to only one (selectable by driver) Tx DMA channel, + * but one DMA channel may take input from several queues. + * + * Tx DMA channels have dedicated purposes. For 4965, they are used as follows: + * + * 0 -- EDCA BK (background) frames, lowest priority + * 1 -- EDCA BE (best effort) frames, normal priority + * 2 -- EDCA VI (video) frames, higher priority + * 3 -- EDCA VO (voice) and management frames, highest priority + * 4 -- Commands (e.g. RXON, etc.) + * 5 -- HCCA short frames + * 6 -- HCCA long frames + * 7 -- not used by driver (device-internal only) + * + * Driver should normally map queues 0-6 to Tx DMA/FIFO channels 0-6. + * In addition, driver can map queues 7-15 to Tx DMA/FIFO channels 0-3 to + * support 11n aggregation via EDCA DMA channels. + * + * The driver sets up each queue to work in one of two modes: + * + * 1) Scheduler-Ack, in which the scheduler automatically supports a + * block-ack (BA) window of up to 64 TFDs. In this mode, each queue + * contains TFDs for a unique combination of Recipient Address (RA) + * and Traffic Identifier (TID), that is, traffic of a given + * Quality-Of-Service (QOS) priority, destined for a single station. + * + * In scheduler-ack mode, the scheduler keeps track of the Tx status of + * each frame within the BA window, including whether it's been transmitted, + * and whether it's been acknowledged by the receiving station. The device + * automatically processes block-acks received from the receiving STA, + * and reschedules un-acked frames to be retransmitted (successful + * Tx completion may end up being out-of-order). + * + * The driver must maintain the queue's Byte Count table in host DRAM + * (struct iwl4965_sched_queue_byte_cnt_tbl) for this mode. + * This mode does not support fragmentation. + * + * 2) FIFO (a.k.a. non-Scheduler-ACK), in which each TFD is processed in order. + * The device may automatically retry Tx, but will retry only one frame + * at a time, until receiving ACK from receiving station, or reaching + * retry limit and giving up. + * + * The command queue (#4) must use this mode! + * This mode does not require use of the Byte Count table in host DRAM. + * + * Driver controls scheduler operation via 3 means: + * 1) Scheduler registers + * 2) Shared scheduler data base in internal 4956 SRAM + * 3) Shared data in host DRAM + * + * Initialization: + * + * When loading, driver should allocate memory for: + * 1) 16 TFD circular buffers, each with space for (typically) 256 TFDs. + * 2) 16 Byte Count circular buffers in 16 KBytes contiguous memory + * (1024 bytes for each queue). + * + * After receiving "Alive" response from uCode, driver must initialize + * the scheduler (especially for queue #4, the command queue, otherwise + * the driver can't issue commands!): + */ + +/** + * Max Tx window size is the max number of contiguous TFDs that the scheduler + * can keep track of at one time when creating block-ack chains of frames. + * Note that "64" matches the number of ack bits in a block-ack packet. + * Driver should use SCD_WIN_SIZE and SCD_FRAME_LIMIT values to initialize + * SCD_CONTEXT_QUEUE_OFFSET(x) values. + */ #define SCD_WIN_SIZE 64 -#define SCD_FRAME_LIMIT 10 +#define SCD_FRAME_LIMIT 64 -/* memory mapped registers */ +/* SCD registers are internal, must be accessed via HBUS_TARG_PRPH regs */ #define SCD_START_OFFSET 0xa02c00 +/* + * 4965 tells driver SRAM address for internal scheduler structs via this reg. + * Value is valid only after "Alive" response from uCode. + */ #define SCD_SRAM_BASE_ADDR (SCD_START_OFFSET + 0x0) + +/* + * Driver may need to update queue-empty bits after changing queue's + * write and read pointers (indexes) during (re-)initialization (i.e. when + * scheduler is not tracking what's happening). + * Bit fields: + * 31-16: Write mask -- 1: update empty bit, 0: don't change empty bit + * 15-00: Empty state, one for each queue -- 1: empty, 0: non-empty + * NOTE: This register is not used by Linux driver. + */ #define SCD_EMPTY_BITS (SCD_START_OFFSET + 0x4) + +/* + * Physical base address of array of byte count (BC) circular buffers (CBs). + * Each Tx queue has a BC CB in host DRAM to support Scheduler-ACK mode. + * This register points to BC CB for queue 0, must be on 1024-byte boundary. + * Others are spaced by 1024 bytes. + * Each BC CB is 2 bytes * (256 + 64) = 740 bytes, followed by 384 bytes pad. + * (Index into a queue's BC CB) = (index into queue's TFD CB) = (SSN & 0xff). + * Bit fields: + * 25-00: Byte Count CB physical address [35:10], must be 1024-byte aligned. + */ #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) +/* + * Enables any/all Tx DMA/FIFO channels. + * Scheduler generates requests for only the active channels. + * Set this to 0xff to enable all 8 channels (normal usage). + * Bit fields: + * 7- 0: Enable (1), disable (0), one bit for each channel 0-7 + */ +#define SCD_TXFACT (SCD_START_OFFSET + 0x1c) +/* Mask to enable contiguous Tx DMA/FIFO channels between "lo" and "hi". */ #define SCD_TXFACT_REG_TXFIFO_MASK(lo, hi) \ ((1<<(hi))|((1<<(hi))-(1<<(lo)))) +/* + * Queue (x) Write Pointers (indexes, really!), one for each Tx queue. + * Initialized and updated by driver as new TFDs are added to queue. + * NOTE: If using Block Ack, index must correspond to frame's + * Start Sequence Number; index = (SSN & 0xff) + * NOTE: Alternative to HBUS_TARG_WRPTR, which is what Linux driver uses? + */ +#define SCD_QUEUE_WRPTR(x) (SCD_START_OFFSET + 0x24 + (x) * 4) -#define SCD_MODE_REG_BIT_SEARCH_MODE (1<<0) -#define SCD_MODE_REG_BIT_SBYP_MODE (1<<1) +/* + * Queue (x) Read Pointers (indexes, really!), one for each Tx queue. + * For FIFO mode, index indicates next frame to transmit. + * For Scheduler-ACK mode, index indicates first frame in Tx window. + * Initialized by driver, updated by scheduler. + */ +#define SCD_QUEUE_RDPTR(x) (SCD_START_OFFSET + 0x64 + (x) * 4) -#define SCD_TXFIFO_POS_TID (0) -#define SCD_TXFIFO_POS_RA (4) +/* + * Select which queues work in chain mode (1) vs. not (0). + * Use chain mode to build chains of aggregated frames. + * Bit fields: + * 31-16: Reserved + * 15-00: Mode, one bit for each queue -- 1: Chain mode, 0: one-at-a-time + * NOTE: If driver sets up queue for chain mode, it should be also set up + * Scheduler-ACK mode as well, via SCD_QUEUE_STATUS_BITS(x). + */ +#define SCD_QUEUECHAIN_SEL (SCD_START_OFFSET + 0xd0) + +/* + * Select which queues interrupt driver when scheduler increments + * a queue's read pointer (index). + * Bit fields: + * 31-16: Reserved + * 15-00: Interrupt enable, one bit for each queue -- 1: enabled, 0: disabled + * NOTE: This functionality is apparently a no-op; driver relies on interrupts + * from Rx queue to read Tx command responses and update Tx queues. + */ +#define SCD_INTERRUPT_MASK (SCD_START_OFFSET + 0xe4) + +/* + * Queue search status registers. One for each queue. + * Sets up queue mode and assigns queue to Tx DMA channel. + * Bit fields: + * 19-10: Write mask/enable bits for bits 0-9 + * 9: Driver should init to "0" + * 8: Scheduler-ACK mode (1), non-Scheduler-ACK (i.e. FIFO) mode (0). + * Driver should init to "1" for aggregation mode, or "0" otherwise. + * 7-6: Driver should init to "0" + * 5: Window Size Left; indicates whether scheduler can request + * another TFD, based on window size, etc. Driver should init + * this bit to "1" for aggregation mode, or "0" for non-agg. + * 4-1: Tx FIFO to use (range 0-7). + * 0: Queue is active (1), not active (0). + * Other bits should be written as "0" + * + * NOTE: If enabling Scheduler-ACK mode, chain mode should also be enabled + * via SCD_QUEUECHAIN_SEL. + */ +#define SCD_QUEUE_STATUS_BITS(x) (SCD_START_OFFSET + 0x104 + (x) * 4) + +/* Bit field positions */ #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) + +/* Write masks */ #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) +/** + * 4965 internal SRAM structures for scheduler, shared with driver ... + * + * Driver should clear and initialize the following areas after receiving + * "Alive" response from 4965 uCode, i.e. after initial + * uCode load, or after a uCode load done for error recovery: + * + * SCD_CONTEXT_DATA_OFFSET (size 128 bytes) + * SCD_TX_STTS_BITMAP_OFFSET (size 256 bytes) + * SCD_TRANSLATE_TBL_OFFSET (size 32 bytes) + * + * Driver accesses SRAM via HBUS_TARG_MEM_* registers. + * Driver reads base address of this scheduler area from SCD_SRAM_BASE_ADDR. + * All OFFSET values must be added to this base address. + */ + +/* + * Queue context. One 8-byte entry for each of 16 queues. + * + * Driver should clear this entire area (size 0x80) to 0 after receiving + * "Alive" notification from uCode. Additionally, driver should init + * each queue's entry as follows: + * + * LS Dword bit fields: + * 0-06: Max Tx window size for Scheduler-ACK. Driver should init to 64. + * + * MS Dword bit fields: + * 16-22: Frame limit. Driver should init to 10 (0xa). + * + * Driver should init all other bits to 0. + * + * Init must be done after driver receives "Alive" response from 4965 uCode, + * and when setting up queue for aggregation. + */ +#define SCD_CONTEXT_DATA_OFFSET 0x380 +#define SCD_CONTEXT_QUEUE_OFFSET(x) (SCD_CONTEXT_DATA_OFFSET + ((x) * 8)) #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 (0x1) -#define STATISTICS_REPLY_FLG_BAND_24G_MSK (0x2) -#define STATISTICS_REPLY_FLG_TGJ_NARROW_BAND_MSK (0x4) -#define STATISTICS_REPLY_FLG_FAT_MODE_MSK (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; - struct iwl_rate rate; /* rate in ucode internal format */ - __le16 byte_count; /* frame's byte-count */ - __le16 reserved3; -} __attribute__ ((packed)); +/* + * Tx Status Bitmap + * + * Driver should clear this entire area (size 0x100) to 0 after receiving + * "Alive" notification from uCode. Area is used only by device itself; + * no other support (besides clearing) is required from driver. + */ +#define SCD_TX_STTS_BITMAP_OFFSET 0x400 -struct iwl4965_rx_mpdu_res_start { - __le16 byte_count; - __le16 reserved; -} __attribute__ ((packed)); +/* + * RAxTID to queue translation mapping. + * + * When queue is in Scheduler-ACK mode, frames placed in a that queue must be + * for only one combination of receiver address (RA) and traffic ID (TID), i.e. + * one QOS priority level destined for one station (for this wireless link, + * not final destination). The SCD_TRANSLATE_TABLE area provides 16 16-bit + * mappings, one for each of the 16 queues. If queue is not in Scheduler-ACK + * mode, the device ignores the mapping value. + * + * Bit fields, for each 16-bit map: + * 15-9: Reserved, set to 0 + * 8-4: Index into device's station table for recipient station + * 3-0: Traffic ID (tid), range 0-15 + * + * Driver should clear this entire area (size 32 bytes) to 0 after receiving + * "Alive" notification from uCode. To update a 16-bit map value, driver + * must read a dword-aligned value from device SRAM, replace the 16-bit map + * value of interest, and write the dword value back into device SRAM. + */ +#define SCD_TRANSLATE_TBL_OFFSET 0x500 -#if 0 +/* Find translation table dword to read/write for given queue */ +#define SCD_TRANSLATE_TBL_OFFSET_QUEUE(x) \ + ((SCD_TRANSLATE_TBL_OFFSET + ((x) * 2)) & 0xfffffffc) -union iwl_dram_scratch_union { - struct iwl_dram_scratch s; - __le32 dw; -}; +#define SCD_TXFIFO_POS_TID (0) +#define SCD_TXFIFO_POS_RA (4) +#define SCD_QUEUE_RA_TID_MAP_RATID_MSK (0x01FF) -struct iwl4965_beacon_notify { - struct iwl_tx_resp beacon_notify_hdr; /*15:4 */ - __le32 low_tsf; /*19:16 */ - __le32 high_tsf; /*23:20 */ - __le32 ibss_mgr_status; /*27:24 */ -} __attribute__ ((packed)); +/*********************** END TX SCHEDULER *************************************/ -struct iwl4965_tx_beacon_cmd { - struct iwl_tx_cmd tx; /*byte 55:4 */ - __le16 tim_idx; /*byte 57:56 */ - u8 tim_size; /*byte 58 */ - u8 reserved1; /*byte 59 */ - struct ieee80211_hdr frame[0]; - /* Beacon Frame */ -} __attribute__ ((packed)); +static inline u8 iwl4965_hw_get_rate(__le32 rate_n_flags) +{ + return le32_to_cpu(rate_n_flags) & 0xFF; +} +static inline u16 iwl4965_hw_get_rate_n_flags(__le32 rate_n_flags) +{ + return le32_to_cpu(rate_n_flags) & 0xFFFF; +} +static inline __le32 iwl4965_hw_set_rate_n_flags(u8 rate, u16 flags) +{ + return cpu_to_le32(flags|(u16)rate); +} -struct iwl4965_powertable_cmd { - __le16 flags; - u8 keep_alive_seconds; - u8 debug_flags; - __le32 rx_data_timeout; - __le32 tx_data_timeout; - __le32 sleep_interval[PMC_TCMD_SLEEP_INTRVL_TABLE_SIZE]; - __le32 keep_alive_beacons; -} __attribute__ ((packed)); -#define IWL_NUM_OF_STATIONS ( 32 ) -#define BYTE_CNT_AREA_OFFSET 0 +/** + * Tx/Rx Queues + * + * Most communication between driver and 4965 is via queues of data buffers. + * For example, all commands that the driver issues to device's embedded + * controller (uCode) are via the command queue (one of the Tx queues). All + * uCode command responses/replies/notifications, including Rx frames, are + * conveyed from uCode to driver via the Rx queue. + * + * Most support for these queues, including handshake support, resides in + * structures in host DRAM, shared between the driver and the device. When + * allocating this memory, the driver must make sure that data written by + * the host CPU updates DRAM immediately (and does not get "stuck" in CPU's + * cache memory), so DRAM and cache are consistent, and the device can + * immediately see changes made by the driver. + * + * 4965 supports up to 16 DRAM-based Tx queues, and services these queues via + * up to 7 DMA channels (FIFOs). Each Tx queue is supported by a circular array + * in DRAM containing 256 Transmit Frame Descriptors (TFDs). + */ +#define IWL4965_MAX_WIN_SIZE 64 +#define IWL4965_QUEUE_SIZE 256 +#define IWL4965_NUM_FIFOS 7 +#define IWL_MAX_NUM_QUEUES 16 -enum HT_STATUS { - BA_STATUS_FAILURE = 0, - BA_STATUS_INITIATOR_DELBA, - BA_STATUS_RECIPIENT_DELBA, - BA_STATUS_RENEW_ADDBA_REQUEST, - BA_STATUS_ACTIVE, -}; -#endif - -#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 { +/** + * struct iwl4965_tfd_frame_data + * + * Describes up to 2 buffers containing (contiguous) portions of a Tx frame. + * Each buffer must be on dword boundary. + * Up to 10 iwl_tfd_frame_data structures, describing up to 20 buffers, + * may be filled within a TFD (iwl_tfd_frame). + * + * Bit fields in tb1_addr: + * 31- 0: Tx buffer 1 address bits [31:0] + * + * Bit fields in val1: + * 31-16: Tx buffer 2 address bits [15:0] + * 15- 4: Tx buffer 1 length (bytes) + * 3- 0: Tx buffer 1 address bits [32:32] + * + * Bit fields in val2: + * 31-20: Tx buffer 2 length (bytes) + * 19- 0: Tx buffer 2 address bits [35:16] + */ +struct iwl4965_tfd_frame_data { __le32 tb1_addr; __le32 val1; @@ -835,7 +1926,36 @@ struct iwl_tfd_frame_data { #define IWL_tb2_len_SYM val2 } __attribute__ ((packed)); -struct iwl_tfd_frame { + +/** + * struct iwl4965_tfd_frame + * + * Transmit Frame Descriptor (TFD) + * + * 4965 supports up to 16 Tx queues resident in host DRAM. + * Each Tx queue uses a circular buffer of 256 TFDs stored in host DRAM. + * Both driver and device share these circular buffers, each of which must be + * contiguous 256 TFDs x 128 bytes-per-TFD = 32 KBytes for 4965. + * + * Driver must indicate the physical address of the base of each + * circular buffer via the 4965's FH_MEM_CBBC_QUEUE registers. + * + * Each TFD contains pointer/size information for up to 20 data buffers + * in host DRAM. These buffers collectively contain the (one) frame described + * by the TFD. Each buffer must be a single contiguous block of memory within + * itself, but buffers may be scattered in host DRAM. Each buffer has max size + * of (4K - 4). The 4965 concatenates all of a TFD's buffers into a single + * Tx frame, up to 8 KBytes in size. + * + * Bit fields in the control dword (val0): + * 31-30: # dwords (0-3) of padding required at end of frame for 16-byte bound + * 29: reserved + * 28-24: # Transmit Buffer Descriptors in TFD + * 23- 0: reserved + * + * A maximum of 255 (not 256!) TFDs may be on a queue waiting for Tx. + */ +struct iwl4965_tfd_frame { __le32 val0; /* __le32 rsvd1:24; */ /* __le32 num_tbs:5; */ @@ -844,15 +1964,20 @@ struct iwl_tfd_frame { #define IWL_num_tbs_SYM val0 /* __le32 rsvd2:1; */ /* __le32 padding:2; */ - struct iwl_tfd_frame_data pa[10]; + struct iwl4965_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 IWL4965_NUM_QUEUES 16 +/** + * struct iwl4965_queue_byte_cnt_entry + * + * Byte Count Table Entry + * + * Bit fields: + * 15-12: reserved + * 11- 0: total to-be-transmitted byte count of frame (does not include command) + */ struct iwl4965_queue_byte_cnt_entry { __le16 val; /* __le16 byte_cnt:12; */ @@ -862,6 +1987,25 @@ struct iwl4965_queue_byte_cnt_entry { /* __le16 rsvd:4; */ } __attribute__ ((packed)); + +/** + * struct iwl4965_sched_queue_byte_cnt_tbl + * + * Byte Count table + * + * Each Tx queue uses a byte-count table containing 320 entries: + * one 16-bit entry for each of 256 TFDs, plus an additional 64 entries that + * duplicate the first 64 entries (to avoid wrap-around within a Tx window; + * max Tx window is 64 TFDs). + * + * When driver sets up a new TFD, it must also enter the total byte count + * of the frame to be transmitted into the corresponding entry in the byte + * count table for the chosen Tx queue. If the TFD index is 0-63, the driver + * must duplicate the byte count entry in corresponding index 256-319. + * + * "dont_care" padding puts each byte count table on a 1024-byte boundary; + * 4965 assumes tables are separated by 1024 bytes. + */ struct iwl4965_sched_queue_byte_cnt_tbl { struct iwl4965_queue_byte_cnt_entry tfd_offset[IWL4965_QUEUE_SIZE + IWL4965_MAX_WIN_SIZE]; @@ -870,11 +2014,33 @@ struct iwl4965_sched_queue_byte_cnt_tbl { 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_shared - handshake area for Tx and Rx + * + * For convenience in allocating memory, this structure combines 2 areas of + * DRAM which must be shared between driver and 4965. These do not need to + * be combined, if better allocation would result from keeping them separate: + * + * 1) The Tx byte count tables occupy 1024 bytes each (16 KBytes total for + * 16 queues). Driver uses SCD_DRAM_BASE_ADDR to tell 4965 where to find + * the first of these tables. 4965 assumes tables are 1024 bytes apart. + * + * 2) The Rx status (val0 and val1) occupies only 8 bytes. Driver uses + * FH_RSCSR_CHNL0_STTS_WPTR_REG to tell 4965 where to find this area. + * Driver reads val0 to determine the latest Receive Buffer Descriptor (RBD) + * that has been filled by the 4965. + * + * Bit fields val0: + * 31-12: Not used + * 11- 0: Index of last filled Rx buffer descriptor (4965 writes, driver reads) + * + * Bit fields val1: + * 31- 0: Not used + */ +struct iwl4965_shared { struct iwl4965_sched_queue_byte_cnt_tbl - queues_byte_cnt_tbls[IWL4965_NUM_QUEUES]; + queues_byte_cnt_tbls[IWL_MAX_NUM_QUEUES]; __le32 val0; /* __le32 rb_closed_stts_rb_num:12; */ @@ -904,4 +2070,4 @@ struct iwl_shared { __le32 padding2; } __attribute__ ((packed)); -#endif /* __iwl_4965_hw_h__ */ +#endif /* __iwl4965_4965_hw_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-4965-io.h b/drivers/net/wireless/iwlwifi/iwl-4965-io.h new file mode 100644 index 0000000..34a0b57 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-4965-io.h @@ -0,0 +1,431 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2007 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * James P. Ketrenos <ipw2100-admin@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#ifndef __iwl4965_io_h__ +#define __iwl4965_io_h__ + +#include <linux/io.h> + +#include "iwl-4965-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 + * 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 _iwl4965_read_direct32 calls the non-check version of + * _iwl4965_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 _iwl4965_write32(iwl, ofs, val) writel((val), (iwl)->hw_base + (ofs)) +#ifdef CONFIG_IWL4965_DEBUG +static inline void __iwl4965_write32(const char *f, u32 l, struct iwl4965_priv *iwl, + u32 ofs, u32 val) +{ + IWL_DEBUG_IO("write32(0x%08X, 0x%08X) - %s %d\n", ofs, val, f, l); + _iwl4965_write32(iwl, ofs, val); +} +#define iwl4965_write32(iwl, ofs, val) \ + __iwl4965_write32(__FILE__, __LINE__, iwl, ofs, val) +#else +#define iwl4965_write32(iwl, ofs, val) _iwl4965_write32(iwl, ofs, val) +#endif + +#define _iwl4965_read32(iwl, ofs) readl((iwl)->hw_base + (ofs)) +#ifdef CONFIG_IWL4965_DEBUG +static inline u32 __iwl4965_read32(char *f, u32 l, struct iwl4965_priv *iwl, u32 ofs) +{ + IWL_DEBUG_IO("read_direct32(0x%08X) - %s %d\n", ofs, f, l); + return _iwl4965_read32(iwl, ofs); +} +#define iwl4965_read32(iwl, ofs) __iwl4965_read32(__FILE__, __LINE__, iwl, ofs) +#else +#define iwl4965_read32(p, o) _iwl4965_read32(p, o) +#endif + +static inline int _iwl4965_poll_bit(struct iwl4965_priv *priv, u32 addr, + u32 bits, u32 mask, int timeout) +{ + int i = 0; + + do { + if ((_iwl4965_read32(priv, addr) & mask) == (bits & mask)) + return i; + mdelay(10); + i += 10; + } while (i < timeout); + + return -ETIMEDOUT; +} +#ifdef CONFIG_IWL4965_DEBUG +static inline int __iwl4965_poll_bit(const char *f, u32 l, + struct iwl4965_priv *priv, u32 addr, + u32 bits, u32 mask, int timeout) +{ + int ret = _iwl4965_poll_bit(priv, addr, bits, mask, timeout); + if (unlikely(ret == -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, ret, f, l); + return ret; +} +#define iwl4965_poll_bit(iwl, addr, bits, mask, timeout) \ + __iwl4965_poll_bit(__FILE__, __LINE__, iwl, addr, bits, mask, timeout) +#else +#define iwl4965_poll_bit(p, a, b, m, t) _iwl4965_poll_bit(p, a, b, m, t) +#endif + +static inline void _iwl4965_set_bit(struct iwl4965_priv *priv, u32 reg, u32 mask) +{ + _iwl4965_write32(priv, reg, _iwl4965_read32(priv, reg) | mask); +} +#ifdef CONFIG_IWL4965_DEBUG +static inline void __iwl4965_set_bit(const char *f, u32 l, + struct iwl4965_priv *priv, u32 reg, u32 mask) +{ + u32 val = _iwl4965_read32(priv, reg) | mask; + IWL_DEBUG_IO("set_bit(0x%08X, 0x%08X) = 0x%08X\n", reg, mask, val); + _iwl4965_write32(priv, reg, val); +} +#define iwl4965_set_bit(p, r, m) __iwl4965_set_bit(__FILE__, __LINE__, p, r, m) +#else +#define iwl4965_set_bit(p, r, m) _iwl4965_set_bit(p, r, m) +#endif + +static inline void _iwl4965_clear_bit(struct iwl4965_priv *priv, u32 reg, u32 mask) +{ + _iwl4965_write32(priv, reg, _iwl4965_read32(priv, reg) & ~mask); +} +#ifdef CONFIG_IWL4965_DEBUG +static inline void __iwl4965_clear_bit(const char *f, u32 l, + struct iwl4965_priv *priv, u32 reg, u32 mask) +{ + u32 val = _iwl4965_read32(priv, reg) & ~mask; + IWL_DEBUG_IO("clear_bit(0x%08X, 0x%08X) = 0x%08X\n", reg, mask, val); + _iwl4965_write32(priv, reg, val); +} +#define iwl4965_clear_bit(p, r, m) __iwl4965_clear_bit(__FILE__, __LINE__, p, r, m) +#else +#define iwl4965_clear_bit(p, r, m) _iwl4965_clear_bit(p, r, m) +#endif + +static inline int _iwl4965_grab_nic_access(struct iwl4965_priv *priv) +{ + int ret; + u32 gp_ctl; + +#ifdef CONFIG_IWL4965_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 = _iwl4965_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 */ + _iwl4965_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + ret = _iwl4965_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 (ret < 0) { + IWL_ERROR("MAC is in deep sleep!\n"); + return -EIO; + } + +#ifdef CONFIG_IWL4965_DEBUG + atomic_inc(&priv->restrict_refcnt); +#endif + return 0; +} + +#ifdef CONFIG_IWL4965_DEBUG +static inline int __iwl4965_grab_nic_access(const char *f, u32 l, + struct iwl4965_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 nic access - %s %d\n", f, l); + return _iwl4965_grab_nic_access(priv); +} +#define iwl4965_grab_nic_access(priv) \ + __iwl4965_grab_nic_access(__FILE__, __LINE__, priv) +#else +#define iwl4965_grab_nic_access(priv) \ + _iwl4965_grab_nic_access(priv) +#endif + +static inline void _iwl4965_release_nic_access(struct iwl4965_priv *priv) +{ +#ifdef CONFIG_IWL4965_DEBUG + if (atomic_dec_and_test(&priv->restrict_refcnt)) +#endif + _iwl4965_clear_bit(priv, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); +} +#ifdef CONFIG_IWL4965_DEBUG +static inline void __iwl4965_release_nic_access(const char *f, u32 l, + struct iwl4965_priv *priv) +{ + if (atomic_read(&priv->restrict_refcnt) <= 0) + IWL_ERROR("Release unheld nic access at line %d.\n", l); + + IWL_DEBUG_IO("releasing nic access - %s %d\n", f, l); + _iwl4965_release_nic_access(priv); +} +#define iwl4965_release_nic_access(priv) \ + __iwl4965_release_nic_access(__FILE__, __LINE__, priv) +#else +#define iwl4965_release_nic_access(priv) \ + _iwl4965_release_nic_access(priv) +#endif + +static inline u32 _iwl4965_read_direct32(struct iwl4965_priv *priv, u32 reg) +{ + return _iwl4965_read32(priv, reg); +} +#ifdef CONFIG_IWL4965_DEBUG +static inline u32 __iwl4965_read_direct32(const char *f, u32 l, + struct iwl4965_priv *priv, u32 reg) +{ + u32 value = _iwl4965_read_direct32(priv, reg); + if (!atomic_read(&priv->restrict_refcnt)) + IWL_ERROR("Nic access not held from %s %d\n", f, l); + IWL_DEBUG_IO("read_direct32(0x%4X) = 0x%08x - %s %d \n", reg, value, + f, l); + return value; +} +#define iwl4965_read_direct32(priv, reg) \ + __iwl4965_read_direct32(__FILE__, __LINE__, priv, reg) +#else +#define iwl4965_read_direct32 _iwl4965_read_direct32 +#endif + +static inline void _iwl4965_write_direct32(struct iwl4965_priv *priv, + u32 reg, u32 value) +{ + _iwl4965_write32(priv, reg, value); +} +#ifdef CONFIG_IWL4965_DEBUG +static void __iwl4965_write_direct32(u32 line, + struct iwl4965_priv *priv, u32 reg, u32 value) +{ + if (!atomic_read(&priv->restrict_refcnt)) + IWL_ERROR("Nic access not held from line %d\n", line); + _iwl4965_write_direct32(priv, reg, value); +} +#define iwl4965_write_direct32(priv, reg, value) \ + __iwl4965_write_direct32(__LINE__, priv, reg, value) +#else +#define iwl4965_write_direct32 _iwl4965_write_direct32 +#endif + +static inline void iwl4965_write_reg_buf(struct iwl4965_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++) + _iwl4965_write_direct32(priv, reg, *values); + } +} + +static inline int _iwl4965_poll_direct_bit(struct iwl4965_priv *priv, + u32 addr, u32 mask, int timeout) +{ + int i = 0; + + do { + if ((_iwl4965_read_direct32(priv, addr) & mask) == mask) + return i; + mdelay(10); + i += 10; + } while (i < timeout); + + return -ETIMEDOUT; +} + +#ifdef CONFIG_IWL4965_DEBUG +static inline int __iwl4965_poll_direct_bit(const char *f, u32 l, + struct iwl4965_priv *priv, + u32 addr, u32 mask, int timeout) +{ + int ret = _iwl4965_poll_direct_bit(priv, addr, mask, timeout); + + if (unlikely(ret == -ETIMEDOUT)) + IWL_DEBUG_IO("poll_direct_bit(0x%08X, 0x%08X) - " + "timedout - %s %d\n", addr, mask, f, l); + else + IWL_DEBUG_IO("poll_direct_bit(0x%08X, 0x%08X) = 0x%08X " + "- %s %d\n", addr, mask, ret, f, l); + return ret; +} +#define iwl4965_poll_direct_bit(iwl, addr, mask, timeout) \ + __iwl4965_poll_direct_bit(__FILE__, __LINE__, iwl, addr, mask, timeout) +#else +#define iwl4965_poll_direct_bit _iwl4965_poll_direct_bit +#endif + +static inline u32 _iwl4965_read_prph(struct iwl4965_priv *priv, u32 reg) +{ + _iwl4965_write_direct32(priv, HBUS_TARG_PRPH_RADDR, reg | (3 << 24)); + return _iwl4965_read_direct32(priv, HBUS_TARG_PRPH_RDAT); +} +#ifdef CONFIG_IWL4965_DEBUG +static inline u32 __iwl4965_read_prph(u32 line, struct iwl4965_priv *priv, u32 reg) +{ + if (!atomic_read(&priv->restrict_refcnt)) + IWL_ERROR("Nic access not held from line %d\n", line); + return _iwl4965_read_prph(priv, reg); +} + +#define iwl4965_read_prph(priv, reg) \ + __iwl4965_read_prph(__LINE__, priv, reg) +#else +#define iwl4965_read_prph _iwl4965_read_prph +#endif + +static inline void _iwl4965_write_prph(struct iwl4965_priv *priv, + u32 addr, u32 val) +{ + _iwl4965_write_direct32(priv, HBUS_TARG_PRPH_WADDR, + ((addr & 0x0000FFFF) | (3 << 24))); + _iwl4965_write_direct32(priv, HBUS_TARG_PRPH_WDAT, val); +} +#ifdef CONFIG_IWL4965_DEBUG +static inline void __iwl4965_write_prph(u32 line, struct iwl4965_priv *priv, + u32 addr, u32 val) +{ + if (!atomic_read(&priv->restrict_refcnt)) + IWL_ERROR("Nic access from line %d\n", line); + _iwl4965_write_prph(priv, addr, val); +} + +#define iwl4965_write_prph(priv, addr, val) \ + __iwl4965_write_prph(__LINE__, priv, addr, val); +#else +#define iwl4965_write_prph _iwl4965_write_prph +#endif + +#define _iwl4965_set_bits_prph(priv, reg, mask) \ + _iwl4965_write_prph(priv, reg, (_iwl4965_read_prph(priv, reg) | mask)) +#ifdef CONFIG_IWL4965_DEBUG +static inline void __iwl4965_set_bits_prph(u32 line, struct iwl4965_priv *priv, + u32 reg, u32 mask) +{ + if (!atomic_read(&priv->restrict_refcnt)) + IWL_ERROR("Nic access not held from line %d\n", line); + + _iwl4965_set_bits_prph(priv, reg, mask); +} +#define iwl4965_set_bits_prph(priv, reg, mask) \ + __iwl4965_set_bits_prph(__LINE__, priv, reg, mask) +#else +#define iwl4965_set_bits_prph _iwl4965_set_bits_prph +#endif + +#define _iwl4965_set_bits_mask_prph(priv, reg, bits, mask) \ + _iwl4965_write_prph(priv, reg, ((_iwl4965_read_prph(priv, reg) & mask) | bits)) + +#ifdef CONFIG_IWL4965_DEBUG +static inline void __iwl4965_set_bits_mask_prph(u32 line, + struct iwl4965_priv *priv, u32 reg, u32 bits, u32 mask) +{ + if (!atomic_read(&priv->restrict_refcnt)) + IWL_ERROR("Nic access not held from line %d\n", line); + _iwl4965_set_bits_mask_prph(priv, reg, bits, mask); +} +#define iwl4965_set_bits_mask_prph(priv, reg, bits, mask) \ + __iwl4965_set_bits_mask_prph(__LINE__, priv, reg, bits, mask) +#else +#define iwl4965_set_bits_mask_prph _iwl4965_set_bits_mask_prph +#endif + +static inline void iwl4965_clear_bits_prph(struct iwl4965_priv + *priv, u32 reg, u32 mask) +{ + u32 val = _iwl4965_read_prph(priv, reg); + _iwl4965_write_prph(priv, reg, (val & ~mask)); +} + +static inline u32 iwl4965_read_targ_mem(struct iwl4965_priv *priv, u32 addr) +{ + iwl4965_write_direct32(priv, HBUS_TARG_MEM_RADDR, addr); + return iwl4965_read_direct32(priv, HBUS_TARG_MEM_RDAT); +} + +static inline void iwl4965_write_targ_mem(struct iwl4965_priv *priv, u32 addr, u32 val) +{ + iwl4965_write_direct32(priv, HBUS_TARG_MEM_WADDR, addr); + iwl4965_write_direct32(priv, HBUS_TARG_MEM_WDAT, val); +} + +static inline void iwl4965_write_targ_mem_buf(struct iwl4965_priv *priv, u32 addr, + u32 len, u32 *values) +{ + iwl4965_write_direct32(priv, HBUS_TARG_MEM_WADDR, addr); + for (; 0 < len; len -= sizeof(u32), values++) + iwl4965_write_direct32(priv, HBUS_TARG_MEM_WDAT, *values); +} +#endif diff --git a/drivers/net/wireless/iwlwifi/iwl-4965-rs.c b/drivers/net/wireless/iwlwifi/iwl-4965-rs.c index 1c5b50b..60fc9ec 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965-rs.c @@ -36,28 +36,23 @@ #include <linux/workqueue.h> -#include <net/mac80211.h> -#include <linux/wireless.h> - #include "../net/mac80211/ieee80211_rate.h" -#include "iwlwifi.h" -#include "iwl-4965-rs.h" +#include "iwl-4965.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 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 */ +#define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */ +#define IWL_RATE_MIN_FAILURE_TH 6 /* min failures to calc tpt */ +#define IWL_RATE_MIN_SUCCESS_TH 8 /* min successes to calc tpt */ + +/* max time to accum history 2 seconds */ +#define IWL_RATE_SCALE_FLUSH_INTVL (2*HZ) static u8 rs_ht_to_legacy[] = { IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, @@ -69,65 +64,109 @@ static u8 rs_ht_to_legacy[] = { IWL_RATE_48M_INDEX, IWL_RATE_54M_INDEX }; -struct iwl_rate_scale_data { - u64 data; - s32 success_counter; - s32 success_ratio; - s32 counter; - s32 average_tpt; +struct iwl4965_rate { + u32 rate_n_flags; +} __attribute__ ((packed)); + +/** + * struct iwl4965_rate_scale_data -- tx success history for one rate + */ +struct iwl4965_rate_scale_data { + u64 data; /* bitmap of successful frames */ + s32 success_counter; /* number of frames successful */ + s32 success_ratio; /* per-cent * 128 */ + s32 counter; /* number of frames attempted */ + s32 average_tpt; /* success ratio * expected throughput */ 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 iwl4965_scale_tbl_info -- tx params and success history for all rates + * + * There are two of these in struct iwl_rate_scale_priv, + * one for "active", and one for "search". + */ +struct iwl4965_scale_tbl_info { + enum iwl4965_table_type lq_type; + enum iwl4965_antenna_type antenna_type; + u8 is_SGI; /* 1 = short guard interval */ + u8 is_fat; /* 1 = 40 MHz channel width */ + u8 is_dup; /* 1 = duplicated data streams */ + u8 action; /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */ + s32 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ + struct iwl4965_rate current_rate; /* rate_n_flags, uCode API format */ + struct iwl4965_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */ }; -struct iwl_rate_scale_priv { - u8 active_tbl; - u8 enable_counter; - u8 stay_in_tbl; - u8 search_better_tbl; +/** + * struct iwl_rate_scale_priv -- driver's rate scaling private structure + * + * Pointer to this gets passed back and forth between driver and mac80211. + */ +struct iwl4965_rate_scale_priv { + u8 active_tbl; /* index of active table, range 0-1 */ + u8 enable_counter; /* indicates HT mode */ + u8 stay_in_tbl; /* 1: disallow, 0: allow search for new mode */ + u8 search_better_tbl; /* 1: currently trying alternate mode */ s32 last_tpt; + + /* The following determine when to search for a new mode */ u32 table_count_limit; - u32 max_failure_limit; - u32 max_success_limit; + u32 max_failure_limit; /* # failed frames before new search */ + u32 max_success_limit; /* # successful frames before new search */ u32 table_count; - u32 total_failed; - u32 total_success; - u8 action_counter; - u32 flush_timer; - u8 commit_lq; + u32 total_failed; /* total failed frames, any/all rates */ + u32 total_success; /* total successful frames, any/all rates */ + u32 flush_timer; /* time staying in mode before new search */ + + u8 action_counter; /* # mode-switch actions tried */ u8 antenna; u8 valid_antenna; u8 is_green; u8 is_dup; u8 phymode; - u8 ready; + u8 ibss_sta_added; + + /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */ + u32 supp_rates; 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]; + + struct iwl4965_link_quality_cmd lq; + struct iwl4965_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */ +#ifdef CONFIG_MAC80211_DEBUGFS + struct dentry *rs_sta_dbgfs_scale_table_file; + struct dentry *rs_sta_dbgfs_stats_table_file; + struct iwl4965_rate dbg_fixed; + struct iwl4965_priv *drv; +#endif }; -static void rs_rate_scale_perform(struct iwl_priv *priv, +static void rs_rate_scale_perform(struct iwl4965_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); - +static void rs_fill_link_cmd(struct iwl4965_rate_scale_priv *lq_data, + struct iwl4965_rate *tx_mcs, + struct iwl4965_link_quality_cmd *tbl); + + +#ifdef CONFIG_MAC80211_DEBUGFS +static void rs_dbgfs_set_mcs(struct iwl4965_rate_scale_priv *rs_priv, + struct iwl4965_rate *mcs, int index); +#else +static void rs_dbgfs_set_mcs(struct iwl4965_rate_scale_priv *rs_priv, + struct iwl4965_rate *mcs, int index) +{} +#endif +/* + * Expected throughput metrics for following rates: + * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits + * "G" is the only table that supports CCK (the first 4 rates). + */ static s32 expected_tpt_A[IWL_RATE_COUNT] = { 0, 0, 0, 0, 40, 57, 72, 98, 121, 154, 177, 186, 186 }; @@ -168,31 +207,34 @@ 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) +static int iwl4965_lq_sync_callback(struct iwl4965_priv *priv, + struct iwl4965_cmd *cmd, struct sk_buff *skb) { /*We didn't cache the SKB; let the caller free it */ return 1; } -static int rs_send_lq_cmd(struct iwl_priv *priv, - struct iwl_link_quality_cmd *lq, u8 flags) +static inline u8 iwl4965_rate_get_rate(u32 rate_n_flags) { -#ifdef CONFIG_IWLWIFI_DEBUG + return (u8)(rate_n_flags & 0xFF); +} + +static int rs_send_lq_cmd(struct iwl4965_priv *priv, + struct iwl4965_link_quality_cmd *lq, u8 flags) +{ +#ifdef CONFIG_IWL4965_DEBUG int i; #endif - int rc = -1; - - struct iwl_host_cmd cmd = { + struct iwl4965_host_cmd cmd = { .id = REPLY_TX_LINK_QUALITY_CMD, - .len = sizeof(struct iwl_link_quality_cmd), + .len = sizeof(struct iwl4965_link_quality_cmd), .meta.flags = flags, .data = lq, }; if ((lq->sta_id == 0xFF) && (priv->iw_mode == IEEE80211_IF_TYPE_IBSS)) - return rc; + return -EINVAL; if (lq->sta_id == 0xFF) lq->sta_id = IWL_AP_ID; @@ -201,54 +243,62 @@ static int rs_send_lq_cmd(struct iwl_priv *priv, 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++) { +#ifdef CONFIG_IWL4965_DEBUG + for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) IWL_DEBUG_RATE("lq index %d 0x%X\n", - i, lq->rate_scale_table[i].rate_n_flags); - } + i, lq->rs_table[i].rate_n_flags); #endif if (flags & CMD_ASYNC) - cmd.meta.u.callback = iwl_lq_sync_callback; + cmd.meta.u.callback = iwl4965_lq_sync_callback; - if (priv->assoc_station_added) - rc = iwl_send_cmd(priv, &cmd); + if (iwl4965_is_associated(priv) && priv->assoc_station_added && + priv->lq_mngr.lq_ready) + return iwl4965_send_cmd(priv, &cmd); - return rc; + return 0; } -static int rs_rate_scale_clear_window(struct iwl_rate_scale_data - *window) +static void rs_rate_scale_clear_window(struct iwl4965_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, +/** + * rs_collect_tx_data - Update the success/failure sliding window + * + * We keep a sliding window of the last 62 packets transmitted + * at this rate. window->data contains the bitmask of successful + * packets. + */ +static int rs_collect_tx_data(struct iwl4965_rate_scale_data *windows, int scale_index, s32 tpt, u32 status) { - int rc = 0; - struct iwl_rate_scale_data *window = NULL; + struct iwl4965_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; + if (scale_index < 0 || scale_index >= IWL_RATE_COUNT) + return -EINVAL; + /* Select data for current tx bit rate */ window = &(windows[scale_index]); + /* + * Keep track of only the latest 62 tx frame attempts in this rate's + * history window; anything older isn't really relevant any more. + * If we have filled up the sliding window, drop the oldest attempt; + * if the oldest attempt (highest bit in bitmap) shows "success", + * subtract "1" from the success counter (this is the main reason + * we keep these bitmaps!). + */ if (window->counter >= win_size) { - window->counter = win_size - 1; mask = 1; mask = (mask << (win_size - 1)); @@ -258,7 +308,11 @@ static int rs_collect_tx_data(struct iwl_rate_scale_data *windows, } } + /* Increment frames-attempted counter */ window->counter = window->counter + 1; + + /* Shift bitmap by one frame (throw away oldest history), + * OR in "1", and increment "success" if this frame was successful. */ mask = window->data; window->data = (mask << 1); if (status != 0) { @@ -266,46 +320,49 @@ static int rs_collect_tx_data(struct iwl_rate_scale_data *windows, window->data |= 0x1; } - if (window->counter > 0) { + /* Calculate current success ratio, avoid divide-by-0! */ + if (window->counter > 0) window->success_ratio = 128 * (100 * window->success_counter) / window->counter; - } else + else window->success_ratio = IWL_INVALID_VALUE; - fail_count = window->counter - window->success_counter; + + /* Calculate average throughput, if we have enough history. */ if ((fail_count >= IWL_RATE_MIN_FAILURE_TH) || (window->success_counter >= IWL_RATE_MIN_SUCCESS_TH)) - window->average_tpt = ((window->success_ratio * - tpt + 64) / 128); + window->average_tpt = (window->success_ratio * tpt + 64) / 128; else - window->average_tpt = IWL_INVALID_VALUE; + window->average_tpt = IWL_INVALID_VALUE; + /* Tag this window as having been updated */ window->stamp = jiffies; - return rc; + return 0; } -int static rs_mcs_from_tbl(struct iwl_rate *mcs_rate, - struct iwl_scale_tbl_info *tbl, +/* + * Fill uCode API rate_n_flags field, based on "search" or "active" table. + */ +static void rs_mcs_from_tbl(struct iwl4965_rate *mcs_rate, + struct iwl4965_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; + mcs_rate->rate_n_flags = iwl4965_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 | + mcs_rate->rate_n_flags = iwl4965_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 | + mcs_rate->rate_n_flags = iwl4965_rates[index].plcp_mimo | RATE_MCS_HT_MSK; } @@ -324,7 +381,7 @@ int static rs_mcs_from_tbl(struct iwl_rate *mcs_rate, } if (is_legacy(tbl->lq_type)) - return rc; + return; if (tbl->is_fat) { if (tbl->is_dup) @@ -340,27 +397,31 @@ int static rs_mcs_from_tbl(struct iwl_rate *mcs_rate, 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(struct iwl_rate *mcs_rate, int phymode, - struct iwl_scale_tbl_info *tbl, +/* + * Interpret uCode API's rate_n_flags format, + * fill "search" or "active" tx mode table. + */ +static int rs_get_tbl_info_from_mcs(const struct iwl4965_rate *mcs_rate, + int phymode, struct iwl4965_scale_tbl_info *tbl, int *rate_idx) { int index; u32 ant_msk; - index = iwl_rate_index_from_plcp(mcs_rate->rate_n_flags); + index = iwl4965_rate_index_from_plcp(mcs_rate->rate_n_flags); if (index == IWL_RATE_INVALID) { *rate_idx = -1; - return -1; + return -EINVAL; } - tbl->is_SGI = 0; + tbl->is_SGI = 0; /* default legacy setup */ tbl->is_fat = 0; tbl->is_dup = 0; - tbl->antenna_type = ANT_BOTH; + tbl->antenna_type = ANT_BOTH; /* default MIMO setup */ + /* legacy rate format */ if (!(mcs_rate->rate_n_flags & RATE_MCS_HT_MSK)) { ant_msk = (mcs_rate->rate_n_flags & RATE_MCS_ANT_AB_MSK); @@ -368,8 +429,7 @@ static int rs_get_tbl_info_from_mcs(struct iwl_rate *mcs_rate, int phymode, tbl->lq_type = LQ_NONE; else { - if ((phymode == MODE_ATHEROS_TURBO) || - (phymode == MODE_IEEE80211A)) + if (phymode == MODE_IEEE80211A) tbl->lq_type = LQ_A; else tbl->lq_type = LQ_G; @@ -381,7 +441,9 @@ static int rs_get_tbl_info_from_mcs(struct iwl_rate *mcs_rate, int phymode, } *rate_idx = index; - } else if (mcs_rate->s.rate <= IWL_RATE_SISO_60M_PLCP) { + /* HT rate format, SISO (might be 20 MHz legacy or 40 MHz fat width) */ + } else if (iwl4965_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); @@ -404,6 +466,8 @@ static int rs_get_tbl_info_from_mcs(struct iwl_rate *mcs_rate, int phymode, tbl->is_dup = 1; *rate_idx = index; + + /* HT rate format, MIMO (might be 20 MHz legacy or 40 MHz fat width) */ } else { tbl->lq_type = LQ_MIMO; if (mcs_rate->rate_n_flags & RATE_MCS_SGI_MSK) @@ -420,8 +484,8 @@ static int rs_get_tbl_info_from_mcs(struct iwl_rate *mcs_rate, int phymode, return 0; } -static inline void rs_toggle_antenna(struct iwl_rate *new_rate, - struct iwl_scale_tbl_info *tbl) +static inline void rs_toggle_antenna(struct iwl4965_rate *new_rate, + struct iwl4965_scale_tbl_info *tbl) { if (tbl->antenna_type == ANT_AUX) { tbl->antenna_type = ANT_MAIN; @@ -434,18 +498,15 @@ static inline void rs_toggle_antenna(struct iwl_rate *new_rate, } } -static inline s8 rs_use_green(struct iwl_priv *priv) +static inline u8 rs_use_green(struct iwl4965_priv *priv, + struct ieee80211_conf *conf) { - 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; +#ifdef CONFIG_IWL4965_HT + return ((conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) && + priv->current_ht_config.is_green_field && + !priv->current_ht_config.non_GF_STA_present); +#endif /* CONFIG_IWL4965_HT */ + return 0; } /** @@ -455,10 +516,10 @@ static inline s8 rs_use_green(struct iwl_priv *priv) * 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) +static void rs_get_supported_rates(struct iwl4965_rate_scale_priv *lq_data, + struct ieee80211_hdr *hdr, + enum iwl4965_table_type rate_type, + u16 *data_rate) { if (is_legacy(rate_type)) *data_rate = lq_data->active_rate; @@ -469,8 +530,8 @@ static void rs_get_supported_rates(struct iwl_rate_scale_priv *lq_data, *data_rate = lq_data->active_mimo_rate; } - if (hdr && (is_multicast_ether_addr(hdr->addr1)) - && (lq_data->active_rate_basic)) + if (hdr && is_multicast_ether_addr(hdr->addr1) && + lq_data->active_rate_basic) *data_rate = lq_data->active_rate_basic; } @@ -479,7 +540,7 @@ 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 + /* 802.11A or ht walks to the next literal adjacent rate in * the rate table */ if (is_a_band(rate_type) || !is_legacy(rate_type)) { int i; @@ -508,7 +569,7 @@ static u16 rs_get_adjacent_rate(u8 index, u16 rate_mask, int rate_type) low = index; while (low != IWL_RATE_INVALID) { - low = iwl_rates[low].prev_rs; + low = iwl4965_rates[low].prev_rs; if (low == IWL_RATE_INVALID) break; if (rate_mask & (1 << low)) @@ -518,7 +579,7 @@ static u16 rs_get_adjacent_rate(u8 index, u16 rate_mask, int rate_type) high = index; while (high != IWL_RATE_INVALID) { - high = iwl_rates[high].next_rs; + high = iwl4965_rates[high].next_rs; if (high == IWL_RATE_INVALID) break; if (rate_mask & (1 << high)) @@ -529,54 +590,65 @@ static u16 rs_get_adjacent_rate(u8 index, u16 rate_mask, int rate_type) 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 *msc_rate) +static void rs_get_lower_rate(struct iwl4965_rate_scale_priv *lq_data, + struct iwl4965_scale_tbl_info *tbl, u8 scale_index, + u8 ht_possible, struct iwl4965_rate *mcs_rate) { - u8 is_green = lq_data->is_green; - s32 low, high; + s32 low; u16 rate_mask; u16 high_low; + u8 switch_to_legacy = 0; + u8 is_green = lq_data->is_green; - if (is_legacy(tbl->lq_type) || (ht_possible && scale_index)) { - rs_get_supported_rates(lq_data, NULL, tbl->lq_type, - &rate_mask); + /* 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; - high_low = rs_get_adjacent_rate(scale_index, - rate_mask, tbl->lq_type); - low = high_low & 0xff; - high = (high_low >> 8) & 0xff; + if ((tbl->antenna_type == ANT_BOTH) || + (tbl->antenna_type == ANT_NONE)) + tbl->antenna_type = ANT_MAIN; - if (low == IWL_RATE_INVALID) { - if (is_legacy(tbl->lq_type)) { - low = scale_index; - rs_mcs_from_tbl(msc_rate, tbl, - low, is_green); - return 0; - } - } else { - rs_mcs_from_tbl(msc_rate, tbl, low, is_green); - return 0; - } + tbl->is_fat = 0; + tbl->is_SGI = 0; } - low = rs_ht_to_legacy[scale_index]; - if ((lq_data->phymode == MODE_IEEE80211A) || - (lq_data->phymode == MODE_ATHEROS_TURBO)) - tbl->lq_type = LQ_A; - else - tbl->lq_type = LQ_G; + rs_get_supported_rates(lq_data, NULL, tbl->lq_type, &rate_mask); - if ((tbl->antenna_type == ANT_BOTH) || (tbl->antenna_type == ANT_NONE)) - tbl->antenna_type = ANT_MAIN; + /* Mask with station rate restriction */ + if (is_legacy(tbl->lq_type)) { + /* supp_rates has no CCK bits in A mode */ + if (lq_data->phymode == (u8) MODE_IEEE80211A) + rate_mask = (u16)(rate_mask & + (lq_data->supp_rates << IWL_FIRST_OFDM_RATE)); + else + rate_mask = (u16)(rate_mask & lq_data->supp_rates); + } - tbl->is_fat = 0; - tbl->is_SGI = 0; + /* If we 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; + } - rs_mcs_from_tbl(msc_rate, tbl, low, 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); } +/* + * mac80211 sends us Tx status + */ static void rs_tx_status(void *priv_rate, struct net_device *dev, struct sk_buff *skb, @@ -585,23 +657,22 @@ static void rs_tx_status(void *priv_rate, int status; u8 retries; int rs_index, index = 0; - struct iwl_rate_scale_priv *lq; - struct iwl_link_quality_cmd *table; + struct iwl4965_rate_scale_priv *lq; + struct iwl4965_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 iwl4965_priv *priv = (struct iwl4965_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; + struct iwl4965_rate_scale_data *window = NULL; + struct iwl4965_rate_scale_data *search_win = NULL; + struct iwl4965_rate tx_mcs; + struct iwl4965_scale_tbl_info tbl_type; + struct iwl4965_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"); + IWL_DEBUG_RATE_LIMIT("get frame ack response, update rate scale window\n"); if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1)) return; @@ -620,20 +691,30 @@ static void rs_tx_status(void *priv_rate, return; } - lq = (struct iwl_rate_scale_priv *)sta->rate_ctrl_priv; + lq = (struct iwl4965_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; + /* Get mac80211 antenna info */ lq->antenna = (lq->valid_antenna & local->hw.conf.antenna_sel_tx); if (!lq->antenna) lq->antenna = lq->valid_antenna; + /* Ignore mac80211 antenna info for now */ 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 *) + window = (struct iwl4965_rate_scale_data *) &(curr_tbl->win[0]); - search_win = (struct iwl_rate_scale_data *) + search_win = (struct iwl4965_rate_scale_data *) &(search_tbl->win[0]); tx_mcs.rate_n_flags = tx_resp->control.tx_rate; @@ -647,21 +728,35 @@ static void rs_tx_status(void *priv_rate, return; } + /* + * Ignore this Tx frame response if its initial rate doesn't match + * that of latest Link Quality command. There may be stragglers + * from a previous Link Quality command, but we're no longer interested + * in those; they're either from the "active" mode while we're trying + * to check "search" mode, or a prior "search" mode after we've moved + * to a new "search" mode (which might become the new "active" mode). + */ if (retries && - (tx_mcs.rate_n_flags != table->rate_scale_table[0].rate_n_flags)) { + (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, - table->rate_scale_table[0].rate_n_flags); + le32_to_cpu(table->rs_table[0].rate_n_flags)); sta_info_put(sta); return; } + /* Update frame history window with "failure" for each Tx retry. */ while (retries) { + /* Look up the rate and other info used for each tx attempt. + * Each tx attempt steps one entry deeper in the rate table. */ tx_mcs.rate_n_flags = - table->rate_scale_table[index].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 type matches "search" table, + * add failure to "search" history */ 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)) { @@ -669,8 +764,10 @@ static void rs_tx_status(void *priv_rate, tpt = search_tbl->expected_tpt[rs_index]; else tpt = 0; - rs_collect_tx_data(search_win, - rs_index, tpt, 0); + rs_collect_tx_data(search_win, rs_index, tpt, 0); + + /* Else if type matches "current/active" table, + * add failure to "current/active" history */ } 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)) { @@ -680,6 +777,9 @@ static void rs_tx_status(void *priv_rate, tpt = 0; rs_collect_tx_data(window, rs_index, tpt, 0); } + + /* If not searching for a new mode, increment failed counter + * ... this helps determine when to start searching again */ if (lq->stay_in_tbl) lq->total_failed++; --retries; @@ -687,20 +787,28 @@ static void rs_tx_status(void *priv_rate, } - if (!tx_resp->retry_count ) + /* + * Find (by rate) the history window to update with final Tx attempt; + * if Tx was successful first try, use original rate, + * else look up the rate that was, finally, successful. + */ + if (!tx_resp->retry_count) tx_mcs.rate_n_flags = tx_resp->control.tx_rate; else tx_mcs.rate_n_flags = - table->rate_scale_table[index].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); + /* Update frame history window with "success" if Tx got ACKed ... */ if (tx_resp->flags & IEEE80211_TX_STATUS_ACK) status = 1; else status = 0; + /* If type matches "search" table, + * add final tx status to "search" history */ 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)) { @@ -710,6 +818,9 @@ static void rs_tx_status(void *priv_rate, tpt = 0; rs_collect_tx_data(search_win, rs_index, tpt, status); + + /* Else if type matches "current/active" table, + * add final tx status to "current/active" history */ } 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)) { @@ -720,6 +831,8 @@ static void rs_tx_status(void *priv_rate, rs_collect_tx_data(window, rs_index, tpt, status); } + /* If not searching for new mode, increment success/failed counter + * ... these help determine when to start searching again */ if (lq->stay_in_tbl) { if (status) lq->total_success++; @@ -727,65 +840,53 @@ static void rs_tx_status(void *priv_rate, lq->total_failed++; } + /* See if there's a better rate or modulation mode to try. */ 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) + enum iwl4965_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; - } + else if (antenna_type == ANT_BOTH) + return ((valid_antenna & 0x3) == 0x3); return 1; } static u8 rs_is_other_ant_connected(u8 valid_antenna, - enum iwl_antenna_type antenna_type) + enum iwl4965_antenna_type antenna_type) { if (antenna_type == ANT_AUX) - return (rs_is_ant_connected(valid_antenna, ANT_MAIN)); + return rs_is_ant_connected(valid_antenna, ANT_MAIN); else - return (rs_is_ant_connected(valid_antenna, ANT_AUX)); + 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) - +/* + * Begin a period of staying with a selected modulation mode. + * Set "stay_in_tbl" flag to prevent any mode switches. + * Set frame tx success limits according to legacy vs. high-throughput, + * and reset overall (spanning all rates) tx success history statistics. + * These control how long we stay using same modulation mode before + * searching for a new mode. + */ static void rs_set_stay_in_table(u8 is_legacy, - struct iwl_rate_scale_priv *lq_data) + struct iwl4965_rate_scale_priv *lq_data) { IWL_DEBUG_HT("we are staying in the same table\n"); - lq_data->stay_in_tbl = 1; + lq_data->stay_in_tbl = 1; /* only place this gets set */ 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; + lq_data->max_success_limit = IWL_LEGACY_SUCCESS_LIMIT; } else { lq_data->table_count_limit = IWL_NONE_LEGACY_TABLE_COUNT; lq_data->max_failure_limit = IWL_NONE_LEGACY_FAILURE_LIMIT; @@ -796,8 +897,11 @@ static void rs_set_stay_in_table(u8 is_legacy, 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) +/* + * Find correct throughput table for given mode of modulation + */ +static void rs_get_expected_tpt_table(struct iwl4965_rate_scale_priv *lq_data, + struct iwl4965_scale_tbl_info *tbl) { if (is_legacy(tbl->lq_type)) { if (!is_a_band(tbl->lq_type)) @@ -829,72 +933,130 @@ static void rs_get_expected_tpt_table(struct iwl_rate_scale_priv *lq_data, 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, +#ifdef CONFIG_IWL4965_HT +/* + * Find starting rate for new "search" high-throughput mode of modulation. + * Goal is to find lowest expected rate (under perfect conditions) that is + * above the current measured throughput of "active" mode, to give new mode + * a fair chance to prove itself without too many challenges. + * + * This gets called when transitioning to more aggressive modulation + * (i.e. legacy to SISO or MIMO, or SISO to MIMO), as well as less aggressive + * (i.e. MIMO to SISO). When moving to MIMO, bit rate will typically need + * to decrease to match "active" throughput. When moving from MIMO to SISO, + * bit rate will typically need to increase, but not if performance was bad. + */ +static s32 rs_get_best_rate(struct iwl4965_priv *priv, + struct iwl4965_rate_scale_priv *lq_data, + struct iwl4965_scale_tbl_info *tbl, /* "search" */ u16 rate_mask, s8 index, s8 rate) { - struct iwl_scale_tbl_info *active_tbl = + /* "active" values */ + struct iwl4965_scale_tbl_info *active_tbl = &(lq_data->lq_info[lq_data->active_tbl]); - s32 new_rate, high, low; s32 active_sr = active_tbl->win[index].success_ratio; - s32 *tpt_tbl = tbl->expected_tpt; s32 active_tpt = active_tbl->expected_tpt[index]; + + /* expected "search" throughput */ + s32 *tpt_tbl = tbl->expected_tpt; + + s32 new_rate, high, low, start_hi; u16 high_low; - new_rate = high = low = IWL_RATE_INVALID; + new_rate = high = low = start_hi = IWL_RATE_INVALID; - for (;;) { - high_low = rs_get_adjacent_rate(rate, - rate_mask, tbl->lq_type); + for (; ;) { + high_low = rs_get_adjacent_rate(rate, rate_mask, tbl->lq_type); low = high_low & 0xff; high = (high_low >> 8) & 0xff; + /* + * Lower the "search" bit rate, to give new "search" mode + * approximately the same throughput as "active" if: + * + * 1) "Active" mode has been working modestly well (but not + * great), and expected "search" throughput (under perfect + * conditions) at candidate rate is above the actual + * measured "active" throughput (but less than expected + * "active" throughput under perfect conditions). + * OR + * 2) "Active" mode has been working perfectly or very well + * and expected "search" throughput (under perfect + * conditions) at candidate rate is above expected + * "active" throughput (under perfect conditions). + */ 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))) { + + /* (2nd or later pass) + * If we've already tried to raise the rate, and are + * now trying to lower it, use the higher rate. */ + if (start_hi != IWL_RATE_INVALID) { + new_rate = start_hi; + break; + } + new_rate = rate; + + /* Loop again with lower rate */ if (low != IWL_RATE_INVALID) rate = low; + + /* Lower rate not available, use the original */ else break; + + /* Else try to raise the "search" rate to match "active" */ } else { + /* (2nd or later pass) + * If we've already tried to lower the rate, and are + * now trying to raise it, use the lower rate. */ if (new_rate != IWL_RATE_INVALID) break; - else if (high != IWL_RATE_INVALID) + + /* Loop again with higher rate */ + else if (high != IWL_RATE_INVALID) { + start_hi = high; rate = high; - else + + /* Higher rate not available, use the original */ + } else { + new_rate = rate; break; + } } } - if (new_rate == IWL_RATE_INVALID) - new_rate = rate; return new_rate; } -#endif /* CONFIG_IWLWIFI_HT */ +#endif /* CONFIG_IWL4965_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) +/* + * Set up search table for MIMO + */ +static int rs_switch_to_mimo(struct iwl4965_priv *priv, + struct iwl4965_rate_scale_priv *lq_data, + struct ieee80211_conf *conf, + struct sta_info *sta, + struct iwl4965_scale_tbl_info *tbl, int index) { - int rc = -1; -#ifdef CONFIG_IWLWIFI_HT +#ifdef CONFIG_IWL4965_HT u16 rate_mask; s32 rate; s8 is_green = lq_data->is_green; - if (!priv->is_ht_enabled || !priv->current_assoc_ht.is_ht) + if (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) || + !sta->ht_info.ht_supported) return -1; IWL_DEBUG_HT("LQ: try to switch to MIMO\n"); @@ -902,36 +1064,34 @@ static int rs_switch_to_mimo(struct iwl_priv *priv, 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) + if (priv->current_ht_config.tx_mimo_ps_mode == IWL_MIMO_PS_STATIC) return -1; + /* Need both Tx chains/antennas to support MIMO */ 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) + if (priv->current_ht_config.supported_chan_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) + if (priv->current_ht_config.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) + } else if (priv->current_ht_config.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, 4); - - if ((rate == IWL_RATE_INVALID) || (rate == 1)) - rate = IWL_RATE_48M_INDEX; + 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)) @@ -940,43 +1100,49 @@ static int rs_switch_to_mimo(struct iwl_priv *priv, 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; + return 0; +#else + return -1; +#endif /*CONFIG_IWL4965_HT */ } -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) +/* + * Set up search table for SISO + */ +static int rs_switch_to_siso(struct iwl4965_priv *priv, + struct iwl4965_rate_scale_priv *lq_data, + struct ieee80211_conf *conf, + struct sta_info *sta, + struct iwl4965_scale_tbl_info *tbl, int index) { - int rc = -1; -#ifdef CONFIG_IWLWIFI_HT +#ifdef CONFIG_IWL4965_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) + if (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) || + !sta->ht_info.ht_supported) 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) + if (priv->current_ht_config.supported_chan_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) + if (priv->current_ht_config.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) + } else if (priv->current_ht_config.sgf & HT_SHORT_GI_20MHZ_ONLY) tbl->is_SGI = 1; else tbl->is_SGI = 0; @@ -985,9 +1151,8 @@ static int rs_switch_to_siso(struct iwl_priv *priv, tbl->is_SGI = 0; rs_get_expected_tpt_table(lq_data, tbl); - rate = rs_get_best_rate(priv, lq_data, tbl, rate_mask, index, 4); - if ((rate == IWL_RATE_INVALID) || (rate == 1)) - rate = IWL_RATE_48M_INDEX; + 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", @@ -997,37 +1162,50 @@ static int rs_switch_to_siso(struct iwl_priv *priv, 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); + return 0; +#else + return -1; -#endif /*CONFIG_IWLWIFI_HT */ - return rc; +#endif /*CONFIG_IWL4965_HT */ } -static int rs_move_legacy_other(struct iwl_priv *priv, - struct iwl_rate_scale_priv *lq_data, +/* + * Try to switch to new modulation mode from legacy + */ +static int rs_move_legacy_other(struct iwl4965_priv *priv, + struct iwl4965_rate_scale_priv *lq_data, + struct ieee80211_conf *conf, + struct sta_info *sta, int index) { - int rc = 0; - struct iwl_scale_tbl_info *tbl = + int ret = 0; + struct iwl4965_scale_tbl_info *tbl = &(lq_data->lq_info[lq_data->active_tbl]); - struct iwl_scale_tbl_info *search_tbl = + struct iwl4965_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)); + struct iwl4965_rate_scale_data *window = &(tbl->win[index]); + u32 sz = (sizeof(struct iwl4965_scale_tbl_info) - + (sizeof(struct iwl4965_rate_scale_data) * IWL_RATE_COUNT)); u8 start_action = tbl->action; - for (;;) { + + 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++; + + /* Don't change antenna if success has been great */ if (window->success_ratio >= IWL_RS_GOOD_RATIO) break; + + /* Don't change antenna if other one is not connected */ if (!rs_is_other_ant_connected(lq_data->antenna, tbl->antenna_type)) break; + /* Set up search table to try other antenna */ memcpy(search_tbl, tbl, sz); rs_toggle_antenna(&(search_tbl->current_rate), @@ -1036,37 +1214,39 @@ static int rs_move_legacy_other(struct iwl_priv *priv, lq_data->search_better_tbl = 1; goto out; - case IWL_LECACY_SWITCH_SISO: + case IWL_LEGACY_SWITCH_SISO: IWL_DEBUG_HT("LQ: Legacy switch to SISO\n"); + + /* Set up search table to try SISO */ 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, + ret = rs_switch_to_siso(priv, lq_data, conf, sta, search_tbl, index); - if (!rc) { + if (!ret) { 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"); + + /* Set up search table to try MIMO */ 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, + ret = rs_switch_to_mimo(priv, lq_data, conf, sta, search_tbl, index); - if (!rc) { + if (!ret) { lq_data->search_better_tbl = 1; lq_data->action_counter = 0; - } - if (!rc) goto out; + } break; } tbl->action++; @@ -1087,23 +1267,24 @@ static int rs_move_legacy_other(struct iwl_priv *priv, } -#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, +/* + * Try to switch to new modulation mode from SISO + */ +static int rs_move_siso_to_other(struct iwl4965_priv *priv, + struct iwl4965_rate_scale_priv *lq_data, + struct ieee80211_conf *conf, + struct sta_info *sta, int index) { - int rc = -1; + int ret; u8 is_green = lq_data->is_green; - struct iwl_scale_tbl_info *tbl = + struct iwl4965_scale_tbl_info *tbl = &(lq_data->lq_info[lq_data->active_tbl]); - struct iwl_scale_tbl_info *search_tbl = + struct iwl4965_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)); + struct iwl4965_rate_scale_data *window = &(tbl->win[index]); + u32 sz = (sizeof(struct iwl4965_scale_tbl_info) - + (sizeof(struct iwl4965_rate_scale_data) * IWL_RATE_COUNT)); u8 start_action = tbl->action; for (;;) { @@ -1133,13 +1314,12 @@ static int rs_move_siso_to_other(struct iwl_priv *priv, search_tbl->is_SGI = 0; search_tbl->is_fat = 0; search_tbl->antenna_type = ANT_BOTH; - rc = rs_switch_to_mimo(priv, lq_data, + ret = rs_switch_to_mimo(priv, lq_data, conf, sta, search_tbl, index); - if (!rc) + if (!ret) { 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"); @@ -1151,8 +1331,17 @@ static int rs_move_siso_to_other(struct iwl_priv *priv, search_tbl->is_SGI = 1; else break; - rs_get_expected_tpt_table(lq_data, search_tbl); 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; @@ -1173,22 +1362,23 @@ static int rs_move_siso_to_other(struct iwl_priv *priv, 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, +/* + * Try to switch to new modulation mode from MIMO + */ +static int rs_move_mimo_to_other(struct iwl4965_priv *priv, + struct iwl4965_rate_scale_priv *lq_data, + struct ieee80211_conf *conf, + struct sta_info *sta, int index) { - int rc = -1; + int ret; s8 is_green = lq_data->is_green; - struct iwl_scale_tbl_info *tbl = + struct iwl4965_scale_tbl_info *tbl = &(lq_data->lq_info[lq_data->active_tbl]); - struct iwl_scale_tbl_info *search_tbl = + struct iwl4965_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)); + u32 sz = (sizeof(struct iwl4965_scale_tbl_info) - + (sizeof(struct iwl4965_rate_scale_data) * IWL_RATE_COUNT)); u8 start_action = tbl->action; for (;;) { @@ -1197,6 +1387,8 @@ static int rs_move_mimo_to_other(struct iwl_priv *priv, case IWL_MIMO_SWITCH_ANTENNA_A: case IWL_MIMO_SWITCH_ANTENNA_B: IWL_DEBUG_HT("LQ: MIMO SWITCH TO SISO\n"); + + /* Set up new search table for SISO */ memcpy(search_tbl, tbl, sz); search_tbl->lq_type = LQ_SISO; search_tbl->is_SGI = 0; @@ -1206,9 +1398,9 @@ static int rs_move_mimo_to_other(struct iwl_priv *priv, else search_tbl->antenna_type = ANT_AUX; - rc = rs_switch_to_siso(priv, lq_data, + ret = rs_switch_to_siso(priv, lq_data, conf, sta, search_tbl, index); - if (!rc) { + if (!ret) { lq_data->search_better_tbl = 1; goto out; } @@ -1216,6 +1408,8 @@ static int rs_move_mimo_to_other(struct iwl_priv *priv, case IWL_MIMO_SWITCH_GI: IWL_DEBUG_HT("LQ: MIMO SWITCH TO GI\n"); + + /* Set up new search table for MIMO */ memcpy(search_tbl, tbl, sz); search_tbl->lq_type = LQ_MIMO; search_tbl->antenna_type = ANT_BOTH; @@ -1225,6 +1419,22 @@ static int rs_move_mimo_to_other(struct iwl_priv *priv, else search_tbl->is_SGI = 1; lq_data->search_better_tbl = 1; + + /* + * If active table already uses the fastest possible + * modulation (dual stream with short guard interval), + * and it's working well, there's no need to look + * for a better type of modulation! + */ + 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); @@ -1232,7 +1442,7 @@ static int rs_move_mimo_to_other(struct iwl_priv *priv, } tbl->action++; - if (tbl->action > IWL_MIMO_SWITCH_ANTENNA_B) + if (tbl->action > IWL_MIMO_SWITCH_GI) tbl->action = IWL_MIMO_SWITCH_ANTENNA_A; if (tbl->action == start_action) @@ -1242,15 +1452,22 @@ static int rs_move_mimo_to_other(struct iwl_priv *priv, return 0; out: tbl->action++; - if (tbl->action > IWL_MIMO_SWITCH_ANTENNA_B) + 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) +/* + * Check whether we should continue using same modulation mode, or + * begin search for a new mode, based on: + * 1) # tx successes or failures while using this mode + * 2) # times calling this function + * 3) elapsed time in this mode (not used, for now) + */ +static void rs_stay_in_table(struct iwl4965_rate_scale_priv *lq_data) { - struct iwl_scale_tbl_info *tbl; + struct iwl4965_scale_tbl_info *tbl; int i; int active_tbl; int flush_interval_passed = 0; @@ -1259,27 +1476,49 @@ static void rs_stay_in_table(struct iwl_rate_scale_priv *lq_data) tbl = &(lq_data->lq_info[active_tbl]); + /* If we've been disallowing search, see if we should now allow it */ if (lq_data->stay_in_tbl) { + /* Elapsed time using current modulation mode */ if (lq_data->flush_timer) flush_interval_passed = time_after(jiffies, (unsigned long)(lq_data->flush_timer + IWL_RATE_SCALE_FLUSH_INTVL)); + /* For now, disable the elapsed time criterion */ flush_interval_passed = 0; + + /* + * Check if we should allow search for new modulation mode. + * If many frames have failed or succeeded, or we've used + * this same modulation for a long time, allow search, and + * reset history stats that keep track of whether we should + * allow a new search. Also (below) reset all bitmaps and + * stats in active history. + */ if ((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 \n:", + IWL_DEBUG_HT("LQ: stay is expired %d %d %d\n:", lq_data->total_failed, - lq_data->total_success); - lq_data->stay_in_tbl = 0; + lq_data->total_success, + flush_interval_passed); + + /* Allow search for new mode */ + lq_data->stay_in_tbl = 0; /* only place reset */ lq_data->total_failed = 0; lq_data->total_success = 0; lq_data->flush_timer = 0; - } else if (lq_data->table_count > 0) { + + /* + * Else if we've used this modulation mode enough repetitions + * (regardless of elapsed time or success/failure), reset + * history bitmaps and rate-specific stats for all rates in + * active table. + */ + } else { lq_data->table_count++; if (lq_data->table_count >= lq_data->table_count_limit) { @@ -1292,6 +1531,9 @@ static void rs_stay_in_table(struct iwl_rate_scale_priv *lq_data) } } + /* If transitioning to allow "search", reset all history + * bitmaps and stats in active table (this will become the new + * "search" table). */ if (!lq_data->stay_in_tbl) { for (i = 0; i < IWL_RATE_COUNT; i++) rs_rate_scale_clear_window(&(tbl->win[i])); @@ -1299,16 +1541,22 @@ static void rs_stay_in_table(struct iwl_rate_scale_priv *lq_data) } } -static void rs_rate_scale_perform(struct iwl_priv *priv, +/* + * Do rate scaling and search for new modulation mode. + */ +static void rs_rate_scale_perform(struct iwl4965_priv *priv, struct net_device *dev, struct ieee80211_hdr *hdr, struct sta_info *sta) { + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_hw *hw = local_to_hw(local); + struct ieee80211_conf *conf = &hw->conf; int low = IWL_RATE_INVALID; int high = IWL_RATE_INVALID; int index; int i; - struct iwl_rate_scale_data *window = NULL; + struct iwl4965_rate_scale_data *window = NULL; int current_tpt = IWL_INVALID_VALUE; int low_tpt = IWL_INVALID_VALUE; int high_tpt = IWL_INVALID_VALUE; @@ -1316,10 +1564,10 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, 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; + struct iwl4965_rate_scale_priv *lq_data; + struct iwl4965_scale_tbl_info *tbl, *tbl1; u16 rate_scale_index_msk = 0; - struct iwl_rate mcs_rate; + struct iwl4965_rate mcs_rate; u8 is_green = 0; u8 active_tbl = 0; u8 done_search = 0; @@ -1338,12 +1586,17 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, if (!sta || !sta->rate_ctrl_priv) return; - lq_data = (struct iwl_rate_scale_priv *)sta->rate_ctrl_priv; - if (!lq_data->ready) { + if (!priv->lq_mngr.lq_ready) { IWL_DEBUG_RATE("still rate scaling not ready\n"); return; } + lq_data = (struct iwl4965_rate_scale_priv *)sta->rate_ctrl_priv; + /* + * Select rate-scale / modulation-mode table to work with in + * the rest of this function: "search" if searching for better + * modulation mode, or "active" if doing rate scaling within a mode. + */ if (!lq_data->search_better_tbl) active_tbl = lq_data->active_tbl; else @@ -1352,11 +1605,13 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, tbl = &(lq_data->lq_info[active_tbl]); is_green = lq_data->is_green; + /* current tx rate */ index = sta->last_txrate; IWL_DEBUG_RATE("Rate scale index %d for type %d\n", index, tbl->lq_type); + /* rates available for this association, and for modulation mode */ rs_get_supported_rates(lq_data, hdr, tbl->lq_type, &rate_mask); @@ -1364,13 +1619,13 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, /* mask with station rate restriction */ if (is_legacy(tbl->lq_type)) { - if ((lq_data->phymode == (u8) MODE_IEEE80211A) || - (lq_data->phymode == (u8) MODE_ATHEROS_TURBO)) + if (lq_data->phymode == (u8) MODE_IEEE80211A) + /* supp_rates has no CCK bits in A mode */ rate_scale_index_msk = (u16) (rate_mask & - (sta->supp_rates << IWL_FIRST_OFDM_RATE)); + (lq_data->supp_rates << IWL_FIRST_OFDM_RATE)); else rate_scale_index_msk = (u16) (rate_mask & - sta->supp_rates); + lq_data->supp_rates); } else rate_scale_index_msk = rate_mask; @@ -1378,11 +1633,13 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, if (!rate_scale_index_msk) rate_scale_index_msk = rate_mask; + /* If current rate is no longer supported on current association, + * or user changed preferences for rates, find a new supported rate. */ if (index < 0 || !((1 << index) & rate_scale_index_msk)) { index = IWL_INVALID_VALUE; update_lq = 1; - /* get the lowest availabe rate */ + /* get the highest available rate */ for (i = 0; i <= IWL_RATE_COUNT; i++) { if ((1 << i) & rate_scale_index_msk) index = i; @@ -1394,36 +1651,55 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, } } + /* Get expected throughput table and history window for current rate */ if (!tbl->expected_tpt) rs_get_expected_tpt_table(lq_data, tbl); window = &(tbl->win[index]); + /* + * If there is not enough history to calculate actual average + * throughput, keep analyzing results of more tx frames, without + * changing rate or mode (bypass most of the rest of this function). + * Set up new rate table in uCode only if old rate is not supported + * in current association (use new rate found above). + */ fail_count = window->counter - window->success_counter; if (((fail_count < IWL_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\n", - window->success_counter, window->counter); + IWL_DEBUG_RATE("LQ: still below TH succ %d total %d " + "for index %d\n", + window->success_counter, window->counter, index); + + /* Can't calculate this yet; not enough history */ window->average_tpt = IWL_INVALID_VALUE; + + /* Should we stay with this modulation mode, + * or search for a new one? */ rs_stay_in_table(lq_data); + + /* Set up new rate table in uCode, if needed */ if (update_lq) { rs_mcs_from_tbl(&mcs_rate, tbl, index, is_green); - rs_fill_link_cmd(lq_data, &mcs_rate, &(lq_data->lq)); - if (!rs_send_lq_cmd(priv, &lq_data->lq, CMD_ASYNC)) - lq_data->commit_lq = 0; - else - lq_data->commit_lq = 1; + rs_fill_link_cmd(lq_data, &mcs_rate, &lq_data->lq); + rs_send_lq_cmd(priv, &lq_data->lq, CMD_ASYNC); } goto out; + /* Else we have enough samples; calculate estimate of + * actual average throughput */ } else window->average_tpt = ((window->success_ratio * tbl->expected_tpt[index] + 64) / 128); + /* If we are searching for better modulation mode, check success. */ if (lq_data->search_better_tbl) { int success_limit = IWL_RATE_SCALE_SWITCH; + /* If good success, continue using the "search" mode; + * no need to send new link quality command, since we're + * continuing to use the setup that we've been trying. */ if ((window->success_ratio > success_limit) || (window->average_tpt > lq_data->last_tpt)) { if (!is_legacy(tbl->lq_type)) { @@ -1435,55 +1711,78 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, lq_data->last_tpt); lq_data->enable_counter = 1; } + /* Swap tables; "search" becomes "active" */ lq_data->active_tbl = active_tbl; current_tpt = window->average_tpt; + + /* Else poor success; go back to mode in "active" table */ } else { + /* Nullify "search" table */ tbl->lq_type = LQ_NONE; + + /* Revert to "active" table */ active_tbl = lq_data->active_tbl; tbl = &(lq_data->lq_info[active_tbl]); - index = iwl_rate_index_from_plcp( + /* Revert to "active" rate and throughput info */ + index = iwl4965_rate_index_from_plcp( tbl->current_rate.rate_n_flags); + current_tpt = lq_data->last_tpt; + /* Need to set up a new rate table in uCode */ update_lq = 1; - current_tpt = lq_data->last_tpt; IWL_DEBUG_HT("XXY GO BACK TO OLD TABLE\n"); } + + /* Either way, we've made a decision; modulation mode + * search is done, allow rate adjustment next time. */ lq_data->search_better_tbl = 0; - done_search = 1; + done_search = 1; /* Don't switch modes below! */ goto lq_update; } + /* (Else) not in search of better modulation mode, try for better + * starting rate, while staying in this mode. */ high_low = rs_get_adjacent_rate(index, rate_scale_index_msk, tbl->lq_type); low = high_low & 0xff; high = (high_low >> 8) & 0xff; + /* Collect measured throughputs for current and adjacent rates */ current_tpt = window->average_tpt; - - if (low != IWL_RATE_INVALID) { + if (low != IWL_RATE_INVALID) low_tpt = tbl->win[low].average_tpt; - } if (high != IWL_RATE_INVALID) high_tpt = tbl->win[high].average_tpt; - + /* Assume rate increase */ scale_action = 1; + /* Too many failures, decrease rate */ 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; + + /* No throughput measured yet for adjacent rates; try increase. */ } else if ((low_tpt == IWL_INVALID_VALUE) && - (high_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)) { + + /* Both adjacent throughputs are measured, but neither one has better + * throughput; we're using the best rate, don't change it! */ + else if ((low_tpt != IWL_INVALID_VALUE) && + (high_tpt != IWL_INVALID_VALUE) && + (low_tpt < current_tpt) && + (high_tpt < current_tpt)) scale_action = 0; - } else { + + /* At least one adjacent rate's throughput is measured, + * and may have better performance. */ + else { + /* Higher adjacent rate's throughput is measured */ if (high_tpt != IWL_INVALID_VALUE) { + /* Higher rate has better throughput */ if (high_tpt > current_tpt) scale_action = 1; else { @@ -1491,7 +1790,10 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, ("decrease rate because of high tpt\n"); scale_action = -1; } + + /* Lower adjacent rate's throughput is measured */ } else if (low_tpt != IWL_INVALID_VALUE) { + /* Lower rate has better throughput */ if (low_tpt > current_tpt) { IWL_DEBUG_RATE ("decrease rate because of low tpt\n"); @@ -1501,82 +1803,103 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, } } + /* Sanity check; asked for decrease, but success rate or throughput + * has been good at old rate. Don't change it. */ 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; + + /* Sanity check; asked for increase, but success rate has not been great + * even at old rate, higher rate will be worse. Don't change it. */ } else if ((scale_action == 1) && (window->success_ratio < IWL_RATE_INCREASE_TH)) scale_action = 0; switch (scale_action) { case -1: + /* Decrease starting rate, update uCode's rate table */ if (low != IWL_RATE_INVALID) { update_lq = 1; index = low; } break; - case 1: + /* Increase starting rate, update uCode's rate table */ if (high != IWL_RATE_INVALID) { update_lq = 1; index = high; } break; - case 0: - + /* No change */ default: break; } - IWL_DEBUG_HT - ("choose rate scale index %d action %d low %d high %d\n", - index, scale_action, low, high); + 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: + /* Replace uCode's rate table for the destination station. */ if (update_lq) { rs_mcs_from_tbl(&mcs_rate, tbl, index, is_green); - rs_fill_link_cmd(lq_data, &mcs_rate, &(lq_data->lq)); - if (!rs_send_lq_cmd(priv, &lq_data->lq, CMD_ASYNC)) - lq_data->commit_lq = 0; - else - lq_data->commit_lq = 1; + rs_fill_link_cmd(lq_data, &mcs_rate, &lq_data->lq); + rs_send_lq_cmd(priv, &lq_data->lq, CMD_ASYNC); } + + /* Should we stay with this modulation mode, or search for a new one? */ rs_stay_in_table(lq_data); - if (!update_lq - && !done_search && !lq_data->stay_in_tbl) { + + /* + * Search for new modulation mode if we're: + * 1) Not changing rates right now + * 2) Not just finishing up a search + * 3) Allowing a new search + */ + if (!update_lq && !done_search && !lq_data->stay_in_tbl) { + /* Save current throughput to compare with "search" throughput*/ lq_data->last_tpt = current_tpt; + + /* Select a new "search" modulation mode to try. + * If one is found, set up the new "search" table. */ if (is_legacy(tbl->lq_type)) - rs_move_legacy_other(priv, lq_data, index); + rs_move_legacy_other(priv, lq_data, conf, sta, index); else if (is_siso(tbl->lq_type)) - rs_move_siso_to_other(priv, lq_data, index); + rs_move_siso_to_other(priv, lq_data, conf, sta, index); else - rs_move_mimo_to_other(priv, lq_data, index); + rs_move_mimo_to_other(priv, lq_data, conf, sta, index); + /* If new "search" mode was selected, set up in uCode table */ if (lq_data->search_better_tbl) { + /* Access the "search" table, clear its history. */ 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); + /* Use new "search" start rate */ + index = iwl4965_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)); - if (!rs_send_lq_cmd(priv, &lq_data->lq, CMD_ASYNC)) - lq_data->commit_lq = 0; - else - lq_data->commit_lq = 1; + &lq_data->lq); + rs_send_lq_cmd(priv, &lq_data->lq, CMD_ASYNC); } + + /* If the "active" (non-search) mode was legacy, + * and we've tried switching antennas, + * but we haven't been able to try HT modes (not available), + * stay with best antenna legacy modulation for a while + * before next round of mode comparisons. */ tbl1 = &(lq_data->lq_info[lq_data->active_tbl]); if (is_legacy(tbl1->lq_type) && -#ifdef CONFIG_IWLWIFI_HT - !priv->current_assoc_ht.is_ht && +#ifdef CONFIG_IWL4965_HT + (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE)) && #endif (lq_data->action_counter >= 1)) { lq_data->action_counter = 0; @@ -1584,48 +1907,69 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, rs_set_stay_in_table(1, lq_data); } + /* If we're in an HT mode, and all 3 mode switch actions + * have been tried and compared, stay in this best modulation + * mode for a while before next round of mode comparisons. */ if (lq_data->enable_counter && (lq_data->action_counter >= IWL_ACTION_LIMIT)) { +#ifdef CONFIG_IWL4965_HT_AGG + /* If appropriate, set up aggregation! */ + 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_IWL4965_HT_AGG */ lq_data->action_counter = 0; rs_set_stay_in_table(0, lq_data); } + + /* + * Else, don't search for a new modulation mode. + * Put new timestamp in stay-in-modulation-mode flush timer if: + * 1) Not changing rates right now + * 2) Not just finishing up a search + * 3) flush timer is empty + */ } else { if ((!update_lq) && (!done_search) && (!lq_data->flush_timer)) lq_data->flush_timer = jiffies; } - out: +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) || - (lq_data->phymode == (u8) MODE_ATHEROS_TURBO)) + * 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; + else + sta->txrate = i; return; } -static void rs_initialize_lq(struct iwl_priv *priv, - struct sta_info *sta, - int flg) +static void rs_initialize_lq(struct iwl4965_priv *priv, + struct ieee80211_conf *conf, + struct sta_info *sta) { int i; - struct iwl_rate_scale_priv *lq; - struct iwl_scale_tbl_info *tbl; + struct iwl4965_rate_scale_priv *lq; + struct iwl4965_scale_tbl_info *tbl; u8 active_tbl = 0; int rate_idx; - u8 use_green = rs_use_green(priv); - struct iwl_rate mcs_rate; + u8 use_green = rs_use_green(priv, conf); + struct iwl4965_rate mcs_rate; if (!sta || !sta->rate_ctrl_priv) goto out; - lq = (struct iwl_rate_scale_priv *)sta->rate_ctrl_priv; + lq = (struct iwl4965_rate_scale_priv *)sta->rate_ctrl_priv; i = sta->last_txrate; if ((lq->lq.sta_id == 0xff) && @@ -1642,7 +1986,7 @@ static void rs_initialize_lq(struct iwl_priv *priv, if ((i < 0) || (i >= IWL_RATE_COUNT)) i = 0; - mcs_rate.rate_n_flags = iwl_rates[i].plcp ; + mcs_rate.rate_n_flags = iwl4965_rates[i].plcp ; mcs_rate.rate_n_flags |= RATE_MCS_ANT_B_MSK; mcs_rate.rate_n_flags &= ~RATE_MCS_ANT_A_MSK; @@ -1652,13 +1996,13 @@ static void rs_initialize_lq(struct iwl_priv *priv, 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_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)); - lq->commit_lq = 1; + rs_fill_link_cmd(lq, &mcs_rate, &lq->lq); + rs_send_lq_cmd(priv, &lq->lq, CMD_ASYNC); out: return; } @@ -1688,13 +2032,14 @@ static struct ieee80211_rate *rs_get_rate(void *priv_rate, int i; struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_conf *conf = &local->hw.conf; 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; + struct iwl4965_priv *priv = (struct iwl4965_priv *)priv_rate; + struct iwl4965_rate_scale_priv *lq; - IWL_DEBUG_RATE("rate scale calculate new rate for skb\n"); + IWL_DEBUG_RATE_LIMIT("rate scale calculate new rate for skb\n"); memset(extra, 0, sizeof(*extra)); @@ -1714,37 +2059,51 @@ static struct ieee80211_rate *rs_get_rate(void *priv_rate, return rs_get_lowest_rate(local); } + lq = (struct iwl4965_rate_scale_priv *)sta->rate_ctrl_priv; i = sta->last_txrate; - lq = (struct iwl_rate_scale_priv *)sta->rate_ctrl_priv; - if (lq->commit_lq && lq->ready) { - lq->commit_lq = 0; - if (rs_send_lq_cmd(priv, &lq->lq, CMD_ASYNC)) - lq->commit_lq = 1; + if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) && !lq->ibss_sta_added) { + u8 sta_id = iwl4965_hw_find_station(priv, hdr->addr1); + DECLARE_MAC_BUF(mac); + + if (sta_id == IWL_INVALID_STATION) { + IWL_DEBUG_RATE("LQ: ADD station %s\n", + print_mac(mac, hdr->addr1)); + sta_id = iwl4965_add_station_flags(priv, hdr->addr1, + 0, CMD_ASYNC, NULL); + } + 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, conf, sta); + } + if (!lq->ibss_sta_added) + goto done; } + done: sta_info_put(sta); - if ((i < 0) || (i > IWL_RATE_COUNT)) { + 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; + struct iwl4965_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); + + crl = kzalloc(sizeof(struct iwl4965_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])); @@ -1757,17 +2116,13 @@ static void rs_rate_init(void *priv_rate, void *priv_sta, struct sta_info *sta) { int i, j; + struct ieee80211_conf *conf = &local->hw.conf; 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; + struct iwl4965_priv *priv = (struct iwl4965_priv *)priv_rate; + struct iwl4965_rate_scale_priv *crl = priv_sta; - if (priv->iw_mode != IEEE80211_IF_TYPE_IBSS) - mutex_lock(&priv->mutex); - - memset(crl, 0, sizeof(struct iwl_rate_scale_priv)); - - crl->lq.sta_id = 0xff; crl->flush_timer = 0; + crl->supp_rates = sta->supp_rates; sta->txrate = 3; for (j = 0; j < LQ_SIZE; j++) for (i = 0; i < IWL_RATE_COUNT; i++) @@ -1779,129 +2134,162 @@ static void rs_rate_init(void *priv_rate, void *priv_sta, * previous packets? Need to have IEEE 802.1X auth succeed immediately * after assoc.. */ - if (((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) || - (priv->iw_mode == IEEE80211_IF_TYPE_AP))) { - u8 sta_id = iwl_hw_find_station(priv, sta->addr); + crl->ibss_sta_added = 0; + if (priv->iw_mode == IEEE80211_IF_TYPE_AP) { + u8 sta_id = iwl4965_hw_find_station(priv, sta->addr); + DECLARE_MAC_BUF(mac); + /* for IBSS the call are from tasklet */ - IWL_DEBUG_HT("LQ: ADD station " MAC_FMT " \n", - MAC_ARG(sta->addr)); + IWL_DEBUG_HT("LQ: ADD station %s\n", + print_mac(mac, 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); + IWL_DEBUG_RATE("LQ: ADD station %s\n", + print_mac(mac, sta->addr)); + sta_id = iwl4965_add_station_flags(priv, sta->addr, + 0, CMD_ASYNC, NULL); } if ((sta_id != IWL_INVALID_STATION)) { crl->lq.sta_id = sta_id; - crl->lq.rate_scale_table[0].rate_n_flags = 0; + crl->lq.rs_table[0].rate_n_flags = 0; } - crl->ready = 1; + /* FIXME: this is w/a remove it later */ + priv->assoc_station_added = 1; } + /* Find highest tx rate supported by hardware and destination station */ 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) || - (local->hw.conf.phymode == MODE_ATHEROS_TURBO)) + /* For MODE_IEEE80211A, cck rates are at end of rate table */ + if (local->hw.conf.phymode == MODE_IEEE80211A) sta->last_txrate += IWL_FIRST_OFDM_RATE; - crl->is_dup = priv->is_dup; + crl->is_dup = 0; crl->valid_antenna = priv->valid_antenna; crl->antenna = priv->antenna; - crl->is_green = rs_use_green(priv); + crl->is_green = rs_use_green(priv, conf); 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->active_rate_ht[0] << 1); - crl->active_siso_rate |= (priv->active_rate_ht[0] & 0x1); +#ifdef CONFIG_IWL4965_HT + /* + * active_siso_rate mask includes 9 MBits (bit 5), and CCK (bits 0-3), + * supp_rates[] does not; shift to convert format, force 9 MBits off. + */ + crl->active_siso_rate = (priv->current_ht_config.supp_mcs_set[0] << 1); + crl->active_siso_rate |= + (priv->current_ht_config.supp_mcs_set[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->active_rate_ht[1] << 1); - crl->active_mimo_rate |= (priv->active_rate_ht[1] & 0x1); + /* Same here */ + crl->active_mimo_rate = (priv->current_ht_config.supp_mcs_set[1] << 1); + crl->active_mimo_rate |= + (priv->current_ht_config.supp_mcs_set[1] & 0x1); crl->active_mimo_rate &= ~((u16)0x2); crl->active_mimo_rate = crl->active_mimo_rate << IWL_FIRST_OFDM_RATE; -#endif /*CONFIG_IWLWIFI_HT*/ + IWL_DEBUG_HT("MIMO RATE 0x%X SISO MASK 0x%X\n", crl->active_siso_rate, + crl->active_mimo_rate); +#endif /*CONFIG_IWL4965_HT*/ +#ifdef CONFIG_MAC80211_DEBUGFS + crl->drv = priv; +#endif if (priv->assoc_station_added) - crl->ready = 1; - - if (priv && (priv->iw_mode != IEEE80211_IF_TYPE_IBSS)) - rs_initialize_lq(priv, sta, 0); - else - rs_initialize_lq(priv, sta, CMD_ASYNC); + priv->lq_mngr.lq_ready = 1; - if (priv->iw_mode != IEEE80211_IF_TYPE_IBSS) - mutex_unlock(&priv->mutex); + rs_initialize_lq(priv, conf, 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) +static void rs_fill_link_cmd(struct iwl4965_rate_scale_priv *lq_data, + struct iwl4965_rate *tx_mcs, + struct iwl4965_link_quality_cmd *lq_cmd) { int index = 0; - int rc = 0; int rate_idx; + int repeat_rate = 0; 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 }; + struct iwl4965_rate new_rate; + struct iwl4965_scale_tbl_info tbl_type = { 0 }; + /* Override starting rate (index 0) if needed for debug purposes */ + rs_dbgfs_set_mcs(lq_data, tx_mcs, index); + + /* Interpret rate_n_flags */ rs_get_tbl_info_from_mcs(tx_mcs, lq_data->phymode, &tbl_type, &rate_idx); + /* How many times should we repeat the initial rate? */ if (is_legacy(tbl_type.lq_type)) { ant_toggle_count = 1; - repeat_cur_rate = IWL_NUMBER_TRY; + repeat_rate = IWL_NUMBER_TRY; } else - repeat_cur_rate = IWL_HT_NUMBER_TRY; + repeat_rate = IWL_HT_NUMBER_TRY; + + lq_cmd->general_params.mimo_delimiter = + is_mimo(tbl_type.lq_type) ? 1 : 0; - lq_cmd->rate_scale_table[index].rate_n_flags = tx_mcs->rate_n_flags; - lq_cmd->general_params.mimo_delimiter = is_mimo(tbl_type.lq_type) ? 1 : 0; + /* Fill 1st table entry (index 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; + lq_cmd->general_params.single_stream_ant_msk + = LINK_QUAL_ANT_A_MSK; else - lq_cmd->general_params.single_stream_ant_msk = 2; + lq_cmd->general_params.single_stream_ant_msk + = LINK_QUAL_ANT_B_MSK; index++; - repeat_cur_rate--; + repeat_rate--; + /* Fill rest of rate table */ 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) + /* Repeat initial/next rate. + * For legacy IWL_NUMBER_TRY == 1, this loop will not execute. + * For HT IWL_HT_NUMBER_TRY == 3, this executes twice. */ + while (repeat_rate > 0 && (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->rate_scale_table[index].rate_n_flags = - new_rate.rate_n_flags; - repeat_cur_rate--; + + /* Override next rate if needed for debug purposes */ + rs_dbgfs_set_mcs(lq_data, &new_rate, index); + + /* Fill next table entry */ + lq_cmd->rs_table[index].rate_n_flags = + cpu_to_le32(new_rate.rate_n_flags); + repeat_rate--; index++; } - rs_get_tbl_info_from_mcs(&lq_cmd->rate_scale_table[index - 1], - lq_data->phymode, - &tbl_type, &rate_idx); + + rs_get_tbl_info_from_mcs(&new_rate, lq_data->phymode, &tbl_type, + &rate_idx); + + /* Indicate to uCode which entries might be MIMO. + * If initial rate was MIMO, this will finally end up + * as (IWL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */ 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); + /* Get next rate */ + rs_get_lower_rate(lq_data, &tbl_type, rate_idx, + use_ht_possible, &new_rate); + /* How many times should we repeat the next rate? */ if (is_legacy(tbl_type.lq_type)) { if (ant_toggle_count < NUM_TRY_BEFORE_ANTENNA_TOGGLE) ant_toggle_count++; @@ -1909,55 +2297,228 @@ static int rs_fill_link_cmd(struct iwl_rate_scale_priv *lq_data, 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; - } + repeat_rate = IWL_NUMBER_TRY; + } else + repeat_rate = IWL_HT_NUMBER_TRY; + /* Don't allow HT rates after next pass. + * rs_get_lower_rate() will change type to LQ_A or LQ_G. */ use_ht_possible = 0; - lq_cmd->rate_scale_table[index].rate_n_flags = - new_rate.rate_n_flags; - /*lq_cmd->rate_scale_table[index].rate_n_flags = 0x800d; */ + /* Override next rate if needed for debug purposes */ + rs_dbgfs_set_mcs(lq_data, &new_rate, index); + + /* Fill next table entry */ + lq_cmd->rs_table[index].rate_n_flags = + cpu_to_le32(new_rate.rate_n_flags); index++; - repeat_cur_rate--; + repeat_rate--; } - /*lq_cmd->rate_scale_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 = 4000; - return rc; + lq_cmd->agg_params.agg_time_limit = cpu_to_le16(4000); } static void *rs_alloc(struct ieee80211_local *local) { - IWL_DEBUG_RATE("enter\n"); - IWL_DEBUG_RATE("leave\n"); return local->hw.priv; } +/* rate scale requires free function to be implemented */ +static void rs_free(void *priv_rate) +{ + return; +} -static void rs_free(void *data) +static void rs_clear(void *priv_rate) { + struct iwl4965_priv *priv = (struct iwl4965_priv *) priv_rate; + IWL_DEBUG_RATE("enter\n"); + + priv->lq_mngr.lq_ready = 0; +#ifdef CONFIG_IWL4965_HT +#ifdef CONFIG_IWL4965_HT_AGG + if (priv->lq_mngr.agg_ctrl.granted_ba) + iwl4965_turn_off_agg(priv, TID_ALL_SPECIFIED); +#endif /*CONFIG_IWL4965_HT_AGG */ +#endif /* CONFIG_IWL4965_HT */ + IWL_DEBUG_RATE("leave\n"); } static void rs_free_sta(void *priv, void *priv_sta) { - struct iwl_rate_scale_priv *rs_priv = priv_sta; + struct iwl4965_rate_scale_priv *rs_priv = priv_sta; IWL_DEBUG_RATE("enter\n"); kfree(rs_priv); IWL_DEBUG_RATE("leave\n"); } -static void rs_clear(void *priv) + +#ifdef CONFIG_MAC80211_DEBUGFS +static int open_file_generic(struct inode *inode, struct file *file) { - IWL_DEBUG_RATE("NOP\n"); + file->private_data = inode->i_private; + return 0; } +static void rs_dbgfs_set_mcs(struct iwl4965_rate_scale_priv *rs_priv, + struct iwl4965_rate *mcs, int index) +{ + u32 base_rate; + + if (rs_priv->phymode == (u8) MODE_IEEE80211A) + base_rate = 0x800D; + else + base_rate = 0x820A; + + if (rs_priv->dbg_fixed.rate_n_flags) { + if (index < 12) + mcs->rate_n_flags = rs_priv->dbg_fixed.rate_n_flags; + else + mcs->rate_n_flags = base_rate; + IWL_DEBUG_RATE("Fixed rate ON\n"); + return; + } + + IWL_DEBUG_RATE("Fixed rate OFF\n"); +} + +static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct iwl4965_rate_scale_priv *rs_priv = file->private_data; + char buf[64]; + int buf_size; + u32 parsed_rate; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + if (sscanf(buf, "%x", &parsed_rate) == 1) + rs_priv->dbg_fixed.rate_n_flags = parsed_rate; + else + rs_priv->dbg_fixed.rate_n_flags = 0; + + rs_priv->active_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */ + rs_priv->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ + rs_priv->active_mimo_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ + + IWL_DEBUG_RATE("sta_id %d rate 0x%X\n", + rs_priv->lq.sta_id, rs_priv->dbg_fixed.rate_n_flags); + + if (rs_priv->dbg_fixed.rate_n_flags) { + rs_fill_link_cmd(rs_priv, &rs_priv->dbg_fixed, &rs_priv->lq); + rs_send_lq_cmd(rs_priv->drv, &rs_priv->lq, CMD_ASYNC); + } + + return count; +} + +static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char buff[1024]; + int desc = 0; + int i = 0; + + struct iwl4965_rate_scale_priv *rs_priv = file->private_data; + + desc += sprintf(buff+desc, "sta_id %d\n", rs_priv->lq.sta_id); + desc += sprintf(buff+desc, "failed=%d success=%d rate=0%X\n", + rs_priv->total_failed, rs_priv->total_success, + rs_priv->active_rate); + desc += sprintf(buff+desc, "fixed rate 0x%X\n", + rs_priv->dbg_fixed.rate_n_flags); + desc += sprintf(buff+desc, "general:" + "flags=0x%X mimo-d=%d s-ant0x%x d-ant=0x%x\n", + rs_priv->lq.general_params.flags, + rs_priv->lq.general_params.mimo_delimiter, + rs_priv->lq.general_params.single_stream_ant_msk, + rs_priv->lq.general_params.dual_stream_ant_msk); + + desc += sprintf(buff+desc, "agg:" + "time_limit=%d dist_start_th=%d frame_cnt_limit=%d\n", + le16_to_cpu(rs_priv->lq.agg_params.agg_time_limit), + rs_priv->lq.agg_params.agg_dis_start_th, + rs_priv->lq.agg_params.agg_frame_cnt_limit); + + desc += sprintf(buff+desc, + "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n", + rs_priv->lq.general_params.start_rate_index[0], + rs_priv->lq.general_params.start_rate_index[1], + rs_priv->lq.general_params.start_rate_index[2], + rs_priv->lq.general_params.start_rate_index[3]); + + + for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) + desc += sprintf(buff+desc, " rate[%d] 0x%X\n", + i, le32_to_cpu(rs_priv->lq.rs_table[i].rate_n_flags)); + + return simple_read_from_buffer(user_buf, count, ppos, buff, desc); +} + +static const struct file_operations rs_sta_dbgfs_scale_table_ops = { + .write = rs_sta_dbgfs_scale_table_write, + .read = rs_sta_dbgfs_scale_table_read, + .open = open_file_generic, +}; +static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char buff[1024]; + int desc = 0; + int i, j; + + struct iwl4965_rate_scale_priv *rs_priv = file->private_data; + for (i = 0; i < LQ_SIZE; i++) { + desc += sprintf(buff+desc, "%s type=%d SGI=%d FAT=%d DUP=%d\n" + "rate=0x%X\n", + rs_priv->active_tbl == i?"*":"x", + rs_priv->lq_info[i].lq_type, + rs_priv->lq_info[i].is_SGI, + rs_priv->lq_info[i].is_fat, + rs_priv->lq_info[i].is_dup, + rs_priv->lq_info[i].current_rate.rate_n_flags); + for (j = 0; j < IWL_RATE_COUNT; j++) { + desc += sprintf(buff+desc, + "counter=%d success=%d %%=%d\n", + rs_priv->lq_info[i].win[j].counter, + rs_priv->lq_info[i].win[j].success_counter, + rs_priv->lq_info[i].win[j].success_ratio); + } + } + return simple_read_from_buffer(user_buf, count, ppos, buff, desc); +} + +static const struct file_operations rs_sta_dbgfs_stats_table_ops = { + .read = rs_sta_dbgfs_stats_table_read, + .open = open_file_generic, +}; + +static void rs_add_debugfs(void *priv, void *priv_sta, + struct dentry *dir) +{ + struct iwl4965_rate_scale_priv *rs_priv = priv_sta; + rs_priv->rs_sta_dbgfs_scale_table_file = + debugfs_create_file("rate_scale_table", 0600, dir, + rs_priv, &rs_sta_dbgfs_scale_table_ops); + rs_priv->rs_sta_dbgfs_stats_table_file = + debugfs_create_file("rate_stats_table", 0600, dir, + rs_priv, &rs_sta_dbgfs_stats_table_ops); +} + +static void rs_remove_debugfs(void *priv, void *priv_sta) +{ + struct iwl4965_rate_scale_priv *rs_priv = priv_sta; + debugfs_remove(rs_priv->rs_sta_dbgfs_scale_table_file); + debugfs_remove(rs_priv->rs_sta_dbgfs_stats_table_file); +} +#endif static struct rate_control_ops rs_ops = { .module = NULL, @@ -1970,13 +2531,17 @@ static struct rate_control_ops rs_ops = { .free = rs_free, .alloc_sta = rs_alloc_sta, .free_sta = rs_free_sta, +#ifdef CONFIG_MAC80211_DEBUGFS + .add_sta_debugfs = rs_add_debugfs, + .remove_sta_debugfs = rs_remove_debugfs, +#endif }; -int iwl_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id) +int iwl4965_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 iwl4965_priv *priv = hw->priv; + struct iwl4965_rate_scale_priv *rs_priv; struct sta_info *sta; int count = 0, i; u32 samples = 0, success = 0, good = 0; @@ -2009,7 +2574,7 @@ int iwl_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id) int active = rs_priv->active_tbl; count += - sprintf(&buf[count], " %2dMbs: ", iwl_rates[i].ieee / 2); + sprintf(&buf[count], " %2dMbs: ", iwl4965_rates[i].ieee / 2); mask = (1ULL << (IWL_RATE_MAX_WINDOW - 1)); for (j = 0; j < IWL_RATE_MAX_WINDOW; j++, mask >>= 1) @@ -2020,7 +2585,7 @@ int iwl_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id) 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; + iwl4965_rates[i].ieee; if (rs_priv->lq_info[active].win[i].stamp) { int delta = @@ -2034,60 +2599,45 @@ int iwl_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id) } else buf[count++] = '\n'; - j = iwl_get_prev_ieee_rate(i); + j = iwl4965_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 */ + * NOTE: We multiply # of samples by 2 since the IEEE measurement + * added from iwl4965_rates is actually 2X the rate */ if (samples) - count += sprintf( - &buf[count], + 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); + count += sprintf(&buf[count], "\nrate scale type %d antenna %d " + "active_search %d rate index %d\n", lq_type, antenna, + rs_priv->search_better_tbl, sta->last_txrate); + sta_info_put(sta); return count; } -void iwl_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id) +void iwl4965_rate_scale_init(struct ieee80211_hw *hw, s32 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; - - 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; - } + struct iwl4965_priv *priv = hw->priv; - rs_priv = (void *)sta->rate_ctrl_priv; - rs_priv->ready = 1; - sta_info_put(sta); + priv->lq_mngr.lq_ready = 1; } -void iwl_rate_control_register(void) +void iwl4965_rate_control_register(struct ieee80211_hw *hw) { ieee80211_rate_control_register(&rs_ops); } -void iwl_rate_control_unregister(void) +void iwl4965_rate_control_unregister(struct ieee80211_hw *hw) { ieee80211_rate_control_unregister(&rs_ops); } diff --git a/drivers/net/wireless/iwlwifi/iwl-4965-rs.h b/drivers/net/wireless/iwlwifi/iwl-4965-rs.h index b1fb0f0..31e21e2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965-rs.h +++ b/drivers/net/wireless/iwlwifi/iwl-4965-rs.h @@ -27,14 +27,13 @@ #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; +struct iwl4965_rate_info { + u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */ + u8 plcp_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */ + u8 plcp_mimo; /* uCode API: IWL_RATE_MIMO_6M_PLCP, etc. */ + u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */ 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 */ @@ -43,6 +42,10 @@ struct iwl_rate_info { u8 next_rs_tgg; /* next rate used in TGG rs algo */ }; +/* + * These serve as indexes into + * struct iwl4965_rate_info iwl4965_rates[IWL_RATE_COUNT]; + */ enum { IWL_RATE_1M_INDEX = 0, IWL_RATE_2M_INDEX, @@ -84,6 +87,7 @@ enum { #define IWL_RATE_5M_MASK (1<<IWL_RATE_5M_INDEX) #define IWL_RATE_11M_MASK (1<<IWL_RATE_11M_INDEX) +/* 4965 uCode API values for legacy bit rates, both OFDM and CCK */ enum { IWL_RATE_6M_PLCP = 13, IWL_RATE_9M_PLCP = 15, @@ -100,7 +104,7 @@ enum { IWL_RATE_11M_PLCP = 110, }; -/* OFDM HT rate plcp */ +/* 4965 uCode API values for OFDM high-throughput (HT) bit rates */ enum { IWL_RATE_SISO_6M_PLCP = 0, IWL_RATE_SISO_12M_PLCP = 1, @@ -122,6 +126,7 @@ enum { IWL_RATE_MIMO_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP, }; +/* MAC header values for bit rates */ enum { IWL_RATE_6M_IEEE = 12, IWL_RATE_9M_IEEE = 18, @@ -171,116 +176,114 @@ enum { #define IWL_MIN_RSSI_VAL -100 #define IWL_MAX_RSSI_VAL 0 -extern const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT]; +/* These values specify how many Tx frame attempts before + * searching for a new modulation mode */ +#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 + +/* Success ratio (ACKed / attempted tx frames) values (perfect is 128 * 100) */ +#define IWL_RS_GOOD_RATIO 12800 /* 100% */ +#define IWL_RATE_SCALE_SWITCH 10880 /* 85% */ +#define IWL_RATE_HIGH_TH 10880 /* 85% */ +#define IWL_RATE_INCREASE_TH 8960 /* 70% */ +#define IWL_RATE_DECREASE_TH 1920 /* 15% */ + +/* possible actions when in legacy mode */ +#define IWL_LEGACY_SWITCH_ANTENNA 0 +#define IWL_LEGACY_SWITCH_SISO 1 +#define IWL_LEGACY_SWITCH_MIMO 2 + +/* possible actions when in siso mode */ +#define IWL_SISO_SWITCH_ANTENNA 0 +#define IWL_SISO_SWITCH_MIMO 1 +#define IWL_SISO_SWITCH_GI 2 -#define LQ_SIZE 2 +/* possible actions when in mimo mode */ +#define IWL_MIMO_SWITCH_ANTENNA_A 0 +#define IWL_MIMO_SWITCH_ANTENNA_B 1 +#define IWL_MIMO_SWITCH_GI 2 -enum iwl_table_type { +#define IWL_ACTION_LIMIT 3 /* # possible actions */ + +#define LQ_SIZE 2 /* 2 mode tables: "Active" and "Search" */ + +extern const struct iwl4965_rate_info iwl4965_rates[IWL_RATE_COUNT]; + +enum iwl4965_table_type { LQ_NONE, - LQ_G, + LQ_G, /* legacy types */ LQ_A, - LQ_SISO, + LQ_SISO, /* high-throughput types */ LQ_MIMO, LQ_MAX, }; -enum iwl_antenna_type { +#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)) + +/* 4965 has 2 antennas/chains for Tx (but 3 for Rx) */ +enum iwl4965_antenna_type { ANT_NONE, ANT_MAIN, ANT_AUX, ANT_BOTH, }; -static inline int iwl_rate_index_from_plcp(int plcp) +static inline u8 iwl4965_get_prev_ieee_rate(u8 rate_index) { - int i = 0; - - if (plcp & RATE_MCS_HT_MSK) { - i = (plcp & 0xff); - - if (i >= IWL_RATE_MIMO_6M_PLCP) - i = i - IWL_RATE_MIMO_6M_PLCP; - - i += IWL_FIRST_OFDM_RATE; - /* skip 9M not supported in ht*/ - if (i >= IWL_RATE_9M_INDEX) - i += 1; - if ((i >= IWL_FIRST_OFDM_RATE) && - (i <= IWL_LAST_OFDM_RATE)) - return i; - } else { - for (i = 0; i < ARRAY_SIZE(iwl_rates); i++) - if (iwl_rates[i].plcp == (plcp &0xFF)) - return i; - } - return -1; -} - -static inline u8 iwl_rate_get_lowest_plcp(int rate_mask) -{ - u8 i; - - for (i = IWL_RATE_1M_INDEX; i != IWL_RATE_INVALID; - i = iwl_rates[i].next_ieee) { - if (rate_mask & (1 << i)) - return iwl_rates[i].plcp; - } + u8 rate = iwl4965_rates[rate_index].prev_ieee; - 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 +extern int iwl4965_rate_index_from_plcp(int plcp); + /** - * iwl_fill_rs_info - Fill an output text buffer with the rate representation + * iwl4965_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 + * the performance of the rate control algorithm and is not meant to be * parsed software. */ -extern int iwl_fill_rs_info(struct ieee80211_hw *, char *buf, u8 sta_id); +extern int iwl4965_fill_rs_info(struct ieee80211_hw *, char *buf, u8 sta_id); /** - * iwl_rate_scale_init - Initialize the rate scale table based on assoc info + * iwl4965_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 specific throughput 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); +extern void iwl4965_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id); /** - * iwl_rate_control_register - Register the rate control algorithm callbacks + * iwl4965_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 + * iwl4965_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(void); +extern void iwl4965_rate_control_register(struct ieee80211_hw *hw); /** - * iwl_rate_control_unregister - Unregister the rate control callbacks + * iwl4965_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(void); -#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(void) {} -static inline void iwl_rate_control_unregister(void) {} -#endif /* IWL == 4965 */ +extern void iwl4965_rate_control_unregister(struct ieee80211_hw *hw); #endif diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index d13ccc9..83cef93 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -35,14 +35,13 @@ #include <linux/netdevice.h> #include <linux/wireless.h> #include <net/mac80211.h> -#include <linux/netdevice.h> #include <linux/etherdevice.h> -#include <linux/delay.h> -#include "iwlwifi.h" #include "iwl-4965.h" #include "iwl-helpers.h" +static void iwl4965_hw_card_show_info(struct iwl4965_priv *priv); + #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, \ @@ -63,13 +62,13 @@ * maps to IWL_RATE_INVALID * */ -const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT] = { +const struct iwl4965_rate_info iwl4965_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, 5, 12, 5, 18), /* 11mbps */ + 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, 5, 11, 5, 11), /* 9mbps */ + 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 */ @@ -79,22 +78,22 @@ const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT] = { IWL_DECLARE_RATE_INFO(60, 60, 48, INV, 48, INV, 48, INV),/* 60mbps */ }; -static int is_fat_channel(struct iwl_priv *priv) +static int is_fat_channel(__le32 rxon_flags) { - return ((priv->active_rxon.flags & RXON_FLG_CHANNEL_MODE_PURE_40_MSK) || - (priv->active_rxon.flags & RXON_FLG_CHANNEL_MODE_MIXED_MSK)); + 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) +static u8 is_single_stream(struct iwl4965_priv *priv) { -#ifdef CONFIG_IWLWIFI_HT - if (!priv->is_ht_enabled || !priv->current_assoc_ht.is_ht || - (priv->active_rate_ht[1] == 0) || +#ifdef CONFIG_IWL4965_HT + if (!priv->current_ht_config.is_ht || + (priv->current_ht_config.supp_mcs_set[1] == 0) || (priv->ps_mode == IWL_MIMO_PS_STATIC)) return 1; #else return 1; -#endif /*CONFIG_IWLWIFI_HT */ +#endif /*CONFIG_IWL4965_HT */ return 0; } @@ -104,11 +103,11 @@ static u8 is_single_stream(struct iwl_priv *priv) * 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) +static int iwl4965_get_rx_chain_counter(struct iwl4965_priv *priv, + u8 *idle_state, u8 *rx_state) { u8 is_single = is_single_stream(priv); - u8 is_cam = (priv->status & STATUS_POWER_PMI) ? 0 : 1; + 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))) @@ -133,163 +132,172 @@ static int iwl4965_get_rx_chain_counter(struct iwl_priv *priv, return 0; } -int iwl_hw_rxq_stop(struct iwl_priv *priv) +int iwl4965_hw_rxq_stop(struct iwl4965_priv *priv) { int rc; unsigned long flags; spin_lock_irqsave(&priv->lock, flags); - rc = iwl_grab_restricted_access(priv); + rc = iwl4965_grab_nic_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, + /* stop Rx DMA */ + iwl4965_write_direct32(priv, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); + rc = iwl4965_poll_direct_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); + iwl4965_release_nic_access(priv); spin_unlock_irqrestore(&priv->lock, flags); return 0; } -u8 iwl_hw_find_station(struct iwl_priv * priv, const u8 * bssid) +u8 iwl4965_hw_find_station(struct iwl4965_priv *priv, const u8 *addr) { int i; int start = 0; int ret = IWL_INVALID_STATION; unsigned long flags; + DECLARE_MAC_BUF(mac); 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; + if (is_broadcast_ether_addr(addr)) + return IWL4965_BROADCAST_ID; spin_lock_irqsave(&priv->sta_lock, flags); - for (i = start; i < (start + priv->num_stations); i++) + for (i = start; i < priv->hw_setting.max_stations; i++) if ((priv->stations[i].used) && (!compare_ether_addr - (priv->stations[i].sta.sta.addr, bssid))) { + (priv->stations[i].sta.sta.addr, addr))) { ret = i; goto out; } - IWL_DEBUG_ASSOC("can not find STA " MAC_FMT " total %d\n", - MAC_ARG(bssid), priv->num_stations); + IWL_DEBUG_ASSOC_LIMIT("can not find STA %s total %d\n", + print_mac(mac, addr), 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) +static int iwl4965_nic_set_pwr_src(struct iwl4965_priv *priv, int pwr_max) { - int rc = 0; + int ret; unsigned long flags; spin_lock_irqsave(&priv->lock, flags); - rc = iwl_grab_restricted_access(priv); - if (rc) { + ret = iwl4965_grab_nic_access(priv); + if (ret) { spin_unlock_irqrestore(&priv->lock, flags); - return rc; + return ret; } if (!pwr_max) { u32 val; - rc = pci_read_config_dword(priv->pci_dev, 0x0C8, &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); + ret = pci_read_config_dword(priv->pci_dev, PCI_POWER_SOURCE, + &val); - } - } 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); - - } + if (val & PCI_CFG_PMC_PME_FROM_D3COLD_SUPPORT) + iwl4965_set_bits_mask_prph(priv, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_PWR_SRC_VAUX, + ~APMG_PS_CTRL_MSK_PWR_SRC); + } else + iwl4965_set_bits_mask_prph(priv, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, + ~APMG_PS_CTRL_MSK_PWR_SRC); - iwl_release_restricted_access(priv); + iwl4965_release_nic_access(priv); spin_unlock_irqrestore(&priv->lock, flags); - return rc; + return ret; } -static int iwl4965_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq) +static int iwl4965_rx_init(struct iwl4965_priv *priv, struct iwl4965_rx_queue *rxq) { int rc; unsigned long flags; + unsigned int rb_size; spin_lock_irqsave(&priv->lock, flags); - rc = iwl_grab_restricted_access(priv); + rc = iwl4965_grab_nic_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); + if (iwl4965_param_amsdu_size_8K) + rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K; + else + rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K; + + /* Stop Rx DMA */ + iwl4965_write_direct32(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, + /* Reset driver's Rx queue write index */ + iwl4965_write_direct32(priv, FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0); + + /* Tell device where to find RBD circular buffer in DRAM */ + iwl4965_write_direct32(priv, FH_RSCSR_CHNL0_RBDCB_BASE_REG, rxq->dma_addr >> 8); - iwl_write_restricted(priv, FH_RSCSR_CHNL0_STTS_WPTR_REG, + /* Tell device where in DRAM to update its Rx status */ + iwl4965_write_direct32(priv, FH_RSCSR_CHNL0_STTS_WPTR_REG, (priv->hw_setting.shared_phys + - offsetof(struct iwl_shared, val0)) >> 4); + offsetof(struct iwl4965_shared, val0)) >> 4); - iwl_write_restricted(priv, FH_MEM_RCSR_CHNL0_CONFIG_REG, + /* Enable Rx DMA, enable host interrupt, Rx buffer size 4k, 256 RBDs */ + iwl4965_write_direct32(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 | + rb_size | /*0x10 << 4 | */ (RX_QUEUE_SIZE_LOG << FH_RCSR_RX_CONFIG_RBDCB_SIZE_BITSHIFT)); /* - * iwl_write32(priv,CSR_INT_COAL_REG,0); + * iwl4965_write32(priv,CSR_INT_COAL_REG,0); */ - iwl_release_restricted_access(priv); - + iwl4965_release_nic_access(priv); spin_unlock_irqrestore(&priv->lock, flags); return 0; } -static int iwl4965_kw_init(struct iwl_priv *priv) +/* Tell 4965 where to find the "keep warm" buffer */ +static int iwl4965_kw_init(struct iwl4965_priv *priv) { unsigned long flags; int rc; spin_lock_irqsave(&priv->lock, flags); - rc = iwl_grab_restricted_access(priv); + rc = iwl4965_grab_nic_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); + iwl4965_write_direct32(priv, IWL_FH_KW_MEM_ADDR_REG, + priv->kw.dma_addr >> 4); + iwl4965_release_nic_access(priv); out: spin_unlock_irqrestore(&priv->lock, flags); return rc; } -static int iwl4965_kw_alloc(struct iwl_priv *priv) +static int iwl4965_kw_alloc(struct iwl4965_priv *priv) { struct pci_dev *dev = priv->pci_dev; - struct iwl_kw *kw = &priv->kw; + struct iwl4965_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) @@ -301,38 +309,39 @@ static int iwl4965_kw_alloc(struct iwl_priv *priv) #define CHECK_AND_PRINT(x) ((eeprom_ch->flags & EEPROM_CHANNEL_##x) \ ? # x " " : "") -int iwl4965_set_fat_chan_info(struct iwl_priv *priv, int phymode, - int channel, - const struct iwl_eeprom_channel *eeprom_ch, +/** + * iwl4965_set_fat_chan_info - Copy fat channel info into driver's priv. + * + * Does not set up a command, or touch hardware. + */ +int iwl4965_set_fat_chan_info(struct iwl4965_priv *priv, int phymode, u16 channel, + const struct iwl4965_eeprom_channel *eeprom_ch, u8 fat_extension_channel) { - struct iwl_channel_info *ch_info; + struct iwl4965_channel_info *ch_info; + + ch_info = (struct iwl4965_channel_info *) + iwl4965_get_channel_info(priv, phymode, channel); - 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(" BIT_FMT8 - " %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), - BIT_ARG8(eeprom_ch->flags), - eeprom_ch-> - max_power_avg, - ((eeprom_ch-> - flags & EEPROM_CHANNEL_IBSS) - && !(eeprom_ch-> - flags & EEPROM_CHANNEL_RADAR)) - ? "" : "not "); + 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; @@ -345,15 +354,18 @@ int iwl4965_set_fat_chan_info(struct iwl_priv *priv, int phymode, return 0; } -static void iwl4965_kw_free(struct iwl_priv *priv) +/** + * iwl4965_kw_free - Free the "keep warm" buffer + */ +static void iwl4965_kw_free(struct iwl4965_priv *priv) { struct pci_dev *dev = priv->pci_dev; - struct iwl_kw *kw = &priv->kw; + struct iwl4965_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)); } - } /** @@ -363,17 +375,18 @@ static void iwl4965_kw_free(struct iwl_priv *priv) * @param priv * @return error code */ -static int iwl4965_txq_ctx_reset(struct iwl_priv *priv) +static int iwl4965_txq_ctx_reset(struct iwl4965_priv *priv) { int rc = 0; - int txq_id, num_slots; + int txq_id, slots_num; unsigned long flags; iwl4965_kw_free(priv); - iwl_hw_txq_ctx_free(priv); + /* Free all tx/cmd queues and keep-warm buffer */ + iwl4965_hw_txq_ctx_free(priv); - /* Tx CMD queue */ + /* Alloc keep-warm buffer */ rc = iwl4965_kw_alloc(priv); if (rc) { IWL_ERROR("Keep Warm allocation failed"); @@ -382,28 +395,31 @@ static int iwl4965_txq_ctx_reset(struct iwl_priv *priv) spin_lock_irqsave(&priv->lock, flags); - rc = iwl_grab_restricted_access(priv); + rc = iwl4965_grab_nic_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); + /* Turn off all Tx DMA channels */ + iwl4965_write_prph(priv, KDR_SCD_TXFACT, 0); + iwl4965_release_nic_access(priv); spin_unlock_irqrestore(&priv->lock, flags); + /* Tell 4965 where to find the keep-warm buffer */ 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_queue_number; txq_id++) { - num_slots = (txq_id == IWL_CMD_QUEUE_NUM) ? + /* Alloc and init all (default 16) Tx queues, + * including the command queue (#4) */ + 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], num_slots, + rc = iwl4965_tx_queue_init(priv, &priv->txq[txq_id], slots_num, txq_id); if (rc) { IWL_ERROR("Tx %d queue init failed\n", txq_id); @@ -414,32 +430,32 @@ static int iwl4965_txq_ctx_reset(struct iwl_priv *priv) return rc; error: - iwl_hw_txq_ctx_free(priv); + iwl4965_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 iwl4965_hw_nic_init(struct iwl4965_priv *priv) { int rc; unsigned long flags; - struct iwl_rx_queue *rxq = &priv->rxq; + struct iwl4965_rx_queue *rxq = &priv->rxq; u8 rev_id; u32 val; u8 val_link; - iwl_power_init_handle(priv); + iwl4965_power_init_handle(priv); /* nic_init */ spin_lock_irqsave(&priv->lock, flags); - iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS, + iwl4965_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, + iwl4965_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + rc = iwl4965_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) { @@ -448,26 +464,26 @@ int iwl_hw_nic_init(struct iwl_priv *priv) return rc; } - rc = iwl_grab_restricted_access(priv); + rc = iwl4965_grab_nic_access(priv); if (rc) { spin_unlock_irqrestore(&priv->lock, flags); return rc; } - iwl_read_restricted_reg(priv, APMG_CLK_CTRL_REG); + iwl4965_read_prph(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); + iwl4965_write_prph(priv, APMG_CLK_CTRL_REG, + APMG_CLK_VAL_DMA_CLK_RQT | + APMG_CLK_VAL_BSM_CLK_RQT); + iwl4965_read_prph(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); + iwl4965_set_bits_prph(priv, APMG_PCIDEV_STT_REG, + APMG_PCIDEV_STT_VAL_L1_ACT_DIS); - iwl_release_restricted_access(priv); - iwl_write32(priv, CSR_INT_COALESCING, 512 / 32); + iwl4965_release_nic_access(priv); + iwl4965_write32(priv, CSR_INT_COALESCING, 512 / 32); spin_unlock_irqrestore(&priv->lock, flags); /* Determine HW type */ @@ -481,15 +497,16 @@ int iwl_hw_nic_init(struct iwl_priv *priv) spin_lock_irqsave(&priv->lock, flags); if ((rev_id & 0x80) == 0x80 && (rev_id & 0x7f) < 8) { - pci_read_config_dword(priv->pci_dev, 0xe8, &val); + pci_read_config_dword(priv->pci_dev, PCI_REG_WUM8, &val); /* Enable No Snoop field */ - pci_write_config_dword(priv->pci_dev, 0xe8, val & ~(1 << 11)); + 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); + rc = iwl4965_eeprom_init(priv); if (rc) return rc; @@ -507,51 +524,53 @@ int iwl_hw_nic_init(struct iwl_priv *priv) /* set CSR_HW_CONFIG_REG for uCode use */ - iwl_set_bit(priv, CSR_SW_VER, CSR_HW_IF_CONFIG_REG_BIT_KEDRON_R | + iwl4965_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); + rc = iwl4965_grab_nic_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); + iwl4965_read_prph(priv, APMG_PS_CTRL_REG); + iwl4965_set_bits_prph(priv, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_RESET_REQ); udelay(5); - iwl_clear_bits_restricted_reg(priv, ALM_APMG_PS_CTL, - APMG_PS_CTRL_REG_VAL_ALM_R_RESET_REQ); + iwl4965_clear_bits_prph(priv, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_RESET_REQ); - iwl_release_restricted_access(priv); + iwl4965_release_nic_access(priv); spin_unlock_irqrestore(&priv->lock, flags); - iwl_hw_card_show_info(priv); + iwl4965_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); + rc = iwl4965_rx_queue_alloc(priv); if (rc) { IWL_ERROR("Unable to initialize Rx queue\n"); return -ENOMEM; } } else - iwl_rx_queue_reset(priv, rxq); + iwl4965_rx_queue_reset(priv, rxq); - iwl_rx_replenish(priv, 1); + iwl4965_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); + iwl4965_rx_queue_update_write_ptr(priv, rxq); spin_unlock_irqrestore(&priv->lock, flags); + + /* Allocate and init all Tx and Command queues */ rc = iwl4965_txq_ctx_reset(priv); if (rc) return rc; @@ -562,12 +581,12 @@ int iwl_hw_nic_init(struct iwl_priv *priv) if (priv->eeprom.sku_cap & EEPROM_SKU_CAP_HW_RF_KILL_ENABLE) IWL_DEBUG_RF_KILL("HW RF KILL supported in EEPROM.\n"); - priv->status |= STATUS_INIT; + set_bit(STATUS_INIT, &priv->status); return 0; } -int iwl_hw_nic_stop_master(struct iwl_priv *priv) +int iwl4965_hw_nic_stop_master(struct iwl4965_priv *priv) { int rc = 0; u32 reg_val; @@ -576,17 +595,16 @@ int iwl_hw_nic_stop_master(struct iwl_priv *priv) spin_lock_irqsave(&priv->lock, flags); /* set stop master bit */ - iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER); + iwl4965_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER); - reg_val = iwl_read32(priv, CSR_GP_CNTRL); + reg_val = iwl4965_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, + (reg_val & CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE)) + IWL_DEBUG_INFO("Card in power save, master is already " + "stopped\n"); + else { + rc = iwl4965_poll_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_MASTER_DISABLED, CSR_RESET_REG_FLAG_MASTER_DISABLED, 100); if (rc < 0) { @@ -601,73 +619,72 @@ int iwl_hw_nic_stop_master(struct iwl_priv *priv) return rc; } -void iwl_hw_txq_ctx_stop(struct iwl_priv *priv) +/** + * iwl4965_hw_txq_ctx_stop - Stop all Tx DMA channels, free Tx queue memory + */ +void iwl4965_hw_txq_ctx_stop(struct iwl4965_priv *priv) { int txq_id; unsigned long flags; - /* reset TFD queues */ - for (txq_id = 0; txq_id < IWL4965_NUM_QUEUES; txq_id++) { + /* Stop each Tx DMA channel, and wait for it to be idle */ + 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)) { + if (iwl4965_grab_nic_access(priv)) { spin_unlock_irqrestore(&priv->lock, flags); continue; } - iwl_write_restricted(priv, + iwl4965_write_direct32(priv, IWL_FH_TCSR_CHNL_TX_CONFIG_REG(txq_id), 0x0); - iwl_poll_restricted_bit(priv, IWL_FH_TSSR_TX_STATUS_REG, + iwl4965_poll_direct_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); + iwl4965_release_nic_access(priv); spin_unlock_irqrestore(&priv->lock, flags); } - iwl_hw_txq_ctx_free(priv); + /* Deallocate memory for all Tx queues */ + iwl4965_hw_txq_ctx_free(priv); } -int iwl_hw_nic_reset(struct iwl_priv *priv) +int iwl4965_hw_nic_reset(struct iwl4965_priv *priv) { int rc = 0; unsigned long flags; - iwl_hw_nic_stop_master(priv); + iwl4965_hw_nic_stop_master(priv); spin_lock_irqsave(&priv->lock, flags); - iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); + iwl4965_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, + iwl4965_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + rc = iwl4965_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); + rc = iwl4965_grab_nic_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); + iwl4965_write_prph(priv, APMG_CLK_EN_REG, + APMG_CLK_VAL_DMA_CLK_RQT | + APMG_CLK_VAL_BSM_CLK_RQT); udelay(10); - iwl_set_bits_restricted_reg( - priv, ALM_APMG_PCIDEV_STT, - APMG_DEV_STATE_REG_VAL_L1_ACTIVE_DISABLE); + iwl4965_set_bits_prph(priv, APMG_PCIDEV_STT_REG, + APMG_PCIDEV_STT_VAL_L1_ACT_DIS); - iwl_release_restricted_access(priv); + iwl4965_release_nic_access(priv); } - spin_unlock_irqrestore(&priv->lock, flags); - - spin_lock_irqsave(&priv->lock, flags); - - priv->status &= ~STATUS_HCMD_ACTIVE; + clear_bit(STATUS_HCMD_ACTIVE, &priv->status); wake_up_interruptible(&priv->wait_command_queue); spin_unlock_irqrestore(&priv->lock, flags); @@ -694,7 +711,7 @@ int iwl_hw_nic_reset(struct iwl_priv *priv) */ static void iwl4965_bg_statistics_periodic(unsigned long data) { - struct iwl_priv *priv = (struct iwl_priv *)data; + struct iwl4965_priv *priv = (struct iwl4965_priv *)data; queue_work(priv->workqueue, &priv->statistics_work); } @@ -702,60 +719,60 @@ static void iwl4965_bg_statistics_periodic(unsigned long data) /** * iwl4965_bg_statistics_work - Send the statistics request to the hardware. * - * This is queued by iwl_bg_statistics_periodic. + * This is queued by iwl4965_bg_statistics_periodic. */ static void iwl4965_bg_statistics_work(void *p) { - struct iwl_priv *priv = p; + struct iwl4965_priv *priv = p; - if (priv->status & STATUS_EXIT_PENDING) + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; mutex_lock(&priv->mutex); - iwl_send_statistics_request(priv); + iwl4965_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) +void iwl4965_rf_kill_ct_config(struct iwl4965_priv *priv) { - struct iwl_ct_kill_config cmd; - u32 R1 = 0, R2 = 0, R3 = 0; - u32 temp_th = 0; + struct iwl4965_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, + iwl4965_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 = priv->card_alive_init.therm_r1[1]; - R2 = priv->card_alive_init.therm_r2[1]; - R3 = priv->card_alive_init.therm_r3[1]; + 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 = priv->card_alive_init.therm_r1[0]; - R2 = priv->card_alive_init.therm_r2[0]; - R3 = priv->card_alive_init.therm_r3[0]; + 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); - cmd.critical_temperature_R = ((temp_th * (R3-R1))/CT_LIMIT_CONST) + R2; - rc = iwl_send_cmd_pdu(priv, + crit_temperature = ((temp_th * (R3-R1))/CT_LIMIT_CONST) + R2; + cmd.critical_temperature_R = cpu_to_le32(crit_temperature); + rc = iwl4965_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_WARNING("REPLY_CT_KILL_CONFIG_CMD succeeded\n"); - - return; + IWL_DEBUG_INFO("REPLY_CT_KILL_CONFIG_CMD succeeded\n"); } -#ifdef CONFIG_IWLWIFI_SENSITIVITY +#ifdef CONFIG_IWL4965_SENSITIVITY /* "false alarms" are signals that our DSP tries to lock onto, * but then determines that they are either noise, or transmissions @@ -765,7 +782,7 @@ void iwl4965_rf_kill_ct_config(struct iwl_priv *priv) * 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, +static int iwl4965_sens_energy_cck(struct iwl4965_priv *priv, u32 norm_fa, u32 rx_enable_time, struct statistics_general_data *rx_info) @@ -791,7 +808,7 @@ static int iwl4965_sens_energy_cck(struct iwl_priv *priv, 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; + struct iwl4965_sensitivity_data *data = NULL; data = &(priv->sensitivity_data); @@ -922,10 +939,8 @@ static int iwl4965_sens_energy_cck(struct iwl_priv *priv, data->auto_corr_cck_mrc = max((u32)AUTO_CORR_MIN_CCK_MRC, val); - } else { + } else IWL_DEBUG_CALIB("... but not changing sensitivity\n"); - } - /* Else we got a healthy number of false alarms, keep status quo */ } else { @@ -945,9 +960,10 @@ static int iwl4965_sens_energy_cck(struct iwl_priv *priv, } /* 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()! */ + * 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); @@ -957,7 +973,7 @@ static int iwl4965_sens_energy_cck(struct iwl_priv *priv, } -static int iwl4965_sens_auto_corr_ofdm(struct iwl_priv *priv, +static int iwl4965_sens_auto_corr_ofdm(struct iwl4965_priv *priv, u32 norm_fa, u32 rx_enable_time) { @@ -965,7 +981,7 @@ static int iwl4965_sens_auto_corr_ofdm(struct iwl_priv *priv, 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; + struct iwl4965_sensitivity_data *data = NULL; data = &(priv->sensitivity_data); @@ -1015,30 +1031,29 @@ static int iwl4965_sens_auto_corr_ofdm(struct iwl_priv *priv, max((u32)AUTO_CORR_MIN_OFDM_MRC_X1, val); } - else { + 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) +static int iwl4965_sensitivity_callback(struct iwl4965_priv *priv, + struct iwl4965_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) +static int iwl4965_sensitivity_write(struct iwl4965_priv *priv, u8 flags) { int rc = 0; - struct iwl_sensitivity_cmd cmd ; - struct iwl_sensitivity_data *data = NULL; - struct iwl_host_cmd cmd_out = { + struct iwl4965_sensitivity_cmd cmd ; + struct iwl4965_sensitivity_data *data = NULL; + struct iwl4965_host_cmd cmd_out = { .id = SENSITIVITY_CMD, - .len = sizeof(struct iwl_sensitivity_cmd), + .len = sizeof(struct iwl4965_sensitivity_cmd), .meta.flags = flags, .data = &cmd, }; @@ -1048,25 +1063,30 @@ static int iwl4965_sensitivity_write(struct iwl_priv *priv, u8 flags) memset(&cmd, 0, sizeof(cmd)); cmd.table[HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX] = - (u16)data->auto_corr_ofdm; + cpu_to_le16((u16)data->auto_corr_ofdm); cmd.table[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX] = - (u16)data->auto_corr_ofdm_mrc; + cpu_to_le16((u16)data->auto_corr_ofdm_mrc); cmd.table[HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX] = - (u16)data->auto_corr_ofdm_x1; + cpu_to_le16((u16)data->auto_corr_ofdm_x1); cmd.table[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX] = - (u16)data->auto_corr_ofdm_mrc_x1; + cpu_to_le16((u16)data->auto_corr_ofdm_mrc_x1); cmd.table[HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX] = - (u16)data->auto_corr_cck; + cpu_to_le16((u16)data->auto_corr_cck); cmd.table[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX] = - (u16)data->auto_corr_cck_mrc; + cpu_to_le16((u16)data->auto_corr_cck_mrc); - cmd.table[HD_MIN_ENERGY_CCK_DET_INDEX] = (u16)data->nrg_th_cck; - cmd.table[HD_MIN_ENERGY_OFDM_DET_INDEX] = (u16)data->nrg_th_ofdm; + 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] = 190; - cmd.table[HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX] = 390; - cmd.table[HD_OFDM_ENERGY_TH_IN_INDEX] = 62; + 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, @@ -1077,10 +1097,11 @@ static int iwl4965_sensitivity_write(struct iwl_priv *priv, u8 flags) data->auto_corr_cck, data->auto_corr_cck_mrc, data->nrg_th_cck); + /* Update uCode's "work" table, and copy it to DSP */ cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE; if (flags & CMD_ASYNC) - cmd_out.meta.u.callback = iwl_sensitivity_callback; + cmd_out.meta.u.callback = iwl4965_sensitivity_callback; /* Don't send command to uCode if nothing has changed */ if (!memcmp(&cmd.table[0], &(priv->sensitivity_tbl[0]), @@ -1093,7 +1114,7 @@ static int iwl4965_sensitivity_write(struct iwl_priv *priv, u8 flags) memcpy(&(priv->sensitivity_tbl[0]), &(cmd.table[0]), sizeof(u16)*HD_TABLE_SIZE); - rc = iwl_send_cmd(priv, &cmd_out); + rc = iwl4965_send_cmd(priv, &cmd_out); if (!rc) { IWL_DEBUG_CALIB("SENSITIVITY_CMD succeeded\n"); return rc; @@ -1102,11 +1123,11 @@ static int iwl4965_sensitivity_write(struct iwl_priv *priv, u8 flags) return 0; } -void iwl4965_init_sensitivity(struct iwl_priv *priv, u8 flags, u8 force) +void iwl4965_init_sensitivity(struct iwl4965_priv *priv, u8 flags, u8 force) { int rc = 0; int i; - struct iwl_sensitivity_data *data = NULL; + struct iwl4965_sensitivity_data *data = NULL; IWL_DEBUG_CALIB("Start iwl4965_init_sensitivity\n"); @@ -1116,7 +1137,7 @@ void iwl4965_init_sensitivity(struct iwl_priv *priv, u8 flags, u8 force) /* Clear driver's sensitivity algo data */ data = &(priv->sensitivity_data); - memset(data, 0, sizeof(struct iwl_sensitivity_data)); + memset(data, 0, sizeof(struct iwl4965_sensitivity_data)); data->num_in_cck_no_fa = 0; data->nrg_curr_state = IWL_FA_TOO_MANY; @@ -1160,21 +1181,21 @@ void iwl4965_init_sensitivity(struct iwl_priv *priv, u8 flags, u8 force) /* Reset differential Rx gains in NIC to prepare for chain noise calibration. * Called after every association, but this runs only once! * ... once chain noise is calibrated the first time, it's good forever. */ -void iwl4965_chain_noise_reset(struct iwl_priv *priv) +void iwl4965_chain_noise_reset(struct iwl4965_priv *priv) { - struct iwl_chain_noise_data *data = NULL; + struct iwl4965_chain_noise_data *data = NULL; int rc = 0; data = &(priv->chain_noise_data); - if ((data->state == IWL_CHAIN_NOISE_ALIVE) && iwl_is_associated(priv)) { - struct iwl_calibration_cmd cmd; + if ((data->state == IWL_CHAIN_NOISE_ALIVE) && iwl4965_is_associated(priv)) { + struct iwl4965_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, + rc = iwl4965_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD, sizeof(cmd), &cmd); msleep(4); data->state = IWL_CHAIN_NOISE_ACCUMULATE; @@ -1189,10 +1210,10 @@ void iwl4965_chain_noise_reset(struct iwl_priv *priv) * 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) +static void iwl4965_noise_calibration(struct iwl4965_priv *priv, + struct iwl4965_notif_statistics *stat_resp) { - struct iwl_chain_noise_data *data = NULL; + struct iwl4965_chain_noise_data *data = NULL; int rc = 0; u32 chain_noise_a; @@ -1218,46 +1239,48 @@ static void iwl4965_noise_calibration(struct iwl_priv *priv, /* 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_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 != 1) { + 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 = priv->staging_rxon.channel; + 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 != (stat_resp->flag >> 16)) || - ((STATISTICS_REPLY_FLG_BAND_24G_MSK == - (stat_resp->flag & STATISTICS_REPLY_FLG_BAND_24G_MSK)) && - band)) { + 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); + chan_num, band); spin_unlock_irqrestore(&priv->lock, flags); return; } /* Accumulate beacon statistics values across 20 beacons */ - chain_noise_a = (rx_info->beacon_silence_rssi_a & IN_BAND_FILTER); - chain_noise_b = (rx_info->beacon_silence_rssi_b & IN_BAND_FILTER); - chain_noise_c = (rx_info->beacon_silence_rssi_c & IN_BAND_FILTER); + 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 = (rx_info->beacon_rssi_a & IN_BAND_FILTER); - chain_sig_b = (rx_info->beacon_rssi_b & IN_BAND_FILTER); - chain_sig_c = (rx_info->beacon_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->beacon_count++; data->chain_noise_a = (chain_noise_a + data->chain_noise_a); data->chain_noise_b = (chain_noise_b + data->chain_noise_b); @@ -1280,12 +1303,9 @@ static void iwl4965_noise_calibration(struct iwl_priv *priv, 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; + 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]; @@ -1311,26 +1331,25 @@ static void iwl4965_noise_calibration(struct iwl_priv *priv, /* Compare signal strengths for all 3 receivers. */ for (i = 0; i < NUM_RX_CHAINS; i++) { if (i != max_average_sig_antenna_i) { - s32 rssiDelta = (max_average_sig - - average_sig[i]); + s32 rssi_delta = (max_average_sig - + average_sig[i]); /* If signal is very weak, compared with * strongest, mark it as disconnected. */ - if (rssiDelta > MAXIMUM_ALLOWED_PATHLOSS) + 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, rssiDelta, - data->disconn_array[i]); + 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]) { data->disconn_array[CHAIN_B] = 0; active_chains |= (1 << CHAIN_B); IWL_DEBUG_CALIB("both A & B chains are disconnected! " @@ -1383,9 +1402,8 @@ static void iwl4965_noise_calibration(struct iwl_priv *priv, data->delta_gain_code[i] = (data->delta_gain_code[i] | (1 << 2)); - } else { + } 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], @@ -1394,7 +1412,7 @@ static void iwl4965_noise_calibration(struct iwl_priv *priv, /* Differential gain gets sent to uCode only once */ if (!data->radio_write) { - struct iwl_calibration_cmd cmd; + struct iwl4965_calibration_cmd cmd; data->radio_write = 1; memset(&cmd, 0, sizeof(cmd)); @@ -1402,7 +1420,7 @@ static void iwl4965_noise_calibration(struct iwl_priv *priv, 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, + rc = iwl4965_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD, sizeof(cmd), &cmd); if (rc) IWL_DEBUG_CALIB("fail sending cmd " @@ -1425,8 +1443,8 @@ static void iwl4965_noise_calibration(struct iwl_priv *priv, return; } -static void iwl4965_sensitivity_calibration(struct iwl_priv *priv, - struct iwl_notif_statistics *resp) +static void iwl4965_sensitivity_calibration(struct iwl4965_priv *priv, + struct iwl4965_notif_statistics *resp) { int rc = 0; u32 rx_enable_time; @@ -1436,7 +1454,7 @@ static void iwl4965_sensitivity_calibration(struct iwl_priv *priv, u32 bad_plcp_ofdm; u32 norm_fa_ofdm; u32 norm_fa_cck; - struct iwl_sensitivity_data *data = NULL; + struct iwl4965_sensitivity_data *data = NULL; struct statistics_rx_non_phy *rx_info = &(resp->rx.general); struct statistics_rx *statistics = &(resp->rx); unsigned long flags; @@ -1444,37 +1462,37 @@ static void iwl4965_sensitivity_calibration(struct iwl_priv *priv, data = &(priv->sensitivity_data); - if (!iwl_is_associated(priv)) { + if (!iwl4965_is_associated(priv)) { IWL_DEBUG_CALIB("<< - not associated\n"); return; } spin_lock_irqsave(&priv->lock, flags); - if (rx_info->interference_data_flag != 1) { + 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 = rx_info->channel_load; - fa_cck = statistics->cck.false_alarm_cnt; - fa_ofdm = statistics->ofdm.false_alarm_cnt; - bad_plcp_cck = statistics->cck.plcp_err; - bad_plcp_ofdm = statistics->ofdm.plcp_err; + 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 = - statistics->general.beacon_silence_rssi_a; + le32_to_cpu(statistics->general.beacon_silence_rssi_a); statis.beacon_silence_rssi_b = - statistics->general.beacon_silence_rssi_b; + le32_to_cpu(statistics->general.beacon_silence_rssi_b); statis.beacon_silence_rssi_c = - statistics->general.beacon_silence_rssi_c; + le32_to_cpu(statistics->general.beacon_silence_rssi_c); statis.beacon_energy_a = - statistics->general.beacon_energy_a; + le32_to_cpu(statistics->general.beacon_energy_a); statis.beacon_energy_b = - statistics->general.beacon_energy_b; + le32_to_cpu(statistics->general.beacon_energy_b); statis.beacon_energy_c = - statistics->general.beacon_energy_c; + le32_to_cpu(statistics->general.beacon_energy_c); spin_unlock_irqrestore(&priv->lock, flags); @@ -1504,7 +1522,7 @@ static void iwl4965_sensitivity_calibration(struct iwl_priv *priv, if (data->last_fa_cnt_ofdm > fa_ofdm) data->last_fa_cnt_ofdm = fa_ofdm; - else{ + else { fa_ofdm -= data->last_fa_cnt_ofdm; data->last_fa_cnt_ofdm += fa_ofdm; } @@ -1524,22 +1542,20 @@ static void iwl4965_sensitivity_calibration(struct iwl_priv *priv, 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); - + 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(void *p) { - struct iwl_priv *priv = p; + struct iwl4965_priv *priv = p; mutex_lock(&priv->mutex); - if ((priv->status & STATUS_EXIT_PENDING) || - (priv->status & STATUS_SCANNING)) { + if (test_bit(STATUS_EXIT_PENDING, &priv->status) || + test_bit(STATUS_SCANNING, &priv->status)) { mutex_unlock(&priv->mutex); return; } @@ -1551,37 +1567,34 @@ static void iwl4965_bg_sensitivity_work(void *p) IWL_SENS_CALIB_NEED_REINIT) { iwl4965_init_sensitivity(priv, CMD_ASYNC, 0); priv->sensitivity_data.state = IWL_SENS_CALIB_ALLOWED; - } else { + } else iwl4965_sensitivity_calibration(priv, - &priv->statistics); - } + &priv->statistics); } mutex_unlock(&priv->mutex); return; } -#endif /*CONFIG_IWLWIFI_SENSITIVITY*/ +#endif /*CONFIG_IWL4965_SENSITIVITY*/ static void iwl4965_bg_txpower_work(void *p) { - struct iwl_priv *priv = p; - - mutex_lock(&priv->mutex); + struct iwl4965_priv *priv = p; /* 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 ((priv->status & STATUS_EXIT_PENDING) || - priv->status & STATUS_SCANNING) { - mutex_unlock(&priv->mutex); + 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); + iwl4965_hw_reg_send_txpower(priv); /* Update last_temperature to keep is_calib_needed from running * when it isn't needed... */ @@ -1590,43 +1603,68 @@ static void iwl4965_bg_txpower_work(void *p) mutex_unlock(&priv->mutex); } -static void iwl4965_set_wr_ptrs(struct iwl_priv *priv, int txq_id, u32 index) +/* + * Acquire priv->lock before calling this function ! + */ +static void iwl4965_set_wr_ptrs(struct iwl4965_priv *priv, int txq_id, u32 index) { - iwl_write_restricted(priv, HBUS_TARG_WRPTR, + iwl4965_write_direct32(priv, HBUS_TARG_WRPTR, (index & 0xff) | (txq_id << 8)); - iwl_write_restricted_reg(priv, SCD_QUEUE_RDPTR(txq_id), index); + iwl4965_write_prph(priv, KDR_SCD_QUEUE_RDPTR(txq_id), index); } -static void iwl4965_tx_queue_set_status(struct iwl_priv *priv, - struct iwl_tx_queue *txq, int tx_fifo_id, - int sched_retry, int active) +/** + * iwl4965_tx_queue_set_status - (optionally) start Tx/Cmd queue + * @tx_fifo_id: Tx DMA/FIFO channel (range 0-7) that the queue will feed + * @scd_retry: (1) Indicates queue will be used in aggregation mode + * + * NOTE: Acquire priv->lock before calling this function ! + */ +static void iwl4965_tx_queue_set_status(struct iwl4965_priv *priv, + struct iwl4965_tx_queue *txq, + int tx_fifo_id, int scd_retry) { int txq_id = txq->q.id; - txq->sched_retry = sched_retry; - txq->sched_retry = active; + /* Find out whether to activate Tx queue */ + int active = test_bit(txq_id, &priv->txq_ctx_active_msk)?1:0; + + /* Set up and activate */ + iwl4965_write_prph(priv, KDR_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); - 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)| - (sched_retry << SCD_QUEUE_STTS_REG_POS_WSL)| - (sched_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"), - sched_retry?"BA":"AC", txq_id, tx_fifo_id); + active ? "Activate" : "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 const u16 default_queue_to_tx_fifo[] = { + IWL_TX_FIFO_AC3, + IWL_TX_FIFO_AC2, + IWL_TX_FIFO_AC1, + IWL_TX_FIFO_AC0, + IWL_CMD_FIFO_NUM, + IWL_TX_FIFO_HCCA_1, + IWL_TX_FIFO_HCCA_2 }; -int iwl4965_alive_notify(struct iwl_priv *priv) +static inline void iwl4965_txq_ctx_activate(struct iwl4965_priv *priv, int txq_id) +{ + set_bit(txq_id, &priv->txq_ctx_active_msk); +} + +static inline void iwl4965_txq_ctx_deactivate(struct iwl4965_priv *priv, int txq_id) +{ + clear_bit(txq_id, &priv->txq_ctx_active_msk); +} + +int iwl4965_alive_notify(struct iwl4965_priv *priv) { u32 a; int i = 0; @@ -1635,49 +1673,55 @@ int iwl4965_alive_notify(struct iwl_priv *priv) spin_lock_irqsave(&priv->lock, flags); -#ifdef CONFIG_IWLWIFI_SENSITIVITY +#ifdef CONFIG_IWL4965_SENSITIVITY memset(&(priv->sensitivity_data), 0, - sizeof(struct iwl_sensitivity_data)); + sizeof(struct iwl4965_sensitivity_data)); memset(&(priv->chain_noise_data), 0, - sizeof(struct iwl_chain_noise_data)); + sizeof(struct iwl4965_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); +#endif /* CONFIG_IWL4965_SENSITIVITY*/ + rc = iwl4965_grab_nic_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); + /* Clear 4965's internal Tx Scheduler data base */ + priv->scd_base_addr = iwl4965_read_prph(priv, KDR_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); + iwl4965_write_targ_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) * IWL4965_NUM_QUEUES; - 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 < IWL4965_NUM_QUEUES; 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 + + iwl4965_write_targ_mem(priv, a, 0); + for (; a < sizeof(u16) * priv->hw_setting.max_txq_num; a += 4) + iwl4965_write_targ_mem(priv, a, 0); + + /* Tel 4965 where to find Tx byte count tables */ + iwl4965_write_prph(priv, KDR_SCD_DRAM_BASE_ADDR, + (priv->hw_setting.shared_phys + + offsetof(struct iwl4965_shared, queues_byte_cnt_tbls)) >> 10); + + /* Disable chain mode for all queues */ + iwl4965_write_prph(priv, KDR_SCD_QUEUECHAIN_SEL, 0); + + /* Initialize each Tx queue (including the command queue) */ + for (i = 0; i < priv->hw_setting.max_txq_num; i++) { + + /* TFD circular buffer read/write indexes */ + iwl4965_write_prph(priv, KDR_SCD_QUEUE_RDPTR(i), 0); + iwl4965_write_direct32(priv, HBUS_TARG_WRPTR, 0 | (i << 8)); + + /* Max Tx Window size for Scheduler-ACK mode */ + iwl4965_write_targ_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 + + + /* Frame limit */ + iwl4965_write_targ_mem(priv, priv->scd_base_addr + SCD_CONTEXT_QUEUE_OFFSET(i) + sizeof(u32), (SCD_FRAME_LIMIT << @@ -1685,85 +1729,98 @@ int iwl4965_alive_notify(struct iwl_priv *priv) SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK); } - iwl_write_restricted_reg(priv, SCD_INTERRUPT_MASK, - (1 << IWL4965_NUM_QUEUES) - 1); + iwl4965_write_prph(priv, KDR_SCD_INTERRUPT_MASK, + (1 << priv->hw_setting.max_txq_num) - 1); - iwl_write_restricted_reg(priv, SCD_TXFACT, + /* Activate all Tx DMA/FIFO channels */ + iwl4965_write_prph(priv, KDR_SCD_TXFACT, SCD_TXFACT_REG_TXFIFO_MASK(0, 7)); iwl4965_set_wr_ptrs(priv, IWL_CMD_QUEUE_NUM, 0); - iwl4965_tx_queue_set_status(priv, &priv->txq[IWL_CMD_QUEUE_NUM], - IWL_CMD_FIFO_NUM, 0, 1); - /* map qos queues to fifos one-to-one */ - for (i = 0; i < GLOBAL_ARRAY_SIZE(default_ac_to_tx_fifo); i++) { - int ac = default_ac_to_tx_fifo[i]; - iwl4965_tx_queue_set_status(priv, &priv->txq[ac], ac, 0, 1); + + /* Map each Tx/cmd queue to its corresponding fifo */ + for (i = 0; i < ARRAY_SIZE(default_queue_to_tx_fifo); i++) { + int ac = default_queue_to_tx_fifo[i]; + iwl4965_txq_ctx_activate(priv, i); + iwl4965_tx_queue_set_status(priv, &priv->txq[i], ac, 0); } - iwl_release_restricted_access(priv); + iwl4965_release_nic_access(priv); spin_unlock_irqrestore(&priv->lock, flags); return 0; } -int iwl_hw_set_hw_setting(struct iwl_priv *priv) +/** + * iwl4965_hw_set_hw_setting + * + * Called when initializing driver + */ +int iwl4965_hw_set_hw_setting(struct iwl4965_priv *priv) { + /* Allocate area for Tx byte count tables and Rx queue status */ priv->hw_setting.shared_virt = pci_alloc_consistent(priv->pci_dev, - sizeof(struct iwl_shared), + sizeof(struct iwl4965_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)); + memset(priv->hw_setting.shared_virt, 0, sizeof(struct iwl4965_shared)); - priv->hw_setting.max_queue_number = IWL4965_NUM_QUEUES; + priv->hw_setting.max_txq_num = iwl4965_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.tx_cmd_len = sizeof(struct iwl4965_tx_cmd); priv->hw_setting.max_rxq_size = RX_QUEUE_SIZE; priv->hw_setting.max_rxq_log = RX_QUEUE_SIZE_LOG; + if (iwl4965_param_amsdu_size_8K) + priv->hw_setting.rx_buf_size = IWL_RX_BUF_SIZE_8K; + else + priv->hw_setting.rx_buf_size = IWL_RX_BUF_SIZE_4K; + priv->hw_setting.max_pkt_size = priv->hw_setting.rx_buf_size - 256; + priv->hw_setting.max_stations = IWL4965_STATION_COUNT; + priv->hw_setting.bcast_sta_id = IWL4965_BROADCAST_ID; return 0; } /** - * iwl_hw_txq_ctx_free - Free TXQ Context + * iwl4965_hw_txq_ctx_free - Free TXQ Context * * Destroy all TX DMA queues and structures */ -void iwl_hw_txq_ctx_free(struct iwl_priv *priv) +void iwl4965_hw_txq_ctx_free(struct iwl4965_priv *priv) { int txq_id; /* Tx queues */ - for (txq_id = 0; txq_id < priv->hw_setting.max_queue_number; txq_id++) - iwl_tx_queue_free(priv, &priv->txq[txq_id]); + for (txq_id = 0; txq_id < priv->hw_setting.max_txq_num; txq_id++) + iwl4965_tx_queue_free(priv, &priv->txq[txq_id]); + /* Keep-warm buffer */ iwl4965_kw_free(priv); } /** - * iwl_hw_tx_queue_free_tfd - Free one TFD, those at index [txq->q.last_used] + * iwl4965_hw_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr] * - * Does NOT advance any indexes + * Does NOT advance any TFD circular buffer read/write indexes + * Does NOT free the TFD itself (which is within circular buffer) */ -int iwl_hw_tx_queue_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq) +int iwl4965_hw_txq_free_tfd(struct iwl4965_priv *priv, struct iwl4965_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 iwl4965_tfd_frame *bd_tmp = (struct iwl4965_tfd_frame *)&txq->bd[0]; + struct iwl4965_tfd_frame *bd = &bd_tmp[txq->q.read_ptr]; struct pci_dev *dev = priv->pci_dev; int i; int counter = 0; int index, is_odd; - /* classify bd */ + /* Host command buffers stay mapped in memory, nothing to clean */ if (txq->q.id == IWL_CMD_QUEUE_NUM) - /* nothing to cleanup after for host commands */ return 0; - /* sanity check */ + /* Sanity check on number of chunks */ counter = IWL_GET_BITS(*bd, num_tbs); if (counter > MAX_NUM_OF_TBS) { IWL_ERROR("Too many chunks: %i\n", counter); @@ -1771,11 +1828,11 @@ int iwl_hw_tx_queue_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq) return 0; } - /* unmap chunks if any */ - + /* Unmap chunks, if any. + * TFD info for odd chunks is different format than for even chunks. */ for (i = 0; i < counter; i++) { index = i / 2; - is_odd = i % 2; + is_odd = i & 0x1; if (is_odd) pci_unmap_single( @@ -1792,37 +1849,24 @@ int iwl_hw_tx_queue_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq) 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]; + /* Free SKB, if any, for this chunk */ + if (txq->txb[txq->q.read_ptr].skb[i]) { + struct sk_buff *skb = txq->txb[txq->q.read_ptr].skb[i]; dev_kfree_skb(skb); - txq->txb[txq->q.last_used].skb[i] = NULL; + txq->txb[txq->q.read_ptr].skb[i] = NULL; } } return 0; } -int iwl_hw_reg_set_txpower(struct iwl_priv *priv, s8 power) +int iwl4965_hw_reg_set_txpower(struct iwl4965_priv *priv, s8 power) { - IWL_ERROR("TODO: Implement iwl_hw_reg_set_txpower!\n"); + IWL_ERROR("TODO: Implement iwl4965_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) +static s32 iwl4965_math_div_round(s32 num, s32 denom, s32 *res) { s32 sign = 1; @@ -1840,8 +1884,19 @@ static s32 iwl4965_math_div_round(s32 num, s32 denom, s32 * res) return 1; } -static s32 iwl4965_get_voltage_compensation(u32 eeprom_voltage, - u32 current_voltage) +/** + * iwl4965_get_voltage_compensation - Power supply voltage comp for txpower + * + * Determines power supply voltage compensation for txpower calculations. + * Returns number of 1/2-dB steps to subtract from gain table index, + * to compensate for difference between power supply voltage during + * factory measurements, vs. current power supply voltage. + * + * Voltage indication is higher for lower voltage. + * Lower voltage requires more gain (lower gain table index). + */ +static s32 iwl4965_get_voltage_compensation(s32 eeprom_voltage, + s32 current_voltage) { s32 comp = 0; @@ -1860,35 +1915,20 @@ static s32 iwl4965_get_voltage_compensation(u32 eeprom_voltage, return comp; } -static const struct iwl_channel_info *iwl4965_get_current_txpower_info( - struct iwl_priv *priv, u8 *band, u8 *channel, u8 *is_fat, - u8 *ctrl_chan_high) +static const struct iwl4965_channel_info * +iwl4965_get_channel_txpower_info(struct iwl4965_priv *priv, u8 phymode, u16 channel) { - const struct iwl_channel_info *ch_info; - - *ctrl_chan_high = 0; - *channel = priv->active_rxon.channel; - *band = ((priv->phymode == MODE_IEEE80211B) || - (priv->phymode == MODE_IEEE80211G) || - (priv->phymode == MODE_ATHEROS_TURBOG)) ? 1 : 0; + const struct iwl4965_channel_info *ch_info; - ch_info = iwl_get_channel_info(priv, priv->phymode, - priv->active_rxon.channel); + ch_info = iwl4965_get_channel_info(priv, phymode, channel); if (!is_channel_valid(ch_info)) return NULL; - *is_fat = is_fat_channel(priv); - - if (*is_fat) - if (priv->active_rxon.flags & - RXON_FLG_CONTROL_CHANNEL_LOC_HIGH_MSK) - *ctrl_chan_high = 1; - return ch_info; } -static s32 iwl4965_get_txatten_group_from_channel(u32 channel) +static s32 iwl4965_get_tx_atten_grp(u16 channel) { if (channel >= CALIB_IWL_TX_ATTEN_GR5_FCH && channel <= CALIB_IWL_TX_ATTEN_GR5_LCH) @@ -1914,24 +1954,20 @@ static s32 iwl4965_get_txatten_group_from_channel(u32 channel) return -1; } -static u32 iwl4965_get_sub_band(const struct iwl_priv *priv, u32 channel) +static u32 iwl4965_get_sub_band(const struct iwl4965_priv *priv, u32 channel) { - s32 sub_band = -1; - - for (sub_band = 0; sub_band < EEPROM_TX_POWER_BANDS; sub_band++) { + s32 b = -1; - if (priv->eeprom.calib_info.band_info_tbl[sub_band].ch_from == - 0) + 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_tbl[sub_band].ch_from) - && (channel <= - priv->eeprom.calib_info.band_info_tbl[sub_band].ch_to)) + if ((channel >= priv->eeprom.calib_info.band_info[b].ch_from) + && (channel <= priv->eeprom.calib_info.band_info[b].ch_to)) break; } - return sub_band; + return b; } static s32 iwl4965_interpolate_value(s32 x, s32 x1, s32 y1, s32 x2, s32 y2) @@ -1946,16 +1982,23 @@ static s32 iwl4965_interpolate_value(s32 x, s32 x1, s32 y1, s32 x2, s32 y2) } } -static int iwl4965_interpolate_chan( - struct iwl_priv *priv, u32 channel, - struct iwl_eeprom_calib_channel_info *chan_info) +/** + * iwl4965_interpolate_chan - Interpolate factory measurements for one channel + * + * Interpolates factory measurements from the two sample channels within a + * sub-band, to apply to channel of interest. Interpolation is proportional to + * differences in channel frequencies, which is proportional to differences + * in channel number. + */ +static int iwl4965_interpolate_chan(struct iwl4965_priv *priv, u32 channel, + struct iwl4965_eeprom_calib_ch_info *chan_info) { s32 s = -1; u32 c; u32 m; - const struct iwl_eeprom_calib_measurement *m1; - const struct iwl_eeprom_calib_measurement *m2; - struct iwl_eeprom_calib_measurement *omeas; + const struct iwl4965_eeprom_calib_measure *m1; + const struct iwl4965_eeprom_calib_measure *m2; + struct iwl4965_eeprom_calib_measure *omeas; u32 ch_i1; u32 ch_i2; @@ -1965,8 +2008,8 @@ static int iwl4965_interpolate_chan( return -1; } - ch_i1 = priv->eeprom.calib_info.band_info_tbl[s].ch1.ch_num; - ch_i2 = priv->eeprom.calib_info.band_info_tbl[s].ch2.ch_num; + 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", @@ -1974,9 +2017,9 @@ static int iwl4965_interpolate_chan( 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_tbl[s].ch1. + m1 = &(priv->eeprom.calib_info.band_info[s].ch1. measurements[c][m]); - m2 = &(priv->eeprom.calib_info.band_info_tbl[s].ch2. + m2 = &(priv->eeprom.calib_info.band_info[s].ch2. measurements[c][m]); omeas = &(chan_info->measurements[c][m]); @@ -2030,7 +2073,7 @@ static s32 back_off_table[] = { /* 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 { +static struct iwl4965_txpower_comp_entry { s32 degrees_per_05db_a; s32 degrees_per_05db_a_denom; } tx_power_cmp_tble[CALIB_CH_GROUP_MAX] = { @@ -2044,7 +2087,7 @@ static struct iwl_txpower_comp_entry { static s32 get_min_power_index(s32 rate_power_index, u32 band) { if (!band) { - if ((rate_power_index % 8) <= 4) + if ((rate_power_index & 7) <= 4) return MIN_TX_GAIN_INDEX_52GHZ_EXT; } return MIN_TX_GAIN_INDEX; @@ -2280,16 +2323,10 @@ static const struct gain_entry gain_table[2][108] = { } }; -/** - * 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) +static int iwl4965_fill_txpower_tbl(struct iwl4965_priv *priv, u8 band, u16 channel, + u8 is_fat, u8 ctrl_chan_high, + struct iwl4965_tx_power_db *tx_power_tbl) { - u16 radio_gain; - u16 dsp_atten; u8 saturation_power; s32 target_power; s32 user_target_power; @@ -2297,15 +2334,14 @@ int iwl_hw_reg_send_txpower(struct iwl_priv *priv) s32 current_temp; s32 reg_limit; s32 current_regulatory; - s32 txatten_group = CALIB_CH_GROUP_MAX; - int i = 0; + s32 txatten_grp = CALIB_CH_GROUP_MAX; + int i; int c; - struct iwl_tx_power_table_cmd cmd = { 0 }; - const struct iwl_channel_info *ch_info = NULL; - struct iwl_eeprom_calib_channel_info ch_eeprom_info; - const struct iwl_eeprom_calib_measurement *measurement; - int rc = 0; + const struct iwl4965_channel_info *ch_info = NULL; + struct iwl4965_eeprom_calib_ch_info ch_eeprom_info; + const struct iwl4965_eeprom_calib_measure *measurement; s16 voltage; + s32 init_voltage; s32 voltage_compensation; s32 degrees_per_05db_num; s32 degrees_per_05db_denom; @@ -2314,18 +2350,6 @@ int iwl_hw_reg_send_txpower(struct iwl_priv *priv) s32 factory_gain_index[2]; s32 factory_actual_pwr[2]; s32 power_index; - u8 band = 0; - u8 channel = 0; - u8 is_fat = 0; - u8 ctrl_chan_high = 0; - - if (priv->status & STATUS_SCANNING) { - /* 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; - } /* Sanity check requested level (dBm) */ if (priv->user_txpower_limit < IWL_TX_POWER_TARGET_POWER_MIN) { @@ -2345,27 +2369,22 @@ int iwl_hw_reg_send_txpower(struct iwl_priv *priv) /* Get current (RXON) channel, band, width */ ch_info = - iwl4965_get_current_txpower_info(priv, &band, &channel, &is_fat, - &ctrl_chan_high); + 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 -1; - - cmd.band = band; - cmd.channel = channel; - cmd.channel_normal_width = 0; + return -EINVAL; /* get txatten group, used to select 1) thermal txpower adjustment * and 2) mimo txpower balance between Tx chains. */ - txatten_group = iwl4965_get_txatten_group_from_channel(channel); - if (txatten_group < 0) - return -1; + 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_group); + channel, txatten_grp); if (is_fat) { if (ctrl_chan_high) @@ -2409,13 +2428,13 @@ int iwl_hw_reg_send_txpower(struct iwl_priv *priv) iwl4965_interpolate_chan(priv, channel, &ch_eeprom_info); /* calculate tx gain adjustment based on power supply voltage */ - voltage = (s16)le16_to_cpu(priv->eeprom.calib_info.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, - priv->card_alive_init.voltage); + iwl4965_get_voltage_compensation(voltage, init_voltage); IWL_DEBUG_TXPOWER("curr volt %d eeprom volt %d volt comp %d\n", - priv->card_alive_init.voltage, + init_voltage, voltage, voltage_compensation); /* get current temperature (Celsius) */ @@ -2423,18 +2442,12 @@ int iwl_hw_reg_send_txpower(struct iwl_priv *priv) current_temp = min(priv->temperature, IWL_TX_POWER_TEMPERATURE_MAX); current_temp = KELVIN_TO_CELSIUS(current_temp); - if (-40 > current_temp) { - IWL_WARNING("Invalid temperature %d, can't calculate " - "txpower\n", current_temp); - return -EINVAL; - } - /* 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_group].degrees_per_05db_a; + tx_power_cmp_tble[txatten_grp].degrees_per_05db_a; degrees_per_05db_denom = - tx_power_cmp_tble[txatten_group].degrees_per_05db_a_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++) { @@ -2463,9 +2476,9 @@ int iwl_hw_reg_send_txpower(struct iwl_priv *priv) } /* for each of 33 bit-rates (including 1 for CCK) */ - for (i = 0; i <= POWER_TABLE_NUM_HT_OFDM_ENTRIES; i++) { + for (i = 0; i < POWER_TABLE_NUM_ENTRIES; i++) { u8 is_mimo_rate; - union tx_power_dual_stream_u *tx_power; + union iwl4965_tx_power_dual_stream tx_power; /* for mimo, reduce each chain's txpower by half * (3dB, 6 steps), so total output power is regulatory @@ -2501,8 +2514,8 @@ int iwl_hw_reg_send_txpower(struct iwl_priv *priv) if (is_mimo_rate) atten_value = - priv->card_alive_init. - tx_atten[txatten_group][c]; + (s32)le32_to_cpu(priv->card_alive_init. + tx_atten[txatten_grp][c]); else atten_value = 0; @@ -2514,8 +2527,8 @@ int iwl_hw_reg_send_txpower(struct iwl_priv *priv) voltage_compensation + atten_value); -/* IWL_DEBUG_TXPOWER("calculated txpower index %d\n", */ -/* power_index); */ +/* 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); @@ -2525,16 +2538,9 @@ int iwl_hw_reg_send_txpower(struct iwl_priv *priv) power_index += 9; /* CCK, rate 32, reduce txpower for CCK */ - if (POWER_TABLE_NUM_HT_OFDM_ENTRIES == i) { + if (i == POWER_TABLE_CCK_ENTRY) power_index += IWL_TX_POWER_CCK_COMPENSATION_C_STEP; - tx_power = &(cmd.tx_power.legacy_cck_power); - } - - /* OFDM, rates 0-31 */ - else { - tx_power = &(cmd.tx_power.ht_ofdm_power[i]); - } /* stay within the table! */ if (power_index > 107) { @@ -2549,65 +2555,134 @@ int iwl_hw_reg_send_txpower(struct iwl_priv *priv) } /* fill txpower command for this rate/chain */ - radio_gain = gain_table[band][power_index].radio; - dsp_atten = gain_table[band][power_index].dsp; - - if (c == 0) { - tx_power->s.ramon_tx_gain = radio_gain; - tx_power->s.dsp_predis_atten = dsp_atten; - } else { - tx_power->s.ramon_tx_gain |= (radio_gain << 8); - tx_power->s.dsp_predis_atten |= - (dsp_atten << 8); - } + 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, - radio_gain, dsp_atten); + tx_power.s.radio_tx_gain[c], + tx_power.s.dsp_predis_atten[c]); + }/* for each chain */ - } /* for each chain */ + tx_power_tbl->power_tbl[i].dw = cpu_to_le32(tx_power.dw); - } /* for each rate */ + }/* for each rate */ - rc = iwl_send_cmd_pdu(priv, REPLY_TX_PWR_TABLE_CMD, sizeof(cmd), &cmd); + return 0; +} +/** + * iwl4965_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 iwl4965_hw_reg_send_txpower(struct iwl4965_priv *priv) +{ + struct iwl4965_txpowertable_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_CTRL_CHANNEL_LOC_HI_MSK)) + ctrl_chan_high = 1; + + cmd.band = band; + cmd.channel = priv->active_rxon.channel; + + 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 = iwl4965_send_cmd_pdu(priv, REPLY_TX_PWR_TABLE_CMD, sizeof(cmd), &cmd); + return rc; +} + +int iwl4965_hw_channel_switch(struct iwl4965_priv *priv, u16 channel) +{ + int rc; + u8 band = 0; + u8 is_fat = 0; + u8 ctrl_chan_high = 0; + struct iwl4965_channel_switch_cmd cmd = { 0 }; + const struct iwl4965_channel_info *ch_info; + + band = ((priv->phymode == MODE_IEEE80211B) || + (priv->phymode == MODE_IEEE80211G)); + + ch_info = iwl4965_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_CTRL_CHANNEL_LOC_HI_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 = iwl4965_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, +void iwl4965_hw_build_tx_cmd_rate(struct iwl4965_priv *priv, + struct iwl4965_cmd *cmd, struct ieee80211_tx_control *ctrl, struct ieee80211_hdr *hdr, int sta_id, int is_hcca) { - unsigned long flags; - int rate = ctrl->tx_rate; + u8 rate; u8 rts_retry_limit = 0; u8 data_retry_limit = 0; - u32 tx_flags; + __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; - - 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); + rate = iwl4965_rates[ctrl->tx_rate].plcp; rts_retry_limit = (is_hcca) ? RTS_HCCA_RETRY_LIMIT : RTS_DFAULT_RETRY_LIMIT; - if (ieee80211_is_probe_response(hdr->frame_control)) { + if (ieee80211_is_probe_response(fc)) { data_retry_limit = 3; if (data_retry_limit < rts_retry_limit) rts_retry_limit = data_retry_limit; @@ -2617,8 +2692,8 @@ void iwl_hw_build_tx_cmd_rate(struct iwl_priv *priv, if (priv->data_retry_limit != -1) data_retry_limit = priv->data_retry_limit; - if (WLAN_FC_GET_TYPE(hdr->frame_control) == IEEE80211_FTYPE_MGMT) { - switch (WLAN_FC_GET_STYPE(hdr->frame_control)) { + 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: @@ -2635,78 +2710,87 @@ void iwl_hw_build_tx_cmd_rate(struct iwl_priv *priv, cmd->cmd.tx.rts_retry_limit = rts_retry_limit; cmd->cmd.tx.data_retry_limit = data_retry_limit; - cmd->cmd.tx.rate.s.rate = rate; + cmd->cmd.tx.rate_n_flags = iwl4965_hw_set_rate_n_flags(rate, 0); cmd->cmd.tx.tx_flags = tx_flags; } -int iwl_hw_get_rx_read(struct iwl_priv *priv) +int iwl4965_hw_get_rx_read(struct iwl4965_priv *priv) { - struct iwl_shared *shared_data = - (struct iwl_shared *)priv->hw_setting.shared_virt; + struct iwl4965_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) +int iwl4965_hw_get_temperature(struct iwl4965_priv *priv) { return priv->temperature; } -int iwl_hw_get_beacon_cmd(struct iwl_priv *priv, - struct iwl_frame *frame, u16 rate) +unsigned int iwl4965_hw_get_beacon_cmd(struct iwl4965_priv *priv, + struct iwl4965_frame *frame, u8 rate) { - struct iwl_tx_beacon_cmd *tx_beacon_cmd; - int frame_size; - - if ((rate == IWL_RATE_1M_PLCP) || (rate >= IWL_RATE_2M_PLCP)) - rate |= RATE_MCS_CCK_MSK; + struct iwl4965_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 = 0xFFFFFFFF; + tx_beacon_cmd->tx.sta_id = IWL4965_BROADCAST_ID; + tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; - frame_size = iwl_fill_beacon_frame(priv, + frame_size = iwl4965_fill_beacon_frame(priv, tx_beacon_cmd->frame, - BROADCAST_ADDR, + iwl4965_broadcast_addr, sizeof(frame->u) - sizeof(*tx_beacon_cmd)); - tx_beacon_cmd->tx.len = frame_size; + 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 = + iwl4965_hw_set_rate_n_flags(rate, RATE_MCS_CCK_MSK); + else + tx_beacon_cmd->tx.rate_n_flags = + iwl4965_hw_set_rate_n_flags(rate, 0); - tx_beacon_cmd->tx.rate.rate_n_flags = rate; 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) +/* + * Tell 4965 where to find circular buffer of Tx Frame Descriptors for + * given Tx queue, and enable the DMA channel used for that queue. + * + * 4965 supports up to 16 Tx queues in DRAM, mapped to up to 8 Tx DMA + * channels supported in hardware. + */ +int iwl4965_hw_tx_queue_init(struct iwl4965_priv *priv, struct iwl4965_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); + rc = iwl4965_grab_nic_access(priv); if (rc) { spin_unlock_irqrestore(&priv->lock, flags); return rc; } - iwl_write_restricted(priv, FH_MEM_CBBC_QUEUE(txq_id), + /* Circular buffer (TFD queue in DRAM) physical base address */ + iwl4965_write_direct32(priv, FH_MEM_CBBC_QUEUE(txq_id), txq->q.dma_addr >> 8); - iwl_write_restricted( + + /* Enable DMA channel, using same id as for TFD queue */ + iwl4965_write_direct32( 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); + iwl4965_release_nic_access(priv); spin_unlock_irqrestore(&priv->lock, flags); - return 0; -} -static inline u32 iwl4965_get_dma_lo_address(dma_addr_t addr) -{ - return (u32) (addr & 0xffffffff); + return 0; } static inline u8 iwl4965_get_dma_hi_address(dma_addr_t addr) @@ -2714,13 +2798,14 @@ static inline u8 iwl4965_get_dma_hi_address(dma_addr_t addr) return sizeof(addr) > sizeof(u32) ? (addr >> 16) >> 16 : 0; } -int iwl_hw_tx_queue_attach_buffer_to_tfd(struct iwl_priv *priv, - void *ptr, dma_addr_t addr, u16 len) +int iwl4965_hw_txq_attach_buf_to_tfd(struct iwl4965_priv *priv, void *ptr, + dma_addr_t addr, u16 len) { int index, is_odd; - struct iwl_tfd_frame *tfd = ptr; + struct iwl4965_tfd_frame *tfd = ptr; u32 num_tbs = IWL_GET_BITS(*tfd, num_tbs); + /* Each TFD can point to a maximum 20 Tx buffers */ 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); @@ -2728,11 +2813,10 @@ int iwl_hw_tx_queue_attach_buffer_to_tfd(struct iwl_priv *priv, } index = num_tbs / 2; - is_odd = num_tbs % 2; + is_odd = num_tbs & 0x1; if (!is_odd) { - tfd->pa[index].tb1_addr = cpu_to_le32( - iwl4965_get_dma_lo_address(addr)); + 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); @@ -2748,7 +2832,7 @@ int iwl_hw_tx_queue_attach_buffer_to_tfd(struct iwl_priv *priv, return 0; } -void iwl_hw_card_show_info(struct iwl_priv *priv) +static void iwl4965_hw_card_show_info(struct iwl4965_priv *priv) { u16 hw_version = priv->eeprom.board_revision_4965; @@ -2763,50 +2847,41 @@ void iwl_hw_card_show_info(struct iwl_priv *priv) #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) +/** + * iwl4965_tx_queue_update_wr_ptr - Set up entry in Tx byte-count array + */ +int iwl4965_tx_queue_update_wr_ptr(struct iwl4965_priv *priv, + struct iwl4965_tx_queue *txq, u16 byte_cnt) { int len; int txq_id = txq->q.id; - struct iwl_shared *shared_data = - (struct iwl_shared *)priv->hw_setting.shared_virt; + struct iwl4965_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_BITS(shared_data->queues_byte_cnt_tbls[txq_id]. - tfd_offset[txq->q.first_empty], byte_cnt, len); + /* Set up byte count within first 256 entries */ + IWL_SET_BITS16(shared_data->queues_byte_cnt_tbls[txq_id]. + tfd_offset[txq->q.write_ptr], byte_cnt, len); - if (txq->q.first_empty < IWL4965_MAX_WIN_SIZE) - IWL_SET_BITS(shared_data->queues_byte_cnt_tbls[txq_id]. - tfd_offset[IWL4965_QUEUE_SIZE + txq->q.first_empty], - byte_cnt, len); + /* If within first 64 entries, duplicate at end */ + if (txq->q.write_ptr < IWL4965_MAX_WIN_SIZE) + IWL_SET_BITS16(shared_data->queues_byte_cnt_tbls[txq_id]. + tfd_offset[IWL4965_QUEUE_SIZE + txq->q.write_ptr], + 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) +/** + * iwl4965_set_rxon_chain - Set up Rx chain usage in "staging" RXON image + * + * Selects how many and which Rx receivers/antennas/chains to use. + * This should not be used for scan command ... it puts data in wrong place. + */ +void iwl4965_set_rxon_chain(struct iwl4965_priv *priv) { u8 is_single = is_single_stream(priv); u8 idle_state, rx_state; @@ -2819,14 +2894,17 @@ void iwl4965_set_rxon_chain(struct iwl_priv *priv) * Just after first association, iwl4965_noise_calibration() * checks which antennas actually *are* connected. */ priv->staging_rxon.rx_chain |= - (priv->valid_antenna << RXON_RX_CHAIN_VALID_POS); + 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 |= (rx_state << RXON_RX_CHAIN_MIMO_CNT_POS); - priv->staging_rxon.rx_chain |= (idle_state << RXON_RX_CHAIN_CNT_POS); + 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 && !(priv->status & STATUS_POWER_PMI) && (rx_state >= 2)) + 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; @@ -2834,27 +2912,378 @@ void iwl4965_set_rxon_chain(struct iwl_priv *priv) IWL_DEBUG_ASSOC("rx chain %X\n", priv->staging_rxon.rx_chain); } -int iwl4965_tx_cmd(struct iwl_priv *priv, struct iwl_cmd *out_cmd, +#ifdef CONFIG_IWL4965_HT +#ifdef CONFIG_IWL4965_HT_AGG +/* + get the traffic load value for tid +*/ +static u32 iwl4965_tl_get_load(struct iwl4965_priv *priv, u8 tid) +{ + u32 load = 0; + u32 current_time = jiffies_to_msecs(jiffies); + u32 time_diff; + s32 index; + unsigned long flags; + struct iwl4965_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 iwl4965_priv *priv, u8 tid) +{ + u32 current_time = jiffies_to_msecs(jiffies); + u32 time_diff; + s32 index; + unsigned long flags; + struct iwl4965_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, +}; + +/** + * iwl4964_tl_ba_avail - Find out if an unused aggregation queue is available + */ +static u8 iwl4964_tl_ba_avail(struct iwl4965_priv *priv) +{ + int i; + struct iwl4965_lq_mngr *lq; + u8 count = 0; + u16 msk; + + lq = (struct iwl4965_lq_mngr *)&(priv->lq_mngr); + + /* Find out how many agg queues are in use */ + 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 iwl4965_priv *priv, + u8 tid, enum HT_STATUS status); + +static int iwl4965_perform_addba(struct iwl4965_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 iwl4965_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 iwl4965_priv *priv, + struct iwl4965_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 iwl4965_priv *priv, u8 tid) +{ + struct iwl4965_lq_mngr *lq; + unsigned long flags; + + lq = (struct iwl4965_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 iwl4965_priv *priv, u8 tid) +{ + u32 tid_msk; + struct iwl4965_lq_mngr *lq; + unsigned long flags; + + lq = (struct iwl4965_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); + } +} + +/** + * iwl4965_ba_status - Update driver's link quality mgr with tid's HT status + */ +static void iwl4965_ba_status(struct iwl4965_priv *priv, + u8 tid, enum HT_STATUS status) +{ + struct iwl4965_lq_mngr *lq; + u32 tid_msk = (1 << tid); + unsigned long flags; + + lq = (struct iwl4965_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(void *p) +{ + struct iwl4965_priv *priv = p; + + u32 tid; + u32 retry_tid; + u32 tid_msk; + unsigned long flags; + struct iwl4965_lq_mngr *lq = (struct iwl4965_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_IWL4965_HT_AGG */ +#endif /* CONFIG_IWL4965_HT */ + +int iwl4965_tx_cmd(struct iwl4965_priv *priv, struct iwl4965_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]; + struct iwl4965_tx_cmd cmd; + struct iwl4965_tx_cmd *tx = (struct iwl4965_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_IWL4965_HT +#ifdef CONFIG_IWL4965_HT_AGG + __le16 *qc; +#endif /*CONFIG_IWL4965_HT_AGG */ +#endif /* CONFIG_IWL4965_HT */ unicast = !is_multicast_ether_addr(hdr->addr1); fc = le16_to_cpu(hdr->frame_control); - if (WLAN_FC_GET_TYPE(fc) != IEEE80211_FTYPE_DATA) + 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(&cmd, &(out_cmd->cmd.tx), sizeof(struct iwl4965_tx_cmd)); + memset(tx, 0, sizeof(struct iwl4965_tx_cmd)); memcpy(tx->hdr, hdr, hdr_len); tx->len = cmd.len; @@ -2873,48 +3302,66 @@ int iwl4965_tx_cmd(struct iwl_priv *priv, struct iwl_cmd *out_cmd, 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 = iwl4965_get_dma_lo_address(scratch_phys); + scratch_phys = txcmd_phys + sizeof(struct iwl4965_cmd_header) + + offsetof(struct iwl4965_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; - tx->rate.s.rate = iwl_rates[rate_index].plcp; /* 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; - tx->rate.rate_n_flags |= RATE_MCS_ANT_B_MSK; - tx->rate.rate_n_flags &= ~RATE_MCS_ANT_A_MSK; + rate_flags = RATE_MCS_ANT_B_MSK; } else { priv->use_ant_b_for_management_frame = 1; - tx->rate.rate_n_flags |= RATE_MCS_ANT_A_MSK; - tx->rate.rate_n_flags &= ~RATE_MCS_ANT_B_MSK; + rate_flags = RATE_MCS_ANT_A_MSK; } - if (!unicast || !is_data ) { + if (!unicast || !is_data) { if ((rate_index >= IWL_FIRST_CCK_RATE) && (rate_index <= IWL_LAST_CCK_RATE)) - tx->rate.rate_n_flags |= RATE_MCS_CCK_MSK; - + rate_flags |= RATE_MCS_CCK_MSK; } else { tx->initial_rate_index = 0; tx->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; } - if (ieee80211_is_probe_request(fc)) - tx->tx_flags |= TX_CMD_FLG_TSF_MSK; - else if (ieee80211_is_back_request(fc)) + tx->rate_n_flags = iwl4965_hw_set_rate_n_flags(iwl4965_rates[rate_index].plcp, + rate_flags); + + if (ieee80211_is_back_request(fc)) tx->tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK; +#ifdef CONFIG_IWL4965_HT +#ifdef CONFIG_IWL4965_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 + * 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. @@ -2922,25 +3369,11 @@ int iwl4965_tx_cmd(struct iwl_priv *priv, struct iwl_cmd *out_cmd, * @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, u8 index) +static s32 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); + u8 shift = 31 - index; - /* negative -- sign extend */ - if (oper & bit) - return oper |= mask; - - /* positive -- sign clear */ - return oper &= ~mask; + return (s32)(oper << shift) >> shift; } /** @@ -2949,38 +3382,40 @@ static s32 iwl4965_sign_extend(u32 oper, u8 index) * * A return of <0 indicates bogus data in the statistics */ -int iwl4965_get_temperature(const struct iwl_priv *priv) +int iwl4965_get_temperature(const struct iwl4965_priv *priv) { s32 temperature; s32 vt; - s32 R1, R2, R3, R4; + s32 R1, R2, R3; + u32 R4; - if ((priv->status & STATUS_TEMPERATURE) && + 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 = priv->card_alive_init.therm_r1[1]; - R2 = priv->card_alive_init.therm_r2[1]; - R3 = priv->card_alive_init.therm_r3[1]; - R4 = priv->card_alive_init.therm_r4[1]; + 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 = priv->card_alive_init.therm_r1[0]; - R2 = priv->card_alive_init.therm_r2[0]; - R3 = priv->card_alive_init.therm_r3[0]; - R4 = priv->card_alive_init.therm_r4[0]; + 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 + * 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 (!(priv->status & STATUS_TEMPERATURE)) - vt = iwl4965_sign_extend(R4, 23); + * "initialize" ALIVE response. + */ + if (!test_bit(STATUS_TEMPERATURE, &priv->status)) + vt = sign_extend(R4, 23); else - vt = iwl4965_sign_extend(priv->statistics.general.temperature, - 23); + vt = 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); @@ -3015,11 +3450,11 @@ int iwl4965_get_temperature(const struct iwl_priv *priv) * Assumes caller will replace priv->last_temperature once calibration * executed. */ -static int iwl4965_is_temp_calib_needed(struct iwl_priv *priv) +static int iwl4965_is_temp_calib_needed(struct iwl4965_priv *priv) { int temp_diff; - if (!(priv->status & STATUS_STATISTICS)) { + if (!test_bit(STATUS_STATISTICS, &priv->status)) { IWL_DEBUG_TEMP("Temperature not updated -- no statistics.\n"); return 0; } @@ -3045,9 +3480,49 @@ static int iwl4965_is_temp_calib_needed(struct iwl_priv *priv) return 1; } -void iwl_hw_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) +/* 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 iwl4965_priv *priv) { - struct iwl_rx_packet *pkt = (void *)rxb->skb->data; + 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 iwl4965_hw_rx_statistics(struct iwl4965_priv *priv, struct iwl4965_rx_mem_buffer *rxb) +{ + struct iwl4965_rx_packet *pkt = (void *)rxb->skb->data; int change; s32 temp; @@ -3062,7 +3537,7 @@ void iwl_hw_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) memcpy(&priv->statistics, &pkt->u.stats, sizeof(priv->statistics)); - priv->status |= STATUS_STATISTICS; + set_bit(STATUS_STATISTICS, &priv->status); /* Reschedule the statistics timer to occur in * REG_RECALIB_PERIOD seconds to ensure we get a @@ -3071,11 +3546,14 @@ void iwl_hw_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) mod_timer(&priv->statistics_periodic, jiffies + msecs_to_jiffies(REG_RECALIB_PERIOD * 1000)); -#ifdef CONFIG_IWLWIFI_SENSITIVITY - if (unlikely(!(priv->status & STATUS_SCANNING)) && - (pkt->hdr.cmd == STATISTICS_NOTIFICATION)) + if (unlikely(!test_bit(STATUS_SCANNING, &priv->status)) && + (pkt->hdr.cmd == STATISTICS_NOTIFICATION)) { + iwl4965_rx_calc_noise(priv); +#ifdef CONFIG_IWL4965_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 */ @@ -3099,26 +3577,26 @@ void iwl_hw_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) } priv->temperature = temp; - priv->status |= STATUS_TEMPERATURE; + set_bit(STATUS_TEMPERATURE, &priv->status); - if (unlikely(!(priv->status & STATUS_SCANNING) && - iwl4965_is_temp_calib_needed(priv))) + 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, +static void iwl4965_handle_data_packet(struct iwl4965_priv *priv, int is_data, int include_phy, - struct iwl_rx_mem_buffer *rxb, + struct iwl4965_rx_mem_buffer *rxb, struct ieee80211_rx_status *stats) { - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl4965_rx_packet *pkt = (struct iwl4965_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; - unsigned int len; - u32 *rx_end; + u16 len; + __le32 *rx_end; unsigned int skblen; - u32 ampd_status; + u32 ampdu_status; if (!include_phy && priv->last_phy_res[0]) rx_start = (struct iwl4965_rx_phy_res *)&priv->last_phy_res[1]; @@ -3131,9 +3609,9 @@ static void iwl4965_handle_data_packet(struct iwl_priv *priv, int is_data, hdr = (struct ieee80211_hdr *)((u8 *) & rx_start[1] + rx_start->cfg_phy_cnt); - len = rx_start->byte_count; + len = le16_to_cpu(rx_start->byte_count); - rx_end = (u32 *) ((u8 *) & pkt->u.raw[0] + + rx_end = (__le32 *) ((u8 *) & pkt->u.raw[0] + sizeof(struct iwl4965_rx_phy_res) + rx_start->cfg_phy_cnt + len); @@ -3141,20 +3619,19 @@ static void iwl4965_handle_data_packet(struct iwl_priv *priv, int is_data, struct iwl4965_rx_mpdu_res_start *amsdu = (struct iwl4965_rx_mpdu_res_start *)pkt->u.raw; - hdr = (void *)(pkt->u.raw + + hdr = (struct ieee80211_hdr *)(pkt->u.raw + sizeof(struct iwl4965_rx_mpdu_res_start)); - rx_end = (u32 *) (((u8 *) hdr) + amsdu->byte_count); - len = amsdu->byte_count; - rx_start->byte_count = len; + 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]" + if (len > priv->hw_setting.max_pkt_size || len < 16) { + IWL_WARNING("byte count out of range [16,4K]" " : %d\n", len); return; - } - ampd_status = *rx_end; + ampdu_status = le32_to_cpu(*rx_end); skblen = ((u8 *) rx_end - (u8 *) & pkt->u.raw[0]) + sizeof(u32); /* start from MAC */ @@ -3163,31 +3640,31 @@ static void iwl4965_handle_data_packet(struct iwl_priv *priv, int is_data, /* We only process data packets if the interface is open */ if (unlikely(!priv->is_open)) { - IWL_DEBUG_DROP + IWL_DEBUG_DROP_LIMIT ("Dropping packet while interface is not open.\n"); return; } if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) { - if (param_hwcrypto) - iwl_set_decrypted_flag(priv, rxb->skb, - ampd_status, stats); - iwl_handle_data_packet_monitor(priv, rxb, hdr, len, stats, 0); + if (iwl4965_param_hwcrypto) + iwl4965_set_decrypted_flag(priv, rxb->skb, + ampdu_status, stats); + iwl4965_handle_data_packet_monitor(priv, rxb, hdr, len, stats, 0); return; } stats->flag = 0; hdr = (struct ieee80211_hdr *)rxb->skb->data; - if (param_hwcrypto) - iwl_set_decrypted_flag(priv, rxb->skb, ampd_status, stats); + if (iwl4965_param_hwcrypto) + iwl4965_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); + iwl4965_setup_activity_timer(priv); #endif } @@ -3198,11 +3675,12 @@ static int iwl4965_calc_rssi(struct iwl4965_rx_phy_res *rx_resp) * 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 = (ncphy->agc_info & IWL_AGC_DB_MASK) >> IWL_AGC_DB_POS; + u32 agc = (le16_to_cpu(ncphy->agc_info) & IWL_AGC_DB_MASK) + >> IWL_AGC_DB_POS; u32 valid_antennae = - (rx_resp->phy_flags & RX_PHY_FLAGS_ANTENNAE_MASK) >> - RX_PHY_FLAGS_ANTENNAE_OFFSET; + (le16_to_cpu(rx_resp->phy_flags) & RX_PHY_FLAGS_ANTENNAE_MASK) + >> RX_PHY_FLAGS_ANTENNAE_OFFSET; u8 max_rssi = 0; u32 i; @@ -3224,7 +3702,7 @@ static int iwl4965_calc_rssi(struct iwl4965_rx_phy_res *rx_resp) return (max_rssi - agc - IWL_RSSI_OFFSET); } -#ifdef CONFIG_IWLWIFI_HT +#ifdef CONFIG_IWL4965_HT /* Parsed Information Elements */ struct ieee802_11_elems { @@ -3242,7 +3720,7 @@ struct ieee802_11_elems { u8 ht_extra_param_len; }; -static int parse_elems(u8 * start, size_t len, struct ieee802_11_elems *elems) +static int parse_elems(u8 *start, size_t len, struct ieee802_11_elems *elems) { size_t left = len; u8 *pos = start; @@ -3296,29 +3774,76 @@ static int parse_elems(u8 * start, size_t len, struct ieee802_11_elems *elems) return 0; } -#endif /* CONFIG_IWLWIFI_HT */ -static void iwl4965_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id) +void iwl4965_init_ht_hw_capab(struct ieee80211_ht_info *ht_info, int mode) +{ + ht_info->cap = 0; + memset(ht_info->supp_mcs_set, 0, 16); + + ht_info->ht_supported = 1; + + if (mode == MODE_IEEE80211A) { + ht_info->cap |= (u16)IEEE80211_HT_CAP_SUP_WIDTH; + ht_info->cap |= (u16)IEEE80211_HT_CAP_SGI_40; + ht_info->supp_mcs_set[4] = 0x01; + } + ht_info->cap |= (u16)IEEE80211_HT_CAP_GRN_FLD; + ht_info->cap |= (u16)IEEE80211_HT_CAP_SGI_20; + ht_info->cap |= (u16)(IEEE80211_HT_CAP_MIMO_PS & + (IWL_MIMO_PS_NONE << 2)); + if (iwl4965_param_amsdu_size_8K) { + printk(KERN_DEBUG "iwl4965 in A-MSDU 8K support mode\n"); + ht_info->cap |= (u16)IEEE80211_HT_CAP_MAX_AMSDU; + } + + ht_info->ampdu_factor = CFG_HT_RX_AMPDU_FACTOR_DEF; + ht_info->ampdu_density = CFG_HT_MPDU_DENSITY_DEF; + + ht_info->supp_mcs_set[0] = 0xFF; + ht_info->supp_mcs_set[1] = 0xFF; +} +#endif /* CONFIG_IWL4965_HT */ + +static void iwl4965_sta_modify_ps_wake(struct iwl4965_priv *priv, int sta_id) { - unsigned long lock_flags; - spin_lock_irqsave(&priv->sta_lock, lock_flags); + 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, lock_flags); - /* assuming we are in rx flow and the lock is already locked */ - iwl_send_add_station(priv, &priv->stations[sta_id].sta, - CMD_ASYNC | CMD_NO_LOCK); + priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + spin_unlock_irqrestore(&priv->sta_lock, flags); + + iwl4965_send_add_station(priv, &priv->stations[sta_id].sta, CMD_ASYNC); } +static void iwl4965_update_ps_mode(struct iwl4965_priv *priv, u16 ps_bit, u8 *addr) +{ + /* FIXME: need locking over ps_status ??? */ + u8 sta_id = iwl4965_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; + } + } +} + +#define IWL_DELAY_NEXT_SCAN_AFTER_ASSOC (HZ*6) + /* 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) +static void iwl4965_rx_reply_rx(struct iwl4965_priv *priv, + struct iwl4965_rx_mem_buffer *rxb) { - struct iwl_rx_packet *pkt = (void *)rxb->skb->data; - + struct iwl4965_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. */ @@ -3326,21 +3851,22 @@ static void iwl4965_rx_reply_rx(struct iwl_priv *priv, 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]; - - u32 *rx_end; + __le32 *rx_end; unsigned int len = 0; struct ieee80211_hdr *header; u16 fc; struct ieee80211_rx_status stats = { - .mactime = rx_start->beacon_time_stamp, - .freq = ieee80211chan2mhz(le16_to_cpu(rx_start->channel)), - .channel = rx_start->channel, + .mactime = le64_to_cpu(rx_start->timestamp), + .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 = rx_start->rate.s.rate, - .flag = rx_start->phy_flags, + .rate = iwl4965_hw_get_rate(rx_start->rate_n_flags), + .flag = 0, +#ifdef CONFIG_IWL4965_HT_AGG + .ordered = 0 +#endif /* CONFIG_IWL4965_HT_AGG */ }; u8 network_packet; @@ -3367,83 +3893,78 @@ static void iwl4965_rx_reply_rx(struct iwl_priv *priv, header = (struct ieee80211_hdr *)((u8 *) & rx_start[1] + rx_start->cfg_phy_cnt); - len = rx_start->byte_count; - rx_end = (u32 *) (pkt->u.raw + rx_start->cfg_phy_cnt + - sizeof(struct iwl4965_rx_phy_res) + - rx_start->byte_count); + 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 = amsdu->byte_count; - rx_end = - (u32 *) (pkt->u.raw + - sizeof(struct iwl4965_rx_mpdu_res_start) + - amsdu->byte_count); + 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", *rx_end); + IWL_DEBUG_RX("Bad CRC or FIFO: 0x%08X.\n", + le32_to_cpu(*rx_end)); return; } - stats.freq = ieee80211chan2mhz((stats.channel)); - stats.flag = 0; + 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); - IWL_DEBUG_RX("Rssi %d, TSF %lu\n", stats.ssi, - (long unsigned int)le64_to_cpu(rx_start->timestamp)); - - /* Sensitivity algo, if used (only while associated, not scanning), - * calculates signal-to-noise ratio in dB. Use this if available, - * else calculate signal quality using only the signal strength. */ - if (priv->last_rx_snr && iwl_is_associated(priv) && - !(priv->status & STATUS_SCANNING)) { - /* TODO: Find better noise level reference, use - * in iwl_calc_sig_qual() */ - stats.noise = stats.ssi - priv->last_rx_snr; - stats.signal = iwl_calc_sig_qual(stats.ssi, 0); + /* 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 (iwl4965_is_associated(priv) && + !test_bit(STATUS_SCANNING, &priv->status)) { + stats.noise = priv->last_rx_noise; + stats.signal = iwl4965_calc_sig_qual(stats.ssi, stats.noise); } else { - stats.signal = iwl_calc_sig_qual(stats.ssi, 0); - - /* Reset noise values if not associated or snr not available. */ - /* Set default noise value to -127 ... this works better than - * 0 when averaging frames with/without noise info; - * 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. */ - priv->last_rx_snr = 0; - priv->last_rx_noise = -127; - stats.noise = -127; - } - IWL_DEBUG_STATS("Rssi %d noise %d qual %d snr db %d\n", stats.ssi, - stats.noise, stats.signal, priv->last_rx_snr); - -#ifdef CONFIG_IWLWIFI_DEBUG - /* TODO: Parts of iwl_report_frame are broken for 4965 */ - if (iwl_debug_level & (IWL_DL_RX)) + stats.noise = IWL_NOISE_MEAS_NOT_AVAILABLE; + stats.signal = iwl4965_calc_sig_qual(stats.ssi, 0); + } + + /* Reset beacon noise level if not associated. */ + if (!iwl4965_is_associated(priv)) + priv->last_rx_noise = IWL_NOISE_MEAS_NOT_AVAILABLE; + +#ifdef CONFIG_IWL4965_DEBUG + /* TODO: Parts of iwl4965_report_frame are broken for 4965 */ + if (iwl4965_debug_level & (IWL_DL_RX)) /* Set "1" to report good data frames in groups of 100 */ - iwl_report_frame(priv, pkt, header, 1); + iwl4965_report_frame(priv, pkt, header, 1); + + if (iwl4965_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); + network_packet = iwl4965_is_network_packet(priv, header); if (network_packet) { priv->last_rx_rssi = stats.ssi; - priv->last_rx_noise = stats.noise; - priv->last_beacon_time = rx_start->beacon_time_stamp; - priv->last_tsf = rx_start->timestamp; + 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 (WLAN_FC_GET_TYPE(fc)) { + switch (fc & IEEE80211_FCTL_FTYPE) { case IEEE80211_FTYPE_MGMT: - switch (WLAN_FC_GET_STYPE(fc)) { + + 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 && @@ -3452,11 +3973,12 @@ static void iwl4965_rx_reply_rx(struct iwl_priv *priv, !compare_ether_addr(header->addr3, priv->bssid))) { struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)header; - u32 *pos; + u64 timestamp = + le64_to_cpu(mgmt->u.beacon.timestamp); - pos = (u32 *) & mgmt->u.beacon.timestamp; - priv->timestamp0 = le32_to_cpu(pos[0]); - priv->timestamp1 = le32_to_cpu(pos[1]); + 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 && @@ -3479,20 +4001,26 @@ static void iwl4965_rx_reply_rx(struct iwl_priv *priv, */ case IEEE80211_STYPE_ASSOC_RESP: case IEEE80211_STYPE_REASSOC_RESP: - if (network_packet && iwl_is_associated(priv)) { -#ifdef CONFIG_IWLWIFI_HT + if (network_packet) { +#ifdef CONFIG_IWL4965_HT u8 *pos = NULL; struct ieee802_11_elems elems; -#endif /*CONFIG_IWLWIFI_HT */ +#endif /*CONFIG_IWL4965_HT */ struct ieee80211_mgmt *mgnt = (struct ieee80211_mgmt *)header; + /* We have just associated, give some + * time for the 4-way handshake if + * any. Don't start scan too early. */ + priv->next_scan_jiffies = jiffies + + IWL_DELAY_NEXT_SCAN_AFTER_ASSOC; + priv->assoc_id = (~((1 << 15) | (1 << 14)) - & mgnt->u.assoc_resp.aid); + & le16_to_cpu(mgnt->u.assoc_resp.aid)); priv->assoc_capability = le16_to_cpu( mgnt->u.assoc_resp.capab_info); -#ifdef CONFIG_IWLWIFI_HT +#ifdef CONFIG_IWL4965_HT pos = mgnt->u.assoc_resp.variable; if (!parse_elems(pos, len - (pos - (u8 *) mgnt), @@ -3501,7 +4029,7 @@ static void iwl4965_rx_reply_rx(struct iwl_priv *priv, elems.ht_cap_param) break; } -#endif /*CONFIG_IWLWIFI_HT */ +#endif /*CONFIG_IWL4965_HT */ /* assoc_id is 0 no association */ if (!priv->assoc_id) break; @@ -3516,13 +4044,16 @@ static void iwl4965_rx_reply_rx(struct iwl_priv *priv, case IEEE80211_STYPE_PROBE_REQ: if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) && - !iwl_is_associated(priv)) { + !iwl4965_is_associated(priv)) { + DECLARE_MAC_BUF(mac1); + DECLARE_MAC_BUF(mac2); + DECLARE_MAC_BUF(mac3); + 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)); + "%s, %s, %s\n", + print_mac(mac1, header->addr1), + print_mac(mac2, header->addr2), + print_mac(mac3, header->addr3)); return; } } @@ -3530,49 +4061,45 @@ static void iwl4965_rx_reply_rx(struct iwl_priv *priv, break; case IEEE80211_FTYPE_CTL: +#ifdef CONFIG_IWL4965_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: - /* FIXME - patch for PS in AP mode */ - if (priv->iw_mode == IEEE80211_IF_TYPE_AP) { - u8 sta_id = iwl_hw_find_station(priv, header->addr2); - u8 sta_awake; - u16 ps_bit; - - if (sta_id == IWL_INVALID_STATION) - break; - - sta_awake = (priv->stations[sta_id].ps_status == - STA_PS_STATUS_WAKE); - ps_bit = (header->frame_control & - cpu_to_le16(IEEE80211_FCTL_PM)); - 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; - } - } + case IEEE80211_FTYPE_DATA: { + DECLARE_MAC_BUF(mac1); + DECLARE_MAC_BUF(mac2); + DECLARE_MAC_BUF(mac3); + + 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)); + "%s, %s, %s\n", + print_mac(mac1, header->addr1), + print_mac(mac2, header->addr2), + print_mac(mac3, header->addr3)); + else if (unlikely(iwl4965_is_duplicate_packet(priv, header))) + IWL_DEBUG_DROP("Dropping (dup): %s, %s, %s\n", + print_mac(mac1, header->addr1), + print_mac(mac2, header->addr2), + print_mac(mac3, header->addr3)); else iwl4965_handle_data_packet(priv, 1, include_phy, rxb, &stats); break; + } default: break; @@ -3581,140 +4108,435 @@ static void iwl4965_rx_reply_rx(struct iwl_priv *priv, /* 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) +static void iwl4965_rx_reply_rx_phy(struct iwl4965_priv *priv, + struct iwl4965_rx_mem_buffer *rxb) { - struct iwl_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl4965_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) +static void iwl4965_rx_missed_beacon_notif(struct iwl4965_priv *priv, + struct iwl4965_rx_mem_buffer *rxb) { -#ifdef CONFIG_IWLWIFI_SENSITIVITY - struct iwl_rx_packet *pkt = (void *)rxb->skb->data; - struct iwl_missed_beacon_notif *missed_beacon; +#ifdef CONFIG_IWL4965_SENSITIVITY + struct iwl4965_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl4965_missed_beacon_notif *missed_beacon; missed_beacon = &pkt->u.missed_beacon; - if (missed_beacon->consequtive_missed_beacons > 5) { + if (le32_to_cpu(missed_beacon->consequtive_missed_beacons) > 5) { IWL_DEBUG_CALIB("missed bcn cnsq %d totl %d rcd %d expctd %d\n", - missed_beacon->consequtive_missed_beacons, - missed_beacon->total_missed_becons, - missed_beacon->num_recvd_beacons, - missed_beacon->num_expected_beacons); + 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(!(priv->status & STATUS_SCANNING))) + if (unlikely(!test_bit(STATUS_SCANNING, &priv->status))) queue_work(priv->workqueue, &priv->sensitivity_work); } -#endif /*CONFIG_IWLWIFI_SENSITIVITY*/ +#endif /*CONFIG_IWL4965_SENSITIVITY*/ +} + +#ifdef CONFIG_IWL4965_HT +#ifdef CONFIG_IWL4965_HT_AGG + +/** + * iwl4965_set_tx_status - Update driver's record of one Tx frame's status + * + * This will get sent to mac80211. + */ +static void iwl4965_set_tx_status(struct iwl4965_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; } -#ifdef CONFIG_IWLWIFI_HT_AGG -static void iwl4965_rx_reply_compressed_ba(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) +/** + * iwl4965_sta_modify_enable_tid_tx - Enable Tx for this TID in station table + */ +static void iwl4965_sta_modify_enable_tid_tx(struct iwl4965_priv *priv, + int sta_id, int tid) { - struct iwl_rx_packet *pkt = (void *)rxb->skb->data; - struct iwl_compressed_ba_resp *compressed_ba = &pkt->u.compressed_ba; - int index = iwl_queue_dec_wrap(compressed_ba->scd_ssn & 0xff, 0xff); - struct iwl_tx_queue *txq = NULL; + unsigned long flags; + + /* Remove "disable" flag, to enable Tx for this TID */ + 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); + + iwl4965_send_add_station(priv, &priv->stations[sta_id].sta, CMD_ASYNC); +} + + +/** + * iwl4965_tx_status_reply_compressed_ba - Update tx status from block-ack + * + * Go through block-ack's bitmap of ACK'd frames, update driver's record of + * ACK vs. not. This gets sent to mac80211, then to rate scaling algo. + */ +static int iwl4965_tx_status_reply_compressed_ba(struct iwl4965_priv *priv, + struct iwl4965_ht_agg *agg, + struct iwl4965_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; + } + + /* Mark that the expected block-ack response arrived */ + agg->wait_for_ba = 0; + IWL_DEBUG_TX_REPLY("BA %d %d\n", agg->start_idx, ba_resp->ba_seq_ctl); + + /* Calculate shift to align block-ack bits with our Tx window bits */ + sh = agg->start_idx - SEQ_TO_INDEX(ba_seq_ctl>>4); + if (sh < 0) /* tbw something is wrong with indices */ + sh += 0x100; + + /* don't use 64-bit values for now */ + bitmap0 = resp_bitmap0 >> sh; + bitmap1 = resp_bitmap1 >> sh; + bitmap0 |= (resp_bitmap1 & ((1<<sh)|((1<<sh)-1))) << (32 - sh); + + if (agg->frame_count > (64 - sh)) { + IWL_DEBUG_TX_REPLY("more frames than bitmap size"); + return -1; + } - BUG_ON(compressed_ba->scd_flow >= ARRAY_SIZE(priv->txq)); - txq = &priv->txq[compressed_ba->scd_flow]; + /* check for success or failure according to the + * transmitted bitmap and block-ack bitmap */ + bitmap0 &= agg->bitmap0; + bitmap1 &= agg->bitmap1; + + /* For each frame attempted in aggregation, + * update driver's record of tx frame's status. */ + 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, 0, + agg->rate_n_flags); - /* TODO: Need to get this copy more sefely - now good for debug */ - IWL_DEBUG_TX_REPLY("REPLY_COMPRESSED_BA Received from " MAC_FMT - " , sta_id = %d\n", - MAC_ARG((u8 *) & compressed_ba->sta_addr_lo32), - compressed_ba->sta_id); - IWL_DEBUG_TX_REPLY - ("TID = %d, seq_ctl = %d, bitmap = 0x%x%x, scd_flow = %d, " - "scd_ssn = %d\n", - compressed_ba->tid, compressed_ba->ba_seq_ctl, - compressed_ba->ba_bitmap0, compressed_ba->ba_bitmap1, - compressed_ba->scd_flow, compressed_ba->scd_ssn); + } - /* releases all the TFDs until the SSN */ - if (txq->q.last_used != (compressed_ba->scd_ssn & 0xff)) - iwl_tx_queue_reclaim(priv, compressed_ba->scd_flow, index); + IWL_DEBUG_TX_REPLY("Bitmap %x%x\n", bitmap0, bitmap1); + return 0; +} + +/** + * iwl4965_queue_dec_wrap - Decrement queue index, wrap back to end if needed + * @index -- current index + * @n_bd -- total number of entries in queue (s/b power of 2) + */ +static inline int iwl4965_queue_dec_wrap(int index, int n_bd) +{ + return (index == 0) ? n_bd - 1 : index - 1; } -#endif /* CONFIG_IWLWIFI_HT_AGG */ +/** + * iwl4965_rx_reply_compressed_ba - Handler for REPLY_COMPRESSED_BA + * + * Handles block-acknowledge notification from device, which reports success + * of frames sent via aggregation. + */ +static void iwl4965_rx_reply_compressed_ba(struct iwl4965_priv *priv, + struct iwl4965_rx_mem_buffer *rxb) +{ + struct iwl4965_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl4965_compressed_ba_resp *ba_resp = &pkt->u.compressed_ba; + int index; + struct iwl4965_tx_queue *txq = NULL; + struct iwl4965_ht_agg *agg; + + /* "flow" corresponds to Tx queue */ + u16 ba_resp_scd_flow = le16_to_cpu(ba_resp->scd_flow); + + /* "ssn" is start of block-ack Tx window, corresponds to index + * (in Tx queue's circular buffer) of first TFD/frame in window */ + u16 ba_resp_scd_ssn = le16_to_cpu(ba_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; + + /* Find index just before block-ack window */ + index = iwl4965_queue_dec_wrap(ba_resp_scd_ssn & 0xff, txq->q.n_bd); + + /* TODO: Need to get this copy more safely - now good for debug */ /* - * RATE SCALE CODE + { + DECLARE_MAC_BUF(mac); + IWL_DEBUG_TX_REPLY("REPLY_COMPRESSED_BA [%d]Received from %s, " + "sta_id = %d\n", + agg->wait_for_ba, + print_mac(mac, (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); + } +*/ + + /* Update driver's record of ACK vs. not for each frame in window */ + iwl4965_tx_status_reply_compressed_ba(priv, agg, ba_resp); + + /* Release all TFDs before the SSN, i.e. all TFDs in front of + * block-ack window (we assume that they've been successfully + * transmitted ... if not, it's too late anyway). */ + if (txq->q.read_ptr != (ba_resp_scd_ssn & 0xff)) + iwl4965_tx_queue_reclaim(priv, ba_resp_scd_flow, index); + +} + + +/** + * iwl4965_tx_queue_stop_scheduler - Stop queue, but keep configuration */ -int iwl4965_init_hw_rates(struct iwl_priv *priv, struct ieee80211_rate *rates) +static void iwl4965_tx_queue_stop_scheduler(struct iwl4965_priv *priv, u16 txq_id) { + /* Simply stop the queue, but don't change any configuration; + * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */ + iwl4965_write_prph(priv, + KDR_SCD_QUEUE_STATUS_BITS(txq_id), + (0 << SCD_QUEUE_STTS_REG_POS_ACTIVE)| + (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); +} + +/** + * iwl4965_tx_queue_set_q2ratid - Map unique receiver/tid combination to a queue + */ +static int iwl4965_tx_queue_set_q2ratid(struct iwl4965_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 = iwl4965_read_targ_mem(priv, tbl_dw_addr); + + if (txq_id & 0x1) + tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF); + else + tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000); + + iwl4965_write_targ_mem(priv, tbl_dw_addr, tbl_dw); + + return 0; +} + +/** + * iwl4965_tx_queue_agg_enable - Set up & enable aggregation for selected queue + * + * NOTE: txq_id must be greater than IWL_BACK_QUEUE_FIRST_ID, + * i.e. it must be one of the higher queues used for aggregation + */ +static int iwl4965_tx_queue_agg_enable(struct iwl4965_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); + + /* Modify device's station table to Tx this TID */ + iwl4965_sta_modify_enable_tid_tx(priv, sta_id, tid); + + spin_lock_irqsave(&priv->lock, flags); + rc = iwl4965_grab_nic_access(priv); + if (rc) { + spin_unlock_irqrestore(&priv->lock, flags); + return rc; + } + + /* Stop this Tx queue before configuring it */ + iwl4965_tx_queue_stop_scheduler(priv, txq_id); + + /* Map receiver-address / traffic-ID to this queue */ + iwl4965_tx_queue_set_q2ratid(priv, ra_tid, txq_id); + + /* Set this queue as a chain-building queue */ + iwl4965_set_bits_prph(priv, KDR_SCD_QUEUECHAIN_SEL, (1<<txq_id)); + + /* Place first TFD at index corresponding to start sequence number. + * Assumes that ssn_idx is valid (!= 0xFFF) */ + priv->txq[txq_id].q.read_ptr = (ssn_idx & 0xff); + priv->txq[txq_id].q.write_ptr = (ssn_idx & 0xff); + iwl4965_set_wr_ptrs(priv, txq_id, ssn_idx); + + /* Set up Tx window size and frame limit for this queue */ + iwl4965_write_targ_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); + + iwl4965_write_targ_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); + + iwl4965_set_bits_prph(priv, KDR_SCD_INTERRUPT_MASK, (1 << txq_id)); + + /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */ + iwl4965_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 1); + + iwl4965_release_nic_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 iwl4965_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 = iwl4965_grab_nic_access(priv); + if (rc) { + spin_unlock_irqrestore(&priv->lock, flags); + return rc; + } + + iwl4965_tx_queue_stop_scheduler(priv, txq_id); + + iwl4965_clear_bits_prph(priv, KDR_SCD_QUEUECHAIN_SEL, (1 << txq_id)); + + priv->txq[txq_id].q.read_ptr = (ssn_idx & 0xff); + priv->txq[txq_id].q.write_ptr = (ssn_idx & 0xff); + /* supposes that ssn_idx is valid (!= 0xFFF) */ + iwl4965_set_wr_ptrs(priv, txq_id, ssn_idx); + + iwl4965_clear_bits_prph(priv, KDR_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); + + iwl4965_release_nic_access(priv); + spin_unlock_irqrestore(&priv->lock, flags); + return 0; } +#endif/* CONFIG_IWL4965_HT_AGG */ +#endif /* CONFIG_IWL4965_HT */ /** * iwl4965_add_station - Initialize a station's hardware rate table * - * The uCode contains a table of fallback rates and retries per rate + * The uCode's station table contains a table of fallback rates * 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. + * NOTE: This sets up a default set of values. These will be replaced later + * if the driver's iwl-4965-rs rate scaling algorithm is used, instead of + * rc80211_simple. * + * NOTE: Run REPLY_ADD_STA command to set up station table entry, before + * calling this function (which runs REPLY_TX_LINK_QUALITY_CMD, + * which requires station table entry to exist). */ -void iwl4965_add_station(struct iwl_priv *priv, const u8 * addr, int is_ap) +void iwl4965_add_station(struct iwl4965_priv *priv, const u8 *addr, int is_ap) { int i, r; - struct iwl_link_quality_cmd link_cmd = { + struct iwl4965_link_quality_cmd link_cmd = { .reserved1 = 0, }; - struct iwl_rate *table = link_cmd.rate_scale_table; + 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 */ - i = 0; + /* Set up the rate scaling to start at selected rate, fall back + * all the way down to 1M in IEEE order, and then spin on 1M */ if (is_ap) r = IWL_RATE_54M_INDEX; - else if ((priv->phymode == MODE_IEEE80211A) || - (priv->phymode == MODE_ATHEROS_TURBO)) + else if (priv->phymode == MODE_IEEE80211A) r = IWL_RATE_6M_INDEX; else r = IWL_RATE_1M_INDEX; - while (i < LINK_QUAL_MAX_RETRY_NUM) { - if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE) { - table[i].rate_n_flags |= RATE_MCS_CCK_MSK; - } - table[i].s.rate = iwl_rates[r].plcp; - table[i].rate_n_flags |= RATE_MCS_ANT_B_MSK; - table[i].rate_n_flags &= ~RATE_MCS_ANT_A_MSK; - r = iwl_get_prev_ieee_rate(r); - i++; + 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; + + /* Use Tx antenna B only */ + rate_flags |= RATE_MCS_ANT_B_MSK; + rate_flags &= ~RATE_MCS_ANT_A_MSK; + + link_cmd.rs_table[i].rate_n_flags = + iwl4965_hw_set_rate_n_flags(iwl4965_rates[r].plcp, rate_flags); + r = iwl4965_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 = 4000; + 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; + link_cmd.sta_id = is_ap ? IWL_AP_ID : IWL4965_BROADCAST_ID; - iwl_send_cmd_pdu(priv, REPLY_TX_LINK_QUALITY_CMD, sizeof(link_cmd), + iwl4965_send_cmd_pdu(priv, REPLY_TX_LINK_QUALITY_CMD, sizeof(link_cmd), &link_cmd); } -#ifdef CONFIG_IWLWIFI_HT +#ifdef CONFIG_IWL4965_HT -static u8 iwl_is_channel_extension(struct iwl_priv *priv, int phymode, - int channel, u8 extension_chan_offset) +static u8 iwl4965_is_channel_extension(struct iwl4965_priv *priv, int phymode, + u16 channel, u8 extension_chan_offset) { - const struct iwl_channel_info *ch_info; + const struct iwl4965_channel_info *ch_info; - ch_info = iwl_get_channel_info(priv, phymode, channel); + ch_info = iwl4965_get_channel_info(priv, phymode, channel); if (!is_channel_valid(ch_info)) return 0; @@ -3728,165 +4550,310 @@ static u8 iwl_is_channel_extension(struct iwl_priv *priv, int phymode, return 0; } -static u8 iwl_is_fat_tx_allowed(struct iwl_priv *priv, - const struct sta_ht_info *ht_info) +static u8 iwl4965_is_fat_tx_allowed(struct iwl4965_priv *priv, + struct ieee80211_ht_info *sta_ht_inf) { + struct iwl_ht_info *iwl_ht_conf = &priv->current_ht_config; - if (priv->channel_width != IWL_CHANNEL_WIDTH_40MHZ) - return 0; - - if (ht_info->supported_chan_width != IWL_CHANNEL_WIDTH_40MHZ) + if ((!iwl_ht_conf->is_ht) || + (iwl_ht_conf->supported_chan_width != IWL_CHANNEL_WIDTH_40MHZ) || + (iwl_ht_conf->extension_chan_offset == IWL_EXT_CHANNEL_OFFSET_AUTO)) return 0; - if (ht_info->extension_chan_offset == IWL_EXT_CHANNEL_OFFSET_AUTO) - return 0; + if (sta_ht_inf) { + if ((!sta_ht_inf->ht_supported) || + (!sta_ht_inf->cap & IEEE80211_HT_CAP_SUP_WIDTH)) + return 0; + } - /* no fat tx allowed on 2.4GHZ */ - if ((priv->phymode != MODE_IEEE80211A) && - (priv->phymode != MODE_ATHEROS_TURBO)) - return 0; - return (iwl_is_channel_extension(priv, priv->phymode, - ht_info->control_chan, - ht_info->extension_chan_offset)); + return (iwl4965_is_channel_extension(priv, priv->phymode, + iwl_ht_conf->control_channel, + iwl_ht_conf->extension_chan_offset)); } -void iwl4965_set_rxon_ht(struct iwl_priv *priv, - struct sta_ht_info *ht_info) +void iwl4965_set_rxon_ht(struct iwl4965_priv *priv, struct iwl_ht_info *ht_info) { - struct iwl_rxon_cmd *rxon = &priv->staging_rxon; + struct iwl4965_rxon_cmd *rxon = &priv->staging_rxon; u32 val; if (!ht_info->is_ht) return; - if (iwl_is_fat_tx_allowed(priv, ht_info)) + /* Set up channel bandwidth: 20 MHz only, or 20/40 mixed if fat ok */ + if (iwl4965_is_fat_tx_allowed(priv, NULL)) rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED_MSK; else - rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY_MSK; + rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED_MSK | + RXON_FLG_CHANNEL_MODE_PURE_40_MSK); - if (rxon->channel != ht_info->control_chan) { + if (le16_to_cpu(rxon->channel) != ht_info->control_channel) { IWL_DEBUG_ASSOC("control diff than current %d %d\n", - rxon->channel, ht_info->control_chan); - rxon->channel = ht_info->control_chan; + 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; - + /* Note: control channel is opposite of extension channel */ switch (ht_info->extension_chan_offset) { case IWL_EXT_CHANNEL_OFFSET_ABOVE: - rxon->flags |= RXON_FLG_CONTROL_CHANNEL_LOC_LOW_MSK; + rxon->flags &= ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); break; case IWL_EXT_CHANNEL_OFFSET_BELOW: - rxon->flags |= RXON_FLG_CONTROL_CHANNEL_LOC_HIGH_MSK; + rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_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; + val = ht_info->ht_protection; - rxon->flags |= val << RXON_FLG_HT_OPERATING_MODE_POS; + 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 " - "falgs 0x%X operation 0x%X\n", - priv->active_rate_ht[0], priv->active_rate_ht[1], - rxon->flags, ht_info->operating_mode); + "rxon flags 0x%X operation mode :0x%X " + "extension channel offset 0x%x " + "control chan %d\n", + ht_info->supp_mcs_set[0], ht_info->supp_mcs_set[1], + le32_to_cpu(rxon->flags), ht_info->ht_protection, + ht_info->extension_chan_offset, + ht_info->control_channel); return; } -void iwl4965_set_ht_add_station(struct iwl_priv *priv, - u8 index, u8 need_to_lock) -{ - u32 val; - u32 sta_rate; - u32 sta_flag; - unsigned long flags = 0; - u8 i = index; - struct sta_ht_info *ht_info = &priv->current_assoc_ht; - if (need_to_lock) - spin_lock_irqsave(&priv->sta_lock, flags); +void iwl4965_set_ht_add_station(struct iwl4965_priv *priv, u8 index, + struct ieee80211_ht_info *sta_ht_inf) +{ + __le32 sta_flags; - priv->current_channel_width = IWL_CHANNEL_WIDTH_20MHZ; - if (!ht_info->is_ht) + if (!sta_ht_inf || !sta_ht_inf->ht_supported) goto done; - sta_rate = priv->stations[i].sta.tx_rate.rate_n_flags; - sta_flag = priv->stations[i].sta.station_flags; + sta_flags = priv->stations[index].sta.station_flags; - if (ht_info->tx_mimo_ps_mode == IWL_MIMO_PS_DYNAMIC) - sta_flag |= STA_FLG_RTS_MIMO_PROT_MSK; + if (((sta_ht_inf->cap & IEEE80211_HT_CAP_MIMO_PS >> 2)) + == IWL_MIMO_PS_DYNAMIC) + sta_flags |= STA_FLG_RTS_MIMO_PROT_MSK; else - sta_flag &= ~STA_FLG_RTS_MIMO_PROT_MSK; + sta_flags &= ~STA_FLG_RTS_MIMO_PROT_MSK; - val = (u32) ht_info->ampdu_factor; - sta_flag |= val << STA_FLG_MAX_AGG_SIZE_POS; + sta_flags |= cpu_to_le32( + (u32)sta_ht_inf->ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS); - val = (u32) ht_info->mpdu_density; - sta_flag |= val << STA_FLG_AGG_MPDU_DENSITY_POS; + sta_flags |= cpu_to_le32( + (u32)sta_ht_inf->ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS); - if ((ht_info->sgf & HT_SHORT_GI_20MHZ_ONLY) && - (ht_info->sgf & HT_SHORT_GI_40MHZ_ONLY)) { - val = 1; - sta_rate |= ((val << RATE_MCS_SGI_POS) & RATE_MCS_SGI_MSK); - } + if (iwl4965_is_fat_tx_allowed(priv, sta_ht_inf)) + sta_flags |= STA_FLG_FAT_EN_MSK; + else + sta_flags &= (~STA_FLG_FAT_EN_MSK); - val = ht_info->is_green_field; - sta_rate |= ((val << RATE_MCS_GF_POS) & RATE_MCS_GF_MSK); + priv->stations[index].sta.station_flags = sta_flags; + done: + return; +} - sta_rate &= (~RATE_MCS_FAT_MSK); - sta_flag &= (~STA_FLG_FAT_EN_MSK); +#ifdef CONFIG_IWL4965_HT_AGG - ht_info->tx_chan_width = IWL_CHANNEL_WIDTH_20MHZ; - ht_info->chan_width_cap = IWL_CHANNEL_WIDTH_20MHZ; +static void iwl4965_sta_modify_add_ba_tid(struct iwl4965_priv *priv, + int sta_id, int tid, u16 ssn) +{ + unsigned long flags; - if (iwl_is_fat_tx_allowed(priv, ht_info)) { - sta_flag |= STA_FLG_FAT_EN_MSK; - ht_info->chan_width_cap = IWL_CHANNEL_WIDTH_40MHZ; + 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); - if (ht_info->supported_chan_width == IWL_CHANNEL_WIDTH_40MHZ) { - sta_rate |= RATE_MCS_FAT_MSK; - ht_info->tx_chan_width = IWL_CHANNEL_WIDTH_40MHZ; - } + iwl4965_send_add_station(priv, &priv->stations[sta_id].sta, CMD_ASYNC); +} + +static void iwl4965_sta_modify_del_ba_tid(struct iwl4965_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); + + iwl4965_send_add_station(priv, &priv->stations[sta_id].sta, CMD_ASYNC); +} + +static const u16 default_tid_to_tx_fifo[] = { + IWL_TX_FIFO_AC1, + IWL_TX_FIFO_AC0, + IWL_TX_FIFO_AC0, + IWL_TX_FIFO_AC1, + IWL_TX_FIFO_AC2, + IWL_TX_FIFO_AC2, + IWL_TX_FIFO_AC3, + IWL_TX_FIFO_AC3, + IWL_TX_FIFO_NONE, + IWL_TX_FIFO_NONE, + IWL_TX_FIFO_NONE, + IWL_TX_FIFO_NONE, + IWL_TX_FIFO_NONE, + IWL_TX_FIFO_NONE, + IWL_TX_FIFO_NONE, + IWL_TX_FIFO_NONE, + IWL_TX_FIFO_AC3 +}; + +/* + * Find first available (lowest unused) Tx Queue, mark it "active". + * Called only when finding queue for aggregation. + * Should never return anything < 7, because they should already + * be in use as EDCA AC (0-3), Command (4), HCCA (5, 6). + */ +static int iwl4965_txq_ctx_activate_free(struct iwl4965_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 iwl4965_mac_ht_tx_agg_start(struct ieee80211_hw *hw, u8 *da, u16 tid, + u16 *start_seq_num) +{ + + struct iwl4965_priv *priv = hw->priv; + int sta_id; + int tx_fifo; + int txq_id; + int ssn = -1; + unsigned long flags; + struct iwl4965_tid_data *tid_data; + DECLARE_MAC_BUF(mac); + + /* Determine Tx DMA/FIFO channel for this Traffic ID */ + if (likely(tid < ARRAY_SIZE(default_tid_to_tx_fifo))) + tx_fifo = default_tid_to_tx_fifo[tid]; + else + return -EINVAL; + + IWL_WARNING("iwl-AGG iwl4965_mac_ht_tx_agg_start on da=%s" + " tid=%d\n", print_mac(mac, da), tid); + + /* Get index into station table */ + sta_id = iwl4965_hw_find_station(priv, da); + if (sta_id == IWL_INVALID_STATION) + return -ENXIO; + + /* Find available Tx queue for aggregation */ + txq_id = iwl4965_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]; + + /* Get starting sequence number for 1st frame in block ack window. + * We'll use least signif byte as 1st frame's index into Tx queue. */ + 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; + + /* Update driver's link quality manager */ + iwl4965_ba_status(priv, tid, BA_STATUS_ACTIVE); + + /* Set up and enable aggregation for selected Tx queue and FIFO */ + return iwl4965_tx_queue_agg_enable(priv, txq_id, tx_fifo, + sta_id, tid, ssn); +} + + +int iwl4965_mac_ht_tx_agg_stop(struct ieee80211_hw *hw, u8 *da, u16 tid, + int generator) +{ + + struct iwl4965_priv *priv = hw->priv; + int tx_fifo_id, txq_id, sta_id, ssn = -1; + struct iwl4965_tid_data *tid_data; + int rc; + DECLARE_MAC_BUF(mac); + + if (!da) { + IWL_ERROR("%s: da = NULL\n", __func__); + return -EINVAL; } - priv->current_channel_width = ht_info->tx_chan_width; + if (likely(tid < ARRAY_SIZE(default_tid_to_tx_fifo))) + tx_fifo_id = default_tid_to_tx_fifo[tid]; + else + return -EINVAL; - priv->stations[i].sta.tx_rate.rate_n_flags = sta_rate; - priv->stations[i].sta.station_flags = sta_flag; + sta_id = iwl4965_hw_find_station(priv, da); - done: - if (need_to_lock) - spin_unlock_irqrestore(&priv->sta_lock, flags); + if (sta_id == IWL_INVALID_STATION) + return -ENXIO; - return; + 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("iwl4965_mac_ht_tx_agg_stop on da=%s tid=%d\n", + print_mac(mac, da), tid); + + return 0; } -#endif /* CONFIG_IWLWIFI_HT */ -#ifdef CONFIG_IWLWIFI_HT_AGG -static void iwl_sta_modify_enable_tid_tx(struct iwl_priv *priv, int sta_id, - int tid) +int iwl4965_mac_ht_rx_agg_start(struct ieee80211_hw *hw, u8 *da, + u16 tid, u16 start_seq_num) { - unsigned long lock_flags; - spin_lock_irqsave(&priv->sta_lock, lock_flags); - priv->stations[sta_id].sta.sta.modify_mask |= STA_MODIFY_TID_DISABLE_TX; - priv->stations[sta_id].sta.tid_disable_tx &= ~(1 << tid); - priv->stations[sta_id].sta.mode |= STA_CONTROL_MODIFY_MSK; - spin_unlock_irqrestore(&priv->sta_lock, lock_flags); - iwl_send_add_station(priv, &priv->stations[sta_id].sta, CMD_ASYNC); + struct iwl4965_priv *priv = hw->priv; + int sta_id; + DECLARE_MAC_BUF(mac); + + IWL_WARNING("iwl-AGG iwl4965_mac_ht_rx_agg_start on da=%s" + " tid=%d\n", print_mac(mac, da), tid); + sta_id = iwl4965_hw_find_station(priv, da); + iwl4965_sta_modify_add_ba_tid(priv, sta_id, tid, start_seq_num); + return 0; } -#endif /* CONFIG_IWLWIFI_HT_AGG */ +int iwl4965_mac_ht_rx_agg_stop(struct ieee80211_hw *hw, u8 *da, + u16 tid, int generator) +{ + struct iwl4965_priv *priv = hw->priv; + int sta_id; + DECLARE_MAC_BUF(mac); + + IWL_WARNING("iwl-AGG iwl4965_mac_ht_rx_agg_stop on da=%s tid=%d\n", + print_mac(mac, da), tid); + sta_id = iwl4965_hw_find_station(priv, da); + iwl4965_sta_modify_del_ba_tid(priv, sta_id, tid); + return 0; +} + +#endif /* CONFIG_IWL4965_HT_AGG */ +#endif /* CONFIG_IWL4965_HT */ /* Set up 4965-specific Rx frame reply handlers */ -void iwl_hw_rx_handler_setup(struct iwl_priv *priv) +void iwl4965_hw_rx_handler_setup(struct iwl4965_priv *priv) { /* Legacy Rx frames */ priv->rx_handlers[REPLY_4965_RX] = iwl4965_rx_reply_rx; @@ -3898,37 +4865,79 @@ void iwl_hw_rx_handler_setup(struct iwl_priv *priv) priv->rx_handlers[MISSED_BEACONS_NOTIFICATION] = iwl4965_rx_missed_beacon_notif; -#ifdef CONFIG_IWLWIFI_HT_AGG +#ifdef CONFIG_IWL4965_HT +#ifdef CONFIG_IWL4965_HT_AGG priv->rx_handlers[REPLY_COMPRESSED_BA] = iwl4965_rx_reply_compressed_ba; -#endif +#endif /* CONFIG_IWL4965_HT_AGG */ +#endif /* CONFIG_IWL4965_HT */ } -void iwl_hw_setup_deferred_work(struct iwl_priv *priv) +void iwl4965_hw_setup_deferred_work(struct iwl4965_priv *priv) { INIT_WORK(&priv->txpower_work, iwl4965_bg_txpower_work, priv); INIT_WORK(&priv->statistics_work, iwl4965_bg_statistics_work, priv); -#ifdef CONFIG_IWLWIFI_SENSITIVITY +#ifdef CONFIG_IWL4965_SENSITIVITY INIT_WORK(&priv->sensitivity_work, iwl4965_bg_sensitivity_work, priv); #endif -#ifdef CONFIG_IWLWIFI_HT_AGG +#ifdef CONFIG_IWL4965_HT +#ifdef CONFIG_IWL4965_HT_AGG INIT_WORK(&priv->agg_work, iwl4965_bg_agg_work, priv); -#endif +#endif /* CONFIG_IWL4965_HT_AGG */ +#endif /* CONFIG_IWL4965_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) +void iwl4965_hw_cancel_deferred_work(struct iwl4965_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}, +struct pci_device_id iwl4965_hw_card_ids[] = { + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4229)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4230)}, {0} }; -MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids); +/* + * The device's EEPROM semaphore prevents conflicts between driver and uCode + * when accessing the EEPROM; each access is a series of pulses to/from the + * EEPROM chip, not a single event, so even reads could conflict if they + * weren't arbitrated by the semaphore. + */ +int iwl4965_eeprom_acquire_semaphore(struct iwl4965_priv *priv) +{ + u16 count; + int rc; + + for (count = 0; count < EEPROM_SEM_RETRY_LIMIT; count++) { + /* Request semaphore */ + iwl4965_set_bit(priv, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); + + /* See if we got it */ + rc = iwl4965_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("Acquired semaphore after %d tries.\n", + count+1); + return rc; + } + } + + return rc; +} + +inline void iwl4965_eeprom_release_semaphore(struct iwl4965_priv *priv) +{ + iwl4965_clear_bit(priv, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); +} + + +MODULE_DEVICE_TABLE(pci, iwl4965_hw_card_ids); diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.h b/drivers/net/wireless/iwlwifi/iwl-4965.h index ec83f08..01fcc23 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.h +++ b/drivers/net/wireless/iwlwifi/iwl-4965.h @@ -23,85 +23,802 @@ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ +/* + * Please use this file (iwl-4965.h) for driver implementation definitions. + * Please use iwl-4965-commands.h for uCode API definitions. + * Please use iwl-4965-hw.h for hardware-related definitions. + */ + #ifndef __iwl_4965_h__ #define __iwl_4965_h__ -struct iwl_priv; -struct sta_ht_info; +#include <linux/pci.h> /* for struct pci_device_id */ +#include <linux/kernel.h> +#include <net/ieee80211_radiotap.h> + +/* Hardware specific file defines the PCI IDs table for that hardware module */ +extern struct pci_device_id iwl4965_hw_card_ids[]; + +#define DRV_NAME "iwl4965" +#include "iwl-4965-hw.h" +#include "iwl-prph.h" +#include "iwl-4965-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 iwl4965_param_hwcrypto; +extern int iwl4965_param_queues_num; +extern int iwl4965_param_amsdu_size_8K; + +enum iwl4965_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 iwl4965_rx_mem_buffer { + dma_addr_t dma_addr; + struct sk_buff *skb; + struct list_head list; +}; + +struct iwl4965_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 iwl4965_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 iwl4965_queue { + int n_bd; /* number of BDs in this queue */ + int write_ptr; /* 1-st empty entry (index) host_w*/ + int read_ptr; /* 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) + +/* One for each TFD */ +struct iwl4965_tx_info { + struct ieee80211_tx_status status; + struct sk_buff *skb[MAX_NUM_OF_TBS]; +}; + +/** + * struct iwl4965_tx_queue - Tx Queue for DMA + * @q: generic Rx/Tx queue descriptor + * @bd: base of circular buffer of TFDs + * @cmd: array of command/Tx buffers + * @dma_addr_cmd: physical address of cmd/tx buffer array + * @txb: array of per-TFD driver data + * @need_update: indicates need to update read/write index + * @sched_retry: indicates queue is high-throughput aggregation (HT AGG) enabled + * + * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame + * descriptors) and required locking structures. + */ +struct iwl4965_tx_queue { + struct iwl4965_queue q; + struct iwl4965_tfd_frame *bd; + struct iwl4965_cmd *cmd; + dma_addr_t dma_addr_cmd; + struct iwl4965_tx_info *txb; + int need_update; + int sched_retry; + int active; +}; + +#define IWL_NUM_SCAN_RATES (2) + +struct iwl4965_channel_tgd_info { + u8 type; + s8 max_power; +}; + +struct iwl4965_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 iwl4965_channel_power_info { + struct iwl4965_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 iwl4965_scan_power_info { + struct iwl4965_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 */ +}; + +/* For fat_extension_channel */ +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 +}; + +/* + * 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 iwl4965_channel_info { + struct iwl4965_channel_tgd_info tgd; + struct iwl4965_channel_tgh_info tgh; + struct iwl4965_eeprom_channel eeprom; /* EEPROM regulatory limit */ + struct iwl4965_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) limit */ + 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 iwl4965_channel_power_info power_info[IWL4965_MAX_RATE]; + + /* 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; /* HT_IE_EXT_CHANNEL_* */ + + /* Radio/DSP gain settings for each scan rate, for directed scans. */ + struct iwl4965_scan_power_info scan_pwr_info[IWL_NUM_SCAN_RATES]; +}; + +struct iwl4965_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]; +}; + +#include "iwl-4965-rs.h" + +#define IWL_TX_FIFO_AC0 0 +#define IWL_TX_FIFO_AC1 1 +#define IWL_TX_FIFO_AC2 2 +#define IWL_TX_FIFO_AC3 3 +#define IWL_TX_FIFO_HCCA_1 5 +#define IWL_TX_FIFO_HCCA_2 6 +#define IWL_TX_FIFO_NONE 7 + +/* Minimum number of queues. MAX_NUM is defined in hw specific files */ +#define IWL_MIN_NUM_QUEUES 4 + +/* Power management (not Tx power) structures */ + +struct iwl4965_power_vec_entry { + struct iwl4965_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 iwl4965_power_mgr { + spinlock_t lock; + struct iwl4965_power_vec_entry pwr_range_0[IWL_POWER_AC]; + struct iwl4965_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 iwl4965_frame { + union { + struct ieee80211_hdr frame; + struct iwl4965_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 iwl4965_cmd; +struct iwl4965_priv; + +struct iwl4965_cmd_meta { + struct iwl4965_cmd_meta *source; + union { + struct sk_buff *skb; + int (*callback)(struct iwl4965_priv *priv, + struct iwl4965_cmd *cmd, struct sk_buff *skb); + } __attribute__ ((packed)) u; + + /* The CMD_SIZE_HUGE flag bit indicates that the command + * structure is stored at the end of the shared queue memory. */ + u32 flags; + +} __attribute__ ((packed)); + +/** + * struct iwl4965_cmd + * + * For allocation of the command and tx queues, this establishes the overall + * size of the largest command we send to uCode, except for a scan command + * (which is relatively huge; space is allocated separately). + */ +struct iwl4965_cmd { + struct iwl4965_cmd_meta meta; /* driver data */ + struct iwl4965_cmd_header hdr; /* uCode API */ + union { + struct iwl4965_addsta_cmd addsta; + struct iwl4965_led_cmd led; + u32 flags; + u8 val8; + u16 val16; + u32 val32; + struct iwl4965_bt_cmd bt; + struct iwl4965_rxon_time_cmd rxon_time; + struct iwl4965_powertable_cmd powertable; + struct iwl4965_qosparam_cmd qosparam; + struct iwl4965_tx_cmd tx; + struct iwl4965_tx_beacon_cmd tx_beacon; + struct iwl4965_rxon_assoc_cmd rxon_assoc; + u8 *indirect; + u8 payload[360]; + } __attribute__ ((packed)) cmd; +} __attribute__ ((packed)); + +struct iwl4965_host_cmd { + u8 id; + u16 len; + struct iwl4965_cmd_meta meta; + const void *data; +}; + +#define TFD_MAX_PAYLOAD_SIZE (sizeof(struct iwl4965_cmd) - \ + sizeof(struct iwl4965_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 iwl4965_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 iwl4965_rx_mem_buffers + */ +struct iwl4965_rx_queue { + __le32 *bd; + dma_addr_t dma_addr; + struct iwl4965_rx_mem_buffer pool[RX_QUEUE_SIZE + RX_FREE_BUFFERS]; + struct iwl4965_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 + +#define MAX_TID_COUNT 9 + +#define IWL_INVALID_RATE 0xFF +#define IWL_INVALID_VALUE -1 + +#ifdef CONFIG_IWL4965_HT +#ifdef CONFIG_IWL4965_HT_AGG +/** + * struct iwl4965_ht_agg -- aggregation status while waiting for block-ack + * @txq_id: Tx queue used for Tx attempt + * @frame_count: # frames attempted by Tx command + * @wait_for_ba: Expect block-ack before next Tx reply + * @start_idx: Index of 1st Transmit Frame Descriptor (TFD) in Tx window + * @bitmap0: Low order bitmap, one bit for each frame pending ACK in Tx window + * @bitmap1: High order, one bit for each frame pending ACK in Tx window + * @rate_n_flags: Rate at which Tx was attempted + * + * If REPLY_TX indicates that aggregation was attempted, driver must wait + * for block ack (REPLY_COMPRESSED_BA). This struct stores tx reply info + * until block ack arrives. + */ +struct iwl4965_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_IWL4965_HT_AGG */ +#endif /* CONFIG_IWL4965_HT */ + +struct iwl4965_tid_data { + u16 seq_number; +#ifdef CONFIG_IWL4965_HT +#ifdef CONFIG_IWL4965_HT_AGG + struct iwl4965_ht_agg agg; +#endif /* CONFIG_IWL4965_HT_AGG */ +#endif /* CONFIG_IWL4965_HT */ +}; + +struct iwl4965_hw_key { + enum ieee80211_key_alg alg; + int keylen; + u8 key[32]; +}; + +union iwl4965_ht_rate_supp { + u16 rates; + struct { + u8 siso_rate; + u8 mimo_rate; + }; +}; + +#ifdef CONFIG_IWL4965_HT +#define CFG_HT_RX_AMPDU_FACTOR_DEF (0x3) +#define CFG_HT_MPDU_DENSITY_2USEC (0x5) +#define CFG_HT_MPDU_DENSITY_DEF CFG_HT_MPDU_DENSITY_2USEC + +struct iwl_ht_info { + /* self configuration data */ + u8 is_ht; + u8 supported_chan_width; + u16 tx_mimo_ps_mode; + u8 is_green_field; + u8 sgf; /* HT_SHORT_GI_* short guard interval */ + u8 max_amsdu_size; + u8 ampdu_factor; + u8 mpdu_density; + u8 supp_mcs_set[16]; + /* BSS related data */ + u8 control_channel; + u8 extension_chan_offset; + u8 tx_chan_width; + u8 ht_protection; + u8 non_GF_STA_present; +}; +#endif /*CONFIG_IWL4965_HT */ + +#ifdef CONFIG_IWL4965_QOS + +union iwl4965_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 structures */ +struct iwl4965_qos_info { + int qos_enable; + int qos_active; + union iwl4965_qos_capabity qos_cap; + struct iwl4965_qosparam_cmd def_qos_parm; +}; +#endif /*CONFIG_IWL4965_QOS */ + +#define STA_PS_STATUS_WAKE 0 +#define STA_PS_STATUS_SLEEP 1 + +struct iwl4965_station_entry { + struct iwl4965_addsta_cmd sta; + struct iwl4965_tid_data tid[MAX_TID_COUNT]; + u8 used; + u8 ps_status; + struct iwl4965_hw_key keyinfo; +}; + +/* one for each uCode image (inst/data, boot/init/runtime) */ +struct fw_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 iwl4965_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 iwl4965_ibss_seq { + u8 mac[ETH_ALEN]; + u16 seq_num; + u16 frag_num; + unsigned long packet_time; + struct list_head list; +}; + +/** + * struct iwl4965_driver_hw_info + * @max_txq_num: Max # Tx queues supported + * @ac_queue_count: # Tx queues for EDCA Access Categories (AC) + * @tx_cmd_len: Size of Tx command (but not including frame itself) + * @max_rxq_size: Max # Rx frames in Rx queue (must be power-of-2) + * @rx_buffer_size: + * @max_rxq_log: Log-base-2 of max_rxq_size + * @max_stations: + * @bcast_sta_id: + * @shared_virt: Pointer to driver/uCode shared Tx Byte Counts and Rx status + * @shared_phys: Physical Pointer to Tx Byte Counts and Rx status + */ +struct iwl4965_driver_hw_info { + u16 max_txq_num; + u16 ac_queue_count; + u16 tx_cmd_len; + u16 max_rxq_size; + u32 rx_buf_size; + u32 max_pkt_size; + u16 max_rxq_log; + u8 max_stations; + u8 bcast_sta_id; + void *shared_virt; + dma_addr_t shared_phys; +}; + +#define HT_SHORT_GI_20MHZ_ONLY (1 << 0) +#define HT_SHORT_GI_40MHZ_ONLY (1 << 1) + + +#define IWL_RX_HDR(x) ((struct iwl4965_rx_frame_hdr *)(\ + x->u.rx_frame.stats.payload + \ + x->u.rx_frame.stats.phy_count)) +#define IWL_RX_END(x) ((struct iwl4965_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 iwl4965_addsta_cmd; +extern int iwl4965_send_add_station(struct iwl4965_priv *priv, + struct iwl4965_addsta_cmd *sta, u8 flags); +extern u8 iwl4965_add_station_flags(struct iwl4965_priv *priv, const u8 *addr, + int is_ap, u8 flags, void *ht_data); +extern int iwl4965_is_network_packet(struct iwl4965_priv *priv, + struct ieee80211_hdr *header); +extern int iwl4965_power_init_handle(struct iwl4965_priv *priv); +extern int iwl4965_eeprom_init(struct iwl4965_priv *priv); +#ifdef CONFIG_IWL4965_DEBUG +extern void iwl4965_report_frame(struct iwl4965_priv *priv, + struct iwl4965_rx_packet *pkt, + struct ieee80211_hdr *header, int group100); +#else +static inline void iwl4965_report_frame(struct iwl4965_priv *priv, + struct iwl4965_rx_packet *pkt, + struct ieee80211_hdr *header, + int group100) {} +#endif +extern void iwl4965_handle_data_packet_monitor(struct iwl4965_priv *priv, + struct iwl4965_rx_mem_buffer *rxb, + void *data, short len, + struct ieee80211_rx_status *stats, + u16 phy_flags); +extern int iwl4965_is_duplicate_packet(struct iwl4965_priv *priv, + struct ieee80211_hdr *header); +extern int iwl4965_rx_queue_alloc(struct iwl4965_priv *priv); +extern void iwl4965_rx_queue_reset(struct iwl4965_priv *priv, + struct iwl4965_rx_queue *rxq); +extern int iwl4965_calc_db_from_ratio(int sig_ratio); +extern int iwl4965_calc_sig_qual(int rssi_dbm, int noise_dbm); +extern int iwl4965_tx_queue_init(struct iwl4965_priv *priv, + struct iwl4965_tx_queue *txq, int count, u32 id); +extern void iwl4965_rx_replenish(void *data); +extern void iwl4965_tx_queue_free(struct iwl4965_priv *priv, struct iwl4965_tx_queue *txq); +extern int iwl4965_send_cmd_pdu(struct iwl4965_priv *priv, u8 id, u16 len, + const void *data); +extern int __must_check iwl4965_send_cmd(struct iwl4965_priv *priv, + struct iwl4965_host_cmd *cmd); +extern unsigned int iwl4965_fill_beacon_frame(struct iwl4965_priv *priv, + struct ieee80211_hdr *hdr, + const u8 *dest, int left); +extern int iwl4965_rx_queue_update_write_ptr(struct iwl4965_priv *priv, + struct iwl4965_rx_queue *q); +extern int iwl4965_send_statistics_request(struct iwl4965_priv *priv); +extern void iwl4965_set_decrypted_flag(struct iwl4965_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 iwl4965_broadcast_addr[ETH_ALEN]; -#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 + * Currently used by iwl-3945-rs... look at restructuring so that it doesn't + * call this... todo... fix that. +*/ +extern u8 iwl4965_sync_station(struct iwl4965_priv *priv, int sta_id, + u16 tx_rate, u8 flags); + +/****************************************************************************** + * + * 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 -- + * iwl4965_ <-- Its part of iwlwifi (should be changed to iwl4965_) + * iwl4965_hw_ <-- Hardware specific (implemented in iwl-XXXX.c by all HW) + * iwlXXXX_ <-- Hardware specific (implemented in iwl-XXXX.c for XXXX) + * iwl4965_bg_ <-- Called from work queue context + * iwl4965_mac_ <-- mac80211 callback + * + ****************************************************************************/ +extern void iwl4965_hw_rx_handler_setup(struct iwl4965_priv *priv); +extern void iwl4965_hw_setup_deferred_work(struct iwl4965_priv *priv); +extern void iwl4965_hw_cancel_deferred_work(struct iwl4965_priv *priv); +extern int iwl4965_hw_rxq_stop(struct iwl4965_priv *priv); +extern int iwl4965_hw_set_hw_setting(struct iwl4965_priv *priv); +extern int iwl4965_hw_nic_init(struct iwl4965_priv *priv); +extern int iwl4965_hw_nic_stop_master(struct iwl4965_priv *priv); +extern void iwl4965_hw_txq_ctx_free(struct iwl4965_priv *priv); +extern void iwl4965_hw_txq_ctx_stop(struct iwl4965_priv *priv); +extern int iwl4965_hw_nic_reset(struct iwl4965_priv *priv); +extern int iwl4965_hw_txq_attach_buf_to_tfd(struct iwl4965_priv *priv, void *tfd, + dma_addr_t addr, u16 len); +extern int iwl4965_hw_txq_free_tfd(struct iwl4965_priv *priv, struct iwl4965_tx_queue *txq); +extern int iwl4965_hw_get_temperature(struct iwl4965_priv *priv); +extern int iwl4965_hw_tx_queue_init(struct iwl4965_priv *priv, + struct iwl4965_tx_queue *txq); +extern unsigned int iwl4965_hw_get_beacon_cmd(struct iwl4965_priv *priv, + struct iwl4965_frame *frame, u8 rate); +extern int iwl4965_hw_get_rx_read(struct iwl4965_priv *priv); +extern void iwl4965_hw_build_tx_cmd_rate(struct iwl4965_priv *priv, + struct iwl4965_cmd *cmd, + struct ieee80211_tx_control *ctrl, + struct ieee80211_hdr *hdr, + int sta_id, int tx_id); +extern int iwl4965_hw_reg_send_txpower(struct iwl4965_priv *priv); +extern int iwl4965_hw_reg_set_txpower(struct iwl4965_priv *priv, s8 power); +extern void iwl4965_hw_rx_statistics(struct iwl4965_priv *priv, + struct iwl4965_rx_mem_buffer *rxb); +extern void iwl4965_disable_events(struct iwl4965_priv *priv); +extern int iwl4965_get_temperature(const struct iwl4965_priv *priv); + +/** + * iwl4965_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. */ -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, - u8 need_to_lock) {} -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, - int 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 */ +extern u8 iwl4965_hw_find_station(struct iwl4965_priv *priv, const u8 *bssid); + +extern int iwl4965_hw_channel_switch(struct iwl4965_priv *priv, u16 channel); +extern int iwl4965_tx_queue_reclaim(struct iwl4965_priv *priv, int txq_id, int index); + +struct iwl4965_priv; + /* - * Forward declare iwl-4965.c functions for base.c + * Forward declare iwl-4965.c functions for iwl-base.c */ -extern int iwl4965_tx_queue_update_wr_ptr(struct iwl_priv *priv, - struct iwl_tx_queue *txq, +extern int iwl4965_eeprom_acquire_semaphore(struct iwl4965_priv *priv); +extern void iwl4965_eeprom_release_semaphore(struct iwl4965_priv *priv); + +extern int iwl4965_tx_queue_update_wr_ptr(struct iwl4965_priv *priv, + struct iwl4965_tx_queue *txq, u16 byte_cnt); -extern void iwl4965_add_station(struct iwl_priv *priv, const u8 * addr, +extern void iwl4965_add_station(struct iwl4965_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, +extern void iwl4965_set_rxon_chain(struct iwl4965_priv *priv); +extern int iwl4965_tx_cmd(struct iwl4965_priv *priv, struct iwl4965_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, u8 need_to_lock); - -extern void iwl4965_chain_noise_reset(struct iwl_priv *priv); -extern void iwl4965_init_sensitivity(struct iwl_priv *priv, u8 flags, +extern int iwl4965_alive_notify(struct iwl4965_priv *priv); +extern void iwl4965_update_rate_scaling(struct iwl4965_priv *priv, u8 mode); +extern void iwl4965_chain_noise_reset(struct iwl4965_priv *priv); +extern void iwl4965_init_sensitivity(struct iwl4965_priv *priv, u8 flags, u8 force); -extern int iwl4965_set_fat_chan_info(struct iwl_priv *priv, int phymode, - int channel, - const struct iwl_eeprom_channel *eeprom_ch, +extern int iwl4965_set_fat_chan_info(struct iwl4965_priv *priv, int phymode, + u16 channel, + const struct iwl4965_eeprom_channel *eeprom_ch, u8 fat_extension_channel); -extern void iwl4965_rf_kill_ct_config(struct iwl_priv *priv); +extern void iwl4965_rf_kill_ct_config(struct iwl4965_priv *priv); + +#ifdef CONFIG_IWL4965_HT +extern void iwl4965_init_ht_hw_capab(struct ieee80211_ht_info *ht_info, + int mode); +extern void iwl4965_set_rxon_ht(struct iwl4965_priv *priv, + struct iwl_ht_info *ht_info); +extern void iwl4965_set_ht_add_station(struct iwl4965_priv *priv, u8 index, + struct ieee80211_ht_info *sta_ht_inf); +#ifdef CONFIG_IWL4965_HT_AGG +extern int iwl4965_mac_ht_tx_agg_start(struct ieee80211_hw *hw, u8 *da, + u16 tid, u16 *start_seq_num); +extern int iwl4965_mac_ht_rx_agg_start(struct ieee80211_hw *hw, u8 *da, + u16 tid, u16 start_seq_num); +extern int iwl4965_mac_ht_rx_agg_stop(struct ieee80211_hw *hw, u8 *da, + u16 tid, int generator); +extern int iwl4965_mac_ht_tx_agg_stop(struct ieee80211_hw *hw, u8 *da, + u16 tid, int generator); +extern void iwl4965_turn_off_agg(struct iwl4965_priv *priv, u8 tid); +#endif /* CONFIG_IWL4965_HT_AGG */ +#endif /*CONFIG_IWL4965_HT */ /* Structures, enum, and defines specific to the 4965 */ #define IWL4965_KW_SIZE 0x1000 /*4k */ -struct iwl_kw { +struct iwl4965_kw { dma_addr_t dma_addr; void *v_addr; size_t size; @@ -141,7 +858,9 @@ struct iwl_kw { #define NRG_NUM_PREV_STAT_L 20 #define NUM_RX_CHAINS (3) -struct iwl_traffic_load { +#define TX_POWER_IWL_ILLEGAL_VOLTAGE -10000 + +struct iwl4965_traffic_load { unsigned long time_stamp; u32 packet_count[TID_QUEUE_MAX_SIZE]; u8 queue_count; @@ -149,8 +868,13 @@ struct iwl_traffic_load { u32 total; }; -#ifdef CONFIG_IWLWIFI_HT_AGG -struct iwl_agg_control { +#ifdef CONFIG_IWL4965_HT_AGG +/** + * struct iwl4965_agg_control + * @requested_ba: bit map of tids requesting aggregation/block-ack + * @granted_ba: bit map of tids granted aggregation/block-ack + */ +struct iwl4965_agg_control { unsigned long next_retry; u32 wait_for_agg_status; u32 tid_retry; @@ -159,17 +883,16 @@ struct iwl_agg_control { u8 auto_agg; u32 tid_traffic_load_threshold; u32 ba_timeout; - struct iwl_traffic_load traffic_load[TID_MAX_LOAD_COUNT]; + struct iwl4965_traffic_load traffic_load[TID_MAX_LOAD_COUNT]; }; -#endif /*CONFIG_IWLWIFI_HT_AGG */ +#endif /*CONFIG_IWL4965_HT_AGG */ -struct iwl_lq_mngr { -#ifdef CONFIG_IWLWIFI_HT_AGG - struct iwl_agg_control agg_ctrl; +struct iwl4965_lq_mngr { +#ifdef CONFIG_IWL4965_HT_AGG + struct iwl4965_agg_control agg_ctrl; #endif spinlock_t lock; s32 max_window_size; - struct iwl_rate_scaling_cmd scale_rate_cmd; s32 *expected_tpt; u8 *next_higher_rate; u8 *next_lower_rate; @@ -177,31 +900,16 @@ struct iwl_lq_mngr { 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 IN_BAND_FILTER 0xFF #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 (0) -#define SENSITIVITY_CMD_CONTROL_WORK_TABLE (1) - #define CHAIN_NOISE_MAX_DELTA_GAIN_CODE 3 #define MAX_FA_OFDM 50 @@ -229,8 +937,6 @@ struct iwl_lq_mngr { #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 @@ -243,26 +949,27 @@ struct iwl_lq_mngr { #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 { +enum iwl4965_false_alarm_state { IWL_FA_TOO_MANY = 0, IWL_FA_TOO_FEW = 1, IWL_FA_GOOD_RANGE = 2, }; -enum iwl_chain_noise_state { +enum iwl4965_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 { +enum iwl4965_sensitivity_state { IWL_SENS_CALIB_ALLOWED = 0, IWL_SENS_CALIB_NEED_REINIT = 1, }; -enum iwl_calib_enabled_state { +enum iwl4965_calib_enabled_state { IWL_CALIB_DISABLED = 0, /* must be 0 */ IWL_CALIB_ENABLED = 1, }; @@ -277,7 +984,7 @@ struct statistics_general_data { }; /* Sensitivity calib data */ -struct iwl_sensitivity_data { +struct iwl4965_sensitivity_data { u32 auto_corr_ofdm; u32 auto_corr_ofdm_mrc; u32 auto_corr_ofdm_x1; @@ -306,7 +1013,7 @@ struct iwl_sensitivity_data { }; /* Chain noise (differential Rx gain) calib data */ -struct iwl_chain_noise_data { +struct iwl4965_chain_noise_data { u8 state; u16 beacon_count; u32 chain_noise_a; @@ -320,26 +1027,323 @@ struct iwl_chain_noise_data { 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 - -#endif /* IWL == 4965 */ -#endif /* __iwl_4965_h__ */ +#define EEPROM_SEM_TIMEOUT 10 /* milliseconds */ +#define EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ + + +#ifdef CONFIG_IWL4965_SPECTRUM_MEASUREMENT + +enum { + MEASUREMENT_READY = (1 << 0), + MEASUREMENT_ACTIVE = (1 << 1), +}; + +#endif + +struct iwl4965_priv { + + /* ieee device used by generic ieee processing code */ + struct ieee80211_hw *hw; + struct ieee80211_channel *ieee_channels; + struct ieee80211_rate *ieee_rates; + struct ieee80211_conf *cache_conf; + + /* temporary frame storage list */ + struct list_head free_frames; + int frames_count; + + u8 phymode; + int alloc_rxb_skb; + + void (*rx_handlers[REPLY_MAX])(struct iwl4965_priv *priv, + struct iwl4965_rx_mem_buffer *rxb); + + const struct ieee80211_hw_mode *modes; + +#ifdef CONFIG_IWL4965_SPECTRUM_MEASUREMENT + /* spectrum measurement report caching */ + struct iwl4965_spectrum_notification measure_report; + u8 measurement_status; +#endif + /* ucode beacon time */ + u32 ucode_beacon_time; + + /* we allocate array of iwl4965_channel_info for NIC's valid channels. + * Access via channel # using indirect index array */ + struct iwl4965_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 iwl4965_clip_group clip_groups[5]; + + /* thermal calibration */ + s32 temperature; /* degrees Kelvin */ + s32 last_temperature; + + /* Scan related variables */ + unsigned long last_scan_jiffies; + unsigned long next_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 iwl4965_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_desc ucode_code; /* runtime inst */ + struct fw_desc ucode_data; /* runtime data original */ + struct fw_desc ucode_data_backup; /* runtime data save/restore */ + struct fw_desc ucode_init; /* initialization inst */ + struct fw_desc ucode_init_data; /* initialization data */ + struct fw_desc ucode_boot; /* bootstrap inst */ + + + struct iwl4965_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 iwl4965_rxon_cmd active_rxon; + struct iwl4965_rxon_cmd staging_rxon; + + int error_recovering; + struct iwl4965_rxon_cmd recovery_rxon; + + /* 1st responses from initialize and runtime uCode images. + * 4965's initialize alive response contains some calibration data. */ + struct iwl4965_init_alive_resp card_alive_init; + struct iwl4965_alive_resp card_alive; + +#ifdef LED + /* LED related variables */ + struct iwl4965_activity_blink activity; + unsigned long led_packets; + int led_state; +#endif + + u16 active_rate; + u16 active_rate_basic; + + u8 call_post_assoc_from_beacon; + u8 assoc_station_added; + u8 use_ant_b_for_management_frame; /* Tx antenna selection */ + u8 valid_antenna; /* Bit mask of antennas actually connected */ +#ifdef CONFIG_IWL4965_SENSITIVITY + struct iwl4965_sensitivity_data sensitivity_data; + struct iwl4965_chain_noise_data chain_noise_data; + u8 start_calib; + __le16 sensitivity_tbl[HD_TABLE_SIZE]; +#endif /*CONFIG_IWL4965_SENSITIVITY*/ + +#ifdef CONFIG_IWL4965_HT + struct iwl_ht_info current_ht_config; +#endif + u8 last_phy_res[100]; + + /* Rate scaling data */ + struct iwl4965_lq_mngr lq_mngr; + + /* 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 iwl4965_rx_queue rxq; + struct iwl4965_tx_queue txq[IWL_MAX_NUM_QUEUES]; + unsigned long txq_ctx_active_msk; + struct iwl4965_kw kw; /* keep warm address */ + u32 scd_base_addr; /* scheduler sram base address */ + + unsigned long status; + u32 config; + + int last_rx_rssi; /* From Rx packet statisitics */ + int last_rx_noise; /* From beacon statistics */ + + struct iwl4965_power_mgr power_data; + + struct iwl4965_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; + int num_stations; + struct iwl4965_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; + + /* Hash table for finding stations in IBSS network */ + struct list_head ibss_mac_hash[IWL_IBSS_MAC_HASH_SIZE]; + + /* eeprom */ + struct iwl4965_eeprom eeprom; + + int iw_mode; + + struct sk_buff *ibss_beacon; + + /* Last Rx'd beacon timestamp */ + u32 timestamp0; + u32 timestamp1; + u16 beacon_int; + struct iwl4965_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_IWL4965_QOS + struct iwl4965_qos_info qos_data; +#endif /*CONFIG_IWL4965_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 work_struct init_alive_start; + struct work_struct alive_start; + struct work_struct activity_timer; + struct work_struct thermal_periodic; + struct work_struct gather_stats; + struct work_struct scan_check; + struct work_struct post_associate; + +#define IWL_DEFAULT_TX_POWER 0x0F + s8 user_txpower_limit; + s8 max_channel_txpower_limit; + +#ifdef CONFIG_PM + u32 pm_state[16]; +#endif + +#ifdef CONFIG_IWL4965_DEBUG + /* debugging info */ + u32 framecnt_to_us; + atomic_t restrict_refcnt; +#endif + + struct work_struct txpower_work; +#ifdef CONFIG_IWL4965_SENSITIVITY + struct work_struct sensitivity_work; +#endif + struct work_struct statistics_work; + struct timer_list statistics_periodic; + +#ifdef CONFIG_IWL4965_HT_AGG + struct work_struct agg_work; +#endif +}; /*iwl4965_priv */ + +static inline int iwl4965_is_associated(struct iwl4965_priv *priv) +{ + return (priv->active_rxon.filter_flags & RXON_FILTER_ASSOC_MSK) ? 1 : 0; +} + +static inline int is_channel_valid(const struct iwl4965_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 iwl4965_channel_info *ch_info) +{ + return (ch_info->flags & EEPROM_CHANNEL_NARROW) ? 1 : 0; +} + +static inline int is_channel_radar(const struct iwl4965_channel_info *ch_info) +{ + return (ch_info->flags & EEPROM_CHANNEL_RADAR) ? 1 : 0; +} + +static inline u8 is_channel_a_band(const struct iwl4965_channel_info *ch_info) +{ + return ch_info->phymode == MODE_IEEE80211A; +} + +static inline u8 is_channel_bg_band(const struct iwl4965_channel_info *ch_info) +{ + return ((ch_info->phymode == MODE_IEEE80211B) || + (ch_info->phymode == MODE_IEEE80211G)); +} + +static inline int is_channel_passive(const struct iwl4965_channel_info *ch) +{ + return (!(ch->flags & EEPROM_CHANNEL_ACTIVE)) ? 1 : 0; +} + +static inline int is_channel_ibss(const struct iwl4965_channel_info *ch) +{ + return ((ch->flags & EEPROM_CHANNEL_IBSS)) ? 1 : 0; +} + +extern const struct iwl4965_channel_info *iwl4965_get_channel_info( + const struct iwl4965_priv *priv, int phymode, u16 channel); + +/* Requires full declaration of iwl4965_priv before including */ +#include "iwl-4965-io.h" + +#endif /* __iwl4965_4965_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-channel.h b/drivers/net/wireless/iwlwifi/iwl-channel.h index d320c2b..023c3f2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-channel.h +++ b/drivers/net/wireless/iwlwifi/iwl-channel.h @@ -136,15 +136,13 @@ static inline int is_channel_radar(const struct iwl_channel_info *ch_info) static inline u8 is_channel_a_band(const struct iwl_channel_info *ch_info) { - return ((ch_info->phymode == MODE_IEEE80211A) || - (ch_info->phymode == MODE_ATHEROS_TURBO)) ? 1 : 0; + 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) || - (ch_info->phymode == MODE_ATHEROS_TURBOG)) ? 1 : 0; + (ch_info->phymode == MODE_IEEE80211G)); } static inline int is_channel_passive(const struct iwl_channel_info *ch) @@ -158,6 +156,6 @@ static inline int is_channel_ibss(const struct iwl_channel_info *ch) } extern const struct iwl_channel_info *iwl_get_channel_info( - const struct iwl_priv *priv, int phymode, int channel); + const struct iwl_priv *priv, int phymode, u16 channel); #endif diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h index f0c0107..9de8d7f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-commands.h @@ -68,7 +68,7 @@ enum { REPLY_ALIVE = 0x1, REPLY_ERROR = 0x2, - /* RXON state commands */ + /* RXON and QOS commands */ REPLY_RXON = 0x10, REPLY_RXON_ASSOC = 0x11, REPLY_QOS_PARAM = 0x13, @@ -76,39 +76,27 @@ enum { /* Multi-Station support */ REPLY_ADD_STA = 0x18, -#if IWL == 3945 - REPLY_REMOVE_STA = 0x19, - REPLY_REMOVE_ALL_STA = 0x1a, -#endif + REPLY_REMOVE_STA = 0x19, /* not used */ + REPLY_REMOVE_ALL_STA = 0x1a, /* not used */ - /* RX, TX */ + /* RX, TX, LEDs */ #if IWL == 3945 - REPLY_3945_RX = 0x1b, + REPLY_3945_RX = 0x1b, /* 3945 only */ #endif - REPLY_TX = 0x1c, - - /* timers commands */ - REPLY_BCON = 0x27, - -#if IWL == 4965 - REPLY_SHUTDOWN = 0x40, -#endif - - /* MISC commands */ - REPLY_RATE_SCALE = 0x47, + REPLY_RATE_SCALE = 0x47, /* 3945 only */ REPLY_LEDS_CMD = 0x48, - REPLY_TX_LINK_QUALITY_CMD = 0x4e, + REPLY_TX_LINK_QUALITY_CMD = 0x4e, /* 4965 only */ /* 802.11h related */ - RADAR_NOTIFICATION = 0x70, - REPLY_QUIET_CMD = 0x71, + 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 Management */ POWER_TABLE_CMD = 0x77, PM_SLEEP_NOTIFICATION = 0x7A, PM_DEBUG_STATISTIC_NOTIFIC = 0x7B, @@ -116,7 +104,6 @@ enum { /* 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, @@ -124,110 +111,602 @@ enum { /* IBSS/AP commands */ BEACON_NOTIFICATION = 0x90, REPLY_TX_BEACON = 0x91, - WHO_IS_AWAKE_NOTIFICATION = 0x94, + WHO_IS_AWAKE_NOTIFICATION = 0x94, /* not used */ - QUIET_NOTIFICATION = 0x96, + /* Miscellaneous commands */ + QUIET_NOTIFICATION = 0x96, /* not used */ REPLY_TX_PWR_TABLE_CMD = 0x97, - MEASURE_ABORT_NOTIFICATION = 0x99, - - REPLY_CALIBRATION_TUNE = 0x9a, + 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 *** */ + /* RF-KILL commands and notifications */ REPLY_CARD_STATE_CMD = 0xa0, CARD_STATE_NOTIFICATION = 0xa1, /* Missed beacons notification */ MISSED_BEACONS_NOTIFICATION = 0xa2, - MISSED_BEACONS_NOTIFICATION_TH_CMD = 0xa3, #if IWL == 4965 REPLY_CT_KILL_CONFIG_CMD = 0xa4, SENSITIVITY_CMD = 0xa8, REPLY_PHY_CALIBRATION_CMD = 0xb0, - REPLY_4965_RX = 0xc3, 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) + /* - * Tx Command & Response: + * 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)); -/* Tx flags */ -enum { - TX_CMD_FLG_RTS_MSK = (1 << 1), - TX_CMD_FLG_CTS_MSK = (1 << 2), - TX_CMD_FLG_ACK_MSK = (1 << 3), - TX_CMD_FLG_STA_RATE_MSK = (1 << 4), - TX_CMD_FLG_IMM_BA_RSP_MASK = (1 << 6), - TX_CMD_FLG_FULL_TXOP_PROT_MSK = (1 << 7), - TX_CMD_FLG_ANT_SEL_MSK = 0xf00, - TX_CMD_FLG_ANT_A_MSK = (1 << 8), - TX_CMD_FLG_ANT_B_MSK = (1 << 9), +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; - /* ucode ignores BT priority for this frame */ - TX_CMD_FLG_BT_DIS_MSK = (1 << 12), +#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)); - /* ucode overrides sequence control */ - TX_CMD_FLG_SEQ_CTL_MSK = (1 << 13), +union tsf { + u8 byte[8]; + __le16 word[4]; + __le32 dw[2]; +}; - /* signal that this frame is non-last MPDU */ - TX_CMD_FLG_MORE_FRAG_MSK = (1 << 14), +/* + * 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)); - /* calculate TSF in outgoing frame */ - TX_CMD_FLG_TSF_MSK = (1 << 16), +/****************************************************************************** + * (1) + * RXON Commands & Responses: + * + *****************************************************************************/ - /* activate TX calibration. */ - TX_CMD_FLG_CALIB_MSK = (1 << 17), +/* + * 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, +}; - /* signals that 2 bytes pad was inserted - * after the MAC header */ - TX_CMD_FLG_MH_PAD_MSK = (1 << 20), +/* 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) - /* HCCA-AP - disable duration overwriting. */ - TX_CMD_FLG_DUR_MSK = (1 << 25), -}; +/* + * 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)); /* - * TX command security control + * REPLY_RXON_ASSOC = 0x11 (command, has simple generic response) */ -#define TX_CMD_SEC_CCM 0x2 -#define TX_CMD_SEC_TKIP 0x3 +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)); /* - * TX command Frame life time + * 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_rate { - union { - struct { - u8 rate; - u8 flags; - } s; - __le16 rate_n_flags; - }; +struct iwl_power_per_rate { + u8 rate; /* plcp */ + struct iwl_tx_power tpc; + u8 reserved; } __attribute__ ((packed)); + #elif IWL == 4965 -struct iwl_rate { - union { - struct { - u8 rate; - u8 flags; - __le16 ext_flags; - } s; - __le32 rate_n_flags; - }; +#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 + */ +#define IWL_AP_ID 0 +#define IWL_MULTICAST_ID 1 +#define IWL_STA_ID 2 + +#define IWL3945_BROADCAST_ID 24 +#define IWL3945_STATION_COUNT 25 + +#define IWL4965_BROADCAST_ID 31 +#define IWL4965_STATION_COUNT 32 + +#define IWL_STATION_COUNT 32 /* MAX(3945,4965)*/ +#define IWL_INVALID_STATION 255 + +#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 */ +#define STA_MODIFY_KEY_MASK 0x01 +#define STA_MODIFY_TID_DISABLE_TX 0x02 +#define STA_MODIFY_TX_RATE_MSK 0x04 +#define STA_MODIFY_ADDBA_TID_MSK 0x08 +#define STA_MODIFY_DELBA_TID_MSK 0x10 +#define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid)) + +/* + * 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)); + +/* Fixed (non-configurable) rx data from phy */ +#define RX_PHY_FLAGS_ANTENNAE_OFFSET (4) +#define RX_PHY_FLAGS_ANTENNAE_MASK (0x70) +#define IWL_AGC_DB_MASK (0x3f80) /* MASK(7,13) */ +#define IWL_AGC_DB_POS (7) +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)); + +/* + * REPLY_4965_RX = 0xc3 (response only, not a command) + * Used only for legacy (non 11n) frames. + */ +#define RX_RES_PHY_CNT 14 +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)); + + +/****************************************************************************** + * (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_WEP 0x01 +#define TX_CMD_SEC_CCM 0x02 +#define TX_CMD_SEC_TKIP 0x03 +#define TX_CMD_SEC_MSK 0x03 +#define TX_CMD_SEC_SHIFT 6 +#define TX_CMD_SEC_KEY128 0x08 + +/* + * TX command Frame life time + */ struct iwl_dram_scratch { u8 try_cnt; @@ -235,6 +714,9 @@ struct iwl_dram_scratch { __le16 reserved; } __attribute__ ((packed)); +/* + * REPLY_TX = 0x1c (command) + */ struct iwl_tx_cmd { __le16 len; __le16 next_frame_len; @@ -245,7 +727,7 @@ struct iwl_tx_cmd { u8 tid_tspec; #elif IWL == 4965 struct iwl_dram_scratch scratch; - struct iwl_rate rate; + __le32 rate_n_flags; u8 sta_id; #endif u8 sec_ctl; @@ -289,8 +771,32 @@ struct iwl_tx_cmd { struct ieee80211_hdr hdr[0]; } __attribute__ ((packed)); -/* - * TX command response status +/* 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, @@ -313,11 +819,9 @@ enum { TX_STATUS_FAIL_NO_BEACON_ON_RADAR = 0x91, }; -enum { - TX_PACKET_MODE_REGULAR = 0x0000, - TX_PACKET_MODE_BURST_PART = 0x00100, - TX_PACKET_MODE_BURST_FIRST = 0x0200, -}; +#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, @@ -365,69 +869,403 @@ enum { #define AGG_TX_STATE_SEQ_NUM_POS 16 #define AGG_TX_STATE_SEQ_NUM_MSK 0xffff0000 -struct iwl_tx_resp { +/* + * REPLY_TX = 0x1c (response) + */ #if IWL == 4965 +struct iwl_tx_resp { u8 frame_count; /* 1 no aggregation, >1 aggregation */ u8 bt_kill_count; -#endif u8 failure_rts; u8 failure_frame; -#if IWL == 3945 - u8 bt_kill_count; - u8 rate; - __le32 wireless_media_time; -#elif IWL == 4965 - struct iwl_rate rate; + __le32 rate_n_flags; __le16 wireless_media_time; __le16 reserved; __le32 pa_power1; __le32 pa_power2; -#endif __le32 status; /* TX status (for aggregation status of 1st frame) */ } __attribute__ ((packed)); -/* TX command response is sent after *all* transmission attempts. +#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 + +/* + * 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)); + +/* + * REPLY_TX_PWR_TABLE_CMD = 0x97 (command, has simple generic response) + */ +struct iwl_txpowertable_cmd { + u8 band; /* 0: 5 GHz, 1: 2.4 GHz */ + u8 reserved; + __le16 channel; +#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)); + +#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 * - * NOTES: + * REPLY_RATE_SCALE = 0x47 (command, has simple generic response) * - * TX_STATUS_FAIL_NEXT_FRAG + * 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. * - * 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'. + * 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: * - * TX_STATUS_FIFO_UNDERRUN + *****************************************************************************/ + +/* + * 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: * - * Indicates the host did not provide bytes to the FIFO fast enough while - * a TX was in progress. + *****************************************************************************/ + +/** + * struct iwl_powertable_cmd - Power Table Command + * @flags: See below: * - * TX_STATUS_FAIL_MGMNT_ABORT + * POWER_TABLE_CMD = 0x77 (command, has simple generic response) * - * This status is only possible if the ABORT ON MGMT RX parameter was - * set to true with the TX command. + * 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 * - * 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. + * 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 -struct iwl_tx_power { - u8 tx_gain; /* gain for analog radio */ - u8 dsp_atten; /* gain for DSP */ + +#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 { - u8 type; /* 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:6 reserved - * 7:7 Narrow + * 5:7 reserved */ + u8 type; u8 channel; struct iwl_tx_power tpc; __le16 active_dwell; @@ -441,11 +1279,13 @@ struct iwl_ssid_ie { } __attribute__ ((packed)); #define PROBE_OPTION_MAX 0x4 -#define TX_CMD_FLG_SEQ_CTL_MSK 0x2000 -#define TX_CMD_LIFE_TIME_INFINITE 0xFFFFFFFF -#define IWL_GOOD_CRC_TH (1) - +#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; @@ -485,225 +1325,410 @@ struct iwl_scan_cmd { */ } __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 + /* - * RXON-ASSOCIATED Command & Response + * REPLY_SCAN_CMD = 0x80 (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; +struct iwl_scanreq_notification { + __le32 status; /* 1: okay, 2: cannot fulfill request */ } __attribute__ ((packed)); /* - * RXON Command & Response + * SCAN_START_NOTIFICATION = 0x82 (notification only, not a command) */ -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)); - -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; +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 PHY_CALIBRATE_DIFF_GAIN_CMD (7) -#define HD_TABLE_SIZE (11) +#define SCAN_OWNER_STATUS 0x1; +#define MEASURE_OWNER_STATUS 0x2; -struct iwl_sensitivity_cmd { - __le16 control; - __le16 table[HD_TABLE_SIZE]; +#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)); -struct iwl_calibration_cmd { - u8 opCode; - u8 flags; - __le16 reserved; - s8 diff_gain_a; - s8 diff_gain_b; - s8 diff_gain_c; - u8 reserved1; +/* + * 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)); -struct iwl_missed_beacon_notif { - __le32 consequtive_missed_beacons; - __le32 total_missed_becons; - __le32 num_expected_beacons; - __le32 num_recvd_beacons; + +/****************************************************************************** + * (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)); -struct iwl_ct_kill_config { - u32 reserved; - u32 critical_temperature_M; - u32 critical_temperature_R; -} __attribute__ ((packed)); /* - * Add/Modify Station Command & Response + * REPLY_TX_BEACON = 0x91 (command, has simple generic response) */ -struct iwl_keyinfo { - __le16 key_flags; - u8 tkip_rx_tsc_byte2; /* TSC[2] for key mix ph1 detection */ +struct iwl_tx_beacon_cmd { + struct iwl_tx_cmd tx; + __le16 tim_idx; + u8 tim_size; u8 reserved1; - __le16 tkip_rx_ttak[5]; /* 10-byte unicast TKIP TTAK */ - __le16 reserved2; - u8 key[16]; /* 16-byte unicast decryption key */ + struct ieee80211_hdr frame[0]; /* beacon frame */ } __attribute__ ((packed)); -struct sta_id_modify { - u8 addr[ETH_ALEN]; - __le16 reserved1; - u8 sta_id; - u8 modify_mask; - __le16 reserved2; -} __attribute__ ((packed)); +/****************************************************************************** + * (10) + * Statistics Commands and Notifications: + * + *****************************************************************************/ -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; +#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 { - struct { - u8 rate; - u8 flags; - } s; - __le16 rate_n_flags; - } tx_rate; - u8 add_immediate_ba_tid; - u8 remove_immediate_ba_tid; - __le16 add_immediate_ba_start_seq; + __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 iwl_add_sta_resp { - u8 status; +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)); -#define ADD_STA_SUCCESS_MSK 0x1 +/* + * 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)); -/** - * struct iwl_powertable_cmd - Power Table Command & Response - * @flags: See below: +/* + * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command) * - * 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 + * 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. * - * 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. + * 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. */ -#define IWL_POWER_TABLE_SIZE 5 +#define STATISTICS_REPLY_FLG_BAND_24G_MSK __constant_cpu_to_le32(0x2) +#define STATISTICS_REPLY_FLG_FAT_MODE_MSK __constant_cpu_to_le32(0x8) +struct iwl_notif_statistics { + __le32 flag; + struct statistics_rx rx; + struct statistics_tx tx; + struct statistics_general general; +} __attribute__ ((packed)); -enum { - IWL_POWER_DRIVER_ALLOW_SLEEP_MSK = (1<<0), - IWL_POWER_SLEEP_OVER_DTIM_MSK = (1<<2), - IWL_POWER_PCI_PM_MSK = (1<<3), -}; -struct iwl_powertable_cmd { -#if IWL == 3945 - __le32 flags; -#elif IWL == 4965 - __le16 flags; - u8 keep_alive_seconds; - u8 debug_flags; -#endif - __le32 rx_data_timeout; - __le32 tx_data_timeout; - __le32 sleep_interval[IWL_POWER_TABLE_SIZE]; -#if IWL == 4965 - __le32 keep_alive_beacons; -#endif +/* + * 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: + * + *****************************************************************************/ -struct iwl_rate_scaling_info { - union { - struct { - u8 tx_rate; - u8 flags; - } s; - __le16 rate_n_flags; - }; - u8 try_cnt; - u8 next_rate_index; +#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_rate_scaling_cmd - Rate Scaling Command & Response +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: * - * 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. + *****************************************************************************/ + +/* + * LEDs Command & Response + * REPLY_LEDS_CMD = 0x48 (command, has simple generic response) * - * 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) + * 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_rate_scaling_cmd { - u8 table_id; - u8 reserved[3]; - struct iwl_rate_scaling_info table[IWL_MAX_RATES]; +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/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h index 19ea474..abd344c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/iwlwifi/iwl-debug.h @@ -35,10 +35,18 @@ extern u32 iwl_debug_level; 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 */ /* @@ -58,11 +66,11 @@ static inline void IWL_DEBUG(int level, const char *fmt, ...) * * To add your debug level to the list of levels seen when you perform * - * % cat /proc/net/ipw/debug_level + * % cat /proc/net/iwl/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 + * If you do not see debug_level in /proc/net/iwl then you do not have * CONFIG_IWLWIFI_DEBUG defined in your kernel configuration * */ @@ -123,6 +131,7 @@ static inline void IWL_DEBUG(int level, const char *fmt, ...) #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) diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.h b/drivers/net/wireless/iwlwifi/iwl-eeprom.h index 19d7028..e473c97 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.h +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.h @@ -69,6 +69,8 @@ * */ +#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 @@ -142,7 +144,7 @@ struct iwl_eeprom_channel { struct iwl_eeprom_txpower_sample { u8 gain_index; /* index into power (gain) setup table ... */ s8 power; /* ... for this pwr level for this chnl group */ - __le16 v_det; /* PA output voltage */ + u16 v_det; /* PA output voltage */ } __attribute__ ((packed)); /* @@ -155,14 +157,14 @@ struct iwl_eeprom_txpower_sample { */ struct iwl_eeprom_txpower_group { struct iwl_eeprom_txpower_sample samples[5]; /* 5 power levels */ - __le32 a, b, c, d, e; /* coefficients for voltage->power + s32 a, b, c, d, e; /* coefficients for voltage->power * formula (signed) */ - __le32 Fa, Fb, Fc, Fd, Fe; /* these modify coeffs based on + 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 */ - __le16 temperature; /* h/w temperature at factory calib this band + s16 temperature; /* h/w temperature at factory calib this band * (signed) */ } __attribute__ ((packed)); @@ -173,11 +175,11 @@ struct iwl_eeprom_txpower_group { * Data copied from EEPROM. */ struct iwl_eeprom_temperature_corr { - __le32 Ta; - __le32 Tb; - __le32 Tc; - __le32 Td; - __le32 Te; + u32 Ta; + u32 Tb; + u32 Tc; + u32 Td; + u32 Te; } __attribute__ ((packed)); #if IWL == 4965 @@ -187,32 +189,31 @@ struct iwl_eeprom_temperature_corr { #define EEPROM_TX_POWER_VERSION (2) #define EEPROM_TX_POWER_VERSION_NEW (5) -struct iwl_eeprom_calib_measurement { +struct iwl_eeprom_calib_measure { u8 temperature; u8 gain_idx; u8 actual_pow; s8 pa_det; } __attribute__ ((packed)); -struct iwl_eeprom_calib_channel_info { +struct iwl_eeprom_calib_ch_info { u8 ch_num; - struct iwl_eeprom_calib_measurement - measurements[EEPROM_TX_POWER_TX_CHAINS][EEPROM_TX_POWER_MEASUREMENTS]; + 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_channel_info ch1; - struct iwl_eeprom_calib_channel_info ch2; + 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; - __le16 voltage; /* signed */ - struct iwl_eeprom_calib_subband_info - band_info_tbl[EEPROM_TX_POWER_BANDS]; + s16 voltage; /* signed */ + struct iwl_eeprom_calib_subband_info band_info[EEPROM_TX_POWER_BANDS]; } __attribute__ ((packed)); #endif @@ -220,32 +221,32 @@ struct iwl_eeprom_calib_info { struct iwl_eeprom { u8 reserved0[16]; #define EEPROM_DEVICE_ID (2*0x08) /* 2 bytes */ - __le16 device_id; /* abs.ofs: 16 */ + u16 device_id; /* abs.ofs: 16 */ u8 reserved1[2]; #define EEPROM_PMC (2*0x0A) /* 2 bytes */ - __le16 pmc; /* abs.ofs: 20 */ + 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 */ - __le16 board_revision; /* abs.ofs: 106 */ + 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 */ - __le16 version; /* abs.ofs: 136 */ + 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 */ - __le16 oem_mode; + u16 oem_mode; #define EEPROM_WOWLAN_MODE (2*0x47) /* 2 bytes */ - __le16 wowlan_mode; /* abs.ofs: 142 */ + u16 wowlan_mode; /* abs.ofs: 142 */ #define EEPROM_LEDS_TIME_INTERVAL (2*0x48) /* 2 bytes */ - __le16 leds_time_interval; /* abs.ofs: 144 */ + 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 */ @@ -259,7 +260,7 @@ struct iwl_eeprom { #else u8 reserved6[8]; #define EEPROM_4965_BOARD_REVISION (2*0x4F) /* 2 bytes */ - __le16 board_revision_4965; /* abs.ofs: 158 */ + 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 */ @@ -268,23 +269,23 @@ struct iwl_eeprom { #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 */ - __le16 band_1_count; /* abs.ofs: 196 */ + 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 */ - __le16 band_2_count; /* abs.ofs: 226 */ + 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 */ - __le16 band_3_count; /* abs.ofs: 254 */ + 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 */ - __le16 band_4_count; /* abs.ofs: 280 */ + 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 */ - __le16 band_5_count; /* abs.ofs: 304 */ + 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 */ @@ -316,10 +317,10 @@ struct iwl_eeprom { struct iwl_eeprom_channel band_52_channels[11]; /* abs.ofs: 336 */ u8 reserved12[6]; #define EEPROM_CALIB_VERSION_OFFSET (2*0xB6) /* 2 bytes */ - __le16 calib_version; /* abs.ofs: 364 */ + u16 calib_version; /* abs.ofs: 364 */ u8 reserved13[2]; #define EEPROM_SATURATION_POWER_OFFSET (2*0xB8) /* 2 bytes */ - __le16 satruation_power; /* abs.ofs: 368 */ + 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 */ diff --git a/drivers/net/wireless/iwlwifi/iwl-helpers.h b/drivers/net/wireless/iwlwifi/iwl-helpers.h index c29a568..e2a8d95 100644 --- a/drivers/net/wireless/iwlwifi/iwl-helpers.h +++ b/drivers/net/wireless/iwlwifi/iwl-helpers.h @@ -30,6 +30,8 @@ #ifndef __iwl_helpers_h__ #define __iwl_helpers_h__ +#include <linux/ctype.h> + /* * The structures defined by the hardware/uCode interface * have bit-wise operations. For each bit-field there is @@ -57,13 +59,14 @@ * NOTE: If used from IWL_GET_BITS then pos and len are compile-constants and * will collapse to minimal code by the compiler. */ -#define iwl_get_bits(src, pos, len) \ -({ \ - u32 __tmp = le32_to_cpu(src); \ - __tmp >>= pos; \ - __tmp &= (1UL << len) - 1; \ - __tmp; \ -}) +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 @@ -78,13 +81,23 @@ * NOTE: If used IWL_SET_BITS pos and len will be compile-constants and * will collapse to minimal code by the compiler. */ -#define iwl_set_bits(dst, pos, len, val) \ -({ \ - u32 __tmp = le32_to_cpu(*dst); \ - __tmp &= ~((1ULL << (pos+len)) - (1 << pos)); \ - __tmp |= (val & ((1UL << len) - 1)) << pos; \ - *dst = cpu_to_le32(__tmp); \ -}) +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: @@ -108,46 +121,18 @@ * and iwl_{get,set}_bits. * */ -#define _IWL_SET_BITS(s, d, o, l, v) \ - iwl_set_bits(&s.d, o, l, v) - #define IWL_SET_BITS(s, sym, v) \ - _IWL_SET_BITS((s), IWL_ ## sym ## _SYM, IWL_ ## sym ## _POS, \ - IWL_ ## sym ## _LEN, (v)) + iwl_set_bits(&(s).IWL_ ## sym ## _SYM, IWL_ ## sym ## _POS, \ + IWL_ ## sym ## _LEN, (v)) -#define _IWL_GET_BITS(s, v, o, l) \ - iwl_get_bits(s.v, o, l) +#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_get_bits((s).IWL_ ## sym ## _SYM, IWL_ ## sym ## _POS, \ IWL_ ## sym ## _LEN) -/* - * make C=2 CF=-Wall will complain if you use ARRAY_SIZE on global data - */ -#define GLOBAL_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) - -/* Debug and printf string expansion helpers for printing bitfields */ -#define BIT_FMT8 "%c%c%c%c-%c%c%c%c" -#define BIT_FMT16 BIT_FMT8 ":" BIT_FMT8 -#define BIT_FMT32 BIT_FMT16 " " BIT_FMT16 - -#define BITC(x, y) (((x>>y) & 1) ? '1' : '0') -#define BIT_ARG8(x) \ -BITC(x, 7), BITC(x, 6), BITC(x, 5), BITC(x, 4), \ -BITC(x, 3), BITC(x, 2), BITC(x, 1), BITC(x, 0) - -#define BIT_ARG16(x) \ -BITC(x, 15), BITC(x, 14), BITC(x, 13), BITC(x, 12), \ -BITC(x, 11), BITC(x, 10), BITC(x, 9), BITC(x, 8), \ -BIT_ARG8(x) - -#define BIT_ARG32(x) \ -BITC(x, 31), BITC(x, 30), BITC(x, 29), BITC(x, 28), \ -BITC(x, 27), BITC(x, 26), BITC(x, 25), BITC(x, 24), \ -BITC(x, 23), BITC(x, 22), BITC(x, 21), BITC(x, 20), \ -BITC(x, 19), BITC(x, 18), BITC(x, 17), BITC(x, 16), \ -BIT_ARG16(x) #define KELVIN_TO_CELSIUS(x) ((x)-273) #define CELSIUS_TO_KELVIN(x) ((x)+273) @@ -160,138 +145,102 @@ static inline struct ieee80211_conf *ieee80211_get_hw_conf( return &hw->conf; } -static inline 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; -} - -#define WLAN_FC_GET_TYPE(fc) (((fc) & IEEE80211_FCTL_FTYPE)) -#define WLAN_FC_GET_STYPE(fc) (((fc) & IEEE80211_FCTL_STYPE)) -#define WLAN_GET_SEQ_FRAG(seq) ((seq) & 0x000f) -#define WLAN_GET_SEQ_SEQ(seq) ((seq) >> 4) - #define QOS_CONTROL_LEN 2 -static inline __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 (u16 *) ((u8 *) hdr + hdr_len - QOS_CONTROL_LEN); - return NULL; -} - #define IEEE80211_STYPE_BACK_REQ 0x0080 #define IEEE80211_STYPE_BACK 0x0090 -#define ieee80211_is_back_request(fc) \ - ((WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_CTL) && \ - (WLAN_FC_GET_STYPE(fc) == IEEE80211_STYPE_BACK_REQ)) -#define ieee80211_is_probe_response(fc) \ - ((WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_MGMT) && \ - ( WLAN_FC_GET_STYPE(fc) == IEEE80211_STYPE_PROBE_RESP )) - -#define ieee80211_is_probe_request(fc) \ - ((WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_MGMT) && \ - ( WLAN_FC_GET_STYPE(fc) == IEEE80211_STYPE_PROBE_REQ )) +static inline int ieee80211_is_management(u16 fc) +{ + return (fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT; +} -#define ieee80211_is_beacon(fc) \ - ((WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_MGMT) && \ - ( WLAN_FC_GET_STYPE(fc) == IEEE80211_STYPE_BEACON )) +static inline int ieee80211_is_control(u16 fc) +{ + return (fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL; +} -#define ieee80211_is_atim(fc) \ - ((WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_MGMT) && \ - ( WLAN_FC_GET_STYPE(fc) == IEEE80211_STYPE_ATIM )) +static inline int ieee80211_is_data(u16 fc) +{ + return (fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA; +} -#define ieee80211_is_management(fc) \ - (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_MGMT) +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); +} -#define ieee80211_is_control(fc) \ - (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_CTL) +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); +} -#define ieee80211_is_data(fc) \ - (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA) +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); +} -#define ieee80211_is_assoc_request(fc) \ - ((WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_MGMT) && \ - (WLAN_FC_GET_STYPE(fc) == IEEE80211_STYPE_ASSOC_REQ)) +static inline int ieee80211_is_beacon(u16 fc) +{ + return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && + ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON); +} -#define ieee80211_is_assoc_response(fc) \ - ((WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_MGMT) && \ - (WLAN_FC_GET_STYPE(fc) == IEEE80211_STYPE_ASSOC_RESP)) +static inline int ieee80211_is_atim(u16 fc) +{ + return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && + ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ATIM); +} -#define ieee80211_is_auth(fc) \ - ((WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_MGMT) && \ - (WLAN_FC_GET_STYPE(fc) == IEEE80211_STYPE_ASSOC_REQ)) +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); +} -#define ieee80211_is_deauth(fc) \ - ((WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_MGMT) && \ - (WLAN_FC_GET_STYPE(fc) == 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); +} -#define ieee80211_is_disassoc(fc) \ - ((WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_MGMT) && \ - (WLAN_FC_GET_STYPE(fc) == IEEE80211_STYPE_ASSOC_REQ)) +static inline int ieee80211_is_auth(u16 fc) +{ + return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && + ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ); +} -#define ieee80211_is_reassoc_request(fc) \ - ((WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_MGMT) && \ - (WLAN_FC_GET_STYPE(fc) == IEEE80211_STYPE_REASSOC_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); +} -#define ieee80211_is_reassoc_response(fc) \ - ((WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_MGMT) && \ - (WLAN_FC_GET_STYPE(fc) == IEEE80211_STYPE_REASSOC_RESP)) +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 iwl_is_empty_essid(const char *essid, int essid_len) +static inline int ieee80211_is_reassoc_request(u16 fc) { - /* 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; + return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && + ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_REASSOC_REQ); } -static inline int iwl_check_bits(unsigned long field, unsigned long mask) +static inline int ieee80211_is_reassoc_response(u16 fc) { - return ((field & mask) == mask) ? 1 : 0; + return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && + ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_REASSOC_RESP); } -static inline const char *iwl_escape_essid(const char *essid, u8 essid_len) +static inline int iwl_check_bits(unsigned long field, unsigned long mask) { - static char escaped[IW_ESSID_MAX_SIZE * 2 + 1]; - const char *s = essid; - char *d = escaped; - - if (iwl_is_empty_essid(essid, essid_len)) { - memcpy(escaped, "<hidden>", sizeof("<hidden>")); - return escaped; - } - - essid_len = min(essid_len, (u8) IW_ESSID_MAX_SIZE); - while (essid_len--) { - if (*s == '\0') { - *d++ = '\\'; - *d++ = '0'; - s++; - } else { - *d++ = *s++; - } - } - *d = '\0'; - return escaped; + return ((field & mask) == mask) ? 1 : 0; } static inline unsigned long elapsed_jiffies(unsigned long start, @@ -303,62 +252,4 @@ static inline unsigned long elapsed_jiffies(unsigned long start, return end + (MAX_JIFFY_OFFSET - start); } -#include <linux/ctype.h> - -static inline int snprint_line(char *buf, size_t count, - const u8 * data, u32 len, u32 ofs) -{ - int out, i, j, l; - char c; - - out = snprintf(buf, count, "%08X", ofs); - - for (l = 0, i = 0; i < 2; i++) { - out += snprintf(buf + out, count - out, " "); - for (j = 0; j < 8 && l < len; j++, l++) - out += - snprintf(buf + out, count - out, "%02X ", - data[(i * 8 + j)]); - for (; j < 8; j++) - out += snprintf(buf + out, count - out, " "); - } - out += snprintf(buf + out, count - out, " "); - for (l = 0, i = 0; i < 2; i++) { - out += snprintf(buf + out, count - out, " "); - for (j = 0; j < 8 && l < len; j++, l++) { - c = data[(i * 8 + j)]; - if (!isascii(c) || !isprint(c)) - c = '.'; - - out += snprintf(buf + out, count - out, "%c", c); - } - - for (; j < 8; j++) - out += snprintf(buf + out, count - out, " "); - } - - return out; -} - -#ifdef CONFIG_IWLWIFI_DEBUG -static inline void printk_buf(int level, const void *p, u32 len) -{ - const u8 *data = p; - char line[81]; - u32 ofs = 0; - if (!(iwl_debug_level & level)) - return; - - while (len) { - snprint_line(line, sizeof(line), &data[ofs], - min(len, 16U), ofs); - printk(KERN_DEBUG "%s\n", line); - ofs += 16; - len -= min(len, 16U); - } -} -#else -#define printk_buf(level, p, len) do {} while (0) -#endif - #endif /* __iwl_helpers_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-hw.h b/drivers/net/wireless/iwlwifi/iwl-hw.h index e0facac..1aa6fcd 100644 --- a/drivers/net/wireless/iwlwifi/iwl-hw.h +++ b/drivers/net/wireless/iwlwifi/iwl-hw.h @@ -64,18 +64,19 @@ #define __iwlwifi_hw_h__ /* - * This file defines hardware / uCode API constants, enums, - * and inline functions. + * This file defines hardware constants common to 3945 and 4965. * - * For common usage code where the implementation can be the same - * with the exception of a different value going into a constant - * those constants are defined in this file. + * 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. * - * NOTE: DO NOT PUT IMPLEMENTATION SPECIFIC DECLRATIONS HERE + * uCode API constants are defined in iwl-commands.h. * - * The iwl-*hw.h (and files they include) files should remain OS driver - * implementation independent, declaring only the hardware/uCode API + * 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 */ @@ -83,6 +84,7 @@ #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 @@ -94,12 +96,9 @@ #define IWL_MAX_RATES (IWL_CCK_RATES+IWL_OFDM_RATES+IWL_HT_RATES) -/* - * Time constants - */ +/* Time constants */ #define SHORT_SLOT_TIME 9 #define LONG_SLOT_TIME 20 -#define OFDM_SYMBOL_TIME 4 /* RSSI to dBm */ #if IWL == 3945 @@ -111,658 +110,13 @@ #include "iwl-eeprom.h" #include "iwl-commands.h" -union tsf { - u8 byte[8]; - __le16 word[4]; - __le32 dw[2]; -}; - -/* - * Alive Command & Response - */ - -#define UCODE_VALID_OK (0x1) -#define INITIALIZE_SUBTYPE (9) - -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)); - -/* - * Error Command & Response - */ - -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)); - #define PCI_LINK_CTRL 0x0F0 - -/* - * 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 */ -enum { - /* band & modulation selection */ - RXON_FLG_BAND_24G_MSK = (1 << 0), - RXON_FLG_CCK_MSK = (1 << 1), - /* auto detection enable */ - RXON_FLG_AUTO_DETECT_MSK = (1 << 2), - /* TGg protection when tx */ - RXON_FLG_TGG_PROTECT_MSK = (1 << 3), - /* cck short slot & preamble */ - RXON_FLG_SHORT_SLOT_MSK = (1 << 4), - RXON_FLG_SHORT_PREAMBLE_MSK = (1 << 5), - /* antenna selection */ - RXON_FLG_DIS_DIV_MSK = (1 << 7), - RXON_FLG_ANT_SEL_MSK = 0x0f00, - RXON_FLG_ANT_A_MSK = (1 << 8), - RXON_FLG_ANT_B_MSK = (1 << 9), - /* radar detection enable */ - RXON_FLG_RADAR_DETECT_MSK = (1 << 12), - RXON_FLG_TGJ_NARROW_BAND_MSK = (1 << 13), - /* rx response to host with 8-byte TSF - * (according to ON_AIR deassertion) */ - RXON_FLG_TSF2HOST_MSK = (1 << 15) -}; - -/* rx_config filter flags */ -enum { - /* accept all data frames */ - RXON_FILTER_PROMISC_MSK = (1 << 0), - /* pass control & management to host */ - RXON_FILTER_CTL2HOST_MSK = (1 << 1), - /* accept multi-cast */ - RXON_FILTER_ACCEPT_GRP_MSK = (1 << 2), - /* don't decrypt uni-cast frames */ - RXON_FILTER_DIS_DECRYPT_MSK = (1 << 3), - /* don't decrypt multi-cast frames */ - RXON_FILTER_DIS_GRP_DECRYPT_MSK = (1 << 4), - /* STA is associated */ - RXON_FILTER_ASSOC_MSK = (1 << 5), - /* transfer to host non bssid beacons in associated state */ - RXON_FILTER_BCON_AWARE_MSK = (1 << 6) -}; - -/* - * RXON-Timings Command & 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)); - -/* - * beacon QOS parameters Command & Response - */ -struct iwl_ac_qos { - __le16 cw_min; - __le16 cw_max; - u8 aifsn; - u8 reserved1; - __le16 edca_txop; -} __attribute__ ((packed)); - -/* QoS flags defines */ -#define MH_QOS_TXOP_TYPE_MSK 0x10 -#define QOS_PARAM_FLG_UPDATE_EDCA_MSK 0x1 -#define QOS_PARAM_FLG_TGN_MSK 0x2 -#define QOS_PARAM_FLG_TXOP_TYPE_MSK MH_QOS_TXOP_TYPE_MSK - -/* - * TXFIFO Queue number defines - */ -/* number of Access categories (AC) (EDCA), queues 0..3 */ -#define AC_NUM 4 -/* total number of queues */ -#define QUEUE_NUM 7 -/* command queue number */ - -struct iwl_qosparam_cmd { - __le32 qos_flags; - struct iwl_ac_qos ac[AC_NUM]; -} __attribute__ ((packed)); - -/* - * 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 - -#define STA_CONTROL_MODIFY_MSK 0x01 - -/* key flags */ -enum { - STA_KEY_FLG_ENCRYPT_MSK = 0x7, - STA_KEY_FLG_NO_ENC = 0x0, - STA_KEY_FLG_WEP = 0x1, - STA_KEY_FLG_CCMP = 0x2, - STA_KEY_FLG_TKIP = 0x3, - - STA_KEY_FLG_KEYID_POS = 8, - STA_KEY_FLG_INVALID = 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 - -/* - * WEP/CKIP group key command - */ -struct iwl_key { - u8 index; - u8 reserved[3]; - __le32 size; - u8 key[16]; -} __attribute__ ((packed)); - -struct iwl_key_cmd { - u8 count; - u8 decrypt_type; - u8 reserved[2]; - struct iwl_key key[4]; -} __attribute__ ((packed)); - -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)); - -enum { - RX_RES_STATUS_NO_CRC32_ERROR = (1 << 0), - RX_RES_STATUS_NO_RXE_OVERFLOW = (1 << 1), -}; - -enum { - RX_RES_PHY_FLAGS_BAND_24_MSK = (1 << 0), - RX_RES_PHY_FLAGS_MOD_CCK_MSK = (1 << 1), - RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK = (1 << 2), - RX_RES_PHY_FLAGS_NARROW_BAND_MSK = (1 << 3), - RX_RES_PHY_FLAGS_ANTENNA_MSK = 0xf0, - - RX_RES_STATUS_SEC_TYPE_MSK = (0x7 << 8), - RX_RES_STATUS_SEC_TYPE_NONE = (STA_KEY_FLG_NO_ENC << 8), - RX_RES_STATUS_SEC_TYPE_WEP = (STA_KEY_FLG_WEP << 8), - RX_RES_STATUS_SEC_TYPE_TKIP = (STA_KEY_FLG_TKIP << 8), - RX_RES_STATUS_SEC_TYPE_CCMP = (STA_KEY_FLG_CCMP << 8), - - RX_RES_STATUS_DECRYPT_TYPE_MSK = (0x3 << 11), - RX_RES_STATUS_NOT_DECRYPT = (0x0 << 11), - RX_RES_STATUS_DECRYPT_OK = (0x3 << 11), - RX_RES_STATUS_BAD_ICV_MIC = (0x1 << 11), - RX_RES_STATUS_BAD_KEY_TTAK = (0x2 << 11), -}; - -struct iwl_rx_frame_end { - __le32 status; - __le64 timestamp; - __le32 beacon_timestamp; -} __attribute__ ((packed)); - -/* 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)); - -/* - * Tx Power Table Command - */ -struct iwl_power_per_rate { - u8 rate; /* plcp */ - struct iwl_tx_power tpc; - u8 reserved; -} __attribute__ ((packed)); - -struct iwl_txpowertable_cmd { - u8 band; - u8 reserved; - __le16 channel; - struct iwl_power_per_rate power[IWL_MAX_RATES]; -} __attribute__ ((packed)); - -/* - * Scan Request Commands , Responses & Notifications - */ - -/* Can abort will notify by complete notification with abort status. */ -#define CAN_ABORT_STATUS 0x1 - -struct iwl_scanreq_notification { - __le32 status; -} __attribute__ ((packed)); - -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 */ -struct iwl_scanresults_notification { - u8 channel; - u8 band; - u8 reserved[2]; - __le32 tsf_low; - __le32 tsf_high; - __le32 statistics[NUMBER_OF_STATISTICS]; -} __attribute__ ((packed)); - -struct iwl_scancomplete_notification { - u8 scanned_channels; - u8 status; - u8 reserved; - u8 last_channel; - __le32 tsf_low; - __le32 tsf_high; -} __attribute__ ((packed)); - -/* complete notification statuses */ -#define ABORT_STATUS 0x2 - -/* - * LEDs Command & Response - */ -struct iwl_led_cmd { - __le32 interval; - u8 id; - u8 off; - u8 on; - u8 reserved; -} __attribute__ ((packed)); - -/* - * card_state Command and Notification - */ - -#define CARD_STATE_CMD_DISABLE 0x00 -#define CARD_STATE_CMD_ENABLE 0x01 - -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 - -/* - * Tx Beacon Command & Response - */ -struct iwl_beacon_notif { - struct iwl_tx_resp beacon_notify_hdr; - __le32 low_tsf; - __le32 high_tsf; - __le32 ibss_mgr_status; -} __attribute__ ((packed)); - -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)); - -/* - * 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)); - -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)); - -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), -}; - -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)); - -struct iwl_csa_notification { - __le16 band; - __le16 channel; - __le32 status; /* 0 - OK, 1 - fail */ -} __attribute__ ((packed)); - -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)); - -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, -}; - -struct iwl_bt_cmd { - u8 flags; - u8 lead_time; - u8 max_kill; - u8 reserved; - __le32 kill_ack_mask; - __le32 kill_cts_mask; -} __attribute__ ((packed)); - -struct rx_phy_statistics { - __le32 ina_cnt; /* number of INA signal assertions (enter RX) */ - __le32 fina_cnt; /* number of FINA signal assertions - * (false_alarm = INA - FINA) */ - __le32 plcp_err; /* number of bad PLCP header detections - * (PLCP_good = FINA - PLCP_bad) */ - __le32 crc32_err; /* number of CRC32 error detections */ - __le32 overrun_err; /* number of Overrun detections (this is due - * to RXE sync overrun) */ - __le32 early_overrun_err; /* number of times RX is aborted at the - * start because rxfifo is full behind - * threshold */ - __le32 crc32_good; /* number of frames with good CRC */ - __le32 false_alarm_cnt; /* number of times false alarm was - * detected (i.e. INA w/o FINA) */ - __le32 fina_sync_err_cnt; /* number of times sync problem between - * HW & SW FINA counter was found */ - __le32 sfd_timeout; /* number of times got SFD timeout - * (i.e. got FINA w/o rx_frame) */ - __le32 fina_timeout; /* number of times got FINA timeout (i.e. got - * INA w/o FINA, w/o false alarm) */ - __le32 unresponded_rts; /* un-responded RTS, due to NAV not zero */ - __le32 rxe_frame_limit_overrun; /* RXE got frame limit overrun */ - __le32 sent_ack_cnt; /* ACK TX count */ - __le32 sent_cts_cnt; /* CTS TX count */ -} __attribute__ ((packed)); - -struct rx_non_phy_statistics { - __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 */ -} __attribute__ ((packed)); - -struct rx_statistics { - struct rx_phy_statistics ofdm; - struct rx_phy_statistics cck; - struct rx_non_phy_statistics general; -} __attribute__ ((packed)); - -struct tx_non_phy_statistics { - __le32 preamble_cnt; /* number of times preamble was asserted */ - __le32 rx_detected_cnt; /* number of times TX was delayed to RX - * detected */ - __le32 bt_prio_defer_cnt;/* number of times TX was deferred due to - * BT priority */ - __le32 bt_prio_kill_cnt;/* number of times TX was killed due to BT - * priority */ - __le32 few_bytes_cnt; /* number of times TX was delayed due to not - * enough bytes in TXFIFO */ - __le32 cts_timeout; /* timeout when waiting for CTS */ - __le32 ack_timeout; /* timeout when waiting for ACK */ - __le32 expected_ack_cnt;/* number of data frames that need ack or - * rts that need cts */ - __le32 actual_ack_cnt; /* number of expected ack or cts that were - * actually received */ -} __attribute__ ((packed)); - -struct tx_statistics { - struct tx_non_phy_statistics general; -} __attribute__ ((packed)); - -struct debug_statistics { - __le32 cont_burst_chk_cnt;/* number of times continuation or - * fragmentation or bursting was checked */ - __le32 cont_burst_cnt; /* number of times continuation, - * fragmentation, or bursting was successful */ - __le32 reserved[4]; -} __attribute__ ((packed)); - -#define IWL_TEMP_CONVERT 260 - -struct general_statistics { - __le32 temperature; - struct debug_statistics debug; - __le32 usec_sleep; /* < usecs NIC was asleep. Running counter. */ - __le32 slots_out; /* < slots NIC was out of serving channel */ - __le32 slots_idle; /* < slots NIC was idle */ -} __attribute__ ((packed)); - -struct statistics { - __le32 flags; - struct rx_statistics rx_statistics; - struct tx_statistics tx_statistics; - struct general_statistics general_statistics; -} __attribute__ ((packed)); - -/* if ucode missed CONSECUTIVE_MISSED_BCONS_TH beacons in a row, - * then this notification will be sent. */ -#define CONSECUTIVE_MISSED_BCONS_TH 20 - -/* register and values */ -/* base */ -#define CSR_BASE (0x0) -#define HBUS_BASE (0x400) -#define FH_BASE (0x800) - -#define HBUS_TARG_MBX_C (HBUS_BASE+0x030) +#define PCI_POWER_SOURCE 0x0C8 +#define PCI_REG_WUM8 0x0E8 +#define PCI_CFG_PMC_PME_FROM_D3COLD_SUPPORT (0x80000000) /*=== CSR (control and status registers) ===*/ +#define CSR_BASE (0x000) #define CSR_SW_VER (CSR_BASE+0x000) #define CSR_HW_IF_CONFIG_REG (CSR_BASE+0x000) /* hardware interface config */ @@ -773,55 +127,129 @@ struct statistics { #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_HW_REV (CSR_BASE+0x028) #define CSR_EEPROM_REG (CSR_BASE+0x02c) #define CSR_EEPROM_GP (CSR_BASE+0x030) -#define CSR_EEPROM_GP_VALID_MSK 0x00000007 -#define CSR_EEPROM_GP_BAD_SIGNATURE 0x00000000 -#if IWL == 3945 -#define CSR_EEPROM_GP_OWNER 0x00000180 -#endif +#define CSR_GP_UCODE (CSR_BASE+0x044) #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_LED_REG (CSR_BASE+0x094) +#define CSR_DRAM_INT_TBL_CTL (CSR_BASE+0x0A0) #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) */ -#define BSM_BASE (CSR_BASE + 0x3400) +/* 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 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 */ +/* interrupt flags in INTA, set by uCode or hardware (e.g. dma), + * acknowledged (reset) by host writing "1" to flagged bits. */ +#define CSR_INT_BIT_FH_RX (1<<31) /* Rx DMA, cmd responses, FH_INT[17:16] */ +#define CSR_INT_BIT_HW_ERR (1<<29) /* DMA hardware error FH_INT[31] */ +#define CSR_INT_BIT_DNLD (1<<28) /* uCode Download */ +#define CSR_INT_BIT_FH_TX (1<<27) /* Tx DMA FH_INT[1:0] */ +#define CSR_INT_BIT_MAC_CLK_ACTV (1<<26) /* NIC controller's clock toggled on/off */ +#define CSR_INT_BIT_SW_ERR (1<<25) /* uCode error */ +#define CSR_INT_BIT_RF_KILL (1<<7) /* HW RFKILL switch GP_CNTRL[27] toggled */ +#define CSR_INT_BIT_CT_KILL (1<<6) /* Critical temp (chip too hot) rfkill */ +#define CSR_INT_BIT_SW_RX (1<<3) /* Rx, command responses, 3945 */ +#define CSR_INT_BIT_WAKEUP (1<<1) /* NIC controller waking up (pwr mgmt) */ +#define CSR_INT_BIT_ALIVE (1<<0) /* uCode interrupts once it initializes */ + +#define CSR_INI_SET_MASK (CSR_INT_BIT_FH_RX | \ + CSR_INT_BIT_HW_ERR | \ + CSR_INT_BIT_FH_TX | \ + CSR_INT_BIT_SW_ERR | \ + CSR_INT_BIT_RF_KILL | \ + CSR_INT_BIT_SW_RX | \ + CSR_INT_BIT_WAKEUP | \ + CSR_INT_BIT_ALIVE) -/* pointers and size regs for bootstrap load and data SRAM save */ -#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) +/* interrupt flags in FH (flow handler) (PCI busmaster DMA) */ +#define CSR_FH_INT_BIT_ERR (1<<31) /* Error */ +#define CSR_FH_INT_BIT_HI_PRIOR (1<<30) /* High priority Rx, bypass coalescing */ +#define CSR_FH_INT_BIT_RX_CHNL2 (1<<18) /* Rx channel 2 (3945 only) */ +#define CSR_FH_INT_BIT_RX_CHNL1 (1<<17) /* Rx channel 1 */ +#define CSR_FH_INT_BIT_RX_CHNL0 (1<<16) /* Rx channel 0 */ +#define CSR_FH_INT_BIT_TX_CHNL6 (1<<6) /* Tx channel 6 (3945 only) */ +#define CSR_FH_INT_BIT_TX_CHNL1 (1<<1) /* Tx channel 1 */ +#define CSR_FH_INT_BIT_TX_CHNL0 (1<<0) /* Tx channel 0 */ -/* BSM special memory, stays powered during power-save sleeps */ -#define BSM_SRAM_LOWER_BOUND (CSR_BASE + 0x3800) -#define BSM_SRAM_SIZE (1024) +#define CSR_FH_INT_RX_MASK (CSR_FH_INT_BIT_HI_PRIOR | \ + CSR_FH_INT_BIT_RX_CHNL2 | \ + CSR_FH_INT_BIT_RX_CHNL1 | \ + CSR_FH_INT_BIT_RX_CHNL0) -/* DBG MON */ +#define CSR_FH_INT_TX_MASK (CSR_FH_INT_BIT_TX_CHNL6 | \ + CSR_FH_INT_BIT_TX_CHNL1 | \ + CSR_FH_INT_BIT_TX_CHNL0 ) -/* SCD */ -#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) +/* 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) + + +/* EEPROM REG */ +#define CSR_EEPROM_REG_READ_VALID_MSK (0x00000001) +#define CSR_EEPROM_REG_BIT_CMD (0x00000002) + +/* EEPROM GP */ +#define CSR_EEPROM_GP_VALID_MSK (0x00000006) +#define CSR_EEPROM_GP_BAD_SIGNATURE (0x00000000) +#define CSR_EEPROM_GP_IF_OWNER_MSK (0x00000180) + +/* UCODE DRV GP */ +#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) + +/* GPIO */ +#define CSR_GPIO_IN_BIT_AUX_POWER (0x00000200) +#define CSR_GPIO_IN_VAL_VAUX_PWR_SRC (0x00000000) +#define CSR_GPIO_IN_VAL_VMAIN_PWR_SRC CSR_GPIO_IN_BIT_AUX_POWER + +/* GI Chicken Bits */ +#define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX (0x00800000) +#define CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER (0x20000000) + +/* CSR_ANA_PLL_CFG */ +#define CSR_ANA_PLL_CFG_SH (0x00880300) + +#define CSR_LED_REG_TRUN_ON (0x00000078) +#define CSR_LED_REG_TRUN_OFF (0x00000038) +#define CSR_LED_BSM_CTRL_MSK (0xFFFFFFDF) + +/* DRAM_INT_TBL_CTRL */ +#define CSR_DRAM_INT_TBL_CTRL_EN (1<<31) +#define CSR_DRAM_INT_TBL_CTRL_WRAP_CHK (1<<27) -/*=== HBUS (Host-side bus) ===*/ +/*=== HBUS (Host-side Bus) ===*/ +#define HBUS_BASE (0x400) #define HBUS_TARG_MEM_RADDR (HBUS_BASE+0x00c) #define HBUS_TARG_MEM_WADDR (HBUS_BASE+0x010) @@ -832,7 +260,23 @@ struct statistics { #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 HBUS_TARG_MBX_C (HBUS_BASE+0x030) + + +/* 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) + +/*=== FH (data Flow Handler) ===*/ +#define FH_BASE (0x800) #define FH_CBCC_TABLE (FH_BASE+0x140) #define FH_TFDB_TABLE (FH_BASE+0x180) @@ -879,125 +323,9 @@ struct statistics { /* 18 - reserved */ /* card static random access memory (SRAM) for processor data and instructs */ -#define RTC_INST_LOWER_BOUND (0x00000) -#define ALM_RTC_INST_UPPER_BOUND (0x14000) - -#define RTC_DATA_LOWER_BOUND (0x800000) -#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 RTC_INST_LOWER_BOUND (0x000000) +#define RTC_DATA_LOWER_BOUND (0x800000) -#define VALID_RTC_DATA_ADDR(addr) \ - ( ((addr) >= RTC_DATA_LOWER_BOUND) && ((addr) < ALM_RTC_DATA_UPPER_BOUND) ) - - -/* 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_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) - -/* BSM (bootstrap state machine) */ -#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*/ /* DBM */ @@ -1075,8 +403,6 @@ struct statistics { #define IWL_MAX_CMD_SIZE 1024 -/* LEDs mode */ - #define IWL_DEFAULT_TX_RETRY 15 #define IWL_MAX_TX_RETRY 16 @@ -1086,35 +412,9 @@ struct statistics { #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 @@ -1124,28 +424,28 @@ struct statistics { #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_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_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_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_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_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 @@ -1210,17 +510,7 @@ struct statistics { #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%4))%4) - -#define AC_BE_TID_MASK 0x9 /* TID 0 and 3 */ -#define AC_BK_TID_MASK 0x6 /* TID 1 and 2 */ +#define U32_PAD(n) ((4-(n))&0x3) /* * Generic queue structure @@ -1232,7 +522,7 @@ struct statistics { #define TFD_CTL_PAD_SET(n) (n<<28) #define TFD_CTL_PAD_GET(ctl) (ctl>>28) -#define TFD_TX_CMD_SLOTS 64 +#define TFD_TX_CMD_SLOTS 256 #define TFD_CMD_SLOTS 32 #define TFD_MAX_PAYLOAD_SIZE (sizeof(struct iwl_cmd) - \ @@ -1242,305 +532,6 @@ struct statistics { * RX related structures and functions */ #define RX_FREE_BUFFERS 64 -#if IWL == 4965 -#define RX_LOW_WATERMARK 25 -#else #define RX_LOW_WATERMARK 8 -#endif - -#define SUP_RATE_11A_MAX_NUM_CHANNELS 8 -#define SUP_RATE_11B_MAX_NUM_CHANNELS 4 -#define SUP_RATE_11G_MAX_NUM_CHANNELS 12 - -#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)); - -/* 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)); - -struct iwl_notif_statistics { - __le32 flag; - struct statistics_rx rx; - struct statistics_tx tx; - struct statistics_general general; -} __attribute__ ((packed)); - -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)) - -struct iwl_multicast_addr { - u8 num_of_multicast_addresses; - u8 reserved[3]; - u8 mac1[6]; - u8 mac2[6]; - u8 mac3[6]; - u8 mac4[6]; -} __attribute__ ((packed)); - -struct iwl_tgi_tx_key { - u8 key_id; - u8 security_type; - u8 station_index; - u8 flags; - u8 key[16]; - __le32 tx_counter[2]; -} __attribute__ ((packed)); - -struct iwl_associate { - u8 channel; - u8 auth; /* & 0xf0 = auth_type, & 0xf0 = auth_key*/ - u8 assoc_type; - u8 reserved; - __le16 policy_support; - u8 preamble_length; - u8 ieee_mode; - u8 bssid[ETH_ALEN]; - __le32 assoc_tsf_msw; - __le32 assoc_tsf_lsw; - __le16 capability; - __le16 listen_interval; - __le16 beacon_interval; - u8 dest[ETH_ALEN]; - __le16 atim_window; - u8 smr; - u8 reserved1; - __le16 reserved2; - __le16 assoc_id; - u8 erp_value; -} __attribute__ ((packed)); - -#define IWL_SUPPORTED_RATES_IE_LEN 8 - -struct iwl_supported_rates { - u8 ieee_mode; - u8 num_rates; - u8 purpose; - u8 reserved; - u8 supported_rates[IWL_MAX_RATES]; -} __attribute__ ((packed)); - -struct iwl_channel_tx_power { - u8 channel_number; - s8 tx_power; -} __attribute__ ((packed)); - -#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/iwlwifi/iwl-io.h b/drivers/net/wireless/iwlwifi/iwl-io.h index 984060d..8a8b96f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.h +++ b/drivers/net/wireless/iwlwifi/iwl-io.h @@ -60,32 +60,29 @@ * */ -#define _iwl_write32(ipw, ofs, val) writel((val), (ipw)->hw_base + (ofs)) -static inline void __iwl_write32(const char *f, u32 l, struct iwl_priv *ipw, +#define _iwl_write32(iwl, ofs, val) writel((val), (iwl)->hw_base + (ofs)) +#ifdef CONFIG_IWLWIFI_DEBUG +static inline void __iwl_write32(const char *f, u32 l, struct iwl_priv *iwl, 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); + _iwl_write32(iwl, ofs, val); } - -#ifdef CONFIG_IWLWIFI_DEBUG -#define iwl_write32(ipw, ofs, val) \ - __iwl_write32(__FILE__, __LINE__, ipw, ofs, val) +#define iwl_write32(iwl, ofs, val) \ + __iwl_write32(__FILE__, __LINE__, iwl, ofs, val) #else -#define iwl_write32(ipw, ofs, val) _iwl_write32(ipw, ofs, val) +#define iwl_write32(iwl, ofs, val) _iwl_write32(iwl, ofs, val) #endif -#define _iwl_read32(ipw, ofs) readl((ipw)->hw_base + (ofs)) -static inline u32 __iwl_read32(char *f, u32 l, struct iwl_priv *ipw, u32 ofs) +#define _iwl_read32(iwl, ofs) readl((iwl)->hw_base + (ofs)) +#ifdef CONFIG_IWLWIFI_DEBUG +static inline u32 __iwl_read32(char *f, u32 l, struct iwl_priv *iwl, u32 ofs) { IWL_DEBUG_IO("read_direct32(0x%08X) - %s %d\n", ofs, f, l); - return _iwl_read32(ipw, ofs); + return _iwl_read32(iwl, ofs); } - -#ifdef CONFIG_IWLWIFI_DEBUG -#define iwl_read32(ipw, ofs) \ - __iwl_read32(__FILE__, __LINE__, ipw, ofs) +#define iwl_read32(iwl, ofs) __iwl_read32(__FILE__, __LINE__, iwl, ofs) #else #define iwl_read32(p, o) _iwl_read32(p, o) #endif @@ -104,6 +101,7 @@ static inline int _iwl_poll_bit(struct iwl_priv *priv, u32 addr, 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) @@ -119,10 +117,8 @@ static inline int __iwl_poll_bit(const char *f, u32 l, addr, bits, mask, rc, f, l); return rc; } - -#ifdef CONFIG_IWLWIFI_DEBUG -#define iwl_poll_bit(ipw, addr, bits, mask, timeout) \ - __iwl_poll_bit(__FILE__, __LINE__, ipw, addr, bits, mask, timeout) +#define iwl_poll_bit(iwl, addr, bits, mask, timeout) \ + __iwl_poll_bit(__FILE__, __LINE__, iwl, addr, bits, mask, timeout) #else #define iwl_poll_bit(p, a, b, m, t) _iwl_poll_bit(p, a, b, m, t) #endif @@ -131,6 +127,7 @@ 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) { @@ -138,8 +135,6 @@ static inline void __iwl_set_bit(const char *f, u32 l, IWL_DEBUG_IO("set_bit(0x%08X, 0x%08X) = 0x%08X\n", reg, mask, val); _iwl_write32(priv, reg, val); } - -#ifdef CONFIG_IWLWIFI_DEBUG #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) @@ -149,6 +144,7 @@ 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) { @@ -156,8 +152,6 @@ static inline void __iwl_clear_bit(const char *f, u32 l, IWL_DEBUG_IO("clear_bit(0x%08X, 0x%08X) = 0x%08X\n", reg, mask, val); _iwl_write32(priv, reg, val); } - -#ifdef CONFIG_IWLWIFI_DEBUG #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) @@ -168,7 +162,12 @@ static inline int _iwl_grab_restricted_access(struct iwl_priv *priv) int rc; u32 gp_ctl; - if (priv->status & STATUS_RF_KILL_MASK) { +#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"); @@ -178,10 +177,9 @@ static inline int _iwl_grab_restricted_access(struct iwl_priv *priv) IWL_DEBUG_RF_KILL("Wait for complete power-down, " "gpctl = 0x%08x\n", gp_ctl); mdelay(10); - } else { + } else IWL_DEBUG_RF_KILL("power-down complete, " - "gpctl = 0x%08x\n", gp_ctl); - } + "gpctl = 0x%08x\n", gp_ctl); } /* this bit wakes up the NIC */ @@ -195,23 +193,24 @@ static inline int _iwl_grab_restricted_access(struct iwl_priv *priv) return -EIO; } - priv->status |= STATUS_RESTRICTED; - +#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 (priv->status & STATUS_RESTRICTED) - IWL_ERROR - ("Grabbing access while already held at line %d.\n", l); + 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); } -#ifdef CONFIG_IWLWIFI_DEBUG #define iwl_grab_restricted_access(priv) \ __iwl_grab_restricted_access(__FILE__, __LINE__, priv) #else @@ -219,75 +218,76 @@ static inline int __iwl_grab_restricted_access(const char *f, u32 l, _iwl_grab_restricted_access(priv) #endif -static inline void _iwl_release_restricted_access(struct iwl_priv - *priv) +static inline void _iwl_release_restricted_access(struct iwl_priv *priv) { - _iwl_clear_bit(priv, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - - priv->status &= ~STATUS_RESTRICTED; +#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 (!(priv->status & STATUS_RESTRICTED)) - IWL_ERROR - ("Release unheld restricted access at line %d.\n", l); + 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); } - -#ifdef CONFIG_IWLWIFI_DEBUG #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 (!(priv->status & STATUS_RESTRICTED)) + 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; } - -#ifdef CONFIG_IWLWIFI_DEBUG #define iwl_read_restricted(priv, reg) \ __iwl_read_restricted(__FILE__, __LINE__, priv, reg) #else -#define iwl_read_restricted(p, r) _iwl_read_restricted(p, r) +#define iwl_read_restricted _iwl_read_restricted #endif -static void inline _iwl_write_restricted(struct iwl_priv *priv, +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 (!(priv->status & STATUS_RESTRICTED)) + 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 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); @@ -308,27 +308,26 @@ static inline int _iwl_poll_restricted_bit(struct iwl_priv *priv, 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); + 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); + IWL_DEBUG_IO("poll_restricted_bit(0x%08X, 0x%08X) = 0x%08X " + "- %s %d\n", addr, mask, rc, f, l); return rc; } - -#ifdef CONFIG_IWLWIFI_DEBUG -#define iwl_poll_restricted_bit(ipw, addr, mask, timeout) \ - __iwl_poll_restricted_bit(__FILE__, __LINE__, ipw, addr, mask, timeout) +#define iwl_poll_restricted_bit(iwl, addr, mask, timeout) \ + __iwl_poll_restricted_bit(__FILE__, __LINE__, iwl, addr, mask, timeout) #else -#define iwl_poll_restricted_bit(p, a, m, t) _iwl_poll_restricted_bit(p, a, m, t) +#define iwl_poll_restricted_bit _iwl_poll_restricted_bit #endif static inline u32 _iwl_read_restricted_reg(struct iwl_priv *priv, u32 reg) @@ -336,16 +335,20 @@ 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 (!(priv->status & STATUS_RESTRICTED)) + 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) @@ -354,46 +357,56 @@ static inline void _iwl_write_restricted_reg(struct iwl_priv *priv, ((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 (!(priv->status & STATUS_RESTRICTED)) + 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 (!(priv->status & STATUS_RESTRICTED)) + 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)) -static void inline __iwl_set_bits_mask_restricted_reg(u32 line, struct iwl_priv - *priv, u32 reg, - u32 bits, u32 mask) +#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 (!(priv->status & STATUS_RESTRICTED)) + 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) @@ -415,18 +428,16 @@ static inline void iwl_write_restricted_mem(struct iwl_priv *priv, u32 addr, iwl_write_restricted(priv, HBUS_TARG_MEM_WDAT, val); } -static inline void iwl_write_restricted_mem_buffer(struct iwl_priv *priv, - u32 addr, u32 len, - u32 * values) +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_reg_buffer(struct iwl_priv *priv, - u32 reg, u32 len, - u8 * 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; @@ -456,19 +467,4 @@ static inline void iwl_write_restricted_reg_buffer(struct iwl_priv *priv, _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 u32 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/iwlwifi/iwl-priv.h b/drivers/net/wireless/iwlwifi/iwl-priv.h index 847ef5a..7da1d4d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-priv.h +++ b/drivers/net/wireless/iwlwifi/iwl-priv.h @@ -52,8 +52,8 @@ struct iwl_priv { u8 phymode; int alloc_rxb_skb; - void (*rx_handlers[REPLY_MAX])(struct iwl_priv * priv, - struct iwl_rx_mem_buffer * rxb); + void (*rx_handlers[REPLY_MAX])(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb); const struct ieee80211_hw_mode *modes; @@ -62,6 +62,8 @@ struct iwl_priv { 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 */ @@ -89,7 +91,8 @@ struct iwl_priv { u8 only_active_channel; /* spinlock */ - spinlock_t lock; + spinlock_t lock; /* protect general shared data */ + spinlock_t hcmd_lock; /* protect hcmd */ struct mutex mutex; /* basic pci-network driver stuff */ @@ -97,7 +100,6 @@ struct iwl_priv { /* pci hardware address support */ void __iomem *hw_base; - unsigned long hw_len; /* uCode images, save to reload in case of failure */ struct fw_image_desc ucode_code; /* runtime inst */ @@ -174,17 +176,16 @@ struct iwl_priv { 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 - u32 status; + unsigned long status; u32 config; - int quality; - int last_rx_rssi; - int last_rx_noise; - int last_rx_snr; + int last_rx_rssi; /* From Rx packet statisitics */ + int last_rx_noise; /* From beacon statistics */ struct iwl_power_mgr power_data; @@ -204,7 +205,7 @@ struct iwl_priv { /*station table variables */ spinlock_t sta_lock; - u8 num_stations; + int num_stations; struct iwl_station_entry stations[IWL_STATION_COUNT]; /* Indication if ieee80211_ops->open has been called */ @@ -245,6 +246,10 @@ struct iwl_priv { 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; @@ -258,6 +263,7 @@ struct iwl_priv { 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; @@ -278,10 +284,11 @@ struct iwl_priv { 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; diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h new file mode 100644 index 0000000..4ba1216 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -0,0 +1,286 @@ +/****************************************************************************** + * + * 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 General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * James P. Ketrenos <ipw2100-admin@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2007 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __iwl_prph_h__ +#define __iwl_prph_h__ + +/* + * Registers in this file are internal, not PCI bus memory mapped. + * Driver accesses these via HBUS_TARG_PRPH_* registers. + */ +#define PRPH_BASE (0x00000) +#define PRPH_END (0xFFFFF) + +/* APMG (power management) constants */ +#define APMG_BASE (PRPH_BASE + 0x3000) +#define APMG_CLK_CTRL_REG (APMG_BASE + 0x0000) +#define APMG_CLK_EN_REG (APMG_BASE + 0x0004) +#define APMG_CLK_DIS_REG (APMG_BASE + 0x0008) +#define APMG_PS_CTRL_REG (APMG_BASE + 0x000c) +#define APMG_PCIDEV_STT_REG (APMG_BASE + 0x0010) +#define APMG_RFKILL_REG (APMG_BASE + 0x0014) +#define APMG_RTC_INT_STT_REG (APMG_BASE + 0x001c) +#define APMG_RTC_INT_MSK_REG (APMG_BASE + 0x0020) + +#define APMG_CLK_VAL_DMA_CLK_RQT (0x00000200) +#define APMG_CLK_VAL_BSM_CLK_RQT (0x00000800) + +#define APMG_PS_CTRL_VAL_RESET_REQ (0x04000000) + +#define APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800) + +#define APMG_PS_CTRL_MSK_PWR_SRC (0x03000000) +#define APMG_PS_CTRL_VAL_PWR_SRC_VMAIN (0x00000000) +#define APMG_PS_CTRL_VAL_PWR_SRC_VAUX (0x01000000) + + +/** + * 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 (PRPH_BASE + 0x3400) +#define BSM_END (PRPH_BASE + 0x3800) + +#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 (PRPH_BASE + 0x3800) +#define BSM_SRAM_SIZE (1024) /* bytes */ + + +/* 3945 Tx scheduler registers */ +#define ALM_SCD_BASE (PRPH_BASE + 0x2E00) +#define ALM_SCD_MODE_REG (ALM_SCD_BASE + 0x000) +#define ALM_SCD_ARASTAT_REG (ALM_SCD_BASE + 0x004) +#define ALM_SCD_TXFACT_REG (ALM_SCD_BASE + 0x010) +#define ALM_SCD_TXF4MF_REG (ALM_SCD_BASE + 0x014) +#define ALM_SCD_TXF5MF_REG (ALM_SCD_BASE + 0x020) +#define ALM_SCD_SBYP_MODE_1_REG (ALM_SCD_BASE + 0x02C) +#define ALM_SCD_SBYP_MODE_2_REG (ALM_SCD_BASE + 0x030) + +/* + * 4965 Tx Scheduler registers. + * Details are documented in iwl-4965-hw.h + */ +#define KDR_SCD_BASE (PRPH_BASE + 0xa02c00) + +#define KDR_SCD_SRAM_BASE_ADDR (KDR_SCD_BASE + 0x0) +#define KDR_SCD_EMPTY_BITS (KDR_SCD_BASE + 0x4) +#define KDR_SCD_DRAM_BASE_ADDR (KDR_SCD_BASE + 0x10) +#define KDR_SCD_AIT (KDR_SCD_BASE + 0x18) +#define KDR_SCD_TXFACT (KDR_SCD_BASE + 0x1c) +#define KDR_SCD_QUEUE_WRPTR(x) (KDR_SCD_BASE + 0x24 + (x) * 4) +#define KDR_SCD_QUEUE_RDPTR(x) (KDR_SCD_BASE + 0x64 + (x) * 4) +#define KDR_SCD_SETQUEUENUM (KDR_SCD_BASE + 0xa4) +#define KDR_SCD_SET_TXSTAT_TXED (KDR_SCD_BASE + 0xa8) +#define KDR_SCD_SET_TXSTAT_DONE (KDR_SCD_BASE + 0xac) +#define KDR_SCD_SET_TXSTAT_NOT_SCHD (KDR_SCD_BASE + 0xb0) +#define KDR_SCD_DECREASE_CREDIT (KDR_SCD_BASE + 0xb4) +#define KDR_SCD_DECREASE_SCREDIT (KDR_SCD_BASE + 0xb8) +#define KDR_SCD_LOAD_CREDIT (KDR_SCD_BASE + 0xbc) +#define KDR_SCD_LOAD_SCREDIT (KDR_SCD_BASE + 0xc0) +#define KDR_SCD_BAR (KDR_SCD_BASE + 0xc4) +#define KDR_SCD_BAR_DW0 (KDR_SCD_BASE + 0xc8) +#define KDR_SCD_BAR_DW1 (KDR_SCD_BASE + 0xcc) +#define KDR_SCD_QUEUECHAIN_SEL (KDR_SCD_BASE + 0xd0) +#define KDR_SCD_QUERY_REQ (KDR_SCD_BASE + 0xd8) +#define KDR_SCD_QUERY_RES (KDR_SCD_BASE + 0xdc) +#define KDR_SCD_PENDING_FRAMES (KDR_SCD_BASE + 0xe0) +#define KDR_SCD_INTERRUPT_MASK (KDR_SCD_BASE + 0xe4) +#define KDR_SCD_INTERRUPT_THRESHOLD (KDR_SCD_BASE + 0xe8) +#define KDR_SCD_QUERY_MIN_FRAME_SIZE (KDR_SCD_BASE + 0x100) +#define KDR_SCD_QUEUE_STATUS_BITS(x) (KDR_SCD_BASE + 0x104 + (x) * 4) + +/* SP SCD */ +#define SHL_SCD_BASE (PRPH_BASE + 0xa02c00) + +#define SHL_SCD_AIT (SHL_SCD_BASE + 0x0c) +#define SHL_SCD_TXFACT (SHL_SCD_BASE + 0x10) +#define SHL_SCD_QUEUE_WRPTR(x) (SHL_SCD_BASE + 0x18 + (x) * 4) +#define SHL_SCD_QUEUE_RDPTR(x) (SHL_SCD_BASE + 0x68 + (x) * 4) +#define SHL_SCD_QUEUECHAIN_SEL (SHL_SCD_BASE + 0xe8) +#define SHL_SCD_AGGR_SEL (SHL_SCD_BASE + 0x248) +#define SHL_SCD_INTERRUPT_MASK (SHL_SCD_BASE + 0x108) + +#endif /* __iwl_prph_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c new file mode 100644 index 0000000..9b62b7e --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -0,0 +1,8969 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2007 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * James P. Ketrenos <ipw2100-admin@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/version.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/dma-mapping.h> +#include <linux/delay.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/wireless.h> +#include <linux/firmware.h> +#include <linux/etherdevice.h> +#include <linux/if_arp.h> + +#include <net/ieee80211_radiotap.h> +#include <net/mac80211.h> + +#include <asm/div64.h> + +#include "iwl-3945.h" +#include "iwl-helpers.h" + +#ifdef CONFIG_IWL3945_DEBUG +u32 iwl3945_debug_level; +#endif + +static int iwl3945_tx_queue_update_write_ptr(struct iwl3945_priv *priv, + struct iwl3945_tx_queue *txq); + +/****************************************************************************** + * + * module boiler plate + * + ******************************************************************************/ + +/* module parameters */ +static int iwl3945_param_disable_hw_scan; /* def: 0 = use 3945's h/w scan */ +static int iwl3945_param_debug; /* def: 0 = minimal debug log messages */ +static int iwl3945_param_disable; /* def: 0 = enable radio */ +static int iwl3945_param_antenna; /* def: 0 = both antennas (use diversity) */ +int iwl3945_param_hwcrypto; /* def: 0 = use software encryption */ +static int iwl3945_param_qos_enable = 1; /* def: 1 = use quality of service */ +int iwl3945_param_queues_num = IWL_MAX_NUM_QUEUES; /* def: 8 Tx queues */ + +/* + * module name, copyright, version, etc. + * NOTE: DRV_NAME is defined in iwlwifi.h for use by iwl-debug.h and printk + */ + +#define DRV_DESCRIPTION \ +"Intel(R) PRO/Wireless 3945ABG/BG Network Connection driver for Linux" + +#ifdef CONFIG_IWL3945_DEBUG +#define VD "d" +#else +#define VD +#endif + +#ifdef CONFIG_IWL3945_SPECTRUM_MEASUREMENT +#define VS "s" +#else +#define VS +#endif + +#define IWLWIFI_VERSION "1.2.22k" 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" + +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_VERSION(DRV_VERSION); +MODULE_AUTHOR(DRV_COPYRIGHT); +MODULE_LICENSE("GPL"); + +static __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 *iwl3945_get_hw_mode( + struct iwl3945_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 iwl3945_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 *iwl3945_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 (iwl3945_is_empty_essid(essid, essid_len)) { + memcpy(escaped, "<hidden>", sizeof("<hidden>")); + return escaped; + } + + essid_len = min(essid_len, (u8) IW_ESSID_MAX_SIZE); + while (essid_len--) { + if (*s == '\0') { + *d++ = '\\'; + *d++ = '0'; + s++; + } else + *d++ = *s++; + } + *d = '\0'; + return escaped; +} + +static void iwl3945_print_hex_dump(int level, void *p, u32 len) +{ +#ifdef CONFIG_IWL3945_DEBUG + if (!(iwl3945_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 Tx or Rx queue resides in host DRAM, and is comprised of a circular buffer + * of buffer descriptors, each of which points to one or more data buffers for + * the device to read from or fill. Driver and device exchange status of each + * queue via "read" and "write" pointers. Driver keeps minimum of 2 empty + * entries in each circular buffer, to protect against confusing empty and full + * queue states. + * + * The device reads or writes the data in the queues via the device's several + * DMA/FIFO channels. Each queue is mapped to a single DMA channel. + * + * 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 3945 operates with six queues: One receive queue, one transmit queue + * (#4) for sending commands to the device firmware, and four transmit queues + * (#0-3) for data tx via EDCA. An additional 2 HCCA queues are unused. + ***************************************************/ + +static int iwl3945_queue_space(const struct iwl3945_queue *q) +{ + int s = q->read_ptr - q->write_ptr; + + if (q->read_ptr > q->write_ptr) + 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; +} + +/** + * iwl3945_queue_inc_wrap - increment queue index, wrap back to beginning + * @index -- current index + * @n_bd -- total number of entries in queue (must be power of 2) + */ +static inline int iwl3945_queue_inc_wrap(int index, int n_bd) +{ + return ++index & (n_bd - 1); +} + +/** + * iwl3945_queue_dec_wrap - increment queue index, wrap back to end + * @index -- current index + * @n_bd -- total number of entries in queue (must be power of 2) + */ +static inline int iwl3945_queue_dec_wrap(int index, int n_bd) +{ + return --index & (n_bd - 1); +} + +static inline int x2_queue_used(const struct iwl3945_queue *q, int i) +{ + return q->write_ptr > q->read_ptr ? + (i >= q->read_ptr && i < q->write_ptr) : + !(i < q->read_ptr && i >= q->write_ptr); +} + +static inline u8 get_cmd_index(struct iwl3945_queue *q, u32 index, int is_huge) +{ + /* This is for scan command, the big buffer at end of command array */ + if (is_huge) + return q->n_window; /* must be power of 2 */ + + /* Otherwise, use normal size buffers */ + return index & (q->n_window - 1); +} + +/** + * iwl3945_queue_init - Initialize queue's high/low-water and read/write indexes + */ +static int iwl3945_queue_init(struct iwl3945_priv *priv, struct iwl3945_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 iwl3945_queue_inc_wrap + * and iwl3945_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->write_ptr = q->read_ptr = 0; + + return 0; +} + +/** + * iwl3945_tx_queue_alloc - Alloc driver data and TFD CB for one Tx/cmd queue + */ +static int iwl3945_tx_queue_alloc(struct iwl3945_priv *priv, + struct iwl3945_tx_queue *txq, u32 id) +{ + struct pci_dev *dev = priv->pci_dev; + + /* Driver private data, only for Tx (not command) queues, + * not shared with device. */ + 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 auxiliary BD " + "structures failed\n"); + goto error; + } + } else + txq->txb = NULL; + + /* Circular buffer of transmit frame descriptors (TFDs), + * shared with device */ + 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; +} + +/** + * iwl3945_tx_queue_init - Allocate and initialize one tx/cmd queue + */ +int iwl3945_tx_queue_init(struct iwl3945_priv *priv, + struct iwl3945_tx_queue *txq, int slots_num, u32 txq_id) +{ + struct pci_dev *dev = priv->pci_dev; + int len; + int rc = 0; + + /* + * Alloc buffer array for commands (Tx or other types of commands). + * For the command queue (#4), allocate command space + one big + * command for scan, since scan command is very huge; the system will + * not have two scans at the same time, so only one is needed. + * For data Tx queues (all other queues), no super-size command + * space is needed. + */ + len = sizeof(struct iwl3945_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; + + /* Alloc driver data array and TFD circular buffer */ + rc = iwl3945_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 + * iwl3945_queue_inc_wrap and iwl3945_queue_dec_wrap are broken. */ + BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1)); + + /* Initialize queue high/low-water, head/tail indexes */ + iwl3945_queue_init(priv, &txq->q, TFD_QUEUE_SIZE_MAX, slots_num, txq_id); + + /* Tell device where to find queue, enable DMA channel. */ + iwl3945_hw_tx_queue_init(priv, txq); + + return 0; +} + +/** + * iwl3945_tx_queue_free - Deallocate DMA queue. + * @txq: Transmit queue to deallocate. + * + * Empty queue by removing and destroying all BD's. + * Free all buffers. + * 0-fill, but do not free "txq" descriptor structure. + */ +void iwl3945_tx_queue_free(struct iwl3945_priv *priv, struct iwl3945_tx_queue *txq) +{ + struct iwl3945_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->write_ptr != q->read_ptr; + q->read_ptr = iwl3945_queue_inc_wrap(q->read_ptr, q->n_bd)) + iwl3945_hw_txq_free_tfd(priv, txq); + + len = sizeof(struct iwl3945_cmd) * q->n_window; + if (q->id == IWL_CMD_QUEUE_NUM) + len += IWL_MAX_SCAN_SIZE; + + /* De-alloc array of command/tx buffers */ + pci_free_consistent(dev, len, txq->cmd, txq->dma_addr_cmd); + + /* De-alloc circular buffer of TFDs */ + if (txq->q.n_bd) + pci_free_consistent(dev, sizeof(struct iwl3945_tfd_frame) * + txq->q.n_bd, txq->bd, txq->q.dma_addr); + + /* De-alloc array of per-TFD driver data */ + if (txq->txb) { + kfree(txq->txb); + txq->txb = NULL; + } + + /* 0-fill queue descriptor structure */ + memset(txq, 0, sizeof(*txq)); +} + +const u8 iwl3945_broadcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + +/*************** STATION TABLE MANAGEMENT **** + * mac80211 should be examined to determine if sta_info is duplicating + * the functionality provided here + */ + +/**************************************************************/ +#if 0 /* temporary disable till we add real remove station */ +/** + * iwl3945_remove_station - Remove driver's knowledge of station. + * + * NOTE: This does not remove station from device's station table. + */ +static u8 iwl3945_remove_station(struct iwl3945_priv *priv, const u8 *addr, 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; + else if (is_broadcast_ether_addr(addr)) + index = priv->hw_setting.bcast_sta_id; + else + for (i = IWL_STA_ID; i < priv->hw_setting.max_stations; i++) + if (priv->stations[i].used && + !compare_ether_addr(priv->stations[i].sta.sta.addr, + addr)) { + index = i; + break; + } + + if (unlikely(index == IWL_INVALID_STATION)) + goto out; + + if (priv->stations[index].used) { + priv->stations[index].used = 0; + priv->num_stations--; + } + + BUG_ON(priv->num_stations < 0); + +out: + spin_unlock_irqrestore(&priv->sta_lock, flags); + return 0; +} +#endif + +/** + * iwl3945_clear_stations_table - Clear the driver's station table + * + * NOTE: This does not clear or otherwise alter the device's station table. + */ +static void iwl3945_clear_stations_table(struct iwl3945_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); +} + +/** + * iwl3945_add_station - Add station to station tables in driver and device + */ +u8 iwl3945_add_station(struct iwl3945_priv *priv, const u8 *addr, int is_ap, u8 flags) +{ + int i; + int index = IWL_INVALID_STATION; + struct iwl3945_station_entry *station; + unsigned long flags_spin; + DECLARE_MAC_BUF(mac); + u8 rate; + + spin_lock_irqsave(&priv->sta_lock, flags_spin); + if (is_ap) + index = IWL_AP_ID; + else if (is_broadcast_ether_addr(addr)) + index = priv->hw_setting.bcast_sta_id; + else + for (i = IWL_STA_ID; i < priv->hw_setting.max_stations; i++) { + if (!compare_ether_addr(priv->stations[i].sta.sta.addr, + addr)) { + index = i; + break; + } + + if (!priv->stations[i].used && + index == IWL_INVALID_STATION) + index = i; + } + + /* These two conditions has the same outcome but keep them separate + since they have different meaning */ + if (unlikely(index == IWL_INVALID_STATION)) { + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + return index; + } + + if (priv->stations[index].used && + !compare_ether_addr(priv->stations[index].sta.sta.addr, addr)) { + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + return index; + } + + IWL_DEBUG_ASSOC("Add STA ID %d: %s\n", index, print_mac(mac, addr)); + station = &priv->stations[index]; + station->used = 1; + priv->num_stations++; + + /* Set up the REPLY_ADD_STA command to send to device */ + memset(&station->sta, 0, sizeof(struct iwl3945_addsta_cmd)); + memcpy(station->sta.sta.addr, addr, ETH_ALEN); + station->sta.mode = 0; + station->sta.sta.sta_id = index; + station->sta.station_flags = 0; + + if (priv->phymode == MODE_IEEE80211A) + rate = IWL_RATE_6M_PLCP; + else + rate = IWL_RATE_1M_PLCP; + + /* Turn on both antennas for the station... */ + station->sta.rate_n_flags = + iwl3945_hw_set_rate_n_flags(rate, RATE_MCS_ANT_AB_MSK); + station->current_rate.rate_n_flags = + le16_to_cpu(station->sta.rate_n_flags); + + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + + /* Add station to device's station table */ + iwl3945_send_add_station(priv, &station->sta, flags); + return index; + +} + +/*************** DRIVER STATUS FUNCTIONS *****/ + +static inline int iwl3945_is_ready(struct iwl3945_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 iwl3945_is_alive(struct iwl3945_priv *priv) +{ + return test_bit(STATUS_ALIVE, &priv->status); +} + +static inline int iwl3945_is_init(struct iwl3945_priv *priv) +{ + return test_bit(STATUS_INIT, &priv->status); +} + +static inline int iwl3945_is_rfkill(struct iwl3945_priv *priv) +{ + return test_bit(STATUS_RF_KILL_HW, &priv->status) || + test_bit(STATUS_RF_KILL_SW, &priv->status); +} + +static inline int iwl3945_is_ready_rf(struct iwl3945_priv *priv) +{ + + if (iwl3945_is_rfkill(priv)) + return 0; + + return iwl3945_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); + IWL_CMD(REPLY_3945_RX); + 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); + default: + return "UNKNOWN"; + + } +} + +#define HOST_COMPLETE_TIMEOUT (HZ / 2) + +/** + * iwl3945_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 iwl3945_enqueue_hcmd(struct iwl3945_priv *priv, struct iwl3945_host_cmd *cmd) +{ + struct iwl3945_tx_queue *txq = &priv->txq[IWL_CMD_QUEUE_NUM]; + struct iwl3945_queue *q = &txq->q; + struct iwl3945_tfd_frame *tfd; + u32 *control_flags; + struct iwl3945_cmd *out_cmd; + u32 idx; + u16 fix_size = (u16)(cmd->len + sizeof(out_cmd->hdr)); + dma_addr_t phys_addr; + int pad; + u16 count; + int ret; + 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 (iwl3945_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->write_ptr]; + memset(tfd, 0, sizeof(*tfd)); + + control_flags = (u32 *) tfd; + + idx = get_cmd_index(q, q->write_ptr, 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->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->write_ptr)); + 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 iwl3945_cmd, hdr); + iwl3945_hw_txq_attach_buf_to_tfd(priv, tfd, phys_addr, fix_size); + + pad = U32_PAD(cmd->len); + count = TFD_CTL_COUNT_GET(*control_flags); + *control_flags = TFD_CTL_COUNT_SET(count) | TFD_CTL_PAD_SET(pad); + + 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->write_ptr, idx, IWL_CMD_QUEUE_NUM); + + txq->need_update = 1; + + /* Increment and update queue's write index */ + q->write_ptr = iwl3945_queue_inc_wrap(q->write_ptr, q->n_bd); + ret = iwl3945_tx_queue_update_write_ptr(priv, txq); + + spin_unlock_irqrestore(&priv->hcmd_lock, flags); + return ret ? ret : idx; +} + +static int iwl3945_send_cmd_async(struct iwl3945_priv *priv, struct iwl3945_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 = iwl3945_enqueue_hcmd(priv, cmd); + if (ret < 0) { + IWL_ERROR("Error sending %s: iwl3945_enqueue_hcmd failed: %d\n", + get_cmd_string(cmd->id), ret); + return ret; + } + return 0; +} + +static int iwl3945_send_cmd_sync(struct iwl3945_priv *priv, struct iwl3945_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; + + cmd_idx = iwl3945_enqueue_hcmd(priv, cmd); + if (cmd_idx < 0) { + ret = cmd_idx; + IWL_ERROR("Error sending %s: iwl3945_enqueue_hcmd failed: %d\n", + get_cmd_string(cmd->id), ret); + goto out; + } + + 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 iwl3945_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 iwl3945_send_cmd(struct iwl3945_priv *priv, struct iwl3945_host_cmd *cmd) +{ + if (cmd->meta.flags & CMD_ASYNC) + return iwl3945_send_cmd_async(priv, cmd); + + return iwl3945_send_cmd_sync(priv, cmd); +} + +int iwl3945_send_cmd_pdu(struct iwl3945_priv *priv, u8 id, u16 len, const void *data) +{ + struct iwl3945_host_cmd cmd = { + .id = id, + .len = len, + .data = data, + }; + + return iwl3945_send_cmd_sync(priv, &cmd); +} + +static int __must_check iwl3945_send_cmd_u32(struct iwl3945_priv *priv, u8 id, u32 val) +{ + struct iwl3945_host_cmd cmd = { + .id = id, + .len = sizeof(val), + .data = &val, + }; + + return iwl3945_send_cmd_sync(priv, &cmd); +} + +int iwl3945_send_statistics_request(struct iwl3945_priv *priv) +{ + return iwl3945_send_cmd_u32(priv, REPLY_STATISTICS_CMD, 0); +} + +/** + * iwl3945_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 iwl3945_set_rxon_channel(struct iwl3945_priv *priv, u8 phymode, u16 channel) +{ + if (!iwl3945_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; +} + +/** + * iwl3945_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 iwl3945_check_rxon_cmd(struct iwl3945_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 ((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); + + if (error) + IWL_WARNING("Tuning to channel %d\n", + le16_to_cpu(rxon->channel)); + + if (error) { + IWL_ERROR("Not a valid iwl3945_rxon_assoc_cmd field values\n"); + return -1; + } + return 0; +} + +/** + * iwl3945_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed + * @priv: staging_rxon is compared to active_rxon + * + * If the RXON structure is changing enough to require a new tune, + * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that + * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required. + */ +static int iwl3945_full_rxon_required(struct iwl3945_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) || + (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 iwl3945_send_rxon_assoc(struct iwl3945_priv *priv) +{ + int rc = 0; + struct iwl3945_rx_packet *res = NULL; + struct iwl3945_rxon_assoc_cmd rxon_assoc; + struct iwl3945_host_cmd cmd = { + .id = REPLY_RXON_ASSOC, + .len = sizeof(rxon_assoc), + .meta.flags = CMD_WANT_SKB, + .data = &rxon_assoc, + }; + const struct iwl3945_rxon_cmd *rxon1 = &priv->staging_rxon; + const struct iwl3945_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) && + (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; + + rc = iwl3945_send_cmd_sync(priv, &cmd); + if (rc) + return rc; + + res = (struct iwl3945_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; +} + +/** + * iwl3945_commit_rxon - commit staging_rxon to hardware + * + * The RXON command in staging_rxon is committed 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 iwl3945_commit_rxon(struct iwl3945_priv *priv) +{ + /* cast away the const for active_rxon in this function */ + struct iwl3945_rxon_cmd *active_rxon = (void *)&priv->active_rxon; + int rc = 0; + DECLARE_MAC_BUF(mac); + + if (!iwl3945_is_alive(priv)) + return -1; + + /* always get timestamp with Rx frame */ + priv->staging_rxon.flags |= RXON_FLG_TSF2HOST_MSK; + + /* 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); + + rc = iwl3945_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 + * iwl3945_rxon_assoc_cmd which is used to reconfigure filter + * and other flags for the current radio configuration. */ + if (!iwl3945_full_rxon_required(priv)) { + rc = iwl3945_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 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 (iwl3945_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 = iwl3945_send_cmd_pdu(priv, REPLY_RXON, + sizeof(struct iwl3945_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; + } + } + + IWL_DEBUG_INFO("Sending RXON\n" + "* with%s RXON_FILTER_ASSOC_MSK\n" + "* channel = %d\n" + "* bssid = %s\n", + ((priv->staging_rxon.filter_flags & + RXON_FILTER_ASSOC_MSK) ? "" : "out"), + le16_to_cpu(priv->staging_rxon.channel), + print_mac(mac, priv->staging_rxon.bssid_addr)); + + /* Apply the new configuration */ + rc = iwl3945_send_cmd_pdu(priv, REPLY_RXON, + sizeof(struct iwl3945_rxon_cmd), &priv->staging_rxon); + if (rc) { + IWL_ERROR("Error setting new configuration (%d).\n", rc); + return rc; + } + + memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon)); + + iwl3945_clear_stations_table(priv); + + /* 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 = iwl3945_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 (iwl3945_add_station(priv, iwl3945_broadcast_addr, 0, 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 (iwl3945_is_associated(priv) && + (priv->iw_mode == IEEE80211_IF_TYPE_STA)) + if (iwl3945_add_station(priv, priv->active_rxon.bssid_addr, 1, 0) + == 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 iwl3945_send_bt_config(struct iwl3945_priv *priv) +{ + struct iwl3945_bt_cmd bt_cmd = { + .flags = 3, + .lead_time = 0xAA, + .max_kill = 1, + .kill_ack_mask = 0, + .kill_cts_mask = 0, + }; + + return iwl3945_send_cmd_pdu(priv, REPLY_BT_CONFIG, + sizeof(struct iwl3945_bt_cmd), &bt_cmd); +} + +static int iwl3945_send_scan_abort(struct iwl3945_priv *priv) +{ + int rc = 0; + struct iwl3945_rx_packet *res; + struct iwl3945_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 = iwl3945_send_cmd_sync(priv, &cmd); + if (rc) { + clear_bit(STATUS_SCAN_ABORTING, &priv->status); + return rc; + } + + res = (struct iwl3945_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 iwl3945_card_state_sync_callback(struct iwl3945_priv *priv, + struct iwl3945_cmd *cmd, + struct sk_buff *skb) +{ + return 1; +} + +/* + * CARD_STATE_CMD + * + * Use: Sets the device's 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 iwl3945_send_card_state(struct iwl3945_priv *priv, u32 flags, u8 meta_flag) +{ + struct iwl3945_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 = iwl3945_card_state_sync_callback; + + return iwl3945_send_cmd(priv, &cmd); +} + +static int iwl3945_add_sta_sync_callback(struct iwl3945_priv *priv, + struct iwl3945_cmd *cmd, struct sk_buff *skb) +{ + struct iwl3945_rx_packet *res = NULL; + + if (!skb) { + IWL_ERROR("Error: Response NULL in REPLY_ADD_STA.\n"); + return 1; + } + + res = (struct iwl3945_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 iwl3945_send_add_station(struct iwl3945_priv *priv, + struct iwl3945_addsta_cmd *sta, u8 flags) +{ + struct iwl3945_rx_packet *res = NULL; + int rc = 0; + struct iwl3945_host_cmd cmd = { + .id = REPLY_ADD_STA, + .len = sizeof(struct iwl3945_addsta_cmd), + .meta.flags = flags, + .data = sta, + }; + + if (flags & CMD_ASYNC) + cmd.meta.u.callback = iwl3945_add_sta_sync_callback; + else + cmd.meta.flags |= CMD_WANT_SKB; + + rc = iwl3945_send_cmd(priv, &cmd); + + if (rc || (flags & CMD_ASYNC)) + return rc; + + res = (struct iwl3945_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 iwl3945_update_sta_key_info(struct iwl3945_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: + 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"); + iwl3945_send_add_station(priv, &priv->stations[sta_id].sta, 0); + return 0; +} + +static int iwl3945_clear_sta_key_info(struct iwl3945_priv *priv, u8 sta_id) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->sta_lock, flags); + memset(&priv->stations[sta_id].keyinfo, 0, sizeof(struct iwl3945_hw_key)); + memset(&priv->stations[sta_id].sta.key, 0, sizeof(struct iwl3945_keyinfo)); + priv->stations[sta_id].sta.key.key_flags = STA_KEY_FLG_NO_ENC; + 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: clear ucode station key info\n"); + iwl3945_send_add_station(priv, &priv->stations[sta_id].sta, 0); + return 0; +} + +static void iwl3945_clear_free_frames(struct iwl3945_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 iwl3945_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 iwl3945_frame *iwl3945_get_free_frame(struct iwl3945_priv *priv) +{ + struct iwl3945_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 iwl3945_frame, list); +} + +static void iwl3945_free_frame(struct iwl3945_priv *priv, struct iwl3945_frame *frame) +{ + memset(frame, 0, sizeof(*frame)); + list_add(&frame->list, &priv->free_frames); +} + +unsigned int iwl3945_fill_beacon_frame(struct iwl3945_priv *priv, + struct ieee80211_hdr *hdr, + const u8 *dest, int left) +{ + + if (!iwl3945_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 iwl3945_rate_index_from_plcp(int plcp) +{ + int i = 0; + + for (i = 0; i < IWL_RATE_COUNT; i++) + if (iwl3945_rates[i].plcp == plcp) + return i; + return -1; +} + +static u8 iwl3945_rate_get_lowest_plcp(int rate_mask) +{ + u8 i; + + for (i = IWL_RATE_1M_INDEX; i != IWL_RATE_INVALID; + i = iwl3945_rates[i].next_ieee) { + if (rate_mask & (1 << i)) + return iwl3945_rates[i].plcp; + } + + return IWL_RATE_INVALID; +} + +static int iwl3945_send_beacon_cmd(struct iwl3945_priv *priv) +{ + struct iwl3945_frame *frame; + unsigned int frame_size; + int rc; + u8 rate; + + frame = iwl3945_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 = iwl3945_rate_get_lowest_plcp(priv->active_rate_basic & + 0xFF0); + if (rate == IWL_INVALID_RATE) + rate = IWL_RATE_6M_PLCP; + } else { + rate = iwl3945_rate_get_lowest_plcp(priv->active_rate_basic & 0xF); + if (rate == IWL_INVALID_RATE) + rate = IWL_RATE_1M_PLCP; + } + + frame_size = iwl3945_hw_get_beacon_cmd(priv, frame, rate); + + rc = iwl3945_send_cmd_pdu(priv, REPLY_TX_BEACON, frame_size, + &frame->u.cmd[0]); + + iwl3945_free_frame(priv, frame); + + return rc; +} + +/****************************************************************************** + * + * EEPROM related functions + * + ******************************************************************************/ + +static void get_eeprom_mac(struct iwl3945_priv *priv, u8 *mac) +{ + memcpy(mac, priv->eeprom.mac_address, 6); +} + +/** + * iwl3945_eeprom_init - read EEPROM contents + * + * Load the EEPROM contents from adapter into priv->eeprom + * + * NOTE: This routine uses the non-debug IO access functions. + */ +int iwl3945_eeprom_init(struct iwl3945_priv *priv) +{ + u16 *e = (u16 *)&priv->eeprom; + u32 gp = iwl3945_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; + } + + /* Make sure driver (instead of uCode) is allowed to read EEPROM */ + rc = iwl3945_eeprom_acquire_semaphore(priv); + if (rc < 0) { + IWL_ERROR("Failed to acquire EEPROM semaphore.\n"); + return -ENOENT; + } + + /* eeprom is an array of 16bit values */ + for (addr = 0; addr < sz; addr += sizeof(u16)) { + _iwl3945_write32(priv, CSR_EEPROM_REG, addr << 1); + _iwl3945_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 = _iwl3945_read_direct32(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); + return -ETIMEDOUT; + } + e[addr / 2] = le16_to_cpu(r >> 16); + } + + return 0; +} + +/****************************************************************************** + * + * Misc. internal state and helper functions + * + ******************************************************************************/ +#ifdef CONFIG_IWL3945_DEBUG + +/** + * iwl3945_report_frame - dump frame to syslog during debug sessions + * + * You may 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. + */ +void iwl3945_report_frame(struct iwl3945_priv *priv, + struct iwl3945_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 iwl3945_rx_frame_stats *rx_stats = IWL_RX_STATS(pkt); + struct iwl3945_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt); + struct iwl3945_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 = iwl3945_rate_index_from_plcp(rate_sym); + if (rate == -1) + rate = 0; + else + rate = iwl3945_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) + iwl3945_print_hex_dump(IWL_DL_RX, data, length); +} +#endif + +static void iwl3945_unset_hw_setting(struct iwl3945_priv *priv) +{ + if (priv->hw_setting.shared_virt) + pci_free_consistent(priv->pci_dev, + sizeof(struct iwl3945_shared), + priv->hw_setting.shared_virt, + priv->hw_setting.shared_phys); +} + +/** + * iwl3945_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 iwl3945_supported_rate_to_ie(u8 *ie, u16 supported_rate, + u16 basic_rate, int *left) +{ + u16 ret_rates = 0, bit; + int i; + u8 *cnt = ie; + u8 *rates = ie + 1; + + for (bit = 1, i = 0; i < IWL_RATE_COUNT; i++, bit <<= 1) { + if (bit & supported_rate) { + ret_rates |= bit; + rates[*cnt] = iwl3945_rates[i].ieee | + ((bit & basic_rate) ? 0x80 : 0x00); + (*cnt)++; + (*left)--; + if ((*left <= 0) || + (*cnt >= IWL_SUPPORTED_RATES_IE_LEN)) + break; + } + } + + return ret_rates; +} + +/** + * iwl3945_fill_probe_req - fill in all required fields and IE for probe request + */ +static u16 iwl3945_fill_probe_req(struct iwl3945_priv *priv, + struct ieee80211_mgmt *frame, + int left, int is_direct) +{ + int len = 0; + u8 *pos = NULL; + u16 active_rates, ret_rates, cck_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, iwl3945_broadcast_addr, ETH_ALEN); + memcpy(frame->sa, priv->mac_addr, ETH_ALEN); + memcpy(frame->bssid, iwl3945_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; + + priv->active_rate = priv->rates_mask; + active_rates = priv->active_rate; + priv->active_rate_basic = priv->rates_mask & IWL_BASIC_RATES_MASK; + + cck_rates = IWL_CCK_RATES_MASK & active_rates; + ret_rates = iwl3945_supported_rate_to_ie(pos, cck_rates, + priv->active_rate_basic, &left); + active_rates &= ~ret_rates; + + ret_rates = iwl3945_supported_rate_to_ie(pos, active_rates, + priv->active_rate_basic, &left); + active_rates &= ~ret_rates; + + len += 2 + *pos; + pos += (*pos) + 1; + if (active_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; + iwl3945_supported_rate_to_ie(pos, active_rates, + priv->active_rate_basic, &left); + if (*pos > 0) + len += 2 + *pos; + + fill_end: + return (u16)len; +} + +/* + * QoS support +*/ +#ifdef CONFIG_IWL3945_QOS +static int iwl3945_send_qos_params_command(struct iwl3945_priv *priv, + struct iwl3945_qosparam_cmd *qos) +{ + + return iwl3945_send_cmd_pdu(priv, REPLY_QOS_PARAM, + sizeof(struct iwl3945_qosparam_cmd), qos); +} + +static void iwl3945_reset_qos(struct iwl3945_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 = 1; + if (!(priv->active_rate & 0xfff0)) { + cw_min = 31; + is_legacy = 1; + } + } else if (priv->iw_mode == IEEE80211_IF_TYPE_AP) { + if (priv->qos_data.qos_enable) + priv->qos_data.qos_active = 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 iwl3945_activate_qos(struct iwl3945_priv *priv, u8 force) +{ + unsigned long flags; + + 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 || iwl3945_is_associated(priv)) { + IWL_DEBUG_QOS("send QoS cmd with Qos active %d \n", + priv->qos_data.qos_active); + + iwl3945_send_qos_params_command(priv, + &(priv->qos_data.def_qos_parm)); + } +} + +#endif /* CONFIG_IWL3945_QOS */ +/* + * Power management (not Tx power!) functions + */ +#define MSEC_TO_USEC 1024 + +#define NOSLP __constant_cpu_to_le32(0) +#define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK +#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 iwl3945_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 iwl3945_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 iwl3945_power_init_handle(struct iwl3945_priv *priv) +{ + int rc = 0, i; + struct iwl3945_power_mgr *pow_data; + int size = sizeof(struct iwl3945_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 iwl3945_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 iwl3945_update_power_cmd(struct iwl3945_priv *priv, + struct iwl3945_powertable_cmd *cmd, u32 mode) +{ + int rc = 0, i; + u8 skip; + u32 max_sleep = 0; + struct iwl3945_power_vec_entry *range; + u8 period = 0; + struct iwl3945_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 iwl3945_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 iwl3945_send_power_mode(struct iwl3945_priv *priv, u32 mode) +{ + u32 uninitialized_var(final_mode); + int rc; + struct iwl3945_powertable_cmd cmd; + + /* If on battery, set to 3, + * if plugged into AC power, set to CAM ("continuously 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; + } + + iwl3945_update_power_cmd(priv, &cmd, final_mode); + + rc = iwl3945_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 iwl3945_is_network_packet(struct iwl3945_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 + +static const char *iwl3945_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"; +} + +/** + * iwl3945_scan_cancel - Cancel any currently executing HW scan + * + * NOTE: priv->mutex is not required before calling this function + */ +static int iwl3945_scan_cancel(struct iwl3945_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; +} + +/** + * iwl3945_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 iwl3945_scan_cancel_timeout(struct iwl3945_priv *priv, unsigned long ms) +{ + unsigned long now = jiffies; + int ret; + + ret = iwl3945_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 iwl3945_sequence_reset(struct iwl3945_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; + + iwl3945_scan_cancel(priv); +} + +#define MAX_UCODE_BEACON_INTERVAL 1024 +#define INTEL_CONN_LISTEN_INTERVAL __constant_cpu_to_le16(0xA) + +static __le16 iwl3945_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 iwl3945_setup_rxon_timing(struct iwl3945_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 = + iwl3945_adjust_beacon_interval( + le16_to_cpu(priv->rxon_timing.beacon_interval)); + } + + priv->rxon_timing.atim_window = 0; + } else { + priv->rxon_timing.beacon_interval = + iwl3945_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 iwl3945_scan_initiate(struct iwl3945_priv *priv) +{ + if (priv->iw_mode == IEEE80211_IF_TYPE_AP) { + IWL_ERROR("APs don't scan.\n"); + return 0; + } + + if (!iwl3945_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 iwl3945_set_rxon_hwcrypto(struct iwl3945_priv *priv, int hw_decrypt) +{ + struct iwl3945_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 iwl3945_set_flags_for_phymode(struct iwl3945_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 iwl3945_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; + } +} + +/* + * initialize rxon structure with default values from eeprom + */ +static void iwl3945_connection_init_rx_config(struct iwl3945_priv *priv) +{ + const struct iwl3945_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 = iwl3945_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; + + iwl3945_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; +} + +static int iwl3945_set_mode(struct iwl3945_priv *priv, int mode) +{ + if (mode == IEEE80211_IF_TYPE_IBSS) { + const struct iwl3945_channel_info *ch_info; + + ch_info = iwl3945_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; + } + } + + priv->iw_mode = mode; + + iwl3945_connection_init_rx_config(priv); + memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN); + + iwl3945_clear_stations_table(priv); + + /* dont commit rxon if rf-kill is on*/ + if (!iwl3945_is_ready_rf(priv)) + return -EAGAIN; + + cancel_delayed_work(&priv->scan_check); + if (iwl3945_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; + } + + iwl3945_commit_rxon(priv); + + return 0; +} + +static void iwl3945_build_tx_cmd_hwcrypto(struct iwl3945_priv *priv, + struct ieee80211_tx_control *ctl, + struct iwl3945_cmd *cmd, + struct sk_buff *skb_frag, + int last_frag) +{ + struct iwl3945_hw_key *keyinfo = &priv->stations[ctl->key_idx].keyinfo; + + switch (keyinfo->alg) { + case ALG_CCMP: + cmd->cmd.tx.sec_ctl = TX_CMD_SEC_CCM; + 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); +#endif + break; + + case ALG_WEP: + cmd->cmd.tx.sec_ctl = TX_CMD_SEC_WEP | + (ctl->key_idx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT; + + if (keyinfo->keylen == 13) + cmd->cmd.tx.sec_ctl |= TX_CMD_SEC_KEY128; + + memcpy(&cmd->cmd.tx.key[3], keyinfo->key, keyinfo->keylen); + + IWL_DEBUG_TX("Configuring packet for WEP encryption " + "with key %d\n", ctl->key_idx); + break; + + default: + printk(KERN_ERR "Unknown encode alg %d\n", keyinfo->alg); + break; + } +} + +/* + * handle build REPLY_TX command notification. + */ +static void iwl3945_build_tx_cmd_basic(struct iwl3945_priv *priv, + struct iwl3945_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; +} + +/** + * iwl3945_get_sta_id - Find station's index within station table + */ +static int iwl3945_get_sta_id(struct iwl3945_priv *priv, struct ieee80211_hdr *hdr) +{ + int sta_id; + u16 fc = le16_to_cpu(hdr->frame_control); + + /* If this frame is broadcast or management, use broadcast station id */ + if (((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA) || + is_multicast_ether_addr(hdr->addr1)) + return priv->hw_setting.bcast_sta_id; + + switch (priv->iw_mode) { + + /* If we are a client station in a BSS network, use the special + * AP station entry (that's the only station we communicate with) */ + 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 = iwl3945_hw_find_station(priv, hdr->addr1); + if (sta_id != IWL_INVALID_STATION) + return sta_id; + return priv->hw_setting.bcast_sta_id; + + /* If this frame is going out to an IBSS network, find the station, + * or create a new station table entry */ + case IEEE80211_IF_TYPE_IBSS: { + DECLARE_MAC_BUF(mac); + + /* Create new station table entry */ + sta_id = iwl3945_hw_find_station(priv, hdr->addr1); + if (sta_id != IWL_INVALID_STATION) + return sta_id; + + sta_id = iwl3945_add_station(priv, hdr->addr1, 0, CMD_ASYNC); + + if (sta_id != IWL_INVALID_STATION) + return sta_id; + + IWL_DEBUG_DROP("Station %s not in station map. " + "Defaulting to broadcast...\n", + print_mac(mac, hdr->addr1)); + iwl3945_print_hex_dump(IWL_DL_DROP, (u8 *) hdr, sizeof(*hdr)); + return priv->hw_setting.bcast_sta_id; + } + default: + IWL_WARNING("Unknown mode of operation: %d", priv->iw_mode); + return priv->hw_setting.bcast_sta_id; + } +} + +/* + * start REPLY_TX command process + */ +static int iwl3945_tx_skb(struct iwl3945_priv *priv, + struct sk_buff *skb, struct ieee80211_tx_control *ctl) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct iwl3945_tfd_frame *tfd; + u32 *control_flags; + int txq_id = ctl->queue; + struct iwl3945_tx_queue *txq = NULL; + struct iwl3945_queue *q = NULL; + dma_addr_t phys_addr; + dma_addr_t txcmd_phys; + struct iwl3945_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 (iwl3945_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_IWL3945_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 + + /* drop all data frame if we are not associated */ + if (!iwl3945_is_associated(priv) && !priv->assoc_id && + ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)) { + IWL_DEBUG_DROP("Dropping - !iwl3945_is_associated\n"); + goto drop_unlock; + } + + spin_unlock_irqrestore(&priv->lock, flags); + + hdr_len = ieee80211_get_hdrlen(fc); + + /* Find (or create) index into station table for destination station */ + sta_id = iwl3945_get_sta_id(priv, hdr); + if (sta_id == IWL_INVALID_STATION) { + DECLARE_MAC_BUF(mac); + + IWL_DEBUG_DROP("Dropping - INVALID STATION: %s\n", + print_mac(mac, 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; + } + + /* Descriptor for chosen Tx queue */ + txq = &priv->txq[txq_id]; + q = &txq->q; + + spin_lock_irqsave(&priv->lock, flags); + + /* Set up first empty TFD within this queue's circular TFD buffer */ + tfd = &txq->bd[q->write_ptr]; + memset(tfd, 0, sizeof(*tfd)); + control_flags = (u32 *) tfd; + idx = get_cmd_index(q, q->write_ptr, 0); + + /* Set up driver data for this TFD */ + memset(&(txq->txb[q->write_ptr]), 0, sizeof(struct iwl3945_tx_info)); + txq->txb[q->write_ptr].skb[0] = skb; + memcpy(&(txq->txb[q->write_ptr].status.control), + ctl, sizeof(struct ieee80211_tx_control)); + + /* Init first empty entry in queue's array of Tx/cmd buffers */ + 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)); + + /* + * Set up the Tx-command (not MAC!) header. + * Store the chosen Tx queue and TFD index within the sequence field; + * after Tx, uCode's Tx response will return this value so driver can + * locate the frame within the tx queue and do post-tx processing. + */ + out_cmd->hdr.cmd = REPLY_TX; + out_cmd->hdr.sequence = cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | + INDEX_TO_SEQ(q->write_ptr))); + + /* Copy MAC header from skb into command buffer */ + memcpy(out_cmd->cmd.tx.hdr, hdr, hdr_len); + + /* + * Use the first empty entry in this queue's command buffer array + * to contain the Tx command and MAC header concatenated together + * (payload data will be in another buffer). + * Size of this varies, due to varying MAC header length. + * If end is not dword aligned, we'll have 2 extra bytes at the end + * of the MAC header (device reads on dword boundaries). + * We'll tell device about this padding later. + */ + len = priv->hw_setting.tx_cmd_len + + sizeof(struct iwl3945_cmd_header) + hdr_len; + + len_org = len; + len = (len + 3) & ~3; + + if (len_org != len) + len_org = 1; + else + len_org = 0; + + /* Physical address of this Tx command's header (not MAC header!), + * within command buffer array. */ + txcmd_phys = txq->dma_addr_cmd + sizeof(struct iwl3945_cmd) * idx + + offsetof(struct iwl3945_cmd, hdr); + + /* Add buffer containing Tx command and MAC(!) header to TFD's + * first entry */ + iwl3945_hw_txq_attach_buf_to_tfd(priv, tfd, txcmd_phys, len); + + if (!(ctl->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT)) + iwl3945_build_tx_cmd_hwcrypto(priv, ctl, out_cmd, skb, 0); + + /* Set up TFD's 2nd entry to point directly to remainder of skb, + * if any (802.11 null frames 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); + iwl3945_hw_txq_attach_buf_to_tfd(priv, tfd, phys_addr, len); + } + + if (!len) + /* If there is no payload, then we use only one Tx buffer */ + *control_flags = TFD_CTL_COUNT_SET(1); + else + /* Else use 2 buffers. + * Tell 3945 about any padding after MAC header */ + *control_flags = TFD_CTL_COUNT_SET(2) | + TFD_CTL_PAD_SET(U32_PAD(len)); + + /* Total # bytes to be transmitted */ + len = (u16)skb->len; + out_cmd->cmd.tx.len = cpu_to_le16(len); + + /* TODO need this for burst mode later on */ + iwl3945_build_tx_cmd_basic(priv, out_cmd, ctl, hdr, unicast, sta_id); + + /* set is_hcca to 0; it probably will never be implemented */ + iwl3945_hw_build_tx_cmd_rate(priv, out_cmd, ctl, hdr, sta_id, 0); + + out_cmd->cmd.tx.tx_flags &= ~TX_CMD_FLG_ANT_A_MSK; + out_cmd->cmd.tx.tx_flags &= ~TX_CMD_FLG_ANT_B_MSK; + + 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; + } + + iwl3945_print_hex_dump(IWL_DL_TX, out_cmd->cmd.payload, + sizeof(out_cmd->cmd.tx)); + + iwl3945_print_hex_dump(IWL_DL_TX, (u8 *)out_cmd->cmd.tx.hdr, + ieee80211_get_hdrlen(fc)); + + /* Tell device the write index *just past* this latest filled TFD */ + q->write_ptr = iwl3945_queue_inc_wrap(q->write_ptr, q->n_bd); + rc = iwl3945_tx_queue_update_write_ptr(priv, txq); + spin_unlock_irqrestore(&priv->lock, flags); + + if (rc) + return rc; + + if ((iwl3945_queue_space(q) < q->high_mark) + && priv->mac80211_registered) { + if (wait_write_ptr) { + spin_lock_irqsave(&priv->lock, flags); + txq->need_update = 1; + iwl3945_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 iwl3945_set_rate(struct iwl3945_priv *priv) +{ + const struct ieee80211_hw_mode *hw = NULL; + struct ieee80211_rate *rate; + int i; + + hw = iwl3945_get_hw_mode(priv, priv->phymode); + if (!hw) { + IWL_ERROR("Failed to set rate: unable to get hw mode\n"); + return; + } + + 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, iwl3945_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, iwl3945_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 iwl3945_radio_kill_sw(struct iwl3945_priv *priv, int disable_radio) +{ + unsigned long flags; + + if (!!disable_radio == test_bit(STATUS_RF_KILL_SW, &priv->status)) + return; + + IWL_DEBUG_RF_KILL("Manual SW RF KILL set to: RADIO %s\n", + disable_radio ? "OFF" : "ON"); + + if (disable_radio) { + iwl3945_scan_cancel(priv); + /* FIXME: This is a workaround for AP */ + if (priv->iw_mode != IEEE80211_IF_TYPE_AP) { + spin_lock_irqsave(&priv->lock, flags); + iwl3945_write32(priv, CSR_UCODE_DRV_GP1_SET, + CSR_UCODE_SW_BIT_RFKILL); + spin_unlock_irqrestore(&priv->lock, flags); + iwl3945_send_card_state(priv, CARD_STATE_CMD_DISABLE, 0); + set_bit(STATUS_RF_KILL_SW, &priv->status); + } + return; + } + + spin_lock_irqsave(&priv->lock, flags); + iwl3945_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); + iwl3945_read32(priv, CSR_UCODE_DRV_GP1); + if (!iwl3945_grab_nic_access(priv)) + iwl3945_release_nic_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 iwl3945_set_decrypted_flag(struct iwl3945_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 iwl3945_handle_data_packet_monitor(struct iwl3945_priv *priv, + struct iwl3945_rx_mem_buffer *rxb, + void *data, short len, + struct ieee80211_rx_status *stats, + u16 phy_flags) +{ + struct iwl3945_rt_rx_hdr *iwl3945_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(*iwl3945_rt)) { + IWL_DEBUG_DROP("Dropping too large packet in monitor\n"); + return; + } + + /* copy the frame data to write after where the radiotap header goes */ + iwl3945_rt = (void *)rxb->skb->data; + memmove(iwl3945_rt->payload, data, len); + + iwl3945_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION; + iwl3945_rt->rt_hdr.it_pad = 0; /* always good to zero */ + + /* total header + data */ + iwl3945_rt->rt_hdr.it_len = cpu_to_le16(sizeof(*iwl3945_rt)); + + /* Set the size of the skb to the size of the frame */ + skb_put(rxb->skb, sizeof(*iwl3945_rt) + len); + + /* Big bitfield of all the fields we provide in radiotap */ + iwl3945_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 */ + iwl3945_rt->rt_flags = 0; + + iwl3945_rt->rt_tsf = cpu_to_le64(tsf); + + /* Convert to dBm */ + iwl3945_rt->rt_dbmsignal = signal; + iwl3945_rt->rt_dbmnoise = noise; + + /* Convert the channel frequency and set the flags */ + iwl3945_rt->rt_channelMHz = cpu_to_le16(stats->freq); + if (!(phy_flags_hw & RX_RES_PHY_FLAGS_BAND_24_MSK)) + iwl3945_rt->rt_chbitmask = + cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ)); + else if (phy_flags_hw & RX_RES_PHY_FLAGS_MOD_CCK_MSK) + iwl3945_rt->rt_chbitmask = + cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ)); + else /* 802.11g */ + iwl3945_rt->rt_chbitmask = + cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ)); + + rate = iwl3945_rate_index_from_plcp(rate); + if (rate == -1) + iwl3945_rt->rt_rate = 0; + else + iwl3945_rt->rt_rate = iwl3945_rates[rate].ieee; + + /* antenna number */ + iwl3945_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) + iwl3945_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 iwl3945_is_duplicate_packet(struct iwl3945_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 iwl3945_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 iwl3945_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_IWL3945_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 iwl3945_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 iwl3945_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 iwl3945_get_measurement(struct iwl3945_priv *priv, + struct ieee80211_measurement_params *params, + u8 type) +{ + struct iwl3945_spectrum_cmd spectrum; + struct iwl3945_rx_packet *res; + struct iwl3945_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 (iwl3945_is_associated(priv)) + add_time = + iwl3945_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 (iwl3945_is_associated(priv)) + spectrum.start_time = + iwl3945_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 = iwl3945_send_cmd_sync(priv, &cmd); + if (rc) + return rc; + + res = (struct iwl3945_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 iwl3945_txstatus_to_ieee(struct iwl3945_priv *priv, + struct iwl3945_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; +} + +/** + * iwl3945_tx_queue_reclaim - Reclaim Tx queue entries already Tx'd + * + * 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 the stack that feeds us. + */ +static int iwl3945_tx_queue_reclaim(struct iwl3945_priv *priv, int txq_id, int index) +{ + struct iwl3945_tx_queue *txq = &priv->txq[txq_id]; + struct iwl3945_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->write_ptr, q->read_ptr); + return 0; + } + + for (index = iwl3945_queue_inc_wrap(index, q->n_bd); + q->read_ptr != index; + q->read_ptr = iwl3945_queue_inc_wrap(q->read_ptr, q->n_bd)) { + if (txq_id != IWL_CMD_QUEUE_NUM) { + iwl3945_txstatus_to_ieee(priv, + &(txq->txb[txq->q.read_ptr])); + iwl3945_hw_txq_free_tfd(priv, txq); + } else if (nfreed > 1) { + IWL_ERROR("HCMD skipped: index (%d) %d %d\n", index, + q->write_ptr, q->read_ptr); + queue_work(priv->workqueue, &priv->restart); + } + nfreed++; + } + + if (iwl3945_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 iwl3945_is_tx_success(u32 status) +{ + return (status & 0xFF) == 0x1; +} + +/****************************************************************************** + * + * Generic RX handler implementations + * + ******************************************************************************/ +/** + * iwl3945_rx_reply_tx - Handle Tx response + */ +static void iwl3945_rx_reply_tx(struct iwl3945_priv *priv, + struct iwl3945_rx_mem_buffer *rxb) +{ + struct iwl3945_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 iwl3945_tx_queue *txq = &priv->txq[txq_id]; + struct ieee80211_tx_status *tx_status; + struct iwl3945_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; + u32 status = le32_to_cpu(tx_resp->status); + + 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.write_ptr, + txq->q.read_ptr); + return; + } + + tx_status = &(txq->txb[txq->q.read_ptr].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 = + iwl3945_is_tx_success(status) ? IEEE80211_TX_STATUS_ACK : 0; + + tx_status->control.tx_rate = iwl3945_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, iwl3945_get_tx_fail_reason(status), status, + tx_resp->rate, tx_resp->failure_frame); + + IWL_DEBUG_TX_REPLY("Tx queue reclaim %d\n", index); + if (index != -1) + iwl3945_tx_queue_reclaim(priv, txq_id, index); + + if (iwl_check_bits(status, TX_ABORT_REQUIRED_MSK)) + IWL_ERROR("TODO: Implement Tx ABORT REQUIRED!!!\n"); +} + + +static void iwl3945_rx_reply_alive(struct iwl3945_priv *priv, + struct iwl3945_rx_mem_buffer *rxb) +{ + struct iwl3945_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl3945_alive_resp *palive; + struct work_struct *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 iwl3945_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 iwl3945_alive_resp)); + pwork = &priv->alive_start; + iwl3945_disable_events(priv); + } + + /* 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 iwl3945_rx_reply_add_sta(struct iwl3945_priv *priv, + struct iwl3945_rx_mem_buffer *rxb) +{ + struct iwl3945_rx_packet *pkt = (void *)rxb->skb->data; + + IWL_DEBUG_RX("Received REPLY_ADD_STA: 0x%02X\n", pkt->u.status); + return; +} + +static void iwl3945_rx_reply_error(struct iwl3945_priv *priv, + struct iwl3945_rx_mem_buffer *rxb) +{ + struct iwl3945_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 iwl3945_rx_csa(struct iwl3945_priv *priv, struct iwl3945_rx_mem_buffer *rxb) +{ + struct iwl3945_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl3945_rxon_cmd *rxon = (void *)&priv->active_rxon; + struct iwl3945_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 iwl3945_rx_spectrum_measure_notif(struct iwl3945_priv *priv, + struct iwl3945_rx_mem_buffer *rxb) +{ +#ifdef CONFIG_IWL3945_SPECTRUM_MEASUREMENT + struct iwl3945_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl3945_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 iwl3945_rx_pm_sleep_notif(struct iwl3945_priv *priv, + struct iwl3945_rx_mem_buffer *rxb) +{ +#ifdef CONFIG_IWL3945_DEBUG + struct iwl3945_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl3945_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 iwl3945_rx_pm_debug_statistics_notif(struct iwl3945_priv *priv, + struct iwl3945_rx_mem_buffer *rxb) +{ + struct iwl3945_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)); + iwl3945_print_hex_dump(IWL_DL_RADIO, pkt->u.raw, le32_to_cpu(pkt->len)); +} + +static void iwl3945_bg_beacon_update(void *p) +{ + struct iwl3945_priv *priv = p; + 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); + + iwl3945_send_beacon_cmd(priv); +} + +static void iwl3945_rx_beacon_notif(struct iwl3945_priv *priv, + struct iwl3945_rx_mem_buffer *rxb) +{ +#ifdef CONFIG_IWL3945_DEBUG + struct iwl3945_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl3945_beacon_notif *beacon = &(pkt->u.beacon_status); + u8 rate = beacon->beacon_notify_hdr.rate; + + 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 iwl3945_rx_reply_scan(struct iwl3945_priv *priv, + struct iwl3945_rx_mem_buffer *rxb) +{ +#ifdef CONFIG_IWL3945_DEBUG + struct iwl3945_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl3945_scanreq_notification *notif = + (struct iwl3945_scanreq_notification *)pkt->u.raw; + + IWL_DEBUG_RX("Scan request status = 0x%x\n", notif->status); +#endif +} + +/* Service SCAN_START_NOTIFICATION (0x82) */ +static void iwl3945_rx_scan_start_notif(struct iwl3945_priv *priv, + struct iwl3945_rx_mem_buffer *rxb) +{ + struct iwl3945_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl3945_scanstart_notification *notif = + (struct iwl3945_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 iwl3945_rx_scan_results_notif(struct iwl3945_priv *priv, + struct iwl3945_rx_mem_buffer *rxb) +{ + struct iwl3945_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl3945_scanresults_notification *notif = + (struct iwl3945_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; + priv->next_scan_jiffies = 0; +} + +/* Service SCAN_COMPLETE_NOTIFICATION (0x84) */ +static void iwl3945_rx_scan_complete_notif(struct iwl3945_priv *priv, + struct iwl3945_rx_mem_buffer *rxb) +{ + struct iwl3945_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl3945_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; + priv->next_scan_jiffies = 0; + 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 iwl3945_rx_card_state_notif(struct iwl3945_priv *priv, + struct iwl3945_rx_mem_buffer *rxb) +{ + struct iwl3945_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"); + + iwl3945_write32(priv, CSR_UCODE_DRV_GP1_SET, + CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + + 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); + + iwl3945_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); +} + +/** + * iwl3945_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 iwl3945_setup_rx_handlers(struct iwl3945_priv *priv) +{ + priv->rx_handlers[REPLY_ALIVE] = iwl3945_rx_reply_alive; + priv->rx_handlers[REPLY_ADD_STA] = iwl3945_rx_reply_add_sta; + priv->rx_handlers[REPLY_ERROR] = iwl3945_rx_reply_error; + priv->rx_handlers[CHANNEL_SWITCH_NOTIFICATION] = iwl3945_rx_csa; + priv->rx_handlers[SPECTRUM_MEASURE_NOTIFICATION] = + iwl3945_rx_spectrum_measure_notif; + priv->rx_handlers[PM_SLEEP_NOTIFICATION] = iwl3945_rx_pm_sleep_notif; + priv->rx_handlers[PM_DEBUG_STATISTIC_NOTIFIC] = + iwl3945_rx_pm_debug_statistics_notif; + priv->rx_handlers[BEACON_NOTIFICATION] = iwl3945_rx_beacon_notif; + + /* + * The same handler is used for both the REPLY to a discrete + * statistics request from the host as well as for the periodic + * statistics notifications (after received beacons) from the uCode. + */ + priv->rx_handlers[REPLY_STATISTICS_CMD] = iwl3945_hw_rx_statistics; + priv->rx_handlers[STATISTICS_NOTIFICATION] = iwl3945_hw_rx_statistics; + + priv->rx_handlers[REPLY_SCAN_CMD] = iwl3945_rx_reply_scan; + priv->rx_handlers[SCAN_START_NOTIFICATION] = iwl3945_rx_scan_start_notif; + priv->rx_handlers[SCAN_RESULTS_NOTIFICATION] = + iwl3945_rx_scan_results_notif; + priv->rx_handlers[SCAN_COMPLETE_NOTIFICATION] = + iwl3945_rx_scan_complete_notif; + priv->rx_handlers[CARD_STATE_NOTIFICATION] = iwl3945_rx_card_state_notif; + priv->rx_handlers[REPLY_TX] = iwl3945_rx_reply_tx; + + /* Set up hardware specific Rx handlers */ + iwl3945_hw_rx_handler_setup(priv); +} + +/** + * iwl3945_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 iwl3945_tx_cmd_complete(struct iwl3945_priv *priv, + struct iwl3945_rx_mem_buffer *rxb) +{ + struct iwl3945_rx_packet *pkt = (struct iwl3945_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 iwl3945_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; + + iwl3945_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 iwl->rxq->rx_free. When + * iwl->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled + * to replenish the iwl->rxq->rx_free. + * + In iwl3945_rx_replenish (scheduled) if 'processed' != 'read' then the + * iwl->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 iwl->rxq. The driver 'processed' index is updated. + * + The Host/Firmware iwl->rxq is replenished at tasklet time from the rx_free + * list. If there are no allocated buffers in iwl->rxq->rx_free, the READ + * INDEX is not incremented and iwl->status(RX_STALLED) is set. If there + * were enough free buffers and RX_STALLED is set it is cleared. + * + * + * Driver sequence: + * + * iwl3945_rx_queue_alloc() Allocates rx_free + * iwl3945_rx_replenish() Replenishes rx_free list from rx_used, and calls + * iwl3945_rx_queue_restock + * iwl3945_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 iwl3945_rx_replenish + * + * -- enable interrupts -- + * ISR - iwl3945_rx() Detach iwl3945_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 iwl3945_rx_queue_restock to refill any empty + * slots. + * ... + * + */ + +/** + * iwl3945_rx_queue_space - Return number of free slots available in queue. + */ +static int iwl3945_rx_queue_space(const struct iwl3945_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; +} + +/** + * iwl3945_rx_queue_update_write_ptr - Update the write pointer for the RX queue + */ +int iwl3945_rx_queue_update_write_ptr(struct iwl3945_priv *priv, struct iwl3945_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 power-saving is in use, make sure device is awake */ + if (test_bit(STATUS_POWER_PMI, &priv->status)) { + reg = iwl3945_read32(priv, CSR_UCODE_DRV_GP1); + + if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { + iwl3945_set_bit(priv, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + goto exit_unlock; + } + + rc = iwl3945_grab_nic_access(priv); + if (rc) + goto exit_unlock; + + /* Device expects a multiple of 8 */ + iwl3945_write_direct32(priv, FH_RSCSR_CHNL0_WPTR, + q->write & ~0x7); + iwl3945_release_nic_access(priv); + + /* Else device is assumed to be awake */ + } else + /* Device expects a multiple of 8 */ + iwl3945_write32(priv, FH_RSCSR_CHNL0_WPTR, q->write & ~0x7); + + + q->need_update = 0; + + exit_unlock: + spin_unlock_irqrestore(&q->lock, flags); + return rc; +} + +/** + * iwl3945_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr + */ +static inline __le32 iwl3945_dma_addr2rbd_ptr(struct iwl3945_priv *priv, + dma_addr_t dma_addr) +{ + return cpu_to_le32((u32)dma_addr); +} + +/** + * iwl3945_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. + */ +static int iwl3945_rx_queue_restock(struct iwl3945_priv *priv) +{ + struct iwl3945_rx_queue *rxq = &priv->rxq; + struct list_head *element; + struct iwl3945_rx_mem_buffer *rxb; + unsigned long flags; + int write, rc; + + spin_lock_irqsave(&rxq->lock, flags); + write = rxq->write & ~0x7; + while ((iwl3945_rx_queue_space(rxq) > 0) && (rxq->free_count)) { + /* Get next free Rx buffer, remove from free list */ + element = rxq->rx_free.next; + rxb = list_entry(element, struct iwl3945_rx_mem_buffer, list); + list_del(element); + + /* Point to Rx buffer via next RBD in circular buffer */ + rxq->bd[rxq->write] = iwl3945_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. + * Increment device's write pointer in multiples of 8. */ + 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 = iwl3945_rx_queue_update_write_ptr(priv, rxq); + if (rc) + return rc; + } + + return 0; +} + +/** + * iwl3945_rx_replenish - 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 iwl3945_rx_queue_restock. + * This is called as a scheduled work item (except for during initialization) + */ +static void iwl3945_rx_allocate(struct iwl3945_priv *priv) +{ + struct iwl3945_rx_queue *rxq = &priv->rxq; + struct list_head *element; + struct iwl3945_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 iwl3945_rx_mem_buffer, list); + + /* Alloc a new receive buffer */ + 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); + + /* Get physical address of RB/SKB */ + 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); +} + +/* + * this should be called while priv->lock is locked + */ +static void __iwl3945_rx_replenish(void *data) +{ + struct iwl3945_priv *priv = data; + + iwl3945_rx_allocate(priv); + iwl3945_rx_queue_restock(priv); +} + + +void iwl3945_rx_replenish(void *data) +{ + struct iwl3945_priv *priv = data; + unsigned long flags; + + iwl3945_rx_allocate(priv); + + spin_lock_irqsave(&priv->lock, flags); + iwl3945_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 its 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 + */ +static void iwl3945_rx_queue_free(struct iwl3945_priv *priv, struct iwl3945_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 iwl3945_rx_queue_alloc(struct iwl3945_priv *priv) +{ + struct iwl3945_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); + + /* Alloc the circular buffer of Read Buffer Descriptors (RBDs) */ + 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 iwl3945_rx_queue_reset(struct iwl3945_priv *priv, struct iwl3945_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 iwl3945_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 iwl3945_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; +} + +/** + * iwl3945_rx_handle - Main entry function for receiving responses from 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 iwl3945_rx_handle(struct iwl3945_priv *priv) +{ + struct iwl3945_rx_mem_buffer *rxb; + struct iwl3945_rx_packet *pkt; + struct iwl3945_rx_queue *rxq = &priv->rxq; + u32 r, i; + int reclaim; + unsigned long flags; + u8 fill_rx = 0; + u32 count = 0; + + /* uCode's read index (stored in shared DRAM) indicates the last Rx + * buffer that the driver may process (last buffer filled by ucode). */ + r = iwl3945_hw_get_rx_read(priv); + i = rxq->read; + + if (iwl3945_rx_queue_space(rxq) > (RX_QUEUE_SIZE / 2)) + fill_rx = 1; + /* 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 Rx 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 iwl3945_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) && + (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 iwl3945_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 iwl3945_send_cmd() + * as we reclaim the driver command queue */ + if (rxb && rxb->skb) + iwl3945_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; + /* If there are a lot of unused frames, + * restock the Rx queue so ucode won't assert. */ + if (fill_rx) { + count++; + if (count >= 8) { + priv->rxq.read = i; + __iwl3945_rx_replenish(priv); + count = 0; + } + } + } + + /* Backtrack one entry */ + priv->rxq.read = i; + iwl3945_rx_queue_restock(priv); +} + +/** + * iwl3945_tx_queue_update_write_ptr - Send new write index to hardware + */ +static int iwl3945_tx_queue_update_write_ptr(struct iwl3945_priv *priv, + struct iwl3945_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 = iwl3945_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); + iwl3945_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 = iwl3945_grab_nic_access(priv); + if (rc) + return rc; + iwl3945_write_direct32(priv, HBUS_TARG_WRPTR, + txq->q.write_ptr | (txq_id << 8)); + iwl3945_release_nic_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 + iwl3945_write32(priv, HBUS_TARG_WRPTR, + txq->q.write_ptr | (txq_id << 8)); + + txq->need_update = 0; + + return rc; +} + +#ifdef CONFIG_IWL3945_DEBUG +static void iwl3945_print_rx_config_cmd(struct iwl3945_rxon_cmd *rxon) +{ + DECLARE_MAC_BUF(mac); + + IWL_DEBUG_RADIO("RX CONFIG:\n"); + iwl3945_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: %s\n", + print_mac(mac, rxon->node_addr)); + IWL_DEBUG_RADIO("u8[6] bssid_addr: %s\n", + print_mac(mac, rxon->bssid_addr)); + IWL_DEBUG_RADIO("u16 assoc_id: 0x%x\n", le16_to_cpu(rxon->assoc_id)); +} +#endif + +static void iwl3945_enable_interrupts(struct iwl3945_priv *priv) +{ + IWL_DEBUG_ISR("Enabling interrupts\n"); + set_bit(STATUS_INT_ENABLED, &priv->status); + iwl3945_write32(priv, CSR_INT_MASK, CSR_INI_SET_MASK); +} + +static inline void iwl3945_disable_interrupts(struct iwl3945_priv *priv) +{ + clear_bit(STATUS_INT_ENABLED, &priv->status); + + /* disable interrupts from uCode/NIC to host */ + iwl3945_write32(priv, CSR_INT_MASK, 0x00000000); + + /* acknowledge/clear/reset any interrupts still pending + * from uCode or flow handler (Rx/Tx DMA) */ + iwl3945_write32(priv, CSR_INT, 0xffffffff); + iwl3945_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 iwl3945_dump_nic_error_log(struct iwl3945_priv *priv) +{ + u32 i; + 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 (!iwl3945_hw_valid_rtc_data_addr(base)) { + IWL_ERROR("Not valid error log pointer 0x%08X\n", base); + return; + } + + rc = iwl3945_grab_nic_access(priv); + if (rc) { + IWL_WARNING("Can not read from adapter at this time.\n"); + return; + } + + count = iwl3945_read_targ_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); + } + + 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 = iwl3945_read_targ_mem(priv, base + i); + time = + iwl3945_read_targ_mem(priv, base + i + 1 * sizeof(u32)); + blink1 = + iwl3945_read_targ_mem(priv, base + i + 2 * sizeof(u32)); + blink2 = + iwl3945_read_targ_mem(priv, base + i + 3 * sizeof(u32)); + ilink1 = + iwl3945_read_targ_mem(priv, base + i + 4 * sizeof(u32)); + ilink2 = + iwl3945_read_targ_mem(priv, base + i + 5 * sizeof(u32)); + data1 = + iwl3945_read_targ_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); + } + + iwl3945_release_nic_access(priv); + +} + +#define EVENT_START_OFFSET (6 * sizeof(u32)) + +/** + * iwl3945_print_event_log - Dump error event log to syslog + * + * NOTE: Must be called with iwl3945_grab_nic_access() already obtained! + */ +static void iwl3945_print_event_log(struct iwl3945_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 = iwl3945_read_targ_mem(priv, ptr); + ptr += sizeof(u32); + time = iwl3945_read_targ_mem(priv, ptr); + ptr += sizeof(u32); + if (mode == 0) + IWL_ERROR("0x%08x\t%04u\n", time, ev); /* data, ev */ + else { + data = iwl3945_read_targ_mem(priv, ptr); + ptr += sizeof(u32); + IWL_ERROR("%010u\t0x%08x\t%04u\n", time, data, ev); + } + } +} + +static void iwl3945_dump_nic_event_log(struct iwl3945_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 (!iwl3945_hw_valid_rtc_data_addr(base)) { + IWL_ERROR("Invalid event log pointer 0x%08X\n", base); + return; + } + + rc = iwl3945_grab_nic_access(priv); + if (rc) { + IWL_WARNING("Can not read from adapter at this time.\n"); + return; + } + + /* event log header */ + capacity = iwl3945_read_targ_mem(priv, base); + mode = iwl3945_read_targ_mem(priv, base + (1 * sizeof(u32))); + num_wraps = iwl3945_read_targ_mem(priv, base + (2 * sizeof(u32))); + next_entry = iwl3945_read_targ_mem(priv, base + (3 * sizeof(u32))); + + size = num_wraps ? capacity : next_entry; + + /* bail out if nothing in log */ + if (size == 0) { + IWL_ERROR("Start IWL Event Log Dump: nothing in log\n"); + iwl3945_release_nic_access(priv); + return; + } + + IWL_ERROR("Start IWL 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) + iwl3945_print_event_log(priv, next_entry, + capacity - next_entry, mode); + + /* (then/else) start at top of log */ + iwl3945_print_event_log(priv, 0, next_entry, mode); + + iwl3945_release_nic_access(priv); +} + +/** + * iwl3945_irq_handle_error - called for HW or SW error interrupt from card + */ +static void iwl3945_irq_handle_error(struct iwl3945_priv *priv) +{ + /* Set the FW error flag -- cleared on iwl3945_down */ + set_bit(STATUS_FW_ERROR, &priv->status); + + /* Cancel currently queued command. */ + clear_bit(STATUS_HCMD_ACTIVE, &priv->status); + +#ifdef CONFIG_IWL3945_DEBUG + if (iwl3945_debug_level & IWL_DL_FW_ERRORS) { + iwl3945_dump_nic_error_log(priv); + iwl3945_dump_nic_event_log(priv); + iwl3945_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 (iwl3945_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 iwl3945_error_recovery(struct iwl3945_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; + iwl3945_commit_rxon(priv); + + iwl3945_add_station(priv, priv->bssid, 1, 0); + + 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 iwl3945_irq_tasklet(struct iwl3945_priv *priv) +{ + u32 inta, handled = 0; + u32 inta_fh; + unsigned long flags; +#ifdef CONFIG_IWL3945_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 = iwl3945_read32(priv, CSR_INT); + iwl3945_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 = iwl3945_read32(priv, CSR_FH_INT_STATUS); + iwl3945_write32(priv, CSR_FH_INT_STATUS, inta_fh); + +#ifdef CONFIG_IWL3945_DEBUG + if (iwl3945_debug_level & IWL_DL_ISR) { + /* just for debug */ + inta_mask = iwl3945_read32(priv, CSR_INT_MASK); + 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 & CSR_FH_INT_RX_MASK) + inta |= CSR_INT_BIT_FH_RX; + if (inta_fh & CSR_FH_INT_TX_MASK) + inta |= CSR_INT_BIT_FH_TX; + + /* Now service all interrupt bits discovered above. */ + if (inta & CSR_INT_BIT_HW_ERR) { + IWL_ERROR("Microcode HW error detected. Restarting.\n"); + + /* Tell the device to stop sending interrupts */ + iwl3945_disable_interrupts(priv); + + iwl3945_irq_handle_error(priv); + + handled |= CSR_INT_BIT_HW_ERR; + + spin_unlock_irqrestore(&priv->lock, flags); + + return; + } + +#ifdef CONFIG_IWL3945_DEBUG + if (iwl3945_debug_level & (IWL_DL_ISR)) { + /* NIC fires this, but we don't use it, redundant with WAKEUP */ + if (inta & CSR_INT_BIT_MAC_CLK_ACTV) + IWL_DEBUG_ISR("Microcode started or stopped.\n"); + + /* Alive notification via Rx interrupt will do the real work */ + if (inta & CSR_INT_BIT_ALIVE) + IWL_DEBUG_ISR("Alive interrupt\n"); + } +#endif + /* Safely ignore these bits for debug checks below */ + inta &= ~(CSR_INT_BIT_MAC_CLK_ACTV | CSR_INT_BIT_ALIVE); + + /* HW RF KILL switch toggled (4965 only) */ + if (inta & CSR_INT_BIT_RF_KILL) { + int hw_rf_kill = 0; + if (!(iwl3945_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 + * iwl3945_rx_card_state_notif() */ + if (!hw_rf_kill && !test_bit(STATUS_ALIVE, &priv->status)) { + clear_bit(STATUS_RF_KILL_HW, &priv->status); + queue_work(priv->workqueue, &priv->restart); + } + + handled |= CSR_INT_BIT_RF_KILL; + } + + /* Chip got too hot and stopped itself (4965 only) */ + if (inta & CSR_INT_BIT_CT_KILL) { + IWL_ERROR("Microcode CT kill error detected.\n"); + handled |= CSR_INT_BIT_CT_KILL; + } + + /* Error detected by uCode */ + if (inta & CSR_INT_BIT_SW_ERR) { + IWL_ERROR("Microcode SW error detected. Restarting 0x%X.\n", + inta); + iwl3945_irq_handle_error(priv); + handled |= CSR_INT_BIT_SW_ERR; + } + + /* uCode wakes up after power-down sleep */ + if (inta & CSR_INT_BIT_WAKEUP) { + IWL_DEBUG_ISR("Wakeup interrupt\n"); + iwl3945_rx_queue_update_write_ptr(priv, &priv->rxq); + iwl3945_tx_queue_update_write_ptr(priv, &priv->txq[0]); + iwl3945_tx_queue_update_write_ptr(priv, &priv->txq[1]); + iwl3945_tx_queue_update_write_ptr(priv, &priv->txq[2]); + iwl3945_tx_queue_update_write_ptr(priv, &priv->txq[3]); + iwl3945_tx_queue_update_write_ptr(priv, &priv->txq[4]); + iwl3945_tx_queue_update_write_ptr(priv, &priv->txq[5]); + + handled |= CSR_INT_BIT_WAKEUP; + } + + /* All uCode command responses, including Tx command responses, + * Rx "responses" (frame-received notification), and other + * notifications from uCode come through here*/ + if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) { + iwl3945_rx_handle(priv); + handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX); + } + + if (inta & CSR_INT_BIT_FH_TX) { + IWL_DEBUG_ISR("Tx interrupt\n"); + + iwl3945_write32(priv, CSR_FH_INT_STATUS, (1 << 6)); + if (!iwl3945_grab_nic_access(priv)) { + iwl3945_write_direct32(priv, + FH_TCSR_CREDIT + (ALM_FH_SRVC_CHNL), 0x0); + iwl3945_release_nic_access(priv); + } + handled |= CSR_INT_BIT_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 */ + iwl3945_enable_interrupts(priv); + +#ifdef CONFIG_IWL3945_DEBUG + if (iwl3945_debug_level & (IWL_DL_ISR)) { + inta = iwl3945_read32(priv, CSR_INT); + inta_mask = iwl3945_read32(priv, CSR_INT_MASK); + inta_fh = iwl3945_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 iwl3945_isr(int irq, void *data, struct pt_regs *regs) +{ + struct iwl3945_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 = iwl3945_read32(priv, CSR_INT_MASK); /* just for debug */ + iwl3945_write32(priv, CSR_INT_MASK, 0x00000000); + + /* Discover which interrupts are active/pending */ + inta = iwl3945_read32(priv, CSR_INT); + inta_fh = iwl3945_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 unplugged; + } + + IWL_DEBUG_ISR("ISR inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", + inta, inta_mask, inta_fh); + + /* iwl3945_irq_tasklet() will service interrupts and re-enable them */ + tasklet_schedule(&priv->irq_tasklet); +unplugged: + spin_unlock(&priv->lock); + + return IRQ_HANDLED; + + none: + /* re-enable interrupts here since we don't have anything to service. */ + iwl3945_enable_interrupts(priv); + spin_unlock(&priv->lock); + return IRQ_NONE; +} + +/************************** EEPROM BANDS **************************** + * + * The iwl3945_eeprom_band definitions below provide the mapping from the + * EEPROM contents to the specific channel number supported for each + * band. + * + * For example, iwl3945_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 iwl3945_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 iwl3945_eeprom_band_2[] = { /* 4915-5080MHz */ + 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 +}; + +static const u8 iwl3945_eeprom_band_3[] = { /* 5170-5320MHz */ + 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 +}; + +static const u8 iwl3945_eeprom_band_4[] = { /* 5500-5700MHz */ + 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 +}; + +static const u8 iwl3945_eeprom_band_5[] = { /* 5725-5825MHz */ + 145, 149, 153, 157, 161, 165 +}; + +static void iwl3945_init_band_reference(const struct iwl3945_priv *priv, int band, + int *eeprom_ch_count, + const struct iwl3945_eeprom_channel + **eeprom_ch_info, + const u8 **eeprom_ch_index) +{ + switch (band) { + case 1: /* 2.4GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl3945_eeprom_band_1); + *eeprom_ch_info = priv->eeprom.band_1_channels; + *eeprom_ch_index = iwl3945_eeprom_band_1; + break; + case 2: /* 4.9GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl3945_eeprom_band_2); + *eeprom_ch_info = priv->eeprom.band_2_channels; + *eeprom_ch_index = iwl3945_eeprom_band_2; + break; + case 3: /* 5.2GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl3945_eeprom_band_3); + *eeprom_ch_info = priv->eeprom.band_3_channels; + *eeprom_ch_index = iwl3945_eeprom_band_3; + break; + case 4: /* 5.5GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl3945_eeprom_band_4); + *eeprom_ch_info = priv->eeprom.band_4_channels; + *eeprom_ch_index = iwl3945_eeprom_band_4; + break; + case 5: /* 5.7GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl3945_eeprom_band_5); + *eeprom_ch_info = priv->eeprom.band_5_channels; + *eeprom_ch_index = iwl3945_eeprom_band_5; + break; + default: + BUG(); + return; + } +} + +/** + * iwl3945_get_channel_info - Find driver's private channel info + * + * Based on band and channel number. + */ +const struct iwl3945_channel_info *iwl3945_get_channel_info(const struct iwl3945_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 " " : "") + +/** + * iwl3945_init_channel_map - Set up driver's info for all possible channels + */ +static int iwl3945_init_channel_map(struct iwl3945_priv *priv) +{ + int eeprom_ch_count = 0; + const u8 *eeprom_ch_index = NULL; + const struct iwl3945_eeprom_channel *eeprom_ch_info = NULL; + int band, ch; + struct iwl3945_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(iwl3945_eeprom_band_1) + + ARRAY_SIZE(iwl3945_eeprom_band_2) + + ARRAY_SIZE(iwl3945_eeprom_band_3) + + ARRAY_SIZE(iwl3945_eeprom_band_4) + + ARRAY_SIZE(iwl3945_eeprom_band_5); + + IWL_DEBUG_INFO("Parsing data for %d channels.\n", priv->channel_count); + + priv->channel_info = kzalloc(sizeof(struct iwl3945_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++) { + + iwl3945_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++; + } + } + + /* Set up txpower settings in driver for all channels */ + 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 iwl3945_get_active_dwell_time(struct iwl3945_priv *priv, int phymode) +{ + if (phymode == MODE_IEEE80211A) + return IWL_ACTIVE_DWELL_TIME_52; + else + return IWL_ACTIVE_DWELL_TIME_24; +} + +static u16 iwl3945_get_passive_dwell_time(struct iwl3945_priv *priv, int phymode) +{ + u16 active = iwl3945_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 (iwl3945_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 iwl3945_get_channels_for_scan(struct iwl3945_priv *priv, int phymode, + u8 is_active, u8 direct_mask, + struct iwl3945_scan_channel *scan_ch) +{ + const struct ieee80211_channel *channels = NULL; + const struct ieee80211_hw_mode *hw_mode; + const struct iwl3945_channel_info *ch_info; + u16 passive_dwell = 0; + u16 active_dwell = 0; + int added, i; + + hw_mode = iwl3945_get_hw_mode(priv, phymode); + if (!hw_mode) + return 0; + + channels = hw_mode->channels; + + active_dwell = iwl3945_get_active_dwell_time(priv, phymode); + passive_dwell = iwl3945_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 (iwl3945_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 = iwl3945_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 txpower 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 iwl3945_reset_channel_flag(struct iwl3945_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 iwl3945_init_hw_rates(struct iwl3945_priv *priv, + struct ieee80211_rate *rates) +{ + int i; + + for (i = 0; i < IWL_RATE_COUNT; i++) { + rates[i].rate = iwl3945_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 |= (iwl3945_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; + } +} + +/** + * iwl3945_init_geos - Initialize mac80211's geo/channel info based from eeprom + */ +static int iwl3945_init_geos(struct iwl3945_priv *priv) +{ + struct iwl3945_channel_info *ch; + struct ieee80211_hw_mode *modes; + struct ieee80211_channel *channels; + struct ieee80211_channel *geo_ch; + struct ieee80211_rate *rates; + int i = 0; + enum { + A = 0, + B = 1, + G = 2, + }; + int mode_count = 3; + + 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(iwl3945_eeprom_band_1)]; + modes[A].rates = &rates[4]; + modes[A].num_rates = 8; /* just OFDM */ + modes[A].num_channels = 0; + + modes[B].mode = MODE_IEEE80211B; + modes[B].channels = channels; + modes[B].rates = rates; + 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; + + priv->ieee_channels = channels; + priv->ieee_rates = rates; + + iwl3945_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++]; + else { + geo_ch = &modes[B].channels[modes[B].num_channels++]; + modes[G].num_channels++; + } + + 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 iwl3945_dealloc_ucode_pci(struct iwl3945_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; + } +} + +/** + * iwl3945_verify_inst_full - verify runtime uCode image in card vs. host, + * looking at all data. + */ +static int iwl3945_verify_inst_full(struct iwl3945_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 = iwl3945_grab_nic_access(priv); + if (rc) + return rc; + + iwl3945_write_direct32(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 = _iwl3945_read_direct32(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; + } + } + + iwl3945_release_nic_access(priv); + + if (!errcnt) + IWL_DEBUG_INFO("ucode image in INSTRUCTION memory is good\n"); + + return rc; +} + + +/** + * iwl3945_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 iwl3945_verify_inst_sparse(struct iwl3945_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 = iwl3945_grab_nic_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 */ + iwl3945_write_direct32(priv, HBUS_TARG_MEM_RADDR, + i + RTC_INST_LOWER_BOUND); + val = _iwl3945_read_direct32(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; + } + } + + iwl3945_release_nic_access(priv); + + return rc; +} + + +/** + * iwl3945_verify_ucode - determine which instruction image is in SRAM, + * and verify its contents + */ +static int iwl3945_verify_ucode(struct iwl3945_priv *priv) +{ + __le32 *image; + u32 len; + int rc = 0; + + /* Try bootstrap */ + image = (__le32 *)priv->ucode_boot.v_addr; + len = priv->ucode_boot.len; + rc = iwl3945_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 = iwl3945_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 = iwl3945_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"); + + /* Since nothing seems to match, show first several data entries in + * instruction SRAM, so maybe visual inspection will give a clue. + * Selection of bootstrap image (vs. other images) is arbitrary. */ + image = (__le32 *)priv->ucode_boot.v_addr; + len = priv->ucode_boot.len; + rc = iwl3945_verify_inst_full(priv, image, len); + + return rc; +} + + +/* check contents of special bootstrap uCode SRAM */ +static int iwl3945_verify_bsm(struct iwl3945_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 = iwl3945_read_prph(priv, BSM_WR_DWCOUNT_REG); + for (reg = BSM_SRAM_LOWER_BOUND; + reg < BSM_SRAM_LOWER_BOUND + len; + reg += sizeof(u32), image ++) { + val = iwl3945_read_prph(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; +} + +/** + * iwl3945_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 iwl3945_load_bsm(struct iwl3945_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 ... host DRAM physical address bits 31:0 for 3945. + * NOTE: iwl3945_initialize_alive_start() will replace these values, + * after the "initialize" uCode has run, to point to + * runtime/protocol instructions and backup data cache. */ + pinst = priv->ucode_init.p_addr; + pdata = priv->ucode_init_data.p_addr; + inst_len = priv->ucode_init.len; + data_len = priv->ucode_init_data.len; + + rc = iwl3945_grab_nic_access(priv); + if (rc) + return rc; + + iwl3945_write_prph(priv, BSM_DRAM_INST_PTR_REG, pinst); + iwl3945_write_prph(priv, BSM_DRAM_DATA_PTR_REG, pdata); + iwl3945_write_prph(priv, BSM_DRAM_INST_BYTECOUNT_REG, inst_len); + iwl3945_write_prph(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++) + _iwl3945_write_prph(priv, reg_offset, + le32_to_cpu(*image)); + + rc = iwl3945_verify_bsm(priv); + if (rc) { + iwl3945_release_nic_access(priv); + return rc; + } + + /* Tell BSM to copy from BSM SRAM into instruction SRAM, when asked */ + iwl3945_write_prph(priv, BSM_WR_MEM_SRC_REG, 0x0); + iwl3945_write_prph(priv, BSM_WR_MEM_DST_REG, + RTC_INST_LOWER_BOUND); + iwl3945_write_prph(priv, BSM_WR_DWCOUNT_REG, len / sizeof(u32)); + + /* Load bootstrap code into instruction SRAM now, + * to prepare to load "initialize" uCode */ + iwl3945_write_prph(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 = iwl3945_read_prph(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) */ + iwl3945_write_prph(priv, BSM_WR_CTRL_REG, + BSM_WR_CTRL_REG_BIT_START_EN); + + iwl3945_release_nic_access(priv); + + return 0; +} + +static void iwl3945_nic_start(struct iwl3945_priv *priv) +{ + /* Remove all resets to allow NIC to operate */ + iwl3945_write32(priv, CSR_RESET, 0); +} + +static int iwl3945_alloc_fw_desc(struct pci_dev *pci_dev, struct fw_desc *desc) +{ + desc->v_addr = pci_alloc_consistent(pci_dev, desc->len, &desc->p_addr); + return (desc->v_addr != NULL) ? 0 : -ENOMEM; +} + +/** + * iwl3945_read_ucode - Read uCode images from disk file. + * + * Copy into buffers for card to fetch via bus-mastering + */ +static int iwl3945_read_ucode(struct iwl3945_priv *priv) +{ + struct iwl3945_ucode *ucode; + int ret = 0; + const struct firmware *ucode_raw; + /* firmware file name contains uCode/driver compatibility version */ + const char *name = "iwlwifi-3945" IWL3945_UCODE_API ".ucode"; + 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. */ + ret = request_firmware(&ucode_raw, name, &priv->pci_dev->dev); + if (ret < 0) { + IWL_ERROR("%s firmware file req failed: Reason %d\n", + name, ret); + 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"); + ret = -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); + ret = -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\n", + inst_size); + ret = -EINVAL; + goto err_release; + } + + if (data_size > IWL_MAX_DATA_SIZE) { + IWL_DEBUG_INFO("uCode data len %d too large to fit in\n", + data_size); + ret = -EINVAL; + goto err_release; + } + if (init_size > IWL_MAX_INST_SIZE) { + IWL_DEBUG_INFO("uCode init instr len %d too large to fit in\n", + init_size); + ret = -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\n", + init_data_size); + ret = -EINVAL; + goto err_release; + } + if (boot_size > IWL_MAX_BSM_SIZE) { + IWL_DEBUG_INFO("uCode boot instr len %d too large to fit in\n", + boot_size); + ret = -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; + iwl3945_alloc_fw_desc(priv->pci_dev, &priv->ucode_code); + + priv->ucode_data.len = data_size; + iwl3945_alloc_fw_desc(priv->pci_dev, &priv->ucode_data); + + priv->ucode_data_backup.len = data_size; + iwl3945_alloc_fw_desc(priv->pci_dev, &priv->ucode_data_backup); + + if (!priv->ucode_code.v_addr || !priv->ucode_data.v_addr || + !priv->ucode_data_backup.v_addr) + goto err_pci_alloc; + + /* Initialization instructions and data */ + if (init_size && init_data_size) { + priv->ucode_init.len = init_size; + iwl3945_alloc_fw_desc(priv->pci_dev, &priv->ucode_init); + + priv->ucode_init_data.len = init_data_size; + iwl3945_alloc_fw_desc(priv->pci_dev, &priv->ucode_init_data); + + if (!priv->ucode_init.v_addr || !priv->ucode_init_data.v_addr) + goto err_pci_alloc; + } + + /* Bootstrap (instructions only, no data) */ + if (boot_size) { + priv->ucode_boot.len = boot_size; + iwl3945_alloc_fw_desc(priv->pci_dev, &priv->ucode_boot); + + if (!priv->ucode_boot.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 %Zd\n", 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 iwl3945_up() */ + src = &ucode->data[inst_size]; + len = priv->ucode_data.len; + IWL_DEBUG_INFO("Copying (but not loading) uCode data len %Zd\n", 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 %Zd\n", + 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"); + ret = -ENOMEM; + iwl3945_dealloc_ucode_pci(priv); + + err_release: + release_firmware(ucode_raw); + + error: + return ret; +} + + +/** + * iwl3945_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 iwl3945_set_ucode_ptrs(struct iwl3945_priv *priv) +{ + dma_addr_t pinst; + dma_addr_t pdata; + int rc = 0; + unsigned long flags; + + /* bits 31:0 for 3945 */ + pinst = priv->ucode_code.p_addr; + pdata = priv->ucode_data_backup.p_addr; + + spin_lock_irqsave(&priv->lock, flags); + rc = iwl3945_grab_nic_access(priv); + if (rc) { + spin_unlock_irqrestore(&priv->lock, flags); + return rc; + } + + /* Tell bootstrap uCode where to find image to load */ + iwl3945_write_prph(priv, BSM_DRAM_INST_PTR_REG, pinst); + iwl3945_write_prph(priv, BSM_DRAM_DATA_PTR_REG, pdata); + iwl3945_write_prph(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 */ + iwl3945_write_prph(priv, BSM_DRAM_INST_BYTECOUNT_REG, + priv->ucode_code.len | BSM_DRAM_INST_LOAD); + + iwl3945_release_nic_access(priv); + + spin_unlock_irqrestore(&priv->lock, flags); + + IWL_DEBUG_INFO("Runtime uCode pointers are set.\n"); + + return rc; +} + +/** + * iwl3945_init_alive_start - Called after REPLY_ALIVE notification received + * + * Called after REPLY_ALIVE notification received from "initialize" uCode. + * + * Tell "initialize" uCode to go ahead and load the runtime uCode. + */ +static void iwl3945_init_alive_start(struct iwl3945_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 (iwl3945_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; + } + + /* 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 (iwl3945_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); +} + + +/** + * iwl3945_alive_start - called after REPLY_ALIVE notification received + * from protocol/runtime uCode (initialization uCode's + * Alive gets handled by iwl3945_init_alive_start()). + */ +static void iwl3945_alive_start(struct iwl3945_priv *priv) +{ + int rc = 0; + int thermal_spin = 0; + u32 rfkill; + + 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 (iwl3945_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; + } + + iwl3945_clear_stations_table(priv); + + rc = iwl3945_grab_nic_access(priv); + if (rc) { + IWL_WARNING("Can not read rfkill status from adapter\n"); + return; + } + + rfkill = iwl3945_read_prph(priv, APMG_RFKILL_REG); + IWL_DEBUG_INFO("RFKILL status: 0x%x\n", rfkill); + iwl3945_release_nic_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 (iwl3945_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); + + /* After the ALIVE response, we can send commands to 3945 uCode */ + set_bit(STATUS_ALIVE, &priv->status); + + /* Clear out the uCode error bit if it is set */ + clear_bit(STATUS_FW_ERROR, &priv->status); + + rc = iwl3945_init_channel_map(priv); + if (rc) { + IWL_ERROR("initializing regulatory failed: %d\n", rc); + return; + } + + iwl3945_init_geos(priv); + + if (iwl3945_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); + iwl3945_rate_control_register(priv->hw); + rc = ieee80211_register_hw(priv->hw); + priv->hw->conf.beacon_int = 100; + mutex_lock(&priv->mutex); + + if (rc) { + iwl3945_rate_control_unregister(priv->hw); + IWL_ERROR("Failed to register network " + "device (error %d)\n", rc); + return; + } + + priv->mac80211_registered = 1; + + iwl3945_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; + + iwl3945_send_power_mode(priv, IWL_POWER_LEVEL(priv->power_mode)); + + if (iwl3945_is_associated(priv)) { + struct iwl3945_rxon_cmd *active_rxon = + (struct iwl3945_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 */ + iwl3945_connection_init_rx_config(priv); + memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN); + } + + /* Configure Bluetooth device coexistence support */ + iwl3945_send_bt_config(priv); + + /* Configure the adapter for unassociated operation */ + iwl3945_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); + + IWL_DEBUG_INFO("ALIVE processing complete.\n"); + + if (priv->error_recovering) + iwl3945_error_recovery(priv); + + return; + + restart: + queue_work(priv->workqueue, &priv->restart); +} + +static void iwl3945_cancel_deferred_work(struct iwl3945_priv *priv); + +static void __iwl3945_down(struct iwl3945_priv *priv) +{ + unsigned long flags; + int exit_pending = test_bit(STATUS_EXIT_PENDING, &priv->status); + struct ieee80211_conf *conf = NULL; + + IWL_DEBUG_INFO(DRV_NAME " is going down\n"); + + conf = ieee80211_get_hw_conf(priv->hw); + + if (!exit_pending) + set_bit(STATUS_EXIT_PENDING, &priv->status); + + iwl3945_clear_stations_table(priv); + + /* Unblock any waiting calls */ + wake_up_interruptible_all(&priv->wait_command_queue); + + iwl3945_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 */ + iwl3945_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); + + /* tell the device to stop sending interrupts */ + iwl3945_disable_interrupts(priv); + + if (priv->mac80211_registered) + ieee80211_stop_queues(priv->hw); + + /* If we have not previously called iwl3945_init() then + * clear all bits but the RF Kill and SUSPEND bits and return */ + if (!iwl3945_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); + iwl3945_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + spin_unlock_irqrestore(&priv->lock, flags); + + iwl3945_hw_txq_ctx_stop(priv); + iwl3945_hw_rxq_stop(priv); + + spin_lock_irqsave(&priv->lock, flags); + if (!iwl3945_grab_nic_access(priv)) { + iwl3945_write_prph(priv, APMG_CLK_DIS_REG, + APMG_CLK_VAL_DMA_CLK_RQT); + iwl3945_release_nic_access(priv); + } + spin_unlock_irqrestore(&priv->lock, flags); + + udelay(5); + + iwl3945_hw_nic_stop_master(priv); + iwl3945_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); + iwl3945_hw_nic_reset(priv); + + exit: + memset(&priv->card_alive, 0, sizeof(struct iwl3945_alive_resp)); + + if (priv->ibss_beacon) + dev_kfree_skb(priv->ibss_beacon); + priv->ibss_beacon = NULL; + + /* clear out any free frames */ + iwl3945_clear_free_frames(priv); +} + +static void iwl3945_down(struct iwl3945_priv *priv) +{ + mutex_lock(&priv->mutex); + __iwl3945_down(priv); + mutex_unlock(&priv->mutex); +} + +#define MAX_HW_RESTARTS 5 + +static int __iwl3945_up(struct iwl3945_priv *priv) +{ + DECLARE_MAC_BUF(mac); + 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; + } + + iwl3945_write32(priv, CSR_INT, 0xFFFFFFFF); + + rc = iwl3945_hw_nic_init(priv); + if (rc) { + IWL_ERROR("Unable to int nic\n"); + return rc; + } + + /* make sure rfkill handshake bits are cleared */ + iwl3945_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + iwl3945_write32(priv, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + + /* clear (again), then enable host interrupts */ + iwl3945_write32(priv, CSR_INT, 0xFFFFFFFF); + iwl3945_enable_interrupts(priv); + + /* really make sure rfkill handshake bits are cleared */ + iwl3945_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + iwl3945_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); + + for (i = 0; i < MAX_HW_RESTARTS; i++) { + + iwl3945_clear_stations_table(priv); + + /* load bootstrap state machine, + * load bootstrap program into processor's memory, + * prepare to load the "initialize" uCode */ + rc = iwl3945_load_bsm(priv); + + if (rc) { + IWL_ERROR("Unable to set up bootstrap uCode: %d\n", rc); + continue; + } + + /* start card; "initialize" will load runtime ucode */ + iwl3945_nic_start(priv); + + /* MAC Address location in EEPROM is same for 3945/4965 */ + get_eeprom_mac(priv, priv->mac_addr); + IWL_DEBUG_INFO("MAC address: %s\n", + print_mac(mac, priv->mac_addr)); + + SET_IEEE80211_PERM_ADDR(priv->hw, priv->mac_addr); + + IWL_DEBUG_INFO(DRV_NAME " is coming up\n"); + + return 0; + } + + set_bit(STATUS_EXIT_PENDING, &priv->status); + __iwl3945_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 iwl3945_bg_init_alive_start(void *p) +{ + struct iwl3945_priv *priv = p; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + mutex_lock(&priv->mutex); + iwl3945_init_alive_start(priv); + mutex_unlock(&priv->mutex); +} + +static void iwl3945_bg_alive_start(void *p) +{ + struct iwl3945_priv *priv = p; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + mutex_lock(&priv->mutex); + iwl3945_alive_start(priv); + mutex_unlock(&priv->mutex); +} + +static void iwl3945_bg_rf_kill(void *p) +{ + struct iwl3945_priv *priv = p; + + wake_up_interruptible(&priv->wait_command_queue); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + mutex_lock(&priv->mutex); + + if (!iwl3945_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 iwl3945_bg_scan_check(void *p) +{ + struct iwl3945_priv *priv = p; + + 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)) + iwl3945_send_scan_abort(priv); + } + mutex_unlock(&priv->mutex); +} + +static void iwl3945_bg_request_scan(void *p) +{ + struct iwl3945_priv *priv = p; + struct iwl3945_host_cmd cmd = { + .id = REPLY_SCAN_CMD, + .len = sizeof(struct iwl3945_scan_cmd), + .meta.flags = CMD_SIZE_HUGE, + }; + int rc = 0; + struct iwl3945_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 (!iwl3945_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 (iwl3945_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 iwl3945_scan_cmd) + + IWL_MAX_SCAN_SIZE, GFP_KERNEL); + if (!priv->scan) { + rc = -ENOMEM; + goto done; + } + } + scan = priv->scan; + memset(scan, 0, sizeof(struct iwl3945_scan_cmd) + IWL_MAX_SCAN_SIZE); + + scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH; + scan->quiet_time = IWL_ACTIVE_QUIET_TIME; + + if (iwl3945_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(200 * 1024); + if (!interval) + interval = suspend_time; + /* + * 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)); + + 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", + iwl3945_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 (!iwl3945_is_associated(priv) && priv->essid_len) { + 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( + iwl3945_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 = priv->hw_setting.bcast_sta_id; + scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; + + /* flags + rate selection */ + + switch (priv->scan_bands) { + case 2: + scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK; + scan->tx_cmd.rate = IWL_RATE_1M_PLCP; + scan->good_CRC_th = 0; + phymode = MODE_IEEE80211G; + break; + + case 1: + scan->tx_cmd.rate = IWL_RATE_6M_PLCP; + 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 */ + scan->flags |= iwl3945_get_antenna_flags(priv); + + 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", + iwl3945_escape_essid(priv->essid, priv->essid_len)); + else + IWL_DEBUG_SCAN("Initiating indirect scan.\n"); + + scan->channel_count = + iwl3945_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 iwl3945_scan_channel); + cmd.data = scan; + scan->len = cpu_to_le16(cmd.len); + + set_bit(STATUS_SCAN_HW, &priv->status); + rc = iwl3945_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 scan aborted */ + queue_work(priv->workqueue, &priv->scan_completed); + mutex_unlock(&priv->mutex); +} + +static void iwl3945_bg_up(void *p) +{ + struct iwl3945_priv *priv = p; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + mutex_lock(&priv->mutex); + __iwl3945_up(priv); + mutex_unlock(&priv->mutex); +} + +static void iwl3945_bg_restart(void *p) +{ + struct iwl3945_priv *priv = p; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + iwl3945_down(priv); + queue_work(priv->workqueue, &priv->up); +} + +static void iwl3945_bg_rx_replenish(void *p) +{ + struct iwl3945_priv *priv = p; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + mutex_lock(&priv->mutex); + iwl3945_rx_replenish(priv); + mutex_unlock(&priv->mutex); +} + +#define IWL_DELAY_NEXT_SCAN (HZ*2) + +static void iwl3945_bg_post_associate(void *p) +{ + struct iwl3945_priv *priv = p; + + int rc = 0; + struct ieee80211_conf *conf = NULL; + DECLARE_MAC_BUF(mac); + + if (priv->iw_mode == IEEE80211_IF_TYPE_AP) { + IWL_ERROR("%s Should not be called in AP mode\n", __FUNCTION__); + return; + } + + + IWL_DEBUG_ASSOC("Associated as %d to: %s\n", + priv->assoc_id, + print_mac(mac, priv->active_rxon.bssid_addr)); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + mutex_lock(&priv->mutex); + + if (!priv->interface_id || !priv->is_open) { + mutex_unlock(&priv->mutex); + return; + } + iwl3945_scan_cancel_timeout(priv, 200); + + conf = ieee80211_get_hw_conf(priv->hw); + + priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + iwl3945_commit_rxon(priv); + + memset(&priv->rxon_timing, 0, sizeof(struct iwl3945_rxon_time_cmd)); + iwl3945_setup_rxon_timing(priv); + rc = iwl3945_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; + + 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; + + } + + iwl3945_commit_rxon(priv); + + switch (priv->iw_mode) { + case IEEE80211_IF_TYPE_STA: + iwl3945_rate_scale_init(priv->hw, IWL_AP_ID); + break; + + case IEEE80211_IF_TYPE_IBSS: + + /* clear out the station table */ + iwl3945_clear_stations_table(priv); + + iwl3945_add_station(priv, iwl3945_broadcast_addr, 0, 0); + iwl3945_add_station(priv, priv->bssid, 0, 0); + iwl3945_sync_sta(priv, IWL_STA_ID, + (priv->phymode == MODE_IEEE80211A)? + IWL_RATE_6M_PLCP : IWL_RATE_1M_PLCP, + CMD_ASYNC); + iwl3945_rate_scale_init(priv->hw, IWL_STA_ID); + iwl3945_send_beacon_cmd(priv); + + break; + + default: + IWL_ERROR("%s Should not be called in %d mode\n", + __FUNCTION__, priv->iw_mode); + break; + } + + iwl3945_sequence_reset(priv); + +#ifdef CONFIG_IWL3945_QOS + iwl3945_activate_qos(priv, 0); +#endif /* CONFIG_IWL3945_QOS */ + /* we have just associated, don't start scan too early */ + priv->next_scan_jiffies = jiffies + IWL_DELAY_NEXT_SCAN; + mutex_unlock(&priv->mutex); +} + +static void iwl3945_bg_abort_scan(void *p) +{ + struct iwl3945_priv *priv = p; + + if (!iwl3945_is_ready(priv)) + return; + + mutex_lock(&priv->mutex); + + set_bit(STATUS_SCAN_ABORTING, &priv->status); + iwl3945_send_scan_abort(priv); + + mutex_unlock(&priv->mutex); +} + +static int iwl3945_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf); + +static void iwl3945_bg_scan_completed(void *p) +{ + struct iwl3945_priv *priv = p; + + IWL_DEBUG(IWL_DL_INFO | IWL_DL_SCAN, "SCAN complete scan\n"); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (priv->cache_conf) + iwl3945_mac_config(priv->hw, priv->cache_conf); + + ieee80211_scan_completed(priv->hw); + + /* Since setting the TXPOWER may have been deferred while + * performing the scan, fire one off */ + mutex_lock(&priv->mutex); + iwl3945_hw_reg_send_txpower(priv); + mutex_unlock(&priv->mutex); +} + +/***************************************************************************** + * + * mac80211 entry point functions + * + *****************************************************************************/ + +static int iwl3945_mac_start(struct ieee80211_hw *hw) +{ + struct iwl3945_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 (!iwl3945_is_rfkill(priv)) + ieee80211_start_queues(priv->hw); + + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211("leave\n"); + return 0; +} + +static void iwl3945_mac_stop(struct ieee80211_hw *hw) +{ + struct iwl3945_priv *priv = hw->priv; + + IWL_DEBUG_MAC80211("enter\n"); + + + mutex_lock(&priv->mutex); + /* stop mac, cancel any scan request and clear + * RXON_FILTER_ASSOC_MSK BIT + */ + priv->is_open = 0; + if (!iwl3945_is_ready_rf(priv)) { + IWL_DEBUG_MAC80211("leave - RF not ready\n"); + mutex_unlock(&priv->mutex); + return; + } + + iwl3945_scan_cancel_timeout(priv, 100); + cancel_delayed_work(&priv->post_associate); + priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + iwl3945_commit_rxon(priv); + mutex_unlock(&priv->mutex); + + IWL_DEBUG_MAC80211("leave\n"); +} + +static int iwl3945_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_tx_control *ctl) +{ + struct iwl3945_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 (iwl3945_tx_skb(priv, skb, ctl)) + dev_kfree_skb_any(skb); + + IWL_DEBUG_MAC80211("leave\n"); + return 0; +} + +static int iwl3945_mac_add_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct iwl3945_priv *priv = hw->priv; + unsigned long flags; + DECLARE_MAC_BUF(mac); + + IWL_DEBUG_MAC80211("enter: id %d, type %d\n", conf->if_id, conf->type); + + if (priv->interface_id) { + IWL_DEBUG_MAC80211("leave - interface_id != 0\n"); + return -EOPNOTSUPP; + } + + spin_lock_irqsave(&priv->lock, flags); + priv->interface_id = conf->if_id; + + spin_unlock_irqrestore(&priv->lock, flags); + + mutex_lock(&priv->mutex); + + if (conf->mac_addr) { + IWL_DEBUG_MAC80211("Set: %s\n", print_mac(mac, conf->mac_addr)); + memcpy(priv->mac_addr, conf->mac_addr, ETH_ALEN); + } + + iwl3945_set_mode(priv, conf->type); + + IWL_DEBUG_MAC80211("leave\n"); + mutex_unlock(&priv->mutex); + + return 0; +} + +/** + * iwl3945_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 iwl3945_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf) +{ + struct iwl3945_priv *priv = hw->priv; + const struct iwl3945_channel_info *ch_info; + unsigned long flags; + int ret = 0; + + mutex_lock(&priv->mutex); + IWL_DEBUG_MAC80211("enter to channel %d\n", conf->channel); + + if (!iwl3945_is_ready(priv)) { + IWL_DEBUG_MAC80211("leave - not ready\n"); + ret = -EIO; + goto out; + } + + /* TODO: Figure out how to get ieee80211_local->sta_scanning w/ only + * what is exposed through include/ declarations */ + if (unlikely(!iwl3945_param_disable_hw_scan && + test_bit(STATUS_SCANNING, &priv->status))) { + + if (priv->cache_conf) + IWL_DEBUG_MAC80211("leave - still scanning\n"); + else { + /* Cache the configuration now so that we can + * replay it after the hardware scan is finished. */ + priv->cache_conf = kmalloc(sizeof(*conf), GFP_KERNEL); + if (priv->cache_conf) { + memcpy(priv->cache_conf, conf, sizeof(*conf)); + IWL_DEBUG_MAC80211("leave - scanning\n"); + } else { + IWL_DEBUG_MAC80211("leave - no memory\n"); + ret = -ENOMEM; + } + } + mutex_unlock(&priv->mutex); + return ret; + } + + spin_lock_irqsave(&priv->lock, flags); + + ch_info = iwl3945_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); + ret = -EINVAL; + goto out; + } + + iwl3945_set_rxon_channel(priv, conf->phymode, conf->channel); + + iwl3945_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 */ + iwl3945_set_rate(priv); + + spin_unlock_irqrestore(&priv->lock, flags); + +#ifdef IEEE80211_CONF_CHANNEL_SWITCH + if (conf->flags & IEEE80211_CONF_CHANNEL_SWITCH) { + iwl3945_hw_channel_switch(priv, conf->channel); + goto out; + } +#endif + + iwl3945_radio_kill_sw(priv, !conf->radio_enabled); + + if (!conf->radio_enabled) { + IWL_DEBUG_MAC80211("leave - radio disabled\n"); + goto out; + } + + if (iwl3945_is_rfkill(priv)) { + IWL_DEBUG_MAC80211("leave - RF kill\n"); + ret = -EIO; + goto out; + } + + iwl3945_set_rate(priv); + + if (memcmp(&priv->active_rxon, + &priv->staging_rxon, sizeof(priv->staging_rxon))) + iwl3945_commit_rxon(priv); + else + IWL_DEBUG_INFO("No re-sending same RXON configuration.\n"); + + IWL_DEBUG_MAC80211("leave\n"); + +out: + if (priv->cache_conf) { + kfree(priv->cache_conf); + priv->cache_conf = NULL; + } + mutex_unlock(&priv->mutex); + return ret; +} + +static void iwl3945_config_ap(struct iwl3945_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; + iwl3945_commit_rxon(priv); + + /* RXON Timing */ + memset(&priv->rxon_timing, 0, sizeof(struct iwl3945_rxon_time_cmd)); + iwl3945_setup_rxon_timing(priv); + rc = iwl3945_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"); + + /* 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; + iwl3945_commit_rxon(priv); + iwl3945_add_station(priv, iwl3945_broadcast_addr, 0, 0); + } + iwl3945_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 iwl3945_mac_config_interface(struct ieee80211_hw *hw, int if_id, + struct ieee80211_if_conf *conf) +{ + struct iwl3945_priv *priv = hw->priv; + DECLARE_MAC_BUF(mac); + unsigned long flags; + int rc; + + if (conf == NULL) + return -EIO; + + /* XXX: this MUST use conf->mac_addr */ + + 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: %s\n", + print_mac(mac, conf->bssid)); + +/* + * very dubious code was here; the probe filtering flag is never set: + * + if (unlikely(test_bit(STATUS_SCANNING, &priv->status)) && + !(priv->hw->flags & IEEE80211_HW_NO_PROBE_FILTERING)) { + */ + if (unlikely(test_bit(STATUS_SCANNING, &priv->status))) { + 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: %s\n", + print_mac(mac, conf->bssid)); + } + if (priv->ibss_beacon) + dev_kfree_skb(priv->ibss_beacon); + + priv->ibss_beacon = conf->beacon; + } + + if (iwl3945_is_rfkill(priv)) + goto done; + + 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 (iwl3945_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) + iwl3945_config_ap(priv); + else { + rc = iwl3945_commit_rxon(priv); + if ((priv->iw_mode == IEEE80211_IF_TYPE_STA) && rc) + iwl3945_add_station(priv, + priv->active_rxon.bssid_addr, 1, 0); + } + + } else { + iwl3945_scan_cancel_timeout(priv, 100); + priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + iwl3945_commit_rxon(priv); + } + + done: + 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 iwl3945_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + int mc_count, struct dev_addr_list *mc_list) +{ + /* + * XXX: dummy + * see also iwl3945_connection_init_rx_config + */ + *total_flags = 0; +} + +static void iwl3945_mac_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct iwl3945_priv *priv = hw->priv; + + IWL_DEBUG_MAC80211("enter\n"); + + mutex_lock(&priv->mutex); + + if (iwl3945_is_ready_rf(priv)) { + iwl3945_scan_cancel_timeout(priv, 100); + cancel_delayed_work(&priv->post_associate); + priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + iwl3945_commit_rxon(priv); + } + 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"); + +} + +static int iwl3945_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t len) +{ + int rc = 0; + unsigned long flags; + struct iwl3945_priv *priv = hw->priv; + + IWL_DEBUG_MAC80211("enter\n"); + + mutex_lock(&priv->mutex); + spin_lock_irqsave(&priv->lock, flags); + + if (!iwl3945_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; + } + + /* we don't schedule scan within next_scan_jiffies period */ + if (priv->next_scan_jiffies && + time_after(priv->next_scan_jiffies, jiffies)) { + rc = -EAGAIN; + 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 ", + iwl3945_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); + } else + priv->one_direct_scan = 0; + + rc = iwl3945_scan_initiate(priv); + + IWL_DEBUG_MAC80211("leave\n"); + +out_unlock: + spin_unlock_irqrestore(&priv->lock, flags); + mutex_unlock(&priv->mutex); + + return rc; +} + +static int iwl3945_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + const u8 *local_addr, const u8 *addr, + struct ieee80211_key_conf *key) +{ + struct iwl3945_priv *priv = hw->priv; + int rc = 0; + u8 sta_id; + + IWL_DEBUG_MAC80211("enter\n"); + + if (!iwl3945_param_hwcrypto) { + IWL_DEBUG_MAC80211("leave - hwcrypto disabled\n"); + return -EOPNOTSUPP; + } + + if (is_zero_ether_addr(addr)) + /* only support pairwise keys */ + return -EOPNOTSUPP; + + sta_id = iwl3945_hw_find_station(priv, addr); + if (sta_id == IWL_INVALID_STATION) { + DECLARE_MAC_BUF(mac); + + IWL_DEBUG_MAC80211("leave - %s not in station map.\n", + print_mac(mac, addr)); + return -EINVAL; + } + + mutex_lock(&priv->mutex); + + iwl3945_scan_cancel_timeout(priv, 100); + + switch (cmd) { + case SET_KEY: + rc = iwl3945_update_sta_key_info(priv, key, sta_id); + if (!rc) { + iwl3945_set_rxon_hwcrypto(priv, 1); + iwl3945_commit_rxon(priv); + key->hw_key_idx = sta_id; + IWL_DEBUG_MAC80211("set_key success, using hwcrypto\n"); + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + } + break; + case DISABLE_KEY: + rc = iwl3945_clear_sta_key_info(priv, sta_id); + if (!rc) { + iwl3945_set_rxon_hwcrypto(priv, 0); + iwl3945_commit_rxon(priv); + IWL_DEBUG_MAC80211("disable hwcrypto key\n"); + } + break; + default: + rc = -EINVAL; + } + + IWL_DEBUG_MAC80211("leave\n"); + mutex_unlock(&priv->mutex); + + return rc; +} + +static int iwl3945_mac_conf_tx(struct ieee80211_hw *hw, int queue, + const struct ieee80211_tx_queue_params *params) +{ + struct iwl3945_priv *priv = hw->priv; +#ifdef CONFIG_IWL3945_QOS + unsigned long flags; + int q; +#endif /* CONFIG_IWL3945_QOS */ + + IWL_DEBUG_MAC80211("enter\n"); + + if (!iwl3945_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_IWL3945_QOS + if (!priv->qos_data.qos_enable) { + priv->qos_data.qos_active = 0; + IWL_DEBUG_MAC80211("leave - qos not enabled\n"); + return 0; + } + q = AC_NUM - 1 - queue; + + spin_lock_irqsave(&priv->lock, flags); + + priv->qos_data.def_qos_parm.ac[q].cw_min = cpu_to_le16(params->cw_min); + priv->qos_data.def_qos_parm.ac[q].cw_max = cpu_to_le16(params->cw_max); + priv->qos_data.def_qos_parm.ac[q].aifsn = params->aifs; + priv->qos_data.def_qos_parm.ac[q].edca_txop = + cpu_to_le16((params->burst_time * 100)); + + priv->qos_data.def_qos_parm.ac[q].reserved1 = 0; + priv->qos_data.qos_active = 1; + + spin_unlock_irqrestore(&priv->lock, flags); + + mutex_lock(&priv->mutex); + if (priv->iw_mode == IEEE80211_IF_TYPE_AP) + iwl3945_activate_qos(priv, 1); + else if (priv->assoc_id && iwl3945_is_associated(priv)) + iwl3945_activate_qos(priv, 0); + + mutex_unlock(&priv->mutex); + +#endif /*CONFIG_IWL3945_QOS */ + + IWL_DEBUG_MAC80211("leave\n"); + return 0; +} + +static int iwl3945_mac_get_tx_stats(struct ieee80211_hw *hw, + struct ieee80211_tx_queue_stats *stats) +{ + struct iwl3945_priv *priv = hw->priv; + int i, avail; + struct iwl3945_tx_queue *txq; + struct iwl3945_queue *q; + unsigned long flags; + + IWL_DEBUG_MAC80211("enter\n"); + + if (!iwl3945_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 = iwl3945_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 iwl3945_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 iwl3945_mac_get_tsf(struct ieee80211_hw *hw) +{ + IWL_DEBUG_MAC80211("enter\n"); + IWL_DEBUG_MAC80211("leave\n"); + + return 0; +} + +static void iwl3945_mac_reset_tsf(struct ieee80211_hw *hw) +{ + struct iwl3945_priv *priv = hw->priv; + unsigned long flags; + + mutex_lock(&priv->mutex); + IWL_DEBUG_MAC80211("enter\n"); + +#ifdef CONFIG_IWL3945_QOS + iwl3945_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); + + if (!iwl3945_is_ready_rf(priv)) { + IWL_DEBUG_MAC80211("leave - not ready\n"); + mutex_unlock(&priv->mutex); + return; + } + + /* we are restarting association process + * clear RXON_FILTER_ASSOC_MSK bit + */ + if (priv->iw_mode != IEEE80211_IF_TYPE_AP) { + iwl3945_scan_cancel_timeout(priv, 100); + priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + iwl3945_commit_rxon(priv); + } + + /* 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; + } + + priv->only_active_channel = 0; + + iwl3945_set_rate(priv); + + mutex_unlock(&priv->mutex); + + IWL_DEBUG_MAC80211("leave\n"); + +} + +static int iwl3945_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_tx_control *control) +{ + struct iwl3945_priv *priv = hw->priv; + unsigned long flags; + + mutex_lock(&priv->mutex); + IWL_DEBUG_MAC80211("enter\n"); + + if (!iwl3945_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_IWL3945_QOS + iwl3945_reset_qos(priv); +#endif + + queue_work(priv->workqueue, &priv->post_associate); + + mutex_unlock(&priv->mutex); + + return 0; +} + +/***************************************************************************** + * + * sysfs attributes + * + *****************************************************************************/ + +#ifdef CONFIG_IWL3945_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/iwl/) + * used for controlling the debug level. + * + * See the level definitions in iwl for details. + */ + +static ssize_t show_debug_level(struct device_driver *d, char *buf) +{ + return sprintf(buf, "0x%08X\n", iwl3945_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 + iwl3945_debug_level = val; + + return strnlen(buf, count); +} + +static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO, + show_debug_level, store_debug_level); + +#endif /* CONFIG_IWL3945_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 iwl3945_priv *priv = (struct iwl3945_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 iwl3945_priv *priv = (struct iwl3945_priv *)d->driver_data; + + mutex_lock(&priv->mutex); + iwl3945_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 iwl3945_priv *priv = (struct iwl3945_priv *)d->driver_data; + + if (!iwl3945_is_alive(priv)) + return -EAGAIN; + + return sprintf(buf, "%d\n", iwl3945_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 iwl3945_priv *priv = d->driver_data; + return iwl3945_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 iwl3945_priv *priv = (struct iwl3945_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 iwl3945_priv *priv = (struct iwl3945_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 + iwl3945_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 iwl3945_priv *priv = (struct iwl3945_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 iwl3945_priv *priv = (struct iwl3945_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 (iwl3945_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); + iwl3945_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 iwl3945_priv *priv = (struct iwl3945_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 iwl3945_priv *priv = (struct iwl3945_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 (iwl3945_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); + iwl3945_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 iwl3945_priv *priv = (struct iwl3945_priv *)d->driver_data; + + return sprintf(buf, "0x%04X\n", + (priv->phymode << 8) | + le16_to_cpu(priv->active_rxon.channel)); +} + +static void iwl3945_set_flags_for_phymode(struct iwl3945_priv *priv, u8 phymode); + +static ssize_t store_tune(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct iwl3945_priv *priv = (struct iwl3945_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 iwl3945_channel_info *ch_info; + + ch_info = iwl3945_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 (iwl3945_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); + + iwl3945_set_rxon_channel(priv, phymode, channel); + iwl3945_set_flags_for_phymode(priv, phymode); + + iwl3945_set_rate(priv); + iwl3945_commit_rxon(priv); + } + } + mutex_unlock(&priv->mutex); + + return count; +} + +static DEVICE_ATTR(tune, S_IWUSR | S_IRUGO, show_tune, store_tune); + +#ifdef CONFIG_IWL3945_SPECTRUM_MEASUREMENT + +static ssize_t show_measurement(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct iwl3945_priv *priv = dev_get_drvdata(d); + struct iwl3945_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 iwl3945_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); + iwl3945_get_measurement(priv, ¶ms, type); + + return count; +} + +static DEVICE_ATTR(measurement, S_IRUSR | S_IWUSR, + show_measurement, store_measurement); +#endif /* CONFIG_IWL3945_SPECTRUM_MEASUREMENT */ + +static ssize_t show_rate(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct iwl3945_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 = iwl3945_rate_index_from_plcp(i); + if (i == -1) + return sprintf(buf, "0\n"); + + return sprintf(buf, "%d%s\n", + (iwl3945_rates[i].ieee >> 1), + (iwl3945_rates[i].ieee & 0x1) ? ".5" : ""); +} + +static DEVICE_ATTR(rate, S_IRUSR, show_rate, NULL); + +static ssize_t store_retry_rate(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct iwl3945_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 iwl3945_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 iwl3945_priv *priv = dev_get_drvdata(d); + int rc; + int mode; + + mode = simple_strtoul(buf, NULL, 0); + mutex_lock(&priv->mutex); + + if (!iwl3945_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 = iwl3945_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 iwl3945_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 iwl3945_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 (!iwl3945_is_ready(priv)) + return -EAGAIN; + + hw_mode = iwl3945_get_hw_mode(priv, MODE_IEEE80211G); + if (!hw_mode) + hw_mode = iwl3945_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 = iwl3945_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 iwl3945_priv *priv = dev_get_drvdata(d); + u32 size = sizeof(struct iwl3945_notif_statistics); + u32 len = 0, ofs = 0; + u8 *data = (u8 *) & priv->statistics; + int rc = 0; + + if (!iwl3945_is_alive(priv)) + return -EAGAIN; + + mutex_lock(&priv->mutex); + rc = iwl3945_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 iwl3945_priv *priv = dev_get_drvdata(d); + + if (!iwl3945_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 iwl3945_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 iwl3945_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 iwl3945_priv *priv = (struct iwl3945_priv *)d->driver_data; + if (!iwl3945_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') + iwl3945_dump_nic_error_log((struct iwl3945_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') + iwl3945_dump_nic_event_log((struct iwl3945_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 iwl3945_setup_deferred_work(struct iwl3945_priv *priv) +{ + priv->workqueue = create_workqueue(DRV_NAME); + + init_waitqueue_head(&priv->wait_command_queue); + + INIT_WORK(&priv->up, iwl3945_bg_up, priv); + INIT_WORK(&priv->restart, iwl3945_bg_restart, priv); + INIT_WORK(&priv->rx_replenish, iwl3945_bg_rx_replenish, priv); + INIT_WORK(&priv->scan_completed, iwl3945_bg_scan_completed, priv); + INIT_WORK(&priv->request_scan, iwl3945_bg_request_scan, priv); + INIT_WORK(&priv->abort_scan, iwl3945_bg_abort_scan, priv); + INIT_WORK(&priv->rf_kill, iwl3945_bg_rf_kill, priv); + INIT_WORK(&priv->beacon_update, iwl3945_bg_beacon_update, priv); + INIT_WORK(&priv->post_associate, iwl3945_bg_post_associate, priv); + INIT_WORK(&priv->init_alive_start, iwl3945_bg_init_alive_start, priv); + INIT_WORK(&priv->alive_start, iwl3945_bg_alive_start, priv); + INIT_WORK(&priv->scan_check, iwl3945_bg_scan_check, priv); + + iwl3945_hw_setup_deferred_work(priv); + + tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) + iwl3945_irq_tasklet, (unsigned long)priv); +} + +static void iwl3945_cancel_deferred_work(struct iwl3945_priv *priv) +{ + iwl3945_hw_cancel_deferred_work(priv); + +#if 0 /* Not in RHEL5...(and probably wrong upstream...) */ + cancel_delayed_work_sync(&priv->init_alive_start); +#else + cancel_delayed_work(&priv->init_alive_start); + /* Make sure he has completed, since he can schedule other work */ + flush_workqueue(priv->workqueue); +#endif + cancel_delayed_work(&priv->scan_check); + cancel_delayed_work(&priv->alive_start); + cancel_delayed_work(&priv->post_associate); +#if 0 /* Not in RHEL5... */ + cancel_work_sync(&priv->beacon_update); +#else + flush_workqueue(priv->workqueue); +#endif +} + +static struct attribute *iwl3945_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_IWL3945_SPECTRUM_MEASUREMENT + &dev_attr_measurement.attr, +#endif + &dev_attr_power_level.attr, + &dev_attr_rate.attr, + &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 iwl3945_attribute_group = { + .name = NULL, /* put in device directory */ + .attrs = iwl3945_sysfs_entries, +}; + +static struct ieee80211_ops iwl3945_hw_ops = { + .tx = iwl3945_mac_tx, + .start = iwl3945_mac_start, + .stop = iwl3945_mac_stop, + .add_interface = iwl3945_mac_add_interface, + .remove_interface = iwl3945_mac_remove_interface, + .config = iwl3945_mac_config, + .config_interface = iwl3945_mac_config_interface, + .configure_filter = iwl3945_configure_filter, + .set_key = iwl3945_mac_set_key, + .get_stats = iwl3945_mac_get_stats, + .get_tx_stats = iwl3945_mac_get_tx_stats, + .conf_tx = iwl3945_mac_conf_tx, + .get_tsf = iwl3945_mac_get_tsf, + .reset_tsf = iwl3945_mac_reset_tsf, + .beacon_update = iwl3945_mac_beacon_update, + .hw_scan = iwl3945_mac_hw_scan +}; + +static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int err = 0; + u32 pci_id; + struct iwl3945_priv *priv; + struct ieee80211_hw *hw; + int i; + + /* Disabling hardware scan means that mac80211 will perform scans + * "the hard way", rather than using device's scan. */ + if (iwl3945_param_disable_hw_scan) { + IWL_DEBUG_INFO("Disabling hw_scan\n"); + iwl3945_hw_ops.hw_scan = NULL; + } + + if ((iwl3945_param_queues_num > IWL_MAX_NUM_QUEUES) || + (iwl3945_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 iwl3945_priv), &iwl3945_hw_ops); + if (hw == NULL) { + IWL_ERROR("Can not allocate network device\n"); + err = -ENOMEM; + goto out; + } + SET_IEEE80211_DEV(hw, &pdev->dev); + + hw->rate_control_algorithm = "iwl-3945-rs"; + + IWL_DEBUG_INFO("*** LOAD DRIVER ***\n"); + priv = hw->priv; + priv->hw = hw; + + priv->pci_dev = pdev; + + /* Select antenna (may be helpful if only one antenna is connected) */ + priv->antenna = (enum iwl3945_antenna)iwl3945_param_antenna; +#ifdef CONFIG_IWL3945_DEBUG + iwl3945_debug_level = iwl3945_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_HOST_GEN_BEACON_TEMPLATE; + + /* 4 EDCA QOS priorities */ + hw->queues = 4; + + spin_lock_init(&priv->lock); + spin_lock_init(&priv->power_data.lock); + spin_lock_init(&priv->sta_lock); + spin_lock_init(&priv->hcmd_lock); + + 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); + + /* Clear the driver's (not device's) station table */ + iwl3945_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 */ + + /* Disable radio (SW RF KILL) via parameter when loading driver */ + if (iwl3945_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; + + 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" : ""); + + /* Device-specific setup */ + if (iwl3945_hw_set_hw_setting(priv)) { + IWL_ERROR("failed to set hw settings\n"); + mutex_unlock(&priv->mutex); + goto out_iounmap; + } + +#ifdef CONFIG_IWL3945_QOS + if (iwl3945_param_qos_enable) + priv->qos_data.qos_enable = 1; + + iwl3945_reset_qos(priv); + + priv->qos_data.qos_active = 0; + priv->qos_data.qos_cap.val = 0; +#endif /* CONFIG_IWL3945_QOS */ + + iwl3945_set_rxon_channel(priv, MODE_IEEE80211G, 6); + iwl3945_setup_deferred_work(priv); + iwl3945_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; + + iwl3945_disable_interrupts(priv); + + pci_enable_msi(pdev); + + err = request_irq(pdev->irq, iwl3945_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, &iwl3945_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 = iwl3945_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("Queueing UP work.\n"); + + queue_work(priv->workqueue, &priv->up); + + return 0; + + out_pci_alloc: + iwl3945_dealloc_ucode_pci(priv); + + sysfs_remove_group(&pdev->dev.kobj, &iwl3945_attribute_group); + + out_release_irq: + free_irq(pdev->irq, priv); + + out_disable_msi: + pci_disable_msi(pdev); + destroy_workqueue(priv->workqueue); + priv->workqueue = NULL; + iwl3945_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 iwl3945_pci_remove(struct pci_dev *pdev) +{ + struct iwl3945_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); + __iwl3945_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 iwl3945_ibss_seq, list)); + } + } + + sysfs_remove_group(&pdev->dev.kobj, &iwl3945_attribute_group); + + iwl3945_dealloc_ucode_pci(priv); + + if (priv->rxq.bd) + iwl3945_rx_queue_free(priv, &priv->rxq); + iwl3945_hw_txq_ctx_free(priv); + + iwl3945_unset_hw_setting(priv); + iwl3945_clear_stations_table(priv); + + if (priv->mac80211_registered) { + ieee80211_unregister_hw(priv->hw); + iwl3945_rate_control_unregister(priv->hw); + } + + /*netif_stop_queue(dev); */ + flush_workqueue(priv->workqueue); + + /* ieee80211_unregister_hw calls iwl3945_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 iwl3945_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct iwl3945_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. */ + __iwl3945_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 iwl3945_resume(struct iwl3945_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 iwl3945_down to be + * called *after* iwl3945_hw_nic_init() has succeeded -- which + * then lets the next init sequence succeed. So, we've + * replicated all of that NIC init code here... */ + + iwl3945_write32(priv, CSR_INT, 0xFFFFFFFF); + + iwl3945_hw_nic_init(priv); + + iwl3945_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + iwl3945_write32(priv, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + iwl3945_write32(priv, CSR_INT, 0xFFFFFFFF); + iwl3945_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + iwl3945_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + + /* tell the device to stop sending interrupts */ + iwl3945_disable_interrupts(priv); + + spin_lock_irqsave(&priv->lock, flags); + iwl3945_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + + if (!iwl3945_grab_nic_access(priv)) { + iwl3945_write_prph(priv, APMG_CLK_DIS_REG, + APMG_CLK_VAL_DMA_CLK_RQT); + iwl3945_release_nic_access(priv); + } + spin_unlock_irqrestore(&priv->lock, flags); + + udelay(5); + + iwl3945_hw_nic_reset(priv); + + /* Bring the device back up */ + clear_bit(STATUS_IN_SUSPEND, &priv->status); + queue_work(priv->workqueue, &priv->up); +} + +static int iwl3945_pci_resume(struct pci_dev *pdev) +{ + struct iwl3945_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); + + iwl3945_resume(priv); + mutex_unlock(&priv->mutex); + + return 0; +} + +#endif /* CONFIG_PM */ + +/***************************************************************************** + * + * driver and module entry point + * + *****************************************************************************/ + +static struct pci_driver iwl3945_driver = { + .name = DRV_NAME, + .id_table = iwl3945_hw_card_ids, + .probe = iwl3945_pci_probe, + .remove = __devexit_p(iwl3945_pci_remove), +#ifdef CONFIG_PM + .suspend = iwl3945_pci_suspend, + .resume = iwl3945_pci_resume, +#endif +}; + +static int __init iwl3945_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(&iwl3945_driver); + if (ret) { + IWL_ERROR("Unable to initialize PCI module\n"); + return ret; + } +#ifdef CONFIG_IWL3945_DEBUG + ret = driver_create_file(&iwl3945_driver.driver, &driver_attr_debug_level); + if (ret) { + IWL_ERROR("Unable to create driver sysfs file\n"); + pci_unregister_driver(&iwl3945_driver); + return ret; + } +#endif + + return ret; +} + +static void __exit iwl3945_exit(void) +{ +#ifdef CONFIG_IWL3945_DEBUG + driver_remove_file(&iwl3945_driver.driver, &driver_attr_debug_level); +#endif + pci_unregister_driver(&iwl3945_driver); +} + +module_param_named(antenna, iwl3945_param_antenna, int, 0444); +MODULE_PARM_DESC(antenna, "select antenna (1=Main, 2=Aux, default 0 [both])"); +module_param_named(disable, iwl3945_param_disable, int, 0444); +MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])"); +module_param_named(hwcrypto, iwl3945_param_hwcrypto, int, 0444); +MODULE_PARM_DESC(hwcrypto, + "using hardware crypto engine (default 0 [software])\n"); +module_param_named(debug, iwl3945_param_debug, int, 0444); +MODULE_PARM_DESC(debug, "debug output mask"); +module_param_named(disable_hw_scan, iwl3945_param_disable_hw_scan, int, 0444); +MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 0)"); + +module_param_named(queues_num, iwl3945_param_queues_num, int, 0444); +MODULE_PARM_DESC(queues_num, "number of hw queues."); + +/* QoS */ +module_param_named(qos_enable, iwl3945_param_qos_enable, int, 0444); +MODULE_PARM_DESC(qos_enable, "enable all QoS functionality"); + +module_exit(iwl3945_exit); +module_init(iwl3945_init); diff --git a/drivers/net/wireless/iwlwifi/iwl4965-base.c b/drivers/net/wireless/iwlwifi/iwl4965-base.c new file mode 100644 index 0000000..0466f61 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl4965-base.c @@ -0,0 +1,9538 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2007 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * James P. Ketrenos <ipw2100-admin@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/version.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/dma-mapping.h> +#include <linux/delay.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/wireless.h> +#include <linux/firmware.h> +#include <linux/etherdevice.h> +#include <linux/if_arp.h> + +#include <net/ieee80211_radiotap.h> +#include <net/mac80211.h> + +#include <asm/div64.h> + +#include "iwl-4965.h" +#include "iwl-helpers.h" + +#ifdef CONFIG_IWL4965_DEBUG +u32 iwl4965_debug_level; +#endif + +static int iwl4965_tx_queue_update_write_ptr(struct iwl4965_priv *priv, + struct iwl4965_tx_queue *txq); + +/****************************************************************************** + * + * module boiler plate + * + ******************************************************************************/ + +/* module parameters */ +static int iwl4965_param_disable_hw_scan; /* def: 0 = use 4965's h/w scan */ +static int iwl4965_param_debug; /* def: 0 = minimal debug log messages */ +static int iwl4965_param_disable; /* def: enable radio */ +static int iwl4965_param_antenna; /* def: 0 = both antennas (use diversity) */ +int iwl4965_param_hwcrypto; /* def: using software encryption */ +static int iwl4965_param_qos_enable = 1; /* def: 1 = use quality of service */ +int iwl4965_param_queues_num = IWL_MAX_NUM_QUEUES; /* def: 16 Tx queues */ +int iwl4965_param_amsdu_size_8K; /* def: enable 8K amsdu size */ + +/* + * module name, copyright, version, etc. + * NOTE: DRV_NAME is defined in iwlwifi.h for use by iwl-debug.h and printk + */ + +#define DRV_DESCRIPTION "Intel(R) Wireless WiFi Link 4965AGN driver for Linux" + +#ifdef CONFIG_IWL4965_DEBUG +#define VD "d" +#else +#define VD +#endif + +#ifdef CONFIG_IWL4965_SPECTRUM_MEASUREMENT +#define VS "s" +#else +#define VS +#endif + +#define IWLWIFI_VERSION "1.2.22k" 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 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 *iwl4965_get_hw_mode( + struct iwl4965_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 iwl4965_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 *iwl4965_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 (iwl4965_is_empty_essid(essid, essid_len)) { + memcpy(escaped, "<hidden>", sizeof("<hidden>")); + return escaped; + } + + essid_len = min(essid_len, (u8) IW_ESSID_MAX_SIZE); + while (essid_len--) { + if (*s == '\0') { + *d++ = '\\'; + *d++ = '0'; + s++; + } else + *d++ = *s++; + } + *d = '\0'; + return escaped; +} + +static void iwl4965_print_hex_dump(int level, void *p, u32 len) +{ +#ifdef CONFIG_IWL4965_DEBUG + if (!(iwl4965_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 Tx or Rx queue resides in host DRAM, and is comprised of a circular buffer + * of buffer descriptors, each of which points to one or more data buffers for + * the device to read from or fill. Driver and device exchange status of each + * queue via "read" and "write" pointers. Driver keeps minimum of 2 empty + * entries in each circular buffer, to protect against confusing empty and full + * queue states. + * + * The device reads or writes the data in the queues via the device's several + * DMA/FIFO channels. Each queue is mapped to a single DMA channel. + * + * 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 4965 operates with up to 17 queues: One receive queue, one transmit + * queue (#4) for sending commands to the device firmware, and 15 other + * Tx queues that may be mapped to prioritized Tx DMA/FIFO channels. + * + * See more detailed info in iwl-4965-hw.h. + ***************************************************/ + +static int iwl4965_queue_space(const struct iwl4965_queue *q) +{ + int s = q->read_ptr - q->write_ptr; + + if (q->read_ptr > q->write_ptr) + 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; +} + +/** + * iwl4965_queue_inc_wrap - increment queue index, wrap back to beginning + * @index -- current index + * @n_bd -- total number of entries in queue (must be power of 2) + */ +static inline int iwl4965_queue_inc_wrap(int index, int n_bd) +{ + return ++index & (n_bd - 1); +} + +/** + * iwl4965_queue_dec_wrap - decrement queue index, wrap back to end + * @index -- current index + * @n_bd -- total number of entries in queue (must be power of 2) + */ +static inline int iwl4965_queue_dec_wrap(int index, int n_bd) +{ + return --index & (n_bd - 1); +} + +static inline int x2_queue_used(const struct iwl4965_queue *q, int i) +{ + return q->write_ptr > q->read_ptr ? + (i >= q->read_ptr && i < q->write_ptr) : + !(i < q->read_ptr && i >= q->write_ptr); +} + +static inline u8 get_cmd_index(struct iwl4965_queue *q, u32 index, int is_huge) +{ + /* This is for scan command, the big buffer at end of command array */ + if (is_huge) + return q->n_window; /* must be power of 2 */ + + /* Otherwise, use normal size buffers */ + return index & (q->n_window - 1); +} + +/** + * iwl4965_queue_init - Initialize queue's high/low-water and read/write indexes + */ +static int iwl4965_queue_init(struct iwl4965_priv *priv, struct iwl4965_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 iwl4965_queue_inc_wrap + * and iwl4965_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->write_ptr = q->read_ptr = 0; + + return 0; +} + +/** + * iwl4965_tx_queue_alloc - Alloc driver data and TFD CB for one Tx/cmd queue + */ +static int iwl4965_tx_queue_alloc(struct iwl4965_priv *priv, + struct iwl4965_tx_queue *txq, u32 id) +{ + struct pci_dev *dev = priv->pci_dev; + + /* Driver private data, only for Tx (not command) queues, + * not shared with device. */ + 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 auxiliary BD " + "structures failed\n"); + goto error; + } + } else + txq->txb = NULL; + + /* Circular buffer of transmit frame descriptors (TFDs), + * shared with device */ + 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; +} + +/** + * iwl4965_tx_queue_init - Allocate and initialize one tx/cmd queue + */ +int iwl4965_tx_queue_init(struct iwl4965_priv *priv, + struct iwl4965_tx_queue *txq, int slots_num, u32 txq_id) +{ + struct pci_dev *dev = priv->pci_dev; + int len; + int rc = 0; + + /* + * Alloc buffer array for commands (Tx or other types of commands). + * For the command queue (#4), allocate command space + one big + * command for scan, since scan command is very huge; the system will + * not have two scans at the same time, so only one is needed. + * For normal Tx queues (all other queues), no super-size command + * space is needed. + */ + len = sizeof(struct iwl4965_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; + + /* Alloc driver data array and TFD circular buffer */ + rc = iwl4965_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 + * iwl4965_queue_inc_wrap and iwl4965_queue_dec_wrap are broken. */ + BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1)); + + /* Initialize queue's high/low-water marks, and head/tail indexes */ + iwl4965_queue_init(priv, &txq->q, TFD_QUEUE_SIZE_MAX, slots_num, txq_id); + + /* Tell device where to find queue */ + iwl4965_hw_tx_queue_init(priv, txq); + + return 0; +} + +/** + * iwl4965_tx_queue_free - Deallocate DMA queue. + * @txq: Transmit queue to deallocate. + * + * Empty queue by removing and destroying all BD's. + * Free all buffers. + * 0-fill, but do not free "txq" descriptor structure. + */ +void iwl4965_tx_queue_free(struct iwl4965_priv *priv, struct iwl4965_tx_queue *txq) +{ + struct iwl4965_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->write_ptr != q->read_ptr; + q->read_ptr = iwl4965_queue_inc_wrap(q->read_ptr, q->n_bd)) + iwl4965_hw_txq_free_tfd(priv, txq); + + len = sizeof(struct iwl4965_cmd) * q->n_window; + if (q->id == IWL_CMD_QUEUE_NUM) + len += IWL_MAX_SCAN_SIZE; + + /* De-alloc array of command/tx buffers */ + pci_free_consistent(dev, len, txq->cmd, txq->dma_addr_cmd); + + /* De-alloc circular buffer of TFDs */ + if (txq->q.n_bd) + pci_free_consistent(dev, sizeof(struct iwl4965_tfd_frame) * + txq->q.n_bd, txq->bd, txq->q.dma_addr); + + /* De-alloc array of per-TFD driver data */ + if (txq->txb) { + kfree(txq->txb); + txq->txb = NULL; + } + + /* 0-fill queue descriptor structure */ + memset(txq, 0, sizeof(*txq)); +} + +const u8 iwl4965_broadcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + +/*************** STATION TABLE MANAGEMENT **** + * mac80211 should be examined to determine if sta_info is duplicating + * the functionality provided here + */ + +/**************************************************************/ + +#if 0 /* temporary disable till we add real remove station */ +/** + * iwl4965_remove_station - Remove driver's knowledge of station. + * + * NOTE: This does not remove station from device's station table. + */ +static u8 iwl4965_remove_station(struct iwl4965_priv *priv, const u8 *addr, 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; + else if (is_broadcast_ether_addr(addr)) + index = priv->hw_setting.bcast_sta_id; + else + for (i = IWL_STA_ID; i < priv->hw_setting.max_stations; i++) + if (priv->stations[i].used && + !compare_ether_addr(priv->stations[i].sta.sta.addr, + addr)) { + index = i; + break; + } + + if (unlikely(index == IWL_INVALID_STATION)) + goto out; + + if (priv->stations[index].used) { + priv->stations[index].used = 0; + priv->num_stations--; + } + + BUG_ON(priv->num_stations < 0); + +out: + spin_unlock_irqrestore(&priv->sta_lock, flags); + return 0; +} +#endif + +/** + * iwl4965_clear_stations_table - Clear the driver's station table + * + * NOTE: This does not clear or otherwise alter the device's station table. + */ +static void iwl4965_clear_stations_table(struct iwl4965_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); +} + +/** + * iwl4965_add_station_flags - Add station to tables in driver and device + */ +u8 iwl4965_add_station_flags(struct iwl4965_priv *priv, const u8 *addr, + int is_ap, u8 flags, void *ht_data) +{ + int i; + int index = IWL_INVALID_STATION; + struct iwl4965_station_entry *station; + unsigned long flags_spin; + DECLARE_MAC_BUF(mac); + + spin_lock_irqsave(&priv->sta_lock, flags_spin); + if (is_ap) + index = IWL_AP_ID; + else if (is_broadcast_ether_addr(addr)) + index = priv->hw_setting.bcast_sta_id; + else + for (i = IWL_STA_ID; i < priv->hw_setting.max_stations; i++) { + if (!compare_ether_addr(priv->stations[i].sta.sta.addr, + addr)) { + index = i; + break; + } + + if (!priv->stations[i].used && + index == IWL_INVALID_STATION) + index = i; + } + + + /* These two conditions have the same outcome, but keep them separate + since they have different meanings */ + if (unlikely(index == IWL_INVALID_STATION)) { + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + return index; + } + + if (priv->stations[index].used && + !compare_ether_addr(priv->stations[index].sta.sta.addr, addr)) { + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + return index; + } + + + IWL_DEBUG_ASSOC("Add STA ID %d: %s\n", index, print_mac(mac, addr)); + station = &priv->stations[index]; + station->used = 1; + priv->num_stations++; + + /* Set up the REPLY_ADD_STA command to send to device */ + memset(&station->sta, 0, sizeof(struct iwl4965_addsta_cmd)); + memcpy(station->sta.sta.addr, addr, ETH_ALEN); + station->sta.mode = 0; + station->sta.sta.sta_id = index; + station->sta.station_flags = 0; + +#ifdef CONFIG_IWL4965_HT + /* BCAST station and IBSS stations do not work in HT mode */ + if (index != priv->hw_setting.bcast_sta_id && + priv->iw_mode != IEEE80211_IF_TYPE_IBSS) + iwl4965_set_ht_add_station(priv, index, + (struct ieee80211_ht_info *) ht_data); +#endif /*CONFIG_IWL4965_HT*/ + + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + + /* Add station to device's station table */ + iwl4965_send_add_station(priv, &station->sta, flags); + return index; + +} + +/*************** DRIVER STATUS FUNCTIONS *****/ + +static inline int iwl4965_is_ready(struct iwl4965_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 iwl4965_is_alive(struct iwl4965_priv *priv) +{ + return test_bit(STATUS_ALIVE, &priv->status); +} + +static inline int iwl4965_is_init(struct iwl4965_priv *priv) +{ + return test_bit(STATUS_INIT, &priv->status); +} + +static inline int iwl4965_is_rfkill(struct iwl4965_priv *priv) +{ + return test_bit(STATUS_RF_KILL_HW, &priv->status) || + test_bit(STATUS_RF_KILL_SW, &priv->status); +} + +static inline int iwl4965_is_ready_rf(struct iwl4965_priv *priv) +{ + + if (iwl4965_is_rfkill(priv)) + return 0; + + return iwl4965_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); + 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); + 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); + default: + return "UNKNOWN"; + + } +} + +#define HOST_COMPLETE_TIMEOUT (HZ / 2) + +/** + * iwl4965_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 iwl4965_enqueue_hcmd(struct iwl4965_priv *priv, struct iwl4965_host_cmd *cmd) +{ + struct iwl4965_tx_queue *txq = &priv->txq[IWL_CMD_QUEUE_NUM]; + struct iwl4965_queue *q = &txq->q; + struct iwl4965_tfd_frame *tfd; + u32 *control_flags; + struct iwl4965_cmd *out_cmd; + u32 idx; + u16 fix_size = (u16)(cmd->len + sizeof(out_cmd->hdr)); + dma_addr_t phys_addr; + int ret; + 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 (iwl4965_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->write_ptr]; + memset(tfd, 0, sizeof(*tfd)); + + control_flags = (u32 *) tfd; + + idx = get_cmd_index(q, q->write_ptr, 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->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->write_ptr)); + 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 iwl4965_cmd, hdr); + iwl4965_hw_txq_attach_buf_to_tfd(priv, tfd, phys_addr, fix_size); + + 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->write_ptr, idx, IWL_CMD_QUEUE_NUM); + + txq->need_update = 1; + + /* Set up entry in queue's byte count circular buffer */ + ret = iwl4965_tx_queue_update_wr_ptr(priv, txq, 0); + + /* Increment and update queue's write index */ + q->write_ptr = iwl4965_queue_inc_wrap(q->write_ptr, q->n_bd); + iwl4965_tx_queue_update_write_ptr(priv, txq); + + spin_unlock_irqrestore(&priv->hcmd_lock, flags); + return ret ? ret : idx; +} + +static int iwl4965_send_cmd_async(struct iwl4965_priv *priv, struct iwl4965_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 = iwl4965_enqueue_hcmd(priv, cmd); + if (ret < 0) { + IWL_ERROR("Error sending %s: iwl4965_enqueue_hcmd failed: %d\n", + get_cmd_string(cmd->id), ret); + return ret; + } + return 0; +} + +static int iwl4965_send_cmd_sync(struct iwl4965_priv *priv, struct iwl4965_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; + + cmd_idx = iwl4965_enqueue_hcmd(priv, cmd); + if (cmd_idx < 0) { + ret = cmd_idx; + IWL_ERROR("Error sending %s: iwl4965_enqueue_hcmd failed: %d\n", + get_cmd_string(cmd->id), ret); + goto out; + } + + 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 iwl4965_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 iwl4965_send_cmd(struct iwl4965_priv *priv, struct iwl4965_host_cmd *cmd) +{ + if (cmd->meta.flags & CMD_ASYNC) + return iwl4965_send_cmd_async(priv, cmd); + + return iwl4965_send_cmd_sync(priv, cmd); +} + +int iwl4965_send_cmd_pdu(struct iwl4965_priv *priv, u8 id, u16 len, const void *data) +{ + struct iwl4965_host_cmd cmd = { + .id = id, + .len = len, + .data = data, + }; + + return iwl4965_send_cmd_sync(priv, &cmd); +} + +static int __must_check iwl4965_send_cmd_u32(struct iwl4965_priv *priv, u8 id, u32 val) +{ + struct iwl4965_host_cmd cmd = { + .id = id, + .len = sizeof(val), + .data = &val, + }; + + return iwl4965_send_cmd_sync(priv, &cmd); +} + +int iwl4965_send_statistics_request(struct iwl4965_priv *priv) +{ + return iwl4965_send_cmd_u32(priv, REPLY_STATISTICS_CMD, 0); +} + +/** + * iwl4965_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 this fnction + */ +static int iwl4965_rxon_add_station(struct iwl4965_priv *priv, + const u8 *addr, int is_ap) +{ + u8 sta_id; + + /* Add station to device's station table */ +#ifdef CONFIG_IWL4965_HT + struct ieee80211_conf *conf = &priv->hw->conf; + struct ieee80211_ht_info *cur_ht_config = &conf->ht_conf; + + if ((is_ap) && + (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) && + (priv->iw_mode == IEEE80211_IF_TYPE_STA)) + sta_id = iwl4965_add_station_flags(priv, addr, is_ap, + 0, cur_ht_config); + else +#endif /* CONFIG_IWL4965_HT */ + sta_id = iwl4965_add_station_flags(priv, addr, is_ap, + 0, NULL); + + /* Set up default rate scaling table in device's station table */ + iwl4965_add_station(priv, addr, is_ap); + + return sta_id; +} + +/** + * iwl4965_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 iwl4965_set_rxon_channel(struct iwl4965_priv *priv, u8 phymode, + u16 channel) +{ + if (!iwl4965_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; +} + +/** + * iwl4965_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 iwl4965_check_rxon_cmd(struct iwl4965_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 (error) + IWL_WARNING("Tuning to channel %d\n", + le16_to_cpu(rxon->channel)); + + if (error) { + IWL_ERROR("Not a valid iwl4965_rxon_assoc_cmd field values\n"); + return -1; + } + return 0; +} + +/** + * iwl4965_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed + * @priv: staging_rxon is compared to active_rxon + * + * If the RXON structure is changing enough to require a new tune, + * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that + * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required. + */ +static int iwl4965_full_rxon_required(struct iwl4965_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) || + (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) || + (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 iwl4965_send_rxon_assoc(struct iwl4965_priv *priv) +{ + int rc = 0; + struct iwl4965_rx_packet *res = NULL; + struct iwl4965_rxon_assoc_cmd rxon_assoc; + struct iwl4965_host_cmd cmd = { + .id = REPLY_RXON_ASSOC, + .len = sizeof(rxon_assoc), + .meta.flags = CMD_WANT_SKB, + .data = &rxon_assoc, + }; + const struct iwl4965_rxon_cmd *rxon1 = &priv->staging_rxon; + const struct iwl4965_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) && + (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) && + (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; + 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; + + rc = iwl4965_send_cmd_sync(priv, &cmd); + if (rc) + return rc; + + res = (struct iwl4965_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; +} + +/** + * iwl4965_commit_rxon - commit staging_rxon to hardware + * + * The RXON command in staging_rxon is committed 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 iwl4965_commit_rxon(struct iwl4965_priv *priv) +{ + /* cast away the const for active_rxon in this function */ + struct iwl4965_rxon_cmd *active_rxon = (void *)&priv->active_rxon; + DECLARE_MAC_BUF(mac); + int rc = 0; + + if (!iwl4965_is_alive(priv)) + return -1; + + /* always get timestamp with Rx frame */ + priv->staging_rxon.flags |= RXON_FLG_TSF2HOST_MSK; + + rc = iwl4965_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 + * iwl4965_rxon_assoc_cmd which is used to reconfigure filter + * and other flags for the current radio configuration. */ + if (!iwl4965_full_rxon_required(priv)) { + rc = iwl4965_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; + } + + /* station table will be cleared */ + priv->assoc_station_added = 0; + +#ifdef CONFIG_IWL4965_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_IWL4965_SENSITIVITY */ + + /* 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 (iwl4965_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 = iwl4965_send_cmd_pdu(priv, REPLY_RXON, + sizeof(struct iwl4965_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; + } + } + + IWL_DEBUG_INFO("Sending RXON\n" + "* with%s RXON_FILTER_ASSOC_MSK\n" + "* channel = %d\n" + "* bssid = %s\n", + ((priv->staging_rxon.filter_flags & + RXON_FILTER_ASSOC_MSK) ? "" : "out"), + le16_to_cpu(priv->staging_rxon.channel), + print_mac(mac, priv->staging_rxon.bssid_addr)); + + /* Apply the new configuration */ + rc = iwl4965_send_cmd_pdu(priv, REPLY_RXON, + sizeof(struct iwl4965_rxon_cmd), &priv->staging_rxon); + if (rc) { + IWL_ERROR("Error setting new configuration (%d).\n", rc); + return rc; + } + + iwl4965_clear_stations_table(priv); + +#ifdef CONFIG_IWL4965_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_IWL4965_SENSITIVITY */ + + 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 = iwl4965_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 (iwl4965_rxon_add_station(priv, iwl4965_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 (iwl4965_is_associated(priv) && + (priv->iw_mode == IEEE80211_IF_TYPE_STA)) { + if (iwl4965_rxon_add_station(priv, priv->active_rxon.bssid_addr, 1) + == IWL_INVALID_STATION) { + IWL_ERROR("Error adding AP address for transmit.\n"); + return -EIO; + } + priv->assoc_station_added = 1; + } + + return 0; +} + +static int iwl4965_send_bt_config(struct iwl4965_priv *priv) +{ + struct iwl4965_bt_cmd bt_cmd = { + .flags = 3, + .lead_time = 0xAA, + .max_kill = 1, + .kill_ack_mask = 0, + .kill_cts_mask = 0, + }; + + return iwl4965_send_cmd_pdu(priv, REPLY_BT_CONFIG, + sizeof(struct iwl4965_bt_cmd), &bt_cmd); +} + +static int iwl4965_send_scan_abort(struct iwl4965_priv *priv) +{ + int rc = 0; + struct iwl4965_rx_packet *res; + struct iwl4965_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 = iwl4965_send_cmd_sync(priv, &cmd); + if (rc) { + clear_bit(STATUS_SCAN_ABORTING, &priv->status); + return rc; + } + + res = (struct iwl4965_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 iwl4965_card_state_sync_callback(struct iwl4965_priv *priv, + struct iwl4965_cmd *cmd, + struct sk_buff *skb) +{ + return 1; +} + +/* + * CARD_STATE_CMD + * + * Use: Sets the device's 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 iwl4965_send_card_state(struct iwl4965_priv *priv, u32 flags, u8 meta_flag) +{ + struct iwl4965_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 = iwl4965_card_state_sync_callback; + + return iwl4965_send_cmd(priv, &cmd); +} + +static int iwl4965_add_sta_sync_callback(struct iwl4965_priv *priv, + struct iwl4965_cmd *cmd, struct sk_buff *skb) +{ + struct iwl4965_rx_packet *res = NULL; + + if (!skb) { + IWL_ERROR("Error: Response NULL in REPLY_ADD_STA.\n"); + return 1; + } + + res = (struct iwl4965_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 iwl4965_send_add_station(struct iwl4965_priv *priv, + struct iwl4965_addsta_cmd *sta, u8 flags) +{ + struct iwl4965_rx_packet *res = NULL; + int rc = 0; + struct iwl4965_host_cmd cmd = { + .id = REPLY_ADD_STA, + .len = sizeof(struct iwl4965_addsta_cmd), + .meta.flags = flags, + .data = sta, + }; + + if (flags & CMD_ASYNC) + cmd.meta.u.callback = iwl4965_add_sta_sync_callback; + else + cmd.meta.flags |= CMD_WANT_SKB; + + rc = iwl4965_send_cmd(priv, &cmd); + + if (rc || (flags & CMD_ASYNC)) + return rc; + + res = (struct iwl4965_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 iwl4965_update_sta_key_info(struct iwl4965_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: + 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"); + iwl4965_send_add_station(priv, &priv->stations[sta_id].sta, 0); + return 0; +} + +static int iwl4965_clear_sta_key_info(struct iwl4965_priv *priv, u8 sta_id) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->sta_lock, flags); + memset(&priv->stations[sta_id].keyinfo, 0, sizeof(struct iwl4965_hw_key)); + memset(&priv->stations[sta_id].sta.key, 0, sizeof(struct iwl4965_keyinfo)); + priv->stations[sta_id].sta.key.key_flags = STA_KEY_FLG_NO_ENC; + 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: clear ucode station key info\n"); + iwl4965_send_add_station(priv, &priv->stations[sta_id].sta, 0); + return 0; +} + +static void iwl4965_clear_free_frames(struct iwl4965_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 iwl4965_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 iwl4965_frame *iwl4965_get_free_frame(struct iwl4965_priv *priv) +{ + struct iwl4965_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 iwl4965_frame, list); +} + +static void iwl4965_free_frame(struct iwl4965_priv *priv, struct iwl4965_frame *frame) +{ + memset(frame, 0, sizeof(*frame)); + list_add(&frame->list, &priv->free_frames); +} + +unsigned int iwl4965_fill_beacon_frame(struct iwl4965_priv *priv, + struct ieee80211_hdr *hdr, + const u8 *dest, int left) +{ + + if (!iwl4965_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; +} + +int iwl4965_rate_index_from_plcp(int plcp) +{ + int i = 0; + + /* 4965 HT rate format */ + if (plcp & RATE_MCS_HT_MSK) { + i = (plcp & 0xff); + + if (i >= IWL_RATE_MIMO_6M_PLCP) + i = i - IWL_RATE_MIMO_6M_PLCP; + + i += IWL_FIRST_OFDM_RATE; + /* skip 9M not supported in ht*/ + if (i >= IWL_RATE_9M_INDEX) + i += 1; + if ((i >= IWL_FIRST_OFDM_RATE) && + (i <= IWL_LAST_OFDM_RATE)) + return i; + + /* 4965 legacy rate format, search for match in table */ + } else { + for (i = 0; i < ARRAY_SIZE(iwl4965_rates); i++) + if (iwl4965_rates[i].plcp == (plcp &0xFF)) + return i; + } + return -1; +} + +static u8 iwl4965_rate_get_lowest_plcp(int rate_mask) +{ + u8 i; + + for (i = IWL_RATE_1M_INDEX; i != IWL_RATE_INVALID; + i = iwl4965_rates[i].next_ieee) { + if (rate_mask & (1 << i)) + return iwl4965_rates[i].plcp; + } + + return IWL_RATE_INVALID; +} + +static int iwl4965_send_beacon_cmd(struct iwl4965_priv *priv) +{ + struct iwl4965_frame *frame; + unsigned int frame_size; + int rc; + u8 rate; + + frame = iwl4965_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 = iwl4965_rate_get_lowest_plcp(priv->active_rate_basic & + 0xFF0); + if (rate == IWL_INVALID_RATE) + rate = IWL_RATE_6M_PLCP; + } else { + rate = iwl4965_rate_get_lowest_plcp(priv->active_rate_basic & 0xF); + if (rate == IWL_INVALID_RATE) + rate = IWL_RATE_1M_PLCP; + } + + frame_size = iwl4965_hw_get_beacon_cmd(priv, frame, rate); + + rc = iwl4965_send_cmd_pdu(priv, REPLY_TX_BEACON, frame_size, + &frame->u.cmd[0]); + + iwl4965_free_frame(priv, frame); + + return rc; +} + +/****************************************************************************** + * + * EEPROM related functions + * + ******************************************************************************/ + +static void get_eeprom_mac(struct iwl4965_priv *priv, u8 *mac) +{ + memcpy(mac, priv->eeprom.mac_address, 6); +} + +/** + * iwl4965_eeprom_init - read EEPROM contents + * + * Load the EEPROM contents from adapter into priv->eeprom + * + * NOTE: This routine uses the non-debug IO access functions. + */ +int iwl4965_eeprom_init(struct iwl4965_priv *priv) +{ + u16 *e = (u16 *)&priv->eeprom; + u32 gp = iwl4965_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; + } + + /* Make sure driver (instead of uCode) is allowed to read EEPROM */ + rc = iwl4965_eeprom_acquire_semaphore(priv); + if (rc < 0) { + IWL_ERROR("Failed to acquire EEPROM semaphore.\n"); + return -ENOENT; + } + + /* eeprom is an array of 16bit values */ + for (addr = 0; addr < sz; addr += sizeof(u16)) { + _iwl4965_write32(priv, CSR_EEPROM_REG, addr << 1); + _iwl4965_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 = _iwl4965_read_direct32(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: + iwl4965_eeprom_release_semaphore(priv); + return rc; +} + +/****************************************************************************** + * + * Misc. internal state and helper functions + * + ******************************************************************************/ +#ifdef CONFIG_IWL4965_DEBUG + +/** + * iwl4965_report_frame - dump frame to syslog during debug sessions + * + * You may 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: This was originally written for 3945, need to audit for + * proper operation with 4965. + */ +void iwl4965_report_frame(struct iwl4965_priv *priv, + struct iwl4965_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 iwl4965_rx_frame_stats *rx_stats = IWL_RX_STATS(pkt); + struct iwl4965_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt); + struct iwl4965_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 = iwl4965_rate_index_from_plcp(rate_sym); + if (rate == -1) + rate = 0; + else + rate = iwl4965_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) + iwl4965_print_hex_dump(IWL_DL_RX, data, length); +} +#endif + +static void iwl4965_unset_hw_setting(struct iwl4965_priv *priv) +{ + if (priv->hw_setting.shared_virt) + pci_free_consistent(priv->pci_dev, + sizeof(struct iwl4965_shared), + priv->hw_setting.shared_virt, + priv->hw_setting.shared_phys); +} + +/** + * iwl4965_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 iwl4965_supported_rate_to_ie(u8 *ie, u16 supported_rate, + u16 basic_rate, int *left) +{ + u16 ret_rates = 0, bit; + int i; + u8 *cnt = ie; + u8 *rates = ie + 1; + + for (bit = 1, i = 0; i < IWL_RATE_COUNT; i++, bit <<= 1) { + if (bit & supported_rate) { + ret_rates |= bit; + rates[*cnt] = iwl4965_rates[i].ieee | + ((bit & basic_rate) ? 0x80 : 0x00); + (*cnt)++; + (*left)--; + if ((*left <= 0) || + (*cnt >= IWL_SUPPORTED_RATES_IE_LEN)) + break; + } + } + + return ret_rates; +} + +#ifdef CONFIG_IWL4965_HT +void static iwl4965_set_ht_capab(struct ieee80211_hw *hw, + struct ieee80211_ht_cap *ht_cap, + u8 use_current_config); +#endif + +/** + * iwl4965_fill_probe_req - fill in all required fields and IE for probe request + */ +static u16 iwl4965_fill_probe_req(struct iwl4965_priv *priv, + struct ieee80211_mgmt *frame, + int left, int is_direct) +{ + int len = 0; + u8 *pos = NULL; + u16 active_rates, ret_rates, cck_rates, active_rate_basic; +#ifdef CONFIG_IWL4965_HT + struct ieee80211_hw_mode *mode; +#endif /* CONFIG_IWL4965_HT */ + + /* 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, iwl4965_broadcast_addr, ETH_ALEN); + memcpy(frame->sa, priv->mac_addr, ETH_ALEN); + memcpy(frame->bssid, iwl4965_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; + + /* exclude 60M rate */ + active_rates = priv->rates_mask; + active_rates &= ~IWL_RATE_60M_MASK; + + active_rate_basic = active_rates & IWL_BASIC_RATES_MASK; + + cck_rates = IWL_CCK_RATES_MASK & active_rates; + ret_rates = iwl4965_supported_rate_to_ie(pos, cck_rates, + active_rate_basic, &left); + active_rates &= ~ret_rates; + + ret_rates = iwl4965_supported_rate_to_ie(pos, active_rates, + active_rate_basic, &left); + active_rates &= ~ret_rates; + + len += 2 + *pos; + pos += (*pos) + 1; + if (active_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; + iwl4965_supported_rate_to_ie(pos, active_rates, + active_rate_basic, &left); + if (*pos > 0) + len += 2 + *pos; + +#ifdef CONFIG_IWL4965_HT + mode = priv->hw->conf.mode; + if (mode->ht_info.ht_supported) { + pos += (*pos) + 1; + *pos++ = WLAN_EID_HT_CAPABILITY; + *pos++ = sizeof(struct ieee80211_ht_cap); + iwl4965_set_ht_capab(priv->hw, + (struct ieee80211_ht_cap *)pos, 0); + len += 2 + sizeof(struct ieee80211_ht_cap); + } +#endif /*CONFIG_IWL4965_HT */ + + fill_end: + return (u16)len; +} + +/* + * QoS support +*/ +#ifdef CONFIG_IWL4965_QOS +static int iwl4965_send_qos_params_command(struct iwl4965_priv *priv, + struct iwl4965_qosparam_cmd *qos) +{ + + return iwl4965_send_cmd_pdu(priv, REPLY_QOS_PARAM, + sizeof(struct iwl4965_qosparam_cmd), qos); +} + +static void iwl4965_reset_qos(struct iwl4965_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 = 1; + if (!(priv->active_rate & 0xfff0)) { + cw_min = 31; + is_legacy = 1; + } + } else if (priv->iw_mode == IEEE80211_IF_TYPE_AP) { + if (priv->qos_data.qos_enable) + priv->qos_data.qos_active = 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 iwl4965_activate_qos(struct iwl4965_priv *priv, u8 force) +{ + unsigned long flags; + + 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; + +#ifdef CONFIG_IWL4965_HT + if (priv->current_ht_config.is_ht) + priv->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK; +#endif /* CONFIG_IWL4965_HT */ + + spin_unlock_irqrestore(&priv->lock, flags); + + if (force || iwl4965_is_associated(priv)) { + IWL_DEBUG_QOS("send QoS cmd with Qos active=%d FLAGS=0x%X\n", + priv->qos_data.qos_active, + priv->qos_data.def_qos_parm.qos_flags); + + iwl4965_send_qos_params_command(priv, + &(priv->qos_data.def_qos_parm)); + } +} + +#endif /* CONFIG_IWL4965_QOS */ +/* + * Power management (not Tx power!) functions + */ +#define MSEC_TO_USEC 1024 + +#define NOSLP __constant_cpu_to_le16(0), 0, 0 +#define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK, 0, 0 +#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 iwl4965_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 iwl4965_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 iwl4965_power_init_handle(struct iwl4965_priv *priv) +{ + int rc = 0, i; + struct iwl4965_power_mgr *pow_data; + int size = sizeof(struct iwl4965_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 iwl4965_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 iwl4965_update_power_cmd(struct iwl4965_priv *priv, + struct iwl4965_powertable_cmd *cmd, u32 mode) +{ + int rc = 0, i; + u8 skip; + u32 max_sleep = 0; + struct iwl4965_power_vec_entry *range; + u8 period = 0; + struct iwl4965_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 iwl4965_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 iwl4965_send_power_mode(struct iwl4965_priv *priv, u32 mode) +{ + u32 uninitialized_var(final_mode); + int rc; + struct iwl4965_powertable_cmd cmd; + + /* If on battery, set to 3, + * if plugged into AC power, set to CAM ("continuously 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; + } + + cmd.keep_alive_beacons = 0; + + iwl4965_update_power_cmd(priv, &cmd, final_mode); + + rc = iwl4965_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 iwl4965_is_network_packet(struct iwl4965_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 + +static const char *iwl4965_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"; +} + +/** + * iwl4965_scan_cancel - Cancel any currently executing HW scan + * + * NOTE: priv->mutex is not required before calling this function + */ +static int iwl4965_scan_cancel(struct iwl4965_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; +} + +/** + * iwl4965_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 iwl4965_scan_cancel_timeout(struct iwl4965_priv *priv, unsigned long ms) +{ + unsigned long now = jiffies; + int ret; + + ret = iwl4965_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 iwl4965_sequence_reset(struct iwl4965_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; + + iwl4965_scan_cancel(priv); +} + +#define MAX_UCODE_BEACON_INTERVAL 4096 +#define INTEL_CONN_LISTEN_INTERVAL __constant_cpu_to_le16(0xA) + +static __le16 iwl4965_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 iwl4965_setup_rxon_timing(struct iwl4965_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 = + iwl4965_adjust_beacon_interval( + le16_to_cpu(priv->rxon_timing.beacon_interval)); + } + + priv->rxon_timing.atim_window = 0; + } else { + priv->rxon_timing.beacon_interval = + iwl4965_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 iwl4965_scan_initiate(struct iwl4965_priv *priv) +{ + if (priv->iw_mode == IEEE80211_IF_TYPE_AP) { + IWL_ERROR("APs don't scan.\n"); + return 0; + } + + if (!iwl4965_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 iwl4965_set_rxon_hwcrypto(struct iwl4965_priv *priv, int hw_decrypt) +{ + struct iwl4965_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 iwl4965_set_flags_for_phymode(struct iwl4965_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 iwl4965_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; + } +} + +/* + * initialize rxon structure with default values from eeprom + */ +static void iwl4965_connection_init_rx_config(struct iwl4965_priv *priv) +{ + const struct iwl4965_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 = iwl4965_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; + + iwl4965_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; + + priv->staging_rxon.flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED_MSK | + RXON_FLG_CHANNEL_MODE_PURE_40_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); +} + +static int iwl4965_set_mode(struct iwl4965_priv *priv, int mode) +{ + if (mode == IEEE80211_IF_TYPE_IBSS) { + const struct iwl4965_channel_info *ch_info; + + ch_info = iwl4965_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; + } + } + + priv->iw_mode = mode; + + iwl4965_connection_init_rx_config(priv); + memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN); + + iwl4965_clear_stations_table(priv); + + /* dont commit rxon if rf-kill is on*/ + if (!iwl4965_is_ready_rf(priv)) + return -EAGAIN; + + cancel_delayed_work(&priv->scan_check); + if (iwl4965_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; + } + + iwl4965_commit_rxon(priv); + + return 0; +} + +static void iwl4965_build_tx_cmd_hwcrypto(struct iwl4965_priv *priv, + struct ieee80211_tx_control *ctl, + struct iwl4965_cmd *cmd, + struct sk_buff *skb_frag, + int last_frag) +{ + struct iwl4965_hw_key *keyinfo = &priv->stations[ctl->key_idx].keyinfo; + + switch (keyinfo->alg) { + case ALG_CCMP: + cmd->cmd.tx.sec_ctl = TX_CMD_SEC_CCM; + 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); +#endif + break; + + case ALG_WEP: + cmd->cmd.tx.sec_ctl = TX_CMD_SEC_WEP | + (ctl->key_idx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT; + + if (keyinfo->keylen == 13) + cmd->cmd.tx.sec_ctl |= TX_CMD_SEC_KEY128; + + memcpy(&cmd->cmd.tx.key[3], keyinfo->key, keyinfo->keylen); + + IWL_DEBUG_TX("Configuring packet for WEP encryption " + "with key %d\n", ctl->key_idx); + break; + + default: + printk(KERN_ERR "Unknown encode alg %d\n", keyinfo->alg); + break; + } +} + +/* + * handle build REPLY_TX command notification. + */ +static void iwl4965_build_tx_cmd_basic(struct iwl4965_priv *priv, + struct iwl4965_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; +} + +/** + * iwl4965_get_sta_id - Find station's index within station table + * + * If new IBSS station, create new entry in station table + */ +static int iwl4965_get_sta_id(struct iwl4965_priv *priv, + struct ieee80211_hdr *hdr) +{ + int sta_id; + u16 fc = le16_to_cpu(hdr->frame_control); + DECLARE_MAC_BUF(mac); + + /* If this frame is broadcast or management, use broadcast station id */ + if (((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA) || + is_multicast_ether_addr(hdr->addr1)) + return priv->hw_setting.bcast_sta_id; + + switch (priv->iw_mode) { + + /* If we are a client station in a BSS network, use the special + * AP station entry (that's the only station we communicate with) */ + 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 = iwl4965_hw_find_station(priv, hdr->addr1); + if (sta_id != IWL_INVALID_STATION) + return sta_id; + return priv->hw_setting.bcast_sta_id; + + /* If this frame is going out to an IBSS network, find the station, + * or create a new station table entry */ + case IEEE80211_IF_TYPE_IBSS: + sta_id = iwl4965_hw_find_station(priv, hdr->addr1); + if (sta_id != IWL_INVALID_STATION) + return sta_id; + + /* Create new station table entry */ + sta_id = iwl4965_add_station_flags(priv, hdr->addr1, + 0, CMD_ASYNC, NULL); + + if (sta_id != IWL_INVALID_STATION) + return sta_id; + + IWL_DEBUG_DROP("Station %s not in station map. " + "Defaulting to broadcast...\n", + print_mac(mac, hdr->addr1)); + iwl4965_print_hex_dump(IWL_DL_DROP, (u8 *) hdr, sizeof(*hdr)); + return priv->hw_setting.bcast_sta_id; + + default: + IWL_WARNING("Unknown mode of operation: %d", priv->iw_mode); + return priv->hw_setting.bcast_sta_id; + } +} + +/* + * start REPLY_TX command process + */ +static int iwl4965_tx_skb(struct iwl4965_priv *priv, + struct sk_buff *skb, struct ieee80211_tx_control *ctl) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct iwl4965_tfd_frame *tfd; + u32 *control_flags; + int txq_id = ctl->queue; + struct iwl4965_tx_queue *txq = NULL; + struct iwl4965_queue *q = NULL; + dma_addr_t phys_addr; + dma_addr_t txcmd_phys; + struct iwl4965_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 (iwl4965_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_IWL4965_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 + + /* drop all data frame if we are not associated */ + if (!iwl4965_is_associated(priv) && !priv->assoc_id && + ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)) { + IWL_DEBUG_DROP("Dropping - !iwl4965_is_associated\n"); + goto drop_unlock; + } + + spin_unlock_irqrestore(&priv->lock, flags); + + hdr_len = ieee80211_get_hdrlen(fc); + + /* Find (or create) index into station table for destination station */ + sta_id = iwl4965_get_sta_id(priv, hdr); + if (sta_id == IWL_INVALID_STATION) { + DECLARE_MAC_BUF(mac); + + IWL_DEBUG_DROP("Dropping - INVALID STATION: %s\n", + print_mac(mac, 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; +#ifdef CONFIG_IWL4965_HT +#ifdef CONFIG_IWL4965_HT_AGG + /* aggregation is on for this <sta,tid> */ + if (ctl->flags & IEEE80211_TXCTL_HT_MPDU_AGG) + txq_id = priv->stations[sta_id].tid[tid].agg.txq_id; +#endif /* CONFIG_IWL4965_HT_AGG */ +#endif /* CONFIG_IWL4965_HT */ + } + + /* Descriptor for chosen Tx queue */ + txq = &priv->txq[txq_id]; + q = &txq->q; + + spin_lock_irqsave(&priv->lock, flags); + + /* Set up first empty TFD within this queue's circular TFD buffer */ + tfd = &txq->bd[q->write_ptr]; + memset(tfd, 0, sizeof(*tfd)); + control_flags = (u32 *) tfd; + idx = get_cmd_index(q, q->write_ptr, 0); + + /* Set up driver data for this TFD */ + memset(&(txq->txb[q->write_ptr]), 0, sizeof(struct iwl4965_tx_info)); + txq->txb[q->write_ptr].skb[0] = skb; + memcpy(&(txq->txb[q->write_ptr].status.control), + ctl, sizeof(struct ieee80211_tx_control)); + + /* Set up first empty entry in queue's array of Tx/cmd buffers */ + 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)); + + /* + * Set up the Tx-command (not MAC!) header. + * Store the chosen Tx queue and TFD index within the sequence field; + * after Tx, uCode's Tx response will return this value so driver can + * locate the frame within the tx queue and do post-tx processing. + */ + out_cmd->hdr.cmd = REPLY_TX; + out_cmd->hdr.sequence = cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | + INDEX_TO_SEQ(q->write_ptr))); + + /* Copy MAC header from skb into command buffer */ + memcpy(out_cmd->cmd.tx.hdr, hdr, hdr_len); + + /* + * Use the first empty entry in this queue's command buffer array + * to contain the Tx command and MAC header concatenated together + * (payload data will be in another buffer). + * Size of this varies, due to varying MAC header length. + * If end is not dword aligned, we'll have 2 extra bytes at the end + * of the MAC header (device reads on dword boundaries). + * We'll tell device about this padding later. + */ + len = priv->hw_setting.tx_cmd_len + + sizeof(struct iwl4965_cmd_header) + hdr_len; + + len_org = len; + len = (len + 3) & ~3; + + if (len_org != len) + len_org = 1; + else + len_org = 0; + + /* Physical address of this Tx command's header (not MAC header!), + * within command buffer array. */ + txcmd_phys = txq->dma_addr_cmd + sizeof(struct iwl4965_cmd) * idx + + offsetof(struct iwl4965_cmd, hdr); + + /* Add buffer containing Tx command and MAC(!) header to TFD's + * first entry */ + iwl4965_hw_txq_attach_buf_to_tfd(priv, tfd, txcmd_phys, len); + + if (!(ctl->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT)) + iwl4965_build_tx_cmd_hwcrypto(priv, ctl, out_cmd, skb, 0); + + /* Set up TFD's 2nd entry to point directly to remainder of skb, + * if any (802.11 null frames 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); + iwl4965_hw_txq_attach_buf_to_tfd(priv, tfd, phys_addr, len); + } + + /* Tell 4965 about any 2-byte padding after MAC header */ + if (len_org) + out_cmd->cmd.tx.tx_flags |= TX_CMD_FLG_MH_PAD_MSK; + + /* Total # bytes to be transmitted */ + len = (u16)skb->len; + out_cmd->cmd.tx.len = cpu_to_le16(len); + + /* TODO need this for burst mode later on */ + iwl4965_build_tx_cmd_basic(priv, out_cmd, ctl, hdr, unicast, sta_id); + + /* set is_hcca to 0; it probably will never be implemented */ + iwl4965_hw_build_tx_cmd_rate(priv, out_cmd, ctl, hdr, sta_id, 0); + + iwl4965_tx_cmd(priv, out_cmd, sta_id, txcmd_phys, + hdr, hdr_len, ctl, NULL); + + 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; + } + + iwl4965_print_hex_dump(IWL_DL_TX, out_cmd->cmd.payload, + sizeof(out_cmd->cmd.tx)); + + iwl4965_print_hex_dump(IWL_DL_TX, (u8 *)out_cmd->cmd.tx.hdr, + ieee80211_get_hdrlen(fc)); + + /* Set up entry for this TFD in Tx byte-count array */ + iwl4965_tx_queue_update_wr_ptr(priv, txq, len); + + /* Tell device the write index *just past* this latest filled TFD */ + q->write_ptr = iwl4965_queue_inc_wrap(q->write_ptr, q->n_bd); + rc = iwl4965_tx_queue_update_write_ptr(priv, txq); + spin_unlock_irqrestore(&priv->lock, flags); + + if (rc) + return rc; + + if ((iwl4965_queue_space(q) < q->high_mark) + && priv->mac80211_registered) { + if (wait_write_ptr) { + spin_lock_irqsave(&priv->lock, flags); + txq->need_update = 1; + iwl4965_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 iwl4965_set_rate(struct iwl4965_priv *priv) +{ + const struct ieee80211_hw_mode *hw = NULL; + struct ieee80211_rate *rate; + int i; + + hw = iwl4965_get_hw_mode(priv, priv->phymode); + if (!hw) { + IWL_ERROR("Failed to set rate: unable to get hw mode\n"); + return; + } + + 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, iwl4965_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, iwl4965_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 iwl4965_radio_kill_sw(struct iwl4965_priv *priv, int disable_radio) +{ + unsigned long flags; + + if (!!disable_radio == test_bit(STATUS_RF_KILL_SW, &priv->status)) + return; + + IWL_DEBUG_RF_KILL("Manual SW RF KILL set to: RADIO %s\n", + disable_radio ? "OFF" : "ON"); + + if (disable_radio) { + iwl4965_scan_cancel(priv); + /* FIXME: This is a workaround for AP */ + if (priv->iw_mode != IEEE80211_IF_TYPE_AP) { + spin_lock_irqsave(&priv->lock, flags); + iwl4965_write32(priv, CSR_UCODE_DRV_GP1_SET, + CSR_UCODE_SW_BIT_RFKILL); + spin_unlock_irqrestore(&priv->lock, flags); + iwl4965_send_card_state(priv, CARD_STATE_CMD_DISABLE, 0); + set_bit(STATUS_RF_KILL_SW, &priv->status); + } + return; + } + + spin_lock_irqsave(&priv->lock, flags); + iwl4965_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); + iwl4965_read32(priv, CSR_UCODE_DRV_GP1); + if (!iwl4965_grab_nic_access(priv)) + iwl4965_release_nic_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 iwl4965_set_decrypted_flag(struct iwl4965_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 iwl4965_handle_data_packet_monitor(struct iwl4965_priv *priv, + struct iwl4965_rx_mem_buffer *rxb, + void *data, short len, + struct ieee80211_rx_status *stats, + u16 phy_flags) +{ + struct iwl4965_rt_rx_hdr *iwl4965_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 > priv->hw_setting.rx_buf_size - sizeof(*iwl4965_rt)) { + IWL_DEBUG_DROP("Dropping too large packet in monitor\n"); + return; + } + + /* copy the frame data to write after where the radiotap header goes */ + iwl4965_rt = (void *)rxb->skb->data; + memmove(iwl4965_rt->payload, data, len); + + iwl4965_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION; + iwl4965_rt->rt_hdr.it_pad = 0; /* always good to zero */ + + /* total header + data */ + iwl4965_rt->rt_hdr.it_len = cpu_to_le16(sizeof(*iwl4965_rt)); + + /* Set the size of the skb to the size of the frame */ + skb_put(rxb->skb, sizeof(*iwl4965_rt) + len); + + /* Big bitfield of all the fields we provide in radiotap */ + iwl4965_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 */ + iwl4965_rt->rt_flags = 0; + + iwl4965_rt->rt_tsf = cpu_to_le64(tsf); + + /* Convert to dBm */ + iwl4965_rt->rt_dbmsignal = signal; + iwl4965_rt->rt_dbmnoise = noise; + + /* Convert the channel frequency and set the flags */ + iwl4965_rt->rt_channelMHz = cpu_to_le16(stats->freq); + if (!(phy_flags_hw & RX_RES_PHY_FLAGS_BAND_24_MSK)) + iwl4965_rt->rt_chbitmask = + cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ)); + else if (phy_flags_hw & RX_RES_PHY_FLAGS_MOD_CCK_MSK) + iwl4965_rt->rt_chbitmask = + cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ)); + else /* 802.11g */ + iwl4965_rt->rt_chbitmask = + cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ)); + + rate = iwl4965_rate_index_from_plcp(rate); + if (rate == -1) + iwl4965_rt->rt_rate = 0; + else + iwl4965_rt->rt_rate = iwl4965_rates[rate].ieee; + + /* antenna number */ + iwl4965_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) + iwl4965_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 iwl4965_is_duplicate_packet(struct iwl4965_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 iwl4965_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 iwl4965_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_IWL4965_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 iwl4965_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 iwl4965_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 iwl4965_get_measurement(struct iwl4965_priv *priv, + struct ieee80211_measurement_params *params, + u8 type) +{ + struct iwl4965_spectrum_cmd spectrum; + struct iwl4965_rx_packet *res; + struct iwl4965_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 (iwl4965_is_associated(priv)) + add_time = + iwl4965_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 (iwl4965_is_associated(priv)) + spectrum.start_time = + iwl4965_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 = iwl4965_send_cmd_sync(priv, &cmd); + if (rc) + return rc; + + res = (struct iwl4965_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 iwl4965_txstatus_to_ieee(struct iwl4965_priv *priv, + struct iwl4965_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; +} + +/** + * iwl4965_tx_queue_reclaim - Reclaim Tx queue entries already Tx'd + * + * 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 the stack that feeds us. + */ +int iwl4965_tx_queue_reclaim(struct iwl4965_priv *priv, int txq_id, int index) +{ + struct iwl4965_tx_queue *txq = &priv->txq[txq_id]; + struct iwl4965_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->write_ptr, q->read_ptr); + return 0; + } + + for (index = iwl4965_queue_inc_wrap(index, q->n_bd); + q->read_ptr != index; + q->read_ptr = iwl4965_queue_inc_wrap(q->read_ptr, q->n_bd)) { + if (txq_id != IWL_CMD_QUEUE_NUM) { + iwl4965_txstatus_to_ieee(priv, + &(txq->txb[txq->q.read_ptr])); + iwl4965_hw_txq_free_tfd(priv, txq); + } else if (nfreed > 1) { + IWL_ERROR("HCMD skipped: index (%d) %d %d\n", index, + q->write_ptr, q->read_ptr); + queue_work(priv->workqueue, &priv->restart); + } + nfreed++; + } + + if (iwl4965_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 iwl4965_is_tx_success(u32 status) +{ + status &= TX_STATUS_MSK; + return (status == TX_STATUS_SUCCESS) + || (status == TX_STATUS_DIRECT_DONE); +} + +/****************************************************************************** + * + * Generic RX handler implementations + * + ******************************************************************************/ +#ifdef CONFIG_IWL4965_HT +#ifdef CONFIG_IWL4965_HT_AGG + +static inline int iwl4965_get_ra_sta_id(struct iwl4965_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 iwl4965_hw_find_station(priv, da); + } +} + +static struct ieee80211_hdr *iwl4965_tx_queue_get_hdr( + struct iwl4965_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 iwl4965_get_scd_ssn(struct iwl4965_tx_resp *tx_resp) +{ + __le32 *scd_ssn = (__le32 *)((u32 *)&tx_resp->status + + tx_resp->frame_count); + return le32_to_cpu(*scd_ssn) & MAX_SN; + +} + +/** + * iwl4965_tx_status_reply_tx - Handle Tx rspnse for frames in aggregation queue + */ +static int iwl4965_tx_status_reply_tx(struct iwl4965_priv *priv, + struct iwl4965_ht_agg *agg, + struct iwl4965_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 response w/o block-ack\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; + + /* # frames attempted by Tx command */ + if (agg->frame_count == 1) { + /* Only one frame was attempted; no block-ack will arrive */ + struct iwl4965_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.read_ptr].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 = iwl4965_is_tx_success(status)? + IEEE80211_TX_STATUS_ACK : 0; + tx_status->control.tx_rate = + iwl4965_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", + iwl4965_hw_get_rate_n_flags(tx_resp->rate_n_flags)); + + agg->wait_for_ba = 0; + } else { + /* Two or more frames were attempted; expect block-ack */ + u64 bitmap = 0; + int start = agg->start_idx; + + /* Construct bit-map of pending frames within Tx window */ + 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 = iwl4965_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 + +/** + * iwl4965_rx_reply_tx - Handle standard (non-aggregation) Tx response + */ +static void iwl4965_rx_reply_tx(struct iwl4965_priv *priv, + struct iwl4965_rx_mem_buffer *rxb) +{ + struct iwl4965_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 iwl4965_tx_queue *txq = &priv->txq[txq_id]; + struct ieee80211_tx_status *tx_status; + struct iwl4965_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; + u32 status = le32_to_cpu(tx_resp->status); +#ifdef CONFIG_IWL4965_HT +#ifdef CONFIG_IWL4965_HT_AGG + int tid, sta_id; +#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.write_ptr, + txq->q.read_ptr); + return; + } + +#ifdef CONFIG_IWL4965_HT +#ifdef CONFIG_IWL4965_HT_AGG + if (txq->sched_retry) { + const u32 scd_ssn = iwl4965_get_scd_ssn(tx_resp); + struct ieee80211_hdr *hdr = + iwl4965_tx_queue_get_hdr(priv, txq_id, index); + struct iwl4965_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 = iwl4965_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) && + !iwl4965_is_tx_success(status)) { + /* TODO: send BAR */ + } + + if ((txq->q.read_ptr != (scd_ssn & 0xff))) { + index = iwl4965_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); + iwl4965_tx_queue_reclaim(priv, txq_id, index); + } + } else { +#endif /* CONFIG_IWL4965_HT_AGG */ +#endif /* CONFIG_IWL4965_HT */ + tx_status = &(txq->txb[txq->q.read_ptr].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 = + iwl4965_is_tx_success(status) ? IEEE80211_TX_STATUS_ACK : 0; + + tx_status->control.tx_rate = + iwl4965_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, iwl4965_get_tx_fail_reason(status), + status, le32_to_cpu(tx_resp->rate_n_flags), + tx_resp->failure_frame); + + IWL_DEBUG_TX_REPLY("Tx queue reclaim %d\n", index); + if (index != -1) + iwl4965_tx_queue_reclaim(priv, txq_id, index); +#ifdef CONFIG_IWL4965_HT +#ifdef CONFIG_IWL4965_HT_AGG + } +#endif /* CONFIG_IWL4965_HT_AGG */ +#endif /* CONFIG_IWL4965_HT */ + + if (iwl_check_bits(status, TX_ABORT_REQUIRED_MSK)) + IWL_ERROR("TODO: Implement Tx ABORT REQUIRED!!!\n"); +} + + +static void iwl4965_rx_reply_alive(struct iwl4965_priv *priv, + struct iwl4965_rx_mem_buffer *rxb) +{ + struct iwl4965_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl4965_alive_resp *palive; + struct work_struct *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 iwl4965_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 iwl4965_alive_resp)); + pwork = &priv->alive_start; + } + + /* 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 iwl4965_rx_reply_add_sta(struct iwl4965_priv *priv, + struct iwl4965_rx_mem_buffer *rxb) +{ + struct iwl4965_rx_packet *pkt = (void *)rxb->skb->data; + + IWL_DEBUG_RX("Received REPLY_ADD_STA: 0x%02X\n", pkt->u.status); + return; +} + +static void iwl4965_rx_reply_error(struct iwl4965_priv *priv, + struct iwl4965_rx_mem_buffer *rxb) +{ + struct iwl4965_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 iwl4965_rx_csa(struct iwl4965_priv *priv, struct iwl4965_rx_mem_buffer *rxb) +{ + struct iwl4965_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl4965_rxon_cmd *rxon = (void *)&priv->active_rxon; + struct iwl4965_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 iwl4965_rx_spectrum_measure_notif(struct iwl4965_priv *priv, + struct iwl4965_rx_mem_buffer *rxb) +{ +#ifdef CONFIG_IWL4965_SPECTRUM_MEASUREMENT + struct iwl4965_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl4965_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 iwl4965_rx_pm_sleep_notif(struct iwl4965_priv *priv, + struct iwl4965_rx_mem_buffer *rxb) +{ +#ifdef CONFIG_IWL4965_DEBUG + struct iwl4965_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl4965_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 iwl4965_rx_pm_debug_statistics_notif(struct iwl4965_priv *priv, + struct iwl4965_rx_mem_buffer *rxb) +{ + struct iwl4965_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)); + iwl4965_print_hex_dump(IWL_DL_RADIO, pkt->u.raw, le32_to_cpu(pkt->len)); +} + +static void iwl4965_bg_beacon_update(void *p) +{ + struct iwl4965_priv *priv = p; + 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); + + iwl4965_send_beacon_cmd(priv); +} + +static void iwl4965_rx_beacon_notif(struct iwl4965_priv *priv, + struct iwl4965_rx_mem_buffer *rxb) +{ +#ifdef CONFIG_IWL4965_DEBUG + struct iwl4965_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl4965_beacon_notif *beacon = &(pkt->u.beacon_status); + u8 rate = iwl4965_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags); + + 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 iwl4965_rx_reply_scan(struct iwl4965_priv *priv, + struct iwl4965_rx_mem_buffer *rxb) +{ +#ifdef CONFIG_IWL4965_DEBUG + struct iwl4965_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl4965_scanreq_notification *notif = + (struct iwl4965_scanreq_notification *)pkt->u.raw; + + IWL_DEBUG_RX("Scan request status = 0x%x\n", notif->status); +#endif +} + +/* Service SCAN_START_NOTIFICATION (0x82) */ +static void iwl4965_rx_scan_start_notif(struct iwl4965_priv *priv, + struct iwl4965_rx_mem_buffer *rxb) +{ + struct iwl4965_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl4965_scanstart_notification *notif = + (struct iwl4965_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 iwl4965_rx_scan_results_notif(struct iwl4965_priv *priv, + struct iwl4965_rx_mem_buffer *rxb) +{ + struct iwl4965_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl4965_scanresults_notification *notif = + (struct iwl4965_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; + priv->next_scan_jiffies = 0; +} + +/* Service SCAN_COMPLETE_NOTIFICATION (0x84) */ +static void iwl4965_rx_scan_complete_notif(struct iwl4965_priv *priv, + struct iwl4965_rx_mem_buffer *rxb) +{ + struct iwl4965_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl4965_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; + priv->next_scan_jiffies = 0; + 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 iwl4965_rx_card_state_notif(struct iwl4965_priv *priv, + struct iwl4965_rx_mem_buffer *rxb) +{ + struct iwl4965_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 (flags & (SW_CARD_DISABLED | HW_CARD_DISABLED | + RF_CARD_DISABLED)) { + + iwl4965_write32(priv, CSR_UCODE_DRV_GP1_SET, + CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + + if (!iwl4965_grab_nic_access(priv)) { + iwl4965_write_direct32( + priv, HBUS_TARG_MBX_C, + HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); + + iwl4965_release_nic_access(priv); + } + + if (!(flags & RXON_CARD_DISABLED)) { + iwl4965_write32(priv, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + if (!iwl4965_grab_nic_access(priv)) { + iwl4965_write_direct32( + priv, HBUS_TARG_MBX_C, + HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); + + iwl4965_release_nic_access(priv); + } + } + + if (flags & RF_CARD_DISABLED) { + iwl4965_write32(priv, CSR_UCODE_DRV_GP1_SET, + CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); + iwl4965_read32(priv, CSR_UCODE_DRV_GP1); + if (!iwl4965_grab_nic_access(priv)) + iwl4965_release_nic_access(priv); + } + } + + 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 (!(flags & RXON_CARD_DISABLED)) + iwl4965_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); +} + +/** + * iwl4965_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 iwl4965_setup_rx_handlers(struct iwl4965_priv *priv) +{ + priv->rx_handlers[REPLY_ALIVE] = iwl4965_rx_reply_alive; + priv->rx_handlers[REPLY_ADD_STA] = iwl4965_rx_reply_add_sta; + priv->rx_handlers[REPLY_ERROR] = iwl4965_rx_reply_error; + priv->rx_handlers[CHANNEL_SWITCH_NOTIFICATION] = iwl4965_rx_csa; + priv->rx_handlers[SPECTRUM_MEASURE_NOTIFICATION] = + iwl4965_rx_spectrum_measure_notif; + priv->rx_handlers[PM_SLEEP_NOTIFICATION] = iwl4965_rx_pm_sleep_notif; + priv->rx_handlers[PM_DEBUG_STATISTIC_NOTIFIC] = + iwl4965_rx_pm_debug_statistics_notif; + priv->rx_handlers[BEACON_NOTIFICATION] = iwl4965_rx_beacon_notif; + + /* + * The same handler is used for both the REPLY to a discrete + * statistics request from the host as well as for the periodic + * statistics notifications (after received beacons) from the uCode. + */ + priv->rx_handlers[REPLY_STATISTICS_CMD] = iwl4965_hw_rx_statistics; + priv->rx_handlers[STATISTICS_NOTIFICATION] = iwl4965_hw_rx_statistics; + + priv->rx_handlers[REPLY_SCAN_CMD] = iwl4965_rx_reply_scan; + priv->rx_handlers[SCAN_START_NOTIFICATION] = iwl4965_rx_scan_start_notif; + priv->rx_handlers[SCAN_RESULTS_NOTIFICATION] = + iwl4965_rx_scan_results_notif; + priv->rx_handlers[SCAN_COMPLETE_NOTIFICATION] = + iwl4965_rx_scan_complete_notif; + priv->rx_handlers[CARD_STATE_NOTIFICATION] = iwl4965_rx_card_state_notif; + priv->rx_handlers[REPLY_TX] = iwl4965_rx_reply_tx; + + /* Set up hardware specific Rx handlers */ + iwl4965_hw_rx_handler_setup(priv); +} + +/** + * iwl4965_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 iwl4965_tx_cmd_complete(struct iwl4965_priv *priv, + struct iwl4965_rx_mem_buffer *rxb) +{ + struct iwl4965_rx_packet *pkt = (struct iwl4965_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 iwl4965_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; + + iwl4965_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 + * + * Driver allocates a circular buffer of Receive Buffer Descriptors (RBDs), + * each of which point to Receive Buffers to be filled by 4965. These get + * used not only for Rx frames, but for any command response or notification + * from the 4965. The driver and 4965 manage the Rx buffers by means + * of indexes into the circular buffer. + * + * 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 iwl->rxq->rx_free. When + * iwl->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled + * to replenish the iwl->rxq->rx_free. + * + In iwl4965_rx_replenish (scheduled) if 'processed' != 'read' then the + * iwl->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 iwl->rxq. The driver 'processed' index is updated. + * + The Host/Firmware iwl->rxq is replenished at tasklet time from the rx_free + * list. If there are no allocated buffers in iwl->rxq->rx_free, the READ + * INDEX is not incremented and iwl->status(RX_STALLED) is set. If there + * were enough free buffers and RX_STALLED is set it is cleared. + * + * + * Driver sequence: + * + * iwl4965_rx_queue_alloc() Allocates rx_free + * iwl4965_rx_replenish() Replenishes rx_free list from rx_used, and calls + * iwl4965_rx_queue_restock + * iwl4965_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 iwl4965_rx_replenish + * + * -- enable interrupts -- + * ISR - iwl4965_rx() Detach iwl4965_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 iwl4965_rx_queue_restock to refill any empty + * slots. + * ... + * + */ + +/** + * iwl4965_rx_queue_space - Return number of free slots available in queue. + */ +static int iwl4965_rx_queue_space(const struct iwl4965_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; +} + +/** + * iwl4965_rx_queue_update_write_ptr - Update the write pointer for the RX queue + */ +int iwl4965_rx_queue_update_write_ptr(struct iwl4965_priv *priv, struct iwl4965_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 power-saving is in use, make sure device is awake */ + if (test_bit(STATUS_POWER_PMI, &priv->status)) { + reg = iwl4965_read32(priv, CSR_UCODE_DRV_GP1); + + if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { + iwl4965_set_bit(priv, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + goto exit_unlock; + } + + rc = iwl4965_grab_nic_access(priv); + if (rc) + goto exit_unlock; + + /* Device expects a multiple of 8 */ + iwl4965_write_direct32(priv, FH_RSCSR_CHNL0_WPTR, + q->write & ~0x7); + iwl4965_release_nic_access(priv); + + /* Else device is assumed to be awake */ + } else + /* Device expects a multiple of 8 */ + iwl4965_write32(priv, FH_RSCSR_CHNL0_WPTR, q->write & ~0x7); + + + q->need_update = 0; + + exit_unlock: + spin_unlock_irqrestore(&q->lock, flags); + return rc; +} + +/** + * iwl4965_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr + */ +static inline __le32 iwl4965_dma_addr2rbd_ptr(struct iwl4965_priv *priv, + dma_addr_t dma_addr) +{ + return cpu_to_le32((u32)(dma_addr >> 8)); +} + + +/** + * iwl4965_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. + */ +static int iwl4965_rx_queue_restock(struct iwl4965_priv *priv) +{ + struct iwl4965_rx_queue *rxq = &priv->rxq; + struct list_head *element; + struct iwl4965_rx_mem_buffer *rxb; + unsigned long flags; + int write, rc; + + spin_lock_irqsave(&rxq->lock, flags); + write = rxq->write & ~0x7; + while ((iwl4965_rx_queue_space(rxq) > 0) && (rxq->free_count)) { + /* Get next free Rx buffer, remove from free list */ + element = rxq->rx_free.next; + rxb = list_entry(element, struct iwl4965_rx_mem_buffer, list); + list_del(element); + + /* Point to Rx buffer via next RBD in circular buffer */ + rxq->bd[rxq->write] = iwl4965_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. + * Increment device's write pointer in multiples of 8. */ + 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 = iwl4965_rx_queue_update_write_ptr(priv, rxq); + if (rc) + return rc; + } + + return 0; +} + +/** + * iwl4965_rx_replenish - 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 iwl4965_rx_queue_restock. + * This is called as a scheduled work item (except for during initialization) + */ +static void iwl4965_rx_allocate(struct iwl4965_priv *priv) +{ + struct iwl4965_rx_queue *rxq = &priv->rxq; + struct list_head *element; + struct iwl4965_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 iwl4965_rx_mem_buffer, list); + + /* Alloc a new receive buffer */ + rxb->skb = + alloc_skb(priv->hw_setting.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); + + /* Get physical address of RB/SKB */ + rxb->dma_addr = + pci_map_single(priv->pci_dev, rxb->skb->data, + priv->hw_setting.rx_buf_size, PCI_DMA_FROMDEVICE); + list_add_tail(&rxb->list, &rxq->rx_free); + rxq->free_count++; + } + spin_unlock_irqrestore(&rxq->lock, flags); +} + +/* + * this should be called while priv->lock is locked +*/ +static void __iwl4965_rx_replenish(void *data) +{ + struct iwl4965_priv *priv = data; + + iwl4965_rx_allocate(priv); + iwl4965_rx_queue_restock(priv); +} + + +void iwl4965_rx_replenish(void *data) +{ + struct iwl4965_priv *priv = data; + unsigned long flags; + + iwl4965_rx_allocate(priv); + + spin_lock_irqsave(&priv->lock, flags); + iwl4965_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 its 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 + */ +static void iwl4965_rx_queue_free(struct iwl4965_priv *priv, struct iwl4965_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, + priv->hw_setting.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 iwl4965_rx_queue_alloc(struct iwl4965_priv *priv) +{ + struct iwl4965_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); + + /* Alloc the circular buffer of Read Buffer Descriptors (RBDs) */ + 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 iwl4965_rx_queue_reset(struct iwl4965_priv *priv, struct iwl4965_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, + priv->hw_setting.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 iwl4965_calc_db_from_ratio(int sig_ratio) +{ + /* 1000:1 or higher just report as 60 dB */ + if (sig_ratio >= 1000) + return 60; + + /* 100:1 or higher, 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 iwl4965_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; +} + +/** + * iwl4965_rx_handle - Main entry function for receiving responses from 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 iwl4965_rx_handle(struct iwl4965_priv *priv) +{ + struct iwl4965_rx_mem_buffer *rxb; + struct iwl4965_rx_packet *pkt; + struct iwl4965_rx_queue *rxq = &priv->rxq; + u32 r, i; + int reclaim; + unsigned long flags; + u8 fill_rx = 0; + u32 count = 0; + + /* uCode's read index (stored in shared DRAM) indicates the last Rx + * buffer that the driver may process (last buffer filled by ucode). */ + r = iwl4965_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); + + if (iwl4965_rx_queue_space(rxq) > (RX_QUEUE_SIZE / 2)) + fill_rx = 1; + + while (i != r) { + rxb = rxq->queue[i]; + + /* If an RXB doesn't have a Rx 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, + priv->hw_setting.rx_buf_size, + PCI_DMA_FROMDEVICE); + pkt = (struct iwl4965_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) && + (pkt->hdr.cmd != REPLY_RX_PHY_CMD) && + (pkt->hdr.cmd != REPLY_4965_RX) && + (pkt->hdr.cmd != REPLY_COMPRESSED_BA) && + (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 iwl4965_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 iwl4965_send_cmd() + * as we reclaim the driver command queue */ + if (rxb && rxb->skb) + iwl4965_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, + priv->hw_setting.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; + /* If there are a lot of unused frames, + * restock the Rx queue so ucode wont assert. */ + if (fill_rx) { + count++; + if (count >= 8) { + priv->rxq.read = i; + __iwl4965_rx_replenish(priv); + count = 0; + } + } + } + + /* Backtrack one entry */ + priv->rxq.read = i; + iwl4965_rx_queue_restock(priv); +} + +/** + * iwl4965_tx_queue_update_write_ptr - Send new write index to hardware + */ +static int iwl4965_tx_queue_update_write_ptr(struct iwl4965_priv *priv, + struct iwl4965_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 = iwl4965_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); + iwl4965_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 = iwl4965_grab_nic_access(priv); + if (rc) + return rc; + iwl4965_write_direct32(priv, HBUS_TARG_WRPTR, + txq->q.write_ptr | (txq_id << 8)); + iwl4965_release_nic_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 + iwl4965_write32(priv, HBUS_TARG_WRPTR, + txq->q.write_ptr | (txq_id << 8)); + + txq->need_update = 0; + + return rc; +} + +#ifdef CONFIG_IWL4965_DEBUG +static void iwl4965_print_rx_config_cmd(struct iwl4965_rxon_cmd *rxon) +{ + DECLARE_MAC_BUF(mac); + + IWL_DEBUG_RADIO("RX CONFIG:\n"); + iwl4965_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: %s\n", + print_mac(mac, rxon->node_addr)); + IWL_DEBUG_RADIO("u8[6] bssid_addr: %s\n", + print_mac(mac, rxon->bssid_addr)); + IWL_DEBUG_RADIO("u16 assoc_id: 0x%x\n", le16_to_cpu(rxon->assoc_id)); +} +#endif + +static void iwl4965_enable_interrupts(struct iwl4965_priv *priv) +{ + IWL_DEBUG_ISR("Enabling interrupts\n"); + set_bit(STATUS_INT_ENABLED, &priv->status); + iwl4965_write32(priv, CSR_INT_MASK, CSR_INI_SET_MASK); +} + +static inline void iwl4965_disable_interrupts(struct iwl4965_priv *priv) +{ + clear_bit(STATUS_INT_ENABLED, &priv->status); + + /* disable interrupts from uCode/NIC to host */ + iwl4965_write32(priv, CSR_INT_MASK, 0x00000000); + + /* acknowledge/clear/reset any interrupts still pending + * from uCode or flow handler (Rx/Tx DMA) */ + iwl4965_write32(priv, CSR_INT, 0xffffffff); + iwl4965_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 iwl4965_dump_nic_error_log(struct iwl4965_priv *priv) +{ + u32 data2, line; + 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 (!iwl4965_hw_valid_rtc_data_addr(base)) { + IWL_ERROR("Not valid error log pointer 0x%08X\n", base); + return; + } + + rc = iwl4965_grab_nic_access(priv); + if (rc) { + IWL_WARNING("Can not read from adapter at this time.\n"); + return; + } + + count = iwl4965_read_targ_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); + } + + desc = iwl4965_read_targ_mem(priv, base + 1 * sizeof(u32)); + blink1 = iwl4965_read_targ_mem(priv, base + 3 * sizeof(u32)); + blink2 = iwl4965_read_targ_mem(priv, base + 4 * sizeof(u32)); + ilink1 = iwl4965_read_targ_mem(priv, base + 5 * sizeof(u32)); + ilink2 = iwl4965_read_targ_mem(priv, base + 6 * sizeof(u32)); + data1 = iwl4965_read_targ_mem(priv, base + 7 * sizeof(u32)); + data2 = iwl4965_read_targ_mem(priv, base + 8 * sizeof(u32)); + line = iwl4965_read_targ_mem(priv, base + 9 * sizeof(u32)); + time = iwl4965_read_targ_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); + + iwl4965_release_nic_access(priv); +} + +#define EVENT_START_OFFSET (4 * sizeof(u32)) + +/** + * iwl4965_print_event_log - Dump error event log to syslog + * + * NOTE: Must be called with iwl4965_grab_nic_access() already obtained! + */ +static void iwl4965_print_event_log(struct iwl4965_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 = iwl4965_read_targ_mem(priv, ptr); + ptr += sizeof(u32); + time = iwl4965_read_targ_mem(priv, ptr); + ptr += sizeof(u32); + if (mode == 0) + IWL_ERROR("0x%08x\t%04u\n", time, ev); /* data, ev */ + else { + data = iwl4965_read_targ_mem(priv, ptr); + ptr += sizeof(u32); + IWL_ERROR("%010u\t0x%08x\t%04u\n", time, data, ev); + } + } +} + +static void iwl4965_dump_nic_event_log(struct iwl4965_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 (!iwl4965_hw_valid_rtc_data_addr(base)) { + IWL_ERROR("Invalid event log pointer 0x%08X\n", base); + return; + } + + rc = iwl4965_grab_nic_access(priv); + if (rc) { + IWL_WARNING("Can not read from adapter at this time.\n"); + return; + } + + /* event log header */ + capacity = iwl4965_read_targ_mem(priv, base); + mode = iwl4965_read_targ_mem(priv, base + (1 * sizeof(u32))); + num_wraps = iwl4965_read_targ_mem(priv, base + (2 * sizeof(u32))); + next_entry = iwl4965_read_targ_mem(priv, base + (3 * sizeof(u32))); + + size = num_wraps ? capacity : next_entry; + + /* bail out if nothing in log */ + if (size == 0) { + IWL_ERROR("Start IWL Event Log Dump: nothing in log\n"); + iwl4965_release_nic_access(priv); + return; + } + + IWL_ERROR("Start IWL 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) + iwl4965_print_event_log(priv, next_entry, + capacity - next_entry, mode); + + /* (then/else) start at top of log */ + iwl4965_print_event_log(priv, 0, next_entry, mode); + + iwl4965_release_nic_access(priv); +} + +/** + * iwl4965_irq_handle_error - called for HW or SW error interrupt from card + */ +static void iwl4965_irq_handle_error(struct iwl4965_priv *priv) +{ + /* Set the FW error flag -- cleared on iwl4965_down */ + set_bit(STATUS_FW_ERROR, &priv->status); + + /* Cancel currently queued command. */ + clear_bit(STATUS_HCMD_ACTIVE, &priv->status); + +#ifdef CONFIG_IWL4965_DEBUG + if (iwl4965_debug_level & IWL_DL_FW_ERRORS) { + iwl4965_dump_nic_error_log(priv); + iwl4965_dump_nic_event_log(priv); + iwl4965_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 (iwl4965_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 iwl4965_error_recovery(struct iwl4965_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; + iwl4965_commit_rxon(priv); + + iwl4965_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 iwl4965_irq_tasklet(struct iwl4965_priv *priv) +{ + u32 inta, handled = 0; + u32 inta_fh; + unsigned long flags; +#ifdef CONFIG_IWL4965_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 = iwl4965_read32(priv, CSR_INT); + iwl4965_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 = iwl4965_read32(priv, CSR_FH_INT_STATUS); + iwl4965_write32(priv, CSR_FH_INT_STATUS, inta_fh); + +#ifdef CONFIG_IWL4965_DEBUG + if (iwl4965_debug_level & IWL_DL_ISR) { + /* just for debug */ + inta_mask = iwl4965_read32(priv, CSR_INT_MASK); + 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 & CSR_FH_INT_RX_MASK) + inta |= CSR_INT_BIT_FH_RX; + if (inta_fh & CSR_FH_INT_TX_MASK) + inta |= CSR_INT_BIT_FH_TX; + + /* Now service all interrupt bits discovered above. */ + if (inta & CSR_INT_BIT_HW_ERR) { + IWL_ERROR("Microcode HW error detected. Restarting.\n"); + + /* Tell the device to stop sending interrupts */ + iwl4965_disable_interrupts(priv); + + iwl4965_irq_handle_error(priv); + + handled |= CSR_INT_BIT_HW_ERR; + + spin_unlock_irqrestore(&priv->lock, flags); + + return; + } + +#ifdef CONFIG_IWL4965_DEBUG + if (iwl4965_debug_level & (IWL_DL_ISR)) { + /* NIC fires this, but we don't use it, redundant with WAKEUP */ + if (inta & CSR_INT_BIT_MAC_CLK_ACTV) + IWL_DEBUG_ISR("Microcode started or stopped.\n"); + + /* Alive notification via Rx interrupt will do the real work */ + if (inta & CSR_INT_BIT_ALIVE) + IWL_DEBUG_ISR("Alive interrupt\n"); + } +#endif + /* Safely ignore these bits for debug checks below */ + inta &= ~(CSR_INT_BIT_MAC_CLK_ACTV | CSR_INT_BIT_ALIVE); + + /* HW RF KILL switch toggled */ + if (inta & CSR_INT_BIT_RF_KILL) { + int hw_rf_kill = 0; + if (!(iwl4965_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 + * iwl4965_rx_card_state_notif() */ + if (!hw_rf_kill && !test_bit(STATUS_ALIVE, &priv->status)) { + clear_bit(STATUS_RF_KILL_HW, &priv->status); + queue_work(priv->workqueue, &priv->restart); + } + + handled |= CSR_INT_BIT_RF_KILL; + } + + /* Chip got too hot and stopped itself */ + if (inta & CSR_INT_BIT_CT_KILL) { + IWL_ERROR("Microcode CT kill error detected.\n"); + handled |= CSR_INT_BIT_CT_KILL; + } + + /* Error detected by uCode */ + if (inta & CSR_INT_BIT_SW_ERR) { + IWL_ERROR("Microcode SW error detected. Restarting 0x%X.\n", + inta); + iwl4965_irq_handle_error(priv); + handled |= CSR_INT_BIT_SW_ERR; + } + + /* uCode wakes up after power-down sleep */ + if (inta & CSR_INT_BIT_WAKEUP) { + IWL_DEBUG_ISR("Wakeup interrupt\n"); + iwl4965_rx_queue_update_write_ptr(priv, &priv->rxq); + iwl4965_tx_queue_update_write_ptr(priv, &priv->txq[0]); + iwl4965_tx_queue_update_write_ptr(priv, &priv->txq[1]); + iwl4965_tx_queue_update_write_ptr(priv, &priv->txq[2]); + iwl4965_tx_queue_update_write_ptr(priv, &priv->txq[3]); + iwl4965_tx_queue_update_write_ptr(priv, &priv->txq[4]); + iwl4965_tx_queue_update_write_ptr(priv, &priv->txq[5]); + + handled |= CSR_INT_BIT_WAKEUP; + } + + /* All uCode command responses, including Tx command responses, + * Rx "responses" (frame-received notification), and other + * notifications from uCode come through here*/ + if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) { + iwl4965_rx_handle(priv); + handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX); + } + + if (inta & CSR_INT_BIT_FH_TX) { + IWL_DEBUG_ISR("Tx interrupt\n"); + handled |= CSR_INT_BIT_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 */ + iwl4965_enable_interrupts(priv); + +#ifdef CONFIG_IWL4965_DEBUG + if (iwl4965_debug_level & (IWL_DL_ISR)) { + inta = iwl4965_read32(priv, CSR_INT); + inta_mask = iwl4965_read32(priv, CSR_INT_MASK); + inta_fh = iwl4965_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 iwl4965_isr(int irq, void *data, struct pt_regs *regs) +{ + struct iwl4965_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 = iwl4965_read32(priv, CSR_INT_MASK); /* just for debug */ + iwl4965_write32(priv, CSR_INT_MASK, 0x00000000); + + /* Discover which interrupts are active/pending */ + inta = iwl4965_read32(priv, CSR_INT); + inta_fh = iwl4965_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. It might have already raised + * an interrupt */ + IWL_WARNING("HARDWARE GONE?? INTA == 0x%080x\n", inta); + goto unplugged; + } + + IWL_DEBUG_ISR("ISR inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", + inta, inta_mask, inta_fh); + + /* iwl4965_irq_tasklet() will service interrupts and re-enable them */ + tasklet_schedule(&priv->irq_tasklet); + + unplugged: + spin_unlock(&priv->lock); + return IRQ_HANDLED; + + none: + /* re-enable interrupts here since we don't have anything to service. */ + iwl4965_enable_interrupts(priv); + spin_unlock(&priv->lock); + return IRQ_NONE; +} + +/************************** EEPROM BANDS **************************** + * + * The iwl4965_eeprom_band definitions below provide the mapping from the + * EEPROM contents to the specific channel number supported for each + * band. + * + * For example, iwl4965_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 iwl4965_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 iwl4965_eeprom_band_2[] = { /* 4915-5080MHz */ + 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 +}; + +static const u8 iwl4965_eeprom_band_3[] = { /* 5170-5320MHz */ + 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 +}; + +static const u8 iwl4965_eeprom_band_4[] = { /* 5500-5700MHz */ + 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 +}; + +static const u8 iwl4965_eeprom_band_5[] = { /* 5725-5825MHz */ + 145, 149, 153, 157, 161, 165 +}; + +static u8 iwl4965_eeprom_band_6[] = { /* 2.4 FAT channel */ + 1, 2, 3, 4, 5, 6, 7 +}; + +static u8 iwl4965_eeprom_band_7[] = { /* 5.2 FAT channel */ + 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 +}; + +static void iwl4965_init_band_reference(const struct iwl4965_priv *priv, + int band, + int *eeprom_ch_count, + const struct iwl4965_eeprom_channel + **eeprom_ch_info, + const u8 **eeprom_ch_index) +{ + switch (band) { + case 1: /* 2.4GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl4965_eeprom_band_1); + *eeprom_ch_info = priv->eeprom.band_1_channels; + *eeprom_ch_index = iwl4965_eeprom_band_1; + break; + case 2: /* 4.9GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl4965_eeprom_band_2); + *eeprom_ch_info = priv->eeprom.band_2_channels; + *eeprom_ch_index = iwl4965_eeprom_band_2; + break; + case 3: /* 5.2GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl4965_eeprom_band_3); + *eeprom_ch_info = priv->eeprom.band_3_channels; + *eeprom_ch_index = iwl4965_eeprom_band_3; + break; + case 4: /* 5.5GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl4965_eeprom_band_4); + *eeprom_ch_info = priv->eeprom.band_4_channels; + *eeprom_ch_index = iwl4965_eeprom_band_4; + break; + case 5: /* 5.7GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl4965_eeprom_band_5); + *eeprom_ch_info = priv->eeprom.band_5_channels; + *eeprom_ch_index = iwl4965_eeprom_band_5; + break; + case 6: /* 2.4GHz FAT channels */ + *eeprom_ch_count = ARRAY_SIZE(iwl4965_eeprom_band_6); + *eeprom_ch_info = priv->eeprom.band_24_channels; + *eeprom_ch_index = iwl4965_eeprom_band_6; + break; + case 7: /* 5 GHz FAT channels */ + *eeprom_ch_count = ARRAY_SIZE(iwl4965_eeprom_band_7); + *eeprom_ch_info = priv->eeprom.band_52_channels; + *eeprom_ch_index = iwl4965_eeprom_band_7; + break; + default: + BUG(); + return; + } +} + +/** + * iwl4965_get_channel_info - Find driver's private channel info + * + * Based on band and channel number. + */ +const struct iwl4965_channel_info *iwl4965_get_channel_info(const struct iwl4965_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 " " : "") + +/** + * iwl4965_init_channel_map - Set up driver's info for all possible channels + */ +static int iwl4965_init_channel_map(struct iwl4965_priv *priv) +{ + int eeprom_ch_count = 0; + const u8 *eeprom_ch_index = NULL; + const struct iwl4965_eeprom_channel *eeprom_ch_info = NULL; + int band, ch; + struct iwl4965_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(iwl4965_eeprom_band_1) + + ARRAY_SIZE(iwl4965_eeprom_band_2) + + ARRAY_SIZE(iwl4965_eeprom_band_3) + + ARRAY_SIZE(iwl4965_eeprom_band_4) + + ARRAY_SIZE(iwl4965_eeprom_band_5); + + IWL_DEBUG_INFO("Parsing data for %d channels.\n", priv->channel_count); + + priv->channel_info = kzalloc(sizeof(struct iwl4965_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++) { + + iwl4965_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++; + } + } + + /* Two additional EEPROM bands for 2.4 and 5 GHz FAT channels */ + for (band = 6; band <= 7; band++) { + int phymode; + u8 fat_extension_chan; + + iwl4965_init_band_reference(priv, band, &eeprom_ch_count, + &eeprom_ch_info, &eeprom_ch_index); + + /* EEPROM band 6 is 2.4, band 7 is 5 GHz */ + 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; + + /* Set up driver's info for lower half */ + iwl4965_set_fat_chan_info(priv, phymode, + eeprom_ch_index[ch], + &(eeprom_ch_info[ch]), + fat_extension_chan); + + /* Set up driver's info for upper half */ + iwl4965_set_fat_chan_info(priv, phymode, + (eeprom_ch_index[ch] + 4), + &(eeprom_ch_info[ch]), + HT_IE_EXT_CHANNEL_BELOW); + } + } + + 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 iwl4965_get_active_dwell_time(struct iwl4965_priv *priv, int phymode) +{ + if (phymode == MODE_IEEE80211A) + return IWL_ACTIVE_DWELL_TIME_52; + else + return IWL_ACTIVE_DWELL_TIME_24; +} + +static u16 iwl4965_get_passive_dwell_time(struct iwl4965_priv *priv, int phymode) +{ + u16 active = iwl4965_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 (iwl4965_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 iwl4965_get_channels_for_scan(struct iwl4965_priv *priv, int phymode, + u8 is_active, u8 direct_mask, + struct iwl4965_scan_channel *scan_ch) +{ + const struct ieee80211_channel *channels = NULL; + const struct ieee80211_hw_mode *hw_mode; + const struct iwl4965_channel_info *ch_info; + u16 passive_dwell = 0; + u16 active_dwell = 0; + int added, i; + + hw_mode = iwl4965_get_hw_mode(priv, phymode); + if (!hw_mode) + return 0; + + channels = hw_mode->channels; + + active_dwell = iwl4965_get_active_dwell_time(priv, phymode); + passive_dwell = iwl4965_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 (iwl4965_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 = iwl4965_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 txpower 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 iwl4965_reset_channel_flag(struct iwl4965_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 iwl4965_init_hw_rates(struct iwl4965_priv *priv, + struct ieee80211_rate *rates) +{ + int i; + + for (i = 0; i < IWL_RATE_COUNT; i++) { + rates[i].rate = iwl4965_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 |= (iwl4965_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; + } +} + +/** + * iwl4965_init_geos - Initialize mac80211's geo/channel info based from eeprom + */ +static int iwl4965_init_geos(struct iwl4965_priv *priv) +{ + struct iwl4965_channel_info *ch; + struct ieee80211_hw_mode *modes; + struct ieee80211_channel *channels; + struct ieee80211_channel *geo_ch; + struct ieee80211_rate *rates; + int i = 0; + enum { + A = 0, + B = 1, + G = 2, + }; + int mode_count = 3; + + 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(iwl4965_eeprom_band_1)]; + modes[A].rates = rates; + modes[A].num_rates = 8; /* just OFDM */ + modes[A].rates = &rates[4]; + modes[A].num_channels = 0; +#ifdef CONFIG_IWL4965_HT + iwl4965_init_ht_hw_capab(&modes[A].ht_info, MODE_IEEE80211A); +#endif + + modes[B].mode = MODE_IEEE80211B; + modes[B].channels = channels; + modes[B].rates = rates; + 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; +#ifdef CONFIG_IWL4965_HT + iwl4965_init_ht_hw_capab(&modes[G].ht_info, MODE_IEEE80211G); +#endif + + priv->ieee_channels = channels; + priv->ieee_rates = rates; + + iwl4965_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++]; + } else { + geo_ch = &modes[B].channels[modes[B].num_channels++]; + modes[G].num_channels++; + } + + 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 iwl4965_dealloc_ucode_pci(struct iwl4965_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; + } +} + +/** + * iwl4965_verify_inst_full - verify runtime uCode image in card vs. host, + * looking at all data. + */ +static int iwl4965_verify_inst_full(struct iwl4965_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 = iwl4965_grab_nic_access(priv); + if (rc) + return rc; + + iwl4965_write_direct32(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 = _iwl4965_read_direct32(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; + } + } + + iwl4965_release_nic_access(priv); + + if (!errcnt) + IWL_DEBUG_INFO + ("ucode image in INSTRUCTION memory is good\n"); + + return rc; +} + + +/** + * iwl4965_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 iwl4965_verify_inst_sparse(struct iwl4965_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 = iwl4965_grab_nic_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 */ + iwl4965_write_direct32(priv, HBUS_TARG_MEM_RADDR, + i + RTC_INST_LOWER_BOUND); + val = _iwl4965_read_direct32(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; + } + } + + iwl4965_release_nic_access(priv); + + return rc; +} + + +/** + * iwl4965_verify_ucode - determine which instruction image is in SRAM, + * and verify its contents + */ +static int iwl4965_verify_ucode(struct iwl4965_priv *priv) +{ + __le32 *image; + u32 len; + int rc = 0; + + /* Try bootstrap */ + image = (__le32 *)priv->ucode_boot.v_addr; + len = priv->ucode_boot.len; + rc = iwl4965_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 = iwl4965_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 = iwl4965_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"); + + /* Since nothing seems to match, show first several data entries in + * instruction SRAM, so maybe visual inspection will give a clue. + * Selection of bootstrap image (vs. other images) is arbitrary. */ + image = (__le32 *)priv->ucode_boot.v_addr; + len = priv->ucode_boot.len; + rc = iwl4965_verify_inst_full(priv, image, len); + + return rc; +} + + +/* check contents of special bootstrap uCode SRAM */ +static int iwl4965_verify_bsm(struct iwl4965_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 = iwl4965_read_prph(priv, BSM_WR_DWCOUNT_REG); + for (reg = BSM_SRAM_LOWER_BOUND; + reg < BSM_SRAM_LOWER_BOUND + len; + reg += sizeof(u32), image ++) { + val = iwl4965_read_prph(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; +} + +/** + * iwl4965_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 iwl4965_load_bsm(struct iwl4965_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 ... host DRAM physical address bits 35:4 for 4965. + * NOTE: iwl4965_initialize_alive_start() will replace these values, + * after the "initialize" uCode has run, to point to + * runtime/protocol instructions and backup data cache. */ + pinst = priv->ucode_init.p_addr >> 4; + pdata = priv->ucode_init_data.p_addr >> 4; + inst_len = priv->ucode_init.len; + data_len = priv->ucode_init_data.len; + + rc = iwl4965_grab_nic_access(priv); + if (rc) + return rc; + + iwl4965_write_prph(priv, BSM_DRAM_INST_PTR_REG, pinst); + iwl4965_write_prph(priv, BSM_DRAM_DATA_PTR_REG, pdata); + iwl4965_write_prph(priv, BSM_DRAM_INST_BYTECOUNT_REG, inst_len); + iwl4965_write_prph(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++) + _iwl4965_write_prph(priv, reg_offset, + le32_to_cpu(*image)); + + rc = iwl4965_verify_bsm(priv); + if (rc) { + iwl4965_release_nic_access(priv); + return rc; + } + + /* Tell BSM to copy from BSM SRAM into instruction SRAM, when asked */ + iwl4965_write_prph(priv, BSM_WR_MEM_SRC_REG, 0x0); + iwl4965_write_prph(priv, BSM_WR_MEM_DST_REG, + RTC_INST_LOWER_BOUND); + iwl4965_write_prph(priv, BSM_WR_DWCOUNT_REG, len / sizeof(u32)); + + /* Load bootstrap code into instruction SRAM now, + * to prepare to load "initialize" uCode */ + iwl4965_write_prph(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 = iwl4965_read_prph(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) */ + iwl4965_write_prph(priv, BSM_WR_CTRL_REG, + BSM_WR_CTRL_REG_BIT_START_EN); + + iwl4965_release_nic_access(priv); + + return 0; +} + +static void iwl4965_nic_start(struct iwl4965_priv *priv) +{ + /* Remove all resets to allow NIC to operate */ + iwl4965_write32(priv, CSR_RESET, 0); +} + +static int iwl4965_alloc_fw_desc(struct pci_dev *pci_dev, struct fw_desc *desc) +{ + desc->v_addr = pci_alloc_consistent(pci_dev, desc->len, &desc->p_addr); + return (desc->v_addr != NULL) ? 0 : -ENOMEM; +} + +/** + * iwl4965_read_ucode - Read uCode images from disk file. + * + * Copy into buffers for card to fetch via bus-mastering + */ +static int iwl4965_read_ucode(struct iwl4965_priv *priv) +{ + struct iwl4965_ucode *ucode; + int ret; + const struct firmware *ucode_raw; + const char *name = "iwlwifi-4965" IWL4965_UCODE_API ".ucode"; + 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. */ + ret = request_firmware(&ucode_raw, name, &priv->pci_dev->dev); + if (ret < 0) { + IWL_ERROR("%s firmware file req failed: Reason %d\n", + name, ret); + 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"); + ret = -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); + ret = -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\n", + inst_size); + ret = -EINVAL; + goto err_release; + } + + if (data_size > IWL_MAX_DATA_SIZE) { + IWL_DEBUG_INFO("uCode data len %d too large to fit in\n", + data_size); + ret = -EINVAL; + goto err_release; + } + if (init_size > IWL_MAX_INST_SIZE) { + IWL_DEBUG_INFO + ("uCode init instr len %d too large to fit in\n", + init_size); + ret = -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\n", + init_data_size); + ret = -EINVAL; + goto err_release; + } + if (boot_size > IWL_MAX_BSM_SIZE) { + IWL_DEBUG_INFO + ("uCode boot instr len %d too large to fit in\n", + boot_size); + ret = -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; + iwl4965_alloc_fw_desc(priv->pci_dev, &priv->ucode_code); + + priv->ucode_data.len = data_size; + iwl4965_alloc_fw_desc(priv->pci_dev, &priv->ucode_data); + + priv->ucode_data_backup.len = data_size; + iwl4965_alloc_fw_desc(priv->pci_dev, &priv->ucode_data_backup); + + /* Initialization instructions and data */ + if (init_size && init_data_size) { + priv->ucode_init.len = init_size; + iwl4965_alloc_fw_desc(priv->pci_dev, &priv->ucode_init); + + priv->ucode_init_data.len = init_data_size; + iwl4965_alloc_fw_desc(priv->pci_dev, &priv->ucode_init_data); + + if (!priv->ucode_init.v_addr || !priv->ucode_init_data.v_addr) + goto err_pci_alloc; + } + + /* Bootstrap (instructions only, no data) */ + if (boot_size) { + priv->ucode_boot.len = boot_size; + iwl4965_alloc_fw_desc(priv->pci_dev, &priv->ucode_boot); + + if (!priv->ucode_boot.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 %Zd\n", 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 iwl4965_up() */ + src = &ucode->data[inst_size]; + len = priv->ucode_data.len; + IWL_DEBUG_INFO("Copying (but not loading) uCode data len %Zd\n", 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 %Zd\n", + 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 %Zd\n", + 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 %Zd\n", 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"); + ret = -ENOMEM; + iwl4965_dealloc_ucode_pci(priv); + + err_release: + release_firmware(ucode_raw); + + error: + return ret; +} + + +/** + * iwl4965_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 iwl4965_set_ucode_ptrs(struct iwl4965_priv *priv) +{ + dma_addr_t pinst; + dma_addr_t pdata; + int rc = 0; + unsigned long flags; + + /* bits 35:4 for 4965 */ + pinst = priv->ucode_code.p_addr >> 4; + pdata = priv->ucode_data_backup.p_addr >> 4; + + spin_lock_irqsave(&priv->lock, flags); + rc = iwl4965_grab_nic_access(priv); + if (rc) { + spin_unlock_irqrestore(&priv->lock, flags); + return rc; + } + + /* Tell bootstrap uCode where to find image to load */ + iwl4965_write_prph(priv, BSM_DRAM_INST_PTR_REG, pinst); + iwl4965_write_prph(priv, BSM_DRAM_DATA_PTR_REG, pdata); + iwl4965_write_prph(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 */ + iwl4965_write_prph(priv, BSM_DRAM_INST_BYTECOUNT_REG, + priv->ucode_code.len | BSM_DRAM_INST_LOAD); + + iwl4965_release_nic_access(priv); + + spin_unlock_irqrestore(&priv->lock, flags); + + IWL_DEBUG_INFO("Runtime uCode pointers are set.\n"); + + return rc; +} + +/** + * iwl4965_init_alive_start - Called after REPLY_ALIVE notification received + * + * 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 iwl4965_init_alive_start(struct iwl4965_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 (iwl4965_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; + } + + /* Calculate temperature */ + priv->temperature = iwl4965_get_temperature(priv); + + /* 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 (iwl4965_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); +} + + +/** + * iwl4965_alive_start - called after REPLY_ALIVE notification received + * from protocol/runtime uCode (initialization uCode's + * Alive gets handled by iwl4965_init_alive_start()). + */ +static void iwl4965_alive_start(struct iwl4965_priv *priv) +{ + int rc = 0; + + 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 (iwl4965_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; + } + + iwl4965_clear_stations_table(priv); + + rc = iwl4965_alive_notify(priv); + if (rc) { + IWL_WARNING("Could not complete ALIVE transition [ntf]: %d\n", + rc); + goto restart; + } + + /* After the ALIVE response, we can send host commands to 4965 uCode */ + set_bit(STATUS_ALIVE, &priv->status); + + /* Clear out the uCode error bit if it is set */ + clear_bit(STATUS_FW_ERROR, &priv->status); + + rc = iwl4965_init_channel_map(priv); + if (rc) { + IWL_ERROR("initializing regulatory failed: %d\n", rc); + return; + } + + iwl4965_init_geos(priv); + + if (iwl4965_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); + iwl4965_rate_control_register(priv->hw); + rc = ieee80211_register_hw(priv->hw); + priv->hw->conf.beacon_int = 100; + mutex_lock(&priv->mutex); + + if (rc) { + iwl4965_rate_control_unregister(priv->hw); + IWL_ERROR("Failed to register network " + "device (error %d)\n", rc); + return; + } + + priv->mac80211_registered = 1; + + iwl4965_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; + + iwl4965_send_power_mode(priv, IWL_POWER_LEVEL(priv->power_mode)); + + if (iwl4965_is_associated(priv)) { + struct iwl4965_rxon_cmd *active_rxon = + (struct iwl4965_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 */ + iwl4965_connection_init_rx_config(priv); + memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN); + } + + /* Configure Bluetooth device coexistence support */ + iwl4965_send_bt_config(priv); + + /* Configure the adapter for unassociated operation */ + iwl4965_commit_rxon(priv); + + /* At this point, the NIC is initialized and operational */ + priv->notif_missed_beacons = 0; + set_bit(STATUS_READY, &priv->status); + + iwl4965_rf_kill_ct_config(priv); + IWL_DEBUG_INFO("ALIVE processing complete.\n"); + + if (priv->error_recovering) + iwl4965_error_recovery(priv); + + return; + + restart: + queue_work(priv->workqueue, &priv->restart); +} + +static void iwl4965_cancel_deferred_work(struct iwl4965_priv *priv); + +static void __iwl4965_down(struct iwl4965_priv *priv) +{ + unsigned long flags; + int exit_pending = test_bit(STATUS_EXIT_PENDING, &priv->status); + struct ieee80211_conf *conf = NULL; + + IWL_DEBUG_INFO(DRV_NAME " is going down\n"); + + conf = ieee80211_get_hw_conf(priv->hw); + + if (!exit_pending) + set_bit(STATUS_EXIT_PENDING, &priv->status); + + iwl4965_clear_stations_table(priv); + + /* Unblock any waiting calls */ + wake_up_interruptible_all(&priv->wait_command_queue); + + iwl4965_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 */ + iwl4965_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); + + /* tell the device to stop sending interrupts */ + iwl4965_disable_interrupts(priv); + + if (priv->mac80211_registered) + ieee80211_stop_queues(priv->hw); + + /* If we have not previously called iwl4965_init() then + * clear all bits but the RF Kill and SUSPEND bits and return */ + if (!iwl4965_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); + iwl4965_clear_bit(priv, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + spin_unlock_irqrestore(&priv->lock, flags); + + iwl4965_hw_txq_ctx_stop(priv); + iwl4965_hw_rxq_stop(priv); + + spin_lock_irqsave(&priv->lock, flags); + if (!iwl4965_grab_nic_access(priv)) { + iwl4965_write_prph(priv, APMG_CLK_DIS_REG, + APMG_CLK_VAL_DMA_CLK_RQT); + iwl4965_release_nic_access(priv); + } + spin_unlock_irqrestore(&priv->lock, flags); + + udelay(5); + + iwl4965_hw_nic_stop_master(priv); + iwl4965_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); + iwl4965_hw_nic_reset(priv); + + exit: + memset(&priv->card_alive, 0, sizeof(struct iwl4965_alive_resp)); + + if (priv->ibss_beacon) + dev_kfree_skb(priv->ibss_beacon); + priv->ibss_beacon = NULL; + + /* clear out any free frames */ + iwl4965_clear_free_frames(priv); +} + +static void iwl4965_down(struct iwl4965_priv *priv) +{ + mutex_lock(&priv->mutex); + __iwl4965_down(priv); + mutex_unlock(&priv->mutex); +} + +#define MAX_HW_RESTARTS 5 + +static int __iwl4965_up(struct iwl4965_priv *priv) +{ + DECLARE_MAC_BUF(mac); + int rc, i; + u32 hw_rf_kill = 0; + + 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; + } + + iwl4965_write32(priv, CSR_INT, 0xFFFFFFFF); + + rc = iwl4965_hw_nic_init(priv); + if (rc) { + IWL_ERROR("Unable to int nic\n"); + return rc; + } + + /* make sure rfkill handshake bits are cleared */ + iwl4965_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + iwl4965_write32(priv, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + + /* clear (again), then enable host interrupts */ + iwl4965_write32(priv, CSR_INT, 0xFFFFFFFF); + iwl4965_enable_interrupts(priv); + + /* really make sure rfkill handshake bits are cleared */ + iwl4965_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + iwl4965_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 platform's RF_KILL switch is set to KILL, + * wait for BIT_INT_RF_KILL interrupt before loading uCode + * and getting things started */ + if (!(iwl4965_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; + } + + for (i = 0; i < MAX_HW_RESTARTS; i++) { + + iwl4965_clear_stations_table(priv); + + /* load bootstrap state machine, + * load bootstrap program into processor's memory, + * prepare to load the "initialize" uCode */ + rc = iwl4965_load_bsm(priv); + + if (rc) { + IWL_ERROR("Unable to set up bootstrap uCode: %d\n", rc); + continue; + } + + /* start card; "initialize" will load runtime ucode */ + iwl4965_nic_start(priv); + + /* MAC Address location in EEPROM is same for 3945/4965 */ + get_eeprom_mac(priv, priv->mac_addr); + IWL_DEBUG_INFO("MAC address: %s\n", + print_mac(mac, priv->mac_addr)); + + SET_IEEE80211_PERM_ADDR(priv->hw, priv->mac_addr); + + IWL_DEBUG_INFO(DRV_NAME " is coming up\n"); + + return 0; + } + + set_bit(STATUS_EXIT_PENDING, &priv->status); + __iwl4965_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 iwl4965_bg_init_alive_start(void *p) +{ + struct iwl4965_priv *priv = p; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + mutex_lock(&priv->mutex); + iwl4965_init_alive_start(priv); + mutex_unlock(&priv->mutex); +} + +static void iwl4965_bg_alive_start(void *p) +{ + struct iwl4965_priv *priv = p; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + mutex_lock(&priv->mutex); + iwl4965_alive_start(priv); + mutex_unlock(&priv->mutex); +} + +static void iwl4965_bg_rf_kill(void *p) +{ + struct iwl4965_priv *priv = p; + + wake_up_interruptible(&priv->wait_command_queue); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + mutex_lock(&priv->mutex); + + if (!iwl4965_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 iwl4965_bg_scan_check(void *p) +{ + struct iwl4965_priv *priv = p; + + 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)) + iwl4965_send_scan_abort(priv); + } + mutex_unlock(&priv->mutex); +} + +static void iwl4965_bg_request_scan(void *p) +{ + struct iwl4965_priv *priv = p; + struct iwl4965_host_cmd cmd = { + .id = REPLY_SCAN_CMD, + .len = sizeof(struct iwl4965_scan_cmd), + .meta.flags = CMD_SIZE_HUGE, + }; + int rc = 0; + struct iwl4965_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 (!iwl4965_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 (iwl4965_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 iwl4965_scan_cmd) + + IWL_MAX_SCAN_SIZE, GFP_KERNEL); + if (!priv->scan) { + rc = -ENOMEM; + goto done; + } + } + scan = priv->scan; + memset(scan, 0, sizeof(struct iwl4965_scan_cmd) + IWL_MAX_SCAN_SIZE); + + scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH; + scan->quiet_time = IWL_ACTIVE_QUIET_TIME; + + if (iwl4965_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(200 * 1024); + if (!interval) + interval = suspend_time; + + extra = (suspend_time / interval) << 22; + scan_suspend_time = (extra | + ((suspend_time % interval) * 1024)); + 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", + iwl4965_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 (!iwl4965_is_associated(priv) && priv->essid_len) { + 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( + iwl4965_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 = priv->hw_setting.bcast_sta_id; + scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; + + /* flags + rate selection */ + + scan->tx_cmd.tx_flags |= cpu_to_le32(0x200); + + switch (priv->scan_bands) { + case 2: + scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK; + scan->tx_cmd.rate_n_flags = + iwl4965_hw_set_rate_n_flags(IWL_RATE_1M_PLCP, + RATE_MCS_ANT_B_MSK|RATE_MCS_CCK_MSK); + + scan->good_CRC_th = 0; + phymode = MODE_IEEE80211G; + break; + + case 1: + scan->tx_cmd.rate_n_flags = + iwl4965_hw_set_rate_n_flags(IWL_RATE_6M_PLCP, + RATE_MCS_ANT_B_MSK); + scan->good_CRC_th = IWL_GOOD_CRC_TH; + phymode = MODE_IEEE80211A; + break; + + default: + IWL_WARNING("Invalid scan band count\n"); + goto done; + } + + /* select Rx chains */ + + /* 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)); + + 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", + iwl4965_escape_essid(priv->essid, priv->essid_len)); + else + IWL_DEBUG_SCAN("Initiating indirect scan.\n"); + + scan->channel_count = + iwl4965_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 iwl4965_scan_channel); + cmd.data = scan; + scan->len = cpu_to_le16(cmd.len); + + set_bit(STATUS_SCAN_HW, &priv->status); + rc = iwl4965_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 scan aborted */ + queue_work(priv->workqueue, &priv->scan_completed); + mutex_unlock(&priv->mutex); +} + +static void iwl4965_bg_up(void *p) +{ + struct iwl4965_priv *priv = p; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + mutex_lock(&priv->mutex); + __iwl4965_up(priv); + mutex_unlock(&priv->mutex); +} + +static void iwl4965_bg_restart(void *p) +{ + struct iwl4965_priv *priv = p; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + iwl4965_down(priv); + queue_work(priv->workqueue, &priv->up); +} + +static void iwl4965_bg_rx_replenish(void *p) +{ + struct iwl4965_priv *priv = p; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + mutex_lock(&priv->mutex); + iwl4965_rx_replenish(priv); + mutex_unlock(&priv->mutex); +} + +#define IWL_DELAY_NEXT_SCAN (HZ*2) + +static void iwl4965_bg_post_associate(void *p) +{ + struct iwl4965_priv *priv = p; + + int rc = 0; + struct ieee80211_conf *conf = NULL; + DECLARE_MAC_BUF(mac); + + if (priv->iw_mode == IEEE80211_IF_TYPE_AP) { + IWL_ERROR("%s Should not be called in AP mode\n", __FUNCTION__); + return; + } + + IWL_DEBUG_ASSOC("Associated as %d to: %s\n", + priv->assoc_id, + print_mac(mac, priv->active_rxon.bssid_addr)); + + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + mutex_lock(&priv->mutex); + + if (!priv->interface_id || !priv->is_open) { + mutex_unlock(&priv->mutex); + return; + } + iwl4965_scan_cancel_timeout(priv, 200); + + conf = ieee80211_get_hw_conf(priv->hw); + + priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + iwl4965_commit_rxon(priv); + + memset(&priv->rxon_timing, 0, sizeof(struct iwl4965_rxon_time_cmd)); + iwl4965_setup_rxon_timing(priv); + rc = iwl4965_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; + +#ifdef CONFIG_IWL4965_HT + if (priv->current_ht_config.is_ht) + iwl4965_set_rxon_ht(priv, &priv->current_ht_config); +#endif /* CONFIG_IWL4965_HT*/ + iwl4965_set_rxon_chain(priv); + 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; + + } + + iwl4965_commit_rxon(priv); + + switch (priv->iw_mode) { + case IEEE80211_IF_TYPE_STA: + iwl4965_rate_scale_init(priv->hw, IWL_AP_ID); + break; + + case IEEE80211_IF_TYPE_IBSS: + + /* clear out the station table */ + iwl4965_clear_stations_table(priv); + + iwl4965_rxon_add_station(priv, iwl4965_broadcast_addr, 0); + iwl4965_rxon_add_station(priv, priv->bssid, 0); + iwl4965_rate_scale_init(priv->hw, IWL_STA_ID); + iwl4965_send_beacon_cmd(priv); + + break; + + default: + IWL_ERROR("%s Should not be called in %d mode\n", + __FUNCTION__, priv->iw_mode); + break; + } + + iwl4965_sequence_reset(priv); + +#ifdef CONFIG_IWL4965_SENSITIVITY + /* Enable Rx differential gain and sensitivity calibrations */ + iwl4965_chain_noise_reset(priv); + priv->start_calib = 1; +#endif /* CONFIG_IWL4965_SENSITIVITY */ + + if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS) + priv->assoc_station_added = 1; + +#ifdef CONFIG_IWL4965_QOS + iwl4965_activate_qos(priv, 0); +#endif /* CONFIG_IWL4965_QOS */ + /* we have just associated, don't start scan too early */ + priv->next_scan_jiffies = jiffies + IWL_DELAY_NEXT_SCAN; + mutex_unlock(&priv->mutex); +} + +static void iwl4965_bg_abort_scan(void *p) +{ + struct iwl4965_priv *priv = p; + + if (!iwl4965_is_ready(priv)) + return; + + mutex_lock(&priv->mutex); + + set_bit(STATUS_SCAN_ABORTING, &priv->status); + iwl4965_send_scan_abort(priv); + + mutex_unlock(&priv->mutex); +} + +static int iwl4965_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf); + +static void iwl4965_bg_scan_completed(void *p) +{ + struct iwl4965_priv *priv = p; + + IWL_DEBUG(IWL_DL_INFO | IWL_DL_SCAN, "SCAN complete scan\n"); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (priv->cache_conf) + iwl4965_mac_config(priv->hw, priv->cache_conf); + + ieee80211_scan_completed(priv->hw); + + /* Since setting the TXPOWER may have been deferred while + * performing the scan, fire one off */ + mutex_lock(&priv->mutex); + iwl4965_hw_reg_send_txpower(priv); + mutex_unlock(&priv->mutex); +} + +/***************************************************************************** + * + * mac80211 entry point functions + * + *****************************************************************************/ + +static int iwl4965_mac_start(struct ieee80211_hw *hw) +{ + struct iwl4965_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 (!iwl4965_is_rfkill(priv)) + ieee80211_start_queues(priv->hw); + + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211("leave\n"); + return 0; +} + +static void iwl4965_mac_stop(struct ieee80211_hw *hw) +{ + struct iwl4965_priv *priv = hw->priv; + + IWL_DEBUG_MAC80211("enter\n"); + + + mutex_lock(&priv->mutex); + /* stop mac, cancel any scan request and clear + * RXON_FILTER_ASSOC_MSK BIT + */ + priv->is_open = 0; + if (!iwl4965_is_ready_rf(priv)) { + IWL_DEBUG_MAC80211("leave - RF not ready\n"); + mutex_unlock(&priv->mutex); + return; + } + + iwl4965_scan_cancel_timeout(priv, 100); + cancel_delayed_work(&priv->post_associate); + priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + iwl4965_commit_rxon(priv); + mutex_unlock(&priv->mutex); + + IWL_DEBUG_MAC80211("leave\n"); +} + +static int iwl4965_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_tx_control *ctl) +{ + struct iwl4965_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 (iwl4965_tx_skb(priv, skb, ctl)) + dev_kfree_skb_any(skb); + + IWL_DEBUG_MAC80211("leave\n"); + return 0; +} + +static int iwl4965_mac_add_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct iwl4965_priv *priv = hw->priv; + unsigned long flags; + DECLARE_MAC_BUF(mac); + + IWL_DEBUG_MAC80211("enter: id %d, type %d\n", conf->if_id, conf->type); + + 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); + + if (conf->mac_addr) { + IWL_DEBUG_MAC80211("Set %s\n", print_mac(mac, conf->mac_addr)); + memcpy(priv->mac_addr, conf->mac_addr, ETH_ALEN); + } + iwl4965_set_mode(priv, conf->type); + + IWL_DEBUG_MAC80211("leave\n"); + mutex_unlock(&priv->mutex); + + return 0; +} + +/** + * iwl4965_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 iwl4965_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf) +{ + struct iwl4965_priv *priv = hw->priv; + const struct iwl4965_channel_info *ch_info; + unsigned long flags; + int ret = 0; + + mutex_lock(&priv->mutex); + IWL_DEBUG_MAC80211("enter to channel %d\n", conf->channel); + + if (!iwl4965_is_ready(priv)) { + IWL_DEBUG_MAC80211("leave - not ready\n"); + ret = -EIO; + goto out; + } + + /* TODO: Figure out how to get ieee80211_local->sta_scanning w/ only + * what is exposed through include/ declarations */ + if (unlikely(!iwl4965_param_disable_hw_scan && + test_bit(STATUS_SCANNING, &priv->status))) { + + if (unlikely(priv->cache_conf)) + IWL_DEBUG_MAC80211("leave - still scanning\n"); + else { + /* Cache the configuration now so that we can + * replay it after the hardware scan is finished. */ + priv->cache_conf = kmalloc(sizeof(*conf), GFP_KERNEL); + if (priv->cache_conf) { + memcpy(priv->cache_conf, conf, sizeof(*conf)); + IWL_DEBUG_MAC80211("leave - scanning\n"); + } else { + IWL_DEBUG_MAC80211("leave - no memory\n"); + ret = -ENOMEM; + } + } + mutex_unlock(&priv->mutex); + return ret; + } + + spin_lock_irqsave(&priv->lock, flags); + + ch_info = iwl4965_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); + ret = -EINVAL; + goto out; + } + +#ifdef CONFIG_IWL4965_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_IWL4965_HT */ + + iwl4965_set_rxon_channel(priv, conf->phymode, conf->channel); + + iwl4965_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 */ + iwl4965_set_rate(priv); + + spin_unlock_irqrestore(&priv->lock, flags); + +#ifdef IEEE80211_CONF_CHANNEL_SWITCH + if (conf->flags & IEEE80211_CONF_CHANNEL_SWITCH) { + iwl4965_hw_channel_switch(priv, conf->channel); + goto out; + } +#endif + + iwl4965_radio_kill_sw(priv, !conf->radio_enabled); + + if (!conf->radio_enabled) { + IWL_DEBUG_MAC80211("leave - radio disabled\n"); + goto out; + } + + if (iwl4965_is_rfkill(priv)) { + IWL_DEBUG_MAC80211("leave - RF kill\n"); + ret = -EIO; + goto out; + } + + iwl4965_set_rate(priv); + + if (memcmp(&priv->active_rxon, + &priv->staging_rxon, sizeof(priv->staging_rxon))) + iwl4965_commit_rxon(priv); + else + IWL_DEBUG_INFO("No re-sending same RXON configuration.\n"); + + IWL_DEBUG_MAC80211("leave\n"); + +out: + if (priv->cache_conf) { + kfree(priv->cache_conf); + priv->cache_conf = NULL; + } + mutex_unlock(&priv->mutex); + return ret; +} + +static void iwl4965_config_ap(struct iwl4965_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; + iwl4965_commit_rxon(priv); + + /* RXON Timing */ + memset(&priv->rxon_timing, 0, sizeof(struct iwl4965_rxon_time_cmd)); + iwl4965_setup_rxon_timing(priv); + rc = iwl4965_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; + iwl4965_commit_rxon(priv); +#ifdef CONFIG_IWL4965_QOS + iwl4965_activate_qos(priv, 1); +#endif + iwl4965_rxon_add_station(priv, iwl4965_broadcast_addr, 0); + } + iwl4965_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 iwl4965_mac_config_interface(struct ieee80211_hw *hw, int if_id, + struct ieee80211_if_conf *conf) +{ + struct iwl4965_priv *priv = hw->priv; + DECLARE_MAC_BUF(mac); + 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: %s\n", + print_mac(mac, conf->bssid)); + +/* + * very dubious code was here; the probe filtering flag is never set: + * + if (unlikely(test_bit(STATUS_SCANNING, &priv->status)) && + !(priv->hw->flags & IEEE80211_HW_NO_PROBE_FILTERING)) { + */ + if (unlikely(test_bit(STATUS_SCANNING, &priv->status))) { + 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: %s\n", + print_mac(mac, conf->bssid)); + } + if (priv->ibss_beacon) + dev_kfree_skb(priv->ibss_beacon); + + priv->ibss_beacon = conf->beacon; + } + + if (iwl4965_is_rfkill(priv)) + goto done; + + 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 (iwl4965_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) + iwl4965_config_ap(priv); + else { + rc = iwl4965_commit_rxon(priv); + if ((priv->iw_mode == IEEE80211_IF_TYPE_STA) && rc) + iwl4965_rxon_add_station( + priv, priv->active_rxon.bssid_addr, 1); + } + + } else { + iwl4965_scan_cancel_timeout(priv, 100); + priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + iwl4965_commit_rxon(priv); + } + + done: + 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 iwl4965_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + int mc_count, struct dev_addr_list *mc_list) +{ + /* + * XXX: dummy + * see also iwl4965_connection_init_rx_config + */ + *total_flags = 0; +} + +static void iwl4965_mac_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct iwl4965_priv *priv = hw->priv; + + IWL_DEBUG_MAC80211("enter\n"); + + mutex_lock(&priv->mutex); + + if (iwl4965_is_ready_rf(priv)) { + iwl4965_scan_cancel_timeout(priv, 100); + cancel_delayed_work(&priv->post_associate); + priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + iwl4965_commit_rxon(priv); + } + 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"); + +} +static void iwl4965_mac_erp_ie_changed(struct ieee80211_hw *hw, + u8 changes, int cts_protection, int preamble) +{ + struct iwl4965_priv *priv = hw->priv; + + if (changes & IEEE80211_ERP_CHANGE_PREAMBLE) { + if (preamble == WLAN_ERP_PREAMBLE_SHORT) + priv->staging_rxon.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; + else + priv->staging_rxon.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; + } + + if (changes & IEEE80211_ERP_CHANGE_PROTECTION) { + if (cts_protection && (priv->phymode != MODE_IEEE80211A)) + priv->staging_rxon.flags |= RXON_FLG_TGG_PROTECT_MSK; + else + priv->staging_rxon.flags &= ~RXON_FLG_TGG_PROTECT_MSK; + } + + if (iwl4965_is_associated(priv)) + iwl4965_send_rxon_assoc(priv); +} + +static int iwl4965_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t len) +{ + int rc = 0; + unsigned long flags; + struct iwl4965_priv *priv = hw->priv; + + IWL_DEBUG_MAC80211("enter\n"); + + mutex_lock(&priv->mutex); + spin_lock_irqsave(&priv->lock, flags); + + if (!iwl4965_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; + } + + /* we don't schedule scan within next_scan_jiffies period */ + if (priv->next_scan_jiffies && + time_after(priv->next_scan_jiffies, jiffies)) { + rc = -EAGAIN; + 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 ", + iwl4965_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); + } else + priv->one_direct_scan = 0; + + rc = iwl4965_scan_initiate(priv); + + IWL_DEBUG_MAC80211("leave\n"); + +out_unlock: + spin_unlock_irqrestore(&priv->lock, flags); + mutex_unlock(&priv->mutex); + + return rc; +} + +static int iwl4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + const u8 *local_addr, const u8 *addr, + struct ieee80211_key_conf *key) +{ + struct iwl4965_priv *priv = hw->priv; + DECLARE_MAC_BUF(mac); + int rc = 0; + u8 sta_id; + + IWL_DEBUG_MAC80211("enter\n"); + + if (!iwl4965_param_hwcrypto) { + IWL_DEBUG_MAC80211("leave - hwcrypto disabled\n"); + return -EOPNOTSUPP; + } + + if (is_zero_ether_addr(addr)) + /* only support pairwise keys */ + return -EOPNOTSUPP; + + sta_id = iwl4965_hw_find_station(priv, addr); + if (sta_id == IWL_INVALID_STATION) { + IWL_DEBUG_MAC80211("leave - %s not in station map.\n", + print_mac(mac, addr)); + return -EINVAL; + } + + mutex_lock(&priv->mutex); + + iwl4965_scan_cancel_timeout(priv, 100); + + switch (cmd) { + case SET_KEY: + rc = iwl4965_update_sta_key_info(priv, key, sta_id); + if (!rc) { + iwl4965_set_rxon_hwcrypto(priv, 1); + iwl4965_commit_rxon(priv); + key->hw_key_idx = sta_id; + IWL_DEBUG_MAC80211("set_key success, using hwcrypto\n"); + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + } + break; + case DISABLE_KEY: + rc = iwl4965_clear_sta_key_info(priv, sta_id); + if (!rc) { + iwl4965_set_rxon_hwcrypto(priv, 0); + iwl4965_commit_rxon(priv); + IWL_DEBUG_MAC80211("disable hwcrypto key\n"); + } + break; + default: + rc = -EINVAL; + } + + IWL_DEBUG_MAC80211("leave\n"); + mutex_unlock(&priv->mutex); + + return rc; +} + +static int iwl4965_mac_conf_tx(struct ieee80211_hw *hw, int queue, + const struct ieee80211_tx_queue_params *params) +{ + struct iwl4965_priv *priv = hw->priv; +#ifdef CONFIG_IWL4965_QOS + unsigned long flags; + int q; +#endif /* CONFIG_IWL4965_QOS */ + + IWL_DEBUG_MAC80211("enter\n"); + + if (!iwl4965_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_IWL4965_QOS + if (!priv->qos_data.qos_enable) { + priv->qos_data.qos_active = 0; + IWL_DEBUG_MAC80211("leave - qos not enabled\n"); + return 0; + } + q = AC_NUM - 1 - queue; + + spin_lock_irqsave(&priv->lock, flags); + + priv->qos_data.def_qos_parm.ac[q].cw_min = cpu_to_le16(params->cw_min); + priv->qos_data.def_qos_parm.ac[q].cw_max = cpu_to_le16(params->cw_max); + priv->qos_data.def_qos_parm.ac[q].aifsn = params->aifs; + priv->qos_data.def_qos_parm.ac[q].edca_txop = + cpu_to_le16((params->burst_time * 100)); + + priv->qos_data.def_qos_parm.ac[q].reserved1 = 0; + priv->qos_data.qos_active = 1; + + spin_unlock_irqrestore(&priv->lock, flags); + + mutex_lock(&priv->mutex); + if (priv->iw_mode == IEEE80211_IF_TYPE_AP) + iwl4965_activate_qos(priv, 1); + else if (priv->assoc_id && iwl4965_is_associated(priv)) + iwl4965_activate_qos(priv, 0); + + mutex_unlock(&priv->mutex); + +#endif /*CONFIG_IWL4965_QOS */ + + IWL_DEBUG_MAC80211("leave\n"); + return 0; +} + +static int iwl4965_mac_get_tx_stats(struct ieee80211_hw *hw, + struct ieee80211_tx_queue_stats *stats) +{ + struct iwl4965_priv *priv = hw->priv; + int i, avail; + struct iwl4965_tx_queue *txq; + struct iwl4965_queue *q; + unsigned long flags; + + IWL_DEBUG_MAC80211("enter\n"); + + if (!iwl4965_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 = iwl4965_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 iwl4965_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 iwl4965_mac_get_tsf(struct ieee80211_hw *hw) +{ + IWL_DEBUG_MAC80211("enter\n"); + IWL_DEBUG_MAC80211("leave\n"); + + return 0; +} + +static void iwl4965_mac_reset_tsf(struct ieee80211_hw *hw) +{ + struct iwl4965_priv *priv = hw->priv; + unsigned long flags; + + mutex_lock(&priv->mutex); + IWL_DEBUG_MAC80211("enter\n"); + + priv->lq_mngr.lq_ready = 0; +#ifdef CONFIG_IWL4965_HT + spin_lock_irqsave(&priv->lock, flags); + memset(&priv->current_ht_config, 0, sizeof(struct iwl_ht_info)); + spin_unlock_irqrestore(&priv->lock, flags); +#ifdef CONFIG_IWL4965_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 iwl4965_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_IWL4965_HT_AGG */ +#endif /* CONFIG_IWL4965_HT */ + +#ifdef CONFIG_IWL4965_QOS + iwl4965_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; + priv->assoc_station_added = 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); + + if (!iwl4965_is_ready_rf(priv)) { + IWL_DEBUG_MAC80211("leave - not ready\n"); + mutex_unlock(&priv->mutex); + return; + } + + /* we are restarting association process + * clear RXON_FILTER_ASSOC_MSK bit + */ + if (priv->iw_mode != IEEE80211_IF_TYPE_AP) { + iwl4965_scan_cancel_timeout(priv, 100); + priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + iwl4965_commit_rxon(priv); + } + + /* 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; + } + + priv->only_active_channel = 0; + + iwl4965_set_rate(priv); + + mutex_unlock(&priv->mutex); + + IWL_DEBUG_MAC80211("leave\n"); + +} + +static int iwl4965_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_tx_control *control) +{ + struct iwl4965_priv *priv = hw->priv; + unsigned long flags; + + mutex_lock(&priv->mutex); + IWL_DEBUG_MAC80211("enter\n"); + + if (!iwl4965_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_IWL4965_QOS + iwl4965_reset_qos(priv); +#endif + + queue_work(priv->workqueue, &priv->post_associate); + + mutex_unlock(&priv->mutex); + + return 0; +} + +#ifdef CONFIG_IWL4965_HT + +static void iwl4965_ht_info_fill(struct ieee80211_conf *conf, + struct iwl4965_priv *priv) +{ + struct iwl_ht_info *iwl_conf = &priv->current_ht_config; + struct ieee80211_ht_info *ht_conf = &conf->ht_conf; + struct ieee80211_ht_bss_info *ht_bss_conf = &conf->ht_bss_conf; + + IWL_DEBUG_MAC80211("enter: \n"); + + if (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE)) { + iwl_conf->is_ht = 0; + return; + } + + iwl_conf->is_ht = 1; + priv->ps_mode = (u8)((ht_conf->cap & IEEE80211_HT_CAP_MIMO_PS) >> 2); + + if (ht_conf->cap & IEEE80211_HT_CAP_SGI_20) + iwl_conf->sgf |= 0x1; + if (ht_conf->cap & IEEE80211_HT_CAP_SGI_40) + iwl_conf->sgf |= 0x2; + + iwl_conf->is_green_field = !!(ht_conf->cap & IEEE80211_HT_CAP_GRN_FLD); + iwl_conf->max_amsdu_size = + !!(ht_conf->cap & IEEE80211_HT_CAP_MAX_AMSDU); + iwl_conf->supported_chan_width = + !!(ht_conf->cap & IEEE80211_HT_CAP_SUP_WIDTH); + iwl_conf->tx_mimo_ps_mode = + (u8)((ht_conf->cap & IEEE80211_HT_CAP_MIMO_PS) >> 2); + memcpy(iwl_conf->supp_mcs_set, ht_conf->supp_mcs_set, 16); + + iwl_conf->control_channel = ht_bss_conf->primary_channel; + iwl_conf->extension_chan_offset = + ht_bss_conf->bss_cap & IEEE80211_HT_IE_CHA_SEC_OFFSET; + iwl_conf->tx_chan_width = + !!(ht_bss_conf->bss_cap & IEEE80211_HT_IE_CHA_WIDTH); + iwl_conf->ht_protection = + ht_bss_conf->bss_op_mode & IEEE80211_HT_IE_HT_PROTECTION; + iwl_conf->non_GF_STA_present = + !!(ht_bss_conf->bss_op_mode & IEEE80211_HT_IE_NON_GF_STA_PRSNT); + + IWL_DEBUG_MAC80211("control channel %d\n", + iwl_conf->control_channel); + IWL_DEBUG_MAC80211("leave\n"); +} + +static int iwl4965_mac_conf_ht(struct ieee80211_hw *hw, + struct ieee80211_conf *conf) +{ + struct iwl4965_priv *priv = hw->priv; + + IWL_DEBUG_MAC80211("enter: \n"); + + iwl4965_ht_info_fill(conf, priv); + 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:\n"); + return 0; +} + +static void iwl4965_set_ht_capab(struct ieee80211_hw *hw, + struct ieee80211_ht_cap *ht_cap, + u8 use_current_config) +{ + struct ieee80211_conf *conf = &hw->conf; + struct ieee80211_hw_mode *mode = conf->mode; + + if (use_current_config) { + ht_cap->cap_info = cpu_to_le16(conf->ht_conf.cap); + memcpy(ht_cap->supp_mcs_set, + conf->ht_conf.supp_mcs_set, 16); + } else { + ht_cap->cap_info = cpu_to_le16(mode->ht_info.cap); + memcpy(ht_cap->supp_mcs_set, + mode->ht_info.supp_mcs_set, 16); + } + ht_cap->ampdu_params_info = + (mode->ht_info.ampdu_factor & IEEE80211_HT_CAP_AMPDU_FACTOR) | + ((mode->ht_info.ampdu_density << 2) & + IEEE80211_HT_CAP_AMPDU_DENSITY); +} + +#endif /*CONFIG_IWL4965_HT*/ + +/***************************************************************************** + * + * sysfs attributes + * + *****************************************************************************/ + +#ifdef CONFIG_IWL4965_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/iwl/) + * used for controlling the debug level. + * + * See the level definitions in iwl for details. + */ + +static ssize_t show_debug_level(struct device_driver *d, char *buf) +{ + return sprintf(buf, "0x%08X\n", iwl4965_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 + iwl4965_debug_level = val; + + return strnlen(buf, count); +} + +static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO, + show_debug_level, store_debug_level); + +#endif /* CONFIG_IWL4965_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 iwl4965_priv *priv = (struct iwl4965_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 iwl4965_priv *priv = (struct iwl4965_priv *)d->driver_data; + + mutex_lock(&priv->mutex); + iwl4965_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 iwl4965_priv *priv = (struct iwl4965_priv *)d->driver_data; + + if (!iwl4965_is_alive(priv)) + return -EAGAIN; + + return sprintf(buf, "%d\n", iwl4965_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 iwl4965_priv *priv = d->driver_data; + return iwl4965_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 iwl4965_priv *priv = (struct iwl4965_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 iwl4965_priv *priv = (struct iwl4965_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 + iwl4965_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 iwl4965_priv *priv = (struct iwl4965_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 iwl4965_priv *priv = (struct iwl4965_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 (iwl4965_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); + iwl4965_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 iwl4965_priv *priv = (struct iwl4965_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 iwl4965_priv *priv = (struct iwl4965_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 (iwl4965_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); + iwl4965_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 iwl4965_priv *priv = (struct iwl4965_priv *)d->driver_data; + + return sprintf(buf, "0x%04X\n", + (priv->phymode << 8) | + le16_to_cpu(priv->active_rxon.channel)); +} + +static void iwl4965_set_flags_for_phymode(struct iwl4965_priv *priv, u8 phymode); + +static ssize_t store_tune(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct iwl4965_priv *priv = (struct iwl4965_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 iwl4965_channel_info *ch_info; + + ch_info = iwl4965_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 (iwl4965_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); + + iwl4965_set_rxon_channel(priv, phymode, channel); + iwl4965_set_flags_for_phymode(priv, phymode); + + iwl4965_set_rate(priv); + iwl4965_commit_rxon(priv); + } + } + mutex_unlock(&priv->mutex); + + return count; +} + +static DEVICE_ATTR(tune, S_IWUSR | S_IRUGO, show_tune, store_tune); + +#ifdef CONFIG_IWL4965_SPECTRUM_MEASUREMENT + +static ssize_t show_measurement(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct iwl4965_priv *priv = dev_get_drvdata(d); + struct iwl4965_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); + 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 iwl4965_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); + iwl4965_get_measurement(priv, ¶ms, type); + + return count; +} + +static DEVICE_ATTR(measurement, S_IRUSR | S_IWUSR, + show_measurement, store_measurement); +#endif /* CONFIG_IWL4965_SPECTRUM_MEASUREMENT */ + +static ssize_t store_retry_rate(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct iwl4965_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 iwl4965_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 iwl4965_priv *priv = dev_get_drvdata(d); + int rc; + int mode; + + mode = simple_strtoul(buf, NULL, 0); + mutex_lock(&priv->mutex); + + if (!iwl4965_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 = iwl4965_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 iwl4965_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 iwl4965_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 (!iwl4965_is_ready(priv)) + return -EAGAIN; + + hw_mode = iwl4965_get_hw_mode(priv, MODE_IEEE80211G); + if (!hw_mode) + hw_mode = iwl4965_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 = iwl4965_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 iwl4965_priv *priv = dev_get_drvdata(d); + u32 size = sizeof(struct iwl4965_notif_statistics); + u32 len = 0, ofs = 0; + u8 *data = (u8 *) & priv->statistics; + int rc = 0; + + if (!iwl4965_is_alive(priv)) + return -EAGAIN; + + mutex_lock(&priv->mutex); + rc = iwl4965_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); + 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 iwl4965_priv *priv = dev_get_drvdata(d); + + if (!iwl4965_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 iwl4965_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 iwl4965_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 iwl4965_priv *priv = (struct iwl4965_priv *)d->driver_data; + if (!iwl4965_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') + iwl4965_dump_nic_error_log((struct iwl4965_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') + iwl4965_dump_nic_event_log((struct iwl4965_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 iwl4965_setup_deferred_work(struct iwl4965_priv *priv) +{ + priv->workqueue = create_workqueue(DRV_NAME); + + init_waitqueue_head(&priv->wait_command_queue); + + INIT_WORK(&priv->up, iwl4965_bg_up, priv); + INIT_WORK(&priv->restart, iwl4965_bg_restart, priv); + INIT_WORK(&priv->rx_replenish, iwl4965_bg_rx_replenish, priv); + INIT_WORK(&priv->scan_completed, iwl4965_bg_scan_completed, priv); + INIT_WORK(&priv->request_scan, iwl4965_bg_request_scan, priv); + INIT_WORK(&priv->abort_scan, iwl4965_bg_abort_scan, priv); + INIT_WORK(&priv->rf_kill, iwl4965_bg_rf_kill, priv); + INIT_WORK(&priv->beacon_update, iwl4965_bg_beacon_update, priv); + INIT_WORK(&priv->post_associate, iwl4965_bg_post_associate, priv); + INIT_WORK(&priv->init_alive_start, iwl4965_bg_init_alive_start, priv); + INIT_WORK(&priv->alive_start, iwl4965_bg_alive_start, priv); + INIT_WORK(&priv->scan_check, iwl4965_bg_scan_check, priv); + + iwl4965_hw_setup_deferred_work(priv); + + tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) + iwl4965_irq_tasklet, (unsigned long)priv); +} + +static void iwl4965_cancel_deferred_work(struct iwl4965_priv *priv) +{ + iwl4965_hw_cancel_deferred_work(priv); + +#if 0 /* Not in RHEL5...(and probably wrong upstream...) */ + cancel_delayed_work_sync(&priv->init_alive_start); +#else + cancel_delayed_work(&priv->init_alive_start); + /* Make sure he has completed, since he can schedule other work */ + flush_workqueue(priv->workqueue); +#endif + cancel_delayed_work(&priv->scan_check); + cancel_delayed_work(&priv->alive_start); + cancel_delayed_work(&priv->post_associate); +#if 0 /* Not in RHEL5... */ + cancel_work_sync(&priv->beacon_update); +#else + flush_workqueue(priv->workqueue); +#endif +} + +static struct attribute *iwl4965_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_IWL4965_SPECTRUM_MEASUREMENT + &dev_attr_measurement.attr, +#endif + &dev_attr_power_level.attr, + &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 iwl4965_attribute_group = { + .name = NULL, /* put in device directory */ + .attrs = iwl4965_sysfs_entries, +}; + +static struct ieee80211_ops iwl4965_hw_ops = { + .tx = iwl4965_mac_tx, + .start = iwl4965_mac_start, + .stop = iwl4965_mac_stop, + .add_interface = iwl4965_mac_add_interface, + .remove_interface = iwl4965_mac_remove_interface, + .config = iwl4965_mac_config, + .config_interface = iwl4965_mac_config_interface, + .configure_filter = iwl4965_configure_filter, + .set_key = iwl4965_mac_set_key, + .get_stats = iwl4965_mac_get_stats, + .get_tx_stats = iwl4965_mac_get_tx_stats, + .conf_tx = iwl4965_mac_conf_tx, + .get_tsf = iwl4965_mac_get_tsf, + .reset_tsf = iwl4965_mac_reset_tsf, + .beacon_update = iwl4965_mac_beacon_update, + .erp_ie_changed = iwl4965_mac_erp_ie_changed, +#ifdef CONFIG_IWL4965_HT + .conf_ht = iwl4965_mac_conf_ht, +#ifdef CONFIG_IWL4965_HT_AGG + .ht_tx_agg_start = iwl4965_mac_ht_tx_agg_start, + .ht_tx_agg_stop = iwl4965_mac_ht_tx_agg_stop, + .ht_rx_agg_start = iwl4965_mac_ht_rx_agg_start, + .ht_rx_agg_stop = iwl4965_mac_ht_rx_agg_stop, +#endif /* CONFIG_IWL4965_HT_AGG */ +#endif /* CONFIG_IWL4965_HT */ + .hw_scan = iwl4965_mac_hw_scan +}; + +static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int err = 0; + struct iwl4965_priv *priv; + struct ieee80211_hw *hw; + int i; + + /* Disabling hardware scan means that mac80211 will perform scans + * "the hard way", rather than using device's scan. */ + if (iwl4965_param_disable_hw_scan) { + IWL_DEBUG_INFO("Disabling hw_scan\n"); + iwl4965_hw_ops.hw_scan = NULL; + } + + if ((iwl4965_param_queues_num > IWL_MAX_NUM_QUEUES) || + (iwl4965_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 iwl4965_priv), &iwl4965_hw_ops); + if (hw == NULL) { + IWL_ERROR("Can not allocate network device\n"); + err = -ENOMEM; + goto out; + } + SET_IEEE80211_DEV(hw, &pdev->dev); + + hw->rate_control_algorithm = "iwl-4965-rs"; + + IWL_DEBUG_INFO("*** LOAD DRIVER ***\n"); + priv = hw->priv; + priv->hw = hw; + + priv->pci_dev = pdev; + priv->antenna = (enum iwl4965_antenna)iwl4965_param_antenna; +#ifdef CONFIG_IWL4965_DEBUG + iwl4965_debug_level = iwl4965_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_HOST_GEN_BEACON_TEMPLATE; + + /* Default value; 4 EDCA QOS priorities */ + hw->queues = 4; +#ifdef CONFIG_IWL4965_HT +#ifdef CONFIG_IWL4965_HT_AGG + /* Enhanced value; more queues, to support 11n aggregation */ + hw->queues = 16; +#endif /* CONFIG_IWL4965_HT_AGG */ +#endif /* CONFIG_IWL4965_HT */ + + spin_lock_init(&priv->lock); + spin_lock_init(&priv->power_data.lock); + spin_lock_init(&priv->sta_lock); + spin_lock_init(&priv->hcmd_lock); + spin_lock_init(&priv->lq_mngr.lock); + + 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); + + /* Clear the driver's (not device's) station table */ + iwl4965_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 */ + + /* Disable radio (SW RF KILL) via parameter when loading driver */ + if (iwl4965_param_disable) { + set_bit(STATUS_RF_KILL_SW, &priv->status); + IWL_DEBUG_INFO("Radio disabled.\n"); + } + + priv->iw_mode = IEEE80211_IF_TYPE_STA; + + priv->ps_mode = 0; + priv->use_ant_b_for_management_frame = 1; /* start with ant B */ + priv->valid_antenna = 0x7; /* assume all 3 connected */ + priv->ps_mode = IWL_MIMO_PS_NONE; + + /* Choose which receivers/antennas to use */ + iwl4965_set_rxon_chain(priv); + + printk(KERN_INFO DRV_NAME + ": Detected Intel Wireless WiFi Link 4965AGN\n"); + + /* Device-specific setup */ + if (iwl4965_hw_set_hw_setting(priv)) { + IWL_ERROR("failed to set hw settings\n"); + mutex_unlock(&priv->mutex); + goto out_iounmap; + } + +#ifdef CONFIG_IWL4965_QOS + if (iwl4965_param_qos_enable) + priv->qos_data.qos_enable = 1; + + iwl4965_reset_qos(priv); + + priv->qos_data.qos_active = 0; + priv->qos_data.qos_cap.val = 0; +#endif /* CONFIG_IWL4965_QOS */ + + iwl4965_set_rxon_channel(priv, MODE_IEEE80211G, 6); + iwl4965_setup_deferred_work(priv); + iwl4965_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; + + iwl4965_disable_interrupts(priv); + + pci_enable_msi(pdev); + + err = request_irq(pdev->irq, iwl4965_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, &iwl4965_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 = iwl4965_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("Queueing UP work.\n"); + + queue_work(priv->workqueue, &priv->up); + + return 0; + + out_pci_alloc: + iwl4965_dealloc_ucode_pci(priv); + + sysfs_remove_group(&pdev->dev.kobj, &iwl4965_attribute_group); + + out_release_irq: + free_irq(pdev->irq, priv); + + out_disable_msi: + pci_disable_msi(pdev); + destroy_workqueue(priv->workqueue); + priv->workqueue = NULL; + iwl4965_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 iwl4965_pci_remove(struct pci_dev *pdev) +{ + struct iwl4965_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); + __iwl4965_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 iwl4965_ibss_seq, list)); + } + } + + sysfs_remove_group(&pdev->dev.kobj, &iwl4965_attribute_group); + + iwl4965_dealloc_ucode_pci(priv); + + if (priv->rxq.bd) + iwl4965_rx_queue_free(priv, &priv->rxq); + iwl4965_hw_txq_ctx_free(priv); + + iwl4965_unset_hw_setting(priv); + iwl4965_clear_stations_table(priv); + + if (priv->mac80211_registered) { + ieee80211_unregister_hw(priv->hw); + iwl4965_rate_control_unregister(priv->hw); + } + + /*netif_stop_queue(dev); */ + flush_workqueue(priv->workqueue); + + /* ieee80211_unregister_hw calls iwl4965_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 iwl4965_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct iwl4965_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. */ + __iwl4965_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 iwl4965_resume(struct iwl4965_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 iwl4965_down to be + * called *after* iwl4965_hw_nic_init() has succeeded -- which + * then lets the next init sequence succeed. So, we've + * replicated all of that NIC init code here... */ + + iwl4965_write32(priv, CSR_INT, 0xFFFFFFFF); + + iwl4965_hw_nic_init(priv); + + iwl4965_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + iwl4965_write32(priv, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + iwl4965_write32(priv, CSR_INT, 0xFFFFFFFF); + iwl4965_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + iwl4965_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + + /* tell the device to stop sending interrupts */ + iwl4965_disable_interrupts(priv); + + spin_lock_irqsave(&priv->lock, flags); + iwl4965_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + + if (!iwl4965_grab_nic_access(priv)) { + iwl4965_write_prph(priv, APMG_CLK_DIS_REG, + APMG_CLK_VAL_DMA_CLK_RQT); + iwl4965_release_nic_access(priv); + } + spin_unlock_irqrestore(&priv->lock, flags); + + udelay(5); + + iwl4965_hw_nic_reset(priv); + + /* Bring the device back up */ + clear_bit(STATUS_IN_SUSPEND, &priv->status); + queue_work(priv->workqueue, &priv->up); +} + +static int iwl4965_pci_resume(struct pci_dev *pdev) +{ + struct iwl4965_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); + + iwl4965_resume(priv); + mutex_unlock(&priv->mutex); + + return 0; +} + +#endif /* CONFIG_PM */ + +/***************************************************************************** + * + * driver and module entry point + * + *****************************************************************************/ + +static struct pci_driver iwl4965_driver = { + .name = DRV_NAME, + .id_table = iwl4965_hw_card_ids, + .probe = iwl4965_pci_probe, + .remove = __devexit_p(iwl4965_pci_remove), +#ifdef CONFIG_PM + .suspend = iwl4965_pci_suspend, + .resume = iwl4965_pci_resume, +#endif +}; + +static int __init iwl4965_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(&iwl4965_driver); + if (ret) { + IWL_ERROR("Unable to initialize PCI module\n"); + return ret; + } +#ifdef CONFIG_IWL4965_DEBUG + ret = driver_create_file(&iwl4965_driver.driver, &driver_attr_debug_level); + if (ret) { + IWL_ERROR("Unable to create driver sysfs file\n"); + pci_unregister_driver(&iwl4965_driver); + return ret; + } +#endif + + return ret; +} + +static void __exit iwl4965_exit(void) +{ +#ifdef CONFIG_IWL4965_DEBUG + driver_remove_file(&iwl4965_driver.driver, &driver_attr_debug_level); +#endif + pci_unregister_driver(&iwl4965_driver); +} + +module_param_named(antenna, iwl4965_param_antenna, int, 0444); +MODULE_PARM_DESC(antenna, "select antenna (1=Main, 2=Aux, default 0 [both])"); +module_param_named(disable, iwl4965_param_disable, int, 0444); +MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])"); +module_param_named(hwcrypto, iwl4965_param_hwcrypto, int, 0444); +MODULE_PARM_DESC(hwcrypto, + "using hardware crypto engine (default 0 [software])\n"); +module_param_named(debug, iwl4965_param_debug, int, 0444); +MODULE_PARM_DESC(debug, "debug output mask"); +module_param_named(disable_hw_scan, iwl4965_param_disable_hw_scan, int, 0444); +MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 0)"); + +module_param_named(queues_num, iwl4965_param_queues_num, int, 0444); +MODULE_PARM_DESC(queues_num, "number of hw queues."); + +/* QoS */ +module_param_named(qos_enable, iwl4965_param_qos_enable, int, 0444); +MODULE_PARM_DESC(qos_enable, "enable all QoS functionality"); +module_param_named(amsdu_size_8K, iwl4965_param_amsdu_size_8K, int, 0444); +MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size"); + +module_exit(iwl4965_exit); +module_init(iwl4965_init); diff --git a/drivers/net/wireless/iwlwifi/iwlwifi.h b/drivers/net/wireless/iwlwifi/iwlwifi.h index 0d45312..87606dd 100644 --- a/drivers/net/wireless/iwlwifi/iwlwifi.h +++ b/drivers/net/wireless/iwlwifi/iwlwifi.h @@ -31,20 +31,31 @@ #define __iwlwifi_h__ #include <linux/pci.h> /* for struct pci_device_id */ +#include <linux/kernel.h> #include <net/ieee80211_radiotap.h> +#include "iwl-compat.h" + struct iwl_priv; /* Hardware specific file defines the PCI IDs table for that hardware module */ extern struct pci_device_id iwl_hw_card_ids[]; #if IWL == 3945 + #define DRV_NAME "iwl3945" +#include "iwl-hw.h" +#include "iwl-3945-hw.h" + #elif IWL == 4965 + #define DRV_NAME "iwl4965" +#include "iwl-hw.h" +#include "iwl-4965-hw.h" + #endif -#include "iwl-hw.h" +#include "iwl-prph.h" /* * Driver implementation data structures, constants, inline @@ -58,16 +69,28 @@ extern struct pci_device_id iwl_hw_card_ids[]; #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 param_disable_hw_scan; -extern int param_debug; -extern int param_mode; -extern int param_disable; -extern int param_antenna; -extern int param_hwcrypto; -extern int param_qos_enable; - +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, @@ -103,7 +126,7 @@ struct iwl_rt_rx_hdr { __le64 rt_tsf; /* TSF */ u8 rt_flags; /* radiotap packet flags */ u8 rt_rate; /* rate in 500kb/s */ - __le16 rt_channel; /* channel in mHz */ + __le16 rt_channelMHz; /* channel in MHz */ __le16 rt_chbitmask; /* channel bitfield */ s8 rt_dbmsignal; /* signal in dBm, kluged to signed */ s8 rt_dbmnoise; @@ -133,7 +156,6 @@ struct iwl_queue { dma_addr_t dma_addr; /* physical addr for BD's */ int n_window; /* safe queue window */ u32 id; - u32 element_size; int low_mark; /* low watermark, resume queue if free * space more than this */ int high_mark; /* high watermark, stop queue if free @@ -156,7 +178,7 @@ struct iwl_tx_info { */ struct iwl_tx_queue { struct iwl_queue q; - u8 *bd; + struct iwl_tfd_frame *bd; struct iwl_cmd *cmd; dma_addr_t dma_addr_cmd; struct iwl_tx_info *txb; @@ -173,14 +195,16 @@ struct iwl_tx_queue { #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_MAX_NUM_QUEUES 16 +#define IWL_TX_FIFO_AC0 0 +#define IWL_TX_FIFO_AC1 1 +#define IWL_TX_FIFO_AC2 2 +#define IWL_TX_FIFO_AC3 3 +#define IWL_TX_FIFO_HCCA_1 5 +#define IWL_TX_FIFO_HCCA_2 6 +#define IWL_TX_FIFO_NONE 7 + +/* Minimum number of queues. MAX_NUM is defined in hw specific files */ +#define IWL_MIN_NUM_QUEUES 4 /* Power management (not Tx power) structures */ @@ -229,9 +253,10 @@ struct iwl_frame { #define SEQ_TO_INDEX(x) (x & 0xff) #define INDEX_TO_SEQ(x) (x & 0xff) #define SEQ_HUGE_FRAME (0x4000) -#define SEQ_RX_FRAME (0x8000) +#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 SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ) +#define MAX_SN ((IEEE80211_SCTL_SEQ) >> 4) enum { /* CMD_SIZE_NORMAL = 0, */ @@ -240,31 +265,23 @@ enum { CMD_ASYNC = (1 << 1), /* CMD_NO_SKB = 0, */ CMD_WANT_SKB = (1 << 2), - /* CMD_LOCK = 0, */ - CMD_NO_LOCK = (1 << 4), }; struct iwl_cmd; struct iwl_priv; -#define CMD_VAR_MAGIC 0xA987 - 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); + 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; + u32 flags; - u8 token; - u16 magic; } __attribute__ ((packed)); struct iwl_cmd { @@ -282,10 +299,8 @@ struct iwl_cmd { struct iwl_powertable_cmd powertable; struct iwl_qosparam_cmd qosparam; struct iwl_tx_cmd tx; - struct iwl_key_cmd key; struct iwl_tx_beacon_cmd tx_beacon; struct iwl_rxon_assoc_cmd rxon_assoc; - struct iwl_rate_scaling_cmd rate_scale; u8 *indirect; u8 payload[360]; } __attribute__ ((packed)) cmd; @@ -304,12 +319,8 @@ struct iwl_host_cmd { /* * RX related structures and functions */ - -#if IWL == 3945 -#define RX_SPACE_HIGH_MARK 52 -#else -#define RX_SPACE_HIGH_MARK 210 -#endif +#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 @@ -328,7 +339,7 @@ struct iwl_host_cmd { * NOTE: rx_free and rx_used are used as a FIFO for iwl_rx_mem_buffers */ struct iwl_rx_queue { - void *bd; + __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]; @@ -352,51 +363,54 @@ struct iwl_rx_queue { #define MAX_B_CHANNELS 14 #define MIN_B_CHANNELS 1 -#define STATUS_HCMD_ACTIVE (1<<0) /* host command in progress */ - -#define STATUS_INT_ENABLED (1<<1) -#define STATUS_RF_KILL_HW (1<<2) -#define STATUS_RF_KILL_SW (1<<3) -#define STATUS_RF_KILL_MASK (STATUS_RF_KILL_HW | STATUS_RF_KILL_SW) - -#define STATUS_INIT (1<<4) -#define STATUS_ALIVE (1<<5) -#define STATUS_READY (1<<6) -#define STATUS_TEMPERATURE (1<<7) -#define STATUS_GEO_CONFIGURED (1<<8) -#define STATUS_EXIT_PENDING (1<<9) -#define STATUS_IN_SUSPEND (1<<10) -#define STATUS_STATISTICS (1<<11) - -#define STATUS_AUTH (1<<13) - -#define STATUS_DISASSOCIATING (1<<15) - -#define STATUS_ROAMING (1<<16) -#define STATUS_SCANNING (1<<17) -#define STATUS_SCAN_ABORTING (1<<19) -#define STATUS_SCAN_PENDING (1<<20) -#define STATUS_SCAN_HW (1<<21) - -#define STATUS_POWER_PMI (1<<24) -#define STATUS_RESTRICTED (1<<26) -#define STATUS_FW_ERROR (1<<27) - -#define STATUS_TX_MEASURE (1<<28) - -/*todoG need to support adding adhoc station MAX_STATION should be 25 */ -#define IWL_INVALID_STATION (0xff) +#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 #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 - s8 txq_id; - u8 ht_agg_active; -#endif /* CONFIG_IWLWIFI_HT_AGG */ + struct iwl_ht_agg agg; +#endif /* CONFIG_IWLWIFI_HT_AGG */ +#endif /* CONFIG_IWLWIFI_HT */ +#endif }; struct iwl_hw_key { @@ -423,10 +437,10 @@ 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 control_chan; u8 operating_mode; u8 supported_chan_width; u8 extension_chan_offset; @@ -438,12 +452,44 @@ struct sta_ht_info { }; #endif /*CONFIG_IWLWIFI_HT */ +#ifdef CONFIG_IWLWIFI_QOS + +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; @@ -451,6 +497,7 @@ struct iwl_station_entry { } s; u16 rate_n_flags; } current_rate; +#endif u8 used; u8 ps_status; struct iwl_hw_key keyinfo; @@ -466,15 +513,15 @@ struct fw_image_desc { /* 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 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 */ + __le32 boot_size; /* bytes of bootstrap instructions */ u8 data[0]; /* data in same order as "size" elements */ }; -#define IWL_IBSS_MAC_HASH_SIZE 31 +#define IWL_IBSS_MAC_HASH_SIZE 32 struct iwl_ibss_seq { u8 mac[ETH_ALEN]; @@ -485,35 +532,31 @@ struct iwl_ibss_seq { }; struct iwl_driver_hw_info { - u16 max_queue_number; + 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; + u8 max_stations; + u8 bcast_sta_id; void *shared_virt; dma_addr_t shared_phys; }; -#define STA_FLG_RTS_MIMO_PROT_POS (17) -#define STA_FLG_RTS_MIMO_PROT_MSK (1 << STA_FLG_RTS_MIMO_PROT_POS) -#define STA_FLG_AGG_MPDU_8US_POS (18) -#define STA_FLG_AGG_MPDU_8US_MSK (1 << STA_FLG_AGG_MPDU_8US_POS) +#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 (3 << STA_FLG_MAX_AGG_SIZE_POS) -#define STA_FLG_FAT_EN_POS (21) -#define STA_FLG_FAT_EN_MSK (1 << STA_FLG_FAT_EN_POS) -#define STA_FLG_MIMO_DIS_POS (22) -#define STA_FLG_MIMO_DIS_MSK (1 << STA_FLG_MIMO_DIS_POS) +#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 (7 << STA_FLG_AGG_MPDU_DENSITY_POS) +#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" @@ -532,7 +575,7 @@ struct iwl_driver_hw_info { /****************************************************************************** * - * Functions implemented in base.c which are forward declared here + * Functions implemented in iwl-base.c which are forward declared here * for use by iwl-*.c * *****************************************************************************/ @@ -540,7 +583,7 @@ 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, +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); @@ -574,26 +617,31 @@ 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, u8 do_lock); +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 int iwl_fill_beacon_frame(struct iwl_priv *priv, - struct ieee80211_hdr *hdr, const u8 * dest, - int left); + 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 + * Currently used by iwl-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, @@ -601,25 +649,23 @@ extern u8 iwl_sync_station(struct iwl_priv *priv, int sta_id, static inline int iwl_is_associated(struct iwl_priv *priv) { - return (priv->active_rxon.filter_flags & RXON_FILTER_ASSOC_MSK) ? - 1 : 0; + return (priv->active_rxon.filter_flags & RXON_FILTER_ASSOC_MSK) ? 1 : 0; } -extern void iwl_down(struct iwl_priv *priv); /****************************************************************************** * - * Functions implemented in iwl-*.c which are forward declared here - * for use by base.c + * 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. base.c) + * 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 - * d_ <-- mac80211 callback + * iwl_mac_ <-- mac80211 callback * ****************************************************************************/ extern void iwl_hw_rx_handler_setup(struct iwl_priv *priv); @@ -633,17 +679,14 @@ 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_tx_queue_attach_buffer_to_tfd( - struct iwl_priv *priv, void *tfd, dma_addr_t addr, u16 len); -extern int iwl_hw_tx_queue_free_tfd(struct iwl_priv *priv, - struct iwl_tx_queue *txq); +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_tx_queue_free_tfd(struct iwl_priv *priv, - struct iwl_tx_queue *txq); extern int iwl_hw_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq); -extern int iwl_hw_get_beacon_cmd(struct iwl_priv *priv, - struct iwl_frame *frame, u16 rate); +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, @@ -665,7 +708,8 @@ extern int iwl4965_get_temperature(const struct iwl_priv *priv); * 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 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/include/net/mac80211_compat.h b/include/net/mac80211_compat.h index bd6ee09..6231b27 100644 --- a/include/net/mac80211_compat.h +++ b/include/net/mac80211_compat.h @@ -3,11 +3,6 @@ #define BIT(nr) (1UL << (nr)) -enum { - false = 0, - true = 1 -}; - /* * We tag multicasts with these structures. */