429 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>4 <http://rt2x00.serialmonkey.com>5 6 */7 8/*9 Module: rt2x00lib10 Abstract: rt2x00 generic link tuning routines.11 */12 13#include <linux/kernel.h>14#include <linux/module.h>15 16#include "rt2x00.h"17#include "rt2x00lib.h"18 19/*20 * When we lack RSSI information return something less then -80 to21 * tell the driver to tune the device to maximum sensitivity.22 */23#define DEFAULT_RSSI -12824 25static inline int rt2x00link_get_avg_rssi(struct ewma_rssi *ewma)26{27 unsigned long avg;28 29 avg = ewma_rssi_read(ewma);30 if (avg)31 return -avg;32 33 return DEFAULT_RSSI;34}35 36static int rt2x00link_antenna_get_link_rssi(struct rt2x00_dev *rt2x00dev)37{38 struct link_ant *ant = &rt2x00dev->link.ant;39 40 if (rt2x00dev->link.qual.rx_success)41 return rt2x00link_get_avg_rssi(&ant->rssi_ant);42 43 return DEFAULT_RSSI;44}45 46static int rt2x00link_antenna_get_rssi_history(struct rt2x00_dev *rt2x00dev)47{48 struct link_ant *ant = &rt2x00dev->link.ant;49 50 if (ant->rssi_history)51 return ant->rssi_history;52 return DEFAULT_RSSI;53}54 55static void rt2x00link_antenna_update_rssi_history(struct rt2x00_dev *rt2x00dev,56 int rssi)57{58 struct link_ant *ant = &rt2x00dev->link.ant;59 ant->rssi_history = rssi;60}61 62static void rt2x00link_antenna_reset(struct rt2x00_dev *rt2x00dev)63{64 ewma_rssi_init(&rt2x00dev->link.ant.rssi_ant);65}66 67static void rt2x00lib_antenna_diversity_sample(struct rt2x00_dev *rt2x00dev)68{69 struct link_ant *ant = &rt2x00dev->link.ant;70 struct antenna_setup new_ant;71 int other_antenna;72 73 int sample_current = rt2x00link_antenna_get_link_rssi(rt2x00dev);74 int sample_other = rt2x00link_antenna_get_rssi_history(rt2x00dev);75 76 memcpy(&new_ant, &ant->active, sizeof(new_ant));77 78 /*79 * We are done sampling. Now we should evaluate the results.80 */81 ant->flags &= ~ANTENNA_MODE_SAMPLE;82 83 /*84 * During the last period we have sampled the RSSI85 * from both antennas. It now is time to determine86 * which antenna demonstrated the best performance.87 * When we are already on the antenna with the best88 * performance, just create a good starting point89 * for the history and we are done.90 */91 if (sample_current >= sample_other) {92 rt2x00link_antenna_update_rssi_history(rt2x00dev,93 sample_current);94 return;95 }96 97 other_antenna = (ant->active.rx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A;98 99 if (ant->flags & ANTENNA_RX_DIVERSITY)100 new_ant.rx = other_antenna;101 102 if (ant->flags & ANTENNA_TX_DIVERSITY)103 new_ant.tx = other_antenna;104 105 rt2x00lib_config_antenna(rt2x00dev, new_ant);106}107 108static void rt2x00lib_antenna_diversity_eval(struct rt2x00_dev *rt2x00dev)109{110 struct link_ant *ant = &rt2x00dev->link.ant;111 struct antenna_setup new_ant;112 int rssi_curr;113 int rssi_old;114 115 memcpy(&new_ant, &ant->active, sizeof(new_ant));116 117 /*118 * Get current RSSI value along with the historical value,119 * after that update the history with the current value.120 */121 rssi_curr = rt2x00link_antenna_get_link_rssi(rt2x00dev);122 rssi_old = rt2x00link_antenna_get_rssi_history(rt2x00dev);123 rt2x00link_antenna_update_rssi_history(rt2x00dev, rssi_curr);124 125 /*126 * Legacy driver indicates that we should swap antenna's127 * when the difference in RSSI is greater that 5. This128 * also should be done when the RSSI was actually better129 * then the previous sample.130 * When the difference exceeds the threshold we should131 * sample the rssi from the other antenna to make a valid132 * comparison between the 2 antennas.133 */134 if (abs(rssi_curr - rssi_old) < 5)135 return;136 137 ant->flags |= ANTENNA_MODE_SAMPLE;138 139 if (ant->flags & ANTENNA_RX_DIVERSITY)140 new_ant.rx = (new_ant.rx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A;141 142 if (ant->flags & ANTENNA_TX_DIVERSITY)143 new_ant.tx = (new_ant.tx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A;144 145 rt2x00lib_config_antenna(rt2x00dev, new_ant);146}147 148static bool rt2x00lib_antenna_diversity(struct rt2x00_dev *rt2x00dev)149{150 struct link_ant *ant = &rt2x00dev->link.ant;151 152 /*153 * Determine if software diversity is enabled for154 * either the TX or RX antenna (or both).155 */156 if (!(ant->flags & ANTENNA_RX_DIVERSITY) &&157 !(ant->flags & ANTENNA_TX_DIVERSITY)) {158 ant->flags = 0;159 return true;160 }161 162 /*163 * If we have only sampled the data over the last period164 * we should now harvest the data. Otherwise just evaluate165 * the data. The latter should only be performed once166 * every 2 seconds.167 */168 if (ant->flags & ANTENNA_MODE_SAMPLE) {169 rt2x00lib_antenna_diversity_sample(rt2x00dev);170 return true;171 } else if (rt2x00dev->link.count & 1) {172 rt2x00lib_antenna_diversity_eval(rt2x00dev);173 return true;174 }175 176 return false;177}178 179void rt2x00link_update_stats(struct rt2x00_dev *rt2x00dev,180 struct sk_buff *skb,181 struct rxdone_entry_desc *rxdesc)182{183 struct link *link = &rt2x00dev->link;184 struct link_qual *qual = &rt2x00dev->link.qual;185 struct link_ant *ant = &rt2x00dev->link.ant;186 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;187 188 /*189 * No need to update the stats for !=STA interfaces190 */191 if (!rt2x00dev->intf_sta_count)192 return;193 194 /*195 * Frame was received successfully since non-successful196 * frames would have been dropped by the hardware.197 */198 qual->rx_success++;199 200 /*201 * We are only interested in quality statistics from202 * beacons which came from the BSS which we are203 * associated with.204 */205 if (!ieee80211_is_beacon(hdr->frame_control) ||206 !(rxdesc->dev_flags & RXDONE_MY_BSS))207 return;208 209 /*210 * Update global RSSI211 */212 ewma_rssi_add(&link->avg_rssi, -rxdesc->rssi);213 214 /*215 * Update antenna RSSI216 */217 ewma_rssi_add(&ant->rssi_ant, -rxdesc->rssi);218}219 220void rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev)221{222 struct link *link = &rt2x00dev->link;223 224 /*225 * Single monitor mode interfaces should never have226 * work with link tuners.227 */228 if (!rt2x00dev->intf_ap_count && !rt2x00dev->intf_sta_count)229 return;230 231 /*232 * While scanning, link tuning is disabled. By default233 * the most sensitive settings will be used to make sure234 * that all beacons and probe responses will be received235 * during the scan.236 */237 if (test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags))238 return;239 240 rt2x00link_reset_tuner(rt2x00dev, false);241 242 if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))243 ieee80211_queue_delayed_work(rt2x00dev->hw,244 &link->work, LINK_TUNE_INTERVAL);245}246 247void rt2x00link_stop_tuner(struct rt2x00_dev *rt2x00dev)248{249 cancel_delayed_work_sync(&rt2x00dev->link.work);250}251 252void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna)253{254 struct link_qual *qual = &rt2x00dev->link.qual;255 u8 vgc_level = qual->vgc_level_reg;256 257 if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))258 return;259 260 /*261 * Reset link information.262 * Both the currently active vgc level as well as263 * the link tuner counter should be reset. Resetting264 * the counter is important for devices where the265 * device should only perform link tuning during the266 * first minute after being enabled.267 */268 rt2x00dev->link.count = 0;269 memset(qual, 0, sizeof(*qual));270 ewma_rssi_init(&rt2x00dev->link.avg_rssi);271 272 /*273 * Restore the VGC level as stored in the registers,274 * the driver can use this to determine if the register275 * must be updated during reset or not.276 */277 qual->vgc_level_reg = vgc_level;278 279 /*280 * Reset the link tuner.281 */282 rt2x00dev->ops->lib->reset_tuner(rt2x00dev, qual);283 284 if (antenna)285 rt2x00link_antenna_reset(rt2x00dev);286}287 288static void rt2x00link_reset_qual(struct rt2x00_dev *rt2x00dev)289{290 struct link_qual *qual = &rt2x00dev->link.qual;291 292 qual->rx_success = 0;293 qual->rx_failed = 0;294 qual->tx_success = 0;295 qual->tx_failed = 0;296}297 298static void rt2x00link_tuner_sta(struct rt2x00_dev *rt2x00dev, struct link *link)299{300 struct link_qual *qual = &rt2x00dev->link.qual;301 302 /*303 * Update statistics.304 */305 rt2x00dev->ops->lib->link_stats(rt2x00dev, qual);306 rt2x00dev->low_level_stats.dot11FCSErrorCount += qual->rx_failed;307 308 /*309 * Update quality RSSI for link tuning,310 * when we have received some frames and we managed to311 * collect the RSSI data we could use this. Otherwise we312 * must fallback to the default RSSI value.313 */314 if (!qual->rx_success)315 qual->rssi = DEFAULT_RSSI;316 else317 qual->rssi = rt2x00link_get_avg_rssi(&link->avg_rssi);318 319 /*320 * Check if link tuning is supported by the hardware, some hardware321 * do not support link tuning at all, while other devices can disable322 * the feature from the EEPROM.323 */324 if (rt2x00_has_cap_link_tuning(rt2x00dev))325 rt2x00dev->ops->lib->link_tuner(rt2x00dev, qual, link->count);326 327 /*328 * Send a signal to the led to update the led signal strength.329 */330 rt2x00leds_led_quality(rt2x00dev, qual->rssi);331 332 /*333 * Evaluate antenna setup, make this the last step when334 * rt2x00lib_antenna_diversity made changes the quality335 * statistics will be reset.336 */337 if (rt2x00lib_antenna_diversity(rt2x00dev))338 rt2x00link_reset_qual(rt2x00dev);339}340 341static void rt2x00link_tuner(struct work_struct *work)342{343 struct rt2x00_dev *rt2x00dev =344 container_of(work, struct rt2x00_dev, link.work.work);345 struct link *link = &rt2x00dev->link;346 347 /*348 * When the radio is shutting down we should349 * immediately cease all link tuning.350 */351 if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags) ||352 test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags))353 return;354 355 /* Do not race with rt2x00mac_config(). */356 mutex_lock(&rt2x00dev->conf_mutex);357 358 if (rt2x00dev->intf_sta_count)359 rt2x00link_tuner_sta(rt2x00dev, link);360 361 if (rt2x00dev->ops->lib->gain_calibration &&362 (link->count % (AGC_SECONDS / LINK_TUNE_SECONDS)) == 0)363 rt2x00dev->ops->lib->gain_calibration(rt2x00dev);364 365 if (rt2x00dev->ops->lib->vco_calibration &&366 rt2x00_has_cap_vco_recalibration(rt2x00dev) &&367 (link->count % (VCO_SECONDS / LINK_TUNE_SECONDS)) == 0)368 rt2x00dev->ops->lib->vco_calibration(rt2x00dev);369 370 mutex_unlock(&rt2x00dev->conf_mutex);371 372 /*373 * Increase tuner counter, and reschedule the next link tuner run.374 */375 link->count++;376 377 if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))378 ieee80211_queue_delayed_work(rt2x00dev->hw,379 &link->work, LINK_TUNE_INTERVAL);380}381 382void rt2x00link_start_watchdog(struct rt2x00_dev *rt2x00dev)383{384 struct link *link = &rt2x00dev->link;385 386 if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) &&387 rt2x00dev->ops->lib->watchdog && link->watchdog)388 ieee80211_queue_delayed_work(rt2x00dev->hw,389 &link->watchdog_work,390 link->watchdog_interval);391}392 393void rt2x00link_stop_watchdog(struct rt2x00_dev *rt2x00dev)394{395 cancel_delayed_work_sync(&rt2x00dev->link.watchdog_work);396}397 398static void rt2x00link_watchdog(struct work_struct *work)399{400 struct rt2x00_dev *rt2x00dev =401 container_of(work, struct rt2x00_dev, link.watchdog_work.work);402 struct link *link = &rt2x00dev->link;403 404 /*405 * When the radio is shutting down we should406 * immediately cease the watchdog monitoring.407 */408 if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))409 return;410 411 rt2x00dev->ops->lib->watchdog(rt2x00dev);412 413 if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))414 ieee80211_queue_delayed_work(rt2x00dev->hw,415 &link->watchdog_work,416 link->watchdog_interval);417}418 419void rt2x00link_register(struct rt2x00_dev *rt2x00dev)420{421 struct link *link = &rt2x00dev->link;422 423 INIT_DELAYED_WORK(&link->work, rt2x00link_tuner);424 INIT_DELAYED_WORK(&link->watchdog_work, rt2x00link_watchdog);425 426 if (link->watchdog_interval == 0)427 link->watchdog_interval = WATCHDOG_INTERVAL;428}429