2007-09-26 08:54:57 +08:00
|
|
|
/******************************************************************************
|
|
|
|
*
|
2010-01-16 05:43:41 +08:00
|
|
|
* Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved.
|
2007-09-26 08:54:57 +08:00
|
|
|
*
|
|
|
|
* 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:
|
2008-12-10 03:28:58 +08:00
|
|
|
* Intel Linux Wireless <ilw@linux.intel.com>
|
2007-09-26 08:54:57 +08:00
|
|
|
* 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 cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2007-09-26 08:54:57 +08:00
|
|
|
#include <linux/wireless.h>
|
|
|
|
#include <net/mac80211.h>
|
|
|
|
|
|
|
|
#include <linux/netdevice.h>
|
|
|
|
#include <linux/etherdevice.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
|
|
|
|
#include <linux/workqueue.h>
|
|
|
|
|
2008-12-19 10:37:04 +08:00
|
|
|
#include "iwl-commands.h"
|
2007-10-25 17:15:50 +08:00
|
|
|
#include "iwl-3945.h"
|
2009-06-04 02:44:07 +08:00
|
|
|
#include "iwl-sta.h"
|
2007-09-26 08:54:57 +08:00
|
|
|
|
|
|
|
#define RS_NAME "iwl-3945-rs"
|
|
|
|
|
2008-12-19 10:37:12 +08:00
|
|
|
static s32 iwl3945_expected_tpt_g[IWL_RATE_COUNT_3945] = {
|
2007-11-12 11:37:42 +08:00
|
|
|
7, 13, 35, 58, 0, 0, 76, 104, 130, 168, 191, 202
|
2007-09-26 08:54:57 +08:00
|
|
|
};
|
|
|
|
|
2008-12-19 10:37:12 +08:00
|
|
|
static s32 iwl3945_expected_tpt_g_prot[IWL_RATE_COUNT_3945] = {
|
2007-11-12 11:37:42 +08:00
|
|
|
7, 13, 35, 58, 0, 0, 0, 80, 93, 113, 123, 125
|
2007-09-26 08:54:57 +08:00
|
|
|
};
|
|
|
|
|
2008-12-19 10:37:12 +08:00
|
|
|
static s32 iwl3945_expected_tpt_a[IWL_RATE_COUNT_3945] = {
|
2007-11-12 11:37:42 +08:00
|
|
|
0, 0, 0, 0, 40, 57, 72, 98, 121, 154, 177, 186
|
2007-09-26 08:54:57 +08:00
|
|
|
};
|
|
|
|
|
2008-12-19 10:37:12 +08:00
|
|
|
static s32 iwl3945_expected_tpt_b[IWL_RATE_COUNT_3945] = {
|
2007-11-12 11:37:42 +08:00
|
|
|
7, 13, 35, 58, 0, 0, 0, 0, 0, 0, 0, 0
|
2007-09-26 08:54:57 +08:00
|
|
|
};
|
|
|
|
|
2008-01-28 08:41:47 +08:00
|
|
|
struct iwl3945_tpt_entry {
|
2007-09-26 08:54:57 +08:00
|
|
|
s8 min_rssi;
|
|
|
|
u8 index;
|
|
|
|
};
|
|
|
|
|
2008-01-28 08:41:47 +08:00
|
|
|
static struct iwl3945_tpt_entry iwl3945_tpt_table_a[] = {
|
2007-09-26 08:54:57 +08:00
|
|
|
{-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}
|
|
|
|
};
|
|
|
|
|
2008-01-28 08:41:47 +08:00
|
|
|
static struct iwl3945_tpt_entry iwl3945_tpt_table_g[] = {
|
2007-09-26 08:54:57 +08:00
|
|
|
{-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
|
2008-12-03 04:14:03 +08:00
|
|
|
#define IWL_RATE_FLUSH (3*HZ)
|
2007-09-26 08:54:57 +08:00
|
|
|
#define IWL_RATE_WIN_FLUSH (HZ/2)
|
2008-12-19 10:37:12 +08:00
|
|
|
#define IWL39_RATE_HIGH_TH 11520
|
2008-12-03 04:14:03 +08:00
|
|
|
#define IWL_SUCCESS_UP_TH 8960
|
|
|
|
#define IWL_SUCCESS_DOWN_TH 10880
|
2009-03-18 12:51:51 +08:00
|
|
|
#define IWL_RATE_MIN_FAILURE_TH 6
|
2007-09-26 08:54:57 +08:00
|
|
|
#define IWL_RATE_MIN_SUCCESS_TH 8
|
|
|
|
#define IWL_RATE_DECREASE_TH 1920
|
2009-03-12 02:17:56 +08:00
|
|
|
#define IWL_RATE_RETRY_TH 15
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2008-01-25 02:38:38 +08:00
|
|
|
static u8 iwl3945_get_rate_index_by_rssi(s32 rssi, enum ieee80211_band band)
|
2007-09-26 08:54:57 +08:00
|
|
|
{
|
|
|
|
u32 index = 0;
|
|
|
|
u32 table_size = 0;
|
2008-01-28 08:41:47 +08:00
|
|
|
struct iwl3945_tpt_entry *tpt_table = NULL;
|
2007-09-26 08:54:57 +08:00
|
|
|
|
|
|
|
if ((rssi < IWL_MIN_RSSI_VAL) || (rssi > IWL_MAX_RSSI_VAL))
|
|
|
|
rssi = IWL_MIN_RSSI_VAL;
|
|
|
|
|
2008-01-25 02:38:38 +08:00
|
|
|
switch (band) {
|
|
|
|
case IEEE80211_BAND_2GHZ:
|
2008-01-28 08:41:47 +08:00
|
|
|
tpt_table = iwl3945_tpt_table_g;
|
|
|
|
table_size = ARRAY_SIZE(iwl3945_tpt_table_g);
|
2007-09-26 08:54:57 +08:00
|
|
|
break;
|
|
|
|
|
2008-01-25 02:38:38 +08:00
|
|
|
case IEEE80211_BAND_5GHZ:
|
2008-01-28 08:41:47 +08:00
|
|
|
tpt_table = iwl3945_tpt_table_a;
|
|
|
|
table_size = ARRAY_SIZE(iwl3945_tpt_table_a);
|
2007-09-26 08:54:57 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2008-01-25 02:38:38 +08:00
|
|
|
BUG();
|
2007-09-26 08:54:57 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((index < table_size) && (rssi < tpt_table[index].min_rssi))
|
|
|
|
index++;
|
|
|
|
|
|
|
|
index = min(index, (table_size - 1));
|
|
|
|
|
|
|
|
return tpt_table[index].index;
|
|
|
|
}
|
|
|
|
|
2008-01-28 08:41:47 +08:00
|
|
|
static void iwl3945_clear_window(struct iwl3945_rate_scale_data *window)
|
2007-09-26 08:54:57 +08:00
|
|
|
{
|
|
|
|
window->data = 0;
|
|
|
|
window->success_counter = 0;
|
2008-03-07 09:36:54 +08:00
|
|
|
window->success_ratio = -1;
|
2007-09-26 08:54:57 +08:00
|
|
|
window->counter = 0;
|
2008-12-19 10:37:12 +08:00
|
|
|
window->average_tpt = IWL_INVALID_VALUE;
|
2007-09-26 08:54:57 +08:00
|
|
|
window->stamp = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-01-28 08:41:47 +08:00
|
|
|
* iwl3945_rate_scale_flush_windows - flush out the rate scale windows
|
2007-09-26 08:54:57 +08:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
2008-01-28 12:41:38 +08:00
|
|
|
static int iwl3945_rate_scale_flush_windows(struct iwl3945_rs_sta *rs_sta)
|
2007-09-26 08:54:57 +08:00
|
|
|
{
|
|
|
|
int unflushed = 0;
|
|
|
|
int i;
|
|
|
|
unsigned long flags;
|
2009-01-28 06:27:53 +08:00
|
|
|
struct iwl_priv *priv __maybe_unused = rs_sta->priv;
|
2007-09-26 08:54:57 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*/
|
2008-12-19 10:37:12 +08:00
|
|
|
for (i = 0; i < IWL_RATE_COUNT_3945; i++) {
|
2008-01-28 12:41:38 +08:00
|
|
|
if (!rs_sta->win[i].counter)
|
2007-09-26 08:54:57 +08:00
|
|
|
continue;
|
|
|
|
|
2008-01-28 12:41:38 +08:00
|
|
|
spin_lock_irqsave(&rs_sta->lock, flags);
|
|
|
|
if (time_after(jiffies, rs_sta->win[i].stamp +
|
2007-09-26 08:54:57 +08:00
|
|
|
IWL_RATE_WIN_FLUSH)) {
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "flushing %d samples of rate "
|
2007-09-26 08:54:57 +08:00
|
|
|
"index %d\n",
|
2008-01-28 12:41:38 +08:00
|
|
|
rs_sta->win[i].counter, i);
|
|
|
|
iwl3945_clear_window(&rs_sta->win[i]);
|
2007-09-26 08:54:57 +08:00
|
|
|
} else
|
|
|
|
unflushed++;
|
2008-01-28 12:41:38 +08:00
|
|
|
spin_unlock_irqrestore(&rs_sta->lock, flags);
|
2007-09-26 08:54:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return unflushed;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define IWL_RATE_FLUSH_MAX 5000 /* msec */
|
|
|
|
#define IWL_RATE_FLUSH_MIN 50 /* msec */
|
2008-12-03 04:14:03 +08:00
|
|
|
#define IWL_AVERAGE_PACKETS 1500
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2008-01-28 08:41:47 +08:00
|
|
|
static void iwl3945_bg_rate_scale_flush(unsigned long data)
|
2007-09-26 08:54:57 +08:00
|
|
|
{
|
2008-01-28 12:41:38 +08:00
|
|
|
struct iwl3945_rs_sta *rs_sta = (void *)data;
|
2009-01-28 06:27:53 +08:00
|
|
|
struct iwl_priv *priv __maybe_unused = rs_sta->priv;
|
2007-09-26 08:54:57 +08:00
|
|
|
int unflushed = 0;
|
|
|
|
unsigned long flags;
|
|
|
|
u32 packet_count, duration, pps;
|
|
|
|
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "enter\n");
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2008-01-28 12:41:38 +08:00
|
|
|
unflushed = iwl3945_rate_scale_flush_windows(rs_sta);
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2008-01-28 12:41:38 +08:00
|
|
|
spin_lock_irqsave(&rs_sta->lock, flags);
|
2007-09-26 08:54:57 +08:00
|
|
|
|
|
|
|
/* Number of packets Rx'd since last time this timer ran */
|
2008-01-28 12:41:38 +08:00
|
|
|
packet_count = (rs_sta->tx_packets - rs_sta->last_tx_packets) + 1;
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2008-01-28 12:41:38 +08:00
|
|
|
rs_sta->last_tx_packets = rs_sta->tx_packets + 1;
|
2007-09-26 08:54:57 +08:00
|
|
|
|
|
|
|
if (unflushed) {
|
|
|
|
duration =
|
2008-01-28 12:41:38 +08:00
|
|
|
jiffies_to_msecs(jiffies - rs_sta->last_partial_flush);
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "Tx'd %d packets in %dms\n",
|
2007-09-26 08:54:57 +08:00
|
|
|
packet_count, duration);
|
|
|
|
|
|
|
|
/* Determine packets per second */
|
|
|
|
if (duration)
|
|
|
|
pps = (packet_count * 1000) / duration;
|
|
|
|
else
|
|
|
|
pps = 0;
|
|
|
|
|
|
|
|
if (pps) {
|
2008-12-03 04:14:03 +08:00
|
|
|
duration = (IWL_AVERAGE_PACKETS * 1000) / pps;
|
2007-09-26 08:54:57 +08:00
|
|
|
if (duration < IWL_RATE_FLUSH_MIN)
|
|
|
|
duration = IWL_RATE_FLUSH_MIN;
|
2008-12-03 04:14:03 +08:00
|
|
|
else if (duration > IWL_RATE_FLUSH_MAX)
|
|
|
|
duration = IWL_RATE_FLUSH_MAX;
|
2007-09-26 08:54:57 +08:00
|
|
|
} else
|
|
|
|
duration = IWL_RATE_FLUSH_MAX;
|
|
|
|
|
2008-01-28 12:41:38 +08:00
|
|
|
rs_sta->flush_time = msecs_to_jiffies(duration);
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "new flush period: %d msec ave %d\n",
|
2007-09-26 08:54:57 +08:00
|
|
|
duration, packet_count);
|
|
|
|
|
2008-01-28 12:41:38 +08:00
|
|
|
mod_timer(&rs_sta->rate_scale_flush, jiffies +
|
|
|
|
rs_sta->flush_time);
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2008-01-28 12:41:38 +08:00
|
|
|
rs_sta->last_partial_flush = jiffies;
|
2008-12-03 04:14:03 +08:00
|
|
|
} else {
|
|
|
|
rs_sta->flush_time = IWL_RATE_FLUSH;
|
|
|
|
rs_sta->flush_pending = 0;
|
2007-09-26 08:54:57 +08:00
|
|
|
}
|
|
|
|
/* If there weren't any unflushed entries, we don't schedule the timer
|
|
|
|
* to run again */
|
|
|
|
|
2008-01-28 12:41:38 +08:00
|
|
|
rs_sta->last_flush = jiffies;
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2008-01-28 12:41:38 +08:00
|
|
|
spin_unlock_irqrestore(&rs_sta->lock, flags);
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "leave\n");
|
2007-09-26 08:54:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-01-28 08:41:47 +08:00
|
|
|
* iwl3945_collect_tx_data - Update the success/failure sliding window
|
2007-09-26 08:54:57 +08:00
|
|
|
*
|
|
|
|
* We keep a sliding window of the last 64 packets transmitted
|
|
|
|
* at this rate. window->data contains the bitmask of successful
|
|
|
|
* packets.
|
|
|
|
*/
|
2008-01-28 12:41:38 +08:00
|
|
|
static void iwl3945_collect_tx_data(struct iwl3945_rs_sta *rs_sta,
|
2008-01-28 08:41:47 +08:00
|
|
|
struct iwl3945_rate_scale_data *window,
|
2008-12-03 04:14:03 +08:00
|
|
|
int success, int retries, int index)
|
2007-09-26 08:54:57 +08:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
2008-12-03 04:14:03 +08:00
|
|
|
s32 fail_count;
|
2009-01-28 06:27:53 +08:00
|
|
|
struct iwl_priv *priv __maybe_unused = rs_sta->priv;
|
2007-09-26 08:54:57 +08:00
|
|
|
|
|
|
|
if (!retries) {
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "leave: retries == 0 -- should be at least 1\n");
|
2007-09-26 08:54:57 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-12-03 04:14:03 +08:00
|
|
|
spin_lock_irqsave(&rs_sta->lock, flags);
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2009-03-12 02:17:56 +08:00
|
|
|
/*
|
|
|
|
* 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!).
|
|
|
|
* */
|
|
|
|
while (retries > 0) {
|
|
|
|
if (window->counter >= IWL_RATE_MAX_WINDOW) {
|
|
|
|
|
|
|
|
/* remove earliest */
|
|
|
|
window->counter = IWL_RATE_MAX_WINDOW - 1;
|
|
|
|
|
|
|
|
if (window->data & (1ULL << (IWL_RATE_MAX_WINDOW - 1))) {
|
|
|
|
window->data &= ~(1ULL << (IWL_RATE_MAX_WINDOW - 1));
|
2007-09-26 08:54:57 +08:00
|
|
|
window->success_counter--;
|
2009-03-12 02:17:56 +08:00
|
|
|
}
|
|
|
|
}
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2009-03-12 02:17:56 +08:00
|
|
|
/* Increment frames-attempted counter */
|
|
|
|
window->counter++;
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2009-03-12 02:17:56 +08:00
|
|
|
/* Shift bitmap by one frame (throw away oldest history),
|
|
|
|
* OR in "1", and increment "success" if this
|
|
|
|
* frame was successful. */
|
|
|
|
window->data <<= 1;
|
|
|
|
if (success > 0) {
|
2007-09-26 08:54:57 +08:00
|
|
|
window->success_counter++;
|
2009-03-12 02:17:56 +08:00
|
|
|
window->data |= 0x1;
|
|
|
|
success--;
|
2007-09-26 08:54:57 +08:00
|
|
|
}
|
|
|
|
|
2009-03-12 02:17:56 +08:00
|
|
|
retries--;
|
2007-09-26 08:54:57 +08:00
|
|
|
}
|
2008-12-03 04:14:03 +08:00
|
|
|
|
2009-03-12 02:17:56 +08:00
|
|
|
/* Calculate current success ratio, avoid divide-by-0! */
|
|
|
|
if (window->counter > 0)
|
|
|
|
window->success_ratio = 128 * (100 * window->success_counter)
|
|
|
|
/ window->counter;
|
|
|
|
else
|
|
|
|
window->success_ratio = IWL_INVALID_VALUE;
|
|
|
|
|
2008-12-03 04:14:03 +08:00
|
|
|
fail_count = window->counter - window->success_counter;
|
2009-03-12 02:17:56 +08:00
|
|
|
|
|
|
|
/* Calculate average throughput, if we have enough history. */
|
2008-12-03 04:14:03 +08:00
|
|
|
if ((fail_count >= IWL_RATE_MIN_FAILURE_TH) ||
|
|
|
|
(window->success_counter >= IWL_RATE_MIN_SUCCESS_TH))
|
|
|
|
window->average_tpt = ((window->success_ratio *
|
|
|
|
rs_sta->expected_tpt[index] + 64) / 128);
|
|
|
|
else
|
2008-12-19 10:37:12 +08:00
|
|
|
window->average_tpt = IWL_INVALID_VALUE;
|
2008-12-03 04:14:03 +08:00
|
|
|
|
2009-03-12 02:17:56 +08:00
|
|
|
/* Tag this window as having been updated */
|
|
|
|
window->stamp = jiffies;
|
|
|
|
|
2008-12-03 04:14:03 +08:00
|
|
|
spin_unlock_irqrestore(&rs_sta->lock, flags);
|
|
|
|
|
2007-09-26 08:54:57 +08:00
|
|
|
}
|
|
|
|
|
2010-02-23 08:24:47 +08:00
|
|
|
/*
|
|
|
|
* Called after adding a new station to initialize rate scaling
|
|
|
|
*/
|
|
|
|
void iwl3945_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_id)
|
2007-09-26 08:54:57 +08:00
|
|
|
{
|
2010-02-23 08:24:47 +08:00
|
|
|
struct ieee80211_hw *hw = priv->hw;
|
|
|
|
struct ieee80211_conf *conf = &priv->hw->conf;
|
|
|
|
struct iwl3945_sta_priv *psta;
|
|
|
|
struct iwl3945_rs_sta *rs_sta;
|
|
|
|
struct ieee80211_supported_band *sband;
|
2007-09-26 08:54:57 +08:00
|
|
|
int i;
|
|
|
|
|
2010-03-25 05:19:58 +08:00
|
|
|
IWL_DEBUG_INFO(priv, "enter\n");
|
2010-08-27 23:53:46 +08:00
|
|
|
if (sta_id == priv->contexts[IWL_RXON_CTX_BSS].bcast_sta_id)
|
2010-02-23 08:24:47 +08:00
|
|
|
goto out;
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2010-02-23 08:24:47 +08:00
|
|
|
psta = (struct iwl3945_sta_priv *) sta->drv_priv;
|
|
|
|
rs_sta = &psta->rs_sta;
|
|
|
|
sband = hw->wiphy->bands[conf->channel->band];
|
2009-10-31 05:36:13 +08:00
|
|
|
|
|
|
|
rs_sta->priv = priv;
|
|
|
|
|
|
|
|
rs_sta->start_rate = IWL_RATE_INVALID;
|
|
|
|
|
|
|
|
/* default to just 802.11b */
|
|
|
|
rs_sta->expected_tpt = iwl3945_expected_tpt_b;
|
|
|
|
|
|
|
|
rs_sta->last_partial_flush = jiffies;
|
|
|
|
rs_sta->last_flush = jiffies;
|
|
|
|
rs_sta->flush_time = IWL_RATE_FLUSH;
|
|
|
|
rs_sta->last_tx_packets = 0;
|
|
|
|
|
|
|
|
rs_sta->rate_scale_flush.data = (unsigned long)rs_sta;
|
2009-11-18 16:25:25 +08:00
|
|
|
rs_sta->rate_scale_flush.function = iwl3945_bg_rate_scale_flush;
|
2009-10-31 05:36:13 +08:00
|
|
|
|
|
|
|
for (i = 0; i < IWL_RATE_COUNT_3945; i++)
|
|
|
|
iwl3945_clear_window(&rs_sta->win[i]);
|
|
|
|
|
2007-09-26 08:54:57 +08:00
|
|
|
/* 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.. */
|
|
|
|
|
2008-12-05 23:58:37 +08:00
|
|
|
for (i = sband->n_bitrates - 1; i >= 0; i--) {
|
2008-09-19 00:14:18 +08:00
|
|
|
if (sta->supp_rates[sband->band] & (1 << i)) {
|
2008-09-11 09:04:36 +08:00
|
|
|
rs_sta->last_txrate_idx = i;
|
2007-09-26 08:54:57 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-21 22:09:28 +08:00
|
|
|
priv->_3945.sta_supp_rates = sta->supp_rates[sband->band];
|
2008-01-25 02:38:38 +08:00
|
|
|
/* For 5 GHz band it start at IWL_FIRST_OFDM_RATE */
|
2008-12-05 23:58:37 +08:00
|
|
|
if (sband->band == IEEE80211_BAND_5GHZ) {
|
2008-09-11 08:22:58 +08:00
|
|
|
rs_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE;
|
2010-01-21 22:09:28 +08:00
|
|
|
priv->_3945.sta_supp_rates = priv->_3945.sta_supp_rates <<
|
2008-12-05 23:58:37 +08:00
|
|
|
IWL_FIRST_OFDM_RATE;
|
|
|
|
}
|
|
|
|
|
2010-02-23 08:24:47 +08:00
|
|
|
out:
|
|
|
|
priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS;
|
2007-11-12 11:37:42 +08:00
|
|
|
|
2010-02-23 08:24:47 +08:00
|
|
|
IWL_DEBUG_INFO(priv, "leave\n");
|
2007-09-26 08:54:57 +08:00
|
|
|
}
|
|
|
|
|
2008-09-19 00:14:18 +08:00
|
|
|
static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
|
2007-09-26 08:54:57 +08:00
|
|
|
{
|
2008-09-19 00:14:18 +08:00
|
|
|
return hw->priv;
|
2007-09-26 08:54:57 +08:00
|
|
|
}
|
|
|
|
|
2007-10-25 17:15:22 +08:00
|
|
|
/* rate scale requires free function to be implemented */
|
2007-09-26 08:54:57 +08:00
|
|
|
static void rs_free(void *priv)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2008-09-19 00:14:18 +08:00
|
|
|
|
2008-12-19 10:37:08 +08:00
|
|
|
static void *rs_alloc_sta(void *iwl_priv, struct ieee80211_sta *sta, gfp_t gfp)
|
2007-09-26 08:54:57 +08:00
|
|
|
{
|
2008-01-28 12:41:38 +08:00
|
|
|
struct iwl3945_rs_sta *rs_sta;
|
2008-09-19 00:14:18 +08:00
|
|
|
struct iwl3945_sta_priv *psta = (void *) sta->drv_priv;
|
2009-10-31 05:36:13 +08:00
|
|
|
struct iwl_priv *priv __maybe_unused = iwl_priv;
|
2008-09-19 00:14:18 +08:00
|
|
|
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "enter\n");
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2009-10-31 05:36:13 +08:00
|
|
|
rs_sta = &psta->rs_sta;
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2010-02-23 08:24:47 +08:00
|
|
|
spin_lock_init(&rs_sta->lock);
|
|
|
|
init_timer(&rs_sta->rate_scale_flush);
|
|
|
|
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "leave\n");
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2008-01-28 12:41:38 +08:00
|
|
|
return rs_sta;
|
2007-09-26 08:54:57 +08:00
|
|
|
}
|
|
|
|
|
2008-12-19 10:37:08 +08:00
|
|
|
static void rs_free_sta(void *iwl_priv, struct ieee80211_sta *sta,
|
2008-09-19 00:14:18 +08:00
|
|
|
void *priv_sta)
|
2007-09-26 08:54:57 +08:00
|
|
|
{
|
2010-02-23 08:24:47 +08:00
|
|
|
struct iwl3945_rs_sta *rs_sta = priv_sta;
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2010-02-23 08:24:47 +08:00
|
|
|
/*
|
|
|
|
* Be careful not to use any members of iwl3945_rs_sta (like trying
|
|
|
|
* to use iwl_priv to print out debugging) since it may not be fully
|
|
|
|
* initialized at this point.
|
|
|
|
*/
|
2008-01-28 12:41:38 +08:00
|
|
|
del_timer_sync(&rs_sta->rate_scale_flush);
|
2007-09-26 08:54:57 +08:00
|
|
|
}
|
|
|
|
|
2007-11-12 11:37:42 +08:00
|
|
|
|
2007-09-26 08:54:57 +08:00
|
|
|
/**
|
|
|
|
* rs_tx_status - Update rate control values based on Tx results
|
|
|
|
*
|
2008-12-19 10:37:28 +08:00
|
|
|
* NOTE: Uses iwl_priv->retry_rate for the # of retries attempted by
|
2007-09-26 08:54:57 +08:00
|
|
|
* the hardware for each rate.
|
|
|
|
*/
|
2008-09-19 00:14:18 +08:00
|
|
|
static void rs_tx_status(void *priv_rate, struct ieee80211_supported_band *sband,
|
|
|
|
struct ieee80211_sta *sta, void *priv_sta,
|
2008-05-15 18:55:29 +08:00
|
|
|
struct sk_buff *skb)
|
2007-09-26 08:54:57 +08:00
|
|
|
{
|
2008-12-03 04:14:03 +08:00
|
|
|
s8 retries = 0, current_count;
|
2007-09-26 08:54:57 +08:00
|
|
|
int scale_rate_index, first_index, last_index;
|
|
|
|
unsigned long flags;
|
2008-12-19 10:37:28 +08:00
|
|
|
struct iwl_priv *priv = (struct iwl_priv *)priv_rate;
|
2008-09-19 00:14:18 +08:00
|
|
|
struct iwl3945_rs_sta *rs_sta = priv_sta;
|
2008-05-15 18:55:29 +08:00
|
|
|
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
2008-01-25 02:38:38 +08:00
|
|
|
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "enter\n");
|
2008-03-07 09:36:54 +08:00
|
|
|
|
2009-03-18 12:51:51 +08:00
|
|
|
retries = info->status.rates[0].count;
|
2009-03-12 02:17:56 +08:00
|
|
|
/* Sanity Check for retries */
|
|
|
|
if (retries > IWL_RATE_RETRY_TH)
|
|
|
|
retries = IWL_RATE_RETRY_TH;
|
2008-10-21 18:40:02 +08:00
|
|
|
|
|
|
|
first_index = sband->bitrates[info->status.rates[0].idx].hw_value;
|
2008-12-19 10:37:12 +08:00
|
|
|
if ((first_index < 0) || (first_index >= IWL_RATE_COUNT_3945)) {
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "leave: Rate out of bounds: %d\n", first_index);
|
2007-09-26 08:54:57 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-09-19 00:14:18 +08:00
|
|
|
if (!priv_sta) {
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "leave: No STA priv data to update!\n");
|
2007-09-26 08:54:57 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-02-23 08:24:47 +08:00
|
|
|
/* Treat uninitialized rate scaling data same as non-existing. */
|
|
|
|
if (!rs_sta->priv) {
|
|
|
|
IWL_DEBUG_RATE(priv, "leave: STA priv data uninitialized!\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-01-28 12:41:38 +08:00
|
|
|
rs_sta->tx_packets++;
|
2007-09-26 08:54:57 +08:00
|
|
|
|
|
|
|
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)
|
|
|
|
*/
|
2008-12-03 04:14:03 +08:00
|
|
|
while (retries > 1) {
|
|
|
|
if ((retries - 1) < priv->retry_rate) {
|
|
|
|
current_count = (retries - 1);
|
2007-09-26 08:54:57 +08:00
|
|
|
last_index = scale_rate_index;
|
|
|
|
} else {
|
|
|
|
current_count = priv->retry_rate;
|
2008-10-21 18:40:02 +08:00
|
|
|
last_index = iwl3945_rs_next_rate(priv,
|
2007-11-12 11:37:42 +08:00
|
|
|
scale_rate_index);
|
2007-09-26 08:54:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Update this rate accounting for as many retries
|
|
|
|
* as was used for it (per current_count) */
|
2008-01-28 12:41:38 +08:00
|
|
|
iwl3945_collect_tx_data(rs_sta,
|
|
|
|
&rs_sta->win[scale_rate_index],
|
2008-12-03 04:14:03 +08:00
|
|
|
0, current_count, scale_rate_index);
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "Update rate %d for %d retries.\n",
|
2007-09-26 08:54:57 +08:00
|
|
|
scale_rate_index, current_count);
|
|
|
|
|
|
|
|
retries -= current_count;
|
|
|
|
|
2008-12-03 04:14:03 +08:00
|
|
|
scale_rate_index = last_index;
|
2007-09-26 08:54:57 +08:00
|
|
|
}
|
|
|
|
|
2007-11-12 11:37:42 +08:00
|
|
|
|
2007-09-26 08:54:57 +08:00
|
|
|
/* Update the last index window with success/failure based on ACK */
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "Update rate %d with %s.\n",
|
2007-09-26 08:54:57 +08:00
|
|
|
last_index,
|
2008-05-15 18:55:29 +08:00
|
|
|
(info->flags & IEEE80211_TX_STAT_ACK) ?
|
2007-09-26 08:54:57 +08:00
|
|
|
"success" : "failure");
|
2008-01-28 12:41:38 +08:00
|
|
|
iwl3945_collect_tx_data(rs_sta,
|
|
|
|
&rs_sta->win[last_index],
|
2008-12-03 04:14:03 +08:00
|
|
|
info->flags & IEEE80211_TX_STAT_ACK, 1, last_index);
|
2007-09-26 08:54:57 +08:00
|
|
|
|
|
|
|
/* We updated the rate scale window -- if its been more than
|
|
|
|
* flush_time since the last run, schedule the flush
|
|
|
|
* again */
|
2008-01-28 12:41:38 +08:00
|
|
|
spin_lock_irqsave(&rs_sta->lock, flags);
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2008-01-28 12:41:38 +08:00
|
|
|
if (!rs_sta->flush_pending &&
|
2008-12-03 04:14:03 +08:00
|
|
|
time_after(jiffies, rs_sta->last_flush +
|
2008-01-28 12:41:38 +08:00
|
|
|
rs_sta->flush_time)) {
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2008-12-03 04:14:03 +08:00
|
|
|
rs_sta->last_partial_flush = jiffies;
|
2008-01-28 12:41:38 +08:00
|
|
|
rs_sta->flush_pending = 1;
|
|
|
|
mod_timer(&rs_sta->rate_scale_flush,
|
|
|
|
jiffies + rs_sta->flush_time);
|
2007-09-26 08:54:57 +08:00
|
|
|
}
|
|
|
|
|
2008-01-28 12:41:38 +08:00
|
|
|
spin_unlock_irqrestore(&rs_sta->lock, flags);
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "leave\n");
|
2007-09-26 08:54:57 +08:00
|
|
|
}
|
|
|
|
|
2008-01-28 12:41:38 +08:00
|
|
|
static u16 iwl3945_get_adjacent_rate(struct iwl3945_rs_sta *rs_sta,
|
2008-01-25 02:38:38 +08:00
|
|
|
u8 index, u16 rate_mask, enum ieee80211_band band)
|
2007-09-26 08:54:57 +08:00
|
|
|
{
|
|
|
|
u8 high = IWL_RATE_INVALID;
|
|
|
|
u8 low = IWL_RATE_INVALID;
|
2009-01-28 06:27:53 +08:00
|
|
|
struct iwl_priv *priv __maybe_unused = rs_sta->priv;
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2007-10-25 17:15:22 +08:00
|
|
|
/* 802.11A walks to the next literal adjacent rate in
|
2007-09-26 08:54:57 +08:00
|
|
|
* the rate table */
|
2008-01-25 02:38:38 +08:00
|
|
|
if (unlikely(band == IEEE80211_BAND_5GHZ)) {
|
2007-09-26 08:54:57 +08:00
|
|
|
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;
|
2008-12-19 10:37:12 +08:00
|
|
|
for (mask = (1 << i); i < IWL_RATE_COUNT_3945;
|
|
|
|
i++, mask <<= 1) {
|
2007-09-26 08:54:57 +08:00
|
|
|
if (rate_mask & mask) {
|
|
|
|
high = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (high << 8) | low;
|
|
|
|
}
|
|
|
|
|
|
|
|
low = index;
|
|
|
|
while (low != IWL_RATE_INVALID) {
|
2008-01-28 12:41:38 +08:00
|
|
|
if (rs_sta->tgg)
|
2008-01-28 08:41:47 +08:00
|
|
|
low = iwl3945_rates[low].prev_rs_tgg;
|
2007-09-26 08:54:57 +08:00
|
|
|
else
|
2008-01-28 08:41:47 +08:00
|
|
|
low = iwl3945_rates[low].prev_rs;
|
2007-09-26 08:54:57 +08:00
|
|
|
if (low == IWL_RATE_INVALID)
|
|
|
|
break;
|
|
|
|
if (rate_mask & (1 << low))
|
|
|
|
break;
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "Skipping masked lower rate: %d\n", low);
|
2007-09-26 08:54:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
high = index;
|
|
|
|
while (high != IWL_RATE_INVALID) {
|
2008-01-28 12:41:38 +08:00
|
|
|
if (rs_sta->tgg)
|
2008-01-28 08:41:47 +08:00
|
|
|
high = iwl3945_rates[high].next_rs_tgg;
|
2007-09-26 08:54:57 +08:00
|
|
|
else
|
2008-01-28 08:41:47 +08:00
|
|
|
high = iwl3945_rates[high].next_rs;
|
2007-09-26 08:54:57 +08:00
|
|
|
if (high == IWL_RATE_INVALID)
|
|
|
|
break;
|
|
|
|
if (rate_mask & (1 << high))
|
|
|
|
break;
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "Skipping masked higher rate: %d\n", high);
|
2007-09-26 08:54:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return (high << 8) | low;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* rs_get_rate - find the rate for the requested packet
|
|
|
|
*
|
|
|
|
* Returns the ieee80211_rate structure allocated by the driver.
|
|
|
|
*
|
|
|
|
* The rate control algorithm has no internal mapping between hw_mode's
|
|
|
|
* rate ordering and the rate ordering used by the rate control algorithm.
|
|
|
|
*
|
|
|
|
* The rate control algorithm uses a single table of rates that goes across
|
|
|
|
* the entire A/B/G spectrum vs. being limited to just one particular
|
|
|
|
* hw_mode.
|
|
|
|
*
|
|
|
|
* As such, we can't convert the index obtained below into the hw_mode's
|
|
|
|
* rate table and must reference the driver allocated rate table
|
|
|
|
*
|
|
|
|
*/
|
2008-10-21 18:40:02 +08:00
|
|
|
static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta,
|
|
|
|
void *priv_sta, struct ieee80211_tx_rate_control *txrc)
|
2007-09-26 08:54:57 +08:00
|
|
|
{
|
2008-10-21 18:40:02 +08:00
|
|
|
struct ieee80211_supported_band *sband = txrc->sband;
|
|
|
|
struct sk_buff *skb = txrc->skb;
|
2007-09-26 08:54:57 +08:00
|
|
|
u8 low = IWL_RATE_INVALID;
|
|
|
|
u8 high = IWL_RATE_INVALID;
|
|
|
|
u16 high_low;
|
|
|
|
int index;
|
2008-09-19 00:14:18 +08:00
|
|
|
struct iwl3945_rs_sta *rs_sta = priv_sta;
|
2008-01-28 08:41:47 +08:00
|
|
|
struct iwl3945_rate_scale_data *window = NULL;
|
2008-12-19 10:37:12 +08:00
|
|
|
int current_tpt = IWL_INVALID_VALUE;
|
|
|
|
int low_tpt = IWL_INVALID_VALUE;
|
|
|
|
int high_tpt = IWL_INVALID_VALUE;
|
2007-09-26 08:54:57 +08:00
|
|
|
u32 fail_count;
|
|
|
|
s8 scale_action = 0;
|
|
|
|
unsigned long flags;
|
2009-07-15 08:13:55 +08:00
|
|
|
u16 rate_mask = sta ? sta->supp_rates[sband->band] : 0;
|
2009-01-21 13:33:52 +08:00
|
|
|
s8 max_rate_idx = -1;
|
2010-04-06 01:43:10 +08:00
|
|
|
struct iwl_priv *priv __maybe_unused = (struct iwl_priv *)priv_r;
|
2008-10-21 18:40:02 +08:00
|
|
|
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "enter\n");
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2010-02-23 08:24:47 +08:00
|
|
|
/* Treat uninitialized rate scaling data same as non-existing. */
|
|
|
|
if (rs_sta && !rs_sta->priv) {
|
|
|
|
IWL_DEBUG_RATE(priv, "Rate scaling information not initialized yet.\n");
|
|
|
|
priv_sta = NULL;
|
|
|
|
}
|
|
|
|
|
2009-07-17 01:05:41 +08:00
|
|
|
if (rate_control_send_low(sta, priv_sta, txrc))
|
2007-12-20 20:50:07 +08:00
|
|
|
return;
|
2009-07-17 01:05:41 +08:00
|
|
|
|
|
|
|
rate_mask = sta->supp_rates[sband->band];
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2009-01-21 13:33:52 +08:00
|
|
|
/* get user max rate if set */
|
|
|
|
max_rate_idx = txrc->max_rate_idx;
|
|
|
|
if ((sband->band == IEEE80211_BAND_5GHZ) && (max_rate_idx != -1))
|
|
|
|
max_rate_idx += IWL_FIRST_OFDM_RATE;
|
|
|
|
if ((max_rate_idx < 0) || (max_rate_idx >= IWL_RATE_COUNT))
|
|
|
|
max_rate_idx = -1;
|
|
|
|
|
2008-12-19 10:37:12 +08:00
|
|
|
index = min(rs_sta->last_txrate_idx & 0xffff, IWL_RATE_COUNT_3945 - 1);
|
2007-11-12 11:37:42 +08:00
|
|
|
|
2008-03-07 09:36:54 +08:00
|
|
|
if (sband->band == IEEE80211_BAND_5GHZ)
|
2007-11-12 11:37:42 +08:00
|
|
|
rate_mask = rate_mask << IWL_FIRST_OFDM_RATE;
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2008-01-28 12:41:38 +08:00
|
|
|
spin_lock_irqsave(&rs_sta->lock, flags);
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2008-12-03 04:14:03 +08:00
|
|
|
/* for recent assoc, choose best rate regarding
|
|
|
|
* to rssi value
|
|
|
|
*/
|
2008-01-28 12:41:38 +08:00
|
|
|
if (rs_sta->start_rate != IWL_RATE_INVALID) {
|
2008-12-03 04:14:03 +08:00
|
|
|
if (rs_sta->start_rate < index &&
|
|
|
|
(rate_mask & (1 << rs_sta->start_rate)))
|
|
|
|
index = rs_sta->start_rate;
|
2008-01-28 12:41:38 +08:00
|
|
|
rs_sta->start_rate = IWL_RATE_INVALID;
|
2007-09-26 08:54:57 +08:00
|
|
|
}
|
|
|
|
|
2009-01-21 13:33:52 +08:00
|
|
|
/* force user max rate if set by user */
|
|
|
|
if ((max_rate_idx != -1) && (max_rate_idx < index)) {
|
|
|
|
if (rate_mask & (1 << max_rate_idx))
|
|
|
|
index = max_rate_idx;
|
|
|
|
}
|
|
|
|
|
2008-01-28 12:41:38 +08:00
|
|
|
window = &(rs_sta->win[index]);
|
2007-09-26 08:54:57 +08:00
|
|
|
|
|
|
|
fail_count = window->counter - window->success_counter;
|
|
|
|
|
2009-03-12 02:17:56 +08:00
|
|
|
if (((fail_count < IWL_RATE_MIN_FAILURE_TH) &&
|
2007-09-26 08:54:57 +08:00
|
|
|
(window->success_counter < IWL_RATE_MIN_SUCCESS_TH))) {
|
2008-01-28 12:41:38 +08:00
|
|
|
spin_unlock_irqrestore(&rs_sta->lock, flags);
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "Invalid average_tpt on rate %d: "
|
2007-09-26 08:54:57 +08:00
|
|
|
"counter: %d, success_counter: %d, "
|
|
|
|
"expected_tpt is %sNULL\n",
|
|
|
|
index,
|
|
|
|
window->counter,
|
|
|
|
window->success_counter,
|
2008-01-28 12:41:38 +08:00
|
|
|
rs_sta->expected_tpt ? "not " : "");
|
2009-03-12 02:17:56 +08:00
|
|
|
|
|
|
|
/* Can't calculate this yet; not enough history */
|
|
|
|
window->average_tpt = IWL_INVALID_VALUE;
|
2007-09-26 08:54:57 +08:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
current_tpt = window->average_tpt;
|
|
|
|
|
2008-01-28 12:41:38 +08:00
|
|
|
high_low = iwl3945_get_adjacent_rate(rs_sta, index, rate_mask,
|
2008-03-07 09:36:54 +08:00
|
|
|
sband->band);
|
2007-09-26 08:54:57 +08:00
|
|
|
low = high_low & 0xff;
|
|
|
|
high = (high_low >> 8) & 0xff;
|
|
|
|
|
2009-01-21 13:33:52 +08:00
|
|
|
/* If user set max rate, dont allow higher than user constrain */
|
|
|
|
if ((max_rate_idx != -1) && (max_rate_idx < high))
|
|
|
|
high = IWL_RATE_INVALID;
|
|
|
|
|
2009-03-12 02:17:56 +08:00
|
|
|
/* Collect Measured throughputs of adjacent rates */
|
2007-09-26 08:54:57 +08:00
|
|
|
if (low != IWL_RATE_INVALID)
|
2008-01-28 12:41:38 +08:00
|
|
|
low_tpt = rs_sta->win[low].average_tpt;
|
2007-09-26 08:54:57 +08:00
|
|
|
|
|
|
|
if (high != IWL_RATE_INVALID)
|
2008-01-28 12:41:38 +08:00
|
|
|
high_tpt = rs_sta->win[high].average_tpt;
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2008-01-28 12:41:38 +08:00
|
|
|
spin_unlock_irqrestore(&rs_sta->lock, flags);
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2009-03-12 02:17:56 +08:00
|
|
|
scale_action = 0;
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2009-03-12 02:17:56 +08:00
|
|
|
/* Low success ratio , need to drop the rate */
|
2007-09-26 08:54:57 +08:00
|
|
|
if ((window->success_ratio < IWL_RATE_DECREASE_TH) || !current_tpt) {
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "decrease rate because of low success_ratio\n");
|
2007-09-26 08:54:57 +08:00
|
|
|
scale_action = -1;
|
2009-03-12 02:17:56 +08:00
|
|
|
/* No throughput measured yet for adjacent rates,
|
|
|
|
* try increase */
|
2008-12-19 10:37:12 +08:00
|
|
|
} else if ((low_tpt == IWL_INVALID_VALUE) &&
|
2009-03-12 02:17:56 +08:00
|
|
|
(high_tpt == IWL_INVALID_VALUE)) {
|
|
|
|
|
2009-03-18 12:51:51 +08:00
|
|
|
if (high != IWL_RATE_INVALID && window->success_ratio >= IWL_RATE_INCREASE_TH)
|
2009-03-12 02:17:56 +08:00
|
|
|
scale_action = 1;
|
|
|
|
else if (low != IWL_RATE_INVALID)
|
2009-03-18 12:51:51 +08:00
|
|
|
scale_action = 0;
|
2009-03-12 02:17:56 +08:00
|
|
|
|
|
|
|
/* 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) &&
|
2008-12-19 10:37:12 +08:00
|
|
|
(high_tpt != IWL_INVALID_VALUE) &&
|
2008-03-07 09:36:54 +08:00
|
|
|
(low_tpt < current_tpt) && (high_tpt < current_tpt)) {
|
2009-03-12 02:17:56 +08:00
|
|
|
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "No action -- low [%d] & high [%d] < "
|
2007-09-26 08:54:57 +08:00
|
|
|
"current_tpt [%d]\n",
|
|
|
|
low_tpt, high_tpt, current_tpt);
|
|
|
|
scale_action = 0;
|
2009-03-12 02:17:56 +08:00
|
|
|
|
|
|
|
/* At least one of the rates has better throughput */
|
2007-09-26 08:54:57 +08:00
|
|
|
} else {
|
2008-12-19 10:37:12 +08:00
|
|
|
if (high_tpt != IWL_INVALID_VALUE) {
|
2009-03-12 02:17:56 +08:00
|
|
|
|
|
|
|
/* High rate has better throughput, Increase
|
|
|
|
* rate */
|
|
|
|
if (high_tpt > current_tpt &&
|
|
|
|
window->success_ratio >= IWL_RATE_INCREASE_TH)
|
2007-09-26 08:54:57 +08:00
|
|
|
scale_action = 1;
|
|
|
|
else {
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv,
|
|
|
|
"decrease rate because of high tpt\n");
|
2009-03-18 12:51:51 +08:00
|
|
|
scale_action = 0;
|
2007-09-26 08:54:57 +08:00
|
|
|
}
|
2008-12-19 10:37:12 +08:00
|
|
|
} else if (low_tpt != IWL_INVALID_VALUE) {
|
2007-09-26 08:54:57 +08:00
|
|
|
if (low_tpt > current_tpt) {
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv,
|
|
|
|
"decrease rate because of low tpt\n");
|
2007-09-26 08:54:57 +08:00
|
|
|
scale_action = -1;
|
2009-03-18 12:51:51 +08:00
|
|
|
} else if (window->success_ratio >= IWL_RATE_INCREASE_TH) {
|
2009-03-12 02:17:56 +08:00
|
|
|
/* Lower rate has better
|
|
|
|
* throughput,decrease rate */
|
2007-09-26 08:54:57 +08:00
|
|
|
scale_action = 1;
|
2009-03-12 02:17:56 +08:00
|
|
|
}
|
2007-09-26 08:54:57 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-12 02:17:56 +08:00
|
|
|
/* Sanity check; asked for decrease, but success rate or throughput
|
|
|
|
* has been good at old rate. Don't change it. */
|
|
|
|
if ((scale_action == -1) && (low != IWL_RATE_INVALID) &&
|
|
|
|
((window->success_ratio > IWL_RATE_HIGH_TH) ||
|
|
|
|
(current_tpt > (100 * rs_sta->expected_tpt[low]))))
|
|
|
|
scale_action = 0;
|
2007-09-26 08:54:57 +08:00
|
|
|
|
|
|
|
switch (scale_action) {
|
|
|
|
case -1:
|
2009-03-12 02:17:56 +08:00
|
|
|
|
|
|
|
/* Decrese rate */
|
2007-09-26 08:54:57 +08:00
|
|
|
if (low != IWL_RATE_INVALID)
|
|
|
|
index = low;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
2009-03-12 02:17:56 +08:00
|
|
|
/* Increase rate */
|
2007-09-26 08:54:57 +08:00
|
|
|
if (high != IWL_RATE_INVALID)
|
|
|
|
index = high;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0:
|
|
|
|
default:
|
2009-03-12 02:17:56 +08:00
|
|
|
/* No change */
|
2007-09-26 08:54:57 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "Selected %d (action %d) - low %d high %d\n",
|
2007-09-26 08:54:57 +08:00
|
|
|
index, scale_action, low, high);
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
2008-09-11 08:22:58 +08:00
|
|
|
rs_sta->last_txrate_idx = index;
|
2008-03-07 09:36:54 +08:00
|
|
|
if (sband->band == IEEE80211_BAND_5GHZ)
|
2008-10-21 18:40:02 +08:00
|
|
|
info->control.rates[0].idx = rs_sta->last_txrate_idx -
|
|
|
|
IWL_FIRST_OFDM_RATE;
|
2007-11-12 11:37:42 +08:00
|
|
|
else
|
2008-10-21 18:40:02 +08:00
|
|
|
info->control.rates[0].idx = rs_sta->last_txrate_idx;
|
2007-11-12 11:37:42 +08:00
|
|
|
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "leave: %d\n", index);
|
2007-09-26 08:54:57 +08:00
|
|
|
}
|
|
|
|
|
2008-12-03 04:14:02 +08:00
|
|
|
#ifdef CONFIG_MAC80211_DEBUGFS
|
|
|
|
static int iwl3945_open_file_generic(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
file->private_data = inode->i_private;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t iwl3945_sta_dbgfs_stats_table_read(struct file *file,
|
|
|
|
char __user *user_buf,
|
|
|
|
size_t count, loff_t *ppos)
|
|
|
|
{
|
2009-03-02 03:25:38 +08:00
|
|
|
char *buff;
|
2008-12-03 04:14:02 +08:00
|
|
|
int desc = 0;
|
|
|
|
int j;
|
2009-03-02 03:25:38 +08:00
|
|
|
ssize_t ret;
|
2008-12-03 04:14:02 +08:00
|
|
|
struct iwl3945_rs_sta *lq_sta = file->private_data;
|
|
|
|
|
2009-03-02 03:25:38 +08:00
|
|
|
buff = kmalloc(1024, GFP_KERNEL);
|
|
|
|
if (!buff)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2008-12-03 04:14:02 +08:00
|
|
|
desc += sprintf(buff + desc, "tx packets=%d last rate index=%d\n"
|
|
|
|
"rate=0x%X flush time %d\n",
|
|
|
|
lq_sta->tx_packets,
|
|
|
|
lq_sta->last_txrate_idx,
|
|
|
|
lq_sta->start_rate, jiffies_to_msecs(lq_sta->flush_time));
|
2008-12-19 10:37:12 +08:00
|
|
|
for (j = 0; j < IWL_RATE_COUNT_3945; j++) {
|
2008-12-03 04:14:02 +08:00
|
|
|
desc += sprintf(buff+desc,
|
|
|
|
"counter=%d success=%d %%=%d\n",
|
|
|
|
lq_sta->win[j].counter,
|
|
|
|
lq_sta->win[j].success_counter,
|
|
|
|
lq_sta->win[j].success_ratio);
|
|
|
|
}
|
2009-03-02 03:25:38 +08:00
|
|
|
ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
|
|
|
|
kfree(buff);
|
|
|
|
return ret;
|
2008-12-03 04:14:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct file_operations rs_sta_dbgfs_stats_table_ops = {
|
|
|
|
.read = iwl3945_sta_dbgfs_stats_table_read,
|
|
|
|
.open = iwl3945_open_file_generic,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void iwl3945_add_debugfs(void *priv, void *priv_sta,
|
|
|
|
struct dentry *dir)
|
|
|
|
{
|
|
|
|
struct iwl3945_rs_sta *lq_sta = priv_sta;
|
|
|
|
|
|
|
|
lq_sta->rs_sta_dbgfs_stats_table_file =
|
|
|
|
debugfs_create_file("rate_stats_table", 0600, dir,
|
|
|
|
lq_sta, &rs_sta_dbgfs_stats_table_ops);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void iwl3945_remove_debugfs(void *priv, void *priv_sta)
|
|
|
|
{
|
|
|
|
struct iwl3945_rs_sta *lq_sta = priv_sta;
|
|
|
|
debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2010-02-23 08:24:47 +08:00
|
|
|
/*
|
|
|
|
* Initialization of rate scaling information is done by driver after
|
|
|
|
* the station is added. Since mac80211 calls this function before a
|
|
|
|
* station is added we ignore it.
|
|
|
|
*/
|
|
|
|
static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband,
|
|
|
|
struct ieee80211_sta *sta, void *priv_sta)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2007-09-26 08:54:57 +08:00
|
|
|
static struct rate_control_ops rs_ops = {
|
|
|
|
.module = NULL,
|
|
|
|
.name = RS_NAME,
|
|
|
|
.tx_status = rs_tx_status,
|
|
|
|
.get_rate = rs_get_rate,
|
2010-02-23 08:24:47 +08:00
|
|
|
.rate_init = rs_rate_init_stub,
|
2007-09-26 08:54:57 +08:00
|
|
|
.alloc = rs_alloc,
|
|
|
|
.free = rs_free,
|
|
|
|
.alloc_sta = rs_alloc_sta,
|
|
|
|
.free_sta = rs_free_sta,
|
2008-12-03 04:14:02 +08:00
|
|
|
#ifdef CONFIG_MAC80211_DEBUGFS
|
|
|
|
.add_sta_debugfs = iwl3945_add_debugfs,
|
|
|
|
.remove_sta_debugfs = iwl3945_remove_debugfs,
|
|
|
|
#endif
|
|
|
|
|
2007-09-26 08:54:57 +08:00
|
|
|
};
|
2008-01-28 08:41:47 +08:00
|
|
|
void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id)
|
2007-09-26 08:54:57 +08:00
|
|
|
{
|
2008-12-19 10:37:28 +08:00
|
|
|
struct iwl_priv *priv = hw->priv;
|
2007-09-26 08:54:57 +08:00
|
|
|
s32 rssi = 0;
|
|
|
|
unsigned long flags;
|
2008-01-28 12:41:38 +08:00
|
|
|
struct iwl3945_rs_sta *rs_sta;
|
2008-09-19 00:14:18 +08:00
|
|
|
struct ieee80211_sta *sta;
|
|
|
|
struct iwl3945_sta_priv *psta;
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "enter\n");
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2008-02-25 23:27:46 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
|
2010-08-23 16:46:40 +08:00
|
|
|
sta = ieee80211_find_sta(priv->contexts[IWL_RXON_CTX_BSS].vif,
|
2009-11-04 21:42:28 +08:00
|
|
|
priv->stations[sta_id].sta.sta.addr);
|
2008-10-11 07:46:25 +08:00
|
|
|
if (!sta) {
|
2010-02-23 08:24:47 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "Unable to find station to initialize rate scaling.\n");
|
2008-02-25 23:27:46 +08:00
|
|
|
rcu_read_unlock();
|
2007-09-26 08:54:57 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-10-11 07:46:25 +08:00
|
|
|
psta = (void *) sta->drv_priv;
|
2009-10-31 05:36:13 +08:00
|
|
|
rs_sta = &psta->rs_sta;
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2008-01-28 12:41:38 +08:00
|
|
|
spin_lock_irqsave(&rs_sta->lock, flags);
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2008-01-28 12:41:38 +08:00
|
|
|
rs_sta->tgg = 0;
|
2008-01-25 02:38:38 +08:00
|
|
|
switch (priv->band) {
|
|
|
|
case IEEE80211_BAND_2GHZ:
|
|
|
|
/* TODO: this always does G, not a regression */
|
2010-08-23 16:46:32 +08:00
|
|
|
if (priv->contexts[IWL_RXON_CTX_BSS].active.flags &
|
|
|
|
RXON_FLG_TGG_PROTECT_MSK) {
|
2008-01-28 12:41:38 +08:00
|
|
|
rs_sta->tgg = 1;
|
|
|
|
rs_sta->expected_tpt = iwl3945_expected_tpt_g_prot;
|
2007-09-26 08:54:57 +08:00
|
|
|
} else
|
2008-01-28 12:41:38 +08:00
|
|
|
rs_sta->expected_tpt = iwl3945_expected_tpt_g;
|
2007-09-26 08:54:57 +08:00
|
|
|
break;
|
|
|
|
|
2008-01-25 02:38:38 +08:00
|
|
|
case IEEE80211_BAND_5GHZ:
|
2008-01-28 12:41:38 +08:00
|
|
|
rs_sta->expected_tpt = iwl3945_expected_tpt_a;
|
2007-09-26 08:54:57 +08:00
|
|
|
break;
|
2008-01-25 02:38:38 +08:00
|
|
|
case IEEE80211_NUM_BANDS:
|
|
|
|
BUG();
|
2007-09-26 08:54:57 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-01-28 12:41:38 +08:00
|
|
|
spin_unlock_irqrestore(&rs_sta->lock, flags);
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2010-01-20 02:04:28 +08:00
|
|
|
rssi = priv->_3945.last_rx_rssi;
|
2007-09-26 08:54:57 +08:00
|
|
|
if (rssi == 0)
|
|
|
|
rssi = IWL_MIN_RSSI_VAL;
|
|
|
|
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "Network RSSI: %d\n", rssi);
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2008-01-25 02:38:38 +08:00
|
|
|
rs_sta->start_rate = iwl3945_get_rate_index_by_rssi(rssi, priv->band);
|
2007-09-26 08:54:57 +08:00
|
|
|
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_RATE(priv, "leave: rssi %d assign rate index: "
|
2008-01-28 12:41:38 +08:00
|
|
|
"%d (plcp 0x%x)\n", rssi, rs_sta->start_rate,
|
|
|
|
iwl3945_rates[rs_sta->start_rate].plcp);
|
2008-10-11 07:46:25 +08:00
|
|
|
rcu_read_unlock();
|
2007-09-26 08:54:57 +08:00
|
|
|
}
|
|
|
|
|
2008-03-29 07:21:09 +08:00
|
|
|
int iwl3945_rate_control_register(void)
|
2007-09-26 08:54:57 +08:00
|
|
|
{
|
2008-03-29 07:21:09 +08:00
|
|
|
return ieee80211_rate_control_register(&rs_ops);
|
2007-09-26 08:54:57 +08:00
|
|
|
}
|
|
|
|
|
2008-03-29 07:21:09 +08:00
|
|
|
void iwl3945_rate_control_unregister(void)
|
2007-09-26 08:54:57 +08:00
|
|
|
{
|
|
|
|
ieee80211_rate_control_unregister(&rs_ops);
|
|
|
|
}
|
|
|
|
|
|
|
|
|