[PATCH] softmac: scan at least once before selecting a network by essid
[safe/jmp/linux-2.6] / net / ieee80211 / softmac / ieee80211softmac_scan.c
1 /*
2  * Scanning routines.
3  *
4  * These are not exported because they're assigned to the function pointers.
5  */
6
7 #include <linux/completion.h>
8 #include "ieee80211softmac_priv.h"
9
10 /* internal, use to trigger scanning if needed.
11  * Returns -EBUSY if already scanning,
12  * result of start_scan otherwise */
13 int
14 ieee80211softmac_start_scan(struct ieee80211softmac_device *sm)
15 {
16         unsigned long flags;
17         int ret;
18
19         spin_lock_irqsave(&sm->lock, flags);
20         if (sm->scanning)
21         {
22                 spin_unlock_irqrestore(&sm->lock, flags);
23                 return -EINPROGRESS;
24         }
25         sm->scanning = 1;
26         spin_unlock_irqrestore(&sm->lock, flags);
27
28         ret = sm->start_scan(sm->dev);
29         if (ret) {
30                 spin_lock_irqsave(&sm->lock, flags);
31                 sm->scanning = 0;
32                 spin_unlock_irqrestore(&sm->lock, flags);
33         }
34         return ret;
35 }
36
37 void
38 ieee80211softmac_stop_scan(struct ieee80211softmac_device *sm)
39 {
40         unsigned long flags;
41
42         spin_lock_irqsave(&sm->lock, flags);
43         
44         if (!sm->scanning) {
45                 spin_unlock_irqrestore(&sm->lock, flags);
46                 return;
47         }
48         
49         spin_unlock_irqrestore(&sm->lock, flags);
50         sm->stop_scan(sm->dev);
51 }
52
53 void
54 ieee80211softmac_wait_for_scan(struct ieee80211softmac_device *sm)
55 {
56         unsigned long flags;
57
58         spin_lock_irqsave(&sm->lock, flags);
59         
60         if (!sm->scanning) {
61                 spin_unlock_irqrestore(&sm->lock, flags);
62                 return;
63         }
64         
65         spin_unlock_irqrestore(&sm->lock, flags);
66         sm->wait_for_scan(sm->dev);
67 }
68
69
70 /* internal scanning implementation follows */
71 void ieee80211softmac_scan(void *d)
72 {
73         int invalid_channel;
74         u8 current_channel_idx;
75         struct ieee80211softmac_device *sm = (struct ieee80211softmac_device *)d;
76         struct ieee80211softmac_scaninfo *si = sm->scaninfo;
77         unsigned long flags;
78
79         while (!(si->stop) && (si->current_channel_idx < si->number_channels)) {
80                 current_channel_idx = si->current_channel_idx;
81                 si->current_channel_idx++; /* go to the next channel */
82
83                 invalid_channel = (si->skip_flags & si->channels[current_channel_idx].flags);
84
85                 if (!invalid_channel) {
86                         sm->set_channel(sm->dev, si->channels[current_channel_idx].channel);
87                         //TODO: Probe the channel
88                         // FIXME make this user configurable (active/passive)
89                         if(ieee80211softmac_send_mgt_frame(sm, NULL, IEEE80211_STYPE_PROBE_REQ, 0))
90                                 printkl(KERN_DEBUG PFX "Sending Probe Request Failed\n");
91
92                         /* also send directed management frame for the network we're looking for */
93                         // TODO: is this if correct, or should we do this only if scanning from assoc request?
94                         if (sm->associnfo.req_essid.len)
95                                 ieee80211softmac_send_mgt_frame(sm, &sm->associnfo.req_essid, IEEE80211_STYPE_PROBE_REQ, 0);
96                         schedule_delayed_work(&si->softmac_scan, IEEE80211SOFTMAC_PROBE_DELAY);
97                         return;
98                 } else {
99                         dprintk(PFX "Not probing Channel %d (not allowed here)\n", si->channels[current_channel_idx].channel);
100                 }
101         }
102
103         spin_lock_irqsave(&sm->lock, flags);
104         cancel_delayed_work(&si->softmac_scan);
105         si->started = 0;
106         spin_unlock_irqrestore(&sm->lock, flags);
107
108         dprintk(PFX "Scanning finished\n");
109         ieee80211softmac_scan_finished(sm);
110         complete_all(&sm->scaninfo->finished);
111 }
112
113 static inline struct ieee80211softmac_scaninfo *allocate_scaninfo(struct ieee80211softmac_device *mac)
114 {
115         /* ugh. can we call this without having the spinlock held? */
116         struct ieee80211softmac_scaninfo *info = kmalloc(sizeof(struct ieee80211softmac_scaninfo), GFP_ATOMIC);
117         if (unlikely(!info))
118                 return NULL;
119         INIT_WORK(&info->softmac_scan, ieee80211softmac_scan, mac);
120         init_completion(&info->finished);
121         return info;
122 }
123
124 int ieee80211softmac_start_scan_implementation(struct net_device *dev)
125 {
126         struct ieee80211softmac_device *sm = ieee80211_priv(dev);
127         unsigned long flags;
128         
129         if (!(dev->flags & IFF_UP))
130                 return -ENODEV;
131
132         assert(ieee80211softmac_scan_handlers_check_self(sm));
133         if (!ieee80211softmac_scan_handlers_check_self(sm))
134                 return -EINVAL;
135                 
136         spin_lock_irqsave(&sm->lock, flags);
137         /* it looks like we need to hold the lock here
138          * to make sure we don't allocate two of these... */
139         if (unlikely(!sm->scaninfo))
140                 sm->scaninfo = allocate_scaninfo(sm);
141         if (unlikely(!sm->scaninfo)) {
142                 spin_unlock_irqrestore(&sm->lock, flags);
143                 return -ENOMEM;
144         }
145
146         sm->scaninfo->skip_flags = IEEE80211_CH_INVALID;
147         if (0 /* not scanning in IEEE802.11b */)//TODO
148                 sm->scaninfo->skip_flags |= IEEE80211_CH_B_ONLY;
149         if (0 /* IEEE802.11a */) {//TODO
150                 sm->scaninfo->channels = sm->ieee->geo.a;
151                 sm->scaninfo->number_channels = sm->ieee->geo.a_channels;
152         } else {
153                 sm->scaninfo->channels = sm->ieee->geo.bg;
154                 sm->scaninfo->number_channels = sm->ieee->geo.bg_channels;
155         }
156         dprintk(PFX "Start scanning with channel: %d\n", sm->scaninfo->channels[0].channel);
157         dprintk(PFX "Scanning %d channels\n", sm->scaninfo->number_channels);
158         sm->scaninfo->current_channel_idx = 0;
159         sm->scaninfo->started = 1;
160         INIT_COMPLETION(sm->scaninfo->finished);
161         schedule_work(&sm->scaninfo->softmac_scan);
162         spin_unlock_irqrestore(&sm->lock, flags);
163         return 0;
164 }
165
166 void ieee80211softmac_stop_scan_implementation(struct net_device *dev)
167 {
168         struct ieee80211softmac_device *sm = ieee80211_priv(dev);
169         unsigned long flags;
170
171         assert(ieee80211softmac_scan_handlers_check_self(sm));
172         if (!ieee80211softmac_scan_handlers_check_self(sm))
173                 return;
174
175         spin_lock_irqsave(&sm->lock, flags);
176         assert(sm->scaninfo != NULL);
177         if (sm->scaninfo) {
178                 if (sm->scaninfo->started)
179                         sm->scaninfo->stop = 1;
180                 else
181                         complete_all(&sm->scaninfo->finished);
182         }
183         spin_unlock_irqrestore(&sm->lock, flags);
184 }
185
186 void ieee80211softmac_wait_for_scan_implementation(struct net_device *dev)
187 {
188         struct ieee80211softmac_device *sm = ieee80211_priv(dev);
189         unsigned long flags;
190
191         assert(ieee80211softmac_scan_handlers_check_self(sm));
192         if (!ieee80211softmac_scan_handlers_check_self(sm))
193                 return;
194
195         spin_lock_irqsave(&sm->lock, flags);
196         if (!sm->scaninfo->started) {
197                 spin_unlock_irqrestore(&sm->lock, flags);
198                 return;
199         }
200         spin_unlock_irqrestore(&sm->lock, flags);
201         wait_for_completion(&sm->scaninfo->finished);
202 }
203
204 /* this is what drivers (that do scanning) call when they're done */
205 void ieee80211softmac_scan_finished(struct ieee80211softmac_device *sm)
206 {
207         unsigned long flags;
208
209         spin_lock_irqsave(&sm->lock, flags);
210         sm->scanning = 0;
211         spin_unlock_irqrestore(&sm->lock, flags);
212         
213         ieee80211softmac_call_events(sm, IEEE80211SOFTMAC_EVENT_SCAN_FINISHED, NULL);
214 }
215
216 EXPORT_SYMBOL_GPL(ieee80211softmac_scan_finished);