diff options
Diffstat (limited to 'drivers/net/wireless/mediatek/mt76/usb.c')
-rw-r--r-- | drivers/net/wireless/mediatek/mt76/usb.c | 78 |
1 files changed, 54 insertions, 24 deletions
diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c index d2c6718b5933..c299c6591072 100644 --- a/drivers/net/wireless/mediatek/mt76/usb.c +++ b/drivers/net/wireless/mediatek/mt76/usb.c @@ -540,7 +540,7 @@ static void mt76u_rx_tasklet(unsigned long data) rcu_read_unlock(); } -int mt76u_submit_rx_buffers(struct mt76_dev *dev) +static int mt76u_submit_rx_buffers(struct mt76_dev *dev) { struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN]; unsigned long flags; @@ -558,7 +558,6 @@ int mt76u_submit_rx_buffers(struct mt76_dev *dev) return err; } -EXPORT_SYMBOL_GPL(mt76u_submit_rx_buffers); static int mt76u_alloc_rx(struct mt76_dev *dev) { @@ -605,14 +604,29 @@ static void mt76u_free_rx(struct mt76_dev *dev) memset(&q->rx_page, 0, sizeof(q->rx_page)); } -static void mt76u_stop_rx(struct mt76_dev *dev) +void mt76u_stop_rx(struct mt76_dev *dev) { struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN]; int i; for (i = 0; i < q->ndesc; i++) - usb_kill_urb(q->entry[i].urb); + usb_poison_urb(q->entry[i].urb); + + tasklet_kill(&dev->usb.rx_tasklet); +} +EXPORT_SYMBOL_GPL(mt76u_stop_rx); + +int mt76u_resume_rx(struct mt76_dev *dev) +{ + struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN]; + int i; + + for (i = 0; i < q->ndesc; i++) + usb_unpoison_urb(q->entry[i].urb); + + return mt76u_submit_rx_buffers(dev); } +EXPORT_SYMBOL_GPL(mt76u_resume_rx); static void mt76u_tx_tasklet(unsigned long data) { @@ -834,38 +848,54 @@ static void mt76u_free_tx(struct mt76_dev *dev) } } -static void mt76u_stop_tx(struct mt76_dev *dev) +void mt76u_stop_tx(struct mt76_dev *dev) { + struct mt76_queue_entry entry; struct mt76_queue *q; - int i, j; + int i, j, ret; - for (i = 0; i < IEEE80211_NUM_ACS; i++) { - q = dev->q_tx[i].q; - for (j = 0; j < q->ndesc; j++) - usb_kill_urb(q->entry[j].urb); - } -} + ret = wait_event_timeout(dev->tx_wait, !mt76_has_tx_pending(dev), HZ/5); + if (!ret) { + dev_err(dev->dev, "timed out waiting for pending tx\n"); -void mt76u_stop_queues(struct mt76_dev *dev) -{ - tasklet_disable(&dev->usb.rx_tasklet); - tasklet_disable(&dev->tx_tasklet); + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + q = dev->q_tx[i].q; + for (j = 0; j < q->ndesc; j++) + usb_kill_urb(q->entry[j].urb); + } - mt76u_stop_rx(dev); - mt76u_stop_tx(dev); -} -EXPORT_SYMBOL_GPL(mt76u_stop_queues); + tasklet_kill(&dev->tx_tasklet); + + /* On device removal we maight queue skb's, but mt76u_tx_kick() + * will fail to submit urb, cleanup those skb's manually. + */ + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + q = dev->q_tx[i].q; + + /* Assure we are in sync with killed tasklet. */ + spin_lock_bh(&q->lock); + while (q->queued) { + entry = q->entry[q->head]; + q->head = (q->head + 1) % q->ndesc; + q->queued--; + + dev->drv->tx_complete_skb(dev, i, &entry); + } + spin_unlock_bh(&q->lock); + } + } -void mt76u_stop_stat_wk(struct mt76_dev *dev) -{ cancel_delayed_work_sync(&dev->usb.stat_work); clear_bit(MT76_READING_STATS, &dev->state); + + mt76_tx_status_check(dev, NULL, true); } -EXPORT_SYMBOL_GPL(mt76u_stop_stat_wk); +EXPORT_SYMBOL_GPL(mt76u_stop_tx); void mt76u_queues_deinit(struct mt76_dev *dev) { - mt76u_stop_queues(dev); + mt76u_stop_rx(dev); + mt76u_stop_tx(dev); mt76u_free_rx(dev); mt76u_free_tx(dev); |