void MSC_DataIn(tUSBInterface *Dev, int EndPt, int Length, void *Data);
// --- Internal Helpers
int MSC_int_CreateCBW(tMSC_CBW *Cbw, int bInput, size_t CmdLen, const void *CmdData, size_t DataLen);
-void MSC_SendData(tUSBInterface *Dev, size_t CmdLen, const void *CmdData, size_t DataLen, const void *Data);
-void MSC_RecvData(tUSBInterface *Dev, size_t CmdLen, const void *CmdData, size_t DataLen, void *Data);
+ int MSC_SendData(tUSBInterface *Dev, size_t CmdLen, const void *CmdData, size_t DataLen, const void *Data);
+ int MSC_RecvData(tUSBInterface *Dev, size_t CmdLen, const void *CmdData, size_t DataLen, void *Data);
// === GLOBALS ===
MODULE_DEFINE(0, VERSION, USB_MSC, MSC_Initialise, MSC_Cleanup, "USB_Core", NULL);
// SCSI Transparent Command Set
case 0x06:
info->BlockCount = MSC_SCSI_GetSize(Dev, &info->BlockSize);
+ // HACK! Try twice if needed
+ // Qemu Tegra2 appears to error with Sense=RESET on the first attempt
+ if( !info->BlockCount )
+ info->BlockCount = MSC_SCSI_GetSize(Dev, &info->BlockSize);
vt = &gMSC_SCSI_VolType;
break;
// Unknown, prepare to chuck sad
free(info);
return ;
}
+
+ // Zero size indicates some form of critical error
+ if( !info->BlockCount ) {
+ Log_Error("USB MSC", "Device did not report a valid size");
+ USB_SetDeviceDataPtr(Dev, NULL);
+ free(info);
+ return ;
+ }
// Create device name from Vendor ID, Device ID and Serial Number
Uint16 vendor, devid;
return 0;
}
-void MSC_SendData(tUSBInterface *Dev, size_t CmdLen, const void *CmdData, size_t DataLen, const void *Data)
+int MSC_SendData(tUSBInterface *Dev, size_t CmdLen, const void *CmdData, size_t DataLen, const void *Data)
{
tMSC_CBW cbw;
tMSC_CSW csw;
const int endpoint_in = 1;
if( MSC_int_CreateCBW(&cbw, 0, CmdLen, CmdData, DataLen) )
- return ;
+ return 2;
// Send CBW
USB_SendData(Dev, endpoint_out, sizeof(cbw), &cbw);
// Read CSW
USB_RecvData(Dev, endpoint_in, sizeof(csw), &csw);
// TODO: Validate CSW
+
+ return (csw.bCSWStatus != 0x00);
}
-void MSC_RecvData(tUSBInterface *Dev, size_t CmdLen, const void *CmdData, size_t DataLen, void *Data)
+int MSC_RecvData(tUSBInterface *Dev, size_t CmdLen, const void *CmdData, size_t DataLen, void *Data)
{
tMSC_CBW cbw;
tMSC_CSW csw;
const int endpoint_in = 1;
if( MSC_int_CreateCBW(&cbw, 1, CmdLen, CmdData, DataLen) )
- return ;
+ return 2;
// Send CBW
USB_SendData(Dev, endpoint_out, sizeof(cbw), &cbw);
// Read CSW
USB_RecvData(Dev, endpoint_in, sizeof(csw), &csw);
+
+ Debug_HexDump("MSC RecvData, cbw=", &cbw, sizeof(cbw));
+ Debug_HexDump("MSC RecvData, csw=", &csw, sizeof(csw));
+ Debug_HexDump("MSC RecvData, Data=", Data, DataLen);
+
// TODO: Validate CSW
+ if( LittleEndian32(csw.dCSWSignature) != MSC_CSW_SIGNATURE ) {
+ Log_Warning("MSC", "CSW Signature invalid %x!=exp %x",
+ LittleEndian32(csw.dCSWSignature), MSC_CSW_SIGNATURE);
+ }
+ if( csw.dCSWTag != cbw.dCBWTag ) {
+ Log_Warning("MSC", "Recv - CSW tag (%x) doesn't match CBW tag (%x)",
+ LittleEndian32(csw.dCSWTag), LittleEndian32(cbw.dCBWTag));
+ }
+ if( LittleEndian32(csw.dCSWDataResidue) > DataLen ) {
+ Log_Warning("MSC", "Recv - CSW residue (0x%x) is larger than DatLen (0x%x)",
+ LittleEndian32(csw.dCSWDataResidue), DataLen);
+ }
+ if( csw.bCSWStatus != 0x00 ) {
+ Log_Warning("MWC", "Recv - CSW indicated error (Status=%02x, Residue=0x%x/0x%x)",
+ csw.bCSWStatus, LittleEndian32(csw.dCSWDataResidue), DataLen);
+ }
+
+ return (csw.bCSWStatus != 0x00);
}
// === PROTOTYPES ===
Uint64 MSC_SCSI_GetSize(tUSBInterface *Dev, size_t *BlockSize);
+void MSC_SCSI_DumpSenseData(tUSBInterface *Dev);
int MSC_SCSI_ReadData(void *Ptr, Uint64 Block, size_t Count, void *Dest);
int MSC_SCSI_WriteData(void *Ptr, Uint64 Block, size_t Count, const void *Dest);
// === CODE ===
Uint64 MSC_SCSI_GetSize(tUSBInterface *Dev, size_t *BlockSize)
{
- struct sSCSI_Cmd_ReadCapacity16 cmd;
- struct sSCSI_Ret_ReadCapacity16 ret;
-
- cmd.Op = 0x9E;
- cmd.Svc = 0x10;
- cmd.LBA = BigEndian64( 0 );
- cmd.AllocationLength = BigEndian32(sizeof(ret));
- cmd.Flags = 0;
- cmd.Control = 0;
+ // 1. Attempt a 10 op
+ {
+ struct sSCSI_Cmd_ReadCapacity10 cmd = {READ_CAPACITY_10,0,0,0,0,0};
+ struct sSCSI_Ret_ReadCapacity10 ret = {0,0};
+
+ if( MSC_RecvData(Dev, sizeof(cmd), &cmd, sizeof(ret), &ret) != 0 ) {
+ // Oops?
+ Log_Warning("MSC SCSI", "Command \"Read Capacity (10)\" failed");
+ MSC_SCSI_DumpSenseData(Dev);
+ return 0;
+ }
+
+ if( ret.LastBlock != 0xFFFFFFFF )
+ {
+ *BlockSize = BigEndian32(ret.BlockSize);
+ return BigEndian32(ret.LastBlock)+1;
+ }
+
+ // Size was too large for 32-bits, attempt a 16-byte op
+ }
+
+ // 2. Attempt a 16 byte op
+ {
+ struct sSCSI_Cmd_ReadCapacity16 cmd;
+ struct sSCSI_Ret_ReadCapacity16 ret;
+
+ cmd.Op = SERVICE_ACTION_IN_16;
+ cmd.Svc = 0x10;
+ cmd.LBA = BigEndian64( 0 );
+ cmd.AllocationLength = BigEndian32(sizeof(ret));
+ cmd.Flags = 0;
+ cmd.Control = 0;
+
+ ret.BlockSize = 0;
+ ret.LastBlock = 0;
+ if( MSC_RecvData(Dev, sizeof(cmd), &cmd, sizeof(ret), &ret) != 0 ) {
+ // Oops?
+ Log_Warning("MSC SCSI", "Command \"Read Capacity (16)\" failed");
+ return 0;
+ }
+
+ *BlockSize = BigEndian32(ret.BlockSize);
+ return BigEndian64(ret.LastBlock)+1;
+ }
+}
- ret.BlockSize = 0;
- ret.BlockCount = 0;
- MSC_RecvData(Dev, sizeof(cmd), &cmd, sizeof(ret), &ret);
+void MSC_SCSI_DumpSenseData(tUSBInterface *Dev)
+{
+ struct sSCSI_SenseData ret;
+ struct sSCSI_Cmd_RequestSense cmd = {0x03, 0, 0, sizeof(ret), 0};
+
+ int rv = MSC_RecvData(Dev, sizeof(cmd), &cmd, sizeof(ret), &ret);
+ if( rv != 0 ) {
+ // ... oh
+ Log_Warning("MSC SCSI", "Command \"Request Sense\" failed, giving up");
+ return ;
+ }
- *BlockSize = BigEndian32(ret.BlockSize);
- return BigEndian64(ret.BlockCount);
+ const char *const sense_keys[16] = {
+ "No Sense", "Recovered Error", "Not Ready", "Medium Error",
+ "Hardware Error", "Illegal Request", "Unit Attention", "Data Protect",
+ "Blank Check", "Vendor Specific", "Copy Aborted", "Aborted Command",
+ "-obs-", "Volume Overflow", "Miscompare", "-rsvd-"
+ };
+ Log_Debug("MSC SCSI", "Dumping Sense Data:");
+ Log_Debug("MSC SCSI", ".ResponseCode = %02x", ret.ResponseCode);
+ Log_Debug("MSC SCSI", ".Flags = %02x (%s%s%s%s)", ret.Flags,
+ (ret.Flags & (1<<7) ? "FILEMARK,":""),
+ (ret.Flags & (1<<6) ? "EOM,":""),
+ (ret.Flags & (1<<6) ? "ILI,":""),
+ sense_keys[ret.Flags & 15]
+ );
+ Log_Debug("MSC SCSI", ".Information = %08x", BigEndian32(ret.Information));
+ Log_Debug("MSC SCSI", ".AdditionalLen = %02x", ret.AdditionalSenseLength);
+ Log_Debug("MSC SCSI", ".CommandSpecInfomation = %08x", BigEndian32(ret.CommandSpecInfomation));
+ Log_Debug("MSC SCSI", ".ASC = %02x", ret.AdditionalSenseCode);
+ Log_Debug("MSC SCSI", ".ASCQ = %02x", ret.AdditionalSenseCodeQual);
}
int MSC_SCSI_ReadData(void *Ptr, Uint64 Block, size_t Count, void *Dest)
{
tUSBInterface *Dev = Ptr;
tMSCInfo *info = USB_GetDeviceDataPtr(Dev);
- struct sSCSI_Cmd_Read16 cmd;
-
- // TODO: Bounds checking?
- cmd.Op = 0x88;
- cmd.Flags = 0;
- cmd.LBA = BigEndian64(Block);
- cmd.Length = BigEndian32(Count);
- cmd.GroupNumber = 0;
- cmd.Control = 0;
+ // Error if more than 64 thousand blocks are read at once
+ if( Count > (1UL<<16) ) {
+ // What the fsck are you smoking?
+ Log_Error("MSC SCSI", "Attempt to read >16-bits worth of blocks");
+ return 0;
+ }
- MSC_RecvData(Dev, sizeof(cmd), &cmd, Count*info->BlockSize, Dest);
- // TODO: Error check
+ // TODO: Bounds checking?
+ int ret;
+ if( Block < (1UL << 20) && Count <= (1UL << 8) )
+ {
+ // Read (6)
+ struct sSCSI_Cmd_Read6 cmd;
+ cmd.Op = 0x08;
+ cmd.LBATop = Block >> 16;
+ cmd.LBALower = BigEndian16( Block & 0xFFFF );
+ cmd.Length = Count & 0xFF; // 0 == 256
+ cmd.Control = 0;
+
+ ret = MSC_RecvData(Dev, sizeof(cmd), &cmd, Count*info->BlockSize, Dest);
+ }
+ else if( Block <= 0xFFFFFFFF )
+ {
+ // Read (10)
+ struct sSCSI_Cmd_Read10 cmd;
+ cmd.Op = 0x28;
+ cmd.Flags = 0;
+ cmd.LBA = BigEndian32( Block );
+ cmd.GroupNumber = 0;
+ cmd.Length = BigEndian16( Count ); // 0 == no blocks
+ cmd.Control = 0;
+
+ ret = MSC_RecvData(Dev, sizeof(cmd), &cmd, Count*info->BlockSize, Dest);
+ }
+ else
+ {
+ // Read (16)
+ struct sSCSI_Cmd_Read16 cmd;
+ cmd.Op = 0x88;
+ cmd.Flags = 0;
+ cmd.LBA = BigEndian64(Block);
+ cmd.Length = BigEndian32(Count);
+ cmd.GroupNumber = 0;
+ cmd.Control = 0;
+
+ ret = MSC_RecvData(Dev, sizeof(cmd), &cmd, Count*info->BlockSize, Dest);
+ // TODO: Error check
+ }
+
+ if( ret != 0 )
+ {
+ // TODO: Get read size and find out how much was read
+ return 0;
+ }
+
return Count;
}
int MSC_SCSI_WriteData(void *Ptr, Uint64 Block, size_t Count, const void *Data)
{
- Log_Warning("MSC_SCSI", "TODO: Impliment MSC_SCSI_WriteData");
+ Log_Warning("MSC_SCSI", "TODO: Implement MSC_SCSI_WriteData");
return 0;
}