linux/drivers/net/wireless/ralink/rt2x00/rt2x00link.c
<<
>>
Prefs
   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, see <http://www.gnu.org/licenses/>.
  17 */
  18
  19/*
  20        Module: rt2x00lib
  21        Abstract: rt2x00 generic link tuning routines.
  22 */
  23
  24#include <linux/kernel.h>
  25#include <linux/module.h>
  26
  27#include "rt2x00.h"
  28#include "rt2x00lib.h"
  29
  30/*
  31 * When we lack RSSI information return something less then -80 to
  32 * tell the driver to tune the device to maximum sensitivity.
  33 */
  34#define DEFAULT_RSSI            -128
  35
  36static inline int rt2x00link_get_avg_rssi(struct ewma_rssi *ewma)
  37{
  38        unsigned long avg;
  39
  40        avg = ewma_rssi_read(ewma);
  41        if (avg)
  42                return -avg;
  43
  44        return DEFAULT_RSSI;
  45}
  46
  47static int rt2x00link_antenna_get_link_rssi(struct rt2x00_dev *rt2x00dev)
  48{
  49        struct link_ant *ant = &rt2x00dev->link.ant;
  50
  51        if (rt2x00dev->link.qual.rx_success)
  52                return rt2x00link_get_avg_rssi(&ant->rssi_ant);
  53
  54        return DEFAULT_RSSI;
  55}
  56
  57static int rt2x00link_antenna_get_rssi_history(struct rt2x00_dev *rt2x00dev)
  58{
  59        struct link_ant *ant = &rt2x00dev->link.ant;
  60
  61        if (ant->rssi_history)
  62                return ant->rssi_history;
  63        return DEFAULT_RSSI;
  64}
  65
  66static void rt2x00link_antenna_update_rssi_history(struct rt2x00_dev *rt2x00dev,
  67                                                   int rssi)
  68{
  69        struct link_ant *ant = &rt2x00dev->link.ant;
  70        ant->rssi_history = rssi;
  71}
  72
  73static void rt2x00link_antenna_reset(struct rt2x00_dev *rt2x00dev)
  74{
  75        ewma_rssi_init(&rt2x00dev->link.ant.rssi_ant);
  76}
  77
  78static void rt2x00lib_antenna_diversity_sample(struct rt2x00_dev *rt2x00dev)
  79{
  80        struct link_ant *ant = &rt2x00dev->link.ant;
  81        struct antenna_setup new_ant;
  82        int other_antenna;
  83
  84        int sample_current = rt2x00link_antenna_get_link_rssi(rt2x00dev);
  85        int sample_other = rt2x00link_antenna_get_rssi_history(rt2x00dev);
  86
  87        memcpy(&new_ant, &ant->active, sizeof(new_ant));
  88
  89        /*
  90         * We are done sampling. Now we should evaluate the results.
  91         */
  92        ant->flags &= ~ANTENNA_MODE_SAMPLE;
  93
  94        /*
  95         * During the last period we have sampled the RSSI
  96         * from both antennas. It now is time to determine
  97         * which antenna demonstrated the best performance.
  98         * When we are already on the antenna with the best
  99         * performance, just create a good starting point
 100         * for the history and we are done.
 101         */
 102        if (sample_current >= sample_other) {
 103                rt2x00link_antenna_update_rssi_history(rt2x00dev,
 104                        sample_current);
 105                return;
 106        }
 107
 108        other_antenna = (ant->active.rx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A;
 109
 110        if (ant->flags & ANTENNA_RX_DIVERSITY)
 111                new_ant.rx = other_antenna;
 112
 113        if (ant->flags & ANTENNA_TX_DIVERSITY)
 114                new_ant.tx = other_antenna;
 115
 116        rt2x00lib_config_antenna(rt2x00dev, new_ant);
 117}
 118
 119static void rt2x00lib_antenna_diversity_eval(struct rt2x00_dev *rt2x00dev)
 120{
 121        struct link_ant *ant = &rt2x00dev->link.ant;
 122        struct antenna_setup new_ant;
 123        int rssi_curr;
 124        int rssi_old;
 125
 126        memcpy(&new_ant, &ant->active, sizeof(new_ant));
 127
 128        /*
 129         * Get current RSSI value along with the historical value,
 130         * after that update the history with the current value.
 131         */
 132        rssi_curr = rt2x00link_antenna_get_link_rssi(rt2x00dev);
 133        rssi_old = rt2x00link_antenna_get_rssi_history(rt2x00dev);
 134        rt2x00link_antenna_update_rssi_history(rt2x00dev, rssi_curr);
 135
 136        /*
 137         * Legacy driver indicates that we should swap antenna's
 138         * when the difference in RSSI is greater that 5. This
 139         * also should be done when the RSSI was actually better
 140         * then the previous sample.
 141         * When the difference exceeds the threshold we should
 142         * sample the rssi from the other antenna to make a valid
 143         * comparison between the 2 antennas.
 144         */
 145        if (abs(rssi_curr - rssi_old) < 5)
 146                return;
 147
 148        ant->flags |= ANTENNA_MODE_SAMPLE;
 149
 150        if (ant->flags & ANTENNA_RX_DIVERSITY)
 151                new_ant.rx = (new_ant.rx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A;
 152
 153        if (ant->flags & ANTENNA_TX_DIVERSITY)
 154                new_ant.tx = (new_ant.tx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A;
 155
 156        rt2x00lib_config_antenna(rt2x00dev, new_ant);
 157}
 158
 159static bool rt2x00lib_antenna_diversity(struct rt2x00_dev *rt2x00dev)
 160{
 161        struct link_ant *ant = &rt2x00dev->link.ant;
 162
 163        /*
 164         * Determine if software diversity is enabled for
 165         * either the TX or RX antenna (or both).
 166         */
 167        if (!(ant->flags & ANTENNA_RX_DIVERSITY) &&
 168            !(ant->flags & ANTENNA_TX_DIVERSITY)) {
 169                ant->flags = 0;
 170                return true;
 171        }
 172
 173        /*
 174         * If we have only sampled the data over the last period
 175         * we should now harvest the data. Otherwise just evaluate
 176         * the data. The latter should only be performed once
 177         * every 2 seconds.
 178         */
 179        if (ant->flags & ANTENNA_MODE_SAMPLE) {
 180                rt2x00lib_antenna_diversity_sample(rt2x00dev);
 181                return true;
 182        } else if (rt2x00dev->link.count & 1) {
 183                rt2x00lib_antenna_diversity_eval(rt2x00dev);
 184                return true;
 185        }
 186
 187        return false;
 188}
 189
 190void rt2x00link_update_stats(struct rt2x00_dev *rt2x00dev,
 191                             struct sk_buff *skb,
 192                             struct rxdone_entry_desc *rxdesc)
 193{
 194        struct link *link = &rt2x00dev->link;
 195        struct link_qual *qual = &rt2x00dev->link.qual;
 196        struct link_ant *ant = &rt2x00dev->link.ant;
 197        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 198
 199        /*
 200         * No need to update the stats for !=STA interfaces
 201         */
 202        if (!rt2x00dev->intf_sta_count)
 203                return;
 204
 205        /*
 206         * Frame was received successfully since non-succesfull
 207         * frames would have been dropped by the hardware.
 208         */
 209        qual->rx_success++;
 210
 211        /*
 212         * We are only interested in quality statistics from
 213         * beacons which came from the BSS which we are
 214         * associated with.
 215         */
 216        if (!ieee80211_is_beacon(hdr->frame_control) ||
 217            !(rxdesc->dev_flags & RXDONE_MY_BSS))
 218                return;
 219
 220        /*
 221         * Update global RSSI
 222         */
 223        ewma_rssi_add(&link->avg_rssi, -rxdesc->rssi);
 224
 225        /*
 226         * Update antenna RSSI
 227         */
 228        ewma_rssi_add(&ant->rssi_ant, -rxdesc->rssi);
 229}
 230
 231void rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev)
 232{
 233        struct link *link = &rt2x00dev->link;
 234
 235        /*
 236         * Link tuning should only be performed when
 237         * an active sta interface exists. AP interfaces
 238         * don't need link tuning and monitor mode interfaces
 239         * should never have to work with link tuners.
 240         */
 241        if (!rt2x00dev->intf_sta_count)
 242                return;
 243
 244        /**
 245         * While scanning, link tuning is disabled. By default
 246         * the most sensitive settings will be used to make sure
 247         * that all beacons and probe responses will be received
 248         * during the scan.
 249         */
 250        if (test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags))
 251                return;
 252
 253        rt2x00link_reset_tuner(rt2x00dev, false);
 254
 255        if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
 256                ieee80211_queue_delayed_work(rt2x00dev->hw,
 257                                             &link->work, LINK_TUNE_INTERVAL);
 258}
 259
 260void rt2x00link_stop_tuner(struct rt2x00_dev *rt2x00dev)
 261{
 262        cancel_delayed_work_sync(&rt2x00dev->link.work);
 263}
 264
 265void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna)
 266{
 267        struct link_qual *qual = &rt2x00dev->link.qual;
 268        u8 vgc_level = qual->vgc_level_reg;
 269
 270        if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
 271                return;
 272
 273        /*
 274         * Reset link information.
 275         * Both the currently active vgc level as well as
 276         * the link tuner counter should be reset. Resetting
 277         * the counter is important for devices where the
 278         * device should only perform link tuning during the
 279         * first minute after being enabled.
 280         */
 281        rt2x00dev->link.count = 0;
 282        memset(qual, 0, sizeof(*qual));
 283        ewma_rssi_init(&rt2x00dev->link.avg_rssi);
 284
 285        /*
 286         * Restore the VGC level as stored in the registers,
 287         * the driver can use this to determine if the register
 288         * must be updated during reset or not.
 289         */
 290        qual->vgc_level_reg = vgc_level;
 291
 292        /*
 293         * Reset the link tuner.
 294         */
 295        rt2x00dev->ops->lib->reset_tuner(rt2x00dev, qual);
 296
 297        if (antenna)
 298                rt2x00link_antenna_reset(rt2x00dev);
 299}
 300
 301static void rt2x00link_reset_qual(struct rt2x00_dev *rt2x00dev)
 302{
 303        struct link_qual *qual = &rt2x00dev->link.qual;
 304
 305        qual->rx_success = 0;
 306        qual->rx_failed = 0;
 307        qual->tx_success = 0;
 308        qual->tx_failed = 0;
 309}
 310
 311static void rt2x00link_tuner(struct work_struct *work)
 312{
 313        struct rt2x00_dev *rt2x00dev =
 314            container_of(work, struct rt2x00_dev, link.work.work);
 315        struct link *link = &rt2x00dev->link;
 316        struct link_qual *qual = &rt2x00dev->link.qual;
 317
 318        /*
 319         * When the radio is shutting down we should
 320         * immediately cease all link tuning.
 321         */
 322        if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags) ||
 323            test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags))
 324                return;
 325
 326        /*
 327         * Update statistics.
 328         */
 329        rt2x00dev->ops->lib->link_stats(rt2x00dev, qual);
 330        rt2x00dev->low_level_stats.dot11FCSErrorCount += qual->rx_failed;
 331
 332        /*
 333         * Update quality RSSI for link tuning,
 334         * when we have received some frames and we managed to
 335         * collect the RSSI data we could use this. Otherwise we
 336         * must fallback to the default RSSI value.
 337         */
 338        if (!qual->rx_success)
 339                qual->rssi = DEFAULT_RSSI;
 340        else
 341                qual->rssi = rt2x00link_get_avg_rssi(&link->avg_rssi);
 342
 343        /*
 344         * Check if link tuning is supported by the hardware, some hardware
 345         * do not support link tuning at all, while other devices can disable
 346         * the feature from the EEPROM.
 347         */
 348        if (rt2x00_has_cap_link_tuning(rt2x00dev))
 349                rt2x00dev->ops->lib->link_tuner(rt2x00dev, qual, link->count);
 350
 351        /*
 352         * Send a signal to the led to update the led signal strength.
 353         */
 354        rt2x00leds_led_quality(rt2x00dev, qual->rssi);
 355
 356        /*
 357         * Evaluate antenna setup, make this the last step when
 358         * rt2x00lib_antenna_diversity made changes the quality
 359         * statistics will be reset.
 360         */
 361        if (rt2x00lib_antenna_diversity(rt2x00dev))
 362                rt2x00link_reset_qual(rt2x00dev);
 363
 364        /*
 365         * Increase tuner counter, and reschedule the next link tuner run.
 366         */
 367        link->count++;
 368
 369        if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
 370                ieee80211_queue_delayed_work(rt2x00dev->hw,
 371                                             &link->work, LINK_TUNE_INTERVAL);
 372}
 373
 374void rt2x00link_start_watchdog(struct rt2x00_dev *rt2x00dev)
 375{
 376        struct link *link = &rt2x00dev->link;
 377
 378        if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) &&
 379            rt2x00dev->ops->lib->watchdog)
 380                ieee80211_queue_delayed_work(rt2x00dev->hw,
 381                                             &link->watchdog_work,
 382                                             WATCHDOG_INTERVAL);
 383}
 384
 385void rt2x00link_stop_watchdog(struct rt2x00_dev *rt2x00dev)
 386{
 387        cancel_delayed_work_sync(&rt2x00dev->link.watchdog_work);
 388}
 389
 390static void rt2x00link_watchdog(struct work_struct *work)
 391{
 392        struct rt2x00_dev *rt2x00dev =
 393            container_of(work, struct rt2x00_dev, link.watchdog_work.work);
 394        struct link *link = &rt2x00dev->link;
 395
 396        /*
 397         * When the radio is shutting down we should
 398         * immediately cease the watchdog monitoring.
 399         */
 400        if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
 401                return;
 402
 403        rt2x00dev->ops->lib->watchdog(rt2x00dev);
 404
 405        if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
 406                ieee80211_queue_delayed_work(rt2x00dev->hw,
 407                                             &link->watchdog_work,
 408                                             WATCHDOG_INTERVAL);
 409}
 410
 411void rt2x00link_start_agc(struct rt2x00_dev *rt2x00dev)
 412{
 413        struct link *link = &rt2x00dev->link;
 414
 415        if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) &&
 416            rt2x00dev->ops->lib->gain_calibration)
 417                ieee80211_queue_delayed_work(rt2x00dev->hw,
 418                                             &link->agc_work,
 419                                             AGC_INTERVAL);
 420}
 421
 422void rt2x00link_start_vcocal(struct rt2x00_dev *rt2x00dev)
 423{
 424        struct link *link = &rt2x00dev->link;
 425
 426        if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) &&
 427            rt2x00dev->ops->lib->vco_calibration)
 428                ieee80211_queue_delayed_work(rt2x00dev->hw,
 429                                             &link->vco_work,
 430                                             VCO_INTERVAL);
 431}
 432
 433void rt2x00link_stop_agc(struct rt2x00_dev *rt2x00dev)
 434{
 435        cancel_delayed_work_sync(&rt2x00dev->link.agc_work);
 436}
 437
 438void rt2x00link_stop_vcocal(struct rt2x00_dev *rt2x00dev)
 439{
 440        cancel_delayed_work_sync(&rt2x00dev->link.vco_work);
 441}
 442
 443static void rt2x00link_agc(struct work_struct *work)
 444{
 445        struct rt2x00_dev *rt2x00dev =
 446            container_of(work, struct rt2x00_dev, link.agc_work.work);
 447        struct link *link = &rt2x00dev->link;
 448
 449        /*
 450         * When the radio is shutting down we should
 451         * immediately cease the watchdog monitoring.
 452         */
 453        if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
 454                return;
 455
 456        rt2x00dev->ops->lib->gain_calibration(rt2x00dev);
 457
 458        if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
 459                ieee80211_queue_delayed_work(rt2x00dev->hw,
 460                                             &link->agc_work,
 461                                             AGC_INTERVAL);
 462}
 463
 464static void rt2x00link_vcocal(struct work_struct *work)
 465{
 466        struct rt2x00_dev *rt2x00dev =
 467            container_of(work, struct rt2x00_dev, link.vco_work.work);
 468        struct link *link = &rt2x00dev->link;
 469
 470        /*
 471         * When the radio is shutting down we should
 472         * immediately cease the VCO calibration.
 473         */
 474        if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
 475                return;
 476
 477        rt2x00dev->ops->lib->vco_calibration(rt2x00dev);
 478
 479        if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
 480                ieee80211_queue_delayed_work(rt2x00dev->hw,
 481                                             &link->vco_work,
 482                                             VCO_INTERVAL);
 483}
 484
 485void rt2x00link_register(struct rt2x00_dev *rt2x00dev)
 486{
 487        INIT_DELAYED_WORK(&rt2x00dev->link.agc_work, rt2x00link_agc);
 488        if (rt2x00_has_cap_vco_recalibration(rt2x00dev))
 489                INIT_DELAYED_WORK(&rt2x00dev->link.vco_work, rt2x00link_vcocal);
 490        INIT_DELAYED_WORK(&rt2x00dev->link.watchdog_work, rt2x00link_watchdog);
 491        INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00link_tuner);
 492}
 493