Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
rt2x00link.c
Go to the documentation of this file.
1 /*
2  Copyright (C) 2004 - 2009 Ivo van Doorn <[email protected]>
3  <http://rt2x00.serialmonkey.com>
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program; if not, write to the
17  Free Software Foundation, Inc.,
18  59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20 
21 /*
22  Module: rt2x00lib
23  Abstract: rt2x00 generic link tuning routines.
24  */
25 
26 #include <linux/kernel.h>
27 #include <linux/module.h>
28 
29 #include "rt2x00.h"
30 #include "rt2x00lib.h"
31 
32 /*
33  * When we lack RSSI information return something less then -80 to
34  * tell the driver to tune the device to maximum sensitivity.
35  */
36 #define DEFAULT_RSSI -128
37 
38 /*
39  * Helper struct and macro to work with moving/walking averages.
40  * When adding a value to the average value the following calculation
41  * is needed:
42  *
43  * avg_rssi = ((avg_rssi * 7) + rssi) / 8;
44  *
45  * The advantage of this approach is that we only need 1 variable
46  * to store the average in (No need for a count and a total).
47  * But more importantly, normal average values will over time
48  * move less and less towards newly added values this results
49  * that with link tuning, the device can have a very good RSSI
50  * for a few minutes but when the device is moved away from the AP
51  * the average will not decrease fast enough to compensate.
52  * The walking average compensates this and will move towards
53  * the new values correctly allowing a effective link tuning,
54  * the speed of the average moving towards other values depends
55  * on the value for the number of samples. The higher the number
56  * of samples, the slower the average will move.
57  * We use two variables to keep track of the average value to
58  * compensate for the rounding errors. This can be a significant
59  * error (>5dBm) if the factor is too low.
60  */
61 #define AVG_SAMPLES 8
62 #define AVG_FACTOR 1000
63 #define MOVING_AVERAGE(__avg, __val) \
64 ({ \
65  struct avg_val __new; \
66  __new.avg_weight = \
67  (__avg).avg_weight ? \
68  ((((__avg).avg_weight * ((AVG_SAMPLES) - 1)) + \
69  ((__val) * (AVG_FACTOR))) / \
70  (AVG_SAMPLES)) : \
71  ((__val) * (AVG_FACTOR)); \
72  __new.avg = __new.avg_weight / (AVG_FACTOR); \
73  __new; \
74 })
75 
76 static int rt2x00link_antenna_get_link_rssi(struct rt2x00_dev *rt2x00dev)
77 {
78  struct link_ant *ant = &rt2x00dev->link.ant;
79 
80  if (ant->rssi_ant.avg && rt2x00dev->link.qual.rx_success)
81  return ant->rssi_ant.avg;
82  return DEFAULT_RSSI;
83 }
84 
85 static int rt2x00link_antenna_get_rssi_history(struct rt2x00_dev *rt2x00dev)
86 {
87  struct link_ant *ant = &rt2x00dev->link.ant;
88 
89  if (ant->rssi_history)
90  return ant->rssi_history;
91  return DEFAULT_RSSI;
92 }
93 
94 static void rt2x00link_antenna_update_rssi_history(struct rt2x00_dev *rt2x00dev,
95  int rssi)
96 {
97  struct link_ant *ant = &rt2x00dev->link.ant;
98  ant->rssi_history = rssi;
99 }
100 
101 static void rt2x00link_antenna_reset(struct rt2x00_dev *rt2x00dev)
102 {
103  rt2x00dev->link.ant.rssi_ant.avg = 0;
104  rt2x00dev->link.ant.rssi_ant.avg_weight = 0;
105 }
106 
107 static void rt2x00lib_antenna_diversity_sample(struct rt2x00_dev *rt2x00dev)
108 {
109  struct link_ant *ant = &rt2x00dev->link.ant;
110  struct antenna_setup new_ant;
111  int other_antenna;
112 
113  int sample_current = rt2x00link_antenna_get_link_rssi(rt2x00dev);
114  int sample_other = rt2x00link_antenna_get_rssi_history(rt2x00dev);
115 
116  memcpy(&new_ant, &ant->active, sizeof(new_ant));
117 
118  /*
119  * We are done sampling. Now we should evaluate the results.
120  */
121  ant->flags &= ~ANTENNA_MODE_SAMPLE;
122 
123  /*
124  * During the last period we have sampled the RSSI
125  * from both antennas. It now is time to determine
126  * which antenna demonstrated the best performance.
127  * When we are already on the antenna with the best
128  * performance, just create a good starting point
129  * for the history and we are done.
130  */
131  if (sample_current >= sample_other) {
132  rt2x00link_antenna_update_rssi_history(rt2x00dev,
133  sample_current);
134  return;
135  }
136 
137  other_antenna = (ant->active.rx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A;
138 
139  if (ant->flags & ANTENNA_RX_DIVERSITY)
140  new_ant.rx = other_antenna;
141 
142  if (ant->flags & ANTENNA_TX_DIVERSITY)
143  new_ant.tx = other_antenna;
144 
145  rt2x00lib_config_antenna(rt2x00dev, new_ant);
146 }
147 
148 static void rt2x00lib_antenna_diversity_eval(struct rt2x00_dev *rt2x00dev)
149 {
150  struct link_ant *ant = &rt2x00dev->link.ant;
151  struct antenna_setup new_ant;
152  int rssi_curr;
153  int rssi_old;
154 
155  memcpy(&new_ant, &ant->active, sizeof(new_ant));
156 
157  /*
158  * Get current RSSI value along with the historical value,
159  * after that update the history with the current value.
160  */
161  rssi_curr = rt2x00link_antenna_get_link_rssi(rt2x00dev);
162  rssi_old = rt2x00link_antenna_get_rssi_history(rt2x00dev);
163  rt2x00link_antenna_update_rssi_history(rt2x00dev, rssi_curr);
164 
165  /*
166  * Legacy driver indicates that we should swap antenna's
167  * when the difference in RSSI is greater that 5. This
168  * also should be done when the RSSI was actually better
169  * then the previous sample.
170  * When the difference exceeds the threshold we should
171  * sample the rssi from the other antenna to make a valid
172  * comparison between the 2 antennas.
173  */
174  if (abs(rssi_curr - rssi_old) < 5)
175  return;
176 
177  ant->flags |= ANTENNA_MODE_SAMPLE;
178 
179  if (ant->flags & ANTENNA_RX_DIVERSITY)
180  new_ant.rx = (new_ant.rx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A;
181 
182  if (ant->flags & ANTENNA_TX_DIVERSITY)
183  new_ant.tx = (new_ant.tx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A;
184 
185  rt2x00lib_config_antenna(rt2x00dev, new_ant);
186 }
187 
188 static bool rt2x00lib_antenna_diversity(struct rt2x00_dev *rt2x00dev)
189 {
190  struct link_ant *ant = &rt2x00dev->link.ant;
191 
192  /*
193  * Determine if software diversity is enabled for
194  * either the TX or RX antenna (or both).
195  */
196  if (!(ant->flags & ANTENNA_RX_DIVERSITY) &&
197  !(ant->flags & ANTENNA_TX_DIVERSITY)) {
198  ant->flags = 0;
199  return true;
200  }
201 
202  /*
203  * If we have only sampled the data over the last period
204  * we should now harvest the data. Otherwise just evaluate
205  * the data. The latter should only be performed once
206  * every 2 seconds.
207  */
208  if (ant->flags & ANTENNA_MODE_SAMPLE) {
209  rt2x00lib_antenna_diversity_sample(rt2x00dev);
210  return true;
211  } else if (rt2x00dev->link.count & 1) {
212  rt2x00lib_antenna_diversity_eval(rt2x00dev);
213  return true;
214  }
215 
216  return false;
217 }
218 
219 void rt2x00link_update_stats(struct rt2x00_dev *rt2x00dev,
220  struct sk_buff *skb,
221  struct rxdone_entry_desc *rxdesc)
222 {
223  struct link *link = &rt2x00dev->link;
224  struct link_qual *qual = &rt2x00dev->link.qual;
225  struct link_ant *ant = &rt2x00dev->link.ant;
226  struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
227 
228  /*
229  * No need to update the stats for !=STA interfaces
230  */
231  if (!rt2x00dev->intf_sta_count)
232  return;
233 
234  /*
235  * Frame was received successfully since non-succesfull
236  * frames would have been dropped by the hardware.
237  */
238  qual->rx_success++;
239 
240  /*
241  * We are only interested in quality statistics from
242  * beacons which came from the BSS which we are
243  * associated with.
244  */
245  if (!ieee80211_is_beacon(hdr->frame_control) ||
246  !(rxdesc->dev_flags & RXDONE_MY_BSS))
247  return;
248 
249  /*
250  * Update global RSSI
251  */
252  link->avg_rssi = MOVING_AVERAGE(link->avg_rssi, rxdesc->rssi);
253 
254  /*
255  * Update antenna RSSI
256  */
257  ant->rssi_ant = MOVING_AVERAGE(ant->rssi_ant, rxdesc->rssi);
258 }
259 
260 void rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev)
261 {
262  struct link *link = &rt2x00dev->link;
263 
264  /*
265  * Link tuning should only be performed when
266  * an active sta interface exists. AP interfaces
267  * don't need link tuning and monitor mode interfaces
268  * should never have to work with link tuners.
269  */
270  if (!rt2x00dev->intf_sta_count)
271  return;
272 
279  if (test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags))
280  return;
281 
282  rt2x00link_reset_tuner(rt2x00dev, false);
283 
284  if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
285  ieee80211_queue_delayed_work(rt2x00dev->hw,
286  &link->work, LINK_TUNE_INTERVAL);
287 }
288 
289 void rt2x00link_stop_tuner(struct rt2x00_dev *rt2x00dev)
290 {
291  cancel_delayed_work_sync(&rt2x00dev->link.work);
292 }
293 
294 void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna)
295 {
296  struct link_qual *qual = &rt2x00dev->link.qual;
297  u8 vgc_level = qual->vgc_level_reg;
298 
299  if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
300  return;
301 
302  /*
303  * Reset link information.
304  * Both the currently active vgc level as well as
305  * the link tuner counter should be reset. Resetting
306  * the counter is important for devices where the
307  * device should only perform link tuning during the
308  * first minute after being enabled.
309  */
310  rt2x00dev->link.count = 0;
311  memset(qual, 0, sizeof(*qual));
312 
313  /*
314  * Restore the VGC level as stored in the registers,
315  * the driver can use this to determine if the register
316  * must be updated during reset or not.
317  */
318  qual->vgc_level_reg = vgc_level;
319 
320  /*
321  * Reset the link tuner.
322  */
323  rt2x00dev->ops->lib->reset_tuner(rt2x00dev, qual);
324 
325  if (antenna)
326  rt2x00link_antenna_reset(rt2x00dev);
327 }
328 
329 static void rt2x00link_reset_qual(struct rt2x00_dev *rt2x00dev)
330 {
331  struct link_qual *qual = &rt2x00dev->link.qual;
332 
333  qual->rx_success = 0;
334  qual->rx_failed = 0;
335  qual->tx_success = 0;
336  qual->tx_failed = 0;
337 }
338 
339 static void rt2x00link_tuner(struct work_struct *work)
340 {
341  struct rt2x00_dev *rt2x00dev =
342  container_of(work, struct rt2x00_dev, link.work.work);
343  struct link *link = &rt2x00dev->link;
344  struct link_qual *qual = &rt2x00dev->link.qual;
345 
346  /*
347  * When the radio is shutting down we should
348  * immediately cease all link tuning.
349  */
350  if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags) ||
351  test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags))
352  return;
353 
354  /*
355  * Update statistics.
356  */
357  rt2x00dev->ops->lib->link_stats(rt2x00dev, qual);
358  rt2x00dev->low_level_stats.dot11FCSErrorCount += qual->rx_failed;
359 
360  /*
361  * Update quality RSSI for link tuning,
362  * when we have received some frames and we managed to
363  * collect the RSSI data we could use this. Otherwise we
364  * must fallback to the default RSSI value.
365  */
366  if (!link->avg_rssi.avg || !qual->rx_success)
367  qual->rssi = DEFAULT_RSSI;
368  else
369  qual->rssi = link->avg_rssi.avg;
370 
371  /*
372  * Check if link tuning is supported by the hardware, some hardware
373  * do not support link tuning at all, while other devices can disable
374  * the feature from the EEPROM.
375  */
376  if (test_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags))
377  rt2x00dev->ops->lib->link_tuner(rt2x00dev, qual, link->count);
378 
379  /*
380  * Send a signal to the led to update the led signal strength.
381  */
382  rt2x00leds_led_quality(rt2x00dev, qual->rssi);
383 
384  /*
385  * Evaluate antenna setup, make this the last step when
386  * rt2x00lib_antenna_diversity made changes the quality
387  * statistics will be reset.
388  */
389  if (rt2x00lib_antenna_diversity(rt2x00dev))
390  rt2x00link_reset_qual(rt2x00dev);
391 
392  /*
393  * Increase tuner counter, and reschedule the next link tuner run.
394  */
395  link->count++;
396 
397  if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
398  ieee80211_queue_delayed_work(rt2x00dev->hw,
399  &link->work, LINK_TUNE_INTERVAL);
400 }
401 
402 void rt2x00link_start_watchdog(struct rt2x00_dev *rt2x00dev)
403 {
404  struct link *link = &rt2x00dev->link;
405 
406  if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) &&
407  rt2x00dev->ops->lib->watchdog)
408  ieee80211_queue_delayed_work(rt2x00dev->hw,
409  &link->watchdog_work,
411 }
412 
413 void rt2x00link_stop_watchdog(struct rt2x00_dev *rt2x00dev)
414 {
415  cancel_delayed_work_sync(&rt2x00dev->link.watchdog_work);
416 }
417 
418 static void rt2x00link_watchdog(struct work_struct *work)
419 {
420  struct rt2x00_dev *rt2x00dev =
421  container_of(work, struct rt2x00_dev, link.watchdog_work.work);
422  struct link *link = &rt2x00dev->link;
423 
424  /*
425  * When the radio is shutting down we should
426  * immediately cease the watchdog monitoring.
427  */
428  if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
429  return;
430 
431  rt2x00dev->ops->lib->watchdog(rt2x00dev);
432 
433  if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
434  ieee80211_queue_delayed_work(rt2x00dev->hw,
435  &link->watchdog_work,
437 }
438 
439 void rt2x00link_start_agc(struct rt2x00_dev *rt2x00dev)
440 {
441  struct link *link = &rt2x00dev->link;
442 
443  if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) &&
444  rt2x00dev->ops->lib->gain_calibration)
445  ieee80211_queue_delayed_work(rt2x00dev->hw,
446  &link->agc_work,
447  AGC_INTERVAL);
448 }
449 
450 void rt2x00link_start_vcocal(struct rt2x00_dev *rt2x00dev)
451 {
452  struct link *link = &rt2x00dev->link;
453 
454  if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) &&
455  rt2x00dev->ops->lib->vco_calibration)
456  ieee80211_queue_delayed_work(rt2x00dev->hw,
457  &link->vco_work,
458  VCO_INTERVAL);
459 }
460 
461 void rt2x00link_stop_agc(struct rt2x00_dev *rt2x00dev)
462 {
463  cancel_delayed_work_sync(&rt2x00dev->link.agc_work);
464 }
465 
466 void rt2x00link_stop_vcocal(struct rt2x00_dev *rt2x00dev)
467 {
468  cancel_delayed_work_sync(&rt2x00dev->link.vco_work);
469 }
470 
471 static void rt2x00link_agc(struct work_struct *work)
472 {
473  struct rt2x00_dev *rt2x00dev =
474  container_of(work, struct rt2x00_dev, link.agc_work.work);
475  struct link *link = &rt2x00dev->link;
476 
477  /*
478  * When the radio is shutting down we should
479  * immediately cease the watchdog monitoring.
480  */
481  if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
482  return;
483 
484  rt2x00dev->ops->lib->gain_calibration(rt2x00dev);
485 
486  if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
487  ieee80211_queue_delayed_work(rt2x00dev->hw,
488  &link->agc_work,
489  AGC_INTERVAL);
490 }
491 
492 static void rt2x00link_vcocal(struct work_struct *work)
493 {
494  struct rt2x00_dev *rt2x00dev =
495  container_of(work, struct rt2x00_dev, link.vco_work.work);
496  struct link *link = &rt2x00dev->link;
497 
498  /*
499  * When the radio is shutting down we should
500  * immediately cease the VCO calibration.
501  */
502  if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
503  return;
504 
505  rt2x00dev->ops->lib->vco_calibration(rt2x00dev);
506 
507  if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
508  ieee80211_queue_delayed_work(rt2x00dev->hw,
509  &link->vco_work,
510  VCO_INTERVAL);
511 }
512 
513 void rt2x00link_register(struct rt2x00_dev *rt2x00dev)
514 {
515  INIT_DELAYED_WORK(&rt2x00dev->link.agc_work, rt2x00link_agc);
517  INIT_DELAYED_WORK(&rt2x00dev->link.vco_work, rt2x00link_vcocal);
518  INIT_DELAYED_WORK(&rt2x00dev->link.watchdog_work, rt2x00link_watchdog);
519  INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00link_tuner);
520 }