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