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

发布时间:2024-08-19  

通用串行总线(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总线规范。

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主机控制器驱动程序。

1、在分析USB主机控制器驱动程序之前先来看几个问题:

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

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

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

那么问题便来了

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

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

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

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

问题5、为什么一接入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总线驱动程序的作用可以总结为:(后面会再详细说明)

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

 

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

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

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

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

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

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

 

3、通过前面的描述对于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 %dn'。它位于hub_port_init函数下,层层查找可以发现最终是hub_irq函数导致了hub_port_init被调用。hub_irq不是一个真正的中断处理程序,它只是集线器USB设备的数据接收完成后的回调函数,其实drivers/usb/core/hub.c也不过是一个USB设备驱动程序而已。


drivers/usb/core/hub.c程序先放到一边,待会再来分析它。先尝试找一下USB控制器的驱动程序,在driversusbhostOhci-s3c2410.c找到了这个驱动程序, 它以平台设备驱动程序为框架,driversusbhostOhci-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层,位于archarmplat-s3c24xxDevs.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函数,它位于driversusbcorehcd.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 failedn', irqnum);

            goto err_request_irq;

        }

        hcd->irq = irqnum;

    ...

    ...


在这里看到了调用注册中断函数,并且它的中断处理回调函数为usb_hcd_irq,它同样位于driversusbcorehcd.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函数


大致总结一下:


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


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


3、USB主机控制器以0地址从USB设备获得设备描述符的前8个字节,从这8个字节可以知道后面所有设备描述符总共的大小


4、知道了剩下设备描述符的大小,就可以将剩余的设备描述符都读出来


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


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


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


8、如果能匹配,调用usb_driver的probe函数


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


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

 

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


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

相关文章

    可直接从宏晶公司官www.stcmcu.com下载STC-ISP,这里面也有很多值得参考的东西,有事没事可以去看看,也可以百度,怎么安装USB转串口的驱动USB转串口常用芯片:CP2102......
    stc51单片机怎么学, 先掌握STC单片机程序烧录方法!;今天我在这里和大家探讨一下关于51单片机怎么学这个话题。 08年我在大学里学习单片机课程的时候,学习内容主要包括单片机的发展历史,单片机......
    :Ubuntu 16.04 LTS amd64 单片机:某宝¥149开发板,芯片STC90C51,板子自带USB转串口芯片CH340G。Ubuntu好像自带了这个的驱动,不用另装驱动了。 本来......
    嵌入式技能-51单片机仿真芯片调试; 程序是调出来的!!经常有小伙伴在知乎里面提问,说怎么实现花样流水灯,怎么实现按键控制LED灯,还有C语言的一些例子怎么......
    面向单片机编程 (一)- 单片机该怎么学;一、什么是单片机单片机(Single-Chip Microcomputer)是一种集成电路芯片,是采......
    方案使用CH341T与计算机进行通信。将计算机应用程序产生的下载数据,通过USB接口输出,通过CH341T将数据转化为串口协议数据,通过单片机AT89C2051将串口数据转化为51S系列单片机的下载协议数据,最后通过驱动......
    问题很难有一个比较准确的回答,因为每个人的基础不一样,学习的方法、学习的途径,每天的学习时间等都息息相关。 那学习单片机需要那些基础?怎样的学习方法?有那些学习途径?学习时间要怎么安排?。在这......
    用的是ISP串口烧录。 单片机开发板一般都会配套烧录器,其实就是一根USB转串口线。 烧录工具: 烧录线(USB转串口线): 烧录线需要安装驱动驱动一般是根据芯片来的,这个一般商家有提供,如果......
    你正在这么干,那我只能告诉你,赶紧停下来,别浪费时间了。 这样时间久了,你就容易产生学习疲惫,最终放弃持续学习。 第二:搭建单片机开发环境。 选择购买合适的学习开发板,安装单片机程序开发环境Keil,安装烧录工具的电脑驱动......
    接触器,所以,继电器驱动就是单片机与其他大功率负载接口。这个很重要,因为,一直让我们的电气工程师(我指的是那些没有学习过相应的电子技术的)感到迷惑不解的是:一个小小的芯片,怎么......

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>