]>
Commit | Line | Data |
---|---|---|
370121e5 JB |
1 | /* |
2 | * Scanning routines. | |
3 | * | |
4 | * These are not exported because they're assigned to the function pointers. | |
4855d25b | 5 | * |
79859051 JB |
6 | * Copyright (c) 2005, 2006 Johannes Berg <[email protected]> |
7 | * Joseph Jezak <[email protected]> | |
8 | * Larry Finger <[email protected]> | |
9 | * Danny van Dyk <[email protected]> | |
10 | * Michael Buesch <[email protected]> | |
4855d25b JB |
11 | * |
12 | * This program is free software; you can redistribute it and/or modify it | |
13 | * under the terms of version 2 of the GNU General Public License as | |
14 | * published by the Free Software Foundation. | |
15 | * | |
16 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
17 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
18 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
19 | * more details. | |
20 | * | |
21 | * You should have received a copy of the GNU General Public License | |
22 | * along with this program; if not, write to the Free Software | |
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
24 | * | |
25 | * The full GNU General Public License is included in this distribution in the | |
26 | * file called COPYING. | |
370121e5 JB |
27 | */ |
28 | ||
29 | #include <linux/completion.h> | |
30 | #include "ieee80211softmac_priv.h" | |
31 | ||
32 | /* internal, use to trigger scanning if needed. | |
33 | * Returns -EBUSY if already scanning, | |
34 | * result of start_scan otherwise */ | |
35 | int | |
36 | ieee80211softmac_start_scan(struct ieee80211softmac_device *sm) | |
37 | { | |
38 | unsigned long flags; | |
39 | int ret; | |
40 | ||
41 | spin_lock_irqsave(&sm->lock, flags); | |
42 | if (sm->scanning) | |
43 | { | |
44 | spin_unlock_irqrestore(&sm->lock, flags); | |
45 | return -EINPROGRESS; | |
46 | } | |
47 | sm->scanning = 1; | |
48 | spin_unlock_irqrestore(&sm->lock, flags); | |
49 | ||
fc242746 | 50 | netif_tx_disable(sm->ieee->dev); |
370121e5 JB |
51 | ret = sm->start_scan(sm->dev); |
52 | if (ret) { | |
53 | spin_lock_irqsave(&sm->lock, flags); | |
54 | sm->scanning = 0; | |
55 | spin_unlock_irqrestore(&sm->lock, flags); | |
56 | } | |
57 | return ret; | |
58 | } | |
59 | ||
60 | void | |
61 | ieee80211softmac_stop_scan(struct ieee80211softmac_device *sm) | |
62 | { | |
63 | unsigned long flags; | |
64 | ||
65 | spin_lock_irqsave(&sm->lock, flags); | |
66 | ||
67 | if (!sm->scanning) { | |
68 | spin_unlock_irqrestore(&sm->lock, flags); | |
69 | return; | |
70 | } | |
71 | ||
72 | spin_unlock_irqrestore(&sm->lock, flags); | |
73 | sm->stop_scan(sm->dev); | |
74 | } | |
75 | ||
76 | void | |
77 | ieee80211softmac_wait_for_scan(struct ieee80211softmac_device *sm) | |
78 | { | |
79 | unsigned long flags; | |
80 | ||
81 | spin_lock_irqsave(&sm->lock, flags); | |
82 | ||
83 | if (!sm->scanning) { | |
84 | spin_unlock_irqrestore(&sm->lock, flags); | |
85 | return; | |
86 | } | |
87 | ||
88 | spin_unlock_irqrestore(&sm->lock, flags); | |
89 | sm->wait_for_scan(sm->dev); | |
90 | } | |
91 | ||
92 | ||
93 | /* internal scanning implementation follows */ | |
c4028958 | 94 | void ieee80211softmac_scan(struct work_struct *work) |
370121e5 JB |
95 | { |
96 | int invalid_channel; | |
97 | u8 current_channel_idx; | |
c4028958 DH |
98 | struct ieee80211softmac_scaninfo *si = |
99 | container_of(work, struct ieee80211softmac_scaninfo, | |
100 | softmac_scan.work); | |
101 | struct ieee80211softmac_device *sm = si->mac; | |
370121e5 JB |
102 | unsigned long flags; |
103 | ||
104 | while (!(si->stop) && (si->current_channel_idx < si->number_channels)) { | |
105 | current_channel_idx = si->current_channel_idx; | |
106 | si->current_channel_idx++; /* go to the next channel */ | |
107 | ||
108 | invalid_channel = (si->skip_flags & si->channels[current_channel_idx].flags); | |
109 | ||
110 | if (!invalid_channel) { | |
111 | sm->set_channel(sm->dev, si->channels[current_channel_idx].channel); | |
370121e5 JB |
112 | // FIXME make this user configurable (active/passive) |
113 | if(ieee80211softmac_send_mgt_frame(sm, NULL, IEEE80211_STYPE_PROBE_REQ, 0)) | |
114 | printkl(KERN_DEBUG PFX "Sending Probe Request Failed\n"); | |
115 | ||
116 | /* also send directed management frame for the network we're looking for */ | |
117 | // TODO: is this if correct, or should we do this only if scanning from assoc request? | |
118 | if (sm->associnfo.req_essid.len) | |
119 | ieee80211softmac_send_mgt_frame(sm, &sm->associnfo.req_essid, IEEE80211_STYPE_PROBE_REQ, 0); | |
d57336e3 DD |
120 | |
121 | spin_lock_irqsave(&sm->lock, flags); | |
122 | if (unlikely(!sm->running)) { | |
123 | /* Prevent reschedule on workqueue flush */ | |
124 | spin_unlock_irqrestore(&sm->lock, flags); | |
125 | break; | |
126 | } | |
5c4df6da | 127 | schedule_delayed_work(&si->softmac_scan, IEEE80211SOFTMAC_PROBE_DELAY); |
d57336e3 | 128 | spin_unlock_irqrestore(&sm->lock, flags); |
370121e5 JB |
129 | return; |
130 | } else { | |
131 | dprintk(PFX "Not probing Channel %d (not allowed here)\n", si->channels[current_channel_idx].channel); | |
132 | } | |
133 | } | |
134 | ||
135 | spin_lock_irqsave(&sm->lock, flags); | |
136 | cancel_delayed_work(&si->softmac_scan); | |
137 | si->started = 0; | |
138 | spin_unlock_irqrestore(&sm->lock, flags); | |
139 | ||
140 | dprintk(PFX "Scanning finished\n"); | |
141 | ieee80211softmac_scan_finished(sm); | |
142 | complete_all(&sm->scaninfo->finished); | |
143 | } | |
144 | ||
145 | static inline struct ieee80211softmac_scaninfo *allocate_scaninfo(struct ieee80211softmac_device *mac) | |
146 | { | |
147 | /* ugh. can we call this without having the spinlock held? */ | |
148 | struct ieee80211softmac_scaninfo *info = kmalloc(sizeof(struct ieee80211softmac_scaninfo), GFP_ATOMIC); | |
149 | if (unlikely(!info)) | |
150 | return NULL; | |
c4028958 DH |
151 | INIT_DELAYED_WORK(&info->softmac_scan, ieee80211softmac_scan); |
152 | info->mac = mac; | |
370121e5 JB |
153 | init_completion(&info->finished); |
154 | return info; | |
155 | } | |
156 | ||
157 | int ieee80211softmac_start_scan_implementation(struct net_device *dev) | |
158 | { | |
159 | struct ieee80211softmac_device *sm = ieee80211_priv(dev); | |
160 | unsigned long flags; | |
161 | ||
162 | if (!(dev->flags & IFF_UP)) | |
163 | return -ENODEV; | |
164 | ||
165 | assert(ieee80211softmac_scan_handlers_check_self(sm)); | |
166 | if (!ieee80211softmac_scan_handlers_check_self(sm)) | |
167 | return -EINVAL; | |
168 | ||
169 | spin_lock_irqsave(&sm->lock, flags); | |
170 | /* it looks like we need to hold the lock here | |
171 | * to make sure we don't allocate two of these... */ | |
172 | if (unlikely(!sm->scaninfo)) | |
173 | sm->scaninfo = allocate_scaninfo(sm); | |
174 | if (unlikely(!sm->scaninfo)) { | |
175 | spin_unlock_irqrestore(&sm->lock, flags); | |
176 | return -ENOMEM; | |
177 | } | |
178 | ||
179 | sm->scaninfo->skip_flags = IEEE80211_CH_INVALID; | |
180 | if (0 /* not scanning in IEEE802.11b */)//TODO | |
181 | sm->scaninfo->skip_flags |= IEEE80211_CH_B_ONLY; | |
182 | if (0 /* IEEE802.11a */) {//TODO | |
183 | sm->scaninfo->channels = sm->ieee->geo.a; | |
184 | sm->scaninfo->number_channels = sm->ieee->geo.a_channels; | |
185 | } else { | |
186 | sm->scaninfo->channels = sm->ieee->geo.bg; | |
187 | sm->scaninfo->number_channels = sm->ieee->geo.bg_channels; | |
188 | } | |
189 | dprintk(PFX "Start scanning with channel: %d\n", sm->scaninfo->channels[0].channel); | |
190 | dprintk(PFX "Scanning %d channels\n", sm->scaninfo->number_channels); | |
191 | sm->scaninfo->current_channel_idx = 0; | |
192 | sm->scaninfo->started = 1; | |
fe0b06b1 | 193 | sm->scaninfo->stop = 0; |
370121e5 | 194 | INIT_COMPLETION(sm->scaninfo->finished); |
c4028958 | 195 | schedule_delayed_work(&sm->scaninfo->softmac_scan, 0); |
370121e5 JB |
196 | spin_unlock_irqrestore(&sm->lock, flags); |
197 | return 0; | |
198 | } | |
199 | ||
200 | void ieee80211softmac_stop_scan_implementation(struct net_device *dev) | |
201 | { | |
202 | struct ieee80211softmac_device *sm = ieee80211_priv(dev); | |
203 | unsigned long flags; | |
204 | ||
205 | assert(ieee80211softmac_scan_handlers_check_self(sm)); | |
206 | if (!ieee80211softmac_scan_handlers_check_self(sm)) | |
207 | return; | |
208 | ||
209 | spin_lock_irqsave(&sm->lock, flags); | |
210 | assert(sm->scaninfo != NULL); | |
211 | if (sm->scaninfo) { | |
212 | if (sm->scaninfo->started) | |
213 | sm->scaninfo->stop = 1; | |
214 | else | |
215 | complete_all(&sm->scaninfo->finished); | |
216 | } | |
217 | spin_unlock_irqrestore(&sm->lock, flags); | |
218 | } | |
219 | ||
220 | void ieee80211softmac_wait_for_scan_implementation(struct net_device *dev) | |
221 | { | |
222 | struct ieee80211softmac_device *sm = ieee80211_priv(dev); | |
223 | unsigned long flags; | |
224 | ||
225 | assert(ieee80211softmac_scan_handlers_check_self(sm)); | |
226 | if (!ieee80211softmac_scan_handlers_check_self(sm)) | |
227 | return; | |
228 | ||
229 | spin_lock_irqsave(&sm->lock, flags); | |
230 | if (!sm->scaninfo->started) { | |
231 | spin_unlock_irqrestore(&sm->lock, flags); | |
232 | return; | |
233 | } | |
234 | spin_unlock_irqrestore(&sm->lock, flags); | |
235 | wait_for_completion(&sm->scaninfo->finished); | |
236 | } | |
237 | ||
238 | /* this is what drivers (that do scanning) call when they're done */ | |
239 | void ieee80211softmac_scan_finished(struct ieee80211softmac_device *sm) | |
240 | { | |
241 | unsigned long flags; | |
242 | ||
243 | spin_lock_irqsave(&sm->lock, flags); | |
244 | sm->scanning = 0; | |
245 | spin_unlock_irqrestore(&sm->lock, flags); | |
246 | ||
4edac92f DW |
247 | if (sm->associnfo.bssvalid) { |
248 | struct ieee80211softmac_network *net; | |
249 | ||
250 | net = ieee80211softmac_get_network_by_bssid(sm, sm->associnfo.bssid); | |
251 | if (net) | |
252 | sm->set_channel(sm->dev, net->channel); | |
253 | } | |
fc242746 | 254 | netif_wake_queue(sm->ieee->dev); |
370121e5 JB |
255 | ieee80211softmac_call_events(sm, IEEE80211SOFTMAC_EVENT_SCAN_FINISHED, NULL); |
256 | } | |
370121e5 | 257 | EXPORT_SYMBOL_GPL(ieee80211softmac_scan_finished); |