CSK.Blog--个人原创Weblog

« Intercessor-Super Task Manager开始实验室生活 »

Linux下USB MASS STORAGE驱动的分析和改造

这原本是我这学期OS课程设计最后一次的作业,因为比较有意思,所以就把它公布出来吧。

作业要求:

开发Linux下的usb存储设备的驱动,仅需支持自己的u盘即可。

下面是我写的驱动,他基于linux下自带的usb-storage驱动(/driver/usb/storage)。仅支持基于Bulk-only传输模式下的ATAPI协议的存储设备。我使用自己的SAMSUNG D828手机的usb mass storage功能测试成功。

使用方法是insmod。

 

代码:

http://www.csksoft.net/data/legacyftp/Products/code_and_lib/csk_udisk_mgr.rar

 

下面是这次作业报告的节选,希望对需要研究usb-storage或者想自己开发linux u盘驱动(虽然那已经没有必要了)的朋友有帮助。这次报告的原文后面给出的地址。

 


 

1.USB MASS STORAGE 协议分析

Sub Class
协议名
说明
0x01
Reduced Block Commands(RBC)
 
通常为Flash Rom介质的存储设备使用
0x02
8020i, MMC-2(ATAPI)
通常为CD/DVD设备使用
0x03
QIC-157
常用于磁带机设备
0x04
UFI
常用于软磁盘设备(FDD)
0x05
8070i
常用于软磁盘设备或者其他设备
0x06
SCSI协议
 
0x07-0xFF
保留
 

摘录至 Universal Serial Bus, Mass Storage Class Specification Overview, P6

 

 Sub Class可以在usb设备连入系统后获取。不同的通讯协议决定了usb驱动要用不同的命令和数据包格式和u盘通讯。
 同时,白皮书还规定了u盘设备的通讯方式,见下表:

 

接口协议号
协议名
0x00
Control/Bulk/Interrupt 协议(CBI)
带有command completion中断
0x01
Control/Bulk/Interrupt 协议(CBI)
不带有command completion中断
0x50
Bulk-Only协议
0x02-0x4F
保留
0x51-0xFF
保留

摘录至 Universal Serial Bus, Mass Storage Class Specification Overview, P7
 
         接口协议号即InterfaceProtocol字段,在usb设备插入后可以从总线上获取。
         在实际usb mass storage设备工作时,设备驱动首先要做的是获得该u盘的通讯协议和通讯方式,然后按照需要产生与目标设备兼容的控制命令,并将该控制命令打包通过设备的通讯传输方式发送至设备,完成一次对设备的读写操作。
         对于上述6中数据通讯协议,均有很详细的说明文档,经过分析,他们的命令有很大的相似之处,这也是通讯usb驱动能够实现的原因之一。

 

2.自带驱动usb-storage的简要原理分析

linux内核中自带的usb mass storage驱动位于内核源代码目录/drivers/usb/storage/下,下表为该目录下各文件的功能说明:

文件
功能说明
usb.c/.h
Usb-storage的核心文件,是整个驱动的框架代码
transport.c/.h
实现了对于不同通讯方式的支持函数
scsiglue.c/.h
Scsi设备的模拟函数
protocol.c/.h
实现了对于几种通讯协议的SCSI命令翻译函数
initializers.c/.h
对于某些设备的专用初始化函数
unusual_devs.h
对于非常规设备ProductIDVendorID的支持
shuttle_usbat.c/.h
支持SCM Microsystems设备的驱动
sddr55.c/.h
SanDisk SDDR-55 SmartMedia reader的驱动
sddr09.c/.h
anDisk SDDR-09 SmartMedia reader的驱动
onetouch.c/.h
Maxtor OneTouch USB hard drive驱动支持
libusual.c
对于常规设备的ProductIDVendorID的支持
karma.c/.h
Rio Karma设备驱动
jumpshot.c/.h
Lexar "Jumpshot" Compact Flash reader驱动
isd200.c/.h
ISD200专属通讯协议支持
freecom.c/.h
Freecom USB/IDE 转化器支持
dpcm.c/.h
DPCM-USB CompactFlash/SmartMedia reader设备支持
debug.c/.h
用于调试的工具函数
datafab.c/.h
Datafab USB Compact Flash reader驱动支持
alauda.c/.h
Alauda-based card readers驱动支持

 
         通过上述文件作用分析后,可以看出,对于常规的usb设备,大部分代码文件都是多余的,核心文件为上表的前4项。这就为进一步分析驱动已经对其进行简化和仿制提供了可能。
         通过对核心代码的分析,本驱动的工作机理是将自身模拟为标准的SCSI设备,并向scsi管理器注册,这样对于上层系统而言,只需操作标准的SCSI设备即可。这样可以简化具体的文件读写功能。同时驱动接受到的SCSI命令转化为对应u盘设备的通讯协议,并用对应设备的通讯方式进行发送,并将结果回馈到SCSI管理器。
         下图展示了整个驱动的工作布局:
图中Transfer layer和protocol layer分别对应了transfer.c和protocol.c文件,Command Transfer Thread是usb.c在探测到有新设备接入后加载的线程,他将不断轮询SCSI发来的消息命令,并负责将这些命令通过protocol layer提供的函数翻译并发送到u盘上,完成对u盘的读写操作。
         Delayed Device Scan是为了防止用户在设备插入后马上拔除,造成驱动在后续通讯中造成混乱。实现方式是创建一个专门线程。
         同时,usb.c在设备插入是会通过scsiglue.c提供的SCSI接口函数向SCSI管理器注册自身。
 

3.对usb-storage的简化

经过上面对usb-storage的分析,可以将该驱动代码作如下方面的简化,方便后续的分析和驱动编写。
  1. 删除其中对罕见设备的支持
  2. 删除Delayed Device Scan机制
  3. 删除对于除了本次试验所用u盘设备外其他的通讯协议和通讯方式的处理函数。
  4. 对于仅调用一次的函数作inline处理

 

其中,每一项操作都会影响到原先的函数依赖关系,因而需要在删除代码或者文件前弄清楚其中的依赖关系。

 

按照原理中提到的步骤,首先是删除其对罕见驱动和专属驱动的支持。最终将保留下上面表格中前4项文件。
         同时处理usb.c对这些代码的引用,主要是在如下代码段中:
126 #ifndef CONFIG_USB_LIBUSUAL
127
128 #define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
129                     vendorName, productName,useProtocol, useTransport, \
130                     initFunction, flags) \
//...//
181 #       undef USUAL_DEV
182
183         /* Terminating entry */
184         { NULL }
185 };
186
187
188 #ifdef CONFIG_PM        /* Minimal support for suspend and resume */
代码:原始usb.c中代码片断
 
         在这个区间中的代码将设置对不同设备,包括罕见设备ProductID和VendorID的支持。可以改为仅对D828设备的支持:
//SAMSUNG MOBILE USB INFO
#define USB_MASS_VENDOR_ID 0x04e8
#define USB_MASS_PRODUCT_ID        0x665c 
 
//REG IT
static struct usb_device_id storage_usb_ids [] = {
         { USB_DEVICE(USB_MASS_VENDOR_ID, USB_MASS_PRODUCT_ID) },
         { }                                          /* Terminating entry */
};
 
MODULE_DEVICE_TABLE (usb, storage_usb_ids);
代码:替换过usb.c后的代码
 
         当然,在后续的代码中,仍需要进行修改,比如us_unusual_dev_list[]在后续的引用也需要删除。
        
         在经过第一个简化操作后,便可以对D828协议以外的处理函数进行删除。在protocol.c中存在如下几个函数:
void usb_stor_qic157_command(struct scsi_cmnd *srb, struct us_data *us);
void usb_stor_ATAPI_command(struct scsi_cmnd *srb, struct us_data *us);
void usb_stor_ufi_command(struct scsi_cmnd *srb, struct us_data *us);
void usb_stor_transparent_scsi_command(struct scsi_cmnd *srb, struct us_data *us);
代码:protocol.c中提供的函数原形
 
         这正是针对白皮书中规定的4种不同的通讯协议的处理函数,而他们是通过usb.c中如下代码片断和当前设备进行联系的:
671 /* Get the protocol settings */
672 static int get_protocol(struct us_data *us)
673 {
674         switch (us->subclass) {
675         case US_SC_RBC:
676                 us->protocol_name = "Reduced Block Commands (RBC)";
677                 us->proto_handler = usb_stor_transparent_scsi_command;
678                 break;
679
680         case US_SC_8020:
681                 us->protocol_name = "8020i";
682                 us->proto_handler = usb_stor_ATAPI_command;
683                 us->max_lun = 0;
684                 break;
代码:usb.cget_protocol函数部分
 
         可以看到每个上述函数按照当前设备的subclass信息被赋值到us->proto_hander指针中。因而,可以只保留其中针对D828的函数,即usb_stor_ATAPI_command同时删除get_protocol函数。
        
         基于同样的原理,可以对transfer.c和get_transport作相同的处理。
 
         接下来是对usb.c中的delayed device scan机制进行删除。因为本次试验不需要适应现实中各类特殊情况。
         在usb.c中usb_stor_scan_thread完成了对设备的延迟探索功能,其中代码段:
926                 /* For bulk-only devices, determine the max LUN value */
927                 if (us->protocol == US_PR_BULK &&
928                                 !(us->flags & US_FL_SINGLE_LUN)) {
929                         mutex_lock(&us->dev_mutex);
930                         us->max_lun = usb_stor_Bulk_max_lun(us);
931                         mutex_unlock(&us->dev_mutex);
932                 }
933                 scsi_scan_host(us_to_host(us));
934                 printk(KERN_DEBUG "usb-storage: device scan complete\n");
//...//
939         scsi_host_put(us_to_host(us));
940         complete_and_exit(&threads_gone, 0);
代码:usb_stor_scan_thread函数片断
 
         上述代码完成了对设备的scan操作,而其他部分则与实际工作无关,因而可以将上述代码复制到storage_probe函数中对应位置,并删除函数usb_stor_scan_thread。

4.制作专用驱动

         在经过对系统自带代码的裁剪操作后,可以通过利用其中设备通讯等工具函数,以及仿造其中的框架重新制作D828的专属驱动。
a)         该操作按照下面的流程进行:
b)         放造usb.c编写框架代码
c)         将框架代码需要调用的工具函数和结构定义复制进本驱动代码
d)         在上次复制的函数中找到需要引用但尚未复制的函数,将他们添加近来
e)         查找仅引用过一次的函数,作inline处理
f)          其他的一些优化
 
         经过上述步骤以后,只要编译通过,该驱动便能顺利运行了。
 

 


 

报告原文,请勿随意抄袭!

ftp://FTP_Visitor:visitor@ftp.csksoft.net/Public/Article/osprj_mass_storage_csk.rar

 

 

  • quote 1.tide
  • 你好,我在尝试编译你的源文件时(环境是ubuntu10.10,linux version 2.6.35),提示出错,原因是linux内核中的很多函数名已经变了,所以我想问一下你编这个程序时的linux版本,谢谢。
  • 5/11/2011 12:25:54 AM 回复该留言
  • quote 2.csk
  • 具体的kernel版本不记得了,当时还在用Fedora Core2
  • 5/14/2011 7:34:02 PM 回复该留言

发表评论:

注意:为了有效防止SPAM,任何含有http://字样的消息会被阻止发布同时,本站仅供技术交流,请不要讨论任何政治敏感话题或者低级趣味问题。

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

日历

最新评论及回复

最近发表

Copyright Shikai Chen 2000-2012. Powered By Z-Blog(CSK Modified)