Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: mbed USBMSD_SD USBDevice
USBMSD/USBMSD.cpp
- Committer:
- samux
- Date:
- 2011-11-16
- Revision:
- 11:a26e7b7a1221
- Parent:
- 10:cf8fd2b6ca23
- Child:
- 12:a12eb1fc05f3
File content as of revision 11:a26e7b7a1221:
// 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
// CSW Status
enum Status {
CSW_PASSED,
CSW_FAILED,
CSW_ERROR,
};
USBMSD::USBMSD(uint16_t vendor_id, uint16_t product_id, uint16_t product_release): USBDevice(vendor_id, product_id, product_release) {
}
bool USBMSD::USBCallback_request() {
bool success = false;
CONTROL_TRANSFER * transfer = getTransferPtr();
uint8_t *hidDescriptor;
// Process additional standard requests
if ((transfer->setup.bmRequestType.Type == STANDARD_TYPE)) {
switch (transfer->setup.bRequest) {
case GET_DESCRIPTOR:
switch (DESCRIPTOR_TYPE(transfer->setup.wValue)) {
case REPORT_DESCRIPTOR:
if ((reportDesc() != NULL) \
&& (reportDescLength() != 0)) {
transfer->remaining = reportDescLength();
transfer->ptr = reportDesc();
transfer->direction = DEVICE_TO_HOST;
success = true;
}
break;
case HID_DESCRIPTOR:
// Find the HID descriptor, after the configuration descriptor
hidDescriptor = findDescriptor(HID_DESCRIPTOR);
if (hidDescriptor != NULL) {
transfer->remaining = HID_DESCRIPTOR_LENGTH;
transfer->ptr = hidDescriptor;
transfer->direction = DEVICE_TO_HOST;
success = true;
}
break;
default:
break;
}
break;
default:
break;
}
}
// Process class-specific requests
if (transfer->setup.bmRequestType.Type == CLASS_TYPE) {
uint8_t * dum = {0};
switch (transfer->setup.bRequest) {
case SET_REPORT:
// First byte will be used for report ID
outputReport.data[0] = transfer->setup.wValue & 0xff;
outputReport.length = transfer->setup.wLength + 1;
transfer->remaining = sizeof(outputReport.data) - 1;
transfer->ptr = &outputReport.data[1];
transfer->direction = HOST_TO_DEVICE;
transfer->notify = true;
success = true;
case MSC_REQUEST_GET_MAX_LUN:
transfer->remaining = 1;
transfer->ptr = dum;
transfer->direction = DEVICE_TO_HOST;
success = true;
break;
case SET_IDLE:
success = true;
default:
break;
}
}
return success;
}
bool USBMSD::send(HID_REPORT *report) {
return USBDevice::write(EPINT_IN, report->data, report->length, MAX_HID_REPORT_SIZE);
}
bool USBMSD::read(HID_REPORT *report) {
uint16_t bytesRead = 0;
bool result;
result = USBDevice::read(EPINT_OUT, report->data, &bytesRead, MAX_HID_REPORT_SIZE);
if (!readStart(EPINT_OUT, MAX_HID_REPORT_SIZE))
return false;
report->length = bytesRead;
return result;
}
bool USBMSD::readNB(HID_REPORT *report) {
uint16_t bytesRead = 0;
bool result;
result = USBDevice::readNB(EPINT_OUT, report->data, &bytesRead, MAX_HID_REPORT_SIZE);
report->length = bytesRead;
if (!readStart(EPINT_OUT, MAX_HID_REPORT_SIZE))
return false;
return result;
}
uint16_t USBMSD::reportDescLength() {
reportDesc();
return reportLength;
}
bool USBMSD::connect() {
//disk initialization
disk_initialize();
// get block size
BlockSize = 512;
if (BlockSize != 0) {
page = (uint8_t *)malloc(BlockSize * sizeof(uint8_t));
if (page == NULL)
return false;
}
BlockCount = disk_sectors();
//get memory size
MemorySize = BlockCount * BlockSize;
if (!MemorySize) {
return false;
}
/*printf("blockSize: %d\r\n", BlockSize);
printf("memSize: %d\r\n", MemorySize);
printf("number of blocks: %d\r\n", BlockCount);*/
//connect the device
USBDevice::connect();
return true;
}
void USBMSD::reset() {
stage = 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];
USBDevice::read(EPBULK_OUT, buf, &size, MAX_PACKET_SIZE_EPBULK);
switch (stage) {
// the device has to decode the CBW received
case READ_CBW:
CBWDecode(buf, size);
break;
// the device has to receive data from the host
case PROCESS_CBW:
switch (cbw.CB[0]) {
case WRITE10:
case WRITE12:
memoryWrite(buf, size);
break;
case VERIFY10:
memoryVerify(buf, size);
break;
}
break;
// an error has occured: stall endpoint and send CSW
default:
stallEndpoint(EPBULK_OUT);
csw.Status = CSW_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) {
// the device has to send data to the host
case PROCESS_CBW:
switch (cbw.CB[0]) {
case READ10:
case READ12:
memoryRead();
break;
}
break;
//the device has to send a CSW
case SEND_CSW:
sendCSW();
break;
// an error has occured
case ERROR:
stallEndpoint(EPBULK_IN);
sendCSW();
break;
// the host has received the CSW -> we wait a CBW
case WAIT_CSW:
stage = READ_CBW;
break;
}
return true;
}
void USBMSD::memoryWrite (uint8_t * buf, uint16_t size) {
if ((addr + size) > MemorySize) {
size = MemorySize - addr;
stage = ERROR;
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))
disk_write((const char *)page, addr/BlockSize);
addr += size;
length -= size;
csw.DataResidue -= size;
if ((!length) || (stage != PROCESS_CBW)) {
csw.Status = (stage == ERROR) ? CSW_FAILED : CSW_PASSED;
sendCSW();
}
}
void USBMSD::memoryVerify (uint8_t * buf, uint16_t size) {
uint32_t n;
if ((addr + size) > MemorySize) {
size = MemorySize - addr;
stage = ERROR;
stallEndpoint(EPBULK_OUT);
}
// beginning of a new block -> load a whole block in RAM
if (!(addr%BlockSize))
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 != PROCESS_CBW)) {
csw.Status = (memOK && (stage == PROCESS_CBW)) ? CSW_PASSED : CSW_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 = SEND_CSW;
writeNB(EPBULK_IN, buf, size, MAX_PACKET_SIZE_EPBULK);
csw.DataResidue -= size;
csw.Status = CSW_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 = 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 = CSW_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 = PROCESS_CBW;
memoryRead();
} else {
stallEndpoint(EPBULK_OUT);
csw.Status = CSW_ERROR;
sendCSW();
}
}
break;
case WRITE10:
case WRITE12:
if (infoTransfer()) {
if (!(cbw.Flags & 0x80)) {
stage = PROCESS_CBW;
} else {
stallEndpoint(EPBULK_IN);
csw.Status = CSW_ERROR;
sendCSW();
}
}
break;
case VERIFY10:
if (!(cbw.CB[1] & 0x02)) {
csw.Status = CSW_PASSED;
sendCSW();
break;
}
if (infoTransfer()) {
if (!(cbw.Flags & 0x80)) {
stage = PROCESS_CBW;
memOK = true;
} else {
stallEndpoint(EPBULK_IN);
csw.Status = CSW_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 = CSW_PASSED;
sendCSW();
}
void USBMSD::memoryRead (void) {
uint32_t n;
n = (length > MAX_PACKET) ? MAX_PACKET : length;
if ((addr + n) > MemorySize) {
n = MemorySize - addr;
stage = ERROR;
}
// we read an entire block
if (!(addr%BlockSize))
disk_read((char *)page, addr/BlockSize);
// write data which are in RAM
writeNB(EPBULK_IN, &page[addr%BlockSize], n, MAX_PACKET_SIZE_EPBULK);
addr += n;
length -= n;
csw.DataResidue -= n;
if ( !length || (stage != PROCESS_CBW)) {
csw.Status = (stage == PROCESS_CBW) ? CSW_PASSED : CSW_FAILED;
sendCSW();
}
}
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 = CSW_FAILED;
sendCSW();
return false;
}
if (cbw.DataLength != length) {
if ((cbw.Flags & 0x80) != 0) {
stallEndpoint(EPBULK_IN);
} else {
stallEndpoint(EPBULK_OUT);
}
csw.Status = CSW_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(EPINT_IN, MAX_PACKET_SIZE_EPINT);
addEndpoint(EPINT_OUT, MAX_PACKET_SIZE_EPINT);
addEndpoint(EPBULK_IN, MAX_PACKET_SIZE_EPBULK);
addEndpoint(EPBULK_OUT, MAX_PACKET_SIZE_EPBULK);
readStart(EPBULK_OUT, MAX_PACKET_SIZE_EPBULK);
readStart(EPINT_OUT, MAX_PACKET_SIZE_EPINT);
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[] = {
0x1e, //bLength
STRING_DESCRIPTOR, //bDescriptorType 0x03
'M',0,'b',0,'e',0,'d',0,' ',0,'M',0,'S',0,'D', 0, ' ', 0, '&', 0, ' ', 0, 'H', 0, 'I', 0, 'D', 0 //bString iProduct - Mbed Audio
};
return stringIproductDescriptor;
}
uint8_t * USBMSD::reportDesc() {
static uint8_t reportDescriptor[] = {
0x06, LSB(0xFFAB), MSB(0xFFAB),
0x0A, LSB(0x0200), MSB(0x0200),
0xA1, 0x01, // Collection 0x01
0x75, 0x08, // report size = 8 bits
0x15, 0x00, // logical minimum = 0
0x26, 0xFF, 0x00, // logical maximum = 255
0x95, 64, // report count
0x09, 0x01, // usage
0x81, 0x02, // Input (array)
0x95, 64, // report count
0x09, 0x02, // usage
0x91, 0x02, // Output (array)
0xC0 // end collection
};
reportLength = sizeof(reportDescriptor);
return reportDescriptor;
}
#define TOTAL_DESCRIPTOR_LENGTH ((1 * CONFIGURATION_DESCRIPTOR_LENGTH) \
+ (2 * INTERFACE_DESCRIPTOR_LENGTH) \
+ (1 * HID_DESCRIPTOR_LENGTH) \
+ (4 * ENDPOINT_DESCRIPTOR_LENGTH))
uint8_t * USBMSD::configurationDesc() {
static uint8_t configDescriptor[] = {
CONFIGURATION_DESCRIPTOR_LENGTH,// bLength
CONFIGURATION_DESCRIPTOR, // bDescriptorType
LSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (LSB)
MSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (MSB)
0x02, // bNumInterfaces (2 interfaces)
DEFAULT_CONFIGURATION, // bConfigurationValue
0x00, // iConfiguration
C_RESERVED | C_SELF_POWERED, // bmAttributes
C_POWER(0), // bMaxPower
// interface 1: HID
INTERFACE_DESCRIPTOR_LENGTH, // bLength
INTERFACE_DESCRIPTOR, // bDescriptorType
0x00, // bInterfaceNumber
0x00, // bAlternateSetting
0x02, // bNumEndpoints
HID_CLASS, // bInterfaceClass
HID_SUBCLASS_NONE, // bInterfaceSubClass
HID_PROTOCOL_NONE, // bInterfaceProtocol
0x00, // iInterface
HID_DESCRIPTOR_LENGTH, // bLength
HID_DESCRIPTOR, // bDescriptorType
LSB(HID_VERSION_1_11), // bcdHID (LSB)
MSB(HID_VERSION_1_11), // bcdHID (MSB)
0x00, // bCountryCode
0x01, // bNumDescriptors
REPORT_DESCRIPTOR, // bDescriptorType
LSB(this->reportDescLength()), // wDescriptorLength (LSB)
MSB(this->reportDescLength()), // wDescriptorLength (MSB)
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
PHY_TO_DESC(EPINT_IN), // bEndpointAddress
E_INTERRUPT, // bmAttributes
LSB(MAX_PACKET_SIZE_EPINT), // wMaxPacketSize (LSB)
MSB(MAX_PACKET_SIZE_EPINT), // wMaxPacketSize (MSB)
10, // bInterval (milliseconds)
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
PHY_TO_DESC(EPINT_OUT), // bEndpointAddress
E_INTERRUPT, // bmAttributes
LSB(MAX_PACKET_SIZE_EPINT), // wMaxPacketSize (LSB)
MSB(MAX_PACKET_SIZE_EPINT), // wMaxPacketSize (MSB)
10, // bInterval (milliseconds)
// Interface 1, Alternate Setting 0, MSC Class
9, // bLength
4, // bDescriptorType
0x01, // bInterfaceNumber
0x00, // bAlternateSetting
0x02, // bNumEndpoints
0x08, // bInterfaceClass
0x06, // bInterfaceSubClass
0x50, // bInterfaceProtocol
0x00, // 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;
}