mirror of
https://github.com/coolsnowwolf/lede.git
synced 2025-07-19 01:56:59 +08:00
601 lines
17 KiB
Diff
601 lines
17 KiB
Diff
From 9dcf6808b253a72b2c90eed179863bf5fab7d68c Mon Sep 17 00:00:00 2001
|
|
From: Wen Gong <quic_wgong@quicinc.com>
|
|
Date: Tue, 7 Dec 2021 17:23:36 +0200
|
|
Subject: [PATCH] ath11k: add 11d scan offload support
|
|
|
|
Add handler for WMI_11D_NEW_COUNTRY_EVENTID, WMI_11D_SCAN_START_CMDID,
|
|
WMI_11D_SCAN_STOP_CMDID.
|
|
|
|
After vdev create for STATION, send WMI_11D_SCAN_START_CMDID to firmware
|
|
and wait firmware complete it, the scan from mac80211 also need to wait
|
|
the 11d scan finished, and send WMI_11D_SCAN_STOP_CMDID to firmware
|
|
before vdev delete for STATION.
|
|
|
|
Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01230-QCAHSTSWPLZ_V2_TO_X86-1
|
|
|
|
Signed-off-by: Wen Gong <quic_wgong@quicinc.com>
|
|
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
|
|
Link: https://lore.kernel.org/r/20211201071745.17746-4-quic_wgong@quicinc.com
|
|
---
|
|
drivers/net/wireless/ath/ath11k/core.c | 31 +++++
|
|
drivers/net/wireless/ath/ath11k/core.h | 9 ++
|
|
drivers/net/wireless/ath/ath11k/mac.c | 163 ++++++++++++++++++++++++-
|
|
drivers/net/wireless/ath/ath11k/mac.h | 7 ++
|
|
drivers/net/wireless/ath/ath11k/reg.c | 15 +++
|
|
drivers/net/wireless/ath/ath11k/wmi.c | 109 +++++++++++++++++
|
|
drivers/net/wireless/ath/ath11k/wmi.h | 27 ++++
|
|
7 files changed, 360 insertions(+), 1 deletion(-)
|
|
|
|
--- a/drivers/net/wireless/ath/ath11k/core.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/core.c
|
|
@@ -1096,6 +1096,7 @@ void ath11k_core_halt(struct ath11k *ar)
|
|
ath11k_mac_peer_cleanup_all(ar);
|
|
cancel_delayed_work_sync(&ar->scan.timeout);
|
|
cancel_work_sync(&ar->regd_update_work);
|
|
+ cancel_work_sync(&ab->update_11d_work);
|
|
|
|
rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], NULL);
|
|
synchronize_rcu();
|
|
@@ -1103,6 +1104,34 @@ void ath11k_core_halt(struct ath11k *ar)
|
|
idr_init(&ar->txmgmt_idr);
|
|
}
|
|
|
|
+static void ath11k_update_11d(struct work_struct *work)
|
|
+{
|
|
+ struct ath11k_base *ab = container_of(work, struct ath11k_base, update_11d_work);
|
|
+ struct ath11k *ar;
|
|
+ struct ath11k_pdev *pdev;
|
|
+ struct wmi_set_current_country_params set_current_param = {};
|
|
+ int ret, i;
|
|
+
|
|
+ spin_lock_bh(&ab->base_lock);
|
|
+ memcpy(&set_current_param.alpha2, &ab->new_alpha2, 2);
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_WMI, "update 11d new cc %c%c\n",
|
|
+ set_current_param.alpha2[0],
|
|
+ set_current_param.alpha2[1]);
|
|
+
|
|
+ for (i = 0; i < ab->num_radios; i++) {
|
|
+ pdev = &ab->pdevs[i];
|
|
+ ar = pdev->ar;
|
|
+
|
|
+ ret = ath11k_wmi_send_set_current_country_cmd(ar, &set_current_param);
|
|
+ if (ret)
|
|
+ ath11k_warn(ar->ab,
|
|
+ "pdev id %d failed set current country code: %d\n",
|
|
+ i, ret);
|
|
+ }
|
|
+}
|
|
+
|
|
static void ath11k_core_restart(struct work_struct *work)
|
|
{
|
|
struct ath11k_base *ab = container_of(work, struct ath11k_base, restart_work);
|
|
@@ -1272,12 +1301,14 @@ struct ath11k_base *ath11k_core_alloc(st
|
|
|
|
mutex_init(&ab->core_lock);
|
|
spin_lock_init(&ab->base_lock);
|
|
+ mutex_init(&ab->vdev_id_11d_lock);
|
|
|
|
INIT_LIST_HEAD(&ab->peers);
|
|
init_waitqueue_head(&ab->peer_mapping_wq);
|
|
init_waitqueue_head(&ab->wmi_ab.tx_credits_wq);
|
|
init_waitqueue_head(&ab->qmi.cold_boot_waitq);
|
|
INIT_WORK(&ab->restart_work, ath11k_core_restart);
|
|
+ INIT_WORK(&ab->update_11d_work, ath11k_update_11d);
|
|
timer_setup(&ab->rx_replenish_retry, ath11k_ce_rx_replenish_retry, 0);
|
|
init_completion(&ab->htc_suspend);
|
|
init_completion(&ab->wow.wakeup_completed);
|
|
--- a/drivers/net/wireless/ath/ath11k/core.h
|
|
+++ b/drivers/net/wireless/ath/ath11k/core.h
|
|
@@ -589,6 +589,11 @@ struct ath11k {
|
|
#endif
|
|
bool dfs_block_radar_events;
|
|
struct ath11k_thermal thermal;
|
|
+ u32 vdev_id_11d_scan;
|
|
+ struct completion finish_11d_scan;
|
|
+ struct completion finish_11d_ch_list;
|
|
+ bool pending_11d;
|
|
+ bool regdom_set_by_user;
|
|
};
|
|
|
|
struct ath11k_band_cap {
|
|
@@ -762,6 +767,8 @@ struct ath11k_base {
|
|
struct completion driver_recovery;
|
|
struct workqueue_struct *workqueue;
|
|
struct work_struct restart_work;
|
|
+ struct work_struct update_11d_work;
|
|
+ u8 new_alpha2[3];
|
|
struct {
|
|
/* protected by data_lock */
|
|
u32 fw_crash_counter;
|
|
@@ -771,6 +778,8 @@ struct ath11k_base {
|
|
struct ath11k_dbring_cap *db_caps;
|
|
u32 num_db_cap;
|
|
|
|
+ /* To synchronize 11d scan vdev id */
|
|
+ struct mutex vdev_id_11d_lock;
|
|
struct timer_list mon_reap_timer;
|
|
|
|
struct completion htc_suspend;
|
|
--- a/drivers/net/wireless/ath/ath11k/mac.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/mac.c
|
|
@@ -2681,6 +2681,8 @@ static void ath11k_bss_assoc(struct ieee
|
|
if (ret)
|
|
ath11k_warn(ar->ab, "failed to set vdev %i OBSS PD parameters: %d\n",
|
|
arvif->vdev_id, ret);
|
|
+
|
|
+ ath11k_mac_11d_scan_stop_all(ar->ab);
|
|
}
|
|
|
|
static void ath11k_bss_disassoc(struct ieee80211_hw *hw,
|
|
@@ -3410,6 +3412,7 @@ static int ath11k_start_scan(struct ath1
|
|
struct scan_req_params *arg)
|
|
{
|
|
int ret;
|
|
+ unsigned long timeout = 1 * HZ;
|
|
|
|
lockdep_assert_held(&ar->conf_mutex);
|
|
|
|
@@ -3420,7 +3423,14 @@ static int ath11k_start_scan(struct ath1
|
|
if (ret)
|
|
return ret;
|
|
|
|
- ret = wait_for_completion_timeout(&ar->scan.started, 1 * HZ);
|
|
+ if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ar->ab->wmi_ab.svc_map)) {
|
|
+ timeout = 5 * HZ;
|
|
+
|
|
+ if (ar->supports_6ghz)
|
|
+ timeout += 5 * HZ;
|
|
+ }
|
|
+
|
|
+ ret = wait_for_completion_timeout(&ar->scan.started, timeout);
|
|
if (ret == 0) {
|
|
ret = ath11k_scan_stop(ar);
|
|
if (ret)
|
|
@@ -3477,6 +3487,26 @@ static int ath11k_mac_op_hw_scan(struct
|
|
if (ret)
|
|
goto exit;
|
|
|
|
+ /* Currently the pending_11d=true only happened 1 time while
|
|
+ * wlan interface up in ath11k_mac_11d_scan_start(), it is called by
|
|
+ * ath11k_mac_op_add_interface(), after wlan interface up,
|
|
+ * pending_11d=false always.
|
|
+ * If remove below wait, it always happened scan fail and lead connect
|
|
+ * fail while wlan interface up, because it has a 11d scan which is running
|
|
+ * in firmware, and lead this scan failed.
|
|
+ */
|
|
+ if (ar->pending_11d) {
|
|
+ long time_left;
|
|
+ unsigned long timeout = 5 * HZ;
|
|
+
|
|
+ if (ar->supports_6ghz)
|
|
+ timeout += 5 * HZ;
|
|
+
|
|
+ time_left = wait_for_completion_timeout(&ar->finish_11d_ch_list, timeout);
|
|
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC,
|
|
+ "mac wait 11d channel list time left %ld\n", time_left);
|
|
+ }
|
|
+
|
|
memset(&arg, 0, sizeof(arg));
|
|
ath11k_wmi_start_scan_init(ar, &arg);
|
|
arg.vdev_id = arvif->vdev_id;
|
|
@@ -5638,6 +5668,7 @@ static void ath11k_mac_op_stop(struct ie
|
|
|
|
cancel_delayed_work_sync(&ar->scan.timeout);
|
|
cancel_work_sync(&ar->regd_update_work);
|
|
+ cancel_work_sync(&ar->ab->update_11d_work);
|
|
|
|
spin_lock_bh(&ar->data_lock);
|
|
list_for_each_entry_safe(ppdu_stats, tmp, &ar->ppdu_stats_info, list) {
|
|
@@ -5791,6 +5822,122 @@ static void ath11k_mac_op_update_vif_off
|
|
}
|
|
}
|
|
|
|
+static bool ath11k_mac_vif_ap_active_any(struct ath11k_base *ab)
|
|
+{
|
|
+ struct ath11k *ar;
|
|
+ struct ath11k_pdev *pdev;
|
|
+ struct ath11k_vif *arvif;
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < ab->num_radios; i++) {
|
|
+ pdev = &ab->pdevs[i];
|
|
+ ar = pdev->ar;
|
|
+ list_for_each_entry(arvif, &ar->arvifs, list) {
|
|
+ if (arvif->is_up && arvif->vdev_type == WMI_VDEV_TYPE_AP)
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ return false;
|
|
+}
|
|
+
|
|
+void ath11k_mac_11d_scan_start(struct ath11k *ar, u32 vdev_id, bool wait)
|
|
+{
|
|
+ struct wmi_11d_scan_start_params param;
|
|
+ int ret;
|
|
+
|
|
+ mutex_lock(&ar->ab->vdev_id_11d_lock);
|
|
+
|
|
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac vdev id for 11d scan %d\n",
|
|
+ ar->vdev_id_11d_scan);
|
|
+
|
|
+ if (ar->regdom_set_by_user)
|
|
+ goto fin;
|
|
+
|
|
+ if (ar->vdev_id_11d_scan != ATH11K_11D_INVALID_VDEV_ID)
|
|
+ goto fin;
|
|
+
|
|
+ if (!test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ar->ab->wmi_ab.svc_map))
|
|
+ goto fin;
|
|
+
|
|
+ if (ath11k_mac_vif_ap_active_any(ar->ab))
|
|
+ goto fin;
|
|
+
|
|
+ param.vdev_id = vdev_id;
|
|
+ param.start_interval_msec = 0;
|
|
+ param.scan_period_msec = ATH11K_SCAN_11D_INTERVAL;
|
|
+
|
|
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac start 11d scan\n");
|
|
+
|
|
+ if (wait)
|
|
+ reinit_completion(&ar->finish_11d_scan);
|
|
+
|
|
+ ret = ath11k_wmi_send_11d_scan_start_cmd(ar, ¶m);
|
|
+ if (ret) {
|
|
+ ath11k_warn(ar->ab, "failed to start 11d scan vdev %d ret: %d\n",
|
|
+ vdev_id, ret);
|
|
+ } else {
|
|
+ ar->vdev_id_11d_scan = vdev_id;
|
|
+ if (wait) {
|
|
+ ar->pending_11d = true;
|
|
+ ret = wait_for_completion_timeout(&ar->finish_11d_scan,
|
|
+ 5 * HZ);
|
|
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC,
|
|
+ "mac 11d scan left time %d\n", ret);
|
|
+
|
|
+ if (!ret)
|
|
+ ar->pending_11d = false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+fin:
|
|
+ mutex_unlock(&ar->ab->vdev_id_11d_lock);
|
|
+}
|
|
+
|
|
+void ath11k_mac_11d_scan_stop(struct ath11k *ar)
|
|
+{
|
|
+ int ret;
|
|
+ u32 vdev_id;
|
|
+
|
|
+ if (!test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ar->ab->wmi_ab.svc_map))
|
|
+ return;
|
|
+
|
|
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac stop 11d scan\n");
|
|
+
|
|
+ mutex_lock(&ar->ab->vdev_id_11d_lock);
|
|
+
|
|
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac stop 11d vdev id %d\n",
|
|
+ ar->vdev_id_11d_scan);
|
|
+
|
|
+ if (ar->vdev_id_11d_scan != ATH11K_11D_INVALID_VDEV_ID) {
|
|
+ vdev_id = ar->vdev_id_11d_scan;
|
|
+
|
|
+ ret = ath11k_wmi_send_11d_scan_stop_cmd(ar, vdev_id);
|
|
+ if (ret)
|
|
+ ath11k_warn(ar->ab,
|
|
+ "failed to stopt 11d scan vdev %d ret: %d\n",
|
|
+ vdev_id, ret);
|
|
+ else
|
|
+ ar->vdev_id_11d_scan = ATH11K_11D_INVALID_VDEV_ID;
|
|
+ }
|
|
+ mutex_unlock(&ar->ab->vdev_id_11d_lock);
|
|
+}
|
|
+
|
|
+void ath11k_mac_11d_scan_stop_all(struct ath11k_base *ab)
|
|
+{
|
|
+ struct ath11k *ar;
|
|
+ struct ath11k_pdev *pdev;
|
|
+ int i;
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_MAC, "mac stop soc 11d scan\n");
|
|
+
|
|
+ for (i = 0; i < ab->num_radios; i++) {
|
|
+ pdev = &ab->pdevs[i];
|
|
+ ar = pdev->ar;
|
|
+
|
|
+ ath11k_mac_11d_scan_stop(ar);
|
|
+ }
|
|
+}
|
|
+
|
|
static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw,
|
|
struct ieee80211_vif *vif)
|
|
{
|
|
@@ -5924,6 +6071,8 @@ static int ath11k_mac_op_add_interface(s
|
|
arvif->vdev_id, ret);
|
|
goto err_peer_del;
|
|
}
|
|
+
|
|
+ ath11k_mac_11d_scan_stop_all(ar->ab);
|
|
break;
|
|
case WMI_VDEV_TYPE_STA:
|
|
param_id = WMI_STA_PS_PARAM_RX_WAKE_POLICY;
|
|
@@ -5963,6 +6112,9 @@ static int ath11k_mac_op_add_interface(s
|
|
arvif->vdev_id, ret);
|
|
goto err_peer_del;
|
|
}
|
|
+
|
|
+ ath11k_mac_11d_scan_start(ar, arvif->vdev_id, true);
|
|
+
|
|
break;
|
|
case WMI_VDEV_TYPE_MONITOR:
|
|
set_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags);
|
|
@@ -6064,6 +6216,9 @@ static void ath11k_mac_op_remove_interfa
|
|
ath11k_dbg(ab, ATH11K_DBG_MAC, "mac remove interface (vdev %d)\n",
|
|
arvif->vdev_id);
|
|
|
|
+ if (arvif->vdev_type == WMI_VDEV_TYPE_STA)
|
|
+ ath11k_mac_11d_scan_stop(ar);
|
|
+
|
|
if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
|
|
ret = ath11k_peer_delete(ar, arvif->vdev_id, vif->addr);
|
|
if (ret)
|
|
@@ -6782,6 +6937,9 @@ ath11k_mac_op_unassign_vif_chanctx(struc
|
|
ret);
|
|
}
|
|
|
|
+ if (arvif->vdev_type == WMI_VDEV_TYPE_STA)
|
|
+ ath11k_mac_11d_scan_start(ar, arvif->vdev_id, false);
|
|
+
|
|
mutex_unlock(&ar->conf_mutex);
|
|
}
|
|
|
|
@@ -8183,6 +8341,9 @@ int ath11k_mac_allocate(struct ath11k_ba
|
|
|
|
ar->monitor_vdev_id = -1;
|
|
clear_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags);
|
|
+ ar->vdev_id_11d_scan = ATH11K_11D_INVALID_VDEV_ID;
|
|
+ init_completion(&ar->finish_11d_scan);
|
|
+ init_completion(&ar->finish_11d_ch_list);
|
|
}
|
|
|
|
return 0;
|
|
--- a/drivers/net/wireless/ath/ath11k/mac.h
|
|
+++ b/drivers/net/wireless/ath/ath11k/mac.h
|
|
@@ -127,6 +127,13 @@ struct ath11k_generic_iter {
|
|
|
|
extern const struct htt_rx_ring_tlv_filter ath11k_mac_mon_status_filter_default;
|
|
|
|
+#define ATH11K_SCAN_11D_INTERVAL 600000
|
|
+#define ATH11K_11D_INVALID_VDEV_ID 0xFFFF
|
|
+
|
|
+void ath11k_mac_11d_scan_start(struct ath11k *ar, u32 vdev_id, bool wait);
|
|
+void ath11k_mac_11d_scan_stop(struct ath11k *ar);
|
|
+void ath11k_mac_11d_scan_stop_all(struct ath11k_base *ab);
|
|
+
|
|
void ath11k_mac_destroy(struct ath11k_base *ab);
|
|
void ath11k_mac_unregister(struct ath11k_base *ab);
|
|
int ath11k_mac_register(struct ath11k_base *ab);
|
|
--- a/drivers/net/wireless/ath/ath11k/reg.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/reg.c
|
|
@@ -86,6 +86,9 @@ ath11k_reg_notifier(struct wiphy *wiphy,
|
|
if (ret)
|
|
ath11k_warn(ar->ab,
|
|
"INIT Country code set to fw failed : %d\n", ret);
|
|
+
|
|
+ ath11k_mac_11d_scan_stop(ar);
|
|
+ ar->regdom_set_by_user = true;
|
|
}
|
|
|
|
int ath11k_reg_update_chan_list(struct ath11k *ar)
|
|
@@ -179,6 +182,11 @@ int ath11k_reg_update_chan_list(struct a
|
|
ret = ath11k_wmi_send_scan_chan_list_cmd(ar, params);
|
|
kfree(params);
|
|
|
|
+ if (ar->pending_11d) {
|
|
+ complete(&ar->finish_11d_ch_list);
|
|
+ ar->pending_11d = false;
|
|
+ }
|
|
+
|
|
return ret;
|
|
}
|
|
|
|
@@ -244,8 +252,15 @@ int ath11k_regd_update(struct ath11k *ar
|
|
goto err;
|
|
}
|
|
|
|
+ if (ar->pending_11d)
|
|
+ complete(&ar->finish_11d_scan);
|
|
+
|
|
rtnl_lock();
|
|
wiphy_lock(ar->hw->wiphy);
|
|
+
|
|
+ if (ar->pending_11d)
|
|
+ reinit_completion(&ar->finish_11d_ch_list);
|
|
+
|
|
ret = regulatory_set_wiphy_regd_sync(ar->hw->wiphy, regd_copy);
|
|
wiphy_unlock(ar->hw->wiphy);
|
|
rtnl_unlock();
|
|
--- a/drivers/net/wireless/ath/ath11k/wmi.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
|
|
@@ -130,6 +130,8 @@ static const struct wmi_tlv_policy wmi_t
|
|
.min_len = sizeof(struct wmi_vdev_delete_resp_event) },
|
|
[WMI_TAG_OBSS_COLOR_COLLISION_EVT] = {
|
|
.min_len = sizeof(struct wmi_obss_color_collision_event) },
|
|
+ [WMI_TAG_11D_NEW_COUNTRY_EVENT] = {
|
|
+ .min_len = sizeof(struct wmi_11d_new_cc_ev) },
|
|
};
|
|
|
|
#define PRIMAP(_hw_mode_) \
|
|
@@ -2898,6 +2900,75 @@ ath11k_wmi_send_thermal_mitigation_param
|
|
return ret;
|
|
}
|
|
|
|
+int ath11k_wmi_send_11d_scan_start_cmd(struct ath11k *ar,
|
|
+ struct wmi_11d_scan_start_params *param)
|
|
+{
|
|
+ struct ath11k_pdev_wmi *wmi = ar->wmi;
|
|
+ struct wmi_11d_scan_start_cmd *cmd;
|
|
+ struct sk_buff *skb;
|
|
+ int ret;
|
|
+
|
|
+ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd));
|
|
+ if (!skb)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ cmd = (struct wmi_11d_scan_start_cmd *)skb->data;
|
|
+ cmd->tlv_header =
|
|
+ FIELD_PREP(WMI_TLV_TAG, WMI_TAG_11D_SCAN_START_CMD) |
|
|
+ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
|
|
+
|
|
+ cmd->vdev_id = param->vdev_id;
|
|
+ cmd->scan_period_msec = param->scan_period_msec;
|
|
+ cmd->start_interval_msec = param->start_interval_msec;
|
|
+ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_11D_SCAN_START_CMDID);
|
|
+
|
|
+ ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
|
|
+ "send 11d scan start vdev id %d period %d ms internal %d ms\n",
|
|
+ cmd->vdev_id,
|
|
+ cmd->scan_period_msec,
|
|
+ cmd->start_interval_msec);
|
|
+
|
|
+ if (ret) {
|
|
+ ath11k_warn(ar->ab,
|
|
+ "failed to send WMI_11D_SCAN_START_CMDID: %d\n", ret);
|
|
+ dev_kfree_skb(skb);
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int ath11k_wmi_send_11d_scan_stop_cmd(struct ath11k *ar, u32 vdev_id)
|
|
+{
|
|
+ struct ath11k_pdev_wmi *wmi = ar->wmi;
|
|
+ struct wmi_11d_scan_stop_cmd *cmd;
|
|
+ struct sk_buff *skb;
|
|
+ int ret;
|
|
+
|
|
+ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd));
|
|
+ if (!skb)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ cmd = (struct wmi_11d_scan_stop_cmd *)skb->data;
|
|
+ cmd->tlv_header =
|
|
+ FIELD_PREP(WMI_TLV_TAG, WMI_TAG_11D_SCAN_STOP_CMD) |
|
|
+ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
|
|
+
|
|
+ cmd->vdev_id = vdev_id;
|
|
+ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_11D_SCAN_STOP_CMDID);
|
|
+
|
|
+ ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
|
|
+ "send 11d scan stop vdev id %d\n",
|
|
+ cmd->vdev_id);
|
|
+
|
|
+ if (ret) {
|
|
+ ath11k_warn(ar->ab,
|
|
+ "failed to send WMI_11D_SCAN_STOP_CMDID: %d\n", ret);
|
|
+ dev_kfree_skb(skb);
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
int ath11k_wmi_pdev_pktlog_enable(struct ath11k *ar, u32 pktlog_filter)
|
|
{
|
|
struct ath11k_pdev_wmi *wmi = ar->wmi;
|
|
@@ -5938,6 +6009,41 @@ static void ath11k_wmi_op_ep_tx_credits(
|
|
wake_up(&ab->wmi_ab.tx_credits_wq);
|
|
}
|
|
|
|
+static int ath11k_reg_11d_new_cc_event(struct ath11k_base *ab, struct sk_buff *skb)
|
|
+{
|
|
+ const struct wmi_11d_new_cc_ev *ev;
|
|
+ const void **tb;
|
|
+ int ret;
|
|
+
|
|
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
|
|
+ if (IS_ERR(tb)) {
|
|
+ ret = PTR_ERR(tb);
|
|
+ ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ev = tb[WMI_TAG_11D_NEW_COUNTRY_EVENT];
|
|
+ if (!ev) {
|
|
+ kfree(tb);
|
|
+ ath11k_warn(ab, "failed to fetch 11d new cc ev");
|
|
+ return -EPROTO;
|
|
+ }
|
|
+
|
|
+ spin_lock_bh(&ab->base_lock);
|
|
+ memcpy(&ab->new_alpha2, &ev->new_alpha2, 2);
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_WMI, "wmi 11d new cc %c%c\n",
|
|
+ ab->new_alpha2[0],
|
|
+ ab->new_alpha2[1]);
|
|
+
|
|
+ kfree(tb);
|
|
+
|
|
+ queue_work(ab->workqueue, &ab->update_11d_work);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static void ath11k_wmi_htc_tx_complete(struct ath11k_base *ab,
|
|
struct sk_buff *skb)
|
|
{
|
|
@@ -7333,6 +7439,9 @@ static void ath11k_wmi_tlv_op_rx(struct
|
|
case WMI_WOW_WAKEUP_HOST_EVENTID:
|
|
ath11k_wmi_event_wow_wakeup_host(ab, skb);
|
|
break;
|
|
+ case WMI_11D_NEW_COUNTRY_EVENTID:
|
|
+ ath11k_reg_11d_new_cc_event(ab, skb);
|
|
+ break;
|
|
/* TODO: Add remaining events */
|
|
default:
|
|
ath11k_dbg(ab, ATH11K_DBG_WMI, "Unknown eventid: 0x%x\n", id);
|
|
--- a/drivers/net/wireless/ath/ath11k/wmi.h
|
|
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
|
|
@@ -3812,6 +3812,28 @@ struct wmi_init_country_cmd {
|
|
} cc_info;
|
|
} __packed;
|
|
|
|
+struct wmi_11d_scan_start_params {
|
|
+ u32 vdev_id;
|
|
+ u32 scan_period_msec;
|
|
+ u32 start_interval_msec;
|
|
+};
|
|
+
|
|
+struct wmi_11d_scan_start_cmd {
|
|
+ u32 tlv_header;
|
|
+ u32 vdev_id;
|
|
+ u32 scan_period_msec;
|
|
+ u32 start_interval_msec;
|
|
+} __packed;
|
|
+
|
|
+struct wmi_11d_scan_stop_cmd {
|
|
+ u32 tlv_header;
|
|
+ u32 vdev_id;
|
|
+} __packed;
|
|
+
|
|
+struct wmi_11d_new_cc_ev {
|
|
+ u32 new_alpha2;
|
|
+} __packed;
|
|
+
|
|
#define THERMAL_LEVELS 1
|
|
struct tt_level_config {
|
|
u32 tmplwm;
|
|
@@ -5447,6 +5469,11 @@ int ath11k_wmi_send_set_current_country_
|
|
int
|
|
ath11k_wmi_send_init_country_cmd(struct ath11k *ar,
|
|
struct wmi_init_country_params init_cc_param);
|
|
+
|
|
+int ath11k_wmi_send_11d_scan_start_cmd(struct ath11k *ar,
|
|
+ struct wmi_11d_scan_start_params *param);
|
|
+int ath11k_wmi_send_11d_scan_stop_cmd(struct ath11k *ar, u32 vdev_id);
|
|
+
|
|
int
|
|
ath11k_wmi_send_thermal_mitigation_param_cmd(struct ath11k *ar,
|
|
struct thermal_mitigation_params *param);
|