学习博客地址:https://blog.csdn.net/fudan_abc
学习书名:《Linux那些事之我是USB》

看代码之前,我曾经认真的思考过这么一个问题,我需要关注的仅仅是drivers/usb/storage/目录下面那相关的3000多行代码吗?就是这样几个文件就能让一个个不同的U盘在Linux下面工作起来吗?
很不幸,问题远不是这样简单.外面的世界很精彩,作为U盘,她需要与usb core打交道,需要与scsi core打交道,需要与内存管理单元打交道,还有内核中许许多多其它模块打交道.外面的世界很大,远比我们想象的大。

什么是usb core?她负责实现一些核心的功能,为别的设备驱动程序提供服务,比如申请内存,比如实现一些所有的设备都会需要的公共的函数,事实上,在usb的世界里,一个普通的设备要正常的工作,除了要有设备本身以外,还需要有一个叫做控制器的冬冬,老外把它叫做host controller,和这个控制器相连接在一起的有另一个咚咚,她叫root hub。
现实中经常是让一个usb控制器和一个hub绑定在一起,专业一点说叫集成,而这个hub也被称作root hub,换言之,和usb控制器绑定在一起的hub就是系统中最根本的hub,其它的hub可以连接到她这里,然后可以延伸出去,外接别的设备,当然也可以不用别的hub,让usb设备直接接到root hub上。
hub干嘛用的我们知道了,那么usb host controller本身是干什么用的呢?controller,控制器,顾名思义,用于控制,控制什么,控制所有的usb设备的通信.通常计算机的cpu并不是直接和usb设备打交道,而是和控制器打交道,他要对设备做什么,他会告诉控制器,而不是直接把指令发给设备,然后控制器再去负责处理这件事情,他会去指挥设备执行命令,而cpu就不用管剩下的事情,他还是该干嘛干嘛去,控制器替他去完成剩下的事情,事情办完了再通知cpu。
Linux内核开发者们,专门写了一些代码,并美其名曰usb core,放置于drivers/usb/core目录下,用于初始化整个usb系统,初始化root hub,并提供内存分配函数等,host controller相关的代码也单独建了一个目录,叫host目录,这是因为usb host controller随着时代的发展,也开始有了好几种,不再像刚开始那样只有一种,所以呢,设计者们把一些host controller公共的代码仍然留在core目录下,而一些各host controller单独的代码则移到host目录下面让负责各种host controller的人去维护,常见的host controller有三种,分别叫做EHCI,UHCI,OHCI,所以这样,出来了三个概念,usb core,usb host,usb device。

Linux设备模型中三个很重要的概念就是总线,设备,驱动.即bus,device,driver,而实际上内核中也定义了这么一些数据结构,他们是struct bus_type,struct device,struct device_driver,这三个重要的数据结构都来自一个地方,include/linux/device.h.我们知道总线有很多种,pci总线,scsi总线,usb总线,所以我们会看到Linux内核代码中出现pci_bus_type,scsi_bus_type,usb_bus_type,他们都是struct bus_type类型的变量.而struct bus_type结构中两个非常重要的成员就是struct kset drivers和struct kset devices。这里我们只需要知道,drivers和devices的存在,让struct bus_type与两个链表联系了起来,一个是devices的链表,一个是drivers的链表,也就是说,知道一条总线所对应的数据结构,就可以找到这条总线所关联的设备有哪些,又有哪些支持这类设备的驱动程序。

struct bus_type中为devices和drivers准备了两个链表,而代表device的结构体struct device中又有两个成员,struct bus_type *bus和struct device_driver *driver,同样,代表driver的结构体struct device_driver同样有两个成员,struct bus_type *bus和struct list_head devices,struct device和struct device_driver的定义和struct bus_type一样,在include/linux/device.h中.凭一种男人的直觉,可以知晓,struct device中的bus记录的是这个设备连在哪条总线上,driver记录的是这个设备用的是哪个驱动,反过来,struct device_driver中的bus代表的也是这个驱动属于哪条总线,devices记录的是这个驱动支持的那些设备,没错,是devices(复数),而不是device(单数),因为一个驱动程序可以支持一个或多个设备,反过来一个设备则只会绑定给一个驱动程序。

usb core的代码会进行整个usb系统的初始化,比如申请struct bus_type usb_bus_type,然后会扫描usb总线,看线上连接了哪些usb设备,或者说root hub上连了哪些usb设备,比如说连了一个usb键盘,那么就为它准备一个struct device,根据它的实际情况,为这个struct device赋值,并插入devices链表中来.又比如root hub上连了一个普通的hub,那么除了要为这个hub本身准备一个struct device以外,还得继续扫描看这个hub上是否又连了别的设备,有的话继续重复之前的事情,这样一直进行下去,直到完成整个扫描,最终就把usb_bus_type中的devices链表给建立了起来。
那么drivers链表呢?这个就不用bus方面主动了,而该由每一个driver本身去bus上面登记,或者说挂牌.具体到usb系统,每一个usb设备的驱动程序都会有一个struct usb_driver结构体。其代码如下,来自include/linux/usb.h

struct usb_driver {const char *name;int (*probe) (struct usb_interface *intf,const struct usb_device_id *id);void (*disconnect) (struct usb_interface *intf);int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code,void *buf);int (*suspend) (struct usb_interface *intf, pm_message_t message);int (*resume) (struct usb_interface *intf);int (*reset_resume)(struct usb_interface *intf);int (*pre_reset)(struct usb_interface *intf);int (*post_reset)(struct usb_interface *intf);const struct usb_device_id *id_table;struct usb_dynids dynids;struct usbdrv_wrap drvwrap;unsigned int no_dynamic_id:1;unsigned int supports_autosuspend:1;unsigned int disable_hub_initiated_lpm:1;unsigned int soft_unbind:1;
};
#define to_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver)

此刻我们只需注意到其中的struct device_driver driver这个成员,usb core为每一个设备驱动准备了一个函数,让它把自己的这个struct device_driver driver插入到usb_bus_type中的drivers链表中去。而这个函数正是我们此前看到的usb_register.而与之对应的usb_deregister所从事的正是与之相反的工作,把这个结构体从drivers链表中删除.可以说,usb core的确是用心良苦,为每一个usb设备驱动做足了功课,正因为如此,作为一个实际的usb设备驱动,它在初始化阶段所要做的事情就很少,很简单了,直接调用usb_register即可.事实上,没有人是理所当然应该为你做什么的,但usb core这么做了.所以每一个写usb设备驱动的人应该铭记,usb device driver绝不是一个人在工作,在他身后,是usb core所提供的默默无闻又不可或缺的支持。

struct usb_driver {const char *name;int (*probe) (struct usb_interface *intf,const struct usb_device_id *id);void (*disconnect) (struct usb_interface *intf);int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code,void *buf);int (*suspend) (struct usb_interface *intf, pm_message_t message);int (*resume) (struct usb_interface *intf);int (*reset_resume)(struct usb_interface *intf);int (*pre_reset)(struct usb_interface *intf);int (*post_reset)(struct usb_interface *intf);const struct usb_device_id *id_table;struct usb_dynids dynids;struct usbdrv_wrap drvwrap;unsigned int no_dynamic_id:1;unsigned int supports_autosuspend:1;unsigned int disable_hub_initiated_lpm:1;unsigned int soft_unbind:1;
};
#define to_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver)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,.supports_autosuspend = 1,.soft_unbind =  1,
};module_usb_driver(usb_storage_driver);#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);#define module_usb_driver(__usb_driver)  module_driver(__usb_driver,  \
usb_register,  usb_deregister)
#define usb_register(driver)     usb_register_driver(driver, THIS_MODULE,  \
KBUILD_MODNAME)

module_usb_driver函数,回调usb_register,usb_register最终会调用 usb_register_driver 函数来进行usb驱动注册。

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,绑定吧,如果不属于这张表里的,那么不好意思,您请便.哪凉快上哪去。


#ifdef MODULE#define MODULE_GENERIC_TABLE(gtype,name)                        \
extern const struct gtype##_id __mod_##gtype##_table            \__attribute__ ((unused, alias(__stringify(name))))#else  /* !MODULE */#define MODULE_GENERIC_TABLE(gtype,name)#endif#define MODULE_DEVICE_TABLE(type,name)          \MODULE_GENERIC_TABLE(type##_device,name)struct usb_device_id usb_storage_usb_ids[] = {
#   include "unusual_devs.h"{ }       /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, usb_storage_usb_ids);struct usb_device_id {/* which fields to match against? */__u16       match_flags;/* Used for product specific matches; range is inclusive */__u16        idVendor;__u16      idProduct;__u16     bcdDevice_lo;__u16      bcdDevice_hi;/* Used for device class matches */__u8        bDeviceClass;__u8       bDeviceSubClass;__u8        bDeviceProtocol;/* Used for interface class matches */__u8      bInterfaceClass;__u8        bInterfaceSubClass;__u8     bInterfaceProtocol;/* Used for vendor-specific interface matches */__u8     bInterfaceNumber;/* not matched against */kernel_ulong_t    driver_info__attribute__((aligned(sizeof(kernel_ulong_t))));
};

实际上 struct usb_device_id 这个结构体对每一个usb设备来说,就相当于是她的身份证,记录了她的一些基本信息,通常我们的身份证上会记录我们的姓名,性别,出生年月,户口地址等等,而usb设备她也有她需要记录的信息,以区分她和别的usb设备,比如Vendor-厂家,Product-产品,以及其他一些比如产品编号,产品的类别,遵循的协议,这些都会在usb的规范里边找到对应的冬冬。
于是我们知道,一个usb_driver会把它的这张id表去和每一个usb设备的实际情况进行比较,如果该设备的实际情况和这张表里的某一个id相同,准确地说,只有这许多特征都吻合,才能够把一个usb device和这个usb driver进行绑定,这些特征哪怕差一点也不行。

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

struct usb_device {int       devnum;char     devpath[16];u32     route;enum usb_device_state state;enum usb_device_speed speed;struct usb_tt *tt;int     ttport;unsigned int toggle[2];struct usb_device *parent;struct usb_bus *bus;struct usb_host_endpoint ep0;struct device dev;struct usb_device_descriptor descriptor;struct usb_host_bos *bos;struct usb_host_config *config;struct usb_host_config *actconfig;struct usb_host_endpoint *ep_in[16];struct usb_host_endpoint *ep_out[16];char **rawdescriptors;unsigned short bus_mA;u8 portnum;u8 level;unsigned can_submit:1;unsigned persist_enabled:1;unsigned have_langid:1;unsigned authorized:1;unsigned authenticated:1;unsigned wusb:1;unsigned lpm_capable:1;unsigned usb2_hw_lpm_capable:1;unsigned usb2_hw_lpm_enabled:1;unsigned usb3_lpm_enabled:1;int string_langid;/* static strings from the device */char *product;char *manufacturer;char *serial;struct list_head filelist;int maxchild;u32 quirks;atomic_t urbnum;unsigned long active_duration;#ifdef CONFIG_PMunsigned long connect_time;unsigned do_remote_wakeup:1;unsigned reset_resume:1;unsigned port_is_suspended:1;
#endifstruct wusb_dev *wusb_dev;int slot_id;enum usb_device_removable removable;struct usb3_lpm_parameters u1_params;struct usb3_lpm_parameters u2_params;unsigned lpm_disable_count;
};
#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设备都得有的.他们是,device descriptor, configuration descriptor, interface descriptor, endpoint descriptor.描述符里的冬冬是一个设备出厂的时候就被厂家给固化在设备里了。

关于这几种描述符,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:

 struct usb_interface {/* array of alternate settings for this interface,* stored in no particular order */struct usb_host_interface *altsetting;struct usb_host_interface *cur_altsetting;  /* the currently* active alternate setting */unsigned num_altsetting;   /* number of alternate settings *//* If there is an interface association descriptor then it will list* the associated interfaces */struct usb_interface_assoc_descriptor *intf_assoc;int minor;            /* minor number this interface is* bound to */enum usb_interface_condition condition;       /* state of binding */unsigned sysfs_files_created:1;   /* the sysfs attributes exist */unsigned ep_devs_created:1; /* endpoint "devices" exist */unsigned unregistering:1;   /* unregistration is in progress */unsigned needs_remote_wakeup:1;  /* driver requires remote wakeup */unsigned needs_altsetting0:1;    /* switch to altsetting 0 is pending */unsigned needs_binding:1;    /* needs delayed unbind/rebind */unsigned reset_running:1;unsigned resetting_device:1;  /* true: bandwidth alloc after reset */struct device dev;       /* interface specific device info */struct device *usb_dev;atomic_t pm_usage_cnt;       /* usage counter for autosuspend */struct work_struct reset_ws; /* for resets in atomic context */
};
#define to_usb_interface(d) container_of(d, struct usb_interface, dev)

这个结构体是一个贯穿整个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。

前面我们已经了解了device,configuration,interface,还剩最后一个endpoint.USB通信的最基本的形式就是通过endpoint,道上的兄弟管这个叫做端点,一个接口有一个或多个端点,而作为像U盘这样的存储设备吧,它至少有一个控制端点,两个bulk端点.这些端点都是干嘛的?
usb协议里规定了,usb设备有四种通信方式,分别是控制传输,中断传输,批量传输,等时传输。
相比之下,等时传输是四种传输中最麻烦的,所以,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.而管道,实际上只是为了让我们能够找到端点,就相当于我们日常说的邮编地址。
严格来说,管道的另一端应该是usb主机,即前面说的那个host,usb协议里边也是这么说的,协议里边说pipes代表着一种能力,怎样一种能力呢,在主机和设备上的端点之间移动数据,听上去挺玄的.因为事实上,usb里边所有的数据传输都是有主机发起的.一切都是以主机为核心,各种设备紧紧围绕在主机周围.所以呢,usb core里边很多函数就是,为了让usb host能够正确的完成数据传输或者说传输调度,就得告诉host这么一个pipe,换言之,它得知道自己这次调度的数据是传送给谁或者从谁那里传输过来。

对于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中:

/* First part of general USB mass-storage probing */
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;dev_info(&intf->dev, "USB Mass Storage device detected\n");/** Ask the SCSI layer to allocate a host structure, with extra* space at the end for our private us_data structure.*/host = scsi_host_alloc(&usb_stor_host_template, sizeof(*us));if (!host) {dev_warn(&intf->dev, "Unable to allocate the scsi host\n");return -ENOMEM;}/** Allow 16-byte CDBs and thus > 2TB*/host->max_cmd_len = 16;host->sg_tablesize = usb_stor_sg_tablesize(intf);*pus = us = host_to_us(host);mutex_init(&(us->dev_mutex));us_set_lock_class(&us->dev_mutex, intf);init_completion(&us->cmnd_ready);init_completion(&(us->notify));init_waitqueue_head(&us->delay_wait);INIT_DELAYED_WORK(&us->scan_dwork, usb_stor_scan_dwork);/* Associate the us_data structure with the USB device */result = associate_dev(us, intf);if (result)goto BadDevice;/* Get the unusual_devs entries and the descriptors */result = get_device_info(us, id, unusual_dev);if (result)goto BadDevice;/* Get standard transport and protocol settings */get_transport(us);get_protocol(us);/* Give the caller a chance to fill in specialized transport* or protocol settings.*/return 0;BadDevice:usb_stor_dbg(us, "storage_probe() failed\n");release_everything(us);return result;
}
EXPORT_SYMBOL_GPL(usb_stor_probe1);/* Second part of general USB mass-storage probing */
int usb_stor_probe2(struct us_data *us)
{int result;struct device *dev = &us->pusb_intf->dev;/* Make sure the transport and protocol have both been set */if (!us->transport || !us->proto_handler) {result = -ENXIO;goto BadDevice;}usb_stor_dbg(us, "Transport: %s\n", us->transport_name);usb_stor_dbg(us, "Protocol: %s\n", us->protocol_name);/* fix for single-lun devices */if (us->fflags & US_FL_SINGLE_LUN)us->max_lun = 0;if (!(us->fflags & US_FL_SCM_MULT_TARG))us_to_host(us)->max_id = 1;/* Find the endpoints and calculate pipe values */result = get_pipes(us);if (result)goto BadDevice;/** If the device returns invalid data for the first READ(10)* command, indicate the command should be retried.*/if (us->fflags & US_FL_INITIAL_READ10)set_bit(US_FLIDX_REDO_READ10, &us->dflags);/* Acquire all the other resources and add the host */result = usb_stor_acquire_resources(us);if (result)goto BadDevice;snprintf(us->scsi_name, sizeof(us->scsi_name), "usb-storage %s",dev_name(&us->pusb_intf->dev));result = scsi_add_host(us_to_host(us), dev);if (result) {dev_warn(dev,"Unable to add the scsi host\n");goto BadDevice;}/* Submit the delayed_work for SCSI-device scanning */usb_autopm_get_interface_no_resume(us->pusb_intf);set_bit(US_FLIDX_SCAN_PENDING, &us->dflags);if (delay_use > 0)dev_dbg(dev, "waiting for device to settle before scanning\n");queue_delayed_work(system_freezable_wq, &us->scan_dwork,delay_use * HZ);return 0;/* We come here if there are any problems */
BadDevice:usb_stor_dbg(us, "storage_probe() failed\n");release_everything(us);return result;
}
EXPORT_SYMBOL_GPL(usb_stor_probe2);/* Handle a USB mass-storage disconnect */
void usb_stor_disconnect(struct usb_interface *intf)
{struct us_data *us = usb_get_intfdata(intf);quiesce_and_remove_host(us);release_everything(us);
}
EXPORT_SYMBOL_GPL(usb_stor_disconnect);/* The main probe routine for standard devices */
static int storage_probe(struct usb_interface *intf,const struct usb_device_id *id)
{struct us_unusual_dev *unusual_dev;struct us_data *us;int result;int size;/** If the device isn't standard (is handled by a subdriver* module) then don't accept it.*/if (usb_usual_ignore_device(intf))return -ENXIO;/** Call the general probe procedures.** The unusual_dev_list array is parallel to the usb_storage_usb_ids* table, so we use the index of the id entry to find the* corresponding unusual_devs entry.*/size = ARRAY_SIZE(us_unusual_dev_list);if (id >= usb_storage_usb_ids && id < usb_storage_usb_ids + size) {unusual_dev = (id - usb_storage_usb_ids) + us_unusual_dev_list;} else {unusual_dev = &for_dynamic_ids;dev_dbg(&intf->dev, "Use Bulk-Only transport with the Transparent SCSI protocol for dynamic id: 0x%04x 0x%04x\n",id->idVendor, id->idProduct);}result = usb_stor_probe1(&us, intf, id, unusual_dev);if (result)return result;/* No special transport or protocol settings in the main module */result = usb_stor_probe2(us);return result;
}

两个参数不用多说了,struct usb_interface和struct usb_device_id的这两个指针都是前面介绍过的,来自usb core那一层,我们整个故事里用到的就是这么一个,不是说一会指向你,一会指向他,这两个指针的指向是定下来的。
最重要的一个数据结构终于在这种神不知鬼不觉的地方惊艳亮相了.整个usb-storage模块里边自己定义的数据结构不多,但是us_data算一个.这个数据结构是跟随我们的代码一直走下去的,如影随形,几乎处处都可以看见她的身影.先把它的代码贴出来,来自drivers/usb/storage/usb.h:

/* we allocate one of these for every device that we remember */
struct us_data {/* The device we're working with* It's important to note:*    (o) you must hold dev_mutex to change pusb_dev*/struct mutex        dev_mutex;   /* protect pusb_dev */struct usb_device    *pusb_dev;   /* this usb_device */struct usb_interface  *pusb_intf;  /* this interface */struct us_unusual_dev   *unusual_dev;   /* device-filter entry     */unsigned long     fflags;      /* fixed flags from filter */unsigned long     dflags;      /* dynamic atomic bitflags */unsigned int      send_bulk_pipe;  /* cached pipe values */unsigned int       recv_bulk_pipe;unsigned int     send_ctrl_pipe;unsigned int     recv_ctrl_pipe;unsigned int     recv_intr_pipe;/* information about the device */char           *transport_name;char            *protocol_name;__le32           bcs_signature;u8            subclass;u8         protocol;u8         max_lun;u8          ifnum;       /* interface number   */u8         ep_bInterval;    /* interrupt interval */ /* function pointers for this device */trans_cmnd     transport;   /* transport function     */trans_reset        transport_reset; /* transport device reset */proto_cmnd     proto_handler;   /* protocol handler       *//* SCSI interfaces */struct scsi_cmnd  *srb;        /* current srb     */unsigned int      tag;         /* current dCBWTag */char          scsi_name[32];   /* scsi_host name  *//* control and bulk communications data */struct urb      *current_urb;    /* USB requests     */struct usb_ctrlrequest   *cr;         /* control requests     */struct usb_sg_request    current_sg;  /* scatter-gather req.  */unsigned char        *iobuf;      /* I/O buffer       */dma_addr_t       iobuf_dma;   /* buffer DMA addresses */struct task_struct   *ctl_thread;     /* the control thread   *//* mutual exclusion and synchronization structures */struct completion   cmnd_ready;  /* to sleep thread on      */struct completion notify;      /* thread begin/end        */wait_queue_head_t delay_wait;  /* wait during reset       */struct delayed_work   scan_dwork;  /* for async scanning      *//* subdriver information */void           *extra;      /* Any extra data          */extra_data_destructor extra_destructor;/* extra data destructor   */
#ifdef CONFIG_PMpm_hook         suspend_resume_hook;
#endif/* hacks for READ CAPACITY bug handling */int         use_last_sector_hacks;int           last_sector_retries;
};

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

storage_probe中:

 struct us_unusual_dev *unusual_dev;size = ARRAY_SIZE(us_unusual_dev_list);if (id >= usb_storage_usb_ids && id < usb_storage_usb_ids + size) {unusual_dev = (id - usb_storage_usb_ids) + us_unusual_dev_list;} else {unusual_dev = &for_dynamic_ids;dev_dbg(&intf->dev, "Use Bulk-Only transport with the Transparent SCSI protocol for dynamic id: 0x%04x 0x%04x\n",id->idVendor, id->idProduct);}

usb_storage_usb_ids和us_unusual_dev_list中的元素是一一对应的,元素个数也是一样多的,所以这里通过id在usb_storage_usb_ids数组中的位置去找到对应的us_unusual_dev_list中的元素并把地址赋值给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文件,里面都一个个类似下面格式的宏替换,此文件包含了大多数usb设备的一些参数,(需要注意,最后的一个宏替换后面接着一个逗号,用于接数组后面的"{}"):

711 /* Submitted by Hartmut Wahl <hwahl@hwahl.de>*/
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),

由此可知,这个数组的作用就是添加了所有类型设备的参数,总线正是用它(这些参数)来匹配设备和驱动的,后面的一个空内容项{}应该是用来判断数组是否结束或者其它用途的。

#define USB_DEVICE_VER(vend, prod, lo, hi) \.match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, \.idVendor = (vend), \.idProduct = (prod), \.bcdDevice_lo = (lo), \.bcdDevice_hi = (hi)/** The table of devices*/
#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \vendorName, productName, useProtocol, useTransport, \initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \.driver_info = (flags) }//这里实际上就是为usb_storage_usb_ids数组元素结构中的6个元素赋值
#undef UNUSUAL_DEV
#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \vendorName, productName, useProtocol, useTransport, \initFunction, flags) \
{                   \.vid   = id_vendor,       \.pid   = id_product,      \.bcdmin    = bcdDeviceMin,        \.bcdmax = bcdDeviceMax,       \
}
//这里实际上是为us_unusual_dev_list数组元素结构中的4个元素赋值

首先不去管各项的具体含义,至少我们看出来,针对同一个文件,我们使用两次定义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中:

struct usb_device_id usb_storage_usb_ids[] = {
#   include "unusual_devs.h"{ }       /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, usb_storage_usb_ids);static struct us_unusual_dev us_unusual_dev_list[] = {
#   include "unusual_devs.h"{ }       /* Terminating entry */
};

而我们之所以使用两个数组的原因是,storage_usb_ids是提供给usb core的,它需要比较driver和device从而确定设备是被这个driver所支持的,我们只需要比较四项就可以了,因为这四项已经足以确定一个设备了,厂商,产品,序列号.比较这些就够了,而us_unusual_dev_list这个数组中的元素是我们接下来的代码要用的,比如它用什么协议,它有什么初始化函数,所以我们使用了两个数组。

/* Storage subclass codes */#define USB_SC_RBC   0x01        /* Typically, flash devices */
#define USB_SC_8020 0x02        /* CD-ROM */
#define USB_SC_QIC  0x03        /* QIC-157 Tapes */
#define USB_SC_UFI  0x04        /* Floppy */
#define USB_SC_8070 0x05        /* Removable media */
#define USB_SC_SCSI 0x06        /* Transparent */
#define USB_SC_LOCKABLE 0x07        /* Password-protected */#define USB_SC_ISD200   0xf0        /* ISD200 ATA */
#define USB_SC_CYP_ATACB    0xf1    /* Cypress ATACB */
#define USB_SC_DEVICE   0xff        /* Use device's value *//* Storage protocol codes */#define USB_PR_CBI 0x00        /* Control/Bulk/Interrupt */
#define USB_PR_CB   0x01        /* Control/Bulk w/o interrupt */
#define USB_PR_BULK 0x50        /* bulk only */
#define USB_PR_UAS  0x62        /* USB Attached SCSI */#define USB_PR_USBAT 0x80        /* SCM-ATAPI bridge */
#define USB_PR_EUSB_SDDR09  0x81    /* SCM-SCSI bridge for SDDR-09 */
#define USB_PR_SDDR55   0x82        /* SDDR-55 (made up) */
#define USB_PR_DPCM_USB 0xf0        /* Combination CB/SDDR09 */
#define USB_PR_FREECOM  0xf1        /* Freecom */
#define USB_PR_DATAFAB  0xf2        /* Datafab chipsets */
#define USB_PR_JUMPSHOT 0xf3        /* Lexar Jumpshot */
#define USB_PR_ALAUDA   0xf4        /* Alauda chipsets */
#define USB_PR_KARMA    0xf5        /* Rio Karma */#define USB_PR_DEVICE    0xff        /* Use device's value */

这一段定义了subclass和protocol宏
usb协议中规定,U盘的Subclass是属于US_SC_SCSI的.而其通信协议使用的是Bulk-Only的。

继续分析storage_probe, storage_probe调用usb_stor_probe1

/* First part of general USB mass-storage probing */
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;dev_info(&intf->dev, "USB Mass Storage device detected\n");/** Ask the SCSI layer to allocate a host structure, with extra* space at the end for our private us_data structure.*/host = scsi_host_alloc(&usb_stor_host_template, sizeof(*us));if (!host) {dev_warn(&intf->dev, "Unable to allocate the scsi host\n");return -ENOMEM;}/** Allow 16-byte CDBs and thus > 2TB*/host->max_cmd_len = 16;host->sg_tablesize = usb_stor_sg_tablesize(intf);*pus = us = host_to_us(host);mutex_init(&(us->dev_mutex));us_set_lock_class(&us->dev_mutex, intf);init_completion(&us->cmnd_ready);init_completion(&(us->notify));init_waitqueue_head(&us->delay_wait);INIT_DELAYED_WORK(&us->scan_dwork, usb_stor_scan_dwork);/* Associate the us_data structure with the USB device */result = associate_dev(us, intf);if (result)goto BadDevice;/* Get the unusual_devs entries and the descriptors */result = get_device_info(us, id, unusual_dev);if (result)goto BadDevice;/* Get standard transport and protocol settings */get_transport(us);get_protocol(us);/* Give the caller a chance to fill in specialized transport* or protocol settings.*/return 0;BadDevice:usb_stor_dbg(us, "storage_probe() failed\n");release_everything(us);return result;
}
EXPORT_SYMBOL_GPL(usb_stor_probe1);

其中associate_dev所做的事情就是为us的各成员赋值,毕竟此刻us和我们之前提到的那些struct usb_device啊,struct usb_interface啊,还没有一点关系.因而,这个函数,以及这之后的好几个函数都是为了给us的各成员赋上适当的值。

整个usb-storage模块的代码中,其最灵魂的部分在一个叫做usb_stor_control_thread()的函数中,而那也自然是我们整个故事的高潮.这个函数的调用有些特殊,我们是从usb_stor_acquire_resources()函数进入的,而后者我们即将遇到,它在整部戏中只出现过一次,然而在此之前,有四个函数挡在我们面前,它们就是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。
get_device_info:获取设备信息
get_transport: 获取传输方式
get_protocol:获取传输协议
get_pipes:获得管道

至此,get_pipes函数结束了,信息都保存到了us里面.下面us该发挥她的作用了.回到storage_probe()函数,998行,把us作为参数传递给了usb_stor_acquire_resources()函数.而这个函数才是故事的高潮。

static int usb_stor_acquire_resources(struct us_data *us)
{struct task_struct *th;pr_info("usb --- usb_stor_acquire_resources\n");us->current_urb = usb_alloc_urb(0, GFP_KERNEL);if (!us->current_urb) {pr_info("URB allocation failed\n");return -ENOMEM;}/* Start up our control thread */th = kthread_run(usb_stor_control_thread, us, "eucr-storage");if (IS_ERR(th)) {pr_info("Unable to start control thread\n");return PTR_ERR(th);}us->ctl_thread = th;return 0;
}

别看usb_stor_acquire_resources的代码不多,每一行都有每一行的故事.本节我们只讲其中的一行代码,没错,就是一行代码,因为我们需要隆重推出一个名词,一个响当当的名字,她就是传说中的urb,全称usb request block。usb设备需要通信,要传递数据,就需要使用urb,确切的说,应该是usb设备驱动程序使用urb.实际上,作为usb设备驱动,它本身并不能直接操纵数据的传输,在usb这个大观园里,外接设备永远都是配角,真正的核心只是usb core,而真正负责调度的是usb host controller,这个您通常看不见的usb主机控制器芯片,他俨然是usb大观园中的大管家.设备驱动要发送信息,所需要做的是建立一个urb数据结构,并把这个数据结构交给核心层,而核心层会为所有设备统一完成调度,而设备在提交了urb之后需要做的,只是等待,等待,在那漫漫长夜中等待。
如果说struct us_data是usb mass storage中的主角,那么struct urb将毫无争议的成为整个usb子系统中的主角. Linux中所有的usb设备驱动,都必然也必须要使用urb。

struct urb {/* private: usb core and host controller only fields in the urb */struct kref kref;      /* reference count of the URB */void *hcpriv;           /* private data for host controller */atomic_t use_count;       /* concurrent submissions counter */atomic_t reject;        /* submissions will fail */int unlinked;            /* unlink error code *//* public: documented fields in the urb that can be used by drivers */struct list_head urb_list; /* list head for use by the urb's* current owner */struct list_head anchor_list;   /* the URB may be anchored */struct usb_anchor *anchor;struct usb_device *dev;      /* (in) pointer to associated device */struct usb_host_endpoint *ep;    /* (internal) pointer to endpoint */unsigned int pipe;      /* (in) pipe information */unsigned int stream_id;      /* (in) stream ID */int status;         /* (return) non-ISO status */unsigned int transfer_flags;   /* (in) URB_SHORT_NOT_OK | ...*/void *transfer_buffer;      /* (in) associated data buffer */dma_addr_t transfer_dma;   /* (in) dma addr for transfer_buffer */struct scatterlist *sg;      /* (in) scatter gather buffer list */int num_mapped_sgs;        /* (internal) mapped sg entries */int num_sgs;          /* (in) number of entries in the sg list */u32 transfer_buffer_length;  /* (in) data buffer length */u32 actual_length;     /* (return) actual transfer length */unsigned char *setup_packet;   /* (in) setup packet (control only) */dma_addr_t setup_dma;     /* (in) dma addr for setup_packet */int start_frame;        /* (modify) start frame (ISO) */int number_of_packets;      /* (in) number of ISO packets */int interval;           /* (modify) transfer interval* (INT/ISO) */int error_count;     /* (return) number of ISO errors */void *context;           /* (in) context for completion */usb_complete_t complete;   /* (in) completion routine */struct usb_iso_packet_descriptor iso_frame_desc[0];/* (in) ISO ONLY */
};

此刻,我们暂时不去理会这个结构体每一个元素的作用,只需要知道,一个urb包含了执行usb传输所需要的所有信息.而作为驱动程序,要通信,就必须创建这么一个数据结构,并且为她赋好值,显然不同类型的传输,需要对urb赋不同的值,然后将她提交给底层,完了底层的usb core会找到相应的usb host controller,从而具体去实现数据的传输,传输完了之后,usb host controller会通知设备驱动程序。
接下来我们就将和us->current_urb打交道了.如果你对urb究竟怎么用还有些困惑的话,可以看看host controller的代码,如果你不想看,那么我可以用一种你最能接受的方式告诉你,usb是一种总线,是总线它就要通信,我们现实生活中真正要使用的是设备,但是光有设备还不足以实现usb通信,于是世界上有了usb host controller(usb主机控制器),它来负责统一调度,这就好比北京的交警,北京这个城市里真正需要的本来是车辆和行人,而光有车辆和行人,没有交警,那么这个城市里的车辆和行人必将乱套,于是诞生了交警这么一个行业,交警站在路口统一来管理调度那混乱的交通.假如车辆和行人可以完全自觉遵守某种规矩而来来往往于这个城市的每一个角落,每一个路口,那么交警就没有必要存在了.同样,假如设备能够完全自觉的传递信息,每一个数据包都能到达它应该去的地方,那么我们根本就不需要有主机控制器这么一个东西.然而,事实是,在北京,遵守交通规则的人不多,我相信每一个来过北京的人都会发现,在北京,闯红灯是一种时尚.我常常对自己说,如果哪一天我没有闯红灯,那么由此可以推导出来,这一天我一定没有出门.同样,在usb的世界中,设备也总是那么的不守规矩,我们必须要设计一个东西出来管理来控制所有的usb设备的通信,这样,主机控制器就横空出世了. 那么设备和主机控制器的分工又是如何呢?硬件实现上我们就不说了,说点具体的,Linux中,设备驱动程序只要为每一次请求准备一个urb结构体变量,把它填充好,(就是说赋上该赋的值)然后它调用usb core提供的函数,把这个urb传递给host controller,host controller就会把各个设备驱动程序所提交的urb统一规划,去执行每一个操作.而这期间,usb设备驱动程序通常会进入睡眠,而一旦host controller把urb要做的事情给做完了,它会调用一个函数去唤醒usb设备驱动程序,然后usb设备驱动程序就可以继续往下走了.这又好比我们学校里的师生关系.考试的时候,我们只管把试卷填好,然后我们交给老师,然后老师拿去批改试卷,这期间我们除了等待别无选择,等待老师改完了试卷,告诉了我们分数,我们又继续我们的生活.当然了,如果成绩不好,也许有些人的生活就不会继续了,在复旦,我们可以选择去曾经的绝情谷上吊,可以选择去第四教学楼顶层跳下来,在交大,则可以去跳思源湖.同样,usb设备驱动程序也是如此,如果urb提交给usb host了,但是最终却没有成功执行,那么也许该usb设备驱动程序的生命也就提前结束.不过这都是后话,现在只要有个感性认识即可,稍后看到了就能更深刻的体会了,这种岗位分工的方式给我们编写设备驱动程序带来了巨大的方便。
在get_transport()函数中,我们针对各种情况给us->max_lun赋了值,但是我们当时就注意到了,唯独对于Bulk-Only的设备,当时并没有赋值,所以这里我们看到,对于us->protocol等于US_PR_BULK的情况,有一个专门的函数来获得这个设备的max lun.网友女为悦己者_整_容好奇的问,为什么写代码的同志在这里对Bulk-Only的设备表现出一种”偏偏喜欢你”的态度呢?没什么特别的,党中央规定的.所有的usb设备遵守一个规范,这个规范叫做usb spec,而usb设备分为很多种类,usb mass storage是其中一类,而mass storage设备又分为很多子类,每个子类又有它自己的规范,比如U盘它所遵守的就是usb
72mass storage class bulk-only transport spec.而这个规范说得很清楚,对于这种设备,它的lun不是凭感觉就能判断的,你得发送一个命令给它,向它去查询,然后它会做出响应,这样你才能知道它究竟是几个lun.这条命令就是”GET MAX LUN”,详见spec 3.2.

所以我们即使不认真看这个函数也可以知道究竟发生了什么.而且我们之前还说过,普通的U盘的max lun肯定是0.对于那种读卡器才可能有多个lun.不过我们还是不妨来深入的看一下这个函数,毕竟这个函数是在drivers/usb/storage/transport.c中,属于我们的辖区.当然更重要的是,此前我们一直没有真正见识过究竟一次usb传输是怎么回事,作为usb设备驱动程序究竟如何发起usb传输,而这个函数正好给了我们一次不错的机会.同时我们也将会知道,了解了一次控制传输之后,别的传输也会很容易理解. 首先我们知道这次传输的目的是为了获得一个数字,max lun,而获得这个数据的方式是发送命令,所以整个过程就是,你发送一个命令给设备,设备返回一个值给你,这么简单的传输叫什么?控制传输.很显然,地球人都知道,控制传输是usb四种传输方式中最简单的那种.来看具体代码:

int usb_stor_Bulk_max_lun(struct us_data *us)
{int result;/* issue the command */us->iobuf[0] = 0;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);usb_stor_dbg(us, "GetMaxLUN command result is %d, data is %d\n",result, us->iobuf[0]);/* if we have a successful request, return the result */if (result > 0)return us->iobuf[0];/** Some devices don't like GetMaxLUN.  They may STALL the control* pipe, they may return a zero-length result, they may do nothing at* all and timeout, or they may fail in even more bizarrely creative* ways.  In these cases the best approach is to use the default* value: only one LUN.*/return 0;
}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;/* pr_info("transport --- usb_stor_control_msg\n"); *//* fill in the devrequest structure */us->cr->bRequestType = requesttype;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);/* fill and submit the URB */usb_fill_control_urb(us->current_urb, us->pusb_dev, pipe,(unsigned char *) us->cr, data, size,usb_stor_blocking_completion, NULL);status = usb_stor_msg_common(us, timeout);/* return the actual length of the data transferred if no error */if (status == 0)status = us->current_urb->actual_length;return status;
}static int usb_stor_msg_common(struct us_data *us, int timeout)
{struct completion urb_done;long timeleft;int status;/* pr_info("transport --- usb_stor_msg_common\n"); */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 = 0;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);if (status)return status;set_bit(US_FLIDX_URB_ACTIVE, &us->dflags);if (test_bit(US_FLIDX_ABORTING, &us->dflags)) {if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags)) {/* pr_info("-- cancelling URB\n"); */usb_unlink_urb(us->current_urb);}}timeleft = wait_for_completion_interruptible_timeout(&urb_done,timeout ? : MAX_SCHEDULE_TIMEOUT);clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags);if (timeleft <= 0) {/* pr_info("%s -- cancelling URB\n",timeleft == 0 ? "Timeout" : "Signal"); */usb_kill_urb(us->current_urb);}return us->current_urb->status;
}

好了,代码贴完了,现在让我们一点一点去认识这段代码.记住我们最终就是为了看清楚usb_stor_Bulk_max_lun()这个函数究竟是怎么获得max lun的。
对于设备驱动程序而言,控制传输要做的事情很简单,向usb core提交一个urb,这个urb中间包含了一个命令,或者说控制请求,因为命令更适合于我们后来要讲的某个重要的概念.这里我们要发送的就是GET MAX LUN.我们调用了一个函数, usb_stor_control_msg,其作用从名字上也可以看出,发送控制信息,即控制请求.做一件事情要遵守一件事情的规矩,发送控制信息必须按照一定的格式,发出去了人家才能看得懂.就像你要给远方的恋人寄一封信,你要在信封上按基本格式填写一些东西,你的信才能被寄出去对吗。

truct usb_ctrlrequest   *cr;              /* control requests      */

她是struct usb_ctrlrequest结构指针, usb规范规定了一个控制请求的格式为一个8个字节的数据包,而struct usb_ctrlrequest正是专门为此而准备的8个字节的一个变量,所以控制传输中总会用到她.她的定义在include/linux/usb_ch9.h中:

struct usb_ctrlrequest {__u8 bRequestType;__u8 bRequest;__le16 wValue;__le16 wIndex;__le16 wLength;
} __attribute__ ((packed));

不过需要说明一点,在usb spec中,不叫cr,而叫setup packet,在struct urb里边就有这么一个名字一样的成员。
因为在Linux中usb的世界里一个通用的数据结构是urb,很多函数都是以urb为参数的,所以稍后我们实际上还是会让urb中的setup_packet指针指向这个usb_ctrlrequest结构体的.毕竟我们最终要提交的就是urb,而不是cr.并且cr只是出现在控制传输,而urb却是四大传输都要用到的。
Ok,在usb mass storage bulk only transport协议里面,规定的很清楚,要发送GET MAX LUN请求,必须把bmRequestType设置为device to host,class,interface,同时把bRequest设置为254(FEh),即咱们这里的0xfe,而wValue设置为0,wIndex设置为接口的编号,最后wLength设置为1。
需要解释一下,什么叫bmRequestType设置device to host,class,interface?实际上,usb 2.0规范里面指出,所有的usb设备都会响应主机的一些请求,这些请求必须来自主机,通过设备的默认控制管道发出.(即0号端点所对应的那个管道)这些请求,或者说这些request,是通过控制传输来实现的,请求以及请求所需的参数都是通过setup packet来发送的.主机负责建立好这个setup packet.(也就是咱们刚才的那个cr,后来的setup_packet.)每个setup packet包含8个bytes.她们的含义如下: byte0: bmRequestType,注意,在刚才咱们代码中数据结构struct ctrlrequest里边是写的bRequestType,但是她们对应的是相同的冬冬.而之所以usb协议里写成bmRequestType,是因为她实际上又是一个位图(m表示map),也就是说,尽管她只有1个byte,但是她仍然是当作8位来用.她的8位的含义是: D7: 控制传输数据传输的方向,0表示方向为主机到设备,1表示方向为设备到主机.(有时控制传输除了发命令阶段以外,可能还有数据传输阶段,此处表示的是在数据传输那个阶段的传输方向.) D6…5: 请求的类型,0称为标准类型(Standard),1称为Class,2称为Vendor,3则是保留的,不能使用. D4…0: 接收者,0表示设备,1表示接口,2表示端点,3表示其它,4…31都是保留的,不能使用. 所以,bmRequestType被设device to host,class,interface,表示,数据传输阶段传输方向是设备到主机,请求的类型是Class,意思是,请求类型可以有好几种,首先usb spec本身定义了很多种标准请求,所有的usb设备都必须支持这些标准请求,或者或标准request,另一方面,一类设备或者厂商自己也可以定义自己的额外的一些请求,显然这里get max lun就是usb mass storage这个类,或者说这个class所定义的请求,因此,请求类型设置为class,而request接收者可以是设备,也可以是接口,还可以是端点,对get max lun这个命令来说,她是针对一个u盘的,所以她的接收者应该是interface. byte1: bRequest,1个byte,指定了是哪个请求.每一个请求都有一个编号,咱们这里是GET MAX LUN,其编号是FEh. byte2…3: wValue,2个bytes,不同请求有不同的值,咱们这里刚才已经说了,必须为0.
78 byte4…5: wIndex,2个bytes,不同请求有不同的值,咱们这里要求被设置为是interface number. byte6…7: wLength,2个bytes,如果接下来有数据传输阶段,则这个值表征了数据传输阶段传输多少个bytes,没啥好说的,这个值在GET MAX LUN请求中被规定为1,也就是说返回1个byte即可。

结合函数调用的实参和函数的定义,可见,对于这个cr来说,其bRequest被赋值为US_BULK_GET_MAX_LUN,bRequestType被赋值为USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE,而wValue被设为0,wIndex被设置为us->ifnum, 推开记忆的门,我们发现在associate_dev()函数中,us->ifnum被赋值为intf->cur_altsetting->desc.bInterfaceNumber,即她确实对应了interface number. wLength被赋值为1.设置好cr之后,就可以把她传递给urb的setup_packet了.对比上面的usb_stor_control_msg()函数中第232行,和下面函数usb_fill_control_urb()的定义,即可看出urb的setup_packet指针指向了这个cr.具体来看usb_fill_control_urb()函数,这个函数定义于include/linux/usb.h中:

static inline void usb_fill_control_urb(struct urb *urb,struct usb_device *dev,unsigned int pipe,unsigned char *setup_packet,void *transfer_buffer,int buffer_length,usb_complete_t complete_fn,void *context)
{urb->dev = dev;urb->pipe = pipe;urb->setup_packet = setup_packet;urb->transfer_buffer = transfer_buffer;urb->transfer_buffer_length = buffer_length;urb->complete = complete_fn;urb->context = context;
}

此处特意提一下usb_complete_t类型,在include/linux/usb.h中,有这么一行,
typedef void (*usb_complete_t)(struct urb *, struct pt_regs *);
这里用了typedef来简化声明,不熟悉typedef功能的可以去查一下,typedef的强大使得以下两种声明作用相同:
一种是:
void (*func1)(struct urb *,struct pt_regs *);
void (*func2)(struct urb *,struct pt_regs *);
void (*func3)(struct urb *,struct pt_regs *);
另一种是:
typedef void (*usb_complete_t)(struct urb *, struct pt_regs *);
usb_complete_t func1;
usb_complete_t func2;
usb_complete_t func3;
看出来了吧,如果要声明很多个函数指针,她们的参数又都很复杂,那么显然使用typedef一次,就可以一劳永逸了,以后声明就很简单了.所以,咱们也就知道实际上,urb中的complete是一个函数指针,她被设置为指向函数usb_stor_blocking_completion().关于usb_stor_blocking_completion()咱们暂且不提,等到用的时候再说。
看完了usb_fill_control_urb,咱们再回到usb_stor_control_msg()函数,接下来是下一个函数,usb_stor_msg_common(),注意了,usb_fill_control_urb这个函数只填充了urb中的几个元素,struct urb里面包含了很多东西,不过有一些设置是共同的,所以下面用usb_stor_msg_common()函数来设置,可以看出给这个函数传递的参数只有两个,一个就是us,另一个是timeout(传给她的值是HZ)。
而这个函数支持各种传输,作为usb-storage模块,无论其采用何种传输方式,无论传输数据量的多少,最终都一定要调用这个函数,所以我们必须承认这个函数很伟大。

设备驱动程序,只需要提交一个urb就可以了,剩下的事情usb core会去处理,有了结果它会通知我们.而提交urb,usb core为我们准备了一个函数,usb_submit_urb()不管我们使用什么传输方式,我们都只要调用这个函数即可,在此之前,我们需要做的只是准备好这么一个urb,把urb中各相关的成员填充好,然后就ok了.而这usb_stor_msg_common正是这样做的.而显然,不同的传输方式其填写urb的方式也不同。

USB U盘Linux mass_storage驱动学习笔记(1)相关推荐

  1. linux设备驱动学习笔记(1)

    学习了将近半个月的设备驱动程序的编写,也有一些体会,这里写下来也给学习做一个总结,为后面的学习做更好的准备. 首先,个人感觉驱动程序的设计是很有套路的,最基本的要求就是要掌握这些套路.所谓的套路就是一 ...

  2. 【嵌入式环境下linux内核及驱动学习笔记-(16)linux总线、设备、驱动模型之input框架】

    目录 1.Linux内核输入子系统概念导入 1.1 输入设备工作机制 1.2 运行框架 1.3 分层思想 2.驱动开发步骤 2.1 在init()或probe()函数中 2.2 在exit()或rem ...

  3. Linux驱动学习笔记

    驱动学习笔记 1.字符设备驱动 Linux 驱动有两种运行方式 第一种就是将驱动编译进 Linux 内核中,这样当 Linux 内核启 动的时候就会自动运行驱动程序. 第二种就是将驱动编译成模块(Li ...

  4. 【嵌入式环境下linux内核及驱动学习笔记-(15-1)例程】

    目录 1.在APP直接调用标准文件IO操作I2C(针对学习笔记-15的15.3节) 1.1 mail.c 1.2 mpu6050.h 1.3 mpu6050.c 1.4 Makefile 2.以外称i ...

  5. Linux 驱动学习笔记 - beep(九)

    Linux 驱动学习笔记 - beep(九) 本系列均为正点原子 Linux 驱动的学习笔记, 以便加深笔者记忆.如读者想进一步学习,可以到正点原子官网中下载资料进行学习. 添加 pinctrl 节点 ...

  6. KALI LINUX渗透测试学习笔记

    KALI LINUX渗透测试学习笔记 (苑房弘主讲) 第1章 课程介绍 任务1:Kali Linux渗透测试介绍.exe 安全问题的根源: 分层思想 只求功能实现 最大的威胁是人 渗透测试: 尝试挫败 ...

  7. [Linux网络编程学习笔记]索引

    一.Linux基本知识 [学习笔记]Linux平台的文件I/O操作 [学习笔记]Linux平台的文件,目录及操作 [Linux学习笔记]标准输入输出 [Linux学习笔记]进程概念及控制 [Linux ...

  8. linux设备驱动读书笔记

    linux设备驱动读书笔记 设备驱动简介 机制:提供什么能力 策略:如何使用这些能力 在编写驱动时, 程序员应当编写内核代码来存取硬件, 但是不能强加特别的策略给用户, 因为不同的用户有不同的需求. ...

  9. linux v4l2系统详解,Linux摄像头驱动学习之:(一)V4L2_框架分析

    这段时间开始搞安卓camera底层驱动了,把以前的的Linux视频驱动回顾一下,本篇主要概述一下vfl2(video for linux 2). 一. V4L2框架: video for linux ...

最新文章

  1. python代码该怎么简化_如何简化这个python代码(从书中分配)?
  2. icmp反弹shell 简介
  3. matlab cameraman,cameraman.tif 原图
  4. Qt Quick入门
  5. 【笔试题】简单的两道笔试题(1、打印杨辉三角;2、三个数排序)
  6. [C++ Mind Map] class and memory
  7. 机器学习--最基础的最常用的聚类算法
  8. 某虚拟化项目中思科与华为交换机链路聚合互连案例
  9. 通过 powershell 配置 IIS
  10. C++,next_permutation(start_array_index,end_index)的使用
  11. 粒子群算法几个适应度评价函数
  12. Word文档字间距怎么调?
  13. C2. Skyscrapers (hard version)(单调栈、dp)
  14. IntelliJ IDEA 配置Jetty启动项目
  15. java newline_Java BufferedWriter newLine()方法
  16. 【Android】图像滤镜框架GPUImage从配置到应用
  17. python-装饰器的使用详解
  18. Red Hat Enterprise Linux 5.1 Server(正式版)各版本下载2011-04-17 22:21
  19. Linux中删除文件夹和文件的命令(☆)
  20. java开源项目CrapApi接口管理系统

热门文章

  1. android一些学习网站
  2. Linux unrar命令如何按目录解压rar文件
  3. 概率论01-样本空间,随机事件
  4. 概率问题的求解 —— 样本空间
  5. Android使用Github Actions持续集成并自动上传apk到蒲公英App内测分发平台(含证书密码脱敏)
  6. 【数据结构】常见数据结构类型
  7. VS编译失败,找不到源文件!!!
  8. 云管平台监控实践分享
  9. Webservice简介
  10. 网站安全不可忽视--一个收费网站的破解过程