aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/rt2x00
diff options
context:
space:
mode:
authorIvo van Doorn <ivdoorn@gmail.com>2010-07-11 06:25:46 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-07-12 16:05:34 -0400
commitc965c74bbc650e5466d2f3e32bd28112ebcdd00c (patch)
treeb24e5a745a40b5f7d83b8fb7b04a5abd1c32445f /drivers/net/wireless/rt2x00
parent223dcc26591aa8e4a6bf623164b775b5bd89c9e1 (diff)
rt2x00: Implement watchdog monitoring
Implement watchdog monitoring for USB devices (PCI support can be added later). This will determine if URBs being uploaded to the hardware are actually returning. Both rt2500usb and rt2800usb have shown that URBs being uploaded can remain hanging without being released by the hardware. By using this watchdog, a queue can be reset when this occurs. For rt2800usb it has been tested that the connection is preserved even though this interruption. Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/rt2x00')
-rw-r--r--drivers/net/wireless/rt2x00/rt2500usb.c2
-rw-r--r--drivers/net/wireless/rt2x00/rt2800usb.c2
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00.h7
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00dev.c10
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00lib.h26
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00link.c38
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00queue.c4
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00queue.h11
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00usb.c50
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00usb.h10
-rw-r--r--drivers/net/wireless/rt2x00/rt73usb.c2
11 files changed, 159 insertions, 3 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c
index 0b7888d43f37..009323e6c20f 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.c
+++ b/drivers/net/wireless/rt2x00/rt2500usb.c
@@ -1736,6 +1736,7 @@ static int rt2500usb_probe_hw(struct rt2x00_dev *rt2x00dev)
1736 __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags); 1736 __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags);
1737 __set_bit(DRIVER_REQUIRE_COPY_IV, &rt2x00dev->flags); 1737 __set_bit(DRIVER_REQUIRE_COPY_IV, &rt2x00dev->flags);
1738 } 1738 }
1739 __set_bit(DRIVER_SUPPORT_WATCHDOG, &rt2x00dev->flags);
1739 1740
1740 /* 1741 /*
1741 * Set the rssi offset. 1742 * Set the rssi offset.
@@ -1772,6 +1773,7 @@ static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = {
1772 .rfkill_poll = rt2500usb_rfkill_poll, 1773 .rfkill_poll = rt2500usb_rfkill_poll,
1773 .link_stats = rt2500usb_link_stats, 1774 .link_stats = rt2500usb_link_stats,
1774 .reset_tuner = rt2500usb_reset_tuner, 1775 .reset_tuner = rt2500usb_reset_tuner,
1776 .watchdog = rt2x00usb_watchdog,
1775 .write_tx_desc = rt2500usb_write_tx_desc, 1777 .write_tx_desc = rt2500usb_write_tx_desc,
1776 .write_beacon = rt2500usb_write_beacon, 1778 .write_beacon = rt2500usb_write_beacon,
1777 .get_tx_data_len = rt2500usb_get_tx_data_len, 1779 .get_tx_data_len = rt2500usb_get_tx_data_len,
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index e4ab4db66ca0..5aa7563155cd 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -633,6 +633,7 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
633 if (!modparam_nohwcrypt) 633 if (!modparam_nohwcrypt)
634 __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags); 634 __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags);
635 __set_bit(DRIVER_SUPPORT_LINK_TUNING, &rt2x00dev->flags); 635 __set_bit(DRIVER_SUPPORT_LINK_TUNING, &rt2x00dev->flags);
636 __set_bit(DRIVER_SUPPORT_WATCHDOG, &rt2x00dev->flags);
636 637
637 /* 638 /*
638 * Set the rssi offset. 639 * Set the rssi offset.
@@ -655,6 +656,7 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
655 .link_stats = rt2800_link_stats, 656 .link_stats = rt2800_link_stats,
656 .reset_tuner = rt2800_reset_tuner, 657 .reset_tuner = rt2800_reset_tuner,
657 .link_tuner = rt2800_link_tuner, 658 .link_tuner = rt2800_link_tuner,
659 .watchdog = rt2x00usb_watchdog,
658 .write_tx_desc = rt2800usb_write_tx_desc, 660 .write_tx_desc = rt2800usb_write_tx_desc,
659 .write_tx_data = rt2800usb_write_tx_data, 661 .write_tx_data = rt2800usb_write_tx_data,
660 .write_beacon = rt2800_write_beacon, 662 .write_beacon = rt2800_write_beacon,
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index bf5e3f37e705..97b6261fee4f 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -332,6 +332,11 @@ struct link {
332 * Work structure for scheduling periodic link tuning. 332 * Work structure for scheduling periodic link tuning.
333 */ 333 */
334 struct delayed_work work; 334 struct delayed_work work;
335
336 /*
337 * Work structure for scheduling periodic watchdog monitoring.
338 */
339 struct delayed_work watchdog_work;
335}; 340};
336 341
337/* 342/*
@@ -543,6 +548,7 @@ struct rt2x00lib_ops {
543 struct link_qual *qual); 548 struct link_qual *qual);
544 void (*link_tuner) (struct rt2x00_dev *rt2x00dev, 549 void (*link_tuner) (struct rt2x00_dev *rt2x00dev,
545 struct link_qual *qual, const u32 count); 550 struct link_qual *qual, const u32 count);
551 void (*watchdog) (struct rt2x00_dev *rt2x00dev);
546 552
547 /* 553 /*
548 * TX control handlers 554 * TX control handlers
@@ -648,6 +654,7 @@ enum rt2x00_flags {
648 DRIVER_SUPPORT_CONTROL_FILTERS, 654 DRIVER_SUPPORT_CONTROL_FILTERS,
649 DRIVER_SUPPORT_CONTROL_FILTER_PSPOLL, 655 DRIVER_SUPPORT_CONTROL_FILTER_PSPOLL,
650 DRIVER_SUPPORT_LINK_TUNING, 656 DRIVER_SUPPORT_LINK_TUNING,
657 DRIVER_SUPPORT_WATCHDOG,
651 658
652 /* 659 /*
653 * Driver configuration 660 * Driver configuration
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index 12ee7bdedd02..0906e14b347f 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -70,6 +70,11 @@ int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev)
70 rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON); 70 rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON);
71 71
72 /* 72 /*
73 * Start watchdog monitoring.
74 */
75 rt2x00link_start_watchdog(rt2x00dev);
76
77 /*
73 * Start the TX queues. 78 * Start the TX queues.
74 */ 79 */
75 ieee80211_wake_queues(rt2x00dev->hw); 80 ieee80211_wake_queues(rt2x00dev->hw);
@@ -89,6 +94,11 @@ void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev)
89 rt2x00queue_stop_queues(rt2x00dev); 94 rt2x00queue_stop_queues(rt2x00dev);
90 95
91 /* 96 /*
97 * Stop watchdog monitoring.
98 */
99 rt2x00link_stop_watchdog(rt2x00dev);
100
101 /*
92 * Disable RX. 102 * Disable RX.
93 */ 103 */
94 rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_OFF); 104 rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_OFF);
diff --git a/drivers/net/wireless/rt2x00/rt2x00lib.h b/drivers/net/wireless/rt2x00/rt2x00lib.h
index ed27de1de57b..dc5c6574aaf4 100644
--- a/drivers/net/wireless/rt2x00/rt2x00lib.h
+++ b/drivers/net/wireless/rt2x00/rt2x00lib.h
@@ -30,6 +30,7 @@
30/* 30/*
31 * Interval defines 31 * Interval defines
32 */ 32 */
33#define WATCHDOG_INTERVAL round_jiffies_relative(HZ)
33#define LINK_TUNE_INTERVAL round_jiffies_relative(HZ) 34#define LINK_TUNE_INTERVAL round_jiffies_relative(HZ)
34 35
35/* 36/*
@@ -257,11 +258,30 @@ void rt2x00link_stop_tuner(struct rt2x00_dev *rt2x00dev);
257void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna); 258void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna);
258 259
259/** 260/**
260 * rt2x00link_register - Initialize link tuning functionality 261 * rt2x00link_start_watchdog - Start periodic watchdog monitoring
261 * @rt2x00dev: Pointer to &struct rt2x00_dev. 262 * @rt2x00dev: Pointer to &struct rt2x00_dev.
262 * 263 *
263 * Initialize work structure and all link tuning related 264 * This start the watchdog periodic work, this work will
264 * parameters. This will not start the link tuning process itself. 265 *be executed periodically until &rt2x00link_stop_watchdog has
266 * been called.
267 */
268void rt2x00link_start_watchdog(struct rt2x00_dev *rt2x00dev);
269
270/**
271 * rt2x00link_stop_watchdog - Stop periodic watchdog monitoring
272 * @rt2x00dev: Pointer to &struct rt2x00_dev.
273 *
274 * After this function completed the watchdog monitoring will not
275 * be running until &rt2x00link_start_watchdog is called.
276 */
277void rt2x00link_stop_watchdog(struct rt2x00_dev *rt2x00dev);
278
279/**
280 * rt2x00link_register - Initialize link tuning & watchdog functionality
281 * @rt2x00dev: Pointer to &struct rt2x00_dev.
282 *
283 * Initialize work structure and all link tuning and watchdog related
284 * parameters. This will not start the periodic work itself.
265 */ 285 */
266void rt2x00link_register(struct rt2x00_dev *rt2x00dev); 286void rt2x00link_register(struct rt2x00_dev *rt2x00dev);
267 287
diff --git a/drivers/net/wireless/rt2x00/rt2x00link.c b/drivers/net/wireless/rt2x00/rt2x00link.c
index 9acfc5c70389..666cef3f8472 100644
--- a/drivers/net/wireless/rt2x00/rt2x00link.c
+++ b/drivers/net/wireless/rt2x00/rt2x00link.c
@@ -407,7 +407,45 @@ static void rt2x00link_tuner(struct work_struct *work)
407 &link->work, LINK_TUNE_INTERVAL); 407 &link->work, LINK_TUNE_INTERVAL);
408} 408}
409 409
410void rt2x00link_start_watchdog(struct rt2x00_dev *rt2x00dev)
411{
412 struct link *link = &rt2x00dev->link;
413
414 if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) ||
415 !test_bit(DRIVER_SUPPORT_WATCHDOG, &rt2x00dev->flags))
416 return;
417
418 ieee80211_queue_delayed_work(rt2x00dev->hw,
419 &link->watchdog_work, WATCHDOG_INTERVAL);
420}
421
422void rt2x00link_stop_watchdog(struct rt2x00_dev *rt2x00dev)
423{
424 cancel_delayed_work_sync(&rt2x00dev->link.watchdog_work);
425}
426
427static void rt2x00link_watchdog(struct work_struct *work)
428{
429 struct rt2x00_dev *rt2x00dev =
430 container_of(work, struct rt2x00_dev, link.watchdog_work.work);
431 struct link *link = &rt2x00dev->link;
432
433 /*
434 * When the radio is shutting down we should
435 * immediately cease the watchdog monitoring.
436 */
437 if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
438 return;
439
440 rt2x00dev->ops->lib->watchdog(rt2x00dev);
441
442 if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
443 ieee80211_queue_delayed_work(rt2x00dev->hw,
444 &link->watchdog_work, WATCHDOG_INTERVAL);
445}
446
410void rt2x00link_register(struct rt2x00_dev *rt2x00dev) 447void rt2x00link_register(struct rt2x00_dev *rt2x00dev)
411{ 448{
449 INIT_DELAYED_WORK(&rt2x00dev->link.watchdog_work, rt2x00link_watchdog);
412 INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00link_tuner); 450 INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00link_tuner);
413} 451}
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index 5097fe0f9f51..a3401d301058 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -688,9 +688,11 @@ void rt2x00queue_index_inc(struct data_queue *queue, enum queue_index index)
688 688
689 if (index == Q_INDEX) { 689 if (index == Q_INDEX) {
690 queue->length++; 690 queue->length++;
691 queue->last_index = jiffies;
691 } else if (index == Q_INDEX_DONE) { 692 } else if (index == Q_INDEX_DONE) {
692 queue->length--; 693 queue->length--;
693 queue->count++; 694 queue->count++;
695 queue->last_index_done = jiffies;
694 } 696 }
695 697
696 spin_unlock_irqrestore(&queue->lock, irqflags); 698 spin_unlock_irqrestore(&queue->lock, irqflags);
@@ -704,6 +706,8 @@ static void rt2x00queue_reset(struct data_queue *queue)
704 706
705 queue->count = 0; 707 queue->count = 0;
706 queue->length = 0; 708 queue->length = 0;
709 queue->last_index = jiffies;
710 queue->last_index_done = jiffies;
707 memset(queue->index, 0, sizeof(queue->index)); 711 memset(queue->index, 0, sizeof(queue->index));
708 712
709 spin_unlock_irqrestore(&queue->lock, irqflags); 713 spin_unlock_irqrestore(&queue->lock, irqflags);
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h
index bd54f55a8cb9..191e7775a9c0 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.h
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.h
@@ -446,6 +446,8 @@ struct data_queue {
446 enum data_queue_qid qid; 446 enum data_queue_qid qid;
447 447
448 spinlock_t lock; 448 spinlock_t lock;
449 unsigned long last_index;
450 unsigned long last_index_done;
449 unsigned int count; 451 unsigned int count;
450 unsigned short limit; 452 unsigned short limit;
451 unsigned short threshold; 453 unsigned short threshold;
@@ -599,6 +601,15 @@ static inline int rt2x00queue_threshold(struct data_queue *queue)
599} 601}
600 602
601/** 603/**
604 * rt2x00queue_timeout - Check if a timeout occured for this queue
605 * @queue: Queue to check.
606 */
607static inline int rt2x00queue_timeout(struct data_queue *queue)
608{
609 return time_after(queue->last_index, queue->last_index_done + (HZ / 10));
610}
611
612/**
602 * _rt2x00_desc_read - Read a word from the hardware descriptor. 613 * _rt2x00_desc_read - Read a word from the hardware descriptor.
603 * @desc: Base descriptor address 614 * @desc: Base descriptor address
604 * @word: Word index from where the descriptor should be read. 615 * @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 a22837c560fd..ff3a36622d1b 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.c
+++ b/drivers/net/wireless/rt2x00/rt2x00usb.c
@@ -292,6 +292,56 @@ void rt2x00usb_kill_tx_queue(struct rt2x00_dev *rt2x00dev,
292} 292}
293EXPORT_SYMBOL_GPL(rt2x00usb_kill_tx_queue); 293EXPORT_SYMBOL_GPL(rt2x00usb_kill_tx_queue);
294 294
295static void rt2x00usb_watchdog_reset_tx(struct data_queue *queue)
296{
297 struct queue_entry_priv_usb *entry_priv;
298 unsigned short threshold = queue->threshold;
299
300 WARNING(queue->rt2x00dev, "TX queue %d timed out, invoke reset", queue->qid);
301
302 /*
303 * Temporarily disable the TX queue, this will force mac80211
304 * to use the other queues until this queue has been restored.
305 *
306 * Set the queue threshold to the queue limit. This prevents the
307 * queue from being enabled during the txdone handler.
308 */
309 queue->threshold = queue->limit;
310 ieee80211_stop_queue(queue->rt2x00dev->hw, queue->qid);
311
312 /*
313 * Reset all currently uploaded TX frames.
314 */
315 while (!rt2x00queue_empty(queue)) {
316 entry_priv = rt2x00queue_get_entry(queue, Q_INDEX_DONE)->priv_data;
317 usb_kill_urb(entry_priv->urb);
318
319 /*
320 * We need a short delay here to wait for
321 * the URB to be canceled and invoked the tx_done handler.
322 */
323 udelay(200);
324 }
325
326 /*
327 * The queue has been reset, and mac80211 is allowed to use the
328 * queue again.
329 */
330 queue->threshold = threshold;
331 ieee80211_wake_queue(queue->rt2x00dev->hw, queue->qid);
332}
333
334void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev)
335{
336 struct data_queue *queue;
337
338 tx_queue_for_each(rt2x00dev, queue) {
339 if (rt2x00queue_timeout(queue))
340 rt2x00usb_watchdog_reset_tx(queue);
341 }
342}
343EXPORT_SYMBOL_GPL(rt2x00usb_watchdog);
344
295/* 345/*
296 * RX data handlers. 346 * RX data handlers.
297 */ 347 */
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.h b/drivers/net/wireless/rt2x00/rt2x00usb.h
index 2b7a1889e72f..d3d3ddc40875 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.h
+++ b/drivers/net/wireless/rt2x00/rt2x00usb.h
@@ -399,6 +399,16 @@ void rt2x00usb_kick_tx_queue(struct rt2x00_dev *rt2x00dev,
399void rt2x00usb_kill_tx_queue(struct rt2x00_dev *rt2x00dev, 399void rt2x00usb_kill_tx_queue(struct rt2x00_dev *rt2x00dev,
400 const enum data_queue_qid qid); 400 const enum data_queue_qid qid);
401 401
402/**
403 * rt2x00usb_watchdog - Watchdog for USB communication
404 * @rt2x00dev: Pointer to &struct rt2x00_dev
405 *
406 * Check the health of the USB communication and determine
407 * if timeouts have occured. If this is the case, this function
408 * will reset all communication to restore functionality again.
409 */
410void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev);
411
402/* 412/*
403 * Device initialization handlers. 413 * Device initialization handlers.
404 */ 414 */
diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c
index 9ea6a672d4e2..cad07b69c53e 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/rt2x00/rt73usb.c
@@ -2136,6 +2136,7 @@ static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev)
2136 if (!modparam_nohwcrypt) 2136 if (!modparam_nohwcrypt)
2137 __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags); 2137 __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags);
2138 __set_bit(DRIVER_SUPPORT_LINK_TUNING, &rt2x00dev->flags); 2138 __set_bit(DRIVER_SUPPORT_LINK_TUNING, &rt2x00dev->flags);
2139 __set_bit(DRIVER_SUPPORT_WATCHDOG, &rt2x00dev->flags);
2139 2140
2140 /* 2141 /*
2141 * Set the rssi offset. 2142 * Set the rssi offset.
@@ -2251,6 +2252,7 @@ static const struct rt2x00lib_ops rt73usb_rt2x00_ops = {
2251 .link_stats = rt73usb_link_stats, 2252 .link_stats = rt73usb_link_stats,
2252 .reset_tuner = rt73usb_reset_tuner, 2253 .reset_tuner = rt73usb_reset_tuner,
2253 .link_tuner = rt73usb_link_tuner, 2254 .link_tuner = rt73usb_link_tuner,
2255 .watchdog = rt2x00usb_watchdog,
2254 .write_tx_desc = rt73usb_write_tx_desc, 2256 .write_tx_desc = rt73usb_write_tx_desc,
2255 .write_beacon = rt73usb_write_beacon, 2257 .write_beacon = rt73usb_write_beacon,
2256 .get_tx_data_len = rt73usb_get_tx_data_len, 2258 .get_tx_data_len = rt73usb_get_tx_data_len,