Commit 8127fbdc authored by Benoit Papillault's avatar Benoit Papillault Committed by John W. Linville

ath5k: Fix TX/RX padding for all frames

Currently, the padding position is based on
ieee80211_get_hdrlen_from_skb(). This is not correct since the HW does
padding on RX (and expect the same padding to be present on TX) at the
following position :

- management : 24 + 6 if 4-addr format
- control    : 24 + 6 if 4-addr format
- data       : 24 + 6 if 4-addr format + 2 if QoS
- invalid    : 24 + 6 if 4-addr format

whereas ieee80211_get_hdrlen_from_skb() is :

- management : 24
- control    : 16 except for ACK/CTS where it is 10
- data       : 24 + 6 if 4-addr format + 2 if QoS + 2 if QoS & order
- invalid    : 24

So, correct frames are not affected : management frames do not use
4-addr format, control frames have no body and invalid frames are ...
not valid by definition. However, in order to use monitor interface for
debugging purpose, one must be able to send/receive any frames, be it
correct or not. Such frames are affected by incorrect padding.

Moreover, since padding is added on TX, we need to remove it before
calling ieee80211_tx_status. This affect TX packets received by monitor
interfaces.

It has been tested between an ath5k based card (AR5212) and an ar9170usb
based card (netgear WNDA3100) using a frame generator and a monitor
interface for each card.

v2: Added ath5k_add_padding / ath5k_remove_padding
Signed-off-by: default avatarBenoit Papillault <benoit.papillault@free.fr>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent c2ef355b
...@@ -1141,9 +1141,9 @@ struct ath5k_hw { ...@@ -1141,9 +1141,9 @@ struct ath5k_hw {
int (*ah_setup_rx_desc)(struct ath5k_hw *ah, struct ath5k_desc *desc, int (*ah_setup_rx_desc)(struct ath5k_hw *ah, struct ath5k_desc *desc,
u32 size, unsigned int flags); u32 size, unsigned int flags);
int (*ah_setup_tx_desc)(struct ath5k_hw *, struct ath5k_desc *, int (*ah_setup_tx_desc)(struct ath5k_hw *, struct ath5k_desc *,
unsigned int, unsigned int, enum ath5k_pkt_type, unsigned int, unsigned int, unsigned int, int, enum ath5k_pkt_type,
unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int,
unsigned int, unsigned int, unsigned int); unsigned int, unsigned int, unsigned int, unsigned int);
int (*ah_setup_mrr_tx_desc)(struct ath5k_hw *, struct ath5k_desc *, int (*ah_setup_mrr_tx_desc)(struct ath5k_hw *, struct ath5k_desc *,
unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int,
unsigned int, unsigned int); unsigned int, unsigned int);
...@@ -1342,9 +1342,4 @@ static inline u32 ath5k_hw_bitswap(u32 val, unsigned int bits) ...@@ -1342,9 +1342,4 @@ static inline u32 ath5k_hw_bitswap(u32 val, unsigned int bits)
return retval; return retval;
} }
static inline int ath5k_pad_size(int hdrlen)
{
return (hdrlen < 24) ? 0 : hdrlen & 3;
}
#endif #endif
...@@ -307,7 +307,7 @@ static int ath5k_rxbuf_setup(struct ath5k_softc *sc, ...@@ -307,7 +307,7 @@ static int ath5k_rxbuf_setup(struct ath5k_softc *sc,
struct ath5k_buf *bf); struct ath5k_buf *bf);
static int ath5k_txbuf_setup(struct ath5k_softc *sc, static int ath5k_txbuf_setup(struct ath5k_softc *sc,
struct ath5k_buf *bf, struct ath5k_buf *bf,
struct ath5k_txq *txq); struct ath5k_txq *txq, int padsize);
static inline void ath5k_txbuf_free(struct ath5k_softc *sc, static inline void ath5k_txbuf_free(struct ath5k_softc *sc,
struct ath5k_buf *bf) struct ath5k_buf *bf)
{ {
...@@ -1271,7 +1271,7 @@ static enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb) ...@@ -1271,7 +1271,7 @@ static enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb)
static int static int
ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf, ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf,
struct ath5k_txq *txq) struct ath5k_txq *txq, int padsize)
{ {
struct ath5k_hw *ah = sc->ah; struct ath5k_hw *ah = sc->ah;
struct ath5k_desc *ds = bf->desc; struct ath5k_desc *ds = bf->desc;
...@@ -1323,7 +1323,7 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf, ...@@ -1323,7 +1323,7 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf,
sc->vif, pktlen, info)); sc->vif, pktlen, info));
} }
ret = ah->ah_setup_tx_desc(ah, ds, pktlen, ret = ah->ah_setup_tx_desc(ah, ds, pktlen,
ieee80211_get_hdrlen_from_skb(skb), ieee80211_get_hdrlen_from_skb(skb), padsize,
get_hw_packet_type(skb), get_hw_packet_type(skb),
(sc->power_level * 2), (sc->power_level * 2),
hw_rate, hw_rate,
...@@ -1805,6 +1805,67 @@ ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb, ...@@ -1805,6 +1805,67 @@ ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb,
} }
} }
/*
* Compute padding position. skb must contains an IEEE 802.11 frame
*/
static int ath5k_common_padpos(struct sk_buff *skb)
{
struct ieee80211_hdr * hdr = (struct ieee80211_hdr *)skb->data;
__le16 frame_control = hdr->frame_control;
int padpos = 24;
if (ieee80211_has_a4(frame_control)) {
padpos += ETH_ALEN;
}
if (ieee80211_is_data_qos(frame_control)) {
padpos += IEEE80211_QOS_CTL_LEN;
}
return padpos;
}
/*
* This function expects a 802.11 frame and returns the number of
* bytes added, or -1 if we don't have enought header room.
*/
static int ath5k_add_padding(struct sk_buff *skb)
{
int padpos = ath5k_common_padpos(skb);
int padsize = padpos & 3;
if (padsize && skb->len>padpos) {
if (skb_headroom(skb) < padsize)
return -1;
skb_push(skb, padsize);
memmove(skb->data, skb->data+padsize, padpos);
return padsize;
}
return 0;
}
/*
* This function expects a 802.11 frame and returns the number of
* bytes removed
*/
static int ath5k_remove_padding(struct sk_buff *skb)
{
int padpos = ath5k_common_padpos(skb);
int padsize = padpos & 3;
if (padsize && skb->len>=padpos+padsize) {
memmove(skb->data + padsize, skb->data, padpos);
skb_pull(skb, padsize);
return padsize;
}
return 0;
}
static void static void
ath5k_tasklet_rx(unsigned long data) ath5k_tasklet_rx(unsigned long data)
{ {
...@@ -1818,8 +1879,6 @@ ath5k_tasklet_rx(unsigned long data) ...@@ -1818,8 +1879,6 @@ ath5k_tasklet_rx(unsigned long data)
struct ath5k_buf *bf; struct ath5k_buf *bf;
struct ath5k_desc *ds; struct ath5k_desc *ds;
int ret; int ret;
int hdrlen;
int padsize;
int rx_flag; int rx_flag;
spin_lock(&sc->rxbuflock); spin_lock(&sc->rxbuflock);
...@@ -1904,12 +1963,8 @@ ath5k_tasklet_rx(unsigned long data) ...@@ -1904,12 +1963,8 @@ ath5k_tasklet_rx(unsigned long data)
* bytes and we can optimize this a bit. In addition, we must * bytes and we can optimize this a bit. In addition, we must
* not try to remove padding from short control frames that do * not try to remove padding from short control frames that do
* not have payload. */ * not have payload. */
hdrlen = ieee80211_get_hdrlen_from_skb(skb); ath5k_remove_padding(skb);
padsize = ath5k_pad_size(hdrlen);
if (padsize) {
memmove(skb->data + padsize, skb->data, hdrlen);
skb_pull(skb, padsize);
}
rxs = IEEE80211_SKB_RXCB(skb); rxs = IEEE80211_SKB_RXCB(skb);
/* /*
...@@ -2029,6 +2084,12 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq) ...@@ -2029,6 +2084,12 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
info->status.ack_signal = ts.ts_rssi; info->status.ack_signal = ts.ts_rssi;
} }
/*
* Remove MAC header padding before giving the frame
* back to mac80211.
*/
ath5k_remove_padding(skb);
ieee80211_tx_status(sc->hw, skb); ieee80211_tx_status(sc->hw, skb);
spin_lock(&sc->txbuflock); spin_lock(&sc->txbuflock);
...@@ -2072,6 +2133,7 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) ...@@ -2072,6 +2133,7 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
int ret = 0; int ret = 0;
u8 antenna; u8 antenna;
u32 flags; u32 flags;
const int padsize = 0;
bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len, bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len,
PCI_DMA_TODEVICE); PCI_DMA_TODEVICE);
...@@ -2119,7 +2181,7 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) ...@@ -2119,7 +2181,7 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
* from tx power (value is in dB units already) */ * from tx power (value is in dB units already) */
ds->ds_data = bf->skbaddr; ds->ds_data = bf->skbaddr;
ret = ah->ah_setup_tx_desc(ah, ds, skb->len, ret = ah->ah_setup_tx_desc(ah, ds, skb->len,
ieee80211_get_hdrlen_from_skb(skb), ieee80211_get_hdrlen_from_skb(skb), padsize,
AR5K_PKT_TYPE_BEACON, (sc->power_level * 2), AR5K_PKT_TYPE_BEACON, (sc->power_level * 2),
ieee80211_get_tx_rate(sc->hw, info)->hw_value, ieee80211_get_tx_rate(sc->hw, info)->hw_value,
1, AR5K_TXKEYIX_INVALID, 1, AR5K_TXKEYIX_INVALID,
...@@ -2679,7 +2741,6 @@ static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, ...@@ -2679,7 +2741,6 @@ static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
struct ath5k_softc *sc = hw->priv; struct ath5k_softc *sc = hw->priv;
struct ath5k_buf *bf; struct ath5k_buf *bf;
unsigned long flags; unsigned long flags;
int hdrlen;
int padsize; int padsize;
ath5k_debug_dump_skb(sc, skb, "TX ", 1); ath5k_debug_dump_skb(sc, skb, "TX ", 1);
...@@ -2691,17 +2752,11 @@ static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, ...@@ -2691,17 +2752,11 @@ static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
* the hardware expects the header padded to 4 byte boundaries * the hardware expects the header padded to 4 byte boundaries
* if this is not the case we add the padding after the header * if this is not the case we add the padding after the header
*/ */
hdrlen = ieee80211_get_hdrlen_from_skb(skb); padsize = ath5k_add_padding(skb);
padsize = ath5k_pad_size(hdrlen); if (padsize < 0) {
if (padsize) { ATH5K_ERR(sc, "tx hdrlen not %%4: not enough"
" headroom to pad");
if (skb_headroom(skb) < padsize) { goto drop_packet;
ATH5K_ERR(sc, "tx hdrlen not %%4: %d not enough"
" headroom to pad %d\n", hdrlen, padsize);
goto drop_packet;
}
skb_push(skb, padsize);
memmove(skb->data, skb->data+padsize, hdrlen);
} }
spin_lock_irqsave(&sc->txbuflock, flags); spin_lock_irqsave(&sc->txbuflock, flags);
...@@ -2720,7 +2775,7 @@ static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, ...@@ -2720,7 +2775,7 @@ static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
bf->skb = skb; bf->skb = skb;
if (ath5k_txbuf_setup(sc, bf, txq)) { if (ath5k_txbuf_setup(sc, bf, txq, padsize)) {
bf->skb = NULL; bf->skb = NULL;
spin_lock_irqsave(&sc->txbuflock, flags); spin_lock_irqsave(&sc->txbuflock, flags);
list_add_tail(&bf->list, &sc->txbuf); list_add_tail(&bf->list, &sc->txbuf);
......
...@@ -35,7 +35,8 @@ ...@@ -35,7 +35,8 @@
*/ */
static int static int
ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc, ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
unsigned int pkt_len, unsigned int hdr_len, enum ath5k_pkt_type type, unsigned int pkt_len, unsigned int hdr_len, int padsize,
enum ath5k_pkt_type type,
unsigned int tx_power, unsigned int tx_rate0, unsigned int tx_tries0, unsigned int tx_power, unsigned int tx_rate0, unsigned int tx_tries0,
unsigned int key_index, unsigned int antenna_mode, unsigned int flags, unsigned int key_index, unsigned int antenna_mode, unsigned int flags,
unsigned int rtscts_rate, unsigned int rtscts_duration) unsigned int rtscts_rate, unsigned int rtscts_duration)
...@@ -71,7 +72,7 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc, ...@@ -71,7 +72,7 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
/* Verify and set frame length */ /* Verify and set frame length */
/* remove padding we might have added before */ /* remove padding we might have added before */
frame_len = pkt_len - ath5k_pad_size(hdr_len) + FCS_LEN; frame_len = pkt_len - padsize + FCS_LEN;
if (frame_len & ~AR5K_2W_TX_DESC_CTL0_FRAME_LEN) if (frame_len & ~AR5K_2W_TX_DESC_CTL0_FRAME_LEN)
return -EINVAL; return -EINVAL;
...@@ -100,7 +101,7 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc, ...@@ -100,7 +101,7 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
AR5K_REG_SM(hdr_len, AR5K_2W_TX_DESC_CTL0_HEADER_LEN); AR5K_REG_SM(hdr_len, AR5K_2W_TX_DESC_CTL0_HEADER_LEN);
} }
/*Diferences between 5210-5211*/ /*Differences between 5210-5211*/
if (ah->ah_version == AR5K_AR5210) { if (ah->ah_version == AR5K_AR5210) {
switch (type) { switch (type) {
case AR5K_PKT_TYPE_BEACON: case AR5K_PKT_TYPE_BEACON:
...@@ -165,6 +166,7 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc, ...@@ -165,6 +166,7 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
*/ */
static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah, static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
struct ath5k_desc *desc, unsigned int pkt_len, unsigned int hdr_len, struct ath5k_desc *desc, unsigned int pkt_len, unsigned int hdr_len,
int padsize,
enum ath5k_pkt_type type, unsigned int tx_power, unsigned int tx_rate0, enum ath5k_pkt_type type, unsigned int tx_power, unsigned int tx_rate0,
unsigned int tx_tries0, unsigned int key_index, unsigned int tx_tries0, unsigned int key_index,
unsigned int antenna_mode, unsigned int flags, unsigned int antenna_mode, unsigned int flags,
...@@ -206,7 +208,7 @@ static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah, ...@@ -206,7 +208,7 @@ static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
/* Verify and set frame length */ /* Verify and set frame length */
/* remove padding we might have added before */ /* remove padding we might have added before */
frame_len = pkt_len - ath5k_pad_size(hdr_len) + FCS_LEN; frame_len = pkt_len - padsize + FCS_LEN;
if (frame_len & ~AR5K_4W_TX_DESC_CTL0_FRAME_LEN) if (frame_len & ~AR5K_4W_TX_DESC_CTL0_FRAME_LEN)
return -EINVAL; return -EINVAL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment