2 * Acess2 USB Stack Mass Storage Driver
3 * - By John Hodge (thePowersGang)
9 #define VERSION VER2(0,1)
13 #include "msc_proto.h"
16 int MSC_Initialise(char **Arguments);
17 int MSC_Cleanup(void);
18 void MSC_DeviceConnected(tUSBInterface *Dev, void *Descriptors, size_t DescriptorsLen);
19 void MSC_DataIn(tUSBInterface *Dev, int EndPt, int Length, void *Data);
20 // --- Internal Helpers
21 int MSC_int_CreateCBW(tMSC_CBW *Cbw, int bInput, size_t CmdLen, const void *CmdData, size_t DataLen);
22 int MSC_SendData(tUSBInterface *Dev, size_t CmdLen, const void *CmdData, size_t DataLen, const void *Data);
23 int MSC_RecvData(tUSBInterface *Dev, size_t CmdLen, const void *CmdData, size_t DataLen, void *Data);
26 MODULE_DEFINE(0, VERSION, USB_MSC, MSC_Initialise, MSC_Cleanup, "USB_Core", NULL);
27 tUSBDriver gMSC_Driver_BulkOnly = {
28 .Name = "MSC_BulkOnly",
29 .Match = {.Class = {0x080050, 0xFF00FF}},
30 .Connected = MSC_DeviceConnected,
40 int MSC_Initialise(char **Arguments)
42 USB_RegisterDriver( &gMSC_Driver_BulkOnly );
51 void MSC_DeviceConnected(tUSBInterface *Dev, void *Descriptors, size_t DescriptorsLen)
56 info = malloc( sizeof(*info) );
57 USB_SetDeviceDataPtr(Dev, info);
59 // Get the class code (and hence what command set is in use)
60 Uint8 sc = (USB_GetInterfaceClass(Dev) & 0x00FF00) >> 8;
63 // SCSI Transparent Command Set
65 info->BlockCount = MSC_SCSI_GetSize(Dev, &info->BlockSize);
66 // HACK! Try twice if needed
67 // Qemu Tegra2 appears to error with Sense=RESET on the first attempt
68 if( !info->BlockCount )
69 info->BlockCount = MSC_SCSI_GetSize(Dev, &info->BlockSize);
70 vt = &gMSC_SCSI_VolType;
72 // Unknown, prepare to chuck sad
74 Log_Error("USB MSC", "Unknown sub-class 0x%02x", sc);
75 USB_SetDeviceDataPtr(Dev, NULL);
80 // Zero size indicates some form of critical error
81 if( !info->BlockCount ) {
82 Log_Error("USB MSC", "Device did not report a valid size");
83 USB_SetDeviceDataPtr(Dev, NULL);
88 // Create device name from Vendor ID, Device ID and Serial Number
91 USB_GetDeviceVendor(Dev, &vendor, &devid);
92 serial_number = USB_GetSerialNumber(Dev);
93 if( !serial_number ) {
94 // No serial number - this breaks spec, but it's easy to handle
95 Log_Warning("USB MSC", "Device does not have a serial number, using a random one");
96 serial_number = malloc(4+8+1);
97 sprintf(serial_number, "rand%08x", rand());
99 char name[4 + 4 + 1 + 4 + 1 + strlen(serial_number) + 1];
100 sprintf(name, "usb-%04x:%04x-%s", vendor, devid, serial_number);
103 // Pass the buck to LVM
104 LOG("Device '%s' has 0x%llx blocks of 0x%x bytes", name, info->BlockCount, info->BlockSize);
105 LVM_AddVolume(vt, name, Dev, info->BlockSize, info->BlockCount);
108 void MSC_DataIn(tUSBInterface *Dev, int EndPt, int Length, void *Data)
110 // Will this ever be needed?
114 * \brief Create a CBW structure
116 int MSC_int_CreateCBW(tMSC_CBW *Cbw, int bInput, size_t CmdLen, const void *CmdData, size_t DataLen)
119 Log_Error("MSC", "Zero length commands are invalid");
124 Log_Error("MSC", "Command data >16 bytes (%i)", CmdLen);
128 Cbw->dCBWSignature = LittleEndian32(0x43425355);
129 Cbw->dCBWTag = giMSC_NextTag ++;
130 Cbw->dCBWDataTransferLength = LittleEndian32(DataLen);
131 Cbw->bmCBWFlags = (bInput ? 0x80 : 0x00);
132 Cbw->bCBWLUN = 0; // TODO: Logical Unit Number (param from proto)
133 Cbw->bCBWLength = CmdLen;
134 memcpy(Cbw->CBWCB, CmdData, CmdLen);
139 int MSC_SendData(tUSBInterface *Dev, size_t CmdLen, const void *CmdData, size_t DataLen, const void *Data)
143 const int endpoint_out = 2;
144 const int endpoint_in = 1;
146 if( MSC_int_CreateCBW(&cbw, 0, CmdLen, CmdData, DataLen) )
150 USB_SendData(Dev, endpoint_out, sizeof(cbw), &cbw);
153 USB_SendData(Dev, endpoint_out, DataLen, Data);
156 USB_RecvData(Dev, endpoint_in, sizeof(csw), &csw);
157 // TODO: Validate CSW
159 return (csw.bCSWStatus != 0x00);
162 int MSC_RecvData(tUSBInterface *Dev, size_t CmdLen, const void *CmdData, size_t DataLen, void *Data)
166 const int endpoint_out = 2;
167 const int endpoint_in = 1;
169 if( MSC_int_CreateCBW(&cbw, 1, CmdLen, CmdData, DataLen) )
173 USB_SendData(Dev, endpoint_out, sizeof(cbw), &cbw);
176 // TODO: use async version and wait for the transaction to complete
177 USB_RecvData(Dev, endpoint_in, DataLen, Data);
180 USB_RecvData(Dev, endpoint_in, sizeof(csw), &csw);
182 //Debug_HexDump("MSC RecvData, cbw=", &cbw, sizeof(cbw));
183 //Debug_HexDump("MSC RecvData, csw=", &csw, sizeof(csw));
184 //Debug_HexDump("MSC RecvData, Data=", Data, DataLen);
186 // TODO: Validate CSW
187 if( LittleEndian32(csw.dCSWSignature) != MSC_CSW_SIGNATURE ) {
188 Log_Warning("MSC", "CSW Signature invalid %x!=exp %x",
189 LittleEndian32(csw.dCSWSignature), MSC_CSW_SIGNATURE);
191 if( csw.dCSWTag != cbw.dCBWTag ) {
192 Log_Warning("MSC", "Recv - CSW tag (%x) doesn't match CBW tag (%x)",
193 LittleEndian32(csw.dCSWTag), LittleEndian32(cbw.dCBWTag));
195 if( LittleEndian32(csw.dCSWDataResidue) > DataLen ) {
196 Log_Warning("MSC", "Recv - CSW residue (0x%x) is larger than DatLen (0x%x)",
197 LittleEndian32(csw.dCSWDataResidue), DataLen);
199 if( csw.bCSWStatus != 0x00 ) {
200 Log_Warning("MWC", "Recv - CSW indicated error (Status=%02x, Residue=0x%x/0x%x)",
201 csw.bCSWStatus, LittleEndian32(csw.dCSWDataResidue), DataLen);
204 return (csw.bCSWStatus != 0x00);