文本旨在简单介绍一下Linux中USB协议栈的代码框架:

下图是USB协议栈相关数据结构的关系图:

下面结合上图看一下系统初始化的流程:

1.USB子系统初始化:\drivers\usb\core\usb.c

subsys_initcall(usb_init);

static int __init usb_init(void)中调用了很多初始化函数,目前关注下面两个:

    retval = bus_register(&usb_bus_type);// 注册usb 总线
retval = usb_hub_init(); // 注册hub driver

1> bus_register()负责注册USB总线,当我们注册一个总线的时候,他会初始化两个链表,一个用来链接总线上所有的device,一个用来链接总线上所有的driver。当我们调用usb_register()来注册一个usb driver时,driver就会被连接到driver链表上。

//
// 初始化总线上的两个链表: one for device; one for driver
//
klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&bus->klist_drivers, NULL, NULL);

2> usb_hub_init()负责初始化hub,他会注册一个hub的驱动,并创建一个内核线程用来查询hub的状态(用来检查设备的插入)。

int usb_hub_init(void)
{
if (usb_register(&hub_driver) < ) { // 注册hub驱动
printk(KERN_ERR "%s: can't register hub driver\n",
usbcore_name);
return -;
} khubd_task = kthread_run(hub_thread, NULL, "khubd"); // 创建内核线程 hub_thread
if (!IS_ERR(khubd_task))
return ; // 成功,返回0 /* Fall through if kernel_thread failed */
usb_deregister(&hub_driver);
printk(KERN_ERR "%s: can't start khubd\n", usbcore_name); return -;
}

3> 当内核线程hub_thread()检测到新设备接入时,他会调用usb_new_device()来把新设备注册到总线的device链表上。

2.驱动与设备是如何绑定的:

不管是调用driver_register()来注册驱动,还是调用device_add()来注册设备;他们都会调用到总线的match函数,在调用驱动的probe函数

int driver_probe_device(struct device_driver * drv, struct device * dev)
{
int ret = ; if (!device_is_registered(dev))
return -ENODEV; // 调用bus的match()
if (drv->bus->match && !drv->bus->match(dev, drv))
goto done; pr_debug("%s: Matched Device %s with Driver %s\n",
drv->bus->name, dev->bus_id, drv->name); // 调用 probe()
ret = really_probe(dev, drv); done:
return ret;
}

3.最后来看一下host controller是如何驱动的:

以EHCI为例,他的驱动入口函数为\drivers\usb\host\ehci-hcd.c::ehci_hcd_init():

// ehci主控制器驱动的入口函数
//
static int __init ehci_hcd_init(void)
{
int retval = ; #ifdef PLATFORM_DRIVER
retval = platform_driver_register(&PLATFORM_DRIVER); // 注册平台驱动#endif #ifdef PCI_DRIVER
// host controller 挂在PCI总线下
retval = pci_register_driver(&PCI_DRIVER); // 注册一个PCI driver ......
}

这里分不同的情况:主控制器有可能挂在PCI总线下面(那它就是一个PCI设备),也有可能直接挂在CPU下面(ARM系统)。

通过分析对应的probe()函数,我们发现,不管是那种情况,他们都会去执行下面的调用:调用usb_create_hcd()来创建usb_hcd结构体;调用usb_add_hcd()来把主控制器添加到usb总线上。

1> usb_create_hcd()主要值得注意的是hc_driver ,它是访问HC的接口(HCDI)(针对上面的不同情况hc_driver的具体实现也不一样,比如PCI平台上的static const struct hc_driver ehci_pci_hc_driver 和 Freescale平台上的static const struct hc_driver ehci_fsl_hc_driver 就不一样):

struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
struct device *dev, char *bus_name)
{
struct usb_hcd *hcd; // 分配资源
hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);
.... // 初始化 hc_driver,并获取其中的product_desc;
// 比如pci的会显示:EHCI Host Controller
// 比如fsl的会显示:Freescale On-Chip EHCI Host Controller
hcd->driver = driver;
hcd->product_desc = (driver->product_desc) ? driver->product_desc :
"USB Host Controller"; return hcd;
}

2> usb_add_hcd()调用register_root_hub()来把EHCI注册为USB总线的控制器:

int usb_add_hcd(struct usb_hcd *hcd,
unsigned int irqnum, unsigned long irqflags)
{
int retval;
struct usb_device *rhdev;
....
// 调用hc_driver->reset()来重启hc
//
if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < ) {
dev_err(hcd->self.controller, "can't setup\n");
goto err_hcd_driver_setup;
}//
// //启动HC、root hub
//
if ((retval = hcd->driver->start(hcd)) < ) { // 注册root hub
if ((retval = register_root_hub(hcd)) != )
....
}

register_root_hub()调用usb_new_device(),通过hcd->self.root_hub(图中的usb_hcd.usb_bus.usb_device)注册主控制器设备:

static int register_root_hub(struct usb_hcd *hcd)
{
struct device *parent_dev = hcd->self.controller;
struct usb_device *usb_dev = hcd->self.root_hub; // 用来把usb_hcd注册到usb总线上
const int devnum = ;
int retval;// 会调用device_add,把device添加到usb总线上
retval = usb_new_device (usb_dev);
....
return retval;
}

通过最上面的图中的数据结构的关系可以看出来各个设备之间的关系,以及底层是通过bus_type、device、device_driver的模型来管理的。理解bus_type、device、device_driver的模型是理解Linux中整个USB子系统架构的关键。理解ehci_hcd和hc_driver是理解HCD的关键。上层驱动(USBD、USB device driver)最终都需要调用HCD来真正操作硬件(所以不同的硬件实现,HCD也不一样)。

Linux中USB协议栈的框架简介的更多相关文章

  1. EDK II之USB协议栈的实现简介

    本文旨在简单介绍一下 UEFI中USB协议栈的代码框架: 主要包括: USB主控制器驱动(HCDI:EFI_USB2_HC_PROTOCOL) USB总线驱动(USBDI:EFI_USB_IO_PRO ...

  2. Linux中的IO复用接口简介(文件监视?)

    I/O复用是Linux中的I/O模型之一.所谓I/O复用,指的是进程预先告诉内核,使得内核一旦发现进程指定的一个或多个I/O条件就绪,就通知进程进行处理,从而不会在单个I/O上导致阻塞. 在Linux ...

  3. Linux中的task,process, thread 简介

    本文的主要目的是介绍在Linux内核中,task,process, thread这3个名字之间的区别和联系.并且和WINDOWS中的相应观念进行比较.如果你已经很清楚了,那么就不用往下看了. LINU ...

  4. Linux中命令选项及参数简介

    登录Linux后,我们就可以在#或$符后面去输入命令,有的时候命令后面还会跟着“选项”(英文options)或“参数”(英文arguments).即Linux中命令格式为: command [opti ...

  5. 【转载】linux中互斥尽量用mutex,不用semaphore

    DEFINE_MUTEX是来自include/linux/mutex.h中的一个宏,用它可以定义一把互斥锁,在Linux内核中,其实是在2005年底才建立比较系统.完善的互斥锁机制,在那年冬天,来自R ...

  6. linux设备驱动程序--串行通信驱动框架分析

    linux 串行通信接口驱动框架 在学习linux内核驱动时,不论是看linux相关的书籍,又或者是直接看linux的源码,总是能在linux中看到各种各样的框架,linux内核极其庞杂,linux各 ...

  7. linux中rpm安装

    目录 一:linux中rpm安装 1.rpm简介 2.区别 3.RPM命令五种基本模式 二:RPM安装全面解析 1,下载软件包 2, 安装软件包 3, 尝试卸载 4, 更新(升级) 5,软件包名称: ...

  8. ethtool 在 Linux 中的实现框架和应用

    转载:http://www.ibm.com/developerworks/cn/linux/1304_wangjy_ethtools/index.html?ca=dat- 王 俊元, 软件工程师, I ...

  9. linux中的strings命令简介2

    摘自:http://blog.csdn.net/stpeace/article/details/46641069 linux中的strings命令简介 之前我们聊过linux strings的用法和用 ...

随机推荐

  1. Centos7 zookeeper单机/集群安装详解和开机自启

    ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件.它是一个为分布式应用提供一致性服务的软件,提供的功 ...

  2. 单例模式多线程安全写法(double-lock-check)

    原始版本 public static Object getInstance() { if (instance != null) { return instance; } instance = new ...

  3. CMB面试

    笔试: 1.登录验证userid password后台sql传入,有什么问题,预防措施? https://bbs.csdn.net/topics/120075716 2.cookie,session, ...

  4. Amazon RDS 上的 Microsoft SQL Server » 导入和导出 SQL Server 数据库

    导入和导出 SQL Server 数据库 Amazon RDS 支持使用完整备份文件 (.bak 文件) 对 Microsoft SQL Server 数据库进行本机备份和还原.您可以在单个便携式文件 ...

  5. CentOS6.5安装sqoop2

    1.下载软件:http://archive.cloudera.com/cdh5/cdh/5/ 2.解压:tar -zxvf mysofts/sqoop2-1.99.5-cdh5.6.0.tar.gz ...

  6. LR、Poly2、FM、FFM

    1. LR LR的linear Margin: 假设特征之间是相互独立的,忽略了feature pair等高阶信息:在LR中,特征组合等高阶信息是通过特征工程在特征侧引入的,那么有哪些模型不需要通过特 ...

  7. php curl POST multipart/form-data与application/x-www-form-urlencode的区别

    背景 CURL在 a.php 中以 POST方式向 b.php 提交数据,但b.php无法接收到数据,而 CURL 操作显示成功. 原来,"传递一个数组到CURLOPT_POSTFIELDS ...

  8. Kotlin 型变 + 星号投影(扯蛋)

    Kotlin中的型变: 1. in,顾名思义,就是只能作为传入参数的参数类型 2.out, ..............,就是只能作为返回类型参数的参数类型 星号投影: 我们引用官网的吧-- For ...

  9. VS2010 正在创建“Debug\test2.unsuccessfulbuild”,因为已指定“AlwaysCreate”。

    问题一:VS2010  正在创建“Debug\test2.unsuccessfulbuild”,因为已指定“AlwaysCreate”. 方法解决: The relevant setting is i ...

  10. leetcode Sort List 对链表进行排序

    描述: Sort a linked list in O(n log n) time using constant space complexity. 在O(n*log(n))的时间复杂度,常数级空间复 ...