2008-05-05 02:22:28 +00:00
|
|
|
/******************************************************************************
|
|
|
|
*
|
2010-01-15 21:43:41 +00:00
|
|
|
* Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved.
|
2008-05-05 02:22:28 +00:00
|
|
|
*
|
|
|
|
* 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:
|
2008-12-09 19:28:58 +00:00
|
|
|
* Intel Linux Wireless <ilw@linux.intel.com>
|
2008-05-05 02:22:28 +00:00
|
|
|
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
|
2008-06-30 09:23:09 +00:00
|
|
|
#include <linux/etherdevice.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 08:04:11 +00:00
|
|
|
#include <linux/slab.h>
|
2008-05-05 02:22:28 +00:00
|
|
|
#include <net/mac80211.h>
|
2008-07-10 11:28:42 +00:00
|
|
|
#include <asm/unaligned.h>
|
2008-05-05 02:22:28 +00:00
|
|
|
#include "iwl-eeprom.h"
|
|
|
|
#include "iwl-dev.h"
|
|
|
|
#include "iwl-core.h"
|
|
|
|
#include "iwl-sta.h"
|
|
|
|
#include "iwl-io.h"
|
2008-05-29 08:35:04 +00:00
|
|
|
#include "iwl-calib.h"
|
2008-05-05 02:22:28 +00:00
|
|
|
#include "iwl-helpers.h"
|
|
|
|
/************************** 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 the NIC. These get
|
|
|
|
* used not only for Rx frames, but for any command response or notification
|
|
|
|
* from the NIC. The driver and NIC 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 iwl_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:
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(iwl_rx_queue_space);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* iwl_rx_queue_update_write_ptr - Update the write pointer for the RX queue
|
|
|
|
*/
|
2010-02-03 21:47:56 +00:00
|
|
|
void iwl_rx_queue_update_write_ptr(struct iwl_priv *priv, struct iwl_rx_queue *q)
|
2008-05-05 02:22:28 +00:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
2009-01-08 18:19:53 +00:00
|
|
|
u32 rx_wrt_ptr_reg = priv->hw_params.rx_wrt_ptr_reg;
|
|
|
|
u32 reg;
|
2008-05-05 02:22:28 +00:00
|
|
|
|
|
|
|
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 = iwl_read32(priv, CSR_UCODE_DRV_GP1);
|
|
|
|
|
|
|
|
if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
|
2009-11-06 22:53:03 +00:00
|
|
|
IWL_DEBUG_INFO(priv, "Rx queue requesting wakeup, GP1 = 0x%x\n",
|
|
|
|
reg);
|
2008-05-05 02:22:28 +00:00
|
|
|
iwl_set_bit(priv, CSR_GP_CNTRL,
|
|
|
|
CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
|
|
|
|
goto exit_unlock;
|
|
|
|
}
|
|
|
|
|
2009-05-22 18:01:51 +00:00
|
|
|
q->write_actual = (q->write & ~0x7);
|
|
|
|
iwl_write_direct32(priv, rx_wrt_ptr_reg, q->write_actual);
|
2008-05-05 02:22:28 +00:00
|
|
|
|
|
|
|
/* Else device is assumed to be awake */
|
2009-01-08 18:19:53 +00:00
|
|
|
} else {
|
2008-05-05 02:22:28 +00:00
|
|
|
/* Device expects a multiple of 8 */
|
2009-05-22 18:01:51 +00:00
|
|
|
q->write_actual = (q->write & ~0x7);
|
|
|
|
iwl_write_direct32(priv, rx_wrt_ptr_reg, q->write_actual);
|
2009-01-08 18:19:53 +00:00
|
|
|
}
|
2008-05-05 02:22:28 +00:00
|
|
|
|
|
|
|
q->need_update = 0;
|
|
|
|
|
|
|
|
exit_unlock:
|
|
|
|
spin_unlock_irqrestore(&q->lock, flags);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(iwl_rx_queue_update_write_ptr);
|
|
|
|
|
|
|
|
int iwl_rx_queue_alloc(struct iwl_priv *priv)
|
|
|
|
{
|
|
|
|
struct iwl_rx_queue *rxq = &priv->rxq;
|
2010-02-10 13:07:45 +00:00
|
|
|
struct device *dev = &priv->pci_dev->dev;
|
2008-05-05 02:22:28 +00:00
|
|
|
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) */
|
2010-02-10 13:07:45 +00:00
|
|
|
rxq->bd = dma_alloc_coherent(dev, 4 * RX_QUEUE_SIZE, &rxq->dma_addr,
|
|
|
|
GFP_KERNEL);
|
2008-05-05 02:22:28 +00:00
|
|
|
if (!rxq->bd)
|
2008-11-07 17:58:39 +00:00
|
|
|
goto err_bd;
|
|
|
|
|
2010-02-10 13:07:45 +00:00
|
|
|
rxq->rb_stts = dma_alloc_coherent(dev, sizeof(struct iwl_rb_status),
|
|
|
|
&rxq->rb_stts_dma, GFP_KERNEL);
|
2008-11-07 17:58:39 +00:00
|
|
|
if (!rxq->rb_stts)
|
|
|
|
goto err_rb;
|
2008-05-05 02:22:28 +00:00
|
|
|
|
|
|
|
/* 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;
|
2009-05-22 18:01:51 +00:00
|
|
|
rxq->write_actual = 0;
|
2008-05-05 02:22:28 +00:00
|
|
|
rxq->free_count = 0;
|
|
|
|
rxq->need_update = 0;
|
|
|
|
return 0;
|
2008-11-07 17:58:39 +00:00
|
|
|
|
|
|
|
err_rb:
|
2010-02-10 13:07:45 +00:00
|
|
|
dma_free_coherent(&priv->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd,
|
|
|
|
rxq->dma_addr);
|
2008-11-07 17:58:39 +00:00
|
|
|
err_bd:
|
|
|
|
return -ENOMEM;
|
2008-05-05 02:22:28 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(iwl_rx_queue_alloc);
|
|
|
|
|
2008-05-29 08:35:04 +00:00
|
|
|
void iwl_rx_missed_beacon_notif(struct iwl_priv *priv,
|
|
|
|
struct iwl_rx_mem_buffer *rxb)
|
|
|
|
|
|
|
|
{
|
2009-10-09 09:19:45 +00:00
|
|
|
struct iwl_rx_packet *pkt = rxb_addr(rxb);
|
2008-12-11 18:33:40 +00:00
|
|
|
struct iwl_missed_beacon_notif *missed_beacon;
|
2008-05-29 08:35:04 +00:00
|
|
|
|
|
|
|
missed_beacon = &pkt->u.missed_beacon;
|
2010-01-22 22:22:42 +00:00
|
|
|
if (le32_to_cpu(missed_beacon->consecutive_missed_beacons) >
|
|
|
|
priv->missed_beacon_threshold) {
|
2009-01-27 22:27:56 +00:00
|
|
|
IWL_DEBUG_CALIB(priv, "missed bcn cnsq %d totl %d rcd %d expctd %d\n",
|
2010-01-22 22:22:42 +00:00
|
|
|
le32_to_cpu(missed_beacon->consecutive_missed_beacons),
|
2008-05-29 08:35:04 +00:00
|
|
|
le32_to_cpu(missed_beacon->total_missed_becons),
|
|
|
|
le32_to_cpu(missed_beacon->num_recvd_beacons),
|
|
|
|
le32_to_cpu(missed_beacon->num_expected_beacons));
|
|
|
|
if (!test_bit(STATUS_SCANNING, &priv->status))
|
|
|
|
iwl_init_sensitivity(priv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(iwl_rx_missed_beacon_notif);
|
2008-06-30 09:23:07 +00:00
|
|
|
|
2010-01-22 22:22:57 +00:00
|
|
|
void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv,
|
|
|
|
struct iwl_rx_mem_buffer *rxb)
|
|
|
|
{
|
|
|
|
struct iwl_rx_packet *pkt = rxb_addr(rxb);
|
|
|
|
struct iwl_spectrum_notification *report = &(pkt->u.spectrum_notif);
|
|
|
|
|
|
|
|
if (!report->state) {
|
|
|
|
IWL_DEBUG_11H(priv,
|
|
|
|
"Spectrum Measure Notification: Start\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(&priv->measure_report, report, sizeof(*report));
|
|
|
|
priv->measurement_status |= MEASUREMENT_READY;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(iwl_rx_spectrum_measure_notif);
|
|
|
|
|
|
|
|
|
2008-06-30 09:23:07 +00:00
|
|
|
|
|
|
|
/* 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 iwl_rx_calc_noise(struct iwl_priv *priv)
|
|
|
|
{
|
|
|
|
struct statistics_rx_non_phy *rx_info
|
2010-06-03 17:55:37 +00:00
|
|
|
= &(priv->statistics.rx.general);
|
2008-06-30 09:23:07 +00:00
|
|
|
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;
|
2010-03-18 16:58:27 +00:00
|
|
|
int last_rx_noise;
|
2008-06-30 09:23:07 +00:00
|
|
|
|
|
|
|
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)
|
2010-03-18 16:58:27 +00:00
|
|
|
last_rx_noise = (total_silence / num_active_rx) - 107;
|
2008-06-30 09:23:07 +00:00
|
|
|
else
|
2010-03-18 16:58:27 +00:00
|
|
|
last_rx_noise = IWL_NOISE_MEAS_NOT_AVAILABLE;
|
2008-06-30 09:23:07 +00:00
|
|
|
|
2009-01-27 22:27:56 +00:00
|
|
|
IWL_DEBUG_CALIB(priv, "inband silence a %u, b %u, c %u, dBm %d\n",
|
2008-06-30 09:23:07 +00:00
|
|
|
bcn_silence_a, bcn_silence_b, bcn_silence_c,
|
2010-03-18 16:58:27 +00:00
|
|
|
last_rx_noise);
|
2008-06-30 09:23:07 +00:00
|
|
|
}
|
|
|
|
|
2010-05-06 19:18:41 +00:00
|
|
|
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
2009-10-09 20:20:29 +00:00
|
|
|
/*
|
|
|
|
* based on the assumption of all statistics counter are in DWORD
|
|
|
|
* FIXME: This function is for debugging, do not deal with
|
|
|
|
* the case of counters roll-over.
|
|
|
|
*/
|
|
|
|
static void iwl_accumulative_statistics(struct iwl_priv *priv,
|
|
|
|
__le32 *stats)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
__le32 *prev_stats;
|
|
|
|
u32 *accum_stats;
|
2010-01-15 21:43:34 +00:00
|
|
|
u32 *delta, *max_delta;
|
2009-10-09 20:20:29 +00:00
|
|
|
|
2010-06-03 17:55:37 +00:00
|
|
|
prev_stats = (__le32 *)&priv->statistics;
|
|
|
|
accum_stats = (u32 *)&priv->accum_statistics;
|
|
|
|
delta = (u32 *)&priv->delta_statistics;
|
|
|
|
max_delta = (u32 *)&priv->max_delta;
|
2009-10-09 20:20:29 +00:00
|
|
|
|
|
|
|
for (i = sizeof(__le32); i < sizeof(struct iwl_notif_statistics);
|
2010-01-15 21:43:34 +00:00
|
|
|
i += sizeof(__le32), stats++, prev_stats++, delta++,
|
|
|
|
max_delta++, accum_stats++) {
|
|
|
|
if (le32_to_cpu(*stats) > le32_to_cpu(*prev_stats)) {
|
|
|
|
*delta = (le32_to_cpu(*stats) -
|
2009-10-09 20:20:29 +00:00
|
|
|
le32_to_cpu(*prev_stats));
|
2010-01-15 21:43:34 +00:00
|
|
|
*accum_stats += *delta;
|
|
|
|
if (*delta > *max_delta)
|
|
|
|
*max_delta = *delta;
|
|
|
|
}
|
|
|
|
}
|
2009-10-09 20:20:29 +00:00
|
|
|
|
|
|
|
/* reset accumulative statistics for "no-counter" type statistics */
|
2010-06-03 17:55:37 +00:00
|
|
|
priv->accum_statistics.general.temperature =
|
|
|
|
priv->statistics.general.temperature;
|
|
|
|
priv->accum_statistics.general.temperature_m =
|
|
|
|
priv->statistics.general.temperature_m;
|
|
|
|
priv->accum_statistics.general.ttl_timestamp =
|
|
|
|
priv->statistics.general.ttl_timestamp;
|
|
|
|
priv->accum_statistics.tx.tx_power.ant_a =
|
|
|
|
priv->statistics.tx.tx_power.ant_a;
|
|
|
|
priv->accum_statistics.tx.tx_power.ant_b =
|
|
|
|
priv->statistics.tx.tx_power.ant_b;
|
|
|
|
priv->accum_statistics.tx.tx_power.ant_c =
|
|
|
|
priv->statistics.tx.tx_power.ant_c;
|
2009-10-09 20:20:29 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-06-30 09:23:07 +00:00
|
|
|
#define REG_RECALIB_PERIOD (60)
|
|
|
|
|
2010-03-05 22:22:46 +00:00
|
|
|
/**
|
|
|
|
* iwl_good_plcp_health - checks for plcp error.
|
|
|
|
*
|
|
|
|
* When the plcp error is exceeding the thresholds, reset the radio
|
|
|
|
* to improve the throughput.
|
|
|
|
*/
|
|
|
|
bool iwl_good_plcp_health(struct iwl_priv *priv,
|
|
|
|
struct iwl_rx_packet *pkt)
|
|
|
|
{
|
|
|
|
bool rc = true;
|
|
|
|
int combined_plcp_delta;
|
|
|
|
unsigned int plcp_msec;
|
|
|
|
unsigned long plcp_received_jiffies;
|
2008-06-30 09:23:07 +00:00
|
|
|
|
2010-01-22 22:22:46 +00:00
|
|
|
/*
|
|
|
|
* check for plcp_err and trigger radio reset if it exceeds
|
|
|
|
* the plcp error threshold plcp_delta.
|
|
|
|
*/
|
|
|
|
plcp_received_jiffies = jiffies;
|
|
|
|
plcp_msec = jiffies_to_msecs((long) plcp_received_jiffies -
|
|
|
|
(long) priv->plcp_jiffies);
|
|
|
|
priv->plcp_jiffies = plcp_received_jiffies;
|
|
|
|
/*
|
|
|
|
* check to make sure plcp_msec is not 0 to prevent division
|
|
|
|
* by zero.
|
|
|
|
*/
|
|
|
|
if (plcp_msec) {
|
|
|
|
combined_plcp_delta =
|
|
|
|
(le32_to_cpu(pkt->u.stats.rx.ofdm.plcp_err) -
|
2010-06-03 17:55:37 +00:00
|
|
|
le32_to_cpu(priv->statistics.rx.ofdm.plcp_err)) +
|
2010-01-22 22:22:46 +00:00
|
|
|
(le32_to_cpu(pkt->u.stats.rx.ofdm_ht.plcp_err) -
|
2010-06-03 17:55:37 +00:00
|
|
|
le32_to_cpu(priv->statistics.rx.ofdm_ht.plcp_err));
|
2010-01-22 22:22:46 +00:00
|
|
|
|
|
|
|
if ((combined_plcp_delta > 0) &&
|
2010-03-04 21:38:58 +00:00
|
|
|
((combined_plcp_delta * 100) / plcp_msec) >
|
2010-01-22 22:22:46 +00:00
|
|
|
priv->cfg->plcp_delta_threshold) {
|
|
|
|
/*
|
2010-03-04 21:38:58 +00:00
|
|
|
* if plcp_err exceed the threshold,
|
|
|
|
* the following data is printed in csv format:
|
2010-01-22 22:22:46 +00:00
|
|
|
* Text: plcp_err exceeded %d,
|
|
|
|
* Received ofdm.plcp_err,
|
|
|
|
* Current ofdm.plcp_err,
|
|
|
|
* Received ofdm_ht.plcp_err,
|
|
|
|
* Current ofdm_ht.plcp_err,
|
|
|
|
* combined_plcp_delta,
|
|
|
|
* plcp_msec
|
|
|
|
*/
|
2010-03-05 22:22:46 +00:00
|
|
|
IWL_DEBUG_RADIO(priv, "plcp_err exceeded %u, "
|
|
|
|
"%u, %u, %u, %u, %d, %u mSecs\n",
|
2010-01-22 22:22:46 +00:00
|
|
|
priv->cfg->plcp_delta_threshold,
|
|
|
|
le32_to_cpu(pkt->u.stats.rx.ofdm.plcp_err),
|
2010-06-03 17:55:37 +00:00
|
|
|
le32_to_cpu(priv->statistics.rx.ofdm.plcp_err),
|
2010-01-22 22:22:46 +00:00
|
|
|
le32_to_cpu(pkt->u.stats.rx.ofdm_ht.plcp_err),
|
|
|
|
le32_to_cpu(
|
2010-06-03 17:55:37 +00:00
|
|
|
priv->statistics.rx.ofdm_ht.plcp_err),
|
2010-01-22 22:22:46 +00:00
|
|
|
combined_plcp_delta, plcp_msec);
|
2010-03-05 22:22:46 +00:00
|
|
|
rc = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(iwl_good_plcp_health);
|
|
|
|
|
2010-04-28 22:47:04 +00:00
|
|
|
void iwl_recover_from_statistics(struct iwl_priv *priv,
|
2010-03-05 22:22:46 +00:00
|
|
|
struct iwl_rx_packet *pkt)
|
|
|
|
{
|
|
|
|
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
|
|
|
return;
|
|
|
|
if (iwl_is_associated(priv)) {
|
|
|
|
if (priv->cfg->ops->lib->check_ack_health) {
|
|
|
|
if (!priv->cfg->ops->lib->check_ack_health(
|
|
|
|
priv, pkt)) {
|
|
|
|
/*
|
|
|
|
* low ack count detected
|
|
|
|
* restart Firmware
|
|
|
|
*/
|
|
|
|
IWL_ERR(priv, "low ack count detected, "
|
|
|
|
"restart firmware\n");
|
2010-04-28 01:43:33 +00:00
|
|
|
if (!iwl_force_reset(priv, IWL_FW_RESET))
|
|
|
|
return;
|
2010-03-05 22:22:46 +00:00
|
|
|
}
|
2010-04-28 01:43:33 +00:00
|
|
|
}
|
|
|
|
if (priv->cfg->ops->lib->check_plcp_health) {
|
2010-03-05 22:22:46 +00:00
|
|
|
if (!priv->cfg->ops->lib->check_plcp_health(
|
|
|
|
priv, pkt)) {
|
|
|
|
/*
|
|
|
|
* high plcp error detected
|
|
|
|
* reset Radio
|
|
|
|
*/
|
|
|
|
iwl_force_reset(priv, IWL_RF_RESET);
|
|
|
|
}
|
2010-01-22 22:22:46 +00:00
|
|
|
}
|
|
|
|
}
|
2010-03-04 21:38:58 +00:00
|
|
|
}
|
2010-04-28 22:47:04 +00:00
|
|
|
EXPORT_SYMBOL(iwl_recover_from_statistics);
|
2010-03-04 21:38:58 +00:00
|
|
|
|
|
|
|
void iwl_rx_statistics(struct iwl_priv *priv,
|
|
|
|
struct iwl_rx_mem_buffer *rxb)
|
|
|
|
{
|
|
|
|
int change;
|
|
|
|
struct iwl_rx_packet *pkt = rxb_addr(rxb);
|
|
|
|
|
|
|
|
|
|
|
|
IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n",
|
2010-06-03 17:55:37 +00:00
|
|
|
(int)sizeof(priv->statistics),
|
2010-03-04 21:38:58 +00:00
|
|
|
le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK);
|
|
|
|
|
2010-06-03 17:55:37 +00:00
|
|
|
change = ((priv->statistics.general.temperature !=
|
2010-03-04 21:38:58 +00:00
|
|
|
pkt->u.stats.general.temperature) ||
|
2010-06-03 17:55:37 +00:00
|
|
|
((priv->statistics.flag &
|
2010-03-04 21:38:58 +00:00
|
|
|
STATISTICS_REPLY_FLG_HT40_MODE_MSK) !=
|
|
|
|
(pkt->u.stats.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK)));
|
|
|
|
|
2010-05-06 19:18:41 +00:00
|
|
|
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
2010-03-04 21:38:58 +00:00
|
|
|
iwl_accumulative_statistics(priv, (__le32 *)&pkt->u.stats);
|
|
|
|
#endif
|
2010-03-05 22:22:46 +00:00
|
|
|
iwl_recover_from_statistics(priv, pkt);
|
2010-01-22 22:22:46 +00:00
|
|
|
|
2010-06-03 17:55:37 +00:00
|
|
|
memcpy(&priv->statistics, &pkt->u.stats, sizeof(priv->statistics));
|
2008-06-30 09:23:07 +00:00
|
|
|
|
|
|
|
set_bit(STATUS_STATISTICS, &priv->status);
|
|
|
|
|
|
|
|
/* Reschedule the statistics timer to occur in
|
|
|
|
* REG_RECALIB_PERIOD seconds to ensure we get a
|
|
|
|
* thermal update even if the uCode doesn't give
|
|
|
|
* us one */
|
|
|
|
mod_timer(&priv->statistics_periodic, jiffies +
|
|
|
|
msecs_to_jiffies(REG_RECALIB_PERIOD * 1000));
|
|
|
|
|
|
|
|
if (unlikely(!test_bit(STATUS_SCANNING, &priv->status)) &&
|
|
|
|
(pkt->hdr.cmd == STATISTICS_NOTIFICATION)) {
|
|
|
|
iwl_rx_calc_noise(priv);
|
|
|
|
queue_work(priv->workqueue, &priv->run_time_calib_work);
|
|
|
|
}
|
2009-05-21 20:44:23 +00:00
|
|
|
if (priv->cfg->ops->lib->temp_ops.temperature && change)
|
|
|
|
priv->cfg->ops->lib->temp_ops.temperature(priv);
|
2008-06-30 09:23:07 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(iwl_rx_statistics);
|
2008-06-30 09:23:09 +00:00
|
|
|
|
2009-11-13 19:56:28 +00:00
|
|
|
void iwl_reply_statistics(struct iwl_priv *priv,
|
|
|
|
struct iwl_rx_mem_buffer *rxb)
|
|
|
|
{
|
|
|
|
struct iwl_rx_packet *pkt = rxb_addr(rxb);
|
|
|
|
|
|
|
|
if (le32_to_cpu(pkt->u.stats.flag) & UCODE_STATISTICS_CLEAR_MSK) {
|
2010-05-06 19:18:41 +00:00
|
|
|
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
2010-06-03 17:55:37 +00:00
|
|
|
memset(&priv->accum_statistics, 0,
|
2009-11-13 19:56:28 +00:00
|
|
|
sizeof(struct iwl_notif_statistics));
|
2010-06-03 17:55:37 +00:00
|
|
|
memset(&priv->delta_statistics, 0,
|
2010-01-15 21:43:34 +00:00
|
|
|
sizeof(struct iwl_notif_statistics));
|
2010-06-03 17:55:37 +00:00
|
|
|
memset(&priv->max_delta, 0,
|
2010-01-15 21:43:34 +00:00
|
|
|
sizeof(struct iwl_notif_statistics));
|
2009-11-13 19:56:28 +00:00
|
|
|
#endif
|
|
|
|
IWL_DEBUG_RX(priv, "Statistics have been cleared\n");
|
|
|
|
}
|
|
|
|
iwl_rx_statistics(priv, rxb);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(iwl_reply_statistics);
|
|
|
|
|
2008-06-30 09:23:09 +00:00
|
|
|
/*
|
|
|
|
* returns non-zero if packet should be dropped
|
|
|
|
*/
|
2009-01-27 22:27:52 +00:00
|
|
|
int iwl_set_decrypted_flag(struct iwl_priv *priv,
|
|
|
|
struct ieee80211_hdr *hdr,
|
|
|
|
u32 decrypt_res,
|
|
|
|
struct ieee80211_rx_status *stats)
|
2008-06-30 09:23:09 +00:00
|
|
|
{
|
|
|
|
u16 fc = le16_to_cpu(hdr->frame_control);
|
|
|
|
|
|
|
|
if (priv->active_rxon.filter_flags & RXON_FILTER_DIS_DECRYPT_MSK)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!(fc & IEEE80211_FCTL_PROTECTED))
|
|
|
|
return 0;
|
|
|
|
|
2009-01-27 22:27:56 +00:00
|
|
|
IWL_DEBUG_RX(priv, "decrypt_res:0x%x\n", decrypt_res);
|
2008-06-30 09:23:09 +00:00
|
|
|
switch (decrypt_res & RX_RES_STATUS_SEC_TYPE_MSK) {
|
|
|
|
case RX_RES_STATUS_SEC_TYPE_TKIP:
|
|
|
|
/* The uCode has got a bad phase 1 Key, pushes the packet.
|
|
|
|
* Decryption will be done in SW. */
|
|
|
|
if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
|
|
|
|
RX_RES_STATUS_BAD_KEY_TTAK)
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RX_RES_STATUS_SEC_TYPE_WEP:
|
|
|
|
if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
|
|
|
|
RX_RES_STATUS_BAD_ICV_MIC) {
|
|
|
|
/* bad ICV, the packet is destroyed since the
|
|
|
|
* decryption is inplace, drop it */
|
2009-01-27 22:27:56 +00:00
|
|
|
IWL_DEBUG_RX(priv, "Packet destroyed\n");
|
2008-06-30 09:23:09 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
case RX_RES_STATUS_SEC_TYPE_CCMP:
|
|
|
|
if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
|
|
|
|
RX_RES_STATUS_DECRYPT_OK) {
|
2009-01-27 22:27:56 +00:00
|
|
|
IWL_DEBUG_RX(priv, "hw decrypt successfully!!!\n");
|
2008-06-30 09:23:09 +00:00
|
|
|
stats->flag |= RX_FLAG_DECRYPTED;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2009-01-27 22:27:52 +00:00
|
|
|
EXPORT_SYMBOL(iwl_set_decrypted_flag);
|