文本旨在简单介绍一下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. 华为核心交换机绑定IP+MAC+端口案例

    1         案例背景 某网络改造项目,核心交换机为华为S5700,接入交换机为不同型号交换机,如下模拟拓扑,客户端接入交换机1通过Access模式与核心交换机连接,该交换机下只有一个Vlan2 ...

  2. Django 框架 数据库操作

    数据库与ORM 1    django默认支持sqlite,mysql, oracle,postgresql数据库.  <1> sqlite django默认使用sqlite的数据库,默认 ...

  3. 使用MySQL的mysqldump命令备份数据库和把数据库备份文件恢复

    1,备份数据库 mysql -uroot -p123456 db_name > /root/db_name.dump 2,数据库备份文件恢复 mysql -uroot -p123456 db_n ...

  4. shell的函数返回值

    1.默认function的返回值包含0 和1,执行成功,返回0,执行失败,返回1,可以采用$?来获取执行结果 2.函数如何返回字符串呢,可以采用echo函数 #!/bin/bashfunction t ...

  5. linux sed 常见字符串处理

    1.删除特殊字符 将 1.1.0_boke_1.0.1 转换为110_boke_101 command: new_var=`echo 1.1.0_boke_1.0.1 |sed  s/\.//g` ( ...

  6. ida pro 使用

    交互式反汇编器专业版(Interactive Disassembler Professional),人们常称其为IDA Pro,或简称为IDA.是目前最棒的一个静态反编译软件,为众多0day世界的成员 ...

  7. python threading

    //test.py 1 import threading 2 import time 3 4 exitFlag = 0 5 6 class myThread (threading.Thread): 7 ...

  8. go https json

    好吧,再来一个看起来高档点的吧 自从知道 Go有本地调用后,我就回到windows了 哈哈,以下内容,均在win10下搞定 预备:先做两个文件,服务器端的私钥KEY和公钥证书 1. openssl g ...

  9. iOS LeftMenu抽屉效果与ScrollView共存时的手势冲突

    公司有个项目,需要做左侧滑动,首页是ScrollView嵌套TableView.首页是一个ScrollView,所以当contentOffset是0.0的时候,无法直接滑动出抽屉效果,用户体验感非常差 ...

  10. glibc源码下载

    https://www.gnu.org/software/libc/ Download sources Releases are available by source branch checkout ...