|
@@ -1006,7 +1006,7 @@ static int iwl_mvm_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm,
|
|
|
}
|
|
|
|
|
|
static void iwl_mvm_mac_ctxt_set_tim(struct iwl_mvm *mvm,
|
|
|
- struct iwl_mac_beacon_cmd *beacon_cmd,
|
|
|
+ struct iwl_mac_beacon_cmd_v6 *beacon_cmd,
|
|
|
u8 *beacon, u32 frame_size)
|
|
|
{
|
|
|
u32 tim_idx;
|
|
@@ -1030,6 +1030,23 @@ static void iwl_mvm_mac_ctxt_set_tim(struct iwl_mvm *mvm,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static u32 iwl_mvm_find_ie_offset(u8 *beacon, u8 eid, u32 frame_size)
|
|
|
+{
|
|
|
+ struct ieee80211_mgmt *mgmt = (void *)beacon;
|
|
|
+ const u8 *ie;
|
|
|
+
|
|
|
+ if (WARN_ON_ONCE(frame_size <= (mgmt->u.beacon.variable - beacon)))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ frame_size -= mgmt->u.beacon.variable - beacon;
|
|
|
+
|
|
|
+ ie = cfg80211_find_ie(eid, mgmt->u.beacon.variable, frame_size);
|
|
|
+ if (!ie)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ return ie - beacon;
|
|
|
+}
|
|
|
+
|
|
|
static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
|
|
|
struct ieee80211_vif *vif,
|
|
|
struct sk_buff *beacon)
|
|
@@ -1039,7 +1056,10 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
|
|
|
.id = BEACON_TEMPLATE_CMD,
|
|
|
.flags = CMD_ASYNC,
|
|
|
};
|
|
|
- struct iwl_mac_beacon_cmd beacon_cmd = {};
|
|
|
+ union {
|
|
|
+ struct iwl_mac_beacon_cmd_v6 beacon_cmd_v6;
|
|
|
+ struct iwl_mac_beacon_cmd beacon_cmd;
|
|
|
+ } u = {};
|
|
|
struct ieee80211_tx_info *info;
|
|
|
u32 beacon_skb_len;
|
|
|
u32 rate, tx_flags;
|
|
@@ -1051,18 +1071,18 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
|
|
|
|
|
|
/* TODO: for now the beacon template id is set to be the mac context id.
|
|
|
* Might be better to handle it as another resource ... */
|
|
|
- beacon_cmd.template_id = cpu_to_le32((u32)mvmvif->id);
|
|
|
+ u.beacon_cmd_v6.template_id = cpu_to_le32((u32)mvmvif->id);
|
|
|
info = IEEE80211_SKB_CB(beacon);
|
|
|
|
|
|
/* Set up TX command fields */
|
|
|
- beacon_cmd.tx.len = cpu_to_le16((u16)beacon_skb_len);
|
|
|
- beacon_cmd.tx.sta_id = mvmvif->bcast_sta.sta_id;
|
|
|
- beacon_cmd.tx.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
|
|
|
+ u.beacon_cmd_v6.tx.len = cpu_to_le16((u16)beacon_skb_len);
|
|
|
+ u.beacon_cmd_v6.tx.sta_id = mvmvif->bcast_sta.sta_id;
|
|
|
+ u.beacon_cmd_v6.tx.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
|
|
|
tx_flags = TX_CMD_FLG_SEQ_CTL | TX_CMD_FLG_TSF;
|
|
|
tx_flags |=
|
|
|
iwl_mvm_bt_coex_tx_prio(mvm, (void *)beacon->data, info, 0) <<
|
|
|
TX_CMD_FLG_BT_PRIO_POS;
|
|
|
- beacon_cmd.tx.tx_flags = cpu_to_le32(tx_flags);
|
|
|
+ u.beacon_cmd_v6.tx.tx_flags = cpu_to_le32(tx_flags);
|
|
|
|
|
|
if (!fw_has_capa(&mvm->fw->ucode_capa,
|
|
|
IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION)) {
|
|
@@ -1071,7 +1091,7 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
|
|
|
mvm->mgmt_last_antenna_idx);
|
|
|
}
|
|
|
|
|
|
- beacon_cmd.tx.rate_n_flags =
|
|
|
+ u.beacon_cmd_v6.tx.rate_n_flags =
|
|
|
cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) <<
|
|
|
RATE_MCS_ANT_POS);
|
|
|
|
|
@@ -1079,20 +1099,37 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
|
|
|
rate = IWL_FIRST_OFDM_RATE;
|
|
|
} else {
|
|
|
rate = IWL_FIRST_CCK_RATE;
|
|
|
- beacon_cmd.tx.rate_n_flags |= cpu_to_le32(RATE_MCS_CCK_MSK);
|
|
|
+ u.beacon_cmd_v6.tx.rate_n_flags |=
|
|
|
+ cpu_to_le32(RATE_MCS_CCK_MSK);
|
|
|
}
|
|
|
- beacon_cmd.tx.rate_n_flags |=
|
|
|
+ u.beacon_cmd_v6.tx.rate_n_flags |=
|
|
|
cpu_to_le32(iwl_mvm_mac80211_idx_to_hwrate(rate));
|
|
|
|
|
|
/* Set up TX beacon command fields */
|
|
|
if (vif->type == NL80211_IFTYPE_AP)
|
|
|
- iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd,
|
|
|
+ iwl_mvm_mac_ctxt_set_tim(mvm, &u.beacon_cmd_v6,
|
|
|
beacon->data,
|
|
|
beacon_skb_len);
|
|
|
|
|
|
/* Submit command */
|
|
|
- cmd.len[0] = sizeof(beacon_cmd);
|
|
|
- cmd.data[0] = &beacon_cmd;
|
|
|
+
|
|
|
+ if (fw_has_capa(&mvm->fw->ucode_capa,
|
|
|
+ IWL_UCODE_TLV_CAPA_CSA_AND_TBTT_OFFLOAD)) {
|
|
|
+ u.beacon_cmd.csa_offset =
|
|
|
+ cpu_to_le32(iwl_mvm_find_ie_offset(beacon->data,
|
|
|
+ WLAN_EID_CHANNEL_SWITCH,
|
|
|
+ beacon_skb_len));
|
|
|
+ u.beacon_cmd.ecsa_offset =
|
|
|
+ cpu_to_le32(iwl_mvm_find_ie_offset(beacon->data,
|
|
|
+ WLAN_EID_EXT_CHANSWITCH_ANN,
|
|
|
+ beacon_skb_len));
|
|
|
+
|
|
|
+ cmd.len[0] = sizeof(u.beacon_cmd);
|
|
|
+ } else {
|
|
|
+ cmd.len[0] = sizeof(u.beacon_cmd_v6);
|
|
|
+ }
|
|
|
+
|
|
|
+ cmd.data[0] = &u;
|
|
|
cmd.dataflags[0] = 0;
|
|
|
cmd.len[1] = beacon_skb_len;
|
|
|
cmd.data[1] = beacon->data;
|
|
@@ -1538,3 +1575,48 @@ void iwl_mvm_rx_stored_beacon_notif(struct iwl_mvm *mvm,
|
|
|
/* pass it as regular rx to mac80211 */
|
|
|
ieee80211_rx_napi(mvm->hw, NULL, skb, NULL);
|
|
|
}
|
|
|
+
|
|
|
+void iwl_mvm_channel_switch_noa_notif(struct iwl_mvm *mvm,
|
|
|
+ struct iwl_rx_cmd_buffer *rxb)
|
|
|
+{
|
|
|
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
|
|
|
+ struct iwl_channel_switch_noa_notif *notif = (void *)pkt->data;
|
|
|
+ struct ieee80211_vif *csa_vif;
|
|
|
+ struct iwl_mvm_vif *mvmvif;
|
|
|
+ int len = iwl_rx_packet_payload_len(pkt);
|
|
|
+ u32 id_n_color;
|
|
|
+
|
|
|
+ if (WARN_ON_ONCE(len < sizeof(*notif)))
|
|
|
+ return;
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+
|
|
|
+ csa_vif = rcu_dereference(mvm->csa_vif);
|
|
|
+ if (WARN_ON(!csa_vif || !csa_vif->csa_active))
|
|
|
+ goto out_unlock;
|
|
|
+
|
|
|
+ id_n_color = le32_to_cpu(notif->id_and_color);
|
|
|
+
|
|
|
+ mvmvif = iwl_mvm_vif_from_mac80211(csa_vif);
|
|
|
+ if (WARN(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color) != id_n_color,
|
|
|
+ "channel switch noa notification on unexpected vif (csa_vif=%d, notif=%d)",
|
|
|
+ FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color), id_n_color))
|
|
|
+ goto out_unlock;
|
|
|
+
|
|
|
+ IWL_DEBUG_INFO(mvm, "Channel Switch Started Notification\n");
|
|
|
+
|
|
|
+ queue_delayed_work(system_wq, &mvm->cs_tx_unblock_dwork,
|
|
|
+ msecs_to_jiffies(IWL_MVM_CS_UNBLOCK_TX_TIMEOUT *
|
|
|
+ csa_vif->bss_conf.beacon_int));
|
|
|
+
|
|
|
+ ieee80211_csa_finish(csa_vif);
|
|
|
+
|
|
|
+ rcu_read_unlock();
|
|
|
+
|
|
|
+ RCU_INIT_POINTER(mvm->csa_vif, NULL);
|
|
|
+
|
|
|
+ return;
|
|
|
+
|
|
|
+out_unlock:
|
|
|
+ rcu_read_unlock();
|
|
|
+}
|