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