whisperer的专栏

首页博文目录订阅
正 文

嵌入式系统USB CDROM虚拟光驱驱动程序开发

(2011-5-16 21:58)
标签:CDROM CD-ROM

   关键词:USB, SCSI, USB CDROM/USB CD-ROM/USB-CDROM, USB虚拟光驱,USB虚拟光盘
   
   带U盘功能的的USB接口设备已经越来越常见了。如果能够把产品说明书或者产品设备驱动程序做成一个USB CDROM,那该多方便。
  
   假设:
        你已经有了USB mass storage驱动。你的任务是在此基础上增加一个USB CDROM。
        请在手边准备好:USB2.0协议,Mass storage协议,SCSI协议(可以忽略)。此外,你需要一个debug工具:bushound。  
   步骤:
        1,制作CDROM image文件;
        2,处理usb device request:Get Max Lun
        3,处理SCSI命令:
                        INQUIRY_CMD,
                        READ_TOC_CMD,
                        MODE_SENSE6_CMD,
                        MODE_SENSE10_CMD,
                        READ6_CMD,
                        READ10_CMD
                       
     OK. Let's go!
     
     制作CDROM image文件:google一个PC工具,比如UltraISO,把你需要出现在光盘上的东东统统塞到一个CDROM image文件里面去。
 然后把此文件放到你的文件系统里去。
     A:什么?你木有文件系统?!这么土?
     B:就这么土!
     A:那就放到一个大的const数组里。
     B:呃...为什么放在const?
     A:真不知道?
     B:真的...
     A:打屁屁!
 
    处理从control out endpoint发来的usb device request: "Get Max Lun"(bRequest等于254,usbmassbulk_10.pdf, page 7 )
此命令在setup阶段收到,主要是向host报告usb mass storage Logic Unit Num。注意Num=总数-1:比如1个U盘和1个CDROM,总数为2,
应该向host报告2-1=1。这样枚举后,PC出现2个盘符。

    处理如下几条Command Block Wrapper里的SCSI命令。
    setup成功后,SCSI命令经过CBW打包,就可以通过BULK OUT端点发送到设备了(参见usbmassbulk_10.pdf, page 13)。
    SCSI命令处理流程参考usbmassbulk_10.pdf figure1.尤其需要注意的是,一定不要忘记发送CSW!
   
    B: host发给U盘和USB CDROM的命令都是通过CBW进行的。那么怎么知道哪个命令发给U盘,哪个是给USB CDROM的呢?
    A: very good question!
       请查看usbmassbulk_10.pdf, page 13,table5.1。在本案中CBW的bCBWLUN会给你两个值:0和1.
       至于0代表光盘或者1代表光盘,你的地盘你做主(木仓 在手,跟你走)
      
    Go on...
   
    SCSI命令在CBW的bCBWCB头一个字节。SCSI命令很多,但是USB CDROM只需要关注这几个SCSI命令:
   
    INQUIRY_CMD(0x12):“报告老大,我是光盘!”
    把usbcdrom_inquiry_data返回给host,注意第一个字节0x05表示CDROM,这样在PC看到的盘符就是光盘。
    请按照实际需要修改如下数据结构。
   
                   char usbcdrom_inquiry_data[] = {
                     0x05,                        /*= 5 for CDROM; 0 for usb mass storage */
                     0x80,                        /*= RMB BYTE is set by inquiry data */
                     0x02,                        /* ANSI SCSI 2 */
                     0x00,                        /*= Data format = 0,  */
                     0x33,                        /*= Additional length   */
                     0x00, 0x00, 0x00,
                     /* Manufacturer = SOMwireless.com */
                     'S', 'O', 'M', 'w', 'i', 'r', 'e', 'l', 'e', 's', 's', '.', 'c', 'o', 'm', 0x20,
                     /* Product = ISO9660 Image */
                     0x49, 0x6d, 0x61, 0x67, 0x65, 0x20, 0x20, 0x20,
                     /* Revision = 2.31 for new transparent command */
                     0x32, 0x2E, 0x33, 0x31,
                     0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 
                     0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
                     0x20,0x20,0x20,0x20
                  };
   
   
    READ_TOC_CMD(0x43):  “报告!目录结构(TOC,Table Of Content)是。。。
    把usbcdrom_read_toc_data返回给host。
    照抄就行了,不用修改。详情参见the f*cking scsi spec.你也可以用光驱读只有一个track的CDROM,用bushound抓包分析。
                   char usbcdrom_read_toc_data[] = {
                     0x00,                       
                     0x12,                        /*number of bytes below */
                     0x01,                        /* first track */
                     0x01,                        /* last track */
                     0x00,                        /*reserved*/
                     0x14,                        //(adr, control)
                     0x01,                        //(track being described)
                     0x00,                        //(reserved)
                     00, 00, 00, 00,              // (start logical block address 0)
                     0x00,              //(reserved)
                     0x14,              //(adr, control)
                     0xAA,                        // (track being described (leadout))
                     0x00,              //(reserved)
                     00, 00, 0xA2, 0x8A           //(start logical block address 41610)
                  };
  
   
    SCSI设备是按照BLOCK来组织的。我们的USB虚拟光驱只是一个文件,是虚拟的CDROM。
    可以按照实际需要定义BLOCK大小(通常是512的整数倍),比如#define CDROM_BLOCK 512
    假设我们的CDROM image文件大小为iso_image_len,那么总的BLOCK数量是block_num = (cdr_iso_len + CDROM_BLOCK - 1)/CDROM_BLOCK
    在SCSI系统中,BLOCK总是从0开始编号的,那么最后一个BLOCK号block_last = block_num - 1;
   
    ok.我们来继续看看这些SCSI命令。
   
    MODE_SENSE6_CMD(0x1A):
    MODE_SENSE10_CMD(0x5A):
    READ_CAPACITY_CMD(0x25):主要向host报告block_last和CDROM_BLOCK滴,只是细节各有不同。
    请注意:SCSI系统采用大端(big endian),通常ARM编译器都将代码编译成小端(little endian),需要把block_last和CDROM_BLOCK变成大端。
   
    USBSDMS_READ_CAPACITY_CMD需要返回的数据结构如下:
                   struct read_cap_data
                   {
                      unsigned long block_last;  //the last block number
                      unsigned long block_len;   //in bytes
                   } ;

    MODE_SENSE6_CMD需要返回的数据结构如下:
                   struct mode_sense6_data
                   {
                       char  length;               //sizeof( struct mode_sense6_data )
                       char  media_type;           //0x05 for CDROM, 0x00 for U disk
                       char  device_specific;      //0x00
                       char  block_descriptor_len; //sizeof( block_last ) + sizeof( block_len )
                       unsigned long block_last;   //the last block number
                       unsigned long block_len;    //in bytes
                   } ;
                  
    MODE_SENSE10_CMD需要返回的数据结构如下:
                   struct mode_sense10_data
                   {
                      unsigned short  length;                    //sizeof( struct mode_sense10_data )
                      char  media_type;                          //0x05 for CDROM, 0x00 for U disk
                      char  write_protect;                       //0x00
                      unsigned short  reserved;                  //0x0000
                      unsigned short  block_descriptor_len;      //sizeof( block_last ) + sizeof( block_len )
                      unsigned long block_last;                  //the last block number
                      unsigned long block_len;                   //in bytes
                   } ;

    好了,现在插入USB(这个词很邪恶~~~~~~)
   
    ~~~~~~~~~~~~~~~~~~~~~~~~~昏哥线~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    老婆,出来看神仙啦~~~~如果你很杯具,神仙木有出来Orz...那就用bushoud
    抓数据包,对照你手边的f*cking spec...blablabla...
    ~~~~~~~~~~~~~~~~~~~~~~~~~昏哥~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   
    Voila~~~~~~
    到此为止,你已经成功将大象装进冰箱了。
    下一步要把大象从冰箱里面取出来!
   
    需要处理的SCSI命令是:
    READ6_CMD(0x08):
    READ10_CMD(0x28):都是读操作,只是细节略有不同,详见SCSI手册。host传过来的主要参数是:起始的Block号,
    以及需要读取的block数(注意:不是字节数)。
    按照要求传给host就ok了。
   
    ~~~~~~~~~~~~~~~~~~~~~~~~~昏哥~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    DONE!
评 论
7楼 52RD网友 发表于 2011-10-20 10:10 回复
求完整源码!hairlun@qq.com
6楼 52RD网友 发表于 2011-9-27 15:49 回复
求完整源码!dianshiju001@qq.com
5楼 52RD网友 发表于 2011-8-22 18:12 回复
求完整源码,谢谢。 tanghz5002@163.com
4楼 lvmohuang 发表于 2011-6-14 07:23 回复
正急用,谢谢啦
3楼 52RD网友 发表于 2011-5-22 11:16 回复
1、2楼,授人与鱼不如授人以渔。纸上得来终觉浅,绝知此事要躬行。 Q: 如何把我自己定制的图标作为光驱图标,还有光驱的名字我自己怎样定义 A: 用光盘制作工具就可以定制图标。光驱名字似乎也通过工具进行。
2楼 youyunyehe(游客) 发表于 2011-5-20 22:33 回复
经典中的经典,但求源码:youyunyehe@126.com 还有个问题是,如何把我自己定制的图标作为光驱图标,还有光驱的名字我自己怎样定义,在程序何处修改,望楼主指点!!!
1楼 sysh1985 发表于 2011-5-17 12:34 回复
求完整源码! sysh1985@163.com
博 主
进入whisperer的首页
博客名称:somwireless
日志总数:1
评论数量:7
访问次数:11778
建立时间:2010-11-20 15:20
导 航
公 告
评 论