iwlwifi: mvm: offload channel switch timing to FW
authorNathan Errera <nathan.errera@intel.com>
Fri, 28 Jan 2022 13:34:21 +0000 (15:34 +0200)
committerLuca Coelho <luciano.coelho@intel.com>
Fri, 18 Feb 2022 08:40:49 +0000 (10:40 +0200)
Since FW is now in charge of timing the channel switch, there is no need
to send the add/modify/remove time event command to fw with every (e)CSA
element.
However, the driver needs to cancel the channel switch if the CS start
notification arrives and it does not know about an ongoing channel switch.

Signed-off-by: Nathan Errera <nathan.errera@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
Link: https://lore.kernel.org/r/iwlwifi.20220128153013.ac3af0ff22c7.Ie87c62047b71b93b12aa80b5dc5391b4798dbe97@changeid
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
drivers/net/wireless/intel/iwlwifi/mvm/ops.c
drivers/net/wireless/intel/iwlwifi/mvm/sta.c
drivers/net/wireless/intel/iwlwifi/mvm/sta.h

index d088c820b1a91401d4cccf3738e3b2b583bff42e..712532f17630875e07b81a5120358691aaf5a111 100644 (file)
@@ -27,6 +27,10 @@ enum iwl_mac_conf_subcmd_ids {
         * @SESSION_PROTECTION_CMD: &struct iwl_mvm_session_prot_cmd
         */
        SESSION_PROTECTION_CMD = 0x5,
+       /**
+        * @CANCEL_CHANNEL_SWITCH_CMD: &struct iwl_cancel_channel_switch_cmd
+        */
+       CANCEL_CHANNEL_SWITCH_CMD = 0x6,
 
        /**
         * @SESSION_PROTECTION_NOTIF: &struct iwl_mvm_session_prot_notif
@@ -42,6 +46,11 @@ enum iwl_mac_conf_subcmd_ids {
         * @CHANNEL_SWITCH_START_NOTIF: &struct iwl_channel_switch_start_notif
         */
        CHANNEL_SWITCH_START_NOTIF = 0xFF,
+
+       /**
+        *@CHANNEL_SWITCH_ERROR_NOTIF: &struct iwl_channel_switch_error_notif
+        */
+       CHANNEL_SWITCH_ERROR_NOTIF = 0xF9,
 };
 
 #define IWL_P2P_NOA_DESC_COUNT (2)
@@ -110,6 +119,31 @@ struct iwl_channel_switch_start_notif {
        __le32 id_and_color;
 } __packed; /* CHANNEL_SWITCH_START_NTFY_API_S_VER_1 */
 
+#define CS_ERR_COUNT_ERROR BIT(0)
+#define CS_ERR_LONG_DELAY_AFTER_CS BIT(1)
+#define CS_ERR_LONG_TX_BLOCK BIT(2)
+#define CS_ERR_TX_BLOCK_TIMER_EXPIRED BIT(3)
+
+/**
+ * struct iwl_channel_switch_error_notif - Channel switch error notification
+ *
+ * @mac_id: the mac for which the ucode sends the notification for
+ * @csa_err_mask: mask of channel switch error that can occur
+ */
+struct iwl_channel_switch_error_notif {
+       __le32 mac_id;
+       __le32 csa_err_mask;
+} __packed; /* CHANNEL_SWITCH_ERROR_NTFY_API_S_VER_1 */
+
+/**
+ * struct iwl_cancel_channel_switch_cmd - Cancel Channel Switch command
+ *
+ * @mac_id: the mac that should cancel the channel switch
+ */
+struct iwl_cancel_channel_switch_cmd {
+       __le32 mac_id;
+} __packed; /* MAC_CANCEL_CHANNEL_SWITCH_S_VER_1 */
+
 /**
  * struct iwl_chan_switch_te_cmd - Channel Switch Time Event command
  *
index fd7d4abfb454b67230c1bb6d111189045a580035..bcce3c1c5feb8e8829de9e1d9948812904f1f22f 100644 (file)
@@ -1602,6 +1602,18 @@ void iwl_mvm_channel_switch_start_notif(struct iwl_mvm *mvm,
                RCU_INIT_POINTER(mvm->csa_vif, NULL);
                return;
        case NL80211_IFTYPE_STATION:
+               /*
+                * if we don't know about an ongoing channel switch,
+                * make sure FW cancels it
+                */
+               if (iwl_fw_lookup_notif_ver(mvm->fw, MAC_CONF_GROUP,
+                                           CHANNEL_SWITCH_ERROR_NOTIF,
+                                           0) && !vif->csa_active) {
+                       IWL_DEBUG_INFO(mvm, "Channel Switch was canceled\n");
+                       iwl_mvm_cancel_channel_switch(mvm, vif, mac_id);
+                       break;
+               }
+
                iwl_mvm_csa_client_absent(mvm, vif);
                cancel_delayed_work(&mvmvif->csa_work);
                ieee80211_chswitch_done(vif, true);
@@ -1615,6 +1627,31 @@ out_unlock:
        rcu_read_unlock();
 }
 
+void iwl_mvm_channel_switch_error_notif(struct iwl_mvm *mvm,
+                                       struct iwl_rx_cmd_buffer *rxb)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_channel_switch_error_notif *notif = (void *)pkt->data;
+       struct ieee80211_vif *vif;
+       u32 id = le32_to_cpu(notif->mac_id);
+       u32 csa_err_mask = le32_to_cpu(notif->csa_err_mask);
+
+       rcu_read_lock();
+       vif = iwl_mvm_rcu_dereference_vif_id(mvm, id, true);
+       if (!vif) {
+               rcu_read_unlock();
+               return;
+       }
+
+       IWL_DEBUG_INFO(mvm, "FW reports CSA error: mac_id=%u, csa_err_mask=%u\n",
+                      id, csa_err_mask);
+       if (csa_err_mask & (CS_ERR_COUNT_ERROR |
+                           CS_ERR_LONG_DELAY_AFTER_CS |
+                           CS_ERR_TX_BLOCK_TIMER_EXPIRED))
+               ieee80211_channel_switch_disconnect(vif, true);
+       rcu_read_unlock();
+}
+
 void iwl_mvm_rx_missed_vap_notif(struct iwl_mvm *mvm,
                                 struct iwl_rx_cmd_buffer *rxb)
 {
index 65f4fe3ef50441ed4ecf70f5eaa95028e1be1114..8ef572bcc2abe596cccdc9cf38f8dcb2973a6f6e 100644 (file)
@@ -1414,6 +1414,15 @@ static void iwl_mvm_abort_channel_switch(struct ieee80211_hw *hw,
                .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
        };
 
+       /*
+        * In the new flow since FW is in charge of the timing,
+        * if driver has canceled the channel switch he will receive the
+        * CHANNEL_SWITCH_START_NOTIF notification from FW and then cancel it
+        */
+       if (iwl_fw_lookup_notif_ver(mvm->fw, MAC_CONF_GROUP,
+                                   CHANNEL_SWITCH_ERROR_NOTIF, 0))
+               return;
+
        IWL_DEBUG_MAC80211(mvm, "Abort CSA on mac %d\n", mvmvif->id);
 
        mutex_lock(&mvm->mutex);
@@ -4846,6 +4855,15 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
 
                break;
        case NL80211_IFTYPE_STATION:
+               /*
+                * In the new flow FW is in charge of timing the switch so there
+                * is no need for all of this
+                */
+               if (iwl_fw_lookup_notif_ver(mvm->fw, MAC_CONF_GROUP,
+                                           CHANNEL_SWITCH_ERROR_NOTIF,
+                                           0))
+                       break;
+
                /*
                 * We haven't configured the firmware to be associated yet since
                 * we don't know the dtim period. In this case, the firmware can't
@@ -4917,6 +4935,14 @@ static void iwl_mvm_channel_switch_rx_beacon(struct ieee80211_hw *hw,
                .cs_mode = chsw->block_tx,
        };
 
+       /*
+        * In the new flow FW is in charge of timing the switch so there is no
+        * need for all of this
+        */
+       if (iwl_fw_lookup_notif_ver(mvm->fw, MAC_CONF_GROUP,
+                                   CHANNEL_SWITCH_ERROR_NOTIF, 0))
+               return;
+
        if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_CS_MODIFY))
                return;
 
index 1dcbb0eb63c3786216abe5276844a3da51e0f763..8a0e17adcb56fc9cd9af844ed1c411d387f06d2f 100644 (file)
@@ -1684,6 +1684,8 @@ void iwl_mvm_rx_missed_vap_notif(struct iwl_mvm *mvm,
                                 struct iwl_rx_cmd_buffer *rxb);
 void iwl_mvm_channel_switch_start_notif(struct iwl_mvm *mvm,
                                        struct iwl_rx_cmd_buffer *rxb);
+void iwl_mvm_channel_switch_error_notif(struct iwl_mvm *mvm,
+                                       struct iwl_rx_cmd_buffer *rxb);
 /* Bindings */
 int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
index 5da9d98043fd51235e3184ce9fc422ab426e8566..f8be52e7e5e7ec0ee916beafbb90b336c0c1e5a2 100644 (file)
@@ -382,6 +382,10 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
        RX_HANDLER_GRP(MAC_CONF_GROUP, CHANNEL_SWITCH_START_NOTIF,
                       iwl_mvm_channel_switch_start_notif,
                       RX_HANDLER_SYNC, struct iwl_channel_switch_start_notif),
+       RX_HANDLER_GRP(MAC_CONF_GROUP, CHANNEL_SWITCH_ERROR_NOTIF,
+                      iwl_mvm_channel_switch_error_notif,
+                      RX_HANDLER_ASYNC_UNLOCKED,
+                      struct iwl_channel_switch_error_notif),
        RX_HANDLER_GRP(DATA_PATH_GROUP, MONITOR_NOTIF,
                       iwl_mvm_rx_monitor_notif, RX_HANDLER_ASYNC_LOCKED,
                       struct iwl_datapath_monitor_notif),
index da40fd043c0eaf9d7c436ed9369254f7c21ba20e..d605a3b51a256f06e0fae64e1d2bec53892cd21a 100644 (file)
@@ -3940,7 +3940,7 @@ void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 
        mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, mvmvif->ap_sta_id);
 
-       if (!WARN_ON(!mvmsta))
+       if (mvmsta)
                iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, true);
 
        rcu_read_unlock();
@@ -3999,3 +3999,21 @@ out:
        iwl_mvm_dealloc_int_sta(mvm, sta);
        return ret;
 }
+
+void iwl_mvm_cancel_channel_switch(struct iwl_mvm *mvm,
+                                  struct ieee80211_vif *vif,
+                                  u32 mac_id)
+{
+       struct iwl_cancel_channel_switch_cmd cancel_channel_switch_cmd = {
+               .mac_id = cpu_to_le32(mac_id),
+       };
+       int ret;
+
+       ret = iwl_mvm_send_cmd_pdu(mvm,
+                                  iwl_cmd_id(CANCEL_CHANNEL_SWITCH_CMD, MAC_CONF_GROUP, 0),
+                                  CMD_ASYNC,
+                                  sizeof(cancel_channel_switch_cmd),
+                                  &cancel_channel_switch_cmd);
+       if (ret)
+               IWL_ERR(mvm, "Failed to cancel the channel switch\n");
+}
index e34b82b2a288cc3ecf0547171719e1b46375c5a6..f1a4fc3e4038706b46e2b333f94577270b3e926f 100644 (file)
@@ -548,4 +548,7 @@ void iwl_mvm_add_new_dqa_stream_wk(struct work_struct *wk);
 int iwl_mvm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                         struct iwl_mvm_int_sta *sta, u8 *addr, u32 cipher,
                         u8 *key, u32 key_len);
+void iwl_mvm_cancel_channel_switch(struct iwl_mvm *mvm,
+                                  struct ieee80211_vif *vif,
+                                  u32 mac_id);
 #endif /* __sta_h__ */