fcafe1925a72d1b2c799f0339cfcfec961cb89f5
[pandora-kernel.git] / drivers / staging / rtl8192e / ieee80211 / ieee80211_softmac_wx.c
1 /* IEEE 802.11 SoftMAC layer
2  * Copyright (c) 2005 Andrea Merello <andreamrl@tiscali.it>
3  *
4  * Mostly extracted from the rtl8180-sa2400 driver for the
5  * in-kernel generic ieee802.11 stack.
6  *
7  * Some pieces of code might be stolen from ipw2100 driver
8  * copyright of who own it's copyright ;-)
9  *
10  * PS wx handler mostly stolen from hostap, copyright who
11  * own it's copyright ;-)
12  *
13  * released under the GPL
14  */
15
16
17 #include "ieee80211.h"
18 #ifdef ENABLE_DOT11D
19 #include "dot11d.h"
20 #endif
21 /* FIXME: add A freqs */
22
23 const long ieee80211_wlan_frequencies[] = {
24         2412, 2417, 2422, 2427,
25         2432, 2437, 2442, 2447,
26         2452, 2457, 2462, 2467,
27         2472, 2484
28 };
29
30
31 int ieee80211_wx_set_freq(struct ieee80211_device *ieee, struct iw_request_info *a,
32                              union iwreq_data *wrqu, char *b)
33 {
34         int ret;
35         struct iw_freq *fwrq = & wrqu->freq;
36
37         down(&ieee->wx_sem);
38
39         if(ieee->iw_mode == IW_MODE_INFRA){
40                 ret = -EOPNOTSUPP;
41                 goto out;
42         }
43
44         /* if setting by freq convert to channel */
45         if (fwrq->e == 1) {
46                 if ((fwrq->m >= (int) 2.412e8 &&
47                      fwrq->m <= (int) 2.487e8)) {
48                         int f = fwrq->m / 100000;
49                         int c = 0;
50
51                         while ((c < 14) && (f != ieee80211_wlan_frequencies[c]))
52                                 c++;
53
54                         /* hack to fall through */
55                         fwrq->e = 0;
56                         fwrq->m = c + 1;
57                 }
58         }
59
60         if (fwrq->e > 0 || fwrq->m > 14 || fwrq->m < 1 ){
61                 ret = -EOPNOTSUPP;
62                 goto out;
63
64         }else { /* Set the channel */
65
66 #ifdef ENABLE_DOT11D
67                 if (!(GET_DOT11D_INFO(ieee)->channel_map)[fwrq->m]) {
68                         ret = -EINVAL;
69                         goto out;
70                 }
71 #endif
72                 ieee->current_network.channel = fwrq->m;
73                 ieee->set_chan(ieee, ieee->current_network.channel);
74
75                 if(ieee->iw_mode == IW_MODE_ADHOC || ieee->iw_mode == IW_MODE_MASTER)
76                         if(ieee->state == IEEE80211_LINKED){
77
78                         ieee80211_stop_send_beacons(ieee);
79                         ieee80211_start_send_beacons(ieee);
80                         }
81         }
82
83         ret = 0;
84 out:
85         up(&ieee->wx_sem);
86         return ret;
87 }
88
89
90 int ieee80211_wx_get_freq(struct ieee80211_device *ieee,
91                              struct iw_request_info *a,
92                              union iwreq_data *wrqu, char *b)
93 {
94         struct iw_freq *fwrq = & wrqu->freq;
95
96         if (ieee->current_network.channel == 0)
97                 return -1;
98         //NM 0.7.0 will not accept channel any more.
99         fwrq->m = ieee80211_wlan_frequencies[ieee->current_network.channel-1] * 100000;
100         fwrq->e = 1;
101
102         return 0;
103 }
104
105 int ieee80211_wx_get_wap(struct ieee80211_device *ieee,
106                             struct iw_request_info *info,
107                             union iwreq_data *wrqu, char *extra)
108 {
109         unsigned long flags;
110         wrqu->ap_addr.sa_family = ARPHRD_ETHER;
111
112         if (ieee->iw_mode == IW_MODE_MONITOR)
113                 return -1;
114
115         /* We want avoid to give to the user inconsistent infos*/
116         spin_lock_irqsave(&ieee->lock, flags);
117
118         if (ieee->state != IEEE80211_LINKED &&
119                 ieee->state != IEEE80211_LINKED_SCANNING &&
120                 ieee->wap_set == 0)
121
122                 memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
123         else
124                 memcpy(wrqu->ap_addr.sa_data,
125                        ieee->current_network.bssid, ETH_ALEN);
126
127         spin_unlock_irqrestore(&ieee->lock, flags);
128
129         return 0;
130 }
131
132
133 int ieee80211_wx_set_wap(struct ieee80211_device *ieee,
134                          struct iw_request_info *info,
135                          union iwreq_data *awrq,
136                          char *extra)
137 {
138
139         int ret = 0;
140         u8 zero[] = {0,0,0,0,0,0};
141         unsigned long flags;
142
143         short ifup = ieee->proto_started;//dev->flags & IFF_UP;
144         struct sockaddr *temp = (struct sockaddr *)awrq;
145
146         ieee->sync_scan_hurryup = 1;
147
148         down(&ieee->wx_sem);
149         /* use ifconfig hw ether */
150         if (ieee->iw_mode == IW_MODE_MASTER){
151                 ret = -1;
152                 goto out;
153         }
154
155         if (temp->sa_family != ARPHRD_ETHER){
156                 ret = -EINVAL;
157                 goto out;
158         }
159
160         if (ifup)
161                 ieee80211_stop_protocol(ieee,true);
162
163         /* just to avoid to give inconsistent infos in the
164          * get wx method. not really needed otherwise
165          */
166         spin_lock_irqsave(&ieee->lock, flags);
167
168         memcpy(ieee->current_network.bssid, temp->sa_data, ETH_ALEN);
169         ieee->wap_set = memcmp(temp->sa_data, zero,ETH_ALEN)!=0;
170
171         spin_unlock_irqrestore(&ieee->lock, flags);
172
173         if (ifup)
174                 ieee80211_start_protocol(ieee);
175 out:
176         up(&ieee->wx_sem);
177         return ret;
178 }
179
180  int ieee80211_wx_get_essid(struct ieee80211_device *ieee, struct iw_request_info *a,union iwreq_data *wrqu,char *b)
181 {
182         int len,ret = 0;
183         unsigned long flags;
184
185         if (ieee->iw_mode == IW_MODE_MONITOR)
186                 return -1;
187
188         /* We want avoid to give to the user inconsistent infos*/
189         spin_lock_irqsave(&ieee->lock, flags);
190
191         if (ieee->current_network.ssid[0] == '\0' ||
192                 ieee->current_network.ssid_len == 0){
193                 ret = -1;
194                 goto out;
195         }
196
197         if (ieee->state != IEEE80211_LINKED &&
198                 ieee->state != IEEE80211_LINKED_SCANNING &&
199                 ieee->ssid_set == 0){
200                 ret = -1;
201                 goto out;
202         }
203         len = ieee->current_network.ssid_len;
204         wrqu->essid.length = len;
205         strncpy(b,ieee->current_network.ssid,len);
206         wrqu->essid.flags = 1;
207
208 out:
209         spin_unlock_irqrestore(&ieee->lock, flags);
210
211         return ret;
212
213 }
214
215 int ieee80211_wx_set_rate(struct ieee80211_device *ieee,
216                              struct iw_request_info *info,
217                              union iwreq_data *wrqu, char *extra)
218 {
219
220         u32 target_rate = wrqu->bitrate.value;
221
222         ieee->rate = target_rate/100000;
223         //FIXME: we might want to limit rate also in management protocols.
224         return 0;
225 }
226
227
228
229 int ieee80211_wx_get_rate(struct ieee80211_device *ieee,
230                              struct iw_request_info *info,
231                              union iwreq_data *wrqu, char *extra)
232 {
233         u32 tmp_rate;
234         tmp_rate = TxCountToDataRate(ieee, ieee->softmac_stats.CurrentShowTxate);
235
236         wrqu->bitrate.value = tmp_rate * 500000;
237
238         return 0;
239 }
240
241
242 int ieee80211_wx_set_rts(struct ieee80211_device *ieee,
243                              struct iw_request_info *info,
244                              union iwreq_data *wrqu, char *extra)
245 {
246         if (wrqu->rts.disabled || !wrqu->rts.fixed)
247                 ieee->rts = DEFAULT_RTS_THRESHOLD;
248         else
249         {
250                 if (wrqu->rts.value < MIN_RTS_THRESHOLD ||
251                                 wrqu->rts.value > MAX_RTS_THRESHOLD)
252                         return -EINVAL;
253                 ieee->rts = wrqu->rts.value;
254         }
255         return 0;
256 }
257
258 int ieee80211_wx_get_rts(struct ieee80211_device *ieee,
259                              struct iw_request_info *info,
260                              union iwreq_data *wrqu, char *extra)
261 {
262         wrqu->rts.value = ieee->rts;
263         wrqu->rts.fixed = 0;    /* no auto select */
264         wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD);
265         return 0;
266 }
267 int ieee80211_wx_set_mode(struct ieee80211_device *ieee, struct iw_request_info *a,
268                              union iwreq_data *wrqu, char *b)
269 {
270
271         ieee->sync_scan_hurryup = 1;
272
273         down(&ieee->wx_sem);
274
275         if (wrqu->mode == ieee->iw_mode)
276                 goto out;
277
278         if (wrqu->mode == IW_MODE_MONITOR){
279
280                 ieee->dev->type = ARPHRD_IEEE80211;
281         }else{
282                 ieee->dev->type = ARPHRD_ETHER;
283         }
284
285         if (!ieee->proto_started){
286                 ieee->iw_mode = wrqu->mode;
287         }else{
288                 ieee80211_stop_protocol(ieee,true);
289                 ieee->iw_mode = wrqu->mode;
290                 ieee80211_start_protocol(ieee);
291         }
292
293 out:
294         up(&ieee->wx_sem);
295         return 0;
296 }
297
298 void ieee80211_wx_sync_scan_wq(struct work_struct *work)
299 {
300         struct ieee80211_device *ieee = container_of(work, struct ieee80211_device, wx_sync_scan_wq);
301         short chan;
302         HT_EXTCHNL_OFFSET chan_offset=0;
303         HT_CHANNEL_WIDTH bandwidth=0;
304         int b40M = 0;
305         static int count = 0;
306         chan = ieee->current_network.channel;
307
308 #ifdef ENABLE_LPS
309         if (ieee->LeisurePSLeave) {
310                 ieee->LeisurePSLeave(ieee);
311         }
312
313         /* notify AP to be in PS mode */
314         ieee80211_sta_ps_send_null_frame(ieee, 1);
315         ieee80211_sta_ps_send_null_frame(ieee, 1);
316 #endif
317
318         if (ieee->data_hard_stop)
319                 ieee->data_hard_stop(ieee);
320
321         ieee80211_stop_send_beacons(ieee);
322
323         ieee->state = IEEE80211_LINKED_SCANNING;
324         ieee->link_change(ieee);
325         ieee->InitialGainHandler(ieee->dev,IG_Backup);
326         if (ieee->pHTInfo->bCurrentHTSupport && ieee->pHTInfo->bEnableHT && ieee->pHTInfo->bCurBW40MHz) {
327                 b40M = 1;
328                 chan_offset = ieee->pHTInfo->CurSTAExtChnlOffset;
329                 bandwidth = (HT_CHANNEL_WIDTH)ieee->pHTInfo->bCurBW40MHz;
330                 printk("Scan in 40M, force to 20M first:%d, %d\n", chan_offset, bandwidth);
331                 ieee->SetBWModeHandler(ieee->dev, HT_CHANNEL_WIDTH_20, HT_EXTCHNL_OFFSET_NO_EXT);
332                 }
333         ieee80211_start_scan_syncro(ieee);
334         if (b40M) {
335                 printk("Scan in 20M, back to 40M\n");
336                 if (chan_offset == HT_EXTCHNL_OFFSET_UPPER)
337                         ieee->set_chan(ieee, chan + 2);
338                 else if (chan_offset == HT_EXTCHNL_OFFSET_LOWER)
339                         ieee->set_chan(ieee, chan - 2);
340                 else
341                         ieee->set_chan(ieee, chan);
342                 ieee->SetBWModeHandler(ieee->dev, bandwidth, chan_offset);
343         } else {
344                 ieee->set_chan(ieee, chan);
345         }
346
347         ieee->InitialGainHandler(ieee->dev,IG_Restore);
348         ieee->state = IEEE80211_LINKED;
349         ieee->link_change(ieee);
350
351 #ifdef ENABLE_LPS
352         /* Notify AP that I wake up again */
353         ieee80211_sta_ps_send_null_frame(ieee, 0);
354 #endif
355
356         // To prevent the immediately calling watch_dog after scan.
357         if(ieee->LinkDetectInfo.NumRecvBcnInPeriod==0||ieee->LinkDetectInfo.NumRecvDataInPeriod==0 )
358         {
359                 ieee->LinkDetectInfo.NumRecvBcnInPeriod = 1;
360                 ieee->LinkDetectInfo.NumRecvDataInPeriod= 1;
361         }
362         if (ieee->data_hard_resume)
363                 ieee->data_hard_resume(ieee);
364
365         if(ieee->iw_mode == IW_MODE_ADHOC || ieee->iw_mode == IW_MODE_MASTER)
366                 ieee80211_start_send_beacons(ieee);
367
368         count = 0;
369         up(&ieee->wx_sem);
370
371 }
372
373 int ieee80211_wx_set_scan(struct ieee80211_device *ieee, struct iw_request_info *a,
374                              union iwreq_data *wrqu, char *b)
375 {
376         int ret = 0;
377
378         down(&ieee->wx_sem);
379
380         if (ieee->iw_mode == IW_MODE_MONITOR || !(ieee->proto_started)){
381                 ret = -1;
382                 goto out;
383         }
384
385         if ( ieee->state == IEEE80211_LINKED){
386                 queue_work(ieee->wq, &ieee->wx_sync_scan_wq);
387                 /* intentionally forget to up sem */
388                 return 0;
389         }
390
391 out:
392         up(&ieee->wx_sem);
393         return ret;
394 }
395
396 int ieee80211_wx_set_essid(struct ieee80211_device *ieee,
397                               struct iw_request_info *a,
398                               union iwreq_data *wrqu, char *extra)
399 {
400
401         int ret=0,len;
402         short proto_started;
403         unsigned long flags;
404
405         ieee->sync_scan_hurryup = 1;
406         down(&ieee->wx_sem);
407
408         proto_started = ieee->proto_started;
409
410         if (wrqu->essid.length > IW_ESSID_MAX_SIZE){
411                 ret= -E2BIG;
412                 goto out;
413         }
414
415         if (ieee->iw_mode == IW_MODE_MONITOR){
416                 ret= -1;
417                 goto out;
418         }
419
420         if(proto_started){
421                 ieee80211_stop_protocol(ieee,true);
422         }
423
424
425         /* this is just to be sure that the GET wx callback
426          * has consisten infos. not needed otherwise
427          */
428         spin_lock_irqsave(&ieee->lock, flags);
429
430         if (wrqu->essid.flags && wrqu->essid.length) {
431                 //first flush current network.ssid
432                 len = ((wrqu->essid.length-1) < IW_ESSID_MAX_SIZE) ? (wrqu->essid.length-1) : IW_ESSID_MAX_SIZE;
433                 strncpy(ieee->current_network.ssid, extra, len+1);
434                 ieee->current_network.ssid_len = len+1;
435                 ieee->ssid_set = 1;
436         }
437         else{
438                 ieee->ssid_set = 0;
439                 ieee->current_network.ssid[0] = '\0';
440                 ieee->current_network.ssid_len = 0;
441         }
442         spin_unlock_irqrestore(&ieee->lock, flags);
443
444         if (proto_started)
445                 ieee80211_start_protocol(ieee);
446 out:
447         up(&ieee->wx_sem);
448         return ret;
449 }
450
451  int ieee80211_wx_get_mode(struct ieee80211_device *ieee, struct iw_request_info *a,
452                              union iwreq_data *wrqu, char *b)
453 {
454
455         wrqu->mode = ieee->iw_mode;
456         return 0;
457 }
458
459  int ieee80211_wx_set_rawtx(struct ieee80211_device *ieee,
460                                struct iw_request_info *info,
461                                union iwreq_data *wrqu, char *extra)
462 {
463
464         int *parms = (int *)extra;
465         int enable = (parms[0] > 0);
466         short prev = ieee->raw_tx;
467
468         down(&ieee->wx_sem);
469
470         if(enable)
471                 ieee->raw_tx = 1;
472         else
473                 ieee->raw_tx = 0;
474
475         printk(KERN_INFO"raw TX is %s\n",
476               ieee->raw_tx ? "enabled" : "disabled");
477
478         if(ieee->iw_mode == IW_MODE_MONITOR)
479         {
480                 if(prev == 0 && ieee->raw_tx){
481                         if (ieee->data_hard_resume)
482                                 ieee->data_hard_resume(ieee);
483
484                         netif_carrier_on(ieee->dev);
485                 }
486
487                 if(prev && ieee->raw_tx == 1)
488                         netif_carrier_off(ieee->dev);
489         }
490
491         up(&ieee->wx_sem);
492
493         return 0;
494 }
495
496 int ieee80211_wx_get_name(struct ieee80211_device *ieee,
497                              struct iw_request_info *info,
498                              union iwreq_data *wrqu, char *extra)
499 {
500         strcpy(wrqu->name, "802.11");
501         if(ieee->modulation & IEEE80211_CCK_MODULATION)
502                 strcat(wrqu->name, "b");
503         if(ieee->modulation & IEEE80211_OFDM_MODULATION)
504                 strcat(wrqu->name, "g");
505         if (ieee->mode & (IEEE_N_24G | IEEE_N_5G))
506                 strcat(wrqu->name, "n");
507         return 0;
508 }
509
510
511 /* this is mostly stolen from hostap */
512 int ieee80211_wx_set_power(struct ieee80211_device *ieee,
513                                  struct iw_request_info *info,
514                                  union iwreq_data *wrqu, char *extra)
515 {
516         int ret = 0;
517
518         if(
519                 (!ieee->sta_wake_up) ||
520                 (!ieee->enter_sleep_state) ||
521                 (!ieee->ps_is_queue_empty)){
522
523                 return -1;
524         }
525
526         down(&ieee->wx_sem);
527
528         if (wrqu->power.disabled){
529                 ieee->ps = IEEE80211_PS_DISABLED;
530                 goto exit;
531         }
532         if (wrqu->power.flags & IW_POWER_TIMEOUT) {
533                 ieee->ps_timeout = wrqu->power.value / 1000;
534         }
535
536         if (wrqu->power.flags & IW_POWER_PERIOD) {
537                 ieee->ps_period = wrqu->power.value / 1000;
538         }
539         switch (wrqu->power.flags & IW_POWER_MODE) {
540         case IW_POWER_UNICAST_R:
541                 ieee->ps = IEEE80211_PS_UNICAST;
542                 break;
543         case IW_POWER_MULTICAST_R:
544                 ieee->ps = IEEE80211_PS_MBCAST;
545                 break;
546         case IW_POWER_ALL_R:
547                 ieee->ps = IEEE80211_PS_UNICAST | IEEE80211_PS_MBCAST;
548                 break;
549
550         case IW_POWER_ON:
551                 break;
552
553         default:
554                 ret = -EINVAL;
555                 goto exit;
556
557         }
558 exit:
559         up(&ieee->wx_sem);
560         return ret;
561
562 }
563
564 /* this is stolen from hostap */
565 int ieee80211_wx_get_power(struct ieee80211_device *ieee,
566                                  struct iw_request_info *info,
567                                  union iwreq_data *wrqu, char *extra)
568 {
569         int ret =0;
570
571         down(&ieee->wx_sem);
572
573         if(ieee->ps == IEEE80211_PS_DISABLED){
574                 wrqu->power.disabled = 1;
575                 goto exit;
576         }
577
578         wrqu->power.disabled = 0;
579
580         if ((wrqu->power.flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
581                 wrqu->power.flags = IW_POWER_TIMEOUT;
582                 wrqu->power.value = ieee->ps_timeout * 1000;
583         } else {
584                 wrqu->power.flags = IW_POWER_PERIOD;
585                 wrqu->power.value = ieee->ps_period * 1000;
586         }
587
588        if ((ieee->ps & (IEEE80211_PS_MBCAST | IEEE80211_PS_UNICAST)) == (IEEE80211_PS_MBCAST | IEEE80211_PS_UNICAST))
589                 wrqu->power.flags |= IW_POWER_ALL_R;
590         else if (ieee->ps & IEEE80211_PS_MBCAST)
591                 wrqu->power.flags |= IW_POWER_MULTICAST_R;
592         else
593                 wrqu->power.flags |= IW_POWER_UNICAST_R;
594
595 exit:
596         up(&ieee->wx_sem);
597         return ret;
598
599 }
600