Linux驱动之USB总线驱动程序框架简析

通用串行总线(USB)是主机和外围设备之间的一种链接。USB总线规范有1.1版和2.0版,固然如今已经有了3.0版本。USB1.1支持两种传输速度:低速为1.5Mbps,高速为12Mbps。USB2.0的传输速度能够高达480Mbps。USB2.0向下兼容USB1.1,能够将USB1.1的设备链接到USB2.0控制器上,也能够把USB2.0的设备链接到USB1.1控制器上。S3C2440的USB主机控制器支持USB1.1总线规范。windows

USB总线的拓扑结构以下图所示:USB主机控制器(USB Host Controller)经过根集线器(Root Hub)与其余USB设备相连。集线器也属于USB设备,经过它能够在一个USB接口上扩展出多个接口。除根集线器外,最多能够层叠5个根集线器。一条USB主线上最多能够外接127个设备,固然包括根集线器和其余集线器。整个结构图是一个星型结构,一条USB总线上全部设备共享一条通往主机的数据通道,同一时刻只能有一个设备与主机通信框架

经过USB主机控制器来管理外接的USB设备。USB控制器实际上由USB控制器硬件+USB控制器软件组成的。USB主机控制器分为3种:UHCI、OHCI、EHCI。UHCI与OHCI支持USB1.1协议;EHCI支持USB2.0协议。UHCI,它的硬件比较简单,因此软件相对就比较复杂;而OHCI,它的硬件具有更多性能,相反的软件作的事情就比较少。S3C2440的USB主机控制器支持OHCI主机控制器。HCI的英文全称为(Host Controller Interface)。函数

根据以上知识能够知道其实USB驱动程序能够分为两类:USB主机控制器驱动程序(Host Controller Drivers)、USB设备驱动程序(USB device drivers)。它们在内核中的层次以下图所示性能

USB主机控制器驱动程序提供访问USB设备的接口,它只是一个数据通道,至于这些数据有什么做用,这要靠上层的USB设备驱动程序来解释。USB设备驱动程序使用下层的驱动提供的数据接口来访问USB设备,不须要关心具体的传输细节。在Linux2.6中,USB主机控制器驱动程序已经帮咱们写好了,下面就简单分析一下USB主机控制器驱动程序。spa

一、在分析USB主机控制器驱动程序以前先来看几个问题:翻译

当把你的安卓手机经过USB接口接到电脑上的USB口时,会有以下现象code

一、右下角弹出“发现andrido phone”。orm

二、跳出一个对话框,提示你安装驱动程序。对象

那么问题便来了blog

问题一、既然尚未“驱动程序”,为什么能知道是“andrido phone”
答1:windows里已经有了USB的总线设备驱动程序,接入USB设备后,是“总线驱动程序”知道你是“andrido phone”,提示你安装的是“设备驱动程序”,USB总线驱动程序负责:识别USB设备,给USB设备找到对应的驱动程序

问题二、USB设备种类很是多,为何一接入电脑就能识别出来?
答2:PC和USB设备都得遵照一些规范。
好比:USB设备接入电脑后,PC会发出“你是什么?”USB设备必须回答“我是XXX”,而且回答的语言必须是中文;
USB总线驱动程序会发出某些命令获取设备信息(描述符),USB设备必须返回“描述符”给PC。

问题三、PC机上接有很是多的USB设备,怎么分辨它们?
答3:接在USB总线上的每一个USB设备都有本身的编号(地址),每个USB设备接入PC时,USB总线驱动程序都会给它分配一个编号,PC机想访问某个USB设备时,要先发出的命令都含有对方的编号(地址)

问题四、USB设备刚接入PC时,尚未编号,那么PC怎么把“分配的编号”告诉它?
答4:新接入的USB设备的默认编号是0,在未分配新的编号前,PC使用0编号和它通信。

问题五、为何一接入USB设备,PC机就能发现它(又没有中断)
答5:USB接口只有4条线:5V、GND、D-、D+。PC的USB口内部D-和D+接有15K的下拉电阻,未接USB设备时为低电平;USB设备的USB口内部,D-或D+接有1.5k的上拉电阻:它一接入就会把PC USB口D-(全速)或D+(高速)拉高,从硬件的角度通知PC有新设备接入。

从以上问答能够看出来USB总线驱动程序的做用能够总结为:(后面会再详细说明)

一、识别设备
二、找到并安装对应的USB设备驱动
三、提供USB读写函数(不了解数据含义)

 

 二、接着再提出一些USB的概念

一、USB是主从结构的
全部的USB传输都是从USB主机这方发起,USB设备没有主动通知USB主机的能力
例子:USB鼠标滑动一下,马上产生数据,可是它没有能力通知PC读数据,只能被动的等待PC机被读

二、USB的传输类型:
a、控制传输:可靠,时间有保证,好比USB设备的识别过程
b、批量传输:可靠,时间没有保证,好比:U盘
c、中断传输(仍是查询方式):可靠,实时,好比:USB鼠标
d、实时传输:数据不可靠,实时,好比:USB摄像头

三、USB传输的对象:端点(endpoint)
咱们说“读U盘”、“写U盘”能够细化为:把数据写到U盘的端点1,把数据从U盘的端点2里读出数据
除了端点0以外,每个端点只支持一个方向的数据传输
端点0用于控制传输,既能输出也能输入

四、每个端点都只有一个传输类型,传输方向

五、术语里、程序里说的输入(IN)、输出(OUT)“都是”基于USB主机的立场说的。
好比鼠标的数据是从鼠标传入PC机的,对应的端点称为“输入端点”

 

三、经过前面的描述对于USB已经有了大体的了解了,接着经过程序分析来更深刻的了解USB设备的识别过程:

把一个USB鼠标接到开发板上会弹出以下信息:

usb 1-1: new full speed USB device using s3c2410-ohci and address 3 usb 1-1: configuration #1 chosen from 1 choice scsi1 : SCSI emulation for USB Mass Storage devices 拔掉 usb 1-1: USB disconnect, address 3 再接上 usb 1-1: USB disconnect, address 3usb 1-1: new full speed USB device using s3c2410-ohci and address 4 usb 1-1: configuration #1 chosen from 1 choice scsi2 : SCSI emulation for USB Mass Storage devices

其中address3,address4就是USB核心程序给USB设备分配的地址。查找“USB device using ”关键字,在drivers/usb/core/hub.c的2186行找到了这句话:"%s %s speed %sUSB device using %s and address %d\n"。它位于hub_port_init函数下,层层查找能够发现最终是hub_irq函数致使了hub_port_init被调用。hub_irq不是一个真正的中断处理程序,它只是集线器USB设备的数据接收完成后的回调函数,其实drivers/usb/core/hub.c也不过是一个USB设备驱动程序而已。

drivers/usb/core/hub.c程序先放到一边,待会再来分析它。先尝试找一下USB控制器的驱动程序,在drivers\usb\host\Ohci-s3c2410.c找到了这个驱动程序, 它以平台设备驱动程序为框架,drivers\usb\host\Ohci-s3c2410.c属于driver层

static struct platform_driver ohci_hcd_s3c2410_driver = { .probe = ohci_hcd_s3c2410_drv_probe,//一旦匹配devices则被调用
    .remove        = ohci_hcd_s3c2410_drv_remove, .shutdown = usb_hcd_platform_shutdown, /*.suspend = ohci_hcd_s3c2410_drv_suspend, */
    /*.resume = ohci_hcd_s3c2410_drv_resume, */ .driver = { .owner = THIS_MODULE, .name = "s3c2410-ohci", }, };

接着搜索“s3c2410-ohci”,找到了devices层,位于arch\arm\plat-s3c24xx\Devs.c

struct platform_device s3c_device_usb = { .name = "s3c2410-ohci", .id = -1, .num_resources = ARRAY_SIZE(s3c_usb_resource), .resource = s3c_usb_resource, .dev = { .dma_mask = &s3c_device_usb_dmamask, .coherent_dma_mask = 0xffffffffUL } };

到这里咱们尚未找到插上USB设备后第一个运行的函数,咱们猜想这个函数一定是一个中断,USB控制器接收到数据后将会产生一个中断给MCU,而后MCU再进行处理。如今咱们就是要找到这一个中断函数,在drivers/usb/core/hub.c中找到了usb_add_hcd函数,它位于usb_hcd_s3c2410_probe函数下,这个函数一旦有USB控制器设备匹配就会被调用。

static int usb_hcd_s3c2410_probe (const struct hc_driver *driver, struct platform_device *dev) { ... ... retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED);//注册OHCI控制器中断
    if (retval != 0) goto err_ioremap; ... ... }

继续往下看usb_add_hcd函数,它位于drivers\usb\core\hcd.c下

int usb_add_hcd(struct usb_hcd *hcd, unsigned int irqnum, unsigned long irqflags) { ... ... if ((retval = request_irq(irqnum, &usb_hcd_irq, irqflags, hcd->irq_descr, hcd)) != 0) {//中断USB中断
            dev_err(hcd->self.controller, "request interrupt %d failed\n", irqnum); goto err_request_irq; } hcd->irq = irqnum; ... ... } 

在这里看到了调用注册中断函数,而且它的中断处理回调函数为usb_hcd_irq,它一样位于drivers\usb\core\hcd.c下

irqreturn_t usb_hcd_irq (int irq, void *__hcd) { struct usb_hcd        *hcd = __hcd; int            start = hcd->state; if (unlikely(start == HC_STATE_HALT ||
        !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) return IRQ_NONE; if (hcd->driver->irq (hcd) == IRQ_NONE) return IRQ_NONE; set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);//设置中断标志

    if (unlikely(hcd->state == HC_STATE_HALT)) usb_hc_died (hcd); return IRQ_HANDLED; }

这样,经过usb_hcd_irq 函数的做用,最终将会调用到drivers/usb/core/hub.c下的hub_irq函数。以前说过drivers/usb/core/hub.c其实也是一个USB设备驱动程序,它实际上是一个根集线器驱动程序。这里直接列出匹配USB设备驱动程序的过程。

 

hub_irq kick_khubd hub_thread hub_events hub_port_connect_change usb_alloc_dev(hdev, hdev->bus, port1); dev->dev.type = &usb_device_type; choose_address(udev); //给新设备分配编号(地址)
 hub_port_init //usb 1-1: new full speed USB device using s3c2410-ohci and address 3
                        hub_set_address  //把编号(地址)告诉USB设备
                        usb_get_device_descriptor(udev, 8);//获取设备描述符
 usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE); usb_new_device(udev); usb_get_configuration(udev);//把全部的描述符都读出来并解析
 usb_parse_configuration device_add//把device放入usb_bus_type的dev链表, //从usb_bus_type的driver链表中取出usb_driver //把usb_interface和usb_driver的id_table比较 //若是能匹配,调用usb_driver的probe函数

 

大体总结一下:

一、当接上USB鼠标后,USB主机控制器检查到D-或D+接口变高知道了有设备接入。

二、USB主机控制器把新分配的编号告诉USB设备,这时候仍是以0地址通信的。

三、USB主机控制器以0地址从USB设备得到设备描述符的前8个字节,从这8个字节能够知道后面全部设备描述符总共的大小

四、知道了剩下设备描述符的大小,就能够将剩余的设备描述符都读出来

五、接着把device放入usb_bus_type的dev链表

六、从usb_bus_type的driver链表中取出usb_driver

七、把usb_interface和usb_driver的id_table比较(对于USB鼠标最终比较的是接口描述符下的bInterfaceClass;bInterfaceSubClass;bInterfaceProtocol;三个信息,中文翻译为设备类型,设备子类型,设备协议)

八、若是能匹配,调用usb_driver的probe函数

 从上面总结能够看出,要写USB设备的驱动程序就须要对usb_driver结构体进行初始。

 

USB各个描述符的关系以下,具体描述符结构将在下一节讲解

 

 以上就是USB总线驱动程序的框架,USB设备是一个很是复杂的东西,这里只是简单的描述,并未深刻理解。目的是为了编写USB设备驱动程序。

相关文章
相关标签/搜索