usb驱动开发5之总线设备与接口
Linux设备模型中的总线落实在USB子系统里就是usb_bus_type,它在usb_init的函数bus_register(&usb_bus_type)里注册。usb_bus_type定义如下:
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
.suspend = usb_suspend,
.resume = usb_resume,
};
显然,在这个结构体里重要的是usb_device_match函数,以后会专门一节来详细分析


- static int usb_device_match(struct device *dev, struct device_driver *drv)
- {
- /* devices and interfaces are handled separately */
- if (is_usb_device(dev)) {
- /* interface drivers never match devices */
- if (!is_usb_device_driver(drv))
- return 0;usb_device_driver
- /* TODO: Add real matching code */
- return 1;
- } else if (is_usb_interface(dev)) {
- struct usb_interface *intf;
- struct usb_driver *usb_drv;
- const struct usb_device_id *id;
- /* device drivers never match interfaces */
- if (is_usb_device_driver(drv))
- return 0;
- intf = to_usb_interface(dev);
- usb_drv = to_usb_driver(drv);
- id = usb_match_id(intf, usb_drv->id_table);
- if (id)
- return 1;
- id = usb_match_dynamic_id(intf, usb_drv);
- if (id)
- return 1;
- }
- return 0;
- }
熟悉Linux设备模型,上述函数的参数我们应该也能够理解。对应的就是总线两条链表里的设备和驱动。注释已经很明确告诉我们分两条路走,一条是设备,另一条是接口,你问我为什么?我说你去问问内核吧。(其实我也不知道)
/* devices and interfaces are handled separately */
接口是设备的接口。设备可以有多个接口,每个接口代表一个功能,每个接口对应着一个驱动。Linux设备模型的device落实在USB子系统,成了两个结构,一个是struct usb_device,一个是struct usb_interface。假设一个usb设备具有两种功能,一个是键盘功能,还有扬声器功能,那么就会存在两个接口,对应得要有两个驱动程序,一个是键盘驱动程序,一个是音频流驱动程序。兄弟喜欢把这样两个整合在一起的东西叫做一个设备,那好,让他们去叫吧,我们用interface来区分这两者行了吧。于是有了这里提到的那个数据结构,struct usb_interface。去看看usb_interface结构体吧。
struct usb_interface {
/* array of alternate settings for this interface,
* stored in no particular order */
struct usb_host_interface *altsetting;
struct usb_host_interface *cur_altsetting; /* the currently
* active alternate setting */
unsigned num_altsetting; /* number of alternate settings */
int minor; /* minor number this interface is
* bound to */
enum usb_interface_condition condition; /* state of binding */
unsigned is_active:1; /* the interface is not suspended */
unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */
struct device dev; /* interface specific device info */
struct device *usb_dev; /* pointer to the usb class's device, if any */
int pm_usage_cnt; /* usage counter for autosuspend */
};
这里有个altsetting成员,意思就是alternate setting,可选的设置,cur_altsetting表示当前正在使用的设置;num_altsetting表示这个接口具有可选设置的数量;minor,分配给接口的次设备号;,condition字段表示接口和驱动的绑定状态,前面说linux设备模型的时候说了,设备和驱动是相生相依的关系usb_interface_condition形象的描绘了这个过程中接口的个中心情,孤苦、期待、幸福、分开,人生又何尝不是如此?is_active:1、needs_remote_wakeup:1和pm_usage_cnt是关于挂起和唤醒的。协议里规定,所有的usb设备都必须支持挂起状态,就是说为了达到节电的目的,当设备在指定的时间内,3ms吧,如果没有发生总线传输,就要进入挂起状态。当它收到一个non-idle的信号时,就会被唤醒。 is_active表示接口是不是处于挂起状态。needs_remote_wakeup表示是否需要打开远程唤醒功能。远程唤醒允许挂起的设备给主机发信号,通知主机它将从挂起状态恢复,注意如果主机处于挂起状态,就会唤醒主机,不然主机仍然在睡着。协议里并没有要求USB设备一定要实现远程唤醒的功能,即使实现了,从主机这边儿也可以打开或关闭它。pm_usage_cnt,就是使用计数,当它为0时,接口允许autosuspend。什么叫autosuspend?有时合上笔记本后,它会自动进入休眠,这就叫autosuspend。但不是每次都是这样的,就像这里只有当pm_usage_cnt为0时才会允许接口autosuspend。
struct device dev和struct device *usb_dev,看到struct device没,它们就是linux设备模型里的device嵌在这儿的对象,我们的心中要时时有个模型。不过这么想当然是不正确的,两个里面只有dev才是模型里的device嵌在这儿的,usb_dev则不是。当接口使用USB_MAJOR作为主设备号时,usb_dev才会用到,你找遍整个内核,也只在usb_register_dev和usb_deregister_dev两个函数里能够看到它,usb_dev指向的就是usb_register_dev函数里创建的usb class device。
下面插讲一下关于设备号的概念。
linux下所有的硬件设备都是用文件来表示的,俗称设备文件,在/dev目录下边儿,为了显示自己并不是普通的文件,它们都会有一个主设备号和次设备号,比如
brw-r----- 1 root disk 8, 0 Sep 26 09:17 /dev/sda
brw-r----- 1 root disk 8, 1 Sep 26 09:17 /dev/sda1
crw-r----- 1 root tty 4, 1 Sep 26 09:17 /dev/tty1
这是在我的系统里使用ls –l命令查看的,当然只是显示了其中的几个来表示表示而已。很显然,任何一个有理智有感情的人都会认为USB设备是很常见的,linux理应为它预留了一个主设备号。看看include/linux/usb.h文件
7 #define USB_MAJOR 180
8 #define USB_DEVICE_MAJOR 189
你可以在内核里搜索它们都曾经出现什么地方,或者就跟随我回到usb_init函数。
之前和usbfs相关的就简单的飘过了,这里略微说的多一点。usbfs为咱们提供了在用户空间直接访问usb硬件设备的接口,但是世界上没有免费的午餐,它需要内核的大力支持,usbfs_driver就是用来完成这个光荣任务的。咱们可以去usb_init的usb_devio_init函数里看一看,它在drivers/usb/devio.c文件里定义
retval = register_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX,
"usb_device");
if (retval) {
err("unable to register minors for usb_device");
goto out;
}
register_chrdev_region函数获得了设备usb_device对应的设备编号,设备usb_device对应的驱动当然就是usbfs_driver,参数USB_DEVICE_DEV也在同一个文件里有定义
#define USB_DEVICE_DEV MKDEV(USB_DEVICE_MAJOR, 0)
终于再次见到了USB_DEVICE_MAJOR,也终于明白它是为了usbfs而生,为了让广大人民群众能够在用户空间直接和usb设备通信而生。因此,它并不是我们所要寻找的。
那么答案很明显了,USB_MAJOR就是咱们苦苦追寻的那个她,就是linux为USB设备预留的主设备号。事实上,前面usb_init函数的880行,usb_major_init函数已经使用USB_MAJOR注册了一个字符设备,名字就叫usb。我们可以在文件/proc/devices里看到它们。
localhost:/usr/src/linux/drivers/usb/core # cat /proc/devices
Character devices:
1 mem
2 pty
3 ttyp
4 /dev/vc/0
4 tty
4 ttyS
5 /dev/tty
5 /dev/console
5 /dev/ptmx
7 vcs
10 misc
13 input
14 sound
29 fb
116 alsa
128 ptm
136 pts
162 raw
180 usb
189 usb_device
/proc/devices文件里显示了所有当前系统里已经分配出去的主设备号,当然上面只是列出了字符设备。事实上,USB设备有很多种,并不是都会用到这个预留的主设备号。比如移动硬盘显示出来的主设备号是8,你的摄像头在linux显示的主设备号也绝对不会是这里的USB_MAJOR。坦白的说,咱们经常遇到的大多数usb设备都会与input、video等子系统关联,并不单单只是作为usb设备而存在。如果usb设备没有与其它任何子系统关联,就需要在对应驱动的probe函数里使用usb_register_dev函数来注册并获得主设备号USB_MAJOR,你可以在drivers/usb/misc目录下看到一些例子,drivers/usb/ usb-skeleton.c文件也属于这种。如果usb设备关联了其它子系统,则需要在对应驱动的probe函数里使用相应的注册函数,USB_MAJOR也就该干吗干吗去,用不着它了。比如,usb键盘关联了input子系统,驱动对应drivers/hid/usbhid目录下的usbkbd.c文件,在它的probe函数里可以看到使用了input_register_device来注册一个输入设备。准确的说,这里的USB设备应该说成USB接口,毕竟一个USB接口才对应着一个USB驱动。当USB接口关联有其它子系统,也就是说不使用USB_MAJOR作为主设备号时,struct usb_interface的字段minor可以简单的忽略。minor只在USB_MAJOR起作用时起作用。
usb驱动开发5之总线设备与接口的更多相关文章
- usb驱动开发4之总线设备驱动模型
在上文说usb_init函数,却给我们留下了很多岔路口.这次就来好好聊聊关于总线设备驱动模型.这节只讲理论,不讲其中的函数方法,关于函数方法使用参考其他资料. 总线.设备.驱动对应内核结构体分别为bu ...
- HarmonyOS USB DDK助你轻松实现USB驱动开发
HDF(Hardware Driver Foundation)驱动框架是HarmonyOS硬件生态开放的基础,为开发者提供了驱动加载.驱动服务管理和驱动消息机制等驱动能力,让开发者能精准且高效地开发驱 ...
- usb驱动开发9之设备描述符
前面分析了usb的四大描述符之端点描述符,接口描述符(每一个接口对应一个功能,与之配备相应驱动),配置描述符,最后分析设备如何包括这些描述符.首先记住,在usb的世界里,设备大于配置,配置大于接口,接 ...
- usb驱动开发16之设备生命线
回到struct usb_hcd,继续努力的往下看. kref,usb主机控制器的引用计数.struct usb_hcd也有自己专用的引用计数函数,看hcd.c文件. static void hcd_ ...
- 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驱动开发11之设备生命线
暂时先告别媒人,我们去分析各自的生命旅程,最后还会回到usb_device_match函数. 首先当你将usb设备连接在hub的某个端口上,hub检测到有设备连接了进来,它会为设备分配一个struct ...
- usb驱动开发1之学习准备
此系列是http://blog.csdn.net/fudan_abc/博文的整理,同时加入了自己的理解.很敬佩fudan_abc的文章,仔细学习和分析受益很多.注:fundan_abc所分析linux ...
随机推荐
- FIM 2010: Kerberos Authentication Setup
The goal of this article is to provide some background information regarding the Kerberos related co ...
- all things are difficult before they are easy
刚开始接触一项新知识时,总是感觉很难,只要你用心钻研下去,它会慢慢变简单的.
- (转)解决ScrollView嵌套ListView或者GridView导致只显示一行的方法
即动态获取ListView和GridView的高度 一.对于ListView ListView listview= new ListView(this); setListViewHeightBased ...
- Android直方图递增View
继上次分析实现Android自定义View之扇形图之后,自己又画了下面的这个递增直方图,本来是想做个静态的直方图就完了,结果想想静态的没啥趣味,于是就加了递增 1 从分析最终效果 界面上要展现的东西有 ...
- 使用docker搭建lnmp环境
Docker容器LNMP环境搭建 安装 制作镜像 启动并关联实例 安装 系统环境 硬件型号: ThinkPad T520 系统版本: ubuntu 14.04 CPU: i7 RAM: 8G 添加软件 ...
- 深入理解java虚拟机(5)---字节码执行引擎
字节码是什么东西? 以下是百度的解释: 字节码(Byte-code)是一种包含执行程序.由一序列 op 代码/数据对组成的二进制文件.字节码是一种中间码,它比机器码更抽象. 它经常被看作是包含一个执行 ...
- XP重装之后蓝屏
最近帮公司的电脑重装XP系统,发现重装之后电脑重启的时候蓝屏 解决方法:开机-->f2-->找到SATA Configration --> 选择ide 重启,就ok了
- 使用ExposedObject对Asp.net MVC中匿名类型的JsonResult做单元测试
返回JsonResult是MVC中的常见返回值类型,而且简单方便的方式是结合匿名类型一起使用. 比如: public ActionResult PreviewEmail() { …… return J ...
- 论近年来IT媒体的怪现象
之前在Svbtle上看过一篇文章干掉这帮搞IT新闻的!,作者因为CNET曲解原意,称Instagram要单方面售卖用户自己照片的乌龙事件,致使内心遭受严重刺激,怒吼出「科技媒体记者都应该被枪毙」的言论 ...
- 《SQL Server企业级平台管理实践》读书笔记——SQL Server数据库文件分配方式
1.文件分配方式以及文件空间检查方法 最常用的检查数据文件和表大小的命令就是:sp_spaceused 此命令有三个缺陷:1.无法直观的看出每个数据文件和日志文件的使用情况.2.这个存储过程依赖SQL ...