// === PROTOTYPES ===
int MSC_Initialise(char **Arguments);
-void MSC_Cleanup(void);
+ int MSC_Cleanup(void);
void MSC_DeviceConnected(tUSBInterface *Dev, void *Descriptors, size_t DescriptorsLen);
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);
return 0;
}
-void MSC_Cleanup(void)
+int MSC_Cleanup(void)
{
+ return 0;
}
void MSC_DeviceConnected(tUSBInterface *Dev, void *Descriptors, size_t DescriptorsLen)
{
tMSCInfo *info;
- // TODO: Get the class code (and hence what command set is in use)
- // TODO: Get the maximum packet size too
-
+ tLVM_VolType *vt;
+
info = malloc( sizeof(*info) );
- USB_SetDeviceDataPtr(Dev, info);
-
- info->BlockCount = MSC_SCSI_GetSize(Dev, &info->BlockSize);
+ USB_SetDeviceDataPtr(Dev, info);
+
+ // Get the class code (and hence what command set is in use)
+ Uint8 sc = (USB_GetInterfaceClass(Dev) & 0x00FF00) >> 8;
+ switch( sc )
+ {
+ // 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
+ default:
+ Log_Error("USB MSC", "Unknown sub-class 0x%02x", sc);
+ USB_SetDeviceDataPtr(Dev, NULL);
+ free(info);
+ return ;
+ }
- LVM_AddVolume(&gMSC_SCSI_VolType, "0", Dev, info->BlockSize, info->BlockCount);
+ // 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;
+ char *serial_number;
+ USB_GetDeviceVendor(Dev, &vendor, &devid);
+ serial_number = USB_GetSerialNumber(Dev);
+ if( !serial_number ) {
+ // No serial number - this breaks spec, but it's easy to handle
+ Log_Warning("USB MSC", "Device does not have a serial number, using a random one");
+ serial_number = malloc(4+8+1);
+ sprintf(serial_number, "rand%08x", rand());
+ }
+ char name[4 + 4 + 1 + 4 + 1 + strlen(serial_number) + 1];
+ sprintf(name, "usb-%04x:%04x-%s", vendor, devid, serial_number);
+ free(serial_number);
+
+ // Pass the buck to LVM
+ LOG("Device '%s' has 0x%llx blocks of 0x%x bytes", name, info->BlockCount, info->BlockSize);
+ LVM_AddVolume(vt, name, Dev, info->BlockSize, info->BlockCount);
}
void MSC_DataIn(tUSBInterface *Dev, int EndPt, int Length, void *Data)
// Will this ever be needed?
}
+/**
+ * \brief Create a CBW structure
+ */
int MSC_int_CreateCBW(tMSC_CBW *Cbw, int bInput, size_t CmdLen, const void *CmdData, size_t DataLen)
{
if( CmdLen == 0 ) {
}
Cbw->dCBWSignature = LittleEndian32(0x43425355);
- Cbw->dCBWTag = giMSC_NextTag ++;
+ Cbw->dCBWTag = giMSC_NextTag ++;
Cbw->dCBWDataTransferLength = LittleEndian32(DataLen);
Cbw->bmCBWFlags = (bInput ? 0x80 : 0x00);
- Cbw->bCBWLUN = 0; // TODO: Logical Unit Number (param from proto)
+ Cbw->bCBWLUN = 0; // TODO: Logical Unit Number (param from proto)
Cbw->bCBWLength = CmdLen;
memcpy(Cbw->CBWCB, CmdData, CmdLen);
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_out = 2;
const int endpoint_in = 1;
- const int max_packet_size = 64;
if( MSC_int_CreateCBW(&cbw, 0, CmdLen, CmdData, DataLen) )
- return ;
+ return 2;
// Send CBW
USB_SendData(Dev, endpoint_out, sizeof(cbw), &cbw);
// Send Data
- for( size_t ofs = 0; ofs < DataLen; ofs += max_packet_size )
- {
- USB_SendData(Dev, endpoint_out, MIN(max_packet_size, DataLen - ofs), Data + ofs);
- }
+ USB_SendData(Dev, endpoint_out, DataLen, Data);
// 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_out = 2;
const int endpoint_in = 1;
- const int max_packet_size = 64;
-
+
if( MSC_int_CreateCBW(&cbw, 1, CmdLen, CmdData, DataLen) )
- return ;
+ return 2;
// Send CBW
USB_SendData(Dev, endpoint_out, sizeof(cbw), &cbw);
// Read Data
- for( size_t ofs = 0; ofs < DataLen; ofs += max_packet_size )
- {
- // TODO: use async version and wait for the transaction to complete
- USB_RecvData(Dev, endpoint_in, MIN(max_packet_size, DataLen - ofs), Data + ofs);
- }
+ // TODO: use async version and wait for the transaction to complete
+ USB_RecvData(Dev, endpoint_in, DataLen, Data);
// 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);
}