aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/wireless/iwlwifi/iwl-agn.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2009-12-08 07:55:01 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2009-12-08 07:55:01 -0800
commitd7fc02c7bae7b1cf69269992cf880a43a350cdaa (patch)
treea43d56fa72913a1cc98a0bbebe054d08581b3a7c /drivers/net/wireless/iwlwifi/iwl-agn.c
parentee1262dbc65ce0b6234a915d8432171e8d77f518 (diff)
parent28b4d5cc17c20786848cdc07b7ea237a309776bb (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6: (1815 commits) mac80211: fix reorder buffer release iwmc3200wifi: Enable wimax core through module parameter iwmc3200wifi: Add wifi-wimax coexistence mode as a module parameter iwmc3200wifi: Coex table command does not expect a response iwmc3200wifi: Update wiwi priority table iwlwifi: driver version track kernel version iwlwifi: indicate uCode type when fail dump error/event log iwl3945: remove duplicated event logging code b43: fix two warnings ipw2100: fix rebooting hang with driver loaded cfg80211: indent regulatory messages with spaces iwmc3200wifi: fix NULL pointer dereference in pmkid update mac80211: Fix TX status reporting for injected data frames ath9k: enable 2GHz band only if the device supports it airo: Fix integer overflow warning rt2x00: Fix padding bug on L2PAD devices. WE: Fix set events not propagated b43legacy: avoid PPC fault during resume b43: avoid PPC fault during resume tcp: fix a timewait refcnt race ... Fix up conflicts due to sysctl cleanups (dead sysctl_check code and CTL_UNNUMBERED removed) in kernel/sysctl_check.c net/ipv4/sysctl_net_ipv4.c net/ipv6/addrconf.c net/sctp/sysctl.c
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-agn.c')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.c776
1 files changed, 614 insertions, 162 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index 921dc4a26fe..b8377efb3ba 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -123,6 +123,17 @@ int iwl_commit_rxon(struct iwl_priv *priv)
return -EINVAL;
}
+ /*
+ * receive commit_rxon request
+ * abort any previous channel switch if still in process
+ */
+ if (priv->switch_rxon.switch_in_progress &&
+ (priv->switch_rxon.channel != priv->staging_rxon.channel)) {
+ IWL_DEBUG_11H(priv, "abort channel switch on %d\n",
+ le16_to_cpu(priv->switch_rxon.channel));
+ priv->switch_rxon.switch_in_progress = false;
+ }
+
/* If we don't need to send a full RXON, we can use
* iwl_rxon_assoc_cmd which is used to reconfigure filter
* and other flags for the current radio configuration. */
@@ -134,6 +145,7 @@ int iwl_commit_rxon(struct iwl_priv *priv)
}
memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon));
+ iwl_print_rx_config_cmd(priv);
return 0;
}
@@ -191,11 +203,7 @@ int iwl_commit_rxon(struct iwl_priv *priv)
priv->start_calib = 0;
/* Add the broadcast address so we can send broadcast frames */
- if (iwl_rxon_add_station(priv, iwl_bcast_addr, 0) ==
- IWL_INVALID_STATION) {
- IWL_ERR(priv, "Error adding BROADCAST address for transmit.\n");
- return -EIO;
- }
+ iwl_add_bcast_station(priv);
/* If we have set the ASSOC_MSK and we are in BSS mode then
* add the IWL_AP_ID to the station rate table */
@@ -233,6 +241,7 @@ int iwl_commit_rxon(struct iwl_priv *priv)
}
memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon));
}
+ iwl_print_rx_config_cmd(priv);
iwl_init_sensitivity(priv);
@@ -302,7 +311,7 @@ static void iwl_free_frame(struct iwl_priv *priv, struct iwl_frame *frame)
list_add(&frame->list, &priv->free_frames);
}
-static unsigned int iwl_fill_beacon_frame(struct iwl_priv *priv,
+static u32 iwl_fill_beacon_frame(struct iwl_priv *priv,
struct ieee80211_hdr *hdr,
int left)
{
@@ -319,34 +328,74 @@ static unsigned int iwl_fill_beacon_frame(struct iwl_priv *priv,
return priv->ibss_beacon->len;
}
+/* Parse the beacon frame to find the TIM element and set tim_idx & tim_size */
+static void iwl_set_beacon_tim(struct iwl_priv *priv,
+ struct iwl_tx_beacon_cmd *tx_beacon_cmd,
+ u8 *beacon, u32 frame_size)
+{
+ u16 tim_idx;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon;
+
+ /*
+ * The index is relative to frame start but we start looking at the
+ * variable-length part of the beacon.
+ */
+ tim_idx = mgmt->u.beacon.variable - beacon;
+
+ /* Parse variable-length elements of beacon to find WLAN_EID_TIM */
+ while ((tim_idx < (frame_size - 2)) &&
+ (beacon[tim_idx] != WLAN_EID_TIM))
+ tim_idx += beacon[tim_idx+1] + 2;
+
+ /* If TIM field was found, set variables */
+ if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) {
+ tx_beacon_cmd->tim_idx = cpu_to_le16(tim_idx);
+ tx_beacon_cmd->tim_size = beacon[tim_idx+1];
+ } else
+ IWL_WARN(priv, "Unable to find TIM Element in beacon\n");
+}
+
static unsigned int iwl_hw_get_beacon_cmd(struct iwl_priv *priv,
- struct iwl_frame *frame, u8 rate)
+ struct iwl_frame *frame)
{
struct iwl_tx_beacon_cmd *tx_beacon_cmd;
- unsigned int frame_size;
+ u32 frame_size;
+ u32 rate_flags;
+ u32 rate;
+ /*
+ * We have to set up the TX command, the TX Beacon command, and the
+ * beacon contents.
+ */
+ /* Initialize memory */
tx_beacon_cmd = &frame->u.beacon;
memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd));
- tx_beacon_cmd->tx.sta_id = priv->hw_params.bcast_sta_id;
- tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
-
+ /* Set up TX beacon contents */
frame_size = iwl_fill_beacon_frame(priv, tx_beacon_cmd->frame,
sizeof(frame->u) - sizeof(*tx_beacon_cmd));
+ if (WARN_ON_ONCE(frame_size > MAX_MPDU_SIZE))
+ return 0;
- BUG_ON(frame_size > MAX_MPDU_SIZE);
+ /* Set up TX command fields */
tx_beacon_cmd->tx.len = cpu_to_le16((u16)frame_size);
+ tx_beacon_cmd->tx.sta_id = priv->hw_params.bcast_sta_id;
+ tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
+ tx_beacon_cmd->tx.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK |
+ TX_CMD_FLG_TSF_MSK | TX_CMD_FLG_STA_RATE_MSK;
- if ((rate == IWL_RATE_1M_PLCP) || (rate >= IWL_RATE_2M_PLCP))
- tx_beacon_cmd->tx.rate_n_flags =
- iwl_hw_set_rate_n_flags(rate, RATE_MCS_CCK_MSK);
- else
- tx_beacon_cmd->tx.rate_n_flags =
- iwl_hw_set_rate_n_flags(rate, 0);
+ /* Set up TX beacon command fields */
+ iwl_set_beacon_tim(priv, tx_beacon_cmd, (u8 *)tx_beacon_cmd->frame,
+ frame_size);
- tx_beacon_cmd->tx.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK |
- TX_CMD_FLG_TSF_MSK |
- TX_CMD_FLG_STA_RATE_MSK;
+ /* Set up packet rate and flags */
+ rate = iwl_rate_get_lowest_plcp(priv);
+ priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant);
+ rate_flags = iwl_ant_idx_to_flags(priv->mgmt_tx_ant);
+ if ((rate >= IWL_FIRST_CCK_RATE) && (rate <= IWL_LAST_CCK_RATE))
+ rate_flags |= RATE_MCS_CCK_MSK;
+ tx_beacon_cmd->tx.rate_n_flags = iwl_hw_set_rate_n_flags(rate,
+ rate_flags);
return sizeof(*tx_beacon_cmd) + frame_size;
}
@@ -355,19 +404,20 @@ static int iwl_send_beacon_cmd(struct iwl_priv *priv)
struct iwl_frame *frame;
unsigned int frame_size;
int rc;
- u8 rate;
frame = iwl_get_free_frame(priv);
-
if (!frame) {
IWL_ERR(priv, "Could not obtain free frame buffer for beacon "
"command.\n");
return -ENOMEM;
}
- rate = iwl_rate_get_lowest_plcp(priv);
-
- frame_size = iwl_hw_get_beacon_cmd(priv, frame, rate);
+ frame_size = iwl_hw_get_beacon_cmd(priv, frame);
+ if (!frame_size) {
+ IWL_ERR(priv, "Error configuring the beacon command\n");
+ iwl_free_frame(priv, frame);
+ return -EINVAL;
+ }
rc = iwl_send_cmd_pdu(priv, REPLY_TX_BEACON, frame_size,
&frame->u.cmd[0]);
@@ -525,7 +575,7 @@ int iwl_hw_tx_queue_init(struct iwl_priv *priv,
static void iwl_rx_reply_alive(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_alive_resp *palive;
struct delayed_work *pwork;
@@ -604,14 +654,14 @@ static void iwl_bg_statistics_periodic(unsigned long data)
if (!iwl_is_ready_rf(priv))
return;
- iwl_send_statistics_request(priv, CMD_ASYNC);
+ iwl_send_statistics_request(priv, CMD_ASYNC, false);
}
static void iwl_rx_beacon_notif(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
#ifdef CONFIG_IWLWIFI_DEBUG
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl4965_beacon_notif *beacon =
(struct iwl4965_beacon_notif *)pkt->u.raw;
u8 rate = iwl_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags);
@@ -635,7 +685,7 @@ static void iwl_rx_beacon_notif(struct iwl_priv *priv,
static void iwl_rx_card_state_notif(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags);
unsigned long status = priv->status;
@@ -721,7 +771,7 @@ static void iwl_setup_rx_handlers(struct iwl_priv *priv)
* statistics request from the host as well as for the periodic
* statistics notifications (after received beacons) from the uCode.
*/
- priv->rx_handlers[REPLY_STATISTICS_CMD] = iwl_rx_statistics;
+ priv->rx_handlers[REPLY_STATISTICS_CMD] = iwl_reply_statistics;
priv->rx_handlers[STATISTICS_NOTIFICATION] = iwl_rx_statistics;
iwl_setup_spectrum_handlers(priv);
@@ -770,7 +820,7 @@ void iwl_rx_handle(struct iwl_priv *priv)
IWL_DEBUG_RX(priv, "r = %d, i = %d\n", r, i);
/* calculate total frames need to be restock after handling RX */
- total_empty = r - priv->rxq.write_actual;
+ total_empty = r - rxq->write_actual;
if (total_empty < 0)
total_empty += RX_QUEUE_SIZE;
@@ -787,10 +837,13 @@ void iwl_rx_handle(struct iwl_priv *priv)
rxq->queue[i] = NULL;
- pci_unmap_single(priv->pci_dev, rxb->real_dma_addr,
- priv->hw_params.rx_buf_size + 256,
- PCI_DMA_FROMDEVICE);
- pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ pci_unmap_page(priv->pci_dev, rxb->page_dma,
+ PAGE_SIZE << priv->hw_params.rx_page_order,
+ PCI_DMA_FROMDEVICE);
+ pkt = rxb_addr(rxb);
+
+ trace_iwlwifi_dev_rx(priv, pkt,
+ le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK);
/* Reclaim a command buffer only if this packet is a response
* to a (driver-originated) command.
@@ -812,8 +865,8 @@ void iwl_rx_handle(struct iwl_priv *priv)
if (priv->rx_handlers[pkt->hdr.cmd]) {
IWL_DEBUG_RX(priv, "r = %d, i = %d, %s, 0x%02x\n", r,
i, get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd);
- priv->rx_handlers[pkt->hdr.cmd] (priv, rxb);
priv->isr_stats.rx_handlers[pkt->hdr.cmd]++;
+ priv->rx_handlers[pkt->hdr.cmd] (priv, rxb);
} else {
/* No handling needed */
IWL_DEBUG_RX(priv,
@@ -822,35 +875,45 @@ void iwl_rx_handle(struct iwl_priv *priv)
pkt->hdr.cmd);
}
+ /*
+ * XXX: After here, we should always check rxb->page
+ * against NULL before touching it or its virtual
+ * memory (pkt). Because some rx_handler might have
+ * already taken or freed the pages.
+ */
+
if (reclaim) {
- /* Invoke any callbacks, transfer the skb to caller, and
- * fire off the (possibly) blocking iwl_send_cmd()
+ /* Invoke any callbacks, transfer the buffer to caller,
+ * and fire off the (possibly) blocking iwl_send_cmd()
* as we reclaim the driver command queue */
- if (rxb && rxb->skb)
+ if (rxb->page)
iwl_tx_cmd_complete(priv, rxb);
else
IWL_WARN(priv, "Claim null rxb?\n");
}
- /* For now we just don't re-use anything. We can tweak this
- * later to try and re-use notification packets and SKBs that
- * fail to Rx correctly */
- if (rxb->skb != NULL) {
- priv->alloc_rxb_skb--;
- dev_kfree_skb_any(rxb->skb);
- rxb->skb = NULL;
- }
-
+ /* Reuse the page if possible. For notification packets and
+ * SKBs that fail to Rx correctly, add them back into the
+ * rx_free list for reuse later. */
spin_lock_irqsave(&rxq->lock, flags);
- list_add_tail(&rxb->list, &priv->rxq.rx_used);
+ if (rxb->page != NULL) {
+ rxb->page_dma = pci_map_page(priv->pci_dev, rxb->page,
+ 0, PAGE_SIZE << priv->hw_params.rx_page_order,
+ PCI_DMA_FROMDEVICE);
+ list_add_tail(&rxb->list, &rxq->rx_free);
+ rxq->free_count++;
+ } else
+ list_add_tail(&rxb->list, &rxq->rx_used);
+
spin_unlock_irqrestore(&rxq->lock, flags);
+
i = (i + 1) & RX_QUEUE_MASK;
/* If there are a lot of unused frames,
* restock the Rx queue so ucode wont assert. */
if (fill_rx) {
count++;
if (count >= 8) {
- priv->rxq.read = i;
+ rxq->read = i;
iwl_rx_replenish_now(priv);
count = 0;
}
@@ -858,7 +921,7 @@ void iwl_rx_handle(struct iwl_priv *priv)
}
/* Backtrack one entry */
- priv->rxq.read = i;
+ rxq->read = i;
if (fill_rx)
iwl_rx_replenish_now(priv);
else
@@ -878,6 +941,7 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv)
u32 inta, handled = 0;
u32 inta_fh;
unsigned long flags;
+ u32 i;
#ifdef CONFIG_IWLWIFI_DEBUG
u32 inta_mask;
#endif
@@ -905,6 +969,8 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv)
}
#endif
+ spin_unlock_irqrestore(&priv->lock, flags);
+
/* Since CSR_INT and CSR_FH_INT_STATUS reads and clears are not
* atomic, make sure that inta covers all the interrupts that
* we've discovered, even if FH interrupt came in just after
@@ -926,8 +992,6 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv)
handled |= CSR_INT_BIT_HW_ERR;
- spin_unlock_irqrestore(&priv->lock, flags);
-
return;
}
@@ -995,19 +1059,17 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv)
handled |= CSR_INT_BIT_SW_ERR;
}
- /* uCode wakes up after power-down sleep */
+ /*
+ * uCode wakes up after power-down sleep.
+ * Tell device about any new tx or host commands enqueued,
+ * and about any Rx buffers made available while asleep.
+ */
if (inta & CSR_INT_BIT_WAKEUP) {
IWL_DEBUG_ISR(priv, "Wakeup interrupt\n");
iwl_rx_queue_update_write_ptr(priv, &priv->rxq);
- iwl_txq_update_write_ptr(priv, &priv->txq[0]);
- iwl_txq_update_write_ptr(priv, &priv->txq[1]);
- iwl_txq_update_write_ptr(priv, &priv->txq[2]);
- iwl_txq_update_write_ptr(priv, &priv->txq[3]);
- iwl_txq_update_write_ptr(priv, &priv->txq[4]);
- iwl_txq_update_write_ptr(priv, &priv->txq[5]);
-
+ for (i = 0; i < priv->hw_params.max_txq_num; i++)
+ iwl_txq_update_write_ptr(priv, &priv->txq[i]);
priv->isr_stats.wakeup++;
-
handled |= CSR_INT_BIT_WAKEUP;
}
@@ -1020,11 +1082,12 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv)
handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX);
}
+ /* This "Tx" DMA channel is used only for loading uCode */
if (inta & CSR_INT_BIT_FH_TX) {
- IWL_DEBUG_ISR(priv, "Tx interrupt\n");
+ IWL_DEBUG_ISR(priv, "uCode load interrupt\n");
priv->isr_stats.tx++;
handled |= CSR_INT_BIT_FH_TX;
- /* FH finished to write, send event */
+ /* Wake up uCode load routine, now that load is complete */
priv->ucode_write_complete = 1;
wake_up_interruptible(&priv->wait_command_queue);
}
@@ -1054,7 +1117,6 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv)
"flags 0x%08lx\n", inta, inta_mask, inta_fh, flags);
}
#endif
- spin_unlock_irqrestore(&priv->lock, flags);
}
/* tasklet for iwlagn interrupt */
@@ -1063,6 +1125,7 @@ static void iwl_irq_tasklet(struct iwl_priv *priv)
u32 inta = 0;
u32 handled = 0;
unsigned long flags;
+ u32 i;
#ifdef CONFIG_IWLWIFI_DEBUG
u32 inta_mask;
#endif
@@ -1084,6 +1147,9 @@ static void iwl_irq_tasklet(struct iwl_priv *priv)
inta, inta_mask);
}
#endif
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
/* saved interrupt in inta variable now we can reset priv->inta */
priv->inta = 0;
@@ -1099,8 +1165,6 @@ static void iwl_irq_tasklet(struct iwl_priv *priv)
handled |= CSR_INT_BIT_HW_ERR;
- spin_unlock_irqrestore(&priv->lock, flags);
-
return;
}
@@ -1172,12 +1236,8 @@ static void iwl_irq_tasklet(struct iwl_priv *priv)
if (inta & CSR_INT_BIT_WAKEUP) {
IWL_DEBUG_ISR(priv, "Wakeup interrupt\n");
iwl_rx_queue_update_write_ptr(priv, &priv->rxq);
- iwl_txq_update_write_ptr(priv, &priv->txq[0]);
- iwl_txq_update_write_ptr(priv, &priv->txq[1]);
- iwl_txq_update_write_ptr(priv, &priv->txq[2]);
- iwl_txq_update_write_ptr(priv, &priv->txq[3]);
- iwl_txq_update_write_ptr(priv, &priv->txq[4]);
- iwl_txq_update_write_ptr(priv, &priv->txq[5]);
+ for (i = 0; i < priv->hw_params.max_txq_num; i++)
+ iwl_txq_update_write_ptr(priv, &priv->txq[i]);
priv->isr_stats.wakeup++;
@@ -1206,26 +1266,36 @@ static void iwl_irq_tasklet(struct iwl_priv *priv)
* 3- update RX shared data to indicate last write index.
* 4- send interrupt.
* This could lead to RX race, driver could receive RX interrupt
- * but the shared data changes does not reflect this.
- * this could lead to RX race, RX periodic will solve this race
+ * but the shared data changes does not reflect this;
+ * periodic interrupt will detect any dangling Rx activity.
*/
- iwl_write32(priv, CSR_INT_PERIODIC_REG,
+
+ /* Disable periodic interrupt; we use it as just a one-shot. */
+ iwl_write8(priv, CSR_INT_PERIODIC_REG,
CSR_INT_PERIODIC_DIS);
iwl_rx_handle(priv);
- /* Only set RX periodic if real RX is received. */
+
+ /*
+ * Enable periodic interrupt in 8 msec only if we received
+ * real RX interrupt (instead of just periodic int), to catch
+ * any dangling Rx interrupt. If it was just the periodic
+ * interrupt, there was no dangling Rx activity, and no need
+ * to extend the periodic interrupt; one-shot is enough.
+ */
if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX))
- iwl_write32(priv, CSR_INT_PERIODIC_REG,
+ iwl_write8(priv, CSR_INT_PERIODIC_REG,
CSR_INT_PERIODIC_ENA);
priv->isr_stats.rx++;
}
+ /* This "Tx" DMA channel is used only for loading uCode */
if (inta & CSR_INT_BIT_FH_TX) {
iwl_write32(priv, CSR_FH_INT_STATUS, CSR49_FH_INT_TX_MASK);
- IWL_DEBUG_ISR(priv, "Tx interrupt\n");
+ IWL_DEBUG_ISR(priv, "uCode load interrupt\n");
priv->isr_stats.tx++;
handled |= CSR_INT_BIT_FH_TX;
- /* FH finished to write, send event */
+ /* Wake up uCode load routine, now that load is complete */
priv->ucode_write_complete = 1;
wake_up_interruptible(&priv->wait_command_queue);
}
@@ -1240,14 +1310,10 @@ static void iwl_irq_tasklet(struct iwl_priv *priv)
inta & ~priv->inta_mask);
}
-
/* Re-enable all interrupts */
/* only Re-enable if diabled by irq */
if (test_bit(STATUS_INT_ENABLED, &priv->status))
iwl_enable_interrupts(priv);
-
- spin_unlock_irqrestore(&priv->lock, flags);
-
}
@@ -1367,6 +1433,14 @@ static int iwl_read_ucode(struct iwl_priv *priv)
IWL_UCODE_API(priv->ucode_ver),
IWL_UCODE_SERIAL(priv->ucode_ver));
+ snprintf(priv->hw->wiphy->fw_version,
+ sizeof(priv->hw->wiphy->fw_version),
+ "%u.%u.%u.%u",
+ IWL_UCODE_MAJOR(priv->ucode_ver),
+ IWL_UCODE_MINOR(priv->ucode_ver),
+ IWL_UCODE_API(priv->ucode_ver),
+ IWL_UCODE_SERIAL(priv->ucode_ver));
+
if (build)
IWL_DEBUG_INFO(priv, "Build %u\n", build);
@@ -1531,7 +1605,6 @@ static int iwl_read_ucode(struct iwl_priv *priv)
return ret;
}
-#ifdef CONFIG_IWLWIFI_DEBUG
static const char *desc_lookup_text[] = {
"OK",
"FAIL",
@@ -1589,7 +1662,9 @@ void iwl_dump_nic_error_log(struct iwl_priv *priv)
base = le32_to_cpu(priv->card_alive.error_event_table_ptr);
if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) {
- IWL_ERR(priv, "Not valid error log pointer 0x%08X\n", base);
+ IWL_ERR(priv,
+ "Not valid error log pointer 0x%08X for %s uCode\n",
+ base, (priv->ucode_type == UCODE_INIT) ? "Init" : "RT");
return;
}
@@ -1611,6 +1686,9 @@ void iwl_dump_nic_error_log(struct iwl_priv *priv)
line = iwl_read_targ_mem(priv, base + 9 * sizeof(u32));
time = iwl_read_targ_mem(priv, base + 11 * sizeof(u32));
+ trace_iwlwifi_dev_ucode_error(priv, desc, time, data1, data2, line,
+ blink1, blink2, ilink1, ilink2);
+
IWL_ERR(priv, "Desc Time "
"data1 data2 line\n");
IWL_ERR(priv, "%-28s (#%02d) %010u 0x%08X 0x%08X %u\n",
@@ -1635,6 +1713,7 @@ static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */
u32 ptr; /* SRAM byte address of log data */
u32 ev, time, data; /* event log data */
+ unsigned long reg_flags;
if (num_events == 0)
return;
@@ -1650,26 +1729,72 @@ static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
ptr = base + EVENT_START_OFFSET + (start_idx * event_size);
+ /* Make sure device is powered up for SRAM reads */
+ spin_lock_irqsave(&priv->reg_lock, reg_flags);
+ iwl_grab_nic_access(priv);
+
+ /* Set starting address; reads will auto-increment */
+ _iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR, ptr);
+ rmb();
+
/* "time" is actually "data" for mode 0 (no timestamp).
* place event id # at far right for easier visual parsing. */
for (i = 0; i < num_events; i++) {
- ev = iwl_read_targ_mem(priv, ptr);
- ptr += sizeof(u32);
- time = iwl_read_targ_mem(priv, ptr);
- ptr += sizeof(u32);
+ ev = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
+ time = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
if (mode == 0) {
/* data, ev */
+ trace_iwlwifi_dev_ucode_event(priv, 0, time, ev);
IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n", time, ev);
} else {
- data = iwl_read_targ_mem(priv, ptr);
- ptr += sizeof(u32);
+ data = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n",
time, data, ev);
+ trace_iwlwifi_dev_ucode_event(priv, time, data, ev);
}
}
+
+ /* Allow device to power down */
+ iwl_release_nic_access(priv);
+ spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
+}
+
+/**
+ * iwl_print_last_event_logs - Dump the newest # of event log to syslog
+ */
+static void iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity,
+ u32 num_wraps, u32 next_entry,
+ u32 size, u32 mode)
+{
+ /*
+ * display the newest DEFAULT_LOG_ENTRIES entries
+ * i.e the entries just before the next ont that uCode would fill.
+ */
+ if (num_wraps) {
+ if (next_entry < size) {
+ iwl_print_event_log(priv,
+ capacity - (size - next_entry),
+ size - next_entry, mode);
+ iwl_print_event_log(priv, 0,
+ next_entry, mode);
+ } else
+ iwl_print_event_log(priv, next_entry - size,
+ size, mode);
+ } else {
+ if (next_entry < size)
+ iwl_print_event_log(priv, 0, next_entry, mode);
+ else
+ iwl_print_event_log(priv, next_entry - size,
+ size, mode);
+ }
}
-void iwl_dump_nic_event_log(struct iwl_priv *priv)
+/* For sanity check only. Actual size is determined by uCode, typ. 512 */
+#define MAX_EVENT_LOG_SIZE (512)
+
+#define DEFAULT_DUMP_EVENT_LOG_ENTRIES (20)
+
+void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log)
{
u32 base; /* SRAM byte address of event log header */
u32 capacity; /* event log capacity in # entries */
@@ -1684,7 +1809,9 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv)
base = le32_to_cpu(priv->card_alive.log_event_table_ptr);
if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) {
- IWL_ERR(priv, "Invalid event log pointer 0x%08X\n", base);
+ IWL_ERR(priv,
+ "Invalid event log pointer 0x%08X for %s uCode\n",
+ base, (priv->ucode_type == UCODE_INIT) ? "Init" : "RT");
return;
}
@@ -1694,6 +1821,18 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv)
num_wraps = iwl_read_targ_mem(priv, base + (2 * sizeof(u32)));
next_entry = iwl_read_targ_mem(priv, base + (3 * sizeof(u32)));
+ if (capacity > MAX_EVENT_LOG_SIZE) {
+ IWL_ERR(priv, "Log capacity %d is bogus, limit to %d entries\n",
+ capacity, MAX_EVENT_LOG_SIZE);
+ capacity = MAX_EVENT_LOG_SIZE;
+ }
+
+ if (next_entry > MAX_EVENT_LOG_SIZE) {
+ IWL_ERR(priv, "Log write index %d is bogus, limit to %d\n",
+ next_entry, MAX_EVENT_LOG_SIZE);
+ next_entry = MAX_EVENT_LOG_SIZE;
+ }
+
size = num_wraps ? capacity : next_entry;
/* bail out if nothing in log */
@@ -1702,19 +1841,37 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv)
return;
}
- IWL_ERR(priv, "Start IWL Event Log Dump: display count %d, wraps %d\n",
- size, num_wraps);
-
- /* if uCode has wrapped back to top of log, start at the oldest entry,
- * i.e the next one that uCode would fill. */
- if (num_wraps)
- iwl_print_event_log(priv, next_entry,
- capacity - next_entry, mode);
- /* (then/else) start at top of log */
- iwl_print_event_log(priv, 0, next_entry, mode);
+#ifdef CONFIG_IWLWIFI_DEBUG
+ if (!(iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS))
+ size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES)
+ ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size;
+#else
+ size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES)
+ ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size;
+#endif
+ IWL_ERR(priv, "Start IWL Event Log Dump: display last %u entries\n",
+ size);
-}
+#ifdef CONFIG_IWLWIFI_DEBUG
+ if ((iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) || full_log) {
+ /*
+ * if uCode has wrapped back to top of log,
+ * start at the oldest entry,
+ * i.e the next one that uCode would fill.
+ */
+ if (num_wraps)
+ iwl_print_event_log(priv, next_entry,
+ capacity - next_entry, mode);
+ /* (then/else) start at top of log */
+ iwl_print_event_log(priv, 0, next_entry, mode);
+ } else
+ iwl_print_last_event_logs(priv, capacity, num_wraps,
+ next_entry, size, mode);
+#else
+ iwl_print_last_event_logs(priv, capacity, num_wraps,
+ next_entry, size, mode);
#endif
+}
/**
* iwl_alive_start - called after REPLY_ALIVE notification received
@@ -1763,6 +1920,10 @@ static void iwl_alive_start(struct iwl_priv *priv)
priv->active_rate = priv->rates_mask;
priv->active_rate_basic = priv->rates_mask & IWL_BASIC_RATES_MASK;
+ /* Configure Tx antenna selection based on H/W config */
+ if (priv->cfg->ops->hcmd->set_tx_ant)
+ priv->cfg->ops->hcmd->set_tx_ant(priv, priv->cfg->valid_tx_ant);
+
if (iwl_is_associated(priv)) {
struct iwl_rxon_cmd *active_rxon =
(struct iwl_rxon_cmd *)&priv->active_rxon;
@@ -1790,7 +1951,7 @@ static void iwl_alive_start(struct iwl_priv *priv)
/* At this point, the NIC is initialized and operational */
iwl_rf_kill_ct_config(priv);
- iwl_leds_register(priv);
+ iwl_leds_init(priv);
IWL_DEBUG_INFO(priv, "ALIVE processing complete.\n");
set_bit(STATUS_READY, &priv->status);
@@ -1828,8 +1989,6 @@ static void __iwl_down(struct iwl_priv *priv)
if (!exit_pending)
set_bit(STATUS_EXIT_PENDING, &priv->status);
- iwl_leds_unregister(priv);
-
iwl_clear_stations_table(priv);
/* Unblock any waiting calls */
@@ -1877,24 +2036,20 @@ static void __iwl_down(struct iwl_priv *priv)
/* device going down, Stop using ICT table */
iwl_disable_ict(priv);
- spin_lock_irqsave(&priv->lock, flags);
- iwl_clear_bit(priv, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
- spin_unlock_irqrestore(&priv->lock, flags);
iwl_txq_ctx_stop(priv);
iwl_rxq_stop(priv);
- iwl_write_prph(priv, APMG_CLK_DIS_REG,
- APMG_CLK_VAL_DMA_CLK_RQT);
-
+ /* Power-down device's busmaster DMA clocks */
+ iwl_write_prph(priv, APMG_CLK_DIS_REG, APMG_CLK_VAL_DMA_CLK_RQT);
udelay(5);
- /* FIXME: apm_ops.suspend(priv) */
- if (exit_pending)
- priv->cfg->ops->lib->apm_ops.stop(priv);
- else
- priv->cfg->ops->lib->apm_ops.reset(priv);
+ /* 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 */
+ priv->cfg->ops->lib->apm_ops.stop(priv);
+
exit:
memset(&priv->card_alive, 0, sizeof(struct iwl_alive_resp));
@@ -2281,6 +2436,67 @@ void iwl_post_associate(struct iwl_priv *priv)
#define UCODE_READY_TIMEOUT (4 * HZ)
+/*
+ * Not a mac80211 entry point function, but it fits in with all the
+ * other mac80211 functions grouped here.
+ */
+static int iwl_setup_mac(struct iwl_priv *priv)
+{
+ int ret;
+ struct ieee80211_hw *hw = priv->hw;
+ hw->rate_control_algorithm = "iwl-agn-rs";
+
+ /* Tell mac80211 our characteristics */
+ hw->flags = IEEE80211_HW_SIGNAL_DBM |
+ IEEE80211_HW_NOISE_DBM |
+ IEEE80211_HW_AMPDU_AGGREGATION |
+ IEEE80211_HW_SPECTRUM_MGMT;
+
+ if (!priv->cfg->broken_powersave)
+ hw->flags |= IEEE80211_HW_SUPPORTS_PS |
+ IEEE80211_HW_SUPPORTS_DYNAMIC_PS;
+
+ hw->sta_data_size = sizeof(struct iwl_station_priv);
+ hw->wiphy->interface_modes =
+ BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC);
+
+ hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY |
+ WIPHY_FLAG_DISABLE_BEACON_HINTS;
+
+ /*
+ * For now, disable PS by default because it affects
+ * RX performance significantly.
+ */
+ hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+
+ hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
+ /* we create the 802.11 header and a zero-length SSID element */
+ hw->wiphy->max_scan_ie_len = IWL_MAX_PROBE_REQUEST - 24 - 2;
+
+ /* Default value; 4 EDCA QOS priorities */
+ hw->queues = 4;
+
+ hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
+
+ if (priv->bands[IEEE80211_BAND_2GHZ].n_channels)
+ priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
+ &priv->bands[IEEE80211_BAND_2GHZ];
+ if (priv->bands[IEEE80211_BAND_5GHZ].n_channels)
+ priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
+ &priv->bands[IEEE80211_BAND_5GHZ];
+
+ ret = ieee80211_register_hw(priv->hw);
+ if (ret) {
+ IWL_ERR(priv, "Failed to register hw (error %d)\n", ret);
+ return ret;
+ }
+ priv->mac80211_registered = 1;
+
+ return 0;
+}
+
+
static int iwl_mac_start(struct ieee80211_hw *hw)
{
struct iwl_priv *priv = hw->priv;
@@ -2328,6 +2544,8 @@ static int iwl_mac_start(struct ieee80211_hw *hw)
}
}
+ iwl_led_start(priv);
+
out:
priv->is_open = 1;
IWL_DEBUG_MAC80211(priv, "leave\n");
@@ -2404,6 +2622,10 @@ void iwl_config_ap(struct iwl_priv *priv)
IWL_WARN(priv, "REPLY_RXON_TIMING failed - "
"Attempting to continue.\n");
+ /* AP has all antennas */
+ priv->chain_noise_data.active_chains =
+ priv->hw_params.valid_rx_ant;
+ iwl_set_rxon_ht(priv, &priv->current_ht_config);
if (priv->cfg->ops->hcmd->set_rxon_chain)
priv->cfg->ops->hcmd->set_rxon_chain(priv);
@@ -2432,10 +2654,11 @@ void iwl_config_ap(struct iwl_priv *priv)
/* restore RXON assoc */
priv->staging_rxon.filter_flags |= RXON_FILTER_ASSOC_MSK;
iwlcore_commit_rxon(priv);
+ iwl_reset_qos(priv);
spin_lock_irqsave(&priv->lock, flags);
iwl_activate_qos(priv, 1);
spin_unlock_irqrestore(&priv->lock, flags);
- iwl_rxon_add_station(priv, iwl_bcast_addr, 0);
+ iwl_add_bcast_station(priv);
}
iwl_send_beacon_cmd(priv);
@@ -2527,6 +2750,7 @@ static int iwl_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
}
static int iwl_mac_ampdu_action(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
enum ieee80211_ampdu_mlme_action action,
struct ieee80211_sta *sta, u16 tid, u16 *ssn)
{
@@ -2580,6 +2804,45 @@ static int iwl_mac_get_stats(struct ieee80211_hw *hw,
return 0;
}
+static void iwl_mac_sta_notify(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum sta_notify_cmd cmd,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_priv *priv = hw->priv;
+ struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
+ int sta_id;
+
+ /*
+ * TODO: We really should use this callback to
+ * actually maintain the station table in
+ * the device.
+ */
+
+ switch (cmd) {
+ case STA_NOTIFY_ADD:
+ atomic_set(&sta_priv->pending_frames, 0);
+ if (vif->type == NL80211_IFTYPE_AP)
+ sta_priv->client = true;
+ break;
+ case STA_NOTIFY_SLEEP:
+ WARN_ON(!sta_priv->client);
+ sta_priv->asleep = true;
+ if (atomic_read(&sta_priv->pending_frames) > 0)
+ ieee80211_sta_block_awake(hw, sta, true);
+ break;
+ case STA_NOTIFY_AWAKE:
+ WARN_ON(!sta_priv->client);
+ sta_priv->asleep = false;
+ sta_id = iwl_find_station(priv, sta->addr);
+ if (sta_id != IWL_INVALID_STATION)
+ iwl_sta_modify_ps_wake(priv, sta_id);
+ break;
+ default:
+ break;
+ }
+}
+
/*****************************************************************************
*
* sysfs attributes
@@ -2774,7 +3037,7 @@ static ssize_t show_statistics(struct device *d,
return -EAGAIN;
mutex_lock(&priv->mutex);
- rc = iwl_send_statistics_request(priv, 0);
+ rc = iwl_send_statistics_request(priv, CMD_SYNC, false);
mutex_unlock(&priv->mutex);
if (rc) {
@@ -2799,6 +3062,40 @@ static ssize_t show_statistics(struct device *d,
static DEVICE_ATTR(statistics, S_IRUGO, show_statistics, NULL);
+static ssize_t show_rts_ht_protection(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct iwl_priv *priv = dev_get_drvdata(d);
+
+ return sprintf(buf, "%s\n",
+ priv->cfg->use_rts_for_ht ? "RTS/CTS" : "CTS-to-self");
+}
+
+static ssize_t store_rts_ht_protection(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct iwl_priv *priv = dev_get_drvdata(d);
+ unsigned long val;
+ int ret;
+
+ ret = strict_strtoul(buf, 10, &val);
+ if (ret)
+ IWL_INFO(priv, "Input is not in decimal form.\n");
+ else {
+ if (!iwl_is_associated(priv))
+ priv->cfg->use_rts_for_ht = val ? true : false;
+ else
+ IWL_ERR(priv, "Sta associated with AP - "
+ "Change protection mechanism is not allowed\n");
+ ret = count;
+ }
+ return ret;
+}
+
+static DEVICE_ATTR(rts_ht_protection, S_IWUSR | S_IRUGO,
+ show_rts_ht_protection, store_rts_ht_protection);
+
/*****************************************************************************
*
@@ -2849,12 +3146,103 @@ static void iwl_cancel_deferred_work(struct iwl_priv *priv)
del_timer_sync(&priv->statistics_periodic);
}
+static void iwl_init_hw_rates(struct iwl_priv *priv,
+ struct ieee80211_rate *rates)
+{
+ int i;
+
+ for (i = 0; i < IWL_RATE_COUNT_LEGACY; i++) {
+ rates[i].bitrate = iwl_rates[i].ieee * 5;
+ rates[i].hw_value = i; /* Rate scaling will work on indexes */
+ rates[i].hw_value_short = i;
+ rates[i].flags = 0;
+ if ((i >= IWL_FIRST_CCK_RATE) && (i <= IWL_LAST_CCK_RATE)) {
+ /*
+ * If CCK != 1M then set short preamble rate flag.
+ */
+ rates[i].flags |=
+ (iwl_rates[i].plcp == IWL_RATE_1M_PLCP) ?
+ 0 : IEEE80211_RATE_SHORT_PREAMBLE;
+ }
+ }
+}
+
+static int iwl_init_drv(struct iwl_priv *priv)
+{
+ int ret;
+
+ priv->ibss_beacon = NULL;
+
+ spin_lock_init(&priv->lock);
+ spin_lock_init(&priv->sta_lock);
+ spin_lock_init(&priv->hcmd_lock);
+
+ INIT_LIST_HEAD(&priv->free_frames);
+
+ mutex_init(&priv->mutex);
+
+ /* Clear the driver's (not device's) station table */
+ iwl_clear_stations_table(priv);
+
+ priv->ieee_channels = NULL;
+ priv->ieee_rates = NULL;
+ priv->band = IEEE80211_BAND_2GHZ;
+
+ priv->iw_mode = NL80211_IFTYPE_STATION;
+
+ /* Choose which receivers/antennas to use */
+ if (priv->cfg->ops->hcmd->set_rxon_chain)
+ priv->cfg->ops->hcmd->set_rxon_chain(priv);
+
+ iwl_init_scan_params(priv);
+
+ iwl_reset_qos(priv);
+
+ priv->qos_data.qos_active = 0;
+ priv->qos_data.qos_cap.val = 0;
+
+ priv->rates_mask = IWL_RATES_MASK;
+ /* Set the tx_power_user_lmt to the lowest power level
+ * this value will get overwritten by channel max power avg
+ * from eeprom */
+ priv->tx_power_user_lmt = IWL_TX_POWER_TARGET_POWER_MIN;
+
+ ret = iwl_init_channel_map(priv);
+ if (ret) {
+ IWL_ERR(priv, "initializing regulatory failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = iwlcore_init_geos(priv);
+ if (ret) {
+ IWL_ERR(priv, "initializing geos failed: %d\n", ret);
+ goto err_free_channel_map;
+ }
+ iwl_init_hw_rates(priv, priv->ieee_rates);
+
+ return 0;
+
+err_free_channel_map:
+ iwl_free_channel_map(priv);
+err:
+ return ret;
+}
+
+static void iwl_uninit_drv(struct iwl_priv *priv)
+{
+ iwl_calib_free_results(priv);
+ iwlcore_free_geos(priv);
+ iwl_free_channel_map(priv);
+ kfree(priv->scan);
+}
+
static struct attribute *iwl_sysfs_entries[] = {
&dev_attr_flags.attr,
&dev_attr_filter_flags.attr,
&dev_attr_statistics.attr,
&dev_attr_temperature.attr,
&dev_attr_tx_power.attr,
+ &dev_attr_rts_ht_protection.attr,
#ifdef CONFIG_IWLWIFI_DEBUG
&dev_attr_debug_level.attr,
#endif
@@ -2882,7 +3270,8 @@ static struct ieee80211_ops iwl_hw_ops = {
.reset_tsf = iwl_mac_reset_tsf,
.bss_info_changed = iwl_bss_info_changed,
.ampdu_action = iwl_mac_ampdu_action,
- .hw_scan = iwl_mac_hw_scan
+ .hw_scan = iwl_mac_hw_scan,
+ .sta_notify = iwl_mac_sta_notify,
};
static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
@@ -2990,12 +3379,6 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto out_iounmap;
}
- /* amp init */
- err = priv->cfg->ops->lib->apm_ops.init(priv);
- if (err < 0) {
- IWL_ERR(priv, "Failed to init APMG\n");
- goto out_iounmap;
- }
/*****************
* 4. Read EEPROM
*****************/
@@ -3141,6 +3524,15 @@ static void __devexit iwl_pci_remove(struct pci_dev *pdev)
iwl_down(priv);
}
+ /*
+ * Make sure device is reset to low power before unloading driver.
+ * This may be redundant with iwl_down(), but there are paths to
+ * run iwl_down() without calling apm_ops.stop(), and there are
+ * paths to avoid running iwl_down() at all before leaving driver.
+ * This (inexpensive) call *makes sure* device is reset.
+ */
+ priv->cfg->ops->lib->apm_ops.stop(priv);
+
iwl_tt_exit(priv);
/* make sure we flush any pending irq or
@@ -3203,37 +3595,97 @@ static struct pci_device_id iwl_hw_card_ids[] = {
{IWL_PCI_DEVICE(0x4230, PCI_ANY_ID, iwl4965_agn_cfg)},
#endif /* CONFIG_IWL4965 */
#ifdef CONFIG_IWL5000
- {IWL_PCI_DEVICE(0x4232, 0x1205, iwl5100_bg_cfg)},
- {IWL_PCI_DEVICE(0x4232, 0x1305, iwl5100_bg_cfg)},
- {IWL_PCI_DEVICE(0x4232, 0x1206, iwl5100_abg_cfg)},
- {IWL_PCI_DEVICE(0x4232, 0x1306, iwl5100_abg_cfg)},
- {IWL_PCI_DEVICE(0x4232, 0x1326, iwl5100_abg_cfg)},
- {IWL_PCI_DEVICE(0x4237, 0x1216, iwl5100_abg_cfg)},
- {IWL_PCI_DEVICE(0x4232, PCI_ANY_ID, iwl5100_agn_cfg)},
- {IWL_PCI_DEVICE(0x4235, PCI_ANY_ID, iwl5300_agn_cfg)},
- {IWL_PCI_DEVICE(0x4236, PCI_ANY_ID, iwl5300_agn_cfg)},
- {IWL_PCI_DEVICE(0x4237, PCI_ANY_ID, iwl5100_agn_cfg)},
-/* 5350 WiFi/WiMax */
- {IWL_PCI_DEVICE(0x423A, 0x1001, iwl5350_agn_cfg)},
- {IWL_PCI_DEVICE(0x423A, 0x1021, iwl5350_agn_cfg)},
- {IWL_PCI_DEVICE(0x423B, 0x1011, iwl5350_agn_cfg)},
-/* 5150 Wifi/WiMax */
- {IWL_PCI_DEVICE(0x423C, PCI_ANY_ID, iwl5150_agn_cfg)},
- {IWL_PCI_DEVICE(0x423D, PCI_ANY_ID, iwl5150_agn_cfg)},
-/* 6000/6050 Series */
- {IWL_PCI_DEVICE(0x008D, PCI_ANY_ID, iwl6000h_2agn_cfg)},
- {IWL_PCI_DEVICE(0x008E, PCI_ANY_ID, iwl6000h_2agn_cfg)},
- {IWL_PCI_DEVICE(0x422B, PCI_ANY_ID, iwl6000_3agn_cfg)},
- {IWL_PCI_DEVICE(0x422C, PCI_ANY_ID, iwl6000i_2agn_cfg)},
- {IWL_PCI_DEVICE(0x4238, PCI_ANY_ID, iwl6000_3agn_cfg)},
- {IWL_PCI_DEVICE(0x4239, PCI_ANY_ID, iwl6000i_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0086, PCI_ANY_ID, iwl6050_3agn_cfg)},
- {IWL_PCI_DEVICE(0x0087, PCI_ANY_ID, iwl6050_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0088, PCI_ANY_ID, iwl6050_3agn_cfg)},
- {IWL_PCI_DEVICE(0x0089, PCI_ANY_ID, iwl6050_2agn_cfg)},
+/* 5100 Series WiFi */
+ {IWL_PCI_DEVICE(0x4232, 0x1201, iwl5100_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1301, iwl5100_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1204, iwl5100_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1304, iwl5100_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1205, iwl5100_bgn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1305, iwl5100_bgn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1206, iwl5100_abg_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1306, iwl5100_abg_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1221, iwl5100_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1321, iwl5100_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1224, iwl5100_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1324, iwl5100_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1225, iwl5100_bgn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1325, iwl5100_bgn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1226, iwl5100_abg_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1326, iwl5100_abg_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1211, iwl5100_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1311, iwl5100_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1214, iwl5100_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1314, iwl5100_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1215, iwl5100_bgn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1315, iwl5100_bgn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1216, iwl5100_abg_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1316, iwl5100_abg_cfg)}, /* Half Mini Card */
+
+/* 5300 Series WiFi */
+ {IWL_PCI_DEVICE(0x4235, 0x1021, iwl5300_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1121, iwl5300_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1024, iwl5300_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1124, iwl5300_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1001, iwl5300_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1101, iwl5300_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1004, iwl5300_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1104, iwl5300_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4236, 0x1011, iwl5300_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4236, 0x1111, iwl5300_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4236, 0x1014, iwl5300_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4236, 0x1114, iwl5300_agn_cfg)}, /* Half Mini Card */
+
+/* 5350 Series WiFi/WiMax */
+ {IWL_PCI_DEVICE(0x423A, 0x1001, iwl5350_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423A, 0x1021, iwl5350_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423B, 0x1011, iwl5350_agn_cfg)}, /* Mini Card */
+
+/* 5150 Series Wifi/WiMax */
+ {IWL_PCI_DEVICE(0x423C, 0x1201, iwl5150_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1301, iwl5150_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1206, iwl5150_abg_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1306, iwl5150_abg_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1221, iwl5150_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1321, iwl5150_agn_cfg)}, /* Half Mini Card */
+
+ {IWL_PCI_DEVICE(0x423D, 0x1211, iwl5150_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423D, 0x1311, iwl5150_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x423D, 0x1216, iwl5150_abg_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423D, 0x1316, iwl5150_abg_cfg)}, /* Half Mini Card */
+
+/* 6x00 Series */
+ {IWL_PCI_DEVICE(0x422B, 0x1101, iwl6000_3agn_cfg)},
+ {IWL_PCI_DEVICE(0x422B, 0x1121, iwl6000_3agn_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1301, iwl6000i_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1306, iwl6000i_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1307, iwl6000i_2bg_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1321, iwl6000i_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1326, iwl6000i_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x4238, 0x1111, iwl6000_3agn_cfg)},
+ {IWL_PCI_DEVICE(0x4239, 0x1311, iwl6000i_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x4239, 0x1316, iwl6000i_2abg_cfg)},
+
+/* 6x50 WiFi/WiMax Series */
+ {IWL_PCI_DEVICE(0x0087, 0x1301, iwl6050_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0087, 0x1306, iwl6050_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x0087, 0x1321, iwl6050_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0087, 0x1326, iwl6050_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x0089, 0x1311, iwl6050_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0089, 0x1316, iwl6050_2abg_cfg)},
+
/* 1000 Series WiFi */
- {IWL_PCI_DEVICE(0x0083, PCI_ANY_ID, iwl1000_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0084, PCI_ANY_ID, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1205, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1305, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1225, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1325, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0084, 0x1215, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0084, 0x1315, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1206, iwl1000_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1306, iwl1000_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1226, iwl1000_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1326, iwl1000_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0084, 0x1216, iwl1000_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0084, 0x1316, iwl1000_bg_cfg)},
#endif /* CONFIG_IWL5000 */
{0}
@@ -3288,9 +3740,9 @@ module_exit(iwl_exit);
module_init(iwl_init);
#ifdef CONFIG_IWLWIFI_DEBUG
-module_param_named(debug50, iwl_debug_level, uint, 0444);
+module_param_named(debug50, iwl_debug_level, uint, S_IRUGO);
MODULE_PARM_DESC(debug50, "50XX debug output mask (deprecated)");
-module_param_named(debug, iwl_debug_level, uint, 0644);
+module_param_named(debug, iwl_debug_level, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "debug output mask");
#endif