From 1e51b2ff0a33797476932a68d36136921e4e1b05 Mon Sep 17 00:00:00 2001 From: Luis R. Rodriguez Date: Thu, 29 Jul 2010 22:56:22 -0400 Subject: ath9k: add fastcc to debug print for channel change This helps us debug channel changes better. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 3caa32316e7b..2ab3df22fbb5 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -226,9 +226,10 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, caldata = &aphy->caldata; ath_print(common, ATH_DBG_CONFIG, - "(%u MHz) -> (%u MHz), conf_is_ht40: %d\n", + "(%u MHz) -> (%u MHz), conf_is_ht40: %d fastcc: %d\n", sc->sc_ah->curchan->channel, - channel->center_freq, conf_is_ht40(conf)); + channel->center_freq, conf_is_ht40(conf), + fastcc); spin_lock_bh(&sc->sc_resetlock); -- cgit v1.2.3 From 6252fcb9f802a992c697edf105e30271a4ae3246 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 2 Aug 2010 15:53:12 +0200 Subject: ath9k: add a separate debug level for stuck beacons Stuck beacons are a useful indicator for debugging various PHY issues such as calibration. Putting them on the same debug level as the other beacon stuff makes it hard to spot them in huge amounts of spam. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/beacon.c | 6 +++--- drivers/net/wireless/ath/debug.h | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 4d4b22d52dfd..102f1234f794 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -359,11 +359,11 @@ void ath_beacon_tasklet(unsigned long data) sc->beacon.bmisscnt++; if (sc->beacon.bmisscnt < BSTUCK_THRESH) { - ath_print(common, ATH_DBG_BEACON, + ath_print(common, ATH_DBG_BSTUCK, "missed %u consecutive beacons\n", sc->beacon.bmisscnt); } else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) { - ath_print(common, ATH_DBG_BEACON, + ath_print(common, ATH_DBG_BSTUCK, "beacon is officially stuck\n"); sc->sc_flags |= SC_OP_TSF_RESET; ath_reset(sc, false); @@ -373,7 +373,7 @@ void ath_beacon_tasklet(unsigned long data) } if (sc->beacon.bmisscnt != 0) { - ath_print(common, ATH_DBG_BEACON, + ath_print(common, ATH_DBG_BSTUCK, "resume beacon xmit after %u misses\n", sc->beacon.bmisscnt); sc->beacon.bmisscnt = 0; diff --git a/drivers/net/wireless/ath/debug.h b/drivers/net/wireless/ath/debug.h index 873bf526e11f..fd3a020682dc 100644 --- a/drivers/net/wireless/ath/debug.h +++ b/drivers/net/wireless/ath/debug.h @@ -36,6 +36,7 @@ * @ATH_DBG_PS: power save processing * @ATH_DBG_HWTIMER: hardware timer handling * @ATH_DBG_BTCOEX: bluetooth coexistance + * @ATH_DBG_BSTUCK: stuck beacons * @ATH_DBG_ANY: enable all debugging * * The debug level is used to control the amount and type of debugging output @@ -60,6 +61,7 @@ enum ATH_DEBUG { ATH_DBG_HWTIMER = 0x00001000, ATH_DBG_BTCOEX = 0x00002000, ATH_DBG_WMI = 0x00004000, + ATH_DBG_BSTUCK = 0x00008000, ATH_DBG_ANY = 0xffffffff }; -- cgit v1.2.3 From 2292ca6d783d3c79b58591bb3ee795bf0144e5ff Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 2 Aug 2010 15:53:13 +0200 Subject: ath9k_hw: apply the noise floor validation to the median instead of single Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/calib.c | 51 +++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 19 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 45208690c0ec..ccb1b2eae85e 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -19,8 +19,7 @@ /* Common calibration code */ -/* We can tune this as we go by monitoring really low values */ -#define ATH9K_NF_TOO_LOW -60 +#define ATH9K_NF_TOO_HIGH -60 static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer) { @@ -45,11 +44,35 @@ static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer) return nfval; } -static void ath9k_hw_update_nfcal_hist_buffer(struct ath9k_nfcal_hist *h, +static struct ath_nf_limits *ath9k_hw_get_nf_limits(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + struct ath_nf_limits *limit; + + if (!chan || IS_CHAN_2GHZ(chan)) + limit = &ah->nf_2g; + else + limit = &ah->nf_5g; + + return limit; +} + +static s16 ath9k_hw_get_default_nf(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + return ath9k_hw_get_nf_limits(ah, chan)->nominal; +} + + +static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah, + struct ath9k_nfcal_hist *h, int16_t *nfarray) { + struct ath_nf_limits *limit; int i; + limit = ath9k_hw_get_nf_limits(ah, ah->curchan); + for (i = 0; i < NUM_NF_READINGS; i++) { h[i].nfCalBuffer[h[i].currIndex] = nfarray[i]; @@ -63,6 +86,9 @@ static void ath9k_hw_update_nfcal_hist_buffer(struct ath9k_nfcal_hist *h, h[i].privNF = ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer); } + + if (h[i].privNF > limit->max) + h[i].privNF = limit->max; } } @@ -104,19 +130,6 @@ void ath9k_hw_reset_calibration(struct ath_hw *ah, ah->cal_samples = 0; } -static s16 ath9k_hw_get_default_nf(struct ath_hw *ah, - struct ath9k_channel *chan) -{ - struct ath_nf_limits *limit; - - if (!chan || IS_CHAN_2GHZ(chan)) - limit = &ah->nf_2g; - else - limit = &ah->nf_5g; - - return limit->nominal; -} - /* This is done for the currently configured channel */ bool ath9k_hw_reset_calvalid(struct ath_hw *ah) { @@ -277,10 +290,10 @@ static void ath9k_hw_nf_sanitize(struct ath_hw *ah, s16 *nf) "NF calibrated [%s] [chain %d] is %d\n", (i >= 3 ? "ext" : "ctl"), i % 3, nf[i]); - if (nf[i] > limit->max) { + if (nf[i] > ATH9K_NF_TOO_HIGH) { ath_print(common, ATH_DBG_CALIBRATE, "NF[%d] (%d) > MAX (%d), correcting to MAX", - i, nf[i], limit->max); + i, nf[i], ATH9K_NF_TOO_HIGH); nf[i] = limit->max; } else if (nf[i] < limit->min) { ath_print(common, ATH_DBG_CALIBRATE, @@ -326,7 +339,7 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan) h = caldata->nfCalHist; caldata->nfcal_pending = false; - ath9k_hw_update_nfcal_hist_buffer(h, nfarray); + ath9k_hw_update_nfcal_hist_buffer(ah, h, nfarray); caldata->rawNoiseFloor = h[0].privNF; return true; } -- cgit v1.2.3 From 70cf15335eb35d562a4ec6d8860611c87f775cf2 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 2 Aug 2010 15:53:14 +0200 Subject: ath9k: use AP beacon miss as a trigger for fast recalibration When beacons get stuck in AP mode, the most likely cause is interference. Such interference can often go on for a while, and too many consecutive beacon misses can lead to connected clients getting dropped. Since connected clients might not be subjected to the same interference if that happens to be very local, the AP should try to deal with it as good as it can. One way to do this is to trigger an NF calibration with automatic baseband update right after the beacon miss. In my tests with very strong interference, this allowed the AP to continue transmitting beacons after only 2-3 misses, which allows a normal client to stay connected. With some of the newer - really sensitive - chips, the maximum noise floor limit is very low, which can be problematic during very strong interference. To avoid an endless loop of stuck beacons -> nfcal -> periodic calibration -> stuck beacons, the beacon miss event also sets a flag, which allows the calibration code to bypass the chip specific maximum NF value. This flag is automatically cleared, as soon as the first NF median goes back below the limits for all chains. In my tests, this allowed an ath9k AP to survive very strong interference (measured NF: -68, or sometimes even higher) without losing connectivity to its clients. Even under these conditions, I was able to transmit several mbits/s through the interface. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/beacon.c | 1 + drivers/net/wireless/ath/ath9k/calib.c | 66 +++++++++++++++++++++++++++++++-- drivers/net/wireless/ath/ath9k/calib.h | 1 + drivers/net/wireless/ath/ath9k/hw.h | 1 + 4 files changed, 65 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 102f1234f794..081192e78a46 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -362,6 +362,7 @@ void ath_beacon_tasklet(unsigned long data) ath_print(common, ATH_DBG_BSTUCK, "missed %u consecutive beacons\n", sc->beacon.bmisscnt); + ath9k_hw_bstuck_nfcal(ah); } else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) { ath_print(common, ATH_DBG_BSTUCK, "beacon is officially stuck\n"); diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index ccb1b2eae85e..67ee5d735cc1 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -65,12 +65,16 @@ static s16 ath9k_hw_get_default_nf(struct ath_hw *ah, static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah, - struct ath9k_nfcal_hist *h, + struct ath9k_hw_cal_data *cal, int16_t *nfarray) { + struct ath_common *common = ath9k_hw_common(ah); struct ath_nf_limits *limit; + struct ath9k_nfcal_hist *h; + bool high_nf_mid = false; int i; + h = cal->nfCalHist; limit = ath9k_hw_get_nf_limits(ah, ah->curchan); for (i = 0; i < NUM_NF_READINGS; i++) { @@ -87,9 +91,38 @@ static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah, ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer); } - if (h[i].privNF > limit->max) - h[i].privNF = limit->max; + if (!h[i].privNF) + continue; + + if (h[i].privNF > limit->max) { + high_nf_mid = true; + + ath_print(common, ATH_DBG_CALIBRATE, + "NFmid[%d] (%d) > MAX (%d), %s\n", + i, h[i].privNF, limit->max, + (cal->nfcal_interference ? + "not corrected (due to interference)" : + "correcting to MAX")); + + /* + * Normally we limit the average noise floor by the + * hardware specific maximum here. However if we have + * encountered stuck beacons because of interference, + * we bypass this limit here in order to better deal + * with our environment. + */ + if (!cal->nfcal_interference) + h[i].privNF = limit->max; + } } + + /* + * If the noise floor seems normal for all chains, assume that + * there is no significant interference in the environment anymore. + * Re-enable the enforcement of the NF maximum again. + */ + if (!high_nf_mid) + cal->nfcal_interference = false; } static bool ath9k_hw_get_nf_thresh(struct ath_hw *ah, @@ -339,7 +372,7 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan) h = caldata->nfCalHist; caldata->nfcal_pending = false; - ath9k_hw_update_nfcal_hist_buffer(ah, h, nfarray); + ath9k_hw_update_nfcal_hist_buffer(ah, caldata, nfarray); caldata->rawNoiseFloor = h[0].privNF; return true; } @@ -374,3 +407,28 @@ s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan) return ah->caldata->rawNoiseFloor; } EXPORT_SYMBOL(ath9k_hw_getchan_noise); + +void ath9k_hw_bstuck_nfcal(struct ath_hw *ah) +{ + struct ath9k_hw_cal_data *caldata = ah->caldata; + + if (unlikely(!caldata)) + return; + + /* + * If beacons are stuck, the most likely cause is interference. + * Triggering a noise floor calibration at this point helps the + * hardware adapt to a noisy environment much faster. + * To ensure that we recover from stuck beacons quickly, let + * the baseband update the internal NF value itself, similar to + * what is being done after a full reset. + */ + if (!caldata->nfcal_pending) + ath9k_hw_start_nfcal(ah, true); + else if (!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF)) + ath9k_hw_getnf(ah, ah->curchan); + + caldata->nfcal_interference = true; +} +EXPORT_SYMBOL(ath9k_hw_bstuck_nfcal); + diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h index 0a304b3eeeb6..5b053a6260b2 100644 --- a/drivers/net/wireless/ath/ath9k/calib.h +++ b/drivers/net/wireless/ath/ath9k/calib.h @@ -113,6 +113,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan); bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan); void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, struct ath9k_channel *chan); +void ath9k_hw_bstuck_nfcal(struct ath_hw *ah); s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan); void ath9k_hw_reset_calibration(struct ath_hw *ah, struct ath9k_cal_list *currCal); diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 399f7c1283cd..1601dd439890 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -355,6 +355,7 @@ struct ath9k_hw_cal_data { int16_t rawNoiseFloor; bool paprd_done; bool nfcal_pending; + bool nfcal_interference; u16 small_signal_gain[AR9300_MAX_CHAINS]; u32 pa_table[AR9300_MAX_CHAINS][PAPRD_TABLE_SZ]; struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS]; -- cgit v1.2.3 From 6044474ed86484b899d46e040ade3872e83fc9f7 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 2 Aug 2010 15:53:15 +0200 Subject: ath9k: shorten the calibration interval during strong interference When the noise floor limits are being bypassed because of strong interference, sensitivity is also reduced. In order to recover from this as quickly as possible, trigger a long periodic calibration every second instead of every 30 seconds, until the NF median is within limits again. This is especially important if the interference lasts for a while, since it takes multiple clean NF calibrations to bring the median back to normal. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 + drivers/net/wireless/ath/ath9k/main.c | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 07f26ee7a723..def0cd308fdd 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -423,6 +423,7 @@ int ath_beaconq_config(struct ath_softc *sc); #define ATH_AP_SHORT_CALINTERVAL 100 /* 100 ms */ #define ATH_ANI_POLLINTERVAL_OLD 100 /* 100 ms */ #define ATH_ANI_POLLINTERVAL_NEW 1000 /* 1000 ms */ +#define ATH_LONG_CALINTERVAL_INT 1000 /* 1000 ms */ #define ATH_LONG_CALINTERVAL 30000 /* 30 seconds */ #define ATH_RESTART_CALINTERVAL 1200000 /* 20 minutes */ diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 2ab3df22fbb5..a3b0ea90439d 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -396,7 +396,12 @@ void ath_ani_calibrate(unsigned long data) bool shortcal = false; bool aniflag = false; unsigned int timestamp = jiffies_to_msecs(jiffies); - u32 cal_interval, short_cal_interval; + u32 cal_interval, short_cal_interval, long_cal_interval; + + if (ah->caldata && ah->caldata->nfcal_interference) + long_cal_interval = ATH_LONG_CALINTERVAL_INT; + else + long_cal_interval = ATH_LONG_CALINTERVAL; short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ? ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL; @@ -408,7 +413,7 @@ void ath_ani_calibrate(unsigned long data) ath9k_ps_wakeup(sc); /* Long calibration runs independently of short calibration. */ - if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) { + if ((timestamp - common->ani.longcal_timer) >= long_cal_interval) { longcal = true; ath_print(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies); common->ani.longcal_timer = timestamp; -- cgit v1.2.3 From 6fe140024ff66d7eccde1a151a08c262dc7a1e6f Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 5 Aug 2010 22:26:56 +0200 Subject: drivers/net/wireless/ath/ath9k: Adjust confusing if indentation Outdent the code following the if. The semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @r disable braces4@ position p1,p2; statement S1,S2; @@ ( if (...) { ... } | if (...) S1@p1 S2@p2 ) @script:python@ p1 << r.p1; p2 << r.p2; @@ if (p1[0].column == p2[0].column): cocci.print_main("branch",p1) cocci.print_secs("after",p2) // Signed-off-by: Julia Lawall Acked-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c index 5b995bee70ae..a462da23e87e 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c @@ -185,7 +185,7 @@ static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) ath_print(common, ATH_DBG_INTERRUPT, "AR_INTR_SYNC_LOCAL_TIMEOUT\n"); - REG_WRITE(ah, AR_INTR_SYNC_CAUSE_CLR, sync_cause); + REG_WRITE(ah, AR_INTR_SYNC_CAUSE_CLR, sync_cause); (void) REG_READ(ah, AR_INTR_SYNC_CAUSE_CLR); } -- cgit v1.2.3 From c17512d846a4b063c8d3e708d82c0664d9c7182e Mon Sep 17 00:00:00 2001 From: Luis R. Rodriguez Date: Thu, 5 Aug 2010 17:56:54 -0400 Subject: ath9k: remove duplicate get_hw_crypto_keytype() Use ath9k_cmn_get_hw_crypto_keytype() instead which is already exported and shared, and does exactly the same thing. Reported-by: Johannes Berg Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/xmit.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 4dda14e36227..457f07692ac7 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1407,22 +1407,6 @@ static enum ath9k_pkt_type get_hw_packet_type(struct sk_buff *skb) return htype; } -static int get_hw_crypto_keytype(struct sk_buff *skb) -{ - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - - if (tx_info->control.hw_key) { - if (tx_info->control.hw_key->alg == ALG_WEP) - return ATH9K_KEY_TYPE_WEP; - else if (tx_info->control.hw_key->alg == ALG_TKIP) - return ATH9K_KEY_TYPE_TKIP; - else if (tx_info->control.hw_key->alg == ALG_CCMP) - return ATH9K_KEY_TYPE_AES; - } - - return ATH9K_KEY_TYPE_CLEAR; -} - static void assign_aggr_tid_seqno(struct sk_buff *skb, struct ath_buf *bf) { @@ -1661,7 +1645,7 @@ static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf, bf->bf_state.bfs_paprd_timestamp = jiffies; bf->bf_flags = setup_tx_flags(skb, use_ldpc); - bf->bf_keytype = get_hw_crypto_keytype(skb); + bf->bf_keytype = ath9k_cmn_get_hw_crypto_keytype(skb); if (bf->bf_keytype != ATH9K_KEY_TYPE_CLEAR) { bf->bf_frmlen += tx_info->control.hw_key->icv_len; bf->bf_keyix = tx_info->control.hw_key->hw_key_idx; -- cgit v1.2.3 From 97359d1235eaf634fe706c9faa6e40181cc95fb8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 10 Aug 2010 09:46:38 +0200 Subject: mac80211: use cipher suite selectors Currently, mac80211 translates the cfg80211 cipher suite selectors into ALG_* values. That isn't all too useful, and some drivers benefit from the distinction between WEP40 and WEP104 as well. Therefore, convert it all to use the cipher suite selectors. Signed-off-by: Johannes Berg Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/at76c50x-usb.c | 7 ++-- drivers/net/wireless/ath/ar9170/main.c | 31 ++++++++------- drivers/net/wireless/ath/ath5k/base.c | 9 +++-- drivers/net/wireless/ath/ath5k/pcu.c | 19 ++++----- drivers/net/wireless/ath/ath9k/common.c | 36 ++++++++++-------- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 5 ++- drivers/net/wireless/ath/ath9k/main.c | 5 ++- drivers/net/wireless/b43/main.c | 16 ++++---- drivers/net/wireless/iwlwifi/iwl-agn-tx.c | 16 ++++---- drivers/net/wireless/iwlwifi/iwl-agn.c | 4 +- drivers/net/wireless/iwlwifi/iwl-dev.h | 2 +- drivers/net/wireless/iwlwifi/iwl-sta.c | 24 ++++++------ drivers/net/wireless/iwlwifi/iwl3945-base.c | 43 ++++++++++++--------- drivers/net/wireless/p54/main.c | 9 +++-- drivers/net/wireless/p54/txrx.c | 17 +++++---- drivers/net/wireless/rt2x00/rt2500usb.c | 4 +- drivers/net/wireless/rt2x00/rt2x00crypto.c | 17 ++++----- drivers/net/wireless/wl12xx/wl1251_main.c | 13 ++++--- drivers/net/wireless/wl12xx/wl1251_tx.c | 4 +- drivers/net/wireless/wl12xx/wl1271_main.c | 13 ++++--- drivers/net/wireless/wl12xx/wl1271_tx.c | 4 +- include/net/mac80211.h | 18 +-------- net/mac80211/cfg.c | 44 ++++++--------------- net/mac80211/debugfs_key.c | 55 +++++++++++---------------- net/mac80211/driver-trace.h | 4 +- net/mac80211/key.c | 25 ++++++------ net/mac80211/key.h | 4 +- net/mac80211/rx.c | 18 +++++---- net/mac80211/tx.c | 22 ++++++----- net/mac80211/wep.c | 2 +- net/mac80211/wpa.c | 6 +-- 31 files changed, 236 insertions(+), 260 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index d5140a87f073..a267bf55574c 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -2061,11 +2061,12 @@ static int at76_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, int i; - at76_dbg(DBG_MAC80211, "%s(): cmd %d key->alg %d key->keyidx %d " + at76_dbg(DBG_MAC80211, "%s(): cmd %d key->cipher %d key->keyidx %d " "key->keylen %d", - __func__, cmd, key->alg, key->keyidx, key->keylen); + __func__, cmd, key->cipher, key->keyidx, key->keylen); - if (key->alg != ALG_WEP) + if ((key->cipher != WLAN_CIPHER_SUITE_WEP40) && + (key->cipher != WLAN_CIPHER_SUITE_WEP104)) return -EOPNOTSUPP; key->hw_key_idx = key->keyidx; diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c index c67b05f3bcbd..29d2253ad7e4 100644 --- a/drivers/net/wireless/ath/ar9170/main.c +++ b/drivers/net/wireless/ath/ar9170/main.c @@ -1190,14 +1190,13 @@ static int ar9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb) if (info->control.hw_key) { icv = info->control.hw_key->icv_len; - switch (info->control.hw_key->alg) { - case ALG_WEP: + switch (info->control.hw_key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + case WLAN_CIPHER_SUITE_TKIP: keytype = AR9170_TX_MAC_ENCR_RC4; break; - case ALG_TKIP: - keytype = AR9170_TX_MAC_ENCR_RC4; - break; - case ALG_CCMP: + case WLAN_CIPHER_SUITE_CCMP: keytype = AR9170_TX_MAC_ENCR_AES; break; default: @@ -1778,17 +1777,17 @@ static int ar9170_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if ((!ar->vif) || (ar->disable_offload)) return -EOPNOTSUPP; - switch (key->alg) { - case ALG_WEP: - if (key->keylen == WLAN_KEY_LEN_WEP40) - ktype = AR9170_ENC_ALG_WEP64; - else - ktype = AR9170_ENC_ALG_WEP128; + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + ktype = AR9170_ENC_ALG_WEP64; + break; + case WLAN_CIPHER_SUITE_WEP104: + ktype = AR9170_ENC_ALG_WEP128; break; - case ALG_TKIP: + case WLAN_CIPHER_SUITE_TKIP: ktype = AR9170_ENC_ALG_TKIP; break; - case ALG_CCMP: + case WLAN_CIPHER_SUITE_CCMP: ktype = AR9170_ENC_ALG_AESCCMP; break; default: @@ -1827,7 +1826,7 @@ static int ar9170_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (err) goto out; - if (key->alg == ALG_TKIP) { + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) { err = ar9170_upload_key(ar, i, sta ? sta->addr : NULL, ktype, 1, key->key + 16, 16); if (err) @@ -1864,7 +1863,7 @@ static int ar9170_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (err) goto out; - if (key->alg == ALG_TKIP) { + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) { err = ar9170_upload_key(ar, key->hw_key_idx, NULL, AR9170_ENC_ALG_NONE, 1, diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 373dcfec689c..a729b8774670 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -3297,11 +3297,12 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (sc->opmode == NL80211_IFTYPE_AP) return -EOPNOTSUPP; - switch (key->alg) { - case ALG_WEP: - case ALG_TKIP: + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + case WLAN_CIPHER_SUITE_TKIP: break; - case ALG_CCMP: + case WLAN_CIPHER_SUITE_CCMP: if (sc->ah->ah_aes_support) break; diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c index 86fdb6ddfaaa..af273358c7cb 100644 --- a/drivers/net/wireless/ath/ath5k/pcu.c +++ b/drivers/net/wireless/ath/ath5k/pcu.c @@ -695,21 +695,18 @@ int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry) static int ath5k_keycache_type(const struct ieee80211_key_conf *key) { - switch (key->alg) { - case ALG_TKIP: + switch (key->cipher) { + case WLAN_CIPHER_SUITE_TKIP: return AR5K_KEYTABLE_TYPE_TKIP; - case ALG_CCMP: + case WLAN_CIPHER_SUITE_CCMP: return AR5K_KEYTABLE_TYPE_CCM; - case ALG_WEP: - if (key->keylen == WLAN_KEY_LEN_WEP40) - return AR5K_KEYTABLE_TYPE_40; - else if (key->keylen == WLAN_KEY_LEN_WEP104) - return AR5K_KEYTABLE_TYPE_104; - return -EINVAL; + case WLAN_CIPHER_SUITE_WEP40: + return AR5K_KEYTABLE_TYPE_40; + case WLAN_CIPHER_SUITE_WEP104: + return AR5K_KEYTABLE_TYPE_104; default: return -EINVAL; } - return -EINVAL; } /* @@ -728,7 +725,7 @@ int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry, bool is_tkip; const u8 *key_ptr; - is_tkip = (key->alg == ALG_TKIP); + is_tkip = (key->cipher == WLAN_CIPHER_SUITE_TKIP); /* * key->keylen comes in from mac80211 in bytes. diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c index c86f7d3593ab..3100c87a4fcd 100644 --- a/drivers/net/wireless/ath/ath9k/common.c +++ b/drivers/net/wireless/ath/ath9k/common.c @@ -46,12 +46,17 @@ int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb) struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); if (tx_info->control.hw_key) { - if (tx_info->control.hw_key->alg == ALG_WEP) + switch (tx_info->control.hw_key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: return ATH9K_KEY_TYPE_WEP; - else if (tx_info->control.hw_key->alg == ALG_TKIP) + case WLAN_CIPHER_SUITE_TKIP: return ATH9K_KEY_TYPE_TKIP; - else if (tx_info->control.hw_key->alg == ALG_CCMP) + case WLAN_CIPHER_SUITE_CCMP: return ATH9K_KEY_TYPE_AES; + default: + break; + } } return ATH9K_KEY_TYPE_CLEAR; @@ -212,11 +217,11 @@ static int ath_reserve_key_cache_slot_tkip(struct ath_common *common) } static int ath_reserve_key_cache_slot(struct ath_common *common, - enum ieee80211_key_alg alg) + u32 cipher) { int i; - if (alg == ALG_TKIP) + if (cipher == WLAN_CIPHER_SUITE_TKIP) return ath_reserve_key_cache_slot_tkip(common); /* First, try to find slots that would not be available for TKIP. */ @@ -293,14 +298,15 @@ int ath9k_cmn_key_config(struct ath_common *common, memset(&hk, 0, sizeof(hk)); - switch (key->alg) { - case ALG_WEP: + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: hk.kv_type = ATH9K_CIPHER_WEP; break; - case ALG_TKIP: + case WLAN_CIPHER_SUITE_TKIP: hk.kv_type = ATH9K_CIPHER_TKIP; break; - case ALG_CCMP: + case WLAN_CIPHER_SUITE_CCMP: hk.kv_type = ATH9K_CIPHER_AES_CCM; break; default: @@ -316,7 +322,7 @@ int ath9k_cmn_key_config(struct ath_common *common, memcpy(gmac, vif->addr, ETH_ALEN); gmac[0] |= 0x01; mac = gmac; - idx = ath_reserve_key_cache_slot(common, key->alg); + idx = ath_reserve_key_cache_slot(common, key->cipher); break; case NL80211_IFTYPE_ADHOC: if (!sta) { @@ -326,7 +332,7 @@ int ath9k_cmn_key_config(struct ath_common *common, memcpy(gmac, sta->addr, ETH_ALEN); gmac[0] |= 0x01; mac = gmac; - idx = ath_reserve_key_cache_slot(common, key->alg); + idx = ath_reserve_key_cache_slot(common, key->cipher); break; default: idx = key->keyidx; @@ -348,13 +354,13 @@ int ath9k_cmn_key_config(struct ath_common *common, return -EOPNOTSUPP; mac = sta->addr; - idx = ath_reserve_key_cache_slot(common, key->alg); + idx = ath_reserve_key_cache_slot(common, key->cipher); } if (idx < 0) return -ENOSPC; /* no free key cache entries */ - if (key->alg == ALG_TKIP) + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) ret = ath_setkey_tkip(common, idx, key->key, &hk, mac, vif->type == NL80211_IFTYPE_AP); else @@ -364,7 +370,7 @@ int ath9k_cmn_key_config(struct ath_common *common, return -EIO; set_bit(idx, common->keymap); - if (key->alg == ALG_TKIP) { + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) { set_bit(idx + 64, common->keymap); if (common->splitmic) { set_bit(idx + 32, common->keymap); @@ -389,7 +395,7 @@ void ath9k_cmn_key_delete(struct ath_common *common, return; clear_bit(key->hw_key_idx, common->keymap); - if (key->alg != ALG_TKIP) + if (key->cipher != WLAN_CIPHER_SUITE_TKIP) return; clear_bit(key->hw_key_idx + 64, common->keymap); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 7d09b4b17bbd..4e345be62435 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1585,9 +1585,10 @@ static int ath9k_htc_set_key(struct ieee80211_hw *hw, key->hw_key_idx = ret; /* push IV and Michael MIC generation to stack */ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - if (key->alg == ALG_TKIP) + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; - if (priv->ah->sw_mgmt_crypto && key->alg == ALG_CCMP) + if (priv->ah->sw_mgmt_crypto && + key->cipher == WLAN_CIPHER_SUITE_CCMP) key->flags |= IEEE80211_KEY_FLAG_SW_MGMT; ret = 0; } diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index a3b0ea90439d..1165f909ef04 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1782,9 +1782,10 @@ static int ath9k_set_key(struct ieee80211_hw *hw, key->hw_key_idx = ret; /* push IV and Michael MIC generation to stack */ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - if (key->alg == ALG_TKIP) + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; - if (sc->sc_ah->sw_mgmt_crypto && key->alg == ALG_CCMP) + if (sc->sc_ah->sw_mgmt_crypto && + key->cipher == WLAN_CIPHER_SUITE_CCMP) key->flags |= IEEE80211_KEY_FLAG_SW_MGMT; ret = 0; } diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index a3e2f2bfe3a7..a1186525c70d 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3759,17 +3759,17 @@ static int b43_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, } err = -EINVAL; - switch (key->alg) { - case ALG_WEP: - if (key->keylen == WLAN_KEY_LEN_WEP40) - algorithm = B43_SEC_ALGO_WEP40; - else - algorithm = B43_SEC_ALGO_WEP104; + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + algorithm = B43_SEC_ALGO_WEP40; + break; + case WLAN_CIPHER_SUITE_WEP104: + algorithm = B43_SEC_ALGO_WEP104; break; - case ALG_TKIP: + case WLAN_CIPHER_SUITE_TKIP: algorithm = B43_SEC_ALGO_TKIP; break; - case ALG_CCMP: + case WLAN_CIPHER_SUITE_CCMP: algorithm = B43_SEC_ALGO_AES; break; default: diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index 69155aa448fb..3fc982e87921 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c @@ -470,8 +470,8 @@ static void iwlagn_tx_cmd_build_hwcrypto(struct iwl_priv *priv, { struct ieee80211_key_conf *keyconf = info->control.hw_key; - switch (keyconf->alg) { - case ALG_CCMP: + switch (keyconf->cipher) { + case WLAN_CIPHER_SUITE_CCMP: tx_cmd->sec_ctl = TX_CMD_SEC_CCM; memcpy(tx_cmd->key, keyconf->key, keyconf->keylen); if (info->flags & IEEE80211_TX_CTL_AMPDU) @@ -479,20 +479,20 @@ static void iwlagn_tx_cmd_build_hwcrypto(struct iwl_priv *priv, IWL_DEBUG_TX(priv, "tx_cmd with AES hwcrypto\n"); break; - case ALG_TKIP: + case WLAN_CIPHER_SUITE_TKIP: tx_cmd->sec_ctl = TX_CMD_SEC_TKIP; ieee80211_get_tkip_key(keyconf, skb_frag, IEEE80211_TKIP_P2_KEY, tx_cmd->key); IWL_DEBUG_TX(priv, "tx_cmd with tkip hwcrypto\n"); break; - case ALG_WEP: + case WLAN_CIPHER_SUITE_WEP104: + tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; + /* fall through */ + case WLAN_CIPHER_SUITE_WEP40: tx_cmd->sec_ctl |= (TX_CMD_SEC_WEP | (keyconf->keyidx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT); - if (keyconf->keylen == WEP_KEY_LEN_128) - tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; - memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen); IWL_DEBUG_TX(priv, "Configuring packet for WEP encryption " @@ -500,7 +500,7 @@ static void iwlagn_tx_cmd_build_hwcrypto(struct iwl_priv *priv, break; default: - IWL_ERR(priv, "Unknown encode alg %d\n", keyconf->alg); + IWL_ERR(priv, "Unknown encode cipher %x\n", keyconf->cipher); break; } } diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 43e078b87378..21a52ae05c0d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -3389,7 +3389,9 @@ static int iwl_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, * in 1X mode. * In legacy wep mode, we use another host command to the uCode. */ - if (key->alg == ALG_WEP && !sta && vif->type != NL80211_IFTYPE_AP) { + if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || + key->cipher == WLAN_CIPHER_SUITE_WEP104) && + !sta && vif->type != NL80211_IFTYPE_AP) { if (cmd == SET_KEY) is_default_wep_key = !priv->key_mapping_key; else diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 599635547021..149619fb1c07 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -420,7 +420,7 @@ struct iwl_tid_data { }; struct iwl_hw_key { - enum ieee80211_key_alg alg; + u32 cipher; int keylen; u8 keyidx; u8 key[32]; diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c index 7e0829be5e78..d5e8db37b86e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sta.c +++ b/drivers/net/wireless/iwlwifi/iwl-sta.c @@ -818,7 +818,7 @@ int iwl_set_default_wep_key(struct iwl_priv *priv, keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV; keyconf->hw_key_idx = HW_KEY_DEFAULT; - priv->stations[IWL_AP_ID].keyinfo.alg = ALG_WEP; + priv->stations[IWL_AP_ID].keyinfo.cipher = keyconf->cipher; priv->wep_keys[keyconf->keyidx].key_size = keyconf->keylen; memcpy(&priv->wep_keys[keyconf->keyidx].key, &keyconf->key, @@ -856,7 +856,7 @@ static int iwl_set_wep_dynamic_key_info(struct iwl_priv *priv, spin_lock_irqsave(&priv->sta_lock, flags); - priv->stations[sta_id].keyinfo.alg = keyconf->alg; + priv->stations[sta_id].keyinfo.cipher = keyconf->cipher; priv->stations[sta_id].keyinfo.keylen = keyconf->keylen; priv->stations[sta_id].keyinfo.keyidx = keyconf->keyidx; @@ -906,7 +906,7 @@ static int iwl_set_ccmp_dynamic_key_info(struct iwl_priv *priv, keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; spin_lock_irqsave(&priv->sta_lock, flags); - priv->stations[sta_id].keyinfo.alg = keyconf->alg; + priv->stations[sta_id].keyinfo.cipher = keyconf->cipher; priv->stations[sta_id].keyinfo.keylen = keyconf->keylen; memcpy(priv->stations[sta_id].keyinfo.key, keyconf->key, @@ -955,7 +955,7 @@ static int iwl_set_tkip_dynamic_key_info(struct iwl_priv *priv, spin_lock_irqsave(&priv->sta_lock, flags); - priv->stations[sta_id].keyinfo.alg = keyconf->alg; + priv->stations[sta_id].keyinfo.cipher = keyconf->cipher; priv->stations[sta_id].keyinfo.keylen = 16; if ((priv->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_ENCRYPT_MSK) @@ -1090,24 +1090,26 @@ int iwl_set_dynamic_key(struct iwl_priv *priv, priv->key_mapping_key++; keyconf->hw_key_idx = HW_KEY_DYNAMIC; - switch (keyconf->alg) { - case ALG_CCMP: + switch (keyconf->cipher) { + case WLAN_CIPHER_SUITE_CCMP: ret = iwl_set_ccmp_dynamic_key_info(priv, keyconf, sta_id); break; - case ALG_TKIP: + case WLAN_CIPHER_SUITE_TKIP: ret = iwl_set_tkip_dynamic_key_info(priv, keyconf, sta_id); break; - case ALG_WEP: + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: ret = iwl_set_wep_dynamic_key_info(priv, keyconf, sta_id); break; default: IWL_ERR(priv, - "Unknown alg: %s alg = %d\n", __func__, keyconf->alg); + "Unknown alg: %s cipher = %x\n", __func__, + keyconf->cipher); ret = -EINVAL; } - IWL_DEBUG_WEP(priv, "Set dynamic key: alg= %d len=%d idx=%d sta=%d ret=%d\n", - keyconf->alg, keyconf->keylen, keyconf->keyidx, + IWL_DEBUG_WEP(priv, "Set dynamic key: cipher=%x len=%d idx=%d sta=%d ret=%d\n", + keyconf->cipher, keyconf->keylen, keyconf->keyidx, sta_id, ret); return ret; diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index b14eaf91c7c2..faa2e0037e10 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -152,7 +152,7 @@ static int iwl3945_set_ccmp_dynamic_key_info(struct iwl_priv *priv, key_flags &= ~STA_KEY_FLG_INVALID; spin_lock_irqsave(&priv->sta_lock, flags); - priv->stations[sta_id].keyinfo.alg = keyconf->alg; + priv->stations[sta_id].keyinfo.cipher = keyconf->cipher; priv->stations[sta_id].keyinfo.keylen = keyconf->keylen; memcpy(priv->stations[sta_id].keyinfo.key, keyconf->key, keyconf->keylen); @@ -223,23 +223,25 @@ static int iwl3945_set_dynamic_key(struct iwl_priv *priv, keyconf->hw_key_idx = HW_KEY_DYNAMIC; - switch (keyconf->alg) { - case ALG_CCMP: + switch (keyconf->cipher) { + case WLAN_CIPHER_SUITE_CCMP: ret = iwl3945_set_ccmp_dynamic_key_info(priv, keyconf, sta_id); break; - case ALG_TKIP: + case WLAN_CIPHER_SUITE_TKIP: ret = iwl3945_set_tkip_dynamic_key_info(priv, keyconf, sta_id); break; - case ALG_WEP: + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: ret = iwl3945_set_wep_dynamic_key_info(priv, keyconf, sta_id); break; default: - IWL_ERR(priv, "Unknown alg: %s alg = %d\n", __func__, keyconf->alg); + IWL_ERR(priv, "Unknown alg: %s alg=%x\n", __func__, + keyconf->cipher); ret = -EINVAL; } - IWL_DEBUG_WEP(priv, "Set dynamic key: alg= %d len=%d idx=%d sta=%d ret=%d\n", - keyconf->alg, keyconf->keylen, keyconf->keyidx, + IWL_DEBUG_WEP(priv, "Set dynamic key: alg=%x len=%d idx=%d sta=%d ret=%d\n", + keyconf->cipher, keyconf->keylen, keyconf->keyidx, sta_id, ret); return ret; @@ -255,10 +257,11 @@ static int iwl3945_remove_static_key(struct iwl_priv *priv) static int iwl3945_set_static_key(struct iwl_priv *priv, struct ieee80211_key_conf *key) { - if (key->alg == ALG_WEP) + if (key->cipher == WLAN_CIPHER_SUITE_WEP40 || + key->cipher == WLAN_CIPHER_SUITE_WEP104) return -EOPNOTSUPP; - IWL_ERR(priv, "Static key invalid: alg %d\n", key->alg); + IWL_ERR(priv, "Static key invalid: cipher %x\n", key->cipher); return -EINVAL; } @@ -370,23 +373,25 @@ static void iwl3945_build_tx_cmd_hwcrypto(struct iwl_priv *priv, struct iwl3945_tx_cmd *tx_cmd = (struct iwl3945_tx_cmd *)cmd->cmd.payload; struct iwl_hw_key *keyinfo = &priv->stations[sta_id].keyinfo; - switch (keyinfo->alg) { - case ALG_CCMP: + tx_cmd->sec_ctl = 0; + + switch (keyinfo->cipher) { + case WLAN_CIPHER_SUITE_CCMP: tx_cmd->sec_ctl = TX_CMD_SEC_CCM; memcpy(tx_cmd->key, keyinfo->key, keyinfo->keylen); IWL_DEBUG_TX(priv, "tx_cmd with AES hwcrypto\n"); break; - case ALG_TKIP: + case WLAN_CIPHER_SUITE_TKIP: break; - case ALG_WEP: - tx_cmd->sec_ctl = TX_CMD_SEC_WEP | + case WLAN_CIPHER_SUITE_WEP104: + tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; + /* fall through */ + case WLAN_CIPHER_SUITE_WEP40: + tx_cmd->sec_ctl |= TX_CMD_SEC_WEP | (info->control.hw_key->hw_key_idx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT; - if (keyinfo->keylen == 13) - tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; - memcpy(&tx_cmd->key[3], keyinfo->key, keyinfo->keylen); IWL_DEBUG_TX(priv, "Configuring packet for WEP encryption " @@ -394,7 +399,7 @@ static void iwl3945_build_tx_cmd_hwcrypto(struct iwl_priv *priv, break; default: - IWL_ERR(priv, "Unknown encode alg %d\n", keyinfo->alg); + IWL_ERR(priv, "Unknown encode cipher %x\n", keyinfo->cipher); break; } } diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index 47db439b63bf..622d27b6d8f2 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -429,8 +429,8 @@ static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, mutex_lock(&priv->conf_mutex); if (cmd == SET_KEY) { - switch (key->alg) { - case ALG_TKIP: + switch (key->cipher) { + case WLAN_CIPHER_SUITE_TKIP: if (!(priv->privacy_caps & (BR_DESC_PRIV_CAP_MICHAEL | BR_DESC_PRIV_CAP_TKIP))) { ret = -EOPNOTSUPP; @@ -439,7 +439,8 @@ static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; algo = P54_CRYPTO_TKIPMICHAEL; break; - case ALG_WEP: + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_WEP)) { ret = -EOPNOTSUPP; goto out_unlock; @@ -447,7 +448,7 @@ static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; algo = P54_CRYPTO_WEP; break; - case ALG_CCMP: + case WLAN_CIPHER_SUITE_CCMP: if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP)) { ret = -EOPNOTSUPP; goto out_unlock; diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index 427b46f558ed..e53f8cec7798 100644 --- a/drivers/net/wireless/p54/txrx.c +++ b/drivers/net/wireless/p54/txrx.c @@ -683,14 +683,15 @@ static void p54_tx_80211_header(struct p54_common *priv, struct sk_buff *skb, } } -static u8 p54_convert_algo(enum ieee80211_key_alg alg) +static u8 p54_convert_algo(u32 cipher) { - switch (alg) { - case ALG_WEP: + switch (cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: return P54_CRYPTO_WEP; - case ALG_TKIP: + case WLAN_CIPHER_SUITE_TKIP: return P54_CRYPTO_TKIPMICHAEL; - case ALG_CCMP: + case WLAN_CIPHER_SUITE_CCMP: return P54_CRYPTO_AESCCMP; default: return 0; @@ -731,7 +732,7 @@ int p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb) if (info->control.hw_key) { crypt_offset = ieee80211_get_hdrlen_from_skb(skb); - if (info->control.hw_key->alg == ALG_TKIP) { + if (info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { u8 *iv = (u8 *)(skb->data + crypt_offset); /* * The firmware excepts that the IV has to have @@ -827,10 +828,10 @@ int p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb) hdr->tries = ridx; txhdr->rts_rate_idx = 0; if (info->control.hw_key) { - txhdr->key_type = p54_convert_algo(info->control.hw_key->alg); + txhdr->key_type = p54_convert_algo(info->control.hw_key->cipher); txhdr->key_len = min((u8)16, info->control.hw_key->keylen); memcpy(txhdr->key, info->control.hw_key->key, txhdr->key_len); - if (info->control.hw_key->alg == ALG_TKIP) { + if (info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { /* reserve space for the MIC key */ len += 8; memcpy(skb_put(skb, 8), &(info->control.hw_key->key diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c index cdaf93f48263..97cf72f8bc12 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/rt2x00/rt2500usb.c @@ -355,7 +355,9 @@ static int rt2500usb_config_key(struct rt2x00_dev *rt2x00dev, * it is known that not work at least on some hardware. * SW crypto will be used in that case. */ - if (key->alg == ALG_WEP && key->keyidx != 0) + if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || + key->cipher == WLAN_CIPHER_SUITE_WEP104) && + key->keyidx != 0) return -EOPNOTSUPP; /* diff --git a/drivers/net/wireless/rt2x00/rt2x00crypto.c b/drivers/net/wireless/rt2x00/rt2x00crypto.c index 583dacd8d241..5e9074bf2b8e 100644 --- a/drivers/net/wireless/rt2x00/rt2x00crypto.c +++ b/drivers/net/wireless/rt2x00/rt2x00crypto.c @@ -31,15 +31,14 @@ enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key) { - switch (key->alg) { - case ALG_WEP: - if (key->keylen == WLAN_KEY_LEN_WEP40) - return CIPHER_WEP64; - else - return CIPHER_WEP128; - case ALG_TKIP: + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + return CIPHER_WEP64; + case WLAN_CIPHER_SUITE_WEP104: + return CIPHER_WEP128; + case WLAN_CIPHER_SUITE_TKIP: return CIPHER_TKIP; - case ALG_CCMP: + case WLAN_CIPHER_SUITE_CCMP: return CIPHER_AES; default: return CIPHER_NONE; @@ -95,7 +94,7 @@ unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev, overhead += key->iv_len; if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) { - if (key->alg == ALG_TKIP) + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) overhead += 8; } diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c index 861a5f33761e..6d31c855fcfe 100644 --- a/drivers/net/wireless/wl12xx/wl1251_main.c +++ b/drivers/net/wireless/wl12xx/wl1251_main.c @@ -725,8 +725,9 @@ static int wl1251_set_key_type(struct wl1251 *wl, struct ieee80211_key_conf *mac80211_key, const u8 *addr) { - switch (mac80211_key->alg) { - case ALG_WEP: + switch (mac80211_key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: if (is_broadcast_ether_addr(addr)) key->key_type = KEY_WEP_DEFAULT; else @@ -734,7 +735,7 @@ static int wl1251_set_key_type(struct wl1251 *wl, mac80211_key->hw_key_idx = mac80211_key->keyidx; break; - case ALG_TKIP: + case WLAN_CIPHER_SUITE_TKIP: if (is_broadcast_ether_addr(addr)) key->key_type = KEY_TKIP_MIC_GROUP; else @@ -742,7 +743,7 @@ static int wl1251_set_key_type(struct wl1251 *wl, mac80211_key->hw_key_idx = mac80211_key->keyidx; break; - case ALG_CCMP: + case WLAN_CIPHER_SUITE_CCMP: if (is_broadcast_ether_addr(addr)) key->key_type = KEY_AES_GROUP; else @@ -750,7 +751,7 @@ static int wl1251_set_key_type(struct wl1251 *wl, mac80211_key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; break; default: - wl1251_error("Unknown key algo 0x%x", mac80211_key->alg); + wl1251_error("Unknown key cipher 0x%x", mac80211_key->cipher); return -EOPNOTSUPP; } @@ -783,7 +784,7 @@ static int wl1251_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, wl1251_debug(DEBUG_CRYPT, "CMD: 0x%x", cmd); wl1251_dump(DEBUG_CRYPT, "ADDR: ", addr, ETH_ALEN); wl1251_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x", - key->alg, key->keyidx, key->keylen, key->flags); + key->cipher, key->keyidx, key->keylen, key->flags); wl1251_dump(DEBUG_CRYPT, "KEY: ", key->key, key->keylen); if (is_zero_ether_addr(addr)) { diff --git a/drivers/net/wireless/wl12xx/wl1251_tx.c b/drivers/net/wireless/wl12xx/wl1251_tx.c index a38ec199187a..6634b3e27cfc 100644 --- a/drivers/net/wireless/wl12xx/wl1251_tx.c +++ b/drivers/net/wireless/wl12xx/wl1251_tx.c @@ -189,7 +189,7 @@ static int wl1251_tx_send_packet(struct wl1251 *wl, struct sk_buff *skb, tx_hdr = (struct tx_double_buffer_desc *) skb->data; if (control->control.hw_key && - control->control.hw_key->alg == ALG_TKIP) { + control->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { int hdrlen; __le16 fc; u16 length; @@ -399,7 +399,7 @@ static void wl1251_tx_packet_cb(struct wl1251 *wl, */ frame = skb_pull(skb, sizeof(struct tx_double_buffer_desc)); if (info->control.hw_key && - info->control.hw_key->alg == ALG_TKIP) { + info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { hdrlen = ieee80211_get_hdrlen_from_skb(skb); memmove(frame + WL1251_TKIP_IV_SPACE, frame, hdrlen); skb_pull(skb, WL1251_TKIP_IV_SPACE); diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index 9d68f0012f05..30194c0f36a9 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -1439,7 +1439,7 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x", cmd); wl1271_dump(DEBUG_CRYPT, "ADDR: ", addr, ETH_ALEN); wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x", - key_conf->alg, key_conf->keyidx, + key_conf->cipher, key_conf->keyidx, key_conf->keylen, key_conf->flags); wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen); @@ -1455,20 +1455,21 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (ret < 0) goto out_unlock; - switch (key_conf->alg) { - case ALG_WEP: + switch (key_conf->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: key_type = KEY_WEP; key_conf->hw_key_idx = key_conf->keyidx; break; - case ALG_TKIP: + case WLAN_CIPHER_SUITE_TKIP: key_type = KEY_TKIP; key_conf->hw_key_idx = key_conf->keyidx; tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq); tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq); break; - case ALG_CCMP: + case WLAN_CIPHER_SUITE_CCMP: key_type = KEY_AES; key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; @@ -1476,7 +1477,7 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq); break; default: - wl1271_error("Unknown key algo 0x%x", key_conf->alg); + wl1271_error("Unknown key algo 0x%x", key_conf->cipher); ret = -EOPNOTSUPP; goto out_sleep; diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.c b/drivers/net/wireless/wl12xx/wl1271_tx.c index c592cc2e9fe8..dc0b46c93c4b 100644 --- a/drivers/net/wireless/wl12xx/wl1271_tx.c +++ b/drivers/net/wireless/wl12xx/wl1271_tx.c @@ -193,7 +193,7 @@ static int wl1271_tx_frame(struct wl1271 *wl, struct sk_buff *skb) info = IEEE80211_SKB_CB(skb); if (info->control.hw_key && - info->control.hw_key->alg == ALG_TKIP) + info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) extra = WL1271_TKIP_IV_SPACE; if (info->control.hw_key) { @@ -347,7 +347,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, /* remove TKIP header space if present */ if (info->control.hw_key && - info->control.hw_key->alg == ALG_TKIP) { + info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { int hdrlen = ieee80211_get_hdrlen_from_skb(skb); memmove(skb->data + WL1271_TKIP_IV_SPACE, skb->data, hdrlen); skb_pull(skb, WL1271_TKIP_IV_SPACE); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 871ed1de736a..914c747ac3a0 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -788,20 +788,6 @@ static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif) return false; } -/** - * enum ieee80211_key_alg - key algorithm - * @ALG_WEP: WEP40 or WEP104 - * @ALG_TKIP: TKIP - * @ALG_CCMP: CCMP (AES) - * @ALG_AES_CMAC: AES-128-CMAC - */ -enum ieee80211_key_alg { - ALG_WEP, - ALG_TKIP, - ALG_CCMP, - ALG_AES_CMAC, -}; - /** * enum ieee80211_key_flags - key flags * @@ -839,7 +825,7 @@ enum ieee80211_key_flags { * @hw_key_idx: To be set by the driver, this is the key index the driver * wants to be given when a frame is transmitted and needs to be * encrypted in hardware. - * @alg: The key algorithm. + * @cipher: The key's cipher suite selector. * @flags: key flags, see &enum ieee80211_key_flags. * @keyidx: the key index (0-3) * @keylen: key material length @@ -852,7 +838,7 @@ enum ieee80211_key_flags { * @iv_len: The IV length for this key type */ struct ieee80211_key_conf { - enum ieee80211_key_alg alg; + u32 cipher; u8 icv_len; u8 iv_len; u8 hw_key_idx; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 19c6146010b7..9a35d9e7efd7 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -116,7 +116,6 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, { struct ieee80211_sub_if_data *sdata; struct sta_info *sta = NULL; - enum ieee80211_key_alg alg; struct ieee80211_key *key; int err; @@ -125,31 +124,20 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); + /* reject WEP and TKIP keys if WEP failed to initialize */ switch (params->cipher) { case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - alg = ALG_WEP; - break; case WLAN_CIPHER_SUITE_TKIP: - alg = ALG_TKIP; - break; - case WLAN_CIPHER_SUITE_CCMP: - alg = ALG_CCMP; - break; - case WLAN_CIPHER_SUITE_AES_CMAC: - alg = ALG_AES_CMAC; + case WLAN_CIPHER_SUITE_WEP104: + if (IS_ERR(sdata->local->wep_tx_tfm)) + return -EINVAL; break; default: - return -EINVAL; + break; } - /* reject WEP and TKIP keys if WEP failed to initialize */ - if ((alg == ALG_WEP || alg == ALG_TKIP) && - IS_ERR(sdata->local->wep_tx_tfm)) - return -EINVAL; - - key = ieee80211_key_alloc(alg, key_idx, params->key_len, params->key, - params->seq_len, params->seq); + key = ieee80211_key_alloc(params->cipher, key_idx, params->key_len, + params->key, params->seq_len, params->seq); if (IS_ERR(key)) return PTR_ERR(key); @@ -247,10 +235,10 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, memset(¶ms, 0, sizeof(params)); - switch (key->conf.alg) { - case ALG_TKIP: - params.cipher = WLAN_CIPHER_SUITE_TKIP; + params.cipher = key->conf.cipher; + switch (key->conf.cipher) { + case WLAN_CIPHER_SUITE_TKIP: iv32 = key->u.tkip.tx.iv32; iv16 = key->u.tkip.tx.iv16; @@ -268,8 +256,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, params.seq = seq; params.seq_len = 6; break; - case ALG_CCMP: - params.cipher = WLAN_CIPHER_SUITE_CCMP; + case WLAN_CIPHER_SUITE_CCMP: seq[0] = key->u.ccmp.tx_pn[5]; seq[1] = key->u.ccmp.tx_pn[4]; seq[2] = key->u.ccmp.tx_pn[3]; @@ -279,14 +266,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, params.seq = seq; params.seq_len = 6; break; - case ALG_WEP: - if (key->conf.keylen == 5) - params.cipher = WLAN_CIPHER_SUITE_WEP40; - else - params.cipher = WLAN_CIPHER_SUITE_WEP104; - break; - case ALG_AES_CMAC: - params.cipher = WLAN_CIPHER_SUITE_AES_CMAC; + case WLAN_CIPHER_SUITE_AES_CMAC: seq[0] = key->u.aes_cmac.tx_pn[5]; seq[1] = key->u.aes_cmac.tx_pn[4]; seq[2] = key->u.aes_cmac.tx_pn[3]; diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c index fa5e76e658ef..1647f8dc5cda 100644 --- a/net/mac80211/debugfs_key.c +++ b/net/mac80211/debugfs_key.c @@ -64,26 +64,13 @@ static ssize_t key_algorithm_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { - char *alg; + char buf[15]; struct ieee80211_key *key = file->private_data; + u32 c = key->conf.cipher; - switch (key->conf.alg) { - case ALG_WEP: - alg = "WEP\n"; - break; - case ALG_TKIP: - alg = "TKIP\n"; - break; - case ALG_CCMP: - alg = "CCMP\n"; - break; - case ALG_AES_CMAC: - alg = "AES-128-CMAC\n"; - break; - default: - return 0; - } - return simple_read_from_buffer(userbuf, count, ppos, alg, strlen(alg)); + sprintf(buf, "%.2x-%.2x-%.2x:%d\n", + c >> 24, (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff); + return simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf)); } KEY_OPS(algorithm); @@ -95,21 +82,22 @@ static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf, int len; struct ieee80211_key *key = file->private_data; - switch (key->conf.alg) { - case ALG_WEP: + switch (key->conf.cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: len = scnprintf(buf, sizeof(buf), "\n"); break; - case ALG_TKIP: + case WLAN_CIPHER_SUITE_TKIP: len = scnprintf(buf, sizeof(buf), "%08x %04x\n", key->u.tkip.tx.iv32, key->u.tkip.tx.iv16); break; - case ALG_CCMP: + case WLAN_CIPHER_SUITE_CCMP: tpn = key->u.ccmp.tx_pn; len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n", tpn[0], tpn[1], tpn[2], tpn[3], tpn[4], tpn[5]); break; - case ALG_AES_CMAC: + case WLAN_CIPHER_SUITE_AES_CMAC: tpn = key->u.aes_cmac.tx_pn; len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n", tpn[0], tpn[1], tpn[2], tpn[3], tpn[4], @@ -130,11 +118,12 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf, int i, len; const u8 *rpn; - switch (key->conf.alg) { - case ALG_WEP: + switch (key->conf.cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: len = scnprintf(buf, sizeof(buf), "\n"); break; - case ALG_TKIP: + case WLAN_CIPHER_SUITE_TKIP: for (i = 0; i < NUM_RX_DATA_QUEUES; i++) p += scnprintf(p, sizeof(buf)+buf-p, "%08x %04x\n", @@ -142,7 +131,7 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf, key->u.tkip.rx[i].iv16); len = p - buf; break; - case ALG_CCMP: + case WLAN_CIPHER_SUITE_CCMP: for (i = 0; i < NUM_RX_DATA_QUEUES + 1; i++) { rpn = key->u.ccmp.rx_pn[i]; p += scnprintf(p, sizeof(buf)+buf-p, @@ -152,7 +141,7 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf, } len = p - buf; break; - case ALG_AES_CMAC: + case WLAN_CIPHER_SUITE_AES_CMAC: rpn = key->u.aes_cmac.rx_pn; p += scnprintf(p, sizeof(buf)+buf-p, "%02x%02x%02x%02x%02x%02x\n", @@ -174,11 +163,11 @@ static ssize_t key_replays_read(struct file *file, char __user *userbuf, char buf[20]; int len; - switch (key->conf.alg) { - case ALG_CCMP: + switch (key->conf.cipher) { + case WLAN_CIPHER_SUITE_CCMP: len = scnprintf(buf, sizeof(buf), "%u\n", key->u.ccmp.replays); break; - case ALG_AES_CMAC: + case WLAN_CIPHER_SUITE_AES_CMAC: len = scnprintf(buf, sizeof(buf), "%u\n", key->u.aes_cmac.replays); break; @@ -196,8 +185,8 @@ static ssize_t key_icverrors_read(struct file *file, char __user *userbuf, char buf[20]; int len; - switch (key->conf.alg) { - case ALG_AES_CMAC: + switch (key->conf.cipher) { + case WLAN_CIPHER_SUITE_AES_CMAC: len = scnprintf(buf, sizeof(buf), "%u\n", key->u.aes_cmac.icverrors); break; diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index 5d5d2a974668..b5a95582d816 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -336,7 +336,7 @@ TRACE_EVENT(drv_set_key, LOCAL_ENTRY VIF_ENTRY STA_ENTRY - __field(enum ieee80211_key_alg, alg) + __field(u32, cipher) __field(u8, hw_key_idx) __field(u8, flags) __field(s8, keyidx) @@ -346,7 +346,7 @@ TRACE_EVENT(drv_set_key, LOCAL_ASSIGN; VIF_ASSIGN; STA_ASSIGN; - __entry->alg = key->alg; + __entry->cipher = key->cipher; __entry->flags = key->flags; __entry->keyidx = key->keyidx; __entry->hw_key_idx = key->hw_key_idx; diff --git a/net/mac80211/key.c b/net/mac80211/key.c index d6dbc8ea4ead..3203d1d3cd38 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -227,9 +227,7 @@ static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, } } -struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, - int idx, - size_t key_len, +struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, const u8 *key_data, size_t seq_len, const u8 *seq) { @@ -249,15 +247,16 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, key->conf.flags = 0; key->flags = 0; - key->conf.alg = alg; + key->conf.cipher = cipher; key->conf.keyidx = idx; key->conf.keylen = key_len; - switch (alg) { - case ALG_WEP: + switch (cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: key->conf.iv_len = WEP_IV_LEN; key->conf.icv_len = WEP_ICV_LEN; break; - case ALG_TKIP: + case WLAN_CIPHER_SUITE_TKIP: key->conf.iv_len = TKIP_IV_LEN; key->conf.icv_len = TKIP_ICV_LEN; if (seq) { @@ -269,7 +268,7 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, } } break; - case ALG_CCMP: + case WLAN_CIPHER_SUITE_CCMP: key->conf.iv_len = CCMP_HDR_LEN; key->conf.icv_len = CCMP_MIC_LEN; if (seq) { @@ -279,7 +278,7 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, seq[CCMP_PN_LEN - j - 1]; } break; - case ALG_AES_CMAC: + case WLAN_CIPHER_SUITE_AES_CMAC: key->conf.iv_len = 0; key->conf.icv_len = sizeof(struct ieee80211_mmie); if (seq) @@ -290,7 +289,7 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, memcpy(key->conf.key, key_data, key_len); INIT_LIST_HEAD(&key->list); - if (alg == ALG_CCMP) { + if (cipher == WLAN_CIPHER_SUITE_CCMP) { /* * Initialize AES key state here as an optimization so that * it does not need to be initialized for every packet. @@ -303,7 +302,7 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, } } - if (alg == ALG_AES_CMAC) { + if (cipher == WLAN_CIPHER_SUITE_AES_CMAC) { /* * Initialize AES key state here as an optimization so that * it does not need to be initialized for every packet. @@ -328,9 +327,9 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key) if (key->local) ieee80211_key_disable_hw_accel(key); - if (key->conf.alg == ALG_CCMP) + if (key->conf.cipher == WLAN_CIPHER_SUITE_CCMP) ieee80211_aes_key_free(key->u.ccmp.tfm); - if (key->conf.alg == ALG_AES_CMAC) + if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC) ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm); if (key->local) ieee80211_debugfs_key_remove(key); diff --git a/net/mac80211/key.h b/net/mac80211/key.h index b665bbb7a471..53b5ce12536f 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h @@ -123,9 +123,7 @@ struct ieee80211_key { struct ieee80211_key_conf conf; }; -struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, - int idx, - size_t key_len, +struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, const u8 *key_data, size_t seq_len, const u8 *seq); /* diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index f24a0a1cff1a..ad2427021b26 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -961,7 +961,8 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) * pairwise or station-to-station keys, but for WEP we allow * using a key index as well. */ - if (rx->key && rx->key->conf.alg != ALG_WEP && + if (rx->key && rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP40 && + rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP104 && !is_multicast_ether_addr(hdr->addr1)) rx->key = NULL; } @@ -977,8 +978,9 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) return RX_DROP_UNUSABLE; /* the hdr variable is invalid now! */ - switch (rx->key->conf.alg) { - case ALG_WEP: + switch (rx->key->conf.cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: /* Check for weak IVs if possible */ if (rx->sta && ieee80211_is_data(fc) && (!(status->flag & RX_FLAG_IV_STRIPPED) || @@ -988,13 +990,13 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) result = ieee80211_crypto_wep_decrypt(rx); break; - case ALG_TKIP: + case WLAN_CIPHER_SUITE_TKIP: result = ieee80211_crypto_tkip_decrypt(rx); break; - case ALG_CCMP: + case WLAN_CIPHER_SUITE_CCMP: result = ieee80211_crypto_ccmp_decrypt(rx); break; - case ALG_AES_CMAC: + case WLAN_CIPHER_SUITE_AES_CMAC: result = ieee80211_crypto_aes_cmac_decrypt(rx); break; } @@ -1291,7 +1293,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) /* This is the first fragment of a new frame. */ entry = ieee80211_reassemble_add(rx->sdata, frag, seq, rx->queue, &(rx->skb)); - if (rx->key && rx->key->conf.alg == ALG_CCMP && + if (rx->key && rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP && ieee80211_has_protected(fc)) { int queue = ieee80211_is_mgmt(fc) ? NUM_RX_DATA_QUEUES : rx->queue; @@ -1320,7 +1322,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) int i; u8 pn[CCMP_PN_LEN], *rpn; int queue; - if (!rx->key || rx->key->conf.alg != ALG_CCMP) + if (!rx->key || rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP) return RX_DROP_UNUSABLE; memcpy(pn, entry->last_pn, CCMP_PN_LEN); for (i = CCMP_PN_LEN - 1; i >= 0; i--) { diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index c54db966926b..bc4fefc91663 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -543,15 +543,16 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) tx->key->tx_rx_count++; /* TODO: add threshold stuff again */ - switch (tx->key->conf.alg) { - case ALG_WEP: + switch (tx->key->conf.cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: if (ieee80211_is_auth(hdr->frame_control)) break; - case ALG_TKIP: + case WLAN_CIPHER_SUITE_TKIP: if (!ieee80211_is_data_present(hdr->frame_control)) tx->key = NULL; break; - case ALG_CCMP: + case WLAN_CIPHER_SUITE_CCMP: if (!ieee80211_is_data_present(hdr->frame_control) && !ieee80211_use_mfp(hdr->frame_control, tx->sta, tx->skb)) @@ -561,7 +562,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) IEEE80211_KEY_FLAG_SW_MGMT) && ieee80211_is_mgmt(hdr->frame_control); break; - case ALG_AES_CMAC: + case WLAN_CIPHER_SUITE_AES_CMAC: if (!ieee80211_is_mgmt(hdr->frame_control)) tx->key = NULL; break; @@ -949,14 +950,15 @@ ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx) if (!tx->key) return TX_CONTINUE; - switch (tx->key->conf.alg) { - case ALG_WEP: + switch (tx->key->conf.cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: return ieee80211_crypto_wep_encrypt(tx); - case ALG_TKIP: + case WLAN_CIPHER_SUITE_TKIP: return ieee80211_crypto_tkip_encrypt(tx); - case ALG_CCMP: + case WLAN_CIPHER_SUITE_CCMP: return ieee80211_crypto_ccmp_encrypt(tx); - case ALG_AES_CMAC: + case WLAN_CIPHER_SUITE_AES_CMAC: return ieee80211_crypto_aes_cmac_encrypt(tx); } diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c index 9ebc8d8a1f5b..f27484c22b9f 100644 --- a/net/mac80211/wep.c +++ b/net/mac80211/wep.c @@ -240,7 +240,7 @@ static int ieee80211_wep_decrypt(struct ieee80211_local *local, keyidx = skb->data[hdrlen + 3] >> 6; - if (!key || keyidx != key->conf.keyidx || key->conf.alg != ALG_WEP) + if (!key || keyidx != key->conf.keyidx) return -1; klen = 3 + key->conf.keylen; diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index 8d59d27d887e..b08ad94b56da 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -36,8 +36,8 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx) int tail; hdr = (struct ieee80211_hdr *)skb->data; - if (!tx->key || tx->key->conf.alg != ALG_TKIP || skb->len < 24 || - !ieee80211_is_data_present(hdr->frame_control)) + if (!tx->key || tx->key->conf.cipher != WLAN_CIPHER_SUITE_TKIP || + skb->len < 24 || !ieee80211_is_data_present(hdr->frame_control)) return TX_CONTINUE; hdrlen = ieee80211_hdrlen(hdr->frame_control); @@ -94,7 +94,7 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx) if (status->flag & RX_FLAG_MMIC_STRIPPED) return RX_CONTINUE; - if (!rx->key || rx->key->conf.alg != ALG_TKIP || + if (!rx->key || rx->key->conf.cipher != WLAN_CIPHER_SUITE_TKIP || !ieee80211_has_protected(hdr->frame_control) || !ieee80211_is_data_present(hdr->frame_control)) return RX_CONTINUE; -- cgit v1.2.3 From 418de6d955fa1da0cb4332661a31d21f57e954ab Mon Sep 17 00:00:00 2001 From: Nick Kossifidis Date: Sun, 15 Aug 2010 13:03:10 -0400 Subject: ath5k: rename ath5k_hw_set_associd to _set_bssid Although the named function also sets the aid, its main purpose is configuring the bssid and we use that everywhere else. Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/ath5k.h | 2 +- drivers/net/wireless/ath/ath5k/attach.c | 2 +- drivers/net/wireless/ath/ath5k/base.c | 4 ++-- drivers/net/wireless/ath/ath5k/pcu.c | 13 ++++++------- drivers/net/wireless/ath/ath5k/reset.c | 2 +- 5 files changed, 11 insertions(+), 12 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index ea6362a8988d..9129e51b4f18 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -1190,7 +1190,7 @@ extern int ath5k_hw_set_opmode(struct ath5k_hw *ah, enum nl80211_iftype opmode); void ath5k_hw_set_coverage_class(struct ath5k_hw *ah, u8 coverage_class); /* BSSID Functions */ int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac); -void ath5k_hw_set_associd(struct ath5k_hw *ah); +void ath5k_hw_set_bssid(struct ath5k_hw *ah); void ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask); /* Receive start/stop functions */ void ath5k_hw_start_rx_pcu(struct ath5k_hw *ah); diff --git a/drivers/net/wireless/ath/ath5k/attach.c b/drivers/net/wireless/ath/ath5k/attach.c index b32e28caeee2..e72a1e5421d7 100644 --- a/drivers/net/wireless/ath/ath5k/attach.c +++ b/drivers/net/wireless/ath/ath5k/attach.c @@ -329,7 +329,7 @@ int ath5k_hw_attach(struct ath5k_softc *sc) /* Set BSSID to bcast address: ff:ff:ff:ff:ff:ff for now */ memcpy(common->curbssid, ath_bcast_mac, ETH_ALEN); - ath5k_hw_set_associd(ah); + ath5k_hw_set_bssid(ah); ath5k_hw_set_opmode(ah, sc->opmode); ath5k_hw_rfgain_opt_init(ah); diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index a729b8774670..0378bbcce494 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -3476,7 +3476,7 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw, /* Cache for later use during resets */ memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); common->curaid = 0; - ath5k_hw_set_associd(ah); + ath5k_hw_set_bssid(ah); mmiowb(); } @@ -3494,7 +3494,7 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw, "Bss Info ASSOC %d, bssid: %pM\n", bss_conf->aid, common->curbssid); common->curaid = bss_conf->aid; - ath5k_hw_set_associd(ah); + ath5k_hw_set_bssid(ah); /* Once ANI is available you would start it here */ } } diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c index af273358c7cb..3fef5931ec3a 100644 --- a/drivers/net/wireless/ath/ath5k/pcu.c +++ b/drivers/net/wireless/ath/ath5k/pcu.c @@ -308,27 +308,26 @@ int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac) } /** - * ath5k_hw_set_associd - Set BSSID for association + * ath5k_hw_set_bssid - Set current BSSID on hw * * @ah: The &struct ath5k_hw - * @bssid: BSSID - * @assoc_id: Assoc id * - * Sets the BSSID which trigers the "SME Join" operation + * Sets the current BSSID and BSSID mask we have from the + * common struct into the hardware */ -void ath5k_hw_set_associd(struct ath5k_hw *ah) +void ath5k_hw_set_bssid(struct ath5k_hw *ah) { struct ath_common *common = ath5k_hw_common(ah); u16 tim_offset = 0; /* - * Set simple BSSID mask on 5212 + * Set BSSID mask on 5212 */ if (ah->ah_version == AR5K_AR5212) ath_hw_setbssidmask(common); /* - * Set BSSID which triggers the "SME Join" operation + * Set BSSID */ ath5k_hw_reg_write(ah, get_unaligned_le32(common->curbssid), diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c index 498aa28ea9e6..f5434eb7f980 100644 --- a/drivers/net/wireless/ath/ath5k/reset.c +++ b/drivers/net/wireless/ath/ath5k/reset.c @@ -1160,7 +1160,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, */ /* Restore bssid and bssid mask */ - ath5k_hw_set_associd(ah); + ath5k_hw_set_bssid(ah); /* Set PCU config */ ath5k_hw_set_opmode(ah, op_mode); -- cgit v1.2.3 From b61279809860690fe5c9fcb40441008d31fabab6 Mon Sep 17 00:00:00 2001 From: Nick Kossifidis Date: Sun, 15 Aug 2010 13:03:11 -0400 Subject: ath5k: remove own (wrong) IEEE80211_MAX_LEN Use the version already supplied in include/linux/ieee80211.h. Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/ath5k.h | 5 +---- drivers/net/wireless/ath/ath5k/base.c | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index 9129e51b4f18..f399c4dd8e69 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -175,7 +175,7 @@ #define AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF 0 #define AR5K_TUNE_RADAR_ALERT false #define AR5K_TUNE_MIN_TX_FIFO_THRES 1 -#define AR5K_TUNE_MAX_TX_FIFO_THRES ((IEEE80211_MAX_LEN / 64) + 1) +#define AR5K_TUNE_MAX_TX_FIFO_THRES ((IEEE80211_MAX_FRAME_LEN / 64) + 1) #define AR5K_TUNE_REGISTER_TIMEOUT 20000 /* Register for RSSI threshold has a mask of 0xff, so 255 seems to * be the max value. */ @@ -343,9 +343,6 @@ struct ath5k_srev_name { #define AR5K_SREV_PHY_5413 0x61 #define AR5K_SREV_PHY_2425 0x70 -/* IEEE defs */ -#define IEEE80211_MAX_LEN 2500 - /* TODO add support to mac80211 for vendor-specific rates and modes */ /* diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 0378bbcce494..91241b4525ad 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1709,7 +1709,7 @@ ath5k_rx_start(struct ath5k_softc *sc) struct ath5k_buf *bf; int ret; - common->rx_bufsize = roundup(IEEE80211_MAX_LEN, common->cachelsz); + common->rx_bufsize = roundup(IEEE80211_MAX_FRAME_LEN, common->cachelsz); ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "cachelsz %u rx_bufsize %u\n", common->cachelsz, common->rx_bufsize); -- cgit v1.2.3 From a180a13081708b78d42232c6d922ce3de63f12e0 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Sun, 15 Aug 2010 13:03:12 -0400 Subject: ath5k: clean up some comments This fixes a few misspellings, word repetitions, and some grammar nits in ath5k comments. No code changes. Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/ani.c | 6 ++-- drivers/net/wireless/ath/ath5k/attach.c | 6 ++-- drivers/net/wireless/ath/ath5k/base.c | 58 +++++++++++++++---------------- drivers/net/wireless/ath/ath5k/dma.c | 4 +-- drivers/net/wireless/ath/ath5k/eeprom.c | 4 +-- drivers/net/wireless/ath/ath5k/phy.c | 15 ++++---- drivers/net/wireless/ath/ath5k/reg.h | 2 +- drivers/net/wireless/ath/ath5k/reset.c | 22 ++++++------ drivers/net/wireless/ath/ath5k/rfbuffer.h | 4 +-- 9 files changed, 59 insertions(+), 62 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/ani.c b/drivers/net/wireless/ath/ath5k/ani.c index 26dbe65fedb0..e4a5f046bba4 100644 --- a/drivers/net/wireless/ath/ath5k/ani.c +++ b/drivers/net/wireless/ath/ath5k/ani.c @@ -552,9 +552,9 @@ ath5k_ani_mib_intr(struct ath5k_hw *ah) if (ah->ah_sc->ani_state.ani_mode != ATH5K_ANI_MODE_AUTO) return; - /* if one of the errors triggered, we can get a superfluous second - * interrupt, even though we have already reset the register. the - * function detects that so we can return early */ + /* If one of the errors triggered, we can get a superfluous second + * interrupt, even though we have already reset the register. The + * function detects that so we can return early. */ if (ath5k_ani_save_and_clear_phy_errors(ah, as) == 0) return; diff --git a/drivers/net/wireless/ath/ath5k/attach.c b/drivers/net/wireless/ath/ath5k/attach.c index e72a1e5421d7..aabad4f13e2a 100644 --- a/drivers/net/wireless/ath/ath5k/attach.c +++ b/drivers/net/wireless/ath/ath5k/attach.c @@ -139,12 +139,12 @@ int ath5k_hw_attach(struct ath5k_softc *sc) else ah->ah_version = AR5K_AR5212; - /*Fill the ath5k_hw struct with the needed functions*/ + /* Fill the ath5k_hw struct with the needed functions */ ret = ath5k_hw_init_desc_functions(ah); if (ret) goto err_free; - /* Bring device out of sleep and reset it's units */ + /* Bring device out of sleep and reset its units */ ret = ath5k_hw_nic_wakeup(ah, 0, true); if (ret) goto err_free; @@ -158,7 +158,7 @@ int ath5k_hw_attach(struct ath5k_softc *sc) CHANNEL_5GHZ); ah->ah_phy = AR5K_PHY(0); - /* Try to identify radio chip based on it's srev */ + /* Try to identify radio chip based on its srev */ switch (ah->ah_radio_5ghz_revision & 0xf0) { case AR5K_SREV_RAD_5111: ah->ah_radio = AR5K_RF5111; diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 91241b4525ad..c3b614d2ef34 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -612,7 +612,7 @@ ath5k_pci_probe(struct pci_dev *pdev, goto err_free; } - /*If we passed the test malloc a ath5k_hw struct*/ + /* If we passed the test, malloc an ath5k_hw struct */ sc->ah = kzalloc(sizeof(struct ath5k_hw), GFP_KERNEL); if (!sc->ah) { ret = -ENOMEM; @@ -786,8 +786,8 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) /* * Check if the MAC has multi-rate retry support. * We do this by trying to setup a fake extended - * descriptor. MAC's that don't have support will - * return false w/o doing anything. MAC's that do + * descriptor. MACs that don't have support will + * return false w/o doing anything. MACs that do * support it will return true w/o doing anything. */ ret = ath5k_hw_setup_mrr_tx_desc(ah, NULL, 0, 0, 0, 0, 0, 0); @@ -827,7 +827,7 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) /* * Allocate hardware transmit queues: one queue for * beacon frames and one data queue for each QoS - * priority. Note that hw functions handle reseting + * priority. Note that hw functions handle resetting * these queues at the needed time. */ ret = ath5k_beaconq_setup(ah); @@ -909,7 +909,7 @@ ath5k_detach(struct pci_dev *pdev, struct ieee80211_hw *hw) /* * NB: the order of these is important: * o call the 802.11 layer before detaching ath5k_hw to - * insure callbacks into the driver to delete global + * ensure callbacks into the driver to delete global * key cache entries can be handled * o reclaim the tx queue data structures after calling * the 802.11 layer as we'll get called back to reclaim @@ -1514,7 +1514,7 @@ ath5k_txq_setup(struct ath5k_softc *sc, /* * Enable interrupts only for EOL and DESC conditions. * We mark tx descriptors to receive a DESC interrupt - * when a tx queue gets deep; otherwise waiting for the + * when a tx queue gets deep; otherwise we wait for the * EOL to reap descriptors. Note that this is done to * reduce interrupt load and this only defers reaping * descriptors, never transmitting frames. Aside from @@ -1859,7 +1859,7 @@ ath5k_update_beacon_rssi(struct ath5k_softc *sc, struct sk_buff *skb, int rssi) } /* - * Compute padding position. skb must contains an IEEE 802.11 frame + * Compute padding position. skb must contain an IEEE 802.11 frame */ static int ath5k_common_padpos(struct sk_buff *skb) { @@ -1878,10 +1878,9 @@ static int ath5k_common_padpos(struct sk_buff *skb) } /* - * This function expects a 802.11 frame and returns the number of - * bytes added, or -1 if we don't have enought header room. + * This function expects an 802.11 frame and returns the number of + * bytes added, or -1 if we don't have enough header room. */ - static int ath5k_add_padding(struct sk_buff *skb) { int padpos = ath5k_common_padpos(skb); @@ -1901,10 +1900,18 @@ static int ath5k_add_padding(struct sk_buff *skb) } /* - * This function expects a 802.11 frame and returns the number of - * bytes removed + * The MAC header is padded to have 32-bit boundary if the + * packet payload is non-zero. The general calculation for + * padsize would take into account odd header lengths: + * padsize = 4 - (hdrlen & 3); however, since only + * even-length headers are used, padding can only be 0 or 2 + * bytes and we can optimize this a bit. We must not try to + * remove padding from short control frames that do not have a + * payload. + * + * This function expects an 802.11 frame and returns the number of + * bytes removed. */ - static int ath5k_remove_padding(struct sk_buff *skb) { int padpos = ath5k_common_padpos(skb); @@ -1925,14 +1932,6 @@ ath5k_receive_frame(struct ath5k_softc *sc, struct sk_buff *skb, { struct ieee80211_rx_status *rxs; - /* The MAC header is padded to have 32-bit boundary if the - * packet payload is non-zero. The general calculation for - * padsize would take into account odd header lengths: - * padsize = (4 - hdrlen % 4) % 4; However, since only - * even-length headers are used, padding can only be 0 or 2 - * bytes and we can optimize this a bit. In addition, we must - * not try to remove padding from short control frames that do - * not have payload. */ ath5k_remove_padding(skb); rxs = IEEE80211_SKB_RXCB(skb); @@ -2281,10 +2280,11 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) * default antenna which is supposed to be an omni. * * Note2: On sectored scenarios it's possible to have - * multiple antennas (1omni -the default- and 14 sectors) - * so if we choose to actually support this mode we need - * to allow user to set how many antennas we have and tweak - * the code below to send beacons on all of them. + * multiple antennas (1 omni -- the default -- and 14 + * sectors), so if we choose to actually support this + * mode, we need to allow the user to set how many antennas + * we have and tweak the code below to send beacons + * on all of them. */ if (ah->ah_ant_mode == AR5K_ANTMODE_SECTOR_AP) antenna = sc->bsent & 4 ? 2 : 1; @@ -2333,7 +2333,7 @@ ath5k_beacon_send(struct ath5k_softc *sc) } /* * Check if the previous beacon has gone out. If - * not don't don't try to post another, skip this + * not, don't don't try to post another: skip this * period and wait for the next. Missed beacons * indicate a problem and should not occur. If we * miss too many consecutive beacons reset the device. @@ -2905,8 +2905,8 @@ static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, ATH5K_DBG(sc, ATH5K_DEBUG_XMIT, "tx in monitor (scan?)\n"); /* - * the hardware expects the header padded to 4 byte boundaries - * if this is not the case we add the padding after the header + * The hardware expects the header padded to 4 byte boundaries. + * If this is not the case, we add the padding after the header. */ padsize = ath5k_add_padding(skb); if (padsize < 0) { @@ -3274,7 +3274,7 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw, /* Set multicast bits */ ath5k_hw_set_mcast_filter(ah, mfilt[0], mfilt[1]); - /* Set the cached hw filter flags, this will alter actually + /* Set the cached hw filter flags, this will later actually * be set in HW */ sc->filter_flags = rfilt; diff --git a/drivers/net/wireless/ath/ath5k/dma.c b/drivers/net/wireless/ath/ath5k/dma.c index 484f31870ba8..58bb6c5dda7b 100644 --- a/drivers/net/wireless/ath/ath5k/dma.c +++ b/drivers/net/wireless/ath/ath5k/dma.c @@ -377,11 +377,11 @@ int ath5k_hw_set_txdp(struct ath5k_hw *ah, unsigned int queue, u32 phys_addr) * * This function increases/decreases the tx trigger level for the tx fifo * buffer (aka FIFO threshold) that is used to indicate when PCU flushes - * the buffer and transmits it's data. Lowering this results sending small + * the buffer and transmits its data. Lowering this results sending small * frames more quickly but can lead to tx underruns, raising it a lot can * result other problems (i think bmiss is related). Right now we start with * the lowest possible (64Bytes) and if we get tx underrun we increase it using - * the increase flag. Returns -EIO if we have have reached maximum/minimum. + * the increase flag. Returns -EIO if we have reached maximum/minimum. * * XXX: Link this with tx DMA size ? * XXX: Use it to save interrupts ? diff --git a/drivers/net/wireless/ath/ath5k/eeprom.c b/drivers/net/wireless/ath/ath5k/eeprom.c index ae316fec4a6a..39722dd73e43 100644 --- a/drivers/net/wireless/ath/ath5k/eeprom.c +++ b/drivers/net/wireless/ath/ath5k/eeprom.c @@ -661,7 +661,7 @@ ath5k_eeprom_init_11bg_2413(struct ath5k_hw *ah, unsigned int mode, int offset) * (eeprom versions < 4). For RF5111 we have 11 pre-defined PCDAC * steps that match with the power values we read from eeprom. On * older eeprom versions (< 3.2) these steps are equaly spaced at - * 10% of the pcdac curve -until the curve reaches it's maximum- + * 10% of the pcdac curve -until the curve reaches its maximum- * (11 steps from 0 to 100%) but on newer eeprom versions (>= 3.2) * these 11 steps are spaced in a different way. This function returns * the pcdac steps based on eeprom version and curve min/max so that we @@ -1113,7 +1113,7 @@ ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode) */ /* For RF2413 power calibration data doesn't start on a fixed location and - * if a mode is not supported, it's section is missing -not zeroed-. + * if a mode is not supported, its section is missing -not zeroed-. * So we need to calculate the starting offset for each section by using * these two functions */ diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c index 6284c389ba18..c7c446592374 100644 --- a/drivers/net/wireless/ath/ath5k/phy.c +++ b/drivers/net/wireless/ath/ath5k/phy.c @@ -115,7 +115,7 @@ static unsigned int ath5k_hw_rfb_op(struct ath5k_hw *ah, \**********************/ /* - * This code is used to optimize rf gain on different environments + * This code is used to optimize RF gain on different environments * (temperature mostly) based on feedback from a power detector. * * It's only used on RF5111 and RF5112, later RF chips seem to have @@ -302,7 +302,7 @@ static bool ath5k_hw_rf_check_gainf_readback(struct ath5k_hw *ah) } /* Perform gain_F adjustment by choosing the right set - * of parameters from rf gain optimization ladder */ + * of parameters from RF gain optimization ladder */ static s8 ath5k_hw_rf_gainf_adjust(struct ath5k_hw *ah) { const struct ath5k_gain_opt *go; @@ -367,7 +367,7 @@ done: return ret; } -/* Main callback for thermal rf gain calibration engine +/* Main callback for thermal RF gain calibration engine * Check for a new gain reading and schedule an adjustment * if needed. * @@ -433,7 +433,7 @@ done: return ah->ah_gain.g_state; } -/* Write initial rf gain table to set the RF sensitivity +/* Write initial RF gain table to set the RF sensitivity * this one works on all RF chips and has nothing to do * with gain_F calibration */ int ath5k_hw_rfgain_init(struct ath5k_hw *ah, unsigned int freq) @@ -496,7 +496,7 @@ int ath5k_hw_rfgain_init(struct ath5k_hw *ah, unsigned int freq) /* - * Setup RF registers by writing rf buffer on hw + * Setup RF registers by writing RF buffer on hw */ int ath5k_hw_rfregs_init(struct ath5k_hw *ah, struct ieee80211_channel *channel, unsigned int mode) @@ -571,7 +571,7 @@ int ath5k_hw_rfregs_init(struct ath5k_hw *ah, struct ieee80211_channel *channel, return -EINVAL; } - /* If it's the first time we set rf buffer, allocate + /* If it's the first time we set RF buffer, allocate * ah->ah_rf_banks based on ah->ah_rf_banks_size * we set above */ if (ah->ah_rf_banks == NULL) { @@ -3035,9 +3035,6 @@ ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, /* Limit max power if we have a CTL available */ ath5k_get_max_ctl_power(ah, channel); - /* FIXME: Tx power limit for this regdomain - * XXX: Mac80211/CRDA will do that anyway ? */ - /* FIXME: Antenna reduction stuff */ /* FIXME: Limit power on turbo modes */ diff --git a/drivers/net/wireless/ath/ath5k/reg.h b/drivers/net/wireless/ath/ath5k/reg.h index 55b4ac6d236f..05ef587ad2b4 100644 --- a/drivers/net/wireless/ath/ath5k/reg.h +++ b/drivers/net/wireless/ath/ath5k/reg.h @@ -1911,7 +1911,7 @@ #define AR5K_PHY_TURBO 0x9804 /* Register Address */ #define AR5K_PHY_TURBO_MODE 0x00000001 /* Enable turbo mode */ #define AR5K_PHY_TURBO_SHORT 0x00000002 /* Set short symbols to turbo mode */ -#define AR5K_PHY_TURBO_MIMO 0x00000004 /* Set turbo for mimo mimo */ +#define AR5K_PHY_TURBO_MIMO 0x00000004 /* Set turbo for mimo */ /* * PHY agility command register diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c index f5434eb7f980..4154959125b6 100644 --- a/drivers/net/wireless/ath/ath5k/reset.c +++ b/drivers/net/wireless/ath/ath5k/reset.c @@ -959,7 +959,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, AR5K_QUEUE_DCU_SEQNUM(0)); } - /* TSF accelerates on AR5211 durring reset + /* TSF accelerates on AR5211 during reset * As a workaround save it here and restore * it later so that it's back in time after * reset. This way it'll get re-synced on the @@ -1080,7 +1080,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, return ret; /* Spur info is available only from EEPROM versions - * bigger than 5.3 but but the EEPOM routines will use + * greater than 5.3, but the EEPROM routines will use * static values for older versions */ if (ah->ah_mac_srev >= AR5K_SREV_AR5424) ath5k_hw_set_spur_mitigation_filter(ah, @@ -1173,11 +1173,11 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, /* Set RSSI/BRSSI thresholds * * Note: If we decide to set this value - * dynamicaly, have in mind that when AR5K_RSSI_THR - * register is read it might return 0x40 if we haven't - * wrote anything to it plus BMISS RSSI threshold is zeroed. + * dynamically, keep in mind that when AR5K_RSSI_THR + * register is read, it might return 0x40 if we haven't + * written anything to it. Also, BMISS RSSI threshold is zeroed. * So doing a save/restore procedure here isn't the right - * choice. Instead store it on ath5k_hw */ + * choice. Instead, store it in ath5k_hw */ ath5k_hw_reg_write(ah, (AR5K_TUNE_RSSI_THRES | AR5K_TUNE_BMISS_THRES << AR5K_RSSI_THR_BMISS_S), @@ -1235,7 +1235,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, /* * Perform ADC test to see if baseband is ready - * Set tx hold and check adc test register + * Set TX hold and check ADC test register */ phy_tst1 = ath5k_hw_reg_read(ah, AR5K_PHY_TST1); ath5k_hw_reg_write(ah, AR5K_PHY_TST1_TXHOLD, AR5K_PHY_TST1); @@ -1254,15 +1254,15 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, * * This method is used to calibrate some static offsets * used together with on-the fly I/Q calibration (the - * one performed via ath5k_hw_phy_calibrate), that doesn't + * one performed via ath5k_hw_phy_calibrate), which doesn't * interrupt rx path. * * While rx path is re-routed to the power detector we also - * start a noise floor calibration, to measure the + * start a noise floor calibration to measure the * card's noise floor (the noise we measure when we are not - * transmiting or receiving anything). + * transmitting or receiving anything). * - * If we are in a noisy environment AGC calibration may time + * If we are in a noisy environment, AGC calibration may time * out and/or noise floor calibration might timeout. */ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, diff --git a/drivers/net/wireless/ath/ath5k/rfbuffer.h b/drivers/net/wireless/ath/ath5k/rfbuffer.h index e50baff66175..3ac4cff4239d 100644 --- a/drivers/net/wireless/ath/ath5k/rfbuffer.h +++ b/drivers/net/wireless/ath/ath5k/rfbuffer.h @@ -25,10 +25,10 @@ * * We don't write on those registers directly but * we send a data packet on the chip, using a special register, - * that holds all the settings we need. After we 've sent the + * that holds all the settings we need. After we've sent the * data packet, we write on another special register to notify hw * to apply the settings. This is done so that control registers - * can be dynamicaly programmed during operation and the settings + * can be dynamically programmed during operation and the settings * are applied faster on the hw. * * We call each data packet an "RF Bank" and all the data we write -- cgit v1.2.3 From 23538c268c834d73a2f3e65a06ea6bf218ba1a0e Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Sun, 15 Aug 2010 13:03:13 -0400 Subject: ath5k: remove monitor check in receive_frame_ok filter Monitor interfaces are never seen by the driver, so tests based on that opmode don't make sense. Also, we already pass all mic failure packets. Consequently this code is actually accepting any frames with just crypto errors and rejecting those with CRC, FIFO, and PHY errors for all interface types. Adjust the code and comment accordingly. Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index c3b614d2ef34..166029913884 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -2035,9 +2035,8 @@ ath5k_receive_frame_ok(struct ath5k_softc *sc, struct ath5k_rx_status *rs) return true; } - /* let crypto-error packets fall through in MNTR */ - if ((rs->rs_status & ~(AR5K_RXERR_DECRYPT|AR5K_RXERR_MIC)) || - sc->opmode != NL80211_IFTYPE_MONITOR) + /* reject any frames with non-crypto errors */ + if (rs->rs_status & ~(AR5K_RXERR_DECRYPT)) return false; } -- cgit v1.2.3 From 4afd89d9cf17df46c3cfa1eb744232e345b3b0e6 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Sun, 15 Aug 2010 13:03:14 -0400 Subject: ath5k: remove all mention of monitor iftype Monitor interfaces are never seen by the driver so these cases are never reached. Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 166029913884..99d56c6f06f1 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -2325,8 +2325,7 @@ ath5k_beacon_send(struct ath5k_softc *sc) ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, "in beacon_send\n"); - if (unlikely(bf->skb == NULL || sc->opmode == NL80211_IFTYPE_STATION || - sc->opmode == NL80211_IFTYPE_MONITOR)) { + if (unlikely(bf->skb == NULL || sc->opmode == NL80211_IFTYPE_STATION)) { ATH5K_WARN(sc, "bf=%p bf_skb=%p\n", bf, bf ? bf->skb : NULL); return; } @@ -2900,9 +2899,6 @@ static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, ath5k_debug_dump_skb(sc, skb, "TX ", 1); - if (sc->opmode == NL80211_IFTYPE_MONITOR) - ATH5K_DBG(sc, ATH5K_DEBUG_XMIT, "tx in monitor (scan?)\n"); - /* * The hardware expects the header padded to 4 byte boundaries. * If this is not the case, we add the padding after the header. @@ -3048,7 +3044,6 @@ static int ath5k_add_interface(struct ieee80211_hw *hw, case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: - case NL80211_IFTYPE_MONITOR: sc->opmode = vif->type; break; default: @@ -3250,7 +3245,6 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw, switch (sc->opmode) { case NL80211_IFTYPE_MESH_POINT: - case NL80211_IFTYPE_MONITOR: rfilt |= AR5K_RX_FILTER_CONTROL | AR5K_RX_FILTER_BEACON | AR5K_RX_FILTER_PROBEREQ | -- cgit v1.2.3 From 30bf4169ad163047f87a2555cb33aa55f5d5e2bc Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Sun, 15 Aug 2010 13:03:15 -0400 Subject: ath5k: don't enable probe request rx for STAs AR5K_RX_FILTER_PROBEREQ enables reception of probe requests, but the filter flag FIF_BCN_PRBRESP_PROMISC is actually about receiving beacons and probe _responses_, so we shouldn't turn on the filter when scanning. Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 99d56c6f06f1..f2fceb10c9fc 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -3227,9 +3227,9 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw, rfilt |= AR5K_RX_FILTER_PHYERR; /* FIF_BCN_PRBRESP_PROMISC really means to enable beacons - * and probes for any BSSID, this needs testing */ + * and probes for any BSSID */ if (*new_flags & FIF_BCN_PRBRESP_PROMISC) - rfilt |= AR5K_RX_FILTER_BEACON | AR5K_RX_FILTER_PROBEREQ; + rfilt |= AR5K_RX_FILTER_BEACON; /* FIF_CONTROL doc says that if FIF_PROMISC_IN_BSS is not * set we should only pass on control frames for this -- cgit v1.2.3 From d99eeb87420a6da1f87064c7fa70bdf299699d90 Mon Sep 17 00:00:00 2001 From: Vivek Natarajan Date: Wed, 18 Aug 2010 19:57:48 +0530 Subject: ath9k_common: Move bt_stomp to common for sharing with ath9k_htc. Signed-off-by: Vivek Natarajan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 8 ------- drivers/net/wireless/ath/ath9k/common.c | 31 +++++++++++++++++++++++++ drivers/net/wireless/ath/ath9k/common.h | 10 +++++++++ drivers/net/wireless/ath/ath9k/gpio.c | 40 +++++---------------------------- 4 files changed, 47 insertions(+), 42 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index def0cd308fdd..f0197a6046ab 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -437,14 +437,6 @@ void ath_ani_calibrate(unsigned long data); /* BTCOEX */ /**********/ -/* Defines the BT AR_BT_COEX_WGHT used */ -enum ath_stomp_type { - ATH_BTCOEX_NO_STOMP, - ATH_BTCOEX_STOMP_ALL, - ATH_BTCOEX_STOMP_LOW, - ATH_BTCOEX_STOMP_NONE -}; - struct ath_btcoex { bool hw_timer_enabled; spinlock_t btcoex_lock; diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c index 3100c87a4fcd..31cfe468e3fc 100644 --- a/drivers/net/wireless/ath/ath9k/common.c +++ b/drivers/net/wireless/ath/ath9k/common.c @@ -420,6 +420,37 @@ int ath9k_cmn_count_streams(unsigned int chainmask, int max) } EXPORT_SYMBOL(ath9k_cmn_count_streams); +/* + * Configures appropriate weight based on stomp type. + */ +void ath9k_cmn_btcoex_bt_stomp(struct ath_common *common, + enum ath_stomp_type stomp_type) +{ + struct ath_hw *ah = common->ah; + + switch (stomp_type) { + case ATH_BTCOEX_STOMP_ALL: + ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, + AR_STOMP_ALL_WLAN_WGHT); + break; + case ATH_BTCOEX_STOMP_LOW: + ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, + AR_STOMP_LOW_WLAN_WGHT); + break; + case ATH_BTCOEX_STOMP_NONE: + ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, + AR_STOMP_NONE_WLAN_WGHT); + break; + default: + ath_print(common, ATH_DBG_BTCOEX, + "Invalid Stomptype\n"); + break; + } + + ath9k_hw_btcoex_enable(ah); +} +EXPORT_SYMBOL(ath9k_cmn_btcoex_bt_stomp); + static int __init ath9k_cmn_init(void) { return 0; diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h index 97809d39c73f..4aa4e7dbe4d2 100644 --- a/drivers/net/wireless/ath/ath9k/common.h +++ b/drivers/net/wireless/ath/ath9k/common.h @@ -52,6 +52,14 @@ #define ATH_EP_RND(x, mul) \ ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) +/* Defines the BT AR_BT_COEX_WGHT used */ +enum ath_stomp_type { + ATH_BTCOEX_NO_STOMP, + ATH_BTCOEX_STOMP_ALL, + ATH_BTCOEX_STOMP_LOW, + ATH_BTCOEX_STOMP_NONE +}; + int ath9k_cmn_padpos(__le16 frame_control); int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb); void ath9k_cmn_update_ichannel(struct ieee80211_hw *hw, @@ -65,3 +73,5 @@ int ath9k_cmn_key_config(struct ath_common *common, void ath9k_cmn_key_delete(struct ath_common *common, struct ieee80211_key_conf *key); int ath9k_cmn_count_streams(unsigned int chainmask, int max); +void ath9k_cmn_btcoex_bt_stomp(struct ath_common *common, + enum ath_stomp_type stomp_type); diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index 3a8ee999da5d..4a9a68bba324 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -251,36 +251,6 @@ static void ath_detect_bt_priority(struct ath_softc *sc) } } -/* - * Configures appropriate weight based on stomp type. - */ -static void ath9k_btcoex_bt_stomp(struct ath_softc *sc, - enum ath_stomp_type stomp_type) -{ - struct ath_hw *ah = sc->sc_ah; - - switch (stomp_type) { - case ATH_BTCOEX_STOMP_ALL: - ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, - AR_STOMP_ALL_WLAN_WGHT); - break; - case ATH_BTCOEX_STOMP_LOW: - ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, - AR_STOMP_LOW_WLAN_WGHT); - break; - case ATH_BTCOEX_STOMP_NONE: - ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, - AR_STOMP_NONE_WLAN_WGHT); - break; - default: - ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX, - "Invalid Stomptype\n"); - break; - } - - ath9k_hw_btcoex_enable(ah); -} - static void ath9k_gen_timer_start(struct ath_hw *ah, struct ath_gen_timer *timer, u32 timer_next, @@ -319,6 +289,7 @@ static void ath_btcoex_period_timer(unsigned long data) struct ath_softc *sc = (struct ath_softc *) data; struct ath_hw *ah = sc->sc_ah; struct ath_btcoex *btcoex = &sc->btcoex; + struct ath_common *common = ath9k_hw_common(ah); u32 timer_period; bool is_btscan; @@ -328,7 +299,7 @@ static void ath_btcoex_period_timer(unsigned long data) spin_lock_bh(&btcoex->btcoex_lock); - ath9k_btcoex_bt_stomp(sc, is_btscan ? ATH_BTCOEX_STOMP_ALL : + ath9k_cmn_btcoex_bt_stomp(common, is_btscan ? ATH_BTCOEX_STOMP_ALL : btcoex->bt_stomp_type); spin_unlock_bh(&btcoex->btcoex_lock); @@ -359,17 +330,18 @@ static void ath_btcoex_no_stomp_timer(void *arg) struct ath_softc *sc = (struct ath_softc *)arg; struct ath_hw *ah = sc->sc_ah; struct ath_btcoex *btcoex = &sc->btcoex; + struct ath_common *common = ath9k_hw_common(ah); bool is_btscan = sc->sc_flags & SC_OP_BT_SCAN; - ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX, + ath_print(common, ATH_DBG_BTCOEX, "no stomp timer running\n"); spin_lock_bh(&btcoex->btcoex_lock); if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || is_btscan) - ath9k_btcoex_bt_stomp(sc, ATH_BTCOEX_STOMP_NONE); + ath9k_cmn_btcoex_bt_stomp(common, ATH_BTCOEX_STOMP_NONE); else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL) - ath9k_btcoex_bt_stomp(sc, ATH_BTCOEX_STOMP_LOW); + ath9k_cmn_btcoex_bt_stomp(common, ATH_BTCOEX_STOMP_LOW); spin_unlock_bh(&btcoex->btcoex_lock); } -- cgit v1.2.3 From 21cb987914cb5334af78378141efed77505ea987 Mon Sep 17 00:00:00 2001 From: Vivek Natarajan Date: Wed, 18 Aug 2010 19:57:49 +0530 Subject: ath9k_htc: Add support for bluetooth coexistence. Signed-off-by: Vivek Natarajan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/Makefile | 3 +- drivers/net/wireless/ath/ath9k/btcoex.c | 7 ++ drivers/net/wireless/ath/ath9k/hif_usb.c | 3 +- drivers/net/wireless/ath/ath9k/htc.h | 42 +++++--- drivers/net/wireless/ath/ath9k/htc_drv_gpio.c | 134 ++++++++++++++++++++++++++ drivers/net/wireless/ath/ath9k/htc_drv_init.c | 42 +++++++- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 12 +++ drivers/net/wireless/ath/ath9k/htc_hst.c | 4 +- drivers/net/wireless/ath/ath9k/htc_hst.h | 2 +- drivers/net/wireless/ath/ath9k/wmi.c | 2 + drivers/net/wireless/ath/ath9k/wmi.h | 1 + 11 files changed, 230 insertions(+), 22 deletions(-) create mode 100644 drivers/net/wireless/ath/ath9k/htc_drv_gpio.c (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 973ae4f49f35..4555e9983903 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -46,6 +46,7 @@ ath9k_htc-y += htc_hst.o \ htc_drv_txrx.o \ htc_drv_main.o \ htc_drv_beacon.o \ - htc_drv_init.o + htc_drv_init.o \ + htc_drv_gpio.o obj-$(CONFIG_ATH9K_HTC) += ath9k_htc.o diff --git a/drivers/net/wireless/ath/ath9k/btcoex.c b/drivers/net/wireless/ath/ath9k/btcoex.c index fb4ac15f3b93..6a92e57fddf0 100644 --- a/drivers/net/wireless/ath/ath9k/btcoex.c +++ b/drivers/net/wireless/ath/ath9k/btcoex.c @@ -168,6 +168,7 @@ EXPORT_SYMBOL(ath9k_hw_btcoex_set_weight); static void ath9k_hw_btcoex_enable_3wire(struct ath_hw *ah) { struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; + u32 val; /* * Program coex mode and weight registers to @@ -177,6 +178,12 @@ static void ath9k_hw_btcoex_enable_3wire(struct ath_hw *ah) REG_WRITE(ah, AR_BT_COEX_WEIGHT, btcoex_hw->bt_coex_weights); REG_WRITE(ah, AR_BT_COEX_MODE2, btcoex_hw->bt_coex_mode2); + if (AR_SREV_9271(ah)) { + val = REG_READ(ah, 0x50040); + val &= 0xFFFFFEFF; + REG_WRITE(ah, 0x50040, val); + } + REG_RMW_FIELD(ah, AR_QUIET1, AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1); REG_RMW_FIELD(ah, AR_PCU_MISC, AR_PCU_BT_ANT_PREVENT_RX, 0); diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 17e7a9a367e7..495f18950ac9 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -920,7 +920,8 @@ static int ath9k_hif_usb_probe(struct usb_interface *interface, } ret = ath9k_htc_hw_init(hif_dev->htc_handle, - &hif_dev->udev->dev, hif_dev->device_id); + &hif_dev->udev->dev, hif_dev->device_id, + hif_dev->udev->product); if (ret) { ret = -EINVAL; goto err_htc_hw_init; diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h index 43b9e21bc562..75ecf6a30d25 100644 --- a/drivers/net/wireless/ath/ath9k/htc.h +++ b/drivers/net/wireless/ath/ath9k/htc.h @@ -316,17 +316,32 @@ struct htc_beacon_config { u8 dtim_count; }; -#define OP_INVALID BIT(0) -#define OP_SCANNING BIT(1) -#define OP_FULL_RESET BIT(2) -#define OP_LED_ASSOCIATED BIT(3) -#define OP_LED_ON BIT(4) -#define OP_PREAMBLE_SHORT BIT(5) -#define OP_PROTECT_ENABLE BIT(6) -#define OP_ASSOCIATED BIT(7) -#define OP_ENABLE_BEACON BIT(8) -#define OP_LED_DEINIT BIT(9) -#define OP_UNPLUGGED BIT(10) +struct ath_btcoex { + u32 bt_priority_cnt; + unsigned long bt_priority_time; + int bt_stomp_type; /* Types of BT stomping */ + u32 btcoex_no_stomp; + u32 btcoex_period; + u32 btscan_no_stomp; +}; + +void ath_htc_init_btcoex_work(struct ath9k_htc_priv *priv); +void ath_htc_resume_btcoex_work(struct ath9k_htc_priv *priv); +void ath_htc_cancel_btcoex_work(struct ath9k_htc_priv *priv); + +#define OP_INVALID BIT(0) +#define OP_SCANNING BIT(1) +#define OP_FULL_RESET BIT(2) +#define OP_LED_ASSOCIATED BIT(3) +#define OP_LED_ON BIT(4) +#define OP_PREAMBLE_SHORT BIT(5) +#define OP_PROTECT_ENABLE BIT(6) +#define OP_ASSOCIATED BIT(7) +#define OP_ENABLE_BEACON BIT(8) +#define OP_LED_DEINIT BIT(9) +#define OP_UNPLUGGED BIT(10) +#define OP_BT_PRIORITY_DETECTED BIT(11) +#define OP_BT_SCAN BIT(12) struct ath9k_htc_priv { struct device *dev; @@ -391,6 +406,9 @@ struct ath9k_htc_priv { int cabq; int hwq_map[WME_NUM_AC]; + struct ath_btcoex btcoex; + struct delayed_work coex_period_work; + struct delayed_work duty_cycle_work; #ifdef CONFIG_ATH9K_HTC_DEBUGFS struct ath9k_debug debug; #endif @@ -443,7 +461,7 @@ void ath9k_init_leds(struct ath9k_htc_priv *priv); void ath9k_deinit_leds(struct ath9k_htc_priv *priv); int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev, - u16 devid); + u16 devid, char *product); void ath9k_htc_disconnect_device(struct htc_target *htc_handle, bool hotunplug); #ifdef CONFIG_PM int ath9k_htc_resume(struct htc_target *htc_handle); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c new file mode 100644 index 000000000000..50eec9a3b88c --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c @@ -0,0 +1,134 @@ +#include "htc.h" + +/******************/ +/* BTCOEX */ +/******************/ + +/* + * Detects if there is any priority bt traffic + */ +static void ath_detect_bt_priority(struct ath9k_htc_priv *priv) +{ + struct ath_btcoex *btcoex = &priv->btcoex; + struct ath_hw *ah = priv->ah; + + if (ath9k_hw_gpio_get(ah, ah->btcoex_hw.btpriority_gpio)) + btcoex->bt_priority_cnt++; + + if (time_after(jiffies, btcoex->bt_priority_time + + msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) { + priv->op_flags &= ~(OP_BT_PRIORITY_DETECTED | OP_BT_SCAN); + /* Detect if colocated bt started scanning */ + if (btcoex->bt_priority_cnt >= ATH_BT_CNT_SCAN_THRESHOLD) { + ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX, + "BT scan detected"); + priv->op_flags |= (OP_BT_SCAN | + OP_BT_PRIORITY_DETECTED); + } else if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) { + ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX, + "BT priority traffic detected"); + priv->op_flags |= OP_BT_PRIORITY_DETECTED; + } + + btcoex->bt_priority_cnt = 0; + btcoex->bt_priority_time = jiffies; + } +} + +/* + * This is the master bt coex work which runs for every + * 45ms, bt traffic will be given priority during 55% of this + * period while wlan gets remaining 45% + */ +static void ath_btcoex_period_work(struct work_struct *work) +{ + struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv, + coex_period_work.work); + struct ath_btcoex *btcoex = &priv->btcoex; + struct ath_common *common = ath9k_hw_common(priv->ah); + u32 timer_period; + bool is_btscan; + int ret; + u8 cmd_rsp, aggr; + + ath_detect_bt_priority(priv); + + is_btscan = !!(priv->op_flags & OP_BT_SCAN); + + aggr = priv->op_flags & OP_BT_PRIORITY_DETECTED; + + WMI_CMD_BUF(WMI_AGGR_LIMIT_CMD, &aggr); + + ath9k_cmn_btcoex_bt_stomp(common, is_btscan ? ATH_BTCOEX_STOMP_ALL : + btcoex->bt_stomp_type); + + timer_period = is_btscan ? btcoex->btscan_no_stomp : + btcoex->btcoex_no_stomp; + ieee80211_queue_delayed_work(priv->hw, &priv->duty_cycle_work, + msecs_to_jiffies(timer_period)); + ieee80211_queue_delayed_work(priv->hw, &priv->coex_period_work, + msecs_to_jiffies(btcoex->btcoex_period)); +} + +/* + * Work to time slice between wlan and bt traffic and + * configure weight registers + */ +static void ath_btcoex_duty_cycle_work(struct work_struct *work) +{ + struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv, + duty_cycle_work.work); + struct ath_hw *ah = priv->ah; + struct ath_btcoex *btcoex = &priv->btcoex; + struct ath_common *common = ath9k_hw_common(ah); + bool is_btscan = priv->op_flags & OP_BT_SCAN; + + ath_print(common, ATH_DBG_BTCOEX, + "time slice work for bt and wlan\n"); + + if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || is_btscan) + ath9k_cmn_btcoex_bt_stomp(common, ATH_BTCOEX_STOMP_NONE); + else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL) + ath9k_cmn_btcoex_bt_stomp(common, ATH_BTCOEX_STOMP_LOW); +} + +void ath_htc_init_btcoex_work(struct ath9k_htc_priv *priv) +{ + struct ath_btcoex *btcoex = &priv->btcoex; + + btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD; + btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) * + btcoex->btcoex_period / 100; + btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) * + btcoex->btcoex_period / 100; + INIT_DELAYED_WORK(&priv->coex_period_work, ath_btcoex_period_work); + INIT_DELAYED_WORK(&priv->duty_cycle_work, ath_btcoex_duty_cycle_work); +} + +/* + * (Re)start btcoex work + */ + +void ath_htc_resume_btcoex_work(struct ath9k_htc_priv *priv) +{ + struct ath_btcoex *btcoex = &priv->btcoex; + struct ath_hw *ah = priv->ah; + + ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX, + "Starting btcoex work"); + + btcoex->bt_priority_cnt = 0; + btcoex->bt_priority_time = jiffies; + priv->op_flags &= ~(OP_BT_PRIORITY_DETECTED | OP_BT_SCAN); + ieee80211_queue_delayed_work(priv->hw, &priv->coex_period_work, 0); +} + + +/* + * Cancel btcoex and bt duty cycle work. + */ +void ath_htc_cancel_btcoex_work(struct ath9k_htc_priv *priv) +{ + cancel_delayed_work_sync(&priv->coex_period_work); + cancel_delayed_work_sync(&priv->duty_cycle_work); +} diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index 2d4279191d7a..695e2b088d10 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -41,6 +41,8 @@ MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption"); .max_power = 20, \ } +#define ATH_HTC_BTCOEX_PRODUCT_ID "wb193" + static struct ieee80211_channel ath9k_2ghz_channels[] = { CHAN2G(2412, 0), /* Channel 1 */ CHAN2G(2417, 1), /* Channel 2 */ @@ -605,7 +607,31 @@ static void ath9k_init_misc(struct ath9k_htc_priv *priv) priv->ah->opmode = NL80211_IFTYPE_STATION; } -static int ath9k_init_priv(struct ath9k_htc_priv *priv, u16 devid) +static void ath9k_init_btcoex(struct ath9k_htc_priv *priv) +{ + int qnum; + + switch (priv->ah->btcoex_hw.scheme) { + case ATH_BTCOEX_CFG_NONE: + break; + case ATH_BTCOEX_CFG_3WIRE: + priv->ah->btcoex_hw.btactive_gpio = 7; + priv->ah->btcoex_hw.btpriority_gpio = 6; + priv->ah->btcoex_hw.wlanactive_gpio = 8; + priv->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; + ath9k_hw_btcoex_init_3wire(priv->ah); + ath_htc_init_btcoex_work(priv); + qnum = priv->hwq_map[WME_AC_BE]; + ath9k_hw_init_btcoex_hw(priv->ah, qnum); + break; + default: + WARN_ON(1); + break; + } +} + +static int ath9k_init_priv(struct ath9k_htc_priv *priv, + u16 devid, char *product) { struct ath_hw *ah = NULL; struct ath_common *common; @@ -672,6 +698,11 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv, u16 devid) ath9k_init_channels_rates(priv); ath9k_init_misc(priv); + if (product && strncmp(product, ATH_HTC_BTCOEX_PRODUCT_ID, 5) == 0) { + ah->btcoex_hw.scheme = ATH_BTCOEX_CFG_3WIRE; + ath9k_init_btcoex(priv); + } + return 0; err_queues: @@ -734,7 +765,8 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, SET_IEEE80211_PERM_ADDR(hw, common->macaddr); } -static int ath9k_init_device(struct ath9k_htc_priv *priv, u16 devid) +static int ath9k_init_device(struct ath9k_htc_priv *priv, + u16 devid, char *product) { struct ieee80211_hw *hw = priv->hw; struct ath_common *common; @@ -743,7 +775,7 @@ static int ath9k_init_device(struct ath9k_htc_priv *priv, u16 devid) struct ath_regulatory *reg; /* Bring up device */ - error = ath9k_init_priv(priv, devid); + error = ath9k_init_priv(priv, devid, product); if (error != 0) goto err_init; @@ -801,7 +833,7 @@ err_init: } int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev, - u16 devid) + u16 devid, char *product) { struct ieee80211_hw *hw; struct ath9k_htc_priv *priv; @@ -835,7 +867,7 @@ int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev, /* The device may have been unplugged earlier. */ priv->op_flags &= ~OP_UNPLUGGED; - ret = ath9k_init_device(priv, devid); + ret = ath9k_init_device(priv, devid, product); if (ret) goto err_init; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 4e345be62435..5e318cb662c6 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1210,6 +1210,12 @@ static int ath9k_htc_start(struct ieee80211_hw *hw) ieee80211_wake_queues(hw); + if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) { + ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, + AR_STOMP_LOW_WLAN_WGHT); + ath9k_hw_btcoex_enable(ah); + ath_htc_resume_btcoex_work(priv); + } mutex_unlock(&priv->mutex); return ret; @@ -1254,6 +1260,12 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw) "Monitor interface removed\n"); } + if (ah->btcoex_hw.enabled) { + ath9k_hw_btcoex_disable(ah); + if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) + ath_htc_cancel_btcoex_work(priv); + } + ath9k_hw_phy_disable(ah); ath9k_hw_disable(ah); ath9k_hw_configpcipowersave(ah, 1, 1); diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c index 705c0f342e1c..861ec9269309 100644 --- a/drivers/net/wireless/ath/ath9k/htc_hst.c +++ b/drivers/net/wireless/ath/ath9k/htc_hst.c @@ -462,9 +462,9 @@ void ath9k_htc_hw_free(struct htc_target *htc) } int ath9k_htc_hw_init(struct htc_target *target, - struct device *dev, u16 devid) + struct device *dev, u16 devid, char *product) { - if (ath9k_htc_probe_device(target, dev, devid)) { + if (ath9k_htc_probe_device(target, dev, devid, product)) { printk(KERN_ERR "Failed to initialize the device\n"); return -ENODEV; } diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.h b/drivers/net/wireless/ath/ath9k/htc_hst.h index faba6790328b..07b6509d5896 100644 --- a/drivers/net/wireless/ath/ath9k/htc_hst.h +++ b/drivers/net/wireless/ath/ath9k/htc_hst.h @@ -239,7 +239,7 @@ struct htc_target *ath9k_htc_hw_alloc(void *hif_handle, struct device *dev); void ath9k_htc_hw_free(struct htc_target *htc); int ath9k_htc_hw_init(struct htc_target *target, - struct device *dev, u16 devid); + struct device *dev, u16 devid, char *product); void ath9k_htc_hw_deinit(struct htc_target *target, bool hot_unplug); #endif /* HTC_HST_H */ diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c index 6260faa658a2..45fe9cac7971 100644 --- a/drivers/net/wireless/ath/ath9k/wmi.c +++ b/drivers/net/wireless/ath/ath9k/wmi.c @@ -85,6 +85,8 @@ static const char *wmi_cmd_to_name(enum wmi_cmd_id wmi_cmd) return "WMI_TGT_DETACH_CMDID"; case WMI_TGT_TXQ_ENABLE_CMDID: return "WMI_TGT_TXQ_ENABLE_CMDID"; + case WMI_AGGR_LIMIT_CMD: + return "WMI_AGGR_LIMIT_CMD"; } return "Bogus"; diff --git a/drivers/net/wireless/ath/ath9k/wmi.h b/drivers/net/wireless/ath/ath9k/wmi.h index 765db5faa2d3..a0bf857625df 100644 --- a/drivers/net/wireless/ath/ath9k/wmi.h +++ b/drivers/net/wireless/ath/ath9k/wmi.h @@ -71,6 +71,7 @@ enum wmi_cmd_id { WMI_TX_AGGR_ENABLE_CMDID, WMI_TGT_DETACH_CMDID, WMI_TGT_TXQ_ENABLE_CMDID, + WMI_AGGR_LIMIT_CMD = 0x0026, }; enum wmi_event_id { -- cgit v1.2.3 From 8801df86d79b1ffda94e3b937bc19bbe9a66557d Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Sat, 21 Aug 2010 16:39:02 -0400 Subject: ath5k: trivial spelling fixes Fix some comments: s/transmition/transmission/ s/puting/putting/ Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/pcu.c | 4 ++-- drivers/net/wireless/ath/ath5k/phy.c | 2 +- drivers/net/wireless/ath/ath5k/reset.c | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c index 3fef5931ec3a..bb2e21553d1b 100644 --- a/drivers/net/wireless/ath/ath5k/pcu.c +++ b/drivers/net/wireless/ath/ath5k/pcu.c @@ -137,11 +137,11 @@ void ath5k_hw_update_mib_counters(struct ath5k_hw *ah) * ath5k_hw_set_ack_bitrate - set bitrate for ACKs * * @ah: The &struct ath5k_hw - * @high: Flag to determine if we want to use high transmition rate + * @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 transmition rate (check out control_rates array inside reset.c). + * 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). */ diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c index c7c446592374..8c27a3644d2f 100644 --- a/drivers/net/wireless/ath/ath5k/phy.c +++ b/drivers/net/wireless/ath/ath5k/phy.c @@ -2987,7 +2987,7 @@ ath5k_setup_rate_powertable(struct ath5k_hw *ah, u16 max_pwr, /* - * Set transmition power + * Set transmission power */ int ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c index 4154959125b6..58912cd762d9 100644 --- a/drivers/net/wireless/ath/ath5k/reset.c +++ b/drivers/net/wireless/ath/ath5k/reset.c @@ -326,7 +326,7 @@ commit: * register). After this MAC and Baseband are * disabled and a full reset is needed to come * back. This way we save as much power as possible - * without puting the card on full sleep. + * without putting the card on full sleep. */ int ath5k_hw_on_hold(struct ath5k_hw *ah) { @@ -344,7 +344,7 @@ int ath5k_hw_on_hold(struct ath5k_hw *ah) /* * Put chipset on warm reset... * - * Note: puting PCI core on warm reset on PCI-E cards + * Note: putting PCI core on warm reset on PCI-E cards * results card to hang and always return 0xffff... so * we ingore that flag for PCI-E cards. On PCI cards * this flag gets cleared after 64 PCI clocks. @@ -400,7 +400,7 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial) /* * Put chipset on warm reset... * - * Note: puting PCI core on warm reset on PCI-E cards + * Note: putting PCI core on warm reset on PCI-E cards * results card to hang and always return 0xffff... so * we ingore that flag for PCI-E cards. On PCI cards * this flag gets cleared after 64 PCI clocks. -- cgit v1.2.3 From b4c52612069c2756ad7506754765bfee57315efe Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Sat, 21 Aug 2010 16:39:03 -0400 Subject: ath5k: log descriptor chains at a new debug level Descriptors are currently logged with ATH5K_DEBUG_RESET, which isn't really apt, and also means we can't see just the descriptor setup or just the resets. Add a new debug level just for that. Acked-by: Bruno Randolf Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/debug.c | 5 +++-- drivers/net/wireless/ath/ath5k/debug.h | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c index 4cccc29964f6..1b7c6d7fde93 100644 --- a/drivers/net/wireless/ath/ath5k/debug.c +++ b/drivers/net/wireless/ath/ath5k/debug.c @@ -312,6 +312,7 @@ static const struct { { ATH5K_DEBUG_DUMP_TX, "dumptx", "print transmit skb content" }, { ATH5K_DEBUG_DUMPBANDS, "dumpbands", "dump bands" }, { ATH5K_DEBUG_ANI, "ani", "adaptive noise immunity" }, + { ATH5K_DEBUG_DESC, "desc", "descriptor chains" }, { ATH5K_DEBUG_ANY, "all", "show all debug levels" }, }; @@ -955,7 +956,7 @@ ath5k_debug_printrxbuffs(struct ath5k_softc *sc, struct ath5k_hw *ah) struct ath5k_rx_status rs = {}; int status; - if (likely(!(sc->debug.level & ATH5K_DEBUG_RESET))) + if (likely(!(sc->debug.level & ATH5K_DEBUG_DESC))) return; printk(KERN_DEBUG "rxdp %x, rxlink %p\n", @@ -997,7 +998,7 @@ ath5k_debug_printtxbuf(struct ath5k_softc *sc, struct ath5k_buf *bf) struct ath5k_tx_status ts = {}; int done; - if (likely(!(sc->debug.level & ATH5K_DEBUG_RESET))) + if (likely(!(sc->debug.level & ATH5K_DEBUG_DESC))) return; done = sc->ah->ah_proc_tx_desc(sc->ah, bf->desc, &ts); diff --git a/drivers/net/wireless/ath/ath5k/debug.h b/drivers/net/wireless/ath/ath5k/debug.h index 606ae94a9157..9b22722a95f0 100644 --- a/drivers/net/wireless/ath/ath5k/debug.h +++ b/drivers/net/wireless/ath/ath5k/debug.h @@ -95,6 +95,7 @@ struct ath5k_dbg_info { * @ATH5K_DEBUG_DUMP_TX: print transmit skb content * @ATH5K_DEBUG_DUMPBANDS: dump bands * @ATH5K_DEBUG_TRACE: trace function calls + * @ATH5K_DEBUG_DESC: descriptor setup * @ATH5K_DEBUG_ANY: show at any debug level * * The debug level is used to control the amount and type of debugging output @@ -117,6 +118,7 @@ enum ath5k_debug_level { ATH5K_DEBUG_DUMP_TX = 0x00000200, ATH5K_DEBUG_DUMPBANDS = 0x00000400, ATH5K_DEBUG_ANI = 0x00002000, + ATH5K_DEBUG_DESC = 0x00004000, ATH5K_DEBUG_ANY = 0xffffffff }; -- cgit v1.2.3 From df1c2986c4685776ed02802c170c7fb6f47d8218 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 21 Aug 2010 21:24:59 +0200 Subject: ath5k: re-order one of the frees on unwind There was a small misordering here. In the original code, if we were to go to err_free_ah then it wouldn't free the irq. Signed-off-by: Dan Carpenter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index f2fceb10c9fc..45789c8990d3 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -700,10 +700,10 @@ ath5k_pci_probe(struct pci_dev *pdev, return 0; err_ah: ath5k_hw_detach(sc->ah); -err_irq: - free_irq(pdev->irq, sc); err_free_ah: kfree(sc->ah); +err_irq: + free_irq(pdev->irq, sc); err_free: ieee80211_free_hw(hw); err_map: -- cgit v1.2.3 From 53b1cf8a28c440fcb241f6fb84da71a7cb0f74f0 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Tue, 24 Aug 2010 21:37:14 -0400 Subject: ath5k: fix off-by-one in pilot magnitude mask If the symbol offset is 46, it will be counted in both the third and fourth bytes of the mask, and in this case the shift will be negative which can pollute high order bits in the mask. This may negatively impact OFDM symbol detection. Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/phy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c index 8c27a3644d2f..984ba92c7df3 100644 --- a/drivers/net/wireless/ath/ath5k/phy.c +++ b/drivers/net/wireless/ath/ath5k/phy.c @@ -1582,7 +1582,7 @@ ath5k_hw_set_spur_mitigation_filter(struct ath5k_hw *ah, else if (curr_sym_off >= 31 && curr_sym_off <= 46) mag_mask[2] |= plt_mag_map << (curr_sym_off - 31) * 2; - else if (curr_sym_off >= 46 && curr_sym_off <= 53) + else if (curr_sym_off >= 47 && curr_sym_off <= 53) mag_mask[3] |= plt_mag_map << (curr_sym_off - 47) * 2; -- cgit v1.2.3 From 93dbbcc45c364d62d58737fb9fe6d31085d6a999 Mon Sep 17 00:00:00 2001 From: Vivek Natarajan Date: Wed, 25 Aug 2010 19:34:52 +0530 Subject: ath9k: Change the default LED pattern. All major Atheros customers require the led to be in continuous ON state rather than the blinking pattern. Signed-off-by: Vivek Natarajan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 243c1775f343..3dbff8d07766 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -33,7 +33,7 @@ int modparam_nohwcrypt; module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444); MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption"); -int led_blink = 1; +int led_blink; module_param_named(blink, led_blink, int, 0444); MODULE_PARM_DESC(blink, "Enable LED blink on activity"); -- cgit v1.2.3 From fe67470ddd075cb7d538de1570950da3c0f67e65 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Fri, 27 Aug 2010 12:09:00 +0530 Subject: ath9k_htc: Enable ANI in associated state only While scanning, ANI is triggered unnecessarily where sta is in unassociated state. And cancelling ani work in ath9k_htc_stop is not required. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 5e318cb662c6..f4672073ac0a 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1239,7 +1239,6 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw) /* Cancel all the running timers/work .. */ cancel_work_sync(&priv->ps_work); - cancel_delayed_work_sync(&priv->ath9k_ani_work); cancel_delayed_work_sync(&priv->ath9k_led_blink_work); ath9k_led_stop_brightness(priv); @@ -1787,7 +1786,8 @@ static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw) priv->op_flags |= OP_SCANNING; spin_unlock_bh(&priv->beacon_lock); cancel_work_sync(&priv->ps_work); - cancel_delayed_work_sync(&priv->ath9k_ani_work); + if (priv->op_flags & OP_ASSOCIATED) + cancel_delayed_work_sync(&priv->ath9k_ani_work); mutex_unlock(&priv->mutex); } @@ -1801,9 +1801,10 @@ static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw) priv->op_flags &= ~OP_SCANNING; spin_unlock_bh(&priv->beacon_lock); priv->op_flags |= OP_FULL_RESET; - if (priv->op_flags & OP_ASSOCIATED) + if (priv->op_flags & OP_ASSOCIATED) { ath9k_htc_beacon_config(priv, priv->vif); - ath_start_ani(priv); + ath_start_ani(priv); + } ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); } -- cgit v1.2.3 From 56363ddeeed3afc5277ca227209773bc1042cc7b Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 28 Aug 2010 18:21:21 +0200 Subject: ath9k: fix spurious MIC failure reports According to the hardware documentation, the MIC failure bit is only valid if the frame was decrypted using a valid TKIP key and is not a fragment. In some setups I've seen hardware-reported MIC failures on an AP that was configured for CCMP only, so it's clear that additional checks are necessary. Signed-off-by: Felix Fietkau Cc: stable@kernel.org Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath.h | 1 + drivers/net/wireless/ath/ath9k/common.c | 11 +++++++++++ drivers/net/wireless/ath/ath9k/mac.c | 3 ++- drivers/net/wireless/ath/ath9k/recv.c | 19 +++++++++++-------- 4 files changed, 25 insertions(+), 9 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index d32f2828b098..a706202fa67c 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -119,6 +119,7 @@ struct ath_common { u32 keymax; DECLARE_BITMAP(keymap, ATH_KEYMAX); + DECLARE_BITMAP(tkip_keymap, ATH_KEYMAX); u8 splitmic; struct ath_regulatory regulatory; diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c index 31cfe468e3fc..2dab64bb23a8 100644 --- a/drivers/net/wireless/ath/ath9k/common.c +++ b/drivers/net/wireless/ath/ath9k/common.c @@ -372,9 +372,13 @@ int ath9k_cmn_key_config(struct ath_common *common, set_bit(idx, common->keymap); if (key->cipher == WLAN_CIPHER_SUITE_TKIP) { set_bit(idx + 64, common->keymap); + set_bit(idx, common->tkip_keymap); + set_bit(idx + 64, common->tkip_keymap); if (common->splitmic) { set_bit(idx + 32, common->keymap); set_bit(idx + 64 + 32, common->keymap); + set_bit(idx + 32, common->tkip_keymap); + set_bit(idx + 64 + 32, common->tkip_keymap); } } @@ -399,10 +403,17 @@ void ath9k_cmn_key_delete(struct ath_common *common, return; clear_bit(key->hw_key_idx + 64, common->keymap); + + clear_bit(key->hw_key_idx, common->tkip_keymap); + clear_bit(key->hw_key_idx + 64, common->tkip_keymap); + if (common->splitmic) { ath9k_hw_keyreset(ah, key->hw_key_idx + 32); clear_bit(key->hw_key_idx + 32, common->keymap); clear_bit(key->hw_key_idx + 64 + 32, common->keymap); + + clear_bit(key->hw_key_idx + 32, common->tkip_keymap); + clear_bit(key->hw_key_idx + 64 + 32, common->tkip_keymap); } } EXPORT_SYMBOL(ath9k_cmn_key_delete); diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index e955bb9d98cb..0b7d1253f0c0 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -711,7 +711,8 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, rs->rs_phyerr = phyerr; } else if (ads.ds_rxstatus8 & AR_DecryptCRCErr) rs->rs_status |= ATH9K_RXERR_DECRYPT; - else if (ads.ds_rxstatus8 & AR_MichaelErr) + else if ((ads.ds_rxstatus8 & AR_MichaelErr) && + rs->rs_keyix != ATH9K_RXKEYIX_INVALID) rs->rs_status |= ATH9K_RXERR_MIC; } diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index a3fc987ebab0..534a91bcc1d9 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -870,15 +870,18 @@ static bool ath9k_rx_accept(struct ath_common *common, if (rx_stats->rs_status & ATH9K_RXERR_DECRYPT) { *decrypt_error = true; } else if (rx_stats->rs_status & ATH9K_RXERR_MIC) { - if (ieee80211_is_ctl(fc)) - /* - * Sometimes, we get invalid - * MIC failures on valid control frames. - * Remove these mic errors. - */ - rx_stats->rs_status &= ~ATH9K_RXERR_MIC; - else + /* + * The MIC error bit is only valid if the frame + * is not a control frame or fragment, and it was + * decrypted using a valid TKIP key. + */ + if (!ieee80211_is_ctl(fc) && + !ieee80211_has_morefrags(fc) && + !(le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG) && + test_bit(rx_stats->rs_keyix, common->tkip_keymap)) rxs->flag |= RX_FLAG_MMIC_ERROR; + else + rx_stats->rs_status &= ~ATH9K_RXERR_MIC; } /* * Reject error frames with the exception of -- cgit v1.2.3 From 754dc53641e0038cd4fd3574b1f5b3c7239f73ce Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Thu, 2 Sep 2010 01:34:41 -0700 Subject: ath9k_hw: Add capability flag for Antenna diversity and combining feature This is enabled only for ar9285. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/eeprom.h | 2 ++ drivers/net/wireless/ath/ath9k/eeprom_4k.c | 4 ++++ drivers/net/wireless/ath/ath9k/hw.c | 9 +++++++++ drivers/net/wireless/ath/ath9k/hw.h | 1 + 4 files changed, 16 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/eeprom.h b/drivers/net/wireless/ath/ath9k/eeprom.h index 7f48df1e2903..090778da5d67 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.h +++ b/drivers/net/wireless/ath/ath9k/eeprom.h @@ -266,6 +266,8 @@ enum eeprom_param { EEP_INTERNAL_REGULATOR, EEP_SWREG, EEP_PAPRD, + EEP_MODAL_VER, + EEP_ANT_DIV_CTL1, }; enum ar5416_rates { diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c index 9cccd12e8f21..2e1397b68a87 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c @@ -213,6 +213,10 @@ static u32 ath9k_hw_4k_get_eeprom(struct ath_hw *ah, return 0; case EEP_PWR_TABLE_OFFSET: return AR5416_PWR_TABLE_OFFSET_DB; + case EEP_MODAL_VER: + return pModal->version; + case EEP_ANT_DIV_CTL1: + return pModal->antdiv_ctl1; default: return 0; } diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 3384ca164562..68940a8864e0 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2056,6 +2056,7 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; u16 capField = 0, eeval; + u8 ant_div_ctl1; eeval = ah->eep_ops->get_eeprom(ah, EEP_REG_0); regulatory->current_rd = eeval; @@ -2280,6 +2281,14 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) if (AR_SREV_9287_10_OR_LATER(ah) || AR_SREV_9271(ah)) pCap->hw_caps |= ATH9K_HW_CAP_SGI_20; + if (AR_SREV_9285(ah)) + if (ah->eep_ops->get_eeprom(ah, EEP_MODAL_VER) >= 3) { + ant_div_ctl1 = + ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1); + if ((ant_div_ctl1 & 0x1) && ((ant_div_ctl1 >> 3) & 0x1)) + pCap->hw_caps |= ATH9K_HW_CAP_ANT_DIV_COMB; + } + return 0; } diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 1601dd439890..1a0efbd03e4f 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -204,6 +204,7 @@ enum ath9k_hw_caps { ATH9K_HW_CAP_FASTCLOCK = BIT(20), ATH9K_HW_CAP_SGI_20 = BIT(21), ATH9K_HW_CAP_PAPRD = BIT(22), + ATH9K_HW_CAP_ANT_DIV_COMB = BIT(23), }; struct ath9k_hw_capabilities { -- cgit v1.2.3 From 21cc630f47d8589a42d563e89be4c631edbe8716 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Thu, 2 Sep 2010 01:34:42 -0700 Subject: ath9k_hw: Add functions to get/set antenna diversity configuration Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9002_phy.c | 35 +++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath9k/ar9002_phy.h | 2 ++ drivers/net/wireless/ath/ath9k/hw.h | 10 +++++++++ 3 files changed, 47 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.c b/drivers/net/wireless/ath/ath9k/ar9002_phy.c index adbf031fbc5a..cd56c8692705 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c @@ -530,3 +530,38 @@ void ar9002_hw_attach_phy_ops(struct ath_hw *ah) ar9002_hw_set_nf_limits(ah); } + +void ath9k_hw_antdiv_comb_conf_get(struct ath_hw *ah, + struct ath_hw_antcomb_conf *antconf) +{ + u32 regval; + + regval = REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL); + antconf->main_lna_conf = (regval & AR_PHY_9285_ANT_DIV_MAIN_LNACONF) >> + AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S; + antconf->alt_lna_conf = (regval & AR_PHY_9285_ANT_DIV_ALT_LNACONF) >> + AR_PHY_9285_ANT_DIV_ALT_LNACONF_S; + antconf->fast_div_bias = (regval & AR_PHY_9285_FAST_DIV_BIAS) >> + AR_PHY_9285_FAST_DIV_BIAS_S; +} +EXPORT_SYMBOL(ath9k_hw_antdiv_comb_conf_get); + +void ath9k_hw_antdiv_comb_conf_set(struct ath_hw *ah, + struct ath_hw_antcomb_conf *antconf) +{ + u32 regval; + + regval = REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL); + regval &= ~(AR_PHY_9285_ANT_DIV_MAIN_LNACONF | + AR_PHY_9285_ANT_DIV_ALT_LNACONF | + AR_PHY_9285_FAST_DIV_BIAS); + regval |= ((antconf->main_lna_conf << AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S) + & AR_PHY_9285_ANT_DIV_MAIN_LNACONF); + regval |= ((antconf->alt_lna_conf << AR_PHY_9285_ANT_DIV_ALT_LNACONF_S) + & AR_PHY_9285_ANT_DIV_ALT_LNACONF); + regval |= ((antconf->fast_div_bias << AR_PHY_9285_FAST_DIV_BIAS_S) + & AR_PHY_9285_FAST_DIV_BIAS); + + REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regval); +} +EXPORT_SYMBOL(ath9k_hw_antdiv_comb_conf_set); diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.h b/drivers/net/wireless/ath/ath9k/ar9002_phy.h index c5151a4dd10b..37663dbbcf57 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_phy.h +++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.h @@ -302,6 +302,8 @@ #define AR_PHY_NEW_ADC_DC_OFFSET_CORR_ENABLE 0x80000000 #define AR_PHY_MULTICHAIN_GAIN_CTL 0x99ac +#define AR_PHY_9285_FAST_DIV_BIAS 0x00007E00 +#define AR_PHY_9285_FAST_DIV_BIAS_S 9 #define AR_PHY_9285_ANT_DIV_CTL_ALL 0x7f000000 #define AR_PHY_9285_ANT_DIV_CTL 0x01000000 #define AR_PHY_9285_ANT_DIV_CTL_S 24 diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 1a0efbd03e4f..197c717286e7 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -496,6 +496,12 @@ struct ath_gen_timer_table { } timer_mask; }; +struct ath_hw_antcomb_conf { + u8 main_lna_conf; + u8 alt_lna_conf; + u8 fast_div_bias; +}; + /** * struct ath_hw_private_ops - callbacks used internally by hardware code * @@ -889,6 +895,10 @@ void ath9k_hw_cfg_output(struct ath_hw *ah, u32 gpio, void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val); u32 ath9k_hw_getdefantenna(struct ath_hw *ah); void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna); +void ath9k_hw_antdiv_comb_conf_get(struct ath_hw *ah, + struct ath_hw_antcomb_conf *antconf); +void ath9k_hw_antdiv_comb_conf_set(struct ath_hw *ah, + struct ath_hw_antcomb_conf *antconf); /* General Operation */ bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout); -- cgit v1.2.3 From 102885a5d114abad8f9d4101f94ce5b28c232231 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Thu, 2 Sep 2010 01:34:43 -0700 Subject: ath9k: Implement an algorithm for Antenna diversity and combining This algorithm chooses the best main and alt lna out of LNA1, LNA2, LNA1+LNA2 and LNA1-LNA2 to improve rx for single chain chips(AR9285). This would greatly improve rx when there is only one antenna is connected with AR9285. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 56 ++++ drivers/net/wireless/ath/ath9k/init.c | 3 + drivers/net/wireless/ath/ath9k/recv.c | 545 +++++++++++++++++++++++++++++++++ 3 files changed, 604 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index f0197a6046ab..5fe570bb5c4c 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -481,6 +481,60 @@ struct ath_led { void ath_init_leds(struct ath_softc *sc); void ath_deinit_leds(struct ath_softc *sc); +/* Antenna diversity/combining */ +#define ATH_ANT_RX_CURRENT_SHIFT 4 +#define ATH_ANT_RX_MAIN_SHIFT 2 +#define ATH_ANT_RX_MASK 0x3 + +#define ATH_ANT_DIV_COMB_SHORT_SCAN_INTR 50 +#define ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT 0x100 +#define ATH_ANT_DIV_COMB_MAX_PKTCOUNT 0x200 +#define ATH_ANT_DIV_COMB_INIT_COUNT 95 +#define ATH_ANT_DIV_COMB_MAX_COUNT 100 +#define ATH_ANT_DIV_COMB_ALT_ANT_RATIO 30 +#define ATH_ANT_DIV_COMB_ALT_ANT_RATIO2 20 + +#define ATH_ANT_DIV_COMB_LNA1_LNA2_DELTA -3 +#define ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA -1 +#define ATH_ANT_DIV_COMB_LNA1_DELTA_HI -4 +#define ATH_ANT_DIV_COMB_LNA1_DELTA_MID -2 +#define ATH_ANT_DIV_COMB_LNA1_DELTA_LOW 2 + +enum ath9k_ant_div_comb_lna_conf { + ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2, + ATH_ANT_DIV_COMB_LNA2, + ATH_ANT_DIV_COMB_LNA1, + ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2, +}; + +struct ath_ant_comb { + u16 count; + u16 total_pkt_count; + bool scan; + bool scan_not_start; + int main_total_rssi; + int alt_total_rssi; + int alt_recv_cnt; + int main_recv_cnt; + int rssi_lna1; + int rssi_lna2; + int rssi_add; + int rssi_sub; + int rssi_first; + int rssi_second; + int rssi_third; + bool alt_good; + int quick_scan_cnt; + int main_conf; + enum ath9k_ant_div_comb_lna_conf first_quick_scan_conf; + enum ath9k_ant_div_comb_lna_conf second_quick_scan_conf; + int first_bias; + int second_bias; + bool first_ratio; + bool second_ratio; + unsigned long scan_start_time; +}; + /********************/ /* Main driver core */ /********************/ @@ -597,6 +651,8 @@ struct ath_softc { struct ath_btcoex btcoex; struct ath_descdma txsdma; + + struct ath_ant_comb ant_comb; }; struct ath_wiphy { diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 3dbff8d07766..e7c07b3370cc 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -531,6 +531,9 @@ static void ath9k_init_misc(struct ath_softc *sc) sc->beacon.bslot[i] = NULL; sc->beacon.bslot_aphy[i] = NULL; } + + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) + sc->ant_comb.count = ATH_ANT_DIV_COMB_INIT_COUNT; } static int ath9k_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid, diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 534a91bcc1d9..b32c8f033d20 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -19,6 +19,15 @@ #define SKB_CB_ATHBUF(__skb) (*((struct ath_buf **)__skb->cb)) +static inline bool ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, + int mindelta, int main_rssi_avg, + int alt_rssi_avg, int pkt_count) +{ + return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) && + (alt_rssi_avg > main_rssi_avg + maxdelta)) || + (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50); +} + static inline bool ath9k_check_auto_sleep(struct ath_softc *sc) { return sc->ps_enabled && @@ -1076,6 +1085,539 @@ static void ath9k_rx_skb_postprocess(struct ath_common *common, rxs->flag &= ~RX_FLAG_DECRYPTED; } +static void ath_lnaconf_alt_good_scan(struct ath_ant_comb *antcomb, + struct ath_hw_antcomb_conf ant_conf, + int main_rssi_avg) +{ + antcomb->quick_scan_cnt = 0; + + if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA2) + antcomb->rssi_lna2 = main_rssi_avg; + else if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA1) + antcomb->rssi_lna1 = main_rssi_avg; + + switch ((ant_conf.main_lna_conf << 4) | ant_conf.alt_lna_conf) { + case (0x10): /* LNA2 A-B */ + antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; + antcomb->first_quick_scan_conf = + ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; + antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1; + break; + case (0x20): /* LNA1 A-B */ + antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; + antcomb->first_quick_scan_conf = + ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; + antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2; + break; + case (0x21): /* LNA1 LNA2 */ + antcomb->main_conf = ATH_ANT_DIV_COMB_LNA2; + antcomb->first_quick_scan_conf = + ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; + antcomb->second_quick_scan_conf = + ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; + break; + case (0x12): /* LNA2 LNA1 */ + antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1; + antcomb->first_quick_scan_conf = + ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; + antcomb->second_quick_scan_conf = + ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; + break; + case (0x13): /* LNA2 A+B */ + antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; + antcomb->first_quick_scan_conf = + ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; + antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1; + break; + case (0x23): /* LNA1 A+B */ + antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; + antcomb->first_quick_scan_conf = + ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; + antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2; + break; + default: + break; + } +} + +static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb, + struct ath_hw_antcomb_conf *div_ant_conf, + int main_rssi_avg, int alt_rssi_avg, + int alt_ratio) +{ + /* alt_good */ + switch (antcomb->quick_scan_cnt) { + case 0: + /* set alt to main, and alt to first conf */ + div_ant_conf->main_lna_conf = antcomb->main_conf; + div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf; + break; + case 1: + /* set alt to main, and alt to first conf */ + div_ant_conf->main_lna_conf = antcomb->main_conf; + div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf; + antcomb->rssi_first = main_rssi_avg; + antcomb->rssi_second = alt_rssi_avg; + + if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) { + /* main is LNA1 */ + if (ath_is_alt_ant_ratio_better(alt_ratio, + ATH_ANT_DIV_COMB_LNA1_DELTA_HI, + ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, + main_rssi_avg, alt_rssi_avg, + antcomb->total_pkt_count)) + antcomb->first_ratio = true; + else + antcomb->first_ratio = false; + } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) { + if (ath_is_alt_ant_ratio_better(alt_ratio, + ATH_ANT_DIV_COMB_LNA1_DELTA_MID, + ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, + main_rssi_avg, alt_rssi_avg, + antcomb->total_pkt_count)) + antcomb->first_ratio = true; + else + antcomb->first_ratio = false; + } else { + if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) && + (alt_rssi_avg > main_rssi_avg + + ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) || + (alt_rssi_avg > main_rssi_avg)) && + (antcomb->total_pkt_count > 50)) + antcomb->first_ratio = true; + else + antcomb->first_ratio = false; + } + break; + case 2: + antcomb->alt_good = false; + antcomb->scan_not_start = false; + antcomb->scan = false; + antcomb->rssi_first = main_rssi_avg; + antcomb->rssi_third = alt_rssi_avg; + + if (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) + antcomb->rssi_lna1 = alt_rssi_avg; + else if (antcomb->second_quick_scan_conf == + ATH_ANT_DIV_COMB_LNA2) + antcomb->rssi_lna2 = alt_rssi_avg; + else if (antcomb->second_quick_scan_conf == + ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2) { + if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) + antcomb->rssi_lna2 = main_rssi_avg; + else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) + antcomb->rssi_lna1 = main_rssi_avg; + } + + if (antcomb->rssi_lna2 > antcomb->rssi_lna1 + + ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA) + div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; + else + div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; + + if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) { + if (ath_is_alt_ant_ratio_better(alt_ratio, + ATH_ANT_DIV_COMB_LNA1_DELTA_HI, + ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, + main_rssi_avg, alt_rssi_avg, + antcomb->total_pkt_count)) + antcomb->second_ratio = true; + else + antcomb->second_ratio = false; + } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) { + if (ath_is_alt_ant_ratio_better(alt_ratio, + ATH_ANT_DIV_COMB_LNA1_DELTA_MID, + ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, + main_rssi_avg, alt_rssi_avg, + antcomb->total_pkt_count)) + antcomb->second_ratio = true; + else + antcomb->second_ratio = false; + } else { + if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) && + (alt_rssi_avg > main_rssi_avg + + ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) || + (alt_rssi_avg > main_rssi_avg)) && + (antcomb->total_pkt_count > 50)) + antcomb->second_ratio = true; + else + antcomb->second_ratio = false; + } + + /* set alt to the conf with maximun ratio */ + if (antcomb->first_ratio && antcomb->second_ratio) { + if (antcomb->rssi_second > antcomb->rssi_third) { + /* first alt*/ + if ((antcomb->first_quick_scan_conf == + ATH_ANT_DIV_COMB_LNA1) || + (antcomb->first_quick_scan_conf == + ATH_ANT_DIV_COMB_LNA2)) + /* Set alt LNA1 or LNA2*/ + if (div_ant_conf->main_lna_conf == + ATH_ANT_DIV_COMB_LNA2) + div_ant_conf->alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + else + div_ant_conf->alt_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + else + /* Set alt to A+B or A-B */ + div_ant_conf->alt_lna_conf = + antcomb->first_quick_scan_conf; + } else if ((antcomb->second_quick_scan_conf == + ATH_ANT_DIV_COMB_LNA1) || + (antcomb->second_quick_scan_conf == + ATH_ANT_DIV_COMB_LNA2)) { + /* Set alt LNA1 or LNA2 */ + if (div_ant_conf->main_lna_conf == + ATH_ANT_DIV_COMB_LNA2) + div_ant_conf->alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + else + div_ant_conf->alt_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + } else { + /* Set alt to A+B or A-B */ + div_ant_conf->alt_lna_conf = + antcomb->second_quick_scan_conf; + } + } else if (antcomb->first_ratio) { + /* first alt */ + if ((antcomb->first_quick_scan_conf == + ATH_ANT_DIV_COMB_LNA1) || + (antcomb->first_quick_scan_conf == + ATH_ANT_DIV_COMB_LNA2)) + /* Set alt LNA1 or LNA2 */ + if (div_ant_conf->main_lna_conf == + ATH_ANT_DIV_COMB_LNA2) + div_ant_conf->alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + else + div_ant_conf->alt_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + else + /* Set alt to A+B or A-B */ + div_ant_conf->alt_lna_conf = + antcomb->first_quick_scan_conf; + } else if (antcomb->second_ratio) { + /* second alt */ + if ((antcomb->second_quick_scan_conf == + ATH_ANT_DIV_COMB_LNA1) || + (antcomb->second_quick_scan_conf == + ATH_ANT_DIV_COMB_LNA2)) + /* Set alt LNA1 or LNA2 */ + if (div_ant_conf->main_lna_conf == + ATH_ANT_DIV_COMB_LNA2) + div_ant_conf->alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + else + div_ant_conf->alt_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + else + /* Set alt to A+B or A-B */ + div_ant_conf->alt_lna_conf = + antcomb->second_quick_scan_conf; + } else { + /* main is largest */ + if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) || + (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)) + /* Set alt LNA1 or LNA2 */ + if (div_ant_conf->main_lna_conf == + ATH_ANT_DIV_COMB_LNA2) + div_ant_conf->alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + else + div_ant_conf->alt_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + else + /* Set alt to A+B or A-B */ + div_ant_conf->alt_lna_conf = antcomb->main_conf; + } + break; + default: + break; + } +} + +void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf) +{ + /* Adjust the fast_div_bias based on main and alt lna conf */ + switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) { + case (0x01): /* A-B LNA2 */ + ant_conf->fast_div_bias = 0x3b; + break; + case (0x02): /* A-B LNA1 */ + ant_conf->fast_div_bias = 0x3d; + break; + case (0x03): /* A-B A+B */ + ant_conf->fast_div_bias = 0x1; + break; + case (0x10): /* LNA2 A-B */ + ant_conf->fast_div_bias = 0x7; + break; + case (0x12): /* LNA2 LNA1 */ + ant_conf->fast_div_bias = 0x2; + break; + case (0x13): /* LNA2 A+B */ + ant_conf->fast_div_bias = 0x7; + break; + case (0x20): /* LNA1 A-B */ + ant_conf->fast_div_bias = 0x6; + break; + case (0x21): /* LNA1 LNA2 */ + ant_conf->fast_div_bias = 0x0; + break; + case (0x23): /* LNA1 A+B */ + ant_conf->fast_div_bias = 0x6; + break; + case (0x30): /* A+B A-B */ + ant_conf->fast_div_bias = 0x1; + break; + case (0x31): /* A+B LNA2 */ + ant_conf->fast_div_bias = 0x3b; + break; + case (0x32): /* A+B LNA1 */ + ant_conf->fast_div_bias = 0x3d; + break; + default: + break; + } +} + +/* Antenna diversity and combining */ +static void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs) +{ + struct ath_hw_antcomb_conf div_ant_conf; + struct ath_ant_comb *antcomb = &sc->ant_comb; + int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set; + int curr_main_set, curr_bias; + int main_rssi = rs->rs_rssi_ctl0; + int alt_rssi = rs->rs_rssi_ctl1; + int rx_ant_conf, main_ant_conf; + bool short_scan = false; + + rx_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_CURRENT_SHIFT) & + ATH_ANT_RX_MASK; + main_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_MAIN_SHIFT) & + ATH_ANT_RX_MASK; + + /* Record packet only when alt_rssi is positive */ + if (alt_rssi > 0) { + antcomb->total_pkt_count++; + antcomb->main_total_rssi += main_rssi; + antcomb->alt_total_rssi += alt_rssi; + if (main_ant_conf == rx_ant_conf) + antcomb->main_recv_cnt++; + else + antcomb->alt_recv_cnt++; + } + + /* Short scan check */ + if (antcomb->scan && antcomb->alt_good) { + if (time_after(jiffies, antcomb->scan_start_time + + msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR))) + short_scan = true; + else + if (antcomb->total_pkt_count == + ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) { + alt_ratio = ((antcomb->alt_recv_cnt * 100) / + antcomb->total_pkt_count); + if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO) + short_scan = true; + } + } + + if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) || + rs->rs_moreaggr) && !short_scan) + return; + + if (antcomb->total_pkt_count) { + alt_ratio = ((antcomb->alt_recv_cnt * 100) / + antcomb->total_pkt_count); + main_rssi_avg = (antcomb->main_total_rssi / + antcomb->total_pkt_count); + alt_rssi_avg = (antcomb->alt_total_rssi / + antcomb->total_pkt_count); + } + + + ath9k_hw_antdiv_comb_conf_get(sc->sc_ah, &div_ant_conf); + curr_alt_set = div_ant_conf.alt_lna_conf; + curr_main_set = div_ant_conf.main_lna_conf; + curr_bias = div_ant_conf.fast_div_bias; + + antcomb->count++; + + if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) { + if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) { + ath_lnaconf_alt_good_scan(antcomb, div_ant_conf, + main_rssi_avg); + antcomb->alt_good = true; + } else { + antcomb->alt_good = false; + } + + antcomb->count = 0; + antcomb->scan = true; + antcomb->scan_not_start = true; + } + + if (!antcomb->scan) { + if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) { + if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) { + /* Switch main and alt LNA */ + div_ant_conf.main_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + } else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) { + div_ant_conf.main_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + } + + goto div_comb_done; + } else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) && + (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) { + /* Set alt to another LNA */ + if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + + goto div_comb_done; + } + + if ((alt_rssi_avg < (main_rssi_avg + + ATH_ANT_DIV_COMB_LNA1_LNA2_DELTA))) + goto div_comb_done; + } + + if (!antcomb->scan_not_start) { + switch (curr_alt_set) { + case ATH_ANT_DIV_COMB_LNA2: + antcomb->rssi_lna2 = alt_rssi_avg; + antcomb->rssi_lna1 = main_rssi_avg; + antcomb->scan = true; + /* set to A+B */ + div_ant_conf.main_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; + break; + case ATH_ANT_DIV_COMB_LNA1: + antcomb->rssi_lna1 = alt_rssi_avg; + antcomb->rssi_lna2 = main_rssi_avg; + antcomb->scan = true; + /* set to A+B */ + div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA2; + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; + break; + case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2: + antcomb->rssi_add = alt_rssi_avg; + antcomb->scan = true; + /* set to A-B */ + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; + break; + case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2: + antcomb->rssi_sub = alt_rssi_avg; + antcomb->scan = false; + if (antcomb->rssi_lna2 > + (antcomb->rssi_lna1 + + ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) { + /* use LNA2 as main LNA */ + if ((antcomb->rssi_add > antcomb->rssi_lna1) && + (antcomb->rssi_add > antcomb->rssi_sub)) { + /* set to A+B */ + div_ant_conf.main_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; + } else if (antcomb->rssi_sub > + antcomb->rssi_lna1) { + /* set to A-B */ + div_ant_conf.main_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; + } else { + /* set to LNA1 */ + div_ant_conf.main_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + } + } else { + /* use LNA1 as main LNA */ + if ((antcomb->rssi_add > antcomb->rssi_lna2) && + (antcomb->rssi_add > antcomb->rssi_sub)) { + /* set to A+B */ + div_ant_conf.main_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; + } else if (antcomb->rssi_sub > + antcomb->rssi_lna1) { + /* set to A-B */ + div_ant_conf.main_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; + } else { + /* set to LNA2 */ + div_ant_conf.main_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + } + } + break; + default: + break; + } + } else { + if (!antcomb->alt_good) { + antcomb->scan_not_start = false; + /* Set alt to another LNA */ + if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) { + div_ant_conf.main_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + } else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) { + div_ant_conf.main_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + } + goto div_comb_done; + } + } + + ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf, + main_rssi_avg, alt_rssi_avg, + alt_ratio); + + antcomb->quick_scan_cnt++; + +div_comb_done: + ath_ant_div_conf_fast_divbias(&div_ant_conf); + + ath9k_hw_antdiv_comb_conf_set(sc->sc_ah, &div_ant_conf); + + antcomb->scan_start_time = jiffies; + antcomb->total_pkt_count = 0; + antcomb->main_total_rssi = 0; + antcomb->alt_total_rssi = 0; + antcomb->main_recv_cnt = 0; + antcomb->alt_recv_cnt = 0; +} + int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) { struct ath_buf *bf; @@ -1213,6 +1755,9 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) PS_WAIT_FOR_PSPOLL_DATA)))) ath_rx_ps(sc, skb); + if (ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) + ath_ant_comb_scan(sc, &rs); + ath_rx_send_to_mac80211(hw, sc, skb, rxs); requeue: -- cgit v1.2.3 From 7cf1f2dd7de98e9a18536ab885d28ecb57e76cb1 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Fri, 3 Sep 2010 12:41:28 +0530 Subject: ath9k_htc: Enable fastcc for HTC devices. By enabling fastcc, the scan time reduced to half. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index f4672073ac0a..bab894873bf5 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -137,8 +137,6 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, if (priv->op_flags & OP_FULL_RESET) fastcc = false; - /* Fiddle around with fastcc later on, for now just use full reset */ - fastcc = false; ath9k_htc_ps_wakeup(priv); htc_stop(priv->htc); WMI_CMD(WMI_DISABLE_INTR_CMDID); @@ -146,9 +144,10 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, WMI_CMD(WMI_STOP_RECV_CMDID); ath_print(common, ATH_DBG_CONFIG, - "(%u MHz) -> (%u MHz), HT: %d, HT40: %d\n", + "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n", priv->ah->curchan->channel, - channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf)); + channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf), + fastcc); caldata = &priv->caldata[channel->hw_value]; ret = ath9k_hw_reset(ah, hchan, caldata, fastcc); -- cgit v1.2.3 From c2ba33424600e742f4d44ba2237024e6322a00fa Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Fri, 3 Sep 2010 16:00:00 +0530 Subject: ath9k_hw: Restore ANI registers to default during partial reset for AR9271 For AR9271 chips, if partial reset is done while scanning, the cycpwrThr1 will be set to maximum. This causes the degrade in DL throughput. So restore the ANI registers to default during the partial reset. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9002_hw.c | 50 ++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath9k/hw.c | 2 ++ drivers/net/wireless/ath/ath9k/hw.h | 1 + 3 files changed, 53 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c index 303c63da5ea3..94392daebaa0 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c @@ -580,3 +580,53 @@ void ar9002_hw_attach_ops(struct ath_hw *ah) else ath9k_hw_attach_ani_ops_old(ah); } + +void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan) +{ + u32 modesIndex; + int i; + + switch (chan->chanmode) { + case CHANNEL_A: + case CHANNEL_A_HT20: + modesIndex = 1; + break; + case CHANNEL_A_HT40PLUS: + case CHANNEL_A_HT40MINUS: + modesIndex = 2; + break; + case CHANNEL_G: + case CHANNEL_G_HT20: + case CHANNEL_B: + modesIndex = 4; + break; + case CHANNEL_G_HT40PLUS: + case CHANNEL_G_HT40MINUS: + modesIndex = 3; + break; + + default: + return; + } + + ENABLE_REGWRITE_BUFFER(ah); + + for (i = 0; i < ah->iniModes_9271_ANI_reg.ia_rows; i++) { + u32 reg = INI_RA(&ah->iniModes_9271_ANI_reg, i, 0); + u32 val = INI_RA(&ah->iniModes_9271_ANI_reg, i, modesIndex); + u32 val_orig; + + if (reg == AR_PHY_CCK_DETECT) { + val_orig = REG_READ(ah, reg); + val &= AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK; + val_orig &= ~AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK; + + REG_WRITE(ah, reg, val|val_orig); + } else + REG_WRITE(ah, reg, val); + } + + REGWRITE_BUFFER_FLUSH(ah); + DISABLE_REGWRITE_BUFFER(ah); + +} diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 68940a8864e0..afadade2a67e 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1263,6 +1263,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (ath9k_hw_channel_change(ah, chan)) { ath9k_hw_loadnf(ah, ah->curchan); ath9k_hw_start_nfcal(ah, true); + if (AR_SREV_9271(ah)) + ar9002_hw_load_ani_reg(ah, chan); return 0; } } diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 197c717286e7..1724b17bfc73 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -996,6 +996,7 @@ void ar9003_hw_attach_calib_ops(struct ath_hw *ah); void ar9002_hw_attach_ops(struct ath_hw *ah); void ar9003_hw_attach_ops(struct ath_hw *ah); +void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan); /* * ANI work can be shared between all families but a next * generation implementation of ANI will be used only for AR9003 only -- cgit v1.2.3 From fe8ee9ad80b28382111f0aab01690b09982e5691 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Mon, 6 Sep 2010 00:48:55 +0200 Subject: carl9170: mac80211 glue and command interface Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/carl9170.h | 626 +++++++++ drivers/net/wireless/ath/carl9170/cmd.c | 188 +++ drivers/net/wireless/ath/carl9170/cmd.h | 158 +++ drivers/net/wireless/ath/carl9170/led.c | 190 +++ drivers/net/wireless/ath/carl9170/main.c | 1855 ++++++++++++++++++++++++++ drivers/net/wireless/ath/carl9170/version.h | 7 + 6 files changed, 3024 insertions(+) create mode 100644 drivers/net/wireless/ath/carl9170/carl9170.h create mode 100644 drivers/net/wireless/ath/carl9170/cmd.c create mode 100644 drivers/net/wireless/ath/carl9170/cmd.h create mode 100644 drivers/net/wireless/ath/carl9170/led.c create mode 100644 drivers/net/wireless/ath/carl9170/main.c create mode 100644 drivers/net/wireless/ath/carl9170/version.h (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h new file mode 100644 index 000000000000..9f1d60359061 --- /dev/null +++ b/drivers/net/wireless/ath/carl9170/carl9170.h @@ -0,0 +1,626 @@ +/* + * Atheros CARL9170 driver + * + * Driver specific definitions + * + * Copyright 2008, Johannes Berg + * Copyright 2009, 2010, Christian Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef __CARL9170_H +#define __CARL9170_H + +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_CARL9170_LEDS +#include +#endif /* CONFIG_CARL170_LEDS */ +#ifdef CONFIG_CARL9170_WPC +#include +#endif /* CONFIG_CARL9170_WPC */ +#include "eeprom.h" +#include "wlan.h" +#include "hw.h" +#include "fwdesc.h" +#include "fwcmd.h" +#include "../regd.h" + +#ifdef CONFIG_CARL9170_DEBUGFS +#include "debug.h" +#endif /* CONFIG_CARL9170_DEBUGFS */ + +#define CARL9170FW_NAME "carl9170-1.fw" + +#define PAYLOAD_MAX (CARL9170_MAX_CMD_LEN / 4 - 1) + +enum carl9170_rf_init_mode { + CARL9170_RFI_NONE, + CARL9170_RFI_WARM, + CARL9170_RFI_COLD, +}; + +#define CARL9170_MAX_RX_BUFFER_SIZE 8192 + +enum carl9170_device_state { + CARL9170_UNKNOWN_STATE, + CARL9170_STOPPED, + CARL9170_IDLE, + CARL9170_STARTED, +}; + +#define CARL9170_NUM_TID 16 +#define WME_BA_BMP_SIZE 64 +#define CARL9170_TX_USER_RATE_TRIES 3 + +#define WME_AC_BE 2 +#define WME_AC_BK 3 +#define WME_AC_VI 1 +#define WME_AC_VO 0 + +#define TID_TO_WME_AC(_tid) \ + ((((_tid) == 0) || ((_tid) == 3)) ? WME_AC_BE : \ + (((_tid) == 1) || ((_tid) == 2)) ? WME_AC_BK : \ + (((_tid) == 4) || ((_tid) == 5)) ? WME_AC_VI : \ + WME_AC_VO) + +#define SEQ_DIFF(_start, _seq) \ + (((_start) - (_seq)) & 0x0fff) +#define SEQ_PREV(_seq) \ + (((_seq) - 1) & 0x0fff) +#define SEQ_NEXT(_seq) \ + (((_seq) + 1) & 0x0fff) +#define BAW_WITHIN(_start, _bawsz, _seqno) \ + ((((_seqno) - (_start)) & 0xfff) < (_bawsz)) + +enum carl9170_tid_state { + CARL9170_TID_STATE_INVALID, + CARL9170_TID_STATE_KILLED, + CARL9170_TID_STATE_SHUTDOWN, + CARL9170_TID_STATE_SUSPEND, + CARL9170_TID_STATE_PROGRESS, + CARL9170_TID_STATE_IDLE, + CARL9170_TID_STATE_XMIT, +}; + +#define CARL9170_BAW_BITS (2 * WME_BA_BMP_SIZE) +#define CARL9170_BAW_SIZE (BITS_TO_LONGS(CARL9170_BAW_BITS)) +#define CARL9170_BAW_LEN (DIV_ROUND_UP(CARL9170_BAW_BITS, BITS_PER_BYTE)) + +struct carl9170_sta_tid { + /* must be the first entry! */ + struct list_head list; + + /* temporary list for RCU unlink procedure */ + struct list_head tmp_list; + + /* lock for the following data structures */ + spinlock_t lock; + + unsigned int counter; + enum carl9170_tid_state state; + u8 tid; /* TID number ( 0 - 15 ) */ + u16 max; /* max. AMPDU size */ + + u16 snx; /* awaiting _next_ frame */ + u16 hsn; /* highest _queued_ sequence */ + u16 bsn; /* base of the tx/agg bitmap */ + unsigned long bitmap[CARL9170_BAW_SIZE]; + + /* Preaggregation reorder queue */ + struct sk_buff_head queue; +}; + +#define CARL9170_QUEUE_TIMEOUT 256 +#define CARL9170_BUMP_QUEUE 1000 +#define CARL9170_TX_TIMEOUT 2500 +#define CARL9170_JANITOR_DELAY 128 +#define CARL9170_QUEUE_STUCK_TIMEOUT 5500 + +#define CARL9170_NUM_TX_AGG_MAX 30 + +/* + * Tradeoff between stability/latency and speed. + * + * AR9170_TXQ_DEPTH is devised by dividing the amount of available + * tx buffers with the size of a full ethernet frame + overhead. + * + * Naturally: The higher the limit, the faster the device CAN send. + * However, even a slight over-commitment at the wrong time and the + * hardware is doomed to send all already-queued frames at suboptimal + * rates. This in turn leads to an enourmous amount of unsuccessful + * retries => Latency goes up, whereas the throughput goes down. CRASH! + */ +#define CARL9170_NUM_TX_LIMIT_HARD ((AR9170_TXQ_DEPTH * 3) / 2) +#define CARL9170_NUM_TX_LIMIT_SOFT (AR9170_TXQ_DEPTH) + +struct carl9170_tx_queue_stats { + unsigned int count; + unsigned int limit; + unsigned int len; +}; + +struct carl9170_vif { + unsigned int id; + struct ieee80211_vif *vif; +}; + +struct carl9170_vif_info { + struct list_head list; + bool active; + unsigned int id; + struct sk_buff *beacon; + bool enable_beacon; +}; + +#define AR9170_NUM_RX_URBS 16 +#define AR9170_NUM_RX_URBS_MUL 2 +#define AR9170_NUM_TX_URBS 8 +#define AR9170_NUM_RX_URBS_POOL (AR9170_NUM_RX_URBS_MUL * AR9170_NUM_RX_URBS) + +enum carl9170_device_features { + CARL9170_WPS_BUTTON = BIT(0), + CARL9170_ONE_LED = BIT(1), +}; + +#ifdef CONFIG_CARL9170_LEDS +struct ar9170; + +struct carl9170_led { + struct ar9170 *ar; + struct led_classdev l; + char name[32]; + unsigned int toggled; + bool last_state; + bool registered; +}; +#endif /* CONFIG_CARL9170_LEDS */ + +enum carl9170_restart_reasons { + CARL9170_RR_NO_REASON = 0, + CARL9170_RR_FATAL_FIRMWARE_ERROR, + CARL9170_RR_TOO_MANY_FIRMWARE_ERRORS, + CARL9170_RR_WATCHDOG, + CARL9170_RR_STUCK_TX, + CARL9170_RR_SLOW_SYSTEM, + CARL9170_RR_COMMAND_TIMEOUT, + CARL9170_RR_TOO_MANY_PHY_ERRORS, + CARL9170_RR_LOST_RSP, + CARL9170_RR_INVALID_RSP, + CARL9170_RR_USER_REQUEST, + + __CARL9170_RR_LAST, +}; + +enum carl9170_erp_modes { + CARL9170_ERP_INVALID, + CARL9170_ERP_AUTO, + CARL9170_ERP_MAC80211, + CARL9170_ERP_OFF, + CARL9170_ERP_CTS, + CARL9170_ERP_RTS, + __CARL9170_ERP_NUM, +}; + +struct ar9170 { + struct ath_common common; + struct ieee80211_hw *hw; + struct mutex mutex; + enum carl9170_device_state state; + spinlock_t state_lock; + enum carl9170_restart_reasons last_reason; + bool registered; + + /* USB */ + struct usb_device *udev; + struct usb_interface *intf; + struct usb_anchor rx_anch; + struct usb_anchor rx_work; + struct usb_anchor rx_pool; + struct usb_anchor tx_wait; + struct usb_anchor tx_anch; + struct usb_anchor tx_cmd; + struct usb_anchor tx_err; + struct tasklet_struct usb_tasklet; + atomic_t tx_cmd_urbs; + atomic_t tx_anch_urbs; + atomic_t rx_anch_urbs; + atomic_t rx_work_urbs; + atomic_t rx_pool_urbs; + kernel_ulong_t features; + + /* firmware settings */ + struct completion fw_load_wait; + struct completion fw_boot_wait; + struct { + const struct carl9170fw_desc_head *desc; + const struct firmware *fw; + unsigned int offset; + unsigned int address; + unsigned int cmd_bufs; + unsigned int api_version; + unsigned int vif_num; + unsigned int err_counter; + unsigned int bug_counter; + u32 beacon_addr; + unsigned int beacon_max_len; + bool rx_stream; + bool tx_stream; + unsigned int mem_blocks; + unsigned int mem_block_size; + unsigned int rx_size; + } fw; + + /* reset / stuck frames/queue detection */ + struct work_struct restart_work; + unsigned int restart_counter; + unsigned long queue_stop_timeout[__AR9170_NUM_TXQ]; + unsigned long max_queue_stop_timeout[__AR9170_NUM_TXQ]; + bool needs_full_reset; + atomic_t pending_restarts; + + /* interface mode settings */ + struct list_head vif_list; + unsigned long vif_bitmap; + unsigned int vifs; + struct carl9170_vif vif_priv[AR9170_MAX_VIRTUAL_MAC]; + + /* beaconing */ + spinlock_t beacon_lock; + unsigned int global_pretbtt; + unsigned int global_beacon_int; + struct carl9170_vif_info *beacon_iter; + unsigned int beacon_enabled; + + /* cryptographic engine */ + u64 usedkeys; + bool rx_software_decryption; + bool disable_offload; + + /* filter settings */ + u64 cur_mc_hash; + u32 cur_filter; + unsigned int filter_state; + bool sniffer_enabled; + + /* MAC */ + enum carl9170_erp_modes erp_mode; + + /* PHY */ + struct ieee80211_channel *channel; + int noise[6]; + unsigned int chan_fail; + unsigned int total_chan_fail; + u8 heavy_clip; + + /* power calibration data */ + u8 power_5G_leg[4]; + u8 power_2G_cck[4]; + u8 power_2G_ofdm[4]; + u8 power_5G_ht20[8]; + u8 power_5G_ht40[8]; + u8 power_2G_ht20[8]; + u8 power_2G_ht40[8]; + +#ifdef CONFIG_CARL9170_LEDS + /* LED */ + struct delayed_work led_work; + struct carl9170_led leds[AR9170_NUM_LEDS]; +#endif /* CONFIG_CARL9170_LEDS */ + + /* qos queue settings */ + spinlock_t tx_stats_lock; + struct carl9170_tx_queue_stats tx_stats[__AR9170_NUM_TXQ]; + struct ieee80211_tx_queue_params edcf[5]; + struct completion tx_flush; + + /* CMD */ + int cmd_seq; + int readlen; + u8 *readbuf; + spinlock_t cmd_lock; + struct completion cmd_wait; + union { + __le32 cmd_buf[PAYLOAD_MAX + 1]; + struct carl9170_cmd cmd; + struct carl9170_rsp rsp; + }; + + /* statistics */ + unsigned int tx_dropped; + unsigned int tx_ack_failures; + unsigned int tx_fcs_errors; + unsigned int tx_ampdu_timeout; + unsigned int rx_dropped; + + /* EEPROM */ + struct ar9170_eeprom eeprom; + + /* tx queuing */ + struct sk_buff_head tx_pending[__AR9170_NUM_TXQ]; + struct sk_buff_head tx_status[__AR9170_NUM_TXQ]; + struct delayed_work tx_janitor; + unsigned long tx_janitor_last_run; + bool tx_schedule; + + /* tx ampdu */ + struct work_struct ampdu_work; + spinlock_t tx_ampdu_list_lock; + struct carl9170_sta_tid *tx_ampdu_iter; + struct list_head tx_ampdu_list; + atomic_t tx_ampdu_upload; + atomic_t tx_ampdu_scheduler; + atomic_t tx_total_pending; + atomic_t tx_total_queued; + unsigned int tx_ampdu_list_len; + int current_density; + int current_factor; + bool tx_ampdu_schedule; + + /* internal memory management */ + spinlock_t mem_lock; + unsigned long *mem_bitmap; + atomic_t mem_free_blocks; + atomic_t mem_allocs; + + /* rxstream mpdu merge */ + struct ar9170_rx_head rx_plcp; + bool rx_has_plcp; + struct sk_buff *rx_failover; + int rx_failover_missing; + +#ifdef CONFIG_CARL9170_WPC + struct { + bool pbc_state; + struct input_dev *pbc; + char name[32]; + char phys[32]; + } wps; +#endif /* CONFIG_CARL9170_WPC */ + +#ifdef CONFIG_CARL9170_DEBUGFS + struct carl9170_debug debug; + struct dentry *debug_dir; +#endif /* CONFIG_CARL9170_DEBUGFS */ + + /* PSM */ + struct work_struct ps_work; + struct { + unsigned int dtim_counter; + unsigned long last_beacon; + unsigned long last_action; + unsigned long last_slept; + unsigned int sleep_ms; + unsigned int off_override; + bool state; + } ps; +}; + +enum carl9170_ps_off_override_reasons { + PS_OFF_VIF = BIT(0), + PS_OFF_BCN = BIT(1), + PS_OFF_5GHZ = BIT(2), +}; + +struct carl9170_ba_stats { + u8 ampdu_len; + u8 ampdu_ack_len; + bool clear; +}; + +struct carl9170_sta_info { + bool ht_sta; + unsigned int ampdu_max_len; + struct carl9170_sta_tid *agg[CARL9170_NUM_TID]; + struct carl9170_ba_stats stats[CARL9170_NUM_TID]; +}; + +struct carl9170_tx_info { + unsigned long timeout; + struct ar9170 *ar; + struct kref ref; +}; + +#define CHK_DEV_STATE(a, s) (((struct ar9170 *)a)->state >= (s)) +#define IS_INITIALIZED(a) (CHK_DEV_STATE(a, CARL9170_STOPPED)) +#define IS_ACCEPTING_CMD(a) (CHK_DEV_STATE(a, CARL9170_IDLE)) +#define IS_STARTED(a) (CHK_DEV_STATE(a, CARL9170_STARTED)) + +static inline void __carl9170_set_state(struct ar9170 *ar, + enum carl9170_device_state newstate) +{ + ar->state = newstate; +} + +static inline void carl9170_set_state(struct ar9170 *ar, + enum carl9170_device_state newstate) +{ + unsigned long flags; + + spin_lock_irqsave(&ar->state_lock, flags); + __carl9170_set_state(ar, newstate); + spin_unlock_irqrestore(&ar->state_lock, flags); +} + +static inline void carl9170_set_state_when(struct ar9170 *ar, + enum carl9170_device_state min, enum carl9170_device_state newstate) +{ + unsigned long flags; + + spin_lock_irqsave(&ar->state_lock, flags); + if (CHK_DEV_STATE(ar, min)) + __carl9170_set_state(ar, newstate); + spin_unlock_irqrestore(&ar->state_lock, flags); +} + +/* exported interface */ +void *carl9170_alloc(size_t priv_size); +int carl9170_register(struct ar9170 *ar); +void carl9170_unregister(struct ar9170 *ar); +void carl9170_free(struct ar9170 *ar); +void carl9170_restart(struct ar9170 *ar, const enum carl9170_restart_reasons r); +void carl9170_ps_check(struct ar9170 *ar); + +/* USB back-end */ +int carl9170_usb_open(struct ar9170 *ar); +void carl9170_usb_stop(struct ar9170 *ar); +void carl9170_usb_tx(struct ar9170 *ar, struct sk_buff *skb); +void carl9170_usb_handle_tx_err(struct ar9170 *ar); +int carl9170_exec_cmd(struct ar9170 *ar, const enum carl9170_cmd_oids, + u32 plen, void *payload, u32 rlen, void *resp); +int __carl9170_exec_cmd(struct ar9170 *ar, struct carl9170_cmd *cmd, + const bool free_buf); +int carl9170_usb_restart(struct ar9170 *ar); +void carl9170_usb_reset(struct ar9170 *ar); + +/* MAC */ +int carl9170_init_mac(struct ar9170 *ar); +int carl9170_set_qos(struct ar9170 *ar); +int carl9170_update_multicast(struct ar9170 *ar, const u64 mc_hast); +int carl9170_mod_virtual_mac(struct ar9170 *ar, const unsigned int id, + const u8 *mac); +int carl9170_set_operating_mode(struct ar9170 *ar); +int carl9170_set_beacon_timers(struct ar9170 *ar); +int carl9170_set_dyn_sifs_ack(struct ar9170 *ar); +int carl9170_set_rts_cts_rate(struct ar9170 *ar); +int carl9170_set_ampdu_settings(struct ar9170 *ar); +int carl9170_set_slot_time(struct ar9170 *ar); +int carl9170_set_mac_rates(struct ar9170 *ar); +int carl9170_set_hwretry_limit(struct ar9170 *ar, const u32 max_retry); +int carl9170_update_beacon(struct ar9170 *ar, const bool submit); +int carl9170_upload_key(struct ar9170 *ar, const u8 id, const u8 *mac, + const u8 ktype, const u8 keyidx, const u8 *keydata, const int keylen); +int carl9170_disable_key(struct ar9170 *ar, const u8 id); + +/* RX */ +void carl9170_rx(struct ar9170 *ar, void *buf, unsigned int len); +void carl9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len); + +/* TX */ +int carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb); +void carl9170_tx_janitor(struct work_struct *work); +void carl9170_tx_process_status(struct ar9170 *ar, + const struct carl9170_rsp *cmd); +void carl9170_tx_status(struct ar9170 *ar, struct sk_buff *skb, + const bool success); +void carl9170_tx_callback(struct ar9170 *ar, struct sk_buff *skb); +void carl9170_tx_drop(struct ar9170 *ar, struct sk_buff *skb); +void carl9170_tx_scheduler(struct ar9170 *ar); +void carl9170_tx_get_skb(struct sk_buff *skb); +int carl9170_tx_put_skb(struct sk_buff *skb); + +/* LEDs */ +#ifdef CONFIG_CARL9170_LEDS +int carl9170_led_register(struct ar9170 *ar); +void carl9170_led_unregister(struct ar9170 *ar); +#endif /* CONFIG_CARL9170_LEDS */ +int carl9170_led_init(struct ar9170 *ar); +int carl9170_led_set_state(struct ar9170 *ar, const u32 led_state); + +/* PHY / RF */ +int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel, + enum nl80211_channel_type bw, enum carl9170_rf_init_mode rfi); +int carl9170_get_noisefloor(struct ar9170 *ar); + +/* FW */ +int carl9170_parse_firmware(struct ar9170 *ar); +int carl9170_fw_fix_eeprom(struct ar9170 *ar); + +extern struct ieee80211_rate __carl9170_ratetable[]; +extern int modparam_noht; + +static inline struct ar9170 *carl9170_get_priv(struct carl9170_vif *carl_vif) +{ + return container_of(carl_vif, struct ar9170, + vif_priv[carl_vif->id]); +} + +static inline struct ieee80211_hdr *carl9170_get_hdr(struct sk_buff *skb) +{ + return (void *)((struct _carl9170_tx_superframe *) + skb->data)->frame_data; +} + +static inline u16 get_seq_h(struct ieee80211_hdr *hdr) +{ + return le16_to_cpu(hdr->seq_ctrl) >> 4; +} + +static inline u16 carl9170_get_seq(struct sk_buff *skb) +{ + return get_seq_h(carl9170_get_hdr(skb)); +} + +static inline u16 get_tid_h(struct ieee80211_hdr *hdr) +{ + return (ieee80211_get_qos_ctl(hdr))[0] & IEEE80211_QOS_CTL_TID_MASK; +} + +static inline u16 carl9170_get_tid(struct sk_buff *skb) +{ + return get_tid_h(carl9170_get_hdr(skb)); +} + +static inline struct ieee80211_vif * +carl9170_get_vif(struct carl9170_vif_info *priv) +{ + return container_of((void *)priv, struct ieee80211_vif, drv_priv); +} + +/* Protected by ar->mutex or RCU */ +static inline struct ieee80211_vif *carl9170_get_main_vif(struct ar9170 *ar) +{ + struct carl9170_vif_info *cvif; + + list_for_each_entry_rcu(cvif, &ar->vif_list, list) { + if (cvif->active) + return carl9170_get_vif(cvif); + } + + return NULL; +} + +static inline bool is_main_vif(struct ar9170 *ar, struct ieee80211_vif *vif) +{ + bool ret; + + rcu_read_lock(); + ret = (carl9170_get_main_vif(ar) == vif); + rcu_read_unlock(); + return ret; +} + +#endif /* __CARL9170_H */ diff --git a/drivers/net/wireless/ath/carl9170/cmd.c b/drivers/net/wireless/ath/carl9170/cmd.c new file mode 100644 index 000000000000..c21f3364bfec --- /dev/null +++ b/drivers/net/wireless/ath/carl9170/cmd.c @@ -0,0 +1,188 @@ +/* + * Atheros CARL9170 driver + * + * Basic HW register/memory/command access functions + * + * Copyright 2008, Johannes Berg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "carl9170.h" +#include "cmd.h" + +int carl9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val) +{ + __le32 buf[2] = { + cpu_to_le32(reg), + cpu_to_le32(val), + }; + int err; + + err = carl9170_exec_cmd(ar, CARL9170_CMD_WREG, sizeof(buf), + (u8 *) buf, 0, NULL); + if (err) { + if (net_ratelimit()) { + wiphy_err(ar->hw->wiphy, "writing reg %#x " + "(val %#x) failed (%d)\n", reg, val, err); + } + } + return err; +} + +int carl9170_read_mreg(struct ar9170 *ar, const int nregs, + const u32 *regs, u32 *out) +{ + int i, err; + __le32 *offs, *res; + + /* abuse "out" for the register offsets, must be same length */ + offs = (__le32 *)out; + for (i = 0; i < nregs; i++) + offs[i] = cpu_to_le32(regs[i]); + + /* also use the same buffer for the input */ + res = (__le32 *)out; + + err = carl9170_exec_cmd(ar, CARL9170_CMD_RREG, + 4 * nregs, (u8 *)offs, + 4 * nregs, (u8 *)res); + if (err) { + if (net_ratelimit()) { + wiphy_err(ar->hw->wiphy, "reading regs failed (%d)\n", + err); + } + return err; + } + + /* convert result to cpu endian */ + for (i = 0; i < nregs; i++) + out[i] = le32_to_cpu(res[i]); + + return 0; +} + +int carl9170_read_reg(struct ar9170 *ar, u32 reg, u32 *val) +{ + return carl9170_read_mreg(ar, 1, ®, val); +} + +int carl9170_echo_test(struct ar9170 *ar, const u32 v) +{ + u32 echores; + int err; + + err = carl9170_exec_cmd(ar, CARL9170_CMD_ECHO, + 4, (u8 *)&v, + 4, (u8 *)&echores); + if (err) + return err; + + if (v != echores) { + wiphy_info(ar->hw->wiphy, "wrong echo %x != %x", v, echores); + return -EINVAL; + } + + return 0; +} + +struct carl9170_cmd *carl9170_cmd_buf(struct ar9170 *ar, + const enum carl9170_cmd_oids cmd, const unsigned int len) +{ + struct carl9170_cmd *tmp; + + tmp = kzalloc(sizeof(struct carl9170_cmd_head) + len, GFP_ATOMIC); + if (tmp) { + tmp->hdr.cmd = cmd; + tmp->hdr.len = len; + } + + return tmp; +} + +int carl9170_reboot(struct ar9170 *ar) +{ + struct carl9170_cmd *cmd; + int err; + + cmd = carl9170_cmd_buf(ar, CARL9170_CMD_REBOOT_ASYNC, 0); + if (!cmd) + return -ENOMEM; + + err = __carl9170_exec_cmd(ar, (struct carl9170_cmd *)cmd, true); + return err; +} + +int carl9170_mac_reset(struct ar9170 *ar) +{ + return carl9170_exec_cmd(ar, CARL9170_CMD_SWRST, + 0, NULL, 0, NULL); +} + +int carl9170_bcn_ctrl(struct ar9170 *ar, const unsigned int vif_id, + const u32 mode, const u32 addr, const u32 len) +{ + struct carl9170_cmd *cmd; + + cmd = carl9170_cmd_buf(ar, CARL9170_CMD_BCN_CTRL_ASYNC, + sizeof(struct carl9170_bcn_ctrl_cmd)); + if (!cmd) + return -ENOMEM; + + cmd->bcn_ctrl.vif_id = cpu_to_le32(vif_id); + cmd->bcn_ctrl.mode = cpu_to_le32(mode); + cmd->bcn_ctrl.bcn_addr = cpu_to_le32(addr); + cmd->bcn_ctrl.bcn_len = cpu_to_le32(len); + + return __carl9170_exec_cmd(ar, cmd, true); +} + +int carl9170_powersave(struct ar9170 *ar, const bool ps) +{ + struct carl9170_cmd *cmd; + u32 state; + + cmd = carl9170_cmd_buf(ar, CARL9170_CMD_PSM_ASYNC, + sizeof(struct carl9170_psm)); + if (!cmd) + return -ENOMEM; + + if (ps) { + /* Sleep until next TBTT */ + state = CARL9170_PSM_SLEEP | 1; + } else { + /* wake up immediately */ + state = 1; + } + + cmd->psm.state = cpu_to_le32(state); + return __carl9170_exec_cmd(ar, cmd, true); +} diff --git a/drivers/net/wireless/ath/carl9170/cmd.h b/drivers/net/wireless/ath/carl9170/cmd.h new file mode 100644 index 000000000000..0fc83d2336fd --- /dev/null +++ b/drivers/net/wireless/ath/carl9170/cmd.h @@ -0,0 +1,158 @@ +/* + * Atheros CARL9170 driver + * + * Basic HW register/memory/command access functions + * + * Copyright 2008, Johannes Berg + * Copyright 2010, Christian Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef __CMD_H +#define __CMD_H + +#include "carl9170.h" + +/* basic HW access */ +int carl9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val); +int carl9170_read_reg(struct ar9170 *ar, const u32 reg, u32 *val); +int carl9170_read_mreg(struct ar9170 *ar, const int nregs, + const u32 *regs, u32 *out); +int carl9170_echo_test(struct ar9170 *ar, u32 v); +int carl9170_reboot(struct ar9170 *ar); +int carl9170_mac_reset(struct ar9170 *ar); +int carl9170_powersave(struct ar9170 *ar, const bool power_on); +int carl9170_bcn_ctrl(struct ar9170 *ar, const unsigned int vif_id, + const u32 mode, const u32 addr, const u32 len); + +static inline int carl9170_flush_cab(struct ar9170 *ar, + const unsigned int vif_id) +{ + return carl9170_bcn_ctrl(ar, vif_id, CARL9170_BCN_CTRL_DRAIN, 0, 0); +} + +struct carl9170_cmd *carl9170_cmd_buf(struct ar9170 *ar, + const enum carl9170_cmd_oids cmd, const unsigned int len); + +/* + * Macros to facilitate writing multiple registers in a single + * write-combining USB command. Note that when the first group + * fails the whole thing will fail without any others attempted, + * but you won't know which write in the group failed. + */ +#define carl9170_regwrite_begin(ar) \ +do { \ + int __nreg = 0, __err = 0; \ + struct ar9170 *__ar = ar; + +#define carl9170_regwrite(r, v) do { \ + __ar->cmd_buf[2 * __nreg + 1] = cpu_to_le32(r); \ + __ar->cmd_buf[2 * __nreg + 2] = cpu_to_le32(v); \ + __nreg++; \ + if ((__nreg >= PAYLOAD_MAX/2)) { \ + if (IS_ACCEPTING_CMD(__ar)) \ + __err = carl9170_exec_cmd(__ar, \ + CARL9170_CMD_WREG, 8 * __nreg, \ + (u8 *) &__ar->cmd_buf[1], 0, NULL); \ + else \ + goto __regwrite_out; \ + \ + __nreg = 0; \ + if (__err) \ + goto __regwrite_out; \ + } \ +} while (0) + +#define carl9170_regwrite_finish() \ +__regwrite_out : \ + if (__err == 0 && __nreg) { \ + if (IS_ACCEPTING_CMD(__ar)) \ + __err = carl9170_exec_cmd(__ar, \ + CARL9170_CMD_WREG, 8 * __nreg, \ + (u8 *) &__ar->cmd_buf[1], 0, NULL); \ + __nreg = 0; \ + } + +#define carl9170_regwrite_result() \ + __err; \ +} while (0); + + +#define carl9170_async_get_buf() \ +do { \ + __cmd = carl9170_cmd_buf(__carl, CARL9170_CMD_WREG_ASYNC, \ + CARL9170_MAX_CMD_PAYLOAD_LEN); \ + if (__cmd == NULL) { \ + __err = -ENOMEM; \ + goto __async_regwrite_out; \ + } \ +} while (0); + +#define carl9170_async_regwrite_begin(carl) \ +do { \ + int __nreg = 0, __err = 0; \ + struct ar9170 *__carl = carl; \ + struct carl9170_cmd *__cmd; \ + carl9170_async_get_buf(); \ + +#define carl9170_async_regwrite(r, v) do { \ + __cmd->wreg.regs[__nreg].addr = cpu_to_le32(r); \ + __cmd->wreg.regs[__nreg].val = cpu_to_le32(v); \ + __nreg++; \ + if ((__nreg >= PAYLOAD_MAX/2)) { \ + if (IS_ACCEPTING_CMD(__carl)) { \ + __cmd->hdr.len = 8 * __nreg; \ + __err = __carl9170_exec_cmd(__carl, __cmd, true);\ + __cmd = NULL; \ + carl9170_async_get_buf(); \ + } else { \ + goto __async_regwrite_out; \ + } \ + __nreg = 0; \ + if (__err) \ + goto __async_regwrite_out; \ + } \ +} while (0) + +#define carl9170_async_regwrite_finish() \ +__async_regwrite_out : \ + if (__err == 0 && __nreg) { \ + __cmd->hdr.len = 8 * __nreg; \ + if (IS_ACCEPTING_CMD(__carl)) \ + __err = __carl9170_exec_cmd(__carl, __cmd, true);\ + __nreg = 0; \ + } + +#define carl9170_async_regwrite_result() \ + __err; \ +} while (0); + +#endif /* __CMD_H */ diff --git a/drivers/net/wireless/ath/carl9170/led.c b/drivers/net/wireless/ath/carl9170/led.c new file mode 100644 index 000000000000..4bb2cbd8bd9b --- /dev/null +++ b/drivers/net/wireless/ath/carl9170/led.c @@ -0,0 +1,190 @@ +/* + * Atheros CARL9170 driver + * + * LED handling + * + * Copyright 2008, Johannes Berg + * Copyright 2009, 2010, Christian Lamparer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "carl9170.h" +#include "cmd.h" + +int carl9170_led_set_state(struct ar9170 *ar, const u32 led_state) +{ + return carl9170_write_reg(ar, AR9170_GPIO_REG_PORT_DATA, led_state); +} + +int carl9170_led_init(struct ar9170 *ar) +{ + int err; + + /* disable LEDs */ + /* GPIO [0/1 mode: output, 2/3: input] */ + err = carl9170_write_reg(ar, AR9170_GPIO_REG_PORT_TYPE, 3); + if (err) + goto out; + + /* GPIO 0/1 value: off */ + err = carl9170_led_set_state(ar, 0); + +out: + return err; +} + +#ifdef CONFIG_CARL9170_LEDS +static void carl9170_led_update(struct work_struct *work) +{ + struct ar9170 *ar = container_of(work, struct ar9170, led_work.work); + int i, tmp = 300, blink_delay = 1000; + u32 led_val = 0; + bool rerun = false; + + if (!IS_ACCEPTING_CMD(ar)) + return; + + mutex_lock(&ar->mutex); + for (i = 0; i < AR9170_NUM_LEDS; i++) { + if (ar->leds[i].registered) { + if (ar->leds[i].last_state || + ar->leds[i].toggled) { + + if (ar->leds[i].toggled) + tmp = 70 + 200 / (ar->leds[i].toggled); + + if (tmp < blink_delay) + blink_delay = tmp; + + led_val |= 1 << i; + ar->leds[i].toggled = 0; + rerun = true; + } + } + } + + carl9170_led_set_state(ar, led_val); + mutex_unlock(&ar->mutex); + + if (!rerun) + return; + + ieee80211_queue_delayed_work(ar->hw, + &ar->led_work, + msecs_to_jiffies(blink_delay)); +} + +static void carl9170_led_set_brightness(struct led_classdev *led, + enum led_brightness brightness) +{ + struct carl9170_led *arl = container_of(led, struct carl9170_led, l); + struct ar9170 *ar = arl->ar; + + if (!arl->registered) + return; + + if (arl->last_state != !!brightness) { + arl->toggled++; + arl->last_state = !!brightness; + } + + if (likely(IS_ACCEPTING_CMD(ar) && arl->toggled)) + ieee80211_queue_delayed_work(ar->hw, &ar->led_work, HZ/10); +} + +static int carl9170_led_register_led(struct ar9170 *ar, int i, char *name, + char *trigger) +{ + int err; + + snprintf(ar->leds[i].name, sizeof(ar->leds[i].name), + "carl9170-%s::%s", wiphy_name(ar->hw->wiphy), name); + + ar->leds[i].ar = ar; + ar->leds[i].l.name = ar->leds[i].name; + ar->leds[i].l.brightness_set = carl9170_led_set_brightness; + ar->leds[i].l.brightness = 0; + ar->leds[i].l.default_trigger = trigger; + + err = led_classdev_register(wiphy_dev(ar->hw->wiphy), + &ar->leds[i].l); + if (err) { + wiphy_err(ar->hw->wiphy, "failed to register %s LED (%d).\n", + ar->leds[i].name, err); + } else { + ar->leds[i].registered = true; + } + + return err; +} + +void carl9170_led_unregister(struct ar9170 *ar) +{ + int i; + + for (i = 0; i < AR9170_NUM_LEDS; i++) + if (ar->leds[i].registered) { + led_classdev_unregister(&ar->leds[i].l); + ar->leds[i].registered = false; + ar->leds[i].toggled = 0; + } + + cancel_delayed_work_sync(&ar->led_work); +} + +int carl9170_led_register(struct ar9170 *ar) +{ + int err; + + INIT_DELAYED_WORK(&ar->led_work, carl9170_led_update); + + err = carl9170_led_register_led(ar, 0, "tx", + ieee80211_get_tx_led_name(ar->hw)); + if (err) + goto fail; + + if (ar->features & CARL9170_ONE_LED) + return 0; + + err = carl9170_led_register_led(ar, 1, "assoc", + ieee80211_get_assoc_led_name(ar->hw)); + if (err) + goto fail; + + return 0; + +fail: + carl9170_led_unregister(ar); + return err; +} + +#endif /* CONFIG_CARL9170_LEDS */ diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c new file mode 100644 index 000000000000..ea49d54ce03b --- /dev/null +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -0,0 +1,1855 @@ +/* + * Atheros CARL9170 driver + * + * mac80211 interaction code + * + * Copyright 2008, Johannes Berg + * Copyright 2009, 2010, Christian Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "hw.h" +#include "carl9170.h" +#include "cmd.h" + +static int modparam_nohwcrypt; +module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware crypto offload."); + +int modparam_noht; +module_param_named(noht, modparam_noht, int, S_IRUGO); +MODULE_PARM_DESC(noht, "Disable MPDU aggregation."); + +#define RATE(_bitrate, _hw_rate, _txpidx, _flags) { \ + .bitrate = (_bitrate), \ + .flags = (_flags), \ + .hw_value = (_hw_rate) | (_txpidx) << 4, \ +} + +struct ieee80211_rate __carl9170_ratetable[] = { + RATE(10, 0, 0, 0), + RATE(20, 1, 1, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(55, 2, 2, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(110, 3, 3, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(60, 0xb, 0, 0), + RATE(90, 0xf, 0, 0), + RATE(120, 0xa, 0, 0), + RATE(180, 0xe, 0, 0), + RATE(240, 0x9, 0, 0), + RATE(360, 0xd, 1, 0), + RATE(480, 0x8, 2, 0), + RATE(540, 0xc, 3, 0), +}; +#undef RATE + +#define carl9170_g_ratetable (__carl9170_ratetable + 0) +#define carl9170_g_ratetable_size 12 +#define carl9170_a_ratetable (__carl9170_ratetable + 4) +#define carl9170_a_ratetable_size 8 + +/* + * NB: The hw_value is used as an index into the carl9170_phy_freq_params + * array in phy.c so that we don't have to do frequency lookups! + */ +#define CHAN(_freq, _idx) { \ + .center_freq = (_freq), \ + .hw_value = (_idx), \ + .max_power = 18, /* XXX */ \ +} + +static struct ieee80211_channel carl9170_2ghz_chantable[] = { + CHAN(2412, 0), + CHAN(2417, 1), + CHAN(2422, 2), + CHAN(2427, 3), + CHAN(2432, 4), + CHAN(2437, 5), + CHAN(2442, 6), + CHAN(2447, 7), + CHAN(2452, 8), + CHAN(2457, 9), + CHAN(2462, 10), + CHAN(2467, 11), + CHAN(2472, 12), + CHAN(2484, 13), +}; + +static struct ieee80211_channel carl9170_5ghz_chantable[] = { + CHAN(4920, 14), + CHAN(4940, 15), + CHAN(4960, 16), + CHAN(4980, 17), + CHAN(5040, 18), + CHAN(5060, 19), + CHAN(5080, 20), + CHAN(5180, 21), + CHAN(5200, 22), + CHAN(5220, 23), + CHAN(5240, 24), + CHAN(5260, 25), + CHAN(5280, 26), + CHAN(5300, 27), + CHAN(5320, 28), + CHAN(5500, 29), + CHAN(5520, 30), + CHAN(5540, 31), + CHAN(5560, 32), + CHAN(5580, 33), + CHAN(5600, 34), + CHAN(5620, 35), + CHAN(5640, 36), + CHAN(5660, 37), + CHAN(5680, 38), + CHAN(5700, 39), + CHAN(5745, 40), + CHAN(5765, 41), + CHAN(5785, 42), + CHAN(5805, 43), + CHAN(5825, 44), + CHAN(5170, 45), + CHAN(5190, 46), + CHAN(5210, 47), + CHAN(5230, 48), +}; +#undef CHAN + +#define CARL9170_HT_CAP \ +{ \ + .ht_supported = true, \ + .cap = IEEE80211_HT_CAP_MAX_AMSDU | \ + IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \ + IEEE80211_HT_CAP_SGI_40 | \ + IEEE80211_HT_CAP_DSSSCCK40 | \ + IEEE80211_HT_CAP_SM_PS, \ + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, \ + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, \ + .mcs = { \ + .rx_mask = { 0xff, 0xff, 0, 0, 0x1, 0, 0, 0, 0, 0, }, \ + .rx_highest = cpu_to_le16(300), \ + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, \ + }, \ +} + +static struct ieee80211_supported_band carl9170_band_2GHz = { + .channels = carl9170_2ghz_chantable, + .n_channels = ARRAY_SIZE(carl9170_2ghz_chantable), + .bitrates = carl9170_g_ratetable, + .n_bitrates = carl9170_g_ratetable_size, + .ht_cap = CARL9170_HT_CAP, +}; + +static struct ieee80211_supported_band carl9170_band_5GHz = { + .channels = carl9170_5ghz_chantable, + .n_channels = ARRAY_SIZE(carl9170_5ghz_chantable), + .bitrates = carl9170_a_ratetable, + .n_bitrates = carl9170_a_ratetable_size, + .ht_cap = CARL9170_HT_CAP, +}; + +static void carl9170_ampdu_gc(struct ar9170 *ar) +{ + struct carl9170_sta_tid *tid_info; + LIST_HEAD(tid_gc); + + rcu_read_lock(); + list_for_each_entry_rcu(tid_info, &ar->tx_ampdu_list, list) { + spin_lock_bh(&ar->tx_ampdu_list_lock); + if (tid_info->state == CARL9170_TID_STATE_SHUTDOWN) { + tid_info->state = CARL9170_TID_STATE_KILLED; + list_del_rcu(&tid_info->list); + ar->tx_ampdu_list_len--; + list_add_tail(&tid_info->tmp_list, &tid_gc); + } + spin_unlock_bh(&ar->tx_ampdu_list_lock); + + } + rcu_assign_pointer(ar->tx_ampdu_iter, tid_info); + rcu_read_unlock(); + + synchronize_rcu(); + + while (!list_empty(&tid_gc)) { + struct sk_buff *skb; + tid_info = list_first_entry(&tid_gc, struct carl9170_sta_tid, + tmp_list); + + while ((skb = __skb_dequeue(&tid_info->queue))) + carl9170_tx_status(ar, skb, false); + + list_del_init(&tid_info->tmp_list); + kfree(tid_info); + } +} + +static void carl9170_flush(struct ar9170 *ar, bool drop_queued) +{ + if (drop_queued) { + int i; + + /* + * We can only drop frames which have not been uploaded + * to the device yet. + */ + + for (i = 0; i < ar->hw->queues; i++) { + struct sk_buff *skb; + + while ((skb = skb_dequeue(&ar->tx_pending[i]))) + carl9170_tx_status(ar, skb, false); + } + } + + /* Wait for all other outstanding frames to timeout. */ + if (atomic_read(&ar->tx_total_queued)) + WARN_ON(wait_for_completion_timeout(&ar->tx_flush, HZ) == 0); +} + +static void carl9170_flush_ba(struct ar9170 *ar) +{ + struct sk_buff_head free; + struct carl9170_sta_tid *tid_info; + struct sk_buff *skb; + + __skb_queue_head_init(&free); + + rcu_read_lock(); + spin_lock_bh(&ar->tx_ampdu_list_lock); + list_for_each_entry_rcu(tid_info, &ar->tx_ampdu_list, list) { + if (tid_info->state > CARL9170_TID_STATE_SUSPEND) { + tid_info->state = CARL9170_TID_STATE_SUSPEND; + + spin_lock(&tid_info->lock); + while ((skb = __skb_dequeue(&tid_info->queue))) + __skb_queue_tail(&free, skb); + spin_unlock(&tid_info->lock); + } + } + spin_unlock_bh(&ar->tx_ampdu_list_lock); + rcu_read_unlock(); + + while ((skb = __skb_dequeue(&free))) + carl9170_tx_status(ar, skb, false); +} + +static void carl9170_zap_queues(struct ar9170 *ar) +{ + struct carl9170_vif_info *cvif; + unsigned int i; + + carl9170_ampdu_gc(ar); + + carl9170_flush_ba(ar); + carl9170_flush(ar, true); + + for (i = 0; i < ar->hw->queues; i++) { + spin_lock_bh(&ar->tx_status[i].lock); + while (!skb_queue_empty(&ar->tx_status[i])) { + struct sk_buff *skb; + + skb = skb_peek(&ar->tx_status[i]); + carl9170_tx_get_skb(skb); + spin_unlock_bh(&ar->tx_status[i].lock); + carl9170_tx_drop(ar, skb); + spin_lock_bh(&ar->tx_status[i].lock); + carl9170_tx_put_skb(skb); + } + spin_unlock_bh(&ar->tx_status[i].lock); + } + + BUILD_BUG_ON(CARL9170_NUM_TX_LIMIT_SOFT < 1); + BUILD_BUG_ON(CARL9170_NUM_TX_LIMIT_HARD < CARL9170_NUM_TX_LIMIT_SOFT); + BUILD_BUG_ON(CARL9170_NUM_TX_LIMIT_HARD >= CARL9170_BAW_BITS); + + /* reinitialize queues statistics */ + memset(&ar->tx_stats, 0, sizeof(ar->tx_stats)); + for (i = 0; i < ar->hw->queues; i++) + ar->tx_stats[i].limit = CARL9170_NUM_TX_LIMIT_HARD; + + for (i = 0; i < DIV_ROUND_UP(ar->fw.mem_blocks, BITS_PER_LONG); i++) + ar->mem_bitmap[i] = 0; + + rcu_read_lock(); + list_for_each_entry_rcu(cvif, &ar->vif_list, list) { + spin_lock_bh(&ar->beacon_lock); + dev_kfree_skb_any(cvif->beacon); + cvif->beacon = NULL; + spin_unlock_bh(&ar->beacon_lock); + } + rcu_read_unlock(); + + atomic_set(&ar->tx_ampdu_upload, 0); + atomic_set(&ar->tx_ampdu_scheduler, 0); + atomic_set(&ar->tx_total_pending, 0); + atomic_set(&ar->tx_total_queued, 0); + atomic_set(&ar->mem_free_blocks, ar->fw.mem_blocks); +} + +#define CARL9170_FILL_QUEUE(queue, ai_fs, cwmin, cwmax, _txop) \ +do { \ + queue.aifs = ai_fs; \ + queue.cw_min = cwmin; \ + queue.cw_max = cwmax; \ + queue.txop = _txop; \ +} while (0) + +static int carl9170_op_start(struct ieee80211_hw *hw) +{ + struct ar9170 *ar = hw->priv; + int err, i; + + mutex_lock(&ar->mutex); + + carl9170_zap_queues(ar); + + /* reset QoS defaults */ + CARL9170_FILL_QUEUE(ar->edcf[0], 3, 15, 1023, 0); /* BEST EFFORT */ + CARL9170_FILL_QUEUE(ar->edcf[1], 2, 7, 15, 94); /* VIDEO */ + CARL9170_FILL_QUEUE(ar->edcf[2], 2, 3, 7, 47); /* VOICE */ + CARL9170_FILL_QUEUE(ar->edcf[3], 7, 15, 1023, 0); /* BACKGROUND */ + CARL9170_FILL_QUEUE(ar->edcf[4], 2, 3, 7, 0); /* SPECIAL */ + + ar->current_factor = ar->current_density = -1; + /* "The first key is unique." */ + ar->usedkeys = 1; + ar->filter_state = 0; + ar->ps.last_action = jiffies; + ar->ps.last_slept = jiffies; + ar->erp_mode = CARL9170_ERP_AUTO; + ar->rx_software_decryption = false; + ar->disable_offload = false; + + for (i = 0; i < ar->hw->queues; i++) { + ar->queue_stop_timeout[i] = jiffies; + ar->max_queue_stop_timeout[i] = 0; + } + + atomic_set(&ar->mem_allocs, 0); + + err = carl9170_usb_open(ar); + if (err) + goto out; + + err = carl9170_init_mac(ar); + if (err) + goto out; + + err = carl9170_set_qos(ar); + if (err) + goto out; + + err = carl9170_write_reg(ar, AR9170_MAC_REG_DMA_TRIGGER, + AR9170_DMA_TRIGGER_RXQ); + if (err) + goto out; + + /* Clear key-cache */ + for (i = 0; i < AR9170_CAM_MAX_USER + 4; i++) { + err = carl9170_upload_key(ar, i, NULL, AR9170_ENC_ALG_NONE, + 0, NULL, 0); + if (err) + goto out; + + err = carl9170_upload_key(ar, i, NULL, AR9170_ENC_ALG_NONE, + 1, NULL, 0); + if (err) + goto out; + + if (i < AR9170_CAM_MAX_USER) { + err = carl9170_disable_key(ar, i); + if (err) + goto out; + } + } + + carl9170_set_state_when(ar, CARL9170_IDLE, CARL9170_STARTED); + + ieee80211_wake_queues(ar->hw); + err = 0; + +out: + mutex_unlock(&ar->mutex); + return err; +} + +static void carl9170_cancel_worker(struct ar9170 *ar) +{ + cancel_delayed_work_sync(&ar->tx_janitor); +#ifdef CONFIG_CARL9170_LEDS + cancel_delayed_work_sync(&ar->led_work); +#endif /* CONFIG_CARL9170_LEDS */ + cancel_work_sync(&ar->ps_work); + cancel_work_sync(&ar->ampdu_work); +} + +static void carl9170_op_stop(struct ieee80211_hw *hw) +{ + struct ar9170 *ar = hw->priv; + + carl9170_set_state_when(ar, CARL9170_STARTED, CARL9170_IDLE); + + ieee80211_stop_queues(ar->hw); + + mutex_lock(&ar->mutex); + if (IS_ACCEPTING_CMD(ar)) { + rcu_assign_pointer(ar->beacon_iter, NULL); + + carl9170_led_set_state(ar, 0); + + /* stop DMA */ + carl9170_write_reg(ar, AR9170_MAC_REG_DMA_TRIGGER, 0); + carl9170_usb_stop(ar); + } + + carl9170_zap_queues(ar); + mutex_unlock(&ar->mutex); + + carl9170_cancel_worker(ar); +} + +static void carl9170_restart_work(struct work_struct *work) +{ + struct ar9170 *ar = container_of(work, struct ar9170, + restart_work); + int err; + + ar->usedkeys = 0; + ar->filter_state = 0; + carl9170_cancel_worker(ar); + + mutex_lock(&ar->mutex); + err = carl9170_usb_restart(ar); + if (net_ratelimit()) { + if (err) { + dev_err(&ar->udev->dev, "Failed to restart device " + " (%d).\n", err); + } else { + dev_info(&ar->udev->dev, "device restarted " + "successfully.\n"); + } + } + + carl9170_zap_queues(ar); + mutex_unlock(&ar->mutex); + if (!err) { + ar->restart_counter++; + atomic_set(&ar->pending_restarts, 0); + + ieee80211_restart_hw(ar->hw); + } else { + /* + * The reset was unsuccessful and the device seems to + * be dead. But there's still one option: a low-level + * usb subsystem reset... + */ + + carl9170_usb_reset(ar); + } +} + +void carl9170_restart(struct ar9170 *ar, const enum carl9170_restart_reasons r) +{ + carl9170_set_state_when(ar, CARL9170_STARTED, CARL9170_IDLE); + + /* + * Sometimes, an error can trigger several different reset events. + * By ignoring these *surplus* reset events, the device won't be + * killed again, right after it has recovered. + */ + if (atomic_inc_return(&ar->pending_restarts) > 1) { + dev_dbg(&ar->udev->dev, "ignoring restart (%d)\n", r); + return; + } + + ieee80211_stop_queues(ar->hw); + + dev_err(&ar->udev->dev, "restart device (%d)\n", r); + + if (!WARN_ON(r == CARL9170_RR_NO_REASON) || + !WARN_ON(r >= __CARL9170_RR_LAST)) + ar->last_reason = r; + + if (!ar->registered) + return; + + if (IS_ACCEPTING_CMD(ar) && !ar->needs_full_reset) + ieee80211_queue_work(ar->hw, &ar->restart_work); + else + carl9170_usb_reset(ar); + + /* + * At this point, the device instance might have vanished/disabled. + * So, don't put any code which access the ar9170 struct + * without proper protection. + */ +} + +static int carl9170_init_interface(struct ar9170 *ar, + struct ieee80211_vif *vif) +{ + struct ath_common *common = &ar->common; + int err; + + if (!vif) { + WARN_ON_ONCE(IS_STARTED(ar)); + return 0; + } + + memcpy(common->macaddr, vif->addr, ETH_ALEN); + + if (modparam_nohwcrypt || + ((vif->type != NL80211_IFTYPE_STATION) && + (vif->type != NL80211_IFTYPE_AP))) { + ar->rx_software_decryption = true; + ar->disable_offload = true; + } + + err = carl9170_set_operating_mode(ar); + return err; +} + +static int carl9170_op_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct carl9170_vif_info *vif_priv = (void *) vif->drv_priv; + struct ieee80211_vif *main_vif; + struct ar9170 *ar = hw->priv; + int vif_id = -1, err = 0; + + mutex_lock(&ar->mutex); + rcu_read_lock(); + if (vif_priv->active) { + /* + * Skip the interface structure initialization, + * if the vif survived the _restart call. + */ + vif_id = vif_priv->id; + vif_priv->enable_beacon = false; + + spin_lock_bh(&ar->beacon_lock); + dev_kfree_skb_any(vif_priv->beacon); + vif_priv->beacon = NULL; + spin_unlock_bh(&ar->beacon_lock); + + goto init; + } + + main_vif = carl9170_get_main_vif(ar); + + if (main_vif) { + switch (main_vif->type) { + case NL80211_IFTYPE_STATION: + if (vif->type == NL80211_IFTYPE_STATION) + break; + + err = -EBUSY; + rcu_read_unlock(); + + goto unlock; + + case NL80211_IFTYPE_AP: + if ((vif->type == NL80211_IFTYPE_STATION) || + (vif->type == NL80211_IFTYPE_WDS) || + (vif->type == NL80211_IFTYPE_AP)) + break; + + err = -EBUSY; + rcu_read_unlock(); + goto unlock; + + default: + rcu_read_unlock(); + goto unlock; + } + } + + vif_id = bitmap_find_free_region(&ar->vif_bitmap, ar->fw.vif_num, 0); + + if (vif_id < 0) { + rcu_read_unlock(); + + err = -ENOSPC; + goto unlock; + } + + BUG_ON(ar->vif_priv[vif_id].id != vif_id); + + vif_priv->active = true; + vif_priv->id = vif_id; + vif_priv->enable_beacon = false; + ar->vifs++; + list_add_tail_rcu(&vif_priv->list, &ar->vif_list); + rcu_assign_pointer(ar->vif_priv[vif_id].vif, vif); + +init: + if (carl9170_get_main_vif(ar) == vif) { + rcu_assign_pointer(ar->beacon_iter, vif_priv); + rcu_read_unlock(); + + err = carl9170_init_interface(ar, vif); + if (err) + goto unlock; + } else { + err = carl9170_mod_virtual_mac(ar, vif_id, vif->addr); + rcu_read_unlock(); + + if (err) + goto unlock; + } + +unlock: + if (err && (vif_id != -1)) { + vif_priv->active = false; + bitmap_release_region(&ar->vif_bitmap, vif_id, 0); + ar->vifs--; + rcu_assign_pointer(ar->vif_priv[vif_id].vif, NULL); + list_del_rcu(&vif_priv->list); + mutex_unlock(&ar->mutex); + synchronize_rcu(); + } else { + if (ar->vifs > 1) + ar->ps.off_override |= PS_OFF_VIF; + + mutex_unlock(&ar->mutex); + } + + return err; +} + +static void carl9170_op_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct carl9170_vif_info *vif_priv = (void *) vif->drv_priv; + struct ieee80211_vif *main_vif; + struct ar9170 *ar = hw->priv; + unsigned int id; + + mutex_lock(&ar->mutex); + + if (WARN_ON_ONCE(!vif_priv->active)) + goto unlock; + + ar->vifs--; + + rcu_read_lock(); + main_vif = carl9170_get_main_vif(ar); + + id = vif_priv->id; + + vif_priv->active = false; + WARN_ON(vif_priv->enable_beacon); + vif_priv->enable_beacon = false; + list_del_rcu(&vif_priv->list); + rcu_assign_pointer(ar->vif_priv[id].vif, NULL); + + if (vif == main_vif) { + rcu_read_unlock(); + + if (ar->vifs) { + WARN_ON(carl9170_init_interface(ar, + carl9170_get_main_vif(ar))); + } else { + carl9170_set_operating_mode(ar); + } + } else { + rcu_read_unlock(); + + WARN_ON(carl9170_mod_virtual_mac(ar, id, NULL)); + } + + carl9170_update_beacon(ar, false); + carl9170_flush_cab(ar, id); + + spin_lock_bh(&ar->beacon_lock); + dev_kfree_skb_any(vif_priv->beacon); + vif_priv->beacon = NULL; + spin_unlock_bh(&ar->beacon_lock); + + bitmap_release_region(&ar->vif_bitmap, id, 0); + + carl9170_set_beacon_timers(ar); + + if (ar->vifs == 1) + ar->ps.off_override &= ~PS_OFF_VIF; + +unlock: + mutex_unlock(&ar->mutex); + + synchronize_rcu(); +} + +void carl9170_ps_check(struct ar9170 *ar) +{ + ieee80211_queue_work(ar->hw, &ar->ps_work); +} + +/* caller must hold ar->mutex */ +static int carl9170_ps_update(struct ar9170 *ar) +{ + bool ps = false; + int err = 0; + + if (!ar->ps.off_override) + ps = (ar->hw->conf.flags & IEEE80211_CONF_PS); + + if (ps != ar->ps.state) { + err = carl9170_powersave(ar, ps); + if (err) + return err; + + if (ar->ps.state && !ps) { + ar->ps.sleep_ms = jiffies_to_msecs(jiffies - + ar->ps.last_action); + } + + if (ps) + ar->ps.last_slept = jiffies; + + ar->ps.last_action = jiffies; + ar->ps.state = ps; + } + + return 0; +} + +static void carl9170_ps_work(struct work_struct *work) +{ + struct ar9170 *ar = container_of(work, struct ar9170, + ps_work); + mutex_lock(&ar->mutex); + if (IS_STARTED(ar)) + WARN_ON_ONCE(carl9170_ps_update(ar) != 0); + mutex_unlock(&ar->mutex); +} + + +static int carl9170_op_config(struct ieee80211_hw *hw, u32 changed) +{ + struct ar9170 *ar = hw->priv; + int err = 0; + + mutex_lock(&ar->mutex); + if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) { + /* TODO */ + err = 0; + } + + if (changed & IEEE80211_CONF_CHANGE_PS) { + err = carl9170_ps_update(ar); + if (err) + goto out; + } + + if (changed & IEEE80211_CONF_CHANGE_POWER) { + /* TODO */ + err = 0; + } + + if (changed & IEEE80211_CONF_CHANGE_SMPS) { + /* TODO */ + err = 0; + } + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + /* adjust slot time for 5 GHz */ + err = carl9170_set_slot_time(ar); + if (err) + goto out; + + err = carl9170_set_channel(ar, hw->conf.channel, + hw->conf.channel_type, CARL9170_RFI_NONE); + if (err) + goto out; + + err = carl9170_set_dyn_sifs_ack(ar); + if (err) + goto out; + + err = carl9170_set_rts_cts_rate(ar); + if (err) + goto out; + } + +out: + mutex_unlock(&ar->mutex); + return err; +} + +static u64 carl9170_op_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list) +{ + struct netdev_hw_addr *ha; + u64 mchash; + + /* always get broadcast frames */ + mchash = 1ULL << (0xff >> 2); + + netdev_hw_addr_list_for_each(ha, mc_list) + mchash |= 1ULL << (ha->addr[5] >> 2); + + return mchash; +} + +static void carl9170_op_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *new_flags, + u64 multicast) +{ + struct ar9170 *ar = hw->priv; + + /* mask supported flags */ + *new_flags &= FIF_ALLMULTI | FIF_FCSFAIL | FIF_PLCPFAIL | + FIF_OTHER_BSS | FIF_PROMISC_IN_BSS; + + if (!IS_ACCEPTING_CMD(ar)) + return; + + mutex_lock(&ar->mutex); + + ar->filter_state = *new_flags; + /* + * We can support more by setting the sniffer bit and + * then checking the error flags, later. + */ + + if (changed_flags & FIF_ALLMULTI && *new_flags & FIF_ALLMULTI) + multicast = ~0ULL; + + if (multicast != ar->cur_mc_hash) + WARN_ON(carl9170_update_multicast(ar, multicast)); + + if (changed_flags & (FIF_OTHER_BSS | FIF_PROMISC_IN_BSS)) { + ar->sniffer_enabled = !!(*new_flags & + (FIF_OTHER_BSS | FIF_PROMISC_IN_BSS)); + + WARN_ON(carl9170_set_operating_mode(ar)); + } + + mutex_unlock(&ar->mutex); +} + + +static void carl9170_op_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changed) +{ + struct ar9170 *ar = hw->priv; + struct ath_common *common = &ar->common; + int err = 0; + struct carl9170_vif_info *vif_priv; + struct ieee80211_vif *main_vif; + + mutex_lock(&ar->mutex); + vif_priv = (void *) vif->drv_priv; + main_vif = carl9170_get_main_vif(ar); + if (WARN_ON(!main_vif)) + goto out; + + if (changed & BSS_CHANGED_BEACON_ENABLED) { + struct carl9170_vif_info *iter; + int i = 0; + + vif_priv->enable_beacon = bss_conf->enable_beacon; + rcu_read_lock(); + list_for_each_entry_rcu(iter, &ar->vif_list, list) { + if (iter->active && iter->enable_beacon) + i++; + + } + rcu_read_unlock(); + + ar->beacon_enabled = i; + } + + if (changed & BSS_CHANGED_BEACON) { + err = carl9170_update_beacon(ar, false); + if (err) + goto out; + } + + if (changed & (BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_INT)) { + + if (main_vif != vif) { + bss_conf->beacon_int = main_vif->bss_conf.beacon_int; + bss_conf->dtim_period = main_vif->bss_conf.dtim_period; + } + + /* + * Therefore a hard limit for the broadcast traffic should + * prevent false alarms. + */ + if (vif->type != NL80211_IFTYPE_STATION && + (bss_conf->beacon_int * bss_conf->dtim_period >= + (CARL9170_QUEUE_STUCK_TIMEOUT / 2))) { + err = -EINVAL; + goto out; + } + + err = carl9170_set_beacon_timers(ar); + if (err) + goto out; + } + + if (changed & BSS_CHANGED_HT) { + /* TODO */ + err = 0; + if (err) + goto out; + } + + if (main_vif != vif) + goto out; + + /* + * The following settings can only be changed by the + * master interface. + */ + + if (changed & BSS_CHANGED_BSSID) { + memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); + err = carl9170_set_operating_mode(ar); + if (err) + goto out; + } + + if (changed & BSS_CHANGED_ASSOC) { + ar->common.curaid = bss_conf->aid; + err = carl9170_set_beacon_timers(ar); + if (err) + goto out; + } + + if (changed & BSS_CHANGED_ERP_SLOT) { + err = carl9170_set_slot_time(ar); + if (err) + goto out; + } + + if (changed & BSS_CHANGED_BASIC_RATES) { + err = carl9170_set_mac_rates(ar); + if (err) + goto out; + } + +out: + WARN_ON_ONCE(err && IS_STARTED(ar)); + mutex_unlock(&ar->mutex); +} + +static u64 carl9170_op_get_tsf(struct ieee80211_hw *hw) +{ + struct ar9170 *ar = hw->priv; + struct carl9170_tsf_rsp tsf; + int err; + + mutex_lock(&ar->mutex); + err = carl9170_exec_cmd(ar, CARL9170_CMD_READ_TSF, + 0, NULL, sizeof(tsf), &tsf); + mutex_unlock(&ar->mutex); + if (WARN_ON(err)) + return 0; + + return le64_to_cpu(tsf.tsf_64); +} + +static int carl9170_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct ar9170 *ar = hw->priv; + int err = 0, i; + u8 ktype; + + if (ar->disable_offload || !vif) + return -EOPNOTSUPP; + + /* + * We have to fall back to software encryption, whenever + * the user choose to participates in an IBSS or is connected + * to more than one network. + * + * This is very unfortunate, because some machines cannot handle + * the high througput speed in 802.11n networks. + */ + + if (!is_main_vif(ar, vif)) + goto err_softw; + + /* + * While the hardware supports *catch-all* key, for offloading + * group-key en-/de-cryption. The way of how the hardware + * decides which keyId maps to which key, remains a mystery... + */ + if ((vif->type != NL80211_IFTYPE_STATION && + vif->type != NL80211_IFTYPE_ADHOC) && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + return -EOPNOTSUPP; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + ktype = AR9170_ENC_ALG_WEP64; + break; + case WLAN_CIPHER_SUITE_WEP104: + ktype = AR9170_ENC_ALG_WEP128; + break; + case WLAN_CIPHER_SUITE_TKIP: + ktype = AR9170_ENC_ALG_TKIP; + break; + case WLAN_CIPHER_SUITE_CCMP: + ktype = AR9170_ENC_ALG_AESCCMP; + break; + default: + return -EOPNOTSUPP; + } + + mutex_lock(&ar->mutex); + if (cmd == SET_KEY) { + if (!IS_STARTED(ar)) { + err = -EOPNOTSUPP; + goto out; + } + + if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { + sta = NULL; + + i = 64 + key->keyidx; + } else { + for (i = 0; i < 64; i++) + if (!(ar->usedkeys & BIT(i))) + break; + if (i == 64) + goto err_softw; + } + + key->hw_key_idx = i; + + err = carl9170_upload_key(ar, i, sta ? sta->addr : NULL, + ktype, 0, key->key, + min_t(u8, 16, key->keylen)); + if (err) + goto out; + + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) { + err = carl9170_upload_key(ar, i, sta ? sta->addr : + NULL, ktype, 1, + key->key + 16, 16); + if (err) + goto out; + + /* + * hardware is not capable generating MMIC + * of fragmented frames! + */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; + } + + if (i < 64) + ar->usedkeys |= BIT(i); + + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + } else { + if (!IS_STARTED(ar)) { + /* The device is gone... together with the key ;-) */ + err = 0; + goto out; + } + + if (key->hw_key_idx < 64) { + ar->usedkeys &= ~BIT(key->hw_key_idx); + } else { + err = carl9170_upload_key(ar, key->hw_key_idx, NULL, + AR9170_ENC_ALG_NONE, 0, + NULL, 0); + if (err) + goto out; + + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) { + err = carl9170_upload_key(ar, key->hw_key_idx, + NULL, + AR9170_ENC_ALG_NONE, + 1, NULL, 0); + if (err) + goto out; + } + + } + + err = carl9170_disable_key(ar, key->hw_key_idx); + if (err) + goto out; + } + +out: + mutex_unlock(&ar->mutex); + return err; + +err_softw: + if (!ar->rx_software_decryption) { + ar->rx_software_decryption = true; + carl9170_set_operating_mode(ar); + } + mutex_unlock(&ar->mutex); + return -ENOSPC; +} + +static int carl9170_op_sta_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct carl9170_sta_info *sta_info = (void *) sta->drv_priv; + unsigned int i; + + if (sta->ht_cap.ht_supported) { + if (sta->ht_cap.ampdu_density > 6) { + /* + * HW does support 16us AMPDU density. + * No HT-Xmit for station. + */ + + return 0; + } + + for (i = 0; i < CARL9170_NUM_TID; i++) + rcu_assign_pointer(sta_info->agg[i], NULL); + + sta_info->ampdu_max_len = 1 << (3 + sta->ht_cap.ampdu_factor); + sta_info->ht_sta = true; + } + + return 0; +} + +static int carl9170_op_sta_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct ar9170 *ar = hw->priv; + struct carl9170_sta_info *sta_info = (void *) sta->drv_priv; + unsigned int i; + bool cleanup = false; + + if (sta->ht_cap.ht_supported) { + + sta_info->ht_sta = false; + + rcu_read_lock(); + for (i = 0; i < CARL9170_NUM_TID; i++) { + struct carl9170_sta_tid *tid_info; + + tid_info = rcu_dereference(sta_info->agg[i]); + rcu_assign_pointer(sta_info->agg[i], NULL); + + if (!tid_info) + continue; + + spin_lock_bh(&ar->tx_ampdu_list_lock); + if (tid_info->state > CARL9170_TID_STATE_SHUTDOWN) + tid_info->state = CARL9170_TID_STATE_SHUTDOWN; + spin_unlock_bh(&ar->tx_ampdu_list_lock); + cleanup = true; + } + rcu_read_unlock(); + + if (cleanup) + carl9170_ampdu_gc(ar); + } + + return 0; +} + +static int carl9170_op_conf_tx(struct ieee80211_hw *hw, u16 queue, + const struct ieee80211_tx_queue_params *param) +{ + struct ar9170 *ar = hw->priv; + int ret; + + mutex_lock(&ar->mutex); + if (queue < ar->hw->queues) { + memcpy(&ar->edcf[ar9170_qmap[queue]], param, sizeof(*param)); + ret = carl9170_set_qos(ar); + } else { + ret = -EINVAL; + } + + mutex_unlock(&ar->mutex); + return ret; +} + +static void carl9170_ampdu_work(struct work_struct *work) +{ + struct ar9170 *ar = container_of(work, struct ar9170, + ampdu_work); + + if (!IS_STARTED(ar)) + return; + + mutex_lock(&ar->mutex); + carl9170_ampdu_gc(ar); + mutex_unlock(&ar->mutex); +} + +static int carl9170_op_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, + u16 tid, u16 *ssn) +{ + struct ar9170 *ar = hw->priv; + struct carl9170_sta_info *sta_info = (void *) sta->drv_priv; + struct carl9170_sta_tid *tid_info; + + if (modparam_noht) + return -EOPNOTSUPP; + + switch (action) { + case IEEE80211_AMPDU_TX_START: + if (WARN_ON_ONCE(!sta_info->ht_sta)) + return -EOPNOTSUPP; + + rcu_read_lock(); + if (rcu_dereference(sta_info->agg[tid])) { + rcu_read_unlock(); + return -EBUSY; + } + + tid_info = kzalloc(sizeof(struct carl9170_sta_tid), + GFP_ATOMIC); + if (!tid_info) { + rcu_read_unlock(); + return -ENOMEM; + } + + tid_info->hsn = tid_info->bsn = tid_info->snx = (*ssn); + tid_info->state = CARL9170_TID_STATE_PROGRESS; + tid_info->tid = tid; + tid_info->max = sta_info->ampdu_max_len; + + INIT_LIST_HEAD(&tid_info->list); + INIT_LIST_HEAD(&tid_info->tmp_list); + skb_queue_head_init(&tid_info->queue); + spin_lock_init(&tid_info->lock); + + spin_lock_bh(&ar->tx_ampdu_list_lock); + ar->tx_ampdu_list_len++; + list_add_tail_rcu(&tid_info->list, &ar->tx_ampdu_list); + rcu_assign_pointer(sta_info->agg[tid], tid_info); + spin_unlock_bh(&ar->tx_ampdu_list_lock); + rcu_read_unlock(); + + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + + case IEEE80211_AMPDU_TX_STOP: + rcu_read_lock(); + tid_info = rcu_dereference(sta_info->agg[tid]); + if (tid_info) { + spin_lock_bh(&ar->tx_ampdu_list_lock); + if (tid_info->state > CARL9170_TID_STATE_SHUTDOWN) + tid_info->state = CARL9170_TID_STATE_SHUTDOWN; + spin_unlock_bh(&ar->tx_ampdu_list_lock); + } + + rcu_assign_pointer(sta_info->agg[tid], NULL); + rcu_read_unlock(); + + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + ieee80211_queue_work(ar->hw, &ar->ampdu_work); + break; + + case IEEE80211_AMPDU_TX_OPERATIONAL: + rcu_read_lock(); + tid_info = rcu_dereference(sta_info->agg[tid]); + + sta_info->stats[tid].clear = true; + + if (tid_info) { + bitmap_zero(tid_info->bitmap, CARL9170_BAW_SIZE); + tid_info->state = CARL9170_TID_STATE_IDLE; + } + rcu_read_unlock(); + + if (WARN_ON_ONCE(!tid_info)) + return -EFAULT; + + break; + + case IEEE80211_AMPDU_RX_START: + case IEEE80211_AMPDU_RX_STOP: + /* Handled by hardware */ + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +#ifdef CONFIG_CARL9170_WPC +static int carl9170_register_wps_button(struct ar9170 *ar) +{ + struct input_dev *input; + int err; + + if (!(ar->features & CARL9170_WPS_BUTTON)) + return 0; + + input = input_allocate_device(); + if (!input) + return -ENOMEM; + + snprintf(ar->wps.name, sizeof(ar->wps.name), "%s WPS Button", + wiphy_name(ar->hw->wiphy)); + + snprintf(ar->wps.phys, sizeof(ar->wps.phys), + "ieee80211/%s/input0", wiphy_name(ar->hw->wiphy)); + + input->name = ar->wps.name; + input->phys = ar->wps.phys; + input->id.bustype = BUS_USB; + input->dev.parent = &ar->hw->wiphy->dev; + + input_set_capability(input, EV_KEY, KEY_WPS_BUTTON); + + err = input_register_device(input); + if (err) { + input_free_device(input); + return err; + } + + ar->wps.pbc = input; + return 0; +} +#endif /* CONFIG_CARL9170_WPC */ + +static int carl9170_op_get_survey(struct ieee80211_hw *hw, int idx, + struct survey_info *survey) +{ + struct ar9170 *ar = hw->priv; + int err; + + if (idx != 0) + return -ENOENT; + + mutex_lock(&ar->mutex); + err = carl9170_get_noisefloor(ar); + mutex_unlock(&ar->mutex); + if (err) + return err; + + survey->channel = ar->channel; + survey->filled = SURVEY_INFO_NOISE_DBM; + survey->noise = ar->noise[0]; + return 0; +} + +static void carl9170_op_flush(struct ieee80211_hw *hw, bool drop) +{ + struct ar9170 *ar = hw->priv; + unsigned int vid; + + mutex_lock(&ar->mutex); + for_each_set_bit(vid, &ar->vif_bitmap, ar->fw.vif_num) + carl9170_flush_cab(ar, vid); + + carl9170_flush(ar, drop); + mutex_unlock(&ar->mutex); +} + +static int carl9170_op_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + struct ar9170 *ar = hw->priv; + + memset(stats, 0, sizeof(*stats)); + stats->dot11ACKFailureCount = ar->tx_ack_failures; + stats->dot11FCSErrorCount = ar->tx_fcs_errors; + return 0; +} + +static void carl9170_op_sta_notify(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum sta_notify_cmd cmd, + struct ieee80211_sta *sta) +{ + struct ar9170 *ar = hw->priv; + struct carl9170_sta_info *sta_info = (void *) sta->drv_priv; + struct sk_buff *skb; + struct sk_buff_head free; + int i; + + switch (cmd) { + case STA_NOTIFY_SLEEP: + /* + * Since the peer is no longer listening, we have to return + * as many SKBs as possible back to the mac80211 stack. + * It will deal with the retry procedure, once the peer + * has become available again. + * + * NB: Ideally, the driver should return the all frames in + * the correct, ascending order. However, I think that this + * functionality should be implemented in the stack and not + * here... + */ + + __skb_queue_head_init(&free); + + if (sta->ht_cap.ht_supported) { + rcu_read_lock(); + for (i = 0; i < CARL9170_NUM_TID; i++) { + struct carl9170_sta_tid *tid_info; + + tid_info = rcu_dereference(sta_info->agg[i]); + + if (!tid_info) + continue; + + spin_lock_bh(&ar->tx_ampdu_list_lock); + if (tid_info->state > + CARL9170_TID_STATE_SUSPEND) + tid_info->state = + CARL9170_TID_STATE_SUSPEND; + spin_unlock_bh(&ar->tx_ampdu_list_lock); + + spin_lock_bh(&tid_info->lock); + while ((skb = __skb_dequeue(&tid_info->queue))) + __skb_queue_tail(&free, skb); + spin_unlock_bh(&tid_info->lock); + + ieee80211_stop_tx_ba_session(sta, + tid_info->tid); + } + rcu_read_unlock(); + } + + for (i = 0; i < ar->hw->queues; i++) { + spin_lock_bh(&ar->tx_pending[i].lock); + skb_queue_walk(&ar->tx_pending[i], skb) { + struct _carl9170_tx_superframe *super; + struct ieee80211_hdr *hdr; + + super = (void *) skb->data; + hdr = (void *) super->frame_data; + + if (compare_ether_addr(hdr->addr1, sta->addr)) + continue; + + __skb_unlink(skb, &ar->tx_pending[i]); + carl9170_tx_status(ar, skb, false); + } + spin_unlock_bh(&ar->tx_pending[i].lock); + } + + while ((skb = __skb_dequeue(&free))) + carl9170_tx_status(ar, skb, false); + + break; + + case STA_NOTIFY_AWAKE: + if (!sta->ht_cap.ht_supported) + return; + + rcu_read_lock(); + for (i = 0; i < CARL9170_NUM_TID; i++) { + struct carl9170_sta_tid *tid_info; + + tid_info = rcu_dereference(sta_info->agg[i]); + + if (!tid_info) + continue; + + if ((tid_info->state == CARL9170_TID_STATE_SUSPEND)) + tid_info->state = CARL9170_TID_STATE_IDLE; + } + rcu_read_unlock(); + break; + } +} + +static const struct ieee80211_ops carl9170_ops = { + .start = carl9170_op_start, + .stop = carl9170_op_stop, + .tx = carl9170_op_tx, + .flush = carl9170_op_flush, + .add_interface = carl9170_op_add_interface, + .remove_interface = carl9170_op_remove_interface, + .config = carl9170_op_config, + .prepare_multicast = carl9170_op_prepare_multicast, + .configure_filter = carl9170_op_configure_filter, + .conf_tx = carl9170_op_conf_tx, + .bss_info_changed = carl9170_op_bss_info_changed, + .get_tsf = carl9170_op_get_tsf, + .set_key = carl9170_op_set_key, + .sta_add = carl9170_op_sta_add, + .sta_remove = carl9170_op_sta_remove, + .sta_notify = carl9170_op_sta_notify, + .get_survey = carl9170_op_get_survey, + .get_stats = carl9170_op_get_stats, + .ampdu_action = carl9170_op_ampdu_action, +}; + +void *carl9170_alloc(size_t priv_size) +{ + struct ieee80211_hw *hw; + struct ar9170 *ar; + struct sk_buff *skb; + int i; + + /* + * this buffer is used for rx stream reconstruction. + * Under heavy load this device (or the transport layer?) + * tends to split the streams into separate rx descriptors. + */ + + skb = __dev_alloc_skb(AR9170_RX_STREAM_MAX_SIZE, GFP_KERNEL); + if (!skb) + goto err_nomem; + + hw = ieee80211_alloc_hw(priv_size, &carl9170_ops); + if (!hw) + goto err_nomem; + + ar = hw->priv; + ar->hw = hw; + ar->rx_failover = skb; + + memset(&ar->rx_plcp, 0, sizeof(struct ar9170_rx_head)); + ar->rx_has_plcp = false; + + /* + * Here's a hidden pitfall! + * + * All 4 AC queues work perfectly well under _legacy_ operation. + * However as soon as aggregation is enabled, the traffic flow + * gets very bumpy. Therefore we have to _switch_ to a + * software AC with a single HW queue. + */ + hw->queues = __AR9170_NUM_TXQ; + + mutex_init(&ar->mutex); + spin_lock_init(&ar->beacon_lock); + spin_lock_init(&ar->cmd_lock); + spin_lock_init(&ar->tx_stats_lock); + spin_lock_init(&ar->tx_ampdu_list_lock); + spin_lock_init(&ar->mem_lock); + spin_lock_init(&ar->state_lock); + atomic_set(&ar->pending_restarts, 0); + ar->vifs = 0; + for (i = 0; i < ar->hw->queues; i++) { + skb_queue_head_init(&ar->tx_status[i]); + skb_queue_head_init(&ar->tx_pending[i]); + } + INIT_WORK(&ar->ps_work, carl9170_ps_work); + INIT_WORK(&ar->restart_work, carl9170_restart_work); + INIT_WORK(&ar->ampdu_work, carl9170_ampdu_work); + INIT_DELAYED_WORK(&ar->tx_janitor, carl9170_tx_janitor); + INIT_LIST_HEAD(&ar->tx_ampdu_list); + rcu_assign_pointer(ar->tx_ampdu_iter, + (struct carl9170_sta_tid *) &ar->tx_ampdu_list); + + bitmap_zero(&ar->vif_bitmap, ar->fw.vif_num); + INIT_LIST_HEAD(&ar->vif_list); + init_completion(&ar->tx_flush); + + /* + * Note: + * IBSS/ADHOC and AP mode are only enabled, if the firmware + * supports these modes. The code which will add the + * additional interface_modes is in fw.c. + */ + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + + hw->flags |= IEEE80211_HW_RX_INCLUDES_FCS | + IEEE80211_HW_REPORTS_TX_ACK_STATUS | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_PS_NULLFUNC_STACK | + IEEE80211_HW_SIGNAL_DBM; + + if (!modparam_noht) { + /* + * see the comment above, why we allow the user + * to disable HT by a module parameter. + */ + hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION; + } + + hw->extra_tx_headroom = sizeof(struct _carl9170_tx_superframe); + hw->sta_data_size = sizeof(struct carl9170_sta_info); + hw->vif_data_size = sizeof(struct carl9170_vif_info); + + hw->max_rates = CARL9170_TX_MAX_RATES; + hw->max_rate_tries = CARL9170_TX_USER_RATE_TRIES; + + for (i = 0; i < ARRAY_SIZE(ar->noise); i++) + ar->noise[i] = -95; /* ATH_DEFAULT_NOISE_FLOOR */ + + hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + return ar; + +err_nomem: + kfree_skb(skb); + return ERR_PTR(-ENOMEM); +} + +static int carl9170_read_eeprom(struct ar9170 *ar) +{ +#define RW 8 /* number of words to read at once */ +#define RB (sizeof(u32) * RW) + u8 *eeprom = (void *)&ar->eeprom; + __le32 offsets[RW]; + int i, j, err; + + BUILD_BUG_ON(sizeof(ar->eeprom) & 3); + + BUILD_BUG_ON(RB > CARL9170_MAX_CMD_LEN - 4); +#ifndef __CHECKER__ + /* don't want to handle trailing remains */ + BUILD_BUG_ON(sizeof(ar->eeprom) % RB); +#endif + + for (i = 0; i < sizeof(ar->eeprom)/RB; i++) { + for (j = 0; j < RW; j++) + offsets[j] = cpu_to_le32(AR9170_EEPROM_START + + RB * i + 4 * j); + + err = carl9170_exec_cmd(ar, CARL9170_CMD_RREG, + RB, (u8 *) &offsets, + RB, eeprom + RB * i); + if (err) + return err; + } + +#undef RW +#undef RB + return 0; +} + +static int carl9170_parse_eeprom(struct ar9170 *ar) +{ + struct ath_regulatory *regulatory = &ar->common.regulatory; + unsigned int rx_streams, tx_streams, tx_params = 0; + int bands = 0; + + if (ar->eeprom.length == cpu_to_le16(0xffff)) + return -ENODATA; + + rx_streams = hweight8(ar->eeprom.rx_mask); + tx_streams = hweight8(ar->eeprom.tx_mask); + + if (rx_streams != tx_streams) { + tx_params = IEEE80211_HT_MCS_TX_RX_DIFF; + + WARN_ON(!(tx_streams >= 1 && tx_streams <= + IEEE80211_HT_MCS_TX_MAX_STREAMS)); + + tx_params = (tx_streams - 1) << + IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; + + carl9170_band_2GHz.ht_cap.mcs.tx_params |= tx_params; + carl9170_band_5GHz.ht_cap.mcs.tx_params |= tx_params; + } + + if (ar->eeprom.operating_flags & AR9170_OPFLAG_2GHZ) { + ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = + &carl9170_band_2GHz; + bands++; + } + if (ar->eeprom.operating_flags & AR9170_OPFLAG_5GHZ) { + ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = + &carl9170_band_5GHz; + bands++; + } + + /* + * I measured this, a bandswitch takes roughly + * 135 ms and a frequency switch about 80. + * + * FIXME: measure these values again once EEPROM settings + * are used, that will influence them! + */ + if (bands == 2) + ar->hw->channel_change_time = 135 * 1000; + else + ar->hw->channel_change_time = 80 * 1000; + + regulatory->current_rd = le16_to_cpu(ar->eeprom.reg_domain[0]); + regulatory->current_rd_ext = le16_to_cpu(ar->eeprom.reg_domain[1]); + + /* second part of wiphy init */ + SET_IEEE80211_PERM_ADDR(ar->hw, ar->eeprom.mac_address); + + return bands ? 0 : -EINVAL; +} + +static int carl9170_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct ar9170 *ar = hw->priv; + + return ath_reg_notifier_apply(wiphy, request, &ar->common.regulatory); +} + +int carl9170_register(struct ar9170 *ar) +{ + struct ath_regulatory *regulatory = &ar->common.regulatory; + int err = 0, i; + + if (WARN_ON(ar->mem_bitmap)) + return -EINVAL; + + ar->mem_bitmap = kzalloc(roundup(ar->fw.mem_blocks, BITS_PER_LONG) * + sizeof(unsigned long), GFP_KERNEL); + + if (!ar->mem_bitmap) + return -ENOMEM; + + /* try to read EEPROM, init MAC addr */ + err = carl9170_read_eeprom(ar); + if (err) + return err; + + err = carl9170_fw_fix_eeprom(ar); + if (err) + return err; + + err = carl9170_parse_eeprom(ar); + if (err) + return err; + + err = ath_regd_init(regulatory, ar->hw->wiphy, + carl9170_reg_notifier); + if (err) + return err; + + if (modparam_noht) { + carl9170_band_2GHz.ht_cap.ht_supported = false; + carl9170_band_5GHz.ht_cap.ht_supported = false; + } + + for (i = 0; i < ar->fw.vif_num; i++) { + ar->vif_priv[i].id = i; + ar->vif_priv[i].vif = NULL; + } + + err = ieee80211_register_hw(ar->hw); + if (err) + return err; + + /* mac80211 interface is now registered */ + ar->registered = true; + + if (!ath_is_world_regd(regulatory)) + regulatory_hint(ar->hw->wiphy, regulatory->alpha2); + +#ifdef CONFIG_CARL9170_DEBUGFS + carl9170_debugfs_register(ar); +#endif /* CONFIG_CARL9170_DEBUGFS */ + + err = carl9170_led_init(ar); + if (err) + goto err_unreg; + +#ifdef CONFIG_CARL9170_LEDS + err = carl9170_led_register(ar); + if (err) + goto err_unreg; +#endif /* CONFIG_CAR9L170_LEDS */ + +#ifdef CONFIG_CARL9170_WPC + err = carl9170_register_wps_button(ar); + if (err) + goto err_unreg; +#endif /* CONFIG_CARL9170_WPC */ + + dev_info(&ar->udev->dev, "Atheros AR9170 is registered as '%s'\n", + wiphy_name(ar->hw->wiphy)); + + return 0; + +err_unreg: + carl9170_unregister(ar); + return err; +} + +void carl9170_unregister(struct ar9170 *ar) +{ + if (!ar->registered) + return; + + ar->registered = false; + +#ifdef CONFIG_CARL9170_LEDS + carl9170_led_unregister(ar); +#endif /* CONFIG_CARL9170_LEDS */ + +#ifdef CONFIG_CARL9170_DEBUGFS + carl9170_debugfs_unregister(ar); +#endif /* CONFIG_CARL9170_DEBUGFS */ + +#ifdef CONFIG_CARL9170_WPC + if (ar->wps.pbc) { + input_unregister_device(ar->wps.pbc); + ar->wps.pbc = NULL; + } +#endif /* CONFIG_CARL9170_WPC */ + + carl9170_cancel_worker(ar); + cancel_work_sync(&ar->restart_work); + + ieee80211_unregister_hw(ar->hw); +} + +void carl9170_free(struct ar9170 *ar) +{ + WARN_ON(ar->registered); + WARN_ON(IS_INITIALIZED(ar)); + + kfree_skb(ar->rx_failover); + ar->rx_failover = NULL; + + kfree(ar->mem_bitmap); + ar->mem_bitmap = NULL; + + mutex_destroy(&ar->mutex); + + ieee80211_free_hw(ar->hw); +} diff --git a/drivers/net/wireless/ath/carl9170/version.h b/drivers/net/wireless/ath/carl9170/version.h new file mode 100644 index 000000000000..0e917f80eab4 --- /dev/null +++ b/drivers/net/wireless/ath/carl9170/version.h @@ -0,0 +1,7 @@ +#ifndef __CARL9170_SHARED_VERSION_H +#define __CARL9170_SHARED_VERSION_H +#define CARL9170FW_VERSION_YEAR 10 +#define CARL9170FW_VERSION_MONTH 8 +#define CARL9170FW_VERSION_DAY 30 +#define CARL9170FW_VERSION_GIT "1.8.8.1" +#endif /* __CARL9170_SHARED_VERSION_H */ -- cgit v1.2.3 From aae9af605a41cb3e9bbad15df72076423a22f903 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Mon, 6 Sep 2010 01:06:57 +0200 Subject: carl9170: Register maps, tx/rx descriptor formats and eeprom layout Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/eeprom.h | 216 +++++++++ drivers/net/wireless/ath/carl9170/fwcmd.h | 268 +++++++++++ drivers/net/wireless/ath/carl9170/fwdesc.h | 237 ++++++++++ drivers/net/wireless/ath/carl9170/hw.h | 736 +++++++++++++++++++++++++++++ drivers/net/wireless/ath/carl9170/phy.h | 567 ++++++++++++++++++++++ drivers/net/wireless/ath/carl9170/wlan.h | 412 ++++++++++++++++ 6 files changed, 2436 insertions(+) create mode 100644 drivers/net/wireless/ath/carl9170/eeprom.h create mode 100644 drivers/net/wireless/ath/carl9170/fwcmd.h create mode 100644 drivers/net/wireless/ath/carl9170/fwdesc.h create mode 100644 drivers/net/wireless/ath/carl9170/hw.h create mode 100644 drivers/net/wireless/ath/carl9170/phy.h create mode 100644 drivers/net/wireless/ath/carl9170/wlan.h (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/eeprom.h b/drivers/net/wireless/ath/carl9170/eeprom.h new file mode 100644 index 000000000000..7cff40ac7759 --- /dev/null +++ b/drivers/net/wireless/ath/carl9170/eeprom.h @@ -0,0 +1,216 @@ +/* + * Shared Atheros AR9170 Header + * + * EEPROM layout + * + * Copyright 2008, Johannes Berg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef __CARL9170_SHARED_EEPROM_H +#define __CARL9170_SHARED_EEPROM_H + +#define AR9170_EEPROM_START 0x1600 + +#define AR5416_MAX_CHAINS 2 +#define AR5416_MODAL_SPURS 5 + +struct ar9170_eeprom_modal { + __le32 antCtrlChain[AR5416_MAX_CHAINS]; + __le32 antCtrlCommon; + s8 antennaGainCh[AR5416_MAX_CHAINS]; + u8 switchSettling; + u8 txRxAttenCh[AR5416_MAX_CHAINS]; + u8 rxTxMarginCh[AR5416_MAX_CHAINS]; + s8 adcDesiredSize; + s8 pgaDesiredSize; + u8 xlnaGainCh[AR5416_MAX_CHAINS]; + u8 txEndToXpaOff; + u8 txEndToRxOn; + u8 txFrameToXpaOn; + u8 thresh62; + s8 noiseFloorThreshCh[AR5416_MAX_CHAINS]; + u8 xpdGain; + u8 xpd; + s8 iqCalICh[AR5416_MAX_CHAINS]; + s8 iqCalQCh[AR5416_MAX_CHAINS]; + u8 pdGainOverlap; + u8 ob; + u8 db; + u8 xpaBiasLvl; + u8 pwrDecreaseFor2Chain; + u8 pwrDecreaseFor3Chain; + u8 txFrameToDataStart; + u8 txFrameToPaOn; + u8 ht40PowerIncForPdadc; + u8 bswAtten[AR5416_MAX_CHAINS]; + u8 bswMargin[AR5416_MAX_CHAINS]; + u8 swSettleHt40; + u8 reserved[22]; + struct spur_channel { + __le16 spurChan; + u8 spurRangeLow; + u8 spurRangeHigh; + } __packed spur_channels[AR5416_MODAL_SPURS]; +} __packed; + +#define AR5416_NUM_PD_GAINS 4 +#define AR5416_PD_GAIN_ICEPTS 5 + +struct ar9170_calibration_data_per_freq { + u8 pwr_pdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS]; + u8 vpd_pdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS]; +} __packed; + +#define AR5416_NUM_5G_CAL_PIERS 8 +#define AR5416_NUM_2G_CAL_PIERS 4 + +#define AR5416_NUM_5G_TARGET_PWRS 8 +#define AR5416_NUM_2G_CCK_TARGET_PWRS 3 +#define AR5416_NUM_2G_OFDM_TARGET_PWRS 4 +#define AR5416_MAX_NUM_TGT_PWRS 8 + +struct ar9170_calibration_target_power_legacy { + u8 freq; + u8 power[4]; +} __packed; + +struct ar9170_calibration_target_power_ht { + u8 freq; + u8 power[8]; +} __packed; + +#define AR5416_NUM_CTLS 24 + +struct ar9170_calctl_edges { + u8 channel; +#define AR9170_CALCTL_EDGE_FLAGS 0xC0 + u8 power_flags; +} __packed; + +#define AR5416_NUM_BAND_EDGES 8 + +struct ar9170_calctl_data { + struct ar9170_calctl_edges + control_edges[AR5416_MAX_CHAINS][AR5416_NUM_BAND_EDGES]; +} __packed; + +struct ar9170_eeprom { + __le16 length; + __le16 checksum; + __le16 version; + u8 operating_flags; +#define AR9170_OPFLAG_5GHZ 1 +#define AR9170_OPFLAG_2GHZ 2 + u8 misc; + __le16 reg_domain[2]; + u8 mac_address[6]; + u8 rx_mask; + u8 tx_mask; + __le16 rf_silent; + __le16 bluetooth_options; + __le16 device_capabilities; + __le32 build_number; + u8 deviceType; + u8 reserved[33]; + + u8 customer_data[64]; + + struct ar9170_eeprom_modal + modal_header[2]; + + u8 cal_freq_pier_5G[AR5416_NUM_5G_CAL_PIERS]; + u8 cal_freq_pier_2G[AR5416_NUM_2G_CAL_PIERS]; + + struct ar9170_calibration_data_per_freq + cal_pier_data_5G[AR5416_MAX_CHAINS][AR5416_NUM_5G_CAL_PIERS], + cal_pier_data_2G[AR5416_MAX_CHAINS][AR5416_NUM_2G_CAL_PIERS]; + + /* power calibration data */ + struct ar9170_calibration_target_power_legacy + cal_tgt_pwr_5G[AR5416_NUM_5G_TARGET_PWRS]; + struct ar9170_calibration_target_power_ht + cal_tgt_pwr_5G_ht20[AR5416_NUM_5G_TARGET_PWRS], + cal_tgt_pwr_5G_ht40[AR5416_NUM_5G_TARGET_PWRS]; + + struct ar9170_calibration_target_power_legacy + cal_tgt_pwr_2G_cck[AR5416_NUM_2G_CCK_TARGET_PWRS], + cal_tgt_pwr_2G_ofdm[AR5416_NUM_2G_OFDM_TARGET_PWRS]; + struct ar9170_calibration_target_power_ht + cal_tgt_pwr_2G_ht20[AR5416_NUM_2G_OFDM_TARGET_PWRS], + cal_tgt_pwr_2G_ht40[AR5416_NUM_2G_OFDM_TARGET_PWRS]; + + /* conformance testing limits */ + u8 ctl_index[AR5416_NUM_CTLS]; + struct ar9170_calctl_data + ctl_data[AR5416_NUM_CTLS]; + + u8 pad; + __le16 subsystem_id; +} __packed; + +#define AR9170_LED_MODE_POWER_ON 0x0001 +#define AR9170_LED_MODE_RESERVED 0x0002 +#define AR9170_LED_MODE_DISABLE_STATE 0x0004 +#define AR9170_LED_MODE_OFF_IN_PSM 0x0008 + +/* AR9170_LED_MODE BIT is set */ +#define AR9170_LED_MODE_FREQUENCY_S 4 +#define AR9170_LED_MODE_FREQUENCY 0x0030 +#define AR9170_LED_MODE_FREQUENCY_1HZ 0x0000 +#define AR9170_LED_MODE_FREQUENCY_0_5HZ 0x0010 +#define AR9170_LED_MODE_FREQUENCY_0_25HZ 0x0020 +#define AR9170_LED_MODE_FREQUENCY_0_125HZ 0x0030 + +/* AR9170_LED_MODE BIT is not set */ +#define AR9170_LED_MODE_CONN_STATE_S 4 +#define AR9170_LED_MODE_CONN_STATE 0x0030 +#define AR9170_LED_MODE_CONN_STATE_FORCE_OFF 0x0000 +#define AR9170_LED_MODE_CONN_STATE_FORCE_ON 0x0010 +/* Idle off / Active on */ +#define AR9170_LED_MODE_CONN_STATE_IOFF_AON 0x0020 +/* Idle on / Active off */ +#define AR9170_LED_MODE_CONN_STATE_ION_AOFF 0x0010 + +#define AR9170_LED_MODE_MODE 0x0040 +#define AR9170_LED_MODE_RESERVED2 0x0080 + +#define AR9170_LED_MODE_TON_SCAN_S 8 +#define AR9170_LED_MODE_TON_SCAN 0x0f00 + +#define AR9170_LED_MODE_TOFF_SCAN_S 12 +#define AR9170_LED_MODE_TOFF_SCAN 0xf000 + +struct ar9170_led_mode { + __le16 led; +}; + +#endif /* __CARL9170_SHARED_EEPROM_H */ diff --git a/drivers/net/wireless/ath/carl9170/fwcmd.h b/drivers/net/wireless/ath/carl9170/fwcmd.h new file mode 100644 index 000000000000..d4a4e1dbef06 --- /dev/null +++ b/drivers/net/wireless/ath/carl9170/fwcmd.h @@ -0,0 +1,268 @@ +/* + * Shared Atheros AR9170 Header + * + * Firmware command interface definitions + * + * Copyright 2008, Johannes Berg + * Copyright 2009, 2010, Christian Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __CARL9170_SHARED_FWCMD_H +#define __CARL9170_SHARED_FWCMD_H + +#define CARL9170_MAX_CMD_LEN 64 +#define CARL9170_MAX_CMD_PAYLOAD_LEN 60 + +#define CARL9170FW_API_MIN_VER 1 +#define CARL9170FW_API_MAX_VER 1 + +enum carl9170_cmd_oids { + CARL9170_CMD_RREG = 0x00, + CARL9170_CMD_WREG = 0x01, + CARL9170_CMD_ECHO = 0x02, + CARL9170_CMD_SWRST = 0x03, + CARL9170_CMD_REBOOT = 0x04, + CARL9170_CMD_BCN_CTRL = 0x05, + CARL9170_CMD_READ_TSF = 0x06, + + /* CAM */ + CARL9170_CMD_EKEY = 0x10, + CARL9170_CMD_DKEY = 0x11, + + /* RF / PHY */ + CARL9170_CMD_FREQUENCY = 0x20, + CARL9170_CMD_RF_INIT = 0x21, + CARL9170_CMD_SYNTH = 0x22, + CARL9170_CMD_FREQ_START = 0x23, + CARL9170_CMD_PSM = 0x24, + + /* Asychronous command flag */ + CARL9170_CMD_ASYNC_FLAG = 0x40, + CARL9170_CMD_WREG_ASYNC = (CARL9170_CMD_WREG | + CARL9170_CMD_ASYNC_FLAG), + CARL9170_CMD_REBOOT_ASYNC = (CARL9170_CMD_REBOOT | + CARL9170_CMD_ASYNC_FLAG), + CARL9170_CMD_BCN_CTRL_ASYNC = (CARL9170_CMD_BCN_CTRL | + CARL9170_CMD_ASYNC_FLAG), + CARL9170_CMD_PSM_ASYNC = (CARL9170_CMD_PSM | + CARL9170_CMD_ASYNC_FLAG), + + /* responses and traps */ + CARL9170_RSP_FLAG = 0xc0, + CARL9170_RSP_PRETBTT = 0xc0, + CARL9170_RSP_TXCOMP = 0xc1, + CARL9170_RSP_BEACON_CONFIG = 0xc2, + CARL9170_RSP_ATIM = 0xc3, + CARL9170_RSP_WATCHDOG = 0xc6, + CARL9170_RSP_TEXT = 0xca, + CARL9170_RSP_HEXDUMP = 0xcc, + CARL9170_RSP_RADAR = 0xcd, + CARL9170_RSP_GPIO = 0xce, + CARL9170_RSP_BOOT = 0xcf, +}; + +struct carl9170_set_key_cmd { + __le16 user; + __le16 keyId; + __le16 type; + u8 macAddr[6]; + u32 key[4]; +} __packed; +#define CARL9170_SET_KEY_CMD_SIZE 28 + +struct carl9170_disable_key_cmd { + __le16 user; + __le16 padding; +} __packed; +#define CARL9170_DISABLE_KEY_CMD_SIZE 4 + +struct carl9170_u32_list { + u32 vals[0]; +} __packed; + +struct carl9170_reg_list { + __le32 regs[0]; +} __packed; + +struct carl9170_write_reg { + struct { + __le32 addr; + __le32 val; + } regs[0] __packed; +} __packed; + +#define CARL9170FW_PHY_HT_ENABLE 0x4 +#define CARL9170FW_PHY_HT_DYN2040 0x8 +#define CARL9170FW_PHY_HT_EXT_CHAN_OFF 0x3 +#define CARL9170FW_PHY_HT_EXT_CHAN_OFF_S 2 + +struct carl9170_rf_init { + __le32 freq; + u8 ht_settings; + u8 padding2[3]; + __le32 delta_slope_coeff_exp; + __le32 delta_slope_coeff_man; + __le32 delta_slope_coeff_exp_shgi; + __le32 delta_slope_coeff_man_shgi; + __le32 finiteLoopCount; +} __packed; +#define CARL9170_RF_INIT_SIZE 28 + +struct carl9170_rf_init_result { + __le32 ret; /* AR9170_PHY_REG_AGC_CONTROL */ +} __packed; +#define CARL9170_RF_INIT_RESULT_SIZE 4 + +#define CARL9170_PSM_SLEEP 0x1000 +#define CARL9170_PSM_SOFTWARE 0 +#define CARL9170_PSM_WAKE 0 /* internally used. */ +#define CARL9170_PSM_COUNTER 0xfff +#define CARL9170_PSM_COUNTER_S 0 + +struct carl9170_psm { + __le32 state; +} __packed; +#define CARL9170_PSM_SIZE 4 + +struct carl9170_bcn_ctrl_cmd { + __le32 vif_id; + __le32 mode; + __le32 bcn_addr; + __le32 bcn_len; +} __packed; +#define CARL9170_BCN_CTRL_CMD_SIZE 16 + +#define CARL9170_BCN_CTRL_DRAIN 0 +#define CARL9170_BCN_CTRL_CAB_TRIGGER 1 + +struct carl9170_cmd_head { + union { + struct { + u8 len; + u8 cmd; + u8 seq; + u8 ext; + } __packed; + + u32 hdr_data; + } __packed; +} __packed; + +struct carl9170_cmd { + struct carl9170_cmd_head hdr; + union { + struct carl9170_set_key_cmd setkey; + struct carl9170_disable_key_cmd disablekey; + struct carl9170_u32_list echo; + struct carl9170_reg_list rreg; + struct carl9170_write_reg wreg; + struct carl9170_rf_init rf_init; + struct carl9170_psm psm; + struct carl9170_bcn_ctrl_cmd bcn_ctrl; + u8 data[CARL9170_MAX_CMD_PAYLOAD_LEN]; + } __packed; +} __packed; + +#define CARL9170_TX_STATUS_QUEUE 3 +#define CARL9170_TX_STATUS_QUEUE_S 0 +#define CARL9170_TX_STATUS_RIX_S 2 +#define CARL9170_TX_STATUS_RIX (3 << CARL9170_TX_STATUS_RIX_S) +#define CARL9170_TX_STATUS_TRIES_S 4 +#define CARL9170_TX_STATUS_TRIES (7 << CARL9170_TX_STATUS_TRIES_S) +#define CARL9170_TX_STATUS_SUCCESS 0x80 + +/* + * NOTE: + * Both structs [carl9170_tx_status and _carl9170_tx_status] + * need to be "bit for bit" in sync. + */ +struct carl9170_tx_status { + /* + * Beware of compiler bugs in all gcc pre 4.4! + */ + + u8 cookie; + u8 queue:2; + u8 rix:2; + u8 tries:3; + u8 success:1; +} __packed; +struct _carl9170_tx_status { + /* + * This version should be immune to all alignment bugs. + */ + + u8 cookie; + u8 info; +} __packed; +#define CARL9170_TX_STATUS_SIZE 2 + +#define CARL9170_RSP_TX_STATUS_NUM (CARL9170_MAX_CMD_PAYLOAD_LEN / \ + sizeof(struct _carl9170_tx_status)) + +#define CARL9170_TX_MAX_RATE_TRIES 7 + +#define CARL9170_TX_MAX_RATES 4 +#define CARL9170_TX_MAX_RETRY_RATES (CARL9170_TX_MAX_RATES - 1) +#define CARL9170_ERR_MAGIC "ERR:" +#define CARL9170_BUG_MAGIC "BUG:" + +struct carl9170_gpio { + __le32 gpio; +} __packed; +#define CARL9170_GPIO_SIZE 4 + +struct carl9170_tsf_rsp { + union { + __le32 tsf[2]; + __le64 tsf_64; + } __packed; +} __packed; +#define CARL9170_TSF_RSP_SIZE 8 + +struct carl9170_rsp { + struct carl9170_cmd_head hdr; + + union { + struct carl9170_rf_init_result rf_init_res; + struct carl9170_u32_list rreg_res; + struct carl9170_u32_list echo; + struct carl9170_tx_status tx_status[0]; + struct _carl9170_tx_status _tx_status[0]; + struct carl9170_gpio gpio; + struct carl9170_tsf_rsp tsf; + struct carl9170_psm psm; + u8 data[CARL9170_MAX_CMD_PAYLOAD_LEN]; + } __packed; +} __packed; + +#endif /* __CARL9170_SHARED_FWCMD_H */ diff --git a/drivers/net/wireless/ath/carl9170/fwdesc.h b/drivers/net/wireless/ath/carl9170/fwdesc.h new file mode 100644 index 000000000000..7cd811708fe5 --- /dev/null +++ b/drivers/net/wireless/ath/carl9170/fwdesc.h @@ -0,0 +1,237 @@ +/* + * Shared CARL9170 Header + * + * Firmware descriptor format + * + * Copyright 2009, 2010, Christian Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + */ + +#ifndef __CARL9170_SHARED_FWDESC_H +#define __CARL9170_SHARED_FWDESC_H + +/* NOTE: Don't mess with the order of the flags! */ +enum carl9170fw_feature_list { + /* Always set */ + CARL9170FW_DUMMY_FEATURE, + + /* + * Indicates that this image has special boot block which prevents + * legacy drivers to drive the firmware. + */ + CARL9170FW_MINIBOOT, + + /* usb registers are initialized by the firmware */ + CARL9170FW_USB_INIT_FIRMWARE, + + /* command traps & notifications are send through EP2 */ + CARL9170FW_USB_RESP_EP2, + + /* usb download (app -> fw) stream */ + CARL9170FW_USB_DOWN_STREAM, + + /* usb upload (fw -> app) stream */ + CARL9170FW_USB_UP_STREAM, + + /* unusable - reserved to flag non-functional debug firmwares */ + CARL9170FW_UNUSABLE, + + /* AR9170_CMD_RF_INIT, AR9170_CMD_FREQ_START, AR9170_CMD_FREQUENCY */ + CARL9170FW_COMMAND_PHY, + + /* AR9170_CMD_EKEY, AR9170_CMD_DKEY */ + CARL9170FW_COMMAND_CAM, + + /* Firmware has a software Content After Beacon Queueing mechanism */ + CARL9170FW_WLANTX_CAB, + + /* The firmware is capable of responding to incoming BAR frames */ + CARL9170FW_HANDLE_BACK_REQ, + + /* GPIO Interrupt | CARL9170_RSP_GPIO */ + CARL9170FW_GPIO_INTERRUPT, + + /* Firmware PSM support | CARL9170_CMD_PSM */ + CARL9170FW_PSM, + + /* KEEP LAST */ + __CARL9170FW_FEATURE_NUM +}; + +#define OTUS_MAGIC "OTAR" +#define MOTD_MAGIC "MOTD" +#define FIX_MAGIC "FIX\0" +#define DBG_MAGIC "DBG\0" +#define CHK_MAGIC "CHK\0" +#define LAST_MAGIC "LAST" + +#define CARL9170FW_SET_DAY(d) (((d) - 1) % 31) +#define CARL9170FW_SET_MONTH(m) ((((m) - 1) % 12) * 31) +#define CARL9170FW_SET_YEAR(y) (((y) - 10) * 372) + +#define CARL9170FW_GET_DAY(d) (((d) % 31) + 1) +#define CARL9170FW_GET_MONTH(m) ((((m) / 31) % 12) + 1) +#define CARL9170FW_GET_YEAR(y) ((y) / 372 + 10) + +struct carl9170fw_desc_head { + u8 magic[4]; + __le16 length; + u8 min_ver; + u8 cur_ver; +} __packed; +#define CARL9170FW_DESC_HEAD_SIZE \ + (sizeof(struct carl9170fw_desc_head)) + +#define CARL9170FW_OTUS_DESC_MIN_VER 6 +#define CARL9170FW_OTUS_DESC_CUR_VER 6 +struct carl9170fw_otus_desc { + struct carl9170fw_desc_head head; + __le32 feature_set; + __le32 fw_address; + __le32 bcn_addr; + __le16 bcn_len; + __le16 miniboot_size; + __le16 tx_frag_len; + __le16 rx_max_frame_len; + u8 tx_descs; + u8 cmd_bufs; + u8 api_ver; + u8 vif_num; +} __packed; +#define CARL9170FW_OTUS_DESC_SIZE \ + (sizeof(struct carl9170fw_otus_desc)) + +#define CARL9170FW_MOTD_STRING_LEN 24 +#define CARL9170FW_MOTD_RELEASE_LEN 20 +#define CARL9170FW_MOTD_DESC_MIN_VER 1 +#define CARL9170FW_MOTD_DESC_CUR_VER 2 +struct carl9170fw_motd_desc { + struct carl9170fw_desc_head head; + __le32 fw_year_month_day; + char desc[CARL9170FW_MOTD_STRING_LEN]; + char release[CARL9170FW_MOTD_RELEASE_LEN]; +} __packed; +#define CARL9170FW_MOTD_DESC_SIZE \ + (sizeof(struct carl9170fw_motd_desc)) + +#define CARL9170FW_FIX_DESC_MIN_VER 1 +#define CARL9170FW_FIX_DESC_CUR_VER 2 +struct carl9170fw_fix_entry { + __le32 address; + __le32 mask; + __le32 value; +} __packed; + +struct carl9170fw_fix_desc { + struct carl9170fw_desc_head head; + struct carl9170fw_fix_entry data[0]; +} __packed; +#define CARL9170FW_FIX_DESC_SIZE \ + (sizeof(struct carl9170fw_fix_desc)) + +#define CARL9170FW_DBG_DESC_MIN_VER 1 +#define CARL9170FW_DBG_DESC_CUR_VER 2 +struct carl9170fw_dbg_desc { + struct carl9170fw_desc_head head; + + __le32 bogoclock_addr; + __le32 counter_addr; + __le32 rx_total_addr; + __le32 rx_overrun_addr; + + /* Put your debugging definitions here */ +} __packed; +#define CARL9170FW_DBG_DESC_SIZE \ + (sizeof(struct carl9170fw_dbg_desc)) + +#define CARL9170FW_CHK_DESC_MIN_VER 1 +#define CARL9170FW_CHK_DESC_CUR_VER 2 +struct carl9170fw_chk_desc { + struct carl9170fw_desc_head head; + __le32 fw_crc32; + __le32 hdr_crc32; +} __packed; +#define CARL9170FW_CHK_DESC_SIZE \ + (sizeof(struct carl9170fw_chk_desc)) + +#define CARL9170FW_LAST_DESC_MIN_VER 1 +#define CARL9170FW_LAST_DESC_CUR_VER 2 +struct carl9170fw_last_desc { + struct carl9170fw_desc_head head; +} __packed; +#define CARL9170FW_LAST_DESC_SIZE \ + (sizeof(struct carl9170fw_fix_desc)) + +#define CARL9170FW_DESC_MAX_LENGTH 8192 + +#define CARL9170FW_FILL_DESC(_magic, _length, _min_ver, _cur_ver) \ + .head = { \ + .magic = _magic, \ + .length = cpu_to_le16(_length), \ + .min_ver = _min_ver, \ + .cur_ver = _cur_ver, \ + } + +static inline void carl9170fw_fill_desc(struct carl9170fw_desc_head *head, + u8 magic[4], __le16 length, + u8 min_ver, u8 cur_ver) +{ + head->magic[0] = magic[0]; + head->magic[1] = magic[1]; + head->magic[2] = magic[2]; + head->magic[3] = magic[3]; + + head->length = length; + head->min_ver = min_ver; + head->cur_ver = cur_ver; +} + +#define carl9170fw_for_each_hdr(desc, fw_desc) \ + for (desc = fw_desc; \ + memcmp(desc->magic, LAST_MAGIC, 4) && \ + le16_to_cpu(desc->length) >= CARL9170FW_DESC_HEAD_SIZE && \ + le16_to_cpu(desc->length) < CARL9170FW_DESC_MAX_LENGTH; \ + desc = (void *)((unsigned long)desc + le16_to_cpu(desc->length))) + +#define CHECK_HDR_VERSION(head, _min_ver) \ + (((head)->cur_ver < _min_ver) || ((head)->min_ver > _min_ver)) \ + +static inline bool carl9170fw_supports(__le32 list, u8 feature) +{ + return le32_to_cpu(list) & BIT(feature); +} + +static inline bool carl9170fw_desc_cmp(const struct carl9170fw_desc_head *head, + const u8 descid[4], u16 min_len, + u8 compatible_revision) +{ + if (descid[0] == head->magic[0] && descid[1] == head->magic[1] && + descid[2] == head->magic[2] && descid[3] == head->magic[3] && + !CHECK_HDR_VERSION(head, compatible_revision) && + (le16_to_cpu(head->length) >= min_len)) + return true; + + return false; +} + +#define CARL9170FW_MIN_SIZE 32 +#define CARL9170FW_MAX_SIZE 16384 + +static inline bool carl9170fw_size_check(unsigned int len) +{ + return (len <= CARL9170FW_MAX_SIZE && len >= CARL9170FW_MIN_SIZE); +} + +#endif /* __CARL9170_SHARED_FWDESC_H */ diff --git a/drivers/net/wireless/ath/carl9170/hw.h b/drivers/net/wireless/ath/carl9170/hw.h new file mode 100644 index 000000000000..b1292ac5b703 --- /dev/null +++ b/drivers/net/wireless/ath/carl9170/hw.h @@ -0,0 +1,736 @@ +/* + * Shared Atheros AR9170 Header + * + * Register map, hardware-specific definitions + * + * Copyright 2008, Johannes Berg + * Copyright 2009, 2010, Christian Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __CARL9170_SHARED_HW_H +#define __CARL9170_SHARED_HW_H + +/* High Speed UART */ +#define AR9170_UART_REG_BASE 0x1c0000 + +/* Definitions of interrupt registers */ +#define AR9170_UART_REG_RX_BUFFER (AR9170_UART_REG_BASE + 0x000) +#define AR9170_UART_REG_TX_HOLDING (AR9170_UART_REG_BASE + 0x004) +#define AR9170_UART_REG_FIFO_CONTROL (AR9170_UART_REG_BASE + 0x010) +#define AR9170_UART_FIFO_CTRL_RESET_RX_FIFO 0x02 +#define AR9170_UART_FIFO_CTRL_RESET_TX_FIFO 0x04 + +#define AR9170_UART_REG_LINE_CONTROL (AR9170_UART_REG_BASE + 0x014) +#define AR9170_UART_REG_MODEM_CONTROL (AR9170_UART_REG_BASE + 0x018) +#define AR9170_UART_MODEM_CTRL_DTR_BIT 0x01 +#define AR9170_UART_MODEM_CTRL_RTS_BIT 0x02 +#define AR9170_UART_MODEM_CTRL_INTERNAL_LOOP_BACK 0x10 +#define AR9170_UART_MODEM_CTRL_AUTO_RTS 0x20 +#define AR9170_UART_MODEM_CTRL_AUTO_CTR 0x40 + +#define AR9170_UART_REG_LINE_STATUS (AR9170_UART_REG_BASE + 0x01c) +#define AR9170_UART_LINE_STS_RX_DATA_READY 0x01 +#define AR9170_UART_LINE_STS_RX_BUFFER_OVERRUN 0x02 +#define AR9170_UART_LINE_STS_RX_BREAK_IND 0x10 +#define AR9170_UART_LINE_STS_TX_FIFO_NEAR_EMPTY 0x20 +#define AR9170_UART_LINE_STS_TRANSMITTER_EMPTY 0x40 + +#define AR9170_UART_REG_MODEM_STATUS (AR9170_UART_REG_BASE + 0x020) +#define AR9170_UART_MODEM_STS_CTS_CHANGE 0x01 +#define AR9170_UART_MODEM_STS_DSR_CHANGE 0x02 +#define AR9170_UART_MODEM_STS_DCD_CHANGE 0x08 +#define AR9170_UART_MODEM_STS_CTS_COMPL 0x10 +#define AR9170_UART_MODEM_STS_DSR_COMPL 0x20 +#define AR9170_UART_MODEM_STS_DCD_COMPL 0x80 + +#define AR9170_UART_REG_SCRATCH (AR9170_UART_REG_BASE + 0x024) +#define AR9170_UART_REG_DIVISOR_LSB (AR9170_UART_REG_BASE + 0x028) +#define AR9170_UART_REG_DIVISOR_MSB (AR9170_UART_REG_BASE + 0x02c) +#define AR9170_UART_REG_WORD_RX_BUFFER (AR9170_UART_REG_BASE + 0x034) +#define AR9170_UART_REG_WORD_TX_HOLDING (AR9170_UART_REG_BASE + 0x038) +#define AR9170_UART_REG_FIFO_COUNT (AR9170_UART_REG_BASE + 0x03c) +#define AR9170_UART_REG_REMAINDER (AR9170_UART_REG_BASE + 0x04c) + +/* Timer */ +#define AR9170_TIMER_REG_BASE 0x1c1000 + +#define AR9170_TIMER_REG_WATCH_DOG (AR9170_TIMER_REG_BASE + 0x000) +#define AR9170_TIMER_REG_TIMER0 (AR9170_TIMER_REG_BASE + 0x010) +#define AR9170_TIMER_REG_TIMER1 (AR9170_TIMER_REG_BASE + 0x014) +#define AR9170_TIMER_REG_TIMER2 (AR9170_TIMER_REG_BASE + 0x018) +#define AR9170_TIMER_REG_TIMER3 (AR9170_TIMER_REG_BASE + 0x01c) +#define AR9170_TIMER_REG_TIMER4 (AR9170_TIMER_REG_BASE + 0x020) +#define AR9170_TIMER_REG_CONTROL (AR9170_TIMER_REG_BASE + 0x024) +#define AR9170_TIMER_CTRL_DISABLE_CLOCK 0x100 + +#define AR9170_TIMER_REG_INTERRUPT (AR9170_TIMER_REG_BASE + 0x028) +#define AR9170_TIMER_INT_TIMER0 0x001 +#define AR9170_TIMER_INT_TIMER1 0x002 +#define AR9170_TIMER_INT_TIMER2 0x004 +#define AR9170_TIMER_INT_TIMER3 0x008 +#define AR9170_TIMER_INT_TIMER4 0x010 +#define AR9170_TIMER_INT_TICK_TIMER 0x100 + +#define AR9170_TIMER_REG_TICK_TIMER (AR9170_TIMER_REG_BASE + 0x030) +#define AR9170_TIMER_REG_CLOCK_LOW (AR9170_TIMER_REG_BASE + 0x040) +#define AR9170_TIMER_REG_CLOCK_HIGH (AR9170_TIMER_REG_BASE + 0x044) + +#define AR9170_MAC_REG_BASE 0x1c3000 + +#define AR9170_MAC_REG_POWER_STATE_CTRL (AR9170_MAC_REG_BASE + 0x500) +#define AR9170_MAC_POWER_STATE_CTRL_RESET 0x20 + +#define AR9170_MAC_REG_MAC_POWER_STATE_CTRL (AR9170_MAC_REG_BASE + 0x50c) + +#define AR9170_MAC_REG_INT_CTRL (AR9170_MAC_REG_BASE + 0x510) +#define AR9170_MAC_INT_TXC BIT(0) +#define AR9170_MAC_INT_RXC BIT(1) +#define AR9170_MAC_INT_RETRY_FAIL BIT(2) +#define AR9170_MAC_INT_WAKEUP BIT(3) +#define AR9170_MAC_INT_ATIM BIT(4) +#define AR9170_MAC_INT_DTIM BIT(5) +#define AR9170_MAC_INT_CFG_BCN BIT(6) +#define AR9170_MAC_INT_ABORT BIT(7) +#define AR9170_MAC_INT_QOS BIT(8) +#define AR9170_MAC_INT_MIMO_PS BIT(9) +#define AR9170_MAC_INT_KEY_GEN BIT(10) +#define AR9170_MAC_INT_DECRY_NOUSER BIT(11) +#define AR9170_MAC_INT_RADAR BIT(12) +#define AR9170_MAC_INT_QUIET_FRAME BIT(13) +#define AR9170_MAC_INT_PRETBTT BIT(14) + +#define AR9170_MAC_REG_TSF_L (AR9170_MAC_REG_BASE + 0x514) +#define AR9170_MAC_REG_TSF_H (AR9170_MAC_REG_BASE + 0x518) + +#define AR9170_MAC_REG_ATIM_WINDOW (AR9170_MAC_REG_BASE + 0x51c) +#define AR9170_MAC_ATIM_PERIOD_S 0 +#define AR9170_MAC_ATIM_PERIOD 0x0000ffff + +#define AR9170_MAC_REG_BCN_PERIOD (AR9170_MAC_REG_BASE + 0x520) +#define AR9170_MAC_BCN_PERIOD_S 0 +#define AR9170_MAC_BCN_PERIOD 0x0000ffff +#define AR9170_MAC_BCN_DTIM_S 16 +#define AR9170_MAC_BCN_DTIM 0x00ff0000 +#define AR9170_MAC_BCN_AP_MODE BIT(24) +#define AR9170_MAC_BCN_IBSS_MODE BIT(25) +#define AR9170_MAC_BCN_PWR_MGT BIT(26) +#define AR9170_MAC_BCN_STA_PS BIT(27) + +#define AR9170_MAC_REG_PRETBTT (AR9170_MAC_REG_BASE + 0x524) +#define AR9170_MAC_PRETBTT_S 0 +#define AR9170_MAC_PRETBTT 0x0000ffff +#define AR9170_MAC_PRETBTT2_S 16 +#define AR9170_MAC_PRETBTT2 0xffff0000 + +#define AR9170_MAC_REG_MAC_ADDR_L (AR9170_MAC_REG_BASE + 0x610) +#define AR9170_MAC_REG_MAC_ADDR_H (AR9170_MAC_REG_BASE + 0x614) +#define AR9170_MAC_REG_BSSID_L (AR9170_MAC_REG_BASE + 0x618) +#define AR9170_MAC_REG_BSSID_H (AR9170_MAC_REG_BASE + 0x61c) + +#define AR9170_MAC_REG_GROUP_HASH_TBL_L (AR9170_MAC_REG_BASE + 0x624) +#define AR9170_MAC_REG_GROUP_HASH_TBL_H (AR9170_MAC_REG_BASE + 0x628) + +#define AR9170_MAC_REG_RX_TIMEOUT (AR9170_MAC_REG_BASE + 0x62c) + +#define AR9170_MAC_REG_BASIC_RATE (AR9170_MAC_REG_BASE + 0x630) +#define AR9170_MAC_REG_MANDATORY_RATE (AR9170_MAC_REG_BASE + 0x634) +#define AR9170_MAC_REG_RTS_CTS_RATE (AR9170_MAC_REG_BASE + 0x638) +#define AR9170_MAC_REG_BACKOFF_PROTECT (AR9170_MAC_REG_BASE + 0x63c) +#define AR9170_MAC_REG_RX_THRESHOLD (AR9170_MAC_REG_BASE + 0x640) +#define AR9170_MAC_REG_AFTER_PNP (AR9170_MAC_REG_BASE + 0x648) +#define AR9170_MAC_REG_RX_PE_DELAY (AR9170_MAC_REG_BASE + 0x64c) + +#define AR9170_MAC_REG_DYNAMIC_SIFS_ACK (AR9170_MAC_REG_BASE + 0x658) +#define AR9170_MAC_REG_SNIFFER (AR9170_MAC_REG_BASE + 0x674) +#define AR9170_MAC_SNIFFER_ENABLE_PROMISC BIT(0) +#define AR9170_MAC_SNIFFER_DEFAULTS 0x02000000 +#define AR9170_MAC_REG_ENCRYPTION (AR9170_MAC_REG_BASE + 0x678) +#define AR9170_MAC_ENCRYPTION_RX_SOFTWARE BIT(3) +#define AR9170_MAC_ENCRYPTION_DEFAULTS 0x70 + +#define AR9170_MAC_REG_MISC_680 (AR9170_MAC_REG_BASE + 0x680) +#define AR9170_MAC_REG_MISC_684 (AR9170_MAC_REG_BASE + 0x684) +#define AR9170_MAC_REG_TX_UNDERRUN (AR9170_MAC_REG_BASE + 0x688) + +#define AR9170_MAC_REG_FRAMETYPE_FILTER (AR9170_MAC_REG_BASE + 0x68c) +#define AR9170_MAC_FTF_ASSOC_REQ BIT(0) +#define AR9170_MAC_FTF_ASSOC_RESP BIT(1) +#define AR9170_MAC_FTF_REASSOC_REQ BIT(2) +#define AR9170_MAC_FTF_REASSOC_RESP BIT(3) +#define AR9170_MAC_FTF_PRB_REQ BIT(4) +#define AR9170_MAC_FTF_PRB_RESP BIT(5) +#define AR9170_MAC_FTF_BIT6 BIT(6) +#define AR9170_MAC_FTF_BIT7 BIT(7) +#define AR9170_MAC_FTF_BEACON BIT(8) +#define AR9170_MAC_FTF_ATIM BIT(9) +#define AR9170_MAC_FTF_DEASSOC BIT(10) +#define AR9170_MAC_FTF_AUTH BIT(11) +#define AR9170_MAC_FTF_DEAUTH BIT(12) +#define AR9170_MAC_FTF_BIT13 BIT(13) +#define AR9170_MAC_FTF_BIT14 BIT(14) +#define AR9170_MAC_FTF_BIT15 BIT(15) +#define AR9170_MAC_FTF_BAR BIT(24) +#define AR9170_MAC_FTF_BA BIT(25) +#define AR9170_MAC_FTF_PSPOLL BIT(26) +#define AR9170_MAC_FTF_RTS BIT(27) +#define AR9170_MAC_FTF_CTS BIT(28) +#define AR9170_MAC_FTF_ACK BIT(29) +#define AR9170_MAC_FTF_CFE BIT(30) +#define AR9170_MAC_FTF_CFE_ACK BIT(31) +#define AR9170_MAC_FTF_DEFAULTS 0x0500ffff +#define AR9170_MAC_FTF_MONITOR 0xff00ffff + +#define AR9170_MAC_REG_ACK_EXTENSION (AR9170_MAC_REG_BASE + 0x690) +#define AR9170_MAC_REG_ACK_TPC (AR9170_MAC_REG_BASE + 0x694) +#define AR9170_MAC_REG_EIFS_AND_SIFS (AR9170_MAC_REG_BASE + 0x698) +#define AR9170_MAC_REG_RX_TIMEOUT_COUNT (AR9170_MAC_REG_BASE + 0x69c) +#define AR9170_MAC_REG_RX_TOTAL (AR9170_MAC_REG_BASE + 0x6a0) +#define AR9170_MAC_REG_RX_CRC32 (AR9170_MAC_REG_BASE + 0x6a4) +#define AR9170_MAC_REG_RX_CRC16 (AR9170_MAC_REG_BASE + 0x6a8) +#define AR9170_MAC_REG_RX_ERR_DECRYPTION_UNI (AR9170_MAC_REG_BASE + 0x6ac) +#define AR9170_MAC_REG_RX_OVERRUN (AR9170_MAC_REG_BASE + 0x6b0) +#define AR9170_MAC_REG_RX_ERR_DECRYPTION_MUL (AR9170_MAC_REG_BASE + 0x6bc) +#define AR9170_MAC_REG_TX_BLOCKACKS (AR9170_MAC_REG_BASE + 0x6c0) +#define AR9170_MAC_REG_NAV_COUNT (AR9170_MAC_REG_BASE + 0x6c4) +#define AR9170_MAC_REG_BACKOFF_STATUS (AR9170_MAC_REG_BASE + 0x6c8) +#define AR9170_MAC_REG_TX_RETRY (AR9170_MAC_REG_BASE + 0x6cc) + +#define AR9170_MAC_REG_TX_COMPLETE (AR9170_MAC_REG_BASE + 0x6d4) + +#define AR9170_MAC_REG_CHANNEL_BUSY (AR9170_MAC_REG_BASE + 0x6e8) +#define AR9170_MAC_REG_EXT_BUSY (AR9170_MAC_REG_BASE + 0x6ec) + +#define AR9170_MAC_REG_SLOT_TIME (AR9170_MAC_REG_BASE + 0x6f0) +#define AR9170_MAC_REG_TX_TOTAL (AR9170_MAC_REG_BASE + 0x6f4) +#define AR9170_MAC_REG_ACK_FC (AR9170_MAC_REG_BASE + 0x6f8) + +#define AR9170_MAC_REG_CAM_MODE (AR9170_MAC_REG_BASE + 0x700) +#define AR9170_MAC_CAM_IBSS 0xe0 +#define AR9170_MAC_CAM_AP 0xa1 +#define AR9170_MAC_CAM_STA 0x2 +#define AR9170_MAC_CAM_AP_WDS 0x3 +#define AR9170_MAC_CAM_DEFAULTS (0xf << 24) +#define AR9170_MAC_CAM_HOST_PENDING 0x80000000 + +#define AR9170_MAC_REG_CAM_ROLL_CALL_TBL_L (AR9170_MAC_REG_BASE + 0x704) +#define AR9170_MAC_REG_CAM_ROLL_CALL_TBL_H (AR9170_MAC_REG_BASE + 0x708) + +#define AR9170_MAC_REG_CAM_ADDR (AR9170_MAC_REG_BASE + 0x70c) +#define AR9170_MAC_CAM_ADDR_WRITE 0x80000000 +#define AR9170_MAC_REG_CAM_DATA0 (AR9170_MAC_REG_BASE + 0x720) +#define AR9170_MAC_REG_CAM_DATA1 (AR9170_MAC_REG_BASE + 0x724) +#define AR9170_MAC_REG_CAM_DATA2 (AR9170_MAC_REG_BASE + 0x728) +#define AR9170_MAC_REG_CAM_DATA3 (AR9170_MAC_REG_BASE + 0x72c) + +#define AR9170_MAC_REG_CAM_DBG0 (AR9170_MAC_REG_BASE + 0x730) +#define AR9170_MAC_REG_CAM_DBG1 (AR9170_MAC_REG_BASE + 0x734) +#define AR9170_MAC_REG_CAM_DBG2 (AR9170_MAC_REG_BASE + 0x738) +#define AR9170_MAC_REG_CAM_STATE (AR9170_MAC_REG_BASE + 0x73c) +#define AR9170_MAC_CAM_STATE_READ_PENDING 0x40000000 +#define AR9170_MAC_CAM_STATE_WRITE_PENDING 0x80000000 + +#define AR9170_MAC_REG_CAM_TXKEY (AR9170_MAC_REG_BASE + 0x740) +#define AR9170_MAC_REG_CAM_RXKEY (AR9170_MAC_REG_BASE + 0x750) + +#define AR9170_MAC_REG_CAM_TX_ENC_TYPE (AR9170_MAC_REG_BASE + 0x760) +#define AR9170_MAC_REG_CAM_RX_ENC_TYPE (AR9170_MAC_REG_BASE + 0x770) +#define AR9170_MAC_REG_CAM_TX_SERACH_HIT (AR9170_MAC_REG_BASE + 0x780) +#define AR9170_MAC_REG_CAM_RX_SERACH_HIT (AR9170_MAC_REG_BASE + 0x790) + +#define AR9170_MAC_REG_AC0_CW (AR9170_MAC_REG_BASE + 0xb00) +#define AR9170_MAC_REG_AC1_CW (AR9170_MAC_REG_BASE + 0xb04) +#define AR9170_MAC_REG_AC2_CW (AR9170_MAC_REG_BASE + 0xb08) +#define AR9170_MAC_REG_AC3_CW (AR9170_MAC_REG_BASE + 0xb0c) +#define AR9170_MAC_REG_AC4_CW (AR9170_MAC_REG_BASE + 0xb10) +#define AR9170_MAC_REG_AC2_AC1_AC0_AIFS (AR9170_MAC_REG_BASE + 0xb14) +#define AR9170_MAC_REG_AC4_AC3_AC2_AIFS (AR9170_MAC_REG_BASE + 0xb18) +#define AR9170_MAC_REG_TXOP_ACK_EXTENSION (AR9170_MAC_REG_BASE + 0xb1c) +#define AR9170_MAC_REG_TXOP_ACK_INTERVAL (AR9170_MAC_REG_BASE + 0xb20) +#define AR9170_MAC_REG_CONTENTION_POINT (AR9170_MAC_REG_BASE + 0xb24) +#define AR9170_MAC_REG_RETRY_MAX (AR9170_MAC_REG_BASE + 0xb28) +#define AR9170_MAC_REG_TID_CFACK_CFEND_RATE (AR9170_MAC_REG_BASE + 0xb2c) +#define AR9170_MAC_REG_TXOP_NOT_ENOUGH_IND (AR9170_MAC_REG_BASE + 0xb30) +#define AR9170_MAC_REG_TKIP_TSC (AR9170_MAC_REG_BASE + 0xb34) +#define AR9170_MAC_REG_TXOP_DURATION (AR9170_MAC_REG_BASE + 0xb38) +#define AR9170_MAC_REG_TX_QOS_THRESHOLD (AR9170_MAC_REG_BASE + 0xb3c) +#define AR9170_MAC_REG_QOS_PRIORITY_VIRTUAL_CCA (AR9170_MAC_REG_BASE + 0xb40) +#define AR9170_MAC_VIRTUAL_CCA_Q0 BIT(15) +#define AR9170_MAC_VIRTUAL_CCA_Q1 BIT(16) +#define AR9170_MAC_VIRTUAL_CCA_Q2 BIT(17) +#define AR9170_MAC_VIRTUAL_CCA_Q3 BIT(18) +#define AR9170_MAC_VIRTUAL_CCA_Q4 BIT(19) +#define AR9170_MAC_VIRTUAL_CCA_ALL (0xf8000) + +#define AR9170_MAC_REG_AC1_AC0_TXOP (AR9170_MAC_REG_BASE + 0xb44) +#define AR9170_MAC_REG_AC3_AC2_TXOP (AR9170_MAC_REG_BASE + 0xb48) + +#define AR9170_MAC_REG_AMPDU_COUNT (AR9170_MAC_REG_BASE + 0xb88) +#define AR9170_MAC_REG_MPDU_COUNT (AR9170_MAC_REG_BASE + 0xb8c) + +#define AR9170_MAC_REG_AMPDU_FACTOR (AR9170_MAC_REG_BASE + 0xb9c) +#define AR9170_MAC_AMPDU_FACTOR 0x7f0000 +#define AR9170_MAC_AMPDU_FACTOR_S 16 +#define AR9170_MAC_REG_AMPDU_DENSITY (AR9170_MAC_REG_BASE + 0xba0) +#define AR9170_MAC_AMPDU_DENSITY 0x7 +#define AR9170_MAC_AMPDU_DENSITY_S 0 + +#define AR9170_MAC_REG_FCS_SELECT (AR9170_MAC_REG_BASE + 0xbb0) +#define AR9170_MAC_FCS_SWFCS 0x1 +#define AR9170_MAC_FCS_FIFO_PROT 0x4 + +#define AR9170_MAC_REG_RTS_CTS_TPC (AR9170_MAC_REG_BASE + 0xbb4) +#define AR9170_MAC_REG_CFEND_QOSNULL_TPC (AR9170_MAC_REG_BASE + 0xbb8) + +#define AR9170_MAC_REG_ACK_TABLE (AR9170_MAC_REG_BASE + 0xc00) +#define AR9170_MAC_REG_RX_CONTROL (AR9170_MAC_REG_BASE + 0xc40) +#define AR9170_MAC_RX_CTRL_DEAGG 0x1 +#define AR9170_MAC_RX_CTRL_SHORT_FILTER 0x2 +#define AR9170_MAC_RX_CTRL_SA_DA_SEARCH 0x20 +#define AR9170_MAC_RX_CTRL_PASS_TO_HOST BIT(28) +#define AR9170_MAC_RX_CTRL_ACK_IN_SNIFFER BIT(30) + +#define AR9170_MAC_REG_RX_CONTROL_1 (AR9170_MAC_REG_BASE + 0xc44) + +#define AR9170_MAC_REG_AMPDU_RX_THRESH (AR9170_MAC_REG_BASE + 0xc50) + +#define AR9170_MAC_REG_RX_MPDU (AR9170_MAC_REG_BASE + 0xca0) +#define AR9170_MAC_REG_RX_DROPPED_MPDU (AR9170_MAC_REG_BASE + 0xca4) +#define AR9170_MAC_REG_RX_DEL_MPDU (AR9170_MAC_REG_BASE + 0xca8) +#define AR9170_MAC_REG_RX_PHY_MISC_ERROR (AR9170_MAC_REG_BASE + 0xcac) +#define AR9170_MAC_REG_RX_PHY_XR_ERROR (AR9170_MAC_REG_BASE + 0xcb0) +#define AR9170_MAC_REG_RX_PHY_OFDM_ERROR (AR9170_MAC_REG_BASE + 0xcb4) +#define AR9170_MAC_REG_RX_PHY_CCK_ERROR (AR9170_MAC_REG_BASE + 0xcb8) +#define AR9170_MAC_REG_RX_PHY_HT_ERROR (AR9170_MAC_REG_BASE + 0xcbc) +#define AR9170_MAC_REG_RX_PHY_TOTAL (AR9170_MAC_REG_BASE + 0xcc0) + +#define AR9170_MAC_REG_DMA_TXQ_ADDR (AR9170_MAC_REG_BASE + 0xd00) +#define AR9170_MAC_REG_DMA_TXQ_CURR_ADDR (AR9170_MAC_REG_BASE + 0xd04) +#define AR9170_MAC_REG_DMA_TXQ0_ADDR (AR9170_MAC_REG_BASE + 0xd00) +#define AR9170_MAC_REG_DMA_TXQ0_CURR_ADDR (AR9170_MAC_REG_BASE + 0xd04) +#define AR9170_MAC_REG_DMA_TXQ1_ADDR (AR9170_MAC_REG_BASE + 0xd08) +#define AR9170_MAC_REG_DMA_TXQ1_CURR_ADDR (AR9170_MAC_REG_BASE + 0xd0c) +#define AR9170_MAC_REG_DMA_TXQ2_ADDR (AR9170_MAC_REG_BASE + 0xd10) +#define AR9170_MAC_REG_DMA_TXQ2_CURR_ADDR (AR9170_MAC_REG_BASE + 0xd14) +#define AR9170_MAC_REG_DMA_TXQ3_ADDR (AR9170_MAC_REG_BASE + 0xd18) +#define AR9170_MAC_REG_DMA_TXQ3_CURR_ADDR (AR9170_MAC_REG_BASE + 0xd1c) +#define AR9170_MAC_REG_DMA_TXQ4_ADDR (AR9170_MAC_REG_BASE + 0xd20) +#define AR9170_MAC_REG_DMA_TXQ4_CURR_ADDR (AR9170_MAC_REG_BASE + 0xd24) +#define AR9170_MAC_REG_DMA_RXQ_ADDR (AR9170_MAC_REG_BASE + 0xd28) +#define AR9170_MAC_REG_DMA_RXQ_CURR_ADDR (AR9170_MAC_REG_BASE + 0xd2c) + +#define AR9170_MAC_REG_DMA_TRIGGER (AR9170_MAC_REG_BASE + 0xd30) +#define AR9170_DMA_TRIGGER_TXQ0 BIT(0) +#define AR9170_DMA_TRIGGER_TXQ1 BIT(1) +#define AR9170_DMA_TRIGGER_TXQ2 BIT(2) +#define AR9170_DMA_TRIGGER_TXQ3 BIT(3) +#define AR9170_DMA_TRIGGER_TXQ4 BIT(4) +#define AR9170_DMA_TRIGGER_RXQ BIT(8) + +#define AR9170_MAC_REG_DMA_WLAN_STATUS (AR9170_MAC_REG_BASE + 0xd38) +#define AR9170_MAC_REG_DMA_STATUS (AR9170_MAC_REG_BASE + 0xd3c) + +#define AR9170_MAC_REG_TXRX_MPI (AR9170_MAC_REG_BASE + 0xd7c) +#define AR9170_MAC_TXRX_MPI_TX_MPI_MASK 0x0000000f +#define AR9170_MAC_TXRX_MPI_TX_TO_MASK 0x0000fff0 +#define AR9170_MAC_TXRX_MPI_RX_MPI_MASK 0x000f0000 +#define AR9170_MAC_TXRX_MPI_RX_TO_MASK 0xfff00000 + +#define AR9170_MAC_REG_BCN_ADDR (AR9170_MAC_REG_BASE + 0xd84) +#define AR9170_MAC_REG_BCN_LENGTH (AR9170_MAC_REG_BASE + 0xd88) +#define AR9170_MAC_BCN_LENGTH_MAX 256 + +#define AR9170_MAC_REG_BCN_STATUS (AR9170_MAC_REG_BASE + 0xd8c) + +#define AR9170_MAC_REG_BCN_PLCP (AR9170_MAC_REG_BASE + 0xd90) +#define AR9170_MAC_REG_BCN_CTRL (AR9170_MAC_REG_BASE + 0xd94) +#define AR9170_BCN_CTRL_READY 0x01 +#define AR9170_BCN_CTRL_LOCK 0x02 + +#define AR9170_MAC_REG_BCN_CURR_ADDR (AR9170_MAC_REG_BASE + 0xd98) +#define AR9170_MAC_REG_BCN_COUNT (AR9170_MAC_REG_BASE + 0xd9c) + + +#define AR9170_MAC_REG_BCN_HT1 (AR9170_MAC_REG_BASE + 0xda0) +#define AR9170_MAC_REG_BCN_HT2 (AR9170_MAC_REG_BASE + 0xda4) + +#define AR9170_MAC_REG_DMA_TXQX_ADDR_CURR (AR9170_MAC_REG_BASE + 0xdc0) + +/* Random number generator */ +#define AR9170_RAND_REG_BASE 0x1d0000 + +#define AR9170_RAND_REG_NUM (AR9170_RAND_REG_BASE + 0x000) +#define AR9170_RAND_REG_MODE (AR9170_RAND_REG_BASE + 0x004) +#define AR9170_RAND_MODE_MANUAL 0x000 +#define AR9170_RAND_MODE_FREE 0x001 + +/* GPIO */ +#define AR9170_GPIO_REG_BASE 0x1d0100 +#define AR9170_GPIO_REG_PORT_TYPE (AR9170_GPIO_REG_BASE + 0x000) +#define AR9170_GPIO_REG_PORT_DATA (AR9170_GPIO_REG_BASE + 0x004) +#define AR9170_GPIO_PORT_LED_0 1 +#define AR9170_GPIO_PORT_LED_1 2 +/* WPS Button GPIO for TP-Link TL-WN821N */ +#define AR9170_GPIO_PORT_WPS_BUTTON_PRESSED 4 + +/* Memory Controller */ +#define AR9170_MC_REG_BASE 0x1d1000 + +#define AR9170_MC_REG_FLASH_WAIT_STATE (AR9170_MC_REG_BASE + 0x000) +#define AR9170_MC_REG_SEEPROM_WP0 (AR9170_MC_REG_BASE + 0x400) +#define AR9170_MC_REG_SEEPROM_WP1 (AR9170_MC_REG_BASE + 0x404) +#define AR9170_MC_REG_SEEPROM_WP2 (AR9170_MC_REG_BASE + 0x408) + +/* Interrupt Controller */ +#define AR9170_MAX_INT_SRC 9 +#define AR9170_INT_REG_BASE 0x1d2000 + +#define AR9170_INT_REG_FLAG (AR9170_INT_REG_BASE + 0x000) +#define AR9170_INT_REG_FIQ_MASK (AR9170_INT_REG_BASE + 0x004) +#define AR9170_INT_REG_IRQ_MASK (AR9170_INT_REG_BASE + 0x008) +/* INT_REG_FLAG, INT_REG_FIQ_MASK and INT_REG_IRQ_MASK */ +#define AR9170_INT_FLAG_WLAN 0x001 +#define AR9170_INT_FLAG_PTAB_BIT 0x002 +#define AR9170_INT_FLAG_SE_BIT 0x004 +#define AR9170_INT_FLAG_UART_BIT 0x008 +#define AR9170_INT_FLAG_TIMER_BIT 0x010 +#define AR9170_INT_FLAG_EXT_BIT 0x020 +#define AR9170_INT_FLAG_SW_BIT 0x040 +#define AR9170_INT_FLAG_USB_BIT 0x080 +#define AR9170_INT_FLAG_ETHERNET_BIT 0x100 + +#define AR9170_INT_REG_PRIORITY1 (AR9170_INT_REG_BASE + 0x00c) +#define AR9170_INT_REG_PRIORITY2 (AR9170_INT_REG_BASE + 0x010) +#define AR9170_INT_REG_PRIORITY3 (AR9170_INT_REG_BASE + 0x014) +#define AR9170_INT_REG_EXT_INT_CONTROL (AR9170_INT_REG_BASE + 0x018) +#define AR9170_INT_REG_SW_INT_CONTROL (AR9170_INT_REG_BASE + 0x01c) +#define AR9170_INT_SW_INT_ENABLE 0x1 + +#define AR9170_INT_REG_FIQ_ENCODE (AR9170_INT_REG_BASE + 0x020) +#define AR9170_INT_INT_IRQ_ENCODE (AR9170_INT_REG_BASE + 0x024) + +/* Power Management */ +#define AR9170_PWR_REG_BASE 0x1d4000 + +#define AR9170_PWR_REG_POWER_STATE (AR9170_PWR_REG_BASE + 0x000) + +#define AR9170_PWR_REG_RESET (AR9170_PWR_REG_BASE + 0x004) +#define AR9170_PWR_RESET_COMMIT_RESET_MASK BIT(0) +#define AR9170_PWR_RESET_WLAN_MASK BIT(1) +#define AR9170_PWR_RESET_DMA_MASK BIT(2) +#define AR9170_PWR_RESET_BRIDGE_MASK BIT(3) +#define AR9170_PWR_RESET_AHB_MASK BIT(9) +#define AR9170_PWR_RESET_BB_WARM_RESET BIT(10) +#define AR9170_PWR_RESET_BB_COLD_RESET BIT(11) +#define AR9170_PWR_RESET_ADDA_CLK_COLD_RESET BIT(12) +#define AR9170_PWR_RESET_PLL BIT(13) +#define AR9170_PWR_RESET_USB_PLL BIT(14) + +#define AR9170_PWR_REG_CLOCK_SEL (AR9170_PWR_REG_BASE + 0x008) +#define AR9170_PWR_CLK_AHB_40MHZ 0 +#define AR9170_PWR_CLK_AHB_20_22MHZ 1 +#define AR9170_PWR_CLK_AHB_40_44MHZ 2 +#define AR9170_PWR_CLK_AHB_80_88MHZ 3 +#define AR9170_PWR_CLK_DAC_160_INV_DLY 0x70 + +#define AR9170_PWR_REG_CHIP_REVISION (AR9170_PWR_REG_BASE + 0x010) +#define AR9170_PWR_REG_PLL_ADDAC (AR9170_PWR_REG_BASE + 0x014) +#define AR9170_PWR_REG_WATCH_DOG_MAGIC (AR9170_PWR_REG_BASE + 0x020) + +/* Faraday USB Controller */ +#define AR9170_USB_REG_BASE 0x1e1000 + +#define AR9170_USB_REG_MAIN_CTRL (AR9170_USB_REG_BASE + 0x000) +#define AR9170_USB_MAIN_CTRL_REMOTE_WAKEUP BIT(0) +#define AR9170_USB_MAIN_CTRL_ENABLE_GLOBAL_INT BIT(2) +#define AR9170_USB_MAIN_CTRL_HIGHSPEED BIT(6) + +#define AR9170_USB_REG_DEVICE_ADDRESS (AR9170_USB_REG_BASE + 0x001) +#define AR9170_USB_DEVICE_ADDRESS_CONFIGURE BIT(7) + +#define AR9170_USB_REG_TEST (AR9170_USB_REG_BASE + 0x002) +#define AR9170_USB_REG_PHY_TEST_SELECT (AR9170_USB_REG_BASE + 0x008) +#define AR9170_USB_REG_CX_CONFIG_STATUS (AR9170_USB_REG_BASE + 0x00b) +#define AR9170_USB_REG_EP0_DATA (AR9170_USB_REG_BASE + 0x00c) +#define AR9170_USB_REG_EP0_DATA1 (AR9170_USB_REG_BASE + 0x00c) +#define AR9170_USB_REG_EP0_DATA2 (AR9170_USB_REG_BASE + 0x00d) + +#define AR9170_USB_REG_INTR_MASK_BYTE_0 (AR9170_USB_REG_BASE + 0x011) +#define AR9170_USB_REG_INTR_MASK_BYTE_1 (AR9170_USB_REG_BASE + 0x012) +#define AR9170_USB_REG_INTR_MASK_BYTE_2 (AR9170_USB_REG_BASE + 0x013) +#define AR9170_USB_REG_INTR_MASK_BYTE_3 (AR9170_USB_REG_BASE + 0x014) +#define AR9170_USB_REG_INTR_MASK_BYTE_4 (AR9170_USB_REG_BASE + 0x015) +#define AR9170_USB_INTR_DISABLE_OUT_INT (BIT(7) | BIT(6)) + +#define AR9170_USB_REG_INTR_MASK_BYTE_5 (AR9170_USB_REG_BASE + 0x016) +#define AR9170_USB_REG_INTR_MASK_BYTE_6 (AR9170_USB_REG_BASE + 0x017) +#define AR9170_USB_INTR_DISABLE_IN_INT BIT(6) + +#define AR9170_USB_REG_INTR_MASK_BYTE_7 (AR9170_USB_REG_BASE + 0x018) + +#define AR9170_USB_REG_INTR_GROUP (AR9170_USB_REG_BASE + 0x020) + +#define AR9170_USB_REG_INTR_SOURCE_0 (AR9170_USB_REG_BASE + 0x021) +#define AR9170_USB_REG_INTR_SOURCE_1 (AR9170_USB_REG_BASE + 0x022) +#define AR9170_USB_REG_INTR_SOURCE_2 (AR9170_USB_REG_BASE + 0x023) +#define AR9170_USB_REG_INTR_SOURCE_3 (AR9170_USB_REG_BASE + 0x024) +#define AR9170_USB_REG_INTR_SOURCE_4 (AR9170_USB_REG_BASE + 0x025) +#define AR9170_USB_REG_INTR_SOURCE_5 (AR9170_USB_REG_BASE + 0x026) +#define AR9170_USB_REG_INTR_SOURCE_6 (AR9170_USB_REG_BASE + 0x027) +#define AR9170_USB_REG_INTR_SOURCE_7 (AR9170_USB_REG_BASE + 0x028) + +#define AR9170_USB_REG_EP_MAP (AR9170_USB_REG_BASE + 0x030) +#define AR9170_USB_REG_EP1_MAP (AR9170_USB_REG_BASE + 0x030) +#define AR9170_USB_REG_EP2_MAP (AR9170_USB_REG_BASE + 0x031) +#define AR9170_USB_REG_EP3_MAP (AR9170_USB_REG_BASE + 0x032) +#define AR9170_USB_REG_EP4_MAP (AR9170_USB_REG_BASE + 0x033) +#define AR9170_USB_REG_EP5_MAP (AR9170_USB_REG_BASE + 0x034) +#define AR9170_USB_REG_EP6_MAP (AR9170_USB_REG_BASE + 0x035) +#define AR9170_USB_REG_EP7_MAP (AR9170_USB_REG_BASE + 0x036) +#define AR9170_USB_REG_EP8_MAP (AR9170_USB_REG_BASE + 0x037) +#define AR9170_USB_REG_EP9_MAP (AR9170_USB_REG_BASE + 0x038) +#define AR9170_USB_REG_EP10_MAP (AR9170_USB_REG_BASE + 0x039) + +#define AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH (AR9170_USB_REG_BASE + 0x03f) +#define AR9170_USB_EP_IN_TOGGLE 0x10 + +#define AR9170_USB_REG_EP_IN_MAX_SIZE_LOW (AR9170_USB_REG_BASE + 0x03e) + +#define AR9170_USB_REG_EP_OUT_MAX_SIZE_HIGH (AR9170_USB_REG_BASE + 0x05f) +#define AR9170_USB_EP_OUT_TOGGLE 0x10 + +#define AR9170_USB_REG_EP_OUT_MAX_SIZE_LOW (AR9170_USB_REG_BASE + 0x05e) + +#define AR9170_USB_REG_EP3_BYTE_COUNT_HIGH (AR9170_USB_REG_BASE + 0x0ae) +#define AR9170_USB_REG_EP3_BYTE_COUNT_LOW (AR9170_USB_REG_BASE + 0x0be) +#define AR9170_USB_REG_EP4_BYTE_COUNT_HIGH (AR9170_USB_REG_BASE + 0x0af) +#define AR9170_USB_REG_EP4_BYTE_COUNT_LOW (AR9170_USB_REG_BASE + 0x0bf) + +#define AR9170_USB_REG_FIFO_MAP (AR9170_USB_REG_BASE + 0x080) +#define AR9170_USB_REG_FIFO0_MAP (AR9170_USB_REG_BASE + 0x080) +#define AR9170_USB_REG_FIFO1_MAP (AR9170_USB_REG_BASE + 0x081) +#define AR9170_USB_REG_FIFO2_MAP (AR9170_USB_REG_BASE + 0x082) +#define AR9170_USB_REG_FIFO3_MAP (AR9170_USB_REG_BASE + 0x083) +#define AR9170_USB_REG_FIFO4_MAP (AR9170_USB_REG_BASE + 0x084) +#define AR9170_USB_REG_FIFO5_MAP (AR9170_USB_REG_BASE + 0x085) +#define AR9170_USB_REG_FIFO6_MAP (AR9170_USB_REG_BASE + 0x086) +#define AR9170_USB_REG_FIFO7_MAP (AR9170_USB_REG_BASE + 0x087) +#define AR9170_USB_REG_FIFO8_MAP (AR9170_USB_REG_BASE + 0x088) +#define AR9170_USB_REG_FIFO9_MAP (AR9170_USB_REG_BASE + 0x089) + +#define AR9170_USB_REG_FIFO_CONFIG (AR9170_USB_REG_BASE + 0x090) +#define AR9170_USB_REG_FIFO0_CONFIG (AR9170_USB_REG_BASE + 0x090) +#define AR9170_USB_REG_FIFO1_CONFIG (AR9170_USB_REG_BASE + 0x091) +#define AR9170_USB_REG_FIFO2_CONFIG (AR9170_USB_REG_BASE + 0x092) +#define AR9170_USB_REG_FIFO3_CONFIG (AR9170_USB_REG_BASE + 0x093) +#define AR9170_USB_REG_FIFO4_CONFIG (AR9170_USB_REG_BASE + 0x094) +#define AR9170_USB_REG_FIFO5_CONFIG (AR9170_USB_REG_BASE + 0x095) +#define AR9170_USB_REG_FIFO6_CONFIG (AR9170_USB_REG_BASE + 0x096) +#define AR9170_USB_REG_FIFO7_CONFIG (AR9170_USB_REG_BASE + 0x097) +#define AR9170_USB_REG_FIFO8_CONFIG (AR9170_USB_REG_BASE + 0x098) +#define AR9170_USB_REG_FIFO9_CONFIG (AR9170_USB_REG_BASE + 0x099) + +#define AR9170_USB_REG_EP3_DATA (AR9170_USB_REG_BASE + 0x0f8) +#define AR9170_USB_REG_EP4_DATA (AR9170_USB_REG_BASE + 0x0fc) + +#define AR9170_USB_REG_FIFO_SIZE (AR9170_USB_REG_BASE + 0x100) +#define AR9170_USB_REG_DMA_CTL (AR9170_USB_REG_BASE + 0x108) +#define AR9170_USB_DMA_CTL_ENABLE_TO_DEVICE BIT(0) +#define AR9170_USB_DMA_CTL_ENABLE_FROM_DEVICE BIT(1) +#define AR9170_USB_DMA_CTL_HIGH_SPEED BIT(2) +#define AR9170_USB_DMA_CTL_UP_PACKET_MODE BIT(3) +#define AR9170_USB_DMA_CTL_UP_STREAM_S 4 +#define AR9170_USB_DMA_CTL_UP_STREAM (BIT(4) | BIT(5)) +#define AR9170_USB_DMA_CTL_UP_STREAM_4K (0) +#define AR9170_USB_DMA_CTL_UP_STREAM_8K BIT(4) +#define AR9170_USB_DMA_CTL_UP_STREAM_16K BIT(5) +#define AR9170_USB_DMA_CTL_UP_STREAM_32K (BIT(4) | BIT(5)) +#define AR9170_USB_DMA_CTL_DOWN_STREAM BIT(6) + +#define AR9170_USB_REG_DMA_STATUS (AR9170_USB_REG_BASE + 0x10c) +#define AR9170_USB_DMA_STATUS_UP_IDLE BIT(8) +#define AR9170_USB_DMA_STATUS_DN_IDLE BIT(16) + +#define AR9170_USB_REG_MAX_AGG_UPLOAD (AR9170_USB_REG_BASE + 0x110) +#define AR9170_USB_REG_UPLOAD_TIME_CTL (AR9170_USB_REG_BASE + 0x114) +#define AR9170_USB_REG_CBUS_CTRL (AR9170_USB_REG_BASE + 0x1f0) +#define AR9170_USB_CBUS_CTRL_BUFFER_END (BIT(1)) + +/* PCI/USB to AHB Bridge */ +#define AR9170_PTA_REG_BASE 0x1e2000 + +#define AR9170_PTA_REG_CMD (AR9170_PTA_REG_BASE + 0x000) +#define AR9170_PTA_REG_PARAM1 (AR9170_PTA_REG_BASE + 0x004) +#define AR9170_PTA_REG_PARAM2 (AR9170_PTA_REG_BASE + 0x008) +#define AR9170_PTA_REG_PARAM3 (AR9170_PTA_REG_BASE + 0x00c) +#define AR9170_PTA_REG_RSP (AR9170_PTA_REG_BASE + 0x010) +#define AR9170_PTA_REG_STATUS1 (AR9170_PTA_REG_BASE + 0x014) +#define AR9170_PTA_REG_STATUS2 (AR9170_PTA_REG_BASE + 0x018) +#define AR9170_PTA_REG_STATUS3 (AR9170_PTA_REG_BASE + 0x01c) +#define AR9170_PTA_REG_AHB_INT_FLAG (AR9170_PTA_REG_BASE + 0x020) +#define AR9170_PTA_REG_AHB_INT_MASK (AR9170_PTA_REG_BASE + 0x024) +#define AR9170_PTA_REG_AHB_INT_ACK (AR9170_PTA_REG_BASE + 0x028) +#define AR9170_PTA_REG_AHB_SCRATCH1 (AR9170_PTA_REG_BASE + 0x030) +#define AR9170_PTA_REG_AHB_SCRATCH2 (AR9170_PTA_REG_BASE + 0x034) +#define AR9170_PTA_REG_AHB_SCRATCH3 (AR9170_PTA_REG_BASE + 0x038) +#define AR9170_PTA_REG_AHB_SCRATCH4 (AR9170_PTA_REG_BASE + 0x03c) + +#define AR9170_PTA_REG_SHARE_MEM_CTRL (AR9170_PTA_REG_BASE + 0x124) + +/* + * PCI to AHB Bridge + */ + +#define AR9170_PTA_REG_INT_FLAG (AR9170_PTA_REG_BASE + 0x100) +#define AR9170_PTA_INT_FLAG_DN 0x01 +#define AR9170_PTA_INT_FLAG_UP 0x02 +#define AR9170_PTA_INT_FLAG_CMD 0x04 + +#define AR9170_PTA_REG_INT_MASK (AR9170_PTA_REG_BASE + 0x104) +#define AR9170_PTA_REG_DN_DMA_ADDRL (AR9170_PTA_REG_BASE + 0x108) +#define AR9170_PTA_REG_DN_DMA_ADDRH (AR9170_PTA_REG_BASE + 0x10c) +#define AR9170_PTA_REG_UP_DMA_ADDRL (AR9170_PTA_REG_BASE + 0x110) +#define AR9170_PTA_REG_UP_DMA_ADDRH (AR9170_PTA_REG_BASE + 0x114) +#define AR9170_PTA_REG_DN_PEND_TIME (AR9170_PTA_REG_BASE + 0x118) +#define AR9170_PTA_REG_UP_PEND_TIME (AR9170_PTA_REG_BASE + 0x11c) +#define AR9170_PTA_REG_CONTROL (AR9170_PTA_REG_BASE + 0x120) +#define AR9170_PTA_CTRL_4_BEAT_BURST 0x00 +#define AR9170_PTA_CTRL_8_BEAT_BURST 0x01 +#define AR9170_PTA_CTRL_16_BEAT_BURST 0x02 +#define AR9170_PTA_CTRL_LOOPBACK_MODE 0x10 + +#define AR9170_PTA_REG_MEM_CTRL (AR9170_PTA_REG_BASE + 0x124) +#define AR9170_PTA_REG_MEM_ADDR (AR9170_PTA_REG_BASE + 0x128) +#define AR9170_PTA_REG_DN_DMA_TRIGGER (AR9170_PTA_REG_BASE + 0x12c) +#define AR9170_PTA_REG_UP_DMA_TRIGGER (AR9170_PTA_REG_BASE + 0x130) +#define AR9170_PTA_REG_DMA_STATUS (AR9170_PTA_REG_BASE + 0x134) +#define AR9170_PTA_REG_DN_CURR_ADDRL (AR9170_PTA_REG_BASE + 0x138) +#define AR9170_PTA_REG_DN_CURR_ADDRH (AR9170_PTA_REG_BASE + 0x13c) +#define AR9170_PTA_REG_UP_CURR_ADDRL (AR9170_PTA_REG_BASE + 0x140) +#define AR9170_PTA_REG_UP_CURR_ADDRH (AR9170_PTA_REG_BASE + 0x144) +#define AR9170_PTA_REG_DMA_MODE_CTRL (AR9170_PTA_REG_BASE + 0x148) +#define AR9170_PTA_DMA_MODE_CTRL_RESET BIT(0) +#define AR9170_PTA_DMA_MODE_CTRL_DISABLE_USB BIT(1) + +/* Protocol Controller Module */ +#define AR9170_MAC_REG_PC_REG_BASE (AR9170_MAC_REG_BASE + 0xe00) + + +#define AR9170_NUM_LEDS 2 + +/* CAM */ +#define AR9170_CAM_MAX_USER 64 +#define AR9170_CAM_MAX_KEY_LENGTH 16 + +#define AR9170_SRAM_OFFSET 0x100000 +#define AR9170_SRAM_SIZE 0x18000 + +#define AR9170_PRAM_OFFSET 0x200000 +#define AR9170_PRAM_SIZE 0x8000 + +enum cpu_clock { + AHB_STATIC_40MHZ = 0, + AHB_GMODE_22MHZ = 1, + AHB_AMODE_20MHZ = 1, + AHB_GMODE_44MHZ = 2, + AHB_AMODE_40MHZ = 2, + AHB_GMODE_88MHZ = 3, + AHB_AMODE_80MHZ = 3 +}; + +/* USB endpoints */ +enum ar9170_usb_ep { + /* + * Control EP is always EP 0 (USB SPEC) + * + * The weird thing is: the original firmware has a few + * comments that suggest that the actual EP numbers + * are in the 1 to 10 range?! + */ + AR9170_USB_EP_CTRL = 0, + + AR9170_USB_EP_TX, + AR9170_USB_EP_RX, + AR9170_USB_EP_IRQ, + AR9170_USB_EP_CMD, + AR9170_USB_NUM_EXTRA_EP = 4, + + __AR9170_USB_NUM_EP, + + __AR9170_USB_NUM_MAX_EP = 10 +}; + +enum ar9170_usb_fifo { + __AR9170_USB_NUM_MAX_FIFO = 10 +}; + +enum ar9170_tx_queues { + AR9170_TXQ0 = 0, + AR9170_TXQ1, + AR9170_TXQ2, + AR9170_TXQ3, + AR9170_TXQ_SPECIAL, + + /* keep last */ + __AR9170_NUM_TX_QUEUES = 5 +}; + +#define AR9170_TX_STREAM_TAG 0x697e +#define AR9170_RX_STREAM_TAG 0x4e00 +#define AR9170_RX_STREAM_MAX_SIZE 0xffff + +struct ar9170_stream { + __le16 length; + __le16 tag; + + u8 payload[0]; +}; + +#define AR9170_MAX_ACKTABLE_ENTRIES 8 +#define AR9170_MAX_VIRTUAL_MAC 7 + +#define AR9170_USB_EP_CTRL_MAX 64 +#define AR9170_USB_EP_TX_MAX 512 +#define AR9170_USB_EP_RX_MAX 512 +#define AR9170_USB_EP_IRQ_MAX 64 +#define AR9170_USB_EP_CMD_MAX 64 + +/* Trigger PRETBTT interrupt 6 Kus earlier */ +#define CARL9170_PRETBTT_KUS 6 + +#define AR5416_MAX_RATE_POWER 63 + +#define SET_VAL(reg, value, newvalue) \ + (value = ((value) & ~reg) | (((newvalue) << reg##_S) & reg)) + +#define MOD_VAL(reg, value, newvalue) \ + (((value) & ~reg) | (((newvalue) << reg##_S) & reg)) +#endif /* __CARL9170_SHARED_HW_H */ diff --git a/drivers/net/wireless/ath/carl9170/phy.h b/drivers/net/wireless/ath/carl9170/phy.h new file mode 100644 index 000000000000..53c18d34ffcc --- /dev/null +++ b/drivers/net/wireless/ath/carl9170/phy.h @@ -0,0 +1,567 @@ +/* + * Shared Atheros AR9170 Header + * + * PHY register map + * + * Copyright (c) 2008-2009 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __CARL9170_SHARED_PHY_H +#define __CARL9170_SHARED_PHY_H + +#define AR9170_PHY_REG_BASE (0x1bc000 + 0x9800) +#define AR9170_PHY_REG(_n) (AR9170_PHY_REG_BASE + \ + ((_n) << 2)) + +#define AR9170_PHY_REG_TEST (AR9170_PHY_REG_BASE + 0x0000) +#define AR9170_PHY_TEST_AGC_CLR 0x10000000 +#define AR9170_PHY_TEST_RFSILENT_BB 0x00002000 + +#define AR9170_PHY_REG_TURBO (AR9170_PHY_REG_BASE + 0x0004) +#define AR9170_PHY_TURBO_FC_TURBO_MODE 0x00000001 +#define AR9170_PHY_TURBO_FC_TURBO_SHORT 0x00000002 +#define AR9170_PHY_TURBO_FC_DYN2040_EN 0x00000004 +#define AR9170_PHY_TURBO_FC_DYN2040_PRI_ONLY 0x00000008 +#define AR9170_PHY_TURBO_FC_DYN2040_PRI_CH 0x00000010 +/* For 25 MHz channel spacing -- not used but supported by hw */ +#define AR9170_PHY_TURBO_FC_DYN2040_EXT_CH 0x00000020 +#define AR9170_PHY_TURBO_FC_HT_EN 0x00000040 +#define AR9170_PHY_TURBO_FC_SHORT_GI_40 0x00000080 +#define AR9170_PHY_TURBO_FC_WALSH 0x00000100 +#define AR9170_PHY_TURBO_FC_SINGLE_HT_LTF1 0x00000200 +#define AR9170_PHY_TURBO_FC_ENABLE_DAC_FIFO 0x00000800 + +#define AR9170_PHY_REG_TEST2 (AR9170_PHY_REG_BASE + 0x0008) + +#define AR9170_PHY_REG_TIMING2 (AR9170_PHY_REG_BASE + 0x0010) +#define AR9170_PHY_TIMING2_USE_FORCE 0x00001000 +#define AR9170_PHY_TIMING2_FORCE 0x00000fff +#define AR9170_PHY_TIMING2_FORCE_S 0 + +#define AR9170_PHY_REG_TIMING3 (AR9170_PHY_REG_BASE + 0x0014) +#define AR9170_PHY_TIMING3_DSC_EXP 0x0001e000 +#define AR9170_PHY_TIMING3_DSC_EXP_S 13 +#define AR9170_PHY_TIMING3_DSC_MAN 0xfffe0000 +#define AR9170_PHY_TIMING3_DSC_MAN_S 17 + +#define AR9170_PHY_REG_CHIP_ID (AR9170_PHY_REG_BASE + 0x0018) +#define AR9170_PHY_CHIP_ID_REV_0 0x80 +#define AR9170_PHY_CHIP_ID_REV_1 0x81 +#define AR9170_PHY_CHIP_ID_9160_REV_0 0xb0 + +#define AR9170_PHY_REG_ACTIVE (AR9170_PHY_REG_BASE + 0x001c) +#define AR9170_PHY_ACTIVE_EN 0x00000001 +#define AR9170_PHY_ACTIVE_DIS 0x00000000 + +#define AR9170_PHY_REG_RF_CTL2 (AR9170_PHY_REG_BASE + 0x0024) +#define AR9170_PHY_RF_CTL2_TX_END_DATA_START 0x000000ff +#define AR9170_PHY_RF_CTL2_TX_END_DATA_START_S 0 +#define AR9170_PHY_RF_CTL2_TX_END_PA_ON 0x0000ff00 +#define AR9170_PHY_RF_CTL2_TX_END_PA_ON_S 8 + +#define AR9170_PHY_REG_RF_CTL3 (AR9170_PHY_REG_BASE + 0x0028) +#define AR9170_PHY_RF_CTL3_TX_END_TO_A2_RX_ON 0x00ff0000 +#define AR9170_PHY_RF_CTL3_TX_END_TO_A2_RX_ON_S 16 + +#define AR9170_PHY_REG_ADC_CTL (AR9170_PHY_REG_BASE + 0x002c) +#define AR9170_PHY_ADC_CTL_OFF_INBUFGAIN 0x00000003 +#define AR9170_PHY_ADC_CTL_OFF_INBUFGAIN_S 0 +#define AR9170_PHY_ADC_CTL_OFF_PWDDAC 0x00002000 +#define AR9170_PHY_ADC_CTL_OFF_PWDBANDGAP 0x00004000 +#define AR9170_PHY_ADC_CTL_OFF_PWDADC 0x00008000 +#define AR9170_PHY_ADC_CTL_ON_INBUFGAIN 0x00030000 +#define AR9170_PHY_ADC_CTL_ON_INBUFGAIN_S 16 + +#define AR9170_PHY_REG_ADC_SERIAL_CTL (AR9170_PHY_REG_BASE + 0x0030) +#define AR9170_PHY_ADC_SCTL_SEL_INTERNAL_ADDAC 0x00000000 +#define AR9170_PHY_ADC_SCTL_SEL_EXTERNAL_RADIO 0x00000001 + +#define AR9170_PHY_REG_RF_CTL4 (AR9170_PHY_REG_BASE + 0x0034) +#define AR9170_PHY_RF_CTL4_TX_END_XPAB_OFF 0xff000000 +#define AR9170_PHY_RF_CTL4_TX_END_XPAB_OFF_S 24 +#define AR9170_PHY_RF_CTL4_TX_END_XPAA_OFF 0x00ff0000 +#define AR9170_PHY_RF_CTL4_TX_END_XPAA_OFF_S 16 +#define AR9170_PHY_RF_CTL4_FRAME_XPAB_ON 0x0000ff00 +#define AR9170_PHY_RF_CTL4_FRAME_XPAB_ON_S 8 +#define AR9170_PHY_RF_CTL4_FRAME_XPAA_ON 0x000000ff +#define AR9170_PHY_RF_CTL4_FRAME_XPAA_ON_S 0 + +#define AR9170_PHY_REG_TSTDAC_CONST (AR9170_PHY_REG_BASE + 0x003c) + +#define AR9170_PHY_REG_SETTLING (AR9170_PHY_REG_BASE + 0x0044) +#define AR9170_PHY_SETTLING_SWITCH 0x00003f80 +#define AR9170_PHY_SETTLING_SWITCH_S 7 + +#define AR9170_PHY_REG_RXGAIN (AR9170_PHY_REG_BASE + 0x0048) +#define AR9170_PHY_REG_RXGAIN_CHAIN_2 (AR9170_PHY_REG_BASE + 0x2048) +#define AR9170_PHY_RXGAIN_TXRX_ATTEN 0x0003f000 +#define AR9170_PHY_RXGAIN_TXRX_ATTEN_S 12 +#define AR9170_PHY_RXGAIN_TXRX_RF_MAX 0x007c0000 +#define AR9170_PHY_RXGAIN_TXRX_RF_MAX_S 18 + +#define AR9170_PHY_REG_DESIRED_SZ (AR9170_PHY_REG_BASE + 0x0050) +#define AR9170_PHY_DESIRED_SZ_ADC 0x000000ff +#define AR9170_PHY_DESIRED_SZ_ADC_S 0 +#define AR9170_PHY_DESIRED_SZ_PGA 0x0000ff00 +#define AR9170_PHY_DESIRED_SZ_PGA_S 8 +#define AR9170_PHY_DESIRED_SZ_TOT_DES 0x0ff00000 +#define AR9170_PHY_DESIRED_SZ_TOT_DES_S 20 + +#define AR9170_PHY_REG_FIND_SIG (AR9170_PHY_REG_BASE + 0x0058) +#define AR9170_PHY_FIND_SIG_FIRSTEP 0x0003f000 +#define AR9170_PHY_FIND_SIG_FIRSTEP_S 12 +#define AR9170_PHY_FIND_SIG_FIRPWR 0x03fc0000 +#define AR9170_PHY_FIND_SIG_FIRPWR_S 18 + +#define AR9170_PHY_REG_AGC_CTL1 (AR9170_PHY_REG_BASE + 0x005c) +#define AR9170_PHY_AGC_CTL1_COARSE_LOW 0x00007f80 +#define AR9170_PHY_AGC_CTL1_COARSE_LOW_S 7 +#define AR9170_PHY_AGC_CTL1_COARSE_HIGH 0x003f8000 +#define AR9170_PHY_AGC_CTL1_COARSE_HIGH_S 15 + +#define AR9170_PHY_REG_AGC_CONTROL (AR9170_PHY_REG_BASE + 0x0060) +#define AR9170_PHY_AGC_CONTROL_CAL 0x00000001 +#define AR9170_PHY_AGC_CONTROL_NF 0x00000002 +#define AR9170_PHY_AGC_CONTROL_ENABLE_NF 0x00008000 +#define AR9170_PHY_AGC_CONTROL_FLTR_CAL 0x00010000 +#define AR9170_PHY_AGC_CONTROL_NO_UPDATE_NF 0x00020000 + +#define AR9170_PHY_REG_CCA (AR9170_PHY_REG_BASE + 0x0064) +#define AR9170_PHY_CCA_MINCCA_PWR 0x0ff80000 +#define AR9170_PHY_CCA_MINCCA_PWR_S 19 +#define AR9170_PHY_CCA_THRESH62 0x0007f000 +#define AR9170_PHY_CCA_THRESH62_S 12 + +#define AR9170_PHY_REG_SFCORR (AR9170_PHY_REG_BASE + 0x0068) +#define AR9170_PHY_SFCORR_M2COUNT_THR 0x0000001f +#define AR9170_PHY_SFCORR_M2COUNT_THR_S 0 +#define AR9170_PHY_SFCORR_M1_THRESH 0x00fe0000 +#define AR9170_PHY_SFCORR_M1_THRESH_S 17 +#define AR9170_PHY_SFCORR_M2_THRESH 0x7f000000 +#define AR9170_PHY_SFCORR_M2_THRESH_S 24 + +#define AR9170_PHY_REG_SFCORR_LOW (AR9170_PHY_REG_BASE + 0x006c) +#define AR9170_PHY_SFCORR_LOW_USE_SELF_CORR_LOW 0x00000001 +#define AR9170_PHY_SFCORR_LOW_M2COUNT_THR_LOW 0x00003f00 +#define AR9170_PHY_SFCORR_LOW_M2COUNT_THR_LOW_S 8 +#define AR9170_PHY_SFCORR_LOW_M1_THRESH_LOW 0x001fc000 +#define AR9170_PHY_SFCORR_LOW_M1_THRESH_LOW_S 14 +#define AR9170_PHY_SFCORR_LOW_M2_THRESH_LOW 0x0fe00000 +#define AR9170_PHY_SFCORR_LOW_M2_THRESH_LOW_S 21 + +#define AR9170_PHY_REG_SLEEP_CTR_CONTROL (AR9170_PHY_REG_BASE + 0x0070) +#define AR9170_PHY_REG_SLEEP_CTR_LIMIT (AR9170_PHY_REG_BASE + 0x0074) +#define AR9170_PHY_REG_SLEEP_SCAL (AR9170_PHY_REG_BASE + 0x0078) + +#define AR9170_PHY_REG_PLL_CTL (AR9170_PHY_REG_BASE + 0x007c) +#define AR9170_PHY_PLL_CTL_40 0xaa +#define AR9170_PHY_PLL_CTL_40_5413 0x04 +#define AR9170_PHY_PLL_CTL_44 0xab +#define AR9170_PHY_PLL_CTL_44_2133 0xeb +#define AR9170_PHY_PLL_CTL_40_2133 0xea + +#define AR9170_PHY_REG_BIN_MASK_1 (AR9170_PHY_REG_BASE + 0x0100) +#define AR9170_PHY_REG_BIN_MASK_2 (AR9170_PHY_REG_BASE + 0x0104) +#define AR9170_PHY_REG_BIN_MASK_3 (AR9170_PHY_REG_BASE + 0x0108) +#define AR9170_PHY_REG_MASK_CTL (AR9170_PHY_REG_BASE + 0x010c) + +/* analogue power on time (100ns) */ +#define AR9170_PHY_REG_RX_DELAY (AR9170_PHY_REG_BASE + 0x0114) +#define AR9170_PHY_REG_SEARCH_START_DELAY (AR9170_PHY_REG_BASE + 0x0118) +#define AR9170_PHY_RX_DELAY_DELAY 0x00003fff + +#define AR9170_PHY_REG_TIMING_CTRL4(_i) (AR9170_PHY_REG_BASE + \ + (0x0120 + ((_i) << 12))) +#define AR9170_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF 0x01f +#define AR9170_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF_S 0 +#define AR9170_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF 0x7e0 +#define AR9170_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF_S 5 +#define AR9170_PHY_TIMING_CTRL4_IQCORR_ENABLE 0x800 +#define AR9170_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX 0xf000 +#define AR9170_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX_S 12 +#define AR9170_PHY_TIMING_CTRL4_DO_IQCAL 0x10000 +#define AR9170_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI 0x80000000 +#define AR9170_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER 0x40000000 +#define AR9170_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK 0x20000000 +#define AR9170_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK 0x10000000 + +#define AR9170_PHY_REG_TIMING5 (AR9170_PHY_REG_BASE + 0x0124) +#define AR9170_PHY_TIMING5_CYCPWR_THR1 0x000000fe +#define AR9170_PHY_TIMING5_CYCPWR_THR1_S 1 + +#define AR9170_PHY_REG_POWER_TX_RATE1 (AR9170_PHY_REG_BASE + 0x0134) +#define AR9170_PHY_REG_POWER_TX_RATE2 (AR9170_PHY_REG_BASE + 0x0138) +#define AR9170_PHY_REG_POWER_TX_RATE_MAX (AR9170_PHY_REG_BASE + 0x013c) +#define AR9170_PHY_POWER_TX_RATE_MAX_TPC_ENABLE 0x00000040 + +#define AR9170_PHY_REG_FRAME_CTL (AR9170_PHY_REG_BASE + 0x0144) +#define AR9170_PHY_FRAME_CTL_TX_CLIP 0x00000038 +#define AR9170_PHY_FRAME_CTL_TX_CLIP_S 3 + +#define AR9170_PHY_REG_SPUR_REG (AR9170_PHY_REG_BASE + 0x014c) +#define AR9170_PHY_SPUR_REG_MASK_RATE_CNTL (0xff << 18) +#define AR9170_PHY_SPUR_REG_MASK_RATE_CNTL_S 18 +#define AR9170_PHY_SPUR_REG_ENABLE_MASK_PPM 0x20000 +#define AR9170_PHY_SPUR_REG_MASK_RATE_SELECT (0xff << 9) +#define AR9170_PHY_SPUR_REG_MASK_RATE_SELECT_S 9 +#define AR9170_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI 0x100 +#define AR9170_PHY_SPUR_REG_SPUR_RSSI_THRESH 0x7f +#define AR9170_PHY_SPUR_REG_SPUR_RSSI_THRESH_S 0 + +#define AR9170_PHY_REG_RADAR_EXT (AR9170_PHY_REG_BASE + 0x0140) +#define AR9170_PHY_RADAR_EXT_ENA 0x00004000 + +#define AR9170_PHY_REG_RADAR_0 (AR9170_PHY_REG_BASE + 0x0154) +#define AR9170_PHY_RADAR_0_ENA 0x00000001 +#define AR9170_PHY_RADAR_0_FFT_ENA 0x80000000 +/* inband pulse threshold */ +#define AR9170_PHY_RADAR_0_INBAND 0x0000003e +#define AR9170_PHY_RADAR_0_INBAND_S 1 +/* pulse RSSI threshold */ +#define AR9170_PHY_RADAR_0_PRSSI 0x00000fc0 +#define AR9170_PHY_RADAR_0_PRSSI_S 6 +/* pulse height threshold */ +#define AR9170_PHY_RADAR_0_HEIGHT 0x0003f000 +#define AR9170_PHY_RADAR_0_HEIGHT_S 12 +/* radar RSSI threshold */ +#define AR9170_PHY_RADAR_0_RRSSI 0x00fc0000 +#define AR9170_PHY_RADAR_0_RRSSI_S 18 +/* radar firepower threshold */ +#define AR9170_PHY_RADAR_0_FIRPWR 0x7f000000 +#define AR9170_PHY_RADAR_0_FIRPWR_S 24 + +#define AR9170_PHY_REG_RADAR_1 (AR9170_PHY_REG_BASE + 0x0158) +#define AR9170_PHY_RADAR_1_RELPWR_ENA 0x00800000 +#define AR9170_PHY_RADAR_1_USE_FIR128 0x00400000 +#define AR9170_PHY_RADAR_1_RELPWR_THRESH 0x003f0000 +#define AR9170_PHY_RADAR_1_RELPWR_THRESH_S 16 +#define AR9170_PHY_RADAR_1_BLOCK_CHECK 0x00008000 +#define AR9170_PHY_RADAR_1_MAX_RRSSI 0x00004000 +#define AR9170_PHY_RADAR_1_RELSTEP_CHECK 0x00002000 +#define AR9170_PHY_RADAR_1_RELSTEP_THRESH 0x00001f00 +#define AR9170_PHY_RADAR_1_RELSTEP_THRESH_S 8 +#define AR9170_PHY_RADAR_1_MAXLEN 0x000000ff +#define AR9170_PHY_RADAR_1_MAXLEN_S 0 + +#define AR9170_PHY_REG_SWITCH_CHAIN_0 (AR9170_PHY_REG_BASE + 0x0160) +#define AR9170_PHY_REG_SWITCH_CHAIN_2 (AR9170_PHY_REG_BASE + 0x2160) + +#define AR9170_PHY_REG_SWITCH_COM (AR9170_PHY_REG_BASE + 0x0164) + +#define AR9170_PHY_REG_CCA_THRESHOLD (AR9170_PHY_REG_BASE + 0x0168) + +#define AR9170_PHY_REG_SIGMA_DELTA (AR9170_PHY_REG_BASE + 0x016c) +#define AR9170_PHY_SIGMA_DELTA_ADC_SEL 0x00000003 +#define AR9170_PHY_SIGMA_DELTA_ADC_SEL_S 0 +#define AR9170_PHY_SIGMA_DELTA_FILT2 0x000000f8 +#define AR9170_PHY_SIGMA_DELTA_FILT2_S 3 +#define AR9170_PHY_SIGMA_DELTA_FILT1 0x00001f00 +#define AR9170_PHY_SIGMA_DELTA_FILT1_S 8 +#define AR9170_PHY_SIGMA_DELTA_ADC_CLIP 0x01ffe000 +#define AR9170_PHY_SIGMA_DELTA_ADC_CLIP_S 13 + +#define AR9170_PHY_REG_RESTART (AR9170_PHY_REG_BASE + 0x0170) +#define AR9170_PHY_RESTART_DIV_GC 0x001c0000 +#define AR9170_PHY_RESTART_DIV_GC_S 18 + +#define AR9170_PHY_REG_RFBUS_REQ (AR9170_PHY_REG_BASE + 0x017c) +#define AR9170_PHY_RFBUS_REQ_EN 0x00000001 + +#define AR9170_PHY_REG_TIMING7 (AR9170_PHY_REG_BASE + 0x0180) +#define AR9170_PHY_REG_TIMING8 (AR9170_PHY_REG_BASE + 0x0184) +#define AR9170_PHY_TIMING8_PILOT_MASK_2 0x000fffff +#define AR9170_PHY_TIMING8_PILOT_MASK_2_S 0 + +#define AR9170_PHY_REG_BIN_MASK2_1 (AR9170_PHY_REG_BASE + 0x0188) +#define AR9170_PHY_REG_BIN_MASK2_2 (AR9170_PHY_REG_BASE + 0x018c) +#define AR9170_PHY_REG_BIN_MASK2_3 (AR9170_PHY_REG_BASE + 0x0190) +#define AR9170_PHY_REG_BIN_MASK2_4 (AR9170_PHY_REG_BASE + 0x0194) +#define AR9170_PHY_BIN_MASK2_4_MASK_4 0x00003fff +#define AR9170_PHY_BIN_MASK2_4_MASK_4_S 0 + +#define AR9170_PHY_REG_TIMING9 (AR9170_PHY_REG_BASE + 0x0198) +#define AR9170_PHY_REG_TIMING10 (AR9170_PHY_REG_BASE + 0x019c) +#define AR9170_PHY_TIMING10_PILOT_MASK_2 0x000fffff +#define AR9170_PHY_TIMING10_PILOT_MASK_2_S 0 + +#define AR9170_PHY_REG_TIMING11 (AR9170_PHY_REG_BASE + 0x01a0) +#define AR9170_PHY_TIMING11_SPUR_DELTA_PHASE 0x000fffff +#define AR9170_PHY_TIMING11_SPUR_DELTA_PHASE_S 0 +#define AR9170_PHY_TIMING11_SPUR_FREQ_SD 0x3ff00000 +#define AR9170_PHY_TIMING11_SPUR_FREQ_SD_S 20 +#define AR9170_PHY_TIMING11_USE_SPUR_IN_AGC 0x40000000 +#define AR9170_PHY_TIMING11_USE_SPUR_IN_SELFCOR 0x80000000 + +#define AR9170_PHY_REG_RX_CHAINMASK (AR9170_PHY_REG_BASE + 0x01a4) +#define AR9170_PHY_REG_NEW_ADC_DC_GAIN_CORR(_i) (AR9170_PHY_REG_BASE + \ + 0x01b4 + ((_i) << 12)) +#define AR9170_PHY_NEW_ADC_GAIN_CORR_ENABLE 0x40000000 +#define AR9170_PHY_NEW_ADC_DC_OFFSET_CORR_ENABLE 0x80000000 + +#define AR9170_PHY_REG_MULTICHAIN_GAIN_CTL (AR9170_PHY_REG_BASE + 0x01ac) +#define AR9170_PHY_9285_ANT_DIV_CTL_ALL 0x7f000000 +#define AR9170_PHY_9285_ANT_DIV_CTL 0x01000000 +#define AR9170_PHY_9285_ANT_DIV_CTL_S 24 +#define AR9170_PHY_9285_ANT_DIV_ALT_LNACONF 0x06000000 +#define AR9170_PHY_9285_ANT_DIV_ALT_LNACONF_S 25 +#define AR9170_PHY_9285_ANT_DIV_MAIN_LNACONF 0x18000000 +#define AR9170_PHY_9285_ANT_DIV_MAIN_LNACONF_S 27 +#define AR9170_PHY_9285_ANT_DIV_ALT_GAINTB 0x20000000 +#define AR9170_PHY_9285_ANT_DIV_ALT_GAINTB_S 29 +#define AR9170_PHY_9285_ANT_DIV_MAIN_GAINTB 0x40000000 +#define AR9170_PHY_9285_ANT_DIV_MAIN_GAINTB_S 30 +#define AR9170_PHY_9285_ANT_DIV_LNA1 2 +#define AR9170_PHY_9285_ANT_DIV_LNA2 1 +#define AR9170_PHY_9285_ANT_DIV_LNA1_PLUS_LNA2 3 +#define AR9170_PHY_9285_ANT_DIV_LNA1_MINUS_LNA2 0 +#define AR9170_PHY_9285_ANT_DIV_GAINTB_0 0 +#define AR9170_PHY_9285_ANT_DIV_GAINTB_1 1 + +#define AR9170_PHY_REG_EXT_CCA0 (AR9170_PHY_REG_BASE + 0x01b8) +#define AR9170_PHY_REG_EXT_CCA0_THRESH62 0x000000ff +#define AR9170_PHY_REG_EXT_CCA0_THRESH62_S 0 + +#define AR9170_PHY_REG_EXT_CCA (AR9170_PHY_REG_BASE + 0x01bc) +#define AR9170_PHY_EXT_CCA_CYCPWR_THR1 0x0000fe00 +#define AR9170_PHY_EXT_CCA_CYCPWR_THR1_S 9 +#define AR9170_PHY_EXT_CCA_THRESH62 0x007f0000 +#define AR9170_PHY_EXT_CCA_THRESH62_S 16 +#define AR9170_PHY_EXT_MINCCA_PWR 0xff800000 +#define AR9170_PHY_EXT_MINCCA_PWR_S 23 + +#define AR9170_PHY_REG_SFCORR_EXT (AR9170_PHY_REG_BASE + 0x01c0) +#define AR9170_PHY_SFCORR_EXT_M1_THRESH 0x0000007f +#define AR9170_PHY_SFCORR_EXT_M1_THRESH_S 0 +#define AR9170_PHY_SFCORR_EXT_M2_THRESH 0x00003f80 +#define AR9170_PHY_SFCORR_EXT_M2_THRESH_S 7 +#define AR9170_PHY_SFCORR_EXT_M1_THRESH_LOW 0x001fc000 +#define AR9170_PHY_SFCORR_EXT_M1_THRESH_LOW_S 14 +#define AR9170_PHY_SFCORR_EXT_M2_THRESH_LOW 0x0fe00000 +#define AR9170_PHY_SFCORR_EXT_M2_THRESH_LOW_S 21 +#define AR9170_PHY_SFCORR_SPUR_SUBCHNL_SD_S 28 + +#define AR9170_PHY_REG_HALFGI (AR9170_PHY_REG_BASE + 0x01d0) +#define AR9170_PHY_HALFGI_DSC_MAN 0x0007fff0 +#define AR9170_PHY_HALFGI_DSC_MAN_S 4 +#define AR9170_PHY_HALFGI_DSC_EXP 0x0000000f +#define AR9170_PHY_HALFGI_DSC_EXP_S 0 + +#define AR9170_PHY_REG_CHANNEL_MASK_01_30 (AR9170_PHY_REG_BASE + 0x01d4) +#define AR9170_PHY_REG_CHANNEL_MASK_31_60 (AR9170_PHY_REG_BASE + 0x01d8) + +#define AR9170_PHY_REG_CHAN_INFO_MEMORY (AR9170_PHY_REG_BASE + 0x01dc) +#define AR9170_PHY_CHAN_INFO_MEMORY_CAPTURE_MASK 0x0001 + +#define AR9170_PHY_REG_HEAVY_CLIP_ENABLE (AR9170_PHY_REG_BASE + 0x01e0) +#define AR9170_PHY_REG_HEAVY_CLIP_FACTOR_RIFS (AR9170_PHY_REG_BASE + 0x01ec) +#define AR9170_PHY_RIFS_INIT_DELAY 0x03ff0000 + +#define AR9170_PHY_REG_CALMODE (AR9170_PHY_REG_BASE + 0x01f0) +#define AR9170_PHY_CALMODE_IQ 0x00000000 +#define AR9170_PHY_CALMODE_ADC_GAIN 0x00000001 +#define AR9170_PHY_CALMODE_ADC_DC_PER 0x00000002 +#define AR9170_PHY_CALMODE_ADC_DC_INIT 0x00000003 + +#define AR9170_PHY_REG_REFCLKDLY (AR9170_PHY_REG_BASE + 0x01f4) +#define AR9170_PHY_REG_REFCLKPD (AR9170_PHY_REG_BASE + 0x01f8) + + +#define AR9170_PHY_REG_CAL_MEAS_0(_i) (AR9170_PHY_REG_BASE + \ + 0x0410 + ((_i) << 12)) +#define AR9170_PHY_REG_CAL_MEAS_1(_i) (AR9170_PHY_REG_BASE + \ + 0x0414 \ + ((_i) << 12)) +#define AR9170_PHY_REG_CAL_MEAS_2(_i) (AR9170_PHY_REG_BASE + \ + 0x0418 + ((_i) << 12)) +#define AR9170_PHY_REG_CAL_MEAS_3(_i) (AR9170_PHY_REG_BASE + \ + 0x041c + ((_i) << 12)) + +#define AR9170_PHY_REG_CURRENT_RSSI (AR9170_PHY_REG_BASE + 0x041c) + +#define AR9170_PHY_REG_RFBUS_GRANT (AR9170_PHY_REG_BASE + 0x0420) +#define AR9170_PHY_RFBUS_GRANT_EN 0x00000001 + +#define AR9170_PHY_REG_CHAN_INFO_GAIN_DIFF (AR9170_PHY_REG_BASE + 0x04f4) +#define AR9170_PHY_CHAN_INFO_GAIN_DIFF_UPPER_LIMIT 320 + +#define AR9170_PHY_REG_CHAN_INFO_GAIN (AR9170_PHY_REG_BASE + 0x04fc) + +#define AR9170_PHY_REG_MODE (AR9170_PHY_REG_BASE + 0x0a00) +#define AR9170_PHY_MODE_ASYNCFIFO 0x80 +#define AR9170_PHY_MODE_AR2133 0x08 +#define AR9170_PHY_MODE_AR5111 0x00 +#define AR9170_PHY_MODE_AR5112 0x08 +#define AR9170_PHY_MODE_DYNAMIC 0x04 +#define AR9170_PHY_MODE_RF2GHZ 0x02 +#define AR9170_PHY_MODE_RF5GHZ 0x00 +#define AR9170_PHY_MODE_CCK 0x01 +#define AR9170_PHY_MODE_OFDM 0x00 +#define AR9170_PHY_MODE_DYN_CCK_DISABLE 0x100 + +#define AR9170_PHY_REG_CCK_TX_CTRL (AR9170_PHY_REG_BASE + 0x0a04) +#define AR9170_PHY_CCK_TX_CTRL_JAPAN 0x00000010 +#define AR9170_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK 0x0000000c +#define AR9170_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK_S 2 + +#define AR9170_PHY_REG_CCK_DETECT (AR9170_PHY_REG_BASE + 0x0a08) +#define AR9170_PHY_CCK_DETECT_WEAK_SIG_THR_CCK 0x0000003f +#define AR9170_PHY_CCK_DETECT_WEAK_SIG_THR_CCK_S 0 +/* [12:6] settling time for antenna switch */ +#define AR9170_PHY_CCK_DETECT_ANT_SWITCH_TIME 0x00001fc0 +#define AR9170_PHY_CCK_DETECT_ANT_SWITCH_TIME_S 6 +#define AR9170_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV 0x2000 +#define AR9170_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV_S 13 + +#define AR9170_PHY_REG_GAIN_2GHZ_CHAIN_2 (AR9170_PHY_REG_BASE + 0x2a0c) +#define AR9170_PHY_REG_GAIN_2GHZ (AR9170_PHY_REG_BASE + 0x0a0c) +#define AR9170_PHY_GAIN_2GHZ_RXTX_MARGIN 0x00fc0000 +#define AR9170_PHY_GAIN_2GHZ_RXTX_MARGIN_S 18 +#define AR9170_PHY_GAIN_2GHZ_BSW_MARGIN 0x00003c00 +#define AR9170_PHY_GAIN_2GHZ_BSW_MARGIN_S 10 +#define AR9170_PHY_GAIN_2GHZ_BSW_ATTEN 0x0000001f +#define AR9170_PHY_GAIN_2GHZ_BSW_ATTEN_S 0 +#define AR9170_PHY_GAIN_2GHZ_XATTEN2_MARGIN 0x003e0000 +#define AR9170_PHY_GAIN_2GHZ_XATTEN2_MARGIN_S 17 +#define AR9170_PHY_GAIN_2GHZ_XATTEN1_MARGIN 0x0001f000 +#define AR9170_PHY_GAIN_2GHZ_XATTEN1_MARGIN_S 12 +#define AR9170_PHY_GAIN_2GHZ_XATTEN2_DB 0x00000fc0 +#define AR9170_PHY_GAIN_2GHZ_XATTEN2_DB_S 6 +#define AR9170_PHY_GAIN_2GHZ_XATTEN1_DB 0x0000003f +#define AR9170_PHY_GAIN_2GHZ_XATTEN1_DB_S 0 + +#define AR9170_PHY_REG_CCK_RXCTRL4 (AR9170_PHY_REG_BASE + 0x0a1c) +#define AR9170_PHY_CCK_RXCTRL4_FREQ_EST_SHORT 0x01f80000 +#define AR9170_PHY_CCK_RXCTRL4_FREQ_EST_SHORT_S 19 + +#define AR9170_PHY_REG_DAG_CTRLCCK (AR9170_PHY_REG_BASE + 0x0a28) +#define AR9170_REG_DAG_CTRLCCK_EN_RSSI_THR 0x00000200 +#define AR9170_REG_DAG_CTRLCCK_RSSI_THR 0x0001fc00 +#define AR9170_REG_DAG_CTRLCCK_RSSI_THR_S 10 + +#define AR9170_PHY_REG_FORCE_CLKEN_CCK (AR9170_PHY_REG_BASE + 0x0a2c) +#define AR9170_FORCE_CLKEN_CCK_MRC_MUX 0x00000040 + +#define AR9170_PHY_REG_POWER_TX_RATE3 (AR9170_PHY_REG_BASE + 0x0a34) +#define AR9170_PHY_REG_POWER_TX_RATE4 (AR9170_PHY_REG_BASE + 0x0a38) + +#define AR9170_PHY_REG_SCRM_SEQ_XR (AR9170_PHY_REG_BASE + 0x0a3c) +#define AR9170_PHY_REG_HEADER_DETECT_XR (AR9170_PHY_REG_BASE + 0x0a40) +#define AR9170_PHY_REG_CHIRP_DETECTED_XR (AR9170_PHY_REG_BASE + 0x0a44) +#define AR9170_PHY_REG_BLUETOOTH (AR9170_PHY_REG_BASE + 0x0a54) + +#define AR9170_PHY_REG_TPCRG1 (AR9170_PHY_REG_BASE + 0x0a58) +#define AR9170_PHY_TPCRG1_NUM_PD_GAIN 0x0000c000 +#define AR9170_PHY_TPCRG1_NUM_PD_GAIN_S 14 +#define AR9170_PHY_TPCRG1_PD_GAIN_1 0x00030000 +#define AR9170_PHY_TPCRG1_PD_GAIN_1_S 16 +#define AR9170_PHY_TPCRG1_PD_GAIN_2 0x000c0000 +#define AR9170_PHY_TPCRG1_PD_GAIN_2_S 18 +#define AR9170_PHY_TPCRG1_PD_GAIN_3 0x00300000 +#define AR9170_PHY_TPCRG1_PD_GAIN_3_S 20 +#define AR9170_PHY_TPCRG1_PD_CAL_ENABLE 0x00400000 +#define AR9170_PHY_TPCRG1_PD_CAL_ENABLE_S 22 + +#define AR9170_PHY_REG_TX_PWRCTRL4 (AR9170_PHY_REG_BASE + 0x0a64) +#define AR9170_PHY_TX_PWRCTRL_PD_AVG_VALID 0x00000001 +#define AR9170_PHY_TX_PWRCTRL_PD_AVG_VALID_S 0 +#define AR9170_PHY_TX_PWRCTRL_PD_AVG_OUT 0x000001fe +#define AR9170_PHY_TX_PWRCTRL_PD_AVG_OUT_S 1 + +#define AR9170_PHY_REG_ANALOG_SWAP (AR9170_PHY_REG_BASE + 0x0a68) +#define AR9170_PHY_ANALOG_SWAP_AB 0x0001 +#define AR9170_PHY_ANALOG_SWAP_ALT_CHAIN 0x00000040 + +#define AR9170_PHY_REG_TPCRG5 (AR9170_PHY_REG_BASE + 0x0a6c) +#define AR9170_PHY_TPCRG5_PD_GAIN_OVERLAP 0x0000000f +#define AR9170_PHY_TPCRG5_PD_GAIN_OVERLAP_S 0 +#define AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_1 0x000003f0 +#define AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_1_S 4 +#define AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_2 0x0000fc00 +#define AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_2_S 10 +#define AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_3 0x003f0000 +#define AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_3_S 16 +#define AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_4 0x0fc00000 +#define AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_4_S 22 + +#define AR9170_PHY_REG_TX_PWRCTRL6_0 (AR9170_PHY_REG_BASE + 0x0a70) +#define AR9170_PHY_REG_TX_PWRCTRL6_1 (AR9170_PHY_REG_BASE + 0x1a70) +#define AR9170_PHY_TX_PWRCTRL_ERR_EST_MODE 0x03000000 +#define AR9170_PHY_TX_PWRCTRL_ERR_EST_MODE_S 24 + +#define AR9170_PHY_REG_TX_PWRCTRL7 (AR9170_PHY_REG_BASE + 0x0a74) +#define AR9170_PHY_TX_PWRCTRL_INIT_TX_GAIN 0x01f80000 +#define AR9170_PHY_TX_PWRCTRL_INIT_TX_GAIN_S 19 + +#define AR9170_PHY_REG_TX_PWRCTRL9 (AR9170_PHY_REG_BASE + 0x0a7c) +#define AR9170_PHY_TX_DESIRED_SCALE_CCK 0x00007c00 +#define AR9170_PHY_TX_DESIRED_SCALE_CCK_S 10 +#define AR9170_PHY_TX_PWRCTRL9_RES_DC_REMOVAL 0x80000000 +#define AR9170_PHY_TX_PWRCTRL9_RES_DC_REMOVAL_S 31 + +#define AR9170_PHY_REG_TX_GAIN_TBL1 (AR9170_PHY_REG_BASE + 0x0b00) +#define AR9170_PHY_TX_GAIN 0x0007f000 +#define AR9170_PHY_TX_GAIN_S 12 + +/* Carrier leak calibration control, do it after AGC calibration */ +#define AR9170_PHY_REG_CL_CAL_CTL (AR9170_PHY_REG_BASE + 0x0b58) +#define AR9170_PHY_CL_CAL_ENABLE 0x00000002 +#define AR9170_PHY_CL_CAL_PARALLEL_CAL_ENABLE 0x00000001 + +#define AR9170_PHY_REG_POWER_TX_RATE5 (AR9170_PHY_REG_BASE + 0x0b8c) +#define AR9170_PHY_REG_POWER_TX_RATE6 (AR9170_PHY_REG_BASE + 0x0b90) + +#define AR9170_PHY_REG_CH0_TX_PWRCTRL11 (AR9170_PHY_REG_BASE + 0x0b98) +#define AR9170_PHY_REG_CH1_TX_PWRCTRL11 (AR9170_PHY_REG_BASE + 0x1b98) +#define AR9170_PHY_TX_CHX_PWRCTRL_OLPC_TEMP_COMP 0x0000fc00 +#define AR9170_PHY_TX_CHX_PWRCTRL_OLPC_TEMP_COMP_S 10 + +#define AR9170_PHY_REG_CAL_CHAINMASK (AR9170_PHY_REG_BASE + 0x0b9c) +#define AR9170_PHY_REG_VIT_MASK2_M_46_61 (AR9170_PHY_REG_BASE + 0x0ba0) +#define AR9170_PHY_REG_MASK2_M_31_45 (AR9170_PHY_REG_BASE + 0x0ba4) +#define AR9170_PHY_REG_MASK2_M_16_30 (AR9170_PHY_REG_BASE + 0x0ba8) +#define AR9170_PHY_REG_MASK2_M_00_15 (AR9170_PHY_REG_BASE + 0x0bac) +#define AR9170_PHY_REG_PILOT_MASK_01_30 (AR9170_PHY_REG_BASE + 0x0bb0) +#define AR9170_PHY_REG_PILOT_MASK_31_60 (AR9170_PHY_REG_BASE + 0x0bb4) +#define AR9170_PHY_REG_MASK2_P_15_01 (AR9170_PHY_REG_BASE + 0x0bb8) +#define AR9170_PHY_REG_MASK2_P_30_16 (AR9170_PHY_REG_BASE + 0x0bbc) +#define AR9170_PHY_REG_MASK2_P_45_31 (AR9170_PHY_REG_BASE + 0x0bc0) +#define AR9170_PHY_REG_MASK2_P_61_45 (AR9170_PHY_REG_BASE + 0x0bc4) +#define AR9170_PHY_REG_POWER_TX_SUB (AR9170_PHY_REG_BASE + 0x0bc8) +#define AR9170_PHY_REG_POWER_TX_RATE7 (AR9170_PHY_REG_BASE + 0x0bcc) +#define AR9170_PHY_REG_POWER_TX_RATE8 (AR9170_PHY_REG_BASE + 0x0bd0) +#define AR9170_PHY_REG_POWER_TX_RATE9 (AR9170_PHY_REG_BASE + 0x0bd4) +#define AR9170_PHY_REG_XPA_CFG (AR9170_PHY_REG_BASE + 0x0bd8) +#define AR9170_PHY_FORCE_XPA_CFG 0x000000001 +#define AR9170_PHY_FORCE_XPA_CFG_S 0 + +#define AR9170_PHY_REG_CH1_CCA (AR9170_PHY_REG_BASE + 0x1064) +#define AR9170_PHY_CH1_MINCCA_PWR 0x0ff80000 +#define AR9170_PHY_CH1_MINCCA_PWR_S 19 + +#define AR9170_PHY_REG_CH2_CCA (AR9170_PHY_REG_BASE + 0x2064) +#define AR9170_PHY_CH2_MINCCA_PWR 0x0ff80000 +#define AR9170_PHY_CH2_MINCCA_PWR_S 19 + +#define AR9170_PHY_REG_CH1_EXT_CCA (AR9170_PHY_REG_BASE + 0x11bc) +#define AR9170_PHY_CH1_EXT_MINCCA_PWR 0xff800000 +#define AR9170_PHY_CH1_EXT_MINCCA_PWR_S 23 + +#define AR9170_PHY_REG_CH2_EXT_CCA (AR9170_PHY_REG_BASE + 0x21bc) +#define AR9170_PHY_CH2_EXT_MINCCA_PWR 0xff800000 +#define AR9170_PHY_CH2_EXT_MINCCA_PWR_S 23 + +#define REDUCE_CHAIN_0 0x00000050 +#define REDUCE_CHAIN_1 0x00000051 + +#endif /* __CARL9170_SHARED_PHY_H */ diff --git a/drivers/net/wireless/ath/carl9170/wlan.h b/drivers/net/wireless/ath/carl9170/wlan.h new file mode 100644 index 000000000000..48ead2268f50 --- /dev/null +++ b/drivers/net/wireless/ath/carl9170/wlan.h @@ -0,0 +1,412 @@ +/* + * Shared Atheros AR9170 Header + * + * RX/TX meta descriptor format + * + * Copyright 2008, Johannes Berg + * Copyright 2009, 2010, Christian Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __CARL9170_SHARED_WLAN_H +#define __CARL9170_SHARED_WLAN_H + +#include "fwcmd.h" + +#define AR9170_RX_PHY_RATE_CCK_1M 0x0a +#define AR9170_RX_PHY_RATE_CCK_2M 0x14 +#define AR9170_RX_PHY_RATE_CCK_5M 0x37 +#define AR9170_RX_PHY_RATE_CCK_11M 0x6e + +#define AR9170_ENC_ALG_NONE 0x0 +#define AR9170_ENC_ALG_WEP64 0x1 +#define AR9170_ENC_ALG_TKIP 0x2 +#define AR9170_ENC_ALG_AESCCMP 0x4 +#define AR9170_ENC_ALG_WEP128 0x5 +#define AR9170_ENC_ALG_WEP256 0x6 +#define AR9170_ENC_ALG_CENC 0x7 + +#define AR9170_RX_ENC_SOFTWARE 0x8 + +#define AR9170_RX_STATUS_MODULATION 0x03 +#define AR9170_RX_STATUS_MODULATION_S 0 +#define AR9170_RX_STATUS_MODULATION_CCK 0x00 +#define AR9170_RX_STATUS_MODULATION_OFDM 0x01 +#define AR9170_RX_STATUS_MODULATION_HT 0x02 +#define AR9170_RX_STATUS_MODULATION_DUPOFDM 0x03 + +/* depends on modulation */ +#define AR9170_RX_STATUS_SHORT_PREAMBLE 0x08 +#define AR9170_RX_STATUS_GREENFIELD 0x08 + +#define AR9170_RX_STATUS_MPDU 0x30 +#define AR9170_RX_STATUS_MPDU_S 4 +#define AR9170_RX_STATUS_MPDU_SINGLE 0x00 +#define AR9170_RX_STATUS_MPDU_FIRST 0x20 +#define AR9170_RX_STATUS_MPDU_MIDDLE 0x30 +#define AR9170_RX_STATUS_MPDU_LAST 0x10 + +#define AR9170_RX_ERROR_RXTO 0x01 +#define AR9170_RX_ERROR_OVERRUN 0x02 +#define AR9170_RX_ERROR_DECRYPT 0x04 +#define AR9170_RX_ERROR_FCS 0x08 +#define AR9170_RX_ERROR_WRONG_RA 0x10 +#define AR9170_RX_ERROR_PLCP 0x20 +#define AR9170_RX_ERROR_MMIC 0x40 +#define AR9170_RX_ERROR_FATAL 0x80 + +/* these are either-or */ +#define AR9170_TX_MAC_PROT_RTS 0x0001 +#define AR9170_TX_MAC_PROT_CTS 0x0002 +#define AR9170_TX_MAC_PROT 0x0003 + +#define AR9170_TX_MAC_NO_ACK 0x0004 +/* if unset, MAC will only do SIFS space before frame */ +#define AR9170_TX_MAC_BACKOFF 0x0008 +#define AR9170_TX_MAC_BURST 0x0010 +#define AR9170_TX_MAC_AGGR 0x0020 + +/* encryption is a two-bit field */ +#define AR9170_TX_MAC_ENCR_NONE 0x0000 +#define AR9170_TX_MAC_ENCR_RC4 0x0040 +#define AR9170_TX_MAC_ENCR_CENC 0x0080 +#define AR9170_TX_MAC_ENCR_AES 0x00c0 + +#define AR9170_TX_MAC_MMIC 0x0100 +#define AR9170_TX_MAC_HW_DURATION 0x0200 +#define AR9170_TX_MAC_QOS_S 10 +#define AR9170_TX_MAC_QOS 0x0c00 +#define AR9170_TX_MAC_DISABLE_TXOP 0x1000 +#define AR9170_TX_MAC_TXOP_RIFS 0x2000 +#define AR9170_TX_MAC_IMM_BA 0x4000 + +/* either-or */ +#define AR9170_TX_PHY_MOD_CCK 0x00000000 +#define AR9170_TX_PHY_MOD_OFDM 0x00000001 +#define AR9170_TX_PHY_MOD_HT 0x00000002 + +/* depends on modulation */ +#define AR9170_TX_PHY_SHORT_PREAMBLE 0x00000004 +#define AR9170_TX_PHY_GREENFIELD 0x00000004 + +#define AR9170_TX_PHY_BW_S 3 +#define AR9170_TX_PHY_BW (3 << AR9170_TX_PHY_BW_SHIFT) +#define AR9170_TX_PHY_BW_20MHZ 0 +#define AR9170_TX_PHY_BW_40MHZ 2 +#define AR9170_TX_PHY_BW_40MHZ_DUP 3 + +#define AR9170_TX_PHY_TX_HEAVY_CLIP_S 6 +#define AR9170_TX_PHY_TX_HEAVY_CLIP (7 << \ + AR9170_TX_PHY_TX_HEAVY_CLIP_S) + +#define AR9170_TX_PHY_TX_PWR_S 9 +#define AR9170_TX_PHY_TX_PWR (0x3f << \ + AR9170_TX_PHY_TX_PWR_S) + +#define AR9170_TX_PHY_TXCHAIN_S 15 +#define AR9170_TX_PHY_TXCHAIN (7 << \ + AR9170_TX_PHY_TXCHAIN_S) +#define AR9170_TX_PHY_TXCHAIN_1 1 +/* use for cck, ofdm 6/9/12/18/24 and HT if capable */ +#define AR9170_TX_PHY_TXCHAIN_2 5 + +#define AR9170_TX_PHY_MCS_S 18 +#define AR9170_TX_PHY_MCS (0x7f << \ + AR9170_TX_PHY_MCS_S) + +#define AR9170_TX_PHY_RATE_CCK_1M 0x0 +#define AR9170_TX_PHY_RATE_CCK_2M 0x1 +#define AR9170_TX_PHY_RATE_CCK_5M 0x2 +#define AR9170_TX_PHY_RATE_CCK_11M 0x3 + +/* same as AR9170_RX_PHY_RATE */ +#define AR9170_TXRX_PHY_RATE_OFDM_6M 0xb +#define AR9170_TXRX_PHY_RATE_OFDM_9M 0xf +#define AR9170_TXRX_PHY_RATE_OFDM_12M 0xa +#define AR9170_TXRX_PHY_RATE_OFDM_18M 0xe +#define AR9170_TXRX_PHY_RATE_OFDM_24M 0x9 +#define AR9170_TXRX_PHY_RATE_OFDM_36M 0xd +#define AR9170_TXRX_PHY_RATE_OFDM_48M 0x8 +#define AR9170_TXRX_PHY_RATE_OFDM_54M 0xc + +#define AR9170_TXRX_PHY_RATE_HT_MCS0 0x0 +#define AR9170_TXRX_PHY_RATE_HT_MCS1 0x1 +#define AR9170_TXRX_PHY_RATE_HT_MCS2 0x2 +#define AR9170_TXRX_PHY_RATE_HT_MCS3 0x3 +#define AR9170_TXRX_PHY_RATE_HT_MCS4 0x4 +#define AR9170_TXRX_PHY_RATE_HT_MCS5 0x5 +#define AR9170_TXRX_PHY_RATE_HT_MCS6 0x6 +#define AR9170_TXRX_PHY_RATE_HT_MCS7 0x7 +#define AR9170_TXRX_PHY_RATE_HT_MCS8 0x8 +#define AR9170_TXRX_PHY_RATE_HT_MCS9 0x9 +#define AR9170_TXRX_PHY_RATE_HT_MCS10 0xa +#define AR9170_TXRX_PHY_RATE_HT_MCS11 0xb +#define AR9170_TXRX_PHY_RATE_HT_MCS12 0xc +#define AR9170_TXRX_PHY_RATE_HT_MCS13 0xd +#define AR9170_TXRX_PHY_RATE_HT_MCS14 0xe +#define AR9170_TXRX_PHY_RATE_HT_MCS15 0xf + +#define AR9170_TX_PHY_SHORT_GI 0x80000000 + +#ifdef __CARL9170FW__ +struct ar9170_tx_hw_mac_control { + union { + struct { + /* + * Beware of compiler bugs in all gcc pre 4.4! + */ + + u8 erp_prot:2; + u8 no_ack:1; + u8 backoff:1; + u8 burst:1; + u8 ampdu:1; + + u8 enc_mode:2; + + u8 hw_mmic:1; + u8 hw_duration:1; + + u8 qos_queue:2; + + u8 disable_txop:1; + u8 txop_rifs:1; + + u8 ba_end:1; + u8 probe:1; + } __packed; + + __le16 set; + } __packed; +} __packed; + +struct ar9170_tx_hw_phy_control { + union { + struct { + /* + * Beware of compiler bugs in all gcc pre 4.4! + */ + + u8 modulation:2; + u8 preamble:1; + u8 bandwidth:2; + u8:1; + u8 heavy_clip:3; + u8 tx_power:6; + u8 chains:3; + u8 mcs:7; + u8:6; + u8 short_gi:1; + } __packed; + + __le32 set; + } __packed; +} __packed; + +struct ar9170_tx_rate_info { + u8 tries:3; + u8 erp_prot:2; + u8 ampdu:1; + u8 free:2; /* free for use (e.g.:RIFS/TXOP/AMPDU) */ +} __packed; + +struct carl9170_tx_superdesc { + __le16 len; + u8 rix; + u8 cnt; + u8 cookie; + u8 ampdu_density:3; + u8 ampdu_factor:2; + u8 ampdu_commit_density:1; + u8 ampdu_commit_factor:1; + u8 ampdu_unused_bit:1; + u8 queue:2; + u8 reserved:1; + u8 vif_id:3; + u8 fill_in_tsf:1; + u8 cab:1; + u8 padding2; + struct ar9170_tx_rate_info ri[CARL9170_TX_MAX_RATES]; + struct ar9170_tx_hw_phy_control rr[CARL9170_TX_MAX_RETRY_RATES]; +} __packed; + +struct ar9170_tx_hwdesc { + __le16 length; + struct ar9170_tx_hw_mac_control mac; + struct ar9170_tx_hw_phy_control phy; +} __packed; + +struct ar9170_tx_frame { + struct ar9170_tx_hwdesc hdr; + + union { + struct ieee80211_hdr i3e; + u8 payload[0]; + } data; +} __packed; + +struct carl9170_tx_superframe { + struct carl9170_tx_superdesc s; + struct ar9170_tx_frame f; +} __packed; + +#endif /* __CARL9170FW__ */ + +struct _ar9170_tx_hwdesc { + __le16 length; + __le16 mac_control; + __le32 phy_control; +} __packed; + +#define CARL9170_TX_SUPER_AMPDU_DENSITY_S 0 +#define CARL9170_TX_SUPER_AMPDU_DENSITY 0x7 +#define CARL9170_TX_SUPER_AMPDU_FACTOR 0x18 +#define CARL9170_TX_SUPER_AMPDU_FACTOR_S 3 +#define CARL9170_TX_SUPER_AMPDU_COMMIT_DENSITY 0x20 +#define CARL9170_TX_SUPER_AMPDU_COMMIT_DENSITY_S 5 +#define CARL9170_TX_SUPER_AMPDU_COMMIT_FACTOR 0x40 +#define CARL9170_TX_SUPER_AMPDU_COMMIT_FACTOR_S 6 + +#define CARL9170_TX_SUPER_MISC_QUEUE 0x3 +#define CARL9170_TX_SUPER_MISC_QUEUE_S 0 +#define CARL9170_TX_SUPER_MISC_VIF_ID 0x38 +#define CARL9170_TX_SUPER_MISC_VIF_ID_S 3 +#define CARL9170_TX_SUPER_MISC_FILL_IN_TSF 0x40 +#define CARL9170_TX_SUPER_MISC_CAB 0x80 + +#define CARL9170_TX_SUPER_RI_TRIES 0x7 +#define CARL9170_TX_SUPER_RI_TRIES_S 0 +#define CARL9170_TX_SUPER_RI_ERP_PROT 0x18 +#define CARL9170_TX_SUPER_RI_ERP_PROT_S 3 +#define CARL9170_TX_SUPER_RI_AMPDU 0x20 +#define CARL9170_TX_SUPER_RI_AMPDU_S 5 + +struct _carl9170_tx_superdesc { + __le16 len; + u8 rix; + u8 cnt; + u8 cookie; + u8 ampdu_settings; + u8 misc; + u8 padding; + u8 ri[CARL9170_TX_MAX_RATES]; + __le32 rr[CARL9170_TX_MAX_RETRY_RATES]; +} __packed; + +struct _carl9170_tx_superframe { + struct _carl9170_tx_superdesc s; + struct _ar9170_tx_hwdesc f; + u8 frame_data[0]; +} __packed; + +#define CARL9170_TX_SUPERDESC_LEN 24 +#define AR9170_TX_HWDESC_LEN 8 +#define AR9170_TX_SUPERFRAME_LEN (CARL9170_TX_HWDESC_LEN + \ + AR9170_TX_SUPERDESC_LEN) + +struct ar9170_rx_head { + u8 plcp[12]; +} __packed; + +struct ar9170_rx_phystatus { + union { + struct { + u8 rssi_ant0, rssi_ant1, rssi_ant2, + rssi_ant0x, rssi_ant1x, rssi_ant2x, + rssi_combined; + } __packed; + u8 rssi[7]; + } __packed; + + u8 evm_stream0[6], evm_stream1[6]; + u8 phy_err; +} __packed; + +struct ar9170_rx_macstatus { + u8 SAidx, DAidx; + u8 error; + u8 status; +} __packed; + +struct ar9170_rx_frame_single { + struct ar9170_rx_head phy_head; + struct ieee80211_hdr i3e; + struct ar9170_rx_phystatus phy_tail; + struct ar9170_rx_macstatus macstatus; +} __packed; + +struct ar9170_rx_frame_head { + struct ar9170_rx_head phy_head; + struct ieee80211_hdr i3e; + struct ar9170_rx_macstatus macstatus; +} __packed; + +struct ar9170_rx_frame_middle { + struct ieee80211_hdr i3e; + struct ar9170_rx_macstatus macstatus; +} __packed; + +struct ar9170_rx_frame_tail { + struct ieee80211_hdr i3e; + struct ar9170_rx_phystatus phy_tail; + struct ar9170_rx_macstatus macstatus; +} __packed; + +struct ar9170_rx_frame { + union { + struct ar9170_rx_frame_single single; + struct ar9170_rx_frame_head head; + struct ar9170_rx_frame_middle middle; + struct ar9170_rx_frame_tail tail; + } __packed; +} __packed; + +static inline u8 ar9170_get_decrypt_type(struct ar9170_rx_macstatus *t) +{ + return (t->SAidx & 0xc0) >> 4 | + (t->DAidx & 0xc0) >> 6; +} + +enum ar9170_txq { + AR9170_TXQ_BE, + + AR9170_TXQ_VI, + AR9170_TXQ_VO, + AR9170_TXQ_BK, + + __AR9170_NUM_TXQ, +}; + +static const u8 ar9170_qmap[__AR9170_NUM_TXQ] = { 2, 1, 0, 3 }; + +#define AR9170_TXQ_DEPTH 32 + +#endif /* __CARL9170_SHARED_WLAN_H */ -- cgit v1.2.3 From 319da621d5c4e9bd8c34feeb200e864e87d91fe7 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Mon, 6 Sep 2010 01:08:38 +0200 Subject: carl9170: PHY/RF and MAC routines Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/mac.c | 604 +++++++++++ drivers/net/wireless/ath/carl9170/phy.c | 1805 +++++++++++++++++++++++++++++++ 2 files changed, 2409 insertions(+) create mode 100644 drivers/net/wireless/ath/carl9170/mac.c create mode 100644 drivers/net/wireless/ath/carl9170/phy.c (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/mac.c b/drivers/net/wireless/ath/carl9170/mac.c new file mode 100644 index 000000000000..2305bc27151c --- /dev/null +++ b/drivers/net/wireless/ath/carl9170/mac.c @@ -0,0 +1,604 @@ +/* + * Atheros CARL9170 driver + * + * MAC programming + * + * Copyright 2008, Johannes Berg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "carl9170.h" +#include "cmd.h" + +int carl9170_set_dyn_sifs_ack(struct ar9170 *ar) +{ + u32 val; + + if (conf_is_ht40(&ar->hw->conf)) + val = 0x010a; + else { + if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ) + val = 0x105; + else + val = 0x104; + } + + return carl9170_write_reg(ar, AR9170_MAC_REG_DYNAMIC_SIFS_ACK, val); +} + +int carl9170_set_rts_cts_rate(struct ar9170 *ar) +{ + u32 rts_rate, cts_rate; + + if (conf_is_ht(&ar->hw->conf)) { + /* 12 mbit OFDM */ + rts_rate = 0x1da; + cts_rate = 0x10a; + } else { + if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ) { + /* 11 mbit CCK */ + rts_rate = 033; + cts_rate = 003; + } else { + /* 6 mbit OFDM */ + rts_rate = 0x1bb; + cts_rate = 0x10b; + } + } + + return carl9170_write_reg(ar, AR9170_MAC_REG_RTS_CTS_RATE, + rts_rate | (cts_rate) << 16); +} + +int carl9170_set_slot_time(struct ar9170 *ar) +{ + struct ieee80211_vif *vif; + u32 slottime = 20; + + rcu_read_lock(); + vif = carl9170_get_main_vif(ar); + if (!vif) { + rcu_read_unlock(); + return 0; + } + + if ((ar->hw->conf.channel->band == IEEE80211_BAND_5GHZ) || + vif->bss_conf.use_short_slot) + slottime = 9; + + rcu_read_unlock(); + + return carl9170_write_reg(ar, AR9170_MAC_REG_SLOT_TIME, + slottime << 10); +} + +int carl9170_set_mac_rates(struct ar9170 *ar) +{ + struct ieee80211_vif *vif; + u32 basic, mandatory; + + rcu_read_lock(); + vif = carl9170_get_main_vif(ar); + + if (!vif) { + rcu_read_unlock(); + return 0; + } + + basic = (vif->bss_conf.basic_rates & 0xf); + basic |= (vif->bss_conf.basic_rates & 0xff0) << 4; + rcu_read_unlock(); + + if (ar->hw->conf.channel->band == IEEE80211_BAND_5GHZ) + mandatory = 0xff00; /* OFDM 6/9/12/18/24/36/48/54 */ + else + mandatory = 0xff0f; /* OFDM (6/9../54) + CCK (1/2/5.5/11) */ + + carl9170_regwrite_begin(ar); + carl9170_regwrite(AR9170_MAC_REG_BASIC_RATE, basic); + carl9170_regwrite(AR9170_MAC_REG_MANDATORY_RATE, mandatory); + carl9170_regwrite_finish(); + + return carl9170_regwrite_result(); +} + +int carl9170_set_qos(struct ar9170 *ar) +{ + carl9170_regwrite_begin(ar); + + carl9170_regwrite(AR9170_MAC_REG_AC0_CW, ar->edcf[0].cw_min | + (ar->edcf[0].cw_max << 16)); + carl9170_regwrite(AR9170_MAC_REG_AC1_CW, ar->edcf[1].cw_min | + (ar->edcf[1].cw_max << 16)); + carl9170_regwrite(AR9170_MAC_REG_AC2_CW, ar->edcf[2].cw_min | + (ar->edcf[2].cw_max << 16)); + carl9170_regwrite(AR9170_MAC_REG_AC3_CW, ar->edcf[3].cw_min | + (ar->edcf[3].cw_max << 16)); + carl9170_regwrite(AR9170_MAC_REG_AC4_CW, ar->edcf[4].cw_min | + (ar->edcf[4].cw_max << 16)); + + carl9170_regwrite(AR9170_MAC_REG_AC2_AC1_AC0_AIFS, + ((ar->edcf[0].aifs * 9 + 10)) | + ((ar->edcf[1].aifs * 9 + 10) << 12) | + ((ar->edcf[2].aifs * 9 + 10) << 24)); + carl9170_regwrite(AR9170_MAC_REG_AC4_AC3_AC2_AIFS, + ((ar->edcf[2].aifs * 9 + 10) >> 8) | + ((ar->edcf[3].aifs * 9 + 10) << 4) | + ((ar->edcf[4].aifs * 9 + 10) << 16)); + + carl9170_regwrite(AR9170_MAC_REG_AC1_AC0_TXOP, + ar->edcf[0].txop | ar->edcf[1].txop << 16); + carl9170_regwrite(AR9170_MAC_REG_AC3_AC2_TXOP, + ar->edcf[2].txop | ar->edcf[3].txop << 16 | + ar->edcf[4].txop << 24); + + carl9170_regwrite_finish(); + + return carl9170_regwrite_result(); +} + +int carl9170_init_mac(struct ar9170 *ar) +{ + carl9170_regwrite_begin(ar); + + /* switch MAC to OTUS interface */ + carl9170_regwrite(0x1c3600, 0x3); + + carl9170_regwrite(AR9170_MAC_REG_ACK_EXTENSION, 0x40); + + carl9170_regwrite(AR9170_MAC_REG_RETRY_MAX, 0x0); + + carl9170_regwrite(AR9170_MAC_REG_FRAMETYPE_FILTER, + AR9170_MAC_FTF_MONITOR); + + /* enable MMIC */ + carl9170_regwrite(AR9170_MAC_REG_SNIFFER, + AR9170_MAC_SNIFFER_DEFAULTS); + + carl9170_regwrite(AR9170_MAC_REG_RX_THRESHOLD, 0xc1f80); + + carl9170_regwrite(AR9170_MAC_REG_RX_PE_DELAY, 0x70); + carl9170_regwrite(AR9170_MAC_REG_EIFS_AND_SIFS, 0xa144000); + carl9170_regwrite(AR9170_MAC_REG_SLOT_TIME, 9 << 10); + + /* CF-END & CF-ACK rate => 24M OFDM */ + carl9170_regwrite(AR9170_MAC_REG_TID_CFACK_CFEND_RATE, 0x59900000); + + /* NAV protects ACK only (in TXOP) */ + carl9170_regwrite(AR9170_MAC_REG_TXOP_DURATION, 0x201); + + /* Set Beacon PHY CTRL's TPC to 0x7, TA1=1 */ + /* OTUS set AM to 0x1 */ + carl9170_regwrite(AR9170_MAC_REG_BCN_HT1, 0x8000170); + + carl9170_regwrite(AR9170_MAC_REG_BACKOFF_PROTECT, 0x105); + + /* Aggregation MAX number and timeout */ + carl9170_regwrite(AR9170_MAC_REG_AMPDU_FACTOR, 0xa); + carl9170_regwrite(AR9170_MAC_REG_AMPDU_DENSITY, 0x140a00); + + carl9170_regwrite(AR9170_MAC_REG_FRAMETYPE_FILTER, + AR9170_MAC_FTF_DEFAULTS); + + carl9170_regwrite(AR9170_MAC_REG_RX_CONTROL, + AR9170_MAC_RX_CTRL_DEAGG | + AR9170_MAC_RX_CTRL_SHORT_FILTER); + + /* rate sets */ + carl9170_regwrite(AR9170_MAC_REG_BASIC_RATE, 0x150f); + carl9170_regwrite(AR9170_MAC_REG_MANDATORY_RATE, 0x150f); + carl9170_regwrite(AR9170_MAC_REG_RTS_CTS_RATE, 0x0030033); + + /* MIMO response control */ + carl9170_regwrite(AR9170_MAC_REG_ACK_TPC, 0x4003c1e); + + carl9170_regwrite(AR9170_MAC_REG_AMPDU_RX_THRESH, 0xffff); + + /* set PHY register read timeout (??) */ + carl9170_regwrite(AR9170_MAC_REG_MISC_680, 0xf00008); + + /* Disable Rx TimeOut, workaround for BB. */ + carl9170_regwrite(AR9170_MAC_REG_RX_TIMEOUT, 0x0); + + /* Set WLAN DMA interrupt mode: generate int per packet */ + carl9170_regwrite(AR9170_MAC_REG_TXRX_MPI, 0x110011); + + carl9170_regwrite(AR9170_MAC_REG_FCS_SELECT, + AR9170_MAC_FCS_FIFO_PROT); + + /* Disables the CF_END frame, undocumented register */ + carl9170_regwrite(AR9170_MAC_REG_TXOP_NOT_ENOUGH_IND, + 0x141e0f48); + + /* reset group hash table */ + carl9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_L, 0xffffffff); + carl9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_H, 0xffffffff); + + /* disable PRETBTT interrupt */ + carl9170_regwrite(AR9170_MAC_REG_PRETBTT, 0x0); + carl9170_regwrite(AR9170_MAC_REG_BCN_PERIOD, 0x0); + + carl9170_regwrite_finish(); + + return carl9170_regwrite_result(); +} + +static int carl9170_set_mac_reg(struct ar9170 *ar, + const u32 reg, const u8 *mac) +{ + static const u8 zero[ETH_ALEN] = { 0 }; + + if (!mac) + mac = zero; + + carl9170_regwrite_begin(ar); + + carl9170_regwrite(reg, get_unaligned_le32(mac)); + carl9170_regwrite(reg + 4, get_unaligned_le16(mac + 4)); + + carl9170_regwrite_finish(); + + return carl9170_regwrite_result(); +} + +int carl9170_mod_virtual_mac(struct ar9170 *ar, const unsigned int id, + const u8 *mac) +{ + if (WARN_ON(id >= ar->fw.vif_num)) + return -EINVAL; + + return carl9170_set_mac_reg(ar, + AR9170_MAC_REG_ACK_TABLE + (id - 1) * 8, mac); +} + +int carl9170_update_multicast(struct ar9170 *ar, const u64 mc_hash) +{ + int err; + + carl9170_regwrite_begin(ar); + carl9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_H, mc_hash >> 32); + carl9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_L, mc_hash); + carl9170_regwrite_finish(); + err = carl9170_regwrite_result(); + if (err) + return err; + + ar->cur_mc_hash = mc_hash; + return 0; +} + +int carl9170_set_operating_mode(struct ar9170 *ar) +{ + struct ieee80211_vif *vif; + struct ath_common *common = &ar->common; + u8 *mac_addr, *bssid; + u32 cam_mode = AR9170_MAC_CAM_DEFAULTS; + u32 enc_mode = AR9170_MAC_ENCRYPTION_DEFAULTS; + u32 rx_ctrl = AR9170_MAC_RX_CTRL_DEAGG | + AR9170_MAC_RX_CTRL_SHORT_FILTER; + u32 sniffer = AR9170_MAC_SNIFFER_DEFAULTS; + int err = 0; + + rcu_read_lock(); + vif = carl9170_get_main_vif(ar); + + if (vif) { + mac_addr = common->macaddr; + bssid = common->curbssid; + + switch (vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_ADHOC: + cam_mode |= AR9170_MAC_CAM_IBSS; + break; + case NL80211_IFTYPE_AP: + cam_mode |= AR9170_MAC_CAM_AP; + + /* iwlagn 802.11n STA Workaround */ + rx_ctrl |= AR9170_MAC_RX_CTRL_PASS_TO_HOST; + break; + case NL80211_IFTYPE_WDS: + cam_mode |= AR9170_MAC_CAM_AP_WDS; + rx_ctrl |= AR9170_MAC_RX_CTRL_PASS_TO_HOST; + break; + case NL80211_IFTYPE_STATION: + cam_mode |= AR9170_MAC_CAM_STA; + rx_ctrl |= AR9170_MAC_RX_CTRL_PASS_TO_HOST; + break; + default: + WARN(1, "Unsupported operation mode %x\n", vif->type); + err = -EOPNOTSUPP; + break; + } + } else { + mac_addr = NULL; + bssid = NULL; + } + rcu_read_unlock(); + + if (err) + return err; + + if (ar->rx_software_decryption) + enc_mode |= AR9170_MAC_ENCRYPTION_RX_SOFTWARE; + + if (ar->sniffer_enabled) { + rx_ctrl |= AR9170_MAC_RX_CTRL_ACK_IN_SNIFFER; + sniffer |= AR9170_MAC_SNIFFER_ENABLE_PROMISC; + enc_mode |= AR9170_MAC_ENCRYPTION_RX_SOFTWARE; + } + + err = carl9170_set_mac_reg(ar, AR9170_MAC_REG_MAC_ADDR_L, mac_addr); + if (err) + return err; + + err = carl9170_set_mac_reg(ar, AR9170_MAC_REG_BSSID_L, bssid); + if (err) + return err; + + carl9170_regwrite_begin(ar); + carl9170_regwrite(AR9170_MAC_REG_SNIFFER, sniffer); + carl9170_regwrite(AR9170_MAC_REG_CAM_MODE, cam_mode); + carl9170_regwrite(AR9170_MAC_REG_ENCRYPTION, enc_mode); + carl9170_regwrite(AR9170_MAC_REG_RX_CONTROL, rx_ctrl); + carl9170_regwrite_finish(); + + return carl9170_regwrite_result(); +} + +int carl9170_set_hwretry_limit(struct ar9170 *ar, const unsigned int max_retry) +{ + u32 tmp = min_t(u32, 0x33333, max_retry * 0x11111); + + return carl9170_write_reg(ar, AR9170_MAC_REG_RETRY_MAX, tmp); +} + +int carl9170_set_beacon_timers(struct ar9170 *ar) +{ + struct ieee80211_vif *vif; + u32 v = 0; + u32 pretbtt = 0; + + rcu_read_lock(); + vif = carl9170_get_main_vif(ar); + + if (vif) { + struct carl9170_vif_info *mvif; + mvif = (void *) vif->drv_priv; + + if (mvif->enable_beacon && !WARN_ON(!ar->beacon_enabled)) { + ar->global_beacon_int = vif->bss_conf.beacon_int / + ar->beacon_enabled; + + SET_VAL(AR9170_MAC_BCN_DTIM, v, + vif->bss_conf.dtim_period); + + switch (vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_ADHOC: + v |= AR9170_MAC_BCN_IBSS_MODE; + break; + case NL80211_IFTYPE_AP: + v |= AR9170_MAC_BCN_AP_MODE; + break; + default: + WARN_ON_ONCE(1); + break; + } + } else if (vif->type == NL80211_IFTYPE_STATION) { + ar->global_beacon_int = vif->bss_conf.beacon_int; + + SET_VAL(AR9170_MAC_BCN_DTIM, v, + ar->hw->conf.ps_dtim_period); + + v |= AR9170_MAC_BCN_STA_PS | + AR9170_MAC_BCN_PWR_MGT; + } + + if (ar->global_beacon_int) { + if (ar->global_beacon_int < 15) { + rcu_read_unlock(); + return -ERANGE; + } + + ar->global_pretbtt = ar->global_beacon_int - + CARL9170_PRETBTT_KUS; + } else { + ar->global_pretbtt = 0; + } + } else { + ar->global_beacon_int = 0; + ar->global_pretbtt = 0; + } + + rcu_read_unlock(); + + SET_VAL(AR9170_MAC_BCN_PERIOD, v, ar->global_beacon_int); + SET_VAL(AR9170_MAC_PRETBTT, pretbtt, ar->global_pretbtt); + SET_VAL(AR9170_MAC_PRETBTT2, pretbtt, ar->global_pretbtt); + + carl9170_regwrite_begin(ar); + carl9170_regwrite(AR9170_MAC_REG_PRETBTT, pretbtt); + carl9170_regwrite(AR9170_MAC_REG_BCN_PERIOD, v); + carl9170_regwrite_finish(); + return carl9170_regwrite_result(); +} + +int carl9170_update_beacon(struct ar9170 *ar, const bool submit) +{ + struct sk_buff *skb; + struct carl9170_vif_info *cvif; + __le32 *data, *old = NULL; + u32 word, off, addr, len; + int i = 0, err = 0; + + rcu_read_lock(); + cvif = rcu_dereference(ar->beacon_iter); +retry: + if (ar->vifs == 0 || !cvif) + goto out_unlock; + + list_for_each_entry_continue_rcu(cvif, &ar->vif_list, list) { + if (cvif->active && cvif->enable_beacon) + goto found; + } + + if (!ar->beacon_enabled || i++) + goto out_unlock; + + goto retry; + +found: + rcu_assign_pointer(ar->beacon_iter, cvif); + + skb = ieee80211_beacon_get_tim(ar->hw, carl9170_get_vif(cvif), + NULL, NULL); + + if (!skb) { + err = -ENOMEM; + goto out_unlock; + } + + spin_lock_bh(&ar->beacon_lock); + data = (__le32 *)skb->data; + if (cvif->beacon) + old = (__le32 *)cvif->beacon->data; + + off = cvif->id * AR9170_MAC_BCN_LENGTH_MAX; + addr = ar->fw.beacon_addr + off; + len = roundup(skb->len + FCS_LEN, 4); + + if ((off + len) > ar->fw.beacon_max_len) { + if (net_ratelimit()) { + wiphy_err(ar->hw->wiphy, "beacon does not " + "fit into device memory!\n"); + } + + spin_unlock_bh(&ar->beacon_lock); + dev_kfree_skb_any(skb); + err = -EINVAL; + goto out_unlock; + } + + if (len > AR9170_MAC_BCN_LENGTH_MAX) { + if (net_ratelimit()) { + wiphy_err(ar->hw->wiphy, "no support for beacons " + "bigger than %d (yours:%d).\n", + AR9170_MAC_BCN_LENGTH_MAX, len); + } + + spin_unlock_bh(&ar->beacon_lock); + dev_kfree_skb_any(skb); + err = -EMSGSIZE; + goto out_unlock; + } + + carl9170_async_regwrite_begin(ar); + + /* XXX: use skb->cb info */ + if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ) { + carl9170_async_regwrite(AR9170_MAC_REG_BCN_PLCP, + ((skb->len + FCS_LEN) << (3 + 16)) + 0x0400); + } else { + carl9170_async_regwrite(AR9170_MAC_REG_BCN_PLCP, + ((skb->len + FCS_LEN) << 16) + 0x001b); + } + + for (i = 0; i < DIV_ROUND_UP(skb->len, 4); i++) { + /* + * XXX: This accesses beyond skb data for up + * to the last 3 bytes!! + */ + + if (old && (data[i] == old[i])) + continue; + + word = le32_to_cpu(data[i]); + carl9170_async_regwrite(addr + 4 * i, word); + } + carl9170_async_regwrite_finish(); + + dev_kfree_skb_any(cvif->beacon); + cvif->beacon = NULL; + + err = carl9170_async_regwrite_result(); + if (!err) + cvif->beacon = skb; + spin_unlock_bh(&ar->beacon_lock); + if (err) + goto out_unlock; + + if (submit) { + err = carl9170_bcn_ctrl(ar, cvif->id, + CARL9170_BCN_CTRL_CAB_TRIGGER, + addr, skb->len + FCS_LEN); + + if (err) + goto out_unlock; + } +out_unlock: + rcu_read_unlock(); + return err; +} + +int carl9170_upload_key(struct ar9170 *ar, const u8 id, const u8 *mac, + const u8 ktype, const u8 keyidx, const u8 *keydata, + const int keylen) +{ + struct carl9170_set_key_cmd key = { }; + static const u8 bcast[ETH_ALEN] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + mac = mac ? : bcast; + + key.user = cpu_to_le16(id); + key.keyId = cpu_to_le16(keyidx); + key.type = cpu_to_le16(ktype); + memcpy(&key.macAddr, mac, ETH_ALEN); + if (keydata) + memcpy(&key.key, keydata, keylen); + + return carl9170_exec_cmd(ar, CARL9170_CMD_EKEY, + sizeof(key), (u8 *)&key, 0, NULL); +} + +int carl9170_disable_key(struct ar9170 *ar, const u8 id) +{ + struct carl9170_disable_key_cmd key = { }; + + key.user = cpu_to_le16(id); + + return carl9170_exec_cmd(ar, CARL9170_CMD_DKEY, + sizeof(key), (u8 *)&key, 0, NULL); +} diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c new file mode 100644 index 000000000000..47709c13d964 --- /dev/null +++ b/drivers/net/wireless/ath/carl9170/phy.c @@ -0,0 +1,1805 @@ +/* + * Atheros CARL9170 driver + * + * PHY and RF code + * + * Copyright 2008, Johannes Berg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include "carl9170.h" +#include "cmd.h" +#include "phy.h" + +static int carl9170_init_power_cal(struct ar9170 *ar) +{ + carl9170_regwrite_begin(ar); + + carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE_MAX, 0x7f); + carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE1, 0x3f3f3f3f); + carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE2, 0x3f3f3f3f); + carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE3, 0x3f3f3f3f); + carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE4, 0x3f3f3f3f); + carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE5, 0x3f3f3f3f); + carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE6, 0x3f3f3f3f); + carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE7, 0x3f3f3f3f); + carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE8, 0x3f3f3f3f); + carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE9, 0x3f3f3f3f); + + carl9170_regwrite_finish(); + return carl9170_regwrite_result(); +} + +struct carl9170_phy_init { + u32 reg, _5ghz_20, _5ghz_40, _2ghz_40, _2ghz_20; +}; + +static struct carl9170_phy_init ar5416_phy_init[] = { + { 0x1c5800, 0x00000007, 0x00000007, 0x00000007, 0x00000007, }, + { 0x1c5804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, }, + { 0x1c5808, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c580c, 0xad848e19, 0xad848e19, 0xad848e19, 0xad848e19, }, + { 0x1c5810, 0x7d14e000, 0x7d14e000, 0x7d14e000, 0x7d14e000, }, + { 0x1c5814, 0x9c0a9f6b, 0x9c0a9f6b, 0x9c0a9f6b, 0x9c0a9f6b, }, + { 0x1c5818, 0x00000090, 0x00000090, 0x00000090, 0x00000090, }, + { 0x1c581c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, }, + { 0x1c5824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, }, + { 0x1c5828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, }, + { 0x1c582c, 0x0000a000, 0x0000a000, 0x0000a000, 0x0000a000, }, + { 0x1c5830, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, }, + { 0x1c5838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, }, + { 0x1c583c, 0x00200400, 0x00200400, 0x00200400, 0x00200400, }, + { 0x1c5840, 0x206a002e, 0x206a002e, 0x206a002e, 0x206a002e, }, + { 0x1c5844, 0x1372161e, 0x13721c1e, 0x13721c24, 0x137216a4, }, + { 0x1c5848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, }, + { 0x1c584c, 0x1284233c, 0x1284233c, 0x1284233c, 0x1284233c, }, + { 0x1c5850, 0x6c48b4e4, 0x6c48b4e4, 0x6c48b0e4, 0x6c48b0e4, }, + { 0x1c5854, 0x00000859, 0x00000859, 0x00000859, 0x00000859, }, + { 0x1c5858, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, }, + { 0x1c585c, 0x31395c5e, 0x31395c5e, 0x31395c5e, 0x31395c5e, }, + { 0x1c5860, 0x0004dd10, 0x0004dd10, 0x0004dd20, 0x0004dd20, }, + { 0x1c5868, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190, }, + { 0x1c586c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, }, + { 0x1c5900, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5904, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5908, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c590c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5914, 0x000007d0, 0x000007d0, 0x00000898, 0x00000898, }, + { 0x1c5918, 0x00000118, 0x00000230, 0x00000268, 0x00000134, }, + { 0x1c591c, 0x10000fff, 0x10000fff, 0x10000fff, 0x10000fff, }, + { 0x1c5920, 0x0510081c, 0x0510081c, 0x0510001c, 0x0510001c, }, + { 0x1c5924, 0xd0058a15, 0xd0058a15, 0xd0058a15, 0xd0058a15, }, + { 0x1c5928, 0x00000001, 0x00000001, 0x00000001, 0x00000001, }, + { 0x1c592c, 0x00000004, 0x00000004, 0x00000004, 0x00000004, }, + { 0x1c5934, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c5938, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c593c, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, }, + { 0x1c5944, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020, }, + { 0x1c5948, 0x9280b212, 0x9280b212, 0x9280b212, 0x9280b212, }, + { 0x1c594c, 0x00020028, 0x00020028, 0x00020028, 0x00020028, }, + { 0x1c5954, 0x5d50e188, 0x5d50e188, 0x5d50e188, 0x5d50e188, }, + { 0x1c5958, 0x00081fff, 0x00081fff, 0x00081fff, 0x00081fff, }, + { 0x1c5960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, }, + { 0x1c5964, 0x00001120, 0x00001120, 0x00001120, 0x00001120, }, + { 0x1c5970, 0x190fb515, 0x190fb515, 0x190fb515, 0x190fb515, }, + { 0x1c5974, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5978, 0x00000001, 0x00000001, 0x00000001, 0x00000001, }, + { 0x1c597c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5980, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5984, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5988, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c598c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5990, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5994, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5998, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c599c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c59a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c59a4, 0x00000007, 0x00000007, 0x00000007, 0x00000007, }, + { 0x1c59a8, 0x001fff00, 0x001fff00, 0x001fff00, 0x001fff00, }, + { 0x1c59ac, 0x006f00c4, 0x006f00c4, 0x006f00c4, 0x006f00c4, }, + { 0x1c59b0, 0x03051000, 0x03051000, 0x03051000, 0x03051000, }, + { 0x1c59b4, 0x00000820, 0x00000820, 0x00000820, 0x00000820, }, + { 0x1c59c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be, }, + { 0x1c59c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, }, + { 0x1c59c8, 0x60f6532c, 0x60f6532c, 0x60f6532c, 0x60f6532c, }, + { 0x1c59cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, }, + { 0x1c59d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, }, + { 0x1c59d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c59d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c59dc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c59e0, 0x00000200, 0x00000200, 0x00000200, 0x00000200, }, + { 0x1c59e4, 0x64646464, 0x64646464, 0x64646464, 0x64646464, }, + { 0x1c59e8, 0x3c787878, 0x3c787878, 0x3c787878, 0x3c787878, }, + { 0x1c59ec, 0x000000aa, 0x000000aa, 0x000000aa, 0x000000aa, }, + { 0x1c59f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c59fc, 0x00001042, 0x00001042, 0x00001042, 0x00001042, }, + { 0x1c5a00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5a04, 0x00000040, 0x00000040, 0x00000040, 0x00000040, }, + { 0x1c5a08, 0x00000080, 0x00000080, 0x00000080, 0x00000080, }, + { 0x1c5a0c, 0x000001a1, 0x000001a1, 0x00000141, 0x00000141, }, + { 0x1c5a10, 0x000001e1, 0x000001e1, 0x00000181, 0x00000181, }, + { 0x1c5a14, 0x00000021, 0x00000021, 0x000001c1, 0x000001c1, }, + { 0x1c5a18, 0x00000061, 0x00000061, 0x00000001, 0x00000001, }, + { 0x1c5a1c, 0x00000168, 0x00000168, 0x00000041, 0x00000041, }, + { 0x1c5a20, 0x000001a8, 0x000001a8, 0x000001a8, 0x000001a8, }, + { 0x1c5a24, 0x000001e8, 0x000001e8, 0x000001e8, 0x000001e8, }, + { 0x1c5a28, 0x00000028, 0x00000028, 0x00000028, 0x00000028, }, + { 0x1c5a2c, 0x00000068, 0x00000068, 0x00000068, 0x00000068, }, + { 0x1c5a30, 0x00000189, 0x00000189, 0x000000a8, 0x000000a8, }, + { 0x1c5a34, 0x000001c9, 0x000001c9, 0x00000169, 0x00000169, }, + { 0x1c5a38, 0x00000009, 0x00000009, 0x000001a9, 0x000001a9, }, + { 0x1c5a3c, 0x00000049, 0x00000049, 0x000001e9, 0x000001e9, }, + { 0x1c5a40, 0x00000089, 0x00000089, 0x00000029, 0x00000029, }, + { 0x1c5a44, 0x00000170, 0x00000170, 0x00000069, 0x00000069, }, + { 0x1c5a48, 0x000001b0, 0x000001b0, 0x00000190, 0x00000190, }, + { 0x1c5a4c, 0x000001f0, 0x000001f0, 0x000001d0, 0x000001d0, }, + { 0x1c5a50, 0x00000030, 0x00000030, 0x00000010, 0x00000010, }, + { 0x1c5a54, 0x00000070, 0x00000070, 0x00000050, 0x00000050, }, + { 0x1c5a58, 0x00000191, 0x00000191, 0x00000090, 0x00000090, }, + { 0x1c5a5c, 0x000001d1, 0x000001d1, 0x00000151, 0x00000151, }, + { 0x1c5a60, 0x00000011, 0x00000011, 0x00000191, 0x00000191, }, + { 0x1c5a64, 0x00000051, 0x00000051, 0x000001d1, 0x000001d1, }, + { 0x1c5a68, 0x00000091, 0x00000091, 0x00000011, 0x00000011, }, + { 0x1c5a6c, 0x000001b8, 0x000001b8, 0x00000051, 0x00000051, }, + { 0x1c5a70, 0x000001f8, 0x000001f8, 0x00000198, 0x00000198, }, + { 0x1c5a74, 0x00000038, 0x00000038, 0x000001d8, 0x000001d8, }, + { 0x1c5a78, 0x00000078, 0x00000078, 0x00000018, 0x00000018, }, + { 0x1c5a7c, 0x00000199, 0x00000199, 0x00000058, 0x00000058, }, + { 0x1c5a80, 0x000001d9, 0x000001d9, 0x00000098, 0x00000098, }, + { 0x1c5a84, 0x00000019, 0x00000019, 0x00000159, 0x00000159, }, + { 0x1c5a88, 0x00000059, 0x00000059, 0x00000199, 0x00000199, }, + { 0x1c5a8c, 0x00000099, 0x00000099, 0x000001d9, 0x000001d9, }, + { 0x1c5a90, 0x000000d9, 0x000000d9, 0x00000019, 0x00000019, }, + { 0x1c5a94, 0x000000f9, 0x000000f9, 0x00000059, 0x00000059, }, + { 0x1c5a98, 0x000000f9, 0x000000f9, 0x00000099, 0x00000099, }, + { 0x1c5a9c, 0x000000f9, 0x000000f9, 0x000000d9, 0x000000d9, }, + { 0x1c5aa0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5aa4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5aa8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5aac, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ab0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ab4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ab8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5abc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ac0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ac4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ac8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5acc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ad0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ad4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ad8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5adc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ae0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ae4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ae8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5aec, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5af0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5af4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5af8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5afc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5b00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5b04, 0x00000001, 0x00000001, 0x00000001, 0x00000001, }, + { 0x1c5b08, 0x00000002, 0x00000002, 0x00000002, 0x00000002, }, + { 0x1c5b0c, 0x00000003, 0x00000003, 0x00000003, 0x00000003, }, + { 0x1c5b10, 0x00000004, 0x00000004, 0x00000004, 0x00000004, }, + { 0x1c5b14, 0x00000005, 0x00000005, 0x00000005, 0x00000005, }, + { 0x1c5b18, 0x00000008, 0x00000008, 0x00000008, 0x00000008, }, + { 0x1c5b1c, 0x00000009, 0x00000009, 0x00000009, 0x00000009, }, + { 0x1c5b20, 0x0000000a, 0x0000000a, 0x0000000a, 0x0000000a, }, + { 0x1c5b24, 0x0000000b, 0x0000000b, 0x0000000b, 0x0000000b, }, + { 0x1c5b28, 0x0000000c, 0x0000000c, 0x0000000c, 0x0000000c, }, + { 0x1c5b2c, 0x0000000d, 0x0000000d, 0x0000000d, 0x0000000d, }, + { 0x1c5b30, 0x00000010, 0x00000010, 0x00000010, 0x00000010, }, + { 0x1c5b34, 0x00000011, 0x00000011, 0x00000011, 0x00000011, }, + { 0x1c5b38, 0x00000012, 0x00000012, 0x00000012, 0x00000012, }, + { 0x1c5b3c, 0x00000013, 0x00000013, 0x00000013, 0x00000013, }, + { 0x1c5b40, 0x00000014, 0x00000014, 0x00000014, 0x00000014, }, + { 0x1c5b44, 0x00000015, 0x00000015, 0x00000015, 0x00000015, }, + { 0x1c5b48, 0x00000018, 0x00000018, 0x00000018, 0x00000018, }, + { 0x1c5b4c, 0x00000019, 0x00000019, 0x00000019, 0x00000019, }, + { 0x1c5b50, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, }, + { 0x1c5b54, 0x0000001b, 0x0000001b, 0x0000001b, 0x0000001b, }, + { 0x1c5b58, 0x0000001c, 0x0000001c, 0x0000001c, 0x0000001c, }, + { 0x1c5b5c, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, }, + { 0x1c5b60, 0x00000020, 0x00000020, 0x00000020, 0x00000020, }, + { 0x1c5b64, 0x00000021, 0x00000021, 0x00000021, 0x00000021, }, + { 0x1c5b68, 0x00000022, 0x00000022, 0x00000022, 0x00000022, }, + { 0x1c5b6c, 0x00000023, 0x00000023, 0x00000023, 0x00000023, }, + { 0x1c5b70, 0x00000024, 0x00000024, 0x00000024, 0x00000024, }, + { 0x1c5b74, 0x00000025, 0x00000025, 0x00000025, 0x00000025, }, + { 0x1c5b78, 0x00000028, 0x00000028, 0x00000028, 0x00000028, }, + { 0x1c5b7c, 0x00000029, 0x00000029, 0x00000029, 0x00000029, }, + { 0x1c5b80, 0x0000002a, 0x0000002a, 0x0000002a, 0x0000002a, }, + { 0x1c5b84, 0x0000002b, 0x0000002b, 0x0000002b, 0x0000002b, }, + { 0x1c5b88, 0x0000002c, 0x0000002c, 0x0000002c, 0x0000002c, }, + { 0x1c5b8c, 0x0000002d, 0x0000002d, 0x0000002d, 0x0000002d, }, + { 0x1c5b90, 0x00000030, 0x00000030, 0x00000030, 0x00000030, }, + { 0x1c5b94, 0x00000031, 0x00000031, 0x00000031, 0x00000031, }, + { 0x1c5b98, 0x00000032, 0x00000032, 0x00000032, 0x00000032, }, + { 0x1c5b9c, 0x00000033, 0x00000033, 0x00000033, 0x00000033, }, + { 0x1c5ba0, 0x00000034, 0x00000034, 0x00000034, 0x00000034, }, + { 0x1c5ba4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5ba8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bac, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bb0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bb4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bb8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bbc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bc0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bc4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bc8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bcc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bd0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bd4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bd8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bdc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5be0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5be4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5be8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bec, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bf0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bf4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bf8, 0x00000010, 0x00000010, 0x00000010, 0x00000010, }, + { 0x1c5bfc, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, }, + { 0x1c5c00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c0c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c10, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c14, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c18, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c1c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c24, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c28, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c2c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c30, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c34, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c38, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c3c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5cf0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5cf4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5cf8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5cfc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6200, 0x00000008, 0x00000008, 0x0000000e, 0x0000000e, }, + { 0x1c6204, 0x00000440, 0x00000440, 0x00000440, 0x00000440, }, + { 0x1c6208, 0xd6be4788, 0xd6be4788, 0xd03e4788, 0xd03e4788, }, + { 0x1c620c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, }, + { 0x1c6210, 0x40806333, 0x40806333, 0x40806333, 0x40806333, }, + { 0x1c6214, 0x00106c10, 0x00106c10, 0x00106c10, 0x00106c10, }, + { 0x1c6218, 0x009c4060, 0x009c4060, 0x009c4060, 0x009c4060, }, + { 0x1c621c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, }, + { 0x1c6220, 0x018830c6, 0x018830c6, 0x018830c6, 0x018830c6, }, + { 0x1c6224, 0x00000400, 0x00000400, 0x00000400, 0x00000400, }, + { 0x1c6228, 0x000009b5, 0x000009b5, 0x000009b5, 0x000009b5, }, + { 0x1c622c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6230, 0x00000108, 0x00000210, 0x00000210, 0x00000108, }, + { 0x1c6234, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c6238, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c623c, 0x13c889af, 0x13c889af, 0x13c889af, 0x13c889af, }, + { 0x1c6240, 0x38490a20, 0x38490a20, 0x38490a20, 0x38490a20, }, + { 0x1c6244, 0x00007bb6, 0x00007bb6, 0x00007bb6, 0x00007bb6, }, + { 0x1c6248, 0x0fff3ffc, 0x0fff3ffc, 0x0fff3ffc, 0x0fff3ffc, }, + { 0x1c624c, 0x00000001, 0x00000001, 0x00000001, 0x00000001, }, + { 0x1c6250, 0x0000a000, 0x0000a000, 0x0000a000, 0x0000a000, }, + { 0x1c6254, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6258, 0x0cc75380, 0x0cc75380, 0x0cc75380, 0x0cc75380, }, + { 0x1c625c, 0x0f0f0f01, 0x0f0f0f01, 0x0f0f0f01, 0x0f0f0f01, }, + { 0x1c6260, 0xdfa91f01, 0xdfa91f01, 0xdfa91f01, 0xdfa91f01, }, + { 0x1c6264, 0x00418a11, 0x00418a11, 0x00418a11, 0x00418a11, }, + { 0x1c6268, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c626c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, }, + { 0x1c6274, 0x0a1a9caa, 0x0a1a9caa, 0x0a1a7caa, 0x0a1a7caa, }, + { 0x1c6278, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, }, + { 0x1c627c, 0x051701ce, 0x051701ce, 0x051701ce, 0x051701ce, }, + { 0x1c6300, 0x18010000, 0x18010000, 0x18010000, 0x18010000, }, + { 0x1c6304, 0x30032602, 0x30032602, 0x2e032402, 0x2e032402, }, + { 0x1c6308, 0x48073e06, 0x48073e06, 0x4a0a3c06, 0x4a0a3c06, }, + { 0x1c630c, 0x560b4c0a, 0x560b4c0a, 0x621a540b, 0x621a540b, }, + { 0x1c6310, 0x641a600f, 0x641a600f, 0x764f6c1b, 0x764f6c1b, }, + { 0x1c6314, 0x7a4f6e1b, 0x7a4f6e1b, 0x845b7a5a, 0x845b7a5a, }, + { 0x1c6318, 0x8c5b7e5a, 0x8c5b7e5a, 0x950f8ccf, 0x950f8ccf, }, + { 0x1c631c, 0x9d0f96cf, 0x9d0f96cf, 0xa5cf9b4f, 0xa5cf9b4f, }, + { 0x1c6320, 0xb51fa69f, 0xb51fa69f, 0xbddfaf1f, 0xbddfaf1f, }, + { 0x1c6324, 0xcb3fbd07, 0xcb3fbcbf, 0xd1ffc93f, 0xd1ffc93f, }, + { 0x1c6328, 0x0000d7bf, 0x0000d7bf, 0x00000000, 0x00000000, }, + { 0x1c632c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6330, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6334, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6338, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c633c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6340, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6344, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6348, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, }, + { 0x1c634c, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, }, + { 0x1c6350, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, }, + { 0x1c6354, 0x0003ffff, 0x0003ffff, 0x0003ffff, 0x0003ffff, }, + { 0x1c6358, 0x79a8aa1f, 0x79a8aa1f, 0x79a8aa1f, 0x79a8aa1f, }, + { 0x1c6388, 0x08000000, 0x08000000, 0x08000000, 0x08000000, }, + { 0x1c638c, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c6390, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c6394, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, }, + { 0x1c6398, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce, }, + { 0x1c639c, 0x00000007, 0x00000007, 0x00000007, 0x00000007, }, + { 0x1c63a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63a4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63a8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63ac, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63b0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63b4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63b8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63bc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63c0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63c4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63c8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63cc, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c63d0, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c63d4, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c63d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63dc, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, }, + { 0x1c63e0, 0x000000c0, 0x000000c0, 0x000000c0, 0x000000c0, }, + { 0x1c6848, 0x00180a65, 0x00180a65, 0x00180a68, 0x00180a68, }, + { 0x1c6920, 0x0510001c, 0x0510001c, 0x0510001c, 0x0510001c, }, + { 0x1c6960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, }, + { 0x1c720c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, }, + { 0x1c726c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, }, + { 0x1c7848, 0x00180a65, 0x00180a65, 0x00180a68, 0x00180a68, }, + { 0x1c7920, 0x0510001c, 0x0510001c, 0x0510001c, 0x0510001c, }, + { 0x1c7960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, }, + { 0x1c820c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, }, + { 0x1c826c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, }, +/* { 0x1c8864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, }, */ + { 0x1c8864, 0x0001c600, 0x0001c600, 0x0001c600, 0x0001c600, }, + { 0x1c895c, 0x004b6a8e, 0x004b6a8e, 0x004b6a8e, 0x004b6a8e, }, + { 0x1c8968, 0x000003ce, 0x000003ce, 0x000003ce, 0x000003ce, }, + { 0x1c89bc, 0x00181400, 0x00181400, 0x00181400, 0x00181400, }, + { 0x1c9270, 0x00820820, 0x00820820, 0x00820820, 0x00820820, }, + { 0x1c935c, 0x066c420f, 0x066c420f, 0x066c420f, 0x066c420f, }, + { 0x1c9360, 0x0f282207, 0x0f282207, 0x0f282207, 0x0f282207, }, + { 0x1c9364, 0x17601685, 0x17601685, 0x17601685, 0x17601685, }, + { 0x1c9368, 0x1f801104, 0x1f801104, 0x1f801104, 0x1f801104, }, + { 0x1c936c, 0x37a00c03, 0x37a00c03, 0x37a00c03, 0x37a00c03, }, + { 0x1c9370, 0x3fc40883, 0x3fc40883, 0x3fc40883, 0x3fc40883, }, + { 0x1c9374, 0x57c00803, 0x57c00803, 0x57c00803, 0x57c00803, }, + { 0x1c9378, 0x5fd80682, 0x5fd80682, 0x5fd80682, 0x5fd80682, }, + { 0x1c937c, 0x7fe00482, 0x7fe00482, 0x7fe00482, 0x7fe00482, }, + { 0x1c9380, 0x7f3c7bba, 0x7f3c7bba, 0x7f3c7bba, 0x7f3c7bba, }, + { 0x1c9384, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, } +}; + +/* + * look up a certain register in ar5416_phy_init[] and return the init. value + * for the band and bandwidth given. Return 0 if register address not found. + */ +static u32 carl9170_def_val(u32 reg, bool is_2ghz, bool is_40mhz) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE(ar5416_phy_init); i++) { + if (ar5416_phy_init[i].reg != reg) + continue; + + if (is_2ghz) { + if (is_40mhz) + return ar5416_phy_init[i]._2ghz_40; + else + return ar5416_phy_init[i]._2ghz_20; + } else { + if (is_40mhz) + return ar5416_phy_init[i]._5ghz_40; + else + return ar5416_phy_init[i]._5ghz_20; + } + } + return 0; +} + +/* + * initialize some phy regs from eeprom values in modal_header[] + * acc. to band and bandwith + */ +static int carl9170_init_phy_from_eeprom(struct ar9170 *ar, + bool is_2ghz, bool is_40mhz) +{ + static const u8 xpd2pd[16] = { + 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x6, 0x2, + 0x2, 0x3, 0x7, 0x2, 0xb, 0x2, 0x2, 0x2 + }; + /* pointer to the modal_header acc. to band */ + struct ar9170_eeprom_modal *m = &ar->eeprom.modal_header[is_2ghz]; + u32 val; + + carl9170_regwrite_begin(ar); + + /* ant common control (index 0) */ + carl9170_regwrite(AR9170_PHY_REG_SWITCH_COM, + le32_to_cpu(m->antCtrlCommon)); + + /* ant control chain 0 (index 1) */ + carl9170_regwrite(AR9170_PHY_REG_SWITCH_CHAIN_0, + le32_to_cpu(m->antCtrlChain[0])); + + /* ant control chain 2 (index 2) */ + carl9170_regwrite(AR9170_PHY_REG_SWITCH_CHAIN_2, + le32_to_cpu(m->antCtrlChain[1])); + + /* SwSettle (index 3) */ + if (!is_40mhz) { + val = carl9170_def_val(AR9170_PHY_REG_SETTLING, + is_2ghz, is_40mhz); + SET_VAL(AR9170_PHY_SETTLING_SWITCH, val, m->switchSettling); + carl9170_regwrite(AR9170_PHY_REG_SETTLING, val); + } + + /* adcDesired, pdaDesired (index 4) */ + val = carl9170_def_val(AR9170_PHY_REG_DESIRED_SZ, is_2ghz, is_40mhz); + SET_VAL(AR9170_PHY_DESIRED_SZ_PGA, val, m->pgaDesiredSize); + SET_VAL(AR9170_PHY_DESIRED_SZ_ADC, val, m->adcDesiredSize); + carl9170_regwrite(AR9170_PHY_REG_DESIRED_SZ, val); + + /* TxEndToXpaOff, TxFrameToXpaOn (index 5) */ + val = carl9170_def_val(AR9170_PHY_REG_RF_CTL4, is_2ghz, is_40mhz); + SET_VAL(AR9170_PHY_RF_CTL4_TX_END_XPAB_OFF, val, m->txEndToXpaOff); + SET_VAL(AR9170_PHY_RF_CTL4_TX_END_XPAA_OFF, val, m->txEndToXpaOff); + SET_VAL(AR9170_PHY_RF_CTL4_FRAME_XPAB_ON, val, m->txFrameToXpaOn); + SET_VAL(AR9170_PHY_RF_CTL4_FRAME_XPAA_ON, val, m->txFrameToXpaOn); + carl9170_regwrite(AR9170_PHY_REG_RF_CTL4, val); + + /* TxEndToRxOn (index 6) */ + val = carl9170_def_val(AR9170_PHY_REG_RF_CTL3, is_2ghz, is_40mhz); + SET_VAL(AR9170_PHY_RF_CTL3_TX_END_TO_A2_RX_ON, val, m->txEndToRxOn); + carl9170_regwrite(AR9170_PHY_REG_RF_CTL3, val); + + /* thresh62 (index 7) */ + val = carl9170_def_val(0x1c8864, is_2ghz, is_40mhz); + val = (val & ~0x7f000) | (m->thresh62 << 12); + carl9170_regwrite(0x1c8864, val); + + /* tx/rx attenuation chain 0 (index 8) */ + val = carl9170_def_val(AR9170_PHY_REG_RXGAIN, is_2ghz, is_40mhz); + SET_VAL(AR9170_PHY_RXGAIN_TXRX_ATTEN, val, m->txRxAttenCh[0]); + carl9170_regwrite(AR9170_PHY_REG_RXGAIN, val); + + /* tx/rx attenuation chain 2 (index 9) */ + val = carl9170_def_val(AR9170_PHY_REG_RXGAIN_CHAIN_2, + is_2ghz, is_40mhz); + SET_VAL(AR9170_PHY_RXGAIN_TXRX_ATTEN, val, m->txRxAttenCh[1]); + carl9170_regwrite(AR9170_PHY_REG_RXGAIN_CHAIN_2, val); + + /* tx/rx margin chain 0 (index 10) */ + val = carl9170_def_val(AR9170_PHY_REG_GAIN_2GHZ, is_2ghz, is_40mhz); + SET_VAL(AR9170_PHY_GAIN_2GHZ_RXTX_MARGIN, val, m->rxTxMarginCh[0]); + /* bsw margin chain 0 for 5GHz only */ + if (!is_2ghz) + SET_VAL(AR9170_PHY_GAIN_2GHZ_BSW_MARGIN, val, m->bswMargin[0]); + carl9170_regwrite(AR9170_PHY_REG_GAIN_2GHZ, val); + + /* tx/rx margin chain 2 (index 11) */ + val = carl9170_def_val(AR9170_PHY_REG_GAIN_2GHZ_CHAIN_2, + is_2ghz, is_40mhz); + SET_VAL(AR9170_PHY_GAIN_2GHZ_RXTX_MARGIN, val, m->rxTxMarginCh[1]); + carl9170_regwrite(AR9170_PHY_REG_GAIN_2GHZ_CHAIN_2, val); + + /* iqCall, iqCallq chain 0 (index 12) */ + val = carl9170_def_val(AR9170_PHY_REG_TIMING_CTRL4(0), + is_2ghz, is_40mhz); + SET_VAL(AR9170_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF, val, m->iqCalICh[0]); + SET_VAL(AR9170_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF, val, m->iqCalQCh[0]); + carl9170_regwrite(AR9170_PHY_REG_TIMING_CTRL4(0), val); + + /* iqCall, iqCallq chain 2 (index 13) */ + val = carl9170_def_val(AR9170_PHY_REG_TIMING_CTRL4(2), + is_2ghz, is_40mhz); + SET_VAL(AR9170_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF, val, m->iqCalICh[1]); + SET_VAL(AR9170_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF, val, m->iqCalQCh[1]); + carl9170_regwrite(AR9170_PHY_REG_TIMING_CTRL4(2), val); + + /* xpd gain mask (index 14) */ + val = carl9170_def_val(AR9170_PHY_REG_TPCRG1, is_2ghz, is_40mhz); + SET_VAL(AR9170_PHY_TPCRG1_PD_GAIN_1, val, + xpd2pd[m->xpdGain & 0xf] & 3); + SET_VAL(AR9170_PHY_TPCRG1_PD_GAIN_2, val, + xpd2pd[m->xpdGain & 0xf] >> 2); + carl9170_regwrite(AR9170_PHY_REG_TPCRG1, val); + carl9170_regwrite_finish(); + + return carl9170_regwrite_result(); +} + +static int carl9170_init_phy(struct ar9170 *ar, enum ieee80211_band band) +{ + int i, err; + u32 val; + bool is_2ghz = band == IEEE80211_BAND_2GHZ; + bool is_40mhz = conf_is_ht40(&ar->hw->conf); + + carl9170_regwrite_begin(ar); + + for (i = 0; i < ARRAY_SIZE(ar5416_phy_init); i++) { + if (is_40mhz) { + if (is_2ghz) + val = ar5416_phy_init[i]._2ghz_40; + else + val = ar5416_phy_init[i]._5ghz_40; + } else { + if (is_2ghz) + val = ar5416_phy_init[i]._2ghz_20; + else + val = ar5416_phy_init[i]._5ghz_20; + } + + carl9170_regwrite(ar5416_phy_init[i].reg, val); + } + + carl9170_regwrite_finish(); + err = carl9170_regwrite_result(); + if (err) + return err; + + err = carl9170_init_phy_from_eeprom(ar, is_2ghz, is_40mhz); + if (err) + return err; + + err = carl9170_init_power_cal(ar); + if (err) + return err; + + /* XXX: remove magic! */ + if (is_2ghz) + err = carl9170_write_reg(ar, AR9170_PWR_REG_PLL_ADDAC, 0x5163); + else + err = carl9170_write_reg(ar, AR9170_PWR_REG_PLL_ADDAC, 0x5143); + + return err; +} + +struct carl9170_rf_initvals { + u32 reg, _5ghz, _2ghz; +}; + +static struct carl9170_rf_initvals carl9170_rf_initval[] = { + /* bank 0 */ + { 0x1c58b0, 0x1e5795e5, 0x1e5795e5}, + { 0x1c58e0, 0x02008020, 0x02008020}, + /* bank 1 */ + { 0x1c58b0, 0x02108421, 0x02108421}, + { 0x1c58ec, 0x00000008, 0x00000008}, + /* bank 2 */ + { 0x1c58b0, 0x0e73ff17, 0x0e73ff17}, + { 0x1c58e0, 0x00000420, 0x00000420}, + /* bank 3 */ + { 0x1c58f0, 0x01400018, 0x01c00018}, + /* bank 4 */ + { 0x1c58b0, 0x000001a1, 0x000001a1}, + { 0x1c58e8, 0x00000001, 0x00000001}, + /* bank 5 */ + { 0x1c58b0, 0x00000013, 0x00000013}, + { 0x1c58e4, 0x00000002, 0x00000002}, + /* bank 6 */ + { 0x1c58b0, 0x00000000, 0x00000000}, + { 0x1c58b0, 0x00000000, 0x00000000}, + { 0x1c58b0, 0x00000000, 0x00000000}, + { 0x1c58b0, 0x00000000, 0x00000000}, + { 0x1c58b0, 0x00000000, 0x00000000}, + { 0x1c58b0, 0x00004000, 0x00004000}, + { 0x1c58b0, 0x00006c00, 0x00006c00}, + { 0x1c58b0, 0x00002c00, 0x00002c00}, + { 0x1c58b0, 0x00004800, 0x00004800}, + { 0x1c58b0, 0x00004000, 0x00004000}, + { 0x1c58b0, 0x00006000, 0x00006000}, + { 0x1c58b0, 0x00001000, 0x00001000}, + { 0x1c58b0, 0x00004000, 0x00004000}, + { 0x1c58b0, 0x00007c00, 0x00007c00}, + { 0x1c58b0, 0x00007c00, 0x00007c00}, + { 0x1c58b0, 0x00007c00, 0x00007c00}, + { 0x1c58b0, 0x00007c00, 0x00007c00}, + { 0x1c58b0, 0x00007c00, 0x00007c00}, + { 0x1c58b0, 0x00087c00, 0x00087c00}, + { 0x1c58b0, 0x00007c00, 0x00007c00}, + { 0x1c58b0, 0x00005400, 0x00005400}, + { 0x1c58b0, 0x00000c00, 0x00000c00}, + { 0x1c58b0, 0x00001800, 0x00001800}, + { 0x1c58b0, 0x00007c00, 0x00007c00}, + { 0x1c58b0, 0x00006c00, 0x00006c00}, + { 0x1c58b0, 0x00006c00, 0x00006c00}, + { 0x1c58b0, 0x00007c00, 0x00007c00}, + { 0x1c58b0, 0x00002c00, 0x00002c00}, + { 0x1c58b0, 0x00003c00, 0x00003c00}, + { 0x1c58b0, 0x00003800, 0x00003800}, + { 0x1c58b0, 0x00001c00, 0x00001c00}, + { 0x1c58b0, 0x00000800, 0x00000800}, + { 0x1c58b0, 0x00000408, 0x00000408}, + { 0x1c58b0, 0x00004c15, 0x00004c15}, + { 0x1c58b0, 0x00004188, 0x00004188}, + { 0x1c58b0, 0x0000201e, 0x0000201e}, + { 0x1c58b0, 0x00010408, 0x00010408}, + { 0x1c58b0, 0x00000801, 0x00000801}, + { 0x1c58b0, 0x00000c08, 0x00000c08}, + { 0x1c58b0, 0x0000181e, 0x0000181e}, + { 0x1c58b0, 0x00001016, 0x00001016}, + { 0x1c58b0, 0x00002800, 0x00002800}, + { 0x1c58b0, 0x00004010, 0x00004010}, + { 0x1c58b0, 0x0000081c, 0x0000081c}, + { 0x1c58b0, 0x00000115, 0x00000115}, + { 0x1c58b0, 0x00000015, 0x00000015}, + { 0x1c58b0, 0x00000066, 0x00000066}, + { 0x1c58b0, 0x0000001c, 0x0000001c}, + { 0x1c58b0, 0x00000000, 0x00000000}, + { 0x1c58b0, 0x00000004, 0x00000004}, + { 0x1c58b0, 0x00000015, 0x00000015}, + { 0x1c58b0, 0x0000001f, 0x0000001f}, + { 0x1c58e0, 0x00000000, 0x00000400}, + /* bank 7 */ + { 0x1c58b0, 0x000000a0, 0x000000a0}, + { 0x1c58b0, 0x00000000, 0x00000000}, + { 0x1c58b0, 0x00000040, 0x00000040}, + { 0x1c58f0, 0x0000001c, 0x0000001c}, +}; + +static int carl9170_init_rf_banks_0_7(struct ar9170 *ar, bool band5ghz) +{ + int err, i; + + carl9170_regwrite_begin(ar); + + for (i = 0; i < ARRAY_SIZE(carl9170_rf_initval); i++) + carl9170_regwrite(carl9170_rf_initval[i].reg, + band5ghz ? carl9170_rf_initval[i]._5ghz + : carl9170_rf_initval[i]._2ghz); + + carl9170_regwrite_finish(); + err = carl9170_regwrite_result(); + if (err) + wiphy_err(ar->hw->wiphy, "rf init failed\n"); + + return err; +} + +struct carl9170_phy_freq_params { + u8 coeff_exp; + u16 coeff_man; + u8 coeff_exp_shgi; + u16 coeff_man_shgi; +}; + +enum carl9170_bw { + CARL9170_BW_20, + CARL9170_BW_40_BELOW, + CARL9170_BW_40_ABOVE, + + __CARL9170_NUM_BW, +}; + +struct carl9170_phy_freq_entry { + u16 freq; + struct carl9170_phy_freq_params params[__CARL9170_NUM_BW]; +}; + +/* NB: must be in sync with channel tables in main! */ +static const struct carl9170_phy_freq_entry carl9170_phy_freq_params[] = { +/* + * freq, + * 20MHz, + * 40MHz (below), + * 40Mhz (above), + */ + { 2412, { + { 3, 21737, 3, 19563, }, + { 3, 21827, 3, 19644, }, + { 3, 21647, 3, 19482, }, + } }, + { 2417, { + { 3, 21692, 3, 19523, }, + { 3, 21782, 3, 19604, }, + { 3, 21602, 3, 19442, }, + } }, + { 2422, { + { 3, 21647, 3, 19482, }, + { 3, 21737, 3, 19563, }, + { 3, 21558, 3, 19402, }, + } }, + { 2427, { + { 3, 21602, 3, 19442, }, + { 3, 21692, 3, 19523, }, + { 3, 21514, 3, 19362, }, + } }, + { 2432, { + { 3, 21558, 3, 19402, }, + { 3, 21647, 3, 19482, }, + { 3, 21470, 3, 19323, }, + } }, + { 2437, { + { 3, 21514, 3, 19362, }, + { 3, 21602, 3, 19442, }, + { 3, 21426, 3, 19283, }, + } }, + { 2442, { + { 3, 21470, 3, 19323, }, + { 3, 21558, 3, 19402, }, + { 3, 21382, 3, 19244, }, + } }, + { 2447, { + { 3, 21426, 3, 19283, }, + { 3, 21514, 3, 19362, }, + { 3, 21339, 3, 19205, }, + } }, + { 2452, { + { 3, 21382, 3, 19244, }, + { 3, 21470, 3, 19323, }, + { 3, 21295, 3, 19166, }, + } }, + { 2457, { + { 3, 21339, 3, 19205, }, + { 3, 21426, 3, 19283, }, + { 3, 21252, 3, 19127, }, + } }, + { 2462, { + { 3, 21295, 3, 19166, }, + { 3, 21382, 3, 19244, }, + { 3, 21209, 3, 19088, }, + } }, + { 2467, { + { 3, 21252, 3, 19127, }, + { 3, 21339, 3, 19205, }, + { 3, 21166, 3, 19050, }, + } }, + { 2472, { + { 3, 21209, 3, 19088, }, + { 3, 21295, 3, 19166, }, + { 3, 21124, 3, 19011, }, + } }, + { 2484, { + { 3, 21107, 3, 18996, }, + { 3, 21192, 3, 19073, }, + { 3, 21022, 3, 18920, }, + } }, + { 4920, { + { 4, 21313, 4, 19181, }, + { 4, 21356, 4, 19220, }, + { 4, 21269, 4, 19142, }, + } }, + { 4940, { + { 4, 21226, 4, 19104, }, + { 4, 21269, 4, 19142, }, + { 4, 21183, 4, 19065, }, + } }, + { 4960, { + { 4, 21141, 4, 19027, }, + { 4, 21183, 4, 19065, }, + { 4, 21098, 4, 18988, }, + } }, + { 4980, { + { 4, 21056, 4, 18950, }, + { 4, 21098, 4, 18988, }, + { 4, 21014, 4, 18912, }, + } }, + { 5040, { + { 4, 20805, 4, 18725, }, + { 4, 20846, 4, 18762, }, + { 4, 20764, 4, 18687, }, + } }, + { 5060, { + { 4, 20723, 4, 18651, }, + { 4, 20764, 4, 18687, }, + { 4, 20682, 4, 18614, }, + } }, + { 5080, { + { 4, 20641, 4, 18577, }, + { 4, 20682, 4, 18614, }, + { 4, 20601, 4, 18541, }, + } }, + { 5180, { + { 4, 20243, 4, 18219, }, + { 4, 20282, 4, 18254, }, + { 4, 20204, 4, 18183, }, + } }, + { 5200, { + { 4, 20165, 4, 18148, }, + { 4, 20204, 4, 18183, }, + { 4, 20126, 4, 18114, }, + } }, + { 5220, { + { 4, 20088, 4, 18079, }, + { 4, 20126, 4, 18114, }, + { 4, 20049, 4, 18044, }, + } }, + { 5240, { + { 4, 20011, 4, 18010, }, + { 4, 20049, 4, 18044, }, + { 4, 19973, 4, 17976, }, + } }, + { 5260, { + { 4, 19935, 4, 17941, }, + { 4, 19973, 4, 17976, }, + { 4, 19897, 4, 17907, }, + } }, + { 5280, { + { 4, 19859, 4, 17873, }, + { 4, 19897, 4, 17907, }, + { 4, 19822, 4, 17840, }, + } }, + { 5300, { + { 4, 19784, 4, 17806, }, + { 4, 19822, 4, 17840, }, + { 4, 19747, 4, 17772, }, + } }, + { 5320, { + { 4, 19710, 4, 17739, }, + { 4, 19747, 4, 17772, }, + { 4, 19673, 4, 17706, }, + } }, + { 5500, { + { 4, 19065, 4, 17159, }, + { 4, 19100, 4, 17190, }, + { 4, 19030, 4, 17127, }, + } }, + { 5520, { + { 4, 18996, 4, 17096, }, + { 4, 19030, 4, 17127, }, + { 4, 18962, 4, 17065, }, + } }, + { 5540, { + { 4, 18927, 4, 17035, }, + { 4, 18962, 4, 17065, }, + { 4, 18893, 4, 17004, }, + } }, + { 5560, { + { 4, 18859, 4, 16973, }, + { 4, 18893, 4, 17004, }, + { 4, 18825, 4, 16943, }, + } }, + { 5580, { + { 4, 18792, 4, 16913, }, + { 4, 18825, 4, 16943, }, + { 4, 18758, 4, 16882, }, + } }, + { 5600, { + { 4, 18725, 4, 16852, }, + { 4, 18758, 4, 16882, }, + { 4, 18691, 4, 16822, }, + } }, + { 5620, { + { 4, 18658, 4, 16792, }, + { 4, 18691, 4, 16822, }, + { 4, 18625, 4, 16762, }, + } }, + { 5640, { + { 4, 18592, 4, 16733, }, + { 4, 18625, 4, 16762, }, + { 4, 18559, 4, 16703, }, + } }, + { 5660, { + { 4, 18526, 4, 16673, }, + { 4, 18559, 4, 16703, }, + { 4, 18493, 4, 16644, }, + } }, + { 5680, { + { 4, 18461, 4, 16615, }, + { 4, 18493, 4, 16644, }, + { 4, 18428, 4, 16586, }, + } }, + { 5700, { + { 4, 18396, 4, 16556, }, + { 4, 18428, 4, 16586, }, + { 4, 18364, 4, 16527, }, + } }, + { 5745, { + { 4, 18252, 4, 16427, }, + { 4, 18284, 4, 16455, }, + { 4, 18220, 4, 16398, }, + } }, + { 5765, { + { 4, 18189, 5, 32740, }, + { 4, 18220, 4, 16398, }, + { 4, 18157, 5, 32683, }, + } }, + { 5785, { + { 4, 18126, 5, 32626, }, + { 4, 18157, 5, 32683, }, + { 4, 18094, 5, 32570, }, + } }, + { 5805, { + { 4, 18063, 5, 32514, }, + { 4, 18094, 5, 32570, }, + { 4, 18032, 5, 32458, }, + } }, + { 5825, { + { 4, 18001, 5, 32402, }, + { 4, 18032, 5, 32458, }, + { 4, 17970, 5, 32347, }, + } }, + { 5170, { + { 4, 20282, 4, 18254, }, + { 4, 20321, 4, 18289, }, + { 4, 20243, 4, 18219, }, + } }, + { 5190, { + { 4, 20204, 4, 18183, }, + { 4, 20243, 4, 18219, }, + { 4, 20165, 4, 18148, }, + } }, + { 5210, { + { 4, 20126, 4, 18114, }, + { 4, 20165, 4, 18148, }, + { 4, 20088, 4, 18079, }, + } }, + { 5230, { + { 4, 20049, 4, 18044, }, + { 4, 20088, 4, 18079, }, + { 4, 20011, 4, 18010, }, + } }, +}; + +static int carl9170_init_rf_bank4_pwr(struct ar9170 *ar, bool band5ghz, + u32 freq, enum carl9170_bw bw) +{ + int err; + u32 d0, d1, td0, td1, fd0, fd1; + u8 chansel; + u8 refsel0 = 1, refsel1 = 0; + u8 lf_synth = 0; + + switch (bw) { + case CARL9170_BW_40_ABOVE: + freq += 10; + break; + case CARL9170_BW_40_BELOW: + freq -= 10; + break; + case CARL9170_BW_20: + break; + default: + BUG(); + return -ENOSYS; + } + + if (band5ghz) { + if (freq % 10) { + chansel = (freq - 4800) / 5; + } else { + chansel = ((freq - 4800) / 10) * 2; + refsel0 = 0; + refsel1 = 1; + } + chansel = byte_rev_table[chansel]; + } else { + if (freq == 2484) { + chansel = 10 + (freq - 2274) / 5; + lf_synth = 1; + } else + chansel = 16 + (freq - 2272) / 5; + chansel *= 4; + chansel = byte_rev_table[chansel]; + } + + d1 = chansel; + d0 = 0x21 | + refsel0 << 3 | + refsel1 << 2 | + lf_synth << 1; + td0 = d0 & 0x1f; + td1 = d1 & 0x1f; + fd0 = td1 << 5 | td0; + + td0 = (d0 >> 5) & 0x7; + td1 = (d1 >> 5) & 0x7; + fd1 = td1 << 5 | td0; + + carl9170_regwrite_begin(ar); + + carl9170_regwrite(0x1c58b0, fd0); + carl9170_regwrite(0x1c58e8, fd1); + + carl9170_regwrite_finish(); + err = carl9170_regwrite_result(); + if (err) + return err; + + msleep(20); + + return 0; +} + +static const struct carl9170_phy_freq_params * +carl9170_get_hw_dyn_params(struct ieee80211_channel *channel, + enum carl9170_bw bw) +{ + unsigned int chanidx = 0; + u16 freq = 2412; + + if (channel) { + chanidx = channel->hw_value; + freq = channel->center_freq; + } + + BUG_ON(chanidx >= ARRAY_SIZE(carl9170_phy_freq_params)); + + BUILD_BUG_ON(__CARL9170_NUM_BW != 3); + + WARN_ON(carl9170_phy_freq_params[chanidx].freq != freq); + + return &carl9170_phy_freq_params[chanidx].params[bw]; +} + +static int carl9170_find_freq_idx(int nfreqs, u8 *freqs, u8 f) +{ + int idx = nfreqs - 2; + + while (idx >= 0) { + if (f >= freqs[idx]) + return idx; + idx--; + } + + return 0; +} + +static s32 carl9170_interpolate_s32(s32 x, s32 x1, s32 y1, s32 x2, s32 y2) +{ + /* nothing to interpolate, it's horizontal */ + if (y2 == y1) + return y1; + + /* check if we hit one of the edges */ + if (x == x1) + return y1; + if (x == x2) + return y2; + + /* x1 == x2 is bad, hopefully == x */ + if (x2 == x1) + return y1; + + return y1 + (((y2 - y1) * (x - x1)) / (x2 - x1)); +} + +static u8 carl9170_interpolate_u8(u8 x, u8 x1, u8 y1, u8 x2, u8 y2) +{ +#define SHIFT 8 + s32 y; + + y = carl9170_interpolate_s32(x << SHIFT, x1 << SHIFT, + y1 << SHIFT, x2 << SHIFT, y2 << SHIFT); + + /* + * XXX: unwrap this expression + * Isn't it just DIV_ROUND_UP(y, 1<> SHIFT) + ((y & (1<<(SHIFT-1))) >> (SHIFT - 1)); +#undef SHIFT +} + +static u8 carl9170_interpolate_val(u8 x, u8 *x_array, u8 *y_array) +{ + int i; + + for (i = 0; i < 3; i++) { + if (x <= x_array[i + 1]) + break; + } + + return carl9170_interpolate_u8(x, x_array[i], y_array[i], + x_array[i + 1], y_array[i + 1]); +} + +static int carl9170_set_freq_cal_data(struct ar9170 *ar, + struct ieee80211_channel *channel) +{ + u8 *cal_freq_pier; + u8 vpds[2][AR5416_PD_GAIN_ICEPTS]; + u8 pwrs[2][AR5416_PD_GAIN_ICEPTS]; + int chain, idx, i; + u32 phy_data = 0; + u8 f, tmp; + + switch (channel->band) { + case IEEE80211_BAND_2GHZ: + f = channel->center_freq - 2300; + cal_freq_pier = ar->eeprom.cal_freq_pier_2G; + i = AR5416_NUM_2G_CAL_PIERS - 1; + break; + + case IEEE80211_BAND_5GHZ: + f = (channel->center_freq - 4800) / 5; + cal_freq_pier = ar->eeprom.cal_freq_pier_5G; + i = AR5416_NUM_5G_CAL_PIERS - 1; + break; + + default: + return -EINVAL; + break; + } + + for (; i >= 0; i--) { + if (cal_freq_pier[i] != 0xff) + break; + } + if (i < 0) + return -EINVAL; + + idx = carl9170_find_freq_idx(i, cal_freq_pier, f); + + carl9170_regwrite_begin(ar); + + for (chain = 0; chain < AR5416_MAX_CHAINS; chain++) { + for (i = 0; i < AR5416_PD_GAIN_ICEPTS; i++) { + struct ar9170_calibration_data_per_freq *cal_pier_data; + int j; + + switch (channel->band) { + case IEEE80211_BAND_2GHZ: + cal_pier_data = &ar->eeprom. + cal_pier_data_2G[chain][idx]; + break; + + case IEEE80211_BAND_5GHZ: + cal_pier_data = &ar->eeprom. + cal_pier_data_5G[chain][idx]; + break; + + default: + return -EINVAL; + } + + for (j = 0; j < 2; j++) { + vpds[j][i] = carl9170_interpolate_u8(f, + cal_freq_pier[idx], + cal_pier_data->vpd_pdg[j][i], + cal_freq_pier[idx + 1], + cal_pier_data[1].vpd_pdg[j][i]); + + pwrs[j][i] = carl9170_interpolate_u8(f, + cal_freq_pier[idx], + cal_pier_data->pwr_pdg[j][i], + cal_freq_pier[idx + 1], + cal_pier_data[1].pwr_pdg[j][i]) / 2; + } + } + + for (i = 0; i < 76; i++) { + if (i < 25) { + tmp = carl9170_interpolate_val(i, &pwrs[0][0], + &vpds[0][0]); + } else { + tmp = carl9170_interpolate_val(i - 12, + &pwrs[1][0], + &vpds[1][0]); + } + + phy_data |= tmp << ((i & 3) << 3); + if ((i & 3) == 3) { + carl9170_regwrite(0x1c6280 + chain * 0x1000 + + (i & ~3), phy_data); + phy_data = 0; + } + } + + for (i = 19; i < 32; i++) + carl9170_regwrite(0x1c6280 + chain * 0x1000 + (i << 2), + 0x0); + } + + carl9170_regwrite_finish(); + return carl9170_regwrite_result(); +} + +static u8 carl9170_get_max_edge_power(struct ar9170 *ar, + u32 freq, struct ar9170_calctl_edges edges[]) +{ + int i; + u8 rc = AR5416_MAX_RATE_POWER; + u8 f; + if (freq < 3000) + f = freq - 2300; + else + f = (freq - 4800) / 5; + + for (i = 0; i < AR5416_NUM_BAND_EDGES; i++) { + if (edges[i].channel == 0xff) + break; + if (f == edges[i].channel) { + /* exact freq match */ + rc = edges[i].power_flags & ~AR9170_CALCTL_EDGE_FLAGS; + break; + } + if (i > 0 && f < edges[i].channel) { + if (f > edges[i - 1].channel && + edges[i - 1].power_flags & + AR9170_CALCTL_EDGE_FLAGS) { + /* lower channel has the inband flag set */ + rc = edges[i - 1].power_flags & + ~AR9170_CALCTL_EDGE_FLAGS; + } + break; + } + } + + if (i == AR5416_NUM_BAND_EDGES) { + if (f > edges[i - 1].channel && + edges[i - 1].power_flags & AR9170_CALCTL_EDGE_FLAGS) { + /* lower channel has the inband flag set */ + rc = edges[i - 1].power_flags & + ~AR9170_CALCTL_EDGE_FLAGS; + } + } + return rc; +} + +static u8 carl9170_get_heavy_clip(struct ar9170 *ar, u32 freq, + enum carl9170_bw bw, struct ar9170_calctl_edges edges[]) +{ + u8 f; + int i; + u8 rc = 0; + + if (freq < 3000) + f = freq - 2300; + else + f = (freq - 4800) / 5; + + if (bw == CARL9170_BW_40_BELOW || bw == CARL9170_BW_40_ABOVE) + rc |= 0xf0; + + for (i = 0; i < AR5416_NUM_BAND_EDGES; i++) { + if (edges[i].channel == 0xff) + break; + if (f == edges[i].channel) { + if (!(edges[i].power_flags & AR9170_CALCTL_EDGE_FLAGS)) + rc |= 0x0f; + break; + } + } + + return rc; +} + +/* + * calculate the conformance test limits and the heavy clip parameter + * and apply them to ar->power* (derived from otus hal/hpmain.c, line 3706) + */ +static void carl9170_calc_ctl(struct ar9170 *ar, u32 freq, enum carl9170_bw bw) +{ + u8 ctl_grp; /* CTL group */ + u8 ctl_idx; /* CTL index */ + int i, j; + struct ctl_modes { + u8 ctl_mode; + u8 max_power; + u8 *pwr_cal_data; + int pwr_cal_len; + } *modes; + + /* + * order is relevant in the mode_list_*: we fall back to the + * lower indices if any mode is missed in the EEPROM. + */ + struct ctl_modes mode_list_2ghz[] = { + { CTL_11B, 0, ar->power_2G_cck, 4 }, + { CTL_11G, 0, ar->power_2G_ofdm, 4 }, + { CTL_2GHT20, 0, ar->power_2G_ht20, 8 }, + { CTL_2GHT40, 0, ar->power_2G_ht40, 8 }, + }; + struct ctl_modes mode_list_5ghz[] = { + { CTL_11A, 0, ar->power_5G_leg, 4 }, + { CTL_5GHT20, 0, ar->power_5G_ht20, 8 }, + { CTL_5GHT40, 0, ar->power_5G_ht40, 8 }, + }; + int nr_modes; + +#define EDGES(c, n) (ar->eeprom.ctl_data[c].control_edges[n]) + + ar->heavy_clip = 0; + + /* + * TODO: investigate the differences between OTUS' + * hpreg.c::zfHpGetRegulatoryDomain() and + * ath/regd.c::ath_regd_get_band_ctl() - + * e.g. for FCC3_WORLD the OTUS procedure + * always returns CTL_FCC, while the one in ath/ delivers + * CTL_ETSI for 2GHz and CTL_FCC for 5GHz. + */ + ctl_grp = ath_regd_get_band_ctl(&ar->common.regulatory, + ar->hw->conf.channel->band); + + /* ctl group not found - either invalid band (NO_CTL) or ww roaming */ + if (ctl_grp == NO_CTL || ctl_grp == SD_NO_CTL) + ctl_grp = CTL_FCC; + + if (ctl_grp != CTL_FCC) + /* skip CTL and heavy clip for CTL_MKK and CTL_ETSI */ + return; + + if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ) { + modes = mode_list_2ghz; + nr_modes = ARRAY_SIZE(mode_list_2ghz); + } else { + modes = mode_list_5ghz; + nr_modes = ARRAY_SIZE(mode_list_5ghz); + } + + for (i = 0; i < nr_modes; i++) { + u8 c = ctl_grp | modes[i].ctl_mode; + for (ctl_idx = 0; ctl_idx < AR5416_NUM_CTLS; ctl_idx++) + if (c == ar->eeprom.ctl_index[ctl_idx]) + break; + if (ctl_idx < AR5416_NUM_CTLS) { + int f_off = 0; + + /* + * determine heavy clip parameter + * from the 11G edges array + */ + if (modes[i].ctl_mode == CTL_11G) { + ar->heavy_clip = + carl9170_get_heavy_clip(ar, + freq, bw, EDGES(ctl_idx, 1)); + } + + /* adjust freq for 40MHz */ + if (modes[i].ctl_mode == CTL_2GHT40 || + modes[i].ctl_mode == CTL_5GHT40) { + if (bw == CARL9170_BW_40_BELOW) + f_off = -10; + else + f_off = 10; + } + + modes[i].max_power = + carl9170_get_max_edge_power(ar, + freq+f_off, EDGES(ctl_idx, 1)); + + /* + * TODO: check if the regulatory max. power is + * controlled by cfg80211 for DFS. + * (hpmain applies it to max_power itself for DFS freq) + */ + + } else { + /* + * Workaround in otus driver, hpmain.c, line 3906: + * if no data for 5GHT20 are found, take the + * legacy 5G value. We extend this here to fallback + * from any other HT* or 11G, too. + */ + int k = i; + + modes[i].max_power = AR5416_MAX_RATE_POWER; + while (k-- > 0) { + if (modes[k].max_power != + AR5416_MAX_RATE_POWER) { + modes[i].max_power = modes[k].max_power; + break; + } + } + } + + /* apply max power to pwr_cal_data (ar->power_*) */ + for (j = 0; j < modes[i].pwr_cal_len; j++) { + modes[i].pwr_cal_data[j] = min(modes[i].pwr_cal_data[j], + modes[i].max_power); + } + } + + if (ar->heavy_clip & 0xf0) { + ar->power_2G_ht40[0]--; + ar->power_2G_ht40[1]--; + ar->power_2G_ht40[2]--; + } + if (ar->heavy_clip & 0xf) { + ar->power_2G_ht20[0]++; + ar->power_2G_ht20[1]++; + ar->power_2G_ht20[2]++; + } + +#undef EDGES +} + +static int carl9170_set_power_cal(struct ar9170 *ar, u32 freq, + enum carl9170_bw bw) +{ + struct ar9170_calibration_target_power_legacy *ctpl; + struct ar9170_calibration_target_power_ht *ctph; + u8 *ctpres; + int ntargets; + int idx, i, n; + u8 ackpower, ackchains, f; + u8 pwr_freqs[AR5416_MAX_NUM_TGT_PWRS]; + + if (freq < 3000) + f = freq - 2300; + else + f = (freq - 4800)/5; + + /* + * cycle through the various modes + * + * legacy modes first: 5G, 2G CCK, 2G OFDM + */ + for (i = 0; i < 3; i++) { + switch (i) { + case 0: /* 5 GHz legacy */ + ctpl = &ar->eeprom.cal_tgt_pwr_5G[0]; + ntargets = AR5416_NUM_5G_TARGET_PWRS; + ctpres = ar->power_5G_leg; + break; + case 1: /* 2.4 GHz CCK */ + ctpl = &ar->eeprom.cal_tgt_pwr_2G_cck[0]; + ntargets = AR5416_NUM_2G_CCK_TARGET_PWRS; + ctpres = ar->power_2G_cck; + break; + case 2: /* 2.4 GHz OFDM */ + ctpl = &ar->eeprom.cal_tgt_pwr_2G_ofdm[0]; + ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS; + ctpres = ar->power_2G_ofdm; + break; + default: + BUG(); + } + + for (n = 0; n < ntargets; n++) { + if (ctpl[n].freq == 0xff) + break; + pwr_freqs[n] = ctpl[n].freq; + } + ntargets = n; + idx = carl9170_find_freq_idx(ntargets, pwr_freqs, f); + for (n = 0; n < 4; n++) + ctpres[n] = carl9170_interpolate_u8(f, + ctpl[idx + 0].freq, ctpl[idx + 0].power[n], + ctpl[idx + 1].freq, ctpl[idx + 1].power[n]); + } + + /* HT modes now: 5G HT20, 5G HT40, 2G CCK, 2G OFDM, 2G HT20, 2G HT40 */ + for (i = 0; i < 4; i++) { + switch (i) { + case 0: /* 5 GHz HT 20 */ + ctph = &ar->eeprom.cal_tgt_pwr_5G_ht20[0]; + ntargets = AR5416_NUM_5G_TARGET_PWRS; + ctpres = ar->power_5G_ht20; + break; + case 1: /* 5 GHz HT 40 */ + ctph = &ar->eeprom.cal_tgt_pwr_5G_ht40[0]; + ntargets = AR5416_NUM_5G_TARGET_PWRS; + ctpres = ar->power_5G_ht40; + break; + case 2: /* 2.4 GHz HT 20 */ + ctph = &ar->eeprom.cal_tgt_pwr_2G_ht20[0]; + ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS; + ctpres = ar->power_2G_ht20; + break; + case 3: /* 2.4 GHz HT 40 */ + ctph = &ar->eeprom.cal_tgt_pwr_2G_ht40[0]; + ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS; + ctpres = ar->power_2G_ht40; + break; + default: + BUG(); + } + + for (n = 0; n < ntargets; n++) { + if (ctph[n].freq == 0xff) + break; + pwr_freqs[n] = ctph[n].freq; + } + ntargets = n; + idx = carl9170_find_freq_idx(ntargets, pwr_freqs, f); + for (n = 0; n < 8; n++) + ctpres[n] = carl9170_interpolate_u8(f, + ctph[idx + 0].freq, ctph[idx + 0].power[n], + ctph[idx + 1].freq, ctph[idx + 1].power[n]); + } + + /* calc. conformance test limits and apply to ar->power*[] */ + carl9170_calc_ctl(ar, freq, bw); + + /* set ACK/CTS TX power */ + carl9170_regwrite_begin(ar); + + if (ar->eeprom.tx_mask != 1) + ackchains = AR9170_TX_PHY_TXCHAIN_2; + else + ackchains = AR9170_TX_PHY_TXCHAIN_1; + + if (freq < 3000) + ackpower = ar->power_2G_ofdm[0] & 0x3f; + else + ackpower = ar->power_5G_leg[0] & 0x3f; + + carl9170_regwrite(AR9170_MAC_REG_ACK_TPC, + 0x3c1e | ackpower << 20 | ackchains << 26); + carl9170_regwrite(AR9170_MAC_REG_RTS_CTS_TPC, + ackpower << 5 | ackchains << 11 | + ackpower << 21 | ackchains << 27); + + carl9170_regwrite(AR9170_MAC_REG_CFEND_QOSNULL_TPC, + ackpower << 5 | ackchains << 11 | + ackpower << 21 | ackchains << 27); + + carl9170_regwrite_finish(); + return carl9170_regwrite_result(); +} + +/* TODO: replace this with sign_extend32(noise, 8) */ +static int carl9170_calc_noise_dbm(u32 raw_noise) +{ + if (raw_noise & 0x100) + return ~((raw_noise & 0x0ff) >> 1); + else + return (raw_noise & 0xff) >> 1; +} + +int carl9170_get_noisefloor(struct ar9170 *ar) +{ + static const u32 phy_regs[] = { + AR9170_PHY_REG_CCA, AR9170_PHY_REG_CH1_CCA, + AR9170_PHY_REG_CH2_CCA, AR9170_PHY_REG_EXT_CCA, + AR9170_PHY_REG_CH1_EXT_CCA, AR9170_PHY_REG_CH2_EXT_CCA }; + u32 phy_res[ARRAY_SIZE(phy_regs)]; + int err, i; + + BUILD_BUG_ON(ARRAY_SIZE(phy_regs) != ARRAY_SIZE(ar->noise)); + + err = carl9170_read_mreg(ar, ARRAY_SIZE(phy_regs), phy_regs, phy_res); + if (err) + return err; + + for (i = 0; i < 3; i++) { + ar->noise[i] = carl9170_calc_noise_dbm( + (phy_res[i] >> 19) & 0x1ff); + + ar->noise[i + 3] = carl9170_calc_noise_dbm( + (phy_res[i + 3] >> 23) & 0x1ff); + } + + return 0; +} + +static enum carl9170_bw nl80211_to_carl(enum nl80211_channel_type type) +{ + switch (type) { + case NL80211_CHAN_NO_HT: + case NL80211_CHAN_HT20: + return CARL9170_BW_20; + case NL80211_CHAN_HT40MINUS: + return CARL9170_BW_40_BELOW; + case NL80211_CHAN_HT40PLUS: + return CARL9170_BW_40_ABOVE; + default: + BUG(); + } +} + +int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel, + enum nl80211_channel_type _bw, + enum carl9170_rf_init_mode rfi) +{ + const struct carl9170_phy_freq_params *freqpar; + struct carl9170_rf_init_result rf_res; + struct carl9170_rf_init rf; + u32 cmd, tmp, offs = 0; + int err; + enum carl9170_bw bw; + bool warm_reset; + struct ieee80211_channel *old_channel = NULL; + + bw = nl80211_to_carl(_bw); + + /* may be NULL at first setup */ + if (ar->channel) { + old_channel = ar->channel; + warm_reset = (old_channel->band != channel->band) || + (old_channel->center_freq == + channel->center_freq); + + ar->channel = NULL; + } else { + warm_reset = true; + } + + /* HW workaround */ + if (!ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] && + channel->center_freq <= 2417) + warm_reset = true; + + if (rfi != CARL9170_RFI_NONE || warm_reset) { + u32 val; + + if (rfi == CARL9170_RFI_COLD) + val = AR9170_PWR_RESET_BB_COLD_RESET; + else + val = AR9170_PWR_RESET_BB_WARM_RESET; + + /* warm/cold reset BB/ADDA */ + err = carl9170_write_reg(ar, AR9170_PWR_REG_RESET, val); + if (err) + return err; + + err = carl9170_write_reg(ar, AR9170_PWR_REG_RESET, 0x0); + if (err) + return err; + + err = carl9170_init_phy(ar, channel->band); + if (err) + return err; + + err = carl9170_init_rf_banks_0_7(ar, + channel->band == IEEE80211_BAND_5GHZ); + if (err) + return err; + + cmd = CARL9170_CMD_RF_INIT; + + msleep(100); + + err = carl9170_echo_test(ar, 0xaabbccdd); + if (err) + return err; + } else { + cmd = CARL9170_CMD_FREQUENCY; + } + + err = carl9170_exec_cmd(ar, CARL9170_CMD_FREQ_START, 0, NULL, 0, NULL); + if (err) + return err; + + err = carl9170_write_reg(ar, AR9170_PHY_REG_HEAVY_CLIP_ENABLE, + 0x200); + + err = carl9170_init_rf_bank4_pwr(ar, + channel->band == IEEE80211_BAND_5GHZ, + channel->center_freq, bw); + if (err) + return err; + + tmp = AR9170_PHY_TURBO_FC_SINGLE_HT_LTF1 | + AR9170_PHY_TURBO_FC_HT_EN; + + switch (bw) { + case CARL9170_BW_20: + break; + case CARL9170_BW_40_BELOW: + tmp |= AR9170_PHY_TURBO_FC_DYN2040_EN | + AR9170_PHY_TURBO_FC_SHORT_GI_40; + offs = 3; + break; + case CARL9170_BW_40_ABOVE: + tmp |= AR9170_PHY_TURBO_FC_DYN2040_EN | + AR9170_PHY_TURBO_FC_SHORT_GI_40 | + AR9170_PHY_TURBO_FC_DYN2040_PRI_CH; + offs = 1; + break; + default: + BUG(); + return -ENOSYS; + } + + if (ar->eeprom.tx_mask != 1) + tmp |= AR9170_PHY_TURBO_FC_WALSH; + + err = carl9170_write_reg(ar, AR9170_PHY_REG_TURBO, tmp); + if (err) + return err; + + err = carl9170_set_freq_cal_data(ar, channel); + if (err) + return err; + + err = carl9170_set_power_cal(ar, channel->center_freq, bw); + if (err) + return err; + + freqpar = carl9170_get_hw_dyn_params(channel, bw); + + rf.ht_settings = 0; + if (conf_is_ht(&ar->hw->conf)) { + rf.ht_settings |= CARL9170FW_PHY_HT_ENABLE; + + if (conf_is_ht40(&ar->hw->conf)) { + rf.ht_settings |= CARL9170FW_PHY_HT_DYN2040; + SET_VAL(CARL9170FW_PHY_HT_EXT_CHAN_OFF, + rf.ht_settings, offs); + } + } + + rf.freq = cpu_to_le32(channel->center_freq * 1000); + rf.delta_slope_coeff_exp = cpu_to_le32(freqpar->coeff_exp); + rf.delta_slope_coeff_man = cpu_to_le32(freqpar->coeff_man); + rf.delta_slope_coeff_exp_shgi = cpu_to_le32(freqpar->coeff_exp_shgi); + rf.delta_slope_coeff_man_shgi = cpu_to_le32(freqpar->coeff_man_shgi); + + if (rfi != CARL9170_RFI_NONE) + rf.finiteLoopCount = cpu_to_le32(2000); + else + rf.finiteLoopCount = cpu_to_le32(1000); + + err = carl9170_exec_cmd(ar, cmd, sizeof(rf), &rf, + sizeof(rf_res), &rf_res); + if (err) + return err; + + err = le32_to_cpu(rf_res.ret); + if (err != 0) { + ar->chan_fail++; + ar->total_chan_fail++; + + wiphy_err(ar->hw->wiphy, "channel change: %d -> %d " + "failed (%d).\n", old_channel ? + old_channel->center_freq : -1, channel->center_freq, + err); + + if ((rfi == CARL9170_RFI_COLD) || (ar->chan_fail > 3)) { + /* + * We have tried very hard to change to _another_ + * channel and we've failed to do so! + * Chances are that the PHY/RF is no longer + * operable (due to corruptions/fatal events/bugs?) + * and we need to reset at a higher level. + */ + carl9170_restart(ar, CARL9170_RR_TOO_MANY_PHY_ERRORS); + return 0; + } + + err = carl9170_set_channel(ar, channel, _bw, + CARL9170_RFI_COLD); + if (err) + return err; + } else { + ar->chan_fail = 0; + } + + err = carl9170_get_noisefloor(ar); + if (err) + return err; + + if (ar->heavy_clip) { + err = carl9170_write_reg(ar, AR9170_PHY_REG_HEAVY_CLIP_ENABLE, + 0x200 | ar->heavy_clip); + if (err) { + if (net_ratelimit()) { + wiphy_err(ar->hw->wiphy, "failed to set " + "heavy clip\n"); + } + + return err; + } + } + + /* FIXME: PSM does not work in 5GHz Band */ + if (channel->band == IEEE80211_BAND_5GHZ) + ar->ps.off_override |= PS_OFF_5GHZ; + else + ar->ps.off_override &= ~PS_OFF_5GHZ; + + ar->channel = channel; + return 0; +} -- cgit v1.2.3 From a84fab3cbfdc427e7d366f1cc844f27b2084c26c Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Mon, 6 Sep 2010 01:09:20 +0200 Subject: carl9170: 802.11 rx/tx processing and usb backend Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/rx.c | 909 ++++++++++++++++++++ drivers/net/wireless/ath/carl9170/tx.c | 1373 +++++++++++++++++++++++++++++++ drivers/net/wireless/ath/carl9170/usb.c | 1137 +++++++++++++++++++++++++ 3 files changed, 3419 insertions(+) create mode 100644 drivers/net/wireless/ath/carl9170/rx.c create mode 100644 drivers/net/wireless/ath/carl9170/tx.c create mode 100644 drivers/net/wireless/ath/carl9170/usb.c (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c new file mode 100644 index 000000000000..671dbc429547 --- /dev/null +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -0,0 +1,909 @@ +/* + * Atheros CARL9170 driver + * + * 802.11 & command trap routines + * + * Copyright 2008, Johannes Berg + * Copyright 2009, 2010, Christian Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include "carl9170.h" +#include "hw.h" +#include "cmd.h" + +static void carl9170_dbg_message(struct ar9170 *ar, const char *buf, u32 len) +{ + bool restart = false; + enum carl9170_restart_reasons reason = CARL9170_RR_NO_REASON; + + if (len > 3) { + if (memcmp(buf, CARL9170_ERR_MAGIC, 3) == 0) { + ar->fw.err_counter++; + if (ar->fw.err_counter > 3) { + restart = true; + reason = CARL9170_RR_TOO_MANY_FIRMWARE_ERRORS; + } + } + + if (memcmp(buf, CARL9170_BUG_MAGIC, 3) == 0) { + ar->fw.bug_counter++; + restart = true; + reason = CARL9170_RR_FATAL_FIRMWARE_ERROR; + } + } + + wiphy_info(ar->hw->wiphy, "FW: %.*s\n", len, buf); + + if (restart) + carl9170_restart(ar, reason); +} + +static void carl9170_handle_ps(struct ar9170 *ar, struct carl9170_rsp *rsp) +{ + u32 ps; + bool new_ps; + + ps = le32_to_cpu(rsp->psm.state); + + new_ps = (ps & CARL9170_PSM_COUNTER) != CARL9170_PSM_WAKE; + if (ar->ps.state != new_ps) { + if (!new_ps) { + ar->ps.sleep_ms = jiffies_to_msecs(jiffies - + ar->ps.last_action); + } + + ar->ps.last_action = jiffies; + + ar->ps.state = new_ps; + } +} + +static int carl9170_check_sequence(struct ar9170 *ar, unsigned int seq) +{ + if (ar->cmd_seq < -1) + return 0; + + /* + * Initialize Counter + */ + if (ar->cmd_seq < 0) + ar->cmd_seq = seq; + + /* + * The sequence is strictly monotonic increasing and it never skips! + * + * Therefore we can safely assume that whenever we received an + * unexpected sequence we have lost some valuable data. + */ + if (seq != ar->cmd_seq) { + int count; + + count = (seq - ar->cmd_seq) % ar->fw.cmd_bufs; + + wiphy_err(ar->hw->wiphy, "lost %d command responses/traps! " + "w:%d g:%d\n", count, ar->cmd_seq, seq); + + carl9170_restart(ar, CARL9170_RR_LOST_RSP); + return -EIO; + } + + ar->cmd_seq = (ar->cmd_seq + 1) % ar->fw.cmd_bufs; + return 0; +} + +static void carl9170_cmd_callback(struct ar9170 *ar, u32 len, void *buffer) +{ + /* + * Some commands may have a variable response length + * and we cannot predict the correct length in advance. + * So we only check if we provided enough space for the data. + */ + if (unlikely(ar->readlen != (len - 4))) { + dev_warn(&ar->udev->dev, "received invalid command response:" + "got %d, instead of %d\n", len - 4, ar->readlen); + print_hex_dump_bytes("carl9170 cmd:", DUMP_PREFIX_OFFSET, + ar->cmd_buf, (ar->cmd.hdr.len + 4) & 0x3f); + print_hex_dump_bytes("carl9170 rsp:", DUMP_PREFIX_OFFSET, + buffer, len); + /* + * Do not complete. The command times out, + * and we get a stack trace from there. + */ + carl9170_restart(ar, CARL9170_RR_INVALID_RSP); + } + + spin_lock(&ar->cmd_lock); + if (ar->readbuf) { + if (len >= 4) + memcpy(ar->readbuf, buffer + 4, len - 4); + + ar->readbuf = NULL; + } + complete(&ar->cmd_wait); + spin_unlock(&ar->cmd_lock); +} + +void carl9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len) +{ + struct carl9170_rsp *cmd = (void *) buf; + struct ieee80211_vif *vif; + + if (carl9170_check_sequence(ar, cmd->hdr.seq)) + return; + + if ((cmd->hdr.cmd & CARL9170_RSP_FLAG) != CARL9170_RSP_FLAG) { + if (!(cmd->hdr.cmd & CARL9170_CMD_ASYNC_FLAG)) + carl9170_cmd_callback(ar, len, buf); + + return; + } + + if (unlikely(cmd->hdr.len != (len - 4))) { + if (net_ratelimit()) { + wiphy_err(ar->hw->wiphy, "FW: received over-/under" + "sized event %x (%d, but should be %d).\n", + cmd->hdr.cmd, cmd->hdr.len, len - 4); + + print_hex_dump_bytes("dump:", DUMP_PREFIX_NONE, + buf, len); + } + + return; + } + + /* hardware event handlers */ + switch (cmd->hdr.cmd) { + case CARL9170_RSP_PRETBTT: + /* pre-TBTT event */ + rcu_read_lock(); + vif = carl9170_get_main_vif(ar); + + if (!vif) { + rcu_read_unlock(); + break; + } + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + carl9170_handle_ps(ar, cmd); + break; + + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_ADHOC: + carl9170_update_beacon(ar, true); + break; + + default: + break; + } + rcu_read_unlock(); + + break; + + + case CARL9170_RSP_TXCOMP: + /* TX status notification */ + carl9170_tx_process_status(ar, cmd); + break; + + case CARL9170_RSP_BEACON_CONFIG: + /* + * (IBSS) beacon send notification + * bytes: 04 c2 XX YY B4 B3 B2 B1 + * + * XX always 80 + * YY always 00 + * B1-B4 "should" be the number of send out beacons. + */ + break; + + case CARL9170_RSP_ATIM: + /* End of Atim Window */ + break; + + case CARL9170_RSP_WATCHDOG: + /* Watchdog Interrupt */ + carl9170_restart(ar, CARL9170_RR_WATCHDOG); + break; + + case CARL9170_RSP_TEXT: + /* firmware debug */ + carl9170_dbg_message(ar, (char *)buf + 4, len - 4); + break; + + case CARL9170_RSP_HEXDUMP: + wiphy_dbg(ar->hw->wiphy, "FW: HD %d\n", len - 4); + print_hex_dump_bytes("FW:", DUMP_PREFIX_NONE, + (char *)buf + 4, len - 4); + break; + + case CARL9170_RSP_RADAR: + if (!net_ratelimit()) + break; + + wiphy_info(ar->hw->wiphy, "FW: RADAR! Please report this " + "incident to linux-wireless@vger.kernel.org !\n"); + break; + + case CARL9170_RSP_GPIO: +#ifdef CONFIG_CARL9170_WPC + if (ar->wps.pbc) { + bool state = !!(cmd->gpio.gpio & cpu_to_le32( + AR9170_GPIO_PORT_WPS_BUTTON_PRESSED)); + + if (state != ar->wps.pbc_state) { + ar->wps.pbc_state = state; + input_report_key(ar->wps.pbc, KEY_WPS_BUTTON, + state); + input_sync(ar->wps.pbc); + } + } +#endif /* CONFIG_CARL9170_WPC */ + break; + + case CARL9170_RSP_BOOT: + complete(&ar->fw_boot_wait); + break; + + default: + wiphy_err(ar->hw->wiphy, "FW: received unhandled event %x\n", + cmd->hdr.cmd); + print_hex_dump_bytes("dump:", DUMP_PREFIX_NONE, buf, len); + break; + } +} + +static int carl9170_rx_mac_status(struct ar9170 *ar, + struct ar9170_rx_head *head, struct ar9170_rx_macstatus *mac, + struct ieee80211_rx_status *status) +{ + struct ieee80211_channel *chan; + u8 error, decrypt; + + BUILD_BUG_ON(sizeof(struct ar9170_rx_head) != 12); + BUILD_BUG_ON(sizeof(struct ar9170_rx_macstatus) != 4); + + error = mac->error; + + if (error & AR9170_RX_ERROR_WRONG_RA) { + if (!ar->sniffer_enabled) + return -EINVAL; + } + + if (error & AR9170_RX_ERROR_PLCP) { + if (!(ar->filter_state & FIF_PLCPFAIL)) + return -EINVAL; + + status->flag |= RX_FLAG_FAILED_PLCP_CRC; + } + + if (error & AR9170_RX_ERROR_FCS) { + ar->tx_fcs_errors++; + + if (!(ar->filter_state & FIF_FCSFAIL)) + return -EINVAL; + + status->flag |= RX_FLAG_FAILED_FCS_CRC; + } + + decrypt = ar9170_get_decrypt_type(mac); + if (!(decrypt & AR9170_RX_ENC_SOFTWARE) && + decrypt != AR9170_ENC_ALG_NONE) { + if ((decrypt == AR9170_ENC_ALG_TKIP) && + (error & AR9170_RX_ERROR_MMIC)) + status->flag |= RX_FLAG_MMIC_ERROR; + + status->flag |= RX_FLAG_DECRYPTED; + } + + if (error & AR9170_RX_ERROR_DECRYPT && !ar->sniffer_enabled) + return -ENODATA; + + error &= ~(AR9170_RX_ERROR_MMIC | + AR9170_RX_ERROR_FCS | + AR9170_RX_ERROR_WRONG_RA | + AR9170_RX_ERROR_DECRYPT | + AR9170_RX_ERROR_PLCP); + + /* drop any other error frames */ + if (unlikely(error)) { + /* TODO: update netdevice's RX dropped/errors statistics */ + + if (net_ratelimit()) + wiphy_dbg(ar->hw->wiphy, "received frame with " + "suspicious error code (%#x).\n", error); + + return -EINVAL; + } + + chan = ar->channel; + if (chan) { + status->band = chan->band; + status->freq = chan->center_freq; + } + + switch (mac->status & AR9170_RX_STATUS_MODULATION) { + case AR9170_RX_STATUS_MODULATION_CCK: + if (mac->status & AR9170_RX_STATUS_SHORT_PREAMBLE) + status->flag |= RX_FLAG_SHORTPRE; + switch (head->plcp[0]) { + case AR9170_RX_PHY_RATE_CCK_1M: + status->rate_idx = 0; + break; + case AR9170_RX_PHY_RATE_CCK_2M: + status->rate_idx = 1; + break; + case AR9170_RX_PHY_RATE_CCK_5M: + status->rate_idx = 2; + break; + case AR9170_RX_PHY_RATE_CCK_11M: + status->rate_idx = 3; + break; + default: + if (net_ratelimit()) { + wiphy_err(ar->hw->wiphy, "invalid plcp cck " + "rate (%x).\n", head->plcp[0]); + } + + return -EINVAL; + } + break; + + case AR9170_RX_STATUS_MODULATION_DUPOFDM: + case AR9170_RX_STATUS_MODULATION_OFDM: + switch (head->plcp[0] & 0xf) { + case AR9170_TXRX_PHY_RATE_OFDM_6M: + status->rate_idx = 0; + break; + case AR9170_TXRX_PHY_RATE_OFDM_9M: + status->rate_idx = 1; + break; + case AR9170_TXRX_PHY_RATE_OFDM_12M: + status->rate_idx = 2; + break; + case AR9170_TXRX_PHY_RATE_OFDM_18M: + status->rate_idx = 3; + break; + case AR9170_TXRX_PHY_RATE_OFDM_24M: + status->rate_idx = 4; + break; + case AR9170_TXRX_PHY_RATE_OFDM_36M: + status->rate_idx = 5; + break; + case AR9170_TXRX_PHY_RATE_OFDM_48M: + status->rate_idx = 6; + break; + case AR9170_TXRX_PHY_RATE_OFDM_54M: + status->rate_idx = 7; + break; + default: + if (net_ratelimit()) { + wiphy_err(ar->hw->wiphy, "invalid plcp ofdm " + "rate (%x).\n", head->plcp[0]); + } + + return -EINVAL; + } + if (status->band == IEEE80211_BAND_2GHZ) + status->rate_idx += 4; + break; + + case AR9170_RX_STATUS_MODULATION_HT: + if (head->plcp[3] & 0x80) + status->flag |= RX_FLAG_40MHZ; + if (head->plcp[6] & 0x80) + status->flag |= RX_FLAG_SHORT_GI; + + status->rate_idx = clamp(0, 75, head->plcp[3] & 0x7f); + status->flag |= RX_FLAG_HT; + break; + + default: + BUG(); + return -ENOSYS; + } + + return 0; +} + +static void carl9170_rx_phy_status(struct ar9170 *ar, + struct ar9170_rx_phystatus *phy, struct ieee80211_rx_status *status) +{ + int i; + + BUILD_BUG_ON(sizeof(struct ar9170_rx_phystatus) != 20); + + for (i = 0; i < 3; i++) + if (phy->rssi[i] != 0x80) + status->antenna |= BIT(i); + + /* post-process RSSI */ + for (i = 0; i < 7; i++) + if (phy->rssi[i] & 0x80) + phy->rssi[i] = ((phy->rssi[i] & 0x7f) + 1) & 0x7f; + + /* TODO: we could do something with phy_errors */ + status->signal = ar->noise[0] + phy->rssi_combined; +} + +static struct sk_buff *carl9170_rx_copy_data(u8 *buf, int len) +{ + struct sk_buff *skb; + int reserved = 0; + struct ieee80211_hdr *hdr = (void *) buf; + + if (ieee80211_is_data_qos(hdr->frame_control)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + reserved += NET_IP_ALIGN; + + if (*qc & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT) + reserved += NET_IP_ALIGN; + } + + if (ieee80211_has_a4(hdr->frame_control)) + reserved += NET_IP_ALIGN; + + reserved = 32 + (reserved & NET_IP_ALIGN); + + skb = dev_alloc_skb(len + reserved); + if (likely(skb)) { + skb_reserve(skb, reserved); + memcpy(skb_put(skb, len), buf, len); + } + + return skb; +} + +static u8 *carl9170_find_ie(u8 *data, unsigned int len, u8 ie) +{ + struct ieee80211_mgmt *mgmt = (void *)data; + u8 *pos, *end; + + pos = (u8 *)mgmt->u.beacon.variable; + end = data + len; + while (pos < end) { + if (pos + 2 + pos[1] > end) + return NULL; + + if (pos[0] == ie) + return pos; + + pos += 2 + pos[1]; + } + return NULL; +} + +/* + * NOTE: + * + * The firmware is in charge of waking up the device just before + * the AP is expected to transmit the next beacon. + * + * This leaves the driver with the important task of deciding when + * to set the PHY back to bed again. + */ +static void carl9170_ps_beacon(struct ar9170 *ar, void *data, unsigned int len) +{ + struct ieee80211_hdr *hdr = (void *) data; + struct ieee80211_tim_ie *tim_ie; + u8 *tim; + u8 tim_len; + bool cam; + + if (likely(!(ar->hw->conf.flags & IEEE80211_CONF_PS))) + return; + + /* check if this really is a beacon */ + if (!ieee80211_is_beacon(hdr->frame_control)) + return; + + /* min. beacon length + FCS_LEN */ + if (len <= 40 + FCS_LEN) + return; + + /* and only beacons from the associated BSSID, please */ + if (compare_ether_addr(hdr->addr3, ar->common.curbssid) || + !ar->common.curaid) + return; + + ar->ps.last_beacon = jiffies; + + tim = carl9170_find_ie(data, len - FCS_LEN, WLAN_EID_TIM); + if (!tim) + return; + + if (tim[1] < sizeof(*tim_ie)) + return; + + tim_len = tim[1]; + tim_ie = (struct ieee80211_tim_ie *) &tim[2]; + + if (!WARN_ON_ONCE(!ar->hw->conf.ps_dtim_period)) + ar->ps.dtim_counter = (tim_ie->dtim_count - 1) % + ar->hw->conf.ps_dtim_period; + + /* Check whenever the PHY can be turned off again. */ + + /* 1. What about buffered unicast traffic for our AID? */ + cam = ieee80211_check_tim(tim_ie, tim_len, ar->common.curaid); + + /* 2. Maybe the AP wants to send multicast/broadcast data? */ + cam = !!(tim_ie->bitmap_ctrl & 0x01); + + if (!cam) { + /* back to low-power land. */ + ar->ps.off_override &= ~PS_OFF_BCN; + carl9170_ps_check(ar); + } else { + /* force CAM */ + ar->ps.off_override |= PS_OFF_BCN; + } +} + +/* + * If the frame alignment is right (or the kernel has + * CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS), and there + * is only a single MPDU in the USB frame, then we could + * submit to mac80211 the SKB directly. However, since + * there may be multiple packets in one SKB in stream + * mode, and we need to observe the proper ordering, + * this is non-trivial. + */ + +static void carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len) +{ + struct ar9170_rx_head *head; + struct ar9170_rx_macstatus *mac; + struct ar9170_rx_phystatus *phy = NULL; + struct ieee80211_rx_status status; + struct sk_buff *skb; + int mpdu_len; + + if (!IS_STARTED(ar)) + return; + + if (unlikely(len < sizeof(*mac))) { + ar->rx_dropped++; + return; + } + + mpdu_len = len - sizeof(*mac); + + mac = (void *)(buf + mpdu_len); + if (unlikely(mac->error & AR9170_RX_ERROR_FATAL)) { + ar->rx_dropped++; + return; + } + + switch (mac->status & AR9170_RX_STATUS_MPDU) { + case AR9170_RX_STATUS_MPDU_FIRST: + /* Aggregated MPDUs start with an PLCP header */ + if (likely(mpdu_len >= sizeof(struct ar9170_rx_head))) { + head = (void *) buf; + + /* + * The PLCP header needs to be cached for the + * following MIDDLE + LAST A-MPDU packets. + * + * So, if you are wondering why all frames seem + * to share a common RX status information, + * then you have the answer right here... + */ + memcpy(&ar->rx_plcp, (void *) buf, + sizeof(struct ar9170_rx_head)); + + mpdu_len -= sizeof(struct ar9170_rx_head); + buf += sizeof(struct ar9170_rx_head); + + ar->rx_has_plcp = true; + } else { + if (net_ratelimit()) { + wiphy_err(ar->hw->wiphy, "plcp info " + "is clipped.\n"); + } + + ar->rx_dropped++; + return; + } + break; + + case AR9170_RX_STATUS_MPDU_LAST: + /* + * The last frame of an A-MPDU has an extra tail + * which does contain the phy status of the whole + * aggregate. + */ + + if (likely(mpdu_len >= sizeof(struct ar9170_rx_phystatus))) { + mpdu_len -= sizeof(struct ar9170_rx_phystatus); + phy = (void *)(buf + mpdu_len); + } else { + if (net_ratelimit()) { + wiphy_err(ar->hw->wiphy, "frame tail " + "is clipped.\n"); + } + + ar->rx_dropped++; + return; + } + + case AR9170_RX_STATUS_MPDU_MIDDLE: + /* These are just data + mac status */ + if (unlikely(!ar->rx_has_plcp)) { + if (!net_ratelimit()) + return; + + wiphy_err(ar->hw->wiphy, "rx stream does not start " + "with a first_mpdu frame tag.\n"); + + ar->rx_dropped++; + return; + } + + head = &ar->rx_plcp; + break; + + case AR9170_RX_STATUS_MPDU_SINGLE: + /* single mpdu has both: plcp (head) and phy status (tail) */ + head = (void *) buf; + + mpdu_len -= sizeof(struct ar9170_rx_head); + mpdu_len -= sizeof(struct ar9170_rx_phystatus); + + buf += sizeof(struct ar9170_rx_head); + phy = (void *)(buf + mpdu_len); + break; + + default: + BUG_ON(1); + break; + } + + /* FC + DU + RA + FCS */ + if (unlikely(mpdu_len < (2 + 2 + 6 + FCS_LEN))) { + ar->rx_dropped++; + return; + } + + memset(&status, 0, sizeof(status)); + if (unlikely(carl9170_rx_mac_status(ar, head, mac, &status))) { + ar->rx_dropped++; + return; + } + + if (phy) + carl9170_rx_phy_status(ar, phy, &status); + + carl9170_ps_beacon(ar, buf, mpdu_len); + + skb = carl9170_rx_copy_data(buf, mpdu_len); + if (likely(skb)) { + memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); + ieee80211_rx(ar->hw, skb); + } else { + ar->rx_dropped++; + } +} + +static void carl9170_rx_untie_cmds(struct ar9170 *ar, const u8 *respbuf, + const unsigned int resplen) +{ + struct carl9170_rsp *cmd; + int i = 0; + + while (i < resplen) { + cmd = (void *) &respbuf[i]; + + i += cmd->hdr.len + 4; + if (unlikely(i > resplen)) + break; + + carl9170_handle_command_response(ar, cmd, cmd->hdr.len + 4); + } + + if (unlikely(i != resplen)) { + if (!net_ratelimit()) + return; + + wiphy_err(ar->hw->wiphy, "malformed firmware trap:\n"); + print_hex_dump_bytes("rxcmd:", DUMP_PREFIX_OFFSET, + respbuf, resplen); + } +} + +static void __carl9170_rx(struct ar9170 *ar, u8 *buf, unsigned int len) +{ + unsigned int i = 0; + + /* weird thing, but this is the same in the original driver */ + while (len > 2 && i < 12 && buf[0] == 0xff && buf[1] == 0xff) { + i += 2; + len -= 2; + buf += 2; + } + + if (unlikely(len < 4)) + return; + + /* found the 6 * 0xffff marker? */ + if (i == 12) + carl9170_rx_untie_cmds(ar, buf, len); + else + carl9170_handle_mpdu(ar, buf, len); +} + +static void carl9170_rx_stream(struct ar9170 *ar, void *buf, unsigned int len) +{ + unsigned int tlen, wlen = 0, clen = 0; + struct ar9170_stream *rx_stream; + u8 *tbuf; + + tbuf = buf; + tlen = len; + + while (tlen >= 4) { + rx_stream = (void *) tbuf; + clen = le16_to_cpu(rx_stream->length); + wlen = ALIGN(clen, 4); + + /* check if this is stream has a valid tag.*/ + if (rx_stream->tag != cpu_to_le16(AR9170_RX_STREAM_TAG)) { + /* + * TODO: handle the highly unlikely event that the + * corrupted stream has the TAG at the right position. + */ + + /* check if the frame can be repaired. */ + if (!ar->rx_failover_missing) { + + /* this is not "short read". */ + if (net_ratelimit()) { + wiphy_err(ar->hw->wiphy, + "missing tag!\n"); + } + + __carl9170_rx(ar, tbuf, tlen); + return; + } + + if (ar->rx_failover_missing > tlen) { + if (net_ratelimit()) { + wiphy_err(ar->hw->wiphy, + "possible multi " + "stream corruption!\n"); + goto err_telluser; + } else { + goto err_silent; + } + } + + memcpy(skb_put(ar->rx_failover, tlen), tbuf, tlen); + ar->rx_failover_missing -= tlen; + + if (ar->rx_failover_missing <= 0) { + /* + * nested carl9170_rx_stream call! + * + * termination is guranteed, even when the + * combined frame also have an element with + * a bad tag. + */ + + ar->rx_failover_missing = 0; + carl9170_rx_stream(ar, ar->rx_failover->data, + ar->rx_failover->len); + + skb_reset_tail_pointer(ar->rx_failover); + skb_trim(ar->rx_failover, 0); + } + + return; + } + + /* check if stream is clipped */ + if (wlen > tlen - 4) { + if (ar->rx_failover_missing) { + /* TODO: handle double stream corruption. */ + if (net_ratelimit()) { + wiphy_err(ar->hw->wiphy, "double rx " + "stream corruption!\n"); + goto err_telluser; + } else { + goto err_silent; + } + } + + /* + * save incomplete data set. + * the firmware will resend the missing bits when + * the rx - descriptor comes round again. + */ + + memcpy(skb_put(ar->rx_failover, tlen), tbuf, tlen); + ar->rx_failover_missing = clen - tlen; + return; + } + __carl9170_rx(ar, rx_stream->payload, clen); + + tbuf += wlen + 4; + tlen -= wlen + 4; + } + + if (tlen) { + if (net_ratelimit()) { + wiphy_err(ar->hw->wiphy, "%d bytes of unprocessed " + "data left in rx stream!\n", tlen); + } + + goto err_telluser; + } + + return; + +err_telluser: + wiphy_err(ar->hw->wiphy, "damaged RX stream data [want:%d, " + "data:%d, rx:%d, pending:%d ]\n", clen, wlen, tlen, + ar->rx_failover_missing); + + if (ar->rx_failover_missing) + print_hex_dump_bytes("rxbuf:", DUMP_PREFIX_OFFSET, + ar->rx_failover->data, + ar->rx_failover->len); + + print_hex_dump_bytes("stream:", DUMP_PREFIX_OFFSET, + buf, len); + + wiphy_err(ar->hw->wiphy, "please check your hardware and cables, if " + "you see this message frequently.\n"); + +err_silent: + if (ar->rx_failover_missing) { + skb_reset_tail_pointer(ar->rx_failover); + skb_trim(ar->rx_failover, 0); + ar->rx_failover_missing = 0; + } +} + +void carl9170_rx(struct ar9170 *ar, void *buf, unsigned int len) +{ + if (ar->fw.rx_stream) + carl9170_rx_stream(ar, buf, len); + else + __carl9170_rx(ar, buf, len); +} diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c new file mode 100644 index 000000000000..e0d2374e0c77 --- /dev/null +++ b/drivers/net/wireless/ath/carl9170/tx.c @@ -0,0 +1,1373 @@ +/* + * Atheros CARL9170 driver + * + * 802.11 xmit & status routines + * + * Copyright 2008, Johannes Berg + * Copyright 2009, 2010, Christian Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include "carl9170.h" +#include "hw.h" +#include "cmd.h" + +static inline unsigned int __carl9170_get_queue(struct ar9170 *ar, + unsigned int queue) +{ + if (unlikely(modparam_noht)) { + return queue; + } else { + /* + * This is just another workaround, until + * someone figures out how to get QoS and + * AMPDU to play nicely together. + */ + + return 2; /* AC_BE */ + } +} + +static inline unsigned int carl9170_get_queue(struct ar9170 *ar, + struct sk_buff *skb) +{ + return __carl9170_get_queue(ar, skb_get_queue_mapping(skb)); +} + +static bool is_mem_full(struct ar9170 *ar) +{ + return (DIV_ROUND_UP(IEEE80211_MAX_FRAME_LEN, ar->fw.mem_block_size) > + atomic_read(&ar->mem_free_blocks)); +} + +static void carl9170_tx_accounting(struct ar9170 *ar, struct sk_buff *skb) +{ + int queue, i; + bool mem_full; + + atomic_inc(&ar->tx_total_queued); + + queue = skb_get_queue_mapping(skb); + spin_lock_bh(&ar->tx_stats_lock); + + /* + * The driver has to accept the frame, regardless if the queue is + * full to the brim, or not. We have to do the queuing internally, + * since mac80211 assumes that a driver which can operate with + * aggregated frames does not reject frames for this reason. + */ + ar->tx_stats[queue].len++; + ar->tx_stats[queue].count++; + + mem_full = is_mem_full(ar); + for (i = 0; i < ar->hw->queues; i++) { + if (mem_full || ar->tx_stats[i].len >= ar->tx_stats[i].limit) { + ieee80211_stop_queue(ar->hw, i); + ar->queue_stop_timeout[i] = jiffies; + } + } + + spin_unlock_bh(&ar->tx_stats_lock); +} + +static void carl9170_tx_accounting_free(struct ar9170 *ar, struct sk_buff *skb) +{ + struct ieee80211_tx_info *txinfo; + int queue; + + txinfo = IEEE80211_SKB_CB(skb); + queue = skb_get_queue_mapping(skb); + + spin_lock_bh(&ar->tx_stats_lock); + + ar->tx_stats[queue].len--; + + if (!is_mem_full(ar)) { + unsigned int i; + for (i = 0; i < ar->hw->queues; i++) { + if (ar->tx_stats[i].len >= CARL9170_NUM_TX_LIMIT_SOFT) + continue; + + if (ieee80211_queue_stopped(ar->hw, i)) { + unsigned long tmp; + + tmp = jiffies - ar->queue_stop_timeout[i]; + if (tmp > ar->max_queue_stop_timeout[i]) + ar->max_queue_stop_timeout[i] = tmp; + } + + ieee80211_wake_queue(ar->hw, i); + } + } + + spin_unlock_bh(&ar->tx_stats_lock); + if (atomic_dec_and_test(&ar->tx_total_queued)) + complete(&ar->tx_flush); +} + +static int carl9170_alloc_dev_space(struct ar9170 *ar, struct sk_buff *skb) +{ + struct _carl9170_tx_superframe *super = (void *) skb->data; + unsigned int chunks; + int cookie = -1; + + atomic_inc(&ar->mem_allocs); + + chunks = DIV_ROUND_UP(skb->len, ar->fw.mem_block_size); + if (unlikely(atomic_sub_return(chunks, &ar->mem_free_blocks) < 0)) { + atomic_add(chunks, &ar->mem_free_blocks); + return -ENOSPC; + } + + spin_lock_bh(&ar->mem_lock); + cookie = bitmap_find_free_region(ar->mem_bitmap, ar->fw.mem_blocks, 0); + spin_unlock_bh(&ar->mem_lock); + + if (unlikely(cookie < 0)) { + atomic_add(chunks, &ar->mem_free_blocks); + return -ENOSPC; + } + + super = (void *) skb->data; + + /* + * Cookie #0 serves two special purposes: + * 1. The firmware might use it generate BlockACK frames + * in responds of an incoming BlockAckReqs. + * + * 2. Prevent double-free bugs. + */ + super->s.cookie = (u8) cookie + 1; + return 0; +} + +static void carl9170_release_dev_space(struct ar9170 *ar, struct sk_buff *skb) +{ + struct _carl9170_tx_superframe *super = (void *) skb->data; + int cookie; + + /* make a local copy of the cookie */ + cookie = super->s.cookie; + /* invalidate cookie */ + super->s.cookie = 0; + + /* + * Do a out-of-bounds check on the cookie: + * + * * cookie "0" is reserved and won't be assigned to any + * out-going frame. Internally however, it is used to + * mark no longer/un-accounted frames and serves as a + * cheap way of preventing frames from being freed + * twice by _accident_. NB: There is a tiny race... + * + * * obviously, cookie number is limited by the amount + * of available memory blocks, so the number can + * never execeed the mem_blocks count. + */ + if (unlikely(WARN_ON_ONCE(cookie == 0) || + WARN_ON_ONCE(cookie > ar->fw.mem_blocks))) + return; + + atomic_add(DIV_ROUND_UP(skb->len, ar->fw.mem_block_size), + &ar->mem_free_blocks); + + spin_lock_bh(&ar->mem_lock); + bitmap_release_region(ar->mem_bitmap, cookie - 1, 0); + spin_unlock_bh(&ar->mem_lock); +} + +/* Called from any context */ +static void carl9170_tx_release(struct kref *ref) +{ + struct ar9170 *ar; + struct carl9170_tx_info *arinfo; + struct ieee80211_tx_info *txinfo; + struct sk_buff *skb; + + arinfo = container_of(ref, struct carl9170_tx_info, ref); + txinfo = container_of((void *) arinfo, struct ieee80211_tx_info, + rate_driver_data); + skb = container_of((void *) txinfo, struct sk_buff, cb); + + ar = arinfo->ar; + if (WARN_ON_ONCE(!ar)) + return; + + BUILD_BUG_ON( + offsetof(struct ieee80211_tx_info, status.ampdu_ack_len) != 23); + + memset(&txinfo->status.ampdu_ack_len, 0, + sizeof(struct ieee80211_tx_info) - + offsetof(struct ieee80211_tx_info, status.ampdu_ack_len)); + + if (atomic_read(&ar->tx_total_queued)) + ar->tx_schedule = true; + + if (txinfo->flags & IEEE80211_TX_CTL_AMPDU) { + if (!atomic_read(&ar->tx_ampdu_upload)) + ar->tx_ampdu_schedule = true; + + if (txinfo->flags & IEEE80211_TX_STAT_AMPDU) { + txinfo->status.ampdu_len = txinfo->pad[0]; + txinfo->status.ampdu_ack_len = txinfo->pad[1]; + txinfo->pad[0] = txinfo->pad[1] = 0; + } else if (txinfo->flags & IEEE80211_TX_STAT_ACK) { + /* + * drop redundant tx_status reports: + * + * 1. ampdu_ack_len of the final tx_status does + * include the feedback of this particular frame. + * + * 2. tx_status_irqsafe only queues up to 128 + * tx feedback reports and discards the rest. + * + * 3. minstrel_ht is picky, it only accepts + * reports of frames with the TX_STATUS_AMPDU flag. + */ + + dev_kfree_skb_any(skb); + return; + } else { + /* + * Frame has failed, but we want to keep it in + * case it was lost due to a power-state + * transition. + */ + } + } + + skb_pull(skb, sizeof(struct _carl9170_tx_superframe)); + ieee80211_tx_status_irqsafe(ar->hw, skb); +} + +void carl9170_tx_get_skb(struct sk_buff *skb) +{ + struct carl9170_tx_info *arinfo = (void *) + (IEEE80211_SKB_CB(skb))->rate_driver_data; + kref_get(&arinfo->ref); +} + +int carl9170_tx_put_skb(struct sk_buff *skb) +{ + struct carl9170_tx_info *arinfo = (void *) + (IEEE80211_SKB_CB(skb))->rate_driver_data; + + return kref_put(&arinfo->ref, carl9170_tx_release); +} + +/* Caller must hold the tid_info->lock & rcu_read_lock */ +static void carl9170_tx_shift_bm(struct ar9170 *ar, + struct carl9170_sta_tid *tid_info, u16 seq) +{ + u16 off; + + off = SEQ_DIFF(seq, tid_info->bsn); + + if (WARN_ON_ONCE(off >= CARL9170_BAW_BITS)) + return; + + /* + * Sanity check. For each MPDU we set the bit in bitmap and + * clear it once we received the tx_status. + * But if the bit is already cleared then we've been bitten + * by a bug. + */ + WARN_ON_ONCE(!test_and_clear_bit(off, tid_info->bitmap)); + + off = SEQ_DIFF(tid_info->snx, tid_info->bsn); + if (WARN_ON_ONCE(off >= CARL9170_BAW_BITS)) + return; + + if (!bitmap_empty(tid_info->bitmap, off)) + off = find_first_bit(tid_info->bitmap, off); + + tid_info->bsn += off; + tid_info->bsn &= 0x0fff; + + bitmap_shift_right(tid_info->bitmap, tid_info->bitmap, + off, CARL9170_BAW_BITS); +} + +static void carl9170_tx_status_process_ampdu(struct ar9170 *ar, + struct sk_buff *skb, struct ieee80211_tx_info *txinfo) +{ + struct _carl9170_tx_superframe *super = (void *) skb->data; + struct ieee80211_hdr *hdr = (void *) super->frame_data; + struct ieee80211_tx_info *tx_info; + struct carl9170_tx_info *ar_info; + struct carl9170_sta_info *sta_info; + struct ieee80211_sta *sta; + struct carl9170_sta_tid *tid_info; + struct ieee80211_vif *vif; + unsigned int vif_id; + u8 tid; + + if (!(txinfo->flags & IEEE80211_TX_CTL_AMPDU) || + txinfo->flags & IEEE80211_TX_CTL_INJECTED) + return; + + tx_info = IEEE80211_SKB_CB(skb); + ar_info = (void *) tx_info->rate_driver_data; + + vif_id = (super->s.misc & CARL9170_TX_SUPER_MISC_VIF_ID) >> + CARL9170_TX_SUPER_MISC_VIF_ID_S; + + if (WARN_ON_ONCE(vif_id >= AR9170_MAX_VIRTUAL_MAC)) + return; + + rcu_read_lock(); + vif = rcu_dereference(ar->vif_priv[vif_id].vif); + if (unlikely(!vif)) + goto out_rcu; + + /* + * Normally we should use wrappers like ieee80211_get_DA to get + * the correct peer ieee80211_sta. + * + * But there is a problem with indirect traffic (broadcasts, or + * data which is designated for other stations) in station mode. + * The frame will be directed to the AP for distribution and not + * to the actual destination. + */ + sta = ieee80211_find_sta(vif, hdr->addr1); + if (unlikely(!sta)) + goto out_rcu; + + tid = get_tid_h(hdr); + + sta_info = (void *) sta->drv_priv; + tid_info = rcu_dereference(sta_info->agg[tid]); + if (!tid_info) + goto out_rcu; + + spin_lock_bh(&tid_info->lock); + if (likely(tid_info->state >= CARL9170_TID_STATE_IDLE)) + carl9170_tx_shift_bm(ar, tid_info, get_seq_h(hdr)); + + if (sta_info->stats[tid].clear) { + sta_info->stats[tid].clear = false; + sta_info->stats[tid].ampdu_len = 0; + sta_info->stats[tid].ampdu_ack_len = 0; + } + + sta_info->stats[tid].ampdu_len++; + if (txinfo->status.rates[0].count == 1) + sta_info->stats[tid].ampdu_ack_len++; + + if (super->f.mac_control & cpu_to_le16(AR9170_TX_MAC_IMM_BA)) { + txinfo->pad[0] = sta_info->stats[tid].ampdu_len; + txinfo->pad[1] = sta_info->stats[tid].ampdu_ack_len; + txinfo->flags |= IEEE80211_TX_STAT_AMPDU; + sta_info->stats[tid].clear = true; + } + spin_unlock_bh(&tid_info->lock); + +out_rcu: + rcu_read_unlock(); +} + +void carl9170_tx_status(struct ar9170 *ar, struct sk_buff *skb, + const bool success) +{ + struct ieee80211_tx_info *txinfo; + + carl9170_tx_accounting_free(ar, skb); + + txinfo = IEEE80211_SKB_CB(skb); + + if (success) + txinfo->flags |= IEEE80211_TX_STAT_ACK; + else + ar->tx_ack_failures++; + + if (txinfo->flags & IEEE80211_TX_CTL_AMPDU) + carl9170_tx_status_process_ampdu(ar, skb, txinfo); + + carl9170_tx_put_skb(skb); +} + +/* This function may be called form any context */ +void carl9170_tx_callback(struct ar9170 *ar, struct sk_buff *skb) +{ + struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb); + + atomic_dec(&ar->tx_total_pending); + + if (txinfo->flags & IEEE80211_TX_CTL_AMPDU) + atomic_dec(&ar->tx_ampdu_upload); + + if (carl9170_tx_put_skb(skb)) + tasklet_hi_schedule(&ar->usb_tasklet); +} + +static struct sk_buff *carl9170_get_queued_skb(struct ar9170 *ar, u8 cookie, + struct sk_buff_head *queue) +{ + struct sk_buff *skb; + + spin_lock_bh(&queue->lock); + skb_queue_walk(queue, skb) { + struct _carl9170_tx_superframe *txc = (void *) skb->data; + + if (txc->s.cookie != cookie) + continue; + + __skb_unlink(skb, queue); + spin_unlock_bh(&queue->lock); + + carl9170_release_dev_space(ar, skb); + return skb; + } + spin_unlock_bh(&queue->lock); + + return NULL; +} + +static void carl9170_tx_fill_rateinfo(struct ar9170 *ar, unsigned int rix, + unsigned int tries, struct ieee80211_tx_info *txinfo) +{ + unsigned int i; + + for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { + if (txinfo->status.rates[i].idx < 0) + break; + + if (i == rix) { + txinfo->status.rates[i].count = tries; + i++; + break; + } + } + + for (; i < IEEE80211_TX_MAX_RATES; i++) { + txinfo->status.rates[i].idx = -1; + txinfo->status.rates[i].count = 0; + } +} + +static void carl9170_check_queue_stop_timeout(struct ar9170 *ar) +{ + int i; + struct sk_buff *skb; + struct ieee80211_tx_info *txinfo; + struct carl9170_tx_info *arinfo; + bool restart = false; + + for (i = 0; i < ar->hw->queues; i++) { + spin_lock_bh(&ar->tx_status[i].lock); + + skb = skb_peek(&ar->tx_status[i]); + + if (!skb) + goto next; + + txinfo = IEEE80211_SKB_CB(skb); + arinfo = (void *) txinfo->rate_driver_data; + + if (time_is_before_jiffies(arinfo->timeout + + msecs_to_jiffies(CARL9170_QUEUE_STUCK_TIMEOUT)) == true) + restart = true; + +next: + spin_unlock_bh(&ar->tx_status[i].lock); + } + + if (restart) { + /* + * At least one queue has been stuck for long enough. + * Give the device a kick and hope it gets back to + * work. + * + * possible reasons may include: + * - frames got lost/corrupted (bad connection to the device) + * - stalled rx processing/usb controller hiccups + * - firmware errors/bugs + * - every bug you can think of. + * - all bugs you can't... + * - ... + */ + carl9170_restart(ar, CARL9170_RR_STUCK_TX); + } +} + +void carl9170_tx_janitor(struct work_struct *work) +{ + struct ar9170 *ar = container_of(work, struct ar9170, + tx_janitor.work); + if (!IS_STARTED(ar)) + return; + + ar->tx_janitor_last_run = jiffies; + + carl9170_check_queue_stop_timeout(ar); + + if (!atomic_read(&ar->tx_total_queued)) + return; + + ieee80211_queue_delayed_work(ar->hw, &ar->tx_janitor, + msecs_to_jiffies(CARL9170_TX_TIMEOUT)); +} + +static void __carl9170_tx_process_status(struct ar9170 *ar, + const uint8_t cookie, const uint8_t info) +{ + struct sk_buff *skb; + struct ieee80211_tx_info *txinfo; + struct carl9170_tx_info *arinfo; + unsigned int r, t, q; + bool success = true; + + q = ar9170_qmap[info & CARL9170_TX_STATUS_QUEUE]; + + skb = carl9170_get_queued_skb(ar, cookie, &ar->tx_status[q]); + if (!skb) { + /* + * We have lost the race to another thread. + */ + + return ; + } + + txinfo = IEEE80211_SKB_CB(skb); + arinfo = (void *) txinfo->rate_driver_data; + + if (!(info & CARL9170_TX_STATUS_SUCCESS)) + success = false; + + r = (info & CARL9170_TX_STATUS_RIX) >> CARL9170_TX_STATUS_RIX_S; + t = (info & CARL9170_TX_STATUS_TRIES) >> CARL9170_TX_STATUS_TRIES_S; + + carl9170_tx_fill_rateinfo(ar, r, t, txinfo); + carl9170_tx_status(ar, skb, success); +} + +void carl9170_tx_process_status(struct ar9170 *ar, + const struct carl9170_rsp *cmd) +{ + unsigned int i; + + for (i = 0; i < cmd->hdr.ext; i++) { + if (WARN_ON(i > ((cmd->hdr.len / 2) + 1))) { + print_hex_dump_bytes("UU:", DUMP_PREFIX_NONE, + (void *) cmd, cmd->hdr.len + 4); + break; + } + + __carl9170_tx_process_status(ar, cmd->_tx_status[i].cookie, + cmd->_tx_status[i].info); + } +} + +static __le32 carl9170_tx_physet(struct ar9170 *ar, + struct ieee80211_tx_info *info, struct ieee80211_tx_rate *txrate) +{ + struct ieee80211_rate *rate = NULL; + u32 power, chains; + __le32 tmp; + + tmp = cpu_to_le32(0); + + if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + tmp |= cpu_to_le32(AR9170_TX_PHY_BW_40MHZ << + AR9170_TX_PHY_BW_S); + /* this works because 40 MHz is 2 and dup is 3 */ + if (txrate->flags & IEEE80211_TX_RC_DUP_DATA) + tmp |= cpu_to_le32(AR9170_TX_PHY_BW_40MHZ_DUP << + AR9170_TX_PHY_BW_S); + + if (txrate->flags & IEEE80211_TX_RC_SHORT_GI) + tmp |= cpu_to_le32(AR9170_TX_PHY_SHORT_GI); + + if (txrate->flags & IEEE80211_TX_RC_MCS) { + u32 r = txrate->idx; + u8 *txpower; + + /* heavy clip control */ + tmp |= cpu_to_le32((r & 0x7) << + AR9170_TX_PHY_TX_HEAVY_CLIP_S); + + if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) { + if (info->band == IEEE80211_BAND_5GHZ) + txpower = ar->power_5G_ht40; + else + txpower = ar->power_2G_ht40; + } else { + if (info->band == IEEE80211_BAND_5GHZ) + txpower = ar->power_5G_ht20; + else + txpower = ar->power_2G_ht20; + } + + power = txpower[r & 7]; + + /* +1 dBm for HT40 */ + if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + power += 2; + + r <<= AR9170_TX_PHY_MCS_S; + BUG_ON(r & ~AR9170_TX_PHY_MCS); + + tmp |= cpu_to_le32(r & AR9170_TX_PHY_MCS); + tmp |= cpu_to_le32(AR9170_TX_PHY_MOD_HT); + + /* + * green field preamble does not work. + * + * if (txrate->flags & IEEE80211_TX_RC_GREEN_FIELD) + * tmp |= cpu_to_le32(AR9170_TX_PHY_GREENFIELD); + */ + } else { + u8 *txpower; + u32 mod; + u32 phyrate; + u8 idx = txrate->idx; + + if (info->band != IEEE80211_BAND_2GHZ) { + idx += 4; + txpower = ar->power_5G_leg; + mod = AR9170_TX_PHY_MOD_OFDM; + } else { + if (idx < 4) { + txpower = ar->power_2G_cck; + mod = AR9170_TX_PHY_MOD_CCK; + } else { + mod = AR9170_TX_PHY_MOD_OFDM; + txpower = ar->power_2G_ofdm; + } + } + + rate = &__carl9170_ratetable[idx]; + + phyrate = rate->hw_value & 0xF; + power = txpower[(rate->hw_value & 0x30) >> 4]; + phyrate <<= AR9170_TX_PHY_MCS_S; + + tmp |= cpu_to_le32(mod); + tmp |= cpu_to_le32(phyrate); + + /* + * short preamble seems to be broken too. + * + * if (txrate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) + * tmp |= cpu_to_le32(AR9170_TX_PHY_SHORT_PREAMBLE); + */ + } + power <<= AR9170_TX_PHY_TX_PWR_S; + power &= AR9170_TX_PHY_TX_PWR; + tmp |= cpu_to_le32(power); + + /* set TX chains */ + if (ar->eeprom.tx_mask == 1) { + chains = AR9170_TX_PHY_TXCHAIN_1; + } else { + chains = AR9170_TX_PHY_TXCHAIN_2; + + /* >= 36M legacy OFDM - use only one chain */ + if (rate && rate->bitrate >= 360 && + !(txrate->flags & IEEE80211_TX_RC_MCS)) + chains = AR9170_TX_PHY_TXCHAIN_1; + } + tmp |= cpu_to_le32(chains << AR9170_TX_PHY_TXCHAIN_S); + + return tmp; +} + +static bool carl9170_tx_rts_check(struct ar9170 *ar, + struct ieee80211_tx_rate *rate, + bool ampdu, bool multi) +{ + switch (ar->erp_mode) { + case CARL9170_ERP_AUTO: + if (ampdu) + break; + + case CARL9170_ERP_MAC80211: + if (!(rate->flags & IEEE80211_TX_RC_USE_RTS_CTS)) + break; + + case CARL9170_ERP_RTS: + if (likely(!multi)) + return true; + + default: + break; + } + + return false; +} + +static bool carl9170_tx_cts_check(struct ar9170 *ar, + struct ieee80211_tx_rate *rate) +{ + switch (ar->erp_mode) { + case CARL9170_ERP_AUTO: + case CARL9170_ERP_MAC80211: + if (!(rate->flags & IEEE80211_TX_RC_USE_CTS_PROTECT)) + break; + + case CARL9170_ERP_CTS: + return true; + + default: + break; + } + + return false; +} + +static int carl9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr; + struct _carl9170_tx_superframe *txc; + struct carl9170_vif_info *cvif; + struct ieee80211_tx_info *info; + struct ieee80211_tx_rate *txrate; + struct ieee80211_sta *sta; + struct carl9170_tx_info *arinfo; + unsigned int hw_queue; + int i; + u16 keytype = 0; + u16 len, icv = 0; + bool ampdu, no_ack; + + BUILD_BUG_ON(sizeof(*arinfo) > sizeof(info->rate_driver_data)); + BUILD_BUG_ON(sizeof(struct _carl9170_tx_superdesc) != + CARL9170_TX_SUPERDESC_LEN); + + BUILD_BUG_ON(sizeof(struct _ar9170_tx_hwdesc) != + AR9170_TX_HWDESC_LEN); + + BUILD_BUG_ON(IEEE80211_TX_MAX_RATES < CARL9170_TX_MAX_RATES); + + hw_queue = ar9170_qmap[carl9170_get_queue(ar, skb)]; + + hdr = (void *)skb->data; + info = IEEE80211_SKB_CB(skb); + len = skb->len; + + /* + * Note: If the frame was sent through a monitor interface, + * the ieee80211_vif pointer can be NULL. + */ + if (likely(info->control.vif)) + cvif = (void *) info->control.vif->drv_priv; + else + cvif = NULL; + + sta = info->control.sta; + + txc = (void *)skb_push(skb, sizeof(*txc)); + memset(txc, 0, sizeof(*txc)); + + ampdu = !!(info->flags & IEEE80211_TX_CTL_AMPDU); + no_ack = !!(info->flags & IEEE80211_TX_CTL_NO_ACK); + + if (info->control.hw_key) { + icv = info->control.hw_key->icv_len; + + switch (info->control.hw_key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + case WLAN_CIPHER_SUITE_TKIP: + keytype = AR9170_TX_MAC_ENCR_RC4; + break; + case WLAN_CIPHER_SUITE_CCMP: + keytype = AR9170_TX_MAC_ENCR_AES; + break; + default: + WARN_ON(1); + goto err_out; + } + } + + BUILD_BUG_ON(AR9170_MAX_VIRTUAL_MAC > + ((CARL9170_TX_SUPER_MISC_VIF_ID >> + CARL9170_TX_SUPER_MISC_VIF_ID_S) + 1)); + + txc->s.len = cpu_to_le16(len + sizeof(*txc)); + txc->f.length = cpu_to_le16(len + icv + 4); + SET_VAL(CARL9170_TX_SUPER_MISC_VIF_ID, txc->s.misc, + cvif ? cvif->id : 0); + + txc->f.mac_control = cpu_to_le16(AR9170_TX_MAC_HW_DURATION | + AR9170_TX_MAC_BACKOFF); + + SET_VAL(CARL9170_TX_SUPER_MISC_QUEUE, txc->s.misc, hw_queue); + + txc->f.mac_control |= cpu_to_le16(hw_queue << AR9170_TX_MAC_QOS_S); + txc->f.mac_control |= cpu_to_le16(keytype); + txc->f.phy_control = cpu_to_le32(0); + + if (no_ack) + txc->f.mac_control |= cpu_to_le16(AR9170_TX_MAC_NO_ACK); + + if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) + txc->s.misc |= CARL9170_TX_SUPER_MISC_CAB; + + txrate = &info->control.rates[0]; + if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack)) + txc->f.mac_control |= cpu_to_le16(AR9170_TX_MAC_PROT_RTS); + else if (carl9170_tx_cts_check(ar, txrate)) + txc->f.mac_control |= cpu_to_le16(AR9170_TX_MAC_PROT_CTS); + + SET_VAL(CARL9170_TX_SUPER_RI_TRIES, txc->s.ri[0], txrate->count); + txc->f.phy_control |= carl9170_tx_physet(ar, info, txrate); + + if (info->flags & IEEE80211_TX_CTL_AMPDU) { + for (i = 1; i < CARL9170_TX_MAX_RATES; i++) { + txrate = &info->control.rates[i]; + if (txrate->idx >= 0) + continue; + + txrate->idx = 0; + txrate->count = ar->hw->max_rate_tries; + } + } + + /* + * NOTE: For the first rate, the ERP & AMPDU flags are directly + * taken from mac_control. For all fallback rate, the firmware + * updates the mac_control flags from the rate info field. + */ + for (i = 1; i < CARL9170_TX_MAX_RATES; i++) { + txrate = &info->control.rates[i]; + if (txrate->idx < 0) + break; + + SET_VAL(CARL9170_TX_SUPER_RI_TRIES, txc->s.ri[i], + txrate->count); + + if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack)) + txc->s.ri[i] |= (AR9170_TX_MAC_PROT_RTS << + CARL9170_TX_SUPER_RI_ERP_PROT_S); + else if (carl9170_tx_cts_check(ar, txrate)) + txc->s.ri[i] |= (AR9170_TX_MAC_PROT_CTS << + CARL9170_TX_SUPER_RI_ERP_PROT_S); + + /* + * unaggregated fallback, in case aggregation + * proves to be unsuccessful and unreliable. + */ + if (ampdu && i < 3) + txc->s.ri[i] |= CARL9170_TX_SUPER_RI_AMPDU; + + txc->s.rr[i - 1] = carl9170_tx_physet(ar, info, txrate); + } + + if (ieee80211_is_probe_resp(hdr->frame_control)) + txc->s.misc |= CARL9170_TX_SUPER_MISC_FILL_IN_TSF; + + if (ampdu) { + unsigned int density, factor; + + if (unlikely(!sta || !cvif)) + goto err_out; + + density = info->control.sta->ht_cap.ampdu_density; + factor = info->control.sta->ht_cap.ampdu_factor; + + if (density) { + /* + * Watch out! + * + * Otus uses slightly different density values than + * those from the 802.11n spec. + */ + + density = max_t(unsigned int, density + 1, 7u); + } + + factor = min_t(unsigned int, 1u, factor); + + SET_VAL(CARL9170_TX_SUPER_AMPDU_DENSITY, + txc->s.ampdu_settings, density); + + SET_VAL(CARL9170_TX_SUPER_AMPDU_FACTOR, + txc->s.ampdu_settings, factor); + + if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS) { + txc->f.mac_control |= cpu_to_le16(AR9170_TX_MAC_AGGR); + } else { + /* + * Not sure if it's even possible to aggregate + * non-ht rates with this HW. + */ + WARN_ON_ONCE(1); + } + } + + arinfo = (void *)info->rate_driver_data; + arinfo->timeout = jiffies; + arinfo->ar = ar; + kref_init(&arinfo->ref); + return 0; + +err_out: + skb_pull(skb, sizeof(*txc)); + return -EINVAL; +} + +static void carl9170_set_immba(struct ar9170 *ar, struct sk_buff *skb) +{ + struct _carl9170_tx_superframe *super; + + super = (void *) skb->data; + super->f.mac_control |= cpu_to_le16(AR9170_TX_MAC_IMM_BA); +} + +static void carl9170_set_ampdu_params(struct ar9170 *ar, struct sk_buff *skb) +{ + struct _carl9170_tx_superframe *super; + int tmp; + + super = (void *) skb->data; + + tmp = (super->s.ampdu_settings & CARL9170_TX_SUPER_AMPDU_DENSITY) << + CARL9170_TX_SUPER_AMPDU_DENSITY_S; + + /* + * If you haven't noticed carl9170_tx_prepare has already filled + * in all ampdu spacing & factor parameters. + * Now it's the time to check whenever the settings have to be + * updated by the firmware, or if everything is still the same. + * + * There's no sane way to handle different density values with + * this hardware, so we may as well just do the compare in the + * driver. + */ + + if (tmp != ar->current_density) { + ar->current_density = tmp; + super->s.ampdu_settings |= + CARL9170_TX_SUPER_AMPDU_COMMIT_DENSITY; + } + + tmp = (super->s.ampdu_settings & CARL9170_TX_SUPER_AMPDU_FACTOR) << + CARL9170_TX_SUPER_AMPDU_FACTOR_S; + + if (tmp != ar->current_factor) { + ar->current_factor = tmp; + super->s.ampdu_settings |= + CARL9170_TX_SUPER_AMPDU_COMMIT_FACTOR; + } +} + +static bool carl9170_tx_rate_check(struct ar9170 *ar, struct sk_buff *_dest, + struct sk_buff *_src) +{ + struct _carl9170_tx_superframe *dest, *src; + + dest = (void *) _dest->data; + src = (void *) _src->data; + + /* + * The mac80211 rate control algorithm expects that all MPDUs in + * an AMPDU share the same tx vectors. + * This is not really obvious right now, because the hardware + * does the AMPDU setup according to its own rulebook. + * Our nicely assembled, strictly monotonic increasing mpdu + * chains will be broken up, mashed back together... + */ + + return (dest->f.phy_control == src->f.phy_control); +} + +static void carl9170_tx_ampdu(struct ar9170 *ar) +{ + struct sk_buff_head agg; + struct carl9170_sta_tid *tid_info; + struct sk_buff *skb, *first; + unsigned int i = 0, done_ampdus = 0; + u16 seq, queue, tmpssn; + + atomic_inc(&ar->tx_ampdu_scheduler); + ar->tx_ampdu_schedule = false; + + if (atomic_read(&ar->tx_ampdu_upload)) + return; + + if (!ar->tx_ampdu_list_len) + return; + + __skb_queue_head_init(&agg); + + rcu_read_lock(); + tid_info = rcu_dereference(ar->tx_ampdu_iter); + if (WARN_ON_ONCE(!tid_info)) { + rcu_read_unlock(); + return; + } + +retry: + list_for_each_entry_continue_rcu(tid_info, &ar->tx_ampdu_list, list) { + i++; + + if (tid_info->state < CARL9170_TID_STATE_PROGRESS) + continue; + + queue = TID_TO_WME_AC(tid_info->tid); + + spin_lock_bh(&tid_info->lock); + if (tid_info->state != CARL9170_TID_STATE_XMIT) { + first = skb_peek(&tid_info->queue); + if (first) { + struct ieee80211_tx_info *txinfo; + struct carl9170_tx_info *arinfo; + + txinfo = IEEE80211_SKB_CB(first); + arinfo = (void *) txinfo->rate_driver_data; + + if (time_is_after_jiffies(arinfo->timeout + + msecs_to_jiffies(CARL9170_QUEUE_TIMEOUT)) + == true) + goto processed; + + /* + * We've been waiting for the frame which + * matches "snx" (start sequence of the + * next aggregate) for some time now. + * + * But it never arrived. Therefore + * jump to the next available frame + * and kick-start the transmission. + * + * Note: This might induce odd latency + * spikes because the receiver will be + * waiting for the lost frame too. + */ + ar->tx_ampdu_timeout++; + + tid_info->snx = carl9170_get_seq(first); + tid_info->state = CARL9170_TID_STATE_XMIT; + } else { + goto processed; + } + } + + tid_info->counter++; + first = skb_peek(&tid_info->queue); + tmpssn = carl9170_get_seq(first); + seq = tid_info->snx; + + if (unlikely(tmpssn != seq)) { + tid_info->state = CARL9170_TID_STATE_IDLE; + + goto processed; + } + + while ((skb = skb_peek(&tid_info->queue))) { + /* strict 0, 1, ..., n - 1, n frame sequence order */ + if (unlikely(carl9170_get_seq(skb) != seq)) + break; + + /* don't upload more than AMPDU FACTOR allows. */ + if (unlikely(SEQ_DIFF(tid_info->snx, tid_info->bsn) >= + (tid_info->max - 1))) + break; + + if (!carl9170_tx_rate_check(ar, skb, first)) + break; + + atomic_inc(&ar->tx_ampdu_upload); + tid_info->snx = seq = SEQ_NEXT(seq); + __skb_unlink(skb, &tid_info->queue); + + __skb_queue_tail(&agg, skb); + + if (skb_queue_len(&agg) >= CARL9170_NUM_TX_AGG_MAX) + break; + } + + if (skb_queue_empty(&tid_info->queue) || + carl9170_get_seq(skb_peek(&tid_info->queue)) != + tid_info->snx) { + /* + * stop TID, if A-MPDU frames are still missing, + * or whenever the queue is empty. + */ + + tid_info->state = CARL9170_TID_STATE_IDLE; + } + done_ampdus++; + +processed: + spin_unlock_bh(&tid_info->lock); + + if (skb_queue_empty(&agg)) + continue; + + /* apply ampdu spacing & factor settings */ + carl9170_set_ampdu_params(ar, skb_peek(&agg)); + + /* set aggregation push bit */ + carl9170_set_immba(ar, skb_peek_tail(&agg)); + + spin_lock_bh(&ar->tx_pending[queue].lock); + skb_queue_splice_tail_init(&agg, &ar->tx_pending[queue]); + spin_unlock_bh(&ar->tx_pending[queue].lock); + ar->tx_schedule = true; + } + if ((done_ampdus++ == 0) && (i++ == 0)) + goto retry; + + rcu_assign_pointer(ar->tx_ampdu_iter, tid_info); + rcu_read_unlock(); +} + +static struct sk_buff *carl9170_tx_pick_skb(struct ar9170 *ar, + struct sk_buff_head *queue) +{ + struct sk_buff *skb; + struct ieee80211_tx_info *info; + struct carl9170_tx_info *arinfo; + + BUILD_BUG_ON(sizeof(*arinfo) > sizeof(info->rate_driver_data)); + + spin_lock_bh(&queue->lock); + skb = skb_peek(queue); + if (unlikely(!skb)) + goto err_unlock; + + if (carl9170_alloc_dev_space(ar, skb)) + goto err_unlock; + + __skb_unlink(skb, queue); + spin_unlock_bh(&queue->lock); + + info = IEEE80211_SKB_CB(skb); + arinfo = (void *) info->rate_driver_data; + + arinfo->timeout = jiffies; + + /* + * increase ref count to "2". + * Ref counting is the easiest way to solve the race between + * the the urb's completion routine: carl9170_tx_callback and + * wlan tx status functions: carl9170_tx_status/janitor. + */ + carl9170_tx_get_skb(skb); + + return skb; + +err_unlock: + spin_unlock_bh(&queue->lock); + return NULL; +} + +void carl9170_tx_drop(struct ar9170 *ar, struct sk_buff *skb) +{ + struct _carl9170_tx_superframe *super; + uint8_t q = 0; + + ar->tx_dropped++; + + super = (void *)skb->data; + SET_VAL(CARL9170_TX_SUPER_MISC_QUEUE, q, + ar9170_qmap[carl9170_get_queue(ar, skb)]); + __carl9170_tx_process_status(ar, super->s.cookie, q); +} + +static void carl9170_tx(struct ar9170 *ar) +{ + struct sk_buff *skb; + unsigned int i, q; + bool schedule_garbagecollector = false; + + ar->tx_schedule = false; + + if (unlikely(!IS_STARTED(ar))) + return; + + carl9170_usb_handle_tx_err(ar); + + for (i = 0; i < ar->hw->queues; i++) { + while (!skb_queue_empty(&ar->tx_pending[i])) { + skb = carl9170_tx_pick_skb(ar, &ar->tx_pending[i]); + if (unlikely(!skb)) + break; + + atomic_inc(&ar->tx_total_pending); + + q = __carl9170_get_queue(ar, i); + /* + * NB: tx_status[i] vs. tx_status[q], + * TODO: Move into pick_skb or alloc_dev_space. + */ + skb_queue_tail(&ar->tx_status[q], skb); + + carl9170_usb_tx(ar, skb); + schedule_garbagecollector = true; + } + } + + if (!schedule_garbagecollector) + return; + + ieee80211_queue_delayed_work(ar->hw, &ar->tx_janitor, + msecs_to_jiffies(CARL9170_TX_TIMEOUT)); +} + +static bool carl9170_tx_ampdu_queue(struct ar9170 *ar, + struct ieee80211_sta *sta, struct sk_buff *skb) +{ + struct carl9170_sta_info *sta_info; + struct carl9170_sta_tid *agg; + struct sk_buff *iter; + unsigned int max; + u16 tid, seq, qseq, off; + bool run = false; + + tid = carl9170_get_tid(skb); + seq = carl9170_get_seq(skb); + sta_info = (void *) sta->drv_priv; + + rcu_read_lock(); + agg = rcu_dereference(sta_info->agg[tid]); + max = sta_info->ampdu_max_len; + + if (!agg) + goto err_unlock_rcu; + + spin_lock_bh(&agg->lock); + if (unlikely(agg->state < CARL9170_TID_STATE_IDLE)) + goto err_unlock; + + /* check if sequence is within the BA window */ + if (unlikely(!BAW_WITHIN(agg->bsn, CARL9170_BAW_BITS, seq))) + goto err_unlock; + + if (WARN_ON_ONCE(!BAW_WITHIN(agg->snx, CARL9170_BAW_BITS, seq))) + goto err_unlock; + + off = SEQ_DIFF(seq, agg->bsn); + if (WARN_ON_ONCE(test_and_set_bit(off, agg->bitmap))) + goto err_unlock; + + if (likely(BAW_WITHIN(agg->hsn, CARL9170_BAW_BITS, seq))) { + __skb_queue_tail(&agg->queue, skb); + agg->hsn = seq; + goto queued; + } + + skb_queue_reverse_walk(&agg->queue, iter) { + qseq = carl9170_get_seq(iter); + + if (BAW_WITHIN(qseq, CARL9170_BAW_BITS, seq)) { + __skb_queue_after(&agg->queue, iter, skb); + goto queued; + } + } + + __skb_queue_head(&agg->queue, skb); +queued: + + if (unlikely(agg->state != CARL9170_TID_STATE_XMIT)) { + if (agg->snx == carl9170_get_seq(skb_peek(&agg->queue))) { + agg->state = CARL9170_TID_STATE_XMIT; + run = true; + } + } + + spin_unlock_bh(&agg->lock); + rcu_read_unlock(); + + return run; + +err_unlock: + spin_unlock_bh(&agg->lock); + +err_unlock_rcu: + rcu_read_unlock(); + carl9170_tx_status(ar, skb, false); + ar->tx_dropped++; + return false; +} + +int carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct ar9170 *ar = hw->priv; + struct ieee80211_tx_info *info; + struct ieee80211_sta *sta; + bool run; + + if (unlikely(!IS_STARTED(ar))) + goto err_free; + + info = IEEE80211_SKB_CB(skb); + sta = info->control.sta; + + if (unlikely(carl9170_tx_prepare(ar, skb))) + goto err_free; + + carl9170_tx_accounting(ar, skb); + /* + * from now on, one has to use carl9170_tx_status to free + * all ressouces which are associated with the frame. + */ + + if (info->flags & IEEE80211_TX_CTL_AMPDU) { + if (WARN_ON_ONCE(!sta)) + goto err_free; + + run = carl9170_tx_ampdu_queue(ar, sta, skb); + if (run) + carl9170_tx_ampdu(ar); + + } else { + unsigned int queue = skb_get_queue_mapping(skb); + + skb_queue_tail(&ar->tx_pending[queue], skb); + } + + carl9170_tx(ar); + return NETDEV_TX_OK; + +err_free: + ar->tx_dropped++; + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +} + +void carl9170_tx_scheduler(struct ar9170 *ar) +{ + + if (ar->tx_ampdu_schedule) + carl9170_tx_ampdu(ar); + + if (ar->tx_schedule) + carl9170_tx(ar); +} diff --git a/drivers/net/wireless/ath/carl9170/usb.c b/drivers/net/wireless/ath/carl9170/usb.c new file mode 100644 index 000000000000..fde918d0120b --- /dev/null +++ b/drivers/net/wireless/ath/carl9170/usb.c @@ -0,0 +1,1137 @@ +/* + * Atheros CARL9170 driver + * + * USB - frontend + * + * Copyright 2008, Johannes Berg + * Copyright 2009, 2010, Christian Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "carl9170.h" +#include "cmd.h" +#include "hw.h" +#include "fwcmd.h" + +MODULE_AUTHOR("Johannes Berg "); +MODULE_AUTHOR("Christian Lamparter "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Atheros AR9170 802.11n USB wireless"); +MODULE_FIRMWARE(CARL9170FW_NAME); +MODULE_ALIAS("ar9170usb"); +MODULE_ALIAS("arusb_lnx"); + +/* + * Note: + * + * Always update our wiki's device list (located at: + * http://wireless.kernel.org/en/users/Drivers/ar9170/devices ), + * whenever you add a new device. + */ +static struct usb_device_id carl9170_usb_ids[] = { + /* Atheros 9170 */ + { USB_DEVICE(0x0cf3, 0x9170) }, + /* Atheros TG121N */ + { USB_DEVICE(0x0cf3, 0x1001) }, + /* TP-Link TL-WN821N v2 */ + { USB_DEVICE(0x0cf3, 0x1002), .driver_info = CARL9170_WPS_BUTTON | + CARL9170_ONE_LED }, + /* 3Com Dual Band 802.11n USB Adapter */ + { USB_DEVICE(0x0cf3, 0x1010) }, + /* H3C Dual Band 802.11n USB Adapter */ + { USB_DEVICE(0x0cf3, 0x1011) }, + /* Cace Airpcap NX */ + { USB_DEVICE(0xcace, 0x0300) }, + /* D-Link DWA 160 A1 */ + { USB_DEVICE(0x07d1, 0x3c10) }, + /* D-Link DWA 160 A2 */ + { USB_DEVICE(0x07d1, 0x3a09) }, + /* Netgear WNA1000 */ + { USB_DEVICE(0x0846, 0x9040) }, + /* Netgear WNDA3100 */ + { USB_DEVICE(0x0846, 0x9010) }, + /* Netgear WN111 v2 */ + { USB_DEVICE(0x0846, 0x9001), .driver_info = CARL9170_ONE_LED }, + /* Zydas ZD1221 */ + { USB_DEVICE(0x0ace, 0x1221) }, + /* Proxim ORiNOCO 802.11n USB */ + { USB_DEVICE(0x1435, 0x0804) }, + /* WNC Generic 11n USB Dongle */ + { USB_DEVICE(0x1435, 0x0326) }, + /* ZyXEL NWD271N */ + { USB_DEVICE(0x0586, 0x3417) }, + /* Z-Com UB81 BG */ + { USB_DEVICE(0x0cde, 0x0023) }, + /* Z-Com UB82 ABG */ + { USB_DEVICE(0x0cde, 0x0026) }, + /* Sphairon Homelink 1202 */ + { USB_DEVICE(0x0cde, 0x0027) }, + /* Arcadyan WN7512 */ + { USB_DEVICE(0x083a, 0xf522) }, + /* Planex GWUS300 */ + { USB_DEVICE(0x2019, 0x5304) }, + /* IO-Data WNGDNUS2 */ + { USB_DEVICE(0x04bb, 0x093f) }, + /* NEC WL300NU-G */ + { USB_DEVICE(0x0409, 0x0249) }, + /* AVM FRITZ!WLAN USB Stick N */ + { USB_DEVICE(0x057c, 0x8401) }, + /* AVM FRITZ!WLAN USB Stick N 2.4 */ + { USB_DEVICE(0x057c, 0x8402) }, + /* Qwest/Actiontec 802AIN Wireless N USB Network Adapter */ + { USB_DEVICE(0x1668, 0x1200) }, + + /* terminate */ + {} +}; +MODULE_DEVICE_TABLE(usb, carl9170_usb_ids); + +static void carl9170_usb_submit_data_urb(struct ar9170 *ar) +{ + struct urb *urb; + int err; + + if (atomic_inc_return(&ar->tx_anch_urbs) > AR9170_NUM_TX_URBS) + goto err_acc; + + urb = usb_get_from_anchor(&ar->tx_wait); + if (!urb) + goto err_acc; + + usb_anchor_urb(urb, &ar->tx_anch); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(err)) { + if (net_ratelimit()) { + dev_err(&ar->udev->dev, "tx submit failed (%d)\n", + urb->status); + } + + usb_unanchor_urb(urb); + usb_anchor_urb(urb, &ar->tx_err); + } + + usb_free_urb(urb); + + if (likely(err == 0)) + return; + +err_acc: + atomic_dec(&ar->tx_anch_urbs); +} + +static void carl9170_usb_tx_data_complete(struct urb *urb) +{ + struct ar9170 *ar = (struct ar9170 *) + usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)); + + if (WARN_ON_ONCE(!ar)) { + dev_kfree_skb_irq(urb->context); + return; + } + + atomic_dec(&ar->tx_anch_urbs); + + switch (urb->status) { + /* everything is fine */ + case 0: + carl9170_tx_callback(ar, (void *)urb->context); + break; + + /* disconnect */ + case -ENOENT: + case -ECONNRESET: + case -ENODEV: + case -ESHUTDOWN: + /* + * Defer the frame clean-up to the tasklet worker. + * This is necessary, because carl9170_tx_drop + * does not work in an irqsave context. + */ + usb_anchor_urb(urb, &ar->tx_err); + return; + + /* a random transmission error has occurred? */ + default: + if (net_ratelimit()) { + dev_err(&ar->udev->dev, "tx failed (%d)\n", + urb->status); + } + + usb_anchor_urb(urb, &ar->tx_err); + break; + } + + if (likely(IS_STARTED(ar))) + carl9170_usb_submit_data_urb(ar); +} + +static int carl9170_usb_submit_cmd_urb(struct ar9170 *ar) +{ + struct urb *urb; + int err; + + if (atomic_inc_return(&ar->tx_cmd_urbs) != 1) { + atomic_dec(&ar->tx_cmd_urbs); + return 0; + } + + urb = usb_get_from_anchor(&ar->tx_cmd); + if (!urb) { + atomic_dec(&ar->tx_cmd_urbs); + return 0; + } + + usb_anchor_urb(urb, &ar->tx_anch); + err = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(err)) { + usb_unanchor_urb(urb); + atomic_dec(&ar->tx_cmd_urbs); + } + usb_free_urb(urb); + + return err; +} + +static void carl9170_usb_cmd_complete(struct urb *urb) +{ + struct ar9170 *ar = urb->context; + int err = 0; + + if (WARN_ON_ONCE(!ar)) + return; + + atomic_dec(&ar->tx_cmd_urbs); + + switch (urb->status) { + /* everything is fine */ + case 0: + break; + + /* disconnect */ + case -ENOENT: + case -ECONNRESET: + case -ENODEV: + case -ESHUTDOWN: + return; + + default: + err = urb->status; + break; + } + + if (!IS_INITIALIZED(ar)) + return; + + if (err) + dev_err(&ar->udev->dev, "submit cmd cb failed (%d).\n", err); + + err = carl9170_usb_submit_cmd_urb(ar); + if (err) + dev_err(&ar->udev->dev, "submit cmd failed (%d).\n", err); +} + +static void carl9170_usb_rx_irq_complete(struct urb *urb) +{ + struct ar9170 *ar = urb->context; + + if (WARN_ON_ONCE(!ar)) + return; + + switch (urb->status) { + /* everything is fine */ + case 0: + break; + + /* disconnect */ + case -ENOENT: + case -ECONNRESET: + case -ENODEV: + case -ESHUTDOWN: + return; + + default: + goto resubmit; + } + + carl9170_handle_command_response(ar, urb->transfer_buffer, + urb->actual_length); + +resubmit: + usb_anchor_urb(urb, &ar->rx_anch); + if (unlikely(usb_submit_urb(urb, GFP_ATOMIC))) + usb_unanchor_urb(urb); +} + +static int carl9170_usb_submit_rx_urb(struct ar9170 *ar, gfp_t gfp) +{ + struct urb *urb; + int err = 0, runs = 0; + + while ((atomic_read(&ar->rx_anch_urbs) < AR9170_NUM_RX_URBS) && + (runs++ < AR9170_NUM_RX_URBS)) { + err = -ENOSPC; + urb = usb_get_from_anchor(&ar->rx_pool); + if (urb) { + usb_anchor_urb(urb, &ar->rx_anch); + err = usb_submit_urb(urb, gfp); + if (unlikely(err)) { + usb_unanchor_urb(urb); + usb_anchor_urb(urb, &ar->rx_pool); + } else { + atomic_dec(&ar->rx_pool_urbs); + atomic_inc(&ar->rx_anch_urbs); + } + usb_free_urb(urb); + } + } + + return err; +} + +static void carl9170_usb_rx_work(struct ar9170 *ar) +{ + struct urb *urb; + int i; + + for (i = 0; i < AR9170_NUM_RX_URBS_POOL; i++) { + urb = usb_get_from_anchor(&ar->rx_work); + if (!urb) + break; + + atomic_dec(&ar->rx_work_urbs); + if (IS_INITIALIZED(ar)) { + carl9170_rx(ar, urb->transfer_buffer, + urb->actual_length); + } + + usb_anchor_urb(urb, &ar->rx_pool); + atomic_inc(&ar->rx_pool_urbs); + + usb_free_urb(urb); + + carl9170_usb_submit_rx_urb(ar, GFP_ATOMIC); + } +} + +void carl9170_usb_handle_tx_err(struct ar9170 *ar) +{ + struct urb *urb; + + while ((urb = usb_get_from_anchor(&ar->tx_err))) { + struct sk_buff *skb = (void *)urb->context; + + carl9170_tx_drop(ar, skb); + carl9170_tx_callback(ar, skb); + usb_free_urb(urb); + } +} + +static void carl9170_usb_tasklet(unsigned long data) +{ + struct ar9170 *ar = (struct ar9170 *) data; + + carl9170_usb_rx_work(ar); + + /* + * Strictly speaking: The tx scheduler is not part of the USB system. + * But the rx worker returns frames back to the mac80211-stack and + * this is the _perfect_ place to generate the next transmissions. + */ + if (IS_STARTED(ar)) + carl9170_tx_scheduler(ar); +} + +static void carl9170_usb_rx_complete(struct urb *urb) +{ + struct ar9170 *ar = (struct ar9170 *)urb->context; + int err; + + if (WARN_ON_ONCE(!ar)) + return; + + atomic_dec(&ar->rx_anch_urbs); + + switch (urb->status) { + case 0: + /* rx path */ + usb_anchor_urb(urb, &ar->rx_work); + atomic_inc(&ar->rx_work_urbs); + break; + + case -ENOENT: + case -ECONNRESET: + case -ENODEV: + case -ESHUTDOWN: + /* handle disconnect events*/ + return; + + default: + /* handle all other errors */ + usb_anchor_urb(urb, &ar->rx_pool); + atomic_inc(&ar->rx_pool_urbs); + break; + } + + err = carl9170_usb_submit_rx_urb(ar, GFP_ATOMIC); + if (unlikely(err)) { + /* + * usb_submit_rx_urb reported a problem. + * In case this is due to a rx buffer shortage, + * elevate the tasklet worker priority to + * the highest available level. + */ + tasklet_hi_schedule(&ar->usb_tasklet); + + if (atomic_read(&ar->rx_anch_urbs) == 0) { + /* + * The system is too slow to cope with + * the enormous workload. We have simply + * run out of active rx urbs and this + * unfortunatly leads to an unpredictable + * device. + */ + + carl9170_restart(ar, CARL9170_RR_SLOW_SYSTEM); + } + } else { + /* + * Using anything less than _high_ priority absolutely + * kills the rx performance my UP-System... + */ + tasklet_hi_schedule(&ar->usb_tasklet); + } +} + +static struct urb *carl9170_usb_alloc_rx_urb(struct ar9170 *ar, gfp_t gfp) +{ + struct urb *urb; + void *buf; + + buf = kmalloc(ar->fw.rx_size, gfp); + if (!buf) + return NULL; + + urb = usb_alloc_urb(0, gfp); + if (!urb) { + kfree(buf); + return NULL; + } + + usb_fill_bulk_urb(urb, ar->udev, usb_rcvbulkpipe(ar->udev, + AR9170_USB_EP_RX), buf, ar->fw.rx_size, + carl9170_usb_rx_complete, ar); + + urb->transfer_flags |= URB_FREE_BUFFER; + + return urb; +} + +static int carl9170_usb_send_rx_irq_urb(struct ar9170 *ar) +{ + struct urb *urb = NULL; + void *ibuf; + int err = -ENOMEM; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + goto out; + + ibuf = kmalloc(AR9170_USB_EP_CTRL_MAX, GFP_KERNEL); + if (!ibuf) + goto out; + + usb_fill_int_urb(urb, ar->udev, usb_rcvintpipe(ar->udev, + AR9170_USB_EP_IRQ), ibuf, AR9170_USB_EP_CTRL_MAX, + carl9170_usb_rx_irq_complete, ar, 1); + + urb->transfer_flags |= URB_FREE_BUFFER; + + usb_anchor_urb(urb, &ar->rx_anch); + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) + usb_unanchor_urb(urb); + +out: + usb_free_urb(urb); + return err; +} + +static int carl9170_usb_init_rx_bulk_urbs(struct ar9170 *ar) +{ + struct urb *urb; + int i, err = -EINVAL; + + /* + * The driver actively maintains a second shadow + * pool for inactive, but fully-prepared rx urbs. + * + * The pool should help the driver to master huge + * workload spikes without running the risk of + * undersupplying the hardware or wasting time by + * processing rx data (streams) inside the urb + * completion (hardirq context). + */ + for (i = 0; i < AR9170_NUM_RX_URBS_POOL; i++) { + urb = carl9170_usb_alloc_rx_urb(ar, GFP_KERNEL); + if (!urb) { + err = -ENOMEM; + goto err_out; + } + + usb_anchor_urb(urb, &ar->rx_pool); + atomic_inc(&ar->rx_pool_urbs); + usb_free_urb(urb); + } + + err = carl9170_usb_submit_rx_urb(ar, GFP_KERNEL); + if (err) + goto err_out; + + /* the device now waiting for the firmware. */ + carl9170_set_state_when(ar, CARL9170_STOPPED, CARL9170_IDLE); + return 0; + +err_out: + + usb_scuttle_anchored_urbs(&ar->rx_pool); + usb_scuttle_anchored_urbs(&ar->rx_work); + usb_kill_anchored_urbs(&ar->rx_anch); + return err; +} + +static int carl9170_usb_flush(struct ar9170 *ar) +{ + struct urb *urb; + int ret, err = 0; + + while ((urb = usb_get_from_anchor(&ar->tx_wait))) { + struct sk_buff *skb = (void *)urb->context; + carl9170_tx_drop(ar, skb); + carl9170_tx_callback(ar, skb); + usb_free_urb(urb); + } + + ret = usb_wait_anchor_empty_timeout(&ar->tx_cmd, HZ); + if (ret == 0) + err = -ETIMEDOUT; + + /* lets wait a while until the tx - queues are dried out */ + ret = usb_wait_anchor_empty_timeout(&ar->tx_anch, HZ); + if (ret == 0) + err = -ETIMEDOUT; + + usb_kill_anchored_urbs(&ar->tx_anch); + carl9170_usb_handle_tx_err(ar); + + return err; +} + +static void carl9170_usb_cancel_urbs(struct ar9170 *ar) +{ + int err; + + carl9170_set_state(ar, CARL9170_UNKNOWN_STATE); + + err = carl9170_usb_flush(ar); + if (err) + dev_err(&ar->udev->dev, "stuck tx urbs!\n"); + + usb_poison_anchored_urbs(&ar->tx_anch); + carl9170_usb_handle_tx_err(ar); + usb_poison_anchored_urbs(&ar->rx_anch); + + tasklet_kill(&ar->usb_tasklet); + + usb_scuttle_anchored_urbs(&ar->rx_work); + usb_scuttle_anchored_urbs(&ar->rx_pool); + usb_scuttle_anchored_urbs(&ar->tx_cmd); +} + +int __carl9170_exec_cmd(struct ar9170 *ar, struct carl9170_cmd *cmd, + const bool free_buf) +{ + struct urb *urb; + + if (!IS_INITIALIZED(ar)) + return -EPERM; + + if (WARN_ON(cmd->hdr.len > CARL9170_MAX_CMD_LEN - 4)) + return -EINVAL; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + usb_fill_int_urb(urb, ar->udev, usb_sndintpipe(ar->udev, + AR9170_USB_EP_CMD), cmd, cmd->hdr.len + 4, + carl9170_usb_cmd_complete, ar, 1); + + urb->transfer_flags |= URB_ZERO_PACKET; + + if (free_buf) + urb->transfer_flags |= URB_FREE_BUFFER; + + usb_anchor_urb(urb, &ar->tx_cmd); + usb_free_urb(urb); + + return carl9170_usb_submit_cmd_urb(ar); +} + +int carl9170_exec_cmd(struct ar9170 *ar, const enum carl9170_cmd_oids cmd, + unsigned int plen, void *payload, unsigned int outlen, void *out) +{ + int err = -ENOMEM; + + if (!IS_ACCEPTING_CMD(ar)) + return -EIO; + + if (!(cmd & CARL9170_CMD_ASYNC_FLAG)) + might_sleep(); + + ar->cmd.hdr.len = plen; + ar->cmd.hdr.cmd = cmd; + /* writing multiple regs fills this buffer already */ + if (plen && payload != (u8 *)(ar->cmd.data)) + memcpy(ar->cmd.data, payload, plen); + + spin_lock_bh(&ar->cmd_lock); + ar->readbuf = (u8 *)out; + ar->readlen = outlen; + spin_unlock_bh(&ar->cmd_lock); + + err = __carl9170_exec_cmd(ar, &ar->cmd, false); + + if (!(cmd & CARL9170_CMD_ASYNC_FLAG)) { + err = wait_for_completion_timeout(&ar->cmd_wait, HZ); + if (err == 0) { + err = -ETIMEDOUT; + goto err_unbuf; + } + + if (ar->readlen != outlen) { + err = -EMSGSIZE; + goto err_unbuf; + } + } + + return 0; + +err_unbuf: + /* Maybe the device was removed in the moment we were waiting? */ + if (IS_STARTED(ar)) { + dev_err(&ar->udev->dev, "no command feedback " + "received (%d).\n", err); + + /* provide some maybe useful debug information */ + print_hex_dump_bytes("carl9170 cmd: ", DUMP_PREFIX_NONE, + &ar->cmd, plen + 4); + + carl9170_restart(ar, CARL9170_RR_COMMAND_TIMEOUT); + } + + /* invalidate to avoid completing the next command prematurely */ + spin_lock_bh(&ar->cmd_lock); + ar->readbuf = NULL; + ar->readlen = 0; + spin_unlock_bh(&ar->cmd_lock); + + return err; +} + +void carl9170_usb_tx(struct ar9170 *ar, struct sk_buff *skb) +{ + struct urb *urb; + struct ar9170_stream *tx_stream; + void *data; + unsigned int len; + + if (!IS_STARTED(ar)) + goto err_drop; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + goto err_drop; + + if (ar->fw.tx_stream) { + tx_stream = (void *) (skb->data - sizeof(*tx_stream)); + + len = skb->len + sizeof(*tx_stream); + tx_stream->length = cpu_to_le16(len); + tx_stream->tag = cpu_to_le16(AR9170_TX_STREAM_TAG); + data = tx_stream; + } else { + data = skb->data; + len = skb->len; + } + + usb_fill_bulk_urb(urb, ar->udev, usb_sndbulkpipe(ar->udev, + AR9170_USB_EP_TX), data, len, + carl9170_usb_tx_data_complete, skb); + + urb->transfer_flags |= URB_ZERO_PACKET; + + usb_anchor_urb(urb, &ar->tx_wait); + + usb_free_urb(urb); + + carl9170_usb_submit_data_urb(ar); + return; + +err_drop: + carl9170_tx_drop(ar, skb); + carl9170_tx_callback(ar, skb); +} + +static void carl9170_release_firmware(struct ar9170 *ar) +{ + if (ar->fw.fw) { + release_firmware(ar->fw.fw); + memset(&ar->fw, 0, sizeof(ar->fw)); + } +} + +void carl9170_usb_stop(struct ar9170 *ar) +{ + int ret; + + carl9170_set_state_when(ar, CARL9170_IDLE, CARL9170_STOPPED); + + ret = carl9170_usb_flush(ar); + if (ret) + dev_err(&ar->udev->dev, "kill pending tx urbs.\n"); + + usb_poison_anchored_urbs(&ar->tx_anch); + carl9170_usb_handle_tx_err(ar); + + /* kill any pending command */ + spin_lock_bh(&ar->cmd_lock); + ar->readlen = 0; + spin_unlock_bh(&ar->cmd_lock); + complete_all(&ar->cmd_wait); + + /* This is required to prevent an early completion on _start */ + INIT_COMPLETION(ar->cmd_wait); + + /* + * Note: + * So far we freed all tx urbs, but we won't dare to touch any rx urbs. + * Else we would end up with a unresponsive device... + */ +} + +int carl9170_usb_open(struct ar9170 *ar) +{ + usb_unpoison_anchored_urbs(&ar->tx_anch); + + carl9170_set_state_when(ar, CARL9170_STOPPED, CARL9170_IDLE); + return 0; +} + +static int carl9170_usb_load_firmware(struct ar9170 *ar) +{ + const u8 *data; + u8 *buf; + unsigned int transfer; + size_t len; + u32 addr; + int err = 0; + + buf = kmalloc(4096, GFP_KERNEL); + if (!buf) { + err = -ENOMEM; + goto err_out; + } + + data = ar->fw.fw->data; + len = ar->fw.fw->size; + addr = ar->fw.address; + + /* this removes the miniboot image */ + data += ar->fw.offset; + len -= ar->fw.offset; + + while (len) { + transfer = min_t(unsigned int, len, 4096u); + memcpy(buf, data, transfer); + + err = usb_control_msg(ar->udev, usb_sndctrlpipe(ar->udev, 0), + 0x30 /* FW DL */, 0x40 | USB_DIR_OUT, + addr >> 8, 0, buf, transfer, 100); + + if (err < 0) { + kfree(buf); + goto err_out; + } + + len -= transfer; + data += transfer; + addr += transfer; + } + kfree(buf); + + err = usb_control_msg(ar->udev, usb_sndctrlpipe(ar->udev, 0), + 0x31 /* FW DL COMPLETE */, + 0x40 | USB_DIR_OUT, 0, 0, NULL, 0, 200); + + if (wait_for_completion_timeout(&ar->fw_boot_wait, HZ) == 0) { + err = -ETIMEDOUT; + goto err_out; + } + + err = carl9170_echo_test(ar, 0x4a110123); + if (err) + goto err_out; + + /* firmware restarts cmd counter */ + ar->cmd_seq = -1; + + return 0; + +err_out: + dev_err(&ar->udev->dev, "firmware upload failed (%d).\n", err); + return err; +} + +int carl9170_usb_restart(struct ar9170 *ar) +{ + int err = 0; + + if (ar->intf->condition != USB_INTERFACE_BOUND) + return 0; + + /* Disable command response sequence counter. */ + ar->cmd_seq = -2; + + err = carl9170_reboot(ar); + + carl9170_usb_stop(ar); + + carl9170_set_state(ar, CARL9170_UNKNOWN_STATE); + + if (err) + goto err_out; + + tasklet_schedule(&ar->usb_tasklet); + + /* The reboot procedure can take quite a while to complete. */ + msleep(1100); + + err = carl9170_usb_open(ar); + if (err) + goto err_out; + + err = carl9170_usb_load_firmware(ar); + if (err) + goto err_out; + + return 0; + +err_out: + carl9170_usb_cancel_urbs(ar); + return err; +} + +void carl9170_usb_reset(struct ar9170 *ar) +{ + /* + * This is the last resort to get the device going again + * without any *user replugging action*. + * + * But there is a catch: usb_reset really is like a physical + * *reconnect*. The mac80211 state will be lost in the process. + * Therefore a userspace application, which is monitoring + * the link must step in. + */ + carl9170_usb_cancel_urbs(ar); + + carl9170_usb_stop(ar); + + usb_queue_reset_device(ar->intf); +} + +static int carl9170_usb_init_device(struct ar9170 *ar) +{ + int err; + + err = carl9170_usb_send_rx_irq_urb(ar); + if (err) + goto err_out; + + err = carl9170_usb_init_rx_bulk_urbs(ar); + if (err) + goto err_unrx; + + mutex_lock(&ar->mutex); + err = carl9170_usb_load_firmware(ar); + mutex_unlock(&ar->mutex); + if (err) + goto err_unrx; + + return 0; + +err_unrx: + carl9170_usb_cancel_urbs(ar); + +err_out: + return err; +} + +static void carl9170_usb_firmware_failed(struct ar9170 *ar) +{ + struct device *parent = ar->udev->dev.parent; + struct usb_device *udev; + + /* + * Store a copy of the usb_device pointer locally. + * This is because device_release_driver initiates + * carl9170_usb_disconnect, which in turn frees our + * driver context (ar). + */ + udev = ar->udev; + + complete(&ar->fw_load_wait); + + /* unbind anything failed */ + if (parent) + device_lock(parent); + + device_release_driver(&udev->dev); + if (parent) + device_unlock(parent); + + usb_put_dev(udev); +} + +static void carl9170_usb_firmware_finish(struct ar9170 *ar) +{ + int err; + + err = carl9170_parse_firmware(ar); + if (err) + goto err_freefw; + + err = carl9170_usb_init_device(ar); + if (err) + goto err_freefw; + + err = carl9170_usb_open(ar); + if (err) + goto err_unrx; + + err = carl9170_register(ar); + + carl9170_usb_stop(ar); + if (err) + goto err_unrx; + + complete(&ar->fw_load_wait); + usb_put_dev(ar->udev); + return; + +err_unrx: + carl9170_usb_cancel_urbs(ar); + +err_freefw: + carl9170_release_firmware(ar); + carl9170_usb_firmware_failed(ar); +} + +static void carl9170_usb_firmware_step2(const struct firmware *fw, + void *context) +{ + struct ar9170 *ar = context; + + if (fw) { + ar->fw.fw = fw; + carl9170_usb_firmware_finish(ar); + return; + } + + dev_err(&ar->udev->dev, "firmware not found.\n"); + carl9170_usb_firmware_failed(ar); +} + +static int carl9170_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct ar9170 *ar; + struct usb_device *udev; + int err; + + err = usb_reset_device(interface_to_usbdev(intf)); + if (err) + return err; + + ar = carl9170_alloc(sizeof(*ar)); + if (IS_ERR(ar)) + return PTR_ERR(ar); + + udev = interface_to_usbdev(intf); + usb_get_dev(udev); + ar->udev = udev; + ar->intf = intf; + ar->features = id->driver_info; + + usb_set_intfdata(intf, ar); + SET_IEEE80211_DEV(ar->hw, &intf->dev); + + init_usb_anchor(&ar->rx_anch); + init_usb_anchor(&ar->rx_pool); + init_usb_anchor(&ar->rx_work); + init_usb_anchor(&ar->tx_wait); + init_usb_anchor(&ar->tx_anch); + init_usb_anchor(&ar->tx_cmd); + init_usb_anchor(&ar->tx_err); + init_completion(&ar->cmd_wait); + init_completion(&ar->fw_boot_wait); + init_completion(&ar->fw_load_wait); + tasklet_init(&ar->usb_tasklet, carl9170_usb_tasklet, + (unsigned long)ar); + + atomic_set(&ar->tx_cmd_urbs, 0); + atomic_set(&ar->tx_anch_urbs, 0); + atomic_set(&ar->rx_work_urbs, 0); + atomic_set(&ar->rx_anch_urbs, 0); + atomic_set(&ar->rx_pool_urbs, 0); + ar->cmd_seq = -2; + + usb_get_dev(ar->udev); + + carl9170_set_state(ar, CARL9170_STOPPED); + + return request_firmware_nowait(THIS_MODULE, 1, CARL9170FW_NAME, + &ar->udev->dev, GFP_KERNEL, ar, carl9170_usb_firmware_step2); +} + +static void carl9170_usb_disconnect(struct usb_interface *intf) +{ + struct ar9170 *ar = usb_get_intfdata(intf); + struct usb_device *udev; + + if (WARN_ON(!ar)) + return; + + udev = ar->udev; + wait_for_completion(&ar->fw_load_wait); + + if (IS_INITIALIZED(ar)) { + carl9170_reboot(ar); + carl9170_usb_stop(ar); + } + + carl9170_usb_cancel_urbs(ar); + carl9170_unregister(ar); + + usb_set_intfdata(intf, NULL); + + carl9170_release_firmware(ar); + carl9170_free(ar); + usb_put_dev(udev); +} + +#ifdef CONFIG_PM +static int carl9170_usb_suspend(struct usb_interface *intf, + pm_message_t message) +{ + struct ar9170 *ar = usb_get_intfdata(intf); + + if (!ar) + return -ENODEV; + + carl9170_usb_cancel_urbs(ar); + + /* + * firmware automatically reboots for usb suspend. + */ + + return 0; +} + +static int carl9170_usb_resume(struct usb_interface *intf) +{ + struct ar9170 *ar = usb_get_intfdata(intf); + int err; + + if (!ar) + return -ENODEV; + + usb_unpoison_anchored_urbs(&ar->rx_anch); + + err = carl9170_usb_init_device(ar); + if (err) + goto err_unrx; + + err = carl9170_usb_open(ar); + if (err) + goto err_unrx; + + return 0; + +err_unrx: + carl9170_usb_cancel_urbs(ar); + + return err; +} +#endif /* CONFIG_PM */ + +static struct usb_driver carl9170_driver = { + .name = KBUILD_MODNAME, + .probe = carl9170_usb_probe, + .disconnect = carl9170_usb_disconnect, + .id_table = carl9170_usb_ids, + .soft_unbind = 1, +#ifdef CONFIG_PM + .suspend = carl9170_usb_suspend, + .resume = carl9170_usb_resume, +#endif /* CONFIG_PM */ +}; + +static int __init carl9170_usb_init(void) +{ + return usb_register(&carl9170_driver); +} + +static void __exit carl9170_usb_exit(void) +{ + usb_deregister(&carl9170_driver); +} + +module_init(carl9170_usb_init); +module_exit(carl9170_usb_exit); -- cgit v1.2.3 From 00c4da27a4219385cfe6a3054ddf3195c9e3c30e Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Mon, 6 Sep 2010 01:09:49 +0200 Subject: carl9170: firmware parser and debugfs code Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/debug.c | 908 ++++++++++++++++++++++++++++++ drivers/net/wireless/ath/carl9170/debug.h | 134 +++++ drivers/net/wireless/ath/carl9170/fw.c | 395 +++++++++++++ 3 files changed, 1437 insertions(+) create mode 100644 drivers/net/wireless/ath/carl9170/debug.c create mode 100644 drivers/net/wireless/ath/carl9170/debug.h create mode 100644 drivers/net/wireless/ath/carl9170/fw.c (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/debug.c b/drivers/net/wireless/ath/carl9170/debug.c new file mode 100644 index 000000000000..3e9b0e8b2b8c --- /dev/null +++ b/drivers/net/wireless/ath/carl9170/debug.c @@ -0,0 +1,908 @@ +/* + * Atheros CARL9170 driver + * + * debug(fs) probing + * + * Copyright 2008, Johannes Berg + * Copyright 2009, 2010, Christian Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2008-2009 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include "carl9170.h" +#include "cmd.h" + +#define ADD(buf, off, max, fmt, args...) \ + off += snprintf(&buf[off], max - off, fmt, ##args); + +static int carl9170_debugfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +struct carl9170_debugfs_fops { + unsigned int read_bufsize; + mode_t attr; + char *(*read)(struct ar9170 *ar, char *buf, size_t bufsize, + ssize_t *len); + ssize_t (*write)(struct ar9170 *aru, const char *buf, size_t size); + const struct file_operations fops; + + enum carl9170_device_state req_dev_state; +}; + +static ssize_t carl9170_debugfs_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct carl9170_debugfs_fops *dfops; + struct ar9170 *ar; + char *buf = NULL, *res_buf = NULL; + ssize_t ret = 0; + int err = 0; + + if (!count) + return 0; + + ar = file->private_data; + + if (!ar) + return -ENODEV; + dfops = container_of(file->f_op, struct carl9170_debugfs_fops, fops); + + if (!dfops->read) + return -ENOSYS; + + if (dfops->read_bufsize) { + buf = vmalloc(dfops->read_bufsize); + if (!buf) + return -ENOMEM; + } + + mutex_lock(&ar->mutex); + if (!CHK_DEV_STATE(ar, dfops->req_dev_state)) { + err = -ENODEV; + res_buf = buf; + goto out_free; + } + + res_buf = dfops->read(ar, buf, dfops->read_bufsize, &ret); + + if (ret > 0) + err = simple_read_from_buffer(userbuf, count, ppos, + res_buf, ret); + else + err = ret; + + WARN_ON_ONCE(dfops->read_bufsize && (res_buf != buf)); + +out_free: + vfree(res_buf); + mutex_unlock(&ar->mutex); + return err; +} + +static ssize_t carl9170_debugfs_write(struct file *file, + const char __user *userbuf, size_t count, loff_t *ppos) +{ + struct carl9170_debugfs_fops *dfops; + struct ar9170 *ar; + char *buf = NULL; + int err = 0; + + if (!count) + return 0; + + if (count > PAGE_SIZE) + return -E2BIG; + + ar = file->private_data; + + if (!ar) + return -ENODEV; + dfops = container_of(file->f_op, struct carl9170_debugfs_fops, fops); + + if (!dfops->write) + return -ENOSYS; + + buf = vmalloc(count); + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, userbuf, count)) { + err = -EFAULT; + goto out_free; + } + + if (mutex_trylock(&ar->mutex) == 0) { + err = -EAGAIN; + goto out_free; + } + + if (!CHK_DEV_STATE(ar, dfops->req_dev_state)) { + err = -ENODEV; + goto out_unlock; + } + + err = dfops->write(ar, buf, count); + if (err) + goto out_unlock; + +out_unlock: + mutex_unlock(&ar->mutex); + +out_free: + vfree(buf); + return err; +} + +#define __DEBUGFS_DECLARE_FILE(name, _read, _write, _read_bufsize, \ + _attr, _dstate) \ +static const struct carl9170_debugfs_fops carl_debugfs_##name ##_ops = {\ + .read_bufsize = _read_bufsize, \ + .read = _read, \ + .write = _write, \ + .attr = _attr, \ + .req_dev_state = _dstate, \ + .fops = { \ + .open = carl9170_debugfs_open, \ + .read = carl9170_debugfs_read, \ + .write = carl9170_debugfs_write, \ + .owner = THIS_MODULE \ + }, \ +} + +#define DEBUGFS_DECLARE_FILE(name, _read, _write, _read_bufsize, _attr) \ + __DEBUGFS_DECLARE_FILE(name, _read, _write, _read_bufsize, \ + _attr, CARL9170_STARTED) \ + +#define DEBUGFS_DECLARE_RO_FILE(name, _read_bufsize) \ + DEBUGFS_DECLARE_FILE(name, carl9170_debugfs_##name ##_read, \ + NULL, _read_bufsize, S_IRUSR) + +#define DEBUGFS_DECLARE_WO_FILE(name) \ + DEBUGFS_DECLARE_FILE(name, NULL, carl9170_debugfs_##name ##_write,\ + 0, S_IWUSR) + +#define DEBUGFS_DECLARE_RW_FILE(name, _read_bufsize) \ + DEBUGFS_DECLARE_FILE(name, carl9170_debugfs_##name ##_read, \ + carl9170_debugfs_##name ##_write, \ + _read_bufsize, S_IRUSR | S_IWUSR) + +#define __DEBUGFS_DECLARE_RW_FILE(name, _read_bufsize, _dstate) \ + __DEBUGFS_DECLARE_FILE(name, carl9170_debugfs_##name ##_read, \ + carl9170_debugfs_##name ##_write, \ + _read_bufsize, S_IRUSR | S_IWUSR, _dstate) + +#define DEBUGFS_READONLY_FILE(name, _read_bufsize, fmt, value...) \ +static char *carl9170_debugfs_ ##name ## _read(struct ar9170 *ar, \ + char *buf, size_t buf_size,\ + ssize_t *len) \ +{ \ + ADD(buf, *len, buf_size, fmt "\n", ##value); \ + return buf; \ +} \ +DEBUGFS_DECLARE_RO_FILE(name, _read_bufsize) + +static char *carl9170_debugfs_mem_usage_read(struct ar9170 *ar, char *buf, + size_t bufsize, ssize_t *len) +{ + ADD(buf, *len, bufsize, "jar: ["); + + spin_lock_bh(&ar->mem_lock); + + *len += bitmap_scnprintf(&buf[*len], bufsize - *len, + ar->mem_bitmap, ar->fw.mem_blocks); + + ADD(buf, *len, bufsize, "]\n"); + + ADD(buf, *len, bufsize, "cookies: used:%3d / total:%3d, allocs:%d\n", + bitmap_weight(ar->mem_bitmap, ar->fw.mem_blocks), + ar->fw.mem_blocks, atomic_read(&ar->mem_allocs)); + + ADD(buf, *len, bufsize, "memory: free:%3d (%3d KiB) / total:%3d KiB)\n", + atomic_read(&ar->mem_free_blocks), + (atomic_read(&ar->mem_free_blocks) * ar->fw.mem_block_size) / 1024, + (ar->fw.mem_blocks * ar->fw.mem_block_size) / 1024); + + spin_unlock_bh(&ar->mem_lock); + + return buf; +} +DEBUGFS_DECLARE_RO_FILE(mem_usage, 512); + +static char *carl9170_debugfs_qos_stat_read(struct ar9170 *ar, char *buf, + size_t bufsize, ssize_t *len) +{ + ADD(buf, *len, bufsize, "%s QoS AC\n", modparam_noht ? "Hardware" : + "Software"); + + ADD(buf, *len, bufsize, "[ VO VI " + " BE BK ]\n"); + + spin_lock_bh(&ar->tx_stats_lock); + ADD(buf, *len, bufsize, "[length/limit length/limit " + "length/limit length/limit ]\n" + "[ %3d/%3d %3d/%3d " + " %3d/%3d %3d/%3d ]\n\n", + ar->tx_stats[0].len, ar->tx_stats[0].limit, + ar->tx_stats[1].len, ar->tx_stats[1].limit, + ar->tx_stats[2].len, ar->tx_stats[2].limit, + ar->tx_stats[3].len, ar->tx_stats[3].limit); + + ADD(buf, *len, bufsize, "[ total total " + " total total ]\n" + "[%10d %10d %10d %10d ]\n\n", + ar->tx_stats[0].count, ar->tx_stats[1].count, + ar->tx_stats[2].count, ar->tx_stats[3].count); + + spin_unlock_bh(&ar->tx_stats_lock); + + ADD(buf, *len, bufsize, "[ pend/waittx pend/waittx " + " pend/waittx pend/waittx]\n" + "[ %3d/%3d %3d/%3d " + " %3d/%3d %3d/%3d ]\n\n", + skb_queue_len(&ar->tx_pending[0]), + skb_queue_len(&ar->tx_status[0]), + skb_queue_len(&ar->tx_pending[1]), + skb_queue_len(&ar->tx_status[1]), + skb_queue_len(&ar->tx_pending[2]), + skb_queue_len(&ar->tx_status[2]), + skb_queue_len(&ar->tx_pending[3]), + skb_queue_len(&ar->tx_status[3])); + + return buf; +} +DEBUGFS_DECLARE_RO_FILE(qos_stat, 512); + +static void carl9170_debugfs_format_frame(struct ar9170 *ar, + struct sk_buff *skb, const char *prefix, char *buf, + ssize_t *off, ssize_t bufsize) +{ + struct _carl9170_tx_superframe *txc = (void *) skb->data; + struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb); + struct carl9170_tx_info *arinfo = (void *) txinfo->rate_driver_data; + struct ieee80211_hdr *hdr = (void *) txc->frame_data; + + ADD(buf, *off, bufsize, "%s %p, c:%2x, DA:%pM, sq:%4d, mc:%.4x, " + "pc:%.8x, to:%d ms\n", prefix, skb, txc->s.cookie, + ieee80211_get_DA(hdr), get_seq_h(hdr), + le16_to_cpu(txc->f.mac_control), le32_to_cpu(txc->f.phy_control), + jiffies_to_msecs(jiffies - arinfo->timeout)); +} + + +static char *carl9170_debugfs_ampdu_state_read(struct ar9170 *ar, char *buf, + size_t bufsize, ssize_t *len) +{ + struct carl9170_sta_tid *iter; + struct sk_buff *skb; + int cnt = 0, fc; + int offset; + + rcu_read_lock(); + list_for_each_entry_rcu(iter, &ar->tx_ampdu_list, list) { + + spin_lock_bh(&iter->lock); + ADD(buf, *len, bufsize, "Entry: #%2d TID:%1d, BSN:%4d, " + "SNX:%4d, HSN:%4d, BAW:%2d, state:%1d, toggles:%d\n", + cnt, iter->tid, iter->bsn, iter->snx, iter->hsn, + iter->max, iter->state, iter->counter); + + ADD(buf, *len, bufsize, "\tWindow: ["); + + *len += bitmap_scnprintf(&buf[*len], bufsize - *len, + iter->bitmap, CARL9170_BAW_BITS); + +#define BM_STR_OFF(offset) \ + ((CARL9170_BAW_BITS - (offset) - 1) / 4 + \ + (CARL9170_BAW_BITS - (offset) - 1) / 32 + 1) + + ADD(buf, *len, bufsize, ",W]\n"); + + offset = BM_STR_OFF(0); + ADD(buf, *len, bufsize, "\tBase Seq: %*s\n", offset, "T"); + + offset = BM_STR_OFF(SEQ_DIFF(iter->snx, iter->bsn)); + ADD(buf, *len, bufsize, "\tNext Seq: %*s\n", offset, "W"); + + offset = BM_STR_OFF(((int)iter->hsn - (int)iter->bsn) % + CARL9170_BAW_BITS); + ADD(buf, *len, bufsize, "\tLast Seq: %*s\n", offset, "N"); + + ADD(buf, *len, bufsize, "\tPre-Aggregation reorder buffer: " + " currently queued:%d\n", skb_queue_len(&iter->queue)); + + fc = 0; + skb_queue_walk(&iter->queue, skb) { + char prefix[32]; + + snprintf(prefix, sizeof(prefix), "\t\t%3d :", fc); + carl9170_debugfs_format_frame(ar, skb, prefix, buf, + len, bufsize); + + fc++; + } + spin_unlock_bh(&iter->lock); + cnt++; + } + rcu_read_unlock(); + + return buf; +} +DEBUGFS_DECLARE_RO_FILE(ampdu_state, 8000); + +static void carl9170_debugfs_queue_dump(struct ar9170 *ar, char *buf, + ssize_t *len, size_t bufsize, struct sk_buff_head *queue) +{ + struct sk_buff *skb; + char prefix[16]; + int fc = 0; + + spin_lock_bh(&queue->lock); + skb_queue_walk(queue, skb) { + snprintf(prefix, sizeof(prefix), "%3d :", fc); + carl9170_debugfs_format_frame(ar, skb, prefix, buf, + len, bufsize); + fc++; + } + spin_unlock_bh(&queue->lock); +} + +#define DEBUGFS_QUEUE_DUMP(q, qi) \ +static char *carl9170_debugfs_##q ##_##qi ##_read(struct ar9170 *ar, \ + char *buf, size_t bufsize, ssize_t *len) \ +{ \ + carl9170_debugfs_queue_dump(ar, buf, len, bufsize, &ar->q[qi]); \ + return buf; \ +} \ +DEBUGFS_DECLARE_RO_FILE(q##_##qi, 8000); + +static char *carl9170_debugfs_sta_psm_read(struct ar9170 *ar, char *buf, + size_t bufsize, ssize_t *len) +{ + ADD(buf, *len, bufsize, "psm state: %s\n", (ar->ps.off_override ? + "FORCE CAM" : (ar->ps.state ? "PSM" : "CAM"))); + + ADD(buf, *len, bufsize, "sleep duration: %d ms.\n", ar->ps.sleep_ms); + ADD(buf, *len, bufsize, "last power-state transition: %d ms ago.\n", + jiffies_to_msecs(jiffies - ar->ps.last_action)); + ADD(buf, *len, bufsize, "last CAM->PSM transition: %d ms ago.\n", + jiffies_to_msecs(jiffies - ar->ps.last_slept)); + + return buf; +} +DEBUGFS_DECLARE_RO_FILE(sta_psm, 160); + +static char *carl9170_debugfs_tx_stuck_read(struct ar9170 *ar, char *buf, + size_t bufsize, ssize_t *len) +{ + int i; + + for (i = 0; i < ar->hw->queues; i++) { + ADD(buf, *len, bufsize, "TX queue [%d]: %10d max:%10d ms.\n", + i, ieee80211_queue_stopped(ar->hw, i) ? + jiffies_to_msecs(jiffies - ar->queue_stop_timeout[i]) : 0, + jiffies_to_msecs(ar->max_queue_stop_timeout[i])); + + ar->max_queue_stop_timeout[i] = 0; + } + + return buf; +} +DEBUGFS_DECLARE_RO_FILE(tx_stuck, 180); + +static char *carl9170_debugfs_phy_noise_read(struct ar9170 *ar, char *buf, + size_t bufsize, ssize_t *len) +{ + int err; + + err = carl9170_get_noisefloor(ar); + if (err) { + *len = err; + return buf; + } + + ADD(buf, *len, bufsize, "Chain 1: %10d dBm, ext. chan.:%10d dBm\n", + ar->noise[1], ar->noise[4]); + ADD(buf, *len, bufsize, "Chain 2: %10d dBm, ext. chan.:%10d dBm\n", + ar->noise[2], ar->noise[5]); + ADD(buf, *len, bufsize, "Combined %10d dBm, ext. chan.:%10d dBm\n", + ar->noise[0], ar->noise[3]); + + return buf; +} +DEBUGFS_DECLARE_RO_FILE(phy_noise, 180); + +static char *carl9170_debugfs_vif_dump_read(struct ar9170 *ar, char *buf, + size_t bufsize, ssize_t *len) +{ + struct carl9170_vif_info *iter; + int i = 0; + + ADD(buf, *len, bufsize, "registered VIFs:%d \\ %d\n", + ar->vifs, ar->fw.vif_num); + + ADD(buf, *len, bufsize, "VIF bitmap: ["); + + *len += bitmap_scnprintf(&buf[*len], bufsize - *len, + &ar->vif_bitmap, ar->fw.vif_num); + + ADD(buf, *len, bufsize, "]\n"); + + rcu_read_lock(); + list_for_each_entry_rcu(iter, &ar->vif_list, list) { + struct ieee80211_vif *vif = carl9170_get_vif(iter); + ADD(buf, *len, bufsize, "\t%d = [%s VIF, id:%d, type:%x " + " mac:%pM %s]\n", i, (carl9170_get_main_vif(ar) == vif ? + "Master" : " Slave"), iter->id, vif->type, vif->addr, + iter->enable_beacon ? "beaconing " : ""); + i++; + } + rcu_read_unlock(); + + return buf; +} +DEBUGFS_DECLARE_RO_FILE(vif_dump, 8000); + +#define UPDATE_COUNTER(ar, name) ({ \ + u32 __tmp[ARRAY_SIZE(name##_regs)]; \ + unsigned int __i, __err = -ENODEV; \ + \ + for (__i = 0; __i < ARRAY_SIZE(name##_regs); __i++) { \ + __tmp[__i] = name##_regs[__i].reg; \ + ar->debug.stats.name##_counter[__i] = 0; \ + } \ + \ + if (IS_STARTED(ar)) \ + __err = carl9170_read_mreg(ar, ARRAY_SIZE(name##_regs), \ + __tmp, ar->debug.stats.name##_counter); \ + (__err); }) + +#define TALLY_SUM_UP(ar, name) do { \ + unsigned int __i; \ + \ + for (__i = 0; __i < ARRAY_SIZE(name##_regs); __i++) { \ + ar->debug.stats.name##_sum[__i] += \ + ar->debug.stats.name##_counter[__i]; \ + } \ +} while (0) + +#define DEBUGFS_HW_TALLY_FILE(name, f) \ +static char *carl9170_debugfs_##name ## _read(struct ar9170 *ar, \ + char *dum, size_t bufsize, ssize_t *ret) \ +{ \ + char *buf; \ + int i, max_len, err; \ + \ + max_len = ARRAY_SIZE(name##_regs) * 80; \ + buf = vmalloc(max_len); \ + if (!buf) \ + return NULL; \ + \ + err = UPDATE_COUNTER(ar, name); \ + if (err) { \ + *ret = err; \ + return buf; \ + } \ + \ + TALLY_SUM_UP(ar, name); \ + \ + for (i = 0; i < ARRAY_SIZE(name##_regs); i++) { \ + ADD(buf, *ret, max_len, "%22s = %" f "[+%" f "]\n", \ + name##_regs[i].nreg, ar->debug.stats.name ##_sum[i],\ + ar->debug.stats.name ##_counter[i]); \ + } \ + \ + return buf; \ +} \ +DEBUGFS_DECLARE_RO_FILE(name, 0); + +#define DEBUGFS_HW_REG_FILE(name, f) \ +static char *carl9170_debugfs_##name ## _read(struct ar9170 *ar, \ + char *dum, size_t bufsize, ssize_t *ret) \ +{ \ + char *buf; \ + int i, max_len, err; \ + \ + max_len = ARRAY_SIZE(name##_regs) * 80; \ + buf = vmalloc(max_len); \ + if (!buf) \ + return NULL; \ + \ + err = UPDATE_COUNTER(ar, name); \ + if (err) { \ + *ret = err; \ + return buf; \ + } \ + \ + for (i = 0; i < ARRAY_SIZE(name##_regs); i++) { \ + ADD(buf, *ret, max_len, "%22s = %" f "\n", \ + name##_regs[i].nreg, \ + ar->debug.stats.name##_counter[i]); \ + } \ + \ + return buf; \ +} \ +DEBUGFS_DECLARE_RO_FILE(name, 0); + +static ssize_t carl9170_debugfs_hw_ioread32_write(struct ar9170 *ar, + const char *buf, size_t count) +{ + int err = 0, i, n = 0, max_len = 32, res; + unsigned int reg, tmp; + + if (!count) + return 0; + + if (count > max_len) + return -E2BIG; + + res = sscanf(buf, "0x%X %d", ®, &n); + if (res < 1) { + err = -EINVAL; + goto out; + } + + if (res == 1) + n = 1; + + if (n > 15) { + err = -EMSGSIZE; + goto out; + } + + if ((reg >= 0x280000) || ((reg + (n << 2)) >= 0x280000)) { + err = -EADDRNOTAVAIL; + goto out; + } + + if (reg & 3) { + err = -EINVAL; + goto out; + } + + for (i = 0; i < n; i++) { + err = carl9170_read_reg(ar, reg + (i << 2), &tmp); + if (err) + goto out; + + ar->debug.ring[ar->debug.ring_tail].reg = reg + (i << 2); + ar->debug.ring[ar->debug.ring_tail].value = tmp; + ar->debug.ring_tail++; + ar->debug.ring_tail %= CARL9170_DEBUG_RING_SIZE; + } + +out: + return err ? err : count; +} + +static char *carl9170_debugfs_hw_ioread32_read(struct ar9170 *ar, char *buf, + size_t bufsize, ssize_t *ret) +{ + int i = 0; + + while (ar->debug.ring_head != ar->debug.ring_tail) { + ADD(buf, *ret, bufsize, "%.8x = %.8x\n", + ar->debug.ring[ar->debug.ring_head].reg, + ar->debug.ring[ar->debug.ring_head].value); + + ar->debug.ring_head++; + ar->debug.ring_head %= CARL9170_DEBUG_RING_SIZE; + + if (i++ == 64) + break; + } + ar->debug.ring_head = ar->debug.ring_tail; + return buf; +} +DEBUGFS_DECLARE_RW_FILE(hw_ioread32, CARL9170_DEBUG_RING_SIZE * 40); + +static ssize_t carl9170_debugfs_bug_write(struct ar9170 *ar, const char *buf, + size_t count) +{ + int err; + + if (count < 1) + return -EINVAL; + + switch (buf[0]) { + case 'F': + ar->needs_full_reset = true; + break; + + case 'R': + if (!IS_STARTED(ar)) { + err = -EAGAIN; + goto out; + } + + ar->needs_full_reset = false; + break; + + case 'M': + err = carl9170_mac_reset(ar); + if (err < 0) + count = err; + + goto out; + + case 'P': + err = carl9170_set_channel(ar, ar->hw->conf.channel, + ar->hw->conf.channel_type, CARL9170_RFI_COLD); + if (err < 0) + count = err; + + goto out; + + default: + return -EINVAL; + } + + carl9170_restart(ar, CARL9170_RR_USER_REQUEST); + +out: + return count; +} + +static char *carl9170_debugfs_bug_read(struct ar9170 *ar, char *buf, + size_t bufsize, ssize_t *ret) +{ + ADD(buf, *ret, bufsize, "[P]hy reinit, [R]estart, [F]ull usb reset, " + "[M]ac reset\n"); + ADD(buf, *ret, bufsize, "firmware restarts:%d, last reason:%d\n", + ar->restart_counter, ar->last_reason); + ADD(buf, *ret, bufsize, "phy reinit errors:%d (%d)\n", + ar->total_chan_fail, ar->chan_fail); + ADD(buf, *ret, bufsize, "reported firmware errors:%d\n", + ar->fw.err_counter); + ADD(buf, *ret, bufsize, "reported firmware BUGs:%d\n", + ar->fw.bug_counter); + ADD(buf, *ret, bufsize, "pending restart requests:%d\n", + atomic_read(&ar->pending_restarts)); + return buf; +} +__DEBUGFS_DECLARE_RW_FILE(bug, 400, CARL9170_STOPPED); + +static const char *erp_modes[] = { + [CARL9170_ERP_INVALID] = "INVALID", + [CARL9170_ERP_AUTO] = "Automatic", + [CARL9170_ERP_MAC80211] = "Set by MAC80211", + [CARL9170_ERP_OFF] = "Force Off", + [CARL9170_ERP_RTS] = "Force RTS", + [CARL9170_ERP_CTS] = "Force CTS" +}; + +static char *carl9170_debugfs_erp_read(struct ar9170 *ar, char *buf, + size_t bufsize, ssize_t *ret) +{ + ADD(buf, *ret, bufsize, "ERP Setting: (%d) -> %s\n", ar->erp_mode, + erp_modes[ar->erp_mode]); + return buf; +} + +static ssize_t carl9170_debugfs_erp_write(struct ar9170 *ar, const char *buf, + size_t count) +{ + int res, val; + + if (count < 1) + return -EINVAL; + + res = sscanf(buf, "%d", &val); + if (res != 1) + return -EINVAL; + + if (!((val > CARL9170_ERP_INVALID) && + (val < __CARL9170_ERP_NUM))) + return -EINVAL; + + ar->erp_mode = val; + return count; +} + +DEBUGFS_DECLARE_RW_FILE(erp, 80); + +static ssize_t carl9170_debugfs_hw_iowrite32_write(struct ar9170 *ar, + const char *buf, size_t count) +{ + int err = 0, max_len = 22, res; + u32 reg, val; + + if (!count) + return 0; + + if (count > max_len) + return -E2BIG; + + res = sscanf(buf, "0x%X 0x%X", ®, &val); + if (res != 2) { + err = -EINVAL; + goto out; + } + + if (reg <= 0x100000 || reg >= 0x280000) { + err = -EADDRNOTAVAIL; + goto out; + } + + if (reg & 3) { + err = -EINVAL; + goto out; + } + + err = carl9170_write_reg(ar, reg, val); + if (err) + goto out; + +out: + return err ? err : count; +} +DEBUGFS_DECLARE_WO_FILE(hw_iowrite32); + +DEBUGFS_HW_TALLY_FILE(hw_tx_tally, "u"); +DEBUGFS_HW_TALLY_FILE(hw_rx_tally, "u"); +DEBUGFS_HW_TALLY_FILE(hw_phy_errors, "u"); +DEBUGFS_HW_REG_FILE(hw_wlan_queue, ".8x"); +DEBUGFS_HW_REG_FILE(hw_pta_queue, ".8x"); +DEBUGFS_HW_REG_FILE(hw_ampdu_info, ".8x"); +DEBUGFS_QUEUE_DUMP(tx_status, 0); +DEBUGFS_QUEUE_DUMP(tx_status, 1); +DEBUGFS_QUEUE_DUMP(tx_status, 2); +DEBUGFS_QUEUE_DUMP(tx_status, 3); +DEBUGFS_QUEUE_DUMP(tx_pending, 0); +DEBUGFS_QUEUE_DUMP(tx_pending, 1); +DEBUGFS_QUEUE_DUMP(tx_pending, 2); +DEBUGFS_QUEUE_DUMP(tx_pending, 3); +DEBUGFS_READONLY_FILE(usb_tx_anch_urbs, 20, "%d", + atomic_read(&ar->tx_anch_urbs)); +DEBUGFS_READONLY_FILE(usb_rx_anch_urbs, 20, "%d", + atomic_read(&ar->rx_anch_urbs)); +DEBUGFS_READONLY_FILE(usb_rx_work_urbs, 20, "%d", + atomic_read(&ar->rx_work_urbs)); +DEBUGFS_READONLY_FILE(usb_rx_pool_urbs, 20, "%d", + atomic_read(&ar->rx_pool_urbs)); + +DEBUGFS_READONLY_FILE(tx_total_queued, 20, "%d", + atomic_read(&ar->tx_total_queued)); +DEBUGFS_READONLY_FILE(tx_ampdu_scheduler, 20, "%d", + atomic_read(&ar->tx_ampdu_scheduler)); +DEBUGFS_READONLY_FILE(tx_ampdu_timeout, 20, "%d", + ar->tx_ampdu_timeout); + +DEBUGFS_READONLY_FILE(tx_total_pending, 20, "%d", + atomic_read(&ar->tx_total_pending)); + +DEBUGFS_READONLY_FILE(tx_ampdu_list_len, 20, "%d", + ar->tx_ampdu_list_len); + +DEBUGFS_READONLY_FILE(tx_ampdu_upload, 20, "%d", + atomic_read(&ar->tx_ampdu_upload)); + +DEBUGFS_READONLY_FILE(tx_janitor_last_run, 64, "last run:%d ms ago", + jiffies_to_msecs(jiffies - ar->tx_janitor_last_run)); + +DEBUGFS_READONLY_FILE(tx_dropped, 20, "%d", ar->tx_dropped); + +DEBUGFS_READONLY_FILE(rx_dropped, 20, "%d", ar->rx_dropped); + +DEBUGFS_READONLY_FILE(sniffer_enabled, 20, "%d", ar->sniffer_enabled); +DEBUGFS_READONLY_FILE(rx_software_decryption, 20, "%d", + ar->rx_software_decryption); +DEBUGFS_READONLY_FILE(ampdu_factor, 20, "%d", + ar->current_factor); +DEBUGFS_READONLY_FILE(ampdu_density, 20, "%d", + ar->current_density); + +DEBUGFS_READONLY_FILE(beacon_int, 20, "%d TU", ar->global_beacon_int); +DEBUGFS_READONLY_FILE(pretbtt, 20, "%d TU", ar->global_pretbtt); + +void carl9170_debugfs_register(struct ar9170 *ar) +{ + ar->debug_dir = debugfs_create_dir(KBUILD_MODNAME, + ar->hw->wiphy->debugfsdir); + +#define DEBUGFS_ADD(name) \ + debugfs_create_file(#name, carl_debugfs_##name ##_ops.attr, \ + ar->debug_dir, ar, \ + &carl_debugfs_##name ## _ops.fops); + + DEBUGFS_ADD(usb_tx_anch_urbs); + DEBUGFS_ADD(usb_rx_pool_urbs); + DEBUGFS_ADD(usb_rx_anch_urbs); + DEBUGFS_ADD(usb_rx_work_urbs); + + DEBUGFS_ADD(tx_total_queued); + DEBUGFS_ADD(tx_total_pending); + DEBUGFS_ADD(tx_dropped); + DEBUGFS_ADD(tx_stuck); + DEBUGFS_ADD(tx_ampdu_upload); + DEBUGFS_ADD(tx_ampdu_scheduler); + DEBUGFS_ADD(tx_ampdu_list_len); + + DEBUGFS_ADD(rx_dropped); + DEBUGFS_ADD(sniffer_enabled); + DEBUGFS_ADD(rx_software_decryption); + + DEBUGFS_ADD(mem_usage); + DEBUGFS_ADD(qos_stat); + DEBUGFS_ADD(sta_psm); + DEBUGFS_ADD(ampdu_state); + + DEBUGFS_ADD(hw_tx_tally); + DEBUGFS_ADD(hw_rx_tally); + DEBUGFS_ADD(hw_phy_errors); + DEBUGFS_ADD(phy_noise); + + DEBUGFS_ADD(hw_wlan_queue); + DEBUGFS_ADD(hw_pta_queue); + DEBUGFS_ADD(hw_ampdu_info); + + DEBUGFS_ADD(ampdu_density); + DEBUGFS_ADD(ampdu_factor); + + DEBUGFS_ADD(tx_ampdu_timeout); + + DEBUGFS_ADD(tx_janitor_last_run); + + DEBUGFS_ADD(tx_status_0); + DEBUGFS_ADD(tx_status_1); + DEBUGFS_ADD(tx_status_2); + DEBUGFS_ADD(tx_status_3); + + DEBUGFS_ADD(tx_pending_0); + DEBUGFS_ADD(tx_pending_1); + DEBUGFS_ADD(tx_pending_2); + DEBUGFS_ADD(tx_pending_3); + + DEBUGFS_ADD(hw_ioread32); + DEBUGFS_ADD(hw_iowrite32); + DEBUGFS_ADD(bug); + + DEBUGFS_ADD(erp); + + DEBUGFS_ADD(vif_dump); + + DEBUGFS_ADD(beacon_int); + DEBUGFS_ADD(pretbtt); + +#undef DEBUGFS_ADD +} + +void carl9170_debugfs_unregister(struct ar9170 *ar) +{ + debugfs_remove_recursive(ar->debug_dir); +} diff --git a/drivers/net/wireless/ath/carl9170/debug.h b/drivers/net/wireless/ath/carl9170/debug.h new file mode 100644 index 000000000000..ea4b97524122 --- /dev/null +++ b/drivers/net/wireless/ath/carl9170/debug.h @@ -0,0 +1,134 @@ +/* + * Atheros CARL9170 driver + * + * debug header + * + * Copyright 2010, Christian Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef __DEBUG_H +#define __DEBUG_H + +#include "eeprom.h" +#include "wlan.h" +#include "hw.h" +#include "fwdesc.h" +#include "fwcmd.h" +#include "../regd.h" + +struct hw_stat_reg_entry { + u32 reg; + char nreg[32]; +}; + +#define STAT_MAC_REG(reg) \ + { (AR9170_MAC_REG_##reg), #reg } + +#define STAT_PTA_REG(reg) \ + { (AR9170_PTA_REG_##reg), #reg } + +#define STAT_USB_REG(reg) \ + { (AR9170_USB_REG_##reg), #reg } + +static const struct hw_stat_reg_entry hw_rx_tally_regs[] = { + STAT_MAC_REG(RX_CRC32), STAT_MAC_REG(RX_CRC16), + STAT_MAC_REG(RX_TIMEOUT_COUNT), STAT_MAC_REG(RX_ERR_DECRYPTION_UNI), + STAT_MAC_REG(RX_ERR_DECRYPTION_MUL), STAT_MAC_REG(RX_MPDU), + STAT_MAC_REG(RX_DROPPED_MPDU), STAT_MAC_REG(RX_DEL_MPDU), +}; + +static const struct hw_stat_reg_entry hw_phy_errors_regs[] = { + STAT_MAC_REG(RX_PHY_MISC_ERROR), STAT_MAC_REG(RX_PHY_XR_ERROR), + STAT_MAC_REG(RX_PHY_OFDM_ERROR), STAT_MAC_REG(RX_PHY_CCK_ERROR), + STAT_MAC_REG(RX_PHY_HT_ERROR), STAT_MAC_REG(RX_PHY_TOTAL), +}; + +static const struct hw_stat_reg_entry hw_tx_tally_regs[] = { + STAT_MAC_REG(TX_TOTAL), STAT_MAC_REG(TX_UNDERRUN), + STAT_MAC_REG(TX_RETRY), +}; + +static const struct hw_stat_reg_entry hw_wlan_queue_regs[] = { + STAT_MAC_REG(DMA_STATUS), STAT_MAC_REG(DMA_TRIGGER), + STAT_MAC_REG(DMA_TXQ0_ADDR), STAT_MAC_REG(DMA_TXQ0_CURR_ADDR), + STAT_MAC_REG(DMA_TXQ1_ADDR), STAT_MAC_REG(DMA_TXQ1_CURR_ADDR), + STAT_MAC_REG(DMA_TXQ2_ADDR), STAT_MAC_REG(DMA_TXQ2_CURR_ADDR), + STAT_MAC_REG(DMA_TXQ3_ADDR), STAT_MAC_REG(DMA_TXQ3_CURR_ADDR), + STAT_MAC_REG(DMA_RXQ_ADDR), STAT_MAC_REG(DMA_RXQ_CURR_ADDR), +}; + +static const struct hw_stat_reg_entry hw_ampdu_info_regs[] = { + STAT_MAC_REG(AMPDU_DENSITY), STAT_MAC_REG(AMPDU_FACTOR), +}; + +static const struct hw_stat_reg_entry hw_pta_queue_regs[] = { + STAT_PTA_REG(DN_CURR_ADDRH), STAT_PTA_REG(DN_CURR_ADDRL), + STAT_PTA_REG(UP_CURR_ADDRH), STAT_PTA_REG(UP_CURR_ADDRL), + STAT_PTA_REG(DMA_STATUS), STAT_PTA_REG(DMA_MODE_CTRL), +}; + +#define DEFINE_TALLY(name) \ + u32 name##_sum[ARRAY_SIZE(name##_regs)], \ + name##_counter[ARRAY_SIZE(name##_regs)] \ + +#define DEFINE_STAT(name) \ + u32 name##_counter[ARRAY_SIZE(name##_regs)] \ + +struct ath_stats { + DEFINE_TALLY(hw_tx_tally); + DEFINE_TALLY(hw_rx_tally); + DEFINE_TALLY(hw_phy_errors); + DEFINE_STAT(hw_wlan_queue); + DEFINE_STAT(hw_pta_queue); + DEFINE_STAT(hw_ampdu_info); +}; + +struct carl9170_debug_mem_rbe { + u32 reg; + u32 value; +}; + +#define CARL9170_DEBUG_RING_SIZE 64 + +struct carl9170_debug { + struct ath_stats stats; + struct carl9170_debug_mem_rbe ring[CARL9170_DEBUG_RING_SIZE]; + struct mutex ring_lock; + unsigned int ring_head, ring_tail; + struct delayed_work update_tally; +}; + +struct ar9170; + +void carl9170_debugfs_register(struct ar9170 *ar); +void carl9170_debugfs_unregister(struct ar9170 *ar); +#endif /* __DEBUG_H */ diff --git a/drivers/net/wireless/ath/carl9170/fw.c b/drivers/net/wireless/ath/carl9170/fw.c new file mode 100644 index 000000000000..36615462b87a --- /dev/null +++ b/drivers/net/wireless/ath/carl9170/fw.c @@ -0,0 +1,395 @@ +/* + * Atheros CARL9170 driver + * + * firmware parser + * + * Copyright 2009, 2010, Christian Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + */ + +#include +#include +#include +#include "carl9170.h" +#include "fwcmd.h" +#include "version.h" + +#define MAKE_STR(symbol) #symbol +#define TO_STR(symbol) MAKE_STR(symbol) +#define CARL9170FW_API_VER_STR TO_STR(CARL9170FW_API_MAX_VER) +MODULE_VERSION(CARL9170FW_API_VER_STR ":" CARL9170FW_VERSION_GIT); + +static const u8 otus_magic[4] = { OTUS_MAGIC }; + +static const void *carl9170_fw_find_desc(struct ar9170 *ar, const u8 descid[4], + const unsigned int len, const u8 compatible_revision) +{ + const struct carl9170fw_desc_head *iter; + + carl9170fw_for_each_hdr(iter, ar->fw.desc) { + if (carl9170fw_desc_cmp(iter, descid, len, + compatible_revision)) + return (void *)iter; + } + + /* needed to find the LAST desc */ + if (carl9170fw_desc_cmp(iter, descid, len, + compatible_revision)) + return (void *)iter; + + return NULL; +} + +static int carl9170_fw_verify_descs(struct ar9170 *ar, + const struct carl9170fw_desc_head *head, unsigned int max_len) +{ + const struct carl9170fw_desc_head *pos; + unsigned long pos_addr, end_addr; + unsigned int pos_length; + + if (max_len < sizeof(*pos)) + return -ENODATA; + + max_len = min_t(unsigned int, CARL9170FW_DESC_MAX_LENGTH, max_len); + + pos = head; + pos_addr = (unsigned long) pos; + end_addr = pos_addr + max_len; + + while (pos_addr < end_addr) { + if (pos_addr + sizeof(*head) > end_addr) + return -E2BIG; + + pos_length = le16_to_cpu(pos->length); + + if (pos_length < sizeof(*head)) + return -EBADMSG; + + if (pos_length > max_len) + return -EOVERFLOW; + + if (pos_addr + pos_length > end_addr) + return -EMSGSIZE; + + if (carl9170fw_desc_cmp(pos, LAST_MAGIC, + CARL9170FW_LAST_DESC_SIZE, + CARL9170FW_LAST_DESC_CUR_VER)) + return 0; + + pos_addr += pos_length; + pos = (void *)pos_addr; + max_len -= pos_length; + } + return -EINVAL; +} + +static void carl9170_fw_info(struct ar9170 *ar) +{ + const struct carl9170fw_motd_desc *motd_desc; + unsigned int str_ver_len; + u32 fw_date; + + dev_info(&ar->udev->dev, "driver API: %s 2%03d-%02d-%02d [%d-%d]\n", + CARL9170FW_VERSION_GIT, CARL9170FW_VERSION_YEAR, + CARL9170FW_VERSION_MONTH, CARL9170FW_VERSION_DAY, + CARL9170FW_API_MIN_VER, CARL9170FW_API_MAX_VER); + + motd_desc = carl9170_fw_find_desc(ar, MOTD_MAGIC, + sizeof(*motd_desc), CARL9170FW_MOTD_DESC_CUR_VER); + + if (motd_desc) { + str_ver_len = strnlen(motd_desc->release, + CARL9170FW_MOTD_RELEASE_LEN); + + fw_date = le32_to_cpu(motd_desc->fw_year_month_day); + + dev_info(&ar->udev->dev, "firmware API: %.*s 2%03d-%02d-%02d\n", + str_ver_len, motd_desc->release, + CARL9170FW_GET_YEAR(fw_date), + CARL9170FW_GET_MONTH(fw_date), + CARL9170FW_GET_DAY(fw_date)); + + strlcpy(ar->hw->wiphy->fw_version, motd_desc->release, + sizeof(ar->hw->wiphy->fw_version)); + } +} + +static bool valid_dma_addr(const u32 address) +{ + if (address >= AR9170_SRAM_OFFSET && + address < (AR9170_SRAM_OFFSET + AR9170_SRAM_SIZE)) + return true; + + return false; +} + +static bool valid_cpu_addr(const u32 address) +{ + if (valid_dma_addr(address) || (address >= AR9170_PRAM_OFFSET && + address < (AR9170_PRAM_OFFSET + AR9170_PRAM_SIZE))) + return true; + + return false; +} + +static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len) +{ + const struct carl9170fw_otus_desc *otus_desc; + const struct carl9170fw_chk_desc *chk_desc; + const struct carl9170fw_last_desc *last_desc; + + last_desc = carl9170_fw_find_desc(ar, LAST_MAGIC, + sizeof(*last_desc), CARL9170FW_LAST_DESC_CUR_VER); + if (!last_desc) + return -EINVAL; + + otus_desc = carl9170_fw_find_desc(ar, OTUS_MAGIC, + sizeof(*otus_desc), CARL9170FW_OTUS_DESC_CUR_VER); + if (!otus_desc) { + dev_err(&ar->udev->dev, "failed to find compatible firmware " + "descriptor.\n"); + return -ENODATA; + } + + chk_desc = carl9170_fw_find_desc(ar, CHK_MAGIC, + sizeof(*chk_desc), CARL9170FW_CHK_DESC_CUR_VER); + + if (chk_desc) { + unsigned long fin, diff; + unsigned int dsc_len; + u32 crc32; + + dsc_len = min_t(unsigned int, len, + (unsigned long)chk_desc - (unsigned long)otus_desc); + + fin = (unsigned long) last_desc + sizeof(*last_desc); + diff = fin - (unsigned long) otus_desc; + + if (diff < len) + len -= diff; + + if (len < 256) + return -EIO; + + crc32 = crc32_le(~0, data, len); + if (cpu_to_le32(crc32) != chk_desc->fw_crc32) { + dev_err(&ar->udev->dev, "fw checksum test failed.\n"); + return -ENOEXEC; + } + + crc32 = crc32_le(crc32, (void *)otus_desc, dsc_len); + if (cpu_to_le32(crc32) != chk_desc->hdr_crc32) { + dev_err(&ar->udev->dev, "descriptor check failed.\n"); + return -EINVAL; + } + } else { + dev_warn(&ar->udev->dev, "Unprotected firmware image.\n"); + } + +#define SUPP(feat) \ + (carl9170fw_supports(otus_desc->feature_set, feat)) + + if (!SUPP(CARL9170FW_DUMMY_FEATURE)) { + dev_err(&ar->udev->dev, "invalid firmware descriptor " + "format detected.\n"); + return -EINVAL; + } + + ar->fw.api_version = otus_desc->api_ver; + + if (ar->fw.api_version < CARL9170FW_API_MIN_VER || + ar->fw.api_version > CARL9170FW_API_MAX_VER) { + dev_err(&ar->udev->dev, "unsupported firmware api version.\n"); + return -EINVAL; + } + + if (!SUPP(CARL9170FW_COMMAND_PHY) || SUPP(CARL9170FW_UNUSABLE) || + !SUPP(CARL9170FW_HANDLE_BACK_REQ)) { + dev_err(&ar->udev->dev, "firmware does support " + "mandatory features.\n"); + return -ECANCELED; + } + + if (ilog2(le32_to_cpu(otus_desc->feature_set)) >= + __CARL9170FW_FEATURE_NUM) { + dev_warn(&ar->udev->dev, "driver does not support all " + "firmware features.\n"); + } + + if (!SUPP(CARL9170FW_COMMAND_CAM)) { + dev_info(&ar->udev->dev, "crypto offloading is disabled " + "by firmware.\n"); + ar->disable_offload = true; + } + + if (SUPP(CARL9170FW_PSM)) + ar->hw->flags |= IEEE80211_HW_SUPPORTS_PS; + + if (!SUPP(CARL9170FW_USB_INIT_FIRMWARE)) { + dev_err(&ar->udev->dev, "firmware does not provide " + "mandatory interfaces.\n"); + return -EINVAL; + } + + if (SUPP(CARL9170FW_MINIBOOT)) + ar->fw.offset = le16_to_cpu(otus_desc->miniboot_size); + else + ar->fw.offset = 0; + + if (SUPP(CARL9170FW_USB_DOWN_STREAM)) { + ar->hw->extra_tx_headroom += sizeof(struct ar9170_stream); + ar->fw.tx_stream = true; + } + + if (SUPP(CARL9170FW_USB_UP_STREAM)) + ar->fw.rx_stream = true; + + ar->fw.vif_num = otus_desc->vif_num; + ar->fw.cmd_bufs = otus_desc->cmd_bufs; + ar->fw.address = le32_to_cpu(otus_desc->fw_address); + ar->fw.rx_size = le16_to_cpu(otus_desc->rx_max_frame_len); + ar->fw.mem_blocks = min_t(unsigned int, otus_desc->tx_descs, 0xfe); + atomic_set(&ar->mem_free_blocks, ar->fw.mem_blocks); + ar->fw.mem_block_size = le16_to_cpu(otus_desc->tx_frag_len); + + if (ar->fw.vif_num >= AR9170_MAX_VIRTUAL_MAC || !ar->fw.vif_num || + ar->fw.mem_blocks < 16 || !ar->fw.cmd_bufs || + ar->fw.mem_block_size < 64 || ar->fw.mem_block_size > 512 || + ar->fw.rx_size > 32768 || ar->fw.rx_size < 4096 || + !valid_cpu_addr(ar->fw.address)) { + dev_err(&ar->udev->dev, "firmware shows obvious signs of " + "malicious tampering.\n"); + return -EINVAL; + } + + ar->fw.beacon_addr = le32_to_cpu(otus_desc->bcn_addr); + ar->fw.beacon_max_len = le16_to_cpu(otus_desc->bcn_len); + + if (valid_dma_addr(ar->fw.beacon_addr) && ar->fw.beacon_max_len >= + AR9170_MAC_BCN_LENGTH_MAX) { + ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC); + + if (SUPP(CARL9170FW_WLANTX_CAB)) { + ar->hw->wiphy->interface_modes |= + BIT(NL80211_IFTYPE_AP); + } + } + +#undef SUPPORTED + return 0; +} + +static struct carl9170fw_desc_head * +carl9170_find_fw_desc(struct ar9170 *ar, const __u8 *fw_data, const size_t len) + +{ + int scan = 0, found = 0; + + if (!carl9170fw_size_check(len)) { + dev_err(&ar->udev->dev, "firmware size is out of bound.\n"); + return NULL; + } + + while (scan < len - sizeof(struct carl9170fw_desc_head)) { + if (fw_data[scan++] == otus_magic[found]) + found++; + else + found = 0; + + if (scan >= len) + break; + + if (found == sizeof(otus_magic)) + break; + } + + if (found != sizeof(otus_magic)) + return NULL; + + return (void *)&fw_data[scan - found]; +} + +int carl9170_fw_fix_eeprom(struct ar9170 *ar) +{ + const struct carl9170fw_fix_desc *fix_desc = NULL; + unsigned int i, n, off; + u32 *data = (void *)&ar->eeprom; + + fix_desc = carl9170_fw_find_desc(ar, FIX_MAGIC, + sizeof(*fix_desc), CARL9170FW_FIX_DESC_CUR_VER); + + if (!fix_desc) + return 0; + + n = (le16_to_cpu(fix_desc->head.length) - sizeof(*fix_desc)) / + sizeof(struct carl9170fw_fix_entry); + + for (i = 0; i < n; i++) { + off = le32_to_cpu(fix_desc->data[i].address) - + AR9170_EEPROM_START; + + if (off >= sizeof(struct ar9170_eeprom) || (off & 3)) { + dev_err(&ar->udev->dev, "Skip invalid entry %d\n", i); + continue; + } + + data[off / sizeof(*data)] &= + le32_to_cpu(fix_desc->data[i].mask); + data[off / sizeof(*data)] |= + le32_to_cpu(fix_desc->data[i].value); + } + + return 0; +} + +int carl9170_parse_firmware(struct ar9170 *ar) +{ + const struct carl9170fw_desc_head *fw_desc = NULL; + const struct firmware *fw = ar->fw.fw; + unsigned long header_offset = 0; + int err; + + if (WARN_ON(!fw)) + return -EINVAL; + + fw_desc = carl9170_find_fw_desc(ar, fw->data, fw->size); + + if (!fw_desc) { + dev_err(&ar->udev->dev, "unsupported firmware.\n"); + return -ENODATA; + } + + header_offset = (unsigned long)fw_desc - (unsigned long)fw->data; + + err = carl9170_fw_verify_descs(ar, fw_desc, fw->size - header_offset); + if (err) { + dev_err(&ar->udev->dev, "damaged firmware (%d).\n", err); + return err; + } + + ar->fw.desc = fw_desc; + + carl9170_fw_info(ar); + + err = carl9170_fw(ar, fw->data, fw->size); + if (err) { + dev_err(&ar->udev->dev, "failed to parse firmware (%d).\n", + err); + return err; + } + + return 0; +} -- cgit v1.2.3 From 1d7e1e6b1b8ed456e7678130ad17086a5f5b3286 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Mon, 6 Sep 2010 01:10:25 +0200 Subject: carl9170: Makefile, Kconfig files and MAINTAINERS Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- MAINTAINERS | 7 +++++ drivers/net/wireless/ath/Kconfig | 1 + drivers/net/wireless/ath/Makefile | 1 + drivers/net/wireless/ath/carl9170/Kconfig | 41 ++++++++++++++++++++++++++++++ drivers/net/wireless/ath/carl9170/Makefile | 4 +++ 5 files changed, 54 insertions(+) create mode 100644 drivers/net/wireless/ath/carl9170/Kconfig create mode 100644 drivers/net/wireless/ath/carl9170/Makefile (limited to 'drivers/net/wireless/ath') diff --git a/MAINTAINERS b/MAINTAINERS index 4a6d93648b9c..80cea03a737e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1102,6 +1102,13 @@ W: http://wireless.kernel.org/en/users/Drivers/ar9170 S: Maintained F: drivers/net/wireless/ath/ar9170/ +CARL9170 LINUX COMMUNITY WIRELESS DRIVER +M: Christian Lamparter +L: linux-wireless@vger.kernel.org +W: http://wireless.kernel.org/en/users/Drivers/carl9170 +S: Maintained +F: drivers/net/wireless/ath/carl9170/ + ATK0110 HWMON DRIVER M: Luca Tettamanti L: lm-sensors@lm-sensors.org diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index 0a75be027afa..92c216263ee9 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -25,5 +25,6 @@ config ATH_DEBUG source "drivers/net/wireless/ath/ath5k/Kconfig" source "drivers/net/wireless/ath/ath9k/Kconfig" source "drivers/net/wireless/ath/ar9170/Kconfig" +source "drivers/net/wireless/ath/carl9170/Kconfig" endif diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile index 8113a5042afa..40fa1794d489 100644 --- a/drivers/net/wireless/ath/Makefile +++ b/drivers/net/wireless/ath/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_ATH5K) += ath5k/ obj-$(CONFIG_ATH9K_HW) += ath9k/ obj-$(CONFIG_AR9170_USB) += ar9170/ +obj-$(CONFIG_CARL9170) += carl9170/ obj-$(CONFIG_ATH_COMMON) += ath.o diff --git a/drivers/net/wireless/ath/carl9170/Kconfig b/drivers/net/wireless/ath/carl9170/Kconfig new file mode 100644 index 000000000000..c5d3a3f2e55b --- /dev/null +++ b/drivers/net/wireless/ath/carl9170/Kconfig @@ -0,0 +1,41 @@ +config CARL9170 + tristate "Linux Community AR9170 802.11n USB support" + depends on USB && MAC80211 && EXPERIMENTAL + select FW_LOADER + select CRC32 + help + This is another driver for the Atheros "otus" 802.11n USB devices. + + This driver provides more features than the original, + but it needs a special firmware (carl9170-1.fw) to do that. + + The firmware can be downloaded from our wiki here: + http://wireless.kernel.org/en/users/Drivers/carl9170 + + If you choose to build a module, it'll be called carl9170. + +config CARL9170_LEDS + bool "SoftLED Support" + depends on CARL9170 + select MAC80211_LEDS + select LEDS_CLASS + select NEW_LEDS + default y + help + This option is necessary, if you want your device' LEDs to blink + + Say Y, unless you need the LEDs for firmware debugging. + +config CARL9170_DEBUGFS + bool "DebugFS Support" + depends on CARL9170 && DEBUG_FS && MAC80211_DEBUGFS + default n + help + Export several driver and device internals to user space. + + Say N. + +config CARL9170_WPC + bool + depends on CARL9170 && (INPUT = y || INPUT = CARL9170) + default y diff --git a/drivers/net/wireless/ath/carl9170/Makefile b/drivers/net/wireless/ath/carl9170/Makefile new file mode 100644 index 000000000000..f64ed76af8ad --- /dev/null +++ b/drivers/net/wireless/ath/carl9170/Makefile @@ -0,0 +1,4 @@ +carl9170-objs := main.o usb.o cmd.o mac.o phy.o led.o fw.o tx.o rx.o +carl9170-$(CONFIG_CARL9170_DEBUGFS) += debug.o + +obj-$(CONFIG_CARL9170) += carl9170.o -- cgit v1.2.3 From 58d7e0f37cfc5b4f473d437024a03d874e5969a3 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Wed, 8 Sep 2010 15:57:12 +0530 Subject: ath9k_hw: Support fastcc for AR7010 To reduce scan time, enable fastcc for AR7010 (fastcc == fast channel change -- JWL) Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index afadade2a67e..0a61f426d7d7 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1258,7 +1258,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, (chan->channel != ah->curchan->channel) && ((chan->channelFlags & CHANNEL_ALL) == (ah->curchan->channelFlags & CHANNEL_ALL)) && - !AR_SREV_9280(ah)) { + (!AR_SREV_9280(ah) || AR_DEVID_7010(ah))) { if (ath9k_hw_channel_change(ah, chan)) { ath9k_hw_loadnf(ah, ah->curchan); -- cgit v1.2.3 From 516c6e1f52a09fb2e7024101db3f0375f65670df Mon Sep 17 00:00:00 2001 From: Fabio Rossi Date: Wed, 8 Sep 2010 22:37:41 +0200 Subject: ath5k: avoid unneeded calibration error messages Don't generate calibration errors messages when not needed. Signed-off-by: Fabio Rossi Acked-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/phy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c index 984ba92c7df3..4932bf2f35eb 100644 --- a/drivers/net/wireless/ath/ath5k/phy.c +++ b/drivers/net/wireless/ath/ath5k/phy.c @@ -1377,7 +1377,7 @@ ath5k_hw_rf511x_iq_calibrate(struct ath5k_hw *ah) /* protect against divide by 0 and loss of sign bits */ if (i_coffd == 0 || q_coffd < 2) - return -1; + return 0; i_coff = (-iq_corr) / i_coffd; i_coff = clamp(i_coff, -32, 31); /* signed 6 bit */ -- cgit v1.2.3 From bbce80e1101526f7a616e75a0d77b57f4062c62d Mon Sep 17 00:00:00 2001 From: Nikitas Angelinas Date: Wed, 8 Sep 2010 22:25:42 +0100 Subject: drivers/net/wireless/ath/ath9k: use ARRAY_SIZE macro in ani.c Replace (sizeof(ofdm_level_table)/sizeof(ofdm_level_table[0]) with ARRAY_SIZE(ofdm_level_table), and (sizeof(cck_level_table)/ sizeof(cck_level_table[0]) with ARRAY_SIZE(cck_level_table) in drivers/net/wireless/ath/ath9k/ani.c Signed-off-by: Nikitas Angelinas Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index cc648b6ae31c..0496f965314f 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -14,6 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include #include "hw.h" #include "hw-ops.h" @@ -48,7 +49,7 @@ static const struct ani_ofdm_level_entry ofdm_level_table[] = { { 7, 8, 0 } /* lvl 9 */ }; #define ATH9K_ANI_OFDM_NUM_LEVEL \ - (sizeof(ofdm_level_table)/sizeof(ofdm_level_table[0])) + ARRAY_SIZE(ofdm_level_table) #define ATH9K_ANI_OFDM_MAX_LEVEL \ (ATH9K_ANI_OFDM_NUM_LEVEL-1) #define ATH9K_ANI_OFDM_DEF_LEVEL \ @@ -94,7 +95,7 @@ static const struct ani_cck_level_entry cck_level_table[] = { }; #define ATH9K_ANI_CCK_NUM_LEVEL \ - (sizeof(cck_level_table)/sizeof(cck_level_table[0])) + ARRAY_SIZE(cck_level_table) #define ATH9K_ANI_CCK_MAX_LEVEL \ (ATH9K_ANI_CCK_NUM_LEVEL-1) #define ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI \ -- cgit v1.2.3 From cc0de6536e8b70d61948128a9cbf86920066c53d Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 14 Sep 2010 13:07:28 +0530 Subject: ath9k_htc: Fix memory leak on WMI event handler ath9k_wmi_ctrl_rx is racy with ath9k_wmi_tasklet on event notification due to which the wmi_skb may be overwritten which leads to memory leak. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/wmi.c | 72 +++++++++++------------------------- drivers/net/wireless/ath/ath9k/wmi.h | 6 +-- 2 files changed, 23 insertions(+), 55 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c index 45fe9cac7971..93a8bda09c25 100644 --- a/drivers/net/wireless/ath/ath9k/wmi.c +++ b/drivers/net/wireless/ath/ath9k/wmi.c @@ -124,55 +124,11 @@ void ath9k_wmi_tasklet(unsigned long data) { struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data; struct ath_common *common = ath9k_hw_common(priv->ah); - struct wmi_cmd_hdr *hdr; - struct wmi_swba *swba_hdr; - enum wmi_event_id event; - struct sk_buff *skb; - void *wmi_event; - unsigned long flags; -#ifdef CONFIG_ATH9K_HTC_DEBUGFS - __be32 txrate; -#endif - spin_lock_irqsave(&priv->wmi->wmi_lock, flags); - skb = priv->wmi->wmi_skb; - spin_unlock_irqrestore(&priv->wmi->wmi_lock, flags); + ath_print(common, ATH_DBG_WMI, "SWBA Event received\n"); - hdr = (struct wmi_cmd_hdr *) skb->data; - event = be16_to_cpu(hdr->command_id); - wmi_event = skb_pull(skb, sizeof(struct wmi_cmd_hdr)); + ath9k_htc_swba(priv, priv->wmi->beacon_pending); - ath_print(common, ATH_DBG_WMI, - "WMI Event: 0x%x\n", event); - - switch (event) { - case WMI_TGT_RDY_EVENTID: - break; - case WMI_SWBA_EVENTID: - swba_hdr = (struct wmi_swba *) wmi_event; - ath9k_htc_swba(priv, swba_hdr->beacon_pending); - break; - case WMI_FATAL_EVENTID: - break; - case WMI_TXTO_EVENTID: - break; - case WMI_BMISS_EVENTID: - break; - case WMI_WLAN_TXCOMP_EVENTID: - break; - case WMI_DELBA_EVENTID: - break; - case WMI_TXRATE_EVENTID: -#ifdef CONFIG_ATH9K_HTC_DEBUGFS - txrate = ((struct wmi_event_txrate *)wmi_event)->txrate; - priv->debug.txrate = be32_to_cpu(txrate); -#endif - break; - default: - break; - } - - kfree_skb(skb); } static void ath9k_wmi_rsp_callback(struct wmi *wmi, struct sk_buff *skb) @@ -191,6 +147,10 @@ static void ath9k_wmi_ctrl_rx(void *priv, struct sk_buff *skb, struct wmi *wmi = (struct wmi *) priv; struct wmi_cmd_hdr *hdr; u16 cmd_id; + void *wmi_event; +#ifdef CONFIG_ATH9K_HTC_DEBUGFS + __be32 txrate; +#endif if (unlikely(wmi->stopped)) goto free_skb; @@ -199,10 +159,22 @@ static void ath9k_wmi_ctrl_rx(void *priv, struct sk_buff *skb, cmd_id = be16_to_cpu(hdr->command_id); if (cmd_id & 0x1000) { - spin_lock(&wmi->wmi_lock); - wmi->wmi_skb = skb; - spin_unlock(&wmi->wmi_lock); - tasklet_schedule(&wmi->drv_priv->wmi_tasklet); + wmi_event = skb_pull(skb, sizeof(struct wmi_cmd_hdr)); + switch (cmd_id) { + case WMI_SWBA_EVENTID: + wmi->beacon_pending = *(u8 *)wmi_event; + tasklet_schedule(&wmi->drv_priv->wmi_tasklet); + break; + case WMI_TXRATE_EVENTID: +#ifdef CONFIG_ATH9K_HTC_DEBUGFS + txrate = ((struct wmi_event_txrate *)wmi_event)->txrate; + wmi->drv_priv->debug.txrate = be32_to_cpu(txrate); +#endif + break; + default: + break; + } + kfree_skb(skb); return; } diff --git a/drivers/net/wireless/ath/ath9k/wmi.h b/drivers/net/wireless/ath/ath9k/wmi.h index a0bf857625df..ac61074af8ac 100644 --- a/drivers/net/wireless/ath/ath9k/wmi.h +++ b/drivers/net/wireless/ath/ath9k/wmi.h @@ -31,10 +31,6 @@ struct wmi_cmd_hdr { __be16 seq_no; } __packed; -struct wmi_swba { - u8 beacon_pending; -} __packed; - enum wmi_cmd_id { WMI_ECHO_CMDID = 0x0001, WMI_ACCESS_MEMORY_CMDID, @@ -104,7 +100,7 @@ struct wmi { u32 cmd_rsp_len; bool stopped; - struct sk_buff *wmi_skb; + u8 beacon_pending; spinlock_t wmi_lock; atomic_t mwrite_cnt; -- cgit v1.2.3 From 4a0e8ecca4eeed38d4b3b7a317a3aaab4dd3cacd Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 14 Sep 2010 14:35:55 +0530 Subject: ath9k_htc: Fix CPU usage issue during scan period The CPU consumption during the scan period is high, since the register write go over Interrupt endpoint. On downloading the firmware to the target, the USB descriptors are 'patched' to change the type of the endpoints from Interrupt to Bulk. With this fix, the CPU usage during a scan run comes down to acceptable levels. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hif_usb.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 495f18950ac9..27f88a0a155b 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -92,10 +92,10 @@ static int hif_usb_send_regout(struct hif_device_usb *hif_dev, cmd->skb = skb; cmd->hif_dev = hif_dev; - usb_fill_int_urb(urb, hif_dev->udev, - usb_sndintpipe(hif_dev->udev, USB_REG_OUT_PIPE), + usb_fill_bulk_urb(urb, hif_dev->udev, + usb_sndbulkpipe(hif_dev->udev, USB_REG_OUT_PIPE), skb->data, skb->len, - hif_usb_regout_cb, cmd, 1); + hif_usb_regout_cb, cmd); usb_anchor_urb(urb, &hif_dev->regout_submitted); ret = usb_submit_urb(urb, GFP_KERNEL); @@ -822,7 +822,9 @@ static int ath9k_hif_usb_download_fw(struct hif_device_usb *hif_dev) static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev) { - int ret; + int ret, idx; + struct usb_host_interface *alt = &hif_dev->interface->altsetting[0]; + struct usb_endpoint_descriptor *endp; /* Request firmware */ ret = request_firmware(&hif_dev->firmware, hif_dev->fw_name, @@ -850,6 +852,22 @@ static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev) goto err_fw_download; } + /* On downloading the firmware to the target, the USB descriptor of EP4 + * is 'patched' to change the type of the endpoint to Bulk. This will + * bring down CPU usage during the scan period. + */ + for (idx = 0; idx < alt->desc.bNumEndpoints; idx++) { + endp = &alt->endpoint[idx].desc; + if (((endp->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) + == 0x04) && + ((endp->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_INT)) { + endp->bmAttributes &= ~USB_ENDPOINT_XFERTYPE_MASK; + endp->bmAttributes |= USB_ENDPOINT_XFER_BULK; + endp->bInterval = 0; + } + } + return 0; err_fw_download: -- cgit v1.2.3 From 31a01645c0c7b46d7f74777cd2a66ddb2a22bbd9 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 14 Sep 2010 18:37:19 +0200 Subject: ath9k: fix BSSID mask calculation At the time the .add_interface driver op is called, the interface has not been marked as running yet, so ieee80211_iterate_active_interfaces will not pass it to the iterator function. Because of this, the calculated BSSID mask is wrong, which breaks multi-BSS operation. Additionally, the current way of comparing all addresses against each other is pointless, as the hardware only uses the hardware MAC address and the BSSID mask for matching the destination address, so all the address array reallocation is completely unnecessary. This patch simplifies the logic by setting the initial mask bytes to 0xff and removing all bits in the iterator call that don't match the hardware MAC address. It also calls the iterator for the vif that was passed to add_interface() Signed-off-by: Felix Fietkau Tested-by: Ben Greear Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 2 +- drivers/net/wireless/ath/ath9k/main.c | 2 +- drivers/net/wireless/ath/ath9k/virtual.c | 63 ++++++++------------------------ 3 files changed, 17 insertions(+), 50 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 5fe570bb5c4c..c8ff417f6c10 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -719,7 +719,7 @@ static inline void ath_ahb_exit(void) {}; void ath9k_ps_wakeup(struct ath_softc *sc); void ath9k_ps_restore(struct ath_softc *sc); -void ath9k_set_bssid_mask(struct ieee80211_hw *hw); +void ath9k_set_bssid_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif); int ath9k_wiphy_add(struct ath_softc *sc); int ath9k_wiphy_del(struct ath_wiphy *aphy); void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 1165f909ef04..d3f96e4520f1 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1415,7 +1415,7 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, sc->nvifs++; if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) - ath9k_set_bssid_mask(hw); + ath9k_set_bssid_mask(hw, vif); if (sc->nvifs > 1) goto out; /* skip global settings for secondary vif */ diff --git a/drivers/net/wireless/ath/ath9k/virtual.c b/drivers/net/wireless/ath/ath9k/virtual.c index fd20241f57d8..ec7cf5ee56bc 100644 --- a/drivers/net/wireless/ath/ath9k/virtual.c +++ b/drivers/net/wireless/ath/ath9k/virtual.c @@ -19,45 +19,36 @@ #include "ath9k.h" struct ath9k_vif_iter_data { - int count; - u8 *addr; + const u8 *hw_macaddr; + u8 mask[ETH_ALEN]; }; static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct ath9k_vif_iter_data *iter_data = data; - u8 *nbuf; - - nbuf = krealloc(iter_data->addr, (iter_data->count + 1) * ETH_ALEN, - GFP_ATOMIC); - if (nbuf == NULL) - return; + int i; - memcpy(nbuf + iter_data->count * ETH_ALEN, mac, ETH_ALEN); - iter_data->addr = nbuf; - iter_data->count++; + for (i = 0; i < ETH_ALEN; i++) + iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]); } -void ath9k_set_bssid_mask(struct ieee80211_hw *hw) +void ath9k_set_bssid_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath9k_vif_iter_data iter_data; - int i, j; - u8 mask[ETH_ALEN]; + int i; /* - * Add primary MAC address even if it is not in active use since it - * will be configured to the hardware as the starting point and the - * BSSID mask will need to be changed if another address is active. + * Use the hardware MAC address as reference, the hardware uses it + * together with the BSSID mask when matching addresses. */ - iter_data.addr = kmalloc(ETH_ALEN, GFP_ATOMIC); - if (iter_data.addr) { - memcpy(iter_data.addr, common->macaddr, ETH_ALEN); - iter_data.count = 1; - } else - iter_data.count = 0; + iter_data.hw_macaddr = common->macaddr; + memset(&iter_data.mask, 0xff, ETH_ALEN); + + if (vif) + ath9k_vif_iter(&iter_data, vif->addr, vif); /* Get list of all active MAC addresses */ spin_lock_bh(&sc->wiphy_lock); @@ -71,31 +62,7 @@ void ath9k_set_bssid_mask(struct ieee80211_hw *hw) } spin_unlock_bh(&sc->wiphy_lock); - /* Generate an address mask to cover all active addresses */ - memset(mask, 0, ETH_ALEN); - for (i = 0; i < iter_data.count; i++) { - u8 *a1 = iter_data.addr + i * ETH_ALEN; - for (j = i + 1; j < iter_data.count; j++) { - u8 *a2 = iter_data.addr + j * ETH_ALEN; - mask[0] |= a1[0] ^ a2[0]; - mask[1] |= a1[1] ^ a2[1]; - mask[2] |= a1[2] ^ a2[2]; - mask[3] |= a1[3] ^ a2[3]; - mask[4] |= a1[4] ^ a2[4]; - mask[5] |= a1[5] ^ a2[5]; - } - } - - kfree(iter_data.addr); - - /* Invert the mask and configure hardware */ - common->bssidmask[0] = ~mask[0]; - common->bssidmask[1] = ~mask[1]; - common->bssidmask[2] = ~mask[2]; - common->bssidmask[3] = ~mask[3]; - common->bssidmask[4] = ~mask[4]; - common->bssidmask[5] = ~mask[5]; - + memcpy(common->bssidmask, iter_data.mask, ETH_ALEN); ath_hw_setbssidmask(common); } -- cgit v1.2.3 From 3ae74c33c4f799f6bf6d67240a94a0814a8f1944 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 14 Sep 2010 18:38:26 +0200 Subject: ath9k_hw: handle rx key miss If AR_KeyMiss is set in the rx descriptor and AR_RxFrameOK is unset, the hardware could not locate a valid key during a decryption attempt. In this case, the frame must not be reported as decrypted, otherwise mac80211 sees only random garbage. Signed-off-by: Felix Fietkau Cc: stable@kernel.org Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mac.c | 3 ++- drivers/net/wireless/ath/ath9k/mac.c | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c index a462da23e87e..3b424ca1ba84 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c @@ -616,7 +616,8 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs, rxs->rs_status |= ATH9K_RXERR_DECRYPT; } else if (rxsp->status11 & AR_MichaelErr) { rxs->rs_status |= ATH9K_RXERR_MIC; - } + } else if (rxsp->status11 & AR_KeyMiss) + rxs->rs_status |= ATH9K_RXERR_DECRYPT; } return 0; diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index 0b7d1253f0c0..3efda8a8a3c1 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -714,6 +714,8 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, else if ((ads.ds_rxstatus8 & AR_MichaelErr) && rs->rs_keyix != ATH9K_RXKEYIX_INVALID) rs->rs_status |= ATH9K_RXERR_MIC; + else if (ads.ds_rxstatus8 & AR_KeyMiss) + rs->rs_status |= ATH9K_RXERR_DECRYPT; } return 0; -- cgit v1.2.3 From 364734fafbba0c3133e482db78149b9a823ae7a5 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 14 Sep 2010 20:22:44 +0200 Subject: ath9k_hw: remove useless hw capability flags Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/htc_drv_init.c | 3 +-- drivers/net/wireless/ath/ath9k/htc_drv_txrx.c | 3 +-- drivers/net/wireless/ath/ath9k/hw.c | 21 --------------- drivers/net/wireless/ath/ath9k/hw.h | 37 ++++++++++----------------- drivers/net/wireless/ath/ath9k/init.c | 3 +-- drivers/net/wireless/ath/ath9k/main.c | 12 ++------- drivers/net/wireless/ath/ath9k/recv.c | 3 +-- 7 files changed, 19 insertions(+), 63 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index 695e2b088d10..717f2b821f0d 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -601,8 +601,7 @@ static void ath9k_init_misc(struct ath9k_htc_priv *priv) common->tx_chainmask = priv->ah->caps.tx_chainmask; common->rx_chainmask = priv->ah->caps.rx_chainmask; - if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) - memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); + memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); priv->ah->opmode = NL80211_IFTYPE_STATION; } diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index 2a6e45a293a9..c99600aff76d 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -415,8 +415,7 @@ static void ath9k_htc_opmode_init(struct ath9k_htc_priv *priv) ath9k_hw_setrxfilter(ah, rfilt); /* configure bssid mask */ - if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) - ath_hw_setbssidmask(common); + ath_hw_setbssidmask(common); /* configure operational mode */ ath9k_hw_setopmode(ah); diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 0a61f426d7d7..40c6451602d3 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1574,12 +1574,6 @@ bool ath9k_hw_set_keycache_entry(struct ath_hw *ah, u16 entry, keyType = AR_KEYTABLE_TYPE_AES; break; case ATH9K_CIPHER_AES_CCM: - if (!(pCap->hw_caps & ATH9K_HW_CAP_CIPHER_AESCCM)) { - ath_print(common, ATH_DBG_ANY, - "AES-CCM not supported by mac rev 0x%x\n", - ah->hw_version.macRev); - return false; - } keyType = AR_KEYTABLE_TYPE_CCM; break; case ATH9K_CIPHER_TKIP: @@ -2143,24 +2137,11 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) pCap->low_5ghz_chan = 4920; pCap->high_5ghz_chan = 6100; - pCap->hw_caps &= ~ATH9K_HW_CAP_CIPHER_CKIP; - pCap->hw_caps |= ATH9K_HW_CAP_CIPHER_TKIP; - pCap->hw_caps |= ATH9K_HW_CAP_CIPHER_AESCCM; - - pCap->hw_caps &= ~ATH9K_HW_CAP_MIC_CKIP; - pCap->hw_caps |= ATH9K_HW_CAP_MIC_TKIP; - pCap->hw_caps |= ATH9K_HW_CAP_MIC_AESCCM; - if (ah->config.ht_enable) pCap->hw_caps |= ATH9K_HW_CAP_HT; else pCap->hw_caps &= ~ATH9K_HW_CAP_HT; - pCap->hw_caps |= ATH9K_HW_CAP_GTT; - pCap->hw_caps |= ATH9K_HW_CAP_VEOL; - pCap->hw_caps |= ATH9K_HW_CAP_BSSIDMASK; - pCap->hw_caps &= ~ATH9K_HW_CAP_MCAST_KEYSEARCH; - if (capField & AR_EEPROM_EEPCAP_MAXQCU) pCap->total_queues = MS(capField, AR_EEPROM_EEPCAP_MAXQCU); @@ -2173,8 +2154,6 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) else pCap->keycache_size = AR_KEYTABLE_SIZE; - pCap->hw_caps |= ATH9K_HW_CAP_FASTCC; - if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) pCap->tx_triglevel_max = MAX_TX_FIFO_THRESHOLD >> 1; else diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 1724b17bfc73..a3c2ce235123 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -181,30 +181,19 @@ enum wireless_mode { }; enum ath9k_hw_caps { - ATH9K_HW_CAP_MIC_AESCCM = BIT(0), - ATH9K_HW_CAP_MIC_CKIP = BIT(1), - ATH9K_HW_CAP_MIC_TKIP = BIT(2), - ATH9K_HW_CAP_CIPHER_AESCCM = BIT(3), - ATH9K_HW_CAP_CIPHER_CKIP = BIT(4), - ATH9K_HW_CAP_CIPHER_TKIP = BIT(5), - ATH9K_HW_CAP_VEOL = BIT(6), - ATH9K_HW_CAP_BSSIDMASK = BIT(7), - ATH9K_HW_CAP_MCAST_KEYSEARCH = BIT(8), - ATH9K_HW_CAP_HT = BIT(9), - ATH9K_HW_CAP_GTT = BIT(10), - ATH9K_HW_CAP_FASTCC = BIT(11), - ATH9K_HW_CAP_RFSILENT = BIT(12), - ATH9K_HW_CAP_CST = BIT(13), - ATH9K_HW_CAP_ENHANCEDPM = BIT(14), - ATH9K_HW_CAP_AUTOSLEEP = BIT(15), - ATH9K_HW_CAP_4KB_SPLITTRANS = BIT(16), - ATH9K_HW_CAP_EDMA = BIT(17), - ATH9K_HW_CAP_RAC_SUPPORTED = BIT(18), - ATH9K_HW_CAP_LDPC = BIT(19), - ATH9K_HW_CAP_FASTCLOCK = BIT(20), - ATH9K_HW_CAP_SGI_20 = BIT(21), - ATH9K_HW_CAP_PAPRD = BIT(22), - ATH9K_HW_CAP_ANT_DIV_COMB = BIT(23), + ATH9K_HW_CAP_HT = BIT(0), + ATH9K_HW_CAP_RFSILENT = BIT(1), + ATH9K_HW_CAP_CST = BIT(2), + ATH9K_HW_CAP_ENHANCEDPM = BIT(3), + ATH9K_HW_CAP_AUTOSLEEP = BIT(4), + ATH9K_HW_CAP_4KB_SPLITTRANS = BIT(5), + ATH9K_HW_CAP_EDMA = BIT(6), + ATH9K_HW_CAP_RAC_SUPPORTED = BIT(7), + ATH9K_HW_CAP_LDPC = BIT(8), + ATH9K_HW_CAP_FASTCLOCK = BIT(9), + ATH9K_HW_CAP_SGI_20 = BIT(10), + ATH9K_HW_CAP_PAPRD = BIT(11), + ATH9K_HW_CAP_ANT_DIV_COMB = BIT(12), }; struct ath9k_hw_capabilities { diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index e7c07b3370cc..5c77a9913e03 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -522,8 +522,7 @@ static void ath9k_init_misc(struct ath_softc *sc) ath9k_hw_set_diversity(sc->sc_ah, true); sc->rx.defant = ath9k_hw_getdefantenna(sc->sc_ah); - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) - memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); + memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); sc->beacon.slottime = ATH9K_SLOT_TIME_9; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index d3f96e4520f1..a2f7eb2a552b 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1156,8 +1156,7 @@ static int ath9k_start(struct ieee80211_hw *hw) else ah->imask |= ATH9K_INT_RX; - if (ah->caps.hw_caps & ATH9K_HW_CAP_GTT) - ah->imask |= ATH9K_INT_GTT; + ah->imask |= ATH9K_INT_GTT; if (ah->caps.hw_caps & ATH9K_HW_CAP_HT) ah->imask |= ATH9K_INT_CST; @@ -1379,12 +1378,6 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, mutex_lock(&sc->mutex); - if (!(ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) && - sc->nvifs > 0) { - ret = -ENOBUFS; - goto out; - } - switch (vif->type) { case NL80211_IFTYPE_STATION: ic_opmode = NL80211_IFTYPE_STATION; @@ -1414,8 +1407,7 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, sc->nvifs++; - if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) - ath9k_set_bssid_mask(hw, vif); + ath9k_set_bssid_mask(hw, vif); if (sc->nvifs > 1) goto out; /* skip global settings for secondary vif */ diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index b32c8f033d20..6fb3b4536045 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -119,8 +119,7 @@ static void ath_opmode_init(struct ath_softc *sc) ath9k_hw_setrxfilter(ah, rfilt); /* configure bssid mask */ - if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) - ath_hw_setbssidmask(common); + ath_hw_setbssidmask(common); /* configure operational mode */ ath9k_hw_setopmode(ah); -- cgit v1.2.3 From 34a1305137f484ee1806df5a00b9d8ee8d4ef758 Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Wed, 8 Sep 2010 16:04:33 +0900 Subject: ath: Copy cryptographic capability flags into ath This will be used later in this patch series. Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index a706202fa67c..057fdd7ddaf4 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -71,6 +71,15 @@ struct ath_regulatory { struct reg_dmn_pair_mapping *regpair; }; +enum ath_crypt_caps { + ATH_CRYPT_CAP_MIC_AESCCM = BIT(0), + ATH_CRYPT_CAP_MIC_CKIP = BIT(1), + ATH_CRYPT_CAP_MIC_TKIP = BIT(2), + ATH_CRYPT_CAP_CIPHER_AESCCM = BIT(3), + ATH_CRYPT_CAP_CIPHER_CKIP = BIT(4), + ATH_CRYPT_CAP_CIPHER_TKIP = BIT(5), +}; + /** * struct ath_ops - Register read/write operations * @@ -121,6 +130,7 @@ struct ath_common { DECLARE_BITMAP(keymap, ATH_KEYMAX); DECLARE_BITMAP(tkip_keymap, ATH_KEYMAX); u8 splitmic; + enum ath_crypt_caps crypt_caps; struct ath_regulatory regulatory; const struct ath_ops *ops; -- cgit v1.2.3 From 1bba5b7329e15555dab90071b24ca84d0afcc635 Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Wed, 8 Sep 2010 16:04:38 +0900 Subject: ath: Copy key cache management functions from ath9k to ath Copied the key cache management functions from ath9k (common.c and hw.c) to ath/key.c so we can use them from ath5k, later. Minor changes have been made: - renamed ath9k_* to ath_* - replaced ah->caps.keycache_size with common->keymax - removed ATH9K_IS_MIC_ENABLED since it is always true. - the AR_PCU_MIC_NEW_LOC_ENA flag is replaced with (splitmic == 0). Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/Makefile | 3 +- drivers/net/wireless/ath/ath.h | 27 ++ drivers/net/wireless/ath/key.c | 568 ++++++++++++++++++++++++++++++++++++++ drivers/net/wireless/ath/reg.h | 23 ++ 4 files changed, 620 insertions(+), 1 deletion(-) create mode 100644 drivers/net/wireless/ath/key.c (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile index 40fa1794d489..6d711ec97ec2 100644 --- a/drivers/net/wireless/ath/Makefile +++ b/drivers/net/wireless/ath/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_ATH_COMMON) += ath.o ath-objs := main.o \ regd.o \ - hw.o + hw.o \ + key.o ath-$(CONFIG_ATH_DEBUG) += debug.o diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index 057fdd7ddaf4..fb24f66373fd 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -80,6 +80,27 @@ enum ath_crypt_caps { ATH_CRYPT_CAP_CIPHER_TKIP = BIT(5), }; +struct ath_keyval { + u8 kv_type; + u8 kv_pad; + u16 kv_len; + u8 kv_val[16]; /* TK */ + u8 kv_mic[8]; /* Michael MIC key */ + u8 kv_txmic[8]; /* Michael MIC TX key (used only if the hardware + * supports both MIC keys in the same key cache entry; + * in that case, kv_mic is the RX key) */ +}; + +enum ath_cipher { + ATH_CIPHER_WEP = 0, + ATH_CIPHER_AES_OCB = 1, + ATH_CIPHER_AES_CCM = 2, + ATH_CIPHER_CKIP = 3, + ATH_CIPHER_TKIP = 4, + ATH_CIPHER_CLR = 5, + ATH_CIPHER_MIC = 127 +}; + /** * struct ath_ops - Register read/write operations * @@ -142,5 +163,11 @@ struct sk_buff *ath_rxbuf_alloc(struct ath_common *common, gfp_t gfp_mask); void ath_hw_setbssidmask(struct ath_common *common); +void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key); +int ath_key_config(struct ath_common *common, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); +bool ath_hw_keyreset(struct ath_common *common, u16 entry); #endif /* ATH_H */ diff --git a/drivers/net/wireless/ath/key.c b/drivers/net/wireless/ath/key.c new file mode 100644 index 000000000000..e45b8546810a --- /dev/null +++ b/drivers/net/wireless/ath/key.c @@ -0,0 +1,568 @@ +/* + * Copyright (c) 2009 Atheros Communications Inc. + * Copyright (c) 2010 Bruno Randolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "ath.h" +#include "reg.h" +#include "debug.h" + +#define REG_READ (common->ops->read) +#define REG_WRITE(_ah, _reg, _val) (common->ops->write)(_ah, _val, _reg) + +#define IEEE80211_WEP_NKID 4 /* number of key ids */ + +/************************/ +/* Key Cache Management */ +/************************/ + +bool ath_hw_keyreset(struct ath_common *common, u16 entry) +{ + u32 keyType; + void *ah = common->ah; + + if (entry >= common->keymax) { + ath_print(common, ATH_DBG_FATAL, + "keychache entry %u out of range\n", entry); + return false; + } + + keyType = REG_READ(ah, AR_KEYTABLE_TYPE(entry)); + + REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), 0); + REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), AR_KEYTABLE_TYPE_CLR); + REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), 0); + REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), 0); + + if (keyType == AR_KEYTABLE_TYPE_TKIP) { + u16 micentry = entry + 64; + + REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0); + + } + + return true; +} +EXPORT_SYMBOL(ath_hw_keyreset); + +bool ath_hw_keysetmac(struct ath_common *common, u16 entry, const u8 *mac) +{ + u32 macHi, macLo; + u32 unicast_flag = AR_KEYTABLE_VALID; + void *ah = common->ah; + + if (entry >= common->keymax) { + ath_print(common, ATH_DBG_FATAL, + "keychache entry %u out of range\n", entry); + return false; + } + + if (mac != NULL) { + /* + * AR_KEYTABLE_VALID indicates that the address is a unicast + * address, which must match the transmitter address for + * decrypting frames. + * Not setting this bit allows the hardware to use the key + * for multicast frame decryption. + */ + if (mac[0] & 0x01) + unicast_flag = 0; + + macHi = (mac[5] << 8) | mac[4]; + macLo = (mac[3] << 24) | + (mac[2] << 16) | + (mac[1] << 8) | + mac[0]; + macLo >>= 1; + macLo |= (macHi & 1) << 31; + macHi >>= 1; + } else { + macLo = macHi = 0; + } + REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), macLo); + REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), macHi | unicast_flag); + + return true; +} + +bool ath_hw_set_keycache_entry(struct ath_common *common, u16 entry, + const struct ath_keyval *k, + const u8 *mac) +{ + void *ah = common->ah; + u32 key0, key1, key2, key3, key4; + u32 keyType; + + if (entry >= common->keymax) { + ath_print(common, ATH_DBG_FATAL, + "keycache entry %u out of range\n", entry); + return false; + } + + switch (k->kv_type) { + case ATH_CIPHER_AES_OCB: + keyType = AR_KEYTABLE_TYPE_AES; + break; + case ATH_CIPHER_AES_CCM: + if (!(common->crypt_caps & ATH_CRYPT_CAP_CIPHER_AESCCM)) { + ath_print(common, ATH_DBG_ANY, + "AES-CCM not supported by this mac rev\n"); + return false; + } + keyType = AR_KEYTABLE_TYPE_CCM; + break; + case ATH_CIPHER_TKIP: + keyType = AR_KEYTABLE_TYPE_TKIP; + if (entry + 64 >= common->keymax) { + ath_print(common, ATH_DBG_ANY, + "entry %u inappropriate for TKIP\n", entry); + return false; + } + break; + case ATH_CIPHER_WEP: + if (k->kv_len < WLAN_KEY_LEN_WEP40) { + ath_print(common, ATH_DBG_ANY, + "WEP key length %u too small\n", k->kv_len); + return false; + } + if (k->kv_len <= WLAN_KEY_LEN_WEP40) + keyType = AR_KEYTABLE_TYPE_40; + else if (k->kv_len <= WLAN_KEY_LEN_WEP104) + keyType = AR_KEYTABLE_TYPE_104; + else + keyType = AR_KEYTABLE_TYPE_128; + break; + case ATH_CIPHER_CLR: + keyType = AR_KEYTABLE_TYPE_CLR; + break; + default: + ath_print(common, ATH_DBG_FATAL, + "cipher %u not supported\n", k->kv_type); + return false; + } + + key0 = get_unaligned_le32(k->kv_val + 0); + key1 = get_unaligned_le16(k->kv_val + 4); + key2 = get_unaligned_le32(k->kv_val + 6); + key3 = get_unaligned_le16(k->kv_val + 10); + key4 = get_unaligned_le32(k->kv_val + 12); + if (k->kv_len <= WLAN_KEY_LEN_WEP104) + key4 &= 0xff; + + /* + * Note: Key cache registers access special memory area that requires + * two 32-bit writes to actually update the values in the internal + * memory. Consequently, the exact order and pairs used here must be + * maintained. + */ + + if (keyType == AR_KEYTABLE_TYPE_TKIP) { + u16 micentry = entry + 64; + + /* + * Write inverted key[47:0] first to avoid Michael MIC errors + * on frames that could be sent or received at the same time. + * The correct key will be written in the end once everything + * else is ready. + */ + REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), ~key0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), ~key1); + + /* Write key[95:48] */ + REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2); + REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3); + + /* Write key[127:96] and key type */ + REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4); + REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType); + + /* Write MAC address for the entry */ + (void) ath_hw_keysetmac(common, entry, mac); + + if (common->splitmic == 0) { + /* + * TKIP uses two key cache entries: + * Michael MIC TX/RX keys in the same key cache entry + * (idx = main index + 64): + * key0 [31:0] = RX key [31:0] + * key1 [15:0] = TX key [31:16] + * key1 [31:16] = reserved + * key2 [31:0] = RX key [63:32] + * key3 [15:0] = TX key [15:0] + * key3 [31:16] = reserved + * key4 [31:0] = TX key [63:32] + */ + u32 mic0, mic1, mic2, mic3, mic4; + + mic0 = get_unaligned_le32(k->kv_mic + 0); + mic2 = get_unaligned_le32(k->kv_mic + 4); + mic1 = get_unaligned_le16(k->kv_txmic + 2) & 0xffff; + mic3 = get_unaligned_le16(k->kv_txmic + 0) & 0xffff; + mic4 = get_unaligned_le32(k->kv_txmic + 4); + + /* Write RX[31:0] and TX[31:16] */ + REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), mic1); + + /* Write RX[63:32] and TX[15:0] */ + REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2); + REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), mic3); + + /* Write TX[63:32] and keyType(reserved) */ + REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), mic4); + REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry), + AR_KEYTABLE_TYPE_CLR); + + } else { + /* + * TKIP uses four key cache entries (two for group + * keys): + * Michael MIC TX/RX keys are in different key cache + * entries (idx = main index + 64 for TX and + * main index + 32 + 96 for RX): + * key0 [31:0] = TX/RX MIC key [31:0] + * key1 [31:0] = reserved + * key2 [31:0] = TX/RX MIC key [63:32] + * key3 [31:0] = reserved + * key4 [31:0] = reserved + * + * Upper layer code will call this function separately + * for TX and RX keys when these registers offsets are + * used. + */ + u32 mic0, mic2; + + mic0 = get_unaligned_le32(k->kv_mic + 0); + mic2 = get_unaligned_le32(k->kv_mic + 4); + + /* Write MIC key[31:0] */ + REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0); + + /* Write MIC key[63:32] */ + REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2); + REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0); + + /* Write TX[63:32] and keyType(reserved) */ + REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), 0); + REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry), + AR_KEYTABLE_TYPE_CLR); + } + + /* MAC address registers are reserved for the MIC entry */ + REG_WRITE(ah, AR_KEYTABLE_MAC0(micentry), 0); + REG_WRITE(ah, AR_KEYTABLE_MAC1(micentry), 0); + + /* + * Write the correct (un-inverted) key[47:0] last to enable + * TKIP now that all other registers are set with correct + * values. + */ + REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1); + } else { + /* Write key[47:0] */ + REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1); + + /* Write key[95:48] */ + REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2); + REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3); + + /* Write key[127:96] and key type */ + REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4); + REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType); + + /* Write MAC address for the entry */ + (void) ath_hw_keysetmac(common, entry, mac); + } + + return true; +} + +static int ath_setkey_tkip(struct ath_common *common, u16 keyix, const u8 *key, + struct ath_keyval *hk, const u8 *addr, + bool authenticator) +{ + const u8 *key_rxmic; + const u8 *key_txmic; + + key_txmic = key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY; + key_rxmic = key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY; + + if (addr == NULL) { + /* + * Group key installation - only two key cache entries are used + * regardless of splitmic capability since group key is only + * used either for TX or RX. + */ + if (authenticator) { + memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic)); + memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_mic)); + } else { + memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); + memcpy(hk->kv_txmic, key_rxmic, sizeof(hk->kv_mic)); + } + return ath_hw_set_keycache_entry(common, keyix, hk, addr); + } + if (!common->splitmic) { + /* TX and RX keys share the same key cache entry. */ + memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); + memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic)); + return ath_hw_set_keycache_entry(common, keyix, hk, addr); + } + + /* Separate key cache entries for TX and RX */ + + /* TX key goes at first index, RX key at +32. */ + memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic)); + if (!ath_hw_set_keycache_entry(common, keyix, hk, NULL)) { + /* TX MIC entry failed. No need to proceed further */ + ath_print(common, ATH_DBG_FATAL, + "Setting TX MIC Key Failed\n"); + return 0; + } + + memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); + /* XXX delete tx key on failure? */ + return ath_hw_set_keycache_entry(common, keyix + 32, hk, addr); +} + +static int ath_reserve_key_cache_slot_tkip(struct ath_common *common) +{ + int i; + + for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) { + if (test_bit(i, common->keymap) || + test_bit(i + 64, common->keymap)) + continue; /* At least one part of TKIP key allocated */ + if (common->splitmic && + (test_bit(i + 32, common->keymap) || + test_bit(i + 64 + 32, common->keymap))) + continue; /* At least one part of TKIP key allocated */ + + /* Found a free slot for a TKIP key */ + return i; + } + return -1; +} + +static int ath_reserve_key_cache_slot(struct ath_common *common, + u32 cipher) +{ + int i; + + if (cipher == WLAN_CIPHER_SUITE_TKIP) + return ath_reserve_key_cache_slot_tkip(common); + + /* First, try to find slots that would not be available for TKIP. */ + if (common->splitmic) { + for (i = IEEE80211_WEP_NKID; i < common->keymax / 4; i++) { + if (!test_bit(i, common->keymap) && + (test_bit(i + 32, common->keymap) || + test_bit(i + 64, common->keymap) || + test_bit(i + 64 + 32, common->keymap))) + return i; + if (!test_bit(i + 32, common->keymap) && + (test_bit(i, common->keymap) || + test_bit(i + 64, common->keymap) || + test_bit(i + 64 + 32, common->keymap))) + return i + 32; + if (!test_bit(i + 64, common->keymap) && + (test_bit(i , common->keymap) || + test_bit(i + 32, common->keymap) || + test_bit(i + 64 + 32, common->keymap))) + return i + 64; + if (!test_bit(i + 64 + 32, common->keymap) && + (test_bit(i, common->keymap) || + test_bit(i + 32, common->keymap) || + test_bit(i + 64, common->keymap))) + return i + 64 + 32; + } + } else { + for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) { + if (!test_bit(i, common->keymap) && + test_bit(i + 64, common->keymap)) + return i; + if (test_bit(i, common->keymap) && + !test_bit(i + 64, common->keymap)) + return i + 64; + } + } + + /* No partially used TKIP slots, pick any available slot */ + for (i = IEEE80211_WEP_NKID; i < common->keymax; i++) { + /* Do not allow slots that could be needed for TKIP group keys + * to be used. This limitation could be removed if we know that + * TKIP will not be used. */ + if (i >= 64 && i < 64 + IEEE80211_WEP_NKID) + continue; + if (common->splitmic) { + if (i >= 32 && i < 32 + IEEE80211_WEP_NKID) + continue; + if (i >= 64 + 32 && i < 64 + 32 + IEEE80211_WEP_NKID) + continue; + } + + if (!test_bit(i, common->keymap)) + return i; /* Found a free slot for a key */ + } + + /* No free slot found */ + return -1; +} + +/* + * Configure encryption in the HW. + */ +int ath_key_config(struct ath_common *common, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct ath_keyval hk; + const u8 *mac = NULL; + u8 gmac[ETH_ALEN]; + int ret = 0; + int idx; + + memset(&hk, 0, sizeof(hk)); + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + hk.kv_type = ATH_CIPHER_WEP; + break; + case WLAN_CIPHER_SUITE_TKIP: + hk.kv_type = ATH_CIPHER_TKIP; + break; + case WLAN_CIPHER_SUITE_CCMP: + hk.kv_type = ATH_CIPHER_AES_CCM; + break; + default: + return -EOPNOTSUPP; + } + + hk.kv_len = key->keylen; + memcpy(hk.kv_val, key->key, key->keylen); + + if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { + switch (vif->type) { + case NL80211_IFTYPE_AP: + memcpy(gmac, vif->addr, ETH_ALEN); + gmac[0] |= 0x01; + mac = gmac; + idx = ath_reserve_key_cache_slot(common, key->cipher); + break; + case NL80211_IFTYPE_ADHOC: + if (!sta) { + idx = key->keyidx; + break; + } + memcpy(gmac, sta->addr, ETH_ALEN); + gmac[0] |= 0x01; + mac = gmac; + idx = ath_reserve_key_cache_slot(common, key->cipher); + break; + default: + idx = key->keyidx; + break; + } + } else if (key->keyidx) { + if (WARN_ON(!sta)) + return -EOPNOTSUPP; + mac = sta->addr; + + if (vif->type != NL80211_IFTYPE_AP) { + /* Only keyidx 0 should be used with unicast key, but + * allow this for client mode for now. */ + idx = key->keyidx; + } else + return -EIO; + } else { + if (WARN_ON(!sta)) + return -EOPNOTSUPP; + mac = sta->addr; + + idx = ath_reserve_key_cache_slot(common, key->cipher); + } + + if (idx < 0) + return -ENOSPC; /* no free key cache entries */ + + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) + ret = ath_setkey_tkip(common, idx, key->key, &hk, mac, + vif->type == NL80211_IFTYPE_AP); + else + ret = ath_hw_set_keycache_entry(common, idx, &hk, mac); + + if (!ret) + return -EIO; + + set_bit(idx, common->keymap); + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) { + set_bit(idx + 64, common->keymap); + set_bit(idx, common->tkip_keymap); + set_bit(idx + 64, common->tkip_keymap); + if (common->splitmic) { + set_bit(idx + 32, common->keymap); + set_bit(idx + 64 + 32, common->keymap); + set_bit(idx + 32, common->tkip_keymap); + set_bit(idx + 64 + 32, common->tkip_keymap); + } + } + + return idx; +} +EXPORT_SYMBOL(ath_key_config); + +/* + * Delete Key. + */ +void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key) +{ + ath_hw_keyreset(common, key->hw_key_idx); + if (key->hw_key_idx < IEEE80211_WEP_NKID) + return; + + clear_bit(key->hw_key_idx, common->keymap); + if (key->cipher != WLAN_CIPHER_SUITE_TKIP) + return; + + clear_bit(key->hw_key_idx + 64, common->keymap); + + clear_bit(key->hw_key_idx, common->tkip_keymap); + clear_bit(key->hw_key_idx + 64, common->tkip_keymap); + + if (common->splitmic) { + ath_hw_keyreset(common, key->hw_key_idx + 32); + clear_bit(key->hw_key_idx + 32, common->keymap); + clear_bit(key->hw_key_idx + 64 + 32, common->keymap); + + clear_bit(key->hw_key_idx + 32, common->tkip_keymap); + clear_bit(key->hw_key_idx + 64 + 32, common->tkip_keymap); + } +} +EXPORT_SYMBOL(ath_key_delete); diff --git a/drivers/net/wireless/ath/reg.h b/drivers/net/wireless/ath/reg.h index dfe1fbec24f5..e798ef476581 100644 --- a/drivers/net/wireless/ath/reg.h +++ b/drivers/net/wireless/ath/reg.h @@ -24,4 +24,27 @@ #define AR_BSSMSKL 0x80e0 #define AR_BSSMSKU 0x80e4 +#define AR_KEYTABLE_0 0x8800 +#define AR_KEYTABLE(_n) (AR_KEYTABLE_0 + ((_n)*32)) +#define AR_KEY_CACHE_SIZE 128 +#define AR_RSVD_KEYTABLE_ENTRIES 4 +#define AR_KEY_TYPE 0x00000007 +#define AR_KEYTABLE_TYPE_40 0x00000000 +#define AR_KEYTABLE_TYPE_104 0x00000001 +#define AR_KEYTABLE_TYPE_128 0x00000003 +#define AR_KEYTABLE_TYPE_TKIP 0x00000004 +#define AR_KEYTABLE_TYPE_AES 0x00000005 +#define AR_KEYTABLE_TYPE_CCM 0x00000006 +#define AR_KEYTABLE_TYPE_CLR 0x00000007 +#define AR_KEYTABLE_ANT 0x00000008 +#define AR_KEYTABLE_VALID 0x00008000 +#define AR_KEYTABLE_KEY0(_n) (AR_KEYTABLE(_n) + 0) +#define AR_KEYTABLE_KEY1(_n) (AR_KEYTABLE(_n) + 4) +#define AR_KEYTABLE_KEY2(_n) (AR_KEYTABLE(_n) + 8) +#define AR_KEYTABLE_KEY3(_n) (AR_KEYTABLE(_n) + 12) +#define AR_KEYTABLE_KEY4(_n) (AR_KEYTABLE(_n) + 16) +#define AR_KEYTABLE_TYPE(_n) (AR_KEYTABLE(_n) + 20) +#define AR_KEYTABLE_MAC0(_n) (AR_KEYTABLE(_n) + 24) +#define AR_KEYTABLE_MAC1(_n) (AR_KEYTABLE(_n) + 28) + #endif /* ATH_REGISTERS_H */ -- cgit v1.2.3 From e0f8c2a9b879e1e65d588a40a3c88db69a7d6956 Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Wed, 8 Sep 2010 16:04:43 +0900 Subject: ath5k: Use common ath key management functions Use common ath key management functions in ath5k. This fixes problems with HW encryption in AP mode, which was broken in the ath5k implementation. Before (with the ath5k implementation) only one client could connect to the AP using HW encryption and WPA. When a second client connected, the first client was not able to send/receive any more packets. Because of the problems with HW encryption, software encryption was always used in AP mode, which resulted in a high CPU load (and/or low thruput) on embedded devices. Instead of trying to fix the implementation in ath5k it makes more sense to share the code with ath9k. This also enables HW encryption for AP mode again. Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/attach.c | 3 +++ drivers/net/wireless/ath/ath5k/base.c | 33 ++++++++++++++------------------- 2 files changed, 17 insertions(+), 19 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/attach.c b/drivers/net/wireless/ath/ath5k/attach.c index aabad4f13e2a..eb125d69fb78 100644 --- a/drivers/net/wireless/ath/ath5k/attach.c +++ b/drivers/net/wireless/ath/ath5k/attach.c @@ -314,6 +314,9 @@ int ath5k_hw_attach(struct ath5k_softc *sc) } /* Crypto settings */ + common->keymax = (sc->ah->ah_version == AR5K_AR5210 ? + AR5K_KEYTABLE_SIZE_5210 : AR5K_KEYTABLE_SIZE_5211); + ah->ah_aes_support = srev >= AR5K_SREV_AR5212_V4 && (ee->ee_version >= AR5K_EEPROM_VERSION_5_0 && !AR5K_EEPROM_AES_DIS(ee->ee_misc5)); diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 45789c8990d3..7933646583a9 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -2570,6 +2570,7 @@ static int ath5k_init(struct ath5k_softc *sc) { struct ath5k_hw *ah = sc->ah; + struct ath_common *common = ath5k_hw_common(ah); int ret, i; mutex_lock(&sc->lock); @@ -2605,8 +2606,8 @@ ath5k_init(struct ath5k_softc *sc) * Reset the key cache since some parts do not reset the * contents on initial power up or resume from suspend. */ - for (i = 0; i < AR5K_KEYTABLE_SIZE; i++) - ath5k_hw_reset_key(ah, i); + for (i = 0; i < common->keymax; i++) + ath_hw_keyreset(common, (u16)i); ath5k_hw_set_ack_bitrate_high(ah, true); ret = 0; @@ -3287,9 +3288,6 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (modparam_nohwcrypt) return -EOPNOTSUPP; - if (sc->opmode == NL80211_IFTYPE_AP) - return -EOPNOTSUPP; - switch (key->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: @@ -3298,7 +3296,6 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, case WLAN_CIPHER_SUITE_CCMP: if (sc->ah->ah_aes_support) break; - return -EOPNOTSUPP; default: WARN_ON(1); @@ -3309,27 +3306,25 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, switch (cmd) { case SET_KEY: - ret = ath5k_hw_set_key(sc->ah, key->keyidx, key, - sta ? sta->addr : NULL); - if (ret) { - ATH5K_ERR(sc, "can't set the key\n"); - goto unlock; + ret = ath_key_config(common, vif, sta, key); + if (ret >= 0) { + key->hw_key_idx = ret; + /* push IV and Michael MIC generation to stack */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; + if (key->cipher == WLAN_CIPHER_SUITE_CCMP) + key->flags |= IEEE80211_KEY_FLAG_SW_MGMT; + ret = 0; } - __set_bit(key->keyidx, common->keymap); - key->hw_key_idx = key->keyidx; - key->flags |= (IEEE80211_KEY_FLAG_GENERATE_IV | - IEEE80211_KEY_FLAG_GENERATE_MMIC); break; case DISABLE_KEY: - ath5k_hw_reset_key(sc->ah, key->keyidx); - __clear_bit(key->keyidx, common->keymap); + ath_key_delete(common, key); break; default: ret = -EINVAL; - goto unlock; } -unlock: mmiowb(); mutex_unlock(&sc->lock); return ret; -- cgit v1.2.3 From d8878f83cf1cc0061e8b3bcf7dbd589410a51039 Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Wed, 8 Sep 2010 16:04:49 +0900 Subject: ath5k: Remove old ath5k key handling functions Remove the old ath5k key handling functions, since we now use the key management in ath common. Signed-off-by: Bruno Randolf Acked-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/ath5k.h | 5 - drivers/net/wireless/ath/ath5k/pcu.c | 191 --------------------------------- drivers/net/wireless/ath/ath5k/reg.h | 42 -------- 3 files changed, 238 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index f399c4dd8e69..932ed5613355 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -1207,11 +1207,6 @@ void ath5k_hw_set_ack_bitrate_high(struct ath5k_hw *ah, bool high); unsigned int ath5k_hw_htoclock(struct ath5k_hw *ah, unsigned int usec); unsigned int ath5k_hw_clocktoh(struct ath5k_hw *ah, unsigned int clock); unsigned int ath5k_hw_get_clockrate(struct ath5k_hw *ah); -/* Key table (WEP) functions */ -int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry); -int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry, - const struct ieee80211_key_conf *key, const u8 *mac); -int ath5k_hw_set_key_lladdr(struct ath5k_hw *ah, u16 entry, const u8 *mac); /* Queue Control Unit, DFS Control Unit Functions */ int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue, diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c index bb2e21553d1b..6a891c4484a0 100644 --- a/drivers/net/wireless/ath/ath5k/pcu.c +++ b/drivers/net/wireless/ath/ath5k/pcu.c @@ -640,197 +640,6 @@ void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval) } - -/*********************\ -* Key table functions * -\*********************/ - -/* - * Reset a key entry on the table - */ -int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry) -{ - unsigned int i, type; - u16 micentry = entry + AR5K_KEYTABLE_MIC_OFFSET; - - AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); - - type = ath5k_hw_reg_read(ah, AR5K_KEYTABLE_TYPE(entry)); - - for (i = 0; i < AR5K_KEYCACHE_SIZE; i++) - ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_OFF(entry, i)); - - /* Reset associated MIC entry if TKIP - * is enabled located at offset (entry + 64) */ - if (type == AR5K_KEYTABLE_TYPE_TKIP) { - AR5K_ASSERT_ENTRY(micentry, AR5K_KEYTABLE_SIZE); - for (i = 0; i < AR5K_KEYCACHE_SIZE / 2 ; i++) - ath5k_hw_reg_write(ah, 0, - AR5K_KEYTABLE_OFF(micentry, i)); - } - - /* - * Set NULL encryption on AR5212+ - * - * Note: AR5K_KEYTABLE_TYPE -> AR5K_KEYTABLE_OFF(entry, 5) - * AR5K_KEYTABLE_TYPE_NULL -> 0x00000007 - * - * Note2: Windows driver (ndiswrapper) sets this to - * 0x00000714 instead of 0x00000007 - */ - if (ah->ah_version >= AR5K_AR5211) { - ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL, - AR5K_KEYTABLE_TYPE(entry)); - - if (type == AR5K_KEYTABLE_TYPE_TKIP) { - ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL, - AR5K_KEYTABLE_TYPE(micentry)); - } - } - - return 0; -} - -static -int ath5k_keycache_type(const struct ieee80211_key_conf *key) -{ - switch (key->cipher) { - case WLAN_CIPHER_SUITE_TKIP: - return AR5K_KEYTABLE_TYPE_TKIP; - case WLAN_CIPHER_SUITE_CCMP: - return AR5K_KEYTABLE_TYPE_CCM; - case WLAN_CIPHER_SUITE_WEP40: - return AR5K_KEYTABLE_TYPE_40; - case WLAN_CIPHER_SUITE_WEP104: - return AR5K_KEYTABLE_TYPE_104; - default: - return -EINVAL; - } -} - -/* - * Set a key entry on the table - */ -int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry, - const struct ieee80211_key_conf *key, const u8 *mac) -{ - unsigned int i; - int keylen; - __le32 key_v[5] = {}; - __le32 key0 = 0, key1 = 0; - __le32 *rxmic, *txmic; - int keytype; - u16 micentry = entry + AR5K_KEYTABLE_MIC_OFFSET; - bool is_tkip; - const u8 *key_ptr; - - is_tkip = (key->cipher == WLAN_CIPHER_SUITE_TKIP); - - /* - * key->keylen comes in from mac80211 in bytes. - * TKIP is 128 bit + 128 bit mic - */ - keylen = (is_tkip) ? (128 / 8) : key->keylen; - - if (entry > AR5K_KEYTABLE_SIZE || - (is_tkip && micentry > AR5K_KEYTABLE_SIZE)) - return -EOPNOTSUPP; - - if (unlikely(keylen > 16)) - return -EOPNOTSUPP; - - keytype = ath5k_keycache_type(key); - if (keytype < 0) - return keytype; - - /* - * each key block is 6 bytes wide, written as pairs of - * alternating 32 and 16 bit le values. - */ - key_ptr = key->key; - for (i = 0; keylen >= 6; keylen -= 6) { - memcpy(&key_v[i], key_ptr, 6); - i += 2; - key_ptr += 6; - } - if (keylen) - memcpy(&key_v[i], key_ptr, keylen); - - /* intentionally corrupt key until mic is installed */ - if (is_tkip) { - key0 = key_v[0] = ~key_v[0]; - key1 = key_v[1] = ~key_v[1]; - } - - for (i = 0; i < ARRAY_SIZE(key_v); i++) - ath5k_hw_reg_write(ah, le32_to_cpu(key_v[i]), - AR5K_KEYTABLE_OFF(entry, i)); - - ath5k_hw_reg_write(ah, keytype, AR5K_KEYTABLE_TYPE(entry)); - - if (is_tkip) { - /* Install rx/tx MIC */ - rxmic = (__le32 *) &key->key[16]; - txmic = (__le32 *) &key->key[24]; - - if (ah->ah_combined_mic) { - key_v[0] = rxmic[0]; - key_v[1] = cpu_to_le32(le32_to_cpu(txmic[0]) >> 16); - key_v[2] = rxmic[1]; - key_v[3] = cpu_to_le32(le32_to_cpu(txmic[0]) & 0xffff); - key_v[4] = txmic[1]; - } else { - key_v[0] = rxmic[0]; - key_v[1] = 0; - key_v[2] = rxmic[1]; - key_v[3] = 0; - key_v[4] = 0; - } - for (i = 0; i < ARRAY_SIZE(key_v); i++) - ath5k_hw_reg_write(ah, le32_to_cpu(key_v[i]), - AR5K_KEYTABLE_OFF(micentry, i)); - - ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL, - AR5K_KEYTABLE_TYPE(micentry)); - ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_MAC0(micentry)); - ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_MAC1(micentry)); - - /* restore first 2 words of key */ - ath5k_hw_reg_write(ah, le32_to_cpu(~key0), - AR5K_KEYTABLE_OFF(entry, 0)); - ath5k_hw_reg_write(ah, le32_to_cpu(~key1), - AR5K_KEYTABLE_OFF(entry, 1)); - } - - return ath5k_hw_set_key_lladdr(ah, entry, mac); -} - -int ath5k_hw_set_key_lladdr(struct ath5k_hw *ah, u16 entry, const u8 *mac) -{ - u32 low_id, high_id; - - /* Invalid entry (key table overflow) */ - AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); - - /* - * MAC may be NULL if it's a broadcast key. In this case no need to - * to compute get_unaligned_le32 and get_unaligned_le16 as we - * already know it. - */ - if (!mac) { - low_id = 0xffffffff; - high_id = 0xffff | AR5K_KEYTABLE_VALID; - } else { - low_id = get_unaligned_le32(mac); - high_id = get_unaligned_le16(mac + 4) | AR5K_KEYTABLE_VALID; - } - - ath5k_hw_reg_write(ah, low_id, AR5K_KEYTABLE_MAC0(entry)); - ath5k_hw_reg_write(ah, high_id, AR5K_KEYTABLE_MAC1(entry)); - - return 0; -} - /** * ath5k_hw_set_coverage_class - Set IEEE 802.11 coverage class * diff --git a/drivers/net/wireless/ath/ath5k/reg.h b/drivers/net/wireless/ath/ath5k/reg.h index 05ef587ad2b4..67d63081705a 100644 --- a/drivers/net/wireless/ath/ath5k/reg.h +++ b/drivers/net/wireless/ath/ath5k/reg.h @@ -1822,50 +1822,8 @@ /*===5212 end===*/ -/* - * Key table (WEP) register - */ -#define AR5K_KEYTABLE_0_5210 0x9000 -#define AR5K_KEYTABLE_0_5211 0x8800 -#define AR5K_KEYTABLE_5210(_n) (AR5K_KEYTABLE_0_5210 + ((_n) << 5)) -#define AR5K_KEYTABLE_5211(_n) (AR5K_KEYTABLE_0_5211 + ((_n) << 5)) -#define AR5K_KEYTABLE(_n) (ah->ah_version == AR5K_AR5210 ? \ - AR5K_KEYTABLE_5210(_n) : AR5K_KEYTABLE_5211(_n)) -#define AR5K_KEYTABLE_OFF(_n, x) (AR5K_KEYTABLE(_n) + (x << 2)) -#define AR5K_KEYTABLE_TYPE(_n) AR5K_KEYTABLE_OFF(_n, 5) -#define AR5K_KEYTABLE_TYPE_40 0x00000000 -#define AR5K_KEYTABLE_TYPE_104 0x00000001 -#define AR5K_KEYTABLE_TYPE_128 0x00000003 -#define AR5K_KEYTABLE_TYPE_TKIP 0x00000004 /* [5212+] */ -#define AR5K_KEYTABLE_TYPE_AES 0x00000005 /* [5211+] */ -#define AR5K_KEYTABLE_TYPE_CCM 0x00000006 /* [5212+] */ -#define AR5K_KEYTABLE_TYPE_NULL 0x00000007 /* [5211+] */ -#define AR5K_KEYTABLE_ANTENNA 0x00000008 /* [5212+] */ -#define AR5K_KEYTABLE_MAC0(_n) AR5K_KEYTABLE_OFF(_n, 6) -#define AR5K_KEYTABLE_MAC1(_n) AR5K_KEYTABLE_OFF(_n, 7) -#define AR5K_KEYTABLE_VALID 0x00008000 - -/* If key type is TKIP and MIC is enabled - * MIC key goes in offset entry + 64 */ -#define AR5K_KEYTABLE_MIC_OFFSET 64 - -/* WEP 40-bit = 40-bit entered key + 24 bit IV = 64-bit - * WEP 104-bit = 104-bit entered key + 24-bit IV = 128-bit - * WEP 128-bit = 128-bit entered key + 24 bit IV = 152-bit - * - * Some vendors have introduced bigger WEP keys to address - * security vulnerabilities in WEP. This includes: - * - * WEP 232-bit = 232-bit entered key + 24 bit IV = 256-bit - * - * We can expand this if we find ar5k Atheros cards with a larger - * key table size. - */ #define AR5K_KEYTABLE_SIZE_5210 64 #define AR5K_KEYTABLE_SIZE_5211 128 -#define AR5K_KEYTABLE_SIZE (ah->ah_version == AR5K_AR5210 ? \ - AR5K_KEYTABLE_SIZE_5210 : AR5K_KEYTABLE_SIZE_5211) - /*===PHY REGISTERS===*/ -- cgit v1.2.3 From 117675d06ad2dd16fcf466669ba9700a8d589ea3 Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Wed, 8 Sep 2010 16:04:54 +0900 Subject: ath/ath9k: Replace common->splitmic with a flag Replace common->splitmic with ATH_CRYPT_CAP_MIC_COMBINED flag. splitmic has to be used when the ATH_CRYPT_CAP_MIC_COMBINED capability flag is not set. Signed-off-by: Bruno Randolf Acked-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath.h | 2 +- drivers/net/wireless/ath/ath9k/common.c | 12 ++++++------ drivers/net/wireless/ath/ath9k/init.c | 4 ++-- drivers/net/wireless/ath/key.c | 14 +++++++------- 4 files changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index fb24f66373fd..c5584077d1de 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -78,6 +78,7 @@ enum ath_crypt_caps { ATH_CRYPT_CAP_CIPHER_AESCCM = BIT(3), ATH_CRYPT_CAP_CIPHER_CKIP = BIT(4), ATH_CRYPT_CAP_CIPHER_TKIP = BIT(5), + ATH_CRYPT_CAP_MIC_COMBINED = BIT(6), }; struct ath_keyval { @@ -150,7 +151,6 @@ struct ath_common { u32 keymax; DECLARE_BITMAP(keymap, ATH_KEYMAX); DECLARE_BITMAP(tkip_keymap, ATH_KEYMAX); - u8 splitmic; enum ath_crypt_caps crypt_caps; struct ath_regulatory regulatory; diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c index 2dab64bb23a8..2db24eb5f0ee 100644 --- a/drivers/net/wireless/ath/ath9k/common.c +++ b/drivers/net/wireless/ath/ath9k/common.c @@ -174,7 +174,7 @@ static int ath_setkey_tkip(struct ath_common *common, u16 keyix, const u8 *key, } return ath9k_hw_set_keycache_entry(ah, keyix, hk, addr); } - if (!common->splitmic) { + if (common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED) { /* TX and RX keys share the same key cache entry. */ memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic)); @@ -205,7 +205,7 @@ static int ath_reserve_key_cache_slot_tkip(struct ath_common *common) if (test_bit(i, common->keymap) || test_bit(i + 64, common->keymap)) continue; /* At least one part of TKIP key allocated */ - if (common->splitmic && + if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED) && (test_bit(i + 32, common->keymap) || test_bit(i + 64 + 32, common->keymap))) continue; /* At least one part of TKIP key allocated */ @@ -225,7 +225,7 @@ static int ath_reserve_key_cache_slot(struct ath_common *common, return ath_reserve_key_cache_slot_tkip(common); /* First, try to find slots that would not be available for TKIP. */ - if (common->splitmic) { + if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) { for (i = IEEE80211_WEP_NKID; i < common->keymax / 4; i++) { if (!test_bit(i, common->keymap) && (test_bit(i + 32, common->keymap) || @@ -266,7 +266,7 @@ static int ath_reserve_key_cache_slot(struct ath_common *common, * TKIP will not be used. */ if (i >= 64 && i < 64 + IEEE80211_WEP_NKID) continue; - if (common->splitmic) { + if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) { if (i >= 32 && i < 32 + IEEE80211_WEP_NKID) continue; if (i >= 64 + 32 && i < 64 + 32 + IEEE80211_WEP_NKID) @@ -374,7 +374,7 @@ int ath9k_cmn_key_config(struct ath_common *common, set_bit(idx + 64, common->keymap); set_bit(idx, common->tkip_keymap); set_bit(idx + 64, common->tkip_keymap); - if (common->splitmic) { + if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) { set_bit(idx + 32, common->keymap); set_bit(idx + 64 + 32, common->keymap); set_bit(idx + 32, common->tkip_keymap); @@ -407,7 +407,7 @@ void ath9k_cmn_key_delete(struct ath_common *common, clear_bit(key->hw_key_idx, common->tkip_keymap); clear_bit(key->hw_key_idx + 64, common->tkip_keymap); - if (common->splitmic) { + if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) { ath9k_hw_keyreset(ah, key->hw_key_idx + 32); clear_bit(key->hw_key_idx + 32, common->keymap); clear_bit(key->hw_key_idx + 64 + 32, common->keymap); diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 5c77a9913e03..fd651d8ef816 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -389,8 +389,8 @@ static void ath9k_init_crypto(struct ath_softc *sc) * With split mic keys the number of stations is limited * to 27 otherwise 59. */ - if (!(sc->sc_ah->misc_mode & AR_PCU_MIC_NEW_LOC_ENA)) - common->splitmic = 1; + if (sc->sc_ah->misc_mode & AR_PCU_MIC_NEW_LOC_ENA) + common->crypt_caps |= ATH_CRYPT_CAP_MIC_COMBINED; } static int ath9k_init_btcoex(struct ath_softc *sc) diff --git a/drivers/net/wireless/ath/key.c b/drivers/net/wireless/ath/key.c index e45b8546810a..bd21a4d82085 100644 --- a/drivers/net/wireless/ath/key.c +++ b/drivers/net/wireless/ath/key.c @@ -201,7 +201,7 @@ bool ath_hw_set_keycache_entry(struct ath_common *common, u16 entry, /* Write MAC address for the entry */ (void) ath_hw_keysetmac(common, entry, mac); - if (common->splitmic == 0) { + if (common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED) { /* * TKIP uses two key cache entries: * Michael MIC TX/RX keys in the same key cache entry @@ -327,7 +327,7 @@ static int ath_setkey_tkip(struct ath_common *common, u16 keyix, const u8 *key, } return ath_hw_set_keycache_entry(common, keyix, hk, addr); } - if (!common->splitmic) { + if (common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED) { /* TX and RX keys share the same key cache entry. */ memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic)); @@ -358,7 +358,7 @@ static int ath_reserve_key_cache_slot_tkip(struct ath_common *common) if (test_bit(i, common->keymap) || test_bit(i + 64, common->keymap)) continue; /* At least one part of TKIP key allocated */ - if (common->splitmic && + if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED) && (test_bit(i + 32, common->keymap) || test_bit(i + 64 + 32, common->keymap))) continue; /* At least one part of TKIP key allocated */ @@ -378,7 +378,7 @@ static int ath_reserve_key_cache_slot(struct ath_common *common, return ath_reserve_key_cache_slot_tkip(common); /* First, try to find slots that would not be available for TKIP. */ - if (common->splitmic) { + if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) { for (i = IEEE80211_WEP_NKID; i < common->keymax / 4; i++) { if (!test_bit(i, common->keymap) && (test_bit(i + 32, common->keymap) || @@ -419,7 +419,7 @@ static int ath_reserve_key_cache_slot(struct ath_common *common, * TKIP will not be used. */ if (i >= 64 && i < 64 + IEEE80211_WEP_NKID) continue; - if (common->splitmic) { + if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) { if (i >= 32 && i < 32 + IEEE80211_WEP_NKID) continue; if (i >= 64 + 32 && i < 64 + 32 + IEEE80211_WEP_NKID) @@ -526,7 +526,7 @@ int ath_key_config(struct ath_common *common, set_bit(idx + 64, common->keymap); set_bit(idx, common->tkip_keymap); set_bit(idx + 64, common->tkip_keymap); - if (common->splitmic) { + if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) { set_bit(idx + 32, common->keymap); set_bit(idx + 64 + 32, common->keymap); set_bit(idx + 32, common->tkip_keymap); @@ -556,7 +556,7 @@ void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key) clear_bit(key->hw_key_idx, common->tkip_keymap); clear_bit(key->hw_key_idx + 64, common->tkip_keymap); - if (common->splitmic) { + if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) { ath_hw_keyreset(common, key->hw_key_idx + 32); clear_bit(key->hw_key_idx + 32, common->keymap); clear_bit(key->hw_key_idx + 64 + 32, common->keymap); -- cgit v1.2.3 From 781f3136ff4cdd2b33149f2295fefa21f77b1c56 Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Wed, 8 Sep 2010 16:04:59 +0900 Subject: ath5k: Use common crypt capabilities flags Replace ah_aes_support and ah_combined_mic with common ath_crypt_caps ATH_CRYPT_CAP_CIPHER_AESCCM and ATH_CRYPT_CAP_MIC_COMBINED. Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/ath5k.h | 2 -- drivers/net/wireless/ath/ath5k/attach.c | 10 ++++++---- drivers/net/wireless/ath/ath5k/base.c | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index 932ed5613355..50209aed0ed2 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -1028,8 +1028,6 @@ struct ath5k_hw { bool ah_turbo; bool ah_calibration; bool ah_single_chip; - bool ah_aes_support; - bool ah_combined_mic; enum ath5k_version ah_version; enum ath5k_radio ah_radio; diff --git a/drivers/net/wireless/ath/ath5k/attach.c b/drivers/net/wireless/ath/ath5k/attach.c index eb125d69fb78..a0e08201c939 100644 --- a/drivers/net/wireless/ath/ath5k/attach.c +++ b/drivers/net/wireless/ath/ath5k/attach.c @@ -317,12 +317,14 @@ int ath5k_hw_attach(struct ath5k_softc *sc) common->keymax = (sc->ah->ah_version == AR5K_AR5210 ? AR5K_KEYTABLE_SIZE_5210 : AR5K_KEYTABLE_SIZE_5211); - ah->ah_aes_support = srev >= AR5K_SREV_AR5212_V4 && - (ee->ee_version >= AR5K_EEPROM_VERSION_5_0 && - !AR5K_EEPROM_AES_DIS(ee->ee_misc5)); + if (srev >= AR5K_SREV_AR5212_V4 && + (ee->ee_version >= AR5K_EEPROM_VERSION_5_0 && + !AR5K_EEPROM_AES_DIS(ee->ee_misc5))) + common->crypt_caps |= ATH_CRYPT_CAP_CIPHER_AESCCM | + ATH_CRYPT_CAP_MIC_AESCCM; if (srev >= AR5K_SREV_AR2414) { - ah->ah_combined_mic = true; + common->crypt_caps |= ATH_CRYPT_CAP_MIC_COMBINED; AR5K_REG_ENABLE_BITS(ah, AR5K_MISC_MODE, AR5K_MISC_MODE_COMBINED_MIC); } diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 7933646583a9..0709bb091b2f 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -3294,7 +3294,7 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, case WLAN_CIPHER_SUITE_TKIP: break; case WLAN_CIPHER_SUITE_CCMP: - if (sc->ah->ah_aes_support) + if (common->crypt_caps & ATH_CRYPT_CAP_CIPHER_AESCCM) break; return -EOPNOTSUPP; default: -- cgit v1.2.3 From 040e539e8e8d5585e1c3d7d15fa7215d3a691258 Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Wed, 8 Sep 2010 16:05:04 +0900 Subject: ath9k: Use common ath key management functions Use key management functions which have been moved to ath/key.c and remove ath9k copies of these functions and other now unused definitions. Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/common.c | 270 ------------------------- drivers/net/wireless/ath/ath9k/common.h | 6 - drivers/net/wireless/ath/ath9k/htc_drv_init.c | 2 +- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 4 +- drivers/net/wireless/ath/ath9k/hw.c | 271 -------------------------- drivers/net/wireless/ath/ath9k/hw.h | 6 - drivers/net/wireless/ath/ath9k/init.c | 2 +- drivers/net/wireless/ath/ath9k/mac.h | 21 -- drivers/net/wireless/ath/ath9k/main.c | 4 +- drivers/net/wireless/ath/ath9k/phy.h | 3 - 10 files changed, 6 insertions(+), 583 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c index 2db24eb5f0ee..f43a2d98421c 100644 --- a/drivers/net/wireless/ath/ath9k/common.c +++ b/drivers/net/wireless/ath/ath9k/common.c @@ -148,276 +148,6 @@ struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ath9k_cmn_get_curchannel); -static int ath_setkey_tkip(struct ath_common *common, u16 keyix, const u8 *key, - struct ath9k_keyval *hk, const u8 *addr, - bool authenticator) -{ - struct ath_hw *ah = common->ah; - const u8 *key_rxmic; - const u8 *key_txmic; - - key_txmic = key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY; - key_rxmic = key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY; - - if (addr == NULL) { - /* - * Group key installation - only two key cache entries are used - * regardless of splitmic capability since group key is only - * used either for TX or RX. - */ - if (authenticator) { - memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic)); - memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_mic)); - } else { - memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); - memcpy(hk->kv_txmic, key_rxmic, sizeof(hk->kv_mic)); - } - return ath9k_hw_set_keycache_entry(ah, keyix, hk, addr); - } - if (common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED) { - /* TX and RX keys share the same key cache entry. */ - memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); - memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic)); - return ath9k_hw_set_keycache_entry(ah, keyix, hk, addr); - } - - /* Separate key cache entries for TX and RX */ - - /* TX key goes at first index, RX key at +32. */ - memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic)); - if (!ath9k_hw_set_keycache_entry(ah, keyix, hk, NULL)) { - /* TX MIC entry failed. No need to proceed further */ - ath_print(common, ATH_DBG_FATAL, - "Setting TX MIC Key Failed\n"); - return 0; - } - - memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); - /* XXX delete tx key on failure? */ - return ath9k_hw_set_keycache_entry(ah, keyix + 32, hk, addr); -} - -static int ath_reserve_key_cache_slot_tkip(struct ath_common *common) -{ - int i; - - for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) { - if (test_bit(i, common->keymap) || - test_bit(i + 64, common->keymap)) - continue; /* At least one part of TKIP key allocated */ - if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED) && - (test_bit(i + 32, common->keymap) || - test_bit(i + 64 + 32, common->keymap))) - continue; /* At least one part of TKIP key allocated */ - - /* Found a free slot for a TKIP key */ - return i; - } - return -1; -} - -static int ath_reserve_key_cache_slot(struct ath_common *common, - u32 cipher) -{ - int i; - - if (cipher == WLAN_CIPHER_SUITE_TKIP) - return ath_reserve_key_cache_slot_tkip(common); - - /* First, try to find slots that would not be available for TKIP. */ - if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) { - for (i = IEEE80211_WEP_NKID; i < common->keymax / 4; i++) { - if (!test_bit(i, common->keymap) && - (test_bit(i + 32, common->keymap) || - test_bit(i + 64, common->keymap) || - test_bit(i + 64 + 32, common->keymap))) - return i; - if (!test_bit(i + 32, common->keymap) && - (test_bit(i, common->keymap) || - test_bit(i + 64, common->keymap) || - test_bit(i + 64 + 32, common->keymap))) - return i + 32; - if (!test_bit(i + 64, common->keymap) && - (test_bit(i , common->keymap) || - test_bit(i + 32, common->keymap) || - test_bit(i + 64 + 32, common->keymap))) - return i + 64; - if (!test_bit(i + 64 + 32, common->keymap) && - (test_bit(i, common->keymap) || - test_bit(i + 32, common->keymap) || - test_bit(i + 64, common->keymap))) - return i + 64 + 32; - } - } else { - for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) { - if (!test_bit(i, common->keymap) && - test_bit(i + 64, common->keymap)) - return i; - if (test_bit(i, common->keymap) && - !test_bit(i + 64, common->keymap)) - return i + 64; - } - } - - /* No partially used TKIP slots, pick any available slot */ - for (i = IEEE80211_WEP_NKID; i < common->keymax; i++) { - /* Do not allow slots that could be needed for TKIP group keys - * to be used. This limitation could be removed if we know that - * TKIP will not be used. */ - if (i >= 64 && i < 64 + IEEE80211_WEP_NKID) - continue; - if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) { - if (i >= 32 && i < 32 + IEEE80211_WEP_NKID) - continue; - if (i >= 64 + 32 && i < 64 + 32 + IEEE80211_WEP_NKID) - continue; - } - - if (!test_bit(i, common->keymap)) - return i; /* Found a free slot for a key */ - } - - /* No free slot found */ - return -1; -} - -/* - * Configure encryption in the HW. - */ -int ath9k_cmn_key_config(struct ath_common *common, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *key) -{ - struct ath_hw *ah = common->ah; - struct ath9k_keyval hk; - const u8 *mac = NULL; - u8 gmac[ETH_ALEN]; - int ret = 0; - int idx; - - memset(&hk, 0, sizeof(hk)); - - switch (key->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - hk.kv_type = ATH9K_CIPHER_WEP; - break; - case WLAN_CIPHER_SUITE_TKIP: - hk.kv_type = ATH9K_CIPHER_TKIP; - break; - case WLAN_CIPHER_SUITE_CCMP: - hk.kv_type = ATH9K_CIPHER_AES_CCM; - break; - default: - return -EOPNOTSUPP; - } - - hk.kv_len = key->keylen; - memcpy(hk.kv_val, key->key, key->keylen); - - if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { - switch (vif->type) { - case NL80211_IFTYPE_AP: - memcpy(gmac, vif->addr, ETH_ALEN); - gmac[0] |= 0x01; - mac = gmac; - idx = ath_reserve_key_cache_slot(common, key->cipher); - break; - case NL80211_IFTYPE_ADHOC: - if (!sta) { - idx = key->keyidx; - break; - } - memcpy(gmac, sta->addr, ETH_ALEN); - gmac[0] |= 0x01; - mac = gmac; - idx = ath_reserve_key_cache_slot(common, key->cipher); - break; - default: - idx = key->keyidx; - break; - } - } else if (key->keyidx) { - if (WARN_ON(!sta)) - return -EOPNOTSUPP; - mac = sta->addr; - - if (vif->type != NL80211_IFTYPE_AP) { - /* Only keyidx 0 should be used with unicast key, but - * allow this for client mode for now. */ - idx = key->keyidx; - } else - return -EIO; - } else { - if (WARN_ON(!sta)) - return -EOPNOTSUPP; - mac = sta->addr; - - idx = ath_reserve_key_cache_slot(common, key->cipher); - } - - if (idx < 0) - return -ENOSPC; /* no free key cache entries */ - - if (key->cipher == WLAN_CIPHER_SUITE_TKIP) - ret = ath_setkey_tkip(common, idx, key->key, &hk, mac, - vif->type == NL80211_IFTYPE_AP); - else - ret = ath9k_hw_set_keycache_entry(ah, idx, &hk, mac); - - if (!ret) - return -EIO; - - set_bit(idx, common->keymap); - if (key->cipher == WLAN_CIPHER_SUITE_TKIP) { - set_bit(idx + 64, common->keymap); - set_bit(idx, common->tkip_keymap); - set_bit(idx + 64, common->tkip_keymap); - if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) { - set_bit(idx + 32, common->keymap); - set_bit(idx + 64 + 32, common->keymap); - set_bit(idx + 32, common->tkip_keymap); - set_bit(idx + 64 + 32, common->tkip_keymap); - } - } - - return idx; -} -EXPORT_SYMBOL(ath9k_cmn_key_config); - -/* - * Delete Key. - */ -void ath9k_cmn_key_delete(struct ath_common *common, - struct ieee80211_key_conf *key) -{ - struct ath_hw *ah = common->ah; - - ath9k_hw_keyreset(ah, key->hw_key_idx); - if (key->hw_key_idx < IEEE80211_WEP_NKID) - return; - - clear_bit(key->hw_key_idx, common->keymap); - if (key->cipher != WLAN_CIPHER_SUITE_TKIP) - return; - - clear_bit(key->hw_key_idx + 64, common->keymap); - - clear_bit(key->hw_key_idx, common->tkip_keymap); - clear_bit(key->hw_key_idx + 64, common->tkip_keymap); - - if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) { - ath9k_hw_keyreset(ah, key->hw_key_idx + 32); - clear_bit(key->hw_key_idx + 32, common->keymap); - clear_bit(key->hw_key_idx + 64 + 32, common->keymap); - - clear_bit(key->hw_key_idx + 32, common->tkip_keymap); - clear_bit(key->hw_key_idx + 64 + 32, common->tkip_keymap); - } -} -EXPORT_SYMBOL(ath9k_cmn_key_delete); - int ath9k_cmn_count_streams(unsigned int chainmask, int max) { int streams = 0; diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h index 4aa4e7dbe4d2..fea3b3315391 100644 --- a/drivers/net/wireless/ath/ath9k/common.h +++ b/drivers/net/wireless/ath/ath9k/common.h @@ -66,12 +66,6 @@ void ath9k_cmn_update_ichannel(struct ieee80211_hw *hw, struct ath9k_channel *ichan); struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw, struct ath_hw *ah); -int ath9k_cmn_key_config(struct ath_common *common, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *key); -void ath9k_cmn_key_delete(struct ath_common *common, - struct ieee80211_key_conf *key); int ath9k_cmn_count_streams(unsigned int chainmask, int max); void ath9k_cmn_btcoex_bt_stomp(struct ath_common *common, enum ath_stomp_type stomp_type); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index 717f2b821f0d..33850c952314 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -566,7 +566,7 @@ static void ath9k_init_crypto(struct ath9k_htc_priv *priv) * reset the contents on initial power up. */ for (i = 0; i < common->keymax; i++) - ath9k_hw_keyreset(priv->ah, (u16) i); + ath_hw_keyreset(common, (u16) i); } static void ath9k_init_channels_rates(struct ath9k_htc_priv *priv) diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index bab894873bf5..5124d04b240b 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1590,7 +1590,7 @@ static int ath9k_htc_set_key(struct ieee80211_hw *hw, switch (cmd) { case SET_KEY: - ret = ath9k_cmn_key_config(common, vif, sta, key); + ret = ath_key_config(common, vif, sta, key); if (ret >= 0) { key->hw_key_idx = ret; /* push IV and Michael MIC generation to stack */ @@ -1604,7 +1604,7 @@ static int ath9k_htc_set_key(struct ieee80211_hw *hw, } break; case DISABLE_KEY: - ath9k_cmn_key_delete(common, key); + ath_key_delete(common, key); break; default: ret = -EINVAL; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 40c6451602d3..f3c9d7549571 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1476,277 +1476,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, } EXPORT_SYMBOL(ath9k_hw_reset); -/************************/ -/* Key Cache Management */ -/************************/ - -bool ath9k_hw_keyreset(struct ath_hw *ah, u16 entry) -{ - u32 keyType; - - if (entry >= ah->caps.keycache_size) { - ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL, - "keychache entry %u out of range\n", entry); - return false; - } - - keyType = REG_READ(ah, AR_KEYTABLE_TYPE(entry)); - - REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), 0); - REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), 0); - REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), 0); - REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), 0); - REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), 0); - REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), AR_KEYTABLE_TYPE_CLR); - REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), 0); - REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), 0); - - if (keyType == AR_KEYTABLE_TYPE_TKIP && ATH9K_IS_MIC_ENABLED(ah)) { - u16 micentry = entry + 64; - - REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), 0); - REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0); - REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), 0); - REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0); - - } - - return true; -} -EXPORT_SYMBOL(ath9k_hw_keyreset); - -static bool ath9k_hw_keysetmac(struct ath_hw *ah, u16 entry, const u8 *mac) -{ - u32 macHi, macLo; - u32 unicast_flag = AR_KEYTABLE_VALID; - - if (entry >= ah->caps.keycache_size) { - ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL, - "keychache entry %u out of range\n", entry); - return false; - } - - if (mac != NULL) { - /* - * AR_KEYTABLE_VALID indicates that the address is a unicast - * address, which must match the transmitter address for - * decrypting frames. - * Not setting this bit allows the hardware to use the key - * for multicast frame decryption. - */ - if (mac[0] & 0x01) - unicast_flag = 0; - - macHi = (mac[5] << 8) | mac[4]; - macLo = (mac[3] << 24) | - (mac[2] << 16) | - (mac[1] << 8) | - mac[0]; - macLo >>= 1; - macLo |= (macHi & 1) << 31; - macHi >>= 1; - } else { - macLo = macHi = 0; - } - REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), macLo); - REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), macHi | unicast_flag); - - return true; -} - -bool ath9k_hw_set_keycache_entry(struct ath_hw *ah, u16 entry, - const struct ath9k_keyval *k, - const u8 *mac) -{ - const struct ath9k_hw_capabilities *pCap = &ah->caps; - struct ath_common *common = ath9k_hw_common(ah); - u32 key0, key1, key2, key3, key4; - u32 keyType; - - if (entry >= pCap->keycache_size) { - ath_print(common, ATH_DBG_FATAL, - "keycache entry %u out of range\n", entry); - return false; - } - - switch (k->kv_type) { - case ATH9K_CIPHER_AES_OCB: - keyType = AR_KEYTABLE_TYPE_AES; - break; - case ATH9K_CIPHER_AES_CCM: - keyType = AR_KEYTABLE_TYPE_CCM; - break; - case ATH9K_CIPHER_TKIP: - keyType = AR_KEYTABLE_TYPE_TKIP; - if (ATH9K_IS_MIC_ENABLED(ah) - && entry + 64 >= pCap->keycache_size) { - ath_print(common, ATH_DBG_ANY, - "entry %u inappropriate for TKIP\n", entry); - return false; - } - break; - case ATH9K_CIPHER_WEP: - if (k->kv_len < WLAN_KEY_LEN_WEP40) { - ath_print(common, ATH_DBG_ANY, - "WEP key length %u too small\n", k->kv_len); - return false; - } - if (k->kv_len <= WLAN_KEY_LEN_WEP40) - keyType = AR_KEYTABLE_TYPE_40; - else if (k->kv_len <= WLAN_KEY_LEN_WEP104) - keyType = AR_KEYTABLE_TYPE_104; - else - keyType = AR_KEYTABLE_TYPE_128; - break; - case ATH9K_CIPHER_CLR: - keyType = AR_KEYTABLE_TYPE_CLR; - break; - default: - ath_print(common, ATH_DBG_FATAL, - "cipher %u not supported\n", k->kv_type); - return false; - } - - key0 = get_unaligned_le32(k->kv_val + 0); - key1 = get_unaligned_le16(k->kv_val + 4); - key2 = get_unaligned_le32(k->kv_val + 6); - key3 = get_unaligned_le16(k->kv_val + 10); - key4 = get_unaligned_le32(k->kv_val + 12); - if (k->kv_len <= WLAN_KEY_LEN_WEP104) - key4 &= 0xff; - - /* - * Note: Key cache registers access special memory area that requires - * two 32-bit writes to actually update the values in the internal - * memory. Consequently, the exact order and pairs used here must be - * maintained. - */ - - if (keyType == AR_KEYTABLE_TYPE_TKIP && ATH9K_IS_MIC_ENABLED(ah)) { - u16 micentry = entry + 64; - - /* - * Write inverted key[47:0] first to avoid Michael MIC errors - * on frames that could be sent or received at the same time. - * The correct key will be written in the end once everything - * else is ready. - */ - REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), ~key0); - REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), ~key1); - - /* Write key[95:48] */ - REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2); - REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3); - - /* Write key[127:96] and key type */ - REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4); - REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType); - - /* Write MAC address for the entry */ - (void) ath9k_hw_keysetmac(ah, entry, mac); - - if (ah->misc_mode & AR_PCU_MIC_NEW_LOC_ENA) { - /* - * TKIP uses two key cache entries: - * Michael MIC TX/RX keys in the same key cache entry - * (idx = main index + 64): - * key0 [31:0] = RX key [31:0] - * key1 [15:0] = TX key [31:16] - * key1 [31:16] = reserved - * key2 [31:0] = RX key [63:32] - * key3 [15:0] = TX key [15:0] - * key3 [31:16] = reserved - * key4 [31:0] = TX key [63:32] - */ - u32 mic0, mic1, mic2, mic3, mic4; - - mic0 = get_unaligned_le32(k->kv_mic + 0); - mic2 = get_unaligned_le32(k->kv_mic + 4); - mic1 = get_unaligned_le16(k->kv_txmic + 2) & 0xffff; - mic3 = get_unaligned_le16(k->kv_txmic + 0) & 0xffff; - mic4 = get_unaligned_le32(k->kv_txmic + 4); - - /* Write RX[31:0] and TX[31:16] */ - REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0); - REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), mic1); - - /* Write RX[63:32] and TX[15:0] */ - REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2); - REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), mic3); - - /* Write TX[63:32] and keyType(reserved) */ - REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), mic4); - REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry), - AR_KEYTABLE_TYPE_CLR); - - } else { - /* - * TKIP uses four key cache entries (two for group - * keys): - * Michael MIC TX/RX keys are in different key cache - * entries (idx = main index + 64 for TX and - * main index + 32 + 96 for RX): - * key0 [31:0] = TX/RX MIC key [31:0] - * key1 [31:0] = reserved - * key2 [31:0] = TX/RX MIC key [63:32] - * key3 [31:0] = reserved - * key4 [31:0] = reserved - * - * Upper layer code will call this function separately - * for TX and RX keys when these registers offsets are - * used. - */ - u32 mic0, mic2; - - mic0 = get_unaligned_le32(k->kv_mic + 0); - mic2 = get_unaligned_le32(k->kv_mic + 4); - - /* Write MIC key[31:0] */ - REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0); - REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0); - - /* Write MIC key[63:32] */ - REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2); - REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0); - - /* Write TX[63:32] and keyType(reserved) */ - REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), 0); - REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry), - AR_KEYTABLE_TYPE_CLR); - } - - /* MAC address registers are reserved for the MIC entry */ - REG_WRITE(ah, AR_KEYTABLE_MAC0(micentry), 0); - REG_WRITE(ah, AR_KEYTABLE_MAC1(micentry), 0); - - /* - * Write the correct (un-inverted) key[47:0] last to enable - * TKIP now that all other registers are set with correct - * values. - */ - REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0); - REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1); - } else { - /* Write key[47:0] */ - REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0); - REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1); - - /* Write key[95:48] */ - REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2); - REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3); - - /* Write key[127:96] and key type */ - REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4); - REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType); - - /* Write MAC address for the entry */ - (void) ath9k_hw_keysetmac(ah, entry, mac); - } - - return true; -} -EXPORT_SYMBOL(ath9k_hw_set_keycache_entry); - /******************************/ /* Power Management (Chipset) */ /******************************/ diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index a3c2ce235123..df47f792cf4e 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -870,12 +870,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, int ath9k_hw_fill_cap_info(struct ath_hw *ah); u32 ath9k_regd_get_ctl(struct ath_regulatory *reg, struct ath9k_channel *chan); -/* Key Cache Management */ -bool ath9k_hw_keyreset(struct ath_hw *ah, u16 entry); -bool ath9k_hw_set_keycache_entry(struct ath_hw *ah, u16 entry, - const struct ath9k_keyval *k, - const u8 *mac); - /* GPIO / RFKILL / Antennae */ void ath9k_hw_cfg_gpio_input(struct ath_hw *ah, u32 gpio); u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio); diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index fd651d8ef816..224df6d8fe2c 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -381,7 +381,7 @@ static void ath9k_init_crypto(struct ath_softc *sc) * reset the contents on initial power up. */ for (i = 0; i < common->keymax; i++) - ath9k_hw_keyreset(sc->sc_ah, (u16) i); + ath_hw_keyreset(common, (u16) i); /* * Check whether the separate key cache entries diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index 2633896d3998..7c1a34d64f6d 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -660,17 +660,6 @@ struct ath9k_11n_rate_series { u32 RateFlags; }; -struct ath9k_keyval { - u8 kv_type; - u8 kv_pad; - u16 kv_len; - u8 kv_val[16]; /* TK */ - u8 kv_mic[8]; /* Michael MIC key */ - u8 kv_txmic[8]; /* Michael MIC TX key (used only if the hardware - * supports both MIC keys in the same key cache entry; - * in that case, kv_mic is the RX key) */ -}; - enum ath9k_key_type { ATH9K_KEY_TYPE_CLEAR, ATH9K_KEY_TYPE_WEP, @@ -678,16 +667,6 @@ enum ath9k_key_type { ATH9K_KEY_TYPE_TKIP, }; -enum ath9k_cipher { - ATH9K_CIPHER_WEP = 0, - ATH9K_CIPHER_AES_OCB = 1, - ATH9K_CIPHER_AES_CCM = 2, - ATH9K_CIPHER_CKIP = 3, - ATH9K_CIPHER_TKIP = 4, - ATH9K_CIPHER_CLR = 5, - ATH9K_CIPHER_MIC = 127 -}; - struct ath_hw; struct ath9k_channel; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index a2f7eb2a552b..fa875d1c7e91 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1769,7 +1769,7 @@ static int ath9k_set_key(struct ieee80211_hw *hw, switch (cmd) { case SET_KEY: - ret = ath9k_cmn_key_config(common, vif, sta, key); + ret = ath_key_config(common, vif, sta, key); if (ret >= 0) { key->hw_key_idx = ret; /* push IV and Michael MIC generation to stack */ @@ -1783,7 +1783,7 @@ static int ath9k_set_key(struct ieee80211_hw *hw, } break; case DISABLE_KEY: - ath9k_cmn_key_delete(common, key); + ath_key_delete(common, key); break; default: ret = -EINVAL; diff --git a/drivers/net/wireless/ath/ath9k/phy.h b/drivers/net/wireless/ath/ath9k/phy.h index e724c2c1ae2a..17969af842f6 100644 --- a/drivers/net/wireless/ath/ath9k/phy.h +++ b/drivers/net/wireless/ath/ath9k/phy.h @@ -45,9 +45,6 @@ } \ } while (0) -#define ATH9K_IS_MIC_ENABLED(ah) \ - ((ah)->sta_id1_defaults & AR_STA_ID1_CRPT_MIC_ENABLE) - #define ANTSWAP_AB 0x0001 #define REDUCE_CHAIN_0 0x00000050 #define REDUCE_CHAIN_1 0x00000051 -- cgit v1.2.3 From cfda669519241fccd5aa66c5a12bdae3dd04d5cd Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Tue, 14 Sep 2010 12:00:22 -0700 Subject: ath9k: calcrxfilter should take multiple VIFs into account. When there is more than one VIF, listen for all beacons and ensure ATH9K_RX_FILTER_MCAST_BCAST_ALL is set. Signed-off-by: Ben Greear Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/recv.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 6fb3b4536045..9eceeae28e7e 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -448,6 +448,7 @@ u32 ath_calcrxfilter(struct ath_softc *sc) rfilt |= ATH9K_RX_FILTER_CONTROL; if ((sc->sc_ah->opmode == NL80211_IFTYPE_STATION) && + (sc->nvifs <= 1) && !(sc->rx.rxfilter & FIF_BCN_PRBRESP_PROMISC)) rfilt |= ATH9K_RX_FILTER_MYBEACON; else @@ -462,9 +463,8 @@ u32 ath_calcrxfilter(struct ath_softc *sc) if (conf_is_ht(&sc->hw->conf)) rfilt |= ATH9K_RX_FILTER_COMP_BAR; - if (sc->sec_wiphy || (sc->rx.rxfilter & FIF_OTHER_BSS)) { - /* TODO: only needed if more than one BSSID is in use in - * station/adhoc mode */ + if (sc->sec_wiphy || (sc->nvifs > 1) || + (sc->rx.rxfilter & FIF_OTHER_BSS)) { /* The following may also be needed for other older chips */ if (sc->sc_ah->hw_version.macVersion == AR_SREV_VERSION_9160) rfilt |= ATH9K_RX_FILTER_PROM; -- cgit v1.2.3 From 3905751243fd84340e027672d2f603e9009d490e Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Tue, 14 Sep 2010 12:46:04 -0700 Subject: ath9k: Print rxfilter in debugfs. Print raw and decoded rxfilter in debufs 'wiphy' file. Also, move variable-length printouts to bottom of file to make bounds checking easier. Signed-off-by: Ben Greear Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/debug.c | 53 +++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 10 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 54aae931424e..d65a896a421d 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -492,12 +492,55 @@ static ssize_t read_file_wiphy(struct file *file, char __user *user_buf, unsigned int len = 0; int i; u8 addr[ETH_ALEN]; + u32 tmp; len += snprintf(buf + len, sizeof(buf) - len, "primary: %s (%s chan=%d ht=%d)\n", wiphy_name(sc->pri_wiphy->hw->wiphy), ath_wiphy_state_str(sc->pri_wiphy->state), sc->pri_wiphy->chan_idx, sc->pri_wiphy->chan_is_ht); + + put_unaligned_le32(REG_READ_D(sc->sc_ah, AR_STA_ID0), addr); + put_unaligned_le16(REG_READ_D(sc->sc_ah, AR_STA_ID1) & 0xffff, addr + 4); + len += snprintf(buf + len, sizeof(buf) - len, + "addr: %pM\n", addr); + put_unaligned_le32(REG_READ_D(sc->sc_ah, AR_BSSMSKL), addr); + put_unaligned_le16(REG_READ_D(sc->sc_ah, AR_BSSMSKU) & 0xffff, addr + 4); + len += snprintf(buf + len, sizeof(buf) - len, + "addrmask: %pM\n", addr); + tmp = ath9k_hw_getrxfilter(sc->sc_ah); + len += snprintf(buf + len, sizeof(buf) - len, + "rfilt: 0x%x", tmp); + if (tmp & ATH9K_RX_FILTER_UCAST) + len += snprintf(buf + len, sizeof(buf) - len, " UCAST"); + if (tmp & ATH9K_RX_FILTER_MCAST) + len += snprintf(buf + len, sizeof(buf) - len, " MCAST"); + if (tmp & ATH9K_RX_FILTER_BCAST) + len += snprintf(buf + len, sizeof(buf) - len, " BCAST"); + if (tmp & ATH9K_RX_FILTER_CONTROL) + len += snprintf(buf + len, sizeof(buf) - len, " CONTROL"); + if (tmp & ATH9K_RX_FILTER_BEACON) + len += snprintf(buf + len, sizeof(buf) - len, " BEACON"); + if (tmp & ATH9K_RX_FILTER_PROM) + len += snprintf(buf + len, sizeof(buf) - len, " PROM"); + if (tmp & ATH9K_RX_FILTER_PROBEREQ) + len += snprintf(buf + len, sizeof(buf) - len, " PROBEREQ"); + if (tmp & ATH9K_RX_FILTER_PHYERR) + len += snprintf(buf + len, sizeof(buf) - len, " PHYERR"); + if (tmp & ATH9K_RX_FILTER_MYBEACON) + len += snprintf(buf + len, sizeof(buf) - len, " MYBEACON"); + if (tmp & ATH9K_RX_FILTER_COMP_BAR) + len += snprintf(buf + len, sizeof(buf) - len, " COMP_BAR"); + if (tmp & ATH9K_RX_FILTER_PSPOLL) + len += snprintf(buf + len, sizeof(buf) - len, " PSPOLL"); + if (tmp & ATH9K_RX_FILTER_PHYRADAR) + len += snprintf(buf + len, sizeof(buf) - len, " PHYRADAR"); + if (tmp & ATH9K_RX_FILTER_MCAST_BCAST_ALL) + len += snprintf(buf + len, sizeof(buf) - len, " MCAST_BCAST_ALL\n"); + else + len += snprintf(buf + len, sizeof(buf) - len, "\n"); + + /* Put variable-length stuff down here, and check for overflows. */ for (i = 0; i < sc->num_sec_wiphy; i++) { struct ath_wiphy *aphy = sc->sec_wiphy[i]; if (aphy == NULL) @@ -508,16 +551,6 @@ static ssize_t read_file_wiphy(struct file *file, char __user *user_buf, ath_wiphy_state_str(aphy->state), aphy->chan_idx, aphy->chan_is_ht); } - - put_unaligned_le32(REG_READ_D(sc->sc_ah, AR_STA_ID0), addr); - put_unaligned_le16(REG_READ_D(sc->sc_ah, AR_STA_ID1) & 0xffff, addr + 4); - len += snprintf(buf + len, sizeof(buf) - len, - "addr: %pM\n", addr); - put_unaligned_le32(REG_READ_D(sc->sc_ah, AR_BSSMSKL), addr); - put_unaligned_le16(REG_READ_D(sc->sc_ah, AR_BSSMSKU) & 0xffff, addr + 4); - len += snprintf(buf + len, sizeof(buf) - len, - "addrmask: %pM\n", addr); - if (len > sizeof(buf)) len = sizeof(buf); -- cgit v1.2.3 From 9bad82b8ae0e96483507e09463d8520bcfa78cc3 Mon Sep 17 00:00:00 2001 From: John W. Linville Date: Wed, 15 Sep 2010 15:26:13 -0400 Subject: ath9k: make ath_ant_div_conf_fast_divbias static CHECK drivers/net/wireless/ath/ath9k/recv.c drivers/net/wireless/ath/ath9k/recv.c:1341:6: warning: symbol 'ath_ant_div_conf_fast_divbias' was not declared. Should it be static? Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/recv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 9eceeae28e7e..f921aa20b301 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -1338,7 +1338,7 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb, } } -void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf) +static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf) { /* Adjust the fast_div_bias based on main and alt lna conf */ switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) { -- cgit v1.2.3 From f799a301abf77b87133d624164d28dc2b521e99d Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Thu, 16 Sep 2010 11:40:06 +0530 Subject: ath9k_hw: remove warning in ath9k_hw_def_get_num_ant_config This patch fixes following warning drivers/net/wireless/ath/ath9k/eeprom_def.c: In function 'ath9k_hw_def_get_num_ant_config' drivers/net/wireless/ath/ath9k/eeprom_def.c:1425:47: warning: comparison between 'enum ath9k_hal_freq_band' and 'enum ieee80211_band' Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_eeprom.c | 2 +- drivers/net/wireless/ath/ath9k/eeprom.h | 3 ++- drivers/net/wireless/ath/ath9k/eeprom_4k.c | 2 +- drivers/net/wireless/ath/ath9k/eeprom_9287.c | 2 +- drivers/net/wireless/ath/ath9k/eeprom_def.c | 4 ++-- 5 files changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index b883b174385b..e2ae98feb0be 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -968,7 +968,7 @@ static int ath9k_hw_ar9300_get_eeprom_rev(struct ath_hw *ah) } static u8 ath9k_hw_ar9300_get_num_ant_config(struct ath_hw *ah, - enum ieee80211_band freq_band) + enum ath9k_hal_freq_band freq_band) { return 1; } diff --git a/drivers/net/wireless/ath/ath9k/eeprom.h b/drivers/net/wireless/ath/ath9k/eeprom.h index 090778da5d67..e583421fcaa6 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.h +++ b/drivers/net/wireless/ath/ath9k/eeprom.h @@ -672,7 +672,8 @@ struct eeprom_ops { bool (*fill_eeprom)(struct ath_hw *hw); int (*get_eeprom_ver)(struct ath_hw *hw); int (*get_eeprom_rev)(struct ath_hw *hw); - u8 (*get_num_ant_config)(struct ath_hw *hw, enum ieee80211_band band); + u8 (*get_num_ant_config)(struct ath_hw *hw, + enum ath9k_hal_freq_band band); u32 (*get_eeprom_antenna_cfg)(struct ath_hw *hw, struct ath9k_channel *chan); void (*set_board_values)(struct ath_hw *hw, struct ath9k_channel *chan); diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c index 2e1397b68a87..ead8b0dd3b53 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c @@ -1161,7 +1161,7 @@ static u32 ath9k_hw_4k_get_eeprom_antenna_cfg(struct ath_hw *ah, } static u8 ath9k_hw_4k_get_num_ant_config(struct ath_hw *ah, - enum ieee80211_band freq_band) + enum ath9k_hal_freq_band freq_band) { return 1; } diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c index dff2da777312..e6186515d05b 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c @@ -1126,7 +1126,7 @@ static void ath9k_hw_ar9287_set_board_values(struct ath_hw *ah, } static u8 ath9k_hw_ar9287_get_num_ant_config(struct ath_hw *ah, - enum ieee80211_band freq_band) + enum ath9k_hal_freq_band freq_band) { return 1; } diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c index afa2b73ddbdd..23f480d4c770 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_def.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c @@ -1418,11 +1418,11 @@ static void ath9k_hw_def_set_txpower(struct ath_hw *ah, } static u8 ath9k_hw_def_get_num_ant_config(struct ath_hw *ah, - enum ieee80211_band freq_band) + enum ath9k_hal_freq_band freq_band) { struct ar5416_eeprom_def *eep = &ah->eeprom.def; struct modal_eep_header *pModal = - &(eep->modalHeader[ATH9K_HAL_FREQ_BAND_2GHZ == freq_band]); + &(eep->modalHeader[freq_band]); struct base_eep_header *pBase = &eep->baseEepHeader; u8 num_ant_config; -- cgit v1.2.3 From 0f529e98498ff1a8bb264958b5fe7c0aa308d5dc Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Thu, 16 Sep 2010 19:56:55 +0530 Subject: ath9k_htc: Fix register read through bulk pipe To optimize register read/write operations, the HTC firmwares were patched to change EP3 and EP4 pipe types from Interrupt to Bulk. So register writes are submitted as bulk urbs, but register reads are not. Also changing the register read endpoint pipe as bulk type when URBs are filled improves the register reads considerably which results in reduced scan time and CPU utilization. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hif_usb.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 27f88a0a155b..728d904c74d7 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -541,7 +541,8 @@ static void ath9k_hif_usb_reg_in_cb(struct urb *urb) } usb_fill_int_urb(urb, hif_dev->udev, - usb_rcvintpipe(hif_dev->udev, USB_REG_IN_PIPE), + usb_rcvbulkpipe(hif_dev->udev, + USB_REG_IN_PIPE), nskb->data, MAX_REG_IN_BUF_SIZE, ath9k_hif_usb_reg_in_cb, nskb, 1); @@ -720,7 +721,8 @@ static int ath9k_hif_usb_alloc_reg_in_urb(struct hif_device_usb *hif_dev) goto err; usb_fill_int_urb(hif_dev->reg_in_urb, hif_dev->udev, - usb_rcvintpipe(hif_dev->udev, USB_REG_IN_PIPE), + usb_rcvbulkpipe(hif_dev->udev, + USB_REG_IN_PIPE), skb->data, MAX_REG_IN_BUF_SIZE, ath9k_hif_usb_reg_in_cb, skb, 1); -- cgit v1.2.3 From 8ab2cd09fecc8819bbaee2d0fd8f3a092d866ce3 Mon Sep 17 00:00:00 2001 From: Luis R. Rodriguez Date: Thu, 16 Sep 2010 15:12:26 -0400 Subject: ath9k: fix power save race conditions ath9k has a race on putting the chip into network sleep and having registers read from hardware. The race occurs because although ath9k_ps_restore() locks its own callers it makes use of some variables which get altered in the driver at different code paths. The variables are the ps_enabled and ps_flags. This is easily reprodicible in large network environments when roaming with the wpa_supplicant simple bgscan. You'd get some 0xdeadbeef read out on certain registers such as: ath: timeout (100000 us) on reg 0x806c: 0xdeadbeef & 0x01f00000 != 0x00000000 ath: RX failed to go idle in 10 ms RXSM=0xdeadbeef ath: timeout (100000 us) on reg 0x7000: 0xdeadbeef & 0x00000003 != 0x00000000 ath: Chip reset failed The fix is to protect the ath9k_config(hw, IEEE80211_CONF_CHANGE_PS) calls with a spin_lock_irqsave() which will disable contendors for these variables from interrupt context, timers, re-entry from mac80211 on the same callback, and most importantly from ath9k_ps_restore() which is the only call which will put the device into network sleep. There are quite a few threads and bug reports on these a few of them are: https://bugs.launchpad.net/ubuntu/karmic/+source/linux/+bug/407040 http://code.google.com/p/chromium-os/issues/detail?id=5709 http://code.google.com/p/chromium-os/issues/detail?id=5943 Stable fixes apply to [2.6.32+] Cc: stable@kernel.org Cc: Paul Stewart Cc: Amod Bodas Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 5 ++++- drivers/net/wireless/ath/ath9k/recv.c | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index fa875d1c7e91..1dd8768c63dc 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1554,6 +1554,8 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) * IEEE80211_CONF_CHANGE_PS is only passed by mac80211 for STA mode. */ if (changed & IEEE80211_CONF_CHANGE_PS) { + unsigned long flags; + spin_lock_irqsave(&sc->sc_pm_lock, flags); if (conf->flags & IEEE80211_CONF_PS) { sc->ps_flags |= PS_ENABLED; /* @@ -1568,7 +1570,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) sc->ps_enabled = false; sc->ps_flags &= ~(PS_ENABLED | PS_NULLFUNC_COMPLETED); - ath9k_setpower(sc, ATH9K_PM_AWAKE); + ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { ath9k_hw_setrxabort(sc->sc_ah, 0); @@ -1583,6 +1585,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) } } } + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); } if (changed & IEEE80211_CONF_CHANGE_MONITOR) { diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index f921aa20b301..3e7b8fe36044 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -1640,6 +1640,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) u8 rx_status_len = ah->caps.rx_status_len; u64 tsf = 0; u32 tsf_lower = 0; + unsigned long flags; if (edma) dma_type = DMA_BIDIRECTIONAL; @@ -1748,11 +1749,13 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) sc->rx.rxotherant = 0; } + spin_lock_irqsave(&sc->sc_pm_lock, flags); if (unlikely(ath9k_check_auto_sleep(sc) || (sc->ps_flags & (PS_WAIT_FOR_BEACON | PS_WAIT_FOR_CAB | PS_WAIT_FOR_PSPOLL_DATA)))) ath_rx_ps(sc, skb); + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); if (ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) ath_ant_comb_scan(sc, &rs); -- cgit v1.2.3 From 52b8ac92496e03d6b5619204d7f3bae6ce6eae45 Mon Sep 17 00:00:00 2001 From: Luis R. Rodriguez Date: Thu, 16 Sep 2010 15:12:27 -0400 Subject: ath9k: fix regression on beacon loss after bgscan When we return to the home channel we were never reseting our beacon timers, this was casued by the fact that the scanning flag was still on even after we returned to our home channel. There are also other reasons why we would get a reset and if we are not off channel we always need to resynch our beacon timers, because a reset will clear them. This bug is a regression introduced on 2.6.36. The order of the changes are as follows: 5ee08656 - Sat Jul 31 - ath9k: prevent calibration during off-channel activity a0daa0e7 - Tue Jul 27 - Revert "mac80211: fix sw scan bracketing" 543708be - Fri Jun 18 - mac80211: fix sw scan bracketing mcgrof@tux ~/linux-2.6-allstable (git::master)$ git describe \ --contains 5ee0865615f65f84e6ee9174771a6716c29e08e1 v2.6.36-rc1~43^2~34^2~22 mcgrof@tux ~/linux-2.6-allstable (git::master)$ git describe \ --contains a0daa0e7592ada797d6835f11529097aabc27ad2 v2.6.36-rc1~571^2~64^2~13 mcgrof@tux ~/linux-2.6-allstable (git::master)$ git describe \ --contains 543708be320d7df692d24b349ca01a947b340764 v2.6.36-rc1~571^2~107^2~187 So 5ee08656 would have worked if a0daa0e7 was not committed but it was so this means 5ee08656 was broken since it assumed that when we were in the channel change routine the scan flag would be lifted. As it turns out the scan flag will be set when we are already on the home channel. For more details refer to: http://code.google.com/p/chromium-os/issues/detail?id=5715 These issues will need to be considered for our solution on reshifting the scan complete callback location on mac80211 on current development kernel work. This patch has stable fixes which apply down to [2.6.36+] Cc: stable@kernel.org Cc: Paul Stewart Cc: Amod Bodas Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 1dd8768c63dc..3295c638f7e3 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -258,9 +258,11 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, if (!(sc->sc_flags & (SC_OP_OFFCHANNEL | SC_OP_SCANNING))) { ath_start_ani(common); ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0); - ath_beacon_config(sc, NULL); } + if (!(sc->sc_flags & (SC_OP_OFFCHANNEL))) + ath_beacon_config(sc, NULL); + ps_restore: ath9k_ps_restore(sc); return r; @@ -957,7 +959,7 @@ int ath_reset(struct ath_softc *sc, bool retry_tx) ath_update_txpow(sc); - if (sc->sc_flags & SC_OP_BEACONS) + if ((sc->sc_flags & SC_OP_BEACONS) || !(sc->sc_flags & (SC_OP_OFFCHANNEL))) ath_beacon_config(sc, NULL); /* restart beacons */ ath9k_hw_set_interrupts(ah, ah->imask); -- cgit v1.2.3 From 48a6a468198aadb54bc5d3fdd065364d43ff5197 Mon Sep 17 00:00:00 2001 From: Luis R. Rodriguez Date: Thu, 16 Sep 2010 15:12:28 -0400 Subject: ath9k: fix enabling ANI / tx monitor after bg scan ath9k's entire logic with SC_OP_SCANNING is incorrect due to the way mac80211 currently implements the scan complete callback and we handle it in ath9k. This patch removes the flag completely in preference for the SC_OP_OFFCHANNEL which is really what we wanted. The scanning flag was used to ensure we reset ANI to the old values when we go back to the home channel, but if we are offchannel we use some defaults. The flag was also used to re-enable the TX monitor. Without this patch we simply never re-enabled ANI and the TX monitor after going offchannel. This means that after one background scan we are prone to noise issues and if we had a TX hang we would not recover. To get this to work properly we must enable ANI after we have configured the beacon timers, otherwise hardware acts really oddly. This patch has stable fixes which apply down to [2.6.36+], there *may* be a to fix this on older kernels but requires a bit of work since this patch relies on the new mac80211 flag IEEE80211_CONF_OFFCHANNEL which was introduced as of 2.6.36. Cc: stable@kernel.org Cc: Paul Stewart Cc: Amod Bodas Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 - drivers/net/wireless/ath/ath9k/main.c | 10 +++------- drivers/net/wireless/ath/ath9k/recv.c | 4 ++-- 3 files changed, 5 insertions(+), 10 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index c8ff417f6c10..98b57e6c614e 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -563,7 +563,6 @@ struct ath_ant_comb { #define SC_OP_RXFLUSH BIT(7) #define SC_OP_LED_ASSOCIATED BIT(8) #define SC_OP_LED_ON BIT(9) -#define SC_OP_SCANNING BIT(10) #define SC_OP_TSF_RESET BIT(11) #define SC_OP_BT_PRIORITY_DETECTED BIT(12) #define SC_OP_BT_SCAN BIT(13) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 3295c638f7e3..5056733e6f66 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -255,14 +255,12 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, ath_update_txpow(sc); ath9k_hw_set_interrupts(ah, ah->imask); - if (!(sc->sc_flags & (SC_OP_OFFCHANNEL | SC_OP_SCANNING))) { - ath_start_ani(common); + if (!(sc->sc_flags & (SC_OP_OFFCHANNEL))) { + ath_beacon_config(sc, NULL); ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0); + ath_start_ani(common); } - if (!(sc->sc_flags & (SC_OP_OFFCHANNEL))) - ath_beacon_config(sc, NULL); - ps_restore: ath9k_ps_restore(sc); return r; @@ -2036,7 +2034,6 @@ static void ath9k_sw_scan_start(struct ieee80211_hw *hw) aphy->state = ATH_WIPHY_SCAN; ath9k_wiphy_pause_all_forced(sc, aphy); - sc->sc_flags |= SC_OP_SCANNING; mutex_unlock(&sc->mutex); } @@ -2051,7 +2048,6 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw) mutex_lock(&sc->mutex); aphy->state = ATH_WIPHY_ACTIVE; - sc->sc_flags &= ~SC_OP_SCANNING; mutex_unlock(&sc->mutex); } diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 3e7b8fe36044..007df33ab05e 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -300,7 +300,7 @@ static void ath_edma_start_recv(struct ath_softc *sc) ath_opmode_init(sc); - ath9k_hw_startpcureceive(sc->sc_ah, (sc->sc_flags & SC_OP_SCANNING)); + ath9k_hw_startpcureceive(sc->sc_ah, (sc->sc_flags & SC_OP_OFFCHANNEL)); } static void ath_edma_stop_recv(struct ath_softc *sc) @@ -506,7 +506,7 @@ int ath_startrecv(struct ath_softc *sc) start_recv: spin_unlock_bh(&sc->rx.rxbuflock); ath_opmode_init(sc); - ath9k_hw_startpcureceive(ah, (sc->sc_flags & SC_OP_SCANNING)); + ath9k_hw_startpcureceive(ah, (sc->sc_flags & SC_OP_OFFCHANNEL)); return 0; } -- cgit v1.2.3 From 3fac6dfdcd2b893c22b20a03dd1bf1af8b627c4b Mon Sep 17 00:00:00 2001 From: Senthil Balasubramanian Date: Thu, 16 Sep 2010 15:12:35 -0400 Subject: ath9k: fix regression which prevents chip sleep after CAB data The patch: commit 293dc5dfdbcc16cde06e40a688394cc8ab083e48 Author: Gabor Juhos Date: Fri Jun 19 12:17:48 2009 +0200 ath9k: remove ath_rx_ps_back_to_sleep helper This helper only clears the SC_OP_WAIT_FOR_{BEACON,CAB} flags. Remove it and clear these flags directly in the approptiate places instead. Changes-licensed-under: ISC Signed-off-by: Gabor Juhos Signed-off-by: John W. Linville introduced a regression which forgot to lift the beacon flag after we received all broadcast and multicast data. This meant we never went to sleep consuming about ~650mW on idle. This pretty much broke power save completely. This patch has fixes for stable kernels [2.6.32+]. Cc: stable@kernel.org Cc: Paul Stewart Cc: Sameer Nanda Cc: Gabor Juhos Cc: Amod Bodas Signed-off-by: Senthil Balasubramanian Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/recv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 007df33ab05e..c5e7af4f51ab 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -639,7 +639,7 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb) * No more broadcast/multicast frames to be received at this * point. */ - sc->ps_flags &= ~PS_WAIT_FOR_CAB; + sc->ps_flags &= ~(PS_WAIT_FOR_CAB | PS_WAIT_FOR_BEACON); ath_print(common, ATH_DBG_PS, "All PS CAB frames received, back to sleep\n"); } else if ((sc->ps_flags & PS_WAIT_FOR_PSPOLL_DATA) && -- cgit v1.2.3 From 008443def34db1dcc8016763587a288254ea5735 Mon Sep 17 00:00:00 2001 From: Luis R. Rodriguez Date: Thu, 16 Sep 2010 15:12:36 -0400 Subject: ath9k: fix regression which disabled ps on ath9k The patch titled "ath9k: Add new file init.c" shuffled some code around but in dong so for some reason also removed the revision check for disablign power save. Add this revision check again so we can get power save re-enabled again by default on cards newer than AR5416 and AR5418. $ git describe --contains 556242049cc3992d0ee625e9f15c4b00ea4baac8 v2.6.34-rc1~233^2~49^2~343 This patch has fixes for stable kernels [2.6.34+]. Cc: stable@kernel.org Cc: Paul Stewart Cc: Amod Bodas Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/init.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 224df6d8fe2c..0364167cbfaf 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -643,7 +643,8 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MESH_POINT); - hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + if (AR_SREV_5416(sc->sc_ah)) + hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; hw->queues = 4; hw->max_rates = 4; -- cgit v1.2.3 From c807666a08800ab387298817db5a6f6e11606adb Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Thu, 16 Sep 2010 22:55:20 +0200 Subject: carl9170: update AR9170 phy initvals This patch is loosely based on an ath9k patch called: "ath9k_hw: sync initvals for ar9001 and ar9002 with Atheros" It includes the following changes/fixes: - AGC setting improvements - timing changes for improved performance Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/phy.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c index 47709c13d964..71dea9707e21 100644 --- a/drivers/net/wireless/ath/carl9170/phy.c +++ b/drivers/net/wireless/ath/carl9170/phy.c @@ -85,11 +85,12 @@ static struct carl9170_phy_init ar5416_phy_init[] = { { 0x1c5844, 0x1372161e, 0x13721c1e, 0x13721c24, 0x137216a4, }, { 0x1c5848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, }, { 0x1c584c, 0x1284233c, 0x1284233c, 0x1284233c, 0x1284233c, }, - { 0x1c5850, 0x6c48b4e4, 0x6c48b4e4, 0x6c48b0e4, 0x6c48b0e4, }, + { 0x1c5850, 0x6c48b4e4, 0x6d48b4e4, 0x6d48b0e4, 0x6c48b0e4, }, { 0x1c5854, 0x00000859, 0x00000859, 0x00000859, 0x00000859, }, { 0x1c5858, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, }, - { 0x1c585c, 0x31395c5e, 0x31395c5e, 0x31395c5e, 0x31395c5e, }, + { 0x1c585c, 0x31395c5e, 0x3139605e, 0x3139605e, 0x31395c5e, }, { 0x1c5860, 0x0004dd10, 0x0004dd10, 0x0004dd20, 0x0004dd20, }, + { 0x1c5864, 0x0001c600, 0x0001c600, 0x0001c600, 0x0001c600, }, { 0x1c5868, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190, }, { 0x1c586c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, }, { 0x1c5900, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, @@ -131,9 +132,10 @@ static struct carl9170_phy_init ar5416_phy_init[] = { { 0x1c59ac, 0x006f00c4, 0x006f00c4, 0x006f00c4, 0x006f00c4, }, { 0x1c59b0, 0x03051000, 0x03051000, 0x03051000, 0x03051000, }, { 0x1c59b4, 0x00000820, 0x00000820, 0x00000820, 0x00000820, }, + { 0x1c59bc, 0x00181400, 0x00181400, 0x00181400, 0x00181400, }, { 0x1c59c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be, }, { 0x1c59c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, }, - { 0x1c59c8, 0x60f6532c, 0x60f6532c, 0x60f6532c, 0x60f6532c, }, + { 0x1c59c8, 0x6af6532c, 0x6af6532c, 0x6af6532c, 0x6af6532c, }, { 0x1c59cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, }, { 0x1c59d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, }, { 0x1c59d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, -- cgit v1.2.3 From ce2220d1da0bad9583af38a03ad508968d554c0f Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Fri, 17 Sep 2010 11:36:25 +0900 Subject: ath/ath5k/ath9k: Fix crypto capabilities merge issue Fixing up a merge issue / concurrent development: Remove unneeded ath_crypt_caps flags, as per "ath9k_hw: remove useless hw capability flags" (364734fafbba0c3133e482db78149b9a823ae7a5), but set the AESCCM flag for ath9k. common ath code still needs a flag for this because there is ath5k hardware which can't do AES in hardware. Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath.h | 9 ++------- drivers/net/wireless/ath/ath5k/attach.c | 3 +-- drivers/net/wireless/ath/ath9k/hw.c | 2 ++ 3 files changed, 5 insertions(+), 9 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index c5584077d1de..dd236c3b52f6 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -72,13 +72,8 @@ struct ath_regulatory { }; enum ath_crypt_caps { - ATH_CRYPT_CAP_MIC_AESCCM = BIT(0), - ATH_CRYPT_CAP_MIC_CKIP = BIT(1), - ATH_CRYPT_CAP_MIC_TKIP = BIT(2), - ATH_CRYPT_CAP_CIPHER_AESCCM = BIT(3), - ATH_CRYPT_CAP_CIPHER_CKIP = BIT(4), - ATH_CRYPT_CAP_CIPHER_TKIP = BIT(5), - ATH_CRYPT_CAP_MIC_COMBINED = BIT(6), + ATH_CRYPT_CAP_CIPHER_AESCCM = BIT(0), + ATH_CRYPT_CAP_MIC_COMBINED = BIT(1), }; struct ath_keyval { diff --git a/drivers/net/wireless/ath/ath5k/attach.c b/drivers/net/wireless/ath/ath5k/attach.c index a0e08201c939..20d178eabc9c 100644 --- a/drivers/net/wireless/ath/ath5k/attach.c +++ b/drivers/net/wireless/ath/ath5k/attach.c @@ -320,8 +320,7 @@ int ath5k_hw_attach(struct ath5k_softc *sc) if (srev >= AR5K_SREV_AR5212_V4 && (ee->ee_version >= AR5K_EEPROM_VERSION_5_0 && !AR5K_EEPROM_AES_DIS(ee->ee_misc5))) - common->crypt_caps |= ATH_CRYPT_CAP_CIPHER_AESCCM | - ATH_CRYPT_CAP_MIC_AESCCM; + common->crypt_caps |= ATH_CRYPT_CAP_CIPHER_AESCCM; if (srev >= AR5K_SREV_AR2414) { common->crypt_caps |= ATH_CRYPT_CAP_MIC_COMBINED; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index f3c9d7549571..0b2ff98b6f33 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1866,6 +1866,8 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) pCap->low_5ghz_chan = 4920; pCap->high_5ghz_chan = 6100; + common->crypt_caps |= ATH_CRYPT_CAP_CIPHER_AESCCM; + if (ah->config.ht_enable) pCap->hw_caps |= ATH9K_HW_CAP_HT; else -- cgit v1.2.3 From 8a63facc376a7b8bb2b64c0ffbdb0949d1d6c71c Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Fri, 17 Sep 2010 12:45:07 +0900 Subject: ath5k: reorder base.c to remove fwd decls This change reorganizes the main ath5k file in order to re-group related functions and remove most of the forward declarations (from 61 down to 3). This is, unfortunately, a lot of churn, but there should be no functional changes. Signed-off-by: Bob Copeland Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 4024 ++++++++++++++++----------------- 1 file changed, 1945 insertions(+), 2079 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 0709bb091b2f..4a07fb89021e 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -70,11 +70,6 @@ static int modparam_all_channels; module_param_named(all_channels, modparam_all_channels, bool, S_IRUGO); MODULE_PARM_DESC(all_channels, "Expose all channels the device can use."); - -/******************\ -* Internal defines * -\******************/ - /* Module info */ MODULE_AUTHOR("Jiri Slaby"); MODULE_AUTHOR("Nick Kossifidis"); @@ -83,6 +78,10 @@ MODULE_SUPPORTED_DEVICE("Atheros 5xxx WLAN cards"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_VERSION("0.6.0 (EXPERIMENTAL)"); +static int ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan); +static int ath5k_beacon_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); +static void ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf); /* Known PCI ids */ static DEFINE_PCI_DEVICE_TABLE(ath5k_pci_id_table) = { @@ -190,129 +189,6 @@ static const struct ieee80211_rate ath5k_rates[] = { /* XR missing */ }; -/* - * Prototypes - PCI stack related functions - */ -static int __devinit ath5k_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *id); -static void __devexit ath5k_pci_remove(struct pci_dev *pdev); -#ifdef CONFIG_PM_SLEEP -static int ath5k_pci_suspend(struct device *dev); -static int ath5k_pci_resume(struct device *dev); - -static SIMPLE_DEV_PM_OPS(ath5k_pm_ops, ath5k_pci_suspend, ath5k_pci_resume); -#define ATH5K_PM_OPS (&ath5k_pm_ops) -#else -#define ATH5K_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ - -static struct pci_driver ath5k_pci_driver = { - .name = KBUILD_MODNAME, - .id_table = ath5k_pci_id_table, - .probe = ath5k_pci_probe, - .remove = __devexit_p(ath5k_pci_remove), - .driver.pm = ATH5K_PM_OPS, -}; - - - -/* - * Prototypes - MAC 802.11 stack related functions - */ -static int ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb); -static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ath5k_txq *txq); -static int ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan); -static int ath5k_start(struct ieee80211_hw *hw); -static void ath5k_stop(struct ieee80211_hw *hw); -static int ath5k_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif); -static void ath5k_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif); -static int ath5k_config(struct ieee80211_hw *hw, u32 changed); -static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw, - struct netdev_hw_addr_list *mc_list); -static void ath5k_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *new_flags, - u64 multicast); -static int ath5k_set_key(struct ieee80211_hw *hw, - enum set_key_cmd cmd, - struct ieee80211_vif *vif, struct ieee80211_sta *sta, - struct ieee80211_key_conf *key); -static int ath5k_get_stats(struct ieee80211_hw *hw, - struct ieee80211_low_level_stats *stats); -static int ath5k_get_survey(struct ieee80211_hw *hw, - int idx, struct survey_info *survey); -static u64 ath5k_get_tsf(struct ieee80211_hw *hw); -static void ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf); -static void ath5k_reset_tsf(struct ieee80211_hw *hw); -static int ath5k_beacon_update(struct ieee80211_hw *hw, - struct ieee80211_vif *vif); -static void ath5k_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *bss_conf, - u32 changes); -static void ath5k_sw_scan_start(struct ieee80211_hw *hw); -static void ath5k_sw_scan_complete(struct ieee80211_hw *hw); -static void ath5k_set_coverage_class(struct ieee80211_hw *hw, - u8 coverage_class); - -static const struct ieee80211_ops ath5k_hw_ops = { - .tx = ath5k_tx, - .start = ath5k_start, - .stop = ath5k_stop, - .add_interface = ath5k_add_interface, - .remove_interface = ath5k_remove_interface, - .config = ath5k_config, - .prepare_multicast = ath5k_prepare_multicast, - .configure_filter = ath5k_configure_filter, - .set_key = ath5k_set_key, - .get_stats = ath5k_get_stats, - .get_survey = ath5k_get_survey, - .conf_tx = NULL, - .get_tsf = ath5k_get_tsf, - .set_tsf = ath5k_set_tsf, - .reset_tsf = ath5k_reset_tsf, - .bss_info_changed = ath5k_bss_info_changed, - .sw_scan_start = ath5k_sw_scan_start, - .sw_scan_complete = ath5k_sw_scan_complete, - .set_coverage_class = ath5k_set_coverage_class, -}; - -/* - * Prototypes - Internal functions - */ -/* Attach detach */ -static int ath5k_attach(struct pci_dev *pdev, - struct ieee80211_hw *hw); -static void ath5k_detach(struct pci_dev *pdev, - struct ieee80211_hw *hw); -/* Channel/mode setup */ -static inline short ath5k_ieee2mhz(short chan); -static unsigned int ath5k_copy_channels(struct ath5k_hw *ah, - struct ieee80211_channel *channels, - unsigned int mode, - unsigned int max); -static int ath5k_setup_bands(struct ieee80211_hw *hw); -static int ath5k_chan_set(struct ath5k_softc *sc, - struct ieee80211_channel *chan); -static void ath5k_setcurmode(struct ath5k_softc *sc, - unsigned int mode); -static void ath5k_mode_setup(struct ath5k_softc *sc); - -/* Descriptor setup */ -static int ath5k_desc_alloc(struct ath5k_softc *sc, - struct pci_dev *pdev); -static void ath5k_desc_free(struct ath5k_softc *sc, - struct pci_dev *pdev); -/* Buffers setup */ -static int ath5k_rxbuf_setup(struct ath5k_softc *sc, - struct ath5k_buf *bf); -static int ath5k_txbuf_setup(struct ath5k_softc *sc, - struct ath5k_buf *bf, - struct ath5k_txq *txq, int padsize); - static inline void ath5k_txbuf_free_skb(struct ath5k_softc *sc, struct ath5k_buf *bf) { @@ -345,35 +221,6 @@ static inline void ath5k_rxbuf_free_skb(struct ath5k_softc *sc, } -/* Queues setup */ -static struct ath5k_txq *ath5k_txq_setup(struct ath5k_softc *sc, - int qtype, int subtype); -static int ath5k_beaconq_setup(struct ath5k_hw *ah); -static int ath5k_beaconq_config(struct ath5k_softc *sc); -static void ath5k_txq_drainq(struct ath5k_softc *sc, - struct ath5k_txq *txq); -static void ath5k_txq_cleanup(struct ath5k_softc *sc); -static void ath5k_txq_release(struct ath5k_softc *sc); -/* Rx handling */ -static int ath5k_rx_start(struct ath5k_softc *sc); -static void ath5k_rx_stop(struct ath5k_softc *sc); -static unsigned int ath5k_rx_decrypted(struct ath5k_softc *sc, - struct sk_buff *skb, - struct ath5k_rx_status *rs); -static void ath5k_tasklet_rx(unsigned long data); -/* Tx handling */ -static void ath5k_tx_processq(struct ath5k_softc *sc, - struct ath5k_txq *txq); -static void ath5k_tasklet_tx(unsigned long data); -/* Beacon handling */ -static int ath5k_beacon_setup(struct ath5k_softc *sc, - struct ath5k_buf *bf); -static void ath5k_beacon_send(struct ath5k_softc *sc); -static void ath5k_beacon_config(struct ath5k_softc *sc); -static void ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf); -static void ath5k_tasklet_beacon(unsigned long data); -static void ath5k_tasklet_ani(unsigned long data); - static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp) { u64 tsf = ath5k_hw_get_tsf64(ah); @@ -384,50 +231,6 @@ static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp) return (tsf & ~0x7fff) | rstamp; } -/* Interrupt handling */ -static int ath5k_init(struct ath5k_softc *sc); -static int ath5k_stop_locked(struct ath5k_softc *sc); -static int ath5k_stop_hw(struct ath5k_softc *sc); -static irqreturn_t ath5k_intr(int irq, void *dev_id); -static void ath5k_reset_work(struct work_struct *work); - -static void ath5k_tasklet_calibrate(unsigned long data); - -/* - * Module init/exit functions - */ -static int __init -init_ath5k_pci(void) -{ - int ret; - - ath5k_debug_init(); - - ret = pci_register_driver(&ath5k_pci_driver); - if (ret) { - printk(KERN_ERR "ath5k_pci: can't register pci driver\n"); - return ret; - } - - return 0; -} - -static void __exit -exit_ath5k_pci(void) -{ - pci_unregister_driver(&ath5k_pci_driver); - - ath5k_debug_finish(); -} - -module_init(init_ath5k_pci); -module_exit(exit_ath5k_pci); - - -/********************\ -* PCI Initialization * -\********************/ - static const char * ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val) { @@ -466,1695 +269,1286 @@ static const struct ath_ops ath5k_common_ops = { .write = ath5k_iowrite32, }; -static int __devinit -ath5k_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *id) +/***********************\ +* Driver Initialization * +\***********************/ + +static int ath5k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { - void __iomem *mem; - struct ath5k_softc *sc; - struct ath_common *common; - struct ieee80211_hw *hw; - int ret; - u8 csz; + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct ath5k_softc *sc = hw->priv; + struct ath_regulatory *regulatory = ath5k_hw_regulatory(sc->ah); - /* - * L0s needs to be disabled on all ath5k cards. - * - * For distributions shipping with CONFIG_PCIEASPM (this will be enabled - * by default in the future in 2.6.36) this will also mean both L1 and - * L0s will be disabled when a pre 1.1 PCIe device is detected. We do - * know L1 works correctly even for all ath5k pre 1.1 PCIe devices - * though but cannot currently undue the effect of a blacklist, for - * details you can read pcie_aspm_sanity_check() and see how it adjusts - * the device link capability. - * - * It may be possible in the future to implement some PCI API to allow - * drivers to override blacklists for pre 1.1 PCIe but for now it is - * best to accept that both L0s and L1 will be disabled completely for - * distributions shipping with CONFIG_PCIEASPM rather than having this - * issue present. Motivation for adding this new API will be to help - * with power consumption for some of these devices. - */ - pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S); + return ath_reg_notifier_apply(wiphy, request, regulatory); +} - ret = pci_enable_device(pdev); - if (ret) { - dev_err(&pdev->dev, "can't enable device\n"); - goto err; - } +/********************\ +* Channel/mode setup * +\********************/ - /* XXX 32-bit addressing only */ - ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); - if (ret) { - dev_err(&pdev->dev, "32-bit DMA not available\n"); - goto err_dis; - } +/* + * Convert IEEE channel number to MHz frequency. + */ +static inline short +ath5k_ieee2mhz(short chan) +{ + if (chan <= 14 || chan >= 27) + return ieee80211chan2mhz(chan); + else + return 2212 + chan * 20; +} - /* - * Cache line size is used to size and align various - * structures used to communicate with the hardware. - */ - pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz); - if (csz == 0) { - /* - * Linux 2.4.18 (at least) writes the cache line size - * register as a 16-bit wide register which is wrong. - * We must have this setup properly for rx buffer - * DMA to work so force a reasonable value here if it - * comes up zero. - */ - csz = L1_CACHE_BYTES >> 2; - pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz); - } - /* - * The default setting of latency timer yields poor results, - * set it to the value used by other systems. It may be worth - * tweaking this setting more. - */ - pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8); +/* + * Returns true for the channel numbers used without all_channels modparam. + */ +static bool ath5k_is_standard_channel(short chan) +{ + return ((chan <= 14) || + /* UNII 1,2 */ + ((chan & 3) == 0 && chan >= 36 && chan <= 64) || + /* midband */ + ((chan & 3) == 0 && chan >= 100 && chan <= 140) || + /* UNII-3 */ + ((chan & 3) == 1 && chan >= 149 && chan <= 165)); +} - /* Enable bus mastering */ - pci_set_master(pdev); +static unsigned int +ath5k_copy_channels(struct ath5k_hw *ah, + struct ieee80211_channel *channels, + unsigned int mode, + unsigned int max) +{ + unsigned int i, count, size, chfreq, freq, ch; - /* - * Disable the RETRY_TIMEOUT register (0x41) to keep - * PCI Tx retries from interfering with C3 CPU state. - */ - pci_write_config_byte(pdev, 0x41, 0); + if (!test_bit(mode, ah->ah_modes)) + return 0; - ret = pci_request_region(pdev, 0, "ath5k"); - if (ret) { - dev_err(&pdev->dev, "cannot reserve PCI memory region\n"); - goto err_dis; + switch (mode) { + case AR5K_MODE_11A: + case AR5K_MODE_11A_TURBO: + /* 1..220, but 2GHz frequencies are filtered by check_channel */ + size = 220 ; + chfreq = CHANNEL_5GHZ; + break; + case AR5K_MODE_11B: + case AR5K_MODE_11G: + case AR5K_MODE_11G_TURBO: + size = 26; + chfreq = CHANNEL_2GHZ; + break; + default: + ATH5K_WARN(ah->ah_sc, "bad mode, not copying channels\n"); + return 0; } - mem = pci_iomap(pdev, 0, 0); - if (!mem) { - dev_err(&pdev->dev, "cannot remap PCI memory region\n") ; - ret = -EIO; - goto err_reg; - } + for (i = 0, count = 0; i < size && max > 0; i++) { + ch = i + 1 ; + freq = ath5k_ieee2mhz(ch); - /* - * Allocate hw (mac80211 main struct) - * and hw->priv (driver private data) - */ - hw = ieee80211_alloc_hw(sizeof(*sc), &ath5k_hw_ops); - if (hw == NULL) { - dev_err(&pdev->dev, "cannot allocate ieee80211_hw\n"); - ret = -ENOMEM; - goto err_map; - } - - dev_info(&pdev->dev, "registered as '%s'\n", wiphy_name(hw->wiphy)); - - /* Initialize driver private data */ - SET_IEEE80211_DEV(hw, &pdev->dev); - hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | - IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | - IEEE80211_HW_SIGNAL_DBM; + /* Check if channel is supported by the chipset */ + if (!ath5k_channel_ok(ah, freq, chfreq)) + continue; - hw->wiphy->interface_modes = - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_MESH_POINT); + if (!modparam_all_channels && !ath5k_is_standard_channel(ch)) + continue; - hw->extra_tx_headroom = 2; - hw->channel_change_time = 5000; - sc = hw->priv; - sc->hw = hw; - sc->pdev = pdev; + /* Write channel info and increment counter */ + channels[count].center_freq = freq; + channels[count].band = (chfreq == CHANNEL_2GHZ) ? + IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; + switch (mode) { + case AR5K_MODE_11A: + case AR5K_MODE_11G: + channels[count].hw_value = chfreq | CHANNEL_OFDM; + break; + case AR5K_MODE_11A_TURBO: + case AR5K_MODE_11G_TURBO: + channels[count].hw_value = chfreq | + CHANNEL_OFDM | CHANNEL_TURBO; + break; + case AR5K_MODE_11B: + channels[count].hw_value = CHANNEL_B; + } - ath5k_debug_init_device(sc); + count++; + max--; + } - /* - * Mark the device as detached to avoid processing - * interrupts until setup is complete. - */ - __set_bit(ATH_STAT_INVALID, sc->status); + return count; +} - sc->iobase = mem; /* So we can unmap it on detach */ - sc->opmode = NL80211_IFTYPE_STATION; - sc->bintval = 1000; - mutex_init(&sc->lock); - spin_lock_init(&sc->rxbuflock); - spin_lock_init(&sc->txbuflock); - spin_lock_init(&sc->block); +static void +ath5k_setup_rate_idx(struct ath5k_softc *sc, struct ieee80211_supported_band *b) +{ + u8 i; - /* Set private data */ - pci_set_drvdata(pdev, sc); + for (i = 0; i < AR5K_MAX_RATES; i++) + sc->rate_idx[b->band][i] = -1; - /* Setup interrupt handler */ - ret = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc); - if (ret) { - ATH5K_ERR(sc, "request_irq failed\n"); - goto err_free; + for (i = 0; i < b->n_bitrates; i++) { + sc->rate_idx[b->band][b->bitrates[i].hw_value] = i; + if (b->bitrates[i].hw_value_short) + sc->rate_idx[b->band][b->bitrates[i].hw_value_short] = i; } +} - /* If we passed the test, malloc an ath5k_hw struct */ - sc->ah = kzalloc(sizeof(struct ath5k_hw), GFP_KERNEL); - if (!sc->ah) { - ret = -ENOMEM; - ATH5K_ERR(sc, "out of memory\n"); - goto err_irq; - } +static int +ath5k_setup_bands(struct ieee80211_hw *hw) +{ + struct ath5k_softc *sc = hw->priv; + struct ath5k_hw *ah = sc->ah; + struct ieee80211_supported_band *sband; + int max_c, count_c = 0; + int i; - sc->ah->ah_sc = sc; - sc->ah->ah_iobase = sc->iobase; - common = ath5k_hw_common(sc->ah); - common->ops = &ath5k_common_ops; - common->ah = sc->ah; - common->hw = hw; - common->cachelsz = csz << 2; /* convert to bytes */ + BUILD_BUG_ON(ARRAY_SIZE(sc->sbands) < IEEE80211_NUM_BANDS); + max_c = ARRAY_SIZE(sc->channels); - /* Initialize device */ - ret = ath5k_hw_attach(sc); - if (ret) { - goto err_free_ah; - } + /* 2GHz band */ + sband = &sc->sbands[IEEE80211_BAND_2GHZ]; + sband->band = IEEE80211_BAND_2GHZ; + sband->bitrates = &sc->rates[IEEE80211_BAND_2GHZ][0]; - /* set up multi-rate retry capabilities */ - if (sc->ah->ah_version == AR5K_AR5212) { - hw->max_rates = 4; - hw->max_rate_tries = 11; - } + if (test_bit(AR5K_MODE_11G, sc->ah->ah_capabilities.cap_mode)) { + /* G mode */ + memcpy(sband->bitrates, &ath5k_rates[0], + sizeof(struct ieee80211_rate) * 12); + sband->n_bitrates = 12; - /* Finish private driver data initialization */ - ret = ath5k_attach(pdev, hw); - if (ret) - goto err_ah; + sband->channels = sc->channels; + sband->n_channels = ath5k_copy_channels(ah, sband->channels, + AR5K_MODE_11G, max_c); - ATH5K_INFO(sc, "Atheros AR%s chip found (MAC: 0x%x, PHY: 0x%x)\n", - ath5k_chip_name(AR5K_VERSION_MAC, sc->ah->ah_mac_srev), - sc->ah->ah_mac_srev, - sc->ah->ah_phy_revision); + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband; + count_c = sband->n_channels; + max_c -= count_c; + } else if (test_bit(AR5K_MODE_11B, sc->ah->ah_capabilities.cap_mode)) { + /* B mode */ + memcpy(sband->bitrates, &ath5k_rates[0], + sizeof(struct ieee80211_rate) * 4); + sband->n_bitrates = 4; - if (!sc->ah->ah_single_chip) { - /* Single chip radio (!RF5111) */ - if (sc->ah->ah_radio_5ghz_revision && - !sc->ah->ah_radio_2ghz_revision) { - /* No 5GHz support -> report 2GHz radio */ - if (!test_bit(AR5K_MODE_11A, - sc->ah->ah_capabilities.cap_mode)) { - ATH5K_INFO(sc, "RF%s 2GHz radio found (0x%x)\n", - ath5k_chip_name(AR5K_VERSION_RAD, - sc->ah->ah_radio_5ghz_revision), - sc->ah->ah_radio_5ghz_revision); - /* No 2GHz support (5110 and some - * 5Ghz only cards) -> report 5Ghz radio */ - } else if (!test_bit(AR5K_MODE_11B, - sc->ah->ah_capabilities.cap_mode)) { - ATH5K_INFO(sc, "RF%s 5GHz radio found (0x%x)\n", - ath5k_chip_name(AR5K_VERSION_RAD, - sc->ah->ah_radio_5ghz_revision), - sc->ah->ah_radio_5ghz_revision); - /* Multiband radio */ - } else { - ATH5K_INFO(sc, "RF%s multiband radio found" - " (0x%x)\n", - ath5k_chip_name(AR5K_VERSION_RAD, - sc->ah->ah_radio_5ghz_revision), - sc->ah->ah_radio_5ghz_revision); + /* 5211 only supports B rates and uses 4bit rate codes + * (e.g normally we have 0x1B for 1M, but on 5211 we have 0x0B) + * fix them up here: + */ + if (ah->ah_version == AR5K_AR5211) { + for (i = 0; i < 4; i++) { + sband->bitrates[i].hw_value = + sband->bitrates[i].hw_value & 0xF; + sband->bitrates[i].hw_value_short = + sband->bitrates[i].hw_value_short & 0xF; } } - /* Multi chip radio (RF5111 - RF2111) -> - * report both 2GHz/5GHz radios */ - else if (sc->ah->ah_radio_5ghz_revision && - sc->ah->ah_radio_2ghz_revision){ - ATH5K_INFO(sc, "RF%s 5GHz radio found (0x%x)\n", - ath5k_chip_name(AR5K_VERSION_RAD, - sc->ah->ah_radio_5ghz_revision), - sc->ah->ah_radio_5ghz_revision); - ATH5K_INFO(sc, "RF%s 2GHz radio found (0x%x)\n", - ath5k_chip_name(AR5K_VERSION_RAD, - sc->ah->ah_radio_2ghz_revision), - sc->ah->ah_radio_2ghz_revision); - } - } + sband->channels = sc->channels; + sband->n_channels = ath5k_copy_channels(ah, sband->channels, + AR5K_MODE_11B, max_c); - /* ready to process interrupts */ - __clear_bit(ATH_STAT_INVALID, sc->status); + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband; + count_c = sband->n_channels; + max_c -= count_c; + } + ath5k_setup_rate_idx(sc, sband); - return 0; -err_ah: - ath5k_hw_detach(sc->ah); -err_free_ah: - kfree(sc->ah); -err_irq: - free_irq(pdev->irq, sc); -err_free: - ieee80211_free_hw(hw); -err_map: - pci_iounmap(pdev, mem); -err_reg: - pci_release_region(pdev, 0); -err_dis: - pci_disable_device(pdev); -err: - return ret; -} + /* 5GHz band, A mode */ + if (test_bit(AR5K_MODE_11A, sc->ah->ah_capabilities.cap_mode)) { + sband = &sc->sbands[IEEE80211_BAND_5GHZ]; + sband->band = IEEE80211_BAND_5GHZ; + sband->bitrates = &sc->rates[IEEE80211_BAND_5GHZ][0]; -static void __devexit -ath5k_pci_remove(struct pci_dev *pdev) -{ - struct ath5k_softc *sc = pci_get_drvdata(pdev); + memcpy(sband->bitrates, &ath5k_rates[4], + sizeof(struct ieee80211_rate) * 8); + sband->n_bitrates = 8; - ath5k_debug_finish_device(sc); - ath5k_detach(pdev, sc->hw); - ath5k_hw_detach(sc->ah); - kfree(sc->ah); - free_irq(pdev->irq, sc); - pci_iounmap(pdev, sc->iobase); - pci_release_region(pdev, 0); - pci_disable_device(pdev); - ieee80211_free_hw(sc->hw); -} + sband->channels = &sc->channels[count_c]; + sband->n_channels = ath5k_copy_channels(ah, sband->channels, + AR5K_MODE_11A, max_c); -#ifdef CONFIG_PM_SLEEP -static int ath5k_pci_suspend(struct device *dev) -{ - struct ath5k_softc *sc = pci_get_drvdata(to_pci_dev(dev)); + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = sband; + } + ath5k_setup_rate_idx(sc, sband); + + ath5k_debug_dump_bands(sc); - ath5k_led_off(sc); return 0; } -static int ath5k_pci_resume(struct device *dev) -{ - struct pci_dev *pdev = to_pci_dev(dev); - struct ath5k_softc *sc = pci_get_drvdata(pdev); - +/* + * Set/change channels. We always reset the chip. + * To accomplish this we must first cleanup any pending DMA, + * then restart stuff after a la ath5k_init. + * + * Called with sc->lock. + */ +static int +ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan) +{ + ATH5K_DBG(sc, ATH5K_DEBUG_RESET, + "channel set, resetting (%u -> %u MHz)\n", + sc->curchan->center_freq, chan->center_freq); + /* - * Suspend/Resume resets the PCI configuration space, so we have to - * re-disable the RETRY_TIMEOUT register (0x41) to keep - * PCI Tx retries from interfering with C3 CPU state + * To switch channels clear any pending DMA operations; + * wait long enough for the RX fifo to drain, reset the + * hardware at the new frequency, and then re-enable + * the relevant bits of the h/w. */ - pci_write_config_byte(pdev, 0x41, 0); - - ath5k_led_enable(sc); - return 0; + return ath5k_reset(sc, chan); } -#endif /* CONFIG_PM_SLEEP */ - - -/***********************\ -* Driver Initialization * -\***********************/ -static int ath5k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) +static void +ath5k_setcurmode(struct ath5k_softc *sc, unsigned int mode) { - struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); - struct ath5k_softc *sc = hw->priv; - struct ath_regulatory *regulatory = ath5k_hw_regulatory(sc->ah); + sc->curmode = mode; - return ath_reg_notifier_apply(wiphy, request, regulatory); + if (mode == AR5K_MODE_11A) { + sc->curband = &sc->sbands[IEEE80211_BAND_5GHZ]; + } else { + sc->curband = &sc->sbands[IEEE80211_BAND_2GHZ]; + } } -static int -ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) +static void +ath5k_mode_setup(struct ath5k_softc *sc) { - struct ath5k_softc *sc = hw->priv; struct ath5k_hw *ah = sc->ah; - struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah); - u8 mac[ETH_ALEN] = {}; - int ret; + u32 rfilt; - ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "devid 0x%x\n", pdev->device); + /* configure rx filter */ + rfilt = sc->filter_flags; + ath5k_hw_set_rx_filter(ah, rfilt); - /* - * Check if the MAC has multi-rate retry support. - * We do this by trying to setup a fake extended - * descriptor. MACs that don't have support will - * return false w/o doing anything. MACs that do - * support it will return true w/o doing anything. - */ - ret = ath5k_hw_setup_mrr_tx_desc(ah, NULL, 0, 0, 0, 0, 0, 0); + if (ath5k_hw_hasbssidmask(ah)) + ath5k_hw_set_bssid_mask(ah, sc->bssidmask); - if (ret < 0) - goto err; - if (ret > 0) - __set_bit(ATH_STAT_MRRETRY, sc->status); + /* configure operational mode */ + ath5k_hw_set_opmode(ah, sc->opmode); - /* - * Collect the channel list. The 802.11 layer - * is resposible for filtering this list based - * on settings like the phy mode and regulatory - * domain restrictions. - */ - ret = ath5k_setup_bands(hw); - if (ret) { - ATH5K_ERR(sc, "can't get channels\n"); - goto err; - } + ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "mode setup opmode %d\n", sc->opmode); + ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "RX filter 0x%x\n", rfilt); +} - /* NB: setup here so ath5k_rate_update is happy */ - if (test_bit(AR5K_MODE_11A, ah->ah_modes)) - ath5k_setcurmode(sc, AR5K_MODE_11A); - else - ath5k_setcurmode(sc, AR5K_MODE_11B); +static inline int +ath5k_hw_to_driver_rix(struct ath5k_softc *sc, int hw_rix) +{ + int rix; - /* - * Allocate tx+rx descriptors and populate the lists. - */ - ret = ath5k_desc_alloc(sc, pdev); - if (ret) { - ATH5K_ERR(sc, "can't allocate descriptors\n"); - goto err; - } + /* return base rate on errors */ + if (WARN(hw_rix < 0 || hw_rix >= AR5K_MAX_RATES, + "hw_rix out of bounds: %x\n", hw_rix)) + return 0; + + rix = sc->rate_idx[sc->curband->band][hw_rix]; + if (WARN(rix < 0, "invalid hw_rix: %x\n", hw_rix)) + rix = 0; + + return rix; +} + +/***************\ +* Buffers setup * +\***************/ + +static +struct sk_buff *ath5k_rx_skb_alloc(struct ath5k_softc *sc, dma_addr_t *skb_addr) +{ + struct ath_common *common = ath5k_hw_common(sc->ah); + struct sk_buff *skb; /* - * Allocate hardware transmit queues: one queue for - * beacon frames and one data queue for each QoS - * priority. Note that hw functions handle resetting - * these queues at the needed time. + * Allocate buffer with headroom_needed space for the + * fake physical layer header at the start. */ - ret = ath5k_beaconq_setup(ah); - if (ret < 0) { - ATH5K_ERR(sc, "can't setup a beacon xmit queue\n"); - goto err_desc; - } - sc->bhalq = ret; - sc->cabq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_CAB, 0); - if (IS_ERR(sc->cabq)) { - ATH5K_ERR(sc, "can't setup cab queue\n"); - ret = PTR_ERR(sc->cabq); - goto err_bhal; - } + skb = ath_rxbuf_alloc(common, + common->rx_bufsize, + GFP_ATOMIC); - sc->txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BK); - if (IS_ERR(sc->txq)) { - ATH5K_ERR(sc, "can't setup xmit queue\n"); - ret = PTR_ERR(sc->txq); - goto err_queues; + if (!skb) { + ATH5K_ERR(sc, "can't alloc skbuff of size %u\n", + common->rx_bufsize); + return NULL; } - tasklet_init(&sc->rxtq, ath5k_tasklet_rx, (unsigned long)sc); - tasklet_init(&sc->txtq, ath5k_tasklet_tx, (unsigned long)sc); - tasklet_init(&sc->calib, ath5k_tasklet_calibrate, (unsigned long)sc); - tasklet_init(&sc->beacontq, ath5k_tasklet_beacon, (unsigned long)sc); - tasklet_init(&sc->ani_tasklet, ath5k_tasklet_ani, (unsigned long)sc); - - INIT_WORK(&sc->reset_work, ath5k_reset_work); - - ret = ath5k_eeprom_read_mac(ah, mac); - if (ret) { - ATH5K_ERR(sc, "unable to read address from EEPROM: 0x%04x\n", - sc->pdev->device); - goto err_queues; + *skb_addr = pci_map_single(sc->pdev, + skb->data, common->rx_bufsize, + PCI_DMA_FROMDEVICE); + if (unlikely(pci_dma_mapping_error(sc->pdev, *skb_addr))) { + ATH5K_ERR(sc, "%s: DMA mapping failed\n", __func__); + dev_kfree_skb(skb); + return NULL; } + return skb; +} - SET_IEEE80211_PERM_ADDR(hw, mac); - /* All MAC address bits matter for ACKs */ - memcpy(sc->bssidmask, ath_bcast_mac, ETH_ALEN); - ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask); +static int +ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) +{ + struct ath5k_hw *ah = sc->ah; + struct sk_buff *skb = bf->skb; + struct ath5k_desc *ds; + int ret; - regulatory->current_rd = ah->ah_capabilities.cap_eeprom.ee_regdomain; - ret = ath_regd_init(regulatory, hw->wiphy, ath5k_reg_notifier); - if (ret) { - ATH5K_ERR(sc, "can't initialize regulatory system\n"); - goto err_queues; + if (!skb) { + skb = ath5k_rx_skb_alloc(sc, &bf->skbaddr); + if (!skb) + return -ENOMEM; + bf->skb = skb; } - ret = ieee80211_register_hw(hw); + /* + * Setup descriptors. For receive we always terminate + * the descriptor list with a self-linked entry so we'll + * not get overrun under high load (as can happen with a + * 5212 when ANI processing enables PHY error frames). + * + * To ensure the last descriptor is self-linked we create + * each descriptor as self-linked and add it to the end. As + * each additional descriptor is added the previous self-linked + * entry is "fixed" naturally. This should be safe even + * if DMA is happening. When processing RX interrupts we + * never remove/process the last, self-linked, entry on the + * descriptor list. This ensures the hardware always has + * someplace to write a new frame. + */ + ds = bf->desc; + ds->ds_link = bf->daddr; /* link to self */ + ds->ds_data = bf->skbaddr; + ret = ath5k_hw_setup_rx_desc(ah, ds, ah->common.rx_bufsize, 0); if (ret) { - ATH5K_ERR(sc, "can't register ieee80211 hw\n"); - goto err_queues; + ATH5K_ERR(sc, "%s: could not setup RX desc\n", __func__); + return ret; } - if (!ath_is_world_regd(regulatory)) - regulatory_hint(hw->wiphy, regulatory->alpha2); - - ath5k_init_leds(sc); - - ath5k_sysfs_register(sc); - + if (sc->rxlink != NULL) + *sc->rxlink = bf->daddr; + sc->rxlink = &ds->ds_link; return 0; -err_queues: - ath5k_txq_release(sc); -err_bhal: - ath5k_hw_release_tx_queue(ah, sc->bhalq); -err_desc: - ath5k_desc_free(sc, pdev); -err: - return ret; } -static void -ath5k_detach(struct pci_dev *pdev, struct ieee80211_hw *hw) +static enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb) { - struct ath5k_softc *sc = hw->priv; + struct ieee80211_hdr *hdr; + enum ath5k_pkt_type htype; + __le16 fc; - /* - * NB: the order of these is important: - * o call the 802.11 layer before detaching ath5k_hw to - * ensure callbacks into the driver to delete global - * key cache entries can be handled - * o reclaim the tx queue data structures after calling - * the 802.11 layer as we'll get called back to reclaim - * node state and potentially want to use them - * o to cleanup the tx queues the hal is called, so detach - * it last - * XXX: ??? detach ath5k_hw ??? - * Other than that, it's straightforward... - */ - ieee80211_unregister_hw(hw); - ath5k_desc_free(sc, pdev); - ath5k_txq_release(sc); - ath5k_hw_release_tx_queue(sc->ah, sc->bhalq); - ath5k_unregister_leds(sc); - - ath5k_sysfs_unregister(sc); - /* - * NB: can't reclaim these until after ieee80211_ifdetach - * returns because we'll get called back to reclaim node - * state and potentially want to use them. - */ -} - - - - -/********************\ -* Channel/mode setup * -\********************/ + hdr = (struct ieee80211_hdr *)skb->data; + fc = hdr->frame_control; -/* - * Convert IEEE channel number to MHz frequency. - */ -static inline short -ath5k_ieee2mhz(short chan) -{ - if (chan <= 14 || chan >= 27) - return ieee80211chan2mhz(chan); + if (ieee80211_is_beacon(fc)) + htype = AR5K_PKT_TYPE_BEACON; + else if (ieee80211_is_probe_resp(fc)) + htype = AR5K_PKT_TYPE_PROBE_RESP; + else if (ieee80211_is_atim(fc)) + htype = AR5K_PKT_TYPE_ATIM; + else if (ieee80211_is_pspoll(fc)) + htype = AR5K_PKT_TYPE_PSPOLL; else - return 2212 + chan * 20; -} + htype = AR5K_PKT_TYPE_NORMAL; -/* - * Returns true for the channel numbers used without all_channels modparam. - */ -static bool ath5k_is_standard_channel(short chan) -{ - return ((chan <= 14) || - /* UNII 1,2 */ - ((chan & 3) == 0 && chan >= 36 && chan <= 64) || - /* midband */ - ((chan & 3) == 0 && chan >= 100 && chan <= 140) || - /* UNII-3 */ - ((chan & 3) == 1 && chan >= 149 && chan <= 165)); + return htype; } -static unsigned int -ath5k_copy_channels(struct ath5k_hw *ah, - struct ieee80211_channel *channels, - unsigned int mode, - unsigned int max) +static int +ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf, + struct ath5k_txq *txq, int padsize) { - unsigned int i, count, size, chfreq, freq, ch; + struct ath5k_hw *ah = sc->ah; + struct ath5k_desc *ds = bf->desc; + struct sk_buff *skb = bf->skb; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + unsigned int pktlen, flags, keyidx = AR5K_TXKEYIX_INVALID; + struct ieee80211_rate *rate; + unsigned int mrr_rate[3], mrr_tries[3]; + int i, ret; + u16 hw_rate; + u16 cts_rate = 0; + u16 duration = 0; + u8 rc_flags; - if (!test_bit(mode, ah->ah_modes)) - return 0; + flags = AR5K_TXDESC_INTREQ | AR5K_TXDESC_CLRDMASK; - switch (mode) { - case AR5K_MODE_11A: - case AR5K_MODE_11A_TURBO: - /* 1..220, but 2GHz frequencies are filtered by check_channel */ - size = 220 ; - chfreq = CHANNEL_5GHZ; - break; - case AR5K_MODE_11B: - case AR5K_MODE_11G: - case AR5K_MODE_11G_TURBO: - size = 26; - chfreq = CHANNEL_2GHZ; - break; - default: - ATH5K_WARN(ah->ah_sc, "bad mode, not copying channels\n"); - return 0; - } + /* XXX endianness */ + bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len, + PCI_DMA_TODEVICE); - for (i = 0, count = 0; i < size && max > 0; i++) { - ch = i + 1 ; - freq = ath5k_ieee2mhz(ch); + rate = ieee80211_get_tx_rate(sc->hw, info); - /* Check if channel is supported by the chipset */ - if (!ath5k_channel_ok(ah, freq, chfreq)) - continue; + if (info->flags & IEEE80211_TX_CTL_NO_ACK) + flags |= AR5K_TXDESC_NOACK; - if (!modparam_all_channels && !ath5k_is_standard_channel(ch)) - continue; + rc_flags = info->control.rates[0].flags; + hw_rate = (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ? + rate->hw_value_short : rate->hw_value; - /* Write channel info and increment counter */ - channels[count].center_freq = freq; - channels[count].band = (chfreq == CHANNEL_2GHZ) ? - IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; - switch (mode) { - case AR5K_MODE_11A: - case AR5K_MODE_11G: - channels[count].hw_value = chfreq | CHANNEL_OFDM; - break; - case AR5K_MODE_11A_TURBO: - case AR5K_MODE_11G_TURBO: - channels[count].hw_value = chfreq | - CHANNEL_OFDM | CHANNEL_TURBO; + pktlen = skb->len; + + /* FIXME: If we are in g mode and rate is a CCK rate + * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta + * from tx power (value is in dB units already) */ + if (info->control.hw_key) { + keyidx = info->control.hw_key->hw_key_idx; + pktlen += info->control.hw_key->icv_len; + } + if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) { + flags |= AR5K_TXDESC_RTSENA; + cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value; + duration = le16_to_cpu(ieee80211_rts_duration(sc->hw, + sc->vif, pktlen, info)); + } + if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { + flags |= AR5K_TXDESC_CTSENA; + cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value; + duration = le16_to_cpu(ieee80211_ctstoself_duration(sc->hw, + sc->vif, pktlen, info)); + } + ret = ah->ah_setup_tx_desc(ah, ds, pktlen, + ieee80211_get_hdrlen_from_skb(skb), padsize, + get_hw_packet_type(skb), + (sc->power_level * 2), + hw_rate, + info->control.rates[0].count, keyidx, ah->ah_tx_ant, flags, + cts_rate, duration); + if (ret) + goto err_unmap; + + memset(mrr_rate, 0, sizeof(mrr_rate)); + memset(mrr_tries, 0, sizeof(mrr_tries)); + for (i = 0; i < 3; i++) { + rate = ieee80211_get_alt_retry_rate(sc->hw, info, i); + if (!rate) break; - case AR5K_MODE_11B: - channels[count].hw_value = CHANNEL_B; - } - count++; - max--; + mrr_rate[i] = rate->hw_value; + mrr_tries[i] = info->control.rates[i + 1].count; } - return count; -} + ath5k_hw_setup_mrr_tx_desc(ah, ds, + mrr_rate[0], mrr_tries[0], + mrr_rate[1], mrr_tries[1], + mrr_rate[2], mrr_tries[2]); -static void -ath5k_setup_rate_idx(struct ath5k_softc *sc, struct ieee80211_supported_band *b) -{ - u8 i; + ds->ds_link = 0; + ds->ds_data = bf->skbaddr; - for (i = 0; i < AR5K_MAX_RATES; i++) - sc->rate_idx[b->band][i] = -1; + spin_lock_bh(&txq->lock); + list_add_tail(&bf->list, &txq->q); + if (txq->link == NULL) /* is this first packet? */ + ath5k_hw_set_txdp(ah, txq->qnum, bf->daddr); + else /* no, so only link it */ + *txq->link = bf->daddr; - for (i = 0; i < b->n_bitrates; i++) { - sc->rate_idx[b->band][b->bitrates[i].hw_value] = i; - if (b->bitrates[i].hw_value_short) - sc->rate_idx[b->band][b->bitrates[i].hw_value_short] = i; - } + txq->link = &ds->ds_link; + ath5k_hw_start_tx_dma(ah, txq->qnum); + mmiowb(); + spin_unlock_bh(&txq->lock); + + return 0; +err_unmap: + pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, PCI_DMA_TODEVICE); + return ret; } +/*******************\ +* Descriptors setup * +\*******************/ + static int -ath5k_setup_bands(struct ieee80211_hw *hw) +ath5k_desc_alloc(struct ath5k_softc *sc, struct pci_dev *pdev) { - struct ath5k_softc *sc = hw->priv; - struct ath5k_hw *ah = sc->ah; - struct ieee80211_supported_band *sband; - int max_c, count_c = 0; - int i; - - BUILD_BUG_ON(ARRAY_SIZE(sc->sbands) < IEEE80211_NUM_BANDS); - max_c = ARRAY_SIZE(sc->channels); + struct ath5k_desc *ds; + struct ath5k_buf *bf; + dma_addr_t da; + unsigned int i; + int ret; - /* 2GHz band */ - sband = &sc->sbands[IEEE80211_BAND_2GHZ]; - sband->band = IEEE80211_BAND_2GHZ; - sband->bitrates = &sc->rates[IEEE80211_BAND_2GHZ][0]; + /* allocate descriptors */ + sc->desc_len = sizeof(struct ath5k_desc) * + (ATH_TXBUF + ATH_RXBUF + ATH_BCBUF + 1); + sc->desc = pci_alloc_consistent(pdev, sc->desc_len, &sc->desc_daddr); + if (sc->desc == NULL) { + ATH5K_ERR(sc, "can't allocate descriptors\n"); + ret = -ENOMEM; + goto err; + } + ds = sc->desc; + da = sc->desc_daddr; + ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "DMA map: %p (%zu) -> %llx\n", + ds, sc->desc_len, (unsigned long long)sc->desc_daddr); - if (test_bit(AR5K_MODE_11G, sc->ah->ah_capabilities.cap_mode)) { - /* G mode */ - memcpy(sband->bitrates, &ath5k_rates[0], - sizeof(struct ieee80211_rate) * 12); - sband->n_bitrates = 12; + bf = kcalloc(1 + ATH_TXBUF + ATH_RXBUF + ATH_BCBUF, + sizeof(struct ath5k_buf), GFP_KERNEL); + if (bf == NULL) { + ATH5K_ERR(sc, "can't allocate bufptr\n"); + ret = -ENOMEM; + goto err_free; + } + sc->bufptr = bf; - sband->channels = sc->channels; - sband->n_channels = ath5k_copy_channels(ah, sband->channels, - AR5K_MODE_11G, max_c); - - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband; - count_c = sband->n_channels; - max_c -= count_c; - } else if (test_bit(AR5K_MODE_11B, sc->ah->ah_capabilities.cap_mode)) { - /* B mode */ - memcpy(sband->bitrates, &ath5k_rates[0], - sizeof(struct ieee80211_rate) * 4); - sband->n_bitrates = 4; - - /* 5211 only supports B rates and uses 4bit rate codes - * (e.g normally we have 0x1B for 1M, but on 5211 we have 0x0B) - * fix them up here: - */ - if (ah->ah_version == AR5K_AR5211) { - for (i = 0; i < 4; i++) { - sband->bitrates[i].hw_value = - sband->bitrates[i].hw_value & 0xF; - sband->bitrates[i].hw_value_short = - sband->bitrates[i].hw_value_short & 0xF; - } - } - - sband->channels = sc->channels; - sband->n_channels = ath5k_copy_channels(ah, sband->channels, - AR5K_MODE_11B, max_c); + INIT_LIST_HEAD(&sc->rxbuf); + for (i = 0; i < ATH_RXBUF; i++, bf++, ds++, da += sizeof(*ds)) { + bf->desc = ds; + bf->daddr = da; + list_add_tail(&bf->list, &sc->rxbuf); + } - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband; - count_c = sband->n_channels; - max_c -= count_c; + INIT_LIST_HEAD(&sc->txbuf); + sc->txbuf_len = ATH_TXBUF; + for (i = 0; i < ATH_TXBUF; i++, bf++, ds++, + da += sizeof(*ds)) { + bf->desc = ds; + bf->daddr = da; + list_add_tail(&bf->list, &sc->txbuf); } - ath5k_setup_rate_idx(sc, sband); - /* 5GHz band, A mode */ - if (test_bit(AR5K_MODE_11A, sc->ah->ah_capabilities.cap_mode)) { - sband = &sc->sbands[IEEE80211_BAND_5GHZ]; - sband->band = IEEE80211_BAND_5GHZ; - sband->bitrates = &sc->rates[IEEE80211_BAND_5GHZ][0]; + /* beacon buffer */ + bf->desc = ds; + bf->daddr = da; + sc->bbuf = bf; - memcpy(sband->bitrates, &ath5k_rates[4], - sizeof(struct ieee80211_rate) * 8); - sband->n_bitrates = 8; + return 0; +err_free: + pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr); +err: + sc->desc = NULL; + return ret; +} - sband->channels = &sc->channels[count_c]; - sband->n_channels = ath5k_copy_channels(ah, sband->channels, - AR5K_MODE_11A, max_c); +static void +ath5k_desc_free(struct ath5k_softc *sc, struct pci_dev *pdev) +{ + struct ath5k_buf *bf; - hw->wiphy->bands[IEEE80211_BAND_5GHZ] = sband; - } - ath5k_setup_rate_idx(sc, sband); + ath5k_txbuf_free_skb(sc, sc->bbuf); + list_for_each_entry(bf, &sc->txbuf, list) + ath5k_txbuf_free_skb(sc, bf); + list_for_each_entry(bf, &sc->rxbuf, list) + ath5k_rxbuf_free_skb(sc, bf); - ath5k_debug_dump_bands(sc); + /* Free memory associated with all descriptors */ + pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr); + sc->desc = NULL; + sc->desc_daddr = 0; - return 0; + kfree(sc->bufptr); + sc->bufptr = NULL; + sc->bbuf = NULL; } -/* - * Set/change channels. We always reset the chip. - * To accomplish this we must first cleanup any pending DMA, - * then restart stuff after a la ath5k_init. - * - * Called with sc->lock. - */ -static int -ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan) + +/**************\ +* Queues setup * +\**************/ + +static struct ath5k_txq * +ath5k_txq_setup(struct ath5k_softc *sc, + int qtype, int subtype) { - ATH5K_DBG(sc, ATH5K_DEBUG_RESET, - "channel set, resetting (%u -> %u MHz)\n", - sc->curchan->center_freq, chan->center_freq); + struct ath5k_hw *ah = sc->ah; + struct ath5k_txq *txq; + struct ath5k_txq_info qi = { + .tqi_subtype = subtype, + .tqi_aifs = AR5K_TXQ_USEDEFAULT, + .tqi_cw_min = AR5K_TXQ_USEDEFAULT, + .tqi_cw_max = AR5K_TXQ_USEDEFAULT + }; + int qnum; /* - * To switch channels clear any pending DMA operations; - * wait long enough for the RX fifo to drain, reset the - * hardware at the new frequency, and then re-enable - * the relevant bits of the h/w. + * Enable interrupts only for EOL and DESC conditions. + * We mark tx descriptors to receive a DESC interrupt + * when a tx queue gets deep; otherwise we wait for the + * EOL to reap descriptors. Note that this is done to + * reduce interrupt load and this only defers reaping + * descriptors, never transmitting frames. Aside from + * reducing interrupts this also permits more concurrency. + * The only potential downside is if the tx queue backs + * up in which case the top half of the kernel may backup + * due to a lack of tx descriptors. */ - return ath5k_reset(sc, chan); + qi.tqi_flags = AR5K_TXQ_FLAG_TXEOLINT_ENABLE | + AR5K_TXQ_FLAG_TXDESCINT_ENABLE; + qnum = ath5k_hw_setup_tx_queue(ah, qtype, &qi); + if (qnum < 0) { + /* + * NB: don't print a message, this happens + * normally on parts with too few tx queues + */ + return ERR_PTR(qnum); + } + if (qnum >= ARRAY_SIZE(sc->txqs)) { + ATH5K_ERR(sc, "hw qnum %u out of range, max %tu!\n", + qnum, ARRAY_SIZE(sc->txqs)); + ath5k_hw_release_tx_queue(ah, qnum); + return ERR_PTR(-EINVAL); + } + txq = &sc->txqs[qnum]; + if (!txq->setup) { + txq->qnum = qnum; + txq->link = NULL; + INIT_LIST_HEAD(&txq->q); + spin_lock_init(&txq->lock); + txq->setup = true; + } + return &sc->txqs[qnum]; } -static void -ath5k_setcurmode(struct ath5k_softc *sc, unsigned int mode) +static int +ath5k_beaconq_setup(struct ath5k_hw *ah) { - sc->curmode = mode; + struct ath5k_txq_info qi = { + .tqi_aifs = AR5K_TXQ_USEDEFAULT, + .tqi_cw_min = AR5K_TXQ_USEDEFAULT, + .tqi_cw_max = AR5K_TXQ_USEDEFAULT, + /* NB: for dynamic turbo, don't enable any other interrupts */ + .tqi_flags = AR5K_TXQ_FLAG_TXDESCINT_ENABLE + }; - if (mode == AR5K_MODE_11A) { - sc->curband = &sc->sbands[IEEE80211_BAND_5GHZ]; - } else { - sc->curband = &sc->sbands[IEEE80211_BAND_2GHZ]; - } + return ath5k_hw_setup_tx_queue(ah, AR5K_TX_QUEUE_BEACON, &qi); } -static void -ath5k_mode_setup(struct ath5k_softc *sc) +static int +ath5k_beaconq_config(struct ath5k_softc *sc) { struct ath5k_hw *ah = sc->ah; - u32 rfilt; - - /* configure rx filter */ - rfilt = sc->filter_flags; - ath5k_hw_set_rx_filter(ah, rfilt); + struct ath5k_txq_info qi; + int ret; - if (ath5k_hw_hasbssidmask(ah)) - ath5k_hw_set_bssid_mask(ah, sc->bssidmask); + ret = ath5k_hw_get_tx_queueprops(ah, sc->bhalq, &qi); + if (ret) + goto err; - /* configure operational mode */ - ath5k_hw_set_opmode(ah, sc->opmode); + if (sc->opmode == NL80211_IFTYPE_AP || + sc->opmode == NL80211_IFTYPE_MESH_POINT) { + /* + * Always burst out beacon and CAB traffic + * (aifs = cwmin = cwmax = 0) + */ + qi.tqi_aifs = 0; + qi.tqi_cw_min = 0; + qi.tqi_cw_max = 0; + } else if (sc->opmode == NL80211_IFTYPE_ADHOC) { + /* + * Adhoc mode; backoff between 0 and (2 * cw_min). + */ + qi.tqi_aifs = 0; + qi.tqi_cw_min = 0; + qi.tqi_cw_max = 2 * ah->ah_cw_min; + } - ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "mode setup opmode %d\n", sc->opmode); - ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "RX filter 0x%x\n", rfilt); -} + ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, + "beacon queueprops tqi_aifs:%d tqi_cw_min:%d tqi_cw_max:%d\n", + qi.tqi_aifs, qi.tqi_cw_min, qi.tqi_cw_max); -static inline int -ath5k_hw_to_driver_rix(struct ath5k_softc *sc, int hw_rix) -{ - int rix; + ret = ath5k_hw_set_tx_queueprops(ah, sc->bhalq, &qi); + if (ret) { + ATH5K_ERR(sc, "%s: unable to update parameters for beacon " + "hardware queue!\n", __func__); + goto err; + } + ret = ath5k_hw_reset_tx_queue(ah, sc->bhalq); /* push to h/w */ + if (ret) + goto err; - /* return base rate on errors */ - if (WARN(hw_rix < 0 || hw_rix >= AR5K_MAX_RATES, - "hw_rix out of bounds: %x\n", hw_rix)) - return 0; + /* reconfigure cabq with ready time to 80% of beacon_interval */ + ret = ath5k_hw_get_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi); + if (ret) + goto err; - rix = sc->rate_idx[sc->curband->band][hw_rix]; - if (WARN(rix < 0, "invalid hw_rix: %x\n", hw_rix)) - rix = 0; + qi.tqi_ready_time = (sc->bintval * 80) / 100; + ret = ath5k_hw_set_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi); + if (ret) + goto err; - return rix; + ret = ath5k_hw_reset_tx_queue(ah, AR5K_TX_QUEUE_ID_CAB); +err: + return ret; } -/***************\ -* Buffers setup * -\***************/ - -static -struct sk_buff *ath5k_rx_skb_alloc(struct ath5k_softc *sc, dma_addr_t *skb_addr) -{ - struct ath_common *common = ath5k_hw_common(sc->ah); - struct sk_buff *skb; +static void +ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq) +{ + struct ath5k_buf *bf, *bf0; /* - * Allocate buffer with headroom_needed space for the - * fake physical layer header at the start. + * NB: this assumes output has been stopped and + * we do not need to block ath5k_tx_tasklet */ - skb = ath_rxbuf_alloc(common, - common->rx_bufsize, - GFP_ATOMIC); + spin_lock_bh(&txq->lock); + list_for_each_entry_safe(bf, bf0, &txq->q, list) { + ath5k_debug_printtxbuf(sc, bf); - if (!skb) { - ATH5K_ERR(sc, "can't alloc skbuff of size %u\n", - common->rx_bufsize); - return NULL; - } + ath5k_txbuf_free_skb(sc, bf); - *skb_addr = pci_map_single(sc->pdev, - skb->data, common->rx_bufsize, - PCI_DMA_FROMDEVICE); - if (unlikely(pci_dma_mapping_error(sc->pdev, *skb_addr))) { - ATH5K_ERR(sc, "%s: DMA mapping failed\n", __func__); - dev_kfree_skb(skb); - return NULL; + spin_lock_bh(&sc->txbuflock); + list_move_tail(&bf->list, &sc->txbuf); + sc->txbuf_len++; + spin_unlock_bh(&sc->txbuflock); } - return skb; + txq->link = NULL; + spin_unlock_bh(&txq->lock); } -static int -ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) +/* + * Drain the transmit queues and reclaim resources. + */ +static void +ath5k_txq_cleanup(struct ath5k_softc *sc) { struct ath5k_hw *ah = sc->ah; - struct sk_buff *skb = bf->skb; - struct ath5k_desc *ds; - int ret; - - if (!skb) { - skb = ath5k_rx_skb_alloc(sc, &bf->skbaddr); - if (!skb) - return -ENOMEM; - bf->skb = skb; - } + unsigned int i; - /* - * Setup descriptors. For receive we always terminate - * the descriptor list with a self-linked entry so we'll - * not get overrun under high load (as can happen with a - * 5212 when ANI processing enables PHY error frames). - * - * To ensure the last descriptor is self-linked we create - * each descriptor as self-linked and add it to the end. As - * each additional descriptor is added the previous self-linked - * entry is "fixed" naturally. This should be safe even - * if DMA is happening. When processing RX interrupts we - * never remove/process the last, self-linked, entry on the - * descriptor list. This ensures the hardware always has - * someplace to write a new frame. - */ - ds = bf->desc; - ds->ds_link = bf->daddr; /* link to self */ - ds->ds_data = bf->skbaddr; - ret = ath5k_hw_setup_rx_desc(ah, ds, ah->common.rx_bufsize, 0); - if (ret) { - ATH5K_ERR(sc, "%s: could not setup RX desc\n", __func__); - return ret; + /* XXX return value */ + if (likely(!test_bit(ATH_STAT_INVALID, sc->status))) { + /* don't touch the hardware if marked invalid */ + ath5k_hw_stop_tx_dma(ah, sc->bhalq); + ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "beacon queue %x\n", + ath5k_hw_get_txdp(ah, sc->bhalq)); + for (i = 0; i < ARRAY_SIZE(sc->txqs); i++) + if (sc->txqs[i].setup) { + ath5k_hw_stop_tx_dma(ah, sc->txqs[i].qnum); + ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "txq [%u] %x, " + "link %p\n", + sc->txqs[i].qnum, + ath5k_hw_get_txdp(ah, + sc->txqs[i].qnum), + sc->txqs[i].link); + } } - if (sc->rxlink != NULL) - *sc->rxlink = bf->daddr; - sc->rxlink = &ds->ds_link; - return 0; + for (i = 0; i < ARRAY_SIZE(sc->txqs); i++) + if (sc->txqs[i].setup) + ath5k_txq_drainq(sc, &sc->txqs[i]); } -static enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb) +static void +ath5k_txq_release(struct ath5k_softc *sc) { - struct ieee80211_hdr *hdr; - enum ath5k_pkt_type htype; - __le16 fc; + struct ath5k_txq *txq = sc->txqs; + unsigned int i; - hdr = (struct ieee80211_hdr *)skb->data; - fc = hdr->frame_control; + for (i = 0; i < ARRAY_SIZE(sc->txqs); i++, txq++) + if (txq->setup) { + ath5k_hw_release_tx_queue(sc->ah, txq->qnum); + txq->setup = false; + } +} - if (ieee80211_is_beacon(fc)) - htype = AR5K_PKT_TYPE_BEACON; - else if (ieee80211_is_probe_resp(fc)) - htype = AR5K_PKT_TYPE_PROBE_RESP; - else if (ieee80211_is_atim(fc)) - htype = AR5K_PKT_TYPE_ATIM; - else if (ieee80211_is_pspoll(fc)) - htype = AR5K_PKT_TYPE_PSPOLL; - else - htype = AR5K_PKT_TYPE_NORMAL; - return htype; -} +/*************\ +* RX Handling * +\*************/ +/* + * Enable the receive h/w following a reset. + */ static int -ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf, - struct ath5k_txq *txq, int padsize) +ath5k_rx_start(struct ath5k_softc *sc) { struct ath5k_hw *ah = sc->ah; - struct ath5k_desc *ds = bf->desc; - struct sk_buff *skb = bf->skb; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - unsigned int pktlen, flags, keyidx = AR5K_TXKEYIX_INVALID; - struct ieee80211_rate *rate; - unsigned int mrr_rate[3], mrr_tries[3]; - int i, ret; - u16 hw_rate; - u16 cts_rate = 0; - u16 duration = 0; - u8 rc_flags; - - flags = AR5K_TXDESC_INTREQ | AR5K_TXDESC_CLRDMASK; - - /* XXX endianness */ - bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len, - PCI_DMA_TODEVICE); - - rate = ieee80211_get_tx_rate(sc->hw, info); - - if (info->flags & IEEE80211_TX_CTL_NO_ACK) - flags |= AR5K_TXDESC_NOACK; - - rc_flags = info->control.rates[0].flags; - hw_rate = (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ? - rate->hw_value_short : rate->hw_value; - - pktlen = skb->len; + struct ath_common *common = ath5k_hw_common(ah); + struct ath5k_buf *bf; + int ret; - /* FIXME: If we are in g mode and rate is a CCK rate - * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta - * from tx power (value is in dB units already) */ - if (info->control.hw_key) { - keyidx = info->control.hw_key->hw_key_idx; - pktlen += info->control.hw_key->icv_len; - } - if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) { - flags |= AR5K_TXDESC_RTSENA; - cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value; - duration = le16_to_cpu(ieee80211_rts_duration(sc->hw, - sc->vif, pktlen, info)); - } - if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { - flags |= AR5K_TXDESC_CTSENA; - cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value; - duration = le16_to_cpu(ieee80211_ctstoself_duration(sc->hw, - sc->vif, pktlen, info)); - } - ret = ah->ah_setup_tx_desc(ah, ds, pktlen, - ieee80211_get_hdrlen_from_skb(skb), padsize, - get_hw_packet_type(skb), - (sc->power_level * 2), - hw_rate, - info->control.rates[0].count, keyidx, ah->ah_tx_ant, flags, - cts_rate, duration); - if (ret) - goto err_unmap; + common->rx_bufsize = roundup(IEEE80211_MAX_FRAME_LEN, common->cachelsz); - memset(mrr_rate, 0, sizeof(mrr_rate)); - memset(mrr_tries, 0, sizeof(mrr_tries)); - for (i = 0; i < 3; i++) { - rate = ieee80211_get_alt_retry_rate(sc->hw, info, i); - if (!rate) - break; + ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "cachelsz %u rx_bufsize %u\n", + common->cachelsz, common->rx_bufsize); - mrr_rate[i] = rate->hw_value; - mrr_tries[i] = info->control.rates[i + 1].count; + spin_lock_bh(&sc->rxbuflock); + sc->rxlink = NULL; + list_for_each_entry(bf, &sc->rxbuf, list) { + ret = ath5k_rxbuf_setup(sc, bf); + if (ret != 0) { + spin_unlock_bh(&sc->rxbuflock); + goto err; + } } + bf = list_first_entry(&sc->rxbuf, struct ath5k_buf, list); + ath5k_hw_set_rxdp(ah, bf->daddr); + spin_unlock_bh(&sc->rxbuflock); - ath5k_hw_setup_mrr_tx_desc(ah, ds, - mrr_rate[0], mrr_tries[0], - mrr_rate[1], mrr_tries[1], - mrr_rate[2], mrr_tries[2]); - - ds->ds_link = 0; - ds->ds_data = bf->skbaddr; - - spin_lock_bh(&txq->lock); - list_add_tail(&bf->list, &txq->q); - if (txq->link == NULL) /* is this first packet? */ - ath5k_hw_set_txdp(ah, txq->qnum, bf->daddr); - else /* no, so only link it */ - *txq->link = bf->daddr; - - txq->link = &ds->ds_link; - ath5k_hw_start_tx_dma(ah, txq->qnum); - mmiowb(); - spin_unlock_bh(&txq->lock); + ath5k_hw_start_rx_dma(ah); /* enable recv descriptors */ + ath5k_mode_setup(sc); /* set filters, etc. */ + ath5k_hw_start_rx_pcu(ah); /* re-enable PCU/DMA engine */ return 0; -err_unmap: - pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, PCI_DMA_TODEVICE); +err: return ret; } -/*******************\ -* Descriptors setup * -\*******************/ - -static int -ath5k_desc_alloc(struct ath5k_softc *sc, struct pci_dev *pdev) +/* + * Disable the receive h/w in preparation for a reset. + */ +static void +ath5k_rx_stop(struct ath5k_softc *sc) { - struct ath5k_desc *ds; - struct ath5k_buf *bf; - dma_addr_t da; - unsigned int i; - int ret; + struct ath5k_hw *ah = sc->ah; - /* allocate descriptors */ - sc->desc_len = sizeof(struct ath5k_desc) * - (ATH_TXBUF + ATH_RXBUF + ATH_BCBUF + 1); - sc->desc = pci_alloc_consistent(pdev, sc->desc_len, &sc->desc_daddr); - if (sc->desc == NULL) { - ATH5K_ERR(sc, "can't allocate descriptors\n"); - ret = -ENOMEM; - goto err; - } - ds = sc->desc; - da = sc->desc_daddr; - ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "DMA map: %p (%zu) -> %llx\n", - ds, sc->desc_len, (unsigned long long)sc->desc_daddr); + ath5k_hw_stop_rx_pcu(ah); /* disable PCU */ + ath5k_hw_set_rx_filter(ah, 0); /* clear recv filter */ + ath5k_hw_stop_rx_dma(ah); /* disable DMA engine */ - bf = kcalloc(1 + ATH_TXBUF + ATH_RXBUF + ATH_BCBUF, - sizeof(struct ath5k_buf), GFP_KERNEL); - if (bf == NULL) { - ATH5K_ERR(sc, "can't allocate bufptr\n"); - ret = -ENOMEM; - goto err_free; - } - sc->bufptr = bf; + ath5k_debug_printrxbuffs(sc, ah); +} - INIT_LIST_HEAD(&sc->rxbuf); - for (i = 0; i < ATH_RXBUF; i++, bf++, ds++, da += sizeof(*ds)) { - bf->desc = ds; - bf->daddr = da; - list_add_tail(&bf->list, &sc->rxbuf); - } +static unsigned int +ath5k_rx_decrypted(struct ath5k_softc *sc, struct sk_buff *skb, + struct ath5k_rx_status *rs) +{ + struct ath5k_hw *ah = sc->ah; + struct ath_common *common = ath5k_hw_common(ah); + struct ieee80211_hdr *hdr = (void *)skb->data; + unsigned int keyix, hlen; - INIT_LIST_HEAD(&sc->txbuf); - sc->txbuf_len = ATH_TXBUF; - for (i = 0; i < ATH_TXBUF; i++, bf++, ds++, - da += sizeof(*ds)) { - bf->desc = ds; - bf->daddr = da; - list_add_tail(&bf->list, &sc->txbuf); - } + if (!(rs->rs_status & AR5K_RXERR_DECRYPT) && + rs->rs_keyix != AR5K_RXKEYIX_INVALID) + return RX_FLAG_DECRYPTED; - /* beacon buffer */ - bf->desc = ds; - bf->daddr = da; - sc->bbuf = bf; + /* Apparently when a default key is used to decrypt the packet + the hw does not set the index used to decrypt. In such cases + get the index from the packet. */ + hlen = ieee80211_hdrlen(hdr->frame_control); + if (ieee80211_has_protected(hdr->frame_control) && + !(rs->rs_status & AR5K_RXERR_DECRYPT) && + skb->len >= hlen + 4) { + keyix = skb->data[hlen + 3] >> 6; + + if (test_bit(keyix, common->keymap)) + return RX_FLAG_DECRYPTED; + } return 0; -err_free: - pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr); -err: - sc->desc = NULL; - return ret; } + static void -ath5k_desc_free(struct ath5k_softc *sc, struct pci_dev *pdev) +ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb, + struct ieee80211_rx_status *rxs) { - struct ath5k_buf *bf; + struct ath_common *common = ath5k_hw_common(sc->ah); + u64 tsf, bc_tstamp; + u32 hw_tu; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; - ath5k_txbuf_free_skb(sc, sc->bbuf); - list_for_each_entry(bf, &sc->txbuf, list) - ath5k_txbuf_free_skb(sc, bf); - list_for_each_entry(bf, &sc->rxbuf, list) - ath5k_rxbuf_free_skb(sc, bf); + if (ieee80211_is_beacon(mgmt->frame_control) && + le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS && + memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) == 0) { + /* + * Received an IBSS beacon with the same BSSID. Hardware *must* + * have updated the local TSF. We have to work around various + * hardware bugs, though... + */ + tsf = ath5k_hw_get_tsf64(sc->ah); + bc_tstamp = le64_to_cpu(mgmt->u.beacon.timestamp); + hw_tu = TSF_TO_TU(tsf); - /* Free memory associated with all descriptors */ - pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr); - sc->desc = NULL; - sc->desc_daddr = 0; + ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, + "beacon %llx mactime %llx (diff %lld) tsf now %llx\n", + (unsigned long long)bc_tstamp, + (unsigned long long)rxs->mactime, + (unsigned long long)(rxs->mactime - bc_tstamp), + (unsigned long long)tsf); - kfree(sc->bufptr); - sc->bufptr = NULL; - sc->bbuf = NULL; -} + /* + * Sometimes the HW will give us a wrong tstamp in the rx + * status, causing the timestamp extension to go wrong. + * (This seems to happen especially with beacon frames bigger + * than 78 byte (incl. FCS)) + * But we know that the receive timestamp must be later than the + * timestamp of the beacon since HW must have synced to that. + * + * NOTE: here we assume mactime to be after the frame was + * received, not like mac80211 which defines it at the start. + */ + if (bc_tstamp > rxs->mactime) { + ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, + "fixing mactime from %llx to %llx\n", + (unsigned long long)rxs->mactime, + (unsigned long long)tsf); + rxs->mactime = tsf; + } + /* + * Local TSF might have moved higher than our beacon timers, + * in that case we have to update them to continue sending + * beacons. This also takes care of synchronizing beacon sending + * times with other stations. + */ + if (hw_tu >= sc->nexttbtt) + ath5k_beacon_update_timers(sc, bc_tstamp); + } +} +static void +ath5k_update_beacon_rssi(struct ath5k_softc *sc, struct sk_buff *skb, int rssi) +{ + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + struct ath5k_hw *ah = sc->ah; + struct ath_common *common = ath5k_hw_common(ah); + /* only beacons from our BSSID */ + if (!ieee80211_is_beacon(mgmt->frame_control) || + memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) != 0) + return; + ah->ah_beacon_rssi_avg = ath5k_moving_average(ah->ah_beacon_rssi_avg, + rssi); -/**************\ -* Queues setup * -\**************/ + /* in IBSS mode we should keep RSSI statistics per neighbour */ + /* le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS */ +} -static struct ath5k_txq * -ath5k_txq_setup(struct ath5k_softc *sc, - int qtype, int subtype) +/* + * Compute padding position. skb must contain an IEEE 802.11 frame + */ +static int ath5k_common_padpos(struct sk_buff *skb) { - struct ath5k_hw *ah = sc->ah; - struct ath5k_txq *txq; - struct ath5k_txq_info qi = { - .tqi_subtype = subtype, - .tqi_aifs = AR5K_TXQ_USEDEFAULT, - .tqi_cw_min = AR5K_TXQ_USEDEFAULT, - .tqi_cw_max = AR5K_TXQ_USEDEFAULT - }; - int qnum; + struct ieee80211_hdr * hdr = (struct ieee80211_hdr *)skb->data; + __le16 frame_control = hdr->frame_control; + int padpos = 24; - /* - * Enable interrupts only for EOL and DESC conditions. - * We mark tx descriptors to receive a DESC interrupt - * when a tx queue gets deep; otherwise we wait for the - * EOL to reap descriptors. Note that this is done to - * reduce interrupt load and this only defers reaping - * descriptors, never transmitting frames. Aside from - * reducing interrupts this also permits more concurrency. - * The only potential downside is if the tx queue backs - * up in which case the top half of the kernel may backup - * due to a lack of tx descriptors. - */ - qi.tqi_flags = AR5K_TXQ_FLAG_TXEOLINT_ENABLE | - AR5K_TXQ_FLAG_TXDESCINT_ENABLE; - qnum = ath5k_hw_setup_tx_queue(ah, qtype, &qi); - if (qnum < 0) { - /* - * NB: don't print a message, this happens - * normally on parts with too few tx queues - */ - return ERR_PTR(qnum); - } - if (qnum >= ARRAY_SIZE(sc->txqs)) { - ATH5K_ERR(sc, "hw qnum %u out of range, max %tu!\n", - qnum, ARRAY_SIZE(sc->txqs)); - ath5k_hw_release_tx_queue(ah, qnum); - return ERR_PTR(-EINVAL); + if (ieee80211_has_a4(frame_control)) { + padpos += ETH_ALEN; } - txq = &sc->txqs[qnum]; - if (!txq->setup) { - txq->qnum = qnum; - txq->link = NULL; - INIT_LIST_HEAD(&txq->q); - spin_lock_init(&txq->lock); - txq->setup = true; + if (ieee80211_is_data_qos(frame_control)) { + padpos += IEEE80211_QOS_CTL_LEN; } - return &sc->txqs[qnum]; + + return padpos; } -static int -ath5k_beaconq_setup(struct ath5k_hw *ah) +/* + * This function expects an 802.11 frame and returns the number of + * bytes added, or -1 if we don't have enough header room. + */ +static int ath5k_add_padding(struct sk_buff *skb) { - struct ath5k_txq_info qi = { - .tqi_aifs = AR5K_TXQ_USEDEFAULT, - .tqi_cw_min = AR5K_TXQ_USEDEFAULT, - .tqi_cw_max = AR5K_TXQ_USEDEFAULT, - /* NB: for dynamic turbo, don't enable any other interrupts */ - .tqi_flags = AR5K_TXQ_FLAG_TXDESCINT_ENABLE - }; + int padpos = ath5k_common_padpos(skb); + int padsize = padpos & 3; - return ath5k_hw_setup_tx_queue(ah, AR5K_TX_QUEUE_BEACON, &qi); -} + if (padsize && skb->len>padpos) { -static int -ath5k_beaconq_config(struct ath5k_softc *sc) -{ - struct ath5k_hw *ah = sc->ah; - struct ath5k_txq_info qi; - int ret; + if (skb_headroom(skb) < padsize) + return -1; - ret = ath5k_hw_get_tx_queueprops(ah, sc->bhalq, &qi); - if (ret) - goto err; + skb_push(skb, padsize); + memmove(skb->data, skb->data+padsize, padpos); + return padsize; + } - if (sc->opmode == NL80211_IFTYPE_AP || - sc->opmode == NL80211_IFTYPE_MESH_POINT) { - /* - * Always burst out beacon and CAB traffic - * (aifs = cwmin = cwmax = 0) - */ - qi.tqi_aifs = 0; - qi.tqi_cw_min = 0; - qi.tqi_cw_max = 0; - } else if (sc->opmode == NL80211_IFTYPE_ADHOC) { - /* - * Adhoc mode; backoff between 0 and (2 * cw_min). - */ - qi.tqi_aifs = 0; - qi.tqi_cw_min = 0; - qi.tqi_cw_max = 2 * ah->ah_cw_min; - } + return 0; +} - ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, - "beacon queueprops tqi_aifs:%d tqi_cw_min:%d tqi_cw_max:%d\n", - qi.tqi_aifs, qi.tqi_cw_min, qi.tqi_cw_max); +/* + * The MAC header is padded to have 32-bit boundary if the + * packet payload is non-zero. The general calculation for + * padsize would take into account odd header lengths: + * padsize = 4 - (hdrlen & 3); however, since only + * even-length headers are used, padding can only be 0 or 2 + * bytes and we can optimize this a bit. We must not try to + * remove padding from short control frames that do not have a + * payload. + * + * This function expects an 802.11 frame and returns the number of + * bytes removed. + */ +static int ath5k_remove_padding(struct sk_buff *skb) +{ + int padpos = ath5k_common_padpos(skb); + int padsize = padpos & 3; - ret = ath5k_hw_set_tx_queueprops(ah, sc->bhalq, &qi); - if (ret) { - ATH5K_ERR(sc, "%s: unable to update parameters for beacon " - "hardware queue!\n", __func__); - goto err; + if (padsize && skb->len>=padpos+padsize) { + memmove(skb->data + padsize, skb->data, padpos); + skb_pull(skb, padsize); + return padsize; } - ret = ath5k_hw_reset_tx_queue(ah, sc->bhalq); /* push to h/w */ - if (ret) - goto err; - - /* reconfigure cabq with ready time to 80% of beacon_interval */ - ret = ath5k_hw_get_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi); - if (ret) - goto err; - - qi.tqi_ready_time = (sc->bintval * 80) / 100; - ret = ath5k_hw_set_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi); - if (ret) - goto err; - ret = ath5k_hw_reset_tx_queue(ah, AR5K_TX_QUEUE_ID_CAB); -err: - return ret; + return 0; } static void -ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq) +ath5k_receive_frame(struct ath5k_softc *sc, struct sk_buff *skb, + struct ath5k_rx_status *rs) { - struct ath5k_buf *bf, *bf0; + struct ieee80211_rx_status *rxs; + + ath5k_remove_padding(skb); + + rxs = IEEE80211_SKB_RXCB(skb); + + rxs->flag = 0; + if (unlikely(rs->rs_status & AR5K_RXERR_MIC)) + rxs->flag |= RX_FLAG_MMIC_ERROR; /* - * NB: this assumes output has been stopped and - * we do not need to block ath5k_tx_tasklet + * always extend the mac timestamp, since this information is + * also needed for proper IBSS merging. + * + * XXX: it might be too late to do it here, since rs_tstamp is + * 15bit only. that means TSF extension has to be done within + * 32768usec (about 32ms). it might be necessary to move this to + * the interrupt handler, like it is done in madwifi. + * + * Unfortunately we don't know when the hardware takes the rx + * timestamp (beginning of phy frame, data frame, end of rx?). + * The only thing we know is that it is hardware specific... + * On AR5213 it seems the rx timestamp is at the end of the + * frame, but i'm not sure. + * + * NOTE: mac80211 defines mactime at the beginning of the first + * data symbol. Since we don't have any time references it's + * impossible to comply to that. This affects IBSS merge only + * right now, so it's not too bad... */ - spin_lock_bh(&txq->lock); - list_for_each_entry_safe(bf, bf0, &txq->q, list) { - ath5k_debug_printtxbuf(sc, bf); - - ath5k_txbuf_free_skb(sc, bf); + rxs->mactime = ath5k_extend_tsf(sc->ah, rs->rs_tstamp); + rxs->flag |= RX_FLAG_TSFT; - spin_lock_bh(&sc->txbuflock); - list_move_tail(&bf->list, &sc->txbuf); - sc->txbuf_len++; - spin_unlock_bh(&sc->txbuflock); - } - txq->link = NULL; - spin_unlock_bh(&txq->lock); -} + rxs->freq = sc->curchan->center_freq; + rxs->band = sc->curband->band; -/* - * Drain the transmit queues and reclaim resources. - */ -static void -ath5k_txq_cleanup(struct ath5k_softc *sc) -{ - struct ath5k_hw *ah = sc->ah; - unsigned int i; + rxs->signal = sc->ah->ah_noise_floor + rs->rs_rssi; - /* XXX return value */ - if (likely(!test_bit(ATH_STAT_INVALID, sc->status))) { - /* don't touch the hardware if marked invalid */ - ath5k_hw_stop_tx_dma(ah, sc->bhalq); - ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "beacon queue %x\n", - ath5k_hw_get_txdp(ah, sc->bhalq)); - for (i = 0; i < ARRAY_SIZE(sc->txqs); i++) - if (sc->txqs[i].setup) { - ath5k_hw_stop_tx_dma(ah, sc->txqs[i].qnum); - ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "txq [%u] %x, " - "link %p\n", - sc->txqs[i].qnum, - ath5k_hw_get_txdp(ah, - sc->txqs[i].qnum), - sc->txqs[i].link); - } - } + rxs->antenna = rs->rs_antenna; - for (i = 0; i < ARRAY_SIZE(sc->txqs); i++) - if (sc->txqs[i].setup) - ath5k_txq_drainq(sc, &sc->txqs[i]); -} + if (rs->rs_antenna > 0 && rs->rs_antenna < 5) + sc->stats.antenna_rx[rs->rs_antenna]++; + else + sc->stats.antenna_rx[0]++; /* invalid */ -static void -ath5k_txq_release(struct ath5k_softc *sc) -{ - struct ath5k_txq *txq = sc->txqs; - unsigned int i; + rxs->rate_idx = ath5k_hw_to_driver_rix(sc, rs->rs_rate); + rxs->flag |= ath5k_rx_decrypted(sc, skb, rs); - for (i = 0; i < ARRAY_SIZE(sc->txqs); i++, txq++) - if (txq->setup) { - ath5k_hw_release_tx_queue(sc->ah, txq->qnum); - txq->setup = false; - } -} + if (rxs->rate_idx >= 0 && rs->rs_rate == + sc->curband->bitrates[rxs->rate_idx].hw_value_short) + rxs->flag |= RX_FLAG_SHORTPRE; + ath5k_debug_dump_skb(sc, skb, "RX ", 0); + ath5k_update_beacon_rssi(sc, skb, rs->rs_rssi); + /* check beacons in IBSS mode */ + if (sc->opmode == NL80211_IFTYPE_ADHOC) + ath5k_check_ibss_tsf(sc, skb, rxs); -/*************\ -* RX Handling * -\*************/ + ieee80211_rx(sc->hw, skb); +} -/* - * Enable the receive h/w following a reset. +/** ath5k_frame_receive_ok() - Do we want to receive this frame or not? + * + * Check if we want to further process this frame or not. Also update + * statistics. Return true if we want this frame, false if not. */ -static int -ath5k_rx_start(struct ath5k_softc *sc) +static bool +ath5k_receive_frame_ok(struct ath5k_softc *sc, struct ath5k_rx_status *rs) { - struct ath5k_hw *ah = sc->ah; - struct ath_common *common = ath5k_hw_common(ah); - struct ath5k_buf *bf; - int ret; - - common->rx_bufsize = roundup(IEEE80211_MAX_FRAME_LEN, common->cachelsz); - - ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "cachelsz %u rx_bufsize %u\n", - common->cachelsz, common->rx_bufsize); + sc->stats.rx_all_count++; - spin_lock_bh(&sc->rxbuflock); - sc->rxlink = NULL; - list_for_each_entry(bf, &sc->rxbuf, list) { - ret = ath5k_rxbuf_setup(sc, bf); - if (ret != 0) { - spin_unlock_bh(&sc->rxbuflock); - goto err; + if (unlikely(rs->rs_status)) { + if (rs->rs_status & AR5K_RXERR_CRC) + sc->stats.rxerr_crc++; + if (rs->rs_status & AR5K_RXERR_FIFO) + sc->stats.rxerr_fifo++; + if (rs->rs_status & AR5K_RXERR_PHY) { + sc->stats.rxerr_phy++; + if (rs->rs_phyerr > 0 && rs->rs_phyerr < 32) + sc->stats.rxerr_phy_code[rs->rs_phyerr]++; + return false; + } + if (rs->rs_status & AR5K_RXERR_DECRYPT) { + /* + * Decrypt error. If the error occurred + * because there was no hardware key, then + * let the frame through so the upper layers + * can process it. This is necessary for 5210 + * parts which have no way to setup a ``clear'' + * key cache entry. + * + * XXX do key cache faulting + */ + sc->stats.rxerr_decrypt++; + if (rs->rs_keyix == AR5K_RXKEYIX_INVALID && + !(rs->rs_status & AR5K_RXERR_CRC)) + return true; + } + if (rs->rs_status & AR5K_RXERR_MIC) { + sc->stats.rxerr_mic++; + return true; } - } - bf = list_first_entry(&sc->rxbuf, struct ath5k_buf, list); - ath5k_hw_set_rxdp(ah, bf->daddr); - spin_unlock_bh(&sc->rxbuflock); - ath5k_hw_start_rx_dma(ah); /* enable recv descriptors */ - ath5k_mode_setup(sc); /* set filters, etc. */ - ath5k_hw_start_rx_pcu(ah); /* re-enable PCU/DMA engine */ + /* reject any frames with non-crypto errors */ + if (rs->rs_status & ~(AR5K_RXERR_DECRYPT)) + return false; + } - return 0; -err: - return ret; + if (unlikely(rs->rs_more)) { + sc->stats.rxerr_jumbo++; + return false; + } + return true; } -/* - * Disable the receive h/w in preparation for a reset. - */ static void -ath5k_rx_stop(struct ath5k_softc *sc) -{ - struct ath5k_hw *ah = sc->ah; - - ath5k_hw_stop_rx_pcu(ah); /* disable PCU */ - ath5k_hw_set_rx_filter(ah, 0); /* clear recv filter */ - ath5k_hw_stop_rx_dma(ah); /* disable DMA engine */ - - ath5k_debug_printrxbuffs(sc, ah); -} - -static unsigned int -ath5k_rx_decrypted(struct ath5k_softc *sc, struct sk_buff *skb, - struct ath5k_rx_status *rs) +ath5k_tasklet_rx(unsigned long data) { + struct ath5k_rx_status rs = {}; + struct sk_buff *skb, *next_skb; + dma_addr_t next_skb_addr; + struct ath5k_softc *sc = (void *)data; struct ath5k_hw *ah = sc->ah; struct ath_common *common = ath5k_hw_common(ah); - struct ieee80211_hdr *hdr = (void *)skb->data; - unsigned int keyix, hlen; + struct ath5k_buf *bf; + struct ath5k_desc *ds; + int ret; - if (!(rs->rs_status & AR5K_RXERR_DECRYPT) && - rs->rs_keyix != AR5K_RXKEYIX_INVALID) - return RX_FLAG_DECRYPTED; + spin_lock(&sc->rxbuflock); + if (list_empty(&sc->rxbuf)) { + ATH5K_WARN(sc, "empty rx buf pool\n"); + goto unlock; + } + do { + bf = list_first_entry(&sc->rxbuf, struct ath5k_buf, list); + BUG_ON(bf->skb == NULL); + skb = bf->skb; + ds = bf->desc; - /* Apparently when a default key is used to decrypt the packet - the hw does not set the index used to decrypt. In such cases - get the index from the packet. */ - hlen = ieee80211_hdrlen(hdr->frame_control); - if (ieee80211_has_protected(hdr->frame_control) && - !(rs->rs_status & AR5K_RXERR_DECRYPT) && - skb->len >= hlen + 4) { - keyix = skb->data[hlen + 3] >> 6; + /* bail if HW is still using self-linked descriptor */ + if (ath5k_hw_get_rxdp(sc->ah) == bf->daddr) + break; - if (test_bit(keyix, common->keymap)) - return RX_FLAG_DECRYPTED; - } + ret = sc->ah->ah_proc_rx_desc(sc->ah, ds, &rs); + if (unlikely(ret == -EINPROGRESS)) + break; + else if (unlikely(ret)) { + ATH5K_ERR(sc, "error in processing rx descriptor\n"); + sc->stats.rxerr_proc++; + break; + } - return 0; -} + if (ath5k_receive_frame_ok(sc, &rs)) { + next_skb = ath5k_rx_skb_alloc(sc, &next_skb_addr); + /* + * If we can't replace bf->skb with a new skb under + * memory pressure, just skip this packet + */ + if (!next_skb) + goto next; -static void -ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb, - struct ieee80211_rx_status *rxs) -{ - struct ath_common *common = ath5k_hw_common(sc->ah); - u64 tsf, bc_tstamp; - u32 hw_tu; - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + pci_unmap_single(sc->pdev, bf->skbaddr, + common->rx_bufsize, + PCI_DMA_FROMDEVICE); - if (ieee80211_is_beacon(mgmt->frame_control) && - le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS && - memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) == 0) { - /* - * Received an IBSS beacon with the same BSSID. Hardware *must* - * have updated the local TSF. We have to work around various - * hardware bugs, though... - */ - tsf = ath5k_hw_get_tsf64(sc->ah); - bc_tstamp = le64_to_cpu(mgmt->u.beacon.timestamp); - hw_tu = TSF_TO_TU(tsf); + skb_put(skb, rs.rs_datalen); - ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, - "beacon %llx mactime %llx (diff %lld) tsf now %llx\n", - (unsigned long long)bc_tstamp, - (unsigned long long)rxs->mactime, - (unsigned long long)(rxs->mactime - bc_tstamp), - (unsigned long long)tsf); + ath5k_receive_frame(sc, skb, &rs); - /* - * Sometimes the HW will give us a wrong tstamp in the rx - * status, causing the timestamp extension to go wrong. - * (This seems to happen especially with beacon frames bigger - * than 78 byte (incl. FCS)) - * But we know that the receive timestamp must be later than the - * timestamp of the beacon since HW must have synced to that. - * - * NOTE: here we assume mactime to be after the frame was - * received, not like mac80211 which defines it at the start. - */ - if (bc_tstamp > rxs->mactime) { - ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, - "fixing mactime from %llx to %llx\n", - (unsigned long long)rxs->mactime, - (unsigned long long)tsf); - rxs->mactime = tsf; + bf->skb = next_skb; + bf->skbaddr = next_skb_addr; } - - /* - * Local TSF might have moved higher than our beacon timers, - * in that case we have to update them to continue sending - * beacons. This also takes care of synchronizing beacon sending - * times with other stations. - */ - if (hw_tu >= sc->nexttbtt) - ath5k_beacon_update_timers(sc, bc_tstamp); - } +next: + list_move_tail(&bf->list, &sc->rxbuf); + } while (ath5k_rxbuf_setup(sc, bf) == 0); +unlock: + spin_unlock(&sc->rxbuflock); } -static void -ath5k_update_beacon_rssi(struct ath5k_softc *sc, struct sk_buff *skb, int rssi) -{ - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; - struct ath5k_hw *ah = sc->ah; - struct ath_common *common = ath5k_hw_common(ah); - /* only beacons from our BSSID */ - if (!ieee80211_is_beacon(mgmt->frame_control) || - memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) != 0) - return; +/*************\ +* TX Handling * +\*************/ - ah->ah_beacon_rssi_avg = ath5k_moving_average(ah->ah_beacon_rssi_avg, - rssi); +static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ath5k_txq *txq) +{ + struct ath5k_softc *sc = hw->priv; + struct ath5k_buf *bf; + unsigned long flags; + int padsize; - /* in IBSS mode we should keep RSSI statistics per neighbour */ - /* le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS */ -} + ath5k_debug_dump_skb(sc, skb, "TX ", 1); -/* - * Compute padding position. skb must contain an IEEE 802.11 frame - */ -static int ath5k_common_padpos(struct sk_buff *skb) -{ - struct ieee80211_hdr * hdr = (struct ieee80211_hdr *)skb->data; - __le16 frame_control = hdr->frame_control; - int padpos = 24; + /* + * The hardware expects the header padded to 4 byte boundaries. + * If this is not the case, we add the padding after the header. + */ + padsize = ath5k_add_padding(skb); + if (padsize < 0) { + ATH5K_ERR(sc, "tx hdrlen not %%4: not enough" + " headroom to pad"); + goto drop_packet; + } - if (ieee80211_has_a4(frame_control)) { - padpos += ETH_ALEN; + spin_lock_irqsave(&sc->txbuflock, flags); + if (list_empty(&sc->txbuf)) { + ATH5K_ERR(sc, "no further txbuf available, dropping packet\n"); + spin_unlock_irqrestore(&sc->txbuflock, flags); + ieee80211_stop_queue(hw, skb_get_queue_mapping(skb)); + goto drop_packet; } - if (ieee80211_is_data_qos(frame_control)) { - padpos += IEEE80211_QOS_CTL_LEN; + bf = list_first_entry(&sc->txbuf, struct ath5k_buf, list); + list_del(&bf->list); + sc->txbuf_len--; + if (list_empty(&sc->txbuf)) + ieee80211_stop_queues(hw); + spin_unlock_irqrestore(&sc->txbuflock, flags); + + bf->skb = skb; + + if (ath5k_txbuf_setup(sc, bf, txq, padsize)) { + bf->skb = NULL; + spin_lock_irqsave(&sc->txbuflock, flags); + list_add_tail(&bf->list, &sc->txbuf); + sc->txbuf_len++; + spin_unlock_irqrestore(&sc->txbuflock, flags); + goto drop_packet; } + return NETDEV_TX_OK; - return padpos; +drop_packet: + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; } -/* - * This function expects an 802.11 frame and returns the number of - * bytes added, or -1 if we don't have enough header room. - */ -static int ath5k_add_padding(struct sk_buff *skb) + +static void +ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq) { - int padpos = ath5k_common_padpos(skb); - int padsize = padpos & 3; + struct ath5k_tx_status ts = {}; + struct ath5k_buf *bf, *bf0; + struct ath5k_desc *ds; + struct sk_buff *skb; + struct ieee80211_tx_info *info; + int i, ret; - if (padsize && skb->len>padpos) { + spin_lock(&txq->lock); + list_for_each_entry_safe(bf, bf0, &txq->q, list) { + ds = bf->desc; - if (skb_headroom(skb) < padsize) - return -1; + /* + * It's possible that the hardware can say the buffer is + * completed when it hasn't yet loaded the ds_link from + * host memory and moved on. If there are more TX + * descriptors in the queue, wait for TXDP to change + * before processing this one. + */ + if (ath5k_hw_get_txdp(sc->ah, txq->qnum) == bf->daddr && + !list_is_last(&bf->list, &txq->q)) + break; - skb_push(skb, padsize); - memmove(skb->data, skb->data+padsize, padpos); - return padsize; - } + ret = sc->ah->ah_proc_tx_desc(sc->ah, ds, &ts); + if (unlikely(ret == -EINPROGRESS)) + break; + else if (unlikely(ret)) { + ATH5K_ERR(sc, "error %d while processing queue %u\n", + ret, txq->qnum); + break; + } - return 0; -} - -/* - * The MAC header is padded to have 32-bit boundary if the - * packet payload is non-zero. The general calculation for - * padsize would take into account odd header lengths: - * padsize = 4 - (hdrlen & 3); however, since only - * even-length headers are used, padding can only be 0 or 2 - * bytes and we can optimize this a bit. We must not try to - * remove padding from short control frames that do not have a - * payload. - * - * This function expects an 802.11 frame and returns the number of - * bytes removed. - */ -static int ath5k_remove_padding(struct sk_buff *skb) -{ - int padpos = ath5k_common_padpos(skb); - int padsize = padpos & 3; - - if (padsize && skb->len>=padpos+padsize) { - memmove(skb->data + padsize, skb->data, padpos); - skb_pull(skb, padsize); - return padsize; - } - - return 0; -} - -static void -ath5k_receive_frame(struct ath5k_softc *sc, struct sk_buff *skb, - struct ath5k_rx_status *rs) -{ - struct ieee80211_rx_status *rxs; - - ath5k_remove_padding(skb); - - rxs = IEEE80211_SKB_RXCB(skb); - - rxs->flag = 0; - if (unlikely(rs->rs_status & AR5K_RXERR_MIC)) - rxs->flag |= RX_FLAG_MMIC_ERROR; - - /* - * always extend the mac timestamp, since this information is - * also needed for proper IBSS merging. - * - * XXX: it might be too late to do it here, since rs_tstamp is - * 15bit only. that means TSF extension has to be done within - * 32768usec (about 32ms). it might be necessary to move this to - * the interrupt handler, like it is done in madwifi. - * - * Unfortunately we don't know when the hardware takes the rx - * timestamp (beginning of phy frame, data frame, end of rx?). - * The only thing we know is that it is hardware specific... - * On AR5213 it seems the rx timestamp is at the end of the - * frame, but i'm not sure. - * - * NOTE: mac80211 defines mactime at the beginning of the first - * data symbol. Since we don't have any time references it's - * impossible to comply to that. This affects IBSS merge only - * right now, so it's not too bad... - */ - rxs->mactime = ath5k_extend_tsf(sc->ah, rs->rs_tstamp); - rxs->flag |= RX_FLAG_TSFT; - - rxs->freq = sc->curchan->center_freq; - rxs->band = sc->curband->band; - - rxs->signal = sc->ah->ah_noise_floor + rs->rs_rssi; - - rxs->antenna = rs->rs_antenna; - - if (rs->rs_antenna > 0 && rs->rs_antenna < 5) - sc->stats.antenna_rx[rs->rs_antenna]++; - else - sc->stats.antenna_rx[0]++; /* invalid */ - - rxs->rate_idx = ath5k_hw_to_driver_rix(sc, rs->rs_rate); - rxs->flag |= ath5k_rx_decrypted(sc, skb, rs); - - if (rxs->rate_idx >= 0 && rs->rs_rate == - sc->curband->bitrates[rxs->rate_idx].hw_value_short) - rxs->flag |= RX_FLAG_SHORTPRE; - - ath5k_debug_dump_skb(sc, skb, "RX ", 0); - - ath5k_update_beacon_rssi(sc, skb, rs->rs_rssi); - - /* check beacons in IBSS mode */ - if (sc->opmode == NL80211_IFTYPE_ADHOC) - ath5k_check_ibss_tsf(sc, skb, rxs); - - ieee80211_rx(sc->hw, skb); -} - -/** ath5k_frame_receive_ok() - Do we want to receive this frame or not? - * - * Check if we want to further process this frame or not. Also update - * statistics. Return true if we want this frame, false if not. - */ -static bool -ath5k_receive_frame_ok(struct ath5k_softc *sc, struct ath5k_rx_status *rs) -{ - sc->stats.rx_all_count++; - - if (unlikely(rs->rs_status)) { - if (rs->rs_status & AR5K_RXERR_CRC) - sc->stats.rxerr_crc++; - if (rs->rs_status & AR5K_RXERR_FIFO) - sc->stats.rxerr_fifo++; - if (rs->rs_status & AR5K_RXERR_PHY) { - sc->stats.rxerr_phy++; - if (rs->rs_phyerr > 0 && rs->rs_phyerr < 32) - sc->stats.rxerr_phy_code[rs->rs_phyerr]++; - return false; - } - if (rs->rs_status & AR5K_RXERR_DECRYPT) { - /* - * Decrypt error. If the error occurred - * because there was no hardware key, then - * let the frame through so the upper layers - * can process it. This is necessary for 5210 - * parts which have no way to setup a ``clear'' - * key cache entry. - * - * XXX do key cache faulting - */ - sc->stats.rxerr_decrypt++; - if (rs->rs_keyix == AR5K_RXKEYIX_INVALID && - !(rs->rs_status & AR5K_RXERR_CRC)) - return true; - } - if (rs->rs_status & AR5K_RXERR_MIC) { - sc->stats.rxerr_mic++; - return true; - } - - /* reject any frames with non-crypto errors */ - if (rs->rs_status & ~(AR5K_RXERR_DECRYPT)) - return false; - } - - if (unlikely(rs->rs_more)) { - sc->stats.rxerr_jumbo++; - return false; - } - return true; -} - -static void -ath5k_tasklet_rx(unsigned long data) -{ - struct ath5k_rx_status rs = {}; - struct sk_buff *skb, *next_skb; - dma_addr_t next_skb_addr; - struct ath5k_softc *sc = (void *)data; - struct ath5k_hw *ah = sc->ah; - struct ath_common *common = ath5k_hw_common(ah); - struct ath5k_buf *bf; - struct ath5k_desc *ds; - int ret; - - spin_lock(&sc->rxbuflock); - if (list_empty(&sc->rxbuf)) { - ATH5K_WARN(sc, "empty rx buf pool\n"); - goto unlock; - } - do { - bf = list_first_entry(&sc->rxbuf, struct ath5k_buf, list); - BUG_ON(bf->skb == NULL); - skb = bf->skb; - ds = bf->desc; - - /* bail if HW is still using self-linked descriptor */ - if (ath5k_hw_get_rxdp(sc->ah) == bf->daddr) - break; - - ret = sc->ah->ah_proc_rx_desc(sc->ah, ds, &rs); - if (unlikely(ret == -EINPROGRESS)) - break; - else if (unlikely(ret)) { - ATH5K_ERR(sc, "error in processing rx descriptor\n"); - sc->stats.rxerr_proc++; - break; - } - - if (ath5k_receive_frame_ok(sc, &rs)) { - next_skb = ath5k_rx_skb_alloc(sc, &next_skb_addr); - - /* - * If we can't replace bf->skb with a new skb under - * memory pressure, just skip this packet - */ - if (!next_skb) - goto next; - - pci_unmap_single(sc->pdev, bf->skbaddr, - common->rx_bufsize, - PCI_DMA_FROMDEVICE); - - skb_put(skb, rs.rs_datalen); - - ath5k_receive_frame(sc, skb, &rs); - - bf->skb = next_skb; - bf->skbaddr = next_skb_addr; - } -next: - list_move_tail(&bf->list, &sc->rxbuf); - } while (ath5k_rxbuf_setup(sc, bf) == 0); -unlock: - spin_unlock(&sc->rxbuflock); -} - - -/*************\ -* TX Handling * -\*************/ - -static void -ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq) -{ - struct ath5k_tx_status ts = {}; - struct ath5k_buf *bf, *bf0; - struct ath5k_desc *ds; - struct sk_buff *skb; - struct ieee80211_tx_info *info; - int i, ret; - - spin_lock(&txq->lock); - list_for_each_entry_safe(bf, bf0, &txq->q, list) { - ds = bf->desc; - - /* - * It's possible that the hardware can say the buffer is - * completed when it hasn't yet loaded the ds_link from - * host memory and moved on. If there are more TX - * descriptors in the queue, wait for TXDP to change - * before processing this one. - */ - if (ath5k_hw_get_txdp(sc->ah, txq->qnum) == bf->daddr && - !list_is_last(&bf->list, &txq->q)) - break; - - ret = sc->ah->ah_proc_tx_desc(sc->ah, ds, &ts); - if (unlikely(ret == -EINPROGRESS)) - break; - else if (unlikely(ret)) { - ATH5K_ERR(sc, "error %d while processing queue %u\n", - ret, txq->qnum); - break; - } - - sc->stats.tx_all_count++; - skb = bf->skb; - info = IEEE80211_SKB_CB(skb); - bf->skb = NULL; + sc->stats.tx_all_count++; + skb = bf->skb; + info = IEEE80211_SKB_CB(skb); + bf->skb = NULL; pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, PCI_DMA_TODEVICE); @@ -2308,6 +1702,43 @@ err_unmap: return ret; } +/* + * Updates the beacon that is sent by ath5k_beacon_send. For adhoc, + * this is called only once at config_bss time, for AP we do it every + * SWBA interrupt so that the TIM will reflect buffered frames. + * + * Called with the beacon lock. + */ +static int +ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + int ret; + struct ath5k_softc *sc = hw->priv; + struct sk_buff *skb; + + if (WARN_ON(!vif)) { + ret = -EINVAL; + goto out; + } + + skb = ieee80211_beacon_get(hw, vif); + + if (!skb) { + ret = -ENOMEM; + goto out; + } + + ath5k_debug_dump_skb(sc, skb, "BC ", 1); + + ath5k_txbuf_free_skb(sc, sc->bbuf); + sc->bbuf->skb = skb; + ret = ath5k_beacon_setup(sc, sc->bbuf); + if (ret) + sc->bbuf->skb = NULL; +out: + return ret; +} + /* * Transmit a beacon frame at SWBA. Dynamic updates to the * frame contents are done as needed and the slot time is @@ -2385,7 +1816,6 @@ ath5k_beacon_send(struct ath5k_softc *sc) sc->bsent++; } - /** * ath5k_beacon_update_timers - update beacon timers * @@ -2487,7 +1917,6 @@ ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf) intval & AR5K_BEACON_RESET_TSF ? "AR5K_BEACON_RESET_TSF" : ""); } - /** * ath5k_beacon_config - Configure the beacon queues and interrupts * @@ -2566,156 +1995,6 @@ static void ath5k_tasklet_beacon(unsigned long data) * Interrupt handling * \********************/ -static int -ath5k_init(struct ath5k_softc *sc) -{ - struct ath5k_hw *ah = sc->ah; - struct ath_common *common = ath5k_hw_common(ah); - int ret, i; - - mutex_lock(&sc->lock); - - ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "mode %d\n", sc->opmode); - - /* - * Stop anything previously setup. This is safe - * no matter this is the first time through or not. - */ - ath5k_stop_locked(sc); - - /* - * The basic interface to setting the hardware in a good - * state is ``reset''. On return the hardware is known to - * be powered up and with interrupts disabled. This must - * be followed by initialization of the appropriate bits - * and then setup of the interrupt mask. - */ - sc->curchan = sc->hw->conf.channel; - sc->curband = &sc->sbands[sc->curchan->band]; - sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL | - AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL | - AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_MIB; - - ret = ath5k_reset(sc, NULL); - if (ret) - goto done; - - ath5k_rfkill_hw_start(ah); - - /* - * Reset the key cache since some parts do not reset the - * contents on initial power up or resume from suspend. - */ - for (i = 0; i < common->keymax; i++) - ath_hw_keyreset(common, (u16)i); - - ath5k_hw_set_ack_bitrate_high(ah, true); - ret = 0; -done: - mmiowb(); - mutex_unlock(&sc->lock); - return ret; -} - -static int -ath5k_stop_locked(struct ath5k_softc *sc) -{ - struct ath5k_hw *ah = sc->ah; - - ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "invalid %u\n", - test_bit(ATH_STAT_INVALID, sc->status)); - - /* - * Shutdown the hardware and driver: - * stop output from above - * disable interrupts - * turn off timers - * turn off the radio - * clear transmit machinery - * clear receive machinery - * drain and release tx queues - * reclaim beacon resources - * power down hardware - * - * Note that some of this work is not possible if the - * hardware is gone (invalid). - */ - ieee80211_stop_queues(sc->hw); - - if (!test_bit(ATH_STAT_INVALID, sc->status)) { - ath5k_led_off(sc); - ath5k_hw_set_imr(ah, 0); - synchronize_irq(sc->pdev->irq); - } - ath5k_txq_cleanup(sc); - if (!test_bit(ATH_STAT_INVALID, sc->status)) { - ath5k_rx_stop(sc); - ath5k_hw_phy_disable(ah); - } - - return 0; -} - -static void stop_tasklets(struct ath5k_softc *sc) -{ - tasklet_kill(&sc->rxtq); - tasklet_kill(&sc->txtq); - tasklet_kill(&sc->calib); - tasklet_kill(&sc->beacontq); - tasklet_kill(&sc->ani_tasklet); -} - -/* - * Stop the device, grabbing the top-level lock to protect - * against concurrent entry through ath5k_init (which can happen - * if another thread does a system call and the thread doing the - * stop is preempted). - */ -static int -ath5k_stop_hw(struct ath5k_softc *sc) -{ - int ret; - - mutex_lock(&sc->lock); - ret = ath5k_stop_locked(sc); - if (ret == 0 && !test_bit(ATH_STAT_INVALID, sc->status)) { - /* - * Don't set the card in full sleep mode! - * - * a) When the device is in this state it must be carefully - * woken up or references to registers in the PCI clock - * domain may freeze the bus (and system). This varies - * by chip and is mostly an issue with newer parts - * (madwifi sources mentioned srev >= 0x78) that go to - * sleep more quickly. - * - * b) On older chips full sleep results a weird behaviour - * during wakeup. I tested various cards with srev < 0x78 - * and they don't wake up after module reload, a second - * module reload is needed to bring the card up again. - * - * Until we figure out what's going on don't enable - * full chip reset on any chip (this is what Legacy HAL - * and Sam's HAL do anyway). Instead Perform a full reset - * on the device (same as initial state after attach) and - * leave it idle (keep MAC/BB on warm reset) */ - ret = ath5k_hw_on_hold(sc->ah); - - ATH5K_DBG(sc, ATH5K_DEBUG_RESET, - "putting device to sleep\n"); - } - ath5k_txbuf_free_skb(sc, sc->bbuf); - - mmiowb(); - mutex_unlock(&sc->lock); - - stop_tasklets(sc); - - ath5k_rfkill_hw_stop(sc->ah); - - return ret; -} - static void ath5k_intr_calibration_poll(struct ath5k_hw *ah) { @@ -2878,68 +2157,158 @@ ath5k_tasklet_ani(unsigned long data) } -/********************\ -* Mac80211 functions * -\********************/ +/*************************\ +* Initialization routines * +\*************************/ static int -ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +ath5k_stop_locked(struct ath5k_softc *sc) { - struct ath5k_softc *sc = hw->priv; + struct ath5k_hw *ah = sc->ah; - return ath5k_tx_queue(hw, skb, sc->txq); + ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "invalid %u\n", + test_bit(ATH_STAT_INVALID, sc->status)); + + /* + * Shutdown the hardware and driver: + * stop output from above + * disable interrupts + * turn off timers + * turn off the radio + * clear transmit machinery + * clear receive machinery + * drain and release tx queues + * reclaim beacon resources + * power down hardware + * + * Note that some of this work is not possible if the + * hardware is gone (invalid). + */ + ieee80211_stop_queues(sc->hw); + + if (!test_bit(ATH_STAT_INVALID, sc->status)) { + ath5k_led_off(sc); + ath5k_hw_set_imr(ah, 0); + synchronize_irq(sc->pdev->irq); + } + ath5k_txq_cleanup(sc); + if (!test_bit(ATH_STAT_INVALID, sc->status)) { + ath5k_rx_stop(sc); + ath5k_hw_phy_disable(ah); + } + + return 0; } -static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ath5k_txq *txq) +static int +ath5k_init(struct ath5k_softc *sc) { - struct ath5k_softc *sc = hw->priv; - struct ath5k_buf *bf; - unsigned long flags; - int padsize; + struct ath5k_hw *ah = sc->ah; + struct ath_common *common = ath5k_hw_common(ah); + int ret, i; - ath5k_debug_dump_skb(sc, skb, "TX ", 1); + mutex_lock(&sc->lock); + + ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "mode %d\n", sc->opmode); /* - * The hardware expects the header padded to 4 byte boundaries. - * If this is not the case, we add the padding after the header. + * Stop anything previously setup. This is safe + * no matter this is the first time through or not. */ - padsize = ath5k_add_padding(skb); - if (padsize < 0) { - ATH5K_ERR(sc, "tx hdrlen not %%4: not enough" - " headroom to pad"); - goto drop_packet; - } + ath5k_stop_locked(sc); - spin_lock_irqsave(&sc->txbuflock, flags); - if (list_empty(&sc->txbuf)) { - ATH5K_ERR(sc, "no further txbuf available, dropping packet\n"); - spin_unlock_irqrestore(&sc->txbuflock, flags); - ieee80211_stop_queue(hw, skb_get_queue_mapping(skb)); - goto drop_packet; - } - bf = list_first_entry(&sc->txbuf, struct ath5k_buf, list); - list_del(&bf->list); - sc->txbuf_len--; - if (list_empty(&sc->txbuf)) - ieee80211_stop_queues(hw); - spin_unlock_irqrestore(&sc->txbuflock, flags); + /* + * The basic interface to setting the hardware in a good + * state is ``reset''. On return the hardware is known to + * be powered up and with interrupts disabled. This must + * be followed by initialization of the appropriate bits + * and then setup of the interrupt mask. + */ + sc->curchan = sc->hw->conf.channel; + sc->curband = &sc->sbands[sc->curchan->band]; + sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL | + AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL | + AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_MIB; - bf->skb = skb; + ret = ath5k_reset(sc, NULL); + if (ret) + goto done; - if (ath5k_txbuf_setup(sc, bf, txq, padsize)) { - bf->skb = NULL; - spin_lock_irqsave(&sc->txbuflock, flags); - list_add_tail(&bf->list, &sc->txbuf); - sc->txbuf_len++; - spin_unlock_irqrestore(&sc->txbuflock, flags); - goto drop_packet; + ath5k_rfkill_hw_start(ah); + + /* + * Reset the key cache since some parts do not reset the + * contents on initial power up or resume from suspend. + */ + for (i = 0; i < common->keymax; i++) + ath_hw_keyreset(common, (u16) i); + + ath5k_hw_set_ack_bitrate_high(ah, true); + ret = 0; +done: + mmiowb(); + mutex_unlock(&sc->lock); + return ret; +} + +static void stop_tasklets(struct ath5k_softc *sc) +{ + tasklet_kill(&sc->rxtq); + tasklet_kill(&sc->txtq); + tasklet_kill(&sc->calib); + tasklet_kill(&sc->beacontq); + tasklet_kill(&sc->ani_tasklet); +} + +/* + * Stop the device, grabbing the top-level lock to protect + * against concurrent entry through ath5k_init (which can happen + * if another thread does a system call and the thread doing the + * stop is preempted). + */ +static int +ath5k_stop_hw(struct ath5k_softc *sc) +{ + int ret; + + mutex_lock(&sc->lock); + ret = ath5k_stop_locked(sc); + if (ret == 0 && !test_bit(ATH_STAT_INVALID, sc->status)) { + /* + * Don't set the card in full sleep mode! + * + * a) When the device is in this state it must be carefully + * woken up or references to registers in the PCI clock + * domain may freeze the bus (and system). This varies + * by chip and is mostly an issue with newer parts + * (madwifi sources mentioned srev >= 0x78) that go to + * sleep more quickly. + * + * b) On older chips full sleep results a weird behaviour + * during wakeup. I tested various cards with srev < 0x78 + * and they don't wake up after module reload, a second + * module reload is needed to bring the card up again. + * + * Until we figure out what's going on don't enable + * full chip reset on any chip (this is what Legacy HAL + * and Sam's HAL do anyway). Instead Perform a full reset + * on the device (same as initial state after attach) and + * leave it idle (keep MAC/BB on warm reset) */ + ret = ath5k_hw_on_hold(sc->ah); + + ATH5K_DBG(sc, ATH5K_DEBUG_RESET, + "putting device to sleep\n"); } - return NETDEV_TX_OK; + ath5k_txbuf_free_skb(sc, sc->bbuf); -drop_packet: - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; + mmiowb(); + mutex_unlock(&sc->lock); + + stop_tasklets(sc); + + ath5k_rfkill_hw_stop(sc->ah); + + return ret; } /* @@ -3016,91 +2385,264 @@ static void ath5k_reset_work(struct work_struct *work) mutex_unlock(&sc->lock); } -static int ath5k_start(struct ieee80211_hw *hw) -{ - return ath5k_init(hw->priv); -} - -static void ath5k_stop(struct ieee80211_hw *hw) -{ - ath5k_stop_hw(hw->priv); -} - -static int ath5k_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) +static int +ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) { struct ath5k_softc *sc = hw->priv; + struct ath5k_hw *ah = sc->ah; + struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah); + u8 mac[ETH_ALEN] = {}; int ret; - mutex_lock(&sc->lock); - if (sc->vif) { - ret = 0; - goto end; - } - - sc->vif = vif; + ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "devid 0x%x\n", pdev->device); - switch (vif->type) { - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_MESH_POINT: - sc->opmode = vif->type; - break; - default: - ret = -EOPNOTSUPP; - goto end; - } + /* + * Check if the MAC has multi-rate retry support. + * We do this by trying to setup a fake extended + * descriptor. MACs that don't have support will + * return false w/o doing anything. MACs that do + * support it will return true w/o doing anything. + */ + ret = ath5k_hw_setup_mrr_tx_desc(ah, NULL, 0, 0, 0, 0, 0, 0); - ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "add interface mode %d\n", sc->opmode); + if (ret < 0) + goto err; + if (ret > 0) + __set_bit(ATH_STAT_MRRETRY, sc->status); - ath5k_hw_set_lladdr(sc->ah, vif->addr); - ath5k_mode_setup(sc); + /* + * Collect the channel list. The 802.11 layer + * is resposible for filtering this list based + * on settings like the phy mode and regulatory + * domain restrictions. + */ + ret = ath5k_setup_bands(hw); + if (ret) { + ATH5K_ERR(sc, "can't get channels\n"); + goto err; + } - ret = 0; -end: - mutex_unlock(&sc->lock); - return ret; -} + /* NB: setup here so ath5k_rate_update is happy */ + if (test_bit(AR5K_MODE_11A, ah->ah_modes)) + ath5k_setcurmode(sc, AR5K_MODE_11A); + else + ath5k_setcurmode(sc, AR5K_MODE_11B); -static void -ath5k_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct ath5k_softc *sc = hw->priv; - u8 mac[ETH_ALEN] = {}; + /* + * Allocate tx+rx descriptors and populate the lists. + */ + ret = ath5k_desc_alloc(sc, pdev); + if (ret) { + ATH5K_ERR(sc, "can't allocate descriptors\n"); + goto err; + } - mutex_lock(&sc->lock); - if (sc->vif != vif) - goto end; + /* + * Allocate hardware transmit queues: one queue for + * beacon frames and one data queue for each QoS + * priority. Note that hw functions handle resetting + * these queues at the needed time. + */ + ret = ath5k_beaconq_setup(ah); + if (ret < 0) { + ATH5K_ERR(sc, "can't setup a beacon xmit queue\n"); + goto err_desc; + } + sc->bhalq = ret; + sc->cabq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_CAB, 0); + if (IS_ERR(sc->cabq)) { + ATH5K_ERR(sc, "can't setup cab queue\n"); + ret = PTR_ERR(sc->cabq); + goto err_bhal; + } - ath5k_hw_set_lladdr(sc->ah, mac); - sc->vif = NULL; -end: - mutex_unlock(&sc->lock); -} + sc->txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BK); + if (IS_ERR(sc->txq)) { + ATH5K_ERR(sc, "can't setup xmit queue\n"); + ret = PTR_ERR(sc->txq); + goto err_queues; + } -/* - * TODO: Phy disable/diversity etc - */ -static int -ath5k_config(struct ieee80211_hw *hw, u32 changed) -{ - struct ath5k_softc *sc = hw->priv; - struct ath5k_hw *ah = sc->ah; - struct ieee80211_conf *conf = &hw->conf; - int ret = 0; + tasklet_init(&sc->rxtq, ath5k_tasklet_rx, (unsigned long)sc); + tasklet_init(&sc->txtq, ath5k_tasklet_tx, (unsigned long)sc); + tasklet_init(&sc->calib, ath5k_tasklet_calibrate, (unsigned long)sc); + tasklet_init(&sc->beacontq, ath5k_tasklet_beacon, (unsigned long)sc); + tasklet_init(&sc->ani_tasklet, ath5k_tasklet_ani, (unsigned long)sc); - mutex_lock(&sc->lock); + INIT_WORK(&sc->reset_work, ath5k_reset_work); - if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { - ret = ath5k_chan_set(sc, conf->channel); - if (ret < 0) - goto unlock; + ret = ath5k_eeprom_read_mac(ah, mac); + if (ret) { + ATH5K_ERR(sc, "unable to read address from EEPROM: 0x%04x\n", + sc->pdev->device); + goto err_queues; } - if ((changed & IEEE80211_CONF_CHANGE_POWER) && - (sc->power_level != conf->power_level)) { + SET_IEEE80211_PERM_ADDR(hw, mac); + /* All MAC address bits matter for ACKs */ + memcpy(sc->bssidmask, ath_bcast_mac, ETH_ALEN); + ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask); + + regulatory->current_rd = ah->ah_capabilities.cap_eeprom.ee_regdomain; + ret = ath_regd_init(regulatory, hw->wiphy, ath5k_reg_notifier); + if (ret) { + ATH5K_ERR(sc, "can't initialize regulatory system\n"); + goto err_queues; + } + + ret = ieee80211_register_hw(hw); + if (ret) { + ATH5K_ERR(sc, "can't register ieee80211 hw\n"); + goto err_queues; + } + + if (!ath_is_world_regd(regulatory)) + regulatory_hint(hw->wiphy, regulatory->alpha2); + + ath5k_init_leds(sc); + + ath5k_sysfs_register(sc); + + return 0; +err_queues: + ath5k_txq_release(sc); +err_bhal: + ath5k_hw_release_tx_queue(ah, sc->bhalq); +err_desc: + ath5k_desc_free(sc, pdev); +err: + return ret; +} + +static void +ath5k_detach(struct pci_dev *pdev, struct ieee80211_hw *hw) +{ + struct ath5k_softc *sc = hw->priv; + + /* + * NB: the order of these is important: + * o call the 802.11 layer before detaching ath5k_hw to + * ensure callbacks into the driver to delete global + * key cache entries can be handled + * o reclaim the tx queue data structures after calling + * the 802.11 layer as we'll get called back to reclaim + * node state and potentially want to use them + * o to cleanup the tx queues the hal is called, so detach + * it last + * XXX: ??? detach ath5k_hw ??? + * Other than that, it's straightforward... + */ + ieee80211_unregister_hw(hw); + ath5k_desc_free(sc, pdev); + ath5k_txq_release(sc); + ath5k_hw_release_tx_queue(sc->ah, sc->bhalq); + ath5k_unregister_leds(sc); + + ath5k_sysfs_unregister(sc); + /* + * NB: can't reclaim these until after ieee80211_ifdetach + * returns because we'll get called back to reclaim node + * state and potentially want to use them. + */ +} + +/********************\ +* Mac80211 functions * +\********************/ + +static int +ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct ath5k_softc *sc = hw->priv; + + return ath5k_tx_queue(hw, skb, sc->txq); +} + +static int ath5k_start(struct ieee80211_hw *hw) +{ + return ath5k_init(hw->priv); +} + +static void ath5k_stop(struct ieee80211_hw *hw) +{ + ath5k_stop_hw(hw->priv); +} + +static int ath5k_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath5k_softc *sc = hw->priv; + int ret; + + mutex_lock(&sc->lock); + if (sc->vif) { + ret = 0; + goto end; + } + + sc->vif = vif; + + switch (vif->type) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + sc->opmode = vif->type; + break; + default: + ret = -EOPNOTSUPP; + goto end; + } + + ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "add interface mode %d\n", sc->opmode); + + ath5k_hw_set_lladdr(sc->ah, vif->addr); + ath5k_mode_setup(sc); + + ret = 0; +end: + mutex_unlock(&sc->lock); + return ret; +} + +static void +ath5k_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath5k_softc *sc = hw->priv; + u8 mac[ETH_ALEN] = {}; + + mutex_lock(&sc->lock); + if (sc->vif != vif) + goto end; + + ath5k_hw_set_lladdr(sc->ah, mac); + sc->vif = NULL; +end: + mutex_unlock(&sc->lock); +} + +/* + * TODO: Phy disable/diversity etc + */ +static int +ath5k_config(struct ieee80211_hw *hw, u32 changed) +{ + struct ath5k_softc *sc = hw->priv; + struct ath5k_hw *ah = sc->ah; + struct ieee80211_conf *conf = &hw->conf; + int ret = 0; + + mutex_lock(&sc->lock); + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + ret = ath5k_chan_set(sc, conf->channel); + if (ret < 0) + goto unlock; + } + + if ((changed & IEEE80211_CONF_CHANGE_POWER) && + (sc->power_level != conf->power_level)) { sc->power_level = conf->power_level; /* Half dB steps */ @@ -3325,214 +2867,538 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ret = -EINVAL; } - mmiowb(); - mutex_unlock(&sc->lock); - return ret; -} - -static int -ath5k_get_stats(struct ieee80211_hw *hw, - struct ieee80211_low_level_stats *stats) -{ - struct ath5k_softc *sc = hw->priv; - - /* Force update */ - ath5k_hw_update_mib_counters(sc->ah); - - stats->dot11ACKFailureCount = sc->stats.ack_fail; - stats->dot11RTSFailureCount = sc->stats.rts_fail; - stats->dot11RTSSuccessCount = sc->stats.rts_ok; - stats->dot11FCSErrorCount = sc->stats.fcs_error; - - return 0; -} - -static int ath5k_get_survey(struct ieee80211_hw *hw, int idx, - struct survey_info *survey) -{ - struct ath5k_softc *sc = hw->priv; - struct ieee80211_conf *conf = &hw->conf; - - if (idx != 0) - return -ENOENT; + mmiowb(); + mutex_unlock(&sc->lock); + return ret; +} + +static int +ath5k_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + struct ath5k_softc *sc = hw->priv; + + /* Force update */ + ath5k_hw_update_mib_counters(sc->ah); + + stats->dot11ACKFailureCount = sc->stats.ack_fail; + stats->dot11RTSFailureCount = sc->stats.rts_fail; + stats->dot11RTSSuccessCount = sc->stats.rts_ok; + stats->dot11FCSErrorCount = sc->stats.fcs_error; + + return 0; +} + +static int ath5k_get_survey(struct ieee80211_hw *hw, int idx, + struct survey_info *survey) +{ + struct ath5k_softc *sc = hw->priv; + struct ieee80211_conf *conf = &hw->conf; + + if (idx != 0) + return -ENOENT; + + survey->channel = conf->channel; + survey->filled = SURVEY_INFO_NOISE_DBM; + survey->noise = sc->ah->ah_noise_floor; + + return 0; +} + +static u64 +ath5k_get_tsf(struct ieee80211_hw *hw) +{ + struct ath5k_softc *sc = hw->priv; + + return ath5k_hw_get_tsf64(sc->ah); +} + +static void +ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf) +{ + struct ath5k_softc *sc = hw->priv; + + ath5k_hw_set_tsf64(sc->ah, tsf); +} + +static void +ath5k_reset_tsf(struct ieee80211_hw *hw) +{ + struct ath5k_softc *sc = hw->priv; + + /* + * in IBSS mode we need to update the beacon timers too. + * this will also reset the TSF if we call it with 0 + */ + if (sc->opmode == NL80211_IFTYPE_ADHOC) + ath5k_beacon_update_timers(sc, 0); + else + ath5k_hw_reset_tsf(sc->ah); +} + +static void +set_beacon_filter(struct ieee80211_hw *hw, bool enable) +{ + struct ath5k_softc *sc = hw->priv; + struct ath5k_hw *ah = sc->ah; + u32 rfilt; + rfilt = ath5k_hw_get_rx_filter(ah); + if (enable) + rfilt |= AR5K_RX_FILTER_BEACON; + else + rfilt &= ~AR5K_RX_FILTER_BEACON; + ath5k_hw_set_rx_filter(ah, rfilt); + sc->filter_flags = rfilt; +} + +static void ath5k_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changes) +{ + struct ath5k_softc *sc = hw->priv; + struct ath5k_hw *ah = sc->ah; + struct ath_common *common = ath5k_hw_common(ah); + unsigned long flags; + + mutex_lock(&sc->lock); + if (WARN_ON(sc->vif != vif)) + goto unlock; + + if (changes & BSS_CHANGED_BSSID) { + /* Cache for later use during resets */ + memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); + common->curaid = 0; + ath5k_hw_set_bssid(ah); + mmiowb(); + } + + if (changes & BSS_CHANGED_BEACON_INT) + sc->bintval = bss_conf->beacon_int; + + if (changes & BSS_CHANGED_ASSOC) { + sc->assoc = bss_conf->assoc; + if (sc->opmode == NL80211_IFTYPE_STATION) + set_beacon_filter(hw, sc->assoc); + ath5k_hw_set_ledstate(sc->ah, sc->assoc ? + AR5K_LED_ASSOC : AR5K_LED_INIT); + if (bss_conf->assoc) { + ATH5K_DBG(sc, ATH5K_DEBUG_ANY, + "Bss Info ASSOC %d, bssid: %pM\n", + bss_conf->aid, common->curbssid); + common->curaid = bss_conf->aid; + ath5k_hw_set_bssid(ah); + /* Once ANI is available you would start it here */ + } + } + + if (changes & BSS_CHANGED_BEACON) { + spin_lock_irqsave(&sc->block, flags); + ath5k_beacon_update(hw, vif); + spin_unlock_irqrestore(&sc->block, flags); + } + + if (changes & BSS_CHANGED_BEACON_ENABLED) + sc->enable_beacon = bss_conf->enable_beacon; + + if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED | + BSS_CHANGED_BEACON_INT)) + ath5k_beacon_config(sc); + + unlock: + mutex_unlock(&sc->lock); +} + +static void ath5k_sw_scan_start(struct ieee80211_hw *hw) +{ + struct ath5k_softc *sc = hw->priv; + if (!sc->assoc) + ath5k_hw_set_ledstate(sc->ah, AR5K_LED_SCAN); +} + +static void ath5k_sw_scan_complete(struct ieee80211_hw *hw) +{ + struct ath5k_softc *sc = hw->priv; + ath5k_hw_set_ledstate(sc->ah, sc->assoc ? + AR5K_LED_ASSOC : AR5K_LED_INIT); +} + +/** + * ath5k_set_coverage_class - Set IEEE 802.11 coverage class + * + * @hw: struct ieee80211_hw pointer + * @coverage_class: IEEE 802.11 coverage class number + * + * Mac80211 callback. Sets slot time, ACK timeout and CTS timeout for given + * coverage class. The values are persistent, they are restored after device + * reset. + */ +static void ath5k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class) +{ + struct ath5k_softc *sc = hw->priv; + + mutex_lock(&sc->lock); + ath5k_hw_set_coverage_class(sc->ah, coverage_class); + mutex_unlock(&sc->lock); +} + +static const struct ieee80211_ops ath5k_hw_ops = { + .tx = ath5k_tx, + .start = ath5k_start, + .stop = ath5k_stop, + .add_interface = ath5k_add_interface, + .remove_interface = ath5k_remove_interface, + .config = ath5k_config, + .prepare_multicast = ath5k_prepare_multicast, + .configure_filter = ath5k_configure_filter, + .set_key = ath5k_set_key, + .get_stats = ath5k_get_stats, + .get_survey = ath5k_get_survey, + .conf_tx = NULL, + .get_tsf = ath5k_get_tsf, + .set_tsf = ath5k_set_tsf, + .reset_tsf = ath5k_reset_tsf, + .bss_info_changed = ath5k_bss_info_changed, + .sw_scan_start = ath5k_sw_scan_start, + .sw_scan_complete = ath5k_sw_scan_complete, + .set_coverage_class = ath5k_set_coverage_class, +}; + +/********************\ +* PCI Initialization * +\********************/ + +static int __devinit +ath5k_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + void __iomem *mem; + struct ath5k_softc *sc; + struct ath_common *common; + struct ieee80211_hw *hw; + int ret; + u8 csz; + + /* + * L0s needs to be disabled on all ath5k cards. + * + * For distributions shipping with CONFIG_PCIEASPM (this will be enabled + * by default in the future in 2.6.36) this will also mean both L1 and + * L0s will be disabled when a pre 1.1 PCIe device is detected. We do + * know L1 works correctly even for all ath5k pre 1.1 PCIe devices + * though but cannot currently undue the effect of a blacklist, for + * details you can read pcie_aspm_sanity_check() and see how it adjusts + * the device link capability. + * + * It may be possible in the future to implement some PCI API to allow + * drivers to override blacklists for pre 1.1 PCIe but for now it is + * best to accept that both L0s and L1 will be disabled completely for + * distributions shipping with CONFIG_PCIEASPM rather than having this + * issue present. Motivation for adding this new API will be to help + * with power consumption for some of these devices. + */ + pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S); + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "can't enable device\n"); + goto err; + } + + /* XXX 32-bit addressing only */ + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "32-bit DMA not available\n"); + goto err_dis; + } + + /* + * Cache line size is used to size and align various + * structures used to communicate with the hardware. + */ + pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz); + if (csz == 0) { + /* + * Linux 2.4.18 (at least) writes the cache line size + * register as a 16-bit wide register which is wrong. + * We must have this setup properly for rx buffer + * DMA to work so force a reasonable value here if it + * comes up zero. + */ + csz = L1_CACHE_BYTES >> 2; + pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz); + } + /* + * The default setting of latency timer yields poor results, + * set it to the value used by other systems. It may be worth + * tweaking this setting more. + */ + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8); + + /* Enable bus mastering */ + pci_set_master(pdev); + + /* + * Disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state. + */ + pci_write_config_byte(pdev, 0x41, 0); + + ret = pci_request_region(pdev, 0, "ath5k"); + if (ret) { + dev_err(&pdev->dev, "cannot reserve PCI memory region\n"); + goto err_dis; + } + + mem = pci_iomap(pdev, 0, 0); + if (!mem) { + dev_err(&pdev->dev, "cannot remap PCI memory region\n") ; + ret = -EIO; + goto err_reg; + } + + /* + * Allocate hw (mac80211 main struct) + * and hw->priv (driver private data) + */ + hw = ieee80211_alloc_hw(sizeof(*sc), &ath5k_hw_ops); + if (hw == NULL) { + dev_err(&pdev->dev, "cannot allocate ieee80211_hw\n"); + ret = -ENOMEM; + goto err_map; + } + + dev_info(&pdev->dev, "registered as '%s'\n", wiphy_name(hw->wiphy)); + + /* Initialize driver private data */ + SET_IEEE80211_DEV(hw, &pdev->dev); + hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | + IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | + IEEE80211_HW_SIGNAL_DBM; + + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_MESH_POINT); + + hw->extra_tx_headroom = 2; + hw->channel_change_time = 5000; + sc = hw->priv; + sc->hw = hw; + sc->pdev = pdev; + + ath5k_debug_init_device(sc); + + /* + * Mark the device as detached to avoid processing + * interrupts until setup is complete. + */ + __set_bit(ATH_STAT_INVALID, sc->status); + + sc->iobase = mem; /* So we can unmap it on detach */ + sc->opmode = NL80211_IFTYPE_STATION; + sc->bintval = 1000; + mutex_init(&sc->lock); + spin_lock_init(&sc->rxbuflock); + spin_lock_init(&sc->txbuflock); + spin_lock_init(&sc->block); + + /* Set private data */ + pci_set_drvdata(pdev, sc); + + /* Setup interrupt handler */ + ret = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc); + if (ret) { + ATH5K_ERR(sc, "request_irq failed\n"); + goto err_free; + } + + /* If we passed the test, malloc an ath5k_hw struct */ + sc->ah = kzalloc(sizeof(struct ath5k_hw), GFP_KERNEL); + if (!sc->ah) { + ret = -ENOMEM; + ATH5K_ERR(sc, "out of memory\n"); + goto err_irq; + } + + sc->ah->ah_sc = sc; + sc->ah->ah_iobase = sc->iobase; + common = ath5k_hw_common(sc->ah); + common->ops = &ath5k_common_ops; + common->ah = sc->ah; + common->hw = hw; + common->cachelsz = csz << 2; /* convert to bytes */ + + /* Initialize device */ + ret = ath5k_hw_attach(sc); + if (ret) { + goto err_free_ah; + } + + /* set up multi-rate retry capabilities */ + if (sc->ah->ah_version == AR5K_AR5212) { + hw->max_rates = 4; + hw->max_rate_tries = 11; + } + + /* Finish private driver data initialization */ + ret = ath5k_attach(pdev, hw); + if (ret) + goto err_ah; + + ATH5K_INFO(sc, "Atheros AR%s chip found (MAC: 0x%x, PHY: 0x%x)\n", + ath5k_chip_name(AR5K_VERSION_MAC, sc->ah->ah_mac_srev), + sc->ah->ah_mac_srev, + sc->ah->ah_phy_revision); + + if (!sc->ah->ah_single_chip) { + /* Single chip radio (!RF5111) */ + if (sc->ah->ah_radio_5ghz_revision && + !sc->ah->ah_radio_2ghz_revision) { + /* No 5GHz support -> report 2GHz radio */ + if (!test_bit(AR5K_MODE_11A, + sc->ah->ah_capabilities.cap_mode)) { + ATH5K_INFO(sc, "RF%s 2GHz radio found (0x%x)\n", + ath5k_chip_name(AR5K_VERSION_RAD, + sc->ah->ah_radio_5ghz_revision), + sc->ah->ah_radio_5ghz_revision); + /* No 2GHz support (5110 and some + * 5Ghz only cards) -> report 5Ghz radio */ + } else if (!test_bit(AR5K_MODE_11B, + sc->ah->ah_capabilities.cap_mode)) { + ATH5K_INFO(sc, "RF%s 5GHz radio found (0x%x)\n", + ath5k_chip_name(AR5K_VERSION_RAD, + sc->ah->ah_radio_5ghz_revision), + sc->ah->ah_radio_5ghz_revision); + /* Multiband radio */ + } else { + ATH5K_INFO(sc, "RF%s multiband radio found" + " (0x%x)\n", + ath5k_chip_name(AR5K_VERSION_RAD, + sc->ah->ah_radio_5ghz_revision), + sc->ah->ah_radio_5ghz_revision); + } + } + /* Multi chip radio (RF5111 - RF2111) -> + * report both 2GHz/5GHz radios */ + else if (sc->ah->ah_radio_5ghz_revision && + sc->ah->ah_radio_2ghz_revision){ + ATH5K_INFO(sc, "RF%s 5GHz radio found (0x%x)\n", + ath5k_chip_name(AR5K_VERSION_RAD, + sc->ah->ah_radio_5ghz_revision), + sc->ah->ah_radio_5ghz_revision); + ATH5K_INFO(sc, "RF%s 2GHz radio found (0x%x)\n", + ath5k_chip_name(AR5K_VERSION_RAD, + sc->ah->ah_radio_2ghz_revision), + sc->ah->ah_radio_2ghz_revision); + } + } + - survey->channel = conf->channel; - survey->filled = SURVEY_INFO_NOISE_DBM; - survey->noise = sc->ah->ah_noise_floor; + /* ready to process interrupts */ + __clear_bit(ATH_STAT_INVALID, sc->status); return 0; +err_ah: + ath5k_hw_detach(sc->ah); +err_free_ah: + kfree(sc->ah); +err_irq: + free_irq(pdev->irq, sc); +err_free: + ieee80211_free_hw(hw); +err_map: + pci_iounmap(pdev, mem); +err_reg: + pci_release_region(pdev, 0); +err_dis: + pci_disable_device(pdev); +err: + return ret; } -static u64 -ath5k_get_tsf(struct ieee80211_hw *hw) +static void __devexit +ath5k_pci_remove(struct pci_dev *pdev) { - struct ath5k_softc *sc = hw->priv; + struct ath5k_softc *sc = pci_get_drvdata(pdev); - return ath5k_hw_get_tsf64(sc->ah); + ath5k_debug_finish_device(sc); + ath5k_detach(pdev, sc->hw); + ath5k_hw_detach(sc->ah); + kfree(sc->ah); + free_irq(pdev->irq, sc); + pci_iounmap(pdev, sc->iobase); + pci_release_region(pdev, 0); + pci_disable_device(pdev); + ieee80211_free_hw(sc->hw); } -static void -ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf) +#ifdef CONFIG_PM_SLEEP +static int ath5k_pci_suspend(struct device *dev) { - struct ath5k_softc *sc = hw->priv; + struct ath5k_softc *sc = pci_get_drvdata(to_pci_dev(dev)); - ath5k_hw_set_tsf64(sc->ah, tsf); + ath5k_led_off(sc); + return 0; } -static void -ath5k_reset_tsf(struct ieee80211_hw *hw) +static int ath5k_pci_resume(struct device *dev) { - struct ath5k_softc *sc = hw->priv; + struct pci_dev *pdev = to_pci_dev(dev); + struct ath5k_softc *sc = pci_get_drvdata(pdev); /* - * in IBSS mode we need to update the beacon timers too. - * this will also reset the TSF if we call it with 0 + * Suspend/Resume resets the PCI configuration space, so we have to + * re-disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state */ - if (sc->opmode == NL80211_IFTYPE_ADHOC) - ath5k_beacon_update_timers(sc, 0); - else - ath5k_hw_reset_tsf(sc->ah); + pci_write_config_byte(pdev, 0x41, 0); + + ath5k_led_enable(sc); + return 0; } +static SIMPLE_DEV_PM_OPS(ath5k_pm_ops, ath5k_pci_suspend, ath5k_pci_resume); +#define ATH5K_PM_OPS (&ath5k_pm_ops) +#else +#define ATH5K_PM_OPS NULL +#endif /* CONFIG_PM_SLEEP */ + +static struct pci_driver ath5k_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = ath5k_pci_id_table, + .probe = ath5k_pci_probe, + .remove = __devexit_p(ath5k_pci_remove), + .driver.pm = ATH5K_PM_OPS, +}; + /* - * Updates the beacon that is sent by ath5k_beacon_send. For adhoc, - * this is called only once at config_bss time, for AP we do it every - * SWBA interrupt so that the TIM will reflect buffered frames. - * - * Called with the beacon lock. + * Module init/exit functions */ -static int -ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +static int __init +init_ath5k_pci(void) { int ret; - struct ath5k_softc *sc = hw->priv; - struct sk_buff *skb; - - if (WARN_ON(!vif)) { - ret = -EINVAL; - goto out; - } - - skb = ieee80211_beacon_get(hw, vif); - - if (!skb) { - ret = -ENOMEM; - goto out; - } - - ath5k_debug_dump_skb(sc, skb, "BC ", 1); - - ath5k_txbuf_free_skb(sc, sc->bbuf); - sc->bbuf->skb = skb; - ret = ath5k_beacon_setup(sc, sc->bbuf); - if (ret) - sc->bbuf->skb = NULL; -out: - return ret; -} - -static void -set_beacon_filter(struct ieee80211_hw *hw, bool enable) -{ - struct ath5k_softc *sc = hw->priv; - struct ath5k_hw *ah = sc->ah; - u32 rfilt; - rfilt = ath5k_hw_get_rx_filter(ah); - if (enable) - rfilt |= AR5K_RX_FILTER_BEACON; - else - rfilt &= ~AR5K_RX_FILTER_BEACON; - ath5k_hw_set_rx_filter(ah, rfilt); - sc->filter_flags = rfilt; -} - -static void ath5k_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *bss_conf, - u32 changes) -{ - struct ath5k_softc *sc = hw->priv; - struct ath5k_hw *ah = sc->ah; - struct ath_common *common = ath5k_hw_common(ah); - unsigned long flags; - - mutex_lock(&sc->lock); - if (WARN_ON(sc->vif != vif)) - goto unlock; - - if (changes & BSS_CHANGED_BSSID) { - /* Cache for later use during resets */ - memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); - common->curaid = 0; - ath5k_hw_set_bssid(ah); - mmiowb(); - } - - if (changes & BSS_CHANGED_BEACON_INT) - sc->bintval = bss_conf->beacon_int; - if (changes & BSS_CHANGED_ASSOC) { - sc->assoc = bss_conf->assoc; - if (sc->opmode == NL80211_IFTYPE_STATION) - set_beacon_filter(hw, sc->assoc); - ath5k_hw_set_ledstate(sc->ah, sc->assoc ? - AR5K_LED_ASSOC : AR5K_LED_INIT); - if (bss_conf->assoc) { - ATH5K_DBG(sc, ATH5K_DEBUG_ANY, - "Bss Info ASSOC %d, bssid: %pM\n", - bss_conf->aid, common->curbssid); - common->curaid = bss_conf->aid; - ath5k_hw_set_bssid(ah); - /* Once ANI is available you would start it here */ - } - } + ath5k_debug_init(); - if (changes & BSS_CHANGED_BEACON) { - spin_lock_irqsave(&sc->block, flags); - ath5k_beacon_update(hw, vif); - spin_unlock_irqrestore(&sc->block, flags); + ret = pci_register_driver(&ath5k_pci_driver); + if (ret) { + printk(KERN_ERR "ath5k_pci: can't register pci driver\n"); + return ret; } - if (changes & BSS_CHANGED_BEACON_ENABLED) - sc->enable_beacon = bss_conf->enable_beacon; - - if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED | - BSS_CHANGED_BEACON_INT)) - ath5k_beacon_config(sc); - - unlock: - mutex_unlock(&sc->lock); + return 0; } -static void ath5k_sw_scan_start(struct ieee80211_hw *hw) +static void __exit +exit_ath5k_pci(void) { - struct ath5k_softc *sc = hw->priv; - if (!sc->assoc) - ath5k_hw_set_ledstate(sc->ah, AR5K_LED_SCAN); -} + pci_unregister_driver(&ath5k_pci_driver); -static void ath5k_sw_scan_complete(struct ieee80211_hw *hw) -{ - struct ath5k_softc *sc = hw->priv; - ath5k_hw_set_ledstate(sc->ah, sc->assoc ? - AR5K_LED_ASSOC : AR5K_LED_INIT); + ath5k_debug_finish(); } -/** - * ath5k_set_coverage_class - Set IEEE 802.11 coverage class - * - * @hw: struct ieee80211_hw pointer - * @coverage_class: IEEE 802.11 coverage class number - * - * Mac80211 callback. Sets slot time, ACK timeout and CTS timeout for given - * coverage class. The values are persistent, they are restored after device - * reset. - */ -static void ath5k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class) -{ - struct ath5k_softc *sc = hw->priv; - - mutex_lock(&sc->lock); - ath5k_hw_set_coverage_class(sc->ah, coverage_class); - mutex_unlock(&sc->lock); -} +module_init(init_ath5k_pci); +module_exit(exit_ath5k_pci); -- cgit v1.2.3 From 925e0b061300c94912be36eac16f0b44249a1add Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Fri, 17 Sep 2010 11:36:35 +0900 Subject: ath5k: Use four hardware queues Prepare ath5k for WME by using four hardware queues. The way we set up our queues matches the mac80211 queue priority 1:1, so we don't have to do any mapping for queue numbers. Every queue uses 50 of the total 200 available transmit buffers, so the DMA memory usage does not increase with this patch, but it might be good to fine-tune the number of buffers per queue later (depending on the CPU speed and load, and the speed of the medium access, it might not be big enough). Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 47 ++++++++++++++++++++++++++++++----- drivers/net/wireless/ath/ath5k/base.h | 5 +++- 2 files changed, 45 insertions(+), 7 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 4a07fb89021e..776f415e8567 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -733,6 +733,7 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf, spin_lock_bh(&txq->lock); list_add_tail(&bf->list, &txq->q); + txq->txq_len++; if (txq->link == NULL) /* is this first packet? */ ath5k_hw_set_txdp(ah, txq->qnum, bf->daddr); else /* no, so only link it */ @@ -889,6 +890,7 @@ ath5k_txq_setup(struct ath5k_softc *sc, INIT_LIST_HEAD(&txq->q); spin_lock_init(&txq->lock); txq->setup = true; + txq->txq_len = 0; } return &sc->txqs[qnum]; } @@ -983,6 +985,7 @@ ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq) spin_lock_bh(&sc->txbuflock); list_move_tail(&bf->list, &sc->txbuf); sc->txbuf_len++; + txq->txq_len--; spin_unlock_bh(&sc->txbuflock); } txq->link = NULL; @@ -1479,6 +1482,9 @@ static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, goto drop_packet; } + if (txq->txq_len >= ATH5K_TXQ_LEN_MAX) + ieee80211_stop_queue(hw, txq->qnum); + spin_lock_irqsave(&sc->txbuflock, flags); if (list_empty(&sc->txbuf)) { ATH5K_ERR(sc, "no further txbuf available, dropping packet\n"); @@ -1601,13 +1607,14 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq) spin_lock(&sc->txbuflock); list_move_tail(&bf->list, &sc->txbuf); sc->txbuf_len++; + txq->txq_len--; spin_unlock(&sc->txbuflock); } if (likely(list_empty(&txq->q))) txq->link = NULL; spin_unlock(&txq->lock); - if (sc->txbuf_len > ATH_TXBUF / 5) - ieee80211_wake_queues(sc->hw); + if (txq->txq_len < ATH5K_TXQ_LEN_LOW) + ieee80211_wake_queue(sc->hw, txq->qnum); } static void @@ -2391,6 +2398,7 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) struct ath5k_softc *sc = hw->priv; struct ath5k_hw *ah = sc->ah; struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah); + struct ath5k_txq *txq; u8 mac[ETH_ALEN] = {}; int ret; @@ -2456,12 +2464,33 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) goto err_bhal; } - sc->txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BK); - if (IS_ERR(sc->txq)) { + /* This order matches mac80211's queue priority, so we can + * directly use the mac80211 queue number without any mapping */ + txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_VO); + if (IS_ERR(txq)) { + ATH5K_ERR(sc, "can't setup xmit queue\n"); + ret = PTR_ERR(txq); + goto err_queues; + } + txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_VI); + if (IS_ERR(txq)) { ATH5K_ERR(sc, "can't setup xmit queue\n"); - ret = PTR_ERR(sc->txq); + ret = PTR_ERR(txq); goto err_queues; } + txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BE); + if (IS_ERR(txq)) { + ATH5K_ERR(sc, "can't setup xmit queue\n"); + ret = PTR_ERR(txq); + goto err_queues; + } + txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BK); + if (IS_ERR(txq)) { + ATH5K_ERR(sc, "can't setup xmit queue\n"); + ret = PTR_ERR(txq); + goto err_queues; + } + hw->queues = 4; tasklet_init(&sc->rxtq, ath5k_tasklet_rx, (unsigned long)sc); tasklet_init(&sc->txtq, ath5k_tasklet_tx, (unsigned long)sc); @@ -2554,8 +2583,14 @@ static int ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ath5k_softc *sc = hw->priv; + u16 qnum = skb_get_queue_mapping(skb); + + if (WARN_ON(qnum >= sc->ah->ah_capabilities.cap_queues.q_tx_num)) { + dev_kfree_skb_any(skb); + return 0; + } - return ath5k_tx_queue(hw, skb, sc->txq); + return ath5k_tx_queue(hw, skb, &sc->txqs[qnum]); } static int ath5k_start(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/ath/ath5k/base.h b/drivers/net/wireless/ath/ath5k/base.h index dc1241f9c4e8..5e2366d3db09 100644 --- a/drivers/net/wireless/ath/ath5k/base.h +++ b/drivers/net/wireless/ath/ath5k/base.h @@ -60,6 +60,9 @@ #define ATH_TXBUF 200 /* number of TX buffers */ #define ATH_BCBUF 1 /* number of beacon buffers */ +#define ATH5K_TXQ_LEN_MAX (ATH_TXBUF / 4) /* bufs per queue */ +#define ATH5K_TXQ_LEN_LOW (ATH5K_TXQ_LEN_MAX / 2) /* low mark */ + struct ath5k_buf { struct list_head list; struct ath5k_desc *desc; /* virtual addr of desc */ @@ -83,6 +86,7 @@ struct ath5k_txq { struct list_head q; /* transmit queue */ spinlock_t lock; /* lock on q and link */ bool setup; + int txq_len; /* number of queued buffers */ }; #define ATH5K_LED_MAX_NAME_LEN 31 @@ -204,7 +208,6 @@ struct ath5k_softc { spinlock_t txbuflock; unsigned int txbuf_len; /* buf count in txbuf list */ struct ath5k_txq txqs[AR5K_NUM_TX_QUEUES]; /* tx queues */ - struct ath5k_txq *txq; /* main tx queue */ struct tasklet_struct txtq; /* tx intr tasklet */ struct ath5k_led tx_led; /* tx led */ -- cgit v1.2.3 From cfddc11c429a655e418ffc111372cc69dee6a1a5 Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Fri, 17 Sep 2010 11:36:40 +0900 Subject: ath5k: Fix queue debug file Take txq lock in debug file and fix reporting of used buffers. Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/debug.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c index 1b7c6d7fde93..b2c610ba8b7e 100644 --- a/drivers/net/wireless/ath/ath5k/debug.c +++ b/drivers/net/wireless/ath/ath5k/debug.c @@ -763,7 +763,7 @@ static ssize_t read_file_queue(struct file *file, char __user *user_buf, struct ath5k_txq *txq; struct ath5k_buf *bf, *bf0; - int i, n = 0; + int i, n; len += snprintf(buf+len, sizeof(buf)-len, "available txbuffers: %d\n", sc->txbuf_len); @@ -777,9 +777,14 @@ static ssize_t read_file_queue(struct file *file, char __user *user_buf, if (!txq->setup) continue; + n = 0; + spin_lock_bh(&txq->lock); list_for_each_entry_safe(bf, bf0, &txq->q, list) n++; - len += snprintf(buf+len, sizeof(buf)-len, " len: %d\n", n); + spin_unlock_bh(&txq->lock); + + len += snprintf(buf+len, sizeof(buf)-len, + " len: %d bufs: %d\n", txq->txq_len, n); } if (len > sizeof(buf)) -- cgit v1.2.3 From 651d9375dca9997ef2b05639191756da73b0cf8d Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Fri, 17 Sep 2010 11:36:46 +0900 Subject: ath5k: Fix TX queues stopping It does not make sense to stop queues for NF calibration. This will not stop transmissions from the card, if there are queued packets. If we run out of TX buffers we need to stop all queues, not only one. Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 776f415e8567..92d139bbcc31 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1489,7 +1489,7 @@ static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, if (list_empty(&sc->txbuf)) { ATH5K_ERR(sc, "no further txbuf available, dropping packet\n"); spin_unlock_irqrestore(&sc->txbuflock, flags); - ieee80211_stop_queue(hw, skb_get_queue_mapping(skb)); + ieee80211_stop_queues(hw); goto drop_packet; } bf = list_first_entry(&sc->txbuf, struct ath5k_buf, list); @@ -2138,14 +2138,13 @@ ath5k_tasklet_calibrate(unsigned long data) sc->curchan->center_freq)); /* Noise floor calibration interrupts rx/tx path while I/Q calibration - * doesn't. We stop the queues so that calibration doesn't interfere - * with TX and don't run it as often */ + * doesn't. + * TODO: We should stop TX here, so that it doesn't interfere. + * Note that stopping the queues is not enough to stop TX! */ if (time_is_before_eq_jiffies(ah->ah_cal_next_nf)) { ah->ah_cal_next_nf = jiffies + msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_NF); - ieee80211_stop_queues(sc->hw); ath5k_hw_update_noise_floor(ah); - ieee80211_wake_queues(sc->hw); } ah->ah_cal_mask &= ~AR5K_CALIBRATION_FULL; -- cgit v1.2.3 From 1440401e7051d4cf66084a7c36125834901bb90d Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Fri, 17 Sep 2010 11:36:51 +0900 Subject: ath5k: Move tx frame completion into separate function Clearer separation between queue handling and what we do with completed frames. Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 107 ++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 50 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 92d139bbcc31..81f4b567c6f2 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1516,6 +1516,61 @@ drop_packet: return NETDEV_TX_OK; } +static void +ath5k_tx_frame_completed(struct ath5k_softc *sc, struct sk_buff *skb, + struct ath5k_tx_status *ts) +{ + struct ieee80211_tx_info *info; + int i; + + sc->stats.tx_all_count++; + info = IEEE80211_SKB_CB(skb); + + ieee80211_tx_info_clear_status(info); + for (i = 0; i < 4; i++) { + struct ieee80211_tx_rate *r = + &info->status.rates[i]; + + if (ts->ts_rate[i]) { + r->idx = ath5k_hw_to_driver_rix(sc, ts->ts_rate[i]); + r->count = ts->ts_retry[i]; + } else { + r->idx = -1; + r->count = 0; + } + } + + /* count the successful attempt as well */ + info->status.rates[ts->ts_final_idx].count++; + + if (unlikely(ts->ts_status)) { + sc->stats.ack_fail++; + if (ts->ts_status & AR5K_TXERR_FILT) { + info->flags |= IEEE80211_TX_STAT_TX_FILTERED; + sc->stats.txerr_filt++; + } + if (ts->ts_status & AR5K_TXERR_XRETRY) + sc->stats.txerr_retry++; + if (ts->ts_status & AR5K_TXERR_FIFO) + sc->stats.txerr_fifo++; + } else { + info->flags |= IEEE80211_TX_STAT_ACK; + info->status.ack_signal = ts->ts_rssi; + } + + /* + * Remove MAC header padding before giving the frame + * back to mac80211. + */ + ath5k_remove_padding(skb); + + if (ts->ts_antenna > 0 && ts->ts_antenna < 5) + sc->stats.antenna_tx[ts->ts_antenna]++; + else + sc->stats.antenna_tx[0]++; /* invalid */ + + ieee80211_tx_status(sc->hw, skb); +} static void ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq) @@ -1524,8 +1579,7 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq) struct ath5k_buf *bf, *bf0; struct ath5k_desc *ds; struct sk_buff *skb; - struct ieee80211_tx_info *info; - int i, ret; + int ret; spin_lock(&txq->lock); list_for_each_entry_safe(bf, bf0, &txq->q, list) { @@ -1541,7 +1595,6 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq) if (ath5k_hw_get_txdp(sc->ah, txq->qnum) == bf->daddr && !list_is_last(&bf->list, &txq->q)) break; - ret = sc->ah->ah_proc_tx_desc(sc->ah, ds, &ts); if (unlikely(ret == -EINPROGRESS)) break; @@ -1551,58 +1604,12 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq) break; } - sc->stats.tx_all_count++; skb = bf->skb; - info = IEEE80211_SKB_CB(skb); bf->skb = NULL; - pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, PCI_DMA_TODEVICE); - ieee80211_tx_info_clear_status(info); - for (i = 0; i < 4; i++) { - struct ieee80211_tx_rate *r = - &info->status.rates[i]; - - if (ts.ts_rate[i]) { - r->idx = ath5k_hw_to_driver_rix(sc, ts.ts_rate[i]); - r->count = ts.ts_retry[i]; - } else { - r->idx = -1; - r->count = 0; - } - } - - /* count the successful attempt as well */ - info->status.rates[ts.ts_final_idx].count++; - - if (unlikely(ts.ts_status)) { - sc->stats.ack_fail++; - if (ts.ts_status & AR5K_TXERR_FILT) { - info->flags |= IEEE80211_TX_STAT_TX_FILTERED; - sc->stats.txerr_filt++; - } - if (ts.ts_status & AR5K_TXERR_XRETRY) - sc->stats.txerr_retry++; - if (ts.ts_status & AR5K_TXERR_FIFO) - sc->stats.txerr_fifo++; - } else { - info->flags |= IEEE80211_TX_STAT_ACK; - info->status.ack_signal = ts.ts_rssi; - } - - /* - * Remove MAC header padding before giving the frame - * back to mac80211. - */ - ath5k_remove_padding(skb); - - if (ts.ts_antenna > 0 && ts.ts_antenna < 5) - sc->stats.antenna_tx[ts.ts_antenna]++; - else - sc->stats.antenna_tx[0]++; /* invalid */ - - ieee80211_tx_status(sc->hw, skb); + ath5k_tx_frame_completed(sc, skb, &ts); spin_lock(&sc->txbuflock); list_move_tail(&bf->list, &sc->txbuf); -- cgit v1.2.3 From 4edd761f4075b03be5932682a2f7b9368dc9e536 Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Fri, 17 Sep 2010 11:36:56 +0900 Subject: ath5k: Add watchdog for stuck TX queues Since we do not know any better solution to the problem that TX queues can get stuck, this adds a timer-based watchdog, which will check for stuck queues and reset the hardware if necessary. Ported from ath9k commit 164ace38536849966ffa377b1b1132993a5a375d. Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/ath5k.h | 2 ++ drivers/net/wireless/ath/ath5k/base.c | 51 ++++++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath5k/base.h | 3 ++ 3 files changed, 56 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index 50209aed0ed2..9475b2157f2d 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -206,6 +206,8 @@ #define ATH5K_TUNE_CALIBRATION_INTERVAL_ANI 1000 /* 1 sec */ #define ATH5K_TUNE_CALIBRATION_INTERVAL_NF 60000 /* 60 sec */ +#define ATH5K_TX_COMPLETE_POLL_INT 3000 /* 3 sec */ + #define AR5K_INIT_CARR_SENSE_EN 1 /*Swap RX/TX Descriptor for big endian archs*/ diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 81f4b567c6f2..afedfeba13dd 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -891,6 +891,7 @@ ath5k_txq_setup(struct ath5k_softc *sc, spin_lock_init(&txq->lock); txq->setup = true; txq->txq_len = 0; + txq->txq_poll_mark = false; } return &sc->txqs[qnum]; } @@ -989,6 +990,7 @@ ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq) spin_unlock_bh(&sc->txbuflock); } txq->link = NULL; + txq->txq_poll_mark = false; spin_unlock_bh(&txq->lock); } @@ -1616,6 +1618,8 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq) sc->txbuf_len++; txq->txq_len--; spin_unlock(&sc->txbuflock); + + txq->txq_poll_mark = false; } if (likely(list_empty(&txq->q))) txq->link = NULL; @@ -2170,6 +2174,46 @@ ath5k_tasklet_ani(unsigned long data) } +static void +ath5k_tx_complete_poll_work(struct work_struct *work) +{ + struct ath5k_softc *sc = container_of(work, struct ath5k_softc, + tx_complete_work.work); + struct ath5k_txq *txq; + int i; + bool needreset = false; + + for (i = 0; i < ARRAY_SIZE(sc->txqs); i++) { + if (sc->txqs[i].setup) { + txq = &sc->txqs[i]; + spin_lock_bh(&txq->lock); + if (txq->txq_len > 0) { + if (txq->txq_poll_mark) { + ATH5K_DBG(sc, ATH5K_DEBUG_XMIT, + "TX queue stuck %d\n", + txq->qnum); + needreset = true; + spin_unlock_bh(&txq->lock); + break; + } else { + txq->txq_poll_mark = true; + } + } + spin_unlock_bh(&txq->lock); + } + } + + if (needreset) { + ATH5K_DBG(sc, ATH5K_DEBUG_RESET, + "TX queues stuck, resetting\n"); + ath5k_reset(sc, sc->curchan); + } + + ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, + msecs_to_jiffies(ATH5K_TX_COMPLETE_POLL_INT)); +} + + /*************************\ * Initialization routines * \*************************/ @@ -2261,6 +2305,10 @@ ath5k_init(struct ath5k_softc *sc) done: mmiowb(); mutex_unlock(&sc->lock); + + ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, + msecs_to_jiffies(ATH5K_TX_COMPLETE_POLL_INT)); + return ret; } @@ -2319,6 +2367,8 @@ ath5k_stop_hw(struct ath5k_softc *sc) stop_tasklets(sc); + cancel_delayed_work_sync(&sc->tx_complete_work); + ath5k_rfkill_hw_stop(sc->ah); return ret; @@ -2505,6 +2555,7 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) tasklet_init(&sc->ani_tasklet, ath5k_tasklet_ani, (unsigned long)sc); INIT_WORK(&sc->reset_work, ath5k_reset_work); + INIT_DELAYED_WORK(&sc->tx_complete_work, ath5k_tx_complete_poll_work); ret = ath5k_eeprom_read_mac(ah, mac); if (ret) { diff --git a/drivers/net/wireless/ath/ath5k/base.h b/drivers/net/wireless/ath/ath5k/base.h index 5e2366d3db09..d8e2674aec71 100644 --- a/drivers/net/wireless/ath/ath5k/base.h +++ b/drivers/net/wireless/ath/ath5k/base.h @@ -87,6 +87,7 @@ struct ath5k_txq { spinlock_t lock; /* lock on q and link */ bool setup; int txq_len; /* number of queued buffers */ + bool txq_poll_mark; }; #define ATH5K_LED_MAX_NAME_LEN 31 @@ -233,6 +234,8 @@ struct ath5k_softc { struct ath5k_ani_state ani_state; struct tasklet_struct ani_tasklet; /* ANI calibration */ + + struct delayed_work tx_complete_work; }; #define ath5k_hw_hasbssidmask(_ah) \ -- cgit v1.2.3 From 923e5b3d3d773b9956b943ac64f782d5a127bdea Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Fri, 17 Sep 2010 11:37:02 +0900 Subject: ath5k: Count how many times a queue got stuck Add a counter to show how many times a queue got stuck in the debugfs queue file. Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 2 ++ drivers/net/wireless/ath/ath5k/base.h | 1 + drivers/net/wireless/ath/ath5k/debug.c | 2 ++ 3 files changed, 5 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index afedfeba13dd..cae9fe4676e7 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -892,6 +892,7 @@ ath5k_txq_setup(struct ath5k_softc *sc, txq->setup = true; txq->txq_len = 0; txq->txq_poll_mark = false; + txq->txq_stuck = 0; } return &sc->txqs[qnum]; } @@ -2193,6 +2194,7 @@ ath5k_tx_complete_poll_work(struct work_struct *work) "TX queue stuck %d\n", txq->qnum); needreset = true; + txq->txq_stuck++; spin_unlock_bh(&txq->lock); break; } else { diff --git a/drivers/net/wireless/ath/ath5k/base.h b/drivers/net/wireless/ath/ath5k/base.h index d8e2674aec71..7f9d0d3018e8 100644 --- a/drivers/net/wireless/ath/ath5k/base.h +++ b/drivers/net/wireless/ath/ath5k/base.h @@ -88,6 +88,7 @@ struct ath5k_txq { bool setup; int txq_len; /* number of queued buffers */ bool txq_poll_mark; + unsigned int txq_stuck; /* informational counter */ }; #define ATH5K_LED_MAX_NAME_LEN 31 diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c index b2c610ba8b7e..6583a82a0783 100644 --- a/drivers/net/wireless/ath/ath5k/debug.c +++ b/drivers/net/wireless/ath/ath5k/debug.c @@ -785,6 +785,8 @@ static ssize_t read_file_queue(struct file *file, char __user *user_buf, len += snprintf(buf+len, sizeof(buf)-len, " len: %d bufs: %d\n", txq->txq_len, n); + len += snprintf(buf+len, sizeof(buf)-len, + " stuck: %d\n", txq->txq_stuck); } if (len > sizeof(buf)) -- cgit v1.2.3 From 234132960dcf8ebd9d424d92a4dfb0e57fa63a17 Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Fri, 17 Sep 2010 11:37:07 +0900 Subject: ath5k: Keep last descriptor in queue If we return a TX descriptor to the pool of available descriptors, while a queues TXDP still points to it we could potentially run into all sorts of troube. It has been suggested that there is hardware which can set the descriptors done bit before it reads ds_link and moves on to the next descriptor. While the documentation says this is not true for newer chipsets (the descriptor contents are copied to some internal memory), we don't know about older hardware. To be safe, we always keep the last descriptor in the queue, and avoid dangling TXDP pointers. Unfortunately this does not fully resolve the problem - queues still get stuck! This is similar to what ath9k does. Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 64 +++++++++++++++++------------------ 1 file changed, 32 insertions(+), 32 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index cae9fe4676e7..3e5ed6c0c808 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1586,44 +1586,44 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq) spin_lock(&txq->lock); list_for_each_entry_safe(bf, bf0, &txq->q, list) { - ds = bf->desc; + + txq->txq_poll_mark = false; + + /* skb might already have been processed last time. */ + if (bf->skb != NULL) { + ds = bf->desc; + + ret = sc->ah->ah_proc_tx_desc(sc->ah, ds, &ts); + if (unlikely(ret == -EINPROGRESS)) + break; + else if (unlikely(ret)) { + ATH5K_ERR(sc, + "error %d while processing " + "queue %u\n", ret, txq->qnum); + break; + } + + skb = bf->skb; + bf->skb = NULL; + pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, + PCI_DMA_TODEVICE); + ath5k_tx_frame_completed(sc, skb, &ts); + } /* * It's possible that the hardware can say the buffer is * completed when it hasn't yet loaded the ds_link from - * host memory and moved on. If there are more TX - * descriptors in the queue, wait for TXDP to change - * before processing this one. + * host memory and moved on. + * Always keep the last descriptor to avoid HW races... */ - if (ath5k_hw_get_txdp(sc->ah, txq->qnum) == bf->daddr && - !list_is_last(&bf->list, &txq->q)) - break; - ret = sc->ah->ah_proc_tx_desc(sc->ah, ds, &ts); - if (unlikely(ret == -EINPROGRESS)) - break; - else if (unlikely(ret)) { - ATH5K_ERR(sc, "error %d while processing queue %u\n", - ret, txq->qnum); - break; + if (ath5k_hw_get_txdp(sc->ah, txq->qnum) != bf->daddr) { + spin_lock(&sc->txbuflock); + list_move_tail(&bf->list, &sc->txbuf); + sc->txbuf_len++; + txq->txq_len--; + spin_unlock(&sc->txbuflock); } - - skb = bf->skb; - bf->skb = NULL; - pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, - PCI_DMA_TODEVICE); - - ath5k_tx_frame_completed(sc, skb, &ts); - - spin_lock(&sc->txbuflock); - list_move_tail(&bf->list, &sc->txbuf); - sc->txbuf_len++; - txq->txq_len--; - spin_unlock(&sc->txbuflock); - - txq->txq_poll_mark = false; } - if (likely(list_empty(&txq->q))) - txq->link = NULL; spin_unlock(&txq->lock); if (txq->txq_len < ATH5K_TXQ_LEN_LOW) ieee80211_wake_queue(sc->hw, txq->qnum); @@ -2188,7 +2188,7 @@ ath5k_tx_complete_poll_work(struct work_struct *work) if (sc->txqs[i].setup) { txq = &sc->txqs[i]; spin_lock_bh(&txq->lock); - if (txq->txq_len > 0) { + if (txq->txq_len > 1) { if (txq->txq_poll_mark) { ATH5K_DBG(sc, ATH5K_DEBUG_XMIT, "TX queue stuck %d\n", -- cgit v1.2.3 From de8af45520f47d14397b603beefae3d2983ce787 Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Fri, 17 Sep 2010 11:37:12 +0900 Subject: ath5k: Simplify cw_min/max and AIFS configuration Get rid of overly complicated cw_min/max and AIFS configuration: * Validate values in ath5k_hw_set_tx_queueprops(), so we can use them directly without further checks or computation in ath5k_hw_reset_tx_queue(). * Simplifiy by using AR5K_TUNE_AIFS|CWMIN|CWMAX variables directly since we don't support XR or B channels. That way we can also remove AR5K_TXQ_USEDEFAULT and the confusing logic around it. * Update data types: AIFS is u8, CW's are u16. * Remove now unneeded variables in ath5k_hw. Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/ath5k.h | 11 +--- drivers/net/wireless/ath/ath5k/attach.c | 2 - drivers/net/wireless/ath/ath5k/base.c | 18 +++--- drivers/net/wireless/ath/ath5k/qcu.c | 99 +++++++++++++++++---------------- 4 files changed, 65 insertions(+), 65 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index 9475b2157f2d..b96bb985b56d 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -258,8 +258,6 @@ (AR5K_INIT_PROG_IFS_TURBO) \ ) -/* token to use for aifs, cwmin, cwmax in MadWiFi */ -#define AR5K_TXQ_USEDEFAULT ((u32) -1) /* GENERIC CHIPSET DEFINITIONS */ @@ -530,9 +528,9 @@ struct ath5k_txq_info { enum ath5k_tx_queue tqi_type; enum ath5k_tx_queue_subtype tqi_subtype; u16 tqi_flags; /* Tx queue flags (see above) */ - u32 tqi_aifs; /* Arbitrated Interframe Space */ - s32 tqi_cw_min; /* Minimum Contention Window */ - s32 tqi_cw_max; /* Maximum Contention Window */ + u8 tqi_aifs; /* Arbitrated Interframe Space */ + u16 tqi_cw_min; /* Minimum Contention Window */ + u16 tqi_cw_max; /* Maximum Contention Window */ u32 tqi_cbr_period; /* Constant bit rate period */ u32 tqi_cbr_overflow_limit; u32 tqi_burst_time; @@ -1044,9 +1042,6 @@ struct ath5k_hw { #define ah_ee_version ah_capabilities.cap_eeprom.ee_version u32 ah_atim_window; - u32 ah_aifs; - u32 ah_cw_min; - u32 ah_cw_max; u32 ah_limit_tx_retries; u8 ah_coverage_class; diff --git a/drivers/net/wireless/ath/ath5k/attach.c b/drivers/net/wireless/ath/ath5k/attach.c index 20d178eabc9c..6e02de311cdd 100644 --- a/drivers/net/wireless/ath/ath5k/attach.c +++ b/drivers/net/wireless/ath/ath5k/attach.c @@ -119,8 +119,6 @@ int ath5k_hw_attach(struct ath5k_softc *sc) ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER; ah->ah_imr = 0; ah->ah_atim_window = 0; - ah->ah_aifs = AR5K_TUNE_AIFS; - ah->ah_cw_min = AR5K_TUNE_CWMIN; ah->ah_limit_tx_retries = AR5K_INIT_TX_RETRY; ah->ah_software_retry = false; ah->ah_ant_mode = AR5K_ANTMODE_DEFAULT; diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 3e5ed6c0c808..6bd8dd6f327b 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -849,9 +849,11 @@ ath5k_txq_setup(struct ath5k_softc *sc, struct ath5k_txq *txq; struct ath5k_txq_info qi = { .tqi_subtype = subtype, - .tqi_aifs = AR5K_TXQ_USEDEFAULT, - .tqi_cw_min = AR5K_TXQ_USEDEFAULT, - .tqi_cw_max = AR5K_TXQ_USEDEFAULT + /* XXX: default values not correct for B and XR channels, + * but who cares? */ + .tqi_aifs = AR5K_TUNE_AIFS, + .tqi_cw_min = AR5K_TUNE_CWMIN, + .tqi_cw_max = AR5K_TUNE_CWMAX }; int qnum; @@ -901,9 +903,11 @@ static int ath5k_beaconq_setup(struct ath5k_hw *ah) { struct ath5k_txq_info qi = { - .tqi_aifs = AR5K_TXQ_USEDEFAULT, - .tqi_cw_min = AR5K_TXQ_USEDEFAULT, - .tqi_cw_max = AR5K_TXQ_USEDEFAULT, + /* XXX: default values not correct for B and XR channels, + * but who cares? */ + .tqi_aifs = AR5K_TUNE_AIFS, + .tqi_cw_min = AR5K_TUNE_CWMIN, + .tqi_cw_max = AR5K_TUNE_CWMAX, /* NB: for dynamic turbo, don't enable any other interrupts */ .tqi_flags = AR5K_TXQ_FLAG_TXDESCINT_ENABLE }; @@ -937,7 +941,7 @@ ath5k_beaconq_config(struct ath5k_softc *sc) */ qi.tqi_aifs = 0; qi.tqi_cw_min = 0; - qi.tqi_cw_max = 2 * ah->ah_cw_min; + qi.tqi_cw_max = 2 * AR5K_TUNE_CWMIN; } ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, diff --git a/drivers/net/wireless/ath/ath5k/qcu.c b/drivers/net/wireless/ath/ath5k/qcu.c index 4186ff4c6e9c..84c717ded1c5 100644 --- a/drivers/net/wireless/ath/ath5k/qcu.c +++ b/drivers/net/wireless/ath/ath5k/qcu.c @@ -35,25 +35,59 @@ int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue, return 0; } +/* + * Make sure cw is a power of 2 minus 1 and smaller than 1024 + */ +static u16 ath5k_cw_validate(u16 cw_req) +{ + u32 cw = 1; + cw_req = min(cw_req, (u16)1023); + + while (cw < cw_req) + cw = (cw << 1) | 1; + + return cw; +} + /* * Set properties for a transmit queue */ int ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah, int queue, - const struct ath5k_txq_info *queue_info) + const struct ath5k_txq_info *qinfo) { + struct ath5k_txq_info *qi; + AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); - if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE) + qi = &ah->ah_txq[queue]; + + if (qi->tqi_type == AR5K_TX_QUEUE_INACTIVE) return -EIO; - memcpy(&ah->ah_txq[queue], queue_info, sizeof(struct ath5k_txq_info)); + /* copy and validate values */ + qi->tqi_type = qinfo->tqi_type; + qi->tqi_subtype = qinfo->tqi_subtype; + qi->tqi_flags = qinfo->tqi_flags; + /* + * According to the docs: Although the AIFS field is 8 bit wide, + * the maximum supported value is 0xFC. Setting it higher than that + * will cause the DCU to hang. + */ + qi->tqi_aifs = min(qinfo->tqi_aifs, (u8)0xFC); + qi->tqi_cw_min = ath5k_cw_validate(qinfo->tqi_cw_min); + qi->tqi_cw_max = ath5k_cw_validate(qinfo->tqi_cw_max); + qi->tqi_cbr_period = qinfo->tqi_cbr_period; + qi->tqi_cbr_overflow_limit = qinfo->tqi_cbr_overflow_limit; + qi->tqi_burst_time = qinfo->tqi_burst_time; + qi->tqi_ready_time = qinfo->tqi_ready_time; /*XXX: Is this supported on 5210 ?*/ - if ((queue_info->tqi_type == AR5K_TX_QUEUE_DATA && - ((queue_info->tqi_subtype == AR5K_WME_AC_VI) || - (queue_info->tqi_subtype == AR5K_WME_AC_VO))) || - queue_info->tqi_type == AR5K_TX_QUEUE_UAPSD) - ah->ah_txq[queue].tqi_flags |= AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS; + /*XXX: Is this correct for AR5K_WME_AC_VI,VO ???*/ + if ((qinfo->tqi_type == AR5K_TX_QUEUE_DATA && + ((qinfo->tqi_subtype == AR5K_WME_AC_VI) || + (qinfo->tqi_subtype == AR5K_WME_AC_VO))) || + qinfo->tqi_type == AR5K_TX_QUEUE_UAPSD) + qi->tqi_flags |= AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS; return 0; } @@ -186,7 +220,7 @@ void ath5k_hw_release_tx_queue(struct ath5k_hw *ah, unsigned int queue) */ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) { - u32 cw_min, cw_max, retry_lg, retry_sh; + u32 retry_lg, retry_sh; struct ath5k_txq_info *tq = &ah->ah_txq[queue]; AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); @@ -217,14 +251,13 @@ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) /* Set IFS0 */ if (ah->ah_turbo) { ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS_TURBO + - (ah->ah_aifs + tq->tqi_aifs) * - AR5K_INIT_SLOT_TIME_TURBO) << + tq->tqi_aifs * AR5K_INIT_SLOT_TIME_TURBO) << AR5K_IFS0_DIFS_S) | AR5K_INIT_SIFS_TURBO, AR5K_IFS0); } else { ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS + - (ah->ah_aifs + tq->tqi_aifs) * - AR5K_INIT_SLOT_TIME) << AR5K_IFS0_DIFS_S) | + tq->tqi_aifs * AR5K_INIT_SLOT_TIME) << + AR5K_IFS0_DIFS_S) | AR5K_INIT_SIFS, AR5K_IFS0); } @@ -247,35 +280,6 @@ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) AR5K_PHY_FRAME_CTL_5210); } - /* - * Calculate cwmin/max by channel mode - */ - cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN; - cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX; - ah->ah_aifs = AR5K_TUNE_AIFS; - /*XR is only supported on 5212*/ - if (IS_CHAN_XR(ah->ah_current_channel) && - ah->ah_version == AR5K_AR5212) { - cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN_XR; - cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX_XR; - ah->ah_aifs = AR5K_TUNE_AIFS_XR; - /*B mode is not supported on 5210*/ - } else if (IS_CHAN_B(ah->ah_current_channel) && - ah->ah_version != AR5K_AR5210) { - cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN_11B; - cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX_11B; - ah->ah_aifs = AR5K_TUNE_AIFS_11B; - } - - cw_min = 1; - while (cw_min < ah->ah_cw_min) - cw_min = (cw_min << 1) | 1; - - cw_min = tq->tqi_cw_min < 0 ? (cw_min >> (-tq->tqi_cw_min)) : - ((cw_min << tq->tqi_cw_min) + (1 << tq->tqi_cw_min) - 1); - cw_max = tq->tqi_cw_max < 0 ? (cw_max >> (-tq->tqi_cw_max)) : - ((cw_max << tq->tqi_cw_max) + (1 << tq->tqi_cw_max) - 1); - /* * Calculate and set retry limits */ @@ -292,7 +296,7 @@ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) /*No QCU/DCU [5210]*/ if (ah->ah_version == AR5K_AR5210) { ath5k_hw_reg_write(ah, - (cw_min << AR5K_NODCU_RETRY_LMT_CW_MIN_S) + (tq->tqi_cw_min << AR5K_NODCU_RETRY_LMT_CW_MIN_S) | AR5K_REG_SM(AR5K_INIT_SLG_RETRY, AR5K_NODCU_RETRY_LMT_SLG_RETRY) | AR5K_REG_SM(AR5K_INIT_SSH_RETRY, @@ -314,14 +318,13 @@ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) /*===Rest is also for QCU/DCU only [5211+]===*/ /* - * Set initial content window (cw_min/cw_max) + * Set contention window (cw_min/cw_max) * and arbitrated interframe space (aifs)... */ ath5k_hw_reg_write(ah, - AR5K_REG_SM(cw_min, AR5K_DCU_LCL_IFS_CW_MIN) | - AR5K_REG_SM(cw_max, AR5K_DCU_LCL_IFS_CW_MAX) | - AR5K_REG_SM(ah->ah_aifs + tq->tqi_aifs, - AR5K_DCU_LCL_IFS_AIFS), + AR5K_REG_SM(tq->tqi_cw_min, AR5K_DCU_LCL_IFS_CW_MIN) | + AR5K_REG_SM(tq->tqi_cw_max, AR5K_DCU_LCL_IFS_CW_MAX) | + AR5K_REG_SM(tq->tqi_aifs, AR5K_DCU_LCL_IFS_AIFS), AR5K_QUEUE_DFS_LOCAL_IFS(queue)); /* -- cgit v1.2.3 From e0b1cc52e5da3e2fa79666b8df81a953c95c91e4 Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Fri, 17 Sep 2010 11:37:18 +0900 Subject: ath5k: Add tx queue configuration function Add the mac80211 callback function to configure the tx queue properties like cw_min, cw_max and aifs. Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 40 ++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 6bd8dd6f327b..504c6d648ecf 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -3140,6 +3140,44 @@ static void ath5k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class) mutex_unlock(&sc->lock); } +static int ath5k_conf_tx(struct ieee80211_hw *hw, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct ath5k_softc *sc = hw->priv; + struct ath5k_hw *ah = sc->ah; + struct ath5k_txq_info qi; + int ret = 0; + + if (queue >= ah->ah_capabilities.cap_queues.q_tx_num) + return 0; + + mutex_lock(&sc->lock); + + ath5k_hw_get_tx_queueprops(ah, queue, &qi); + + qi.tqi_aifs = params->aifs; + qi.tqi_cw_min = params->cw_min; + qi.tqi_cw_max = params->cw_max; + qi.tqi_burst_time = params->txop; + + ATH5K_DBG(sc, ATH5K_DEBUG_ANY, + "Configure tx [queue %d], " + "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n", + queue, params->aifs, params->cw_min, + params->cw_max, params->txop); + + if (ath5k_hw_set_tx_queueprops(ah, queue, &qi)) { + ATH5K_ERR(sc, + "Unable to update hardware queue %u!\n", queue); + ret = -EIO; + } else + ath5k_hw_reset_tx_queue(ah, queue); + + mutex_unlock(&sc->lock); + + return ret; +} + static const struct ieee80211_ops ath5k_hw_ops = { .tx = ath5k_tx, .start = ath5k_start, @@ -3152,7 +3190,7 @@ static const struct ieee80211_ops ath5k_hw_ops = { .set_key = ath5k_set_key, .get_stats = ath5k_get_stats, .get_survey = ath5k_get_survey, - .conf_tx = NULL, + .conf_tx = ath5k_conf_tx, .get_tsf = ath5k_get_tsf, .set_tsf = ath5k_set_tsf, .reset_tsf = ath5k_reset_tsf, -- cgit v1.2.3 From 9dec6f9c48242eec742c9475f32eeef29448701c Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Fri, 17 Sep 2010 22:07:59 +0200 Subject: carl9170: use rx chainmask from eeprom The eeprom provides a mask for all present rx chains. Why not use it instead of the generic initval default? Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/phy.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c index 71dea9707e21..fe265e3e6ac4 100644 --- a/drivers/net/wireless/ath/carl9170/phy.c +++ b/drivers/net/wireless/ath/carl9170/phy.c @@ -532,8 +532,11 @@ static int carl9170_init_phy_from_eeprom(struct ar9170 *ar, SET_VAL(AR9170_PHY_TPCRG1_PD_GAIN_2, val, xpd2pd[m->xpdGain & 0xf] >> 2); carl9170_regwrite(AR9170_PHY_REG_TPCRG1, val); - carl9170_regwrite_finish(); + carl9170_regwrite(AR9170_PHY_REG_RX_CHAINMASK, ar->eeprom.rx_mask); + carl9170_regwrite(AR9170_PHY_REG_CAL_CHAINMASK, ar->eeprom.rx_mask); + + carl9170_regwrite_finish(); return carl9170_regwrite_result(); } -- cgit v1.2.3 From e278c5a90368408bd191743e7c6f978f068f4b8d Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Fri, 17 Sep 2010 22:22:50 +0200 Subject: carl9170: fix noise dBm conversion Ever since carl9170 gained support to read the noisefloor, the reported noisefloor level was pretty poor. Initially I assumed that something was wrong in the PHY setup and it would be impossible to fix without any guidances. But this was not the case. In fact the nf readings were correct and the thing that was broken was the "simple" sign extension code! Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/phy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c index fe265e3e6ac4..7df8f711f5d8 100644 --- a/drivers/net/wireless/ath/carl9170/phy.c +++ b/drivers/net/wireless/ath/carl9170/phy.c @@ -1558,9 +1558,9 @@ static int carl9170_set_power_cal(struct ar9170 *ar, u32 freq, static int carl9170_calc_noise_dbm(u32 raw_noise) { if (raw_noise & 0x100) - return ~((raw_noise & 0x0ff) >> 1); + return ~0x1ff | raw_noise; else - return (raw_noise & 0xff) >> 1; + return raw_noise; } int carl9170_get_noisefloor(struct ar9170 *ar) -- cgit v1.2.3 From 9adc9e0ff397fb2d4f383cc2d399b18adc32e6eb Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Fri, 17 Sep 2010 22:42:37 +0200 Subject: carl9170: don't load bogus nf of chain 1 According to Atheros, chain 1 is not connected. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/carl9170.h | 2 +- drivers/net/wireless/ath/carl9170/debug.c | 8 +++----- drivers/net/wireless/ath/carl9170/phy.c | 11 +++++------ 3 files changed, 9 insertions(+), 12 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h index 9f1d60359061..d43675aca0bb 100644 --- a/drivers/net/wireless/ath/carl9170/carl9170.h +++ b/drivers/net/wireless/ath/carl9170/carl9170.h @@ -321,7 +321,7 @@ struct ar9170 { /* PHY */ struct ieee80211_channel *channel; - int noise[6]; + int noise[4]; unsigned int chan_fail; unsigned int total_chan_fail; u8 heavy_clip; diff --git a/drivers/net/wireless/ath/carl9170/debug.c b/drivers/net/wireless/ath/carl9170/debug.c index 3e9b0e8b2b8c..19b48369ffed 100644 --- a/drivers/net/wireless/ath/carl9170/debug.c +++ b/drivers/net/wireless/ath/carl9170/debug.c @@ -436,12 +436,10 @@ static char *carl9170_debugfs_phy_noise_read(struct ar9170 *ar, char *buf, return buf; } - ADD(buf, *len, bufsize, "Chain 1: %10d dBm, ext. chan.:%10d dBm\n", - ar->noise[1], ar->noise[4]); + ADD(buf, *len, bufsize, "Chain 0: %10d dBm, ext. chan.:%10d dBm\n", + ar->noise[0], ar->noise[2]); ADD(buf, *len, bufsize, "Chain 2: %10d dBm, ext. chan.:%10d dBm\n", - ar->noise[2], ar->noise[5]); - ADD(buf, *len, bufsize, "Combined %10d dBm, ext. chan.:%10d dBm\n", - ar->noise[0], ar->noise[3]); + ar->noise[1], ar->noise[3]); return buf; } diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c index 7df8f711f5d8..fa349b86ea62 100644 --- a/drivers/net/wireless/ath/carl9170/phy.c +++ b/drivers/net/wireless/ath/carl9170/phy.c @@ -1566,9 +1566,8 @@ static int carl9170_calc_noise_dbm(u32 raw_noise) int carl9170_get_noisefloor(struct ar9170 *ar) { static const u32 phy_regs[] = { - AR9170_PHY_REG_CCA, AR9170_PHY_REG_CH1_CCA, - AR9170_PHY_REG_CH2_CCA, AR9170_PHY_REG_EXT_CCA, - AR9170_PHY_REG_CH1_EXT_CCA, AR9170_PHY_REG_CH2_EXT_CCA }; + AR9170_PHY_REG_CCA, AR9170_PHY_REG_CH2_CCA, + AR9170_PHY_REG_EXT_CCA, AR9170_PHY_REG_CH2_EXT_CCA }; u32 phy_res[ARRAY_SIZE(phy_regs)]; int err, i; @@ -1578,12 +1577,12 @@ int carl9170_get_noisefloor(struct ar9170 *ar) if (err) return err; - for (i = 0; i < 3; i++) { + for (i = 0; i < 2; i++) { ar->noise[i] = carl9170_calc_noise_dbm( (phy_res[i] >> 19) & 0x1ff); - ar->noise[i + 3] = carl9170_calc_noise_dbm( - (phy_res[i + 3] >> 23) & 0x1ff); + ar->noise[i + 2] = carl9170_calc_noise_dbm( + (phy_res[i + 2] >> 23) & 0x1ff); } return 0; -- cgit v1.2.3 From cf6487d0d656994cc43851b8c2384741e220f7ac Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Fri, 17 Sep 2010 22:47:28 +0200 Subject: carl9170: abort tasklet during usb reset This patch prevents the tasklet code from interfering while the firmware is down for an unscheduled maintenance. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/usb.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/usb.c b/drivers/net/wireless/ath/carl9170/usb.c index fde918d0120b..1e7c2748d85a 100644 --- a/drivers/net/wireless/ath/carl9170/usb.c +++ b/drivers/net/wireless/ath/carl9170/usb.c @@ -367,6 +367,9 @@ static void carl9170_usb_tasklet(unsigned long data) { struct ar9170 *ar = (struct ar9170 *) data; + if (!IS_INITIALIZED(ar)) + return; + carl9170_usb_rx_work(ar); /* -- cgit v1.2.3 From 78ec789bd397249a9bb412ca91bd360079e7b446 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Fri, 17 Sep 2010 22:58:40 +0200 Subject: carl9170: fix state downgrade during reset Don't mark the device as completely dead just yet. If all goes to plan and carl9170_reboot succeeds then we can skip the expensive userspace-driven reinitialization anyway. And if it doesn't and carl9170_reboot fails, then carl9170_usb_cancel_urbs will do the necessary steps. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/usb.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/usb.c b/drivers/net/wireless/ath/carl9170/usb.c index 1e7c2748d85a..eb789a9e4f15 100644 --- a/drivers/net/wireless/ath/carl9170/usb.c +++ b/drivers/net/wireless/ath/carl9170/usb.c @@ -846,8 +846,6 @@ int carl9170_usb_restart(struct ar9170 *ar) carl9170_usb_stop(ar); - carl9170_set_state(ar, CARL9170_UNKNOWN_STATE); - if (err) goto err_out; -- cgit v1.2.3 From 735761108f435a30bbabb5254fc5a8e17b99f2a1 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Fri, 17 Sep 2010 23:09:19 +0200 Subject: carl9170: reinit phy after HT settings have changed The driver has a set of different initvals for 20 MHz vs dynamic HT2040 operation. Because we can't change some of the registers "in-flight", the driver needs to perform a warm reset. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/carl9170.h | 1 + drivers/net/wireless/ath/carl9170/phy.c | 25 +++++++++++++------------ 2 files changed, 14 insertions(+), 12 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h index d43675aca0bb..d7c5482d74ce 100644 --- a/drivers/net/wireless/ath/carl9170/carl9170.h +++ b/drivers/net/wireless/ath/carl9170/carl9170.h @@ -325,6 +325,7 @@ struct ar9170 { unsigned int chan_fail; unsigned int total_chan_fail; u8 heavy_clip; + u8 ht_settings; /* power calibration data */ u8 power_5G_leg[4]; diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c index fa349b86ea62..89deca37a988 100644 --- a/drivers/net/wireless/ath/carl9170/phy.c +++ b/drivers/net/wireless/ath/carl9170/phy.c @@ -1610,7 +1610,7 @@ int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel, const struct carl9170_phy_freq_params *freqpar; struct carl9170_rf_init_result rf_res; struct carl9170_rf_init rf; - u32 cmd, tmp, offs = 0; + u32 cmd, tmp, offs = 0, new_ht = 0; int err; enum carl9170_bw bw; bool warm_reset; @@ -1618,12 +1618,19 @@ int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel, bw = nl80211_to_carl(_bw); + if (conf_is_ht(&ar->hw->conf)) + new_ht |= CARL9170FW_PHY_HT_ENABLE; + + if (conf_is_ht40(&ar->hw->conf)) + new_ht |= CARL9170FW_PHY_HT_DYN2040; + /* may be NULL at first setup */ if (ar->channel) { old_channel = ar->channel; warm_reset = (old_channel->band != channel->band) || (old_channel->center_freq == - channel->center_freq); + channel->center_freq) || + (ar->ht_settings != new_ht); ar->channel = NULL; } else { @@ -1724,16 +1731,9 @@ int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel, freqpar = carl9170_get_hw_dyn_params(channel, bw); - rf.ht_settings = 0; - if (conf_is_ht(&ar->hw->conf)) { - rf.ht_settings |= CARL9170FW_PHY_HT_ENABLE; - - if (conf_is_ht40(&ar->hw->conf)) { - rf.ht_settings |= CARL9170FW_PHY_HT_DYN2040; - SET_VAL(CARL9170FW_PHY_HT_EXT_CHAN_OFF, - rf.ht_settings, offs); - } - } + rf.ht_settings = new_ht; + if (conf_is_ht40(&ar->hw->conf)) + SET_VAL(CARL9170FW_PHY_HT_EXT_CHAN_OFF, rf.ht_settings, offs); rf.freq = cpu_to_le32(channel->center_freq * 1000); rf.delta_slope_coeff_exp = cpu_to_le32(freqpar->coeff_exp); @@ -1805,5 +1805,6 @@ int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel, ar->ps.off_override &= ~PS_OFF_5GHZ; ar->channel = channel; + ar->ht_settings = new_ht; return 0; } -- cgit v1.2.3 From 2c7808d9345b91bb19f6bfaebf7b9e0916b0859a Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 18 Sep 2010 00:15:13 +0200 Subject: carl9170: fix hang in AP mode when HT STA does PSM This is a obvious bug, skb_queue_walk does not work if the iterator gets removed from the queue. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index ea49d54ce03b..43de9dfa5820 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1412,7 +1412,7 @@ static void carl9170_op_sta_notify(struct ieee80211_hw *hw, { struct ar9170 *ar = hw->priv; struct carl9170_sta_info *sta_info = (void *) sta->drv_priv; - struct sk_buff *skb; + struct sk_buff *skb, *tmp; struct sk_buff_head free; int i; @@ -1462,7 +1462,7 @@ static void carl9170_op_sta_notify(struct ieee80211_hw *hw, for (i = 0; i < ar->hw->queues; i++) { spin_lock_bh(&ar->tx_pending[i].lock); - skb_queue_walk(&ar->tx_pending[i], skb) { + skb_queue_walk_safe(&ar->tx_pending[i], skb, tmp) { struct _carl9170_tx_superframe *super; struct ieee80211_hdr *hdr; -- cgit v1.2.3 From 81ee13ba7ef8c9eaebe91ed06edb844ab6403d4e Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 20 Sep 2010 13:45:36 +0200 Subject: ath9k: clean up block ack window handling There's no reason to keep pointers to pending tx buffers around, if they're only used to keep track of which frames are still pending. Use a bitfield instead. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 2 +- drivers/net/wireless/ath/ath9k/xmit.c | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 98b57e6c614e..79217c3c92e5 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -254,7 +254,7 @@ struct ath_atx_tid { struct list_head buf_q; struct ath_node *an; struct ath_atx_ac *ac; - struct ath_buf *tx_buf[ATH_TID_MAX_BUFS]; + unsigned long tx_buf[BITS_TO_LONGS(ATH_TID_MAX_BUFS)]; u16 seq_start; u16 seq_next; u16 baw_size; diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 457f07692ac7..5323a4d9ebb8 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -168,9 +168,9 @@ static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid, index = ATH_BA_INDEX(tid->seq_start, seqno); cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1); - tid->tx_buf[cindex] = NULL; + __clear_bit(cindex, tid->tx_buf); - while (tid->baw_head != tid->baw_tail && !tid->tx_buf[tid->baw_head]) { + while (tid->baw_head != tid->baw_tail && !test_bit(tid->baw_head, tid->tx_buf)) { INCR(tid->seq_start, IEEE80211_SEQ_MAX); INCR(tid->baw_head, ATH_TID_MAX_BUFS); } @@ -186,9 +186,7 @@ static void ath_tx_addto_baw(struct ath_softc *sc, struct ath_atx_tid *tid, index = ATH_BA_INDEX(tid->seq_start, bf->bf_seqno); cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1); - - BUG_ON(tid->tx_buf[cindex] != NULL); - tid->tx_buf[cindex] = bf; + __set_bit(cindex, tid->tx_buf); if (index >= ((tid->baw_tail - tid->baw_head) & (ATH_TID_MAX_BUFS - 1))) { -- cgit v1.2.3 From 231c3a1f0630c07a584905507a1cb7b705a56ab7 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 20 Sep 2010 19:35:28 +0200 Subject: ath9k: fix an aggregation start related race condition A new aggregation session start can be issued by mac80211, even when the cleanup of the previous session has not completed yet. Since the data structure for the session is not recreated, this could corrupt the block ack window and lock up the aggregation session. Fix this by delaying the new session until the old one has been cleaned up. Signed-off-by: Felix Fietkau Cc: stable@kernel.org Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 4 ++-- drivers/net/wireless/ath/ath9k/main.c | 5 +++-- drivers/net/wireless/ath/ath9k/xmit.c | 10 ++++++++-- 3 files changed, 13 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 79217c3c92e5..004533d2ca5f 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -346,8 +346,8 @@ void ath_tx_tasklet(struct ath_softc *sc); void ath_tx_edma_tasklet(struct ath_softc *sc); void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb); bool ath_tx_aggr_check(struct ath_softc *sc, struct ath_node *an, u8 tidno); -void ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, - u16 tid, u16 *ssn); +int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, + u16 tid, u16 *ssn); void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid); void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid); void ath9k_enable_ps(struct ath_softc *sc); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 5056733e6f66..8b327bcad695 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1970,8 +1970,9 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, break; case IEEE80211_AMPDU_TX_START: ath9k_ps_wakeup(sc); - ath_tx_aggr_start(sc, sta, tid, ssn); - ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); + ret = ath_tx_aggr_start(sc, sta, tid, ssn); + if (!ret) + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); ath9k_ps_restore(sc); break; case IEEE80211_AMPDU_TX_STOP: diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 5323a4d9ebb8..d629bfbdfab4 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -783,17 +783,23 @@ static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq, status != ATH_AGGR_BAW_CLOSED); } -void ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, - u16 tid, u16 *ssn) +int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, + u16 tid, u16 *ssn) { struct ath_atx_tid *txtid; struct ath_node *an; an = (struct ath_node *)sta->drv_priv; txtid = ATH_AN_2_TID(an, tid); + + if (txtid->state & (AGGR_CLEANUP | AGGR_ADDBA_COMPLETE)) + return -EAGAIN; + txtid->state |= AGGR_ADDBA_PROGRESS; txtid->paused = true; *ssn = txtid->seq_start; + + return 0; } void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) -- cgit v1.2.3 From 90fa539ca3f07323da5a90f5c8f4e5cd952875e7 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 20 Sep 2010 13:45:38 +0200 Subject: ath9k: clean up / fix aggregation session flush The tid aggregation cleanup is a bit fragile, as it discards failed subframes in some places, and retransmits them in others. This could block the cleanup of an existing aggregation session, if a retransmission for a tid is issued, yet the tid is never scheduled again because of the cleanup state. Fix this by getting rid of as many subframes as possible, as early as possible, and immediately transmitting pending subframes as regular HT frames instead of waiting for the cleanup to complete. Drop all pending subframes while keeping track of the Block ACK window during aggregate tx completion to prevent sending out stale subframes, which could confuse the receiver side. Signed-off-by: Felix Fietkau Cc: stable@kernel.org Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/xmit.c | 63 +++++++++++++++-------------------- 1 file changed, 26 insertions(+), 37 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index d629bfbdfab4..e53433e3e4cc 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -61,6 +61,8 @@ static int ath_tx_num_badfrms(struct ath_softc *sc, struct ath_buf *bf, struct ath_tx_status *ts, int txok); static void ath_tx_rc_status(struct ath_buf *bf, struct ath_tx_status *ts, int nbad, int txok, bool update_rc); +static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid, + int seqno); enum { MCS_HT20, @@ -143,18 +145,23 @@ static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid) struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum]; struct ath_buf *bf; struct list_head bf_head; - INIT_LIST_HEAD(&bf_head); + struct ath_tx_status ts; - WARN_ON(!tid->paused); + INIT_LIST_HEAD(&bf_head); + memset(&ts, 0, sizeof(ts)); spin_lock_bh(&txq->axq_lock); - tid->paused = false; while (!list_empty(&tid->buf_q)) { bf = list_first_entry(&tid->buf_q, struct ath_buf, list); - BUG_ON(bf_isretried(bf)); list_move_tail(&bf->list, &bf_head); - ath_tx_send_ht_normal(sc, txq, tid, &bf_head); + + if (bf_isretried(bf)) { + ath_tx_update_baw(sc, tid, bf->bf_seqno); + ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0, 0); + } else { + ath_tx_send_ht_normal(sc, txq, tid, &bf_head); + } } spin_unlock_bh(&txq->axq_lock); @@ -429,7 +436,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, list_move_tail(&bf->list, &bf_head); } - if (!txpending) { + if (!txpending || (tid->state & AGGR_CLEANUP)) { /* * complete the acked-ones/xretried ones; update * block-ack window @@ -508,15 +515,12 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, } if (tid->state & AGGR_CLEANUP) { + ath_tx_flush_tid(sc, tid); + if (tid->baw_head == tid->baw_tail) { tid->state &= ~AGGR_ADDBA_COMPLETE; tid->state &= ~AGGR_CLEANUP; - - /* send buffered frames as singles */ - ath_tx_flush_tid(sc, tid); } - rcu_read_unlock(); - return; } rcu_read_unlock(); @@ -807,12 +811,6 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) struct ath_node *an = (struct ath_node *)sta->drv_priv; struct ath_atx_tid *txtid = ATH_AN_2_TID(an, tid); struct ath_txq *txq = &sc->tx.txq[txtid->ac->qnum]; - struct ath_tx_status ts; - struct ath_buf *bf; - struct list_head bf_head; - - memset(&ts, 0, sizeof(ts)); - INIT_LIST_HEAD(&bf_head); if (txtid->state & AGGR_CLEANUP) return; @@ -822,31 +820,22 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) return; } - /* drop all software retried frames and mark this TID */ spin_lock_bh(&txq->axq_lock); txtid->paused = true; - while (!list_empty(&txtid->buf_q)) { - bf = list_first_entry(&txtid->buf_q, struct ath_buf, list); - if (!bf_isretried(bf)) { - /* - * NB: it's based on the assumption that - * software retried frame will always stay - * at the head of software queue. - */ - break; - } - list_move_tail(&bf->list, &bf_head); - ath_tx_update_baw(sc, txtid, bf->bf_seqno); - ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0, 0); - } - spin_unlock_bh(&txq->axq_lock); - if (txtid->baw_head != txtid->baw_tail) { + /* + * If frames are still being transmitted for this TID, they will be + * cleaned up during tx completion. To prevent race conditions, this + * TID can only be reused after all in-progress subframes have been + * completed. + */ + if (txtid->baw_head != txtid->baw_tail) txtid->state |= AGGR_CLEANUP; - } else { + else txtid->state &= ~AGGR_ADDBA_COMPLETE; - ath_tx_flush_tid(sc, txtid); - } + spin_unlock_bh(&txq->axq_lock); + + ath_tx_flush_tid(sc, txtid); } void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) -- cgit v1.2.3 From 334b06029ed3f5e31d773527d54fb40e3ee571f9 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 20 Sep 2010 13:45:39 +0200 Subject: ath9k: move ath_tx_aggr_check() to the rate control module It is not used anywhere else and can be made static Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 - drivers/net/wireless/ath/ath9k/rc.c | 16 ++++++++++++++++ drivers/net/wireless/ath/ath9k/xmit.c | 14 -------------- 3 files changed, 16 insertions(+), 15 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 004533d2ca5f..9f8e542ef47e 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -345,7 +345,6 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, void ath_tx_tasklet(struct ath_softc *sc); void ath_tx_edma_tasklet(struct ath_softc *sc); void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb); -bool ath_tx_aggr_check(struct ath_softc *sc, struct ath_node *an, u8 tidno); int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid, u16 *ssn); void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid); diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index e49be733d546..ce1cd6d85847 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -1320,6 +1320,22 @@ static u8 ath_rc_build_ht_caps(struct ath_softc *sc, struct ieee80211_sta *sta, return caps; } +static bool ath_tx_aggr_check(struct ath_softc *sc, struct ath_node *an, + u8 tidno) +{ + struct ath_atx_tid *txtid; + + if (!(sc->sc_flags & SC_OP_TXAGGR)) + return false; + + txtid = ATH_AN_2_TID(an, tidno); + + if (!(txtid->state & (AGGR_ADDBA_COMPLETE | AGGR_ADDBA_PROGRESS))) + return true; + return false; +} + + /***********************************/ /* mac80211 Rate Control callbacks */ /***********************************/ diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index e53433e3e4cc..85a7323a04ef 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -855,20 +855,6 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid } } -bool ath_tx_aggr_check(struct ath_softc *sc, struct ath_node *an, u8 tidno) -{ - struct ath_atx_tid *txtid; - - if (!(sc->sc_flags & SC_OP_TXAGGR)) - return false; - - txtid = ATH_AN_2_TID(an, tidno); - - if (!(txtid->state & (AGGR_ADDBA_COMPLETE | AGGR_ADDBA_PROGRESS))) - return true; - return false; -} - /********************/ /* Queue Management */ /********************/ -- cgit v1.2.3 From 6e5c2b4e8addfaab8ef54dedaf7b607e1585c35b Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 20 Sep 2010 13:45:40 +0200 Subject: ath9k: make the driver specific rate control module optional ath9k can use minstrel_ht instead, so it makes sense to save some space here. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/Kconfig | 8 ++++++++ drivers/net/wireless/ath/ath9k/Makefile | 2 +- drivers/net/wireless/ath/ath9k/init.c | 2 ++ drivers/net/wireless/ath/ath9k/rc.h | 11 +++++++++++ 4 files changed, 22 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index 35f23bdc442f..ad57a6d23110 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -32,6 +32,14 @@ config ATH9K_DEBUGFS Also required for changing debug message flags at run time. +config ATH9K_RATE_CONTROL + bool "Atheros ath9k rate control" + depends on ATH9K + default y + ---help--- + Say Y, if you want to use the ath9k specific rate control + module instead of minstrel_ht. + config ATH9K_HTC tristate "Atheros HTC based wireless cards support" depends on USB && MAC80211 diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 4555e9983903..aca01621c205 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -5,8 +5,8 @@ ath9k-y += beacon.o \ recv.o \ xmit.o \ virtual.o \ - rc.o +ath9k-$(CONFIG_ATH9K_RATE_CONTROL) += rc.o ath9k-$(CONFIG_PCI) += pci.o ath9k-$(CONFIG_ATHEROS_AR71XX) += ahb.o ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 0364167cbfaf..573899e27b3d 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -654,7 +654,9 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->sta_data_size = sizeof(struct ath_node); hw->vif_data_size = sizeof(struct ath_vif); +#ifdef CONFIG_ATH9K_RATE_CONTROL hw->rate_control_algorithm = "ath9k_rate_control"; +#endif if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) hw->wiphy->bands[IEEE80211_BAND_2GHZ] = diff --git a/drivers/net/wireless/ath/ath9k/rc.h b/drivers/net/wireless/ath/ath9k/rc.h index dc1082654501..268072fd3c1c 100644 --- a/drivers/net/wireless/ath/ath9k/rc.h +++ b/drivers/net/wireless/ath/ath9k/rc.h @@ -224,7 +224,18 @@ enum ath9k_internal_frame_type { ATH9K_IFT_UNPAUSE }; +#ifdef CONFIG_ATH9K_RATE_CONTROL int ath_rate_control_register(void); void ath_rate_control_unregister(void); +#else +static inline int ath_rate_control_register(void) +{ + return 0; +} + +static inline void ath_rate_control_unregister(void) +{ +} +#endif #endif /* RC_H */ -- cgit v1.2.3 From 9094537c3a9ef9e127e844254a74186735c9a90b Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Mon, 20 Sep 2010 22:54:46 -0700 Subject: ath9k: Fix tx struck state with paprd Paprd needs to be done only on active chains(not for all the chains that hw can support). The paprd training frames which are sent for inactive chains would be hanging on the hw queue without getting transmitted and would make the connection so unstable. This issue happens only with the hw which supports paprd cal(ar9003). Signed-off-by: Vasanthakumar Thiagarajan Cc: stable@kernel.org Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 8b327bcad695..a13387882636 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -270,6 +270,7 @@ static void ath_paprd_activate(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath9k_hw_cal_data *caldata = ah->caldata; + struct ath_common *common = ath9k_hw_common(ah); int chain; if (!caldata || !caldata->paprd_done) @@ -278,7 +279,7 @@ static void ath_paprd_activate(struct ath_softc *sc) ath9k_ps_wakeup(sc); ar9003_paprd_enable(ah, false); for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { - if (!(ah->caps.tx_chainmask & BIT(chain))) + if (!(common->tx_chainmask & BIT(chain))) continue; ar9003_paprd_populate_single_table(ah, caldata, chain); @@ -300,6 +301,7 @@ void ath_paprd_calibrate(struct work_struct *work) struct ieee80211_supported_band *sband = &sc->sbands[band]; struct ath_tx_control txctl; struct ath9k_hw_cal_data *caldata = ah->caldata; + struct ath_common *common = ath9k_hw_common(ah); int qnum, ftype; int chain_ok = 0; int chain; @@ -333,7 +335,7 @@ void ath_paprd_calibrate(struct work_struct *work) ath9k_ps_wakeup(sc); ar9003_paprd_init_table(ah); for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { - if (!(ah->caps.tx_chainmask & BIT(chain))) + if (!(common->tx_chainmask & BIT(chain))) continue; chain_ok = 0; -- cgit v1.2.3 From 9cf13668a5f8165a81349defc5f82c57a4a8279b Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 21 Sep 2010 12:58:09 +0530 Subject: ath9k_htc: Fix beacon distribution in IBSS mode This patch ensures fair beacon distribution in IBSS mode by configuring proper CWmin based on slot time. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/htc_drv_beacon.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c index bd1506e69105..1b72aa482ac7 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c @@ -235,7 +235,14 @@ void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv) ath9k_hw_get_txq_props(ah, qnum, &qi_be); qi.tqi_aifs = qi_be.tqi_aifs; - qi.tqi_cwmin = 4*qi_be.tqi_cwmin; + /* For WIFI Beacon Distribution + * Long slot time : 2x cwmin + * Short slot time : 4x cwmin + */ + if (ah->slottime == ATH9K_SLOT_TIME_20) + qi.tqi_cwmin = 2*qi_be.tqi_cwmin; + else + qi.tqi_cwmin = 4*qi_be.tqi_cwmin; qi.tqi_cwmax = qi_be.tqi_cwmax; if (!ath9k_hw_set_txq_props(ah, priv->beaconq, &qi)) { -- cgit v1.2.3 From 87fd2e6c90acb45a5741207fc953190596f93841 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Tue, 21 Sep 2010 17:03:57 -0700 Subject: ath5k: Add bssid mask and rxfilter to debugfs. Helps with debugging virtual interfaces. Signed-off-by: Ben Greear Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/debug.c | 58 ++++++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath5k/debug.h | 1 + 2 files changed, 59 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c index 6583a82a0783..0f06e8490314 100644 --- a/drivers/net/wireless/ath/ath5k/debug.c +++ b/drivers/net/wireless/ath/ath5k/debug.c @@ -483,6 +483,59 @@ static const struct file_operations fops_antenna = { .owner = THIS_MODULE, }; +/* debugfs: misc */ + +static ssize_t read_file_misc(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath5k_softc *sc = file->private_data; + char buf[700]; + unsigned int len = 0; + u32 filt = ath5k_hw_get_rx_filter(sc->ah); + + len += snprintf(buf+len, sizeof(buf)-len, "bssid-mask: %pM\n", + sc->bssidmask); + len += snprintf(buf+len, sizeof(buf)-len, "filter-flags: 0x%x ", + filt); + if (filt & AR5K_RX_FILTER_UCAST) + len += snprintf(buf+len, sizeof(buf)-len, " UCAST"); + if (filt & AR5K_RX_FILTER_MCAST) + len += snprintf(buf+len, sizeof(buf)-len, " MCAST"); + if (filt & AR5K_RX_FILTER_BCAST) + len += snprintf(buf+len, sizeof(buf)-len, " BCAST"); + if (filt & AR5K_RX_FILTER_CONTROL) + len += snprintf(buf+len, sizeof(buf)-len, " CONTROL"); + if (filt & AR5K_RX_FILTER_BEACON) + len += snprintf(buf+len, sizeof(buf)-len, " BEACON"); + if (filt & AR5K_RX_FILTER_PROM) + len += snprintf(buf+len, sizeof(buf)-len, " PROM"); + if (filt & AR5K_RX_FILTER_XRPOLL) + len += snprintf(buf+len, sizeof(buf)-len, " XRPOLL"); + if (filt & AR5K_RX_FILTER_PROBEREQ) + len += snprintf(buf+len, sizeof(buf)-len, " PROBEREQ"); + if (filt & AR5K_RX_FILTER_PHYERR_5212) + len += snprintf(buf+len, sizeof(buf)-len, " PHYERR-5212"); + if (filt & AR5K_RX_FILTER_RADARERR_5212) + len += snprintf(buf+len, sizeof(buf)-len, " RADARERR-5212"); + if (filt & AR5K_RX_FILTER_PHYERR_5211) + snprintf(buf+len, sizeof(buf)-len, " PHYERR-5211"); + if (filt & AR5K_RX_FILTER_RADARERR_5211) + len += snprintf(buf+len, sizeof(buf)-len, " RADARERR-5211\n"); + else + len += snprintf(buf+len, sizeof(buf)-len, "\n"); + + if (len > sizeof(buf)) + len = sizeof(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_misc = { + .read = read_file_misc, + .open = ath5k_debugfs_open, + .owner = THIS_MODULE, +}; + /* debugfs: frameerrors */ @@ -856,6 +909,10 @@ ath5k_debug_init_device(struct ath5k_softc *sc) S_IWUSR | S_IRUSR, sc->debug.debugfs_phydir, sc, &fops_antenna); + sc->debug.debugfs_misc = debugfs_create_file("misc", + S_IRUSR, + sc->debug.debugfs_phydir, sc, &fops_misc); + sc->debug.debugfs_frameerrors = debugfs_create_file("frameerrors", S_IWUSR | S_IRUSR, sc->debug.debugfs_phydir, sc, @@ -886,6 +943,7 @@ ath5k_debug_finish_device(struct ath5k_softc *sc) debugfs_remove(sc->debug.debugfs_beacon); debugfs_remove(sc->debug.debugfs_reset); debugfs_remove(sc->debug.debugfs_antenna); + debugfs_remove(sc->debug.debugfs_misc); debugfs_remove(sc->debug.debugfs_frameerrors); debugfs_remove(sc->debug.debugfs_ani); debugfs_remove(sc->debug.debugfs_queue); diff --git a/drivers/net/wireless/ath/ath5k/debug.h b/drivers/net/wireless/ath/ath5k/debug.h index 9b22722a95f0..4f078b134015 100644 --- a/drivers/net/wireless/ath/ath5k/debug.h +++ b/drivers/net/wireless/ath/ath5k/debug.h @@ -75,6 +75,7 @@ struct ath5k_dbg_info { struct dentry *debugfs_beacon; struct dentry *debugfs_reset; struct dentry *debugfs_antenna; + struct dentry *debugfs_misc; struct dentry *debugfs_frameerrors; struct dentry *debugfs_ani; struct dentry *debugfs_queue; -- cgit v1.2.3 From 7a37081e2e25e58701b17c41579fd06bc353b392 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 22 Sep 2010 12:34:52 +0200 Subject: ath9k_hw: simplify revision checks for AR9280 Since AR9280 v1.0 was never sold (and the initvals removed), v1.0 specific revision checks can be removed and the 'v2.0 or later' check can be simplified to a check for AR9280 or later. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar5008_phy.c | 14 +++++++------- drivers/net/wireless/ath/ath9k/ar9002_calib.c | 6 +++--- drivers/net/wireless/ath/ath9k/ar9002_hw.c | 2 +- drivers/net/wireless/ath/ath9k/eeprom_4k.c | 4 ++-- drivers/net/wireless/ath/ath9k/eeprom_9287.c | 6 +++--- drivers/net/wireless/ath/ath9k/eeprom_def.c | 18 +++++++++--------- drivers/net/wireless/ath/ath9k/hw.c | 15 +++++++-------- drivers/net/wireless/ath/ath9k/init.c | 2 +- drivers/net/wireless/ath/ath9k/recv.c | 2 +- drivers/net/wireless/ath/ath9k/reg.h | 9 ++------- 10 files changed, 36 insertions(+), 42 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index 3d2c8679bc85..9318ae736982 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -118,7 +118,7 @@ static void ar5008_hw_force_bias(struct ath_hw *ah, u16 synth_freq) if (!AR_SREV_5416(ah) || synth_freq >= 3000) return; - BUG_ON(AR_SREV_9280_10_OR_LATER(ah)); + BUG_ON(AR_SREV_9280_20_OR_LATER(ah)); if (synth_freq < 2412) new_bias = 0; @@ -454,7 +454,7 @@ static int ar5008_hw_rf_alloc_ext_banks(struct ath_hw *ah) struct ath_common *common = ath9k_hw_common(ah); - BUG_ON(AR_SREV_9280_10_OR_LATER(ah)); + BUG_ON(AR_SREV_9280_20_OR_LATER(ah)); ATH_ALLOC_BANK(ah->analogBank0Data, ah->iniBank0.ia_rows); ATH_ALLOC_BANK(ah->analogBank1Data, ah->iniBank1.ia_rows); @@ -484,7 +484,7 @@ static void ar5008_hw_rf_free_ext_banks(struct ath_hw *ah) bank = NULL; \ } while (0); - BUG_ON(AR_SREV_9280_10_OR_LATER(ah)); + BUG_ON(AR_SREV_9280_20_OR_LATER(ah)); ATH_FREE_BANK(ah->analogBank0Data); ATH_FREE_BANK(ah->analogBank1Data); @@ -525,7 +525,7 @@ static bool ar5008_hw_set_rf_regs(struct ath_hw *ah, * for single chip devices, that is AR9280 or anything * after that. */ - if (AR_SREV_9280_10_OR_LATER(ah)) + if (AR_SREV_9280_20_OR_LATER(ah)) return true; /* Setup rf parameters */ @@ -663,7 +663,7 @@ static void ar5008_hw_override_ini(struct ath_hw *ah, */ REG_SET_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); - if (AR_SREV_9280_10_OR_LATER(ah)) { + if (AR_SREV_9280_20_OR_LATER(ah)) { val = REG_READ(ah, AR_PCU_MISC_MODE2); if (!AR_SREV_9271(ah)) @@ -676,7 +676,7 @@ static void ar5008_hw_override_ini(struct ath_hw *ah, } if (!AR_SREV_5416_20_OR_LATER(ah) || - AR_SREV_9280_10_OR_LATER(ah)) + AR_SREV_9280_20_OR_LATER(ah)) return; /* * Disable BB clock gating @@ -900,7 +900,7 @@ static void ar5008_hw_set_rfmode(struct ath_hw *ah, struct ath9k_channel *chan) rfMode |= (IS_CHAN_B(chan) || IS_CHAN_G(chan)) ? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM; - if (!AR_SREV_9280_10_OR_LATER(ah)) + if (!AR_SREV_9280_20_OR_LATER(ah)) rfMode |= (IS_CHAN_5GHZ(chan)) ? AR_PHY_MODE_RF5GHZ : AR_PHY_MODE_RF2GHZ; diff --git a/drivers/net/wireless/ath/ath9k/ar9002_calib.c b/drivers/net/wireless/ath/ath9k/ar9002_calib.c index fe7418aefc4a..dc64afeeaa12 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c @@ -841,7 +841,7 @@ static bool ar9002_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) if (!ar9285_hw_clc(ah, chan)) return false; } else { - if (AR_SREV_9280_10_OR_LATER(ah)) { + if (AR_SREV_9280_20_OR_LATER(ah)) { if (!AR_SREV_9287_10_OR_LATER(ah)) REG_CLR_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); @@ -864,7 +864,7 @@ static bool ar9002_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) return false; } - if (AR_SREV_9280_10_OR_LATER(ah)) { + if (AR_SREV_9280_20_OR_LATER(ah)) { if (!AR_SREV_9287_10_OR_LATER(ah)) REG_SET_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); @@ -976,7 +976,7 @@ static void ar9002_hw_init_cal_settings(struct ath_hw *ah) } if (AR_SREV_9160_10_OR_LATER(ah)) { - if (AR_SREV_9280_10_OR_LATER(ah)) { + if (AR_SREV_9280_20_OR_LATER(ah)) { ah->iq_caldata.calData = &iq_cal_single_sample; ah->adcgain_caldata.calData = &adc_gain_cal_single_sample; diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c index 94392daebaa0..fde45082a13b 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c @@ -569,7 +569,7 @@ void ar9002_hw_attach_ops(struct ath_hw *ah) ops->config_pci_powersave = ar9002_hw_configpcipowersave; ar5008_hw_attach_phy_ops(ah); - if (AR_SREV_9280_10_OR_LATER(ah)) + if (AR_SREV_9280_20_OR_LATER(ah)) ar9002_hw_attach_phy_ops(ah); ar9002_hw_attach_calib_ops(ah); diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c index ead8b0dd3b53..677e0c9d0481 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c @@ -333,7 +333,7 @@ static void ath9k_hw_get_4k_gain_boundaries_pdadcs(struct ath_hw *ah, } if (i == 0) { - if (AR_SREV_9280_10_OR_LATER(ah)) + if (AR_SREV_9280_20_OR_LATER(ah)) ss = (int16_t)(0 - (minPwrT4[i] / 2)); else ss = 0; @@ -761,7 +761,7 @@ static void ath9k_hw_4k_set_txpower(struct ath_hw *ah, regulatory->max_power_level = ratesArray[i]; - if (AR_SREV_9280_10_OR_LATER(ah)) { + if (AR_SREV_9280_20_OR_LATER(ah)) { for (i = 0; i < Ar5416RateSize; i++) ratesArray[i] -= AR5416_PWR_TABLE_OFFSET_DB * 2; } diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c index e6186515d05b..966b9496a9dd 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c @@ -324,7 +324,7 @@ static void ath9k_hw_get_ar9287_gain_boundaries_pdadcs(struct ath_hw *ah, minDelta = 0; if (i == 0) { - if (AR_SREV_9280_10_OR_LATER(ah)) + if (AR_SREV_9280_20_OR_LATER(ah)) ss = (int16_t)(0 - (minPwrT4[i] / 2)); else ss = 0; @@ -883,7 +883,7 @@ static void ath9k_hw_ar9287_set_txpower(struct ath_hw *ah, ratesArray[i] = AR9287_MAX_RATE_POWER; } - if (AR_SREV_9280_10_OR_LATER(ah)) { + if (AR_SREV_9280_20_OR_LATER(ah)) { for (i = 0; i < Ar5416RateSize; i++) ratesArray[i] -= AR9287_PWR_TABLE_OFFSET_DB * 2; } @@ -977,7 +977,7 @@ static void ath9k_hw_ar9287_set_txpower(struct ath_hw *ah, else i = rate6mb; - if (AR_SREV_9280_10_OR_LATER(ah)) + if (AR_SREV_9280_20_OR_LATER(ah)) regulatory->max_power_level = ratesArray[i] + AR9287_PWR_TABLE_OFFSET_DB * 2; else diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c index 23f480d4c770..76b4d65472dd 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_def.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c @@ -223,7 +223,7 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) } /* Enable fixup for AR_AN_TOP2 if necessary */ - if (AR_SREV_9280_10_OR_LATER(ah) && + if (AR_SREV_9280_20_OR_LATER(ah) && (eep->baseEepHeader.version & 0xff) > 0x0a && eep->baseEepHeader.pwdclkind == 0) ah->need_an_top2_fixup = 1; @@ -317,7 +317,7 @@ static void ath9k_hw_def_set_gain(struct ath_hw *ah, if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_3) { txRxAttenLocal = pModal->txRxAttenCh[i]; - if (AR_SREV_9280_10_OR_LATER(ah)) { + if (AR_SREV_9280_20_OR_LATER(ah)) { REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, pModal->bswMargin[i]); @@ -344,7 +344,7 @@ static void ath9k_hw_def_set_gain(struct ath_hw *ah, } } - if (AR_SREV_9280_10_OR_LATER(ah)) { + if (AR_SREV_9280_20_OR_LATER(ah)) { REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset, AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal); @@ -408,7 +408,7 @@ static void ath9k_hw_def_set_board_values(struct ath_hw *ah, regChainOffset, i); } - if (AR_SREV_9280_10_OR_LATER(ah)) { + if (AR_SREV_9280_20_OR_LATER(ah)) { if (IS_CHAN_2GHZ(chan)) { ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH0, AR_AN_RF2G1_CH0_OB, @@ -461,7 +461,7 @@ static void ath9k_hw_def_set_board_values(struct ath_hw *ah, REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_ADC, pModal->adcDesiredSize); - if (!AR_SREV_9280_10_OR_LATER(ah)) + if (!AR_SREV_9280_20_OR_LATER(ah)) REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_PGA, pModal->pgaDesiredSize); @@ -478,7 +478,7 @@ static void ath9k_hw_def_set_board_values(struct ath_hw *ah, REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON, pModal->txEndToRxOn); - if (AR_SREV_9280_10_OR_LATER(ah)) { + if (AR_SREV_9280_20_OR_LATER(ah)) { REG_RMW_FIELD(ah, AR_PHY_CCA, AR9280_PHY_CCA_THRESH62, pModal->thresh62); REG_RMW_FIELD(ah, AR_PHY_EXT_CCA0, @@ -696,7 +696,7 @@ static void ath9k_hw_get_def_gain_boundaries_pdadcs(struct ath_hw *ah, } if (i == 0) { - if (AR_SREV_9280_10_OR_LATER(ah)) + if (AR_SREV_9280_20_OR_LATER(ah)) ss = (int16_t)(0 - (minPwrT4[i] / 2)); else ss = 0; @@ -1291,7 +1291,7 @@ static void ath9k_hw_def_set_txpower(struct ath_hw *ah, ratesArray[i] = AR5416_MAX_RATE_POWER; } - if (AR_SREV_9280_10_OR_LATER(ah)) { + if (AR_SREV_9280_20_OR_LATER(ah)) { for (i = 0; i < Ar5416RateSize; i++) { int8_t pwr_table_offset; @@ -1395,7 +1395,7 @@ static void ath9k_hw_def_set_txpower(struct ath_hw *ah, else if (IS_CHAN_HT20(chan)) i = rateHt20_0; - if (AR_SREV_9280_10_OR_LATER(ah)) + if (AR_SREV_9280_20_OR_LATER(ah)) regulatory->max_power_level = ratesArray[i] + AR5416_PWR_TABLE_OFFSET_DB * 2; else diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 0b2ff98b6f33..f2255a211637 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -565,7 +565,7 @@ static int __ath9k_hw_init(struct ath_hw *ah) ath9k_hw_init_cal_settings(ah); ah->ani_function = ATH9K_ANI_ALL; - if (AR_SREV_9280_10_OR_LATER(ah) && !AR_SREV_9300_20_OR_LATER(ah)) + if (AR_SREV_9280_20_OR_LATER(ah) && !AR_SREV_9300_20_OR_LATER(ah)) ah->ani_function &= ~ATH9K_ANI_NOISE_IMMUNITY_LEVEL; if (!AR_SREV_9300_20_OR_LATER(ah)) ah->ani_function &= ~ATH9K_ANI_MRC_CCK; @@ -1312,7 +1312,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (tsf) ath9k_hw_settsf64(ah, tsf); - if (AR_SREV_9280_10_OR_LATER(ah)) + if (AR_SREV_9280_20_OR_LATER(ah)) REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_JTAG_DISABLE); if (!AR_SREV_9300_20_OR_LATER(ah)) @@ -1857,8 +1857,7 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) /* Use rx_chainmask from EEPROM. */ pCap->rx_chainmask = ah->eep_ops->get_eeprom(ah, EEP_RX_MASK); - if (!(AR_SREV_9280(ah) && (ah->hw_version.macRev == 0))) - ah->misc_mode |= AR_PCU_MIC_NEW_LOC_ENA; + ah->misc_mode |= AR_PCU_MIC_NEW_LOC_ENA; pCap->low_2ghz_chan = 2312; pCap->high_2ghz_chan = 2732; @@ -1896,7 +1895,7 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) pCap->num_gpio_pins = AR7010_NUM_GPIO; else if (AR_SREV_9285_10_OR_LATER(ah)) pCap->num_gpio_pins = AR9285_NUM_GPIO; - else if (AR_SREV_9280_10_OR_LATER(ah)) + else if (AR_SREV_9280_20_OR_LATER(ah)) pCap->num_gpio_pins = AR928X_NUM_GPIO; else pCap->num_gpio_pins = AR_NUM_GPIO; @@ -1953,7 +1952,7 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) pCap->num_antcfg_2ghz = ah->eep_ops->get_num_ant_config(ah, ATH9K_HAL_FREQ_BAND_2GHZ); - if (AR_SREV_9280_10_OR_LATER(ah) && + if (AR_SREV_9280_20_OR_LATER(ah) && ath9k_hw_btcoex_supported(ah)) { btcoex_hw->btactive_gpio = ATH_BTACTIVE_GPIO; btcoex_hw->wlanactive_gpio = ATH_WLANACTIVE_GPIO; @@ -2078,7 +2077,7 @@ u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio) return MS_REG_READ(AR9287, gpio) != 0; else if (AR_SREV_9285_10_OR_LATER(ah)) return MS_REG_READ(AR9285, gpio) != 0; - else if (AR_SREV_9280_10_OR_LATER(ah)) + else if (AR_SREV_9280_20_OR_LATER(ah)) return MS_REG_READ(AR928X, gpio) != 0; else return MS_REG_READ(AR, gpio) != 0; @@ -2575,7 +2574,7 @@ void ath9k_hw_name(struct ath_hw *ah, char *hw_name, size_t len) int used; /* chipsets >= AR9280 are single-chip */ - if (AR_SREV_9280_10_OR_LATER(ah)) { + if (AR_SREV_9280_20_OR_LATER(ah)) { used = snprintf(hw_name, len, "Atheros AR%s Rev:%x", ath9k_hw_mac_bb_name(ah->hw_version.macVersion), diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 573899e27b3d..de3393867e37 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -211,7 +211,7 @@ static void setup_ht_cap(struct ath_softc *sc, else max_streams = 2; - if (AR_SREV_9280_10_OR_LATER(ah)) { + if (AR_SREV_9280_20_OR_LATER(ah)) { if (max_streams >= 2) ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT); diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index c5e7af4f51ab..7b6f66bf3739 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -454,7 +454,7 @@ u32 ath_calcrxfilter(struct ath_softc *sc) else rfilt |= ATH9K_RX_FILTER_BEACON; - if ((AR_SREV_9280_10_OR_LATER(sc->sc_ah) || + if ((AR_SREV_9280_20_OR_LATER(sc->sc_ah) || AR_SREV_9285_10_OR_LATER(sc->sc_ah)) && (sc->sc_ah->opmode == NL80211_IFTYPE_AP) && (sc->rx.rxfilter & FIF_PSPOLL)) diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index d01c4adab8d6..cabfa0356c9c 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -819,15 +819,10 @@ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9160_11)) #define AR_SREV_9280(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9280)) -#define AR_SREV_9280_10_OR_LATER(_ah) \ +#define AR_SREV_9280_20_OR_LATER(_ah) \ (((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9280)) #define AR_SREV_9280_20(_ah) \ - (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9280) && \ - ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9280_20)) -#define AR_SREV_9280_20_OR_LATER(_ah) \ - (((_ah)->hw_version.macVersion > AR_SREV_VERSION_9280) || \ - (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9280) && \ - ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9280_20))) + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9280)) #define AR_SREV_9285(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9285)) -- cgit v1.2.3 From e17f83eafd37129f9e09425136e59bc4333bdb9c Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 22 Sep 2010 12:34:53 +0200 Subject: ath9k_hw: simplify revision checks for AR9285 Since AR9285 v1.0 and v1.1 were never sold (and the initvals removed), its revision checks can be simplified similar to AR9280 Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar5008_phy.c | 2 +- drivers/net/wireless/ath/ath9k/ar9002_calib.c | 11 +---------- drivers/net/wireless/ath/ath9k/eeprom_4k.c | 6 ------ drivers/net/wireless/ath/ath9k/hw.c | 8 ++++---- drivers/net/wireless/ath/ath9k/recv.c | 2 +- drivers/net/wireless/ath/ath9k/reg.h | 16 +--------------- 6 files changed, 8 insertions(+), 37 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index 9318ae736982..79dfe9dded52 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -701,7 +701,7 @@ static void ar5008_hw_set_channel_regs(struct ath_hw *ah, u32 phymode; u32 enableDacFifo = 0; - if (AR_SREV_9285_10_OR_LATER(ah)) + if (AR_SREV_9285_12_OR_LATER(ah)) enableDacFifo = (REG_READ(ah, AR_PHY_TURBO) & AR_PHY_FC_ENABLE_DAC_FIFO); diff --git a/drivers/net/wireless/ath/ath9k/ar9002_calib.c b/drivers/net/wireless/ath/ath9k/ar9002_calib.c index dc64afeeaa12..5d0815145b78 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c @@ -567,11 +567,6 @@ static inline void ar9285_hw_pa_cal(struct ath_hw *ah, bool is_reset) AR5416_EEP_TXGAIN_HIGH_POWER) return; - if (AR_SREV_9285_11(ah)) { - REG_WRITE(ah, AR9285_AN_TOP4, (AR9285_AN_TOP4_DEFAULT | 0x14)); - udelay(10); - } - for (i = 0; i < ARRAY_SIZE(regList); i++) regList[i][1] = REG_READ(ah, regList[i][0]); @@ -651,10 +646,6 @@ static inline void ar9285_hw_pa_cal(struct ath_hw *ah, bool is_reset) REG_WRITE(ah, regList[i][0], regList[i][1]); REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_CCOMP, ccomp_org); - - if (AR_SREV_9285_11(ah)) - REG_WRITE(ah, AR9285_AN_TOP4, AR9285_AN_TOP4_DEFAULT); - } static void ar9002_hw_pa_cal(struct ath_hw *ah, bool is_reset) @@ -664,7 +655,7 @@ static void ar9002_hw_pa_cal(struct ath_hw *ah, bool is_reset) ar9271_hw_pa_cal(ah, is_reset); else ah->pacal_info.skipcount--; - } else if (AR_SREV_9285_11_OR_LATER(ah)) { + } else if (AR_SREV_9285_12_OR_LATER(ah)) { if (is_reset || !ah->pacal_info.skipcount) ar9285_hw_pa_cal(ah, is_reset); else diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c index 677e0c9d0481..d6eed1f02e84 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c @@ -909,9 +909,6 @@ static void ath9k_hw_4k_set_gain(struct ath_hw *ah, AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal); REG_RMW_FIELD(ah, AR_PHY_RXGAIN + 0x1000, AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[0]); - - if (AR_SREV_9285_11(ah)) - REG_WRITE(ah, AR9285_AN_TOP4, (AR9285_AN_TOP4_DEFAULT | 0x14)); } /* @@ -1109,9 +1106,6 @@ static void ath9k_hw_4k_set_board_values(struct ath_hw *ah, } - if (AR_SREV_9285_11(ah)) - REG_WRITE(ah, AR9285_AN_TOP4, AR9285_AN_TOP4_DEFAULT); - REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH, pModal->switchSettling); REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_ADC, diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index f2255a211637..1f41b4513c52 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1190,7 +1190,7 @@ bool ath9k_hw_check_alive(struct ath_hw *ah) int count = 50; u32 reg; - if (AR_SREV_9285_10_OR_LATER(ah)) + if (AR_SREV_9285_12_OR_LATER(ah)) return true; do { @@ -1787,7 +1787,7 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) regulatory->current_rd = eeval; eeval = ah->eep_ops->get_eeprom(ah, EEP_REG_1); - if (AR_SREV_9285_10_OR_LATER(ah)) + if (AR_SREV_9285_12_OR_LATER(ah)) eeval |= AR9285_RDEXT_DEFAULT; regulatory->current_rd_ext = eeval; @@ -1893,7 +1893,7 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) pCap->num_gpio_pins = AR9271_NUM_GPIO; else if (AR_DEVID_7010(ah)) pCap->num_gpio_pins = AR7010_NUM_GPIO; - else if (AR_SREV_9285_10_OR_LATER(ah)) + else if (AR_SREV_9285_12_OR_LATER(ah)) pCap->num_gpio_pins = AR9285_NUM_GPIO; else if (AR_SREV_9280_20_OR_LATER(ah)) pCap->num_gpio_pins = AR928X_NUM_GPIO; @@ -2075,7 +2075,7 @@ u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio) return MS_REG_READ(AR9271, gpio) != 0; else if (AR_SREV_9287_10_OR_LATER(ah)) return MS_REG_READ(AR9287, gpio) != 0; - else if (AR_SREV_9285_10_OR_LATER(ah)) + else if (AR_SREV_9285_12_OR_LATER(ah)) return MS_REG_READ(AR9285, gpio) != 0; else if (AR_SREV_9280_20_OR_LATER(ah)) return MS_REG_READ(AR928X, gpio) != 0; diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 7b6f66bf3739..00140489becb 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -455,7 +455,7 @@ u32 ath_calcrxfilter(struct ath_softc *sc) rfilt |= ATH9K_RX_FILTER_BEACON; if ((AR_SREV_9280_20_OR_LATER(sc->sc_ah) || - AR_SREV_9285_10_OR_LATER(sc->sc_ah)) && + AR_SREV_9285_12_OR_LATER(sc->sc_ah)) && (sc->sc_ah->opmode == NL80211_IFTYPE_AP) && (sc->rx.rxfilter & FIF_PSPOLL)) rfilt |= ATH9K_RX_FILTER_PSPOLL; diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index cabfa0356c9c..7ff814d4a792 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -826,22 +826,8 @@ #define AR_SREV_9285(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9285)) -#define AR_SREV_9285_10_OR_LATER(_ah) \ - (((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9285)) -#define AR_SREV_9285_11(_ah) \ - (AR_SREV_9285(ah) && \ - ((_ah)->hw_version.macRev == AR_SREV_REVISION_9285_11)) -#define AR_SREV_9285_11_OR_LATER(_ah) \ - (((_ah)->hw_version.macVersion > AR_SREV_VERSION_9285) || \ - (AR_SREV_9285(ah) && ((_ah)->hw_version.macRev >= \ - AR_SREV_REVISION_9285_11))) -#define AR_SREV_9285_12(_ah) \ - (AR_SREV_9285(ah) && \ - ((_ah)->hw_version.macRev == AR_SREV_REVISION_9285_12)) #define AR_SREV_9285_12_OR_LATER(_ah) \ - (((_ah)->hw_version.macVersion > AR_SREV_VERSION_9285) || \ - (AR_SREV_9285(ah) && ((_ah)->hw_version.macRev >= \ - AR_SREV_REVISION_9285_12))) + (((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9285)) #define AR_SREV_9287(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9287)) -- cgit v1.2.3 From a42acef0dd3548ffda03d245d41b95250354017e Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 22 Sep 2010 12:34:54 +0200 Subject: ath9k_hw: simplify revision checks for AR9287 Since AR9287 v1.0 was never sold (and the initvals removed), its revision checks can be simplified similar to AR9280 Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar5008_phy.c | 6 +++--- drivers/net/wireless/ath/ath9k/ar9002_calib.c | 4 ++-- drivers/net/wireless/ath/ath9k/eeprom.h | 2 +- drivers/net/wireless/ath/ath9k/hw.c | 4 ++-- drivers/net/wireless/ath/ath9k/reg.h | 9 +-------- 5 files changed, 9 insertions(+), 16 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index 79dfe9dded52..525671f52b45 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -669,7 +669,7 @@ static void ar5008_hw_override_ini(struct ath_hw *ah, if (!AR_SREV_9271(ah)) val &= ~AR_PCU_MISC_MODE2_HWWAR1; - if (AR_SREV_9287_10_OR_LATER(ah)) + if (AR_SREV_9287_11_OR_LATER(ah)) val = val & (~AR_PCU_MISC_MODE2_HWWAR2); REG_WRITE(ah, AR_PCU_MISC_MODE2, val); @@ -820,11 +820,11 @@ static int ar5008_hw_process_ini(struct ath_hw *ah, REGWRITE_BUFFER_FLUSH(ah); DISABLE_REGWRITE_BUFFER(ah); - if (AR_SREV_9280(ah) || AR_SREV_9287_10_OR_LATER(ah)) + if (AR_SREV_9280(ah) || AR_SREV_9287_11_OR_LATER(ah)) REG_WRITE_ARRAY(&ah->iniModesRxGain, modesIndex, regWrites); if (AR_SREV_9280(ah) || AR_SREV_9285_12_OR_LATER(ah) || - AR_SREV_9287_10_OR_LATER(ah)) + AR_SREV_9287_11_OR_LATER(ah)) REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites); if (AR_SREV_9271_10(ah)) diff --git a/drivers/net/wireless/ath/ath9k/ar9002_calib.c b/drivers/net/wireless/ath/ath9k/ar9002_calib.c index 5d0815145b78..d7d1d55362e6 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c @@ -833,7 +833,7 @@ static bool ar9002_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) return false; } else { if (AR_SREV_9280_20_OR_LATER(ah)) { - if (!AR_SREV_9287_10_OR_LATER(ah)) + if (!AR_SREV_9287_11_OR_LATER(ah)) REG_CLR_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, @@ -856,7 +856,7 @@ static bool ar9002_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) } if (AR_SREV_9280_20_OR_LATER(ah)) { - if (!AR_SREV_9287_10_OR_LATER(ah)) + if (!AR_SREV_9287_11_OR_LATER(ah)) REG_SET_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, diff --git a/drivers/net/wireless/ath/ath9k/eeprom.h b/drivers/net/wireless/ath/ath9k/eeprom.h index 3030564a0f21..dacb45e1b906 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.h +++ b/drivers/net/wireless/ath/ath9k/eeprom.h @@ -101,7 +101,7 @@ #define AR5416_VER_MASK (eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) #define OLC_FOR_AR9280_20_LATER (AR_SREV_9280_20_OR_LATER(ah) && \ ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL)) -#define OLC_FOR_AR9287_10_LATER (AR_SREV_9287_10_OR_LATER(ah) && \ +#define OLC_FOR_AR9287_10_LATER (AR_SREV_9287_11_OR_LATER(ah) && \ ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL)) #define AR_EEPROM_RFSILENT_GPIO_SEL 0x001c diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 1f41b4513c52..25ed65ac992c 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1989,7 +1989,7 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) if (AR_SREV_9300_20_OR_LATER(ah)) pCap->hw_caps |= ATH9K_HW_CAP_RAC_SUPPORTED; - if (AR_SREV_9287_10_OR_LATER(ah) || AR_SREV_9271(ah)) + if (AR_SREV_9287_11_OR_LATER(ah) || AR_SREV_9271(ah)) pCap->hw_caps |= ATH9K_HW_CAP_SGI_20; if (AR_SREV_9285(ah)) @@ -2073,7 +2073,7 @@ u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio) return MS_REG_READ(AR9300, gpio) != 0; else if (AR_SREV_9271(ah)) return MS_REG_READ(AR9271, gpio) != 0; - else if (AR_SREV_9287_10_OR_LATER(ah)) + else if (AR_SREV_9287_11_OR_LATER(ah)) return MS_REG_READ(AR9287, gpio) != 0; else if (AR_SREV_9285_12_OR_LATER(ah)) return MS_REG_READ(AR9285, gpio) != 0; diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index 7ff814d4a792..6d01e501b9b4 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -831,18 +831,11 @@ #define AR_SREV_9287(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9287)) -#define AR_SREV_9287_10_OR_LATER(_ah) \ +#define AR_SREV_9287_11_OR_LATER(_ah) \ (((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9287)) -#define AR_SREV_9287_10(_ah) \ - (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9287) && \ - ((_ah)->hw_version.macRev == AR_SREV_REVISION_9287_10)) #define AR_SREV_9287_11(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9287) && \ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9287_11)) -#define AR_SREV_9287_11_OR_LATER(_ah) \ - (((_ah)->hw_version.macVersion > AR_SREV_VERSION_9287) || \ - (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9287) && \ - ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9287_11))) #define AR_SREV_9287_12(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9287) && \ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9287_12)) -- cgit v1.2.3 From 686b9cb994f5f74be790df4cd12873dfdc8a6984 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Thu, 23 Sep 2010 09:44:36 -0700 Subject: mac80211/ath9k: Support AMPDU with multiple VIFs. The old ieee80211_find_sta_by_hw method didn't properly find VIFS when there was more than one per AP. This caused AMPDU logic in ath9k to get the wrong VIF when trying to account for transmitted SKBs. This patch changes ieee80211_find_sta_by_hw to take a localaddr argument to distinguish between VIFs with the same AP but different local addresses. The method name is changed to ieee80211_find_sta_by_ifaddr. Signed-off-by: Ben Greear Acked-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/recv.c | 6 +++++- drivers/net/wireless/ath/ath9k/xmit.c | 3 +-- include/net/mac80211.h | 25 ++++++++++++++----------- net/mac80211/sta_info.c | 15 +++++++++++---- 4 files changed, 31 insertions(+), 18 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 00140489becb..9c166f3804ab 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -977,7 +977,11 @@ static void ath9k_process_rssi(struct ath_common *common, * at least one sdata of a wiphy on mac80211 but with ath9k virtual * wiphy you'd have to iterate over every wiphy and each sdata. */ - sta = ieee80211_find_sta_by_hw(hw, hdr->addr2); + if (is_multicast_ether_addr(hdr->addr1)) + sta = ieee80211_find_sta_by_ifaddr(hw, hdr->addr2, NULL); + else + sta = ieee80211_find_sta_by_ifaddr(hw, hdr->addr2, hdr->addr1); + if (sta) { an = (struct ath_node *) sta->drv_priv; if (rx_stats->rs_rssi != ATH9K_RSSI_BAD && diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 85a7323a04ef..f7da6b20a925 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -328,8 +328,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, rcu_read_lock(); - /* XXX: use ieee80211_find_sta! */ - sta = ieee80211_find_sta_by_hw(hw, hdr->addr1); + sta = ieee80211_find_sta_by_ifaddr(hw, hdr->addr1, hdr->addr2); if (!sta) { rcu_read_unlock(); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 5d1187d7c5e5..73469d8b64bb 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2432,25 +2432,28 @@ struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_vif *vif, const u8 *addr); /** - * ieee80211_find_sta_by_hw - find a station on hardware + * ieee80211_find_sta_by_ifaddr - find a station on hardware * * @hw: pointer as obtained from ieee80211_alloc_hw() - * @addr: station's address + * @addr: remote station's address + * @localaddr: local address (vif->sdata->vif.addr). Use NULL for 'any'. * * This function must be called under RCU lock and the * resulting pointer is only valid under RCU lock as well. * - * NOTE: This function should not be used! When mac80211 is converted - * internally to properly keep track of stations on multiple - * virtual interfaces, it will not always know which station to - * return here since a single address might be used by multiple - * logical stations (e.g. consider a station connecting to another - * BSSID on the same AP hardware without disconnecting first). + * NOTE: You may pass NULL for localaddr, but then you will just get + * the first STA that matches the remote address 'addr'. + * We can have multiple STA associated with multiple + * logical stations (e.g. consider a station connecting to another + * BSSID on the same AP hardware without disconnecting first). + * In this case, the result of this method with localaddr NULL + * is not reliable. * - * DO NOT USE THIS FUNCTION. + * DO NOT USE THIS FUNCTION with localaddr NULL if at all possible. */ -struct ieee80211_sta *ieee80211_find_sta_by_hw(struct ieee80211_hw *hw, - const u8 *addr); +struct ieee80211_sta *ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw, + const u8 *addr, + const u8 *localaddr); /** * ieee80211_sta_block_awake - block station from waking up diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 44e10a9de0a7..ca2cba9cea87 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -838,13 +838,20 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, mutex_unlock(&local->sta_mtx); } -struct ieee80211_sta *ieee80211_find_sta_by_hw(struct ieee80211_hw *hw, - const u8 *addr) +struct ieee80211_sta *ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw, + const u8 *addr, + const u8 *localaddr) { struct sta_info *sta, *nxt; - /* Just return a random station ... first in list ... */ + /* + * Just return a random station if localaddr is NULL + * ... first in list. + */ for_each_sta_info(hw_to_local(hw), addr, sta, nxt) { + if (localaddr && + compare_ether_addr(sta->sdata->vif.addr, localaddr) != 0) + continue; if (!sta->uploaded) return NULL; return &sta->sta; @@ -852,7 +859,7 @@ struct ieee80211_sta *ieee80211_find_sta_by_hw(struct ieee80211_hw *hw, return NULL; } -EXPORT_SYMBOL_GPL(ieee80211_find_sta_by_hw); +EXPORT_SYMBOL_GPL(ieee80211_find_sta_by_ifaddr); struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_vif *vif, const u8 *addr) -- cgit v1.2.3 From e2b626248b0799bd14be40ce290c6681a8419512 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Fri, 24 Sep 2010 17:58:50 +0530 Subject: ath9k_htc: Fix TKIP disconnect failure with HTC drivers The following commit removed splitmic. But forgot to add ATH_CRYPT_CAP_MIC_COMBINED flag for HTC drivers which causes TKIP to fail. Author: Bruno Randolf Date: Wed Sep 8 16:04:54 2010 +0900 ath/ath9k: Replace common->splitmic with a flag Replace common->splitmic with ATH_CRYPT_CAP_MIC_COMBINED flag. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/htc_drv_init.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index 33850c952314..b100db2766cf 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -561,6 +561,9 @@ static void ath9k_init_crypto(struct ath9k_htc_priv *priv) common->keymax = ATH_KEYMAX; } + if (priv->ah->misc_mode & AR_PCU_MIC_NEW_LOC_ENA) + common->crypt_caps |= ATH_CRYPT_CAP_MIC_COMBINED; + /* * Reset the key cache since some parts do not * reset the contents on initial power up. -- cgit v1.2.3 From 793e721d21fd7d2825e3b7e56ff5009ec6d052e9 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sun, 26 Sep 2010 21:28:51 +0200 Subject: carl9170: remove non-standard ba session teardown 802.11n-2009 demands in 11.2.1: " When a STA enters normal (non-APSD) PS mode, any downlink Block ACK agreement without an associated schedule is suspended for the duration of this PS mode." The operative word is "suspended" and not terminated. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/main.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 43de9dfa5820..d28b4ff8d281 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1453,9 +1453,6 @@ static void carl9170_op_sta_notify(struct ieee80211_hw *hw, while ((skb = __skb_dequeue(&tid_info->queue))) __skb_queue_tail(&free, skb); spin_unlock_bh(&tid_info->lock); - - ieee80211_stop_tx_ba_session(sta, - tid_info->tid); } rcu_read_unlock(); } -- cgit v1.2.3 From b4c85d459c3a2c539a2d5885b12372e404bafc6b Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sun, 26 Sep 2010 21:40:40 +0200 Subject: carl9170: angle brackets for wiki link "The convention seems to be angle brackets around URLS in Kconfig." -- Finn Thain (to update web addresses in the kernel) Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/Kconfig b/drivers/net/wireless/ath/carl9170/Kconfig index c5d3a3f2e55b..2d1b821b440d 100644 --- a/drivers/net/wireless/ath/carl9170/Kconfig +++ b/drivers/net/wireless/ath/carl9170/Kconfig @@ -10,7 +10,7 @@ config CARL9170 but it needs a special firmware (carl9170-1.fw) to do that. The firmware can be downloaded from our wiki here: - http://wireless.kernel.org/en/users/Drivers/carl9170 + If you choose to build a module, it'll be called carl9170. -- cgit v1.2.3 From 042c53f6e84896701b67012ad525bc44ea6de25f Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sun, 26 Sep 2010 21:48:31 +0200 Subject: carl9170: remove lost-frame workaround This patch removes some cruft, which survived the RFC review phase. Originally, carl9170_tx_ampdu_queue erroneously dropped a lot of frames. As a result the ampdu scheduler bogged down quite frequently and the affected BA session timed out. However this bug has been fixed and the WA and its debugfs counter is no longer useful. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/carl9170.h | 1 - drivers/net/wireless/ath/carl9170/debug.c | 4 --- drivers/net/wireless/ath/carl9170/tx.c | 37 ++-------------------------- 3 files changed, 2 insertions(+), 40 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h index d7c5482d74ce..20f2a77e54d2 100644 --- a/drivers/net/wireless/ath/carl9170/carl9170.h +++ b/drivers/net/wireless/ath/carl9170/carl9170.h @@ -364,7 +364,6 @@ struct ar9170 { unsigned int tx_dropped; unsigned int tx_ack_failures; unsigned int tx_fcs_errors; - unsigned int tx_ampdu_timeout; unsigned int rx_dropped; /* EEPROM */ diff --git a/drivers/net/wireless/ath/carl9170/debug.c b/drivers/net/wireless/ath/carl9170/debug.c index 19b48369ffed..0ac1124c2a0b 100644 --- a/drivers/net/wireless/ath/carl9170/debug.c +++ b/drivers/net/wireless/ath/carl9170/debug.c @@ -798,8 +798,6 @@ DEBUGFS_READONLY_FILE(tx_total_queued, 20, "%d", atomic_read(&ar->tx_total_queued)); DEBUGFS_READONLY_FILE(tx_ampdu_scheduler, 20, "%d", atomic_read(&ar->tx_ampdu_scheduler)); -DEBUGFS_READONLY_FILE(tx_ampdu_timeout, 20, "%d", - ar->tx_ampdu_timeout); DEBUGFS_READONLY_FILE(tx_total_pending, 20, "%d", atomic_read(&ar->tx_total_pending)); @@ -872,8 +870,6 @@ void carl9170_debugfs_register(struct ar9170 *ar) DEBUGFS_ADD(ampdu_density); DEBUGFS_ADD(ampdu_factor); - DEBUGFS_ADD(tx_ampdu_timeout); - DEBUGFS_ADD(tx_janitor_last_run); DEBUGFS_ADD(tx_status_0); diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c index e0d2374e0c77..e6be5e6c1723 100644 --- a/drivers/net/wireless/ath/carl9170/tx.c +++ b/drivers/net/wireless/ath/carl9170/tx.c @@ -1042,41 +1042,8 @@ retry: queue = TID_TO_WME_AC(tid_info->tid); spin_lock_bh(&tid_info->lock); - if (tid_info->state != CARL9170_TID_STATE_XMIT) { - first = skb_peek(&tid_info->queue); - if (first) { - struct ieee80211_tx_info *txinfo; - struct carl9170_tx_info *arinfo; - - txinfo = IEEE80211_SKB_CB(first); - arinfo = (void *) txinfo->rate_driver_data; - - if (time_is_after_jiffies(arinfo->timeout + - msecs_to_jiffies(CARL9170_QUEUE_TIMEOUT)) - == true) - goto processed; - - /* - * We've been waiting for the frame which - * matches "snx" (start sequence of the - * next aggregate) for some time now. - * - * But it never arrived. Therefore - * jump to the next available frame - * and kick-start the transmission. - * - * Note: This might induce odd latency - * spikes because the receiver will be - * waiting for the lost frame too. - */ - ar->tx_ampdu_timeout++; - - tid_info->snx = carl9170_get_seq(first); - tid_info->state = CARL9170_TID_STATE_XMIT; - } else { - goto processed; - } - } + if (tid_info->state != CARL9170_TID_STATE_XMIT) + goto processed; tid_info->counter++; first = skb_peek(&tid_info->queue); -- cgit v1.2.3 From cb139ecc0c030e8ccc736bd86cfef502c200825a Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sun, 26 Sep 2010 22:49:34 +0200 Subject: carl9170: fix tx_ampdu_upload counter tx_ampdu_upload was not decreased when an a-MPDU frame had to be kicked out from the tx_pending queues. This broke ampdu aggregation, because the scheduler waits until tx_ampdu_upload drops to zero, before making the next aggregate. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/main.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index d28b4ff8d281..a8b0cec78b32 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -230,8 +230,15 @@ static void carl9170_flush(struct ar9170 *ar, bool drop_queued) for (i = 0; i < ar->hw->queues; i++) { struct sk_buff *skb; - while ((skb = skb_dequeue(&ar->tx_pending[i]))) + while ((skb = skb_dequeue(&ar->tx_pending[i]))) { + struct ieee80211_tx_info *info; + + info = IEEE80211_SKB_CB(skb); + if (info->flags & IEEE80211_TX_CTL_AMPDU) + atomic_dec(&ar->tx_ampdu_upload); + carl9170_tx_status(ar, skb, false); + } } } @@ -1462,6 +1469,7 @@ static void carl9170_op_sta_notify(struct ieee80211_hw *hw, skb_queue_walk_safe(&ar->tx_pending[i], skb, tmp) { struct _carl9170_tx_superframe *super; struct ieee80211_hdr *hdr; + struct ieee80211_tx_info *info; super = (void *) skb->data; hdr = (void *) super->frame_data; @@ -1470,6 +1478,11 @@ static void carl9170_op_sta_notify(struct ieee80211_hw *hw, continue; __skb_unlink(skb, &ar->tx_pending[i]); + + info = IEEE80211_SKB_CB(skb); + if (info->flags & IEEE80211_TX_CTL_AMPDU) + atomic_dec(&ar->tx_ampdu_upload); + carl9170_tx_status(ar, skb, false); } spin_unlock_bh(&ar->tx_pending[i].lock); -- cgit v1.2.3 From 9c655c8be9053a65886ac3e06420399a9bfdbd70 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sun, 26 Sep 2010 23:06:56 +0200 Subject: carl9170: fix WARN_ON triggered by Broadcom HT STAs Broadcom's Windows driver for the 4313 advertises an ampdu density of 7 => 16 us. The AR9170 MAC on the other hand only supports densities up to 8 us. This patch removes the noisy WARN_ON, because there is nothing we can do about it. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index a8b0cec78b32..84bd38e9961c 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1248,7 +1248,7 @@ static int carl9170_op_ampdu_action(struct ieee80211_hw *hw, switch (action) { case IEEE80211_AMPDU_TX_START: - if (WARN_ON_ONCE(!sta_info->ht_sta)) + if (!sta_info->ht_sta) return -EOPNOTSUPP; rcu_read_lock(); -- cgit v1.2.3 From 4bd437ea40b81fb4c047034de6dca1b5af496fb0 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Mon, 27 Sep 2010 01:36:38 +0200 Subject: carl9170: revamp carl9170_tx_prepare David Miller complained about the driver's excessive use of variables in __packed structs. While I did not fully agree with his sole "performance" argument on all accounts. I do see some room for improvement in hot-paths on architectures without an efficient access to unaligned elements. This first patch (dare I say?) optimizes an important tx hot-path in the driver: carl9170_tx_prepare. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/tx.c | 155 ++++++++++++++++----------------- 1 file changed, 75 insertions(+), 80 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c index e6be5e6c1723..b575c865142d 100644 --- a/drivers/net/wireless/ath/carl9170/tx.c +++ b/drivers/net/wireless/ath/carl9170/tx.c @@ -760,8 +760,8 @@ static int carl9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb) struct carl9170_tx_info *arinfo; unsigned int hw_queue; int i; - u16 keytype = 0; - u16 len, icv = 0; + __le16 mac_tmp; + u16 len; bool ampdu, no_ack; BUILD_BUG_ON(sizeof(*arinfo) > sizeof(info->rate_driver_data)); @@ -773,6 +773,10 @@ static int carl9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb) BUILD_BUG_ON(IEEE80211_TX_MAX_RATES < CARL9170_TX_MAX_RATES); + BUILD_BUG_ON(AR9170_MAX_VIRTUAL_MAC > + ((CARL9170_TX_SUPER_MISC_VIF_ID >> + CARL9170_TX_SUPER_MISC_VIF_ID_S) + 1)); + hw_queue = ar9170_qmap[carl9170_get_queue(ar, skb)]; hdr = (void *)skb->data; @@ -793,20 +797,37 @@ static int carl9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb) txc = (void *)skb_push(skb, sizeof(*txc)); memset(txc, 0, sizeof(*txc)); - ampdu = !!(info->flags & IEEE80211_TX_CTL_AMPDU); + SET_VAL(CARL9170_TX_SUPER_MISC_QUEUE, txc->s.misc, hw_queue); + + if (likely(cvif)) + SET_VAL(CARL9170_TX_SUPER_MISC_VIF_ID, txc->s.misc, cvif->id); + + if (unlikely(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM)) + txc->s.misc |= CARL9170_TX_SUPER_MISC_CAB; + + if (unlikely(ieee80211_is_probe_resp(hdr->frame_control))) + txc->s.misc |= CARL9170_TX_SUPER_MISC_FILL_IN_TSF; + + mac_tmp = cpu_to_le16(AR9170_TX_MAC_HW_DURATION | + AR9170_TX_MAC_BACKOFF); + mac_tmp |= cpu_to_le16((hw_queue << AR9170_TX_MAC_QOS_S) && + AR9170_TX_MAC_QOS); + no_ack = !!(info->flags & IEEE80211_TX_CTL_NO_ACK); + if (unlikely(no_ack)) + mac_tmp |= cpu_to_le16(AR9170_TX_MAC_NO_ACK); if (info->control.hw_key) { - icv = info->control.hw_key->icv_len; + len += info->control.hw_key->icv_len; switch (info->control.hw_key->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: case WLAN_CIPHER_SUITE_TKIP: - keytype = AR9170_TX_MAC_ENCR_RC4; + mac_tmp |= cpu_to_le16(AR9170_TX_MAC_ENCR_RC4); break; case WLAN_CIPHER_SUITE_CCMP: - keytype = AR9170_TX_MAC_ENCR_AES; + mac_tmp |= cpu_to_le16(AR9170_TX_MAC_ENCR_AES); break; default: WARN_ON(1); @@ -814,48 +835,58 @@ static int carl9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb) } } - BUILD_BUG_ON(AR9170_MAX_VIRTUAL_MAC > - ((CARL9170_TX_SUPER_MISC_VIF_ID >> - CARL9170_TX_SUPER_MISC_VIF_ID_S) + 1)); - - txc->s.len = cpu_to_le16(len + sizeof(*txc)); - txc->f.length = cpu_to_le16(len + icv + 4); - SET_VAL(CARL9170_TX_SUPER_MISC_VIF_ID, txc->s.misc, - cvif ? cvif->id : 0); + ampdu = !!(info->flags & IEEE80211_TX_CTL_AMPDU); + if (ampdu) { + unsigned int density, factor; - txc->f.mac_control = cpu_to_le16(AR9170_TX_MAC_HW_DURATION | - AR9170_TX_MAC_BACKOFF); + if (unlikely(!sta || !cvif)) + goto err_out; - SET_VAL(CARL9170_TX_SUPER_MISC_QUEUE, txc->s.misc, hw_queue); + factor = min_t(unsigned int, 1u, + info->control.sta->ht_cap.ampdu_factor); - txc->f.mac_control |= cpu_to_le16(hw_queue << AR9170_TX_MAC_QOS_S); - txc->f.mac_control |= cpu_to_le16(keytype); - txc->f.phy_control = cpu_to_le32(0); + density = info->control.sta->ht_cap.ampdu_density; - if (no_ack) - txc->f.mac_control |= cpu_to_le16(AR9170_TX_MAC_NO_ACK); + if (density) { + /* + * Watch out! + * + * Otus uses slightly different density values than + * those from the 802.11n spec. + */ - if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) - txc->s.misc |= CARL9170_TX_SUPER_MISC_CAB; + density = max_t(unsigned int, density + 1, 7u); + } - txrate = &info->control.rates[0]; - if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack)) - txc->f.mac_control |= cpu_to_le16(AR9170_TX_MAC_PROT_RTS); - else if (carl9170_tx_cts_check(ar, txrate)) - txc->f.mac_control |= cpu_to_le16(AR9170_TX_MAC_PROT_CTS); + SET_VAL(CARL9170_TX_SUPER_AMPDU_DENSITY, + txc->s.ampdu_settings, density); - SET_VAL(CARL9170_TX_SUPER_RI_TRIES, txc->s.ri[0], txrate->count); - txc->f.phy_control |= carl9170_tx_physet(ar, info, txrate); + SET_VAL(CARL9170_TX_SUPER_AMPDU_FACTOR, + txc->s.ampdu_settings, factor); - if (info->flags & IEEE80211_TX_CTL_AMPDU) { - for (i = 1; i < CARL9170_TX_MAX_RATES; i++) { + for (i = 0; i < CARL9170_TX_MAX_RATES; i++) { txrate = &info->control.rates[i]; - if (txrate->idx >= 0) + if (txrate->idx >= 0) { + txc->s.ri[i] = + CARL9170_TX_SUPER_RI_AMPDU; + + if (WARN_ON(!(txrate->flags & + IEEE80211_TX_RC_MCS))) { + /* + * Not sure if it's even possible + * to aggregate non-ht rates with + * this HW. + */ + goto err_out; + } continue; + } txrate->idx = 0; txrate->count = ar->hw->max_rate_tries; } + + mac_tmp |= cpu_to_le16(AR9170_TX_MAC_AGGR); } /* @@ -878,57 +909,21 @@ static int carl9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb) txc->s.ri[i] |= (AR9170_TX_MAC_PROT_CTS << CARL9170_TX_SUPER_RI_ERP_PROT_S); - /* - * unaggregated fallback, in case aggregation - * proves to be unsuccessful and unreliable. - */ - if (ampdu && i < 3) - txc->s.ri[i] |= CARL9170_TX_SUPER_RI_AMPDU; - txc->s.rr[i - 1] = carl9170_tx_physet(ar, info, txrate); } - if (ieee80211_is_probe_resp(hdr->frame_control)) - txc->s.misc |= CARL9170_TX_SUPER_MISC_FILL_IN_TSF; - - if (ampdu) { - unsigned int density, factor; - - if (unlikely(!sta || !cvif)) - goto err_out; - - density = info->control.sta->ht_cap.ampdu_density; - factor = info->control.sta->ht_cap.ampdu_factor; - - if (density) { - /* - * Watch out! - * - * Otus uses slightly different density values than - * those from the 802.11n spec. - */ - - density = max_t(unsigned int, density + 1, 7u); - } - - factor = min_t(unsigned int, 1u, factor); - - SET_VAL(CARL9170_TX_SUPER_AMPDU_DENSITY, - txc->s.ampdu_settings, density); + txrate = &info->control.rates[0]; + SET_VAL(CARL9170_TX_SUPER_RI_TRIES, txc->s.ri[0], txrate->count); - SET_VAL(CARL9170_TX_SUPER_AMPDU_FACTOR, - txc->s.ampdu_settings, factor); + if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack)) + mac_tmp |= cpu_to_le16(AR9170_TX_MAC_PROT_RTS); + else if (carl9170_tx_cts_check(ar, txrate)) + mac_tmp |= cpu_to_le16(AR9170_TX_MAC_PROT_CTS); - if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS) { - txc->f.mac_control |= cpu_to_le16(AR9170_TX_MAC_AGGR); - } else { - /* - * Not sure if it's even possible to aggregate - * non-ht rates with this HW. - */ - WARN_ON_ONCE(1); - } - } + txc->s.len = cpu_to_le16(skb->len); + txc->f.length = cpu_to_le16(len + FCS_LEN); + txc->f.mac_control = mac_tmp; + txc->f.phy_control = carl9170_tx_physet(ar, info, txrate); arinfo = (void *)info->rate_driver_data; arinfo->timeout = jiffies; -- cgit v1.2.3 From 4a79f2c517cce31c3b25aab0ec5078368b22c363 Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Mon, 27 Sep 2010 12:22:16 +0900 Subject: ath5k: Remove unused variable for atim window It's not used and it's unlikely we will ever implement ATIM. Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/ath5k.h | 1 - drivers/net/wireless/ath/ath5k/attach.c | 1 - drivers/net/wireless/ath/ath5k/pcu.c | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index b96bb985b56d..42e6e821259e 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -1041,7 +1041,6 @@ struct ath5k_hw { #define ah_modes ah_capabilities.cap_mode #define ah_ee_version ah_capabilities.cap_eeprom.ee_version - u32 ah_atim_window; u32 ah_limit_tx_retries; u8 ah_coverage_class; diff --git a/drivers/net/wireless/ath/ath5k/attach.c b/drivers/net/wireless/ath/ath5k/attach.c index 6e02de311cdd..cd0b14a0a93a 100644 --- a/drivers/net/wireless/ath/ath5k/attach.c +++ b/drivers/net/wireless/ath/ath5k/attach.c @@ -118,7 +118,6 @@ int ath5k_hw_attach(struct ath5k_softc *sc) ah->ah_turbo = false; ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER; ah->ah_imr = 0; - ah->ah_atim_window = 0; ah->ah_limit_tx_retries = AR5K_INIT_TX_RETRY; ah->ah_software_retry = false; ah->ah_ant_mode = AR5K_ANTMODE_DEFAULT; diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c index 6a891c4484a0..71dca1023e72 100644 --- a/drivers/net/wireless/ath/ath5k/pcu.c +++ b/drivers/net/wireless/ath/ath5k/pcu.c @@ -600,7 +600,7 @@ void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval) /* Timer3 marks the end of our ATIM window * a zero length window is not allowed because * we 'll get no beacons */ - timer3 = next_beacon + (ah->ah_atim_window ? ah->ah_atim_window : 1); + timer3 = next_beacon + 1; /* * Set the beacon register and enable all timers. -- cgit v1.2.3 From 7f896126017830b29cf501d246ee32b81e359acd Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Mon, 27 Sep 2010 12:22:21 +0900 Subject: ath5k: Check and fix ATIM window This patch adds sanity-checks for the beacon timers and especially the ATIM window to ath5k. It is basically the same what i did for madwifi two years ago and fixes a problem in IBSS mode which has been described as "ramping" pings. See the code comments for a more detailed description and these links: http://madwifi-project.org/ticket/1154 http://madwifi-project.org/changeset/3867 http://thread.gmane.org/gmane.linux.drivers.madwifi.devel/6066 Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/ath5k.h | 1 + drivers/net/wireless/ath/ath5k/base.c | 9 ++++ drivers/net/wireless/ath/ath5k/pcu.c | 91 ++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index 42e6e821259e..0cba2e315d9a 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -1195,6 +1195,7 @@ u64 ath5k_hw_get_tsf64(struct ath5k_hw *ah); void ath5k_hw_set_tsf64(struct ath5k_hw *ah, u64 tsf64); void ath5k_hw_reset_tsf(struct ath5k_hw *ah); void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval); +bool ath5k_hw_check_beacon_timers(struct ath5k_hw *ah, int intval); /* ACK bit rate */ void ath5k_hw_set_ack_bitrate_high(struct ath5k_hw *ah, bool high); /* Clock rate related functions */ diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 95072db0ec21..4103765000ea 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1191,6 +1191,15 @@ ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb, */ if (hw_tu >= sc->nexttbtt) ath5k_beacon_update_timers(sc, bc_tstamp); + + /* Check if the beacon timers are still correct, because a TSF + * update might have created a window between them - for a + * longer description see the comment of this function: */ + if (!ath5k_hw_check_beacon_timers(sc->ah, sc->bintval)) { + ath5k_beacon_update_timers(sc, bc_tstamp); + ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, + "fixed beacon timers after beacon receive\n"); + } } } diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c index 71dca1023e72..604114fb34b1 100644 --- a/drivers/net/wireless/ath/ath5k/pcu.c +++ b/drivers/net/wireless/ath/ath5k/pcu.c @@ -640,6 +640,97 @@ void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval) } +/** + * ath5k_check_timer_win - Check if timer B is timer A + window + * + * @a: timer a (before b) + * @b: timer b (after a) + * @window: difference between a and b + * @intval: timers are increased by this interval + * + * This helper function checks if timer B is timer A + window and covers + * cases where timer A or B might have already been updated or wrapped + * around (Timers are 16 bit). + * + * Returns true if O.K. + */ +static inline bool +ath5k_check_timer_win(int a, int b, int window, int intval) +{ + /* + * 1.) usually B should be A + window + * 2.) A already updated, B not updated yet + * 3.) A already updated and has wrapped around + * 4.) B has wrapped around + */ + if ((b - a == window) || /* 1.) */ + (a - b == intval - window) || /* 2.) */ + ((a | 0x10000) - b == intval - window) || /* 3.) */ + ((b | 0x10000) - a == window)) /* 4.) */ + return true; /* O.K. */ + return false; +} + +/** + * ath5k_hw_check_beacon_timers - Check if the beacon timers are correct + * + * @ah: The &struct ath5k_hw + * @intval: beacon interval + * + * This is a workaround for IBSS mode: + * + * The need for this function arises from the fact that we have 4 separate + * HW timer registers (TIMER0 - TIMER3), which are closely related to the + * next beacon target time (NBTT), and that the HW updates these timers + * seperately based on the current TSF value. The hardware increments each + * timer by the beacon interval, when the local TSF coverted to TU is equal + * to the value stored in the timer. + * + * The reception of a beacon with the same BSSID can update the local HW TSF + * at any time - this is something we can't avoid. If the TSF jumps to a + * time which is later than the time stored in a timer, this timer will not + * be updated until the TSF in TU wraps around at 16 bit (the size of the + * timers) and reaches the time which is stored in the timer. + * + * The problem is that these timers are closely related to TIMER0 (NBTT) and + * that they define a time "window". When the TSF jumps between two timers + * (e.g. ATIM and NBTT), the one in the past will be left behind (not + * updated), while the one in the future will be updated every beacon + * interval. This causes the window to get larger, until the TSF wraps + * around as described above and the timer which was left behind gets + * updated again. But - because the beacon interval is usually not an exact + * divisor of the size of the timers (16 bit), an unwanted "window" between + * these timers has developed! + * + * This is especially important with the ATIM window, because during + * the ATIM window only ATIM frames and no data frames are allowed to be + * sent, which creates transmission pauses after each beacon. This symptom + * has been described as "ramping ping" because ping times increase linearly + * for some time and then drop down again. A wrong window on the DMA beacon + * timer has the same effect, so we check for these two conditions. + * + * Returns true if O.K. + */ +bool +ath5k_hw_check_beacon_timers(struct ath5k_hw *ah, int intval) +{ + unsigned int nbtt, atim, dma; + + nbtt = ath5k_hw_reg_read(ah, AR5K_TIMER0); + atim = ath5k_hw_reg_read(ah, AR5K_TIMER3); + dma = ath5k_hw_reg_read(ah, AR5K_TIMER1) >> 3; + + /* NOTE: SWBA is different. Having a wrong window there does not + * stop us from sending data and this condition is catched thru + * other means (SWBA interrupt) */ + + if (ath5k_check_timer_win(nbtt, atim, 1, intval) && + ath5k_check_timer_win(dma, nbtt, AR5K_TUNE_DMA_BEACON_RESP, + intval)) + return true; /* O.K. */ + return false; +} + /** * ath5k_hw_set_coverage_class - Set IEEE 802.11 coverage class * -- cgit v1.2.3 From 11f21df36cbcffbfada9307e809d4372ece27f47 Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Mon, 27 Sep 2010 12:22:26 +0900 Subject: ath5k: Increase "fudge" for beacon timers We use FUDGE to make sure the next TBTT is ahead of the current TU. Since we later substract AR5K_TUNE_SW_BEACON_RESP (10) in the timer configuration we need to make sure it is bigger than that. Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 4103765000ea..94cc3354f3a6 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1886,8 +1886,11 @@ ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf) hw_tsf = ath5k_hw_get_tsf64(ah); hw_tu = TSF_TO_TU(hw_tsf); -#define FUDGE 3 - /* we use FUDGE to make sure the next TBTT is ahead of the current TU */ +#define FUDGE AR5K_TUNE_SW_BEACON_RESP + 3 + /* We use FUDGE to make sure the next TBTT is ahead of the current TU. + * Since we later substract AR5K_TUNE_SW_BEACON_RESP (10) in the timer + * configuration we need to make sure it is bigger than that. */ + if (bc_tsf == -1) { /* * no beacons received, called internally. -- cgit v1.2.3 From 28df897a42aa41d6318be5b9872c4bb5c8d8d7e3 Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Mon, 27 Sep 2010 12:22:32 +0900 Subject: ath5k: Disable interrupts in ath5k_hw_get_tsf64 The code in ath5k_hw_get_tsf64() is time critical and will return wrong results if we get interrupted, so disable local interrupts. Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/pcu.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c index 604114fb34b1..095d30b50ec7 100644 --- a/drivers/net/wireless/ath/ath5k/pcu.c +++ b/drivers/net/wireless/ath/ath5k/pcu.c @@ -495,6 +495,10 @@ u64 ath5k_hw_get_tsf64(struct ath5k_hw *ah) { u32 tsf_lower, tsf_upper1, tsf_upper2; int i; + unsigned long flags; + + /* This code is time critical - we don't want to be interrupted here */ + local_irq_save(flags); /* * While reading TSF upper and then lower part, the clock is still @@ -517,6 +521,8 @@ u64 ath5k_hw_get_tsf64(struct ath5k_hw *ah) tsf_upper1 = tsf_upper2; } + local_irq_restore(flags); + WARN_ON( i == ATH5K_MAX_TSF_READ ); return (((u64)tsf_upper1 << 32) | tsf_lower); -- cgit v1.2.3 From eada7cad6005006b457f10a4c3e1881ff99d03a4 Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Mon, 27 Sep 2010 13:02:40 +0900 Subject: ath5k: Fix bitmasks and typos for PCU Diagnostic register As reported by Ryan Niemi, some bitmasks in the register definition for the PCU Diagnostic register (DIAG_SW) were missing a zero at the end. While at it fix some typos and add more comments. Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/dma.c | 4 ++-- drivers/net/wireless/ath/ath5k/phy.c | 4 ++-- drivers/net/wireless/ath/ath5k/reg.h | 29 ++++++++++++++--------------- 3 files changed, 18 insertions(+), 19 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/dma.c b/drivers/net/wireless/ath/ath5k/dma.c index 58bb6c5dda7b..923c9ca5c4f0 100644 --- a/drivers/net/wireless/ath/ath5k/dma.c +++ b/drivers/net/wireless/ath/ath5k/dma.c @@ -244,7 +244,7 @@ int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue) /* Force channel idle high */ AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5211, - AR5K_DIAG_SW_CHANEL_IDLE_HIGH); + AR5K_DIAG_SW_CHANNEL_IDLE_HIGH); /* Wait a while and disable mechanism */ udelay(200); @@ -261,7 +261,7 @@ int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue) } while (--i && pending); AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW_5211, - AR5K_DIAG_SW_CHANEL_IDLE_HIGH); + AR5K_DIAG_SW_CHANNEL_IDLE_HIGH); } /* Clear register */ diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c index 4932bf2f35eb..61da913e7c8f 100644 --- a/drivers/net/wireless/ath/ath5k/phy.c +++ b/drivers/net/wireless/ath/ath5k/phy.c @@ -1257,7 +1257,7 @@ static int ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah, * Disable beacons and RX/TX queues, wait */ AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5210, - AR5K_DIAG_SW_DIS_TX | AR5K_DIAG_SW_DIS_RX_5210); + AR5K_DIAG_SW_DIS_TX_5210 | AR5K_DIAG_SW_DIS_RX_5210); beacon = ath5k_hw_reg_read(ah, AR5K_BEACON_5210); ath5k_hw_reg_write(ah, beacon & ~AR5K_BEACON_ENABLE, AR5K_BEACON_5210); @@ -1336,7 +1336,7 @@ static int ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah, * Re-enable RX/TX and beacons */ AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW_5210, - AR5K_DIAG_SW_DIS_TX | AR5K_DIAG_SW_DIS_RX_5210); + AR5K_DIAG_SW_DIS_TX_5210 | AR5K_DIAG_SW_DIS_RX_5210); ath5k_hw_reg_write(ah, beacon, AR5K_BEACON_5210); return 0; diff --git a/drivers/net/wireless/ath/ath5k/reg.h b/drivers/net/wireless/ath/ath5k/reg.h index 67d63081705a..a34929f06533 100644 --- a/drivers/net/wireless/ath/ath5k/reg.h +++ b/drivers/net/wireless/ath/ath5k/reg.h @@ -1387,10 +1387,9 @@ /* - * PCU control register + * PCU Diagnostic register * - * Only DIS_RX is used in the code, the rest i guess are - * for tweaking/diagnostics. + * Used for tweaking/diagnostics. */ #define AR5K_DIAG_SW_5210 0x8068 /* Register Address [5210] */ #define AR5K_DIAG_SW_5211 0x8048 /* Register Address [5211+] */ @@ -1399,22 +1398,22 @@ #define AR5K_DIAG_SW_DIS_WEP_ACK 0x00000001 /* Disable ACKs if WEP key is invalid */ #define AR5K_DIAG_SW_DIS_ACK 0x00000002 /* Disable ACKs */ #define AR5K_DIAG_SW_DIS_CTS 0x00000004 /* Disable CTSs */ -#define AR5K_DIAG_SW_DIS_ENC 0x00000008 /* Disable encryption */ -#define AR5K_DIAG_SW_DIS_DEC 0x00000010 /* Disable decryption */ -#define AR5K_DIAG_SW_DIS_TX 0x00000020 /* Disable transmit [5210] */ -#define AR5K_DIAG_SW_DIS_RX_5210 0x00000040 /* Disable recieve */ +#define AR5K_DIAG_SW_DIS_ENC 0x00000008 /* Disable HW encryption */ +#define AR5K_DIAG_SW_DIS_DEC 0x00000010 /* Disable HW decryption */ +#define AR5K_DIAG_SW_DIS_TX_5210 0x00000020 /* Disable transmit [5210] */ +#define AR5K_DIAG_SW_DIS_RX_5210 0x00000040 /* Disable receive */ #define AR5K_DIAG_SW_DIS_RX_5211 0x00000020 #define AR5K_DIAG_SW_DIS_RX (ah->ah_version == AR5K_AR5210 ? \ AR5K_DIAG_SW_DIS_RX_5210 : AR5K_DIAG_SW_DIS_RX_5211) -#define AR5K_DIAG_SW_LOOP_BACK_5210 0x00000080 /* Loopback (i guess it goes with DIS_TX) [5210] */ +#define AR5K_DIAG_SW_LOOP_BACK_5210 0x00000080 /* TX Data Loopback (i guess it goes with DIS_TX) [5210] */ #define AR5K_DIAG_SW_LOOP_BACK_5211 0x00000040 #define AR5K_DIAG_SW_LOOP_BACK (ah->ah_version == AR5K_AR5210 ? \ AR5K_DIAG_SW_LOOP_BACK_5210 : AR5K_DIAG_SW_LOOP_BACK_5211) -#define AR5K_DIAG_SW_CORR_FCS_5210 0x00000100 /* Corrupted FCS */ +#define AR5K_DIAG_SW_CORR_FCS_5210 0x00000100 /* Generate invalid TX FCS */ #define AR5K_DIAG_SW_CORR_FCS_5211 0x00000080 #define AR5K_DIAG_SW_CORR_FCS (ah->ah_version == AR5K_AR5210 ? \ AR5K_DIAG_SW_CORR_FCS_5210 : AR5K_DIAG_SW_CORR_FCS_5211) -#define AR5K_DIAG_SW_CHAN_INFO_5210 0x00000200 /* Dump channel info */ +#define AR5K_DIAG_SW_CHAN_INFO_5210 0x00000200 /* Add 56 bytes of channel info before the frame data in the RX buffer */ #define AR5K_DIAG_SW_CHAN_INFO_5211 0x00000100 #define AR5K_DIAG_SW_CHAN_INFO (ah->ah_version == AR5K_AR5210 ? \ AR5K_DIAG_SW_CHAN_INFO_5210 : AR5K_DIAG_SW_CHAN_INFO_5211) @@ -1426,17 +1425,17 @@ #define AR5K_DIAG_SW_SCVRAM_SEED 0x0003f800 /* [5210] */ #define AR5K_DIAG_SW_SCRAM_SEED_M 0x0001fc00 /* Scrambler seed mask */ #define AR5K_DIAG_SW_SCRAM_SEED_S 10 -#define AR5K_DIAG_SW_DIS_SEQ_INC 0x00040000 /* Disable seqnum increment (?)[5210] */ +#define AR5K_DIAG_SW_DIS_SEQ_INC_5210 0x00040000 /* Disable seqnum increment (?)[5210] */ #define AR5K_DIAG_SW_FRAME_NV0_5210 0x00080000 #define AR5K_DIAG_SW_FRAME_NV0_5211 0x00020000 /* Accept frames of non-zero protocol number */ #define AR5K_DIAG_SW_FRAME_NV0 (ah->ah_version == AR5K_AR5210 ? \ AR5K_DIAG_SW_FRAME_NV0_5210 : AR5K_DIAG_SW_FRAME_NV0_5211) #define AR5K_DIAG_SW_OBSPT_M 0x000c0000 /* Observation point select (?) */ #define AR5K_DIAG_SW_OBSPT_S 18 -#define AR5K_DIAG_SW_RX_CLEAR_HIGH 0x0010000 /* Force RX Clear high */ -#define AR5K_DIAG_SW_IGNORE_CARR_SENSE 0x0020000 /* Ignore virtual carrier sense */ -#define AR5K_DIAG_SW_CHANEL_IDLE_HIGH 0x0040000 /* Force channel idle high */ -#define AR5K_DIAG_SW_PHEAR_ME 0x0080000 /* ??? */ +#define AR5K_DIAG_SW_RX_CLEAR_HIGH 0x00100000 /* Ignore carrier sense */ +#define AR5K_DIAG_SW_IGNORE_CARR_SENSE 0x00200000 /* Ignore virtual carrier sense */ +#define AR5K_DIAG_SW_CHANNEL_IDLE_HIGH 0x00400000 /* Force channel idle high */ +#define AR5K_DIAG_SW_PHEAR_ME 0x00800000 /* ??? */ /* * TSF (clock) register (lower 32 bits) -- cgit v1.2.3 From dc4769f0bb949e312ad8d9b652047ff6709978c2 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Tue, 28 Sep 2010 01:53:42 +0200 Subject: carl9170: interrupt urbs must not set URB_ZERO_PACKET This patch fixes a bug in the driver which was exposed by CONFIG_USB_DEBUG: "usb 1-1.6.3: BOGUS urb flags, 40 --> 0" The transfer flag "URB_ZERO_PACKET" is only valid for bulk urbs. Reported-by: André Erdmann Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/usb.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/usb.c b/drivers/net/wireless/ath/carl9170/usb.c index eb789a9e4f15..c7f6193934ea 100644 --- a/drivers/net/wireless/ath/carl9170/usb.c +++ b/drivers/net/wireless/ath/carl9170/usb.c @@ -606,8 +606,6 @@ int __carl9170_exec_cmd(struct ar9170 *ar, struct carl9170_cmd *cmd, AR9170_USB_EP_CMD), cmd, cmd->hdr.len + 4, carl9170_usb_cmd_complete, ar, 1); - urb->transfer_flags |= URB_ZERO_PACKET; - if (free_buf) urb->transfer_flags |= URB_FREE_BUFFER; -- cgit v1.2.3 From 5c895691944862e782c285a871f5d3466797b4ef Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Tue, 28 Sep 2010 23:00:59 +0200 Subject: carl9170: support firmware-based rx filter The hardware rx-filter was essentially disabled, because of a serve, yet unidentifiable problem with iwlagn. Due to these circumstances the driver and mac80211 were left with the job of filtering. This is very unfortunate and has proven to be expensive in terms of latency, memory and load. Therefore the new 1.8.8.3 firmware introduces a flexible filtering infrastructure which allows the driver to offload some of the checks (FCS & PLCP crc check, RA match, control frame filter, etc...) whenever possible. Note: This patch also includes all changes to the shared headers files since the inclusion. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/carl9170.h | 2 ++ drivers/net/wireless/ath/carl9170/cmd.h | 10 ++++++++++ drivers/net/wireless/ath/carl9170/fw.c | 7 +++++++ drivers/net/wireless/ath/carl9170/fwcmd.h | 16 +++++++++++++++ drivers/net/wireless/ath/carl9170/fwdesc.h | 6 +++++- drivers/net/wireless/ath/carl9170/hw.h | 3 +++ drivers/net/wireless/ath/carl9170/main.c | 30 ++++++++++++++++++++++++++-- drivers/net/wireless/ath/carl9170/phy.h | 5 +---- drivers/net/wireless/ath/carl9170/version.h | 6 +++--- 9 files changed, 75 insertions(+), 10 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h index 20f2a77e54d2..6cf0c9ef47aa 100644 --- a/drivers/net/wireless/ath/carl9170/carl9170.h +++ b/drivers/net/wireless/ath/carl9170/carl9170.h @@ -279,6 +279,7 @@ struct ar9170 { unsigned int beacon_max_len; bool rx_stream; bool tx_stream; + bool rx_filter; unsigned int mem_blocks; unsigned int mem_block_size; unsigned int rx_size; @@ -314,6 +315,7 @@ struct ar9170 { u64 cur_mc_hash; u32 cur_filter; unsigned int filter_state; + unsigned int rx_filter_caps; bool sniffer_enabled; /* MAC */ diff --git a/drivers/net/wireless/ath/carl9170/cmd.h b/drivers/net/wireless/ath/carl9170/cmd.h index 0fc83d2336fd..f78728c38294 100644 --- a/drivers/net/wireless/ath/carl9170/cmd.h +++ b/drivers/net/wireless/ath/carl9170/cmd.h @@ -59,6 +59,16 @@ static inline int carl9170_flush_cab(struct ar9170 *ar, return carl9170_bcn_ctrl(ar, vif_id, CARL9170_BCN_CTRL_DRAIN, 0, 0); } +static inline int carl9170_rx_filter(struct ar9170 *ar, + const unsigned int _rx_filter) +{ + __le32 rx_filter = cpu_to_le32(_rx_filter); + + return carl9170_exec_cmd(ar, CARL9170_CMD_RX_FILTER, + sizeof(rx_filter), (u8 *)&rx_filter, + 0, NULL); +} + struct carl9170_cmd *carl9170_cmd_buf(struct ar9170 *ar, const enum carl9170_cmd_oids cmd, const unsigned int len); diff --git a/drivers/net/wireless/ath/carl9170/fw.c b/drivers/net/wireless/ath/carl9170/fw.c index 36615462b87a..ae6c006bbc56 100644 --- a/drivers/net/wireless/ath/carl9170/fw.c +++ b/drivers/net/wireless/ath/carl9170/fw.c @@ -257,6 +257,13 @@ static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len) if (SUPP(CARL9170FW_USB_UP_STREAM)) ar->fw.rx_stream = true; + if (SUPP(CARL9170FW_RX_FILTER)) { + ar->fw.rx_filter = true; + ar->rx_filter_caps = FIF_FCSFAIL | FIF_PLCPFAIL | + FIF_CONTROL | FIF_PSPOLL | FIF_OTHER_BSS | + FIF_PROMISC_IN_BSS; + } + ar->fw.vif_num = otus_desc->vif_num; ar->fw.cmd_bufs = otus_desc->cmd_bufs; ar->fw.address = le32_to_cpu(otus_desc->fw_address); diff --git a/drivers/net/wireless/ath/carl9170/fwcmd.h b/drivers/net/wireless/ath/carl9170/fwcmd.h index d4a4e1dbef06..d552166db505 100644 --- a/drivers/net/wireless/ath/carl9170/fwcmd.h +++ b/drivers/net/wireless/ath/carl9170/fwcmd.h @@ -53,6 +53,7 @@ enum carl9170_cmd_oids { CARL9170_CMD_REBOOT = 0x04, CARL9170_CMD_BCN_CTRL = 0x05, CARL9170_CMD_READ_TSF = 0x06, + CARL9170_CMD_RX_FILTER = 0x07, /* CAM */ CARL9170_CMD_EKEY = 0x10, @@ -153,6 +154,20 @@ struct carl9170_psm { } __packed; #define CARL9170_PSM_SIZE 4 +struct carl9170_rx_filter_cmd { + __le32 rx_filter; +} __packed; +#define CARL9170_RX_FILTER_CMD_SIZE 4 + +#define CARL9170_RX_FILTER_BAD 0x01 +#define CARL9170_RX_FILTER_OTHER_RA 0x02 +#define CARL9170_RX_FILTER_DECRY_FAIL 0x04 +#define CARL9170_RX_FILTER_CTL_OTHER 0x08 +#define CARL9170_RX_FILTER_CTL_PSPOLL 0x10 +#define CARL9170_RX_FILTER_CTL_BACKR 0x20 +#define CARL9170_RX_FILTER_MGMT 0x40 +#define CARL9170_RX_FILTER_DATA 0x80 + struct carl9170_bcn_ctrl_cmd { __le32 vif_id; __le32 mode; @@ -188,6 +203,7 @@ struct carl9170_cmd { struct carl9170_rf_init rf_init; struct carl9170_psm psm; struct carl9170_bcn_ctrl_cmd bcn_ctrl; + struct carl9170_rx_filter_cmd rx_filter; u8 data[CARL9170_MAX_CMD_PAYLOAD_LEN]; } __packed; } __packed; diff --git a/drivers/net/wireless/ath/carl9170/fwdesc.h b/drivers/net/wireless/ath/carl9170/fwdesc.h index 7cd811708fe5..71f3821f6058 100644 --- a/drivers/net/wireless/ath/carl9170/fwdesc.h +++ b/drivers/net/wireless/ath/carl9170/fwdesc.h @@ -66,6 +66,9 @@ enum carl9170fw_feature_list { /* Firmware PSM support | CARL9170_CMD_PSM */ CARL9170FW_PSM, + /* Firmware RX filter | CARL9170_CMD_RX_FILTER */ + CARL9170FW_RX_FILTER, + /* KEEP LAST */ __CARL9170FW_FEATURE_NUM }; @@ -142,7 +145,7 @@ struct carl9170fw_fix_desc { (sizeof(struct carl9170fw_fix_desc)) #define CARL9170FW_DBG_DESC_MIN_VER 1 -#define CARL9170FW_DBG_DESC_CUR_VER 2 +#define CARL9170FW_DBG_DESC_CUR_VER 3 struct carl9170fw_dbg_desc { struct carl9170fw_desc_head head; @@ -150,6 +153,7 @@ struct carl9170fw_dbg_desc { __le32 counter_addr; __le32 rx_total_addr; __le32 rx_overrun_addr; + __le32 rx_filter; /* Put your debugging definitions here */ } __packed; diff --git a/drivers/net/wireless/ath/carl9170/hw.h b/drivers/net/wireless/ath/carl9170/hw.h index b1292ac5b703..2f471b3f05af 100644 --- a/drivers/net/wireless/ath/carl9170/hw.h +++ b/drivers/net/wireless/ath/carl9170/hw.h @@ -731,6 +731,9 @@ struct ar9170_stream { #define SET_VAL(reg, value, newvalue) \ (value = ((value) & ~reg) | (((newvalue) << reg##_S) & reg)) +#define SET_CONSTVAL(reg, newvalue) \ + (((newvalue) << reg##_S) & reg) + #define MOD_VAL(reg, value, newvalue) \ (((value) & ~reg) | (((newvalue) << reg##_S) & reg)) #endif /* __CARL9170_SHARED_HW_H */ diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 84bd38e9961c..3cc99f3f7ab5 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -380,6 +380,13 @@ static int carl9170_op_start(struct ieee80211_hw *hw) if (err) goto out; + if (ar->fw.rx_filter) { + err = carl9170_rx_filter(ar, CARL9170_RX_FILTER_OTHER_RA | + CARL9170_RX_FILTER_CTL_OTHER | CARL9170_RX_FILTER_BAD); + if (err) + goto out; + } + err = carl9170_write_reg(ar, AR9170_MAC_REG_DMA_TRIGGER, AR9170_DMA_TRIGGER_RXQ); if (err) @@ -840,8 +847,7 @@ static void carl9170_op_configure_filter(struct ieee80211_hw *hw, struct ar9170 *ar = hw->priv; /* mask supported flags */ - *new_flags &= FIF_ALLMULTI | FIF_FCSFAIL | FIF_PLCPFAIL | - FIF_OTHER_BSS | FIF_PROMISC_IN_BSS; + *new_flags &= FIF_ALLMULTI | ar->rx_filter_caps; if (!IS_ACCEPTING_CMD(ar)) return; @@ -867,6 +873,26 @@ static void carl9170_op_configure_filter(struct ieee80211_hw *hw, WARN_ON(carl9170_set_operating_mode(ar)); } + if (ar->fw.rx_filter && changed_flags & ar->rx_filter_caps) { + u32 rx_filter = 0; + + if (!(*new_flags & (FIF_FCSFAIL | FIF_PLCPFAIL))) + rx_filter |= CARL9170_RX_FILTER_BAD; + + if (!(*new_flags & FIF_CONTROL)) + rx_filter |= CARL9170_RX_FILTER_CTL_OTHER; + + if (!(*new_flags & FIF_PSPOLL)) + rx_filter |= CARL9170_RX_FILTER_CTL_PSPOLL; + + if (!(*new_flags & (FIF_OTHER_BSS | FIF_PROMISC_IN_BSS))) { + rx_filter |= CARL9170_RX_FILTER_OTHER_RA; + rx_filter |= CARL9170_RX_FILTER_DECRY_FAIL; + } + + WARN_ON(carl9170_rx_filter(ar, rx_filter)); + } + mutex_unlock(&ar->mutex); } diff --git a/drivers/net/wireless/ath/carl9170/phy.h b/drivers/net/wireless/ath/carl9170/phy.h index 53c18d34ffcc..02c34eb4ebde 100644 --- a/drivers/net/wireless/ath/carl9170/phy.h +++ b/drivers/net/wireless/ath/carl9170/phy.h @@ -423,8 +423,8 @@ #define AR9170_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV 0x2000 #define AR9170_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV_S 13 -#define AR9170_PHY_REG_GAIN_2GHZ_CHAIN_2 (AR9170_PHY_REG_BASE + 0x2a0c) #define AR9170_PHY_REG_GAIN_2GHZ (AR9170_PHY_REG_BASE + 0x0a0c) +#define AR9170_PHY_REG_GAIN_2GHZ_CHAIN_2 (AR9170_PHY_REG_BASE + 0x2a0c) #define AR9170_PHY_GAIN_2GHZ_RXTX_MARGIN 0x00fc0000 #define AR9170_PHY_GAIN_2GHZ_RXTX_MARGIN_S 18 #define AR9170_PHY_GAIN_2GHZ_BSW_MARGIN 0x00003c00 @@ -561,7 +561,4 @@ #define AR9170_PHY_CH2_EXT_MINCCA_PWR 0xff800000 #define AR9170_PHY_CH2_EXT_MINCCA_PWR_S 23 -#define REDUCE_CHAIN_0 0x00000050 -#define REDUCE_CHAIN_1 0x00000051 - #endif /* __CARL9170_SHARED_PHY_H */ diff --git a/drivers/net/wireless/ath/carl9170/version.h b/drivers/net/wireless/ath/carl9170/version.h index 0e917f80eab4..ff53f078a0b5 100644 --- a/drivers/net/wireless/ath/carl9170/version.h +++ b/drivers/net/wireless/ath/carl9170/version.h @@ -1,7 +1,7 @@ #ifndef __CARL9170_SHARED_VERSION_H #define __CARL9170_SHARED_VERSION_H #define CARL9170FW_VERSION_YEAR 10 -#define CARL9170FW_VERSION_MONTH 8 -#define CARL9170FW_VERSION_DAY 30 -#define CARL9170FW_VERSION_GIT "1.8.8.1" +#define CARL9170FW_VERSION_MONTH 9 +#define CARL9170FW_VERSION_DAY 28 +#define CARL9170FW_VERSION_GIT "1.8.8.3" #endif /* __CARL9170_SHARED_VERSION_H */ -- cgit v1.2.3 From 35ecfe03d96b7cdaf6e50aba13af37e2c99deb57 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 29 Sep 2010 17:15:26 +0200 Subject: ath9k_htc: remove use of common->ani.noise_floor It is unused aside from a single redundant debug message Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 5124d04b240b..f12591f5d02a 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -760,23 +760,12 @@ void ath9k_ani_work(struct work_struct *work) ath9k_hw_ani_monitor(ah, ah->curchan); /* Perform calibration if necessary */ - if (longcal || shortcal) { + if (longcal || shortcal) common->ani.caldone = ath9k_hw_calibrate(ah, ah->curchan, common->rx_chainmask, longcal); - if (longcal) - common->ani.noise_floor = - ath9k_hw_getchan_noise(ah, ah->curchan); - - ath_print(common, ATH_DBG_ANI, - " calibrate chan %u/%x nf: %d\n", - ah->curchan->channel, - ah->curchan->channelFlags, - common->ani.noise_floor); - } - ath9k_htc_ps_restore(priv); } -- cgit v1.2.3 From d9891c7804f27c5b0ea9eb51f42bf473b24338a0 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 29 Sep 2010 17:15:27 +0200 Subject: ath9k_hw: keep calibrated noise floor values per channel Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/calib.c | 40 +++++++++++++++++----------------- drivers/net/wireless/ath/ath9k/hw.c | 2 +- drivers/net/wireless/ath/ath9k/hw.h | 2 +- 3 files changed, 22 insertions(+), 22 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 67ee5d735cc1..6351e76792a6 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -346,34 +346,34 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan) struct ieee80211_channel *c = chan->chan; struct ath9k_hw_cal_data *caldata = ah->caldata; - if (!caldata) - return false; - chan->channelFlags &= (~CHANNEL_CW_INT); if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) { ath_print(common, ATH_DBG_CALIBRATE, "NF did not complete in calibration window\n"); - nf = 0; - caldata->rawNoiseFloor = nf; return false; - } else { - ath9k_hw_do_getnf(ah, nfarray); - ath9k_hw_nf_sanitize(ah, nfarray); - nf = nfarray[0]; - if (ath9k_hw_get_nf_thresh(ah, c->band, &nfThresh) - && nf > nfThresh) { - ath_print(common, ATH_DBG_CALIBRATE, - "noise floor failed detected; " - "detected %d, threshold %d\n", - nf, nfThresh); - chan->channelFlags |= CHANNEL_CW_INT; - } + } + + ath9k_hw_do_getnf(ah, nfarray); + ath9k_hw_nf_sanitize(ah, nfarray); + nf = nfarray[0]; + if (ath9k_hw_get_nf_thresh(ah, c->band, &nfThresh) + && nf > nfThresh) { + ath_print(common, ATH_DBG_CALIBRATE, + "noise floor failed detected; " + "detected %d, threshold %d\n", + nf, nfThresh); + chan->channelFlags |= CHANNEL_CW_INT; + } + + if (!caldata) { + chan->noisefloor = nf; + return false; } h = caldata->nfCalHist; caldata->nfcal_pending = false; ath9k_hw_update_nfcal_hist_buffer(ah, caldata, nfarray); - caldata->rawNoiseFloor = h[0].privNF; + chan->noisefloor = h[0].privNF; return true; } @@ -401,10 +401,10 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan) { - if (!ah->caldata || !ah->caldata->rawNoiseFloor) + if (!ah->curchan || !ah->curchan->noisefloor) return ath9k_hw_get_default_nf(ah, chan); - return ah->caldata->rawNoiseFloor; + return ah->curchan->noisefloor; } EXPORT_SYMBOL(ath9k_hw_getchan_noise); diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 25ed65ac992c..1b066043d6cb 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1239,7 +1239,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) return -EIO; - if (curchan && !ah->chip_fullsleep && ah->caldata) + if (curchan && !ah->chip_fullsleep) ath9k_hw_getnf(ah, curchan); ah->caldata = caldata; diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index df47f792cf4e..1b6739bd93ff 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -342,7 +342,6 @@ struct ath9k_hw_cal_data { int32_t CalValid; int8_t iCoff; int8_t qCoff; - int16_t rawNoiseFloor; bool paprd_done; bool nfcal_pending; bool nfcal_interference; @@ -356,6 +355,7 @@ struct ath9k_channel { u16 channel; u32 channelFlags; u32 chanmode; + s16 noisefloor; }; #define IS_CHAN_G(_c) ((((_c)->channelFlags & (CHANNEL_G)) == CHANNEL_G) || \ -- cgit v1.2.3 From 4f1a5a4b62c5335aafd5830866320c8918103a3a Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 29 Sep 2010 17:15:28 +0200 Subject: ath9k: do not return default noise floor values via survey Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index a13387882636..fcba402d5a1e 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2004,15 +2004,17 @@ static int ath9k_get_survey(struct ieee80211_hw *hw, int idx, struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &hw->conf; if (idx != 0) return -ENOENT; survey->channel = conf->channel; - survey->filled = SURVEY_INFO_NOISE_DBM; - survey->noise = common->ani.noise_floor; + survey->filled = 0; + if (ah->curchan && ah->curchan->noisefloor) { + survey->filled |= SURVEY_INFO_NOISE_DBM; + survey->noise = ah->curchan->noisefloor; + } return 0; } -- cgit v1.2.3 From 0bda652300dff8136c9d889f1133462c7c7d332c Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 29 Sep 2010 17:15:29 +0200 Subject: ath9k: remove the noise floor value in the ani struct common->ani.noise_floor is now only used for a similar redundant debug message similar to the one that was removed from ath9k_htc in an earlier patch. Remove it from ath9k as well now. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath.h | 1 - drivers/net/wireless/ath/ath9k/init.c | 1 - drivers/net/wireless/ath/ath9k/main.c | 10 ---------- 3 files changed, 12 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index dd236c3b52f6..5894fcc2c628 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -35,7 +35,6 @@ static const u8 ath_bcast_mac[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; struct ath_ani { bool caldone; - int16_t noise_floor; unsigned int longcal_timer; unsigned int shortcal_timer; unsigned int resetcal_timer; diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index de3393867e37..11dc3ac20cd6 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -506,7 +506,6 @@ static void ath9k_init_misc(struct ath_softc *sc) struct ath_common *common = ath9k_hw_common(sc->sc_ah); int i = 0; - common->ani.noise_floor = ATH_DEFAULT_NOISE_FLOOR; setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc); sc->config.txpowlimit = ATH_TXPOWER_MAX; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index fcba402d5a1e..a801a08c4da2 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -459,16 +459,6 @@ void ath_ani_calibrate(unsigned long data) ah->curchan, common->rx_chainmask, longcal); - - if (longcal) - common->ani.noise_floor = ath9k_hw_getchan_noise(ah, - ah->curchan); - - ath_print(common, ATH_DBG_ANI, - " calibrate chan %u/%x nf: %d\n", - ah->curchan->channel, - ah->curchan->channelFlags, - common->ani.noise_floor); } } -- cgit v1.2.3 From 39162dbe813a2c90b7b9018a74dc1befb9ff4205 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 29 Sep 2010 19:12:06 +0200 Subject: ath9k: return survey data for all channels instead of just the current one Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index a801a08c4da2..49ea45896b89 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1994,16 +1994,31 @@ static int ath9k_get_survey(struct ieee80211_hw *hw, int idx, struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; struct ath_hw *ah = sc->sc_ah; - struct ieee80211_conf *conf = &hw->conf; + struct ieee80211_supported_band *sband; + struct ath9k_channel *chan; + + sband = hw->wiphy->bands[IEEE80211_BAND_2GHZ]; + if (sband && idx >= sband->n_channels) { + idx -= sband->n_channels; + sband = NULL; + } - if (idx != 0) - return -ENOENT; + if (!sband) + sband = hw->wiphy->bands[IEEE80211_BAND_5GHZ]; - survey->channel = conf->channel; + if (!sband || idx >= sband->n_channels) + return -ENOENT; + + survey->channel = &sband->channels[idx]; + chan = &ah->channels[survey->channel->hw_value]; survey->filled = 0; - if (ah->curchan && ah->curchan->noisefloor) { + + if (chan == ah->curchan) + survey->filled |= SURVEY_INFO_IN_USE; + + if (chan->noisefloor) { survey->filled |= SURVEY_INFO_NOISE_DBM; - survey->noise = ah->curchan->noisefloor; + survey->noise = chan->noisefloor; } return 0; -- cgit v1.2.3 From e32ee80b855914e48b1beb53e13203bc874288d8 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 30 Sep 2010 02:44:56 +0100 Subject: ar9170: Remove MODULE_FIRMWARE references to two-stage firmware The combined firmware ar9170.fw is preferred and supports all devices. References to the older two-stage firmware are unnecessary. Signed-off-by: Ben Hutchings Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ar9170/usb.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ar9170/usb.c b/drivers/net/wireless/ath/ar9170/usb.c index a93dc18a45c3..5dbb5361fd51 100644 --- a/drivers/net/wireless/ath/ar9170/usb.c +++ b/drivers/net/wireless/ath/ar9170/usb.c @@ -54,8 +54,6 @@ MODULE_AUTHOR("Christian Lamparter "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Atheros AR9170 802.11n USB wireless"); MODULE_FIRMWARE("ar9170.fw"); -MODULE_FIRMWARE("ar9170-1.fw"); -MODULE_FIRMWARE("ar9170-2.fw"); enum ar9170_requirements { AR9170_REQ_FW1_ONLY = 1, -- cgit v1.2.3 From b1ae1edf9e9872d3aa657cc34ae40c9aadfbc72f Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Thu, 30 Sep 2010 12:22:58 -0700 Subject: ath5k: Allow ath5k to support virtual STA and AP interfaces. Support up to 4 virtual APs and as many virtual STA interfaces as desired. This patch is ported forward from a patch that Patrick McHardy did for me against 2.6.31. Signed-off-by: Ben Greear Acked-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 275 +++++++++++++++++++++++++++------ drivers/net/wireless/ath/ath5k/base.h | 19 ++- drivers/net/wireless/ath/ath5k/reset.c | 4 +- 3 files changed, 249 insertions(+), 49 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 94cc3354f3a6..2ed327a8d690 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -52,6 +52,7 @@ #include #include #include +#include #include @@ -509,8 +510,71 @@ ath5k_setcurmode(struct ath5k_softc *sc, unsigned int mode) } } +struct ath_vif_iter_data { + const u8 *hw_macaddr; + u8 mask[ETH_ALEN]; + u8 active_mac[ETH_ALEN]; /* first active MAC */ + bool need_set_hw_addr; + bool found_active; + bool any_assoc; +}; + +static void ath_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct ath_vif_iter_data *iter_data = data; + int i; + + if (iter_data->hw_macaddr) + for (i = 0; i < ETH_ALEN; i++) + iter_data->mask[i] &= + ~(iter_data->hw_macaddr[i] ^ mac[i]); + + if (!iter_data->found_active) { + iter_data->found_active = true; + memcpy(iter_data->active_mac, mac, ETH_ALEN); + } + + if (iter_data->need_set_hw_addr && iter_data->hw_macaddr) + if (compare_ether_addr(iter_data->hw_macaddr, mac) == 0) + iter_data->need_set_hw_addr = false; + + if (!iter_data->any_assoc) { + struct ath5k_vif *avf = (void *)vif->drv_priv; + if (avf->assoc) + iter_data->any_assoc = true; + } +} + +void ath5k_update_bssid_mask(struct ath5k_softc *sc, struct ieee80211_vif *vif) +{ + struct ath_common *common = ath5k_hw_common(sc->ah); + struct ath_vif_iter_data iter_data; + + /* + * Use the hardware MAC address as reference, the hardware uses it + * together with the BSSID mask when matching addresses. + */ + iter_data.hw_macaddr = common->macaddr; + memset(&iter_data.mask, 0xff, ETH_ALEN); + iter_data.found_active = false; + iter_data.need_set_hw_addr = true; + + if (vif) + ath_vif_iter(&iter_data, vif->addr, vif); + + /* Get list of all active MAC addresses */ + ieee80211_iterate_active_interfaces_atomic(sc->hw, ath_vif_iter, + &iter_data); + memcpy(sc->bssidmask, iter_data.mask, ETH_ALEN); + + if (iter_data.need_set_hw_addr && iter_data.found_active) + ath5k_hw_set_lladdr(sc->ah, iter_data.active_mac); + + ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask); +} + static void -ath5k_mode_setup(struct ath5k_softc *sc) +ath5k_mode_setup(struct ath5k_softc *sc, struct ieee80211_vif *vif) { struct ath5k_hw *ah = sc->ah; u32 rfilt; @@ -520,7 +584,7 @@ ath5k_mode_setup(struct ath5k_softc *sc) ath5k_hw_set_rx_filter(ah, rfilt); if (ath5k_hw_hasbssidmask(ah)) - ath5k_hw_set_bssid_mask(ah, sc->bssidmask); + ath5k_update_bssid_mask(sc, vif); /* configure operational mode */ ath5k_hw_set_opmode(ah, sc->opmode); @@ -698,13 +762,13 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf, flags |= AR5K_TXDESC_RTSENA; cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value; duration = le16_to_cpu(ieee80211_rts_duration(sc->hw, - sc->vif, pktlen, info)); + info->control.vif, pktlen, info)); } if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { flags |= AR5K_TXDESC_CTSENA; cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value; duration = le16_to_cpu(ieee80211_ctstoself_duration(sc->hw, - sc->vif, pktlen, info)); + info->control.vif, pktlen, info)); } ret = ah->ah_setup_tx_desc(ah, ds, pktlen, ieee80211_get_hdrlen_from_skb(skb), padsize, @@ -806,10 +870,13 @@ ath5k_desc_alloc(struct ath5k_softc *sc, struct pci_dev *pdev) list_add_tail(&bf->list, &sc->txbuf); } - /* beacon buffer */ - bf->desc = ds; - bf->daddr = da; - sc->bbuf = bf; + /* beacon buffers */ + INIT_LIST_HEAD(&sc->bcbuf); + for (i = 0; i < ATH_BCBUF; i++, bf++, ds++, da += sizeof(*ds)) { + bf->desc = ds; + bf->daddr = da; + list_add_tail(&bf->list, &sc->bcbuf); + } return 0; err_free: @@ -824,11 +891,12 @@ ath5k_desc_free(struct ath5k_softc *sc, struct pci_dev *pdev) { struct ath5k_buf *bf; - ath5k_txbuf_free_skb(sc, sc->bbuf); list_for_each_entry(bf, &sc->txbuf, list) ath5k_txbuf_free_skb(sc, bf); list_for_each_entry(bf, &sc->rxbuf, list) ath5k_rxbuf_free_skb(sc, bf); + list_for_each_entry(bf, &sc->bcbuf, list) + ath5k_txbuf_free_skb(sc, bf); /* Free memory associated with all descriptors */ pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr); @@ -837,7 +905,6 @@ ath5k_desc_free(struct ath5k_softc *sc, struct pci_dev *pdev) kfree(sc->bufptr); sc->bufptr = NULL; - sc->bbuf = NULL; } @@ -1083,7 +1150,7 @@ ath5k_rx_start(struct ath5k_softc *sc) spin_unlock_bh(&sc->rxbuflock); ath5k_hw_start_rx_dma(ah); /* enable recv descriptors */ - ath5k_mode_setup(sc); /* set filters, etc. */ + ath5k_mode_setup(sc, NULL); /* set filters, etc. */ ath5k_hw_start_rx_pcu(ah); /* re-enable PCU/DMA engine */ return 0; @@ -1750,6 +1817,7 @@ ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { int ret; struct ath5k_softc *sc = hw->priv; + struct ath5k_vif *avf = (void *)vif->drv_priv; struct sk_buff *skb; if (WARN_ON(!vif)) { @@ -1766,11 +1834,11 @@ ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif) ath5k_debug_dump_skb(sc, skb, "BC ", 1); - ath5k_txbuf_free_skb(sc, sc->bbuf); - sc->bbuf->skb = skb; - ret = ath5k_beacon_setup(sc, sc->bbuf); + ath5k_txbuf_free_skb(sc, avf->bbuf); + avf->bbuf->skb = skb; + ret = ath5k_beacon_setup(sc, avf->bbuf); if (ret) - sc->bbuf->skb = NULL; + avf->bbuf->skb = NULL; out: return ret; } @@ -1786,16 +1854,14 @@ out: static void ath5k_beacon_send(struct ath5k_softc *sc) { - struct ath5k_buf *bf = sc->bbuf; struct ath5k_hw *ah = sc->ah; + struct ieee80211_vif *vif; + struct ath5k_vif *avf; + struct ath5k_buf *bf; struct sk_buff *skb; ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, "in beacon_send\n"); - if (unlikely(bf->skb == NULL || sc->opmode == NL80211_IFTYPE_STATION)) { - ATH5K_WARN(sc, "bf=%p bf_skb=%p\n", bf, bf ? bf->skb : NULL); - return; - } /* * Check if the previous beacon has gone out. If * not, don't don't try to post another: skip this @@ -1824,6 +1890,28 @@ ath5k_beacon_send(struct ath5k_softc *sc) sc->bmisscount = 0; } + if (sc->opmode == NL80211_IFTYPE_AP && sc->num_ap_vifs > 1) { + u64 tsf = ath5k_hw_get_tsf64(ah); + u32 tsftu = TSF_TO_TU(tsf); + int slot = ((tsftu % sc->bintval) * ATH_BCBUF) / sc->bintval; + vif = sc->bslot[(slot + 1) % ATH_BCBUF]; + ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, + "tsf %llx tsftu %x intval %u slot %u vif %p\n", + (unsigned long long)tsf, tsftu, sc->bintval, slot, vif); + } else /* only one interface */ + vif = sc->bslot[0]; + + if (!vif) + return; + + avf = (void *)vif->drv_priv; + bf = avf->bbuf; + if (unlikely(bf->skb == NULL || sc->opmode == NL80211_IFTYPE_STATION || + sc->opmode == NL80211_IFTYPE_MONITOR)) { + ATH5K_WARN(sc, "bf=%p bf_skb=%p\n", bf, bf ? bf->skb : NULL); + return; + } + /* * Stop any current dma and put the new frame on the queue. * This should never fail since we check above that no frames @@ -1836,17 +1924,17 @@ ath5k_beacon_send(struct ath5k_softc *sc) /* refresh the beacon for AP mode */ if (sc->opmode == NL80211_IFTYPE_AP) - ath5k_beacon_update(sc->hw, sc->vif); + ath5k_beacon_update(sc->hw, vif); ath5k_hw_set_txdp(ah, sc->bhalq, bf->daddr); ath5k_hw_start_tx_dma(ah, sc->bhalq); ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, "TXDP[%u] = %llx (%p)\n", sc->bhalq, (unsigned long long)bf->daddr, bf->desc); - skb = ieee80211_get_buffered_bc(sc->hw, sc->vif); + skb = ieee80211_get_buffered_bc(sc->hw, vif); while (skb) { ath5k_tx_queue(sc->hw, skb, sc->cabq); - skb = ieee80211_get_buffered_bc(sc->hw, sc->vif); + skb = ieee80211_get_buffered_bc(sc->hw, vif); } sc->bsent++; @@ -1876,6 +1964,12 @@ ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf) u64 hw_tsf; intval = sc->bintval & AR5K_BEACON_PERIOD; + if (sc->opmode == NL80211_IFTYPE_AP && sc->num_ap_vifs > 1) { + intval /= ATH_BCBUF; /* staggered multi-bss beacons */ + if (intval < 15) + ATH5K_WARN(sc, "intval %u is too low, min 15\n", + intval); + } if (WARN_ON(!intval)) return; @@ -2323,6 +2417,10 @@ ath5k_init(struct ath5k_softc *sc) ath_hw_keyreset(common, (u16) i); ath5k_hw_set_ack_bitrate_high(ah, true); + + for (i = 0; i < ARRAY_SIZE(sc->bslot); i++) + sc->bslot[i] = NULL; + ret = 0; done: mmiowb(); @@ -2382,7 +2480,6 @@ ath5k_stop_hw(struct ath5k_softc *sc) ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "putting device to sleep\n"); } - ath5k_txbuf_free_skb(sc, sc->bbuf); mmiowb(); mutex_unlock(&sc->lock); @@ -2587,9 +2684,9 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) } SET_IEEE80211_PERM_ADDR(hw, mac); + memcpy(&sc->lladdr, mac, ETH_ALEN); /* All MAC address bits matter for ACKs */ - memcpy(sc->bssidmask, ath_bcast_mac, ETH_ALEN); - ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask); + ath5k_update_bssid_mask(sc, NULL); regulatory->current_rd = ah->ah_capabilities.cap_eeprom.ee_regdomain; ret = ath_regd_init(regulatory, hw->wiphy, ath5k_reg_notifier); @@ -2687,31 +2784,91 @@ static int ath5k_add_interface(struct ieee80211_hw *hw, { struct ath5k_softc *sc = hw->priv; int ret; + struct ath5k_hw *ah = sc->ah; + struct ath5k_vif *avf = (void *)vif->drv_priv; mutex_lock(&sc->lock); - if (sc->vif) { - ret = 0; + + if ((vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_ADHOC) + && (sc->num_ap_vifs + sc->num_adhoc_vifs) >= ATH_BCBUF) { + ret = -ELNRNG; goto end; } - sc->vif = vif; + /* Don't allow other interfaces if one ad-hoc is configured. + * TODO: Fix the problems with ad-hoc and multiple other interfaces. + * We would need to operate the HW in ad-hoc mode to allow TSF updates + * for the IBSS, but this breaks with additional AP or STA interfaces + * at the moment. */ + if (sc->num_adhoc_vifs || + (sc->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) { + ATH5K_ERR(sc, "Only one single ad-hoc interface is allowed.\n"); + ret = -ELNRNG; + goto end; + } switch (vif->type) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: - sc->opmode = vif->type; + avf->opmode = vif->type; break; default: ret = -EOPNOTSUPP; goto end; } - ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "add interface mode %d\n", sc->opmode); + sc->nvifs++; + ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "add interface mode %d\n", avf->opmode); + /* Assign the vap/adhoc to a beacon xmit slot. */ + if ((avf->opmode == NL80211_IFTYPE_AP) || + (avf->opmode == NL80211_IFTYPE_ADHOC)) { + int slot; + + WARN_ON(list_empty(&sc->bcbuf)); + avf->bbuf = list_first_entry(&sc->bcbuf, struct ath5k_buf, + list); + list_del(&avf->bbuf->list); + + avf->bslot = 0; + for (slot = 0; slot < ATH_BCBUF; slot++) { + if (!sc->bslot[slot]) { + avf->bslot = slot; + break; + } + } + BUG_ON(sc->bslot[avf->bslot] != NULL); + sc->bslot[avf->bslot] = vif; + if (avf->opmode == NL80211_IFTYPE_AP) + sc->num_ap_vifs++; + else + sc->num_adhoc_vifs++; + } + + /* Set combined mode - when APs are configured, operate in AP mode. + * Otherwise use the mode of the new interface. This can currently + * only deal with combinations of APs and STAs. Only one ad-hoc + * interfaces is allowed above. + */ + if (sc->num_ap_vifs) + sc->opmode = NL80211_IFTYPE_AP; + else + sc->opmode = vif->type; + + ath5k_hw_set_opmode(ah, sc->opmode); + + /* Any MAC address is fine, all others are included through the + * filter. + */ + memcpy(&sc->lladdr, vif->addr, ETH_ALEN); ath5k_hw_set_lladdr(sc->ah, vif->addr); - ath5k_mode_setup(sc); + + memcpy(&avf->lladdr, vif->addr, ETH_ALEN); + + ath5k_mode_setup(sc, vif); ret = 0; end: @@ -2724,15 +2881,29 @@ ath5k_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath5k_softc *sc = hw->priv; - u8 mac[ETH_ALEN] = {}; + struct ath5k_vif *avf = (void *)vif->drv_priv; + unsigned int i; mutex_lock(&sc->lock); - if (sc->vif != vif) - goto end; + sc->nvifs--; + + if (avf->bbuf) { + ath5k_txbuf_free_skb(sc, avf->bbuf); + list_add_tail(&avf->bbuf->list, &sc->bcbuf); + for (i = 0; i < ATH_BCBUF; i++) { + if (sc->bslot[i] == vif) { + sc->bslot[i] = NULL; + break; + } + } + avf->bbuf = NULL; + } + if (avf->opmode == NL80211_IFTYPE_AP) + sc->num_ap_vifs--; + else if (avf->opmode == NL80211_IFTYPE_ADHOC) + sc->num_adhoc_vifs--; - ath5k_hw_set_lladdr(sc->ah, mac); - sc->vif = NULL; -end: + ath5k_update_bssid_mask(sc, NULL); mutex_unlock(&sc->lock); } @@ -2815,6 +2986,19 @@ static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw, return ((u64)(mfilt[1]) << 32) | mfilt[0]; } +static bool ath_any_vif_assoc(struct ath5k_softc *sc) +{ + struct ath_vif_iter_data iter_data; + iter_data.hw_macaddr = NULL; + iter_data.any_assoc = false; + iter_data.need_set_hw_addr = false; + iter_data.found_active = true; + + ieee80211_iterate_active_interfaces_atomic(sc->hw, ath_vif_iter, + &iter_data); + return iter_data.any_assoc; +} + #define SUPPORTED_FIF_FLAGS \ FIF_PROMISC_IN_BSS | FIF_ALLMULTI | FIF_FCSFAIL | \ FIF_PLCPFAIL | FIF_CONTROL | FIF_OTHER_BSS | \ @@ -2885,7 +3069,7 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw, /* FIF_BCN_PRBRESP_PROMISC really means to enable beacons * and probes for any BSSID */ - if (*new_flags & FIF_BCN_PRBRESP_PROMISC) + if ((*new_flags & FIF_BCN_PRBRESP_PROMISC) || (sc->nvifs > 1)) rfilt |= AR5K_RX_FILTER_BEACON; /* FIF_CONTROL doc says that if FIF_PROMISC_IN_BSS is not @@ -3070,14 +3254,13 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_bss_conf *bss_conf, u32 changes) { + struct ath5k_vif *avf = (void *)vif->drv_priv; struct ath5k_softc *sc = hw->priv; struct ath5k_hw *ah = sc->ah; struct ath_common *common = ath5k_hw_common(ah); unsigned long flags; mutex_lock(&sc->lock); - if (WARN_ON(sc->vif != vif)) - goto unlock; if (changes & BSS_CHANGED_BSSID) { /* Cache for later use during resets */ @@ -3091,7 +3274,12 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw, sc->bintval = bss_conf->beacon_int; if (changes & BSS_CHANGED_ASSOC) { - sc->assoc = bss_conf->assoc; + avf->assoc = bss_conf->assoc; + if (bss_conf->assoc) + sc->assoc = bss_conf->assoc; + else + sc->assoc = ath_any_vif_assoc(sc); + if (sc->opmode == NL80211_IFTYPE_STATION) set_beacon_filter(hw, sc->assoc); ath5k_hw_set_ledstate(sc->ah, sc->assoc ? @@ -3119,7 +3307,6 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw, BSS_CHANGED_BEACON_INT)) ath5k_beacon_config(sc); - unlock: mutex_unlock(&sc->lock); } @@ -3394,6 +3581,8 @@ ath5k_pci_probe(struct pci_dev *pdev, hw->max_rate_tries = 11; } + hw->vif_data_size = sizeof(struct ath5k_vif); + /* Finish private driver data initialization */ ret = ath5k_attach(pdev, hw); if (ret) diff --git a/drivers/net/wireless/ath/ath5k/base.h b/drivers/net/wireless/ath/ath5k/base.h index 7f9d0d3018e8..b9f6d13b7675 100644 --- a/drivers/net/wireless/ath/ath5k/base.h +++ b/drivers/net/wireless/ath/ath5k/base.h @@ -58,8 +58,7 @@ #define ATH_RXBUF 40 /* number of RX buffers */ #define ATH_TXBUF 200 /* number of TX buffers */ -#define ATH_BCBUF 1 /* number of beacon buffers */ - +#define ATH_BCBUF 4 /* number of beacon buffers */ #define ATH5K_TXQ_LEN_MAX (ATH_TXBUF / 4) /* bufs per queue */ #define ATH5K_TXQ_LEN_LOW (ATH5K_TXQ_LEN_MAX / 2) /* low mark */ @@ -152,6 +151,14 @@ struct ath5k_statistics { #define ATH_CHAN_MAX (14+14+14+252+20) #endif +struct ath5k_vif { + bool assoc; /* are we associated or not */ + enum nl80211_iftype opmode; + int bslot; + struct ath5k_buf *bbuf; /* beacon buffer */ + u8 lladdr[ETH_ALEN]; +}; + /* Software Carrier, keeps track of the driver state * associated with an instance of a device */ struct ath5k_softc { @@ -188,10 +195,11 @@ struct ath5k_softc { unsigned int curmode; /* current phy mode */ struct ieee80211_channel *curchan; /* current h/w channel */ - struct ieee80211_vif *vif; + u16 nvifs; enum ath5k_int imask; /* interrupt mask copy */ + u8 lladdr[ETH_ALEN]; u8 bssidmask[ETH_ALEN]; unsigned int led_pin, /* GPIO pin for driving LED */ @@ -219,7 +227,10 @@ struct ath5k_softc { spinlock_t block; /* protects beacon */ struct tasklet_struct beacontq; /* beacon intr tasklet */ - struct ath5k_buf *bbuf; /* beacon buffer */ + struct list_head bcbuf; /* beacon buffer */ + struct ieee80211_vif *bslot[ATH_BCBUF]; + u16 num_ap_vifs; + u16 num_adhoc_vifs; unsigned int bhalq, /* SW q for outgoing beacons */ bmisscount, /* missed beacon transmits */ bintval, /* beacon interval in TU */ diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c index 58912cd762d9..5b179d01f97d 100644 --- a/drivers/net/wireless/ath/ath5k/reset.c +++ b/drivers/net/wireless/ath/ath5k/reset.c @@ -167,7 +167,7 @@ static inline void ath5k_hw_write_rate_duration(struct ath5k_hw *ah, * 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, - sc->vif, 10, rate)); + NULL, 10, rate)); ath5k_hw_reg_write(ah, tx_time, reg); @@ -1060,7 +1060,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, * XXX: rethink this after new mode changes to * mac80211 are integrated */ if (ah->ah_version == AR5K_AR5212 && - ah->ah_sc->vif != NULL) + ah->ah_sc->nvifs) ath5k_hw_write_rate_duration(ah, mode); /* -- cgit v1.2.3 From f209f5298217cf54cd5a9163e18b08d093faf8d9 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 1 Oct 2010 01:06:53 +0200 Subject: ath9k: fix channel flag / regd issues with multiple cards Since the regulatory code touches the channel array, it needs to be copied for each device instance. That way the original channel array can also be made const. Signed-off-by: Felix Fietkau Cc: stable@kernel.org [all] Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/init.c | 37 +++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 11dc3ac20cd6..282bb482424f 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -56,7 +56,7 @@ MODULE_PARM_DESC(blink, "Enable LED blink on activity"); * on 5 MHz steps, we support the channels which we know * we have calibration data for all cards though to make * this static */ -static struct ieee80211_channel ath9k_2ghz_chantable[] = { +static const struct ieee80211_channel ath9k_2ghz_chantable[] = { CHAN2G(2412, 0), /* Channel 1 */ CHAN2G(2417, 1), /* Channel 2 */ CHAN2G(2422, 2), /* Channel 3 */ @@ -77,7 +77,7 @@ static struct ieee80211_channel ath9k_2ghz_chantable[] = { * on 5 MHz steps, we support the channels which we know * we have calibration data for all cards though to make * this static */ -static struct ieee80211_channel ath9k_5ghz_chantable[] = { +static const struct ieee80211_channel ath9k_5ghz_chantable[] = { /* _We_ call this UNII 1 */ CHAN5G(5180, 14), /* Channel 36 */ CHAN5G(5200, 15), /* Channel 40 */ @@ -477,10 +477,17 @@ err: return -EIO; } -static void ath9k_init_channels_rates(struct ath_softc *sc) +static int ath9k_init_channels_rates(struct ath_softc *sc) { + void *channels; + if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) { - sc->sbands[IEEE80211_BAND_2GHZ].channels = ath9k_2ghz_chantable; + channels = kmemdup(ath9k_2ghz_chantable, + sizeof(ath9k_2ghz_chantable), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + sc->sbands[IEEE80211_BAND_2GHZ].channels = channels; sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; sc->sbands[IEEE80211_BAND_2GHZ].n_channels = ARRAY_SIZE(ath9k_2ghz_chantable); @@ -490,7 +497,15 @@ static void ath9k_init_channels_rates(struct ath_softc *sc) } if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) { - sc->sbands[IEEE80211_BAND_5GHZ].channels = ath9k_5ghz_chantable; + channels = kmemdup(ath9k_5ghz_chantable, + sizeof(ath9k_5ghz_chantable), GFP_KERNEL); + if (!channels) { + if (sc->sbands[IEEE80211_BAND_2GHZ].channels) + kfree(sc->sbands[IEEE80211_BAND_2GHZ].channels); + return -ENOMEM; + } + + sc->sbands[IEEE80211_BAND_5GHZ].channels = channels; sc->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ; sc->sbands[IEEE80211_BAND_5GHZ].n_channels = ARRAY_SIZE(ath9k_5ghz_chantable); @@ -499,6 +514,7 @@ static void ath9k_init_channels_rates(struct ath_softc *sc) sc->sbands[IEEE80211_BAND_5GHZ].n_bitrates = ARRAY_SIZE(ath9k_legacy_rates) - 4; } + return 0; } static void ath9k_init_misc(struct ath_softc *sc) @@ -594,8 +610,11 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid, if (ret) goto err_btcoex; + ret = ath9k_init_channels_rates(sc); + if (ret) + goto err_btcoex; + ath9k_init_crypto(sc); - ath9k_init_channels_rates(sc); ath9k_init_misc(sc); return 0; @@ -755,6 +774,12 @@ static void ath9k_deinit_softc(struct ath_softc *sc) { int i = 0; + if (sc->sbands[IEEE80211_BAND_2GHZ].channels) + kfree(sc->sbands[IEEE80211_BAND_2GHZ].channels); + + if (sc->sbands[IEEE80211_BAND_5GHZ].channels) + kfree(sc->sbands[IEEE80211_BAND_5GHZ].channels); + if ((sc->btcoex.no_stomp_timer) && sc->sc_ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) ath_gen_timer_free(sc->sc_ah, sc->btcoex.no_stomp_timer); -- cgit v1.2.3 From 9094a086f24bfb1d1f244883020c4d9453ffc0b6 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 1 Oct 2010 01:46:13 +0200 Subject: ath9k_hw: remove some useless calibration data The percal struct and bitmask for the initial DC calibration are not used anywhere, so they can be removed. Signed-off-by: Felix Fietkau --- drivers/net/wireless/ath/ath9k/ar9002_calib.c | 16 ---------------- drivers/net/wireless/ath/ath9k/ar9003_calib.c | 1 - drivers/net/wireless/ath/ath9k/calib.h | 1 - drivers/net/wireless/ath/ath9k/hw.h | 1 - 4 files changed, 19 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9002_calib.c b/drivers/net/wireless/ath/ath9k/ar9002_calib.c index d7d1d55362e6..f2da119bbf02 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c @@ -45,11 +45,6 @@ static void ar9002_hw_setup_calibration(struct ath_hw *ah, ath_print(common, ATH_DBG_CALIBRATE, "starting ADC DC Calibration\n"); break; - case ADC_DC_INIT_CAL: - REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_INIT); - ath_print(common, ATH_DBG_CALIBRATE, - "starting Init ADC DC Calibration\n"); - break; case TEMP_COMP_CAL: break; /* Not supported */ } @@ -950,13 +945,6 @@ static const struct ath9k_percal_data adc_dc_cal_single_sample = { ar9002_hw_adc_dccal_collect, ar9002_hw_adc_dccal_calibrate }; -static const struct ath9k_percal_data adc_init_dc_cal = { - ADC_DC_INIT_CAL, - MIN_CAL_SAMPLES, - INIT_LOG_COUNT, - ar9002_hw_adc_dccal_collect, - ar9002_hw_adc_dccal_calibrate -}; static void ar9002_hw_init_cal_settings(struct ath_hw *ah) { @@ -973,16 +961,12 @@ static void ar9002_hw_init_cal_settings(struct ath_hw *ah) &adc_gain_cal_single_sample; ah->adcdc_caldata.calData = &adc_dc_cal_single_sample; - ah->adcdc_calinitdata.calData = - &adc_init_dc_cal; } else { ah->iq_caldata.calData = &iq_cal_multi_sample; ah->adcgain_caldata.calData = &adc_gain_cal_multi_sample; ah->adcdc_caldata.calData = &adc_dc_cal_multi_sample; - ah->adcdc_calinitdata.calData = - &adc_init_dc_cal; } ah->supp_cals = ADC_GAIN_CAL | ADC_DC_CAL | IQ_MISMATCH_CAL; } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 4674ea8c9c99..b41f5cda824b 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -50,7 +50,6 @@ static void ar9003_hw_setup_calibration(struct ath_hw *ah, ath_print(common, ATH_DBG_CALIBRATE, "starting Temperature Compensation Calibration\n"); break; - case ADC_DC_INIT_CAL: case ADC_GAIN_CAL: case ADC_DC_CAL: /* Not yet */ diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h index 5b053a6260b2..1fa56c91a894 100644 --- a/drivers/net/wireless/ath/ath9k/calib.h +++ b/drivers/net/wireless/ath/ath9k/calib.h @@ -59,7 +59,6 @@ struct ar5416IniArray { } while (0) enum ath9k_cal_types { - ADC_DC_INIT_CAL = 0x1, ADC_GAIN_CAL = 0x2, ADC_DC_CAL = 0x4, IQ_MISMATCH_CAL = 0x8, diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 1b6739bd93ff..235cb5357a22 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -692,7 +692,6 @@ struct ath_hw { enum ath9k_cal_types supp_cals; struct ath9k_cal_list iq_caldata; struct ath9k_cal_list adcgain_caldata; - struct ath9k_cal_list adcdc_calinitdata; struct ath9k_cal_list adcdc_caldata; struct ath9k_cal_list tempCompCalData; struct ath9k_cal_list *cal_list; -- cgit v1.2.3 From 8e0167a4bdce4adcea64d2197378673d332cda28 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Fri, 1 Oct 2010 15:25:05 +0530 Subject: ath9k : Fix for displaying the channel number In the ath9k debugging feature 'wiphy' the current channel used by the station is incorrectly displayed.This is because the channels available are sequentially mapped from numbers 0 to 37.This mapping cannot be changed as the channel number is also used as an array index This fix solves the above problem by calculating the channel number from center frequency. Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/debug.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index d65a896a421d..7a5932a6691a 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -488,6 +488,8 @@ static ssize_t read_file_wiphy(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; + struct ath_wiphy *aphy = sc->pri_wiphy; + struct ieee80211_channel *chan = aphy->hw->conf.channel; char buf[512]; unsigned int len = 0; int i; @@ -498,7 +500,8 @@ static ssize_t read_file_wiphy(struct file *file, char __user *user_buf, "primary: %s (%s chan=%d ht=%d)\n", wiphy_name(sc->pri_wiphy->hw->wiphy), ath_wiphy_state_str(sc->pri_wiphy->state), - sc->pri_wiphy->chan_idx, sc->pri_wiphy->chan_is_ht); + ieee80211_frequency_to_channel(chan->center_freq), + aphy->chan_is_ht); put_unaligned_le32(REG_READ_D(sc->sc_ah, AR_STA_ID0), addr); put_unaligned_le16(REG_READ_D(sc->sc_ah, AR_STA_ID1) & 0xffff, addr + 4); @@ -545,11 +548,13 @@ static ssize_t read_file_wiphy(struct file *file, char __user *user_buf, struct ath_wiphy *aphy = sc->sec_wiphy[i]; if (aphy == NULL) continue; + chan = aphy->hw->conf.channel; len += snprintf(buf + len, sizeof(buf) - len, - "secondary: %s (%s chan=%d ht=%d)\n", - wiphy_name(aphy->hw->wiphy), - ath_wiphy_state_str(aphy->state), - aphy->chan_idx, aphy->chan_is_ht); + "secondary: %s (%s chan=%d ht=%d)\n", + wiphy_name(aphy->hw->wiphy), + ath_wiphy_state_str(aphy->state), + ieee80211_frequency_to_channel(chan->center_freq), + aphy->chan_is_ht); } if (len > sizeof(buf)) len = sizeof(buf); -- cgit v1.2.3 From e51f3eff9a7e17ddd749799d4291f7e33e9883b9 Mon Sep 17 00:00:00 2001 From: Bill Jordan Date: Fri, 1 Oct 2010 11:20:39 -0400 Subject: ath9k: add WDS interfaces to ath9k Enable WDS for the ath9k driver. Signed-off-by: Bill Jordan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/init.c | 1 + drivers/net/wireless/ath/ath9k/main.c | 3 +++ 2 files changed, 4 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 282bb482424f..d76003c06fe4 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -657,6 +657,7 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_WDS) | BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MESH_POINT); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 49ea45896b89..e6ddf45506be 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1374,6 +1374,9 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, case NL80211_IFTYPE_STATION: ic_opmode = NL80211_IFTYPE_STATION; break; + case NL80211_IFTYPE_WDS: + ic_opmode = NL80211_IFTYPE_WDS; + break; case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_AP: case NL80211_IFTYPE_MESH_POINT: -- cgit v1.2.3 From b72acddbbe521d1372e7e9106e9d72e1cbab3010 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Fri, 1 Oct 2010 10:54:04 -0700 Subject: ath5k: Print rx/tx bytes in debugfs This adds counters for tx and rx bytes, including any errored packets as well as all wireless headers. Signed-off-by: Ben Greear Acked-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 2 ++ drivers/net/wireless/ath/ath5k/base.h | 7 +++++++ drivers/net/wireless/ath/ath5k/debug.c | 4 ++++ 3 files changed, 13 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 2ed327a8d690..d914a31cb0b6 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1433,6 +1433,7 @@ static bool ath5k_receive_frame_ok(struct ath5k_softc *sc, struct ath5k_rx_status *rs) { sc->stats.rx_all_count++; + sc->stats.rx_bytes_count += rs->rs_datalen; if (unlikely(rs->rs_status)) { if (rs->rs_status & AR5K_RXERR_CRC) @@ -1611,6 +1612,7 @@ ath5k_tx_frame_completed(struct ath5k_softc *sc, struct sk_buff *skb, int i; sc->stats.tx_all_count++; + sc->stats.tx_bytes_count += skb->len; info = IEEE80211_SKB_CB(skb); ieee80211_tx_info_clear_status(info); diff --git a/drivers/net/wireless/ath/ath5k/base.h b/drivers/net/wireless/ath/ath5k/base.h index b9f6d13b7675..9a79773cdc2a 100644 --- a/drivers/net/wireless/ath/ath5k/base.h +++ b/drivers/net/wireless/ath/ath5k/base.h @@ -121,6 +121,13 @@ struct ath5k_statistics { /* frame errors */ unsigned int rx_all_count; /* all RX frames, including errors */ unsigned int tx_all_count; /* all TX frames, including errors */ + unsigned int rx_bytes_count; /* all RX bytes, including errored pks + * and the MAC headers for each packet + */ + unsigned int tx_bytes_count; /* all TX bytes, including errored pkts + * and the MAC headers and padding for + * each packet. + */ unsigned int rxerr_crc; unsigned int rxerr_phy; unsigned int rxerr_phy_code[32]; diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c index 0f06e8490314..c2d549f871f9 100644 --- a/drivers/net/wireless/ath/ath5k/debug.c +++ b/drivers/net/wireless/ath/ath5k/debug.c @@ -587,6 +587,8 @@ static ssize_t read_file_frameerrors(struct file *file, char __user *user_buf, st->rxerr_jumbo*100/st->rx_all_count : 0); len += snprintf(buf+len, sizeof(buf)-len, "[RX all\t%d]\n", st->rx_all_count); + len += snprintf(buf+len, sizeof(buf)-len, "RX-all-bytes\t%d\n", + st->rx_bytes_count); len += snprintf(buf+len, sizeof(buf)-len, "\nTX\n---------------------\n"); @@ -604,6 +606,8 @@ static ssize_t read_file_frameerrors(struct file *file, char __user *user_buf, st->txerr_filt*100/st->tx_all_count : 0); len += snprintf(buf+len, sizeof(buf)-len, "[TX all\t%d]\n", st->tx_all_count); + len += snprintf(buf+len, sizeof(buf)-len, "TX-all-bytes\t%d\n", + st->tx_bytes_count); if (len > sizeof(buf)) len = sizeof(buf); -- cgit v1.2.3 From 99c15bf575b18e12c9373304a6a09a78f9c8a017 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Fri, 1 Oct 2010 12:26:30 -0700 Subject: ath9k: Report total tx/rx bytes and packets in debugfs. Includes pkts/bytes that may have had errors, and includes wireless headers when counting bytes. Signed-off-by: Ben Greear Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/debug.c | 15 +++++++++++++++ drivers/net/wireless/ath/ath9k/debug.h | 12 ++++++++++++ 2 files changed, 27 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 7a5932a6691a..74a4570dc87f 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -701,6 +701,8 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf, PR("DESC CFG Error: ", desc_cfg_err); PR("DATA Underrun: ", data_underrun); PR("DELIM Underrun: ", delim_underrun); + PR("TX-Pkts-All: ", tx_pkts_all); + PR("TX-Bytes-All: ", tx_bytes_all); if (len > size) len = size; @@ -714,6 +716,9 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf, void ath_debug_stat_tx(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf, struct ath_tx_status *ts) { + TX_STAT_INC(txq->axq_qnum, tx_pkts_all); + sc->debug.stats.txstats[txq->axq_qnum].tx_bytes_all += bf->bf_mpdu->len; + if (bf_isampdu(bf)) { if (bf_isxretried(bf)) TX_STAT_INC(txq->axq_qnum, a_xretries); @@ -808,6 +813,13 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, PHY_ERR("HT-LENGTH", ATH9K_PHYERR_HT_LENGTH_ILLEGAL); PHY_ERR("HT-RATE", ATH9K_PHYERR_HT_RATE_ILLEGAL); + len += snprintf(buf + len, size - len, + "%18s : %10u\n", "RX-Pkts-All", + sc->debug.stats.rxstats.rx_pkts_all); + len += snprintf(buf + len, size - len, + "%18s : %10u\n", "RX-Bytes-All", + sc->debug.stats.rxstats.rx_bytes_all); + if (len > size) len = size; @@ -826,6 +838,9 @@ void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs) u32 phyerr; + RX_STAT_INC(rx_pkts_all); + sc->debug.stats.rxstats.rx_bytes_all += rs->rs_datalen; + if (rs->rs_status & ATH9K_RXERR_CRC) RX_STAT_INC(crc_err); if (rs->rs_status & ATH9K_RXERR_DECRYPT) diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 5d21704e87ff..822b6f3f23c5 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -89,6 +89,10 @@ struct ath_rc_stats { /** * struct ath_tx_stats - Statistics about TX + * @tx_pkts_all: No. of total frames transmitted, including ones that + may have had errors. + * @tx_bytes_all: No. of total bytes transmitted, including ones that + may have had errors. * @queued: Total MPDUs (non-aggr) queued * @completed: Total MPDUs (non-aggr) completed * @a_aggr: Total no. of aggregates queued @@ -107,6 +111,8 @@ struct ath_rc_stats { * @delim_urn: TX delimiter underrun errors */ struct ath_tx_stats { + u32 tx_pkts_all; + u32 tx_bytes_all; u32 queued; u32 completed; u32 a_aggr; @@ -124,6 +130,10 @@ struct ath_tx_stats { /** * struct ath_rx_stats - RX Statistics + * @rx_pkts_all: No. of total frames received, including ones that + may have had errors. + * @rx_bytes_all: No. of total bytes received, including ones that + may have had errors. * @crc_err: No. of frames with incorrect CRC value * @decrypt_crc_err: No. of frames whose CRC check failed after decryption process completed @@ -136,6 +146,8 @@ struct ath_tx_stats { * @phy_err_stats: Individual PHY error statistics */ struct ath_rx_stats { + u32 rx_pkts_all; + u32 rx_bytes_all; u32 crc_err; u32 decrypt_crc_err; u32 phy_err; -- cgit v1.2.3 From 4198a8d036c998ca8cec1b08116ab9f6a3a9ffff Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Tue, 5 Oct 2010 13:27:17 +0900 Subject: ath5k: Don't wake internal queues We should only wake up queues which mac80211 knows about (queues 0-3). We have another internal queue ("CAB", queue number 6) which we use for power-saved frames. When transmitted frames are processed from this queue, we have to make sure we don't bother mac80211 with waking a queue it doesn't know about. this fixes: WARNING: at /home/br1/ath/wireless-testing/net/mac80211/util.c:275 __ieee80211_wake_queue+0xd6/0xe0 [mac80211]() Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index d914a31cb0b6..dad726585637 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1711,7 +1711,7 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq) } } spin_unlock(&txq->lock); - if (txq->txq_len < ATH5K_TXQ_LEN_LOW) + if (txq->txq_len < ATH5K_TXQ_LEN_LOW && txq->qnum < 4) ieee80211_wake_queue(sc->hw, txq->qnum); } -- cgit v1.2.3 From 970bf9d40c03e48cc34ee2c1a70693a0e0fca3f6 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Tue, 5 Oct 2010 11:32:17 +0200 Subject: ath9k: get correct tx gain type in ath9k_hw_4k_get_eeprom The base_eep_header_4k structure contains information that the device supports high power tx gain table or not. However the ath9k_hw_4k_get_eeprom function does not return that value when it is called with EEP_TXGAIN_TYPE. This leads to that the tx gain initialization will use the init values from the original tx gain table even if the device inidicates that the high power table should be used. Changes-licensed-under: ISC Signed-off-by: Gabor Juhos Signed-off-by: Shu Hwa Shen Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/eeprom_4k.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c index d6eed1f02e84..872e75b0b574 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c @@ -179,6 +179,9 @@ static u32 ath9k_hw_4k_get_eeprom(struct ath_hw *ah, struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; struct modal_eep_4k_header *pModal = &eep->modalHeader; struct base_eep_header_4k *pBase = &eep->baseEepHeader; + u16 ver_minor; + + ver_minor = pBase->version & AR5416_EEP_VER_MINOR_MASK; switch (param) { case EEP_NFTHRESH_2: @@ -204,7 +207,7 @@ static u32 ath9k_hw_4k_get_eeprom(struct ath_hw *ah, case EEP_DB_2: return pModal->db1_1; case EEP_MINOR_REV: - return pBase->version & AR5416_EEP_VER_MINOR_MASK; + return ver_minor; case EEP_TX_MASK: return pBase->txMask; case EEP_RX_MASK: @@ -217,6 +220,11 @@ static u32 ath9k_hw_4k_get_eeprom(struct ath_hw *ah, return pModal->version; case EEP_ANT_DIV_CTL1: return pModal->antdiv_ctl1; + case EEP_TXGAIN_TYPE: + if (ver_minor >= AR5416_EEP_MINOR_VER_19) + return pBase->txGainType; + else + return AR5416_EEP_TXGAIN_ORIGINAL; default: return 0; } -- cgit v1.2.3 From 6497827f53eb90dcf30c5d6414c83238f722e8ae Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 3 Oct 2010 19:07:16 +0200 Subject: ath9k_hw: clean up calibration flags The calibration actual calibration flags are only used by the per chip family source files, so it makes more sense to define them in those files instead of globally. That way the code has to test for less flags. Also instead of using a separate callback for testing whether a particular calibration type is supported, simply adjust ah->supp_cals in the calibration init which is called right after the hardware reset, before any of the calibrations are run. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9002_calib.c | 51 +++++++++++---------------- drivers/net/wireless/ath/ath9k/ar9003_calib.c | 36 +++++-------------- drivers/net/wireless/ath/ath9k/calib.c | 2 +- drivers/net/wireless/ath/ath9k/calib.h | 9 +---- drivers/net/wireless/ath/ath9k/hw-ops.h | 6 ---- drivers/net/wireless/ath/ath9k/hw.h | 4 +-- 6 files changed, 31 insertions(+), 77 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9002_calib.c b/drivers/net/wireless/ath/ath9k/ar9002_calib.c index f2da119bbf02..f0525fb62a2c 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c @@ -20,6 +20,13 @@ #define AR9285_CLCAL_REDO_THRESH 1 +enum ar9002_cal_types { + ADC_GAIN_CAL = BIT(0), + ADC_DC_CAL = BIT(1), + IQ_MISMATCH_CAL = BIT(2), +}; + + static void ar9002_hw_setup_calibration(struct ath_hw *ah, struct ath9k_cal_list *currCal) { @@ -45,8 +52,6 @@ static void ar9002_hw_setup_calibration(struct ath_hw *ah, ath_print(common, ATH_DBG_CALIBRATE, "starting ADC DC Calibration\n"); break; - case TEMP_COMP_CAL: - break; /* Not supported */ } REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4(0), @@ -91,25 +96,6 @@ static bool ar9002_hw_per_calibration(struct ath_hw *ah, return iscaldone; } -/* Assumes you are talking about the currently configured channel */ -static bool ar9002_hw_iscal_supported(struct ath_hw *ah, - enum ath9k_cal_types calType) -{ - struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; - - switch (calType & ah->supp_cals) { - case IQ_MISMATCH_CAL: /* Both 2 GHz and 5 GHz support OFDM */ - return true; - case ADC_GAIN_CAL: - case ADC_DC_CAL: - if (!(conf->channel->band == IEEE80211_BAND_2GHZ && - conf_is_ht20(conf))) - return true; - break; - } - return false; -} - static void ar9002_hw_iqcal_collect(struct ath_hw *ah) { int i; @@ -872,24 +858,28 @@ static bool ar9002_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) /* Enable IQ, ADC Gain and ADC DC offset CALs */ if (AR_SREV_9100(ah) || AR_SREV_9160_10_OR_LATER(ah)) { - if (ar9002_hw_iscal_supported(ah, ADC_GAIN_CAL)) { + ah->supp_cals = IQ_MISMATCH_CAL; + + if (AR_SREV_9160_10_OR_LATER(ah) && + !(IS_CHAN_2GHZ(chan) && IS_CHAN_HT20(chan))) { + ah->supp_cals |= ADC_GAIN_CAL | ADC_DC_CAL; + + INIT_CAL(&ah->adcgain_caldata); INSERT_CAL(ah, &ah->adcgain_caldata); ath_print(common, ATH_DBG_CALIBRATE, "enabling ADC Gain Calibration.\n"); - } - if (ar9002_hw_iscal_supported(ah, ADC_DC_CAL)) { + INIT_CAL(&ah->adcdc_caldata); INSERT_CAL(ah, &ah->adcdc_caldata); ath_print(common, ATH_DBG_CALIBRATE, "enabling ADC DC Calibration.\n"); } - if (ar9002_hw_iscal_supported(ah, IQ_MISMATCH_CAL)) { - INIT_CAL(&ah->iq_caldata); - INSERT_CAL(ah, &ah->iq_caldata); - ath_print(common, ATH_DBG_CALIBRATE, - "enabling IQ Calibration.\n"); - } + + INIT_CAL(&ah->iq_caldata); + INSERT_CAL(ah, &ah->iq_caldata); + ath_print(common, ATH_DBG_CALIBRATE, + "enabling IQ Calibration.\n"); ah->cal_list_curr = ah->cal_list; @@ -980,7 +970,6 @@ void ar9002_hw_attach_calib_ops(struct ath_hw *ah) priv_ops->init_cal_settings = ar9002_hw_init_cal_settings; priv_ops->init_cal = ar9002_hw_init_cal; priv_ops->setup_calibration = ar9002_hw_setup_calibration; - priv_ops->iscal_supported = ar9002_hw_iscal_supported; ops->calibrate = ar9002_hw_calibrate; } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index b41f5cda824b..9e6edffe0bd1 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -18,6 +18,11 @@ #include "hw-ops.h" #include "ar9003_phy.h" +enum ar9003_cal_types { + IQ_MISMATCH_CAL = BIT(0), + TEMP_COMP_CAL = BIT(1), +}; + static void ar9003_hw_setup_calibration(struct ath_hw *ah, struct ath9k_cal_list *currCal) { @@ -50,10 +55,6 @@ static void ar9003_hw_setup_calibration(struct ath_hw *ah, ath_print(common, ATH_DBG_CALIBRATE, "starting Temperature Compensation Calibration\n"); break; - case ADC_GAIN_CAL: - case ADC_DC_CAL: - /* Not yet */ - break; } } @@ -313,27 +314,6 @@ static const struct ath9k_percal_data iq_cal_single_sample = { static void ar9003_hw_init_cal_settings(struct ath_hw *ah) { ah->iq_caldata.calData = &iq_cal_single_sample; - ah->supp_cals = IQ_MISMATCH_CAL; -} - -static bool ar9003_hw_iscal_supported(struct ath_hw *ah, - enum ath9k_cal_types calType) -{ - switch (calType & ah->supp_cals) { - case IQ_MISMATCH_CAL: - /* - * XXX: Run IQ Mismatch for non-CCK only - * Note that CHANNEL_B is never set though. - */ - return true; - case ADC_GAIN_CAL: - case ADC_DC_CAL: - return false; - case TEMP_COMP_CAL: - return true; - } - - return false; } /* @@ -772,15 +752,16 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, /* Initialize list pointers */ ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL; + ah->supp_cals = IQ_MISMATCH_CAL; - if (ar9003_hw_iscal_supported(ah, IQ_MISMATCH_CAL)) { + if (ah->supp_cals & IQ_MISMATCH_CAL) { INIT_CAL(&ah->iq_caldata); INSERT_CAL(ah, &ah->iq_caldata); ath_print(common, ATH_DBG_CALIBRATE, "enabling IQ Calibration.\n"); } - if (ar9003_hw_iscal_supported(ah, TEMP_COMP_CAL)) { + if (ah->supp_cals & TEMP_COMP_CAL) { INIT_CAL(&ah->tempCompCalData); INSERT_CAL(ah, &ah->tempCompCalData); ath_print(common, ATH_DBG_CALIBRATE, @@ -807,7 +788,6 @@ void ar9003_hw_attach_calib_ops(struct ath_hw *ah) priv_ops->init_cal_settings = ar9003_hw_init_cal_settings; priv_ops->init_cal = ar9003_hw_init_cal; priv_ops->setup_calibration = ar9003_hw_setup_calibration; - priv_ops->iscal_supported = ar9003_hw_iscal_supported; ops->calibrate = ar9003_hw_calibrate; } diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 6351e76792a6..6c38c72915c1 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -186,7 +186,7 @@ bool ath9k_hw_reset_calvalid(struct ath_hw *ah) return true; } - if (!ath9k_hw_iscal_supported(ah, currCal->calData->calType)) + if (!(ah->supp_cals & currCal->calData->calType)) return true; ath_print(common, ATH_DBG_CALIBRATE, diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h index 1fa56c91a894..b8973eb8d858 100644 --- a/drivers/net/wireless/ath/ath9k/calib.h +++ b/drivers/net/wireless/ath/ath9k/calib.h @@ -58,13 +58,6 @@ struct ar5416IniArray { } \ } while (0) -enum ath9k_cal_types { - ADC_GAIN_CAL = 0x2, - ADC_DC_CAL = 0x4, - IQ_MISMATCH_CAL = 0x8, - TEMP_COMP_CAL = 0x10, -}; - enum ath9k_cal_state { CAL_INACTIVE, CAL_WAITING, @@ -79,7 +72,7 @@ enum ath9k_cal_state { #define PER_MAX_LOG_COUNT 10 struct ath9k_percal_data { - enum ath9k_cal_types calType; + u32 calType; u32 calNumSamples; u32 calCountMax; void (*calCollect) (struct ath_hw *); diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h index ffecbadaea4a..9c4dd0ec9a15 100644 --- a/drivers/net/wireless/ath/ath9k/hw-ops.h +++ b/drivers/net/wireless/ath/ath9k/hw-ops.h @@ -276,12 +276,6 @@ static inline void ath9k_hw_setup_calibration(struct ath_hw *ah, ath9k_hw_private_ops(ah)->setup_calibration(ah, currCal); } -static inline bool ath9k_hw_iscal_supported(struct ath_hw *ah, - enum ath9k_cal_types calType) -{ - return ath9k_hw_private_ops(ah)->iscal_supported(ah, calType); -} - static inline void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) { ath9k_hw_private_ops(ah)->ani_reset(ah, is_scanning); diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 235cb5357a22..246c707cea00 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -535,8 +535,6 @@ struct ath_hw_private_ops { bool (*macversion_supported)(u32 macversion); void (*setup_calibration)(struct ath_hw *ah, struct ath9k_cal_list *currCal); - bool (*iscal_supported)(struct ath_hw *ah, - enum ath9k_cal_types calType); /* PHY ops */ int (*rf_set_freq)(struct ath_hw *ah, @@ -689,7 +687,7 @@ struct ath_hw { u32 atim_window; /* Calibration */ - enum ath9k_cal_types supp_cals; + u32 supp_cals; struct ath9k_cal_list iq_caldata; struct ath9k_cal_list adcgain_caldata; struct ath9k_cal_list adcdc_caldata; -- cgit v1.2.3 From 9dbebc7fd07ab66341dce8d001272db400c11e03 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 3 Oct 2010 19:07:17 +0200 Subject: ath9k_hw: merge codepaths that access the cycle counter registers The cycle counters are used by ANI to determine the amount of time that the radio spent not receiving or transmitting. They're also used for debugging purposes if the baseband watchdog on AR9003 detects a lockup. In the future, we want to use these counters to determine the medium utilization and export this information via survey. For that, we need to make sure that the counter is only accessed from one place, which also ensures that wraparounds won't occur at inconvenient points in time. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 119 +++++++++++----------------- drivers/net/wireless/ath/ath9k/ani.h | 13 +-- drivers/net/wireless/ath/ath9k/ar5008_phy.c | 9 +-- drivers/net/wireless/ath/ath9k/ar9003_phy.c | 14 ++-- drivers/net/wireless/ath/ath9k/hw.h | 2 + 5 files changed, 65 insertions(+), 92 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 45f477ad7608..3fba81e3a61f 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -549,47 +549,15 @@ static u8 ath9k_hw_chan_2_clockrate_mhz(struct ath_hw *ah) static int32_t ath9k_hw_ani_get_listen_time(struct ath_hw *ah) { - struct ar5416AniState *aniState; - struct ath_common *common = ath9k_hw_common(ah); - u32 txFrameCount, rxFrameCount, cycleCount; - int32_t listenTime; - - txFrameCount = REG_READ(ah, AR_TFCNT); - rxFrameCount = REG_READ(ah, AR_RFCNT); - cycleCount = REG_READ(ah, AR_CCCNT); - - aniState = ah->curani; - if (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) { - listenTime = 0; - ah->stats.ast_ani_lzero++; - ath_print(common, ATH_DBG_ANI, - "1st call: aniState->cycleCount=%d\n", - aniState->cycleCount); - } else { - int32_t ccdelta = cycleCount - aniState->cycleCount; - int32_t rfdelta = rxFrameCount - aniState->rxFrameCount; - int32_t tfdelta = txFrameCount - aniState->txFrameCount; - int32_t clock_rate; - - /* - * convert HW counter values to ms using mode - * specifix clock rate - */ - clock_rate = ath9k_hw_chan_2_clockrate_mhz(ah) * 1000;; + int32_t listen_time; + int32_t clock_rate; - listenTime = (ccdelta - rfdelta - tfdelta) / clock_rate; + ath9k_hw_update_cycle_counters(ah); + clock_rate = ath9k_hw_chan_2_clockrate_mhz(ah) * 1000; + listen_time = ah->listen_time / clock_rate; + ah->listen_time = 0; - ath_print(common, ATH_DBG_ANI, - "cyclecount=%d, rfcount=%d, " - "tfcount=%d, listenTime=%d CLOCK_RATE=%d\n", - ccdelta, rfdelta, tfdelta, listenTime, clock_rate); - } - - aniState->cycleCount = cycleCount; - aniState->txFrameCount = txFrameCount; - aniState->rxFrameCount = rxFrameCount; - - return listenTime; + return listen_time; } static void ath9k_ani_reset_old(struct ath_hw *ah, bool is_scanning) @@ -1041,45 +1009,52 @@ void ath9k_hw_disable_mib_counters(struct ath_hw *ah) } EXPORT_SYMBOL(ath9k_hw_disable_mib_counters); -u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hw *ah, - u32 *rxc_pcnt, - u32 *rxf_pcnt, - u32 *txf_pcnt) +void ath9k_hw_update_cycle_counters(struct ath_hw *ah) { - struct ath_common *common = ath9k_hw_common(ah); - static u32 cycles, rx_clear, rx_frame, tx_frame; - u32 good = 1; + struct ath_cycle_counters cc; + bool clear; - u32 rc = REG_READ(ah, AR_RCCNT); - u32 rf = REG_READ(ah, AR_RFCNT); - u32 tf = REG_READ(ah, AR_TFCNT); - u32 cc = REG_READ(ah, AR_CCCNT); + memcpy(&cc, &ah->cc, sizeof(cc)); - if (cycles == 0 || cycles > cc) { - ath_print(common, ATH_DBG_ANI, - "cycle counter wrap. ExtBusy = 0\n"); - good = 0; - } else { - u32 cc_d = cc - cycles; - u32 rc_d = rc - rx_clear; - u32 rf_d = rf - rx_frame; - u32 tf_d = tf - tx_frame; - - if (cc_d != 0) { - *rxc_pcnt = rc_d * 100 / cc_d; - *rxf_pcnt = rf_d * 100 / cc_d; - *txf_pcnt = tf_d * 100 / cc_d; - } else { - good = 0; - } + /* freeze counters */ + REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC); + + ah->cc.cycles = REG_READ(ah, AR_CCCNT); + if (ah->cc.cycles < cc.cycles) { + clear = true; + goto skip; } - cycles = cc; - rx_frame = rf; - rx_clear = rc; - tx_frame = tf; + ah->cc.rx_clear = REG_READ(ah, AR_RCCNT); + ah->cc.rx_frame = REG_READ(ah, AR_RFCNT); + ah->cc.tx_frame = REG_READ(ah, AR_TFCNT); + + /* prevent wraparound */ + if (ah->cc.cycles & BIT(31)) + clear = true; + +#define CC_DELTA(_field, _reg) ah->cc_delta._field += ah->cc._field - cc._field + CC_DELTA(cycles, AR_CCCNT); + CC_DELTA(rx_frame, AR_RFCNT); + CC_DELTA(rx_clear, AR_RCCNT); + CC_DELTA(tx_frame, AR_TFCNT); +#undef CC_DELTA + + ah->listen_time += (ah->cc.cycles - cc.cycles) - + ((ah->cc.rx_frame - cc.rx_frame) + + (ah->cc.tx_frame - cc.tx_frame)); + +skip: + if (clear) { + REG_WRITE(ah, AR_CCCNT, 0); + REG_WRITE(ah, AR_RFCNT, 0); + REG_WRITE(ah, AR_RCCNT, 0); + REG_WRITE(ah, AR_TFCNT, 0); + memset(&ah->cc, 0, sizeof(ah->cc)); + } - return good; + /* unfreeze counters */ + REG_WRITE(ah, AR_MIBC, 0); } /* diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h index f4d0a4d48b37..15f9d67a18c8 100644 --- a/drivers/net/wireless/ath/ath9k/ani.h +++ b/drivers/net/wireless/ath/ath9k/ani.h @@ -93,6 +93,13 @@ struct ath9k_mib_stats { u32 beacons; }; +struct ath_cycle_counters { + u32 cycles; + u32 rx_frame; + u32 rx_clear; + u32 tx_frame; +}; + /* INI default values for ANI registers */ struct ath9k_ani_default { u16 m1ThreshLow; @@ -130,9 +137,6 @@ struct ar5416AniState { int32_t rssiThrLow; int32_t rssiThrHigh; u32 noiseFloor; - u32 txFrameCount; - u32 rxFrameCount; - u32 cycleCount; u32 ofdmPhyErrCount; u32 cckPhyErrCount; u32 ofdmPhyErrBase; @@ -166,8 +170,7 @@ struct ar5416Stats { void ath9k_enable_mib_counters(struct ath_hw *ah); void ath9k_hw_disable_mib_counters(struct ath_hw *ah); -u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hw *ah, u32 *rxc_pcnt, - u32 *rxf_pcnt, u32 *txf_pcnt); +void ath9k_hw_update_cycle_counters(struct ath_hw *ah); void ath9k_hw_ani_setup(struct ath_hw *ah); void ath9k_hw_ani_init(struct ath_hw *ah); int ath9k_hw_get_ani_channel_idx(struct ath_hw *ah, diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index 525671f52b45..a11ca0247793 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -1225,8 +1225,7 @@ static bool ar5008_hw_ani_control_old(struct ath_hw *ah, aniState->firstepLevel, aniState->listenTime); ath_print(common, ATH_DBG_ANI, - "cycleCount=%d, ofdmPhyErrCount=%d, cckPhyErrCount=%d\n\n", - aniState->cycleCount, + "ofdmPhyErrCount=%d, cckPhyErrCount=%d\n\n", aniState->ofdmPhyErrCount, aniState->cckPhyErrCount); @@ -1478,15 +1477,13 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, ath_print(common, ATH_DBG_ANI, "ANI parameters: SI=%d, ofdmWS=%s FS=%d " - "MRCcck=%s listenTime=%d CC=%d listen=%d " + "MRCcck=%s listenTime=%d " "ofdmErrs=%d cckErrs=%d\n", aniState->spurImmunityLevel, !aniState->ofdmWeakSigDetectOff ? "on" : "off", aniState->firstepLevel, !aniState->mrcCCKOff ? "on" : "off", aniState->listenTime, - aniState->cycleCount, - aniState->listenTime, aniState->ofdmPhyErrCount, aniState->cckPhyErrCount); return true; @@ -1579,8 +1576,6 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah) aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_NEW; aniState->ofdmWeakSigDetectOff = !ATH9K_ANI_USE_OFDM_WEAK_SIG; aniState->mrcCCKOff = true; /* not available on pre AR9003 */ - - aniState->cycleCount = 0; } static void ar5008_hw_set_nf_limits(struct ath_hw *ah) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index a491854fa38a..e15574caf61a 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -1005,15 +1005,13 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, ath_print(common, ATH_DBG_ANI, "ANI parameters: SI=%d, ofdmWS=%s FS=%d " - "MRCcck=%s listenTime=%d CC=%d listen=%d " + "MRCcck=%s listenTime=%d " "ofdmErrs=%d cckErrs=%d\n", aniState->spurImmunityLevel, !aniState->ofdmWeakSigDetectOff ? "on" : "off", aniState->firstepLevel, !aniState->mrcCCKOff ? "on" : "off", aniState->listenTime, - aniState->cycleCount, - aniState->listenTime, aniState->ofdmPhyErrCount, aniState->cckPhyErrCount); return true; @@ -1116,8 +1114,6 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah) aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_NEW; aniState->ofdmWeakSigDetectOff = !ATH9K_ANI_USE_OFDM_WEAK_SIG; aniState->mrcCCKOff = !ATH9K_ANI_ENABLE_MRC_CCK; - - aniState->cycleCount = 0; } void ar9003_hw_attach_phy_ops(struct ath_hw *ah) @@ -1232,7 +1228,7 @@ void ar9003_hw_bb_watchdog_read(struct ath_hw *ah) void ar9003_hw_bb_watchdog_dbg_info(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); - u32 rxc_pcnt = 0, rxf_pcnt = 0, txf_pcnt = 0, status; + u32 status; if (likely(!(common->debug_mask & ATH_DBG_RESET))) return; @@ -1261,11 +1257,13 @@ void ar9003_hw_bb_watchdog_dbg_info(struct ath_hw *ah) "** BB mode: BB_gen_controls=0x%08x **\n", REG_READ(ah, AR_PHY_GEN_CTRL)); - if (ath9k_hw_GetMibCycleCountsPct(ah, &rxc_pcnt, &rxf_pcnt, &txf_pcnt)) + ath9k_hw_update_cycle_counters(ah); +#define PCT(_field) (ah->cc_delta._field * 100 / ah->cc_delta.cycles) + if (ah->cc_delta.cycles) ath_print(common, ATH_DBG_RESET, "** BB busy times: rx_clear=%d%%, " "rx_frame=%d%%, tx_frame=%d%% **\n", - rxc_pcnt, rxf_pcnt, txf_pcnt); + PCT(rx_clear), PCT(rx_frame), PCT(tx_frame)); ath_print(common, ATH_DBG_RESET, "==== BB update: done ====\n\n"); diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 246c707cea00..87dbb8502469 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -765,6 +765,8 @@ struct ath_hw { int coarse_low[5]; int firpwr[5]; enum ath9k_ani_cmd ani_function; + struct ath_cycle_counters cc, cc_delta; + int32_t listen_time; /* Bluetooth coexistance */ struct ath_btcoex_hw btcoex_hw; -- cgit v1.2.3 From 435c1610f46dc4d86a6633adb037b18109e6ffdc Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 5 Oct 2010 12:03:42 +0200 Subject: ath9k_hw: clean up register write buffering Throughout the code, DISABLE_REGWRITE_BUFFER is always called right after REGWRITE_BUFFER_FLUSH. Since that's unlikely to change any time soon, that makes keeping those ops separate rather pointless, as it only increases code size and line number counts. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath.h | 4 +--- drivers/net/wireless/ath/ath9k/ani.c | 6 ------ drivers/net/wireless/ath/ath9k/ar5008_phy.c | 9 ++------- drivers/net/wireless/ath/ath9k/ar9002_calib.c | 1 - drivers/net/wireless/ath/ath9k/ar9002_hw.c | 4 ---- drivers/net/wireless/ath/ath9k/ar9002_phy.c | 1 - drivers/net/wireless/ath/ath9k/calib.c | 1 - drivers/net/wireless/ath/ath9k/eeprom_4k.c | 2 -- drivers/net/wireless/ath/ath9k/htc_drv_init.c | 12 ++---------- drivers/net/wireless/ath/ath9k/hw.c | 14 -------------- drivers/net/wireless/ath/ath9k/hw.h | 10 ++-------- drivers/net/wireless/ath/ath9k/mac.c | 4 ---- 12 files changed, 7 insertions(+), 61 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index 5894fcc2c628..cee0191704f5 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -102,14 +102,12 @@ enum ath_cipher { * @read: Register read * @write: Register write * @enable_write_buffer: Enable multiple register writes - * @disable_write_buffer: Disable multiple register writes - * @write_flush: Flush buffered register writes + * @write_flush: flush buffered register writes and disable buffering */ struct ath_ops { unsigned int (*read)(void *, u32 reg_offset); void (*write)(void *, u32 val, u32 reg_offset); void (*enable_write_buffer)(void *); - void (*disable_write_buffer)(void *); void (*write_flush) (void *); }; diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 3fba81e3a61f..a1894d240773 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -180,7 +180,6 @@ static void ath9k_ani_restart_old(struct ath_hw *ah) REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); @@ -215,7 +214,6 @@ static void ath9k_ani_restart_new(struct ath_hw *ah) REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); @@ -643,7 +641,6 @@ static void ath9k_ani_reset_old(struct ath_hw *ah, bool is_scanning) REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); } /* @@ -737,7 +734,6 @@ static void ath9k_ani_reset_new(struct ath_hw *ah, bool is_scanning) REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); } static void ath9k_hw_ani_monitor_old(struct ath_hw *ah, @@ -991,7 +987,6 @@ void ath9k_enable_mib_counters(struct ath_hw *ah) REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); } /* Freeze the MIB counters, get the stats and then clear them */ @@ -1261,7 +1256,6 @@ void ath9k_hw_ani_init(struct ath_hw *ah) REG_WRITE(ah, AR_PHY_ERR_2, ah->ani[0].cckPhyErrBase); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); ath9k_enable_mib_counters(ah); diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index a11ca0247793..7b26292daf35 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -613,14 +613,11 @@ static void ar5008_hw_init_chain_masks(struct ath_hw *ah) rx_chainmask = ah->rxchainmask; tx_chainmask = ah->txchainmask; - ENABLE_REGWRITE_BUFFER(ah); switch (rx_chainmask) { case 0x5: - DISABLE_REGWRITE_BUFFER(ah); REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, AR_PHY_SWAP_ALT_CHAIN); - ENABLE_REGWRITE_BUFFER(ah); case 0x3: if (ah->hw_version.macVersion == AR_SREV_REVISION_5416_10) { REG_WRITE(ah, AR_PHY_RX_CHAINMASK, 0x7); @@ -630,17 +627,18 @@ static void ar5008_hw_init_chain_masks(struct ath_hw *ah) case 0x1: case 0x2: case 0x7: + ENABLE_REGWRITE_BUFFER(ah); REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx_chainmask); REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx_chainmask); break; default: + ENABLE_REGWRITE_BUFFER(ah); break; } REG_WRITE(ah, AR_SELFGEN_MASK, tx_chainmask); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); if (tx_chainmask == 0x5) { REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, @@ -726,7 +724,6 @@ static void ar5008_hw_set_channel_regs(struct ath_hw *ah, REG_WRITE(ah, AR_CST, 0xF << AR_CST_TIMEOUT_LIMIT_S); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); } @@ -818,7 +815,6 @@ static int ar5008_hw_process_ini(struct ath_hw *ah, } REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); if (AR_SREV_9280(ah) || AR_SREV_9287_11_OR_LATER(ah)) REG_WRITE_ARRAY(&ah->iniModesRxGain, modesIndex, regWrites); @@ -849,7 +845,6 @@ static int ar5008_hw_process_ini(struct ath_hw *ah, } REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); if (AR_SREV_9271(ah)) { if (ah->eep_ops->get_eeprom(ah, EEP_TXGAIN_TYPE) == 1) diff --git a/drivers/net/wireless/ath/ath9k/ar9002_calib.c b/drivers/net/wireless/ath/ath9k/ar9002_calib.c index f0525fb62a2c..15f62cd0cc38 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c @@ -522,7 +522,6 @@ static void ar9271_hw_pa_cal(struct ath_hw *ah, bool is_reset) REG_WRITE(ah, regList[i][0], regList[i][1]); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); } static inline void ar9285_hw_pa_cal(struct ath_hw *ah, bool is_reset) diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c index fde45082a13b..78bdf0cec5f0 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c @@ -371,7 +371,6 @@ static void ar9002_hw_configpcipowersave(struct ath_hw *ah, REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); } udelay(1000); @@ -468,7 +467,6 @@ static int ar9002_hw_get_radiorev(struct ath_hw *ah) REG_WRITE(ah, AR_PHY(0x20), 0x00010000); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); val = (REG_READ(ah, AR_PHY(256)) >> 24) & 0xff; val = ((val & 0xf0) >> 4) | ((val & 0x0f) << 4); @@ -627,6 +625,4 @@ void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan) } REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); - } diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.c b/drivers/net/wireless/ath/ath9k/ar9002_phy.c index cd56c8692705..c00cdc67b55b 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c @@ -415,7 +415,6 @@ static void ar9002_hw_spur_mitigate(struct ath_hw *ah, REG_WRITE(ah, AR_PHY_MASK2_P_61_45, tmp_mask); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); } static void ar9002_olc_init(struct ath_hw *ah) diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 6c38c72915c1..6d509484b5f6 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -300,7 +300,6 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) } } REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); } diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c index 872e75b0b574..4fa4d8e28c64 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c @@ -508,7 +508,6 @@ static void ath9k_hw_set_4k_power_cal_table(struct ath_hw *ah, } REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); } } @@ -840,7 +839,6 @@ static void ath9k_hw_4k_set_txpower(struct ath_hw *ah, } REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); } static void ath9k_hw_4k_set_addac(struct ath_hw *ah, diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index b100db2766cf..bbb54bc28a44 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -380,15 +380,6 @@ static void ath9k_enable_regwrite_buffer(void *hw_priv) atomic_inc(&priv->wmi->mwrite_cnt); } -static void ath9k_disable_regwrite_buffer(void *hw_priv) -{ - struct ath_hw *ah = (struct ath_hw *) hw_priv; - struct ath_common *common = ath9k_hw_common(ah); - struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; - - atomic_dec(&priv->wmi->mwrite_cnt); -} - static void ath9k_regwrite_flush(void *hw_priv) { struct ath_hw *ah = (struct ath_hw *) hw_priv; @@ -397,6 +388,8 @@ static void ath9k_regwrite_flush(void *hw_priv) u32 rsp_status; int r; + atomic_dec(&priv->wmi->mwrite_cnt); + mutex_lock(&priv->wmi->multi_write_mutex); if (priv->wmi->multi_write_idx) { @@ -420,7 +413,6 @@ static const struct ath_ops ath9k_common_ops = { .read = ath9k_regread, .write = ath9k_regwrite, .enable_write_buffer = ath9k_enable_regwrite_buffer, - .disable_write_buffer = ath9k_disable_regwrite_buffer, .write_flush = ath9k_regwrite_flush, }; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 1b066043d6cb..98c97653c21b 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -299,7 +299,6 @@ static void ath9k_hw_disablepcie(struct ath_hw *ah) REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); } /* This should work for all families including legacy */ @@ -676,7 +675,6 @@ static void ath9k_hw_init_qos(struct ath_hw *ah) REG_WRITE(ah, AR_TXOP_12_15, 0xFFFFFFFF); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); } static void ath9k_hw_init_pll(struct ath_hw *ah, @@ -741,7 +739,6 @@ static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah, } REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); if (AR_SREV_9300_20_OR_LATER(ah)) { REG_WRITE(ah, AR_INTR_PRIO_ASYNC_ENABLE, 0); @@ -885,7 +882,6 @@ static inline void ath9k_hw_set_dma(struct ath_hw *ah) REG_WRITE(ah, AR_TXCFG, regval | AR_TXCFG_DMASZ_128B); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); /* * Restore TX Trigger Level to its pre-reset value. @@ -933,7 +929,6 @@ static inline void ath9k_hw_set_dma(struct ath_hw *ah) } REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); if (AR_SREV_9300_20_OR_LATER(ah)) ath9k_hw_reset_txstatus_ring(ah); @@ -1031,7 +1026,6 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type) REG_WRITE(ah, AR_RTC_RC, rst_flags); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); udelay(50); @@ -1070,7 +1064,6 @@ static bool ath9k_hw_set_reset_power_on(struct ath_hw *ah) udelay(2); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); if (!AR_SREV_9300_20_OR_LATER(ah)) udelay(2); @@ -1374,7 +1367,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, REG_WRITE(ah, AR_RSSI_THR, INIT_RSSI_THR); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); r = ath9k_hw_rf_set_freq(ah, chan); if (r) @@ -1386,7 +1378,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, REG_WRITE(ah, AR_DQCUMASK(i), 1 << i); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); ah->intr_txqs = 0; for (i = 0; i < ah->caps.total_queues; i++) @@ -1434,7 +1425,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, REG_WRITE(ah, AR_CFG_LED, saveLedState | AR_CFG_SCLK_32KHZ); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); /* * For big endian systems turn on swapping for descriptors @@ -1684,7 +1674,6 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period) REG_WRITE(ah, AR_NDP_PERIOD, TU_TO_USEC(beacon_period)); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); beacon_period &= ~ATH9K_BEACON_ENA; if (beacon_period & ATH9K_BEACON_RESET_TSF) { @@ -1712,7 +1701,6 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, TU_TO_USEC(bs->bs_intval & ATH9K_BEACON_PERIOD)); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR, bs->bs_bmissthreshold); @@ -1758,7 +1746,6 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, REG_WRITE(ah, AR_DTIM_PERIOD, TU_TO_USEC(dtimperiod)); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); REG_SET_BIT(ah, AR_TIMER_MODE, AR_TBTT_TIMER_EN | AR_TIM_TIMER_EN | @@ -2176,7 +2163,6 @@ void ath9k_hw_setrxfilter(struct ath_hw *ah, u32 bits) REG_READ(ah, AR_RXCFG) & ~AR_RXCFG_ZLFDMA); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); } EXPORT_SYMBOL(ath9k_hw_setrxfilter); diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 87dbb8502469..d558c51236f9 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -70,19 +70,13 @@ #define ENABLE_REGWRITE_BUFFER(_ah) \ do { \ - if (AR_SREV_9271(_ah)) \ + if (ath9k_hw_common(_ah)->ops->enable_write_buffer) \ ath9k_hw_common(_ah)->ops->enable_write_buffer((_ah)); \ } while (0) -#define DISABLE_REGWRITE_BUFFER(_ah) \ - do { \ - if (AR_SREV_9271(_ah)) \ - ath9k_hw_common(_ah)->ops->disable_write_buffer((_ah)); \ - } while (0) - #define REGWRITE_BUFFER_FLUSH(_ah) \ do { \ - if (AR_SREV_9271(_ah)) \ + if (ath9k_hw_common(_ah)->ops->write_flush) \ ath9k_hw_common(_ah)->ops->write_flush((_ah)); \ } while (0) diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index 3efda8a8a3c1..e5784595bd0f 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -40,7 +40,6 @@ static void ath9k_hw_set_txq_interrupts(struct ath_hw *ah, REG_WRITE(ah, AR_IMR_S2, ah->imrs2_reg); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); } u32 ath9k_hw_gettxbuf(struct ath_hw *ah, u32 q) @@ -530,7 +529,6 @@ bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q) } REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); if (qi->tqi_qflags & TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE) { REG_WRITE(ah, AR_DMISC(q), @@ -553,7 +551,6 @@ bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q) | AR_D_MISC_POST_FR_BKOFF_DIS); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); /* * cwmin and cwmax should be 0 for beacon queue @@ -585,7 +582,6 @@ bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q) AR_D_MISC_ARB_LOCKOUT_CNTRL_S)); REGWRITE_BUFFER_FLUSH(ah); - DISABLE_REGWRITE_BUFFER(ah); break; case ATH9K_TX_QUEUE_PSPOLL: -- cgit v1.2.3 From 71ea420992149294e74da3fa34ca8f111326bb6d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 4 Oct 2010 20:09:46 +0200 Subject: ath9k_hw: add a helper function to check for the new ANI implementation Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index a1894d240773..9856a1b0ca2d 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -109,6 +109,11 @@ static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah) ath9k_hw_private_ops(ah)->ani_lower_immunity(ah); } +static bool use_new_ani(struct ath_hw *ah) +{ + return AR_SREV_9300_20_OR_LATER(ah) || modparam_force_new_ani; +} + int ath9k_hw_get_ani_channel_idx(struct ath_hw *ah, struct ath9k_channel *chan) { @@ -1178,7 +1183,7 @@ void ath9k_hw_ani_init(struct ath_hw *ah) memset(ah->ani, 0, sizeof(ah->ani)); for (i = 0; i < ARRAY_SIZE(ah->ani); i++) { - if (AR_SREV_9300_20_OR_LATER(ah) || modparam_force_new_ani) { + if (use_new_ani(ah)) { ah->ani[i].ofdmTrigHigh = ATH9K_ANI_OFDM_TRIG_HIGH_NEW; ah->ani[i].ofdmTrigLow = ATH9K_ANI_OFDM_TRIG_LOW_NEW; @@ -1230,7 +1235,7 @@ void ath9k_hw_ani_init(struct ath_hw *ah) * since we expect some ongoing maintenance on the tables, let's sanity * check here default level should not modify INI setting. */ - if (AR_SREV_9300_20_OR_LATER(ah) || modparam_force_new_ani) { + if (use_new_ani(ah)) { const struct ani_ofdm_level_entry *entry_ofdm; const struct ani_cck_level_entry *entry_cck; -- cgit v1.2.3 From 093115b7fd641f03d89404252044c976928764cb Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 4 Oct 2010 20:09:47 +0200 Subject: ath9k_hw: clean up ANI state handling ANI state is kept per channel, so instead of keeping an array of ANI states with an arbitrary size of 255, move the ANI state into the channel struct. Move some config settings that are not per-channel out of the per-channel struct to save some memory. With those changes, ath9k_ani_restart_old and ath9k_ani_restart_new can be merged into a single function. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 338 +++++++++------------------- drivers/net/wireless/ath/ath9k/ani.h | 8 +- drivers/net/wireless/ath/ath9k/ar5008_phy.c | 10 +- drivers/net/wireless/ath/ath9k/ar9003_phy.c | 7 +- drivers/net/wireless/ath/ath9k/hw.c | 4 - drivers/net/wireless/ath/ath9k/hw.h | 3 +- 6 files changed, 110 insertions(+), 260 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 9856a1b0ca2d..6bae601c63a3 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -114,27 +114,6 @@ static bool use_new_ani(struct ath_hw *ah) return AR_SREV_9300_20_OR_LATER(ah) || modparam_force_new_ani; } -int ath9k_hw_get_ani_channel_idx(struct ath_hw *ah, - struct ath9k_channel *chan) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ah->ani); i++) { - if (ah->ani[i].c && - ah->ani[i].c->channel == chan->channel) - return i; - if (ah->ani[i].c == NULL) { - ah->ani[i].c = chan; - return i; - } - } - - ath_print(ath9k_hw_common(ah), ATH_DBG_ANI, - "No more channel states left. Using channel 0\n"); - - return 0; -} - static void ath9k_hw_update_mibstats(struct ath_hw *ah, struct ath9k_mib_stats *stats) { @@ -145,76 +124,30 @@ static void ath9k_hw_update_mibstats(struct ath_hw *ah, stats->beacons += REG_READ(ah, AR_BEACON_CNT); } -static void ath9k_ani_restart_old(struct ath_hw *ah) +static void ath9k_ani_restart(struct ath_hw *ah) { struct ar5416AniState *aniState; struct ath_common *common = ath9k_hw_common(ah); + u32 ofdm_base = 0, cck_base = 0; if (!DO_ANI(ah)) return; - aniState = ah->curani; + aniState = &ah->curchan->ani; aniState->listenTime = 0; - if (aniState->ofdmTrigHigh > AR_PHY_COUNTMAX) { - aniState->ofdmPhyErrBase = 0; - ath_print(common, ATH_DBG_ANI, - "OFDM Trigger is too high for hw counters\n"); - } else { - aniState->ofdmPhyErrBase = - AR_PHY_COUNTMAX - aniState->ofdmTrigHigh; + if (!use_new_ani(ah)) { + ofdm_base = AR_PHY_COUNTMAX - ah->config.ofdm_trig_high; + cck_base = AR_PHY_COUNTMAX - ah->config.cck_trig_high; } - if (aniState->cckTrigHigh > AR_PHY_COUNTMAX) { - aniState->cckPhyErrBase = 0; - ath_print(common, ATH_DBG_ANI, - "CCK Trigger is too high for hw counters\n"); - } else { - aniState->cckPhyErrBase = - AR_PHY_COUNTMAX - aniState->cckTrigHigh; - } - ath_print(common, ATH_DBG_ANI, - "Writing ofdmbase=%u cckbase=%u\n", - aniState->ofdmPhyErrBase, - aniState->cckPhyErrBase); - - ENABLE_REGWRITE_BUFFER(ah); - - REG_WRITE(ah, AR_PHY_ERR_1, aniState->ofdmPhyErrBase); - REG_WRITE(ah, AR_PHY_ERR_2, aniState->cckPhyErrBase); - REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); - REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); - - REGWRITE_BUFFER_FLUSH(ah); - - ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); - - aniState->ofdmPhyErrCount = 0; - aniState->cckPhyErrCount = 0; -} - -static void ath9k_ani_restart_new(struct ath_hw *ah) -{ - struct ar5416AniState *aniState; - struct ath_common *common = ath9k_hw_common(ah); - - if (!DO_ANI(ah)) - return; - - aniState = ah->curani; - aniState->listenTime = 0; - - aniState->ofdmPhyErrBase = 0; - aniState->cckPhyErrBase = 0; ath_print(common, ATH_DBG_ANI, - "Writing ofdmbase=%08x cckbase=%08x\n", - aniState->ofdmPhyErrBase, - aniState->cckPhyErrBase); + "Writing ofdmbase=%u cckbase=%u\n", ofdm_base, cck_base); ENABLE_REGWRITE_BUFFER(ah); - REG_WRITE(ah, AR_PHY_ERR_1, aniState->ofdmPhyErrBase); - REG_WRITE(ah, AR_PHY_ERR_2, aniState->cckPhyErrBase); + REG_WRITE(ah, AR_PHY_ERR_1, ofdm_base); + REG_WRITE(ah, AR_PHY_ERR_2, cck_base); REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); @@ -235,7 +168,7 @@ static void ath9k_hw_ani_ofdm_err_trigger_old(struct ath_hw *ah) if (!DO_ANI(ah)) return; - aniState = ah->curani; + aniState = &ah->curchan->ani; if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) { if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, @@ -307,7 +240,7 @@ static void ath9k_hw_ani_cck_err_trigger_old(struct ath_hw *ah) if (!DO_ANI(ah)) return; - aniState = ah->curani; + aniState = &ah->curchan->ani; if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) { if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, aniState->noiseImmunityLevel + 1)) { @@ -339,7 +272,7 @@ static void ath9k_hw_ani_cck_err_trigger_old(struct ath_hw *ah) /* Adjust the OFDM Noise Immunity Level */ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel) { - struct ar5416AniState *aniState = ah->curani; + struct ar5416AniState *aniState = &ah->curchan->ani; struct ath_common *common = ath9k_hw_common(ah); const struct ani_ofdm_level_entry *entry_ofdm; const struct ani_cck_level_entry *entry_cck; @@ -391,7 +324,7 @@ static void ath9k_hw_ani_ofdm_err_trigger_new(struct ath_hw *ah) if (!DO_ANI(ah)) return; - aniState = ah->curani; + aniState = &ah->curchan->ani; if (aniState->ofdmNoiseImmunityLevel < ATH9K_ANI_OFDM_MAX_LEVEL) ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel + 1); @@ -402,7 +335,7 @@ static void ath9k_hw_ani_ofdm_err_trigger_new(struct ath_hw *ah) */ static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel) { - struct ar5416AniState *aniState = ah->curani; + struct ar5416AniState *aniState = &ah->curchan->ani; struct ath_common *common = ath9k_hw_common(ah); const struct ani_ofdm_level_entry *entry_ofdm; const struct ani_cck_level_entry *entry_cck; @@ -448,7 +381,7 @@ static void ath9k_hw_ani_cck_err_trigger_new(struct ath_hw *ah) if (!DO_ANI(ah)) return; - aniState = ah->curani; + aniState = &ah->curchan->ani; if (aniState->cckNoiseImmunityLevel < ATH9K_ANI_CCK_MAX_LEVEL) ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel + 1); @@ -459,7 +392,7 @@ static void ath9k_hw_ani_lower_immunity_old(struct ath_hw *ah) struct ar5416AniState *aniState; int32_t rssi; - aniState = ah->curani; + aniState = &ah->curchan->ani; if (ah->opmode == NL80211_IFTYPE_AP) { if (aniState->firstepLevel > 0) { @@ -515,7 +448,7 @@ static void ath9k_hw_ani_lower_immunity_new(struct ath_hw *ah) { struct ar5416AniState *aniState; - aniState = ah->curani; + aniState = &ah->curchan->ani; /* lower OFDM noise immunity */ if (aniState->ofdmNoiseImmunityLevel > 0 && @@ -568,16 +501,13 @@ static void ath9k_ani_reset_old(struct ath_hw *ah, bool is_scanning) struct ar5416AniState *aniState; struct ath9k_channel *chan = ah->curchan; struct ath_common *common = ath9k_hw_common(ah); - int index; if (!DO_ANI(ah)) return; - index = ath9k_hw_get_ani_channel_idx(ah, chan); - aniState = &ah->ani[index]; - ah->curani = aniState; + aniState = &ah->curchan->ani; - if (DO_ANI(ah) && ah->opmode != NL80211_IFTYPE_STATION + if (ah->opmode != NL80211_IFTYPE_STATION && ah->opmode != NL80211_IFTYPE_ADHOC) { ath_print(common, ATH_DBG_ANI, "Reset ANI state opmode %u\n", ah->opmode); @@ -606,17 +536,7 @@ static void ath9k_ani_reset_old(struct ath_hw *ah, bool is_scanning) ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) | ATH9K_RX_FILTER_PHYERR); - if (ah->opmode == NL80211_IFTYPE_AP) { - ah->curani->ofdmTrigHigh = - ah->config.ofdm_trig_high; - ah->curani->ofdmTrigLow = - ah->config.ofdm_trig_low; - ah->curani->cckTrigHigh = - ah->config.cck_trig_high; - ah->curani->cckTrigLow = - ah->config.cck_trig_low; - } - ath9k_ani_restart_old(ah); + ath9k_ani_restart(ah); return; } @@ -638,7 +558,7 @@ static void ath9k_ani_reset_old(struct ath_hw *ah, bool is_scanning) ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) & ~ATH9K_RX_FILTER_PHYERR); - ath9k_ani_restart_old(ah); + ath9k_ani_restart(ah); ENABLE_REGWRITE_BUFFER(ah); @@ -655,7 +575,7 @@ static void ath9k_ani_reset_old(struct ath_hw *ah, bool is_scanning) */ static void ath9k_ani_reset_new(struct ath_hw *ah, bool is_scanning) { - struct ar5416AniState *aniState = ah->curani; + struct ar5416AniState *aniState = &ah->curchan->ani; struct ath9k_channel *chan = ah->curchan; struct ath_common *common = ath9k_hw_common(ah); @@ -731,7 +651,7 @@ static void ath9k_ani_reset_new(struct ath_hw *ah, bool is_scanning) * enable phy counters if hw supports or if not, enable phy * interrupts (so we can count each one) */ - ath9k_ani_restart_new(ah); + ath9k_ani_restart(ah); ENABLE_REGWRITE_BUFFER(ah); @@ -749,16 +669,18 @@ static void ath9k_hw_ani_monitor_old(struct ath_hw *ah, int32_t listenTime; u32 phyCnt1, phyCnt2; u32 ofdmPhyErrCnt, cckPhyErrCnt; + u32 ofdm_base = AR_PHY_COUNTMAX - ah->config.ofdm_trig_high; + u32 cck_base = AR_PHY_COUNTMAX - ah->config.cck_trig_high; if (!DO_ANI(ah)) return; - aniState = ah->curani; + aniState = &ah->curchan->ani; listenTime = ath9k_hw_ani_get_listen_time(ah); if (listenTime < 0) { ah->stats.ast_ani_lneg++; - ath9k_ani_restart_old(ah); + ath9k_ani_restart(ah); return; } @@ -769,60 +691,55 @@ static void ath9k_hw_ani_monitor_old(struct ath_hw *ah, phyCnt1 = REG_READ(ah, AR_PHY_ERR_1); phyCnt2 = REG_READ(ah, AR_PHY_ERR_2); - if (phyCnt1 < aniState->ofdmPhyErrBase || - phyCnt2 < aniState->cckPhyErrBase) { - if (phyCnt1 < aniState->ofdmPhyErrBase) { + if (phyCnt1 < ofdm_base || phyCnt2 < cck_base) { + if (phyCnt1 < ofdm_base) { ath_print(common, ATH_DBG_ANI, "phyCnt1 0x%x, resetting " "counter value to 0x%x\n", - phyCnt1, - aniState->ofdmPhyErrBase); - REG_WRITE(ah, AR_PHY_ERR_1, - aniState->ofdmPhyErrBase); + phyCnt1, ofdm_base); + REG_WRITE(ah, AR_PHY_ERR_1, ofdm_base); REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); } - if (phyCnt2 < aniState->cckPhyErrBase) { + if (phyCnt2 < cck_base) { ath_print(common, ATH_DBG_ANI, "phyCnt2 0x%x, resetting " "counter value to 0x%x\n", - phyCnt2, - aniState->cckPhyErrBase); - REG_WRITE(ah, AR_PHY_ERR_2, - aniState->cckPhyErrBase); + phyCnt2, cck_base); + REG_WRITE(ah, AR_PHY_ERR_2, cck_base); REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); } return; } - ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase; + ofdmPhyErrCnt = phyCnt1 - ofdm_base; ah->stats.ast_ani_ofdmerrs += ofdmPhyErrCnt - aniState->ofdmPhyErrCount; aniState->ofdmPhyErrCount = ofdmPhyErrCnt; - cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase; + cckPhyErrCnt = phyCnt2 - cck_base; ah->stats.ast_ani_cckerrs += cckPhyErrCnt - aniState->cckPhyErrCount; aniState->cckPhyErrCount = cckPhyErrCnt; if (aniState->listenTime > 5 * ah->aniperiod) { if (aniState->ofdmPhyErrCount <= aniState->listenTime * - aniState->ofdmTrigLow / 1000 && + ah->config.ofdm_trig_low / 1000 && aniState->cckPhyErrCount <= aniState->listenTime * - aniState->cckTrigLow / 1000) + ah->config.cck_trig_low / 1000) ath9k_hw_ani_lower_immunity(ah); - ath9k_ani_restart_old(ah); + ath9k_ani_restart(ah); } else if (aniState->listenTime > ah->aniperiod) { if (aniState->ofdmPhyErrCount > aniState->listenTime * - aniState->ofdmTrigHigh / 1000) { + ah->config.ofdm_trig_high / 1000) { ath9k_hw_ani_ofdm_err_trigger_old(ah); - ath9k_ani_restart_old(ah); + ath9k_ani_restart(ah); } else if (aniState->cckPhyErrCount > - aniState->listenTime * aniState->cckTrigHigh / + aniState->listenTime * ah->config.cck_trig_high / 1000) { ath9k_hw_ani_cck_err_trigger_old(ah); - ath9k_ani_restart_old(ah); + ath9k_ani_restart(ah); } } } @@ -833,14 +750,13 @@ static void ath9k_hw_ani_monitor_new(struct ath_hw *ah, struct ar5416AniState *aniState; struct ath_common *common = ath9k_hw_common(ah); int32_t listenTime; - u32 phyCnt1, phyCnt2; u32 ofdmPhyErrCnt, cckPhyErrCnt; u32 ofdmPhyErrRate, cckPhyErrRate; if (!DO_ANI(ah)) return; - aniState = ah->curani; + aniState = &ah->curchan->ani; if (WARN_ON(!aniState)) return; @@ -851,7 +767,7 @@ static void ath9k_hw_ani_monitor_new(struct ath_hw *ah, ath_print(common, ATH_DBG_ANI, "listenTime=%d - on new ani monitor\n", listenTime); - ath9k_ani_restart_new(ah); + ath9k_ani_restart(ah); return; } @@ -859,55 +775,20 @@ static void ath9k_hw_ani_monitor_new(struct ath_hw *ah, ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); - phyCnt1 = REG_READ(ah, AR_PHY_ERR_1); - phyCnt2 = REG_READ(ah, AR_PHY_ERR_2); - - if (phyCnt1 < aniState->ofdmPhyErrBase || - phyCnt2 < aniState->cckPhyErrBase) { - if (phyCnt1 < aniState->ofdmPhyErrBase) { - ath_print(common, ATH_DBG_ANI, - "phyCnt1 0x%x, resetting " - "counter value to 0x%x\n", - phyCnt1, - aniState->ofdmPhyErrBase); - REG_WRITE(ah, AR_PHY_ERR_1, - aniState->ofdmPhyErrBase); - REG_WRITE(ah, AR_PHY_ERR_MASK_1, - AR_PHY_ERR_OFDM_TIMING); - } - if (phyCnt2 < aniState->cckPhyErrBase) { - ath_print(common, ATH_DBG_ANI, - "phyCnt2 0x%x, resetting " - "counter value to 0x%x\n", - phyCnt2, - aniState->cckPhyErrBase); - REG_WRITE(ah, AR_PHY_ERR_2, - aniState->cckPhyErrBase); - REG_WRITE(ah, AR_PHY_ERR_MASK_2, - AR_PHY_ERR_CCK_TIMING); - } - return; - } + ofdmPhyErrCnt = REG_READ(ah, AR_PHY_ERR_1); + cckPhyErrCnt = REG_READ(ah, AR_PHY_ERR_2); - ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase; ah->stats.ast_ani_ofdmerrs += ofdmPhyErrCnt - aniState->ofdmPhyErrCount; aniState->ofdmPhyErrCount = ofdmPhyErrCnt; - cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase; ah->stats.ast_ani_cckerrs += cckPhyErrCnt - aniState->cckPhyErrCount; aniState->cckPhyErrCount = cckPhyErrCnt; ath_print(common, ATH_DBG_ANI, - "Errors: OFDM=0x%08x-0x%08x=%d " - "CCK=0x%08x-0x%08x=%d\n", - phyCnt1, - aniState->ofdmPhyErrBase, - ofdmPhyErrCnt, - phyCnt2, - aniState->cckPhyErrBase, - cckPhyErrCnt); + "Errors: OFDM=%d, CCK=%d\n", + ofdmPhyErrCnt, cckPhyErrCnt); ofdmPhyErrRate = aniState->ofdmPhyErrCount * 1000 / aniState->listenTime; @@ -922,8 +803,8 @@ static void ath9k_hw_ani_monitor_new(struct ath_hw *ah, cckPhyErrRate, aniState->ofdmsTurn); if (aniState->listenTime > 5 * ah->aniperiod) { - if (ofdmPhyErrRate <= aniState->ofdmTrigLow && - cckPhyErrRate <= aniState->cckTrigLow) { + if (ofdmPhyErrRate <= ah->config.ofdm_trig_low && + cckPhyErrRate <= ah->config.cck_trig_low) { ath_print(common, ATH_DBG_ANI, "1. listenTime=%d OFDM:%d errs=%d/s(<%d) " "CCK:%d errs=%d/s(<%d) -> " @@ -931,22 +812,22 @@ static void ath9k_hw_ani_monitor_new(struct ath_hw *ah, aniState->listenTime, aniState->ofdmNoiseImmunityLevel, ofdmPhyErrRate, - aniState->ofdmTrigLow, + ah->config.ofdm_trig_low, aniState->cckNoiseImmunityLevel, cckPhyErrRate, - aniState->cckTrigLow); + ah->config.cck_trig_low); ath9k_hw_ani_lower_immunity(ah); aniState->ofdmsTurn = !aniState->ofdmsTurn; } ath_print(common, ATH_DBG_ANI, "1 listenTime=%d ofdm=%d/s cck=%d/s - " - "calling ath9k_ani_restart_new()\n", + "calling ath9k_ani_restart()\n", aniState->listenTime, ofdmPhyErrRate, cckPhyErrRate); - ath9k_ani_restart_new(ah); + ath9k_ani_restart(ah); } else if (aniState->listenTime > ah->aniperiod) { /* check to see if need to raise immunity */ - if (ofdmPhyErrRate > aniState->ofdmTrigHigh && - (cckPhyErrRate <= aniState->cckTrigHigh || + if (ofdmPhyErrRate > ah->config.ofdm_trig_high && + (cckPhyErrRate <= ah->config.cck_trig_high || aniState->ofdmsTurn)) { ath_print(common, ATH_DBG_ANI, "2 listenTime=%d OFDM:%d errs=%d/s(>%d) -> " @@ -954,20 +835,20 @@ static void ath9k_hw_ani_monitor_new(struct ath_hw *ah, aniState->listenTime, aniState->ofdmNoiseImmunityLevel, ofdmPhyErrRate, - aniState->ofdmTrigHigh); + ah->config.ofdm_trig_high); ath9k_hw_ani_ofdm_err_trigger_new(ah); - ath9k_ani_restart_new(ah); + ath9k_ani_restart(ah); aniState->ofdmsTurn = false; - } else if (cckPhyErrRate > aniState->cckTrigHigh) { + } else if (cckPhyErrRate > ah->config.cck_trig_high) { ath_print(common, ATH_DBG_ANI, "3 listenTime=%d CCK:%d errs=%d/s(>%d) -> " "ath9k_hw_ani_cck_err_trigger_new()\n", aniState->listenTime, aniState->cckNoiseImmunityLevel, cckPhyErrRate, - aniState->cckTrigHigh); + ah->config.cck_trig_high); ath9k_hw_ani_cck_err_trigger_new(ah); - ath9k_ani_restart_new(ah); + ath9k_ani_restart(ah); aniState->ofdmsTurn = true; } } @@ -1064,6 +945,8 @@ skip: */ static void ath9k_hw_proc_mib_event_old(struct ath_hw *ah) { + u32 ofdm_base = AR_PHY_COUNTMAX - ah->config.ofdm_trig_high; + u32 cck_base = AR_PHY_COUNTMAX - ah->config.cck_trig_high; u32 phyCnt1, phyCnt2; /* Reset these counters regardless */ @@ -1090,16 +973,16 @@ static void ath9k_hw_proc_mib_event_old(struct ath_hw *ah) phyCnt2 = REG_READ(ah, AR_PHY_ERR_2); if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) || ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) { - struct ar5416AniState *aniState = ah->curani; + struct ar5416AniState *aniState = &ah->curchan->ani; u32 ofdmPhyErrCnt, cckPhyErrCnt; /* NB: only use ast_ani_*errs with AH_PRIVATE_DIAG */ - ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase; + ofdmPhyErrCnt = phyCnt1 - ofdm_base; ah->stats.ast_ani_ofdmerrs += ofdmPhyErrCnt - aniState->ofdmPhyErrCount; aniState->ofdmPhyErrCount = ofdmPhyErrCnt; - cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase; + cckPhyErrCnt = phyCnt2 - cck_base; ah->stats.ast_ani_cckerrs += cckPhyErrCnt - aniState->cckPhyErrCount; aniState->cckPhyErrCount = cckPhyErrCnt; @@ -1110,12 +993,12 @@ static void ath9k_hw_proc_mib_event_old(struct ath_hw *ah) * clobbers the error counter so the trigger threshold * check will never be true. */ - if (aniState->ofdmPhyErrCount > aniState->ofdmTrigHigh) + if (aniState->ofdmPhyErrCount > ah->config.ofdm_trig_high) ath9k_hw_ani_ofdm_err_trigger_new(ah); - if (aniState->cckPhyErrCount > aniState->cckTrigHigh) + if (aniState->cckPhyErrCount > ah->config.cck_trig_high) ath9k_hw_ani_cck_err_trigger_old(ah); /* NB: always restart to insure the h/w counters are reset */ - ath9k_ani_restart_old(ah); + ath9k_ani_restart(ah); } } @@ -1154,7 +1037,7 @@ static void ath9k_hw_proc_mib_event_new(struct ath_hw *ah) /* NB: always restart to insure the h/w counters are reset */ if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) || ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) - ath9k_ani_restart_new(ah); + ath9k_ani_restart(ah); } void ath9k_hw_ani_setup(struct ath_hw *ah) @@ -1181,54 +1064,51 @@ void ath9k_hw_ani_init(struct ath_hw *ah) ath_print(common, ATH_DBG_ANI, "Initialize ANI\n"); - memset(ah->ani, 0, sizeof(ah->ani)); - for (i = 0; i < ARRAY_SIZE(ah->ani); i++) { - if (use_new_ani(ah)) { - ah->ani[i].ofdmTrigHigh = ATH9K_ANI_OFDM_TRIG_HIGH_NEW; - ah->ani[i].ofdmTrigLow = ATH9K_ANI_OFDM_TRIG_LOW_NEW; + if (use_new_ani(ah)) { + ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH_NEW; + ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW_NEW; - ah->ani[i].cckTrigHigh = ATH9K_ANI_CCK_TRIG_HIGH_NEW; - ah->ani[i].cckTrigLow = ATH9K_ANI_CCK_TRIG_LOW_NEW; + ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH_NEW; + ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW_NEW; + } else { + ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH_OLD; + ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW_OLD; - ah->ani[i].spurImmunityLevel = - ATH9K_ANI_SPUR_IMMUNE_LVL_NEW; + ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH_OLD; + ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW_OLD; + } - ah->ani[i].firstepLevel = ATH9K_ANI_FIRSTEP_LVL_NEW; + for (i = 0; i < ARRAY_SIZE(ah->channels); i++) { + struct ath9k_channel *chan = &ah->channels[i]; + struct ar5416AniState *ani = &chan->ani; + + if (use_new_ani(ah)) { + ani->spurImmunityLevel = + ATH9K_ANI_SPUR_IMMUNE_LVL_NEW; - ah->ani[i].ofdmPhyErrBase = 0; - ah->ani[i].cckPhyErrBase = 0; + ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_NEW; if (AR_SREV_9300_20_OR_LATER(ah)) - ah->ani[i].mrcCCKOff = + ani->mrcCCKOff = !ATH9K_ANI_ENABLE_MRC_CCK; else - ah->ani[i].mrcCCKOff = true; + ani->mrcCCKOff = true; - ah->ani[i].ofdmsTurn = true; + ani->ofdmsTurn = true; } else { - ah->ani[i].ofdmTrigHigh = ATH9K_ANI_OFDM_TRIG_HIGH_OLD; - ah->ani[i].ofdmTrigLow = ATH9K_ANI_OFDM_TRIG_LOW_OLD; - - ah->ani[i].cckTrigHigh = ATH9K_ANI_CCK_TRIG_HIGH_OLD; - ah->ani[i].cckTrigLow = ATH9K_ANI_CCK_TRIG_LOW_OLD; - - ah->ani[i].spurImmunityLevel = + ani->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL_OLD; - ah->ani[i].firstepLevel = ATH9K_ANI_FIRSTEP_LVL_OLD; + ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_OLD; - ah->ani[i].ofdmPhyErrBase = - AR_PHY_COUNTMAX - ATH9K_ANI_OFDM_TRIG_HIGH_OLD; - ah->ani[i].cckPhyErrBase = - AR_PHY_COUNTMAX - ATH9K_ANI_CCK_TRIG_HIGH_OLD; - ah->ani[i].cckWeakSigThreshold = + ani->cckWeakSigThreshold = ATH9K_ANI_CCK_WEAK_SIG_THR; } - ah->ani[i].rssiThrHigh = ATH9K_ANI_RSSI_THR_HIGH; - ah->ani[i].rssiThrLow = ATH9K_ANI_RSSI_THR_LOW; - ah->ani[i].ofdmWeakSigDetectOff = + ani->rssiThrHigh = ATH9K_ANI_RSSI_THR_HIGH; + ani->rssiThrLow = ATH9K_ANI_RSSI_THR_LOW; + ani->ofdmWeakSigDetectOff = !ATH9K_ANI_USE_OFDM_WEAK_SIG; - ah->ani[i].cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL; + ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL; } /* @@ -1249,23 +1129,11 @@ void ath9k_hw_ani_init(struct ath_hw *ah) ah->config.ani_poll_interval = ATH9K_ANI_POLLINTERVAL_OLD; } - ath_print(common, ATH_DBG_ANI, - "Setting OfdmErrBase = 0x%08x\n", - ah->ani[0].ofdmPhyErrBase); - ath_print(common, ATH_DBG_ANI, "Setting cckErrBase = 0x%08x\n", - ah->ani[0].cckPhyErrBase); - - ENABLE_REGWRITE_BUFFER(ah); - - REG_WRITE(ah, AR_PHY_ERR_1, ah->ani[0].ofdmPhyErrBase); - REG_WRITE(ah, AR_PHY_ERR_2, ah->ani[0].cckPhyErrBase); - - REGWRITE_BUFFER_FLUSH(ah); - - ath9k_enable_mib_counters(ah); - if (ah->config.enable_ani) ah->proc_phyerr |= HAL_PROCESS_ANI; + + ath9k_ani_restart(ah); + ath9k_enable_mib_counters(ah); } void ath9k_hw_attach_ani_ops_old(struct ath_hw *ah) diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h index 15f9d67a18c8..98cfd8154c71 100644 --- a/drivers/net/wireless/ath/ath9k/ani.h +++ b/drivers/net/wireless/ath/ath9k/ani.h @@ -19,7 +19,7 @@ #define HAL_PROCESS_ANI 0x00000001 -#define DO_ANI(ah) (((ah)->proc_phyerr & HAL_PROCESS_ANI)) +#define DO_ANI(ah) (((ah)->proc_phyerr & HAL_PROCESS_ANI) && ah->curchan) #define BEACON_RSSI(ahp) (ahp->stats.avgbrssi) @@ -130,17 +130,11 @@ struct ar5416AniState { u8 ofdmWeakSigDetectOff; u8 cckWeakSigThreshold; u32 listenTime; - u32 ofdmTrigHigh; - u32 ofdmTrigLow; - int32_t cckTrigHigh; - int32_t cckTrigLow; int32_t rssiThrLow; int32_t rssiThrHigh; u32 noiseFloor; u32 ofdmPhyErrCount; u32 cckPhyErrCount; - u32 ofdmPhyErrBase; - u32 cckPhyErrBase; int16_t pktRssi[2]; int16_t ofdmErrRssi[2]; int16_t cckErrRssi[2]; diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index 7b26292daf35..ea9f4497f58c 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -1048,7 +1048,7 @@ static bool ar5008_hw_ani_control_old(struct ath_hw *ah, enum ath9k_ani_cmd cmd, int param) { - struct ar5416AniState *aniState = ah->curani; + struct ar5416AniState *aniState = &ah->curchan->ani; struct ath_common *common = ath9k_hw_common(ah); switch (cmd & ah->ani_function) { @@ -1231,9 +1231,9 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, enum ath9k_ani_cmd cmd, int param) { - struct ar5416AniState *aniState = ah->curani; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_channel *chan = ah->curchan; + struct ar5416AniState *aniState = &chan->ani; s32 value, value2; switch (cmd & ah->ani_function) { @@ -1518,16 +1518,12 @@ static void ar5008_hw_do_getnf(struct ath_hw *ah, */ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah) { - struct ar5416AniState *aniState; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_channel *chan = ah->curchan; + struct ar5416AniState *aniState = &chan->ani; struct ath9k_ani_default *iniDef; - int index; u32 val; - index = ath9k_hw_get_ani_channel_idx(ah, chan); - aniState = &ah->ani[index]; - ah->curani = aniState; iniDef = &aniState->iniDef; ath_print(common, ATH_DBG_ANI, diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index e15574caf61a..efb05599b84c 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -747,9 +747,9 @@ static void ar9003_hw_set_diversity(struct ath_hw *ah, bool value) static bool ar9003_hw_ani_control(struct ath_hw *ah, enum ath9k_ani_cmd cmd, int param) { - struct ar5416AniState *aniState = ah->curani; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_channel *chan = ah->curchan; + struct ar5416AniState *aniState = &chan->ani; s32 value, value2; switch (cmd & ah->ani_function) { @@ -1065,12 +1065,9 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah) struct ath_common *common = ath9k_hw_common(ah); struct ath9k_channel *chan = ah->curchan; struct ath9k_ani_default *iniDef; - int index; u32 val; - index = ath9k_hw_get_ani_channel_idx(ah, chan); - aniState = &ah->ani[index]; - ah->curani = aniState; + aniState = &ah->curchan->ani; iniDef = &aniState->iniDef; ath_print(common, ATH_DBG_ANI, diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 98c97653c21b..05e9935ef160 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -370,10 +370,6 @@ static void ath9k_hw_init_config(struct ath_hw *ah) ah->config.pcie_clock_req = 0; ah->config.pcie_waen = 0; ah->config.analog_shiftreg = 1; - ah->config.ofdm_trig_low = 200; - ah->config.ofdm_trig_high = 500; - ah->config.cck_trig_high = 200; - ah->config.cck_trig_low = 100; ah->config.enable_ani = true; for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index d558c51236f9..506346384c4f 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -346,6 +346,7 @@ struct ath9k_hw_cal_data { struct ath9k_channel { struct ieee80211_channel *chan; + struct ar5416AniState ani; u16 channel; u32 channelFlags; u32 chanmode; @@ -752,8 +753,6 @@ struct ath_hw { /* ANI */ u32 proc_phyerr; u32 aniperiod; - struct ar5416AniState *curani; - struct ar5416AniState ani[255]; int totalSizeDesired[5]; int coarse_high[5]; int coarse_low[5]; -- cgit v1.2.3 From bfc472bb736bf309158ea76897d255a283d0d31c Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 4 Oct 2010 20:09:48 +0200 Subject: ath9k_hw: remove code duplication in phy error counter handling Split out the PHY error counter update from ath9k_hw_ani_monitor_*, reuse it in ath9k_hw_proc_mib_event (merged from ath9k_hw_proc_mib_event_old and ath9k_hw_proc_mib_event_new). Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 142 +++++++------------------------- drivers/net/wireless/ath/ath9k/hw-ops.h | 5 -- drivers/net/wireless/ath/ath9k/hw.h | 4 +- drivers/net/wireless/ath/ath9k/main.c | 2 +- 4 files changed, 34 insertions(+), 119 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 6bae601c63a3..d5c9df5c4569 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -661,21 +661,15 @@ static void ath9k_ani_reset_new(struct ath_hw *ah, bool is_scanning) REGWRITE_BUFFER_FLUSH(ah); } -static void ath9k_hw_ani_monitor_old(struct ath_hw *ah, - struct ath9k_channel *chan) +static void ath9k_hw_ani_read_counters(struct ath_hw *ah) { - struct ar5416AniState *aniState; struct ath_common *common = ath9k_hw_common(ah); - int32_t listenTime; - u32 phyCnt1, phyCnt2; + struct ar5416AniState *aniState = &ah->curchan->ani; + u32 ofdm_base = 0; + u32 cck_base = 0; u32 ofdmPhyErrCnt, cckPhyErrCnt; - u32 ofdm_base = AR_PHY_COUNTMAX - ah->config.ofdm_trig_high; - u32 cck_base = AR_PHY_COUNTMAX - ah->config.cck_trig_high; - - if (!DO_ANI(ah)) - return; - - aniState = &ah->curchan->ani; + u32 phyCnt1, phyCnt2; + int32_t listenTime; listenTime = ath9k_hw_ani_get_listen_time(ah); if (listenTime < 0) { @@ -684,6 +678,11 @@ static void ath9k_hw_ani_monitor_old(struct ath_hw *ah, return; } + if (!use_new_ani(ah)) { + ofdm_base = AR_PHY_COUNTMAX - ah->config.ofdm_trig_high; + cck_base = AR_PHY_COUNTMAX - ah->config.cck_trig_high; + } + aniState->listenTime += listenTime; ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); @@ -691,7 +690,7 @@ static void ath9k_hw_ani_monitor_old(struct ath_hw *ah, phyCnt1 = REG_READ(ah, AR_PHY_ERR_1); phyCnt2 = REG_READ(ah, AR_PHY_ERR_2); - if (phyCnt1 < ofdm_base || phyCnt2 < cck_base) { + if (use_new_ani(ah) && (phyCnt1 < ofdm_base || phyCnt2 < cck_base)) { if (phyCnt1 < ofdm_base) { ath_print(common, ATH_DBG_ANI, "phyCnt1 0x%x, resetting " @@ -723,6 +722,19 @@ static void ath9k_hw_ani_monitor_old(struct ath_hw *ah, cckPhyErrCnt - aniState->cckPhyErrCount; aniState->cckPhyErrCount = cckPhyErrCnt; +} + +static void ath9k_hw_ani_monitor_old(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + struct ar5416AniState *aniState; + + if (!DO_ANI(ah)) + return; + + aniState = &ah->curchan->ani; + ath9k_hw_ani_read_counters(ah); + if (aniState->listenTime > 5 * ah->aniperiod) { if (aniState->ofdmPhyErrCount <= aniState->listenTime * ah->config.ofdm_trig_low / 1000 && @@ -749,8 +761,6 @@ static void ath9k_hw_ani_monitor_new(struct ath_hw *ah, { struct ar5416AniState *aniState; struct ath_common *common = ath9k_hw_common(ah); - int32_t listenTime; - u32 ofdmPhyErrCnt, cckPhyErrCnt; u32 ofdmPhyErrRate, cckPhyErrRate; if (!DO_ANI(ah)) @@ -760,35 +770,7 @@ static void ath9k_hw_ani_monitor_new(struct ath_hw *ah, if (WARN_ON(!aniState)) return; - listenTime = ath9k_hw_ani_get_listen_time(ah); - if (listenTime <= 0) { - ah->stats.ast_ani_lneg++; - /* restart ANI period if listenTime is invalid */ - ath_print(common, ATH_DBG_ANI, - "listenTime=%d - on new ani monitor\n", - listenTime); - ath9k_ani_restart(ah); - return; - } - - aniState->listenTime += listenTime; - - ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); - - ofdmPhyErrCnt = REG_READ(ah, AR_PHY_ERR_1); - cckPhyErrCnt = REG_READ(ah, AR_PHY_ERR_2); - - ah->stats.ast_ani_ofdmerrs += - ofdmPhyErrCnt - aniState->ofdmPhyErrCount; - aniState->ofdmPhyErrCount = ofdmPhyErrCnt; - - ah->stats.ast_ani_cckerrs += - cckPhyErrCnt - aniState->cckPhyErrCount; - aniState->cckPhyErrCount = cckPhyErrCnt; - - ath_print(common, ATH_DBG_ANI, - "Errors: OFDM=%d, CCK=%d\n", - ofdmPhyErrCnt, cckPhyErrCnt); + ath9k_hw_ani_read_counters(ah); ofdmPhyErrRate = aniState->ofdmPhyErrCount * 1000 / aniState->listenTime; @@ -798,7 +780,8 @@ static void ath9k_hw_ani_monitor_new(struct ath_hw *ah, ath_print(common, ATH_DBG_ANI, "listenTime=%d OFDM:%d errs=%d/s CCK:%d " "errs=%d/s ofdm_turn=%d\n", - listenTime, aniState->ofdmNoiseImmunityLevel, + aniState->listenTime, + aniState->ofdmNoiseImmunityLevel, ofdmPhyErrRate, aniState->cckNoiseImmunityLevel, cckPhyErrRate, aniState->ofdmsTurn); @@ -943,10 +926,8 @@ skip: * any of the MIB counters overflow/trigger so don't assume we're * here because a PHY error counter triggered. */ -static void ath9k_hw_proc_mib_event_old(struct ath_hw *ah) +void ath9k_hw_proc_mib_event(struct ath_hw *ah) { - u32 ofdm_base = AR_PHY_COUNTMAX - ah->config.ofdm_trig_high; - u32 cck_base = AR_PHY_COUNTMAX - ah->config.cck_trig_high; u32 phyCnt1, phyCnt2; /* Reset these counters regardless */ @@ -973,72 +954,15 @@ static void ath9k_hw_proc_mib_event_old(struct ath_hw *ah) phyCnt2 = REG_READ(ah, AR_PHY_ERR_2); if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) || ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) { - struct ar5416AniState *aniState = &ah->curchan->ani; - u32 ofdmPhyErrCnt, cckPhyErrCnt; - - /* NB: only use ast_ani_*errs with AH_PRIVATE_DIAG */ - ofdmPhyErrCnt = phyCnt1 - ofdm_base; - ah->stats.ast_ani_ofdmerrs += - ofdmPhyErrCnt - aniState->ofdmPhyErrCount; - aniState->ofdmPhyErrCount = ofdmPhyErrCnt; - cckPhyErrCnt = phyCnt2 - cck_base; - ah->stats.ast_ani_cckerrs += - cckPhyErrCnt - aniState->cckPhyErrCount; - aniState->cckPhyErrCount = cckPhyErrCnt; + if (!use_new_ani(ah)) + ath9k_hw_ani_read_counters(ah); - /* - * NB: figure out which counter triggered. If both - * trigger we'll only deal with one as the processing - * clobbers the error counter so the trigger threshold - * check will never be true. - */ - if (aniState->ofdmPhyErrCount > ah->config.ofdm_trig_high) - ath9k_hw_ani_ofdm_err_trigger_new(ah); - if (aniState->cckPhyErrCount > ah->config.cck_trig_high) - ath9k_hw_ani_cck_err_trigger_old(ah); /* NB: always restart to insure the h/w counters are reset */ ath9k_ani_restart(ah); } } - -/* - * Process a MIB interrupt. We may potentially be invoked because - * any of the MIB counters overflow/trigger so don't assume we're - * here because a PHY error counter triggered. - */ -static void ath9k_hw_proc_mib_event_new(struct ath_hw *ah) -{ - u32 phyCnt1, phyCnt2; - - /* Reset these counters regardless */ - REG_WRITE(ah, AR_FILT_OFDM, 0); - REG_WRITE(ah, AR_FILT_CCK, 0); - if (!(REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING)) - REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR); - - /* Clear the mib counters and save them in the stats */ - ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); - - if (!DO_ANI(ah)) { - /* - * We must always clear the interrupt cause by - * resetting the phy error regs. - */ - REG_WRITE(ah, AR_PHY_ERR_1, 0); - REG_WRITE(ah, AR_PHY_ERR_2, 0); - return; - } - - /* NB: these are not reset-on-read */ - phyCnt1 = REG_READ(ah, AR_PHY_ERR_1); - phyCnt2 = REG_READ(ah, AR_PHY_ERR_2); - - /* NB: always restart to insure the h/w counters are reset */ - if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) || - ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) - ath9k_ani_restart(ah); -} +EXPORT_SYMBOL(ath9k_hw_proc_mib_event); void ath9k_hw_ani_setup(struct ath_hw *ah) { @@ -1144,7 +1068,6 @@ void ath9k_hw_attach_ani_ops_old(struct ath_hw *ah) priv_ops->ani_reset = ath9k_ani_reset_old; priv_ops->ani_lower_immunity = ath9k_hw_ani_lower_immunity_old; - ops->ani_proc_mib_event = ath9k_hw_proc_mib_event_old; ops->ani_monitor = ath9k_hw_ani_monitor_old; ath_print(ath9k_hw_common(ah), ATH_DBG_ANY, "Using ANI v1\n"); @@ -1158,7 +1081,6 @@ void ath9k_hw_attach_ani_ops_new(struct ath_hw *ah) priv_ops->ani_reset = ath9k_ani_reset_new; priv_ops->ani_lower_immunity = ath9k_hw_ani_lower_immunity_new; - ops->ani_proc_mib_event = ath9k_hw_proc_mib_event_new; ops->ani_monitor = ath9k_hw_ani_monitor_new; ath_print(ath9k_hw_common(ah), ATH_DBG_ANY, "Using ANI v2\n"); diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h index 9c4dd0ec9a15..f42c1980e654 100644 --- a/drivers/net/wireless/ath/ath9k/hw-ops.h +++ b/drivers/net/wireless/ath/ath9k/hw-ops.h @@ -128,11 +128,6 @@ static inline void ath9k_hw_set11n_virtualmorefrag(struct ath_hw *ah, void *ds, ath9k_hw_ops(ah)->set11n_virtualmorefrag(ah, ds, vmf); } -static inline void ath9k_hw_procmibevent(struct ath_hw *ah) -{ - ath9k_hw_ops(ah)->ani_proc_mib_event(ah); -} - static inline void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan) { diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 506346384c4f..a87840bab2ad 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -575,8 +575,6 @@ struct ath_hw_private_ops { * @config_pci_powersave: * @calibrate: periodic calibration for NF, ANI, IQ, ADC gain, ADC-DC * - * @ani_proc_mib_event: process MIB events, this would happen upon specific ANI - * thresholds being reached or having overflowed. * @ani_monitor: called periodically by the core driver to collect * MIB stats and adjust ANI if specific thresholds have been reached. */ @@ -620,7 +618,6 @@ struct ath_hw_ops { void (*set11n_virtualmorefrag)(struct ath_hw *ah, void *ds, u32 vmf); - void (*ani_proc_mib_event)(struct ath_hw *ah); void (*ani_monitor)(struct ath_hw *ah, struct ath9k_channel *chan); }; @@ -980,6 +977,7 @@ void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan); * older families (AR5008, AR9001, AR9002) by using modparam_force_new_ani. */ extern int modparam_force_new_ani; +void ath9k_hw_proc_mib_event(struct ath_hw *ah); void ath9k_hw_attach_ani_ops_old(struct ath_hw *ah); void ath9k_hw_attach_ani_ops_new(struct ath_hw *ah); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index e6ddf45506be..d9c2e2d93136 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -713,7 +713,7 @@ irqreturn_t ath_isr(int irq, void *dev) * it will clear whatever condition caused * the interrupt. */ - ath9k_hw_procmibevent(ah); + ath9k_hw_proc_mib_event(ah); ath9k_hw_set_interrupts(ah, ah->imask); } -- cgit v1.2.3 From 8eb4980c33c35e97a0a226fdbc07e38da0f1f4aa Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 4 Oct 2010 20:09:49 +0200 Subject: ath9k_hw: remove function pointer abstraction for internal ANI ops The code gets more concise and readable when making the new ANI functions fall back to the old ones if ANI v2 is disabled. This also makes further code cleanup easier. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 58 ++++++++++++++++----------------- drivers/net/wireless/ath/ath9k/hw-ops.h | 5 --- drivers/net/wireless/ath/ath9k/hw.h | 11 +------ 3 files changed, 29 insertions(+), 45 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index d5c9df5c4569..b9595647810a 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -103,12 +103,6 @@ static const struct ani_cck_level_entry cck_level_table[] = { #define ATH9K_ANI_CCK_DEF_LEVEL \ 2 /* default level - matches the INI settings */ -/* Private to ani.c */ -static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah) -{ - ath9k_hw_private_ops(ah)->ani_lower_immunity(ah); -} - static bool use_new_ani(struct ath_hw *ah) { return AR_SREV_9300_20_OR_LATER(ah) || modparam_force_new_ani; @@ -165,9 +159,6 @@ static void ath9k_hw_ani_ofdm_err_trigger_old(struct ath_hw *ah) struct ar5416AniState *aniState; int32_t rssi; - if (!DO_ANI(ah)) - return; - aniState = &ah->curchan->ani; if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) { @@ -237,9 +228,6 @@ static void ath9k_hw_ani_cck_err_trigger_old(struct ath_hw *ah) struct ar5416AniState *aniState; int32_t rssi; - if (!DO_ANI(ah)) - return; - aniState = &ah->curchan->ani; if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) { if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, @@ -317,13 +305,18 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel) } } -static void ath9k_hw_ani_ofdm_err_trigger_new(struct ath_hw *ah) +static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah) { struct ar5416AniState *aniState; if (!DO_ANI(ah)) return; + if (!use_new_ani(ah)) { + ath9k_hw_ani_ofdm_err_trigger_old(ah); + return; + } + aniState = &ah->curchan->ani; if (aniState->ofdmNoiseImmunityLevel < ATH9K_ANI_OFDM_MAX_LEVEL) @@ -374,13 +367,18 @@ static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel) entry_cck->mrc_cck_on); } -static void ath9k_hw_ani_cck_err_trigger_new(struct ath_hw *ah) +static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah) { struct ar5416AniState *aniState; if (!DO_ANI(ah)) return; + if (!use_new_ani(ah)) { + ath9k_hw_ani_cck_err_trigger_old(ah); + return; + } + aniState = &ah->curchan->ani; if (aniState->cckNoiseImmunityLevel < ATH9K_ANI_CCK_MAX_LEVEL) @@ -444,12 +442,17 @@ static void ath9k_hw_ani_lower_immunity_old(struct ath_hw *ah) * only lower either OFDM or CCK errors per turn * we lower the other one next time */ -static void ath9k_hw_ani_lower_immunity_new(struct ath_hw *ah) +static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah) { struct ar5416AniState *aniState; aniState = &ah->curchan->ani; + if (!use_new_ani(ah)) { + ath9k_hw_ani_lower_immunity_old(ah); + return; + } + /* lower OFDM noise immunity */ if (aniState->ofdmNoiseImmunityLevel > 0 && (aniState->ofdmsTurn || aniState->cckNoiseImmunityLevel == 0)) { @@ -573,7 +576,7 @@ static void ath9k_ani_reset_old(struct ath_hw *ah, bool is_scanning) * This routine should be called for every hardware reset and for * every channel change. */ -static void ath9k_ani_reset_new(struct ath_hw *ah, bool is_scanning) +void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) { struct ar5416AniState *aniState = &ah->curchan->ani; struct ath9k_channel *chan = ah->curchan; @@ -582,6 +585,9 @@ static void ath9k_ani_reset_new(struct ath_hw *ah, bool is_scanning) if (!DO_ANI(ah)) return; + if (!use_new_ani(ah)) + return ath9k_ani_reset_old(ah, is_scanning); + BUG_ON(aniState == NULL); ah->stats.ast_ani_reset++; @@ -745,12 +751,12 @@ static void ath9k_hw_ani_monitor_old(struct ath_hw *ah, } else if (aniState->listenTime > ah->aniperiod) { if (aniState->ofdmPhyErrCount > aniState->listenTime * ah->config.ofdm_trig_high / 1000) { - ath9k_hw_ani_ofdm_err_trigger_old(ah); + ath9k_hw_ani_ofdm_err_trigger(ah); ath9k_ani_restart(ah); } else if (aniState->cckPhyErrCount > aniState->listenTime * ah->config.cck_trig_high / 1000) { - ath9k_hw_ani_cck_err_trigger_old(ah); + ath9k_hw_ani_cck_err_trigger(ah); ath9k_ani_restart(ah); } } @@ -814,23 +820,23 @@ static void ath9k_hw_ani_monitor_new(struct ath_hw *ah, aniState->ofdmsTurn)) { ath_print(common, ATH_DBG_ANI, "2 listenTime=%d OFDM:%d errs=%d/s(>%d) -> " - "ath9k_hw_ani_ofdm_err_trigger_new()\n", + "ath9k_hw_ani_ofdm_err_trigger()\n", aniState->listenTime, aniState->ofdmNoiseImmunityLevel, ofdmPhyErrRate, ah->config.ofdm_trig_high); - ath9k_hw_ani_ofdm_err_trigger_new(ah); + ath9k_hw_ani_ofdm_err_trigger(ah); ath9k_ani_restart(ah); aniState->ofdmsTurn = false; } else if (cckPhyErrRate > ah->config.cck_trig_high) { ath_print(common, ATH_DBG_ANI, "3 listenTime=%d CCK:%d errs=%d/s(>%d) -> " - "ath9k_hw_ani_cck_err_trigger_new()\n", + "ath9k_hw_ani_cck_err_trigger()\n", aniState->listenTime, aniState->cckNoiseImmunityLevel, cckPhyErrRate, ah->config.cck_trig_high); - ath9k_hw_ani_cck_err_trigger_new(ah); + ath9k_hw_ani_cck_err_trigger(ah); ath9k_ani_restart(ah); aniState->ofdmsTurn = true; } @@ -1062,12 +1068,8 @@ void ath9k_hw_ani_init(struct ath_hw *ah) void ath9k_hw_attach_ani_ops_old(struct ath_hw *ah) { - struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); struct ath_hw_ops *ops = ath9k_hw_ops(ah); - priv_ops->ani_reset = ath9k_ani_reset_old; - priv_ops->ani_lower_immunity = ath9k_hw_ani_lower_immunity_old; - ops->ani_monitor = ath9k_hw_ani_monitor_old; ath_print(ath9k_hw_common(ah), ATH_DBG_ANY, "Using ANI v1\n"); @@ -1075,12 +1077,8 @@ void ath9k_hw_attach_ani_ops_old(struct ath_hw *ah) void ath9k_hw_attach_ani_ops_new(struct ath_hw *ah) { - struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); struct ath_hw_ops *ops = ath9k_hw_ops(ah); - priv_ops->ani_reset = ath9k_ani_reset_new; - priv_ops->ani_lower_immunity = ath9k_hw_ani_lower_immunity_new; - ops->ani_monitor = ath9k_hw_ani_monitor_new; ath_print(ath9k_hw_common(ah), ATH_DBG_ANY, "Using ANI v2\n"); diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h index f42c1980e654..6564d1f0ffb1 100644 --- a/drivers/net/wireless/ath/ath9k/hw-ops.h +++ b/drivers/net/wireless/ath/ath9k/hw-ops.h @@ -271,9 +271,4 @@ static inline void ath9k_hw_setup_calibration(struct ath_hw *ah, ath9k_hw_private_ops(ah)->setup_calibration(ah, currCal); } -static inline void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) -{ - ath9k_hw_private_ops(ah)->ani_reset(ah, is_scanning); -} - #endif /* ATH9K_HW_OPS_H */ diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index a87840bab2ad..c982a24146d2 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -509,14 +509,6 @@ struct ath_hw_antcomb_conf { * @setup_calibration: set up calibration * @iscal_supported: used to query if a type of calibration is supported * - * @ani_reset: reset ANI parameters to default values - * @ani_lower_immunity: lower the noise immunity level. The level controls - * the power-based packet detection on hardware. If a power jump is - * detected the adapter takes it as an indication that a packet has - * arrived. The level ranges from 0-5. Each level corresponds to a - * few dB more of noise immunity. If you have a strong time-varying - * interference that is causing false detections (OFDM timing errors or - * CCK timing errors) the level can be increased. * @ani_cache_ini_regs: cache the values for ANI from the initial * register settings through the register initialization. */ @@ -561,8 +553,6 @@ struct ath_hw_private_ops { void (*do_getnf)(struct ath_hw *ah, int16_t nfarray[NUM_NF_READINGS]); /* ANI */ - void (*ani_reset)(struct ath_hw *ah, bool is_scanning); - void (*ani_lower_immunity)(struct ath_hw *ah); void (*ani_cache_ini_regs)(struct ath_hw *ah); }; @@ -977,6 +967,7 @@ void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan); * older families (AR5008, AR9001, AR9002) by using modparam_force_new_ani. */ extern int modparam_force_new_ani; +void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning); void ath9k_hw_proc_mib_event(struct ath_hw *ah); void ath9k_hw_attach_ani_ops_old(struct ath_hw *ah); void ath9k_hw_attach_ani_ops_new(struct ath_hw *ah); -- cgit v1.2.3 From 95792178a58716a6afaeb5ab9654f1a0f17a5e8e Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 4 Oct 2010 20:09:50 +0200 Subject: ath9k_hw: merge ath9k_hw_ani_monitor_old and ath9k_hw_ani_monitor_new After the last rounds of cleanup, these functions are now functionally equivalent and can thus be merged. Also get rid of some excessive (and redundant) debug messages. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 83 +----------------------------- drivers/net/wireless/ath/ath9k/ar9002_hw.c | 5 -- drivers/net/wireless/ath/ath9k/ar9003_hw.c | 2 - drivers/net/wireless/ath/ath9k/hw-ops.h | 6 --- drivers/net/wireless/ath/ath9k/hw.h | 8 +-- 5 files changed, 3 insertions(+), 101 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index b9595647810a..f2a907b4acb8 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -730,40 +730,7 @@ static void ath9k_hw_ani_read_counters(struct ath_hw *ah) } -static void ath9k_hw_ani_monitor_old(struct ath_hw *ah, - struct ath9k_channel *chan) -{ - struct ar5416AniState *aniState; - - if (!DO_ANI(ah)) - return; - - aniState = &ah->curchan->ani; - ath9k_hw_ani_read_counters(ah); - - if (aniState->listenTime > 5 * ah->aniperiod) { - if (aniState->ofdmPhyErrCount <= aniState->listenTime * - ah->config.ofdm_trig_low / 1000 && - aniState->cckPhyErrCount <= aniState->listenTime * - ah->config.cck_trig_low / 1000) - ath9k_hw_ani_lower_immunity(ah); - ath9k_ani_restart(ah); - } else if (aniState->listenTime > ah->aniperiod) { - if (aniState->ofdmPhyErrCount > aniState->listenTime * - ah->config.ofdm_trig_high / 1000) { - ath9k_hw_ani_ofdm_err_trigger(ah); - ath9k_ani_restart(ah); - } else if (aniState->cckPhyErrCount > - aniState->listenTime * ah->config.cck_trig_high / - 1000) { - ath9k_hw_ani_cck_err_trigger(ah); - ath9k_ani_restart(ah); - } - } -} - -static void ath9k_hw_ani_monitor_new(struct ath_hw *ah, - struct ath9k_channel *chan) +void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan) { struct ar5416AniState *aniState; struct ath_common *common = ath9k_hw_common(ah); @@ -794,54 +761,26 @@ static void ath9k_hw_ani_monitor_new(struct ath_hw *ah, if (aniState->listenTime > 5 * ah->aniperiod) { if (ofdmPhyErrRate <= ah->config.ofdm_trig_low && cckPhyErrRate <= ah->config.cck_trig_low) { - ath_print(common, ATH_DBG_ANI, - "1. listenTime=%d OFDM:%d errs=%d/s(<%d) " - "CCK:%d errs=%d/s(<%d) -> " - "ath9k_hw_ani_lower_immunity()\n", - aniState->listenTime, - aniState->ofdmNoiseImmunityLevel, - ofdmPhyErrRate, - ah->config.ofdm_trig_low, - aniState->cckNoiseImmunityLevel, - cckPhyErrRate, - ah->config.cck_trig_low); ath9k_hw_ani_lower_immunity(ah); aniState->ofdmsTurn = !aniState->ofdmsTurn; } - ath_print(common, ATH_DBG_ANI, - "1 listenTime=%d ofdm=%d/s cck=%d/s - " - "calling ath9k_ani_restart()\n", - aniState->listenTime, ofdmPhyErrRate, cckPhyErrRate); ath9k_ani_restart(ah); } else if (aniState->listenTime > ah->aniperiod) { /* check to see if need to raise immunity */ if (ofdmPhyErrRate > ah->config.ofdm_trig_high && (cckPhyErrRate <= ah->config.cck_trig_high || aniState->ofdmsTurn)) { - ath_print(common, ATH_DBG_ANI, - "2 listenTime=%d OFDM:%d errs=%d/s(>%d) -> " - "ath9k_hw_ani_ofdm_err_trigger()\n", - aniState->listenTime, - aniState->ofdmNoiseImmunityLevel, - ofdmPhyErrRate, - ah->config.ofdm_trig_high); ath9k_hw_ani_ofdm_err_trigger(ah); ath9k_ani_restart(ah); aniState->ofdmsTurn = false; } else if (cckPhyErrRate > ah->config.cck_trig_high) { - ath_print(common, ATH_DBG_ANI, - "3 listenTime=%d CCK:%d errs=%d/s(>%d) -> " - "ath9k_hw_ani_cck_err_trigger()\n", - aniState->listenTime, - aniState->cckNoiseImmunityLevel, - cckPhyErrRate, - ah->config.cck_trig_high); ath9k_hw_ani_cck_err_trigger(ah); ath9k_ani_restart(ah); aniState->ofdmsTurn = true; } } } +EXPORT_SYMBOL(ath9k_hw_ani_monitor); void ath9k_enable_mib_counters(struct ath_hw *ah) { @@ -1065,21 +1004,3 @@ void ath9k_hw_ani_init(struct ath_hw *ah) ath9k_ani_restart(ah); ath9k_enable_mib_counters(ah); } - -void ath9k_hw_attach_ani_ops_old(struct ath_hw *ah) -{ - struct ath_hw_ops *ops = ath9k_hw_ops(ah); - - ops->ani_monitor = ath9k_hw_ani_monitor_old; - - ath_print(ath9k_hw_common(ah), ATH_DBG_ANY, "Using ANI v1\n"); -} - -void ath9k_hw_attach_ani_ops_new(struct ath_hw *ah) -{ - struct ath_hw_ops *ops = ath9k_hw_ops(ah); - - ops->ani_monitor = ath9k_hw_ani_monitor_new; - - ath_print(ath9k_hw_common(ah), ATH_DBG_ANY, "Using ANI v2\n"); -} diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c index 78bdf0cec5f0..a0471f2e1c7a 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c @@ -572,11 +572,6 @@ void ar9002_hw_attach_ops(struct ath_hw *ah) ar9002_hw_attach_calib_ops(ah); ar9002_hw_attach_mac_ops(ah); - - if (modparam_force_new_ani) - ath9k_hw_attach_ani_ops_new(ah); - else - ath9k_hw_attach_ani_ops_old(ah); } void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index 064168909108..02c970819f79 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -333,6 +333,4 @@ void ar9003_hw_attach_ops(struct ath_hw *ah) ar9003_hw_attach_phy_ops(ah); ar9003_hw_attach_calib_ops(ah); ar9003_hw_attach_mac_ops(ah); - - ath9k_hw_attach_ani_ops_new(ah); } diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h index 6564d1f0ffb1..0a4ad348b699 100644 --- a/drivers/net/wireless/ath/ath9k/hw-ops.h +++ b/drivers/net/wireless/ath/ath9k/hw-ops.h @@ -128,12 +128,6 @@ static inline void ath9k_hw_set11n_virtualmorefrag(struct ath_hw *ah, void *ds, ath9k_hw_ops(ah)->set11n_virtualmorefrag(ah, ds, vmf); } -static inline void ath9k_hw_ani_monitor(struct ath_hw *ah, - struct ath9k_channel *chan) -{ - ath9k_hw_ops(ah)->ani_monitor(ah, chan); -} - /* Private hardware call ops */ /* PHY ops */ diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index c982a24146d2..87627dd63463 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -564,9 +564,6 @@ struct ath_hw_private_ops { * * @config_pci_powersave: * @calibrate: periodic calibration for NF, ANI, IQ, ADC gain, ADC-DC - * - * @ani_monitor: called periodically by the core driver to collect - * MIB stats and adjust ANI if specific thresholds have been reached. */ struct ath_hw_ops { void (*config_pci_powersave)(struct ath_hw *ah, @@ -607,8 +604,6 @@ struct ath_hw_ops { u32 burstDuration); void (*set11n_virtualmorefrag)(struct ath_hw *ah, void *ds, u32 vmf); - - void (*ani_monitor)(struct ath_hw *ah, struct ath9k_channel *chan); }; struct ath_nf_limits { @@ -969,8 +964,7 @@ void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan); extern int modparam_force_new_ani; void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning); void ath9k_hw_proc_mib_event(struct ath_hw *ah); -void ath9k_hw_attach_ani_ops_old(struct ath_hw *ah); -void ath9k_hw_attach_ani_ops_new(struct ath_hw *ah); +void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan); #define ATH_PCIE_CAP_LINK_CTRL 0x70 #define ATH_PCIE_CAP_LINK_L0S 1 -- cgit v1.2.3 From fbab7390f954e3517c72d12cfd9a1502699aa368 Mon Sep 17 00:00:00 2001 From: Senthil Balasubramanian Date: Tue, 5 Oct 2010 20:36:40 +0530 Subject: ath9k: remove unnecessary power save flags. drv_config callback is called only after the ack for the nullframe is received and so driver need not do anything special for this. So remove NULLFUNC_COMPLETED, PS_ENABLED flags and bf_isnullfunc flags from ath9k as mac80211 already handles them properly. Signed-off-by: Senthil Balasubramanian Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 4 ---- drivers/net/wireless/ath/ath9k/main.c | 18 ++++-------------- drivers/net/wireless/ath/ath9k/xmit.c | 30 ------------------------------ 3 files changed, 4 insertions(+), 48 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 9f8e542ef47e..de2b18ee7f77 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -241,7 +241,6 @@ struct ath_buf { dma_addr_t bf_daddr; /* physical addr of desc */ dma_addr_t bf_buf_addr; /* physical addr of data buffer */ bool bf_stale; - bool bf_isnullfunc; bool bf_tx_aborted; u16 bf_flags; struct ath_buf_state bf_state; @@ -349,7 +348,6 @@ int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid, u16 *ssn); void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid); void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid); -void ath9k_enable_ps(struct ath_softc *sc); /********/ /* VIFs */ @@ -573,8 +571,6 @@ struct ath_ant_comb { #define PS_WAIT_FOR_PSPOLL_DATA BIT(2) #define PS_WAIT_FOR_TX_ACK BIT(3) #define PS_BEACON_SYNC BIT(4) -#define PS_NULLFUNC_COMPLETED BIT(5) -#define PS_ENABLED BIT(6) struct ath_wiphy; struct ath_rate_table; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index d9c2e2d93136..d0694cb685fa 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1484,7 +1484,7 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, mutex_unlock(&sc->mutex); } -void ath9k_enable_ps(struct ath_softc *sc) +static void ath9k_enable_ps(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; @@ -1551,20 +1551,10 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) if (changed & IEEE80211_CONF_CHANGE_PS) { unsigned long flags; spin_lock_irqsave(&sc->sc_pm_lock, flags); - if (conf->flags & IEEE80211_CONF_PS) { - sc->ps_flags |= PS_ENABLED; - /* - * At this point we know hardware has received an ACK - * of a previously sent null data frame. - */ - if ((sc->ps_flags & PS_NULLFUNC_COMPLETED)) { - sc->ps_flags &= ~PS_NULLFUNC_COMPLETED; - ath9k_enable_ps(sc); - } - } else { + if (conf->flags & IEEE80211_CONF_PS) + ath9k_enable_ps(sc); + else { sc->ps_enabled = false; - sc->ps_flags &= ~(PS_ENABLED | - PS_NULLFUNC_COMPLETED); ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index f7da6b20a925..aa447770eb2b 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1648,13 +1648,6 @@ static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf, bf->bf_buf_addr = bf->bf_dmacontext; - /* tag if this is a nullfunc frame to enable PS when AP acks it */ - if (ieee80211_is_nullfunc(fc) && ieee80211_has_pm(fc)) { - bf->bf_isnullfunc = true; - sc->ps_flags &= ~PS_NULLFUNC_COMPLETED; - } else - bf->bf_isnullfunc = false; - bf->bf_tx_aborted = false; return 0; @@ -2081,18 +2074,6 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) break; } - /* - * We now know the nullfunc frame has been ACKed so we - * can disable RX. - */ - if (bf->bf_isnullfunc && - (ts.ts_status & ATH9K_TX_ACKED)) { - if ((sc->ps_flags & PS_ENABLED)) - ath9k_enable_ps(sc); - else - sc->ps_flags |= PS_NULLFUNC_COMPLETED; - } - /* * Remove ath_buf's of the same transmit unit from txq, * however leave the last descriptor back as the holding @@ -2236,17 +2217,6 @@ void ath_tx_edma_tasklet(struct ath_softc *sc) txok = !(txs.ts_status & ATH9K_TXERR_MASK); - /* - * Make sure null func frame is acked before configuring - * hw into ps mode. - */ - if (bf->bf_isnullfunc && txok) { - if ((sc->ps_flags & PS_ENABLED)) - ath9k_enable_ps(sc); - else - sc->ps_flags |= PS_NULLFUNC_COMPLETED; - } - if (!bf_isampdu(bf)) { if (txs.ts_status & ATH9K_TXERR_XRETRY) bf->bf_state.bf_type |= BUF_XRETRY; -- cgit v1.2.3 From 845d708e62f08a45ff716fdb270c52585cad31d0 Mon Sep 17 00:00:00 2001 From: Senthil Balasubramanian Date: Tue, 5 Oct 2010 20:36:41 +0530 Subject: ath9k: Introduce a wrapper for power save disable. Signed-off-by: Senthil Balasubramanian Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 40 +++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 18 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index d0694cb685fa..74c2dc8a8b8a 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1498,13 +1498,32 @@ static void ath9k_enable_ps(struct ath_softc *sc) } } +static void ath9k_disable_ps(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + + sc->ps_enabled = false; + ath9k_hw_setpower(ah, ATH9K_PM_AWAKE); + if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { + ath9k_hw_setrxabort(ah, 0); + sc->ps_flags &= ~(PS_WAIT_FOR_BEACON | + PS_WAIT_FOR_CAB | + PS_WAIT_FOR_PSPOLL_DATA | + PS_WAIT_FOR_TX_ACK); + if (ah->imask & ATH9K_INT_TIM_TIMER) { + ah->imask &= ~ATH9K_INT_TIM_TIMER; + ath9k_hw_set_interrupts(ah, ah->imask); + } + } + +} + static int ath9k_config(struct ieee80211_hw *hw, u32 changed) { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ieee80211_conf *conf = &hw->conf; - struct ath_hw *ah = sc->sc_ah; bool disable_radio; mutex_lock(&sc->mutex); @@ -1553,23 +1572,8 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) spin_lock_irqsave(&sc->sc_pm_lock, flags); if (conf->flags & IEEE80211_CONF_PS) ath9k_enable_ps(sc); - else { - sc->ps_enabled = false; - ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); - if (!(ah->caps.hw_caps & - ATH9K_HW_CAP_AUTOSLEEP)) { - ath9k_hw_setrxabort(sc->sc_ah, 0); - sc->ps_flags &= ~(PS_WAIT_FOR_BEACON | - PS_WAIT_FOR_CAB | - PS_WAIT_FOR_PSPOLL_DATA | - PS_WAIT_FOR_TX_ACK); - if (ah->imask & ATH9K_INT_TIM_TIMER) { - ah->imask &= ~ATH9K_INT_TIM_TIMER; - ath9k_hw_set_interrupts(sc->sc_ah, - ah->imask); - } - } - } + else + ath9k_disable_ps(sc); spin_unlock_irqrestore(&sc->sc_pm_lock, flags); } -- cgit v1.2.3 From 15a6321d1c0f8db561932cd99e1b9897981da71f Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Thu, 7 Oct 2010 20:58:39 +0530 Subject: ath9k_hw: Fix hw reset failure with HTC driver The following commit removed DISABLE_REGWRITE_BUFFER ops. The unnecessary REGWRITE_BUFFER_FLUSH was not removed properly which is causing failure on hw reset. Author: Felix Fietkau Date: Tue Oct 5 12:03:42 2010 +0200 ath9k_hw: clean up register write buffering Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/mac.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index e5784595bd0f..8c13479b17cd 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -491,8 +491,6 @@ bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q) REG_WRITE(ah, AR_DMISC(q), AR_D_MISC_CW_BKOFF_EN | AR_D_MISC_FRAG_WAIT_EN | 0x2); - REGWRITE_BUFFER_FLUSH(ah); - if (qi->tqi_cbrPeriod) { REG_WRITE(ah, AR_QCBRCFG(q), SM(qi->tqi_cbrPeriod, AR_Q_CBRCFG_INTERVAL) | @@ -508,8 +506,6 @@ bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q) AR_Q_RDYTIMECFG_EN); } - REGWRITE_BUFFER_FLUSH(ah); - REG_WRITE(ah, AR_DCHNTIME(q), SM(qi->tqi_burstTime, AR_D_CHNTIME_DUR) | (qi->tqi_burstTime ? AR_D_CHNTIME_EN : 0)); -- cgit v1.2.3 From 92c68a66a8f73c51f062ae8cae958c86a21fea78 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Fri, 8 Oct 2010 09:43:29 -0700 Subject: ath5k: Print out opmode in debugfs. Helps debug multi-VIF scenarios. Signed-off-by: Ben Greear Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/debug.c | 10 ++++++++++ drivers/net/wireless/ath/debug.c | 29 +++++++++++++++++++++++++++++ drivers/net/wireless/ath/debug.h | 3 +++ 3 files changed, 42 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c index c2d549f871f9..a342a9da1b3e 100644 --- a/drivers/net/wireless/ath/ath5k/debug.c +++ b/drivers/net/wireless/ath/ath5k/debug.c @@ -60,6 +60,7 @@ #include "base.h" #include "debug.h" +#include "../debug.h" static unsigned int ath5k_debug; module_param_named(debug, ath5k_debug, uint, 0); @@ -492,6 +493,7 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf, char buf[700]; unsigned int len = 0; u32 filt = ath5k_hw_get_rx_filter(sc->ah); + const char *tmp; len += snprintf(buf+len, sizeof(buf)-len, "bssid-mask: %pM\n", sc->bssidmask); @@ -524,6 +526,14 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf, else len += snprintf(buf+len, sizeof(buf)-len, "\n"); + tmp = ath_opmode_to_string(sc->opmode); + if (tmp) + len += snprintf(buf+len, sizeof(buf)-len, "opmode: %s\n", + tmp); + else + len += snprintf(buf+len, sizeof(buf)-len, + "opmode: UNKNOWN-%i\n", sc->opmode); + if (len > sizeof(buf)) len = sizeof(buf); diff --git a/drivers/net/wireless/ath/debug.c b/drivers/net/wireless/ath/debug.c index 53e77bd131b9..a9eb7876dbe1 100644 --- a/drivers/net/wireless/ath/debug.c +++ b/drivers/net/wireless/ath/debug.c @@ -30,3 +30,32 @@ void ath_print(struct ath_common *common, int dbg_mask, const char *fmt, ...) va_end(args); } EXPORT_SYMBOL(ath_print); + +const char *ath_opmode_to_string(enum nl80211_iftype opmode) +{ + switch (opmode) { + case NL80211_IFTYPE_UNSPECIFIED: + return "UNSPEC"; + case NL80211_IFTYPE_ADHOC: + return "ADHOC"; + case NL80211_IFTYPE_STATION: + return "STATION"; + case NL80211_IFTYPE_AP: + return "AP"; + case NL80211_IFTYPE_AP_VLAN: + return "AP-VLAN"; + case NL80211_IFTYPE_WDS: + return "WDS"; + case NL80211_IFTYPE_MONITOR: + return "MONITOR"; + case NL80211_IFTYPE_MESH_POINT: + return "MESH"; + case NL80211_IFTYPE_P2P_CLIENT: + return "P2P-CLIENT"; + case NL80211_IFTYPE_P2P_GO: + return "P2P-GO"; + default: + return NULL; + } +} +EXPORT_SYMBOL(ath_opmode_to_string); diff --git a/drivers/net/wireless/ath/debug.h b/drivers/net/wireless/ath/debug.h index fd3a020682dc..a3a5a628d24d 100644 --- a/drivers/net/wireless/ath/debug.h +++ b/drivers/net/wireless/ath/debug.h @@ -77,4 +77,7 @@ ath_print(struct ath_common *common, int dbg_mask, const char *fmt, ...) } #endif /* CONFIG_ATH_DEBUG */ +/** Returns string describing opmode, or NULL if unknown mode. */ +const char *ath_opmode_to_string(enum nl80211_iftype opmode); + #endif /* ATH_DEBUG_H */ -- cgit v1.2.3 From 62c58fb4316905cd9c1dbf01967935ed5610b45d Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Fri, 8 Oct 2010 12:01:15 -0700 Subject: ath5k: Adjust opmode when interfaces are removed. Otherwise, if there is an AP and a STATION, and AP is removed, the NIC will not revert back to STATION mode. Reported-by: Eliad Peller Signed-off-by: Ben Greear Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 66 +++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 26 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index dad726585637..c9732a6ce87f 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -62,6 +62,7 @@ #include "reg.h" #include "debug.h" #include "ani.h" +#include "../debug.h" static int modparam_nohwcrypt; module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); @@ -517,12 +518,14 @@ struct ath_vif_iter_data { bool need_set_hw_addr; bool found_active; bool any_assoc; + enum nl80211_iftype opmode; }; static void ath_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct ath_vif_iter_data *iter_data = data; int i; + struct ath5k_vif *avf = (void *)vif->drv_priv; if (iter_data->hw_macaddr) for (i = 0; i < ETH_ALEN; i++) @@ -539,13 +542,34 @@ static void ath_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) iter_data->need_set_hw_addr = false; if (!iter_data->any_assoc) { - struct ath5k_vif *avf = (void *)vif->drv_priv; if (avf->assoc) iter_data->any_assoc = true; } + + /* Calculate combined mode - when APs are active, operate in AP mode. + * Otherwise use the mode of the new interface. This can currently + * only deal with combinations of APs and STAs. Only one ad-hoc + * interfaces is allowed above. + */ + if (avf->opmode == NL80211_IFTYPE_AP) + iter_data->opmode = NL80211_IFTYPE_AP; + else + if (iter_data->opmode == NL80211_IFTYPE_UNSPECIFIED) + iter_data->opmode = avf->opmode; } -void ath5k_update_bssid_mask(struct ath5k_softc *sc, struct ieee80211_vif *vif) +static void ath_do_set_opmode(struct ath5k_softc *sc) +{ + struct ath5k_hw *ah = sc->ah; + ath5k_hw_set_opmode(ah, sc->opmode); + ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "mode setup opmode %d (%s)\n", + sc->opmode, + ath_opmode_to_string(sc->opmode) ? + ath_opmode_to_string(sc->opmode) : "UKNOWN"); +} + +void ath5k_update_bssid_mask_and_opmode(struct ath5k_softc *sc, + struct ieee80211_vif *vif) { struct ath_common *common = ath5k_hw_common(sc->ah); struct ath_vif_iter_data iter_data; @@ -558,6 +582,7 @@ void ath5k_update_bssid_mask(struct ath5k_softc *sc, struct ieee80211_vif *vif) memset(&iter_data.mask, 0xff, ETH_ALEN); iter_data.found_active = false; iter_data.need_set_hw_addr = true; + iter_data.opmode = NL80211_IFTYPE_UNSPECIFIED; if (vif) ath_vif_iter(&iter_data, vif->addr, vif); @@ -567,10 +592,18 @@ void ath5k_update_bssid_mask(struct ath5k_softc *sc, struct ieee80211_vif *vif) &iter_data); memcpy(sc->bssidmask, iter_data.mask, ETH_ALEN); + sc->opmode = iter_data.opmode; + if (sc->opmode == NL80211_IFTYPE_UNSPECIFIED) + /* Nothing active, default to station mode */ + sc->opmode = NL80211_IFTYPE_STATION; + + ath_do_set_opmode(sc); + if (iter_data.need_set_hw_addr && iter_data.found_active) ath5k_hw_set_lladdr(sc->ah, iter_data.active_mac); - ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask); + if (ath5k_hw_hasbssidmask(sc->ah)) + ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask); } static void @@ -582,15 +615,9 @@ ath5k_mode_setup(struct ath5k_softc *sc, struct ieee80211_vif *vif) /* configure rx filter */ rfilt = sc->filter_flags; ath5k_hw_set_rx_filter(ah, rfilt); - - if (ath5k_hw_hasbssidmask(ah)) - ath5k_update_bssid_mask(sc, vif); - - /* configure operational mode */ - ath5k_hw_set_opmode(ah, sc->opmode); - - ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "mode setup opmode %d\n", sc->opmode); ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "RX filter 0x%x\n", rfilt); + + ath5k_update_bssid_mask_and_opmode(sc, vif); } static inline int @@ -2688,7 +2715,7 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) SET_IEEE80211_PERM_ADDR(hw, mac); memcpy(&sc->lladdr, mac, ETH_ALEN); /* All MAC address bits matter for ACKs */ - ath5k_update_bssid_mask(sc, NULL); + ath5k_update_bssid_mask_and_opmode(sc, NULL); regulatory->current_rd = ah->ah_capabilities.cap_eeprom.ee_regdomain; ret = ath_regd_init(regulatory, hw->wiphy, ath5k_reg_notifier); @@ -2786,7 +2813,6 @@ static int ath5k_add_interface(struct ieee80211_hw *hw, { struct ath5k_softc *sc = hw->priv; int ret; - struct ath5k_hw *ah = sc->ah; struct ath5k_vif *avf = (void *)vif->drv_priv; mutex_lock(&sc->lock); @@ -2850,18 +2876,6 @@ static int ath5k_add_interface(struct ieee80211_hw *hw, sc->num_adhoc_vifs++; } - /* Set combined mode - when APs are configured, operate in AP mode. - * Otherwise use the mode of the new interface. This can currently - * only deal with combinations of APs and STAs. Only one ad-hoc - * interfaces is allowed above. - */ - if (sc->num_ap_vifs) - sc->opmode = NL80211_IFTYPE_AP; - else - sc->opmode = vif->type; - - ath5k_hw_set_opmode(ah, sc->opmode); - /* Any MAC address is fine, all others are included through the * filter. */ @@ -2905,7 +2919,7 @@ ath5k_remove_interface(struct ieee80211_hw *hw, else if (avf->opmode == NL80211_IFTYPE_ADHOC) sc->num_adhoc_vifs--; - ath5k_update_bssid_mask(sc, NULL); + ath5k_update_bssid_mask_and_opmode(sc, NULL); mutex_unlock(&sc->lock); } -- cgit v1.2.3 From dfdac8ac033c9ad048a5c68563bd41bda6c5e60b Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 8 Oct 2010 22:13:51 +0200 Subject: ath9k_hw: store the clock rate in common data on channel changes Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath.h | 2 ++ drivers/net/wireless/ath/ath9k/ani.c | 26 ++------------------------ drivers/net/wireless/ath/ath9k/hw.c | 32 +++++++++++++++++++------------- 3 files changed, 23 insertions(+), 37 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index cee0191704f5..b36d9d770ff1 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -145,6 +145,8 @@ struct ath_common { DECLARE_BITMAP(tkip_keymap, ATH_KEYMAX); enum ath_crypt_caps crypt_caps; + unsigned int clockrate; + struct ath_regulatory regulatory; const struct ath_ops *ops; const struct ath_bus_ops *bus_ops; diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index f2a907b4acb8..f2aa68405d2f 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -465,35 +465,13 @@ static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah) ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel - 1); } -static u8 ath9k_hw_chan_2_clockrate_mhz(struct ath_hw *ah) -{ - struct ath9k_channel *chan = ah->curchan; - struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; - u8 clockrate; /* in MHz */ - - if (!ah->curchan) /* should really check for CCK instead */ - clockrate = ATH9K_CLOCK_RATE_CCK; - else if (conf->channel->band == IEEE80211_BAND_2GHZ) - clockrate = ATH9K_CLOCK_RATE_2GHZ_OFDM; - else if (IS_CHAN_A_FAST_CLOCK(ah, chan)) - clockrate = ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM; - else - clockrate = ATH9K_CLOCK_RATE_5GHZ_OFDM; - - if (conf_is_ht40(conf)) - return clockrate * 2; - - return clockrate; -} - static int32_t ath9k_hw_ani_get_listen_time(struct ath_hw *ah) { + struct ath_common *common = ath9k_hw_common(ah); int32_t listen_time; - int32_t clock_rate; ath9k_hw_update_cycle_counters(ah); - clock_rate = ath9k_hw_chan_2_clockrate_mhz(ah) * 1000; - listen_time = ah->listen_time / clock_rate; + listen_time = ah->listen_time / (common->clockrate * 1000); ah->listen_time = 0; return listen_time; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 05e9935ef160..f5d79177770c 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -88,29 +88,32 @@ static void ath9k_hw_ani_cache_ini_regs(struct ath_hw *ah) /* Helper Functions */ /********************/ -static u32 ath9k_hw_mac_clks(struct ath_hw *ah, u32 usecs) +static void ath9k_hw_set_clockrate(struct ath_hw *ah) { struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; + struct ath_common *common = ath9k_hw_common(ah); + unsigned int clockrate; if (!ah->curchan) /* should really check for CCK instead */ - return usecs *ATH9K_CLOCK_RATE_CCK; - if (conf->channel->band == IEEE80211_BAND_2GHZ) - return usecs *ATH9K_CLOCK_RATE_2GHZ_OFDM; - - if (ah->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK) - return usecs * ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM; + clockrate = ATH9K_CLOCK_RATE_CCK; + else if (conf->channel->band == IEEE80211_BAND_2GHZ) + clockrate = ATH9K_CLOCK_RATE_2GHZ_OFDM; + else if (ah->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK) + clockrate = ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM; else - return usecs * ATH9K_CLOCK_RATE_5GHZ_OFDM; + clockrate = ATH9K_CLOCK_RATE_5GHZ_OFDM; + + if (conf_is_ht40(conf)) + clockrate *= 2; + + common->clockrate = clockrate; } static u32 ath9k_hw_mac_to_clks(struct ath_hw *ah, u32 usecs) { - struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; + struct ath_common *common = ath9k_hw_common(ah); - if (conf_is_ht40(conf)) - return ath9k_hw_mac_clks(ah, usecs) * 2; - else - return ath9k_hw_mac_clks(ah, usecs); + return usecs * common->clockrate; } bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout) @@ -1156,6 +1159,7 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah, "Failed to set channel\n"); return false; } + ath9k_hw_set_clockrate(ah); ah->eep_ops->set_txpower(ah, chan, ath9k_regd_get_ctl(regulatory, chan), @@ -1368,6 +1372,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (r) return r; + ath9k_hw_set_clockrate(ah); + ENABLE_REGWRITE_BUFFER(ah); for (i = 0; i < AR_NUM_DCU; i++) -- cgit v1.2.3 From 9d119f3ebd074bde0b801f476a44ca60d222efb2 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 8 Oct 2010 22:13:52 +0200 Subject: ath5k: store the clock rate in common data on channel changes Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/ath5k.h | 2 +- drivers/net/wireless/ath/ath5k/pcu.c | 13 ++++++++----- drivers/net/wireless/ath/ath5k/phy.c | 1 + 3 files changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index 0cba2e315d9a..4a367cdb3eb9 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -1201,7 +1201,7 @@ void ath5k_hw_set_ack_bitrate_high(struct ath5k_hw *ah, bool high); /* Clock rate related functions */ unsigned int ath5k_hw_htoclock(struct ath5k_hw *ah, unsigned int usec); unsigned int ath5k_hw_clocktoh(struct ath5k_hw *ah, unsigned int clock); -unsigned int ath5k_hw_get_clockrate(struct ath5k_hw *ah); +void ath5k_hw_set_clockrate(struct ath5k_hw *ah); /* Queue Control Unit, DFS Control Unit Functions */ int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue, diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c index 095d30b50ec7..074b4c644399 100644 --- a/drivers/net/wireless/ath/ath5k/pcu.c +++ b/drivers/net/wireless/ath/ath5k/pcu.c @@ -207,7 +207,8 @@ static int ath5k_hw_set_cts_timeout(struct ath5k_hw *ah, unsigned int timeout) */ unsigned int ath5k_hw_htoclock(struct ath5k_hw *ah, unsigned int usec) { - return usec * ath5k_hw_get_clockrate(ah); + struct ath_common *common = ath5k_hw_common(ah); + return usec * common->clockrate; } /** @@ -216,17 +217,19 @@ unsigned int ath5k_hw_htoclock(struct ath5k_hw *ah, unsigned int usec) */ unsigned int ath5k_hw_clocktoh(struct ath5k_hw *ah, unsigned int clock) { - return clock / ath5k_hw_get_clockrate(ah); + struct ath_common *common = ath5k_hw_common(ah); + return clock / common->clockrate; } /** - * ath5k_hw_get_clockrate - Get the clock rate for current mode + * ath5k_hw_set_clockrate - Set common->clockrate for the current channel * * @ah: The &struct ath5k_hw */ -unsigned int ath5k_hw_get_clockrate(struct ath5k_hw *ah) +void ath5k_hw_set_clockrate(struct ath5k_hw *ah) { struct ieee80211_channel *channel = ah->ah_current_channel; + struct ath_common *common = ath5k_hw_common(ah); int clock; if (channel->hw_value & CHANNEL_5GHZ) @@ -240,7 +243,7 @@ unsigned int ath5k_hw_get_clockrate(struct ath5k_hw *ah) if (channel->hw_value & CHANNEL_TURBO) clock *= 2; - return clock; + common->clockrate = clock; } /** diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c index 61da913e7c8f..219367884e64 100644 --- a/drivers/net/wireless/ath/ath5k/phy.c +++ b/drivers/net/wireless/ath/ath5k/phy.c @@ -1093,6 +1093,7 @@ int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel) ah->ah_current_channel = channel; ah->ah_turbo = channel->hw_value == CHANNEL_T ? true : false; + ath5k_hw_set_clockrate(ah); return 0; } -- cgit v1.2.3 From b5bfc5683db44a121ad47ec0a9f4efd4aac040e0 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 8 Oct 2010 22:13:53 +0200 Subject: ath9k_hw: move the cycle counter tracking to ath Instead of keeping track of wraparound, clear the counters on every access and keep separate deltas for ANI and later survey use. Also moves the function for calculating the 'listen time' for ANI Signed-off-by: Felix Fietkau Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath.h | 14 +++++++ drivers/net/wireless/ath/ath9k/ani.c | 64 ++--------------------------- drivers/net/wireless/ath/ath9k/ani.h | 8 ---- drivers/net/wireless/ath/ath9k/ar9003_phy.c | 7 ++-- drivers/net/wireless/ath/ath9k/hw.h | 2 - drivers/net/wireless/ath/ath9k/main.c | 12 +++++- drivers/net/wireless/ath/ath9k/reg.h | 11 ----- drivers/net/wireless/ath/hw.c | 59 ++++++++++++++++++++++++++ drivers/net/wireless/ath/reg.h | 11 +++++ 9 files changed, 101 insertions(+), 87 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index b36d9d770ff1..501050c0296f 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -19,6 +19,7 @@ #include #include +#include #include /* @@ -42,6 +43,13 @@ struct ath_ani { struct timer_list timer; }; +struct ath_cycle_counters { + u32 cycles; + u32 rx_busy; + u32 rx_frame; + u32 tx_frame; +}; + enum ath_device_state { ATH_HW_UNAVAILABLE, ATH_HW_INITIALIZED, @@ -147,6 +155,10 @@ struct ath_common { unsigned int clockrate; + spinlock_t cc_lock; + struct ath_cycle_counters cc_ani; + struct ath_cycle_counters cc_survey; + struct ath_regulatory regulatory; const struct ath_ops *ops; const struct ath_bus_ops *bus_ops; @@ -163,5 +175,7 @@ int ath_key_config(struct ath_common *common, struct ieee80211_sta *sta, struct ieee80211_key_conf *key); bool ath_hw_keyreset(struct ath_common *common, u16 entry); +void ath_hw_cycle_counters_update(struct ath_common *common); +int32_t ath_hw_get_listen_time(struct ath_common *common); #endif /* ATH_H */ diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index f2aa68405d2f..3aa8fb1ad77f 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -465,18 +465,6 @@ static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah) ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel - 1); } -static int32_t ath9k_hw_ani_get_listen_time(struct ath_hw *ah) -{ - struct ath_common *common = ath9k_hw_common(ah); - int32_t listen_time; - - ath9k_hw_update_cycle_counters(ah); - listen_time = ah->listen_time / (common->clockrate * 1000); - ah->listen_time = 0; - - return listen_time; -} - static void ath9k_ani_reset_old(struct ath_hw *ah, bool is_scanning) { struct ar5416AniState *aniState; @@ -655,7 +643,9 @@ static void ath9k_hw_ani_read_counters(struct ath_hw *ah) u32 phyCnt1, phyCnt2; int32_t listenTime; - listenTime = ath9k_hw_ani_get_listen_time(ah); + ath_hw_cycle_counters_update(common); + listenTime = ath_hw_get_listen_time(common); + if (listenTime < 0) { ah->stats.ast_ani_lneg++; ath9k_ani_restart(ah); @@ -796,54 +786,6 @@ void ath9k_hw_disable_mib_counters(struct ath_hw *ah) } EXPORT_SYMBOL(ath9k_hw_disable_mib_counters); -void ath9k_hw_update_cycle_counters(struct ath_hw *ah) -{ - struct ath_cycle_counters cc; - bool clear; - - memcpy(&cc, &ah->cc, sizeof(cc)); - - /* freeze counters */ - REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC); - - ah->cc.cycles = REG_READ(ah, AR_CCCNT); - if (ah->cc.cycles < cc.cycles) { - clear = true; - goto skip; - } - - ah->cc.rx_clear = REG_READ(ah, AR_RCCNT); - ah->cc.rx_frame = REG_READ(ah, AR_RFCNT); - ah->cc.tx_frame = REG_READ(ah, AR_TFCNT); - - /* prevent wraparound */ - if (ah->cc.cycles & BIT(31)) - clear = true; - -#define CC_DELTA(_field, _reg) ah->cc_delta._field += ah->cc._field - cc._field - CC_DELTA(cycles, AR_CCCNT); - CC_DELTA(rx_frame, AR_RFCNT); - CC_DELTA(rx_clear, AR_RCCNT); - CC_DELTA(tx_frame, AR_TFCNT); -#undef CC_DELTA - - ah->listen_time += (ah->cc.cycles - cc.cycles) - - ((ah->cc.rx_frame - cc.rx_frame) + - (ah->cc.tx_frame - cc.tx_frame)); - -skip: - if (clear) { - REG_WRITE(ah, AR_CCCNT, 0); - REG_WRITE(ah, AR_RFCNT, 0); - REG_WRITE(ah, AR_RCCNT, 0); - REG_WRITE(ah, AR_TFCNT, 0); - memset(&ah->cc, 0, sizeof(ah->cc)); - } - - /* unfreeze counters */ - REG_WRITE(ah, AR_MIBC, 0); -} - /* * Process a MIB interrupt. We may potentially be invoked because * any of the MIB counters overflow/trigger so don't assume we're diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h index 98cfd8154c71..0cd6783de883 100644 --- a/drivers/net/wireless/ath/ath9k/ani.h +++ b/drivers/net/wireless/ath/ath9k/ani.h @@ -93,13 +93,6 @@ struct ath9k_mib_stats { u32 beacons; }; -struct ath_cycle_counters { - u32 cycles; - u32 rx_frame; - u32 rx_clear; - u32 tx_frame; -}; - /* INI default values for ANI registers */ struct ath9k_ani_default { u16 m1ThreshLow; @@ -164,7 +157,6 @@ struct ar5416Stats { void ath9k_enable_mib_counters(struct ath_hw *ah); void ath9k_hw_disable_mib_counters(struct ath_hw *ah); -void ath9k_hw_update_cycle_counters(struct ath_hw *ah); void ath9k_hw_ani_setup(struct ath_hw *ah); void ath9k_hw_ani_init(struct ath_hw *ah); int ath9k_hw_get_ani_channel_idx(struct ath_hw *ah, diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index efb05599b84c..669b777729b3 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -1254,13 +1254,12 @@ void ar9003_hw_bb_watchdog_dbg_info(struct ath_hw *ah) "** BB mode: BB_gen_controls=0x%08x **\n", REG_READ(ah, AR_PHY_GEN_CTRL)); - ath9k_hw_update_cycle_counters(ah); -#define PCT(_field) (ah->cc_delta._field * 100 / ah->cc_delta.cycles) - if (ah->cc_delta.cycles) +#define PCT(_field) (common->cc_survey._field * 100 / common->cc_survey.cycles) + if (common->cc_survey.cycles) ath_print(common, ATH_DBG_RESET, "** BB busy times: rx_clear=%d%%, " "rx_frame=%d%%, tx_frame=%d%% **\n", - PCT(rx_clear), PCT(rx_frame), PCT(tx_frame)); + PCT(rx_busy), PCT(rx_frame), PCT(tx_frame)); ath_print(common, ATH_DBG_RESET, "==== BB update: done ====\n\n"); diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 87627dd63463..7f696c82ca0a 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -740,8 +740,6 @@ struct ath_hw { int coarse_low[5]; int firpwr[5]; enum ath9k_ani_cmd ani_function; - struct ath_cycle_counters cc, cc_delta; - int32_t listen_time; /* Bluetooth coexistance */ struct ath_btcoex_hw btcoex_hw; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 74c2dc8a8b8a..360c6f5e843a 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -399,6 +399,7 @@ void ath_ani_calibrate(unsigned long data) bool aniflag = false; unsigned int timestamp = jiffies_to_msecs(jiffies); u32 cal_interval, short_cal_interval, long_cal_interval; + unsigned long flags; if (ah->caldata && ah->caldata->nfcal_interference) long_cal_interval = ATH_LONG_CALINTERVAL_INT; @@ -449,8 +450,11 @@ void ath_ani_calibrate(unsigned long data) /* Skip all processing if there's nothing to do. */ if (longcal || shortcal || aniflag) { /* Call ANI routine if necessary */ - if (aniflag) + if (aniflag) { + spin_lock_irqsave(&common->cc_lock, flags); ath9k_hw_ani_monitor(ah, ah->curchan); + spin_unlock_irqrestore(&common->cc_lock, flags); + } /* Perform calibration if necessary */ if (longcal || shortcal) { @@ -635,6 +639,7 @@ irqreturn_t ath_isr(int irq, void *dev) struct ath_softc *sc = dev; struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); enum ath9k_int status; bool sched = false; @@ -684,7 +689,12 @@ irqreturn_t ath_isr(int irq, void *dev) if ((ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) && (status & ATH9K_INT_BB_WATCHDOG)) { + + spin_lock(&common->cc_lock); + ath_hw_cycle_counters_update(common); ar9003_hw_bb_watchdog_dbg_info(ah); + spin_unlock(&common->cc_lock); + goto chip_reset; } diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index 6d01e501b9b4..017617894533 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -107,12 +107,6 @@ #define AR_RXCFG_DMASZ_256B 6 #define AR_RXCFG_DMASZ_512B 7 -#define AR_MIBC 0x0040 -#define AR_MIBC_COW 0x00000001 -#define AR_MIBC_FMC 0x00000002 -#define AR_MIBC_CMC 0x00000004 -#define AR_MIBC_MCS 0x00000008 - #define AR_TOPS 0x0044 #define AR_TOPS_MASK 0x0000FFFF @@ -1524,11 +1518,6 @@ enum { #define AR_TPC_CHIRP 0x003f0000 #define AR_TPC_CHIRP_S 0x16 -#define AR_TFCNT 0x80ec -#define AR_RFCNT 0x80f0 -#define AR_RCCNT 0x80f4 -#define AR_CCCNT 0x80f8 - #define AR_QUIET1 0x80fc #define AR_QUIET1_NEXT_QUIET_S 0 #define AR_QUIET1_NEXT_QUIET_M 0x0000ffff diff --git a/drivers/net/wireless/ath/hw.c b/drivers/net/wireless/ath/hw.c index a8f81ea09f14..183c28281385 100644 --- a/drivers/net/wireless/ath/hw.c +++ b/drivers/net/wireless/ath/hw.c @@ -124,3 +124,62 @@ void ath_hw_setbssidmask(struct ath_common *common) REG_WRITE(ah, get_unaligned_le16(common->bssidmask + 4), AR_BSSMSKU); } EXPORT_SYMBOL(ath_hw_setbssidmask); + + +/** + * ath_hw_cycle_counters_update - common function to update cycle counters + * + * @common: the ath_common struct for the device. + * + * This function is used to update all cycle counters in one place. + * It has to be called while holding common->cc_lock! + */ +void ath_hw_cycle_counters_update(struct ath_common *common) +{ + u32 cycles, busy, rx, tx; + void *ah = common->ah; + + /* freeze */ + REG_WRITE(ah, AR_MIBC_FMC, AR_MIBC); + + /* read */ + cycles = REG_READ(ah, AR_CCCNT); + busy = REG_READ(ah, AR_RCCNT); + rx = REG_READ(ah, AR_RFCNT); + tx = REG_READ(ah, AR_TFCNT); + + /* clear */ + REG_WRITE(ah, 0, AR_CCCNT); + REG_WRITE(ah, 0, AR_RFCNT); + REG_WRITE(ah, 0, AR_RCCNT); + REG_WRITE(ah, 0, AR_TFCNT); + + /* unfreeze */ + REG_WRITE(ah, 0, AR_MIBC); + + /* update all cycle counters here */ + common->cc_ani.cycles += cycles; + common->cc_ani.rx_busy += busy; + common->cc_ani.rx_frame += rx; + common->cc_ani.tx_frame += tx; + + common->cc_survey.cycles += cycles; + common->cc_survey.rx_busy += busy; + common->cc_survey.rx_frame += rx; + common->cc_survey.tx_frame += tx; +} +EXPORT_SYMBOL(ath_hw_cycle_counters_update); + +int32_t ath_hw_get_listen_time(struct ath_common *common) +{ + struct ath_cycle_counters *cc = &common->cc_ani; + int32_t listen_time; + + listen_time = (cc->cycles - cc->rx_frame - cc->tx_frame) / + (common->clockrate * 1000); + + memset(cc, 0, sizeof(*cc)); + + return listen_time; +} +EXPORT_SYMBOL(ath_hw_get_listen_time); diff --git a/drivers/net/wireless/ath/reg.h b/drivers/net/wireless/ath/reg.h index e798ef476581..298e53f3fa48 100644 --- a/drivers/net/wireless/ath/reg.h +++ b/drivers/net/wireless/ath/reg.h @@ -17,6 +17,12 @@ #ifndef ATH_REGISTERS_H #define ATH_REGISTERS_H +#define AR_MIBC 0x0040 +#define AR_MIBC_COW 0x00000001 +#define AR_MIBC_FMC 0x00000002 +#define AR_MIBC_CMC 0x00000004 +#define AR_MIBC_MCS 0x00000008 + /* * BSSID mask registers. See ath_hw_set_bssid_mask() * for detailed documentation about these registers. @@ -24,6 +30,11 @@ #define AR_BSSMSKL 0x80e0 #define AR_BSSMSKU 0x80e4 +#define AR_TFCNT 0x80ec +#define AR_RFCNT 0x80f0 +#define AR_RCCNT 0x80f4 +#define AR_CCCNT 0x80f8 + #define AR_KEYTABLE_0 0x8800 #define AR_KEYTABLE(_n) (AR_KEYTABLE_0 + ((_n)*32)) #define AR_KEY_CACHE_SIZE 128 -- cgit v1.2.3 From 7109ca5c80a0bb94378ebd7f8bb6d00edb5e6fba Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 8 Oct 2010 22:13:54 +0200 Subject: ath5k: use the common cycle counter / listen time implementation Signed-off-by: Felix Fietkau Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/ani.c | 41 ++++++++++++---------------------- drivers/net/wireless/ath/ath5k/ani.h | 5 +---- drivers/net/wireless/ath/ath5k/debug.c | 21 ++++++++--------- 3 files changed, 26 insertions(+), 41 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/ani.c b/drivers/net/wireless/ath/ath5k/ani.c index e4a5f046bba4..f1419198a479 100644 --- a/drivers/net/wireless/ath/ath5k/ani.c +++ b/drivers/net/wireless/ath/ath5k/ani.c @@ -355,41 +355,28 @@ ath5k_ani_lower_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as) /** - * ath5k_hw_ani_get_listen_time() - Calculate time spent listening + * ath5k_hw_ani_get_listen_time() - Update counters and return listening time * * Return an approximation of the time spent "listening" in milliseconds (ms) - * since the last call of this function by deducting the cycles spent - * transmitting and receiving from the total cycle count. - * Save profile count values for debugging/statistics and because we might want - * to use them later. - * - * We assume no one else clears these registers! + * since the last call of this function. + * Save a snapshot of the counter values for debugging/statistics. */ static int ath5k_hw_ani_get_listen_time(struct ath5k_hw *ah, struct ath5k_ani_state *as) { + struct ath_common *common = ath5k_hw_common(ah); int listen; - /* freeze */ - ath5k_hw_reg_write(ah, AR5K_MIBC_FMC, AR5K_MIBC); - /* read */ - as->pfc_cycles = ath5k_hw_reg_read(ah, AR5K_PROFCNT_CYCLE); - as->pfc_busy = ath5k_hw_reg_read(ah, AR5K_PROFCNT_RXCLR); - as->pfc_tx = ath5k_hw_reg_read(ah, AR5K_PROFCNT_TX); - as->pfc_rx = ath5k_hw_reg_read(ah, AR5K_PROFCNT_RX); - /* clear */ - ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_TX); - ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_RX); - ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_RXCLR); - ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_CYCLE); - /* un-freeze */ - ath5k_hw_reg_write(ah, 0, AR5K_MIBC); - - /* TODO: where does 44000 come from? (11g clock rate?) */ - listen = (as->pfc_cycles - as->pfc_rx - as->pfc_tx) / 44000; - - if (as->pfc_cycles == 0 || listen < 0) - return 0; + spin_lock_bh(&common->cc_lock); + + ath_hw_cycle_counters_update(common); + memcpy(&as->last_cc, &common->cc_ani, sizeof(as->last_cc)); + + /* clears common->cc_ani */ + listen = ath_hw_get_listen_time(common); + + spin_unlock_bh(&common->cc_lock); + return listen; } diff --git a/drivers/net/wireless/ath/ath5k/ani.h b/drivers/net/wireless/ath/ath5k/ani.h index 55cf26d8522c..d0a664039c87 100644 --- a/drivers/net/wireless/ath/ath5k/ani.h +++ b/drivers/net/wireless/ath/ath5k/ani.h @@ -75,10 +75,7 @@ struct ath5k_ani_state { unsigned int cck_errors; /* debug/statistics only: numbers from last ANI calibration */ - unsigned int pfc_tx; - unsigned int pfc_rx; - unsigned int pfc_busy; - unsigned int pfc_cycles; + struct ath_cycle_counters last_cc; unsigned int last_listen; unsigned int last_ofdm_errors; unsigned int last_cck_errors; diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c index a342a9da1b3e..a3b217125331 100644 --- a/drivers/net/wireless/ath/ath5k/debug.c +++ b/drivers/net/wireless/ath/ath5k/debug.c @@ -725,20 +725,21 @@ static ssize_t read_file_ani(struct file *file, char __user *user_buf, len += snprintf(buf+len, sizeof(buf)-len, "beacon RSSI average:\t%d\n", sc->ah->ah_beacon_rssi_avg.avg); + +#define CC_PRINT(_struct, _field) \ + _struct._field, \ + _struct.cycles > 0 ? \ + _struct._field*100/_struct.cycles : 0 + len += snprintf(buf+len, sizeof(buf)-len, "profcnt tx\t\t%u\t(%d%%)\n", - as->pfc_tx, - as->pfc_cycles > 0 ? - as->pfc_tx*100/as->pfc_cycles : 0); + CC_PRINT(as->last_cc, tx_frame)); len += snprintf(buf+len, sizeof(buf)-len, "profcnt rx\t\t%u\t(%d%%)\n", - as->pfc_rx, - as->pfc_cycles > 0 ? - as->pfc_rx*100/as->pfc_cycles : 0); + CC_PRINT(as->last_cc, rx_frame)); len += snprintf(buf+len, sizeof(buf)-len, "profcnt busy\t\t%u\t(%d%%)\n", - as->pfc_busy, - as->pfc_cycles > 0 ? - as->pfc_busy*100/as->pfc_cycles : 0); + CC_PRINT(as->last_cc, rx_busy)); +#undef CC_PRINT len += snprintf(buf+len, sizeof(buf)-len, "profcnt cycles\t\t%u\n", - as->pfc_cycles); + as->last_cc.cycles); len += snprintf(buf+len, sizeof(buf)-len, "listen time\t\t%d\tlast: %d\n", as->listen_time, as->last_listen); -- cgit v1.2.3 From cac4220b2e93e6344f987581d52d5bd71ff2cc0e Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 9 Oct 2010 02:39:30 +0200 Subject: ath9k: add compile time checking for the size of the channel list This prevents random memory corruption if the number of channels ever gets changed without an update to the internal channel array size. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.h | 4 +++- drivers/net/wireless/ath/ath9k/init.c | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 7f696c82ca0a..6b9233472fbf 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -61,6 +61,8 @@ #define ATH9K_RSSI_BAD -128 +#define ATH9K_NUM_CHANNELS 38 + /* Register read/write primitives */ #define REG_WRITE(_ah, _reg, _val) \ ath9k_hw_common(_ah)->ops->write((_ah), (_val), (_reg)) @@ -618,7 +620,7 @@ struct ath_hw { struct ath9k_hw_version hw_version; struct ath9k_ops_config config; struct ath9k_hw_capabilities caps; - struct ath9k_channel channels[38]; + struct ath9k_channel channels[ATH9K_NUM_CHANNELS]; struct ath9k_channel *curchan; union { diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index d76003c06fe4..a4c5ed41b176 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -481,6 +481,10 @@ static int ath9k_init_channels_rates(struct ath_softc *sc) { void *channels; + BUILD_BUG_ON(ARRAY_SIZE(ath9k_2ghz_chantable) + + ARRAY_SIZE(ath9k_5ghz_chantable) != + ATH9K_NUM_CHANNELS); + if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) { channels = kmemdup(ath9k_2ghz_chantable, sizeof(ath9k_2ghz_chantable), GFP_KERNEL); -- cgit v1.2.3 From 3430098ae463e31ab16926ac3eb295368a3ca5d9 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 10 Oct 2010 18:21:52 +0200 Subject: ath9k: implement channel utilization stats for survey Results for the active channel are updated whenever a new survey dump is requested, the old data is kept to allow multiple processes to make their own channel utilization averages. All other channels only contain the data for the last time that the hardware was on the channel, i.e. the last scan result or other off-channel activity. Running a background scan does not clear the data for the active channel. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 2 + drivers/net/wireless/ath/ath9k/main.c | 112 ++++++++++++++++++++++++++++----- 2 files changed, 98 insertions(+), 16 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index de2b18ee7f77..4e81fe35aa3f 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -593,6 +593,8 @@ struct ath_softc { struct delayed_work wiphy_work; unsigned long wiphy_scheduler_int; int wiphy_scheduler_index; + struct survey_info *cur_survey; + struct survey_info survey[ATH9K_NUM_CHANNELS]; struct tasklet_struct intr_tq; struct tasklet_struct bcon_tasklet; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 360c6f5e843a..865649120075 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -175,6 +175,44 @@ static void ath_start_ani(struct ath_common *common) msecs_to_jiffies((u32)ah->config.ani_poll_interval)); } +static void ath_update_survey_nf(struct ath_softc *sc, int channel) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath9k_channel *chan = &ah->channels[channel]; + struct survey_info *survey = &sc->survey[channel]; + + if (chan->noisefloor) { + survey->filled |= SURVEY_INFO_NOISE_DBM; + survey->noise = chan->noisefloor; + } +} + +static void ath_update_survey_stats(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + int pos = ah->curchan - &ah->channels[0]; + struct survey_info *survey = &sc->survey[pos]; + struct ath_cycle_counters *cc = &common->cc_survey; + unsigned int div = common->clockrate * 1000; + + ath_hw_cycle_counters_update(common); + + if (cc->cycles > 0) { + survey->filled |= SURVEY_INFO_CHANNEL_TIME | + SURVEY_INFO_CHANNEL_TIME_BUSY | + SURVEY_INFO_CHANNEL_TIME_RX | + SURVEY_INFO_CHANNEL_TIME_TX; + survey->channel_time += cc->cycles / div; + survey->channel_time_busy += cc->rx_busy / div; + survey->channel_time_rx += cc->rx_frame / div; + survey->channel_time_tx += cc->tx_frame / div; + } + memset(cc, 0, sizeof(*cc)); + + ath_update_survey_nf(sc, pos); +} + /* * Set/change channels. If the channel is really being changed, it's done * by reseting the chip. To accomplish this we must first cleanup any pending @@ -453,6 +491,7 @@ void ath_ani_calibrate(unsigned long data) if (aniflag) { spin_lock_irqsave(&common->cc_lock, flags); ath9k_hw_ani_monitor(ah, ah->curchan); + ath_update_survey_stats(sc); spin_unlock_irqrestore(&common->cc_lock, flags); } @@ -1532,7 +1571,8 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; - struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &hw->conf; bool disable_radio; @@ -1598,6 +1638,11 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { struct ieee80211_channel *curchan = hw->conf.channel; int pos = curchan->hw_value; + int old_pos = -1; + unsigned long flags; + + if (ah->curchan) + old_pos = ah->curchan - &ah->channels[0]; aphy->chan_idx = pos; aphy->chan_is_ht = conf_is_ht(conf); @@ -1625,12 +1670,45 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) ath_update_chainmask(sc, conf_is_ht(conf)); + /* update survey stats for the old channel before switching */ + spin_lock_irqsave(&common->cc_lock, flags); + ath_update_survey_stats(sc); + spin_unlock_irqrestore(&common->cc_lock, flags); + + /* + * If the operating channel changes, change the survey in-use flags + * along with it. + * Reset the survey data for the new channel, unless we're switching + * back to the operating channel from an off-channel operation. + */ + if (!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) && + sc->cur_survey != &sc->survey[pos]) { + + if (sc->cur_survey) + sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE; + + sc->cur_survey = &sc->survey[pos]; + + memset(sc->cur_survey, 0, sizeof(struct survey_info)); + sc->cur_survey->filled |= SURVEY_INFO_IN_USE; + } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) { + memset(&sc->survey[pos], 0, sizeof(struct survey_info)); + } + if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) { ath_print(common, ATH_DBG_FATAL, "Unable to set channel\n"); mutex_unlock(&sc->mutex); return -EINVAL; } + + /* + * The most recent snapshot of channel->noisefloor for the old + * channel is only available after the hardware reset. Copy it to + * the survey stats now. + */ + if (old_pos >= 0) + ath_update_survey_nf(sc, old_pos); } skip_chan_change: @@ -2000,9 +2078,15 @@ static int ath9k_get_survey(struct ieee80211_hw *hw, int idx, { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; - struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ieee80211_supported_band *sband; - struct ath9k_channel *chan; + struct ieee80211_channel *chan; + unsigned long flags; + int pos; + + spin_lock_irqsave(&common->cc_lock, flags); + if (idx == 0) + ath_update_survey_stats(sc); sband = hw->wiphy->bands[IEEE80211_BAND_2GHZ]; if (sband && idx >= sband->n_channels) { @@ -2013,21 +2097,17 @@ static int ath9k_get_survey(struct ieee80211_hw *hw, int idx, if (!sband) sband = hw->wiphy->bands[IEEE80211_BAND_5GHZ]; - if (!sband || idx >= sband->n_channels) - return -ENOENT; - - survey->channel = &sband->channels[idx]; - chan = &ah->channels[survey->channel->hw_value]; - survey->filled = 0; - - if (chan == ah->curchan) - survey->filled |= SURVEY_INFO_IN_USE; - - if (chan->noisefloor) { - survey->filled |= SURVEY_INFO_NOISE_DBM; - survey->noise = chan->noisefloor; + if (!sband || idx >= sband->n_channels) { + spin_unlock_irqrestore(&common->cc_lock, flags); + return -ENOENT; } + chan = &sband->channels[idx]; + pos = chan->hw_value; + memcpy(survey, &sc->survey[pos], sizeof(*survey)); + survey->channel = chan; + spin_unlock_irqrestore(&common->cc_lock, flags); + return 0; } -- cgit v1.2.3 From c8a16c68ef4eb7817e41759c7105678ebc155377 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 9 Oct 2010 21:37:11 +0200 Subject: carl9170: common error path for bad frames This patch replaces several identical frame drop paths with a single shared rx frame error handler. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/rx.c | 48 ++++++++++++++-------------------- 1 file changed, 20 insertions(+), 28 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index 671dbc429547..31287e0d362f 100644 --- a/drivers/net/wireless/ath/carl9170/rx.c +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -598,18 +598,14 @@ static void carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len) if (!IS_STARTED(ar)) return; - if (unlikely(len < sizeof(*mac))) { - ar->rx_dropped++; - return; - } + if (unlikely(len < sizeof(*mac))) + goto drop; mpdu_len = len - sizeof(*mac); mac = (void *)(buf + mpdu_len); - if (unlikely(mac->error & AR9170_RX_ERROR_FATAL)) { - ar->rx_dropped++; - return; - } + if (unlikely(mac->error & AR9170_RX_ERROR_FATAL)) + goto drop; switch (mac->status & AR9170_RX_STATUS_MPDU) { case AR9170_RX_STATUS_MPDU_FIRST: @@ -638,8 +634,7 @@ static void carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len) "is clipped.\n"); } - ar->rx_dropped++; - return; + goto drop; } break; @@ -659,8 +654,7 @@ static void carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len) "is clipped.\n"); } - ar->rx_dropped++; - return; + goto drop; } case AR9170_RX_STATUS_MPDU_MIDDLE: @@ -672,8 +666,7 @@ static void carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len) wiphy_err(ar->hw->wiphy, "rx stream does not start " "with a first_mpdu frame tag.\n"); - ar->rx_dropped++; - return; + goto drop; } head = &ar->rx_plcp; @@ -696,16 +689,12 @@ static void carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len) } /* FC + DU + RA + FCS */ - if (unlikely(mpdu_len < (2 + 2 + 6 + FCS_LEN))) { - ar->rx_dropped++; - return; - } + if (unlikely(mpdu_len < (2 + 2 + ETH_ALEN + FCS_LEN))) + goto drop; memset(&status, 0, sizeof(status)); - if (unlikely(carl9170_rx_mac_status(ar, head, mac, &status))) { - ar->rx_dropped++; - return; - } + if (unlikely(carl9170_rx_mac_status(ar, head, mac, &status))) + goto drop; if (phy) carl9170_rx_phy_status(ar, phy, &status); @@ -713,12 +702,15 @@ static void carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len) carl9170_ps_beacon(ar, buf, mpdu_len); skb = carl9170_rx_copy_data(buf, mpdu_len); - if (likely(skb)) { - memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); - ieee80211_rx(ar->hw, skb); - } else { - ar->rx_dropped++; - } + if (!skb) + goto drop; + + memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); + ieee80211_rx(ar->hw, skb); + return; + +drop: + ar->rx_dropped++; } static void carl9170_rx_untie_cmds(struct ar9170 *ar, const u8 *respbuf, -- cgit v1.2.3 From 9f59f3c694c184c69e0be7d0fd0829bcb61b0429 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 9 Oct 2010 22:23:37 +0200 Subject: carl9170: remove stale rx error path The total/fatal error bit was erroneously prefixed with AR9170_RX_ERROR instead of AR9170_RX_STATUS. Luckily, the hardware specification confirmed that the 0x80 flag will never be set for mac->error. So, it was always just a dead branch. This patch also imports the latest version of shared wlan.h header from the firmware git. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/rx.c | 3 --- drivers/net/wireless/ath/carl9170/wlan.h | 14 +++++++++++--- 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index 31287e0d362f..256dd42bb4ab 100644 --- a/drivers/net/wireless/ath/carl9170/rx.c +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -604,9 +604,6 @@ static void carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len) mpdu_len = len - sizeof(*mac); mac = (void *)(buf + mpdu_len); - if (unlikely(mac->error & AR9170_RX_ERROR_FATAL)) - goto drop; - switch (mac->status & AR9170_RX_STATUS_MPDU) { case AR9170_RX_STATUS_MPDU_FIRST: /* Aggregated MPDUs start with an PLCP header */ diff --git a/drivers/net/wireless/ath/carl9170/wlan.h b/drivers/net/wireless/ath/carl9170/wlan.h index 48ead2268f50..24d63b583b6b 100644 --- a/drivers/net/wireless/ath/carl9170/wlan.h +++ b/drivers/net/wireless/ath/carl9170/wlan.h @@ -74,6 +74,9 @@ #define AR9170_RX_STATUS_MPDU_MIDDLE 0x30 #define AR9170_RX_STATUS_MPDU_LAST 0x10 +#define AR9170_RX_STATUS_CONT_AGGR 0x40 +#define AR9170_RX_STATUS_TOTAL_ERROR 0x80 + #define AR9170_RX_ERROR_RXTO 0x01 #define AR9170_RX_ERROR_OVERRUN 0x02 #define AR9170_RX_ERROR_DECRYPT 0x04 @@ -81,7 +84,6 @@ #define AR9170_RX_ERROR_WRONG_RA 0x10 #define AR9170_RX_ERROR_PLCP 0x20 #define AR9170_RX_ERROR_MMIC 0x40 -#define AR9170_RX_ERROR_FATAL 0x80 /* these are either-or */ #define AR9170_TX_MAC_PROT_RTS 0x0001 @@ -329,13 +331,15 @@ struct _carl9170_tx_superframe { #define CARL9170_TX_SUPERDESC_LEN 24 #define AR9170_TX_HWDESC_LEN 8 -#define AR9170_TX_SUPERFRAME_LEN (CARL9170_TX_HWDESC_LEN + \ - AR9170_TX_SUPERDESC_LEN) +#define CARL9170_TX_SUPERFRAME_LEN (CARL9170_TX_SUPERDESC_LEN + \ + AR9170_TX_HWDESC_LEN) struct ar9170_rx_head { u8 plcp[12]; } __packed; +#define AR9170_RX_HEAD_LEN 12 + struct ar9170_rx_phystatus { union { struct { @@ -350,12 +354,16 @@ struct ar9170_rx_phystatus { u8 phy_err; } __packed; +#define AR9170_RX_PHYSTATUS_LEN 20 + struct ar9170_rx_macstatus { u8 SAidx, DAidx; u8 error; u8 status; } __packed; +#define AR9170_RX_MACSTATUS_LEN 4 + struct ar9170_rx_frame_single { struct ar9170_rx_head phy_head; struct ieee80211_hdr i3e; -- cgit v1.2.3 From 8f236d1bef659ca69c912536a69b3031e5ba3269 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sun, 10 Oct 2010 01:15:07 +0200 Subject: carl9170: A-MPDU frame type filter In the past, carl9170 has been plagued by mysterious ghosts. e.g.: wlan4: deauthenticated from 02:04:d8:3c:ac:c1 (Reason: 0) Apparently, the AP sent us a bogus deauthentication notification. But upon closer inspection the "management frame" turned out to be a corrupted scrap of an unsuccessful A-MPDU. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/rx.c | 42 +++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index 256dd42bb4ab..939a0e96ed1f 100644 --- a/drivers/net/wireless/ath/carl9170/rx.c +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -576,6 +576,41 @@ static void carl9170_ps_beacon(struct ar9170 *ar, void *data, unsigned int len) } } +static bool carl9170_ampdu_check(struct ar9170 *ar, u8 *buf, u8 ms) +{ + __le16 fc; + + if ((ms & AR9170_RX_STATUS_MPDU) == AR9170_RX_STATUS_MPDU_SINGLE) { + /* + * This frame is not part of an aMPDU. + * Therefore it is not subjected to any + * of the following content restrictions. + */ + return true; + } + + /* + * "802.11n - 7.4a.3 A-MPDU contents" describes in which contexts + * certain frame types can be part of an aMPDU. + * + * In order to keep the processing cost down, I opted for a + * stateless filter solely based on the frame control field. + */ + + fc = ((struct ieee80211_hdr *)buf)->frame_control; + if (ieee80211_is_data_qos(fc) && ieee80211_is_data_present(fc)) + return true; + + if (ieee80211_is_ack(fc) || ieee80211_is_back(fc) || + ieee80211_is_back_req(fc)) + return true; + + if (ieee80211_is_action(fc)) + return true; + + return false; +} + /* * If the frame alignment is right (or the kernel has * CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS), and there @@ -594,6 +629,7 @@ static void carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len) struct ieee80211_rx_status status; struct sk_buff *skb; int mpdu_len; + u8 mac_status; if (!IS_STARTED(ar)) return; @@ -604,7 +640,8 @@ static void carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len) mpdu_len = len - sizeof(*mac); mac = (void *)(buf + mpdu_len); - switch (mac->status & AR9170_RX_STATUS_MPDU) { + mac_status = mac->status; + switch (mac_status & AR9170_RX_STATUS_MPDU) { case AR9170_RX_STATUS_MPDU_FIRST: /* Aggregated MPDUs start with an PLCP header */ if (likely(mpdu_len >= sizeof(struct ar9170_rx_head))) { @@ -693,6 +730,9 @@ static void carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len) if (unlikely(carl9170_rx_mac_status(ar, head, mac, &status))) goto drop; + if (!carl9170_ampdu_check(ar, buf, mac_status)) + goto drop; + if (phy) carl9170_rx_phy_status(ar, phy, &status); -- cgit v1.2.3 From ebd022873aa61937603d2c4dfea19ce63ea1a3c8 Mon Sep 17 00:00:00 2001 From: Björn Smedman Date: Sun, 10 Oct 2010 22:44:39 +0200 Subject: ath9k: A-MPDU rate control info fix This patch fixes the following problems with the rate control feedback generated by ath9k for A-MPDU frames: 1. Rate control feedback is carried on the first frame of an aggregate that is either ACKed, or has execeeded the software retry count and is considered failed. However, ath9k would incorrectly assume the aggregate had the length 1 if one of these conditions did not apply to the first frame of the aggregate, but instead a later frame. This fix therefor copies the bf_nframes field of the buffer in the same manner as the rates field of the tx status. 2. Sometimes the ampdu_len and ampdu_ack_len fields of the tx status was left uninitialized eventhough the IEEE80211_TX_STAT_AMPDU flag was set. This is now avoid by setting flag and fields in the same place. 3. Even if a frame has been selected for aggregation by mac80211 and marked with the IEEE80211_TX_CTL_AMPDU flag it can sometimes happen that ath9k transmits the frame without aggregation. In these cases the ampdu_ack_len field could be incorrectly computed because the nbad parameter to ath_tx_rc_status was incorrect. Cc: Signed-off-by: Björn Smedman Acked-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/xmit.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index aa447770eb2b..9a11099dd86a 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -317,6 +317,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, int isaggr, txfail, txpending, sendbar = 0, needreset = 0, nbad = 0; bool rc_update = true; struct ieee80211_tx_rate rates[4]; + int nframes; skb = bf->bf_mpdu; hdr = (struct ieee80211_hdr *)skb->data; @@ -325,6 +326,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, hw = bf->aphy->hw; memcpy(rates, tx_info->control.rates, sizeof(rates)); + nframes = bf->bf_nframes; rcu_read_lock(); @@ -341,7 +343,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, !bf->bf_stale || bf_next != NULL) list_move_tail(&bf->list, &bf_head); - ath_tx_rc_status(bf, ts, 0, 0, false); + ath_tx_rc_status(bf, ts, 1, 0, false); ath_tx_complete_buf(sc, bf, txq, &bf_head, ts, 0, 0); @@ -446,6 +448,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, if (rc_update && (acked_cnt == 1 || txfail_cnt == 1)) { memcpy(tx_info->control.rates, rates, sizeof(rates)); + bf->bf_nframes = nframes; ath_tx_rc_status(bf, ts, nbad, txok, true); rc_update = false; } else { @@ -1979,9 +1982,15 @@ static void ath_tx_rc_status(struct ath_buf *bf, struct ath_tx_status *ts, if (ts->ts_status & ATH9K_TXERR_FILT) tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED; - if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && update_rc) + if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && update_rc) { tx_info->flags |= IEEE80211_TX_STAT_AMPDU; + BUG_ON(nbad > bf->bf_nframes); + + tx_info->status.ampdu_len = bf->bf_nframes; + tx_info->status.ampdu_ack_len = bf->bf_nframes - nbad; + } + if ((ts->ts_status & ATH9K_TXERR_FILT) == 0 && (bf->bf_flags & ATH9K_TXDESC_NOACK) == 0 && update_rc) { if (ieee80211_is_data(hdr->frame_control)) { @@ -1991,8 +2000,6 @@ static void ath_tx_rc_status(struct ath_buf *bf, struct ath_tx_status *ts, if ((ts->ts_status & ATH9K_TXERR_XRETRY) || (ts->ts_status & ATH9K_TXERR_FIFO)) tx_info->pad[0] |= ATH_TX_INFO_XRETRY; - tx_info->status.ampdu_len = bf->bf_nframes; - tx_info->status.ampdu_ack_len = bf->bf_nframes - nbad; } } @@ -2102,7 +2109,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) */ if (ts.ts_status & ATH9K_TXERR_XRETRY) bf->bf_state.bf_type |= BUF_XRETRY; - ath_tx_rc_status(bf, &ts, 0, txok, true); + ath_tx_rc_status(bf, &ts, txok ? 0 : 1, txok, true); } if (bf_isampdu(bf)) @@ -2220,7 +2227,7 @@ void ath_tx_edma_tasklet(struct ath_softc *sc) if (!bf_isampdu(bf)) { if (txs.ts_status & ATH9K_TXERR_XRETRY) bf->bf_state.bf_type |= BUF_XRETRY; - ath_tx_rc_status(bf, &txs, 0, txok, true); + ath_tx_rc_status(bf, &txs, txok ? 0 : 1, txok, true); } if (bf_isampdu(bf)) -- cgit v1.2.3 From a8909cfb1832ac623142898df2a9374722cfe68f Mon Sep 17 00:00:00 2001 From: Björn Smedman Date: Sun, 10 Oct 2010 22:51:54 +0200 Subject: ath9k: built-in rate control A-MPDU fix This patch attempts to ensure that ath9k's built-in rate control algorithm does not rely on the value of the ampdu_len and ampdu_ack_len tx status fields unless the IEEE80211_TX_STAT_AMPDU flag is set. This patch has not been tested. Cc: Signed-off-by: Björn Smedman Acked-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/rc.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index ce1cd6d85847..13f9e88e1187 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -1375,6 +1375,12 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband, if (tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) return; + if (!(tx_info->flags & IEEE80211_TX_STAT_AMPDU)) { + tx_info->status.ampdu_ack_len = + (tx_info->flags & IEEE80211_TX_STAT_ACK ? 1 : 0); + tx_info->status.ampdu_len = 1; + } + /* * If an underrun error is seen assume it as an excessive retry only * if max frame trigger level has been reached (2 KB for singel stream, -- cgit v1.2.3 From 908ebfb95d16bdf7f5f37ad911ccd9b7350ba780 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 12 Oct 2010 11:07:44 -0700 Subject: ath5k: fix build break from "ath5k: Print out opmode in debugfs" Also improve ath_opmode_to_string usage by having it return UNKNOWN rather than NULL in the event of failure to map the opmode value to a representative string. Signed-off-by: Joe Perches Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 4 +--- drivers/net/wireless/ath/ath5k/debug.c | 16 ++++------------ drivers/net/wireless/ath/debug.c | 2 +- drivers/net/wireless/ath/debug.h | 7 +++++++ 4 files changed, 13 insertions(+), 16 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index c9732a6ce87f..7baaf04f092d 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -563,9 +563,7 @@ static void ath_do_set_opmode(struct ath5k_softc *sc) struct ath5k_hw *ah = sc->ah; ath5k_hw_set_opmode(ah, sc->opmode); ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "mode setup opmode %d (%s)\n", - sc->opmode, - ath_opmode_to_string(sc->opmode) ? - ath_opmode_to_string(sc->opmode) : "UKNOWN"); + sc->opmode, ath_opmode_to_string(sc->opmode)); } void ath5k_update_bssid_mask_and_opmode(struct ath5k_softc *sc, diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c index a3b217125331..7c77e5b7c9af 100644 --- a/drivers/net/wireless/ath/ath5k/debug.c +++ b/drivers/net/wireless/ath/ath5k/debug.c @@ -493,7 +493,6 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf, char buf[700]; unsigned int len = 0; u32 filt = ath5k_hw_get_rx_filter(sc->ah); - const char *tmp; len += snprintf(buf+len, sizeof(buf)-len, "bssid-mask: %pM\n", sc->bssidmask); @@ -522,17 +521,10 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf, if (filt & AR5K_RX_FILTER_PHYERR_5211) snprintf(buf+len, sizeof(buf)-len, " PHYERR-5211"); if (filt & AR5K_RX_FILTER_RADARERR_5211) - len += snprintf(buf+len, sizeof(buf)-len, " RADARERR-5211\n"); - else - len += snprintf(buf+len, sizeof(buf)-len, "\n"); - - tmp = ath_opmode_to_string(sc->opmode); - if (tmp) - len += snprintf(buf+len, sizeof(buf)-len, "opmode: %s\n", - tmp); - else - len += snprintf(buf+len, sizeof(buf)-len, - "opmode: UNKNOWN-%i\n", sc->opmode); + len += snprintf(buf+len, sizeof(buf)-len, " RADARERR-5211"); + + len += snprintf(buf+len, sizeof(buf)-len, "\nopmode: %s (%d)\n", + ath_opmode_to_string(sc->opmode), sc->opmode); if (len > sizeof(buf)) len = sizeof(buf); diff --git a/drivers/net/wireless/ath/debug.c b/drivers/net/wireless/ath/debug.c index a9eb7876dbe1..dacfb234f491 100644 --- a/drivers/net/wireless/ath/debug.c +++ b/drivers/net/wireless/ath/debug.c @@ -55,7 +55,7 @@ const char *ath_opmode_to_string(enum nl80211_iftype opmode) case NL80211_IFTYPE_P2P_GO: return "P2P-GO"; default: - return NULL; + return "UNKNOWN"; } } EXPORT_SYMBOL(ath_opmode_to_string); diff --git a/drivers/net/wireless/ath/debug.h b/drivers/net/wireless/ath/debug.h index a3a5a628d24d..64e4af2c2887 100644 --- a/drivers/net/wireless/ath/debug.h +++ b/drivers/net/wireless/ath/debug.h @@ -78,6 +78,13 @@ ath_print(struct ath_common *common, int dbg_mask, const char *fmt, ...) #endif /* CONFIG_ATH_DEBUG */ /** Returns string describing opmode, or NULL if unknown mode. */ +#ifdef CONFIG_ATH_DEBUG const char *ath_opmode_to_string(enum nl80211_iftype opmode); +#else +static inline const char *ath_opmode_to_string(enum nl80211_iftype opmode) +{ + return "UNKNOWN"; +} +#endif #endif /* ATH_DEBUG_H */ -- cgit v1.2.3 From d84a35d1323bc62f9b26a707072767a60da75015 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Tue, 12 Oct 2010 10:55:38 -0700 Subject: ath5k: Move debugfs under ieee80211/[wiphy-name] This automatically keeps things proper when wiphy is renamed. Based on patch by Johannes Berg Signed-off-by: Ben Greear Acked-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 7 +------ drivers/net/wireless/ath/ath5k/debug.c | 20 ++------------------ drivers/net/wireless/ath/ath5k/debug.h | 12 ------------ 3 files changed, 3 insertions(+), 36 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 7baaf04f092d..f1ae75d35d5d 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -3541,8 +3541,6 @@ ath5k_pci_probe(struct pci_dev *pdev, sc->hw = hw; sc->pdev = pdev; - ath5k_debug_init_device(sc); - /* * Mark the device as detached to avoid processing * interrupts until setup is complete. @@ -3650,6 +3648,7 @@ ath5k_pci_probe(struct pci_dev *pdev, } } + ath5k_debug_init_device(sc); /* ready to process interrupts */ __clear_bit(ATH_STAT_INVALID, sc->status); @@ -3736,8 +3735,6 @@ init_ath5k_pci(void) { int ret; - ath5k_debug_init(); - ret = pci_register_driver(&ath5k_pci_driver); if (ret) { printk(KERN_ERR "ath5k_pci: can't register pci driver\n"); @@ -3751,8 +3748,6 @@ static void __exit exit_ath5k_pci(void) { pci_unregister_driver(&ath5k_pci_driver); - - ath5k_debug_finish(); } module_init(init_ath5k_pci); diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c index 7c77e5b7c9af..42ea5b1bdb12 100644 --- a/drivers/net/wireless/ath/ath5k/debug.c +++ b/drivers/net/wireless/ath/ath5k/debug.c @@ -72,8 +72,6 @@ module_param_named(debug, ath5k_debug, uint, 0); #include "reg.h" #include "ani.h" -static struct dentry *ath5k_global_debugfs; - static int ath5k_debugfs_open(struct inode *inode, struct file *file) { file->private_data = inode->i_private; @@ -882,21 +880,13 @@ static const struct file_operations fops_queue = { }; -/* init */ - -void -ath5k_debug_init(void) -{ - ath5k_global_debugfs = debugfs_create_dir("ath5k", NULL); -} - void ath5k_debug_init_device(struct ath5k_softc *sc) { sc->debug.level = ath5k_debug; - sc->debug.debugfs_phydir = debugfs_create_dir(wiphy_name(sc->hw->wiphy), - ath5k_global_debugfs); + sc->debug.debugfs_phydir = debugfs_create_dir("ath5k", + sc->hw->wiphy->debugfsdir); sc->debug.debugfs_debug = debugfs_create_file("debug", S_IWUSR | S_IRUSR, @@ -936,12 +926,6 @@ ath5k_debug_init_device(struct ath5k_softc *sc) &fops_queue); } -void -ath5k_debug_finish(void) -{ - debugfs_remove(ath5k_global_debugfs); -} - void ath5k_debug_finish_device(struct ath5k_softc *sc) { diff --git a/drivers/net/wireless/ath/ath5k/debug.h b/drivers/net/wireless/ath/ath5k/debug.h index 4f078b134015..236edbd2507d 100644 --- a/drivers/net/wireless/ath/ath5k/debug.h +++ b/drivers/net/wireless/ath/ath5k/debug.h @@ -137,15 +137,9 @@ enum ath5k_debug_level { __func__, __LINE__, ##__VA_ARGS__); \ } while (0) -void -ath5k_debug_init(void); - void ath5k_debug_init_device(struct ath5k_softc *sc); -void -ath5k_debug_finish(void); - void ath5k_debug_finish_device(struct ath5k_softc *sc); @@ -173,15 +167,9 @@ static inline void __attribute__ ((format (printf, 3, 4))) ATH5K_DBG_UNLIMIT(struct ath5k_softc *sc, unsigned int m, const char *fmt, ...) {} -static inline void -ath5k_debug_init(void) {} - static inline void ath5k_debug_init_device(struct ath5k_softc *sc) {} -static inline void -ath5k_debug_finish(void) {} - static inline void ath5k_debug_finish_device(struct ath5k_softc *sc) {} -- cgit v1.2.3 From 7a8266524af7ec70d1b3d10f964ce911d922d466 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Wed, 13 Oct 2010 11:47:09 +0530 Subject: ath9k: Fix documentation in rate control This fix updates the documenation in Rate Control Table structure Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/rc.c | 2 +- drivers/net/wireless/ath/ath9k/rc.h | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index 13f9e88e1187..abebf2466a13 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -302,7 +302,7 @@ static const struct ath_rate_table ar5416_11ng_ratetable = { [64] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 243000, 205100, 20, 20, 8, 64, 65, 65 }, /* 243 Mb */ [65] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS_HGI, 270000, - 224700, 20, 20, 8, 64, 65, 65 }, /* 170 Mb */ + 224700, 20, 20, 8, 64, 65, 65 }, /* 270 Mb */ [66] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 324000, 263100, 21, 21, 8, 66, 67, 67 }, /* 324 Mb */ [67] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 360000, diff --git a/drivers/net/wireless/ath/ath9k/rc.h b/drivers/net/wireless/ath/ath9k/rc.h index 268072fd3c1c..fbb1d33ee4b7 100644 --- a/drivers/net/wireless/ath/ath9k/rc.h +++ b/drivers/net/wireless/ath/ath9k/rc.h @@ -135,20 +135,21 @@ enum { /** * struct ath_rate_table - Rate Control table - * @valid: valid for use in rate control - * @valid_single_stream: valid for use in rate control for - * single stream operation - * @phy: CCK/OFDM + * @rate_cnt: total number of rates for the given wireless mode + * @mcs_start: MCS rate index offset + * @rate_flags: Rate Control flags + * @phy: CCK/OFDM/HT20/HT40 * @ratekbps: rate in Kbits per second * @user_ratekbps: user rate in Kbits per second * @ratecode: rate that goes into HW descriptors - * @short_preamble: Mask for enabling short preamble in ratecode for CCK * @dot11rate: value that goes into supported * rates info element of MLME * @ctrl_rate: Index of next lower basic rate, used for duration computation - * @max_4ms_framelen: maximum frame length(bytes) for tx duration + * @cw40index: Index of rates having 40MHz channel width + * @sgi_index: Index of rates having Short Guard Interval + * @ht_index: high throughput rates having 40MHz channel width and + * Short Guard Interval * @probe_interval: interval for rate control to probe for other rates - * @rssi_reduce_interval: interval for rate control to reduce rssi * @initial_ratemax: initial ratemax value */ struct ath_rate_table { -- cgit v1.2.3 From 9c1d8e4affe6748d884a677cf5db19ae0c20ef07 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Wed, 13 Oct 2010 17:29:31 +0300 Subject: ath9k: Set RX filter for Probe Request based on filter flag This allows mac80211 to enable receiving of Probe Request frames in station mode which is needed for P2P. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 1 + drivers/net/wireless/ath/ath9k/recv.c | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 865649120075..bcd3892dca8d 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1739,6 +1739,7 @@ skip_chan_change: FIF_PSPOLL | \ FIF_OTHER_BSS | \ FIF_BCN_PRBRESP_PROMISC | \ + FIF_PROBE_REQ | \ FIF_FCSFAIL) /* FIXME: sc->sc_full_reset ? */ diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 9c166f3804ab..7c90eaf9ec55 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -430,8 +430,7 @@ u32 ath_calcrxfilter(struct ath_softc *sc) | ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST | ATH9K_RX_FILTER_MCAST; - /* If not a STA, enable processing of Probe Requests */ - if (sc->sc_ah->opmode != NL80211_IFTYPE_STATION) + if (sc->rx.rxfilter & FIF_PROBE_REQ) rfilt |= ATH9K_RX_FILTER_PROBEREQ; /* -- cgit v1.2.3 From c23cc81a5e5c1d4486b662cedd0afcdf9145f154 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Wed, 13 Oct 2010 12:01:23 -0700 Subject: ath9k: Fix potential use-after-free. The ath_debug_stat_tx references bf->bf_mpdu, which is the skb consumed by ath_tx_complete. So, call the ath_debug_stat_tx method first. Signed-off-by: Ben Greear Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/xmit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 9a11099dd86a..9b17108adc7c 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1924,8 +1924,8 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, else complete(&sc->paprd_complete); } else { - ath_tx_complete(sc, skb, bf->aphy, tx_flags); ath_debug_stat_tx(sc, txq, bf, ts); + ath_tx_complete(sc, skb, bf->aphy, tx_flags); } /* -- cgit v1.2.3 From 898c914a0871ea7e5557b77156d4358c0f15d98a Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 12 Oct 2010 14:02:53 +0200 Subject: ath9k: do not track cycle counter updates in powersave mode While the chip is in powersave mode, the cycle counter updates do not contain useful values. While the chip is in full sleep, the rx_clear signal stays high, indicating a busy medium. To ensure sane values, update cycle counters before going into powersave, and clear them right after switching back to awake. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index bcd3892dca8d..74ff51d4c339 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -121,6 +121,7 @@ bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode) void ath9k_ps_wakeup(struct ath_softc *sc) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); unsigned long flags; spin_lock_irqsave(&sc->sc_pm_lock, flags); @@ -129,18 +130,33 @@ void ath9k_ps_wakeup(struct ath_softc *sc) ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); + /* + * While the hardware is asleep, the cycle counters contain no + * useful data. Better clear them now so that they don't mess up + * survey data results. + */ + spin_lock(&common->cc_lock); + ath_hw_cycle_counters_update(common); + memset(&common->cc_survey, 0, sizeof(common->cc_survey)); + spin_unlock(&common->cc_lock); + unlock: spin_unlock_irqrestore(&sc->sc_pm_lock, flags); } void ath9k_ps_restore(struct ath_softc *sc) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); unsigned long flags; spin_lock_irqsave(&sc->sc_pm_lock, flags); if (--sc->ps_usecount != 0) goto unlock; + spin_lock(&common->cc_lock); + ath_hw_cycle_counters_update(common); + spin_unlock(&common->cc_lock); + if (sc->ps_idle) ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP); else if (sc->ps_enabled && @@ -196,7 +212,8 @@ static void ath_update_survey_stats(struct ath_softc *sc) struct ath_cycle_counters *cc = &common->cc_survey; unsigned int div = common->clockrate * 1000; - ath_hw_cycle_counters_update(common); + if (ah->power_mode == ATH9K_PM_AWAKE) + ath_hw_cycle_counters_update(common); if (cc->cycles > 0) { survey->filled |= SURVEY_INFO_CHANNEL_TIME | -- cgit v1.2.3 From e49f913750bb8745085cc4c547912c330cd3eacb Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 12 Oct 2010 16:08:01 +0200 Subject: ath9k_hw: fix division by zero in the ANI monitor code The commit "ath9k_hw: remove code duplication in phy error counter handling" split off some duplicate code into a separate function, but did not have a return code for aborting ANI processing based on counter values. This introduced a divide by zero issue. This patch adds the missing return code check in ath9k_hw_ani_monitor Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 3aa8fb1ad77f..9297f574b71c 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -633,7 +633,7 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) REGWRITE_BUFFER_FLUSH(ah); } -static void ath9k_hw_ani_read_counters(struct ath_hw *ah) +static bool ath9k_hw_ani_read_counters(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); struct ar5416AniState *aniState = &ah->curchan->ani; @@ -646,10 +646,10 @@ static void ath9k_hw_ani_read_counters(struct ath_hw *ah) ath_hw_cycle_counters_update(common); listenTime = ath_hw_get_listen_time(common); - if (listenTime < 0) { + if (listenTime <= 0) { ah->stats.ast_ani_lneg++; ath9k_ani_restart(ah); - return; + return false; } if (!use_new_ani(ah)) { @@ -683,7 +683,7 @@ static void ath9k_hw_ani_read_counters(struct ath_hw *ah) REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); } - return; + return false; } ofdmPhyErrCnt = phyCnt1 - ofdm_base; @@ -695,7 +695,7 @@ static void ath9k_hw_ani_read_counters(struct ath_hw *ah) ah->stats.ast_ani_cckerrs += cckPhyErrCnt - aniState->cckPhyErrCount; aniState->cckPhyErrCount = cckPhyErrCnt; - + return true; } void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan) @@ -711,7 +711,8 @@ void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan) if (WARN_ON(!aniState)) return; - ath9k_hw_ani_read_counters(ah); + if (!ath9k_hw_ani_read_counters(ah)) + return; ofdmPhyErrRate = aniState->ofdmPhyErrCount * 1000 / aniState->listenTime; -- cgit v1.2.3 From 431c74821432a6078d54f08de7cd561f8ef8f1ba Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 12 Oct 2010 16:08:02 +0200 Subject: ath9k_hw: fix PHY counter overflow handling in ANI v1 PHY counter overflows need to be checked for the old ANI version, because of its use of interrupt based counter overflow reports when the counters exceed the configured thresholds. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 9297f574b71c..63ccb39cdcd4 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -664,7 +664,7 @@ static bool ath9k_hw_ani_read_counters(struct ath_hw *ah) phyCnt1 = REG_READ(ah, AR_PHY_ERR_1); phyCnt2 = REG_READ(ah, AR_PHY_ERR_2); - if (use_new_ani(ah) && (phyCnt1 < ofdm_base || phyCnt2 < cck_base)) { + if (!use_new_ani(ah) && (phyCnt1 < ofdm_base || phyCnt2 < cck_base)) { if (phyCnt1 < ofdm_base) { ath_print(common, ATH_DBG_ANI, "phyCnt1 0x%x, resetting " -- cgit v1.2.3 From 88eac2dad876a58b9c6a4c4805c3fc27b02c048f Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 12 Oct 2010 16:08:03 +0200 Subject: ath9k: add missing locking around ath9k_hw_proc_mib_event ath9k_hw_proc_mib_event updates the cycle counters, so it common->cc_lock must be acquired. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 74ff51d4c339..2c7f28e022b8 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -779,7 +779,9 @@ irqreturn_t ath_isr(int irq, void *dev) * it will clear whatever condition caused * the interrupt. */ + spin_lock(&common->cc_lock); ath9k_hw_proc_mib_event(ah); + spin_unlock(&common->cc_lock); ath9k_hw_set_interrupts(ah, ah->imask); } -- cgit v1.2.3 From 772d5515635fef5bc7a9d0efee785b58b0181ee5 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 13 Oct 2010 16:43:26 +0200 Subject: ath9k: make rate control debugfs stats per station Move them to the same debugfs file that the other rc modules use. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/debug.c | 93 -------------------------- drivers/net/wireless/ath/ath9k/debug.h | 21 ------ drivers/net/wireless/ath/ath9k/rc.c | 115 ++++++++++++++++++++++++++++++++- drivers/net/wireless/ath/ath9k/rc.h | 11 ++++ 4 files changed, 124 insertions(+), 116 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 74a4570dc87f..7f764e3d1c0a 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -378,95 +378,6 @@ static const struct file_operations fops_interrupt = { .owner = THIS_MODULE }; -void ath_debug_stat_rc(struct ath_softc *sc, int final_rate) -{ - struct ath_rc_stats *stats; - - stats = &sc->debug.stats.rcstats[final_rate]; - stats->success++; -} - -void ath_debug_stat_retries(struct ath_softc *sc, int rix, - int xretries, int retries, u8 per) -{ - struct ath_rc_stats *stats = &sc->debug.stats.rcstats[rix]; - - stats->xretries += xretries; - stats->retries += retries; - stats->per = per; -} - -static ssize_t read_file_rcstat(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - char *buf; - unsigned int len = 0, max; - int i = 0; - ssize_t retval; - - if (sc->cur_rate_table == NULL) - return 0; - - max = 80 + sc->cur_rate_table->rate_cnt * 1024 + 1; - buf = kmalloc(max, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - len += sprintf(buf, "%6s %6s %6s " - "%10s %10s %10s %10s\n", - "HT", "MCS", "Rate", - "Success", "Retries", "XRetries", "PER"); - - for (i = 0; i < sc->cur_rate_table->rate_cnt; i++) { - u32 ratekbps = sc->cur_rate_table->info[i].ratekbps; - struct ath_rc_stats *stats = &sc->debug.stats.rcstats[i]; - char mcs[5]; - char htmode[5]; - int used_mcs = 0, used_htmode = 0; - - if (WLAN_RC_PHY_HT(sc->cur_rate_table->info[i].phy)) { - used_mcs = snprintf(mcs, 5, "%d", - sc->cur_rate_table->info[i].ratecode); - - if (WLAN_RC_PHY_40(sc->cur_rate_table->info[i].phy)) - used_htmode = snprintf(htmode, 5, "HT40"); - else if (WLAN_RC_PHY_20(sc->cur_rate_table->info[i].phy)) - used_htmode = snprintf(htmode, 5, "HT20"); - else - used_htmode = snprintf(htmode, 5, "????"); - } - - mcs[used_mcs] = '\0'; - htmode[used_htmode] = '\0'; - - len += snprintf(buf + len, max - len, - "%6s %6s %3u.%d: " - "%10u %10u %10u %10u\n", - htmode, - mcs, - ratekbps / 1000, - (ratekbps % 1000) / 100, - stats->success, - stats->retries, - stats->xretries, - stats->per); - } - - if (len > max) - len = max; - - retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - return retval; -} - -static const struct file_operations fops_rcstat = { - .read = read_file_rcstat, - .open = ath9k_debugfs_open, - .owner = THIS_MODULE -}; - static const char * ath_wiphy_state_str(enum ath_wiphy_state state) { switch (state) { @@ -977,10 +888,6 @@ int ath9k_init_debug(struct ath_hw *ah) sc, &fops_interrupt)) goto err; - if (!debugfs_create_file("rcstat", S_IRUSR, sc->debug.debugfs_phy, - sc, &fops_rcstat)) - goto err; - if (!debugfs_create_file("wiphy", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, sc, &fops_wiphy)) goto err; diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 822b6f3f23c5..bb0823242ba0 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -80,13 +80,6 @@ struct ath_interrupt_stats { u32 bb_watchdog; }; -struct ath_rc_stats { - u32 success; - u32 retries; - u32 xretries; - u8 per; -}; - /** * struct ath_tx_stats - Statistics about TX * @tx_pkts_all: No. of total frames transmitted, including ones that @@ -160,7 +153,6 @@ struct ath_rx_stats { struct ath_stats { struct ath_interrupt_stats istats; - struct ath_rc_stats rcstats[RATE_TABLE_SIZE]; struct ath_tx_stats txstats[ATH9K_NUM_TX_QUEUES]; struct ath_rx_stats rxstats; }; @@ -177,12 +169,9 @@ void ath9k_exit_debug(struct ath_hw *ah); int ath9k_debug_create_root(void); void ath9k_debug_remove_root(void); void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status); -void ath_debug_stat_rc(struct ath_softc *sc, int final_rate); void ath_debug_stat_tx(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf, struct ath_tx_status *ts); void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs); -void ath_debug_stat_retries(struct ath_softc *sc, int rix, - int xretries, int retries, u8 per); #else @@ -209,11 +198,6 @@ static inline void ath_debug_stat_interrupt(struct ath_softc *sc, { } -static inline void ath_debug_stat_rc(struct ath_softc *sc, - int final_rate) -{ -} - static inline void ath_debug_stat_tx(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf, @@ -226,11 +210,6 @@ static inline void ath_debug_stat_rx(struct ath_softc *sc, { } -static inline void ath_debug_stat_retries(struct ath_softc *sc, int rix, - int xretries, int retries, u8 per) -{ -} - #endif /* CONFIG_ATH9K_DEBUGFS */ #endif /* DEBUG_H */ diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index abebf2466a13..5db3a12356ec 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -1026,6 +1026,16 @@ static bool ath_rc_update_per(struct ath_softc *sc, return state_change; } +static void ath_debug_stat_retries(struct ath_rate_priv *rc, int rix, + int xretries, int retries, u8 per) +{ + struct ath_rc_stats *stats = &rc->rcstats[rix]; + + stats->xretries += xretries; + stats->retries += retries; + stats->per = per; +} + /* Update PER, RSSI and whatever else that the code thinks it is doing. If you can make sense of all this, you really need to go out more. */ @@ -1098,7 +1108,7 @@ static void ath_rc_update_ht(struct ath_softc *sc, ath_rc_priv->per_down_time = now_msec; } - ath_debug_stat_retries(sc, tx_rate, xretries, retries, + ath_debug_stat_retries(ath_rc_priv, tx_rate, xretries, retries, ath_rc_priv->per[tx_rate]); } @@ -1294,6 +1304,7 @@ static void ath_rc_init(struct ath_softc *sc, ath_rc_sort_validrates(rate_table, ath_rc_priv); ath_rc_priv->rate_max_phy = ath_rc_priv->valid_rate_index[k-4]; sc->cur_rate_table = rate_table; + ath_rc_priv->rate_table = rate_table; ath_print(common, ATH_DBG_CONFIG, "RC Initialized with capabilities: 0x%x\n", @@ -1340,6 +1351,15 @@ static bool ath_tx_aggr_check(struct ath_softc *sc, struct ath_node *an, /* mac80211 Rate Control callbacks */ /***********************************/ +static void ath_debug_stat_rc(struct ath_rate_priv *rc, int final_rate) +{ + struct ath_rc_stats *stats; + + stats = &rc->rcstats[final_rate]; + stats->success++; +} + + static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta, struct sk_buff *skb) @@ -1419,7 +1439,7 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband, } } - ath_debug_stat_rc(sc, ath_rc_get_rateindex(sc->cur_rate_table, + ath_debug_stat_rc(ath_rc_priv, ath_rc_get_rateindex(sc->cur_rate_table, &tx_info->status.rates[final_ts_idx])); } @@ -1521,6 +1541,94 @@ static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband, } } +#ifdef CONFIG_ATH9K_DEBUGFS + +static int ath9k_debugfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t read_file_rcstat(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_rate_priv *rc = file->private_data; + char *buf; + unsigned int len = 0, max; + int i = 0; + ssize_t retval; + + if (rc->rate_table == NULL) + return 0; + + max = 80 + rc->rate_table->rate_cnt * 1024 + 1; + buf = kmalloc(max, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + len += sprintf(buf, "%6s %6s %6s " + "%10s %10s %10s %10s\n", + "HT", "MCS", "Rate", + "Success", "Retries", "XRetries", "PER"); + + for (i = 0; i < rc->rate_table->rate_cnt; i++) { + u32 ratekbps = rc->rate_table->info[i].ratekbps; + struct ath_rc_stats *stats = &rc->rcstats[i]; + char mcs[5]; + char htmode[5]; + int used_mcs = 0, used_htmode = 0; + + if (WLAN_RC_PHY_HT(rc->rate_table->info[i].phy)) { + used_mcs = snprintf(mcs, 5, "%d", + rc->rate_table->info[i].ratecode); + + if (WLAN_RC_PHY_40(rc->rate_table->info[i].phy)) + used_htmode = snprintf(htmode, 5, "HT40"); + else if (WLAN_RC_PHY_20(rc->rate_table->info[i].phy)) + used_htmode = snprintf(htmode, 5, "HT20"); + else + used_htmode = snprintf(htmode, 5, "????"); + } + + mcs[used_mcs] = '\0'; + htmode[used_htmode] = '\0'; + + len += snprintf(buf + len, max - len, + "%6s %6s %3u.%d: " + "%10u %10u %10u %10u\n", + htmode, + mcs, + ratekbps / 1000, + (ratekbps % 1000) / 100, + stats->success, + stats->retries, + stats->xretries, + stats->per); + } + + if (len > max) + len = max; + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + return retval; +} + +static const struct file_operations fops_rcstat = { + .read = read_file_rcstat, + .open = ath9k_debugfs_open, + .owner = THIS_MODULE +}; + +static void ath_rate_add_sta_debugfs(void *priv, void *priv_sta, + struct dentry *dir) +{ + struct ath_rate_priv *rc = priv_sta; + debugfs_create_file("rc_stats", S_IRUGO, dir, rc, &fops_rcstat); +} + +#endif /* CONFIG_ATH9K_DEBUGFS */ + static void *ath_rate_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) { struct ath_wiphy *aphy = hw->priv; @@ -1567,6 +1675,9 @@ static struct rate_control_ops ath_rate_ops = { .free = ath_rate_free, .alloc_sta = ath_rate_alloc_sta, .free_sta = ath_rate_free_sta, +#ifdef CONFIG_ATH9K_DEBUGFS + .add_sta_debugfs = ath_rate_add_sta_debugfs, +#endif }; int ath_rate_control_register(void) diff --git a/drivers/net/wireless/ath/ath9k/rc.h b/drivers/net/wireless/ath/ath9k/rc.h index fbb1d33ee4b7..2f46a2266ba1 100644 --- a/drivers/net/wireless/ath/ath9k/rc.h +++ b/drivers/net/wireless/ath/ath9k/rc.h @@ -176,6 +176,13 @@ struct ath_rateset { u8 rs_rates[ATH_RATE_MAX]; }; +struct ath_rc_stats { + u32 success; + u32 retries; + u32 xretries; + u8 per; +}; + /** * struct ath_rate_priv - Rate Control priv data * @state: RC state @@ -212,6 +219,10 @@ struct ath_rate_priv { struct ath_rateset neg_rates; struct ath_rateset neg_ht_rates; struct ath_rate_softc *asc; + const struct ath_rate_table *rate_table; + + struct dentry *debugfs_rcstats; + struct ath_rc_stats rcstats[RATE_TABLE_SIZE]; }; #define ATH_TX_INFO_FRAME_TYPE_INTERNAL (1 << 0) -- cgit v1.2.3 From 4e9900180eb72b3b22fc9742999045b11607eb24 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 13 Oct 2010 16:43:27 +0200 Subject: ath9k: remove sc->cur_rate_table and sc->cur_rate_mode Set the rate table in the rc module properly based on band and HT capabilities instead, which was already partially done, but not for every mode. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 2 -- drivers/net/wireless/ath/ath9k/main.c | 35 ---------------------------------- drivers/net/wireless/ath/ath9k/rc.c | 24 ++++++++--------------- 3 files changed, 8 insertions(+), 53 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 4e81fe35aa3f..e435c1d38826 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -623,8 +623,6 @@ struct ath_softc { struct ath_rx rx; struct ath_tx tx; struct ath_beacon beacon; - const struct ath_rate_table *cur_rate_table; - enum wireless_mode cur_rate_mode; struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; struct ath_led radio_led; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 2c7f28e022b8..3ff0e476c2b3 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -18,36 +18,6 @@ #include "ath9k.h" #include "btcoex.h" -static void ath_cache_conf_rate(struct ath_softc *sc, - struct ieee80211_conf *conf) -{ - switch (conf->channel->band) { - case IEEE80211_BAND_2GHZ: - if (conf_is_ht20(conf)) - sc->cur_rate_mode = ATH9K_MODE_11NG_HT20; - else if (conf_is_ht40_minus(conf)) - sc->cur_rate_mode = ATH9K_MODE_11NG_HT40MINUS; - else if (conf_is_ht40_plus(conf)) - sc->cur_rate_mode = ATH9K_MODE_11NG_HT40PLUS; - else - sc->cur_rate_mode = ATH9K_MODE_11G; - break; - case IEEE80211_BAND_5GHZ: - if (conf_is_ht20(conf)) - sc->cur_rate_mode = ATH9K_MODE_11NA_HT20; - else if (conf_is_ht40_minus(conf)) - sc->cur_rate_mode = ATH9K_MODE_11NA_HT40MINUS; - else if (conf_is_ht40_plus(conf)) - sc->cur_rate_mode = ATH9K_MODE_11NA_HT40PLUS; - else - sc->cur_rate_mode = ATH9K_MODE_11A; - break; - default: - BUG_ON(1); - break; - } -} - static void ath_update_txpow(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; @@ -306,7 +276,6 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, goto ps_restore; } - ath_cache_conf_rate(sc, &hw->conf); ath_update_txpow(sc); ath9k_hw_set_interrupts(ah, ah->imask); @@ -1013,8 +982,6 @@ int ath_reset(struct ath_softc *sc, bool retry_tx) * that changes the channel so update any state that * might change as a result. */ - ath_cache_conf_rate(sc, &hw->conf); - ath_update_txpow(sc); if ((sc->sc_flags & SC_OP_BEACONS) || !(sc->sc_flags & (SC_OP_OFFCHANNEL))) @@ -1221,8 +1188,6 @@ static int ath9k_start(struct ieee80211_hw *hw) if (ah->caps.hw_caps & ATH9K_HW_CAP_HT) ah->imask |= ATH9K_INT_CST; - ath_cache_conf_rate(sc, &hw->conf); - sc->sc_flags &= ~SC_OP_INVALID; /* Disable BMISS interrupt when we're not associated */ diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index 5db3a12356ec..2d3a67fc9bbf 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -791,7 +791,7 @@ static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, */ try_per_rate = 4; - rate_table = sc->cur_rate_table; + rate_table = ath_rc_priv->rate_table; rix = ath_rc_get_highest_rix(sc, ath_rc_priv, rate_table, &is_probe); /* @@ -1048,7 +1048,7 @@ static void ath_rc_update_ht(struct ath_softc *sc, int rate; u8 last_per; bool state_change = false; - const struct ath_rate_table *rate_table = sc->cur_rate_table; + const struct ath_rate_table *rate_table = ath_rc_priv->rate_table; int size = ath_rc_priv->rate_table_size; if ((tx_rate < 0) || (tx_rate > rate_table->rate_cnt)) @@ -1150,7 +1150,7 @@ static void ath_rc_tx_status(struct ath_softc *sc, u8 flags; u32 i = 0, rix; - rate_table = sc->cur_rate_table; + rate_table = ath_rc_priv->rate_table; /* * If the first rate is not the final index, there @@ -1231,7 +1231,6 @@ struct ath_rate_table *ath_choose_rate_table(struct ath_softc *sc, ath_print(common, ATH_DBG_CONFIG, "Choosing rate table for mode: %d\n", mode); - sc->cur_rate_mode = mode; return hw_rate_table[mode]; } @@ -1303,7 +1302,6 @@ static void ath_rc_init(struct ath_softc *sc, ath_rc_priv->max_valid_rate = k; ath_rc_sort_validrates(rate_table, ath_rc_priv); ath_rc_priv->rate_max_phy = ath_rc_priv->valid_rate_index[k-4]; - sc->cur_rate_table = rate_table; ath_rc_priv->rate_table = rate_table; ath_print(common, ATH_DBG_CONFIG, @@ -1439,8 +1437,9 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband, } } - ath_debug_stat_rc(ath_rc_priv, ath_rc_get_rateindex(sc->cur_rate_table, - &tx_info->status.rates[final_ts_idx])); + ath_debug_stat_rc(ath_rc_priv, + ath_rc_get_rateindex(ath_rc_priv->rate_table, + &tx_info->status.rates[final_ts_idx])); } static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband, @@ -1480,14 +1479,8 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband, /* Choose rate table first */ - if ((sc->sc_ah->opmode == NL80211_IFTYPE_STATION) || - (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT) || - (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC)) { - rate_table = ath_choose_rate_table(sc, sband->band, - sta->ht_cap.ht_supported, is_cw40); - } else { - rate_table = hw_rate_table[sc->cur_rate_mode]; - } + rate_table = ath_choose_rate_table(sc, sband->band, + sta->ht_cap.ht_supported, is_cw40); ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta, is_cw40, is_sgi); ath_rc_init(sc, priv_sta, sband, sta, rate_table); @@ -1536,7 +1529,6 @@ static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband, ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_CONFIG, "Operating HT Bandwidth changed to: %d\n", sc->hw->conf.channel_type); - sc->cur_rate_table = hw_rate_table[sc->cur_rate_mode]; } } } -- cgit v1.2.3 From d4659912b557e9f68c0ad8be14e2cafd3210dd16 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 14 Oct 2010 16:02:39 +0200 Subject: ath9k_hw: remove enum wireless_mode and its users The wireless mode bitfield was only used to detect 2.4 and 5 GHz support, which can be simplified by using ATH9K_HW_CAP_* capabilities. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/htc_drv_init.c | 12 ++++---- drivers/net/wireless/ath/ath9k/hw.c | 34 +++------------------- drivers/net/wireless/ath/ath9k/hw.h | 15 ++-------- drivers/net/wireless/ath/ath9k/init.c | 12 ++++---- drivers/net/wireless/ath/ath9k/rc.c | 41 +++++---------------------- 5 files changed, 25 insertions(+), 89 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index bbb54bc28a44..3d7b97f1b3ae 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -566,7 +566,7 @@ static void ath9k_init_crypto(struct ath9k_htc_priv *priv) static void ath9k_init_channels_rates(struct ath9k_htc_priv *priv) { - if (test_bit(ATH9K_MODE_11G, priv->ah->caps.wireless_modes)) { + if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) { priv->sbands[IEEE80211_BAND_2GHZ].channels = ath9k_2ghz_channels; priv->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; @@ -577,7 +577,7 @@ static void ath9k_init_channels_rates(struct ath9k_htc_priv *priv) ARRAY_SIZE(ath9k_legacy_rates); } - if (test_bit(ATH9K_MODE_11A, priv->ah->caps.wireless_modes)) { + if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) { priv->sbands[IEEE80211_BAND_5GHZ].channels = ath9k_5ghz_channels; priv->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ; priv->sbands[IEEE80211_BAND_5GHZ].n_channels = @@ -740,18 +740,18 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, hw->extra_tx_headroom = sizeof(struct tx_frame_hdr) + sizeof(struct htc_frame_hdr) + 4; - if (test_bit(ATH9K_MODE_11G, priv->ah->caps.wireless_modes)) + if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->sbands[IEEE80211_BAND_2GHZ]; - if (test_bit(ATH9K_MODE_11A, priv->ah->caps.wireless_modes)) + if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &priv->sbands[IEEE80211_BAND_5GHZ]; if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_HT) { - if (test_bit(ATH9K_MODE_11G, priv->ah->caps.wireless_modes)) + if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) setup_ht_cap(priv, &priv->sbands[IEEE80211_BAND_2GHZ].ht_cap); - if (test_bit(ATH9K_MODE_11A, priv->ah->caps.wireless_modes)) + if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) setup_ht_cap(priv, &priv->sbands[IEEE80211_BAND_5GHZ].ht_cap); } diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index f5d79177770c..cc13ee117823 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1800,37 +1800,11 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) return -EINVAL; } - bitmap_zero(pCap->wireless_modes, ATH9K_MODE_MAX); - - if (eeval & AR5416_OPFLAGS_11A) { - set_bit(ATH9K_MODE_11A, pCap->wireless_modes); - if (ah->config.ht_enable) { - if (!(eeval & AR5416_OPFLAGS_N_5G_HT20)) - set_bit(ATH9K_MODE_11NA_HT20, - pCap->wireless_modes); - if (!(eeval & AR5416_OPFLAGS_N_5G_HT40)) { - set_bit(ATH9K_MODE_11NA_HT40PLUS, - pCap->wireless_modes); - set_bit(ATH9K_MODE_11NA_HT40MINUS, - pCap->wireless_modes); - } - } - } + if (eeval & AR5416_OPFLAGS_11A) + pCap->hw_caps |= ATH9K_HW_CAP_5GHZ; - if (eeval & AR5416_OPFLAGS_11G) { - set_bit(ATH9K_MODE_11G, pCap->wireless_modes); - if (ah->config.ht_enable) { - if (!(eeval & AR5416_OPFLAGS_N_2G_HT20)) - set_bit(ATH9K_MODE_11NG_HT20, - pCap->wireless_modes); - if (!(eeval & AR5416_OPFLAGS_N_2G_HT40)) { - set_bit(ATH9K_MODE_11NG_HT40PLUS, - pCap->wireless_modes); - set_bit(ATH9K_MODE_11NG_HT40MINUS, - pCap->wireless_modes); - } - } - } + if (eeval & AR5416_OPFLAGS_11G) + pCap->hw_caps |= ATH9K_HW_CAP_2GHZ; pCap->tx_chainmask = ah->eep_ops->get_eeprom(ah, EEP_TX_MASK); /* diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 6b9233472fbf..d032939768b0 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -164,18 +164,6 @@ enum ath_ini_subsys { ATH_INI_NUM_SPLIT, }; -enum wireless_mode { - ATH9K_MODE_11A = 0, - ATH9K_MODE_11G, - ATH9K_MODE_11NA_HT20, - ATH9K_MODE_11NG_HT20, - ATH9K_MODE_11NA_HT40PLUS, - ATH9K_MODE_11NA_HT40MINUS, - ATH9K_MODE_11NG_HT40PLUS, - ATH9K_MODE_11NG_HT40MINUS, - ATH9K_MODE_MAX, -}; - enum ath9k_hw_caps { ATH9K_HW_CAP_HT = BIT(0), ATH9K_HW_CAP_RFSILENT = BIT(1), @@ -190,11 +178,12 @@ enum ath9k_hw_caps { ATH9K_HW_CAP_SGI_20 = BIT(10), ATH9K_HW_CAP_PAPRD = BIT(11), ATH9K_HW_CAP_ANT_DIV_COMB = BIT(12), + ATH9K_HW_CAP_2GHZ = BIT(13), + ATH9K_HW_CAP_5GHZ = BIT(14), }; struct ath9k_hw_capabilities { u32 hw_caps; /* ATH9K_HW_CAP_* from ath9k_hw_caps */ - DECLARE_BITMAP(wireless_modes, ATH9K_MODE_MAX); /* ATH9K_MODE_* */ u16 total_queues; u16 keycache_size; u16 low_5ghz_chan, high_5ghz_chan; diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index a4c5ed41b176..bc6c4df9712c 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -485,7 +485,7 @@ static int ath9k_init_channels_rates(struct ath_softc *sc) ARRAY_SIZE(ath9k_5ghz_chantable) != ATH9K_NUM_CHANNELS); - if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) { + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) { channels = kmemdup(ath9k_2ghz_chantable, sizeof(ath9k_2ghz_chantable), GFP_KERNEL); if (!channels) @@ -500,7 +500,7 @@ static int ath9k_init_channels_rates(struct ath_softc *sc) ARRAY_SIZE(ath9k_legacy_rates); } - if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) { + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) { channels = kmemdup(ath9k_5ghz_chantable, sizeof(ath9k_5ghz_chantable), GFP_KERNEL); if (!channels) { @@ -681,17 +681,17 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->rate_control_algorithm = "ath9k_rate_control"; #endif - if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &sc->sbands[IEEE80211_BAND_2GHZ]; - if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &sc->sbands[IEEE80211_BAND_5GHZ]; if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) { - if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap); - if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap); } diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index 2d3a67fc9bbf..0cee90cf8dc9 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -378,17 +378,6 @@ static const struct ath_rate_table ar5416_11g_ratetable = { 0, /* Phy rates allowed initially */ }; -static const struct ath_rate_table *hw_rate_table[ATH9K_MODE_MAX] = { - [ATH9K_MODE_11A] = &ar5416_11a_ratetable, - [ATH9K_MODE_11G] = &ar5416_11g_ratetable, - [ATH9K_MODE_11NA_HT20] = &ar5416_11na_ratetable, - [ATH9K_MODE_11NG_HT20] = &ar5416_11ng_ratetable, - [ATH9K_MODE_11NA_HT40PLUS] = &ar5416_11na_ratetable, - [ATH9K_MODE_11NA_HT40MINUS] = &ar5416_11na_ratetable, - [ATH9K_MODE_11NG_HT40PLUS] = &ar5416_11ng_ratetable, - [ATH9K_MODE_11NG_HT40MINUS] = &ar5416_11ng_ratetable, -}; - static int ath_rc_get_rateindex(const struct ath_rate_table *rate_table, struct ieee80211_tx_rate *rate); @@ -1200,38 +1189,23 @@ static void ath_rc_tx_status(struct ath_softc *sc, static const struct ath_rate_table *ath_choose_rate_table(struct ath_softc *sc, enum ieee80211_band band, - bool is_ht, - bool is_cw_40) + bool is_ht) { - int mode = 0; struct ath_common *common = ath9k_hw_common(sc->sc_ah); switch(band) { case IEEE80211_BAND_2GHZ: - mode = ATH9K_MODE_11G; if (is_ht) - mode = ATH9K_MODE_11NG_HT20; - if (is_cw_40) - mode = ATH9K_MODE_11NG_HT40PLUS; - break; + return &ar5416_11ng_ratetable; + return &ar5416_11g_ratetable; case IEEE80211_BAND_5GHZ: - mode = ATH9K_MODE_11A; if (is_ht) - mode = ATH9K_MODE_11NA_HT20; - if (is_cw_40) - mode = ATH9K_MODE_11NA_HT40PLUS; - break; + return &ar5416_11na_ratetable; + return &ar5416_11a_ratetable; default: ath_print(common, ATH_DBG_CONFIG, "Invalid band\n"); return NULL; } - - BUG_ON(mode >= ATH9K_MODE_MAX); - - ath_print(common, ATH_DBG_CONFIG, - "Choosing rate table for mode: %d\n", mode); - - return hw_rate_table[mode]; } static void ath_rc_init(struct ath_softc *sc, @@ -1480,7 +1454,7 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband, /* Choose rate table first */ rate_table = ath_choose_rate_table(sc, sband->band, - sta->ht_cap.ht_supported, is_cw40); + sta->ht_cap.ht_supported); ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta, is_cw40, is_sgi); ath_rc_init(sc, priv_sta, sband, sta, rate_table); @@ -1520,8 +1494,7 @@ static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband, if ((local_cw40 != oper_cw40) || (local_sgi != oper_sgi)) { rate_table = ath_choose_rate_table(sc, sband->band, - sta->ht_cap.ht_supported, - oper_cw40); + sta->ht_cap.ht_supported); ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta, oper_cw40, oper_sgi); ath_rc_init(sc, priv_sta, sband, sta, rate_table); -- cgit v1.2.3 From 94a40c0c6bcc47ceba12e0247c5a23fb1e6c81e4 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Thu, 14 Oct 2010 10:50:26 +0530 Subject: ath9k_htc: set probe request rx filter This patch enables to receive probe request frames on p2p client mode. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 1 + drivers/net/wireless/ath/ath9k/htc_drv_txrx.c | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index f12591f5d02a..55c80866dfc6 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1454,6 +1454,7 @@ out: FIF_PSPOLL | \ FIF_OTHER_BSS | \ FIF_BCN_PRBRESP_PROMISC | \ + FIF_PROBE_REQ | \ FIF_FCSFAIL) static void ath9k_htc_configure_filter(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index c99600aff76d..3d19b5bc937f 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -369,8 +369,7 @@ u32 ath9k_htc_calcrxfilter(struct ath9k_htc_priv *priv) | ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST | ATH9K_RX_FILTER_MCAST; - /* If not a STA, enable processing of Probe Requests */ - if (ah->opmode != NL80211_IFTYPE_STATION) + if (priv->rxfilter & FIF_PROBE_REQ) rfilt |= ATH9K_RX_FILTER_PROBEREQ; /* -- cgit v1.2.3 From 886b42bf5e54098061c8bae3d5e292a8b6897401 Mon Sep 17 00:00:00 2001 From: Luis R. Rodriguez Date: Thu, 14 Oct 2010 11:44:27 -0700 Subject: ath9k_hw: remove AR9003 2.0 support These chipsets will not hit the market, all customers will be on >= AR9003 2.2. This shaves down the ath9k_hw size by 24161 bytes (24 KB) on my system. Before: $ size drivers/net/wireless/ath/ath9k/ath9k_hw.ko text data bss dec hex filename 292328 616 1824 294768 47f70 drivers/net/wireless/ath/ath9k/ath9k_hw.ko $ du -b drivers/net/wireless/ath/ath9k/ath9k_hw.ko 5987825 drivers/net/wireless/ath/ath9k/ath9k_hw.ko After: $ size drivers/net/wireless/ath/ath9k/ath9k_hw.ko text data bss dec hex filename 277192 616 1824 279632 44450 drivers/net/wireless/ath/ath9k/ath9k_hw.ko $ du -b drivers/net/wireless/ath/ath9k/ath9k_hw.ko 5963664 drivers/net/wireless/ath/ath9k/ath9k_hw.ko Cc: Yixiang Li Cc: Don Breslin Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- .../net/wireless/ath/ath9k/ar9003_2p0_initvals.h | 1784 -------------------- drivers/net/wireless/ath/ath9k/ar9003_hw.c | 162 +- drivers/net/wireless/ath/ath9k/reg.h | 3 - 3 files changed, 26 insertions(+), 1923 deletions(-) delete mode 100644 drivers/net/wireless/ath/ath9k/ar9003_2p0_initvals.h (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_2p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9003_2p0_initvals.h deleted file mode 100644 index d3375fc4ce8b..000000000000 --- a/drivers/net/wireless/ath/ath9k/ar9003_2p0_initvals.h +++ /dev/null @@ -1,1784 +0,0 @@ -/* - * Copyright (c) 2010 Atheros Communications Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef INITVALS_9003_2P0_H -#define INITVALS_9003_2P0_H - -/* AR9003 2.0 */ - -static const u32 ar9300_2p0_radio_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x0001609c, 0x0dd08f29, 0x0dd08f29, 0x0b283f31, 0x0b283f31}, - {0x000160ac, 0xa4653c00, 0xa4653c00, 0x24652800, 0x24652800}, - {0x000160b0, 0x03284f3e, 0x03284f3e, 0x05d08f20, 0x05d08f20}, - {0x0001610c, 0x08000000, 0x00000000, 0x00000000, 0x00000000}, - {0x00016140, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, - {0x0001650c, 0x08000000, 0x00000000, 0x00000000, 0x00000000}, - {0x00016540, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, - {0x0001690c, 0x08000000, 0x00000000, 0x00000000, 0x00000000}, - {0x00016940, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, -}; - -static const u32 ar9300Modes_lowest_ob_db_tx_gain_table_2p0[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, - {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, - {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, - {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, - {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202}, - {0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400}, - {0x0000a518, 0x21020220, 0x21020220, 0x16000402, 0x16000402}, - {0x0000a51c, 0x27020223, 0x27020223, 0x19000404, 0x19000404}, - {0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603}, - {0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02}, - {0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04}, - {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x28000a20, 0x28000a20}, - {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2c000e20, 0x2c000e20}, - {0x0000a534, 0x4202242a, 0x4202242a, 0x30000e22, 0x30000e22}, - {0x0000a538, 0x4702244a, 0x4702244a, 0x34000e24, 0x34000e24}, - {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x38001640, 0x38001640}, - {0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660}, - {0x0000a544, 0x5302266c, 0x5302266c, 0x3f001861, 0x3f001861}, - {0x0000a548, 0x5702286c, 0x5702286c, 0x43001a81, 0x43001a81}, - {0x0000a54c, 0x5c04286b, 0x5c04286b, 0x47001a83, 0x47001a83}, - {0x0000a550, 0x61042a6c, 0x61042a6c, 0x4a001c84, 0x4a001c84}, - {0x0000a554, 0x66062a6c, 0x66062a6c, 0x4e001ce3, 0x4e001ce3}, - {0x0000a558, 0x6b062e6c, 0x6b062e6c, 0x52001ce5, 0x52001ce5}, - {0x0000a55c, 0x7006308c, 0x7006308c, 0x56001ce9, 0x56001ce9}, - {0x0000a560, 0x730a308a, 0x730a308a, 0x5a001ceb, 0x5a001ceb}, - {0x0000a564, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a568, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a56c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a570, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a574, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a578, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a57c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000}, - {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002}, - {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004}, - {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200}, - {0x0000a590, 0x16800220, 0x16800220, 0x0f800202, 0x0f800202}, - {0x0000a594, 0x1c800223, 0x1c800223, 0x12800400, 0x12800400}, - {0x0000a598, 0x21820220, 0x21820220, 0x16800402, 0x16800402}, - {0x0000a59c, 0x27820223, 0x27820223, 0x19800404, 0x19800404}, - {0x0000a5a0, 0x2b822220, 0x2b822220, 0x1c800603, 0x1c800603}, - {0x0000a5a4, 0x2f822222, 0x2f822222, 0x21800a02, 0x21800a02}, - {0x0000a5a8, 0x34822225, 0x34822225, 0x25800a04, 0x25800a04}, - {0x0000a5ac, 0x3a82222a, 0x3a82222a, 0x28800a20, 0x28800a20}, - {0x0000a5b0, 0x3e82222c, 0x3e82222c, 0x2c800e20, 0x2c800e20}, - {0x0000a5b4, 0x4282242a, 0x4282242a, 0x30800e22, 0x30800e22}, - {0x0000a5b8, 0x4782244a, 0x4782244a, 0x34800e24, 0x34800e24}, - {0x0000a5bc, 0x4b82244c, 0x4b82244c, 0x38801640, 0x38801640}, - {0x0000a5c0, 0x4e82246c, 0x4e82246c, 0x3c801660, 0x3c801660}, - {0x0000a5c4, 0x5382266c, 0x5382266c, 0x3f801861, 0x3f801861}, - {0x0000a5c8, 0x5782286c, 0x5782286c, 0x43801a81, 0x43801a81}, - {0x0000a5cc, 0x5c84286b, 0x5c84286b, 0x47801a83, 0x47801a83}, - {0x0000a5d0, 0x61842a6c, 0x61842a6c, 0x4a801c84, 0x4a801c84}, - {0x0000a5d4, 0x66862a6c, 0x66862a6c, 0x4e801ce3, 0x4e801ce3}, - {0x0000a5d8, 0x6b862e6c, 0x6b862e6c, 0x52801ce5, 0x52801ce5}, - {0x0000a5dc, 0x7086308c, 0x7086308c, 0x56801ce9, 0x56801ce9}, - {0x0000a5e0, 0x738a308a, 0x738a308a, 0x5a801ceb, 0x5a801ceb}, - {0x0000a5e4, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, - {0x0000a5e8, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, - {0x0000a5ec, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, - {0x0000a5f0, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, - {0x0000a5f4, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, - {0x0000a5f8, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, - {0x0000a5fc, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, - {0x00016044, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, - {0x00016048, 0x62480001, 0x62480001, 0x62480001, 0x62480001}, - {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, - {0x00016444, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, - {0x00016448, 0x62480001, 0x62480001, 0x62480001, 0x62480001}, - {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, - {0x00016844, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, - {0x00016848, 0x62480001, 0x62480001, 0x62480001, 0x62480001}, - {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, -}; - -static const u32 ar9300Modes_fast_clock_2p0[][3] = { - /* Addr 5G_HT20 5G_HT40 */ - {0x00001030, 0x00000268, 0x000004d0}, - {0x00001070, 0x0000018c, 0x00000318}, - {0x000010b0, 0x00000fd0, 0x00001fa0}, - {0x00008014, 0x044c044c, 0x08980898}, - {0x0000801c, 0x148ec02b, 0x148ec057}, - {0x00008318, 0x000044c0, 0x00008980}, - {0x00009e00, 0x03721821, 0x03721821}, - {0x0000a230, 0x0000000b, 0x00000016}, - {0x0000a254, 0x00000898, 0x00001130}, -}; - -static const u32 ar9300_2p0_radio_core[][2] = { - /* Addr allmodes */ - {0x00016000, 0x36db6db6}, - {0x00016004, 0x6db6db40}, - {0x00016008, 0x73f00000}, - {0x0001600c, 0x00000000}, - {0x00016040, 0x7f80fff8}, - {0x0001604c, 0x76d005b5}, - {0x00016050, 0x556cf031}, - {0x00016054, 0x13449440}, - {0x00016058, 0x0c51c92c}, - {0x0001605c, 0x3db7fffc}, - {0x00016060, 0xfffffffc}, - {0x00016064, 0x000f0278}, - {0x0001606c, 0x6db60000}, - {0x00016080, 0x00000000}, - {0x00016084, 0x0e48048c}, - {0x00016088, 0x54214514}, - {0x0001608c, 0x119f481e}, - {0x00016090, 0x24926490}, - {0x00016098, 0xd2888888}, - {0x000160a0, 0x0a108ffe}, - {0x000160a4, 0x812fc370}, - {0x000160a8, 0x423c8000}, - {0x000160b4, 0x92480080}, - {0x000160c0, 0x00adb6d0}, - {0x000160c4, 0x6db6db60}, - {0x000160c8, 0x6db6db6c}, - {0x000160cc, 0x01e6c000}, - {0x00016100, 0x3fffbe01}, - {0x00016104, 0xfff80000}, - {0x00016108, 0x00080010}, - {0x00016144, 0x02084080}, - {0x00016148, 0x00000000}, - {0x00016280, 0x058a0001}, - {0x00016284, 0x3d840208}, - {0x00016288, 0x05a20408}, - {0x0001628c, 0x00038c07}, - {0x00016290, 0x40000004}, - {0x00016294, 0x458aa14f}, - {0x00016380, 0x00000000}, - {0x00016384, 0x00000000}, - {0x00016388, 0x00800700}, - {0x0001638c, 0x00800700}, - {0x00016390, 0x00800700}, - {0x00016394, 0x00000000}, - {0x00016398, 0x00000000}, - {0x0001639c, 0x00000000}, - {0x000163a0, 0x00000001}, - {0x000163a4, 0x00000001}, - {0x000163a8, 0x00000000}, - {0x000163ac, 0x00000000}, - {0x000163b0, 0x00000000}, - {0x000163b4, 0x00000000}, - {0x000163b8, 0x00000000}, - {0x000163bc, 0x00000000}, - {0x000163c0, 0x000000a0}, - {0x000163c4, 0x000c0000}, - {0x000163c8, 0x14021402}, - {0x000163cc, 0x00001402}, - {0x000163d0, 0x00000000}, - {0x000163d4, 0x00000000}, - {0x00016400, 0x36db6db6}, - {0x00016404, 0x6db6db40}, - {0x00016408, 0x73f00000}, - {0x0001640c, 0x00000000}, - {0x00016440, 0x7f80fff8}, - {0x0001644c, 0x76d005b5}, - {0x00016450, 0x556cf031}, - {0x00016454, 0x13449440}, - {0x00016458, 0x0c51c92c}, - {0x0001645c, 0x3db7fffc}, - {0x00016460, 0xfffffffc}, - {0x00016464, 0x000f0278}, - {0x0001646c, 0x6db60000}, - {0x00016500, 0x3fffbe01}, - {0x00016504, 0xfff80000}, - {0x00016508, 0x00080010}, - {0x00016544, 0x02084080}, - {0x00016548, 0x00000000}, - {0x00016780, 0x00000000}, - {0x00016784, 0x00000000}, - {0x00016788, 0x00800700}, - {0x0001678c, 0x00800700}, - {0x00016790, 0x00800700}, - {0x00016794, 0x00000000}, - {0x00016798, 0x00000000}, - {0x0001679c, 0x00000000}, - {0x000167a0, 0x00000001}, - {0x000167a4, 0x00000001}, - {0x000167a8, 0x00000000}, - {0x000167ac, 0x00000000}, - {0x000167b0, 0x00000000}, - {0x000167b4, 0x00000000}, - {0x000167b8, 0x00000000}, - {0x000167bc, 0x00000000}, - {0x000167c0, 0x000000a0}, - {0x000167c4, 0x000c0000}, - {0x000167c8, 0x14021402}, - {0x000167cc, 0x00001402}, - {0x000167d0, 0x00000000}, - {0x000167d4, 0x00000000}, - {0x00016800, 0x36db6db6}, - {0x00016804, 0x6db6db40}, - {0x00016808, 0x73f00000}, - {0x0001680c, 0x00000000}, - {0x00016840, 0x7f80fff8}, - {0x0001684c, 0x76d005b5}, - {0x00016850, 0x556cf031}, - {0x00016854, 0x13449440}, - {0x00016858, 0x0c51c92c}, - {0x0001685c, 0x3db7fffc}, - {0x00016860, 0xfffffffc}, - {0x00016864, 0x000f0278}, - {0x0001686c, 0x6db60000}, - {0x00016900, 0x3fffbe01}, - {0x00016904, 0xfff80000}, - {0x00016908, 0x00080010}, - {0x00016944, 0x02084080}, - {0x00016948, 0x00000000}, - {0x00016b80, 0x00000000}, - {0x00016b84, 0x00000000}, - {0x00016b88, 0x00800700}, - {0x00016b8c, 0x00800700}, - {0x00016b90, 0x00800700}, - {0x00016b94, 0x00000000}, - {0x00016b98, 0x00000000}, - {0x00016b9c, 0x00000000}, - {0x00016ba0, 0x00000001}, - {0x00016ba4, 0x00000001}, - {0x00016ba8, 0x00000000}, - {0x00016bac, 0x00000000}, - {0x00016bb0, 0x00000000}, - {0x00016bb4, 0x00000000}, - {0x00016bb8, 0x00000000}, - {0x00016bbc, 0x00000000}, - {0x00016bc0, 0x000000a0}, - {0x00016bc4, 0x000c0000}, - {0x00016bc8, 0x14021402}, - {0x00016bcc, 0x00001402}, - {0x00016bd0, 0x00000000}, - {0x00016bd4, 0x00000000}, -}; - -static const u32 ar9300Common_rx_gain_table_merlin_2p0[][2] = { - /* Addr allmodes */ - {0x0000a000, 0x02000101}, - {0x0000a004, 0x02000102}, - {0x0000a008, 0x02000103}, - {0x0000a00c, 0x02000104}, - {0x0000a010, 0x02000200}, - {0x0000a014, 0x02000201}, - {0x0000a018, 0x02000202}, - {0x0000a01c, 0x02000203}, - {0x0000a020, 0x02000204}, - {0x0000a024, 0x02000205}, - {0x0000a028, 0x02000208}, - {0x0000a02c, 0x02000302}, - {0x0000a030, 0x02000303}, - {0x0000a034, 0x02000304}, - {0x0000a038, 0x02000400}, - {0x0000a03c, 0x02010300}, - {0x0000a040, 0x02010301}, - {0x0000a044, 0x02010302}, - {0x0000a048, 0x02000500}, - {0x0000a04c, 0x02010400}, - {0x0000a050, 0x02020300}, - {0x0000a054, 0x02020301}, - {0x0000a058, 0x02020302}, - {0x0000a05c, 0x02020303}, - {0x0000a060, 0x02020400}, - {0x0000a064, 0x02030300}, - {0x0000a068, 0x02030301}, - {0x0000a06c, 0x02030302}, - {0x0000a070, 0x02030303}, - {0x0000a074, 0x02030400}, - {0x0000a078, 0x02040300}, - {0x0000a07c, 0x02040301}, - {0x0000a080, 0x02040302}, - {0x0000a084, 0x02040303}, - {0x0000a088, 0x02030500}, - {0x0000a08c, 0x02040400}, - {0x0000a090, 0x02050203}, - {0x0000a094, 0x02050204}, - {0x0000a098, 0x02050205}, - {0x0000a09c, 0x02040500}, - {0x0000a0a0, 0x02050301}, - {0x0000a0a4, 0x02050302}, - {0x0000a0a8, 0x02050303}, - {0x0000a0ac, 0x02050400}, - {0x0000a0b0, 0x02050401}, - {0x0000a0b4, 0x02050402}, - {0x0000a0b8, 0x02050403}, - {0x0000a0bc, 0x02050500}, - {0x0000a0c0, 0x02050501}, - {0x0000a0c4, 0x02050502}, - {0x0000a0c8, 0x02050503}, - {0x0000a0cc, 0x02050504}, - {0x0000a0d0, 0x02050600}, - {0x0000a0d4, 0x02050601}, - {0x0000a0d8, 0x02050602}, - {0x0000a0dc, 0x02050603}, - {0x0000a0e0, 0x02050604}, - {0x0000a0e4, 0x02050700}, - {0x0000a0e8, 0x02050701}, - {0x0000a0ec, 0x02050702}, - {0x0000a0f0, 0x02050703}, - {0x0000a0f4, 0x02050704}, - {0x0000a0f8, 0x02050705}, - {0x0000a0fc, 0x02050708}, - {0x0000a100, 0x02050709}, - {0x0000a104, 0x0205070a}, - {0x0000a108, 0x0205070b}, - {0x0000a10c, 0x0205070c}, - {0x0000a110, 0x0205070d}, - {0x0000a114, 0x02050710}, - {0x0000a118, 0x02050711}, - {0x0000a11c, 0x02050712}, - {0x0000a120, 0x02050713}, - {0x0000a124, 0x02050714}, - {0x0000a128, 0x02050715}, - {0x0000a12c, 0x02050730}, - {0x0000a130, 0x02050731}, - {0x0000a134, 0x02050732}, - {0x0000a138, 0x02050733}, - {0x0000a13c, 0x02050734}, - {0x0000a140, 0x02050735}, - {0x0000a144, 0x02050750}, - {0x0000a148, 0x02050751}, - {0x0000a14c, 0x02050752}, - {0x0000a150, 0x02050753}, - {0x0000a154, 0x02050754}, - {0x0000a158, 0x02050755}, - {0x0000a15c, 0x02050770}, - {0x0000a160, 0x02050771}, - {0x0000a164, 0x02050772}, - {0x0000a168, 0x02050773}, - {0x0000a16c, 0x02050774}, - {0x0000a170, 0x02050775}, - {0x0000a174, 0x00000776}, - {0x0000a178, 0x00000776}, - {0x0000a17c, 0x00000776}, - {0x0000a180, 0x00000776}, - {0x0000a184, 0x00000776}, - {0x0000a188, 0x00000776}, - {0x0000a18c, 0x00000776}, - {0x0000a190, 0x00000776}, - {0x0000a194, 0x00000776}, - {0x0000a198, 0x00000776}, - {0x0000a19c, 0x00000776}, - {0x0000a1a0, 0x00000776}, - {0x0000a1a4, 0x00000776}, - {0x0000a1a8, 0x00000776}, - {0x0000a1ac, 0x00000776}, - {0x0000a1b0, 0x00000776}, - {0x0000a1b4, 0x00000776}, - {0x0000a1b8, 0x00000776}, - {0x0000a1bc, 0x00000776}, - {0x0000a1c0, 0x00000776}, - {0x0000a1c4, 0x00000776}, - {0x0000a1c8, 0x00000776}, - {0x0000a1cc, 0x00000776}, - {0x0000a1d0, 0x00000776}, - {0x0000a1d4, 0x00000776}, - {0x0000a1d8, 0x00000776}, - {0x0000a1dc, 0x00000776}, - {0x0000a1e0, 0x00000776}, - {0x0000a1e4, 0x00000776}, - {0x0000a1e8, 0x00000776}, - {0x0000a1ec, 0x00000776}, - {0x0000a1f0, 0x00000776}, - {0x0000a1f4, 0x00000776}, - {0x0000a1f8, 0x00000776}, - {0x0000a1fc, 0x00000776}, - {0x0000b000, 0x02000101}, - {0x0000b004, 0x02000102}, - {0x0000b008, 0x02000103}, - {0x0000b00c, 0x02000104}, - {0x0000b010, 0x02000200}, - {0x0000b014, 0x02000201}, - {0x0000b018, 0x02000202}, - {0x0000b01c, 0x02000203}, - {0x0000b020, 0x02000204}, - {0x0000b024, 0x02000205}, - {0x0000b028, 0x02000208}, - {0x0000b02c, 0x02000302}, - {0x0000b030, 0x02000303}, - {0x0000b034, 0x02000304}, - {0x0000b038, 0x02000400}, - {0x0000b03c, 0x02010300}, - {0x0000b040, 0x02010301}, - {0x0000b044, 0x02010302}, - {0x0000b048, 0x02000500}, - {0x0000b04c, 0x02010400}, - {0x0000b050, 0x02020300}, - {0x0000b054, 0x02020301}, - {0x0000b058, 0x02020302}, - {0x0000b05c, 0x02020303}, - {0x0000b060, 0x02020400}, - {0x0000b064, 0x02030300}, - {0x0000b068, 0x02030301}, - {0x0000b06c, 0x02030302}, - {0x0000b070, 0x02030303}, - {0x0000b074, 0x02030400}, - {0x0000b078, 0x02040300}, - {0x0000b07c, 0x02040301}, - {0x0000b080, 0x02040302}, - {0x0000b084, 0x02040303}, - {0x0000b088, 0x02030500}, - {0x0000b08c, 0x02040400}, - {0x0000b090, 0x02050203}, - {0x0000b094, 0x02050204}, - {0x0000b098, 0x02050205}, - {0x0000b09c, 0x02040500}, - {0x0000b0a0, 0x02050301}, - {0x0000b0a4, 0x02050302}, - {0x0000b0a8, 0x02050303}, - {0x0000b0ac, 0x02050400}, - {0x0000b0b0, 0x02050401}, - {0x0000b0b4, 0x02050402}, - {0x0000b0b8, 0x02050403}, - {0x0000b0bc, 0x02050500}, - {0x0000b0c0, 0x02050501}, - {0x0000b0c4, 0x02050502}, - {0x0000b0c8, 0x02050503}, - {0x0000b0cc, 0x02050504}, - {0x0000b0d0, 0x02050600}, - {0x0000b0d4, 0x02050601}, - {0x0000b0d8, 0x02050602}, - {0x0000b0dc, 0x02050603}, - {0x0000b0e0, 0x02050604}, - {0x0000b0e4, 0x02050700}, - {0x0000b0e8, 0x02050701}, - {0x0000b0ec, 0x02050702}, - {0x0000b0f0, 0x02050703}, - {0x0000b0f4, 0x02050704}, - {0x0000b0f8, 0x02050705}, - {0x0000b0fc, 0x02050708}, - {0x0000b100, 0x02050709}, - {0x0000b104, 0x0205070a}, - {0x0000b108, 0x0205070b}, - {0x0000b10c, 0x0205070c}, - {0x0000b110, 0x0205070d}, - {0x0000b114, 0x02050710}, - {0x0000b118, 0x02050711}, - {0x0000b11c, 0x02050712}, - {0x0000b120, 0x02050713}, - {0x0000b124, 0x02050714}, - {0x0000b128, 0x02050715}, - {0x0000b12c, 0x02050730}, - {0x0000b130, 0x02050731}, - {0x0000b134, 0x02050732}, - {0x0000b138, 0x02050733}, - {0x0000b13c, 0x02050734}, - {0x0000b140, 0x02050735}, - {0x0000b144, 0x02050750}, - {0x0000b148, 0x02050751}, - {0x0000b14c, 0x02050752}, - {0x0000b150, 0x02050753}, - {0x0000b154, 0x02050754}, - {0x0000b158, 0x02050755}, - {0x0000b15c, 0x02050770}, - {0x0000b160, 0x02050771}, - {0x0000b164, 0x02050772}, - {0x0000b168, 0x02050773}, - {0x0000b16c, 0x02050774}, - {0x0000b170, 0x02050775}, - {0x0000b174, 0x00000776}, - {0x0000b178, 0x00000776}, - {0x0000b17c, 0x00000776}, - {0x0000b180, 0x00000776}, - {0x0000b184, 0x00000776}, - {0x0000b188, 0x00000776}, - {0x0000b18c, 0x00000776}, - {0x0000b190, 0x00000776}, - {0x0000b194, 0x00000776}, - {0x0000b198, 0x00000776}, - {0x0000b19c, 0x00000776}, - {0x0000b1a0, 0x00000776}, - {0x0000b1a4, 0x00000776}, - {0x0000b1a8, 0x00000776}, - {0x0000b1ac, 0x00000776}, - {0x0000b1b0, 0x00000776}, - {0x0000b1b4, 0x00000776}, - {0x0000b1b8, 0x00000776}, - {0x0000b1bc, 0x00000776}, - {0x0000b1c0, 0x00000776}, - {0x0000b1c4, 0x00000776}, - {0x0000b1c8, 0x00000776}, - {0x0000b1cc, 0x00000776}, - {0x0000b1d0, 0x00000776}, - {0x0000b1d4, 0x00000776}, - {0x0000b1d8, 0x00000776}, - {0x0000b1dc, 0x00000776}, - {0x0000b1e0, 0x00000776}, - {0x0000b1e4, 0x00000776}, - {0x0000b1e8, 0x00000776}, - {0x0000b1ec, 0x00000776}, - {0x0000b1f0, 0x00000776}, - {0x0000b1f4, 0x00000776}, - {0x0000b1f8, 0x00000776}, - {0x0000b1fc, 0x00000776}, -}; - -static const u32 ar9300_2p0_mac_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, - {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, - {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, - {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00}, - {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b}, - {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810}, - {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a}, - {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, -}; - -static const u32 ar9300_2p0_soc_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x00007010, 0x00000023, 0x00000023, 0x00000023, 0x00000023}, -}; - -static const u32 ar9200_merlin_2p0_radio_core[][2] = { - /* Addr allmodes */ - {0x00007800, 0x00040000}, - {0x00007804, 0xdb005012}, - {0x00007808, 0x04924914}, - {0x0000780c, 0x21084210}, - {0x00007810, 0x6d801300}, - {0x00007814, 0x0019beff}, - {0x00007818, 0x07e41000}, - {0x0000781c, 0x00392000}, - {0x00007820, 0x92592480}, - {0x00007824, 0x00040000}, - {0x00007828, 0xdb005012}, - {0x0000782c, 0x04924914}, - {0x00007830, 0x21084210}, - {0x00007834, 0x6d801300}, - {0x00007838, 0x0019beff}, - {0x0000783c, 0x07e40000}, - {0x00007840, 0x00392000}, - {0x00007844, 0x92592480}, - {0x00007848, 0x00100000}, - {0x0000784c, 0x773f0567}, - {0x00007850, 0x54214514}, - {0x00007854, 0x12035828}, - {0x00007858, 0x92592692}, - {0x0000785c, 0x00000000}, - {0x00007860, 0x56400000}, - {0x00007864, 0x0a8e370e}, - {0x00007868, 0xc0102850}, - {0x0000786c, 0x812d4000}, - {0x00007870, 0x807ec400}, - {0x00007874, 0x001b6db0}, - {0x00007878, 0x00376b63}, - {0x0000787c, 0x06db6db6}, - {0x00007880, 0x006d8000}, - {0x00007884, 0xffeffffe}, - {0x00007888, 0xffeffffe}, - {0x0000788c, 0x00010000}, - {0x00007890, 0x02060aeb}, - {0x00007894, 0x5a108000}, -}; - -static const u32 ar9300_2p0_baseband_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011}, - {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e}, - {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, - {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881}, - {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, - {0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c}, - {0x00009c00, 0x00000044, 0x000000c4, 0x000000c4, 0x00000044}, - {0x00009e00, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0}, - {0x00009e04, 0x00802020, 0x00802020, 0x00802020, 0x00802020}, - {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2}, - {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec84d2e}, - {0x00009e14, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e}, - {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, - {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, - {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, - {0x00009e44, 0x02321e27, 0x02321e27, 0x02291e27, 0x02291e27}, - {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012}, - {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, - {0x0000a204, 0x000037c0, 0x000037c4, 0x000037c4, 0x000037c0}, - {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004}, - {0x0000a230, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b}, - {0x0000a238, 0xffb81018, 0xffb81018, 0xffb81018, 0xffb81018}, - {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, - {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, - {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002}, - {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e}, - {0x0000a260, 0x0a021501, 0x0a021501, 0x3a021501, 0x3a021501}, - {0x0000a264, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, - {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b}, - {0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150}, - {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110}, - {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222}, - {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, - {0x0000a2d0, 0x00071981, 0x00071981, 0x00071981, 0x00071982}, - {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0xf999a83a, 0xf999a83a}, - {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, - {0x0000ae04, 0x00800000, 0x00800000, 0x00800000, 0x00800000}, - {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, - {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce}, - {0x0000b284, 0x00000000, 0x00000000, 0x00000150, 0x00000150}, - {0x0000b830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, - {0x0000be04, 0x00800000, 0x00800000, 0x00800000, 0x00800000}, - {0x0000be18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000be1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, - {0x0000be20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce}, - {0x0000c284, 0x00000000, 0x00000000, 0x00000150, 0x00000150}, -}; - -static const u32 ar9300_2p0_baseband_core[][2] = { - /* Addr allmodes */ - {0x00009800, 0xafe68e30}, - {0x00009804, 0xfd14e000}, - {0x00009808, 0x9c0a9f6b}, - {0x0000980c, 0x04900000}, - {0x00009814, 0x9280c00a}, - {0x00009818, 0x00000000}, - {0x0000981c, 0x00020028}, - {0x00009834, 0x5f3ca3de}, - {0x00009838, 0x0108ecff}, - {0x0000983c, 0x14750600}, - {0x00009880, 0x201fff00}, - {0x00009884, 0x00001042}, - {0x000098a4, 0x00200400}, - {0x000098b0, 0x52440bbe}, - {0x000098d0, 0x004b6a8e}, - {0x000098d4, 0x00000820}, - {0x000098dc, 0x00000000}, - {0x000098f0, 0x00000000}, - {0x000098f4, 0x00000000}, - {0x00009c04, 0xff55ff55}, - {0x00009c08, 0x0320ff55}, - {0x00009c0c, 0x00000000}, - {0x00009c10, 0x00000000}, - {0x00009c14, 0x00046384}, - {0x00009c18, 0x05b6b440}, - {0x00009c1c, 0x00b6b440}, - {0x00009d00, 0xc080a333}, - {0x00009d04, 0x40206c10}, - {0x00009d08, 0x009c4060}, - {0x00009d0c, 0x9883800a}, - {0x00009d10, 0x01834061}, - {0x00009d14, 0x00c0040b}, - {0x00009d18, 0x00000000}, - {0x00009e08, 0x0038230c}, - {0x00009e24, 0x990bb515}, - {0x00009e28, 0x0c6f0000}, - {0x00009e30, 0x06336f77}, - {0x00009e34, 0x6af6532f}, - {0x00009e38, 0x0cc80c00}, - {0x00009e3c, 0xcf946222}, - {0x00009e40, 0x0d261820}, - {0x00009e4c, 0x00001004}, - {0x00009e50, 0x00ff03f1}, - {0x00009e54, 0x00000000}, - {0x00009fc0, 0x803e4788}, - {0x00009fc4, 0x0001efb5}, - {0x00009fcc, 0x40000014}, - {0x00009fd0, 0x01193b93}, - {0x0000a20c, 0x00000000}, - {0x0000a220, 0x00000000}, - {0x0000a224, 0x00000000}, - {0x0000a228, 0x10002310}, - {0x0000a22c, 0x01036a1e}, - {0x0000a234, 0x10000fff}, - {0x0000a23c, 0x00000000}, - {0x0000a244, 0x0c000000}, - {0x0000a2a0, 0x00000001}, - {0x0000a2c0, 0x00000001}, - {0x0000a2c8, 0x00000000}, - {0x0000a2cc, 0x18c43433}, - {0x0000a2d4, 0x00000000}, - {0x0000a2dc, 0x00000000}, - {0x0000a2e0, 0x00000000}, - {0x0000a2e4, 0x00000000}, - {0x0000a2e8, 0x00000000}, - {0x0000a2ec, 0x00000000}, - {0x0000a2f0, 0x00000000}, - {0x0000a2f4, 0x00000000}, - {0x0000a2f8, 0x00000000}, - {0x0000a344, 0x00000000}, - {0x0000a34c, 0x00000000}, - {0x0000a350, 0x0000a000}, - {0x0000a364, 0x00000000}, - {0x0000a370, 0x00000000}, - {0x0000a390, 0x00000001}, - {0x0000a394, 0x00000444}, - {0x0000a398, 0x001f0e0f}, - {0x0000a39c, 0x0075393f}, - {0x0000a3a0, 0xb79f6427}, - {0x0000a3a4, 0x00000000}, - {0x0000a3a8, 0xaaaaaaaa}, - {0x0000a3ac, 0x3c466478}, - {0x0000a3c0, 0x20202020}, - {0x0000a3c4, 0x22222220}, - {0x0000a3c8, 0x20200020}, - {0x0000a3cc, 0x20202020}, - {0x0000a3d0, 0x20202020}, - {0x0000a3d4, 0x20202020}, - {0x0000a3d8, 0x20202020}, - {0x0000a3dc, 0x20202020}, - {0x0000a3e0, 0x20202020}, - {0x0000a3e4, 0x20202020}, - {0x0000a3e8, 0x20202020}, - {0x0000a3ec, 0x20202020}, - {0x0000a3f0, 0x00000000}, - {0x0000a3f4, 0x00000246}, - {0x0000a3f8, 0x0cdbd380}, - {0x0000a3fc, 0x000f0f01}, - {0x0000a400, 0x8fa91f01}, - {0x0000a404, 0x00000000}, - {0x0000a408, 0x0e79e5c6}, - {0x0000a40c, 0x00820820}, - {0x0000a414, 0x1ce739ce}, - {0x0000a418, 0x2d001dce}, - {0x0000a41c, 0x1ce739ce}, - {0x0000a420, 0x000001ce}, - {0x0000a424, 0x1ce739ce}, - {0x0000a428, 0x000001ce}, - {0x0000a42c, 0x1ce739ce}, - {0x0000a430, 0x1ce739ce}, - {0x0000a434, 0x00000000}, - {0x0000a438, 0x00001801}, - {0x0000a43c, 0x00000000}, - {0x0000a440, 0x00000000}, - {0x0000a444, 0x00000000}, - {0x0000a448, 0x04000080}, - {0x0000a44c, 0x00000001}, - {0x0000a450, 0x00010000}, - {0x0000a458, 0x00000000}, - {0x0000a600, 0x00000000}, - {0x0000a604, 0x00000000}, - {0x0000a608, 0x00000000}, - {0x0000a60c, 0x00000000}, - {0x0000a610, 0x00000000}, - {0x0000a614, 0x00000000}, - {0x0000a618, 0x00000000}, - {0x0000a61c, 0x00000000}, - {0x0000a620, 0x00000000}, - {0x0000a624, 0x00000000}, - {0x0000a628, 0x00000000}, - {0x0000a62c, 0x00000000}, - {0x0000a630, 0x00000000}, - {0x0000a634, 0x00000000}, - {0x0000a638, 0x00000000}, - {0x0000a63c, 0x00000000}, - {0x0000a640, 0x00000000}, - {0x0000a644, 0x3fad9d74}, - {0x0000a648, 0x0048060a}, - {0x0000a64c, 0x00000637}, - {0x0000a670, 0x03020100}, - {0x0000a674, 0x09080504}, - {0x0000a678, 0x0d0c0b0a}, - {0x0000a67c, 0x13121110}, - {0x0000a680, 0x31301514}, - {0x0000a684, 0x35343332}, - {0x0000a688, 0x00000036}, - {0x0000a690, 0x00000838}, - {0x0000a7c0, 0x00000000}, - {0x0000a7c4, 0xfffffffc}, - {0x0000a7c8, 0x00000000}, - {0x0000a7cc, 0x00000000}, - {0x0000a7d0, 0x00000000}, - {0x0000a7d4, 0x00000004}, - {0x0000a7dc, 0x00000001}, - {0x0000a8d0, 0x004b6a8e}, - {0x0000a8d4, 0x00000820}, - {0x0000a8dc, 0x00000000}, - {0x0000a8f0, 0x00000000}, - {0x0000a8f4, 0x00000000}, - {0x0000b2d0, 0x00000080}, - {0x0000b2d4, 0x00000000}, - {0x0000b2dc, 0x00000000}, - {0x0000b2e0, 0x00000000}, - {0x0000b2e4, 0x00000000}, - {0x0000b2e8, 0x00000000}, - {0x0000b2ec, 0x00000000}, - {0x0000b2f0, 0x00000000}, - {0x0000b2f4, 0x00000000}, - {0x0000b2f8, 0x00000000}, - {0x0000b408, 0x0e79e5c0}, - {0x0000b40c, 0x00820820}, - {0x0000b420, 0x00000000}, - {0x0000b8d0, 0x004b6a8e}, - {0x0000b8d4, 0x00000820}, - {0x0000b8dc, 0x00000000}, - {0x0000b8f0, 0x00000000}, - {0x0000b8f4, 0x00000000}, - {0x0000c2d0, 0x00000080}, - {0x0000c2d4, 0x00000000}, - {0x0000c2dc, 0x00000000}, - {0x0000c2e0, 0x00000000}, - {0x0000c2e4, 0x00000000}, - {0x0000c2e8, 0x00000000}, - {0x0000c2ec, 0x00000000}, - {0x0000c2f0, 0x00000000}, - {0x0000c2f4, 0x00000000}, - {0x0000c2f8, 0x00000000}, - {0x0000c408, 0x0e79e5c0}, - {0x0000c40c, 0x00820820}, - {0x0000c420, 0x00000000}, -}; - -static const u32 ar9300Modes_high_power_tx_gain_table_2p0[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9}, - {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, - {0x0000a504, 0x04002222, 0x04002222, 0x04000002, 0x04000002}, - {0x0000a508, 0x09002421, 0x09002421, 0x08000004, 0x08000004}, - {0x0000a50c, 0x0d002621, 0x0d002621, 0x0b000200, 0x0b000200}, - {0x0000a510, 0x13004620, 0x13004620, 0x0f000202, 0x0f000202}, - {0x0000a514, 0x19004a20, 0x19004a20, 0x11000400, 0x11000400}, - {0x0000a518, 0x1d004e20, 0x1d004e20, 0x15000402, 0x15000402}, - {0x0000a51c, 0x21005420, 0x21005420, 0x19000404, 0x19000404}, - {0x0000a520, 0x26005e20, 0x26005e20, 0x1b000603, 0x1b000603}, - {0x0000a524, 0x2b005e40, 0x2b005e40, 0x1f000a02, 0x1f000a02}, - {0x0000a528, 0x2f005e42, 0x2f005e42, 0x23000a04, 0x23000a04}, - {0x0000a52c, 0x33005e44, 0x33005e44, 0x26000a20, 0x26000a20}, - {0x0000a530, 0x38005e65, 0x38005e65, 0x2a000e20, 0x2a000e20}, - {0x0000a534, 0x3c005e69, 0x3c005e69, 0x2e000e22, 0x2e000e22}, - {0x0000a538, 0x40005e6b, 0x40005e6b, 0x31000e24, 0x31000e24}, - {0x0000a53c, 0x44005e6d, 0x44005e6d, 0x34001640, 0x34001640}, - {0x0000a540, 0x49005e72, 0x49005e72, 0x38001660, 0x38001660}, - {0x0000a544, 0x4e005eb2, 0x4e005eb2, 0x3b001861, 0x3b001861}, - {0x0000a548, 0x53005f12, 0x53005f12, 0x3e001a81, 0x3e001a81}, - {0x0000a54c, 0x59025eb5, 0x59025eb5, 0x42001a83, 0x42001a83}, - {0x0000a550, 0x5e025f12, 0x5e025f12, 0x44001c84, 0x44001c84}, - {0x0000a554, 0x61027f12, 0x61027f12, 0x48001ce3, 0x48001ce3}, - {0x0000a558, 0x6702bf12, 0x6702bf12, 0x4c001ce5, 0x4c001ce5}, - {0x0000a55c, 0x6b02bf14, 0x6b02bf14, 0x50001ce9, 0x50001ce9}, - {0x0000a560, 0x6f02bf16, 0x6f02bf16, 0x54001ceb, 0x54001ceb}, - {0x0000a564, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a568, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a56c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a570, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a574, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a578, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a57c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a580, 0x00802220, 0x00802220, 0x00800000, 0x00800000}, - {0x0000a584, 0x04802222, 0x04802222, 0x04800002, 0x04800002}, - {0x0000a588, 0x09802421, 0x09802421, 0x08800004, 0x08800004}, - {0x0000a58c, 0x0d802621, 0x0d802621, 0x0b800200, 0x0b800200}, - {0x0000a590, 0x13804620, 0x13804620, 0x0f800202, 0x0f800202}, - {0x0000a594, 0x19804a20, 0x19804a20, 0x11800400, 0x11800400}, - {0x0000a598, 0x1d804e20, 0x1d804e20, 0x15800402, 0x15800402}, - {0x0000a59c, 0x21805420, 0x21805420, 0x19800404, 0x19800404}, - {0x0000a5a0, 0x26805e20, 0x26805e20, 0x1b800603, 0x1b800603}, - {0x0000a5a4, 0x2b805e40, 0x2b805e40, 0x1f800a02, 0x1f800a02}, - {0x0000a5a8, 0x2f805e42, 0x2f805e42, 0x23800a04, 0x23800a04}, - {0x0000a5ac, 0x33805e44, 0x33805e44, 0x26800a20, 0x26800a20}, - {0x0000a5b0, 0x38805e65, 0x38805e65, 0x2a800e20, 0x2a800e20}, - {0x0000a5b4, 0x3c805e69, 0x3c805e69, 0x2e800e22, 0x2e800e22}, - {0x0000a5b8, 0x40805e6b, 0x40805e6b, 0x31800e24, 0x31800e24}, - {0x0000a5bc, 0x44805e6d, 0x44805e6d, 0x34801640, 0x34801640}, - {0x0000a5c0, 0x49805e72, 0x49805e72, 0x38801660, 0x38801660}, - {0x0000a5c4, 0x4e805eb2, 0x4e805eb2, 0x3b801861, 0x3b801861}, - {0x0000a5c8, 0x53805f12, 0x53805f12, 0x3e801a81, 0x3e801a81}, - {0x0000a5cc, 0x59825eb2, 0x59825eb2, 0x42801a83, 0x42801a83}, - {0x0000a5d0, 0x5e825f12, 0x5e825f12, 0x44801c84, 0x44801c84}, - {0x0000a5d4, 0x61827f12, 0x61827f12, 0x48801ce3, 0x48801ce3}, - {0x0000a5d8, 0x6782bf12, 0x6782bf12, 0x4c801ce5, 0x4c801ce5}, - {0x0000a5dc, 0x6b82bf14, 0x6b82bf14, 0x50801ce9, 0x50801ce9}, - {0x0000a5e0, 0x6f82bf16, 0x6f82bf16, 0x54801ceb, 0x54801ceb}, - {0x0000a5e4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5e8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5ec, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5f0, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5f4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5f8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5fc, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x00016044, 0x056db2e6, 0x056db2e6, 0x056db2e6, 0x056db2e6}, - {0x00016048, 0xae480001, 0xae480001, 0xae480001, 0xae480001}, - {0x00016068, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c}, - {0x00016444, 0x056db2e6, 0x056db2e6, 0x056db2e6, 0x056db2e6}, - {0x00016448, 0xae480001, 0xae480001, 0xae480001, 0xae480001}, - {0x00016468, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c}, - {0x00016844, 0x056db2e6, 0x056db2e6, 0x056db2e6, 0x056db2e6}, - {0x00016848, 0xae480001, 0xae480001, 0xae480001, 0xae480001}, - {0x00016868, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c}, -}; - -static const u32 ar9300Modes_high_ob_db_tx_gain_table_2p0[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9}, - {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, - {0x0000a504, 0x04002222, 0x04002222, 0x04000002, 0x04000002}, - {0x0000a508, 0x09002421, 0x09002421, 0x08000004, 0x08000004}, - {0x0000a50c, 0x0d002621, 0x0d002621, 0x0b000200, 0x0b000200}, - {0x0000a510, 0x13004620, 0x13004620, 0x0f000202, 0x0f000202}, - {0x0000a514, 0x19004a20, 0x19004a20, 0x11000400, 0x11000400}, - {0x0000a518, 0x1d004e20, 0x1d004e20, 0x15000402, 0x15000402}, - {0x0000a51c, 0x21005420, 0x21005420, 0x19000404, 0x19000404}, - {0x0000a520, 0x26005e20, 0x26005e20, 0x1b000603, 0x1b000603}, - {0x0000a524, 0x2b005e40, 0x2b005e40, 0x1f000a02, 0x1f000a02}, - {0x0000a528, 0x2f005e42, 0x2f005e42, 0x23000a04, 0x23000a04}, - {0x0000a52c, 0x33005e44, 0x33005e44, 0x26000a20, 0x26000a20}, - {0x0000a530, 0x38005e65, 0x38005e65, 0x2a000e20, 0x2a000e20}, - {0x0000a534, 0x3c005e69, 0x3c005e69, 0x2e000e22, 0x2e000e22}, - {0x0000a538, 0x40005e6b, 0x40005e6b, 0x31000e24, 0x31000e24}, - {0x0000a53c, 0x44005e6d, 0x44005e6d, 0x34001640, 0x34001640}, - {0x0000a540, 0x49005e72, 0x49005e72, 0x38001660, 0x38001660}, - {0x0000a544, 0x4e005eb2, 0x4e005eb2, 0x3b001861, 0x3b001861}, - {0x0000a548, 0x53005f12, 0x53005f12, 0x3e001a81, 0x3e001a81}, - {0x0000a54c, 0x59025eb5, 0x59025eb5, 0x42001a83, 0x42001a83}, - {0x0000a550, 0x5e025f12, 0x5e025f12, 0x44001c84, 0x44001c84}, - {0x0000a554, 0x61027f12, 0x61027f12, 0x48001ce3, 0x48001ce3}, - {0x0000a558, 0x6702bf12, 0x6702bf12, 0x4c001ce5, 0x4c001ce5}, - {0x0000a55c, 0x6b02bf14, 0x6b02bf14, 0x50001ce9, 0x50001ce9}, - {0x0000a560, 0x6f02bf16, 0x6f02bf16, 0x54001ceb, 0x54001ceb}, - {0x0000a564, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a568, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a56c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a570, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a574, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a578, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a57c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a580, 0x00802220, 0x00802220, 0x00800000, 0x00800000}, - {0x0000a584, 0x04802222, 0x04802222, 0x04800002, 0x04800002}, - {0x0000a588, 0x09802421, 0x09802421, 0x08800004, 0x08800004}, - {0x0000a58c, 0x0d802621, 0x0d802621, 0x0b800200, 0x0b800200}, - {0x0000a590, 0x13804620, 0x13804620, 0x0f800202, 0x0f800202}, - {0x0000a594, 0x19804a20, 0x19804a20, 0x11800400, 0x11800400}, - {0x0000a598, 0x1d804e20, 0x1d804e20, 0x15800402, 0x15800402}, - {0x0000a59c, 0x21805420, 0x21805420, 0x19800404, 0x19800404}, - {0x0000a5a0, 0x26805e20, 0x26805e20, 0x1b800603, 0x1b800603}, - {0x0000a5a4, 0x2b805e40, 0x2b805e40, 0x1f800a02, 0x1f800a02}, - {0x0000a5a8, 0x2f805e42, 0x2f805e42, 0x23800a04, 0x23800a04}, - {0x0000a5ac, 0x33805e44, 0x33805e44, 0x26800a20, 0x26800a20}, - {0x0000a5b0, 0x38805e65, 0x38805e65, 0x2a800e20, 0x2a800e20}, - {0x0000a5b4, 0x3c805e69, 0x3c805e69, 0x2e800e22, 0x2e800e22}, - {0x0000a5b8, 0x40805e6b, 0x40805e6b, 0x31800e24, 0x31800e24}, - {0x0000a5bc, 0x44805e6d, 0x44805e6d, 0x34801640, 0x34801640}, - {0x0000a5c0, 0x49805e72, 0x49805e72, 0x38801660, 0x38801660}, - {0x0000a5c4, 0x4e805eb2, 0x4e805eb2, 0x3b801861, 0x3b801861}, - {0x0000a5c8, 0x53805f12, 0x53805f12, 0x3e801a81, 0x3e801a81}, - {0x0000a5cc, 0x59825eb2, 0x59825eb2, 0x42801a83, 0x42801a83}, - {0x0000a5d0, 0x5e825f12, 0x5e825f12, 0x44801c84, 0x44801c84}, - {0x0000a5d4, 0x61827f12, 0x61827f12, 0x48801ce3, 0x48801ce3}, - {0x0000a5d8, 0x6782bf12, 0x6782bf12, 0x4c801ce5, 0x4c801ce5}, - {0x0000a5dc, 0x6b82bf14, 0x6b82bf14, 0x50801ce9, 0x50801ce9}, - {0x0000a5e0, 0x6f82bf16, 0x6f82bf16, 0x54801ceb, 0x54801ceb}, - {0x0000a5e4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5e8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5ec, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5f0, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5f4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5f8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5fc, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x00016044, 0x056db2e4, 0x056db2e4, 0x056db2e4, 0x056db2e4}, - {0x00016048, 0x8e480001, 0x8e480001, 0x8e480001, 0x8e480001}, - {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, - {0x00016444, 0x056db2e4, 0x056db2e4, 0x056db2e4, 0x056db2e4}, - {0x00016448, 0x8e480001, 0x8e480001, 0x8e480001, 0x8e480001}, - {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, - {0x00016844, 0x056db2e4, 0x056db2e4, 0x056db2e4, 0x056db2e4}, - {0x00016848, 0x8e480001, 0x8e480001, 0x8e480001, 0x8e480001}, - {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, -}; - -static const u32 ar9300Common_rx_gain_table_2p0[][2] = { - /* Addr allmodes */ - {0x0000a000, 0x00010000}, - {0x0000a004, 0x00030002}, - {0x0000a008, 0x00050004}, - {0x0000a00c, 0x00810080}, - {0x0000a010, 0x00830082}, - {0x0000a014, 0x01810180}, - {0x0000a018, 0x01830182}, - {0x0000a01c, 0x01850184}, - {0x0000a020, 0x01890188}, - {0x0000a024, 0x018b018a}, - {0x0000a028, 0x018d018c}, - {0x0000a02c, 0x01910190}, - {0x0000a030, 0x01930192}, - {0x0000a034, 0x01950194}, - {0x0000a038, 0x038a0196}, - {0x0000a03c, 0x038c038b}, - {0x0000a040, 0x0390038d}, - {0x0000a044, 0x03920391}, - {0x0000a048, 0x03940393}, - {0x0000a04c, 0x03960395}, - {0x0000a050, 0x00000000}, - {0x0000a054, 0x00000000}, - {0x0000a058, 0x00000000}, - {0x0000a05c, 0x00000000}, - {0x0000a060, 0x00000000}, - {0x0000a064, 0x00000000}, - {0x0000a068, 0x00000000}, - {0x0000a06c, 0x00000000}, - {0x0000a070, 0x00000000}, - {0x0000a074, 0x00000000}, - {0x0000a078, 0x00000000}, - {0x0000a07c, 0x00000000}, - {0x0000a080, 0x22222229}, - {0x0000a084, 0x1d1d1d1d}, - {0x0000a088, 0x1d1d1d1d}, - {0x0000a08c, 0x1d1d1d1d}, - {0x0000a090, 0x171d1d1d}, - {0x0000a094, 0x11111717}, - {0x0000a098, 0x00030311}, - {0x0000a09c, 0x00000000}, - {0x0000a0a0, 0x00000000}, - {0x0000a0a4, 0x00000000}, - {0x0000a0a8, 0x00000000}, - {0x0000a0ac, 0x00000000}, - {0x0000a0b0, 0x00000000}, - {0x0000a0b4, 0x00000000}, - {0x0000a0b8, 0x00000000}, - {0x0000a0bc, 0x00000000}, - {0x0000a0c0, 0x001f0000}, - {0x0000a0c4, 0x01000101}, - {0x0000a0c8, 0x011e011f}, - {0x0000a0cc, 0x011c011d}, - {0x0000a0d0, 0x02030204}, - {0x0000a0d4, 0x02010202}, - {0x0000a0d8, 0x021f0200}, - {0x0000a0dc, 0x0302021e}, - {0x0000a0e0, 0x03000301}, - {0x0000a0e4, 0x031e031f}, - {0x0000a0e8, 0x0402031d}, - {0x0000a0ec, 0x04000401}, - {0x0000a0f0, 0x041e041f}, - {0x0000a0f4, 0x0502041d}, - {0x0000a0f8, 0x05000501}, - {0x0000a0fc, 0x051e051f}, - {0x0000a100, 0x06010602}, - {0x0000a104, 0x061f0600}, - {0x0000a108, 0x061d061e}, - {0x0000a10c, 0x07020703}, - {0x0000a110, 0x07000701}, - {0x0000a114, 0x00000000}, - {0x0000a118, 0x00000000}, - {0x0000a11c, 0x00000000}, - {0x0000a120, 0x00000000}, - {0x0000a124, 0x00000000}, - {0x0000a128, 0x00000000}, - {0x0000a12c, 0x00000000}, - {0x0000a130, 0x00000000}, - {0x0000a134, 0x00000000}, - {0x0000a138, 0x00000000}, - {0x0000a13c, 0x00000000}, - {0x0000a140, 0x001f0000}, - {0x0000a144, 0x01000101}, - {0x0000a148, 0x011e011f}, - {0x0000a14c, 0x011c011d}, - {0x0000a150, 0x02030204}, - {0x0000a154, 0x02010202}, - {0x0000a158, 0x021f0200}, - {0x0000a15c, 0x0302021e}, - {0x0000a160, 0x03000301}, - {0x0000a164, 0x031e031f}, - {0x0000a168, 0x0402031d}, - {0x0000a16c, 0x04000401}, - {0x0000a170, 0x041e041f}, - {0x0000a174, 0x0502041d}, - {0x0000a178, 0x05000501}, - {0x0000a17c, 0x051e051f}, - {0x0000a180, 0x06010602}, - {0x0000a184, 0x061f0600}, - {0x0000a188, 0x061d061e}, - {0x0000a18c, 0x07020703}, - {0x0000a190, 0x07000701}, - {0x0000a194, 0x00000000}, - {0x0000a198, 0x00000000}, - {0x0000a19c, 0x00000000}, - {0x0000a1a0, 0x00000000}, - {0x0000a1a4, 0x00000000}, - {0x0000a1a8, 0x00000000}, - {0x0000a1ac, 0x00000000}, - {0x0000a1b0, 0x00000000}, - {0x0000a1b4, 0x00000000}, - {0x0000a1b8, 0x00000000}, - {0x0000a1bc, 0x00000000}, - {0x0000a1c0, 0x00000000}, - {0x0000a1c4, 0x00000000}, - {0x0000a1c8, 0x00000000}, - {0x0000a1cc, 0x00000000}, - {0x0000a1d0, 0x00000000}, - {0x0000a1d4, 0x00000000}, - {0x0000a1d8, 0x00000000}, - {0x0000a1dc, 0x00000000}, - {0x0000a1e0, 0x00000000}, - {0x0000a1e4, 0x00000000}, - {0x0000a1e8, 0x00000000}, - {0x0000a1ec, 0x00000000}, - {0x0000a1f0, 0x00000396}, - {0x0000a1f4, 0x00000396}, - {0x0000a1f8, 0x00000396}, - {0x0000a1fc, 0x00000196}, - {0x0000b000, 0x00010000}, - {0x0000b004, 0x00030002}, - {0x0000b008, 0x00050004}, - {0x0000b00c, 0x00810080}, - {0x0000b010, 0x00830082}, - {0x0000b014, 0x01810180}, - {0x0000b018, 0x01830182}, - {0x0000b01c, 0x01850184}, - {0x0000b020, 0x02810280}, - {0x0000b024, 0x02830282}, - {0x0000b028, 0x02850284}, - {0x0000b02c, 0x02890288}, - {0x0000b030, 0x028b028a}, - {0x0000b034, 0x0388028c}, - {0x0000b038, 0x038a0389}, - {0x0000b03c, 0x038c038b}, - {0x0000b040, 0x0390038d}, - {0x0000b044, 0x03920391}, - {0x0000b048, 0x03940393}, - {0x0000b04c, 0x03960395}, - {0x0000b050, 0x00000000}, - {0x0000b054, 0x00000000}, - {0x0000b058, 0x00000000}, - {0x0000b05c, 0x00000000}, - {0x0000b060, 0x00000000}, - {0x0000b064, 0x00000000}, - {0x0000b068, 0x00000000}, - {0x0000b06c, 0x00000000}, - {0x0000b070, 0x00000000}, - {0x0000b074, 0x00000000}, - {0x0000b078, 0x00000000}, - {0x0000b07c, 0x00000000}, - {0x0000b080, 0x32323232}, - {0x0000b084, 0x2f2f3232}, - {0x0000b088, 0x23282a2d}, - {0x0000b08c, 0x1c1e2123}, - {0x0000b090, 0x14171919}, - {0x0000b094, 0x0e0e1214}, - {0x0000b098, 0x03050707}, - {0x0000b09c, 0x00030303}, - {0x0000b0a0, 0x00000000}, - {0x0000b0a4, 0x00000000}, - {0x0000b0a8, 0x00000000}, - {0x0000b0ac, 0x00000000}, - {0x0000b0b0, 0x00000000}, - {0x0000b0b4, 0x00000000}, - {0x0000b0b8, 0x00000000}, - {0x0000b0bc, 0x00000000}, - {0x0000b0c0, 0x003f0020}, - {0x0000b0c4, 0x00400041}, - {0x0000b0c8, 0x0140005f}, - {0x0000b0cc, 0x0160015f}, - {0x0000b0d0, 0x017e017f}, - {0x0000b0d4, 0x02410242}, - {0x0000b0d8, 0x025f0240}, - {0x0000b0dc, 0x027f0260}, - {0x0000b0e0, 0x0341027e}, - {0x0000b0e4, 0x035f0340}, - {0x0000b0e8, 0x037f0360}, - {0x0000b0ec, 0x04400441}, - {0x0000b0f0, 0x0460045f}, - {0x0000b0f4, 0x0541047f}, - {0x0000b0f8, 0x055f0540}, - {0x0000b0fc, 0x057f0560}, - {0x0000b100, 0x06400641}, - {0x0000b104, 0x0660065f}, - {0x0000b108, 0x067e067f}, - {0x0000b10c, 0x07410742}, - {0x0000b110, 0x075f0740}, - {0x0000b114, 0x077f0760}, - {0x0000b118, 0x07800781}, - {0x0000b11c, 0x07a0079f}, - {0x0000b120, 0x07c107bf}, - {0x0000b124, 0x000007c0}, - {0x0000b128, 0x00000000}, - {0x0000b12c, 0x00000000}, - {0x0000b130, 0x00000000}, - {0x0000b134, 0x00000000}, - {0x0000b138, 0x00000000}, - {0x0000b13c, 0x00000000}, - {0x0000b140, 0x003f0020}, - {0x0000b144, 0x00400041}, - {0x0000b148, 0x0140005f}, - {0x0000b14c, 0x0160015f}, - {0x0000b150, 0x017e017f}, - {0x0000b154, 0x02410242}, - {0x0000b158, 0x025f0240}, - {0x0000b15c, 0x027f0260}, - {0x0000b160, 0x0341027e}, - {0x0000b164, 0x035f0340}, - {0x0000b168, 0x037f0360}, - {0x0000b16c, 0x04400441}, - {0x0000b170, 0x0460045f}, - {0x0000b174, 0x0541047f}, - {0x0000b178, 0x055f0540}, - {0x0000b17c, 0x057f0560}, - {0x0000b180, 0x06400641}, - {0x0000b184, 0x0660065f}, - {0x0000b188, 0x067e067f}, - {0x0000b18c, 0x07410742}, - {0x0000b190, 0x075f0740}, - {0x0000b194, 0x077f0760}, - {0x0000b198, 0x07800781}, - {0x0000b19c, 0x07a0079f}, - {0x0000b1a0, 0x07c107bf}, - {0x0000b1a4, 0x000007c0}, - {0x0000b1a8, 0x00000000}, - {0x0000b1ac, 0x00000000}, - {0x0000b1b0, 0x00000000}, - {0x0000b1b4, 0x00000000}, - {0x0000b1b8, 0x00000000}, - {0x0000b1bc, 0x00000000}, - {0x0000b1c0, 0x00000000}, - {0x0000b1c4, 0x00000000}, - {0x0000b1c8, 0x00000000}, - {0x0000b1cc, 0x00000000}, - {0x0000b1d0, 0x00000000}, - {0x0000b1d4, 0x00000000}, - {0x0000b1d8, 0x00000000}, - {0x0000b1dc, 0x00000000}, - {0x0000b1e0, 0x00000000}, - {0x0000b1e4, 0x00000000}, - {0x0000b1e8, 0x00000000}, - {0x0000b1ec, 0x00000000}, - {0x0000b1f0, 0x00000396}, - {0x0000b1f4, 0x00000396}, - {0x0000b1f8, 0x00000396}, - {0x0000b1fc, 0x00000196}, -}; - -static const u32 ar9300Modes_low_ob_db_tx_gain_table_2p0[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, - {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, - {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, - {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, - {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202}, - {0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400}, - {0x0000a518, 0x21020220, 0x21020220, 0x16000402, 0x16000402}, - {0x0000a51c, 0x27020223, 0x27020223, 0x19000404, 0x19000404}, - {0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603}, - {0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02}, - {0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04}, - {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x28000a20, 0x28000a20}, - {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2c000e20, 0x2c000e20}, - {0x0000a534, 0x4202242a, 0x4202242a, 0x30000e22, 0x30000e22}, - {0x0000a538, 0x4702244a, 0x4702244a, 0x34000e24, 0x34000e24}, - {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x38001640, 0x38001640}, - {0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660}, - {0x0000a544, 0x5302266c, 0x5302266c, 0x3f001861, 0x3f001861}, - {0x0000a548, 0x5702286c, 0x5702286c, 0x43001a81, 0x43001a81}, - {0x0000a54c, 0x5c04286b, 0x5c04286b, 0x47001a83, 0x47001a83}, - {0x0000a550, 0x61042a6c, 0x61042a6c, 0x4a001c84, 0x4a001c84}, - {0x0000a554, 0x66062a6c, 0x66062a6c, 0x4e001ce3, 0x4e001ce3}, - {0x0000a558, 0x6b062e6c, 0x6b062e6c, 0x52001ce5, 0x52001ce5}, - {0x0000a55c, 0x7006308c, 0x7006308c, 0x56001ce9, 0x56001ce9}, - {0x0000a560, 0x730a308a, 0x730a308a, 0x5a001ceb, 0x5a001ceb}, - {0x0000a564, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a568, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a56c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a570, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a574, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a578, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a57c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000}, - {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002}, - {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004}, - {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200}, - {0x0000a590, 0x16800220, 0x16800220, 0x0f800202, 0x0f800202}, - {0x0000a594, 0x1c800223, 0x1c800223, 0x12800400, 0x12800400}, - {0x0000a598, 0x21820220, 0x21820220, 0x16800402, 0x16800402}, - {0x0000a59c, 0x27820223, 0x27820223, 0x19800404, 0x19800404}, - {0x0000a5a0, 0x2b822220, 0x2b822220, 0x1c800603, 0x1c800603}, - {0x0000a5a4, 0x2f822222, 0x2f822222, 0x21800a02, 0x21800a02}, - {0x0000a5a8, 0x34822225, 0x34822225, 0x25800a04, 0x25800a04}, - {0x0000a5ac, 0x3a82222a, 0x3a82222a, 0x28800a20, 0x28800a20}, - {0x0000a5b0, 0x3e82222c, 0x3e82222c, 0x2c800e20, 0x2c800e20}, - {0x0000a5b4, 0x4282242a, 0x4282242a, 0x30800e22, 0x30800e22}, - {0x0000a5b8, 0x4782244a, 0x4782244a, 0x34800e24, 0x34800e24}, - {0x0000a5bc, 0x4b82244c, 0x4b82244c, 0x38801640, 0x38801640}, - {0x0000a5c0, 0x4e82246c, 0x4e82246c, 0x3c801660, 0x3c801660}, - {0x0000a5c4, 0x5382266c, 0x5382266c, 0x3f801861, 0x3f801861}, - {0x0000a5c8, 0x5782286c, 0x5782286c, 0x43801a81, 0x43801a81}, - {0x0000a5cc, 0x5c84286b, 0x5c84286b, 0x47801a83, 0x47801a83}, - {0x0000a5d0, 0x61842a6c, 0x61842a6c, 0x4a801c84, 0x4a801c84}, - {0x0000a5d4, 0x66862a6c, 0x66862a6c, 0x4e801ce3, 0x4e801ce3}, - {0x0000a5d8, 0x6b862e6c, 0x6b862e6c, 0x52801ce5, 0x52801ce5}, - {0x0000a5dc, 0x7086308c, 0x7086308c, 0x56801ce9, 0x56801ce9}, - {0x0000a5e0, 0x738a308a, 0x738a308a, 0x5a801ceb, 0x5a801ceb}, - {0x0000a5e4, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, - {0x0000a5e8, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, - {0x0000a5ec, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, - {0x0000a5f0, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, - {0x0000a5f4, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, - {0x0000a5f8, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, - {0x0000a5fc, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, - {0x00016044, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, - {0x00016048, 0x64000001, 0x64000001, 0x64000001, 0x64000001}, - {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, - {0x00016444, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, - {0x00016448, 0x64000001, 0x64000001, 0x64000001, 0x64000001}, - {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, - {0x00016844, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, - {0x00016848, 0x64000001, 0x64000001, 0x64000001, 0x64000001}, - {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, -}; - -static const u32 ar9300_2p0_mac_core[][2] = { - /* Addr allmodes */ - {0x00000008, 0x00000000}, - {0x00000030, 0x00020085}, - {0x00000034, 0x00000005}, - {0x00000040, 0x00000000}, - {0x00000044, 0x00000000}, - {0x00000048, 0x00000008}, - {0x0000004c, 0x00000010}, - {0x00000050, 0x00000000}, - {0x00001040, 0x002ffc0f}, - {0x00001044, 0x002ffc0f}, - {0x00001048, 0x002ffc0f}, - {0x0000104c, 0x002ffc0f}, - {0x00001050, 0x002ffc0f}, - {0x00001054, 0x002ffc0f}, - {0x00001058, 0x002ffc0f}, - {0x0000105c, 0x002ffc0f}, - {0x00001060, 0x002ffc0f}, - {0x00001064, 0x002ffc0f}, - {0x000010f0, 0x00000100}, - {0x00001270, 0x00000000}, - {0x000012b0, 0x00000000}, - {0x000012f0, 0x00000000}, - {0x0000143c, 0x00000000}, - {0x0000147c, 0x00000000}, - {0x00008000, 0x00000000}, - {0x00008004, 0x00000000}, - {0x00008008, 0x00000000}, - {0x0000800c, 0x00000000}, - {0x00008018, 0x00000000}, - {0x00008020, 0x00000000}, - {0x00008038, 0x00000000}, - {0x0000803c, 0x00000000}, - {0x00008040, 0x00000000}, - {0x00008044, 0x00000000}, - {0x00008048, 0x00000000}, - {0x0000804c, 0xffffffff}, - {0x00008054, 0x00000000}, - {0x00008058, 0x00000000}, - {0x0000805c, 0x000fc78f}, - {0x00008060, 0x0000000f}, - {0x00008064, 0x00000000}, - {0x00008070, 0x00000310}, - {0x00008074, 0x00000020}, - {0x00008078, 0x00000000}, - {0x0000809c, 0x0000000f}, - {0x000080a0, 0x00000000}, - {0x000080a4, 0x02ff0000}, - {0x000080a8, 0x0e070605}, - {0x000080ac, 0x0000000d}, - {0x000080b0, 0x00000000}, - {0x000080b4, 0x00000000}, - {0x000080b8, 0x00000000}, - {0x000080bc, 0x00000000}, - {0x000080c0, 0x2a800000}, - {0x000080c4, 0x06900168}, - {0x000080c8, 0x13881c20}, - {0x000080cc, 0x01f40000}, - {0x000080d0, 0x00252500}, - {0x000080d4, 0x00a00000}, - {0x000080d8, 0x00400000}, - {0x000080dc, 0x00000000}, - {0x000080e0, 0xffffffff}, - {0x000080e4, 0x0000ffff}, - {0x000080e8, 0x3f3f3f3f}, - {0x000080ec, 0x00000000}, - {0x000080f0, 0x00000000}, - {0x000080f4, 0x00000000}, - {0x000080fc, 0x00020000}, - {0x00008100, 0x00000000}, - {0x00008108, 0x00000052}, - {0x0000810c, 0x00000000}, - {0x00008110, 0x00000000}, - {0x00008114, 0x000007ff}, - {0x00008118, 0x000000aa}, - {0x0000811c, 0x00003210}, - {0x00008124, 0x00000000}, - {0x00008128, 0x00000000}, - {0x0000812c, 0x00000000}, - {0x00008130, 0x00000000}, - {0x00008134, 0x00000000}, - {0x00008138, 0x00000000}, - {0x0000813c, 0x0000ffff}, - {0x00008144, 0xffffffff}, - {0x00008168, 0x00000000}, - {0x0000816c, 0x00000000}, - {0x00008170, 0x18486200}, - {0x00008174, 0x33332210}, - {0x00008178, 0x00000000}, - {0x0000817c, 0x00020000}, - {0x000081c0, 0x00000000}, - {0x000081c4, 0x33332210}, - {0x000081c8, 0x00000000}, - {0x000081cc, 0x00000000}, - {0x000081d4, 0x00000000}, - {0x000081ec, 0x00000000}, - {0x000081f0, 0x00000000}, - {0x000081f4, 0x00000000}, - {0x000081f8, 0x00000000}, - {0x000081fc, 0x00000000}, - {0x00008240, 0x00100000}, - {0x00008244, 0x0010f424}, - {0x00008248, 0x00000800}, - {0x0000824c, 0x0001e848}, - {0x00008250, 0x00000000}, - {0x00008254, 0x00000000}, - {0x00008258, 0x00000000}, - {0x0000825c, 0x40000000}, - {0x00008260, 0x00080922}, - {0x00008264, 0x98a00010}, - {0x00008268, 0xffffffff}, - {0x0000826c, 0x0000ffff}, - {0x00008270, 0x00000000}, - {0x00008274, 0x40000000}, - {0x00008278, 0x003e4180}, - {0x0000827c, 0x00000004}, - {0x00008284, 0x0000002c}, - {0x00008288, 0x0000002c}, - {0x0000828c, 0x000000ff}, - {0x00008294, 0x00000000}, - {0x00008298, 0x00000000}, - {0x0000829c, 0x00000000}, - {0x00008300, 0x00000140}, - {0x00008314, 0x00000000}, - {0x0000831c, 0x0000010d}, - {0x00008328, 0x00000000}, - {0x0000832c, 0x00000007}, - {0x00008330, 0x00000302}, - {0x00008334, 0x00000700}, - {0x00008338, 0x00ff0000}, - {0x0000833c, 0x02400000}, - {0x00008340, 0x000107ff}, - {0x00008344, 0xaa48105b}, - {0x00008348, 0x008f0000}, - {0x0000835c, 0x00000000}, - {0x00008360, 0xffffffff}, - {0x00008364, 0xffffffff}, - {0x00008368, 0x00000000}, - {0x00008370, 0x00000000}, - {0x00008374, 0x000000ff}, - {0x00008378, 0x00000000}, - {0x0000837c, 0x00000000}, - {0x00008380, 0xffffffff}, - {0x00008384, 0xffffffff}, - {0x00008390, 0xffffffff}, - {0x00008394, 0xffffffff}, - {0x00008398, 0x00000000}, - {0x0000839c, 0x00000000}, - {0x000083a0, 0x00000000}, - {0x000083a4, 0x0000fa14}, - {0x000083a8, 0x000f0c00}, - {0x000083ac, 0x33332210}, - {0x000083b0, 0x33332210}, - {0x000083b4, 0x33332210}, - {0x000083b8, 0x33332210}, - {0x000083bc, 0x00000000}, - {0x000083c0, 0x00000000}, - {0x000083c4, 0x00000000}, - {0x000083c8, 0x00000000}, - {0x000083cc, 0x00000200}, - {0x000083d0, 0x000301ff}, -}; - -static const u32 ar9300Common_wo_xlna_rx_gain_table_2p0[][2] = { - /* Addr allmodes */ - {0x0000a000, 0x00010000}, - {0x0000a004, 0x00030002}, - {0x0000a008, 0x00050004}, - {0x0000a00c, 0x00810080}, - {0x0000a010, 0x00830082}, - {0x0000a014, 0x01810180}, - {0x0000a018, 0x01830182}, - {0x0000a01c, 0x01850184}, - {0x0000a020, 0x01890188}, - {0x0000a024, 0x018b018a}, - {0x0000a028, 0x018d018c}, - {0x0000a02c, 0x03820190}, - {0x0000a030, 0x03840383}, - {0x0000a034, 0x03880385}, - {0x0000a038, 0x038a0389}, - {0x0000a03c, 0x038c038b}, - {0x0000a040, 0x0390038d}, - {0x0000a044, 0x03920391}, - {0x0000a048, 0x03940393}, - {0x0000a04c, 0x03960395}, - {0x0000a050, 0x00000000}, - {0x0000a054, 0x00000000}, - {0x0000a058, 0x00000000}, - {0x0000a05c, 0x00000000}, - {0x0000a060, 0x00000000}, - {0x0000a064, 0x00000000}, - {0x0000a068, 0x00000000}, - {0x0000a06c, 0x00000000}, - {0x0000a070, 0x00000000}, - {0x0000a074, 0x00000000}, - {0x0000a078, 0x00000000}, - {0x0000a07c, 0x00000000}, - {0x0000a080, 0x29292929}, - {0x0000a084, 0x29292929}, - {0x0000a088, 0x29292929}, - {0x0000a08c, 0x29292929}, - {0x0000a090, 0x22292929}, - {0x0000a094, 0x1d1d2222}, - {0x0000a098, 0x0c111117}, - {0x0000a09c, 0x00030303}, - {0x0000a0a0, 0x00000000}, - {0x0000a0a4, 0x00000000}, - {0x0000a0a8, 0x00000000}, - {0x0000a0ac, 0x00000000}, - {0x0000a0b0, 0x00000000}, - {0x0000a0b4, 0x00000000}, - {0x0000a0b8, 0x00000000}, - {0x0000a0bc, 0x00000000}, - {0x0000a0c0, 0x001f0000}, - {0x0000a0c4, 0x01000101}, - {0x0000a0c8, 0x011e011f}, - {0x0000a0cc, 0x011c011d}, - {0x0000a0d0, 0x02030204}, - {0x0000a0d4, 0x02010202}, - {0x0000a0d8, 0x021f0200}, - {0x0000a0dc, 0x0302021e}, - {0x0000a0e0, 0x03000301}, - {0x0000a0e4, 0x031e031f}, - {0x0000a0e8, 0x0402031d}, - {0x0000a0ec, 0x04000401}, - {0x0000a0f0, 0x041e041f}, - {0x0000a0f4, 0x0502041d}, - {0x0000a0f8, 0x05000501}, - {0x0000a0fc, 0x051e051f}, - {0x0000a100, 0x06010602}, - {0x0000a104, 0x061f0600}, - {0x0000a108, 0x061d061e}, - {0x0000a10c, 0x07020703}, - {0x0000a110, 0x07000701}, - {0x0000a114, 0x00000000}, - {0x0000a118, 0x00000000}, - {0x0000a11c, 0x00000000}, - {0x0000a120, 0x00000000}, - {0x0000a124, 0x00000000}, - {0x0000a128, 0x00000000}, - {0x0000a12c, 0x00000000}, - {0x0000a130, 0x00000000}, - {0x0000a134, 0x00000000}, - {0x0000a138, 0x00000000}, - {0x0000a13c, 0x00000000}, - {0x0000a140, 0x001f0000}, - {0x0000a144, 0x01000101}, - {0x0000a148, 0x011e011f}, - {0x0000a14c, 0x011c011d}, - {0x0000a150, 0x02030204}, - {0x0000a154, 0x02010202}, - {0x0000a158, 0x021f0200}, - {0x0000a15c, 0x0302021e}, - {0x0000a160, 0x03000301}, - {0x0000a164, 0x031e031f}, - {0x0000a168, 0x0402031d}, - {0x0000a16c, 0x04000401}, - {0x0000a170, 0x041e041f}, - {0x0000a174, 0x0502041d}, - {0x0000a178, 0x05000501}, - {0x0000a17c, 0x051e051f}, - {0x0000a180, 0x06010602}, - {0x0000a184, 0x061f0600}, - {0x0000a188, 0x061d061e}, - {0x0000a18c, 0x07020703}, - {0x0000a190, 0x07000701}, - {0x0000a194, 0x00000000}, - {0x0000a198, 0x00000000}, - {0x0000a19c, 0x00000000}, - {0x0000a1a0, 0x00000000}, - {0x0000a1a4, 0x00000000}, - {0x0000a1a8, 0x00000000}, - {0x0000a1ac, 0x00000000}, - {0x0000a1b0, 0x00000000}, - {0x0000a1b4, 0x00000000}, - {0x0000a1b8, 0x00000000}, - {0x0000a1bc, 0x00000000}, - {0x0000a1c0, 0x00000000}, - {0x0000a1c4, 0x00000000}, - {0x0000a1c8, 0x00000000}, - {0x0000a1cc, 0x00000000}, - {0x0000a1d0, 0x00000000}, - {0x0000a1d4, 0x00000000}, - {0x0000a1d8, 0x00000000}, - {0x0000a1dc, 0x00000000}, - {0x0000a1e0, 0x00000000}, - {0x0000a1e4, 0x00000000}, - {0x0000a1e8, 0x00000000}, - {0x0000a1ec, 0x00000000}, - {0x0000a1f0, 0x00000396}, - {0x0000a1f4, 0x00000396}, - {0x0000a1f8, 0x00000396}, - {0x0000a1fc, 0x00000196}, - {0x0000b000, 0x00010000}, - {0x0000b004, 0x00030002}, - {0x0000b008, 0x00050004}, - {0x0000b00c, 0x00810080}, - {0x0000b010, 0x00830082}, - {0x0000b014, 0x01810180}, - {0x0000b018, 0x01830182}, - {0x0000b01c, 0x01850184}, - {0x0000b020, 0x02810280}, - {0x0000b024, 0x02830282}, - {0x0000b028, 0x02850284}, - {0x0000b02c, 0x02890288}, - {0x0000b030, 0x028b028a}, - {0x0000b034, 0x0388028c}, - {0x0000b038, 0x038a0389}, - {0x0000b03c, 0x038c038b}, - {0x0000b040, 0x0390038d}, - {0x0000b044, 0x03920391}, - {0x0000b048, 0x03940393}, - {0x0000b04c, 0x03960395}, - {0x0000b050, 0x00000000}, - {0x0000b054, 0x00000000}, - {0x0000b058, 0x00000000}, - {0x0000b05c, 0x00000000}, - {0x0000b060, 0x00000000}, - {0x0000b064, 0x00000000}, - {0x0000b068, 0x00000000}, - {0x0000b06c, 0x00000000}, - {0x0000b070, 0x00000000}, - {0x0000b074, 0x00000000}, - {0x0000b078, 0x00000000}, - {0x0000b07c, 0x00000000}, - {0x0000b080, 0x32323232}, - {0x0000b084, 0x2f2f3232}, - {0x0000b088, 0x23282a2d}, - {0x0000b08c, 0x1c1e2123}, - {0x0000b090, 0x14171919}, - {0x0000b094, 0x0e0e1214}, - {0x0000b098, 0x03050707}, - {0x0000b09c, 0x00030303}, - {0x0000b0a0, 0x00000000}, - {0x0000b0a4, 0x00000000}, - {0x0000b0a8, 0x00000000}, - {0x0000b0ac, 0x00000000}, - {0x0000b0b0, 0x00000000}, - {0x0000b0b4, 0x00000000}, - {0x0000b0b8, 0x00000000}, - {0x0000b0bc, 0x00000000}, - {0x0000b0c0, 0x003f0020}, - {0x0000b0c4, 0x00400041}, - {0x0000b0c8, 0x0140005f}, - {0x0000b0cc, 0x0160015f}, - {0x0000b0d0, 0x017e017f}, - {0x0000b0d4, 0x02410242}, - {0x0000b0d8, 0x025f0240}, - {0x0000b0dc, 0x027f0260}, - {0x0000b0e0, 0x0341027e}, - {0x0000b0e4, 0x035f0340}, - {0x0000b0e8, 0x037f0360}, - {0x0000b0ec, 0x04400441}, - {0x0000b0f0, 0x0460045f}, - {0x0000b0f4, 0x0541047f}, - {0x0000b0f8, 0x055f0540}, - {0x0000b0fc, 0x057f0560}, - {0x0000b100, 0x06400641}, - {0x0000b104, 0x0660065f}, - {0x0000b108, 0x067e067f}, - {0x0000b10c, 0x07410742}, - {0x0000b110, 0x075f0740}, - {0x0000b114, 0x077f0760}, - {0x0000b118, 0x07800781}, - {0x0000b11c, 0x07a0079f}, - {0x0000b120, 0x07c107bf}, - {0x0000b124, 0x000007c0}, - {0x0000b128, 0x00000000}, - {0x0000b12c, 0x00000000}, - {0x0000b130, 0x00000000}, - {0x0000b134, 0x00000000}, - {0x0000b138, 0x00000000}, - {0x0000b13c, 0x00000000}, - {0x0000b140, 0x003f0020}, - {0x0000b144, 0x00400041}, - {0x0000b148, 0x0140005f}, - {0x0000b14c, 0x0160015f}, - {0x0000b150, 0x017e017f}, - {0x0000b154, 0x02410242}, - {0x0000b158, 0x025f0240}, - {0x0000b15c, 0x027f0260}, - {0x0000b160, 0x0341027e}, - {0x0000b164, 0x035f0340}, - {0x0000b168, 0x037f0360}, - {0x0000b16c, 0x04400441}, - {0x0000b170, 0x0460045f}, - {0x0000b174, 0x0541047f}, - {0x0000b178, 0x055f0540}, - {0x0000b17c, 0x057f0560}, - {0x0000b180, 0x06400641}, - {0x0000b184, 0x0660065f}, - {0x0000b188, 0x067e067f}, - {0x0000b18c, 0x07410742}, - {0x0000b190, 0x075f0740}, - {0x0000b194, 0x077f0760}, - {0x0000b198, 0x07800781}, - {0x0000b19c, 0x07a0079f}, - {0x0000b1a0, 0x07c107bf}, - {0x0000b1a4, 0x000007c0}, - {0x0000b1a8, 0x00000000}, - {0x0000b1ac, 0x00000000}, - {0x0000b1b0, 0x00000000}, - {0x0000b1b4, 0x00000000}, - {0x0000b1b8, 0x00000000}, - {0x0000b1bc, 0x00000000}, - {0x0000b1c0, 0x00000000}, - {0x0000b1c4, 0x00000000}, - {0x0000b1c8, 0x00000000}, - {0x0000b1cc, 0x00000000}, - {0x0000b1d0, 0x00000000}, - {0x0000b1d4, 0x00000000}, - {0x0000b1d8, 0x00000000}, - {0x0000b1dc, 0x00000000}, - {0x0000b1e0, 0x00000000}, - {0x0000b1e4, 0x00000000}, - {0x0000b1e8, 0x00000000}, - {0x0000b1ec, 0x00000000}, - {0x0000b1f0, 0x00000396}, - {0x0000b1f4, 0x00000396}, - {0x0000b1f8, 0x00000396}, - {0x0000b1fc, 0x00000196}, -}; - -static const u32 ar9300_2p0_soc_preamble[][2] = { - /* Addr allmodes */ - {0x000040a4, 0x00a0c1c9}, - {0x00007008, 0x00000000}, - {0x00007020, 0x00000000}, - {0x00007034, 0x00000002}, - {0x00007038, 0x000004c2}, -}; - -static const u32 ar9300PciePhy_pll_on_clkreq_disable_L1_2p0[][2] = { - /* Addr allmodes */ - {0x00004040, 0x08212e5e}, - {0x00004040, 0x0008003b}, - {0x00004044, 0x00000000}, -}; - -static const u32 ar9300PciePhy_clkreq_enable_L1_2p0[][2] = { - /* Addr allmodes */ - {0x00004040, 0x08253e5e}, - {0x00004040, 0x0008003b}, - {0x00004044, 0x00000000}, -}; - -static const u32 ar9300PciePhy_clkreq_disable_L1_2p0[][2] = { - /* Addr allmodes */ - {0x00004040, 0x08213e5e}, - {0x00004040, 0x0008003b}, - {0x00004044, 0x00000000}, -}; - -#endif /* INITVALS_9003_2P0_H */ diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index 02c970819f79..c2a057156bfa 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -16,7 +16,6 @@ #include "hw.h" #include "ar9003_mac.h" -#include "ar9003_2p0_initvals.h" #include "ar9003_2p2_initvals.h" /* General hardware code for the AR9003 hadware family */ @@ -32,79 +31,12 @@ static bool ar9003_hw_macversion_supported(u32 macversion) return false; } -/* AR9003 2.0 */ -static void ar9003_2p0_hw_init_mode_regs(struct ath_hw *ah) -{ - /* mac */ - INIT_INI_ARRAY(&ah->iniMac[ATH_INI_PRE], NULL, 0, 0); - INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], - ar9300_2p0_mac_core, - ARRAY_SIZE(ar9300_2p0_mac_core), 2); - INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST], - ar9300_2p0_mac_postamble, - ARRAY_SIZE(ar9300_2p0_mac_postamble), 5); - - /* bb */ - INIT_INI_ARRAY(&ah->iniBB[ATH_INI_PRE], NULL, 0, 0); - INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE], - ar9300_2p0_baseband_core, - ARRAY_SIZE(ar9300_2p0_baseband_core), 2); - INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST], - ar9300_2p0_baseband_postamble, - ARRAY_SIZE(ar9300_2p0_baseband_postamble), 5); - - /* radio */ - INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_PRE], NULL, 0, 0); - INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE], - ar9300_2p0_radio_core, - ARRAY_SIZE(ar9300_2p0_radio_core), 2); - INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST], - ar9300_2p0_radio_postamble, - ARRAY_SIZE(ar9300_2p0_radio_postamble), 5); - - /* soc */ - INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_PRE], - ar9300_2p0_soc_preamble, - ARRAY_SIZE(ar9300_2p0_soc_preamble), 2); - INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_CORE], NULL, 0, 0); - INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_POST], - ar9300_2p0_soc_postamble, - ARRAY_SIZE(ar9300_2p0_soc_postamble), 5); - - /* rx/tx gain */ - INIT_INI_ARRAY(&ah->iniModesRxGain, - ar9300Common_rx_gain_table_2p0, - ARRAY_SIZE(ar9300Common_rx_gain_table_2p0), 2); - INIT_INI_ARRAY(&ah->iniModesTxGain, - ar9300Modes_lowest_ob_db_tx_gain_table_2p0, - ARRAY_SIZE(ar9300Modes_lowest_ob_db_tx_gain_table_2p0), - 5); - - /* Load PCIE SERDES settings from INI */ - - /* Awake Setting */ - - INIT_INI_ARRAY(&ah->iniPcieSerdes, - ar9300PciePhy_pll_on_clkreq_disable_L1_2p0, - ARRAY_SIZE(ar9300PciePhy_pll_on_clkreq_disable_L1_2p0), - 2); - - /* Sleep Setting */ - - INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, - ar9300PciePhy_clkreq_enable_L1_2p0, - ARRAY_SIZE(ar9300PciePhy_clkreq_enable_L1_2p0), - 2); - - /* Fast clock modal settings */ - INIT_INI_ARRAY(&ah->iniModesAdditional, - ar9300Modes_fast_clock_2p0, - ARRAY_SIZE(ar9300Modes_fast_clock_2p0), - 3); -} - -/* AR9003 2.2 */ -static void ar9003_2p2_hw_init_mode_regs(struct ath_hw *ah) +/* + * The AR9003 family uses a new INI format (pre, core, post + * arrays per subsystem). This provides support for the + * AR9003 2.2 chipsets. + */ +static void ar9003_hw_init_mode_regs(struct ath_hw *ah) { /* mac */ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_PRE], NULL, 0, 0); @@ -174,57 +106,27 @@ static void ar9003_2p2_hw_init_mode_regs(struct ath_hw *ah) 3); } -/* - * The AR9003 family uses a new INI format (pre, core, post - * arrays per subsystem). - */ -static void ar9003_hw_init_mode_regs(struct ath_hw *ah) -{ - if (AR_SREV_9300_20(ah)) - ar9003_2p0_hw_init_mode_regs(ah); - else - ar9003_2p2_hw_init_mode_regs(ah); -} - static void ar9003_tx_gain_table_apply(struct ath_hw *ah) { switch (ar9003_hw_get_tx_gain_idx(ah)) { case 0: default: - if (AR_SREV_9300_20(ah)) - INIT_INI_ARRAY(&ah->iniModesTxGain, - ar9300Modes_lowest_ob_db_tx_gain_table_2p0, - ARRAY_SIZE(ar9300Modes_lowest_ob_db_tx_gain_table_2p0), - 5); - else - INIT_INI_ARRAY(&ah->iniModesTxGain, - ar9300Modes_lowest_ob_db_tx_gain_table_2p2, - ARRAY_SIZE(ar9300Modes_lowest_ob_db_tx_gain_table_2p2), - 5); + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9300Modes_lowest_ob_db_tx_gain_table_2p2, + ARRAY_SIZE(ar9300Modes_lowest_ob_db_tx_gain_table_2p2), + 5); break; case 1: - if (AR_SREV_9300_20(ah)) - INIT_INI_ARRAY(&ah->iniModesTxGain, - ar9300Modes_high_ob_db_tx_gain_table_2p0, - ARRAY_SIZE(ar9300Modes_high_ob_db_tx_gain_table_2p0), - 5); - else - INIT_INI_ARRAY(&ah->iniModesTxGain, - ar9300Modes_high_ob_db_tx_gain_table_2p2, - ARRAY_SIZE(ar9300Modes_high_ob_db_tx_gain_table_2p2), - 5); + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9300Modes_high_ob_db_tx_gain_table_2p2, + ARRAY_SIZE(ar9300Modes_high_ob_db_tx_gain_table_2p2), + 5); break; case 2: - if (AR_SREV_9300_20(ah)) - INIT_INI_ARRAY(&ah->iniModesTxGain, - ar9300Modes_low_ob_db_tx_gain_table_2p0, - ARRAY_SIZE(ar9300Modes_low_ob_db_tx_gain_table_2p0), - 5); - else - INIT_INI_ARRAY(&ah->iniModesTxGain, - ar9300Modes_low_ob_db_tx_gain_table_2p2, - ARRAY_SIZE(ar9300Modes_low_ob_db_tx_gain_table_2p2), - 5); + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9300Modes_low_ob_db_tx_gain_table_2p2, + ARRAY_SIZE(ar9300Modes_low_ob_db_tx_gain_table_2p2), + 5); break; } } @@ -234,28 +136,16 @@ static void ar9003_rx_gain_table_apply(struct ath_hw *ah) switch (ar9003_hw_get_rx_gain_idx(ah)) { case 0: default: - if (AR_SREV_9300_20(ah)) - INIT_INI_ARRAY(&ah->iniModesRxGain, - ar9300Common_rx_gain_table_2p0, - ARRAY_SIZE(ar9300Common_rx_gain_table_2p0), - 2); - else - INIT_INI_ARRAY(&ah->iniModesRxGain, - ar9300Common_rx_gain_table_2p2, - ARRAY_SIZE(ar9300Common_rx_gain_table_2p2), - 2); + INIT_INI_ARRAY(&ah->iniModesRxGain, + ar9300Common_rx_gain_table_2p2, + ARRAY_SIZE(ar9300Common_rx_gain_table_2p2), + 2); break; case 1: - if (AR_SREV_9300_20(ah)) - INIT_INI_ARRAY(&ah->iniModesRxGain, - ar9300Common_wo_xlna_rx_gain_table_2p0, - ARRAY_SIZE(ar9300Common_wo_xlna_rx_gain_table_2p0), - 2); - else - INIT_INI_ARRAY(&ah->iniModesRxGain, - ar9300Common_wo_xlna_rx_gain_table_2p2, - ARRAY_SIZE(ar9300Common_wo_xlna_rx_gain_table_2p2), - 2); + INIT_INI_ARRAY(&ah->iniModesRxGain, + ar9300Common_wo_xlna_rx_gain_table_2p2, + ARRAY_SIZE(ar9300Common_wo_xlna_rx_gain_table_2p2), + 2); break; } } diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index 017617894533..42976b0a01c1 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -853,9 +853,6 @@ #define AR_SREV_9300(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9300)) -#define AR_SREV_9300_20(_ah) \ - (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9300) && \ - ((_ah)->hw_version.macRev == AR_SREV_REVISION_9300_20)) #define AR_SREV_9300_20_OR_LATER(_ah) \ (((_ah)->hw_version.macVersion > AR_SREV_VERSION_9300) || \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9300) && \ -- cgit v1.2.3 From c1739eb3e61e160f124bc842c219011916f63068 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Thu, 14 Oct 2010 12:45:29 -0700 Subject: ath9k: Remove bf_dmacontext. The bf_dmacontext seems to be totally useless and duplicated by bf_buf_addr. Remove it entirely, use bf_buf_addr in its place. Signed-off-by: Ben Greear Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 3 +-- drivers/net/wireless/ath/ath9k/beacon.c | 16 +++++++--------- drivers/net/wireless/ath/ath9k/recv.c | 2 -- drivers/net/wireless/ath/ath9k/xmit.c | 11 ++++------- 4 files changed, 12 insertions(+), 20 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index e435c1d38826..973c919fdd27 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -239,12 +239,11 @@ struct ath_buf { struct sk_buff *bf_mpdu; /* enclosing frame structure */ void *bf_desc; /* virtual addr of desc */ dma_addr_t bf_daddr; /* physical addr of desc */ - dma_addr_t bf_buf_addr; /* physical addr of data buffer */ + dma_addr_t bf_buf_addr; /* physical addr of data buffer, for DMA */ bool bf_stale; bool bf_tx_aborted; u16 bf_flags; struct ath_buf_state bf_state; - dma_addr_t bf_dmacontext; struct ath_wiphy *aphy; }; diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 081192e78a46..af8fedda001b 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -136,7 +136,7 @@ static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw, bf = avp->av_bcbuf; skb = bf->bf_mpdu; if (skb) { - dma_unmap_single(sc->dev, bf->bf_dmacontext, + dma_unmap_single(sc->dev, bf->bf_buf_addr, skb->len, DMA_TO_DEVICE); dev_kfree_skb_any(skb); } @@ -162,9 +162,8 @@ static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw, hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no); } - bf->bf_buf_addr = bf->bf_dmacontext = - dma_map_single(sc->dev, skb->data, - skb->len, DMA_TO_DEVICE); + bf->bf_buf_addr = dma_map_single(sc->dev, skb->data, + skb->len, DMA_TO_DEVICE); if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { dev_kfree_skb_any(skb); bf->bf_mpdu = NULL; @@ -252,7 +251,7 @@ int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif) bf = avp->av_bcbuf; if (bf->bf_mpdu != NULL) { skb = bf->bf_mpdu; - dma_unmap_single(sc->dev, bf->bf_dmacontext, + dma_unmap_single(sc->dev, bf->bf_buf_addr, skb->len, DMA_TO_DEVICE); dev_kfree_skb_any(skb); bf->bf_mpdu = NULL; @@ -296,9 +295,8 @@ int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif) avp->tsf_adjust = cpu_to_le64(0); bf->bf_mpdu = skb; - bf->bf_buf_addr = bf->bf_dmacontext = - dma_map_single(sc->dev, skb->data, - skb->len, DMA_TO_DEVICE); + bf->bf_buf_addr = dma_map_single(sc->dev, skb->data, + skb->len, DMA_TO_DEVICE); if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { dev_kfree_skb_any(skb); bf->bf_mpdu = NULL; @@ -324,7 +322,7 @@ void ath_beacon_return(struct ath_softc *sc, struct ath_vif *avp) bf = avp->av_bcbuf; if (bf->bf_mpdu != NULL) { struct sk_buff *skb = bf->bf_mpdu; - dma_unmap_single(sc->dev, bf->bf_dmacontext, + dma_unmap_single(sc->dev, bf->bf_buf_addr, skb->len, DMA_TO_DEVICE); dev_kfree_skb_any(skb); bf->bf_mpdu = NULL; diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 7c90eaf9ec55..2e427522672d 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -363,7 +363,6 @@ int ath_rx_init(struct ath_softc *sc, int nbufs) error = -ENOMEM; goto err; } - bf->bf_dmacontext = bf->bf_buf_addr; } sc->rx.rxlink = NULL; } @@ -1739,7 +1738,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) ath_rx_send_to_mac80211(hw, sc, skb, rxs); break; } - bf->bf_dmacontext = bf->bf_buf_addr; /* * change the default rx antenna if rx diversity chooses the diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 9b17108adc7c..b053058ae5ea 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -294,7 +294,6 @@ static struct ath_buf* ath_clone_txbuf(struct ath_softc *sc, struct ath_buf *bf) tbf->bf_buf_addr = bf->bf_buf_addr; memcpy(tbf->bf_desc, bf->bf_desc, sc->sc_ah->caps.tx_desc_len); tbf->bf_state = bf->bf_state; - tbf->bf_dmacontext = bf->bf_dmacontext; return tbf; } @@ -1640,17 +1639,15 @@ static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf, bf->bf_mpdu = skb; - bf->bf_dmacontext = dma_map_single(sc->dev, skb->data, - skb->len, DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(sc->dev, bf->bf_dmacontext))) { + bf->bf_buf_addr = dma_map_single(sc->dev, skb->data, + skb->len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { bf->bf_mpdu = NULL; ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL, "dma_mapping_error() on TX\n"); return -ENOMEM; } - bf->bf_buf_addr = bf->bf_dmacontext; - bf->bf_tx_aborted = false; return 0; @@ -1914,7 +1911,7 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, tx_flags |= ATH_TX_XRETRY; } - dma_unmap_single(sc->dev, bf->bf_dmacontext, skb->len, DMA_TO_DEVICE); + dma_unmap_single(sc->dev, bf->bf_buf_addr, skb->len, DMA_TO_DEVICE); if (bf->bf_state.bfs_paprd) { if (time_after(jiffies, -- cgit v1.2.3 From 6cf9e995f91e5bbffb2bef85feef490e5b67605d Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Thu, 14 Oct 2010 12:45:30 -0700 Subject: ath9k: Null out references to stale pointers. This doesn't fix any problem that I'm aware of, but should make it harder to add use-after-free type bugs in the future. Signed-off-by: Ben Greear Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/beacon.c | 5 +++++ drivers/net/wireless/ath/ath9k/recv.c | 5 +++++ drivers/net/wireless/ath/ath9k/xmit.c | 6 ++++++ 3 files changed, 16 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index af8fedda001b..4ed010d4ef96 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -139,6 +139,7 @@ static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw, dma_unmap_single(sc->dev, bf->bf_buf_addr, skb->len, DMA_TO_DEVICE); dev_kfree_skb_any(skb); + bf->bf_buf_addr = 0; } /* Get a new beacon from mac80211 */ @@ -167,6 +168,7 @@ static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw, if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { dev_kfree_skb_any(skb); bf->bf_mpdu = NULL; + bf->bf_buf_addr = 0; ath_print(common, ATH_DBG_FATAL, "dma_mapping_error on beaconing\n"); return NULL; @@ -255,6 +257,7 @@ int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif) skb->len, DMA_TO_DEVICE); dev_kfree_skb_any(skb); bf->bf_mpdu = NULL; + bf->bf_buf_addr = 0; } /* NB: the beacon data buffer must be 32-bit aligned. */ @@ -300,6 +303,7 @@ int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif) if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { dev_kfree_skb_any(skb); bf->bf_mpdu = NULL; + bf->bf_buf_addr = 0; ath_print(common, ATH_DBG_FATAL, "dma_mapping_error on beacon alloc\n"); return -ENOMEM; @@ -326,6 +330,7 @@ void ath_beacon_return(struct ath_softc *sc, struct ath_vif *avp) skb->len, DMA_TO_DEVICE); dev_kfree_skb_any(skb); bf->bf_mpdu = NULL; + bf->bf_buf_addr = 0; } list_add_tail(&bf->list, &sc->beacon.bbuf); diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 2e427522672d..fe73fc50082a 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -268,6 +268,7 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs) bf->bf_buf_addr))) { dev_kfree_skb_any(skb); bf->bf_mpdu = NULL; + bf->bf_buf_addr = 0; ath_print(common, ATH_DBG_FATAL, "dma_mapping_error() on RX init\n"); error = -ENOMEM; @@ -358,6 +359,7 @@ int ath_rx_init(struct ath_softc *sc, int nbufs) bf->bf_buf_addr))) { dev_kfree_skb_any(skb); bf->bf_mpdu = NULL; + bf->bf_buf_addr = 0; ath_print(common, ATH_DBG_FATAL, "dma_mapping_error() on RX init\n"); error = -ENOMEM; @@ -392,6 +394,8 @@ void ath_rx_cleanup(struct ath_softc *sc) common->rx_bufsize, DMA_FROM_DEVICE); dev_kfree_skb(skb); + bf->bf_buf_addr = 0; + bf->bf_mpdu = NULL; } } @@ -1733,6 +1737,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) bf->bf_buf_addr))) { dev_kfree_skb_any(requeue_skb); bf->bf_mpdu = NULL; + bf->bf_buf_addr = 0; ath_print(common, ATH_DBG_FATAL, "dma_mapping_error() on RX\n"); ath_rx_send_to_mac80211(hw, sc, skb, rxs); diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index b053058ae5ea..d077186da870 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1643,6 +1643,7 @@ static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf, skb->len, DMA_TO_DEVICE); if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { bf->bf_mpdu = NULL; + bf->bf_buf_addr = 0; ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL, "dma_mapping_error() on TX\n"); return -ENOMEM; @@ -1912,6 +1913,7 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, } dma_unmap_single(sc->dev, bf->bf_buf_addr, skb->len, DMA_TO_DEVICE); + bf->bf_buf_addr = 0; if (bf->bf_state.bfs_paprd) { if (time_after(jiffies, @@ -1924,6 +1926,10 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, ath_debug_stat_tx(sc, txq, bf, ts); ath_tx_complete(sc, skb, bf->aphy, tx_flags); } + /* At this point, skb (bf->bf_mpdu) is consumed...make sure we don't + * accidentally reference it later. + */ + bf->bf_mpdu = NULL; /* * Return the list of ath_buf of this mpdu to free queue -- cgit v1.2.3