2 * Scanning implementation
4 * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
5 * Copyright 2004, Instant802 Networks, Inc.
6 * Copyright 2005, Devicescape Software, Inc.
7 * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
8 * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
15 /* TODO: figure out how to avoid that the "current BSS" expires */
17 #include <linux/wireless.h>
18 #include <linux/if_arp.h>
19 #include <linux/rtnetlink.h>
20 #include <net/mac80211.h>
21 #include <net/iw_handler.h>
23 #include "ieee80211_i.h"
26 #define IEEE80211_PROBE_DELAY (HZ / 33)
27 #define IEEE80211_CHANNEL_TIME (HZ / 33)
28 #define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 5)
30 struct ieee80211_bss
*
31 ieee80211_rx_bss_get(struct ieee80211_local
*local
, u8
*bssid
, int freq
,
32 u8
*ssid
, u8 ssid_len
)
34 return (void *)cfg80211_get_bss(local
->hw
.wiphy
,
35 ieee80211_get_channel(local
->hw
.wiphy
,
37 bssid
, ssid
, ssid_len
,
41 static void ieee80211_rx_bss_free(struct cfg80211_bss
*cbss
)
43 struct ieee80211_bss
*bss
= (void *)cbss
;
45 kfree(bss_mesh_id(bss
));
46 kfree(bss_mesh_cfg(bss
));
49 void ieee80211_rx_bss_put(struct ieee80211_local
*local
,
50 struct ieee80211_bss
*bss
)
52 cfg80211_put_bss((struct cfg80211_bss
*)bss
);
55 struct ieee80211_bss
*
56 ieee80211_bss_info_update(struct ieee80211_local
*local
,
57 struct ieee80211_rx_status
*rx_status
,
58 struct ieee80211_mgmt
*mgmt
,
60 struct ieee802_11_elems
*elems
,
61 struct ieee80211_channel
*channel
,
64 struct ieee80211_bss
*bss
;
68 if (local
->hw
.flags
& IEEE80211_HW_SIGNAL_DBM
)
69 signal
= rx_status
->signal
* 100;
70 else if (local
->hw
.flags
& IEEE80211_HW_SIGNAL_UNSPEC
)
71 signal
= (rx_status
->signal
* 100) / local
->hw
.max_signal
;
73 bss
= (void *)cfg80211_inform_bss_frame(local
->hw
.wiphy
, channel
,
74 mgmt
, len
, signal
, GFP_ATOMIC
);
79 bss
->cbss
.free_priv
= ieee80211_rx_bss_free
;
81 /* save the ERP value so that it is available at association time */
82 if (elems
->erp_info
&& elems
->erp_info_len
>= 1) {
83 bss
->erp_value
= elems
->erp_info
[0];
84 bss
->has_erp_value
= 1;
88 struct ieee80211_tim_ie
*tim_ie
=
89 (struct ieee80211_tim_ie
*)elems
->tim
;
90 bss
->dtim_period
= tim_ie
->dtim_period
;
93 /* set default value for buggy APs */
94 if (!elems
->tim
|| bss
->dtim_period
== 0)
97 bss
->supp_rates_len
= 0;
98 if (elems
->supp_rates
) {
99 clen
= IEEE80211_MAX_SUPP_RATES
- bss
->supp_rates_len
;
100 if (clen
> elems
->supp_rates_len
)
101 clen
= elems
->supp_rates_len
;
102 memcpy(&bss
->supp_rates
[bss
->supp_rates_len
], elems
->supp_rates
,
104 bss
->supp_rates_len
+= clen
;
106 if (elems
->ext_supp_rates
) {
107 clen
= IEEE80211_MAX_SUPP_RATES
- bss
->supp_rates_len
;
108 if (clen
> elems
->ext_supp_rates_len
)
109 clen
= elems
->ext_supp_rates_len
;
110 memcpy(&bss
->supp_rates
[bss
->supp_rates_len
],
111 elems
->ext_supp_rates
, clen
);
112 bss
->supp_rates_len
+= clen
;
115 bss
->wmm_used
= elems
->wmm_param
|| elems
->wmm_info
;
118 bss
->last_probe_resp
= jiffies
;
123 void ieee80211_rx_bss_remove(struct ieee80211_sub_if_data
*sdata
, u8
*bssid
,
124 int freq
, u8
*ssid
, u8 ssid_len
)
126 struct ieee80211_bss
*bss
;
127 struct ieee80211_local
*local
= sdata
->local
;
129 bss
= ieee80211_rx_bss_get(local
, bssid
, freq
, ssid
, ssid_len
);
131 cfg80211_unlink_bss(local
->hw
.wiphy
, (void *)bss
);
132 ieee80211_rx_bss_put(local
, bss
);
137 ieee80211_scan_rx(struct ieee80211_sub_if_data
*sdata
, struct sk_buff
*skb
,
138 struct ieee80211_rx_status
*rx_status
)
140 struct ieee80211_mgmt
*mgmt
;
141 struct ieee80211_bss
*bss
;
143 struct ieee80211_channel
*channel
;
147 bool presp
, beacon
= false;
148 struct ieee802_11_elems elems
;
151 return RX_DROP_UNUSABLE
;
153 mgmt
= (struct ieee80211_mgmt
*) skb
->data
;
154 fc
= mgmt
->frame_control
;
156 if (ieee80211_is_ctl(fc
))
160 return RX_DROP_MONITOR
;
162 presp
= ieee80211_is_probe_resp(fc
);
164 /* ignore ProbeResp to foreign address */
165 if (memcmp(mgmt
->da
, sdata
->dev
->dev_addr
, ETH_ALEN
))
166 return RX_DROP_MONITOR
;
169 elements
= mgmt
->u
.probe_resp
.variable
;
170 baselen
= offsetof(struct ieee80211_mgmt
, u
.probe_resp
.variable
);
172 beacon
= ieee80211_is_beacon(fc
);
173 baselen
= offsetof(struct ieee80211_mgmt
, u
.beacon
.variable
);
174 elements
= mgmt
->u
.beacon
.variable
;
177 if (!presp
&& !beacon
)
180 if (baselen
> skb
->len
)
181 return RX_DROP_MONITOR
;
183 ieee802_11_parse_elems(elements
, skb
->len
- baselen
, &elems
);
185 if (elems
.ds_params
&& elems
.ds_params_len
== 1)
186 freq
= ieee80211_channel_to_frequency(elems
.ds_params
[0]);
188 freq
= rx_status
->freq
;
190 channel
= ieee80211_get_channel(sdata
->local
->hw
.wiphy
, freq
);
192 if (!channel
|| channel
->flags
& IEEE80211_CHAN_DISABLED
)
193 return RX_DROP_MONITOR
;
195 bss
= ieee80211_bss_info_update(sdata
->local
, rx_status
,
196 mgmt
, skb
->len
, &elems
,
199 ieee80211_rx_bss_put(sdata
->local
, bss
);
205 void ieee80211_scan_completed(struct ieee80211_hw
*hw
, bool aborted
)
207 struct ieee80211_local
*local
= hw_to_local(hw
);
208 struct ieee80211_sub_if_data
*sdata
;
210 if (WARN_ON(!local
->hw_scanning
&& !local
->sw_scanning
))
213 if (WARN_ON(!local
->scan_req
))
216 if (local
->scan_req
!= &local
->int_scan_req
)
217 cfg80211_scan_done(local
->scan_req
, aborted
);
218 local
->scan_req
= NULL
;
220 local
->last_scan_completed
= jiffies
;
222 if (local
->hw_scanning
) {
223 local
->hw_scanning
= false;
225 * Somebody might have requested channel change during scan
226 * that we won't have acted upon, try now. ieee80211_hw_config
227 * will set the flag based on actual changes.
229 ieee80211_hw_config(local
, 0);
233 local
->sw_scanning
= false;
234 ieee80211_hw_config(local
, IEEE80211_CONF_CHANGE_CHANNEL
);
236 netif_tx_lock_bh(local
->mdev
);
237 netif_addr_lock(local
->mdev
);
238 local
->filter_flags
&= ~FIF_BCN_PRBRESP_PROMISC
;
239 local
->ops
->configure_filter(local_to_hw(local
),
240 FIF_BCN_PRBRESP_PROMISC
,
241 &local
->filter_flags
,
242 local
->mdev
->mc_count
,
243 local
->mdev
->mc_list
);
245 netif_addr_unlock(local
->mdev
);
246 netif_tx_unlock_bh(local
->mdev
);
248 if (local
->ops
->sw_scan_complete
)
249 local
->ops
->sw_scan_complete(local_to_hw(local
));
251 mutex_lock(&local
->iflist_mtx
);
252 list_for_each_entry(sdata
, &local
->interfaces
, list
) {
253 if (!netif_running(sdata
->dev
))
256 /* Tell AP we're back */
257 if (sdata
->vif
.type
== NL80211_IFTYPE_STATION
) {
258 if (sdata
->u
.mgd
.flags
& IEEE80211_STA_ASSOCIATED
) {
259 ieee80211_send_nullfunc(local
, sdata
, 0);
260 netif_tx_wake_all_queues(sdata
->dev
);
263 netif_tx_wake_all_queues(sdata
->dev
);
265 /* re-enable beaconing */
266 if (sdata
->vif
.type
== NL80211_IFTYPE_AP
||
267 sdata
->vif
.type
== NL80211_IFTYPE_ADHOC
||
268 sdata
->vif
.type
== NL80211_IFTYPE_MESH_POINT
)
269 ieee80211_if_config(sdata
,
270 IEEE80211_IFCC_BEACON_ENABLED
);
272 mutex_unlock(&local
->iflist_mtx
);
275 ieee80211_mlme_notify_scan_completed(local
);
276 ieee80211_ibss_notify_scan_completed(local
);
277 ieee80211_mesh_notify_scan_completed(local
);
279 EXPORT_SYMBOL(ieee80211_scan_completed
);
281 void ieee80211_scan_work(struct work_struct
*work
)
283 struct ieee80211_local
*local
=
284 container_of(work
, struct ieee80211_local
, scan_work
.work
);
285 struct ieee80211_sub_if_data
*sdata
= local
->scan_sdata
;
286 struct ieee80211_channel
*chan
;
288 unsigned long next_delay
= 0;
291 * Avoid re-scheduling when the sdata is going away.
293 if (!netif_running(sdata
->dev
))
296 switch (local
->scan_state
) {
297 case SCAN_SET_CHANNEL
:
298 /* if no more bands/channels left, complete scan */
299 if (local
->scan_channel_idx
>= local
->scan_req
->n_channels
) {
300 ieee80211_scan_completed(local_to_hw(local
), false);
304 chan
= local
->scan_req
->channels
[local
->scan_channel_idx
];
306 if (chan
->flags
& IEEE80211_CHAN_DISABLED
||
307 (sdata
->vif
.type
== NL80211_IFTYPE_ADHOC
&&
308 chan
->flags
& IEEE80211_CHAN_NO_IBSS
))
312 local
->scan_channel
= chan
;
313 if (ieee80211_hw_config(local
,
314 IEEE80211_CONF_CHANGE_CHANNEL
))
318 /* advance state machine to next channel/band */
319 local
->scan_channel_idx
++;
324 next_delay
= IEEE80211_PROBE_DELAY
+
325 usecs_to_jiffies(local
->hw
.channel_change_time
);
326 local
->scan_state
= SCAN_SEND_PROBE
;
328 case SCAN_SEND_PROBE
:
329 next_delay
= IEEE80211_PASSIVE_CHANNEL_TIME
;
330 local
->scan_state
= SCAN_SET_CHANNEL
;
332 if (local
->scan_channel
->flags
& IEEE80211_CHAN_PASSIVE_SCAN
||
333 !local
->scan_req
->n_ssids
)
335 for (i
= 0; i
< local
->scan_req
->n_ssids
; i
++)
336 ieee80211_send_probe_req(
338 local
->scan_req
->ssids
[i
].ssid
,
339 local
->scan_req
->ssids
[i
].ssid_len
,
340 local
->scan_req
->ie
, local
->scan_req
->ie_len
);
341 next_delay
= IEEE80211_CHANNEL_TIME
;
345 queue_delayed_work(local
->hw
.workqueue
, &local
->scan_work
,
350 int ieee80211_start_scan(struct ieee80211_sub_if_data
*scan_sdata
,
351 struct cfg80211_scan_request
*req
)
353 struct ieee80211_local
*local
= scan_sdata
->local
;
354 struct ieee80211_sub_if_data
*sdata
;
359 if (local
->scan_req
&& local
->scan_req
!= req
)
362 local
->scan_req
= req
;
364 /* MLME-SCAN.request (page 118) page 144 (11.1.3.1)
365 * BSSType: INFRASTRUCTURE, INDEPENDENT, ANY_BSS
368 * ScanType: ACTIVE, PASSIVE
369 * ProbeDelay: delay (in microseconds) to be used prior to transmitting
370 * a Probe frame during active scanning
372 * MinChannelTime (>= ProbeDelay), in TU
373 * MaxChannelTime: (>= MinChannelTime), in TU
378 * ResultCode: SUCCESS, INVALID_PARAMETERS
381 if (local
->sw_scanning
|| local
->hw_scanning
) {
382 if (local
->scan_sdata
== scan_sdata
)
387 if (local
->ops
->hw_scan
) {
390 local
->hw_scanning
= true;
391 rc
= local
->ops
->hw_scan(local_to_hw(local
), req
);
393 local
->hw_scanning
= false;
396 local
->scan_sdata
= scan_sdata
;
400 local
->sw_scanning
= true;
401 if (local
->ops
->sw_scan_start
)
402 local
->ops
->sw_scan_start(local_to_hw(local
));
404 mutex_lock(&local
->iflist_mtx
);
405 list_for_each_entry(sdata
, &local
->interfaces
, list
) {
406 if (!netif_running(sdata
->dev
))
409 /* disable beaconing */
410 if (sdata
->vif
.type
== NL80211_IFTYPE_AP
||
411 sdata
->vif
.type
== NL80211_IFTYPE_ADHOC
||
412 sdata
->vif
.type
== NL80211_IFTYPE_MESH_POINT
)
413 ieee80211_if_config(sdata
,
414 IEEE80211_IFCC_BEACON_ENABLED
);
416 if (sdata
->vif
.type
== NL80211_IFTYPE_STATION
) {
417 if (sdata
->u
.mgd
.flags
& IEEE80211_STA_ASSOCIATED
) {
418 netif_tx_stop_all_queues(sdata
->dev
);
419 ieee80211_send_nullfunc(local
, sdata
, 1);
422 netif_tx_stop_all_queues(sdata
->dev
);
424 mutex_unlock(&local
->iflist_mtx
);
426 local
->scan_state
= SCAN_SET_CHANNEL
;
427 local
->scan_channel_idx
= 0;
428 local
->scan_sdata
= scan_sdata
;
429 local
->scan_req
= req
;
431 netif_addr_lock_bh(local
->mdev
);
432 local
->filter_flags
|= FIF_BCN_PRBRESP_PROMISC
;
433 local
->ops
->configure_filter(local_to_hw(local
),
434 FIF_BCN_PRBRESP_PROMISC
,
435 &local
->filter_flags
,
436 local
->mdev
->mc_count
,
437 local
->mdev
->mc_list
);
438 netif_addr_unlock_bh(local
->mdev
);
440 /* TODO: start scan as soon as all nullfunc frames are ACKed */
441 queue_delayed_work(local
->hw
.workqueue
, &local
->scan_work
,
442 IEEE80211_CHANNEL_TIME
);
448 int ieee80211_request_scan(struct ieee80211_sub_if_data
*sdata
,
449 struct cfg80211_scan_request
*req
)
451 struct ieee80211_local
*local
= sdata
->local
;
452 struct ieee80211_if_managed
*ifmgd
;
457 if (local
->scan_req
&& local
->scan_req
!= req
)
460 local
->scan_req
= req
;
462 if (sdata
->vif
.type
!= NL80211_IFTYPE_STATION
)
463 return ieee80211_start_scan(sdata
, req
);
466 * STA has a state machine that might need to defer scanning
467 * while it's trying to associate/authenticate, therefore we
468 * queue it up to the state machine in that case.
471 if (local
->sw_scanning
|| local
->hw_scanning
) {
472 if (local
->scan_sdata
== sdata
)
477 ifmgd
= &sdata
->u
.mgd
;
478 set_bit(IEEE80211_STA_REQ_SCAN
, &ifmgd
->request
);
479 queue_work(local
->hw
.workqueue
, &ifmgd
->work
);