162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> 462306a36Sopenharmony_ci <http://rt2x00.serialmonkey.com> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* 962306a36Sopenharmony_ci Module: rt2x00lib 1062306a36Sopenharmony_ci Abstract: rt2x00 generic link tuning routines. 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "rt2x00.h" 1762306a36Sopenharmony_ci#include "rt2x00lib.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * When we lack RSSI information return something less then -80 to 2162306a36Sopenharmony_ci * tell the driver to tune the device to maximum sensitivity. 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci#define DEFAULT_RSSI -128 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic inline int rt2x00link_get_avg_rssi(struct ewma_rssi *ewma) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci unsigned long avg; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci avg = ewma_rssi_read(ewma); 3062306a36Sopenharmony_ci if (avg) 3162306a36Sopenharmony_ci return -avg; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci return DEFAULT_RSSI; 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic int rt2x00link_antenna_get_link_rssi(struct rt2x00_dev *rt2x00dev) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci struct link_ant *ant = &rt2x00dev->link.ant; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (rt2x00dev->link.qual.rx_success) 4162306a36Sopenharmony_ci return rt2x00link_get_avg_rssi(&ant->rssi_ant); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci return DEFAULT_RSSI; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic int rt2x00link_antenna_get_rssi_history(struct rt2x00_dev *rt2x00dev) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct link_ant *ant = &rt2x00dev->link.ant; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (ant->rssi_history) 5162306a36Sopenharmony_ci return ant->rssi_history; 5262306a36Sopenharmony_ci return DEFAULT_RSSI; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic void rt2x00link_antenna_update_rssi_history(struct rt2x00_dev *rt2x00dev, 5662306a36Sopenharmony_ci int rssi) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct link_ant *ant = &rt2x00dev->link.ant; 5962306a36Sopenharmony_ci ant->rssi_history = rssi; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic void rt2x00link_antenna_reset(struct rt2x00_dev *rt2x00dev) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci ewma_rssi_init(&rt2x00dev->link.ant.rssi_ant); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic void rt2x00lib_antenna_diversity_sample(struct rt2x00_dev *rt2x00dev) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct link_ant *ant = &rt2x00dev->link.ant; 7062306a36Sopenharmony_ci struct antenna_setup new_ant; 7162306a36Sopenharmony_ci int other_antenna; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci int sample_current = rt2x00link_antenna_get_link_rssi(rt2x00dev); 7462306a36Sopenharmony_ci int sample_other = rt2x00link_antenna_get_rssi_history(rt2x00dev); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci memcpy(&new_ant, &ant->active, sizeof(new_ant)); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci /* 7962306a36Sopenharmony_ci * We are done sampling. Now we should evaluate the results. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_ci ant->flags &= ~ANTENNA_MODE_SAMPLE; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* 8462306a36Sopenharmony_ci * During the last period we have sampled the RSSI 8562306a36Sopenharmony_ci * from both antennas. It now is time to determine 8662306a36Sopenharmony_ci * which antenna demonstrated the best performance. 8762306a36Sopenharmony_ci * When we are already on the antenna with the best 8862306a36Sopenharmony_ci * performance, just create a good starting point 8962306a36Sopenharmony_ci * for the history and we are done. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ci if (sample_current >= sample_other) { 9262306a36Sopenharmony_ci rt2x00link_antenna_update_rssi_history(rt2x00dev, 9362306a36Sopenharmony_ci sample_current); 9462306a36Sopenharmony_ci return; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci other_antenna = (ant->active.rx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (ant->flags & ANTENNA_RX_DIVERSITY) 10062306a36Sopenharmony_ci new_ant.rx = other_antenna; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (ant->flags & ANTENNA_TX_DIVERSITY) 10362306a36Sopenharmony_ci new_ant.tx = other_antenna; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci rt2x00lib_config_antenna(rt2x00dev, new_ant); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic void rt2x00lib_antenna_diversity_eval(struct rt2x00_dev *rt2x00dev) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct link_ant *ant = &rt2x00dev->link.ant; 11162306a36Sopenharmony_ci struct antenna_setup new_ant; 11262306a36Sopenharmony_ci int rssi_curr; 11362306a36Sopenharmony_ci int rssi_old; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci memcpy(&new_ant, &ant->active, sizeof(new_ant)); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* 11862306a36Sopenharmony_ci * Get current RSSI value along with the historical value, 11962306a36Sopenharmony_ci * after that update the history with the current value. 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ci rssi_curr = rt2x00link_antenna_get_link_rssi(rt2x00dev); 12262306a36Sopenharmony_ci rssi_old = rt2x00link_antenna_get_rssi_history(rt2x00dev); 12362306a36Sopenharmony_ci rt2x00link_antenna_update_rssi_history(rt2x00dev, rssi_curr); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* 12662306a36Sopenharmony_ci * Legacy driver indicates that we should swap antenna's 12762306a36Sopenharmony_ci * when the difference in RSSI is greater that 5. This 12862306a36Sopenharmony_ci * also should be done when the RSSI was actually better 12962306a36Sopenharmony_ci * then the previous sample. 13062306a36Sopenharmony_ci * When the difference exceeds the threshold we should 13162306a36Sopenharmony_ci * sample the rssi from the other antenna to make a valid 13262306a36Sopenharmony_ci * comparison between the 2 antennas. 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_ci if (abs(rssi_curr - rssi_old) < 5) 13562306a36Sopenharmony_ci return; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci ant->flags |= ANTENNA_MODE_SAMPLE; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (ant->flags & ANTENNA_RX_DIVERSITY) 14062306a36Sopenharmony_ci new_ant.rx = (new_ant.rx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (ant->flags & ANTENNA_TX_DIVERSITY) 14362306a36Sopenharmony_ci new_ant.tx = (new_ant.tx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci rt2x00lib_config_antenna(rt2x00dev, new_ant); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic bool rt2x00lib_antenna_diversity(struct rt2x00_dev *rt2x00dev) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct link_ant *ant = &rt2x00dev->link.ant; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* 15362306a36Sopenharmony_ci * Determine if software diversity is enabled for 15462306a36Sopenharmony_ci * either the TX or RX antenna (or both). 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_ci if (!(ant->flags & ANTENNA_RX_DIVERSITY) && 15762306a36Sopenharmony_ci !(ant->flags & ANTENNA_TX_DIVERSITY)) { 15862306a36Sopenharmony_ci ant->flags = 0; 15962306a36Sopenharmony_ci return true; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* 16362306a36Sopenharmony_ci * If we have only sampled the data over the last period 16462306a36Sopenharmony_ci * we should now harvest the data. Otherwise just evaluate 16562306a36Sopenharmony_ci * the data. The latter should only be performed once 16662306a36Sopenharmony_ci * every 2 seconds. 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_ci if (ant->flags & ANTENNA_MODE_SAMPLE) { 16962306a36Sopenharmony_ci rt2x00lib_antenna_diversity_sample(rt2x00dev); 17062306a36Sopenharmony_ci return true; 17162306a36Sopenharmony_ci } else if (rt2x00dev->link.count & 1) { 17262306a36Sopenharmony_ci rt2x00lib_antenna_diversity_eval(rt2x00dev); 17362306a36Sopenharmony_ci return true; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return false; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_civoid rt2x00link_update_stats(struct rt2x00_dev *rt2x00dev, 18062306a36Sopenharmony_ci struct sk_buff *skb, 18162306a36Sopenharmony_ci struct rxdone_entry_desc *rxdesc) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci struct link *link = &rt2x00dev->link; 18462306a36Sopenharmony_ci struct link_qual *qual = &rt2x00dev->link.qual; 18562306a36Sopenharmony_ci struct link_ant *ant = &rt2x00dev->link.ant; 18662306a36Sopenharmony_ci struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* 18962306a36Sopenharmony_ci * No need to update the stats for !=STA interfaces 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_ci if (!rt2x00dev->intf_sta_count) 19262306a36Sopenharmony_ci return; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* 19562306a36Sopenharmony_ci * Frame was received successfully since non-successful 19662306a36Sopenharmony_ci * frames would have been dropped by the hardware. 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_ci qual->rx_success++; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* 20162306a36Sopenharmony_ci * We are only interested in quality statistics from 20262306a36Sopenharmony_ci * beacons which came from the BSS which we are 20362306a36Sopenharmony_ci * associated with. 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_ci if (!ieee80211_is_beacon(hdr->frame_control) || 20662306a36Sopenharmony_ci !(rxdesc->dev_flags & RXDONE_MY_BSS)) 20762306a36Sopenharmony_ci return; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* 21062306a36Sopenharmony_ci * Update global RSSI 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_ci ewma_rssi_add(&link->avg_rssi, -rxdesc->rssi); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* 21562306a36Sopenharmony_ci * Update antenna RSSI 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_ci ewma_rssi_add(&ant->rssi_ant, -rxdesc->rssi); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_civoid rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct link *link = &rt2x00dev->link; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* 22562306a36Sopenharmony_ci * Single monitor mode interfaces should never have 22662306a36Sopenharmony_ci * work with link tuners. 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci if (!rt2x00dev->intf_ap_count && !rt2x00dev->intf_sta_count) 22962306a36Sopenharmony_ci return; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* 23262306a36Sopenharmony_ci * While scanning, link tuning is disabled. By default 23362306a36Sopenharmony_ci * the most sensitive settings will be used to make sure 23462306a36Sopenharmony_ci * that all beacons and probe responses will be received 23562306a36Sopenharmony_ci * during the scan. 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_ci if (test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags)) 23862306a36Sopenharmony_ci return; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci rt2x00link_reset_tuner(rt2x00dev, false); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) 24362306a36Sopenharmony_ci ieee80211_queue_delayed_work(rt2x00dev->hw, 24462306a36Sopenharmony_ci &link->work, LINK_TUNE_INTERVAL); 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_civoid rt2x00link_stop_tuner(struct rt2x00_dev *rt2x00dev) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci cancel_delayed_work_sync(&rt2x00dev->link.work); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_civoid rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct link_qual *qual = &rt2x00dev->link.qual; 25562306a36Sopenharmony_ci u8 vgc_level = qual->vgc_level_reg; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 25862306a36Sopenharmony_ci return; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* 26162306a36Sopenharmony_ci * Reset link information. 26262306a36Sopenharmony_ci * Both the currently active vgc level as well as 26362306a36Sopenharmony_ci * the link tuner counter should be reset. Resetting 26462306a36Sopenharmony_ci * the counter is important for devices where the 26562306a36Sopenharmony_ci * device should only perform link tuning during the 26662306a36Sopenharmony_ci * first minute after being enabled. 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_ci rt2x00dev->link.count = 0; 26962306a36Sopenharmony_ci memset(qual, 0, sizeof(*qual)); 27062306a36Sopenharmony_ci ewma_rssi_init(&rt2x00dev->link.avg_rssi); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* 27362306a36Sopenharmony_ci * Restore the VGC level as stored in the registers, 27462306a36Sopenharmony_ci * the driver can use this to determine if the register 27562306a36Sopenharmony_ci * must be updated during reset or not. 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_ci qual->vgc_level_reg = vgc_level; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* 28062306a36Sopenharmony_ci * Reset the link tuner. 28162306a36Sopenharmony_ci */ 28262306a36Sopenharmony_ci rt2x00dev->ops->lib->reset_tuner(rt2x00dev, qual); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (antenna) 28562306a36Sopenharmony_ci rt2x00link_antenna_reset(rt2x00dev); 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic void rt2x00link_reset_qual(struct rt2x00_dev *rt2x00dev) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct link_qual *qual = &rt2x00dev->link.qual; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci qual->rx_success = 0; 29362306a36Sopenharmony_ci qual->rx_failed = 0; 29462306a36Sopenharmony_ci qual->tx_success = 0; 29562306a36Sopenharmony_ci qual->tx_failed = 0; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic void rt2x00link_tuner_sta(struct rt2x00_dev *rt2x00dev, struct link *link) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci struct link_qual *qual = &rt2x00dev->link.qual; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* 30362306a36Sopenharmony_ci * Update statistics. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_ci rt2x00dev->ops->lib->link_stats(rt2x00dev, qual); 30662306a36Sopenharmony_ci rt2x00dev->low_level_stats.dot11FCSErrorCount += qual->rx_failed; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* 30962306a36Sopenharmony_ci * Update quality RSSI for link tuning, 31062306a36Sopenharmony_ci * when we have received some frames and we managed to 31162306a36Sopenharmony_ci * collect the RSSI data we could use this. Otherwise we 31262306a36Sopenharmony_ci * must fallback to the default RSSI value. 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_ci if (!qual->rx_success) 31562306a36Sopenharmony_ci qual->rssi = DEFAULT_RSSI; 31662306a36Sopenharmony_ci else 31762306a36Sopenharmony_ci qual->rssi = rt2x00link_get_avg_rssi(&link->avg_rssi); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* 32062306a36Sopenharmony_ci * Check if link tuning is supported by the hardware, some hardware 32162306a36Sopenharmony_ci * do not support link tuning at all, while other devices can disable 32262306a36Sopenharmony_ci * the feature from the EEPROM. 32362306a36Sopenharmony_ci */ 32462306a36Sopenharmony_ci if (rt2x00_has_cap_link_tuning(rt2x00dev)) 32562306a36Sopenharmony_ci rt2x00dev->ops->lib->link_tuner(rt2x00dev, qual, link->count); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* 32862306a36Sopenharmony_ci * Send a signal to the led to update the led signal strength. 32962306a36Sopenharmony_ci */ 33062306a36Sopenharmony_ci rt2x00leds_led_quality(rt2x00dev, qual->rssi); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* 33362306a36Sopenharmony_ci * Evaluate antenna setup, make this the last step when 33462306a36Sopenharmony_ci * rt2x00lib_antenna_diversity made changes the quality 33562306a36Sopenharmony_ci * statistics will be reset. 33662306a36Sopenharmony_ci */ 33762306a36Sopenharmony_ci if (rt2x00lib_antenna_diversity(rt2x00dev)) 33862306a36Sopenharmony_ci rt2x00link_reset_qual(rt2x00dev); 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic void rt2x00link_tuner(struct work_struct *work) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci struct rt2x00_dev *rt2x00dev = 34462306a36Sopenharmony_ci container_of(work, struct rt2x00_dev, link.work.work); 34562306a36Sopenharmony_ci struct link *link = &rt2x00dev->link; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci /* 34862306a36Sopenharmony_ci * When the radio is shutting down we should 34962306a36Sopenharmony_ci * immediately cease all link tuning. 35062306a36Sopenharmony_ci */ 35162306a36Sopenharmony_ci if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags) || 35262306a36Sopenharmony_ci test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags)) 35362306a36Sopenharmony_ci return; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci /* Do not race with rt2x00mac_config(). */ 35662306a36Sopenharmony_ci mutex_lock(&rt2x00dev->conf_mutex); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (rt2x00dev->intf_sta_count) 35962306a36Sopenharmony_ci rt2x00link_tuner_sta(rt2x00dev, link); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (rt2x00dev->ops->lib->gain_calibration && 36262306a36Sopenharmony_ci (link->count % (AGC_SECONDS / LINK_TUNE_SECONDS)) == 0) 36362306a36Sopenharmony_ci rt2x00dev->ops->lib->gain_calibration(rt2x00dev); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (rt2x00dev->ops->lib->vco_calibration && 36662306a36Sopenharmony_ci rt2x00_has_cap_vco_recalibration(rt2x00dev) && 36762306a36Sopenharmony_ci (link->count % (VCO_SECONDS / LINK_TUNE_SECONDS)) == 0) 36862306a36Sopenharmony_ci rt2x00dev->ops->lib->vco_calibration(rt2x00dev); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci mutex_unlock(&rt2x00dev->conf_mutex); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci /* 37362306a36Sopenharmony_ci * Increase tuner counter, and reschedule the next link tuner run. 37462306a36Sopenharmony_ci */ 37562306a36Sopenharmony_ci link->count++; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) 37862306a36Sopenharmony_ci ieee80211_queue_delayed_work(rt2x00dev->hw, 37962306a36Sopenharmony_ci &link->work, LINK_TUNE_INTERVAL); 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_civoid rt2x00link_start_watchdog(struct rt2x00_dev *rt2x00dev) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct link *link = &rt2x00dev->link; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && 38762306a36Sopenharmony_ci rt2x00dev->ops->lib->watchdog && !link->watchdog_disabled) 38862306a36Sopenharmony_ci ieee80211_queue_delayed_work(rt2x00dev->hw, 38962306a36Sopenharmony_ci &link->watchdog_work, 39062306a36Sopenharmony_ci link->watchdog_interval); 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_civoid rt2x00link_stop_watchdog(struct rt2x00_dev *rt2x00dev) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci cancel_delayed_work_sync(&rt2x00dev->link.watchdog_work); 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic void rt2x00link_watchdog(struct work_struct *work) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci struct rt2x00_dev *rt2x00dev = 40162306a36Sopenharmony_ci container_of(work, struct rt2x00_dev, link.watchdog_work.work); 40262306a36Sopenharmony_ci struct link *link = &rt2x00dev->link; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* 40562306a36Sopenharmony_ci * When the radio is shutting down we should 40662306a36Sopenharmony_ci * immediately cease the watchdog monitoring. 40762306a36Sopenharmony_ci */ 40862306a36Sopenharmony_ci if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 40962306a36Sopenharmony_ci return; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci rt2x00dev->ops->lib->watchdog(rt2x00dev); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) 41462306a36Sopenharmony_ci ieee80211_queue_delayed_work(rt2x00dev->hw, 41562306a36Sopenharmony_ci &link->watchdog_work, 41662306a36Sopenharmony_ci link->watchdog_interval); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_civoid rt2x00link_register(struct rt2x00_dev *rt2x00dev) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci struct link *link = &rt2x00dev->link; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci INIT_DELAYED_WORK(&link->work, rt2x00link_tuner); 42462306a36Sopenharmony_ci INIT_DELAYED_WORK(&link->watchdog_work, rt2x00link_watchdog); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (link->watchdog_interval == 0) 42762306a36Sopenharmony_ci link->watchdog_interval = WATCHDOG_INTERVAL; 42862306a36Sopenharmony_ci} 429