回到struct usb_hcd,继续努力的往下看。

kref,usb主机控制器的引用计数。struct usb_hcd也有自己专用的引用计数函数,看hcd.c文件。

static void hcd_release (struct kref *kref)
{
struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref); kfree(hcd);
} struct usb_hcd *usb_get_hcd (struct usb_hcd *hcd)
{
if (hcd)
kref_get (&hcd->kref);
return hcd;
}
EXPORT_SYMBOL (usb_get_hcd); void usb_put_hcd (struct usb_hcd *hcd)
{
if (hcd)
kref_put (&hcd->kref, hcd_release);
}
EXPORT_SYMBOL (usb_put_hcd);

和struct urb差不多,如果不明白就回去看看聊struct urb吧。

product_desc,主机控制器的产品描述字符串,对于UHCI,它为“UHCI Host Controller”,对于EHCI,它为“EHCI Host Controller”。

irq_descr[24],这里边儿保存的是“ehci-hcd:usb1”之类的字符串,也就是驱动名加上总线编号。

CONFIG_PM,电源管理相关,飘过。

driver,每个主机控制器驱动都有一个struct hc_driver结构体。有兴趣的去看看它在hcd.h里的定义。和usb_driver、pci_driver一样,所有的xxx_driver都有一堆函数指针,这里只说下函数指针之外的东西,也就是开头儿的那三个成员。description直白点说就是驱动的大名,比如对于UHCI,它是“uhci_hcd”,对于EHCI,它就是“ehci_hcd”。product_desc和struct usb_hcd里的那个是一个样儿。hcd_priv_size还是有点儿意思的,前面喝那杯茶的时候提到过,每个主机控制器驱动都会有一个私有结构体,藏在struct usb_hcd最后的那个变长数组里,这个变也是相对的,在创建usb_hcd的时候也得知道它能变多长,不然谁知道要申请多少内存啊,这个长度就hcd_priv_size。

flags,属于HCD的一些标志,可用值就在接下来的HCD_FLAG_HW_ACCESSIBLE和HCD_FLAG_SAW_IRQ两行,至于什么作用,用到时候再聊。

rh_timer,status_urb,rh_registered ,poll_pending,poll_rh,poll_pending这几行都是专为root hub服务的。一个host controller对应一个root hub,即使某些嵌入式系统里,硬件上host controller没有集成root hub,软件上也需要虚拟一个出来,也就是所谓的virtual root hub。它位置是特殊的,但需要提供的功能和其它hub是没有什么差别的,仅仅是在和host controller的软硬件接口上有一些特别的规定。root hub再怎么特别也始终是一个hub,是一个usb设备,也不能脱离usb这个大家庭,也要向组织注册,也要有自己的设备生命线。关于root hub的生命线我们放到本节最后说,先看其他成员。

回到struct usb_hcd,看wireless,无线usb相关。

继续看irq、regs、rsrc_start、rsrc_len这几行,都是与PCI有关的。

power_budget,能够提供的电流。

*pool [HCD_BUFFER_POOLS],几个dma池。因为HCD_BUFFER_POOLS定义为4,所以这里就表示每个主机控制器可以有4个dma池。

我们知道主机控制器是可以进行DMA传输的,想想前面的struct urb,那里有两个成员transfer_dma和setup_dma,当时只是说你可以使用usb_buffer_alloc分配好dma缓冲区给它们,然后再告诉HCD你的urb已经有了,HCD就可以不用再进行复杂的DMA映射,并没有提到你这个获取的dma缓冲区是从哪里来的,实际上就是从这里所说的dma池子里来的。

一般来说DMA映射获得的都是以页为单位的内存,urb需要不了这么大,如果需要比较小的DMA缓冲区,就离不开DMA池了。还是看看主机控制器的这几个池子是怎么创建的,在buffer.c文件里主要有四个函数,hcd_buffer_create(),hcd_buffer_destroy(),hcd_buffer_alloc(),

hcd_buffer_free();需要说明两点,第一个是即使你的主机控制器不支持DMA,这几个函数也是可以用的,只不过创建的不是DMA池子,取的也不是DMA缓冲区,此时,DMA池子不存在,hcd_buffer_alloc获取的只是适用kmalloc申请的普通内存,当然相应的,你必须在它没有利用价值的时候使用hcd_buffer_free将它释放掉。第二个问题是size的问题,看到它们里面都有个pool_max吧,这是个同一个文件里定义的数组。这个数组里定义的就是四个池子中每个池子里保存的DMA缓冲区的size。注意这里虽说只定义了四种size,但是并不说明你使用hcd_buffer_alloc获取DMA缓冲区的时候不能指定更大的size,如果都满足不了要求,那就会使用dma_alloc_coherent为她建立一个新的DMA映射。不可能每次分配都会完全恰好和上面定义的四种size一致,这个问题不用担心,实际上使用这个size获取DMA缓冲区的时候,池子会选择一个略大一些的回馈过去。

回到struct usb_hcd中来, state,主机控制器的状态,紧挨着它的下面那些行就是相关的可用值和宏定义。

hcd_priv[0],主机控制器的私有数据被存储在这个结构体的末尾。

别忘了,我们还有一个重要的任务就是分析root hub的生命线,涉及usb_hcd结构体的主要成员有rh_timer,status_urb,rh_registered ,poll_pending,poll_rh,poll_pending这几行。还是要先声明一下,root hub的生命线只是咱们的设备生命线里的一个岔路口,不会沿着它去仔细的走,只会尽量的使用快镜头去展示一下。如果有哪里不明白,等走完了设备声明主线,你再回过头来看看,很多事情都是在我们蓦然回首的时候才明白的。

基于root hub和host controller这种极为特殊的关系,UHCI、EHCI等主机控制器驱动程序在自己的初始化代码里都有大量的篇幅花在root hub上面。不管是UHCI,还是EHCI,一般PCI都应该有个struct pci_driver结构体,都应该有一个属于自己的probe。在这个probe里,也都会调用usb_create_hcd()来创建一个属于自己的usb_hcd,也都会调用usb_add_hcd()将这个刚刚创建的usb_hcd注册到usb组织里。下面贴usb_add_hcd函数里的部分代码,主要是与root hub相关的代码。

if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) {

dev_err(hcd->self.controller, "unable to allocate root hub\n");

retval = -ENOMEM;

goto err_allocate_root_hub;

}

rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH :

USB_SPEED_FULL;

hcd->self.root_hub = rhdev;

在usb_add_hcd函数里,调用usb_alloc_dev()来为root hub准备一个struct usb_device结构体,这时root hub的设备类型被赋值为usb_device_type,所属总线类型赋值为usb_bus_type。然后根据各个host controller的实际情况,设定它的速度。struct usb_device结构体准备妥当之后,就要赋给struct usb_bus的root_hub元素。这些都只不过是准备工作,重头戏都在usb_add_hcd()最后调用的register_root_hub()里面。register_root_hub()非常肯定的将root hub的设备号devnum设置为1,于是就少了选择地址设置地址的过程,root hub直接跳跃式发展进入了Address状态,在这个状态我们应该知道此时可以很方便的获得root hub的设备描述符,可以调用usb_new_device将root hub送给无所不能的设备模型,去寻找它命中的驱动。

retval = usb_new_device (usb_dev);

if (retval) {

dev_err (parent_dev, "can't register root hub for %s, %d\n",

usb_dev->dev.bus_id, retval);

}

如果这些都一切顺利的话,register_root_hub()在最后就会将struct usb_hcd的rh_registered设置为1,表示root hub已经找到组织并加入到革命队伍里了。

Linux设备模型根据root hub所属的总线类型usb_bus_type将它添加到usb总线的那条有名的设备链表里,然后会轮询usb总线的另外一条有名的驱动链表,针对每个找到的驱动去调用usb总线的match函数,也就是咱们以前一再遇到以后不断遇到的usb_device_match(),为root hub牵线搭桥寻找另一个匹配的半圆。要注意,root hub的设备类型是usb_device_type,所以在usb_device_match()里走的设备那条路,匹配成功的是那个对所有usb_device_type类型的设备都来者不拒的花心大萝卜,usb世界里唯一的那个usb设备驱动(不是usb接口驱动)struct device_driver结构体对象usb_generic_driver。所以接下来要调用的就是usb_generic_driver的probe函数generic_probe(),去配置root hub,然后root hub就大步迈进了Configured状态。因为root hub只有一个配置,所以generic_probe()配置root hub时并没有多少选择的烦恼,如果有所疑问,可以look下hcd.c的开头就已经设定好的root hub设备描述符

/* usb 2.0 root hub device descriptor */

static const u8 usb2_rh_dev_descriptor [18] = {

0x12, /* __u8 bLength; */

0x01, /* __u8 bDescriptorType; Device */

0x00, 0x02, /* __le16 bcdUSB; v2.0 */

0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */

0x00, /* __u8 bDeviceSubClass; */

0x01, /* __u8 bDeviceProtocol; [ usb 2.0 single TT ]*/

0x40, /* __u8 bMaxPacketSize0; 64 Bytes */

0x00, 0x00, /* __le16 idVendor; */

0x00, 0x00, /* __le16 idProduct; */

KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */

0x03, /* __u8 iManufacturer; */

0x02, /* __u8 iProduct; */

0x01, /* __u8 iSerialNumber; */

0x01 /* __u8 bNumConfigurations; */

};

这张表是const的,也就是说在整个usb的世界里它都是只能去读不能去修改的,要用严谨科学的态度去对待root hub。明眼人一眼就能看到躲在它最后的那个0x01,这就是root hub支持的配置数量。在它下边儿还有针对usb 1.1的root hub设备描述符,大同小异就不贴了。

既然进入了Configured状态,就可以无拘无束的使用root hub提供的所有功能了,不过,对于咱们使用usb的劳苦大众来说,实际在起作用的还是设备里的接口,所以generic_probe()接下来就会根据root hub使用的配置,为它所有的接口准备struct usb_interface结构体对象,这时接口所属的总线类型仍然为usb_bus_type,设备类型就不一样了,为usb_if_device_type,早先说过的那句总线有总线的类型,设备有设备的类型到这里就应该再加上一句,接口有接口的类型。usb_if_device_type在message.c里定义

为root hub的接口准备struct usb_interface结构体也没费太大功夫,因为spec里规定了hub上除了端点0,就只有一个中断的IN端点,并不用准备太多的东西。root hub的配置描述符也已经在hcd.c的开头儿指定好了,确实只有一个接口,一个设置,一个端点,去look一下

/*-------------------------------------------------------------------------*/

/* Configuration descriptors for our root hubs */

static const u8 fs_rh_config_descriptor [] = {

/* one configuration */

0x09, /* __u8 bLength; */

0x02, /* __u8 bDescriptorType; Configuration */

0x19, 0x00, /* __le16 wTotalLength; */

0x01, /* __u8 bNumInterfaces; (1) */

0x01, /* __u8 bConfigurationValue; */

0x00, /* __u8 iConfiguration; */

0xc0, /* __u8 bmAttributes;

Bit 7: must be set,

6: Self-powered,

5: Remote wakeup,

4..0: resvd */

0x00, /* __u8 MaxPower; */

/* USB 1.1:

* USB 2.0, single TT organization (mandatory):

* one interface, protocol 0

*

* USB 2.0, multiple TT organization (optional):

* two interfaces, protocols 1 (like single TT)

* and 2 (multiple TT mode) ... config is

* sometimes settable

* NOT IMPLEMENTED

*/

/* one interface */

0x09, /* __u8 if_bLength; */

0x04, /* __u8 if_bDescriptorType; Interface */

0x00, /* __u8 if_bInterfaceNumber; */

0x00, /* __u8 if_bAlternateSetting; */

0x01, /* __u8 if_bNumEndpoints; */

0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */

0x00, /* __u8 if_bInterfaceSubClass; */

0x00, /* __u8 if_bInterfaceProtocol; [usb1.1 or single tt] */

0x00, /* __u8 if_iInterface; */

/* one endpoint (status change endpoint) */

0x07, /* __u8 ep_bLength; */

0x05, /* __u8 ep_bDescriptorType; Endpoint */

0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */

0x03, /* __u8 ep_bmAttributes; Interrupt */

0x02, 0x00, /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */

0xff /* __u8 ep_bInterval; (255ms -- usb 2.0 spec) */

};

bNumInterfaces,if_bAlternateSetting,if_bNumEndpoints,分别指定了接口数量,使用的设置,端点的数目。也就是说,如果加上端点0,root hub只有两个端点,而另外一个端点为IN端点,端点号为1,中断类型,每次可以处理的最大字节数为2,期望host controller访问自己的时间间隔为256ms。这里为什么说这个中断端点的wMaxPacketSize为2,wMaxPacketSize行不是明确写着0x02,0x00么?协议规定usb设备的各种描述符都是按照little-endian方式的字节序存储的,先是低字节然后是高字节。

为root hub的那个唯一接口准备好struct usb_interface结构体之后应该怎么做?一个接口一个驱动,接下来显然是应该将它送给设备模型去寻找它命中注定的驱动了,所以到了usb_device_match()。不过你说这个时候设备和接口两条路它应该走哪条?它的类型已经设置成usb_if_device_type了,设备那条路把门儿的根本就不会让它进,所以它必须得去走接口那条路。有关在接口这条路上它怎么去寻找另一半儿,日后再说。

回头再看root hub的设备描述符,它的bDeviceClass 被指定为0x09,在include/linux/usb/ch9.h里,它对应的是USB_CLASS_HUB,再看看root hub配置描述符的bInterfaceClass也被指定成0x09,也就是说同样为USB_CLASS_HUB。你再去hub驱动对应的hub.c文件里看看那里的hub_id_table,是不是应该明白点什么了? root hub里的那个接口所对应的驱动就是hub驱动,虽然它很特殊,可它也是个hub啊。root hub一边和host controller相生相依,一边和hub驱动眉来眼去花前月下,多么巨大的讽刺。

接下来要做的就是调用hub驱动里的hub_probe()。不管是root hub还是一般的hub,走到这一步都是必然的,不同的是对于root hub在host controller初始化的过程中就会去调用hub_probe()。hub_probe()通过hub_configure()创建并初始化了一个中断的urb,然后在hub_activate()里调用usb_submit_urb()将它提交给core,接着就又回到了HCD。

每个hub都需要进行中断传输来获得hub的状态变化,不然hub驱动里那个著名的hub_events就活不下去了,你连在hub上的usb设备系统也就察觉不到了,你也就不能用usb摄像头来泡mm了。我们都应该知道中断传输不会提交一次就完事儿了,需要在你的结束处理函数里提交再提交。中断传输都是有个间隔时间的,这个时间不由你决定,也不由我决定,咱们只能期待,决定权在host controller那里。host controller就决定了,对于root hub要每隔250ms去访问一次,你再看上面root hub的描述符,里面显示它期待的时间为0xff,也就是256ms,250与256也差不了多少,root hub也应该满意了。这个固定的访问频率host controller是怎么实现的?就是通过struct usb_hcd里的那个定时器rh_timer,定时器就是专门干这种事儿的,root hub每提交一次urb,在HCD里都会对它初始化一次,指定250ms之后去获得root hub的状态,然后就让urb返回给root hub,于是root hub再提交,再250ms后返回。

但是这种对root hub的轮询机制是早先版本里的,现在就不一样了。struct usb_hcd里出现了uses_new_polling,看名字就知道是一种新机制出现了。在新的机制里,root hub仍然是要提交一个中断urb的,不同的是这时它可以不用再请定时器来帮忙,在有设备插入时,host controller会在自己的中断处理函数里调用一个名叫usb_hcd_poll_rh_status的函数去获得root hub的状态并将urb的所有权归还给hub驱动。

那是不是就可以不需要定时器rh_timer了?事情没这么简单,原来的机制还必须得保留着,给不同的host controller选择使用。为了让新旧机制和谐的运作,usb_hcd_poll_rh_status多了一个职务,就是作为rh_timer的定时器函数,而且struct usb_hcd里除了uses_new_polling之外就又多了poll_rh等几个元素。如果uses_new_polling没有被设置,则一定会采用旧的轮询机制,如果uses_new_polling已经被设置了,但同时又设置了poll_rh,也是要采用旧机制,否则采用的就是新机制。

至于status_urb,其实就是root hub提交的那个urb。

root hub的生命线就还是说到这里吧,那么我们的usb_hcd结构体也分析完了。

usb驱动开发16之设备生命线的更多相关文章

  1. usb驱动开发17之设备生命线

    拜会完了山头的几位大哥,还记得我们从哪里来要到哪里去吗?时刻不能忘记自身的使命啊.我们是从usb_submit_urb()最后的那个遗留问题usb_hcd_submit_urb()函数一路走来,现在就 ...

  2. usb驱动开发15之设备生命线

    总算是进入了HCD的片儿区,既然来到一个片区,怎么都要去拜会一下山头几个大哥吧.,先回忆一些我们怎么到这里的?给你列举一个调用函数过程usb_control_msg->usb_internal_ ...

  3. usb驱动开发14之设备生命线

    直接看代码吧. /*-------------------------------------------------------------------*/ /** * usb_submit_urb ...

  4. usb驱动开发12之设备生命线

    函数usb_control_msg完成一些初始化后调用了usb_internal_control_msg之后就free urb.剩下的活,全部留给usb_internal_control_msg去做了 ...

  5. usb驱动开发18之设备生命线

    现在已经使用GET_DESCRIPTOR请求取到了包含一个配置里所有相关描述符内容的一堆数据,这些数据是raw的,即原始的,所有数据不管是配置描述符.接口描述符还是端点描述符都挤在一起,所以得想办法将 ...

  6. usb驱动开发11之设备生命线

    暂时先告别媒人,我们去分析各自的生命旅程,最后还会回到usb_device_match函数. 首先当你将usb设备连接在hub的某个端口上,hub检测到有设备连接了进来,它会为设备分配一个struct ...

  7. usb驱动开发13之设备生命线

    上一节勉勉强强把struct urb这个中心给说完,接着看那三个基本点. 第一个基本点,usb_alloc_urb函数,创建urb的专用函数,为一个urb申请内存并做初始化,在drviers/usb/ ...

  8. HarmonyOS USB DDK助你轻松实现USB驱动开发

    HDF(Hardware Driver Foundation)驱动框架是HarmonyOS硬件生态开放的基础,为开发者提供了驱动加载.驱动服务管理和驱动消息机制等驱动能力,让开发者能精准且高效地开发驱 ...

  9. usb驱动开发22之驱动生命线

    我们总是很喜欢高潮,不是吗?那就好好对待她哦.我们来看一下linux中的高潮部分设备是怎么从Address进入Configured的. usb_set_configuration函数的代码就不贴了,可 ...

随机推荐

  1. 【读书笔记】iOS网络-测试与操纵网络流量

    一,观测网络流量. 观测网络流量的行为叫做嗅探或数据包分析. 1,嗅探硬件. 从iOS模拟器捕获数据包不需要做特别的硬件或网络配置.如果需要捕获这些数据包,那么可以使用嗅探软件来监听回送设备或是用于连 ...

  2. 【读书笔记】iOS网络-保护网络传输

    一,验证服务器通信. 二,HTTP认证. 手机银行应用有两种认证模式:标准验证与快速验证.标准验证只是提示用户输入用户名与密码,而快速验证则让用户注册设备,然后使用PIN进行验证,每次验证时无需用户名 ...

  3. 深入浅出Block的方方面面

    内容大纲: 1.Blocks概要 2.Blocks模式 3.Block实质(面试常问重点) 1.Blocks概要 什么是Blocks:Blocks是C语言的扩充的功能,可以用一句话来表示Blocks的 ...

  4. apache 日志轮询 linux cronolog

    Linux下运行的Web服务器Apache,默认日志文件是不分割的,一个整文件既不易于管理,也不易于分析统计.安装cronolog后,可以将日志文件按时间分割,易于管理和分析. cronolog安装配 ...

  5. TSQL--临时表和表变量

    1. 临时表适用数据量较大的情况,因为临时表可以建立索引 2. 表变量适用于数据较小的情况,表变量只能在定义时创建约束(PRIMARY KEY/UNIQUE)从而间接建立索引 3. 临时表是事务性的, ...

  6. mysql-5 数据检索(3)

    计算字段 如果想在一个字段中既显示公司的名称,又显示公司的地址,但是这两个信息一般包含在不同的表列中 城市.州和邮政编码存储在不同的列中,但是邮件标签打印程序却需要把它们作为一个恰当格式的字段检索出来 ...

  7. 《CLR via C#》读书笔记--基元类型、引用类型和值类型

    编程语言的基元类型 编译器直接支持的数据类型称为基元类型.基元类型直接映射到Framework类库中存在的类型.例如:C#中的int直接映射到System.Int32类型.下表给出了C#基元类型与对应 ...

  8. mongodb的备份

    转载请附原文链接:http://www.cnblogs.com/wingsless/p/5672057.html mongodb现在为止还是没有像XtraBackup这样好用的备份工具,因此一般来说会 ...

  9. 金士顿U盘,群联PS2251-60主控,量产CDROM教程

    量产前准备: 1. 插上U盘,(台式机的话插机箱后面) 2. 一台电脑,最好不要装杀毒软件(特别是360) 3. ISO镜像文件 4. 下载MPALL v3.29.0B.zip 请先耐心看完教程: 1 ...

  10. JAVA 基本运算符(摘)

    (搞自:Java经典入门教程) http://wenku.baidu.com/link?url=IoWI58cD5vzeHN-NL4pN7Gren-RfzydrhjDlETAByC9L-9ANinyL ...