《Linux那些事儿之我是USB》我是U盘(11)从协议中来到协议中去

从协议中来,到协议中去(上)

在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盘里面吗?这个就只能在后面的代码里去知道了。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值