summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2008-12-21 19:57:10 -0800
committerDavid S. Miller <davem@davemloft.net>2008-12-21 19:57:10 -0800
commitc2da953a46b18b7515ad476c1c1686640a12e93a (patch)
treeb384f097718119e715cdebd74710ddceeb80fdc8
parentc94cb314503a69492bf4455dce4f6d300cff0851 (diff)
parent9cf7f247bd0cd21e475c71a4e018bb612ef02aab (diff)
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
-rw-r--r--drivers/net/wireless/ath5k/ath5k.h5
-rw-r--r--drivers/net/wireless/ath5k/base.c38
-rw-r--r--drivers/net/wireless/ath5k/desc.c4
-rw-r--r--drivers/net/wireless/ath9k/core.h1
-rw-r--r--drivers/net/wireless/ath9k/main.c377
-rw-r--r--drivers/net/wireless/ath9k/rc.c3
-rw-r--r--drivers/net/wireless/ath9k/recv.c75
-rw-r--r--drivers/net/wireless/ath9k/xmit.c22
-rw-r--r--drivers/net/wireless/b43/debugfs.c1
-rw-r--r--drivers/net/wireless/b43/debugfs.h1
-rw-r--r--drivers/net/wireless/b43/main.c103
-rw-r--r--drivers/net/wireless/b43/phy_common.c18
-rw-r--r--drivers/net/wireless/b43/phy_g.c8
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945.c1
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945.h9
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-4965.c10
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-5000.c10
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-rs.c26
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.c41
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-calib.c9
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-commands.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-debug.h17
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-dev.h29
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-io.h11
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-led.c2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-rx.c29
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-tx.c8
-rw-r--r--drivers/net/wireless/iwlwifi/iwl3945-base.c6
-rw-r--r--drivers/net/wireless/libertas/if_sdio.c3
-rw-r--r--drivers/net/wireless/mac80211_hwsim.c6
-rw-r--r--drivers/net/wireless/p54/p54.h23
-rw-r--r--drivers/net/wireless/p54/p54common.c198
-rw-r--r--drivers/net/wireless/p54/p54common.h25
-rw-r--r--drivers/net/wireless/p54/p54pci.c12
-rw-r--r--drivers/net/wireless/p54/p54usb.c43
-rw-r--r--drivers/net/wireless/rtl818x/rtl8187_dev.c16
-rw-r--r--include/linux/nl80211.h53
-rw-r--r--include/net/cfg80211.h42
-rw-r--r--include/net/mac80211.h26
-rw-r--r--net/mac80211/cfg.c42
-rw-r--r--net/mac80211/ht.c35
-rw-r--r--net/mac80211/ieee80211_i.h25
-rw-r--r--net/mac80211/main.c39
-rw-r--r--net/mac80211/mlme.c70
-rw-r--r--net/mac80211/rx.c59
-rw-r--r--net/mac80211/tx.c13
-rw-r--r--net/mac80211/util.c88
-rw-r--r--net/mac80211/wext.c46
-rw-r--r--net/wireless/nl80211.c85
50 files changed, 1166 insertions, 651 deletions
diff --git a/drivers/net/wireless/ath5k/ath5k.h b/drivers/net/wireless/ath5k/ath5k.h
index 13df1191b07..183ffc8e62c 100644
--- a/drivers/net/wireless/ath5k/ath5k.h
+++ b/drivers/net/wireless/ath5k/ath5k.h
@@ -1350,4 +1350,9 @@ static inline u32 ath5k_hw_bitswap(u32 val, unsigned int bits)
return retval;
}
+static inline int ath5k_pad_size(int hdrlen)
+{
+ return (hdrlen < 24) ? 0 : hdrlen & 3;
+}
+
#endif
diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c
index 0e4317010ed..4af2607deec 100644
--- a/drivers/net/wireless/ath5k/base.c
+++ b/drivers/net/wireless/ath5k/base.c
@@ -1668,7 +1668,7 @@ ath5k_tasklet_rx(unsigned long data)
struct ath5k_desc *ds;
int ret;
int hdrlen;
- int pad;
+ int padsize;
spin_lock(&sc->rxbuflock);
if (list_empty(&sc->rxbuf)) {
@@ -1753,16 +1753,19 @@ accept:
skb_put(skb, rs.rs_datalen);
- /*
- * the hardware adds a padding to 4 byte boundaries between
- * the header and the payload data if the header length is
- * not multiples of 4 - remove it
- */
+ /* 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. */
hdrlen = ieee80211_get_hdrlen_from_skb(skb);
- if (hdrlen & 3) {
- pad = hdrlen % 4;
- memmove(skb->data + pad, skb->data, hdrlen);
- skb_pull(skb, pad);
+ padsize = ath5k_pad_size(hdrlen);
+ if (padsize) {
+ memmove(skb->data + padsize, skb->data, hdrlen);
+ skb_pull(skb, padsize);
}
/*
@@ -2623,7 +2626,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
struct ath5k_buf *bf;
unsigned long flags;
int hdrlen;
- int pad;
+ int padsize;
ath5k_debug_dump_skb(sc, skb, "TX ", 1);
@@ -2635,15 +2638,16 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
* if this is not the case we add the padding after the header
*/
hdrlen = ieee80211_get_hdrlen_from_skb(skb);
- if (hdrlen & 3) {
- pad = hdrlen % 4;
- if (skb_headroom(skb) < pad) {
+ padsize = ath5k_pad_size(hdrlen);
+ if (padsize) {
+
+ if (skb_headroom(skb) < padsize) {
ATH5K_ERR(sc, "tx hdrlen not %%4: %d not enough"
- " headroom to pad %d\n", hdrlen, pad);
+ " headroom to pad %d\n", hdrlen, padsize);
return -1;
}
- skb_push(skb, pad);
- memmove(skb->data, skb->data+pad, hdrlen);
+ skb_push(skb, padsize);
+ memmove(skb->data, skb->data+padsize, hdrlen);
}
spin_lock_irqsave(&sc->txbuflock, flags);
diff --git a/drivers/net/wireless/ath5k/desc.c b/drivers/net/wireless/ath5k/desc.c
index 5e362a7a362..b40a9287a39 100644
--- a/drivers/net/wireless/ath5k/desc.c
+++ b/drivers/net/wireless/ath5k/desc.c
@@ -71,7 +71,7 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
/* Verify and set frame length */
/* remove padding we might have added before */
- frame_len = pkt_len - (hdr_len & 3) + FCS_LEN;
+ frame_len = pkt_len - ath5k_pad_size(hdr_len) + FCS_LEN;
if (frame_len & ~AR5K_2W_TX_DESC_CTL0_FRAME_LEN)
return -EINVAL;
@@ -202,7 +202,7 @@ static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
/* Verify and set frame length */
/* remove padding we might have added before */
- frame_len = pkt_len - (hdr_len & 3) + FCS_LEN;
+ frame_len = pkt_len - ath5k_pad_size(hdr_len) + FCS_LEN;
if (frame_len & ~AR5K_4W_TX_DESC_CTL0_FRAME_LEN)
return -EINVAL;
diff --git a/drivers/net/wireless/ath9k/core.h b/drivers/net/wireless/ath9k/core.h
index e38f0331cfd..4ca2aed236e 100644
--- a/drivers/net/wireless/ath9k/core.h
+++ b/drivers/net/wireless/ath9k/core.h
@@ -701,6 +701,7 @@ struct ath_softc {
struct ath_hal *sc_ah;
void __iomem *mem;
spinlock_t sc_resetlock;
+ struct mutex mutex;
u8 sc_curbssid[ETH_ALEN];
u8 sc_myaddr[ETH_ALEN];
diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c
index 02e1771bb27..191eec50dc7 100644
--- a/drivers/net/wireless/ath9k/main.c
+++ b/drivers/net/wireless/ath9k/main.c
@@ -61,8 +61,7 @@ static void bus_read_cachesize(struct ath_softc *sc, int *csz)
static void ath_setcurmode(struct ath_softc *sc, enum wireless_mode mode)
{
- if (!sc->sc_curaid)
- sc->cur_rate_table = sc->hw_rate_table[mode];
+ sc->cur_rate_table = sc->hw_rate_table[mode];
/*
* All protection frames are transmited at 2Mb/s for
* 11g, otherwise at 1Mb/s.
@@ -623,37 +622,40 @@ static int ath_get_channel(struct ath_softc *sc,
return -1;
}
-/* ext_chan_offset: (-1, 0, 1) (below, none, above) */
-
static u32 ath_get_extchanmode(struct ath_softc *sc,
struct ieee80211_channel *chan,
- int ext_chan_offset,
- enum ath9k_ht_macmode tx_chan_width)
+ enum nl80211_channel_type channel_type)
{
u32 chanmode = 0;
switch (chan->band) {
case IEEE80211_BAND_2GHZ:
- if ((ext_chan_offset == 0) &&
- (tx_chan_width == ATH9K_HT_MACMODE_20))
+ switch(channel_type) {
+ case NL80211_CHAN_NO_HT:
+ case NL80211_CHAN_HT20:
chanmode = CHANNEL_G_HT20;
- if ((ext_chan_offset == 1) &&
- (tx_chan_width == ATH9K_HT_MACMODE_2040))
+ break;
+ case NL80211_CHAN_HT40PLUS:
chanmode = CHANNEL_G_HT40PLUS;
- if ((ext_chan_offset == -1) &&
- (tx_chan_width == ATH9K_HT_MACMODE_2040))
+ break;
+ case NL80211_CHAN_HT40MINUS:
chanmode = CHANNEL_G_HT40MINUS;
+ break;
+ }
break;
case IEEE80211_BAND_5GHZ:
- if ((ext_chan_offset == 0) &&
- (tx_chan_width == ATH9K_HT_MACMODE_20))
+ switch(channel_type) {
+ case NL80211_CHAN_NO_HT:
+ case NL80211_CHAN_HT20:
chanmode = CHANNEL_A_HT20;
- if ((ext_chan_offset == 1) &&
- (tx_chan_width == ATH9K_HT_MACMODE_2040))
+ break;
+ case NL80211_CHAN_HT40PLUS:
chanmode = CHANNEL_A_HT40PLUS;
- if ((ext_chan_offset == -1) &&
- (tx_chan_width == ATH9K_HT_MACMODE_2040))
+ break;
+ case NL80211_CHAN_HT40MINUS:
chanmode = CHANNEL_A_HT40MINUS;
+ break;
+ }
break;
default:
break;
@@ -662,13 +664,6 @@ static u32 ath_get_extchanmode(struct ath_softc *sc,
return chanmode;
}
-static void ath_key_reset(struct ath_softc *sc, u16 keyix, int freeslot)
-{
- ath9k_hw_keyreset(sc->sc_ah, keyix);
- if (freeslot)
- clear_bit(keyix, sc->sc_keymap);
-}
-
static int ath_keyset(struct ath_softc *sc, u16 keyix,
struct ath9k_keyval *hk, const u8 mac[ETH_ALEN])
{
@@ -680,21 +675,20 @@ static int ath_keyset(struct ath_softc *sc, u16 keyix,
return status != false;
}
-static int ath_setkey_tkip(struct ath_softc *sc,
- struct ieee80211_key_conf *key,
+static int ath_setkey_tkip(struct ath_softc *sc, u16 keyix, const u8 *key,
struct ath9k_keyval *hk,
const u8 *addr)
{
- u8 *key_rxmic = NULL;
- u8 *key_txmic = NULL;
+ const u8 *key_rxmic;
+ const u8 *key_txmic;
- key_txmic = key->key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY;
- key_rxmic = key->key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY;
+ 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 */
- memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
- return ath_keyset(sc, key->keyidx, hk, addr);
+ memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
+ return ath_keyset(sc, keyix, hk, addr);
}
if (!sc->sc_splitmic) {
/*
@@ -703,14 +697,14 @@ static int ath_setkey_tkip(struct ath_softc *sc,
*/
memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic));
- return ath_keyset(sc, key->keyidx, hk, addr);
+ return ath_keyset(sc, keyix, hk, addr);
}
/*
* TX key goes at first index, RX key at +32.
* The hal handles the MIC keys at index+64.
*/
memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic));
- if (!ath_keyset(sc, key->keyidx, hk, NULL)) {
+ if (!ath_keyset(sc, keyix, hk, NULL)) {
/* Txmic entry failed. No need to proceed further */
DPRINTF(sc, ATH_DBG_KEYCACHE,
"Setting TX MIC Key Failed\n");
@@ -719,18 +713,97 @@ static int ath_setkey_tkip(struct ath_softc *sc,
memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
/* XXX delete tx key on failure? */
- return ath_keyset(sc, key->keyidx+32, hk, addr);
+ return ath_keyset(sc, 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))
+ 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)))
+ 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_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)))
+ 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)))
+ 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)))
+ 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)))
+ 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))
+ return i;
+ if (test_bit(i, sc->sc_keymap) &&
+ !test_bit(i + 64, sc->sc_keymap))
+ return i + 64;
+ }
+ }
+
+ /* No partially used TKIP slots, pick any available slot */
+ for (i = IEEE80211_WEP_NKID; i < sc->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 (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))
+ return i; /* Found a free slot for a key */
+ }
+
+ /* No free slot found */
+ return -1;
}
static int ath_key_config(struct ath_softc *sc,
const u8 *addr,
struct ieee80211_key_conf *key)
{
- struct ieee80211_vif *vif;
struct ath9k_keyval hk;
const u8 *mac = NULL;
int ret = 0;
- enum nl80211_iftype opmode;
+ int idx;
memset(&hk, 0, sizeof(hk));
@@ -748,65 +821,69 @@ static int ath_key_config(struct ath_softc *sc,
return -EINVAL;
}
- hk.kv_len = key->keylen;
+ hk.kv_len = key->keylen;
memcpy(hk.kv_val, key->key, key->keylen);
- if (!sc->sc_vaps[0])
- return -EIO;
-
- vif = sc->sc_vaps[0];
- opmode = vif->type;
+ if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
+ /* For now, use the default keys for broadcast keys. This may
+ * need to change with virtual interfaces. */
+ idx = key->keyidx;
+ } else if (key->keyidx) {
+ struct ieee80211_vif *vif;
- /*
- * Strategy:
- * For STA mc tx, we will not setup a key at
- * all since we never tx mc.
- *
- * For STA mc rx, we will use the keyID.
- *
- * For ADHOC mc tx, we will use the keyID, and no macaddr.
- *
- * For ADHOC mc rx, we will alloc a slot and plumb the mac of
- * the peer node.
- * BUT we will plumb a cleartext key so that we can do
- * per-Sta default key table lookup in software.
- */
- if (is_broadcast_ether_addr(addr)) {
- switch (opmode) {
- case NL80211_IFTYPE_STATION:
- /* default key: could be group WPA key
- * or could be static WEP key */
- mac = NULL;
- break;
- case NL80211_IFTYPE_ADHOC:
- break;
- case NL80211_IFTYPE_AP:
- break;
- default:
- ASSERT(0);
- break;
- }
+ 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. */
+ idx = key->keyidx;
+ } else
+ return -EIO;
} else {
mac = 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 */
}
if (key->alg == ALG_TKIP)
- ret = ath_setkey_tkip(sc, key, &hk, mac);
+ ret = ath_setkey_tkip(sc, idx, key->key, &hk, mac);
else
- ret = ath_keyset(sc, key->keyidx, &hk, mac);
+ ret = ath_keyset(sc, idx, &hk, mac);
if (!ret)
return -EIO;
- return 0;
+ set_bit(idx, sc->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);
+ }
+ }
+
+ return idx;
}
static void ath_key_delete(struct ath_softc *sc, struct ieee80211_key_conf *key)
{
- int freeslot;
+ ath9k_hw_keyreset(sc->sc_ah, key->hw_key_idx);
+ if (key->hw_key_idx < IEEE80211_WEP_NKID)
+ return;
+
+ clear_bit(key->hw_key_idx, sc->sc_keymap);
+ if (key->alg != ALG_TKIP)
+ return;
- freeslot = (key->keyidx >= 4) ? 1 : 0;
- ath_key_reset(sc, key->keyidx, freeslot);
+ 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);
+ }
}
static void setup_ht_cap(struct ieee80211_sta_ht_cap *ht_info)
@@ -829,45 +906,15 @@ static void setup_ht_cap(struct ieee80211_sta_ht_cap *ht_info)
ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
}
-static void ath9k_ht_conf(struct ath_softc *sc,
- struct ieee80211_bss_conf *bss_conf)
-{
- if (sc->hw->conf.ht.enabled) {
- if (bss_conf->ht.width_40_ok)
- sc->tx_chan_width = ATH9K_HT_MACMODE_2040;
- else
- sc->tx_chan_width = ATH9K_HT_MACMODE_20;
-
- ath9k_hw_set11nmac2040(sc->sc_ah, sc->tx_chan_width);
-
- DPRINTF(sc, ATH_DBG_CONFIG,
- "BSS Changed HT, chanwidth: %d\n", sc->tx_chan_width);
- }
-}
-
-static inline int ath_sec_offset(u8 ext_offset)
-{
- if (ext_offset == IEEE80211_HT_PARAM_CHA_SEC_NONE)
- return 0;
- else if (ext_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE)
- return 1;
- else if (ext_offset == IEEE80211_HT_PARAM_CHA_SEC_BELOW)
- return -1;
-
- return 0;
-}
-
static void ath9k_bss_assoc_info(struct ath_softc *sc,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf)
{
- struct ieee80211_hw *hw = sc->hw;
- struct ieee80211_channel *curchan = hw->conf.channel;
struct ath_vap *avp = (void *)vif->drv_priv;
- int pos;
if (bss_conf->assoc) {
- DPRINTF(sc, ATH_DBG_CONFIG, "Bss Info ASSOC %d\n", bss_conf->aid);
+ DPRINTF(sc, ATH_DBG_CONFIG, "Bss Info ASSOC %d, bssid: %pM\n",
+ bss_conf->aid, sc->sc_curbssid);
/* New association, store aid */
if (avp->av_opmode == NL80211_IFTYPE_STATION) {
@@ -886,40 +933,6 @@ static void ath9k_bss_assoc_info(struct ath_softc *sc,
sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER;
sc->sc_halstats.ns_avgtxrate = ATH_RATE_DUMMY_MARKER;
- /* Update chainmask */
- ath_update_chainmask(sc, hw->conf.ht.enabled);
-
- DPRINTF(sc, ATH_DBG_CONFIG,
- "bssid %pM aid 0x%x\n",
- sc->sc_curbssid, sc->sc_curaid);
-
- pos = ath_get_channel(sc, curchan);
- if (pos == -1) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "Invalid channel: %d\n", curchan->center_freq);
- return;
- }
-
- if (hw->conf.ht.enabled) {
- int offset =
- ath_sec_offset(bss_conf->ht.secondary_channel_offset);
- sc->tx_chan_width = (bss_conf->ht.width_40_ok) ?
- ATH9K_HT_MACMODE_2040 : ATH9K_HT_MACMODE_20;
-
- sc->sc_ah->ah_channels[pos].chanmode =
- ath_get_extchanmode(sc, curchan,
- offset, sc->tx_chan_width);
- } else {
- sc->sc_ah->ah_channels[pos].chanmode =
- (curchan->band == IEEE80211_BAND_2GHZ) ?
- CHANNEL_G : CHANNEL_A;
- }
-
- /* set h/w channel */
- if (ath_set_channel(sc, &sc->sc_ah->ah_channels[pos]) < 0)
- DPRINTF(sc, ATH_DBG_FATAL, "Unable to set channel: %d\n",
- curchan->center_freq);
-
/* Start ANI */
mod_timer(&sc->sc_ani.timer,
jiffies + msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
@@ -1291,9 +1304,6 @@ static void ath_detach(struct ath_softc *sc)
ath_deinit_leds(sc);
ieee80211_unregister_hw(hw);
-
- ath_rate_control_unregister();
-
ath_rx_cleanup(sc);
ath_tx_cleanup(sc);
@@ -1326,6 +1336,7 @@ static int ath_init(u16 devid, struct ath_softc *sc)
printk(KERN_ERR "Unable to create debugfs files\n");
spin_lock_init(&sc->sc_resetlock);
+ mutex_init(&sc->mutex);
tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet,
(unsigned long)sc);
@@ -1362,18 +1373,6 @@ static int ath_init(u16 devid, struct ath_softc *sc)
*/
for (i = 0; i < sc->sc_keymax; i++)
ath9k_hw_keyreset(ah, (u16) i);
- /*
- * Mark key cache slots associated with global keys
- * as in use. If we knew TKIP was not to be used we
- * could leave the +32, +64, and +32+64 slots free.
- * XXX only for splitmic.
- */
- for (i = 0; i < IEEE80211_WEP_NKID; i++) {
- set_bit(i, sc->sc_keymap);
- set_bit(i + 32, sc->sc_keymap);
- set_bit(i + 64, sc->sc_keymap);
- set_bit(i + 32 + 64, sc->sc_keymap);
- }
/* Collect the channel list using the default country code */
@@ -1574,15 +1573,7 @@ static int ath_attach(u16 devid, struct ath_softc *sc)
hw->sta_data_size = sizeof(struct ath_node);
hw->vif_data_size = sizeof(struct ath_vap);
- /* Register rate control */
hw->rate_control_algorithm = "ath9k_rate_control";
- error = ath_rate_control_register();
- if (error != 0) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to register rate control algorithm: %d\n", error);
- ath_rate_control_unregister();
- goto bad;
- }
if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) {
setup_ht_cap(&sc->sbands[IEEE80211_BAND_2GHZ].ht_cap);
@@ -1615,10 +1606,6 @@ static int ath_attach(u16 devid, struct ath_softc *sc)
#endif
error = ieee80211_register_hw(hw);
- if (error != 0) {
- ath_rate_control_unregister();
- goto bad;
- }
/* Initialize LED control */
ath_init_leds(sc);
@@ -1626,7 +1613,6 @@ static int ath_attach(u16 devid, struct ath_softc *sc)
return 0;
detach:
ath_detach(sc);
-bad:
return error;
}
@@ -2146,7 +2132,9 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
struct ath_softc *sc = hw->priv;
struct ieee80211_conf *conf = &hw->conf;
- if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+ mutex_lock(&sc->mutex);
+ if (changed & (IEEE80211_CONF_CHANGE_CHANNEL |
+ IEEE80211_CONF_CHANGE_HT)) {
struct ieee80211_channel *curchan = hw->conf.channel;
int pos;
@@ -2157,6 +2145,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
if (pos == -1) {
DPRINTF(sc, ATH_DBG_FATAL, "Invalid channel: %d\n",
curchan->center_freq);
+ mutex_unlock(&sc->mutex);
return -EINVAL;
}
@@ -2165,29 +2154,29 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
(curchan->band == IEEE80211_BAND_2GHZ) ?
CHANNEL_G : CHANNEL_A;
- if ((sc->sc_ah->ah_opmode == NL80211_IFTYPE_AP) &&
- (conf->ht.enabled)) {
- sc->tx_chan_width = (!!conf->ht.sec_chan_offset) ?
- ATH9K_HT_MACMODE_2040 : ATH9K_HT_MACMODE_20;
+ if (conf->ht.enabled) {
+ if (conf->ht.channel_type == NL80211_CHAN_HT40PLUS ||
+ conf->ht.channel_type == NL80211_CHAN_HT40MINUS)
+ sc->tx_chan_width = ATH9K_HT_MACMODE_2040;
sc->sc_ah->ah_channels[pos].chanmode =
ath_get_extchanmode(sc, curchan,
- conf->ht.sec_chan_offset,
- sc->tx_chan_width);
+ conf->ht.channel_type);
}
if (ath_set_channel(sc, &sc->sc_ah->ah_channels[pos]) < 0) {
DPRINTF(sc, ATH_DBG_FATAL, "Unable to set channel\n");
+ mutex_unlock(&sc->mutex);
return -EINVAL;
}
- }
- if (changed & IEEE80211_CONF_CHANGE_HT)
ath_update_chainmask(sc, conf->ht.enabled);
+ }
if (changed & IEEE80211_CONF_CHANGE_POWER)
sc->sc_config.txpowlimit = 2 * conf->power_level;
+ mutex_unlock(&sc->mutex);
return 0;
}
@@ -2371,18 +2360,17 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
switch (cmd) {
case SET_KEY:
ret = ath_key_config(sc, addr, key);
- if (!ret) {
- set_bit(key->keyidx, sc->sc_keymap);
- key->hw_key_idx = key->keyidx;
+ 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->alg == ALG_TKIP)
key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+ ret = 0;
}
break;
case DISABLE_KEY:
ath_key_delete(sc, key);
- clear_bit(key->keyidx, sc->sc_keymap);
break;
default:
ret = -EINVAL;
@@ -2417,9 +2405,6 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
sc->sc_flags &= ~SC_OP_PROTECT_ENABLE;
}
- if (changed & BSS_CHANGED_HT)
- ath9k_ht_conf(sc, bss_conf);
-
if (changed & BSS_CHANGED_ASSOC) {
DPRINTF(sc, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
bss_conf->assoc);
@@ -2780,11 +2765,24 @@ static struct pci_driver ath_pci_driver = {
static int __init init_ath_pci(void)
{
+ int error;
+
printk(KERN_INFO "%s: %s\n", dev_info, ATH_PCI_VERSION);
+ /* Register rate control algorithm */
+ error = ath_rate_control_register();
+ if (error != 0) {
+ printk(KERN_ERR
+ "Unable to register rate control algorithm: %d\n",
+ error);
+ ath_rate_control_unregister();
+ return error;
+ }
+
if (pci_register_driver(&ath_pci_driver) < 0) {
printk(KERN_ERR
"ath_pci: No devices found, driver not installed.\n");
+ ath_rate_control_unregister();
pci_unregister_driver(&ath_pci_driver);
return -ENODEV;
}
@@ -2795,6 +2793,7 @@ module_init(init_ath_pci);
static void __exit exit_ath_pci(void)
{
+ ath_rate_control_unregister();
pci_unregister_driver(&ath_pci_driver);
printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
}
diff --git a/drivers/net/wireless/ath9k/rc.c b/drivers/net/wireless/ath9k/rc.c
index 0ae5988e0b6..04ab457a8fa 100644
--- a/drivers/net/wireless/ath9k/rc.c
+++ b/drivers/net/wireless/ath9k/rc.c
@@ -1498,7 +1498,8 @@ static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
__le16 fc = hdr->frame_control;
/* lowest rate for management and multicast/broadcast frames */
- if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1)) {
+ if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1) ||
+ !sta) {
tx_info->control.rates[0].idx = rate_lowest_index(sband, sta);
tx_info->control.rates[0].count =
is_multicast_ether_addr(hdr->addr1) ? 1 : ATH_MGT_TXMAXTRY;
diff --git a/drivers/net/wireless/ath9k/recv.c b/drivers/net/wireless/ath9k/recv.c
index f2327d8e9c2..462e08c3d09 100644
--- a/drivers/net/wireless/ath9k/recv.c
+++ b/drivers/net/wireless/ath9k/recv.c
@@ -111,33 +111,6 @@ static struct sk_buff *ath_rxbuf_alloc(struct ath_softc *sc, u32 len)
return skb;
}
-static int ath_rate2idx(struct ath_softc *sc, int rate)
-{
- int i = 0, cur_band, n_rates;
- struct ieee80211_hw *hw = sc->hw;
-
- cur_band = hw->conf.channel->band;
- n_rates = sc->sbands[cur_band].n_bitrates;
-
- for (i = 0; i < n_rates; i++) {
- if (sc->sbands[cur_band].bitrates[i].bitrate == rate)
- break;
- }
-
- /*
- * NB:mac80211 validates rx rate index against the supported legacy rate
- * index only (should be done against ht rates also), return the highest
- * legacy rate index for rx rate which does not match any one of the
- * supported basic and extended rates to make mac80211 happy.
- * The following hack will be cleaned up once the issue with
- * the rx rate index validation in mac80211 is fixed.
- */
- if (i == n_rates)
- return n_rates - 1;
-
- return i;
-}
-
/*
* For Decrypt or Demic errors, we only mark packet status here and always push
* up the frame up to let mac80211 handle the actual error case, be it no
@@ -147,9 +120,7 @@ static int ath_rx_prepare(struct sk_buff *skb, struct ath_desc *ds,
struct ieee80211_rx_status *rx_status, bool *decrypt_error,
struct ath_softc *sc)
{
- struct ath_rate_table *rate_table = sc->cur_rate_table;
struct ieee80211_hdr *hdr;
- int ratekbps, rix;
u8 ratecode;
__le16 fc;
@@ -204,15 +175,36 @@ static int ath_rx_prepare(struct sk_buff *skb, struct ath_desc *ds,
}
ratecode = ds->ds_rxstat.rs_rate;
- rix = rate_table->rateCodeToIndex[ratecode];
- ratekbps = rate_table->info[rix].ratekbps;
- /* HT rate */
if (ratecode & 0x80) {
+ /* HT rate */
+ rx_status->flag |= RX_FLAG_HT;
if (ds->ds_rxstat.rs_flags & ATH9K_RX_2040)
- ratekbps = (ratekbps * 27) / 13;
+ rx_status->flag |= RX_FLAG_40MHZ;
if (ds->ds_rxstat.rs_flags & ATH9K_RX_GI)
- ratekbps = (ratekbps * 10) / 9;
+ rx_status->flag |= RX_FLAG_SHORT_GI;
+ rx_status->rate_idx = ratecode & 0x7f;
+ } else {
+ int i = 0, cur_band, n_rates;
+ struct ieee80211_hw *hw = sc->hw;
+
+ cur_band = hw->conf.channel->band;
+ n_rates = sc->sbands[cur_band].n_bitrates;
+
+ for (i = 0; i < n_rates; i++) {
+ if (sc->sbands[cur_band].bitrates[i].hw_value ==
+ ratecode) {
+ rx_status->rate_idx = i;
+ break;
+ }
+
+ if (sc->sbands[cur_band].bitrates[i].hw_value_short ==
+ ratecode) {
+ rx_status->rate_idx = i;
+ rx_status->flag |= RX_FLAG_SHORTPRE;
+ break;
+ }
+ }
}
rx_status->mactime = ath_extend_tsf(sc, ds->ds_rxstat.rs_tstamp);
@@ -220,7 +212,6 @@ static int ath_rx_prepare(struct sk_buff *skb, struct ath_desc *ds,
rx_status->freq = sc->hw->conf.channel->center_freq;
rx_status->noise = sc->sc_ani.sc_noise_floor;
rx_status->signal = rx_status->noise + ds->ds_rxstat.rs_rssi;
- rx_status->rate_idx = ath_rate2idx(sc, (ratekbps / 100));
rx_status->antenna = ds->ds_rxstat.rs_antenna;
/* at 45 you will be able to use MCS 15 reliably. A more elaborate
@@ -529,6 +520,15 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush)
continue;
/*
+ * Synchronize the DMA transfer with CPU before
+ * 1. accessing the frame
+ * 2. requeueing the same buffer to h/w
+ */
+ pci_dma_sync_single_for_cpu(sc->pdev, bf->bf_buf_addr,
+ sc->rx.bufsize,
+ PCI_DMA_FROMDEVICE);
+
+ /*
* If we're asked to flush receive queue, directly
* chain it back at the queue without processing it.
*/
@@ -556,10 +556,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush)
if (!requeue_skb)
goto requeue;
- /* Sync and unmap the frame */
- pci_dma_sync_single_for_cpu(sc->pdev, bf->bf_buf_addr,
- sc->rx.bufsize,
- PCI_DMA_FROMDEVICE);
+ /* Unmap the frame */
pci_unmap_single(sc->pdev, bf->bf_buf_addr,
sc->rx.bufsize,
PCI_DMA_FROMDEVICE);
diff --git a/drivers/net/wireless/ath9k/xmit.c b/drivers/net/wireless/ath9k/xmit.c
index f9c309ed3a2..3bfc3b90f25 100644
--- a/drivers/net/wireless/ath9k/xmit.c
+++ b/drivers/net/wireless/ath9k/xmit.c
@@ -106,6 +106,7 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
struct ieee80211_hw *hw = sc->hw;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info);
+ int hdrlen, padsize;
DPRINTF(sc, ATH_DBG_XMIT, "TX complete: skb: %p\n", skb);
@@ -125,7 +126,26 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
tx_info->flags |= IEEE80211_TX_STAT_ACK;
}
- tx_info->status.rates[0].count = tx_status->retries + 1;
+ tx_info->status.rates[0].count = tx_status->retries;
+ if (tx_info->status.rates[0].flags & IEEE80211_TX_RC_MCS) {
+ /* Change idx from internal table index to MCS index */
+ int idx = tx_info->status.rates[0].idx;
+ struct ath_rate_table *rate_table = sc->cur_rate_table;
+ if (idx >= 0 && idx < rate_table->rate_cnt)
+ tx_info->status.rates[0].idx =
+ rate_table->info[idx].ratecode & 0x7f;
+ }
+
+ hdrlen = ieee80211_get_hdrlen_from_skb(skb);
+ padsize = hdrlen & 3;
+ if (padsize && hdrlen >= 24) {
+ /*
+ * Remove MAC header padding before giving the frame back to
+ * mac80211.
+ */
+ memmove(skb->data + padsize, skb->data, hdrlen);
+ skb_pull(skb, padsize);
+ }
ieee80211_tx_status(hw, skb);
}
diff --git a/drivers/net/wireless/b43/debugfs.c b/drivers/net/wireless/b43/debugfs.c
index 06a01da8016..e04fc91f569 100644
--- a/drivers/net/wireless/b43/debugfs.c
+++ b/drivers/net/wireless/b43/debugfs.c
@@ -731,6 +731,7 @@ static void b43_add_dynamic_debug(struct b43_wldev *dev)
add_dyn_dbg("debug_pwork_stop", B43_DBG_PWORK_STOP, 0);
add_dyn_dbg("debug_lo", B43_DBG_LO, 0);
add_dyn_dbg("debug_firmware", B43_DBG_FIRMWARE, 0);
+ add_dyn_dbg("debug_keys", B43_DBG_KEYS, 0);
#undef add_dyn_dbg
}
diff --git a/drivers/net/wireless/b43/debugfs.h b/drivers/net/wireless/b43/debugfs.h
index 22ffd02ba55..7886cbe2d1d 100644
--- a/drivers/net/wireless/b43/debugfs.h
+++ b/drivers/net/wireless/b43/debugfs.h
@@ -12,6 +12,7 @@ enum b43_dyndbg { /* Dynamic debugging features */
B43_DBG_PWORK_STOP,
B43_DBG_LO,
B43_DBG_FIRMWARE,
+ B43_DBG_KEYS,
__B43_NR_DYNDBG,
};
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index ba7a5ab7fe1..7b31a327b24 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -992,6 +992,52 @@ static void b43_clear_keys(struct b43_wldev *dev)
b43_key_clear(dev, i);
}
+static void b43_dump_keymemory(struct b43_wldev *dev)
+{
+ unsigned int i, index, offset;
+ DECLARE_MAC_BUF(macbuf);
+ u8 mac[ETH_ALEN];
+ u16 algo;
+ u32 rcmta0;
+ u16 rcmta1;
+ u64 hf;
+ struct b43_key *key;
+
+ if (!b43_debug(dev, B43_DBG_KEYS))
+ return;
+
+ hf = b43_hf_read(dev);
+ b43dbg(dev->wl, "Hardware key memory dump: USEDEFKEYS=%u\n",
+ !!(hf & B43_HF_USEDEFKEYS));
+ for (index = 0; index < dev->max_nr_keys; index++) {
+ key = &(dev->key[index]);
+ printk(KERN_DEBUG "Key slot %02u: %s",
+ index, (key->keyconf == NULL) ? " " : "*");
+ offset = dev->ktp + (index * B43_SEC_KEYSIZE);
+ for (i = 0; i < B43_SEC_KEYSIZE; i += 2) {
+ u16 tmp = b43_shm_read16(dev, B43_SHM_SHARED, offset + i);
+ printk("%02X%02X", (tmp & 0xFF), ((tmp >> 8) & 0xFF));
+ }
+
+ algo = b43_shm_read16(dev, B43_SHM_SHARED,
+ B43_SHM_SH_KEYIDXBLOCK + (index * 2));
+ printk(" Algo: %04X/%02X", algo, key->algorithm);
+
+ if (index >= 4) {
+ rcmta0 = b43_shm_read32(dev, B43_SHM_RCMTA,
+ ((index - 4) * 2) + 0);
+ rcmta1 = b43_shm_read16(dev, B43_SHM_RCMTA,
+ ((index - 4) * 2) + 1);
+ *((__le32 *)(&mac[0])) = cpu_to_le32(rcmta0);
+ *((__le16 *)(&mac[4])) = cpu_to_le16(rcmta1);
+ printk(" MAC: %s",
+ print_mac(macbuf, mac));
+ } else
+ printk(" DEFAULT KEY");
+ printk("\n");
+ }
+}
+
void b43_power_saving_ctl_bits(struct b43_wldev *dev, unsigned int ps_flags)
{
u32 macctl;
@@ -3324,7 +3370,6 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
unsigned long flags;
int antenna;
int err = 0;
- u32 savedirqs;
mutex_lock(&wl->mutex);
@@ -3335,24 +3380,14 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
dev = wl->current_dev;
phy = &dev->phy;
+ b43_mac_suspend(dev);
+
if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
b43_set_retry_limits(dev, conf->short_frame_max_tx_count,
conf->long_frame_max_tx_count);
changed &= ~IEEE80211_CONF_CHANGE_RETRY_LIMITS;
if (!changed)
- goto out_unlock_mutex;
-
- /* Disable IRQs while reconfiguring the device.
- * This makes it possible to drop the spinlock throughout
- * the reconfiguration process. */
- spin_lock_irqsave(&wl->irq_lock, flags);
- if (b43_status(dev) < B43_STAT_STARTED) {
- spin_unlock_irqrestore(&wl->irq_lock, flags);
- goto out_unlock_mutex;
- }
- savedirqs = b43_interrupt_disable(dev, B43_IRQ_ALL);
- spin_unlock_irqrestore(&wl->irq_lock, flags);
- b43_synchronize_irq(dev);
+ goto out_mac_enable;
/* Switch to the requested channel.
* The firmware takes care of races with the TX handler. */
@@ -3399,11 +3434,9 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
}
}
- spin_lock_irqsave(&wl->irq_lock, flags);
- b43_interrupt_enable(dev, savedirqs);
- mmiowb();
- spin_unlock_irqrestore(&wl->irq_lock, flags);
- out_unlock_mutex:
+out_mac_enable:
+ b43_mac_enable(dev);
+out_unlock_mutex:
mutex_unlock(&wl->mutex);
return err;
@@ -3461,27 +3494,12 @@ static void b43_op_bss_info_changed(struct ieee80211_hw *hw,
{
struct b43_wl *wl = hw_to_b43_wl(hw);
struct b43_wldev *dev;
- struct b43_phy *phy;
- unsigned long flags;
- u32 savedirqs;
mutex_lock(&wl->mutex);
dev = wl->current_dev;
- phy = &dev->phy;
-
- /* Disable IRQs while reconfiguring the device.
- * This makes it possible to drop the spinlock throughout
- * the reconfiguration process. */
- spin_lock_irqsave(&wl->irq_lock, flags);
- if (b43_status(dev) < B43_STAT_STARTED) {
- spin_unlock_irqrestore(&wl->irq_lock, flags);
+ if (!dev || b43_status(dev) < B43_STAT_STARTED)
goto out_unlock_mutex;
- }
- savedirqs = b43_interrupt_disable(dev, B43_IRQ_ALL);
- spin_unlock_irqrestore(&wl->irq_lock, flags);
- b43_synchronize_irq(dev);
-
b43_mac_suspend(dev);
if (changed & BSS_CHANGED_BASIC_RATES)
@@ -3495,13 +3513,7 @@ static void b43_op_bss_info_changed(struct ieee80211_hw *hw,
}
b43_mac_enable(dev);
-
- spin_lock_irqsave(&wl->irq_lock, flags);
- b43_interrupt_enable(dev, savedirqs);
- /* XXX: why? */
- mmiowb();
- spin_unlock_irqrestore(&wl->irq_lock, flags);
- out_unlock_mutex:
+out_unlock_mutex:
mutex_unlock(&wl->mutex);
return;
@@ -3599,15 +3611,18 @@ static int b43_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
default:
B43_WARN_ON(1);
}
+
out_unlock:
- spin_unlock_irqrestore(&wl->irq_lock, flags);
- mutex_unlock(&wl->mutex);
if (!err) {
b43dbg(wl, "%s hardware based encryption for keyidx: %d, "
"mac: %pM\n",
cmd == SET_KEY ? "Using" : "Disabling", key->keyidx,
addr);
+ b43_dump_keymemory(dev);
}
+ spin_unlock_irqrestore(&wl->irq_lock, flags);
+ mutex_unlock(&wl->mutex);
+
return err;
}
diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c
index af37abccccb..026b61c03fb 100644
--- a/drivers/net/wireless/b43/phy_common.c
+++ b/drivers/net/wireless/b43/phy_common.c
@@ -178,13 +178,27 @@ void b43_phy_unlock(struct b43_wldev *dev)
b43_power_saving_ctl_bits(dev, 0);
}
+static inline void assert_mac_suspended(struct b43_wldev *dev)
+{
+ if (!B43_DEBUG)
+ return;
+ if ((b43_status(dev) >= B43_STAT_INITIALIZED) &&
+ (dev->mac_suspended <= 0)) {
+ b43dbg(dev->wl, "PHY/RADIO register access with "
+ "enabled MAC.\n");
+ dump_stack();
+ }
+}
+
u16 b43_radio_read(struct b43_wldev *dev, u16 reg)
{
+ assert_mac_suspended(dev);
return dev->phy.ops->radio_read(dev, reg);
}
void b43_radio_write(struct b43_wldev *dev, u16 reg, u16 value)
{
+ assert_mac_suspended(dev);
dev->phy.ops->radio_write(dev, reg, value);
}
@@ -208,11 +222,13 @@ void b43_radio_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set)
u16 b43_phy_read(struct b43_wldev *dev, u16 reg)
{
+ assert_mac_suspended(dev);
return dev->phy.ops->phy_read(dev, reg);
}
void b43_phy_write(struct b43_wldev *dev, u16 reg, u16 value)
{
+ assert_mac_suspended(dev);
dev->phy.ops->phy_write(dev, reg, value);
}
@@ -280,8 +296,10 @@ void b43_software_rfkill(struct b43_wldev *dev, enum rfkill_state state)
state = RFKILL_STATE_SOFT_BLOCKED;
}
+ b43_mac_suspend(dev);
phy->ops->software_rfkill(dev, state);
phy->radio_on = (state == RFKILL_STATE_UNBLOCKED);
+ b43_mac_enable(dev);
}
/**
diff --git a/drivers/net/wireless/b43/phy_g.c b/drivers/net/wireless/b43/phy_g.c
index 232181f6333..3b1d673796c 100644
--- a/drivers/net/wireless/b43/phy_g.c
+++ b/drivers/net/wireless/b43/phy_g.c
@@ -3047,6 +3047,8 @@ static void b43_gphy_op_adjust_txpower(struct b43_wldev *dev)
int rfatt, bbatt;
u8 tx_control;
+ b43_mac_suspend(dev);
+
spin_lock_irq(&dev->wl->irq_lock);
/* Calculate the new attenuation values. */
@@ -3103,6 +3105,8 @@ static void b43_gphy_op_adjust_txpower(struct b43_wldev *dev)
gphy->tx_control);
b43_radio_unlock(dev);
b43_phy_unlock(dev);
+
+ b43_mac_enable(dev);
}
static enum b43_txpwr_result b43_gphy_op_recalc_txpower(struct b43_wldev *dev,
@@ -3215,9 +3219,9 @@ static void b43_gphy_op_pwork_15sec(struct b43_wldev *dev)
struct b43_phy *phy = &dev->phy;
struct b43_phy_g *gphy = phy->g;
+ b43_mac_suspend(dev);
//TODO: update_aci_moving_average
if (gphy->aci_enable && gphy->aci_wlan_automatic) {
- b43_mac_suspend(dev);
if (!gphy->aci_enable && 1 /*TODO: not scanning? */ ) {
if (0 /*TODO: bunch of conditions */ ) {
phy->ops->interf_mitigation(dev,
@@ -3227,12 +3231,12 @@ static void b43_gphy_op_pwork_15sec(struct b43_wldev *dev)
if (/*(aci_average > 1000) &&*/ !b43_gphy_aci_scan(dev))
phy->ops->interf_mitigation(dev, B43_INTERFMODE_NONE);
}
- b43_mac_enable(dev);
} else if (gphy->interfmode == B43_INTERFMODE_NONWLAN &&
phy->rev == 1) {
//TODO: implement rev1 workaround
}
b43_lo_g_maintanance_work(dev);
+ b43_mac_enable(dev);
}
static void b43_gphy_op_pwork_60sec(struct b43_wldev *dev)
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c
index d0c9bdcf393..8fdb34222c0 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945.c
+++ b/drivers/net/wireless/iwlwifi/iwl-3945.c
@@ -2482,7 +2482,6 @@ unsigned int iwl3945_hw_get_beacon_cmd(struct iwl3945_priv *priv,
frame_size = iwl3945_fill_beacon_frame(priv,
tx_beacon_cmd->frame,
- iwl3945_broadcast_addr,
sizeof(frame->u) - sizeof(*tx_beacon_cmd));
BUG_ON(frame_size > MAX_MPDU_SIZE);
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.h b/drivers/net/wireless/iwlwifi/iwl-3945.h
index ec6084c8fd1..2c0ddc5110c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945.h
+++ b/drivers/net/wireless/iwlwifi/iwl-3945.h
@@ -405,12 +405,6 @@ struct iwl3945_rx_queue {
#define SCAN_INTERVAL 100
-#define MAX_A_CHANNELS 252
-#define MIN_A_CHANNELS 7
-
-#define MAX_B_CHANNELS 14
-#define MIN_B_CHANNELS 1
-
#define STATUS_HCMD_ACTIVE 0 /* host command in progress */
#define STATUS_HCMD_SYNC_ACTIVE 1 /* sync host command in progress */
#define STATUS_INT_ENABLED 2
@@ -590,8 +584,7 @@ extern int iwl3945_send_cmd_pdu(struct iwl3945_priv *priv, u8 id, u16 len,
extern int __must_check iwl3945_send_cmd(struct iwl3945_priv *priv,
struct iwl3945_host_cmd *cmd);
extern unsigned int iwl3945_fill_beacon_frame(struct iwl3945_priv *priv,
- struct ieee80211_hdr *hdr,
- const u8 *dest, int left);
+ struct ieee80211_hdr *hdr,int left);
extern int iwl3945_rx_queue_update_write_ptr(struct iwl3945_priv *priv,
struct iwl3945_rx_queue *q);
extern int iwl3945_send_statistics_request(struct iwl3945_priv *priv);
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c
index a7e6e32bd48..5a72bc0377d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-4965.c
+++ b/drivers/net/wireless/iwlwifi/iwl-4965.c
@@ -426,7 +426,6 @@ static void iwl4965_nic_config(struct iwl_priv *priv)
static int iwl4965_apm_stop_master(struct iwl_priv *priv)
{
- int ret = 0;
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
@@ -434,16 +433,13 @@ static int iwl4965_apm_stop_master(struct iwl_priv *priv)
/* set stop master bit */
iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER);
- ret = iwl_poll_direct_bit(priv, CSR_RESET,
+ iwl_poll_direct_bit(priv, CSR_RESET,
CSR_RESET_REG_FLAG_MASTER_DISABLED, 100);
- if (ret < 0)
- goto out;
-out:
spin_unlock_irqrestore(&priv->lock, flags);
IWL_DEBUG_INFO("stop master\n");
- return ret;
+ return 0;
}
static void iwl4965_apm_stop(struct iwl_priv *priv)
@@ -2354,7 +2350,7 @@ module_param_named(disable, iwl4965_mod_params.disable, int, 0444);
MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])");
module_param_named(swcrypto, iwl4965_mod_params.sw_crypto, int, 0444);
MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])");
-module_param_named(debug, iwl4965_mod_params.debug, int, 0444);
+module_param_named(debug, iwl4965_mod_params.debug, uint, 0444);
MODULE_PARM_DESC(debug, "debug output mask");
module_param_named(
disable_hw_scan, iwl4965_mod_params.disable_hw_scan, int, 0444);
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c
index 2344de94597..66d053d28a7 100644
--- a/drivers/net/wireless/iwlwifi/iwl-5000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-5000.c
@@ -73,7 +73,6 @@ static const u16 iwl5000_default_queue_to_tx_fifo[] = {
/* FIXME: same implementation as 4965 */
static int iwl5000_apm_stop_master(struct iwl_priv *priv)
{
- int ret = 0;
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
@@ -81,16 +80,13 @@ static int iwl5000_apm_stop_master(struct iwl_priv *priv)
/* set stop master bit */
iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER);
- ret = iwl_poll_direct_bit(priv, CSR_RESET,
+ iwl_poll_direct_bit(priv, CSR_RESET,
CSR_RESET_REG_FLAG_MASTER_DISABLED, 100);
- if (ret < 0)
- goto out;
-out:
spin_unlock_irqrestore(&priv->lock, flags);
IWL_DEBUG_INFO("stop master\n");
- return ret;
+ return 0;
}
@@ -1623,7 +1619,7 @@ MODULE_PARM_DESC(disable50,
module_param_named(swcrypto50, iwl50_mod_params.sw_crypto, bool, 0444);
MODULE_PARM_DESC(swcrypto50,
"using software crypto engine (default 0 [hardware])\n");
-module_param_named(debug50, iwl50_mod_params.debug, int, 0444);
+module_param_named(debug50, iwl50_mod_params.debug, uint, 0444);
MODULE_PARM_DESC(debug50, "50XX debug output mask");
module_param_named(queues_num50, iwl50_mod_params.num_of_queues, int, 0444);
MODULE_PARM_DESC(queues_num50, "number of hw queues in 50xx series");
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
index 37b892044a1..f3f17929ca0 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
@@ -836,6 +836,10 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
(hw->wiphy->bands[priv->band]->bitrates[rs_index].bitrate !=
hw->wiphy->bands[info->band]->bitrates[info->status.rates[0].idx].bitrate)) {
IWL_DEBUG_RATE("initial rate does not match 0x%x\n", tx_rate);
+ /* the last LQ command could failed so the LQ in ucode not
+ * the same in driver sync up
+ */
+ iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC);
goto out;
}
@@ -2167,6 +2171,7 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband,
struct iwl_priv *priv = (struct iwl_priv *)priv_r;
struct ieee80211_conf *conf = &priv->hw->conf;
struct iwl_lq_sta *lq_sta = priv_sta;
+ u16 mask_bit = 0;
lq_sta->flush_timer = 0;
lq_sta->supp_rates = sta->supp_rates[sband->band];
@@ -2200,16 +2205,6 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband,
priv->assoc_station_added = 1;
}
- /* Find highest tx rate supported by hardware and destination station */
- lq_sta->last_txrate_idx = 3;
- for (i = 0; i < sband->n_bitrates; i++)
- if (sta->supp_rates[sband->band] & BIT(i))
- lq_sta->last_txrate_idx = i;
-
- /* For MODE_IEEE80211A, skip over cck rates in global rate table */
- if (sband->band == IEEE80211_BAND_5GHZ)
- lq_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE;
-
lq_sta->is_dup = 0;
lq_sta->is_green = rs_use_green(priv, conf);
lq_sta->active_legacy_rate = priv->active_rate & ~(0x1000);
@@ -2248,6 +2243,17 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband,
lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID;
lq_sta->drv = priv;
+ /* Find highest tx rate supported by hardware and destination station */
+ mask_bit = sta->supp_rates[sband->band] & lq_sta->active_legacy_rate;
+ lq_sta->last_txrate_idx = 3;
+ for (i = 0; i < sband->n_bitrates; i++)
+ if (mask_bit & BIT(i))
+ lq_sta->last_txrate_idx = i;
+
+ /* For MODE_IEEE80211A, skip over cck rates in global rate table */
+ if (sband->band == IEEE80211_BAND_5GHZ)
+ lq_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE;
+
rs_initialize_lq(priv, conf, sta, lq_sta);
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index 2f5e86e1291..5da6b35cd26 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -371,7 +371,7 @@ static void iwl_free_frame(struct iwl_priv *priv, struct iwl_frame *frame)
static unsigned int iwl_fill_beacon_frame(struct iwl_priv *priv,
struct ieee80211_hdr *hdr,
- const u8 *dest, int left)
+ int left)
{
if (!iwl_is_associated(priv) || !priv->ibss_beacon ||
((priv->iw_mode != NL80211_IFTYPE_ADHOC) &&
@@ -424,7 +424,6 @@ static unsigned int iwl_hw_get_beacon_cmd(struct iwl_priv *priv,
tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
frame_size = iwl_fill_beacon_frame(priv, tx_beacon_cmd->frame,
- iwl_bcast_addr,
sizeof(frame->u) - sizeof(*tx_beacon_cmd));
BUG_ON(frame_size > MAX_MPDU_SIZE);
@@ -515,19 +514,27 @@ static void iwl_ht_conf(struct iwl_priv *priv,
iwl_conf->supported_chan_width =
!!(ht_conf->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40);
- iwl_conf->extension_chan_offset = bss_conf->ht.secondary_channel_offset;
+ /*
+ * XXX: The HT configuration needs to be moved into iwl_mac_config()
+ * to be done there correctly.
+ */
+
+ iwl_conf->extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
+ if (priv->hw->conf.ht.channel_type == NL80211_CHAN_HT40MINUS)
+ iwl_conf->extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+ else if(priv->hw->conf.ht.channel_type == NL80211_CHAN_HT40PLUS)
+ iwl_conf->extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+
/* If no above or below channel supplied disable FAT channel */
if (iwl_conf->extension_chan_offset != IEEE80211_HT_PARAM_CHA_SEC_ABOVE &&
- iwl_conf->extension_chan_offset != IEEE80211_HT_PARAM_CHA_SEC_BELOW) {
- iwl_conf->extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
+ iwl_conf->extension_chan_offset != IEEE80211_HT_PARAM_CHA_SEC_BELOW)
iwl_conf->supported_chan_width = 0;
- }
iwl_conf->sm_ps = (u8)((ht_conf->cap & IEEE80211_HT_CAP_SM_PS) >> 2);
memcpy(&iwl_conf->mcs, &ht_conf->mcs, 16);
- iwl_conf->tx_chan_width = bss_conf->ht.width_40_ok;
+ iwl_conf->tx_chan_width = iwl_conf->supported_chan_width != 0;
iwl_conf->ht_protection =
bss_conf->ht.operation_mode & IEEE80211_HT_OP_MODE_PROTECTION;
iwl_conf->non_GF_STA_present =
@@ -1103,16 +1110,6 @@ static void iwl_setup_rx_handlers(struct iwl_priv *priv)
priv->cfg->ops->lib->rx_handler_setup(priv);
}
-/*
- * this should be called while priv->lock is locked
-*/
-static void __iwl_rx_replenish(struct iwl_priv *priv)
-{
- iwl_rx_allocate(priv);
- iwl_rx_queue_restock(priv);
-}
-
-
/**
* iwl_rx_handle - Main entry function for receiving responses from uCode
*
@@ -1221,7 +1218,7 @@ void iwl_rx_handle(struct iwl_priv *priv)
count++;
if (count >= 8) {
priv->rxq.read = i;
- __iwl_rx_replenish(priv);
+ iwl_rx_queue_restock(priv);
count = 0;
}
}
@@ -3335,7 +3332,7 @@ static int iwl_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb)
/*
* The following adds a new attribute to the sysfs representation
- * of this device driver (i.e. a new file in /sys/bus/pci/drivers/iwl/)
+ * of this device driver (i.e. a new file in /sys/class/net/wlan0/device/)
* used for controlling the debug level.
*
* See the level definitions in iwl for details.
@@ -3421,7 +3418,11 @@ static ssize_t show_tx_power(struct device *d,
struct device_attribute *attr, char *buf)
{
struct iwl_priv *priv = (struct iwl_priv *)d->driver_data;
- return sprintf(buf, "%d\n", priv->tx_power_user_lmt);
+
+ if (!iwl_is_ready_rf(priv))
+ return sprintf(buf, "off\n");
+ else
+ return sprintf(buf, "%d\n", priv->tx_power_user_lmt);
}
static ssize_t store_tx_power(struct device *d,
diff --git a/drivers/net/wireless/iwlwifi/iwl-calib.c b/drivers/net/wireless/iwlwifi/iwl-calib.c
index 7956e087361..f836ecc5575 100644
--- a/drivers/net/wireless/iwlwifi/iwl-calib.c
+++ b/drivers/net/wireless/iwlwifi/iwl-calib.c
@@ -70,6 +70,15 @@
* INIT calibrations framework
*****************************************************************************/
+struct statistics_general_data {
+ u32 beacon_silence_rssi_a;
+ u32 beacon_silence_rssi_b;
+ u32 beacon_silence_rssi_c;
+ u32 beacon_energy_a;
+ u32 beacon_energy_b;
+ u32 beacon_energy_c;
+};
+
int iwl_send_calib_results(struct iwl_priv *priv)
{
int ret = 0;
diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h
index 60e79d9bd72..52966ffbef6 100644
--- a/drivers/net/wireless/iwlwifi/iwl-commands.h
+++ b/drivers/net/wireless/iwlwifi/iwl-commands.h
@@ -2418,6 +2418,8 @@ struct statistics_rx_ht_phy {
__le32 reserved2;
} __attribute__ ((packed));
+#define INTERFERENCE_DATA_AVAILABLE __constant_cpu_to_le32(1)
+
struct statistics_rx_non_phy {
__le32 bogus_cts; /* CTS received when not expecting CTS */
__le32 bogus_ack; /* ACK received when not expecting ACK */
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
index 08b842f8576..7c3a20a986b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.h
+++ b/drivers/net/wireless/iwlwifi/iwl-core.h
@@ -154,7 +154,7 @@ struct iwl_ops {
struct iwl_mod_params {
int disable; /* def: 0 = enable radio */
int sw_crypto; /* def: 0 = using hardware encryption */
- int debug; /* def: 0 = minimal debug log messages */
+ u32 debug; /* def: 0 = minimal debug log messages */
int disable_hw_scan; /* def: 0 = use h/w scan */
int num_of_queues; /* def: HW dependent */
int num_of_ampdu_queues;/* def: HW dependent */
diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h
index e4c264b4f7b..56c13b458de 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debug.h
+++ b/drivers/net/wireless/iwlwifi/iwl-debug.h
@@ -96,28 +96,25 @@ static inline void iwl_dbgfs_unregister(struct iwl_priv *priv)
#endif /* CONFIG_IWLWIFI_DEBUGFS */
/*
- * To use the debug system;
+ * To use the debug system:
*
* If you are defining a new debug classification, simply add it to the #define
- * list here in the form of:
+ * list here in the form of
*
* #define IWL_DL_xxxx VALUE
*
- * shifting value to the left one bit from the previous entry. xxxx should be
- * the name of the classification (for example, WEP)
+ * where xxxx should be the name of the classification (for example, WEP).
*
* You then need to either add a IWL_xxxx_DEBUG() macro definition for your
* classification, or use IWL_DEBUG(IWL_DL_xxxx, ...) whenever you want
* to send output to that classification.
*
- * To add your debug level to the list of levels seen when you perform
+ * The active debug levels can be accessed via files
*
- * % cat /sys/class/net/wlanX/device/debug_level
+ * /sys/module/iwlagn/parameters/debug{50}
+ * /sys/class/net/wlan0/device/debug_level
*
- * you simply need to add your entry to the iwl_debug_levels array.
- *
- * If you do not see debug_level in /sys/class/net/wlanX/device/debug_level
- * then you do not have CONFIG_IWLWIFI_DEBUG defined in your kernel config file
+ * when CONFIG_IWLWIFI_DEBUG=y.
*/
#define IWL_DL_INFO (1 << 0)
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h
index eaf0c9cc33a..0468fcc1ea9 100644
--- a/drivers/net/wireless/iwlwifi/iwl-dev.h
+++ b/drivers/net/wireless/iwlwifi/iwl-dev.h
@@ -323,14 +323,6 @@ struct iwl_rx_queue {
#define IWL_SUPPORTED_RATES_IE_LEN 8
-#define SCAN_INTERVAL 100
-
-#define MAX_A_CHANNELS 252
-#define MIN_A_CHANNELS 7
-
-#define MAX_B_CHANNELS 14
-#define MIN_B_CHANNELS 1
-
#define MAX_TID_COUNT 9
#define IWL_INVALID_RATE 0xFF
@@ -496,8 +488,6 @@ struct iwl_sensitivity_ranges {
};
-#define IWL_FAT_CHANNEL_52 BIT(IEEE80211_BAND_5GHZ)
-
#define KELVIN_TO_CELSIUS(x) ((x)-273)
#define CELSIUS_TO_KELVIN(x) ((x)+273)
@@ -546,9 +536,6 @@ struct iwl_hw_params {
const struct iwl_sensitivity_ranges *sens;
};
-#define HT_SHORT_GI_20MHZ (1 << 0)
-#define HT_SHORT_GI_40MHZ (1 << 1)
-
/******************************************************************************
*
@@ -590,15 +577,15 @@ static inline u8 get_cmd_index(struct iwl_queue *q, u32 index, int is_huge)
}
-struct iwl_priv;
-
-
struct iwl_dma_ptr {
dma_addr_t dma;
void *addr;
size_t size;
};
+#define HT_SHORT_GI_20MHZ (1 << 0)
+#define HT_SHORT_GI_40MHZ (1 << 1)
+
#define IWL_CHANNEL_WIDTH_20MHZ 0
#define IWL_CHANNEL_WIDTH_40MHZ 1
@@ -613,7 +600,6 @@ struct iwl_dma_ptr {
#define TX_POWER_IWL_ILLEGAL_VOLTAGE -10000
/* Sensitivity and chain noise calibration */
-#define INTERFERENCE_DATA_AVAILABLE __constant_cpu_to_le32(1)
#define INITIALIZATION_VALUE 0xFFFF
#define CAL_NUM_OF_BEACONS 20
#define MAXIMUM_ALLOWED_PATHLOSS 15
@@ -666,15 +652,6 @@ enum iwl4965_calib_enabled_state {
IWL_CALIB_ENABLED = 1,
};
-struct statistics_general_data {
- u32 beacon_silence_rssi_a;
- u32 beacon_silence_rssi_b;
- u32 beacon_silence_rssi_c;
- u32 beacon_energy_a;
- u32 beacon_energy_b;
- u32 beacon_energy_c;
-};
-
/*
* enum iwl_calib
diff --git a/drivers/net/wireless/iwlwifi/iwl-io.h b/drivers/net/wireless/iwlwifi/iwl-io.h
index 998ac197169..0a92e7431ad 100644
--- a/drivers/net/wireless/iwlwifi/iwl-io.h
+++ b/drivers/net/wireless/iwlwifi/iwl-io.h
@@ -87,17 +87,18 @@ static inline u32 __iwl_read32(char *f, u32 l, struct iwl_priv *priv, u32 ofs)
#define iwl_read32(p, o) _iwl_read32(p, o)
#endif
+#define IWL_POLL_INTERVAL 10 /* microseconds */
static inline int _iwl_poll_bit(struct iwl_priv *priv, u32 addr,
u32 bits, u32 mask, int timeout)
{
- int i = 0;
+ int t = 0;
do {
if ((_iwl_read32(priv, addr) & mask) == (bits & mask))
- return i;
- udelay(10);
- i += 10;
- } while (i < timeout);
+ return t;
+ udelay(IWL_POLL_INTERVAL);
+ t += IWL_POLL_INTERVAL;
+ } while (t < timeout);
return -ETIMEDOUT;
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-led.c b/drivers/net/wireless/iwlwifi/iwl-led.c
index dce32ff8dec..11eccd7d268 100644
--- a/drivers/net/wireless/iwlwifi/iwl-led.c
+++ b/drivers/net/wireless/iwlwifi/iwl-led.c
@@ -292,7 +292,7 @@ static int iwl_get_blink_rate(struct iwl_priv *priv)
if (tpt > (blink_tbl[i].tpt * IWL_1MB_RATE))
break;
- IWL_DEBUG_LED("LED BLINK IDX=%d", i);
+ IWL_DEBUG_LED("LED BLINK IDX=%d\n", i);
return i;
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c
index 919a775121e..c5f1aa0feac 100644
--- a/drivers/net/wireless/iwlwifi/iwl-rx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-rx.c
@@ -244,25 +244,31 @@ void iwl_rx_allocate(struct iwl_priv *priv)
struct list_head *element;
struct iwl_rx_mem_buffer *rxb;
unsigned long flags;
- spin_lock_irqsave(&rxq->lock, flags);
- while (!list_empty(&rxq->rx_used)) {
+
+ while (1) {
+ spin_lock_irqsave(&rxq->lock, flags);
+
+ if (list_empty(&rxq->rx_used)) {
+ spin_unlock_irqrestore(&rxq->lock, flags);
+ return;
+ }
element = rxq->rx_used.next;
rxb = list_entry(element, struct iwl_rx_mem_buffer, list);
+ list_del(element);
+
+ spin_unlock_irqrestore(&rxq->lock, flags);
/* Alloc a new receive buffer */
rxb->skb = alloc_skb(priv->hw_params.rx_buf_size + 256,
- __GFP_NOWARN | GFP_ATOMIC);
+ GFP_KERNEL);
if (!rxb->skb) {
- if (net_ratelimit())
- printk(KERN_CRIT DRV_NAME
- ": Can not allocate SKB buffers\n");
+ printk(KERN_CRIT DRV_NAME
+ "Can not allocate SKB buffers\n");
/* We don't reschedule replenish work here -- we will
* call the restock method and if it still needs
* more buffers it will schedule replenish */
break;
}
- priv->alloc_rxb_skb++;
- list_del(element);
/* Get physical address of RB/SKB */
rxb->real_dma_addr = pci_map_single(
@@ -276,12 +282,15 @@ void iwl_rx_allocate(struct iwl_priv *priv)
rxb->aligned_dma_addr = ALIGN(rxb->real_dma_addr, 256);
skb_reserve(rxb->skb, rxb->aligned_dma_addr - rxb->real_dma_addr);
+ spin_lock_irqsave(&rxq->lock, flags);
+
list_add_tail(&rxb->list, &rxq->rx_free);
rxq->free_count++;
+ priv->alloc_rxb_skb++;
+
+ spin_unlock_irqrestore(&rxq->lock, flags);
}
- spin_unlock_irqrestore(&rxq->lock, flags);
}
-EXPORT_SYMBOL(iwl_rx_allocate);
void iwl_rx_replenish(struct iwl_priv *priv)
{
diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c
index 1e7cd8d9039..b0ee86c6268 100644
--- a/drivers/net/wireless/iwlwifi/iwl-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-tx.c
@@ -645,7 +645,7 @@ static void iwl_tx_cmd_build_basic(struct iwl_priv *priv,
struct iwl_tx_cmd *tx_cmd,
struct ieee80211_tx_info *info,
struct ieee80211_hdr *hdr,
- int is_unicast, u8 std_id)
+ u8 std_id)
{
__le16 fc = hdr->frame_control;
__le32 tx_flags = tx_cmd->tx_flags;
@@ -834,7 +834,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
u16 len, len_org;
u16 seq_number = 0;
__le16 fc;
- u8 hdr_len, unicast;
+ u8 hdr_len;
u8 sta_id;
u8 wait_write_ptr = 0;
u8 tid = 0;
@@ -854,8 +854,6 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
goto drop_unlock;
}
- unicast = !is_multicast_ether_addr(hdr->addr1);
-
fc = hdr->frame_control;
#ifdef CONFIG_IWLWIFI_DEBUG
@@ -994,7 +992,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
len = (u16)skb->len;
tx_cmd->len = cpu_to_le16(len);
/* TODO need this for burst mode later on */
- iwl_tx_cmd_build_basic(priv, tx_cmd, info, hdr, unicast, sta_id);
+ iwl_tx_cmd_build_basic(priv, tx_cmd, info, hdr, sta_id);
/* set is_hcca to 0; it probably will never be implemented */
iwl_tx_cmd_build_rate(priv, tx_cmd, info, fc, sta_id, 0);
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
index 352ccac7187..d64580805d6 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -65,7 +65,7 @@ static int iwl3945_tx_queue_update_write_ptr(struct iwl3945_priv *priv,
/* module parameters */
static int iwl3945_param_disable_hw_scan; /* def: 0 = use 3945's h/w scan */
-static int iwl3945_param_debug; /* def: 0 = minimal debug log messages */
+static u32 iwl3945_param_debug; /* def: 0 = minimal debug log messages */
static int iwl3945_param_disable; /* def: 0 = enable radio */
static int iwl3945_param_antenna; /* def: 0 = both antennas (use diversity) */
int iwl3945_param_hwcrypto; /* def: 0 = use software encryption */
@@ -1402,7 +1402,7 @@ static void iwl3945_free_frame(struct iwl3945_priv *priv, struct iwl3945_frame *
unsigned int iwl3945_fill_beacon_frame(struct iwl3945_priv *priv,
struct ieee80211_hdr *hdr,
- const u8 *dest, int left)
+ int left)
{
if (!iwl3945_is_associated(priv) || !priv->ibss_beacon ||
@@ -8343,7 +8343,7 @@ MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])");
module_param_named(hwcrypto, iwl3945_param_hwcrypto, int, 0444);
MODULE_PARM_DESC(hwcrypto,
"using hardware crypto engine (default 0 [software])\n");
-module_param_named(debug, iwl3945_param_debug, int, 0444);
+module_param_named(debug, iwl3945_param_debug, uint, 0444);
MODULE_PARM_DESC(debug, "debug output mask");
module_param_named(disable_hw_scan, iwl3945_param_disable_hw_scan, int, 0444);
MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 0)");
diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c
index b54e2ea8346..4519d7314f4 100644
--- a/drivers/net/wireless/libertas/if_sdio.c
+++ b/drivers/net/wireless/libertas/if_sdio.c
@@ -26,6 +26,7 @@
* if_sdio_card_to_host() to pad the data.
*/
+#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include <linux/firmware.h>
#include <linux/netdevice.h>
@@ -581,7 +582,7 @@ static int if_sdio_prog_real(struct if_sdio_card *card)
chunk_size, (chunk_size + 31) / 32 * 32);
*/
ret = sdio_writesb(card->func, card->ioport,
- chunk_buffer, (chunk_size + 31) / 32 * 32);
+ chunk_buffer, roundup(chunk_size, 32));
if (ret)
goto release;
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index fd5a537ac51..f83d69e813d 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -495,11 +495,9 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
}
if (changed & BSS_CHANGED_HT) {
- printk(KERN_DEBUG " %s: HT: sec_ch_offs=%d width_40_ok=%d "
- "op_mode=%d\n",
+ printk(KERN_DEBUG " %s: HT: op_mode=0x%x\n",
wiphy_name(hw->wiphy),
- info->ht.secondary_channel_offset,
- info->ht.width_40_ok, info->ht.operation_mode);
+ info->ht.operation_mode);
}
if (changed & BSS_CHANGED_BASIC_RATES) {
diff --git a/drivers/net/wireless/p54/p54.h b/drivers/net/wireless/p54/p54.h
index d2dbb9e15d9..ab79e32f0b2 100644
--- a/drivers/net/wireless/p54/p54.h
+++ b/drivers/net/wireless/p54/p54.h
@@ -44,6 +44,9 @@ enum p54_control_frame_types {
P54_CONTROL_TYPE_BT_OPTIONS = 35
};
+#define P54_HDR_FLAG_CONTROL BIT(15)
+#define P54_HDR_FLAG_CONTROL_OPSET (BIT(15) + BIT(0))
+
struct p54_hdr {
__le16 flags;
__le16 len;
@@ -54,6 +57,10 @@ struct p54_hdr {
u8 data[0];
} __attribute__ ((packed));
+#define FREE_AFTER_TX(skb) \
+ ((((struct p54_hdr *) ((struct sk_buff *) skb)->data)-> \
+ flags) == cpu_to_le16(P54_HDR_FLAG_CONTROL_OPSET))
+
struct p54_edcf_queue_param {
__le16 aifs;
__le16 cwmin;
@@ -61,6 +68,13 @@ struct p54_edcf_queue_param {
__le16 txop;
} __attribute__ ((packed));
+struct p54_rssi_linear_approximation {
+ s16 mul;
+ s16 add;
+ s16 longbow_unkn;
+ s16 longbow_unk2;
+};
+
#define EEPROM_READBACK_LEN 0x3fc
#define ISL38XX_DEV_FIRMWARE_ADDR 0x20000
@@ -71,11 +85,11 @@ struct p54_edcf_queue_param {
#define FW_LM20 0x4c4d3230
struct p54_common {
+ struct ieee80211_hw *hw;
u32 rx_start;
u32 rx_end;
struct sk_buff_head tx_queue;
- void (*tx)(struct ieee80211_hw *dev, struct sk_buff *skb,
- int free_on_tx);
+ void (*tx)(struct ieee80211_hw *dev, struct sk_buff *skb);
int (*open)(struct ieee80211_hw *dev);
void (*stop)(struct ieee80211_hw *dev);
int mode;
@@ -90,6 +104,7 @@ struct p54_common {
struct pda_channel_output_limit *output_limit;
unsigned int output_limit_len;
struct pda_pa_curve_data *curve_data;
+ struct p54_rssi_linear_approximation rssical_db[IEEE80211_NUM_BANDS];
unsigned int filter_flags;
bool use_short_slot;
u16 rxhw;
@@ -106,9 +121,7 @@ struct p54_common {
struct ieee80211_tx_queue_stats tx_stats[8];
struct p54_edcf_queue_param qos_params[8];
struct ieee80211_low_level_stats stats;
- struct timer_list stats_timer;
- struct completion stats_comp;
- struct sk_buff *cached_stats;
+ struct delayed_work work;
struct sk_buff *cached_beacon;
int noise;
void *eeprom;
diff --git a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c
index a4e99b02af0..82354b974a0 100644
--- a/drivers/net/wireless/p54/p54common.c
+++ b/drivers/net/wireless/p54/p54common.c
@@ -335,6 +335,36 @@ static const char *p54_rf_chips[] = { "NULL", "Duette3", "Duette2",
"Frisbee", "Xbow", "Longbow", "NULL", "NULL" };
static int p54_init_xbow_synth(struct ieee80211_hw *dev);
+static void p54_parse_rssical(struct ieee80211_hw *dev, void *data, int len,
+ u16 type)
+{
+ struct p54_common *priv = dev->priv;
+ int offset = (type == PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) ? 2 : 0;
+ int entry_size = sizeof(struct pda_rssi_cal_entry) + offset;
+ int num_entries = (type == PDR_RSSI_LINEAR_APPROXIMATION) ? 1 : 2;
+ int i;
+
+ if (len != (entry_size * num_entries)) {
+ printk(KERN_ERR "%s: unknown rssi calibration data packing "
+ " type:(%x) len:%d.\n",
+ wiphy_name(dev->wiphy), type, len);
+
+ print_hex_dump_bytes("rssical:", DUMP_PREFIX_NONE,
+ data, len);
+
+ printk(KERN_ERR "%s: please report this issue.\n",
+ wiphy_name(dev->wiphy));
+ return;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ struct pda_rssi_cal_entry *cal = data +
+ (offset + i * entry_size);
+ priv->rssical_db[i].mul = (s16) le16_to_cpu(cal->mul);
+ priv->rssical_db[i].add = (s16) le16_to_cpu(cal->add);
+ }
+}
+
static int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
{
struct p54_common *priv = dev->priv;
@@ -434,6 +464,12 @@ static int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
case PDR_HARDWARE_PLATFORM_COMPONENT_ID:
priv->version = *(u8 *)(entry->data + 1);
break;
+ case PDR_RSSI_LINEAR_APPROXIMATION:
+ case PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND:
+ case PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED:
+ p54_parse_rssical(dev, entry->data, data_len,
+ le16_to_cpu(entry->code));
+ break;
case PDR_END:
/* make it overrun */
entry_len = len;
@@ -453,10 +489,7 @@ static int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
case PDR_DEFAULT_COUNTRY:
case PDR_ANTENNA_GAIN:
case PDR_PRISM_INDIGO_PA_CALIBRATION_DATA:
- case PDR_RSSI_LINEAR_APPROXIMATION:
- case PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND:
case PDR_REGULATORY_POWER_LIMITS:
- case PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED:
case PDR_RADIATED_TRANSMISSION_CORRECTION:
case PDR_PRISM_TX_IQ_CALIBRATION:
case PDR_BASEBAND_REGISTERS:
@@ -527,8 +560,11 @@ static int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
static int p54_rssi_to_dbm(struct ieee80211_hw *dev, int rssi)
{
- /* TODO: get the rssi_add & rssi_mul data from the eeprom */
- return ((rssi * 0x83) / 64 - 400) / 4;
+ struct p54_common *priv = dev->priv;
+ int band = dev->conf.channel->band;
+
+ return ((rssi * priv->rssical_db[band].mul) / 64 +
+ priv->rssical_db[band].add) / 4;
}
static int p54_rx_data(struct ieee80211_hw *dev, struct sk_buff *skb)
@@ -589,6 +625,9 @@ static int p54_rx_data(struct ieee80211_hw *dev, struct sk_buff *skb)
ieee80211_rx_irqsafe(dev, skb, &rx_status);
+ queue_delayed_work(dev->workqueue, &priv->work,
+ msecs_to_jiffies(P54_STATISTICS_UPDATE));
+
return -1;
}
@@ -644,7 +683,7 @@ void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb)
freed = priv->rx_end - last_addr;
__skb_unlink(skb, &priv->tx_queue);
spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
if (freed >= priv->headroom + sizeof(struct p54_hdr) + 48 +
IEEE80211_MAX_RTS_THRESHOLD + priv->tailroom)
@@ -652,6 +691,27 @@ void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb)
}
EXPORT_SYMBOL_GPL(p54_free_skb);
+static struct sk_buff *p54_find_tx_entry(struct ieee80211_hw *dev,
+ __le32 req_id)
+{
+ struct p54_common *priv = dev->priv;
+ struct sk_buff *entry = priv->tx_queue.next;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->tx_queue.lock, flags);
+ while (entry != (struct sk_buff *)&priv->tx_queue) {
+ struct p54_hdr *hdr = (struct p54_hdr *) entry->data;
+
+ if (hdr->req_id == req_id) {
+ spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
+ return entry;
+ }
+ entry = entry->next;
+ }
+ spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
+ return NULL;
+}
+
static void p54_rx_frame_sent(struct ieee80211_hw *dev, struct sk_buff *skb)
{
struct p54_common *priv = dev->priv;
@@ -696,6 +756,7 @@ static void p54_rx_frame_sent(struct ieee80211_hw *dev, struct sk_buff *skb)
entry_hdr = (struct p54_hdr *) entry->data;
entry_data = (struct p54_tx_data *) entry_hdr->data;
priv->tx_stats[entry_data->hw_queue].len--;
+ priv->stats.dot11ACKFailureCount += payload->tries - 1;
if (unlikely(entry == priv->cached_beacon)) {
kfree_skb(entry);
@@ -775,8 +836,12 @@ static void p54_rx_stats(struct ieee80211_hw *dev, struct sk_buff *skb)
struct p54_common *priv = dev->priv;
struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
struct p54_statistics *stats = (struct p54_statistics *) hdr->data;
- u32 tsf32 = le32_to_cpu(stats->tsf32);
+ u32 tsf32;
+ if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED))
+ return ;
+
+ tsf32 = le32_to_cpu(stats->tsf32);
if (tsf32 < priv->tsf_low32)
priv->tsf_high32++;
priv->tsf_low32 = tsf32;
@@ -786,9 +851,8 @@ static void p54_rx_stats(struct ieee80211_hw *dev, struct sk_buff *skb)
priv->stats.dot11FCSErrorCount = le32_to_cpu(stats->rx_bad_fcs);
priv->noise = p54_rssi_to_dbm(dev, le32_to_cpu(stats->noise));
- complete(&priv->stats_comp);
- mod_timer(&priv->stats_timer, jiffies + 5 * HZ);
+ p54_free_skb(dev, p54_find_tx_entry(dev, hdr->req_id));
}
static void p54_rx_trap(struct ieee80211_hw *dev, struct sk_buff *skb)
@@ -897,6 +961,8 @@ static int p54_assign_address(struct ieee80211_hw *dev, struct sk_buff *skb,
* have a few spare slots for control frames left.
*/
ieee80211_stop_queues(dev);
+ queue_delayed_work(dev->workqueue, &priv->work,
+ msecs_to_jiffies(P54_TX_TIMEOUT));
if (unlikely(left == 32)) {
/*
@@ -1022,7 +1088,7 @@ int p54_read_eeprom(struct ieee80211_hw *dev)
eeprom_hdr->v2.magic2 = 0xf;
memcpy(eeprom_hdr->v2.magic, (const char *)"LOCK", 4);
}
- priv->tx(dev, skb, 0);
+ priv->tx(dev, skb);
if (!wait_for_completion_interruptible_timeout(&priv->eeprom_comp, HZ)) {
printk(KERN_ERR "%s: device does not respond!\n",
@@ -1063,7 +1129,7 @@ static int p54_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
tim = (struct p54_tim *) skb_put(skb, sizeof(*tim));
tim->count = 1;
tim->entry[0] = cpu_to_le16(set ? (sta->aid | 0x8000) : sta->aid);
- priv->tx(dev, skb, 1);
+ priv->tx(dev, skb);
return 0;
}
@@ -1081,7 +1147,7 @@ static int p54_sta_unlock(struct ieee80211_hw *dev, u8 *addr)
sta = (struct p54_sta_unlock *)skb_put(skb, sizeof(*sta));
memcpy(sta->addr, addr, ETH_ALEN);
- priv->tx(dev, skb, 1);
+ priv->tx(dev, skb);
return 0;
}
@@ -1124,7 +1190,7 @@ static int p54_tx_cancel(struct ieee80211_hw *dev, struct sk_buff *entry)
hdr = (void *)entry->data;
cancel = (struct p54_txcancel *)skb_put(skb, sizeof(*cancel));
cancel->req_id = hdr->req_id;
- priv->tx(dev, skb, 1);
+ priv->tx(dev, skb);
return 0;
}
@@ -1353,7 +1419,11 @@ static int p54_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
/* modifies skb->cb and with it info, so must be last! */
if (unlikely(p54_assign_address(dev, skb, hdr, skb->len + tim_len)))
goto err;
- priv->tx(dev, skb, 0);
+ priv->tx(dev, skb);
+
+ queue_delayed_work(dev->workqueue, &priv->work,
+ msecs_to_jiffies(P54_TX_FRAME_LIFETIME));
+
return 0;
err:
@@ -1428,19 +1498,19 @@ static int p54_setup_mac(struct ieee80211_hw *dev)
setup->v2.lpf_bandwidth = cpu_to_le16(65535);
setup->v2.osc_start_delay = cpu_to_le16(65535);
}
- priv->tx(dev, skb, 1);
+ priv->tx(dev, skb);
return 0;
}
-static int p54_scan(struct ieee80211_hw *dev, u16 mode, u16 dwell,
- u16 frequency)
+static int p54_scan(struct ieee80211_hw *dev, u16 mode, u16 dwell)
{
struct p54_common *priv = dev->priv;
struct sk_buff *skb;
struct p54_scan *chan;
unsigned int i;
void *entry;
- __le16 freq = cpu_to_le16(frequency);
+ __le16 freq = cpu_to_le16(dev->conf.channel->center_freq);
+ int band = dev->conf.channel->band;
skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*chan) +
sizeof(struct p54_hdr), P54_CONTROL_TYPE_SCAN,
@@ -1501,15 +1571,15 @@ static int p54_scan(struct ieee80211_hw *dev, u16 mode, u16 dwell,
}
if (priv->fw_var < 0x500) {
- chan->v1.rssical_mul = cpu_to_le16(130);
- chan->v1.rssical_add = cpu_to_le16(0xfe70);
+ chan->v1_rssi.mul = cpu_to_le16(priv->rssical_db[band].mul);
+ chan->v1_rssi.add = cpu_to_le16(priv->rssical_db[band].add);
} else {
- chan->v2.rssical_mul = cpu_to_le16(130);
- chan->v2.rssical_add = cpu_to_le16(0xfe70);
+ chan->v2.rssi.mul = cpu_to_le16(priv->rssical_db[band].mul);
+ chan->v2.rssi.add = cpu_to_le16(priv->rssical_db[band].add);
chan->v2.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask);
memset(chan->v2.rts_rates, 0, 8);
}
- priv->tx(dev, skb, 1);
+ priv->tx(dev, skb);
return 0;
err:
@@ -1535,7 +1605,7 @@ static int p54_set_leds(struct ieee80211_hw *dev, int mode, int link, int act)
led->led_permanent = cpu_to_le16(link);
led->led_temporary = cpu_to_le16(act);
led->duration = cpu_to_le16(1000);
- priv->tx(dev, skb, 1);
+ priv->tx(dev, skb);
return 0;
}
@@ -1575,21 +1645,7 @@ static int p54_set_edcf(struct ieee80211_hw *dev)
edcf->flags = 0;
memset(edcf->mapping, 0, sizeof(edcf->mapping));
memcpy(edcf->queue, priv->qos_params, sizeof(edcf->queue));
- priv->tx(dev, skb, 1);
- return 0;
-}
-
-static int p54_init_stats(struct ieee80211_hw *dev)
-{
- struct p54_common *priv = dev->priv;
-
- priv->cached_stats = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL,
- sizeof(struct p54_hdr) + sizeof(struct p54_statistics),
- P54_CONTROL_TYPE_STAT_READBACK, GFP_KERNEL);
- if (!priv->cached_stats)
- return -ENOMEM;
-
- mod_timer(&priv->stats_timer, jiffies + HZ);
+ priv->tx(dev, skb);
return 0;
}
@@ -1686,9 +1742,6 @@ static int p54_start(struct ieee80211_hw *dev)
err = p54_set_edcf(dev);
if (err)
goto out;
- err = p54_init_stats(dev);
- if (err)
- goto out;
memset(priv->bssid, ~0, ETH_ALEN);
priv->mode = NL80211_IFTYPE_MONITOR;
@@ -1698,6 +1751,8 @@ static int p54_start(struct ieee80211_hw *dev)
goto out;
}
+ queue_delayed_work(dev->workqueue, &priv->work, 0);
+
out:
mutex_unlock(&priv->conf_mutex);
return err;
@@ -1710,9 +1765,7 @@ static void p54_stop(struct ieee80211_hw *dev)
mutex_lock(&priv->conf_mutex);
priv->mode = NL80211_IFTYPE_UNSPECIFIED;
- del_timer(&priv->stats_timer);
- p54_free_skb(dev, priv->cached_stats);
- priv->cached_stats = NULL;
+ cancel_delayed_work_sync(&priv->work);
if (priv->cached_beacon)
p54_tx_cancel(dev, priv->cached_beacon);
@@ -1784,8 +1837,7 @@ static int p54_config(struct ieee80211_hw *dev, u32 changed)
goto out;
}
if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
- ret = p54_scan(dev, P54_SCAN_EXIT, 0,
- conf->channel->center_freq);
+ ret = p54_scan(dev, P54_SCAN_EXIT, 0);
if (ret)
goto out;
}
@@ -1811,8 +1863,7 @@ static int p54_config_interface(struct ieee80211_hw *dev,
}
if (conf->changed & IEEE80211_IFCC_BEACON) {
- ret = p54_scan(dev, P54_SCAN_EXIT, 0,
- dev->conf.channel->center_freq);
+ ret = p54_scan(dev, P54_SCAN_EXIT, 0);
if (ret)
goto out;
ret = p54_setup_mac(dev);
@@ -1885,18 +1936,33 @@ static int p54_init_xbow_synth(struct ieee80211_hw *dev)
xbow->magic2 = cpu_to_le16(0x2);
xbow->freq = cpu_to_le16(5390);
memset(xbow->padding, 0, sizeof(xbow->padding));
- priv->tx(dev, skb, 1);
+ priv->tx(dev, skb);
return 0;
}
-static void p54_statistics_timer(unsigned long data)
+static void p54_work(struct work_struct *work)
{
- struct ieee80211_hw *dev = (struct ieee80211_hw *) data;
- struct p54_common *priv = dev->priv;
+ struct p54_common *priv = container_of(work, struct p54_common,
+ work.work);
+ struct ieee80211_hw *dev = priv->hw;
+ struct sk_buff *skb;
- BUG_ON(!priv->cached_stats);
+ if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED))
+ return ;
+
+ /*
+ * TODO: walk through tx_queue and do the following tasks
+ * 1. initiate bursts.
+ * 2. cancel stuck frames / reset the device if necessary.
+ */
- priv->tx(dev, priv->cached_stats, 0);
+ skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL, sizeof(struct p54_hdr) +
+ sizeof(struct p54_statistics),
+ P54_CONTROL_TYPE_STAT_READBACK, GFP_KERNEL);
+ if (!skb)
+ return ;
+
+ priv->tx(dev, skb);
}
static int p54_get_stats(struct ieee80211_hw *dev,
@@ -1904,17 +1970,7 @@ static int p54_get_stats(struct ieee80211_hw *dev,
{
struct p54_common *priv = dev->priv;
- del_timer(&priv->stats_timer);
- p54_statistics_timer((unsigned long)dev);
-
- if (!wait_for_completion_interruptible_timeout(&priv->stats_comp, HZ)) {
- printk(KERN_ERR "%s: device does not respond!\n",
- wiphy_name(dev->wiphy));
- return -EBUSY;
- }
-
memcpy(stats, &priv->stats, sizeof(*stats));
-
return 0;
}
@@ -1946,8 +2002,7 @@ static void p54_bss_info_changed(struct ieee80211_hw *dev,
priv->basic_rate_mask = info->basic_rates;
p54_setup_mac(dev);
if (priv->fw_var >= 0x500)
- p54_scan(dev, P54_SCAN_EXIT, 0,
- dev->conf.channel->center_freq);
+ p54_scan(dev, P54_SCAN_EXIT, 0);
}
if (changed & BSS_CHANGED_ASSOC) {
if (info->assoc) {
@@ -2039,7 +2094,7 @@ static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY]), 8);
}
- priv->tx(dev, skb, 1);
+ priv->tx(dev, skb);
mutex_unlock(&priv->conf_mutex);
return 0;
}
@@ -2072,6 +2127,7 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len)
return NULL;
priv = dev->priv;
+ priv->hw = dev;
priv->mode = NL80211_IFTYPE_UNSPECIFIED;
priv->basic_rate_mask = 0x15f;
skb_queue_head_init(&priv->tx_queue);
@@ -2107,9 +2163,7 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len)
mutex_init(&priv->conf_mutex);
init_completion(&priv->eeprom_comp);
- init_completion(&priv->stats_comp);
- setup_timer(&priv->stats_timer, p54_statistics_timer,
- (unsigned long)dev);
+ INIT_DELAYED_WORK(&priv->work, p54_work);
return dev;
}
@@ -2118,8 +2172,6 @@ EXPORT_SYMBOL_GPL(p54_init_common);
void p54_free_common(struct ieee80211_hw *dev)
{
struct p54_common *priv = dev->priv;
- del_timer(&priv->stats_timer);
- kfree_skb(priv->cached_stats);
kfree(priv->iq_autocal);
kfree(priv->output_limit);
kfree(priv->curve_data);
diff --git a/drivers/net/wireless/p54/p54common.h b/drivers/net/wireless/p54/p54common.h
index 5a68fdae773..f5729de83fe 100644
--- a/drivers/net/wireless/p54/p54common.h
+++ b/drivers/net/wireless/p54/p54common.h
@@ -84,9 +84,6 @@ struct bootrec_desc {
#define BR_CODE_END_OF_BRA 0xFF0000FF
#define LEGACY_BR_CODE_END_OF_BRA 0xFFFFFFFF
-#define P54_HDR_FLAG_CONTROL BIT(15)
-#define P54_HDR_FLAG_CONTROL_OPSET (BIT(15) + BIT(0))
-
#define P54_HDR_FLAG_DATA_ALIGN BIT(14)
#define P54_HDR_FLAG_DATA_OUT_PROMISC BIT(0)
#define P54_HDR_FLAG_DATA_OUT_TIMESTAMP BIT(1)
@@ -178,6 +175,11 @@ struct pda_pa_curve_data {
u8 data[0];
} __attribute__ ((packed));
+struct pda_rssi_cal_entry {
+ __le16 mul;
+ __le16 add;
+} __attribute__ ((packed));
+
/*
* this defines the PDR codes used to build PDAs as defined in document
* number 553155. The current implementation mirrors version 1.1 of the
@@ -355,6 +357,11 @@ struct p54_tx_data {
u8 align[0];
} __attribute__ ((packed));
+/* unit is ms */
+#define P54_TX_FRAME_LIFETIME 2000
+#define P54_TX_TIMEOUT 4000
+#define P54_STATISTICS_UPDATE 5000
+
#define P54_FILTER_TYPE_NONE 0
#define P54_FILTER_TYPE_STATION BIT(0)
#define P54_FILTER_TYPE_IBSS BIT(1)
@@ -424,22 +431,18 @@ struct p54_scan {
u8 dup_16qam;
u8 dup_64qam;
union {
- struct {
- __le16 rssical_mul;
- __le16 rssical_add;
- } v1 __attribute__ ((packed));
+ struct pda_rssi_cal_entry v1_rssi;
struct {
__le32 basic_rate_mask;
u8 rts_rates[8];
- __le16 rssical_mul;
- __le16 rssical_add;
+ struct pda_rssi_cal_entry rssi;
} v2 __attribute__ ((packed));
} __attribute__ ((packed));
} __attribute__ ((packed));
-#define P54_SCAN_V1_LEN (sizeof(struct p54_scan)-12)
-#define P54_SCAN_V2_LEN (sizeof(struct p54_scan))
+#define P54_SCAN_V1_LEN 0x70
+#define P54_SCAN_V2_LEN 0x7c
struct p54_led {
__le16 mode;
diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/p54/p54pci.c
index c28220e401b..aa367a0ddc4 100644
--- a/drivers/net/wireless/p54/p54pci.c
+++ b/drivers/net/wireless/p54/p54pci.c
@@ -227,7 +227,9 @@ static void p54p_check_tx_ring(struct ieee80211_hw *dev, u32 *index,
while (i != idx) {
desc = &ring[i];
- p54_free_skb(dev, tx_buf[i]);
+ if (tx_buf[i])
+ if (FREE_AFTER_TX((struct sk_buff *) tx_buf[i]))
+ p54_free_skb(dev, tx_buf[i]);
tx_buf[i] = NULL;
pci_unmap_single(priv->pdev, le32_to_cpu(desc->host_addr),
@@ -298,8 +300,7 @@ static irqreturn_t p54p_interrupt(int irq, void *dev_id)
return reg ? IRQ_HANDLED : IRQ_NONE;
}
-static void p54p_tx(struct ieee80211_hw *dev, struct sk_buff *skb,
- int free_on_tx)
+static void p54p_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
{
struct p54p_priv *priv = dev->priv;
struct p54p_ring_control *ring_control = priv->ring_control;
@@ -314,6 +315,7 @@ static void p54p_tx(struct ieee80211_hw *dev, struct sk_buff *skb,
idx = le32_to_cpu(ring_control->host_idx[1]);
i = idx % ARRAY_SIZE(ring_control->tx_data);
+ priv->tx_buf_data[i] = skb;
mapping = pci_map_single(priv->pdev, skb->data, skb->len,
PCI_DMA_TODEVICE);
desc = &ring_control->tx_data[i];
@@ -324,10 +326,6 @@ static void p54p_tx(struct ieee80211_hw *dev, struct sk_buff *skb,
wmb();
ring_control->host_idx[1] = cpu_to_le32(idx + 1);
-
- if (free_on_tx)
- priv->tx_buf_data[i] = skb;
-
spin_unlock_irqrestore(&priv->lock, flags);
P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE));
diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c
index c2789e53b98..c44a200059d 100644
--- a/drivers/net/wireless/p54/p54usb.c
+++ b/drivers/net/wireless/p54/p54usb.c
@@ -138,22 +138,16 @@ static void p54u_rx_cb(struct urb *urb)
}
}
-static void p54u_tx_reuse_skb_cb(struct urb *urb)
-{
- struct sk_buff *skb = urb->context;
- struct p54u_priv *priv = (struct p54u_priv *)((struct ieee80211_hw *)
- usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)))->priv;
-
- skb_pull(skb, priv->common.tx_hdr_len);
-}
-
-static void p54u_tx_free_skb_cb(struct urb *urb)
+static void p54u_tx_cb(struct urb *urb)
{
struct sk_buff *skb = urb->context;
struct ieee80211_hw *dev = (struct ieee80211_hw *)
usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
+ struct p54u_priv *priv = dev->priv;
- p54_free_skb(dev, skb);
+ skb_pull(skb, priv->common.tx_hdr_len);
+ if (FREE_AFTER_TX(skb))
+ p54_free_skb(dev, skb);
}
static void p54u_tx_dummy_cb(struct urb *urb) { }
@@ -213,8 +207,7 @@ static int p54u_init_urbs(struct ieee80211_hw *dev)
return ret;
}
-static void p54u_tx_3887(struct ieee80211_hw *dev, struct sk_buff *skb,
- int free_on_tx)
+static void p54u_tx_3887(struct ieee80211_hw *dev, struct sk_buff *skb)
{
struct p54u_priv *priv = dev->priv;
struct urb *addr_urb, *data_urb;
@@ -236,9 +229,7 @@ static void p54u_tx_3887(struct ieee80211_hw *dev, struct sk_buff *skb,
p54u_tx_dummy_cb, dev);
usb_fill_bulk_urb(data_urb, priv->udev,
usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
- skb->data, skb->len,
- free_on_tx ? p54u_tx_free_skb_cb :
- p54u_tx_reuse_skb_cb, skb);
+ skb->data, skb->len, p54u_tx_cb, skb);
usb_anchor_urb(addr_urb, &priv->submitted);
err = usb_submit_urb(addr_urb, GFP_ATOMIC);
@@ -273,8 +264,7 @@ static __le32 p54u_lm87_chksum(const __le32 *data, size_t length)
return cpu_to_le32(chk);
}
-static void p54u_tx_lm87(struct ieee80211_hw *dev, struct sk_buff *skb,
- int free_on_tx)
+static void p54u_tx_lm87(struct ieee80211_hw *dev, struct sk_buff *skb)
{
struct p54u_priv *priv = dev->priv;
struct urb *data_urb;
@@ -293,9 +283,7 @@ static void p54u_tx_lm87(struct ieee80211_hw *dev, struct sk_buff *skb,
usb_fill_bulk_urb(data_urb, priv->udev,
usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
- skb->data, skb->len,
- free_on_tx ? p54u_tx_free_skb_cb :
- p54u_tx_reuse_skb_cb, skb);
+ skb->data, skb->len, p54u_tx_cb, skb);
usb_anchor_urb(data_urb, &priv->submitted);
if (usb_submit_urb(data_urb, GFP_ATOMIC)) {
@@ -306,14 +294,15 @@ static void p54u_tx_lm87(struct ieee80211_hw *dev, struct sk_buff *skb,
usb_free_urb(data_urb);
}
-static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb,
- int free_on_tx)
+static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb)
{
struct p54u_priv *priv = dev->priv;
struct urb *int_urb, *data_urb;
struct net2280_tx_hdr *hdr;
struct net2280_reg_write *reg;
int err = 0;
+ __le32 addr = ((struct p54_hdr *) skb->data)->req_id;
+ __le16 len = cpu_to_le16(skb->len);
reg = kmalloc(sizeof(*reg), GFP_ATOMIC);
if (!reg)
@@ -338,8 +327,8 @@ static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb,
hdr = (void *)skb_push(skb, sizeof(*hdr));
memset(hdr, 0, sizeof(*hdr));
- hdr->device_addr = ((struct p54_hdr *)skb->data)->req_id;
- hdr->len = cpu_to_le16(skb->len + sizeof(struct p54_hdr));
+ hdr->len = len;
+ hdr->device_addr = addr;
usb_fill_bulk_urb(int_urb, priv->udev,
usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV), reg, sizeof(*reg),
@@ -354,9 +343,7 @@ static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb,
usb_fill_bulk_urb(data_urb, priv->udev,
usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
- skb->data, skb->len,
- free_on_tx ? p54u_tx_free_skb_cb :
- p54u_tx_reuse_skb_cb, skb);
+ skb->data, skb->len, p54u_tx_cb, skb);
usb_anchor_urb(int_urb, &priv->submitted);
err = usb_submit_urb(int_urb, GFP_ATOMIC);
diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c
index 74f5449b792..00ce3ef39ab 100644
--- a/drivers/net/wireless/rtl818x/rtl8187_dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c
@@ -213,7 +213,7 @@ static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb) {
kfree_skb(skb);
- return 0;
+ return -ENOMEM;
}
flags = skb->len;
@@ -281,7 +281,7 @@ static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
}
usb_free_urb(urb);
- return 0;
+ return rc;
}
static void rtl8187_rx_cb(struct urb *urb)
@@ -294,15 +294,16 @@ static void rtl8187_rx_cb(struct urb *urb)
int rate, signal;
u32 flags;
u32 quality;
+ unsigned long f;
- spin_lock(&priv->rx_queue.lock);
+ spin_lock_irqsave(&priv->rx_queue.lock, f);
if (skb->next)
__skb_unlink(skb, &priv->rx_queue);
else {
- spin_unlock(&priv->rx_queue.lock);
+ spin_unlock_irqrestore(&priv->rx_queue.lock, f);
return;
}
- spin_unlock(&priv->rx_queue.lock);
+ spin_unlock_irqrestore(&priv->rx_queue.lock, f);
skb_put(skb, urb->actual_length);
if (unlikely(urb->status)) {
@@ -942,7 +943,6 @@ static int rtl8187_start(struct ieee80211_hw *dev)
static void rtl8187_stop(struct ieee80211_hw *dev)
{
struct rtl8187_priv *priv = dev->priv;
- struct rtl8187_rx_info *info;
struct sk_buff *skb;
u32 reg;
@@ -961,10 +961,6 @@ static void rtl8187_stop(struct ieee80211_hw *dev)
rtl818x_iowrite8(priv, &priv->map->CONFIG4, reg | RTL818X_CONFIG4_VCOOFF);
rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
- while ((skb = skb_dequeue(&priv->rx_queue))) {
- info = (struct rtl8187_rx_info *)skb->cb;
- kfree_skb(skb);
- }
while ((skb = skb_dequeue(&priv->b_tx_status.queue)))
dev_kfree_skb_any(skb);
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 04d4516f9c7..e86ed59f9ad 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -201,13 +201,13 @@ enum nl80211_commands {
* @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming)
* @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters
* @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz
- * @NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET: included with NL80211_ATTR_WIPHY_FREQ
+ * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ
* if HT20 or HT40 are allowed (i.e., 802.11n disabled if not included):
- * NL80211_SEC_CHAN_NO_HT = HT not allowed (i.e., same as not including
+ * NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including
* this attribute)
- * NL80211_SEC_CHAN_DISABLED = HT20 only
- * NL80211_SEC_CHAN_BELOW = secondary channel is below the primary channel
- * NL80211_SEC_CHAN_ABOVE = secondary channel is above the primary channel
+ * NL80211_CHAN_HT20 = HT20 only
+ * NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel
+ * NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel
*
* @NL80211_ATTR_IFINDEX: network interface index of the device to operate on
* @NL80211_ATTR_IFNAME: network interface name
@@ -344,7 +344,7 @@ enum nl80211_attrs {
NL80211_ATTR_WIPHY_TXQ_PARAMS,
NL80211_ATTR_WIPHY_FREQ,
- NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET,
+ NL80211_ATTR_WIPHY_CHANNEL_TYPE,
/* add attributes here, update the policy in nl80211.c */
@@ -425,6 +425,32 @@ enum nl80211_sta_flags {
};
/**
+ * enum nl80211_rate_info - bitrate information
+ *
+ * These attribute types are used with %NL80211_STA_INFO_TXRATE
+ * when getting information about the bitrate of a station.
+ *
+ * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s)
+ * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8)
+ * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 Mhz dualchannel bitrate
+ * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval
+ * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined
+ * @__NL80211_RATE_INFO_AFTER_LAST: internal use
+ */
+enum nl80211_rate_info {
+ __NL80211_RATE_INFO_INVALID,
+ NL80211_RATE_INFO_BITRATE,
+ NL80211_RATE_INFO_MCS,
+ NL80211_RATE_INFO_40_MHZ_WIDTH,
+ NL80211_RATE_INFO_SHORT_GI,
+
+ /* keep last */
+ __NL80211_RATE_INFO_AFTER_LAST,
+ NL80211_RATE_INFO_MAX = __NL80211_RATE_INFO_AFTER_LAST - 1
+};
+
+/**
* enum nl80211_sta_info - station information
*
* These attribute types are used with %NL80211_ATTR_STA_INFO
@@ -436,6 +462,9 @@ enum nl80211_sta_flags {
* @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station)
* @__NL80211_STA_INFO_AFTER_LAST: internal
* @NL80211_STA_INFO_MAX: highest possible station info attribute
+ * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm)
+ * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute
+ * containing info as possible, see &enum nl80211_sta_info_txrate.
*/
enum nl80211_sta_info {
__NL80211_STA_INFO_INVALID,
@@ -445,6 +474,8 @@ enum nl80211_sta_info {
NL80211_STA_INFO_LLID,
NL80211_STA_INFO_PLID,
NL80211_STA_INFO_PLINK_STATE,
+ NL80211_STA_INFO_SIGNAL,
+ NL80211_STA_INFO_TX_BITRATE,
/* keep last */
__NL80211_STA_INFO_AFTER_LAST,
@@ -774,10 +805,10 @@ enum nl80211_txq_q {
NL80211_TXQ_Q_BK
};
-enum nl80211_sec_chan_offset {
- NL80211_SEC_CHAN_NO_HT /* No HT */,
- NL80211_SEC_CHAN_DISABLED /* HT20 only */,
- NL80211_SEC_CHAN_BELOW /* HT40- */,
- NL80211_SEC_CHAN_ABOVE /* HT40+ */
+enum nl80211_channel_type {
+ NL80211_CHAN_NO_HT,
+ NL80211_CHAN_HT20,
+ NL80211_CHAN_HT40MINUS,
+ NL80211_CHAN_HT40PLUS
};
#endif /* __LINUX_NL80211_H */
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index a0c0bf19496..23c0ab74ded 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -169,6 +169,9 @@ struct station_parameters {
* @STATION_INFO_LLID: @llid filled
* @STATION_INFO_PLID: @plid filled
* @STATION_INFO_PLINK_STATE: @plink_state filled
+ * @STATION_INFO_SIGNAL: @signal filled
+ * @STATION_INFO_TX_BITRATE: @tx_bitrate fields are filled
+ * (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs)
*/
enum station_info_flags {
STATION_INFO_INACTIVE_TIME = 1<<0,
@@ -177,6 +180,39 @@ enum station_info_flags {
STATION_INFO_LLID = 1<<3,
STATION_INFO_PLID = 1<<4,
STATION_INFO_PLINK_STATE = 1<<5,
+ STATION_INFO_SIGNAL = 1<<6,
+ STATION_INFO_TX_BITRATE = 1<<7,
+};
+
+/**
+ * enum station_info_rate_flags - bitrate info flags
+ *
+ * Used by the driver to indicate the specific rate transmission
+ * type for 802.11n transmissions.
+ *
+ * @RATE_INFO_FLAGS_MCS: @tx_bitrate_mcs filled
+ * @RATE_INFO_FLAGS_40_MHZ_WIDTH: 40 Mhz width transmission
+ * @RATE_INFO_FLAGS_SHORT_GI: 400ns guard interval
+ */
+enum rate_info_flags {
+ RATE_INFO_FLAGS_MCS = 1<<0,
+ RATE_INFO_FLAGS_40_MHZ_WIDTH = 1<<1,
+ RATE_INFO_FLAGS_SHORT_GI = 1<<2,
+};
+
+/**
+ * struct rate_info - bitrate information
+ *
+ * Information about a receiving or transmitting bitrate
+ *
+ * @flags: bitflag of flags from &enum rate_info_flags
+ * @mcs: mcs index if struct describes a 802.11n bitrate
+ * @legacy: bitrate in 100kbit/s for 802.11abg
+ */
+struct rate_info {
+ u8 flags;
+ u8 mcs;
+ u16 legacy;
};
/**
@@ -191,6 +227,8 @@ enum station_info_flags {
* @llid: mesh local link id
* @plid: mesh peer link id
* @plink_state: mesh peer link state
+ * @signal: signal strength of last received packet in dBm
+ * @txrate: current unicast bitrate to this station
*/
struct station_info {
u32 filled;
@@ -200,6 +238,8 @@ struct station_info {
u16 llid;
u16 plid;
u8 plink_state;
+ s8 signal;
+ struct rate_info txrate;
};
/**
@@ -523,7 +563,7 @@ struct cfg80211_ops {
int (*set_channel)(struct wiphy *wiphy,
struct ieee80211_channel *chan,
- enum nl80211_sec_chan_offset);
+ enum nl80211_channel_type channel_type);
};
/* temporary wext handlers */
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 046ce692a90..b3bd00a9d99 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -165,14 +165,9 @@ enum ieee80211_bss_change {
/**
* struct ieee80211_bss_ht_conf - BSS's changing HT configuration
- * @secondary_channel_offset: secondary channel offset, uses
- * %IEEE80211_HT_PARAM_CHA_SEC_ values
- * @width_40_ok: indicates that 40 MHz bandwidth may be used for TX
* @operation_mode: HT operation mode (like in &struct ieee80211_ht_info)
*/
struct ieee80211_bss_ht_conf {
- u8 secondary_channel_offset;
- bool width_40_ok;
u16 operation_mode;
};
@@ -441,6 +436,9 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
* is valid. This is useful in monitor mode and necessary for beacon frames
* to enable IBSS merging.
* @RX_FLAG_SHORTPRE: Short preamble was used for this frame
+ * @RX_FLAG_HT: HT MCS was used and rate_idx is MCS index
+ * @RX_FLAG_40MHZ: HT40 (40 MHz) was used
+ * @RX_FLAG_SHORT_GI: Short guard interval was used
*/
enum mac80211_rx_flags {
RX_FLAG_MMIC_ERROR = 1<<0,
@@ -451,7 +449,10 @@ enum mac80211_rx_flags {
RX_FLAG_FAILED_FCS_CRC = 1<<5,
RX_FLAG_FAILED_PLCP_CRC = 1<<6,
RX_FLAG_TSFT = 1<<7,
- RX_FLAG_SHORTPRE = 1<<8
+ RX_FLAG_SHORTPRE = 1<<8,
+ RX_FLAG_HT = 1<<9,
+ RX_FLAG_40MHZ = 1<<10,
+ RX_FLAG_SHORT_GI = 1<<11,
};
/**
@@ -471,7 +472,8 @@ enum mac80211_rx_flags {
* @noise: noise when receiving this frame, in dBm.
* @qual: overall signal quality indication, in percent (0-100).
* @antenna: antenna used
- * @rate_idx: index of data rate into band's supported rates
+ * @rate_idx: index of data rate into band's supported rates or MCS index if
+ * HT rates are use (RX_FLAG_HT)
* @flag: %RX_FLAG_*
*/
struct ieee80211_rx_status {
@@ -508,9 +510,7 @@ static inline int __deprecated __IEEE80211_CONF_SHORT_SLOT_TIME(void)
struct ieee80211_ht_conf {
bool enabled;
- int sec_chan_offset; /* 0 = HT40 disabled; -1 = HT40 enabled, secondary
- * channel below primary; 1 = HT40 enabled,
- * secondary channel above primary */
+ enum nl80211_channel_type channel_type;
};
/**
@@ -854,6 +854,11 @@ enum ieee80211_tkip_key_type {
*
* @IEEE80211_HW_AMPDU_AGGREGATION:
* Hardware supports 11n A-MPDU aggregation.
+ *
+ * @IEEE80211_HW_NO_STACK_DYNAMIC_PS:
+ * Hardware which has dynamic power save support, meaning
+ * that power save is enabled in idle periods, and don't need support
+ * from stack.
*/
enum ieee80211_hw_flags {
IEEE80211_HW_RX_INCLUDES_FCS = 1<<1,
@@ -866,6 +871,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_NOISE_DBM = 1<<8,
IEEE80211_HW_SPECTRUM_MGMT = 1<<9,
IEEE80211_HW_AMPDU_AGGREGATION = 1<<10,
+ IEEE80211_HW_NO_STACK_DYNAMIC_PS = 1<<11,
};
/**
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 7912eb14eca..9d4e4d846ec 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -310,12 +310,35 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
sinfo->filled = STATION_INFO_INACTIVE_TIME |
STATION_INFO_RX_BYTES |
- STATION_INFO_TX_BYTES;
+ STATION_INFO_TX_BYTES |
+ STATION_INFO_TX_BITRATE;
sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
sinfo->rx_bytes = sta->rx_bytes;
sinfo->tx_bytes = sta->tx_bytes;
+ if (sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) {
+ sinfo->filled |= STATION_INFO_SIGNAL;
+ sinfo->signal = (s8)sta->last_signal;
+ }
+
+ sinfo->txrate.flags = 0;
+ if (sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)
+ sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
+ if (sta->last_tx_rate.flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+ sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+ if (sta->last_tx_rate.flags & IEEE80211_TX_RC_SHORT_GI)
+ sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+
+ if (!(sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)) {
+ struct ieee80211_supported_band *sband;
+ sband = sta->local->hw.wiphy->bands[
+ sta->local->hw.conf.channel->band];
+ sinfo->txrate.legacy =
+ sband->bitrates[sta->last_tx_rate.idx].bitrate;
+ } else
+ sinfo->txrate.mcs = sta->last_tx_rate.idx;
+
if (ieee80211_vif_is_mesh(&sdata->vif)) {
#ifdef CONFIG_MAC80211_MESH
sinfo->filled |= STATION_INFO_LLID |
@@ -663,6 +686,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
struct sta_info *sta;
struct ieee80211_sub_if_data *sdata;
int err;
+ int layer2_update;
/* Prevent a race with changing the rate control algorithm */
if (!netif_running(dev))
@@ -693,17 +717,25 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
rate_control_rate_init(sta);
+ layer2_update = sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
+ sdata->vif.type == NL80211_IFTYPE_AP;
+
rcu_read_lock();
err = sta_info_insert(sta);
if (err) {
/* STA has been freed */
+ if (err == -EEXIST && layer2_update) {
+ /* Need to update layer 2 devices on reassociation */
+ sta = sta_info_get(local, mac);
+ if (sta)
+ ieee80211_send_layer2_update(sta);
+ }
rcu_read_unlock();
return err;
}
- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
- sdata->vif.type == NL80211_IFTYPE_AP)
+ if (layer2_update)
ieee80211_send_layer2_update(sta);
rcu_read_unlock();
@@ -1099,12 +1131,12 @@ static int ieee80211_set_txq_params(struct wiphy *wiphy,
static int ieee80211_set_channel(struct wiphy *wiphy,
struct ieee80211_channel *chan,
- enum nl80211_sec_chan_offset sec_chan_offset)
+ enum nl80211_channel_type channel_type)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
local->oper_channel = chan;
- local->oper_sec_chan_offset = sec_chan_offset;
+ local->oper_channel_type = channel_type;
return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
}
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index a1eed7032c9..5f510a13b9f 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -98,6 +98,7 @@ u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
struct ieee80211_bss_ht_conf ht;
u32 changed = 0;
bool enable_ht = true, ht_changed;
+ enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
@@ -112,24 +113,36 @@ u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
ieee80211_channel_to_frequency(hti->control_chan))
enable_ht = false;
- /*
- * XXX: This is totally incorrect when there are multiple virtual
- * interfaces, needs to be fixed later.
- */
- ht_changed = local->hw.conf.ht.enabled != enable_ht;
+ if (enable_ht) {
+ channel_type = NL80211_CHAN_HT20;
+
+ if (!(ap_ht_cap_flags & IEEE80211_HT_CAP_40MHZ_INTOLERANT) &&
+ (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
+ (hti->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) {
+ switch(hti->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ channel_type = NL80211_CHAN_HT40PLUS;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ channel_type = NL80211_CHAN_HT40MINUS;
+ break;
+ }
+ }
+ }
+
+ ht_changed = local->hw.conf.ht.enabled != enable_ht ||
+ channel_type != local->hw.conf.ht.channel_type;
+
+ local->oper_channel_type = channel_type;
local->hw.conf.ht.enabled = enable_ht;
+
if (ht_changed)
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_HT);
/* disable HT */
if (!enable_ht)
return 0;
- ht.secondary_channel_offset =
- hti->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
- ht.width_40_ok =
- !(ap_ht_cap_flags & IEEE80211_HT_CAP_40MHZ_INTOLERANT) &&
- (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
- (hti->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY);
+
ht.operation_mode = le16_to_cpu(hti->operation_mode);
/* if bss configuration changed store the new one */
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 6f59e11d7b3..f3eec989662 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -538,6 +538,11 @@ enum {
IEEE80211_ADDBA_MSG = 4,
};
+enum queue_stop_reason {
+ IEEE80211_QUEUE_STOP_REASON_DRIVER,
+ IEEE80211_QUEUE_STOP_REASON_PS,
+};
+
/* maximum number of hardware queues we support. */
#define QD_MAX_QUEUES (IEEE80211_MAX_AMPDU_QUEUES + IEEE80211_MAX_QUEUES)
@@ -554,7 +559,8 @@ struct ieee80211_local {
const struct ieee80211_ops *ops;
unsigned long queue_pool[BITS_TO_LONGS(QD_MAX_QUEUES)];
-
+ unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES];
+ spinlock_t queue_stop_reason_lock;
struct net_device *mdev; /* wmaster# - "master" 802.11 device */
int open_count;
int monitors, cooked_mntrs;
@@ -625,7 +631,7 @@ struct ieee80211_local {
struct delayed_work scan_work;
struct ieee80211_sub_if_data *scan_sdata;
struct ieee80211_channel *oper_channel, *scan_channel;
- enum nl80211_sec_chan_offset oper_sec_chan_offset;
+ enum nl80211_channel_type oper_channel_type;
u8 scan_ssid[IEEE80211_MAX_SSID_LEN];
size_t scan_ssid_len;
struct list_head bss_list;
@@ -689,6 +695,12 @@ struct ieee80211_local {
int wifi_wme_noack_test;
unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */
+ bool powersave;
+ int dynamic_ps_timeout;
+ struct work_struct dynamic_ps_enable_work;
+ struct work_struct dynamic_ps_disable_work;
+ struct timer_list dynamic_ps_timer;
+
#ifdef CONFIG_MAC80211_DEBUGFS
struct local_debugfsdentries {
struct dentry *rcdir;
@@ -971,6 +983,15 @@ int ieee80211_set_freq(struct ieee80211_sub_if_data *sdata, int freq);
u64 ieee80211_mandatory_rates(struct ieee80211_local *local,
enum ieee80211_band band);
+void ieee80211_dynamic_ps_enable_work(struct work_struct *work);
+void ieee80211_dynamic_ps_disable_work(struct work_struct *work);
+void ieee80211_dynamic_ps_timer(unsigned long data);
+
+void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
+ enum queue_stop_reason reason);
+void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
+ enum queue_stop_reason reason);
+
#ifdef CONFIG_MAC80211_NOINLINE
#define debug_noinline noinline
#else
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 6d8710327d1..24b14363d6e 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -195,37 +195,30 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
struct ieee80211_channel *chan;
int ret = 0;
int power;
- enum nl80211_sec_chan_offset sec_chan_offset;
+ enum nl80211_channel_type channel_type;
might_sleep();
if (local->sw_scanning) {
chan = local->scan_channel;
- sec_chan_offset = NL80211_SEC_CHAN_NO_HT;
+ channel_type = NL80211_CHAN_NO_HT;
} else {
chan = local->oper_channel;
- sec_chan_offset = local->oper_sec_chan_offset;
+ channel_type = local->oper_channel_type;
}
if (chan != local->hw.conf.channel ||
- sec_chan_offset != local->hw.conf.ht.sec_chan_offset) {
+ channel_type != local->hw.conf.ht.channel_type) {
local->hw.conf.channel = chan;
- switch (sec_chan_offset) {
- case NL80211_SEC_CHAN_NO_HT:
+ local->hw.conf.ht.channel_type = channel_type;
+ switch (channel_type) {
+ case NL80211_CHAN_NO_HT:
local->hw.conf.ht.enabled = false;
- local->hw.conf.ht.sec_chan_offset = 0;
break;
- case NL80211_SEC_CHAN_DISABLED:
+ case NL80211_CHAN_HT20:
+ case NL80211_CHAN_HT40MINUS:
+ case NL80211_CHAN_HT40PLUS:
local->hw.conf.ht.enabled = true;
- local->hw.conf.ht.sec_chan_offset = 0;
- break;
- case NL80211_SEC_CHAN_BELOW:
- local->hw.conf.ht.enabled = true;
- local->hw.conf.ht.sec_chan_offset = -1;
- break;
- case NL80211_SEC_CHAN_ABOVE:
- local->hw.conf.ht.enabled = true;
- local->hw.conf.ht.sec_chan_offset = 1;
break;
}
changed |= IEEE80211_CONF_CHANGE_CHANNEL;
@@ -348,7 +341,8 @@ static void ieee80211_tasklet_handler(unsigned long data)
dev_kfree_skb(skb);
break ;
default:
- WARN_ON(1);
+ WARN(1, "mac80211: Packet is of unknown type %d\n",
+ skb->pkt_type);
dev_kfree_skb(skb);
break;
}
@@ -731,8 +725,17 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
spin_lock_init(&local->key_lock);
+ spin_lock_init(&local->queue_stop_reason_lock);
+
INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work);
+ INIT_WORK(&local->dynamic_ps_enable_work,
+ ieee80211_dynamic_ps_enable_work);
+ INIT_WORK(&local->dynamic_ps_disable_work,
+ ieee80211_dynamic_ps_disable_work);
+ setup_timer(&local->dynamic_ps_timer,
+ ieee80211_dynamic_ps_timer, (unsigned long) local);
+
sta_info_init(local);
tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending,
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 290b0017ef2..5ba721b6a39 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -309,7 +309,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ASSOC_REQ);
mgmt->u.assoc_req.capab_info = cpu_to_le16(capab);
- mgmt->u.reassoc_req.listen_interval =
+ mgmt->u.assoc_req.listen_interval =
cpu_to_le16(local->hw.conf.listen_interval);
}
@@ -744,6 +744,17 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
bss_info_changed |= BSS_CHANGED_BASIC_RATES;
ieee80211_bss_info_change_notify(sdata, bss_info_changed);
+ if (local->powersave) {
+ if (local->dynamic_ps_timeout > 0)
+ mod_timer(&local->dynamic_ps_timer, jiffies +
+ msecs_to_jiffies(local->dynamic_ps_timeout));
+ else {
+ conf->flags |= IEEE80211_CONF_PS;
+ ieee80211_hw_config(local,
+ IEEE80211_CONF_CHANGE_PS);
+ }
+ }
+
netif_tx_start_all_queues(sdata->dev);
netif_carrier_on(sdata->dev);
@@ -812,7 +823,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
- u32 changed = 0;
+ u32 changed = 0, config_changed = 0;
rcu_read_lock();
@@ -858,8 +869,18 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
rcu_read_unlock();
local->hw.conf.ht.enabled = false;
- ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_HT);
+ local->oper_channel_type = NL80211_CHAN_NO_HT;
+ config_changed |= IEEE80211_CONF_CHANGE_HT;
+
+ del_timer_sync(&local->dynamic_ps_timer);
+ cancel_work_sync(&local->dynamic_ps_enable_work);
+ if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+ local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+ config_changed |= IEEE80211_CONF_CHANGE_PS;
+ }
+
+ ieee80211_hw_config(local, config_changed);
ieee80211_bss_info_change_notify(sdata, changed);
rcu_read_lock();
@@ -1612,8 +1633,13 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
* e.g: at 1 MBit that means mactime is 192 usec earlier
* (=24 bytes * 8 usecs/byte) than the beacon timestamp.
*/
- int rate = local->hw.wiphy->bands[band]->
+ int rate;
+ if (rx_status->flag & RX_FLAG_HT) {
+ rate = 65; /* TODO: HT rates */
+ } else {
+ rate = local->hw.wiphy->bands[band]->
bitrates[rx_status->rate_idx].bitrate;
+ }
rx_timestamp = rx_status->mactime + (24 * 8 * 10 / rate);
} else if (local && local->ops && local->ops->get_tsf)
/* second best option: get current TSF */
@@ -2576,3 +2602,39 @@ void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
ieee80211_restart_sta_timer(sdata);
rcu_read_unlock();
}
+
+void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
+{
+ struct ieee80211_local *local =
+ container_of(work, struct ieee80211_local,
+ dynamic_ps_disable_work);
+
+ if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+ local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+ }
+
+ ieee80211_wake_queues_by_reason(&local->hw,
+ IEEE80211_QUEUE_STOP_REASON_PS);
+}
+
+void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
+{
+ struct ieee80211_local *local =
+ container_of(work, struct ieee80211_local,
+ dynamic_ps_enable_work);
+
+ if (local->hw.conf.flags & IEEE80211_CONF_PS)
+ return;
+
+ local->hw.conf.flags |= IEEE80211_CONF_PS;
+
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+}
+
+void ieee80211_dynamic_ps_timer(unsigned long data)
+{
+ struct ieee80211_local *local = (void *) data;
+
+ queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work);
+}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 73cf126cef4..7175ae80c36 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -123,7 +123,6 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
/* radiotap header, set always present flags */
rthdr->it_present =
cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
- (1 << IEEE80211_RADIOTAP_RATE) |
(1 << IEEE80211_RADIOTAP_CHANNEL) |
(1 << IEEE80211_RADIOTAP_ANTENNA) |
(1 << IEEE80211_RADIOTAP_RX_FLAGS));
@@ -149,7 +148,19 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
pos++;
/* IEEE80211_RADIOTAP_RATE */
- *pos = rate->bitrate / 5;
+ if (status->flag & RX_FLAG_HT) {
+ /*
+ * TODO: add following information into radiotap header once
+ * suitable fields are defined for it:
+ * - MCS index (status->rate_idx)
+ * - HT40 (status->flag & RX_FLAG_40MHZ)
+ * - short-GI (status->flag & RX_FLAG_SHORT_GI)
+ */
+ *pos = 0;
+ } else {
+ rthdr->it_present |= (1 << IEEE80211_RADIOTAP_RATE);
+ *pos = rate->bitrate / 5;
+ }
pos++;
/* IEEE80211_RADIOTAP_CHANNEL */
@@ -1849,9 +1860,15 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
if (!(sdata->dev->flags & IFF_PROMISC))
return 0;
rx->flags &= ~IEEE80211_RX_RA_MATCH;
- } else if (!rx->sta)
+ } else if (!rx->sta) {
+ int rate_idx;
+ if (rx->status->flag & RX_FLAG_HT)
+ rate_idx = 0; /* TODO: HT rates */
+ else
+ rate_idx = rx->status->rate_idx;
rx->sta = ieee80211_ibss_add_sta(sdata, bssid, hdr->addr2,
- BIT(rx->status->rate_idx));
+ BIT(rate_idx));
+ }
break;
case NL80211_IFTYPE_MESH_POINT:
if (!multicast &&
@@ -2057,7 +2074,13 @@ static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
tid_agg_rx->reorder_buf[index]->cb,
sizeof(status));
sband = local->hw.wiphy->bands[status.band];
- rate = &sband->bitrates[status.rate_idx];
+ if (status.flag & RX_FLAG_HT) {
+ /* TODO: HT rates */
+ rate = sband->bitrates;
+ } else {
+ rate = &sband->bitrates
+ [status.rate_idx];
+ }
__ieee80211_rx_handle_packet(hw,
tid_agg_rx->reorder_buf[index],
&status, rate);
@@ -2101,7 +2124,10 @@ static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
memcpy(&status, tid_agg_rx->reorder_buf[index]->cb,
sizeof(status));
sband = local->hw.wiphy->bands[status.band];
- rate = &sband->bitrates[status.rate_idx];
+ if (status.flag & RX_FLAG_HT)
+ rate = sband->bitrates; /* TODO: HT rates */
+ else
+ rate = &sband->bitrates[status.rate_idx];
__ieee80211_rx_handle_packet(hw, tid_agg_rx->reorder_buf[index],
&status, rate);
tid_agg_rx->stored_mpdu_num--;
@@ -2189,15 +2215,26 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
}
sband = local->hw.wiphy->bands[status->band];
-
- if (!sband ||
- status->rate_idx < 0 ||
- status->rate_idx >= sband->n_bitrates) {
+ if (!sband) {
WARN_ON(1);
return;
}
- rate = &sband->bitrates[status->rate_idx];
+ if (status->flag & RX_FLAG_HT) {
+ /* rate_idx is MCS index */
+ if (WARN_ON(status->rate_idx < 0 ||
+ status->rate_idx >= 76))
+ return;
+ /* HT rates are not in the table - use the highest legacy rate
+ * for now since other parts of mac80211 may not yet be fully
+ * MCS aware. */
+ rate = &sband->bitrates[sband->n_bitrates - 1];
+ } else {
+ if (WARN_ON(status->rate_idx < 0 ||
+ status->rate_idx >= sband->n_bitrates))
+ return;
+ rate = &sband->bitrates[status->rate_idx];
+ }
/*
* key references and virtual interfaces are protected using RCU
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index b098c58d216..a4af3a124cc 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1473,6 +1473,19 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
goto fail;
}
+ if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) &&
+ local->dynamic_ps_timeout > 0) {
+ if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+ ieee80211_stop_queues_by_reason(&local->hw,
+ IEEE80211_QUEUE_STOP_REASON_PS);
+ queue_work(local->hw.workqueue,
+ &local->dynamic_ps_disable_work);
+ }
+
+ mod_timer(&local->dynamic_ps_timer, jiffies +
+ msecs_to_jiffies(local->dynamic_ps_timeout));
+ }
+
nh_pos = skb_network_header(skb) - skb->data;
h_pos = skb_transport_header(skb) - skb->data;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 505d68f344c..fb89e1d0aa0 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -330,10 +330,20 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_ctstoself_duration);
-void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
+static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
+ enum queue_stop_reason reason)
{
struct ieee80211_local *local = hw_to_local(hw);
+ /* we don't need to track ampdu queues */
+ if (queue < ieee80211_num_regular_queues(hw)) {
+ __clear_bit(reason, &local->queue_stop_reasons[queue]);
+
+ if (local->queue_stop_reasons[queue] != 0)
+ /* someone still has this queue stopped */
+ return;
+ }
+
if (test_bit(queue, local->queues_pending)) {
set_bit(queue, local->queues_pending_run);
tasklet_schedule(&local->tx_pending_tasklet);
@@ -341,22 +351,74 @@ void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
netif_wake_subqueue(local->mdev, queue);
}
}
+
+void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
+ enum queue_stop_reason reason)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+ __ieee80211_wake_queue(hw, queue, reason);
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
+{
+ ieee80211_wake_queue_by_reason(hw, queue,
+ IEEE80211_QUEUE_STOP_REASON_DRIVER);
+}
EXPORT_SYMBOL(ieee80211_wake_queue);
-void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
+static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
+ enum queue_stop_reason reason)
{
struct ieee80211_local *local = hw_to_local(hw);
+ /* we don't need to track ampdu queues */
+ if (queue < ieee80211_num_regular_queues(hw))
+ __set_bit(reason, &local->queue_stop_reasons[queue]);
+
netif_stop_subqueue(local->mdev, queue);
}
+
+void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
+ enum queue_stop_reason reason)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+ __ieee80211_stop_queue(hw, queue, reason);
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
+{
+ ieee80211_stop_queue_by_reason(hw, queue,
+ IEEE80211_QUEUE_STOP_REASON_DRIVER);
+}
EXPORT_SYMBOL(ieee80211_stop_queue);
-void ieee80211_stop_queues(struct ieee80211_hw *hw)
+void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
+ enum queue_stop_reason reason)
{
+ struct ieee80211_local *local = hw_to_local(hw);
+ unsigned long flags;
int i;
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+
for (i = 0; i < ieee80211_num_queues(hw); i++)
- ieee80211_stop_queue(hw, i);
+ __ieee80211_stop_queue(hw, i, reason);
+
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+void ieee80211_stop_queues(struct ieee80211_hw *hw)
+{
+ ieee80211_stop_queues_by_reason(hw,
+ IEEE80211_QUEUE_STOP_REASON_DRIVER);
}
EXPORT_SYMBOL(ieee80211_stop_queues);
@@ -367,12 +429,24 @@ int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue)
}
EXPORT_SYMBOL(ieee80211_queue_stopped);
-void ieee80211_wake_queues(struct ieee80211_hw *hw)
+void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
+ enum queue_stop_reason reason)
{
+ struct ieee80211_local *local = hw_to_local(hw);
+ unsigned long flags;
int i;
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+
for (i = 0; i < hw->queues + hw->ampdu_queues; i++)
- ieee80211_wake_queue(hw, i);
+ __ieee80211_wake_queue(hw, i, reason);
+
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+void ieee80211_wake_queues(struct ieee80211_hw *hw)
+{
+ ieee80211_wake_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_DRIVER);
}
EXPORT_SYMBOL(ieee80211_wake_queues);
@@ -641,7 +715,7 @@ int ieee80211_set_freq(struct ieee80211_sub_if_data *sdata, int freqMHz)
chan->flags & IEEE80211_CHAN_NO_IBSS)
return ret;
local->oper_channel = chan;
- local->oper_sec_chan_offset = NL80211_SEC_CHAN_NO_HT;
+ local->oper_channel_type = NL80211_CHAN_NO_HT;
if (local->sw_scanning || local->hw_scanning)
ret = 0;
diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c
index 15428048d01..7162d5816f3 100644
--- a/net/mac80211/wext.c
+++ b/net/mac80211/wext.c
@@ -830,25 +830,56 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev,
struct iw_param *wrq,
char *extra)
{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_conf *conf = &local->hw.conf;
+ int ret = 0, timeout = 0;
+ bool ps;
+
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return -EINVAL;
if (wrq->disabled) {
- conf->flags &= ~IEEE80211_CONF_PS;
- return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+ ps = false;
+ timeout = 0;
+ goto set;
}
switch (wrq->flags & IW_POWER_MODE) {
case IW_POWER_ON: /* If not specified */
case IW_POWER_MODE: /* If set all mask */
case IW_POWER_ALL_R: /* If explicitely state all */
- conf->flags |= IEEE80211_CONF_PS;
+ ps = true;
+ break;
+ default: /* Otherwise we ignore */
break;
- default: /* Otherwise we don't support it */
- return -EINVAL;
}
- return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+ if (wrq->flags & IW_POWER_TIMEOUT)
+ timeout = wrq->value / 1000;
+
+set:
+ if (ps == local->powersave && timeout == local->dynamic_ps_timeout)
+ return ret;
+
+ local->powersave = ps;
+ local->dynamic_ps_timeout = timeout;
+
+ if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) {
+ if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) &&
+ local->dynamic_ps_timeout > 0)
+ mod_timer(&local->dynamic_ps_timer, jiffies +
+ msecs_to_jiffies(local->dynamic_ps_timeout));
+ else {
+ if (local->powersave)
+ conf->flags |= IEEE80211_CONF_PS;
+ else
+ conf->flags &= ~IEEE80211_CONF_PS;
+ }
+ ret = ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+ }
+
+ return ret;
}
static int ieee80211_ioctl_giwpower(struct net_device *dev,
@@ -857,9 +888,8 @@ static int ieee80211_ioctl_giwpower(struct net_device *dev,
char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_conf *conf = &local->hw.conf;
- wrqu->power.disabled = !(conf->flags & IEEE80211_CONF_PS);
+ wrqu->power.disabled = !local->powersave;
return 0;
}
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 4335f76be71..1e728fff474 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -60,7 +60,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
.len = BUS_ID_SIZE-1 },
[NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
[NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
- [NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET] = { .type = NLA_U32 },
+ [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
[NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
[NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
@@ -362,8 +362,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
}
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
- enum nl80211_sec_chan_offset sec_chan_offset =
- NL80211_SEC_CHAN_NO_HT;
+ enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
struct ieee80211_channel *chan;
struct ieee80211_sta_ht_cap *ht_cap;
u32 freq, sec_freq;
@@ -375,13 +374,13 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
result = -EINVAL;
- if (info->attrs[NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET]) {
- sec_chan_offset = nla_get_u32(info->attrs[
- NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET]);
- if (sec_chan_offset != NL80211_SEC_CHAN_NO_HT &&
- sec_chan_offset != NL80211_SEC_CHAN_DISABLED &&
- sec_chan_offset != NL80211_SEC_CHAN_BELOW &&
- sec_chan_offset != NL80211_SEC_CHAN_ABOVE)
+ if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+ channel_type = nla_get_u32(info->attrs[
+ NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+ if (channel_type != NL80211_CHAN_NO_HT &&
+ channel_type != NL80211_CHAN_HT20 &&
+ channel_type != NL80211_CHAN_HT40PLUS &&
+ channel_type != NL80211_CHAN_HT40MINUS)
goto bad_res;
}
@@ -392,9 +391,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
goto bad_res;
- if (sec_chan_offset == NL80211_SEC_CHAN_BELOW)
+ if (channel_type == NL80211_CHAN_HT40MINUS)
sec_freq = freq - 20;
- else if (sec_chan_offset == NL80211_SEC_CHAN_ABOVE)
+ else if (channel_type == NL80211_CHAN_HT40PLUS)
sec_freq = freq + 20;
else
sec_freq = 0;
@@ -402,7 +401,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
/* no HT capabilities */
- if (sec_chan_offset != NL80211_SEC_CHAN_NO_HT &&
+ if (channel_type != NL80211_CHAN_NO_HT &&
!ht_cap->ht_supported)
goto bad_res;
@@ -422,7 +421,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
}
result = rdev->ops->set_channel(&rdev->wiphy, chan,
- sec_chan_offset);
+ channel_type);
if (result)
goto bad_res;
}
@@ -1091,12 +1090,46 @@ static int parse_station_flags(struct nlattr *nla, u32 *staflags)
return 0;
}
+static u16 nl80211_calculate_bitrate(struct rate_info *rate)
+{
+ int modulation, streams, bitrate;
+
+ if (!(rate->flags & RATE_INFO_FLAGS_MCS))
+ return rate->legacy;
+
+ /* the formula below does only work for MCS values smaller than 32 */
+ if (rate->mcs >= 32)
+ return 0;
+
+ modulation = rate->mcs & 7;
+ streams = (rate->mcs >> 3) + 1;
+
+ bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ?
+ 13500000 : 6500000;
+
+ if (modulation < 4)
+ bitrate *= (modulation + 1);
+ else if (modulation == 4)
+ bitrate *= (modulation + 2);
+ else
+ bitrate *= (modulation + 3);
+
+ bitrate *= streams;
+
+ if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
+ bitrate = (bitrate / 9) * 10;
+
+ /* do NOT round down here */
+ return (bitrate + 50000) / 100000;
+}
+
static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
int flags, struct net_device *dev,
u8 *mac_addr, struct station_info *sinfo)
{
void *hdr;
- struct nlattr *sinfoattr;
+ struct nlattr *sinfoattr, *txrate;
+ u16 bitrate;
hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
if (!hdr)
@@ -1126,7 +1159,29 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
if (sinfo->filled & STATION_INFO_PLINK_STATE)
NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
sinfo->plink_state);
+ if (sinfo->filled & STATION_INFO_SIGNAL)
+ NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
+ sinfo->signal);
+ if (sinfo->filled & STATION_INFO_TX_BITRATE) {
+ txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE);
+ if (!txrate)
+ goto nla_put_failure;
+
+ /* nl80211_calculate_bitrate will return 0 for mcs >= 32 */
+ bitrate = nl80211_calculate_bitrate(&sinfo->txrate);
+ if (bitrate > 0)
+ NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
+ if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS)
+ NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS,
+ sinfo->txrate.mcs);
+ if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH)
+ NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH);
+ if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI)
+ NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI);
+
+ nla_nest_end(msg, txrate);
+ }
nla_nest_end(msg, sinfoattr);
return genlmsg_end(msg, hdr);