usb驱动开发5之总线设备与接口

时间:2024-09-09 15:37:14

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函数,以后会专门一节来详细分析

usb驱动开发5之总线设备与接口usb驱动开发5之总线设备与接口
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起作用时起作用。