q->head = (q->head + 1) % q->ndesc;
q->queued++;
- tasklet_schedule(&dev->usb.rx_tasklet);
+ mt76_worker_schedule(&dev->usb.rx_worker);
out:
spin_unlock_irqrestore(&q->lock, flags);
}
}
mt76u_submit_rx_buf(dev, qid, urb);
}
- if (qid == MT_RXQ_MAIN)
+ if (qid == MT_RXQ_MAIN) {
+ local_bh_disable();
mt76_rx_poll_complete(dev, MT_RXQ_MAIN, NULL);
+ local_bh_enable();
+ }
}
-static void mt76u_rx_tasklet(unsigned long data)
+static void mt76u_rx_worker(struct mt76_worker *w)
{
- struct mt76_dev *dev = (struct mt76_dev *)data;
+ struct mt76_usb *usb = container_of(w, struct mt76_usb, rx_worker);
+ struct mt76_dev *dev = container_of(usb, struct mt76_dev, usb);
int i;
rcu_read_lock();
struct page *page;
int i;
- for (i = 0; i < q->ndesc; i++)
+ for (i = 0; i < q->ndesc; i++) {
+ if (!q->entry[i].urb)
+ continue;
+
mt76u_urb_free(q->entry[i].urb);
+ q->entry[i].urb = NULL;
+ }
if (!q->rx_page.va)
return;
{
int i;
+ mt76_worker_teardown(&dev->usb.rx_worker);
+
mt76_for_each_q_rx(dev, i)
mt76u_free_rx_queue(dev, &dev->q_rx[i]);
}
{
int i;
+ mt76_worker_disable(&dev->usb.rx_worker);
+
mt76_for_each_q_rx(dev, i) {
struct mt76_queue *q = &dev->q_rx[i];
int j;
for (j = 0; j < q->ndesc; j++)
usb_poison_urb(q->entry[j].urb);
}
-
- tasklet_kill(&dev->usb.rx_tasklet);
}
EXPORT_SYMBOL_GPL(mt76u_stop_rx);
return err;
}
+ mt76_worker_enable(&dev->usb.rx_worker);
+
return 0;
}
EXPORT_SYMBOL_GPL(mt76u_resume_rx);
-static void mt76u_tx_worker(struct mt76_worker *w)
+static void mt76u_status_worker(struct mt76_worker *w)
{
- struct mt76_dev *dev = container_of(w, struct mt76_dev, tx_worker);
+ struct mt76_usb *usb = container_of(w, struct mt76_usb, status_worker);
+ struct mt76_dev *dev = container_of(usb, struct mt76_dev, usb);
struct mt76_queue_entry entry;
struct mt76_queue *q;
bool wake;
int i;
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
- q = dev->q_tx[i];
+ q = dev->phy.q_tx[i];
while (q->queued > 0) {
if (!q->entry[q->tail].done)
if (!q->queued)
wake_up(&dev->tx_wait);
- mt76_txq_schedule(&dev->phy, i);
+ mt76_worker_schedule(&dev->tx_worker);
if (dev->drv->tx_status_data &&
!test_and_set_bit(MT76_READING_STATS, &dev->phy.state))
dev_err(dev->dev, "tx urb failed: %d\n", urb->status);
e->done = true;
- mt76_worker_schedule(&dev->tx_worker);
+ mt76_worker_schedule(&dev->usb.status_worker);
}
static int
}
static int
-mt76u_tx_queue_skb(struct mt76_dev *dev, enum mt76_txq_id qid,
+mt76u_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
struct sk_buff *skb, struct mt76_wcid *wcid,
struct ieee80211_sta *sta)
{
- struct mt76_queue *q = dev->q_tx[qid];
struct mt76_tx_info tx_info = {
.skb = skb,
};
return -ENOSPC;
skb->prev = skb->next = NULL;
- err = dev->drv->tx_prepare_skb(dev, NULL, qid, wcid, sta, &tx_info);
+ err = dev->drv->tx_prepare_skb(dev, NULL, q->qid, wcid, sta, &tx_info);
if (err < 0)
return err;
for (i = 0; i <= MT_TXQ_PSD; i++) {
if (i >= IEEE80211_NUM_ACS) {
- dev->q_tx[i] = dev->q_tx[0];
+ dev->phy.q_tx[i] = dev->phy.q_tx[0];
continue;
}
spin_lock_init(&q->lock);
q->hw_idx = mt76u_ac_to_hwq(dev, i);
- dev->q_tx[i] = q;
+ q->qid = i;
+
+ dev->phy.q_tx[i] = q;
q->entry = devm_kcalloc(dev->dev,
MT_NUM_TX_ENTRIES, sizeof(*q->entry),
{
int i;
+ mt76_worker_teardown(&dev->usb.status_worker);
+
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
struct mt76_queue *q;
int j;
- q = dev->q_tx[i];
+ q = dev->phy.q_tx[i];
if (!q)
continue;
- for (j = 0; j < q->ndesc; j++)
+ for (j = 0; j < q->ndesc; j++) {
usb_free_urb(q->entry[j].urb);
+ q->entry[j].urb = NULL;
+ }
}
}
{
int ret;
- mt76_worker_disable(&dev->tx_worker);
+ mt76_worker_disable(&dev->usb.status_worker);
ret = wait_event_timeout(dev->tx_wait, !mt76_has_tx_pending(&dev->phy),
HZ / 5);
dev_err(dev->dev, "timed out waiting for pending tx\n");
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
- q = dev->q_tx[i];
+ q = dev->phy.q_tx[i];
if (!q)
continue;
usb_kill_urb(q->entry[j].urb);
}
+ mt76_worker_disable(&dev->tx_worker);
+
/* 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 = dev->phy.q_tx[i];
if (!q)
continue;
- entry = q->entry[q->tail];
- q->entry[q->tail].done = false;
-
- mt76_queue_tx_complete(dev, q, &entry);
+ while (q->queued > 0) {
+ entry = q->entry[q->tail];
+ q->entry[q->tail].done = false;
+ mt76_queue_tx_complete(dev, q, &entry);
+ }
}
+
+ mt76_worker_enable(&dev->tx_worker);
}
cancel_work_sync(&dev->usb.stat_work);
clear_bit(MT76_READING_STATS, &dev->phy.state);
- mt76_worker_enable(&dev->tx_worker);
+ mt76_worker_enable(&dev->usb.status_worker);
mt76_tx_status_check(dev, NULL, true);
}
};
struct usb_device *udev = interface_to_usbdev(intf);
struct mt76_usb *usb = &dev->usb;
- int err = -ENOMEM;
+ int err;
mt76u_ops.rr = ext ? mt76u_rr_ext : mt76u_rr;
mt76u_ops.wr = ext ? mt76u_wr_ext : mt76u_wr;
mt76u_ops.rmw = ext ? mt76u_rmw_ext : mt76u_rmw;
mt76u_ops.write_copy = ext ? mt76u_copy_ext : mt76u_copy;
- dev->tx_worker.fn = mt76u_tx_worker;
- tasklet_init(&usb->rx_tasklet, mt76u_rx_tasklet, (unsigned long)dev);
INIT_WORK(&usb->stat_work, mt76u_tx_status_data);
usb->data_len = usb_maxpacket(udev, usb_sndctrlpipe(udev, 0), 1);
usb->data = devm_kmalloc(dev->dev, usb->data_len, GFP_KERNEL);
if (!usb->data)
- goto error;
+ return -ENOMEM;
mutex_init(&usb->usb_ctrl_mtx);
dev->bus = &mt76u_ops;
err = mt76u_set_endpoints(intf, usb);
if (err < 0)
- goto error;
+ return err;
- return 0;
+ err = mt76_worker_setup(dev->hw, &usb->rx_worker, mt76u_rx_worker,
+ "usb-rx");
+ if (err)
+ return err;
-error:
- destroy_workqueue(dev->wq);
+ err = mt76_worker_setup(dev->hw, &usb->status_worker,
+ mt76u_status_worker, "usb-status");
+ if (err)
+ return err;
- return err;
+ sched_set_fifo_low(usb->rx_worker.task);
+ sched_set_fifo_low(usb->status_worker.task);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(mt76u_init);