Linux那些事儿 之 戏说USB(12)接口是设备的接口(一)

来源:互联网 发布:单片机计数器实验报告 编辑:程序博客网 时间:2024/04/18 13:00

前面的前面已经说了,接口是设备的接口。设备可以有多个接口,每个接口代表一个功能,每个接口对应着一个驱动。Linux设备模型的device落实在USB子系统,成了两个结构,一个是struct usb_device,一个是struct usb_interface。听听复旦人甲怎么说,一个usb设备,两种功能,一个键盘,上面带一个扬声器,两个接口,那这样肯定得要两个驱动程序,一个是键盘驱动程序,一个是音频流驱动程序。道上的兄弟喜欢把这样两个整合在一起的东西叫做一个设备,那好,让他们去叫吧,我们用interface来区分这两者行了吧。于是有了这里提到的那个数据结构,struct usb_interface。

include/linux/usb.h

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 *//* If there is an interface association descriptor then it will list * the associated interfaces */struct usb_interface_assoc_descriptor *intf_assoc;int minor;/* minor number this interface is * bound to */enum usb_interface_condition condition;/* state of binding */unsigned sysfs_files_created:1;/* the sysfs attributes exist */unsigned ep_devs_created:1;/* endpoint "devices" exist */unsigned unregistering:1;/* unregistration is in progress */unsigned needs_remote_wakeup:1;/* driver requires remote wakeup */unsigned needs_altsetting0:1;/* switch to altsetting 0 is pending */unsigned needs_binding:1;/* needs delayed unbind/rebind */unsigned reset_running:1;unsigned resetting_device:1;/* true: bandwidth alloc after reset */struct device dev;/* interface specific device info */struct device *usb_dev;atomic_t pm_usage_cnt;/* usage counter for autosuspend */struct work_struct reset_ws;/* for resets in atomic context */};
这里有个altsetting成员,只用耗费一个脑细胞就可以明白它的意思就是alternate setting,可选的设置。那么再耗费一个脑细胞就可以知道cur_altsetting表示当前正在使用的设置,num_altsetting表示这个接口具有可选设置的数量。前面提过USB设备的配置,那这里的设置是嘛意思?这可不是耗费一两个脑细胞就可以明白的了,不过不要怕,虽然说脑细胞是不可再生资源,但并不是多宝贵的东东,不然咋黄金煤炭的股票都在疯长,就不见人的工资长。

咱们是难得糊涂几千年了,不会去区分配置还有设置有什么区别,起码我平时即使是再无聊也不会去想这个,但老外不一样,他们挺较真儿这个,还分了两个词,配置是configuration,设置是setting。先说配置,一个手机可以有多种配置,比如可以摄像,可以接在电脑里当做一个U盘,那么这两种情况就属于不同的配置,在手机里面有相应的选择菜单,你选择了哪种它就按哪种配置进行工作,供你选择的这个就叫做配置。很显然,当你摄像的时候你不可以访问这块U盘,当你访问这块U盘的时候你不可以摄像,因为你做了选择。第二,既然一个配置代表一种不同的功能,那么很显然,不同的配置可能需要的接口就不一样,我假设你的手机里从硬件上来说一共有5个接口,那么可能当你配置成U盘的时候它只需要用到某一个接口,当你配置成摄像的时候,它可能只需要用到另外两个接口,可能你还有别的配置,然后你可能就会用到剩下那两个接口。

再说说设置,一个手机可能各种配置都确定了,是振动还是铃声已经确定了,各种功能都确定了,但是声音的大小还可以变吧,通常手机的音量是一格一格的变动,大概也就5、6格,那么这个可以算一个setting吧。

你还是不明白啥是配置啥是设置的话,那我多说一下,就直接用大小关系来理解好了,毕竟大家对互相之间的大小关系都更敏感一些,这么说吧,设备大于配置,配置大于接口,接口大于设置,更准确的说是设备可以有多个配置,配置里可以包含一个或更多的接口,而接口通常又具有一个或更多的设置。

minor,分配给接口的次设备号。地球人都知道,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命令查看的,当然只是显示了其中的几个来表示表示而已。咱们对数字都是比较敏感的,一眼就能看到,在每一行的日期前面有两个逗号隔开的数字,对于普通文件而言这个位置显示的是文件的长度,而对于设备文件,这里显示的两个数字表示了该设备的主设备号和次设备号。一般来说,主设备号表明了设备的种类,也表明了设备对应着哪个驱动程序,而次设备号则是因为一个驱动程序要支持多个设备而为了让驱动程序区分它们而设置的。也就是说,主设备号用来帮你找到对应的驱动程序,次设备号给你的驱动用来决定对哪个设备进行操作。上面就显示了俺的移动硬盘主设备号为8,系统里tty设备的主设备为4。

设备要想在linux里分得一个主设备号,有个立足之地,也并不是那么容易的,主设备号虽说不是什么特别稀缺的资源,但还是需要设备先在驱动里提出申请,获得系统的批准才能拥有一个。因为一部分的主设备号已经被静态的预先指定给了许多常见的设备,你申请的时候要避开它们,选择一个里面没有列出来的。这些已经被分配掉的主设备号都列在Documentation/devices.txt文件里。当然,如果你是用动态分配的形式,就可以不去理会这些,直接让系统为你作主,替你选择一个即可。

很显然,任何一个有理智有感情的人都会认为USB设备是很常见的,linux理应为它预留了一个主设备号。看看include/linux/usb.h文件
#define USB_MAJOR180#define USB_DEVICE_MAJOR189
当我们知道了主设备号,满怀激情与向往的来寻找USB的主设备号时,我们却发现这里在上演真假李逵。这两个哪个才是我们苦苦追寻的她?
你可以在内核里搜索它们都曾经出现什么地方,或者就跟随我回到usb_init函数。
retval = usb_major_init();if (retval)goto major_init_failed;retval = usb_register(&usbfs_driver);if (retval)goto driver_register_failed;retval = usb_devio_init();if (retval)goto usb_devio_init_failed;
前面只提了句上面代码是与usbfs相关的就简单的飘过了,这里略微说的多一点。usbfs为咱们提供了在用户空间直接访问usb硬件设备的接口,但是它需要内核的大力支持,usbfs_driver就是用来完成这个光荣任务的。咱们可以去usb_devio_init函数里看一看,它在drivers/usb/core/devio.c文件里定义
int __init usb_devio_init(void){int retval;retval = register_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX,"usb_device");if (retval) {printk(KERN_ERR "Unable to register minors for usb_device\n");goto out;}cdev_init(&usb_device_cdev, &usbdev_file_operations);retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX);if (retval) {printk(KERN_ERR "Unable to get usb_device major %d\n",       USB_DEVICE_MAJOR);goto error_cdev;}usb_register_notify(&usbdev_nb);out:return retval;error_cdev:unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);goto out;}

register_chrdev_region函数获得了设备usb_device对应的设备编号,设备usb_device对应的驱动当然就是usbfs_driver,参数USB_DEVICE_DEV也在同一个文件里有定义
#define USB_DEVICE_DEVMKDEV(USB_DEVICE_MAJOR, 0)
终于再次见到了USB_DEVICE_MAJOR,也终于明白它是为了usbfs而生,为了让广大人民群众能够在用户空间直接和usb设备通信而生。因此,它并不是我们所要寻找的。
那么答案很明显了,USB_MAJOR就是咱们苦苦追寻的那个她,就是linux为USB设备预留的主设备号。事实上,前面usb_init函数调用的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文件里显示了所有当前系统里已经分配出去的主设备号,当然上面只是列出了字符设备,Block devices被有意的飘过了。很明显,咱们前面提到的usb_device和usb都在里面。

不过到这里还没完,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起作用时起作用。

说完了设备号,回到struct usb_interface的151行,condition字段表示接口和驱动的绑定状态,enum usb_interface_condition类型,在include/linux/usb.h里定义
enum usb_interface_condition {USB_INTERFACE_UNBOUND = 0,USB_INTERFACE_BINDING,USB_INTERFACE_BOUND,USB_INTERFACE_UNBINDING,};

前面说linux设备模型的时候说了,设备和驱动是相生相依的关系,总线上的每个设备和驱动都在等待着命中的那个她,找到了,执子之手与子偕老,找不到,孤苦伶仃北冰洋。enum usb_interface_condition形象的描绘了这个过程中接口的个中心情,孤苦、期待、幸福、分开,人生又何尝不是如此?

needs_remote_wakeup和pm_usage_cnt是关于挂起和唤醒的。协议里规定,所有的usb设备都必须支持挂起状态,就是说为了达到节电的目的,当设备在指定的时间内,3ms吧,如果没有发生总线传输,就要进入挂起状态。当它收到一个non-idle的信号时,就会被唤醒。needs_remote_wakeup表示是否需要打开远程唤醒功能。远程唤醒允许挂起的设备给主机发信号,通知主机它将从挂起状态恢复,注意如果主机处于挂起状态,就会唤醒主机,不然主机仍然在睡着,设备自个醒过来干吗用。协议里并没有要求USB设备一定要实现远程唤醒的功能,即使实现了,从主机这边儿也可以打开或关闭它。pm_usage_cnt,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。
0 0