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