Commit 9dcf6808 authored by Wen Gong's avatar Wen Gong Committed by Kalle Valo
Browse files

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: default avatarWen Gong <quic_wgong@quicinc.com>
Signed-off-by: default avatarKalle Valo <quic_kvalo@quicinc.com>
Link: https://lore.kernel.org/r/20211201071745.17746-4-quic_wgong@quicinc.com
parent 0b05ddad
Loading
Loading
Loading
Loading
+31 −0
Original line number Diff line number Diff line
@@ -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(struct device *dev, size_t priv_size,

	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);
+9 −0
Original line number Diff line number Diff line
@@ -588,6 +588,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;
+162 −1
Original line number Diff line number Diff line
@@ -2681,6 +2681,8 @@ static void ath11k_bss_assoc(struct ieee80211_hw *hw,
	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 ath11k *ar,
			     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 ath11k *ar,
	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 ieee80211_hw *hw,
	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;
@@ -5635,6 +5665,7 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw)

	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) {
@@ -5788,6 +5819,122 @@ static void ath11k_mac_op_update_vif_offload(struct ieee80211_hw *hw,
	}
}

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, &param);
	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)
{
@@ -5921,6 +6068,8 @@ static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw,
				    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;
@@ -5960,6 +6109,9 @@ static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw,
				    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);
@@ -6061,6 +6213,9 @@ static void ath11k_mac_op_remove_interface(struct ieee80211_hw *hw,
	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)
@@ -6779,6 +6934,9 @@ ath11k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
				    ret);
	}

	if (arvif->vdev_type == WMI_VDEV_TYPE_STA)
		ath11k_mac_11d_scan_start(ar, arvif->vdev_id, false);

	mutex_unlock(&ar->conf_mutex);
}

@@ -8180,6 +8338,9 @@ int ath11k_mac_allocate(struct ath11k_base *ab)

		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;
+7 −0
Original line number Diff line number Diff line
@@ -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);
+15 −0
Original line number Diff line number Diff line
@@ -86,6 +86,9 @@ ath11k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
	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 ath11k *ar)
	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();
Loading