xref: /openbmc/linux/drivers/net/wireless/st/cw1200/scan.c (revision 3f60f468)
1d2912cb1SThomas 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 
cw1200_scan_start(struct cw1200_common * priv,struct wsm_scan * scan)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 
cw1200_hw_scan(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_scan_request * hw_req)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 
78560424e9SKalle Valo 	frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0,
79560424e9SKalle Valo 		req->ie_len);
80*3f60f468SHang Zhang 	if (!frame.skb)
81560424e9SKalle Valo 		return -ENOMEM;
82560424e9SKalle Valo 
83560424e9SKalle Valo 	if (req->ie_len)
8459ae1d12SJohannes Berg 		skb_put_data(frame.skb, req->ie, req->ie_len);
85560424e9SKalle Valo 
86*3f60f468SHang Zhang 	/* will be unlocked in cw1200_scan_work() */
87*3f60f468SHang Zhang 	down(&priv->scan.lock);
88*3f60f468SHang Zhang 	mutex_lock(&priv->conf_mutex);
89*3f60f468SHang Zhang 
90560424e9SKalle Valo 	ret = wsm_set_template_frame(priv, &frame);
91560424e9SKalle Valo 	if (!ret) {
92560424e9SKalle Valo 		/* Host want to be the probe responder. */
93560424e9SKalle Valo 		ret = wsm_set_probe_responder(priv, true);
94560424e9SKalle Valo 	}
95560424e9SKalle Valo 	if (ret) {
96560424e9SKalle Valo 		mutex_unlock(&priv->conf_mutex);
97560424e9SKalle Valo 		up(&priv->scan.lock);
98*3f60f468SHang Zhang 		dev_kfree_skb(frame.skb);
99560424e9SKalle Valo 		return ret;
100560424e9SKalle Valo 	}
101560424e9SKalle Valo 
102560424e9SKalle Valo 	wsm_lock_tx(priv);
103560424e9SKalle Valo 
104560424e9SKalle Valo 	BUG_ON(priv->scan.req);
105560424e9SKalle Valo 	priv->scan.req = req;
106560424e9SKalle Valo 	priv->scan.n_ssids = 0;
107560424e9SKalle Valo 	priv->scan.status = 0;
108560424e9SKalle Valo 	priv->scan.begin = &req->channels[0];
109560424e9SKalle Valo 	priv->scan.curr = priv->scan.begin;
110560424e9SKalle Valo 	priv->scan.end = &req->channels[req->n_channels];
111560424e9SKalle Valo 	priv->scan.output_power = priv->output_power;
112560424e9SKalle Valo 
113560424e9SKalle Valo 	for (i = 0; i < req->n_ssids; ++i) {
114560424e9SKalle Valo 		struct wsm_ssid *dst = &priv->scan.ssids[priv->scan.n_ssids];
115560424e9SKalle Valo 		memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid));
116560424e9SKalle Valo 		dst->length = req->ssids[i].ssid_len;
117560424e9SKalle Valo 		++priv->scan.n_ssids;
118560424e9SKalle Valo 	}
119560424e9SKalle Valo 
1204f68ef64SJia-Ju Bai 	mutex_unlock(&priv->conf_mutex);
121*3f60f468SHang Zhang 	dev_kfree_skb(frame.skb);
122560424e9SKalle Valo 	queue_work(priv->workqueue, &priv->scan.work);
123560424e9SKalle Valo 	return 0;
124560424e9SKalle Valo }
125560424e9SKalle Valo 
cw1200_scan_work(struct work_struct * work)126560424e9SKalle Valo void cw1200_scan_work(struct work_struct *work)
127560424e9SKalle Valo {
128560424e9SKalle Valo 	struct cw1200_common *priv = container_of(work, struct cw1200_common,
129560424e9SKalle Valo 							scan.work);
130560424e9SKalle Valo 	struct ieee80211_channel **it;
131560424e9SKalle Valo 	struct wsm_scan scan = {
132560424e9SKalle Valo 		.type = WSM_SCAN_TYPE_FOREGROUND,
133560424e9SKalle Valo 		.flags = WSM_SCAN_FLAG_SPLIT_METHOD,
134560424e9SKalle Valo 	};
135560424e9SKalle Valo 	bool first_run = (priv->scan.begin == priv->scan.curr &&
136560424e9SKalle Valo 			  priv->scan.begin != priv->scan.end);
137560424e9SKalle Valo 	int i;
138560424e9SKalle Valo 
139560424e9SKalle Valo 	if (first_run) {
140560424e9SKalle Valo 		/* Firmware gets crazy if scan request is sent
141560424e9SKalle Valo 		 * when STA is joined but not yet associated.
142560424e9SKalle Valo 		 * Force unjoin in this case.
143560424e9SKalle Valo 		 */
144560424e9SKalle Valo 		if (cancel_delayed_work_sync(&priv->join_timeout) > 0)
145560424e9SKalle Valo 			cw1200_join_timeout(&priv->join_timeout.work);
146560424e9SKalle Valo 	}
147560424e9SKalle Valo 
148560424e9SKalle Valo 	mutex_lock(&priv->conf_mutex);
149560424e9SKalle Valo 
150560424e9SKalle Valo 	if (first_run) {
151560424e9SKalle Valo 		if (priv->join_status == CW1200_JOIN_STATUS_STA &&
152560424e9SKalle Valo 		    !(priv->powersave_mode.mode & WSM_PSM_PS)) {
153560424e9SKalle Valo 			struct wsm_set_pm pm = priv->powersave_mode;
154560424e9SKalle Valo 			pm.mode = WSM_PSM_PS;
155560424e9SKalle Valo 			cw1200_set_pm(priv, &pm);
156560424e9SKalle Valo 		} else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
157560424e9SKalle Valo 			/* FW bug: driver has to restart p2p-dev mode
158560424e9SKalle Valo 			 * after scan
159560424e9SKalle Valo 			 */
160560424e9SKalle Valo 			cw1200_disable_listening(priv);
161560424e9SKalle Valo 		}
162560424e9SKalle Valo 	}
163560424e9SKalle Valo 
164560424e9SKalle Valo 	if (!priv->scan.req || (priv->scan.curr == priv->scan.end)) {
1657947d3e0SAvraham Stern 		struct cfg80211_scan_info info = {
1667947d3e0SAvraham Stern 			.aborted = priv->scan.status ? 1 : 0,
1677947d3e0SAvraham Stern 		};
1687947d3e0SAvraham Stern 
169560424e9SKalle Valo 		if (priv->scan.output_power != priv->output_power)
170560424e9SKalle Valo 			wsm_set_output_power(priv, priv->output_power * 10);
171560424e9SKalle Valo 		if (priv->join_status == CW1200_JOIN_STATUS_STA &&
172560424e9SKalle Valo 		    !(priv->powersave_mode.mode & WSM_PSM_PS))
173560424e9SKalle Valo 			cw1200_set_pm(priv, &priv->powersave_mode);
174560424e9SKalle Valo 
175560424e9SKalle Valo 		if (priv->scan.status < 0)
176560424e9SKalle Valo 			wiphy_warn(priv->hw->wiphy,
177560424e9SKalle Valo 				   "[SCAN] Scan failed (%d).\n",
178560424e9SKalle Valo 				   priv->scan.status);
179560424e9SKalle Valo 		else if (priv->scan.req)
180560424e9SKalle Valo 			wiphy_dbg(priv->hw->wiphy,
181560424e9SKalle Valo 				  "[SCAN] Scan completed.\n");
182560424e9SKalle Valo 		else
183560424e9SKalle Valo 			wiphy_dbg(priv->hw->wiphy,
184560424e9SKalle Valo 				  "[SCAN] Scan canceled.\n");
185560424e9SKalle Valo 
186560424e9SKalle Valo 		priv->scan.req = NULL;
187560424e9SKalle Valo 		cw1200_scan_restart_delayed(priv);
188560424e9SKalle Valo 		wsm_unlock_tx(priv);
189560424e9SKalle Valo 		mutex_unlock(&priv->conf_mutex);
1907947d3e0SAvraham Stern 		ieee80211_scan_completed(priv->hw, &info);
191560424e9SKalle Valo 		up(&priv->scan.lock);
192560424e9SKalle Valo 		return;
193560424e9SKalle Valo 	} else {
194560424e9SKalle Valo 		struct ieee80211_channel *first = *priv->scan.curr;
195560424e9SKalle Valo 		for (it = priv->scan.curr + 1, i = 1;
196560424e9SKalle Valo 		     it != priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS;
197560424e9SKalle Valo 		     ++it, ++i) {
198560424e9SKalle Valo 			if ((*it)->band != first->band)
199560424e9SKalle Valo 				break;
200560424e9SKalle Valo 			if (((*it)->flags ^ first->flags) &
201560424e9SKalle Valo 					IEEE80211_CHAN_NO_IR)
202560424e9SKalle Valo 				break;
203560424e9SKalle Valo 			if (!(first->flags & IEEE80211_CHAN_NO_IR) &&
204560424e9SKalle Valo 			    (*it)->max_power != first->max_power)
205560424e9SKalle Valo 				break;
206560424e9SKalle Valo 		}
207560424e9SKalle Valo 		scan.band = first->band;
208560424e9SKalle Valo 
209560424e9SKalle Valo 		if (priv->scan.req->no_cck)
210560424e9SKalle Valo 			scan.max_tx_rate = WSM_TRANSMIT_RATE_6;
211560424e9SKalle Valo 		else
212560424e9SKalle Valo 			scan.max_tx_rate = WSM_TRANSMIT_RATE_1;
213560424e9SKalle Valo 		scan.num_probes =
214560424e9SKalle Valo 			(first->flags & IEEE80211_CHAN_NO_IR) ? 0 : 2;
215560424e9SKalle Valo 		scan.num_ssids = priv->scan.n_ssids;
216560424e9SKalle Valo 		scan.ssids = &priv->scan.ssids[0];
217560424e9SKalle Valo 		scan.num_channels = it - priv->scan.curr;
218560424e9SKalle Valo 		/* TODO: Is it optimal? */
219560424e9SKalle Valo 		scan.probe_delay = 100;
220560424e9SKalle Valo 		/* It is not stated in WSM specification, however
221560424e9SKalle Valo 		 * FW team says that driver may not use FG scan
222560424e9SKalle Valo 		 * when joined.
223560424e9SKalle Valo 		 */
224560424e9SKalle Valo 		if (priv->join_status == CW1200_JOIN_STATUS_STA) {
225560424e9SKalle Valo 			scan.type = WSM_SCAN_TYPE_BACKGROUND;
226560424e9SKalle Valo 			scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
227560424e9SKalle Valo 		}
2286396bb22SKees Cook 		scan.ch = kcalloc(it - priv->scan.curr,
2296396bb22SKees Cook 				  sizeof(struct wsm_scan_ch),
230560424e9SKalle Valo 				  GFP_KERNEL);
231560424e9SKalle Valo 		if (!scan.ch) {
232560424e9SKalle Valo 			priv->scan.status = -ENOMEM;
233560424e9SKalle Valo 			goto fail;
234560424e9SKalle Valo 		}
235560424e9SKalle Valo 		for (i = 0; i < scan.num_channels; ++i) {
236560424e9SKalle Valo 			scan.ch[i].number = priv->scan.curr[i]->hw_value;
237560424e9SKalle Valo 			if (priv->scan.curr[i]->flags & IEEE80211_CHAN_NO_IR) {
238560424e9SKalle Valo 				scan.ch[i].min_chan_time = 50;
239560424e9SKalle Valo 				scan.ch[i].max_chan_time = 100;
240560424e9SKalle Valo 			} else {
241560424e9SKalle Valo 				scan.ch[i].min_chan_time = 10;
242560424e9SKalle Valo 				scan.ch[i].max_chan_time = 25;
243560424e9SKalle Valo 			}
244560424e9SKalle Valo 		}
245560424e9SKalle Valo 		if (!(first->flags & IEEE80211_CHAN_NO_IR) &&
246560424e9SKalle Valo 		    priv->scan.output_power != first->max_power) {
247560424e9SKalle Valo 			priv->scan.output_power = first->max_power;
248560424e9SKalle Valo 			wsm_set_output_power(priv,
249560424e9SKalle Valo 					     priv->scan.output_power * 10);
250560424e9SKalle Valo 		}
251560424e9SKalle Valo 		priv->scan.status = cw1200_scan_start(priv, &scan);
252560424e9SKalle Valo 		kfree(scan.ch);
253560424e9SKalle Valo 		if (priv->scan.status)
254560424e9SKalle Valo 			goto fail;
255560424e9SKalle Valo 		priv->scan.curr = it;
256560424e9SKalle Valo 	}
257560424e9SKalle Valo 	mutex_unlock(&priv->conf_mutex);
258560424e9SKalle Valo 	return;
259560424e9SKalle Valo 
260560424e9SKalle Valo fail:
261560424e9SKalle Valo 	priv->scan.curr = priv->scan.end;
262560424e9SKalle Valo 	mutex_unlock(&priv->conf_mutex);
263560424e9SKalle Valo 	queue_work(priv->workqueue, &priv->scan.work);
264560424e9SKalle Valo 	return;
265560424e9SKalle Valo }
266560424e9SKalle Valo 
cw1200_scan_restart_delayed(struct cw1200_common * priv)267560424e9SKalle Valo static void cw1200_scan_restart_delayed(struct cw1200_common *priv)
268560424e9SKalle Valo {
269560424e9SKalle Valo 	/* FW bug: driver has to restart p2p-dev mode after scan. */
270560424e9SKalle Valo 	if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
271560424e9SKalle Valo 		cw1200_enable_listening(priv);
272560424e9SKalle Valo 		cw1200_update_filtering(priv);
273560424e9SKalle Valo 	}
274560424e9SKalle Valo 
275560424e9SKalle Valo 	if (priv->delayed_unjoin) {
276560424e9SKalle Valo 		priv->delayed_unjoin = false;
277560424e9SKalle Valo 		if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
278560424e9SKalle Valo 			wsm_unlock_tx(priv);
279560424e9SKalle Valo 	} else if (priv->delayed_link_loss) {
280560424e9SKalle Valo 			wiphy_dbg(priv->hw->wiphy, "[CQM] Requeue BSS loss.\n");
281560424e9SKalle Valo 			priv->delayed_link_loss = 0;
282560424e9SKalle Valo 			cw1200_cqm_bssloss_sm(priv, 1, 0, 0);
283560424e9SKalle Valo 	}
284560424e9SKalle Valo }
285560424e9SKalle Valo 
cw1200_scan_complete(struct cw1200_common * priv)286560424e9SKalle Valo static void cw1200_scan_complete(struct cw1200_common *priv)
287560424e9SKalle Valo {
288560424e9SKalle Valo 	queue_delayed_work(priv->workqueue, &priv->clear_recent_scan_work, HZ);
289560424e9SKalle Valo 	if (priv->scan.direct_probe) {
290560424e9SKalle Valo 		wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe complete.\n");
291560424e9SKalle Valo 		cw1200_scan_restart_delayed(priv);
292560424e9SKalle Valo 		priv->scan.direct_probe = 0;
293560424e9SKalle Valo 		up(&priv->scan.lock);
294560424e9SKalle Valo 		wsm_unlock_tx(priv);
295560424e9SKalle Valo 	} else {
296560424e9SKalle Valo 		cw1200_scan_work(&priv->scan.work);
297560424e9SKalle Valo 	}
298560424e9SKalle Valo }
299560424e9SKalle Valo 
cw1200_scan_failed_cb(struct cw1200_common * priv)300560424e9SKalle Valo void cw1200_scan_failed_cb(struct cw1200_common *priv)
301560424e9SKalle Valo {
302560424e9SKalle Valo 	if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
303560424e9SKalle Valo 		/* STA is stopped. */
304560424e9SKalle Valo 		return;
305560424e9SKalle Valo 
306560424e9SKalle Valo 	if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) {
307560424e9SKalle Valo 		priv->scan.status = -EIO;
308560424e9SKalle Valo 		queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0);
309560424e9SKalle Valo 	}
310560424e9SKalle Valo }
311560424e9SKalle Valo 
312560424e9SKalle Valo 
cw1200_scan_complete_cb(struct cw1200_common * priv,struct wsm_scan_complete * arg)313560424e9SKalle Valo void cw1200_scan_complete_cb(struct cw1200_common *priv,
314560424e9SKalle Valo 				struct wsm_scan_complete *arg)
315560424e9SKalle Valo {
316560424e9SKalle Valo 	if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
317560424e9SKalle Valo 		/* STA is stopped. */
318560424e9SKalle Valo 		return;
319560424e9SKalle Valo 
320560424e9SKalle Valo 	if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) {
321560424e9SKalle Valo 		priv->scan.status = 1;
322560424e9SKalle Valo 		queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0);
323560424e9SKalle Valo 	}
324560424e9SKalle Valo }
325560424e9SKalle Valo 
cw1200_clear_recent_scan_work(struct work_struct * work)326560424e9SKalle Valo void cw1200_clear_recent_scan_work(struct work_struct *work)
327560424e9SKalle Valo {
328560424e9SKalle Valo 	struct cw1200_common *priv =
329560424e9SKalle Valo 		container_of(work, struct cw1200_common,
330560424e9SKalle Valo 			     clear_recent_scan_work.work);
331560424e9SKalle Valo 	atomic_xchg(&priv->recent_scan, 0);
332560424e9SKalle Valo }
333560424e9SKalle Valo 
cw1200_scan_timeout(struct work_struct * work)334560424e9SKalle Valo void cw1200_scan_timeout(struct work_struct *work)
335560424e9SKalle Valo {
336560424e9SKalle Valo 	struct cw1200_common *priv =
337560424e9SKalle Valo 		container_of(work, struct cw1200_common, scan.timeout.work);
338560424e9SKalle Valo 	if (atomic_xchg(&priv->scan.in_progress, 0)) {
339560424e9SKalle Valo 		if (priv->scan.status > 0) {
340560424e9SKalle Valo 			priv->scan.status = 0;
341560424e9SKalle Valo 		} else if (!priv->scan.status) {
342560424e9SKalle Valo 			wiphy_warn(priv->hw->wiphy,
343560424e9SKalle Valo 				   "Timeout waiting for scan complete notification.\n");
344560424e9SKalle Valo 			priv->scan.status = -ETIMEDOUT;
345560424e9SKalle Valo 			priv->scan.curr = priv->scan.end;
346560424e9SKalle Valo 			wsm_stop_scan(priv);
347560424e9SKalle Valo 		}
348560424e9SKalle Valo 		cw1200_scan_complete(priv);
349560424e9SKalle Valo 	}
350560424e9SKalle Valo }
351560424e9SKalle Valo 
cw1200_probe_work(struct work_struct * work)352560424e9SKalle Valo void cw1200_probe_work(struct work_struct *work)
353560424e9SKalle Valo {
354560424e9SKalle Valo 	struct cw1200_common *priv =
355560424e9SKalle Valo 		container_of(work, struct cw1200_common, scan.probe_work.work);
356560424e9SKalle Valo 	u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id);
357560424e9SKalle Valo 	struct cw1200_queue *queue = &priv->tx_queue[queue_id];
358560424e9SKalle Valo 	const struct cw1200_txpriv *txpriv;
359560424e9SKalle Valo 	struct wsm_tx *wsm;
360560424e9SKalle Valo 	struct wsm_template_frame frame = {
361560424e9SKalle Valo 		.frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
362560424e9SKalle Valo 	};
363560424e9SKalle Valo 	struct wsm_ssid ssids[1] = {{
364560424e9SKalle Valo 		.length = 0,
365560424e9SKalle Valo 	} };
366560424e9SKalle Valo 	struct wsm_scan_ch ch[1] = {{
367560424e9SKalle Valo 		.min_chan_time = 0,
368560424e9SKalle Valo 		.max_chan_time = 10,
369560424e9SKalle Valo 	} };
370560424e9SKalle Valo 	struct wsm_scan scan = {
371560424e9SKalle Valo 		.type = WSM_SCAN_TYPE_FOREGROUND,
372560424e9SKalle Valo 		.num_probes = 1,
373560424e9SKalle Valo 		.probe_delay = 0,
374560424e9SKalle Valo 		.num_channels = 1,
375560424e9SKalle Valo 		.ssids = ssids,
376560424e9SKalle Valo 		.ch = ch,
377560424e9SKalle Valo 	};
378560424e9SKalle Valo 	u8 *ies;
379560424e9SKalle Valo 	size_t ies_len;
380560424e9SKalle Valo 	int ret;
381560424e9SKalle Valo 
382560424e9SKalle Valo 	wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe work.\n");
383560424e9SKalle Valo 
384560424e9SKalle Valo 	mutex_lock(&priv->conf_mutex);
385560424e9SKalle Valo 	if (down_trylock(&priv->scan.lock)) {
386560424e9SKalle Valo 		/* Scan is already in progress. Requeue self. */
387560424e9SKalle Valo 		schedule();
388560424e9SKalle Valo 		queue_delayed_work(priv->workqueue, &priv->scan.probe_work,
389560424e9SKalle Valo 				   msecs_to_jiffies(100));
390560424e9SKalle Valo 		mutex_unlock(&priv->conf_mutex);
391560424e9SKalle Valo 		return;
392560424e9SKalle Valo 	}
393560424e9SKalle Valo 
394560424e9SKalle Valo 	/* Make sure we still have a pending probe req */
395560424e9SKalle Valo 	if (cw1200_queue_get_skb(queue,	priv->pending_frame_id,
396560424e9SKalle Valo 				 &frame.skb, &txpriv)) {
397560424e9SKalle Valo 		up(&priv->scan.lock);
398560424e9SKalle Valo 		mutex_unlock(&priv->conf_mutex);
399560424e9SKalle Valo 		wsm_unlock_tx(priv);
400560424e9SKalle Valo 		return;
401560424e9SKalle Valo 	}
402560424e9SKalle Valo 	wsm = (struct wsm_tx *)frame.skb->data;
403560424e9SKalle Valo 	scan.max_tx_rate = wsm->max_tx_rate;
40457fbcce3SJohannes Berg 	scan.band = (priv->channel->band == NL80211_BAND_5GHZ) ?
405560424e9SKalle Valo 		WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
406560424e9SKalle Valo 	if (priv->join_status == CW1200_JOIN_STATUS_STA ||
407560424e9SKalle Valo 	    priv->join_status == CW1200_JOIN_STATUS_IBSS) {
408560424e9SKalle Valo 		scan.type = WSM_SCAN_TYPE_BACKGROUND;
409560424e9SKalle Valo 		scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
410560424e9SKalle Valo 	}
411560424e9SKalle Valo 	ch[0].number = priv->channel->hw_value;
412560424e9SKalle Valo 
413560424e9SKalle Valo 	skb_pull(frame.skb, txpriv->offset);
414560424e9SKalle Valo 
415560424e9SKalle Valo 	ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)];
416560424e9SKalle Valo 	ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr);
417560424e9SKalle Valo 
418560424e9SKalle Valo 	if (ies_len) {
419560424e9SKalle Valo 		u8 *ssidie =
420560424e9SKalle Valo 			(u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len);
421560424e9SKalle Valo 		if (ssidie && ssidie[1] && ssidie[1] <= sizeof(ssids[0].ssid)) {
422560424e9SKalle Valo 			u8 *nextie = &ssidie[2 + ssidie[1]];
423560424e9SKalle Valo 			/* Remove SSID from the IE list. It has to be provided
424560424e9SKalle Valo 			 * as a separate argument in cw1200_scan_start call
425560424e9SKalle Valo 			 */
426560424e9SKalle Valo 
427560424e9SKalle Valo 			/* Store SSID localy */
428560424e9SKalle Valo 			ssids[0].length = ssidie[1];
429560424e9SKalle Valo 			memcpy(ssids[0].ssid, &ssidie[2], ssids[0].length);
430560424e9SKalle Valo 			scan.num_ssids = 1;
431560424e9SKalle Valo 
432560424e9SKalle Valo 			/* Remove SSID from IE list */
433560424e9SKalle Valo 			ssidie[1] = 0;
434560424e9SKalle Valo 			memmove(&ssidie[2], nextie, &ies[ies_len] - nextie);
435560424e9SKalle Valo 			skb_trim(frame.skb, frame.skb->len - ssids[0].length);
436560424e9SKalle Valo 		}
437560424e9SKalle Valo 	}
438560424e9SKalle Valo 
439560424e9SKalle Valo 	/* FW bug: driver has to restart p2p-dev mode after scan */
440560424e9SKalle Valo 	if (priv->join_status == CW1200_JOIN_STATUS_MONITOR)
441560424e9SKalle Valo 		cw1200_disable_listening(priv);
442560424e9SKalle Valo 	ret = wsm_set_template_frame(priv, &frame);
443560424e9SKalle Valo 	priv->scan.direct_probe = 1;
444560424e9SKalle Valo 	if (!ret) {
445560424e9SKalle Valo 		wsm_flush_tx(priv);
446560424e9SKalle Valo 		ret = cw1200_scan_start(priv, &scan);
447560424e9SKalle Valo 	}
448560424e9SKalle Valo 	mutex_unlock(&priv->conf_mutex);
449560424e9SKalle Valo 
450560424e9SKalle Valo 	skb_push(frame.skb, txpriv->offset);
451560424e9SKalle Valo 	if (!ret)
452560424e9SKalle Valo 		IEEE80211_SKB_CB(frame.skb)->flags |= IEEE80211_TX_STAT_ACK;
453560424e9SKalle Valo 	BUG_ON(cw1200_queue_remove(queue, priv->pending_frame_id));
454560424e9SKalle Valo 
455560424e9SKalle Valo 	if (ret) {
456560424e9SKalle Valo 		priv->scan.direct_probe = 0;
457560424e9SKalle Valo 		up(&priv->scan.lock);
458560424e9SKalle Valo 		wsm_unlock_tx(priv);
459560424e9SKalle Valo 	}
460560424e9SKalle Valo 
461560424e9SKalle Valo 	return;
462560424e9SKalle Valo }
463