From cd8146698b87d571d8393336fb013e6406ba7e5a Mon Sep 17 00:00:00 2001 From: John Hodge Date: Wed, 18 Dec 2013 22:15:22 +0800 Subject: [PATCH] Modules/USB MSC - Error handling and fix for Tegra2 Qemu --- KernelLand/Modules/USB/MSC/common.h | 4 +- KernelLand/Modules/USB/MSC/main.c | 49 +++++++- KernelLand/Modules/USB/MSC/msc_proto.h | 4 +- KernelLand/Modules/USB/MSC/scsi.c | 157 +++++++++++++++++++++---- KernelLand/Modules/USB/MSC/scsi.h | 65 +++++++++- 5 files changed, 243 insertions(+), 36 deletions(-) mode change 100644 => 100755 KernelLand/Modules/USB/MSC/common.h mode change 100644 => 100755 KernelLand/Modules/USB/MSC/main.c mode change 100644 => 100755 KernelLand/Modules/USB/MSC/scsi.c mode change 100644 => 100755 KernelLand/Modules/USB/MSC/scsi.h diff --git a/KernelLand/Modules/USB/MSC/common.h b/KernelLand/Modules/USB/MSC/common.h old mode 100644 new mode 100755 index f459080c..48fa398d --- a/KernelLand/Modules/USB/MSC/common.h +++ b/KernelLand/Modules/USB/MSC/common.h @@ -19,8 +19,8 @@ struct sMSCInfo size_t BlockSize; }; -extern void MSC_SendData(tUSBInterface *Dev, size_t CmdLen, const void *CmdData, size_t DataLen, const void *Data); -extern void MSC_RecvData(tUSBInterface *Dev, size_t CmdLen, const void *CmdData, size_t DataLen, void *Data); +extern int MSC_SendData(tUSBInterface *Dev, size_t CmdLen, const void *CmdData, size_t DataLen, const void *Data); +extern int MSC_RecvData(tUSBInterface *Dev, size_t CmdLen, const void *CmdData, size_t DataLen, void *Data); extern tLVM_VolType gMSC_SCSI_VolType; extern Uint64 MSC_SCSI_GetSize(tUSBInterface *Dev, size_t *BlockSize); diff --git a/KernelLand/Modules/USB/MSC/main.c b/KernelLand/Modules/USB/MSC/main.c old mode 100644 new mode 100755 index 94d258a6..1cc4d020 --- a/KernelLand/Modules/USB/MSC/main.c +++ b/KernelLand/Modules/USB/MSC/main.c @@ -19,8 +19,8 @@ void MSC_DeviceConnected(tUSBInterface *Dev, void *Descriptors, size_t Descripto 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); @@ -63,6 +63,10 @@ void MSC_DeviceConnected(tUSBInterface *Dev, void *Descriptors, size_t Descripto // 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 @@ -72,6 +76,14 @@ void MSC_DeviceConnected(tUSBInterface *Dev, void *Descriptors, size_t Descripto 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; @@ -124,7 +136,7 @@ int MSC_int_CreateCBW(tMSC_CBW *Cbw, int bInput, size_t CmdLen, const void *CmdD 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; @@ -132,7 +144,7 @@ void MSC_SendData(tUSBInterface *Dev, size_t CmdLen, const void *CmdData, size_t 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); @@ -143,9 +155,11 @@ void MSC_SendData(tUSBInterface *Dev, size_t CmdLen, const void *CmdData, size_t // 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; @@ -153,7 +167,7 @@ void MSC_RecvData(tUSBInterface *Dev, size_t CmdLen, const void *CmdData, size_t 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); @@ -164,6 +178,29 @@ void MSC_RecvData(tUSBInterface *Dev, size_t CmdLen, const void *CmdData, size_t // 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); } diff --git a/KernelLand/Modules/USB/MSC/msc_proto.h b/KernelLand/Modules/USB/MSC/msc_proto.h index 8cbd8524..b35a5873 100644 --- a/KernelLand/Modules/USB/MSC/msc_proto.h +++ b/KernelLand/Modules/USB/MSC/msc_proto.h @@ -22,12 +22,14 @@ struct sMSC_CBW Uint8 CBWCB[16]; } PACKED; +#define MSC_CSW_SIGNATURE 0x53425355 + struct sMSC_CSW { Uint32 dCSWSignature; // = 0x53425355 Uint32 dCSWTag; Uint32 dCSWDataResidue; - Uint8 dCSWStatus; + Uint8 bCSWStatus; } PACKED; #endif diff --git a/KernelLand/Modules/USB/MSC/scsi.c b/KernelLand/Modules/USB/MSC/scsi.c old mode 100644 new mode 100755 index c4220d80..0fd8c12f --- a/KernelLand/Modules/USB/MSC/scsi.c +++ b/KernelLand/Modules/USB/MSC/scsi.c @@ -11,6 +11,7 @@ // === 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); @@ -24,48 +25,152 @@ tLVM_VolType gMSC_SCSI_VolType = { // === 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; } diff --git a/KernelLand/Modules/USB/MSC/scsi.h b/KernelLand/Modules/USB/MSC/scsi.h old mode 100644 new mode 100755 index 47959890..7bd50a54 --- a/KernelLand/Modules/USB/MSC/scsi.h +++ b/KernelLand/Modules/USB/MSC/scsi.h @@ -9,6 +9,44 @@ #define _MSC__SCSI_H_ // NOTE: All commands are big-endian +enum eSCSI_Ops +{ + READ_CAPACITY_10 = 0x25, + SERVICE_ACTION_IN_16 = 0x9E +}; + +struct sSCSI_Cmd_RequestSense +{ + Uint8 Op; // 0x03 + Uint8 Desc; + Uint16 _resvd; + Uint8 AllocationLength; + Uint8 Control; +} PACKED; + +struct sSCSI_SenseData +{ + Uint8 ResponseCode; + Uint8 _obselete; + Uint8 Flags; + Uint32 Information; + Uint8 AdditionalSenseLength; + Uint32 CommandSpecInfomation; + Uint8 AdditionalSenseCode; + Uint8 AdditionalSenseCodeQual; + Uint8 FRUC; + //Uint +} PACKED; + +struct sSCSI_Cmd_ReadCapacity10 +{ + Uint8 Op; // 0x25 + Uint8 _resvd1; + Uint32 LBA; + Uint16 _resvd2; + Uint8 Flags; + Uint8 Control; +} PACKED; struct sSCSI_Cmd_ReadCapacity16 { @@ -20,14 +58,39 @@ struct sSCSI_Cmd_ReadCapacity16 Uint8 Control; } PACKED; +struct sSCSI_Ret_ReadCapacity10 +{ + Uint32 LastBlock; + Uint32 BlockSize; +} PACKED; + struct sSCSI_Ret_ReadCapacity16 { - Uint64 BlockCount; + Uint64 LastBlock; Uint32 BlockSize; Uint8 Flags; Uint8 _resvd[32-13]; } PACKED; +struct sSCSI_Cmd_Read6 +{ + Uint8 Op; // 0x08 + Uint8 LBATop; + Uint16 LBALower; + Uint8 Length; + Uint8 Control; +} PACKED; + +struct sSCSI_Cmd_Read10 +{ + Uint8 Op; // 0x28 + Uint8 Flags; + Uint32 LBA; + Uint8 GroupNumber; + Uint16 Length; + Uint8 Control; +} PACKED; + struct sSCSI_Cmd_Read16 { Uint8 Op; // 0x88 -- 2.20.1