mirror of
https://github.com/torvalds/linux
synced 2024-11-05 18:23:50 +00:00
c04f9f2203
fw_restart module parameter was broken by the recent check for stuck queue patch, driver check the fx_restart module parameter before reload the firmware; but the stuck queue timer kick in after firmware error and reload the firmware even fw_restart=0. In this case, driver should not reload the firmware, it is important to help debugging uCode error. The only case we can ignore the module parameter is when user request firmware reload from debugfs, which can bypass the checking and perform firmware reload all the time. Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com> Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
305 lines
9.7 KiB
C
305 lines
9.7 KiB
C
/******************************************************************************
|
|
*
|
|
* Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved.
|
|
*
|
|
* Portions of this file are derived from the ipw3945 project, as well
|
|
* as portions of the ieee80211 subsystem header files.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of version 2 of the GNU General Public License as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
|
|
*
|
|
* The full GNU General Public License is included in this distribution in the
|
|
* file called LICENSE.
|
|
*
|
|
* Contact Information:
|
|
* Intel Linux Wireless <ilw@linux.intel.com>
|
|
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/slab.h>
|
|
#include <net/mac80211.h>
|
|
#include <asm/unaligned.h>
|
|
#include "iwl-eeprom.h"
|
|
#include "iwl-dev.h"
|
|
#include "iwl-core.h"
|
|
#include "iwl-sta.h"
|
|
#include "iwl-io.h"
|
|
#include "iwl-calib.h"
|
|
#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
|
|
*/
|
|
void iwl_rx_queue_update_write_ptr(struct iwl_priv *priv, struct iwl_rx_queue *q)
|
|
{
|
|
unsigned long flags;
|
|
u32 rx_wrt_ptr_reg = priv->hw_params.rx_wrt_ptr_reg;
|
|
u32 reg;
|
|
|
|
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) {
|
|
IWL_DEBUG_INFO(priv, "Rx queue requesting wakeup, GP1 = 0x%x\n",
|
|
reg);
|
|
iwl_set_bit(priv, CSR_GP_CNTRL,
|
|
CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
|
|
goto exit_unlock;
|
|
}
|
|
|
|
q->write_actual = (q->write & ~0x7);
|
|
iwl_write_direct32(priv, rx_wrt_ptr_reg, q->write_actual);
|
|
|
|
/* Else device is assumed to be awake */
|
|
} else {
|
|
/* Device expects a multiple of 8 */
|
|
q->write_actual = (q->write & ~0x7);
|
|
iwl_write_direct32(priv, rx_wrt_ptr_reg, q->write_actual);
|
|
}
|
|
|
|
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;
|
|
struct device *dev = &priv->pci_dev->dev;
|
|
int i;
|
|
|
|
spin_lock_init(&rxq->lock);
|
|
INIT_LIST_HEAD(&rxq->rx_free);
|
|
INIT_LIST_HEAD(&rxq->rx_used);
|
|
|
|
/* Alloc the circular buffer of Read Buffer Descriptors (RBDs) */
|
|
rxq->bd = dma_alloc_coherent(dev, 4 * RX_QUEUE_SIZE, &rxq->bd_dma,
|
|
GFP_KERNEL);
|
|
if (!rxq->bd)
|
|
goto err_bd;
|
|
|
|
rxq->rb_stts = dma_alloc_coherent(dev, sizeof(struct iwl_rb_status),
|
|
&rxq->rb_stts_dma, GFP_KERNEL);
|
|
if (!rxq->rb_stts)
|
|
goto err_rb;
|
|
|
|
/* Fill the rx_used queue with _all_ of the Rx buffers */
|
|
for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++)
|
|
list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
|
|
|
|
/* Set us so that we have processed and used all buffers, but have
|
|
* not restocked the Rx queue with fresh buffers */
|
|
rxq->read = rxq->write = 0;
|
|
rxq->write_actual = 0;
|
|
rxq->free_count = 0;
|
|
rxq->need_update = 0;
|
|
return 0;
|
|
|
|
err_rb:
|
|
dma_free_coherent(&priv->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd,
|
|
rxq->bd_dma);
|
|
err_bd:
|
|
return -ENOMEM;
|
|
}
|
|
EXPORT_SYMBOL(iwl_rx_queue_alloc);
|
|
|
|
|
|
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);
|
|
|
|
void iwl_recover_from_statistics(struct iwl_priv *priv,
|
|
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");
|
|
if (!iwl_force_reset(priv, IWL_FW_RESET, false))
|
|
return;
|
|
}
|
|
}
|
|
if (priv->cfg->ops->lib->check_plcp_health) {
|
|
if (!priv->cfg->ops->lib->check_plcp_health(
|
|
priv, pkt)) {
|
|
/*
|
|
* high plcp error detected
|
|
* reset Radio
|
|
*/
|
|
iwl_force_reset(priv, IWL_RF_RESET, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(iwl_recover_from_statistics);
|
|
|
|
/*
|
|
* returns non-zero if packet should be dropped
|
|
*/
|
|
int iwl_set_decrypted_flag(struct iwl_priv *priv,
|
|
struct ieee80211_hdr *hdr,
|
|
u32 decrypt_res,
|
|
struct ieee80211_rx_status *stats)
|
|
{
|
|
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;
|
|
|
|
IWL_DEBUG_RX(priv, "decrypt_res:0x%x\n", decrypt_res);
|
|
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 */
|
|
IWL_DEBUG_RX(priv, "Packet destroyed\n");
|
|
return -1;
|
|
}
|
|
case RX_RES_STATUS_SEC_TYPE_CCMP:
|
|
if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
|
|
RX_RES_STATUS_DECRYPT_OK) {
|
|
IWL_DEBUG_RX(priv, "hw decrypt successfully!!!\n");
|
|
stats->flag |= RX_FLAG_DECRYPTED;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(iwl_set_decrypted_flag);
|