brintos

brintos / linux-shallow public Read only

0
0
Text · 11.2 KiB · fb23d40 Raw
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