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 ...
随机推荐
- Java集合 之 Queue集合
什么是Queue集合? 答:Queue用于模拟队列这种数据结构.队列通常是指“先进先出(FIFO)”的容器.队列的头部保存在队列中存放时间最长的元素,尾部保存存放时间最短的元素.新元素插入到队列的尾部 ...
- 使用Java正则表达式提取字符串中的数字一例
直接上代码: String reg = "\\D+(\\d+)$"; //提取字符串末尾的数字:封妖塔守卫71 == >> 71 String s = monster. ...
- javascript/jquery 常见功能实现(持续更新...)
1. input 只能输入整数数字和字母 $(document).on('keyup','#no',function(){ var val = $.trim($(this).val()); if(va ...
- jQuery Form 表单提交插件-----ajaxForm() 的应用
Form Plugin API 里提供了很多有用的方法可以让你轻松的处理表单里的数据和表单的提交过程. 测试环境:部署到Tomcat中的web项目. 一.ajaxForm() 介绍 ajaxForm ...
- 在jsp中默认写上的一段java代码表示basePath 的路径的具体的意思是什么?
<% String path = request.getContextPath(); String basePath = request.getScheme() + "://" ...
- 04_最长上升子序列问题(LIS)
来源:刘汝佳<算法竞赛入门经典--训练指南> P60 问题6: 问题描述:给定n个整数a1,a2,...,an,按从左到右的顺序选出尽量多的整数,组成一个上升子序列(子序列可以理解为:删除 ...
- maven添加远程私服
文件放在maven下和..m2下面 <?xml version="1.0" encoding="UTF-8"?> <!-- License ...
- TCP/IP详解--TCP首部选项中时间戳选项
一.简介 TCP时间戳选项会在TCP包头增加12个字节,以一种比重发超时更精确的方法来启用对RTT 的计算. 二.作用 ) TCP时间戳位于TCP选项中,kind=:lenth=:data由tim ...
- KEIL与ADS1.2共存
出现的问题: 原来电脑已经安装了ADS1.2.现在安装keil5编译一个32位新唐单片机程序时,出现了如下错误: Error: L6411E: No compatible library exists ...
- java 基础
一 4类8种基本数据类型 逻辑型 - boolean 文本型 - char 整数型 - byte short int long 浮点数型 - float double ☆java各整数类型 ...