2010-10-02 09:35:14

by Ivo Van Doorn

[permalink] [raw]
Subject: [PATCH 01/20] rt2x00: Don't overwrite beacon buffers in pairwise key setup

From: Helmut Schaa <[email protected]>

rt2800 devices use parts of the pariwise key table to store the beacon
frames for beacon 6 and 7. To not overwrite the beacon frame buffers
limit the number of entries we store in the pairwise key table to 222.

Also add some descriptive comments about this shared memory usage.

Signed-off-by: Helmut Schaa <[email protected]>
Signed-off-by: Ivo van Doorn <[email protected]>
---
drivers/net/wireless/rt2x00/rt2800.h | 21 ++++++++++++++++++++-
drivers/net/wireless/rt2x00/rt2800lib.c | 6 +++++-
2 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h
index 2edc774..e1583cf 100644
--- a/drivers/net/wireless/rt2x00/rt2800.h
+++ b/drivers/net/wireless/rt2x00/rt2800.h
@@ -1435,6 +1435,24 @@

/*
* Security key table memory.
+ *
+ * The pairwise key table shares some memory with the beacon frame
+ * buffers 6 and 7. That basically means that when beacon 6 & 7
+ * are used we should only use the reduced pairwise key table which
+ * has a maximum of 222 entries.
+ *
+ * ---------------------------------------------
+ * |0x4000 | Pairwise Key | Reduced Pairwise |
+ * | | Table | Key Table |
+ * | | Size: 256 * 32 | Size: 222 * 32 |
+ * |0x5BC0 | |-------------------
+ * | | | Beacon 6 |
+ * |0x5DC0 | |-------------------
+ * | | | Beacon 7 |
+ * |0x5FC0 | |-------------------
+ * |0x5FFF | |
+ * --------------------------
+ *
* MAC_WCID_BASE: 8-bytes (use only 6 bytes) * 256 entry
* PAIRWISE_KEY_TABLE_BASE: 32-byte * 256 entry
* MAC_IVEIV_TABLE_BASE: 8-byte * 256-entry
@@ -1584,7 +1602,8 @@ struct mac_iveiv_entry {
* 2. Extract memory from FCE table for BCN 4~5
* 3. Extract memory from Pair-wise key table for BCN 6~7
* It occupied those memory of wcid 238~253 for BCN 6
- * and wcid 222~237 for BCN 7
+ * and wcid 222~237 for BCN 7 (see Security key table memory
+ * for more info).
*
* IMPORTANT NOTE: Not sure why legacy driver does this,
* but HW_BEACON_BASE7 is 0x0200 bytes below HW_BEACON_BASE6.
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index c7076de..daa32ae 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -1031,8 +1031,12 @@ int rt2800_config_pairwise_key(struct rt2x00_dev *rt2x00dev,
* 1 pairwise key is possible per AID, this means that the AID
* equals our hw_key_idx. Make sure the WCID starts _after_ the
* last possible shared key entry.
+ *
+ * Since parts of the pairwise key table might be shared with
+ * the beacon frame buffers 6 & 7 we should only write into the
+ * first 222 entries.
*/
- if (crypto->aid > (256 - 32))
+ if (crypto->aid > (222 - 32))
return -ENOSPC;

key->hw_key_idx = 32 + crypto->aid;
--
1.7.2.3



2010-10-02 09:35:25

by Ivo Van Doorn

[permalink] [raw]
Subject: [PATCH 06/20] rt2x00: Don't initialize MM40 HT protection to RTS/CTS on PCI devices

From: Helmut Schaa <[email protected]>

Since HT protection is now configurable via mac80211 we don't need this
special case for PCI devices anymore. The HT protection config will be
overwritten as soon as mac80211 sends us a HT operation mode. Hence,
bring the HT MM40 protection config in sync with the other HT protection
registers and initialize it to no protection.

Signed-off-by: Helmut Schaa <[email protected]>
Signed-off-by: Ivo van Doorn <[email protected]>
---
drivers/net/wireless/rt2x00/rt2800lib.c | 3 +--
1 files changed, 1 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 4ecacea..76b8783 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -2016,8 +2016,7 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)

rt2800_register_read(rt2x00dev, MM40_PROT_CFG, &reg);
rt2x00_set_field32(&reg, MM40_PROT_CFG_PROTECT_RATE, 0x4084);
- rt2x00_set_field32(&reg, MM40_PROT_CFG_PROTECT_CTRL,
- !rt2x00_is_usb(rt2x00dev));
+ rt2x00_set_field32(&reg, MM40_PROT_CFG_PROTECT_CTRL, 0);
rt2x00_set_field32(&reg, MM40_PROT_CFG_PROTECT_NAV, 1);
rt2x00_set_field32(&reg, MM40_PROT_CFG_TX_OP_ALLOW_CCK, 1);
rt2x00_set_field32(&reg, MM40_PROT_CFG_TX_OP_ALLOW_OFDM, 1);
--
1.7.2.3


2010-10-02 09:35:36

by Ivo Van Doorn

[permalink] [raw]
Subject: [PATCH 14/20] rt2x00: correctly set max_report_rates in rt61pci and rt2800

From: Helmut Schaa <[email protected]>

rt61pci and rt2800 devices can use up to 7 different rates per tx frame.
However, the device uses a global fallback table. Hence, the rc
algortihm cannot specify multiple rates to try but the device is able to
report multiple rates (based on the retry table). Specify that behavior
by correctly setting max_report_rates and max_rates.

This makes rt2x00 and minstrel play nicer together.

Signed-off-by: Helmut Schaa <[email protected]>
Signed-off-by: Ivo van Doorn <[email protected]>
---
drivers/net/wireless/rt2x00/rt2800lib.c | 5 +++--
drivers/net/wireless/rt2x00/rt61pci.c | 5 +++--
2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 99b505f..403e2ed 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -3188,12 +3188,13 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
* As rt2800 has a global fallback table we cannot specify
* more then one tx rate per frame but since the hw will
* try several rates (based on the fallback table) we should
- * still initialize max_rates to the maximum number of rates
+ * initialize max_report_rates to the maximum number of rates
* we are going to try. Otherwise mac80211 will truncate our
* reported tx rates and the rc algortihm will end up with
* incorrect data.
*/
- rt2x00dev->hw->max_rates = 7;
+ rt2x00dev->hw->max_rates = 1;
+ rt2x00dev->hw->max_report_rates = 7;
rt2x00dev->hw->max_rate_tries = 1;

rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom);
diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c
index 8940070f..ac370f1 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.c
+++ b/drivers/net/wireless/rt2x00/rt61pci.c
@@ -2630,12 +2630,13 @@ static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
* As rt61 has a global fallback table we cannot specify
* more then one tx rate per frame but since the hw will
* try several rates (based on the fallback table) we should
- * still initialize max_rates to the maximum number of rates
+ * initialize max_report_rates to the maximum number of rates
* we are going to try. Otherwise mac80211 will truncate our
* reported tx rates and the rc algortihm will end up with
* incorrect data.
*/
- rt2x00dev->hw->max_rates = 7;
+ rt2x00dev->hw->max_rates = 1;
+ rt2x00dev->hw->max_report_rates = 7;
rt2x00dev->hw->max_rate_tries = 1;

/*
--
1.7.2.3


2010-10-02 11:06:35

by Ivo Van Doorn

[permalink] [raw]
Subject: Re: [PATCH 15/20] rt2x00: Improve TX status entry validation

On Sat, Oct 2, 2010 at 11:46 AM, Walter Goldens
<[email protected]> wrote:
> Do all the latest patches fix the packet loss and general connectivity issues caused by the DMA in rt2800usb devices?

Nope. Still working on that.

Ivo

2010-10-02 09:35:16

by Ivo Van Doorn

[permalink] [raw]
Subject: [PATCH 02/20] rt2x00: Split out parts of the rt2800_txdone function for easier reuse

From: Helmut Schaa <[email protected]>

Signed-off-by: Helmut Schaa <[email protected]>
Signed-off-by: Ivo van Doorn <[email protected]>
---
drivers/net/wireless/rt2x00/rt2800lib.c | 100 ++++++++++++++++--------------
drivers/net/wireless/rt2x00/rt2800lib.h | 1 +
2 files changed, 54 insertions(+), 47 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index daa32ae..19534f2 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -630,15 +630,63 @@ static bool rt2800_txdone_entry_check(struct queue_entry *entry, u32 reg)
return true;
}

+void rt2800_txdone_entry(struct queue_entry *entry, u32 status)
+{
+ struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
+ struct txdone_entry_desc txdesc;
+ u32 word;
+ u16 mcs, real_mcs;
+ __le32 *txwi;
+
+ /*
+ * Obtain the status about this packet.
+ */
+ txdesc.flags = 0;
+ txwi = rt2800_drv_get_txwi(entry);
+ rt2x00_desc_read(txwi, 0, &word);
+ mcs = rt2x00_get_field32(word, TXWI_W0_MCS);
+ real_mcs = rt2x00_get_field32(status, TX_STA_FIFO_MCS);
+
+ /*
+ * Ralink has a retry mechanism using a global fallback
+ * table. We setup this fallback table to try the immediate
+ * lower rate for all rates. In the TX_STA_FIFO, the MCS field
+ * always contains the MCS used for the last transmission, be
+ * it successful or not.
+ */
+ if (rt2x00_get_field32(status, TX_STA_FIFO_TX_SUCCESS)) {
+ /*
+ * Transmission succeeded. The number of retries is
+ * mcs - real_mcs
+ */
+ __set_bit(TXDONE_SUCCESS, &txdesc.flags);
+ txdesc.retry = ((mcs > real_mcs) ? mcs - real_mcs : 0);
+ } else {
+ /*
+ * Transmission failed. The number of retries is
+ * always 7 in this case (for a total number of 8
+ * frames sent).
+ */
+ __set_bit(TXDONE_FAILURE, &txdesc.flags);
+ txdesc.retry = rt2x00dev->long_retry;
+ }
+
+ /*
+ * the frame was retried at least once
+ * -> hw used fallback rates
+ */
+ if (txdesc.retry)
+ __set_bit(TXDONE_FALLBACK, &txdesc.flags);
+
+ rt2x00lib_txdone(entry, &txdesc);
+}
+EXPORT_SYMBOL_GPL(rt2800_txdone_entry);
+
void rt2800_txdone(struct rt2x00_dev *rt2x00dev)
{
struct data_queue *queue;
struct queue_entry *entry;
- __le32 *txwi;
- struct txdone_entry_desc txdesc;
- u32 word;
u32 reg;
- u16 mcs, real_mcs;
u8 pid;
int i;

@@ -673,7 +721,6 @@ void rt2800_txdone(struct rt2x00_dev *rt2x00dev)
* order. We first check that the queue is not empty.
*/
entry = NULL;
- txwi = NULL;
while (!rt2x00queue_empty(queue)) {
entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
if (rt2800_txdone_entry_check(entry, reg))
@@ -683,48 +730,7 @@ void rt2800_txdone(struct rt2x00_dev *rt2x00dev)
if (!entry || rt2x00queue_empty(queue))
break;

-
- /*
- * Obtain the status about this packet.
- */
- txdesc.flags = 0;
- txwi = rt2800_drv_get_txwi(entry);
- rt2x00_desc_read(txwi, 0, &word);
- mcs = rt2x00_get_field32(word, TXWI_W0_MCS);
- real_mcs = rt2x00_get_field32(reg, TX_STA_FIFO_MCS);
-
- /*
- * Ralink has a retry mechanism using a global fallback
- * table. We setup this fallback table to try the immediate
- * lower rate for all rates. In the TX_STA_FIFO, the MCS field
- * always contains the MCS used for the last transmission, be
- * it successful or not.
- */
- if (rt2x00_get_field32(reg, TX_STA_FIFO_TX_SUCCESS)) {
- /*
- * Transmission succeeded. The number of retries is
- * mcs - real_mcs
- */
- __set_bit(TXDONE_SUCCESS, &txdesc.flags);
- txdesc.retry = ((mcs > real_mcs) ? mcs - real_mcs : 0);
- } else {
- /*
- * Transmission failed. The number of retries is
- * always 7 in this case (for a total number of 8
- * frames sent).
- */
- __set_bit(TXDONE_FAILURE, &txdesc.flags);
- txdesc.retry = rt2x00dev->long_retry;
- }
-
- /*
- * the frame was retried at least once
- * -> hw used fallback rates
- */
- if (txdesc.retry)
- __set_bit(TXDONE_FALLBACK, &txdesc.flags);
-
- rt2x00lib_txdone(entry, &txdesc);
+ rt2800_txdone_entry(entry, reg);
}
}
EXPORT_SYMBOL_GPL(rt2800_txdone);
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/rt2x00/rt2800lib.h
index 600c5eb..81cbc92 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.h
+++ b/drivers/net/wireless/rt2x00/rt2800lib.h
@@ -153,6 +153,7 @@ void rt2800_write_tx_data(struct queue_entry *entry,
void rt2800_process_rxwi(struct queue_entry *entry, struct rxdone_entry_desc *txdesc);

void rt2800_txdone(struct rt2x00_dev *rt2x00dev);
+void rt2800_txdone_entry(struct queue_entry *entry, u32 status);

void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc);

--
1.7.2.3


2010-10-02 09:35:46

by Ivo Van Doorn

[permalink] [raw]
Subject: [PATCH 20/20] rt2x00: Work around hw aggregation oddity in rt2800

From: Helmut Schaa <[email protected]>

If a frame is not meant to be sent as AMPDU or part of it the hw might
still decide to aggregate this frame if a previous frame started an
AMPDU. However, this will limit the usefulness of the reported tx rate
since the reported rate will be the one specified in the TXWI of the
first frame and thus it is not possible to reliably caculate the
number of retrys by substracting the reported tx rate from the tx rate
in the TXWI.

To fix this issue, only report the successful rate for frames that were
not meant to be aggregated but ended up in an aggregate.

Example:
Frame A (MCS7, AMPDU=1) B (MCS7, AMPDU=1) C (MCS12, AMDPU=0, PROBE_RATE)

Although frame C shoudn't be aggregated the hw might sill put it
into an AMPDU together with A and B. If the transmission succeeds the tx
status will contain MCS7 for all three frames. In that case we should
only report MCS7 as success rate and avoid reporting MCS12-MCS8 as
failed tx attempts as this will affect the future rate control
decisions.

This oddity might strike us in other scenarious as well but the most
common "wrong" report happened for frames used to probe a different tx
rate.

This improves the rate control decisions notable.

Signed-off-by: Helmut Schaa <[email protected]>
Signed-off-by: Ivo van Doorn <[email protected]>
---
drivers/net/wireless/rt2x00/rt2800lib.c | 27 +++++++++++++++++++++++++++
1 files changed, 27 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 0287157..7f040b0 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -634,9 +634,11 @@ static bool rt2800_txdone_entry_check(struct queue_entry *entry, u32 reg)
void rt2800_txdone_entry(struct queue_entry *entry, u32 status)
{
struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
+ struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
struct txdone_entry_desc txdesc;
u32 word;
u16 mcs, real_mcs;
+ int aggr, ampdu;
__le32 *txwi;

/*
@@ -645,8 +647,33 @@ void rt2800_txdone_entry(struct queue_entry *entry, u32 status)
txdesc.flags = 0;
txwi = rt2800_drv_get_txwi(entry);
rt2x00_desc_read(txwi, 0, &word);
+
mcs = rt2x00_get_field32(word, TXWI_W0_MCS);
+ ampdu = rt2x00_get_field32(word, TXWI_W0_AMPDU);
+
real_mcs = rt2x00_get_field32(status, TX_STA_FIFO_MCS);
+ aggr = rt2x00_get_field32(status, TX_STA_FIFO_TX_AGGRE);
+
+ /*
+ * If a frame was meant to be sent as a single non-aggregated MPDU
+ * but ended up in an aggregate the used tx rate doesn't correlate
+ * with the one specified in the TXWI as the whole aggregate is sent
+ * with the same rate.
+ *
+ * For example: two frames are sent to rt2x00, the first one sets
+ * AMPDU=1 and requests MCS7 whereas the second frame sets AMDPU=0
+ * and requests MCS15. If the hw aggregates both frames into one
+ * AMDPU the tx status for both frames will contain MCS7 although
+ * the frame was sent successfully.
+ *
+ * Hence, replace the requested rate with the real tx rate to not
+ * confuse the rate control algortihm by providing clearly wrong
+ * data.
+ */
+ if (aggr == 1 && ampdu == 0 && real_mcs != mcs) {
+ skbdesc->tx_rate_idx = real_mcs;
+ mcs = real_mcs;
+ }

/*
* Ralink has a retry mechanism using a global fallback
--
1.7.2.3


2010-10-02 09:35:33

by Ivo Van Doorn

[permalink] [raw]
Subject: [PATCH 12/20] rt2x00: Don't enable broad- and multicast buffering on USB devices

From: Helmut Schaa <[email protected]>

Since rt2x00 USB devices have no chance to know when a beacon was sent
out in AP mode currently all broad- and multicast traffic is buffered in
mac80211 but never sent out at all.

Unfortunately we have no chance in sending the traffic out after a
DTIM beacon due to hw limitations. Hence, instead of never sending the
buffered traffic out better send it out immediately.

Signed-off-by: Helmut Schaa <[email protected]>
Reported-by: Lauri Hintsala <[email protected]>
Signed-off-by: Ivo van Doorn <[email protected]>
---
drivers/net/wireless/rt2x00/rt2500usb.c | 7 ++++++-
drivers/net/wireless/rt2x00/rt2800lib.c | 11 ++++++++++-
drivers/net/wireless/rt2x00/rt73usb.c | 7 ++++++-
3 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c
index 5843a6d..1033964 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.c
+++ b/drivers/net/wireless/rt2x00/rt2500usb.c
@@ -1674,10 +1674,15 @@ static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)

/*
* Initialize all hw fields.
+ *
+ * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING unless we are
+ * capable of sending the buffered frames out after the DTIM
+ * transmission using rt2x00lib_beacondone. This will send out
+ * multicast and broadcast traffic immediately instead of buffering it
+ * infinitly and thus dropping it after some time.
*/
rt2x00dev->hw->flags =
IEEE80211_HW_RX_INCLUDES_FCS |
- IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_PS_NULLFUNC_STACK;
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 76b8783..99b505f 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -3164,11 +3164,20 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
* Initialize all hw fields.
*/
rt2x00dev->hw->flags =
- IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_PS_NULLFUNC_STACK |
IEEE80211_HW_AMPDU_AGGREGATION;
+ /*
+ * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING for USB devices
+ * unless we are capable of sending the buffered frames out after the
+ * DTIM transmission using rt2x00lib_beacondone. This will send out
+ * multicast and broadcast traffic immediately instead of buffering it
+ * infinitly and thus dropping it after some time.
+ */
+ if (!rt2x00_is_usb(rt2x00dev))
+ rt2x00dev->hw->flags |=
+ IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING;

SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev);
SET_IEEE80211_PERM_ADDR(rt2x00dev->hw,
diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c
index f78bc2a..66939fd 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/rt2x00/rt73usb.c
@@ -2063,9 +2063,14 @@ static int rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)

/*
* Initialize all hw fields.
+ *
+ * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING unless we are
+ * capable of sending the buffered frames out after the DTIM
+ * transmission using rt2x00lib_beacondone. This will send out
+ * multicast and broadcast traffic immediately instead of buffering it
+ * infinitly and thus dropping it after some time.
*/
rt2x00dev->hw->flags =
- IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_PS_NULLFUNC_STACK;
--
1.7.2.3


2010-10-02 09:35:40

by Ivo Van Doorn

[permalink] [raw]
Subject: [PATCH 16/20] rt2x00: Enable rx aggregation in rt2800

From: Helmut Schaa <[email protected]>

Signed-off-by: Helmut Schaa <[email protected]>
Signed-off-by: Ivo van Doorn <[email protected]>
---
drivers/net/wireless/rt2x00/rt2800lib.c | 8 ++++++--
1 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 8e507d0..0287157 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -3452,8 +3452,12 @@ int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
switch (action) {
case IEEE80211_AMPDU_RX_START:
case IEEE80211_AMPDU_RX_STOP:
- /* we don't support RX aggregation yet */
- ret = -ENOTSUPP;
+ /*
+ * The hw itself takes care of setting up BlockAck mechanisms.
+ * So, we only have to allow mac80211 to nagotiate a BlockAck
+ * agreement. Once that is done, the hw will BlockAck incoming
+ * AMPDUs without further setup.
+ */
break;
case IEEE80211_AMPDU_TX_START:
ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
--
1.7.2.3


2010-10-02 09:35:34

by Ivo Van Doorn

[permalink] [raw]
Subject: [PATCH 13/20] mac80211: distinct between max rates and the number of rates the hw can report

From: Helmut Schaa <[email protected]>

Some drivers cannot handle multiple retry rates specified by the rc
algorithm but instead use their own retry table (for example rt2800).
However, if such a device registers itself with a max_rates value of 1
the rc algorithm cannot make use of the extended information the device
can provide about retried rates. On the other hand, if a device
registers itself with a max_rates value > 1 the rc algorithm assumes
that the device can handle multi rate retries.

Fix this issue by introducing another hw parameter max_report_rates that
can be set to a different value then max_rates to indicate if a device
is capable of reporting more rates then specified in max_rates.

Signed-off-by: Helmut Schaa <[email protected]>
Signed-off-by: Ivo van Doorn <[email protected]>
---
include/net/mac80211.h | 6 +++++-
net/mac80211/main.c | 4 ++++
net/mac80211/status.c | 2 +-
3 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 12a49f0..2f11881 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1095,7 +1095,10 @@ enum ieee80211_hw_flags {
* @sta_data_size: size (in bytes) of the drv_priv data area
* within &struct ieee80211_sta.
*
- * @max_rates: maximum number of alternate rate retry stages
+ * @max_rates: maximum number of alternate rate retry stages the hw
+ * can handle.
+ * @max_report_rates: maximum number of alternate rate retry stages
+ * the hw can report back.
* @max_rate_tries: maximum number of tries for each stage
*
* @napi_weight: weight used for NAPI polling. You must specify an
@@ -1117,6 +1120,7 @@ struct ieee80211_hw {
u16 max_listen_interval;
s8 max_signal;
u8 max_rates;
+ u8 max_report_rates;
u8 max_rate_tries;
};

diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 18fdeca..f245c83 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -532,6 +532,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
/* set up some defaults */
local->hw.queues = 1;
local->hw.max_rates = 1;
+ local->hw.max_report_rates = 0;
local->hw.conf.long_frame_max_tx_count = wiphy->retry_long;
local->hw.conf.short_frame_max_tx_count = wiphy->retry_short;
local->user_power_level = -1;
@@ -607,6 +608,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
WLAN_CIPHER_SUITE_AES_CMAC
};

+ if (hw->max_report_rates == 0)
+ hw->max_report_rates = hw->max_rates;
+
/*
* generic code guarantees at least one band,
* set this very early because much code assumes
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 571b32b..855000d 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -175,7 +175,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)

for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
/* the HW cannot have attempted that rate */
- if (i >= hw->max_rates) {
+ if (i >= hw->max_report_rates) {
info->status.rates[i].idx = -1;
info->status.rates[i].count = 0;
} else if (info->status.rates[i].idx >= 0) {
--
1.7.2.3


2010-10-02 09:35:26

by Ivo Van Doorn

[permalink] [raw]
Subject: [PATCH 07/20] rt2x00: Fix race between dma mapping and clearing rx entries in rt2800pci

From: Helmut Schaa <[email protected]>

During rx, rt2x00lib calls rt2800pci_fill_rxdone to read the RX
descriptor. At that time the skb is already dma unmapped but no new skb
was dma mapped for this entry again. However, rt2800pci_fill_rxdone also
moves the hw rx queue index, marking this entry to be available for
reuse. Since no new skb was dma mapped and also the previous skb was
unmapped this might lead to strange hw behavior.

To fix this issue move the hw rx queue index increment to
rt2800pci_clear_entry where a new skb was already dma mapped and can be
safely used by the hw.

Signed-off-by: Helmut Schaa <[email protected]>
Signed-off-by: Ivo van Doorn <[email protected]>
---
drivers/net/wireless/rt2x00/rt2800pci.c | 15 ++++++++-------
1 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c
index 3806454..85a134c 100644
--- a/drivers/net/wireless/rt2x00/rt2800pci.c
+++ b/drivers/net/wireless/rt2x00/rt2800pci.c
@@ -241,6 +241,7 @@ static void rt2800pci_clear_entry(struct queue_entry *entry)
{
struct queue_entry_priv_pci *entry_priv = entry->priv_data;
struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
+ struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
u32 word;

if (entry->queue->qid == QID_RX) {
@@ -251,6 +252,13 @@ static void rt2800pci_clear_entry(struct queue_entry *entry)
rt2x00_desc_read(entry_priv->desc, 1, &word);
rt2x00_set_field32(&word, RXD_W1_DMA_DONE, 0);
rt2x00_desc_write(entry_priv->desc, 1, word);
+
+ /*
+ * Set RX IDX in register to inform hardware that we have
+ * handled this entry and it is available for reuse again.
+ */
+ rt2800_register_write(rt2x00dev, RX_CRX_IDX,
+ entry->entry_idx);
} else {
rt2x00_desc_read(entry_priv->desc, 1, &word);
rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 1);
@@ -599,7 +607,6 @@ static void rt2800pci_kill_tx_queue(struct data_queue *queue)
static void rt2800pci_fill_rxdone(struct queue_entry *entry,
struct rxdone_entry_desc *rxdesc)
{
- struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
struct queue_entry_priv_pci *entry_priv = entry->priv_data;
__le32 *rxd = entry_priv->desc;
u32 word;
@@ -641,12 +648,6 @@ static void rt2800pci_fill_rxdone(struct queue_entry *entry,
* Process the RXWI structure that is at the start of the buffer.
*/
rt2800_process_rxwi(entry, rxdesc);
-
- /*
- * Set RX IDX in register to inform hardware that we have handled
- * this entry and it is available for reuse again.
- */
- rt2800_register_write(rt2x00dev, RX_CRX_IDX, entry->entry_idx);
}

/*
--
1.7.2.3


2010-10-02 09:35:28

by Ivo Van Doorn

[permalink] [raw]
Subject: [PATCH 09/20] rt2x00: Add rt73usb device ID


Signed-off-by: Ivo van Doorn <[email protected]>
---
drivers/net/wireless/rt2x00/rt73usb.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c
index cc6a72b..f78bc2a 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/rt2x00/rt73usb.c
@@ -2365,6 +2365,7 @@ static struct usb_device_id rt73usb_device_table[] = {
{ USB_DEVICE(0x0411, 0x00f4), USB_DEVICE_DATA(&rt73usb_ops) },
{ USB_DEVICE(0x0411, 0x0116), USB_DEVICE_DATA(&rt73usb_ops) },
{ USB_DEVICE(0x0411, 0x0119), USB_DEVICE_DATA(&rt73usb_ops) },
+ { USB_DEVICE(0x0411, 0x0137), USB_DEVICE_DATA(&rt73usb_ops) },
/* CEIVA */
{ USB_DEVICE(0x178d, 0x02be), USB_DEVICE_DATA(&rt73usb_ops) },
/* CNet */
--
1.7.2.3


2010-10-02 09:35:23

by Ivo Van Doorn

[permalink] [raw]
Subject: [PATCH 05/20] rt2x00: Implement HT protection for rt2800

From: Helmut Schaa <[email protected]>

Update the HT operation mode when mac80211 sends it to us and set
the different HT protection modes and rates accordingly. For now
only use CTS-to-self with OFDM 24M or CCK 11M when protection is
required.

Signed-off-by: Helmut Schaa <[email protected]>
Signed-off-by: Ivo van Doorn <[email protected]>
---
drivers/net/wireless/rt2x00/rt2800lib.c | 99 ++++++++++++++++++++++++++++
drivers/net/wireless/rt2x00/rt2x00.h | 1 +
drivers/net/wireless/rt2x00/rt2x00config.c | 3 +
drivers/net/wireless/rt2x00/rt2x00mac.c | 2 +-
4 files changed, 104 insertions(+), 1 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 19534f2..4ecacea 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -1169,6 +1169,102 @@ void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf,
}
EXPORT_SYMBOL_GPL(rt2800_config_intf);

+static void rt2800_config_ht_opmode(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00lib_erp *erp)
+{
+ bool any_sta_nongf = !!(erp->ht_opmode &
+ IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
+ u8 protection = erp->ht_opmode & IEEE80211_HT_OP_MODE_PROTECTION;
+ u8 mm20_mode, mm40_mode, gf20_mode, gf40_mode;
+ u16 mm20_rate, mm40_rate, gf20_rate, gf40_rate;
+ u32 reg;
+
+ /* default protection rate for HT20: OFDM 24M */
+ mm20_rate = gf20_rate = 0x4004;
+
+ /* default protection rate for HT40: duplicate OFDM 24M */
+ mm40_rate = gf40_rate = 0x4084;
+
+ switch (protection) {
+ case IEEE80211_HT_OP_MODE_PROTECTION_NONE:
+ /*
+ * All STAs in this BSS are HT20/40 but there might be
+ * STAs not supporting greenfield mode.
+ * => Disable protection for HT transmissions.
+ */
+ mm20_mode = mm40_mode = gf20_mode = gf40_mode = 0;
+
+ break;
+ case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ:
+ /*
+ * All STAs in this BSS are HT20 or HT20/40 but there
+ * might be STAs not supporting greenfield mode.
+ * => Protect all HT40 transmissions.
+ */
+ mm20_mode = gf20_mode = 0;
+ mm40_mode = gf40_mode = 2;
+
+ break;
+ case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER:
+ /*
+ * Nonmember protection:
+ * According to 802.11n we _should_ protect all
+ * HT transmissions (but we don't have to).
+ *
+ * But if cts_protection is enabled we _shall_ protect
+ * all HT transmissions using a CCK rate.
+ *
+ * And if any station is non GF we _shall_ protect
+ * GF transmissions.
+ *
+ * We decide to protect everything
+ * -> fall through to mixed mode.
+ */
+ case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED:
+ /*
+ * Legacy STAs are present
+ * => Protect all HT transmissions.
+ */
+ mm20_mode = mm40_mode = gf20_mode = gf40_mode = 2;
+
+ /*
+ * If erp protection is needed we have to protect HT
+ * transmissions with CCK 11M long preamble.
+ */
+ if (erp->cts_protection) {
+ /* don't duplicate RTS/CTS in CCK mode */
+ mm20_rate = mm40_rate = 0x0003;
+ gf20_rate = gf40_rate = 0x0003;
+ }
+ break;
+ };
+
+ /* check for STAs not supporting greenfield mode */
+ if (any_sta_nongf)
+ gf20_mode = gf40_mode = 2;
+
+ /* Update HT protection config */
+ rt2800_register_read(rt2x00dev, MM20_PROT_CFG, &reg);
+ rt2x00_set_field32(&reg, MM20_PROT_CFG_PROTECT_RATE, mm20_rate);
+ rt2x00_set_field32(&reg, MM20_PROT_CFG_PROTECT_CTRL, mm20_mode);
+ rt2800_register_write(rt2x00dev, MM20_PROT_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, MM40_PROT_CFG, &reg);
+ rt2x00_set_field32(&reg, MM40_PROT_CFG_PROTECT_RATE, mm40_rate);
+ rt2x00_set_field32(&reg, MM40_PROT_CFG_PROTECT_CTRL, mm40_mode);
+ rt2800_register_write(rt2x00dev, MM40_PROT_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, GF20_PROT_CFG, &reg);
+ rt2x00_set_field32(&reg, GF20_PROT_CFG_PROTECT_RATE, gf20_rate);
+ rt2x00_set_field32(&reg, GF20_PROT_CFG_PROTECT_CTRL, gf20_mode);
+ rt2800_register_write(rt2x00dev, GF20_PROT_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, GF40_PROT_CFG, &reg);
+ rt2x00_set_field32(&reg, GF40_PROT_CFG_PROTECT_RATE, gf40_rate);
+ rt2x00_set_field32(&reg, GF40_PROT_CFG_PROTECT_CTRL, gf40_mode);
+ rt2800_register_write(rt2x00dev, GF40_PROT_CFG, reg);
+}
+
void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp,
u32 changed)
{
@@ -1213,6 +1309,9 @@ void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp,
erp->beacon_int * 16);
rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
}
+
+ if (changed & BSS_CHANGED_HT)
+ rt2800_config_ht_opmode(rt2x00dev, erp);
}
EXPORT_SYMBOL_GPL(rt2800_config_erp);

diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index bab10ad..75ac662 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -458,6 +458,7 @@ struct rt2x00lib_erp {
short eifs;

u16 beacon_int;
+ u16 ht_opmode;
};

/*
diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/rt2x00/rt2x00config.c
index 4c7ff76..54ffb5a 100644
--- a/drivers/net/wireless/rt2x00/rt2x00config.c
+++ b/drivers/net/wireless/rt2x00/rt2x00config.c
@@ -103,6 +103,9 @@ void rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev,
/* Update global beacon interval time, this is needed for PS support */
rt2x00dev->beacon_int = bss_conf->beacon_int;

+ if (changed & BSS_CHANGED_HT)
+ erp.ht_opmode = bss_conf->ht_operation_mode;
+
rt2x00dev->ops->lib->config_erp(rt2x00dev, &erp, changed);
}

diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c
index 7862a84..c3c206a 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
@@ -671,7 +671,7 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
*/
if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_ERP_PREAMBLE |
BSS_CHANGED_ERP_SLOT | BSS_CHANGED_BASIC_RATES |
- BSS_CHANGED_BEACON_INT))
+ BSS_CHANGED_BEACON_INT | BSS_CHANGED_HT))
rt2x00lib_config_erp(rt2x00dev, intf, bss_conf, changes);
}
EXPORT_SYMBOL_GPL(rt2x00mac_bss_info_changed);
--
1.7.2.3


2010-10-02 09:35:31

by Ivo Van Doorn

[permalink] [raw]
Subject: [PATCH 11/20] rt2x00: add field definitions for the TBTT_SYNC_CFG register

From: Helmut Schaa <[email protected]>

Signed-off-by: Helmut Schaa <[email protected]>
Signed-off-by: Ivo van Doorn <[email protected]>
---
drivers/net/wireless/rt2x00/rt2800.h | 6 ++++++
1 files changed, 6 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h
index f02ea12..4930d29 100644
--- a/drivers/net/wireless/rt2x00/rt2800.h
+++ b/drivers/net/wireless/rt2x00/rt2800.h
@@ -710,8 +710,14 @@

/*
* TBTT_SYNC_CFG:
+ * BCN_AIFSN: Beacon AIFSN after TBTT interrupt in slots
+ * BCN_CWMIN: Beacon CWMin after TBTT interrupt in slots
*/
#define TBTT_SYNC_CFG 0x1118
+#define TBTT_SYNC_CFG_TBTT_ADJUST FIELD32(0x000000ff)
+#define TBTT_SYNC_CFG_BCN_EXP_WIN FIELD32(0x0000ff00)
+#define TBTT_SYNC_CFG_BCN_AIFSN FIELD32(0x000f0000)
+#define TBTT_SYNC_CFG_BCN_CWMIN FIELD32(0x00f00000)

/*
* TSF_TIMER_DW0: Local lsb TSF timer, read-only
--
1.7.2.3


2010-10-02 09:35:38

by Ivo Van Doorn

[permalink] [raw]
Subject: [PATCH 15/20] rt2x00: Improve TX status entry validation

From: Ivo van Doorn <[email protected]>

The TX_STA_FIFO contains some information for identifying
a outgoing frame, however matching by WCID and ACK status is
not sufficient to 100% identify the macthing queue_entry structure
(containing the SKB buffer) which belongs to the status report.

Within TX_STA_FIFO we have a 4-bit field named PACKETID, which is
currently used to encode the queue id. The queue ID is however
limited to values from 0 to 3, which means 2 bits are sufficient
to encode the value. With the remaining 2 bits we can encode a
partial queue_entry index number. The value of PACKETID is not
allowed to become 0, with the queue ID ranging from 0 to 3, at least
one of the bits for the entry identification must be 1.

That leaves us with 3 possible values we can still encode in the
bits. Altough this doesn't allow 100% accurate matching of the
TX_STA_FIFO queue to a queue_entry structure, it at least improves
the accuracy. This allows us to better detect if we have missed the
TX_STA_FIFO report, which in turn reduces the number of watchdog
warnings regarding the TX status timeout.

Signed-off-by: Ivo van Doorn <[email protected]>
---
drivers/net/wireless/rt2x00/rt2800.h | 14 +++++++++++++-
drivers/net/wireless/rt2x00/rt2800lib.c | 5 +++--
2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h
index 4930d29..2a9f402 100644
--- a/drivers/net/wireless/rt2x00/rt2800.h
+++ b/drivers/net/wireless/rt2x00/rt2800.h
@@ -1,5 +1,6 @@
/*
- Copyright (C) 2004 - 2009 Ivo van Doorn <[email protected]>
+ Copyright (C) 2004 - 2010 Ivo van Doorn <[email protected]>
+ Copyright (C) 2010 Willow Garage <http://www.willowgarage.com>
Copyright (C) 2009 Alban Browaeys <[email protected]>
Copyright (C) 2009 Felix Fietkau <[email protected]>
Copyright (C) 2009 Luis Correia <[email protected]>
@@ -1353,6 +1354,9 @@
* PID_TYPE: The PID latched from the PID field in the TXWI, can be used
* to match a frame with its tx result (even though the PID is
* only 4 bits wide).
+ * PID_QUEUE: Part of PID_TYPE, this is the queue index number (0-3)
+ * PID_ENTRY: Part of PID_TYPE, this is the queue entry index number (1-3)
+ * This identification number is calculated by ((idx % 3) + 1).
* TX_SUCCESS: Indicates tx success (1) or failure (0)
* TX_AGGRE: Indicates if the frame was part of an aggregate (1) or not (0)
* TX_ACK_REQUIRED: Indicates if the frame needed to get ack'ed (1) or not (0)
@@ -1364,6 +1368,8 @@
#define TX_STA_FIFO 0x1718
#define TX_STA_FIFO_VALID FIELD32(0x00000001)
#define TX_STA_FIFO_PID_TYPE FIELD32(0x0000001e)
+#define TX_STA_FIFO_PID_QUEUE FIELD32(0x00000006)
+#define TX_STA_FIFO_PID_ENTRY FIELD32(0x00000018)
#define TX_STA_FIFO_TX_SUCCESS FIELD32(0x00000020)
#define TX_STA_FIFO_TX_AGGRE FIELD32(0x00000040)
#define TX_STA_FIFO_TX_ACK_REQUIRED FIELD32(0x00000080)
@@ -2024,6 +2030,10 @@ struct mac_iveiv_entry {
* frame was processed. If multiple frames are aggregated together
* (AMPDU==1) the reported tx status will always contain the packet
* id of the first frame. 0: Don't report tx status for this frame.
+ * PACKETID_QUEUE: Part of PACKETID, This is the queue index (0-3)
+ * PACKETID_ENTRY: Part of PACKETID, THis is the queue entry index (1-3)
+ * This identification number is calculated by ((idx % 3) + 1).
+ * The (+1) is required to prevent PACKETID to become 0.
*/
#define TXWI_W1_ACK FIELD32(0x00000001)
#define TXWI_W1_NSEQ FIELD32(0x00000002)
@@ -2031,6 +2041,8 @@ struct mac_iveiv_entry {
#define TXWI_W1_WIRELESS_CLI_ID FIELD32(0x0000ff00)
#define TXWI_W1_MPDU_TOTAL_BYTE_COUNT FIELD32(0x0fff0000)
#define TXWI_W1_PACKETID FIELD32(0xf0000000)
+#define TXWI_W1_PACKETID_QUEUE FIELD32(0x30000000)
+#define TXWI_W1_PACKETID_ENTRY FIELD32(0xc0000000)

/*
* Word2
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 403e2ed..8e507d0 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -483,7 +483,8 @@ void rt2800_write_tx_data(struct queue_entry *entry,
txdesc->key_idx : 0xff);
rt2x00_set_field32(&word, TXWI_W1_MPDU_TOTAL_BYTE_COUNT,
txdesc->length);
- rt2x00_set_field32(&word, TXWI_W1_PACKETID, txdesc->qid + 1);
+ rt2x00_set_field32(&word, TXWI_W1_PACKETID_QUEUE, txdesc->qid);
+ rt2x00_set_field32(&word, TXWI_W1_PACKETID_ENTRY, (entry->entry_idx % 3) + 1);
rt2x00_desc_write(txwi, 1, word);

/*
@@ -708,7 +709,7 @@ void rt2800_txdone(struct rt2x00_dev *rt2x00dev)
* Skip this entry when it contains an invalid
* queue identication number.
*/
- pid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE) - 1;
+ pid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_QUEUE);
if (pid >= QID_RX)
continue;

--
1.7.2.3


2010-10-02 09:35:43

by Ivo Van Doorn

[permalink] [raw]
Subject: [PATCH 18/20] rt2x00: Fix oops caused by error path in rt2x00lib_start

From: Helmut Schaa <[email protected]>

When rt2x00lib_enable_radio fails to enable the radio, rt2x00lib_start
will call rt2x00queue_uninitialize to uninitialize the queues. Since,
the queues are not initialized here but already in rt2x00lib_initialize
we shouldn't uninitialize the queues here. Otherwise, a consecutive call
to rt2x00lib_start will oops (see below) because it assumes the queues
are already initialized.

BUG: unable to handle kernel NULL pointer dereference at 00000010
IP: [<f8d2d901>] :rt2800pci:rt2800pci_clear_entry+0x1/0x40
*pde = 00000000
Oops: 0000 [#1] SMP
Modules linked in: ... rt2800pci ...

Pid: 5995, comm: hostapd Not tainted (2.6.27.8 #1)
EIP: 0060:[<f8d2d901>] EFLAGS: 00210246 CPU: 3
EIP is at rt2800pci_clear_entry+0x1/0x40 [rt2800pci]
EAX: 00000000 EBX: f698863c ECX: 00200296 EDX: f8d2dee0
ESI: f6988600 EDI: f5b6f000 EBP: 00000000 ESP: f6d75e4c
DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068
Process hostapd (pid: 5995, ti=f6d74000 task=f6ce2300 task.ti=f6d74000)
Stack: f698863c fa00eaec 00000000 f5b6f000 00000000 f7b67000 f5b6e280 fa00c629
f5b6f000 00000000 fa00ca3d f7b67480 00000001 fa177d4c 01b6e890 f7b67000
00000000 f7b67000 00000001 00001003 00001002 c066c366 f7b67000 c0668ad0
Call Trace:
[<fa00eaec>] rt2x00queue_init_queues+0x5c/0x90 [rt2x00lib]
[<fa00c629>] rt2x00lib_enable_radio+0x29/0xa0 [rt2x00lib]
[<fa00ca3d>] rt2x00lib_start+0x5d/0xd0 [rt2x00lib]
[<fa177d4c>] ieee80211_do_open+0x21c/0x510 [mac80211]
[<c066c366>] dev_open+0x56/0xb0
[<c0668ad0>] dev_set_rx_mode+0x20/0x40
[<c066a67f>] dev_change_flags+0x7f/0x190
[<c06b1495>] devinet_ioctl+0x515/0x690
[<c0668d24>] __dev_get_by_name+0x74/0x90
[<c065d3f0>] sock_ioctl+0xd0/0x240
[<c065d320>] sock_ioctl+0x0/0x240
[<c018179b>] vfs_ioctl+0x2b/0x90
[<c0181a5b>] do_vfs_ioctl+0x25b/0x2a0
[<c0181af6>] sys_ioctl+0x56/0x70
[<c0103262>] syscall_call+0x7/0xb
[<c0700000>] add_card+0xad0/0xba0
=======================
Code: 83 78 08 0e 74 14 8b 02 8b 48 04 85 c9 0f 99 c0 0f b6 c0 c3 8d b6
00 00 00 00 8b 02 8b 40 04 85 c0 0f 99 c0 0f b6 c0 c3 66 90 53 <8b>
48 10 8b 58 08 8b 40 04 83 78 08 0e 74 15 8b 11 83 c2 04 8b
EIP: [<f8d2d901>] rt2800pci_clear_entry+0x1/0x40 [rt2800pci] SS:ESP 0068:f6d75e4c
---[ end trace cff9a5c094bb8837 ]---

Reported-by: Joshua Smith <[email protected]>
Signed-off-by: Helmut Schaa <[email protected]>
Signed-off-by: Ivo van Doorn <[email protected]>
---
drivers/net/wireless/rt2x00/rt2x00dev.c | 4 +---
1 files changed, 1 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index b03e6e4..6f442b0 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -933,10 +933,8 @@ int rt2x00lib_start(struct rt2x00_dev *rt2x00dev)

/* Enable the radio */
retval = rt2x00lib_enable_radio(rt2x00dev);
- if (retval) {
- rt2x00queue_uninitialize(rt2x00dev);
+ if (retval)
return retval;
- }

set_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags);

--
1.7.2.3


2010-10-02 09:35:27

by Ivo Van Doorn

[permalink] [raw]
Subject: [PATCH 08/20] rt2x00: Allow tx duplication for legacy rates in HT40 mode

From: Helmut Schaa <[email protected]>

Make use of the IEEE80211_TX_RC_DUP_DATA flag to duplicate a
transmission with legacy rates to both 20Mhz channels if set.
Also update the related comment in rt2800.h to describe the
behavior of the BW_40 flag for legacy rates.

Signed-off-by: Helmut Schaa <[email protected]>
Signed-off-by: Ivo van Doorn <[email protected]>
---
drivers/net/wireless/rt2x00/rt2800.h | 3 ++-
drivers/net/wireless/rt2x00/rt2x00ht.c | 8 +++++++-
2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h
index e1583cf..9bf50a1 100644
--- a/drivers/net/wireless/rt2x00/rt2800.h
+++ b/drivers/net/wireless/rt2x00/rt2800.h
@@ -1982,7 +1982,8 @@ struct mac_iveiv_entry {
* FRAG: 1 To inform TKIP engine this is a fragment.
* MIMO_PS: The remote peer is in dynamic MIMO-PS mode
* TX_OP: 0:HT TXOP rule , 1:PIFS TX ,2:Backoff, 3:sifs
- * BW: Channel bandwidth 20MHz or 40 MHz
+ * BW: Channel bandwidth 0:20MHz, 1:40 MHz (for legacy rates this will
+ * duplicate the frame to both channels).
* STBC: 1: STBC support MCS =0-7, 2,3 : RESERVED
* AMPDU: 1: this frame is eligible for AMPDU aggregation, the hw will
* aggregate consecutive frames with the same RA and QoS TID.
diff --git a/drivers/net/wireless/rt2x00/rt2x00ht.c b/drivers/net/wireless/rt2x00/rt2x00ht.c
index 48b6ed4..2023419 100644
--- a/drivers/net/wireless/rt2x00/rt2x00ht.c
+++ b/drivers/net/wireless/rt2x00/rt2x00ht.c
@@ -85,7 +85,13 @@ void rt2x00ht_create_tx_descriptor(struct queue_entry *entry,
txdesc->rate_mode = RATE_MODE_HT_MIX;
if (txrate->flags & IEEE80211_TX_RC_GREEN_FIELD)
txdesc->rate_mode = RATE_MODE_HT_GREENFIELD;
- if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+
+ /*
+ * Set 40Mhz mode if necessary (for legacy rates this will
+ * duplicate the frame to both channels).
+ */
+ if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH ||
+ txrate->flags & IEEE80211_TX_RC_DUP_DATA)
__set_bit(ENTRY_TXD_HT_BW_40, &txdesc->flags);
if (txrate->flags & IEEE80211_TX_RC_SHORT_GI)
__set_bit(ENTRY_TXD_HT_SHORT_GI, &txdesc->flags);
--
1.7.2.3


2010-10-02 09:35:42

by Ivo Van Doorn

[permalink] [raw]
Subject: [PATCH 17/20] rt2x00: Update comment about the AMPDU flag in the TXWI

From: Helmut Schaa <[email protected]>

During testing with AMPDUs it turned out that the rt2800 hw will aggregate
consecutive frames with the same RA and TID when the first frame in a
possible aggregate has set AMPDU=1 in the TXWI. If a following frame has
set AMPDU=0 in its TXWI it might sill end up in the aggregate of the
previous frame. Update the comment accordingly.

Signed-off-by: Helmut Schaa <[email protected]>
Signed-off-by: Ivo van Doorn <[email protected]>
---
drivers/net/wireless/rt2x00/rt2800.h | 8 +++++++-
1 files changed, 7 insertions(+), 1 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h
index 2a9f402..eb8b6ca 100644
--- a/drivers/net/wireless/rt2x00/rt2800.h
+++ b/drivers/net/wireless/rt2x00/rt2800.h
@@ -2003,7 +2003,13 @@ struct mac_iveiv_entry {
* duplicate the frame to both channels).
* STBC: 1: STBC support MCS =0-7, 2,3 : RESERVED
* AMPDU: 1: this frame is eligible for AMPDU aggregation, the hw will
- * aggregate consecutive frames with the same RA and QoS TID.
+ * aggregate consecutive frames with the same RA and QoS TID. If
+ * a frame A with the same RA and QoS TID but AMPDU=0 is queued
+ * directly after a frame B with AMPDU=1, frame A might still
+ * get aggregated into the AMPDU started by frame B. So, setting
+ * AMPDU to 0 does _not_ necessarily mean the frame is sent as
+ * MPDU, it can still end up in an AMPDU if the previous frame
+ * was tagged as AMPDU.
*/
#define TXWI_W0_FRAG FIELD32(0x00000001)
#define TXWI_W0_MIMO_PS FIELD32(0x00000002)
--
1.7.2.3


2010-10-02 09:35:24

by Ivo Van Doorn

[permalink] [raw]
Subject: [PATCH 04/20] rt2x00: Fix SM PS check

From: Helmut Schaa <[email protected]>

Fix a check for dynamic SM PS mode in the STAs HT caps. Since a
value of 3 means "SM PS disabled" the previous check assumed in
that case that "dynamic SM PS" was enabled and as such prefixed
every MCS>7 frame with a unnecessary RTS/CTS exchange. Also,
the bit shift was done in the wrong direction.

Signed-off-by: Helmut Schaa <[email protected]>
Signed-off-by: Ivo van Doorn <[email protected]>
---
drivers/net/wireless/rt2x00/rt2x00ht.c | 7 ++++---
1 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2x00ht.c b/drivers/net/wireless/rt2x00/rt2x00ht.c
index ad3c7ff..48b6ed4 100644
--- a/drivers/net/wireless/rt2x00/rt2x00ht.c
+++ b/drivers/net/wireless/rt2x00/rt2x00ht.c
@@ -60,9 +60,10 @@ void rt2x00ht_create_tx_descriptor(struct queue_entry *entry,
* when using more then one tx stream (>MCS7).
*/
if (tx_info->control.sta && txdesc->mcs > 7 &&
- (tx_info->control.sta->ht_cap.cap &
- (WLAN_HT_CAP_SM_PS_DYNAMIC <<
- IEEE80211_HT_CAP_SM_PS_SHIFT)))
+ ((tx_info->control.sta->ht_cap.cap &
+ IEEE80211_HT_CAP_SM_PS) >>
+ IEEE80211_HT_CAP_SM_PS_SHIFT) ==
+ WLAN_HT_CAP_SM_PS_DYNAMIC)
__set_bit(ENTRY_TXD_HT_MIMO_PS, &txdesc->flags);
} else {
txdesc->mcs = rt2x00_get_rate_mcs(hwrate->mcs);
--
1.7.2.3


2010-10-02 09:46:24

by Walter Goldens

[permalink] [raw]
Subject: Re: [PATCH 15/20] rt2x00: Improve TX status entry validation

Do all the latest patches fix the packet loss and general connectivity issues caused by the DMA in rt2800usb devices?




2010-10-02 09:35:45

by Ivo Van Doorn

[permalink] [raw]
Subject: [PATCH 19/20] rt2x00: Improve cooperation between rt2800pci and minstrel

From: Helmut Schaa <[email protected]>

In order to lower the impact of probe rates don't send a frame as AMPDU
if the rate control algorithm sets IEEE80211_TX_CTL_RATE_CTRL_PROBE.
Otherwise a whole aggregate would be send with a probe rate which might
lead to numerous retries.

Signed-off-by: Helmut Schaa <[email protected]>
Signed-off-by: Ivo van Doorn <[email protected]>
---
drivers/net/wireless/rt2x00/rt2x00ht.c | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2x00ht.c b/drivers/net/wireless/rt2x00/rt2x00ht.c
index 2023419..c637bca 100644
--- a/drivers/net/wireless/rt2x00/rt2x00ht.c
+++ b/drivers/net/wireless/rt2x00/rt2x00ht.c
@@ -73,9 +73,11 @@ void rt2x00ht_create_tx_descriptor(struct queue_entry *entry,


/*
- * Convert flags
+ * This frame is eligible for an AMPDU, however, don't aggregate
+ * frames that are intended to probe a specific tx rate.
*/
- if (tx_info->flags & IEEE80211_TX_CTL_AMPDU)
+ if (tx_info->flags & IEEE80211_TX_CTL_AMPDU &&
+ !(tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE))
__set_bit(ENTRY_TXD_HT_AMPDU, &txdesc->flags);

/*
--
1.7.2.3


2010-10-02 09:35:30

by Ivo Van Doorn

[permalink] [raw]
Subject: [PATCH 10/20] rt2x00: Add register definition for busy time on secondary channel

From: Helmut Schaa <[email protected]>

Add the register definition CH_BUSY_STA_SEC for reading the busy time
on the secondary channel in HT40 mode. Also update the comments about
channel busy/idle time registers to express the used unit.

Signed-off-by: Helmut Schaa <[email protected]>
Signed-off-by: Ivo van Doorn <[email protected]>
---
drivers/net/wireless/rt2x00/rt2800.h | 9 +++++++--
1 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h
index 9bf50a1..f02ea12 100644
--- a/drivers/net/wireless/rt2x00/rt2800.h
+++ b/drivers/net/wireless/rt2x00/rt2800.h
@@ -747,16 +747,21 @@
#define INT_TIMER_EN_GP_TIMER FIELD32(0x00000002)

/*
- * CH_IDLE_STA: channel idle time
+ * CH_IDLE_STA: channel idle time (in us)
*/
#define CH_IDLE_STA 0x1130

/*
- * CH_BUSY_STA: channel busy time
+ * CH_BUSY_STA: channel busy time on primary channel (in us)
*/
#define CH_BUSY_STA 0x1134

/*
+ * CH_BUSY_STA_SEC: channel busy time on secondary channel in HT40 mode (in us)
+ */
+#define CH_BUSY_STA_SEC 0x1138
+
+/*
* MAC_STATUS_CFG:
* BBP_RF_BUSY: When set to 0, BBP and RF are stable.
* if 1 or higher one of the 2 registers is busy.
--
1.7.2.3


2010-10-02 09:35:19

by Ivo Van Doorn

[permalink] [raw]
Subject: [PATCH 03/20] rt2x00: rework tx status handling in rt2800pci

From: Helmut Schaa <[email protected]>

This patch changes the way tx status reports are handled by rt2800pci.
Previously rt2800pci would sometimes lose tx status reports as the
TX_STA_FIFO register is a fifo of 16 entries that can overflow in case
we don't read it often/fast enough. Since interrupts are disabled in the
device during the execution of the interrupt thread it happend sometimes
under high network and CPU load that processing took too long and a few
tx status reports were dropped by the hw.

To fix this issue the TX_STA_FIFO register is read directly in the
interrupt handler and stored in a kfifo which is large enough to hold
all status reports of all used tx queues.

To process the status reports a new tasklet txstatus_tasklet is used.
Using the already used interrupt thread is not possible since we don't
want to disable the TX_FIFO_STATUS interrupt while processing them and
it is not possible to schedule the interrupt thread multiple times for
execution. A tasklet instead can be scheduled multiple times which
allows to leave the TX_FIFO_STATUS interrupt enabled while a previously
scheduled tasklet is still executing.

In short: All other interrupts are handled in the interrupt thread as
before. Only the TX_FIFO_STATUS interrupt is partly handled in the
interrupt handler and finished in the according tasklet.

One drawback of this patch is that it duplicates some code from
rt2800lib. However, that can be cleaned up in the future once the
rt2800usb and rt2800pci tx status handling converge more.

Using this patch on a Ralink RT3052 embedded board gives me a reliable
wireless connection even under high CPU and network load. I've
transferred several gigabytes without any queue lockups.

Signed-off-by: Helmut Schaa <[email protected]>
Signed-off-by: Ivo van Doorn <[email protected]>
---
drivers/net/wireless/rt2x00/rt2800pci.c | 150 ++++++++++++++++++++++++++++---
drivers/net/wireless/rt2x00/rt2x00.h | 17 ++++
drivers/net/wireless/rt2x00/rt2x00dev.c | 34 +++++++
3 files changed, 188 insertions(+), 13 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c
index 005ee15..3806454 100644
--- a/drivers/net/wireless/rt2x00/rt2800pci.c
+++ b/drivers/net/wireless/rt2x00/rt2800pci.c
@@ -660,6 +660,63 @@ static void rt2800pci_wakeup(struct rt2x00_dev *rt2x00dev)
rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS);
}

+static void rt2800pci_txdone(struct rt2x00_dev *rt2x00dev)
+{
+ struct data_queue *queue;
+ struct queue_entry *entry;
+ u32 status;
+ u8 qid;
+
+ while (!kfifo_is_empty(&rt2x00dev->txstatus_fifo)) {
+ /* Now remove the tx status from the FIFO */
+ if (kfifo_out(&rt2x00dev->txstatus_fifo, &status,
+ sizeof(status)) != sizeof(status)) {
+ WARN_ON(1);
+ break;
+ }
+
+ qid = rt2x00_get_field32(status, TX_STA_FIFO_PID_TYPE) - 1;
+ if (qid >= QID_RX) {
+ /*
+ * Unknown queue, this shouldn't happen. Just drop
+ * this tx status.
+ */
+ WARNING(rt2x00dev, "Got TX status report with "
+ "unexpected pid %u, dropping", qid);
+ break;
+ }
+
+ queue = rt2x00queue_get_queue(rt2x00dev, qid);
+ if (unlikely(queue == NULL)) {
+ /*
+ * The queue is NULL, this shouldn't happen. Stop
+ * processing here and drop the tx status
+ */
+ WARNING(rt2x00dev, "Got TX status for an unavailable "
+ "queue %u, dropping", qid);
+ break;
+ }
+
+ if (rt2x00queue_empty(queue)) {
+ /*
+ * The queue is empty. Stop processing here
+ * and drop the tx status.
+ */
+ WARNING(rt2x00dev, "Got TX status for an empty "
+ "queue %u, dropping", qid);
+ break;
+ }
+
+ entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
+ rt2800_txdone_entry(entry, status);
+ }
+}
+
+static void rt2800pci_txstatus_tasklet(unsigned long data)
+{
+ rt2800pci_txdone((struct rt2x00_dev *)data);
+}
+
static irqreturn_t rt2800pci_interrupt_thread(int irq, void *dev_instance)
{
struct rt2x00_dev *rt2x00dev = dev_instance;
@@ -684,13 +741,7 @@ static irqreturn_t rt2800pci_interrupt_thread(int irq, void *dev_instance)
rt2x00pci_rxdone(rt2x00dev);

/*
- * 4 - Tx done interrupt.
- */
- if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS))
- rt2800_txdone(rt2x00dev);
-
- /*
- * 5 - Auto wakeup interrupt.
+ * 4 - Auto wakeup interrupt.
*/
if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP))
rt2800pci_wakeup(rt2x00dev);
@@ -702,10 +753,58 @@ static irqreturn_t rt2800pci_interrupt_thread(int irq, void *dev_instance)
return IRQ_HANDLED;
}

+static void rt2800pci_txstatus_interrupt(struct rt2x00_dev *rt2x00dev)
+{
+ u32 status;
+ int i;
+
+ /*
+ * The TX_FIFO_STATUS interrupt needs special care. We should
+ * read TX_STA_FIFO but we should do it immediately as otherwise
+ * the register can overflow and we would lose status reports.
+ *
+ * Hence, read the TX_STA_FIFO register and copy all tx status
+ * reports into a kernel FIFO which is handled in the txstatus
+ * tasklet. We use a tasklet to process the tx status reports
+ * because we can schedule the tasklet multiple times (when the
+ * interrupt fires again during tx status processing).
+ *
+ * Furthermore we don't disable the TX_FIFO_STATUS
+ * interrupt here but leave it enabled so that the TX_STA_FIFO
+ * can also be read while the interrupt thread gets executed.
+ *
+ * Since we have only one producer and one consumer we don't
+ * need to lock the kfifo.
+ */
+ for (i = 0; i < TX_ENTRIES; i++) {
+ rt2800_register_read(rt2x00dev, TX_STA_FIFO, &status);
+
+ if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID))
+ break;
+
+ if (kfifo_is_full(&rt2x00dev->txstatus_fifo)) {
+ WARNING(rt2x00dev, "TX status FIFO overrun,"
+ " drop tx status report.\n");
+ break;
+ }
+
+ if (kfifo_in(&rt2x00dev->txstatus_fifo, &status,
+ sizeof(status)) != sizeof(status)) {
+ WARNING(rt2x00dev, "TX status FIFO overrun,"
+ "drop tx status report.\n");
+ break;
+ }
+ }
+
+ /* Schedule the tasklet for processing the tx status. */
+ tasklet_schedule(&rt2x00dev->txstatus_tasklet);
+}
+
static irqreturn_t rt2800pci_interrupt(int irq, void *dev_instance)
{
struct rt2x00_dev *rt2x00dev = dev_instance;
u32 reg;
+ irqreturn_t ret = IRQ_HANDLED;

/* Read status and ACK all interrupts */
rt2800_register_read(rt2x00dev, INT_SOURCE_CSR, &reg);
@@ -717,15 +816,38 @@ static irqreturn_t rt2800pci_interrupt(int irq, void *dev_instance)
if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
return IRQ_HANDLED;

- /* Store irqvalue for use in the interrupt thread. */
- rt2x00dev->irqvalue[0] = reg;
+ if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS))
+ rt2800pci_txstatus_interrupt(rt2x00dev);
+
+ if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT) ||
+ rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT) ||
+ rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE) ||
+ rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP)) {
+ /*
+ * All other interrupts are handled in the interrupt thread.
+ * Store irqvalue for use in the interrupt thread.
+ */
+ rt2x00dev->irqvalue[0] = reg;

- /* Disable interrupts, will be enabled again in the interrupt thread. */
- rt2x00dev->ops->lib->set_device_state(rt2x00dev,
- STATE_RADIO_IRQ_OFF_ISR);
+ /*
+ * Disable interrupts, will be enabled again in the
+ * interrupt thread.
+ */
+ rt2x00dev->ops->lib->set_device_state(rt2x00dev,
+ STATE_RADIO_IRQ_OFF_ISR);

+ /*
+ * Leave the TX_FIFO_STATUS interrupt enabled to not lose any
+ * tx status reports.
+ */
+ rt2800_register_read(rt2x00dev, INT_MASK_CSR, &reg);
+ rt2x00_set_field32(&reg, INT_MASK_CSR_TX_FIFO_STATUS, 1);
+ rt2800_register_write(rt2x00dev, INT_MASK_CSR, reg);

- return IRQ_WAKE_THREAD;
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ return ret;
}

/*
@@ -788,6 +910,7 @@ static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev)
__set_bit(DRIVER_REQUIRE_FIRMWARE, &rt2x00dev->flags);
__set_bit(DRIVER_REQUIRE_DMA, &rt2x00dev->flags);
__set_bit(DRIVER_REQUIRE_L2PAD, &rt2x00dev->flags);
+ __set_bit(DRIVER_REQUIRE_TXSTATUS_FIFO, &rt2x00dev->flags);
if (!modparam_nohwcrypt)
__set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags);
__set_bit(DRIVER_SUPPORT_LINK_TUNING, &rt2x00dev->flags);
@@ -837,6 +960,7 @@ static const struct rt2800_ops rt2800pci_rt2800_ops = {
static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
.irq_handler = rt2800pci_interrupt,
.irq_handler_thread = rt2800pci_interrupt_thread,
+ .txstatus_tasklet = rt2800pci_txstatus_tasklet,
.probe_hw = rt2800pci_probe_hw,
.get_firmware_name = rt2800pci_get_firmware_name,
.check_firmware = rt2800_check_firmware,
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index 7832a59..bab10ad 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -36,6 +36,7 @@
#include <linux/mutex.h>
#include <linux/etherdevice.h>
#include <linux/input-polldev.h>
+#include <linux/kfifo.h>

#include <net/mac80211.h>

@@ -522,6 +523,11 @@ struct rt2x00lib_ops {
irq_handler_t irq_handler_thread;

/*
+ * TX status tasklet handler.
+ */
+ void (*txstatus_tasklet) (unsigned long data);
+
+ /*
* Device init handlers.
*/
int (*probe_hw) (struct rt2x00_dev *rt2x00dev);
@@ -651,6 +657,7 @@ enum rt2x00_flags {
DRIVER_REQUIRE_DMA,
DRIVER_REQUIRE_COPY_IV,
DRIVER_REQUIRE_L2PAD,
+ DRIVER_REQUIRE_TXSTATUS_FIFO,

/*
* Driver features
@@ -884,6 +891,16 @@ struct rt2x00_dev {
* and interrupt thread routine.
*/
u32 irqvalue[2];
+
+ /*
+ * FIFO for storing tx status reports between isr and tasklet.
+ */
+ struct kfifo txstatus_fifo;
+
+ /*
+ * Tasklet for processing tx status reports (rt2800pci).
+ */
+ struct tasklet_struct txstatus_tasklet;
};

/*
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index 053fdd3..b03e6e4 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -813,6 +813,30 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev)
rt2x00dev->hw->extra_tx_headroom += RT2X00_ALIGN_SIZE;

/*
+ * Allocate tx status FIFO for driver use.
+ */
+ if (test_bit(DRIVER_REQUIRE_TXSTATUS_FIFO, &rt2x00dev->flags) &&
+ rt2x00dev->ops->lib->txstatus_tasklet) {
+ /*
+ * Allocate txstatus fifo and tasklet, we use a size of 512
+ * for the kfifo which is big enough to store 512/4=128 tx
+ * status reports. In the worst case (tx status for all tx
+ * queues gets reported before we've got a chance to handle
+ * them) 24*4=384 tx status reports need to be cached.
+ */
+ status = kfifo_alloc(&rt2x00dev->txstatus_fifo, 512,
+ GFP_KERNEL);
+ if (status)
+ return status;
+
+ /* tasklet for processing the tx status reports. */
+ tasklet_init(&rt2x00dev->txstatus_tasklet,
+ rt2x00dev->ops->lib->txstatus_tasklet,
+ (unsigned long)rt2x00dev);
+
+ }
+
+ /*
* Register HW.
*/
status = ieee80211_register_hw(rt2x00dev->hw);
@@ -1028,6 +1052,16 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev)
cancel_work_sync(&rt2x00dev->txdone_work);

/*
+ * Free the tx status fifo.
+ */
+ kfifo_free(&rt2x00dev->txstatus_fifo);
+
+ /*
+ * Kill the tx status tasklet.
+ */
+ tasklet_kill(&rt2x00dev->txstatus_tasklet);
+
+ /*
* Uninitialize device.
*/
rt2x00lib_uninitialize(rt2x00dev);
--
1.7.2.3