本节主要介绍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驱动框架(六)的更多相关文章

  1. USB摄像头驱动框架分析(五)

    一.USB摄像头驱动框架如下所示:1.构造一个usb_driver2.设置   probe:        2.1. 分配video_device:video_device_alloc        ...

  2. USB gadget 驱动 printer.c 分析

    1. modprobe g_printer idVendor=0x0525 idProduct=0xa4a8 modprobe后面也可以加模块参数 2. prn_example从stdout获取数据然 ...

  3. USB摄像头驱动框架分析

    usb摄像头驱动程序,里面涉及硬件的操作.比如说,想设置亮度的时候,需要把亮度的参数发给硬件.去得到真正视频数据的时候,需要访问硬件得到数据.usb摄像头驱动程序框架与虚拟摄像头驱动程序的框架是一样的 ...

  4. Linux USB ECM Gadget 驱动介绍

    ​1 USB ECM介绍 USB ECM,属于USB-IF定义的CDC(Communication Device Class)下的一个子类:Ethernet Networking Control Mo ...

  5. Linux下USB驱动框架分析【转】

    转自:http://blog.csdn.net/brucexu1978/article/details/17583407 版权声明:本文为博主原创文章,未经博主允许不得转载. http://www.c ...

  6. Linux usb子系统(一) _写一个usb鼠标驱动

    USB总线是一种典型的热插拔的总线标准,由于其优异的性能几乎成为了当下大小设备中的标配. USB的驱动可以分为3类:SoC的USB控制器的驱动,主机端USB设备的驱动,设备上的USB Gadget驱动 ...

  7. linux usb总线驱动(一)

    目录 linux usb总线驱动框架 USB 介绍 传输类型 控制器接口 2440接口 基本流程 alloc_dev choose_address hub_port_init usb_get_devi ...

  8. Linux 串口、usb转串口驱动分析(2-2) 【转】

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186852 Linux 串口.usb转 ...

  9. Linux 串口、usb转串口驱动分析(2-1) 【转】

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186851 Linux 串口.usb转 ...

  10. linux驱动基础系列--Linux 串口、usb转串口驱动分析

    前言 主要是想对Linux 串口.usb转串口驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如字符设备驱动.平台驱动等也不进行详细说明原理.如果有任何错误地方,请指出, ...

随机推荐

  1. 【IDEA】创建Maven工程

    当前工程,点new - project 选Maven,不需要点选什么骨架创建,骨架创建要下载大量依赖,生成时间太长, 空Maven的目的是让我们自己了解这个项目结构,需要什么依赖再加什么依赖 框线内的 ...

  2. 【IDEA】转大小写快速操作

    需求场景: 快速修改一些字符全部变成大写,或者小写 例如修改SQL语句,部分字段大写,部分字段小写,需要统一 快捷键: [Ctrl + Shift + U] 演示案例: SELECT ( (SELEC ...

  3. NVIDA GPU-SXM和NVIDA GPU-PCIe 两种类型显卡到底哪个性能更高?

    相关: 大模型时代该用什么样的显卡 -- 实验室新进两块A800显卡 浅析:NVIDA GPU卡SXM和PCIe之间的差异性 原来SXM类型的显卡比PCIex类型显卡性能要高.PCIE版本是通用接口, ...

  4. 外文论文同行评审平台——PubPeer——论文打假平台

    参考: https://baijiahao.baidu.com/s?id=1757051752090030001&wfr=spider&for=pc ================= ...

  5. python版本的两款NVIDIA显卡管理查询工具

    本文所述如题; 给出两个python版本的NVIDIA显卡管理查询工具 1.  py3nvml github下载地址: https://github.com/fbcotter/py3nvml Requ ...

  6. Python示例——负数的位运算

    平时在coding的时候虽然会遇到位运算但一般也都是正数的位运算,今天突然见到了使用负数的位运算,对此十分好奇和困惑,为此做了下了解,于是有了此文. 给出一些位运算的例子: 其中,正数的位运算是最为常 ...

  7. BossPlayersCTF靶机笔记

    BossPlayersCTF靶机 靶机概述 这是vulnhub上的一个简单的linux靶机,适合初级渗透测试人员,同时也告诉我们在渗透测试过程中要有耐心,要允许有兔子洞. 靶机整体思路: 主机端口探测 ...

  8. 【1】Kaggle赛题解读:RSNA 2024 Lumbar Spine Degenerative Classification

    赛题名称:RSNA 2024 Lumbar Spine Degenerative Classification 中文:腰椎退行性病变分类 kaggle官网赛题链接:https://www.kaggle ...

  9. Dialog封装的消息映射(弄了好久终于弄过了,不是静态函数哦,和MFC一样,嘻嘻)

    前面弄的是全局的仿消息映射,现在这是封装到类中的消息映射,一直弄不明白,现在也不太明白,就是今天在看虚函数表的用法视频时有位老师用了个共有体转化全局函数为类成员函数,这就给我指了条明路,这不今晚又来弄 ...

  10. java_GUI2

    package GUi;import java.awt.*;public class GuI2 { public static void main(String[] args) { MyFrame n ...