前面介绍了Linux USB Gadget的软件结构与各软件层的整合过程。经过各种注册函数,Gadget功能驱动层,USB设备层与UDC底层结合在了一起形成了一个完整的USB设备。而这个设备已经准备好了接受主机的枚举。在介绍USB设备枚举之前。先熟悉一下各层通信所用的数据结构,在USB主机端编写USB设备驱动程序,最重要的结构就是URB了,我们只需要将各种URB提交给USB核心,核心就会自动给我们的数据发送到指定的设备。而对于设备端也有这样一个类似的重要的数据结构。这个数据结构就是urt--usb_request。每一个端点都有一个urt链表,上面挂着各种urt。在底层的UDC的中断处理程序中,针对不同的端点调用不同的处理函数,总之是处理端点上的urt链表,处理完一个urt就调用预先设置好的回调函数。这就是设备端数据处理的流程。下面分析一下usb_request结构:

struct usb_request {

void            *buf;

unsigned        length;

dma_addr_t      dma;

unsigned        no_interrupt:1;

unsigned        zero:1;

unsigned        short_not_ok:1;

void            (*complete)(struct usb_ep *ep,

struct usb_request *req);

void            *context;

struct list_head    list;

int         status;

unsigned        actual;

};struct usb_request {

void*buf;

unsignedlength;

dma_addr_tdma;

unsignedno_interrupt:1;

unsignedzero:1;

unsignedshort_not_ok:1;

void(*complete)(struct usb_ep *ep,

struct usb_request *req);

void*context;

struct list_headlist;

intstatus;

unsignedactual;

};

(1)buf 字段是要接受或者发送数据存储的地方,而length代表了数据的长度。

(2)dma 是dma_addr_t类型的,有DMA传输有关。虽然s3c2440的USB设备控制器支持DMA操作,但是底层UDC驱动没有实现,所以不用管这个字段了。

(3)三个位域分别代表了:

(4)(*complete)(struct usb_ep *ep, struct usb_request *req); 这个是回调函数,在端点处理完一个urt的时候调用,非常重要

(5)context

(6)list 作用是将自己链接在端点链表

(7)status 状态

(8)actual 实际传输的字节

一. USB设备枚举

分析完urt,那么就实际进入主机识别USB设备的最关键的设备枚举。这里主要分析设备怎么相应主机,对于主机究竟是怎么完成这些操作的还的找一种主机控制器来研究一下。首先先回顾一下USB设备枚举都要完成那些步骤吧:

(1)设备插入主机,主机检测到设备。复位设备

(2)主机向设备控制端点发送Get_Descriptor来了解设备默认管道的大小。

(3)主机指定一个地址,发送Set_Address标准请求设置设备的地址

(4)主机使用新的地址,再次发送Get_Descriptor或得各种描述符

(5)主机加载一个USB设备驱动

(6)USB设备驱动再发送Set_Confuration标准设备请求配置设备

以上就是USB设备枚举的过程。USB设备必须正确的相应主机的要求才能顺利的完成设备枚举。我们知道USB是主从式总线结构,全部通信都是由主机发起,设备没有一点自主权。s3c2440 USB设备控制器,当主机向USB设备发送一个包时,USB设备控制器就会产生相应的中断。当出现传输错误的时候,也会以中断的形式来通知。所以理解USB设备控制器的中断是理解USB通信过程的关键。在s3c2410_udc.c在设备初始化的时候已经注册了中断处理程序:

static irqreturn_t s3c2410_udc_irq(int dummy, void *_dev)

{

//这个是在设备控制器初始化的时候注册中断处理程序的语句:retval = request_irq(IRQ_USBD, s3c2410_udc_irq, IRQF_DISABLED, gadget_name, udc); 最后一个参数udc是一个struct s3c2410_udc

//的结构体,代表了一个USB设备控制器也就是s3c2410的设备控制器,这个结构体指针指向了一个已经初始化好了的struct s3c2410_udc变量。所以在中断处理程序开始在参数中提取了这个指针。

struct s3c2410_udc *dev = _dev;

int usb_status;

int usbd_status;

int pwr_reg;

int ep0csr;

int i;

u32 idx;

unsigned long flags;

//自旋锁,保护dev这个结构避免并发引起的竞态,因为是单处理器。这里的自旋锁退化成了一个禁止内核抢占的开关,上锁就是禁止内核抢占

spin_lock_irqsave(&dev->lock, flags);

/* Driver connected ? */

//当没有初始化好USB设备而发生中断时,清除中断标志

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);

}

//s3c2440 USB设备控制器,因为有五个端点,每个端点的寄存器都相似。所以硬件设计的时候将寄存器分组了,名称一样但是物理寄存器不同。S3C2410_UDC_INDEX_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

*/

//下面就是不同的中断处理,复位对应这设备枚举的(1)

/* RESET */

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

*/

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);

}

/* endpoint data transfers */

for (i = 1; i

u32 tmp = 1 <

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]);

}

}

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;

}static irqreturn_t s3c2410_udc_irq(int dummy, void *_dev)

{

//这个是在设备控制器初始化的时候注册中断处理程序的语句:retval = request_irq(IRQ_USBD, s3c2410_udc_irq, IRQF_DISABLED, gadget_name, udc); 最后一个参数udc是一个struct s3c2410_udc

//的结构体,代表了一个USB设备控制器也就是s3c2410的设备控制器,这个结构体指针指向了一个已经初始化好了的struct s3c2410_udc变量。所以在中断处理程序开始在参数中提取了这个指针。

struct s3c2410_udc *dev = _dev;

int usb_status;

int usbd_status;

int pwr_reg;

int ep0csr;

int i;

u32 idx;

unsigned long flags;

//自旋锁,保护dev这个结构避免并发引起的竞态,因为是单处理器。这里的自旋锁退化成了一个禁止内核抢占的开关,上锁就是禁止内核抢占

spin_lock_irqsave(&dev->lock, flags);

/* Driver connected ? */

//当没有初始化好USB设备而发生中断时,清除中断标志

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);

}

//s3c2440 USB设备控制器,因为有五个端点,每个端点的寄存器都相似。所以硬件设计的时候将寄存器分组了,名称一样但是物理寄存器不同。S3C2410_UDC_INDEX_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

*/

//下面就是不同的中断处理,复位对应这设备枚举的(1)

/* RESET */

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

*/

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);

}

/* 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]);

}

}

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;

}

这个函数根据不同的中断类型进行处理。设备枚举的时候USB设备控制器产生的中断都是端点0的,所以调用s3c2410_udc_handle_ep0(dev)函数。这个函数也定义在s3c2410_udc.c中,如下:

staticvoid 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;

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(struct s3c2410_udc *dev)

{

u32ep0csr;

struct s3c2410_ep*ep = &dev->ep[0];

struct s3c2410_request*req;

struct usb_ctrlrequestcrq;

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;

}

}

看这个函数之前,先要看一个struct s3c2410_ep这个数据结构,这个数据结构代表一个s3c2410 usb设备控制器的一个端点。结构如下:

struct s3c2410_ep {

struct list_head        queue;

unsigned long           last_io;    /* jiffies timestamp */

struct usb_gadget       *gadget;

struct s3c2410_udc      *dev;

conststruct usb_endpoint_descriptor *desc;

struct usb_ep           ep;

u8              num;

unsigned short          fifo_size;

u8              bEndpointAddress;

u8              bmAttributes;

unsigned            halted : 1;

unsigned            already_seen : 1;

unsigned            setup_stage : 1;

};struct s3c2410_ep {

struct list_headqueue;

unsigned longlast_io;/* jiffies timestamp */

struct usb_gadget*gadget;

struct s3c2410_udc*dev;

const struct usb_endpoint_descriptor *desc;

struct usb_epep;

u8num;

unsigned shortfifo_size;

u8bEndpointAddress;

u8bmAttributes;

unsignedhalted : 1;

unsignedalready_seen : 1;

unsignedsetup_stage : 1;

};

这结构的成员一部分是在定义的时候静态初始化的,端点0在定义的时候如下初始化:

.ep[0] = {

.num        = 0,

.ep = {

.name       = ep0name,

.ops        = &s3c2410_ep_ops,

.maxpacket  = EP0_FIFO_SIZE,

},

.dev        = &memory,

},.ep[0] = {

.num= 0,

.ep = {

.name= ep0name,

.ops= &s3c2410_ep_ops,

.maxpacket= EP0_FIFO_SIZE,

},

.dev= &memory,

},

看完struct s3c2410_ep结构后,再来分析s3c2410_udc_handle_ep0这个函数。函数首先判断ep的queue是否为空,这个字段链接了struct s3c2410_request结构的链表,struct s3c2410_request是struct usb_request的简单封装,代表一个一次usb传输。如果不为空那么,取到这个成员赋值给req变量。接下来读取端点0的状态寄存器EP0_CSR,这个寄存器反映了端点0的状态。将端点0的状态读到ep0csr局部变量中。这时在中断处理程序中。按照USB设备枚举的过程,最先发生的中断是复位。然后USB主机就会发起一次控制传输来获得设备描述符。这个控制传输是Get_Descriptor标准设备请求。我们知道USB控制传输有数据传输的分为三个阶段,没有数据传输的分为两个阶段。而Get_Descriptor是有数据传输的,USB设备要返回设备描述符号。所以有三个阶段:分别是建立阶段,数据阶段,状态阶段。建立阶段分为三个USB数据包:分别是setup包,data包,与握手包。当建立阶段完毕后,data包的数据会写入端点0的FIFO,s3c2410 USB设备控制器就会产生中断,对应的EP0_CSR的SETUP_END 位就会置位。这时可以判断这个状态。然后调用相应的函数读取在FIFO的数据,判断是控制传输的类型,然后针对不同的类型采取不同的操作,或接受数据,或发送数据。现在针对Get_Descriptor这个USB标准请求来分析一下s3c2410_udc_handle_ep0函数中代码的执行:

(1)首先,建立阶段完成中断,执行如下代码:

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;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;

}

(2)然后,因为dev->ep0state = EP0_IDLE所以执行如下代码:

case EP0_IDLE:

s3c2410_udc_handle_ep0_idle(dev, ep, &crq, ep0csr);

break;case EP0_IDLE:

s3c2410_udc_handle_ep0_idle(dev, ep, &crq, ep0csr);

break;

s3c2410_udc_handle_ep0_idle函数也定义在s3c2410_udc.c中,如下:

staticvoid 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);

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;

ret = dev->driver->setup(&dev->gadget, crq);

if (ret

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 */

} elseif (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]);

}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);

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;

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]);

}

这个函数所做的主要工作就是读取端点0 FIFO中的数据,这里的数据就是控制传输的类型。然后通过一个switch语句来判断到底是什么控制传输。根据控制传输的不同类型采用不同的操作。这里我们假设的是Get_Descriptor。那么switch语句都不执行,执行下面的语句,这些是针对有数据传输的控制传输。Get_Descriptor的方向是IN,所以dev->ep0state = EP0_IN_DATA_PHASE设备端点状态。然后执行下面的预计 ret = dev->driver->setup(&dev->gadget, crq);在这里dev->driver->setup已经初始化好了是composite_setup,在composite.c中定义,如下:

staticint

composite_setup(struct usb_gadget *gadget, conststruct usb_ctrlrequest *ctrl)

{

struct usb_composite_dev    *cdev = get_gadget_data(gadget);

struct usb_request      *req = cdev->req;

int             value = -EOPNOTSUPP;

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;

/* 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->complete = composite_setup_complete;

req->length = USB_BUFSIZ;

gadget->ep0->driver_data = cdev;

switch (ctrl->bRequest) {

/* we handle all standard USB descriptors */

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);

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))

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))

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;

}

break;

/* any number of configs can work */

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");

elseif (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;

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; if there's

* no get() method, we know only altsetting zero works.

*/

case USB_REQ_SET_INTERFACE:

if (ctrl->bRequestType != USB_RECIP_INTERFACE)

goto unknown;

if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES)

break;

f = cdev->config->interface[intf];

if (!f)

break;

if (w_value && !f->set_alt)

break;

value = f->set_alt(f, w_index, w_value);

break;

case USB_REQ_GET_INTERFACE:

if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))

goto unknown;

if (!cdev->config || w_index >= 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

break;

*((u8 *)req->buf) = value;

value = min(w_length, (u16) 1);

break;

default:

unknown:

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 ... punt other

* recipients (endpoint, other, WUSB, ...) to the current

* configuration code.

*

* REVISIT it could make sense to let the composite device

* take such requests too, if that's ever needed:  to work

* in config 0, etc.

*/

if ((ctrl->bRequestType & USB_RECIP_MASK)

== USB_RECIP_INTERFACE) {

f = cdev->config->interface[intf];

if (f && f->setup)

value = f->setup(f, ctrl);

else

f = NULL;

}

if (value

struct usb_configuration    *c;

c = cdev->config;

if (c && c->setup)

value = c->setup(c, ctrl);

}

goto done;

}

/* respond with data transfer before status phase? */

if (value >= 0) {

req->length = value;

req->zero = value

value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);

if (value

DBG(cdev, "ep_queue --> %d\n", value);

req->status = 0;

composite_setup_complete(gadget->ep0, req);

}

}

done:

/* device either stalls (value

return value;

}static 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;

intvalue = -EOPNOTSUPP;

u16w_index = le16_to_cpu(ctrl->wIndex);

u8intf = w_index & 0xFF;

u16w_value = le16_to_cpu(ctrl->wValue);

u16w_length = le16_to_cpu(ctrl->wLength);

struct usb_function*f = NULL;

/* 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->complete = composite_setup_complete;

req->length = USB_BUFSIZ;

gadget->ep0->driver_data = cdev;

switch (ctrl->bRequest) {

/* we handle all standard USB descriptors */

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);

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))

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))

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;

}

break;

/* any number of configs can work */

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;

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; if there's

* no get() method, we know only altsetting zero works.

*/

case USB_REQ_SET_INTERFACE:

if (ctrl->bRequestType != USB_RECIP_INTERFACE)

goto unknown;

if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES)

break;

f = cdev->config->interface[intf];

if (!f)

break;

if (w_value && !f->set_alt)

break;

value = f->set_alt(f, w_index, w_value);

break;

case USB_REQ_GET_INTERFACE:

if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))

goto unknown;

if (!cdev->config || w_index >= 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;

default:

unknown:

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 ... punt other

* recipients (endpoint, other, WUSB, ...) to the current

* configuration code.

*

* REVISIT it could make sense to let the composite device

* take such requests too, if that's ever needed: to work

* in config 0, etc.

*/

if ((ctrl->bRequestType & USB_RECIP_MASK)

== USB_RECIP_INTERFACE) {

f = cdev->config->interface[intf];

if (f && f->setup)

value = f->setup(f, ctrl);

else

f = NULL;

}

if (value < 0 && !f) {

struct usb_configuration*c;

c = cdev->config;

if (c && c->setup)

value = c->setup(c, ctrl);

}

goto done;

}

/* respond with data transfer before status phase? */

if (value >= 0) {

req->length = value;

req->zero = value < w_length;

value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);

if (value < 0) {

DBG(cdev, "ep_queue --> %d\n", value);

req->status = 0;

composite_setup_complete(gadget->ep0, req);

}

}

done:

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

return value;

}

这个函数首先提取出USB控制请求的各个字段,然后初始化了端点0的struct usb_request结构。设置了完成回调函数composite_setup_complete。这时通过switch语句来判断是何种控制传输。比如这里是Get_Descriptor,而且设备枚举的时候这时只获取设备描述符的前八个字节以了解端点0的FIFO深度。所以下面的代码执行:

cdev->desc.bNumConfigurations =

count_configs(cdev, USB_DT_DEVICE);

value = min(w_length, (u16) sizeof cdev->desc);

memcpy(req->buf, &cdev->desc, value);cdev->desc.bNumConfigurations =

count_configs(cdev, USB_DT_DEVICE);

value = min(w_length, (u16) sizeof cdev->desc);

memcpy(req->buf, &cdev->desc, value);

这段代码就是复制设备描述符到req的缓冲区。然后执行下面的代码:

if (value >= 0) {

req->length = value;

req->zero = value

value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);

if (value

DBG(cdev, "ep_queue --> %d\n", value);

req->status = 0;

composite_setup_complete(gadget->ep0, req);

}

}if (value >= 0) {

req->length = value;

req->zero = value < w_length;

value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);

if (value < 0) {

DBG(cdev, "ep_queue --> %d\n", value);

req->status = 0;

composite_setup_complete(gadget->ep0, req);

}

}

这时value不为0,所以if里面的语句执行。usb_ep_queue函数主要作用就是将req中的数据写入到FIFO中。函数如下定义:

staticint 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->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) { //以下是针对普通端点的,对于端点0执行else以后的语句

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 { //端点0

udc_write(0, S3C2410_UDC_INDEX_REG);

ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);

fifo_count = s3c2410_udc_fifo_count_out(); //读出当前fifo的位置

}

/* kickstart this i/o queue? */

if (list_empty(&ep->queue) && !ep->halted) { //如果端点的urt链表为空,而端点正常,执行下面的语句,这正是目前的情况,所以下面的语句执行

if (ep->bEndpointAddress == 0 /* ep0 */) {

switch (dev->ep0state) {

case EP0_IN_DATA_PHASE: //对于Get_Descriptor,在s3c2410_udc_handle_ep0_idle中已经设置dev->ep0state为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;

//以上代码才真正的将urt的数据写入USB设备控制器端点0的FIFO,这里有一个条件就是S3C2410_UDC_EP0_CSR_IPKRDY应该为0,USB设备控制器将FIFO中的内容发送出去会设置S3C2410_UDC_EP0_CSR_IPKRDY

//为0,当我们将数据写入FIFO中,设置S3C2410_UDC_EP0_CSR_IPKRDY为1。s3c2410_udc_write_fifo函数完成的就是这个功能

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;

}

} elseif ((ep->bEndpointAddress & USB_DIR_IN) != 0

&& (!(ep_csr&S3C2410_UDC_OCSR1_PKTRDY))

&& s3c2410_udc_write_fifo(ep, req)) {

req = NULL;

} elseif ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY)

&& fifo_count

&& s3c2410_udc_read_fifo(ep, req)) {

req = NULL;

}

}

/* pio or dma irq handler advances the queue. */

if (likely (req != 0)) //对于控制传输这时req为0所以一下代码不执行,这里likely说明req不为0的情况居多,这是针对普通端点来说的

list_add_tail(&req->queue, &ep->queue);

local_irq_restore(flags);

dprintk(DEBUG_VERBOSE, "%s ok\n", __func__);

return 0;

}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;

u32ep_csr = 0;

intfifo_count = 0;

unsigned longflags;

if (unlikely (!_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) { //以下是针对普通端点的,对于端点0执行else以后的语句

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 { //端点0

udc_write(0, S3C2410_UDC_INDEX_REG);

ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);

fifo_count = s3c2410_udc_fifo_count_out(); //读出当前fifo的位置

}

/* kickstart this i/o queue? */

if (list_empty(&ep->queue) && !ep->halted) { //如果端点的urt链表为空,而端点正常,执行下面的语句,这正是目前的情况,所以下面的语句执行

if (ep->bEndpointAddress == 0 /* ep0 */) {

switch (dev->ep0state) {

case EP0_IN_DATA_PHASE: //对于Get_Descriptor,在s3c2410_udc_handle_ep0_idle中已经设置dev->ep0state为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;

//以上代码才真正的将urt的数据写入USB设备控制器端点0的FIFO,这里有一个条件就是S3C2410_UDC_EP0_CSR_IPKRDY应该为0,USB设备控制器将FIFO中的内容发送出去会设置S3C2410_UDC_EP0_CSR_IPKRDY

//为0,当我们将数据写入FIFO中,设置S3C2410_UDC_EP0_CSR_IPKRDY为1。s3c2410_udc_write_fifo函数完成的就是这个功能

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;

}

}

/* pio or dma irq handler advances the queue. */

if (likely (req != 0)) //对于控制传输这时req为0所以一下代码不执行,这里likely说明req不为0的情况居多,这是针对普通端点来说的

list_add_tail(&req->queue, &ep->queue);

local_irq_restore(flags);

dprintk(DEBUG_VERBOSE, "%s ok\n", __func__);

return 0;

}

函数返回到了composite_setup中,再把代码贴上来:

if (value >= 0) {

req->length = value;

req->zero = value

value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);

if (value

DBG(cdev, "ep_queue --> %d\n", value);

req->status = 0;

composite_setup_complete(gadget->ep0, req);

}

}if (value >= 0) {

req->length = value;

req->zero = value < w_length;

value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);

if (value < 0) {

DBG(cdev, "ep_queue --> %d\n", value);

req->status = 0;

composite_setup_complete(gadget->ep0, req);

}

}

现在执行到了if (value ep0state还是EP0_IDLE状态。所以s3c2410_udc_handle_ep0也返回了。最后中断返回完成了这个控制传输。至于控制传输的状态阶段是由硬件来完成的。当我们向FIFO中写入了全部的数据,就可以设置EP0_CSR寄存器的DATA_END为1,硬件将自动完成状态阶段。还有一点,控制传输的urt回调函数非常简单,只打印一些调试信息,当最后一次调用s3c2410_udc_read_fifo时,s3c2410_udc_read_fifo会在函数里面调用urt的回调函数。

以上分析了USB设备枚举过程中的第二步:Get_Descriptor阶段的控制传输。其他的步骤大同小异,都是主机端发起,然后USB设备通过中端来处理。依次经过文中最前面提到的六步,USB主机就识别咱们的设备了 。

linux下gadget复合设备,Linux USB Gadget--设备枚举相关推荐

  1. USB总线-Linux内核USB3.0设备控制器复合设备之USB gadget configfs分析(七)

    1.简介 configfs是基于ram的文件系统,与sysfs的功能有所不同.sysfs是基于文件系统的kernel对象视图,虽然某些属性允许用户读写,但对象是在kernel中创建.注册.销毁,由ke ...

  2. Linux下的硬件驱动——USB设备(下)

    Linux下的硬件驱动--USB设备(下)(驱动开发部分) 文档选项 打印本页 将此页作为电子邮件发送 未显示需要 JavaScript 的文档选项 级别: 初级 赵明, 联想软件设计中心嵌入式研发处 ...

  3. Linux下的硬件驱动——USB设备

    想起当初对于破安卓手机,挂在系统上可是费了好些劲,今偶遇USB驱动开发,收集备用,哪天一生气,说不定也写一个linux下的手机驱动,类似于91手机助手的,也不用配置了. Linux下的硬件驱动--US ...

  4. Linux下的硬件驱动——USB设备配置以及开发

    Linux下的硬件驱动--USB设备(上)(驱动配置部分) USB设备越来越多,而Linux在硬件配置上仍然没有做到完全即插即用,对于Linux怎样配置和使用他们,也越来越成为困扰我们的一大问题.本文 ...

  5. linux内核不能识别u盘分区,一种在Linux内核中识别特定USB大容量存储设备的方法及系统与流程...

    本发明涉配usb设备识别技术领域,特别是涉及一种在linux内核中识别特定usb大容量存储设备的方法及系统. 背景技术: 在linux系统下对usb设备进行管控,一般而言有两种方法,一种是阻断新插入设 ...

  6. 玩转USB HID系列:Linux下使用Python开发USB HID

    玩转USB系列:Linux下使用Python开发USB HID 实验环境 开发步骤 安装pyusb 库 我们来与HID设备通讯! 注意 实验环境 ubuntu 16.04 LTS 64-bit pyt ...

  7. 嵌入式 linux usbmon,浅析linux下替代usbhound的usb总线sniffer抓包模块usbmon安装和使用...

    浅析linux下替代usbhound的usb总线sniffer抓包模块usbmon安装和使用 操作系统: ubuntu 8.10 内核版本: 2.6.27-7-generic ubuntu 8.10内 ...

  8. Linux下基于XScale的USB摄像头图像采集

    1.引言 摄像头分为数字摄像头和模拟摄像头两大类.传统的模拟摄像头,获取图像信息需要先将视频采集设备产生的模拟视频信号经过特定的视频捕捉卡转换成数字信号,进而才能进行存储等处理.数字摄像头可以直接捕捉 ...

  9. Linux下用FFMPEG采集usb摄像头到RTMP

    Linux下用 FFMPEG 采集 usb摄像头视频 和 摄像头内置麦克风音频 到RTMP服务 ffmpeg -f video4linux2 -qscale 10 -r 12 -s 640x480 - ...

  10. Linux下netstat常用,Linux netstat常用命令

    1.统计80端口连接数 netstat -nat|grep -i "80"|wc -l 2.统计httpd协议连接数(查看Apache的并发请求数及其TCP连接状态) ps -ef ...

最新文章

  1. 计算用户输入的数字的平均数,并输出大于平均数的数字输出
  2. 每日阅读(产品) 汤道QQ与微信
  3. 医疗软件产品核心算法部分说明--转载截取
  4. linux下启动和关闭网卡命令
  5. 64位虚拟机下asm()语法_用Hyper-V在win10中创建虚拟机,简单快捷,不用安装其它软件...
  6. Could not get lock /var/lib/apt/lists/lock - open (11: Resource temporarily unavailable)
  7. MFCC梅尔频率倒谱系数
  8. 组装r730服务器,戴尔poweredge r730服务器配置及系统安装详解教程
  9. sql etl_使用SQL TRY函数进行ETL优化
  10. 那些不开源的公司,后来怎么样了?
  11. Oracle 警告:Weblogic 服务器中含有多个可遭远程利用的严重漏洞
  12. servelet 实现Post接口访问
  13. 关于RDP报表工具参数配置
  14. 我为什么关注范冰冰和苍井空
  15. Lenovo笔记本BIOS详解
  16. 开源Android自定义思维导图控件ThinkMap树状图TreeView(类似xMind那种效果)
  17. Git全解 idea github gitee gitlab
  18. 盘点:20位近两年新任“院士校长”!
  19. gdb x命令_Coresight及GDB使用介绍 - 大海在倾听
  20. 全国智能网联与无人驾驶职教联盟筹建工作正式开启

热门文章

  1. 句型与句子,编译原理基本概念
  2. Putty免密码登录(亲测好用)
  3. Origin数字化(Digitize)插件
  4. JAVA导出excel 动态合并单元格
  5. 涡轮流量计原理和选择方法
  6. yolov5运行以及训练时报错的问题
  7. 2022软件测试高频面试题汇总(附带答案)「 建议收藏 」
  8. Java面试经验总结
  9. WAP(wml)开发教程
  10. python 苹果试玩_python2下载