Commit f0187a19 authored by Johannes Stezenbach's avatar Johannes Stezenbach Committed by John W. Linville

rt2800usb: add timer to handle TX_STA_FIFO

TX status is reported by the hardware when a packet has been
sent (or after TX failed after possible retries), which is some
time after the DMA completion.  Since the rt2800usb hardware can
not signal interrupts we have to use a timer, otherwise the
TX status would only be read by the next packet's TX DMA
completion, or by the watchdog thread.
Signed-off-by: default avatarJohannes Stezenbach <js@sig21.net>
Signed-off-by: default avatarIvo van Doorn <IvDoorn@gmail.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 6e6d6932
...@@ -98,6 +98,22 @@ static void rt2800usb_stop_queue(struct data_queue *queue) ...@@ -98,6 +98,22 @@ static void rt2800usb_stop_queue(struct data_queue *queue)
} }
} }
/*
* test if there is an entry in any TX queue for which DMA is done
* but the TX status has not been returned yet
*/
static bool rt2800usb_txstatus_pending(struct rt2x00_dev *rt2x00dev)
{
struct data_queue *queue;
tx_queue_for_each(rt2x00dev, queue) {
if (rt2x00queue_get_entry(queue, Q_INDEX_DMA_DONE) !=
rt2x00queue_get_entry(queue, Q_INDEX_DONE))
return true;
}
return false;
}
static void rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev, static void rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev,
int urb_status, u32 tx_status) int urb_status, u32 tx_status)
{ {
...@@ -115,8 +131,11 @@ static void rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev, ...@@ -115,8 +131,11 @@ static void rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev,
} else } else
rt2x00usb_register_read_async(rt2x00dev, TX_STA_FIFO, rt2x00usb_register_read_async(rt2x00dev, TX_STA_FIFO,
rt2800usb_tx_sta_fifo_read_completed); rt2800usb_tx_sta_fifo_read_completed);
} else if (!kfifo_is_empty(&rt2x00dev->txstatus_fifo)) } else if (!kfifo_is_empty(&rt2x00dev->txstatus_fifo)) {
queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work);
} else if (rt2800usb_txstatus_pending(rt2x00dev)) {
mod_timer(&rt2x00dev->txstatus_timer, jiffies + msecs_to_jiffies(20));
}
} }
static void rt2800usb_tx_dma_done(struct queue_entry *entry) static void rt2800usb_tx_dma_done(struct queue_entry *entry)
...@@ -127,6 +146,14 @@ static void rt2800usb_tx_dma_done(struct queue_entry *entry) ...@@ -127,6 +146,14 @@ static void rt2800usb_tx_dma_done(struct queue_entry *entry)
rt2800usb_tx_sta_fifo_read_completed); rt2800usb_tx_sta_fifo_read_completed);
} }
static void rt2800usb_tx_sta_fifo_timeout(unsigned long data)
{
struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
rt2x00usb_register_read_async(rt2x00dev, TX_STA_FIFO,
rt2800usb_tx_sta_fifo_read_completed);
}
/* /*
* Firmware functions * Firmware functions
*/ */
...@@ -459,6 +486,14 @@ static void rt2800usb_work_txdone(struct work_struct *work) ...@@ -459,6 +486,14 @@ static void rt2800usb_work_txdone(struct work_struct *work)
break; break;
} }
} }
/*
* The hw may delay sending the packet after DMA complete
* if the medium is busy, thus the TX_STA_FIFO entry is
* also delayed -> use a timer to retrieve it.
*/
if (rt2800usb_txstatus_pending(rt2x00dev))
mod_timer(&rt2x00dev->txstatus_timer, jiffies + msecs_to_jiffies(20));
} }
/* /*
...@@ -599,6 +634,10 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev) ...@@ -599,6 +634,10 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
__set_bit(REQUIRE_HT_TX_DESC, &rt2x00dev->cap_flags); __set_bit(REQUIRE_HT_TX_DESC, &rt2x00dev->cap_flags);
__set_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags); __set_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags);
setup_timer(&rt2x00dev->txstatus_timer,
rt2800usb_tx_sta_fifo_timeout,
(unsigned long) rt2x00dev);
/* /*
* Set the rssi offset. * Set the rssi offset.
*/ */
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/input-polldev.h> #include <linux/input-polldev.h>
#include <linux/kfifo.h> #include <linux/kfifo.h>
#include <linux/timer.h>
#include <net/mac80211.h> #include <net/mac80211.h>
...@@ -923,6 +924,11 @@ struct rt2x00_dev { ...@@ -923,6 +924,11 @@ struct rt2x00_dev {
*/ */
DECLARE_KFIFO_PTR(txstatus_fifo, u32); DECLARE_KFIFO_PTR(txstatus_fifo, u32);
/*
* Timer to ensure tx status reports are read (rt2800usb).
*/
struct timer_list txstatus_timer;
/* /*
* Tasklet for processing tx status reports (rt2800pci). * Tasklet for processing tx status reports (rt2800pci).
*/ */
......
...@@ -1071,6 +1071,7 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) ...@@ -1071,6 +1071,7 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev)
/* /*
* Stop all work. * Stop all work.
*/ */
del_timer_sync(&rt2x00dev->txstatus_timer);
cancel_work_sync(&rt2x00dev->intf_work); cancel_work_sync(&rt2x00dev->intf_work);
if (rt2x00_is_usb(rt2x00dev)) { if (rt2x00_is_usb(rt2x00dev)) {
cancel_work_sync(&rt2x00dev->rxdone_work); cancel_work_sync(&rt2x00dev->rxdone_work);
......
...@@ -280,7 +280,9 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb) ...@@ -280,7 +280,9 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb)
* Schedule the delayed work for reading the TX status * Schedule the delayed work for reading the TX status
* from the device. * from the device.
*/ */
queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); if (!test_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags) ||
!kfifo_is_empty(&rt2x00dev->txstatus_fifo))
queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work);
} }
static bool rt2x00usb_kick_tx_entry(struct queue_entry *entry, void* data) static bool rt2x00usb_kick_tx_entry(struct queue_entry *entry, void* data)
...@@ -816,6 +818,7 @@ int rt2x00usb_probe(struct usb_interface *usb_intf, ...@@ -816,6 +818,7 @@ int rt2x00usb_probe(struct usb_interface *usb_intf,
INIT_WORK(&rt2x00dev->rxdone_work, rt2x00usb_work_rxdone); INIT_WORK(&rt2x00dev->rxdone_work, rt2x00usb_work_rxdone);
INIT_WORK(&rt2x00dev->txdone_work, rt2x00usb_work_txdone); INIT_WORK(&rt2x00dev->txdone_work, rt2x00usb_work_txdone);
init_timer(&rt2x00dev->txstatus_timer);
retval = rt2x00usb_alloc_reg(rt2x00dev); retval = rt2x00usb_alloc_reg(rt2x00dev);
if (retval) if (retval)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment