linux-wireless.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 01/10] rt2x00: Rename txentry_desc.queue -> txentry_desc.qid
@ 2010-08-30 19:12 Ivo van Doorn
  2010-08-30 19:12 ` [PATCH 02/10] rt2x00: Update rt2800 comments regarding AMPDU and PACKET_ID in TXWI Ivo van Doorn
  0 siblings, 1 reply; 10+ messages in thread
From: Ivo van Doorn @ 2010-08-30 19:12 UTC (permalink / raw)
  To: John W. Linville, linux-wireless, users

From: Helmut Schaa <helmut.schaa@googlemail.com>

Variables containing queue ids are called qid everywhere else, hence
rename the queue field in txentry_desc to qid as well.

Signed-off-by: Helmut Schaa <helmut.schaa@googlemail.com>
Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com>
---
 drivers/net/wireless/rt2x00/rt2800lib.c   |    2 +-
 drivers/net/wireless/rt2x00/rt2x00queue.c |    2 +-
 drivers/net/wireless/rt2x00/rt2x00queue.h |    4 ++--
 drivers/net/wireless/rt2x00/rt61pci.c     |    6 +++---
 drivers/net/wireless/rt2x00/rt73usb.c     |    2 +-
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 2fd326c..ac92e7c 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -469,7 +469,7 @@ 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->queue + 1);
+	rt2x00_set_field32(&word, TXWI_W1_PACKETID, txdesc->qid + 1);
 	rt2x00_desc_write(txwi, 1, word);
 
 	/*
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index a2bd176..7b25d66 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -312,7 +312,7 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry,
 	/*
 	 * Initialize information from queue
 	 */
-	txdesc->queue = entry->queue->qid;
+	txdesc->qid = entry->queue->qid;
 	txdesc->cw_min = entry->queue->cw_min;
 	txdesc->cw_max = entry->queue->cw_max;
 	txdesc->aifs = entry->queue->aifs;
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h
index c451a50..0e38a91 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.h
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.h
@@ -296,7 +296,7 @@ enum txentry_desc_flags {
  * Summary of information for the frame descriptor before sending a TX frame.
  *
  * @flags: Descriptor flags (See &enum queue_entry_flags).
- * @queue: Queue identification (See &enum data_queue_qid).
+ * @qid: Queue identification (See &enum data_queue_qid).
  * @length: Length of the entire frame.
  * @header_length: Length of 802.11 header.
  * @length_high: PLCP length high word.
@@ -322,7 +322,7 @@ enum txentry_desc_flags {
 struct txentry_desc {
 	unsigned long flags;
 
-	enum data_queue_qid queue;
+	enum data_queue_qid qid;
 
 	u16 length;
 	u16 header_length;
diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c
index 8b67cfd..3a77599 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.c
+++ b/drivers/net/wireless/rt2x00/rt61pci.c
@@ -1778,7 +1778,7 @@ static void rt61pci_write_tx_desc(struct queue_entry *entry,
 	 * Start writing the descriptor words.
 	 */
 	rt2x00_desc_read(txd, 1, &word);
-	rt2x00_set_field32(&word, TXD_W1_HOST_Q_ID, txdesc->queue);
+	rt2x00_set_field32(&word, TXD_W1_HOST_Q_ID, txdesc->qid);
 	rt2x00_set_field32(&word, TXD_W1_AIFSN, txdesc->aifs);
 	rt2x00_set_field32(&word, TXD_W1_CWMIN, txdesc->cw_min);
 	rt2x00_set_field32(&word, TXD_W1_CWMAX, txdesc->cw_max);
@@ -1809,7 +1809,7 @@ static void rt61pci_write_tx_desc(struct queue_entry *entry,
 	rt2x00_set_field32(&word, TXD_W5_WAITING_DMA_DONE_INT, 1);
 	rt2x00_desc_write(txd, 5, word);
 
-	if (txdesc->queue != QID_BEACON) {
+	if (txdesc->qid != QID_BEACON) {
 		rt2x00_desc_read(txd, 6, &word);
 		rt2x00_set_field32(&word, TXD_W6_BUFFER_PHYSICAL_ADDRESS,
 				   skbdesc->skb_dma);
@@ -1856,7 +1856,7 @@ static void rt61pci_write_tx_desc(struct queue_entry *entry,
 	 */
 	skbdesc->desc = txd;
 	skbdesc->desc_len =
-		(txdesc->queue == QID_BEACON) ?  TXINFO_SIZE : TXD_DESC_SIZE;
+		(txdesc->qid == QID_BEACON) ?  TXINFO_SIZE : TXD_DESC_SIZE;
 }
 
 /*
diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c
index b04e618..b9dfa7b 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/rt2x00/rt73usb.c
@@ -1463,7 +1463,7 @@ static void rt73usb_write_tx_desc(struct queue_entry *entry,
 	rt2x00_desc_write(txd, 0, word);
 
 	rt2x00_desc_read(txd, 1, &word);
-	rt2x00_set_field32(&word, TXD_W1_HOST_Q_ID, txdesc->queue);
+	rt2x00_set_field32(&word, TXD_W1_HOST_Q_ID, txdesc->qid);
 	rt2x00_set_field32(&word, TXD_W1_AIFSN, txdesc->aifs);
 	rt2x00_set_field32(&word, TXD_W1_CWMIN, txdesc->cw_min);
 	rt2x00_set_field32(&word, TXD_W1_CWMAX, txdesc->cw_max);
-- 
1.7.2.2


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 02/10] rt2x00: Update rt2800 comments regarding AMPDU and PACKET_ID in TXWI
  2010-08-30 19:12 [PATCH 01/10] rt2x00: Rename txentry_desc.queue -> txentry_desc.qid Ivo van Doorn
@ 2010-08-30 19:12 ` Ivo van Doorn
  2010-08-30 19:13   ` [PATCH 03/10] rt2x00: Add rt2800_wait_csr_ready Ivo van Doorn
  0 siblings, 1 reply; 10+ messages in thread
From: Ivo van Doorn @ 2010-08-30 19:12 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, users

From: Helmut Schaa <helmut.schaa@googlemail.com>

Signed-off-by: Helmut Schaa <helmut.schaa@googlemail.com>
Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com>
---
 drivers/net/wireless/rt2x00/rt2800.h |    6 +++++-
 1 files changed, 5 insertions(+), 1 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h
index 5c006a7..70a5cb8 100644
--- a/drivers/net/wireless/rt2x00/rt2800.h
+++ b/drivers/net/wireless/rt2x00/rt2800.h
@@ -1953,6 +1953,8 @@ struct mac_iveiv_entry {
  * TX_OP: 0:HT TXOP rule , 1:PIFS TX ,2:Backoff, 3:sifs
  * BW: Channel bandwidth 20MHz or 40 MHz
  * 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.
  */
 #define TXWI_W0_FRAG			FIELD32(0x00000001)
 #define TXWI_W0_MIMO_PS			FIELD32(0x00000002)
@@ -1976,7 +1978,9 @@ struct mac_iveiv_entry {
  * WIRELESS_CLI_ID: Client ID for WCID table access
  * MPDU_TOTAL_BYTE_COUNT: Length of 802.11 frame
  * PACKETID: Will be latched into the TX_STA_FIFO register once the according
- *           frame was processed. 0: Don't report tx status for this frame.
+ *           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.
  */
 #define TXWI_W1_ACK			FIELD32(0x00000001)
 #define TXWI_W1_NSEQ			FIELD32(0x00000002)
-- 
1.7.2.2


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 03/10] rt2x00: Add rt2800_wait_csr_ready
  2010-08-30 19:12 ` [PATCH 02/10] rt2x00: Update rt2800 comments regarding AMPDU and PACKET_ID in TXWI Ivo van Doorn
@ 2010-08-30 19:13   ` Ivo van Doorn
  2010-08-30 19:13     ` [PATCH 04/10] rt2x00: Validate TX status results with current data entry Ivo van Doorn
  0 siblings, 1 reply; 10+ messages in thread
From: Ivo van Doorn @ 2010-08-30 19:13 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, users

Similar to rt2800_wait_wpdma_ready() we can add a
function to waiting until the CSR is ready. This
centralizes some additional code into rt2800lib.

Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com>
Acked-by: Gertjan van Wingerde <gwingerde@gmail.com>
---
 drivers/net/wireless/rt2x00/rt2800lib.c |   28 ++++++++++++++++++----------
 drivers/net/wireless/rt2x00/rt2800lib.h |    1 +
 drivers/net/wireless/rt2x00/rt2800usb.c |   12 +-----------
 3 files changed, 20 insertions(+), 21 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index ac92e7c..967f3e5 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -255,6 +255,23 @@ void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev,
 }
 EXPORT_SYMBOL_GPL(rt2800_mcu_request);
 
+int rt2800_wait_csr_ready(struct rt2x00_dev *rt2x00dev)
+{
+	unsigned int i = 0;
+	u32 reg;
+
+	for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+		rt2800_register_read(rt2x00dev, MAC_CSR0, &reg);
+		if (reg && reg != ~0)
+			return 0;
+		msleep(1);
+	}
+
+	ERROR(rt2x00dev, "Unstable hardware.\n");
+	return -EBUSY;
+}
+EXPORT_SYMBOL_GPL(rt2800_wait_csr_ready);
+
 int rt2800_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev)
 {
 	unsigned int i;
@@ -370,17 +387,8 @@ int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev,
 	/*
 	 * Wait for stable hardware.
 	 */
-	for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
-		rt2800_register_read(rt2x00dev, MAC_CSR0, &reg);
-		if (reg && reg != ~0)
-			break;
-		msleep(1);
-	}
-
-	if (i == REGISTER_BUSY_COUNT) {
-		ERROR(rt2x00dev, "Unstable hardware.\n");
+	if (rt2800_wait_csr_ready(rt2x00dev))
 		return -EBUSY;
-	}
 
 	if (rt2x00_is_pci(rt2x00dev))
 		rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000002);
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/rt2x00/rt2800lib.h
index deb10b1..986229c 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.h
+++ b/drivers/net/wireless/rt2x00/rt2800lib.h
@@ -140,6 +140,7 @@ void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev,
 			const u8 command, const u8 token,
 			const u8 arg0, const u8 arg1);
 
+int rt2800_wait_csr_ready(struct rt2x00_dev *rt2x00dev);
 int rt2800_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev);
 
 int rt2800_check_firmware(struct rt2x00_dev *rt2x00dev,
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index 82ae58e..b1a8e94 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -122,22 +122,12 @@ static void rt2800usb_toggle_rx(struct rt2x00_dev *rt2x00dev,
 static int rt2800usb_init_registers(struct rt2x00_dev *rt2x00dev)
 {
 	u32 reg;
-	int i;
 
 	/*
 	 * Wait until BBP and RF are ready.
 	 */
-	for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
-		rt2800_register_read(rt2x00dev, MAC_CSR0, &reg);
-		if (reg && reg != ~0)
-			break;
-		msleep(1);
-	}
-
-	if (i == REGISTER_BUSY_COUNT) {
-		ERROR(rt2x00dev, "Unstable hardware.\n");
+	if (rt2800_wait_csr_ready(rt2x00dev))
 		return -EBUSY;
-	}
 
 	rt2800_register_read(rt2x00dev, PBF_SYS_CTRL, &reg);
 	rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, reg & ~0x00002000);
-- 
1.7.2.2


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 04/10] rt2x00: Validate TX status results with current data entry
  2010-08-30 19:13   ` [PATCH 03/10] rt2x00: Add rt2800_wait_csr_ready Ivo van Doorn
@ 2010-08-30 19:13     ` Ivo van Doorn
  2010-08-30 19:13       ` [PATCH 05/10] rt2x00: Wakeup hardware before loading firmware Ivo van Doorn
  0 siblings, 1 reply; 10+ messages in thread
From: Ivo van Doorn @ 2010-08-30 19:13 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, users

Instead of printing a warning when the PID, ACK, or WCID of
an entry don't match the TX status report, we should skip the
entry to search for the entry which actually does match
the TX status data.

This reduces the number of watchdog errors on the TX queues
for rt2800usb, and seems to improve the reliability of the
TX flow a bit.

Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com>
Acked-by: Gertjan van Wingerde <gwingerde@gmail.com>
---
 drivers/net/wireless/rt2x00/rt2800lib.c |   73 +++++++++++++++++++++----------
 1 files changed, 50 insertions(+), 23 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 967f3e5..c8af221 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -581,6 +581,49 @@ void rt2800_process_rxwi(struct queue_entry *entry,
 }
 EXPORT_SYMBOL_GPL(rt2800_process_rxwi);
 
+static bool rt2800_txdone_entry_check(struct queue_entry *entry, u32 reg)
+{
+	__le32 *txwi;
+	u32 word;
+	int wcid, ack, pid;
+	int tx_wcid, tx_ack, tx_pid;
+
+	wcid	= rt2x00_get_field32(reg, TX_STA_FIFO_WCID);
+	ack	= rt2x00_get_field32(reg, TX_STA_FIFO_TX_ACK_REQUIRED);
+	pid	= rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE);
+
+	/*
+	 * This frames has returned with an IO error,
+	 * so the status report is not intended for this
+	 * frame.
+	 */
+	if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) {
+		rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE);
+		return false;
+	}
+
+	/*
+	 * Validate if this TX status report is intended for
+	 * this entry by comparing the WCID/ACK/PID fields.
+	 */
+	txwi = rt2800_drv_get_txwi(entry);
+
+	rt2x00_desc_read(txwi, 1, &word);
+	tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID);
+	tx_ack  = rt2x00_get_field32(word, TXWI_W1_ACK);
+	tx_pid  = rt2x00_get_field32(word, TXWI_W1_PACKETID);
+
+	if ((wcid != tx_wcid) || (ack != tx_ack) || (pid != tx_pid)) {
+		WARNING(entry->queue->rt2x00dev,
+			"TX status report missed for queue %d entry %d\n",
+		entry->queue->qid, entry->entry_idx);
+		rt2x00lib_txdone_noinfo(entry, TXDONE_UNKNOWN);
+		return false;
+	}
+
+	return true;
+}
+
 void rt2800_txdone(struct rt2x00_dev *rt2x00dev)
 {
 	struct data_queue *queue;
@@ -589,8 +632,8 @@ void rt2800_txdone(struct rt2x00_dev *rt2x00dev)
 	struct txdone_entry_desc txdesc;
 	u32 word;
 	u32 reg;
-	int wcid, ack, pid, tx_wcid, tx_ack, tx_pid;
 	u16 mcs, real_mcs;
+	u8 pid;
 	int i;
 
 	/*
@@ -607,18 +650,15 @@ void rt2800_txdone(struct rt2x00_dev *rt2x00dev)
 		if (!rt2x00_get_field32(reg, TX_STA_FIFO_VALID))
 			break;
 
-		wcid	= rt2x00_get_field32(reg, TX_STA_FIFO_WCID);
-		ack	= rt2x00_get_field32(reg, TX_STA_FIFO_TX_ACK_REQUIRED);
-		pid	= rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE);
-
 		/*
 		 * Skip this entry when it contains an invalid
 		 * queue identication number.
 		 */
-		if (pid <= 0 || pid > QID_RX)
+		pid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE) - 1;
+		if (pid >= QID_RX)
 			continue;
 
-		queue = rt2x00queue_get_queue(rt2x00dev, pid - 1);
+		queue = rt2x00queue_get_queue(rt2x00dev, pid);
 		if (unlikely(!queue))
 			continue;
 
@@ -627,35 +667,22 @@ 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 (!test_bit(ENTRY_DATA_IO_FAILED, &entry->flags))
+			if (rt2800_txdone_entry_check(entry, reg))
 				break;
-
-			rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE);
 		}
 
 		if (!entry || rt2x00queue_empty(queue))
 			break;
 
-		/*
-		 * Check if we got a match by looking at WCID/ACK/PID
-		 * fields
-		 */
-		txwi = rt2800_drv_get_txwi(entry);
-
-		rt2x00_desc_read(txwi, 1, &word);
-		tx_wcid	= rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID);
-		tx_ack	= rt2x00_get_field32(word, TXWI_W1_ACK);
-		tx_pid	= rt2x00_get_field32(word, TXWI_W1_PACKETID);
-
-		if ((wcid != tx_wcid) || (ack != tx_ack) || (pid != tx_pid))
-			WARNING(rt2x00dev, "invalid TX_STA_FIFO content");
 
 		/*
 		 * 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);
-- 
1.7.2.2


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 05/10] rt2x00: Wakeup hardware before loading firmware
  2010-08-30 19:13     ` [PATCH 04/10] rt2x00: Validate TX status results with current data entry Ivo van Doorn
@ 2010-08-30 19:13       ` Ivo van Doorn
  2010-08-30 19:14         ` [PATCH 06/10] rt2x00: Don't set unicast/BSSID masks when clearning MAC or BSSID Ivo van Doorn
  0 siblings, 1 reply; 10+ messages in thread
From: Ivo van Doorn @ 2010-08-30 19:13 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, users

According to the legacy drivers the AUTOWAKEUP_CFG
register must be reset to 0 before loading the firmware.

Instead of during rt2800{pci,usb}_write_firmware it
must actually be done in rt2800_load_firmware() before
resetting the WPDMA_GLO_CFG and PWR_PIN_CFG registers.

Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com>
Acked-by: Gertjan van Wingerde <gwingerde@gmail.com>
---
 drivers/net/wireless/rt2x00/rt2800lib.c |    6 ++++++
 drivers/net/wireless/rt2x00/rt2800pci.c |    2 --
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index c8af221..1bc7f49 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -385,6 +385,12 @@ int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev,
 	u32 reg;
 
 	/*
+	 * If driver doesn't wake up firmware here,
+	 * rt2800_load_firmware will hang forever when interface is up again.
+	 */
+	rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, 0x00000000);
+
+	/*
 	 * Wait for stable hardware.
 	 */
 	if (rt2800_wait_csr_ready(rt2x00dev))
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c
index 4d93204..2bcb150 100644
--- a/drivers/net/wireless/rt2x00/rt2800pci.c
+++ b/drivers/net/wireless/rt2x00/rt2800pci.c
@@ -196,8 +196,6 @@ static int rt2800pci_write_firmware(struct rt2x00_dev *rt2x00dev,
 {
 	u32 reg;
 
-	rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, 0x00000000);
-
 	/*
 	 * enable Host program ram write selection
 	 */
-- 
1.7.2.2


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 06/10] rt2x00: Don't set unicast/BSSID masks when clearning MAC or BSSID
  2010-08-30 19:13       ` [PATCH 05/10] rt2x00: Wakeup hardware before loading firmware Ivo van Doorn
@ 2010-08-30 19:14         ` Ivo van Doorn
  2010-08-30 19:14           ` [PATCH 07/10] rt2x00: Set PWR_PIN_CFG during initialization Ivo van Doorn
  0 siblings, 1 reply; 10+ messages in thread
From: Ivo van Doorn @ 2010-08-30 19:14 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, users

When configuring the MAC_ADDR or MAC_BSSID with an empty address,
the UNICAST_TO_ME_MASK and BSS_ID_MASK must also be reset to prevent
invalid interpretation of the addresses.

Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com>
Acked-by: Gertjan van Wingerde <gwingerde@gmail.com>
---
 drivers/net/wireless/rt2x00/rt2800lib.c |   18 +++++++++++-------
 1 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 1bc7f49..27a6e22 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -1135,19 +1135,23 @@ void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf,
 	}
 
 	if (flags & CONFIG_UPDATE_MAC) {
-		reg = le32_to_cpu(conf->mac[1]);
-		rt2x00_set_field32(&reg, MAC_ADDR_DW1_UNICAST_TO_ME_MASK, 0xff);
-		conf->mac[1] = cpu_to_le32(reg);
+		if (!is_zero_ether_addr((const u8 *)conf->mac)) {
+			reg = le32_to_cpu(conf->mac[1]);
+			rt2x00_set_field32(&reg, MAC_ADDR_DW1_UNICAST_TO_ME_MASK, 0xff);
+			conf->mac[1] = cpu_to_le32(reg);
+		}
 
 		rt2800_register_multiwrite(rt2x00dev, MAC_ADDR_DW0,
 					      conf->mac, sizeof(conf->mac));
 	}
 
 	if (flags & CONFIG_UPDATE_BSSID) {
-		reg = le32_to_cpu(conf->bssid[1]);
-		rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_ID_MASK, 3);
-		rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_BCN_NUM, 7);
-		conf->bssid[1] = cpu_to_le32(reg);
+		if (!is_zero_ether_addr((const u8 *)conf->bssid)) {
+			reg = le32_to_cpu(conf->bssid[1]);
+			rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_ID_MASK, 3);
+			rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_BCN_NUM, 7);
+			conf->bssid[1] = cpu_to_le32(reg);
+		}
 
 		rt2800_register_multiwrite(rt2x00dev, MAC_BSSID_DW0,
 					      conf->bssid, sizeof(conf->bssid));
-- 
1.7.2.2


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 07/10] rt2x00: Set PWR_PIN_CFG during initialization
  2010-08-30 19:14         ` [PATCH 06/10] rt2x00: Don't set unicast/BSSID masks when clearning MAC or BSSID Ivo van Doorn
@ 2010-08-30 19:14           ` Ivo van Doorn
  2010-08-30 19:14             ` [PATCH 08/10] rt2x00: Correctly kill beacon queue Ivo van Doorn
  0 siblings, 1 reply; 10+ messages in thread
From: Ivo van Doorn @ 2010-08-30 19:14 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, users

Add PWR_PIN_CFG initialization for rt2800usb at the same point
as rt2800pci.

Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com>
Acked-by: Gertjan van Wingerde <gwingerde@gmail.com>
---
 drivers/net/wireless/rt2x00/rt2800usb.c |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index b1a8e94..d275549 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -132,6 +132,8 @@ static int rt2800usb_init_registers(struct rt2x00_dev *rt2x00dev)
 	rt2800_register_read(rt2x00dev, PBF_SYS_CTRL, &reg);
 	rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, reg & ~0x00002000);
 
+	rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003);
+
 	rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
 	rt2x00_set_field32(&reg, MAC_SYS_CTRL_RESET_CSR, 1);
 	rt2x00_set_field32(&reg, MAC_SYS_CTRL_RESET_BBP, 1);
-- 
1.7.2.2


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 08/10] rt2x00: Correctly kill beacon queue
  2010-08-30 19:14           ` [PATCH 07/10] rt2x00: Set PWR_PIN_CFG during initialization Ivo van Doorn
@ 2010-08-30 19:14             ` Ivo van Doorn
  2010-08-30 19:15               ` [PATCH 09/10] rt2x00: Split watchdog check into a DMA and STATUS timeout Ivo van Doorn
  0 siblings, 1 reply; 10+ messages in thread
From: Ivo van Doorn @ 2010-08-30 19:14 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, users

USB devices upload their beacon and then automatically send
it out every beacon interval. However when killing a TX queue
we only kill the URB and not the actual transmission of the beacon.

This will reset the Beacon register to prevent any beacons from
being transmitted.

Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com>
Acked-by: Gertjan van Wingerde <gwingerde@gmail.com>
---
 drivers/net/wireless/rt2x00/rt2500usb.c |   10 +++++++++-
 drivers/net/wireless/rt2x00/rt2800usb.c |   10 +++++++++-
 drivers/net/wireless/rt2x00/rt73usb.c   |   10 +++++++++-
 3 files changed, 27 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c
index 63b363c..db64df4 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.c
+++ b/drivers/net/wireless/rt2x00/rt2500usb.c
@@ -1196,6 +1196,14 @@ static int rt2500usb_get_tx_data_len(struct queue_entry *entry)
 	return length;
 }
 
+static void rt2500usb_kill_tx_queue(struct data_queue *queue)
+{
+	if (queue->qid == QID_BEACON)
+		rt2500usb_register_write(queue->rt2x00dev, TXRX_CSR19, 0);
+
+	rt2x00usb_kill_tx_queue(queue);
+}
+
 /*
  * RX control handlers
  */
@@ -1794,7 +1802,7 @@ static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = {
 	.write_beacon		= rt2500usb_write_beacon,
 	.get_tx_data_len	= rt2500usb_get_tx_data_len,
 	.kick_tx_queue		= rt2x00usb_kick_tx_queue,
-	.kill_tx_queue		= rt2x00usb_kill_tx_queue,
+	.kill_tx_queue		= rt2500usb_kill_tx_queue,
 	.fill_rxdone		= rt2500usb_fill_rxdone,
 	.config_shared_key	= rt2500usb_config_key,
 	.config_pairwise_key	= rt2500usb_config_key,
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index d275549..3dff56e 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -335,6 +335,14 @@ static void rt2800usb_work_txdone(struct work_struct *work)
 	}
 }
 
+static void rt2800usb_kill_tx_queue(struct data_queue *queue)
+{
+	if (queue->qid == QID_BEACON)
+		rt2x00usb_register_write(queue->rt2x00dev, BCN_TIME_CFG, 0);
+
+	rt2x00usb_kill_tx_queue(queue);
+}
+
 /*
  * RX control handlers
  */
@@ -533,7 +541,7 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
 	.write_beacon		= rt2800_write_beacon,
 	.get_tx_data_len	= rt2800usb_get_tx_data_len,
 	.kick_tx_queue		= rt2x00usb_kick_tx_queue,
-	.kill_tx_queue		= rt2x00usb_kill_tx_queue,
+	.kill_tx_queue		= rt2800usb_kill_tx_queue,
 	.fill_rxdone		= rt2800usb_fill_rxdone,
 	.config_shared_key	= rt2800_config_shared_key,
 	.config_pairwise_key	= rt2800_config_pairwise_key,
diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c
index b9dfa7b..87fb220 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/rt2x00/rt73usb.c
@@ -1573,6 +1573,14 @@ static int rt73usb_get_tx_data_len(struct queue_entry *entry)
 	return length;
 }
 
+static void rt73usb_kill_tx_queue(struct data_queue *queue)
+{
+	if (queue->qid == QID_BEACON)
+		rt2x00usb_register_write(queue->rt2x00dev, TXRX_CSR9, 0);
+
+	rt2x00usb_kill_tx_queue(queue);
+}
+
 /*
  * RX control handlers
  */
@@ -2262,7 +2270,7 @@ static const struct rt2x00lib_ops rt73usb_rt2x00_ops = {
 	.write_beacon		= rt73usb_write_beacon,
 	.get_tx_data_len	= rt73usb_get_tx_data_len,
 	.kick_tx_queue		= rt2x00usb_kick_tx_queue,
-	.kill_tx_queue		= rt2x00usb_kill_tx_queue,
+	.kill_tx_queue		= rt73usb_kill_tx_queue,
 	.fill_rxdone		= rt73usb_fill_rxdone,
 	.config_shared_key	= rt73usb_config_shared_key,
 	.config_pairwise_key	= rt73usb_config_pairwise_key,
-- 
1.7.2.2


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 09/10] rt2x00: Split watchdog check into a DMA and STATUS timeout
  2010-08-30 19:14             ` [PATCH 08/10] rt2x00: Correctly kill beacon queue Ivo van Doorn
@ 2010-08-30 19:15               ` Ivo van Doorn
  2010-08-30 19:15                 ` [PATCH 10/10] rt2x00: Cleanup rt2x00usb_watchdog_reset_tx Ivo van Doorn
  0 siblings, 1 reply; 10+ messages in thread
From: Ivo van Doorn @ 2010-08-30 19:15 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, users

The watchdog for rt2800usb triggers frequently causing all URB's
to be canceled often enough to interrupt the normal TX flow.
More research indicated that not the URB upload to the USB host
were hanging, but instead the TX status reports.

To correctly detect what is going on, we introduce Q_INDEX_DMA_DONE
which is an index counter between Q_INDEX_DONE and Q_INDEX and indicates
if the frame has been transfered to the device.

This also requires the rt2x00queue timeout functions to be updated
to differentiate between a DMA timeout (time between Q_INDEX and
Q_INDEX_DMA_DONE timeout) and a STATUS timeout (time between
Q_INDEX_DMA_DONE and Q_INDEX_DONE timeout)

All Q_INDEX_DMA_DONE code was taken from the RFC from
Helmut Schaa <helmut.schaa@googlemail.com> for the implementation
for watchdog for rt2800pci.

Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com>
Acked-by: Gertjan van Wingerde <gwingerde@gmail.com>
---
 drivers/net/wireless/rt2x00/rt2x00.h      |    1 +
 drivers/net/wireless/rt2x00/rt2x00debug.c |    5 +++--
 drivers/net/wireless/rt2x00/rt2x00dev.c   |    6 ++++++
 drivers/net/wireless/rt2x00/rt2x00queue.c |   13 ++++++++-----
 drivers/net/wireless/rt2x00/rt2x00queue.h |   21 +++++++++++++++++----
 drivers/net/wireless/rt2x00/rt2x00usb.c   |   27 ++++++++++++++++++++++++---
 6 files changed, 59 insertions(+), 14 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index 762f6b4..0ae942c 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -1070,6 +1070,7 @@ static inline void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev,
  */
 void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev);
 void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev);
+void rt2x00lib_dmadone(struct queue_entry *entry);
 void rt2x00lib_txdone(struct queue_entry *entry,
 		      struct txdone_entry_desc *txdesc);
 void rt2x00lib_txdone_noinfo(struct queue_entry *entry, u32 status);
diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.c b/drivers/net/wireless/rt2x00/rt2x00debug.c
index 7a907bc..a211e77 100644
--- a/drivers/net/wireless/rt2x00/rt2x00debug.c
+++ b/drivers/net/wireless/rt2x00/rt2x00debug.c
@@ -338,14 +338,15 @@ static ssize_t rt2x00debug_read_queue_stats(struct file *file,
 		return -ENOMEM;
 
 	temp = data +
-	    sprintf(data, "qid\tcount\tlimit\tlength\tindex\tdone\n");
+	    sprintf(data, "qid\tcount\tlimit\tlength\tindex\tdma done\tdone\n");
 
 	queue_for_each(intf->rt2x00dev, queue) {
 		spin_lock_irqsave(&queue->lock, irqflags);
 
-		temp += sprintf(temp, "%d\t%d\t%d\t%d\t%d\t%d\n", queue->qid,
+		temp += sprintf(temp, "%d\t%d\t%d\t%d\t%d\t%d\t%d\n", queue->qid,
 				queue->count, queue->limit, queue->length,
 				queue->index[Q_INDEX],
+				queue->index[Q_INDEX_DMA_DONE],
 				queue->index[Q_INDEX_DONE]);
 
 		spin_unlock_irqrestore(&queue->lock, irqflags);
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index 580595b..053fdd3 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -251,6 +251,12 @@ void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev)
 }
 EXPORT_SYMBOL_GPL(rt2x00lib_pretbtt);
 
+void rt2x00lib_dmadone(struct queue_entry *entry)
+{
+	rt2x00queue_index_inc(entry->queue, Q_INDEX_DMA_DONE);
+}
+EXPORT_SYMBOL_GPL(rt2x00lib_dmadone);
+
 void rt2x00lib_txdone(struct queue_entry *entry,
 		      struct txdone_entry_desc *txdesc)
 {
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index 7b25d66..eede999 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -731,13 +731,13 @@ void rt2x00queue_index_inc(struct data_queue *queue, enum queue_index index)
 	if (queue->index[index] >= queue->limit)
 		queue->index[index] = 0;
 
+	queue->last_action[index] = jiffies;
+
 	if (index == Q_INDEX) {
 		queue->length++;
-		queue->last_index = jiffies;
 	} else if (index == Q_INDEX_DONE) {
 		queue->length--;
 		queue->count++;
-		queue->last_index_done = jiffies;
 	}
 
 	spin_unlock_irqrestore(&queue->lock, irqflags);
@@ -746,14 +746,17 @@ void rt2x00queue_index_inc(struct data_queue *queue, enum queue_index index)
 static void rt2x00queue_reset(struct data_queue *queue)
 {
 	unsigned long irqflags;
+	unsigned int i;
 
 	spin_lock_irqsave(&queue->lock, irqflags);
 
 	queue->count = 0;
 	queue->length = 0;
-	queue->last_index = jiffies;
-	queue->last_index_done = jiffies;
-	memset(queue->index, 0, sizeof(queue->index));
+
+	for (i = 0; i < Q_INDEX_MAX; i++) {
+		queue->index[i] = 0;
+		queue->last_action[i] = jiffies;
+	}
 
 	spin_unlock_irqrestore(&queue->lock, irqflags);
 }
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h
index 0e38a91..d81d85f 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.h
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.h
@@ -401,6 +401,8 @@ struct queue_entry {
  *
  * @Q_INDEX: Index pointer to the current entry in the queue, if this entry is
  *	owned by the hardware then the queue is considered to be full.
+ * @Q_INDEX_DMA_DONE: Index pointer for the next entry which will have been
+ *	transfered to the hardware.
  * @Q_INDEX_DONE: Index pointer to the next entry which will be completed by
  *	the hardware and for which we need to run the txdone handler. If this
  *	entry is not owned by the hardware the queue is considered to be empty.
@@ -409,6 +411,7 @@ struct queue_entry {
  */
 enum queue_index {
 	Q_INDEX,
+	Q_INDEX_DMA_DONE,
 	Q_INDEX_DONE,
 	Q_INDEX_MAX,
 };
@@ -445,13 +448,12 @@ struct data_queue {
 	enum data_queue_qid qid;
 
 	spinlock_t lock;
-	unsigned long last_index;
-	unsigned long last_index_done;
 	unsigned int count;
 	unsigned short limit;
 	unsigned short threshold;
 	unsigned short length;
 	unsigned short index[Q_INDEX_MAX];
+	unsigned long last_action[Q_INDEX_MAX];
 
 	unsigned short txop;
 	unsigned short aifs;
@@ -616,12 +618,23 @@ static inline int rt2x00queue_threshold(struct data_queue *queue)
 }
 
 /**
- * rt2x00queue_timeout - Check if a timeout occured for this queue
+ * rt2x00queue_timeout - Check if a timeout occured for STATUS reorts
  * @queue: Queue to check.
  */
 static inline int rt2x00queue_timeout(struct data_queue *queue)
 {
-	return time_after(queue->last_index, queue->last_index_done + (HZ / 10));
+	return time_after(queue->last_action[Q_INDEX_DMA_DONE],
+			  queue->last_action[Q_INDEX_DONE] + (HZ / 10));
+}
+
+/**
+ * rt2x00queue_timeout - Check if a timeout occured for DMA transfers
+ * @queue: Queue to check.
+ */
+static inline int rt2x00queue_dma_timeout(struct data_queue *queue)
+{
+	return time_after(queue->last_action[Q_INDEX],
+			  queue->last_action[Q_INDEX_DMA_DONE] + (HZ / 10));
 }
 
 /**
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c
index 6cc7aa4..aec6440 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.c
+++ b/drivers/net/wireless/rt2x00/rt2x00usb.c
@@ -213,6 +213,11 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb)
 		return;
 
 	/*
+	 * Report the frame as DMA done
+	 */
+	rt2x00lib_dmadone(entry);
+
+	/*
 	 * Check if the frame was correctly uploaded
 	 */
 	if (urb->status)
@@ -283,13 +288,14 @@ void rt2x00usb_kill_tx_queue(struct data_queue *queue)
 }
 EXPORT_SYMBOL_GPL(rt2x00usb_kill_tx_queue);
 
-static void rt2x00usb_watchdog_reset_tx(struct data_queue *queue)
+static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue)
 {
 	struct queue_entry *entry;
 	struct queue_entry_priv_usb *entry_priv;
 	unsigned short threshold = queue->threshold;
 
-	WARNING(queue->rt2x00dev, "TX queue %d timed out, invoke reset", queue->qid);
+	WARNING(queue->rt2x00dev, "TX queue %d DMA timed out,"
+		" invoke forced forced reset", queue->qid);
 
 	/*
 	 * Temporarily disable the TX queue, this will force mac80211
@@ -331,13 +337,23 @@ static void rt2x00usb_watchdog_reset_tx(struct data_queue *queue)
 	ieee80211_wake_queue(queue->rt2x00dev->hw, queue->qid);
 }
 
+static void rt2x00usb_watchdog_tx_status(struct data_queue *queue)
+{
+	WARNING(queue->rt2x00dev, "TX queue %d status timed out,"
+		" invoke forced tx handler", queue->qid);
+
+	ieee80211_queue_work(queue->rt2x00dev->hw, &queue->rt2x00dev->txdone_work);
+}
+
 void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev)
 {
 	struct data_queue *queue;
 
 	tx_queue_for_each(rt2x00dev, queue) {
+		if (rt2x00queue_dma_timeout(queue))
+			rt2x00usb_watchdog_tx_dma(queue);
 		if (rt2x00queue_timeout(queue))
-			rt2x00usb_watchdog_reset_tx(queue);
+			rt2x00usb_watchdog_tx_status(queue);
 	}
 }
 EXPORT_SYMBOL_GPL(rt2x00usb_watchdog);
@@ -383,6 +399,11 @@ static void rt2x00usb_interrupt_rxdone(struct urb *urb)
 		return;
 
 	/*
+	 * Report the frame as DMA done
+	 */
+	rt2x00lib_dmadone(entry);
+
+	/*
 	 * Check if the received data is simply too small
 	 * to be actually valid, or if the urb is signaling
 	 * a problem.
-- 
1.7.2.2


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 10/10] rt2x00: Cleanup rt2x00usb_watchdog_reset_tx
  2010-08-30 19:15               ` [PATCH 09/10] rt2x00: Split watchdog check into a DMA and STATUS timeout Ivo van Doorn
@ 2010-08-30 19:15                 ` Ivo van Doorn
  0 siblings, 0 replies; 10+ messages in thread
From: Ivo van Doorn @ 2010-08-30 19:15 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, users

rt2x00usb_watchdog_reset_tx performs the same task
as rt2x00usb_kill_tx_queue, with the only difference
is that it waits for all entries to be returned to
the driver and for all frames the status has been
reported to mac80211.

We can easily split this task by calling rt2x00usb_kill_tx_queue,
sleep for a short period and invoke the TX status reporting
function. By adding the sleep() to the kill_entry we make sure
that even during shutdown we guarentee the entry has been killed when
the function returns. To make this work correctly the interrupt
handlers have to be updated to prevent checking for the RADIO_ENABLED
flag too early which prevents the ownership of the entry to be reset.
Additionally a check for the DEVICE_PRESENT flag is not required but
is nice to prevent race conditions when the device was unplugged.

Additionally rather then calling rt2x00usb_work_txdone() for
status reporting we let the driver perform the TX status reporting
first. If this is not sufficient then rt2x00usb_work_txdone() will
still be used to cleanup the mess.

Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com>
Acked-by: Gertjan van Wingerde <gwingerde@gmail.com>
---
 drivers/net/wireless/rt2x00/rt2x00usb.c |   66 +++++++++++++++++++------------
 1 files changed, 40 insertions(+), 26 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c
index aec6440..4c5ae3d 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.c
+++ b/drivers/net/wireless/rt2x00/rt2x00usb.c
@@ -208,8 +208,7 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb)
 	struct queue_entry *entry = (struct queue_entry *)urb->context;
 	struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
 
-	if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags) ||
-	    !__test_and_clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags))
+	if (!__test_and_clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags))
 		return;
 
 	/*
@@ -227,7 +226,9 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb)
 	 * Schedule the delayed work for reading the TX status
 	 * from the device.
 	 */
-	ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->txdone_work);
+	if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) &&
+	    test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+		ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->txdone_work);
 }
 
 static void rt2x00usb_kick_tx_entry(struct queue_entry *entry)
@@ -279,6 +280,14 @@ static void rt2x00usb_kill_tx_entry(struct queue_entry *entry)
 	if ((entry->queue->qid == QID_BEACON) &&
 	    (test_bit(DRIVER_REQUIRE_BEACON_GUARD, &rt2x00dev->flags)))
 		usb_kill_urb(bcn_priv->guardian_urb);
+
+	/*
+	 * We need a short delay here to wait for
+	 * the URB to be canceled
+	 */
+	do {
+		udelay(100);
+	} while (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags));
 }
 
 void rt2x00usb_kill_tx_queue(struct data_queue *queue)
@@ -290,8 +299,7 @@ EXPORT_SYMBOL_GPL(rt2x00usb_kill_tx_queue);
 
 static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue)
 {
-	struct queue_entry *entry;
-	struct queue_entry_priv_usb *entry_priv;
+	struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
 	unsigned short threshold = queue->threshold;
 
 	WARNING(queue->rt2x00dev, "TX queue %d DMA timed out,"
@@ -305,28 +313,33 @@ static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue)
 	 * queue from being enabled during the txdone handler.
 	 */
 	queue->threshold = queue->limit;
-	ieee80211_stop_queue(queue->rt2x00dev->hw, queue->qid);
+	ieee80211_stop_queue(rt2x00dev->hw, queue->qid);
 
 	/*
-	 * Reset all currently uploaded TX frames.
+	 * Kill all entries in the queue, afterwards we need to
+	 * wait a bit for all URBs to be cancelled.
 	 */
-	while (!rt2x00queue_empty(queue)) {
-		entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
-		entry_priv = entry->priv_data;
-		usb_kill_urb(entry_priv->urb);
+	rt2x00usb_kill_tx_queue(queue);
 
-		/*
-		 * We need a short delay here to wait for
-		 * the URB to be canceled
-		 */
-		do {
-			udelay(100);
-		} while (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags));
+	/*
+	 * In case that a driver has overriden the txdone_work
+	 * function, we invoke the TX done through there.
+	 */
+	rt2x00dev->txdone_work.func(&rt2x00dev->txdone_work);
 
-		/*
-		 * Invoke the TX done handler
-		 */
-		rt2x00usb_work_txdone_entry(entry);
+	/*
+	 * Security measure: if the driver did override the
+	 * txdone_work function, and the hardware did arrive
+	 * in a state which causes it to malfunction, it is
+	 * possible that the driver couldn't handle the txdone
+	 * event correctly. So after giving the driver the
+	 * chance to cleanup, we now force a cleanup of any
+	 * leftovers.
+	 */
+	if (!rt2x00queue_empty(queue)) {
+		WARNING(queue->rt2x00dev, "TX queue %d DMA timed out,"
+			" status handling failed, invoke hard reset", queue->qid);
+		rt2x00usb_work_txdone(&rt2x00dev->txdone_work);
 	}
 
 	/*
@@ -334,7 +347,7 @@ static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue)
 	 * queue again.
 	 */
 	queue->threshold = threshold;
-	ieee80211_wake_queue(queue->rt2x00dev->hw, queue->qid);
+	ieee80211_wake_queue(rt2x00dev->hw, queue->qid);
 }
 
 static void rt2x00usb_watchdog_tx_status(struct data_queue *queue)
@@ -394,8 +407,7 @@ static void rt2x00usb_interrupt_rxdone(struct urb *urb)
 	struct queue_entry *entry = (struct queue_entry *)urb->context;
 	struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
 
-	if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags) ||
-	    !__test_and_clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags))
+	if (!__test_and_clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags))
 		return;
 
 	/*
@@ -415,7 +427,9 @@ static void rt2x00usb_interrupt_rxdone(struct urb *urb)
 	 * Schedule the delayed work for reading the RX status
 	 * from the device.
 	 */
-	ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->rxdone_work);
+	if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) &&
+	    test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+		ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->rxdone_work);
 }
 
 /*
-- 
1.7.2.2


^ permalink raw reply related	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2010-08-30 19:16 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-08-30 19:12 [PATCH 01/10] rt2x00: Rename txentry_desc.queue -> txentry_desc.qid Ivo van Doorn
2010-08-30 19:12 ` [PATCH 02/10] rt2x00: Update rt2800 comments regarding AMPDU and PACKET_ID in TXWI Ivo van Doorn
2010-08-30 19:13   ` [PATCH 03/10] rt2x00: Add rt2800_wait_csr_ready Ivo van Doorn
2010-08-30 19:13     ` [PATCH 04/10] rt2x00: Validate TX status results with current data entry Ivo van Doorn
2010-08-30 19:13       ` [PATCH 05/10] rt2x00: Wakeup hardware before loading firmware Ivo van Doorn
2010-08-30 19:14         ` [PATCH 06/10] rt2x00: Don't set unicast/BSSID masks when clearning MAC or BSSID Ivo van Doorn
2010-08-30 19:14           ` [PATCH 07/10] rt2x00: Set PWR_PIN_CFG during initialization Ivo van Doorn
2010-08-30 19:14             ` [PATCH 08/10] rt2x00: Correctly kill beacon queue Ivo van Doorn
2010-08-30 19:15               ` [PATCH 09/10] rt2x00: Split watchdog check into a DMA and STATUS timeout Ivo van Doorn
2010-08-30 19:15                 ` [PATCH 10/10] rt2x00: Cleanup rt2x00usb_watchdog_reset_tx Ivo van Doorn

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).