diff options
author | Emmanuel Grumbach <emmanuel.grumbach@intel.com> | 2012-10-25 11:25:52 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2012-10-31 12:02:06 -0400 |
commit | f946b529502399d09471c5d13845fefbfe8555a6 (patch) | |
tree | 597c826ef9bfb679fd765796fd3d83845c76d1cc /drivers | |
parent | 3e2c159260eed10c44f0dd028c328a40a27ad235 (diff) |
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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/iwlwifi/dvm/main.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/dvm/rx.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans.h | 7 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/internal.h | 13 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/rx.c | 20 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/trans.c | 3 | ||||
-rw-r--r-- | 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) | |||
1926 | * commands by clearing the ready bit */ | 1926 | * commands by clearing the ready bit */ |
1927 | clear_bit(STATUS_READY, &priv->status); | 1927 | clear_bit(STATUS_READY, &priv->status); |
1928 | 1928 | ||
1929 | wake_up(&priv->trans->wait_command_queue); | ||
1930 | |||
1931 | if (!ondemand) { | 1929 | if (!ondemand) { |
1932 | /* | 1930 | /* |
1933 | * If firmware keep reloading, then it indicate something | 1931 | * 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, | |||
631 | test_bit(STATUS_RF_KILL_HW, &priv->status))) | 631 | test_bit(STATUS_RF_KILL_HW, &priv->status))) |
632 | wiphy_rfkill_set_hw_state(priv->hw->wiphy, | 632 | wiphy_rfkill_set_hw_state(priv->hw->wiphy, |
633 | test_bit(STATUS_RF_KILL_HW, &priv->status)); | 633 | test_bit(STATUS_RF_KILL_HW, &priv->status)); |
634 | else | ||
635 | wake_up(&priv->trans->wait_command_queue); | ||
636 | return 0; | 634 | return 0; |
637 | } | 635 | } |
638 | 636 | ||
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; | |||
362 | * @wowlan_suspend: put the device into the correct mode for WoWLAN during | 362 | * @wowlan_suspend: put the device into the correct mode for WoWLAN during |
363 | * suspend. This is optional, if not implemented WoWLAN will not be | 363 | * suspend. This is optional, if not implemented WoWLAN will not be |
364 | * supported. This callback may sleep. | 364 | * supported. This callback may sleep. |
365 | * @send_cmd:send a host command | 365 | * @send_cmd:send a host command. Must return -ERFKILL if RFkill is asserted. |
366 | * If RFkill is asserted in the middle of a SYNC host command, it must | ||
367 | * return -ERFKILL straight away. | ||
366 | * May sleep only if CMD_SYNC is set | 368 | * May sleep only if CMD_SYNC is set |
367 | * @tx: send an skb | 369 | * @tx: send an skb |
368 | * Must be atomic | 370 | * Must be atomic |
@@ -445,7 +447,6 @@ enum iwl_trans_state { | |||
445 | * Set during transport allocation. | 447 | * Set during transport allocation. |
446 | * @hw_id_str: a string with info about HW ID. Set during transport allocation. | 448 | * @hw_id_str: a string with info about HW ID. Set during transport allocation. |
447 | * @pm_support: set to true in start_hw if link pm is supported | 449 | * @pm_support: set to true in start_hw if link pm is supported |
448 | * @wait_command_queue: the wait_queue for SYNC host commands | ||
449 | * @dev_cmd_pool: pool for Tx cmd allocation - for internal use only. | 450 | * @dev_cmd_pool: pool for Tx cmd allocation - for internal use only. |
450 | * The user should use iwl_trans_{alloc,free}_tx_cmd. | 451 | * The user should use iwl_trans_{alloc,free}_tx_cmd. |
451 | * @dev_cmd_headroom: room needed for the transport's private use before the | 452 | * @dev_cmd_headroom: room needed for the transport's private use before the |
@@ -472,8 +473,6 @@ struct iwl_trans { | |||
472 | 473 | ||
473 | bool pm_support; | 474 | bool pm_support; |
474 | 475 | ||
475 | wait_queue_head_t wait_command_queue; | ||
476 | |||
477 | /* The following fields are internal only */ | 476 | /* The following fields are internal only */ |
478 | struct kmem_cache *dev_cmd_pool; | 477 | struct kmem_cache *dev_cmd_pool; |
479 | size_t dev_cmd_headroom; | 478 | 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 { | |||
270 | 270 | ||
271 | bool ucode_write_complete; | 271 | bool ucode_write_complete; |
272 | wait_queue_head_t ucode_write_waitq; | 272 | wait_queue_head_t ucode_write_waitq; |
273 | wait_queue_head_t wait_command_queue; | ||
274 | |||
273 | unsigned long status; | 275 | unsigned long status; |
274 | u8 cmd_queue; | 276 | u8 cmd_queue; |
275 | u8 cmd_fifo; | 277 | u8 cmd_fifo; |
@@ -288,10 +290,13 @@ struct iwl_trans_pcie { | |||
288 | /***************************************************** | 290 | /***************************************************** |
289 | * DRIVER STATUS FUNCTIONS | 291 | * DRIVER STATUS FUNCTIONS |
290 | ******************************************************/ | 292 | ******************************************************/ |
291 | #define STATUS_HCMD_ACTIVE 0 | 293 | enum { |
292 | #define STATUS_DEVICE_ENABLED 1 | 294 | STATUS_HCMD_ACTIVE, |
293 | #define STATUS_TPOWER_PMI 2 | 295 | STATUS_DEVICE_ENABLED, |
294 | #define STATUS_INT_ENABLED 3 | 296 | STATUS_TPOWER_PMI, |
297 | STATUS_INT_ENABLED, | ||
298 | STATUS_RFKILL, | ||
299 | }; | ||
295 | 300 | ||
296 | #define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \ | 301 | #define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \ |
297 | ((struct iwl_trans_pcie *) ((_iwl_trans)->trans_specific)) | 302 | ((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) | |||
568 | */ | 568 | */ |
569 | static void iwl_irq_handle_error(struct iwl_trans *trans) | 569 | static void iwl_irq_handle_error(struct iwl_trans *trans) |
570 | { | 570 | { |
571 | struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); | ||
572 | |||
571 | /* W/A for WiFi/WiMAX coex and WiMAX own the RF */ | 573 | /* W/A for WiFi/WiMAX coex and WiMAX own the RF */ |
572 | if (trans->cfg->internal_wimax_coex && | 574 | if (trans->cfg->internal_wimax_coex && |
573 | (!(iwl_read_prph(trans, APMG_CLK_CTRL_REG) & | 575 | (!(iwl_read_prph(trans, APMG_CLK_CTRL_REG) & |
574 | APMS_CLK_VAL_MRB_FUNC_MODE) || | 576 | APMS_CLK_VAL_MRB_FUNC_MODE) || |
575 | (iwl_read_prph(trans, APMG_PS_CTRL_REG) & | 577 | (iwl_read_prph(trans, APMG_PS_CTRL_REG) & |
576 | APMG_PS_CTRL_VAL_RESET_REQ))) { | 578 | APMG_PS_CTRL_VAL_RESET_REQ))) { |
577 | struct iwl_trans_pcie *trans_pcie = | ||
578 | IWL_TRANS_GET_PCIE_TRANS(trans); | ||
579 | |||
580 | clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); | 579 | clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); |
581 | iwl_op_mode_wimax_active(trans->op_mode); | 580 | iwl_op_mode_wimax_active(trans->op_mode); |
582 | wake_up(&trans->wait_command_queue); | 581 | wake_up(&trans_pcie->wait_command_queue); |
583 | return; | 582 | return; |
584 | } | 583 | } |
585 | 584 | ||
586 | iwl_dump_csr(trans); | 585 | iwl_dump_csr(trans); |
587 | iwl_dump_fh(trans, NULL); | 586 | iwl_dump_fh(trans, NULL); |
588 | 587 | ||
588 | clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); | ||
589 | wake_up(&trans_pcie->wait_command_queue); | ||
590 | |||
589 | iwl_op_mode_nic_error(trans->op_mode); | 591 | iwl_op_mode_nic_error(trans->op_mode); |
590 | } | 592 | } |
591 | 593 | ||
@@ -679,6 +681,16 @@ void iwl_irq_tasklet(struct iwl_trans *trans) | |||
679 | isr_stats->rfkill++; | 681 | isr_stats->rfkill++; |
680 | 682 | ||
681 | iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); | 683 | iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); |
684 | if (hw_rfkill) { | ||
685 | set_bit(STATUS_RFKILL, &trans_pcie->status); | ||
686 | if (test_and_clear_bit(STATUS_HCMD_ACTIVE, | ||
687 | &trans_pcie->status)) | ||
688 | IWL_DEBUG_RF_KILL(trans, | ||
689 | "Rfkill while SYNC HCMD in flight\n"); | ||
690 | wake_up(&trans_pcie->wait_command_queue); | ||
691 | } else { | ||
692 | clear_bit(STATUS_RFKILL, &trans_pcie->status); | ||
693 | } | ||
682 | 694 | ||
683 | handled |= CSR_INT_BIT_RF_KILL; | 695 | handled |= CSR_INT_BIT_RF_KILL; |
684 | } | 696 | } |
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) | |||
1246 | clear_bit(STATUS_INT_ENABLED, &trans_pcie->status); | 1246 | clear_bit(STATUS_INT_ENABLED, &trans_pcie->status); |
1247 | clear_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status); | 1247 | clear_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status); |
1248 | clear_bit(STATUS_TPOWER_PMI, &trans_pcie->status); | 1248 | clear_bit(STATUS_TPOWER_PMI, &trans_pcie->status); |
1249 | clear_bit(STATUS_RFKILL, &trans_pcie->status); | ||
1249 | } | 1250 | } |
1250 | 1251 | ||
1251 | static void iwl_trans_pcie_wowlan_suspend(struct iwl_trans *trans) | 1252 | 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 | } | 2207 | } |
2207 | 2208 | ||
2208 | /* Initialize the wait queue for commands */ | 2209 | /* Initialize the wait queue for commands */ |
2209 | init_waitqueue_head(&trans->wait_command_queue); | 2210 | init_waitqueue_head(&trans_pcie->wait_command_queue); |
2210 | spin_lock_init(&trans->reg_lock); | 2211 | spin_lock_init(&trans->reg_lock); |
2211 | 2212 | ||
2212 | snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name), | 2213 | 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, | |||
827 | IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", | 827 | IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", |
828 | trans_pcie_get_cmd_string(trans_pcie, | 828 | trans_pcie_get_cmd_string(trans_pcie, |
829 | cmd->hdr.cmd)); | 829 | cmd->hdr.cmd)); |
830 | wake_up(&trans->wait_command_queue); | 830 | wake_up(&trans_pcie->wait_command_queue); |
831 | } | 831 | } |
832 | 832 | ||
833 | meta->flags = 0; | 833 | meta->flags = 0; |
@@ -886,7 +886,7 @@ static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) | |||
886 | return ret; | 886 | return ret; |
887 | } | 887 | } |
888 | 888 | ||
889 | ret = wait_event_timeout(trans->wait_command_queue, | 889 | ret = wait_event_timeout(trans_pcie->wait_command_queue, |
890 | !test_bit(STATUS_HCMD_ACTIVE, | 890 | !test_bit(STATUS_HCMD_ACTIVE, |
891 | &trans_pcie->status), | 891 | &trans_pcie->status), |
892 | HOST_COMPLETE_TIMEOUT); | 892 | HOST_COMPLETE_TIMEOUT); |
@@ -915,6 +915,12 @@ static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) | |||
915 | } | 915 | } |
916 | } | 916 | } |
917 | 917 | ||
918 | if (test_bit(STATUS_RFKILL, &trans_pcie->status)) { | ||
919 | IWL_DEBUG_RF_KILL(trans, "RFKILL in SYNC CMD... no rsp\n"); | ||
920 | ret = -ERFKILL; | ||
921 | goto cancel; | ||
922 | } | ||
923 | |||
918 | if ((cmd->flags & CMD_WANT_SKB) && !cmd->resp_pkt) { | 924 | if ((cmd->flags & CMD_WANT_SKB) && !cmd->resp_pkt) { |
919 | IWL_ERR(trans, "Error: Response NULL in '%s'\n", | 925 | IWL_ERR(trans, "Error: Response NULL in '%s'\n", |
920 | trans_pcie_get_cmd_string(trans_pcie, cmd->id)); | 926 | trans_pcie_get_cmd_string(trans_pcie, cmd->id)); |
@@ -946,9 +952,15 @@ cancel: | |||
946 | 952 | ||
947 | int iwl_trans_pcie_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) | 953 | int iwl_trans_pcie_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) |
948 | { | 954 | { |
955 | struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); | ||
956 | |||
957 | if (test_bit(STATUS_RFKILL, &trans_pcie->status)) | ||
958 | return -ERFKILL; | ||
959 | |||
949 | if (cmd->flags & CMD_ASYNC) | 960 | if (cmd->flags & CMD_ASYNC) |
950 | return iwl_send_cmd_async(trans, cmd); | 961 | return iwl_send_cmd_async(trans, cmd); |
951 | 962 | ||
963 | /* We still can fail on RFKILL that can be asserted while we wait */ | ||
952 | return iwl_send_cmd_sync(trans, cmd); | 964 | return iwl_send_cmd_sync(trans, cmd); |
953 | } | 965 | } |
954 | 966 | ||