首页 > 系统相关 >Linux USB Gadget--设备枚举

Linux USB Gadget--设备枚举

时间:2023-01-30 13:57:42浏览次数:48  
标签:USB -- Gadget req dev udc s3c2410 ep

前面介绍了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;

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;

};

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

}

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中,如下:

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;

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)

{

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;

}

}

看这个函数之前,先要看一个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;

const struct 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_head queue;

unsigned long last_io; /* jiffies timestamp */

struct usb_gadget *gadget;

struct s3c2410_udc *dev;

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

};

这结构的成员一部分是在定义的时候静态初始化的,端点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中,如下:

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

}

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中定义,如下:

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;

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

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;

}

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;

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

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 < 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 >= 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中。函数如下定义:

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

}

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->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 < 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 >= 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 < 0) { 这里对于上面的分析value是0所以下面的代码不执行。composite_setup函数也就返回了。返回到了 s3c2410_udc_handle_ep0_idle。因为没有出错, s3c2410_udc_handle_ep0_idle函数也返回了。返回到了s3c2410_udc_handle_ep0中,这时dev->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主机就识别咱们的设备了 。

标签:USB,--,Gadget,req,dev,udc,s3c2410,ep
From: https://www.cnblogs.com/kn-zheng/p/17075659.html

相关文章

  • 安装jdk并配置环境变量
    JDK有rpm安装包,也有tar.gz的安装包https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html rpm包安装:rpm-ivhjdk-8u241-linux-x64.rpmtar.gz......
  • 动画效果:关于找不到匿名类 java.lang.NoClassDefFoundError: Failed resolution of:
    //接到需求密码不对时,textView左右浮动,ObjectAnimator直接创建匿名内部类Animator.AnimatorListener,会报以下错误,程序崩溃publicvoidfloatTextView(TextViewv......
  • 局部变量与全局变量
    不同的函数之间传递数据:(1)参数:通过形参或实参(2)返回值:用return语句返回计算结果(3)全局变量:外部变量注:如果全局变量与局部变量重名,那么将采取就近原则,即实际获取和修改的值......
  • Git、GitHub、GitLab
    Git、GitHub与GitLab的区别1、Git是一种版本控制系统,是一种工具,用于代码的存储和版本控制。2、GitHub是一个基于Git实现的在线代码仓库,是目前全球最大的代码托管平台,可以......
  • Linux USB Gadget--软件结构
    USBGadget是分层的软件结构,本文分析的是2.6.32.2版本的Gadget软件结构,这个软件结构与以前版本的变化很大。USBGadget软件结构总共分为三层:一.UDC层这一层是与硬件相关层......
  • 部署tomcat
    部署tomcat https://tomcat.apache.org/download-90.cgi   直接解压即完成安装;然后通过window的浏览器去访问,注意一下把8080端口的防火墙要开放;< 防火墙开......
  • Linux 记录和查看登录日和操作志|查看登录历史
    零、查看最近登录ip以及历史命令执行日期  [root@izbp~]#last [root@izbp~]#last-10 //表示只显示10行[root@izbp~]#last-n5-a-i //显示5行且主机IP......
  • terminate called without an active exception错误分析
    项目场景:在使用C++中thread类创建子线程完成相关功能,并且主线程不阻塞直到子线程完成。问题描述:子线程的初始化在对象的初始化过程中,并且子线程有自己独立的功能,主线......
  • CSS布局display值inline、block、inline-block区别
    inline前后不会有换行,block前后会有换行,inline-block前后不会有换行,但内部会换行且可以设置高宽。,如下图所示: ......
  • Linux USB Gadget--各环节的整合
    LinuxUSBGadget软件结构一文中分析LinuxUSBGadget软件分为三层。这三层其中两层是与硬件无关的,分别是Gadget功能驱动层,USB设备层。一层是与硬件相关的是UDC层。每一层......