USBMSD SD card Hello World for Mbed platforms

Dependencies:   mbed USBMSD_SD USBDevice

Revision:
2:27a7e7f8d399
Child:
3:0ffb2eee9e06
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USBDevice/USBMSD/USBMSD.cpp	Fri Nov 11 15:22:53 2011 +0000
@@ -0,0 +1,582 @@
+// USBMSD.cpp
+// USB mass storage device example
+// Copyright (c) 2011 ARM Limited. All rights reserved.
+
+#include "stdint.h"
+#include "USBMSD.h"
+#include "USBBusInterface.h"
+
+#define CBW_Signature               0x43425355
+#define CSW_Signature               0x53425355
+
+// SCSI Commands
+#define TEST_UNIT_READY            0x00
+#define REQUEST_SENSE              0x03
+#define FORMAT_UNIT                0x04
+#define INQUIRY                    0x12
+#define MODE_SELECT6               0x15
+#define MODE_SENSE6                0x1A
+#define START_STOP_UNIT            0x1B
+#define MEDIA_REMOVAL              0x1E
+#define READ_FORMAT_CAPACITIES     0x23
+#define READ_CAPACITY              0x25
+#define READ10                     0x28
+#define WRITE10                    0x2A
+#define VERIFY10                   0x2F
+#define READ12                     0xA8
+#define WRITE12                    0xAA
+#define MODE_SELECT10              0x55
+#define MODE_SENSE10               0x5A
+
+// MSC class specific requests
+#define MSC_REQUEST_RESET               0xFF
+#define MSC_REQUEST_GET_MAX_LUN         0xFE
+
+#define DEFAULT_CONFIGURATION (1)
+
+// Max In/Out Packet Size on the bulk endpoint */
+#define MAX_PACKET  MAX_PACKET_SIZE_EPBULK
+
+// memory size
+#define MemorySize  0x2000000
+
+//number of blocks
+#define BlockCount  (MemorySize / BlockSize)
+
+// CSW Status
+enum Status {
+    SCSI_PASSED,
+    SCSI_FAILED,
+    SCSI_PHASE_ERROR,
+};
+
+
+USBMSD::USBMSD(uint16_t vendor_id, uint16_t product_id, uint16_t product_release): USBDevice(vendor_id, product_id, product_release), mem(p5, p6, p7, p8) {
+}
+
+// Called in ISR context to process a class specific request
+bool USBMSD::USBCallback_request(void) {
+
+    bool success = false;
+    CONTROL_TRANSFER * transfer = getTransferPtr();
+
+    if (transfer->setup.bmRequestType.Type == CLASS_TYPE) {
+        switch (transfer->setup.bRequest) {
+        case MSC_REQUEST_RESET:
+            reset();
+            success = true;
+            break;
+        case MSC_REQUEST_GET_MAX_LUN:
+            transfer->remaining = 1;
+            transfer->ptr = getMaxLUN();
+            transfer->direction = DEVICE_TO_HOST;
+            success = true;
+            break;
+        default:
+            break;
+        }
+    }
+
+    return success;
+}
+
+void USBMSD::reset()
+{
+    stage = STATE_READ_CBW;
+}
+
+uint8_t * USBMSD::getMaxLUN()
+{
+    static uint8_t LUN[] = {0};
+    return LUN;
+}
+
+// Called in ISR context called when a data is received
+bool USBMSD::EP2_OUT_callback() {
+    uint16_t size = 0;
+    uint8_t buf[MAX_PACKET_SIZE_EPBULK];
+    read(EPBULK_OUT, buf, &size, MAX_PACKET_SIZE_EPBULK);
+    switch (stage) {
+        case STATE_READ_CBW:
+            CBWDecode(buf, size);
+            break;
+        case STATE_PROCESS_CBW:
+            switch (cbw.CB[0]) {
+                case WRITE10:
+                case WRITE12:
+                    memoryWrite(buf, size);
+                    break;
+                case VERIFY10:
+                    memoryVerify(buf, size);
+                    break;
+            }
+            break;
+        case STATE_WAIT_CSW:
+            break;
+        default:
+            csw.Status = SCSI_PHASE_ERROR;
+            sendCSW();
+            break;
+    }
+
+    //reactivate readings on the OUT bulk endpoint
+    readStart(EPBULK_OUT, MAX_PACKET_SIZE_EPBULK);
+    return true;
+}
+
+// Called in ISR context when a data has been transferred
+bool USBMSD::EP2_IN_callback() {
+    switch (stage) {
+        case STATE_PROCESS_CBW:
+            switch (cbw.CB[0]) {
+                case READ10:
+                case READ12:
+                    memoryRead();
+                    break;
+            }
+            break;
+        case STATE_SEND_CSW:
+            sendCSW();
+            break;
+        case STATE_ERROR:
+            stallEndpoint(EPBULK_IN);
+            sendCSW();
+            break;
+        case STATE_WAIT_CSW:
+            stage = STATE_READ_CBW;
+            break;
+    }
+    return true;
+}
+
+
+void USBMSD::memoryWrite (uint8_t * buf, uint16_t size) {
+
+    if ((addr + size) > MemorySize) {
+        size = MemorySize - addr;
+        stage = STATE_WAIT_CSW;
+        stallEndpoint(EPBULK_OUT);
+    }
+
+    // we fill an array in RAM of 1 block before writing it in memory
+    for (int i = 0; i < size; i++)
+        page[addr%BlockSize + i] = buf[i];
+
+    // if the array is filled, write it in memory
+    if (!((addr + size)%BlockSize))
+        mem.disk_write((char *)page, addr/BlockSize);
+
+    addr += size;
+    length -= size;
+    csw.DataResidue -= size;
+
+    if ((!length) || (stage == STATE_WAIT_CSW)) {
+        csw.Status = SCSI_PASSED;
+        sendCSW();
+    }
+}
+
+void USBMSD::memoryVerify (uint8_t * buf, uint16_t size) {
+    uint32_t n;
+
+    if ((addr + size) > MemorySize) {
+        size = MemorySize - addr;
+        stage = STATE_WAIT_CSW;
+        stallEndpoint(EPBULK_OUT);
+    }
+
+    // beginning of a new block -> load a whole block in RAM
+    if (!(addr%BlockSize))
+        mem.disk_read((char *)page, addr/BlockSize);
+
+    // info are in RAM -> no need to re-read memory
+    for (n = 0; n < size; n++) {
+        if (page[addr%BlockSize + n] != buf[n]) {
+            memOK = false;
+            break;
+        }
+    }
+
+    addr += size;
+    length -= size;
+    csw.DataResidue -= size;
+
+    if ( !length || (stage == STATE_WAIT_CSW)) {
+        csw.Status = (memOK) ? SCSI_PASSED : SCSI_FAILED;
+        sendCSW();
+    }
+}
+
+
+bool USBMSD::inquiryRequest (void) {
+    uint8_t inquiry[] = { 0x00, 0x80, 0x00, 0x01,
+                          36 - 4, 0x80, 0x00, 0x00,
+                          'M', 'B', 'E', 'D', '.', 'O', 'R', 'G',
+                          'M', 'B', 'E', 'D', ' ', 'U', 'S', 'B', ' ', 'D', 'I', 'S', 'K', ' ', ' ', ' ',
+                          '1', '.', '0', ' ',
+                        };
+    if (!write(inquiry, sizeof(inquiry))) {
+        return false;
+    }
+    return true;
+}
+
+
+bool USBMSD::readFormatCapacity() {
+    uint8_t capacity[] = { 0x00, 0x00, 0x00, 0x08,
+                           (BlockCount >> 24) & 0xff,
+                           (BlockCount >> 16) & 0xff,
+                           (BlockCount >> 8) & 0xff,
+                           (BlockCount >> 0) & 0xff,
+
+                           0x02,
+                           (BlockSize >> 16) & 0xff,
+                           (BlockSize >> 8) & 0xff,
+                           (BlockSize >> 0) & 0xff,
+                         };
+    if (!write(capacity, sizeof(capacity))) {
+        return false;
+    }
+    return true;
+}
+
+
+bool USBMSD::readCapacity (void) {
+    uint8_t capacity[] = {
+        ((BlockCount - 1) >> 24) & 0xff,
+        ((BlockCount - 1) >> 16) & 0xff,
+        ((BlockCount - 1) >> 8) & 0xff,
+        ((BlockCount - 1) >> 0) & 0xff,
+
+        (BlockSize >> 24) & 0xff,
+        (BlockSize >> 16) & 0xff,
+        (BlockSize >> 8) & 0xff,
+        (BlockSize >> 0) & 0xff,
+    };
+    if (!write(capacity, sizeof(capacity))) {
+        return false;
+    }
+    return true;
+
+}
+
+bool USBMSD::write (uint8_t * buf, uint16_t size) {
+
+    if (size >= cbw.DataLength) {
+        size = cbw.DataLength;
+    }
+    stage = STATE_SEND_CSW;
+
+    if (!writeNB(EPBULK_IN, buf, size, MAX_PACKET_SIZE_EPBULK)) {
+        return false;
+    }
+
+    csw.DataResidue -= size;
+    csw.Status = SCSI_PASSED;
+    return true;
+}
+
+
+bool USBMSD::modeSense6 (void) {
+    uint8_t sense6[] = { 0x03, 0x00, 0x00, 0x00 };
+    if (!write(sense6, sizeof(sense6))) {
+        return false;
+    }
+    return true;
+}
+
+void USBMSD::sendCSW() {
+    csw.Signature = CSW_Signature;
+    writeNB(EPBULK_IN, (uint8_t *)&csw, sizeof(CSW), MAX_PACKET_SIZE_EPBULK);
+    stage = STATE_WAIT_CSW;
+}
+
+bool USBMSD::requestSense (void) {
+    uint8_t request_sense[] = {
+        0x70,   // Response Code
+        0x00,
+        0x05,   // Sense Key: illegal request
+        0x00,
+        0x00,
+        0x00,
+        0x00,
+        0x0A,   // Additional Length
+        0x00,
+        0x00,
+        0x00,
+        0x00,
+        0x30,   // ASC
+        0x01,   // ASCQ
+        0x00,
+        0x00,
+        0x00,
+        0x00,
+    };
+
+    if (!write(request_sense, sizeof(request_sense))) {
+        return false;
+    }
+
+    return true;
+}
+
+void USBMSD::fail() {
+    csw.Status = SCSI_FAILED;
+    sendCSW();
+}
+
+void USBMSD::CBWDecode(uint8_t * buf, uint16_t size) {
+    if (size == sizeof(cbw)) {
+        memcpy((uint8_t *)&cbw, buf, size);
+        if (cbw.Signature == CBW_Signature) {
+            csw.Tag = cbw.Tag;
+            csw.DataResidue = cbw.DataLength;
+            if ((cbw.CBLength <  1) || (cbw.CBLength > 16) ) {
+                fail();
+            } else {
+                switch (cbw.CB[0]) {
+                    case TEST_UNIT_READY:
+                        testUnitReady();
+                        break;
+                    case REQUEST_SENSE:
+                        requestSense();
+                        break;
+                    case INQUIRY:
+                        inquiryRequest();
+                        break;
+                    case MODE_SENSE6:
+                        modeSense6();
+                        break;
+                    case READ_FORMAT_CAPACITIES:
+                        readFormatCapacity();
+                        break;
+                    case READ_CAPACITY:
+                        readCapacity();
+                        break;
+                    case READ10:
+                    case READ12:
+                        if (infoTransfer()) {
+                            if ((cbw.Flags & 0x80)) {
+                                stage = STATE_PROCESS_CBW;
+                                memoryRead();
+                            } else {
+                                stallEndpoint(EPBULK_OUT);
+                                csw.Status = SCSI_PHASE_ERROR;
+                                sendCSW();
+                            }
+                        }
+                        break;
+                    case WRITE10:
+                    case WRITE12:
+                        if (infoTransfer()) {
+                            if (!(cbw.Flags & 0x80)) {
+                                stage = STATE_PROCESS_CBW;
+                            } else {
+                                stallEndpoint(EPBULK_IN);
+                                csw.Status = SCSI_PHASE_ERROR;
+                                sendCSW();
+                            }
+                        }
+                        break;
+                    case VERIFY10:
+                        if (!(cbw.CB[1] & 0x02)) {
+                            csw.Status = SCSI_PASSED;
+                            sendCSW();
+                            break;
+                        }
+                        if (infoTransfer()) {
+                            if (!(cbw.Flags & 0x80)) {
+                                stage = STATE_PROCESS_CBW;
+                                memOK = true;
+                            } else {
+                                stallEndpoint(EPBULK_IN);
+                                csw.Status = SCSI_PHASE_ERROR;
+                                sendCSW();
+                            }
+                        }
+                        break;
+                    default:
+                        fail();
+                        break;
+                }
+            }
+        }
+    }
+}
+
+void USBMSD::testUnitReady (void) {
+
+    if (cbw.DataLength != 0) {
+        if ((cbw.Flags & 0x80) != 0) {
+            stallEndpoint(EPBULK_IN);
+        } else {
+            stallEndpoint(EPBULK_OUT);
+        }
+    }
+
+    csw.Status = SCSI_PASSED;
+    sendCSW();
+}
+
+
+void USBMSD::memoryRead (void) {
+    uint32_t n;
+
+    n = (length > MAX_PACKET) ? MAX_PACKET : length;
+
+    if ((addr + n) > MemorySize) {
+        n = MemorySize - addr;
+        stage = STATE_ERROR;
+    }
+
+    if (!(addr%BlockSize))
+        mem.disk_read((char *)page, addr/BlockSize);
+
+    writeNB(EPBULK_IN, &page[addr%BlockSize], n, MAX_PACKET_SIZE_EPBULK);
+
+    addr += n;
+    length -= n;
+
+    csw.DataResidue -= n;
+
+    if (!length) {
+        stage = STATE_SEND_CSW;
+    }
+
+    if (stage != STATE_PROCESS_CBW) {
+        csw.Status = SCSI_PASSED;
+    }
+}
+
+
+bool USBMSD::infoTransfer (void) {
+    uint32_t n;
+
+    // Logical Block Address of First Block
+    n = (cbw.CB[2] << 24) | (cbw.CB[3] << 16) | (cbw.CB[4] <<  8) | (cbw.CB[5] <<  0);
+
+    addr = n * BlockSize;
+
+    // Number of Blocks to transfer
+    switch (cbw.CB[0]) {
+        case READ10:
+        case WRITE10:
+        case VERIFY10:
+            n = (cbw.CB[7] <<  8) | (cbw.CB[8] <<  0);
+            break;
+
+        case READ12:
+        case WRITE12:
+            n = (cbw.CB[6] << 24) | (cbw.CB[7] << 16) | (cbw.CB[8] <<  8) | (cbw.CB[9] <<  0);
+            break;
+    }
+
+    length = n * BlockSize;
+
+    if (!cbw.DataLength) {              // host requests no data
+        csw.Status = SCSI_FAILED;
+        sendCSW();
+        return false;
+    }
+
+    if (cbw.DataLength != length) {
+        if ((cbw.Flags & 0x80) != 0) {
+            stallEndpoint(EPBULK_IN);
+        } else {
+            stallEndpoint(EPBULK_OUT);
+        }
+
+        csw.Status = SCSI_FAILED;
+        sendCSW();
+        return false;
+    }
+
+    return true;
+}
+
+
+
+
+
+// Called in ISR context
+// Set configuration. Return false if the
+// configuration is not supported.
+bool USBMSD::USBCallback_setConfiguration(uint8_t configuration) {
+    if (configuration != DEFAULT_CONFIGURATION) {
+        return false;
+    }
+
+    // Configure endpoints > 0
+    addEndpoint(EPBULK_IN, MAX_PACKET_SIZE_EPBULK);
+    addEndpoint(EPBULK_OUT, MAX_PACKET_SIZE_EPBULK);
+    
+    //activate readings
+    readStart(EPBULK_OUT, MAX_PACKET_SIZE_EPBULK);
+    return true;
+}
+
+
+uint8_t * USBMSD::stringIinterfaceDesc() {
+    static uint8_t stringIinterfaceDescriptor[] = {
+        0x08,                           //bLength
+        STRING_DESCRIPTOR,              //bDescriptorType 0x03
+        'M',0,'S',0,'D',0               //bString iInterface - MSD
+    };
+    return stringIinterfaceDescriptor;
+}
+
+uint8_t * USBMSD::stringIproductDesc() {
+    static uint8_t stringIproductDescriptor[] = {
+        0x12,                                                       //bLength
+        STRING_DESCRIPTOR,                                          //bDescriptorType 0x03
+        'M',0,'b',0,'e',0,'d',0,' ',0,'M',0,'S',0,'D',0 //bString iProduct - Mbed Audio
+    };
+    return stringIproductDescriptor;
+}
+
+
+uint8_t * USBMSD::configurationDesc() {
+    static uint8_t configDescriptor[] = {
+
+        // Configuration 1
+        9,      // bLength
+        2,      // bDescriptorType
+        LSB(9 + 9 + 7 + 7), // wTotalLength
+        MSB(9 + 9 + 7 + 7),
+        0x01,   // bNumInterfaces
+        0x01,   // bConfigurationValue: 0x01 is used to select this configuration
+        0x00,   // iConfiguration: no string to describe this configuration
+        0xC0,   // bmAttributes
+        100,    // bMaxPower, device power consumption is 100 mA
+
+        // Interface 0, Alternate Setting 0, MSC Class
+        9,      // bLength
+        4,      // bDescriptorType
+        0x00,   // bInterfaceNumber
+        0x00,   // bAlternateSetting
+        0x02,   // bNumEndpoints
+        0x08,   // bInterfaceClass
+        0x06,   // bInterfaceSubClass
+        0x50,   // bInterfaceProtocol
+        0x04,   // iInterface
+
+        // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
+        7,                          // bLength
+        5,                          // bDescriptorType
+        PHY_TO_DESC(EPBULK_IN),     // bEndpointAddress
+        0x02,                       // bmAttributes (0x02=bulk)
+        LSB(MAX_PACKET_SIZE_EPBULK),// wMaxPacketSize (LSB)
+        MSB(MAX_PACKET_SIZE_EPBULK),// wMaxPacketSize (MSB)
+        0,                          // bInterval
+
+        // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
+        7,                          // bLength
+        5,                          // bDescriptorType
+        PHY_TO_DESC(EPBULK_OUT),    // bEndpointAddress
+        0x02,                       // bmAttributes (0x02=bulk)
+        LSB(MAX_PACKET_SIZE_EPBULK),// wMaxPacketSize (LSB)
+        MSB(MAX_PACKET_SIZE_EPBULK),// wMaxPacketSize (MSB)
+        0                           // bInterval
+    };
+    return configDescriptor;
+}