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 <emmanuel.grumbach@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
3e2c159260
commit
f946b52950
7 changed files with 44 additions and 19 deletions
|
@ -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,
|
||||||
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;
|
||||||
* @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 {
|
||||||
* 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 {
|
||||||
|
|
||||||
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 {
|
||||||
|
|
||||||
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 {
|
||||||
/*****************************************************
|
/*****************************************************
|
||||||
* 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)
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
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)
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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,
|
||||||
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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 @@ cancel:
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue