Hi my friends!
I've used RL-ARM mass storage code from sample codes of KEIL 4.03 on AT91SAM7S256. when I attach it to PC , the system recognizes it as a flash disk, correctly;
now I want to change it to apear as a CD-ROM in windows.
I want to know any changes in code.
can anyone help me?
thanks for your help!
Both of USB drive (stick) and CD/DVD drive are supported by USB Mass-Storage Class, Bulk-Only Transport. Therefore, there is no difference on the USB protocol layer.
The differences lie in the SCSI command layer. 1) PDT (Peripheral Device Type): 00h (Direct-Access device, eg. USB drive) --> 05h (CD/DVD)
2) Sector (Block) size: 512 byte --> 2048 bytes
3) Required SCSI command set - READ10 --> READ12 - WRITE10 --> WRITE12 (for CD-R/DVD-R) - Start/Stop Uint (optional --> required) - Read TOC (Not Available --> required) - information of sections/tracks on the CD -- see "5.1 READ TOC" on usb_msc_boot_1.0.pdf for bootable CD www.usb.org/.../usb_msc_boot_1.0.pdf
Lastly, as of the contents on the CD image, see these specs.
ECMA-119 (ISO9660) www.ecma-international.org/.../Ecma-119.htm
Joliet Specification pierrelib.pagesperso-orange.fr/.../joliet_spec_v1.html
“El Torito” Bootable CD-ROM Format Specification bochs.sourceforge.net/.../el-torito.pdf.gz
> I've used RL-ARM mass storage code from sample codes of KEIL 4.03 on AT91SAM7S256.
Is this example? C:\Keil\ARM\Boards\Atmel\AT91SAM7S-EK\RL\USB\RTX_Memory
It's the first time I look in this example. With quick look, it looks like almost same as NXP LPC examples, except for the hardware layer.
1) PDT PDT is reported in the reply to INQUIRY command.
mscuser.c void MSC_Inquiry (void) { if (!DataInFormat()) return; BulkBuf[ 0] = 0x00; /* Direct Access Device */ <--- 0x05 (CD/DVD) ...
2) Sector (Block) size The sector size is defined in the mscuser.h It is applied to READ CAPACITY and READ FORMAT CAPACITY command, automatically.
mscuser.h /* Mass Storage Memory Layout */ #define MSC_MemorySize 8192 // <---- tune this size for the total CD capacity #define MSC_BlockSize 512 // <---- 2048
3) Required SCSI command set Add definitions for the extra SCSI commands
msc.h /* SCSI Commands */ ... #define SCSI_READ12 0xA8 #define SCSI_WRITE12 0xAA #define SCSI_READ_TOC 0x43
SCSI command parser is modified, as follows,
mscuser.c void MSC_GetCBW (void) { ... ... switch (CBW.CB[0]) { ... ... case SCSI_START_STOP_UNIT: // goto fail; MSC_StartStopUnit(); // <--- add these lines break; ... ... case SCSI_READ10: // <--- change just the label to SCSI_READ12 ... ... case SCSI_WRITE10: // <--- change just the label to SCSI_WRITE12 ... ... case SCSI_READ_TOC: // <--- add these lines MSC_ReadTOC(); break;
void MSC_StartStopUnit (void) { CSW.bStatus = CSW_CMD_FAILED; if (CBW.CB[4] & 0x02) { // check LOEJ (Load/Eject) bit if (CBW.CB[4] & 0x01) { // check START bit // // load medium, if required // } else { // // unload medium, if required // } CSW.bStatus = CSW_CMD_PASSED; } MSC_SetCSW(); } BOOL MSC_RWSetup (void) { ... ... /* Number of Blocks to transfer */ // n = (CBW.CB[7] << 8) | // <--- modify these lines // (CBW.CB[8] << 0); n = (CBW.CB[6] << 24) | (CBW.CB[7] << 16) | (CBW.CB[8] << 8) | (CBW.CB[9] << 0); ... } void MSC_ReadTOC (void) { // check command parameters if ( ((CBW.CB[1] & 0x02) != 0) // MSF field || ((CBW.CB[2] & 0x0F) != 0) // Format-A field || ( CBW.CB[7] != 0x00) // Allocation Length MSB field || ( CBW.CB[8] != 0x0C) // Allocation Length LSB field || ((CBW.CB[9] & 0xC0) != 0x40) // Format-B field ) return; // TOC header BulkBuf[ 0] = 0x00; // TOC Data Length (= 0x0A) BulkBuf[ 1] = 0x0A; BulkBuf[ 2] = 0x01; // First Complete Session Number (= 0x01) BulkBuf[ 3] = 0x01; // Last Complete Session Number (= 0x01 for single session) // TOC Track Descriptor BulkBuf[ 4] = 0x00; // Reserved BulkBuf[ 5] = 0x14; // ADR/CTL BulkBuf[ 6] = 0x01; // First Track Number in Last Complete Session BulkBuf[ 7] = 0x00; // Reserved BulkBuf[ 8] = 0x00; // Logical Block Address of First Track in Last Session BulkBuf[ 9] = 0x00; BulkBuf[10] = 0x00; BulkBuf[11] = 0x00; BulkLen = 12; DataInTransfer(); }
Tsuneo
Hey Tsuneo! your answers are the most unique, effective, flawless and complete ones all over this forum and any other forum i saw.
Thank you man and ... God Bless You!(well, Keil should not forget about its own share too :D)
Hi Tsuneo!
Thanks for your first help!
I changed codes according to your help and loaded it to my board and attached it to a PC. It appeared as a CD-ROM in different versions of windows (WIN& & XP)(x64 & x32).
But there is a problem, it didn't work correctly. Then I debugged it and I found that the MSC_ReadTOC function returns at if. . . at the beginning of function, because some of condition is true and it becomes true and the function returns. according to usb_msc_boot_1.0.pdf document , CBW.CB elements must be differ from what are now.
whats your idea about this!
And one another question: Original sample code of atmel consist of one image of a text file , that is copied to ram for demo in USB drive (stick) state, and the ram is the storage media. So when we attached it to computer we see a text file in it .Now we change the code to CD-ROM state , must we see it in cd-rom as before or cd-rom can't show it because of CD IMAGE concepts or other reason?
You are best helper for this forum, thank you for your reply again. :)
Hi my friend , Tsuneo!
I come back for more explanation about the problem.
first question is that in mscuser.c , we have SCSI_READ10 in definition of function MSC_BulkIn() , we must change it to 12 or not?
When I debug code, the CBW.CB elements that make the if statement true are:
CBW.CB[1]= 0x02 CBW.CB[2]= 0x00 CBW.CB[7]= 0x03 CBW.CB[8]= 0x24 CBW.CB[9]= 0x00
I think that there are some wrong parameter that are sent in previous commands before MSC-ReadTOC that make the wrong received command from pc in this point.
please help me, thanks.
Hi my friend again!
I thought that monitoring device with a application like device monitoring studio can help us, so the result is :
command level
START
000027: SCSI Primary Command Set 2 (DOWN), 09.09.2012 15:46:16.282 +0.0 Opcode: 0x12 Name: INQUIRY Enable Vital Product Data: FALSE Command Support Data: FALSE Page or OPCODE: 0x0 Allocation Length: 0x24 Control: 0x0 000030: Expected Data Received (UP), 09.09.2012 15:46:16.286 +0.002 Size: 0x24 000032: Command Status Wrapper (UP), 09.09.2012 15:46:16.288 +0.002 000033: SCSI Primary Command Set 2 (DOWN), 09.09.2012 15:46:16.288 +0.0 Opcode: 0x12 Name: INQUIRY Enable Vital Product Data: FALSE Command Support Data: FALSE Page or OPCODE: 0x0 Allocation Length: 0x24 Control: 0x0 000036: Expected Data Received (UP), 09.09.2012 15:46:16.292 +0.002 Size: 0x24 000038: Command Status Wrapper (UP), 09.09.2012 15:46:16.294 +0.002 000039: SCSI Primary Command Set 2 (DOWN), 09.09.2012 15:46:16.294 +0.0 Opcode: 0x12 Name: INQUIRY Enable Vital Product Data: TRUE Command Support Data: FALSE Page or OPCODE: 0x80 Allocation Length: 0xff Control: 0x0 000042: Expected Data Received (UP), 09.09.2012 15:46:16.298 +0.002 Size: 0x24 000044: Command Status Wrapper (UP), 09.09.2012 15:46:16.299 +0.001 000047: SCSI Primary Command Set 2 (DOWN), 09.09.2012 15:46:16.303 +0.004 Opcode: 0x12 Name: INQUIRY Enable Vital Product Data: FALSE Command Support Data: FALSE Page or OPCODE: 0x0 Allocation Length: 0x24 Control: 0x0 000050: Expected Data Received (UP), 09.09.2012 15:46:16.306 +0.002 Size: 0x24 000052: Command Status Wrapper (UP), 09.09.2012 15:46:16.308 +0.002 000053: SCSI Primary Command Set 2 (DOWN), 09.09.2012 15:46:16.308 +0.0 Opcode: 0x12 Name: INQUIRY Enable Vital Product Data: FALSE Command Support Data: FALSE Page or OPCODE: 0x0 Allocation Length: 0x25 Control: 0x0 000056: Expected Data Received (UP), 09.09.2012 15:46:16.312 +0.002 Size: 0x24 000058: Command Status Wrapper (UP), 09.09.2012 15:46:16.313 +0.001 000059: SCSI Primary Command Set 2 (DOWN), 09.09.2012 15:46:16.313 +0.0 Opcode: 0x12 Name: INQUIRY Enable Vital Product Data: FALSE Command Support Data: FALSE Page or OPCODE: 0x0 Allocation Length: 0x24 Control: 0x0 000062: Expected Data Received (UP), 09.09.2012 15:46:16.317 +0.002 Size: 0x24 000064: Command Status Wrapper (UP), 09.09.2012 15:46:16.319 +0.002 000065: SFF-8020i, MMC2 (ATAPI) command set (DOWN), 09.09.2012 15:46:16.320 +0.001 Opcode: 0x4a Name: GET EVENT/STATUS IMMED: TRUE Notification Class Request:0x0 Allocation Length:0x8 Control: 0x0 000069: SFF-8020i, MMC2 (ATAPI) command set (DOWN), 09.09.2012 15:46:16.476 +0.153 Opcode: 0x46 Name: GET CONFIGURATION (MMC) Request Type: LU shall return Feature Header and all Feature Descriptors Starting Feature Number:0x0 Allocation Length:0x8 Control: 0x0 000073: SFF-8020i, MMC2 (ATAPI) command set (DOWN), 09.09.2012 15:46:16.633 +0.153 Opcode: 0x46 Name: GET CONFIGURATION (MMC) Request Type: LU shall return Feature Header and all Feature Descriptors Starting Feature Number:0x0 Allocation Length:0x8 Control: 0x0 000077: SFF-8020i, MMC2 (ATAPI) command set (DOWN), 09.09.2012 15:46:16.790 +0.153 Opcode: 0x46 Name: GET CONFIGURATION (MMC) Request Type: LU shall return Feature Header and all Feature Descriptors Starting Feature Number:0x0 Allocation Length:0x8 Control: 0x0 000081: SFF-8020i, MMC2 (ATAPI) command set (DOWN), 09.09.2012 15:46:16.947 +0.153 Opcode: 0x46 Name: GET CONFIGURATION (MMC) Request Type: LU shall return Feature Header and all Feature Descriptors Starting Feature Number:0x0 Allocation Length:0x8 Control: 0x0 000085: SFF-8020i, MMC2 (ATAPI) command set (DOWN), 09.09.2012 15:46:17.104 +0.153 Opcode: 0x46 Name: GET CONFIGURATION (MMC) Request Type: LU shall return Feature Header and all Feature Descriptors Starting Feature Number:0x0 Allocation Length:0x8 Control: 0x0 000089: SFF-8020i, MMC2 (ATAPI) command set (DOWN), 09.09.2012 15:46:17.261 +0.153 Opcode: 0x46 Name: GET CONFIGURATION (MMC) Request Type: LU shall return Feature Header and all Feature Descriptors Starting Feature Number:0x0 Allocation Length:0x8 Control: 0x0 000093: SFF-8020i, MMC2 (ATAPI) command set (DOWN), 09.09.2012 15:46:17.418 +0.153 Opcode: 0x25 Name: READ CAPACITY (MMC) Logical Block Address:0x0 Control: 0x0 000097: SFF-8020i, MMC2 (ATAPI) command set (DOWN), 09.09.2012 15:46:17.575 +0.153 Opcode: 0x25 Name: READ CAPACITY (MMC) Logical Block Address:0x0 Control: 0x0 000101: SFF-8020i, MMC2 (ATAPI) command set (DOWN), 09.09.2012 15:46:17.732 +0.153 Opcode: 0x25 Name: READ CAPACITY (MMC) Logical Block Address:0x0 Control: 0x0 000105: SCSI Primary Command Set 2 (DOWN), 09.09.2012 15:46:17.889 +0.153 Name: TEST UNIT READY Control: 0x0 000108: Command Status Wrapper (UP), 09.09.2012 15:46:17.893 +0.002 000109: SCSI Primary Command Set 2 (DOWN), 09.09.2012 15:46:18.046 +0.153 Name: TEST UNIT READY Control: 0x0 000112: Command Status Wrapper (UP), 09.09.2012 15:46:18.050 +0.002 000113: SCSI Primary Command Set 2 (DOWN), 09.09.2012 15:46:18.050 +0.0 Name: TEST UNIT READY Control: 0x0 000116: Command Status Wrapper (UP), 09.09.2012 15:46:18.054 +0.002 000117: SCSI Primary Command Set 2 (DOWN), 09.09.2012 15:46:18.054 +0.0 Name: TEST UNIT READY Control: 0x0 000120: Command Status Wrapper (UP), 09.09.2012 15:46:18.058 +0.002 000121: SFF-8020i, MMC2 (ATAPI) command set (DOWN), 09.09.2012 15:46:18.058 +0.0 Opcode: 0x43 Name: READ TOC/PMA/ATIP (MMC) MSF: TRUE SUBQ: FALSE Track Number: 0x0 Allocation Length:0x324 Control: 0x0
END After this command , no other command transferred. I will post mass storage level in next post.
Thank you for your help!
> first question is that in mscuser.c , we have SCSI_READ10 in definition of function MSC_BulkIn() , we must change it to 12 or not?
READ10 or READ12? USB MSC compliance (*1) defines READ12 is required, and READ10 is optional for CD/DVD. But Major OS (Windows/Linux/MacOSX) issues READ10 first. When READ10 fails, READ12 is applied. Therefore, you may support both of them; READ12 for compliance, READ10 for performance.
The difference of READ10 and READ12 lies in the size and location of TRANSFER LENGTH parameter. READ10: two bytes (byte 7, 8 - starting from 0) READ12: four bytes (byte 6, 7, 8, 9) In the KEIL MSC implementation, MSC_RWSetup() picks up TRANSFER LENGTH from the CB (Command Block)
To support both of READ10 and READ12, - Don't need to touch to SCSI_READ10 label and MSC_RWSetup() - Add extra SCSI_READ12 label and MSC_RWSetup12(), which are copied from READ10 implementation, and modified.
mscuser.c void MSC_GetCBW (void) { ... ... switch (CBW.CB[0]) { ... ... case SCSI_READ10: // <--- don't need to touch to ... // no change ... case SCSI_READ12: // <--- Add this label and contents if (MSC_RWSetup12()) { // <--- replace to MSC_RWSetup12, the rest is same as READ10 if ((CBW.bmFlags & 0x80) != 0) { AT91C_BASE_PIOA->PIO_CODR = LED1; /* Turn On Read LED */ BulkStage = MSC_BS_DATA_IN; MSC_MemoryRead(); } else { USB_SetStallEP(MSC_EP_OUT); CSW.bStatus = CSW_PHASE_ERROR; MSC_SetCSW(); } } break;
BOOL MSC_RWSetup (void) { ... // no change ... } BOOL MSC_RWSetup12 (void) { ... // <--- copy the contents of MSC_RWSetup() ... /* Number of Blocks to transfer */ // n = (CBW.CB[7] << 8) | // <--- modify these lines // (CBW.CB[8] << 0); n = (CBW.CB[6] << 24) | (CBW.CB[7] << 16) | (CBW.CB[8] << 8) | (CBW.CB[9] << 0); ... }
(*1) USB Mass Storage Class Compliance Test Specification www.singlix.org/.../MSC-compliance-0_9a.pdf 4.2 Command Set Information
> I think that there are some wrong parameter that are sent in previous commands before MSC-ReadTOC that make the wrong received command from pc in this point.
Windows put both of READ_TOC(MSF:0) and READ_TOC(MSF:1) In above implementation, just READ_TOC(MSF:0) is supported. To support both, MSC_ReadTOC() changes as follows,
const BYTE SCSI_TOC_MSF0[] = { // TOC response header 0x00, 0x0A, // Data Length 0x01, // First Track 0x01, // Last Track // track descriptor 0x00, // Reserved 0x14, // ADR/CONTROL 0x01, // Track Number 0x00, // Reserved 0x00, 0x00, 0x00, 0x00 // Track Start Address }; const BYTE SCSI_TOC_MSF1[] = { // TOC response header 0x00, 0x12, // Data Length 0x01, // First Track 0x01, // Last Track // track descriptor 1 0x00, // Reserved 0x14, // ADR/CONTROL 0x01, // Track Number 0x00, // Reserved 0x00, 0x00, 0x02, 0x00, // start address in MSF format (fixed) // track descriptor 2 0x00, // Reserved 0x14, // ADR/CONTROL 0xAA, // Track Number: Lead-out area 0x00, // Reserved 0x00, 0x39, 0x06, 0x06 // Lead-out start address in MSF format // (variable, depending on the size of track 1) }; void MSC_ReadTOC (void) { if ( ((CBW.CB[1] & 0x02) == 0) // MSF field && ((CBW.CB[2] & 0x0F) == 0) // Format field ) { memcpy( BulkBuf, SCSI_TOC_MSF0, sizeof(SCSI_TOC_MSF0) ); BulkLen = sizeof(SCSI_TOC_MSF0); DataInTransfer(); } if ( ((CBW.CB[1] & 0x02) == 1) // MSF field && ((CBW.CB[2] & 0x0F) == 0) // Format field ) { memcpy( BulkBuf, SCSI_TOC_MSF1, sizeof(SCSI_TOC_MSF1) ); BulkLen = sizeof(SCSI_TOC_MSF1); DataInTransfer(); } }
> Now we change the code to CD-ROM state , must we see it in cd-rom as before or cd-rom can't show it because of CD IMAGE concepts or other reason?
KEIL original example gives disk image in DiskImage[] ROM array (DiskImg.c). This disk image consists of FAT file system - MBR (Master Boot Record) - Boot sector - FAT - Root directory - File sectors
For CD implementation, you have to replace the contents of the disk image into CD one, which is formatted following to ISO9660 (ECMA-119) spec (see my first post). You may easily make it using a CD-R writer utility, which has a function to make ISO9660 image file.
At the entry of main() (memory.c), the contents of DiskImage[] is copied to Memory[] RAM array. READ10 and WRITE10 access to Memory[] array. For read-only CD, READ10 may directly access to DiskImage[] ROM array, instead of the RAM array.
Hi my friend Tsuneo!
I do what you post for me but , that didn't work . I changed what you said and I made an iso file and fetch the byte stream of it and changed the code according that (disk Image , size , . . . ) , but when I attach device to computer , my computer (explorer) goes to a wait sate and can't work with the device.
thank my friend!
Hi Mr Chinzei.
Me and my friend Meysam were working around your solution to make our USB Mass storage device a CD-ROM.
BUt As we started the work, lots of problems poped out.
we changed the PDT and Sector size as you guided. We also implemented the following commands as you said: * Read12 * Write12 * Start/Stop unit * Read TOC
Then we loaded an image of a sample CD into device memory.
But it seams we can't get it to work, So I think we have to check it out with the standard specifications.
We searced for some guides or references about this matter and this is what we found:
SCSI Multimedia commands from T10.org.
What i see in here is 6 different versions of MMC commands. I looked in MMC-4 and there's alot of commands for CD-ROM devices in it which I can't get why and how you only choosed a subset of these commands for us to implement.
I am wondering now, whether you know an implementation guidance or a book in which the essential steps for making a CD-ROM device is explained or you use the T10 references too.
Do you know any useful book or guide for my problem?
We could get our CD-ROM to work!
I post the modifications we made to your soluton in order to help others who might read this thread later.
First: I changed the Read TOC function so that it respond to all other read TOC formats too. It is a shame! but I responded all Formats with the same data, Like another USB CD-ROM I was monitoring its commands.
well, my analisys is that we had to make a proper response to any Read TOC which this code do not.
Also the '1' at second if is a logical error.
void MSC_ReadTOC (void) { if ( ((CBW.CB[1] & 0x02) == 0) // MSF field && ((CBW.CB[2] & 0x0F) == 0) // Format field ) { memcpy( BulkBuf, SCSI_TOC_MSF0, sizeof(SCSI_TOC_MSF0) ); BulkLen = sizeof(SCSI_TOC_MSF0); DataInTransfer(); } if ( ((CBW.CB[1] & 0x02) == 1) // MSF field <-----===== Change '1' to '2' && ((CBW.CB[2] & 0x0F) == 0) // Format field ) { memcpy( BulkBuf, SCSI_TOC_MSF1, sizeof(SCSI_TOC_MSF1) ); BulkLen = sizeof(SCSI_TOC_MSF1); DataInTransfer(); } //<-----===== Put the code to handle other formats here, even if you going to fail it(i did not test the fail myself) }
After this the CD can be succecfully recognized in windows, but it has two halts(1 min each). These halts happen after failing "Read Disc Information" and "Get Configuration Commands" which was removed after implementing these commads based on MMC commands reffrence from T10.org