18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Scan implementation for ST-Ericsson CW1200 mac80211 drivers
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2010, ST-Ericsson
68c2ecf20Sopenharmony_ci * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/sched.h>
108c2ecf20Sopenharmony_ci#include "cw1200.h"
118c2ecf20Sopenharmony_ci#include "scan.h"
128c2ecf20Sopenharmony_ci#include "sta.h"
138c2ecf20Sopenharmony_ci#include "pm.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_cistatic void cw1200_scan_restart_delayed(struct cw1200_common *priv);
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	int ret, i;
208c2ecf20Sopenharmony_ci	int tmo = 2000;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	switch (priv->join_status) {
238c2ecf20Sopenharmony_ci	case CW1200_JOIN_STATUS_PRE_STA:
248c2ecf20Sopenharmony_ci	case CW1200_JOIN_STATUS_JOINING:
258c2ecf20Sopenharmony_ci		return -EBUSY;
268c2ecf20Sopenharmony_ci	default:
278c2ecf20Sopenharmony_ci		break;
288c2ecf20Sopenharmony_ci	}
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	wiphy_dbg(priv->hw->wiphy, "[SCAN] hw req, type %d, %d channels, flags: 0x%x.\n",
318c2ecf20Sopenharmony_ci		  scan->type, scan->num_channels, scan->flags);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	for (i = 0; i < scan->num_channels; ++i)
348c2ecf20Sopenharmony_ci		tmo += scan->ch[i].max_chan_time + 10;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&priv->clear_recent_scan_work);
378c2ecf20Sopenharmony_ci	atomic_set(&priv->scan.in_progress, 1);
388c2ecf20Sopenharmony_ci	atomic_set(&priv->recent_scan, 1);
398c2ecf20Sopenharmony_ci	cw1200_pm_stay_awake(&priv->pm_state, msecs_to_jiffies(tmo));
408c2ecf20Sopenharmony_ci	queue_delayed_work(priv->workqueue, &priv->scan.timeout,
418c2ecf20Sopenharmony_ci			   msecs_to_jiffies(tmo));
428c2ecf20Sopenharmony_ci	ret = wsm_scan(priv, scan);
438c2ecf20Sopenharmony_ci	if (ret) {
448c2ecf20Sopenharmony_ci		atomic_set(&priv->scan.in_progress, 0);
458c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&priv->scan.timeout);
468c2ecf20Sopenharmony_ci		cw1200_scan_restart_delayed(priv);
478c2ecf20Sopenharmony_ci	}
488c2ecf20Sopenharmony_ci	return ret;
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ciint cw1200_hw_scan(struct ieee80211_hw *hw,
528c2ecf20Sopenharmony_ci		   struct ieee80211_vif *vif,
538c2ecf20Sopenharmony_ci		   struct ieee80211_scan_request *hw_req)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	struct cw1200_common *priv = hw->priv;
568c2ecf20Sopenharmony_ci	struct cfg80211_scan_request *req = &hw_req->req;
578c2ecf20Sopenharmony_ci	struct wsm_template_frame frame = {
588c2ecf20Sopenharmony_ci		.frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
598c2ecf20Sopenharmony_ci	};
608c2ecf20Sopenharmony_ci	int i, ret;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	if (!priv->vif)
638c2ecf20Sopenharmony_ci		return -EINVAL;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	/* Scan when P2P_GO corrupt firmware MiniAP mode */
668c2ecf20Sopenharmony_ci	if (priv->join_status == CW1200_JOIN_STATUS_AP)
678c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	if (req->n_ssids == 1 && !req->ssids[0].ssid_len)
708c2ecf20Sopenharmony_ci		req->n_ssids = 0;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n",
738c2ecf20Sopenharmony_ci		  req->n_ssids);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS)
768c2ecf20Sopenharmony_ci		return -EINVAL;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0,
798c2ecf20Sopenharmony_ci		req->ie_len);
808c2ecf20Sopenharmony_ci	if (!frame.skb)
818c2ecf20Sopenharmony_ci		return -ENOMEM;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	if (req->ie_len)
848c2ecf20Sopenharmony_ci		skb_put_data(frame.skb, req->ie, req->ie_len);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	/* will be unlocked in cw1200_scan_work() */
878c2ecf20Sopenharmony_ci	down(&priv->scan.lock);
888c2ecf20Sopenharmony_ci	mutex_lock(&priv->conf_mutex);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	ret = wsm_set_template_frame(priv, &frame);
918c2ecf20Sopenharmony_ci	if (!ret) {
928c2ecf20Sopenharmony_ci		/* Host want to be the probe responder. */
938c2ecf20Sopenharmony_ci		ret = wsm_set_probe_responder(priv, true);
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci	if (ret) {
968c2ecf20Sopenharmony_ci		mutex_unlock(&priv->conf_mutex);
978c2ecf20Sopenharmony_ci		up(&priv->scan.lock);
988c2ecf20Sopenharmony_ci		dev_kfree_skb(frame.skb);
998c2ecf20Sopenharmony_ci		return ret;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	wsm_lock_tx(priv);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	BUG_ON(priv->scan.req);
1058c2ecf20Sopenharmony_ci	priv->scan.req = req;
1068c2ecf20Sopenharmony_ci	priv->scan.n_ssids = 0;
1078c2ecf20Sopenharmony_ci	priv->scan.status = 0;
1088c2ecf20Sopenharmony_ci	priv->scan.begin = &req->channels[0];
1098c2ecf20Sopenharmony_ci	priv->scan.curr = priv->scan.begin;
1108c2ecf20Sopenharmony_ci	priv->scan.end = &req->channels[req->n_channels];
1118c2ecf20Sopenharmony_ci	priv->scan.output_power = priv->output_power;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	for (i = 0; i < req->n_ssids; ++i) {
1148c2ecf20Sopenharmony_ci		struct wsm_ssid *dst = &priv->scan.ssids[priv->scan.n_ssids];
1158c2ecf20Sopenharmony_ci		memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid));
1168c2ecf20Sopenharmony_ci		dst->length = req->ssids[i].ssid_len;
1178c2ecf20Sopenharmony_ci		++priv->scan.n_ssids;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	mutex_unlock(&priv->conf_mutex);
1218c2ecf20Sopenharmony_ci	dev_kfree_skb(frame.skb);
1228c2ecf20Sopenharmony_ci	queue_work(priv->workqueue, &priv->scan.work);
1238c2ecf20Sopenharmony_ci	return 0;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_civoid cw1200_scan_work(struct work_struct *work)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	struct cw1200_common *priv = container_of(work, struct cw1200_common,
1298c2ecf20Sopenharmony_ci							scan.work);
1308c2ecf20Sopenharmony_ci	struct ieee80211_channel **it;
1318c2ecf20Sopenharmony_ci	struct wsm_scan scan = {
1328c2ecf20Sopenharmony_ci		.type = WSM_SCAN_TYPE_FOREGROUND,
1338c2ecf20Sopenharmony_ci		.flags = WSM_SCAN_FLAG_SPLIT_METHOD,
1348c2ecf20Sopenharmony_ci	};
1358c2ecf20Sopenharmony_ci	bool first_run = (priv->scan.begin == priv->scan.curr &&
1368c2ecf20Sopenharmony_ci			  priv->scan.begin != priv->scan.end);
1378c2ecf20Sopenharmony_ci	int i;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	if (first_run) {
1408c2ecf20Sopenharmony_ci		/* Firmware gets crazy if scan request is sent
1418c2ecf20Sopenharmony_ci		 * when STA is joined but not yet associated.
1428c2ecf20Sopenharmony_ci		 * Force unjoin in this case.
1438c2ecf20Sopenharmony_ci		 */
1448c2ecf20Sopenharmony_ci		if (cancel_delayed_work_sync(&priv->join_timeout) > 0)
1458c2ecf20Sopenharmony_ci			cw1200_join_timeout(&priv->join_timeout.work);
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	mutex_lock(&priv->conf_mutex);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (first_run) {
1518c2ecf20Sopenharmony_ci		if (priv->join_status == CW1200_JOIN_STATUS_STA &&
1528c2ecf20Sopenharmony_ci		    !(priv->powersave_mode.mode & WSM_PSM_PS)) {
1538c2ecf20Sopenharmony_ci			struct wsm_set_pm pm = priv->powersave_mode;
1548c2ecf20Sopenharmony_ci			pm.mode = WSM_PSM_PS;
1558c2ecf20Sopenharmony_ci			cw1200_set_pm(priv, &pm);
1568c2ecf20Sopenharmony_ci		} else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
1578c2ecf20Sopenharmony_ci			/* FW bug: driver has to restart p2p-dev mode
1588c2ecf20Sopenharmony_ci			 * after scan
1598c2ecf20Sopenharmony_ci			 */
1608c2ecf20Sopenharmony_ci			cw1200_disable_listening(priv);
1618c2ecf20Sopenharmony_ci		}
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	if (!priv->scan.req || (priv->scan.curr == priv->scan.end)) {
1658c2ecf20Sopenharmony_ci		struct cfg80211_scan_info info = {
1668c2ecf20Sopenharmony_ci			.aborted = priv->scan.status ? 1 : 0,
1678c2ecf20Sopenharmony_ci		};
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci		if (priv->scan.output_power != priv->output_power)
1708c2ecf20Sopenharmony_ci			wsm_set_output_power(priv, priv->output_power * 10);
1718c2ecf20Sopenharmony_ci		if (priv->join_status == CW1200_JOIN_STATUS_STA &&
1728c2ecf20Sopenharmony_ci		    !(priv->powersave_mode.mode & WSM_PSM_PS))
1738c2ecf20Sopenharmony_ci			cw1200_set_pm(priv, &priv->powersave_mode);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci		if (priv->scan.status < 0)
1768c2ecf20Sopenharmony_ci			wiphy_warn(priv->hw->wiphy,
1778c2ecf20Sopenharmony_ci				   "[SCAN] Scan failed (%d).\n",
1788c2ecf20Sopenharmony_ci				   priv->scan.status);
1798c2ecf20Sopenharmony_ci		else if (priv->scan.req)
1808c2ecf20Sopenharmony_ci			wiphy_dbg(priv->hw->wiphy,
1818c2ecf20Sopenharmony_ci				  "[SCAN] Scan completed.\n");
1828c2ecf20Sopenharmony_ci		else
1838c2ecf20Sopenharmony_ci			wiphy_dbg(priv->hw->wiphy,
1848c2ecf20Sopenharmony_ci				  "[SCAN] Scan canceled.\n");
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci		priv->scan.req = NULL;
1878c2ecf20Sopenharmony_ci		cw1200_scan_restart_delayed(priv);
1888c2ecf20Sopenharmony_ci		wsm_unlock_tx(priv);
1898c2ecf20Sopenharmony_ci		mutex_unlock(&priv->conf_mutex);
1908c2ecf20Sopenharmony_ci		ieee80211_scan_completed(priv->hw, &info);
1918c2ecf20Sopenharmony_ci		up(&priv->scan.lock);
1928c2ecf20Sopenharmony_ci		return;
1938c2ecf20Sopenharmony_ci	} else {
1948c2ecf20Sopenharmony_ci		struct ieee80211_channel *first = *priv->scan.curr;
1958c2ecf20Sopenharmony_ci		for (it = priv->scan.curr + 1, i = 1;
1968c2ecf20Sopenharmony_ci		     it != priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS;
1978c2ecf20Sopenharmony_ci		     ++it, ++i) {
1988c2ecf20Sopenharmony_ci			if ((*it)->band != first->band)
1998c2ecf20Sopenharmony_ci				break;
2008c2ecf20Sopenharmony_ci			if (((*it)->flags ^ first->flags) &
2018c2ecf20Sopenharmony_ci					IEEE80211_CHAN_NO_IR)
2028c2ecf20Sopenharmony_ci				break;
2038c2ecf20Sopenharmony_ci			if (!(first->flags & IEEE80211_CHAN_NO_IR) &&
2048c2ecf20Sopenharmony_ci			    (*it)->max_power != first->max_power)
2058c2ecf20Sopenharmony_ci				break;
2068c2ecf20Sopenharmony_ci		}
2078c2ecf20Sopenharmony_ci		scan.band = first->band;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci		if (priv->scan.req->no_cck)
2108c2ecf20Sopenharmony_ci			scan.max_tx_rate = WSM_TRANSMIT_RATE_6;
2118c2ecf20Sopenharmony_ci		else
2128c2ecf20Sopenharmony_ci			scan.max_tx_rate = WSM_TRANSMIT_RATE_1;
2138c2ecf20Sopenharmony_ci		scan.num_probes =
2148c2ecf20Sopenharmony_ci			(first->flags & IEEE80211_CHAN_NO_IR) ? 0 : 2;
2158c2ecf20Sopenharmony_ci		scan.num_ssids = priv->scan.n_ssids;
2168c2ecf20Sopenharmony_ci		scan.ssids = &priv->scan.ssids[0];
2178c2ecf20Sopenharmony_ci		scan.num_channels = it - priv->scan.curr;
2188c2ecf20Sopenharmony_ci		/* TODO: Is it optimal? */
2198c2ecf20Sopenharmony_ci		scan.probe_delay = 100;
2208c2ecf20Sopenharmony_ci		/* It is not stated in WSM specification, however
2218c2ecf20Sopenharmony_ci		 * FW team says that driver may not use FG scan
2228c2ecf20Sopenharmony_ci		 * when joined.
2238c2ecf20Sopenharmony_ci		 */
2248c2ecf20Sopenharmony_ci		if (priv->join_status == CW1200_JOIN_STATUS_STA) {
2258c2ecf20Sopenharmony_ci			scan.type = WSM_SCAN_TYPE_BACKGROUND;
2268c2ecf20Sopenharmony_ci			scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
2278c2ecf20Sopenharmony_ci		}
2288c2ecf20Sopenharmony_ci		scan.ch = kcalloc(it - priv->scan.curr,
2298c2ecf20Sopenharmony_ci				  sizeof(struct wsm_scan_ch),
2308c2ecf20Sopenharmony_ci				  GFP_KERNEL);
2318c2ecf20Sopenharmony_ci		if (!scan.ch) {
2328c2ecf20Sopenharmony_ci			priv->scan.status = -ENOMEM;
2338c2ecf20Sopenharmony_ci			goto fail;
2348c2ecf20Sopenharmony_ci		}
2358c2ecf20Sopenharmony_ci		for (i = 0; i < scan.num_channels; ++i) {
2368c2ecf20Sopenharmony_ci			scan.ch[i].number = priv->scan.curr[i]->hw_value;
2378c2ecf20Sopenharmony_ci			if (priv->scan.curr[i]->flags & IEEE80211_CHAN_NO_IR) {
2388c2ecf20Sopenharmony_ci				scan.ch[i].min_chan_time = 50;
2398c2ecf20Sopenharmony_ci				scan.ch[i].max_chan_time = 100;
2408c2ecf20Sopenharmony_ci			} else {
2418c2ecf20Sopenharmony_ci				scan.ch[i].min_chan_time = 10;
2428c2ecf20Sopenharmony_ci				scan.ch[i].max_chan_time = 25;
2438c2ecf20Sopenharmony_ci			}
2448c2ecf20Sopenharmony_ci		}
2458c2ecf20Sopenharmony_ci		if (!(first->flags & IEEE80211_CHAN_NO_IR) &&
2468c2ecf20Sopenharmony_ci		    priv->scan.output_power != first->max_power) {
2478c2ecf20Sopenharmony_ci			priv->scan.output_power = first->max_power;
2488c2ecf20Sopenharmony_ci			wsm_set_output_power(priv,
2498c2ecf20Sopenharmony_ci					     priv->scan.output_power * 10);
2508c2ecf20Sopenharmony_ci		}
2518c2ecf20Sopenharmony_ci		priv->scan.status = cw1200_scan_start(priv, &scan);
2528c2ecf20Sopenharmony_ci		kfree(scan.ch);
2538c2ecf20Sopenharmony_ci		if (priv->scan.status)
2548c2ecf20Sopenharmony_ci			goto fail;
2558c2ecf20Sopenharmony_ci		priv->scan.curr = it;
2568c2ecf20Sopenharmony_ci	}
2578c2ecf20Sopenharmony_ci	mutex_unlock(&priv->conf_mutex);
2588c2ecf20Sopenharmony_ci	return;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cifail:
2618c2ecf20Sopenharmony_ci	priv->scan.curr = priv->scan.end;
2628c2ecf20Sopenharmony_ci	mutex_unlock(&priv->conf_mutex);
2638c2ecf20Sopenharmony_ci	queue_work(priv->workqueue, &priv->scan.work);
2648c2ecf20Sopenharmony_ci	return;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic void cw1200_scan_restart_delayed(struct cw1200_common *priv)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	/* FW bug: driver has to restart p2p-dev mode after scan. */
2708c2ecf20Sopenharmony_ci	if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
2718c2ecf20Sopenharmony_ci		cw1200_enable_listening(priv);
2728c2ecf20Sopenharmony_ci		cw1200_update_filtering(priv);
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (priv->delayed_unjoin) {
2768c2ecf20Sopenharmony_ci		priv->delayed_unjoin = false;
2778c2ecf20Sopenharmony_ci		if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
2788c2ecf20Sopenharmony_ci			wsm_unlock_tx(priv);
2798c2ecf20Sopenharmony_ci	} else if (priv->delayed_link_loss) {
2808c2ecf20Sopenharmony_ci			wiphy_dbg(priv->hw->wiphy, "[CQM] Requeue BSS loss.\n");
2818c2ecf20Sopenharmony_ci			priv->delayed_link_loss = 0;
2828c2ecf20Sopenharmony_ci			cw1200_cqm_bssloss_sm(priv, 1, 0, 0);
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistatic void cw1200_scan_complete(struct cw1200_common *priv)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	queue_delayed_work(priv->workqueue, &priv->clear_recent_scan_work, HZ);
2898c2ecf20Sopenharmony_ci	if (priv->scan.direct_probe) {
2908c2ecf20Sopenharmony_ci		wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe complete.\n");
2918c2ecf20Sopenharmony_ci		cw1200_scan_restart_delayed(priv);
2928c2ecf20Sopenharmony_ci		priv->scan.direct_probe = 0;
2938c2ecf20Sopenharmony_ci		up(&priv->scan.lock);
2948c2ecf20Sopenharmony_ci		wsm_unlock_tx(priv);
2958c2ecf20Sopenharmony_ci	} else {
2968c2ecf20Sopenharmony_ci		cw1200_scan_work(&priv->scan.work);
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_civoid cw1200_scan_failed_cb(struct cw1200_common *priv)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
3038c2ecf20Sopenharmony_ci		/* STA is stopped. */
3048c2ecf20Sopenharmony_ci		return;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) {
3078c2ecf20Sopenharmony_ci		priv->scan.status = -EIO;
3088c2ecf20Sopenharmony_ci		queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0);
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_civoid cw1200_scan_complete_cb(struct cw1200_common *priv,
3148c2ecf20Sopenharmony_ci				struct wsm_scan_complete *arg)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
3178c2ecf20Sopenharmony_ci		/* STA is stopped. */
3188c2ecf20Sopenharmony_ci		return;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) {
3218c2ecf20Sopenharmony_ci		priv->scan.status = 1;
3228c2ecf20Sopenharmony_ci		queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0);
3238c2ecf20Sopenharmony_ci	}
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_civoid cw1200_clear_recent_scan_work(struct work_struct *work)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	struct cw1200_common *priv =
3298c2ecf20Sopenharmony_ci		container_of(work, struct cw1200_common,
3308c2ecf20Sopenharmony_ci			     clear_recent_scan_work.work);
3318c2ecf20Sopenharmony_ci	atomic_xchg(&priv->recent_scan, 0);
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_civoid cw1200_scan_timeout(struct work_struct *work)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	struct cw1200_common *priv =
3378c2ecf20Sopenharmony_ci		container_of(work, struct cw1200_common, scan.timeout.work);
3388c2ecf20Sopenharmony_ci	if (atomic_xchg(&priv->scan.in_progress, 0)) {
3398c2ecf20Sopenharmony_ci		if (priv->scan.status > 0) {
3408c2ecf20Sopenharmony_ci			priv->scan.status = 0;
3418c2ecf20Sopenharmony_ci		} else if (!priv->scan.status) {
3428c2ecf20Sopenharmony_ci			wiphy_warn(priv->hw->wiphy,
3438c2ecf20Sopenharmony_ci				   "Timeout waiting for scan complete notification.\n");
3448c2ecf20Sopenharmony_ci			priv->scan.status = -ETIMEDOUT;
3458c2ecf20Sopenharmony_ci			priv->scan.curr = priv->scan.end;
3468c2ecf20Sopenharmony_ci			wsm_stop_scan(priv);
3478c2ecf20Sopenharmony_ci		}
3488c2ecf20Sopenharmony_ci		cw1200_scan_complete(priv);
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_civoid cw1200_probe_work(struct work_struct *work)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	struct cw1200_common *priv =
3558c2ecf20Sopenharmony_ci		container_of(work, struct cw1200_common, scan.probe_work.work);
3568c2ecf20Sopenharmony_ci	u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id);
3578c2ecf20Sopenharmony_ci	struct cw1200_queue *queue = &priv->tx_queue[queue_id];
3588c2ecf20Sopenharmony_ci	const struct cw1200_txpriv *txpriv;
3598c2ecf20Sopenharmony_ci	struct wsm_tx *wsm;
3608c2ecf20Sopenharmony_ci	struct wsm_template_frame frame = {
3618c2ecf20Sopenharmony_ci		.frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
3628c2ecf20Sopenharmony_ci	};
3638c2ecf20Sopenharmony_ci	struct wsm_ssid ssids[1] = {{
3648c2ecf20Sopenharmony_ci		.length = 0,
3658c2ecf20Sopenharmony_ci	} };
3668c2ecf20Sopenharmony_ci	struct wsm_scan_ch ch[1] = {{
3678c2ecf20Sopenharmony_ci		.min_chan_time = 0,
3688c2ecf20Sopenharmony_ci		.max_chan_time = 10,
3698c2ecf20Sopenharmony_ci	} };
3708c2ecf20Sopenharmony_ci	struct wsm_scan scan = {
3718c2ecf20Sopenharmony_ci		.type = WSM_SCAN_TYPE_FOREGROUND,
3728c2ecf20Sopenharmony_ci		.num_probes = 1,
3738c2ecf20Sopenharmony_ci		.probe_delay = 0,
3748c2ecf20Sopenharmony_ci		.num_channels = 1,
3758c2ecf20Sopenharmony_ci		.ssids = ssids,
3768c2ecf20Sopenharmony_ci		.ch = ch,
3778c2ecf20Sopenharmony_ci	};
3788c2ecf20Sopenharmony_ci	u8 *ies;
3798c2ecf20Sopenharmony_ci	size_t ies_len;
3808c2ecf20Sopenharmony_ci	int ret;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe work.\n");
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	mutex_lock(&priv->conf_mutex);
3858c2ecf20Sopenharmony_ci	if (down_trylock(&priv->scan.lock)) {
3868c2ecf20Sopenharmony_ci		/* Scan is already in progress. Requeue self. */
3878c2ecf20Sopenharmony_ci		schedule();
3888c2ecf20Sopenharmony_ci		queue_delayed_work(priv->workqueue, &priv->scan.probe_work,
3898c2ecf20Sopenharmony_ci				   msecs_to_jiffies(100));
3908c2ecf20Sopenharmony_ci		mutex_unlock(&priv->conf_mutex);
3918c2ecf20Sopenharmony_ci		return;
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	/* Make sure we still have a pending probe req */
3958c2ecf20Sopenharmony_ci	if (cw1200_queue_get_skb(queue,	priv->pending_frame_id,
3968c2ecf20Sopenharmony_ci				 &frame.skb, &txpriv)) {
3978c2ecf20Sopenharmony_ci		up(&priv->scan.lock);
3988c2ecf20Sopenharmony_ci		mutex_unlock(&priv->conf_mutex);
3998c2ecf20Sopenharmony_ci		wsm_unlock_tx(priv);
4008c2ecf20Sopenharmony_ci		return;
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci	wsm = (struct wsm_tx *)frame.skb->data;
4038c2ecf20Sopenharmony_ci	scan.max_tx_rate = wsm->max_tx_rate;
4048c2ecf20Sopenharmony_ci	scan.band = (priv->channel->band == NL80211_BAND_5GHZ) ?
4058c2ecf20Sopenharmony_ci		WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
4068c2ecf20Sopenharmony_ci	if (priv->join_status == CW1200_JOIN_STATUS_STA ||
4078c2ecf20Sopenharmony_ci	    priv->join_status == CW1200_JOIN_STATUS_IBSS) {
4088c2ecf20Sopenharmony_ci		scan.type = WSM_SCAN_TYPE_BACKGROUND;
4098c2ecf20Sopenharmony_ci		scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
4108c2ecf20Sopenharmony_ci	}
4118c2ecf20Sopenharmony_ci	ch[0].number = priv->channel->hw_value;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	skb_pull(frame.skb, txpriv->offset);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)];
4168c2ecf20Sopenharmony_ci	ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	if (ies_len) {
4198c2ecf20Sopenharmony_ci		u8 *ssidie =
4208c2ecf20Sopenharmony_ci			(u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len);
4218c2ecf20Sopenharmony_ci		if (ssidie && ssidie[1] && ssidie[1] <= sizeof(ssids[0].ssid)) {
4228c2ecf20Sopenharmony_ci			u8 *nextie = &ssidie[2 + ssidie[1]];
4238c2ecf20Sopenharmony_ci			/* Remove SSID from the IE list. It has to be provided
4248c2ecf20Sopenharmony_ci			 * as a separate argument in cw1200_scan_start call
4258c2ecf20Sopenharmony_ci			 */
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci			/* Store SSID localy */
4288c2ecf20Sopenharmony_ci			ssids[0].length = ssidie[1];
4298c2ecf20Sopenharmony_ci			memcpy(ssids[0].ssid, &ssidie[2], ssids[0].length);
4308c2ecf20Sopenharmony_ci			scan.num_ssids = 1;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci			/* Remove SSID from IE list */
4338c2ecf20Sopenharmony_ci			ssidie[1] = 0;
4348c2ecf20Sopenharmony_ci			memmove(&ssidie[2], nextie, &ies[ies_len] - nextie);
4358c2ecf20Sopenharmony_ci			skb_trim(frame.skb, frame.skb->len - ssids[0].length);
4368c2ecf20Sopenharmony_ci		}
4378c2ecf20Sopenharmony_ci	}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	/* FW bug: driver has to restart p2p-dev mode after scan */
4408c2ecf20Sopenharmony_ci	if (priv->join_status == CW1200_JOIN_STATUS_MONITOR)
4418c2ecf20Sopenharmony_ci		cw1200_disable_listening(priv);
4428c2ecf20Sopenharmony_ci	ret = wsm_set_template_frame(priv, &frame);
4438c2ecf20Sopenharmony_ci	priv->scan.direct_probe = 1;
4448c2ecf20Sopenharmony_ci	if (!ret) {
4458c2ecf20Sopenharmony_ci		wsm_flush_tx(priv);
4468c2ecf20Sopenharmony_ci		ret = cw1200_scan_start(priv, &scan);
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci	mutex_unlock(&priv->conf_mutex);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	skb_push(frame.skb, txpriv->offset);
4518c2ecf20Sopenharmony_ci	if (!ret)
4528c2ecf20Sopenharmony_ci		IEEE80211_SKB_CB(frame.skb)->flags |= IEEE80211_TX_STAT_ACK;
4538c2ecf20Sopenharmony_ci	BUG_ON(cw1200_queue_remove(queue, priv->pending_frame_id));
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	if (ret) {
4568c2ecf20Sopenharmony_ci		priv->scan.direct_probe = 0;
4578c2ecf20Sopenharmony_ci		up(&priv->scan.lock);
4588c2ecf20Sopenharmony_ci		wsm_unlock_tx(priv);
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	return;
4628c2ecf20Sopenharmony_ci}
463