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之总线设备与接口的更多相关文章

  1. usb驱动开发4之总线设备驱动模型

    在上文说usb_init函数,却给我们留下了很多岔路口.这次就来好好聊聊关于总线设备驱动模型.这节只讲理论,不讲其中的函数方法,关于函数方法使用参考其他资料. 总线.设备.驱动对应内核结构体分别为bu ...

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

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

  3. usb驱动开发9之设备描述符

    前面分析了usb的四大描述符之端点描述符,接口描述符(每一个接口对应一个功能,与之配备相应驱动),配置描述符,最后分析设备如何包括这些描述符.首先记住,在usb的世界里,设备大于配置,配置大于接口,接 ...

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

    回到struct usb_hcd,继续努力的往下看. kref,usb主机控制器的引用计数.struct usb_hcd也有自己专用的引用计数函数,看hcd.c文件. static void hcd_ ...

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

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

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

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

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

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

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

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

  9. usb驱动开发1之学习准备

    此系列是http://blog.csdn.net/fudan_abc/博文的整理,同时加入了自己的理解.很敬佩fudan_abc的文章,仔细学习和分析受益很多.注:fundan_abc所分析linux ...

随机推荐

  1. 复杂领域的Cynefin模型和Stacey模型

    最近好奇“复杂系统”,收集了点资料,本文关于Cynefin模型和Stacey模型.图文转自互联网后稍做修改. Cynefin模型提供一个从因果关系复杂情度来分析当前情况而作决定的框架,提出有五个领域: ...

  2. 实践GTD三周年后的体会

    从2011年7月开始实践GTD到现在已经3年多了,按照年初设定的目标,应该写点GTD的体会,但提起笔来的时候却发现并没有什么太新的体会,大部分体会都已经在以前的文章中写到了,甚至有些流程已经简化了,看 ...

  3. OC中NSDictionary和NSSet简单操作

    /** *  字典 存放键值对类型的数据 存放数据是无序的 */ // 字典在控制台输出是用{}包括起来的 // NSDictionary 不可变字典 // 1.创建对象 // 初始化方法 NSDic ...

  4. Enterprise Library +Caliburn.Micro+WPF CM框架下使用企业库验证,验证某一个属性,整个页面的文本框都变红的原因

    我用的是CM这个框架做的WPF,在用企业库的验证的时候,我用标签的方式给一个属性加了不能为空的验证,但整个页面的所有控件的外面框都变红了.原因是CM框架的绑定方式是直接X:Name="你的属 ...

  5. CGContextRef使用简要教程

    CGContextRef使用简要教程 Graphics Context是图形上下文,也可以理解为一块画布,我们可以在上面进行绘画操作,绘制完成后,将画布放到我们的view中显示即可,view看作是一个 ...

  6. iOS-代理反向传值<转>

    在上篇博客 iOS代理协议 中,侧重解析了委托代理协议的概念等,本文将侧重于它们在开发中的应用. 假如我们有一个需求如下:界面A上面有一个button.一个label.从界面A跳转到界面B,在界面B的 ...

  7. CentOS6.5+nginx+tomcat负载均衡集群

    思路: 1.安装jdk 2.安装tomcat 3.安装nginx依赖库 4.安装nginx 5.nginx+2个tomcat集群 一:网络拓扑

  8. Erlang 虚拟机 BEAM 指令集之内存管理相关的指令

    翻看 BEAM 虚拟机指令集的时候(在编译器源码目录下:lib/compiler/src/genop.tab),会发现有一些和内存分配/解除分配相关的指令,如下所示: allocate StackNe ...

  9. 通过XmlSerializer 实现XML的序列化与反序列化

    通过XmlSerializer 我们可以十分简单的将Model与XML进行转换 官文在点这里 帮助类 using System; using System.Text; using System.Xml ...

  10. oracle中的数值函数整理

    主要分为三块介绍(单值函数.聚合函数.列表函数) 一.单值函数(比较简单,看一遍基本也就理解记住了) 1.基本加减乘车没有什么可说的,只需要注意一点,任何值与null一起运算 ,结果都为null,因为 ...