diff options
Diffstat (limited to 'drivers/net/wireless/iwlegacy/iwl-scan.c')
-rw-r--r-- | drivers/net/wireless/iwlegacy/iwl-scan.c | 549 |
1 files changed, 549 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlegacy/iwl-scan.c b/drivers/net/wireless/iwlegacy/iwl-scan.c new file mode 100644 index 00000000000..a6b5222fc59 --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-scan.c | |||
@@ -0,0 +1,549 @@ | |||
1 | /****************************************************************************** | ||
2 | * | ||
3 | * GPL LICENSE SUMMARY | ||
4 | * | ||
5 | * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of version 2 of the GNU General Public License as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, but | ||
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
14 | * General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, | ||
19 | * USA | ||
20 | * | ||
21 | * The full GNU General Public License is included in this distribution | ||
22 | * in the file called LICENSE.GPL. | ||
23 | * | ||
24 | * Contact Information: | ||
25 | * Intel Linux Wireless <ilw@linux.intel.com> | ||
26 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 | ||
27 | *****************************************************************************/ | ||
28 | #include <linux/slab.h> | ||
29 | #include <linux/types.h> | ||
30 | #include <linux/etherdevice.h> | ||
31 | #include <net/mac80211.h> | ||
32 | |||
33 | #include "iwl-eeprom.h" | ||
34 | #include "iwl-dev.h" | ||
35 | #include "iwl-core.h" | ||
36 | #include "iwl-sta.h" | ||
37 | #include "iwl-io.h" | ||
38 | #include "iwl-helpers.h" | ||
39 | |||
40 | /* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after | ||
41 | * sending probe req. This should be set long enough to hear probe responses | ||
42 | * from more than one AP. */ | ||
43 | #define IWL_ACTIVE_DWELL_TIME_24 (30) /* all times in msec */ | ||
44 | #define IWL_ACTIVE_DWELL_TIME_52 (20) | ||
45 | |||
46 | #define IWL_ACTIVE_DWELL_FACTOR_24GHZ (3) | ||
47 | #define IWL_ACTIVE_DWELL_FACTOR_52GHZ (2) | ||
48 | |||
49 | /* For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel. | ||
50 | * Must be set longer than active dwell time. | ||
51 | * For the most reliable scan, set > AP beacon interval (typically 100msec). */ | ||
52 | #define IWL_PASSIVE_DWELL_TIME_24 (20) /* all times in msec */ | ||
53 | #define IWL_PASSIVE_DWELL_TIME_52 (10) | ||
54 | #define IWL_PASSIVE_DWELL_BASE (100) | ||
55 | #define IWL_CHANNEL_TUNE_TIME 5 | ||
56 | |||
57 | static int iwl_legacy_send_scan_abort(struct iwl_priv *priv) | ||
58 | { | ||
59 | int ret; | ||
60 | struct iwl_rx_packet *pkt; | ||
61 | struct iwl_host_cmd cmd = { | ||
62 | .id = REPLY_SCAN_ABORT_CMD, | ||
63 | .flags = CMD_WANT_SKB, | ||
64 | }; | ||
65 | |||
66 | /* Exit instantly with error when device is not ready | ||
67 | * to receive scan abort command or it does not perform | ||
68 | * hardware scan currently */ | ||
69 | if (!test_bit(STATUS_READY, &priv->status) || | ||
70 | !test_bit(STATUS_GEO_CONFIGURED, &priv->status) || | ||
71 | !test_bit(STATUS_SCAN_HW, &priv->status) || | ||
72 | test_bit(STATUS_FW_ERROR, &priv->status) || | ||
73 | test_bit(STATUS_EXIT_PENDING, &priv->status)) | ||
74 | return -EIO; | ||
75 | |||
76 | ret = iwl_legacy_send_cmd_sync(priv, &cmd); | ||
77 | if (ret) | ||
78 | return ret; | ||
79 | |||
80 | pkt = (struct iwl_rx_packet *)cmd.reply_page; | ||
81 | if (pkt->u.status != CAN_ABORT_STATUS) { | ||
82 | /* The scan abort will return 1 for success or | ||
83 | * 2 for "failure". A failure condition can be | ||
84 | * due to simply not being in an active scan which | ||
85 | * can occur if we send the scan abort before we | ||
86 | * the microcode has notified us that a scan is | ||
87 | * completed. */ | ||
88 | IWL_DEBUG_SCAN(priv, "SCAN_ABORT ret %d.\n", pkt->u.status); | ||
89 | ret = -EIO; | ||
90 | } | ||
91 | |||
92 | iwl_legacy_free_pages(priv, cmd.reply_page); | ||
93 | return ret; | ||
94 | } | ||
95 | |||
96 | static void iwl_legacy_complete_scan(struct iwl_priv *priv, bool aborted) | ||
97 | { | ||
98 | /* check if scan was requested from mac80211 */ | ||
99 | if (priv->scan_request) { | ||
100 | IWL_DEBUG_SCAN(priv, "Complete scan in mac80211\n"); | ||
101 | ieee80211_scan_completed(priv->hw, aborted); | ||
102 | } | ||
103 | |||
104 | priv->scan_vif = NULL; | ||
105 | priv->scan_request = NULL; | ||
106 | } | ||
107 | |||
108 | void iwl_legacy_force_scan_end(struct iwl_priv *priv) | ||
109 | { | ||
110 | lockdep_assert_held(&priv->mutex); | ||
111 | |||
112 | if (!test_bit(STATUS_SCANNING, &priv->status)) { | ||
113 | IWL_DEBUG_SCAN(priv, "Forcing scan end while not scanning\n"); | ||
114 | return; | ||
115 | } | ||
116 | |||
117 | IWL_DEBUG_SCAN(priv, "Forcing scan end\n"); | ||
118 | clear_bit(STATUS_SCANNING, &priv->status); | ||
119 | clear_bit(STATUS_SCAN_HW, &priv->status); | ||
120 | clear_bit(STATUS_SCAN_ABORTING, &priv->status); | ||
121 | iwl_legacy_complete_scan(priv, true); | ||
122 | } | ||
123 | |||
124 | static void iwl_legacy_do_scan_abort(struct iwl_priv *priv) | ||
125 | { | ||
126 | int ret; | ||
127 | |||
128 | lockdep_assert_held(&priv->mutex); | ||
129 | |||
130 | if (!test_bit(STATUS_SCANNING, &priv->status)) { | ||
131 | IWL_DEBUG_SCAN(priv, "Not performing scan to abort\n"); | ||
132 | return; | ||
133 | } | ||
134 | |||
135 | if (test_and_set_bit(STATUS_SCAN_ABORTING, &priv->status)) { | ||
136 | IWL_DEBUG_SCAN(priv, "Scan abort in progress\n"); | ||
137 | return; | ||
138 | } | ||
139 | |||
140 | ret = iwl_legacy_send_scan_abort(priv); | ||
141 | if (ret) { | ||
142 | IWL_DEBUG_SCAN(priv, "Send scan abort failed %d\n", ret); | ||
143 | iwl_legacy_force_scan_end(priv); | ||
144 | } else | ||
145 | IWL_DEBUG_SCAN(priv, "Successfully send scan abort\n"); | ||
146 | } | ||
147 | |||
148 | /** | ||
149 | * iwl_scan_cancel - Cancel any currently executing HW scan | ||
150 | */ | ||
151 | int iwl_legacy_scan_cancel(struct iwl_priv *priv) | ||
152 | { | ||
153 | IWL_DEBUG_SCAN(priv, "Queuing abort scan\n"); | ||
154 | queue_work(priv->workqueue, &priv->abort_scan); | ||
155 | return 0; | ||
156 | } | ||
157 | EXPORT_SYMBOL(iwl_legacy_scan_cancel); | ||
158 | |||
159 | /** | ||
160 | * iwl_legacy_scan_cancel_timeout - Cancel any currently executing HW scan | ||
161 | * @ms: amount of time to wait (in milliseconds) for scan to abort | ||
162 | * | ||
163 | */ | ||
164 | int iwl_legacy_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms) | ||
165 | { | ||
166 | unsigned long timeout = jiffies + msecs_to_jiffies(ms); | ||
167 | |||
168 | lockdep_assert_held(&priv->mutex); | ||
169 | |||
170 | IWL_DEBUG_SCAN(priv, "Scan cancel timeout\n"); | ||
171 | |||
172 | iwl_legacy_do_scan_abort(priv); | ||
173 | |||
174 | while (time_before_eq(jiffies, timeout)) { | ||
175 | if (!test_bit(STATUS_SCAN_HW, &priv->status)) | ||
176 | break; | ||
177 | msleep(20); | ||
178 | } | ||
179 | |||
180 | return test_bit(STATUS_SCAN_HW, &priv->status); | ||
181 | } | ||
182 | EXPORT_SYMBOL(iwl_legacy_scan_cancel_timeout); | ||
183 | |||
184 | /* Service response to REPLY_SCAN_CMD (0x80) */ | ||
185 | static void iwl_legacy_rx_reply_scan(struct iwl_priv *priv, | ||
186 | struct iwl_rx_mem_buffer *rxb) | ||
187 | { | ||
188 | #ifdef CONFIG_IWLWIFI_LEGACY_DEBUG | ||
189 | struct iwl_rx_packet *pkt = rxb_addr(rxb); | ||
190 | struct iwl_scanreq_notification *notif = | ||
191 | (struct iwl_scanreq_notification *)pkt->u.raw; | ||
192 | |||
193 | IWL_DEBUG_SCAN(priv, "Scan request status = 0x%x\n", notif->status); | ||
194 | #endif | ||
195 | } | ||
196 | |||
197 | /* Service SCAN_START_NOTIFICATION (0x82) */ | ||
198 | static void iwl_legacy_rx_scan_start_notif(struct iwl_priv *priv, | ||
199 | struct iwl_rx_mem_buffer *rxb) | ||
200 | { | ||
201 | struct iwl_rx_packet *pkt = rxb_addr(rxb); | ||
202 | struct iwl_scanstart_notification *notif = | ||
203 | (struct iwl_scanstart_notification *)pkt->u.raw; | ||
204 | priv->scan_start_tsf = le32_to_cpu(notif->tsf_low); | ||
205 | IWL_DEBUG_SCAN(priv, "Scan start: " | ||
206 | "%d [802.11%s] " | ||
207 | "(TSF: 0x%08X:%08X) - %d (beacon timer %u)\n", | ||
208 | notif->channel, | ||
209 | notif->band ? "bg" : "a", | ||
210 | le32_to_cpu(notif->tsf_high), | ||
211 | le32_to_cpu(notif->tsf_low), | ||
212 | notif->status, notif->beacon_timer); | ||
213 | } | ||
214 | |||
215 | /* Service SCAN_RESULTS_NOTIFICATION (0x83) */ | ||
216 | static void iwl_legacy_rx_scan_results_notif(struct iwl_priv *priv, | ||
217 | struct iwl_rx_mem_buffer *rxb) | ||
218 | { | ||
219 | #ifdef CONFIG_IWLWIFI_LEGACY_DEBUG | ||
220 | struct iwl_rx_packet *pkt = rxb_addr(rxb); | ||
221 | struct iwl_scanresults_notification *notif = | ||
222 | (struct iwl_scanresults_notification *)pkt->u.raw; | ||
223 | |||
224 | IWL_DEBUG_SCAN(priv, "Scan ch.res: " | ||
225 | "%d [802.11%s] " | ||
226 | "(TSF: 0x%08X:%08X) - %d " | ||
227 | "elapsed=%lu usec\n", | ||
228 | notif->channel, | ||
229 | notif->band ? "bg" : "a", | ||
230 | le32_to_cpu(notif->tsf_high), | ||
231 | le32_to_cpu(notif->tsf_low), | ||
232 | le32_to_cpu(notif->statistics[0]), | ||
233 | le32_to_cpu(notif->tsf_low) - priv->scan_start_tsf); | ||
234 | #endif | ||
235 | } | ||
236 | |||
237 | /* Service SCAN_COMPLETE_NOTIFICATION (0x84) */ | ||
238 | static void iwl_legacy_rx_scan_complete_notif(struct iwl_priv *priv, | ||
239 | struct iwl_rx_mem_buffer *rxb) | ||
240 | { | ||
241 | |||
242 | #ifdef CONFIG_IWLWIFI_LEGACY_DEBUG | ||
243 | struct iwl_rx_packet *pkt = rxb_addr(rxb); | ||
244 | struct iwl_scancomplete_notification *scan_notif = (void *)pkt->u.raw; | ||
245 | #endif | ||
246 | |||
247 | IWL_DEBUG_SCAN(priv, | ||
248 | "Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n", | ||
249 | scan_notif->scanned_channels, | ||
250 | scan_notif->tsf_low, | ||
251 | scan_notif->tsf_high, scan_notif->status); | ||
252 | |||
253 | /* The HW is no longer scanning */ | ||
254 | clear_bit(STATUS_SCAN_HW, &priv->status); | ||
255 | |||
256 | IWL_DEBUG_SCAN(priv, "Scan on %sGHz took %dms\n", | ||
257 | (priv->scan_band == IEEE80211_BAND_2GHZ) ? "2.4" : "5.2", | ||
258 | jiffies_to_msecs(jiffies - priv->scan_start)); | ||
259 | |||
260 | queue_work(priv->workqueue, &priv->scan_completed); | ||
261 | } | ||
262 | |||
263 | void iwl_legacy_setup_rx_scan_handlers(struct iwl_priv *priv) | ||
264 | { | ||
265 | /* scan handlers */ | ||
266 | priv->rx_handlers[REPLY_SCAN_CMD] = iwl_legacy_rx_reply_scan; | ||
267 | priv->rx_handlers[SCAN_START_NOTIFICATION] = | ||
268 | iwl_legacy_rx_scan_start_notif; | ||
269 | priv->rx_handlers[SCAN_RESULTS_NOTIFICATION] = | ||
270 | iwl_legacy_rx_scan_results_notif; | ||
271 | priv->rx_handlers[SCAN_COMPLETE_NOTIFICATION] = | ||
272 | iwl_legacy_rx_scan_complete_notif; | ||
273 | } | ||
274 | EXPORT_SYMBOL(iwl_legacy_setup_rx_scan_handlers); | ||
275 | |||
276 | inline u16 iwl_legacy_get_active_dwell_time(struct iwl_priv *priv, | ||
277 | enum ieee80211_band band, | ||
278 | u8 n_probes) | ||
279 | { | ||
280 | if (band == IEEE80211_BAND_5GHZ) | ||
281 | return IWL_ACTIVE_DWELL_TIME_52 + | ||
282 | IWL_ACTIVE_DWELL_FACTOR_52GHZ * (n_probes + 1); | ||
283 | else | ||
284 | return IWL_ACTIVE_DWELL_TIME_24 + | ||
285 | IWL_ACTIVE_DWELL_FACTOR_24GHZ * (n_probes + 1); | ||
286 | } | ||
287 | EXPORT_SYMBOL(iwl_legacy_get_active_dwell_time); | ||
288 | |||
289 | u16 iwl_legacy_get_passive_dwell_time(struct iwl_priv *priv, | ||
290 | enum ieee80211_band band, | ||
291 | struct ieee80211_vif *vif) | ||
292 | { | ||
293 | struct iwl_rxon_context *ctx; | ||
294 | u16 passive = (band == IEEE80211_BAND_2GHZ) ? | ||
295 | IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_24 : | ||
296 | IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_52; | ||
297 | |||
298 | if (iwl_legacy_is_any_associated(priv)) { | ||
299 | /* | ||
300 | * If we're associated, we clamp the maximum passive | ||
301 | * dwell time to be 98% of the smallest beacon interval | ||
302 | * (minus 2 * channel tune time) | ||
303 | */ | ||
304 | for_each_context(priv, ctx) { | ||
305 | u16 value; | ||
306 | |||
307 | if (!iwl_legacy_is_associated_ctx(ctx)) | ||
308 | continue; | ||
309 | value = ctx->vif ? ctx->vif->bss_conf.beacon_int : 0; | ||
310 | if ((value > IWL_PASSIVE_DWELL_BASE) || !value) | ||
311 | value = IWL_PASSIVE_DWELL_BASE; | ||
312 | value = (value * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2; | ||
313 | passive = min(value, passive); | ||
314 | } | ||
315 | } | ||
316 | |||
317 | return passive; | ||
318 | } | ||
319 | EXPORT_SYMBOL(iwl_legacy_get_passive_dwell_time); | ||
320 | |||
321 | void iwl_legacy_init_scan_params(struct iwl_priv *priv) | ||
322 | { | ||
323 | u8 ant_idx = fls(priv->hw_params.valid_tx_ant) - 1; | ||
324 | if (!priv->scan_tx_ant[IEEE80211_BAND_5GHZ]) | ||
325 | priv->scan_tx_ant[IEEE80211_BAND_5GHZ] = ant_idx; | ||
326 | if (!priv->scan_tx_ant[IEEE80211_BAND_2GHZ]) | ||
327 | priv->scan_tx_ant[IEEE80211_BAND_2GHZ] = ant_idx; | ||
328 | } | ||
329 | EXPORT_SYMBOL(iwl_legacy_init_scan_params); | ||
330 | |||
331 | static int iwl_legacy_scan_initiate(struct iwl_priv *priv, | ||
332 | struct ieee80211_vif *vif) | ||
333 | { | ||
334 | int ret; | ||
335 | |||
336 | lockdep_assert_held(&priv->mutex); | ||
337 | |||
338 | if (WARN_ON(!priv->cfg->ops->utils->request_scan)) | ||
339 | return -EOPNOTSUPP; | ||
340 | |||
341 | cancel_delayed_work(&priv->scan_check); | ||
342 | |||
343 | if (!iwl_legacy_is_ready_rf(priv)) { | ||
344 | IWL_WARN(priv, "Request scan called when driver not ready.\n"); | ||
345 | return -EIO; | ||
346 | } | ||
347 | |||
348 | if (test_bit(STATUS_SCAN_HW, &priv->status)) { | ||
349 | IWL_DEBUG_SCAN(priv, | ||
350 | "Multiple concurrent scan requests in parallel.\n"); | ||
351 | return -EBUSY; | ||
352 | } | ||
353 | |||
354 | if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) { | ||
355 | IWL_DEBUG_SCAN(priv, "Scan request while abort pending.\n"); | ||
356 | return -EBUSY; | ||
357 | } | ||
358 | |||
359 | IWL_DEBUG_SCAN(priv, "Starting scan...\n"); | ||
360 | |||
361 | set_bit(STATUS_SCANNING, &priv->status); | ||
362 | priv->scan_start = jiffies; | ||
363 | |||
364 | ret = priv->cfg->ops->utils->request_scan(priv, vif); | ||
365 | if (ret) { | ||
366 | clear_bit(STATUS_SCANNING, &priv->status); | ||
367 | return ret; | ||
368 | } | ||
369 | |||
370 | queue_delayed_work(priv->workqueue, &priv->scan_check, | ||
371 | IWL_SCAN_CHECK_WATCHDOG); | ||
372 | |||
373 | return 0; | ||
374 | } | ||
375 | |||
376 | int iwl_legacy_mac_hw_scan(struct ieee80211_hw *hw, | ||
377 | struct ieee80211_vif *vif, | ||
378 | struct cfg80211_scan_request *req) | ||
379 | { | ||
380 | struct iwl_priv *priv = hw->priv; | ||
381 | int ret; | ||
382 | |||
383 | IWL_DEBUG_MAC80211(priv, "enter\n"); | ||
384 | |||
385 | if (req->n_channels == 0) | ||
386 | return -EINVAL; | ||
387 | |||
388 | mutex_lock(&priv->mutex); | ||
389 | |||
390 | if (test_bit(STATUS_SCANNING, &priv->status)) { | ||
391 | IWL_DEBUG_SCAN(priv, "Scan already in progress.\n"); | ||
392 | ret = -EAGAIN; | ||
393 | goto out_unlock; | ||
394 | } | ||
395 | |||
396 | /* mac80211 will only ask for one band at a time */ | ||
397 | priv->scan_request = req; | ||
398 | priv->scan_vif = vif; | ||
399 | priv->scan_band = req->channels[0]->band; | ||
400 | |||
401 | ret = iwl_legacy_scan_initiate(priv, vif); | ||
402 | |||
403 | IWL_DEBUG_MAC80211(priv, "leave\n"); | ||
404 | |||
405 | out_unlock: | ||
406 | mutex_unlock(&priv->mutex); | ||
407 | |||
408 | return ret; | ||
409 | } | ||
410 | EXPORT_SYMBOL(iwl_legacy_mac_hw_scan); | ||
411 | |||
412 | static void iwl_legacy_bg_scan_check(struct work_struct *data) | ||
413 | { | ||
414 | struct iwl_priv *priv = | ||
415 | container_of(data, struct iwl_priv, scan_check.work); | ||
416 | |||
417 | IWL_DEBUG_SCAN(priv, "Scan check work\n"); | ||
418 | |||
419 | /* Since we are here firmware does not finish scan and | ||
420 | * most likely is in bad shape, so we don't bother to | ||
421 | * send abort command, just force scan complete to mac80211 */ | ||
422 | mutex_lock(&priv->mutex); | ||
423 | iwl_legacy_force_scan_end(priv); | ||
424 | mutex_unlock(&priv->mutex); | ||
425 | } | ||
426 | |||
427 | /** | ||
428 | * iwl_legacy_fill_probe_req - fill in all required fields and IE for probe request | ||
429 | */ | ||
430 | |||
431 | u16 | ||
432 | iwl_legacy_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame, | ||
433 | const u8 *ta, const u8 *ies, int ie_len, int left) | ||
434 | { | ||
435 | int len = 0; | ||
436 | u8 *pos = NULL; | ||
437 | |||
438 | /* Make sure there is enough space for the probe request, | ||
439 | * two mandatory IEs and the data */ | ||
440 | left -= 24; | ||
441 | if (left < 0) | ||
442 | return 0; | ||
443 | |||
444 | frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); | ||
445 | memcpy(frame->da, iwlegacy_bcast_addr, ETH_ALEN); | ||
446 | memcpy(frame->sa, ta, ETH_ALEN); | ||
447 | memcpy(frame->bssid, iwlegacy_bcast_addr, ETH_ALEN); | ||
448 | frame->seq_ctrl = 0; | ||
449 | |||
450 | len += 24; | ||
451 | |||
452 | /* ...next IE... */ | ||
453 | pos = &frame->u.probe_req.variable[0]; | ||
454 | |||
455 | /* fill in our indirect SSID IE */ | ||
456 | left -= 2; | ||
457 | if (left < 0) | ||
458 | return 0; | ||
459 | *pos++ = WLAN_EID_SSID; | ||
460 | *pos++ = 0; | ||
461 | |||
462 | len += 2; | ||
463 | |||
464 | if (WARN_ON(left < ie_len)) | ||
465 | return len; | ||
466 | |||
467 | if (ies && ie_len) { | ||
468 | memcpy(pos, ies, ie_len); | ||
469 | len += ie_len; | ||
470 | } | ||
471 | |||
472 | return (u16)len; | ||
473 | } | ||
474 | EXPORT_SYMBOL(iwl_legacy_fill_probe_req); | ||
475 | |||
476 | static void iwl_legacy_bg_abort_scan(struct work_struct *work) | ||
477 | { | ||
478 | struct iwl_priv *priv = container_of(work, struct iwl_priv, abort_scan); | ||
479 | |||
480 | IWL_DEBUG_SCAN(priv, "Abort scan work\n"); | ||
481 | |||
482 | /* We keep scan_check work queued in case when firmware will not | ||
483 | * report back scan completed notification */ | ||
484 | mutex_lock(&priv->mutex); | ||
485 | iwl_legacy_scan_cancel_timeout(priv, 200); | ||
486 | mutex_unlock(&priv->mutex); | ||
487 | } | ||
488 | |||
489 | static void iwl_legacy_bg_scan_completed(struct work_struct *work) | ||
490 | { | ||
491 | struct iwl_priv *priv = | ||
492 | container_of(work, struct iwl_priv, scan_completed); | ||
493 | bool aborted; | ||
494 | |||
495 | IWL_DEBUG_SCAN(priv, "Completed scan.\n"); | ||
496 | |||
497 | cancel_delayed_work(&priv->scan_check); | ||
498 | |||
499 | mutex_lock(&priv->mutex); | ||
500 | |||
501 | aborted = test_and_clear_bit(STATUS_SCAN_ABORTING, &priv->status); | ||
502 | if (aborted) | ||
503 | IWL_DEBUG_SCAN(priv, "Aborted scan completed.\n"); | ||
504 | |||
505 | if (!test_and_clear_bit(STATUS_SCANNING, &priv->status)) { | ||
506 | IWL_DEBUG_SCAN(priv, "Scan already completed.\n"); | ||
507 | goto out_settings; | ||
508 | } | ||
509 | |||
510 | iwl_legacy_complete_scan(priv, aborted); | ||
511 | |||
512 | out_settings: | ||
513 | /* Can we still talk to firmware ? */ | ||
514 | if (!iwl_legacy_is_ready_rf(priv)) | ||
515 | goto out; | ||
516 | |||
517 | /* | ||
518 | * We do not commit power settings while scan is pending, | ||
519 | * do it now if the settings changed. | ||
520 | */ | ||
521 | iwl_legacy_power_set_mode(priv, &priv->power_data.sleep_cmd_next, false); | ||
522 | iwl_legacy_set_tx_power(priv, priv->tx_power_next, false); | ||
523 | |||
524 | priv->cfg->ops->utils->post_scan(priv); | ||
525 | |||
526 | out: | ||
527 | mutex_unlock(&priv->mutex); | ||
528 | } | ||
529 | |||
530 | void iwl_legacy_setup_scan_deferred_work(struct iwl_priv *priv) | ||
531 | { | ||
532 | INIT_WORK(&priv->scan_completed, iwl_legacy_bg_scan_completed); | ||
533 | INIT_WORK(&priv->abort_scan, iwl_legacy_bg_abort_scan); | ||
534 | INIT_DELAYED_WORK(&priv->scan_check, iwl_legacy_bg_scan_check); | ||
535 | } | ||
536 | EXPORT_SYMBOL(iwl_legacy_setup_scan_deferred_work); | ||
537 | |||
538 | void iwl_legacy_cancel_scan_deferred_work(struct iwl_priv *priv) | ||
539 | { | ||
540 | cancel_work_sync(&priv->abort_scan); | ||
541 | cancel_work_sync(&priv->scan_completed); | ||
542 | |||
543 | if (cancel_delayed_work_sync(&priv->scan_check)) { | ||
544 | mutex_lock(&priv->mutex); | ||
545 | iwl_legacy_force_scan_end(priv); | ||
546 | mutex_unlock(&priv->mutex); | ||
547 | } | ||
548 | } | ||
549 | EXPORT_SYMBOL(iwl_legacy_cancel_scan_deferred_work); | ||