// -------------------------------------------------------------------- // 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 #include "terasic_includes.h" #include "usb_disk_transport.h" #include "usb_protocol.h" #ifdef DEBUG_USB_BOT #define DEBUG_DATA(x) {printf x;} #define DEBUG_OUT(x) {printf("[BOT]"); printf x;} #define DEBUG_ERR(x) {printf("[BOT_ERR]"); printf x;} #else #define DEBUG_DATA(x) #define DEBUG_OUT(x) #define DEBUG_ERR(x) #endif /* Refere to usbmass-ufi1.0.pdf (http://www.usb.org/developers/devclass_docs#approved) * Command: 0x12 (inquery), 0x23 (Read Format Capacity), 0x03 (Request Sense), 0x25 (Read Capability), 0x28 (READ_10), 0x1A (Mode Sense), */ bool BOT_Execute(USBDISK_DRIVER *pUsbDisk, alt_u8 *pCommand, alt_u32 CommandLen, bool bDataIn, alt_u8 *pData, alt_u32 DataLen){ PTD_RESULT PtdResult; bool bSuccess = FALSE; // bool bCheckCSW=FALSE; static BOT_CBW CBW = {BOT_CBW_SIGNATURE}; static BOT_CSW CSW; static alt_u32 dCSWTag = 0x891694D8; alt_u16 InLen, TotalInLen, ThisInLen; bool bSendCBWSuccess = FALSE; if (CommandLen > 16){ DEBUG_ERR(("Invalid Command Length\n")); return FALSE; } // 1A Wrap SCSI command as CBW(Command Block Wrapper), fixed 31 bytes CBW.dCBWDataTransferLength = DataLen; CBW.dCBWTag = dCSWTag; CBW.bmCBWFlags = bDataIn?0x80:0x00; CBW.bCBWLUN = 0; //??? CBW.bCBWCBLength = CommandLen; memset(&CBW.CBWCB, 0, 16); memcpy(&CBW.CBWCB, pCommand, CommandLen); //DEBUG_OUT(("sizeof(BOT_CBW)=%d\n", sizeof(BOT_CBW) )); // 1B. send CBW PtdResult = PORT_BulkOut(&pUsbDisk->UsbDevice, (alt_u8 *)&CBW, sizeof(CBW), TOKEN_OUT, pUsbDisk->BulkOutEp, pUsbDisk->BulkOutDataToggle); if (PtdResult == PTD_RESULT_SUCCESS) pUsbDisk->BulkOutDataToggle++; // 2. data in or out if (PtdResult == PTD_RESULT_SUCCESS){ bSendCBWSuccess = TRUE; if (bDataIn){ bool bDone = FALSE; alt_u16 BulkInMaxPacketSize = pUsbDisk->UsbDevice.EpIn[pUsbDisk->BulkInEp].MaxPacketSize; //int nTryCnt = 0; TotalInLen = 0; while(!bDone){ //bSuccess && TotalInLen < DataLen){ ThisInLen = DataLen - TotalInLen; if (ThisInLen > BulkInMaxPacketSize) ThisInLen = BulkInMaxPacketSize; PtdResult = PORT_BulkIn(&pUsbDisk->UsbDevice, pData+TotalInLen, ThisInLen, TOKEN_IN, pUsbDisk->BulkInEp, pUsbDisk->BulkInDataToggle, &InLen); if (PtdResult == PTD_RESULT_SUCCESS){ pUsbDisk->BulkInDataToggle++; TotalInLen += InLen; if (TotalInLen >= DataLen) bDone = TRUE; //nTryCnt = 0; }else if (PtdResult == PTD_RESULT_NACK){ // already re-try in PORT_BulkIn, so just terminate it here bDone = TRUE; }else if (PtdResult == PTD_RESULT_STALL){ // clear stall flag USB_ClearFeature(&pUsbDisk->UsbDevice, ENDPOINT_HALT, pUsbDisk->BulkInEp | 0x80); // re-try? bDone = TRUE; //if (nTryCnt++ > 100) // bDone = TRUE; }else{ // error bDone = TRUE; } } }else{ PtdResult = PORT_BulkOut(&pUsbDisk->UsbDevice, pData, DataLen, TOKEN_OUT, pUsbDisk->BulkInEp, pUsbDisk->BulkOutDataToggle); if (PtdResult == PTD_RESULT_SUCCESS) pUsbDisk->BulkOutDataToggle++; } } // 3A. receivce CSW(Command Status Wrapper), fixed 13 byte if (bSendCBWSuccess && ((PtdResult == PTD_RESULT_SUCCESS) || (PtdResult == PTD_RESULT_STALL))){ PTD_RESULT CswPtdResult; CswPtdResult = PORT_BulkIn(&pUsbDisk->UsbDevice, (alt_u8 *)&CSW, sizeof(CSW), TOKEN_IN, pUsbDisk->BulkInEp, pUsbDisk->BulkInDataToggle, &InLen); if ((CswPtdResult == PTD_RESULT_SUCCESS) && (InLen == sizeof(CSW))){ pUsbDisk->BulkInDataToggle++; // 3B. check CSW if ((CSW.dCSWSignature != BOT_CSW_SIGNATURE) || (CSW.dCSWTag != dCSWTag) || (CSW.dCSWDataResidue != 0) || (CSW.bCSWStatus != BOT_COMMAND_PASS)){ if ((CSW.dCSWSignature == BOT_CSW_SIGNATURE) || (CSW.dCSWTag == dCSWTag) || (CSW.dCSWDataResidue == 0)){ switch(CSW.bCSWStatus){ case BOT_COMMAND_FAILED: DEBUG_ERR(("CSW: COMAMND FAILED\n")); break; case BOT_PHASE_ERROR: DEBUG_ERR(("CSW: PHASE ERROR\n")); break; } } }else{ if (PtdResult == PTD_RESULT_SUCCESS) bSuccess = TRUE; } } } dCSWTag++; return bSuccess; } bool BOT_Inquery(USBDISK_DRIVER *pUsbDisk, SCSI_INQUERY_RESULT *pInqueryResult){ bool bSuccess; SCSI_INQUERY_CDB InqueryCommand; const bool bDataIn = TRUE; // init command memset(&InqueryCommand, 0, sizeof(InqueryCommand)); InqueryCommand.OP = SCSI_INQUERY; InqueryCommand.AllocationLendth = sizeof(SCSI_INQUERY_RESULT); bSuccess = BOT_Execute(pUsbDisk, (alt_u8 *)&InqueryCommand, sizeof(InqueryCommand), bDataIn, (alt_u8 *)pInqueryResult, sizeof(SCSI_INQUERY_RESULT) ); return bSuccess; } bool BOT_ReadFormatCap(USBDISK_DRIVER *pUsbDisk, SCSI_CAP_LIST *pCapList, alt_u16 ReadLen){ bool bSuccess; SCSI_READ_FORMAT_CAP_CDB ReadFormatCap; const bool bDataIn = TRUE; // init command memset(&ReadFormatCap, 0, sizeof(ReadFormatCap)); ReadFormatCap.OP = SCSI_READ_FORMAT_CAP; ReadFormatCap.LogicalUnitNumber = 0; // ?? #if 1 ReadFormatCap.AllocationLendth_MSB = (ReadLen >> 8 ) & 0xFF; ReadFormatCap.AllocationLendth_LSB = ReadLen & 0xFF; #else ReadFormatCap.AllocationLendth_MSB = 0;//(ReadLen >> 8 ) & 0xFF; ReadFormatCap.AllocationLendth_LSB = 0xFC;//ReadLen & 0xFF; #endif bSuccess = BOT_Execute(pUsbDisk, (alt_u8 *)&ReadFormatCap, sizeof(ReadFormatCap), bDataIn, (alt_u8 *)pCapList, ReadLen ); return bSuccess; } bool BOT_RequestSense(USBDISK_DRIVER *pUsbDisk, SCSI_REQUEST_SENSE_DATA *pRequestSenseData){ bool bSuccess; SCSI_REQUEST_SENSE_CDB RequestSense; const bool bDataIn = TRUE; // init command memset(&RequestSense, 0, sizeof(RequestSense)); RequestSense.OP = SCSI_REQUEST_SENSE; RequestSense.LogicalUnitNumber = 0; // ?? RequestSense.AllocationLength = 0x12; // fixed to 0x12 bSuccess = BOT_Execute(pUsbDisk, (alt_u8 *)&RequestSense, sizeof(RequestSense), bDataIn, (alt_u8 *)pRequestSenseData, sizeof(SCSI_REQUEST_SENSE_DATA) ); return bSuccess; } bool BOT_ReadCap(USBDISK_DRIVER *pUsbDisk, SCSI_READ_CAP_RESULT *pResult){ bool bSuccess; SCSI_READ_CAP_CDB ReadCap; const bool bDataIn = TRUE; // init command memset(&ReadCap, 0, sizeof(ReadCap)); ReadCap.OP = SCSI_READ_CAP; ReadCap.LogicUnitNumber = 0; // ?? bSuccess = BOT_Execute(pUsbDisk, (alt_u8 *)&ReadCap, sizeof(ReadCap), bDataIn, (alt_u8 *)pResult, sizeof(SCSI_READ_CAP_RESULT) ); return bSuccess; } // TransferLength: number of blocks for transfer bool BOT_Read10(USBDISK_DRIVER *pUsbDisk, alt_u8 LogicalUnitNumber, alt_u32 LogicalBlockAddr, alt_u16 TransferLength, alt_u8 *pBuf, alt_u16 BufLen){ bool bSuccess; SCSI_READ10_CDB Read10; const bool bDataIn = TRUE; // init command memset(&Read10, 0, sizeof(Read10)); Read10.OP = SCSI_READ10; Read10.LogicalUnitNumber = LogicalUnitNumber; Read10.LogicalBlockAddress[0] = (LogicalBlockAddr >> 24) & 0xFF; Read10.LogicalBlockAddress[1] = (LogicalBlockAddr >> 16) & 0xFF; Read10.LogicalBlockAddress[2] = (LogicalBlockAddr >> 8) & 0xFF; Read10.LogicalBlockAddress[3] = LogicalBlockAddr & 0xFF; Read10.TransferLength_MSB = (TransferLength >> 8) & 0xFF; Read10.TransferLength_LSB = TransferLength & 0xFF; bSuccess = BOT_Execute(pUsbDisk, (alt_u8 *)&Read10, sizeof(Read10), bDataIn, (alt_u8 *)pBuf, BufLen ); return bSuccess; } /* bool SCSI_Inquery(USB_DEVICE *pUsbDevice){ bool bSuccess; alt_u8 DataToggle = 0, InLen; const alt_u8 BulkOutEp = 1; const alt_u8 BulInEp = 2; alt_u8 szInquery[] = { 0x55, 0x53, 0x42, 0x43, 0xD8, 0x94, 0x16, 0x89, 0x24, 0x00, 0x00, 0x00, 0x80, 0x00, 0x06, 0x12, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; alt_u8 szRead[49]; // bulk out bSuccess = PORT_BulkOut(pUsbDevice, szInquery, sizeof(szInquery), TOKEN_OUT, BulkOutEp, DataToggle++); // bulk in if (bSuccess){ DataToggle = 0; bSuccess = PORT_BulkIn(pUsbDevice, szRead, 36, TOKEN_IN, BulInEp, DataToggle++, &InLen); } // bulk in if (bSuccess){ //DataToggle = 0; bSuccess = PORT_BulkIn(pUsbDevice, szRead+36, 13, TOKEN_IN, BulInEp, DataToggle++, &InLen); } return bSuccess; } */ void BOT_ExplainInqueryResult(SCSI_INQUERY_RESULT *pResult, alt_u16 Len){ int i; DEBUG_OUT(("Inquery Result:\n")); DEBUG_OUT((" PeripheralDeviceType = %d\n", (int)pResult->PeripheralDeviceType)); DEBUG_OUT((" RMB = %d\n", (int)pResult->RMB)); DEBUG_OUT((" ISOVersion = %d\n", (int)pResult->ISOVersion)); DEBUG_OUT((" ECMAVersion = %d\n", (int)pResult->ECMAVersion)); DEBUG_OUT((" ANSIApprovedVersion = %d\n", (int)pResult->ANSIApprovedVersion)); DEBUG_OUT((" ResponseDataFormat = %d\n", (int)pResult->ResponseDataFormat)); DEBUG_OUT((" AdditionalLength = %d\n", (int)pResult->AdditionalLength)); // 31 DEBUG_OUT((" Vendor Info = " )); for(i=0;iVerdorInformation);i++){ DEBUG_DATA(("%c", pResult->VerdorInformation[i])); } DEBUG_DATA(("\n")); DEBUG_OUT((" Product Info = " )); for(i=0;iProductInformation);i++){ DEBUG_DATA(("%c", pResult->ProductInformation[i])); } DEBUG_DATA(("\n")); DEBUG_OUT((" Product Revision Level = " )); for(i=0;iProductRevisionLevel);i++){ DEBUG_DATA(("%c", pResult->ProductRevisionLevel[i])); } DEBUG_DATA(("\n")); } alt_u32 Array2Value(alt_u8 *pData, alt_u8 DataLen){ alt_u32 Value=0; int i; for(i=0;iCapListHeader.CapListLength)); if (Len <= (4+8)) return; DEBUG_OUT((" Cur/Max Capacity Descriptor\n" )); Value = Array2Value(pResult->CurMaxCapHeader.NumberOfBlocks, 4); DEBUG_OUT((" Number of Blocks= %ld\n", Value )); switch(pResult->CurMaxCapHeader.DescriptorCode){ case CAP_UNFORMATTED_MEDIA: strcpy(szText, "Unformatted Media"); break; case CAP_FORMATTED_MEDIA: strcpy(szText, "Formatted Media"); break; case CAP_NO_CARTRIGE: strcpy(szText, "No Cartrige"); break; default: strcpy(szText,"Unkown"); } // switch DEBUG_OUT((" Descriptor Code = %d (%s)\n", (int)pResult->CurMaxCapHeader.DescriptorCode, szText )); Value = Array2Value(pResult->CurMaxCapHeader.BlockLength, 3); DEBUG_OUT((" Block Length = %ld\n", Value )); // other for(i=1;iCapListHeader.CapListLength/sizeof(SCSI_FORMATTABLE_CAP_DESCRIPTOR);i++){ DEBUG_OUT((" CAP[%d]\n", i )); Value = Array2Value(pResult->FormattableCapDescriptr[i-1].NumberOfBlocks, 4); DEBUG_OUT((" Number of Blocks= %ld\n", Value )); Value = Array2Value(pResult->FormattableCapDescriptr[i-1].BlockLength, 3); DEBUG_OUT((" Block Length = %ld\n", Value )); } } void BOT_ExplainRequestSense(SCSI_REQUEST_SENSE_DATA *pResult){ alt_u32 Value; DEBUG_OUT(("Request Sense Data:\n")); DEBUG_OUT((" Valid = %d\n", pResult->Valid )); DEBUG_OUT((" Error Code = %Xh\n", pResult->ErrorCode )); DEBUG_OUT((" SenseKey = %d\n", pResult->SenseKey )); Value = Array2Value(pResult->Information, 4); DEBUG_OUT((" Information = %lX\n", Value )); DEBUG_OUT((" Additional Sense Length = %d\n", pResult->AdditionalSenseLength )); DEBUG_OUT((" Additional Sense Code = %d\n", pResult->AdditionalSenseCode )); DEBUG_OUT((" Additional Sense Code Qualifer = %d\n", pResult->AdditionalSenseCodeQualifer )); } void BOT_ExplainCapResult(SCSI_READ_CAP_RESULT *pResult){ alt_u32 Value; DEBUG_OUT(("Capacities Result:\n")); Value = Array2Value(pResult->LastLogicalBlockAddress, 4); DEBUG_OUT((" Last Logical Block Address = %ld\n", Value )); Value = Array2Value(pResult->BlockLengthInBytes, 4); DEBUG_OUT((" Block Length In Bytes=%ld\n", Value )); } void BOT_Raw(alt_u8 *pData, alt_u16 DataLen){ int i; DEBUG_OUT(("=== raw data ===")); for(i=0;i