usb驱动分析一:http://wenku.baidu.com/view/fbce5512a2161479171128f5.html

USB驱动分析(二)

        分类:            USB驱动2012-07-18 10:59615人阅读评论(0)收藏举报
structinterfacedescriptor数据结构semaphoretable

probe,disconnect,id_table,这三个咚咚中首先要登场亮相的是id_table,它是干嘛用的呢?

我们说过,一个device只能绑定一个driver,但driver并非只能支持一种设备,道理很简单,比如我有两块U盘,那么我可以一起都插入,但是我只需要加载一个模块,usb-storage,没听说过插入两块U盘就得加载两次驱动程序的,除非这两块U盘本身就得使用不同的驱动程序.也正是因为一个模块可以被多个设备共用,才会有模块计数这么一个说法.

ok,既然一个driver可以支持多个device,那么当发现一个device的时候,如何知道哪个driver才是她的Mr.Right呢?这就是id_table的用处,让每一个struct usb_driver准备一张表,里边注明该driver支持哪些设备,这总可以了吧.如果你这个设备属于这张表里的,那么ok,绑定吧,如果不属于这张表里的,那么不好意思,您请便.哪凉快上哪去.

来自struct usb_driver中的id_table,

const struct usb_device_id *id_table;

实际上是一个指针,一个struct usb_device_id结构体的指针,当然赋了值以后就是代表一个数组名了,正如我们在定义struct usb_driver usb_storage_driver中所赋的值那样,.id_table=storage_usb_ids,那好,我们来看一下usb_device_id这究竟是怎样一个结构体.

struct usb_device_id来自include/linux/mod_devicetable.h,

40 /*
     41  * Device table entry for "new style" table-driven USB drivers.
     42  * User mode code can read these tables to choose which modules to load.
     43  * Declare the table as a MODULE_DEVICE_TABLE.
     44  *
     45  * A probe() parameter will point to a matching entry from this table.
     46  * Use the driver_info field for each match to hold information tied
     47  * to that match:  device quirks, etc.
     48  *
     49  * Terminate the driver's table with an all-zeroes entry.
     50  * Use the flag values to control which fields are compared.
     51  */
     52
     53 /**
     54  * struct usb_device_id - identifies USB devices for probing and hotplugging
     55  * @match_flags: Bit mask controlling of the other fields are used to match
     56  *      against new devices.  Any field except for driver_info may be used,
     57  *      although some only make sense in conjunction with other fields.
     58  *      This is usually set by a USB_DEVICE_*() macro, which sets all
     59  *      other fields in this structure except for driver_info.
     60  * @idVendor: USB vendor ID for a device; numbers are assigned
     61  *      by the USB forum to its members.
     62  * @idProduct: Vendor-assigned product ID.
     63  * @bcdDevice_lo: Low end of range of vendor-assigned product version numbers.
     64  *      This is also used to identify individual product versions, for
     65  *      a range consisting of a single device.
     66  * @bcdDevice_hi: High end of version number range.  The range of product
     67  *      versions is inclusive.
     68  * @bDeviceClass: Class of device; numbers are assigned
     69  *      by the USB forum.  Products may choose to implement classes,
     70  *      or be vendor-specific.  Device classes specify behavior of all
     71  *      the interfaces on a devices.
     72  * @bDeviceSubClass: Subclass of device; associated with bDeviceClass.
     73  * @bDeviceProtocol: Protocol of device; associated with bDeviceClass.
     74  * @bInterfaceClass: Class of interface; numbers are assigned
     75  *      by the USB forum.  Products may choose to implement classes,
     76  *      or be vendor-specific.  Interface classes specify behavior only
     77  *      of a given interface; other interfaces may support other classes.
     78  * @bInterfaceSubClass: Subclass of interface; associated with bInterfaceClass.
     79  * @bInterfaceProtocol: Protocol of interface; associated with bInterfaceClass.
     80  * @driver_info: Holds information used by the driver.  Usually it holds
     81  *      a pointer to a descriptor understood by the driver, or perhaps
     82  *      device flags.
     83  *
     84  * In most cases, drivers will create a table of device IDs by using
     85  * USB_DEVICE(), or similar macros designed for that purpose.
     86  * They will then export it to userspace using MODULE_DEVICE_TABLE(),
     87  * and provide it to the USB core through their usb_driver structure.
     88  *
     89  * See the usb_match_id() function for information about how matches are
     90  * performed.  Briefly, you will normally use one of several macros to help
     91  * construct these entries.  Each entry you provide will either identify
     92  * one or more specific products, or will identify a class of products
     93  * which have agreed to behave the same.  You should put the more specific
     94  * matches towards the beginning of your table, so that driver_info can
     95  * record quirks of specific products.
     96  */
     97 struct usb_device_id {
     98         /* which fields to match against? */
     99         __u16           match_flags;
    100
    101         /* Used for product specific matches; range is inclusive */
    102         __u16           idVendor;
    103         __u16           idProduct;
    104         __u16           bcdDevice_lo;
    105         __u16           bcdDevice_hi;
    106
    107         /* Used for device class matches */
    108         __u8            bDeviceClass;
    109         __u8            bDeviceSubClass;
    110         __u8            bDeviceProtocol;
    111
    112         /* Used for interface class matches */
    113         __u8            bInterfaceClass;
    114         __u8            bInterfaceSubClass;
    115         __u8            bInterfaceProtocol;
    116
    117         /* not matched against */
    118         kernel_ulong_t  driver_info;
    119 };
实际上这个结构体对每一个usb设备来说,就相当于是她的身份证,记录了她的一些基本信息,通常我们的身份证上会记录我们的姓名,性别,出生年月,户口地址等等,而usb设备她也有她需要记录的信息,以区分她和别的usb设备,比如Vendor-厂家,Product-产品,以及其他一些比如产品编号,产品的类别,遵循的协议,这些都会在usb的规范里边找到对应的冬冬.暂且先不细说.

于是我们知道,一个usb_driver会把它的这张id表去和每一个usb设备的实际情况进行比较,如果该设备的实际情况和这张表里的某一个id相同,准确地说,只有这许多特征都吻合,才能够把一个usb device和这个usb driver进行绑定,这些特征哪怕差一点也不行.就像我们每个人都是一道弧,都在不停寻找能让彼此嵌成完整的圆的另一道弧,事实却是,每个人对∏(PI)的理解不尽相同,而圆心能否重合,或许只有痛过才知道.差之毫厘,失之交臂.

那么usb设备的实际情况是什么时候建立起来的?嗯,在介绍.probe指针之前有必要先谈一谈另一个数据结构了,她就是struct usb_device.

在struct usb_driver中,.probe和.disconnect的原型如下:

int (*probe) (struct usb_interface *intf,const struct usb_device_id *id);

void (*disconnect) (struct usb_interface *intf);

我们来看其中的参数,struct usb_device_id这个不用说了,刚才已经介绍过,那么struct usb_interface从何而来?还是让我们先从struct usb_device说起.

我们知道每一个device对应一个struct device结构体变量,但是device不可能是万能的,生命是多样性的,就像我们可以用"人"来统称全人类,但是分的细一点,又有男人和女人的区别,那么device也一样,由于有各种各样的设备,于是又出来了更多的词汇(数据结构),比如针对usb设备,开发者们设计了一个叫做struct usb_device的结构体.她定义于include/linux/usb.h,

294 /*
    295  * struct usb_device - kernel's representation of a USB device
    296  *
    297  * FIXME: Write the kerneldoc!
    298  *
    299  * Usbcore drivers should not set usbdev->state directly.  Instead use
    300  * usb_set_device_state().
    301  */
    302 struct usb_device {
    303         int             devnum;         /* Address on USB bus */
    304         char            devpath [16];   /* Use in messages: /port/port/... */
    305         enum usb_device_state   state;  /* configured, not attached, etc */
    306         enum usb_device_speed   speed;  /* high/full/low (or error) */
    307
    308         struct usb_tt   *tt;            /* low/full speed dev, highspeed hub */
    309         int             ttport;         /* device port on that tt hub */
    310
    311         struct semaphore serialize;
    312
    313         unsigned int toggle[2];         /* one bit for each endpoint ([0] = IN, [1] = OUT) */
    314         int epmaxpacketin[16];          /* INput endpoint specific maximums */
    315         int epmaxpacketout[16];         /* OUTput endpoint specific maximums */
    316
    317         struct usb_device *parent;      /* our hub, unless we're the root */
    318         struct usb_bus *bus;            /* Bus we're part of */
    319
    320         struct device dev;              /* Generic device interface */
    321
    322         struct usb_device_descriptor descriptor;/* Descriptor */
    323         struct usb_host_config *config; /* All of the configs */
    324         struct usb_host_config *actconfig;/* the active configuration */
    325
    326         char **rawdescriptors;          /* Raw descriptors for each config */
    327
    328         int have_langid;                /* whether string_langid is valid yet */
    329         int string_langid;              /* language ID for strings */
    330
    331         void *hcpriv;                   /* Host Controller private data */
    332
    333         struct list_head filelist;
    334         struct dentry *usbfs_dentry;    /* usbfs dentry entry for the device */
    335
    336         /*
    337          * Child devices - these can be either new devices
    338          * (if this is a hub device), or different instances
    339          * of this same device.
    340          *
    341          * Each instance needs its own set of data structures.
    342          */
    343
    344         int maxchild;                   /* Number of ports if hub */
    345         struct usb_device *children[USB_MAXCHILDREN];
    346 };
    347 #define to_usb_device(d) container_of(d, struct usb_device, dev)
看起来很复杂的一个数据结构,不过我们目前不需要去理解她的每一个成员,不过我们可以看到,其中有一个成员struct device dev,没错,这就是前面说的那个属于每个设备的struct device结构体变量.

实际上,U盘驱动里边并不会直接去处理这个结构体,因为对于一个U盘来说,她就是对应这么一个struct usb_device的变量,这个变量由usb core负责申请和赋值.但是我们需要记住这个结构体变量,因为日后我们调用usb core提供的函数的时候,会把这个变量作为参数传递上去,因为很简单,要和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,configuration descriptor,interface descriptor,endpoint descriptor.描述符里的冬冬是一个设备出厂的时候就被厂家给固化在设备里了.这种东西不管怎样也改变不了,比如我有个Intel的U盘,那里边的固有的信息肯定是在Intel出厂的时候就被烙在里边了,厂家早已把它的一切,烙上Intel印.所以当我插入U盘,用cat /proc/scsi/scsi命令看一下的话,"Vendor"那一项显示的肯定是Intel. 关于这几种描述符,usb core在总线扫描那会就会去读取,会去获得里边的信息,其中,device描述符描述的是整个device,注意了,这个device和咱们一直讲的device和driver那里的device是不一样的.因为一个usb device实际上指的是一种宏观上的概念,它可以是一种多功能的设备,改革开放之后,多功能的东西越来越多了,比如外企常见的多功能一体机,就是集打印机,复印机,扫描仪,传真机于一体的设备, 当然,这不属于usb设备,但是usb设备当然也有这种情况,比如电台DJ可能会用到的,一个键盘,上边带一个扬声器,它们用两个usb接口接到usb hub上去,而device描述符描述的就是这整个设备的特点.那么configuration描述符呢,老实说,对我们了解U盘驱动真是没有什么意义,但是作为一个有责任心的男人,此刻,我必须为它多说几句,虽然只是很简单的说几句.一个设备可以有一种或者几种配置,这能理解吧?没见过具体的usb设备?那么好,手机见过吧,每个手机都会有多种配置,或者说"设定",比如,我的这款,Nokia6300,手机语言,可以设定为English,繁体中文,简体中文,一旦选择了其中一种,那么手机里边所显示的所有的信息都是该种语言/字体.还有最简单的例子,操作模式也有好几种,标准,无声,会议,etc.基本上如果我设为"会议",那么就是只振动不发声,要是设为无声,那么就啥动静也不会有,只能凭感觉了,以前去公司面试的话通常就是设为无声,因为觉得振动也不好,让人家面试官听到了还是不合适.那么usb设备的配置也是如此,不同的usb设备当然有不同的配置了,或者说需要配置哪些东西也会不一样.好了,关于配置,就说这么多,更多的我们暂时也不需要了解了.

对于usb设备驱动程序编写者来说,更为关键的是下面两个,interface和endpoint.先说,interface.第一句,一个interface对应一个usb设备驱动程序.没错,还说前边那个例子,一个usb设备,两种功能,一个键盘,上面带一个扬声器,两个接口,那这样肯定得要两个驱动程序,一个是键盘驱动程序,一个是音频流驱动程序.道上的兄弟喜欢把这样两个整合在一起的东西叫做一个设备,那好,让他们去叫吧,我门用interface来区分这两者行了吧.于是有了我们前面提到的那个数据结构,struct usb_interface.它定义于include/linux/usb.h:

71 /**
     72  * struct usb_interface - what usb device drivers talk to
     73  * @altsetting: array of interface structures, one for each alternate
     74  *      setting that may be selected.  Each one includes a set of
     75  *      endpoint configurations.  They will be in no particular order.
     76  * @num_altsetting: number of altsettings defined.
     77  * @cur_altsetting: the current altsetting.
     78  * @driver: the USB driver that is bound to this interface.
     79  * @minor: the minor number assigned to this interface, if this
     80  *      interface is bound to a driver that uses the USB major number.
     81  *      If this interface does not use the USB major, this field should
     82  *      be unused.  The driver should set this value in the probe()
     83  *      function of the driver, after it has been assigned a minor
     84  *      number from the USB core by calling usb_register_dev().
     85  * @condition: binding state of the interface: not bound, binding
     86  *      (in probe()), bound to a driver, or unbinding (in disconnect())
     87  * @dev: driver model's view of this device
     88  * @class_dev: driver model's class view of this device.
     89  *
     90  * USB device drivers attach to interfaces on a physical device.  Each
     91  * interface encapsulates a single high level function, such as feeding
     92  * an audio stream to a speaker or reporting a change in a volume control.
     93  * Many USB devices only have one interface.  The protocol used to talk to
     94  * an interface's endpoints can be defined in a usb "class" specification,
     95  * or by a product's vendor.  The (default) control endpoint is part of
     96  * every interface, but is never listed among the interface's descriptors.
     97  *
     98  * The driver that is bound to the interface can use standard driver model
     99  * calls such as dev_get_drvdata() on the dev member of this structure.
    100  *
    101  * Each interface may have alternate settings.  The initial configuration
    102  * of a device sets altsetting 0, but the device driver can change
    103  * that setting using usb_set_interface().  Alternate settings are often
    104  * used to control the the use of periodic endpoints, such as by having
    105  * different endpoints use different amounts of reserved USB bandwidth.
    106  * All standards-conformant USB devices that use isochronous endpoints
    107  * will use them in non-default settings.
    108  *
    109  * The USB specification says that alternate setting numbers must run from
    110  * 0 to one less than the total number of alternate settings.  But some
    111  * devices manage to mess this up, and the structures aren't necessarily
    112  * stored in numerical order anyhow.  Use usb_altnum_to_altsetting() to
    113  * look up an alternate setting in the altsetting array based on its number.
    114  */
    115 struct usb_interface {
    116         /* array of alternate settings for this interface,
    117          * stored in no particular order */
    118         struct usb_host_interface *altsetting;
    119
    120         struct usb_host_interface *cur_altsetting;      /* the currently
    121                                          * active alternate setting */
    122         unsigned num_altsetting;        /* number of alternate settings */
    123
    124         int minor;                      /* minor number this interface is bound to */
    125         enum usb_interface_condition condition;         /* state of binding */
    126         struct device dev;              /* interface specific device info */
    127         struct class_device *class_dev;
    128 };
    129 #define to_usb_interface(d) container_of(d, struct usb_interface, dev)
    130 #define interface_to_usbdev(intf) \
    131         container_of(intf->dev.parent, struct usb_device, dev)
嗬,贴这么长一段,怎么又是注释为主啊?知足吧,Linux代码中注释实在是太少了,等你真的需要认真看某一个模块的时候你就会嫌注释少了.这个结构体是一个贯穿整个U盘驱动程序的,所以虽然我们不用去深入了解,但是需要记住,整个U盘驱动程序在后面任何一处提到的struct usb_interface都是同一个变量,这个变量是在usb core总线扫描的时候就申请好了的.我们只需贯彻鲁迅先生的拿来主义即可,直接用就是了.比如前面说过的storage_probe(struct usb_interface *intf,const struct usb_device_id *id),storage_disconnect(struct usb_interface *intf)这两个函数中的那个参数intf.

而这里130行这个宏-interface_to_usbdev,也会用得着的,顾名思义,就是从一个struct usb_interface转换成一个struct usb_device,我们说过了,有些函数需要的参数就是struct usb_device,而不是usb_interface,所以这种转换是经常会用到的,而这个宏,usb core的设计者们也为我们准备好了,除了感激,我们还能说什么呢?

如果你是急性子,那这时候你一定很想开始看storage_probe函数了,因为整个U盘的工作就是从这里开始的.不过,莎士比亚说过,磨刀不误砍柴功.不妨继续花点时间,至少把四大关键词中最后一个给弄明白了,

前面我们已经了解了device,configuration,interface,还剩最后一个endpoint.USB通信的最基本的形式就是通过endpoint,道上的兄弟管这个叫做端点,一个接口有一个或多个端点,而作为像U盘这样的存储设备吧,它至少有一个控制端点,两个bulk端点.这些端点都是干嘛的?说来话长,真是一言难尽哪.

usb协议里规定了,usb设备有四种通信方式,分别是控制传输,中断传输,批量传输,等时传输.其中,等时传输显然是用于音频和视频一类的设备,这类设备期望能够有个比较稳定的数据流,比如你在网上QQ视频聊天,肯定希望每分钟传输的图像/声音速率是比较稳定的,不能说这一分钟对方看到你在向她向你深情表白,可是下一分钟却看见画面停滞在那里,只能看到你那傻样一动不动,你说这不浪费感情嘛.所以,每一个有良知的男人都应该知道,usb-storage里边肯定不会用到等时传输.因为我们只管copy一个文件,管它第一秒和第二秒的传输有什么区别,只要几十秒内传完了就ok.

相比之下,等时传输是四种传输中最麻烦的,所以,U盘里边用不着,那您就偷着乐去吧.不过我要说,中断传输也用不着,对于U盘来说,的确用不着,虽然说usb mass storage的协议里边有一个叫做CBI的传输协议,CBI就是Control/Bulk/Interrupt,即控制/批量/中断,这三种传输都会用到,但这种传输协议并不适用于U盘,U盘使用的是一个叫做Bulk-Only的传输协议.使用这种协议的设备只有两种传输方式,一种是批量传输,另一种是控制传输,控制传输是任何一种usb设备都必须支持的,它专门用于传输一些控制信息.比如我想查询一下关于这个interface的一些信息,那么就用控制传输,而bulk传输,它就是U盘的主要工作了,读写数据,这种情况就得用bulk传输.具体的传输我们后面再讲.

好了,知道了传输方式,就可以来认识endpoint了.和endpoint齐名的有一个叫做管道,或者有文化的人管这个叫pipe.endpoint就是通信的发送或者接收点,你要发送数据,那你只要把数据发送到正确的端点那里就可以了.之所以U盘有两个bulk端点,是因为端点也是有方向的,一个叫做Bulk in,一个叫做Bulk out,从usb主机到设备称为out,从设备到主机称为in.而管道,实际上只是为了让我们能够找到端点,就相当于我们日常说的邮编地址,比如一个国家,为了通信,我们必须给各个地方取名,完了给各条大大小小的路取名,比如你要揭发你们那里的官员腐败,你要去上访,你从某偏僻的小县城出发,要到北京来上访,那你的这个端点(endpoint)就是北京,而你得知道你来北京的路线,那这个从你们县城到北京的路线就算一条管道.有人好奇的问了,管道应该有两端吧,一个端点是北京,那另一个端点呢?答案是,这条管道有些特殊,就比如上访,我们只需要知道一端是北京,而另一端是哪里无所谓,因为不管你在哪里你都得到北京来上访.没听说过你在山西你可以上访,你要在宁夏还不能上访了,没这事对吧.

严格来说,管道的另一端应该是usb主机,即前面说的那个host,usb协议里边也是这么说的,协议里边说pipes代表着一种能力,怎样一种能力呢,在主机和设备上的端点之间移动数据,听上去挺玄的.因为事实上,usb里边所有的数据传输都是有主机发起的.一切都是以主机为核心,各种设备紧紧围绕在主机周围.所以呢,usb core里边很多函数就是,为了让usb host能够正确的完成数据传输或者说传输调度,就得告诉host这么一个pipe,换言之,它得知道自己这次调度的数据是传送给谁或者从谁那里传输过来.不过有人又要问了,比如说我要从U盘里读一个文件,那我告诉usb host某个端点能有用吗?那个文件又不是存放在一个端点里边,它不该是存放在U盘里边吗?这个就只能在后面的代码里去知道了.实际上端点这东西是一个很虚的东西,它的身上充分体现了我国军事思想中的声东击西的想法,即数据本身并不是在端点里,但是看上去却觉得仿佛在端点里.这一切的谜团,让我们在storage_probe()函数里去慢慢解开吧.

对于整个usb-storage模块,usb_stor_init()是它的开始,然而,对于U盘驱动程序来说,它真正驱使U盘工作却是始于storage_probe().

两条平行线只要相交,就注定开始纠缠一生,不管中间是否短暂分离. usbcore为设备找到了适合她的驱动程序,或者为驱动程序找到了他所支持的设备,但这只是表明双方的第一印象还可以,但彼此是否真的适合对方还需要进一步的了解.毋庸置疑,了解对方的第一步是,知道她有哪些爱好,她的生日,她的星座,喜欢吃什么,而U盘driver则会调用函数storage_probe()去认识对方,她是个什么样的设备,她的生活习惯是?她的联系方式是?这里调用了四个函数get_device_info,get_protocol,get_transport,get_pipes.当然还有一些别人并不了解的冬冬你也会知道,比如她的三围,这里对应的就是usb_stor_Bulk_man_lun().

整个U盘驱动这部大戏,由storage_probe开始,由storage_disconnect结束.其中,storage_probe这个函数占了相当大的篇幅.我们一段一段来看.这两个函数都来自drivers/usb/storage/usb.c中:

926 /* Probe to see if we can drive a newly-connected USB device */
    927 static int storage_probe(struct usb_interface *intf,
    928                          const struct usb_device_id *id)
    929 {
    930         struct us_data *us;
    931         const int id_index = id - storage_usb_ids;
    932         int result;
    933
    934         US_DEBUGP("USB Mass Storage device detected\n");
    935
    936         /* Allocate the us_data structure and initialize the mutexes */
    937         us = (struct us_data *) kmalloc(sizeof(*us), GFP_KERNEL);
    938         if (!us) {
    939                 printk(KERN_WARNING USB_STORAGE "Out of memory\n");
    940                 return -ENOMEM;
    941         }
    942         memset(us, 0, sizeof(struct us_data));
首先先贴出这么几行,两个参数不用多说了,struct usb_interface和struct usb_device_id的这两个指针都是前面介绍过的,来自usb core那一层,我们整个故事里用到的就是这么一个,不是说一会指向你,一会指向他,这两个指针的指向是定下来的.

930行,最重要的一个数据结构终于在这种神不知鬼不觉的地方惊艳亮相了.整个usb-storage模块里边自己定义的数据结构不多,但是us_data算一个.这个数据结构是跟随我们的代码一直走下去的,如影随形,几乎处处都可以看见她的身影.先把它的代码贴出来,来自drivers/usb/storage/usb.h:

105 /* we allocate one of these for every device that we remember */
    106 struct us_data {
    107         /* The device we're working with
    108          * It's important to note:
    109          *    (o) you must hold dev_semaphore to change pusb_dev
    110          */
    111         struct semaphore        dev_semaphore;   /* protect pusb_dev */
    112         struct usb_device       *pusb_dev;       /* this usb_device */
    113         struct usb_interface    *pusb_intf;      /* this interface */
    114         struct us_unusual_dev   *unusual_dev;    /* device-filter entry     */
    115         unsigned long           flags;           /* from filter initially */
    116         unsigned int            send_bulk_pipe;  /* cached pipe values */
    117         unsigned int            recv_bulk_pipe;
    118         unsigned int            send_ctrl_pipe;
    119         unsigned int            recv_ctrl_pipe;
    120         unsigned int            recv_intr_pipe;
    121
    122         /* information about the device */
    123         char                    vendor[USB_STOR_STRING_LEN];
    124         char                    product[USB_STOR_STRING_LEN];
    125         char                    serial[USB_STOR_STRING_LEN];
    126         char                    *transport_name;
    127         char                    *protocol_name;
    128         u8                      subclass;
    129         u8                      protocol;
    130         u8                      max_lun;
    131
    132         u8                      ifnum;           /* interface number   */
    133         u8                      ep_bInterval;    /* interrupt interval */
    134
    135         /* function pointers for this device */
    136         trans_cmnd              transport;       /* transport function     */
    137         trans_reset             transport_reset; /* transport device reset */
    138         proto_cmnd              proto_handler;   /* protocol handler       */
    139
    140         /* SCSI interfaces */
    141         struct Scsi_Host        *host;           /* our dummy host data */
    142         struct scsi_cmnd        *srb;            /* current srb         */
    143
    144         /* thread information */
    145         int                     pid;             /* control thread       */
    146
    147         /* control and bulk communications data */
    148         struct urb              *current_urb;    /* USB requests         */
    149         struct usb_ctrlrequest  *cr;             /* control requests     */
    150         struct usb_sg_request   current_sg;      /* scatter-gather req.  */
    151         unsigned char           *iobuf;          /* I/O buffer           */
    152         dma_addr_t              cr_dma;          /* buffer DMA addresses */
    153         dma_addr_t              iobuf_dma;
    154
    155         /* mutual exclusion and synchronization structures */
    156         struct semaphore        sema;            /* to sleep thread on   */
    157         struct completion       notify;          /* thread begin/end     */
    158         wait_queue_head_t       dev_reset_wait;  /* wait during reset    */
    159         wait_queue_head_t       scsi_scan_wait;  /* wait before scanning */
    160         struct completion       scsi_scan_done;  /* scan thread end      */
    161
    162         /* subdriver information */
    163         void                    *extra;          /* Any extra data          */
    164         extra_data_destructor   extra_destructor;/* extra data destructor   */
    165 };

不难发现,Linux内核中每一个重要的数据结构都很复杂,这体现了内核代码编写者们的一种清高,仿佛不用点复杂的数据结构不足以体现他们是个腕儿.这可就苦了我们这些读代码的了,尤其是中国的学生,毕竟谭浩强的书里边翻多少遍也翻不出这么一变态的数据结构吧.所以,此刻,每一个有志青年都应该倍感责任重大,只有我们国家强大了,我们才能把谭浩强的书籍向全世界推广,从而告诉那些写内核代码的家伙,不要写那么复杂的冬冬,要按照谭浩强的书里的规矩来设计数据结构,来编写代码.这才是造福全人类的做法.不是吗?

先不说这些了,总之,这个令人头疼的数据结构是每一个device都有的,换句话说,我们会为每一个device申请一个us_data,因为这个结构里边的冬冬我们之后一直会用得着的.至于怎么用,每个成员什么意思,以后用上了再细说.930行,struct us_data *us,于是,日后我们会非常频繁的看到us的.另,us什么意思?尼采说:us者,usb storage是也.

937行,就是为us申请内存空间,而938行就是判断内存空间是否申请成功,成功的话us就不会为0,或者说为NULL,如果为NULL那么就是失败了,那么别再浪费表情了,整部戏就这么提前夭折了.在这里需要强调的是,整个内核代码中,像这种判断内存申请是否成功的语句是无处不在,每次有内存申请的语句,其后边一定会跟随一句判断申请成功与否的语句.写过代码的人都该知道,这样做是很有必要的,因为你没有申请到内存,那么继续下去就是没有意义的,除了可能让人见识计算机是如何崩溃之外,没有别的好处.而内存申请不管申请了多大,都有可能失败,写代码的人这样做无非是想告诫我们,我们的计算机并不总像人民币那般坚挺,她很脆弱.当你真正用计算机写代码的时候你就会发现计算机多么的脆弱和无力。

942行,给us初始化为全0.

934行这个US_DEBUGP,是一个宏,来自drivers/usb/storage/debug.h,接下来很多代码中我们也会看到这个宏,她无非就是打印一些调试信息.debug.h中有这么一段,

54 #ifdef CONFIG_USB_STORAGE_DEBUG
     55 void usb_stor_show_command(struct scsi_cmnd *srb);
     56 void usb_stor_show_sense( unsigned char key,
     57                 unsigned char asc, unsigned char ascq );
     58 #define US_DEBUGP(x...) printk( KERN_DEBUG USB_STORAGE x )
     59 #define US_DEBUGPX(x...) printk( x )
     60 #define US_DEBUG(x) x
     61 #else
     62 #define US_DEBUGP(x...)
     63 #define US_DEBUGPX(x...)
     64 #define US_DEBUG(x)
     65 #endif
     66
     67 #endif
这里一共定义了几个宏,US_DEBUGP,US_DEBUGPX,US_DEBUG,差别不大,只是形式上略有不同罢了.

USB驱动分析(三)
来源:  ChinaUnix博客  日期:2008.06.05 21:26 (共有条评论) 我要评论
 

需要注意的是,这些调试信息得是我们打开了编译选项CONFIG_USB_STORAGE_DEBUG才有意义的,这里也看出来了,如果这个选项为0,那么这几个宏就什么也不干,因为它们被赋为空了.关于US_DEBUG系列的这几个宏,就讲这么多,之后再碰上,将直接过滤掉,不予多说.
关于prink和kmalloc,这两个函数也没有太多需要说的,对大多数人来讲,就把printk当成printf,把kmalloc当成malloc即可,只不过是这两个函数是专门用于内核代码中的.一个是打印一些东西,一个是申请内存空间.
931行呢?id_index干嘛用的?让我们在下节不见不散吧.Be there or be square!-孙楠如是说.
storage_probe这个函数挺有意思的,总长度不足100行,但是干了许多事情,这就像足球场上的后腰,比如切尔西的马克莱莱,在场上并不起眼,但是却为整个团队做出了卓越的贡献.也有很多评论家说银河战舰皇家马德里这几年的衰落正是从赶走这个不起眼的马克莱莱开始的.
在讲id_index之前,我们继续贴storage_probe的代码:
    943         init_MUTEX(&(us->dev_semaphore));
    944         init_MUTEX_LOCKED(&(us->sema));
    945         init_completion(&(us->notify));
    946         init_waitqueue_head(&us->dev_reset_wait);
    947         init_waitqueue_head(&us->scsi_scan_wait);
    948         init_completion(&us->scsi_scan_done);
    949
    950         /* Associate the us_data structure with the USB device */
    951         result = associate_dev(us, intf);
    952         if (result)
    953                 goto BadDevice;
    954
    955         /*
    956          * Get the unusual_devs entries and the descriptors
    957          *
    958          * id_index is calculated in the declaration to be the index number
    959          * of the match from the usb_device_id table, so we can find the
    960          * corresponding entry in the private table.
    961          */
    962         get_device_info(us, id_index);
storage_probe这个函数之所以短小,是因为它调用了大量的函数.所以,看起来短短一段代码,实际上却要花费我们读代码的人好几个小时,想想真是觉得不划算,难怪朱自清先生看到这个storage_probe函数的时候,不禁感慨,燕子去了,有再来的时候,杨柳枯了,有再青的时候,桃花谢了,有再开的时候,但是,聪明的你告诉我,我们读这段不足100行的函数花掉的时间为何一去不复返呢?
其实我们不知道id_index也不影响对后面问题的理解,甚至某种意义上来说,花太多笔墨去讲这个id_index有一点喧宾夺主的感觉,但是,有时候,有些事情,你明知它是无奈的事,无奈的心情,却还要无奈的面对,无奈的去选择,有时想无奈的逃避都不可能,因为我们都被无奈禁锢了.比如这里,注意到962行出现了一个get_device_info的函数,它的一个参数就是id_index,所以,我们别无选择,只能看看这个id_index究竟是干嘛的.
上节我们注意到id_index=id-storage_usb_ids,id我们知道,storage_probe函数的两个形参之一,而storage_usb_ids,不是别人,正是我们曾经赋给usb_storage_driver的成员id_table的值.忘记了id_table的可以回去看.它实际上就是一张表格,告诉全世界我这个driver支持怎样的一些设备.storage_usb_ids同样来自drivers/usb/storage/usb.c中,
    111 /* The entries in this table, except for final ones here
    112  * (USB_MASS_STORAGE_CLASS and the empty entry), correspond,
    113  * line for line with the entries of us_unsuaul_dev_list[].
    114  */
    115
    116 #define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
    117                     vendorName, productName,useProtocol, useTransport, \
    118                     initFunction, flags) \
    119 { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin,bcdDeviceMax) }
    120
    121 static struct usb_device_id storage_usb_ids [] = {
    122
    123 #       include "unusual_devs.h"
    124 #undef UNUSUAL_DEV
    125         /* Control/Bulk transport for all SubClass values */
    126         { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_RBC, US_PR_CB) },
    127         { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8020, US_PR_CB) },
    128         { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_QIC, US_PR_CB) },
    129         { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_UFI, US_PR_CB) },
    130         { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8070, US_PR_CB) },
    131         { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_CB) },
    132
    133         /* Control/Bulk/Interrupt transport for all SubClass values */
    134         { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_RBC, US_PR_CBI) },
    135         { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8020, US_PR_CBI) },
    136         { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_QIC, US_PR_CBI) },
    137         { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_UFI, US_PR_CBI) },
    138         { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8070, US_PR_CBI) },
    139         { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_CBI) },
    140
    141         /* Bulk-only transport for all SubClass values */
    142         { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_RBC, US_PR_BULK) },
    143         { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8020, US_PR_BULK) },
    144         { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_QIC, US_PR_BULK) },
    145         { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_UFI, US_PR_BULK) },
    146         { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8070, US_PR_BULK) },
    147 #if !defined(CONFIG_BLK_DEV_UB) && !defined(CONFIG_BLK_DEV_UB_MODULE)
    148         { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_BULK) },
    149 #endif
    150
    151         /* Terminating entry */
    152         { }
    153 };
注意到这是一个struct usb_device_id结构体的数组,所以即使我们用下半身思考也能知道,其中每一项必然是一个struct usb_device_id的结构体变量.我们先来看USB_INTERFACE_INFO这个咚咚,很显然这是一个宏,来自include/linux/usb.h,
    473 /**
    474  * USB_INTERFACE_INFO - macro used to describe a class of usb interfaces
    475  * @cl: bInterfaceClass value
    476  * @sc: bInterfaceSubClass value
    477  * @pr: bInterfaceProtocol value
    478  *
    479  * This macro is used to create a struct usb_device_id that matches a
    480  * specific class of interfaces.
    481  */
    482 #define USB_INTERFACE_INFO(cl,sc,pr) \
    483         .match_flags = USB_DEVICE_ID_MATCH_INT_INFO, .bInterfaceClass = (cl), .bInterfaceSubClass = (sc), .bInterfaceProtocol = (pr)
每一个USB_INTERFACE_INFO就是构造一个struct usb_device_id的结构体变量,回顾一下我们之前给出的struct usb_device_id的定义,这里实际上就是为其中的四个元素赋了值,它们是match_flags,bInterfaceClass,bInterfaceSubClass,bInterfaceProtocol.这里不得不说的是,这个世界上有许许多多的usb设备,它们各有各的特点,为了区分它们,usb规范,或者说usb协议,把usb设备分成了很多类,然而每个类又分成子类,这很好理解,我们一个大学也是如此,先是分成很多个学院,比如我们复旦大学,信息学院,经济学院,管理学院,外文学院,等等.然后每个学院又被分为很多个系,比如信息学院,下面分了电子工程系,微电子系,计算机系,通信工程系,然后可能每个系下边又分了各个专业,usb协议也是这样干的,首先每个Interface属于一个Class,(为什么是把Interface分类,而不把Device分类?前面讲过了,在usb设备驱动中,不用再提Device,因为每个设备驱动对应的是一种Interface,而不是一种Device),然后Class下面又分了SubClass,完了SubClass下面又按各种设备所遵循的不同的通信协议继续细分.usb协议里边为每一种Class,每一种SubClass,每一种Protocol定义一个数值,比如mass storage的Class就是0x08,而这里USB_CLASS_MASS_STORAGE这个宏在include/linux/usb_ch9.h中定义,其值正是8.
我们拿第126行来举例,
  { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_RBC, US_PR_CB) },
  把这个宏展开,就是说定义了这么一个usb_device_id结构体变量,其match_flags=USB_DEVICE_ID_MATCH_INT_INFO,而bInterfaceClass=USB_CLASS_MASS_STORAGE,bInterfaceSubClass=US_SC_RBC,而bInterfaceProtocol=US_PR_CB.
USB_CLASS_MASS_STORAGE就不用再说了,咱们这个驱动程序所支持的每一种设备都是属于这个类,或者说这个Class.但是这个Class里边包含不同的SubClass,比如subclass 02为CD-ROM设备,04为软盘驱动器,06为通用SCSI类设备.而通信协议则主要有CBI协议和Bulk-Only协议.
像US_SC_RBC这些关于sub class的宏的定义是在文件drivers/usb/storage/protocol.h中:
     47 /* Sub Classes */
     48
     49 #define US_SC_RBC       0x01            /* Typically, flash devices */
     50 #define US_SC_8020      0x02            /* CD-ROM */
     51 #define US_SC_QIC       0x03            /* QIC-157 Tapes */
     52 #define US_SC_UFI       0x04            /* Floppy */
     53 #define US_SC_8070      0x05            /* Removable media */
     54 #define US_SC_SCSI      0x06            /* Transparent */
     55 #define US_SC_ISD200    0x07            /* ISD200 ATA */
而像US_PR_CB这些关于传输协议的宏则在另一个文件中,drivers/usb/storage/transport.h
  /* Protocols */
#define US_PR_CBI       0x00            /* Control/Bulk/Interrupt */
#define US_PR_CB        0x01            /* Control/Bulk w/o interrupt */
#define US_PR_BULK      0x50            /* bulk only */
这个文件中还定义了更多的协议,不过我们暂时只需要知道这三种,因为其她协议都是专门针对一些特殊设备的,在storage_usb_ids数组中使用宏USB_INTERFACE_INFO定义的usb_device_id都只是用的这三种协议.(US_PR_CBI和US_PR_CB这两种协议在usb协议中都唤作CBI,不过之间有点差别.)而对于一些特殊的设备,则还在unusual_devs.h文件中有专门的一些变量定义,我们暂且不去关注它们.
说了这许多,U盘属于其中的哪一种呢?usb协议中规定,U盘的Subclass是属于US_SC_SCSI的.而其通信协议使用的是Bulk-Only的.显然这些东西我们后来都会用得上.
那么这里还有一个match_flag,它又是表示什么意思?USB_INTERFACE_INFO这个宏貌似把所有的设备的match_flag都给设成了USB_DEVICE_ID_MATCH_INT_INFO,这是为啥?这个宏来自include/linux/usb.h,
    435 #define USB_DEVICE_ID_MATCH_INT_INFO \
    436         (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS | USB_DEVICE_ID_MATCH_INT_PROTOCOL)
match_flag这个咚咚是给usb core去用的,usb core负责给设备寻找适合她的driver,负责给driver寻找适合他的device,它所比较的就是struct usb_device_id的变量,而struct usb_device_id结构体中有许多成员,那么是不是一定要把每一个成员都给比较一下呢,其实没有必要那么细,差不多就行了,比如咱们这里,就是告诉usb core,你只要比较bInterfaceClass,bInterfaceSubClass,bInterfaceProtocol即可.include/linux/mod_devicetable.h中针对struct usb_device_id中的每一个要比较的项定义了一个宏:
    121 /* Some useful macros to use to create struct usb_device_id */
    122 #define USB_DEVICE_ID_MATCH_VENDOR              0x0001
    123 #define USB_DEVICE_ID_MATCH_PRODUCT             0x0002
    124 #define USB_DEVICE_ID_MATCH_DEV_LO              0x0004
    125 #define USB_DEVICE_ID_MATCH_DEV_HI              0x0008
    126 #define USB_DEVICE_ID_MATCH_DEV_CLASS           0x0010
    127 #define USB_DEVICE_ID_MATCH_DEV_SUBCLASS        0x0020
    128 #define USB_DEVICE_ID_MATCH_DEV_PROTOCOL        0x0040
    129 #define USB_DEVICE_ID_MATCH_INT_CLASS           0x0080
    130 #define USB_DEVICE_ID_MATCH_INT_SUBCLASS        0x0100
    131 #define USB_DEVICE_ID_MATCH_INT_PROTOCOL        0x0200
回去对比一下struct usb_device_id就知道这些宏是什么意思了.
然后我们再看storage_usb_ids中那个#include "unusual_devs.h",实际上这个文件也是在我们这个drivers/usb/storage/目录下面,它里边定义了一些特殊的设备,也是以struct usb_device_id结构体变量的形式,这些设备或者是有一些别的设备不具备的特性,或者是他们遵循的通信协议有些与众不同,比如,它既不是Bulk-Only也不是CBI,像这些不按常理出牌的设备,写代码的同志们把他们单独给列了出来.整理在这个文件中.当然,从大的类来分,它们依然是属于usb mass storage这个类别的,否则也没必要放在这个目录下面了.
至此,我们可以知道storage_usb_ids这个表是怎么回事了.usb core为每个设备在这张表里查找,如果找到了某一行和这个设备相匹配,那么该行就是我们前面提到的那个storage_probe()的参数id.所以id_index=id-storage_usb_ids就是如其字面意义那样,在表中的编号.至于这个编号有什么用,那我们骑驴看唱本.总之,费这么大劲干了这么一件事,总是有它的意义的.
最后,总结陈词,这个所谓的花名册,就好比我们大学生申请国外学校,每个人都会事先搜集一大批学校名单,然后结合各方面,比如师资力量,是否牛的导师够多,比如经济实力,是否能给够多的奖学金,比如教授本人资历,是否获得过重大奖项,取得过何种成绩,比如学校声望, 是否是名校,综合这些来看最终确定一份名单,就是自己真正心仪的学校花名册.那么那个match_flags是什么意思呢,而有的同学嫌这样太麻烦,又或者他们可能只是需要一个能发学历的,像杨澜老公吴征所获得博士学位的那个巴林顿大学那样卖假文凭的学校也许就能满足他们了,那么他们就不必去评估那么多项,反正他们心中的match_flags就是能够和吴征成为校友就可以了,管它是否因为该学校被取缔以后他们也会像吴征那样痛失母校呢.
罗马不是一天建成的.在让U盘工作之前,其实我们的驱动作了很多准备工作.
我们继续跟着感觉走,storage_probe(),943行至948行,一系列的以init_*命名的函数在此刻被调用,这里涉及了一些锁机制,等待机制,不过只是初始化,暂且不理睬,到后面用到的时候再细说,不过请记住,这几行每一行都是有用的.后面自然会用得着.
此时,我们先往下走,951行associate_dev()和962行get_device_info(),这两个函数是我们目前需要看的,一个一个来.
先看associate_dev(),定义于drivers/usb/storage/usb.c,
429 /* Associate our private data with the USB device */
    430 static int associate_dev(struct us_data *us, struct usb_interface *intf)
    431 {
    432         US_DEBUGP("-- %s\n", __FUNCTION__);
    433
    434         /* Fill in the device-related fields */
    435         us->pusb_dev = interface_to_usbdev(intf);
    436         us->pusb_intf = intf;
    437         us->ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
    438         US_DEBUGP("Vendor: 0x%04x, Product: 0x%04x, Revision: 0x%04x\n",
    439                         us->pusb_dev->descriptor.idVendor,
    440                         us->pusb_dev->descriptor.idProduct,
    441                         us->pusb_dev->descriptor.bcdDevice);
    442         US_DEBUGP("Interface Subclass: 0x%02x, Protocol: 0x%02x\n",
    443                         intf->cur_altsetting->desc.bInterfaceSubClass,
    444                         intf->cur_altsetting->desc.bInterfaceProtocol);
    445
    446         /* Store our private data in the interface */
    447         usb_set_intfdata(intf, us);
    448
    449         /* Allocate the device-related DMA-mapped buffers */
    450         us->cr = usb_buffer_alloc(us->pusb_dev, sizeof(*us->cr),
    451                         GFP_KERNEL, &us->cr_dma);
    452         if (!us->cr) {
    453                 US_DEBUGP("usb_ctrlrequest allocation failed\n");
    454                 return -ENOMEM;
    455         }
    456
    457         us->iobuf = usb_buffer_alloc(us->pusb_dev, US_IOBUF_SIZE,
    458                         GFP_KERNEL, &us->iobuf_dma);
    459         if (!us->iobuf) {
    460                 US_DEBUGP("I/O buffer allocation failed\n");
    461                 return -ENOMEM;
    462         }
    463         return 0;
    464 }
  我们首先来关注函数associate_dev的参数, struct us_data *us,传递给它的是us,这个不用多说了吧,此前刚刚为它申请了内存,并且初始化各成员为0. 这个us将一直陪伴我们走下去,直到我们的故事结束.所以其重要性不言而喻. struct usb_interface *intf,这个也不用说,storage_probe()函数传进来的两个参数之一.总之,此处郑重申明一次,struct us_data的结构体指针us,struct usb_interface结构体的指针intf,以及struct usb_device结构体和struct usb_device_id结构体在整个U盘驱动的故事中是唯一的,每次提到都是那个. 而以后我们会遇上的几个重要的数据结构,struct urb urb,struct scsi_cmnd srb这也非常重要,但是它们并不唯一,也许每次遇上都不一样,就像演戏一样.前边这几个数据结构的变量就像那些主角,而之后遇见的urb啊,srb啊,虽然频繁露面,但是只是群众演员,只不过这次是路人甲,下次是路人乙. 所以,以后我们将只说us,不再说struct us_data *us,struct usb_interface * intf也将只用intf来代替.
us之所以重要,是因为接下来很多函数都要用到它以及它的各个成员.实际上目前这个函数,associate_dev所做的事情就是为us的各成员赋值,毕竟此刻us和我们之前提到的那些struct usb_device啊,struct usb_interface啊,还没有一点关系.因而,这个函数,以及这之后的好几个函数都是为了给us的各成员赋上适当的值,之所以如此兴师动众去为它赋值,主要就是因为后面要利用它.所谓天下没有免费的午餐.
432行,本来无须多讲,因为只是一个debug语句,不过提一下__FUNCTION__这个"宏",gcc 2.95以后的版本支持这么一个冬冬,这个"宏"在编译的时候会被转换为函数名(字符串),这里自然就是"associate_dev"这么一个字符串,于是函数执行到这里就会打印一句话告诉世人我们执行到这个函数来了,这种做法显然会有利于咱们调试程序.不过这个冬冬实际上不是宏,因为预处理器对她一无所知.她的心只有编译器才懂.
435行,pusb_dev,就是point of usb device的意思.struct us_data中的一个成员,按照我们刚才约定的规矩,此刻我将说us的一个成员,us->pusb_dev= interface_to_usbdev(intf), interface_to_usbdev我们前面已经讲过,其含义正如字面表示的那样,把一个struct interface结构体的指针转换成一个struct usb_device的结构体指针.前面我们说过,struct usb_device对我们没有什么用,但是usb core层的一些函数要求使用这个参数,所以我们不得已而为止,正所谓人在江湖身不由己.
436行,把intf赋给us的pusb_intf.
437行,us的ifnum, 先看intf的cur_altsetting,这个容易令外行混淆.usb设备有一个configuration的概念,这个我们前面讲协议的时候说了,而这里又有一个setting,咋一看有些奇怪,这两个词不是一回事吗.这时候,就体现出外语水平了,上过新东方没上过新东方,背没背过俞敏洪的GRE红宝书,在这时候就体现出差距了.还是拿我们最熟悉的手机来打比方,configuration不说了,setting,一个手机可能各种配置都确定了,是振动还是铃声已经确定了,各种功能都确定了,但是声音的大小还可以变吧,通常手机的音量是一格一格的变动,大概也就5,6格,那么这个可以算一个setting吧.这里cur_altsetting就是表示的当前的这个setting,或者说设置.cur_altsetting是一个struct usb_host_interface的指针,这个结构体定义于include/linux/usb.h:
51 /* host-side wrapper for one interface setting's parsed descriptors */
     52 struct usb_host_interface {
     53         struct usb_interface_descriptor desc;
     54
     55         /* array of desc.bNumEndpoint endpoints associated with this
     56          * interface setting.  these will be in no particular order.
     57          */
     58         struct usb_host_endpoint *endpoint;
     59
     60         unsigned char *extra;   /* Extra descriptors */
     61         int extralen;
     62 };
它的成员desc是一个struct usb_interface_descriptor结构体变量,这个结构体的定义是和usb协议直接对应的,定义于include/linux/usb_ch9.h.(这里取名为"ch9"是因为这个文件很多东西对应于usb spec 2.0中的第九章,chapter 9.):
    242 /* USB_DT_INTERFACE: Interface descriptor */
    243 struct usb_interface_descriptor {
    244         __u8  bLength;
    245         __u8  bDescriptorType;
    246
    247         __u8  bInterfaceNumber;
    248         __u8  bAlternateSetting;
    249         __u8  bNumEndpoints;
    250         __u8  bInterfaceClass;
    251         __u8  bInterfaceSubClass;
    252         __u8  bInterfaceProtocol;
    253         __u8  iInterface;
    254 } __attribute__ ((packed));
而其中我们这里提到的是bInterfaceNumber,一个设备可以有多个Interface,于是每一个Interface当然就得用个编号了,要不然咋区分啊?所有这些描述符里的冬冬都是出厂的时候就固化在设备里边的,而我们这里之所以可以用bInterfaceNumber来赋值,是因为usbcore在为设备初始化的时候就已经做足了这些功课.否则的话,我们真是寸步难行.
总之,us->ifnum就是这样,最终就是等于咱们眼下这个interface的编号.
438到444行就是两句调试语句,打印更多一些描述符信息,包括device描述符和interface描述符.
447行, usb_set_intfdata(),这其实是一个内联函数,她就一行代码,也是定义于include/linux/usb.h中:
    138 static inline void usb_set_intfdata (struct usb_interface *intf, void *data)
    139 {
    140         dev_set_drvdata(&intf->dev, data);
    141 }
  有趣的是,dev_set_drvdata这个函数也是内联函数,也只有一行代码,她定义于include/linux/device.h中:
302 static inline void
    303 dev_set_drvdata (struct device *dev, void *data)
    304 {
    305         dev->driver_data = data;
    306 }
  所以,结合来看,最终做的事情就是让&intf->dev->driver_data=data,即&intf->dev->driver_data=us.
再往下走,就是申请内存了,us->cr和us->iobuf都是指针,这里让它们指向两段内存空间,下面会用得到.需要注意的是usb_buffer_alloc(),这个函数是usbcore提供的,我们只管调用即可.从名字上就能知道它是用来申请内存的,第一个参数就是struct usb_device结构体的指针,所以我们要传递一个pusb_dev,第三个参数,GFP_KERNEL,是一个内存申请的flag,通常内存申请都用这个flag,除非是中断上下文,不能睡眠,那就得用GPF_ATOMIC,这里没那么多要求.第二个参数申请的buffer的大小,对于cr,传递的是sizeof(*us->cr),而对于iobuf,传递的是US_IOBUF_SIZE,这是一个宏,大小是64,是我们自己定义的,来自drivers/usb/storage/usb.h:
     91 /*
     92  * We provide a DMA-mapped I/O buffer for use with small USB transfers.
     93  * It turns out that CB[I] needs a 12-byte buffer and Bulk-only needs a
     94  * 31-byte buffer.  But Freecom needs a 64-byte buffer, so that's the
     95  * size we'll allocate.
     96  */
     97
     98 #define US_IOBUF_SIZE           64      /* Size of the DMA-mapped I/O buffer */
而usb_buffer_alloc()的第四个参数有些意思了,第一次我们传递的是&us->cr_dma,第二次传递的是&us->iobuf_dma,这涉及到dma传输.这两个咚咚此前我们都没有赋过值,相反它们是在这个函数调用之后被赋上值的.cr_dma和iobuf_dma都是dma_addr_t类型的变量,这个数据类型是Linux内核中专门为dma传输而准备的.为了支持dma传输,usb_buffer_alloc不仅仅是申请了地址,并且建立了dma映射,cr_dma和iobuf_dma就是记录着cr和iobuf的dma地址.关于什么是cr,关于这些dma地址究竟有什么用,我们稍候就会遇到,那时候再讲也不迟.现在需要知道的就是usb_buffer_alloc申请的空间分别返回给了cr和iobuf.顺便提一下,用usb_buffer_alloc申请的内存空间需要用它的搭档usb_buffer_free()来释放.
452行和459行,每一次申请完内存就要检查成功与否,这是惯例.驱动程序能否驱动设备,关键就是看能否申请到内存空间,任何一处内存空间申请失败,整个驱动程序就没法正常工作.这就像如今找对象,谈婚姻,总是要看有没有房子.没有房子的话,那么基本上爱情也就没戏.然而现实中要拥有房子比计算机里分配内存却要难上许多,许多.可怜的我们这一代人,当我们不能挣钱的时候,房子是分配的,当我们能挣钱的时候,却发现房子已经买不起了.哭…
整个usb-storage模块的代码中,其最灵魂的部分在一个叫做usb_stor_control_thread()的函数中,而那也自然是我们整个故事的高潮.这个函数的调用有些特殊,我们是从usb_stor_acquire_resources()函数进入的,而后者我们即将遇到,它在整部戏中只出现过一次,即storage_probe中,行号为998的地方.然而在此之前,有四个函数挡在我们面前,它们就是get_device_info,get_transport,get_protocol,get_pipes.如我前面所说,两个人要走到一起,首先要了解彼此,这四个函数就是让driver去认识device的.这一点我们从名字上也能看出来.driver需要知道device姓甚名谁,所以有了get_device_info,driver需要知道device喜欢用什么方式沟通,是用QQ还是用msn还是只用手机短信,如果是某一种,那么账号是多少,或者手机号是多少,写代码的人把这些工作分配给了get_transport,get_protocol,get_pipes.
实际上,这四个函数,加上之前刚说过的那个associate_dev(),是整个故事中最平淡最枯燥的部分,第一次读这部分代码总让人困惑,怎么没看见一点usb数据通信啊?完全没有看到usb host和usb device是如何在交流的,这是usb吗?这一刻,这颗浮躁的心,在这个城市,迷失了.但是,我们知道,爱情,并非都与风花雪月有关,友情,并非总与疯斗打闹有关.这几个函数应该说是给后面做铺垫,红花总要有绿叶配,没有这段代码的铺垫,到了后面usb设备恐怕也无法正常工作吧.不过,一个利好消息是,这几个函数我们只会遇见这一次,它们在整个故事中就这么一次露脸的机会,像我们每个人的青春,只有一次,无法回头.和我们每个人的青春一样,都是绝版的.所以,让我们享受这段平淡无奇的代码吧.
get_device_info, 这个函数定义于drivers/usb/storage/usb.c中:
466 /* Get the unusual_devs entries and the string descriptors */
    467 static void get_device_info(struct us_data *us, int id_index)
    468 {
    469         struct usb_device *dev = us->pusb_dev;
    470         struct usb_interface_descriptor *idesc =
    471                 &us->pusb_intf->cur_altsetting->desc;
    472         struct us_unusual_dev *unusual_dev = &us_unusual_dev_list[id_index];
    473         struct usb_device_id *id = &storage_usb_ids[id_index];
    474
    475         /* Store the entries */
    476         us->unusual_dev = unusual_dev;
    477         us->subclass = (unusual_dev->useProtocol == US_SC_DEVICE) ?
    478                         idesc->bInterfaceSubClass :
    479                         unusual_dev->useProtocol;
    480         us->protocol = (unusual_dev->useTransport == US_PR_DEVICE) ?
    481                         idesc->bInterfaceProtocol :
    482                         unusual_dev->useTransport;
    483         us->flags = unusual_dev->flags;
    484
    485         /* Log a message if a non-generic unusual_dev entry contains an
    486          * unnecessary subclass or protocol override.  This may stimulate
    487          * reports from users that will help us remove unneeded entries
    488          * from the unusual_devs.h table.
    489          */
    490         if (id->idVendor || id->idProduct) {
    491                 static char *msgs[3] = {
    492                         "an unneeded SubClass entry",
    493                         "an unneeded Protocol entry",
    494                         "unneeded SubClass and Protocol entries"};
    495                 struct usb_device_descriptor *ddesc = &dev->descriptor;
    496                 int msg = -1;
    497
    498                 if (unusual_dev->useProtocol != US_SC_DEVICE &&
    499                         us->subclass == idesc->bInterfaceSubClass)
    500                         msg += 1;
    501                 if (unusual_dev->useTransport != US_PR_DEVICE &&
    502                         us->protocol == idesc->bInterfaceProtocol)
    503                         msg += 2;
    504                 if (msg >= 0 && !(unusual_dev->flags & US_FL_NEED_OVERRIDE))
    505                         printk(KERN_NOTICE USB_STORAGE "This device "
    506                                 "(%04x,%04x,%04x S %02x P %02x)"
    507                                 " has %s in unusual_devs.h\n"
    508                                 "   Please send a copy of this message to "
    509                                 "\n]linux-usb-devel@lists.sourceforge.net>\n[/email]
",
    510                                 ddesc->idVendor, ddesc->idProduct,
    511                                 ddesc->bcdDevice,
    512                                 idesc->bInterfaceSubClass,
    513                                 idesc->bInterfaceProtocol,
    514                                 msgs[msg]);
    515         }
    516
    517         /* Read the device's string descriptors */
    518         if (dev->descriptor.iManufacturer)
    519                 usb_string(dev, dev->descriptor.iManufacturer,
    520                            us->vendor, sizeof(us->vendor));
    521         if (dev->descriptor.iProduct)
    522                 usb_string(dev, dev->descriptor.iProduct,
    523                            us->product, sizeof(us->product));
    524         if (dev->descriptor.iSerialNumber)
    525                 usb_string(dev, dev->descriptor.iSerialNumber,
    526                            us->serial, sizeof(us->serial));
    527
    528         /* Use the unusual_dev strings if the device didn't provide them */
    529         if (strlen(us->vendor) == 0) {
    530                 if (unusual_dev->vendorName)
    531                         strlcpy(us->vendor, unusual_dev->vendorName,
    532                                 sizeof(us->vendor));
    533                 else
    534                         strcpy(us->vendor, "Unknown");
    535         }
    536         if (strlen(us->product) == 0) {
    537                 if (unusual_dev->productName)
    538                         strlcpy(us->product, unusual_dev->productName,
    539                                 sizeof(us->product));
    540                 else
    541                         strcpy(us->product, "Unknown");
    542         }
    543         if (strlen(us->serial) == 0)
    544                 strcpy(us->serial, "None");
    545
    546         US_DEBUGP("Vendor: %s,  Product: %s\n", us->vendor, us->product);
    547 }
469行,不多说,dev还是那个dev.
470行,struct usb_interface_descriptor *idesc,这个也无须再说,之前那个associate_dev函数里已经介绍过这个结构体,而且整个故事就是针对一个interface的,一个interface就对应一个interface描述符.所以不管在哪里看到,她总还是她.
472行, struct us_unusual_dev,这个结构体是第一次出现,她定义于drivers/usb/storage/usb.h中,
55 /*
     56  * Unusual device list definitions
     57  */
     58
     59 struct us_unusual_dev {
     60         const char* vendorName;
     61         const char* productName;
     62         __u8  useProtocol;
     63         __u8  useTransport;
     64         int (*initFunction)(struct us_data *);
     65         unsigned int flags;
     66 };
而等号右边的us_unusal_dev_list是一个数组,定义于drivers/usb/storage/usb.c:
    157 /* This is the list of devices we recognize, along with their flag data */
    158
    159 /* The vendor name should be kept at eight characters or less, and
    160  * the product name should be kept at 16 characters or less. If a device
    161  * has the US_FL_FIX_INQUIRY flag, then the vendor and product names
    162  * normally generated by a device thorugh the INQUIRY response will be
    163  * taken from this list, and this is the reason for the above size
    164  * restriction. However, if the flag is not present, then you
    165  * are free to use as many characters as you like.
    166  */
    167
    168 #undef UNUSUAL_DEV
    169 #define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \
    170                     vendor_name, product_name, use_protocol, use_transport, \
    171                     init_function, Flags) \
    172 { \
    173         .vendorName = vendor_name,      \
    174         .productName = product_name,    \
    175         .useProtocol = use_protocol,    \
    176         .useTransport = use_transport,  \
    177         .initFunction = init_function,  \
    178         .flags = Flags, \
    179 }
    180
    181 static struct us_unusual_dev us_unusual_dev_list[] = {
    182 #       include "unusual_devs.h"
    183 #       undef UNUSUAL_DEV
    184         /* Control/Bulk transport for all SubClass values */
    185         { .useProtocol = US_SC_RBC,
    186           .useTransport = US_PR_CB},
    187         { .useProtocol = US_SC_8020,
    188           .useTransport = US_PR_CB},
    189         { .useProtocol = US_SC_QIC,
    190           .useTransport = US_PR_CB},
    191         { .useProtocol = US_SC_UFI,
    192           .useTransport = US_PR_CB},
    193         { .useProtocol = US_SC_8070,
    194           .useTransport = US_PR_CB},
    195         { .useProtocol = US_SC_SCSI,
    196           .useTransport = US_PR_CB},
    197
    198         /* Control/Bulk/Interrupt transport for all SubClass values */
    199         { .useProtocol = US_SC_RBC,
    200           .useTransport = US_PR_CBI},
    201         { .useProtocol = US_SC_8020,
    202           .useTransport = US_PR_CBI},
    203         { .useProtocol = US_SC_QIC,
    204           .useTransport = US_PR_CBI},
    205         { .useProtocol = US_SC_UFI,
    206           .useTransport = US_PR_CBI},
    207         { .useProtocol = US_SC_8070,
    208           .useTransport = US_PR_CBI},
    209         { .useProtocol = US_SC_SCSI,
    210           .useTransport = US_PR_CBI},
    211
    212         /* Bulk-only transport for all SubClass values */
    213         { .useProtocol = US_SC_RBC,
    214           .useTransport = US_PR_BULK},
    215         { .useProtocol = US_SC_8020,
    216           .useTransport = US_PR_BULK},
    217         { .useProtocol = US_SC_QIC,
    218           .useTransport = US_PR_BULK},
    219         { .useProtocol = US_SC_UFI,
    220           .useTransport = US_PR_BULK},
    221         { .useProtocol = US_SC_8070,
    222           .useTransport = US_PR_BULK},
    223 #if !defined(CONFIG_BLK_DEV_UB) && !defined(CONFIG_BLK_DEV_UB_MODULE)
    224         { .useProtocol = US_SC_SCSI,
    225           .useTransport = US_PR_BULK},
    226 #endif
    227
    228         /* Terminating entry */
    229         { NULL }
    230 };
无可奈何花落去,似曾相识燕归来.这个数组看上去特别面熟对不对?可曾记得当初我们见过的那个storage_usb_ids,仔细对比一下会发现,这两个数组的元素个数完全一样,只不过,一个由是struct usb_device_id结构体构成的,另一个则是struct us_unusual_dev结构体构成的,其实这两个表格是一一对应的,它们也都定义于同一个文件中. 细心一点会注意到,UNUSUAL_DEV这个宏在这个文件里被定义了两次,这是干嘛用的?听仔细了,我们曾经提过unusual_devs.h这个文件,这个文件的作用是什么?假设没有这个文件,那么storage_usb_ids这张表就是用USB_INTERFACE_INFO这个宏定义了几种常规的usb mass storage的设备,比如,
{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_RBC, US_PR_CB) },
这一行就是定义一个设备的bInterfaceClass,bInterfaceSubClass,bInterfaceProtocol,也就是说一个usb interface只要其这三个项特点与这张表里定义的任何一行匹配,那么就可以把这个设备和这个驱动绑定,但是这三个条件毕竟只是规定了一些主流的或者说常规的设备的特点,这个世界上usb mass storage设备有许多种,林子大了,什么鸟都有.如果你的设备不符合这三个条件但是你仍然要用这个驱动怎么办?或者说你的设备不一般,在正式工作之前需要进行一些初始化的操作,你想自己提供一个初始化函数,那怎么办?伟大的Linux内核开发者们为你准备了一个文件,它就是让诸多usb mass storage设备厂家欢欣鼓舞的unusual_devs.h,有了它,厂家们不用再为自己的设备不被Linux内核支持而烦恼了.虽然我们的U盘基本上不属于这些另类设备,但作为一个有责任心的社会主义新青年,我们有必要为这个文件说两句.
我们打开unusual_devs.h吧,随便看一下,发现里边就是很多一个个UNUSUAL_DEV宏,每一行就是这么一个宏,毫无疑问它对应一种设备,我们从其中挑一个来看,比如挑一个三星的吧,过去在Intel的时候,前辈们会说,若不是当初我们对自己太自信了,这个世界上又怎么有三星的生存空间.说的是上世纪末,Intel决定提高flash产品的价格,而Nokia这个大客户不干了,它想找别人,一个叫三星的小公司鬼魅般的出现了,没有人相信这样一个小公司能够满足Nokia,可是,韩国人,韩国人的韧劲不仅仅是体现在足球场上.于是,世界上有了三星,Nokia养活了三星,而Intel,在flash这一领域失去了一个重要的客户,副CEO也为此引咎辞职了.而下面这个设备,正是来自三星的一个flash产品.
    711 /* Submitted by Hartmut Wahl */]hwahl@hwahl.de>*/[/email]
    712 UNUSUAL_DEV( 0x0839, 0x000a, 0x0001, 0x0001,
    713                 "Samsung",
    714                 "Digimax 410",
    715                 US_SC_DEVICE, US_PR_DEVICE, NULL,
716                 US_FL_FIX_INQUIRY),
Digimax 410,熟悉数码相机的哥们儿大概对三星的这款410万像素3倍光学变焦的产品不会陌生,不过数码相机更新得很快,这款2002年推出的相机如今当然也算是很一般了,市场上卖的话也就1500以下,不过当时刚推出的时候也是3000到4000元的.ok,我们来看这一行是什么意思.
UNUSUAL_DEV这个宏被定义过两次,当然,#define了之后再下一次#define之前有一个#undef,否则就重复定义了.在storage_usb_ids之前,它的定义是
    116 #define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
    117                     vendorName, productName,useProtocol, useTransport, \
    118                     initFunction, flags) \
    119 { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin,bcdDeviceMax) }
USB_DEVICE_VER的定义在include/linux/usb.h中,
    448 /**
    449  * USB_DEVICE_VER - macro used to describe a specific usb device with a version range
    450  * @vend: the 16 bit USB Vendor ID
    451  * @prod: the 16 bit USB Product ID
    452  * @lo: the bcdDevice_lo value
    453  * @hi: the bcdDevice_hi value
    454  *
    455  * This macro is used to create a struct usb_device_id that matches a
    456  * specific device, with a version range.
    457  */
    458 #define USB_DEVICE_VER(vend,prod,lo,hi) \
    459         .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, .idVendor = (vend), .idProduct = (prod), .bcdDevice_lo         = (lo), .bcdDevice_hi = (hi)
所以这行最终出现在storage_usb_ids中的意思就是令match_flags为 USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,idVendor为0x0839,idProduct为0x000a,bcdDevice_lo为0x0001,bcdDevice_hi为0x0001.
而在us_unusal_dev_list这张表之前,UNUSUAL_DEV又被定义为:
    168 #undef UNUSUAL_DEV
    169 #define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \
    170                     vendor_name, product_name, use_protocol, use_transport, \
    171                     init_function, Flags) \
    172 { \
    173         .vendorName = vendor_name,      \
    174         .productName = product_name,    \
    175         .useProtocol = use_protocol,    \
    176         .useTransport = use_transport,  \
    177         .initFunction = init_function,  \
    178         .flags = Flags, \
    179 }
Ok.这样这个宏的意思又是令vendorName为"Samsung",令productName为"Digimax 410",而useProtocol为US_SC_DEVICE, useTransport为US_PR_DEVICE,initFunction为NULL,flag为US_FL_FIX_INQUIRY.
看明白了吗?首先不去管各项的具体含义,至少我们看出来,针对同一个文件,我们使用两次定义UNUSUAL_DEV这个宏的方法,两次利用了它的不同元素,换言之,UNUSUAL_DEV这个宏本来可以设定10个参数,而storage_usb_ids中需要使用其中的前4个参数,同时us_unusual_dev_list中需要使用其中的后6个参数,所以在unusual_devs.h中定义的一行起了两个作用.我们注意到不管是storage_usb_ids数组还是us_unusual_dev_list,其中都通过这么一行把unusual_devs.h文件给包含了进来.storage_usb_ids中:
    121 static struct usb_device_id storage_usb_ids [] = {
    122
    123 #       include "unusual_devs.h"
    124 #undef UNUSUAL_DEV
us_unusual_dev_list中:
    181 static struct us_unusual_dev us_unusual_dev_list[] = {
    182 #       include "unusual_devs.h"
    183 #       undef UNUSUAL_DEV
而我们之所以使用两个数组的原因是,storage_usb_ids是提供给usb core的,它需要比较driver和device从而确定设备是被这个driver所支持的,我们只需要比较四项就可以了,因为这四项已经足以确定一个设备了,厂商,产品,序列号.比较这些就够了,而us_unusual_dev_list这个数组中的元素是我们接下来的代码要用的,比如它用什么协议,它有什么初始化函数,所以我们使用了两个数组.而我们需要注意的是,这两个数组中元素的顺序是一样的,所以我们从storage_usb_ids中得到的id_index用于us_unusual_dev_list也是可以的,表示的还是同一个设备.而这也就是我们刚才在get_device_info中看到的.
472         struct us_unusual_dev *unusual_dev = &us_unusual_dev_list[id_index];
    473         struct usb_device_id *id = &storage_usb_ids[id_index];
这样,unusual_dev和id就各取所需了.下面我们将会用到这两个指针.暂且不表.
总结陈词,最后具体解释一下这行为三星这款数码相机写的语句,
1.               关于match_flags,它的值是USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,这是一个宏,它就告诉usb core,要比较这样几个方面,idVendor,idProduct,bcdDevice_lo,bcdDevice_hi,其中idVendor和下面的vendorName是对应的,而idProduct和下面的productName是对应的,业内为每家公司编一个号,这样便于管理,比如三星的编号就是0x0839,那么三星的产品中就会在其设备描述符中idVendor的烙上0x0839.而三星自己的每种产品也会有个编号,和Digimax 410对应的编号就是0x000a,而bcdDevice_lo和bcdDevice_hi共同组成一个具体设备的编号(device release number),bcd就意味着这个编号是二进制的格式.
2.               vendorName和productName不用再说了, "Samsung"和"Digimax 410".
3.               useProtocol为US_SC_DEVICE, useTransport为US_PR_DEVICE,这种情况就说明对于这种设备,它属于什么subclass,它使用什么通信协议,得从设备描述符里边去读取,它都写在那里边了.一会我们会看到我们的代码中会如何判断这个的.
4.               initFunction等于NULL,这个很有意义的,这个函数就是设备的初始化函数,一般的设备都不需要这个函数,但是有些设备它偏要标新立异,它就告诉你,要用我的设备你必须先做一些初始化,于是它提供了一个函数,initFunction当然是一个函数指针,这里如果不为NULL的话,到时候就会被调用,以后我们会看到代码中对这个指针进行了判断.如果为空不理睬,否则就会执行.比如我们看下面两处,惠普的两个设备,它就提供了一个叫做init_8200e的初始化函数,
     63 UNUSUAL_DEV(  0x03f0, 0x0207, 0x0001, 0x0001,
     64                 "HP",
     65                 "CD-Writer+ 8200e",
     66                 US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0),
     67
     68 UNUSUAL_DEV(  0x03f0, 0x0307, 0x0001, 0x0001,
     69                 "HP",
     70                 "CD-Writer+ CD-4e",
     71                 US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0),
5.               flag等于US_FL_FIX_INQUIRY,这个flag可以设为很多值,这个flag的存在本身就表示这个设备有些与众不同,因为一般的设备是不用这个flag的,有这个flag就表明这个设备可能在某些地方需要进行特殊的处理,所以今后在代码中我们会看到突然跳出一句,判断us->flag等于某个咚咚不,如果等于,就执行一些代码,如果不等于,那就不做任何事情.这个flag的存在也使得我们可以方便处理一些设备的bug,比如正常的设备你问它吃了吗?它就回答吃了.可是不正常的设备可能就会根本不回答,或者回答北京房价真贵!于是对于这种设备,可能我们就需要一些专门的代码来对付.具体到这个US_FL_FIX_INQUIRY,这个flag这么一设置,就表明这个设备在接受到INQUIRY命令的时候会有一些异常的特征,所以以后我们会在代码里看到我们是如何处理它的.设置了这个flag的当然不只是三星的这款相机,别的设备也有可能设置的.
6.               既然明白了unusual_devs.h的作用,那么很显然的一个事情,如果一个厂家推出了一个新的设备,它有一些新的特征,而目前的设备驱动不足以完全支持它,那么厂家首先需要做的事情就是在unusual_devs.h中添加一个UNUSUAL_DEV来定义自己的设备,然后再看是否需要给内核打补丁以及如何打.因此这几年unusual_devs.h这个文件的长度也是慢慢在增长。

从两张表得到了我们需要的冬冬,然后下面的代码就是围绕着这两个指针来展开了.(unusual_dev和id)
476行,把unusual_dev给记录在us里边,反正us里边也有这么一个成员.这样记录下来日后要用起来就方便了,因为us是贯穿整个故事的,所以访问他的成员很方便,随时都可以,但是us_unusual_dev_list以及storage_usb_ids这两张表这次之后就不会再用了.因为我们已经得到了我们想要的,所以我们就不用再去骚扰这两个数组了.
477至483行,给us的另外三个成员赋值,subclass,protocol,flags.比如我们的U盘,它属于主流设备,在us_unusual_dev_list列表中能找到它,其subclass是US_SC_SCSI,而protocol是Bulk-only,即这里用宏US_PR_BULK所代表的.(224行和225行.)关于US_SC_DEVICE和US_PR_DEVICE我们之前讲那个三星的数码相机的时候已经看到了,它就表示subclass和protocol得从设备的描述符里边读出来.这样做看起来很滑稽,因为三星完全可以把subclass和protocol在UNUSUAL_DEV中写清楚,何必让我们再去读设备描述符呢.然而,我们可以想象,它这样的好处是定义一个UNUSUAL_DEV可以代表几种设备,即它可以让几个不同subclass的设备共用这么一个宏,或者几个不同protocol的设备共用这么一个宏.能省一点就省一点吧,这里体现了开源社区人们勤俭节约的高尚品德. 需要特别指出的是us->flags,对于U盘来说,它当然没有什么flags需要设定,但是unusual_devs.h中的很多设备都设置了各种flags,稍后在代码中我们会看到,时不时我们就得判断一下是否某个flag设置了,通常是如果设置了,就要多执行某段代码,以满足某种要求.
490至515行,这是一段纯粹的调试代码,对我们理解usb没有任何意义的.这段代码检查unusual_devs.h,看是否这个文件定义了一行没有意义的句子.什么叫没有意义?我们刚才看见了,如果这个设备设了US_SC_DEVICE,那么其subclass将从描述符中读出来,如果不然,则让subclass=unusual_dev->useProtocol,但是如果后者又真的和描述符里读出来的一样,那么这个设备就没有必要把自己的useProtocol定义在unusual_devs.h中了,因为反正也可以从描述符里读出来.还不如和大众一样设为US_SC_DEVICE得了.就比如我们来看下面这行代表一个Sony的Memory Stick产品的代码:
    371 UNUSUAL_DEV(  0x054c, 0x0069, 0x0000, 0x9999,
    372                 "Sony",
    373                 "Memorystick MSC-U03",
    374                 US_SC_UFI, US_PR_CB, NULL,
    375                 US_FL_SINGLE_LUN ),
我们看到其useProtocol这一栏里写了US_SC_UFI,这表明它自称是属于UFI这个subclass的,但是如果我们从它的描述符里边读出来也是这个,那就没有必要注明在这里了,这里直接写成US_SC_DEVICE好了.当然,总的来说这段代码有些傻.写代码的是希望能够更好的管理unusual_devs.h,希望它不要不断的增加,他总希望能够从这个文件里删除一些行,并且即使不能删除一行,也希望每一行都看上去整齐一点,让这个文件看上去更加小巧玲珑,更加精致.而不是无休的增加,不息的扩充.于是我们也只能对其良苦用心多一份理解吧.
518至526行,这里调用了一个来自usb core的函数,usb_string,这个函数的作用是获得一个设备的字符串描述符.咦?怎么跳出来一个叫做字符串描述符的冬冬?之前不是只讲了四种描述符吗?没错,设备描述符,配置描述符,接口描述符,端点描述符,这是每个设备都有的,而还有些描述符是可有可无的,字符串描述符就是这么一种情况,有的设备有,有的设备没有.又比如,hub,它就有一个hub描述符,这当然是一般的设备没有的.那么字符串描述符是干嘛的呢?有些东西模糊一些也未偿不是一件好事,看得太透彻了才知道很残酷.如果你一定要知道的话,举个例子,我的机器里很多usb设备,有一个和lspci类似的命令,可以查看一下,这个命令就是lsusb.你也可以试一下,安装一个软件包usbutils,然后就可以使用这个命令.我们看:
localhost:~/test # lsusb
Bus 004 Device 003: ID 0ea0:1001 Ours Technology, Inc.
Bus 004 Device 002: ID 04b4:6560 Cypress Semiconductor Corp. CY7C65640 USB-2.0 "TetraHub"
Bus 004 Device 001: ID 0000:0000
Bus 003 Device 001: ID 0000:0000
Bus 002 Device 002: ID 0624:0294 Avocent Corp.
Bus 002 Device 001: ID 0000:0000
Bus 001 Device 001: ID 0000:0000
看这个第二行, Cypress Semiconductor Corp., 这么一长串的东西,你说哪来的?是不是应该从设备里来?设备的那几个标准描述符,整个描述符的大小也不一定放得下这么一长串,所以,一些设备专门准备了一些字符串描述符(string descriptor).就用来记这些长串的东西,我们结合刚才的518行开始讲,如果设备描述符里边iManufacturer不为0,那么调用usb_string,这句话具体做了什么?就是根据iManufactuer的值得到公司名字,而iManufactuer的第一个字母i,就表示index,它记录的是真正的公司名字保存在哪一个字符串描述符中,因为字符串描述符可以有多个,那么必然就有个号码来区分,接下来几行,iProduct记录了产品名在第几个字符串描述符中,iSerialNumber记录了产品序列号在第几个字符串描述中,然后调用usb_string这个函数,就把真正的字符串描述符里的冬冬给记录了下来.我们看到,我们三次调用的时候分别传递了us->vendor,us->product,us->serial.这样函数调用结束之后,这三个里面就记录了必要的信息,于是以后我们就可以用了.
得到了us->vendor,us->product,us->serial,那么下面528直到547行就不需要多讲了,就是说如果得到的东西是空的,(得到的是空可以有两种可能,一个是设备根本就没提供这些字符串描述符,另一种情况是usb_string函数没能成功,但是这个函数不成功也无所谓,没影响.)那也没关系,毕竟这些信息我们可有可无,无非是打印出来给客户看看.如果unusual_dev里边有的话,那就拷贝过来,如果也没有,那没办法,设为Unknown.而序列号这个就索性置为None好了,最后US_DEBUGP把这些信息给打印出来,如果你打开了debug开关,那么你会在日志文件里看到这么一句话,在/var/log/messages里边.
至此,get_device_info这个函数就结束了他的使命.在usb storage这部戏里,他将不再出场.但我想说,对于usb storage这整个模块来说,主角配角不重要,每个函数都是画布上的一抹色彩.就像我们每一个人,不也是别人人生中的配角,但总是自己人生的主角吗?

下面我们分析下USB鼠标驱动,鼠标输入HID类型,其数据传输采用中断URB,鼠标端点类型为IN。好了,我们先看看这个驱动的模块加载部分。

static int __init usb_mouse_init(void)

{

int retval = usb_register(&usb_mouse_driver);

if (retval == 0)

printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"

DRIVER_DESC "\n");

return retval;

}

模块加载部分仍然是调用usb_register注册USB驱动,我们跟踪看看被注册的usb_mouse_driver

static struct usb_driver usb_mouse_driver = {

.name             = "usbmouse",    //驱动名

.probe            = usb_mouse_probe,  //探测

.disconnect     = usb_mouse_disconnect,

.id_table  = usb_mouse_id_table,  //支持项

};

关于设备支持项我们前面已经讨论过了

static struct usb_device_id usb_mouse_id_table [] = {

{USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,

USB_INTERFACE_PROTOCOL_MOUSE) },

{ }

};

再细细看看USB_INTERFACE_INFO宏的定义

#define USB_INTERFACE_INFO(cl, sc, pr) \

.match_flags = USB_DEVICE_ID_MATCH_INT_INFO, \

.bInterfaceClass = (cl), \

.bInterfaceSubClass = (sc), \

.bInterfaceProtocol = (pr)

根据宏,我们知道,我们设置的支持项包括接口类,接口子类,接口协议三个匹配项。

好了,我们主要看看usb_driver中定义的probe函数

static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)

{

struct usb_device *dev = interface_to_usbdev(intf);  //由接口获取usb_device

struct usb_host_interface *interface;   //设置

struct usb_endpoint_descriptor *endpoint;  //端点描述符

struct usb_mouse *mouse            //本驱动私有结构体

struct input_dev *input_dev;          //输入结构体

int pipe, maxp;

int error = -ENOMEM;

interface = intf->cur_altsetting;          //获取设置

if (interface->desc.bNumEndpoints != 1)    //鼠标端点只有1个

return -ENODEV;

endpoint = &interface->endpoint[0].desc;   //获得端点描述符

if (!usb_endpoint_is_int_in(endpoint))       //检查该端点是否是中断输入端点

return -ENODEV;

pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);   //建立中断输入端点

//返回端点能传输的最大的数据包,鼠标的返回的最大数据包为4个字节

maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));

mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);  //分配mouse结构体

input_dev = input_allocate_device();    //分配input设备空间

if (!mouse || !input_dev)

goto fail1;

mouse->data = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &mouse->data_dma); //分配缓冲区

if (!mouse->data)

goto fail1;

mouse->irq = usb_alloc_urb(0, GFP_KERNEL);   //分配urb

if (!mouse->irq)

goto fail2;

mouse->usbdev = dev;        //填充mouse的usb_device结构体

mouse->dev = input_dev;     //填充mouse的 input结构体

if (dev->manufacturer)    //拷贝厂商ID

strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));

if (dev->product) {   //拷贝产品ID

if (dev->manufacturer)

strlcat(mouse->name, " ", sizeof(mouse->name));

strlcat(mouse->name, dev->product, sizeof(mouse->name));

}

if (!strlen(mouse->name))   //拷贝产品ID

snprintf(mouse->name, sizeof(mouse->name),

"USB HIDBP Mouse %04x:%04x",

le16_to_cpu(dev->descriptor.idVendor),

le16_to_cpu(dev->descriptor.idProduct));

usb_make_path(dev, mouse->phys, sizeof(mouse->phys));

strlcat(mouse->phys, "/input0", sizeof(mouse->phys));

input_dev->name = mouse->name;   //将鼠标名赋给内嵌input结构体

input_dev->phys = mouse->phys;    //将鼠标设备节点名赋给内嵌input结构体

usb_to_input_id(dev, &input_dev->id);  //将usb_driver的支持项拷贝给input

input_dev->dev.parent = &intf->dev;

//evbit表明支持按键事件(EV_KEY)和相对坐标事件(EV_REL)

input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);

//keybit表明按键值包括左键、右键和中键

input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |

BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);

//relbit表明相对坐标事件值包括X坐标和Y坐标

input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);

//keybit表明除了左键、右键和中键,还支持其他按键

input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |

BIT_MASK(BTN_EXTRA);

//relbit表明除了X坐标和Y坐标,还支持中键滚轮的滚动值

input_dev->relbit[0] |= BIT_MASK(REL_WHEEL);

input_set_drvdata(input_dev, mouse);  //将mouse设置为input的私有数据

input_dev->open = usb_mouse_open;    //input设备的open

input_dev->close = usb_mouse_close;

usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data, (maxp > 8 ? 8 : maxp),

usb_mouse_irq, mouse, endpoint->bInterval);  //填充urb

mouse->irq->transfer_dma = mouse->data_dma;

mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; //使用transfer_dma

error = input_register_device(mouse->dev);  //注册input设备

if (error)

goto fail3;

usb_set_intfdata(intf, mouse);

return 0;

fail3:

usb_free_urb(mouse->irq);

fail2:

usb_buffer_free(dev, 8, mouse->data, mouse->data_dma);

fail1:

input_free_device(input_dev);

kfree(mouse);

return error;

}

其实上面这个probe主要是初始化usb设备和input设备,终极目标是为了完成urb的提交和input设备的注册。由于注册为input设备类型,那么当用户层open打开设备时候,最终会调用input中的open实现打开,我们看看input中open的实现

static int usb_mouse_open(struct input_dev *dev)

{

struct usb_mouse *mouse = input_get_drvdata(dev);  //获取私有数据

mouse->irq->dev = mouse->usbdev;  //获取urb指针

if (usb_submit_urb(mouse->irq, GFP_KERNEL))  //提交urb

return -EIO;

return 0;

}

好了,当用户层open打开这个USB鼠标后,我们就已经将urb提交给了USB核心,那么根据USB数据处理流程知道,当处理完毕后,USB核心会通知USB设备驱动程序,这里我们是响应中断服务程序,这就相当于该URB的回调函数。我们在提交urb时候定义了中断服务程序usb_mouse_irq,我们跟踪看看

static void usb_mouse_irq(struct urb *urb)

{

struct usb_mouse *mouse = urb->context;

signed char *data = mouse->data;

struct input_dev *dev = mouse->dev;

int status;

switch (urb->status) {

case 0:                  //成功

break;

case -ECONNRESET:    //未连接

case -ENOENT:

case -ESHUTDOWN:

return;

default:

goto resubmit;   //数据处理没成功,重新提交urb

}

input_report_key(dev, BTN_LEFT,   data[0] & 0x01);    //鼠标左键

input_report_key(dev, BTN_RIGHT,  data[0] & 0x02);    //鼠标右键

input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);    //鼠标中键

input_report_key(dev, BTN_SIDE,   data[0] & 0x08);    //鼠标SIDE

input_report_key(dev, BTN_EXTRA,  data[0] & 0x10);    //鼠标EXTRA

input_report_rel(dev, REL_X,     data[1]);  //鼠标的水平位移

input_report_rel(dev, REL_Y,     data[2]);  //鼠标的垂直位移

input_report_rel(dev, REL_WHEEL, data[3]); //鼠标的滚轮滚动值

input_sync(dev);

resubmit:

status = usb_submit_urb (urb, GFP_ATOMIC);  //再次提交urb,等待下次响应

if (status)

err ("can't resubmit intr, %s-%s/input0, status %d",

mouse->usbdev->bus->bus_name,

mouse->usbdev->devpath, status);

}

根据上面的中断服务程序,我们应该知道,系统是周期性地获取鼠标的事件信息,因此在URB回调函数的末尾再次提交URB请求块,这样又会调用新的回调函数,周而复始。在回调函数中提交URB只能是GFP_ATOMIC优先级,因为URB回调函数运行于中断上下文中禁止导致睡眠的行为。而在提交URB过程中可能会需要申请内存、保持信号量,这些操作或许会导致USB内核睡眠。

最后我们再看看这个驱动的私有数据mouse的定义

struct usb_mouse {

char name[128];             //名字

char phys[64];              //设备节点

struct usb_device *usbdev;    //内嵌usb_device设备

struct input_dev *dev;        //内嵌input_dev设备

struct urb *irq;              //urb结构体

signed char *data;           //transfer_buffer缓冲区

dma_addr_t data_dma;       // transfer _dma缓冲区

};

在上面这个结构体中,每一个成员的作用都应该很清楚了,尤其最后两个的使用区别和作用,前面也已经说过。

如果最终需要测试这个USB鼠标驱动,需要在内核中配置USB支持、对HID接口的支持、对OHCI HCD驱动的支持。另外,将驱动移植到开发板之后,由于采用的是input设备模型,所以还需要开发板带LCD屏才能测试。

3.USB键盘驱动usbkbd.c

跟USB鼠标类型,USB键盘也属于HID类型,代码在/dirver/hid/usbhid/usbkbd.c下。USB键盘除了提交中断URB外,还需要提交控制URB。不多话,我们看代码

static int __init usb_kbd_init(void)

{

int result = usb_register(&usb_kbd_driver);  //注册USB驱动

if (result == 0)

printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"

DRIVER_DESC "\n");

return result;

}

static struct usb_driver usb_kbd_driver = {

.name =          "usbkbd",

.probe =  usb_kbd_probe,

.disconnect =  usb_kbd_disconnect,

.id_table =      usb_kbd_id_table,

};

大家都懂,下面跟踪usb_driver中的probe

static int usb_kbd_probe(struct usb_interface *iface,

const struct usb_device_id *id)

{

struct usb_device *dev = interface_to_usbdev(iface);  //由接口获得设备

struct usb_host_interface *interface;   //设置

struct usb_endpoint_descriptor *endpoint;  //端点描述符

struct usb_kbd *kbd;     //私有数据

struct input_dev *input_dev;  //input设备

int i, pipe, maxp;

int error = -ENOMEM;

interface = iface->cur_altsetting;   //获得设置

if (interface->desc.bNumEndpoints != 1)  //只有一个端点

return -ENODEV;

endpoint = &interface->endpoint[0].desc;  //获取端点描述符

if (!usb_endpoint_is_int_in(endpoint))      //端点必须是中断输入端点

return -ENODEV;

pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);   //建立中断输入端点

maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));  //获取返回字节大小

kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);  //分配私有数据空间

input_dev = input_allocate_device();            //分配input设备空间

if (!kbd || !input_dev)

goto fail1;

if (usb_kbd_alloc_mem(dev, kbd))             //分配urb空间和其他缓冲空间

goto fail2;

kbd->usbdev = dev;                        //给内嵌结构体赋值

kbd->dev = input_dev;                        //给内嵌结构体赋值

if (dev->manufacturer)

strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));

if (dev->product) {

if (dev->manufacturer)

strlcat(kbd->name, " ", sizeof(kbd->name));

strlcat(kbd->name, dev->product, sizeof(kbd->name));

}

if (!strlen(kbd->name))

snprintf(kbd->name, sizeof(kbd->name),

"USB HIDBP Keyboard %04x:%04x",

le16_to_cpu(dev->descriptor.idVendor),

le16_to_cpu(dev->descriptor.idProduct));

usb_make_path(dev, kbd->phys, sizeof(kbd->phys));

strlcpy(kbd->phys, "/input0", sizeof(kbd->phys));

input_dev->name = kbd->name;

input_dev->phys = kbd->phys;

usb_to_input_id(dev, &input_dev->id);     //复制usb_driver的支持项给input的支持项

input_dev->dev.parent = &iface->dev;

input_set_drvdata(input_dev, kbd);       //将kbd设置为input的私有数据

input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) |

BIT_MASK(EV_REP);

input_dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) |

BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_COMPOSE) |

BIT_MASK(LED_KANA);

for (i = 0; i < 255; i++)

set_bit(usb_kbd_keycode[i], input_dev->keybit);

clear_bit(0, input_dev->keybit);

input_dev->event = usb_kbd_event;  //定义event函数

input_dev->open = usb_kbd_open;

input_dev->close = usb_kbd_close;

usb_fill_int_urb(kbd->irq, dev, pipe,kbd->new, (maxp > 8 ? 8 : maxp),

usb_kbd_irq, kbd, endpoint->bInterval);  //填充中断urb

kbd->irq->transfer_dma = kbd->new_dma;

kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;  //dma方式传输

kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;

kbd->cr->bRequest = 0x09;   //设置控制请求的格式

kbd->cr->wValue = cpu_to_le16(0x200);

kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);

kbd->cr->wLength = cpu_to_le16(1);

usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0), (void *) kbd->cr, kbd->leds, 1,

usb_kbd_led, kbd);   //填充控制urb

kbd->led->setup_dma = kbd->cr_dma;

kbd->led->transfer_dma = kbd->leds_dma;

kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);   //设置dma和setup_dma有效

error = input_register_device(kbd->dev);   //注册input设备

if (error)

goto fail2;

usb_set_intfdata(iface, kbd);

return 0;

fail2:

usb_kbd_free_mem(dev, kbd);

fail1:

input_free_device(input_dev);

kfree(kbd);

return error;

}

在上面的probe中,我们主要是初始化一些结构体,然后提交中断urb和控制urb,并注册input设备。其中有几个地方需要细看下,其一,usb_kbd_alloc_mem的实现。其二,设置控制请求的格式。

先来看看usb_kbd_alloc_mem的实现

static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)

{

if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL)))  //分配中断urb

return -1;

if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL)))  //分配控制urb

return -1;

if (!(kbd->new = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &kbd->new_dma)))            return -1;

if (!(kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), GFP_ATOMIC, &kbd->cr_dma)))              //分配控制urb使用的控制请求描述符

return -1;

if (!(kbd->leds = usb_buffer_alloc(dev, 1, GFP_ATOMIC, &kbd->leds_dma)))

return -1;                   //分配中断urb使用的缓冲区

return 0;

}

这里我们需要明白中断urb和控制urb需要分配不同的urb结构体,同时在提交urb之前,需要填充的内容也不同,中断urb填充的是缓冲区和中断处理函数,控制urb填充的是控制请求描述符合回调函数。

好了,接着我们解决第二个问题,设置控制请求的格式。cr是struct usb_ctrlrequest结构的指针,USB协议中规定一个控制请求的格式为一个8个字节的数据包,其定义如下

struct usb_ctrlrequest {

__u8 bRequestType;  //设定传输方向、请求类型等

__u8 bRequest;  //指定哪个请求,可以是规定的标准值也可以是厂家定义的值

__le16 wValue;  //即将写到寄存器的数据

__le16 wIndex;  //接口数量,也就是寄存器的偏移地址

__le16 wLength;  //数据传输阶段传输多少个字节

} __attribute__ ((packed));

USB协议中规定,所有的USB设备都会响应主机的一些请求,这些请求来自USB主机控制器,主机控制器通过设备的默认控制管道发出这些请求。默认的管道为0号端口对应的那个管道。

同样这个input设备首先由用户层调用open函数,所以先看看input中定义的open

static int usb_kbd_open(struct input_dev *dev)

{

struct usb_kbd *kbd = input_get_drvdata(dev);

kbd->irq->dev = kbd->usbdev;

if (usb_submit_urb(kbd->irq, GFP_KERNEL))  //提交中断urb

return -EIO;

return 0;

}

因为这个驱动里面有一个中断urb一个控制urb,我们先看中断urb的处理流程。中断urb在input的open中被提交后,当USB核心处理完毕,会通知这个USB设备驱动,然后执行回调函数,也就是中断处理函数usb_kbd_irq

static void usb_kbd_irq(struct urb *urb)

{

struct usb_kbd *kbd = urb->context;

int i;

switch (urb->status) {

case 0:                  //成功

break;

case -ECONNRESET:    //未连接

case -ENOENT:

case -ESHUTDOWN:

return;

default:

goto resubmit;  //出错就再次提交中断urb

}

for (i = 0; i < 8; i++)  //向input子系统报告

input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);

for (i = 2; i < 8; i++) {

if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {

if (usb_kbd_keycode[kbd->old[i]])

input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0);

else

dev_info(&urb->dev->dev,

"Unknown key (scancode %#x) released.\n", kbd->old[i]);

}

if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {

if (usb_kbd_keycode[kbd->new[i]])

input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);

else

dev_info(&urb->dev->dev,

"Unknown key (scancode %#x) released.\n", kbd->new[i]);

}

}

input_sync(kbd->dev);

memcpy(kbd->old, kbd->new, 8);

resubmit:

i = usb_submit_urb (urb, GFP_ATOMIC);  //再次提交中断urb

if (i)

err_hid ("can't resubmit intr, %s-%s/input0, status %d",

kbd->usbdev->bus->bus_name,

kbd->usbdev->devpath, i);

}

这个就是中断urb的处理流程,跟前面讲的的USB鼠标中断处理流程类似。好了,我们再来看看剩下的控制urb处理流程吧。

我们有个疑问,我们知道在probe中,我们填充了中断urb和控制urb,但是在input的open中,我们只提交了中断urb,那么控制urb什么时候提交呢?

我们知道对于input子系统,如果有事件被响应,我们会调用事件处理层的event函数,而该函数最终调用的是input下的event。所以,对于input设备,我们在USB键盘驱动中只设置了支持LED选项,也就是ledbit项,这是怎么回事呢?刚才我们分析的那个中断urb其实跟这个input基本没啥关系,中断urb并不是像讲键盘input实现的那样属于input下的中断。我们在USB键盘驱动中的input子系统中只设计了LED选项,那么当input子系统有按键选项的时候必然会使得内核调用调用事件处理层的event函数,最终调用input下的event。好了,那我们来看看input下的event干了些什么。

static int usb_kbd_event(struct input_dev *dev, unsigned int type,

unsigned int code, int value)

{

struct usb_kbd *kbd = input_get_drvdata(dev);

if (type != EV_LED)    //不是LED事件就返回

return -1;

//将当前的LED值保存在kbd->newleds中

kbd->newleds = (!!test_bit(LED_KANA,    dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) | (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL,   dev->led) << 1) |(!!test_bit(LED_NUML,    dev->led));

if (kbd->led->status == -EINPROGRESS)

return 0;

if (*(kbd->leds) == kbd->newleds)

return 0;

*(kbd->leds) = kbd->newleds;

kbd->led->dev = kbd->usbdev;

if (usb_submit_urb(kbd->led, GFP_ATOMIC))  //提交控制urb

err_hid("usb_submit_urb(leds) failed");

return 0;

}

当在input的event里提交了控制urb后,经过URB处理流程,最后返回给USB设备驱动的回调函数,也就是在probe中定义的usb_kbd_led

static void usb_kbd_led(struct urb *urb)

{

struct usb_kbd *kbd = urb->context;

if (urb->status)              //提交失败显示

dev_warn(&urb->dev->dev, "led urb status %d received\n",

urb->status);

//比较kbd->leds和kbd->newleds,如果发生变化,则更新kbd->leds

if (*(kbd->leds) == kbd->newleds)

return;

*(kbd->leds) = kbd->newleds;

kbd->led->dev = kbd->usbdev;

if (usb_submit_urb(kbd->led, GFP_ATOMIC))  //再次提交控制urb

err_hid("usb_submit_urb(leds) failed");

}

总结下,我们的控制urb走的是先由input的event提交,触发后由控制urb的回调函数再次提交。好了,通过USB鼠标,我们已经知道了控制urb和中断urb的设计和处理流程。

USB Mass Storage是一类USB存储设备,这些设备包括USB磁盘、USB硬盘、USB磁带机、USB光驱、U盘、记忆棒、智能卡和一些USB摄像头等,这类设备由USB协议支持。

首先我想去看看/driver/usb/storage/Makefile

EXTRA_CFLAGS       := -Idrivers/scsi

obj-$(CONFIG_USB_STORAGE)      += usb-storage.o

usb-storage-obj-$(CONFIG_USB_STORAGE_DEBUG)   += debug.o

usb-storage-objs :=      scsiglue.o protocol.o transport.o usb.o \

initializers.o sierra_ms.o option_ms.o $(usb-storage-obj-y)

这是Makefile中前几行代码,在此我进行一个说明。第一行,-I选项表示需要编译的目录。当本Makefile文件被编译器读取时,会先判断/driver/scsi目录下的文件是否已经被编译,如果没有被编译,则先编译该目录下的文件后,再转到该Makefile文件中。第二行就是USB Mass Storage选项,是总指挥。第三行是调试部分。第四行说明了这个文件夹也就是usb-storage模块必须包含的文件,这些文件将是主要分析的对象,目前我们分析USB驱动,所以重点去分析这些文件中的usb.c

同样,我们先看看usb.c中的模块加载部分

static int __init usb_stor_init(void)

{

int retval;

printk(KERN_INFO "Initializing USB Mass Storage driver...\n");

retval = usb_register(&usb_storage_driver);  //注册usb_driver驱动

if (retval == 0) {

printk(KERN_INFO "USB Mass Storage support registered.\n");

usb_usual_set_present(USB_US_TYPE_STOR);

}

return retval;

}

static struct usb_driver usb_storage_driver = {

.name =          "usb-storage",

.probe =  storage_probe,

.disconnect =  usb_stor_disconnect,

.suspend =      usb_stor_suspend,

.resume =       usb_stor_resume,

.reset_resume =     usb_stor_reset_resume,

.pre_reset =    usb_stor_pre_reset,

.post_reset =   usb_stor_post_reset,

.id_table =      usb_storage_usb_ids,

.soft_unbind =       1,

};

下面重点我们来看看这个probe函数

static int storage_probe(struct usb_interface *intf,

const struct usb_device_id *id)

{

struct us_data *us;

int result;

//检测匹配

if (usb_usual_check_type(id, USB_US_TYPE_STOR) ||usb_usual_ignore_device(intf))

return -ENXIO;

//探测的第一部分

result = usb_stor_probe1(&us, intf, id,(id - usb_storage_usb_ids) + us_unusual_dev_list);

if (result)

return result;

result = usb_stor_probe2(us);   //探测的第二部分

return result;

}

我们发现U盘驱动的探测分为两个部分,我们先来看看第一个部分usb_stor_probe1

int usb_stor_probe1(struct us_data **pus,struct usb_interface *intf,

const struct usb_device_id *id,struct us_unusual_dev *unusual_dev)

{

struct Scsi_Host *host;

struct us_data *us;

int result;

US_DEBUGP("USB Mass Storage device detected\n");

host = scsi_host_alloc(&usb_stor_host_template, sizeof(*us));  //分配Scsi_Host结构体

if (!host) {

printk(KERN_WARNING USB_STORAGE

"Unable to allocate the scsi host\n");

return -ENOMEM;

}

host->max_cmd_len = 16;

*pus = us = host_to_us(host);        //从host结构体中提取出us_data结构体

memset(us, 0, sizeof(struct us_data));

mutex_init(&(us->dev_mutex));

init_completion(&us->cmnd_ready);  //初始化完成量

init_completion(&(us->notify));

init_waitqueue_head(&us->delay_wait);  //初始化等待队列头

init_completion(&us->scanning_done);   //初始化完成量

result = associate_dev(us, intf);         //将us_data与USB设备相关联

if (result)

goto BadDevice;

result = get_device_info(us, id, unusual_dev);  //获取设备信息

if (result)

goto BadDevice;

get_transport(us);                        //获取传输方式

get_protocol(us);                         //获取传输协议

return 0;

BadDevice:

US_DEBUGP("storage_probe() failed\n");

release_everything(us);

return result;

}

我们再看看U盘驱动的探测的第二部分usb_stor_probe2

int usb_stor_probe2(struct us_data *us)

{

struct task_struct *th;

int result;

if (!us->transport || !us->proto_handler) {

result = -ENXIO;

goto BadDevice;

}

US_DEBUGP("Transport: %s\n", us->transport_name);

US_DEBUGP("Protocol: %s\n", us->protocol_name);

if (us->fflags & US_FL_SINGLE_LUN)

us->max_lun = 0;

result = get_pipes(us);   //获得管道

if (result)

goto BadDevice;

result = usb_stor_acquire_resources(us);   //获取资源

if (result)

goto BadDevice;

result = scsi_add_host(us_to_host(us), &us->pusb_intf->dev);  //添加scsi

if (result) {

printk(KERN_WARNING USB_STORAGE

"Unable to add the scsi host\n");

goto BadDevice;

}

th = kthread_create(usb_stor_scan_thread, us, "usb-stor-scan");  //创建线程

if (IS_ERR(th)) {

printk(KERN_WARNING USB_STORAGE

"Unable to start the device-scanning thread\n");

complete(&us->scanning_done);

quiesce_and_remove_host(us);

result = PTR_ERR(th);

goto BadDevice;

}

wake_up_process(th);   //唤醒usb_stor_scan_thread线程

return 0;

BadDevice:

US_DEBUGP("storage_probe() failed\n");

release_everything(us);

return result;

}

好了,我们已经把probe大致阅读了一下,主要通过assocaite_dev(),get_device_info(),get_transport(),get_protocol(),get_pipes()五个函数来为us结构体赋值,然后调用usb_stor_acquire_resources()来得到设备需要的动态资源。最后创建扫描线程usb_stor_scan_thread,让用户能通过cat /proc/scsi/scsi看到U盘设备。现在我们一个个分析下这里提到了每个函数。

首先我们看看来为us结构体赋值的设备关联函数associate_dev的实现

static int associate_dev(struct us_data *us, struct usb_interface *intf)

{

US_DEBUGP("-- %s\n", __func__);

us->pusb_dev = interface_to_usbdev(intf); //由接口获取设备

us->pusb_intf = intf;                  //接口赋值

us->ifnum = intf->cur_altsetting->desc.bInterfaceNumber; //接口数量

US_DEBUGP("Vendor: 0x%04x, Product: 0x%04x, Revision: 0x%04x\n",

le16_to_cpu(us->pusb_dev->descriptor.idVendor),

le16_to_cpu(us->pusb_dev->descriptor.idProduct),

le16_to_cpu(us->pusb_dev->descriptor.bcdDevice));

US_DEBUGP("Interface Subclass: 0x%02x, Protocol: 0x%02x\n",

intf->cur_altsetting->desc.bInterfaceSubClass,

intf->cur_altsetting->desc.bInterfaceProtocol);

usb_set_intfdata(intf, us);      //把us设置为接口的私有数据

//分配控制urb的控制字符空间

us->cr = usb_buffer_alloc(us->pusb_dev, sizeof(*us->cr),GFP_KERNEL, &us->cr_dma);

if (!us->cr) {

US_DEBUGP("usb_ctrlrequest allocation failed\n");

return -ENOMEM;

}

us->iobuf = usb_buffer_alloc(us->pusb_dev, US_IOBUF_SIZE,

GFP_KERNEL, &us->iobuf_dma); //分配urb的缓冲区

if (!us->iobuf) {

US_DEBUGP("I/O buffer allocation failed\n");

return -ENOMEM;

}

return 0;

}

然后我们继续看获得设备信息函数get_device_info的实现

static int get_device_info(struct us_data *us, const struct usb_device_id *id,

struct us_unusual_dev *unusual_dev)

{

struct usb_device *dev = us->pusb_dev;

struct usb_interface_descriptor *idesc =&us->pusb_intf->cur_altsetting->desc;

us->unusual_dev = unusual_dev;  //不常用的设备

//找到USB设备支持的子类和协议

us->subclass = (unusual_dev->useProtocol == US_SC_DEVICE) ?

idesc->bInterfaceSubClass :unusual_dev->useProtocol;

us->protocol = (unusual_dev->useTransport == US_PR_DEVICE) ?

idesc->bInterfaceProtocol :unusual_dev->useTransport;

us->fflags = USB_US_ORIG_FLAGS(id->driver_info);

adjust_quirks(us);

if (us->fflags & US_FL_IGNORE_DEVICE) {//USB设备不能被系统识别则退出

printk(KERN_INFO USB_STORAGE "device ignored\n");

return -ENODEV;

}

if (dev->speed != USB_SPEED_HIGH)  //USB设备不支持高速则改为低速

us->fflags &= ~US_FL_GO_SLOW;

//根据生产厂商和产品号来设置协议、传输类型等参数

if (id->idVendor || id->idProduct) {

static const char *msgs[3] = {

"an unneeded SubClass entry",

"an unneeded Protocol entry",

"unneeded SubClass and Protocol entries"};

struct usb_device_descriptor *ddesc = &dev->descriptor;

int msg = -1;

if (unusual_dev->useProtocol != US_SC_DEVICE &&

us->subclass == idesc->bInterfaceSubClass)

msg += 1;

if (unusual_dev->useTransport != US_PR_DEVICE &&

us->protocol == idesc->bInterfaceProtocol)

msg += 2;

if (msg >= 0 && !(us->fflags & US_FL_NEED_OVERRIDE))

printk(KERN_NOTICE USB_STORAGE "This device "

"(%04x,%04x,%04x S %02x P %02x)"

" has %s in unusual_devs.h (kernel"

" %s)\n"

"   Please send a copy of this message to "

"<linux-usb@vger.kernel.org> and "

"<usb-storage@lists.one-eyed-alien.net>\n",

le16_to_cpu(ddesc->idVendor),

le16_to_cpu(ddesc->idProduct),

le16_to_cpu(ddesc->bcdDevice),

idesc->bInterfaceSubClass,

idesc->bInterfaceProtocol,

msgs[msg],

utsname()->release);

}

return 0;

}

我们继续看得到传输协议函数get_transport,这个函数主要获得USB设备支持的通信协议,并设置USB驱动的传输类型。对于U盘,USB协议规定它属于Bulk-only的传输方式,也就是它的us->protocot为US_PR_BULK

static void get_transport(struct us_data *us)

{

switch (us->protocol) {

case US_PR_CB:

us->transport_name = "Control/Bulk";

us->transport = usb_stor_CB_transport;

us->transport_reset = usb_stor_CB_reset;

us->max_lun = 7;

break;

case US_PR_CBI:

us->transport_name = "Control/Bulk/Interrupt";

us->transport = usb_stor_CB_transport;

us->transport_reset = usb_stor_CB_reset;

us->max_lun = 7;

break;

case US_PR_BULK:

us->transport_name = "Bulk";

us->transport = usb_stor_Bulk_transport;  //传输函数

us->transport_reset = usb_stor_Bulk_reset;

break;

}

}

好了,接着我们看获得协议信息的get_protocol函数,该函数根据不同的协议,用来设置协议的传输函数。对于U盘,USB协议规定us->subclass为US_SC_SCSI

static void get_protocol(struct us_data *us)

{

switch (us->subclass) {

case US_SC_RBC:

us->protocol_name = "Reduced Block Commands (RBC)";

us->proto_handler = usb_stor_transparent_scsi_command;

break;

case US_SC_8020:

us->protocol_name = "8020i";

us->proto_handler = usb_stor_pad12_command;

us->max_lun = 0;

break;

case US_SC_QIC:

us->protocol_name = "QIC-157";

us->proto_handler = usb_stor_pad12_command;

us->max_lun = 0;

break;

case US_SC_8070:

us->protocol_name = "8070i";

us->proto_handler = usb_stor_pad12_command;

us->max_lun = 0;

break;

case US_SC_SCSI:

us->protocol_name = "Transparent SCSI";

us->proto_handler = usb_stor_transparent_scsi_command;  //协议处理函数

break;

case US_SC_UFI:

us->protocol_name = "Uniform Floppy Interface (UFI)";

us->proto_handler = usb_stor_ufi_command;

break;

}

}

最后一个初始化us的函数是获得管道信息的get_pipes函数。

static int get_pipes(struct us_data *us)

{

struct usb_host_interface *altsetting =us->pusb_intf->cur_altsetting;  //获取设置

int i;

struct usb_endpoint_descriptor *ep;      //定义端点描述符

struct usb_endpoint_descriptor *ep_in = NULL; //定义输入端点描述符

struct usb_endpoint_descriptor *ep_out = NULL; //定义输出端点描述符

struct usb_endpoint_descriptor *ep_int = NULL; //定义中断端点描述符

for (i = 0; i < altsetting->desc.bNumEndpoints; i++) {

ep = &altsetting->endpoint[i].desc;  //获取端点描述符

if (usb_endpoint_xfer_bulk(ep)) { //是否是批量传输端点

if (usb_endpoint_dir_in(ep)) {  //是否是输入端点

if (!ep_in)

ep_in = ep;   //设置为批量传输输入端点

} else {

if (!ep_out)

ep_out = ep;   //设置为批量传输输出端点

}

}

else if (usb_endpoint_is_int_in(ep)) { //是否是中断端点

if (!ep_int)

ep_int = ep;             //设置为中断端点

}

}

if (!ep_in || !ep_out || (us->protocol == US_PR_CBI && !ep_int)) {

US_DEBUGP("Endpoint sanity check failed! Rejecting dev.\n");

return -EIO;

}

us->send_ctrl_pipe = usb_sndctrlpipe(us->pusb_dev, 0);  //建立输出控制端点

us->recv_ctrl_pipe = usb_rcvctrlpipe(us->pusb_dev, 0);   //建立输入控制端点

//建立输出批量传输端点

us->send_bulk_pipe = usb_sndbulkpipe(us->pusb_dev, usb_endpoint_num(ep_out));

//建立输入批量传输端点

us->recv_bulk_pipe = usb_rcvbulkpipe(us->pusb_dev, usb_endpoint_num(ep_in));

if (ep_int) {

//建立中断传输端点

us->recv_intr_pipe = usb_rcvintpipe(us->pusb_dev,usb_endpoint_num(ep_int));

us->ep_bInterval = ep_int->bInterval; //设置中断间隔时间

}

return 0;

}

分析完上面get_pipes的代码,需要补充说明的是,在我们的U盘中只有输入批量传输和输出批量传输两个端点,不存在控制端点,如果出现控制端点,那么设备支持CBI协议,即Control/Bulk/Interrupt协议,另外U盘也没有中断端点。

分析完上面五个对cr初始化的函数后,我们接着需要看usb_stor_acquire_resources了,这个函数主要功能是初始化设备,并创建数据传输的控制线程。

static int usb_stor_acquire_resources(struct us_data *us)

{

int p;

struct task_struct *th;

us->current_urb = usb_alloc_urb(0, GFP_KERNEL);  //申请urb

if (!us->current_urb) {

US_DEBUGP("URB allocation failed\n");

return -ENOMEM;

}

if (us->unusual_dev->initFunction) {    //特殊设备的初始化函数

p = us->unusual_dev->initFunction(us);

if (p)

return p;

}

th = kthread_run(usb_stor_control_thread, us, "usb-storage"); //创建并执行控制线程

if (IS_ERR(th)) {

printk(KERN_WARNING USB_STORAGE

"Unable to start control thread\n");

return PTR_ERR(th);

}

us->ctl_thread = th;  //保存线程号

return 0;

}

在上面这个usb_stor_acquire_resources函数中,我们创建并执行了usb_stor_control_thread这个内核线程,这个控制线程用来完成数据的接收和发送,它会一直运行,直到驱动程序退出。

我们来看看这个控制线程。

static int usb_stor_control_thread(void * __us)

{

struct us_data *us = (struct us_data *)__us;

struct Scsi_Host *host = us_to_host(us);

for(;;) {

US_DEBUGP("*** thread sleeping.\n");

if (wait_for_completion_interruptible(&us->cmnd_ready)) //等待用户层SISI命令唤醒

break;

US_DEBUGP("*** thread awakened.\n");

mutex_lock(&(us->dev_mutex));

scsi_lock(host);

if (us->srb == NULL) {  //为循环中超时后的退出

scsi_unlock(host);

mutex_unlock(&us->dev_mutex);

US_DEBUGP("-- exiting\n");

break;

}

if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) { //直接跳到超时判断去

us->srb->result = DID_ABORT << 16;

goto SkipForAbort;

}

scsi_unlock(host);

if (us->srb->sc_data_direction == DMA_BIDIRECTIONAL) { //方向

US_DEBUGP("UNKNOWN data direction\n");

us->srb->result = DID_ERROR << 16;

}

else if (us->srb->device->id && !(us->fflags & US_FL_SCM_MULT_TARG)) {

US_DEBUGP("Bad target number (%d:%d)\n",

us->srb->device->id, us->srb->device->lun);

us->srb->result = DID_BAD_TARGET << 16;

}

else if (us->srb->device->lun > us->max_lun) {US_DEBUGP("Bad LUN (%d:%d)\n",

us->srb->device->id, us->srb->device->lun);

us->srb->result = DID_BAD_TARGET << 16;

}

else if ((us->srb->cmnd[0] == INQUIRY) &&  (us->fflags & US_FL_FIX_INQUIRY)) {                              //如果SCSI是请求命令的处理

unsigned char data_ptr[36] = {

0x00, 0x80, 0x02, 0x02,

0x1F, 0x00, 0x00, 0x00};

US_DEBUGP("Faking INQUIRY command\n");

fill_inquiry_response(us, data_ptr, 36);  //填充一个请求命令

us->srb->result = SAM_STAT_GOOD;

}

else {

US_DEBUG(usb_stor_show_command(us->srb));

us->proto_handler(us->srb, us);   //数据传输

}

scsi_lock(host);

if (us->srb->result != DID_ABORT << 16) {

US_DEBUGP("scsi cmd done, result=0x%x\n",

us->srb->result);

us->srb->scsi_done(us->srb);

} else {

SkipForAbort:

US_DEBUGP("scsi command aborted\n");

}

if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {  //超时处理

complete(&(us->notify));

clear_bit(US_FLIDX_ABORTING, &us->dflags);

clear_bit(US_FLIDX_TIMED_OUT, &us->dflags);

}

us->srb = NULL;

scsi_unlock(host);

mutex_unlock(&us->dev_mutex);

}

for (;;) {

set_current_state(TASK_INTERRUPTIBLE);

if (kthread_should_stop())

break;

schedule();

}

__set_current_state(TASK_RUNNING);

return 0;

}

对于上面这个控制线程,首先该函数执行了一个for(;;),这是一个死循环,也就是这个函数作为一些线程用可以不停息的运行。同时,根据刚开始wait_for_completion_interruptible代码,我们知道开始就进入睡眠状态了,只有唤醒us->cmnd_ready这个控制线程才能继续执行下去,那我们要知道什么时候释放这把锁来唤醒下面的程序呢?

其实有两个地方,一个是模块卸载的时候,另一个就是有SCSI命令发过来。每一次应用层发过来SCSI命令了,比如你去读写/dev/sda,最终SCSI核心层就会调用该与该主机对应的queuecommand函数,这个函数是scsi_host_template结构体成员,在probe中scsi_host_alloc时候注册的。下面是queuecommand函数的实现。

static int queuecommand(struct scsi_cmnd *srb,

void (*done)(struct scsi_cmnd *))

{

struct us_data *us = host_to_us(srb->device->host);

US_DEBUGP("%s called\n", __func__);

if (us->srb != NULL) {

printk(KERN_ERR USB_STORAGE "Error in %s: us->srb = %p\n",

__func__, us->srb);

return SCSI_MLQUEUE_HOST_BUSY;

}

if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) {

US_DEBUGP("Fail command during disconnect\n");

srb->result = DID_NO_CONNECT << 16;

done(srb);

return 0;

}

srb->scsi_done = done;

us->srb = srb;

complete(&us->cmnd_ready);  //释放锁,唤醒控制线程

return 0;

}

好了,用户层有了SCSI命令,就会执行我们驱动中这个控制线程。这个死循环首先会做一些判断,然后一直进行数据通信。那么这个死循环也是会退出的,什么时候呢?当执行这个死循环的最后一个if语句,会进行超时处理,如果超时会将us->srb=NULL,而我们在这个控制线程的死循环中发现,当获取us->cmnd_ready锁后,第一个执行的代码就是判断us->srb是否为NULL,如果us->srb=NULL就会执行break语句,从而跳出第一个死循环。接下来进入第二个死循环,这个死循环首先判断是否真的该结束了,如果真的结束了,那么就break,彻底退出这个控制线程,如果不是应该彻底结束,那进行schedule重新调度控制子线程。

到目前为止,我们的控制线程就已经分析完了,不过我们发现,这个控制线程是在usb_stor_acquire_resources中定义的,在usb_stor_acquire_resources之后,我们还创建了usb_stor_scan_thread线程,这是一个扫描线程。

static int usb_stor_scan_thread(void * __us)

{

struct us_data *us = (struct us_data *)__us;

printk(KERN_DEBUG

"usb-storage: device found at %d\n", us->pusb_dev->devnum);

set_freezable();  //设备在一定时间内没有响应,会挂起

if (delay_use > 0) {  // delay_use秒后如果U盘没拔出则继续执行,否则执行disconnect

printk(KERN_DEBUG "usb-storage: waiting for device "

"to settle before scanning\n");

wait_event_freezable_timeout(us->delay_wait,test_bit(US_FLIDX_DONT_SCAN, &us->dflags), delay_use * HZ);

}

if (!test_bit(US_FLIDX_DONT_SCAN, &us->dflags)) {

if (us->protocol == US_PR_BULK &&

!(us->fflags & US_FL_SINGLE_LUN)) {

mutex_lock(&us->dev_mutex);

us->max_lun = usb_stor_Bulk_max_lun(us);  //询问设备支持多少个LUN

mutex_unlock(&us->dev_mutex);

}

scsi_scan_host(us_to_host(us));

printk(KERN_DEBUG "usb-storage: device scan complete\n");

}

complete_and_exit(&us->scanning_done, 0);  //本进程结束,唤醒disconnect中的进程

}

对于上面这个扫描线程,里面的usb_stor_Bulk_max_lun函数完成了主机控制器与设备之间的第一次通信。USB驱动程序首先发送一个命令,然后设备根据命令返回一些信息,这里显示的是一个表示LUN个数的数字,usb_stor_Bulk_max_lun完成的是一次控制传输。

int usb_stor_Bulk_max_lun(struct us_data *us)

{

int result;

us->iobuf[0] = 0; //默认只有0个LUN

result = usb_stor_control_msg(us, us->recv_ctrl_pipe,

US_BULK_GET_MAX_LUN,

USB_DIR_IN | USB_TYPE_CLASS |

USB_RECIP_INTERFACE,

0, us->ifnum, us->iobuf, 1, 10*HZ); //向设备发送一个命令

US_DEBUGP("GetMaxLUN command result is %d, data is %d\n",

result, us->iobuf[0]);

if (result > 0)

return us->iobuf[0];

return 0;

}

我们看看里面usb_stor_control_msg的实现

int usb_stor_control_msg(struct us_data *us, unsigned int pipe,

u8 request, u8 requesttype, u16 value, u16 index,

void *data, u16 size, int timeout)

{

int status;

US_DEBUGP("%s: rq=%02x rqtype=%02x value=%04x index=%02x len=%u\n",

__func__, request, requesttype,

value, index, size);

us->cr->bRequestType = requesttype;  //初始化us->cr

us->cr->bRequest = request;

us->cr->wValue = cpu_to_le16(value);

us->cr->wIndex = cpu_to_le16(index);

us->cr->wLength = cpu_to_le16(size);

usb_fill_control_urb(us->current_urb, us->pusb_dev, pipe,

(unsigned char*) us->cr, data, size,

usb_stor_blocking_completion, NULL);   //填充控制urb

status = usb_stor_msg_common(us, timeout);   //继续填充控制urb并提交

if (status == 0)

status = us->current_urb->actual_length;

return status;

}

继续往下看usb_stor_msg_common的实现

static int usb_stor_msg_common(struct us_data *us, int timeout)

{

struct completion urb_done;

long timeleft;

int status;

if (test_bit(US_FLIDX_ABORTING, &us->dflags))  //设备处于放弃状态则结束

return -EIO;

init_completion(&urb_done);  //初始化完成量

us->current_urb->context = &urb_done;

us->current_urb->actual_length = 0;

us->current_urb->error_count = 0;

us->current_urb->status = 0;

us->current_urb->transfer_flags = URB_NO_SETUP_DMA_MAP;

if (us->current_urb->transfer_buffer == us->iobuf)

us->current_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

us->current_urb->transfer_dma = us->iobuf_dma;

us->current_urb->setup_dma = us->cr_dma;

status = usb_submit_urb(us->current_urb, GFP_NOIO);  //提交控制urb

if (status) {

return status;

}

set_bit(US_FLIDX_URB_ACTIVE, &us->dflags);

//当前还没取消urb时,取消urb请求

if (test_bit(US_FLIDX_ABORTING, &us->dflags)) {

if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags)) {

US_DEBUGP("-- cancelling URB\n");

usb_unlink_urb(us->current_urb);

}

}

//等待直到urb完成,如果1秒时间到进程没有被信号唤醒,则自动唤醒

timeleft = wait_for_completion_interruptible_timeout(

&urb_done, timeout ? : MAX_SCHEDULE_TIMEOUT);

clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags);

if (timeleft <= 0) {

US_DEBUGP("%s -- cancelling URB\n",

timeleft == 0 ? "Timeout" : "Signal");

usb_kill_urb(us->current_urb);

}

return us->current_urb->status;

}

通过对上面这个usb_stor_msg_common函数的分析,我们现在已经把控制urb提交给USB内核了,当处理完,就会通知USB设备驱动,调用其回调函数,该回调函数在填充控制urb时已经说明,也就是usb_stor_blocking_completion函数

static void usb_stor_blocking_completion(struct urb *urb)

{

struct completion *urb_done_ptr = urb->context;

complete(urb_done_ptr);

}

当一次通信完成,执行了回调函数后,就会释放锁,这样stor_msg_common函数中的wait_for_completion_interruptible_timeout处就会被唤醒,至此一次通信完毕。

最后需要补充说明一个问题,在上面提交控制urb时,flag标志使用的是GFP_NOIO。GFP_NOIO标志的意思是不能在申请内存的时候进行I/O操作,原因是usb_submit_urb()提交之后,会读取磁盘或者U盘中的数据,这种情况下,由于虚拟内存的原因,,申请内存的函数还需要读取磁盘。所以不允许在usb_submit_urb()提交urb时进行I/O操作。

总结下,USB设备驱动主要围绕URB请求块,也就是控制URB、中断URB、批量URB、等时URB。USB骨骼框架是批量URB的例子。USB鼠标是一个中断URB和input子系统结合的例子。USB键盘是一个控制URB和中断URB结合input子系统的例子。USB的U盘是批量URB和控制URB结合的例子。不幸的是,等时URB没有填充函数,因此等时URB在被提交给USB核心之前,需要手动进行初始化。

三.          U盘驱动测试

本Mini2440开发板具有两种USB接口,一个是USB Host,它和普通PC的USB接口是一样的,可以接USB摄像头、USB 键盘、USB鼠标、优盘等常见的USB外设,另外一种是USB Slave,我们一般使用它来下载程序到目标板。对于U盘的测试,要配置内核后才能进行测试。

实验环境:内核linux2.6.32.2,arm-linux-gcc交叉编译器,mini2440开发板。

内核配置:(1)因为优盘用到了 SCSI命令,所以我们先增加SCSI 支持。在 Device Drivers菜单里面,选择SCSI device support。(2)选择 USB support,按回车进入USB support菜单,找到并选中USB Mass Storage support。(3)另外,现在的优盘等移动存储器使用的大都是FAT/FAT32格式的,因此我们还需要添加FAT32 文件系统的支持,在内核配置主菜单下进入FAT32文件系统配置子菜单,为了支持中英文的编码,在File systems菜单下选择Native language support。

接上面的步骤,在内核源代码根目录下执行:make zImage,把生成的新内核烧写到开发板中,先不要插入优盘(这样做是为了看插入时的打印信息),等系统启动后,进入命令行控制台,此时优盘,可以看到其厂家ID和产品ID,以及存储设备uba1信息。

(1) 执行cat /proc/partitions查看磁盘分区信息

(2) 挂载U盘,在mnt目录下建立usb目录

执行mkdir /mnt/usb  ;mount /dev/uba1  /mnt/usb/

(3) 查看U盘信息 ls /mnt/usb –l

(4) 查看挂载后的分区信息 df –h

1. usb-storage 驱动注册过程.  module_init(usb_stor_init);

  1. usb_stor_init(void) <drivers/usb/storage/usb.c>
  2. usb_register(&usb_storage_driver);
  3. usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME);
  4. driver_register(&new_driver->drvwrap.driver);
  5. driver_find(drv->name, drv->bus);
  6. bus_add_driver(drv);
  7. kobject_init_and_add
  8. driver_attach(drv);
  9. bus_for_each_dev
  10. __driver_attach
  11. driver_probe_device(drv, dev);
  12. really_probe(dev, drv);
  13. drv->probe(dev);
  14. kobject_uevent(&priv->kobj, KOBJ_ADD);

2. storage_probe 函数的处理过程.

在 usb-stroage 驱动程序注册的过程中, 由于 bus_usb_type 没有定义 probe 函数,所以会执行代码 usb_storage_driver 中定义的 probe 函数.
  1. storage_probe(struct usb_interface *intf, const struct usb_device_id *id)
  2. usb_stor_probe1(&us, intf, id, (id - usb_storage_usb_ids) + us_unusual_dev_list);
  3. scsi_host_alloc(&usb_stor_host_template, sizeof(*us)); //alloc us->host
  4. associate_dev(us, intf);
  5. interface_to_usbdev(intf);
  6. usb_set_intfdata(intf, us);
  7. usb_buffer_alloc(us->pusb_dev, sizeof(*us->cr),    GFP_KERNEL, &us->cr_dma); //set us->cr
  8. usb_buffer_alloc(us->pusb_dev, US_IOBUF_SIZE, GFP_KERNEL, &us->iobuf_dma);//set us->iobuf
  9. get_device_info(us, id, unusual_dev);
  10. get_transport(us); //set us->transport = usb_stor_CB_transport; us->transport_reset = usb_stor_CB_reset;
  11. get_protocol(us); //set us->proto_handler = usb_stor_transparent_scsi_command;
  12. usb_stor_probe2(us);
  13. get_pipes(us); //get bulk in/out endpoint.and get contrl in/out endpoint.
  14. usb_stor_acquire_resources(us); //Acquire all the other resources and add the host
  15. usb_alloc_urb(0, GFP_KERNEL); //set us->current_urb.
  16. kthread_run(usb_stor_control_thread, us, "usb-storage"); //set to us->ctl_thread. struct task_struct.sleep at the beginning.
  17. scsi_add_host(us_to_host(us), &us->pusb_intf->dev);//add us->host into scsi-core.
  18. kthread_create(usb_stor_scan_thread, us, "usb-stor-scan"); //Start up the thread for delayed SCSI-device scanning
  19. wake_up_process(th); // wake up kthread usb_stor_scan_thread, which create above.
  20. set_freezable(); // usb_stor_scan_thread run as below flow. begin this line.
  21. usb_stor_Bulk_max_lun(us); //set us->max_lun. lun<logical unit number>
  22. usb_stor_control_msg(...); //args doesn't show as too long.
  23. usb_fill_control_urb(...); //fill urb with args.
  24. usb_stor_msg_common(us, timeout);
  25. init_completion(&urb_done);
  26. usb_submit_urb(us->current_urb, GFP_NOIO); //submit urb to the usb-core.
  27. wait_for_completion_interruptible_timeout(...);//wait urb to be done.
  28. scsi_scan_host(us_to_host(us));
3. usb_stor_host_template 数据结构.
  1. struct scsi_host_template usb_stor_host_template = {
  2. .name =                "usb-storage",
  3. .proc_name =            "usb-storage",
  4. .proc_info =            proc_info,
  5. .info =                host_info,
  6. .queuecommand =            queuecommand, //called by scsi_dispatch_cmd() in scsi.c, dispatch srb.
  7. .eh_abort_handler =        command_abort, //abort command.
  8. .eh_device_reset_handler =    device_reset, //reset device.
  9. .eh_bus_reset_handler =        bus_reset, //reset all, include usb scsi host, bus, driver.
  10. .can_queue =            1,
  11. .cmd_per_lun =            1,
  12. .this_id =            -1,
  13. .slave_alloc =            slave_alloc,
  14. .slave_configure =        slave_configure,
  15. .sg_tablesize =            SG_ALL,
  16. .max_sectors = 240,
  17. .use_clustering =        1,
  18. .emulated =            1,
  19. .skip_settle_delay =        1,
  20. .sdev_attrs =            sysfs_device_attr_list,
  21. .module =            THIS_MODULE
  22. }
4. usb_storage_driver 数据结构.
usb-storage 驱动的核心结构.
  1. static struct usb_driver usb_storage_driver = {
  2. .name =        "usb-storage",
  3. .probe =    storage_probe, //注册驱动或者有设备插入时被调用.
  4. .disconnect =    usb_stor_disconnect,//移除设备或者移除驱动时调用.
  5. .suspend =    usb_stor_suspend, //系统挂起时调用
  6. .resume =    usb_stor_resume, //系统从挂起恢复执行时调用.
  7. .reset_resume =    usb_stor_reset_resume,//系统从挂起恢复执行时,用reset替代resume.
  8. .pre_reset =    usb_stor_pre_reset, //USB设备reset之前调用.
  9. .post_reset =    usb_stor_post_reset,//USB设备reset之后调用.
  10. .id_table =    usb_storage_usb_ids,
  11. .soft_unbind =    1,
  12. };
5. USB 读写.
(1) 当用户需要访问 USB 设备时, scsi-core 会调用 scsi_dispatch_cmd(), 而 scsi_dispatch_cmd()
会进一步调用 scsi_host_template 中的 queuecommand, 也就是 usb_stor_host_template 中的 queuecommand() 函数.

  1. queuecommand(struct scsi_cmnd *srb,    void (*done)(struct scsi_cmnd *))
  2. host_to_us(srb->device->host) //get us.
  3. complete(&us->cmnd_ready); //this would wake up usb-storage kthread, execute func usb_stor_control_thread.

(2) usb_stor_control_thread 线程的处理过程.
  1. usb_stor_control_thread(void * __us)
  2. if (wait_for_completion_interruptible(&us->cmnd_ready)) //wake up by queuecommand(). see 4 lines above.
  3. us->proto_handler(us->srb, us); //see storage_probe() -> get_protocol(us). called usb_stor_transparent_scsi_command actually.
  4. us->srb->scsi_done(us->srb); //transfer already done.

(3) usb_stor_transparent_scsi_command() 函数的处理过程. <drivers/usb/storage/protocol.c>
USB 的数据传输分为三个过程: 命令传输, 数据传输, 状态传输.
  1. usb_stor_transparent_scsi_command(struct scsi_cmnd *srb, struct us_data *us)
  2. usb_stor_invoke_transport(srb, us); //send the command to the transport layer
  3. scsi_set_resid(srb, 0);
  4. us->transport(srb, us); //see storage_probe() -> get_transport(us), called usb_stor_CB_transport() acctually.
  5. usb_stor_ctrl_transfer(...); //send command.
  6. usb_fill_control_urb(...); //fill urb with args.
  7. usb_stor_msg_common(us, 0); //submit urb.
  8. init_completion(&urb_done);
  9. usb_submit_urb(us->current_urb, GFP_NOIO); //submit urb to the usb-core.
  10. wait_for_completion_interruptible_timeout(...);//wait urb to be done.
  11. interpret_urb_result(...); //translate result of usb_stor_msg_common().
  12. usb_stor_bulk_srb(us, pipe, srb); //transfer data.
  13. usb_stor_bulk_transfer_sglist(...); //use scatter/gather.
  14. usb_sg_init(...); //init scatter/gather.
  15. usb_sg_wait(&us->current_sg); //wait for the completion of the transfer .
  16. interpret_urb_result(...);
  17. scsi_set_resid(srb, scsi_bufflen(srb) - partial);
  18. usb_stor_intr_transfer(us, us->iobuf, 2);// get transfer status.
  19. usb_fill_int_urb(...);
  20. usb_stor_msg_common(us, 0);
  21. interpret_urb_result(...);
6. 移除 USB 设备. usb_stor_disconnect() 函数的处理过程. 基本都是释放资源, 释放锁.
  1. usb_stor_disconnect(struct usb_interface *intf)
  2. usb_get_intfdata(intf); // get usb-data.
  3. quiesce_and_remove_host(us);
  4. wake_up(&us->delay_wait);
  5. wait_for_completion(&us->scanning_done);
  6. scsi_remove_host(host);
  7. wake_up(&us->delay_wait);
  8. release_everything(us);
  9. usb_stor_release_resources(us);
  10. complete(&us->cmnd_ready);
  11. kthread_stop(us->ctl_thread);
  12. kfree(us->extra);
  13. usb_free_urb(us->current_urb);
  14. dissociate_dev(us);
  15. usb_buffer_free(us->pusb_dev, sizeof(*us->cr), us->cr, us->cr_dma);
  16. usb_buffer_free(us->pusb_dev, US_IOBUF_SIZE, us->iobuf,    us->iobuf_dma);
  17. usb_set_intfdata(us->pusb_intf, NULL);
  18. scsi_host_put(us_to_host(us));

http://www.linuxidc.com/Linux/2012-12/76197p17.htm

usb storage相关推荐

  1. usb storage驱动分析

    本文以usb storage设备驱动来分析,由于usb storage涉及到usb驱动框架,scsi驱动框架,block io驱动框架,先来看下初始化 1. usb storage设备的初始化流程 s ...

  2. uboot中usb storage启动方法1

    转载自:http://blog.chinaunix.net/uid-22117117-id-2881169.html 这种方法比较简单,就是利用uboot自带命令usbboot来实现启动,也算是最粗糙 ...

  3. 如何实现Linux下的U盘(USB Mass Storage)驱动

    如何实现Linux下的U盘(USB Mass Storage)驱动 版本:v0.7 How to Write LinuxUSB MSC (Mass Storage Class) Driver Crif ...

  4. USB Mass Storage Class

    编辑博客时,回车的意思是切换段落,shift+回车才是换行. SCSI Interface Controller: AMD am5380 1 U盘量产工具 - 主控芯片私有的SCSI命令 ChipEa ...

  5. mac 制作usb启动盘_如何使用Mac制作Windows 10 USB-从Mac终端构建可启动的ISO

    mac 制作usb启动盘 Most new PCs don't come with DVD drives anymore. So it can be a pain to install Windows ...

  6. Linux下的USB总线驱动 3

    版权所有,转载请说明转自 http://my.csdn.net/weiqing1981127 4.U盘驱动分析 USB Mass Storage是一类USB存储设备,这些设备包括USB磁盘.USB硬盘 ...

  7. 在WinCE 6.0系统下实现USB功能定制

    USB的广泛应用就不用多说了,相信目前的各个领域的嵌入式产品中,很少有不用USB的.USB是主从结构的,分为USB Host和USB Slave,从USB1.0,USB1.1到现在的USB2.0,基于 ...

  8. 慢慢学Linux驱动开发,第四篇,USB工作原理简单分析

    1.简单概念 学习U盘驱动,不仅仅是去关注drivers/usb/storage目录下那相关的3000行代码. 毕竟,作为U盘,她需要与usb core打交道,需要与scsi core打交道,需要与内 ...

  9. C# 系统应用之通过注册表获取USB使用记录(一)

    该文章是"个人电脑历史记录清除软件"项目的系统应用系列文章. 前面已经讲述了如何清除IE浏览器的历史记录.获取Windows最近访问文件记录.清除回收站等功能.现在我需要完成的是删 ...

最新文章

  1. php中静态方法有什么作用,PHP中的静态方法与非静态方法有什么不同吗?
  2. Web开发-Django模型层
  3. Sequence with Digits CodeForces - 1355A(暴力+数学)
  4. switchhosts 没有修改hosts的权限解决方案
  5. ZZULIOJ 1063:最大公约与最小公倍
  6. 计算机系统如何禁止删除文件,电脑禁止安装删除文件 禁止复制电脑文件的方法...
  7. 用DOS的FOR命令高速ping指定的所有局域网机器
  8. 1日元是多少人民币(2014年04月23日)
  9. [self Introduce]热情洋溢的白羊座
  10. zookeeper 与dubbo管理平台Window 安装与使用
  11. byte数组转字符串_字符串性能优化不容小觑
  12. 视频教程-SPSS统计自动化-VBA脚本开发-其他
  13. Pigsty:开箱即用的数据库发行版
  14. 编译疑问: Warning: Backslash and Newline separated by space
  15. 一起动才够嗨!Android CoordinatorLayout 自定义 Behavior
  16. 【翻译】Chrome Developer Tools: Element 样式
  17. 猫捉老鼠java_一个用java程序写的猫捉老鼠程序
  18. 基于E-PUCK 2.0多智能体自主协同 高频投影定位系统
  19. OSChina 周二乱弹 —— 你醒啦?现在你已经是丧尸了
  20. 斐波那契数列前20项

热门文章

  1. 堪比病毒营销的新思路:换个姿势玩抖音!
  2. java ftp删除_java ftp实现文件上传和删除
  3. 覆盖alert对话框-自制Jquery.alert插件
  4. sql study conclusion
  5. #(二)、股市是混沌还是浑沌?
  6. 擦窗机器人不用时怎么收纳_擦窗机器人如何保证不会高空掉落,这些措施必不可少...
  7. 权限管理实现的两种方式(详解)
  8. java的八种基本蕾西_(1/7)[代码整洁之道]你真的会用枚举吗?非也!
  9. IoT物联网平台如何实现设备全球分发、就近接入?
  10. 华硕win10U盘重装系统进入pe