* @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
         * @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)
        __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
  *
 
                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);
        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)
 {
 
                .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);
 
                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
                .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;
 
 
                                 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);
 
        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),
 
 
        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();
        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");
+}
 
 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__ */