本节主要介绍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