xref: /openbmc/linux/drivers/net/wireless/st/cw1200/scan.c (revision d2912cb15bdda8ba4a5dd73396ad62641af2f520)
1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2560424e9SKalle Valo /*
3560424e9SKalle Valo  * Scan implementation for ST-Ericsson CW1200 mac80211 drivers
4560424e9SKalle Valo  *
5560424e9SKalle Valo  * Copyright (c) 2010, ST-Ericsson
6560424e9SKalle Valo  * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
7560424e9SKalle Valo  */
8560424e9SKalle Valo 
9560424e9SKalle Valo #include <linux/sched.h>
10560424e9SKalle Valo #include "cw1200.h"
11560424e9SKalle Valo #include "scan.h"
12560424e9SKalle Valo #include "sta.h"
13560424e9SKalle Valo #include "pm.h"
14560424e9SKalle Valo 
15560424e9SKalle Valo static void cw1200_scan_restart_delayed(struct cw1200_common *priv);
16560424e9SKalle Valo 
17560424e9SKalle Valo static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan)
18560424e9SKalle Valo {
19560424e9SKalle Valo 	int ret, i;
20560424e9SKalle Valo 	int tmo = 2000;
21560424e9SKalle Valo 
22560424e9SKalle Valo 	switch (priv->join_status) {
23560424e9SKalle Valo 	case CW1200_JOIN_STATUS_PRE_STA:
24560424e9SKalle Valo 	case CW1200_JOIN_STATUS_JOINING:
25560424e9SKalle Valo 		return -EBUSY;
26560424e9SKalle Valo 	default:
27560424e9SKalle Valo 		break;
28560424e9SKalle Valo 	}
29560424e9SKalle Valo 
30560424e9SKalle Valo 	wiphy_dbg(priv->hw->wiphy, "[SCAN] hw req, type %d, %d channels, flags: 0x%x.\n",
31560424e9SKalle Valo 		  scan->type, scan->num_channels, scan->flags);
32560424e9SKalle Valo 
33560424e9SKalle Valo 	for (i = 0; i < scan->num_channels; ++i)
34560424e9SKalle Valo 		tmo += scan->ch[i].max_chan_time + 10;
35560424e9SKalle Valo 
36560424e9SKalle Valo 	cancel_delayed_work_sync(&priv->clear_recent_scan_work);
37560424e9SKalle Valo 	atomic_set(&priv->scan.in_progress, 1);
38560424e9SKalle Valo 	atomic_set(&priv->recent_scan, 1);
39560424e9SKalle Valo 	cw1200_pm_stay_awake(&priv->pm_state, msecs_to_jiffies(tmo));
40560424e9SKalle Valo 	queue_delayed_work(priv->workqueue, &priv->scan.timeout,
41560424e9SKalle Valo 			   msecs_to_jiffies(tmo));
42560424e9SKalle Valo 	ret = wsm_scan(priv, scan);
43560424e9SKalle Valo 	if (ret) {
44560424e9SKalle Valo 		atomic_set(&priv->scan.in_progress, 0);
45560424e9SKalle Valo 		cancel_delayed_work_sync(&priv->scan.timeout);
46560424e9SKalle Valo 		cw1200_scan_restart_delayed(priv);
47560424e9SKalle Valo 	}
48560424e9SKalle Valo 	return ret;
49560424e9SKalle Valo }
50560424e9SKalle Valo 
51560424e9SKalle Valo int cw1200_hw_scan(struct ieee80211_hw *hw,
52560424e9SKalle Valo 		   struct ieee80211_vif *vif,
53560424e9SKalle Valo 		   struct ieee80211_scan_request *hw_req)
54560424e9SKalle Valo {
55560424e9SKalle Valo 	struct cw1200_common *priv = hw->priv;
56560424e9SKalle Valo 	struct cfg80211_scan_request *req = &hw_req->req;
57560424e9SKalle Valo 	struct wsm_template_frame frame = {
58560424e9SKalle Valo 		.frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
59560424e9SKalle Valo 	};
60560424e9SKalle Valo 	int i, ret;
61560424e9SKalle Valo 
62560424e9SKalle Valo 	if (!priv->vif)
63560424e9SKalle Valo 		return -EINVAL;
64560424e9SKalle Valo 
65560424e9SKalle Valo 	/* Scan when P2P_GO corrupt firmware MiniAP mode */
66560424e9SKalle Valo 	if (priv->join_status == CW1200_JOIN_STATUS_AP)
67560424e9SKalle Valo 		return -EOPNOTSUPP;
68560424e9SKalle Valo 
69560424e9SKalle Valo 	if (req->n_ssids == 1 && !req->ssids[0].ssid_len)
70560424e9SKalle Valo 		req->n_ssids = 0;
71560424e9SKalle Valo 
72560424e9SKalle Valo 	wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n",
73560424e9SKalle Valo 		  req->n_ssids);
74560424e9SKalle Valo 
75560424e9SKalle Valo 	if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS)
76560424e9SKalle Valo 		return -EINVAL;
77560424e9SKalle Valo 
784f68ef64SJia-Ju Bai 	/* will be unlocked in cw1200_scan_work() */
794f68ef64SJia-Ju Bai 	down(&priv->scan.lock);
804f68ef64SJia-Ju Bai 	mutex_lock(&priv->conf_mutex);
814f68ef64SJia-Ju Bai 
82560424e9SKalle Valo 	frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0,
83560424e9SKalle Valo 		req->ie_len);
8451c8d241SWei Yongjun 	if (!frame.skb) {
8551c8d241SWei Yongjun 		mutex_unlock(&priv->conf_mutex);
8651c8d241SWei Yongjun 		up(&priv->scan.lock);
87560424e9SKalle Valo 		return -ENOMEM;
8851c8d241SWei Yongjun 	}
89560424e9SKalle Valo 
90560424e9SKalle Valo 	if (req->ie_len)
9159ae1d12SJohannes Berg 		skb_put_data(frame.skb, req->ie, req->ie_len);
92560424e9SKalle Valo 
93560424e9SKalle Valo 	ret = wsm_set_template_frame(priv, &frame);
94560424e9SKalle Valo 	if (!ret) {
95560424e9SKalle Valo 		/* Host want to be the probe responder. */
96560424e9SKalle Valo 		ret = wsm_set_probe_responder(priv, true);
97560424e9SKalle Valo 	}
98560424e9SKalle Valo 	if (ret) {
994f68ef64SJia-Ju Bai 		dev_kfree_skb(frame.skb);
100560424e9SKalle Valo 		mutex_unlock(&priv->conf_mutex);
101560424e9SKalle Valo 		up(&priv->scan.lock);
102560424e9SKalle Valo 		return ret;
103560424e9SKalle Valo 	}
104560424e9SKalle Valo 
105560424e9SKalle Valo 	wsm_lock_tx(priv);
106560424e9SKalle Valo 
107560424e9SKalle Valo 	BUG_ON(priv->scan.req);
108560424e9SKalle Valo 	priv->scan.req = req;
109560424e9SKalle Valo 	priv->scan.n_ssids = 0;
110560424e9SKalle Valo 	priv->scan.status = 0;
111560424e9SKalle Valo 	priv->scan.begin = &req->channels[0];
112560424e9SKalle Valo 	priv->scan.curr = priv->scan.begin;
113560424e9SKalle Valo 	priv->scan.end = &req->channels[req->n_channels];
114560424e9SKalle Valo 	priv->scan.output_power = priv->output_power;
115560424e9SKalle Valo 
116560424e9SKalle Valo 	for (i = 0; i < req->n_ssids; ++i) {
117560424e9SKalle Valo 		struct wsm_ssid *dst = &priv->scan.ssids[priv->scan.n_ssids];
118560424e9SKalle Valo 		memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid));
119560424e9SKalle Valo 		dst->length = req->ssids[i].ssid_len;
120560424e9SKalle Valo 		++priv->scan.n_ssids;
121560424e9SKalle Valo 	}
122560424e9SKalle Valo 
123560424e9SKalle Valo 	if (frame.skb)
124560424e9SKalle Valo 		dev_kfree_skb(frame.skb);
1254f68ef64SJia-Ju Bai 	mutex_unlock(&priv->conf_mutex);
126560424e9SKalle Valo 	queue_work(priv->workqueue, &priv->scan.work);
127560424e9SKalle Valo 	return 0;
128560424e9SKalle Valo }
129560424e9SKalle Valo 
130560424e9SKalle Valo void cw1200_scan_work(struct work_struct *work)
131560424e9SKalle Valo {
132560424e9SKalle Valo 	struct cw1200_common *priv = container_of(work, struct cw1200_common,
133560424e9SKalle Valo 							scan.work);
134560424e9SKalle Valo 	struct ieee80211_channel **it;
135560424e9SKalle Valo 	struct wsm_scan scan = {
136560424e9SKalle Valo 		.type = WSM_SCAN_TYPE_FOREGROUND,
137560424e9SKalle Valo 		.flags = WSM_SCAN_FLAG_SPLIT_METHOD,
138560424e9SKalle Valo 	};
139560424e9SKalle Valo 	bool first_run = (priv->scan.begin == priv->scan.curr &&
140560424e9SKalle Valo 			  priv->scan.begin != priv->scan.end);
141560424e9SKalle Valo 	int i;
142560424e9SKalle Valo 
143560424e9SKalle Valo 	if (first_run) {
144560424e9SKalle Valo 		/* Firmware gets crazy if scan request is sent
145560424e9SKalle Valo 		 * when STA is joined but not yet associated.
146560424e9SKalle Valo 		 * Force unjoin in this case.
147560424e9SKalle Valo 		 */
148560424e9SKalle Valo 		if (cancel_delayed_work_sync(&priv->join_timeout) > 0)
149560424e9SKalle Valo 			cw1200_join_timeout(&priv->join_timeout.work);
150560424e9SKalle Valo 	}
151560424e9SKalle Valo 
152560424e9SKalle Valo 	mutex_lock(&priv->conf_mutex);
153560424e9SKalle Valo 
154560424e9SKalle Valo 	if (first_run) {
155560424e9SKalle Valo 		if (priv->join_status == CW1200_JOIN_STATUS_STA &&
156560424e9SKalle Valo 		    !(priv->powersave_mode.mode & WSM_PSM_PS)) {
157560424e9SKalle Valo 			struct wsm_set_pm pm = priv->powersave_mode;
158560424e9SKalle Valo 			pm.mode = WSM_PSM_PS;
159560424e9SKalle Valo 			cw1200_set_pm(priv, &pm);
160560424e9SKalle Valo 		} else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
161560424e9SKalle Valo 			/* FW bug: driver has to restart p2p-dev mode
162560424e9SKalle Valo 			 * after scan
163560424e9SKalle Valo 			 */
164560424e9SKalle Valo 			cw1200_disable_listening(priv);
165560424e9SKalle Valo 		}
166560424e9SKalle Valo 	}
167560424e9SKalle Valo 
168560424e9SKalle Valo 	if (!priv->scan.req || (priv->scan.curr == priv->scan.end)) {
1697947d3e0SAvraham Stern 		struct cfg80211_scan_info info = {
1707947d3e0SAvraham Stern 			.aborted = priv->scan.status ? 1 : 0,
1717947d3e0SAvraham Stern 		};
1727947d3e0SAvraham Stern 
173560424e9SKalle Valo 		if (priv->scan.output_power != priv->output_power)
174560424e9SKalle Valo 			wsm_set_output_power(priv, priv->output_power * 10);
175560424e9SKalle Valo 		if (priv->join_status == CW1200_JOIN_STATUS_STA &&
176560424e9SKalle Valo 		    !(priv->powersave_mode.mode & WSM_PSM_PS))
177560424e9SKalle Valo 			cw1200_set_pm(priv, &priv->powersave_mode);
178560424e9SKalle Valo 
179560424e9SKalle Valo 		if (priv->scan.status < 0)
180560424e9SKalle Valo 			wiphy_warn(priv->hw->wiphy,
181560424e9SKalle Valo 				   "[SCAN] Scan failed (%d).\n",
182560424e9SKalle Valo 				   priv->scan.status);
183560424e9SKalle Valo 		else if (priv->scan.req)
184560424e9SKalle Valo 			wiphy_dbg(priv->hw->wiphy,
185560424e9SKalle Valo 				  "[SCAN] Scan completed.\n");
186560424e9SKalle Valo 		else
187560424e9SKalle Valo 			wiphy_dbg(priv->hw->wiphy,
188560424e9SKalle Valo 				  "[SCAN] Scan canceled.\n");
189560424e9SKalle Valo 
190560424e9SKalle Valo 		priv->scan.req = NULL;
191560424e9SKalle Valo 		cw1200_scan_restart_delayed(priv);
192560424e9SKalle Valo 		wsm_unlock_tx(priv);
193560424e9SKalle Valo 		mutex_unlock(&priv->conf_mutex);
1947947d3e0SAvraham Stern 		ieee80211_scan_completed(priv->hw, &info);
195560424e9SKalle Valo 		up(&priv->scan.lock);
196560424e9SKalle Valo 		return;
197560424e9SKalle Valo 	} else {
198560424e9SKalle Valo 		struct ieee80211_channel *first = *priv->scan.curr;
199560424e9SKalle Valo 		for (it = priv->scan.curr + 1, i = 1;
200560424e9SKalle Valo 		     it != priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS;
201560424e9SKalle Valo 		     ++it, ++i) {
202560424e9SKalle Valo 			if ((*it)->band != first->band)
203560424e9SKalle Valo 				break;
204560424e9SKalle Valo 			if (((*it)->flags ^ first->flags) &
205560424e9SKalle Valo 					IEEE80211_CHAN_NO_IR)
206560424e9SKalle Valo 				break;
207560424e9SKalle Valo 			if (!(first->flags & IEEE80211_CHAN_NO_IR) &&
208560424e9SKalle Valo 			    (*it)->max_power != first->max_power)
209560424e9SKalle Valo 				break;
210560424e9SKalle Valo 		}
211560424e9SKalle Valo 		scan.band = first->band;
212560424e9SKalle Valo 
213560424e9SKalle Valo 		if (priv->scan.req->no_cck)
214560424e9SKalle Valo 			scan.max_tx_rate = WSM_TRANSMIT_RATE_6;
215560424e9SKalle Valo 		else
216560424e9SKalle Valo 			scan.max_tx_rate = WSM_TRANSMIT_RATE_1;
217560424e9SKalle Valo 		scan.num_probes =
218560424e9SKalle Valo 			(first->flags & IEEE80211_CHAN_NO_IR) ? 0 : 2;
219560424e9SKalle Valo 		scan.num_ssids = priv->scan.n_ssids;
220560424e9SKalle Valo 		scan.ssids = &priv->scan.ssids[0];
221560424e9SKalle Valo 		scan.num_channels = it - priv->scan.curr;
222560424e9SKalle Valo 		/* TODO: Is it optimal? */
223560424e9SKalle Valo 		scan.probe_delay = 100;
224560424e9SKalle Valo 		/* It is not stated in WSM specification, however
225560424e9SKalle Valo 		 * FW team says that driver may not use FG scan
226560424e9SKalle Valo 		 * when joined.
227560424e9SKalle Valo 		 */
228560424e9SKalle Valo 		if (priv->join_status == CW1200_JOIN_STATUS_STA) {
229560424e9SKalle Valo 			scan.type = WSM_SCAN_TYPE_BACKGROUND;
230560424e9SKalle Valo 			scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
231560424e9SKalle Valo 		}
2326396bb22SKees Cook 		scan.ch = kcalloc(it - priv->scan.curr,
2336396bb22SKees Cook 				  sizeof(struct wsm_scan_ch),
234560424e9SKalle Valo 				  GFP_KERNEL);
235560424e9SKalle Valo 		if (!scan.ch) {
236560424e9SKalle Valo 			priv->scan.status = -ENOMEM;
237560424e9SKalle Valo 			goto fail;
238560424e9SKalle Valo 		}
239560424e9SKalle Valo 		for (i = 0; i < scan.num_channels; ++i) {
240560424e9SKalle Valo 			scan.ch[i].number = priv->scan.curr[i]->hw_value;
241560424e9SKalle Valo 			if (priv->scan.curr[i]->flags & IEEE80211_CHAN_NO_IR) {
242560424e9SKalle Valo 				scan.ch[i].min_chan_time = 50;
243560424e9SKalle Valo 				scan.ch[i].max_chan_time = 100;
244560424e9SKalle Valo 			} else {
245560424e9SKalle Valo 				scan.ch[i].min_chan_time = 10;
246560424e9SKalle Valo 				scan.ch[i].max_chan_time = 25;
247560424e9SKalle Valo 			}
248560424e9SKalle Valo 		}
249560424e9SKalle Valo 		if (!(first->flags & IEEE80211_CHAN_NO_IR) &&
250560424e9SKalle Valo 		    priv->scan.output_power != first->max_power) {
251560424e9SKalle Valo 			priv->scan.output_power = first->max_power;
252560424e9SKalle Valo 			wsm_set_output_power(priv,
253560424e9SKalle Valo 					     priv->scan.output_power * 10);
254560424e9SKalle Valo 		}
255560424e9SKalle Valo 		priv->scan.status = cw1200_scan_start(priv, &scan);
256560424e9SKalle Valo 		kfree(scan.ch);
257560424e9SKalle Valo 		if (priv->scan.status)
258560424e9SKalle Valo 			goto fail;
259560424e9SKalle Valo 		priv->scan.curr = it;
260560424e9SKalle Valo 	}
261560424e9SKalle Valo 	mutex_unlock(&priv->conf_mutex);
262560424e9SKalle Valo 	return;
263560424e9SKalle Valo 
264560424e9SKalle Valo fail:
265560424e9SKalle Valo 	priv->scan.curr = priv->scan.end;
266560424e9SKalle Valo 	mutex_unlock(&priv->conf_mutex);
267560424e9SKalle Valo 	queue_work(priv->workqueue, &priv->scan.work);
268560424e9SKalle Valo 	return;
269560424e9SKalle Valo }
270560424e9SKalle Valo 
271560424e9SKalle Valo static void cw1200_scan_restart_delayed(struct cw1200_common *priv)
272560424e9SKalle Valo {
273560424e9SKalle Valo 	/* FW bug: driver has to restart p2p-dev mode after scan. */
274560424e9SKalle Valo 	if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
275560424e9SKalle Valo 		cw1200_enable_listening(priv);
276560424e9SKalle Valo 		cw1200_update_filtering(priv);
277560424e9SKalle Valo 	}
278560424e9SKalle Valo 
279560424e9SKalle Valo 	if (priv->delayed_unjoin) {
280560424e9SKalle Valo 		priv->delayed_unjoin = false;
281560424e9SKalle Valo 		if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
282560424e9SKalle Valo 			wsm_unlock_tx(priv);
283560424e9SKalle Valo 	} else if (priv->delayed_link_loss) {
284560424e9SKalle Valo 			wiphy_dbg(priv->hw->wiphy, "[CQM] Requeue BSS loss.\n");
285560424e9SKalle Valo 			priv->delayed_link_loss = 0;
286560424e9SKalle Valo 			cw1200_cqm_bssloss_sm(priv, 1, 0, 0);
287560424e9SKalle Valo 	}
288560424e9SKalle Valo }
289560424e9SKalle Valo 
290560424e9SKalle Valo static void cw1200_scan_complete(struct cw1200_common *priv)
291560424e9SKalle Valo {
292560424e9SKalle Valo 	queue_delayed_work(priv->workqueue, &priv->clear_recent_scan_work, HZ);
293560424e9SKalle Valo 	if (priv->scan.direct_probe) {
294560424e9SKalle Valo 		wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe complete.\n");
295560424e9SKalle Valo 		cw1200_scan_restart_delayed(priv);
296560424e9SKalle Valo 		priv->scan.direct_probe = 0;
297560424e9SKalle Valo 		up(&priv->scan.lock);
298560424e9SKalle Valo 		wsm_unlock_tx(priv);
299560424e9SKalle Valo 	} else {
300560424e9SKalle Valo 		cw1200_scan_work(&priv->scan.work);
301560424e9SKalle Valo 	}
302560424e9SKalle Valo }
303560424e9SKalle Valo 
304560424e9SKalle Valo void cw1200_scan_failed_cb(struct cw1200_common *priv)
305560424e9SKalle Valo {
306560424e9SKalle Valo 	if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
307560424e9SKalle Valo 		/* STA is stopped. */
308560424e9SKalle Valo 		return;
309560424e9SKalle Valo 
310560424e9SKalle Valo 	if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) {
311560424e9SKalle Valo 		priv->scan.status = -EIO;
312560424e9SKalle Valo 		queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0);
313560424e9SKalle Valo 	}
314560424e9SKalle Valo }
315560424e9SKalle Valo 
316560424e9SKalle Valo 
317560424e9SKalle Valo void cw1200_scan_complete_cb(struct cw1200_common *priv,
318560424e9SKalle Valo 				struct wsm_scan_complete *arg)
319560424e9SKalle Valo {
320560424e9SKalle Valo 	if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
321560424e9SKalle Valo 		/* STA is stopped. */
322560424e9SKalle Valo 		return;
323560424e9SKalle Valo 
324560424e9SKalle Valo 	if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) {
325560424e9SKalle Valo 		priv->scan.status = 1;
326560424e9SKalle Valo 		queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0);
327560424e9SKalle Valo 	}
328560424e9SKalle Valo }
329560424e9SKalle Valo 
330560424e9SKalle Valo void cw1200_clear_recent_scan_work(struct work_struct *work)
331560424e9SKalle Valo {
332560424e9SKalle Valo 	struct cw1200_common *priv =
333560424e9SKalle Valo 		container_of(work, struct cw1200_common,
334560424e9SKalle Valo 			     clear_recent_scan_work.work);
335560424e9SKalle Valo 	atomic_xchg(&priv->recent_scan, 0);
336560424e9SKalle Valo }
337560424e9SKalle Valo 
338560424e9SKalle Valo void cw1200_scan_timeout(struct work_struct *work)
339560424e9SKalle Valo {
340560424e9SKalle Valo 	struct cw1200_common *priv =
341560424e9SKalle Valo 		container_of(work, struct cw1200_common, scan.timeout.work);
342560424e9SKalle Valo 	if (atomic_xchg(&priv->scan.in_progress, 0)) {
343560424e9SKalle Valo 		if (priv->scan.status > 0) {
344560424e9SKalle Valo 			priv->scan.status = 0;
345560424e9SKalle Valo 		} else if (!priv->scan.status) {
346560424e9SKalle Valo 			wiphy_warn(priv->hw->wiphy,
347560424e9SKalle Valo 				   "Timeout waiting for scan complete notification.\n");
348560424e9SKalle Valo 			priv->scan.status = -ETIMEDOUT;
349560424e9SKalle Valo 			priv->scan.curr = priv->scan.end;
350560424e9SKalle Valo 			wsm_stop_scan(priv);
351560424e9SKalle Valo 		}
352560424e9SKalle Valo 		cw1200_scan_complete(priv);
353560424e9SKalle Valo 	}
354560424e9SKalle Valo }
355560424e9SKalle Valo 
356560424e9SKalle Valo void cw1200_probe_work(struct work_struct *work)
357560424e9SKalle Valo {
358560424e9SKalle Valo 	struct cw1200_common *priv =
359560424e9SKalle Valo 		container_of(work, struct cw1200_common, scan.probe_work.work);
360560424e9SKalle Valo 	u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id);
361560424e9SKalle Valo 	struct cw1200_queue *queue = &priv->tx_queue[queue_id];
362560424e9SKalle Valo 	const struct cw1200_txpriv *txpriv;
363560424e9SKalle Valo 	struct wsm_tx *wsm;
364560424e9SKalle Valo 	struct wsm_template_frame frame = {
365560424e9SKalle Valo 		.frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
366560424e9SKalle Valo 	};
367560424e9SKalle Valo 	struct wsm_ssid ssids[1] = {{
368560424e9SKalle Valo 		.length = 0,
369560424e9SKalle Valo 	} };
370560424e9SKalle Valo 	struct wsm_scan_ch ch[1] = {{
371560424e9SKalle Valo 		.min_chan_time = 0,
372560424e9SKalle Valo 		.max_chan_time = 10,
373560424e9SKalle Valo 	} };
374560424e9SKalle Valo 	struct wsm_scan scan = {
375560424e9SKalle Valo 		.type = WSM_SCAN_TYPE_FOREGROUND,
376560424e9SKalle Valo 		.num_probes = 1,
377560424e9SKalle Valo 		.probe_delay = 0,
378560424e9SKalle Valo 		.num_channels = 1,
379560424e9SKalle Valo 		.ssids = ssids,
380560424e9SKalle Valo 		.ch = ch,
381560424e9SKalle Valo 	};
382560424e9SKalle Valo 	u8 *ies;
383560424e9SKalle Valo 	size_t ies_len;
384560424e9SKalle Valo 	int ret;
385560424e9SKalle Valo 
386560424e9SKalle Valo 	wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe work.\n");
387560424e9SKalle Valo 
388560424e9SKalle Valo 	mutex_lock(&priv->conf_mutex);
389560424e9SKalle Valo 	if (down_trylock(&priv->scan.lock)) {
390560424e9SKalle Valo 		/* Scan is already in progress. Requeue self. */
391560424e9SKalle Valo 		schedule();
392560424e9SKalle Valo 		queue_delayed_work(priv->workqueue, &priv->scan.probe_work,
393560424e9SKalle Valo 				   msecs_to_jiffies(100));
394560424e9SKalle Valo 		mutex_unlock(&priv->conf_mutex);
395560424e9SKalle Valo 		return;
396560424e9SKalle Valo 	}
397560424e9SKalle Valo 
398560424e9SKalle Valo 	/* Make sure we still have a pending probe req */
399560424e9SKalle Valo 	if (cw1200_queue_get_skb(queue,	priv->pending_frame_id,
400560424e9SKalle Valo 				 &frame.skb, &txpriv)) {
401560424e9SKalle Valo 		up(&priv->scan.lock);
402560424e9SKalle Valo 		mutex_unlock(&priv->conf_mutex);
403560424e9SKalle Valo 		wsm_unlock_tx(priv);
404560424e9SKalle Valo 		return;
405560424e9SKalle Valo 	}
406560424e9SKalle Valo 	wsm = (struct wsm_tx *)frame.skb->data;
407560424e9SKalle Valo 	scan.max_tx_rate = wsm->max_tx_rate;
40857fbcce3SJohannes Berg 	scan.band = (priv->channel->band == NL80211_BAND_5GHZ) ?
409560424e9SKalle Valo 		WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
410560424e9SKalle Valo 	if (priv->join_status == CW1200_JOIN_STATUS_STA ||
411560424e9SKalle Valo 	    priv->join_status == CW1200_JOIN_STATUS_IBSS) {
412560424e9SKalle Valo 		scan.type = WSM_SCAN_TYPE_BACKGROUND;
413560424e9SKalle Valo 		scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
414560424e9SKalle Valo 	}
415560424e9SKalle Valo 	ch[0].number = priv->channel->hw_value;
416560424e9SKalle Valo 
417560424e9SKalle Valo 	skb_pull(frame.skb, txpriv->offset);
418560424e9SKalle Valo 
419560424e9SKalle Valo 	ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)];
420560424e9SKalle Valo 	ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr);
421560424e9SKalle Valo 
422560424e9SKalle Valo 	if (ies_len) {
423560424e9SKalle Valo 		u8 *ssidie =
424560424e9SKalle Valo 			(u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len);
425560424e9SKalle Valo 		if (ssidie && ssidie[1] && ssidie[1] <= sizeof(ssids[0].ssid)) {
426560424e9SKalle Valo 			u8 *nextie = &ssidie[2 + ssidie[1]];
427560424e9SKalle Valo 			/* Remove SSID from the IE list. It has to be provided
428560424e9SKalle Valo 			 * as a separate argument in cw1200_scan_start call
429560424e9SKalle Valo 			 */
430560424e9SKalle Valo 
431560424e9SKalle Valo 			/* Store SSID localy */
432560424e9SKalle Valo 			ssids[0].length = ssidie[1];
433560424e9SKalle Valo 			memcpy(ssids[0].ssid, &ssidie[2], ssids[0].length);
434560424e9SKalle Valo 			scan.num_ssids = 1;
435560424e9SKalle Valo 
436560424e9SKalle Valo 			/* Remove SSID from IE list */
437560424e9SKalle Valo 			ssidie[1] = 0;
438560424e9SKalle Valo 			memmove(&ssidie[2], nextie, &ies[ies_len] - nextie);
439560424e9SKalle Valo 			skb_trim(frame.skb, frame.skb->len - ssids[0].length);
440560424e9SKalle Valo 		}
441560424e9SKalle Valo 	}
442560424e9SKalle Valo 
443560424e9SKalle Valo 	/* FW bug: driver has to restart p2p-dev mode after scan */
444560424e9SKalle Valo 	if (priv->join_status == CW1200_JOIN_STATUS_MONITOR)
445560424e9SKalle Valo 		cw1200_disable_listening(priv);
446560424e9SKalle Valo 	ret = wsm_set_template_frame(priv, &frame);
447560424e9SKalle Valo 	priv->scan.direct_probe = 1;
448560424e9SKalle Valo 	if (!ret) {
449560424e9SKalle Valo 		wsm_flush_tx(priv);
450560424e9SKalle Valo 		ret = cw1200_scan_start(priv, &scan);
451560424e9SKalle Valo 	}
452560424e9SKalle Valo 	mutex_unlock(&priv->conf_mutex);
453560424e9SKalle Valo 
454560424e9SKalle Valo 	skb_push(frame.skb, txpriv->offset);
455560424e9SKalle Valo 	if (!ret)
456560424e9SKalle Valo 		IEEE80211_SKB_CB(frame.skb)->flags |= IEEE80211_TX_STAT_ACK;
457560424e9SKalle Valo 	BUG_ON(cw1200_queue_remove(queue, priv->pending_frame_id));
458560424e9SKalle Valo 
459560424e9SKalle Valo 	if (ret) {
460560424e9SKalle Valo 		priv->scan.direct_probe = 0;
461560424e9SKalle Valo 		up(&priv->scan.lock);
462560424e9SKalle Valo 		wsm_unlock_tx(priv);
463560424e9SKalle Valo 	}
464560424e9SKalle Valo 
465560424e9SKalle Valo 	return;
466560424e9SKalle Valo }
467