From 6c3fd3f00c67105b49d57525614fcfa6816d604d Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 18 Oct 2012 12:38:37 +0200 Subject: [PATCH 01/17] iwlwifi: don't leak Tx skb when a queue is disabled Since the queue might not be empty, we need to free the pending Tx packets. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/pcie/internal.h | 1 + drivers/net/wireless/iwlwifi/pcie/trans.c | 4 ++-- drivers/net/wireless/iwlwifi/pcie/tx.c | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index 401178f44a3b..6ce58f03bc53 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -346,6 +346,7 @@ void iwl_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, enum dma_data_direction dma_dir); int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, struct sk_buff_head *skbs); +void iwl_tx_queue_unmap(struct iwl_trans *trans, int txq_id); int iwl_queue_space(const struct iwl_queue *q); /***************************************************** diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index f95d88df7772..a6a518116c7f 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -442,10 +442,10 @@ static int iwl_trans_txq_init(struct iwl_trans *trans, struct iwl_tx_queue *txq, return 0; } -/** +/* * iwl_tx_queue_unmap - Unmap any remaining DMA mappings and free skb's */ -static void iwl_tx_queue_unmap(struct iwl_trans *trans, int txq_id) +void iwl_tx_queue_unmap(struct iwl_trans *trans, int txq_id) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index db3efbb84d92..5db03145186c 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -494,6 +494,8 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id) _iwl_write_targ_mem_dwords(trans, stts_addr, zero_val, ARRAY_SIZE(zero_val)); + iwl_tx_queue_unmap(trans, txq_id); + IWL_DEBUG_TX_QUEUES(trans, "Deactivate queue %d\n", txq_id); } From 86052a77067df53ad48800e74984f84806baddbd Mon Sep 17 00:00:00 2001 From: Assaf Krauss Date: Wed, 18 Jul 2012 14:58:21 +0300 Subject: [PATCH 02/17] iwlwifi: remove MFP Kconfig option Remove the Kconfig option CONFIG_IWLWIFI_EXPERIMENTAL_MFP, if the firmware doesn't support MFP then the user shouldn't have the option to enable it as it won't work correctly. Signed-off-by: Assaf Krauss Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/Kconfig | 9 --------- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 2 -- 2 files changed, 11 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig index 727fbb5db9da..5cf43236421e 100644 --- a/drivers/net/wireless/iwlwifi/Kconfig +++ b/drivers/net/wireless/iwlwifi/Kconfig @@ -133,12 +133,3 @@ config IWLWIFI_P2P support when it is loaded. Say Y only if you want to experiment with P2P. - -config IWLWIFI_EXPERIMENTAL_MFP - bool "support MFP (802.11w) even if uCode doesn't advertise" - depends on IWLWIFI - help - This option enables experimental MFP (802.11W) support - even if the microcode doesn't advertise it. - - Say Y only if you want to experiment with MFP. diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index ff8162d4c454..46b0d1c46529 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -168,10 +168,8 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv, hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | IEEE80211_HW_SUPPORTS_STATIC_SMPS; -#ifndef CONFIG_IWLWIFI_EXPERIMENTAL_MFP /* enable 11w if the uCode advertise */ if (capa->flags & IWL_UCODE_TLV_FLAGS_MFP) -#endif /* !CONFIG_IWLWIFI_EXPERIMENTAL_MFP */ hw->flags |= IEEE80211_HW_MFP_CAPABLE; hw->sta_data_size = sizeof(struct iwl_station_priv); From f4feb8ac6e666d2ca37cf722166bbfadf2c6adf8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 19 Oct 2012 14:24:43 +0200 Subject: [PATCH 03/17] iwlwifi: support host command with copied data In addition to the NOCOPY flag, add a DUP flag that tells the transport to kmemdup() the buffer and free it after the command completes. Currently this is only supported for a single buffer in a given command, but that could be extended if it should be needed. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-trans.h | 6 ++- drivers/net/wireless/iwlwifi/pcie/internal.h | 2 + drivers/net/wireless/iwlwifi/pcie/rx.c | 3 ++ drivers/net/wireless/iwlwifi/pcie/trans.c | 1 + drivers/net/wireless/iwlwifi/pcie/tx.c | 57 ++++++++++++++++---- 5 files changed, 59 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index f75ea6d73ffc..76c52378f8f7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -221,14 +221,18 @@ struct iwl_device_cmd { /** * struct iwl_hcmd_dataflag - flag for each one of the chunks of the command * - * IWL_HCMD_DFL_NOCOPY: By default, the command is copied to the host command's + * @IWL_HCMD_DFL_NOCOPY: By default, the command is copied to the host command's * ring. The transport layer doesn't map the command's buffer to DMA, but * rather copies it to an previously allocated DMA buffer. This flag tells * the transport layer not to copy the command, but to map the existing * buffer. This can save memcpy and is worth with very big comamnds. + * @IWL_HCMD_DFL_DUP: Only valid without NOCOPY, duplicate the memory for this + * chunk internally and free it again after the command completes. This + * can (currently) be used only once per command. */ enum iwl_hcmd_dataflag { IWL_HCMD_DFL_NOCOPY = BIT(0), + IWL_HCMD_DFL_DUP = BIT(1), }; /** diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index 6ce58f03bc53..ae0f87e0e585 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -186,6 +186,8 @@ struct iwl_pcie_tx_queue_entry { struct iwl_device_cmd *cmd; struct iwl_device_cmd *copy_cmd; struct sk_buff *skb; + /* buffer to free after command completes */ + const void *free_buf; struct iwl_cmd_meta meta; }; diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index 137af4c46a6c..3f03f6e322c3 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -452,6 +452,9 @@ static void iwl_rx_handle_rxbuf(struct iwl_trans *trans, /* The original command isn't needed any more */ kfree(txq->entries[cmd_index].copy_cmd); txq->entries[cmd_index].copy_cmd = NULL; + /* nor is the duplicated part of the command */ + kfree(txq->entries[cmd_index].free_buf); + txq->entries[cmd_index].free_buf = NULL; } /* diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index a6a518116c7f..b8a155af12cc 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -496,6 +496,7 @@ static void iwl_tx_queue_free(struct iwl_trans *trans, int txq_id) for (i = 0; i < txq->q.n_window; i++) { kfree(txq->entries[i].cmd); kfree(txq->entries[i].copy_cmd); + kfree(txq->entries[i].free_buf); } /* De-alloc circular buffer of TFDs */ diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 5db03145186c..9cb30ae5e9a1 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -517,8 +517,9 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) struct iwl_queue *q = &txq->q; struct iwl_device_cmd *out_cmd; struct iwl_cmd_meta *out_meta; + void *dup_buf = NULL; dma_addr_t phys_addr; - u32 idx; + int idx; u16 copy_size, cmd_size; bool had_nocopy = false; int i; @@ -535,10 +536,33 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) continue; if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) { had_nocopy = true; + if (WARN_ON(cmd->dataflags[i] & IWL_HCMD_DFL_DUP)) { + idx = -EINVAL; + goto free_dup_buf; + } + } else if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP) { + /* + * This is also a chunk that isn't copied + * to the static buffer so set had_nocopy. + */ + had_nocopy = true; + + /* only allowed once */ + if (WARN_ON(dup_buf)) { + idx = -EINVAL; + goto free_dup_buf; + } + + dup_buf = kmemdup(cmd->data[i], cmd->len[i], + GFP_ATOMIC); + if (!dup_buf) + return -ENOMEM; } else { /* NOCOPY must not be followed by normal! */ - if (WARN_ON(had_nocopy)) - return -EINVAL; + if (WARN_ON(had_nocopy)) { + idx = -EINVAL; + goto free_dup_buf; + } copy_size += cmd->len[i]; } cmd_size += cmd->len[i]; @@ -553,8 +577,10 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) if (WARN(copy_size > TFD_MAX_PAYLOAD_SIZE, "Command %s (%#x) is too large (%d bytes)\n", trans_pcie_get_cmd_string(trans_pcie, cmd->id), - cmd->id, copy_size)) - return -EINVAL; + cmd->id, copy_size)) { + idx = -EINVAL; + goto free_dup_buf; + } spin_lock_bh(&txq->lock); @@ -563,7 +589,8 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) IWL_ERR(trans, "No space in command queue\n"); iwl_op_mode_cmd_queue_full(trans->op_mode); - return -ENOSPC; + idx = -ENOSPC; + goto free_dup_buf; } idx = get_cmd_index(q, q->write_ptr); @@ -587,7 +614,8 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { if (!cmd->len[i]) continue; - if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) + if (cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY | + IWL_HCMD_DFL_DUP)) break; memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], cmd->len[i]); cmd_pos += cmd->len[i]; @@ -629,11 +657,16 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, copy_size, 1); for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { + const void *data = cmd->data[i]; + if (!cmd->len[i]) continue; - if (!(cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY)) + if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY | + IWL_HCMD_DFL_DUP))) continue; - phys_addr = dma_map_single(trans->dev, (void *)cmd->data[i], + if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP) + data = dup_buf; + phys_addr = dma_map_single(trans->dev, (void *)data, cmd->len[i], DMA_BIDIRECTIONAL); if (dma_mapping_error(trans->dev, phys_addr)) { iwl_unmap_tfd(trans, out_meta, @@ -648,6 +681,9 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) } out_meta->flags = cmd->flags; + if (WARN_ON_ONCE(txq->entries[idx].free_buf)) + kfree(txq->entries[idx].free_buf); + txq->entries[idx].free_buf = dup_buf; txq->need_update = 1; @@ -664,6 +700,9 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) out: spin_unlock_bh(&txq->lock); + free_dup_buf: + if (idx < 0) + kfree(dup_buf); return idx; } From 3e2c159260eed10c44f0dd028c328a40a27ad235 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 24 Oct 2012 13:26:15 +0200 Subject: [PATCH 04/17] iwlwifi: clarify NOCOPY/DUP documentation Clarify the documentation to indicate that these flags can only be used at the end, i.e. after them a copy TFD (no flags set) is invalid. Reported-by: Inbal Hacohen Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-trans.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 76c52378f8f7..47bbe399c068 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -225,10 +225,13 @@ struct iwl_device_cmd { * ring. The transport layer doesn't map the command's buffer to DMA, but * rather copies it to an previously allocated DMA buffer. This flag tells * the transport layer not to copy the command, but to map the existing - * buffer. This can save memcpy and is worth with very big comamnds. + * buffer (that is passed in) instead. This saves the memcpy and allows + * commands that are bigger than the fixed buffer to be submitted. + * Note that a TFD entry after a NOCOPY one cannot be a normal copied one. * @IWL_HCMD_DFL_DUP: Only valid without NOCOPY, duplicate the memory for this * chunk internally and free it again after the command completes. This * can (currently) be used only once per command. + * Note that a TFD entry after a DUP one cannot be a normal copied one. */ enum iwl_hcmd_dataflag { IWL_HCMD_DFL_NOCOPY = BIT(0), From f946b529502399d09471c5d13845fefbfe8555a6 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 25 Oct 2012 17:25:52 +0200 Subject: [PATCH 05/17] iwlwifi: handle RFKILL logic in the transport layer No HCMD can be sent while RFKILL is asserted. If a SYNC command is running while RFKILL is asserted the fw will silently discard it. This means that the driver needs to wake the process that sleeps on the CMD_SYNC. Since the RFKILL interrupt is handled in the transport layer and the code that sleeps in CMD_SYNC is also in the transport layer, all this logic can be handled there. This simplifies the work of the op_mode. So the transport layer will now return -ERFKILL when a CMD is sent and RFKILL is asserted. This will be the case even when the CMD is SYNC. The transport layer will return -ERFKILL straight away. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/main.c | 2 -- drivers/net/wireless/iwlwifi/dvm/rx.c | 2 -- drivers/net/wireless/iwlwifi/iwl-trans.h | 7 +++---- drivers/net/wireless/iwlwifi/pcie/internal.h | 13 +++++++++---- drivers/net/wireless/iwlwifi/pcie/rx.c | 20 ++++++++++++++++---- drivers/net/wireless/iwlwifi/pcie/trans.c | 3 ++- drivers/net/wireless/iwlwifi/pcie/tx.c | 16 ++++++++++++++-- 7 files changed, 44 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index 475df45c8320..2b50d11720d4 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -1926,8 +1926,6 @@ static void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand) * commands by clearing the ready bit */ clear_bit(STATUS_READY, &priv->status); - wake_up(&priv->trans->wait_command_queue); - if (!ondemand) { /* * If firmware keep reloading, then it indicate something diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/iwlwifi/dvm/rx.c index 5a9c325804f6..9a8d5020774e 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rx.c +++ b/drivers/net/wireless/iwlwifi/dvm/rx.c @@ -631,8 +631,6 @@ static int iwlagn_rx_card_state_notif(struct iwl_priv *priv, test_bit(STATUS_RF_KILL_HW, &priv->status))) wiphy_rfkill_set_hw_state(priv->hw->wiphy, test_bit(STATUS_RF_KILL_HW, &priv->status)); - else - wake_up(&priv->trans->wait_command_queue); return 0; } diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 47bbe399c068..b065d48de464 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -362,7 +362,9 @@ struct iwl_trans; * @wowlan_suspend: put the device into the correct mode for WoWLAN during * suspend. This is optional, if not implemented WoWLAN will not be * supported. This callback may sleep. - * @send_cmd:send a host command + * @send_cmd:send a host command. Must return -ERFKILL if RFkill is asserted. + * If RFkill is asserted in the middle of a SYNC host command, it must + * return -ERFKILL straight away. * May sleep only if CMD_SYNC is set * @tx: send an skb * Must be atomic @@ -445,7 +447,6 @@ enum iwl_trans_state { * Set during transport allocation. * @hw_id_str: a string with info about HW ID. Set during transport allocation. * @pm_support: set to true in start_hw if link pm is supported - * @wait_command_queue: the wait_queue for SYNC host commands * @dev_cmd_pool: pool for Tx cmd allocation - for internal use only. * The user should use iwl_trans_{alloc,free}_tx_cmd. * @dev_cmd_headroom: room needed for the transport's private use before the @@ -472,8 +473,6 @@ struct iwl_trans { bool pm_support; - wait_queue_head_t wait_command_queue; - /* The following fields are internal only */ struct kmem_cache *dev_cmd_pool; size_t dev_cmd_headroom; diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index ae0f87e0e585..847ef1e067bb 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -270,6 +270,8 @@ struct iwl_trans_pcie { bool ucode_write_complete; wait_queue_head_t ucode_write_waitq; + wait_queue_head_t wait_command_queue; + unsigned long status; u8 cmd_queue; u8 cmd_fifo; @@ -288,10 +290,13 @@ struct iwl_trans_pcie { /***************************************************** * DRIVER STATUS FUNCTIONS ******************************************************/ -#define STATUS_HCMD_ACTIVE 0 -#define STATUS_DEVICE_ENABLED 1 -#define STATUS_TPOWER_PMI 2 -#define STATUS_INT_ENABLED 3 +enum { + STATUS_HCMD_ACTIVE, + STATUS_DEVICE_ENABLED, + STATUS_TPOWER_PMI, + STATUS_INT_ENABLED, + STATUS_RFKILL, +}; #define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \ ((struct iwl_trans_pcie *) ((_iwl_trans)->trans_specific)) diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index 3f03f6e322c3..50c9147278b3 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -568,24 +568,26 @@ static void iwl_rx_handle(struct iwl_trans *trans) */ static void iwl_irq_handle_error(struct iwl_trans *trans) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + /* W/A for WiFi/WiMAX coex and WiMAX own the RF */ if (trans->cfg->internal_wimax_coex && (!(iwl_read_prph(trans, APMG_CLK_CTRL_REG) & APMS_CLK_VAL_MRB_FUNC_MODE) || (iwl_read_prph(trans, APMG_PS_CTRL_REG) & APMG_PS_CTRL_VAL_RESET_REQ))) { - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); iwl_op_mode_wimax_active(trans->op_mode); - wake_up(&trans->wait_command_queue); + wake_up(&trans_pcie->wait_command_queue); return; } iwl_dump_csr(trans); iwl_dump_fh(trans, NULL); + clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); + wake_up(&trans_pcie->wait_command_queue); + iwl_op_mode_nic_error(trans->op_mode); } @@ -679,6 +681,16 @@ void iwl_irq_tasklet(struct iwl_trans *trans) isr_stats->rfkill++; iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); + if (hw_rfkill) { + set_bit(STATUS_RFKILL, &trans_pcie->status); + if (test_and_clear_bit(STATUS_HCMD_ACTIVE, + &trans_pcie->status)) + IWL_DEBUG_RF_KILL(trans, + "Rfkill while SYNC HCMD in flight\n"); + wake_up(&trans_pcie->wait_command_queue); + } else { + clear_bit(STATUS_RFKILL, &trans_pcie->status); + } handled |= CSR_INT_BIT_RF_KILL; } diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index b8a155af12cc..288d2297f77d 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -1246,6 +1246,7 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) clear_bit(STATUS_INT_ENABLED, &trans_pcie->status); clear_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status); clear_bit(STATUS_TPOWER_PMI, &trans_pcie->status); + clear_bit(STATUS_RFKILL, &trans_pcie->status); } static void iwl_trans_pcie_wowlan_suspend(struct iwl_trans *trans) @@ -2206,7 +2207,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, } /* Initialize the wait queue for commands */ - init_waitqueue_head(&trans->wait_command_queue); + init_waitqueue_head(&trans_pcie->wait_command_queue); spin_lock_init(&trans->reg_lock); snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name), diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 9cb30ae5e9a1..ae73bd3944e8 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -827,7 +827,7 @@ void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_cmd_buffer *rxb, IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", trans_pcie_get_cmd_string(trans_pcie, cmd->hdr.cmd)); - wake_up(&trans->wait_command_queue); + wake_up(&trans_pcie->wait_command_queue); } meta->flags = 0; @@ -886,7 +886,7 @@ static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) return ret; } - ret = wait_event_timeout(trans->wait_command_queue, + ret = wait_event_timeout(trans_pcie->wait_command_queue, !test_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status), HOST_COMPLETE_TIMEOUT); @@ -915,6 +915,12 @@ static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) } } + if (test_bit(STATUS_RFKILL, &trans_pcie->status)) { + IWL_DEBUG_RF_KILL(trans, "RFKILL in SYNC CMD... no rsp\n"); + ret = -ERFKILL; + goto cancel; + } + if ((cmd->flags & CMD_WANT_SKB) && !cmd->resp_pkt) { IWL_ERR(trans, "Error: Response NULL in '%s'\n", trans_pcie_get_cmd_string(trans_pcie, cmd->id)); @@ -946,9 +952,15 @@ cancel: int iwl_trans_pcie_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + if (test_bit(STATUS_RFKILL, &trans_pcie->status)) + return -ERFKILL; + if (cmd->flags & CMD_ASYNC) return iwl_send_cmd_async(trans, cmd); + /* We still can fail on RFKILL that can be asserted while we wait */ return iwl_send_cmd_sync(trans, cmd); } From 0daf7d9605352d4f0c5575a897dd71787ea92b9c Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 30 Oct 2012 18:31:41 +0200 Subject: [PATCH 06/17] iwlwifi: don't call stop_device twice When we unregister from mac80211 it will down the device anyway. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/main.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index 2b50d11720d4..4547f36bcc6c 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -1507,10 +1507,6 @@ static void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode) iwl_tt_exit(priv); - /*This will stop the queues, move the device to low power state */ - priv->ucode_loaded = false; - iwl_trans_stop_device(priv->trans); - kfree(priv->eeprom_blob); iwl_free_eeprom_data(priv->eeprom_data); From 37c477dc1c6cad4a36049a9a10e4aa3dbf450c0f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 31 Oct 2012 20:49:32 +0100 Subject: [PATCH 07/17] iwlwifi: fix flush command The flush command really flushes queues, not FIFOs, and the first 32 bits indicate the queues to flush, not FIFOs. Change the command accordingly. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/commands.h | 4 ++-- drivers/net/wireless/iwlwifi/dvm/lib.c | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/dvm/commands.h b/drivers/net/wireless/iwlwifi/dvm/commands.h index 01128c96b5d8..ac24861f04ad 100644 --- a/drivers/net/wireless/iwlwifi/dvm/commands.h +++ b/drivers/net/wireless/iwlwifi/dvm/commands.h @@ -1004,14 +1004,14 @@ struct iwl_rem_sta_cmd { * the flush operation ends when both the scheduler DMA done and TXFIFO empty * are set. * - * @fifo_control: bit mask for which queues to flush + * @queue_control: bit mask for which queues to flush * @flush_control: flush controls * 0: Dump single MSDU * 1: Dump multiple MSDU according to PS, INVALID STA, TTL, TID disable. * 2: Dump all FIFO */ struct iwl_txfifo_flush_cmd { - __le32 fifo_control; + __le32 queue_control; __le16 flush_control; __le16 reserved; } __packed; diff --git a/drivers/net/wireless/iwlwifi/dvm/lib.c b/drivers/net/wireless/iwlwifi/dvm/lib.c index bef88c1a2c9b..01ff55faf2ab 100644 --- a/drivers/net/wireless/iwlwifi/dvm/lib.c +++ b/drivers/net/wireless/iwlwifi/dvm/lib.c @@ -150,21 +150,21 @@ int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control) memset(&flush_cmd, 0, sizeof(flush_cmd)); if (flush_control & BIT(IWL_RXON_CTX_BSS)) - flush_cmd.fifo_control = IWL_SCD_VO_MSK | IWL_SCD_VI_MSK | + flush_cmd.queue_control = IWL_SCD_VO_MSK | IWL_SCD_VI_MSK | IWL_SCD_BE_MSK | IWL_SCD_BK_MSK | IWL_SCD_MGMT_MSK; if ((flush_control & BIT(IWL_RXON_CTX_PAN)) && (priv->valid_contexts != BIT(IWL_RXON_CTX_BSS))) - flush_cmd.fifo_control |= IWL_PAN_SCD_VO_MSK | + flush_cmd.queue_control |= IWL_PAN_SCD_VO_MSK | IWL_PAN_SCD_VI_MSK | IWL_PAN_SCD_BE_MSK | IWL_PAN_SCD_BK_MSK | IWL_PAN_SCD_MGMT_MSK | IWL_PAN_SCD_MULTICAST_MSK; if (priv->eeprom_data->sku & EEPROM_SKU_CAP_11N_ENABLE) - flush_cmd.fifo_control |= IWL_AGG_TX_QUEUE_MSK; + flush_cmd.queue_control |= IWL_AGG_TX_QUEUE_MSK; - IWL_DEBUG_INFO(priv, "fifo queue control: 0X%x\n", - flush_cmd.fifo_control); + IWL_DEBUG_INFO(priv, "queue control: 0x%x\n", + flush_cmd.queue_control); flush_cmd.flush_control = cpu_to_le16(flush_control); return iwl_dvm_send_cmd(priv, &cmd); From 2eb81a40aa521035ff9c8c8309e482dff523f8c9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 31 Oct 2012 21:46:07 +0100 Subject: [PATCH 08/17] iwlwifi: don't clear CTL_AMPDU on frame status There's no reason to clear the CTL_AMPDU flag on transmitted frame status, it's not used by the driver here and mac80211 only uses it for some rate statistics. Also remove a stray space in the function. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/tx.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index f5ca73a89870..4ae031f6726b 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -1075,14 +1075,11 @@ static void iwlagn_count_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 iwlagn_tx_resp *tx_resp, - bool is_agg) + struct iwlagn_tx_resp *tx_resp) { - u16 status = le16_to_cpu(tx_resp->status.status); + u16 status = le16_to_cpu(tx_resp->status.status); info->status.rates[0].count = tx_resp->failure_frame + 1; - if (is_agg) - info->flags &= ~IEEE80211_TX_CTL_AMPDU; info->flags |= iwl_tx_status_to_mac80211(status); iwlagn_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags), info); @@ -1231,7 +1228,7 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, if (is_agg && !iwl_is_tx_success(status)) info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; iwlagn_set_tx_status(priv, IEEE80211_SKB_CB(skb), - tx_resp, is_agg); + tx_resp); if (!is_agg) iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1); From a4dece9abce75332a082340bcc9b176f10484a62 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 31 Oct 2012 22:21:28 +0100 Subject: [PATCH 09/17] iwlwifi: fix queue flush confusion The flush_control parameter to iwlagn_txfifo_flush is passed as an internal value (context flags) and then sent to the device, that can't be right. Fix the confusion by removing the parameter, always use IWL_DROP_ALL that is redefined according to the firmware API in the flush control. Signed-off-by: Johannes Berg Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/agn.h | 4 +-- drivers/net/wireless/iwlwifi/dvm/commands.h | 3 +-- drivers/net/wireless/iwlwifi/dvm/debugfs.c | 2 +- drivers/net/wireless/iwlwifi/dvm/lib.c | 29 ++++++++++----------- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 2 +- drivers/net/wireless/iwlwifi/dvm/main.c | 2 +- 6 files changed, 20 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h index 75e12f29d9eb..33b3ad2e546b 100644 --- a/drivers/net/wireless/iwlwifi/dvm/agn.h +++ b/drivers/net/wireless/iwlwifi/dvm/agn.h @@ -176,8 +176,8 @@ int iwlagn_hw_valid_rtc_data_addr(u32 addr); /* lib */ int iwlagn_send_tx_power(struct iwl_priv *priv); void iwlagn_temperature(struct iwl_priv *priv); -int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control); -void iwlagn_dev_txfifo_flush(struct iwl_priv *priv, u16 flush_control); +int iwlagn_txfifo_flush(struct iwl_priv *priv); +void iwlagn_dev_txfifo_flush(struct iwl_priv *priv); int iwlagn_send_beacon_cmd(struct iwl_priv *priv); int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear); diff --git a/drivers/net/wireless/iwlwifi/dvm/commands.h b/drivers/net/wireless/iwlwifi/dvm/commands.h index ac24861f04ad..71ab76b2b39d 100644 --- a/drivers/net/wireless/iwlwifi/dvm/commands.h +++ b/drivers/net/wireless/iwlwifi/dvm/commands.h @@ -986,8 +986,7 @@ struct iwl_rem_sta_cmd { #define IWL_AGG_TX_QUEUE_MSK cpu_to_le32(0xffc00) -#define IWL_DROP_SINGLE 0 -#define IWL_DROP_ALL (BIT(IWL_RXON_CTX_BSS) | BIT(IWL_RXON_CTX_PAN)) +#define IWL_DROP_ALL BIT(1) /* * REPLY_TXFIFO_FLUSH = 0x1e(command and response) diff --git a/drivers/net/wireless/iwlwifi/dvm/debugfs.c b/drivers/net/wireless/iwlwifi/dvm/debugfs.c index 1a98fa3ab06d..769a08bca86f 100644 --- a/drivers/net/wireless/iwlwifi/dvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/dvm/debugfs.c @@ -2101,7 +2101,7 @@ static ssize_t iwl_dbgfs_txfifo_flush_write(struct file *file, if (iwl_is_rfkill(priv)) return -EFAULT; - iwlagn_dev_txfifo_flush(priv, IWL_DROP_ALL); + iwlagn_dev_txfifo_flush(priv); return count; } diff --git a/drivers/net/wireless/iwlwifi/dvm/lib.c b/drivers/net/wireless/iwlwifi/dvm/lib.c index 01ff55faf2ab..7e59be4b89b8 100644 --- a/drivers/net/wireless/iwlwifi/dvm/lib.c +++ b/drivers/net/wireless/iwlwifi/dvm/lib.c @@ -136,7 +136,7 @@ int iwlagn_manage_ibss_station(struct iwl_priv *priv, * 1. acquire mutex before calling * 2. make sure rf is on and not in exit state */ -int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control) +int iwlagn_txfifo_flush(struct iwl_priv *priv) { struct iwl_txfifo_flush_cmd flush_cmd; struct iwl_host_cmd cmd = { @@ -146,35 +146,34 @@ int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control) .data = { &flush_cmd, }, }; - might_sleep(); - memset(&flush_cmd, 0, sizeof(flush_cmd)); - if (flush_control & BIT(IWL_RXON_CTX_BSS)) - flush_cmd.queue_control = IWL_SCD_VO_MSK | IWL_SCD_VI_MSK | - IWL_SCD_BE_MSK | IWL_SCD_BK_MSK | - IWL_SCD_MGMT_MSK; - if ((flush_control & BIT(IWL_RXON_CTX_PAN)) && - (priv->valid_contexts != BIT(IWL_RXON_CTX_BSS))) + + flush_cmd.queue_control = IWL_SCD_VO_MSK | IWL_SCD_VI_MSK | + IWL_SCD_BE_MSK | IWL_SCD_BK_MSK | + IWL_SCD_MGMT_MSK; + if ((priv->valid_contexts != BIT(IWL_RXON_CTX_BSS))) flush_cmd.queue_control |= IWL_PAN_SCD_VO_MSK | - IWL_PAN_SCD_VI_MSK | IWL_PAN_SCD_BE_MSK | - IWL_PAN_SCD_BK_MSK | IWL_PAN_SCD_MGMT_MSK | - IWL_PAN_SCD_MULTICAST_MSK; + IWL_PAN_SCD_VI_MSK | + IWL_PAN_SCD_BE_MSK | + IWL_PAN_SCD_BK_MSK | + IWL_PAN_SCD_MGMT_MSK | + IWL_PAN_SCD_MULTICAST_MSK; if (priv->eeprom_data->sku & EEPROM_SKU_CAP_11N_ENABLE) flush_cmd.queue_control |= IWL_AGG_TX_QUEUE_MSK; IWL_DEBUG_INFO(priv, "queue control: 0x%x\n", flush_cmd.queue_control); - flush_cmd.flush_control = cpu_to_le16(flush_control); + flush_cmd.flush_control = cpu_to_le16(IWL_DROP_ALL); return iwl_dvm_send_cmd(priv, &cmd); } -void iwlagn_dev_txfifo_flush(struct iwl_priv *priv, u16 flush_control) +void iwlagn_dev_txfifo_flush(struct iwl_priv *priv) { mutex_lock(&priv->mutex); ieee80211_stop_queues(priv->hw); - if (iwlagn_txfifo_flush(priv, IWL_DROP_ALL)) { + if (iwlagn_txfifo_flush(priv)) { IWL_ERR(priv, "flush request fail\n"); goto done; } diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 46b0d1c46529..cb443d54f9b9 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -1017,7 +1017,7 @@ static void iwlagn_mac_flush(struct ieee80211_hw *hw, bool drop) */ if (drop) { IWL_DEBUG_MAC80211(priv, "send flush command\n"); - if (iwlagn_txfifo_flush(priv, IWL_DROP_ALL)) { + if (iwlagn_txfifo_flush(priv)) { IWL_ERR(priv, "flush request fail\n"); goto done; } diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index 4547f36bcc6c..95d348b02846 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -511,7 +511,7 @@ static void iwl_bg_tx_flush(struct work_struct *work) return; IWL_DEBUG_INFO(priv, "device request: flush all tx frames\n"); - iwlagn_dev_txfifo_flush(priv, IWL_DROP_ALL); + iwlagn_dev_txfifo_flush(priv); } /* From e2b1930e6fd9aaa72cc44519b257ae71a1c2e29f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 4 Nov 2012 09:31:25 +0100 Subject: [PATCH 10/17] iwlwifi: use list_first_entry Instead of open-coding it with a temporary list_head pointer, just use list_first_entry. Reviewed-by: Gregory Greenman Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/pcie/rx.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index 50c9147278b3..25e6f868cfb6 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -199,7 +199,6 @@ static void iwl_rx_queue_restock(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rx_queue *rxq = &trans_pcie->rxq; - struct list_head *element; struct iwl_rx_mem_buffer *rxb; unsigned long flags; @@ -221,9 +220,9 @@ static void iwl_rx_queue_restock(struct iwl_trans *trans) BUG_ON(rxb && rxb->page); /* Get next free Rx buffer, remove from free list */ - element = rxq->rx_free.next; - rxb = list_entry(element, struct iwl_rx_mem_buffer, list); - list_del(element); + rxb = list_first_entry(&rxq->rx_free, struct iwl_rx_mem_buffer, + list); + list_del(&rxb->list); /* Point to Rx buffer via next RBD in circular buffer */ rxq->bd[rxq->write] = iwl_dma_addr2rbd_ptr(rxb->page_dma); @@ -260,7 +259,6 @@ static void iwl_rx_allocate(struct iwl_trans *trans, gfp_t priority) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rx_queue *rxq = &trans_pcie->rxq; - struct list_head *element; struct iwl_rx_mem_buffer *rxb; struct page *page; unsigned long flags; @@ -308,10 +306,9 @@ static void iwl_rx_allocate(struct iwl_trans *trans, gfp_t priority) __free_pages(page, trans_pcie->rx_page_order); return; } - element = rxq->rx_used.next; - rxb = list_entry(element, struct iwl_rx_mem_buffer, list); - list_del(element); - + rxb = list_first_entry(&rxq->rx_used, struct iwl_rx_mem_buffer, + list); + list_del(&rxb->list); spin_unlock_irqrestore(&rxq->lock, flags); BUG_ON(rxb->page); From adca1235cccacee91beb640a044e51b1c457b1ea Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 25 Oct 2012 23:08:27 +0200 Subject: [PATCH 11/17] iwlwifi: check the SCD conf from ALIVE response The ALIVE response of new fw inclues the base address of the SCD in SRAM. Until we read it from a prph register, which was set by the fw. Since the fw might well stop updating the prph register, add a WARN when there is an inconsitency between the ALIVE response and the register to catch any change in the behavior. Reviewed-by: Gregory Greenman Reviewed-by: Johannes Berg Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/ucode.c | 2 +- drivers/net/wireless/iwlwifi/iwl-trans.h | 9 +++++---- drivers/net/wireless/iwlwifi/pcie/trans.c | 10 +++++++--- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c index 2cb1efbc5ed1..95e6d33f5159 100644 --- a/drivers/net/wireless/iwlwifi/dvm/ucode.c +++ b/drivers/net/wireless/iwlwifi/dvm/ucode.c @@ -254,7 +254,7 @@ static int iwl_alive_notify(struct iwl_priv *priv) int ret; int i; - iwl_trans_fw_alive(priv->trans); + iwl_trans_fw_alive(priv->trans, 0); if (priv->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PAN && priv->eeprom_data->sku & EEPROM_SKU_CAP_IPAN_ENABLE) { diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index b065d48de464..e378ea6dca9c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -355,7 +355,8 @@ struct iwl_trans; * @start_fw: allocates and inits all the resources for the transport * layer. Also kick a fw image. * May sleep - * @fw_alive: called when the fw sends alive notification + * @fw_alive: called when the fw sends alive notification. If the fw provides + * the SCD base address in SRAM, then provide it here, or 0 otherwise. * May sleep * @stop_device:stops the whole device (embedded CPU put to reset) * May sleep @@ -394,7 +395,7 @@ struct iwl_trans_ops { int (*start_hw)(struct iwl_trans *iwl_trans); void (*stop_hw)(struct iwl_trans *iwl_trans, bool op_mode_leaving); int (*start_fw)(struct iwl_trans *trans, const struct fw_img *fw); - void (*fw_alive)(struct iwl_trans *trans); + void (*fw_alive)(struct iwl_trans *trans, u32 scd_addr); void (*stop_device)(struct iwl_trans *trans); void (*wowlan_suspend)(struct iwl_trans *trans); @@ -514,13 +515,13 @@ static inline void iwl_trans_stop_hw(struct iwl_trans *trans, trans->state = IWL_TRANS_NO_FW; } -static inline void iwl_trans_fw_alive(struct iwl_trans *trans) +static inline void iwl_trans_fw_alive(struct iwl_trans *trans, u32 scd_addr) { might_sleep(); trans->state = IWL_TRANS_FW_ALIVE; - trans->ops->fw_alive(trans); + trans->ops->fw_alive(trans, scd_addr); } static inline int iwl_trans_start_fw(struct iwl_trans *trans, diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 288d2297f77d..0703da892e59 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -1079,7 +1079,7 @@ static void iwl_trans_txq_set_sched(struct iwl_trans *trans, u32 mask) iwl_write_prph(trans, SCD_TXFACT, mask); } -static void iwl_tx_start(struct iwl_trans *trans) +static void iwl_tx_start(struct iwl_trans *trans, u32 scd_base_addr) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u32 a; @@ -1092,6 +1092,10 @@ static void iwl_tx_start(struct iwl_trans *trans) trans_pcie->scd_base_addr = iwl_read_prph(trans, SCD_SRAM_BASE_ADDR); + + WARN_ON(scd_base_addr != 0 && + scd_base_addr != trans_pcie->scd_base_addr); + a = trans_pcie->scd_base_addr + SCD_CONTEXT_MEM_LOWER_BOUND; /* reset conext data memory */ for (; a < trans_pcie->scd_base_addr + SCD_CONTEXT_MEM_UPPER_BOUND; @@ -1137,10 +1141,10 @@ static void iwl_tx_start(struct iwl_trans *trans) APMG_PCIDEV_STT_VAL_L1_ACT_DIS); } -static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans) +static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr) { iwl_reset_ict(trans); - iwl_tx_start(trans); + iwl_tx_start(trans, scd_addr); } /** From e7332691de2f97e47099088ca5d20f1ca6be8e0b Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 5 Nov 2012 12:52:57 +0200 Subject: [PATCH 12/17] iwlwifi: zero trans_cfg before settings its fields People tend not to set the fields they want to leave as 0. So make sure the struct is zeroed properly. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index 95d348b02846..bcaec84974d0 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -1231,7 +1231,7 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, struct iwl_op_mode *op_mode; u16 num_mac; u32 ucode_flags; - struct iwl_trans_config trans_cfg; + struct iwl_trans_config trans_cfg = {}; static const u8 no_reclaim_cmds[] = { REPLY_RX_PHY_CMD, REPLY_RX_MPDU_CMD, From d01031b23e1cdbfe173404150804284a3aba714b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 5 Nov 2012 19:50:26 +0100 Subject: [PATCH 13/17] iwlwifi: remove EEPROM version message by default If the EEPROM reading was successful, don't print a message by default, the EEPROM version isn't all that interesting. Change the message to DEBUG_INFO priority so it can still be obtained. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c index f10170fe8799..4a9dc9629efe 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c @@ -889,8 +889,8 @@ int iwl_eeprom_check_version(struct iwl_eeprom_data *data, { if (data->eeprom_version >= trans->cfg->eeprom_ver || data->calib_version >= trans->cfg->eeprom_calib_ver) { - IWL_INFO(trans, "device EEPROM VER=0x%x, CALIB=0x%x\n", - data->eeprom_version, data->calib_version); + IWL_DEBUG_INFO(trans, "device EEPROM VER=0x%x, CALIB=0x%x\n", + data->eeprom_version, data->calib_version); return 0; } From 468b4bf38950b03ff2ce8316f836d2c33b17a321 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 5 Nov 2012 19:48:33 +0100 Subject: [PATCH 14/17] iwlwifi: remove SKU/antenna messages by default There's no reason to print these all the time, the messages aren't all that interesting. Leave them as DEBUG_INFO though, just in case. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index bcaec84974d0..03cbfa765f87 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -1204,7 +1204,7 @@ static int iwl_eeprom_init_hw_params(struct iwl_priv *priv) return -EINVAL; } - IWL_INFO(priv, "Device SKU: 0x%X\n", priv->eeprom_data->sku); + IWL_DEBUG_INFO(priv, "Device SKU: 0x%X\n", priv->eeprom_data->sku); priv->hw_params.tx_chains_num = num_of_ant(priv->eeprom_data->valid_tx_ant); @@ -1214,9 +1214,9 @@ static int iwl_eeprom_init_hw_params(struct iwl_priv *priv) priv->hw_params.rx_chains_num = num_of_ant(priv->eeprom_data->valid_rx_ant); - IWL_INFO(priv, "Valid Tx ant: 0x%X, Valid Rx ant: 0x%X\n", - priv->eeprom_data->valid_tx_ant, - priv->eeprom_data->valid_rx_ant); + IWL_DEBUG_INFO(priv, "Valid Tx ant: 0x%X, Valid Rx ant: 0x%X\n", + priv->eeprom_data->valid_tx_ant, + priv->eeprom_data->valid_rx_ant); return 0; } From 6b450fcbff6a6d9e4f7ebe47e579ab4c43a6b745 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 5 Nov 2012 19:45:36 +0100 Subject: [PATCH 15/17] iwlwifi: remove useless messages There's no need to print the PCI resource length and base address, nor the hardware revision ID (which can be found in lspci) Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/pcie/trans.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 0703da892e59..74d4c792bc75 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -2176,15 +2176,6 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, goto out_pci_release_regions; } - dev_printk(KERN_INFO, &pdev->dev, - "pci_resource_len = 0x%08llx\n", - (unsigned long long) pci_resource_len(pdev, 0)); - dev_printk(KERN_INFO, &pdev->dev, - "pci_resource_base = %p\n", trans_pcie->hw_base); - - dev_printk(KERN_INFO, &pdev->dev, - "HW Revision ID = 0x%X\n", pdev->revision); - /* We disable the RETRY_TIMEOUT register (0x41) to keep * PCI Tx retries from interfering with C3 CPU state */ pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); From 2e6e6b1f3f6ca4903f2b2a8e8ac764e5cef2e7a8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Nov 2012 12:37:21 +0100 Subject: [PATCH 16/17] iwlwifi: fix typo in RX data tracing The printk message should say RX, not TX. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-devtrace.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/iwlwifi/iwl-devtrace.h index 678717bf62eb..b3fde5f7b9bc 100644 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace.h +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.h @@ -306,7 +306,7 @@ TRACE_EVENT(iwlwifi_dev_rx_data, memcpy(__get_dynamic_array(data), ((u8 *)rxbuf) + offs, len - offs); ), - TP_printk("[%s] TX frame data", __get_str(dev)) + TP_printk("[%s] RX frame data", __get_str(dev)) ); #undef TRACE_SYSTEM From d18aa87fbfe80f33076942d11f19c9d813e835b1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Nov 2012 16:36:21 +0100 Subject: [PATCH 17/17] iwlwifi: return commands with error on FW error When a firmware error occurs, don't just abort synchronous commands but also return an error (-EIO) and block any new commands as well. Currently, an error is only returned if WANT_SKB was set which is confusing and can lead to issues. Blocking is done until a new firmware image is loaded. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/pcie/internal.h | 1 + drivers/net/wireless/iwlwifi/pcie/rx.c | 1 + drivers/net/wireless/iwlwifi/pcie/trans.c | 3 +++ drivers/net/wireless/iwlwifi/pcie/tx.c | 10 ++++++++++ 4 files changed, 15 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index 847ef1e067bb..1f065c630d43 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -296,6 +296,7 @@ enum { STATUS_TPOWER_PMI, STATUS_INT_ENABLED, STATUS_RFKILL, + STATUS_FW_ERROR, }; #define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \ diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index 25e6f868cfb6..11a93eddc84f 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -582,6 +582,7 @@ static void iwl_irq_handle_error(struct iwl_trans *trans) iwl_dump_csr(trans); iwl_dump_fh(trans, NULL); + set_bit(STATUS_FW_ERROR, &trans_pcie->status); clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); wake_up(&trans_pcie->wait_command_queue); diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 74d4c792bc75..3adbf4c21ffc 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -1026,6 +1026,7 @@ static int iwl_load_given_ucode(struct iwl_trans *trans, static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, const struct fw_img *fw) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret; bool hw_rfkill; @@ -1035,6 +1036,8 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, return -EIO; } + clear_bit(STATUS_FW_ERROR, &trans_pcie->status); + iwl_enable_rfkill_int(trans); /* If platform's RF_KILL switch is NOT set to KILL */ diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index ae73bd3944e8..dcc7e1256e39 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -915,6 +915,13 @@ static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) } } + if (test_bit(STATUS_FW_ERROR, &trans_pcie->status)) { + IWL_ERR(trans, "FW error in SYNC CMD %s\n", + trans_pcie_get_cmd_string(trans_pcie, cmd->id)); + ret = -EIO; + goto cancel; + } + if (test_bit(STATUS_RFKILL, &trans_pcie->status)) { IWL_DEBUG_RF_KILL(trans, "RFKILL in SYNC CMD... no rsp\n"); ret = -ERFKILL; @@ -954,6 +961,9 @@ int iwl_trans_pcie_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + if (test_bit(STATUS_FW_ERROR, &trans_pcie->status)) + return -EIO; + if (test_bit(STATUS_RFKILL, &trans_pcie->status)) return -ERFKILL;