2 * Contains some basic softmac functions along with module registration code etc.
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>
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.
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
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
23 * The full GNU General Public License is included in this distribution in the
24 * file called COPYING.
27 #include "ieee80211softmac_priv.h"
28 #include <linux/sort.h>
30 struct net_device *alloc_ieee80211softmac(int sizeof_priv)
32 struct ieee80211softmac_device *softmac;
33 struct net_device *dev;
35 dev = alloc_ieee80211(sizeof(struct ieee80211softmac_device) + sizeof_priv);
36 softmac = ieee80211_priv(dev);
38 softmac->ieee = netdev_priv(dev);
39 spin_lock_init(&softmac->lock);
41 softmac->ieee->handle_auth = ieee80211softmac_auth_resp;
42 softmac->ieee->handle_deauth = ieee80211softmac_deauth_resp;
43 softmac->ieee->handle_assoc_response = ieee80211softmac_handle_assoc_response;
44 softmac->ieee->handle_reassoc_request = ieee80211softmac_handle_reassoc_req;
45 softmac->ieee->handle_disassoc = ieee80211softmac_handle_disassoc;
46 softmac->scaninfo = NULL;
48 /* TODO: initialise all the other callbacks in the ieee struct
49 * (once they're written)
52 INIT_LIST_HEAD(&softmac->auth_queue);
53 INIT_LIST_HEAD(&softmac->network_list);
54 INIT_LIST_HEAD(&softmac->events);
56 INIT_WORK(&softmac->associnfo.work, ieee80211softmac_assoc_work, softmac);
57 INIT_WORK(&softmac->associnfo.timeout, ieee80211softmac_assoc_timeout, softmac);
58 softmac->start_scan = ieee80211softmac_start_scan_implementation;
59 softmac->wait_for_scan = ieee80211softmac_wait_for_scan_implementation;
60 softmac->stop_scan = ieee80211softmac_stop_scan_implementation;
62 //TODO: The mcast rate has to be assigned dynamically somewhere (in scanning, association. Not sure...)
63 // It has to be set to the highest rate all stations in the current network can handle.
64 softmac->txrates.mcast_rate = IEEE80211_CCK_RATE_1MB;
65 softmac->txrates.mcast_fallback = IEEE80211_CCK_RATE_1MB;
66 /* This is reassigned in ieee80211softmac_start to sane values. */
67 softmac->txrates.default_rate = IEEE80211_CCK_RATE_1MB;
68 softmac->txrates.default_fallback = IEEE80211_CCK_RATE_1MB;
70 /* to start with, we can't send anything ... */
71 netif_carrier_off(dev);
75 EXPORT_SYMBOL_GPL(alloc_ieee80211softmac);
77 /* Clears the pending work queue items, stops all scans, etc. */
79 ieee80211softmac_clear_pending_work(struct ieee80211softmac_device *sm)
82 struct ieee80211softmac_event *eventptr, *eventtmp;
83 struct ieee80211softmac_auth_queue_item *authptr, *authtmp;
84 struct ieee80211softmac_network *netptr, *nettmp;
86 ieee80211softmac_stop_scan(sm);
87 ieee80211softmac_wait_for_scan(sm);
89 spin_lock_irqsave(&sm->lock, flags);
90 /* Free all pending assoc work items */
91 cancel_delayed_work(&sm->associnfo.work);
93 /* Free all pending scan work items */
94 if(sm->scaninfo != NULL)
95 cancel_delayed_work(&sm->scaninfo->softmac_scan);
97 /* Free all pending auth work items */
98 list_for_each_entry(authptr, &sm->auth_queue, list)
99 cancel_delayed_work(&authptr->work);
101 /* delete all pending event calls and work items */
102 list_for_each_entry_safe(eventptr, eventtmp, &sm->events, list)
103 cancel_delayed_work(&eventptr->work);
105 spin_unlock_irqrestore(&sm->lock, flags);
106 flush_scheduled_work();
108 /* now we should be save and no longer need locking... */
109 spin_lock_irqsave(&sm->lock, flags);
110 /* Free all pending auth work items */
111 list_for_each_entry_safe(authptr, authtmp, &sm->auth_queue, list) {
112 list_del(&authptr->list);
116 /* delete all pending event calls and work items */
117 list_for_each_entry_safe(eventptr, eventtmp, &sm->events, list) {
118 list_del(&eventptr->list);
122 /* Free all networks */
123 list_for_each_entry_safe(netptr, nettmp, &sm->network_list, list) {
124 ieee80211softmac_del_network_locked(sm, netptr);
125 if(netptr->challenge != NULL)
126 kfree(netptr->challenge);
130 spin_unlock_irqrestore(&sm->lock, flags);
132 EXPORT_SYMBOL_GPL(ieee80211softmac_clear_pending_work);
134 void free_ieee80211softmac(struct net_device *dev)
136 struct ieee80211softmac_device *sm = ieee80211_priv(dev);
137 ieee80211softmac_clear_pending_work(sm);
142 EXPORT_SYMBOL_GPL(free_ieee80211softmac);
144 static void ieee80211softmac_start_check_rates(struct ieee80211softmac_device *mac)
146 struct ieee80211softmac_ratesinfo *ri = &mac->ratesinfo;
147 /* I took out the sorting check, we're seperating by modulation now. */
150 /* otherwise assume we hav'em all! */
151 if (mac->ieee->modulation & IEEE80211_CCK_MODULATION) {
152 ri->rates[ri->count++] = IEEE80211_CCK_RATE_1MB;
153 ri->rates[ri->count++] = IEEE80211_CCK_RATE_2MB;
154 ri->rates[ri->count++] = IEEE80211_CCK_RATE_5MB;
155 ri->rates[ri->count++] = IEEE80211_CCK_RATE_11MB;
157 if (mac->ieee->modulation & IEEE80211_OFDM_MODULATION) {
158 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_6MB;
159 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_9MB;
160 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_12MB;
161 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_18MB;
162 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_24MB;
163 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_36MB;
164 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_48MB;
165 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_54MB;
169 void ieee80211softmac_start(struct net_device *dev)
171 struct ieee80211softmac_device *mac = ieee80211_priv(dev);
172 struct ieee80211_device *ieee = mac->ieee;
174 struct ieee80211softmac_txrates oldrates;
176 ieee80211softmac_start_check_rates(mac);
178 /* TODO: We need some kind of state machine to lower the default rates
179 * if we loose too many packets.
181 /* Change the default txrate to the highest possible value.
182 * The txrate machine will lower it, if it is too high.
184 if (mac->txrates_change)
185 oldrates = mac->txrates;
186 /* FIXME: We don't correctly handle backing down to lower
187 rates, so 801.11g devices start off at 11M for now. People
188 can manually change it if they really need to, but 11M is
189 more reliable. Note similar logic in
190 ieee80211softmac_wx_set_rate() */
191 if (ieee->modulation & IEEE80211_CCK_MODULATION) {
192 mac->txrates.default_rate = IEEE80211_CCK_RATE_11MB;
193 change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
194 mac->txrates.default_fallback = IEEE80211_CCK_RATE_5MB;
195 change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
196 } else if (ieee->modulation & IEEE80211_OFDM_MODULATION) {
197 mac->txrates.default_rate = IEEE80211_OFDM_RATE_54MB;
198 change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
199 mac->txrates.default_fallback = IEEE80211_OFDM_RATE_24MB;
200 change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
203 if (mac->txrates_change)
204 mac->txrates_change(dev, change, &oldrates);
206 EXPORT_SYMBOL_GPL(ieee80211softmac_start);
208 void ieee80211softmac_stop(struct net_device *dev)
210 struct ieee80211softmac_device *mac = ieee80211_priv(dev);
212 ieee80211softmac_clear_pending_work(mac);
214 EXPORT_SYMBOL_GPL(ieee80211softmac_stop);
216 void ieee80211softmac_set_rates(struct net_device *dev, u8 count, u8 *rates)
218 struct ieee80211softmac_device *mac = ieee80211_priv(dev);
221 spin_lock_irqsave(&mac->lock, flags);
222 memcpy(mac->ratesinfo.rates, rates, count);
223 mac->ratesinfo.count = count;
224 spin_unlock_irqrestore(&mac->lock, flags);
226 EXPORT_SYMBOL_GPL(ieee80211softmac_set_rates);
228 static u8 raise_rate(struct ieee80211softmac_device *mac, u8 rate)
231 struct ieee80211softmac_ratesinfo *ri = &mac->ratesinfo;
233 for (i=0; i<ri->count-1; i++) {
234 if (ri->rates[i] == rate)
235 return ri->rates[i+1];
237 /* I guess we can't go any higher... */
238 return ri->rates[ri->count];
241 u8 ieee80211softmac_lower_rate_delta(struct ieee80211softmac_device *mac, u8 rate, int delta)
244 struct ieee80211softmac_ratesinfo *ri = &mac->ratesinfo;
246 for (i=delta; i<ri->count; i++) {
247 if (ri->rates[i] == rate)
248 return ri->rates[i-delta];
250 /* I guess we can't go any lower... */
254 static void ieee80211softmac_add_txrates_badness(struct ieee80211softmac_device *mac,
257 struct ieee80211softmac_txrates oldrates;
258 u8 default_rate = mac->txrates.default_rate;
259 u8 default_fallback = mac->txrates.default_fallback;
262 //TODO: This is highly experimental code.
263 // Maybe the dynamic rate selection does not work
264 // and it has to be removed again.
266 printk("badness %d\n", mac->txrate_badness);
267 mac->txrate_badness += amount;
268 if (mac->txrate_badness <= -1000) {
269 /* Very small badness. Try a faster bitrate. */
270 if (mac->txrates_change)
271 memcpy(&oldrates, &mac->txrates, sizeof(oldrates));
272 default_rate = raise_rate(mac, default_rate);
273 changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
274 default_fallback = get_fallback_rate(mac, default_rate);
275 changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
276 mac->txrate_badness = 0;
277 printk("Bitrate raised to %u\n", default_rate);
278 } else if (mac->txrate_badness >= 10000) {
279 /* Very high badness. Try a slower bitrate. */
280 if (mac->txrates_change)
281 memcpy(&oldrates, &mac->txrates, sizeof(oldrates));
282 default_rate = lower_rate(mac, default_rate);
283 changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
284 default_fallback = get_fallback_rate(mac, default_rate);
285 changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
286 mac->txrate_badness = 0;
287 printk("Bitrate lowered to %u\n", default_rate);
290 mac->txrates.default_rate = default_rate;
291 mac->txrates.default_fallback = default_fallback;
293 if (changes && mac->txrates_change)
294 mac->txrates_change(mac->dev, changes, &oldrates);
297 void ieee80211softmac_fragment_lost(struct net_device *dev,
300 struct ieee80211softmac_device *mac = ieee80211_priv(dev);
303 spin_lock_irqsave(&mac->lock, flags);
304 ieee80211softmac_add_txrates_badness(mac, 1000);
307 spin_unlock_irqrestore(&mac->lock, flags);
309 EXPORT_SYMBOL_GPL(ieee80211softmac_fragment_lost);
311 static int rate_cmp(const void *a_, const void *b_) {
315 return ((*a & ~IEEE80211_BASIC_RATE_MASK) - (*b & ~IEEE80211_BASIC_RATE_MASK));
318 /* Allocate a softmac network struct and fill it from a network */
319 struct ieee80211softmac_network *
320 ieee80211softmac_create_network(struct ieee80211softmac_device *mac,
321 struct ieee80211_network *net)
323 struct ieee80211softmac_network *softnet;
324 softnet = kzalloc(sizeof(struct ieee80211softmac_network), GFP_ATOMIC);
327 memcpy(softnet->bssid, net->bssid, ETH_ALEN);
328 softnet->channel = net->channel;
329 softnet->essid.len = net->ssid_len;
330 memcpy(softnet->essid.data, net->ssid, softnet->essid.len);
332 /* copy rates over */
333 softnet->supported_rates.count = net->rates_len;
334 memcpy(&softnet->supported_rates.rates[0], net->rates, net->rates_len);
335 memcpy(&softnet->supported_rates.rates[softnet->supported_rates.count], net->rates_ex, net->rates_ex_len);
336 softnet->supported_rates.count += net->rates_ex_len;
337 sort(softnet->supported_rates.rates, softnet->supported_rates.count, sizeof(softnet->supported_rates.rates[0]), rate_cmp, NULL);
339 softnet->capabilities = net->capability;
344 /* Add a network to the list, while locked */
346 ieee80211softmac_add_network_locked(struct ieee80211softmac_device *mac,
347 struct ieee80211softmac_network *add_net)
349 struct list_head *list_ptr;
350 struct ieee80211softmac_network *softmac_net = NULL;
352 list_for_each(list_ptr, &mac->network_list) {
353 softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list);
354 if(!memcmp(softmac_net->bssid, add_net->bssid, ETH_ALEN))
359 if(softmac_net == NULL)
360 list_add(&(add_net->list), &mac->network_list);
363 /* Add a network to the list, with locking */
365 ieee80211softmac_add_network(struct ieee80211softmac_device *mac,
366 struct ieee80211softmac_network *add_net)
369 spin_lock_irqsave(&mac->lock, flags);
370 ieee80211softmac_add_network_locked(mac, add_net);
371 spin_unlock_irqrestore(&mac->lock, flags);
375 /* Delete a network from the list, while locked*/
377 ieee80211softmac_del_network_locked(struct ieee80211softmac_device *mac,
378 struct ieee80211softmac_network *del_net)
380 list_del(&(del_net->list));
383 /* Delete a network from the list with locking */
385 ieee80211softmac_del_network(struct ieee80211softmac_device *mac,
386 struct ieee80211softmac_network *del_net)
389 spin_lock_irqsave(&mac->lock, flags);
390 ieee80211softmac_del_network_locked(mac, del_net);
391 spin_unlock_irqrestore(&mac->lock, flags);
394 /* Get a network from the list by MAC while locked */
395 struct ieee80211softmac_network *
396 ieee80211softmac_get_network_by_bssid_locked(struct ieee80211softmac_device *mac,
399 struct list_head *list_ptr;
400 struct ieee80211softmac_network *softmac_net = NULL;
401 list_for_each(list_ptr, &mac->network_list) {
402 softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list);
403 if(!memcmp(softmac_net->bssid, bssid, ETH_ALEN))
411 /* Get a network from the list by BSSID with locking */
412 struct ieee80211softmac_network *
413 ieee80211softmac_get_network_by_bssid(struct ieee80211softmac_device *mac,
417 struct ieee80211softmac_network *softmac_net;
419 spin_lock_irqsave(&mac->lock, flags);
420 softmac_net = ieee80211softmac_get_network_by_bssid_locked(mac, bssid);
421 spin_unlock_irqrestore(&mac->lock, flags);
425 /* Get a network from the list by ESSID while locked */
426 struct ieee80211softmac_network *
427 ieee80211softmac_get_network_by_essid_locked(struct ieee80211softmac_device *mac,
428 struct ieee80211softmac_essid *essid)
430 struct list_head *list_ptr;
431 struct ieee80211softmac_network *softmac_net = NULL;
433 list_for_each(list_ptr, &mac->network_list) {
434 softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list);
435 if (softmac_net->essid.len == essid->len &&
436 !memcmp(softmac_net->essid.data, essid->data, essid->len))
442 /* Get a network from the list by ESSID with locking */
443 struct ieee80211softmac_network *
444 ieee80211softmac_get_network_by_essid(struct ieee80211softmac_device *mac,
445 struct ieee80211softmac_essid *essid)
448 struct ieee80211softmac_network *softmac_net = NULL;
450 spin_lock_irqsave(&mac->lock, flags);
451 softmac_net = ieee80211softmac_get_network_by_essid_locked(mac, essid);
452 spin_unlock_irqrestore(&mac->lock, flags);
456 MODULE_LICENSE("GPL");
457 MODULE_AUTHOR("Johannes Berg");
458 MODULE_AUTHOR("Joseph Jezak");
459 MODULE_AUTHOR("Larry Finger");
460 MODULE_AUTHOR("Danny van Dyk");
461 MODULE_AUTHOR("Michael Buesch");
462 MODULE_DESCRIPTION("802.11 software MAC");