iwlwifi: handle RFKILL logic in the transport layer
authorEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Thu, 25 Oct 2012 15:25:52 +0000 (17:25 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Wed, 31 Oct 2012 16:02:06 +0000 (17:02 +0100)
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>
drivers/net/wireless/iwlwifi/dvm/main.c
drivers/net/wireless/iwlwifi/dvm/rx.c
drivers/net/wireless/iwlwifi/iwl-trans.h
drivers/net/wireless/iwlwifi/pcie/internal.h
drivers/net/wireless/iwlwifi/pcie/rx.c
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/iwlwifi/pcie/tx.c

index 475df45c8320b370010b5f70d4af0c3280ef102d..2b50d11720d42d54d7f256ec0f9c8ac8bcd4922d 100644 (file)
@@ -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
index 5a9c325804f6dcdc8d47d44faf15dec1051d4471..9a8d5020774eedab9d07a930e81d1d7ee8341cd2 100644 (file)
@@ -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;
 }
 
index 47bbe399c06811f04971204dc86700499c28d0dd..b065d48de4647966f6846903a72f57e7ae319d34 100644 (file)
@@ -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;
index ae0f87e0e5850c043f8365840586e97d51d1c63c..847ef1e067bb9b2d53fd995afa5017d861ca0d8d 100644 (file)
@@ -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))
index 3f03f6e322c360f5cd637fcaad32075774a211fe..50c9147278b31c82a34328eb4b6fef8c79258d92 100644 (file)
@@ -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;
        }
index b8a155af12ccfcbe1ca10466288f048ba7804cb2..288d2297f77d2826f5b4948844ff08a90839c617 100644 (file)
@@ -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),
index 9cb30ae5e9a14a69e184d951882f396374a4c437..ae73bd3944e87c3f5272711ab794cfd010f0f863 100644 (file)
@@ -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);
 }