aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/wireless/ath/ath5k/base.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ath/ath5k/base.c')
-rw-r--r--drivers/net/wireless/ath/ath5k/base.c581
1 files changed, 392 insertions, 189 deletions
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index b346d049200..4b18434ba69 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -40,6 +40,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
@@ -54,10 +56,12 @@
#include <linux/etherdevice.h>
#include <linux/nl80211.h>
+#include <net/cfg80211.h>
#include <net/ieee80211_radiotap.h>
#include <asm/unaligned.h>
+#include <net/mac80211.h>
#include "base.h"
#include "reg.h"
#include "debug.h"
@@ -68,18 +72,19 @@
#define CREATE_TRACE_POINTS
#include "trace.h"
-int ath5k_modparam_nohwcrypt;
+bool ath5k_modparam_nohwcrypt;
module_param_named(nohwcrypt, ath5k_modparam_nohwcrypt, bool, S_IRUGO);
MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
-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.");
-
-static int modparam_fastchanswitch;
+static bool modparam_fastchanswitch;
module_param_named(fastchanswitch, modparam_fastchanswitch, bool, S_IRUGO);
MODULE_PARM_DESC(fastchanswitch, "Enable fast channel switching for AR2413/AR5413 radios.");
+static bool ath5k_modparam_no_hw_rfkill_switch;
+module_param_named(no_hw_rfkill_switch, ath5k_modparam_no_hw_rfkill_switch,
+ bool, S_IRUGO);
+MODULE_PARM_DESC(no_hw_rfkill_switch, "Ignore the GPIO RFKill switch state");
+
/* Module info */
MODULE_AUTHOR("Jiri Slaby");
@@ -161,29 +166,36 @@ static const struct ieee80211_rate ath5k_rates[] = {
.flags = IEEE80211_RATE_SHORT_PREAMBLE },
{ .bitrate = 60,
.hw_value = ATH5K_RATE_CODE_6M,
- .flags = 0 },
+ .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ },
{ .bitrate = 90,
.hw_value = ATH5K_RATE_CODE_9M,
- .flags = 0 },
+ .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ },
{ .bitrate = 120,
.hw_value = ATH5K_RATE_CODE_12M,
- .flags = 0 },
+ .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ },
{ .bitrate = 180,
.hw_value = ATH5K_RATE_CODE_18M,
- .flags = 0 },
+ .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ },
{ .bitrate = 240,
.hw_value = ATH5K_RATE_CODE_24M,
- .flags = 0 },
+ .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ },
{ .bitrate = 360,
.hw_value = ATH5K_RATE_CODE_36M,
- .flags = 0 },
+ .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ },
{ .bitrate = 480,
.hw_value = ATH5K_RATE_CODE_48M,
- .flags = 0 },
+ .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ },
{ .bitrate = 540,
.hw_value = ATH5K_RATE_CODE_54M,
- .flags = 0 },
- /* XR missing */
+ .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ },
};
static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp)
@@ -238,13 +250,14 @@ static const struct ath_ops ath5k_common_ops = {
* Driver Initialization *
\***********************/
-static int ath5k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
+static void ath5k_reg_notifier(struct wiphy *wiphy,
+ struct regulatory_request *request)
{
struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
struct ath5k_hw *ah = hw->priv;
struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah);
- return ath_reg_notifier_apply(wiphy, request, regulatory);
+ ath_reg_notifier_apply(wiphy, request, regulatory);
}
/********************\
@@ -252,8 +265,15 @@ static int ath5k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *re
\********************/
/*
- * Returns true for the channel numbers used without all_channels modparam.
+ * Returns true for the channel numbers used.
*/
+#ifdef CONFIG_ATH5K_TEST_CHANNELS
+static bool ath5k_is_standard_channel(short chan, enum ieee80211_band band)
+{
+ return true;
+}
+
+#else
static bool ath5k_is_standard_channel(short chan, enum ieee80211_band band)
{
if (band == IEEE80211_BAND_2GHZ && chan <= 14)
@@ -270,6 +290,7 @@ static bool ath5k_is_standard_channel(short chan, enum ieee80211_band band)
/* 802.11j 4.9GHz (20MHz) */
(chan == 184 || chan == 188 || chan == 192 || chan == 196));
}
+#endif
static unsigned int
ath5k_setup_channels(struct ath5k_hw *ah, struct ieee80211_channel *channels,
@@ -310,8 +331,7 @@ ath5k_setup_channels(struct ath5k_hw *ah, struct ieee80211_channel *channels,
if (!ath5k_channel_ok(ah, &channels[count]))
continue;
- if (!modparam_all_channels &&
- !ath5k_is_standard_channel(ch, band))
+ if (!ath5k_is_standard_channel(ch, band))
continue;
count++;
@@ -424,11 +444,27 @@ ath5k_setup_bands(struct ieee80211_hw *hw)
* Called with ah->lock.
*/
int
-ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan)
+ath5k_chan_set(struct ath5k_hw *ah, struct cfg80211_chan_def *chandef)
{
ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
"channel set, resetting (%u -> %u MHz)\n",
- ah->curchan->center_freq, chan->center_freq);
+ ah->curchan->center_freq, chandef->chan->center_freq);
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ ah->ah_bwmode = AR5K_BWMODE_DEFAULT;
+ break;
+ case NL80211_CHAN_WIDTH_5:
+ ah->ah_bwmode = AR5K_BWMODE_5MHZ;
+ break;
+ case NL80211_CHAN_WIDTH_10:
+ ah->ah_bwmode = AR5K_BWMODE_10MHZ;
+ break;
+ default:
+ WARN_ON(1);
+ return -EINVAL;
+ }
/*
* To switch channels clear any pending DMA operations;
@@ -436,7 +472,7 @@ ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan)
* hardware at the new frequency, and then re-enable
* the relevant bits of the h/w.
*/
- return ath5k_reset(ah, chan, true);
+ return ath5k_reset(ah, chandef->chan, true);
}
void ath5k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
@@ -456,7 +492,7 @@ void ath5k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
}
if (iter_data->need_set_hw_addr && iter_data->hw_macaddr)
- if (compare_ether_addr(iter_data->hw_macaddr, mac) == 0)
+ if (ether_addr_equal(iter_data->hw_macaddr, mac))
iter_data->need_set_hw_addr = false;
if (!iter_data->any_assoc) {
@@ -502,8 +538,9 @@ ath5k_update_bssid_mask_and_opmode(struct ath5k_hw *ah,
ath5k_vif_iter(&iter_data, vif->addr, vif);
/* Get list of all active MAC addresses */
- ieee80211_iterate_active_interfaces_atomic(ah->hw, ath5k_vif_iter,
- &iter_data);
+ ieee80211_iterate_active_interfaces_atomic(
+ ah->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+ ath5k_vif_iter, &iter_data);
memcpy(ah->bssidmask, iter_data.mask, ETH_ALEN);
ah->opmode = iter_data.opmode;
@@ -655,9 +692,46 @@ static enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb)
return htype;
}
+static struct ieee80211_rate *
+ath5k_get_rate(const struct ieee80211_hw *hw,
+ const struct ieee80211_tx_info *info,
+ struct ath5k_buf *bf, int idx)
+{
+ /*
+ * convert a ieee80211_tx_rate RC-table entry to
+ * the respective ieee80211_rate struct
+ */
+ if (bf->rates[idx].idx < 0) {
+ return NULL;
+ }
+
+ return &hw->wiphy->bands[info->band]->bitrates[ bf->rates[idx].idx ];
+}
+
+static u16
+ath5k_get_rate_hw_value(const struct ieee80211_hw *hw,
+ const struct ieee80211_tx_info *info,
+ struct ath5k_buf *bf, int idx)
+{
+ struct ieee80211_rate *rate;
+ u16 hw_rate;
+ u8 rc_flags;
+
+ rate = ath5k_get_rate(hw, info, bf, idx);
+ if (!rate)
+ return 0;
+
+ rc_flags = bf->rates[idx].flags;
+ hw_rate = (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ?
+ rate->hw_value_short : rate->hw_value;
+
+ return hw_rate;
+}
+
static int
ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf,
- struct ath5k_txq *txq, int padsize)
+ struct ath5k_txq *txq, int padsize,
+ struct ieee80211_tx_control *control)
{
struct ath5k_desc *ds = bf->desc;
struct sk_buff *skb = bf->skb;
@@ -677,7 +751,14 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf,
bf->skbaddr = dma_map_single(ah->dev, skb->data, skb->len,
DMA_TO_DEVICE);
- rate = ieee80211_get_tx_rate(ah->hw, info);
+ if (dma_mapping_error(ah->dev, bf->skbaddr))
+ return -ENOSPC;
+
+ ieee80211_get_tx_rates(info->control.vif, (control) ? control->sta : NULL, skb, bf->rates,
+ ARRAY_SIZE(bf->rates));
+
+ rate = ath5k_get_rate(ah->hw, info, bf, 0);
+
if (!rate) {
ret = -EINVAL;
goto err_unmap;
@@ -687,8 +768,8 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf,
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;
+
+ hw_rate = ath5k_get_rate_hw_value(ah->hw, info, bf, 0);
pktlen = skb->len;
@@ -711,31 +792,37 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf,
duration = le16_to_cpu(ieee80211_ctstoself_duration(ah->hw,
info->control.vif, pktlen, info));
}
+
ret = ah->ah_setup_tx_desc(ah, ds, pktlen,
ieee80211_get_hdrlen_from_skb(skb), padsize,
get_hw_packet_type(skb),
- (ah->power_level * 2),
+ (ah->ah_txpower.txp_requested * 2),
hw_rate,
- info->control.rates[0].count, keyidx, ah->ah_tx_ant, flags,
+ bf->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(ah->hw, info, i);
- if (!rate)
- break;
+ /* Set up MRR descriptor */
+ if (ah->ah_capabilities.cap_has_mrr_support) {
+ memset(mrr_rate, 0, sizeof(mrr_rate));
+ memset(mrr_tries, 0, sizeof(mrr_tries));
- mrr_rate[i] = rate->hw_value;
- mrr_tries[i] = info->control.rates[i + 1].count;
- }
+ for (i = 0; i < 3; i++) {
+
+ rate = ath5k_get_rate(ah->hw, info, bf, i);
+ if (!rate)
+ break;
- 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]);
+ mrr_rate[i] = ath5k_get_rate_hw_value(ah->hw, info, bf, i);
+ mrr_tries[i] = bf->rates[i].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]);
+ }
ds->ds_link = 0;
ds->ds_data = bf->skbaddr;
@@ -836,7 +923,7 @@ ath5k_txbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf)
return;
dma_unmap_single(ah->dev, bf->skbaddr, bf->skb->len,
DMA_TO_DEVICE);
- dev_kfree_skb_any(bf->skb);
+ ieee80211_free_txskb(ah->hw, bf->skb);
bf->skb = NULL;
bf->skbaddr = 0;
bf->desc->ds_data = 0;
@@ -1036,11 +1123,11 @@ ath5k_drain_tx_buffs(struct ath5k_hw *ah)
ath5k_txbuf_free_skb(ah, bf);
- spin_lock_bh(&ah->txbuflock);
+ spin_lock(&ah->txbuflock);
list_move_tail(&bf->list, &ah->txbuf);
ah->txbuf_len++;
txq->txq_len--;
- spin_unlock_bh(&ah->txbuflock);
+ spin_unlock(&ah->txbuflock);
}
txq->link = NULL;
txq->txq_poll_mark = false;
@@ -1154,14 +1241,11 @@ static void
ath5k_check_ibss_tsf(struct ath5k_hw *ah, struct sk_buff *skb,
struct ieee80211_rx_status *rxs)
{
- struct ath_common *common = ath5k_hw_common(ah);
u64 tsf, bc_tstamp;
u32 hw_tu;
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
- 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) {
+ if (le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS) {
/*
* Received an IBSS beacon with the same BSSID. Hardware *must*
* have updated the local TSF. We have to work around various
@@ -1217,23 +1301,6 @@ ath5k_check_ibss_tsf(struct ath5k_hw *ah, struct sk_buff *skb,
}
}
-static void
-ath5k_update_beacon_rssi(struct ath5k_hw *ah, struct sk_buff *skb, int rssi)
-{
- struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
- 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;
-
- ewma_add(&ah->ah_beacon_rssi_avg, rssi);
-
- /* in IBSS mode we should keep RSSI statistics per neighbour */
- /* le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS */
-}
-
/*
* Compute padding position. skb must contain an IEEE 802.11 frame
*/
@@ -1306,6 +1373,7 @@ ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb,
struct ath5k_rx_status *rs)
{
struct ieee80211_rx_status *rxs;
+ struct ath_common *common = ath5k_hw_common(ah);
ath5k_remove_padding(skb);
@@ -1323,20 +1391,9 @@ ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb,
* 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(ah, rs->rs_tstamp);
- rxs->flag |= RX_FLAG_MACTIME_MPDU;
+ rxs->flag |= RX_FLAG_MACTIME_END;
rxs->freq = ah->curchan->center_freq;
rxs->band = ah->curchan->band;
@@ -1352,6 +1409,16 @@ ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb,
rxs->rate_idx = ath5k_hw_to_driver_rix(ah, rs->rs_rate);
rxs->flag |= ath5k_rx_decrypted(ah, skb, rs);
+ switch (ah->ah_bwmode) {
+ case AR5K_BWMODE_5MHZ:
+ rxs->flag |= RX_FLAG_5MHZ;
+ break;
+ case AR5K_BWMODE_10MHZ:
+ rxs->flag |= RX_FLAG_10MHZ;
+ break;
+ default:
+ break;
+ }
if (rxs->rate_idx >= 0 && rs->rs_rate ==
ah->sbands[ah->curchan->band].bitrates[rxs->rate_idx].hw_value_short)
@@ -1359,11 +1426,13 @@ ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb,
trace_ath5k_rx(ah, skb);
- ath5k_update_beacon_rssi(ah, skb, rs->rs_rssi);
+ if (ath_is_mybeacon(common, (struct ieee80211_hdr *)skb->data)) {
+ ewma_add(&ah->ah_beacon_rssi_avg, rs->rs_rssi);
- /* check beacons in IBSS mode */
- if (ah->opmode == NL80211_IFTYPE_ADHOC)
- ath5k_check_ibss_tsf(ah, skb, rxs);
+ /* check beacons in IBSS mode */
+ if (ah->opmode == NL80211_IFTYPE_ADHOC)
+ ath5k_check_ibss_tsf(ah, skb, rxs);
+ }
ieee80211_rx(ah->hw, skb);
}
@@ -1512,7 +1581,7 @@ unlock:
void
ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
- struct ath5k_txq *txq)
+ struct ath5k_txq *txq, struct ieee80211_tx_control *control)
{
struct ath5k_hw *ah = hw->priv;
struct ath5k_buf *bf;
@@ -1552,7 +1621,7 @@ ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
bf->skb = skb;
- if (ath5k_txbuf_setup(ah, bf, txq, padsize)) {
+ if (ath5k_txbuf_setup(ah, bf, txq, padsize, control)) {
bf->skb = NULL;
spin_lock_irqsave(&ah->txbuflock, flags);
list_add_tail(&bf->list, &ah->txbuf);
@@ -1563,21 +1632,26 @@ ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
return;
drop_packet:
- dev_kfree_skb_any(skb);
+ ieee80211_free_txskb(hw, skb);
}
static void
ath5k_tx_frame_completed(struct ath5k_hw *ah, struct sk_buff *skb,
- struct ath5k_txq *txq, struct ath5k_tx_status *ts)
+ struct ath5k_txq *txq, struct ath5k_tx_status *ts,
+ struct ath5k_buf *bf)
{
struct ieee80211_tx_info *info;
u8 tries[3];
int i;
+ int size = 0;
ah->stats.tx_all_count++;
ah->stats.tx_bytes_count += skb->len;
info = IEEE80211_SKB_CB(skb);
+ size = min_t(int, sizeof(info->status.rates), sizeof(bf->rates));
+ memcpy(info->status.rates, bf->rates, size);
+
tries[0] = info->status.rates[0].count;
tries[1] = info->status.rates[1].count;
tries[2] = info->status.rates[2].count;
@@ -1660,7 +1734,7 @@ ath5k_tx_processq(struct ath5k_hw *ah, struct ath5k_txq *txq)
dma_unmap_single(ah->dev, bf->skbaddr, skb->len,
DMA_TO_DEVICE);
- ath5k_tx_frame_completed(ah, skb, txq, &ts);
+ ath5k_tx_frame_completed(ah, skb, txq, &ts, bf);
}
/*
@@ -1689,7 +1763,7 @@ ath5k_tasklet_tx(unsigned long data)
struct ath5k_hw *ah = (void *)data;
for (i = 0; i < AR5K_NUM_TX_QUEUES; i++)
- if (ah->txqs[i].setup && (ah->ah_txq_isr & BIT(i)))
+ if (ah->txqs[i].setup && (ah->ah_txq_isr_txok_all & BIT(i)))
ath5k_tx_processq(ah, &ah->txqs[i]);
ah->tx_pending = false;
@@ -1766,7 +1840,8 @@ ath5k_beacon_setup(struct ath5k_hw *ah, struct ath5k_buf *bf)
ds->ds_data = bf->skbaddr;
ret = ah->ah_setup_tx_desc(ah, ds, skb->len,
ieee80211_get_hdrlen_from_skb(skb), padsize,
- AR5K_PKT_TYPE_BEACON, (ah->power_level * 2),
+ AR5K_PKT_TYPE_BEACON,
+ (ah->ah_txpower.txp_requested * 2),
ieee80211_get_tx_rate(ah->hw, info)->hw_value,
1, AR5K_TXKEYIX_INVALID,
antenna, flags, 0, 0);
@@ -1791,7 +1866,7 @@ ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
int ret;
struct ath5k_hw *ah = hw->priv;
- struct ath5k_vif *avf = (void *)vif->drv_priv;
+ struct ath5k_vif *avf;
struct sk_buff *skb;
if (WARN_ON(!vif)) {
@@ -1806,6 +1881,7 @@ ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
goto out;
}
+ avf = (void *)vif->drv_priv;
ath5k_txbuf_free_skb(ah, avf->bbuf);
avf->bbuf->skb = skb;
ret = ath5k_beacon_setup(ah, avf->bbuf);
@@ -1860,7 +1936,8 @@ ath5k_beacon_send(struct ath5k_hw *ah)
ah->bmisscount = 0;
}
- if ((ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs > 1) ||
+ if ((ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs +
+ ah->num_mesh_vifs > 1) ||
ah->opmode == NL80211_IFTYPE_MESH_POINT) {
u64 tsf = ath5k_hw_get_tsf64(ah);
u32 tsftu = TSF_TO_TU(tsf);
@@ -1911,7 +1988,7 @@ ath5k_beacon_send(struct ath5k_hw *ah)
skb = ieee80211_get_buffered_bc(ah->hw, vif);
while (skb) {
- ath5k_tx_queue(ah->hw, skb, ah->cabq);
+ ath5k_tx_queue(ah->hw, skb, ah->cabq, NULL);
if (ah->cabq->txq_len >= ah->cabq->txq_max)
break;
@@ -1945,7 +2022,8 @@ ath5k_beacon_update_timers(struct ath5k_hw *ah, u64 bc_tsf)
u64 hw_tsf;
intval = ah->bintval & AR5K_BEACON_PERIOD;
- if (ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs > 1) {
+ if (ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs
+ + ah->num_mesh_vifs > 1) {
intval /= ATH_BCBUF; /* staggered multi-bss beacons */
if (intval < 15)
ATH5K_WARN(ah, "intval %u is too low, min 15\n",
@@ -2005,7 +2083,7 @@ ath5k_beacon_update_timers(struct ath5k_hw *ah, u64 bc_tsf)
ah->nexttbtt = nexttbtt;
intval |= AR5K_BEACON_ENA;
- ath5k_hw_init_beacon(ah, nexttbtt, intval);
+ ath5k_hw_init_beacon_timers(ah, nexttbtt, intval);
/*
* debugging output last in order to preserve the time critical aspect
@@ -2042,9 +2120,7 @@ ath5k_beacon_update_timers(struct ath5k_hw *ah, u64 bc_tsf)
void
ath5k_beacon_config(struct ath5k_hw *ah)
{
- unsigned long flags;
-
- spin_lock_irqsave(&ah->block, flags);
+ spin_lock_bh(&ah->block);
ah->bmisscount = 0;
ah->imask &= ~(AR5K_INT_BMISS | AR5K_INT_SWBA);
@@ -2071,7 +2147,7 @@ ath5k_beacon_config(struct ath5k_hw *ah)
ath5k_hw_set_imr(ah, ah->imask);
mmiowb();
- spin_unlock_irqrestore(&ah->block, flags);
+ spin_unlock_bh(&ah->block);
}
static void ath5k_tasklet_beacon(unsigned long data)
@@ -2112,16 +2188,29 @@ static void
ath5k_intr_calibration_poll(struct ath5k_hw *ah)
{
if (time_is_before_eq_jiffies(ah->ah_cal_next_ani) &&
- !(ah->ah_cal_mask & AR5K_CALIBRATION_FULL)) {
- /* run ANI only when full calibration is not active */
+ !(ah->ah_cal_mask & AR5K_CALIBRATION_FULL) &&
+ !(ah->ah_cal_mask & AR5K_CALIBRATION_SHORT)) {
+
+ /* Run ANI only when calibration is not active */
+
ah->ah_cal_next_ani = jiffies +
msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_ANI);
tasklet_schedule(&ah->ani_tasklet);
- } else if (time_is_before_eq_jiffies(ah->ah_cal_next_full)) {
- ah->ah_cal_next_full = jiffies +
- msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_FULL);
- tasklet_schedule(&ah->calib);
+ } else if (time_is_before_eq_jiffies(ah->ah_cal_next_short) &&
+ !(ah->ah_cal_mask & AR5K_CALIBRATION_FULL) &&
+ !(ah->ah_cal_mask & AR5K_CALIBRATION_SHORT)) {
+
+ /* Run calibration only when another calibration
+ * is not running.
+ *
+ * Note: This is for both full/short calibration,
+ * if it's time for a full one, ath5k_calibrate_work will deal
+ * with it. */
+
+ ah->ah_cal_next_short = jiffies +
+ msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_SHORT);
+ ieee80211_queue_work(ah->hw, &ah->calib_work);
}
/* we could use SWI to generate enough interrupts to meet our
* calibration interval requirements, if necessary:
@@ -2149,69 +2238,110 @@ ath5k_intr(int irq, void *dev_id)
enum ath5k_int status;
unsigned int counter = 1000;
+
+ /*
+ * If hw is not ready (or detached) and we get an
+ * interrupt, or if we have no interrupts pending
+ * (that means it's not for us) skip it.
+ *
+ * NOTE: Group 0/1 PCI interface registers are not
+ * supported on WiSOCs, so we can't check for pending
+ * interrupts (ISR belongs to another register group
+ * so we are ok).
+ */
if (unlikely(test_bit(ATH_STAT_INVALID, ah->status) ||
- ((ath5k_get_bus_type(ah) != ATH_AHB) &&
- !ath5k_hw_is_intr_pending(ah))))
+ ((ath5k_get_bus_type(ah) != ATH_AHB) &&
+ !ath5k_hw_is_intr_pending(ah))))
return IRQ_NONE;
+ /** Main loop **/
do {
- ath5k_hw_get_isr(ah, &status); /* NB: clears IRQ too */
+ ath5k_hw_get_isr(ah, &status); /* NB: clears IRQ too */
+
ATH5K_DBG(ah, ATH5K_DEBUG_INTR, "status 0x%x/0x%x\n",
status, ah->imask);
+
+ /*
+ * Fatal hw error -> Log and reset
+ *
+ * Fatal errors are unrecoverable so we have to
+ * reset the card. These errors include bus and
+ * dma errors.
+ */
if (unlikely(status & AR5K_INT_FATAL)) {
- /*
- * Fatal errors are unrecoverable.
- * Typically these are caused by DMA errors.
- */
+
ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
"fatal int, resetting\n");
ieee80211_queue_work(ah->hw, &ah->reset_work);
+
+ /*
+ * RX Overrun -> Count and reset if needed
+ *
+ * Receive buffers are full. Either the bus is busy or
+ * the CPU is not fast enough to process all received
+ * frames.
+ */
} else if (unlikely(status & AR5K_INT_RXORN)) {
+
/*
- * Receive buffers are full. Either the bus is busy or
- * the CPU is not fast enough to process all received
- * frames.
* Older chipsets need a reset to come out of this
* condition, but we treat it as RX for newer chips.
- * We don't know exactly which versions need a reset -
+ * We don't know exactly which versions need a reset
* this guess is copied from the HAL.
*/
ah->stats.rxorn_intr++;
+
if (ah->ah_mac_srev < AR5K_SREV_AR5212) {
ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
"rx overrun, resetting\n");
ieee80211_queue_work(ah->hw, &ah->reset_work);
} else
ath5k_schedule_rx(ah);
+
} else {
+
+ /* Software Beacon Alert -> Schedule beacon tasklet */
if (status & AR5K_INT_SWBA)
tasklet_hi_schedule(&ah->beacontq);
- if (status & AR5K_INT_RXEOL) {
- /*
- * NB: the hardware should re-read the link when
- * RXE bit is written, but it doesn't work at
- * least on older hardware revs.
- */
+ /*
+ * No more RX descriptors -> Just count
+ *
+ * NB: the hardware should re-read the link when
+ * RXE bit is written, but it doesn't work at
+ * least on older hardware revs.
+ */
+ if (status & AR5K_INT_RXEOL)
ah->stats.rxeol_intr++;
- }
- if (status & AR5K_INT_TXURN) {
- /* bump tx trigger level */
+
+
+ /* TX Underrun -> Bump tx trigger level */
+ if (status & AR5K_INT_TXURN)
ath5k_hw_update_tx_triglevel(ah, true);
- }
+
+ /* RX -> Schedule rx tasklet */
if (status & (AR5K_INT_RXOK | AR5K_INT_RXERR))
ath5k_schedule_rx(ah);
- if (status & (AR5K_INT_TXOK | AR5K_INT_TXDESC
- | AR5K_INT_TXERR | AR5K_INT_TXEOL))
+
+ /* TX -> Schedule tx tasklet */
+ if (status & (AR5K_INT_TXOK
+ | AR5K_INT_TXDESC
+ | AR5K_INT_TXERR
+ | AR5K_INT_TXEOL))
ath5k_schedule_tx(ah);
- if (status & AR5K_INT_BMISS) {
- /* TODO */
- }
+
+ /* Missed beacon -> TODO
+ if (status & AR5K_INT_BMISS)
+ */
+
+ /* MIB event -> Update counters and notify ANI */
if (status & AR5K_INT_MIB) {
ah->stats.mib_intr++;
ath5k_hw_update_mib_counters(ah);
ath5k_ani_mib_intr(ah);
}
+
+ /* GPIO -> Notify RFKill layer */
if (status & AR5K_INT_GPIO)
tasklet_schedule(&ah->rf_kill.toggleq);
@@ -2222,12 +2352,19 @@ ath5k_intr(int irq, void *dev_id)
} while (ath5k_hw_is_intr_pending(ah) && --counter > 0);
+ /*
+ * Until we handle rx/tx interrupts mask them on IMR
+ *
+ * NOTE: ah->(rx/tx)_pending are set when scheduling the tasklets
+ * and unset after we 've handled the interrupts.
+ */
if (ah->rx_pending || ah->tx_pending)
ath5k_set_current_imask(ah);
if (unlikely(!counter))
ATH5K_WARN(ah, "too many interrupts, giving up for now\n");
+ /* Fire up calibration poll */
ath5k_intr_calibration_poll(ah);
return IRQ_HANDLED;
@@ -2238,41 +2375,48 @@ ath5k_intr(int irq, void *dev_id)
* for temperature/environment changes.
*/
static void
-ath5k_tasklet_calibrate(unsigned long data)
+ath5k_calibrate_work(struct work_struct *work)
{
- struct ath5k_hw *ah = (void *)data;
+ struct ath5k_hw *ah = container_of(work, struct ath5k_hw,
+ calib_work);
+
+ /* Should we run a full calibration ? */
+ if (time_is_before_eq_jiffies(ah->ah_cal_next_full)) {
+
+ ah->ah_cal_next_full = jiffies +
+ msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_FULL);
+ ah->ah_cal_mask |= AR5K_CALIBRATION_FULL;
+
+ ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE,
+ "running full calibration\n");
+
+ if (ath5k_hw_gainf_calibrate(ah) == AR5K_RFGAIN_NEED_CHANGE) {
+ /*
+ * Rfgain is out of bounds, reset the chip
+ * to load new gain values.
+ */
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
+ "got new rfgain, resetting\n");
+ ieee80211_queue_work(ah->hw, &ah->reset_work);
+ }
+ } else
+ ah->ah_cal_mask |= AR5K_CALIBRATION_SHORT;
- /* Only full calibration for now */
- ah->ah_cal_mask |= AR5K_CALIBRATION_FULL;
ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE, "channel %u/%x\n",
ieee80211_frequency_to_channel(ah->curchan->center_freq),
ah->curchan->hw_value);
- if (ath5k_hw_gainf_calibrate(ah) == AR5K_RFGAIN_NEED_CHANGE) {
- /*
- * Rfgain is out of bounds, reset the chip
- * to load new gain values.
- */
- ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "calibration, resetting\n");
- ieee80211_queue_work(ah->hw, &ah->reset_work);
- }
if (ath5k_hw_phy_calibrate(ah, ah->curchan))
ATH5K_ERR(ah, "calibration of channel %u failed\n",
ieee80211_frequency_to_channel(
ah->curchan->center_freq));
- /* Noise floor calibration interrupts rx/tx path while I/Q calibration
- * 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);
- ath5k_hw_update_noise_floor(ah);
- }
-
- ah->ah_cal_mask &= ~AR5K_CALIBRATION_FULL;
+ /* Clear calibration flags */
+ if (ah->ah_cal_mask & AR5K_CALIBRATION_FULL)
+ ah->ah_cal_mask &= ~AR5K_CALIBRATION_FULL;
+ else if (ah->ah_cal_mask & AR5K_CALIBRATION_SHORT)
+ ah->ah_cal_mask &= ~AR5K_CALIBRATION_SHORT;
}
@@ -2296,6 +2440,9 @@ ath5k_tx_complete_poll_work(struct work_struct *work)
int i;
bool needreset = false;
+ if (!test_bit(ATH_STAT_STARTED, ah->status))
+ return;
+
mutex_lock(&ah->lock);
for (i = 0; i < ARRAY_SIZE(ah->txqs); i++) {
@@ -2336,7 +2483,23 @@ ath5k_tx_complete_poll_work(struct work_struct *work)
* Initialization routines *
\*************************/
-int __devinit
+static const struct ieee80211_iface_limit if_limits[] = {
+ { .max = 2048, .types = BIT(NL80211_IFTYPE_STATION) },
+ { .max = 4, .types =
+#ifdef CONFIG_MAC80211_MESH
+ BIT(NL80211_IFTYPE_MESH_POINT) |
+#endif
+ BIT(NL80211_IFTYPE_AP) },
+};
+
+static const struct ieee80211_iface_combination if_comb = {
+ .limits = if_limits,
+ .n_limits = ARRAY_SIZE(if_limits),
+ .max_interfaces = 2048,
+ .num_different_channels = 1,
+};
+
+int
ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops)
{
struct ieee80211_hw *hw = ah->hw;
@@ -2349,7 +2512,9 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops)
hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
IEEE80211_HW_SIGNAL_DBM |
- IEEE80211_HW_REPORTS_TX_ACK_STATUS;
+ IEEE80211_HW_MFP_CAPABLE |
+ IEEE80211_HW_REPORTS_TX_ACK_STATUS |
+ IEEE80211_HW_SUPPORTS_RC_TABLE;
hw->wiphy->interface_modes =
BIT(NL80211_IFTYPE_AP) |
@@ -2357,12 +2522,19 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops)
BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_MESH_POINT);
+ hw->wiphy->iface_combinations = &if_comb;
+ hw->wiphy->n_iface_combinations = 1;
+
+ /* SW support for IBSS_RSN is provided by mac80211 */
+ hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
+
+ hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_5_10_MHZ;
+
/* both antennas can be configured as RX or TX */
hw->wiphy->available_antennas_tx = 0x3;
hw->wiphy->available_antennas_rx = 0x3;
hw->extra_tx_headroom = 2;
- hw->channel_change_time = 5000;
/*
* Mark the device as detached to avoid processing
@@ -2407,8 +2579,8 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops)
if (ret)
goto err_irq;
- /* set up multi-rate retry capabilities */
- if (ah->ah_version == AR5K_AR5212) {
+ /* Set up multi-rate retry capabilities */
+ if (ah->ah_capabilities.cap_has_mrr_support) {
hw->max_rates = 4;
hw->max_rate_tries = max(AR5K_INIT_RETRY_SHORT,
AR5K_INIT_RETRY_LONG);
@@ -2543,16 +2715,23 @@ int ath5k_start(struct ieee80211_hw *hw)
* be followed by initialization of the appropriate bits
* and then setup of the interrupt mask.
*/
- ah->curchan = ah->hw->conf.channel;
- ah->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;
+ ah->curchan = ah->hw->conf.chandef.chan;
+ ah->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(ah, NULL, false);
if (ret)
goto done;
- ath5k_rfkill_hw_start(ah);
+ if (!ath5k_modparam_no_hw_rfkill_switch)
+ ath5k_rfkill_hw_start(ah);
/*
* Reset the key cache since some parts do not reset the
@@ -2573,6 +2752,7 @@ done:
mmiowb();
mutex_unlock(&ah->lock);
+ set_bit(ATH_STAT_STARTED, ah->status);
ieee80211_queue_delayed_work(ah->hw, &ah->tx_complete_work,
msecs_to_jiffies(ATH5K_TX_COMPLETE_POLL_INT));
@@ -2585,7 +2765,6 @@ static void ath5k_stop_tasklets(struct ath5k_hw *ah)
ah->tx_pending = false;
tasklet_kill(&ah->rxtq);
tasklet_kill(&ah->txtq);
- tasklet_kill(&ah->calib);
tasklet_kill(&ah->beacontq);
tasklet_kill(&ah->ani_tasklet);
}
@@ -2635,9 +2814,11 @@ void ath5k_stop(struct ieee80211_hw *hw)
ath5k_stop_tasklets(ah);
+ clear_bit(ATH_STAT_STARTED, ah->status);
cancel_delayed_work_sync(&ah->tx_complete_work);
- ath5k_rfkill_hw_stop(ah);
+ if (!ath5k_modparam_no_hw_rfkill_switch)
+ ath5k_rfkill_hw_stop(ah);
}
/*
@@ -2689,9 +2870,24 @@ ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan,
ath5k_ani_init(ah, ani_mode);
- ah->ah_cal_next_full = jiffies + msecs_to_jiffies(100);
- ah->ah_cal_next_ani = jiffies;
- ah->ah_cal_next_nf = jiffies;
+ /*
+ * Set calibration intervals
+ *
+ * Note: We don't need to run calibration imediately
+ * since some initial calibration is done on reset
+ * even for fast channel switching. Also on scanning
+ * this will get set again and again and it won't get
+ * executed unless we connect somewhere and spend some
+ * time on the channel (that's what calibration needs
+ * anyway to be accurate).
+ */
+ ah->ah_cal_next_full = jiffies +
+ msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_FULL);
+ ah->ah_cal_next_ani = jiffies +
+ msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_ANI);
+ ah->ah_cal_next_short = jiffies +
+ msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_SHORT);
+
ewma_init(&ah->ah_beacon_rssi_avg, 1024, 8);
/* clear survey data and cycle counters */
@@ -2733,7 +2929,7 @@ static void ath5k_reset_work(struct work_struct *work)
mutex_unlock(&ah->lock);
}
-static int __devinit
+static int
ath5k_init(struct ieee80211_hw *hw)
{
@@ -2745,20 +2941,6 @@ ath5k_init(struct ieee80211_hw *hw)
/*
- * 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 (ret < 0)
- goto err;
- if (ret > 0)
- __set_bit(ATH_STAT_MRRETRY, ah->status);
-
- /*
* Collect the channel list. The 802.11 layer
* is responsible for filtering this list based
* on settings like the phy mode and regulatory
@@ -2841,11 +3023,11 @@ ath5k_init(struct ieee80211_hw *hw)
tasklet_init(&ah->rxtq, ath5k_tasklet_rx, (unsigned long)ah);
tasklet_init(&ah->txtq, ath5k_tasklet_tx, (unsigned long)ah);
- tasklet_init(&ah->calib, ath5k_tasklet_calibrate, (unsigned long)ah);
tasklet_init(&ah->beacontq, ath5k_tasklet_beacon, (unsigned long)ah);
tasklet_init(&ah->ani_tasklet, ath5k_tasklet_ani, (unsigned long)ah);
INIT_WORK(&ah->reset_work, ath5k_reset_work);
+ INIT_WORK(&ah->calib_work, ath5k_calibrate_work);
INIT_DELAYED_WORK(&ah->tx_complete_work, ath5k_tx_complete_poll_work);
ret = ath5k_hw_common(ah)->bus_ops->eeprom_read_mac(ah, mac);
@@ -2932,8 +3114,9 @@ ath5k_any_vif_assoc(struct ath5k_hw *ah)
iter_data.need_set_hw_addr = false;
iter_data.found_active = true;
- ieee80211_iterate_active_interfaces_atomic(ah->hw, ath5k_vif_iter,
- &iter_data);
+ ieee80211_iterate_active_interfaces_atomic(
+ ah->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+ ath5k_vif_iter, &iter_data);
return iter_data.any_assoc;
}
@@ -2950,3 +3133,23 @@ ath5k_set_beacon_filter(struct ieee80211_hw *hw, bool enable)
ath5k_hw_set_rx_filter(ah, rfilt);
ah->filter_flags = rfilt;
}
+
+void _ath5k_printk(const struct ath5k_hw *ah, const char *level,
+ const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ if (ah && ah->hw)
+ printk("%s" pr_fmt("%s: %pV"),
+ level, wiphy_name(ah->hw->wiphy), &vaf);
+ else
+ printk("%s" pr_fmt("%pV"), level, &vaf);
+
+ va_end(args);
+}