Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.5 520 lines 15 kB view raw
1/* 2 Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> 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 76static 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 85static 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 94static 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 101static 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 107static 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 148static 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 188static 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 219void 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 260void 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 273 /** 274 * While scanning, link tuning is disabled. By default 275 * the most sensitive settings will be used to make sure 276 * that all beacons and probe responses will be received 277 * during the scan. 278 */ 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 289void rt2x00link_stop_tuner(struct rt2x00_dev *rt2x00dev) 290{ 291 cancel_delayed_work_sync(&rt2x00dev->link.work); 292} 293 294void 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 329static 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 339static 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 402void 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, 410 WATCHDOG_INTERVAL); 411} 412 413void rt2x00link_stop_watchdog(struct rt2x00_dev *rt2x00dev) 414{ 415 cancel_delayed_work_sync(&rt2x00dev->link.watchdog_work); 416} 417 418static 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, 436 WATCHDOG_INTERVAL); 437} 438 439void 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 450void 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 461void rt2x00link_stop_agc(struct rt2x00_dev *rt2x00dev) 462{ 463 cancel_delayed_work_sync(&rt2x00dev->link.agc_work); 464} 465 466void rt2x00link_stop_vcocal(struct rt2x00_dev *rt2x00dev) 467{ 468 cancel_delayed_work_sync(&rt2x00dev->link.vco_work); 469} 470 471static 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 492static 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 513void rt2x00link_register(struct rt2x00_dev *rt2x00dev) 514{ 515 INIT_DELAYED_WORK(&rt2x00dev->link.agc_work, rt2x00link_agc); 516 if (test_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags)) 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}