Linux gadget 驱动1

来源:互联网 发布:云计算工资多少 编辑:程序博客网 时间:2024/05/17 10:28

  为了解决一个问题,简单看了一遍linux gadget驱动的加载流程.做一下记录.

  使用的内核为linux 2.6.35 硬件为芯唐NUC950. gadget是在UDC驱动上面的一层,如果要编写gadget驱动只需调用linux 的gadget API,不需设计底层的UDC驱动. 但要是分析驱动BUG,就需要了同时了解一下UDC.

  下面以简单的gadget zero驱动分析驱动的加载流程.

  主要是一系列的bind的调用,让gadget驱动一步步与硬件的端点联系起来.

  从insmod g_zero.ko开始.

zero.c

复制代码
1 static struct usb_composite_driver zero_driver = {2     .name        = "zero",3     .dev        = &device_desc,4     .strings    = dev_strings,5     .bind        = zero_bind,6     .unbind        = zero_unbind,7     .suspend    = zero_suspend,8     .resume        = zero_resume,9 };
复制代码

这个结构体是zero.c中的,如果是自己写的gadget驱动,这个结构体及这些函数需要自己实现.

先不去细看结构体中的具体内容,现在只关注注册流程.

1 static int __init init(void)2 {3     return usb_composite_register(&zero_driver);4 }

调用
usb_composite_register(&zero_driver);

zero_driver作为参数传递,类型为struct usb_composite_driver

composite.c

 

 

复制代码
int usb_composite_register(struct usb_composite_driver *driver)//zero_driver{    if (!driver || !driver->dev || !driver->bind || composite)        return -EINVAL;    if (!driver->name)        driver->name = "composite";    composite_driver.function =  (char *) driver->name;    composite_driver.driver.name = driver->name;    composite = driver;    return usb_gadget_register_driver(&composite_driver);}
复制代码

composite_driver定义在composite.c中

复制代码
 1 static struct usb_gadget_driver composite_driver = { 2     .speed        = USB_SPEED_HIGH, 3  4     .bind        = composite_bind, 5     .unbind        = composite_unbind, 6  7     .setup        = composite_setup, 8     .disconnect    = composite_disconnect, 9 10     .suspend    = composite_suspend,11     .resume        = composite_resume,12 13     .driver    = {14         .owner        = THIS_MODULE,15     },16 };
复制代码

 

composite = driver;

用全局指针指向zero_driver,后面用到compoite这个指针时候知道它的值在这里赋好了.

最后调用usb_gadget_register_driver(&composite_driver);

不同的芯片实现不同,但原理应该类似,一般在xxx_udc.c中

nuc950在nuc900_udc.c中:

 

复制代码
 1 int usb_gadget_register_driver (struct usb_gadget_driver *driver) 2 { 3         struct nuc900_udc *udc = &controller; 4         int retval; 5  6  7         printk("usb_gadget_register_driver() '%s'\n", driver->driver.name); 8  9         if (!udc)10                 return -ENODEV;11 12         if (udc->driver)13                 return -EBUSY;14         if (!driver->bind || !driver->unbind || !driver->setup15                         || driver->speed == USB_SPEED_UNKNOWN)16                 return -EINVAL;17         printk("driver->speed=%d\n", driver->speed);18         udc->gadget.name = gadget_name;19         udc->gadget.ops = &nuc900_ops;20         udc->gadget.is_dualspeed = 1;21         udc->gadget.speed = USB_SPEED_HIGH;//USB_SPEED_FULL;22         udc->ep0state = EP0_IDLE;23 24         udc->gadget.dev.release = nop_release;25 26         udc->driver = driver;27 28         udc->gadget.dev.driver = &driver->driver;29 30         printk( "binding gadget driver '%s'\n", driver->driver.name);31         if ((retval = driver->bind (&udc->gadget)) != 0) {32                 printk("bind fail\n");33                 udc->driver = 0;34                 udc->gadget.dev.driver = 0;35                 return retval;36         }37         printk( "after driver bind:%p\n" , driver->bind);38 39         mdelay(300);40         __raw_writel(__raw_readl(REG_PWRON) | 0x400, REG_PWRON);//power on usb D+ high41 42         return 0;43 }
复制代码

 

controller是udc中很重要的一个变量,结构为

 

复制代码
 1 struct nuc900_udc { 2         spinlock_t            lock; 3  4         struct nuc900_ep        ep[NUC900_ENDPOINTS]; 5         struct usb_gadget        gadget; 6         struct usb_gadget_driver    *driver; 7         struct platform_device        *pdev; 8  9         struct clk                      *clk;10         struct resource                 *res;11         void __iomem                    *reg;12         int                             irq;13 14         enum ep0_state                     ep0state;15 16         u8                usb_devstate;17         u8                usb_address;18 19 20         u8                usb_dma_dir;21 22         u8                usb_dma_trigger;//bool. dma triggered23         u8                usb_dma_trigger_next;//need trigger again24         u8                usb_less_mps;25         u32                usb_dma_cnt;//one dma transfer count26         u32                usb_dma_loop;//for short packet only;dma loop, each loop 32byte;27         u32                     usb_dma_owner;28 29         struct usb_ctrlrequest            crq;30         s32                setup_ret;31 32         u32                             irq_enbl;33 };
复制代码

 

这个结构中大部分不需要关注,需要关注的是第5行:

 struct usb_gadget        gadget;
定义在gadget.h中,这linux标准的结构体:

复制代码
 1 struct usb_gadget { 2     /* readonly to gadget driver */ 3     const struct usb_gadget_ops    *ops; 4     struct usb_ep            *ep0; 5     struct list_head        ep_list;    /* of usb_ep */ 6     enum usb_device_speed        speed; 7     unsigned            is_dualspeed:1; 8     unsigned            is_otg:1; 9     unsigned            is_a_peripheral:1;10     unsigned            b_hnp_enable:1;11     unsigned            a_hnp_support:1;12     unsigned            a_alt_hnp_support:1;13     const char            *name;14     struct device            dev;15 };
复制代码

 

大致先扫一下这个结构,然后回到

usb_gadget_register_driver函数。

大体意思就是对上面的结构体进行了一番赋值,具体意义再回头看

然后在31行调用

 if ((retval = driver->bind (&udc->gadget)) != 0)

第一个bind被调用了。

继续贴代码

复制代码
 1 static int composite_bind(struct usb_gadget *gadget) 2 { 3     struct usb_composite_dev    *cdev; 4     int                status = -ENOMEM; 5  6     cdev = kzalloc(sizeof *cdev, GFP_KERNEL); 7     if (!cdev) 8         return status; 9 10     spin_lock_init(&cdev->lock);11     cdev->gadget = gadget;12     set_gadget_data(gadget, cdev);13     INIT_LIST_HEAD(&cdev->configs);14 15     /* preallocate control response and buffer */16     cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);17     if (!cdev->req)18         goto fail;19     cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);20     if (!cdev->req->buf)21         goto fail;22     cdev->req->complete = composite_setup_complete;23     gadget->ep0->driver_data = cdev;24 25     cdev->bufsiz = USB_BUFSIZ;26     cdev->driver = composite;27 28     usb_gadget_set_selfpowered(gadget);29 30     /* interface and string IDs start at zero via kzalloc.31      * we force endpoints to start unassigned; few controller32      * drivers will zero ep->driver_data.33      */34     usb_ep_autoconfig_reset(cdev->gadget);35 36     /* standardized runtime overrides for device ID data */37     if (idVendor)38         cdev->desc.idVendor = cpu_to_le16(idVendor);39     if (idProduct)40         cdev->desc.idProduct = cpu_to_le16(idProduct);41     if (bcdDevice)42         cdev->desc.bcdDevice = cpu_to_le16(bcdDevice);43 44     /* composite gadget needs to assign strings for whole device (like45      * serial number), register function drivers, potentially update46      * power state and consumption, etc47      */48     status = composite->bind(cdev);49     if (status < 0)50         goto fail;51 52     cdev->desc = *composite->dev;53     cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket;54 55     /* strings can't be assigned before bind() allocates the56      * releavnt identifiers57      */58     if (cdev->desc.iManufacturer && iManufacturer)59         string_override(composite->strings,60             cdev->desc.iManufacturer, iManufacturer);61     if (cdev->desc.iProduct && iProduct)62         string_override(composite->strings,63             cdev->desc.iProduct, iProduct);64     if (cdev->desc.iSerialNumber && iSerialNumber)65         string_override(composite->strings,66             cdev->desc.iSerialNumber, iSerialNumber);67 68     status = device_create_file(&gadget->dev, &dev_attr_suspended);69     if (status)70         goto fail;71 72     INFO(cdev, "%s ready\n", composite->name);73     return 0;74 75 fail:76     composite_unbind(gadget);77     return status;78 }
复制代码

 

还是简单分析

11~12行就是你中有我,我中有你

13行值得注意一下,初始化一个链表,config就是配置链表。

一个设备可能有多个配置

一个配置可能有多个接口

一个接口可能有多个端点或设置

15~23行 都与ep0这个控制端口有关,控制端口的相关内直接在设备bind的时候做也比较合理。

26行 cdev->driver = composite; //还记得composite指向的是谁,就是zero_driver

 这就bind好了吧。

直接看48行

 status = composite->bind(cdev);

第二个bind被调用,

satic int __init zero_bind(struct usb_composite_dev *cdev)

 

这个函数需要关注的这几行

复制代码
    if (loopdefault) {        loopback_add(cdev, autoresume != 0);        sourcesink_add(cdev, autoresume != 0);    } else {        sourcesink_add(cdev, autoresume != 0);        loopback_add(cdev, autoresume != 0);    }
复制代码

应该就是gadget zero的两种配置 
sourcesink_add()在f_sourcesink.c中,是自己实现的

在此函数中调用

 

1 return usb_add_config(cdev, &sourcesink_driver);

 

 

复制代码
1 static struct usb_configuration sourcesink_driver = {2     .label        = "source/sink",3     .strings    = sourcesink_strings,4     .bind        = sourcesink_bind_config,5     .setup        = sourcesink_setup,6     .bConfigurationValue = 3,7     .bmAttributes    = USB_CONFIG_ATT_SELFPOWER,8     /* .iConfiguration = DYNAMIC */9 };
复制代码

注意这个结构体中又出现一个bind

usb_add_config 在composite.c 中

复制代码
 1 int usb_add_config(struct usb_composite_dev *cdev, 2         struct usb_configuration *config) 3 { 4     int                status = -EINVAL; 5     struct usb_configuration    *c; 6  7     DBG(cdev, "adding config #%u '%s'/%p\n", 8             config->bConfigurationValue, 9             config->label, config);10 11     if (!config->bConfigurationValue || !config->bind)12         goto done;13 14     /* Prevent duplicate configuration identifiers */15     list_for_each_entry(c, &cdev->configs, list) {16         if (c->bConfigurationValue == config->bConfigurationValue) {17             status = -EBUSY;18             goto done;19         }20     }21 22     config->cdev = cdev;23     list_add_tail(&config->list, &cdev->configs);24 25     INIT_LIST_HEAD(&config->functions);26     config->next_interface_id = 0;27 28     status = config->bind(config);29     if (status < 0) {30         list_del(&config->list);31         config->cdev = NULL;32     } else {33         unsigned    i;34 35         DBG(cdev, "cfg %d/%p speeds:%s%s\n",36             config->bConfigurationValue, config,37             config->highspeed ? " high" : "",38             config->fullspeed39                 ? (gadget_is_dualspeed(cdev->gadget)40                     ? " full"41                     : " full/low")42                 : "");43 44         for (i = 0; i < MAX_CONFIG_INTERFACES; i++) {45             struct usb_function    *f = config->interface[i];46 47             if (!f)48                 continue;49             DBG(cdev, "  interface %d = %s/%p\n",50                 i, f->name, f);51         }52     }53 54     /* set_alt(), or next config->bind(), sets up55      * ep->driver_data as needed.56      */57     usb_ep_autoconfig_reset(cdev->gadget);58 59 done:60     if (status)61         DBG(cdev, "added config '%s'/%u --> %d\n", config->label,62                 config->bConfigurationValue, status);63     return status;64 }
复制代码

23行,把配置插入链表。(bind设备的时候初始化的那个链表)

25行,又初始化一个function链表。(一个配置可以有多个接口)

28行,status = config->bind(config);
  第三次调用bind

找到config->bind的真身,在f_sourcesink.c中

 

复制代码
 1 static int __init sourcesink_bind_config(struct usb_configuration *c) 2 { 3     struct f_sourcesink    *ss; 4     int            status; 5  6     ss = kzalloc(sizeof *ss, GFP_KERNEL); 7     if (!ss) 8         return -ENOMEM; 9     init_completion(&ss->gdt_completion);10     ss->function.name = "source/sink";11     ss->function.descriptors = fs_source_sink_descs;12     ss->function.bind = sourcesink_bind;13     ss->function.unbind = sourcesink_unbind;14     ss->function.set_alt = sourcesink_set_alt;15     ss->function.disable = sourcesink_disable;16 17     status = usb_add_function(c, &ss->function);18     if (status)19         kfree(ss);20     return status;21 }
复制代码

 

留意一下12行function.bind

17行status = usb_add_function(c, &ss->function);

函数在composite.c中

复制代码
 1 int usb_add_function(struct usb_configuration *config, 2         struct usb_function *function) 3 { 4     int    value = -EINVAL; 5  6     DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n", 7             function->name, function, 8             config->label, config); 9 10     if (!function->set_alt || !function->disable)11         goto done;12 13     function->config = config;14     list_add_tail(&function->list, &config->functions);15 16     /* REVISIT *require* function->bind? */17     if (function->bind) {18         value = function->bind(config, function);19         if (value < 0) {20             list_del(&function->list);21             function->config = NULL;22         }23     } else24         value = 0;25 26     /* We allow configurations that don't work at both speeds.27      * If we run into a lowspeed Linux system, treat it the same28      * as full speed ... it's the function drivers that will need29      * to avoid bulk and ISO transfers.30      */31     if (!config->fullspeed && function->descriptors)32         config->fullspeed = true;33     if (!config->highspeed && function->hs_descriptors)34         config->highspeed = true;35 36 done:37     if (value)38         DBG(config->cdev, "adding '%s'/%p --> %d\n",39                 function->name, function, value);40     return value;41 }
复制代码

 

14行,同样把function插入链表

18行,第四次调用bind

回顾一下第一次bind设备,第二次bind配置,第三次bind接口,第四次该端点了

直接到f_sourcesink.c中:

复制代码
 1 static int __init 2 sourcesink_bind(struct usb_configuration *c, struct usb_function *f) 3 { 4     struct usb_composite_dev *cdev = c->cdev; 5     struct f_sourcesink    *ss = func_to_ss(f); 6     int    id; 7  8     /* allocate interface ID(s) */ 9     id = usb_interface_id(c, f);10     if (id < 0)11         return id;12     source_sink_intf.bInterfaceNumber = id;13 14     /* allocate endpoints */15     ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);16     if (!ss->in_ep) {17 autoconf_fail:18         ERROR(cdev, "%s: can't autoconfigure on %s\n",19             f->name, cdev->gadget->name);20         return -ENODEV;21     }22     ss->in_ep->driver_data = cdev;    /* claim */23 24     ss->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc);25     if (!ss->out_ep)26         goto autoconf_fail;27     ss->out_ep->driver_data = cdev;    /* claim */28 29     /* support high speed hardware */30     if (gadget_is_dualspeed(c->cdev->gadget)) {31         hs_source_desc.bEndpointAddress =32                 fs_source_desc.bEndpointAddress;33         hs_sink_desc.bEndpointAddress =34                 fs_sink_desc.bEndpointAddress;35         f->hs_descriptors = hs_source_sink_descs;36     }37 38     DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",39             gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",40             f->name, ss->in_ep->name, ss->out_ep->name);41     return 0;42 }
复制代码

 

15 和24 行分别获得了一个端口。 gadget zero设备使用了两个端口来收发数据。

 

以上差不多就是gadget驱动的注册和bind的过程。

 

原创粉丝点击