839 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			839 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
 | 
						|
 *
 | 
						|
 * Permission to use, copy, modify, and/or distribute this software for any
 | 
						|
 * purpose with or without fee is hereby granted, provided that the above
 | 
						|
 * copyright notice and this permission notice appear in all copies.
 | 
						|
 *
 | 
						|
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
						|
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
						|
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
						|
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
						|
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
						|
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
						|
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
						|
 */
 | 
						|
 | 
						|
#include "mt76.h"
 | 
						|
#include "usb_trace.h"
 | 
						|
#include "dma.h"
 | 
						|
 | 
						|
#define MT_VEND_REQ_MAX_RETRY	10
 | 
						|
#define MT_VEND_REQ_TOUT_MS	300
 | 
						|
 | 
						|
/* 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,
 | 
						|
				  void *buf, size_t len)
 | 
						|
{
 | 
						|
	struct usb_interface *intf = to_usb_interface(dev->dev);
 | 
						|
	struct usb_device *udev = interface_to_usbdev(intf);
 | 
						|
	unsigned int pipe;
 | 
						|
	int i, ret;
 | 
						|
 | 
						|
	pipe = (req_type & USB_DIR_IN) ? usb_rcvctrlpipe(udev, 0)
 | 
						|
				       : usb_sndctrlpipe(udev, 0);
 | 
						|
	for (i = 0; i < MT_VEND_REQ_MAX_RETRY; i++) {
 | 
						|
		if (test_bit(MT76_REMOVED, &dev->state))
 | 
						|
			return -EIO;
 | 
						|
 | 
						|
		ret = usb_control_msg(udev, pipe, req, req_type, val,
 | 
						|
				      offset, buf, len, MT_VEND_REQ_TOUT_MS);
 | 
						|
		if (ret == -ENODEV)
 | 
						|
			set_bit(MT76_REMOVED, &dev->state);
 | 
						|
		if (ret >= 0 || ret == -ENODEV)
 | 
						|
			return ret;
 | 
						|
		usleep_range(5000, 10000);
 | 
						|
	}
 | 
						|
 | 
						|
	dev_err(dev->dev, "vendor request req:%02x off:%04x failed:%d\n",
 | 
						|
		req, offset, ret);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
int mt76u_vendor_request(struct mt76_dev *dev, u8 req,
 | 
						|
			 u8 req_type, u16 val, u16 offset,
 | 
						|
			 void *buf, size_t len)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
 | 
						|
	mutex_lock(&dev->usb.usb_ctrl_mtx);
 | 
						|
	ret = __mt76u_vendor_request(dev, req, req_type,
 | 
						|
				     val, offset, buf, len);
 | 
						|
	trace_usb_reg_wr(dev, offset, val);
 | 
						|
	mutex_unlock(&dev->usb.usb_ctrl_mtx);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
EXPORT_SYMBOL_GPL(mt76u_vendor_request);
 | 
						|
 | 
						|
/* should be called with usb_ctrl_mtx locked */
 | 
						|
static u32 __mt76u_rr(struct mt76_dev *dev, u32 addr)
 | 
						|
{
 | 
						|
	struct mt76_usb *usb = &dev->usb;
 | 
						|
	u32 data = ~0;
 | 
						|
	u16 offset;
 | 
						|
	int ret;
 | 
						|
	u8 req;
 | 
						|
 | 
						|
	switch (addr & MT_VEND_TYPE_MASK) {
 | 
						|
	case MT_VEND_TYPE_EEPROM:
 | 
						|
		req = MT_VEND_READ_EEPROM;
 | 
						|
		break;
 | 
						|
	case MT_VEND_TYPE_CFG:
 | 
						|
		req = MT_VEND_READ_CFG;
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		req = MT_VEND_MULTI_READ;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
	offset = addr & ~MT_VEND_TYPE_MASK;
 | 
						|
 | 
						|
	ret = __mt76u_vendor_request(dev, req,
 | 
						|
				     USB_DIR_IN | USB_TYPE_VENDOR,
 | 
						|
				     0, offset, usb->data, sizeof(__le32));
 | 
						|
	if (ret == sizeof(__le32))
 | 
						|
		data = get_unaligned_le32(usb->data);
 | 
						|
	trace_usb_reg_rr(dev, addr, data);
 | 
						|
 | 
						|
	return data;
 | 
						|
}
 | 
						|
 | 
						|
u32 mt76u_rr(struct mt76_dev *dev, u32 addr)
 | 
						|
{
 | 
						|
	u32 ret;
 | 
						|
 | 
						|
	mutex_lock(&dev->usb.usb_ctrl_mtx);
 | 
						|
	ret = __mt76u_rr(dev, addr);
 | 
						|
	mutex_unlock(&dev->usb.usb_ctrl_mtx);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
/* should be called with usb_ctrl_mtx locked */
 | 
						|
static void __mt76u_wr(struct mt76_dev *dev, u32 addr, u32 val)
 | 
						|
{
 | 
						|
	struct mt76_usb *usb = &dev->usb;
 | 
						|
	u16 offset;
 | 
						|
	u8 req;
 | 
						|
 | 
						|
	switch (addr & MT_VEND_TYPE_MASK) {
 | 
						|
	case MT_VEND_TYPE_CFG:
 | 
						|
		req = MT_VEND_WRITE_CFG;
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		req = MT_VEND_MULTI_WRITE;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
	offset = addr & ~MT_VEND_TYPE_MASK;
 | 
						|
 | 
						|
	put_unaligned_le32(val, usb->data);
 | 
						|
	__mt76u_vendor_request(dev, req,
 | 
						|
			       USB_DIR_OUT | USB_TYPE_VENDOR, 0,
 | 
						|
			       offset, usb->data, sizeof(__le32));
 | 
						|
	trace_usb_reg_wr(dev, addr, val);
 | 
						|
}
 | 
						|
 | 
						|
void mt76u_wr(struct mt76_dev *dev, u32 addr, u32 val)
 | 
						|
{
 | 
						|
	mutex_lock(&dev->usb.usb_ctrl_mtx);
 | 
						|
	__mt76u_wr(dev, addr, val);
 | 
						|
	mutex_unlock(&dev->usb.usb_ctrl_mtx);
 | 
						|
}
 | 
						|
 | 
						|
static u32 mt76u_rmw(struct mt76_dev *dev, u32 addr,
 | 
						|
		     u32 mask, u32 val)
 | 
						|
{
 | 
						|
	mutex_lock(&dev->usb.usb_ctrl_mtx);
 | 
						|
	val |= __mt76u_rr(dev, addr) & ~mask;
 | 
						|
	__mt76u_wr(dev, addr, val);
 | 
						|
	mutex_unlock(&dev->usb.usb_ctrl_mtx);
 | 
						|
 | 
						|
	return val;
 | 
						|
}
 | 
						|
 | 
						|
static void mt76u_copy(struct mt76_dev *dev, u32 offset,
 | 
						|
		       const void *data, int len)
 | 
						|
{
 | 
						|
	struct mt76_usb *usb = &dev->usb;
 | 
						|
	const u32 *val = data;
 | 
						|
	int i, ret;
 | 
						|
 | 
						|
	mutex_lock(&usb->usb_ctrl_mtx);
 | 
						|
	for (i = 0; i < (len / 4); i++) {
 | 
						|
		put_unaligned_le32(val[i], usb->data);
 | 
						|
		ret = __mt76u_vendor_request(dev, MT_VEND_MULTI_WRITE,
 | 
						|
					     USB_DIR_OUT | USB_TYPE_VENDOR,
 | 
						|
					     0, offset + i * 4, usb->data,
 | 
						|
					     sizeof(__le32));
 | 
						|
		if (ret < 0)
 | 
						|
			break;
 | 
						|
	}
 | 
						|
	mutex_unlock(&usb->usb_ctrl_mtx);
 | 
						|
}
 | 
						|
 | 
						|
void mt76u_single_wr(struct mt76_dev *dev, const u8 req,
 | 
						|
		     const u16 offset, const u32 val)
 | 
						|
{
 | 
						|
	mutex_lock(&dev->usb.usb_ctrl_mtx);
 | 
						|
	__mt76u_vendor_request(dev, req,
 | 
						|
			       USB_DIR_OUT | USB_TYPE_VENDOR,
 | 
						|
			       val & 0xffff, offset, NULL, 0);
 | 
						|
	__mt76u_vendor_request(dev, req,
 | 
						|
			       USB_DIR_OUT | USB_TYPE_VENDOR,
 | 
						|
			       val >> 16, offset + 2, NULL, 0);
 | 
						|
	mutex_unlock(&dev->usb.usb_ctrl_mtx);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL_GPL(mt76u_single_wr);
 | 
						|
 | 
						|
static int
 | 
						|
mt76u_set_endpoints(struct usb_interface *intf,
 | 
						|
		    struct mt76_usb *usb)
 | 
						|
{
 | 
						|
	struct usb_host_interface *intf_desc = intf->cur_altsetting;
 | 
						|
	struct usb_endpoint_descriptor *ep_desc;
 | 
						|
	int i, in_ep = 0, out_ep = 0;
 | 
						|
 | 
						|
	for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) {
 | 
						|
		ep_desc = &intf_desc->endpoint[i].desc;
 | 
						|
 | 
						|
		if (usb_endpoint_is_bulk_in(ep_desc) &&
 | 
						|
		    in_ep < __MT_EP_IN_MAX) {
 | 
						|
			usb->in_ep[in_ep] = usb_endpoint_num(ep_desc);
 | 
						|
			usb->in_max_packet = usb_endpoint_maxp(ep_desc);
 | 
						|
			in_ep++;
 | 
						|
		} else if (usb_endpoint_is_bulk_out(ep_desc) &&
 | 
						|
			   out_ep < __MT_EP_OUT_MAX) {
 | 
						|
			usb->out_ep[out_ep] = usb_endpoint_num(ep_desc);
 | 
						|
			usb->out_max_packet = usb_endpoint_maxp(ep_desc);
 | 
						|
			out_ep++;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (in_ep != __MT_EP_IN_MAX || out_ep != __MT_EP_OUT_MAX)
 | 
						|
		return -EINVAL;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
mt76u_fill_rx_sg(struct mt76_dev *dev, struct mt76u_buf *buf,
 | 
						|
		 int nsgs, int len, int sglen)
 | 
						|
{
 | 
						|
	struct urb *urb = buf->urb;
 | 
						|
	int i;
 | 
						|
 | 
						|
	for (i = 0; i < nsgs; i++) {
 | 
						|
		struct page *page;
 | 
						|
		void *data;
 | 
						|
		int offset;
 | 
						|
 | 
						|
		data = netdev_alloc_frag(len);
 | 
						|
		if (!data)
 | 
						|
			break;
 | 
						|
 | 
						|
		page = virt_to_head_page(data);
 | 
						|
		offset = data - page_address(page);
 | 
						|
		sg_set_page(&urb->sg[i], page, sglen, offset);
 | 
						|
	}
 | 
						|
 | 
						|
	if (i < nsgs) {
 | 
						|
		int j;
 | 
						|
 | 
						|
		for (j = nsgs; j < urb->num_sgs; j++)
 | 
						|
			skb_free_frag(sg_virt(&urb->sg[j]));
 | 
						|
		urb->num_sgs = i;
 | 
						|
	}
 | 
						|
 | 
						|
	urb->num_sgs = max_t(int, i, urb->num_sgs);
 | 
						|
	buf->len = urb->num_sgs * sglen,
 | 
						|
	sg_init_marker(urb->sg, urb->num_sgs);
 | 
						|
 | 
						|
	return i ? : -ENOMEM;
 | 
						|
}
 | 
						|
 | 
						|
int mt76u_buf_alloc(struct mt76_dev *dev, struct mt76u_buf *buf,
 | 
						|
		    int nsgs, int len, int sglen, gfp_t gfp)
 | 
						|
{
 | 
						|
	buf->urb = usb_alloc_urb(0, gfp);
 | 
						|
	if (!buf->urb)
 | 
						|
		return -ENOMEM;
 | 
						|
 | 
						|
	buf->urb->sg = devm_kcalloc(dev->dev, nsgs, sizeof(*buf->urb->sg),
 | 
						|
				    gfp);
 | 
						|
	if (!buf->urb->sg)
 | 
						|
		return -ENOMEM;
 | 
						|
 | 
						|
	sg_init_table(buf->urb->sg, nsgs);
 | 
						|
	buf->dev = dev;
 | 
						|
 | 
						|
	return mt76u_fill_rx_sg(dev, buf, nsgs, len, sglen);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL_GPL(mt76u_buf_alloc);
 | 
						|
 | 
						|
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]));
 | 
						|
	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)
 | 
						|
{
 | 
						|
	struct usb_interface *intf = to_usb_interface(dev->dev);
 | 
						|
	struct usb_device *udev = interface_to_usbdev(intf);
 | 
						|
	unsigned int pipe;
 | 
						|
 | 
						|
	if (dir == USB_DIR_IN)
 | 
						|
		pipe = usb_rcvbulkpipe(udev, dev->usb.in_ep[index]);
 | 
						|
	else
 | 
						|
		pipe = usb_sndbulkpipe(udev, dev->usb.out_ep[index]);
 | 
						|
 | 
						|
	usb_fill_bulk_urb(buf->urb, udev, pipe, NULL, buf->len,
 | 
						|
			  complete_fn, context);
 | 
						|
 | 
						|
	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)
 | 
						|
{
 | 
						|
	struct mt76u_buf *buf = NULL;
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	spin_lock_irqsave(&q->lock, flags);
 | 
						|
	if (q->queued > 0) {
 | 
						|
		buf = &q->entry[q->head].ubuf;
 | 
						|
		q->head = (q->head + 1) % q->ndesc;
 | 
						|
		q->queued--;
 | 
						|
	}
 | 
						|
	spin_unlock_irqrestore(&q->lock, flags);
 | 
						|
 | 
						|
	return buf;
 | 
						|
}
 | 
						|
 | 
						|
static int mt76u_get_rx_entry_len(u8 *data, u32 data_len)
 | 
						|
{
 | 
						|
	u16 dma_len, min_len;
 | 
						|
 | 
						|
	dma_len = get_unaligned_le16(data);
 | 
						|
	min_len = MT_DMA_HDR_LEN + MT_RX_RXWI_LEN +
 | 
						|
		  MT_FCE_INFO_LEN;
 | 
						|
 | 
						|
	if (data_len < min_len || WARN_ON(!dma_len) ||
 | 
						|
	    WARN_ON(dma_len + MT_DMA_HDR_LEN > data_len) ||
 | 
						|
	    WARN_ON(dma_len & 0x3))
 | 
						|
		return -EINVAL;
 | 
						|
	return dma_len;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
mt76u_process_rx_entry(struct mt76_dev *dev, struct urb *urb)
 | 
						|
{
 | 
						|
	struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
 | 
						|
	u8 *data = sg_virt(&urb->sg[0]);
 | 
						|
	int data_len, len, nsgs = 1;
 | 
						|
	struct sk_buff *skb;
 | 
						|
 | 
						|
	if (!test_bit(MT76_STATE_INITIALIZED, &dev->state))
 | 
						|
		return 0;
 | 
						|
 | 
						|
	len = mt76u_get_rx_entry_len(data, urb->actual_length);
 | 
						|
	if (len < 0)
 | 
						|
		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) {
 | 
						|
		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]),
 | 
						|
				urb->sg[nsgs].offset,
 | 
						|
				data_len, q->buf_size);
 | 
						|
		len -= data_len;
 | 
						|
		nsgs++;
 | 
						|
	}
 | 
						|
	dev->drv->rx_skb(dev, MT_RXQ_MAIN, skb);
 | 
						|
 | 
						|
	return nsgs;
 | 
						|
}
 | 
						|
 | 
						|
static void mt76u_complete_rx(struct urb *urb)
 | 
						|
{
 | 
						|
	struct mt76_dev *dev = urb->context;
 | 
						|
	struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	switch (urb->status) {
 | 
						|
	case -ECONNRESET:
 | 
						|
	case -ESHUTDOWN:
 | 
						|
	case -ENOENT:
 | 
						|
		return;
 | 
						|
	default:
 | 
						|
		dev_err(dev->dev, "rx urb failed: %d\n", urb->status);
 | 
						|
		/* fall through */
 | 
						|
	case 0:
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	spin_lock_irqsave(&q->lock, flags);
 | 
						|
	if (WARN_ONCE(q->entry[q->tail].ubuf.urb != urb, "rx urb mismatch"))
 | 
						|
		goto out;
 | 
						|
 | 
						|
	q->tail = (q->tail + 1) % q->ndesc;
 | 
						|
	q->queued++;
 | 
						|
	tasklet_schedule(&dev->usb.rx_tasklet);
 | 
						|
out:
 | 
						|
	spin_unlock_irqrestore(&q->lock, flags);
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
 | 
						|
	rcu_read_lock();
 | 
						|
 | 
						|
	while (true) {
 | 
						|
		buf = mt76u_get_next_rx_entry(q);
 | 
						|
		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));
 | 
						|
			if (err < 0)
 | 
						|
				break;
 | 
						|
		}
 | 
						|
		mt76u_submit_buf(dev, USB_DIR_IN, MT_EP_IN_PKT_RX,
 | 
						|
				 buf, GFP_ATOMIC,
 | 
						|
				 mt76u_complete_rx, dev);
 | 
						|
	}
 | 
						|
	mt76_rx_poll_complete(dev, MT_RXQ_MAIN, NULL);
 | 
						|
 | 
						|
	rcu_read_unlock();
 | 
						|
}
 | 
						|
 | 
						|
int mt76u_submit_rx_buffers(struct mt76_dev *dev)
 | 
						|
{
 | 
						|
	struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
 | 
						|
	unsigned long flags;
 | 
						|
	int i, err = 0;
 | 
						|
 | 
						|
	spin_lock_irqsave(&q->lock, flags);
 | 
						|
	for (i = 0; i < q->ndesc; i++) {
 | 
						|
		err = mt76u_submit_buf(dev, USB_DIR_IN, MT_EP_IN_PKT_RX,
 | 
						|
				       &q->entry[i].ubuf, GFP_ATOMIC,
 | 
						|
				       mt76u_complete_rx, dev);
 | 
						|
		if (err < 0)
 | 
						|
			break;
 | 
						|
	}
 | 
						|
	q->head = q->tail = 0;
 | 
						|
	q->queued = 0;
 | 
						|
	spin_unlock_irqrestore(&q->lock, flags);
 | 
						|
 | 
						|
	return err;
 | 
						|
}
 | 
						|
EXPORT_SYMBOL_GPL(mt76u_submit_rx_buffers);
 | 
						|
 | 
						|
static int mt76u_alloc_rx(struct mt76_dev *dev)
 | 
						|
{
 | 
						|
	struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
 | 
						|
	int i, err, nsgs;
 | 
						|
 | 
						|
	spin_lock_init(&q->lock);
 | 
						|
	q->entry = devm_kcalloc(dev->dev,
 | 
						|
				MT_NUM_RX_ENTRIES, sizeof(*q->entry),
 | 
						|
				GFP_KERNEL);
 | 
						|
	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);
 | 
						|
		if (err < 0)
 | 
						|
			return err;
 | 
						|
	}
 | 
						|
	q->ndesc = MT_NUM_RX_ENTRIES;
 | 
						|
 | 
						|
	return mt76u_submit_rx_buffers(dev);
 | 
						|
}
 | 
						|
 | 
						|
static void mt76u_free_rx(struct mt76_dev *dev)
 | 
						|
{
 | 
						|
	struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
 | 
						|
	int i;
 | 
						|
 | 
						|
	for (i = 0; i < q->ndesc; i++)
 | 
						|
		mt76u_buf_free(&q->entry[i].ubuf);
 | 
						|
}
 | 
						|
 | 
						|
static 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].ubuf.urb);
 | 
						|
}
 | 
						|
 | 
						|
int mt76u_skb_dma_info(struct sk_buff *skb, int port, u32 flags)
 | 
						|
{
 | 
						|
	struct sk_buff *iter, *last = skb;
 | 
						|
	u32 info, pad;
 | 
						|
 | 
						|
	/* Buffer layout:
 | 
						|
	 *	|   4B   | xfer len |      pad       |  4B  |
 | 
						|
	 *	| TXINFO | pkt/cmd  | zero pad to 4B | zero |
 | 
						|
	 *
 | 
						|
	 * length field of TXINFO should be set to 'xfer len'.
 | 
						|
	 */
 | 
						|
	info = FIELD_PREP(MT_TXD_INFO_LEN, round_up(skb->len, 4)) |
 | 
						|
	       FIELD_PREP(MT_TXD_INFO_DPORT, port) | flags;
 | 
						|
	put_unaligned_le32(info, skb_push(skb, sizeof(info)));
 | 
						|
 | 
						|
	pad = round_up(skb->len, 4) + 4 - skb->len;
 | 
						|
	skb_walk_frags(skb, iter) {
 | 
						|
		last = iter;
 | 
						|
		if (!iter->next) {
 | 
						|
			skb->data_len += pad;
 | 
						|
			skb->len += pad;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (unlikely(pad)) {
 | 
						|
		if (__skb_pad(last, pad, true))
 | 
						|
			return -ENOMEM;
 | 
						|
		__skb_put(last, pad);
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
EXPORT_SYMBOL_GPL(mt76u_skb_dma_info);
 | 
						|
 | 
						|
static void mt76u_tx_tasklet(unsigned long data)
 | 
						|
{
 | 
						|
	struct mt76_dev *dev = (struct mt76_dev *)data;
 | 
						|
	struct mt76u_buf *buf;
 | 
						|
	struct mt76_queue *q;
 | 
						|
	bool wake;
 | 
						|
	int i;
 | 
						|
 | 
						|
	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
 | 
						|
		q = &dev->q_tx[i];
 | 
						|
 | 
						|
		spin_lock_bh(&q->lock);
 | 
						|
		while (true) {
 | 
						|
			buf = &q->entry[q->head].ubuf;
 | 
						|
			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--;
 | 
						|
			}
 | 
						|
 | 
						|
			q->head = (q->head + 1) % q->ndesc;
 | 
						|
			q->queued--;
 | 
						|
		}
 | 
						|
		mt76_txq_schedule(dev, q);
 | 
						|
		wake = i < IEEE80211_NUM_ACS && q->queued < q->ndesc - 8;
 | 
						|
		if (!q->queued)
 | 
						|
			wake_up(&dev->tx_wait);
 | 
						|
 | 
						|
		spin_unlock_bh(&q->lock);
 | 
						|
 | 
						|
		if (!test_and_set_bit(MT76_READING_STATS, &dev->state))
 | 
						|
			ieee80211_queue_delayed_work(dev->hw,
 | 
						|
						     &dev->usb.stat_work,
 | 
						|
						     msecs_to_jiffies(10));
 | 
						|
 | 
						|
		if (wake)
 | 
						|
			ieee80211_wake_queue(dev->hw, i);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void mt76u_tx_status_data(struct work_struct *work)
 | 
						|
{
 | 
						|
	struct mt76_usb *usb;
 | 
						|
	struct mt76_dev *dev;
 | 
						|
	u8 update = 1;
 | 
						|
	u16 count = 0;
 | 
						|
 | 
						|
	usb = container_of(work, struct mt76_usb, stat_work.work);
 | 
						|
	dev = container_of(usb, struct mt76_dev, usb);
 | 
						|
 | 
						|
	while (true) {
 | 
						|
		if (test_bit(MT76_REMOVED, &dev->state))
 | 
						|
			break;
 | 
						|
 | 
						|
		if (!dev->drv->tx_status_data(dev, &update))
 | 
						|
			break;
 | 
						|
		count++;
 | 
						|
	}
 | 
						|
 | 
						|
	if (count && test_bit(MT76_STATE_RUNNING, &dev->state))
 | 
						|
		ieee80211_queue_delayed_work(dev->hw, &usb->stat_work,
 | 
						|
					     msecs_to_jiffies(10));
 | 
						|
	else
 | 
						|
		clear_bit(MT76_READING_STATS, &dev->state);
 | 
						|
}
 | 
						|
 | 
						|
static void mt76u_complete_tx(struct urb *urb)
 | 
						|
{
 | 
						|
	struct mt76u_buf *buf = urb->context;
 | 
						|
	struct mt76_dev *dev = buf->dev;
 | 
						|
 | 
						|
	if (mt76u_urb_error(urb))
 | 
						|
		dev_err(dev->dev, "tx urb failed: %d\n", urb->status);
 | 
						|
	buf->done = true;
 | 
						|
 | 
						|
	tasklet_schedule(&dev->usb.tx_tasklet);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
mt76u_tx_build_sg(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;
 | 
						|
 | 
						|
	return skb_to_sgvec_nomark(skb, urb->sg, 0, skb->len);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
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)
 | 
						|
		return -ENOSPC;
 | 
						|
 | 
						|
	err = dev->drv->tx_prepare_skb(dev, NULL, skb, q, wcid, sta, NULL);
 | 
						|
	if (err < 0)
 | 
						|
		return err;
 | 
						|
 | 
						|
	buf = &q->entry[idx].ubuf;
 | 
						|
	buf->done = false;
 | 
						|
 | 
						|
	err = mt76u_tx_build_sg(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);
 | 
						|
 | 
						|
	q->tail = (q->tail + 1) % q->ndesc;
 | 
						|
	q->entry[idx].skb = skb;
 | 
						|
	q->queued++;
 | 
						|
 | 
						|
	return idx;
 | 
						|
}
 | 
						|
 | 
						|
static void mt76u_tx_kick(struct mt76_dev *dev, struct mt76_queue *q)
 | 
						|
{
 | 
						|
	struct mt76u_buf *buf;
 | 
						|
	int err;
 | 
						|
 | 
						|
	while (q->first != q->tail) {
 | 
						|
		buf = &q->entry[q->first].ubuf;
 | 
						|
		err = usb_submit_urb(buf->urb, GFP_ATOMIC);
 | 
						|
		if (err < 0) {
 | 
						|
			if (err == -ENODEV)
 | 
						|
				set_bit(MT76_REMOVED, &dev->state);
 | 
						|
			else
 | 
						|
				dev_err(dev->dev, "tx urb submit failed:%d\n",
 | 
						|
					err);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		q->first = (q->first + 1) % q->ndesc;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
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);
 | 
						|
		INIT_LIST_HEAD(&q->swq);
 | 
						|
		q->hw_idx = q2hwq(i);
 | 
						|
 | 
						|
		q->entry = devm_kcalloc(dev->dev,
 | 
						|
					MT_NUM_TX_ENTRIES, sizeof(*q->entry),
 | 
						|
					GFP_KERNEL);
 | 
						|
		if (!q->entry)
 | 
						|
			return -ENOMEM;
 | 
						|
 | 
						|
		q->ndesc = MT_NUM_TX_ENTRIES;
 | 
						|
		for (j = 0; j < q->ndesc; j++) {
 | 
						|
			buf = &q->entry[j].ubuf;
 | 
						|
			buf->dev = dev;
 | 
						|
 | 
						|
			buf->urb = usb_alloc_urb(0, GFP_KERNEL);
 | 
						|
			if (!buf->urb)
 | 
						|
				return -ENOMEM;
 | 
						|
 | 
						|
			buf->urb->sg = devm_kzalloc(dev->dev, size, GFP_KERNEL);
 | 
						|
			if (!buf->urb->sg)
 | 
						|
				return -ENOMEM;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void mt76u_free_tx(struct mt76_dev *dev)
 | 
						|
{
 | 
						|
	struct mt76_queue *q;
 | 
						|
	int i, j;
 | 
						|
 | 
						|
	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
 | 
						|
		q = &dev->q_tx[i];
 | 
						|
		for (j = 0; j < q->ndesc; j++)
 | 
						|
			usb_free_urb(q->entry[j].ubuf.urb);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void mt76u_stop_tx(struct mt76_dev *dev)
 | 
						|
{
 | 
						|
	struct mt76_queue *q;
 | 
						|
	int i, j;
 | 
						|
 | 
						|
	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
 | 
						|
		q = &dev->q_tx[i];
 | 
						|
		for (j = 0; j < q->ndesc; j++)
 | 
						|
			usb_kill_urb(q->entry[j].ubuf.urb);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void mt76u_stop_queues(struct mt76_dev *dev)
 | 
						|
{
 | 
						|
	tasklet_disable(&dev->usb.rx_tasklet);
 | 
						|
	tasklet_disable(&dev->usb.tx_tasklet);
 | 
						|
 | 
						|
	mt76u_stop_rx(dev);
 | 
						|
	mt76u_stop_tx(dev);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL_GPL(mt76u_stop_queues);
 | 
						|
 | 
						|
void mt76u_stop_stat_wk(struct mt76_dev *dev)
 | 
						|
{
 | 
						|
	cancel_delayed_work_sync(&dev->usb.stat_work);
 | 
						|
	clear_bit(MT76_READING_STATS, &dev->state);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL_GPL(mt76u_stop_stat_wk);
 | 
						|
 | 
						|
void mt76u_queues_deinit(struct mt76_dev *dev)
 | 
						|
{
 | 
						|
	mt76u_stop_queues(dev);
 | 
						|
 | 
						|
	mt76u_free_rx(dev);
 | 
						|
	mt76u_free_tx(dev);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL_GPL(mt76u_queues_deinit);
 | 
						|
 | 
						|
int mt76u_alloc_queues(struct mt76_dev *dev)
 | 
						|
{
 | 
						|
	int err;
 | 
						|
 | 
						|
	err = mt76u_alloc_rx(dev);
 | 
						|
	if (err < 0)
 | 
						|
		return err;
 | 
						|
 | 
						|
	return mt76u_alloc_tx(dev);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL_GPL(mt76u_alloc_queues);
 | 
						|
 | 
						|
static const struct mt76_queue_ops usb_queue_ops = {
 | 
						|
	.tx_queue_skb = mt76u_tx_queue_skb,
 | 
						|
	.kick = mt76u_tx_kick,
 | 
						|
};
 | 
						|
 | 
						|
int mt76u_init(struct mt76_dev *dev,
 | 
						|
	       struct usb_interface *intf)
 | 
						|
{
 | 
						|
	static const struct mt76_bus_ops mt76u_ops = {
 | 
						|
		.rr = mt76u_rr,
 | 
						|
		.wr = mt76u_wr,
 | 
						|
		.rmw = mt76u_rmw,
 | 
						|
		.copy = mt76u_copy,
 | 
						|
	};
 | 
						|
	struct mt76_usb *usb = &dev->usb;
 | 
						|
 | 
						|
	tasklet_init(&usb->rx_tasklet, mt76u_rx_tasklet, (unsigned long)dev);
 | 
						|
	tasklet_init(&usb->tx_tasklet, mt76u_tx_tasklet, (unsigned long)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;
 | 
						|
 | 
						|
	return mt76u_set_endpoints(intf, usb);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL_GPL(mt76u_init);
 | 
						|
 | 
						|
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>");
 | 
						|
MODULE_LICENSE("Dual BSD/GPL");
 |