Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 10509f90 authored by Kalle Valo's avatar Kalle Valo
Browse files

ath6kl: implement scheduled scan



ath6kl firmware supports scheduled scan functionality with the wow ssid
filter. But the firmware does not send any events after scan results
so I had to add a timer which notifies about new scan results.

Sched scan needs firmware version 3.2.0.6 or later. If firmware doesn't
support sched scan the driver will not enable the feature.

Signed-off-by: default avatarKalle Valo <kvalo@qca.qualcomm.com>
parent 277d90f4
Loading
Loading
Loading
Loading
+144 −0
Original line number Diff line number Diff line
@@ -123,6 +123,37 @@ static struct ieee80211_supported_band ath6kl_band_5ghz = {

#define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */

/* returns true if scheduled scan was stopped */
static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif)
{
	struct ath6kl *ar = vif->ar;

	if (ar->state != ATH6KL_STATE_SCHED_SCAN)
		return false;

	del_timer_sync(&vif->sched_scan_timer);

	ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
					   ATH6KL_HOST_MODE_AWAKE);

	ar->state = ATH6KL_STATE_ON;

	return true;
}

static void ath6kl_cfg80211_sscan_disable(struct ath6kl_vif *vif)
{
	struct ath6kl *ar = vif->ar;
	bool stopped;

	stopped = __ath6kl_cfg80211_sscan_stop(vif);

	if (!stopped)
		return;

	cfg80211_sched_scan_stopped(ar->wiphy);
}

static int ath6kl_set_wpa_version(struct ath6kl_vif *vif,
				  enum nl80211_wpa_versions wpa_version)
{
@@ -383,6 +414,8 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
	struct ath6kl_vif *vif = netdev_priv(dev);
	int status;

	ath6kl_cfg80211_sscan_disable(vif);

	vif->sme_state = SME_CONNECTING;

	if (!ath6kl_cfg80211_ready(vif))
@@ -710,6 +743,8 @@ static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__,
		   reason_code);

	ath6kl_cfg80211_sscan_disable(vif);

	if (!ath6kl_cfg80211_ready(vif))
		return -EIO;

@@ -812,6 +847,8 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
	if (!ath6kl_cfg80211_ready(vif))
		return -EIO;

	ath6kl_cfg80211_sscan_disable(vif);

	if (!ar->usr_bss_filter) {
		clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
		ret = ath6kl_wmi_bssfilter_cmd(
@@ -837,6 +874,10 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
						  request->ssids[i].ssid);
	}

	/*
	 * FIXME: we should clear the IE in fw if it's not set so just
	 * remove the check altogether
	 */
	if (request->ie) {
		ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
					       WMI_FRAME_PROBE_REQ,
@@ -1835,6 +1876,13 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar,

		break;

	case ATH6KL_CFG_SUSPEND_SCHED_SCAN:
		/*
		 * Nothing needed for schedule scan, firmware is already in
		 * wow mode and sleeping most of the time.
		 */
		break;

	default:
		break;
	}
@@ -1883,6 +1931,9 @@ int ath6kl_cfg80211_resume(struct ath6kl *ar)
		}
		break;

	case ATH6KL_STATE_SCHED_SCAN:
		break;

	default:
		break;
	}
@@ -2329,6 +2380,90 @@ static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
	}
}

static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
			struct net_device *dev,
			struct cfg80211_sched_scan_request *request)
{
	struct ath6kl *ar = ath6kl_priv(dev);
	struct ath6kl_vif *vif = netdev_priv(dev);
	u16 interval;
	int ret;
	u8 i;

	if (ar->state != ATH6KL_STATE_ON)
		return -EIO;

	if (vif->sme_state != SME_DISCONNECTED)
		return -EBUSY;

	for (i = 0; i < ar->wiphy->max_sched_scan_ssids; i++) {
		ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
					  i, DISABLE_SSID_FLAG,
					  0, NULL);
	}

	/* fw uses seconds, also make sure that it's >0 */
	interval = max_t(u16, 1, request->interval / 1000);

	ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
				  interval, interval,
				  10, 0, 0, 0, 3, 0, 0, 0);

	if (request->n_ssids && request->ssids[0].ssid_len) {
		for (i = 0; i < request->n_ssids; i++) {
			ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
						  i, SPECIFIC_SSID_FLAG,
						  request->ssids[i].ssid_len,
						  request->ssids[i].ssid);
		}
	}

	ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
					  ATH6KL_WOW_MODE_ENABLE,
					  WOW_FILTER_SSID,
					  WOW_HOST_REQ_DELAY);
	if (ret) {
		ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret);
		return ret;
	}

	/* this also clears IE in fw if it's not set */
	ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
				       WMI_FRAME_PROBE_REQ,
				       request->ie, request->ie_len);
	if (ret) {
		ath6kl_warn("Failed to set probe request IE for scheduled scan: %d",
			    ret);
		return ret;
	}

	ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
						 ATH6KL_HOST_MODE_ASLEEP);
	if (ret) {
		ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n",
			    ret);
		return ret;
	}

	ar->state = ATH6KL_STATE_SCHED_SCAN;

	return ret;
}

static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy,
				      struct net_device *dev)
{
	struct ath6kl_vif *vif = netdev_priv(dev);
	bool stopped;

	stopped = __ath6kl_cfg80211_sscan_stop(vif);

	if (!stopped)
		return -EIO;

	return 0;
}

static const struct ieee80211_txrx_stypes
ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
	[NL80211_IFTYPE_STATION] = {
@@ -2386,10 +2521,14 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = {
	.cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
	.mgmt_tx = ath6kl_mgmt_tx,
	.mgmt_frame_register = ath6kl_mgmt_frame_register,
	.sched_scan_start = ath6kl_cfg80211_sscan_start,
	.sched_scan_stop = ath6kl_cfg80211_sscan_stop,
};

void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
{
	ath6kl_cfg80211_sscan_disable(vif);

	switch (vif->sme_state) {
	case SME_DISCONNECTED:
		break;
@@ -2546,6 +2685,8 @@ int ath6kl_register_ieee80211_hw(struct ath6kl *ar)
	wiphy->wowlan.pattern_min_len = 1;
	wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;

	wiphy->max_sched_scan_ssids = 10;

	ret = wiphy_register(wiphy);
	if (ret < 0) {
		ath6kl_err("couldn't register wiphy device\n");
@@ -2565,6 +2706,9 @@ static int ath6kl_init_if_data(struct ath6kl_vif *vif)

	setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
		    (unsigned long) vif->ndev);
	setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer,
		    (unsigned long) vif);

	set_bit(WMM_ENABLED, &vif->flags);
	spin_lock_init(&vif->if_lock);

+2 −1
Original line number Diff line number Diff line
@@ -20,7 +20,8 @@
enum ath6kl_cfg_suspend_mode {
	ATH6KL_CFG_SUSPEND_DEEPSLEEP,
	ATH6KL_CFG_SUSPEND_CUTPOWER,
	ATH6KL_CFG_SUSPEND_WOW
	ATH6KL_CFG_SUSPEND_WOW,
	ATH6KL_CFG_SUSPEND_SCHED_SCAN,
};

struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
+7 −0
Original line number Diff line number Diff line
@@ -76,6 +76,7 @@ enum ath6kl_fw_ie_type {

enum ath6kl_fw_capability {
	ATH6KL_FW_CAPABILITY_HOST_P2P = 0,
	ATH6KL_FW_CAPABILITY_SCHED_SCAN = 1,

	/* this needs to be last */
	ATH6KL_FW_CAPABILITY_MAX,
@@ -447,7 +448,10 @@ struct ath6kl_vif {
	struct ath6kl_wep_key wep_key_list[WMI_MAX_KEY_INDEX + 1];
	struct ath6kl_key keys[WMI_MAX_KEY_INDEX + 1];
	struct aggr_info *aggr_cntxt;

	struct timer_list disconnect_timer;
	struct timer_list sched_scan_timer;

	struct cfg80211_scan_request *scan_req;
	enum sme_state sme_state;
	int reconnect_flag;
@@ -465,6 +469,8 @@ struct ath6kl_vif {
#define WOW_LIST_ID		0
#define WOW_HOST_REQ_DELAY	500 /* ms */

#define ATH6KL_SCHED_SCAN_RESULT_DELAY 5000 /* ms */

/* Flag info */
enum ath6kl_dev_state {
	WMI_ENABLED,
@@ -483,6 +489,7 @@ enum ath6kl_state {
	ATH6KL_STATE_DEEPSLEEP,
	ATH6KL_STATE_CUTPOWER,
	ATH6KL_STATE_WOW,
	ATH6KL_STATE_SCHED_SCAN,
};

struct ath6kl {
+3 −0
Original line number Diff line number Diff line
@@ -1644,6 +1644,9 @@ int ath6kl_core_init(struct ath6kl *ar)
			    WIPHY_FLAG_HAVE_AP_SME |
			    WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;

	if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities))
		ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;

	ar->wiphy->probe_resp_offload =
		NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
		NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
+25 −1
Original line number Diff line number Diff line
@@ -806,7 +806,28 @@ static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
		return ret;
	}

	if ((flags & MMC_PM_WAKE_SDIO_IRQ) && wow) {
	if (!(flags & MMC_PM_WAKE_SDIO_IRQ))
		goto deepsleep;

	/* sdio irq wakes up host */

	if (ar->state == ATH6KL_STATE_SCHED_SCAN) {
		ret =  ath6kl_cfg80211_suspend(ar,
					       ATH6KL_CFG_SUSPEND_SCHED_SCAN,
					       NULL);
		if (ret) {
			ath6kl_warn("Schedule scan suspend failed: %d", ret);
			return ret;
		}

		ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ);
		if (ret)
			ath6kl_warn("set sdio wake irq flag failed: %d\n", ret);

		return ret;
	}

	if (wow) {
		/*
		 * The host sdio controller is capable of keep power and
		 * sdio irq wake up at this point. It's fine to continue
@@ -823,6 +844,7 @@ static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
		return ret;
	}

deepsleep:
	return ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_DEEPSLEEP, NULL);
}

@@ -846,6 +868,8 @@ static int ath6kl_sdio_resume(struct ath6kl *ar)

	case ATH6KL_STATE_WOW:
		break;
	case ATH6KL_STATE_SCHED_SCAN:
		break;
	}

	ath6kl_cfg80211_resume(ar);
Loading