aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
diff options
context:
space:
mode:
authorSujith Manoharan <Sujith.Manoharan@atheros.com>2011-04-13 01:55:59 -0400
committerJohn W. Linville <linville@tuxdriver.com>2011-04-13 15:24:08 -0400
commitb587fc81a80b9656f64e89fe0a106ffa4b35abca (patch)
tree1945232f9c8adad3810229249f5e6aa5ca0f9057 /drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
parentf2820f4583b233827f10d91adea70225e196d852 (diff)
ath9k_htc: Drain pending TX frames properly
When doing a channel set or a reset operation the pending frames queued up for transmission have to be flushed and sent to mac80211. Fixing this has to be done in two separate steps: * Flush queued frames and kill the URB TX completion handler. * Complete all the frames that in the TX pending queue. This patch adds proper support for draining and all the callsites namely, channel change/reset/idle/stop are fixed. A separate queue is used for handling failed frames. Signed-off-by: Sujith Manoharan <Sujith.Manoharan@atheros.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/htc_drv_txrx.c')
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_txrx.c31
1 files changed, 30 insertions, 1 deletions
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
index 944440c84c49..9e0c34b0a794 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
@@ -423,6 +423,26 @@ send_mac80211:
423 ieee80211_tx_status(priv->hw, skb); 423 ieee80211_tx_status(priv->hw, skb);
424} 424}
425 425
426void ath9k_htc_tx_drain(struct ath9k_htc_priv *priv)
427{
428 struct sk_buff *skb = NULL;
429
430 /*
431 * Ensure that all pending TX frames are flushed,
432 * and that the TX completion tasklet is killed.
433 */
434 htc_stop(priv->htc);
435 tasklet_kill(&priv->tx_tasklet);
436
437 while ((skb = skb_dequeue(&priv->tx.tx_queue)) != NULL) {
438 ath9k_htc_tx_process(priv, skb);
439 }
440
441 while ((skb = skb_dequeue(&priv->tx.tx_failed)) != NULL) {
442 ath9k_htc_tx_process(priv, skb);
443 }
444}
445
426void ath9k_tx_tasklet(unsigned long data) 446void ath9k_tx_tasklet(unsigned long data)
427{ 447{
428 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data; 448 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data;
@@ -432,6 +452,10 @@ void ath9k_tx_tasklet(unsigned long data)
432 ath9k_htc_tx_process(priv, skb); 452 ath9k_htc_tx_process(priv, skb);
433 } 453 }
434 454
455 while ((skb = skb_dequeue(&priv->tx.tx_failed)) != NULL) {
456 ath9k_htc_tx_process(priv, skb);
457 }
458
435 /* Wake TX queues if needed */ 459 /* Wake TX queues if needed */
436 ath9k_htc_check_wake_queues(priv); 460 ath9k_htc_check_wake_queues(priv);
437} 461}
@@ -445,13 +469,18 @@ void ath9k_htc_txep(void *drv_priv, struct sk_buff *skb,
445 tx_ctl = HTC_SKB_CB(skb); 469 tx_ctl = HTC_SKB_CB(skb);
446 tx_ctl->txok = txok; 470 tx_ctl->txok = txok;
447 471
448 skb_queue_tail(&priv->tx.tx_queue, skb); 472 if (txok)
473 skb_queue_tail(&priv->tx.tx_queue, skb);
474 else
475 skb_queue_tail(&priv->tx.tx_failed, skb);
476
449 tasklet_schedule(&priv->tx_tasklet); 477 tasklet_schedule(&priv->tx_tasklet);
450} 478}
451 479
452int ath9k_tx_init(struct ath9k_htc_priv *priv) 480int ath9k_tx_init(struct ath9k_htc_priv *priv)
453{ 481{
454 skb_queue_head_init(&priv->tx.tx_queue); 482 skb_queue_head_init(&priv->tx.tx_queue);
483 skb_queue_head_init(&priv->tx.tx_failed);
455 return 0; 484 return 0;
456} 485}
457 486