码迷,mamicode.com
首页 > 其他好文 > 详细

usb开发笔记

时间:2015-03-30 17:53:39      阅读:317      评论:0      收藏:0      [点我收藏+]

标签:

  U盘应属于海量存储类。
  USB海量存储设备,又包括通用海量存储子类,CDROM,Tape等,U盘实际上属于海量存储类中通用海量存储子类。通用海量存储设备实现上是基于块/扇区存储的设备
  USB组织定义了海量存储设备类的规范,这个类规范包括4个独立的子类规范。主要是指USB总线上的传输方法与存储介质的操作命令。
  海量存储设备只支持一个接口,即数据接口,此接口有三个端点Bulk input ,Bulk output,中断端点。这种设备的接口采用SCSI-2的直接存取设备协议,USB设备上的介质使用与SCSI-2以相同的逻辑块方式寻址。 
  二、 Bulk-Only传输协议
  当一个U盘插入主机以后,主机会要求USB设备传回它们的描述符,当主机得到这些描述符后,即完成了设备的配置。识别出USB设备是一个支持Bulk-Only传输协议的海量存储设备。这时应可进行Bulk-Only传输方式。在此方式下USB与设备之间的数据传输都是通过Bulk-In和Bulk-Out来实现的。
  在这种传输方式下,有三种类型数据在USB和设备传送,它们是命令块包(CBW),命令执行状态包(CSW)和普通数据包。CBW是主机发往设备的命令。格式如下: 
技术分享

其中dCBWSignature的值为43425355h,表示当前发送的是一个CBW。
dCBWTag:主机发送的一个命令块标识,设备需要原样作为dCSWTag(CSW中的一部分)再发送给Host;主要用于关联CSW到对应的CBW

dCBWDataTransferLength:表示这次CBW要传送数据长度。
bmCBWFlags:表示本次CBW是读数据还是写数所
bCWCBLength:表示命令的长度。
CBWCB:表示本次命令内容。也即是SCSI命令。
当设备从主机收到CBW块以后,它会把SCSI命令从CBW中分离出来,然后根据要求执行,执行的结果又以CSW的形式发给主机。

CSW的格式如下:
技术分享

其中dCSWSignature的值为53425355h,表示当前发送的是一个CSW。
DCSWTag:必须和CBW中dCBWTag一样。
DCSWDataResidue:还要传送的数据。
BCSWStatue:命令执行状态,命令正确执行时,为0。

三、 SCSI命令集

技术分享

在Bulk-Only的命令块包(CBW)中,有一段CBECB内容,它就是SCSI命令块描述符。其内容如下:
Operation Code:是SCSI命令操作代码。
Logical Block Address:逻辑块地址,对U盘而言应是扇区。前面已经讲过:通用海量存储设备是一个基于块/扇区存储的设备,因此在SCSI中要提供这个参数是很显然的。
transfer length:为要传送的扇区数

SCSI中直接存取类型的存储介质的传输命令有很多,如:
INQUIRY:其操作码为12H
Test Unit Ready:其操作码为00H
Format Unit:其操作码为04H
.......


开发可以自动运行程序的U盘
一、为什么要开发这样的U盘 
  可以自动运行程序的U盘,插入电脑后U盘里面的程序会自动运行,这样的U盘可以作为软件厂商存储程序的载体,也可以作为U盘厂商扩展U盘功能的一种途径。举几个例子:有加密功能的U盘可以把加密软件存放到U盘中,插入U盘后自动运行加密管理软件,极大方便了用户,厂商也不用另外提供软件;我甚至这么想过,把温度芯片加入到U盘电路里面,U盘上的程序自动运行后,通过USB接口读出温度,显示给用户:)只要你多想,这样的U盘还可以做很多事情。 

二、开发思路 
我们知道光驱插入光盘可以自动运行,所以我们可以让U盘的一个区模拟成光盘的形式。这样我们可以利用autorun.inf,让程序自动运行。 

三、开发步骤 
1. 让U盘一个区显示成光盘 
这个对于开发过U盘的人来说应该很简单,就是在操作系统发送SCSI设备的 INQUIRY 指令的时候,返回的INQUIRY里面指明设备类型,我们设置为CD设备。具体指令可以参考相关资料(spc3r23.pdf)。我开发的时候 INQUIRY返回数据的前几个字节如下:0x05,0x80,0x02,0x02仅供参考。 
2. 响应操作系统对这光盘区的指令 
成光盘后,操作系统可能会发送一些SCSI指令给这个光盘区。应该响应某些指令,有些并不需要响应。后面我会附录一个我弄过的一个U盘的BusHound监测文件,大家可以参考。最好买一个这样的U盘,然后用BusHound监测通讯过程。 
3. 向光盘区写入文件 
应该可以把光盘区弄成CDRW,这样直接写入就可以了,但这样需要了解很多协议。由于时间关系,我采用了简单的方法,把需要写入光盘的文件用ISO制作工具弄成一个ISO镜像文件,编写一个小程序向这个光盘区写入ISO镜像文件。程序部分代码附在文档后面,仅供参考。写入的程序最终调用 DeviceIoControl函数。文件写入后,再重新拔插U盘就可以了。 
四、说明 
这里只是简单说明了原理,如果是刚接触这个,还有很多东西要弄清楚,我是在别人一个普通U盘开发板的基础上添加这个功能的,最后弄通了,感觉很不容易,以前也没有弄过硬件的固件程序。希望对新手有帮助。有什么问题,可以邮件联系交流。 
附录: 
1. Write ISO程序部分代码:
UINT CWriteIsoFileDlg::ThreadWrite(LPVOID pParam) 
{ 
CWriteIsoFileDlg * p = (CWriteIsoFileDlg*)pParam; 
CSDsk sd; 
CString str; 
HANDLE fh,hFind; 
WIN32_FIND_DATA FindFileData; 
ULONGLONG fSize = 0; 
CFileFind ff; 
DWORD startAddr = 0; 
DWORD nBytesRead = 0; 
PBYTE buf = new BYTE[20480]; 
memset(buf,0,20480); 
try 
{ 
    if(!ff.FindFile(p->filename)) 
    { 
        str.Format("找不到文件 %s ",p->filename); 
        throw 1; 
    } 
    ff.FindNextFile(); 
    fh = CreateFile(ff.GetFilePath(),GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); 
    if(fh==INVALID_HANDLE_VALUE) 
    { 
        str.Format("打开文件 %s 失败",p->filename); 
        throw 1; 
    } 
    if(!sd.Open()) 
    throw 0; 

    int index = sd.GetDevNum()-1; 
    if(!sd.ReadCapacity(index,0,fSize)) 
    throw 0; 
    if(ff.GetLength()>fSize) 
    { 
        str = "光盘空间不足,不能写入。"; 
        throw 1; 
    } 
    fSize = ff.GetLength(); 
    if(!sd.GetFlashParam(index,0,1,buf)) 
    throw 0; 
    buf[0]&=0xBF; 
    if(!sd.SetFlashParam(index,0,1,buf,false)) 
    throw 0; 
    while(1) 
    { 
        if(ReadFile(fh,buf,20480,&nBytesRead,NULL)) 
        { 
            if(nBytesRead!=0) 
            { 
                if(!sd.Write(index,0,startAddr,40,buf)) 
                { 
                    throw 0; 
                } 
                startAddr+=40; 
                memset(buf,0,20480); 
            } 
            else 
            break; 
        } 
        else 
        { 
            str = "读文件错误!"; 
            throw 1; 
        } 
    } 
    //p->MessageBox("写入镜像文件成功!",NULL,MB_OK|MB_ICONINFORMATION); 
    sd.PlugDisk(index,0,true); 
    delete [] buf; 
    exit(0); 
} 

catch(int e) 
{ 
    if(e==0) 
    { 
        p->MessageBox(sd.err.GetErrMsg(),NULL,MB_OK|MB_ICONERROR); 
    } 
    else 
        p->MessageBox(str,NULL,MB_OK|MB_ICONERROR); 
    } 
    delete [] buf; 
    exit(-1); 
    return 1; 
}

 












usb开发笔记

标签:

原文地址:http://www.cnblogs.com/heiyue/p/4378263.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!