diff options
Diffstat (limited to 'drivers/net/wireless/mediatek/mt76/usb.c')
-rw-r--r-- | drivers/net/wireless/mediatek/mt76/usb.c | 210 |
1 files changed, 121 insertions, 89 deletions
diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c index b061263453d4..ae6ada370597 100644 --- a/drivers/net/wireless/mediatek/mt76/usb.c +++ b/drivers/net/wireless/mediatek/mt76/usb.c @@ -22,6 +22,10 @@ #define MT_VEND_REQ_MAX_RETRY 10 #define MT_VEND_REQ_TOUT_MS 300 +static bool disable_usb_sg; +module_param_named(disable_usb_sg, disable_usb_sg, bool, 0644); +MODULE_PARM_DESC(disable_usb_sg, "Disable usb scatter-gather support"); + /* should be called with usb_ctrl_mtx locked */ static int __mt76u_vendor_request(struct mt76_dev *dev, u8 req, u8 req_type, u16 val, u16 offset, @@ -241,6 +245,16 @@ mt76u_rd_rp(struct mt76_dev *dev, u32 base, return mt76u_req_rd_rp(dev, base, data, n); } +static bool mt76u_check_sg(struct mt76_dev *dev) +{ + struct usb_interface *intf = to_usb_interface(dev->dev); + struct usb_device *udev = interface_to_usbdev(intf); + + return (!disable_usb_sg && udev->bus->sg_tablesize > 0 && + (udev->bus->no_sg_constraint || + udev->speed == USB_SPEED_WIRELESS)); +} + static int mt76u_set_endpoints(struct usb_interface *intf, struct mt76_usb *usb) @@ -309,42 +323,66 @@ mt76u_fill_rx_sg(struct mt76_dev *dev, struct mt76u_buf *buf, return i ? : -ENOMEM; } -int mt76u_buf_alloc(struct mt76_dev *dev, struct mt76u_buf *buf, - int nsgs, int len, int sglen, gfp_t gfp) +static int +mt76u_refill_rx(struct mt76_dev *dev, struct mt76_queue *q, + struct mt76u_buf *buf, int nsgs, gfp_t gfp) +{ + if (dev->usb.sg_en) { + return mt76u_fill_rx_sg(dev, buf, nsgs, q->buf_size, + SKB_WITH_OVERHEAD(q->buf_size)); + } else { + buf->buf = page_frag_alloc(&q->rx_page, q->buf_size, gfp); + return buf->buf ? 0 : -ENOMEM; + } +} + +static int +mt76u_buf_alloc(struct mt76_dev *dev, struct mt76u_buf *buf) { - buf->urb = usb_alloc_urb(0, gfp); + struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN]; + + buf->len = SKB_WITH_OVERHEAD(q->buf_size); + buf->dev = dev; + + buf->urb = usb_alloc_urb(0, GFP_KERNEL); if (!buf->urb) return -ENOMEM; - buf->urb->sg = devm_kcalloc(dev->dev, nsgs, sizeof(*buf->urb->sg), - gfp); - if (!buf->urb->sg) - return -ENOMEM; + if (dev->usb.sg_en) { + buf->urb->sg = devm_kcalloc(dev->dev, MT_SG_MAX_SIZE, + sizeof(*buf->urb->sg), + GFP_KERNEL); + if (!buf->urb->sg) + return -ENOMEM; - sg_init_table(buf->urb->sg, nsgs); - buf->dev = dev; + sg_init_table(buf->urb->sg, MT_SG_MAX_SIZE); + } - return mt76u_fill_rx_sg(dev, buf, nsgs, len, sglen); + return mt76u_refill_rx(dev, q, buf, MT_SG_MAX_SIZE, GFP_KERNEL); } -EXPORT_SYMBOL_GPL(mt76u_buf_alloc); -void mt76u_buf_free(struct mt76u_buf *buf) +static void mt76u_buf_free(struct mt76u_buf *buf) { struct urb *urb = buf->urb; int i; for (i = 0; i < urb->num_sgs; i++) skb_free_frag(sg_virt(&urb->sg[i])); + + if (buf->buf) + skb_free_frag(buf->buf); + usb_free_urb(buf->urb); } -EXPORT_SYMBOL_GPL(mt76u_buf_free); -int mt76u_submit_buf(struct mt76_dev *dev, int dir, int index, - struct mt76u_buf *buf, gfp_t gfp, - usb_complete_t complete_fn, void *context) +static void +mt76u_fill_bulk_urb(struct mt76_dev *dev, int dir, int index, + struct mt76u_buf *buf, usb_complete_t complete_fn, + void *context) { struct usb_interface *intf = to_usb_interface(dev->dev); struct usb_device *udev = interface_to_usbdev(intf); + u8 *data = buf->urb->num_sgs ? NULL : buf->buf; unsigned int pipe; if (dir == USB_DIR_IN) @@ -352,13 +390,21 @@ int mt76u_submit_buf(struct mt76_dev *dev, int dir, int index, else pipe = usb_sndbulkpipe(udev, dev->usb.out_ep[index]); - usb_fill_bulk_urb(buf->urb, udev, pipe, NULL, buf->len, + usb_fill_bulk_urb(buf->urb, udev, pipe, data, buf->len, complete_fn, context); +} + +static int +mt76u_submit_buf(struct mt76_dev *dev, int dir, int index, + struct mt76u_buf *buf, gfp_t gfp, + usb_complete_t complete_fn, void *context) +{ + mt76u_fill_bulk_urb(dev, dir, index, buf, complete_fn, + context); trace_submit_urb(dev, buf->urb); return usb_submit_urb(buf->urb, gfp); } -EXPORT_SYMBOL_GPL(mt76u_submit_buf); static inline struct mt76u_buf *mt76u_get_next_rx_entry(struct mt76_queue *q) @@ -393,10 +439,11 @@ static int mt76u_get_rx_entry_len(u8 *data, u32 data_len) } static int -mt76u_process_rx_entry(struct mt76_dev *dev, struct urb *urb) +mt76u_process_rx_entry(struct mt76_dev *dev, struct mt76u_buf *buf) { struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN]; - u8 *data = sg_virt(&urb->sg[0]); + struct urb *urb = buf->urb; + u8 *data = urb->num_sgs ? sg_virt(&urb->sg[0]) : buf->buf; int data_len, len, nsgs = 1; struct sk_buff *skb; @@ -407,21 +454,20 @@ mt76u_process_rx_entry(struct mt76_dev *dev, struct urb *urb) if (len < 0) return 0; + data_len = urb->num_sgs ? urb->sg[0].length : buf->len; + data_len = min_t(int, len, data_len - MT_DMA_HDR_LEN); + if (MT_DMA_HDR_LEN + data_len > SKB_WITH_OVERHEAD(q->buf_size)) + return 0; + skb = build_skb(data, q->buf_size); if (!skb) return 0; - data_len = min_t(int, len, urb->sg[0].length - MT_DMA_HDR_LEN); skb_reserve(skb, MT_DMA_HDR_LEN); - if (skb->tail + data_len > skb->end) { - dev_kfree_skb(skb); - return 1; - } - __skb_put(skb, data_len); len -= data_len; - while (len > 0) { + while (len > 0 && nsgs < urb->num_sgs) { data_len = min_t(int, len, urb->sg[nsgs].length); skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, sg_page(&urb->sg[nsgs]), @@ -449,7 +495,8 @@ static void mt76u_complete_rx(struct urb *urb) case -ENOENT: return; default: - dev_err(dev->dev, "rx urb failed: %d\n", urb->status); + dev_err_ratelimited(dev->dev, "rx urb failed: %d\n", + urb->status); /* fall through */ case 0: break; @@ -470,8 +517,8 @@ static void mt76u_rx_tasklet(unsigned long data) { struct mt76_dev *dev = (struct mt76_dev *)data; struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN]; - int err, nsgs, buf_len = q->buf_size; struct mt76u_buf *buf; + int err, count; rcu_read_lock(); @@ -480,11 +527,10 @@ static void mt76u_rx_tasklet(unsigned long data) if (!buf) break; - nsgs = mt76u_process_rx_entry(dev, buf->urb); - if (nsgs > 0) { - err = mt76u_fill_rx_sg(dev, buf, nsgs, - buf_len, - SKB_WITH_OVERHEAD(buf_len)); + count = mt76u_process_rx_entry(dev, buf); + if (count > 0) { + err = mt76u_refill_rx(dev, q, buf, count, + GFP_ATOMIC); if (err < 0) break; } @@ -521,8 +567,13 @@ EXPORT_SYMBOL_GPL(mt76u_submit_rx_buffers); static int mt76u_alloc_rx(struct mt76_dev *dev) { + struct mt76_usb *usb = &dev->usb; struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN]; - int i, err, nsgs; + int i, err; + + usb->mcu.data = devm_kmalloc(dev->dev, MCU_RESP_URB_SIZE, GFP_KERNEL); + if (!usb->mcu.data) + return -ENOMEM; spin_lock_init(&q->rx_page_lock); spin_lock_init(&q->lock); @@ -532,23 +583,13 @@ static int mt76u_alloc_rx(struct mt76_dev *dev) if (!q->entry) return -ENOMEM; - if (mt76u_check_sg(dev)) { - q->buf_size = MT_RX_BUF_SIZE; - nsgs = MT_SG_MAX_SIZE; - } else { - q->buf_size = PAGE_SIZE; - nsgs = 1; - } - - for (i = 0; i < MT_NUM_RX_ENTRIES; i++) { - err = mt76u_buf_alloc(dev, &q->entry[i].ubuf, - nsgs, q->buf_size, - SKB_WITH_OVERHEAD(q->buf_size), - GFP_KERNEL); + q->buf_size = dev->usb.sg_en ? MT_RX_BUF_SIZE : PAGE_SIZE; + q->ndesc = MT_NUM_RX_ENTRIES; + for (i = 0; i < q->ndesc; i++) { + err = mt76u_buf_alloc(dev, &q->entry[i].ubuf); if (err < 0) return err; } - q->ndesc = MT_NUM_RX_ENTRIES; return mt76u_submit_rx_buffers(dev); } @@ -585,6 +626,7 @@ static void mt76u_stop_rx(struct mt76_dev *dev) static void mt76u_tx_tasklet(unsigned long data) { struct mt76_dev *dev = (struct mt76_dev *)data; + struct mt76_queue_entry entry; struct mt76u_buf *buf; struct mt76_queue *q; bool wake; @@ -599,17 +641,18 @@ static void mt76u_tx_tasklet(unsigned long data) if (!buf->done || !q->queued) break; - dev->drv->tx_complete_skb(dev, q, - &q->entry[q->head], - false); - if (q->entry[q->head].schedule) { q->entry[q->head].schedule = false; q->swq_queued--; } + entry = q->entry[q->head]; q->head = (q->head + 1) % q->ndesc; q->queued--; + + spin_unlock_bh(&q->lock); + dev->drv->tx_complete_skb(dev, q, &entry, false); + spin_lock_bh(&q->lock); } mt76_txq_schedule(dev, q); wake = i < IEEE80211_NUM_ACS && q->queued < q->ndesc - 8; @@ -667,21 +710,15 @@ static void mt76u_complete_tx(struct urb *urb) } static int -mt76u_tx_build_sg(struct sk_buff *skb, struct urb *urb) +mt76u_tx_build_sg(struct mt76_dev *dev, struct sk_buff *skb, + struct urb *urb) { - int nsgs = 1 + skb_shinfo(skb)->nr_frags; - struct sk_buff *iter; - - skb_walk_frags(skb, iter) - nsgs += 1 + skb_shinfo(iter)->nr_frags; - - memset(urb->sg, 0, sizeof(*urb->sg) * MT_SG_MAX_SIZE); - - nsgs = min_t(int, MT_SG_MAX_SIZE, nsgs); - sg_init_marker(urb->sg, nsgs); - urb->num_sgs = nsgs; + if (!dev->usb.sg_en) + return 0; - return skb_to_sgvec_nomark(skb, urb->sg, 0, skb->len); + sg_init_table(urb->sg, MT_SG_MAX_SIZE); + urb->num_sgs = skb_to_sgvec(skb, urb->sg, 0, skb->len); + return urb->num_sgs; } static int @@ -689,12 +726,8 @@ 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 usb_interface *intf = to_usb_interface(dev->dev); - struct usb_device *udev = interface_to_usbdev(intf); - u8 ep = q2ep(q->hw_idx); struct mt76u_buf *buf; u16 idx = q->tail; - unsigned int pipe; int err; if (q->queued == q->ndesc) @@ -706,15 +739,16 @@ mt76u_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q, return err; buf = &q->entry[idx].ubuf; + buf->buf = skb->data; + buf->len = skb->len; buf->done = false; - err = mt76u_tx_build_sg(skb, buf->urb); + err = mt76u_tx_build_sg(dev, skb, buf->urb); if (err < 0) return err; - pipe = usb_sndbulkpipe(udev, dev->usb.out_ep[ep]); - usb_fill_bulk_urb(buf->urb, udev, pipe, NULL, skb->len, - mt76u_complete_tx, buf); + mt76u_fill_bulk_urb(dev, USB_DIR_OUT, q2ep(q->hw_idx), + buf, mt76u_complete_tx, buf); q->tail = (q->tail + 1) % q->ndesc; q->entry[idx].skb = skb; @@ -749,10 +783,8 @@ static int mt76u_alloc_tx(struct mt76_dev *dev) { struct mt76u_buf *buf; struct mt76_queue *q; - size_t size; int i, j; - size = MT_SG_MAX_SIZE * sizeof(struct scatterlist); for (i = 0; i < IEEE80211_NUM_ACS; i++) { q = &dev->q_tx[i]; spin_lock_init(&q->lock); @@ -774,9 +806,15 @@ static int mt76u_alloc_tx(struct mt76_dev *dev) if (!buf->urb) return -ENOMEM; - buf->urb->sg = devm_kzalloc(dev->dev, size, GFP_KERNEL); - if (!buf->urb->sg) - return -ENOMEM; + if (dev->usb.sg_en) { + size_t size = MT_SG_MAX_SIZE * + sizeof(struct scatterlist); + + buf->urb->sg = devm_kzalloc(dev->dev, size, + GFP_KERNEL); + if (!buf->urb->sg) + return -ENOMEM; + } } } return 0; @@ -838,16 +876,9 @@ int mt76u_alloc_queues(struct mt76_dev *dev) err = mt76u_alloc_rx(dev); if (err < 0) - goto err; - - err = mt76u_alloc_tx(dev); - if (err < 0) - goto err; + return err; - return 0; -err: - mt76u_queues_deinit(dev); - return err; + return mt76u_alloc_tx(dev); } EXPORT_SYMBOL_GPL(mt76u_alloc_queues); @@ -875,13 +906,14 @@ int mt76u_init(struct mt76_dev *dev, INIT_DELAYED_WORK(&usb->stat_work, mt76u_tx_status_data); skb_queue_head_init(&dev->rx_skb[MT_RXQ_MAIN]); - init_completion(&usb->mcu.cmpl); mutex_init(&usb->mcu.mutex); mutex_init(&usb->usb_ctrl_mtx); dev->bus = &mt76u_ops; dev->queue_ops = &usb_queue_ops; + usb->sg_en = mt76u_check_sg(dev); + return mt76u_set_endpoints(intf, usb); } EXPORT_SYMBOL_GPL(mt76u_init); |