aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/wireless/ath9k/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ath9k/main.c')
-rw-r--r--drivers/net/wireless/ath9k/main.c1944
1 files changed, 1023 insertions, 921 deletions
diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c
index 3c04044a60b..13d4e6756c9 100644
--- a/drivers/net/wireless/ath9k/main.c
+++ b/drivers/net/wireless/ath9k/main.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * 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
@@ -15,9 +15,7 @@
*/
#include <linux/nl80211.h>
-#include "core.h"
-#include "reg.h"
-#include "hw.h"
+#include "ath9k.h"
#define ATH_PCI_VERSION "0.1"
@@ -28,84 +26,129 @@ MODULE_DESCRIPTION("Support for Atheros 802.11n wireless LAN cards.");
MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards");
MODULE_LICENSE("Dual BSD/GPL");
-static struct pci_device_id ath_pci_id_table[] __devinitdata = {
- { PCI_VDEVICE(ATHEROS, 0x0023) }, /* PCI */
- { PCI_VDEVICE(ATHEROS, 0x0024) }, /* PCI-E */
- { PCI_VDEVICE(ATHEROS, 0x0027) }, /* PCI */
- { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */
- { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */
- { PCI_VDEVICE(ATHEROS, 0x002B) }, /* PCI-E */
- { 0 }
+static int modparam_nohwcrypt;
+module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444);
+MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption");
+
+/* We use the hw_value as an index into our private channel structure */
+
+#define CHAN2G(_freq, _idx) { \
+ .center_freq = (_freq), \
+ .hw_value = (_idx), \
+ .max_power = 30, \
+}
+
+#define CHAN5G(_freq, _idx) { \
+ .band = IEEE80211_BAND_5GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = (_idx), \
+ .max_power = 30, \
+}
+
+/* Some 2 GHz radios are actually tunable on 2312-2732
+ * 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[] = {
+ CHAN2G(2412, 0), /* Channel 1 */
+ CHAN2G(2417, 1), /* Channel 2 */
+ CHAN2G(2422, 2), /* Channel 3 */
+ CHAN2G(2427, 3), /* Channel 4 */
+ CHAN2G(2432, 4), /* Channel 5 */
+ CHAN2G(2437, 5), /* Channel 6 */
+ CHAN2G(2442, 6), /* Channel 7 */
+ CHAN2G(2447, 7), /* Channel 8 */
+ CHAN2G(2452, 8), /* Channel 9 */
+ CHAN2G(2457, 9), /* Channel 10 */
+ CHAN2G(2462, 10), /* Channel 11 */
+ CHAN2G(2467, 11), /* Channel 12 */
+ CHAN2G(2472, 12), /* Channel 13 */
+ CHAN2G(2484, 13), /* Channel 14 */
};
-static void ath_detach(struct ath_softc *sc);
-
-/* return bus cachesize in 4B word units */
-
-static void bus_read_cachesize(struct ath_softc *sc, int *csz)
-{
- u8 u8tmp;
-
- pci_read_config_byte(sc->pdev, PCI_CACHE_LINE_SIZE, (u8 *)&u8tmp);
- *csz = (int)u8tmp;
-
- /*
- * This check was put in to avoid "unplesant" consequences if
- * the bootrom has not fully initialized all PCI devices.
- * Sometimes the cache line size register is not set
- */
-
- if (*csz == 0)
- *csz = DEFAULT_CACHELINE >> 2; /* Use the default size */
-}
-
-static void ath_setcurmode(struct ath_softc *sc, enum wireless_mode mode)
-{
- sc->cur_rate_table = sc->hw_rate_table[mode];
- /*
- * All protection frames are transmited at 2Mb/s for
- * 11g, otherwise at 1Mb/s.
- * XXX select protection rate index from rate table.
- */
- sc->sc_protrix = (mode == ATH9K_MODE_11G ? 1 : 0);
-}
+/* Some 5 GHz radios are actually tunable on XXXX-YYYY
+ * 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[] = {
+ /* _We_ call this UNII 1 */
+ CHAN5G(5180, 14), /* Channel 36 */
+ CHAN5G(5200, 15), /* Channel 40 */
+ CHAN5G(5220, 16), /* Channel 44 */
+ CHAN5G(5240, 17), /* Channel 48 */
+ /* _We_ call this UNII 2 */
+ CHAN5G(5260, 18), /* Channel 52 */
+ CHAN5G(5280, 19), /* Channel 56 */
+ CHAN5G(5300, 20), /* Channel 60 */
+ CHAN5G(5320, 21), /* Channel 64 */
+ /* _We_ call this "Middle band" */
+ CHAN5G(5500, 22), /* Channel 100 */
+ CHAN5G(5520, 23), /* Channel 104 */
+ CHAN5G(5540, 24), /* Channel 108 */
+ CHAN5G(5560, 25), /* Channel 112 */
+ CHAN5G(5580, 26), /* Channel 116 */
+ CHAN5G(5600, 27), /* Channel 120 */
+ CHAN5G(5620, 28), /* Channel 124 */
+ CHAN5G(5640, 29), /* Channel 128 */
+ CHAN5G(5660, 30), /* Channel 132 */
+ CHAN5G(5680, 31), /* Channel 136 */
+ CHAN5G(5700, 32), /* Channel 140 */
+ /* _We_ call this UNII 3 */
+ CHAN5G(5745, 33), /* Channel 149 */
+ CHAN5G(5765, 34), /* Channel 153 */
+ CHAN5G(5785, 35), /* Channel 157 */
+ CHAN5G(5805, 36), /* Channel 161 */
+ CHAN5G(5825, 37), /* Channel 165 */
+};
-static enum wireless_mode ath_chan2mode(struct ath9k_channel *chan)
+static void ath_cache_conf_rate(struct ath_softc *sc,
+ struct ieee80211_conf *conf)
{
- if (chan->chanmode == CHANNEL_A)
- return ATH9K_MODE_11A;
- else if (chan->chanmode == CHANNEL_G)
- return ATH9K_MODE_11G;
- else if (chan->chanmode == CHANNEL_B)
- return ATH9K_MODE_11B;
- else if (chan->chanmode == CHANNEL_A_HT20)
- return ATH9K_MODE_11NA_HT20;
- else if (chan->chanmode == CHANNEL_G_HT20)
- return ATH9K_MODE_11NG_HT20;
- else if (chan->chanmode == CHANNEL_A_HT40PLUS)
- return ATH9K_MODE_11NA_HT40PLUS;
- else if (chan->chanmode == CHANNEL_A_HT40MINUS)
- return ATH9K_MODE_11NA_HT40MINUS;
- else if (chan->chanmode == CHANNEL_G_HT40PLUS)
- return ATH9K_MODE_11NG_HT40PLUS;
- else if (chan->chanmode == CHANNEL_G_HT40MINUS)
- return ATH9K_MODE_11NG_HT40MINUS;
-
- WARN_ON(1); /* should not get here */
-
- return ATH9K_MODE_11B;
+ switch (conf->channel->band) {
+ case IEEE80211_BAND_2GHZ:
+ if (conf_is_ht20(conf))
+ sc->cur_rate_table =
+ sc->hw_rate_table[ATH9K_MODE_11NG_HT20];
+ else if (conf_is_ht40_minus(conf))
+ sc->cur_rate_table =
+ sc->hw_rate_table[ATH9K_MODE_11NG_HT40MINUS];
+ else if (conf_is_ht40_plus(conf))
+ sc->cur_rate_table =
+ sc->hw_rate_table[ATH9K_MODE_11NG_HT40PLUS];
+ else
+ sc->cur_rate_table =
+ sc->hw_rate_table[ATH9K_MODE_11G];
+ break;
+ case IEEE80211_BAND_5GHZ:
+ if (conf_is_ht20(conf))
+ sc->cur_rate_table =
+ sc->hw_rate_table[ATH9K_MODE_11NA_HT20];
+ else if (conf_is_ht40_minus(conf))
+ sc->cur_rate_table =
+ sc->hw_rate_table[ATH9K_MODE_11NA_HT40MINUS];
+ else if (conf_is_ht40_plus(conf))
+ sc->cur_rate_table =
+ sc->hw_rate_table[ATH9K_MODE_11NA_HT40PLUS];
+ else
+ sc->cur_rate_table =
+ sc->hw_rate_table[ATH9K_MODE_11A];
+ break;
+ default:
+ BUG_ON(1);
+ break;
+ }
}
static void ath_update_txpow(struct ath_softc *sc)
{
- struct ath_hal *ah = sc->sc_ah;
+ struct ath_hw *ah = sc->sc_ah;
u32 txpow;
- if (sc->sc_curtxpow != sc->sc_config.txpowlimit) {
- ath9k_hw_set_txpowerlimit(ah, sc->sc_config.txpowlimit);
+ if (sc->curtxpow != sc->config.txpowlimit) {
+ ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit);
/* read back in case value is clamped */
ath9k_hw_getcapability(ah, ATH9K_CAP_TXPOW, 1, &txpow);
- sc->sc_curtxpow = txpow;
+ sc->curtxpow = txpow;
}
}
@@ -176,150 +219,87 @@ static void ath_setup_rates(struct ath_softc *sc, enum ieee80211_band band)
for (i = 0; i < maxrates; i++) {
rate[i].bitrate = rate_table->info[i].ratekbps / 100;
rate[i].hw_value = rate_table->info[i].ratecode;
+ if (rate_table->info[i].short_preamble) {
+ rate[i].hw_value_short = rate_table->info[i].ratecode |
+ rate_table->info[i].short_preamble;
+ rate[i].flags = IEEE80211_RATE_SHORT_PREAMBLE;
+ }
sband->n_bitrates++;
+
DPRINTF(sc, ATH_DBG_CONFIG, "Rate: %2dMbps, ratecode: %2d\n",
rate[i].bitrate / 10, rate[i].hw_value);
}
}
-static int ath_setup_channels(struct ath_softc *sc)
-{
- struct ath_hal *ah = sc->sc_ah;
- int nchan, i, a = 0, b = 0;
- u8 regclassids[ATH_REGCLASSIDS_MAX];
- u32 nregclass = 0;
- struct ieee80211_supported_band *band_2ghz;
- struct ieee80211_supported_band *band_5ghz;
- struct ieee80211_channel *chan_2ghz;
- struct ieee80211_channel *chan_5ghz;
- struct ath9k_channel *c;
-
- /* Fill in ah->ah_channels */
- if (!ath9k_regd_init_channels(ah, ATH_CHAN_MAX, (u32 *)&nchan,
- regclassids, ATH_REGCLASSIDS_MAX,
- &nregclass, CTRY_DEFAULT, false, 1)) {
- u32 rd = ah->ah_currentRD;
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to collect channel list; "
- "regdomain likely %u country code %u\n",
- rd, CTRY_DEFAULT);
- return -EINVAL;
- }
-
- band_2ghz = &sc->sbands[IEEE80211_BAND_2GHZ];
- band_5ghz = &sc->sbands[IEEE80211_BAND_5GHZ];
- chan_2ghz = sc->channels[IEEE80211_BAND_2GHZ];
- chan_5ghz = sc->channels[IEEE80211_BAND_5GHZ];
-
- for (i = 0; i < nchan; i++) {
- c = &ah->ah_channels[i];
- if (IS_CHAN_2GHZ(c)) {
- chan_2ghz[a].band = IEEE80211_BAND_2GHZ;
- chan_2ghz[a].center_freq = c->channel;
- chan_2ghz[a].max_power = c->maxTxPower;
-
- if (c->privFlags & CHANNEL_DISALLOW_ADHOC)
- chan_2ghz[a].flags |= IEEE80211_CHAN_NO_IBSS;
- if (c->channelFlags & CHANNEL_PASSIVE)
- chan_2ghz[a].flags |= IEEE80211_CHAN_PASSIVE_SCAN;
-
- band_2ghz->n_channels = ++a;
-
- DPRINTF(sc, ATH_DBG_CONFIG, "2MHz channel: %d, "
- "channelFlags: 0x%x\n",
- c->channel, c->channelFlags);
- } else if (IS_CHAN_5GHZ(c)) {
- chan_5ghz[b].band = IEEE80211_BAND_5GHZ;
- chan_5ghz[b].center_freq = c->channel;
- chan_5ghz[b].max_power = c->maxTxPower;
-
- if (c->privFlags & CHANNEL_DISALLOW_ADHOC)
- chan_5ghz[b].flags |= IEEE80211_CHAN_NO_IBSS;
- if (c->channelFlags & CHANNEL_PASSIVE)
- chan_5ghz[b].flags |= IEEE80211_CHAN_PASSIVE_SCAN;
-
- band_5ghz->n_channels = ++b;
-
- DPRINTF(sc, ATH_DBG_CONFIG, "5MHz channel: %d, "
- "channelFlags: 0x%x\n",
- c->channel, c->channelFlags);
- }
- }
-
- return 0;
-}
-
/*
* 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
* DMA, then restart stuff.
*/
-static int ath_set_channel(struct ath_softc *sc, struct ath9k_channel *hchan)
+int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
+ struct ath9k_channel *hchan)
{
- struct ath_hal *ah = sc->sc_ah;
+ struct ath_hw *ah = sc->sc_ah;
bool fastcc = true, stopped;
+ struct ieee80211_channel *channel = hw->conf.channel;
+ int r;
if (sc->sc_flags & SC_OP_INVALID)
return -EIO;
- if (hchan->channel != sc->sc_ah->ah_curchan->channel ||
- hchan->channelFlags != sc->sc_ah->ah_curchan->channelFlags ||
- (sc->sc_flags & SC_OP_CHAINMASK_UPDATE) ||
- (sc->sc_flags & SC_OP_FULL_RESET)) {
- int status;
- /*
- * This is only performed if the channel settings have
- * actually changed.
- *
- * 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.
- */
- ath9k_hw_set_interrupts(ah, 0);
- ath_draintxq(sc, false);
- stopped = ath_stoprecv(sc);
-
- /* XXX: do not flush receive queue here. We don't want
- * to flush data frames already in queue because of
- * changing channel. */
-
- if (!stopped || (sc->sc_flags & SC_OP_FULL_RESET))
- fastcc = false;
-
- DPRINTF(sc, ATH_DBG_CONFIG,
- "(%u MHz) -> (%u MHz), cflags:%x, chanwidth: %d\n",
- sc->sc_ah->ah_curchan->channel,
- hchan->channel, hchan->channelFlags, sc->tx_chan_width);
-
- spin_lock_bh(&sc->sc_resetlock);
- if (!ath9k_hw_reset(ah, hchan, sc->tx_chan_width,
- sc->sc_tx_chainmask, sc->sc_rx_chainmask,
- sc->sc_ht_extprotspacing, fastcc, &status)) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to reset channel %u (%uMhz) "
- "flags 0x%x hal status %u\n",
- ath9k_hw_mhz2ieee(ah, hchan->channel,
- hchan->channelFlags),
- hchan->channel, hchan->channelFlags, status);
- spin_unlock_bh(&sc->sc_resetlock);
- return -EIO;
- }
- spin_unlock_bh(&sc->sc_resetlock);
+ ath9k_ps_wakeup(sc);
- sc->sc_flags &= ~SC_OP_CHAINMASK_UPDATE;
- sc->sc_flags &= ~SC_OP_FULL_RESET;
+ /*
+ * This is only performed if the channel settings have
+ * actually changed.
+ *
+ * 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.
+ */
+ ath9k_hw_set_interrupts(ah, 0);
+ ath_drain_all_txq(sc, false);
+ stopped = ath_stoprecv(sc);
- if (ath_startrecv(sc) != 0) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to restart recv logic\n");
- return -EIO;
- }
+ /* XXX: do not flush receive queue here. We don't want
+ * to flush data frames already in queue because of
+ * changing channel. */
+
+ if (!stopped || (sc->sc_flags & SC_OP_FULL_RESET))
+ fastcc = false;
- ath_setcurmode(sc, ath_chan2mode(hchan));
- ath_update_txpow(sc);
- ath9k_hw_set_interrupts(ah, sc->sc_imask);
+ DPRINTF(sc, ATH_DBG_CONFIG,
+ "(%u MHz) -> (%u MHz), chanwidth: %d\n",
+ sc->sc_ah->curchan->channel,
+ channel->center_freq, sc->tx_chan_width);
+
+ spin_lock_bh(&sc->sc_resetlock);
+
+ r = ath9k_hw_reset(ah, hchan, fastcc);
+ if (r) {
+ DPRINTF(sc, ATH_DBG_FATAL,
+ "Unable to reset channel (%u Mhz) "
+ "reset status %u\n",
+ channel->center_freq, r);
+ spin_unlock_bh(&sc->sc_resetlock);
+ return r;
}
+ spin_unlock_bh(&sc->sc_resetlock);
+
+ sc->sc_flags &= ~SC_OP_CHAINMASK_UPDATE;
+ sc->sc_flags &= ~SC_OP_FULL_RESET;
+
+ if (ath_startrecv(sc) != 0) {
+ DPRINTF(sc, ATH_DBG_FATAL,
+ "Unable to restart recv logic\n");
+ return -EIO;
+ }
+
+ ath_cache_conf_rate(sc, &hw->conf);
+ ath_update_txpow(sc);
+ ath9k_hw_set_interrupts(ah, sc->imask);
+ ath9k_ps_restore(sc);
return 0;
}
@@ -332,123 +312,122 @@ static int ath_set_channel(struct ath_softc *sc, struct ath9k_channel *hchan)
*/
static void ath_ani_calibrate(unsigned long data)
{
- struct ath_softc *sc;
- struct ath_hal *ah;
+ struct ath_softc *sc = (struct ath_softc *)data;
+ struct ath_hw *ah = sc->sc_ah;
bool longcal = false;
bool shortcal = false;
bool aniflag = false;
unsigned int timestamp = jiffies_to_msecs(jiffies);
- u32 cal_interval;
+ u32 cal_interval, short_cal_interval;
- sc = (struct ath_softc *)data;
- ah = sc->sc_ah;
+ short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ?
+ ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL;
/*
* don't calibrate when we're scanning.
* we are most likely not on our home channel.
*/
- if (sc->rx.rxfilter & FIF_BCN_PRBRESP_PROMISC)
- return;
+ if (sc->sc_flags & SC_OP_SCANNING)
+ goto set_timer;
/* Long calibration runs independently of short calibration. */
- if ((timestamp - sc->sc_ani.sc_longcal_timer) >= ATH_LONG_CALINTERVAL) {
+ if ((timestamp - sc->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
longcal = true;
DPRINTF(sc, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
- sc->sc_ani.sc_longcal_timer = timestamp;
+ sc->ani.longcal_timer = timestamp;
}
- /* Short calibration applies only while sc_caldone is false */
- if (!sc->sc_ani.sc_caldone) {
- if ((timestamp - sc->sc_ani.sc_shortcal_timer) >=
- ATH_SHORT_CALINTERVAL) {
+ /* Short calibration applies only while caldone is false */
+ if (!sc->ani.caldone) {
+ if ((timestamp - sc->ani.shortcal_timer) >= short_cal_interval) {
shortcal = true;
DPRINTF(sc, ATH_DBG_ANI, "shortcal @%lu\n", jiffies);
- sc->sc_ani.sc_shortcal_timer = timestamp;
- sc->sc_ani.sc_resetcal_timer = timestamp;
+ sc->ani.shortcal_timer = timestamp;
+ sc->ani.resetcal_timer = timestamp;
}
} else {
- if ((timestamp - sc->sc_ani.sc_resetcal_timer) >=
+ if ((timestamp - sc->ani.resetcal_timer) >=
ATH_RESTART_CALINTERVAL) {
- ath9k_hw_reset_calvalid(ah, ah->ah_curchan,
- &sc->sc_ani.sc_caldone);
- if (sc->sc_ani.sc_caldone)
- sc->sc_ani.sc_resetcal_timer = timestamp;
+ sc->ani.caldone = ath9k_hw_reset_calvalid(ah);
+ if (sc->ani.caldone)
+ sc->ani.resetcal_timer = timestamp;
}
}
/* Verify whether we must check ANI */
- if ((timestamp - sc->sc_ani.sc_checkani_timer) >=
- ATH_ANI_POLLINTERVAL) {
+ if ((timestamp - sc->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
aniflag = true;
- sc->sc_ani.sc_checkani_timer = timestamp;
+ sc->ani.checkani_timer = timestamp;
}
/* Skip all processing if there's nothing to do. */
if (longcal || shortcal || aniflag) {
/* Call ANI routine if necessary */
if (aniflag)
- ath9k_hw_ani_monitor(ah, &sc->sc_halstats,
- ah->ah_curchan);
+ ath9k_hw_ani_monitor(ah, &sc->nodestats, ah->curchan);
/* Perform calibration if necessary */
if (longcal || shortcal) {
bool iscaldone = false;
- if (ath9k_hw_calibrate(ah, ah->ah_curchan,
- sc->sc_rx_chainmask, longcal,
+ if (ath9k_hw_calibrate(ah, ah->curchan,
+ sc->rx_chainmask, longcal,
&iscaldone)) {
if (longcal)
- sc->sc_ani.sc_noise_floor =
+ sc->ani.noise_floor =
ath9k_hw_getchan_noise(ah,
- ah->ah_curchan);
+ ah->curchan);
DPRINTF(sc, ATH_DBG_ANI,
"calibrate chan %u/%x nf: %d\n",
- ah->ah_curchan->channel,
- ah->ah_curchan->channelFlags,
- sc->sc_ani.sc_noise_floor);
+ ah->curchan->channel,
+ ah->curchan->channelFlags,
+ sc->ani.noise_floor);
} else {
DPRINTF(sc, ATH_DBG_ANY,
"calibrate chan %u/%x failed\n",
- ah->ah_curchan->channel,
- ah->ah_curchan->channelFlags);
+ ah->curchan->channel,
+ ah->curchan->channelFlags);
}
- sc->sc_ani.sc_caldone = iscaldone;
+ sc->ani.caldone = iscaldone;
}
}
+set_timer:
/*
* Set timer interval based on previous results.
* The interval must be the shortest necessary to satisfy ANI,
* short calibration and long calibration.
*/
cal_interval = ATH_LONG_CALINTERVAL;
- if (sc->sc_ah->ah_config.enable_ani)
+ if (sc->sc_ah->config.enable_ani)
cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
- if (!sc->sc_ani.sc_caldone)
- cal_interval = min(cal_interval, (u32)ATH_SHORT_CALINTERVAL);
+ if (!sc->ani.caldone)
+ cal_interval = min(cal_interval, (u32)short_cal_interval);
- mod_timer(&sc->sc_ani.timer, jiffies + msecs_to_jiffies(cal_interval));
+ mod_timer(&sc->ani.timer, jiffies + msecs_to_jiffies(cal_interval));
}
/*
* Update tx/rx chainmask. For legacy association,
* hard code chainmask to 1x1, for 11n association, use
- * the chainmask configuration.
+ * the chainmask configuration, for bt coexistence, use
+ * the chainmask configuration even in legacy mode.
*/
-static void ath_update_chainmask(struct ath_softc *sc, int is_ht)
+void ath_update_chainmask(struct ath_softc *sc, int is_ht)
{
sc->sc_flags |= SC_OP_CHAINMASK_UPDATE;
- if (is_ht) {
- sc->sc_tx_chainmask = sc->sc_ah->ah_caps.tx_chainmask;
- sc->sc_rx_chainmask = sc->sc_ah->ah_caps.rx_chainmask;
+ if (is_ht ||
+ (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BT_COEX)) {
+ sc->tx_chainmask = sc->sc_ah->caps.tx_chainmask;
+ sc->rx_chainmask = sc->sc_ah->caps.rx_chainmask;
} else {
- sc->sc_tx_chainmask = 1;
- sc->sc_rx_chainmask = 1;
+ sc->tx_chainmask = 1;
+ sc->rx_chainmask = 1;
}
DPRINTF(sc, ATH_DBG_CONFIG, "tx chmask: %d, rx chmask: %d\n",
- sc->sc_tx_chainmask, sc->sc_rx_chainmask);
+ sc->tx_chainmask, sc->rx_chainmask);
}
static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta)
@@ -476,7 +455,7 @@ static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta)
static void ath9k_tasklet(unsigned long data)
{
struct ath_softc *sc = (struct ath_softc *)data;
- u32 status = sc->sc_intrstatus;
+ u32 status = sc->intrstatus;
if (status & ATH9K_INT_FATAL) {
/* need a chip reset */
@@ -496,13 +475,13 @@ static void ath9k_tasklet(unsigned long data)
}
/* re-enable hardware interrupt */
- ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask);
+ ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
}
-static irqreturn_t ath_isr(int irq, void *dev)
+irqreturn_t ath_isr(int irq, void *dev)
{
struct ath_softc *sc = dev;
- struct ath_hal *ah = sc->sc_ah;
+ struct ath_hw *ah = sc->sc_ah;
enum ath9k_int status;
bool sched = false;
@@ -527,7 +506,7 @@ static irqreturn_t ath_isr(int irq, void *dev)
*/
ath9k_hw_getisr(ah, &status); /* NB: clears ISR too */
- status &= sc->sc_imask; /* discard unasked-for bits */
+ status &= sc->imask; /* discard unasked-for bits */
/*
* If there are no status bits set, then this interrupt was not
@@ -536,7 +515,8 @@ static irqreturn_t ath_isr(int irq, void *dev)
if (!status)
return IRQ_NONE;
- sc->sc_intrstatus = status;
+ sc->intrstatus = status;
+ ath9k_ps_wakeup(sc);
if (status & ATH9K_INT_FATAL) {
/* need a chip reset */
@@ -583,45 +563,39 @@ static irqreturn_t ath_isr(int irq, void *dev)
* it will clear whatever condition caused
* the interrupt.
*/
- ath9k_hw_procmibevent(ah, &sc->sc_halstats);
- ath9k_hw_set_interrupts(ah, sc->sc_imask);
+ ath9k_hw_procmibevent(ah, &sc->nodestats);
+ ath9k_hw_set_interrupts(ah, sc->imask);
}
if (status & ATH9K_INT_TIM_TIMER) {
- if (!(ah->ah_caps.hw_caps &
+ if (!(ah->caps.hw_caps &
ATH9K_HW_CAP_AUTOSLEEP)) {
/* Clear RxAbort bit so that we can
* receive frames */
+ ath9k_hw_setpower(ah, ATH9K_PM_AWAKE);
ath9k_hw_setrxabort(ah, 0);
sched = true;
+ sc->sc_flags |= SC_OP_WAIT_FOR_BEACON;
}
}
+ if (status & ATH9K_INT_TSFOOR) {
+ /* FIXME: Handle this interrupt for power save */
+ sched = true;
+ }
}
+ ath9k_ps_restore(sc);
} while (0);
ath_debug_stat_interrupt(sc, status);
if (sched) {
/* turn off every interrupt except SWBA */
- ath9k_hw_set_interrupts(ah, (sc->sc_imask & ATH9K_INT_SWBA));
+ ath9k_hw_set_interrupts(ah, (sc->imask & ATH9K_INT_SWBA));
tasklet_schedule(&sc->intr_tq);
}
return IRQ_HANDLED;
}
-static int ath_get_channel(struct ath_softc *sc,
- struct ieee80211_channel *chan)
-{
- int i;
-
- for (i = 0; i < sc->sc_ah->ah_nchan; i++) {
- if (sc->sc_ah->ah_channels[i].channel == chan->center_freq)
- return i;
- }
-
- return -1;
-}
-
static u32 ath_get_extchanmode(struct ath_softc *sc,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type)
@@ -664,20 +638,9 @@ static u32 ath_get_extchanmode(struct ath_softc *sc,
return chanmode;
}
-static int ath_keyset(struct ath_softc *sc, u16 keyix,
- struct ath9k_keyval *hk, const u8 mac[ETH_ALEN])
-{
- bool status;
-
- status = ath9k_hw_set_keycache_entry(sc->sc_ah,
- keyix, hk, mac, false);
-
- return status != false;
-}
-
static int ath_setkey_tkip(struct ath_softc *sc, u16 keyix, const u8 *key,
- struct ath9k_keyval *hk,
- const u8 *addr)
+ struct ath9k_keyval *hk, const u8 *addr,
+ bool authenticator)
{
const u8 *key_rxmic;
const u8 *key_txmic;
@@ -686,26 +649,33 @@ static int ath_setkey_tkip(struct ath_softc *sc, u16 keyix, const u8 *key,
key_rxmic = key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY;
if (addr == NULL) {
- /* Group key installation */
- memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
- return ath_keyset(sc, keyix, hk, addr);
- }
- if (!sc->sc_splitmic) {
/*
- * data key goes at first index,
- * the hal handles the MIC keys at index+64.
+ * 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(sc->sc_ah, keyix, hk, addr);
+ }
+ if (!sc->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_keyset(sc, keyix, hk, addr);
+ return ath9k_hw_set_keycache_entry(sc->sc_ah, keyix, hk, addr);
}
- /*
- * TX key goes at first index, RX key at +32.
- * The hal handles the MIC keys at index+64.
- */
+
+ /* 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_keyset(sc, keyix, hk, NULL)) {
- /* Txmic entry failed. No need to proceed further */
+ if (!ath9k_hw_set_keycache_entry(sc->sc_ah, keyix, hk, NULL)) {
+ /* TX MIC entry failed. No need to proceed further */
DPRINTF(sc, ATH_DBG_KEYCACHE,
"Setting TX MIC Key Failed\n");
return 0;
@@ -713,20 +683,20 @@ static int ath_setkey_tkip(struct ath_softc *sc, u16 keyix, const u8 *key,
memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
/* XXX delete tx key on failure? */
- return ath_keyset(sc, keyix + 32, hk, addr);
+ return ath9k_hw_set_keycache_entry(sc->sc_ah, keyix + 32, hk, addr);
}
static int ath_reserve_key_cache_slot_tkip(struct ath_softc *sc)
{
int i;
- for (i = IEEE80211_WEP_NKID; i < sc->sc_keymax / 2; i++) {
- if (test_bit(i, sc->sc_keymap) ||
- test_bit(i + 64, sc->sc_keymap))
+ for (i = IEEE80211_WEP_NKID; i < sc->keymax / 2; i++) {
+ if (test_bit(i, sc->keymap) ||
+ test_bit(i + 64, sc->keymap))
continue; /* At least one part of TKIP key allocated */
- if (sc->sc_splitmic &&
- (test_bit(i + 32, sc->sc_keymap) ||
- test_bit(i + 64 + 32, sc->sc_keymap)))
+ if (sc->splitmic &&
+ (test_bit(i + 32, sc->keymap) ||
+ test_bit(i + 64 + 32, sc->keymap)))
continue; /* At least one part of TKIP key allocated */
/* Found a free slot for a TKIP key */
@@ -740,55 +710,55 @@ static int ath_reserve_key_cache_slot(struct ath_softc *sc)
int i;
/* First, try to find slots that would not be available for TKIP. */
- if (sc->sc_splitmic) {
- for (i = IEEE80211_WEP_NKID; i < sc->sc_keymax / 4; i++) {
- if (!test_bit(i, sc->sc_keymap) &&
- (test_bit(i + 32, sc->sc_keymap) ||
- test_bit(i + 64, sc->sc_keymap) ||
- test_bit(i + 64 + 32, sc->sc_keymap)))
+ if (sc->splitmic) {
+ for (i = IEEE80211_WEP_NKID; i < sc->keymax / 4; i++) {
+ if (!test_bit(i, sc->keymap) &&
+ (test_bit(i + 32, sc->keymap) ||
+ test_bit(i + 64, sc->keymap) ||
+ test_bit(i + 64 + 32, sc->keymap)))
return i;
- if (!test_bit(i + 32, sc->sc_keymap) &&
- (test_bit(i, sc->sc_keymap) ||
- test_bit(i + 64, sc->sc_keymap) ||
- test_bit(i + 64 + 32, sc->sc_keymap)))
+ if (!test_bit(i + 32, sc->keymap) &&
+ (test_bit(i, sc->keymap) ||
+ test_bit(i + 64, sc->keymap) ||
+ test_bit(i + 64 + 32, sc->keymap)))
return i + 32;
- if (!test_bit(i + 64, sc->sc_keymap) &&
- (test_bit(i , sc->sc_keymap) ||
- test_bit(i + 32, sc->sc_keymap) ||
- test_bit(i + 64 + 32, sc->sc_keymap)))
+ if (!test_bit(i + 64, sc->keymap) &&
+ (test_bit(i , sc->keymap) ||
+ test_bit(i + 32, sc->keymap) ||
+ test_bit(i + 64 + 32, sc->keymap)))
return i + 64;
- if (!test_bit(i + 64 + 32, sc->sc_keymap) &&
- (test_bit(i, sc->sc_keymap) ||
- test_bit(i + 32, sc->sc_keymap) ||
- test_bit(i + 64, sc->sc_keymap)))
+ if (!test_bit(i + 64 + 32, sc->keymap) &&
+ (test_bit(i, sc->keymap) ||
+ test_bit(i + 32, sc->keymap) ||
+ test_bit(i + 64, sc->keymap)))
return i + 64 + 32;
}
} else {
- for (i = IEEE80211_WEP_NKID; i < sc->sc_keymax / 2; i++) {
- if (!test_bit(i, sc->sc_keymap) &&
- test_bit(i + 64, sc->sc_keymap))
+ for (i = IEEE80211_WEP_NKID; i < sc->keymax / 2; i++) {
+ if (!test_bit(i, sc->keymap) &&
+ test_bit(i + 64, sc->keymap))
return i;
- if (test_bit(i, sc->sc_keymap) &&
- !test_bit(i + 64, sc->sc_keymap))
+ if (test_bit(i, sc->keymap) &&
+ !test_bit(i + 64, sc->keymap))
return i + 64;
}
}
/* No partially used TKIP slots, pick any available slot */
- for (i = IEEE80211_WEP_NKID; i < sc->sc_keymax; i++) {
+ for (i = IEEE80211_WEP_NKID; i < sc->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 (sc->sc_splitmic) {
+ if (sc->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, sc->sc_keymap))
+ if (!test_bit(i, sc->keymap))
return i; /* Found a free slot for a key */
}
@@ -797,7 +767,8 @@ static int ath_reserve_key_cache_slot(struct ath_softc *sc)
}
static int ath_key_config(struct ath_softc *sc,
- const u8 *addr,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
struct ieee80211_key_conf *key)
{
struct ath9k_keyval hk;
@@ -818,7 +789,7 @@ static int ath_key_config(struct ath_softc *sc,
hk.kv_type = ATH9K_CIPHER_AES_CCM;
break;
default:
- return -EINVAL;
+ return -EOPNOTSUPP;
}
hk.kv_len = key->keylen;
@@ -829,10 +800,10 @@ static int ath_key_config(struct ath_softc *sc,
* need to change with virtual interfaces. */
idx = key->keyidx;
} else if (key->keyidx) {
- struct ieee80211_vif *vif;
+ if (WARN_ON(!sta))
+ return -EOPNOTSUPP;
+ mac = sta->addr;
- mac = addr;
- vif = sc->sc_vaps[0];
if (vif->type != NL80211_IFTYPE_AP) {
/* Only keyidx 0 should be used with unicast key, but
* allow this for client mode for now. */
@@ -840,29 +811,33 @@ static int ath_key_config(struct ath_softc *sc,
} else
return -EIO;
} else {
- mac = addr;
+ if (WARN_ON(!sta))
+ return -EOPNOTSUPP;
+ mac = sta->addr;
+
if (key->alg == ALG_TKIP)
idx = ath_reserve_key_cache_slot_tkip(sc);
else
idx = ath_reserve_key_cache_slot(sc);
if (idx < 0)
- return -EIO; /* no free key cache entries */
+ return -ENOSPC; /* no free key cache entries */
}
if (key->alg == ALG_TKIP)
- ret = ath_setkey_tkip(sc, idx, key->key, &hk, mac);
+ ret = ath_setkey_tkip(sc, idx, key->key, &hk, mac,
+ vif->type == NL80211_IFTYPE_AP);
else
- ret = ath_keyset(sc, idx, &hk, mac);
+ ret = ath9k_hw_set_keycache_entry(sc->sc_ah, idx, &hk, mac);
if (!ret)
return -EIO;
- set_bit(idx, sc->sc_keymap);
+ set_bit(idx, sc->keymap);
if (key->alg == ALG_TKIP) {
- set_bit(idx + 64, sc->sc_keymap);
- if (sc->sc_splitmic) {
- set_bit(idx + 32, sc->sc_keymap);
- set_bit(idx + 64 + 32, sc->sc_keymap);
+ set_bit(idx + 64, sc->keymap);
+ if (sc->splitmic) {
+ set_bit(idx + 32, sc->keymap);
+ set_bit(idx + 64 + 32, sc->keymap);
}
}
@@ -875,18 +850,19 @@ static void ath_key_delete(struct ath_softc *sc, struct ieee80211_key_conf *key)
if (key->hw_key_idx < IEEE80211_WEP_NKID)
return;
- clear_bit(key->hw_key_idx, sc->sc_keymap);
+ clear_bit(key->hw_key_idx, sc->keymap);
if (key->alg != ALG_TKIP)
return;
- clear_bit(key->hw_key_idx + 64, sc->sc_keymap);
- if (sc->sc_splitmic) {
- clear_bit(key->hw_key_idx + 32, sc->sc_keymap);
- clear_bit(key->hw_key_idx + 64 + 32, sc->sc_keymap);
+ clear_bit(key->hw_key_idx + 64, sc->keymap);
+ if (sc->splitmic) {
+ clear_bit(key->hw_key_idx + 32, sc->keymap);
+ clear_bit(key->hw_key_idx + 64 + 32, sc->keymap);
}
}
-static void setup_ht_cap(struct ieee80211_sta_ht_cap *ht_info)
+static void setup_ht_cap(struct ath_softc *sc,
+ struct ieee80211_sta_ht_cap *ht_info)
{
#define ATH9K_HT_CAP_MAXRXAMPDU_65536 0x3 /* 2 ^ 16 */
#define ATH9K_HT_CAP_MPDUDENSITY_8 0x6 /* 8 usec */
@@ -899,10 +875,23 @@ static void setup_ht_cap(struct ieee80211_sta_ht_cap *ht_info)
ht_info->ampdu_factor = ATH9K_HT_CAP_MAXRXAMPDU_65536;
ht_info->ampdu_density = ATH9K_HT_CAP_MPDUDENSITY_8;
+
/* set up supported mcs set */
memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
- ht_info->mcs.rx_mask[0] = 0xff;
- ht_info->mcs.rx_mask[1] = 0xff;
+
+ switch(sc->rx_chainmask) {
+ case 1:
+ ht_info->mcs.rx_mask[0] = 0xff;
+ break;
+ case 3:
+ case 5:
+ case 7:
+ default:
+ ht_info->mcs.rx_mask[0] = 0xff;
+ ht_info->mcs.rx_mask[1] = 0xff;
+ break;
+ }
+
ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
}
@@ -910,36 +899,33 @@ static void ath9k_bss_assoc_info(struct ath_softc *sc,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf)
{
- struct ath_vap *avp = (void *)vif->drv_priv;
+ struct ath_vif *avp = (void *)vif->drv_priv;
if (bss_conf->assoc) {
DPRINTF(sc, ATH_DBG_CONFIG, "Bss Info ASSOC %d, bssid: %pM\n",
- bss_conf->aid, sc->sc_curbssid);
+ bss_conf->aid, sc->curbssid);
/* New association, store aid */
if (avp->av_opmode == NL80211_IFTYPE_STATION) {
- sc->sc_curaid = bss_conf->aid;
- ath9k_hw_write_associd(sc->sc_ah, sc->sc_curbssid,
- sc->sc_curaid);
+ sc->curaid = bss_conf->aid;
+ ath9k_hw_write_associd(sc);
}
/* Configure the beacon */
- ath_beacon_config(sc, 0);
- sc->sc_flags |= SC_OP_BEACONS;
+ ath_beacon_config(sc, vif);
/* Reset rssi stats */
- sc->sc_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER;
- sc->sc_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER;
- sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER;
- sc->sc_halstats.ns_avgtxrate = ATH_RATE_DUMMY_MARKER;
+ sc->nodestats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER;
+ sc->nodestats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER;
+ sc->nodestats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER;
+ sc->nodestats.ns_avgtxrate = ATH_RATE_DUMMY_MARKER;
/* Start ANI */
- mod_timer(&sc->sc_ani.timer,
- jiffies + msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
-
+ mod_timer(&sc->ani.timer,
+ jiffies + msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
} else {
DPRINTF(sc, ATH_DBG_CONFIG, "Bss Info DISSOC\n");
- sc->sc_curaid = 0;
+ sc->curaid = 0;
}
}
@@ -947,6 +933,39 @@ static void ath9k_bss_assoc_info(struct ath_softc *sc,
/* LED functions */
/********************************/