linux恢复u盘驱动,CSK.Blog

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

作业要求:

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

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

使用方法是insmod。

代码:

下面是这次作业报告的节选,希望对需要研究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 对于非常规设备ProductID和VendorID的支持

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 对于常规设备的ProductID和VendorID的支持

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管理器。

下图展示了整个驱动的工作布局:

2278cceeb920e896c02599ce4d8ac235.png

图中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的分析,可以将该驱动代码作如下方面的简化,方便后续的分析和驱动编写。删除其中对罕见设备的支持

删除Delayed Device Scan机制

删除对于除了本次试验所用u盘设备外其他的通讯协议和通讯方式的处理函数。

对于仅调用一次的函数作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

#defineUSB_MASS_VENDOR_ID 0x04e8

#defineUSB_MASS_PRODUCT_ID        0x665c

//REG IT

staticstruct 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中存在如下几个函数:

voidusb_stor_qic157_command(struct scsi_cmnd *srb, struct us_data *us);

voidusb_stor_ATAPI_command(struct scsi_cmnd *srb, struct us_data *us);

voidusb_stor_ufi_command(struct scsi_cmnd *srb, struct us_data *us);

voidusb_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.c中get_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)其他的一些优化

经过上述步骤以后,只要编译通过,该驱动便能顺利运行了。

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值