Per request, I have moved this info to this lone thread.The MSD code has many serious bugs that cause intermittant problems. Here are the solutions from a 24 hour hack that I did.
============================
I have made so many changes that it is difficult to relate them to the original code. I will try.
1. The FLASH timing flag is missing. Add:
FLSCL |= 0x90; // set 48MHz for flash
At the bottom of the clock set if you are using USB_4X_CLOCK.
2. A trick I got from Tsuneo, if you are not bus powered, disable suspend:
POLL_WRITE_BYTE(POWER, 0x00); // disable USB0 suspend detection
Everywhere there is a POWER. I also added it just before setting the ports in Port_Init() just to be safe.
3. [Removed]
4. Add a short delay before setting the crossbar in Port_Init();
for(i=0;i<5000;i++) Delay();
//XBARE = 1; // Enable Crossbar
XBR1 |= 0x40;
5. In the descriptors:
A. configuration_descriptor - bmAttributes - If you are not bus-powered, change to 0xC0 from 0x80. This will prevent random overheats in the usb hub.
B. when Get_Descriptor() is called with DSC_CONFIG, the whole set needs to be returned, configuration_descriptor, interface_descriptor, and the two endpoint_descriptors. Group them into one structure containing all four, and return that structure and the size of the super-struct.
C. Get_Descriptor() - DSC_STRING - String #3 the serial number. Always remember to return a unique number in UNICODE.
6. I turned on Reset_MSD() and Get_MaxLUN() although I'm not sure they are needed.
7. in ISR.c
A. Important! Change USB_In to return a true or false depending on if Handle_In() is called. This is important later.
B. Disable interrupts around the code in Handle_In1(). It uses EINCSR1, which is indexed by INDEX. EINCSR1 is also called inside the interrupt, with a different INDEX!
8. In MSD_MSD.c.
A. Mistake - the MSD_READY code is very flawed. It throws out many good packets. Here is fixed code:
case MSD_READY:
if(Out_Count)
{
static BYTE xdata Out_Packet[EP2_PACKET_SIZE];
static unsigned outc;
// Look for a "valid" and "meaningful" CBW, as defined in the spec:
outc = Out_Count;
// Get the Data - use Out_Packet in case it is more than 29 bytes
Out2_Get_Data(Out_Packet);
Out2_Done();
if( outc < sizeof(CBW) )
{
// Garbage
return;
}
cbw = *(CBW*)Out_Packet;
// Valid signature?
if( cbw.dCBWSignature != CBW_SIGNATURE ) return;
// Valid CBW length?
if( ((cbw.bCBWCBLength>16) || (cbw.bCBWCBLength<1)) ) return;
// LUN for me?
if( (cbw.bCBWLUN!=0x00) ) return;
// do the data
Msd_State=MSD_DATA;
}
break;
B. The MSD_STATUS_TRANSPORT assumes that the data is sent. That is incorrect. MSD can jam if the data is not sent. Based on setting the USB_In() to return a value:
case MSD_STATUS_TRANSPORT:
// Reply with a CSW:
csw.dCSWSignature=CSW_SIGNATURE;
csw.dCSWTag=cbw.dCBWTag;
csw.bCSWStatus=Scsi_Status;
csw.dCSWDataResidue=ntohl(Scsi_Residue);
// Send the CSW
if( USB_In((BYTE*)&csw,sizeof(CSW)) )
{
// Success!
Msd_State=MSD_READY;
}
else
{
// Keep trying until I get something new
if( Out_Count )
Msd_State=MSD_READY;
}
break;
This will keep trying until it succeeds or a recovery packet is sent.
9. MSD_scsi.c – This is really messed up. I rewrote it, so here are some of my basic changes:
A. The Scsi_Standard_Inquiry_Data structure.
The response format should be 0, not 2.
There are eight bytes of data missing. Make sure the Mass Storage descriptor is complete, and that there is a version number.
code const BYTE Scsi_Standard_Inquiry_Data[36]= {
0x00, // Peripheral qualifier & peripheral device type
0x80, // Removable medium
0x05, // Version of the standard (2=obsolete, 5=SPC-3)
0x00, // No NormACA, No HiSup, response data format=2
0x1F, // No extra parameters
0x00, // No flags
0x80, // 0x80 => BQue => Basic Task Management supported
0x00, // No flags
'S','i','L','a','b','s',' ',' ', // Requested by Dekimo via www.t10.org
'M','a','s','s',' ','S','t','o','r','a','g','e',' ',' ',' ',' ',
'1','.','0','0'
};
B. Mode Sense 6 should NOT be used. Mode Sense 10 is the one allowed for Mass Storage. And you also need to add the Flex Disk descriptor:
code const BYTE __Scsi_Mode_Sense_10[8] =
{ 0x00,0x26,0,0,0,0,0,0 }; // No mode sense parameter
#define bedistsb(s) (BYTE)((((unsigned short)s)>>8)&0x0FF), (BYTE)(((( unsigned short)s))&0x0FF)
#define _CYL(l) (((l)/SECTORS_PER_TRACK)/(HEADS_PER_CYLINDER+1))
#define _HEAD(l) (((l)/SECTORS_PER_TRACK)%(HEADS_PER_CYLINDER+1))
#define _SECT(l) (((l)%SECTORS_PER_TRACK)+1)
code const BYTE __Scsi_Request_Sense_Flexible_Disk_Page[32] =
{
0x05, // Page Code
0x1E, // Page Length
bedistsb(0x00FA), // Transfer rate
HEADS_PER_CYLINDER, // Heads
SECTORS_PER_TRACK, // Sectors/Track
bedistsb(PHYSICAL_BLOCK_SIZE), // Block length
bedistsb(_CYL(MAX_SECTORS)), // Cylinders
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x05, // Motor ON
0x1E, // Motor OFF
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
bedistsb(300), // rotation rate
0x00, 0x00
};
void Scsi_Mode_Sense10()
{
struct ModeSenseIn
{
BYTE OperationCode;
BYTE LUN;
BYTE Page;
BYTE Res[4];
unsigned short wListLen;
BYTE Res2;
};
unsigned short _maxsize = ntohs(((struct ModeSenseIn *)cbw.CBWCB)->wListLen);
Scsi_Status=SCSI_PASSED;
// Return Default
//if( cbw.CBWCB[2]&0x80 )
{
Scsi_SenseKey=SCSI_NO_SENSE;
Scsi_SenseCode=0x00;
Scsi_SenseQualifier=0x00;
if( sizeof(__Scsi_Mode_Sense_10) <= _maxsize )
_maxsize -= sizeof(__Scsi_Mode_Sense_10);
else
{
Scsi_SenseKey=SCSI_ILLEGAL_REQUEST;
Scsi_SenseCode=0x1A; // Illegal Parameter
Scsi_SenseQualifier=0x00;
return;
}
Scsi_Send(__Scsi_Mode_Sense_10,sizeof(__Scsi_Mode_Sense_10));
if( _maxsize >= sizeof(__Scsi_Request_Sense_Flexible_Disk_Page) )
Scsi_Send(__Scsi_Request_Sense_Flexible_Disk_Page,sizeof(__Scsi_Request_Sense_Flexible_Disk_Page));
return;
}
}
C. And the matching call in Scsi_rx():
#define SCSI_MODE_SELECT_10 0x55
#define SCSI_MODE_SENSE_10 0x5A case SCSI_MODE_SENSE_10:
Scsi_Mode_Sense10();
break;
D. On Read10 and Write10, the big mistake is using the cbw.dCBWDataTransferLength.
void Scsi_Read10()
{
struct ReadIn
{
BYTE OperationCode;
BYTE LUN;
DWORD dwLBA;
BYTE Res;
unsigned short wTransferLen;
BYTE Res2;
BYTE PAD[2];
};
unsigned short i,j;
//DWORD xdata d_len = ntohl(cbw.dCBWDataTransferLength);
unsigned short d_len = ntohs(((struct ReadIn *)cbw.CBWCB)->wTransferLen);
DWORD d_LBA = 0;
for(i=2;i<6;++i)
d_LBA = (d_LBA<<8)+cbw.CBWCB[i]; Scsi_Status=SCSI_PASSED;
Scsi_SenseKey=SCSI_NO_SENSE;
Scsi_SenseCode=0x00;
Scsi_SenseQualifier=0x00;
for(i=0;i< d_len && (Scsi_Residue >= PHYSICAL_BLOCK_SIZE);i++)
{
Sect_Read(d_LBA+i);
for(j=0; j < (PHYSICAL_BLOCK_SIZE-1);)
{
if( USB_In(Scratch+j, EP1_PACKET_SIZE) )
{
j += EP1_PACKET_SIZE;
}
}
Scsi_Residue -= PHYSICAL_BLOCK_SIZE;
}
}
void Scsi_Write10()
{
struct WriteIn
{
BYTE OperationCode;
BYTE LUN;
DWORD dwLBA;
BYTE Res;
unsigned short wTransferLen;
BYTE Res2;
BYTE PAD[2];
};
unsigned short i,j;
//DWORD xdata d_len = ntohl(cbw.dCBWDataTransferLength);
DWORD d_len = ntohs(((struct WriteIn *)cbw.CBWCB)->wTransferLen);
DWORD d_LBA = 0;
for(i=2;i<6;++i)
d_LBA = (d_LBA<<8)+cbw.CBWCB[i];
Scsi_Status=SCSI_PASSED;
Scsi_SenseKey=SCSI_NO_SENSE;
Scsi_SenseCode=0x00;
Scsi_SenseQualifier=0x00;
for(i=0;(i< d_len) && (Scsi_Residue >= PHYSICAL_BLOCK_SIZE);i++)
{
START_WRITE_COPY;
for(j=0;j < PHYSICAL_BLOCK_SIZE-1;)
{
while(!Out_Count);
Out2_Get_Data(Scratch+j);
j += Out_Count;
Out2_Done();
}
STOP_WRITE_COPY;
Sect_Write(d_LBA+i);
Scsi_Residue-=PHYSICAL_BLOCK_SIZE;
}
}
10. ntohs and ntohl are not defined in Keil
#define htonl(d) ((((DWORD)(d)&0xFF000000L)>>24)|(((DWORD)(d)&0x00FF0000L)>> 8)|(((DWORD)(d)&0x0000FF00L)<< 8)|(((DWORD)(d)&0x000000FFL)<<24))
#define htons(w) (((((WORD2)w)&0xFF00u)>>8)|((((WORD2)w)&0x00FFu)<<8))
#define ntohs htons
#define ntohl htonl
===========================================
I have many many more fixes, but they have to do with the disk.
-Erik
[This message has been edited by egawtry (edited August 29, 2010).]