Merge branch 'upstream-fixes' of git://lost.foo-projects.org/~ahkok/git/netdev-2...
[pandora-kernel.git] / net / ieee80211 / softmac / ieee80211softmac_module.c
1 /*
2  * Contains some basic softmac functions along with module registration code etc.
3  *
4  * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
5  *                          Joseph Jezak <josejx@gentoo.org>
6  *                          Larry Finger <Larry.Finger@lwfinger.net>
7  *                          Danny van Dyk <kugelfang@gentoo.org>
8  *                          Michael Buesch <mbuesch@freenet.de>
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of version 2 of the GNU General Public License as
12  * published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
17  * more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
22  *
23  * The full GNU General Public License is included in this distribution in the
24  * file called COPYING.
25  */
26
27 #include "ieee80211softmac_priv.h"
28 #include <linux/sort.h>
29 #include <linux/etherdevice.h>
30
31 struct net_device *alloc_ieee80211softmac(int sizeof_priv)
32 {
33         struct ieee80211softmac_device *softmac;
34         struct net_device *dev;
35         
36         dev = alloc_ieee80211(sizeof(struct ieee80211softmac_device) + sizeof_priv);
37         softmac = ieee80211_priv(dev);
38         softmac->dev = dev;
39         softmac->ieee = netdev_priv(dev);
40         spin_lock_init(&softmac->lock);
41         
42         softmac->ieee->handle_auth = ieee80211softmac_auth_resp;
43         softmac->ieee->handle_deauth = ieee80211softmac_deauth_resp;
44         softmac->ieee->handle_assoc_response = ieee80211softmac_handle_assoc_response;
45         softmac->ieee->handle_reassoc_request = ieee80211softmac_handle_reassoc_req;
46         softmac->ieee->handle_disassoc = ieee80211softmac_handle_disassoc;
47         softmac->ieee->handle_beacon = ieee80211softmac_handle_beacon;
48         softmac->scaninfo = NULL;
49
50         softmac->associnfo.scan_retry = IEEE80211SOFTMAC_ASSOC_SCAN_RETRY_LIMIT;
51
52         /* TODO: initialise all the other callbacks in the ieee struct
53          *       (once they're written)
54          */
55
56         INIT_LIST_HEAD(&softmac->auth_queue);
57         INIT_LIST_HEAD(&softmac->network_list);
58         INIT_LIST_HEAD(&softmac->events);
59
60         INIT_WORK(&softmac->associnfo.work, ieee80211softmac_assoc_work, softmac);
61         INIT_WORK(&softmac->associnfo.timeout, ieee80211softmac_assoc_timeout, softmac);
62         softmac->start_scan = ieee80211softmac_start_scan_implementation;
63         softmac->wait_for_scan = ieee80211softmac_wait_for_scan_implementation;
64         softmac->stop_scan = ieee80211softmac_stop_scan_implementation;
65
66         /* to start with, we can't send anything ... */
67         netif_carrier_off(dev);
68         
69         return dev;
70 }
71 EXPORT_SYMBOL_GPL(alloc_ieee80211softmac);
72
73 /* Clears the pending work queue items, stops all scans, etc. */
74 void 
75 ieee80211softmac_clear_pending_work(struct ieee80211softmac_device *sm)
76 {
77         unsigned long flags;
78         struct ieee80211softmac_event *eventptr, *eventtmp;
79         struct ieee80211softmac_auth_queue_item *authptr, *authtmp;
80         struct ieee80211softmac_network *netptr, *nettmp;
81         
82         ieee80211softmac_stop_scan(sm);
83         ieee80211softmac_wait_for_scan(sm);
84         
85         spin_lock_irqsave(&sm->lock, flags);
86         sm->running = 0;
87
88         /* Free all pending assoc work items */
89         cancel_delayed_work(&sm->associnfo.work);
90         
91         /* Free all pending scan work items */
92         if(sm->scaninfo != NULL)
93                 cancel_delayed_work(&sm->scaninfo->softmac_scan);       
94         
95         /* Free all pending auth work items */
96         list_for_each_entry(authptr, &sm->auth_queue, list)
97                 cancel_delayed_work(&authptr->work);
98         
99         /* delete all pending event calls and work items */
100         list_for_each_entry_safe(eventptr, eventtmp, &sm->events, list)
101                 cancel_delayed_work(&eventptr->work);
102
103         spin_unlock_irqrestore(&sm->lock, flags);
104         flush_scheduled_work();
105
106         /* now we should be save and no longer need locking... */
107         spin_lock_irqsave(&sm->lock, flags);
108         /* Free all pending auth work items */
109         list_for_each_entry_safe(authptr, authtmp, &sm->auth_queue, list) {
110                 list_del(&authptr->list);
111                 kfree(authptr);
112         }
113         
114         /* delete all pending event calls and work items */
115         list_for_each_entry_safe(eventptr, eventtmp, &sm->events, list) {
116                 list_del(&eventptr->list);
117                 kfree(eventptr);
118         }
119                 
120         /* Free all networks */
121         list_for_each_entry_safe(netptr, nettmp, &sm->network_list, list) {
122                 ieee80211softmac_del_network_locked(sm, netptr);
123                 if(netptr->challenge != NULL)
124                         kfree(netptr->challenge);
125                 kfree(netptr);
126         }
127
128         spin_unlock_irqrestore(&sm->lock, flags);
129 }
130 EXPORT_SYMBOL_GPL(ieee80211softmac_clear_pending_work);
131
132 void free_ieee80211softmac(struct net_device *dev)
133 {
134         struct ieee80211softmac_device *sm = ieee80211_priv(dev);
135         ieee80211softmac_clear_pending_work(sm);        
136         kfree(sm->scaninfo);
137         kfree(sm->wpa.IE);
138         free_ieee80211(dev);
139 }
140 EXPORT_SYMBOL_GPL(free_ieee80211softmac);
141
142 static void ieee80211softmac_start_check_rates(struct ieee80211softmac_device *mac)
143 {
144         struct ieee80211softmac_ratesinfo *ri = &mac->ratesinfo;
145         /* I took out the sorting check, we're seperating by modulation now. */
146         if (ri->count)
147                 return;
148         /* otherwise assume we hav'em all! */
149         if (mac->ieee->modulation & IEEE80211_CCK_MODULATION) {
150                 ri->rates[ri->count++] = IEEE80211_CCK_RATE_1MB;
151                 ri->rates[ri->count++] = IEEE80211_CCK_RATE_2MB;
152                 ri->rates[ri->count++] = IEEE80211_CCK_RATE_5MB;
153                 ri->rates[ri->count++] = IEEE80211_CCK_RATE_11MB;
154         }
155         if (mac->ieee->modulation & IEEE80211_OFDM_MODULATION) {
156                 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_6MB;
157                 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_9MB;
158                 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_12MB;
159                 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_18MB;
160                 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_24MB;
161                 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_36MB;
162                 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_48MB;
163                 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_54MB;
164         }
165 }
166
167 int ieee80211softmac_ratesinfo_rate_supported(struct ieee80211softmac_ratesinfo *ri, u8 rate)
168 {
169         int search;
170         u8 search_rate;
171
172         for (search = 0; search < ri->count; search++) {
173                 search_rate = ri->rates[search];
174                 search_rate &= ~IEEE80211_BASIC_RATE_MASK;
175                 if (rate == search_rate)
176                         return 1;
177         }
178
179         return 0;
180 }
181
182 u8 ieee80211softmac_highest_supported_rate(struct ieee80211softmac_device *mac,
183         struct ieee80211softmac_ratesinfo *ri, int basic_only)
184 {
185         u8 user_rate = mac->txrates.user_rate;
186         int i;
187
188         if (ri->count == 0)
189                 return IEEE80211_CCK_RATE_1MB;
190
191         for (i = ri->count - 1; i >= 0; i--) {
192                 u8 rate = ri->rates[i];
193                 if (basic_only && !(rate & IEEE80211_BASIC_RATE_MASK))
194                         continue;
195                 rate &= ~IEEE80211_BASIC_RATE_MASK;
196                 if (rate > user_rate)
197                         continue;
198                 if (ieee80211softmac_ratesinfo_rate_supported(&mac->ratesinfo, rate))
199                         return rate;
200         }
201
202         /* If we haven't found a suitable rate by now, just trust the user */
203         return user_rate;
204 }
205 EXPORT_SYMBOL_GPL(ieee80211softmac_highest_supported_rate);
206
207 void ieee80211softmac_process_erp(struct ieee80211softmac_device *mac,
208         u8 erp_value)
209 {
210         int use_protection;
211         int short_preamble;
212         u32 changes = 0;
213
214         /* Barker preamble mode */
215         short_preamble = ((erp_value & WLAN_ERP_BARKER_PREAMBLE) == 0
216                           && mac->associnfo.short_preamble_available) ? 1 : 0;
217
218         /* Protection needed? */
219         use_protection = (erp_value & WLAN_ERP_USE_PROTECTION) != 0;
220
221         if (mac->bssinfo.short_preamble != short_preamble) {
222                 changes |= IEEE80211SOFTMAC_BSSINFOCHG_SHORT_PREAMBLE;
223                 mac->bssinfo.short_preamble = short_preamble;
224         }
225
226         if (mac->bssinfo.use_protection != use_protection) {
227                 changes |= IEEE80211SOFTMAC_BSSINFOCHG_PROTECTION;
228                 mac->bssinfo.use_protection = use_protection;
229         }
230
231         if (mac->bssinfo_change && changes)
232                 mac->bssinfo_change(mac->dev, changes);
233 }
234
235 void ieee80211softmac_recalc_txrates(struct ieee80211softmac_device *mac)
236 {
237         struct ieee80211softmac_txrates *txrates = &mac->txrates;
238         u32 change = 0;
239
240         change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
241         txrates->default_rate = ieee80211softmac_highest_supported_rate(mac, &mac->bssinfo.supported_rates, 0);
242
243         change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
244         txrates->default_fallback = lower_rate(mac, txrates->default_rate);
245
246         change |= IEEE80211SOFTMAC_TXRATECHG_MCAST;
247         txrates->mcast_rate = ieee80211softmac_highest_supported_rate(mac, &mac->bssinfo.supported_rates, 1);
248
249         if (mac->txrates_change)
250                 mac->txrates_change(mac->dev, change);
251
252 }
253
254 void ieee80211softmac_init_bss(struct ieee80211softmac_device *mac)
255 {
256         struct ieee80211_device *ieee = mac->ieee;
257         u32 change = 0;
258         struct ieee80211softmac_txrates *txrates = &mac->txrates;
259         struct ieee80211softmac_bss_info *bssinfo = &mac->bssinfo;
260
261         /* TODO: We need some kind of state machine to lower the default rates
262          *       if we loose too many packets.
263          */
264         /* Change the default txrate to the highest possible value.
265          * The txrate machine will lower it, if it is too high.
266          */
267         /* FIXME: We don't correctly handle backing down to lower
268            rates, so 801.11g devices start off at 11M for now. People
269            can manually change it if they really need to, but 11M is
270            more reliable. Note similar logic in
271            ieee80211softmac_wx_set_rate() */     
272         if (ieee->modulation & IEEE80211_CCK_MODULATION) {
273                 txrates->user_rate = IEEE80211_CCK_RATE_11MB;
274         } else if (ieee->modulation & IEEE80211_OFDM_MODULATION) {
275                 txrates->user_rate = IEEE80211_OFDM_RATE_54MB;
276         } else
277                 assert(0);
278
279         txrates->default_rate = IEEE80211_CCK_RATE_1MB;
280         change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
281
282         txrates->default_fallback = IEEE80211_CCK_RATE_1MB;
283         change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
284
285         txrates->mcast_rate = IEEE80211_CCK_RATE_1MB;
286         change |= IEEE80211SOFTMAC_TXRATECHG_MCAST;
287
288         txrates->mgt_mcast_rate = IEEE80211_CCK_RATE_1MB;
289         change |= IEEE80211SOFTMAC_TXRATECHG_MGT_MCAST;
290
291         if (mac->txrates_change)
292                 mac->txrates_change(mac->dev, change);
293
294         change = 0;
295
296         bssinfo->supported_rates.count = 0;
297         memset(bssinfo->supported_rates.rates, 0,
298                 sizeof(bssinfo->supported_rates.rates));
299         change |= IEEE80211SOFTMAC_BSSINFOCHG_RATES;
300
301         bssinfo->short_preamble = 0;
302         change |= IEEE80211SOFTMAC_BSSINFOCHG_SHORT_PREAMBLE;
303
304         bssinfo->use_protection = 0;
305         change |= IEEE80211SOFTMAC_BSSINFOCHG_PROTECTION;
306
307         if (mac->bssinfo_change)
308                 mac->bssinfo_change(mac->dev, change);
309
310         mac->running = 1;
311 }
312
313 void ieee80211softmac_start(struct net_device *dev)
314 {
315         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
316
317         ieee80211softmac_start_check_rates(mac);
318         ieee80211softmac_init_bss(mac);
319 }
320 EXPORT_SYMBOL_GPL(ieee80211softmac_start);
321
322 void ieee80211softmac_stop(struct net_device *dev)
323 {
324         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
325
326         ieee80211softmac_clear_pending_work(mac);
327 }
328 EXPORT_SYMBOL_GPL(ieee80211softmac_stop);
329
330 void ieee80211softmac_set_rates(struct net_device *dev, u8 count, u8 *rates)
331 {
332         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
333         unsigned long flags;
334         
335         spin_lock_irqsave(&mac->lock, flags);
336         memcpy(mac->ratesinfo.rates, rates, count);
337         mac->ratesinfo.count = count;
338         spin_unlock_irqrestore(&mac->lock, flags);
339 }
340 EXPORT_SYMBOL_GPL(ieee80211softmac_set_rates);
341
342 static u8 raise_rate(struct ieee80211softmac_device *mac, u8 rate)
343 {
344         int i;
345         struct ieee80211softmac_ratesinfo *ri = &mac->ratesinfo;
346         
347         for (i=0; i<ri->count-1; i++) {
348                 if (ri->rates[i] == rate)
349                         return ri->rates[i+1];
350         }
351         /* I guess we can't go any higher... */
352         return ri->rates[ri->count];
353 }
354
355 u8 ieee80211softmac_lower_rate_delta(struct ieee80211softmac_device *mac, u8 rate, int delta)
356 {
357         int i;
358         struct ieee80211softmac_ratesinfo *ri = &mac->ratesinfo;
359         
360         for (i=delta; i<ri->count; i++) {
361                 if (ri->rates[i] == rate)
362                         return ri->rates[i-delta];
363         }
364         /* I guess we can't go any lower... */
365         return ri->rates[0];
366 }
367
368 static void ieee80211softmac_add_txrates_badness(struct ieee80211softmac_device *mac,
369                                                  int amount)
370 {
371         u8 default_rate = mac->txrates.default_rate;
372         u8 default_fallback = mac->txrates.default_fallback;
373         u32 changes = 0;
374
375         //TODO: This is highly experimental code.
376         //      Maybe the dynamic rate selection does not work
377         //      and it has to be removed again.
378
379 printk("badness %d\n", mac->txrate_badness);
380         mac->txrate_badness += amount;
381         if (mac->txrate_badness <= -1000) {
382                 /* Very small badness. Try a faster bitrate. */
383                 default_rate = raise_rate(mac, default_rate);
384                 changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
385                 default_fallback = get_fallback_rate(mac, default_rate);
386                 changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
387                 mac->txrate_badness = 0;
388 printk("Bitrate raised to %u\n", default_rate);
389         } else if (mac->txrate_badness >= 10000) {
390                 /* Very high badness. Try a slower bitrate. */
391                 default_rate = lower_rate(mac, default_rate);
392                 changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
393                 default_fallback = get_fallback_rate(mac, default_rate);
394                 changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
395                 mac->txrate_badness = 0;
396 printk("Bitrate lowered to %u\n", default_rate);
397         }
398
399         mac->txrates.default_rate = default_rate;
400         mac->txrates.default_fallback = default_fallback;
401
402         if (changes && mac->txrates_change)
403                 mac->txrates_change(mac->dev, changes);
404 }
405
406 void ieee80211softmac_fragment_lost(struct net_device *dev,
407                                     u16 wl_seq)
408 {
409         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
410         unsigned long flags;
411
412         spin_lock_irqsave(&mac->lock, flags);
413         ieee80211softmac_add_txrates_badness(mac, 1000);
414         //TODO
415
416         spin_unlock_irqrestore(&mac->lock, flags);
417 }
418 EXPORT_SYMBOL_GPL(ieee80211softmac_fragment_lost);
419
420 static int rate_cmp(const void *a_, const void *b_) {
421         u8 *a, *b;
422         a = (u8*)a_;
423         b = (u8*)b_;
424         return ((*a & ~IEEE80211_BASIC_RATE_MASK) - (*b & ~IEEE80211_BASIC_RATE_MASK));
425 }
426
427 /* Allocate a softmac network struct and fill it from a network */
428 struct ieee80211softmac_network *
429 ieee80211softmac_create_network(struct ieee80211softmac_device *mac,
430         struct ieee80211_network *net)
431 {
432         struct ieee80211softmac_network *softnet;
433         softnet = kzalloc(sizeof(struct ieee80211softmac_network), GFP_ATOMIC);
434         if(softnet == NULL)
435                 return NULL;
436         memcpy(softnet->bssid, net->bssid, ETH_ALEN);
437         softnet->channel = net->channel;
438         softnet->essid.len = net->ssid_len;
439         memcpy(softnet->essid.data, net->ssid, softnet->essid.len);
440         
441         /* copy rates over */
442         softnet->supported_rates.count = net->rates_len;
443         memcpy(&softnet->supported_rates.rates[0], net->rates, net->rates_len);
444         memcpy(&softnet->supported_rates.rates[softnet->supported_rates.count], net->rates_ex, net->rates_ex_len);
445         softnet->supported_rates.count += net->rates_ex_len;
446         sort(softnet->supported_rates.rates, softnet->supported_rates.count, sizeof(softnet->supported_rates.rates[0]), rate_cmp, NULL);
447
448         /* we save the ERP value because it is needed at association time, and
449          * many AP's do not include an ERP IE in the association response. */
450         softnet->erp_value = net->erp_value;
451
452         softnet->capabilities = net->capability;
453         return softnet;
454 }
455
456
457 /* Add a network to the list, while locked */
458 void
459 ieee80211softmac_add_network_locked(struct ieee80211softmac_device *mac,
460         struct ieee80211softmac_network *add_net)
461 {
462         struct list_head *list_ptr;
463         struct ieee80211softmac_network *softmac_net = NULL;
464
465         list_for_each(list_ptr, &mac->network_list) {
466                 softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list);
467                 if(!memcmp(softmac_net->bssid, add_net->bssid, ETH_ALEN))
468                         break;
469                 else
470                         softmac_net = NULL;
471         }
472         if(softmac_net == NULL)
473                 list_add(&(add_net->list), &mac->network_list);
474 }
475
476 /* Add a network to the list, with locking */
477 void
478 ieee80211softmac_add_network(struct ieee80211softmac_device *mac,
479         struct ieee80211softmac_network *add_net)
480 {
481         unsigned long flags;
482         spin_lock_irqsave(&mac->lock, flags);
483         ieee80211softmac_add_network_locked(mac, add_net);
484         spin_unlock_irqrestore(&mac->lock, flags);
485 }
486
487
488 /* Delete a network from the list, while locked*/
489 void
490 ieee80211softmac_del_network_locked(struct ieee80211softmac_device *mac,
491         struct ieee80211softmac_network *del_net)
492 {
493         list_del(&(del_net->list));
494 }
495
496 /* Delete a network from the list with locking */
497 void
498 ieee80211softmac_del_network(struct ieee80211softmac_device *mac,
499         struct ieee80211softmac_network *del_net)
500 {
501         unsigned long flags;
502         spin_lock_irqsave(&mac->lock, flags);
503         ieee80211softmac_del_network_locked(mac, del_net);
504         spin_unlock_irqrestore(&mac->lock, flags);
505 }
506
507 /* Get a network from the list by MAC while locked */
508 struct ieee80211softmac_network *
509 ieee80211softmac_get_network_by_bssid_locked(struct ieee80211softmac_device *mac,
510         u8 *bssid)
511 {
512         struct list_head *list_ptr;
513         struct ieee80211softmac_network *softmac_net = NULL;
514         list_for_each(list_ptr, &mac->network_list) {
515                 softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list);
516                 if(!memcmp(softmac_net->bssid, bssid, ETH_ALEN))
517                         break;
518                 else
519                         softmac_net = NULL;
520         }
521         return softmac_net;
522 }
523
524 /* Get a network from the list by BSSID with locking */
525 struct ieee80211softmac_network *
526 ieee80211softmac_get_network_by_bssid(struct ieee80211softmac_device *mac,
527         u8 *bssid)
528 {
529         unsigned long flags;
530         struct ieee80211softmac_network *softmac_net;
531         
532         spin_lock_irqsave(&mac->lock, flags);
533         softmac_net = ieee80211softmac_get_network_by_bssid_locked(mac, bssid);
534         spin_unlock_irqrestore(&mac->lock, flags);
535         return softmac_net;
536 }
537
538 /* Get a network from the list by ESSID while locked */
539 struct ieee80211softmac_network *
540 ieee80211softmac_get_network_by_essid_locked(struct ieee80211softmac_device *mac,
541         struct ieee80211softmac_essid *essid)
542 {
543         struct list_head *list_ptr;
544         struct ieee80211softmac_network *softmac_net = NULL;
545
546         list_for_each(list_ptr, &mac->network_list) {
547                 softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list);
548                 if (softmac_net->essid.len == essid->len &&
549                         !memcmp(softmac_net->essid.data, essid->data, essid->len))
550                         return softmac_net;
551         }
552         return NULL;
553 }
554
555 /* Get a network from the list by ESSID with locking */
556 struct ieee80211softmac_network *
557 ieee80211softmac_get_network_by_essid(struct ieee80211softmac_device *mac,
558         struct ieee80211softmac_essid *essid)   
559 {
560         unsigned long flags;
561         struct ieee80211softmac_network *softmac_net = NULL;
562
563         spin_lock_irqsave(&mac->lock, flags);
564         softmac_net = ieee80211softmac_get_network_by_essid_locked(mac, essid); 
565         spin_unlock_irqrestore(&mac->lock, flags);
566         return softmac_net;
567 }
568
569 MODULE_LICENSE("GPL");
570 MODULE_AUTHOR("Johannes Berg");
571 MODULE_AUTHOR("Joseph Jezak");
572 MODULE_AUTHOR("Larry Finger");
573 MODULE_AUTHOR("Danny van Dyk");
574 MODULE_AUTHOR("Michael Buesch");
575 MODULE_DESCRIPTION("802.11 software MAC");