mini2440 usb device controller 驱动的分析--gadget设备

转自:http://blog.csdn.net/ajigegege/article/details/12710707


已经看了很长时间USB的驱动了。USB是目前看到最复杂的驱动,内容很多,但网上分析USB驱动的资料很少,因此我在学习的过程中走了很多弯路。在这里记录自己的学习过程,希望可以帮到其他研究USB驱动的同事。先推荐一本书:Bootstrap Yourself With Linux-USB Stack

       首先,USB的驱动分成两部分:host和gadget。usb是一个host driven的协议,即host是完全主动,每次通信过程都是由host发起的,包括读和写。而device只负责回应,不能主动去读写。另外,虽然usb有四种传输(在很多书中都有介绍),但是usb的传输和其他的通信方式都一样,都是基于硬件中断的。无论在host方还是device方,都是产生中断之后,进行相应的处理。因此,中断函数是很多处理过程的入口点。这里的硬件中断和四种传输方式中的中断传输是完全不同的概念。

       在LDD中,说不介绍gadget部分的驱动,但是它提到了configuration\interface\endpoint等概念。其实这些设置在host和gadget上面都有,在gadget的驱动里负责实现 configuration\interface,在host的驱动中,主要是得到gadget的configuration和interface,进而对gadget进行控制。

       本文以s3c2440上的gadget驱动为例,分析gadget的驱动。 在以后的文章中在分析host的驱动。因为gadget的驱动相对简单。

       首先,usb的驱动都是分层的。gadget部分的驱动分层如下:

SouthEast

其中,1.usb device controller就是具体的硬件,对应到2440的usb device controller。在数据手册上面可以看到相应的说明。2440还有一个usb host controller。这是两个不同的硬件,分别用于实现2440的usb device 和 usb host功能,在使用时,各用各的,没有相互的影响。

2. device firmware driver。这是最低层的驱动实现,其实这部分的驱动就是负责直接控制 device controller硬件的。对应的文件是usb/gadget/s3c2410_udc.c。在这部分文件中,可以配置相应的寄存器,获取硬件资源等。device controller也是实现成了一个platform device的。

3.  chapter 9-- Driver/usb device framework。按照我的理解,这部分的驱动主要是一个中间层,在这部分实现了 usb 枚举的过程(在device部分是被动的,主要是如何回应host 发过来的请求。),实现从 frimware driver 和 class driver 的通信。我们可以对应到 composite.c 这个文件。

4.  usb class driver。这部分驱动是要实现具体的功能的。比如最常用的u盘的功能、usb 鼠标的功能。我们以zero.c驱动为例进行分析,这个驱动可以实现的功能是可以将host发送过来的消息 回送 给主机。相当于是一个echo的作用。

从上面的分析,可以看出,firmware driver负责控制硬件,class driver 负责处理具体的业务,完成具体的功能。device frimwork负责处理其他的事情。上面就是usb device部分的分层模型。

通常,在一般介绍usb驱动的资料中,上来就介绍几个usb相关的结构体,每个结构体都很大,让人看了根本摸不到头脑。我觉得这里的分层模型是更重要的。

前面转载了两篇介绍usb 枚举过程的博客,都是写的很好的。网上的资料中,最丰富的算是 linux那些事之USB。不得不佩服写相关博客的那位博主,真是相当有才。深入分析usb的同时穿插着各种各种段子,我强忍着剧烈痛苦看了一个星期,收获不大,不得不说那个博文太过庞杂。

下面,具体分析各层。首先,是s3c2410_udc.c文件。这个文件中先是按照platform device,实现了基本的probe函数等。

[cpp] view plain copy print ?
  1. /*

  2. *  probe - binds to the platform device

  3. */

  4. staticint s3c2410_udc_probe(struct platform_device *pdev)  

  5. {  

  6. struct s3c2410_udc *udc = &memory;  

  7. struct device *dev = &pdev->dev;  

  8. int retval;  

  9. int irq;  

  10.    dev_dbg(dev, "%s()\n", __func__);  

  11.    usb_bus_clock = clk_get(NULL, "usb-bus-gadget");  

  12. if (IS_ERR(usb_bus_clock)) {  

  13.        dev_err(dev, "failed to get usb bus clock source\n");  

  14. return PTR_ERR(usb_bus_clock);  

  15.    }  

  16.    clk_enable(usb_bus_clock);  

  17.    udc_clock = clk_get(NULL, "usb-device");  

  18. if (IS_ERR(udc_clock)) {  

  19.        dev_err(dev, "failed to get udc clock source\n");  

  20. return PTR_ERR(udc_clock);  

  21.    }  

  22.    clk_enable(udc_clock);  

  23.    mdelay(10);  

  24.    dev_dbg(dev, "got and enabled clocks\n");  

  25. if (strncmp(pdev->name, "s3c2440", 7) == 0) {  

  26.        dev_info(dev, "S3C2440: increasing FIFO to 128 bytes\n");  

  27.        memory.ep[1].fifo_size = S3C2440_EP_FIFO_SIZE;  

  28.        memory.ep[2].fifo_size = S3C2440_EP_FIFO_SIZE;  

  29.        memory.ep[3].fifo_size = S3C2440_EP_FIFO_SIZE;  

  30.        memory.ep[4].fifo_size = S3C2440_EP_FIFO_SIZE;  

  31.    }  

  32.    spin_lock_init (&udc->lock);  

  33.    udc_info = pdev->dev.platform_data;  

  34.    rsrc_start = S3C2410_PA_USBDEV;  

  35.    rsrc_len   = S3C24XX_SZ_USBDEV;  

  36. if (!request_mem_region(rsrc_start, rsrc_len, gadget_name))  

  37. return -EBUSY;  

  38.    base_addr = ioremap(rsrc_start, rsrc_len);  

  39. if (!base_addr) {  

  40.        retval = -ENOMEM;  

  41. goto err_mem;  

  42.    }  

  43.    device_initialize(&udc->gadget.dev);  

  44.    udc->gadget.dev.parent = &pdev->dev;  

  45.    udc->gadget.dev.dma_mask = pdev->dev.dma_mask;  

  46.    the_controller = udc;  

  47.    platform_set_drvdata(pdev, udc);  

  48.    s3c2410_udc_disable(udc);  

  49.    s3c2410_udc_reinit(udc);  

  50. /* irq setup after old hardware state is cleaned up */

  51.    retval = request_irq(IRQ_USBD, s3c2410_udc_irq,  

  52.                 IRQF_DISABLED, gadget_name, udc);  

  53. if (retval != 0) {  

  54.        dev_err(dev, "cannot get irq %i, err %d\n", IRQ_USBD, retval);  

  55.        retval = -EBUSY;  

  56. goto err_map;  

  57.    }  

  58.    dev_dbg(dev, "got irq %i\n", IRQ_USBD);  

  59. if (udc_info && udc_info->vbus_pin > 0) {  

  60.        retval = gpio_request(udc_info->vbus_pin, "udc vbus");  

  61. if (retval < 0) {  

  62.            dev_err(dev, "cannot claim vbus pin\n");  

  63. goto err_int;  

  64.        }  

  65.        irq = gpio_to_irq(udc_info->vbus_pin);  

  66. if (irq < 0) {  

  67.            dev_err(dev, "no irq for gpio vbus pin\n");  

  68. goto err_gpio_claim;  

  69.        }  

  70.        retval = request_irq(irq, s3c2410_udc_vbus_irq,  

  71.                     IRQF_DISABLED | IRQF_TRIGGER_RISING  

  72.                     | IRQF_TRIGGER_FALLING | IRQF_SHARED,  

  73.                     gadget_name, udc);  

  74. if (retval != 0) {  

  75.            dev_err(dev, "can't get vbus irq %d, err %d\n",  

  76.                irq, retval);  

  77.            retval = -EBUSY;  

  78. goto err_gpio_claim;  

  79.        }  

  80.        dev_dbg(dev, "got irq %i\n", irq);  

  81.    } else {  

  82.        udc->vbus = 1;  

  83.    }  

  84.    dev_dbg(dev, "probe ok\n");  

  85. return 0;  

  86. }  

  87. /*

  88. *  s3c2410_udc_remove

  89. */

  90. staticint s3c2410_udc_remove(struct platform_device *pdev)  

  91. {  

  92.    dev_dbg(&pdev->dev, "%s: remove ok\n", __func__);  

[cpp] view plain copy print ?
  1.       ...  

  2. return 0;  

  3. }  

  4. staticstruct platform_driver udc_driver_2440 = {  

  5.    .driver     = {  

  6.        .name   = "s3c2440-usbgadget",  

  7.        .owner  = THIS_MODULE,  

  8.    },  

  9.    .probe      = s3c2410_udc_probe,  

  10.    .remove     = s3c2410_udc_remove,  

  11.    .suspend    = s3c2410_udc_suspend,  

  12.    .resume     = s3c2410_udc_resume,  

  13. };  

这里的实现和一般的platfrom device 是一样的。需要说明的

1. 就是probe函数中的vbus_pin。这个管脚是与vbus相连的,练到了一个mcu的外部中断。这样,当vbus有电平变化的时候,mcu就可以知道。因为,device设备正常来讲不知道自己被插入(或拔出)到了主设备的root hub中,vbus用于给usb设备供电,当device被插入时,vbus会使高电平,拔出时会是低电平。所以,通过vbus的中断,device设备就可以感应到自己被插拔的事件。通常,在插入时,enable_udc。被拔出后,disable_udc。当然,如果在硬件上面,没有这部分电路,那么就需要一直enable_udc。

2. memory。在probe函数中有一个memory。在这个struct s3c2410_udc memory 中记录了device controller的5个end point的相关信息。其中ep0是用作control功能的,其余的用于传输数据。在udc的driver中,并不指定ep的方向,但在class driver 中会指定。


到这里probe函数执行完后,usb device controller的初始化就完成了。

接下来,程序的入口就是usb的中断函数s3c2410_udc_irq了。

[cpp] view plain copy print ?
  1. static irqreturn_t s3c2410_udc_irq(int dummy, void *_dev)  

  2. {  

  3. struct s3c2410_udc *dev = _dev;  

  4. int usb_status;  

  5. int usbd_status;  

  6. int pwr_reg;  

  7. int ep0csr;  

  8. int i;  

  9.    u32 idx;  

  10.    unsigned long flags;  

  11.    spin_lock_irqsave(&dev->lock, flags);  

  12. /* Save index */

  13.    idx = udc_read(S3C2410_UDC_INDEX_REG);            读取index register

  14. /* Read status registers */

  15.    usb_status = udc_read(S3C2410_UDC_USB_INT_REG);  

  16.    usbd_status = udc_read(S3C2410_UDC_EP_INT_REG);  

  17.    pwr_reg = udc_read(S3C2410_UDC_PWR_REG);  

  18.    udc_writeb(base_addr, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);  

  19.    ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG);  

  20.    dprintk(DEBUG_NORMAL, "usbs=%02x, usbds=%02x, pwr=%02x ep0csr=%02x\n",  

  21.        usb_status, usbd_status, pwr_reg, ep0csr);  

  22. /*

  23.     * Now, handle interrupts. There's two types :

  24.     * - Reset, Resume, Suspend coming -> usb_int_reg

  25.     * - EP -> ep_int_reg

  26.     */

  27. /* RESET */

  28. if (usb_status & S3C2410_UDC_USBINT_RESET) {  

  29. /* two kind of reset :

  30.         * - reset start -> pwr reg = 8

  31.         * - reset end   -> pwr reg = 0

  32.         **/

  33.        dprintk(DEBUG_NORMAL, "USB reset csr %x pwr %x\n",  

  34.            ep0csr, pwr_reg);  

  35.        dev->gadget.speed = USB_SPEED_UNKNOWN;  

  36.        udc_write(0x00, S3C2410_UDC_INDEX_REG);  

  37.        udc_write((dev->ep[0].ep.maxpacket & 0x7ff) >> 3,  

  38.                S3C2410_UDC_MAXP_REG);  

  39.        dev->address = 0;  

  40.        dev->ep0state = EP0_IDLE;  

  41.        dev->gadget.speed = USB_SPEED_FULL;  

  42. /* clear interrupt */

  43.        udc_write(S3C2410_UDC_USBINT_RESET,  

  44.                S3C2410_UDC_USB_INT_REG);  

  45.        udc_write(idx, S3C2410_UDC_INDEX_REG);  

  46.        spin_unlock_irqrestore(&dev->lock, flags);  

  47. return IRQ_HANDLED;  

  48.    }  

  49. /* RESUME */

  50. if (usb_status & S3C2410_UDC_USBINT_RESUME) {  

  51.        dprintk(DEBUG_NORMAL, "USB resume\n");  

  52. /* clear interrupt */

  53.        udc_write(S3C2410_UDC_USBINT_RESUME,  

  54.                S3C2410_UDC_USB_INT_REG);  

  55. if (dev->gadget.speed != USB_SPEED_UNKNOWN  

  56.                && dev->driver  

  57.                && dev->driver->resume)  

  58.            dev->driver->resume(&dev->gadget);  

  59.    }  

  60. /* SUSPEND */

  61. if (usb_status & S3C2410_UDC_USBINT_SUSPEND) {  

  62.        dprintk(DEBUG_NORMAL, "USB suspend\n");  

  63. /* clear interrupt */

  64.        udc_write(S3C2410_UDC_USBINT_SUSPEND,  

  65.                S3C2410_UDC_USB_INT_REG);  

  66. if (dev->gadget.speed != USB_SPEED_UNKNOWN  

  67.                && dev->driver  

  68.                && dev->driver->suspend)  

  69.            dev->driver->suspend(&dev->gadget);  

  70.        dev->ep0state = EP0_IDLE;  

  71.    }  

  72. /* EP */

  73. /* control traffic */

  74. /* check on ep0csr != 0 is not a good idea as clearing in_pkt_ready

  75.     * generate an interrupt

  76.     */

  77. if (usbd_status & S3C2410_UDC_INT_EP0) {  

  78.        dprintk(DEBUG_VERBOSE, "USB ep0 irq\n");  

  79. /* Clear the interrupt bit by setting it to 1 */

  80.        udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_REG);  

  81.        s3c2410_udc_handle_ep0(dev);  

  82.    }  

  83. /* endpoint data transfers */

  84. for (i = 1; i < S3C2410_ENDPOINTS; i++) {  

  85.        u32 tmp = 1 << i;  

  86. if (usbd_status & tmp) {  

  87.            dprintk(DEBUG_VERBOSE, "USB ep%d irq\n", i);  

  88. /* Clear the interrupt bit by setting it to 1 */

  89.            udc_write(tmp, S3C2410_UDC_EP_INT_REG);  

  90.            s3c2410_udc_handle_ep(&dev->ep[i]);  

  91.        }  

  92.    }  

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

  94. /* Restore old index */

  95.    udc_write(idx, S3C2410_UDC_INDEX_REG);  

  96.    spin_unlock_irqrestore(&dev->lock, flags);  

  97. return IRQ_HANDLED;  

[cpp] view plain copy print ?
  1. }  

可以看出,上面的函数主要是处理了两种类型的中断,usb interrupt和end point interrupt。其中usb interrupt主要是指usb device的suspend、resume、reset中断, 目前我还不知道这些中断是如何产生的?endpoint interrupt就是指ep0~5 收到/发完数据之后产生的中断。因为ep0是特殊的用于控制的endpoint,因此,对ep0进行了特殊的处理。

我们先看s3c2410_udc_handle_ep0

[cpp] view plain copy print ?
  1. staticvoid s3c2410_udc_handle_ep0(struct s3c2410_udc *dev)  

  2. {  

  3.    u32         ep0csr;  

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

  5. struct s3c2410_request  *req;  

  6. struct usb_ctrlrequest  crq;  

  7. if (list_empty(&ep->queue))  

  8.        req = NULL;  

  9. else

  10.        req = list_entry(ep->queue.next, struct s3c2410_request, queue);  

  11. /* We make the assumption that S3C2410_UDC_IN_CSR1_REG equal to

  12.     * S3C2410_UDC_EP0_CSR_REG when index is zero */

  13.    udc_write(0, S3C2410_UDC_INDEX_REG);  

  14.    ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG);  

  15. /* clear stall status */

  16. if (ep0csr & S3C2410_UDC_EP0_CSR_SENTSTL) {  

  17.        s3c2410_udc_nuke(dev, ep, -EPIPE);  

  18.        dprintk(DEBUG_NORMAL, "... clear SENT_STALL ...\n");  

  19.        s3c2410_udc_clear_ep0_sst(base_addr);  

  20.        dev->ep0state = EP0_IDLE;  

  21. return;  

  22.    }  

  23. /* clear setup end */

  24. if (ep0csr & S3C2410_UDC_EP0_CSR_SE) {  

  25.        dprintk(DEBUG_NORMAL, "... serviced SETUP_END ...\n");  

  26.        s3c2410_udc_nuke(dev, ep, 0);  

  27.        s3c2410_udc_clear_ep0_se(base_addr);  

  28.        dev->ep0state = EP0_IDLE;  

  29.    }  

  30. switch (dev->ep0state) {  

  31. case EP0_IDLE:  

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

  33. break;  

  34. case EP0_IN_DATA_PHASE:         /* GET_DESCRIPTOR etc */

  35.        dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n");  

  36. if (!(ep0csr & S3C2410_UDC_EP0_CSR_IPKRDY) && req) {  

  37.            s3c2410_udc_write_fifo(ep, req);  

  38.        }  

  39. break;  

  40. case EP0_OUT_DATA_PHASE:        /* SET_DESCRIPTOR etc */

  41.        dprintk(DEBUG_NORMAL, "EP0_OUT_DATA_PHASE ... what now?\n");  

  42. if ((ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) && req ) {  

  43.            s3c2410_udc_read_fifo(ep,req);  

  44.        }  

  45. break;  

  46. case EP0_END_XFER:  

  47.        dprintk(DEBUG_NORMAL, "EP0_END_XFER ... what now?\n");  

  48.        dev->ep0state = EP0_IDLE;  

  49. break;  

  50. case EP0_STALL:  

  51.        dprintk(DEBUG_NORMAL, "EP0_STALL ... what now?\n");  

  52.        dev->ep0state = EP0_IDLE;  

  53. break;  

  54.    }  

  55. }  

首先是从ep0的request queue上得到一个req。struct s3c2410_request *req。request是usb device用于描述IO请求的。结构体对应如下:

[cpp] view plain copy print ?
  1. struct usb_request {  

  2. void            *buf;  

  3.    unsigned        length;  

  4.    dma_addr_t      dma;  

  5.    unsigned        no_interrupt:1;  

  6.    unsigned        zero:1;  

  7.    unsigned        short_not_ok:1;  

  8. void            (*complete)(struct usb_ep *ep,  

  9. struct usb_request *req); 这个是io request完成时的回调函数。  

  10. void            *context;  

  11. struct list_head    list;  

  12. int         status;  

  13.    unsigned        actual;  

  14. };  

每个ep都有一个queue,上面有由这个ep产生的所有read、write request。按照代码的意思,这个req(queue上面的第一个req)就和上面产生的中断相对应。也就是说中断与req完全是按照时间先后顺序对应的。 我觉得这里是否会有问题?

上面比较主要的状态是idle、in_data_phase、out_data_phase。其中idle应该是ep0的初始状态,in_data_phase\out_data_phase 应该是对应接收数据和发送数据的状态。分别看三个函数。

[cpp] view plain copy print ?
  1. staticvoid s3c2410_udc_handle_ep0_idle(struct s3c2410_udc *dev,  

  2. struct s3c2410_ep *ep,  

  3. struct usb_ctrlrequest *crq,  

  4.                    u32 ep0csr)  

  5. {  

  6. int len, ret, tmp;  

  7.    s3c2410_udc_nuke(dev, ep, -EPROTO);         // 这个函数的作用?

  8.    len = s3c2410_udc_read_fifo_crq(crq);       //从fifo中读取control request

  9.    dprintk(DEBUG_NORMAL, "bRequest = %d bRequestType %d wLength = %d\n",  

  10.        crq->bRequest, crq->bRequestType, crq->wLength);  

  11. /* cope with automagic for some standard requests. */

  12.    dev->req_std = (crq->bRequestType & USB_TYPE_MASK)  

  13.        == USB_TYPE_STANDARD;  

  14.    dev->req_config = 0;  

  15.    dev->req_pending = 1;  

[cpp] view plain copy print ?
  1. // 这里是根据control request的,采取不同的措施。  

  2. switch (crq->bRequest) {  

  3. case USB_REQ_SET_CONFIGURATION:  

  4.    dprintk(DEBUG_NORMAL, "USB_REQ_SET_CONFIGURATION ... \n");  

  5. if (crq->bRequestType == USB_RECIP_DEVICE) {  

  6.        dev->req_config = 1;  

  7.        s3c2410_udc_set_ep0_de_out(base_addr);  

  8.    }  

  9. break;  

  10. case USB_REQ_SET_INTERFACE:  

  11.    dprintk(DEBUG_NORMAL, "USB_REQ_SET_INTERFACE ... \n");  

  12. if (crq->bRequestType == USB_RECIP_INTERFACE) {  

  13.        dev->req_config = 1;  

  14.        s3c2410_udc_set_ep0_de_out(base_addr);  

  15.    }  

  16. break;  

  17. case USB_REQ_SET_ADDRESS:  

  18.    dprintk(DEBUG_NORMAL, "USB_REQ_SET_ADDRESS ... \n");  

  19. if (crq->bRequestType == USB_RECIP_DEVICE) {  

  20.        tmp = crq->wValue & 0x7F;  

  21.        dev->address = tmp;  

  22.        udc_write((tmp | S3C2410_UDC_FUNCADDR_UPDATE),  

  23.                S3C2410_UDC_FUNC_ADDR_REG);  

  24.        s3c2410_udc_set_ep0_de_out(base_addr);  

  25. return;  

  26.    }  

  27. break;  

  28. case USB_REQ_GET_STATUS:  

  29.    dprintk(DEBUG_NORMAL, "USB_REQ_GET_STATUS ... \n");  

  30.    s3c2410_udc_clear_ep0_opr(base_addr);  

  31. if (dev->req_std) {  

  32. if (!s3c2410_udc_get_status(dev, crq)) {  

  33. return;  

  34.        }  

  35.    }  

  36. break;  

  37. case USB_REQ_CLEAR_FEATURE:  

  38.    s3c2410_udc_clear_ep0_opr(base_addr);  

  39. if (crq->bRequestType != USB_RECIP_ENDPOINT)  

  40. break;  

  41. if (crq->wValue != USB_ENDPOINT_HALT || crq->wLength != 0)  

  42. break;  

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

  44.    s3c2410_udc_set_ep0_de_out(base_addr);  

  45. return;  

  46. case USB_REQ_SET_FEATURE:  

  47.    s3c2410_udc_clear_ep0_opr(base_addr);  

  48. if (crq->bRequestType != USB_RECIP_ENDPOINT)  

  49. break;  

  50. if (crq->wValue != USB_ENDPOINT_HALT || crq->wLength != 0)  

  51. break;  

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

  53.    s3c2410_udc_set_ep0_de_out(base_addr);  

  54. return;  

  55. default:  

  56.    s3c2410_udc_clear_ep0_opr(base_addr);  

  57. break;  

  58. }  

  59. if (crq->bRequestType & USB_DIR_IN)  

  60.    dev->ep0state = EP0_IN_DATA_PHASE;  

  61. else

  62.    dev->ep0state = EP0_OUT_DATA_PHASE;  

[cpp] view plain copy print ?
  1.        设备枚举的具体过程。  

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

  3. }  

由dev->driver->setup(&dev->gadget,crq)执行具体的设备枚举的过程。


你可能感兴趣的:(通信,资料,记录,硬件,网上)