blob: bef454c32d3f6e22334a256a34c7793225faf31f [file] [log] [blame]
Larry Finger94a79942011-08-23 19:00:42 -05001/* 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 "rtllib.h"
18#include "rtl_core.h"
Larry Finger94a79942011-08-23 19:00:42 -050019#include "dot11d.h"
Larry Finger94a79942011-08-23 19:00:42 -050020/* FIXME: add A freqs */
21
22const long rtllib_wlan_frequencies[] = {
23 2412, 2417, 2422, 2427,
24 2432, 2437, 2442, 2447,
25 2452, 2457, 2462, 2467,
26 2472, 2484
27};
28
29
30int rtllib_wx_set_freq(struct rtllib_device *ieee, struct iw_request_info *a,
31 union iwreq_data *wrqu, char *b)
32{
33 int ret;
34 struct iw_freq *fwrq = & wrqu->freq;
35
36 down(&ieee->wx_sem);
37
38 if (ieee->iw_mode == IW_MODE_INFRA){
39 ret = 0;
40 goto out;
41 }
42
43 /* if setting by freq convert to channel */
44 if (fwrq->e == 1) {
45 if ((fwrq->m >= (int) 2.412e8 &&
46 fwrq->m <= (int) 2.487e8)) {
47 int f = fwrq->m / 100000;
48 int c = 0;
49
50 while ((c < 14) && (f != rtllib_wlan_frequencies[c]))
51 c++;
52
53 /* hack to fall through */
54 fwrq->e = 0;
55 fwrq->m = c + 1;
56 }
57 }
58
59 if (fwrq->e > 0 || fwrq->m > 14 || fwrq->m < 1 ){
60 ret = -EOPNOTSUPP;
61 goto out;
62
63 }else { /* Set the channel */
64
Larry Finger94a79942011-08-23 19:00:42 -050065 if (ieee->active_channel_map[fwrq->m] != 1) {
66 ret = -EINVAL;
67 goto out;
68 }
Larry Finger94a79942011-08-23 19:00:42 -050069 ieee->current_network.channel = fwrq->m;
70 ieee->set_chan(ieee->dev, ieee->current_network.channel);
71
72 if (ieee->iw_mode == IW_MODE_ADHOC || ieee->iw_mode == IW_MODE_MASTER)
73 if (ieee->state == RTLLIB_LINKED){
74
75 rtllib_stop_send_beacons(ieee);
76 rtllib_start_send_beacons(ieee);
77 }
78 }
79
80 ret = 0;
81out:
82 up(&ieee->wx_sem);
83 return ret;
84}
85
86
87int rtllib_wx_get_freq(struct rtllib_device *ieee,
88 struct iw_request_info *a,
89 union iwreq_data *wrqu, char *b)
90{
91 struct iw_freq *fwrq = & wrqu->freq;
92
93 if (ieee->current_network.channel == 0)
94 return -1;
95 fwrq->m = rtllib_wlan_frequencies[ieee->current_network.channel-1] * 100000;
96 fwrq->e = 1;
97 return 0;
98}
99
100int rtllib_wx_get_wap(struct rtllib_device *ieee,
101 struct iw_request_info *info,
102 union iwreq_data *wrqu, char *extra)
103{
104 unsigned long flags;
105
106 wrqu->ap_addr.sa_family = ARPHRD_ETHER;
107
108 if (ieee->iw_mode == IW_MODE_MONITOR)
109 return -1;
110
111 /* We want avoid to give to the user inconsistent infos*/
112 spin_lock_irqsave(&ieee->lock, flags);
113
114 if (ieee->state != RTLLIB_LINKED &&
115 ieee->state != RTLLIB_LINKED_SCANNING &&
116 ieee->wap_set == 0)
117
118 memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
119 else
120 memcpy(wrqu->ap_addr.sa_data,
121 ieee->current_network.bssid, ETH_ALEN);
122
123 spin_unlock_irqrestore(&ieee->lock, flags);
124
125 return 0;
126}
127
128
129int rtllib_wx_set_wap(struct rtllib_device *ieee,
130 struct iw_request_info *info,
131 union iwreq_data *awrq,
132 char *extra)
133{
134
135 int ret = 0;
136 u8 zero[] = {0,0,0,0,0,0};
137 unsigned long flags;
138
139 short ifup = ieee->proto_started;
140 struct sockaddr *temp = (struct sockaddr *)awrq;
141
142 rtllib_stop_scan_syncro(ieee);
143
144 down(&ieee->wx_sem);
145 /* use ifconfig hw ether */
146 if (ieee->iw_mode == IW_MODE_MASTER){
147 ret = -1;
148 goto out;
149 }
150
151 if (temp->sa_family != ARPHRD_ETHER){
152 ret = -EINVAL;
153 goto out;
154 }
155
156 if (memcmp(temp->sa_data, zero,ETH_ALEN) == 0){
157 spin_lock_irqsave(&ieee->lock, flags);
158 memcpy(ieee->current_network.bssid, temp->sa_data, ETH_ALEN);
159 ieee->wap_set = 0;
160 spin_unlock_irqrestore(&ieee->lock, flags);
161 ret = -1;
162 goto out;
163 }
164
165
166 if (ifup)
167 rtllib_stop_protocol(ieee,true);
168
169 /* just to avoid to give inconsistent infos in the
170 * get wx method. not really needed otherwise
171 */
172 spin_lock_irqsave(&ieee->lock, flags);
173
174 ieee->cannot_notify = false;
175 memcpy(ieee->current_network.bssid, temp->sa_data, ETH_ALEN);
176 ieee->wap_set = (memcmp(temp->sa_data, zero,ETH_ALEN)!=0);
177
178 spin_unlock_irqrestore(&ieee->lock, flags);
179
180 if (ifup)
181 rtllib_start_protocol(ieee);
182out:
183 up(&ieee->wx_sem);
184 return ret;
185}
186
187 int rtllib_wx_get_essid(struct rtllib_device *ieee, struct iw_request_info *a,union iwreq_data *wrqu,char *b)
188{
189 int len,ret = 0;
190 unsigned long flags;
191
192 if (ieee->iw_mode == IW_MODE_MONITOR)
193 return -1;
194
195 /* We want avoid to give to the user inconsistent infos*/
196 spin_lock_irqsave(&ieee->lock, flags);
197
198 if (ieee->current_network.ssid[0] == '\0' ||
199 ieee->current_network.ssid_len == 0){
200 ret = -1;
201 goto out;
202 }
203
204 if (ieee->state != RTLLIB_LINKED &&
205 ieee->state != RTLLIB_LINKED_SCANNING &&
206 ieee->ssid_set == 0){
207 ret = -1;
208 goto out;
209 }
210 len = ieee->current_network.ssid_len;
211 wrqu->essid.length = len;
212 strncpy(b,ieee->current_network.ssid,len);
213 wrqu->essid.flags = 1;
214
215out:
216 spin_unlock_irqrestore(&ieee->lock, flags);
217
218 return ret;
219
220}
221
222int rtllib_wx_set_rate(struct rtllib_device *ieee,
223 struct iw_request_info *info,
224 union iwreq_data *wrqu, char *extra)
225{
226
227 u32 target_rate = wrqu->bitrate.value;
228
229 ieee->rate = target_rate/100000;
230 return 0;
231}
232
233int rtllib_wx_get_rate(struct rtllib_device *ieee,
234 struct iw_request_info *info,
235 union iwreq_data *wrqu, char *extra)
236{
237 u32 tmp_rate = 0;
Larry Finger94a79942011-08-23 19:00:42 -0500238 tmp_rate = TxCountToDataRate(ieee, ieee->softmac_stats.CurrentShowTxate);
Larry Finger94a79942011-08-23 19:00:42 -0500239 wrqu->bitrate.value = tmp_rate * 500000;
240
241 return 0;
242}
243
244
245int rtllib_wx_set_rts(struct rtllib_device *ieee,
246 struct iw_request_info *info,
247 union iwreq_data *wrqu, char *extra)
248{
249 if (wrqu->rts.disabled || !wrqu->rts.fixed)
250 ieee->rts = DEFAULT_RTS_THRESHOLD;
251 else
252 {
253 if (wrqu->rts.value < MIN_RTS_THRESHOLD ||
254 wrqu->rts.value > MAX_RTS_THRESHOLD)
255 return -EINVAL;
256 ieee->rts = wrqu->rts.value;
257 }
258 return 0;
259}
260
261int rtllib_wx_get_rts(struct rtllib_device *ieee,
262 struct iw_request_info *info,
263 union iwreq_data *wrqu, char *extra)
264{
265 wrqu->rts.value = ieee->rts;
266 wrqu->rts.fixed = 0; /* no auto select */
267 wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD);
268 return 0;
269}
270
271int rtllib_wx_set_mode(struct rtllib_device *ieee, struct iw_request_info *a,
272 union iwreq_data *wrqu, char *b)
273{
274 int set_mode_status = 0;
275
276 rtllib_stop_scan_syncro(ieee);
277 down(&ieee->wx_sem);
278 switch (wrqu->mode) {
279 case IW_MODE_MONITOR:
280 case IW_MODE_ADHOC:
281 case IW_MODE_INFRA:
282 break;
283 case IW_MODE_AUTO:
284 wrqu->mode = IW_MODE_INFRA;
285 break;
286 default:
287 set_mode_status = -EINVAL;
288 goto out;
289 }
290
291 if (wrqu->mode == ieee->iw_mode)
292 goto out;
293
294 if (wrqu->mode == IW_MODE_MONITOR) {
Larry Finger94a79942011-08-23 19:00:42 -0500295 ieee->dev->type = ARPHRD_IEEE80211;
Larry Finger94a79942011-08-23 19:00:42 -0500296 rtllib_EnableNetMonitorMode(ieee->dev,false);
Larry Finger94a79942011-08-23 19:00:42 -0500297 } else {
298 ieee->dev->type = ARPHRD_ETHER;
299 if (ieee->iw_mode == IW_MODE_MONITOR)
300 rtllib_DisableNetMonitorMode(ieee->dev,false);
301 }
302
303 if (!ieee->proto_started) {
304 ieee->iw_mode = wrqu->mode;
305 } else {
306 rtllib_stop_protocol(ieee,true);
307 ieee->iw_mode = wrqu->mode;
308 rtllib_start_protocol(ieee);
309 }
310
311out:
312 up(&ieee->wx_sem);
313 return set_mode_status;
314}
315
316void rtllib_wx_sync_scan_wq(void *data)
317{
318 struct rtllib_device *ieee = container_of_work_rsl(data, struct rtllib_device, wx_sync_scan_wq);
319 short chan;
320 HT_EXTCHNL_OFFSET chan_offset=0;
Larry Finger6e579112011-07-19 18:20:30 -0500321 enum ht_channel_width bandwidth=0;
Larry Finger94a79942011-08-23 19:00:42 -0500322 int b40M = 0;
323 static int count = 0;
324
325 if (!(ieee->softmac_features & IEEE_SOFTMAC_SCAN)){
326 rtllib_start_scan_syncro(ieee, 0);
327 goto out;
328 }
329
330 chan = ieee->current_network.channel;
331
332 if (ieee->LeisurePSLeave)
333 ieee->LeisurePSLeave(ieee->dev);
334 /* notify AP to be in PS mode */
335 rtllib_sta_ps_send_null_frame(ieee, 1);
336 rtllib_sta_ps_send_null_frame(ieee, 1);
337
338 rtllib_stop_all_queues(ieee);
339
340 if (ieee->data_hard_stop)
341 ieee->data_hard_stop(ieee->dev);
342 rtllib_stop_send_beacons(ieee);
343 ieee->state = RTLLIB_LINKED_SCANNING;
344 ieee->link_change(ieee->dev);
345 /* wait for ps packet to be kicked out successfully */
346 msleep(50);
347
348 if (ieee->ScanOperationBackupHandler)
349 ieee->ScanOperationBackupHandler(ieee->dev,SCAN_OPT_BACKUP);
350
351 if (ieee->pHTInfo->bCurrentHTSupport && ieee->pHTInfo->bEnableHT && ieee->pHTInfo->bCurBW40MHz) {
352 b40M = 1;
353 chan_offset = ieee->pHTInfo->CurSTAExtChnlOffset;
Larry Finger6e579112011-07-19 18:20:30 -0500354 bandwidth = (enum ht_channel_width)ieee->pHTInfo->bCurBW40MHz;
Larry Finger94a79942011-08-23 19:00:42 -0500355 RT_TRACE(COMP_DBG, "Scan in 40M, force to 20M first:%d, %d\n", chan_offset, bandwidth);
356 ieee->SetBWModeHandler(ieee->dev, HT_CHANNEL_WIDTH_20, HT_EXTCHNL_OFFSET_NO_EXT);
357 }
358
359 rtllib_start_scan_syncro(ieee, 0);
360
361 if (b40M) {
362 RT_TRACE(COMP_DBG, "Scan in 20M, back to 40M\n");
363 if (chan_offset == HT_EXTCHNL_OFFSET_UPPER)
364 ieee->set_chan(ieee->dev, chan + 2);
365 else if (chan_offset == HT_EXTCHNL_OFFSET_LOWER)
366 ieee->set_chan(ieee->dev, chan - 2);
367 else
368 ieee->set_chan(ieee->dev, chan);
369 ieee->SetBWModeHandler(ieee->dev, bandwidth, chan_offset);
370 } else {
371 ieee->set_chan(ieee->dev, chan);
372 }
373
374 if (ieee->ScanOperationBackupHandler)
375 ieee->ScanOperationBackupHandler(ieee->dev,SCAN_OPT_RESTORE);
376
377 ieee->state = RTLLIB_LINKED;
378 ieee->link_change(ieee->dev);
379
380 /* Notify AP that I wake up again */
381 rtllib_sta_ps_send_null_frame(ieee, 0);
382
383 if (ieee->LinkDetectInfo.NumRecvBcnInPeriod == 0 ||
384 ieee->LinkDetectInfo.NumRecvDataInPeriod == 0 ) {
385 ieee->LinkDetectInfo.NumRecvBcnInPeriod = 1;
386 ieee->LinkDetectInfo.NumRecvDataInPeriod= 1;
387 }
388
389 if (ieee->data_hard_resume)
390 ieee->data_hard_resume(ieee->dev);
391
392 if (ieee->iw_mode == IW_MODE_ADHOC || ieee->iw_mode == IW_MODE_MASTER)
393 rtllib_start_send_beacons(ieee);
394
395 rtllib_wake_all_queues(ieee);
396
397 count = 0;
398out:
399 up(&ieee->wx_sem);
400
401}
402
403int rtllib_wx_set_scan(struct rtllib_device *ieee, struct iw_request_info *a,
404 union iwreq_data *wrqu, char *b)
405{
406 int ret = 0;
407
408 down(&ieee->wx_sem);
409
410 if (ieee->iw_mode == IW_MODE_MONITOR || !(ieee->proto_started)){
411 ret = -1;
412 goto out;
413 }
414
415 if ( ieee->state == RTLLIB_LINKED){
416 queue_work_rsl(ieee->wq, &ieee->wx_sync_scan_wq);
417 /* intentionally forget to up sem */
418 return 0;
419 }
420
421out:
422 up(&ieee->wx_sem);
423 return ret;
424}
425
426int rtllib_wx_set_essid(struct rtllib_device *ieee,
427 struct iw_request_info *a,
428 union iwreq_data *wrqu, char *extra)
429{
430
431 int ret=0,len,i;
432 short proto_started;
433 unsigned long flags;
434
435 rtllib_stop_scan_syncro(ieee);
436 down(&ieee->wx_sem);
437
438 proto_started = ieee->proto_started;
439
Larry Finger94a79942011-08-23 19:00:42 -0500440 len = (wrqu->essid.length < IW_ESSID_MAX_SIZE) ? wrqu->essid.length : IW_ESSID_MAX_SIZE;
Larry Finger94a79942011-08-23 19:00:42 -0500441
442 if (len > IW_ESSID_MAX_SIZE){
443 ret= -E2BIG;
444 goto out;
445 }
446
447 if (ieee->iw_mode == IW_MODE_MONITOR){
448 ret= -1;
449 goto out;
450 }
451
452 for (i=0; i<len; i++){
453 if (extra[i] < 0){
454 ret= -1;
455 goto out;
456 }
457 }
458
459 if (proto_started)
460 rtllib_stop_protocol(ieee,true);
461
462
463 /* this is just to be sure that the GET wx callback
464 * has consisten infos. not needed otherwise
465 */
466 spin_lock_irqsave(&ieee->lock, flags);
467
468 if (wrqu->essid.flags && wrqu->essid.length) {
469 strncpy(ieee->current_network.ssid, extra, len);
470 ieee->current_network.ssid_len = len;
471 ieee->cannot_notify = false;
472 ieee->ssid_set = 1;
473 }
474 else{
475 ieee->ssid_set = 0;
476 ieee->current_network.ssid[0] = '\0';
477 ieee->current_network.ssid_len = 0;
478 }
479 spin_unlock_irqrestore(&ieee->lock, flags);
480
481 if (proto_started)
482 rtllib_start_protocol(ieee);
483out:
484 up(&ieee->wx_sem);
485 return ret;
486}
487
488 int rtllib_wx_get_mode(struct rtllib_device *ieee, struct iw_request_info *a,
489 union iwreq_data *wrqu, char *b)
490{
491 wrqu->mode = ieee->iw_mode;
492 return 0;
493}
494
495 int rtllib_wx_set_rawtx(struct rtllib_device *ieee,
496 struct iw_request_info *info,
497 union iwreq_data *wrqu, char *extra)
498{
499
500 int *parms = (int *)extra;
501 int enable = (parms[0] > 0);
502 short prev = ieee->raw_tx;
503
504 down(&ieee->wx_sem);
505
506 if (enable)
507 ieee->raw_tx = 1;
508 else
509 ieee->raw_tx = 0;
510
511 printk(KERN_INFO"raw TX is %s\n",
512 ieee->raw_tx ? "enabled" : "disabled");
513
514 if (ieee->iw_mode == IW_MODE_MONITOR)
515 {
516 if (prev == 0 && ieee->raw_tx){
517 if (ieee->data_hard_resume)
518 ieee->data_hard_resume(ieee->dev);
519
520 netif_carrier_on(ieee->dev);
521 }
522
523 if (prev && ieee->raw_tx == 1)
524 netif_carrier_off(ieee->dev);
525 }
526
527 up(&ieee->wx_sem);
528
529 return 0;
530}
531
532int rtllib_wx_get_name(struct rtllib_device *ieee,
533 struct iw_request_info *info,
534 union iwreq_data *wrqu, char *extra)
535{
536 strcpy(wrqu->name, "802.11");
537
538 if (ieee->modulation & RTLLIB_CCK_MODULATION)
539 strcat(wrqu->name, "b");
540 if (ieee->modulation & RTLLIB_OFDM_MODULATION)
541 strcat(wrqu->name, "g");
542 if (ieee->mode & (IEEE_N_24G | IEEE_N_5G))
543 strcat(wrqu->name, "n");
544 return 0;
545}
546
547
548/* this is mostly stolen from hostap */
549int rtllib_wx_set_power(struct rtllib_device *ieee,
550 struct iw_request_info *info,
551 union iwreq_data *wrqu, char *extra)
552{
553 int ret = 0;
Mike McCormack4f6807e2011-07-11 08:56:20 +0900554
Larry Finger94a79942011-08-23 19:00:42 -0500555 if (
556 (!ieee->sta_wake_up) ||
557 (!ieee->enter_sleep_state) ||
558 (!ieee->ps_is_queue_empty)){
559
560 RTLLIB_DEBUG(RTLLIB_DL_ERR,"%s(): PS mode is tryied to be use but driver missed a callback\n\n",__func__);
561
562 return -1;
563 }
Mike McCormack4f6807e2011-07-11 08:56:20 +0900564
Larry Finger94a79942011-08-23 19:00:42 -0500565 down(&ieee->wx_sem);
566
567 if (wrqu->power.disabled){
568 RT_TRACE(COMP_DBG, "===>%s(): power disable\n",__func__);
569 ieee->ps = RTLLIB_PS_DISABLED;
570 goto exit;
571 }
572 if (wrqu->power.flags & IW_POWER_TIMEOUT) {
573 ieee->ps_timeout = wrqu->power.value / 1000;
574 RT_TRACE(COMP_DBG, "===>%s():ps_timeout is %d\n",__func__,ieee->ps_timeout);
575 }
576
577 if (wrqu->power.flags & IW_POWER_PERIOD) {
578
579 ieee->ps_period = wrqu->power.value / 1000;
580
581 }
582 switch (wrqu->power.flags & IW_POWER_MODE) {
583 case IW_POWER_UNICAST_R:
584 ieee->ps = RTLLIB_PS_UNICAST;
585 break;
586 case IW_POWER_MULTICAST_R:
587 ieee->ps = RTLLIB_PS_MBCAST;
588 break;
589 case IW_POWER_ALL_R:
590 ieee->ps = RTLLIB_PS_UNICAST | RTLLIB_PS_MBCAST;
591 break;
592
593 case IW_POWER_ON:
594 break;
595
596 default:
597 ret = -EINVAL;
598 goto exit;
599
600 }
601exit:
602 up(&ieee->wx_sem);
603 return ret;
604
605}
606
607/* this is stolen from hostap */
608int rtllib_wx_get_power(struct rtllib_device *ieee,
609 struct iw_request_info *info,
610 union iwreq_data *wrqu, char *extra)
611{
612 int ret =0;
613
614 down(&ieee->wx_sem);
615
616 if (ieee->ps == RTLLIB_PS_DISABLED) {
617 wrqu->power.disabled = 1;
618 goto exit;
619 }
620
621 wrqu->power.disabled = 0;
622
623 if ((wrqu->power.flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
624 wrqu->power.flags = IW_POWER_TIMEOUT;
625 wrqu->power.value = ieee->ps_timeout * 1000;
626 } else {
627 wrqu->power.flags = IW_POWER_PERIOD;
628 wrqu->power.value = ieee->ps_period * 1000;
629 }
630
631 if ((ieee->ps & (RTLLIB_PS_MBCAST | RTLLIB_PS_UNICAST)) == (RTLLIB_PS_MBCAST | RTLLIB_PS_UNICAST))
632 wrqu->power.flags |= IW_POWER_ALL_R;
633 else if (ieee->ps & RTLLIB_PS_MBCAST)
634 wrqu->power.flags |= IW_POWER_MULTICAST_R;
635 else
636 wrqu->power.flags |= IW_POWER_UNICAST_R;
637
638exit:
639 up(&ieee->wx_sem);
640 return ret;
641
642}