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