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