aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorAndrei Otcheretianski <andrei.otcheretianski@intel.com>2016-02-28 10:12:21 -0500
committerLuca Coelho <luciano.coelho@intel.com>2016-07-01 11:09:45 -0400
commitd3a108a48dc670d539c58d4339d211b914a1e1b5 (patch)
tree860fddc03aa5c5b0076a3e219497abb4470deca6 /drivers/net
parentab2e696bd25c11bf4baf84f285555b069ae2dd30 (diff)
iwlwifi: mvm: Support CSA countdown offloading
Add support CSA countdown offloading. When CSA starts, the driver specifies the offsets to the eCSA and CSA IEs in the beacon template command and the fw performs the countdown. The fw notifies the driver when the channel switch flow should be performed. Beacon sent notifications are not used anymore. Signed-off-by: Andrei Otcheretianski <andrei.otcheretianski@intel.com> Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h27
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h12
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c108
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c10
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mvm.h5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/ops.c26
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rx.c13
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c13
9 files changed, 197 insertions, 21 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h
index 3f5029ee1425..eb18de800c2c 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h
@@ -311,6 +311,9 @@ typedef unsigned int __bitwise__ iwl_ucode_tlv_capa_t;
311 * @IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE: extended DTS measurement 311 * @IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE: extended DTS measurement
312 * @IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS: supports short PM timeouts 312 * @IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS: supports short PM timeouts
313 * @IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT: supports bt-coex Multi-priority LUT 313 * @IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT: supports bt-coex Multi-priority LUT
314 * @IWL_UCODE_TLV_CAPA_CSA_AND_TBTT_OFFLOAD: the firmware supports CSA
315 * countdown offloading. Beacon notifications are not sent to the host.
316 * The fw also offloads TBTT alignment.
314 * @IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION: firmware will decide on what 317 * @IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION: firmware will decide on what
315 * antenna the beacon should be transmitted 318 * antenna the beacon should be transmitted
316 * @IWL_UCODE_TLV_CAPA_BEACON_STORING: firmware will store the latest beacon 319 * @IWL_UCODE_TLV_CAPA_BEACON_STORING: firmware will store the latest beacon
@@ -355,6 +358,7 @@ enum iwl_ucode_tlv_capa {
355 IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS = (__force iwl_ucode_tlv_capa_t)65, 358 IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS = (__force iwl_ucode_tlv_capa_t)65,
356 IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT = (__force iwl_ucode_tlv_capa_t)67, 359 IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT = (__force iwl_ucode_tlv_capa_t)67,
357 IWL_UCODE_TLV_CAPA_MULTI_QUEUE_RX_SUPPORT = (__force iwl_ucode_tlv_capa_t)68, 360 IWL_UCODE_TLV_CAPA_MULTI_QUEUE_RX_SUPPORT = (__force iwl_ucode_tlv_capa_t)68,
361 IWL_UCODE_TLV_CAPA_CSA_AND_TBTT_OFFLOAD = (__force iwl_ucode_tlv_capa_t)70,
358 IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION = (__force iwl_ucode_tlv_capa_t)71, 362 IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION = (__force iwl_ucode_tlv_capa_t)71,
359 IWL_UCODE_TLV_CAPA_BEACON_STORING = (__force iwl_ucode_tlv_capa_t)72, 363 IWL_UCODE_TLV_CAPA_BEACON_STORING = (__force iwl_ucode_tlv_capa_t)72,
360 IWL_UCODE_TLV_CAPA_LAR_SUPPORT_V2 = (__force iwl_ucode_tlv_capa_t)73, 364 IWL_UCODE_TLV_CAPA_LAR_SUPPORT_V2 = (__force iwl_ucode_tlv_capa_t)73,
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h
index dadcccd88255..ee59511323c1 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h
@@ -562,8 +562,8 @@ struct iwl_mvm_ba_notif {
562 u8 reserved1; 562 u8 reserved1;
563} __packed; 563} __packed;
564 564
565/* 565/**
566 * struct iwl_mac_beacon_cmd - beacon template command 566 * struct iwl_mac_beacon_cmd_v6 - beacon template command
567 * @tx: the tx commands associated with the beacon frame 567 * @tx: the tx commands associated with the beacon frame
568 * @template_id: currently equal to the mac context id of the coresponding 568 * @template_id: currently equal to the mac context id of the coresponding
569 * mac. 569 * mac.
@@ -571,13 +571,34 @@ struct iwl_mvm_ba_notif {
571 * @tim_size: the length of the tim IE 571 * @tim_size: the length of the tim IE
572 * @frame: the template of the beacon frame 572 * @frame: the template of the beacon frame
573 */ 573 */
574struct iwl_mac_beacon_cmd_v6 {
575 struct iwl_tx_cmd tx;
576 __le32 template_id;
577 __le32 tim_idx;
578 __le32 tim_size;
579 struct ieee80211_hdr frame[0];
580} __packed; /* BEACON_TEMPLATE_CMD_API_S_VER_6 */
581
582/**
583 * struct iwl_mac_beacon_cmd - beacon template command with offloaded CSA
584 * @tx: the tx commands associated with the beacon frame
585 * @template_id: currently equal to the mac context id of the coresponding
586 * mac.
587 * @tim_idx: the offset of the tim IE in the beacon
588 * @tim_size: the length of the tim IE
589 * @ecsa_offset: offset to the ECSA IE if present
590 * @csa_offset: offset to the CSA IE if present
591 * @frame: the template of the beacon frame
592 */
574struct iwl_mac_beacon_cmd { 593struct iwl_mac_beacon_cmd {
575 struct iwl_tx_cmd tx; 594 struct iwl_tx_cmd tx;
576 __le32 template_id; 595 __le32 template_id;
577 __le32 tim_idx; 596 __le32 tim_idx;
578 __le32 tim_size; 597 __le32 tim_size;
598 __le32 ecsa_offset;
599 __le32 csa_offset;
579 struct ieee80211_hdr frame[0]; 600 struct ieee80211_hdr frame[0];
580} __packed; 601} __packed; /* BEACON_TEMPLATE_CMD_API_S_VER_7 */
581 602
582struct iwl_beacon_notif { 603struct iwl_beacon_notif {
583 struct iwl_mvm_tx_resp beacon_notify_hdr; 604 struct iwl_mvm_tx_resp beacon_notify_hdr;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h
index 41b80ae2d5f8..b06380d8473b 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h
@@ -314,6 +314,7 @@ enum {
314enum iwl_mac_conf_subcmd_ids { 314enum iwl_mac_conf_subcmd_ids {
315 LINK_QUALITY_MEASUREMENT_CMD = 0x1, 315 LINK_QUALITY_MEASUREMENT_CMD = 0x1,
316 LINK_QUALITY_MEASUREMENT_COMPLETE_NOTIF = 0xFE, 316 LINK_QUALITY_MEASUREMENT_COMPLETE_NOTIF = 0xFE,
317 CHANNEL_SWITCH_NOA_NOTIF = 0xFF,
317}; 318};
318 319
319enum iwl_phy_ops_subcmd_ids { 320enum iwl_phy_ops_subcmd_ids {
@@ -732,7 +733,7 @@ enum iwl_time_event_type {
732 733
733 /* P2P GO Events */ 734 /* P2P GO Events */
734 TE_P2P_GO_ASSOC_PROT, 735 TE_P2P_GO_ASSOC_PROT,
735 TE_P2P_GO_REPETITIVE_NOA, 736 TE_P2P_GO_REPETITIVET_NOA,
736 TE_P2P_GO_CT_WINDOW, 737 TE_P2P_GO_CT_WINDOW,
737 738
738 /* WiDi Sync Events */ 739 /* WiDi Sync Events */
@@ -2111,4 +2112,13 @@ struct iwl_link_qual_msrmnt_notif {
2111 __le32 reserved[3]; 2112 __le32 reserved[3];
2112} __packed; /* LQM_MEASUREMENT_COMPLETE_NTF_API_S_VER1 */ 2113} __packed; /* LQM_MEASUREMENT_COMPLETE_NTF_API_S_VER1 */
2113 2114
2115/**
2116 * Channel switch NOA notification
2117 *
2118 * @id_and_color: ID and color of the MAC
2119 */
2120struct iwl_channel_switch_noa_notif {
2121 __le32 id_and_color;
2122} __packed; /* CHANNEL_SWITCH_START_NTFY_API_S_VER_1 */
2123
2114#endif /* __fw_api_h__ */ 2124#endif /* __fw_api_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
index 7aae068c02e5..69c42ce45b8a 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
@@ -1006,7 +1006,7 @@ static int iwl_mvm_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm,
1006} 1006}
1007 1007
1008static void iwl_mvm_mac_ctxt_set_tim(struct iwl_mvm *mvm, 1008static void iwl_mvm_mac_ctxt_set_tim(struct iwl_mvm *mvm,
1009 struct iwl_mac_beacon_cmd *beacon_cmd, 1009 struct iwl_mac_beacon_cmd_v6 *beacon_cmd,
1010 u8 *beacon, u32 frame_size) 1010 u8 *beacon, u32 frame_size)
1011{ 1011{
1012 u32 tim_idx; 1012 u32 tim_idx;
@@ -1030,6 +1030,23 @@ static void iwl_mvm_mac_ctxt_set_tim(struct iwl_mvm *mvm,
1030 } 1030 }
1031} 1031}
1032 1032
1033static u32 iwl_mvm_find_ie_offset(u8 *beacon, u8 eid, u32 frame_size)
1034{
1035 struct ieee80211_mgmt *mgmt = (void *)beacon;
1036 const u8 *ie;
1037
1038 if (WARN_ON_ONCE(frame_size <= (mgmt->u.beacon.variable - beacon)))
1039 return 0;
1040
1041 frame_size -= mgmt->u.beacon.variable - beacon;
1042
1043 ie = cfg80211_find_ie(eid, mgmt->u.beacon.variable, frame_size);
1044 if (!ie)
1045 return 0;
1046
1047 return ie - beacon;
1048}
1049
1033static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, 1050static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
1034 struct ieee80211_vif *vif, 1051 struct ieee80211_vif *vif,
1035 struct sk_buff *beacon) 1052 struct sk_buff *beacon)
@@ -1039,7 +1056,10 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
1039 .id = BEACON_TEMPLATE_CMD, 1056 .id = BEACON_TEMPLATE_CMD,
1040 .flags = CMD_ASYNC, 1057 .flags = CMD_ASYNC,
1041 }; 1058 };
1042 struct iwl_mac_beacon_cmd beacon_cmd = {}; 1059 union {
1060 struct iwl_mac_beacon_cmd_v6 beacon_cmd_v6;
1061 struct iwl_mac_beacon_cmd beacon_cmd;
1062 } u = {};
1043 struct ieee80211_tx_info *info; 1063 struct ieee80211_tx_info *info;
1044 u32 beacon_skb_len; 1064 u32 beacon_skb_len;
1045 u32 rate, tx_flags; 1065 u32 rate, tx_flags;
@@ -1051,18 +1071,18 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
1051 1071
1052 /* TODO: for now the beacon template id is set to be the mac context id. 1072 /* TODO: for now the beacon template id is set to be the mac context id.
1053 * Might be better to handle it as another resource ... */ 1073 * Might be better to handle it as another resource ... */
1054 beacon_cmd.template_id = cpu_to_le32((u32)mvmvif->id); 1074 u.beacon_cmd_v6.template_id = cpu_to_le32((u32)mvmvif->id);
1055 info = IEEE80211_SKB_CB(beacon); 1075 info = IEEE80211_SKB_CB(beacon);
1056 1076
1057 /* Set up TX command fields */ 1077 /* Set up TX command fields */
1058 beacon_cmd.tx.len = cpu_to_le16((u16)beacon_skb_len); 1078 u.beacon_cmd_v6.tx.len = cpu_to_le16((u16)beacon_skb_len);
1059 beacon_cmd.tx.sta_id = mvmvif->bcast_sta.sta_id; 1079 u.beacon_cmd_v6.tx.sta_id = mvmvif->bcast_sta.sta_id;
1060 beacon_cmd.tx.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE); 1080 u.beacon_cmd_v6.tx.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
1061 tx_flags = TX_CMD_FLG_SEQ_CTL | TX_CMD_FLG_TSF; 1081 tx_flags = TX_CMD_FLG_SEQ_CTL | TX_CMD_FLG_TSF;
1062 tx_flags |= 1082 tx_flags |=
1063 iwl_mvm_bt_coex_tx_prio(mvm, (void *)beacon->data, info, 0) << 1083 iwl_mvm_bt_coex_tx_prio(mvm, (void *)beacon->data, info, 0) <<
1064 TX_CMD_FLG_BT_PRIO_POS; 1084 TX_CMD_FLG_BT_PRIO_POS;
1065 beacon_cmd.tx.tx_flags = cpu_to_le32(tx_flags); 1085 u.beacon_cmd_v6.tx.tx_flags = cpu_to_le32(tx_flags);
1066 1086
1067 if (!fw_has_capa(&mvm->fw->ucode_capa, 1087 if (!fw_has_capa(&mvm->fw->ucode_capa,
1068 IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION)) { 1088 IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION)) {
@@ -1071,7 +1091,7 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
1071 mvm->mgmt_last_antenna_idx); 1091 mvm->mgmt_last_antenna_idx);
1072 } 1092 }
1073 1093
1074 beacon_cmd.tx.rate_n_flags = 1094 u.beacon_cmd_v6.tx.rate_n_flags =
1075 cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) << 1095 cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) <<
1076 RATE_MCS_ANT_POS); 1096 RATE_MCS_ANT_POS);
1077 1097
@@ -1079,20 +1099,37 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
1079 rate = IWL_FIRST_OFDM_RATE; 1099 rate = IWL_FIRST_OFDM_RATE;
1080 } else { 1100 } else {
1081 rate = IWL_FIRST_CCK_RATE; 1101 rate = IWL_FIRST_CCK_RATE;
1082 beacon_cmd.tx.rate_n_flags |= cpu_to_le32(RATE_MCS_CCK_MSK); 1102 u.beacon_cmd_v6.tx.rate_n_flags |=
1103 cpu_to_le32(RATE_MCS_CCK_MSK);
1083 } 1104 }
1084 beacon_cmd.tx.rate_n_flags |= 1105 u.beacon_cmd_v6.tx.rate_n_flags |=
1085 cpu_to_le32(iwl_mvm_mac80211_idx_to_hwrate(rate)); 1106 cpu_to_le32(iwl_mvm_mac80211_idx_to_hwrate(rate));
1086 1107
1087 /* Set up TX beacon command fields */ 1108 /* Set up TX beacon command fields */
1088 if (vif->type == NL80211_IFTYPE_AP) 1109 if (vif->type == NL80211_IFTYPE_AP)
1089 iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd, 1110 iwl_mvm_mac_ctxt_set_tim(mvm, &u.beacon_cmd_v6,
1090 beacon->data, 1111 beacon->data,
1091 beacon_skb_len); 1112 beacon_skb_len);
1092 1113
1093 /* Submit command */ 1114 /* Submit command */
1094 cmd.len[0] = sizeof(beacon_cmd); 1115
1095 cmd.data[0] = &beacon_cmd; 1116 if (fw_has_capa(&mvm->fw->ucode_capa,
1117 IWL_UCODE_TLV_CAPA_CSA_AND_TBTT_OFFLOAD)) {
1118 u.beacon_cmd.csa_offset =
1119 cpu_to_le32(iwl_mvm_find_ie_offset(beacon->data,
1120 WLAN_EID_CHANNEL_SWITCH,
1121 beacon_skb_len));
1122 u.beacon_cmd.ecsa_offset =
1123 cpu_to_le32(iwl_mvm_find_ie_offset(beacon->data,
1124 WLAN_EID_EXT_CHANSWITCH_ANN,
1125 beacon_skb_len));
1126
1127 cmd.len[0] = sizeof(u.beacon_cmd);
1128 } else {
1129 cmd.len[0] = sizeof(u.beacon_cmd_v6);
1130 }
1131
1132 cmd.data[0] = &u;
1096 cmd.dataflags[0] = 0; 1133 cmd.dataflags[0] = 0;
1097 cmd.len[1] = beacon_skb_len; 1134 cmd.len[1] = beacon_skb_len;
1098 cmd.data[1] = beacon->data; 1135 cmd.data[1] = beacon->data;
@@ -1538,3 +1575,48 @@ void iwl_mvm_rx_stored_beacon_notif(struct iwl_mvm *mvm,
1538 /* pass it as regular rx to mac80211 */ 1575 /* pass it as regular rx to mac80211 */
1539 ieee80211_rx_napi(mvm->hw, NULL, skb, NULL); 1576 ieee80211_rx_napi(mvm->hw, NULL, skb, NULL);
1540} 1577}
1578
1579void iwl_mvm_channel_switch_noa_notif(struct iwl_mvm *mvm,
1580 struct iwl_rx_cmd_buffer *rxb)
1581{
1582 struct iwl_rx_packet *pkt = rxb_addr(rxb);
1583 struct iwl_channel_switch_noa_notif *notif = (void *)pkt->data;
1584 struct ieee80211_vif *csa_vif;
1585 struct iwl_mvm_vif *mvmvif;
1586 int len = iwl_rx_packet_payload_len(pkt);
1587 u32 id_n_color;
1588
1589 if (WARN_ON_ONCE(len < sizeof(*notif)))
1590 return;
1591
1592 rcu_read_lock();
1593
1594 csa_vif = rcu_dereference(mvm->csa_vif);
1595 if (WARN_ON(!csa_vif || !csa_vif->csa_active))
1596 goto out_unlock;
1597
1598 id_n_color = le32_to_cpu(notif->id_and_color);
1599
1600 mvmvif = iwl_mvm_vif_from_mac80211(csa_vif);
1601 if (WARN(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color) != id_n_color,
1602 "channel switch noa notification on unexpected vif (csa_vif=%d, notif=%d)",
1603 FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color), id_n_color))
1604 goto out_unlock;
1605
1606 IWL_DEBUG_INFO(mvm, "Channel Switch Started Notification\n");
1607
1608 queue_delayed_work(system_wq, &mvm->cs_tx_unblock_dwork,
1609 msecs_to_jiffies(IWL_MVM_CS_UNBLOCK_TX_TIMEOUT *
1610 csa_vif->bss_conf.beacon_int));
1611
1612 ieee80211_csa_finish(csa_vif);
1613
1614 rcu_read_unlock();
1615
1616 RCU_INIT_POINTER(mvm->csa_vif, NULL);
1617
1618 return;
1619
1620out_unlock:
1621 rcu_read_unlock();
1622}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
index b7014bc3fbd4..e5cb7dbcd05c 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
@@ -1199,6 +1199,7 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
1199 flush_work(&mvm->async_handlers_wk); 1199 flush_work(&mvm->async_handlers_wk);
1200 flush_work(&mvm->add_stream_wk); 1200 flush_work(&mvm->add_stream_wk);
1201 cancel_delayed_work_sync(&mvm->fw_dump_wk); 1201 cancel_delayed_work_sync(&mvm->fw_dump_wk);
1202 cancel_delayed_work_sync(&mvm->cs_tx_unblock_dwork);
1202 iwl_mvm_free_fw_dump_desc(mvm); 1203 iwl_mvm_free_fw_dump_desc(mvm);
1203 1204
1204 mutex_lock(&mvm->mutex); 1205 mutex_lock(&mvm->mutex);
@@ -3687,6 +3688,13 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
3687 goto out_unlock; 3688 goto out_unlock;
3688 } 3689 }
3689 3690
3691 /* we still didn't unblock tx. prevent new CS meanwhile */
3692 if (rcu_dereference_protected(mvm->csa_tx_blocked_vif,
3693 lockdep_is_held(&mvm->mutex))) {
3694 ret = -EBUSY;
3695 goto out_unlock;
3696 }
3697
3690 rcu_assign_pointer(mvm->csa_vif, vif); 3698 rcu_assign_pointer(mvm->csa_vif, vif);
3691 3699
3692 if (WARN_ONCE(mvmvif->csa_countdown, 3700 if (WARN_ONCE(mvmvif->csa_countdown,
@@ -3695,6 +3703,8 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
3695 goto out_unlock; 3703 goto out_unlock;
3696 } 3704 }
3697 3705
3706 mvmvif->csa_target_freq = chsw->chandef.chan->center_freq;
3707
3698 break; 3708 break;
3699 case NL80211_IFTYPE_STATION: 3709 case NL80211_IFTYPE_STATION:
3700 if (mvmvif->lqm_active) 3710 if (mvmvif->lqm_active)
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
index 65f9a4b4b3b8..4b75b9226898 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
@@ -452,6 +452,7 @@ struct iwl_mvm_vif {
452 /* Indicates that CSA countdown may be started */ 452 /* Indicates that CSA countdown may be started */
453 bool csa_countdown; 453 bool csa_countdown;
454 bool csa_failed; 454 bool csa_failed;
455 u16 csa_target_freq;
455 456
456 /* TCP Checksum Offload */ 457 /* TCP Checksum Offload */
457 netdev_features_t features; 458 netdev_features_t features;
@@ -1007,6 +1008,8 @@ struct iwl_mvm {
1007 * clients. 1008 * clients.
1008 */ 1009 */
1009 bool drop_bcn_ap_mode; 1010 bool drop_bcn_ap_mode;
1011
1012 struct delayed_work cs_tx_unblock_dwork;
1010}; 1013};
1011 1014
1012/* Extract MVM priv from op_mode and _hw */ 1015/* Extract MVM priv from op_mode and _hw */
@@ -1381,6 +1384,8 @@ void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm,
1381 struct ieee80211_vif *vif); 1384 struct ieee80211_vif *vif);
1382unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm, 1385unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm,
1383 struct ieee80211_vif *exclude_vif); 1386 struct ieee80211_vif *exclude_vif);
1387void iwl_mvm_channel_switch_noa_notif(struct iwl_mvm *mvm,
1388 struct iwl_rx_cmd_buffer *rxb);
1384/* Bindings */ 1389/* Bindings */
1385int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); 1390int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
1386int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); 1391int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
index 55f114d24148..ddc400436782 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
@@ -431,6 +431,7 @@ static const struct iwl_hcmd_names iwl_mvm_system_names[] = {
431static const struct iwl_hcmd_names iwl_mvm_mac_conf_names[] = { 431static const struct iwl_hcmd_names iwl_mvm_mac_conf_names[] = {
432 HCMD_NAME(LINK_QUALITY_MEASUREMENT_CMD), 432 HCMD_NAME(LINK_QUALITY_MEASUREMENT_CMD),
433 HCMD_NAME(LINK_QUALITY_MEASUREMENT_COMPLETE_NOTIF), 433 HCMD_NAME(LINK_QUALITY_MEASUREMENT_COMPLETE_NOTIF),
434 HCMD_NAME(CHANNEL_SWITCH_NOA_NOTIF),
434}; 435};
435 436
436/* Please keep this array *SORTED* by hex value. 437/* Please keep this array *SORTED* by hex value.
@@ -494,6 +495,29 @@ static u32 calc_min_backoff(struct iwl_trans *trans, const struct iwl_cfg *cfg)
494 495
495static void iwl_mvm_fw_error_dump_wk(struct work_struct *work); 496static void iwl_mvm_fw_error_dump_wk(struct work_struct *work);
496 497
498static void iwl_mvm_tx_unblock_dwork(struct work_struct *work)
499{
500 struct iwl_mvm *mvm =
501 container_of(work, struct iwl_mvm, cs_tx_unblock_dwork.work);
502 struct ieee80211_vif *tx_blocked_vif;
503 struct iwl_mvm_vif *mvmvif;
504
505 mutex_lock(&mvm->mutex);
506
507 tx_blocked_vif =
508 rcu_dereference_protected(mvm->csa_tx_blocked_vif,
509 lockdep_is_held(&mvm->mutex));
510
511 if (!tx_blocked_vif)
512 goto unlock;
513
514 mvmvif = iwl_mvm_vif_from_mac80211(tx_blocked_vif);
515 iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, false);
516 RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL);
517unlock:
518 mutex_unlock(&mvm->mutex);
519}
520
497static struct iwl_op_mode * 521static struct iwl_op_mode *
498iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, 522iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
499 const struct iwl_fw *fw, struct dentry *dbgfs_dir) 523 const struct iwl_fw *fw, struct dentry *dbgfs_dir)
@@ -595,6 +619,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
595 619
596 SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev); 620 SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev);
597 621
622 INIT_DELAYED_WORK(&mvm->cs_tx_unblock_dwork, iwl_mvm_tx_unblock_dwork);
623
598 /* 624 /*
599 * Populate the state variables that the transport layer needs 625 * Populate the state variables that the transport layer needs
600 * to know about. 626 * to know about.
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
index ab7f7eda9c13..6d096b6c3d50 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
@@ -354,13 +354,22 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi,
354 354
355 if (sta) { 355 if (sta) {
356 struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 356 struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
357 struct ieee80211_vif *tx_blocked_vif =
358 rcu_dereference(mvm->csa_tx_blocked_vif);
357 359
358 /* We have tx blocked stations (with CS bit). If we heard 360 /* We have tx blocked stations (with CS bit). If we heard
359 * frames from a blocked station on a new channel we can 361 * frames from a blocked station on a new channel we can
360 * TX to it again. 362 * TX to it again.
361 */ 363 */
362 if (unlikely(mvm->csa_tx_block_bcn_timeout)) 364 if (unlikely(tx_blocked_vif) &&
363 iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false); 365 mvmsta->vif == tx_blocked_vif) {
366 struct iwl_mvm_vif *mvmvif =
367 iwl_mvm_vif_from_mac80211(tx_blocked_vif);
368
369 if (mvmvif->csa_target_freq == rx_status->freq)
370 iwl_mvm_sta_modify_disable_tx_ap(mvm, sta,
371 false);
372 }
364 373
365 rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status); 374 rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status);
366 375
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
index 1f4fef4469d1..d13397a17365 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
@@ -817,6 +817,8 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
817 817
818 if (sta) { 818 if (sta) {
819 struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 819 struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
820 struct ieee80211_vif *tx_blocked_vif =
821 rcu_dereference(mvm->csa_tx_blocked_vif);
820 u8 baid = (u8)((le32_to_cpu(desc->reorder_data) & 822 u8 baid = (u8)((le32_to_cpu(desc->reorder_data) &
821 IWL_RX_MPDU_REORDER_BAID_MASK) >> 823 IWL_RX_MPDU_REORDER_BAID_MASK) >>
822 IWL_RX_MPDU_REORDER_BAID_SHIFT); 824 IWL_RX_MPDU_REORDER_BAID_SHIFT);
@@ -826,8 +828,15 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
826 * frames from a blocked station on a new channel we can 828 * frames from a blocked station on a new channel we can
827 * TX to it again. 829 * TX to it again.
828 */ 830 */
829 if (unlikely(mvm->csa_tx_block_bcn_timeout)) 831 if (unlikely(tx_blocked_vif) &&
830 iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false); 832 tx_blocked_vif == mvmsta->vif) {
833 struct iwl_mvm_vif *mvmvif =
834 iwl_mvm_vif_from_mac80211(tx_blocked_vif);
835
836 if (mvmvif->csa_target_freq == rx_status->freq)
837 iwl_mvm_sta_modify_disable_tx_ap(mvm, sta,
838 false);
839 }
831 840
832 rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status); 841 rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status);
833 842