summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/ath/ath5k/pcu.c
diff options
context:
space:
mode:
authorNick Kossifidis <mickflemm@gmail.com>2010-11-23 21:12:23 +0200
committerJohn W. Linville <linville@tuxdriver.com>2010-11-30 13:53:21 -0500
commit61cde037234c4b8e6497a23f5f236c64cbf9d41d (patch)
treeca988a018dc9d9e4f433f273d48d4ebe517eeec0 /drivers/net/wireless/ath/ath5k/pcu.c
parent3017fcab416d8d1ee48ca16aa9a3062f600dab8e (diff)
ath5k: Extend rate_duration
* Extend ieee80211_generic_frame_duration to support the various bwmodes. * Better document what's going on with ack bitrates and update write_rate_duration to support the standard ack bitrates (when we don't set the high bit). * Get rid of set_ack_bitrate_high and introduce a flag on ath5k_hw for this (we only called the function on init anyway so there is no difference). Signed-off-by: Nick Kossifidis <mickflemm@gmail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/ath/ath5k/pcu.c')
-rw-r--r--drivers/net/wireless/ath/ath5k/pcu.c159
1 files changed, 116 insertions, 43 deletions
diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c
index 2118f7048f37..e7f6be9cdf1a 100644
--- a/drivers/net/wireless/ath/ath5k/pcu.c
+++ b/drivers/net/wireless/ath/ath5k/pcu.c
@@ -31,11 +31,100 @@
#include "debug.h"
#include "base.h"
+/*
+ * AR5212+ can use higher rates for ack transmition
+ * based on current tx rate instead of the base rate.
+ * It does this to better utilize channel usage.
+ * This is a mapping between G rates (that cover both
+ * CCK and OFDM) and ack rates that we use when setting
+ * rate -> duration table. This mapping is hw-based so
+ * don't change anything.
+ *
+ * To enable this functionality we must set
+ * ah->ah_ack_bitrate_high to true else base rate is
+ * used (1Mb for CCK, 6Mb for OFDM).
+ */
+static const unsigned int ack_rates_high[] =
+/* Tx -> ACK */
+/* 1Mb -> 1Mb */ { 0,
+/* 2MB -> 2Mb */ 1,
+/* 5.5Mb -> 2Mb */ 1,
+/* 11Mb -> 2Mb */ 1,
+/* 6Mb -> 6Mb */ 4,
+/* 9Mb -> 6Mb */ 4,
+/* 12Mb -> 12Mb */ 6,
+/* 18Mb -> 12Mb */ 6,
+/* 24Mb -> 24Mb */ 8,
+/* 36Mb -> 24Mb */ 8,
+/* 48Mb -> 24Mb */ 8,
+/* 54Mb -> 24Mb */ 8 };
+
/*******************\
* Helper functions *
\*******************/
/**
+ * ath5k_hw_get_frame_duration - Get tx time of a frame
+ *
+ * @ah: The &struct ath5k_hw
+ * @len: Frame's length in bytes
+ * @rate: The @struct ieee80211_rate
+ *
+ * Calculate tx duration of a frame given it's rate and length
+ * It extends ieee80211_generic_frame_duration for non standard
+ * bwmodes.
+ */
+int ath5k_hw_get_frame_duration(struct ath5k_hw *ah,
+ int len, struct ieee80211_rate *rate)
+{
+ struct ath5k_softc *sc = ah->ah_sc;
+ int sifs, preamble, plcp_bits, sym_time;
+ int bitrate, bits, symbols, symbol_bits;
+ int dur;
+
+ /* Fallback */
+ if (!ah->ah_bwmode) {
+ dur = ieee80211_generic_frame_duration(sc->hw,
+ NULL, len, rate);
+ return dur;
+ }
+
+ bitrate = rate->bitrate;
+ preamble = AR5K_INIT_OFDM_PREAMPLE_TIME;
+ plcp_bits = AR5K_INIT_OFDM_PLCP_BITS;
+ sym_time = AR5K_INIT_OFDM_SYMBOL_TIME;
+
+ switch (ah->ah_bwmode) {
+ case AR5K_BWMODE_40MHZ:
+ sifs = AR5K_INIT_SIFS_TURBO;
+ preamble = AR5K_INIT_OFDM_PREAMBLE_TIME_MIN;
+ break;
+ case AR5K_BWMODE_10MHZ:
+ sifs = AR5K_INIT_SIFS_HALF_RATE;
+ preamble *= 2;
+ sym_time *= 2;
+ break;
+ case AR5K_BWMODE_5MHZ:
+ sifs = AR5K_INIT_SIFS_QUARTER_RATE;
+ preamble *= 4;
+ sym_time *= 4;
+ break;
+ default:
+ sifs = AR5K_INIT_SIFS_DEFAULT_BG;
+ break;
+ }
+
+ bits = plcp_bits + (len << 3);
+ /* Bit rate is in 100Kbits */
+ symbol_bits = bitrate * sym_time;
+ symbols = DIV_ROUND_UP(bits * 10, symbol_bits);
+
+ dur = sifs + preamble + (sym_time * symbols);
+
+ return dur;
+}
+
+/**
* ath5k_hw_get_default_slottime - Get the default slot time for current mode
*
* @ah: The &struct ath5k_hw
@@ -120,43 +209,11 @@ void ath5k_hw_update_mib_counters(struct ath5k_hw *ah)
stats->beacons += ath5k_hw_reg_read(ah, AR5K_BEACON_CNT);
}
-/**
- * ath5k_hw_set_ack_bitrate - set bitrate for ACKs
- *
- * @ah: The &struct ath5k_hw
- * @high: Flag to determine if we want to use high transmission rate
- * for ACKs or not
- *
- * If high flag is set, we tell hw to use a set of control rates based on
- * the current transmission rate (check out control_rates array inside reset.c).
- * If not hw just uses the lowest rate available for the current modulation
- * scheme being used (1Mbit for CCK and 6Mbits for OFDM).
- */
-void ath5k_hw_set_ack_bitrate_high(struct ath5k_hw *ah, bool high)
-{
- if (ah->ah_version != AR5K_AR5212)
- return;
- else {
- u32 val = AR5K_STA_ID1_BASE_RATE_11B | AR5K_STA_ID1_ACKCTS_6MB;
- if (high)
- AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, val);
- else
- AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1, val);
- }
-}
-
/******************\
* ACK/CTS Timeouts *
\******************/
-/*
- * index into rates for control rates, we can set it up like this because
- * this is only used for AR5212 and we know it supports G mode
- */
-static const unsigned int control_rates[] =
- { 0, 1, 1, 1, 4, 4, 6, 6, 8, 8, 8, 8 };
-
/**
* ath5k_hw_write_rate_duration - fill rate code to duration table
*
@@ -164,7 +221,7 @@ static const unsigned int control_rates[] =
* @mode: one of enum ath5k_driver_mode
*
* Write the rate code to duration table upon hw reset. This is a helper for
- * ath5k_hw_reset(). It seems all this is doing is setting an ACK timeout on
+ * ath5k_hw_pcu_init(). It seems all this is doing is setting an ACK timeout on
* the hardware, based on current mode, for each rate. The rates which are
* capable of short preamble (802.11b rates 2Mbps, 5.5Mbps, and 11Mbps) have
* different rate code so we write their value twice (one for long preamble
@@ -172,23 +229,30 @@ static const unsigned int control_rates[] =
*
* Note: Band doesn't matter here, if we set the values for OFDM it works
* on both a and g modes. So all we have to do is set values for all g rates
- * that include all OFDM and CCK rates. If we operate in turbo or xr/half/
- * quarter rate mode, we need to use another set of bitrates (that's why we
- * need the mode parameter) but we don't handle these proprietary modes yet.
+ * that include all OFDM and CCK rates.
+ *
*/
-static inline void ath5k_hw_write_rate_duration(struct ath5k_hw *ah,
- unsigned int mode)
+static inline void ath5k_hw_write_rate_duration(struct ath5k_hw *ah)
{
struct ath5k_softc *sc = ah->ah_sc;
struct ieee80211_rate *rate;
unsigned int i;
+ /* 802.11g covers both OFDM and CCK */
+ u8 band = IEEE80211_BAND_2GHZ;
/* Write rate duration table */
- for (i = 0; i < sc->sbands[IEEE80211_BAND_2GHZ].n_bitrates; i++) {
+ for (i = 0; i < sc->sbands[band].n_bitrates; i++) {
u32 reg;
u16 tx_time;
- rate = &sc->sbands[IEEE80211_BAND_2GHZ].bitrates[control_rates[i]];
+ if (ah->ah_ack_bitrate_high)
+ rate = &sc->sbands[band].bitrates[ack_rates_high[i]];
+ /* CCK -> 1Mb */
+ else if (i < 4)
+ rate = &sc->sbands[band].bitrates[0];
+ /* OFDM -> 6Mb */
+ else
+ rate = &sc->sbands[band].bitrates[4];
/* Set ACK timeout */
reg = AR5K_RATE_DUR(rate->hw_value);
@@ -199,8 +263,9 @@ static inline void ath5k_hw_write_rate_duration(struct ath5k_hw *ah,
* actual rate for this rate. See mac80211 tx.c
* ieee80211_duration() for a brief description of
* what rate we should choose to TX ACKs. */
- tx_time = le16_to_cpu(ieee80211_generic_frame_duration(sc->hw,
- NULL, 10, rate));
+ tx_time = ath5k_hw_get_frame_duration(ah, 10, rate);
+
+ tx_time = le16_to_cpu(tx_time);
ath5k_hw_reg_write(ah, tx_time, reg);
@@ -835,7 +900,7 @@ void ath5k_hw_pcu_init(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
* mac80211 are integrated */
if (ah->ah_version == AR5K_AR5212 &&
ah->ah_sc->nvifs)
- ath5k_hw_write_rate_duration(ah, mode);
+ ath5k_hw_write_rate_duration(ah);
/* Set RSSI/BRSSI thresholds
*
@@ -869,5 +934,13 @@ void ath5k_hw_pcu_init(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
if (ah->ah_coverage_class > 0)
ath5k_hw_set_coverage_class(ah, ah->ah_coverage_class);
+ /* Set ACK bitrate mode (see ack_rates_high) */
+ if (ah->ah_version == AR5K_AR5212) {
+ u32 val = AR5K_STA_ID1_BASE_RATE_11B | AR5K_STA_ID1_ACKCTS_6MB;
+ if (ah->ah_ack_bitrate_high)
+ AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, val);
+ else
+ AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1, val);
+ }
return;
}