blob: be83bdc1644a54e35b64a0ad21a5e1e23208f71a [file] [log] [blame]
Johannes Berg4855d252006-01-12 21:12:59 +01001/*
2 * Contains some basic softmac functions along with module registration code etc.
3 *
Johannes Berg79859052006-01-31 19:31:41 +01004 * 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>
Johannes Berg4855d252006-01-12 21:12:59 +01009 *
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
Johannes Berg370121e2006-01-04 16:32:16 +010027#include "ieee80211softmac_priv.h"
28#include <linux/sort.h>
29
30struct net_device *alloc_ieee80211softmac(int sizeof_priv)
31{
32 struct ieee80211softmac_device *softmac;
33 struct net_device *dev;
34
35 dev = alloc_ieee80211(sizeof(struct ieee80211softmac_device) + sizeof_priv);
36 softmac = ieee80211_priv(dev);
37 softmac->dev = dev;
38 softmac->ieee = netdev_priv(dev);
39 spin_lock_init(&softmac->lock);
40
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;
Johannes Bergb6c76582006-01-31 19:49:42 +010044 softmac->ieee->handle_reassoc_request = ieee80211softmac_handle_reassoc_req;
Johannes Berg370121e2006-01-04 16:32:16 +010045 softmac->ieee->handle_disassoc = ieee80211softmac_handle_disassoc;
46 softmac->scaninfo = NULL;
47
Johannes Berg818667f2006-04-20 20:02:03 +020048 softmac->associnfo.scan_retry = IEEE80211SOFTMAC_ASSOC_SCAN_RETRY_LIMIT;
49
Johannes Berg370121e2006-01-04 16:32:16 +010050 /* TODO: initialise all the other callbacks in the ieee struct
51 * (once they're written)
52 */
53
Johannes Berg370121e2006-01-04 16:32:16 +010054 INIT_LIST_HEAD(&softmac->auth_queue);
55 INIT_LIST_HEAD(&softmac->network_list);
56 INIT_LIST_HEAD(&softmac->events);
57
58 INIT_WORK(&softmac->associnfo.work, ieee80211softmac_assoc_work, softmac);
59 INIT_WORK(&softmac->associnfo.timeout, ieee80211softmac_assoc_timeout, softmac);
60 softmac->start_scan = ieee80211softmac_start_scan_implementation;
61 softmac->wait_for_scan = ieee80211softmac_wait_for_scan_implementation;
62 softmac->stop_scan = ieee80211softmac_stop_scan_implementation;
63
64 //TODO: The mcast rate has to be assigned dynamically somewhere (in scanning, association. Not sure...)
65 // It has to be set to the highest rate all stations in the current network can handle.
66 softmac->txrates.mcast_rate = IEEE80211_CCK_RATE_1MB;
67 softmac->txrates.mcast_fallback = IEEE80211_CCK_RATE_1MB;
68 /* This is reassigned in ieee80211softmac_start to sane values. */
69 softmac->txrates.default_rate = IEEE80211_CCK_RATE_1MB;
70 softmac->txrates.default_fallback = IEEE80211_CCK_RATE_1MB;
71
Johannes Berg2dd50802006-01-06 18:11:23 +010072 /* to start with, we can't send anything ... */
73 netif_carrier_off(dev);
Johannes Berg370121e2006-01-04 16:32:16 +010074
Johannes Berg370121e2006-01-04 16:32:16 +010075 return dev;
Johannes Berg370121e2006-01-04 16:32:16 +010076}
Johannes Berg4c718cf2006-01-12 21:19:48 +010077EXPORT_SYMBOL_GPL(alloc_ieee80211softmac);
Johannes Berg370121e2006-01-04 16:32:16 +010078
79/* Clears the pending work queue items, stops all scans, etc. */
80void
81ieee80211softmac_clear_pending_work(struct ieee80211softmac_device *sm)
82{
83 unsigned long flags;
84 struct ieee80211softmac_event *eventptr, *eventtmp;
85 struct ieee80211softmac_auth_queue_item *authptr, *authtmp;
86 struct ieee80211softmac_network *netptr, *nettmp;
87
88 ieee80211softmac_stop_scan(sm);
89 ieee80211softmac_wait_for_scan(sm);
90
91 spin_lock_irqsave(&sm->lock, flags);
92 /* Free all pending assoc work items */
93 cancel_delayed_work(&sm->associnfo.work);
94
95 /* Free all pending scan work items */
96 if(sm->scaninfo != NULL)
97 cancel_delayed_work(&sm->scaninfo->softmac_scan);
98
99 /* Free all pending auth work items */
100 list_for_each_entry(authptr, &sm->auth_queue, list)
101 cancel_delayed_work(&authptr->work);
102
103 /* delete all pending event calls and work items */
104 list_for_each_entry_safe(eventptr, eventtmp, &sm->events, list)
105 cancel_delayed_work(&eventptr->work);
106
107 spin_unlock_irqrestore(&sm->lock, flags);
Johannes Berg5c4df6d2006-01-06 01:43:45 +0100108 flush_scheduled_work();
Johannes Berg370121e2006-01-04 16:32:16 +0100109
Johannes Bergb2b9b6512006-01-11 19:32:02 +0100110 /* now we should be save and no longer need locking... */
Johannes Berg370121e2006-01-04 16:32:16 +0100111 spin_lock_irqsave(&sm->lock, flags);
112 /* Free all pending auth work items */
113 list_for_each_entry_safe(authptr, authtmp, &sm->auth_queue, list) {
114 list_del(&authptr->list);
115 kfree(authptr);
116 }
117
118 /* delete all pending event calls and work items */
119 list_for_each_entry_safe(eventptr, eventtmp, &sm->events, list) {
120 list_del(&eventptr->list);
121 kfree(eventptr);
122 }
123
124 /* Free all networks */
125 list_for_each_entry_safe(netptr, nettmp, &sm->network_list, list) {
126 ieee80211softmac_del_network_locked(sm, netptr);
127 if(netptr->challenge != NULL)
128 kfree(netptr->challenge);
129 kfree(netptr);
130 }
131
132 spin_unlock_irqrestore(&sm->lock, flags);
133}
Johannes Berg4c718cf2006-01-12 21:19:48 +0100134EXPORT_SYMBOL_GPL(ieee80211softmac_clear_pending_work);
Johannes Berg370121e2006-01-04 16:32:16 +0100135
136void free_ieee80211softmac(struct net_device *dev)
137{
138 struct ieee80211softmac_device *sm = ieee80211_priv(dev);
139 ieee80211softmac_clear_pending_work(sm);
Johannes Berg370121e2006-01-04 16:32:16 +0100140 kfree(sm->scaninfo);
141 kfree(sm->wpa.IE);
142 free_ieee80211(dev);
143}
Johannes Berg4c718cf2006-01-12 21:19:48 +0100144EXPORT_SYMBOL_GPL(free_ieee80211softmac);
Johannes Berg370121e2006-01-04 16:32:16 +0100145
146static void ieee80211softmac_start_check_rates(struct ieee80211softmac_device *mac)
147{
148 struct ieee80211softmac_ratesinfo *ri = &mac->ratesinfo;
149 /* I took out the sorting check, we're seperating by modulation now. */
150 if (ri->count)
151 return;
152 /* otherwise assume we hav'em all! */
153 if (mac->ieee->modulation & IEEE80211_CCK_MODULATION) {
154 ri->rates[ri->count++] = IEEE80211_CCK_RATE_1MB;
155 ri->rates[ri->count++] = IEEE80211_CCK_RATE_2MB;
156 ri->rates[ri->count++] = IEEE80211_CCK_RATE_5MB;
157 ri->rates[ri->count++] = IEEE80211_CCK_RATE_11MB;
158 }
159 if (mac->ieee->modulation & IEEE80211_OFDM_MODULATION) {
160 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_6MB;
161 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_9MB;
162 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_12MB;
163 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_18MB;
164 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_24MB;
165 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_36MB;
166 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_48MB;
167 ri->rates[ri->count++] = IEEE80211_OFDM_RATE_54MB;
168 }
169}
170
171void ieee80211softmac_start(struct net_device *dev)
172{
173 struct ieee80211softmac_device *mac = ieee80211_priv(dev);
174 struct ieee80211_device *ieee = mac->ieee;
175 u32 change = 0;
176 struct ieee80211softmac_txrates oldrates;
177
178 ieee80211softmac_start_check_rates(mac);
179
180 /* TODO: We need some kind of state machine to lower the default rates
181 * if we loose too many packets.
182 */
183 /* Change the default txrate to the highest possible value.
184 * The txrate machine will lower it, if it is too high.
185 */
186 if (mac->txrates_change)
187 oldrates = mac->txrates;
David Woodhouse2638fed2006-03-23 22:43:38 +0000188 /* FIXME: We don't correctly handle backing down to lower
189 rates, so 801.11g devices start off at 11M for now. People
190 can manually change it if they really need to, but 11M is
191 more reliable. Note similar logic in
192 ieee80211softmac_wx_set_rate() */
193 if (ieee->modulation & IEEE80211_CCK_MODULATION) {
Johannes Berg370121e2006-01-04 16:32:16 +0100194 mac->txrates.default_rate = IEEE80211_CCK_RATE_11MB;
195 change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
196 mac->txrates.default_fallback = IEEE80211_CCK_RATE_5MB;
197 change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
David Woodhouse2638fed2006-03-23 22:43:38 +0000198 } else if (ieee->modulation & IEEE80211_OFDM_MODULATION) {
199 mac->txrates.default_rate = IEEE80211_OFDM_RATE_54MB;
200 change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
201 mac->txrates.default_fallback = IEEE80211_OFDM_RATE_24MB;
202 change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
Johannes Berg370121e2006-01-04 16:32:16 +0100203 } else
204 assert(0);
205 if (mac->txrates_change)
206 mac->txrates_change(dev, change, &oldrates);
207}
Johannes Berg4c718cf2006-01-12 21:19:48 +0100208EXPORT_SYMBOL_GPL(ieee80211softmac_start);
Johannes Berg370121e2006-01-04 16:32:16 +0100209
210void ieee80211softmac_stop(struct net_device *dev)
211{
212 struct ieee80211softmac_device *mac = ieee80211_priv(dev);
213
214 ieee80211softmac_clear_pending_work(mac);
215}
Johannes Berg4c718cf2006-01-12 21:19:48 +0100216EXPORT_SYMBOL_GPL(ieee80211softmac_stop);
Johannes Berg370121e2006-01-04 16:32:16 +0100217
218void ieee80211softmac_set_rates(struct net_device *dev, u8 count, u8 *rates)
219{
220 struct ieee80211softmac_device *mac = ieee80211_priv(dev);
221 unsigned long flags;
222
223 spin_lock_irqsave(&mac->lock, flags);
224 memcpy(mac->ratesinfo.rates, rates, count);
225 mac->ratesinfo.count = count;
226 spin_unlock_irqrestore(&mac->lock, flags);
227}
Johannes Berg4c718cf2006-01-12 21:19:48 +0100228EXPORT_SYMBOL_GPL(ieee80211softmac_set_rates);
Johannes Berg370121e2006-01-04 16:32:16 +0100229
230static u8 raise_rate(struct ieee80211softmac_device *mac, u8 rate)
231{
232 int i;
233 struct ieee80211softmac_ratesinfo *ri = &mac->ratesinfo;
234
235 for (i=0; i<ri->count-1; i++) {
236 if (ri->rates[i] == rate)
237 return ri->rates[i+1];
238 }
239 /* I guess we can't go any higher... */
240 return ri->rates[ri->count];
241}
242
243u8 ieee80211softmac_lower_rate_delta(struct ieee80211softmac_device *mac, u8 rate, int delta)
244{
245 int i;
246 struct ieee80211softmac_ratesinfo *ri = &mac->ratesinfo;
247
248 for (i=delta; i<ri->count; i++) {
249 if (ri->rates[i] == rate)
250 return ri->rates[i-delta];
251 }
252 /* I guess we can't go any lower... */
253 return ri->rates[0];
254}
255
256static void ieee80211softmac_add_txrates_badness(struct ieee80211softmac_device *mac,
257 int amount)
258{
259 struct ieee80211softmac_txrates oldrates;
260 u8 default_rate = mac->txrates.default_rate;
261 u8 default_fallback = mac->txrates.default_fallback;
262 u32 changes = 0;
263
264 //TODO: This is highly experimental code.
265 // Maybe the dynamic rate selection does not work
266 // and it has to be removed again.
267
268printk("badness %d\n", mac->txrate_badness);
269 mac->txrate_badness += amount;
270 if (mac->txrate_badness <= -1000) {
271 /* Very small badness. Try a faster bitrate. */
272 if (mac->txrates_change)
273 memcpy(&oldrates, &mac->txrates, sizeof(oldrates));
274 default_rate = raise_rate(mac, default_rate);
275 changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
276 default_fallback = get_fallback_rate(mac, default_rate);
277 changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
278 mac->txrate_badness = 0;
279printk("Bitrate raised to %u\n", default_rate);
280 } else if (mac->txrate_badness >= 10000) {
281 /* Very high badness. Try a slower bitrate. */
282 if (mac->txrates_change)
283 memcpy(&oldrates, &mac->txrates, sizeof(oldrates));
284 default_rate = lower_rate(mac, default_rate);
285 changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
286 default_fallback = get_fallback_rate(mac, default_rate);
287 changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
288 mac->txrate_badness = 0;
289printk("Bitrate lowered to %u\n", default_rate);
290 }
291
292 mac->txrates.default_rate = default_rate;
293 mac->txrates.default_fallback = default_fallback;
294
295 if (changes && mac->txrates_change)
296 mac->txrates_change(mac->dev, changes, &oldrates);
297}
298
299void ieee80211softmac_fragment_lost(struct net_device *dev,
300 u16 wl_seq)
301{
302 struct ieee80211softmac_device *mac = ieee80211_priv(dev);
303 unsigned long flags;
304
305 spin_lock_irqsave(&mac->lock, flags);
306 ieee80211softmac_add_txrates_badness(mac, 1000);
307 //TODO
308
309 spin_unlock_irqrestore(&mac->lock, flags);
310}
Johannes Berg4c718cf2006-01-12 21:19:48 +0100311EXPORT_SYMBOL_GPL(ieee80211softmac_fragment_lost);
Johannes Berg370121e2006-01-04 16:32:16 +0100312
313static int rate_cmp(const void *a_, const void *b_) {
314 u8 *a, *b;
315 a = (u8*)a_;
316 b = (u8*)b_;
317 return ((*a & ~IEEE80211_BASIC_RATE_MASK) - (*b & ~IEEE80211_BASIC_RATE_MASK));
318}
319
320/* Allocate a softmac network struct and fill it from a network */
321struct ieee80211softmac_network *
322ieee80211softmac_create_network(struct ieee80211softmac_device *mac,
323 struct ieee80211_network *net)
324{
325 struct ieee80211softmac_network *softnet;
326 softnet = kzalloc(sizeof(struct ieee80211softmac_network), GFP_ATOMIC);
327 if(softnet == NULL)
328 return NULL;
329 memcpy(softnet->bssid, net->bssid, ETH_ALEN);
330 softnet->channel = net->channel;
331 softnet->essid.len = net->ssid_len;
332 memcpy(softnet->essid.data, net->ssid, softnet->essid.len);
333
334 /* copy rates over */
335 softnet->supported_rates.count = net->rates_len;
336 memcpy(&softnet->supported_rates.rates[0], net->rates, net->rates_len);
337 memcpy(&softnet->supported_rates.rates[softnet->supported_rates.count], net->rates_ex, net->rates_ex_len);
338 softnet->supported_rates.count += net->rates_ex_len;
339 sort(softnet->supported_rates.rates, softnet->supported_rates.count, sizeof(softnet->supported_rates.rates[0]), rate_cmp, NULL);
340
341 softnet->capabilities = net->capability;
342 return softnet;
343}
344
345
346/* Add a network to the list, while locked */
347void
348ieee80211softmac_add_network_locked(struct ieee80211softmac_device *mac,
349 struct ieee80211softmac_network *add_net)
350{
351 struct list_head *list_ptr;
352 struct ieee80211softmac_network *softmac_net = NULL;
353
354 list_for_each(list_ptr, &mac->network_list) {
355 softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list);
356 if(!memcmp(softmac_net->bssid, add_net->bssid, ETH_ALEN))
357 break;
358 else
359 softmac_net = NULL;
360 }
361 if(softmac_net == NULL)
362 list_add(&(add_net->list), &mac->network_list);
363}
364
365/* Add a network to the list, with locking */
366void
367ieee80211softmac_add_network(struct ieee80211softmac_device *mac,
368 struct ieee80211softmac_network *add_net)
369{
370 unsigned long flags;
371 spin_lock_irqsave(&mac->lock, flags);
372 ieee80211softmac_add_network_locked(mac, add_net);
373 spin_unlock_irqrestore(&mac->lock, flags);
374}
375
376
377/* Delete a network from the list, while locked*/
378void
379ieee80211softmac_del_network_locked(struct ieee80211softmac_device *mac,
380 struct ieee80211softmac_network *del_net)
381{
382 list_del(&(del_net->list));
383}
384
385/* Delete a network from the list with locking */
386void
387ieee80211softmac_del_network(struct ieee80211softmac_device *mac,
388 struct ieee80211softmac_network *del_net)
389{
390 unsigned long flags;
391 spin_lock_irqsave(&mac->lock, flags);
392 ieee80211softmac_del_network_locked(mac, del_net);
393 spin_unlock_irqrestore(&mac->lock, flags);
394}
395
396/* Get a network from the list by MAC while locked */
397struct ieee80211softmac_network *
398ieee80211softmac_get_network_by_bssid_locked(struct ieee80211softmac_device *mac,
399 u8 *bssid)
400{
401 struct list_head *list_ptr;
402 struct ieee80211softmac_network *softmac_net = NULL;
403 list_for_each(list_ptr, &mac->network_list) {
404 softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list);
405 if(!memcmp(softmac_net->bssid, bssid, ETH_ALEN))
406 break;
407 else
408 softmac_net = NULL;
409 }
410 return softmac_net;
411}
412
413/* Get a network from the list by BSSID with locking */
414struct ieee80211softmac_network *
415ieee80211softmac_get_network_by_bssid(struct ieee80211softmac_device *mac,
416 u8 *bssid)
417{
418 unsigned long flags;
419 struct ieee80211softmac_network *softmac_net;
420
421 spin_lock_irqsave(&mac->lock, flags);
422 softmac_net = ieee80211softmac_get_network_by_bssid_locked(mac, bssid);
423 spin_unlock_irqrestore(&mac->lock, flags);
424 return softmac_net;
425}
426
427/* Get a network from the list by ESSID while locked */
428struct ieee80211softmac_network *
429ieee80211softmac_get_network_by_essid_locked(struct ieee80211softmac_device *mac,
430 struct ieee80211softmac_essid *essid)
431{
432 struct list_head *list_ptr;
433 struct ieee80211softmac_network *softmac_net = NULL;
434
435 list_for_each(list_ptr, &mac->network_list) {
436 softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list);
437 if (softmac_net->essid.len == essid->len &&
438 !memcmp(softmac_net->essid.data, essid->data, essid->len))
439 return softmac_net;
440 }
441 return NULL;
442}
443
444/* Get a network from the list by ESSID with locking */
445struct ieee80211softmac_network *
446ieee80211softmac_get_network_by_essid(struct ieee80211softmac_device *mac,
447 struct ieee80211softmac_essid *essid)
448{
449 unsigned long flags;
450 struct ieee80211softmac_network *softmac_net = NULL;
451
452 spin_lock_irqsave(&mac->lock, flags);
453 softmac_net = ieee80211softmac_get_network_by_essid_locked(mac, essid);
454 spin_unlock_irqrestore(&mac->lock, flags);
455 return softmac_net;
456}
457
458MODULE_LICENSE("GPL");
Johannes Berg9ebdd462006-01-12 21:18:25 +0100459MODULE_AUTHOR("Johannes Berg");
460MODULE_AUTHOR("Joseph Jezak");
461MODULE_AUTHOR("Larry Finger");
462MODULE_AUTHOR("Danny van Dyk");
463MODULE_AUTHOR("Michael Buesch");
464MODULE_DESCRIPTION("802.11 software MAC");