[PATCH v10 2/4] wifi: ath12k: add 11d scan offload support

Vasanthakumar Thiagarajan vasanthakumar.thiagarajan at oss.qualcomm.com
Tue Feb 18 20:47:50 PST 2025



On 2/12/2025 12:01 PM, Kang Yang wrote:
> From: Wen Gong <quic_wgong at quicinc.com>
> 
> The flow of 11d scan is:
> 1. trigger 11d scan.
> 2. receive, parse, and update 11d scan result.
> 3. stop 11d scan.
> 
> So need to add handler for WMI_11D_SCAN_START_CMDID and
> WMI_11D_SCAN_STOP_CMDID to trigger/stop 11d scan. Add process of WMI
> event WMI_11D_NEW_COUNTRY_EVENTID for 11d scan result.
> 
> There are two points that need to be noted:
> 1. The 11d scan priority is 'MEDIUM' in firmware, the hw scan priority
> is 'LOW'. When 11d scan is running, hw scan will be canceled.
> To avoid this, change the hw scan priority to 'MEDIUM' when 11d scan
> is running.
> 
> 2. Need to add wait_for_completion_timeout() for scan.complete in
> ath12k_reg_update_chan_list() because 11d scan will cost more than 5
> seconds. Due to another existing wait in ath12k_scan_stop(), there will
> be two scan.complete in different threads. Therefore use complete_all()
> instead of complete() for scan.complete. complete_all() can work well
> when it is only one thread wait for scan.complete.
> 
> Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3
> 
> Signed-off-by: Wen Gong <quic_wgong at quicinc.com>
> Signed-off-by: Kang Yang <quic_kangyang at quicinc.com>
> ---
>   drivers/net/wireless/ath/ath12k/core.c |  33 ++++-
>   drivers/net/wireless/ath/ath12k/core.h |  16 +++
>   drivers/net/wireless/ath/ath12k/mac.c  | 159 ++++++++++++++++++++++++-
>   drivers/net/wireless/ath/ath12k/mac.h  |   7 ++
>   drivers/net/wireless/ath/ath12k/reg.c  |  42 ++++++-
>   drivers/net/wireless/ath/ath12k/reg.h  |   2 +-
>   drivers/net/wireless/ath/ath12k/wmi.c  | 122 ++++++++++++++++++-
>   drivers/net/wireless/ath/ath12k/wmi.h  |  25 ++++
>   8 files changed, 396 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
> index 0c556023141b..598b562b5edf 100644
> --- a/drivers/net/wireless/ath/ath12k/core.c
> +++ b/drivers/net/wireless/ath/ath12k/core.c
> @@ -1259,6 +1259,7 @@ void ath12k_core_halt(struct ath12k *ar)
>   	cancel_delayed_work_sync(&ar->scan.timeout);
>   	cancel_work_sync(&ar->regd_update_work);
>   	cancel_work_sync(&ab->rfkill_work);
> +	cancel_work_sync(&ab->update_11d_work);
>   
>   	rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], NULL);
>   	synchronize_rcu();
> @@ -1292,8 +1293,10 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab)
>   			ar = &ah->radio[j];
>   
>   			ath12k_mac_drain_tx(ar);
> +			ar->state_11d = ATH12K_11D_IDLE;
> +			complete(&ar->completed_11d_scan);
>   			complete(&ar->scan.started);
> -			complete(&ar->scan.completed);
> +			complete_all(&ar->scan.completed);
>   			complete(&ar->scan.on_channel);
>   			complete(&ar->peer_assoc_done);
>   			complete(&ar->peer_delete_done);
> @@ -1314,6 +1317,33 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab)
>   	wake_up(&ab->peer_mapping_wq);
>   }
>   
> +static void ath12k_update_11d(struct work_struct *work)
> +{
> +	struct ath12k_base *ab = container_of(work, struct ath12k_base, update_11d_work);
> +	struct ath12k *ar;
> +	struct ath12k_pdev *pdev;
> +	struct wmi_set_current_country_arg arg = {};
> +	int ret, i;
> +
> +	spin_lock_bh(&ab->base_lock);
> +	memcpy(&arg.alpha2, &ab->new_alpha2, 2);
> +	spin_unlock_bh(&ab->base_lock);
> +
> +	ath12k_dbg(ab, ATH12K_DBG_WMI, "update 11d new cc %c%c\n",
> +		   arg.alpha2[0], arg.alpha2[1]);
> +
> +	for (i = 0; i < ab->num_radios; i++) {
> +		pdev = &ab->pdevs[i];
> +		ar = pdev->ar;
> +
> +		ret = ath12k_wmi_send_set_current_country_cmd(ar, &arg);
> +		if (ret)
> +			ath12k_warn(ar->ab,
> +				    "pdev id %d failed set current country code: %d\n",
> +				    i, ret);
> +	}
> +}
> +
>   static void ath12k_core_post_reconfigure_recovery(struct ath12k_base *ab)
>   {
>   	struct ath12k_hw_group *ag = ab->ag;
> @@ -1967,6 +1997,7 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size,
>   	INIT_WORK(&ab->reset_work, ath12k_core_reset);
>   	INIT_WORK(&ab->rfkill_work, ath12k_rfkill_work);
>   	INIT_WORK(&ab->dump_work, ath12k_coredump_upload);
> +	INIT_WORK(&ab->update_11d_work, ath12k_update_11d);
>   
>   	timer_setup(&ab->rx_replenish_retry, ath12k_ce_rx_replenish_retry, 0);
>   	init_completion(&ab->htc_suspend);
> diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
> index 3fac4f00d383..ba6a023e43cc 100644
> --- a/drivers/net/wireless/ath/ath12k/core.h
> +++ b/drivers/net/wireless/ath/ath12k/core.h
> @@ -219,6 +219,12 @@ enum ath12k_scan_state {
>   	ATH12K_SCAN_ABORTING,
>   };
>   
> +enum ath12k_11d_state {
> +	ATH12K_11D_IDLE,
> +	ATH12K_11D_PREPARING,
> +	ATH12K_11D_RUNNING,
> +};
> +
>   enum ath12k_hw_group_flags {
>   	ATH12K_GROUP_FLAG_REGISTERED,
>   	ATH12K_GROUP_FLAG_UNREGISTER,
> @@ -364,6 +370,8 @@ struct ath12k_vif_iter {
>   #define HAL_RX_MAX_NSS		8
>   #define HAL_RX_MAX_NUM_LEGACY_RATES 12
>   
> +#define ATH12K_SCAN_TIMEOUT_HZ (20 * HZ)
> +
>   struct ath12k_rx_peer_rate_stats {
>   	u64 ht_mcs_count[HAL_RX_MAX_MCS_HT + 1];
>   	u64 vht_mcs_count[HAL_RX_MAX_MCS_VHT + 1];
> @@ -737,6 +745,12 @@ struct ath12k {
>   
>   	bool nlo_enabled;
>   
> +	/* Protected by wiphy::mtx lock. */
> +	u32 vdev_id_11d_scan;
> +	struct completion completed_11d_scan;
> +	enum ath12k_11d_state state_11d;
> +	bool regdom_set_by_user;
> +
>   	struct completion fw_stats_complete;
>   
>   	struct completion mlo_setup_done;
> @@ -1011,6 +1025,8 @@ struct ath12k_base {
>   	/* continuous recovery fail count */
>   	atomic_t fail_cont_count;
>   	unsigned long reset_fail_timeout;
> +	struct work_struct update_11d_work;
> +	u8 new_alpha2[2];
>   	struct {
>   		/* protected by data_lock */
>   		u32 fw_crash_counter;
> diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
> index 27d2fad1b915..6ef98837e84f 100644
> --- a/drivers/net/wireless/ath/ath12k/mac.c
> +++ b/drivers/net/wireless/ath/ath12k/mac.c
> @@ -3375,6 +3375,11 @@ static void ath12k_bss_assoc(struct ath12k *ar,
>   	if (ret)
>   		ath12k_warn(ar->ab, "failed to set vdev %i OBSS PD parameters: %d\n",
>   			    arvif->vdev_id, ret);
> +
> +	if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ar->ab->wmi_ab.svc_map) &&
> +	    ahvif->vdev_type == WMI_VDEV_TYPE_STA &&
> +	    ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE)
> +		ath12k_mac_11d_scan_stop_all(ar->ab);
>   }
>   
>   static void ath12k_bss_disassoc(struct ath12k *ar,
> @@ -3532,6 +3537,11 @@ static void ath12k_mac_remove_link_interface(struct ieee80211_hw *hw,
>   	ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac remove link interface (vdev %d link id %d)",
>   		   arvif->vdev_id, arvif->link_id);
>   
> +	if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ar->ab->wmi_ab.svc_map) &&
> +	    ahvif->vdev_type == WMI_VDEV_TYPE_STA &&
> +	    ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE)
> +		ath12k_mac_11d_scan_stop(ar);
> +
>   	if (ahvif->vdev_type == WMI_VDEV_TYPE_AP) {
>   		ret = ath12k_peer_delete(ar, arvif->vdev_id, arvif->bssid);
>   		if (ret)
> @@ -4192,7 +4202,7 @@ void __ath12k_mac_scan_finish(struct ath12k *ar)
>   		fallthrough;
>   	case ATH12K_SCAN_STARTING:
>   		cancel_delayed_work(&ar->scan.timeout);
> -		complete(&ar->scan.completed);
> +		complete_all(&ar->scan.completed);
>   		wiphy_work_queue(ar->ah->hw->wiphy, &ar->scan.vdev_clean_wk);
>   		break;
>   	}
> @@ -4536,7 +4546,12 @@ static int ath12k_mac_op_hw_scan(struct ieee80211_hw *hw,
>   
>   	ret = ath12k_start_scan(ar, arg);
>   	if (ret) {
> -		ath12k_warn(ar->ab, "failed to start hw scan: %d\n", ret);
> +		if (ret == -EBUSY)
> +			ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
> +				   "scan engine is busy 11d state %d\n", ar->state_11d);
> +		else
> +			ath12k_warn(ar->ab, "failed to start hw scan: %d\n", ret);
> +
>   		spin_lock_bh(&ar->data_lock);
>   		ar->scan.state = ATH12K_SCAN_IDLE;
>   		spin_unlock_bh(&ar->data_lock);
> @@ -4563,6 +4578,11 @@ static int ath12k_mac_op_hw_scan(struct ieee80211_hw *hw,
>   		kfree(arg);
>   	}
>   
> +	if (ar->state_11d == ATH12K_11D_PREPARING &&
> +	    ahvif->vdev_type == WMI_VDEV_TYPE_STA &&
> +	    ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE)
> +		ath12k_mac_11d_scan_start(ar, arvif->vdev_id);
> +
>   	return ret;
>   }
>   
> @@ -7592,7 +7612,7 @@ static int ath12k_mac_start(struct ath12k *ar)
>   
>   	/* TODO: Do we need to enable ANI? */
>   
> -	ath12k_reg_update_chan_list(ar);
> +	ath12k_reg_update_chan_list(ar, false);
>   
>   	ar->num_started_vdevs = 0;
>   	ar->num_created_vdevs = 0;
> @@ -7779,6 +7799,9 @@ static void ath12k_mac_stop(struct ath12k *ar)
>   	wiphy_work_cancel(ath12k_ar_to_hw(ar)->wiphy, &ar->scan.vdev_clean_wk);
>   	cancel_work_sync(&ar->regd_update_work);
>   	cancel_work_sync(&ar->ab->rfkill_work);
> +	cancel_work_sync(&ar->ab->update_11d_work);
> +	ar->state_11d = ATH12K_11D_IDLE;
> +	complete(&ar->completed_11d_scan);
>   
>   	spin_lock_bh(&ar->data_lock);
>   	list_for_each_entry_safe(ppdu_stats, tmp, &ar->ppdu_stats_info, list) {
> @@ -8072,6 +8095,118 @@ static void ath12k_mac_op_update_vif_offload(struct ieee80211_hw *hw,
>   	ath12k_mac_update_vif_offload(&ahvif->deflink);
>   }
>   
> +static bool ath12k_mac_vif_ap_active_any(struct ath12k_base *ab)
> +{
> +	struct ath12k *ar;
> +	struct ath12k_pdev *pdev;
> +	struct ath12k_link_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->ahvif->vdev_type == WMI_VDEV_TYPE_AP)
> +				return true;
> +		}
> +	}
> +	return false;
> +}
> +
> +void ath12k_mac_11d_scan_start(struct ath12k *ar, u32 vdev_id)
> +{
> +	struct wmi_11d_scan_start_arg arg;
> +	int ret;
> +
> +	lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
> +
> +	if (ar->regdom_set_by_user)
> +		goto fin;
> +
> +	if (ar->vdev_id_11d_scan != ATH12K_11D_INVALID_VDEV_ID)
> +		goto fin;
> +
> +	if (!test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ar->ab->wmi_ab.svc_map))
> +		goto fin;
> +
> +	if (ath12k_mac_vif_ap_active_any(ar->ab))
> +		goto fin;
> +
> +	arg.vdev_id = vdev_id;
> +	arg.start_interval_msec = 0;
> +	arg.scan_period_msec = ATH12K_SCAN_11D_INTERVAL;
> +
> +	ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
> +		   "mac start 11d scan for vdev %d\n", vdev_id);
> +
> +	ret = ath12k_wmi_send_11d_scan_start_cmd(ar, &arg);
> +	if (ret) {
> +		ath12k_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 (ar->state_11d == ATH12K_11D_PREPARING)
> +			ar->state_11d = ATH12K_11D_RUNNING;
> +	}
> +
> +fin:
> +	if (ar->state_11d == ATH12K_11D_PREPARING) {
> +		ar->state_11d = ATH12K_11D_IDLE;
> +		complete(&ar->completed_11d_scan);
> +	}
> +}
> +
> +void ath12k_mac_11d_scan_stop(struct ath12k *ar)
> +{
> +	int ret;
> +	u32 vdev_id;
> +
> +	lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
> +
> +	if (!test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ar->ab->wmi_ab.svc_map))
> +		return;
> +
> +	ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac stop 11d for vdev %d\n",
> +		   ar->vdev_id_11d_scan);
> +
> +	if (ar->state_11d == ATH12K_11D_PREPARING) {
> +		ar->state_11d = ATH12K_11D_IDLE;
> +		complete(&ar->completed_11d_scan);
> +	}
> +
> +	if (ar->vdev_id_11d_scan != ATH12K_11D_INVALID_VDEV_ID) {
> +		vdev_id = ar->vdev_id_11d_scan;
> +
> +		ret = ath12k_wmi_send_11d_scan_stop_cmd(ar, vdev_id);
> +		if (ret) {
> +			ath12k_warn(ar->ab,
> +				    "failed to stopt 11d scan vdev %d ret: %d\n",
> +				    vdev_id, ret);
> +		} else {
> +			ar->vdev_id_11d_scan = ATH12K_11D_INVALID_VDEV_ID;
> +			ar->state_11d = ATH12K_11D_IDLE;
> +			complete(&ar->completed_11d_scan);
> +		}
> +	}
> +}
> +
> +void ath12k_mac_11d_scan_stop_all(struct ath12k_base *ab)
> +{
> +	struct ath12k *ar;
> +	struct ath12k_pdev *pdev;
> +	int i;
> +
> +	ath12k_dbg(ab, ATH12K_DBG_MAC, "mac stop soc 11d scan\n");
> +
> +	for (i = 0; i < ab->num_radios; i++) {
> +		pdev = &ab->pdevs[i];
> +		ar = pdev->ar;
> +
> +		ath12k_mac_11d_scan_stop(ar);
> +	}
> +}
> +
>   int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif)
>   {
>   	struct ath12k_hw *ah = ar->ah;
> @@ -8206,6 +8341,7 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif)
>   				    arvif->vdev_id, ret);
>   			goto err_peer_del;
>   		}
> +		ath12k_mac_11d_scan_stop_all(ar->ab);
>   		break;
>   	case WMI_VDEV_TYPE_STA:
>   		param_id = WMI_STA_PS_PARAM_RX_WAKE_POLICY;
> @@ -8244,6 +8380,13 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif)
>   				    arvif->vdev_id, ret);
>   			goto err_peer_del;
>   		}
> +
> +		if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ab->wmi_ab.svc_map) &&
> +		    ahvif->vdev_type == WMI_VDEV_TYPE_STA &&
> +		    ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE) {
> +			reinit_completion(&ar->completed_11d_scan);
> +			ar->state_11d = ATH12K_11D_PREPARING;
> +		}
>   		break;
>   	default:
>   		break;
> @@ -9571,6 +9714,14 @@ ath12k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
>   	if (ahvif->vdev_type != WMI_VDEV_TYPE_MONITOR &&
>   	    ar->num_started_vdevs == 1 && ar->monitor_vdev_created)
>   		ath12k_mac_monitor_stop(ar);
> +
> +	if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ab->wmi_ab.svc_map) &&
> +	    ahvif->vdev_type == WMI_VDEV_TYPE_STA &&
> +	    ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE &&
> +	    ar->state_11d != ATH12K_11D_PREPARING) {
> +		reinit_completion(&ar->completed_11d_scan);
> +		ar->state_11d = ATH12K_11D_PREPARING;
> +	}
>   }
>   
>   static int
> @@ -11392,6 +11543,7 @@ static void ath12k_mac_setup(struct ath12k *ar)
>   	ar->num_tx_chains = hweight32(pdev->cap.tx_chain_mask);
>   	ar->num_rx_chains = hweight32(pdev->cap.rx_chain_mask);
>   	ar->scan.arvif = NULL;
> +	ar->vdev_id_11d_scan = ATH12K_11D_INVALID_VDEV_ID;
>   
>   	spin_lock_init(&ar->data_lock);
>   	INIT_LIST_HEAD(&ar->arvifs);
> @@ -11407,6 +11559,7 @@ static void ath12k_mac_setup(struct ath12k *ar)
>   	init_completion(&ar->scan.completed);
>   	init_completion(&ar->scan.on_channel);
>   	init_completion(&ar->mlo_setup_done);
> +	init_completion(&ar->completed_11d_scan);
>   
>   	INIT_DELAYED_WORK(&ar->scan.timeout, ath12k_scan_timeout_work);
>   	wiphy_work_init(&ar->scan.vdev_clean_wk, ath12k_scan_vdev_clean_work);
> diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h
> index ae35b73312bf..6b3dce98185a 100644
> --- a/drivers/net/wireless/ath/ath12k/mac.h
> +++ b/drivers/net/wireless/ath/ath12k/mac.h
> @@ -66,6 +66,13 @@ struct ath12k_mac_get_any_chanctx_conf_arg {
>   
>   extern const struct htt_rx_ring_tlv_filter ath12k_mac_mon_status_filter_default;
>   
> +#define ATH12K_SCAN_11D_INTERVAL		600000
> +#define ATH12K_11D_INVALID_VDEV_ID		0xFFFF
> +
> +void ath12k_mac_11d_scan_start(struct ath12k *ar, u32 vdev_id);
> +void ath12k_mac_11d_scan_stop(struct ath12k *ar);
> +void ath12k_mac_11d_scan_stop_all(struct ath12k_base *ab);
> +
>   void ath12k_mac_destroy(struct ath12k_hw_group *ag);
>   void ath12k_mac_unregister(struct ath12k_hw_group *ag);
>   int ath12k_mac_register(struct ath12k_hw_group *ag);
> diff --git a/drivers/net/wireless/ath/ath12k/reg.c b/drivers/net/wireless/ath/ath12k/reg.c
> index 439d61f284d8..01741b1fb5bb 100644
> --- a/drivers/net/wireless/ath/ath12k/reg.c
> +++ b/drivers/net/wireless/ath/ath12k/reg.c
> @@ -1,7 +1,7 @@
>   // SPDX-License-Identifier: BSD-3-Clause-Clear
>   /*
>    * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
> - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
> + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
>    */
>   #include <linux/rtnetlink.h>
>   #include "core.h"
> @@ -94,10 +94,16 @@ ath12k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
>   		if (ret)
>   			ath12k_warn(ar->ab,
>   				    "INIT Country code set to fw failed : %d\n", ret);
> +
> +		wiphy_lock(wiphy);
> +		ath12k_mac_11d_scan_stop(ar);
> +		wiphy_unlock(wiphy);
> +
> +		ar->regdom_set_by_user = true;
>   	}
>   }
>   
> -int ath12k_reg_update_chan_list(struct ath12k *ar)
> +int ath12k_reg_update_chan_list(struct ath12k *ar, bool wait)
>   {
>   	struct ieee80211_supported_band **bands;
>   	struct ath12k_wmi_scan_chan_list_arg *arg;
> @@ -106,7 +112,35 @@ int ath12k_reg_update_chan_list(struct ath12k *ar)
>   	struct ath12k_wmi_channel_arg *ch;
>   	enum nl80211_band band;
>   	int num_channels = 0;
> -	int i, ret;
> +	int i, ret, left;
> +
> +	if (wait && ar->state_11d != ATH12K_11D_IDLE) {
> +		left = wait_for_completion_timeout(&ar->completed_11d_scan,
> +						   ATH12K_SCAN_TIMEOUT_HZ);
> +		if (!left) {
> +			ath12k_dbg(ar->ab, ATH12K_DBG_REG,
> +				   "failed to receive 11d scan complete: timed out\n");
> +			ar->state_11d = ATH12K_11D_IDLE;
> +		}
> +		ath12k_dbg(ar->ab, ATH12K_DBG_REG,
> +			   "reg 11d scan wait left time %d\n", left);
> +	}
> +
> +	if (wait &&
> +	    (ar->scan.state == ATH12K_SCAN_STARTING ||
> +	    ar->scan.state == ATH12K_SCAN_RUNNING)) {
> +		left = wait_for_completion_timeout(&ar->scan.completed,
> +						   ATH12K_SCAN_TIMEOUT_HZ);
> +		if (!left)
> +			ath12k_dbg(ar->ab, ATH12K_DBG_REG,
> +				   "failed to receive hw scan complete: timed out\n");
> +
> +		ath12k_dbg(ar->ab, ATH12K_DBG_REG,
> +			   "reg hw scan wait left time %d\n", left);
> +	}
> +
> +	if (ar->ah->state == ATH12K_HW_STATE_RESTARTING)
> +		return 0;
>   
>   	bands = hw->wiphy->bands;
>   	for (band = 0; band < NUM_NL80211_BANDS; band++) {
> @@ -295,7 +329,7 @@ int ath12k_regd_update(struct ath12k *ar, bool init)
>   	 */
>   	for_each_ar(ah, ar, i) {
>   		ab = ar->ab;
> -		ret = ath12k_reg_update_chan_list(ar);
> +		ret = ath12k_reg_update_chan_list(ar, true);
>   		if (ret)
>   			goto err;
>   	}
> diff --git a/drivers/net/wireless/ath/ath12k/reg.h b/drivers/net/wireless/ath/ath12k/reg.h
> index 75f80df2aa0c..b1eb584ff34c 100644
> --- a/drivers/net/wireless/ath/ath12k/reg.h
> +++ b/drivers/net/wireless/ath/ath12k/reg.h
> @@ -99,6 +99,6 @@ struct ieee80211_regdomain *ath12k_reg_build_regd(struct ath12k_base *ab,
>   						  struct ath12k_reg_info *reg_info,
>   						  bool intersect);
>   int ath12k_regd_update(struct ath12k *ar, bool init);
> -int ath12k_reg_update_chan_list(struct ath12k *ar);
> +int ath12k_reg_update_chan_list(struct ath12k *ar, bool wait);
>   
>   #endif
> diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
> index 0794aaf9ca03..80e285207785 100644
> --- a/drivers/net/wireless/ath/ath12k/wmi.c
> +++ b/drivers/net/wireless/ath/ath12k/wmi.c
> @@ -177,6 +177,8 @@ static const struct ath12k_wmi_tlv_policy ath12k_wmi_tlv_policies[] = {
>   		.min_len = sizeof(struct ath12k_wmi_p2p_noa_info) },
>   	[WMI_TAG_P2P_NOA_EVENT] = {
>   		.min_len = sizeof(struct wmi_p2p_noa_event) },
> +	[WMI_TAG_11D_NEW_COUNTRY_EVENT] = {
> +		.min_len = sizeof(struct wmi_11d_new_cc_event) },
>   };
>   
>   __le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len)
> @@ -2591,7 +2593,10 @@ int ath12k_wmi_send_scan_start_cmd(struct ath12k *ar,
>   	cmd->scan_id = cpu_to_le32(arg->scan_id);
>   	cmd->scan_req_id = cpu_to_le32(arg->scan_req_id);
>   	cmd->vdev_id = cpu_to_le32(arg->vdev_id);
> -	cmd->scan_priority = cpu_to_le32(arg->scan_priority);
> +	if (ar->state_11d == ATH12K_11D_PREPARING)
> +		arg->scan_priority = WMI_SCAN_PRIORITY_MEDIUM;
> +	else
> +		arg->scan_priority = WMI_SCAN_PRIORITY_LOW;
>   	cmd->notify_scan_events = cpu_to_le32(arg->notify_scan_events);
>   
>   	ath12k_wmi_copy_scan_event_cntrl_flags(cmd, arg);
> @@ -3349,6 +3354,74 @@ int ath12k_wmi_send_set_current_country_cmd(struct ath12k *ar,
>   	return ret;
>   }
>   
> +int ath12k_wmi_send_11d_scan_start_cmd(struct ath12k *ar,
> +				       struct wmi_11d_scan_start_arg *arg)
> +{
> +	struct ath12k_wmi_pdev *wmi = ar->wmi;
> +	struct wmi_11d_scan_start_cmd *cmd;
> +	struct sk_buff *skb;
> +	int ret;
> +
> +	skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd));
> +	if (!skb)
> +		return -ENOMEM;
> +
> +	cmd = (struct wmi_11d_scan_start_cmd *)skb->data;
> +	cmd->tlv_header =
> +		ath12k_wmi_tlv_cmd_hdr(WMI_TAG_11D_SCAN_START_CMD,
> +				       sizeof(*cmd));
> +
> +	cmd->vdev_id = cpu_to_le32(arg->vdev_id);
> +	cmd->scan_period_msec = cpu_to_le32(arg->scan_period_msec);
> +	cmd->start_interval_msec = cpu_to_le32(arg->start_interval_msec);
> +	ret = ath12k_wmi_cmd_send(wmi, skb, WMI_11D_SCAN_START_CMDID);
> +
> +	ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
> +		   "send 11d scan start vdev id %d period %d ms internal %d ms\n",
> +		   arg->vdev_id, arg->scan_period_msec,
> +		   arg->start_interval_msec);
> +
> +	if (ret) {
> +		ath12k_warn(ar->ab,
> +			    "failed to send WMI_11D_SCAN_START_CMDID: %d\n", ret);
> +		dev_kfree_skb(skb);
> +	}
> +
> +	return ret;
> +}
> +
> +int ath12k_wmi_send_11d_scan_stop_cmd(struct ath12k *ar, u32 vdev_id)
> +{
> +	struct ath12k_wmi_pdev *wmi = ar->wmi;
> +	struct wmi_11d_scan_stop_cmd *cmd;
> +	struct sk_buff *skb;
> +	int ret;
> +
> +	skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd));
> +	if (!skb)
> +		return -ENOMEM;
> +
> +	cmd = (struct wmi_11d_scan_stop_cmd *)skb->data;
> +	cmd->tlv_header =
> +		ath12k_wmi_tlv_cmd_hdr(WMI_TAG_11D_SCAN_STOP_CMD,
> +				       sizeof(*cmd));
> +
> +	cmd->vdev_id = cpu_to_le32(vdev_id);
> +	ret = ath12k_wmi_cmd_send(wmi, skb, WMI_11D_SCAN_STOP_CMDID);
> +
> +	ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
> +		   "send 11d scan stop vdev id %d\n",
> +		   cmd->vdev_id);
> +
> +	if (ret) {
> +		ath12k_warn(ar->ab,
> +			    "failed to send WMI_11D_SCAN_STOP_CMDID: %d\n", ret);
> +		dev_kfree_skb(skb);
> +	}
> +
> +	return ret;
> +}
> +
>   int
>   ath12k_wmi_send_twt_enable_cmd(struct ath12k *ar, u32 pdev_id)
>   {
> @@ -5969,6 +6042,50 @@ static void ath12k_wmi_op_ep_tx_credits(struct ath12k_base *ab)
>   	wake_up(&ab->wmi_ab.tx_credits_wq);
>   }
>   
> +static int ath12k_reg_11d_new_cc_event(struct ath12k_base *ab, struct sk_buff *skb)
> +{
> +	const struct wmi_11d_new_cc_event *ev;
> +	struct ath12k *ar;
> +	struct ath12k_pdev *pdev;
> +	const void **tb;
> +	int ret, i;
> +
> +	tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
> +	if (IS_ERR(tb)) {
> +		ret = PTR_ERR(tb);
> +		ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ev = tb[WMI_TAG_11D_NEW_COUNTRY_EVENT];
> +	if (!ev) {
> +		kfree(tb);
> +		ath12k_warn(ab, "failed to fetch 11d new cc ev");
> +		return -EPROTO;
> +	}
> +
> +	spin_lock_bh(&ab->base_lock);
> +	memcpy(&ab->new_alpha2, &ev->new_alpha2, REG_ALPHA2_LEN);
> +	spin_unlock_bh(&ab->base_lock);
> +
> +	ath12k_dbg(ab, ATH12K_DBG_WMI, "wmi 11d new cc %c%c\n",
> +		   ab->new_alpha2[0],
> +		   ab->new_alpha2[1]);
> +
> +	kfree(tb);
> +
> +	for (i = 0; i < ab->num_radios; i++) {
> +		pdev = &ab->pdevs[i];
> +		ar = pdev->ar;
> +		ar->state_11d = ATH12K_11D_IDLE;
> +		complete(&ar->completed_11d_scan);
> +	}
> +
> +	queue_work(ab->workqueue, &ab->update_11d_work);
> +
> +	return 0;
> +}
> +
>   static void ath12k_wmi_htc_tx_complete(struct ath12k_base *ab,
>   				       struct sk_buff *skb)
>   {
> @@ -8673,6 +8790,9 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
>   	case WMI_HALPHY_STATS_CTRL_PATH_EVENTID:
>   		ath12k_wmi_process_tpc_stats(ab, skb);
>   		break;
> +	case WMI_11D_NEW_COUNTRY_EVENTID:
> +		ath12k_reg_11d_new_cc_event(ab, skb);
> +		break;
>   	/* add Unsupported events (rare) here */
>   	case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID:
>   	case WMI_PEER_OPER_MODE_CHANGE_EVENTID:
> diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h
> index 8a8504ccdc02..b011a52d40e4 100644
> --- a/drivers/net/wireless/ath/ath12k/wmi.h
> +++ b/drivers/net/wireless/ath/ath12k/wmi.h
> @@ -4026,6 +4026,28 @@ struct wmi_init_country_cmd {
>   	} cc_info;
>   } __packed;
>   
> +struct wmi_11d_scan_start_arg {
> +	u32 vdev_id;
> +	u32 scan_period_msec;
> +	u32 start_interval_msec;
> +};
> +
> +struct wmi_11d_scan_start_cmd {
> +	__le32 tlv_header;
> +	__le32 vdev_id;
> +	__le32 scan_period_msec;
> +	__le32 start_interval_msec;
> +} __packed;
> +
> +struct wmi_11d_scan_stop_cmd {
> +	__le32 tlv_header;
> +	__le32 vdev_id;
> +} __packed;
> +
> +struct wmi_11d_new_cc_event {
> +	__le32 new_alpha2;
> +} __packed;
> +
>   struct wmi_delba_send_cmd {
>   	__le32 tlv_header;
>   	__le32 vdev_id;
> @@ -6008,6 +6030,9 @@ int ath12k_wmi_peer_rx_reorder_queue_setup(struct ath12k *ar,
>   					   dma_addr_t paddr, u8 tid,
>   					   u8 ba_window_size_valid,
>   					   u32 ba_window_size);
> +int ath12k_wmi_send_11d_scan_start_cmd(struct ath12k *ar,
> +				       struct wmi_11d_scan_start_arg *arg);
> +int ath12k_wmi_send_11d_scan_stop_cmd(struct ath12k *ar, u32 vdev_id);
>   int
>   ath12k_wmi_rx_reord_queue_remove(struct ath12k *ar,
>   				 struct ath12k_wmi_rx_reorder_queue_remove_arg *arg);


The actual patch looks good to me other than a comment on the patch order

Reviewed-by: Vasanthakumar Thiagarajan <vasanthakumar.thiagarajan at oss.qualcomm.com>



More information about the ath12k mailing list