diff options
author | Zhu Yi <yi.zhu@intel.com> | 2007-09-25 17:54:57 -0700 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-10-10 16:51:39 -0700 |
commit | b481de9ca074528fe8c429604e2777db8b89806a (patch) | |
tree | cf226646d73c56af843e8a656a296905ad6df179 /drivers | |
parent | 75388acd0cd827dc1498043daa7d1c760902cd67 (diff) |
[IWLWIFI]: add iwlwifi wireless drivers
This patch adds the mac80211 based wireless drivers for the Intel
PRO/Wireless 3945ABG/BG Network Connection and Intel Wireless WiFi
Link AGN (4965) adapters.
[ Move driver into it's own directory -DaveM ]
Signed-off-by: Zhu Yi <yi.zhu@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
27 files changed, 34824 insertions, 0 deletions
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index c2102653b2a..085ba132835 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -577,6 +577,7 @@ config ADM8211 Thanks to Infineon-ADMtek for their support of this driver. +source "drivers/net/wireless/iwlwifi/Kconfig" source "drivers/net/wireless/hostap/Kconfig" source "drivers/net/wireless/bcm43xx/Kconfig" source "drivers/net/wireless/b43/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index d8dd907e499..351024f2fe7 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -51,3 +51,5 @@ rtl8187-objs := rtl8187_dev.o rtl8187_rtl8225.o obj-$(CONFIG_RTL8187) += rtl8187.o obj-$(CONFIG_ADM8211) += adm8211.o + +obj-$(CONFIG_IWLWIFI) += iwlwifi/ diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig new file mode 100644 index 00000000000..25cfc6c3250 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/Kconfig @@ -0,0 +1,128 @@ +config IWLWIFI + bool "Intel Wireless WiFi Link Drivers" + depends on PCI && MAC80211 && WLAN_80211 && EXPERIMENTAL + select FW_LOADER + default n + ---help--- + Select to enable drivers based on the iwlwifi project. This + project provides a common foundation for Intel's wireless + drivers designed to use the mac80211 subsystem. + + See <file:Documentation/networking/README.iwlwifi> for + information on the capabilities currently enabled in this + driver and for tips for debugging issues and problems. + +config IWLWIFI_DEBUG + bool "Enable full debugging output in iwlwifi drivers" + depends on IWLWIFI + default y + ---help--- + This option will enable debug tracing output for the iwlwifi + drivers. + + This will result in the kernel module being ~100k larger. You can + control which debug output is sent to the kernel log by setting the + value in + + /sys/bus/pci/drivers/${DRIVER}/debug_level + + This entry will only exist if this option is enabled. + + To set a value, simply echo an 8-byte hex value to the same file: + + % echo 0x43fff > /sys/bus/pci/drivers/${DRIVER}/debug_level + + You can find the list of debug mask values in: + drivers/net/wireless/mac80211/iwlwifi/iwl-debug.h + + If this is your first time using this driver, you should say Y here + as the debug information can assist others in helping you resolve + any problems you may encounter. + +config IWLWIFI_SENSITIVITY + bool "Enable Sensitivity Calibration in iwlwifi drivers" + depends on IWLWIFI + default y + ---help--- + This option will enable sensitivity calibration for the iwlwifi + drivers. + +config IWLWIFI_SPECTRUM_MEASUREMENT + bool "Enable Spectrum Measurement in iwlwifi drivers" + depends on IWLWIFI + default y + ---help--- + This option will enable spectrum measurement for the iwlwifi drivers. + +config IWLWIFI_QOS + bool "Enable Wireless QoS in iwlwifi drivers" + depends on IWLWIFI + default y + ---help--- + This option will enable wireless quality of service (QoS) for the + iwlwifi drivers. + +config IWLWIFI_HT + bool "Enable 802.11n HT features in iwlwifi drivers" + depends on EXPERIMENTAL + depends on IWLWIFI && MAC80211_HT + default n + ---help--- + This option enables IEEE 802.11n High Throughput features + for the iwlwifi drivers. + +config IWL4965 + tristate "Intel Wireless WiFi 4965AGN" + depends on m && IWLWIFI && EXPERIMENTAL + default m + ---help--- + Select to build the driver supporting the: + + Intel Wireless WiFi Link 4965AGN + + This driver uses the kernel's mac80211 subsystem. + + See <file:Documentation/networking/README.iwlwifi> for + information on the capabilities currently enabled in this + driver and for tips for debugging any issues or problems. + + In order to use this driver, you will need a microcode (uCode) + image for it. You can obtain the microcode from: + + <http://intellinuxwireless.org/>. + + See the above referenced README.iwlwifi for information on where + to install the microcode images. + + If you want to compile the driver as a module ( = code which can be + inserted in and remvoed from the running kernel whenever you want), + say M here and read <file:Documentation/modules.txt>. The module + will be called iwl4965.ko. + +config IWL3945 + tristate "Intel PRO/Wireless 3945ABG/BG Network Connection" + depends on m && IWLWIFI && EXPERIMENTAL + default m + ---help--- + Select to build the driver supporting the: + + Intel PRO/Wireless 3945ABG/BG Network Connection + + This driver uses the kernel's mac80211 subsystem. + + See <file:Documentation/networking/README.iwlwifi> for + information on the capabilities currently enabled in this + driver and for tips for debugging any issues or problems. + + In order to use this driver, you will need a microcode (uCode) + image for it. You can obtain the microcode from: + + <http://intellinuxwireless.org/>. + + See the above referenced README.iwlwifi for information on where + to install the microcode images. + + If you want to compile the driver as a module ( = code which can be + inserted in and remvoed from the running kernel whenever you want), + say M here and read <file:Documentation/modules.txt>. The module + will be called iwl3945.ko. diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile new file mode 100644 index 00000000000..03837ff5431 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/Makefile @@ -0,0 +1,11 @@ +obj-$(CONFIG_IWL3945) += iwl3945.o +iwl3945-objs = iwl3945-base.o iwl-3945.o iwl-3945-rs.o +CFLAGS_iwl3945-base.o = -DIWL=3945 +CFLAGS_iwl-3945.o = -DIWL=3945 +CFLAGS_iwl-3945-rs.o = -DIWL=3945 + +obj-$(CONFIG_IWL4965) += iwl4965.o +iwl4965-objs = iwl4965-base.o iwl-4965.o iwl-4965-rs.o +CFLAGS_iwl4965-base.o = -DIWL=4965 +CFLAGS_iwl-4965.o = -DIWL=4965 +CFLAGS_iwl-4965-rs.o = -DIWL=4965 diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-hw.h b/drivers/net/wireless/iwlwifi/iwl-3945-hw.h new file mode 100644 index 00000000000..fb5f0649f4f --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-3945-hw.h @@ -0,0 +1,118 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2005 - 2007 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU Geeral Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * James P. Ketrenos <ipw2100-admin@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2007 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __iwl_3945_hw__ +#define __iwl_3945_hw__ + +#define IWL_RX_BUF_SIZE 3000 +/* card static random access memory (SRAM) for processor data and instructs */ +#define ALM_RTC_INST_UPPER_BOUND (0x014000) +#define ALM_RTC_DATA_UPPER_BOUND (0x808000) + +#define ALM_RTC_INST_SIZE (ALM_RTC_INST_UPPER_BOUND - RTC_INST_LOWER_BOUND) +#define ALM_RTC_DATA_SIZE (ALM_RTC_DATA_UPPER_BOUND - RTC_DATA_LOWER_BOUND) + +#define IWL_MAX_BSM_SIZE ALM_RTC_INST_SIZE +#define IWL_MAX_INST_SIZE ALM_RTC_INST_SIZE +#define IWL_MAX_DATA_SIZE ALM_RTC_DATA_SIZE +#define IWL_MAX_NUM_QUEUES 8 + +static inline int iwl_hw_valid_rtc_data_addr(u32 addr) +{ + return (addr >= RTC_DATA_LOWER_BOUND) && + (addr < ALM_RTC_DATA_UPPER_BOUND); +} + +/* Base physical address of iwl_shared is provided to FH_TSSR_CBB_BASE + * and &iwl_shared.rx_read_ptr[0] is provided to FH_RCSR_RPTR_ADDR(0) */ +struct iwl_shared { + __le32 tx_base_ptr[8]; + __le32 rx_read_ptr[3]; +} __attribute__ ((packed)); + +struct iwl_tfd_frame_data { + __le32 addr; + __le32 len; +} __attribute__ ((packed)); + +struct iwl_tfd_frame { + __le32 control_flags; + struct iwl_tfd_frame_data pa[4]; + u8 reserved[28]; +} __attribute__ ((packed)); + +static inline u8 iwl_hw_get_rate(__le16 rate_n_flags) +{ + return le16_to_cpu(rate_n_flags) & 0xFF; +} + +static inline u16 iwl_hw_get_rate_n_flags(__le16 rate_n_flags) +{ + return le16_to_cpu(rate_n_flags); +} + +static inline __le16 iwl_hw_set_rate_n_flags(u8 rate, u16 flags) +{ + return cpu_to_le16((u16)rate|flags); +} +#endif diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c new file mode 100644 index 00000000000..a4f4c8798a8 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c @@ -0,0 +1,979 @@ +/****************************************************************************** + * + * Copyright(c) 2005 - 2007 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * James P. Ketrenos <ipw2100-admin@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/skbuff.h> +#include <linux/wireless.h> +#include <net/mac80211.h> +#include <net/ieee80211.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/delay.h> + +#include <linux/workqueue.h> + +#include <net/mac80211.h> +#include <linux/wireless.h> + +#include "../net/mac80211/ieee80211_rate.h" + +#include "iwlwifi.h" + +#define RS_NAME "iwl-3945-rs" + +struct iwl_rate_scale_data { + u64 data; + s32 success_counter; + s32 success_ratio; + s32 counter; + s32 average_tpt; + unsigned long stamp; +}; + +struct iwl_rate_scale_priv { + spinlock_t lock; + s32 *expected_tpt; + unsigned long last_partial_flush; + unsigned long last_flush; + u32 flush_time; + u32 last_tx_packets; + 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]; +}; + +static s32 iwl_expected_tpt_g[IWL_RATE_COUNT] = { + 0, 0, 76, 104, 130, 168, 191, 202, 7, 13, 35, 58 +}; + +static s32 iwl_expected_tpt_g_prot[IWL_RATE_COUNT] = { + 0, 0, 0, 80, 93, 113, 123, 125, 7, 13, 35, 58 +}; + +static s32 iwl_expected_tpt_a[IWL_RATE_COUNT] = { + 40, 57, 72, 98, 121, 154, 177, 186, 0, 0, 0, 0 +}; + +static s32 iwl_expected_tpt_b[IWL_RATE_COUNT] = { + 0, 0, 0, 0, 0, 0, 0, 0, 7, 13, 35, 58 +}; + +struct iwl_tpt_entry { + s8 min_rssi; + u8 index; +}; + +static struct iwl_tpt_entry iwl_tpt_table_a[] = { + {-60, IWL_RATE_54M_INDEX}, + {-64, IWL_RATE_48M_INDEX}, + {-72, IWL_RATE_36M_INDEX}, + {-80, IWL_RATE_24M_INDEX}, + {-84, IWL_RATE_18M_INDEX}, + {-85, IWL_RATE_12M_INDEX}, + {-87, IWL_RATE_9M_INDEX}, + {-89, IWL_RATE_6M_INDEX} +}; + +static struct iwl_tpt_entry iwl_tpt_table_b[] = { + {-86, IWL_RATE_11M_INDEX}, + {-88, IWL_RATE_5M_INDEX}, + {-90, IWL_RATE_2M_INDEX}, + {-92, IWL_RATE_1M_INDEX} + +}; + +static struct iwl_tpt_entry iwl_tpt_table_g[] = { + {-60, IWL_RATE_54M_INDEX}, + {-64, IWL_RATE_48M_INDEX}, + {-68, IWL_RATE_36M_INDEX}, + {-80, IWL_RATE_24M_INDEX}, + {-84, IWL_RATE_18M_INDEX}, + {-85, IWL_RATE_12M_INDEX}, + {-86, IWL_RATE_11M_INDEX}, + {-88, IWL_RATE_5M_INDEX}, + {-90, IWL_RATE_2M_INDEX}, + {-92, IWL_RATE_1M_INDEX} +}; + +#define IWL_RATE_MAX_WINDOW 62 +#define IWL_RATE_FLUSH (3*HZ/10) +#define IWL_RATE_WIN_FLUSH (HZ/2) +#define IWL_RATE_HIGH_TH 11520 +#define IWL_RATE_MIN_FAILURE_TH 8 +#define IWL_RATE_MIN_SUCCESS_TH 8 +#define IWL_RATE_DECREASE_TH 1920 + +static u8 iwl_get_rate_index_by_rssi(s32 rssi, u8 mode) +{ + u32 index = 0; + u32 table_size = 0; + struct iwl_tpt_entry *tpt_table = NULL; + + if ((rssi < IWL_MIN_RSSI_VAL) || (rssi > IWL_MAX_RSSI_VAL)) + rssi = IWL_MIN_RSSI_VAL; + + switch (mode) { + case MODE_IEEE80211G: + tpt_table = iwl_tpt_table_g; + table_size = ARRAY_SIZE(iwl_tpt_table_g); + break; + + case MODE_IEEE80211A: + tpt_table = iwl_tpt_table_a; + table_size = ARRAY_SIZE(iwl_tpt_table_a); + break; + + default: + case MODE_IEEE80211B: + tpt_table = iwl_tpt_table_b; + table_size = ARRAY_SIZE(iwl_tpt_table_b); + break; + } + + while ((index < table_size) && (rssi < tpt_table[index].min_rssi)) + index++; + + index = min(index, (table_size - 1)); + + return tpt_table[index].index; +} + +static void iwl_clear_window(struct iwl_rate_scale_data *window) +{ + window->data = 0; + window->success_counter = 0; + window->success_ratio = IWL_INVALID_VALUE; + window->counter = 0; + window->average_tpt = IWL_INVALID_VALUE; + window->stamp = 0; +} + +/** + * iwl_rate_scale_flush_windows - flush out the rate scale windows + * + * Returns the number of windows that have gathered data but were + * not flushed. If there were any that were not flushed, then + * reschedule the rate flushing routine. + */ +static int iwl_rate_scale_flush_windows(struct iwl_rate_scale_priv *rs_priv) +{ + int unflushed = 0; + int i; + unsigned long flags; + + /* + * For each rate, if we have collected data on that rate + * and it has been more than IWL_RATE_WIN_FLUSH + * since we flushed, clear out the gathered statistics + */ + for (i = 0; i < IWL_RATE_COUNT; i++) { + if (!rs_priv->win[i].counter) + continue; + + spin_lock_irqsave(&rs_priv->lock, flags); + if (time_after(jiffies, rs_priv->win[i].stamp + + IWL_RATE_WIN_FLUSH)) { + IWL_DEBUG_RATE("flushing %d samples of rate " + "index %d\n", + rs_priv->win[i].counter, i); + iwl_clear_window(&rs_priv->win[i]); + } else + unflushed++; + spin_unlock_irqrestore(&rs_priv->lock, flags); + } + + return unflushed; +} + +#define IWL_RATE_FLUSH_MAX 5000 /* msec */ +#define IWL_RATE_FLUSH_MIN 50 /* msec */ + +static void iwl_bg_rate_scale_flush(unsigned long data) +{ + struct iwl_rate_scale_priv *rs_priv = (void *)data; + int unflushed = 0; + unsigned long flags; + u32 packet_count, duration, pps; + + IWL_DEBUG_RATE("enter\n"); + + unflushed = iwl_rate_scale_flush_windows(rs_priv); + + spin_lock_irqsave(&rs_priv->lock, flags); + + rs_priv->flush_pending = 0; + + /* Number of packets Rx'd since last time this timer ran */ + packet_count = (rs_priv->tx_packets - rs_priv->last_tx_packets) + 1; + + rs_priv->last_tx_packets = rs_priv->tx_packets + 1; + + if (unflushed) { + duration = + jiffies_to_msecs(jiffies - rs_priv->last_partial_flush); +/* duration = jiffies_to_msecs(rs_priv->flush_time); */ + + IWL_DEBUG_RATE("Tx'd %d packets in %dms\n", + packet_count, duration); + + /* Determine packets per second */ + if (duration) + pps = (packet_count * 1000) / duration; + else + pps = 0; + + if (pps) { + duration = IWL_RATE_FLUSH_MAX / pps; + if (duration < IWL_RATE_FLUSH_MIN) + duration = IWL_RATE_FLUSH_MIN; + } else + duration = IWL_RATE_FLUSH_MAX; + + rs_priv->flush_time = msecs_to_jiffies(duration); + + IWL_DEBUG_RATE("new flush period: %d msec ave %d\n", + duration, packet_count); + + mod_timer(&rs_priv->rate_scale_flush, jiffies + + rs_priv->flush_time); + + rs_priv->last_partial_flush = jiffies; + } + + /* If there weren't any unflushed entries, we don't schedule the timer + * to run again */ + + rs_priv->last_flush = jiffies; + + spin_unlock_irqrestore(&rs_priv->lock, flags); + + IWL_DEBUG_RATE("leave\n"); +} + +/** + * iwl_collect_tx_data - Update the success/failure sliding window + * + * We keep a sliding window of the last 64 packets transmitted + * at this rate. window->data contains the bitmask of successful + * packets. + */ +static void iwl_collect_tx_data(struct iwl_rate_scale_priv *rs_priv, + struct iwl_rate_scale_data *window, + int success, int retries) +{ + unsigned long flags; + + if (!retries) { + IWL_DEBUG_RATE("leave: retries == 0 -- should be at least 1\n"); + return; + } + + while (retries--) { + spin_lock_irqsave(&rs_priv->lock, flags); + + /* If we have filled up the window then subtract one from the + * success counter if the high-bit is counting toward + * success */ + if (window->counter == IWL_RATE_MAX_WINDOW) { + if (window->data & (1ULL << (IWL_RATE_MAX_WINDOW - 1))) + window->success_counter--; + } else + window->counter++; + + /* Slide the window to the left one bit */ + window->data = (window->data << 1); + + /* If this packet was a success then set the low bit high */ + if (success) { + window->success_counter++; + window->data |= 1; + } + + /* window->counter can't be 0 -- it is either >0 or + * IWL_RATE_MAX_WINDOW */ + window->success_ratio = 12800 * window->success_counter / + window->counter; + + /* Tag this window as having been updated */ + window->stamp = jiffies; + + spin_unlock_irqrestore(&rs_priv->lock, flags); + } +} + +static void rs_rate_init(void *priv_rate, void *priv_sta, + struct ieee80211_local *local, struct sta_info *sta) +{ + int i; + + IWL_DEBUG_RATE("enter\n"); + + /* TODO: what is a good starting rate for STA? About middle? Maybe not + * the lowest or the highest rate.. Could consider using RSSI from + * previous packets? Need to have IEEE 802.1X auth succeed immediately + * after assoc.. */ + + for (i = IWL_RATE_COUNT - 1; i >= 0; i--) { + if (sta->supp_rates & (1 << i)) { + sta->txrate = i; + break; + } + } + + sta->last_txrate = sta->txrate; + + IWL_DEBUG_RATE("leave\n"); +} + +static void *rs_alloc(struct ieee80211_local *local) +{ + return local->hw.priv; +} + +/* rate scale requires free function to be implmented */ +static void rs_free(void *priv) +{ + 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; + int i; + + IWL_DEBUG_RATE("enter\n"); + + rs_priv = kzalloc(sizeof(struct iwl_rate_scale_priv), gfp); + if (!rs_priv) { + IWL_DEBUG_RATE("leave: ENOMEM\n"); + return NULL; + } + + spin_lock_init(&rs_priv->lock); + + rs_priv->start_rate = IWL_RATE_INVALID; + + /* default to just 802.11b */ + rs_priv->expected_tpt = iwl_expected_tpt_b; + + rs_priv->last_partial_flush = jiffies; + rs_priv->last_flush = jiffies; + rs_priv->flush_time = IWL_RATE_FLUSH; + rs_priv->last_tx_packets = 0; + rs_priv->ibss_sta_added = 0; + + init_timer(&rs_priv->rate_scale_flush); + rs_priv->rate_scale_flush.data = (unsigned long)rs_priv; + rs_priv->rate_scale_flush.function = &iwl_bg_rate_scale_flush; + + for (i = 0; i < IWL_RATE_COUNT; i++) + iwl_clear_window(&rs_priv->win[i]); + + IWL_DEBUG_RATE("leave\n"); + + return rs_priv; +} + +static void rs_free_sta(void *priv, void *priv_sta) +{ + struct iwl_rate_scale_priv *rs_priv = priv_sta; + + IWL_DEBUG_RATE("enter\n"); + del_timer_sync(&rs_priv->rate_scale_flush); + kfree(rs_priv); + IWL_DEBUG_RATE("leave\n"); +} + +/** + * rs_tx_status - Update rate control values based on Tx results + * + * NOTE: Uses iwl_priv->retry_rate for the # of retries attempted by + * the hardware for each rate. + */ +static void rs_tx_status(void *priv_rate, + struct net_device *dev, + struct sk_buff *skb, + struct ieee80211_tx_status *tx_resp) +{ + u8 retries, current_count; + int scale_rate_index, first_index, last_index; + unsigned long flags; + struct sta_info *sta; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct iwl_priv *priv = (struct iwl_priv *)priv_rate; + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct iwl_rate_scale_priv *rs_priv; + + IWL_DEBUG_RATE("enter\n"); + + retries = tx_resp->retry_count; + + first_index = tx_resp->control.tx_rate; + if ((first_index < 0) || (first_index >= IWL_RATE_COUNT)) { + IWL_DEBUG_RATE("leave: Rate out of bounds: %0x for %d\n", + tx_resp->control.tx_rate, first_index); + return; + } + + sta = sta_info_get(local, hdr->addr1); + if (!sta || !sta->rate_ctrl_priv) { + if (sta) + sta_info_put(sta); + IWL_DEBUG_RATE("leave: No STA priv data to update!\n"); + return; + } + + rs_priv = (void *)sta->rate_ctrl_priv; + + rs_priv->tx_packets++; + + scale_rate_index = first_index; + last_index = first_index; + + /* + * Update the window for each rate. We determine which rates + * were Tx'd based on the total number of retries vs. the number + * of retries configured for each rate -- currently set to the + * priv value 'retry_rate' vs. rate specific + * + * On exit from this while loop last_index indicates the rate + * at which the frame was finally transmitted (or failed if no + * ACK) + */ + while (retries > 0) { + if (retries < priv->retry_rate) { + current_count = retries; + last_index = scale_rate_index; + } else { + current_count = priv->retry_rate; + last_index = iwl_get_prev_ieee_rate(scale_rate_index); + } + + /* Update this rate accounting for as many retries + * as was used for it (per current_count) */ + iwl_collect_tx_data(rs_priv, + &rs_priv->win[scale_rate_index], + 0, current_count); + IWL_DEBUG_RATE("Update rate %d for %d retries.\n", + scale_rate_index, current_count); + + retries -= current_count; + + if (retries) + scale_rate_index = + iwl_get_prev_ieee_rate(scale_rate_index); + } + + /* Update the last index window with success/failure based on ACK */ + IWL_DEBUG_RATE("Update rate %d with %s.\n", + last_index, + (tx_resp->flags & IEEE80211_TX_STATUS_ACK) ? + "success" : "failure"); + iwl_collect_tx_data(rs_priv, + &rs_priv->win[last_index], + tx_resp->flags & IEEE80211_TX_STATUS_ACK, 1); + + /* We updated the rate scale window -- if its been more than + * flush_time since the last run, schedule the flush + * again */ + spin_lock_irqsave(&rs_priv->lock, flags); + + if (!rs_priv->flush_pending && + time_after(jiffies, rs_priv->last_partial_flush + + rs_priv->flush_time)) { + + rs_priv->flush_pending = 1; + mod_timer(&rs_priv->rate_scale_flush, + jiffies + rs_priv->flush_time); + } + + spin_unlock_irqrestore(&rs_priv->lock, flags); + + sta_info_put(sta); + + IWL_DEBUG_RATE("leave\n"); + + return; +} + +static struct ieee80211_rate *iwl_get_lowest_rate(struct ieee80211_local + *local) +{ + struct ieee80211_hw_mode *mode = local->oper_hw_mode; + int i; + + for (i = 0; i < mode->num_rates; i++) { + struct ieee80211_rate *rate = &mode->rates[i]; + + if (rate->flags & IEEE80211_RATE_SUPPORTED) + return rate; + } + + return &mode->rates[0]; +} + +static u16 iwl_get_adjacent_rate(struct iwl_rate_scale_priv *rs_priv, + u8 index, u16 rate_mask, int phymode) +{ + u8 high = IWL_RATE_INVALID; + u8 low = IWL_RATE_INVALID; + + /* 802.11A walks to the next literal adjascent rate in + * the rate table */ + if (unlikely(phymode == MODE_IEEE80211A)) { + int i; + u32 mask; + + /* Find the previous rate that is in the rate mask */ + i = index - 1; + for (mask = (1 << i); i >= 0; i--, mask >>= 1) { + if (rate_mask & mask) { + low = i; + break; + } + } + + /* Find the next rate that is in the rate mask */ + i = index + 1; + for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) { + if (rate_mask & mask) { + high = i; + break; + } + } + + return (high << 8) | low; + } + + low = index; + while (low != IWL_RATE_INVALID) { + if (rs_priv->tgg) + low = iwl_rates[low].prev_rs_tgg; + else + low = iwl_rates[low].prev_rs; + if (low == IWL_RATE_INVALID) + break; |