为了解决一个问题,简单看了一遍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

 static struct usb_composite_driver zero_driver = {
.name = "zero",
.dev = &device_desc,
.strings = dev_strings,
.bind = zero_bind,
.unbind = zero_unbind,
.suspend = zero_suspend,
.resume = zero_resume,
};

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

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

 static int __init init(void)
{
return usb_composite_register(&zero_driver);
}

调用
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中

 static struct usb_gadget_driver composite_driver = {
.speed = USB_SPEED_HIGH, .bind = composite_bind,
.unbind = composite_unbind, .setup = composite_setup,
.disconnect = composite_disconnect, .suspend = composite_suspend,
.resume = composite_resume, .driver = {
.owner = THIS_MODULE,
},
};

composite = driver;

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

最后调用usb_gadget_register_driver(&composite_driver);

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

nuc950在nuc900_udc.c中:

 int usb_gadget_register_driver (struct usb_gadget_driver *driver)
{
struct nuc900_udc *udc = &controller;
int retval; printk("usb_gadget_register_driver() '%s'\n", driver->driver.name); if (!udc)
return -ENODEV; if (udc->driver)
return -EBUSY;
if (!driver->bind || !driver->unbind || !driver->setup
|| driver->speed == USB_SPEED_UNKNOWN)
return -EINVAL;
printk("driver->speed=%d\n", driver->speed);
udc->gadget.name = gadget_name;
udc->gadget.ops = &nuc900_ops;
udc->gadget.is_dualspeed = ;
udc->gadget.speed = USB_SPEED_HIGH;//USB_SPEED_FULL;
udc->ep0state = EP0_IDLE; udc->gadget.dev.release = nop_release; udc->driver = driver; udc->gadget.dev.driver = &driver->driver; printk( "binding gadget driver '%s'\n", driver->driver.name);
if ((retval = driver->bind (&udc->gadget)) != ) {
printk("bind fail\n");
udc->driver = ;
udc->gadget.dev.driver = ;
return retval;
}
printk( "after driver bind:%p\n" , driver->bind); mdelay();
__raw_writel(__raw_readl(REG_PWRON) | 0x400, REG_PWRON);//power on usb D+ high return ;
}

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

 struct nuc900_udc {
spinlock_t lock; struct nuc900_ep ep[NUC900_ENDPOINTS];
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
struct platform_device *pdev; struct clk *clk;
struct resource *res;
void __iomem *reg;
int irq; enum ep0_state ep0state; u8 usb_devstate;
u8 usb_address; u8 usb_dma_dir; u8 usb_dma_trigger;//bool. dma triggered
u8 usb_dma_trigger_next;//need trigger again
u8 usb_less_mps;
u32 usb_dma_cnt;//one dma transfer count
u32 usb_dma_loop;//for short packet only;dma loop, each loop 32byte;
u32 usb_dma_owner; struct usb_ctrlrequest crq;
s32 setup_ret; u32 irq_enbl;
};

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

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

 struct usb_gadget {
/* readonly to gadget driver */
const struct usb_gadget_ops *ops;
struct usb_ep *ep0;
struct list_head ep_list; /* of usb_ep */
enum usb_device_speed speed;
unsigned is_dualspeed:;
unsigned is_otg:;
unsigned is_a_peripheral:;
unsigned b_hnp_enable:;
unsigned a_hnp_support:;
unsigned a_alt_hnp_support:;
const char *name;
struct device dev;
};

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

usb_gadget_register_driver函数。

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

然后在31行调用

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

第一个bind被调用了。

继续贴代码

 static int composite_bind(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev;
int status = -ENOMEM; cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
if (!cdev)
return status; spin_lock_init(&cdev->lock);
cdev->gadget = gadget;
set_gadget_data(gadget, cdev);
INIT_LIST_HEAD(&cdev->configs); /* preallocate control response and buffer */
cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
if (!cdev->req)
goto fail;
cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);
if (!cdev->req->buf)
goto fail;
cdev->req->complete = composite_setup_complete;
gadget->ep0->driver_data = cdev; cdev->bufsiz = USB_BUFSIZ;
cdev->driver = composite; usb_gadget_set_selfpowered(gadget); /* interface and string IDs start at zero via kzalloc.
* we force endpoints to start unassigned; few controller
* drivers will zero ep->driver_data.
*/
usb_ep_autoconfig_reset(cdev->gadget); /* standardized runtime overrides for device ID data */
if (idVendor)
cdev->desc.idVendor = cpu_to_le16(idVendor);
if (idProduct)
cdev->desc.idProduct = cpu_to_le16(idProduct);
if (bcdDevice)
cdev->desc.bcdDevice = cpu_to_le16(bcdDevice); /* composite gadget needs to assign strings for whole device (like
* serial number), register function drivers, potentially update
* power state and consumption, etc
*/
status = composite->bind(cdev);
if (status < )
goto fail; cdev->desc = *composite->dev;
cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket; /* strings can't be assigned before bind() allocates the
* releavnt identifiers
*/
if (cdev->desc.iManufacturer && iManufacturer)
string_override(composite->strings,
cdev->desc.iManufacturer, iManufacturer);
if (cdev->desc.iProduct && iProduct)
string_override(composite->strings,
cdev->desc.iProduct, iProduct);
if (cdev->desc.iSerialNumber && iSerialNumber)
string_override(composite->strings,
cdev->desc.iSerialNumber, iSerialNumber); status = device_create_file(&gadget->dev, &dev_attr_suspended);
if (status)
goto fail; INFO(cdev, "%s ready\n", composite->name);
return ; fail:
composite_unbind(gadget);
return status;
}

还是简单分析

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 != );
sourcesink_add(cdev, autoresume != );
} else {
sourcesink_add(cdev, autoresume != );
loopback_add(cdev, autoresume != );
}

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

在此函数中调用

 return usb_add_config(cdev, &sourcesink_driver);
 static struct usb_configuration sourcesink_driver = {
.label = "source/sink",
.strings = sourcesink_strings,
.bind = sourcesink_bind_config,
.setup = sourcesink_setup,
.bConfigurationValue = ,
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
/* .iConfiguration = DYNAMIC */
};

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

usb_add_config 在composite.c 中

 int usb_add_config(struct usb_composite_dev *cdev,
struct usb_configuration *config)
{
int status = -EINVAL;
struct usb_configuration *c; DBG(cdev, "adding config #%u '%s'/%p\n",
config->bConfigurationValue,
config->label, config); if (!config->bConfigurationValue || !config->bind)
goto done; /* Prevent duplicate configuration identifiers */
list_for_each_entry(c, &cdev->configs, list) {
if (c->bConfigurationValue == config->bConfigurationValue) {
status = -EBUSY;
goto done;
}
} config->cdev = cdev;
list_add_tail(&config->list, &cdev->configs); INIT_LIST_HEAD(&config->functions);
config->next_interface_id = ; status = config->bind(config);
if (status < ) {
list_del(&config->list);
config->cdev = NULL;
} else {
unsigned i; DBG(cdev, "cfg %d/%p speeds:%s%s\n",
config->bConfigurationValue, config,
config->highspeed ? " high" : "",
config->fullspeed
? (gadget_is_dualspeed(cdev->gadget)
? " full"
: " full/low")
: ""); for (i = ; i < MAX_CONFIG_INTERFACES; i++) {
struct usb_function *f = config->interface[i]; if (!f)
continue;
DBG(cdev, " interface %d = %s/%p\n",
i, f->name, f);
}
} /* set_alt(), or next config->bind(), sets up
* ep->driver_data as needed.
*/
usb_ep_autoconfig_reset(cdev->gadget); done:
if (status)
DBG(cdev, "added config '%s'/%u --> %d\n", config->label,
config->bConfigurationValue, status);
return status;
}

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

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

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

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

 static int __init sourcesink_bind_config(struct usb_configuration *c)
{
struct f_sourcesink *ss;
int status; ss = kzalloc(sizeof *ss, GFP_KERNEL);
if (!ss)
return -ENOMEM;
init_completion(&ss->gdt_completion);
ss->function.name = "source/sink";
ss->function.descriptors = fs_source_sink_descs;
ss->function.bind = sourcesink_bind;
ss->function.unbind = sourcesink_unbind;
ss->function.set_alt = sourcesink_set_alt;
ss->function.disable = sourcesink_disable; status = usb_add_function(c, &ss->function);
if (status)
kfree(ss);
return status;
}

留意一下12行function.bind

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

函数在composite.c中

 int usb_add_function(struct usb_configuration *config,
struct usb_function *function)
{
int value = -EINVAL; DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n",
function->name, function,
config->label, config); if (!function->set_alt || !function->disable)
goto done; function->config = config;
list_add_tail(&function->list, &config->functions); /* REVISIT *require* function->bind? */
if (function->bind) {
value = function->bind(config, function);
if (value < ) {
list_del(&function->list);
function->config = NULL;
}
} else
value = ; /* We allow configurations that don't work at both speeds.
* If we run into a lowspeed Linux system, treat it the same
* as full speed ... it's the function drivers that will need
* to avoid bulk and ISO transfers.
*/
if (!config->fullspeed && function->descriptors)
config->fullspeed = true;
if (!config->highspeed && function->hs_descriptors)
config->highspeed = true; done:
if (value)
DBG(config->cdev, "adding '%s'/%p --> %d\n",
function->name, function, value);
return value;
}

14行,同样把function插入链表

18行,第四次调用bind

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

直接到f_sourcesink.c中:

 static int __init
sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_sourcesink *ss = func_to_ss(f);
int id; /* allocate interface ID(s) */
id = usb_interface_id(c, f);
if (id < )
return id;
source_sink_intf.bInterfaceNumber = id; /* allocate endpoints */
ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
if (!ss->in_ep) {
autoconf_fail:
ERROR(cdev, "%s: can't autoconfigure on %s\n",
f->name, cdev->gadget->name);
return -ENODEV;
}
ss->in_ep->driver_data = cdev; /* claim */ ss->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc);
if (!ss->out_ep)
goto autoconf_fail;
ss->out_ep->driver_data = cdev; /* claim */ /* support high speed hardware */
if (gadget_is_dualspeed(c->cdev->gadget)) {
hs_source_desc.bEndpointAddress =
fs_source_desc.bEndpointAddress;
hs_sink_desc.bEndpointAddress =
fs_sink_desc.bEndpointAddress;
f->hs_descriptors = hs_source_sink_descs;
} DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
f->name, ss->in_ep->name, ss->out_ep->name);
return ;
}

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

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

Linux gadget驱动分析1------驱动加载过程的更多相关文章

  1. 第42天学习打卡(Class类 Class类的常用方法 内存分析 类的加载过程 类加载器 反射操作泛型 反射操作注解)

    Class类 对象照镜子后得到的信息:某个类的属性.方法和构造器.某个类到底实现了哪些接口.对于每个类而言,JRE都为其保留一个不变的Class类型的对象.一个Class对象包含了特定某个结构(cla ...

  2. linux内核启动以及文件系统的加载过程

    Linux 内核启动及文件系统加载过程 当u-boot 开始执行 bootcmd 命令,就进入 Linux 内核启动阶段.普通 Linux 内核的启动过程也可以分为两个阶段.本文以项目中使用的 lin ...

  3. Dubbo源码分析之ExtensionLoader加载过程解析

    ExtensionLoader加载机制阅读: Dubbo的类加载机制是模仿jdk的spi加载机制:  Jdk的SPI扩展加载机制:约定是当服务的提供者每增加一个接口的实现类时,需要在jar包的META ...

  4. 分析ELF的加载过程

    http://blog.chinaunix.net/uid-72446-id-2060538.html 对于可执行文件来说,段的加载位置是固定的,程序段表中如实反映了段的加载地址.对于共享库来?段的加 ...

  5. 重温.NET下Assembly的加载过程

    最近在工作中牵涉到了.NET下的一个古老的问题:Assembly的加载过程.虽然网上有很多文章介绍这部分内容,很多文章也是很久以前就已经出现了,但阅读之后发现,并没能解决我的问题,有些点写的不是特别详 ...

  6. 重温.NET下Assembly的加载过程 ASP.NET Core Web API下事件驱动型架构的实现(三):基于RabbitMQ的事件总线

    重温.NET下Assembly的加载过程   最近在工作中牵涉到了.NET下的一个古老的问题:Assembly的加载过程.虽然网上有很多文章介绍这部分内容,很多文章也是很久以前就已经出现了,但阅读之后 ...

  7. NET下Assembly的加载过程

    NET下Assembly的加载过程 最近在工作中牵涉到了.NET下的一个古老的问题:Assembly的加载过程.虽然网上有很多文章介绍这部分内容,很多文章也是很久以前就已经出现了,但阅读之后发现,并没 ...

  8. Linux驱动的两种加载方式过程分析

    一.概念简述 在Linux下可以通过两种方式加载驱动程序:静态加载和动态加载. 静态加载就是把驱动程序直接编译进内核,系统启动后可以直接调用.静态加载的缺点是调试起来比较麻烦,每次修改一个地方都要重新 ...

  9. linux驱动动态与静态加载

    在Linux中驱动的加载方式有动态加载和静态加载.动态加载,即驱动不添加到内核中,在内核启动完成后,仅在用到这一驱动时才会进行加载静态加载,驱动编译进内核中,随内核的启动而完成驱动的加载.添加字符驱动 ...

随机推荐

  1. CSS知识点整理(2):框模型,定位

    1. 框模型:Box Model 规定了元素处理元素框处理元素内容.外边距.边框.内边距的方式. 2. 当边距给定的值 可以小于4个.CSS定义了一些规则.处理这中情况: 如果缺少左外边距的值,则使用 ...

  2. Rsync 传输不需要输入密码

    1.背景 1)        一个作为服务器端:VM3(IP: 3.9.8.151) 2)        一个作为客户端:VM2(IP: 3.9.8.157) 3)        服务器端和客户端网络 ...

  3. [Windows Server 2008] ASP.net安装方法

    ★ 欢迎来到[护卫神·V课堂],网站地址:http://v.huweishen.com★ 护卫神·V课堂 是护卫神旗下专业提供服务器教学视频的网站,每周更新视频.★ 本节我们将带领大家:安装ASP.n ...

  4. ES6 数组去重 方法用了filter或者 indexOf Array.from

  5. windows程序设为开机自启动

    在Windows文件管理器中输入 %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup 把程序快捷方式放到此处即可.

  6. js的一些工具类

    //写入cookie function setCookie(name, value) {     var Days = 30; //此 cookie 将被保存 30 天     var exp = n ...

  7. SSH技术介绍和Xshell公钥远程登陆

    SSH简介 传统的网络服务程序,比如FTP,POP,Telnet,本质上都是不安全的,因为它们在网络上用明文传送数据.用户账号和用户口令,很容易受到中间人攻击方式的攻击,攻击者会冒充真正的服务器接收用 ...

  8. Vova and Train (codeforces 1066A)

    数学题.用右边界以内的区间内的灯减去左边界以内区间内的灯,并且如果左边界正好有灯再减去一即可 我的代码 #include <bits/stdc++.h> using namespace s ...

  9. ganlgia-rrdcached

    一.介绍 rrdcached是一个高性能的RRD缓存守护进程,在不带来大量磁盘读/写文件i/o负荷的情况下,允许gmetad实例维护多个RRD文件.rrdcached可通过命令套接字控制,并且包含在大 ...

  10. Spring MVC学习总结(9)——Spring MVC整合swagger自动生成api接口文档

    Swagger 号称:世界最流行的API框架,官网:http://swagger.io/,Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视化 RESTful 风格的 Web 服务.总 ...