usb驱动开发16之设备生命线
回到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之设备生命线的更多相关文章
- usb驱动开发17之设备生命线
拜会完了山头的几位大哥,还记得我们从哪里来要到哪里去吗?时刻不能忘记自身的使命啊.我们是从usb_submit_urb()最后的那个遗留问题usb_hcd_submit_urb()函数一路走来,现在就 ...
- usb驱动开发15之设备生命线
总算是进入了HCD的片儿区,既然来到一个片区,怎么都要去拜会一下山头几个大哥吧.,先回忆一些我们怎么到这里的?给你列举一个调用函数过程usb_control_msg->usb_internal_ ...
- usb驱动开发14之设备生命线
直接看代码吧. /*-------------------------------------------------------------------*/ /** * usb_submit_urb ...
- usb驱动开发12之设备生命线
函数usb_control_msg完成一些初始化后调用了usb_internal_control_msg之后就free urb.剩下的活,全部留给usb_internal_control_msg去做了 ...
- usb驱动开发18之设备生命线
现在已经使用GET_DESCRIPTOR请求取到了包含一个配置里所有相关描述符内容的一堆数据,这些数据是raw的,即原始的,所有数据不管是配置描述符.接口描述符还是端点描述符都挤在一起,所以得想办法将 ...
- usb驱动开发11之设备生命线
暂时先告别媒人,我们去分析各自的生命旅程,最后还会回到usb_device_match函数. 首先当你将usb设备连接在hub的某个端口上,hub检测到有设备连接了进来,它会为设备分配一个struct ...
- usb驱动开发13之设备生命线
上一节勉勉强强把struct urb这个中心给说完,接着看那三个基本点. 第一个基本点,usb_alloc_urb函数,创建urb的专用函数,为一个urb申请内存并做初始化,在drviers/usb/ ...
- HarmonyOS USB DDK助你轻松实现USB驱动开发
HDF(Hardware Driver Foundation)驱动框架是HarmonyOS硬件生态开放的基础,为开发者提供了驱动加载.驱动服务管理和驱动消息机制等驱动能力,让开发者能精准且高效地开发驱 ...
- usb驱动开发22之驱动生命线
我们总是很喜欢高潮,不是吗?那就好好对待她哦.我们来看一下linux中的高潮部分设备是怎么从Address进入Configured的. usb_set_configuration函数的代码就不贴了,可 ...
随机推荐
- iOS之 开发常用到的宏定义
不久前做过一个小项目种用到了就记录下来方便自己以后使用,一个非常实用的宏定义来打印函数名称等 #ifdef DEBUG #define DebugLog(fmt, ...) NSLog((@" ...
- iOS之 PJSIP蓝牙外设音频支持
如题,蓝牙设备作为音频输出,在app中如果包含VoIP那么就要设定当连接蓝牙设备时候是否选择或者支持蓝牙输出 最近在处理一个工单就是公司的voip-app与硬件话机底座联调(蓝牙2.0)坑爹的如果是4 ...
- Java中的基本数据类型
什么是基本数据类型 就是我们在编程的时候经常需要用到的数据类型,如整型,浮点型等,把这些数据类型专门拿出来特殊对待,并想象成所谓的“基本类型”. Java中有哪些基本数据类型
- 分组函数 ----group by 使用总结
总结:1,在where子句中不能用分组聚合函数. 2,如果没有group by 子句,select 不能同时出现字段与分组的聚合函数. 3,在有 group by 的子句的查询 ...
- Hibernate 缓存介绍
Hibernate中提供了两级缓存,一级缓存是Session级别的缓存,它属于事务范围的缓存,该级缓存由hibernate管理,应用程序无需干预:二级缓存是SessionFactory级别的缓存,该级 ...
- U-BLOX GPS 模块及GPRMC指令解析
受朋友所托,调试一款GPS模块,该模块是UBLOX的NEO-6M GPS模组.想到用这款GPS的人较多,自己日后也有可能在用到这个模块,就写下这份笔记. 1. 介绍 基本信息如下: 1, 模块采用U- ...
- RabbitMQ入门教程——.NET客户端使用
众所周知RabbitMQ使用的是AMQP协议.我们知道AMQP是一种网络协议,能够支持符合要求的客户端应用和消息中间件代理之间进行通信. 其中消息代理扮演的角色就是从生产者那儿接受消息,并根据既定的路 ...
- MySQL 调优基础(五) Linux网络
1. TCP/IP模型 我们一般知道OSI的网络参考模型是分为7层:“应表会传网数物”——应用层,表示层,会话层,传输层,网络层,数据链路层,物理层.而实际的Linux网络层协议是参照了OSI标准,但 ...
- su认证失败&文件夹里打开终端的方法&atom安装
很久没用笔记本上的ubuntu,用不顺手,比在公司调教了半年多的电脑差远了.一步一步来.先解决最不顺手的三件事 1.su认证失败. 新安装的ubuntu系统是无法切换到root账户的,得做一番修改 s ...
- stm32 加入 USE_STDPERIPH_DRIVER、STM32F10X_HD的原因
初学STM32,在RealView MDK 环境中使用STM32固件库建立工程时,初学者可能会遇到编译不通过的问题.出现如下警告或错误提示: warning: #223-D: function &qu ...