发布时间:2014-09-05 16:47:24作者:知识屋
对于整个usb-storage模块,usb_stor_init()函数是它的开始,然而,对于U盘驱动程序来说,它真正驱使U盘工作却是始于storage_probe()函数。
两条平行线只要相交,就注定开始纠缠一生,不管中间是否短暂分离。USB Core为设备找到了适合它的驱动程序,或者为驱动程序找到了它所支持的设备,但这只是表明双方的第一印象还可以,但彼此是否真的适合对方还需要进一步了解。而U盘驱动则会调用函数storage_probe()去认识对方,它是一个什么样的设备?这里调用了四个函数get_device_info,get_protocol,get_transport,get_pipes。
整个U盘驱动这部大戏,由storage_probe开始,由storage_disconnect结束。其中,storage_probe这个函数占了相当大的篇幅。我们一段一段来看。这两个函数都来自drivers/usb/storage/usb.c中:
945 /* Probe to see if we can drive anewly-connected USB device */
946 static int storage_probe(struct usb_interface*intf,
947 const struct usb_device_id *id)
948 {
949 structScsi_Host *host;
950 structus_data *us;
951 intresult;
952 structtask_struct *th;
953
954 if(usb_usual_check_type(id, USB_US_TYPE_STOR))
955 return -ENXIO;
956
957 US_DEBUGP("USB Mass Storagedevice detected/n");
958
959 /*
960 * Ask the SCSI layer to allocate a hoststructure, with extra
961 * space at the end for our privateus_data structure.
962 */
963 host =scsi_host_alloc(&usb_stor_host_template, sizeof(*us));
964 if (!host){
965 printk(KERN_WARNING USB_STORAGE
966 "Unable to allocate the scsi host/n");
967 return -ENOMEM;
968 }
969
970 us =host_to_us(host);
971 me mset(us,0, sizeof(struct us_data));
首先先贴出这么几行,两个参数不用多说了,struct usb_interface和struct usb_device_id的这两个指针都是前面介绍过的,来自USB Core一层,我们整个故事里用到的就是这么一个,这两个指针的指向是定下来的。
950行,最重要的一个数据结构终于出现了。整个usb-storage模块里边自己定义的数据结构不多,但是us_data算一个。这个数据结构是跟随我们的代码一直走下去的,如影随形,几乎处处都可以看见它的身影。先把它的代码“贴”出来,来自drivers/usb/storage/usb.h:
102 /* we allocate one of these for every devicethat we remember */
103 struct us_data {
104 /* Thedevice we're working with
105 * It's important to note:
106 * (o) you must hold dev_mutex to change pusb_dev
107 */
108 structmutex dev_mutex; /* protect pusb_dev */
109 structusb_device *pusb_dev; /* this usb_device */
110 structusb_interface *pusb_intf; /* this interface */
111 structus_unusual_dev *unusual_dev;/* device-filter entry */
112 unsignedlong flags; /*from filter initially */
113 unsignedint send_bulk_pipe; /* cachedpipe values */
114 unsignedint recv_bulk_pipe;
115 unsignedint send_ctrl_pipe;
116 unsignedint recv_ctrl_pipe;
117 unsignedint recv_intr_pipe;
118
119 /*information about the device */
120 char *transport_name;
121 char *protocol_name;
122 __le32 bcs_signature;
123 u8 subclass;
124 u8 protocol;
125 u8 max_lun;
126
127 u8 ifnum; /*interface number */
128 u8 ep_bInterval; /* interrupt interval */
129
130 /*function pointers for this device */
131 trans_cmnd transport; /*transport function */
132 trans_reset transport_reset; /* transport devicereset */
133 proto_cmnd proto_handler; /* protocol handler */
134
135 /* SCSIinterfaces */
136 structscsi_cmnd *srb; /* current srb */
137 unsignedint tag; /* current dCBWTag */
138
139 /*control and bulk communications data */
140 structurb *current_urb; /* USB requests */
141 structusb_ctrlrequest *cr; /* control requests */
142 struct usb_sg_request current_sg; /* scatter-gather req. */
143 unsignedchar *iobuf; /* I/Obuffer */
144 unsignedchar *sensebuf; /* sense data buffer */
145 dma_addr_t cr_dma; /* bufferDMA addresses */
146 dma_addr_t iobuf_dma;
147
148 /* mutualexclusion and synchronization structures */
149 struct semaphore sema; /* to sleepthread on */
150 struct completion notify; /* thread begin/end */
151 wait_queue_head_t delay_wait; /* wait duringscan, reset */
152
153 /*subdriver information */
154 void *extra; /* Any extradata */
155 extra_data_destructor extra_destructor;/*extra data destructor */
156 #ifdef CONFIG_PM
157 pm_hook suspend_resume_hook;
158 #endif
159 };
不难发现,Linux内核中每一个重要的数据结构都很复杂。总之,这个令人头疼的数据结构是每一个设备都有的。换句话说,我们会为每一个设备申请一个us_data,因为这个结构中边的东西我们之后一直会用得着的。950行,structus_data *us,于是,日后我们会非常频繁地看到us的。另,us什么意思?us,即usb storage。
963行,关于usb_stor_host_template,必须得认真看一下,因为这个变量我们以后还会多次碰到。scsi_host_alloc 就是SCSI子系统提供的函数,它的作用就是申请一个SCSI Host相应的数据结构。而它的第一个参数&usb_stor_host_template,其实这是一个struct scsi_host_template的结构体指针,之所以USB Mass Storage里面会涉及SCSI这一层,是因为我们事实上把一块U盘模拟成了一块SCSI设备,对于SCSI设备来说,要想正常工作,得有一个SCSI卡,或者说SCSI Host。而按照SCSI这一层的规矩,要想申请一个SCSI Host的结构体,我们就得提供一个struct scsi_host_template结构体,这其实从名字可以看出,是一个模版,SCSI那层把一切都封装好了,只要提交一个模版给它,它就能为你提供一个structScsi_Host结构体。关于这个usb_stor_host_template,它的定义或者说初始化是在drivers/usb/storage/scsiglue.c中:
441 struct scsi_host_templateusb_stor_host_template = {
442 /* basic userland interface stuff*/
443 .name = "usb-storage",
444 .proc_name= "usb-storage",
445 .proc_info = proc_info,
446 .info= host_info,
447
448 /* command interface -- queued only*/
449 .queuecommand= queuecommand,
450
451 /*error and abort handlers */
452 .eh_abort_handler= command_abort,
453 .eh_device_reset_handler= device_reset,
454 .eh_bus_reset_handler= bus_reset,
455
456 /* queuecommands only, only one command per LUN */
457 .can_queue= 1,
458 .cmd_per_lun= 1,
459
460 /*unknown initiator id */
461 .this_id= -1,
462
463 .slave_alloc= slave_alloc,
464 .slave_configure= slave_configure,
465
466 /* lots ofsg segments can be handled */
467 .sg_tablesize= SG_ALL,
468
469 /*limit the total size of a transfer to 120 KB */
470 .max_sectors= 240,
471
472 /* mergecommands... this see ms to help performance, but
473 * periodically someone should test tosee which setting is more
474 * optimal.
475 */
476 .use_clustering = 1,
477
478 /*emulated HBA */
479 .emulated= 1,
480
481 /*we do our own delay after a device or bus reset */
482 .skip_settle_delay = 1,
483
484 /*sysfs device attributes */
485 .sdev_attrs= sysfs_device_attr_list,
486
487 /* modulemanagement */
488 .module= THIS_MODULE
489 };
按照SCSI层的规矩,要想申请一个SCSI Host,并且让它工作,我们需要调用三个函数,第一个是scsi_host_alloc(),第二个是scsi_add_host(),第三个是scsi_scan_host()。这三个函数我们都会在接下来的代码里面看到。
scsi_host_alloc()一调用,就是给struct Scsi_Host 结构申请了空间, 而只有调用了scsi_add_host()之后,SCSI核心层才知道有这个Host的存在,然后只有scsi_scan_host()被调用了之后,真正的设备才被发现。这些函数的含义和它们的名字吻合的很好。不是吗?
最后需要指出的是,scsi_host_alloc()需要两个参数,第一个参数是structscsi_host_template 的指针,咱们当然给了它&usb_stor_host_template,而第二个参数实际上是被称为驱动自己的数据,咱们传递的是sizeof(*us)。SCSI层非常友好地给咱们提供了一个接口,在struct Scsi_Host结构体被设计时就专门准备了一个unsigned long hostdata[0]来给别的设备驱动使用,这个hostdata的大小是可以由咱们来定,把sizeof(*us)传递给scsi_host_alloc()就意味着给us申请了内存。而今后如果我们需要从us得到相应的SCSI Host就可以使用内联函数us_to_host();而反过来要想从SCSI Host得到相应的us则可以使用内联函数host_to_us(),这两个明显是一对,都定义于drivers/usb/storage/usb.h:
161 /* Convert between us_data and thecorresponding Scsi_Host */
162 static inline struct Scsi_Host*us_to_host(struct us_data *us) {
163 returncontainer_of((void *) us, struct Scsi_Host, hostdata);
164 }
165 static inline struct us_data*host_to_us(struct Scsi_Host *host) {
166 return(struct us_data *) host->hostdata;
167 }
总之咱们这么一折腾,就让USB驱动和SCSI Host联系起来。从此以后这个U盘既要扮演U盘的角色又要扮演SCSI设备的角色。
957行,US_DEBUGP是一个宏,来自drivers/usb/storage/debug.h,接下来很多代码中我们也会看到这个宏,它无非就是打印一些调试信息。debug.h中有这么一段:
51 #ifdef CONFIG_USB_STORAGE_DEBUG
52 void usb_stor_show_command(struct scsi_cmnd*srb);
53 void usb_stor_show_sense( unsigned char key,
54 unsigned char asc, unsigned char ascq );
55 #define US_DEBUGP(x...) printk( KERN_DEBUGUSB_STORAGE x )
56 #define US_DEBUGPX(x...) printk( x )
57 #define US_DEBUG(x) x
58 #else
59 #define US_DEBUGP(x...)
60 #define US_DEBUGPX(x...)
61 #define US_DEBUG(x)
62 #endif
这里一共定义了几个宏,US_DEBUGP,US_DEBUGPX,US_DEBUG,差别不大,只是形式上略有不同罢了。
需要注意的是,这些调试信息得是我们打开了编译选项CONFIG_USB_STORAGE_DEBUG才有意义的,如果这个选项为0,那么这几个宏就什么也不干,因为它们被赋为空了。关于US_DEBUG系列的这几个宏,就讲到这了。
954行,usb_usual_check_type()干什么用的?这个函数来自drivers/usb/storage/libusual.c中:
98 int usb_usual_check_type(const struct usb_device_id*id, int caller_type)
99 {
100 int id_type =USB_US_TYPE(id->driver_info);
101
102 if(caller_type <= 0 || caller_type >= 3)
103 return -EINVAL;
104
105 /* Driversgrab fixed assignment devices */
106 if (id_type == caller_type)
107 return 0;
108 /* Driversgrab devices biased to them */
109 if (id_type==USB_US_TYPE_NONE&& caller_type==atomic_read(&usu_bias))
110 return 0;
111 return -ENODEV;
112 }
这个函数保证现在认领的这个设备属于usb-storage所支持的设备,而不是另一个叫做ub的驱动所支持的设备,如果你足够细心可能会注意到,drivers/block目录下面竟然也有一段与usb相关的驱动代码,它就是drivers/block/ub.c。实际上ub是一个简化版的usb-storage和sd两个模块的结合体,它的功能比较弱,但是要更稳定。如果感兴趣的话可以去读一下ub这个模块的代码
linux一键安装web环境全攻略 在linux系统中怎么一键安装web环境方法
Linux网络基本网络配置方法介绍 如何配置Linux系统的网络方法
Linux下DNS服务器搭建详解 Linux下搭建DNS服务器和配置文件
对Linux进行详细的性能监控的方法 Linux 系统性能监控命令详解
linux系统root密码忘了怎么办 linux忘记root密码后找回密码的方法
Linux基本命令有哪些 Linux系统常用操作命令有哪些
Linux必学的网络操作命令 linux网络操作相关命令汇总
linux系统从入侵到提权的详细过程 linux入侵提权服务器方法技巧
linux系统怎么用命令切换用户登录 Linux切换用户的命令是什么
在linux中添加普通新用户登录 如何在Linux中添加一个新的用户
2012-07-10
CentOS 6.3安装(详细图解教程)
Linux怎么查看网卡驱动?Linux下查看网卡的驱动程序
centos修改主机名命令
Ubuntu或UbuntuKyKin14.04Unity桌面风格与Gnome桌面风格的切换
FEDORA 17中设置TIGERVNC远程访问
StartOS 5.0相关介绍,新型的Linux系统!
解决vSphere Client登录linux版vCenter失败
LINUX最新提权 Exploits Linux Kernel <= 2.6.37
nginx在网站中的7层转发功能