diff options
author | Sujith Manoharan <Sujith.Manoharan@atheros.com> | 2011-04-13 01:55:59 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-04-13 15:24:08 -0400 |
commit | b587fc81a80b9656f64e89fe0a106ffa4b35abca (patch) | |
tree | 1945232f9c8adad3810229249f5e6aa5ca0f9057 /drivers/net/wireless/ath/ath9k/htc_drv_txrx.c | |
parent | f2820f4583b233827f10d91adea70225e196d852 (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.c | 31 |
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 | ||
426 | void 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 | |||
426 | void ath9k_tx_tasklet(unsigned long data) | 446 | void 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 | ||
452 | int ath9k_tx_init(struct ath9k_htc_priv *priv) | 480 | int 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 | ||