diff options
author | Ivo van Doorn <ivdoorn@gmail.com> | 2007-10-13 10:26:36 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-01-28 18:02:53 -0500 |
commit | 69f81a2cac860cf183eb9ef7787525c3552d4612 (patch) | |
tree | b149e55f1e21df8edf33046e662ce8f00834f1ac /drivers/net | |
parent | 8de8c5162b157884aa4855564cbfd9ec9119c819 (diff) |
[PATCH] rt2x00: Implement SW diversity
When mac80211 indicates that the default antenna setup
should be used _and_ that this default setup is SW_DIVERSITY.
This requires sampling and storing the RSSI per antenna
and check once every 2 seconds to determine if the RSSI
has changed significantly. Once this is the case we should sample
the other antenna for a short period and evaluate if
we need to swap antenna or not.
Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00.h | 87 | ||||
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00config.c | 26 | ||||
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00dev.c | 138 | ||||
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00lib.h | 2 |
4 files changed, 232 insertions, 21 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index d6f0a72b7a0c..e7533e2ccd2e 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h | |||
@@ -241,6 +241,43 @@ struct link_qual { | |||
241 | }; | 241 | }; |
242 | 242 | ||
243 | /* | 243 | /* |
244 | * Antenna settings about the currently active link. | ||
245 | */ | ||
246 | struct link_ant { | ||
247 | /* | ||
248 | * Antenna flags | ||
249 | */ | ||
250 | unsigned int flags; | ||
251 | #define ANTENNA_RX_DIVERSITY 0x00000001 | ||
252 | #define ANTENNA_TX_DIVERSITY 0x00000002 | ||
253 | #define ANTENNA_MODE_SAMPLE 0x00000004 | ||
254 | |||
255 | /* | ||
256 | * Currently active TX/RX antenna setup. | ||
257 | * When software diversity is used, this will indicate | ||
258 | * which antenna is actually used at this time. | ||
259 | */ | ||
260 | struct antenna_setup active; | ||
261 | |||
262 | /* | ||
263 | * RSSI information for the different antenna's. | ||
264 | * These statistics are used to determine when | ||
265 | * to switch antenna when using software diversity. | ||
266 | * | ||
267 | * rssi[0] -> Antenna A RSSI | ||
268 | * rssi[1] -> Antenna B RSSI | ||
269 | */ | ||
270 | int rssi_history[2]; | ||
271 | |||
272 | /* | ||
273 | * Current RSSI average of the currently active antenna. | ||
274 | * Similar to the avg_rssi in the link_qual structure | ||
275 | * this value is updated by using the walking average. | ||
276 | */ | ||
277 | int rssi_ant; | ||
278 | }; | ||
279 | |||
280 | /* | ||
244 | * To optimize the quality of the link we need to store | 281 | * To optimize the quality of the link we need to store |
245 | * the quality of received frames and periodically | 282 | * the quality of received frames and periodically |
246 | * optimize the link. | 283 | * optimize the link. |
@@ -259,11 +296,9 @@ struct link { | |||
259 | struct link_qual qual; | 296 | struct link_qual qual; |
260 | 297 | ||
261 | /* | 298 | /* |
262 | * Currently active TX/RX antenna setup. | 299 | * TX/RX antenna setup. |
263 | * When software diversity is used, this will indicate | ||
264 | * which antenna is actually used at this time. | ||
265 | */ | 300 | */ |
266 | struct antenna_setup active_ant; | 301 | struct link_ant ant; |
267 | 302 | ||
268 | /* | 303 | /* |
269 | * Active VGC level | 304 | * Active VGC level |
@@ -277,25 +312,47 @@ struct link { | |||
277 | }; | 312 | }; |
278 | 313 | ||
279 | /* | 314 | /* |
280 | * Update the rssi using the walking average approach. | 315 | * Small helper macro to work with moving/walking averages. |
281 | */ | 316 | */ |
282 | static inline void rt2x00_update_link_rssi(struct link *link, int rssi) | 317 | #define MOVING_AVERAGE(__avg, __val, __samples) \ |
283 | { | 318 | ( (((__avg) * ((__samples) - 1)) + (__val)) / (__samples) ) |
284 | if (link->qual.avg_rssi) | 319 | |
285 | rssi = ((link->qual.avg_rssi * 7) + rssi) / 8; | 320 | /* |
286 | link->qual.avg_rssi = rssi; | 321 | * When we lack RSSI information return something less then -80 to |
287 | } | 322 | * tell the driver to tune the device to maximum sensitivity. |
323 | */ | ||
324 | #define DEFAULT_RSSI ( -128 ) | ||
288 | 325 | ||
289 | /* | 326 | /* |
290 | * When the avg_rssi is unset or no frames have been received), | 327 | * Link quality access functions. |
291 | * we need to return the default value which needs to be less | ||
292 | * than -80 so the device will select the maximum sensitivity. | ||
293 | */ | 328 | */ |
294 | static inline int rt2x00_get_link_rssi(struct link *link) | 329 | static inline int rt2x00_get_link_rssi(struct link *link) |
295 | { | 330 | { |
296 | if (link->qual.avg_rssi && link->qual.rx_success) | 331 | if (link->qual.avg_rssi && link->qual.rx_success) |
297 | return link->qual.avg_rssi; | 332 | return link->qual.avg_rssi; |
298 | return -128; | 333 | return DEFAULT_RSSI; |
334 | } | ||
335 | |||
336 | static inline int rt2x00_get_link_ant_rssi(struct link *link) | ||
337 | { | ||
338 | if (link->ant.rssi_ant && link->qual.rx_success) | ||
339 | return link->ant.rssi_ant; | ||
340 | return DEFAULT_RSSI; | ||
341 | } | ||
342 | |||
343 | static inline int rt2x00_get_link_ant_rssi_history(struct link *link, | ||
344 | enum antenna ant) | ||
345 | { | ||
346 | if (link->ant.rssi_history[ant - ANTENNA_A]) | ||
347 | return link->ant.rssi_history[ant - ANTENNA_A]; | ||
348 | return DEFAULT_RSSI; | ||
349 | } | ||
350 | |||
351 | static inline int rt2x00_update_ant_rssi(struct link *link, int rssi) | ||
352 | { | ||
353 | int old_rssi = link->ant.rssi_history[link->ant.active.rx - ANTENNA_A]; | ||
354 | link->ant.rssi_history[link->ant.active.rx - ANTENNA_A] = rssi; | ||
355 | return old_rssi; | ||
299 | } | 356 | } |
300 | 357 | ||
301 | /* | 358 | /* |
diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/rt2x00/rt2x00config.c index 04518b089bdb..2b0edd20eea5 100644 --- a/drivers/net/wireless/rt2x00/rt2x00config.c +++ b/drivers/net/wireless/rt2x00/rt2x00config.c | |||
@@ -94,6 +94,26 @@ void rt2x00lib_config_type(struct rt2x00_dev *rt2x00dev, const int type) | |||
94 | rt2x00dev->ops->lib->config_type(rt2x00dev, type, tsf_sync); | 94 | rt2x00dev->ops->lib->config_type(rt2x00dev, type, tsf_sync); |
95 | } | 95 | } |
96 | 96 | ||
97 | void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev, | ||
98 | enum antenna rx, enum antenna tx) | ||
99 | { | ||
100 | struct rt2x00lib_conf libconf; | ||
101 | |||
102 | libconf.ant.rx = rx; | ||
103 | libconf.ant.tx = tx; | ||
104 | |||
105 | /* | ||
106 | * Write new antenna setup to device and reset the link tuner. | ||
107 | * The latter is required since we need to recalibrate the | ||
108 | * noise-sensitivity ratio for the new setup. | ||
109 | */ | ||
110 | rt2x00dev->ops->lib->config(rt2x00dev, CONFIG_UPDATE_ANTENNA, &libconf); | ||
111 | rt2x00lib_reset_link_tuner(rt2x00dev); | ||
112 | |||
113 | rt2x00dev->link.ant.active.rx = libconf.ant.rx; | ||
114 | rt2x00dev->link.ant.active.tx = libconf.ant.tx; | ||
115 | } | ||
116 | |||
97 | void rt2x00lib_config(struct rt2x00_dev *rt2x00dev, | 117 | void rt2x00lib_config(struct rt2x00_dev *rt2x00dev, |
98 | struct ieee80211_conf *conf, const int force_config) | 118 | struct ieee80211_conf *conf, const int force_config) |
99 | { | 119 | { |
@@ -101,7 +121,7 @@ void rt2x00lib_config(struct rt2x00_dev *rt2x00dev, | |||
101 | struct ieee80211_hw_mode *mode; | 121 | struct ieee80211_hw_mode *mode; |
102 | struct ieee80211_rate *rate; | 122 | struct ieee80211_rate *rate; |
103 | struct antenna_setup *default_ant = &rt2x00dev->default_ant; | 123 | struct antenna_setup *default_ant = &rt2x00dev->default_ant; |
104 | struct antenna_setup *active_ant = &rt2x00dev->link.active_ant; | 124 | struct antenna_setup *active_ant = &rt2x00dev->link.ant.active; |
105 | int flags = 0; | 125 | int flags = 0; |
106 | int short_slot_time; | 126 | int short_slot_time; |
107 | 127 | ||
@@ -247,6 +267,6 @@ config: | |||
247 | rt2x00dev->rx_status.freq = conf->freq; | 267 | rt2x00dev->rx_status.freq = conf->freq; |
248 | rt2x00dev->rx_status.channel = conf->channel; | 268 | rt2x00dev->rx_status.channel = conf->channel; |
249 | rt2x00dev->tx_power = conf->power_level; | 269 | rt2x00dev->tx_power = conf->power_level; |
250 | rt2x00dev->link.active_ant.rx = libconf.ant.rx; | 270 | rt2x00dev->link.ant.active.rx = libconf.ant.rx; |
251 | rt2x00dev->link.active_ant.tx = libconf.ant.tx; | 271 | rt2x00dev->link.ant.active.tx = libconf.ant.tx; |
252 | } | 272 | } |
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 0f824b27f1ef..360f03ae17a5 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c | |||
@@ -193,6 +193,133 @@ void rt2x00lib_toggle_rx(struct rt2x00_dev *rt2x00dev, enum dev_state state) | |||
193 | rt2x00lib_start_link_tuner(rt2x00dev); | 193 | rt2x00lib_start_link_tuner(rt2x00dev); |
194 | } | 194 | } |
195 | 195 | ||
196 | static void rt2x00lib_evaluate_antenna_sample(struct rt2x00_dev *rt2x00dev) | ||
197 | { | ||
198 | enum antenna rx = rt2x00dev->link.ant.active.rx; | ||
199 | enum antenna tx = rt2x00dev->link.ant.active.tx; | ||
200 | int sample_a = | ||
201 | rt2x00_get_link_ant_rssi_history(&rt2x00dev->link, ANTENNA_A); | ||
202 | int sample_b = | ||
203 | rt2x00_get_link_ant_rssi_history(&rt2x00dev->link, ANTENNA_B); | ||
204 | |||
205 | /* | ||
206 | * We are done sampling. Now we should evaluate the results. | ||
207 | */ | ||
208 | rt2x00dev->link.ant.flags &= ~ANTENNA_MODE_SAMPLE; | ||
209 | |||
210 | /* | ||
211 | * During the last period we have sampled the RSSI | ||
212 | * from both antenna's. It now is time to determine | ||
213 | * which antenna demonstrated the best performance. | ||
214 | * When we are already on the antenna with the best | ||
215 | * performance, then there really is nothing for us | ||
216 | * left to do. | ||
217 | */ | ||
218 | if (sample_a == sample_b) | ||
219 | return; | ||
220 | |||
221 | if (rt2x00dev->link.ant.flags & ANTENNA_RX_DIVERSITY) { | ||
222 | if (sample_a > sample_b && rx == ANTENNA_B) | ||
223 | rx = ANTENNA_A; | ||
224 | else if (rx == ANTENNA_A) | ||
225 | rx = ANTENNA_B; | ||
226 | } | ||
227 | |||
228 | if (rt2x00dev->link.ant.flags & ANTENNA_TX_DIVERSITY) { | ||
229 | if (sample_a > sample_b && tx == ANTENNA_B) | ||
230 | tx = ANTENNA_A; | ||
231 | else if (tx == ANTENNA_A) | ||
232 | tx = ANTENNA_B; | ||
233 | } | ||
234 | |||
235 | rt2x00lib_config_antenna(rt2x00dev, rx, tx); | ||
236 | } | ||
237 | |||
238 | static void rt2x00lib_evaluate_antenna_eval(struct rt2x00_dev *rt2x00dev) | ||
239 | { | ||
240 | enum antenna rx = rt2x00dev->link.ant.active.rx; | ||
241 | enum antenna tx = rt2x00dev->link.ant.active.tx; | ||
242 | int rssi_curr = rt2x00_get_link_ant_rssi(&rt2x00dev->link); | ||
243 | int rssi_old = rt2x00_update_ant_rssi(&rt2x00dev->link, rssi_curr); | ||
244 | |||
245 | /* | ||
246 | * Legacy driver indicates that we should swap antenna's | ||
247 | * when the difference in RSSI is greater that 5. This | ||
248 | * also should be done when the RSSI was actually better | ||
249 | * then the previous sample. | ||
250 | * When the difference exceeds the threshold we should | ||
251 | * sample the rssi from the other antenna to make a valid | ||
252 | * comparison between the 2 antennas. | ||
253 | */ | ||
254 | if ((rssi_curr - rssi_old) > -5 || (rssi_curr - rssi_old) < 5) | ||
255 | return; | ||
256 | |||
257 | rt2x00dev->link.ant.flags |= ANTENNA_MODE_SAMPLE; | ||
258 | |||
259 | if (rt2x00dev->link.ant.flags & ANTENNA_RX_DIVERSITY) | ||
260 | rx = (rx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A; | ||
261 | |||
262 | if (rt2x00dev->link.ant.flags & ANTENNA_TX_DIVERSITY) | ||
263 | tx = (tx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A; | ||
264 | |||
265 | rt2x00lib_config_antenna(rt2x00dev, rx, tx); | ||
266 | } | ||
267 | |||
268 | static void rt2x00lib_evaluate_antenna(struct rt2x00_dev *rt2x00dev) | ||
269 | { | ||
270 | /* | ||
271 | * Determine if software diversity is enabled for | ||
272 | * either the TX or RX antenna (or both). | ||
273 | * Always perform this check since within the link | ||
274 | * tuner interval the configuration might have changed. | ||
275 | */ | ||
276 | rt2x00dev->link.ant.flags &= ~ANTENNA_RX_DIVERSITY; | ||
277 | rt2x00dev->link.ant.flags &= ~ANTENNA_TX_DIVERSITY; | ||
278 | |||
279 | if (rt2x00dev->hw->conf.antenna_sel_rx == 0 && | ||
280 | rt2x00dev->default_ant.rx != ANTENNA_SW_DIVERSITY) | ||
281 | rt2x00dev->link.ant.flags |= ANTENNA_RX_DIVERSITY; | ||
282 | if (rt2x00dev->hw->conf.antenna_sel_tx == 0 && | ||
283 | rt2x00dev->default_ant.tx != ANTENNA_SW_DIVERSITY) | ||
284 | rt2x00dev->link.ant.flags |= ANTENNA_TX_DIVERSITY; | ||
285 | |||
286 | if (!(rt2x00dev->link.ant.flags & ANTENNA_RX_DIVERSITY) && | ||
287 | !(rt2x00dev->link.ant.flags & ANTENNA_TX_DIVERSITY)) { | ||
288 | rt2x00dev->link.ant.flags &= ~ANTENNA_MODE_SAMPLE; | ||
289 | return; | ||
290 | } | ||
291 | |||
292 | /* | ||
293 | * If we have only sampled the data over the last period | ||
294 | * we should now harvest the data. Otherwise just evaluate | ||
295 | * the data. The latter should only be performed once | ||
296 | * every 2 seconds. | ||
297 | */ | ||
298 | if (rt2x00dev->link.ant.flags & ANTENNA_MODE_SAMPLE) | ||
299 | rt2x00lib_evaluate_antenna_sample(rt2x00dev); | ||
300 | else if (rt2x00dev->link.count & 1) | ||
301 | rt2x00lib_evaluate_antenna_eval(rt2x00dev); | ||
302 | } | ||
303 | |||
304 | static void rt2x00lib_update_link_stats(struct link *link, int rssi) | ||
305 | { | ||
306 | int avg_rssi = rssi; | ||
307 | |||
308 | /* | ||
309 | * Update global RSSI | ||
310 | */ | ||
311 | if (link->qual.avg_rssi) | ||
312 | avg_rssi = MOVING_AVERAGE(link->qual.avg_rssi, rssi, 8); | ||
313 | link->qual.avg_rssi = avg_rssi; | ||
314 | |||
315 | /* | ||
316 | * Update antenna RSSI | ||
317 | */ | ||
318 | if (link->ant.rssi_ant) | ||
319 | rssi = MOVING_AVERAGE(link->ant.rssi_ant, rssi, 8); | ||
320 | link->ant.rssi_ant = rssi; | ||
321 | } | ||
322 | |||
196 | static void rt2x00lib_precalculate_link_signal(struct link_qual *qual) | 323 | static void rt2x00lib_precalculate_link_signal(struct link_qual *qual) |
197 | { | 324 | { |
198 | if (qual->rx_failed || qual->rx_success) | 325 | if (qual->rx_failed || qual->rx_success) |
@@ -261,7 +388,6 @@ static void rt2x00lib_link_tuner(struct work_struct *work) | |||
261 | * Update statistics. | 388 | * Update statistics. |
262 | */ | 389 | */ |
263 | rt2x00dev->ops->lib->link_stats(rt2x00dev, &rt2x00dev->link.qual); | 390 | rt2x00dev->ops->lib->link_stats(rt2x00dev, &rt2x00dev->link.qual); |
264 | |||
265 | rt2x00dev->low_level_stats.dot11FCSErrorCount += | 391 | rt2x00dev->low_level_stats.dot11FCSErrorCount += |
266 | rt2x00dev->link.qual.rx_failed; | 392 | rt2x00dev->link.qual.rx_failed; |
267 | 393 | ||
@@ -273,6 +399,11 @@ static void rt2x00lib_link_tuner(struct work_struct *work) | |||
273 | rt2x00dev->ops->lib->link_tuner(rt2x00dev); | 399 | rt2x00dev->ops->lib->link_tuner(rt2x00dev); |
274 | 400 | ||
275 | /* | 401 | /* |
402 | * Evaluate antenna setup. | ||
403 | */ | ||
404 | rt2x00lib_evaluate_antenna(rt2x00dev); | ||
405 | |||
406 | /* | ||
276 | * Precalculate a portion of the link signal which is | 407 | * Precalculate a portion of the link signal which is |
277 | * in based on the tx/rx success/failure counters. | 408 | * in based on the tx/rx success/failure counters. |
278 | */ | 409 | */ |
@@ -426,14 +557,15 @@ void rt2x00lib_rxdone(struct data_entry *entry, struct sk_buff *skb, | |||
426 | } | 557 | } |
427 | } | 558 | } |
428 | 559 | ||
429 | rt2x00_update_link_rssi(&rt2x00dev->link, desc->rssi); | 560 | rt2x00lib_update_link_stats(&rt2x00dev->link, desc->rssi); |
430 | rt2x00dev->link.qual.rx_success++; | 561 | rt2x00dev->link.qual.rx_success++; |
562 | |||
431 | rx_status->rate = val; | 563 | rx_status->rate = val; |
432 | rx_status->signal = | 564 | rx_status->signal = |
433 | rt2x00lib_calculate_link_signal(rt2x00dev, desc->rssi); | 565 | rt2x00lib_calculate_link_signal(rt2x00dev, desc->rssi); |
434 | rx_status->ssi = desc->rssi; | 566 | rx_status->ssi = desc->rssi; |
435 | rx_status->flag = desc->flags; | 567 | rx_status->flag = desc->flags; |
436 | rx_status->antenna = rt2x00dev->link.active_ant.rx; | 568 | rx_status->antenna = rt2x00dev->link.ant.active.rx; |
437 | 569 | ||
438 | /* | 570 | /* |
439 | * Send frame to mac80211 | 571 | * Send frame to mac80211 |
diff --git a/drivers/net/wireless/rt2x00/rt2x00lib.h b/drivers/net/wireless/rt2x00/rt2x00lib.h index 06d9bc0015c0..73194112f45c 100644 --- a/drivers/net/wireless/rt2x00/rt2x00lib.h +++ b/drivers/net/wireless/rt2x00/rt2x00lib.h | |||
@@ -53,6 +53,8 @@ void rt2x00lib_uninitialize(struct rt2x00_dev *rt2x00dev); | |||
53 | void rt2x00lib_config_mac_addr(struct rt2x00_dev *rt2x00dev, u8 *mac); | 53 | void rt2x00lib_config_mac_addr(struct rt2x00_dev *rt2x00dev, u8 *mac); |
54 | void rt2x00lib_config_bssid(struct rt2x00_dev *rt2x00dev, u8 *bssid); | 54 | void rt2x00lib_config_bssid(struct rt2x00_dev *rt2x00dev, u8 *bssid); |
55 | void rt2x00lib_config_type(struct rt2x00_dev *rt2x00dev, const int type); | 55 | void rt2x00lib_config_type(struct rt2x00_dev *rt2x00dev, const int type); |
56 | void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev, | ||
57 | enum antenna rx, enum antenna tx); | ||
56 | void rt2x00lib_config(struct rt2x00_dev *rt2x00dev, | 58 | void rt2x00lib_config(struct rt2x00_dev *rt2x00dev, |
57 | struct ieee80211_conf *conf, const int force_config); | 59 | struct ieee80211_conf *conf, const int force_config); |
58 | 60 | ||