阅读:5423回复:33
在RAM中实现USB DISK---1
我最近用D12实现了一个U盘,目前只是在内存中存放数据,给大家说说我的开发过程。
首先我用的是嵌入式linux+arm7+16Msdram+16M nor-flash+ pdiusbd12,因此我的内存比MCU大多了:),我第一步设想就是在ram中完全实现U盘。首先是最基本的初始化,包括USB标准函数的实现,具体参数采用philips的mass storage参数。这一步能让windows2k认出是个USB mass storage disk,开始下面的有关U盘的设置。 |
|
沙发#
发布于:2002-08-23 23:00
好东西,继续呀
|
|
板凳#
发布于: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单位,先擦除才能写。 |
|
地板#
发布于:2002-08-21 15:34
我的例子?我的情况比较特殊,没有几个人用arm来控制d12,给了也不能编译。
|
|
地下室#
发布于:2002-08-21 09:50
用户被禁言,该主题自动屏蔽! |
|
5楼#
发布于:2002-08-21 09:22
mbr里1024block就对应logic 1000个block,512-500,再加上fat表等,可用容量减小了不少。你实际flash容量多大?
|
|
6楼#
发布于:2002-08-21 01:49
将文件系统的磁盘信息翻译成NAND_FLASH地址的程序放到固件中就行,我已经实现;但是不知道问什么,空间少了2M,如有高手请指教
|
|
|
7楼#
发布于:2002-08-16 17:12
论坛上有这个东西。
|
|
8楼#
发布于:2002-08-16 15:06
你的bus bound 是什么呢?工具名是bus bound 吗?
|
|
9楼#
发布于:2002-08-16 11:15
另外,请高手指点多分区U盘该怎么实现。谢谢!
|
|
10楼#
发布于:2002-08-16 11:13
基本命令就这么多,如果windows还发送了其他命令,只要敷衍过去就行了。这样一个U盘就出现在windows里,很简单吧?:)程序很大部分是参考philips的例程,大家有不明白的可以去看那个程序,或者发email跟我讨论,tianfulei@sina.com
|
|
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。 |
|
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。 |
|
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 } |
|
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。 |
|
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一次。 |
|
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); } |
|
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; } } |
|
18楼#
发布于:2002-08-16 10:31
为了更好的理解软件流程,放一个流程图上来,有3页,第一页是philips mass storage流程分析,第2页是我原来d12基本驱动分析,第3页是u盘流程图,都是最上层示意图。现在驱动跟philips相比,最大不同是所有工作都在USB中断里完成,而且数据都是一次性传输。
|
|
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。
|
|
上一页
下一页