// -------------------------------------------------------------------- // Copyright (c) 2007 by Terasic Technologies Inc. // -------------------------------------------------------------------- // // Permission: // // Terasic grants permission to use and modify this code for use // in synthesis for all Terasic Development Boards and Altera Development // Kits made by Terasic. Other use of this code, including the selling // ,duplication, or modification of any portion is strictly prohibited. // // Disclaimer: // // This VHDL/Verilog or C/C++ source code is intended as a design reference // which illustrates how these types of functions can be implemented. // It is the user's responsibility to verify their design for // consistency and functionality through the use of formal // verification methods. Terasic provides no warranty regarding the use // or functionality of this code. // // -------------------------------------------------------------------- // // Terasic Technologies Inc // 356 Fu-Shin E. Rd Sec. 1. JhuBei City, // HsinChu County, Taiwan // 302 // // web: http://www.terasic.com/ // email: support@terasic.com // // -------------------------------------------------------------------- // Author: Richard Chang // USB Mass Storage: Bulk-Only Transport Protocol // Control sequqnce // 1. Inquery (0x12) // 2. loop: Read Format Capacities(0x23), if fail, call Request Sense (0x03) // 3. Read Capacities (0x25) // 4. Read10 (0x28) // 5. (0x1A) ??? // 5. (0x1A) ??? // 1. Inquery (0x12) // 3. Read Capacities (0x25) x 2 #include "terasic_includes.h" #include "usb_disk.h" #include "usb_disk_transport.h" #ifdef DEBUG_USB_DISK #define DEBUG_DATA(x) {printf x;} #define DEBUG_OUT(x) {printf("[USB_DISK]"); printf x; msleep(10);} #define DEBUG_ERR(x) {printf("[USB_DISK_ERR]"); printf x;} #else #define DEBUG_DATA(x) #define DEBUG_OUT(x) #define DEBUG_ERR(x) #endif static bool botSetup(alt_u8 *pConfigStart, alt_u16 ConfigLen, USBDISK_DRIVER *pUsbDisk); static bool botCapQuery(USBDISK_DRIVER *pUsbDisk); // internal function (BOM: Bulk-only Mass Storage) bool BOT_Reset(alt_u8 DeviceAddr, alt_u8 Interface){ bool bSuccess; return bSuccess; } bool BOT_GetMaxLUN(USB_DEVICE *pUsbDevice, alt_u16 Interface, alt_u8 *pLUN){ //PTD_RESULT PtdResult; bool bSuccess; //alt_u16 InLen; //alt_u8 DataToggle = 0; alt_u8 szSetup[] = { 0xA1, 0xFE, 0x00, 0x00, //wValue: 0 0x00, 0x00, // wIndex: Interface 0x01, 0x00 // wLength: 1 }; szSetup[4] = Interface & 0xFF; szSetup[5] = (Interface >> 8) & 0xFF; bSuccess = USB_ControlRequest(pUsbDevice, szSetup, pLUN, 1); if (bSuccess){ DEBUG_OUT(("BOM_GetMaxLUN success, Addr=%d, Interface=%d\n", pUsbDevice->Addr, Interface)); }else{ DEBUG_ERR(("BOM_GetMaxLUN fail, Addr=%d, OldInterface=%d\n", pUsbDevice->Addr, Interface)); } return bSuccess; } static bool botSetup(alt_u8 *pConfigStart, alt_u16 ConfigLen, USBDISK_DRIVER *pUsbDisk){ bool bSuccess = FALSE, bBulkInFound=FALSE, bBulkOutFound=FALSE; int nPos; USB_CONFIG_DESCRIPTOR *pConfig; USB_INTERFACE_DESCRIPTOR *pInterface; USB_ENDPOINT_DESCRIPTOR *pEndpoint; pConfig = (USB_CONFIG_DESCRIPTOR *)pConfigStart; if (pConfig->bLength != 0x09){ DEBUG_OUT(("Invalid Config Length\n")); return FALSE; } if (pConfig->bDescriptorType != USBDT_CONFIGURATION){ DEBUG_OUT(("Invalid Config Type\n")); return FALSE; } pInterface = (USB_INTERFACE_DESCRIPTOR *)(pConfigStart + 9); if (pInterface->bLength != 0x09){ DEBUG_OUT(("Invalid Interface Length\n")); return FALSE; } if (pInterface->bDescriptorType != 0x04){ DEBUG_OUT(("Invalid Interface Descriptor Type\n")); return FALSE; } if (pInterface->bInterfaceClass != USBCS_MASS_STORAGE || pInterface->bInterfaceSubClass != 0x06 || pInterface->bInterfaceProtocol != 0x50 || pInterface->bNumEndpoints < 2){ DEBUG_OUT(("Not bulk-only Storage\n")); return FALSE; } DEBUG_OUT(("Bulk-only Mass-Storage is found.\n")); // nPos = 9 + 9; while((nPos+1) < ConfigLen && !bSuccess){ if (pConfigStart[nPos+1] == 0x05){ // end-point type pEndpoint = (USB_ENDPOINT_DESCRIPTOR *)(pConfigStart + nPos); if (pEndpoint->bLength == 0x07 && pEndpoint->bmAttributes == 0x02){ // bulk endpoint if (pEndpoint->bEndpointAddress & 0x80){ // bulk in pUsbDisk->BulkInEp = pEndpoint->bEndpointAddress & 0x7F; pUsbDisk->BulkInDataToggle = 0; pUsbDisk->UsbDevice.EpIn[pUsbDisk->BulkInEp].Type = EPType_BULK; pUsbDisk->UsbDevice.EpIn[pUsbDisk->BulkInEp].MaxPacketSize = pEndpoint->wMaxPacketSize; bBulkInFound = TRUE; }else{ // build out pUsbDisk->BulkOutEp = pEndpoint->bEndpointAddress & 0x7F; pUsbDisk->BulkOutDataToggle = 0; pUsbDisk->UsbDevice.EpOut[pUsbDisk->BulkOutEp].Type = EPType_BULK; pUsbDisk->UsbDevice.EpOut[pUsbDisk->BulkOutEp].MaxPacketSize = pEndpoint->wMaxPacketSize; bBulkOutFound = TRUE; } } } if (bBulkInFound && bBulkOutFound) bSuccess = TRUE; else nPos += pConfigStart[nPos]; } return bSuccess; } USBDISK_HANDLE USBDISK_Open(HUB_HANDLE hHub, alt_u8 Port, USB_SPEED DeviceSpeed, alt_u8 AssignedAddress){ //HUB_DEVICE *pHub = (HUB_DEVICE *)hHub; bool bSuccess; USBDISK_HANDLE DiskHandle = 0; USB_DEVICE_DESCRIPTOR DeviceDesc; USBDISK_DRIVER UsbDisk; memset(&UsbDisk, 0, sizeof(UsbDisk)); UsbDisk.UsbDevice.HubAddr = hHub->UsbDevice.Addr; UsbDisk.UsbDevice.Port = Port; UsbDisk.UsbDevice.Speed = DeviceSpeed; UsbDisk.UsbDevice.EpIn[0].Type = EPType_CONTROL; UsbDisk.UsbDevice.EpIn[0].MaxPacketSize = 8; UsbDisk.UsbDevice.EpOut[0].Type = EPType_CONTROL; UsbDisk.UsbDevice.EpOut[0].MaxPacketSize = 8; if (!Hub_IsDeviceAttached(hHub, Port)) return 0; // determine max packet size bSuccess = USB_GetDescriptor(&UsbDisk.UsbDevice, USBDT_DEVICE, (alt_u8 *)&DeviceDesc, 8); if (bSuccess){ UsbDisk.UsbDevice.EpIn[0].MaxPacketSize = DeviceDesc.bMaxPacketSize0; UsbDisk.UsbDevice.EpOut[0].MaxPacketSize = DeviceDesc.bMaxPacketSize0; bSuccess = USB_GetDescriptor(&UsbDisk.UsbDevice, USBDT_DEVICE, (alt_u8 *)&DeviceDesc, sizeof(DeviceDesc)); if (!bSuccess){ DEBUG_OUT(("Get Device Descriptor fail\n")); } }else{ DEBUG_OUT(("Get Device Descriptor fail\n")); } // assign address if (bSuccess){ bSuccess = USB_SetDeviceAddres(&UsbDisk.UsbDevice,AssignedAddress); if (bSuccess){ UsbDisk.UsbDevice.Addr = AssignedAddress; }else{ DEBUG_OUT(("Set Device Address fail\n")); } } // get configuration descriptor if (bSuccess){ alt_u8 szBuf[8]; bSuccess = USB_GetDescriptor(&UsbDisk.UsbDevice, USBDT_CONFIGURATION, szBuf, 8); if (bSuccess){ USB_CONFIG_DESCRIPTOR *pConfig; pConfig = (USB_CONFIG_DESCRIPTOR *)szBuf; if (pConfig->bLength != 0x09 || pConfig->bDescriptorType != USBDT_CONFIGURATION){ DEBUG_OUT(("Invalid Config Descriptor\n")); }else{ alt_u8 *pBuf; alt_u16 ConfigLen; ConfigLen = pConfig->wTotalLength; pBuf = malloc(ConfigLen); bSuccess = USB_GetDescriptor(&UsbDisk.UsbDevice, USBDT_CONFIGURATION, pBuf, ConfigLen); if (!bSuccess){ DEBUG_OUT(("Get Config Descriptor fail (len=%d)\n", ConfigLen)); }else{ // check whether it is Bulk-Only Protocol //pConfig = (USB_CONFIG_DESCRIPTOR *)pBuf; //USB_INTERFACE_DESCRIPTOR *pInterface; bSuccess = botSetup(pBuf, ConfigLen, &UsbDisk); if (!bSuccess){ DEBUG_OUT(("Failed to find interface descriptor\n")); }else{ // set config const alt_u8 ConfigValue = pConfig->bConfigurationValue; bSuccess = USB_SetConfig(&UsbDisk.UsbDevice, ConfigValue); msleep(100); } } free(pBuf); } }else{ DEBUG_OUT(("Get Config Descriptor fail (len=%d)\n", 8)); } } if (bSuccess){ //UsbDisk.UsbDevice = Device; bSuccess = botCapQuery(&UsbDisk); } /* if (bSuccess){ // request in ??? //SCSI_INQUERY_RESULT InqueryResult; //BOT_DEVICE BotDevice; alt_u8 LUN; const alt_u16 Interface = 0; if (BOT_GetMaxLUN(&Device, Interface, &LUN)){ DEBUG_OUT(("Get LUN=%d\n", LUN)); }else{ DEBUG_OUT(("Fail to get LUN\n")); } } // // Inquery if (bSuccess){ SCSI_INQUERY_RESULT InqueryResult; bSuccess = BOT_Inquery(&BotDevice, &InqueryResult); if (bSuccess){ BOT_ExplainInqueryResult(&InqueryResult, sizeof(InqueryResult)); }else{ DEBUG_OUT(("Inqeury fail\n")); } } // Read Format Capacities if (bSuccess){ SCSI_CAP_LIST_HEADER CapListHeader; msleep(1000); // determine length first bSuccess = BOT_ReadFormatCap(&BotDevice, (SCSI_CAP_LIST *)&CapListHeader, sizeof(CapListHeader)); if (bSuccess){ // find length alt_u8 *pCap; alt_u16 CapLen; CapLen = CapListHeader.CapListLength * sizeof(SCSI_FORMATTABLE_CAP_DESCRIPTOR) + sizeof(CapListHeader); pCap = (alt_u8 *)malloc(CapLen); bSuccess = BOT_ReadFormatCap(&BotDevice, (SCSI_CAP_LIST *)pCap, CapLen); if (bSuccess){ BOT_ExplainFormatCapResult((SCSI_CAP_LIST *)pCap, CapLen); }else{ DEBUG_OUT(("Read Format Capacities(all) fail\n")); } free(pCap); }else{ DEBUG_OUT(("Read Format Capacities fail\n")); bSuccess = TRUE; } } // request sense if (bSuccess){ SCSI_REQUEST_SENSE_DATA RequestSenseData; // determine length first bSuccess = BOT_RequestSense(&BotDevice, &RequestSenseData); if (bSuccess){ BOT_ExplainRequestSense(&RequestSenseData); }else{ DEBUG_OUT(("Read Request Sense fail\n")); } } // Read Format Capacities if (bSuccess){ SCSI_CAP_LIST_HEADER CapListHeader; msleep(1000); // determine length first bSuccess = BOT_ReadFormatCap(&BotDevice, (SCSI_CAP_LIST *)&CapListHeader, sizeof(CapListHeader)); if (bSuccess){ // find length alt_u8 *pCap; alt_u16 CapLen; CapLen = CapListHeader.CapListLength + sizeof(CapListHeader); pCap = (alt_u8 *)malloc(CapLen); bSuccess = BOT_ReadFormatCap(&BotDevice, (SCSI_CAP_LIST *)pCap, CapLen); if (bSuccess){ BOT_ExplainFormatCapResult((SCSI_CAP_LIST *)pCap, CapLen); }else{ DEBUG_OUT(("Read Format Capacities(all) fail\n")); } free(pCap); }else{ DEBUG_OUT(("Read Format Capacities fail\n")); bSuccess = TRUE; } } // read Capacities if (bSuccess){ SCSI_READ_CAP_RESULT CapResult; // determine length first bSuccess = BOT_ReadCap(&BotDevice, &CapResult); if (bSuccess){ BOT_ExplainCapResult(&CapResult); }else{ DEBUG_OUT(("Read Capacities fail\n")); } bSuccess = TRUE; } // request sense if (bSuccess){ SCSI_REQUEST_SENSE_DATA RequestSenseData; // determine length first bSuccess = BOT_RequestSense(&BotDevice, &RequestSenseData); if (bSuccess){ BOT_ExplainRequestSense(&RequestSenseData); }else{ DEBUG_OUT(("Read Request Sense fail\n")); } } // read Capacities if (bSuccess){ SCSI_READ_CAP_RESULT CapResult; // determine length first bSuccess = BOT_ReadCap(&BotDevice, &CapResult); if (bSuccess){ BOT_ExplainCapResult(&CapResult); }else{ DEBUG_OUT(("Read Capacities fail\n")); } bSuccess = TRUE; } */ if (bSuccess){ DiskHandle = malloc(sizeof(USBDISK_DRIVER)); memcpy(DiskHandle, &UsbDisk, sizeof(UsbDisk)); } return DiskHandle; } void USBDISK_Close(USBDISK_HANDLE hUsbDisk){ USBDISK_DRIVER *pDisk = (USBDISK_DRIVER *)hUsbDisk; if (pDisk) free(pDisk); } alt_u16 USBDISK_GetBlockSize(USBDISK_HANDLE hUsbDisk, alt_u8 LogicalUnitNumber){ USBDISK_DRIVER *p = (USBDISK_DRIVER *)hUsbDisk; if (p) return p->szLunCap[LogicalUnitNumber].BlockSize; return 0; } alt_u32 USBDISK_GetNumOfBlock(USBDISK_HANDLE UsbDisk, alt_u8 LogicalUnitNumber){ USBDISK_DRIVER *p = (USBDISK_DRIVER *)UsbDisk; if (p) return p->szLunCap[LogicalUnitNumber].NumOfBlock; return 0; } // see usbmass-ufi10.pdf, page 13 // LBA: Logica Block Address // LBA = (((Track x uHeadTrk)+ Head) x SecTrk ) + (Sector-1) // Logica Block = Logical sector // Logical Block 0: first sector on the diskette, track=0, head=, sector=1. bool USBDISK_ReadBlock512(USBDISK_HANDLE hUsbDisk, alt_u32 BlockIndex, alt_u8 *pbuf){ bool bSuccess; alt_u16 BufLen = 512; alt_u8 LogicalUnitNumber = 0; alt_u32 LogicalBlockAddr; alt_u16 TransferLength; // alt_u8 LUN; USBDISK_DRIVER *pUsbDisk = (USBDISK_DRIVER *)hUsbDisk; if (!pUsbDisk->szLunCap[LogicalUnitNumber].BlockSize || !pUsbDisk->szLunCap[LogicalUnitNumber].NumOfBlock) return FALSE; //LogicalUnitNumber = 0; LogicalBlockAddr = BlockIndex; TransferLength = BufLen / pUsbDisk->szLunCap[LogicalUnitNumber].BlockSize; bSuccess = BOT_Read10(pUsbDisk, LogicalUnitNumber, LogicalBlockAddr, TransferLength, pbuf, BufLen); return bSuccess; } static bool botCapQuery(USBDISK_DRIVER *pUsbDisk){ bool bSuccess = TRUE; bool bOK; int i, TryNum=0; enum{ DO_INQUERY=0, DO_GET_FORMAT_CAP, DO_GET_CAP, DO_READ10, DO_NUM }; // get LUN alt_u8 LUN; const alt_u16 Interface = 0; if (BOT_GetMaxLUN(&pUsbDisk->UsbDevice, Interface, &LUN)){ DEBUG_OUT(("Get LUN=%d\n", LUN)); pUsbDisk->LUN = LUN; }else{ DEBUG_OUT(("Fail to get LUN\n")); pUsbDisk->LUN = 0; // not support } i = DO_INQUERY; while(iCurMaxCapHeader.NumberOfBlocks, 4); pUsbDisk->szLunCap[0].NumOfBlock = Value; Value = Array2Value(pCapList->CurMaxCapHeader.BlockLength, 3); pUsbDisk->szLunCap[0].BlockSize = Value; }else{ for(k=0;kFormattableCapDescriptr[k].NumberOfBlocks, 4); pUsbDisk->szLunCap[k].NumOfBlock = Value; Value = Array2Value(pCapList->FormattableCapDescriptr[k].BlockLength, 3); pUsbDisk->szLunCap[k].BlockSize = Value; } } // BOT_ExplainFormatCapResult(pCapList, CapLen); }else{ DEBUG_OUT(("Read Format Capacities(all) fail\n")); } free((void *)pCapList); }else{ DEBUG_OUT(("Read Format Capacities fail\n")); } }else if (i == DO_GET_CAP){ SCSI_READ_CAP_RESULT CapResult; bOK = BOT_ReadCap(pUsbDisk, &CapResult); if (bOK){ BOT_ExplainCapResult(&CapResult); }else{ DEBUG_OUT(("Read Capacities fail\n")); } }else if (i == DO_READ10){ alt_u8 LogicalUnitNumber = 0; alt_u32 LogicalBlockAddr = 0; alt_u16 TransferLength = 1; alt_u8 szBuf[512]; bOK = BOT_Read10(pUsbDisk, LogicalUnitNumber, LogicalBlockAddr, TransferLength, szBuf, sizeof(szBuf)); if (bOK){ DEBUG_OUT(("Read10 success\n")); BOT_Raw(szBuf, sizeof(szBuf)); }else{ DEBUG_OUT(("Read10 fail\n")); } } // if (!bOK){ if (TryNum++ > 3){ bSuccess = FALSE; }else{ // request sense SCSI_REQUEST_SENSE_DATA RequestSenseData; // determine length first if (BOT_RequestSense(pUsbDisk, &RequestSenseData)){ BOT_ExplainRequestSense(&RequestSenseData); }else{ DEBUG_OUT(("Read Request Sense fail\n")); } } }else{ i++; TryNum = 0; } } DEBUG_OUT(("disk_CapQuery %s\n", bSuccess?"success":"fail" )); return bSuccess; }