Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6

This commit is contained in:
David S. Miller 2008-12-21 19:57:10 -08:00
commit c2da953a46
50 changed files with 1166 additions and 651 deletions

View File

@ -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

View File

@ -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 @@ ath5k_tasklet_rx(unsigned long data)
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);

View File

@ -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;

View File

@ -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];

View File

@ -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;
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;
vif = sc->sc_vaps[0];
opmode = vif->type;
/*
* 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;
freeslot = (key->keyidx >= 4) ? 1 : 0;
ath_key_reset(sc, key->keyidx, freeslot);
clear_bit(key->hw_key_idx, sc->sc_keymap);
if (key->alg != ALG_TKIP)
return;
clear_bit(key->hw_key_idx + 64, sc->sc_keymap);
if (sc->sc_splitmic) {
clear_bit(key->hw_key_idx + 32, sc->sc_keymap);
clear_bit(key->hw_key_idx + 64 + 32, sc->sc_keymap);
}
}
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);
}

View File

@ -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;

View File

@ -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
@ -528,6 +519,15 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush)
if (!skb)
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);

View File

@ -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);
}

View File

@ -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
}

View File

@ -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,
};

View File

@ -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;
}

View File

@ -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);
}
/**

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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");

View File

@ -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);
}

View File

@ -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,

View File

@ -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;

View File

@ -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 */

View File

@ -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 */

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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)
{

View File

@ -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);

View File

@ -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)");

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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;
}
@ -1684,9 +1740,6 @@ static int p54_start(struct ieee80211_hw *dev)
P54_SET_QUEUE(priv->qos_params[2], 0x0003, 0x000f, 0x03ff, 0);
P54_SET_QUEUE(priv->qos_params[3], 0x0007, 0x000f, 0x03ff, 0);
err = p54_set_edcf(dev);
if (err)
goto out;
err = p54_init_stats(dev);
if (err)
goto out;
@ -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 ;
priv->tx(dev, priv->cached_stats, 0);
/*
* TODO: walk through tx_queue and do the following tasks
* 1. initiate bursts.
* 2. cancel stuck frames / reset the device if necessary.
*/
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);

View File

@ -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;

View File

@ -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));

View File

@ -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);

View File

@ -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);

View File

@ -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 */
@ -424,6 +424,32 @@ enum nl80211_sta_flags {
NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1
};
/**
* 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
*
@ -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 */

View File

@ -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 */

View File

@ -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,
};
/**

View File

@ -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);
}

View File

@ -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 */

View File

@ -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

View File

@ -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,

View File

@ -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);
}

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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);