发布时间:2014-09-05 16:47:26作者:知识屋
从协议中来,到协议中去(上)
在structusb_driver中,.probe和.disconnect的原型如下:
836 int(*probe) (struct usb_interface *intf,
837 const struct usb_device_id *id);
838
839 void(*disconnect) (struct usb_interface *intf);
我们来看其中的参数,structusb_device_id这个不用说了,刚才已经介绍过,那么struct usb_interface从何而来?还是让我们先从struct usb_device说起。
我们知道每一个设备对应一个struct device结构体变量,但是设备不可能是万能的,生命是多样性的,就像我们可以用“人”来统称全人类,但是分得细一点,又有男人和女人的区别。那么设备也一样,由于有各种各样的设备,于是又出来了更多的词汇(数据结构),比如针对USB设备,开发人员们设计了一个叫做struct usb_device的结构体。它定义于include/linux/usb.h:
336 struct usb_device {
337 int devnum; /* Address on USBbus */
338 char devpath [16]; /* Use in messages: /port/port/... */
339 enumusb_device_state state;/*configured, not attached, etc */
340 enum usb_device_speed speed; /* high/full/low (or error) */
341
342 structusb_tt *tt; /* low/full speed dev, highspeed hub */
343 int ttport; /* device port onthat tt hub */
344
345 unsigned inttoggle[2]; /* one bit foreach endpoint
346 * ([0] = IN, [1] = OUT) */
347
348 structusb_device *parent; /* ourhub, unless we're the root */
349 structusb_bus *bus; /* Bus we're part of */
350 structusb_host_endpoint ep0;
351
352 structdevice dev; /* Generic device interface */
353
354 structusb_device_descriptor descriptor;/* Descriptor */
355 struct usb_host_config *config; /*All of the configs */
356
357 structusb_host_config *actconfig;/* the active configuration */
358 structusb_host_endpoint *ep_in[16];
359 structusb_host_endpoint *ep_out[16];
360
361 char**rawdescriptors; /* Raw descriptors for eachconfig */
362
363 unsignedshort bus_mA; /* Current available from thebus */
364 u8portnum; /* Parent port number (origin 1) */
365 u8level; /* Number of USB hub ancestors */
366
367 unsigneddiscon_suspended:1; /* Disconnected while suspended */
368 unsignedhave_langid:1; /* whether string_langid is valid */
369 intstring_langid; /* language ID for strings */
370
371 /* staticstrings from the device */
372 char*product; /* iProduct string, if present */
373 char*manufacturer; /* iManufacturer string, if present */
374 char*serial; /* iSerialNumber string, if present */
375
376 structlist_head filelist;
377 #ifdef CONFIG_USB_DEVICE_CLASS
378 structdevice *usb_classdev;
379 #endif
380 #ifdef CONFIG_USB_DEVICEFS
381 structdentry *usbfs_dentry;/*usbfs dentry entry for the device*/
382 #endif
383 /*
384 * Child devices - these can be eithernew devices
385 * (if this is a hub device), ordifferent instances
386 * of this same device.
387 *
388 * Each instance needs its own set ofdata structures.
389 */
390
391 intmaxchild; /* Number of ports if hub */
392 structusb_device *children[USB_MAXCHILDREN];
393
394 intpm_usage_cnt; /* usage counter for autosuspend */
395 u32quirks; /* quirks of the whole device */
396
397 #ifdef CONFIG_PM
398 structdelayed_work autosuspend; /* for delayed autosuspends */
399 struct mutex pm_mutex; /* protectsPM operations */
400
401 unsignedlong last_busy; /* time of last use */
402 intautosuspend_delay; /* injiffies */
403
404 unsignedauto_pm:1; /*autosuspend/resume in progress */
405 unsigneddo_remote_wakeup:1;/* remote wakeup should be enabled */
406 unsignedautosuspend_disabled:1; /* autosuspend and autoresume */
407 unsignedautoresume_disabled:1; /* disabled by the user */
408 #endif
409 };
410 #define to_usb_device(d) container_of(d,struct usb_device, dev)
看起来很复杂的一个数据结构,不过我们目前不需要去理解她的每一个成员,不过我们可以看到,其中有一个成员struct device dev,这就是前面说的那个属于每个设备的struct device结构体变量。
实际上,U盘驱动里边并不会直接去处理这个结构体,因为对于一个U盘来说,它就是对应这么一个struct usb_device的变量,这个变量由USB Core负责申请和赋值。但是我们需要记住这个结构体变量,因为日后我们调用USBCore提供的函数时,会把这个变量作为参数传递上去。因为很简单,要和USB Core交流,总得让人家知道我们是谁吧。比如后来要调用的一个函数,usb_buffer_alloc,它就需要这个参数。
而对U盘设备驱动来说,比这个struct usb_device更重要的数据结构是,struct usb_interface。走到这一步,我们不得不去了解一些USB设备的规范了,或者专业一点说,USB协议。因为我们至少要知道什么是USB接口(Interface)。
从协议中来,到协议中去(中)
任何事物都有其要遵守的规矩。USB设备要遵循的就是USB协议。 不管是软件还是硬件,在设计的开始,总是要参考USB协议。怎么设计硬件?如何编写软件?不看USB协议,谁也不可能凭空想象出来。
USB协议规定了,每个USB设备都得有一些基本的元素,称为描述符。有四类描述符是任何一种USB设备都得有的,它们是,设备描述符(Device Descriptor),配置描述符(ConfigurationDescriptor),接口描述符(Interface Descriptor),端点描述符(Endpoint Descriptor)。描述符里的东西是一个设备出厂时就被厂家给固化在设备中了。这种东西不管怎样也改变不了,比如我有一个Intel的U盘,里面的固有的信息肯定是在Intel出厂时就被烙在里边了,厂家早已把它的一切,烙上Intel印。所以当我插入U盘,用cat /proc/scsi/scsi命令看一下的话,“Vendor”一项显示的肯定是Intel。
关于这几种描述符,USB Core在总线扫描就会去读取,获得里边的信息,其中,设备描述符描述的是整个设备。注意了,这个设备和咱们一直讲的设备驱动那里的设备是不一样的。因为一个USB设备实际上指的是一种宏观上的概念,它可以是一种多功能的设备,改革开放之后,多功能的东西越来越多了,比如外企常见的多功能一体机,就是集打印机、复印机、扫描仪、传真机于一体的设备。当然,这不属于USB设备,但是USB设备当然也有这种情况,比如电台DJ可能会用到的,一个键盘,上边带一个扬声器,它们用两个USB接口接到USB Hub上去,而设备描述符描述的就是这整个设备的特点。
那么配置描述符呢?老实说,对我们了解U盘驱动真是没有什么意义,但是作为一个有责任的人,此刻,我必须为它多说几句,一个设备可以有一种或者几种配置。没见过具体的USB设备?那么好,手机见过吧,每个手机都会有多种配置,或者说“设定”,比如,我的这款,Nokia 6300。手机语言可以设定为English、繁体中文或简体中文。一旦选择了其中一种,那么手机里面所显示的所有的信息都是该种语言/字体。还有最简单的例子,操作模式也有好几种,标准、无声、会议等。基本上如果我设为“会议”,那么就是只振动不发声;要是设为无声,那么就什么动静也不会有,只能凭感觉了。那么USB设备的配置也是如此,不同的USB设备当然有不同的配置了,或者说需要配置哪些东西也会不一样。好了,关于配置,就说这么多。
对于USB设备驱动程序编写者来说,更为关键的是下面的接口和端点。先说接口。第一句,一个接口对应一个USB设备驱动程序。没错,还举前边那个例子。一个USB设备,两种功能,一个键盘,上面带一个扬声器,两个接口,那肯定得要两个驱动程序,一个是键盘驱动程序,一个是音频流驱动程序。“道上”的兄弟喜欢把这样两个整合在一起的东西叫做一个设备,我门用接口来区分这两者。于是有了我们前面提到的那个数据结构,struct usb_interface,它定义于include/linux/usb.h:
140 struct usb_interface {
141 /* array of alternate settings forthis interface,
142 * stored in no particular order */
143 struct usb_host_interface *altsetting;
144
145 struct usb_host_interface*cur_altsetting; /* the currently
146 * active alternate setting */
147 unsignednum_altsetting; /* number of alternatesettings */
148
149 int minor; /* minor number this interface is
150 * bound to */
151 enumusb_interface_condition condition; /* state of binding */
152 unsignedis_active:1; /* the interfaceis not suspended */
153 unsignedneeds_remote_wakeup:1; /*driver requires remote wakeup*/
154
155 structdevice dev; /* interface specific device info */
156 struct device *usb_dev;/* pointer to the usbclass's device, if any*/
157 int pm_usage_cnt; /* usage counter for autosuspend */
158 };
159 #defineto_usb_interface(d) container_of(d, struct usb_interface, dev)
160 #define interface_to_usbdev(intf) /
161 container_of(intf->dev.parent, struct usb_device, dev)
这个结构体是一个贯穿整个U盘驱动程序的,所以虽然我们不用去深入了解,但是需要记住,整个U盘驱动程序在后面任何一处提到的struct usb_interface都是同一个变量,这个变量是在USB Core总线扫描时就申请好了的。我们只需要直接用就是了,比如前面说过的storage_probe(struct usb_interface *intf,const struct usb_device_id*id),storage_disconnect(structusb_interface *intf)这两个函数中的参数intf。
而这里130行的宏interface_to_usbdev,也会用得着的,顾名思义,就是从一个structusb_interface转换成一个structusb_device,我们说过了,有些函数需要的参数就是struct usb_device,而不是usb_interface,所以这种转换是经常会用到的,而这个宏,USB Core的设计者们也为我们准备好了,除了感激,我们还能说什么呢?
从协议中来,到协议中去(下)
如果你是急性子,那这时候你一定很想开始看storage_probe函数了,因为整个U盘的工作就是从这里开始的。
前面我们已经了解了设备、配置和接口,还剩最后一个就是端点。USB通信的最基本的形式就是通过端点,一个接口有一个或多个端点,而作为像U盘这样的存储设备,它至少有一个控制端点,两个批量端点。这些端点都是干什么用的?说来话长,真是一言难尽啊。
USB协议中规定了,USB设备有四种通信方式,分别是控制传输、中断传输、批量传输、等时传输。其中,等时传输显然是用于音频和视频一类的设备,这类设备期望能够有一个比较稳定的数据流,比如你在网上QQ视频聊天,肯定希望每分钟传输的图像/声音速率是比较稳定的。usb-storage里边肯定不会用到等时传输。因为我们只管复制一个文件,管它第一秒和第二秒的传输有什么区别,只要几十秒内传完了就行了。
相比之下,等时传输是四种传输中最麻烦的,所以,U盘用不着。不过我要说,中断传输也用不着,对于U盘来说,的确用不着,虽然说USBMass Storage的协议中边有一个叫做CBI的传输协议,CBI就是Control/Bulk/Interrupt,即控制/批量/中断,这三种传输都会用到,但这种传输协议并不适用于U盘,U盘使用的是叫做Bulk-Only的传输协议。使用这种协议的设备只有两种传输方式,一种是批量传输,另一种是控制传输,控制传输是任何一种USB设备都必须支持的,它专门用于传输一些控制信息。比如我想查询一下关于这个接口的一些信息,那么就用控制传输。而批量传输,它就是U盘的主要工作了,读写数据,这种情况就得用批量传输。具体的传输我们后面再讲。
好了,知道了传输方式,就可以来认识端点了。和端点齐名的有一个叫做管道,或者有文化的人管这个叫Pipe。端点就是通信的发送点或者接收点,要发送数据,那你只要把数据发送到正确的端点那里就可以了。之所以U盘有两个批量端点,是因为端点也是有方向的,一个叫做Bulk IN,一个叫做Bulk OUT。从USB主机到设备称为OUT,从设备到主机称为IN。而管道实际上只是为了让我们能够找到端点,就相当于我们日常说的邮编地址,比如一个国家,为了通信,我们必须给各个地方取名,给各条大大小小的路取名,比如要从某偏僻的小县城出发,要到北京,那你的这个端点就是北京,而你得知道你来北京的路线,那这个从你们县城到北京的路线就算一条管道。有人好奇地问了,管道应该有两个端点吧,一个端点是北京,那另一个端点呢?答案是,这条管道有些特殊,我们只需要知道一端的目的地是北京,而另一端是哪里无所谓,因为不管你在哪里你都得到北京。
严格来说,管道的另一端应该是USB主机,USB协议中边也是这么规定的,协议中管道代表着一种能力。怎样一种能力呢?在主机和设备上的端点之间移动数据,听上去挺玄的。因为事实上,USB里边所有的数据传输都是由主机发起的。一切都是以主机为核心,各种设备紧紧围绕在主机周围。所以USB Core里边很多函数就是为了让USB主机能够正确地完成数据传输或者说传输调度,就得告诉主机这个管道,换而言之,它得知道自己这次调度的数据是传送给谁,或者从谁那里传输过来。不过有人又要问了,比如说要从U盘里读一个文件,那告诉USB主机某个端点能有用吗?那个文件又不是存放在一个端点里边,它不是存放在U盘里面吗?这个就只能在后面的代码里去知道了。
linux一键安装web环境全攻略 在linux系统中怎么一键安装web环境方法
Linux网络基本网络配置方法介绍 如何配置Linux系统的网络方法
Linux下DNS服务器搭建详解 Linux下搭建DNS服务器和配置文件
对Linux进行详细的性能监控的方法 Linux 系统性能监控命令详解
linux系统root密码忘了怎么办 linux忘记root密码后找回密码的方法
Linux基本命令有哪些 Linux系统常用操作命令有哪些
Linux必学的网络操作命令 linux网络操作相关命令汇总
linux系统从入侵到提权的详细过程 linux入侵提权服务器方法技巧
linux系统怎么用命令切换用户登录 Linux切换用户的命令是什么
在linux中添加普通新用户登录 如何在Linux中添加一个新的用户
2012-07-10
CentOS 6.3安装(详细图解教程)
Linux怎么查看网卡驱动?Linux下查看网卡的驱动程序
centos修改主机名命令
Ubuntu或UbuntuKyKin14.04Unity桌面风格与Gnome桌面风格的切换
FEDORA 17中设置TIGERVNC远程访问
StartOS 5.0相关介绍,新型的Linux系统!
解决vSphere Client登录linux版vCenter失败
LINUX最新提权 Exploits Linux Kernel <= 2.6.37
nginx在网站中的7层转发功能