BaseUsbHost example program
Dependencies: BaseUsbHost FATFileSystem mbed mbed-rtos
Diff: UsbFlashDrive/UsbFlashDrive.cpp
- Revision:
- 0:2a9734a95d55
- Child:
- 1:80205a2de336
diff -r 000000000000 -r 2a9734a95d55 UsbFlashDrive/UsbFlashDrive.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UsbFlashDrive/UsbFlashDrive.cpp Tue Dec 04 13:39:57 2012 +0000 @@ -0,0 +1,388 @@ +// UsbFlashDrive.cpp 2012/12/4 +#include "mbed.h" +#include "rtos.h" +#include "BaseUsbHost.h" +#define DEBUG +#include "BaseUsbHostDebug.h" +#define TEST +#include "BaseUsbHostTest.h" + +#include "UsbFlashDrive.h" + +//#define WRITE_PROTECT + +int LE16(const uint8_t* d) +{ + return d[0] | (d[1] << 8); +} + +uint32_t BE32(uint8_t* d) +{ + return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3]; +} + +void BE16(uint32_t n, uint8_t* d) +{ + d[0] = (uint8_t)(n >> 8); + d[1] = (uint8_t)n; +} + +void BE32(uint32_t n, uint8_t* d) +{ + d[0] = (uint8_t)(n >> 24); + d[1] = (uint8_t)(n >> 16); + d[2] = (uint8_t)(n >> 8); + d[3] = (uint8_t)n; +} + +UsbFlashDrive::UsbFlashDrive(const char* name, ControlEp* ctlEp): FATFileSystem(name) +{ + m_name = name; + + if (ctlEp == NULL) { // root hub + DBG_OHCI(LPC_USB->HcRhPortStatus1); + TEST_ASSERT_FALSE(LPC_USB->HcRhPortStatus1 & 0x200); + m_ctlEp = new ControlEp(); + TEST_ASSERT_TRUE(m_ctlEp); + } else { + m_ctlEp = ctlEp; + } + + TEST_ASSERT(sizeof(CBW) == 31); + TEST_ASSERT(sizeof(CSW) == 13); + + m_numBlocks = 0; + m_BlockSize = 0; + m_lun = 0; + m_interface = 0; + m_pEpBulkIn = NULL; + m_pEpBulkOut = NULL; + + ParseConfiguration(); + int rc = m_ctlEp->SetConfiguration(1); + TEST_ASSERT_EQUAL(rc, USB_OK); + GetMaxLUN(); + setup(); +} + +int UsbFlashDrive::disk_initialize() +{ + //DBG("m_BlockSize=%d\n", m_BlockSize); + if (m_BlockSize != 512) { + return 1; + } + return 0; +} + +int UsbFlashDrive::disk_write(const uint8_t* buffer, uint64_t sector) +{ + m_report_disk_write++; + //DBG("buffer=%p block_number=%d\n", buffer, sector); + int ret = MS_BulkSend(sector, 1, buffer); + if (ret >= 0) { + return 0; + } + return 1; +} + +int UsbFlashDrive::disk_read(uint8_t* buffer, uint64_t sector) +{ + m_report_disk_read++; + //DBG("buffer=%p block_number=%d\n", buffer, sector); + int ret = MS_BulkRecv(sector, 1, buffer); + if (ret >= 0) { + return 0; + } + return 1; +} + +int UsbFlashDrive::disk_status() +{ + m_report_disk_status++; + return 0; +} + +int UsbFlashDrive::disk_sync() +{ + m_report_disk_sync++; + return 0; +} + +uint64_t UsbFlashDrive::disk_sectors() +{ + DBG("m_numBlocks=%d\n", m_numBlocks); + return m_numBlocks; +} + +int UsbFlashDrive::setup(int timeout) +{ + + int retry = 0; + Timer t; + t.start(); + t.reset(); + while(t.read_ms() < timeout) { + DBG("retry=%d t=%d\n", retry, t.read_ms()); + if (retry > 80) { + return -1; + } + int rc = TestUnitReady(); + DBG("TestUnitReady(): %d\n", rc); + if (rc == USB_OK) { + DBG("m_CSW.bCSWStatus: %02X\n", m_CSW.bCSWStatus); + if (m_CSW.bCSWStatus == 0x00) { + break; + } + } + GetSenseInfo(); + retry++; + wait_ms(50); + } + if (t.read_ms() >= timeout) { + return -1; + } + ReadCapacity(); + Inquire(); + return 0; +} + +int UsbFlashDrive::ParseConfiguration() +{ + int rc; + uint8_t ConfigDesc[9]; + int index = 0; + TEST_ASSERT(m_ctlEp); + rc = m_ctlEp->GetDescriptor(USB_DESCRIPTOR_TYPE_CONFIGURATION, index, ConfigDesc, sizeof(ConfigDesc)); + TEST_ASSERT(rc == USB_OK); + DBG_BYTES("ConfigDescriptor 9bytes", ConfigDesc, sizeof(ConfigDesc)); + TEST_ASSERT(ConfigDesc[0] == 9); + TEST_ASSERT(ConfigDesc[1] == 0x02); + int wTotalLength = *((uint16_t*)&ConfigDesc[2]); + DBG("TotalLength: %d\n", wTotalLength); + int bConfigValue = ConfigDesc[5]; + TEST_ASSERT(bConfigValue == 1); + DBG("ConfigValue: %d\n", bConfigValue); + DBG("MaxPower: %d mA\n", ConfigDesc[8]*2); + + uint8_t* buf = new uint8_t[wTotalLength]; + TEST_ASSERT(buf); + rc = m_ctlEp->GetDescriptor(USB_DESCRIPTOR_TYPE_CONFIGURATION, index, buf, wTotalLength); + TEST_ASSERT(rc == USB_OK); + TEST_ASSERT(ConfigDesc[1] == 0x02); + for (int pos = 0; pos < wTotalLength; pos += buf[pos]) { + DBG_BYTES("CFG", buf+pos, buf[pos]); + int type = buf[pos+1]; + if (USB_DESCRIPTOR_TYPE_INTERFACE == type) { // 0x04 + DBG("InterfaceNumber: %d\n", buf[pos+2]); + DBG("AlternateSetting: %d\n", buf[pos+3]); + DBG("NumEndpoint: %d\n", buf[pos+4]); + DBG("InterfaceClass: %02X\n", buf[pos+5]); + DBG("InterfaceSubClass: %02X\n", buf[pos+6]); + DBG("InterfaceProtocol: %02X\n", buf[pos+7]); + TEST_ASSERT(buf[pos+6] == 0x06); // SCSI + TEST_ASSERT(buf[pos+7] == 0x50); // bulk only + } + if (USB_DESCRIPTOR_TYPE_ENDPOINT == type) { + TEST_ASSERT(buf[pos] == 7); + uint8_t att = buf[pos+3]; + if (att == 2) { // bulk + uint8_t ep = buf[pos+2]; + bool dir = ep & 0x80; // true=IN + uint16_t size = LE16(buf+pos+4); + DBG("EndpointAddress: %02X\n", ep); + DBG("Attribute: %02X\n", att); + DBG("MaxPacketSize: %d\n", size); + BulkEp* pEp = new BulkEp(m_ctlEp->GetAddr(), ep, size); + TEST_ASSERT(pEp); + if (dir) { + m_pEpBulkIn = pEp; + } else { + m_pEpBulkOut = pEp; + } + } + } + } + delete[] buf; + TEST_ASSERT(m_pEpBulkIn); + TEST_ASSERT(m_pEpBulkOut); + return 0; +} + +int UsbFlashDrive::BulkOnlyMassStorageReset() +{ + TEST_ASSERT(m_ctlEp); + int rc = m_ctlEp->controlReceive(0x21, 0xff, 0x0000, m_interface, NULL, 0); + TEST_ASSERT(rc == USB_OK); + return rc; +} + +int UsbFlashDrive::GetMaxLUN() +{ + TEST_ASSERT(m_interface == 0); + uint8_t temp[1]; + TEST_ASSERT(m_ctlEp); + int rc = m_ctlEp->controlReceive(0xa1, 0xfe, 0x0000, m_interface, temp, sizeof(temp)); + TEST_ASSERT(rc == USB_OK); + DBG_BYTES("GetMaxLUN", temp, sizeof(temp)); + m_MaxLUN = temp[0]; + TEST_ASSERT(m_MaxLUN <= 15); + return rc; +} + + +int UsbFlashDrive::TestUnitReady() +{ + const uint8_t cdb[6] = {SCSI_CMD_TEST_UNIT_READY, 0x00, 0x00, 0x00, 0x00, 0x00}; + m_CBW.dCBWDataTraansferLength = 0; + m_CBW.bmCBWFlags = 0x00; + CommandTransport(cdb, sizeof(cdb)); + StatusTransport(); + return 0; +} + +int UsbFlashDrive::GetSenseInfo() +{ + const uint8_t cdb[6] = {SCSI_CMD_REQUEST_SENSE, 0x00, 0x00, 0x00, 18, 0x00}; + m_CBW.dCBWDataTraansferLength = 18; + m_CBW.bmCBWFlags = 0x80; // data In + CommandTransport(cdb, sizeof(cdb)); + + uint8_t buf[18]; + _bulkRecv(buf, sizeof(buf)); + DBG_HEX(buf, sizeof(buf)); + + StatusTransport(); + TEST_ASSERT(m_CSW.bCSWStatus == 0x00); + return 0; +} + +int UsbFlashDrive::ReadCapacity() +{ + const uint8_t cdb[10] = {SCSI_CMD_READ_CAPACITY, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00}; + m_CBW.dCBWDataTraansferLength = 8; + m_CBW.bmCBWFlags = 0x80; // data In + CommandTransport(cdb, sizeof(cdb)); + + uint8_t buf[8]; + int rc = _bulkRecv(buf, sizeof(buf)); + TEST_ASSERT(rc >= 0); + DBG_HEX(buf, sizeof(buf)); + + StatusTransport(); + TEST_ASSERT(m_CSW.bCSWStatus == 0x00); + + m_numBlocks = BE32(buf); + m_BlockSize = BE32(buf+4); + DBG("m_numBlocks=%d m_BlockSize=%d\n", m_numBlocks, m_BlockSize); + TEST_ASSERT(m_BlockSize == 512); + TEST_ASSERT(m_numBlocks > 0); + return 0; +} + +int UsbFlashDrive::Inquire() +{ + const uint8_t cdb[6] = {SCSI_CMD_INQUIRY, 0x00, 0x00, 0x00, 36, 0x00}; + m_CBW.dCBWDataTraansferLength = 36; + m_CBW.bmCBWFlags = 0x80; // data In + CommandTransport(cdb, sizeof(cdb)); + + uint8_t buf[36]; + int rc = _bulkRecv(buf, sizeof(buf)); + TEST_ASSERT(rc >= 0); + DBG_HEX(buf, sizeof(buf)); + + StatusTransport(); + return 0; +} + +int UsbFlashDrive::MS_BulkRecv(uint32_t block_number, int num_blocks, uint8_t* user_buffer) +{ + TEST_ASSERT(m_BlockSize == 512); + TEST_ASSERT(num_blocks == 1); + TEST_ASSERT(user_buffer); + uint8_t cdb[10] = {SCSI_CMD_READ_10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00}; + BE32(block_number, cdb+2); + BE16(num_blocks, cdb+7); + uint32_t len = m_BlockSize * num_blocks; + TEST_ASSERT(len <= 512); + m_CBW.dCBWDataTraansferLength = len; + m_CBW.bmCBWFlags = 0x80; // data In + CommandTransport(cdb, sizeof(cdb)); + + int ret = _bulkRecv(user_buffer, len); + //DBG_HEX(user_buffer, len); + + StatusTransport(); + TEST_ASSERT(m_CSW.bCSWStatus == 0x00); + return ret; +} + +int UsbFlashDrive::MS_BulkSend(uint32_t block_number, int num_blocks, const uint8_t* user_buffer) +{ +#ifdef WRITE_PROTECT + return 0; +#else + TEST_ASSERT(num_blocks == 1); + TEST_ASSERT(user_buffer); + uint8_t cdb[10] = {SCSI_CMD_WRITE_10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00}; + BE32(block_number, cdb+2); + BE16(num_blocks, cdb+7); + uint32_t len = m_BlockSize * num_blocks; + TEST_ASSERT(len <= 512); + m_CBW.dCBWDataTraansferLength = len; + m_CBW.bmCBWFlags = 0x00; // data Out + CommandTransport(cdb, sizeof(cdb)); + + int ret = _bulkSend(user_buffer, len); + //DBG_HEX(user_buffer, len); + + StatusTransport(); + TEST_ASSERT(m_CSW.bCSWStatus == 0x00); + return ret; +#endif //WRITE_PROTECT +} + +int UsbFlashDrive::CommandTransport(const uint8_t* cdb, int size) +{ + TEST_ASSERT(cdb); + TEST_ASSERT(size >= 6); + TEST_ASSERT(size <= 16); + m_CBW.bCBWLUN = m_lun; + m_CBW.bCBWCBLength = size; + memcpy(m_CBW.CBWCB, cdb, size); + + m_CBW.dCBWSignature = 0x43425355; + m_CBW.dCBWTag = m_tag++; + m_CBW.bCBWLUN = 0; + //DBG_HEX((uint8_t*)&m_CBW, sizeof(CBW)); + int rc = _bulkSend((uint8_t*)&m_CBW, sizeof(CBW)); + return rc; +} + +int UsbFlashDrive::StatusTransport() +{ + TEST_ASSERT(sizeof(CSW) == 13); + int rc = _bulkRecv((uint8_t*)&m_CSW, sizeof(CSW)); + //DBG_HEX((uint8_t*)&m_CSW, sizeof(CSW)); + TEST_ASSERT(m_CSW.dCSWSignature == 0x53425355); + TEST_ASSERT(m_CSW.dCSWTag == m_CBW.dCBWTag); + TEST_ASSERT(m_CSW.dCSWDataResidue == 0); + return rc; +} + +int UsbFlashDrive::_bulkRecv(uint8_t* buf, int size) +{ + TEST_ASSERT(m_pEpBulkIn); + int ret = m_pEpBulkIn->read(buf, size); + return ret; +} + +int UsbFlashDrive::_bulkSend(const uint8_t* buf, int size) +{ + TEST_ASSERT(m_pEpBulkOut); + int ret = m_pEpBulkOut->write(buf, size); + return ret; +}