[PATCH] wireless: Add softmac layer to the kernel
[safe/jmp/linux-2.6] / net / ieee80211 / softmac / ieee80211softmac_module.c
1 #include "ieee80211softmac_priv.h"
2 #include <linux/sort.h>
3
4 struct net_device *alloc_ieee80211softmac(int sizeof_priv)
5 {
6         struct ieee80211softmac_device *softmac;
7         struct net_device *dev;
8         
9         dev = alloc_ieee80211(sizeof(struct ieee80211softmac_device) + sizeof_priv);
10         softmac = ieee80211_priv(dev);
11         softmac->dev = dev;
12         softmac->ieee = netdev_priv(dev);
13         spin_lock_init(&softmac->lock);
14         
15         softmac->ieee->handle_auth = ieee80211softmac_auth_resp;
16         softmac->ieee->handle_deauth = ieee80211softmac_deauth_resp;
17         softmac->ieee->handle_assoc_response = ieee80211softmac_handle_assoc_response;
18         softmac->ieee->handle_disassoc = ieee80211softmac_handle_disassoc;
19         softmac->scaninfo = NULL;
20
21         /* TODO: initialise all the other callbacks in the ieee struct
22          *       (once they're written)
23          */
24
25         softmac->workqueue = create_workqueue("80211softmac");
26         if (!softmac->workqueue)
27                 goto err_free_ieee80211;
28
29         INIT_LIST_HEAD(&softmac->auth_queue);
30         INIT_LIST_HEAD(&softmac->network_list);
31         INIT_LIST_HEAD(&softmac->events);
32
33         INIT_WORK(&softmac->associnfo.work, ieee80211softmac_assoc_work, softmac);
34         INIT_WORK(&softmac->associnfo.timeout, ieee80211softmac_assoc_timeout, softmac);
35         softmac->start_scan = ieee80211softmac_start_scan_implementation;
36         softmac->wait_for_scan = ieee80211softmac_wait_for_scan_implementation;
37         softmac->stop_scan = ieee80211softmac_stop_scan_implementation;
38
39         //TODO: The mcast rate has to be assigned dynamically somewhere (in scanning, association. Not sure...)
40         //      It has to be set to the highest rate all stations in the current network can handle.
41         softmac->txrates.mcast_rate = IEEE80211_CCK_RATE_1MB;
42         softmac->txrates.mcast_fallback = IEEE80211_CCK_RATE_1MB;
43         /* This is reassigned in ieee80211softmac_start to sane values. */
44         softmac->txrates.default_rate = IEEE80211_CCK_RATE_1MB;
45         softmac->txrates.default_fallback = IEEE80211_CCK_RATE_1MB;
46
47         /* should we also assign softmac->mgmt_xmit here so
48          * that it is always valid? If so, we probably want
49          * to define a new function for that which just
50          * wraps ieee80211_tx_frame
51          */
52         
53         /* until associated, we're not ready */
54         dev->flags &= ~IFF_RUNNING;
55
56         return dev;
57
58 err_free_ieee80211:
59         free_ieee80211(dev);
60
61         return NULL;
62 }
63
64 /* Clears the pending work queue items, stops all scans, etc. */
65 void 
66 ieee80211softmac_clear_pending_work(struct ieee80211softmac_device *sm)
67 {
68         unsigned long flags;
69         struct ieee80211softmac_event *eventptr, *eventtmp;
70         struct ieee80211softmac_auth_queue_item *authptr, *authtmp;
71         struct ieee80211softmac_network *netptr, *nettmp;
72         
73         ieee80211softmac_stop_scan(sm);
74         ieee80211softmac_wait_for_scan(sm);
75         
76         spin_lock_irqsave(&sm->lock, flags);
77         /* Free all pending assoc work items */
78         cancel_delayed_work(&sm->associnfo.work);
79         
80         /* Free all pending scan work items */
81         if(sm->scaninfo != NULL)
82                 cancel_delayed_work(&sm->scaninfo->softmac_scan);       
83         
84         /* Free all pending auth work items */
85         list_for_each_entry(authptr, &sm->auth_queue, list)
86                 cancel_delayed_work(&authptr->work);
87         
88         /* delete all pending event calls and work items */
89         list_for_each_entry_safe(eventptr, eventtmp, &sm->events, list)
90                 cancel_delayed_work(&eventptr->work);
91
92         spin_unlock_irqrestore(&sm->lock, flags);
93         flush_workqueue(sm->workqueue);
94
95         // now we should be save and no longer need locking...
96         spin_lock_irqsave(&sm->lock, flags);
97         /* Free all pending auth work items */
98         list_for_each_entry_safe(authptr, authtmp, &sm->auth_queue, list) {
99                 list_del(&authptr->list);
100                 kfree(authptr);
101         }
102         
103         /* delete all pending event calls and work items */
104         list_for_each_entry_safe(eventptr, eventtmp, &sm->events, list) {
105                 list_del(&eventptr->list);
106                 kfree(eventptr);
107         }
108                 
109         /* Free all networks */
110         list_for_each_entry_safe(netptr, nettmp, &sm->network_list, list) {
111                 ieee80211softmac_del_network_locked(sm, netptr);
112                 if(netptr->challenge != NULL)
113                         kfree(netptr->challenge);
114                 kfree(netptr);
115         }
116
117         spin_unlock_irqrestore(&sm->lock, flags);
118 }
119
120 void free_ieee80211softmac(struct net_device *dev)
121 {
122         struct ieee80211softmac_device *sm = ieee80211_priv(dev);
123         ieee80211softmac_clear_pending_work(sm);        
124         destroy_workqueue(sm->workqueue);
125         kfree(sm->scaninfo);
126         kfree(sm->wpa.IE);
127         free_ieee80211(dev);
128 }
129
130 static void ieee80211softmac_start_check_rates(struct ieee80211softmac_device *mac)
131 {
132         struct ieee80211softmac_ratesinfo *ri = &mac->ratesinfo;
133         /* I took out the sorting check, we're seperating by modulation now. */
134         if (ri->count)
135                 return;
136         /* otherwise assume we hav'em all! */
137         if (mac->ieee->modulation & IEEE80211_CCK_MODULATION) {
138                 ri->rates[ri->count++] = IEEE80211_CCK_RATE_1MB;
139                 ri->rates[ri->count++] = IEEE80211_CCK_RATE_2MB;
140                 ri->rates[ri->count++] = IEEE80211_CCK_RATE_5MB;
141                 ri->rates[ri->count++] = IEEE80211_CCK_RATE_11MB;
142         }
143         if (mac->ieee->modulation & IEEE80211_OFDM_MODULATION) {
144                 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_6MB;
145                 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_9MB;
146                 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_12MB;
147                 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_18MB;
148                 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_24MB;
149                 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_36MB;
150                 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_48MB;
151                 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_54MB;
152         }
153 }
154
155 void ieee80211softmac_start(struct net_device *dev)
156 {
157         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
158         struct ieee80211_device *ieee = mac->ieee;
159         u32 change = 0;
160         struct ieee80211softmac_txrates oldrates;
161
162         ieee80211softmac_start_check_rates(mac);
163
164         /* TODO: We need some kind of state machine to lower the default rates
165          *       if we loose too many packets.
166          */
167         /* Change the default txrate to the highest possible value.
168          * The txrate machine will lower it, if it is too high.
169          */
170         if (mac->txrates_change)
171                 oldrates = mac->txrates;
172         if (ieee->modulation & IEEE80211_OFDM_MODULATION) {
173                 mac->txrates.default_rate = IEEE80211_OFDM_RATE_54MB;
174                 change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
175                 mac->txrates.default_fallback = IEEE80211_OFDM_RATE_24MB;
176                 change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
177         } else if (ieee->modulation & IEEE80211_CCK_MODULATION) {
178                 mac->txrates.default_rate = IEEE80211_CCK_RATE_11MB;
179                 change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
180                 mac->txrates.default_fallback = IEEE80211_CCK_RATE_5MB;
181                 change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
182         } else
183                 assert(0);
184         if (mac->txrates_change)
185                 mac->txrates_change(dev, change, &oldrates);
186 }
187
188 void ieee80211softmac_stop(struct net_device *dev)
189 {
190         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
191
192         ieee80211softmac_clear_pending_work(mac);
193 }
194
195 void ieee80211softmac_set_rates(struct net_device *dev, u8 count, u8 *rates)
196 {
197         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
198         unsigned long flags;
199         
200         spin_lock_irqsave(&mac->lock, flags);
201         memcpy(mac->ratesinfo.rates, rates, count);
202         mac->ratesinfo.count = count;
203         spin_unlock_irqrestore(&mac->lock, flags);
204 }
205
206 static u8 raise_rate(struct ieee80211softmac_device *mac, u8 rate)
207 {
208         int i;
209         struct ieee80211softmac_ratesinfo *ri = &mac->ratesinfo;
210         
211         for (i=0; i<ri->count-1; i++) {
212                 if (ri->rates[i] == rate)
213                         return ri->rates[i+1];
214         }
215         /* I guess we can't go any higher... */
216         return ri->rates[ri->count];
217 }
218
219 u8 ieee80211softmac_lower_rate_delta(struct ieee80211softmac_device *mac, u8 rate, int delta)
220 {
221         int i;
222         struct ieee80211softmac_ratesinfo *ri = &mac->ratesinfo;
223         
224         for (i=delta; i<ri->count; i++) {
225                 if (ri->rates[i] == rate)
226                         return ri->rates[i-delta];
227         }
228         /* I guess we can't go any lower... */
229         return ri->rates[0];
230 }
231
232 static void ieee80211softmac_add_txrates_badness(struct ieee80211softmac_device *mac,
233                                                  int amount)
234 {
235         struct ieee80211softmac_txrates oldrates;
236         u8 default_rate = mac->txrates.default_rate;
237         u8 default_fallback = mac->txrates.default_fallback;
238         u32 changes = 0;
239
240         //TODO: This is highly experimental code.
241         //      Maybe the dynamic rate selection does not work
242         //      and it has to be removed again.
243
244 printk("badness %d\n", mac->txrate_badness);
245         mac->txrate_badness += amount;
246         if (mac->txrate_badness <= -1000) {
247                 /* Very small badness. Try a faster bitrate. */
248                 if (mac->txrates_change)
249                         memcpy(&oldrates, &mac->txrates, sizeof(oldrates));
250                 default_rate = raise_rate(mac, default_rate);
251                 changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
252                 default_fallback = get_fallback_rate(mac, default_rate);
253                 changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
254                 mac->txrate_badness = 0;
255 printk("Bitrate raised to %u\n", default_rate);
256         } else if (mac->txrate_badness >= 10000) {
257                 /* Very high badness. Try a slower bitrate. */
258                 if (mac->txrates_change)
259                         memcpy(&oldrates, &mac->txrates, sizeof(oldrates));
260                 default_rate = lower_rate(mac, default_rate);
261                 changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
262                 default_fallback = get_fallback_rate(mac, default_rate);
263                 changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
264                 mac->txrate_badness = 0;
265 printk("Bitrate lowered to %u\n", default_rate);
266         }
267
268         mac->txrates.default_rate = default_rate;
269         mac->txrates.default_fallback = default_fallback;
270
271         if (changes && mac->txrates_change)
272                 mac->txrates_change(mac->dev, changes, &oldrates);
273 }
274
275 void ieee80211softmac_fragment_lost(struct net_device *dev,
276                                     u16 wl_seq)
277 {
278         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
279         unsigned long flags;
280
281         spin_lock_irqsave(&mac->lock, flags);
282         ieee80211softmac_add_txrates_badness(mac, 1000);
283         //TODO
284
285         spin_unlock_irqrestore(&mac->lock, flags);
286 }
287
288 static int rate_cmp(const void *a_, const void *b_) {
289         u8 *a, *b;
290         a = (u8*)a_;
291         b = (u8*)b_;
292         return ((*a & ~IEEE80211_BASIC_RATE_MASK) - (*b & ~IEEE80211_BASIC_RATE_MASK));
293 }
294
295 /* Allocate a softmac network struct and fill it from a network */
296 struct ieee80211softmac_network *
297 ieee80211softmac_create_network(struct ieee80211softmac_device *mac,
298         struct ieee80211_network *net)
299 {
300         struct ieee80211softmac_network *softnet;
301         softnet = kzalloc(sizeof(struct ieee80211softmac_network), GFP_ATOMIC);
302         if(softnet == NULL)
303                 return NULL;
304         memcpy(softnet->bssid, net->bssid, ETH_ALEN);
305         softnet->channel = net->channel;
306         softnet->essid.len = net->ssid_len;
307         memcpy(softnet->essid.data, net->ssid, softnet->essid.len);
308         
309         /* copy rates over */
310         softnet->supported_rates.count = net->rates_len;
311         memcpy(&softnet->supported_rates.rates[0], net->rates, net->rates_len);
312         memcpy(&softnet->supported_rates.rates[softnet->supported_rates.count], net->rates_ex, net->rates_ex_len);
313         softnet->supported_rates.count += net->rates_ex_len;
314         sort(softnet->supported_rates.rates, softnet->supported_rates.count, sizeof(softnet->supported_rates.rates[0]), rate_cmp, NULL);
315         
316         softnet->capabilities = net->capability;
317         return softnet;
318 }
319
320
321 /* Add a network to the list, while locked */
322 void
323 ieee80211softmac_add_network_locked(struct ieee80211softmac_device *mac,
324         struct ieee80211softmac_network *add_net)
325 {
326         struct list_head *list_ptr;
327         struct ieee80211softmac_network *softmac_net = NULL;
328
329         list_for_each(list_ptr, &mac->network_list) {
330                 softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list);
331                 if(!memcmp(softmac_net->bssid, add_net->bssid, ETH_ALEN))
332                         break;
333                 else
334                         softmac_net = NULL;
335         }
336         if(softmac_net == NULL)
337                 list_add(&(add_net->list), &mac->network_list);
338 }
339
340 /* Add a network to the list, with locking */
341 void
342 ieee80211softmac_add_network(struct ieee80211softmac_device *mac,
343         struct ieee80211softmac_network *add_net)
344 {
345         unsigned long flags;
346         spin_lock_irqsave(&mac->lock, flags);
347         ieee80211softmac_add_network_locked(mac, add_net);
348         spin_unlock_irqrestore(&mac->lock, flags);
349 }
350
351
352 /* Delete a network from the list, while locked*/
353 void
354 ieee80211softmac_del_network_locked(struct ieee80211softmac_device *mac,
355         struct ieee80211softmac_network *del_net)
356 {
357         list_del(&(del_net->list));
358 }
359
360 /* Delete a network from the list with locking */
361 void
362 ieee80211softmac_del_network(struct ieee80211softmac_device *mac,
363         struct ieee80211softmac_network *del_net)
364 {
365         unsigned long flags;
366         spin_lock_irqsave(&mac->lock, flags);
367         ieee80211softmac_del_network_locked(mac, del_net);
368         spin_unlock_irqrestore(&mac->lock, flags);
369 }
370
371 /* Get a network from the list by MAC while locked */
372 struct ieee80211softmac_network *
373 ieee80211softmac_get_network_by_bssid_locked(struct ieee80211softmac_device *mac,
374         u8 *bssid)
375 {
376         struct list_head *list_ptr;
377         struct ieee80211softmac_network *softmac_net = NULL;
378         list_for_each(list_ptr, &mac->network_list) {
379                 softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list);
380                 if(!memcmp(softmac_net->bssid, bssid, ETH_ALEN))
381                         break;
382                 else
383                         softmac_net = NULL;
384         }
385         return softmac_net;
386 }
387
388 /* Get a network from the list by BSSID with locking */
389 struct ieee80211softmac_network *
390 ieee80211softmac_get_network_by_bssid(struct ieee80211softmac_device *mac,
391         u8 *bssid)
392 {
393         unsigned long flags;
394         struct ieee80211softmac_network *softmac_net;
395         
396         spin_lock_irqsave(&mac->lock, flags);
397         softmac_net = ieee80211softmac_get_network_by_bssid_locked(mac, bssid);
398         spin_unlock_irqrestore(&mac->lock, flags);
399         return softmac_net;
400 }
401
402 /* Get a network from the list by ESSID while locked */
403 struct ieee80211softmac_network *
404 ieee80211softmac_get_network_by_essid_locked(struct ieee80211softmac_device *mac,
405         struct ieee80211softmac_essid *essid)
406 {
407         struct list_head *list_ptr;
408         struct ieee80211softmac_network *softmac_net = NULL;
409
410         list_for_each(list_ptr, &mac->network_list) {
411                 softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list);
412                 if (softmac_net->essid.len == essid->len &&
413                         !memcmp(softmac_net->essid.data, essid->data, essid->len))
414                         return softmac_net;
415         }
416         return NULL;
417 }
418
419 /* Get a network from the list by ESSID with locking */
420 struct ieee80211softmac_network *
421 ieee80211softmac_get_network_by_essid(struct ieee80211softmac_device *mac,
422         struct ieee80211softmac_essid *essid)   
423 {
424         unsigned long flags;
425         struct ieee80211softmac_network *softmac_net = NULL;
426
427         spin_lock_irqsave(&mac->lock, flags);
428         softmac_net = ieee80211softmac_get_network_by_essid_locked(mac, essid); 
429         spin_unlock_irqrestore(&mac->lock, flags);
430         return softmac_net;
431 }
432
433 MODULE_LICENSE("GPL");
434
435 EXPORT_SYMBOL_GPL(alloc_ieee80211softmac);
436 EXPORT_SYMBOL_GPL(free_ieee80211softmac);
437 EXPORT_SYMBOL_GPL(ieee80211softmac_set_rates);
438 EXPORT_SYMBOL_GPL(ieee80211softmac_start);
439 EXPORT_SYMBOL_GPL(ieee80211softmac_stop);
440 EXPORT_SYMBOL_GPL(ieee80211softmac_fragment_lost);
441 EXPORT_SYMBOL_GPL(ieee80211softmac_clear_pending_work);