首页 > 其他分享 >USB gadget驱动框架(六)

USB gadget驱动框架(六)

时间:2024-09-17 15:48:02浏览次数:9  
标签:USB 框架 gadget req value udc dev ep

本节主要介绍udc驱动枚举过程,需结合udc驱动、composite.c、function等一同分析整个过程。

udc驱动中断处理函数

当host检测到DP上拉,则认为有新的device插入,此时host将发起进入枚举流程,整个枚举流程大部分是在中断函数中处理,协议对时间有相关的要求,因此整个枚举流程是不能加入过多的调式信息,否则将会影响到枚举的时序。

本文基于Linux4.19.123-s3c2410_udc.c进行分析,中断函数具体如下:

/*
 *	s3c2410_udc_irq - interrupt handler
 */
static irqreturn_t s3c2410_udc_irq(int dummy, void *_dev)
{
	struct s3c2410_udc *dev = _dev;
	int usb_status;
	int usbd_status;
	int pwr_reg;
	int ep0csr;
	int i;
	u32 idx, idx2;
	unsigned long flags;

	spin_lock_irqsave(&dev->lock, flags);

	/* Driver connected ? */
	if (!dev->driver) {
		/* Clear interrupts */
		udc_write(udc_read(S3C2410_UDC_USB_INT_REG),
				S3C2410_UDC_USB_INT_REG);
		udc_write(udc_read(S3C2410_UDC_EP_INT_REG),
				S3C2410_UDC_EP_INT_REG);
	}

	/* Save index */
	idx = udc_read(S3C2410_UDC_INDEX_REG);

	/* Read status registers */
	usb_status = udc_read(S3C2410_UDC_USB_INT_REG);
	usbd_status = udc_read(S3C2410_UDC_EP_INT_REG);
	pwr_reg = udc_read(S3C2410_UDC_PWR_REG);

	udc_writeb(base_addr, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
	ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG);

	dprintk(DEBUG_NORMAL, "usbs=%02x, usbds=%02x, pwr=%02x ep0csr=%02x\n",
		usb_status, usbd_status, pwr_reg, ep0csr);

	/*
	 * Now, handle interrupts. There's two types :
	 * - Reset, Resume, Suspend coming -> usb_int_reg
	 * - EP -> ep_int_reg
	 */

	/* RESET */
	//reset中断,将复位udc,后续正式进入枚举过程
	if (usb_status & S3C2410_UDC_USBINT_RESET) {
		/* two kind of reset :
		 * - reset start -> pwr reg = 8
		 * - reset end   -> pwr reg = 0
		 **/
		dprintk(DEBUG_NORMAL, "USB reset csr %x pwr %x\n",
			ep0csr, pwr_reg);

		dev->gadget.speed = USB_SPEED_UNKNOWN;
		udc_write(0x00, S3C2410_UDC_INDEX_REG);
		udc_write((dev->ep[0].ep.maxpacket & 0x7ff) >> 3,
				S3C2410_UDC_MAXP_REG);
		dev->address = 0;

		dev->ep0state = EP0_IDLE;
		dev->gadget.speed = USB_SPEED_FULL;

		/* clear interrupt */
		udc_write(S3C2410_UDC_USBINT_RESET,
				S3C2410_UDC_USB_INT_REG);

		udc_write(idx, S3C2410_UDC_INDEX_REG);
		spin_unlock_irqrestore(&dev->lock, flags);
		return IRQ_HANDLED;
	}

	/* RESUME */
	if (usb_status & S3C2410_UDC_USBINT_RESUME) {
		dprintk(DEBUG_NORMAL, "USB resume\n");

		/* clear interrupt */
		udc_write(S3C2410_UDC_USBINT_RESUME,
				S3C2410_UDC_USB_INT_REG);

		if (dev->gadget.speed != USB_SPEED_UNKNOWN
				&& dev->driver
				&& dev->driver->resume)
			dev->driver->resume(&dev->gadget);
	}

	/* SUSPEND */
	if (usb_status & S3C2410_UDC_USBINT_SUSPEND) {
		dprintk(DEBUG_NORMAL, "USB suspend\n");

		/* clear interrupt */
		udc_write(S3C2410_UDC_USBINT_SUSPEND,
				S3C2410_UDC_USB_INT_REG);

		if (dev->gadget.speed != USB_SPEED_UNKNOWN
				&& dev->driver
				&& dev->driver->suspend)
			dev->driver->suspend(&dev->gadget);

		dev->ep0state = EP0_IDLE;
	}

	/* EP */
	/* control traffic */
	/* check on ep0csr != 0 is not a good idea as clearing in_pkt_ready
	 * generate an interrupt
	 */
	//ep0中断,由于ep0是控制端点,枚举过程中传输的信息由ep0完成
	if (usbd_status & S3C2410_UDC_INT_EP0) {
		dprintk(DEBUG_VERBOSE, "USB ep0 irq\n");
		/* Clear the interrupt bit by setting it to 1 */
		udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_REG);
		s3c2410_udc_handle_ep0(dev);
	}

    //其他epx的中断
	/* endpoint data transfers */
	for (i = 1; i < S3C2410_ENDPOINTS; i++) {
		u32 tmp = 1 << i;
		if (usbd_status & tmp) {
			dprintk(DEBUG_VERBOSE, "USB ep%d irq\n", i);

			/* Clear the interrupt bit by setting it to 1 */
			udc_write(tmp, S3C2410_UDC_EP_INT_REG);
			s3c2410_udc_handle_ep(&dev->ep[i]);
		}
	}

	/* what else causes this interrupt? a receive! who is it? */
	if (!usb_status && !usbd_status && !pwr_reg && !ep0csr) {
		for (i = 1; i < S3C2410_ENDPOINTS; i++) {
			idx2 = udc_read(S3C2410_UDC_INDEX_REG);
			udc_write(i, S3C2410_UDC_INDEX_REG);

			if (udc_read(S3C2410_UDC_OUT_CSR1_REG) & 0x1)
				s3c2410_udc_handle_ep(&dev->ep[i]);

			/* restore index */
			udc_write(idx2, S3C2410_UDC_INDEX_REG);
		}
	}

	dprintk(DEBUG_VERBOSE, "irq: %d s3c2410_udc_done.\n", IRQ_USBD);

	/* Restore old index */
	udc_write(idx, S3C2410_UDC_INDEX_REG);

	spin_unlock_irqrestore(&dev->lock, flags);

	return IRQ_HANDLED;
}

ep0中断处理函数

static void s3c2410_udc_handle_ep0(struct s3c2410_udc *dev)
{
	u32			ep0csr;
	struct s3c2410_ep	*ep = &dev->ep[0];
	struct s3c2410_request	*req;
	struct usb_ctrlrequest	crq;

    //从ep的queue中取出一个req进行处理
	if (list_empty(&ep->queue))
		req = NULL;
	else
		req = list_entry(ep->queue.next, struct s3c2410_request, queue);

	/* We make the assumption that S3C2410_UDC_IN_CSR1_REG equal to
	 * S3C2410_UDC_EP0_CSR_REG when index is zero */

	udc_write(0, S3C2410_UDC_INDEX_REG);
	ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG);

	dprintk(DEBUG_NORMAL, "ep0csr %x ep0state %s\n",
		ep0csr, ep0states[dev->ep0state]);

	/* clear stall status */
	if (ep0csr & S3C2410_UDC_EP0_CSR_SENTSTL) {
		s3c2410_udc_nuke(dev, ep, -EPIPE);
		dprintk(DEBUG_NORMAL, "... clear SENT_STALL ...\n");
		s3c2410_udc_clear_ep0_sst(base_addr);
		dev->ep0state = EP0_IDLE;
		return;
	}

	/* clear setup end */
	if (ep0csr & S3C2410_UDC_EP0_CSR_SE) {
		dprintk(DEBUG_NORMAL, "... serviced SETUP_END ...\n");
		s3c2410_udc_nuke(dev, ep, 0);
		s3c2410_udc_clear_ep0_se(base_addr);
		dev->ep0state = EP0_IDLE;
	}

	switch (dev->ep0state) {
	case EP0_IDLE:
		s3c2410_udc_handle_ep0_idle(dev, ep, &crq, ep0csr);
		break;

	case EP0_IN_DATA_PHASE:			/* GET_DESCRIPTOR etc */
		dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n");
		if (!(ep0csr & S3C2410_UDC_EP0_CSR_IPKRDY) && req)
			s3c2410_udc_write_fifo(ep, req);
		break;

	case EP0_OUT_DATA_PHASE:		/* SET_DESCRIPTOR etc */
		dprintk(DEBUG_NORMAL, "EP0_OUT_DATA_PHASE ... what now?\n");
		if ((ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) && req)
			s3c2410_udc_read_fifo(ep, req);
		break;

	case EP0_END_XFER:
		dprintk(DEBUG_NORMAL, "EP0_END_XFER ... what now?\n");
		dev->ep0state = EP0_IDLE;
		break;

	case EP0_STALL:
		dprintk(DEBUG_NORMAL, "EP0_STALL ... what now?\n");
		dev->ep0state = EP0_IDLE;
		break;
	}
}


static void s3c2410_udc_handle_ep0_idle(struct s3c2410_udc *dev,
					struct s3c2410_ep *ep,
					struct usb_ctrlrequest *crq,
					u32 ep0csr)
{
	int len, ret, tmp;

	/* start control request? */
	if (!(ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY))
		return;

	s3c2410_udc_nuke(dev, ep, -EPROTO);

    //读取usb_ctrlrequest信息
	len = s3c2410_udc_read_fifo_crq(crq);
	if (len != sizeof(*crq)) {
		dprintk(DEBUG_NORMAL, "setup begin: fifo READ ERROR"
			" wanted %d bytes got %d. Stalling out...\n",
			sizeof(*crq), len);
		s3c2410_udc_set_ep0_ss(base_addr);
		return;
	}

	dprintk(DEBUG_NORMAL, "bRequest = %d bRequestType %d wLength = %d\n",
		crq->bRequest, crq->bRequestType, crq->wLength);

	/* cope with automagic for some standard requests. */
	dev->req_std = (crq->bRequestType & USB_TYPE_MASK)
		== USB_TYPE_STANDARD;
	dev->req_config = 0;
	dev->req_pending = 1;

	switch (crq->bRequest) {
	case USB_REQ_SET_CONFIGURATION:
		dprintk(DEBUG_NORMAL, "USB_REQ_SET_CONFIGURATION ...\n");

		if (crq->bRequestType == USB_RECIP_DEVICE) {
			dev->req_config = 1;
			s3c2410_udc_set_ep0_de_out(base_addr);
		}
		break;

	case USB_REQ_SET_INTERFACE:
		dprintk(DEBUG_NORMAL, "USB_REQ_SET_INTERFACE ...\n");

		if (crq->bRequestType == USB_RECIP_INTERFACE) {
			dev->req_config = 1;
			s3c2410_udc_set_ep0_de_out(base_addr);
		}
		break;

	case USB_REQ_SET_ADDRESS:
		dprintk(DEBUG_NORMAL, "USB_REQ_SET_ADDRESS ...\n");

		if (crq->bRequestType == USB_RECIP_DEVICE) {
			tmp = crq->wValue & 0x7F;
			dev->address = tmp;
			udc_write((tmp | S3C2410_UDC_FUNCADDR_UPDATE),
					S3C2410_UDC_FUNC_ADDR_REG);
			s3c2410_udc_set_ep0_de_out(base_addr);
			return;
		}
		break;

	case USB_REQ_GET_STATUS:
		dprintk(DEBUG_NORMAL, "USB_REQ_GET_STATUS ...\n");
		s3c2410_udc_clear_ep0_opr(base_addr);

		if (dev->req_std) {
			if (!s3c2410_udc_get_status(dev, crq))
				return;
		}
		break;

	case USB_REQ_CLEAR_FEATURE:
		s3c2410_udc_clear_ep0_opr(base_addr);

		if (crq->bRequestType != USB_RECIP_ENDPOINT)
			break;

		if (crq->wValue != USB_ENDPOINT_HALT || crq->wLength != 0)
			break;

		s3c2410_udc_set_halt(&dev->ep[crq->wIndex & 0x7f].ep, 0);
		s3c2410_udc_set_ep0_de_out(base_addr);
		return;

	case USB_REQ_SET_FEATURE:
		s3c2410_udc_clear_ep0_opr(base_addr);

		if (crq->bRequestType != USB_RECIP_ENDPOINT)
			break;

		if (crq->wValue != USB_ENDPOINT_HALT || crq->wLength != 0)
			break;

		s3c2410_udc_set_halt(&dev->ep[crq->wIndex & 0x7f].ep, 1);
		s3c2410_udc_set_ep0_de_out(base_addr);
		return;

	default:
		s3c2410_udc_clear_ep0_opr(base_addr);
		break;
	}

	if (crq->bRequestType & USB_DIR_IN)
		dev->ep0state = EP0_IN_DATA_PHASE;
	else
		dev->ep0state = EP0_OUT_DATA_PHASE;

	if (!dev->driver)
		return;

	/* deliver the request to the gadget driver */
	//调用usb_gadget_driver的setup函数,完成后续流程
	ret = dev->driver->setup(&dev->gadget, crq);
	if (ret < 0) {
		if (dev->req_config) {
			dprintk(DEBUG_NORMAL, "config change %02x fail %d?\n",
				crq->bRequest, ret);
			return;
		}

		if (ret == -EOPNOTSUPP)
			dprintk(DEBUG_NORMAL, "Operation not supported\n");
		else
			dprintk(DEBUG_NORMAL,
				"dev->driver->setup failed. (%d)\n", ret);

		udelay(5);
		s3c2410_udc_set_ep0_ss(base_addr);
		s3c2410_udc_set_ep0_de_out(base_addr);
		dev->ep0state = EP0_IDLE;
		/* deferred i/o == no response yet */
	} else if (dev->req_pending) {
		dprintk(DEBUG_VERBOSE, "dev->req_pending... what now?\n");
		dev->req_pending = 0;
	}

	dprintk(DEBUG_VERBOSE, "ep0state %s\n", ep0states[dev->ep0state]);
}

epx中断处理函数

/*
 *	handle_ep - Manage I/O endpoints
 */

static void s3c2410_udc_handle_ep(struct s3c2410_ep *ep)
{
	struct s3c2410_request	*req;
	int			is_in = ep->bEndpointAddress & USB_DIR_IN;
	u32			ep_csr1;
	u32			idx;

    //从ep的queue中取出一个req进行处理
	if (likely(!list_empty(&ep->queue)))
		req = list_entry(ep->queue.next,
				struct s3c2410_request, queue);
	else
		req = NULL;

	idx = ep->bEndpointAddress & 0x7F;

	if (is_in) {
		udc_write(idx, S3C2410_UDC_INDEX_REG);
		ep_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG);
		dprintk(DEBUG_VERBOSE, "ep%01d write csr:%02x %d\n",
			idx, ep_csr1, req ? 1 : 0);

		if (ep_csr1 & S3C2410_UDC_ICSR1_SENTSTL) {
			dprintk(DEBUG_VERBOSE, "st\n");
			udc_write(idx, S3C2410_UDC_INDEX_REG);
			udc_write(ep_csr1 & ~S3C2410_UDC_ICSR1_SENTSTL,
					S3C2410_UDC_IN_CSR1_REG);
			return;
		}

        //发送数据到host
		if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req)
			s3c2410_udc_write_fifo(ep, req);
	} else {
		udc_write(idx, S3C2410_UDC_INDEX_REG);
		ep_csr1 = udc_read(S3C2410_UDC_OUT_CSR1_REG);
		dprintk(DEBUG_VERBOSE, "ep%01d rd csr:%02x\n", idx, ep_csr1);

		if (ep_csr1 & S3C2410_UDC_OCSR1_SENTSTL) {
			udc_write(idx, S3C2410_UDC_INDEX_REG);
			udc_write(ep_csr1 & ~S3C2410_UDC_OCSR1_SENTSTL,
					S3C2410_UDC_OUT_CSR1_REG);
			return;
		}

        //读取host所发送的数据
		if ((ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req)
			s3c2410_udc_read_fifo(ep, req);
	}
}

epx的queue处理

/*
 *	s3c2410_udc_queue
 */
static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req,
		gfp_t gfp_flags)
{
	struct s3c2410_request	*req = to_s3c2410_req(_req);
	struct s3c2410_ep	*ep = to_s3c2410_ep(_ep);
	struct s3c2410_udc	*dev;
	u32			ep_csr = 0;
	int			fifo_count = 0;
	unsigned long		flags;

	if (unlikely(!_ep || (!ep->ep.desc && ep->ep.name != ep0name))) {
		dprintk(DEBUG_NORMAL, "%s: invalid args\n", __func__);
		return -EINVAL;
	}

	dev = ep->dev;
	if (unlikely(!dev->driver
			|| dev->gadget.speed == USB_SPEED_UNKNOWN)) {
		return -ESHUTDOWN;
	}

	local_irq_save(flags);

	if (unlikely(!_req || !_req->complete
			|| !_req->buf || !list_empty(&req->queue))) {
		if (!_req)
			dprintk(DEBUG_NORMAL, "%s: 1 X X X\n", __func__);
		else {
			dprintk(DEBUG_NORMAL, "%s: 0 %01d %01d %01d\n",
				__func__, !_req->complete, !_req->buf,
				!list_empty(&req->queue));
		}

		local_irq_restore(flags);
		return -EINVAL;
	}

	_req->status = -EINPROGRESS;
	_req->actual = 0;

	dprintk(DEBUG_VERBOSE, "%s: ep%x len %d\n",
		 __func__, ep->bEndpointAddress, _req->length);

	if (ep->bEndpointAddress) {
		udc_write(ep->bEndpointAddress & 0x7F, S3C2410_UDC_INDEX_REG);

		ep_csr = udc_read((ep->bEndpointAddress & USB_DIR_IN)
				? S3C2410_UDC_IN_CSR1_REG
				: S3C2410_UDC_OUT_CSR1_REG);
		fifo_count = s3c2410_udc_fifo_count_out();
	} else {
		udc_write(0, S3C2410_UDC_INDEX_REG);
		ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
		fifo_count = s3c2410_udc_fifo_count_out();
	}

	/* kickstart this i/o queue? */
	if (list_empty(&ep->queue) && !ep->halted) {
		if (ep->bEndpointAddress == 0 /* ep0 */) {
			switch (dev->ep0state) {
			case EP0_IN_DATA_PHASE:
				if (!(ep_csr&S3C2410_UDC_EP0_CSR_IPKRDY)
						&& s3c2410_udc_write_fifo(ep,
							req)) {
					dev->ep0state = EP0_IDLE;
					req = NULL;
				}
				break;

			case EP0_OUT_DATA_PHASE:
				if ((!_req->length)
					|| ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY)
						&& s3c2410_udc_read_fifo(ep,
							req))) {
					dev->ep0state = EP0_IDLE;
					req = NULL;
				}
				break;

			default:
				local_irq_restore(flags);
				return -EL2HLT;
			}
		} else if ((ep->bEndpointAddress & USB_DIR_IN) != 0
				&& (!(ep_csr&S3C2410_UDC_OCSR1_PKTRDY))
				&& s3c2410_udc_write_fifo(ep, req)) {
			req = NULL;
		} else if ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY)
				&& fifo_count
				&& s3c2410_udc_read_fifo(ep, req)) {
			req = NULL;
		}
	}

    //将新的req插入到ep queue的队列尾部
	/* pio or dma irq handler advances the queue. */
	if (likely(req))
		list_add_tail(&req->queue, &ep->queue);

	local_irq_restore(flags);

	dprintk(DEBUG_VERBOSE, "%s ok\n", __func__);
	return 0;
}
/*
 *	s3c2410_udc_dequeue
 */
static int s3c2410_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
	struct s3c2410_ep	*ep = to_s3c2410_ep(_ep);
	struct s3c2410_udc	*udc;
	int			retval = -EINVAL;
	unsigned long		flags;
	struct s3c2410_request	*req = NULL;

	dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _ep, _req);

	if (!the_controller->driver)
		return -ESHUTDOWN;

	if (!_ep || !_req)
		return retval;

	udc = to_s3c2410_udc(ep->gadget);

	local_irq_save(flags);

    //将当前req重ep queue中移出
	list_for_each_entry(req, &ep->queue, queue) {
		if (&req->req == _req) {
			list_del_init(&req->queue);
			_req->status = -ECONNRESET;
			retval = 0;
			break;
		}
	}

	if (retval == 0) {
		dprintk(DEBUG_VERBOSE,
			"dequeued req %p from %s, len %d buf %p\n",
			req, _ep->name, _req->length, _req->buf);

		s3c2410_udc_done(ep, req, -ECONNRESET);
	}

	local_irq_restore(flags);
	return retval;
}

usb_gadget_driver的composite_setup

/*
 * The setup() callback implements all the ep0 functionality that's
 * not handled lower down, in hardware or the hardware driver(like
 * device and endpoint feature flags, and their status).  It's all
 * housekeeping for the gadget function we're implementing.  Most of
 * the work is in config and function specific setup.
 */
int
composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
{
	struct usb_composite_dev	*cdev = get_gadget_data(gadget);
	struct usb_request		*req = cdev->req;
	int				value = -EOPNOTSUPP;
	int				status = 0;
	u16				w_index = le16_to_cpu(ctrl->wIndex);
	u8				intf = w_index & 0xFF;
	u16				w_value = le16_to_cpu(ctrl->wValue);
	u16				w_length = le16_to_cpu(ctrl->wLength);
	struct usb_function		*f = NULL;
	u8				endp;

	/* partial re-init of the response message; the function or the
	 * gadget might need to intercept e.g. a control-OUT completion
	 * when we delegate to it.
	 */
	req->zero = 0;
	req->context = cdev;
	req->complete = composite_setup_complete;
	req->length = 0;
	gadget->ep0->driver_data = cdev;

	/*
	 * Don't let non-standard requests match any of the cases below
	 * by accident.
	 */
	if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
		goto unknown;

	switch (ctrl->bRequest) {

	/* we handle all standard USB descriptors */
	//获取描述符,在bind过程中已完成描述符的初始化
	case USB_REQ_GET_DESCRIPTOR:
		if (ctrl->bRequestType != USB_DIR_IN)
			goto unknown;
		switch (w_value >> 8) {

        //获取设备描述符
		case USB_DT_DEVICE:
			cdev->desc.bNumConfigurations =
				count_configs(cdev, USB_DT_DEVICE);
			cdev->desc.bMaxPacketSize0 =
				cdev->gadget->ep0->maxpacket;
			if (gadget_is_superspeed(gadget)) {
				if (gadget->speed >= USB_SPEED_SUPER) {
					cdev->desc.bcdUSB = cpu_to_le16(0x0320);
					cdev->desc.bMaxPacketSize0 = 9;
				} else {
					cdev->desc.bcdUSB = cpu_to_le16(0x0210);
				}
			} else {
				if (gadget->lpm_capable)
					cdev->desc.bcdUSB = cpu_to_le16(0x0201);
				else
					cdev->desc.bcdUSB = cpu_to_le16(0x0200);
			}

			value = min(w_length, (u16) sizeof cdev->desc);
			memcpy(req->buf, &cdev->desc, value);
			break;
		case USB_DT_DEVICE_QUALIFIER:
			if (!gadget_is_dualspeed(gadget) ||
			    gadget->speed >= USB_SPEED_SUPER)
				break;
			device_qual(cdev);
			value = min_t(int, w_length,
				sizeof(struct usb_qualifier_descriptor));
			break;
		case USB_DT_OTHER_SPEED_CONFIG:
			if (!gadget_is_dualspeed(gadget) ||
			    gadget->speed >= USB_SPEED_SUPER)
				break;
			/* FALLTHROUGH */
		case USB_DT_CONFIG:
			value = config_desc(cdev, w_value);
			if (value >= 0)
				value = min(w_length, (u16) value);
			break;
		//获取字符串描述符
		case USB_DT_STRING:
			value = get_string(cdev, req->buf,
					w_index, w_value & 0xff);
			if (value >= 0)
				value = min(w_length, (u16) value);
			break;
		case USB_DT_BOS:
			if (gadget_is_superspeed(gadget) ||
			    gadget->lpm_capable) {
				value = bos_desc(cdev);
				value = min(w_length, (u16) value);
			}
			break;
		case USB_DT_OTG:
			if (gadget_is_otg(gadget)) {
				struct usb_configuration *config;
				int otg_desc_len = 0;

				if (cdev->config)
					config = cdev->config;
				else
					config = list_first_entry(
							&cdev->configs,
						struct usb_configuration, list);
				if (!config)
					goto done;

				if (gadget->otg_caps &&
					(gadget->otg_caps->otg_rev >= 0x0200))
					otg_desc_len += sizeof(
						struct usb_otg20_descriptor);
				else
					otg_desc_len += sizeof(
						struct usb_otg_descriptor);

				value = min_t(int, w_length, otg_desc_len);
				memcpy(req->buf, config->descriptors[0], value);
			}
			break;
		}
		break;

	/* any number of configs can work */
	//确认set configuration
	case USB_REQ_SET_CONFIGURATION:
		if (ctrl->bRequestType != 0)
			goto unknown;
		if (gadget_is_otg(gadget)) {
			if (gadget->a_hnp_support)
				DBG(cdev, "HNP available\n");
			else if (gadget->a_alt_hnp_support)
				DBG(cdev, "HNP on another port\n");
			else
				VDBG(cdev, "HNP inactive\n");
		}
		spin_lock(&cdev->lock);
		value = set_config(cdev, ctrl, w_value);
		spin_unlock(&cdev->lock);
		break;
	
	//获取configuration
	case USB_REQ_GET_CONFIGURATION:
		if (ctrl->bRequestType != USB_DIR_IN)
			goto unknown;
		if (cdev->config)
			*(u8 *)req->buf = cdev->config->bConfigurationValue;
		else
			*(u8 *)req->buf = 0;
		value = min(w_length, (u16) 1);
		break;

	/* function drivers must handle get/set altsetting */
	case USB_REQ_SET_INTERFACE:
		if (ctrl->bRequestType != USB_RECIP_INTERFACE)
			goto unknown;
		if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
			break;
		f = cdev->config->interface[intf];
		if (!f)
			break;

		/*
		 * If there's no get_alt() method, we know only altsetting zero
		 * works. There is no need to check if set_alt() is not NULL
		 * as we check this in usb_add_function().
		 */
		if (w_value && !f->get_alt)
			break;

		spin_lock(&cdev->lock);
		//调用usb_function的set_alt完成,非ep0的使能
		//详见drivers/usb/gadget/function/f_acm.c的acm_set_alt
		value = f->set_alt(f, w_index, w_value);
		if (value == USB_GADGET_DELAYED_STATUS) {
			DBG(cdev,
			 "%s: interface %d (%s) requested delayed status\n",
					__func__, intf, f->name);
			cdev->delayed_status++;
			DBG(cdev, "delayed_status count %d\n",
					cdev->delayed_status);
		}
		spin_unlock(&cdev->lock);
		break;
	case USB_REQ_GET_INTERFACE:
		if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
			goto unknown;
		if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
			break;
		f = cdev->config->interface[intf];
		if (!f)
			break;
		/* lots of interfaces only need altsetting zero... */
		value = f->get_alt ? f->get_alt(f, w_index) : 0;
		if (value < 0)
			break;
		*((u8 *)req->buf) = value;
		value = min(w_length, (u16) 1);
		break;
	case USB_REQ_GET_STATUS:
		if (gadget_is_otg(gadget) && gadget->hnp_polling_support &&
						(w_index == OTG_STS_SELECTOR)) {
			if (ctrl->bRequestType != (USB_DIR_IN |
							USB_RECIP_DEVICE))
				goto unknown;
			*((u8 *)req->buf) = gadget->host_request_flag;
			value = 1;
			break;
		}

		/*
		 * USB 3.0 additions:
		 * Function driver should handle get_status request. If such cb
		 * wasn't supplied we respond with default value = 0
		 * Note: function driver should supply such cb only for the
		 * first interface of the function
		 */
		if (!gadget_is_superspeed(gadget))
			goto unknown;
		if (ctrl->bRequestType != (USB_DIR_IN | USB_RECIP_INTERFACE))
			goto unknown;
		value = 2;	/* This is the length of the get_status reply */
		put_unaligned_le16(0, req->buf);
		if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
			break;
		f = cdev->config->interface[intf];
		if (!f)
			break;
		status = f->get_status ? f->get_status(f) : 0;
		if (status < 0)
			break;
		put_unaligned_le16(status & 0x0000ffff, req->buf);
		break;
	/*
	 * Function drivers should handle SetFeature/ClearFeature
	 * (FUNCTION_SUSPEND) request. function_suspend cb should be supplied
	 * only for the first interface of the function
	 */
	case USB_REQ_CLEAR_FEATURE:
	case USB_REQ_SET_FEATURE:
		if (!gadget_is_superspeed(gadget))
			goto unknown;
		if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_INTERFACE))
			goto unknown;
		switch (w_value) {
		case USB_INTRF_FUNC_SUSPEND:
			if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
				break;
			f = cdev->config->interface[intf];
			if (!f)
				break;
			value = 0;
			if (f->func_suspend)
				value = f->func_suspend(f, w_index >> 8);
			if (value < 0) {
				ERROR(cdev,
				      "func_suspend() returned error %d\n",
				      value);
				value = 0;
			}
			break;
		}
		break;
	default:
unknown:
		/*
		 * OS descriptors handling
		 */
		if (cdev->use_os_string && cdev->os_desc_config &&
		    (ctrl->bRequestType & USB_TYPE_VENDOR) &&
		    ctrl->bRequest == cdev->b_vendor_code) {
			struct usb_configuration	*os_desc_cfg;
			u8				*buf;
			int				interface;
			int				count = 0;

			req = cdev->os_desc_req;
			req->context = cdev;
			req->complete = composite_setup_complete;
			buf = req->buf;
			os_desc_cfg = cdev->os_desc_config;
			w_length = min_t(u16, w_length, USB_COMP_EP0_OS_DESC_BUFSIZ);
			memset(buf, 0, w_length);
			buf[5] = 0x01;
			switch (ctrl->bRequestType & USB_RECIP_MASK) {
			case USB_RECIP_DEVICE:
				if (w_index != 0x4 || (w_value >> 8))
					break;
				buf[6] = w_index;
				/* Number of ext compat interfaces */
				count = count_ext_compat(os_desc_cfg);
				buf[8] = count;
				count *= 24; /* 24 B/ext compat desc */
				count += 16; /* header */
				put_unaligned_le32(count, buf);
				value = w_length;
				if (w_length > 0x10) {
					value = fill_ext_compat(os_desc_cfg, buf);
					value = min_t(u16, w_length, value);
				}
				break;
			case USB_RECIP_INTERFACE:
				if (w_index != 0x5 || (w_value >> 8))
					break;
				interface = w_value & 0xFF;
				buf[6] = w_index;
				count = count_ext_prop(os_desc_cfg,
					interface);
				put_unaligned_le16(count, buf + 8);
				count = len_ext_prop(os_desc_cfg,
					interface);
				put_unaligned_le32(count, buf);
				value = w_length;
				if (w_length > 0x0A) {
					value = fill_ext_prop(os_desc_cfg,
							      interface, buf);
					if (value >= 0)
						value = min_t(u16, w_length, value);
				}
				break;
			}

			goto check_value;
		}

		VDBG(cdev,
			"non-core control req%02x.%02x v%04x i%04x l%d\n",
			ctrl->bRequestType, ctrl->bRequest,
			w_value, w_index, w_length);

		/* functions always handle their interfaces and endpoints...
		 * punt other recipients (other, WUSB, ...) to the current
		 * configuration code.
		 */
		if (cdev->config) {
			list_for_each_entry(f, &cdev->config->functions, list)
				if (f->req_match &&
				    f->req_match(f, ctrl, false))
					goto try_fun_setup;
		} else {
			struct usb_configuration *c;
			list_for_each_entry(c, &cdev->configs, list)
				list_for_each_entry(f, &c->functions, list)
					if (f->req_match &&
					    f->req_match(f, ctrl, true))
						goto try_fun_setup;
		}
		f = NULL;

		switch (ctrl->bRequestType & USB_RECIP_MASK) {
		case USB_RECIP_INTERFACE:
			if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
				break;
			f = cdev->config->interface[intf];
			break;

		case USB_RECIP_ENDPOINT:
			if (!cdev->config)
				break;
			endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f);
			list_for_each_entry(f, &cdev->config->functions, list) {
				if (test_bit(endp, f->endpoints))
					break;
			}
			if (&f->list == &cdev->config->functions)
				f = NULL;
			break;
		}
try_fun_setup:
        //调用usb_function的setup函数完成后续的setup操作
        //虚拟串口无usb_configuration的setup函数,有usb_function的setup函数
        //详见:drivers/usb/gadget/function/f_acm.c的acm_setup
		if (f && f->setup)
			value = f->setup(f, ctrl);
		else {//调用usb_configuration的setup函数完成后续的setup操作
			struct usb_configuration	*c;

			c = cdev->config;
			if (!c)
				goto done;

			/* try current config's setup */
			if (c->setup) {
				value = c->setup(c, ctrl);
				goto done;
			}

			/* try the only function in the current config */
			if (!list_is_singular(&c->functions))
				goto done;
			f = list_first_entry(&c->functions, struct usb_function,
					     list);
			if (f->setup)
				value = f->setup(f, ctrl);
		}

		goto done;
	}

check_value:
	/* respond with data transfer before status phase? */
	if (value >= 0 && value != USB_GADGET_DELAYED_STATUS) {
		req->length = value;
		req->context = cdev;
		req->zero = value < w_length;
		//将当前req放入到ep0的queue中,并将数据发送给host
		value = composite_ep0_queue(cdev, req, GFP_ATOMIC);
		if (value < 0) {
			DBG(cdev, "ep_queue --> %d\n", value);
			req->status = 0;
			composite_setup_complete(gadget->ep0, req);
		}
	} else if (value == USB_GADGET_DELAYED_STATUS && w_length != 0) {
		WARN(cdev,
			"%s: Delayed status not supported for w_length != 0",
			__func__);
	}

done:
	/* device either stalls (value < 0) or reports success */
	return value;
}

usb_function的setup

static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
{
	struct f_acm		*acm = func_to_acm(f);
	struct usb_composite_dev *cdev = f->config->cdev;
	struct usb_request	*req = cdev->req;
	int			value = -EOPNOTSUPP;
	u16			w_index = le16_to_cpu(ctrl->wIndex);
	u16			w_value = le16_to_cpu(ctrl->wValue);
	u16			w_length = le16_to_cpu(ctrl->wLength);

	/* composite driver infrastructure handles everything except
	 * CDC class messages; interface activation uses set_alt().
	 *
	 * Note CDC spec table 4 lists the ACM request profile.  It requires
	 * encapsulated command support ... we don't handle any, and respond
	 * to them by stalling.  Options include get/set/clear comm features
	 * (not that useful) and SEND_BREAK.
	 */
	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {

	/* SET_LINE_CODING ... just read and save what the host sends */
	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
			| USB_CDC_REQ_SET_LINE_CODING:
		if (w_length != sizeof(struct usb_cdc_line_coding)
				|| w_index != acm->ctrl_id)
			goto invalid;

		value = w_length;
		cdev->gadget->ep0->driver_data = acm;
		req->complete = acm_complete_set_line_coding;
		break;

	/* GET_LINE_CODING ... return what host sent, or initial value */
	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
			| USB_CDC_REQ_GET_LINE_CODING:
		if (w_index != acm->ctrl_id)
			goto invalid;

		value = min_t(unsigned, w_length,
				sizeof(struct usb_cdc_line_coding));
		memcpy(req->buf, &acm->port_line_coding, value);
		break;

	/* SET_CONTROL_LINE_STATE ... save what the host sent */
	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
			| USB_CDC_REQ_SET_CONTROL_LINE_STATE:
		if (w_index != acm->ctrl_id)
			goto invalid;

		value = 0;

		/* FIXME we should not allow data to flow until the
		 * host sets the ACM_CTRL_DTR bit; and when it clears
		 * that bit, we should return to that no-flow state.
		 */
		acm->port_handshake_bits = w_value;
		break;

	default:
invalid:
		dev_vdbg(&cdev->gadget->dev,
			 "invalid control req%02x.%02x v%04x i%04x l%d\n",
			 ctrl->bRequestType, ctrl->bRequest,
			 w_value, w_index, w_length);
	}

	/* respond with data transfer or status phase? */
	if (value >= 0) {
		dev_dbg(&cdev->gadget->dev,
			"acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n",
			acm->port_num, ctrl->bRequestType, ctrl->bRequest,
			w_value, w_index, w_length);
		req->zero = 0;
		req->length = value;
		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
		if (value < 0)
			ERROR(cdev, "acm response on ttyGS%d, err %d\n",
					acm->port_num, value);
	}

	/* device either stalls (value < 0) or reports success */
	return value;
}

标签:USB,框架,gadget,req,value,udc,dev,ep
From: https://www.cnblogs.com/linhaostudy/p/18417221

相关文章

  • USB gadget驱动框架(五)
    本节主要分析虚拟串口的tty设备的注册、创建/dev/ttyGSx设备节点、tty相关接口的实现。tty的申请与注册源码:drivers/usb/gadget/function/u_serial.cstaticconststructtty_operationsgs_tty_ops={ .open= gs_open, .close= gs_close, .write= gs_write, .put_......
  • VUE框架CLI组件化组件解绑事件实现------VUE框架
    <template> <div> <!--内置函数的实现步骤--> <!--提供事件源,给事件源绑定事件,编写回调函数,将回调函数和事件进行绑定--> <!--等待事件的触发,事件触发执行回调函数--> <!--组件的自定义事件实现步骤--> <button@click="Hello()">你好</button> <!--给Us......
  • 【工具推荐】Agently:一款灵活易用的 AI 应用开发框架
    本文内容均来自个人笔记并重新梳理,如有错误欢迎指正!如果对您有帮助,烦请点赞、关注、转发、订阅专栏!专栏订阅入口| 精选文章 | Kubernetes |Docker|Linux |羊毛资源 | 工具推荐 |往期精彩文章【Docker】(全网首发)KylinV10下MySQL容器内存占用异常的解决......
  • 基于springboot+vue的SringBoot框架的智慧博物馆预约平台的设计与实现
    前言......
  • Spring框架漏洞总结
    目录SpEL注入攻击SpringH2DatabaseConsole未授权访问SpringSecurityOAuth2远程命令执行漏洞(CVE-2016-4977)SpringWebFlow远程代码执行漏洞(CVE-2017-4971)SpringDataRest远程命令执行漏洞(CVE-2017-8046)SpringMessaging远程命令执行漏洞(CVE-2018-1270)SpringDataCom......
  • 计算机毕业设计选题推荐-基于PHP框架项目推荐(上)
    博主介绍:✌十余年IT大项目实战经验、在某机构培训学员上千名、专注于本行业领域✌技术范围:Java实战项目、Python实战项目、微信小程序/安卓实战项目、爬虫+大数据实战项目、Nodejs实战项目、PHP实战项目、.NET实战项目、Golang实战项目。主要内容:系统功能设计、开题报告......
  • 支持线程编排的并行框架AsyncTool
    它是由京东零售开源的项目,作者是天涯泪小武。如果大家想更深入理解可以去作者的博客看一下。为什么会学习这个框架最近在学习java并发中的CompletableFuture,它除了提供了更为好用和强大的Future特性之外,还提供了函数式编程、异步任务编排组合(可以将多个异步任务串联起来......
  • 60.《Java集合框架-List-Set-Map》
    此篇所写不知道你们是否在网页开发的时候当看到要写Map集合什么HashMap之类的突然蒙了虽然之前学过突然让你调用方法懵了所以在此总结一下以备后需对比数组可存储任意类型对象且存储长度是可以变的集合类乃内存层面对数据进行存储数据库是持久层断电后仍长期存在......
  • 超轻量级、支持插件的 .NET 网络通信框架
    超轻量级、支持插件的.NET网络通信框架在当今高度互联的世界中,高效、可靠的网络通信是构建各种应用程序的关键。无论是开发Web服务、实时通信应用,还是物联网设备,都需要一个强大且灵活的网络通信框架来支撑。然而,传统的网络通信框架往往过于臃肿,难以满足现代应用程序对性能和灵......
  • 基于AG32 的USB转以太网方案
    如何通过USB转以太网标准模块?AG32支持USBFS+OTG和以太网MAC,并且提供了标准例程,包括网络Lwip和USB的开发例程,上层应用调tinyUSB的接口即可。   以下是AG32VF407VG的引脚定义,支持USB外设。LQFP-100PinnameAG32VFxxxVAGRV2KL1001PIN_1IOIO2PIN_2IOIO3PIN_3IOIO4PIN_4......