[rt2x00-users] [PATCH 03/07] rt2x00: Add RX watchdog handlers

Ivo van Doorn ivdoorn at gmail.com
Mon Oct 25 11:33:44 UTC 2010


Add watchdog handlers for the USB RX queue.
The most basic watchdog handler is used for checking
the RX status timeout which simply invokes the TX
completion handler.

The more complicated one checks if it has been more
then a second since the last RX frame. If this is
the case then the RX queue needs to be flushed since
it could mean the hardware locked up and can only
be revived by killing all URB's and restart the queue.

The legacy driver performs this flush only if the
RX queue has overflowed, but while testing the
RX queue never overflowed while the lockup occured.
So we use an alternative which requires 1 RX frame
every second.

Signed-off-by: Ivo van Doorn <IvDoorn at gmail.com>
---
 drivers/net/wireless/rt2x00/rt2x00queue.h |   10 +++
 drivers/net/wireless/rt2x00/rt2x00usb.c   |   93 +++++++++++++++++++++++++----
 2 files changed, 90 insertions(+), 13 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h
index 64be4cf..09002d2 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.h
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.h
@@ -638,6 +638,16 @@ static inline int rt2x00queue_dma_timeout(struct data_queue *queue)
 }
 
 /**
+ * rt2x00queue_action_timeout - Check if a timeout occured for a queue action
+ * @queue: Queue to check.
+ */
+static inline int rt2x00queue_action_timeout(struct data_queue *queue)
+{
+	return time_after(jiffies,
+			  queue->last_action[Q_INDEX_DMA_DONE] + HZ);
+}
+
+/**
  * _rt2x00_desc_read - Read a word from the hardware descriptor.
  * @desc: Base descriptor address
  * @word: Word index from where the descriptor should be read.
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c
index 6dd9619..5e1e9d7 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.c
+++ b/drivers/net/wireless/rt2x00/rt2x00usb.c
@@ -292,13 +292,39 @@ void rt2x00usb_kill_tx_queue(struct data_queue *queue)
 }
 EXPORT_SYMBOL_GPL(rt2x00usb_kill_tx_queue);
 
-static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue)
+static void rt2x00usb_flush_rx(struct data_queue *queue)
 {
 	struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
-	unsigned short threshold = queue->threshold;
 
-	WARNING(queue->rt2x00dev, "TX queue %d DMA timed out,"
-		" invoke forced forced reset", queue->qid);
+	/**
+	 * Temporarily disable the RX queue, this will prevent
+	 * any frames to be recieved while we are restoring the queue.
+	 */
+	rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_RX_OFF);
+
+	/**
+	 * Kill all entries in the queue, the rt2x00usb_kill_tx_queue
+	 * function also works on the queue.
+	 */
+	rt2x00usb_kill_tx_queue(queue);
+
+	/*
+	 * In case that a driver has overriden the rxdone_work
+	 * function, we invoke the RX done through there.
+	 */
+	rt2x00dev->rxdone_work.func(&rt2x00dev->rxdone_work);
+
+	/*
+	 * The queue has been reset, and we can continue recieving frames
+	 * again.
+	 */
+	rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_RX_ON);
+}
+
+static void rt2x00usb_flush_tx(struct data_queue *queue)
+{
+	struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
+	unsigned short threshold = queue->threshold;
 
 	/*
 	 * Temporarily disable the TX queue, this will force mac80211
@@ -311,8 +337,7 @@ static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue)
 	ieee80211_stop_queue(rt2x00dev->hw, queue->qid);
 
 	/*
-	 * Kill all entries in the queue, afterwards we need to
-	 * wait a bit for all URBs to be cancelled.
+	 * Kill all entries in the queue.
 	 */
 	rt2x00usb_kill_tx_queue(queue);
 
@@ -330,24 +355,66 @@ static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue)
 	ieee80211_wake_queue(rt2x00dev->hw, queue->qid);
 }
 
-static void rt2x00usb_watchdog_tx_status(struct data_queue *queue)
+static void rt2x00usb_watchdog_dma_timeout(struct data_queue *queue)
 {
-	WARNING(queue->rt2x00dev, "TX queue %d status timed out,"
-		" invoke forced tx handler", queue->qid);
+	WARNING(queue->rt2x00dev, "Queue %d DMA timed out,"
+		" invoke forced reset", queue->qid);
+
+	switch (queue->qid) {
+	case QID_AC_BE:
+	case QID_AC_BK:
+	case QID_AC_VI:
+	case QID_AC_VO:
+		rt2x00usb_flush_tx(queue);
+		break;
+	case QID_RX:
+		rt2x00usb_flush_rx(queue);
+		break;
+	default:
+		return;
+	}
+}
 
-	ieee80211_queue_work(queue->rt2x00dev->hw, &queue->rt2x00dev->txdone_work);
+static void rt2x00usb_watchdog_status_timeout(struct data_queue *queue)
+{
+	struct work_struct *work;
+
+	WARNING(queue->rt2x00dev, "Queue %d status timed out,"
+		" invoke forced completion handler", queue->qid);
+
+	switch (queue->qid) {
+	case QID_AC_BE:
+	case QID_AC_BK:
+	case QID_AC_VI:
+	case QID_AC_VO:
+		work = &queue->rt2x00dev->txdone_work;
+		break;
+	case QID_RX:
+		work = &queue->rt2x00dev->rxdone_work;
+		break;
+	default:
+		return;
+	}
+
+	ieee80211_queue_work(queue->rt2x00dev->hw, work);
 }
 
 void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev)
 {
-	struct data_queue *queue;
+	struct data_queue *queue = rt2x00dev->rx;
+
+	if (rt2x00queue_action_timeout(queue))
+		rt2x00usb_watchdog_dma_timeout(queue);
+
+	if (rt2x00queue_status_timeout(queue))
+		rt2x00usb_watchdog_status_timeout(queue);
 
 	tx_queue_for_each(rt2x00dev, queue) {
 		if (!rt2x00queue_empty(queue)) {
 			if (rt2x00queue_dma_timeout(queue))
-				rt2x00usb_watchdog_tx_dma(queue);
+				rt2x00usb_watchdog_dma_timeout(queue);
 			if (rt2x00queue_status_timeout(queue))
-				rt2x00usb_watchdog_tx_status(queue);
+				rt2x00usb_watchdog_status_timeout(queue);
 		}
 	}
 }
-- 
1.7.2.3




More information about the users mailing list