hunterkiller
驱动牛犊
驱动牛犊
  • 注册日期2002-05-20
  • 最后登录2004-03-27
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
阅读:5423回复:33

在RAM中实现USB DISK---1

楼主#
更多 发布于:2002-08-15 12:24
我最近用D12实现了一个U盘,目前只是在内存中存放数据,给大家说说我的开发过程。
首先我用的是嵌入式linux+arm7+16Msdram+16M nor-flash+
pdiusbd12,因此我的内存比MCU大多了:),我第一步设想就是在ram中完全实现U盘。首先是最基本的初始化,包括USB标准函数的实现,具体参数采用philips的mass storage参数。这一步能让windows2k认出是个USB mass storage disk,开始下面的有关U盘的设置。

最新喜欢:

templatempla sraccoonsracco...
singmama
驱动牛犊
驱动牛犊
  • 注册日期2002-06-27
  • 最后登录2007-09-10
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
沙发#
发布于:2002-08-23 23:00
好东西,继续呀
hunterkiller
驱动牛犊
驱动牛犊
  • 注册日期2002-05-20
  • 最后登录2004-03-27
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
板凳#
发布于:2002-08-23 15:16
继续贴,ram换成smartmedia。
首先要知道smartmedia卡的组织结构。从大到小分zone,block,sector。一般1024个block为1zone,block跟sector关系与容量有关:(见www.ssfdc.or.jp标准)
1M 2M 4M 8M 16M 32M 64M 128M
4  8  8  16 16  16  32  32
zone感觉更像个逻辑概念,实际访问时不会有zone出现。
读写地址是sector物理地址,block则在擦除时有用,所有擦除按照block单位,先擦除才能写。
hunterkiller
驱动牛犊
驱动牛犊
  • 注册日期2002-05-20
  • 最后登录2004-03-27
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
地板#
发布于:2002-08-21 15:34
我的例子?我的情况比较特殊,没有几个人用arm来控制d12,给了也不能编译。
qzhqzh
禁止发言
禁止发言
  • 注册日期2001-03-23
  • 最后登录2018-07-12
  • 粉丝0
  • 关注0
  • 积分16分
  • 威望23点
  • 贡献值0点
  • 好评度3点
  • 原创分0分
  • 专家分0分
地下室#
发布于:2002-08-21 09:50
用户被禁言,该主题自动屏蔽!
hunterkiller
驱动牛犊
驱动牛犊
  • 注册日期2002-05-20
  • 最后登录2004-03-27
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
5楼#
发布于:2002-08-21 09:22
mbr里1024block就对应logic 1000个block,512-500,再加上fat表等,可用容量减小了不少。你实际flash容量多大?
free_best
驱动牛犊
驱动牛犊
  • 注册日期2002-03-10
  • 最后登录2002-12-22
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
6楼#
发布于:2002-08-21 01:49
将文件系统的磁盘信息翻译成NAND_FLASH地址的程序放到固件中就行,我已经实现;但是不知道问什么,空间少了2M,如有高手请指教
free_best
hunterkiller
驱动牛犊
驱动牛犊
  • 注册日期2002-05-20
  • 最后登录2004-03-27
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
7楼#
发布于:2002-08-16 17:12
论坛上有这个东西。
kill008
驱动牛犊
驱动牛犊
  • 注册日期2002-02-06
  • 最后登录2011-08-15
  • 粉丝0
  • 关注0
  • 积分18分
  • 威望2点
  • 贡献值0点
  • 好评度2点
  • 原创分0分
  • 专家分0分
8楼#
发布于:2002-08-16 15:06
你的bus bound 是什么呢?工具名是bus bound 吗?
hunterkiller
驱动牛犊
驱动牛犊
  • 注册日期2002-05-20
  • 最后登录2004-03-27
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
9楼#
发布于:2002-08-16 11:15
另外,请高手指点多分区U盘该怎么实现。谢谢!
hunterkiller
驱动牛犊
驱动牛犊
  • 注册日期2002-05-20
  • 最后登录2004-03-27
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
10楼#
发布于:2002-08-16 11:13
基本命令就这么多,如果windows还发送了其他命令,只要敷衍过去就行了。这样一个U盘就出现在windows里,很简单吧?:)程序很大部分是参考philips的例程,大家有不明白的可以去看那个程序,或者发email跟我讨论,tianfulei@sina.com
hunterkiller
驱动牛犊
驱动牛犊
  • 注册日期2002-05-20
  • 最后登录2004-03-27
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
11楼#
发布于:2002-08-16 11:08
readcapacity告诉windows有多少个扇区,每个扇区大小,这样windows就知道你的U盘的容量了。
BOOLEAN RBC_ReadCapacity(void)
{
//printk(\"In RBC_ReadCapacity.\\n\");
#define cdbReadCap RBC_CDB.RbcCdb_ReadCapacity

/*
// Calculate last sector.
*/
bFlags.bits.BI_Stalled = 0;
bFlags.bits.BO_Stalled = 0;
cdbReadCap.tmpVar.l[1] =  RAMDISKSECTORS; //201 ;//4096*16; //((INT32)ATADevExt_IDData.CurrentSectorCapacity.u0) - 1;

// 32bits info
//ATADevExt_IDData.CurrentSectorCapacity.ints.i0 = Hal4Sys_ATADataPortInW()
//ATADevExt_IDData.CurrentSectorCapacity.ints.i1 = Hal4Sys_ATADataPortInW()
/* struct
    {
        INT8 c3;  // MSB for 8051 Keil C
        INT8 c2;
        INT8 c1;
        INT8 c0;   // LSB for 8051 Keil C
    } chars0;
*/
/* store it in big endian */
cdbReadCap.tmpVar.CapData.LBA_3 = ( INT8 ) cdbReadCap.tmpVar.l0[1].chars0.c3;
cdbReadCap.tmpVar.CapData.LBA_2 = ( INT8 ) cdbReadCap.tmpVar.l0[1].chars0.c2;
cdbReadCap.tmpVar.CapData.LBA_1 = ( INT8 ) cdbReadCap.tmpVar.l0[1].chars0.c1;
cdbReadCap.tmpVar.CapData.LBA_0 = ( INT8 ) cdbReadCap.tmpVar.l0[1].chars0.c0;

/*
// Bytes Per Block is 512Bytes
// 00020000 is 0x200 in big endian
*/
cdbReadCap.tmpVar.CapData.BlockLen_3 = 0;
cdbReadCap.tmpVar.CapData.BlockLen_2 = 0;
cdbReadCap.tmpVar.CapData.BlockLen_1 = 0x02;  //0x02;
cdbReadCap.tmpVar.CapData.BlockLen_0 = 0;
/*
// Adjust TPBulkXfer Paras
*/
    Xfer_Space &= BOTXFERSPACE_MASK;
    Xfer_Space = BOTXfer_atRAM;

BOTXfer_wResidue = sizeof(READ_CAPACITY_DATA);
BOTXfer_pData = (PINT8)&(cdbReadCap.tmpVar);

TPBulksup_ErrorHandler(CASE6, BOTXfer_wResidue);
RBC_BuildSenseData(SCSI_SENSE_NO_SENSE,0,0);

BOTFSMstate = USBFSM4BOT_DATAIN;

USBWriteCommand(USB_COMMAND_SELECT_ENDPOINT + USB_ENDPOINT_BULK_IN);
    FlexByte = USBReadData();
//printk(\"ep5 status is %x.\\n\",FlexByte);
    if(FlexByte == 0) {// BulkIn is not full
//printk(\"inquiry data len is%d\\n\",BOTXfer_wResidue);
// show_char_buf(&inquiryData,BOTXfer_wResidue);
//show_char_buf(BOTXfer_pData,BOTXfer_wResidue);
USBWriteEndpoint(0x05, (char **)&BOTXfer_pData,&BOTXfer_wResidue);
}

return(TRUE);
#undef cdbReadCap
}
我分配了201*512字节内存,因此我的U盘大概80K。
hunterkiller
驱动牛犊
驱动牛犊
  • 注册日期2002-05-20
  • 最后登录2004-03-27
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
12楼#
发布于:2002-08-16 11:06
write是windows写数据类型命令。
BOOLEAN RBC_Write(void) //0x2a
{
unsigned int tt;
#define cdbWrite    RBC_CDB.RbcCdb_Write


tt = cdbWrite.LBA.LBA_W8.LBA_3 <<24 | cdbWrite.LBA.LBA_W8.LBA_2 <<16 |cdbWrite.LBA.LBA_W8.LBA_1 <<8 |cdbWrite.LBA.LBA_W8.LBA_0;

//printk(\"In RBC_Write,len=%d,start sector=%d,logic0_sec=%p.\\n\",CBW_wXferLen,tt,logic0_sec);

    Xfer_Space &= BOTXFERSPACE_MASK;
    //BOTXfer_atATA = 1;
    BOTXfer_pData = logic0_sec + (tt)*512; //save to correct sector
    BOTXfer_wResidue = CBW_wXferLen;
    printk(\"In RBC write,BOTXfer_pData=%p,BOTXfer_wResidue=%d.\\n\",BOTXfer_pData,BOTXfer_wResidue);
    //sUSB.usBulkOutCount = CBW_wXferLen;

if(BOTXfer_wResidue > 1024*10) { // large than 10k,we want discard last part for mem limit.
//longdatacount = 512;
//show_flag = 1;
}

TPBulksup_ErrorHandler(CASE12,BOTXfer_wResidue);
RBC_BuildSenseData(SCSI_SENSE_NO_SENSE,0,0);

BOTFSMstate = USBFSM4BOT_DATAOUT;

return (TRUE);

#undef cdbWrite
}
只要设置正确状态,windows发送CBW后跟着发数据,就会进入endpoint out中断,在out中断里读数据,完成后发送CSW。
hunterkiller
驱动牛犊
驱动牛犊
  • 注册日期2002-05-20
  • 最后登录2004-03-27
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
13楼#
发布于:2002-08-16 11:04
read也是读数据,跟inquiry差不多。
BOOLEAN RBC_Read(void)
{

#define cdbRead RBC_CDB.RbcCdb_Read
unsigned int tt;
BOOLEAN retStatus = FALSE;
tt = cdbRead.LBA.LBA_W8.LBA_3 <<24 | cdbRead.LBA.LBA_W8.LBA_2 <<16 |cdbRead.LBA.LBA_W8.LBA_1 <<8 |cdbRead.LBA.LBA_W8.LBA_0;
//printk(\"In RBC_Read.\\n\");
//BOTXfer_pData =(PINT8) logic0_sec;
    
//if(tt >10) tt =0;   //for debug only,we do not want to halt here for mem limit.
BOTXfer_pData = logic0_sec + (tt)*512; //save to correct sector
BOTXfer_wResidue = CBW_wXferLen; //sizeof(logic0_sec)*(cdbRead.XferLength_1*256+cdbRead.XferLength_0);
//printk(\"BOTXfer_pData is %p,BOTXfer_wResidue=%d\\n\",BOTXfer_pData,BOTXfer_wResidue);

if(BOTXfer_wResidue > 1024*10) { // large than 10k,we want discard last part for mem limit.
//longdatacount = 512;
}

retStatus = TRUE;
    Xfer_Space &= BOTXFERSPACE_MASK;
    //BOTXfer_atROM = 1;
    Xfer_Space = BOTXfer_atROM; //bit6 =1

BOTFSMstate = USBFSM4BOT_DATAIN;// Goto USBFSM4BOT_DATAIN

TPBulksup_ErrorHandler(CASE6,BOTXfer_wResidue);
RBC_BuildSenseData(SCSI_SENSE_NO_SENSE,0,0);

USBWriteCommand(USB_COMMAND_SELECT_ENDPOINT + USB_ENDPOINT_BULK_IN);
    FlexByte = USBReadData();
//printk(\"ep5 status is %x.\\n\",FlexByte);
    if(FlexByte == 0) {// BulkIn is not full
//printk(\"write data len is%d\\n\",BOTXfer_wResidue);
// show_char_buf(&inquiryData,BOTXfer_wResidue);
//show_char_buf(BOTXfer_pData,BOTXfer_wResidue);
USBWriteEndpoint(USB_ENDPOINT_BULK_IN, (char **)&BOTXfer_pData,&BOTXfer_wResidue);
}
//printk(\"First sent inquiry,len is %d\\n\",BOTXfer_wResidue);
return retStatus;
#undef cdbRead
}
hunterkiller
驱动牛犊
驱动牛犊
  • 注册日期2002-05-20
  • 最后登录2004-03-27
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
14楼#
发布于:2002-08-16 11:02
inquiry属于windows读数据的rbc命令类型,testunit属于无数据的rbc命令,
BOOLEAN SPC_TestUnit(void)
{
//printk(\"In SPC_TestUnit.\\n\");

TPBulksup_ErrorHandler(CASE6,BOTXfer_wResidue);
RBC_BuildSenseData(SCSI_SENSE_NO_SENSE,0,0);
    TPBulk_CSWHandler();

USBWriteCommand(USB_COMMAND_SELECT_ENDPOINT + USB_ENDPOINT_BULK_IN);
    FlexByte = USBReadData();
    if((FlexByte&0x01) == 0x00) // BulkIn is empty
    {
//     printk(\"send bulk CSW\\n\");
     //show_char_buf(BOTXfer_pData,BOTXfer_wResidue);
if(BOTXfer_wResidue>0)
USBWriteEndpoint(USB_ENDPOINT_BULK_IN, (char **)&BOTXfer_pData,&BOTXfer_wResidue);
   if(BOTXfer_wResidue == 0)
   BOTFSMstate = USBFSM4BOT_IDLE; // Goto BOTFSMstate_IDLE
}    
return (TRUE);
}
首先设置csw,传输状态为BOTFSMstate = USBFSM4BOT_CSW;写csw到endpoint in fifo里,d12 ep in fifo大小64字节,显然13字节的csw一次发送完毕,传输状态设为USBFSW4BOT_IDLE。
hunterkiller
驱动牛犊
驱动牛犊
  • 注册日期2002-05-20
  • 最后登录2004-03-27
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
15楼#
发布于:2002-08-16 10:58
inquiry就是返回serialpage数据给windows,关键是最后的部分,
USBWriteEndpoint(USB_ENDPOINT_BULK_IN, (char **)  &BOTXfer_pData,&BOTXfer_wResidue);
RBC命令处理是在endpoint out中断里,发送数据在endpoint in中断里完成。windows要读数据,发送cbw后就会读endpoint in端口,因此这里我们要先写endpoint in一次。
 
hunterkiller
驱动牛犊
驱动牛犊
  • 注册日期2002-05-20
  • 最后登录2004-03-27
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
16楼#
发布于:2002-08-16 10:50
下面具体分析各个命令的实现。总的来说关键命令是read,write,readcapacity,其它命令只要能让windows认为正确就可以了,就是说windows读写多少数据你能正确处理返回good状态就可以了,比如testunit,直接在csw里返回0x00就行了。
第一个命令,inquiry,0x12.
BOOLEAN SPC_Inquiry(void) //0x12
{

#define cdbInquirySPC RBC_CDB.SpcCdb_Inquiry
BOOLEAN retStatus = FALSE;
printk(\"In SPC_Inquiry.\\n\");

// show_spc_inquiry_cmd();

if(cdbInquirySPC.EnableVPD)
{
switch(cdbInquirySPC.PageCode)
{
case VPDPAGE_SERIAL_NUMBER:

BOTXfer_pData =(PINT8) &SerialPage;
BOTXfer_wResidue = sizeof(VPD_SERIAL_PAGE);
break;

case VPDPAGE_DEVICE_IDENTITY:

retStatus = TRUE;
BOTXfer_pData = (PINT8)&DeviceIDPage;
BOTXfer_wResidue = sizeof(VPD_DEVICE_ID_PAGE);
break;

default:
//retStatus = FALSE;
TPBulksup_ErrorHandler(CASECMDFAIL,BOTXfer_wResidue);
RBC_BuildSenseData(SCSI_SENSE_ILLEGAL_REQUEST,SCSI_ADSENSE_ILLEGAL_COMMAND,0x00);

TPBulk_CSWHandler();// Goto USBFSM4BOT_CSWPROC;
return retStatus;
}
}
else if(cdbInquirySPC.CmdSupportData)
{
//retStatus = FALSE;
TPBulksup_ErrorHandler(CASECMDFAIL,BOTXfer_wResidue);
RBC_BuildSenseData(SCSI_SENSE_ILLEGAL_REQUEST,SCSI_ADSENSE_ILLEGAL_COMMAND,0x00);

TPBulk_CSWHandler();// Goto USBFSM4BOT_CSWPROC;
return retStatus;
}
else
{

        BOTXfer_pData =(PINT8) &inquiryData;
BOTXfer_wResidue = sizeof(STD_INQUIRYDATA);
}

retStatus = TRUE;
    Xfer_Space &= BOTXFERSPACE_MASK;
    //BOTXfer_atROM = 1;
    Xfer_Space = BOTXfer_atROM; //bit6 =1

//printk(\"BOTXfer_wResidue =%d ,CBW_wXferLen = %d.\\n\",BOTXfer_wResidue,CBW_wXferLen);

if( BOTXfer_wResidue > CBW_wXferLen )
{
BOTXfer_wResidue = CBW_wXferLen;
TPBulksup_ErrorHandler(CASE6,BOTXfer_wResidue);
RBC_BuildSenseData(SCSI_SENSE_NO_SENSE,0,0);
}
else if ( BOTXfer_wResidue == CBW_wXferLen )
{
TPBulksup_ErrorHandler(CASE6,BOTXfer_wResidue);
RBC_BuildSenseData(SCSI_SENSE_NO_SENSE,0,0);
}
else
{
TPBulksup_ErrorHandler(CASE5,BOTXfer_wResidue);
RBC_BuildSenseData(SCSI_SENSE_NO_SENSE,0,0);
}

BOTFSMstate = USBFSM4BOT_DATAIN;// Goto USBFSM4BOT_DATAIN

USBWriteCommand(USB_COMMAND_SELECT_ENDPOINT + USB_ENDPOINT_BULK_IN);
    FlexByte = USBReadData();
//printk(\"ep5 status is %x.\\n\",FlexByte);
    if(FlexByte == 0) {// BulkIn is not full
// printk(\"inquiry data len is%d\\n\",BOTXfer_wResidue);
// show_char_buf(&inquiryData,BOTXfer_wResidue);
// show_char_buf(BOTXfer_pData,BOTXfer_wResidue);
USBWriteEndpoint(USB_ENDPOINT_BULK_IN, (char **)&BOTXfer_pData,&BOTXfer_wResidue);
}
//printk(\"First sent inquiry,len is %d\\n\",BOTXfer_wResidue);
return retStatus;
#undef cdbInquirySPC
// return (TRUE);
}
hunterkiller
驱动牛犊
驱动牛犊
  • 注册日期2002-05-20
  • 最后登录2004-03-27
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
17楼#
发布于:2002-08-16 10:45
结合流程图,说说最基本的思想。windows发送rbc命令,底层处理数据并返回csw,有3种可能:windows发数据,windows收数据,没有数据。1.windows发数据,底层收到rbc命令后,设置状态为dataout,在dataout状态里收数据,收完后发送csw。2.windows收数据,底层设置状态为datain,然后写一包数据,在datain里底层发送所有数据,发送完成后发送csw。3.底层直接发送csw,先写csw到fifo里一次。软件框架大概这样
    // Handle an interrupt on the bulk out endpoint.
    if(ulIntStatus & USB_INT1_ENDPOINT2_OUT)  {
        if(BOTFSMstate == USBFSM4BOT_IDLE) {
         收rbc命令并处理;根据命令设置BOTFSMstate;
   }
   if(BOTFSMstate == USBFSM4BOT_DATAOUT) {
读数据;
       if(BOTXfer_wResidue == 0)  {
          数据读完,发CSW;
}
}
}
    // Handle an interrupt on the bulk in endpoint.
    if(ulIntStatus & USB_INT1_ENDPOINT2_IN)   {
        if(BOTFSMstate == USBFSM4BOT_IDLE) return;
        if(BOTFSMstate == USBFSM4BOT_DATAIN)
发数据;
        if(BOTXfer_wResidue == 0)
         发送CSW;
if(BOTFSMstate == USBFSM4BOT_CSW) { //发送CSW状态
发送CSW剩下数据;
发送完成后状态设为BOTFSMstate = USBFSM4BOT_IDLE;
}
}
hunterkiller
驱动牛犊
驱动牛犊
  • 注册日期2002-05-20
  • 最后登录2004-03-27
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
18楼#
发布于:2002-08-16 10:31
为了更好的理解软件流程,放一个流程图上来,有3页,第一页是philips mass storage流程分析,第2页是我原来d12基本驱动分析,第3页是u盘流程图,都是最上层示意图。现在驱动跟philips相比,最大不同是所有工作都在USB中断里完成,而且数据都是一次性传输。
hunterkiller
驱动牛犊
驱动牛犊
  • 注册日期2002-05-20
  • 最后登录2004-03-27
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
19楼#
发布于:2002-08-16 10:25
我们的驱动仿照philips的参数,包括descriptor,SerialPage等,对照协议可以很好的理解这些参数的意义。有了这些基本的东西,正确的控制中断,windows可以顺利的发现U盘设备,然后用bus hound就可以收到RBC命令包了。philips驱动定义成0x06,scsi命令集而不是rbc,这也无关紧要,好像windows反复也就是那几个命令。另外,不同的U盘windows发送的命令顺序不太一样,至少我收的第一个命令是0x12,inquiry,而oti的u盘上来就是0x28,read。
上一页
游客

返回顶部