Linux中USB协议栈的框架简介
文本旨在简单介绍一下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协议栈的框架简介的更多相关文章
- EDK II之USB协议栈的实现简介
本文旨在简单介绍一下 UEFI中USB协议栈的代码框架: 主要包括: USB主控制器驱动(HCDI:EFI_USB2_HC_PROTOCOL) USB总线驱动(USBDI:EFI_USB_IO_PRO ...
- Linux中的IO复用接口简介(文件监视?)
I/O复用是Linux中的I/O模型之一.所谓I/O复用,指的是进程预先告诉内核,使得内核一旦发现进程指定的一个或多个I/O条件就绪,就通知进程进行处理,从而不会在单个I/O上导致阻塞. 在Linux ...
- Linux中的task,process, thread 简介
本文的主要目的是介绍在Linux内核中,task,process, thread这3个名字之间的区别和联系.并且和WINDOWS中的相应观念进行比较.如果你已经很清楚了,那么就不用往下看了. LINU ...
- Linux中命令选项及参数简介
登录Linux后,我们就可以在#或$符后面去输入命令,有的时候命令后面还会跟着“选项”(英文options)或“参数”(英文arguments).即Linux中命令格式为: command [opti ...
- 【转载】linux中互斥尽量用mutex,不用semaphore
DEFINE_MUTEX是来自include/linux/mutex.h中的一个宏,用它可以定义一把互斥锁,在Linux内核中,其实是在2005年底才建立比较系统.完善的互斥锁机制,在那年冬天,来自R ...
- linux设备驱动程序--串行通信驱动框架分析
linux 串行通信接口驱动框架 在学习linux内核驱动时,不论是看linux相关的书籍,又或者是直接看linux的源码,总是能在linux中看到各种各样的框架,linux内核极其庞杂,linux各 ...
- linux中rpm安装
目录 一:linux中rpm安装 1.rpm简介 2.区别 3.RPM命令五种基本模式 二:RPM安装全面解析 1,下载软件包 2, 安装软件包 3, 尝试卸载 4, 更新(升级) 5,软件包名称: ...
- ethtool 在 Linux 中的实现框架和应用
转载:http://www.ibm.com/developerworks/cn/linux/1304_wangjy_ethtools/index.html?ca=dat- 王 俊元, 软件工程师, I ...
- linux中的strings命令简介2
摘自:http://blog.csdn.net/stpeace/article/details/46641069 linux中的strings命令简介 之前我们聊过linux strings的用法和用 ...
随机推荐
- js快速排序算法解析
数组的快速排序算法,和并归排序步骤基本类似. 都是先拆分,后合并.并归排序是:拆分容易,合并难. 快速排序是:拆分难,合并容易 要理解快速排序,首先要理解拆分逻辑 要素:找一个基准点,通过操作使得数列 ...
- js模拟队列----小优先队列
队列:先进先出,后进后出 var Queue = (function(){ var item = new WeakMap(); class Queue{ constructor(){ item.set ...
- python SMTP other
HTML 正文,带链接和图片 //test.py import smtplib from email.mime.image import MIMEImage from email.mime.text ...
- RF基础(一) RF内建函数库BuiltIn
Robot framework做为一个测试框架,并不是只能做selenium测试,是支持扩展的, 比如说,你引用requests库就可以做接口测试, 那么无论你用什么库 首先要了解, RF本身提供的内 ...
- Mybatis中的常用的三个查询方法
selectList 用于查询多条数据的情况,返回值是一个list集合.如果没有查到任何数据,返回没有元素的集合(空集合,不是null) selectOne 用于查询单条数据的情况,返回值是一个对象, ...
- Hibarnate控制台打印不出sql,并且报出异常:org.hibernate.exception.JDBCConnectionException: Cannot open connection
1.认真查看hibarnate.cfg.xml文件中连接数据库的各个信息是否正确;如果正确看下一步; 2.MySQL版本>=5.6.X,对应的mysql-connector-java jar 的 ...
- RocketMQ的使用
1 在resources目录下创建config目录,新建文件rocketmq.properties文件 # 指定namesrv地址 suning.rocketmq.namesrvAddr=localh ...
- linux ~/ 和 /
/是目录层的分隔.表示符.只有一个/表明是root,/etc/表明是根目录下面的etc目录(当然目录最后不需要/,但有/直接表明他是目录,没有末尾的/,那么/etc需要检测一下确定是目录还是文件,虽然 ...
- hdu 5126 cdq+Treap+BIT
这题说的是给了三维空间然后操作 寻求在 x1,y1,z1 x2, y2, z2; (x1<x2, y1<y2,z1<z2) 计算出在 以这两个端点为右下和左上端点的方体内的点的 ...
- web基础,用html元素制作web页面
用div,form制作登录页面,尽可能做得漂亮. 练习使用下拉列表选择框,无序列表,有序列表,定义列表. 观察常用网页的HTML元素,在实际的应用场景中,用已学的标签模仿制作. <!DOCTYP ...