Commit f946b529 authored by Emmanuel Grumbach's avatar Emmanuel Grumbach Committed by Johannes Berg

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: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 3e2c1592
...@@ -1926,8 +1926,6 @@ static void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand) ...@@ -1926,8 +1926,6 @@ static void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand)
* commands by clearing the ready bit */ * commands by clearing the ready bit */
clear_bit(STATUS_READY, &priv->status); clear_bit(STATUS_READY, &priv->status);
wake_up(&priv->trans->wait_command_queue);
if (!ondemand) { if (!ondemand) {
/* /*
* If firmware keep reloading, then it indicate something * If firmware keep reloading, then it indicate something
......
...@@ -631,8 +631,6 @@ static int iwlagn_rx_card_state_notif(struct iwl_priv *priv, ...@@ -631,8 +631,6 @@ static int iwlagn_rx_card_state_notif(struct iwl_priv *priv,
test_bit(STATUS_RF_KILL_HW, &priv->status))) test_bit(STATUS_RF_KILL_HW, &priv->status)))
wiphy_rfkill_set_hw_state(priv->hw->wiphy, wiphy_rfkill_set_hw_state(priv->hw->wiphy,
test_bit(STATUS_RF_KILL_HW, &priv->status)); test_bit(STATUS_RF_KILL_HW, &priv->status));
else
wake_up(&priv->trans->wait_command_queue);
return 0; return 0;
} }
......
...@@ -362,7 +362,9 @@ struct iwl_trans; ...@@ -362,7 +362,9 @@ struct iwl_trans;
* @wowlan_suspend: put the device into the correct mode for WoWLAN during * @wowlan_suspend: put the device into the correct mode for WoWLAN during
* suspend. This is optional, if not implemented WoWLAN will not be * suspend. This is optional, if not implemented WoWLAN will not be
* supported. This callback may sleep. * 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 * May sleep only if CMD_SYNC is set
* @tx: send an skb * @tx: send an skb
* Must be atomic * Must be atomic
...@@ -445,7 +447,6 @@ enum iwl_trans_state { ...@@ -445,7 +447,6 @@ enum iwl_trans_state {
* Set during transport allocation. * Set during transport allocation.
* @hw_id_str: a string with info about HW ID. 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 * @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. * @dev_cmd_pool: pool for Tx cmd allocation - for internal use only.
* The user should use iwl_trans_{alloc,free}_tx_cmd. * The user should use iwl_trans_{alloc,free}_tx_cmd.
* @dev_cmd_headroom: room needed for the transport's private use before the * @dev_cmd_headroom: room needed for the transport's private use before the
...@@ -472,8 +473,6 @@ struct iwl_trans { ...@@ -472,8 +473,6 @@ struct iwl_trans {
bool pm_support; bool pm_support;
wait_queue_head_t wait_command_queue;
/* The following fields are internal only */ /* The following fields are internal only */
struct kmem_cache *dev_cmd_pool; struct kmem_cache *dev_cmd_pool;
size_t dev_cmd_headroom; size_t dev_cmd_headroom;
......
...@@ -270,6 +270,8 @@ struct iwl_trans_pcie { ...@@ -270,6 +270,8 @@ struct iwl_trans_pcie {
bool ucode_write_complete; bool ucode_write_complete;
wait_queue_head_t ucode_write_waitq; wait_queue_head_t ucode_write_waitq;
wait_queue_head_t wait_command_queue;
unsigned long status; unsigned long status;
u8 cmd_queue; u8 cmd_queue;
u8 cmd_fifo; u8 cmd_fifo;
...@@ -288,10 +290,13 @@ struct iwl_trans_pcie { ...@@ -288,10 +290,13 @@ struct iwl_trans_pcie {
/***************************************************** /*****************************************************
* DRIVER STATUS FUNCTIONS * DRIVER STATUS FUNCTIONS
******************************************************/ ******************************************************/
#define STATUS_HCMD_ACTIVE 0 enum {
#define STATUS_DEVICE_ENABLED 1 STATUS_HCMD_ACTIVE,
#define STATUS_TPOWER_PMI 2 STATUS_DEVICE_ENABLED,
#define STATUS_INT_ENABLED 3 STATUS_TPOWER_PMI,
STATUS_INT_ENABLED,
STATUS_RFKILL,
};
#define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \ #define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \
((struct iwl_trans_pcie *) ((_iwl_trans)->trans_specific)) ((struct iwl_trans_pcie *) ((_iwl_trans)->trans_specific))
......
...@@ -568,24 +568,26 @@ static void iwl_rx_handle(struct iwl_trans *trans) ...@@ -568,24 +568,26 @@ static void iwl_rx_handle(struct iwl_trans *trans)
*/ */
static void iwl_irq_handle_error(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 */ /* W/A for WiFi/WiMAX coex and WiMAX own the RF */
if (trans->cfg->internal_wimax_coex && if (trans->cfg->internal_wimax_coex &&
(!(iwl_read_prph(trans, APMG_CLK_CTRL_REG) & (!(iwl_read_prph(trans, APMG_CLK_CTRL_REG) &
APMS_CLK_VAL_MRB_FUNC_MODE) || APMS_CLK_VAL_MRB_FUNC_MODE) ||
(iwl_read_prph(trans, APMG_PS_CTRL_REG) & (iwl_read_prph(trans, APMG_PS_CTRL_REG) &
APMG_PS_CTRL_VAL_RESET_REQ))) { 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); clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status);
iwl_op_mode_wimax_active(trans->op_mode); iwl_op_mode_wimax_active(trans->op_mode);
wake_up(&trans->wait_command_queue); wake_up(&trans_pcie->wait_command_queue);
return; return;
} }
iwl_dump_csr(trans); iwl_dump_csr(trans);
iwl_dump_fh(trans, NULL); 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); iwl_op_mode_nic_error(trans->op_mode);
} }
...@@ -679,6 +681,16 @@ void iwl_irq_tasklet(struct iwl_trans *trans) ...@@ -679,6 +681,16 @@ void iwl_irq_tasklet(struct iwl_trans *trans)
isr_stats->rfkill++; isr_stats->rfkill++;
iwl_op_mode_hw_rf_kill(trans->op_mode, hw_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; handled |= CSR_INT_BIT_RF_KILL;
} }
......
...@@ -1246,6 +1246,7 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) ...@@ -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_INT_ENABLED, &trans_pcie->status);
clear_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status); clear_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status);
clear_bit(STATUS_TPOWER_PMI, &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) 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, ...@@ -2206,7 +2207,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
} }
/* Initialize the wait queue for commands */ /* 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); spin_lock_init(&trans->reg_lock);
snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name), snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name),
......
...@@ -827,7 +827,7 @@ void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_cmd_buffer *rxb, ...@@ -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", IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n",
trans_pcie_get_cmd_string(trans_pcie, trans_pcie_get_cmd_string(trans_pcie,
cmd->hdr.cmd)); cmd->hdr.cmd));
wake_up(&trans->wait_command_queue); wake_up(&trans_pcie->wait_command_queue);
} }
meta->flags = 0; meta->flags = 0;
...@@ -886,7 +886,7 @@ static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) ...@@ -886,7 +886,7 @@ static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
return ret; return ret;
} }
ret = wait_event_timeout(trans->wait_command_queue, ret = wait_event_timeout(trans_pcie->wait_command_queue,
!test_bit(STATUS_HCMD_ACTIVE, !test_bit(STATUS_HCMD_ACTIVE,
&trans_pcie->status), &trans_pcie->status),
HOST_COMPLETE_TIMEOUT); HOST_COMPLETE_TIMEOUT);
...@@ -915,6 +915,12 @@ static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) ...@@ -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) { if ((cmd->flags & CMD_WANT_SKB) && !cmd->resp_pkt) {
IWL_ERR(trans, "Error: Response NULL in '%s'\n", IWL_ERR(trans, "Error: Response NULL in '%s'\n",
trans_pcie_get_cmd_string(trans_pcie, cmd->id)); trans_pcie_get_cmd_string(trans_pcie, cmd->id));
...@@ -946,9 +952,15 @@ static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) ...@@ -946,9 +952,15 @@ static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
int iwl_trans_pcie_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) 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) if (cmd->flags & CMD_ASYNC)
return iwl_send_cmd_async(trans, cmd); 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); return iwl_send_cmd_sync(trans, cmd);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment