aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-agn-lib.c')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-lib.c169
1 files changed, 140 insertions, 29 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
index 08ccb9496f76..8e79653aed9a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
@@ -2,7 +2,7 @@
*
* GPL LICENSE SUMMARY
*
- * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2011 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
@@ -172,6 +172,7 @@ static void iwlagn_count_agg_tx_err_status(struct iwl_priv *priv, u16 status)
static void iwlagn_set_tx_status(struct iwl_priv *priv,
struct ieee80211_tx_info *info,
+ struct iwl_rxon_context *ctx,
struct iwlagn_tx_resp *tx_resp,
int txq_id, bool is_agg)
{
@@ -186,6 +187,13 @@ static void iwlagn_set_tx_status(struct iwl_priv *priv,
if (!iwl_is_tx_success(status))
iwlagn_count_tx_err_status(priv, status);
+ if (status == TX_STATUS_FAIL_PASSIVE_NO_RX &&
+ iwl_is_associated_ctx(ctx) && ctx->vif &&
+ ctx->vif->type == NL80211_IFTYPE_STATION) {
+ ctx->last_tx_rejected = true;
+ iwl_stop_queue(priv, &priv->txq[txq_id]);
+ }
+
IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x) rate_n_flags "
"0x%x retries %d\n",
txq_id,
@@ -242,15 +250,16 @@ static int iwlagn_tx_status_reply_tx(struct iwl_priv *priv,
/* # frames attempted by Tx command */
if (agg->frame_count == 1) {
+ struct iwl_tx_info *txb;
+
/* Only one frame was attempted; no block-ack will arrive */
idx = start_idx;
IWL_DEBUG_TX_REPLY(priv, "FrameCnt = %d, StartIdx=%d idx=%d\n",
agg->frame_count, agg->start_idx, idx);
- iwlagn_set_tx_status(priv,
- IEEE80211_SKB_CB(
- priv->txq[txq_id].txb[idx].skb),
- tx_resp, txq_id, true);
+ txb = &priv->txq[txq_id].txb[idx];
+ iwlagn_set_tx_status(priv, IEEE80211_SKB_CB(txb->skb),
+ txb->ctx, tx_resp, txq_id, true);
agg->wait_for_ba = 0;
} else {
/* Two or more frames were attempted; expect block-ack */
@@ -391,7 +400,8 @@ static void iwlagn_rx_reply_tx(struct iwl_priv *priv,
struct iwl_tx_queue *txq = &priv->txq[txq_id];
struct ieee80211_tx_info *info;
struct iwlagn_tx_resp *tx_resp = (void *)&pkt->u.raw[0];
- u32 status = le16_to_cpu(tx_resp->status.status);
+ struct iwl_tx_info *txb;
+ u32 status = le16_to_cpu(tx_resp->status.status);
int tid;
int sta_id;
int freed;
@@ -406,7 +416,8 @@ static void iwlagn_rx_reply_tx(struct iwl_priv *priv,
}
txq->time_stamp = jiffies;
- info = IEEE80211_SKB_CB(txq->txb[txq->q.read_ptr].skb);
+ txb = &txq->txb[txq->q.read_ptr];
+ info = IEEE80211_SKB_CB(txb->skb);
memset(&info->status, 0, sizeof(info->status));
tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >>
@@ -450,12 +461,14 @@ static void iwlagn_rx_reply_tx(struct iwl_priv *priv,
iwl_wake_queue(priv, txq);
}
} else {
- iwlagn_set_tx_status(priv, info, tx_resp, txq_id, false);
+ iwlagn_set_tx_status(priv, info, txb->ctx, tx_resp,
+ txq_id, false);
freed = iwlagn_tx_queue_reclaim(priv, txq_id, index);
iwl_free_tfds_in_queue(priv, sta_id, tid, freed);
if (priv->mac80211_registered &&
- (iwl_queue_space(&txq->q) > txq->q.low_mark))
+ iwl_queue_space(&txq->q) > txq->q.low_mark &&
+ status != TX_STATUS_FAIL_PASSIVE_NO_RX)
iwl_wake_queue(priv, txq);
}
@@ -470,8 +483,6 @@ void iwlagn_rx_handler_setup(struct iwl_priv *priv)
/* init calibration handlers */
priv->rx_handlers[CALIBRATION_RES_NOTIFICATION] =
iwlagn_rx_calib_result;
- priv->rx_handlers[CALIBRATION_COMPLETE_NOTIFICATION] =
- iwlagn_rx_calib_complete;
priv->rx_handlers[REPLY_TX] = iwlagn_rx_reply_tx;
/* set up notification wait support */
@@ -482,8 +493,10 @@ void iwlagn_rx_handler_setup(struct iwl_priv *priv)
void iwlagn_setup_deferred_work(struct iwl_priv *priv)
{
- /* in agn, the tx power calibration is done in uCode */
- priv->disable_tx_power_cal = 1;
+ /*
+ * nothing need to be done here anymore
+ * still keep for future use if needed
+ */
}
int iwlagn_hw_valid_rtc_data_addr(u32 addr)
@@ -534,9 +547,7 @@ int iwlagn_send_tx_power(struct iwl_priv *priv)
void iwlagn_temperature(struct iwl_priv *priv)
{
/* store temperature from correct statistics (in Celsius) */
- priv->temperature = le32_to_cpu((iwl_bt_statistics(priv)) ?
- priv->_agn.statistics_bt.general.common.temperature :
- priv->_agn.statistics.general.common.temperature);
+ priv->temperature = le32_to_cpu(priv->statistics.common.temperature);
iwl_tt_handler(priv);
}
@@ -652,10 +663,9 @@ int iwlagn_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */
u32 rb_timeout = 0; /* FIXME: RX_RB_TIMEOUT for all devices? */
- if (!priv->cfg->base_params->use_isr_legacy)
- rb_timeout = RX_RB_TIMEOUT;
+ rb_timeout = RX_RB_TIMEOUT;
- if (priv->cfg->mod_params->amsdu_size_8K)
+ if (iwlagn_mod_params.amsdu_size_8K)
rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K;
else
rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K;
@@ -913,7 +923,6 @@ void iwlagn_rx_allocate(struct iwl_priv *priv, gfp_t priority)
list_add_tail(&rxb->list, &rxq->rx_free);
rxq->free_count++;
- priv->alloc_rxb_page++;
spin_unlock_irqrestore(&rxq->lock, flags);
}
@@ -1285,9 +1294,17 @@ int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
* mean we never reach it, but at the same time work around
* the aforementioned issue. Thus use IWL_GOOD_CRC_TH_NEVER
* here instead of IWL_GOOD_CRC_TH_DISABLED.
+ *
+ * This was fixed in later versions along with some other
+ * scan changes, and the threshold behaves as a flag in those
+ * versions.
*/
- scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT :
- IWL_GOOD_CRC_TH_NEVER;
+ if (priv->new_scan_threshold_behaviour)
+ scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT :
+ IWL_GOOD_CRC_TH_DISABLED;
+ else
+ scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT :
+ IWL_GOOD_CRC_TH_NEVER;
band = priv->scan_band;
@@ -2245,34 +2262,44 @@ int iwl_dump_fh(struct iwl_priv *priv, char **buf, bool display)
/* notification wait support */
void iwlagn_init_notification_wait(struct iwl_priv *priv,
struct iwl_notification_wait *wait_entry,
+ u8 cmd,
void (*fn)(struct iwl_priv *priv,
- struct iwl_rx_packet *pkt),
- u8 cmd)
+ struct iwl_rx_packet *pkt,
+ void *data),
+ void *fn_data)
{
wait_entry->fn = fn;
+ wait_entry->fn_data = fn_data;
wait_entry->cmd = cmd;
wait_entry->triggered = false;
+ wait_entry->aborted = false;
spin_lock_bh(&priv->_agn.notif_wait_lock);
list_add(&wait_entry->list, &priv->_agn.notif_waits);
spin_unlock_bh(&priv->_agn.notif_wait_lock);
}
-signed long iwlagn_wait_notification(struct iwl_priv *priv,
- struct iwl_notification_wait *wait_entry,
- unsigned long timeout)
+int iwlagn_wait_notification(struct iwl_priv *priv,
+ struct iwl_notification_wait *wait_entry,
+ unsigned long timeout)
{
int ret;
ret = wait_event_timeout(priv->_agn.notif_waitq,
- wait_entry->triggered,
+ wait_entry->triggered || wait_entry->aborted,
timeout);
spin_lock_bh(&priv->_agn.notif_wait_lock);
list_del(&wait_entry->list);
spin_unlock_bh(&priv->_agn.notif_wait_lock);
- return ret;
+ if (wait_entry->aborted)
+ return -EIO;
+
+ /* return value is always >= 0 */
+ if (ret <= 0)
+ return -ETIMEDOUT;
+ return 0;
}
void iwlagn_remove_notification(struct iwl_priv *priv,
@@ -2282,3 +2309,87 @@ void iwlagn_remove_notification(struct iwl_priv *priv,
list_del(&wait_entry->list);
spin_unlock_bh(&priv->_agn.notif_wait_lock);
}
+
+int iwlagn_start_device(struct iwl_priv *priv)
+{
+ int ret;
+
+ if (iwl_prepare_card_hw(priv)) {
+ IWL_WARN(priv, "Exit HW not ready\n");
+ return -EIO;
+ }
+
+ /* If platform's RF_KILL switch is NOT set to KILL */
+ if (iwl_read32(priv, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)
+ clear_bit(STATUS_RF_KILL_HW, &priv->status);
+ else
+ set_bit(STATUS_RF_KILL_HW, &priv->status);
+
+ if (iwl_is_rfkill(priv)) {
+ wiphy_rfkill_set_hw_state(priv->hw->wiphy, true);
+ iwl_enable_interrupts(priv);
+ return -ERFKILL;
+ }
+
+ iwl_write32(priv, CSR_INT, 0xFFFFFFFF);
+
+ ret = iwlagn_hw_nic_init(priv);
+ if (ret) {
+ IWL_ERR(priv, "Unable to init nic\n");
+ return ret;
+ }
+
+ /* make sure rfkill handshake bits are cleared */
+ iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
+ iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR,
+ CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
+
+ /* clear (again), then enable host interrupts */
+ iwl_write32(priv, CSR_INT, 0xFFFFFFFF);
+ iwl_enable_interrupts(priv);
+
+ /* really make sure rfkill handshake bits are cleared */
+ iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
+ iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
+
+ return 0;
+}
+
+void iwlagn_stop_device(struct iwl_priv *priv)
+{
+ unsigned long flags;
+
+ /* stop and reset the on-board processor */
+ iwl_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
+
+ /* tell the device to stop sending interrupts */
+ spin_lock_irqsave(&priv->lock, flags);
+ iwl_disable_interrupts(priv);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ iwl_synchronize_irq(priv);
+
+ /* device going down, Stop using ICT table */
+ iwl_disable_ict(priv);
+
+ /*
+ * If a HW restart happens during firmware loading,
+ * then the firmware loading might call this function
+ * and later it might be called again due to the
+ * restart. So don't process again if the device is
+ * already dead.
+ */
+ if (test_bit(STATUS_DEVICE_ENABLED, &priv->status)) {
+ iwlagn_txq_ctx_stop(priv);
+ iwlagn_rxq_stop(priv);
+
+ /* Power-down device's busmaster DMA clocks */
+ iwl_write_prph(priv, APMG_CLK_DIS_REG, APMG_CLK_VAL_DMA_CLK_RQT);
+ udelay(5);
+ }
+
+ /* Make sure (redundant) we've released our request to stay awake */
+ iwl_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+
+ /* Stop the device, and put it in low power state */
+ iwl_apm_stop(priv);
+}