diff --git a/usb/device/USBMSD/USBMSD.cpp b/usb/device/USBMSD/USBMSD.cpp new file mode 100644 index 0000000000..a8d5991b77 --- /dev/null +++ b/usb/device/USBMSD/USBMSD.cpp @@ -0,0 +1,887 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018-2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "stdint.h" +#include "USBMSD.h" +#include "EndpointResolver.h" + +#define DISK_OK 0x00 +#define NO_INIT 0x01 +#define NO_DISK 0x02 +#define WRITE_PROTECT 0x04 + +#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 packet size +#define MAX_PACKET 64 + +// CSW Status +enum Status { + CSW_PASSED, + CSW_FAILED, + CSW_ERROR, +}; + +USBMSD::USBMSD(BlockDevice *bd, USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release) + : USBDevice(phy, vendor_id, product_id, product_release), + _in_task(&_queue), _out_task(&_queue), _reset_task(&_queue), _control_task(&_queue), _configure_task(&_queue) +{ + _bd = bd; + _bd->init(); + + _in_task = callback(this, &USBMSD::_in); + _out_task = callback(this, &USBMSD::_out); + _reset_task = callback(this, &USBMSD::_reset); + _control_task = callback(this, &USBMSD::_control); + _configure_task = callback(this, &USBMSD::_configure); + + EndpointResolver resolver(endpoint_table()); + + resolver.endpoint_ctrl(64); + _bulk_in = resolver.endpoint_in(USB_EP_TYPE_BULK, MAX_PACKET); + _bulk_out = resolver.endpoint_out(USB_EP_TYPE_BULK, MAX_PACKET); + MBED_ASSERT(resolver.valid()); + + _stage = READ_CBW; + memset((void *)&_cbw, 0, sizeof(CBW)); + memset((void *)&_csw, 0, sizeof(CSW)); + _page = NULL; +} + +USBMSD::~USBMSD() +{ + disconnect(); + _bd->deinit(); +} + +bool USBMSD::connect() +{ + _mutex.lock(); + + //disk initialization + if (disk_status() & NO_INIT) { + if (disk_initialize()) { + _mutex.unlock(); + return false; + } + } + + // get number of blocks + _block_count = disk_sectors(); + + // get memory size + _memory_size = disk_size(); + + if (_block_count > 0) { + _block_size = _memory_size / _block_count; + if (_block_size != 0) { + free(_page); + _page = (uint8_t *)malloc(_block_size * sizeof(uint8_t)); + if (_page == NULL) { + _mutex.unlock(); + return false; + } + } + } else { + _mutex.unlock(); + return false; + } + + //connect the device + USBDevice::connect(); + _mutex.unlock(); + return true; +} + +void USBMSD::disconnect() +{ + _mutex.lock(); + + USBDevice::disconnect(); + + _in_task.cancel(); + _out_task.cancel(); + _reset_task.cancel(); + _control_task.cancel(); + _configure_task.cancel(); + + _in_task.wait(); + _out_task.wait(); + _reset_task.wait(); + _control_task.wait(); + _configure_task.wait(); + + //De-allocate MSD page size: + free(_page); + _page = NULL; + + _mutex.unlock(); +} + +bool USBMSD::ready() +{ + return configured(); +} + +void USBMSD::process() +{ + _queue.dispatch(); +} + +void USBMSD::attach(mbed::Callback cb) +{ + lock(); + + _queue.attach(cb); + + unlock(); +} + +int USBMSD::disk_read(uint8_t* data, uint64_t block, uint8_t count) +{ + bd_addr_t addr = block * _bd->get_erase_size(); + bd_size_t size = count * _bd->get_erase_size(); + return _bd->read(data, addr, size); +} + +int USBMSD::disk_write(const uint8_t* data, uint64_t block, uint8_t count) +{ + bd_addr_t addr = block * _bd->get_erase_size(); + bd_size_t size = count * _bd->get_erase_size(); + int ret = _bd->erase(addr, size); + if (ret != 0) { + return ret; + } + + return _bd->program(data, addr, size); +} + +int USBMSD::disk_initialize() +{ + return 0; +} + +uint64_t USBMSD::disk_sectors() +{ + return _bd->size() / _bd->get_erase_size(); +} + +uint64_t USBMSD::disk_size() +{ + return _bd->size(); +} + + +int USBMSD::disk_status() +{ + return 0; +} + +void USBMSD::_isr_out(usb_ep_t endpoint) +{ + _out_task.call(); +} + +void USBMSD::_isr_in(usb_ep_t endpoint) +{ + _in_task.call(); +} + +void USBMSD::callback_state_change(DeviceState new_state) +{ + // called in ISR context + + if (new_state != Configured) { + _reset_task.call(); + } +} + +void USBMSD::callback_request(const setup_packet_t *setup) +{ + // called in ISR context + + if (setup->bmRequestType.Type == CLASS_TYPE) { + _control_task.call(setup); + } else { + complete_request(PassThrough, NULL, 0); + } +} + +void USBMSD::callback_request_xfer_done(const setup_packet_t *setup, bool aborted) +{ + // called in ISR context + + bool success = setup->bRequest == MSC_REQUEST_GET_MAX_LUN; + complete_request_xfer_done(success); +} + +void USBMSD::callback_set_configuration(uint8_t configuration) +{ + // called in ISR context + + if (configuration != DEFAULT_CONFIGURATION) { + complete_set_configuration(false); + return; + } + _configure_task.call(); +} + +void USBMSD::callback_set_interface(uint16_t interface, uint8_t alternate) +{ + // called in ISR context + + bool success = (interface == 0) && (alternate == 0); + complete_set_interface(success); +} + + +const uint8_t *USBMSD::string_iinterface_desc() +{ + static const uint8_t string_iinterface_descriptor[] = { + 0x08, //bLength + STRING_DESCRIPTOR, //bDescriptorType 0x03 + 'M', 0, 'S', 0, 'D', 0 //bString iInterface - MSD + }; + return string_iinterface_descriptor; +} + +const uint8_t *USBMSD::string_iproduct_desc() +{ + static const uint8_t string_iproduct_descriptor[] = { + 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 string_iproduct_descriptor; +} + + +const uint8_t *USBMSD::configuration_desc(uint8_t index) +{ + if (index != 0) { + return NULL; + } + + uint8_t config_descriptor_temp[] = { + + // 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 + _bulk_in, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + LSB(MAX_PACKET), // wMaxPacketSize (LSB) + MSB(MAX_PACKET), // wMaxPacketSize (MSB) + 0, // bInterval + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + _bulk_out, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + LSB(MAX_PACKET), // wMaxPacketSize (LSB) + MSB(MAX_PACKET), // wMaxPacketSize (MSB) + 0 // bInterval + }; + MBED_ASSERT(sizeof(config_descriptor_temp) == sizeof(_configuration_descriptor)); + memcpy(_configuration_descriptor, config_descriptor_temp, sizeof(_configuration_descriptor)); + return _configuration_descriptor; +} + +void USBMSD::_out() +{ + _mutex.lock(); + + _bulk_out_size = read_finish(_bulk_out); + _out_ready = true; + _process(); + + _mutex.unlock(); +} + +void USBMSD::_in() +{ + _mutex.lock(); + + write_finish(_bulk_in); + _in_ready = true; + _process(); + + _mutex.unlock(); +} + +void USBMSD::_reset() +{ + _mutex.lock(); + + msd_reset(); + + _mutex.unlock(); +} + +void USBMSD::_control(const setup_packet_t *setup) +{ + _mutex.lock(); + + static const uint8_t maxLUN[1] = {0}; + + RequestResult result = PassThrough; + uint8_t *data = NULL; + uint32_t size = 0; + + if (setup->bmRequestType.Type == CLASS_TYPE) { + switch (setup->bRequest) { + case MSC_REQUEST_RESET: + result = Success; + msd_reset(); + break; + case MSC_REQUEST_GET_MAX_LUN: + result = Send; + data = (uint8_t*)maxLUN; + size = 1; + break; + default: + break; + } + } + + complete_request(result, data, size); + + _mutex.unlock(); +} + +void USBMSD::_configure() +{ + _mutex.lock(); + + // Configure endpoints > 0 + endpoint_add(_bulk_in, MAX_PACKET, USB_EP_TYPE_BULK, &USBMSD::_isr_in); + endpoint_add(_bulk_out, MAX_PACKET, USB_EP_TYPE_BULK, &USBMSD::_isr_out); + MBED_ASSERT(sizeof(_bulk_out_buf) == MAX_PACKET); + MBED_ASSERT(sizeof(_bulk_in_buf) == MAX_PACKET); + + _out_ready = false; + _in_ready = true; + + //activate readings + read_start(_bulk_out, _bulk_out_buf, sizeof(_bulk_out_buf)); + complete_set_configuration(true); + + _mutex.unlock(); +} + +void USBMSD::_process() +{ + // Mutex must be locked by caller + + switch (_stage) { + // the device has to decode the CBW received + case READ_CBW: + if (!_out_ready) { + break; + } + CBWDecode(_bulk_out_buf, _bulk_out_size); + _read_next(); + break; + + + case PROCESS_CBW: + switch (_cbw.CB[0]) { + // the device has to receive data from the host + case WRITE10: + case WRITE12: + if (!_out_ready) { + break; + } + memoryWrite(_bulk_out_buf, _bulk_out_size); + _read_next(); + break; + case VERIFY10: + if (!_out_ready) { + break; + } + memoryVerify(_bulk_out_buf, _bulk_out_size); + _read_next(); + break; + // the device has to send data to the host + case READ10: + case READ12: + if (!_in_ready) { + break; + } + memoryRead(); + break; + } + break; + + //the device has to send a CSW + case SEND_CSW: + if (!_in_ready) { + break; + } + sendCSW(); + break; + + // an error has occurred: stall endpoint and send CSW + default: + endpoint_stall(_bulk_out); + endpoint_stall(_bulk_in); + _csw.Status = CSW_ERROR; + sendCSW(); + break; + } +} + +void USBMSD::_write_next(uint8_t *data, uint32_t size) +{ + lock(); + + MBED_ASSERT(size <= MAX_PACKET); + MBED_ASSERT(_in_ready); + uint32_t send_size = MAX_PACKET > size ? size : MAX_PACKET; + memcpy(_bulk_in_buf, data, send_size); + write_start(_bulk_in, _bulk_in_buf, send_size); + _in_ready = false; + + unlock(); +} + +void USBMSD::_read_next() +{ + lock(); + + MBED_ASSERT(_out_ready); + read_start(_bulk_out, _bulk_out_buf, sizeof(_bulk_out_buf)); + _out_ready = false; + + unlock(); +} + +void USBMSD::memoryWrite(uint8_t *buf, uint16_t size) +{ + if ((_addr + size) > _memory_size) { + size = _memory_size - _addr; + _stage = ERROR; + endpoint_stall(_bulk_out); + } + + // we fill an array in RAM of 1 block before writing it in memory + for (int i = 0; i < size; i++) { + _page[_addr % _block_size + i] = buf[i]; + } + + // if the array is filled, write it in memory + if (!((_addr + size) % _block_size)) { + if (!(disk_status() & WRITE_PROTECT)) { + disk_write(_page, _addr / _block_size, 1); + } + } + + _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) > _memory_size) { + size = _memory_size - _addr; + _stage = ERROR; + endpoint_stall(_bulk_out); + } + + // beginning of a new block -> load a whole block in RAM + if (!(_addr % _block_size)) { + disk_read(_page, _addr / _block_size, 1); + } + + // info are in RAM -> no need to re-read memory + for (n = 0; n < size; n++) { + if (_page[_addr % _block_size + n] != buf[n]) { + _mem_ok = false; + break; + } + } + + _addr += size; + _length -= size; + _csw.DataResidue -= size; + + if (!_length || (_stage != PROCESS_CBW)) { + _csw.Status = (_mem_ok && (_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, + (uint8_t)((_block_count >> 24) & 0xff), + (uint8_t)((_block_count >> 16) & 0xff), + (uint8_t)((_block_count >> 8) & 0xff), + (uint8_t)((_block_count >> 0) & 0xff), + + 0x02, + (uint8_t)((_block_size >> 16) & 0xff), + (uint8_t)((_block_size >> 8) & 0xff), + (uint8_t)((_block_size >> 0) & 0xff), + }; + if (!write(capacity, sizeof(capacity))) { + return false; + } + return true; +} + + +bool USBMSD::readCapacity(void) +{ + uint8_t capacity[] = { + (uint8_t)(((_block_count - 1) >> 24) & 0xff), + (uint8_t)(((_block_count - 1) >> 16) & 0xff), + (uint8_t)(((_block_count - 1) >> 8) & 0xff), + (uint8_t)(((_block_count - 1) >> 0) & 0xff), + + (uint8_t)((_block_size >> 24) & 0xff), + (uint8_t)((_block_size >> 16) & 0xff), + (uint8_t)((_block_size >> 8) & 0xff), + (uint8_t)((_block_size >> 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; + + _write_next(buf, size); + + _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; + _write_next((uint8_t *)&_csw, sizeof(CSW)); + _stage = READ_CBW; +} + +bool USBMSD::requestSense(void) +{ + uint8_t request_sense[] = { + 0x70, + 0x00, + 0x05, // Sense Key: illegal request + 0x00, + 0x00, + 0x00, + 0x00, + 0x0A, + 0x00, + 0x00, + 0x00, + 0x00, + 0x30, + 0x01, + 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 { + endpoint_stall(_bulk_out); + _csw.Status = CSW_ERROR; + sendCSW(); + } + } + break; + case WRITE10: + case WRITE12: + if (infoTransfer()) { + if (!(_cbw.Flags & 0x80)) { + _stage = PROCESS_CBW; + } else { + endpoint_stall(_bulk_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; + _mem_ok = true; + } else { + endpoint_stall(_bulk_in); + _csw.Status = CSW_ERROR; + sendCSW(); + } + } + break; + case MEDIA_REMOVAL: + _csw.Status = CSW_PASSED; + sendCSW(); + break; + default: + fail(); + break; + } + } + } + } +} + +void USBMSD::testUnitReady(void) +{ + + if (_cbw.DataLength != 0) { + if ((_cbw.Flags & 0x80) != 0) { + endpoint_stall(_bulk_in); + } else { + endpoint_stall(_bulk_out); + } + } + + _csw.Status = CSW_PASSED; + sendCSW(); +} + + +void USBMSD::memoryRead(void) +{ + uint32_t n; + + n = (_length > MAX_PACKET) ? MAX_PACKET : _length; + + if ((_addr + n) > _memory_size) { + n = _memory_size - _addr; + _stage = ERROR; + } + + // we read an entire block + if (!(_addr % _block_size)) { + disk_read(_page, _addr / _block_size, 1); + } + + // write data which are in RAM + _write_next(&_page[_addr % _block_size], MAX_PACKET); + + _addr += n; + _length -= n; + + _csw.DataResidue -= n; + + if (!_length || (_stage != PROCESS_CBW)) { + _csw.Status = (_stage == PROCESS_CBW) ? CSW_PASSED : CSW_FAILED; + _stage = (_stage == PROCESS_CBW) ? SEND_CSW : _stage; + } +} + + +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 * _block_size; + + // 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 * _block_size; + + if (!_cbw.DataLength) { // host requests no data + _csw.Status = CSW_FAILED; + sendCSW(); + return false; + } + + if (_cbw.DataLength != _length) { + if ((_cbw.Flags & 0x80) != 0) { + endpoint_stall(_bulk_in); + } else { + endpoint_stall(_bulk_out); + } + + _csw.Status = CSW_FAILED; + sendCSW(); + return false; + } + + return true; +} + +void USBMSD::msd_reset() +{ + _stage = READ_CBW; +} diff --git a/usb/device/USBMSD/USBMSD.h b/usb/device/USBMSD/USBMSD.h new file mode 100644 index 0000000000..3efcd5d5dd --- /dev/null +++ b/usb/device/USBMSD/USBMSD.h @@ -0,0 +1,279 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018-2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef USBMSD_H +#define USBMSD_H + +/* These headers are included for child class. */ +#include "USBDescriptor.h" +#include "USBDevice_Types.h" +#include "platform/Callback.h" +#include "events/PolledQueue.h" +#include "events/Task.h" +#include "BlockDevice.h" +#include "Mutex.h" +#include "usb_phy_api.h" + +#include "USBDevice.h" + +/** + * USBMSD class: generic class in order to use all kinds of blocks storage chip + * + * Introduction + * + * USBMSD implements the MSD protocol. It permits to access a block device (flash, sdcard,...) + * from a computer over USB. + * + * @code + * #include "mbed.h" + * #include "SDBlockDevice.h" + * #include "USBMSD.h" + * + * SDBlockDevice sd(PTE3, PTE1, PTE2, PTE4); + * USBMSD usb(&sd); + * + * int main() { + * usb.connect(); + * + * while(true) { + * usb.process(); + * } + * + * return 0; + * } + * @endcode + */ +class USBMSD: public USBDevice { +public: + + /** + * Constructor + * + * This creates a new USBMSD object with the given block device. Connect must be called + * for the block device to connect. + * + * @param bd BlockDevice to mount as a USB drive + * @param phy USB phy to use + * @param vendor_id Your vendor_id + * @param product_id Your product_id + * @param product_release Your preoduct_release + */ + USBMSD(BlockDevice *bd, USBPhy *phy=get_usb_phy(), uint16_t vendor_id = 0x0703, uint16_t product_id = 0x0104, uint16_t product_release = 0x0001); + + /** + * Destroy this object + * + * Any classes which inherit from this class must call disconnect + * before this destructor runs. + */ + virtual ~USBMSD(); + + /** + * Connect the USB MSD device. Establish disk initialization before really connect the device. + * + * @returns true if successful + */ + bool connect(); + + /** + * Disconnect the USB MSD device. + */ + void disconnect(); + + /** + * Check if USB is connected + * + * @return true if a USB is connected, false otherwise + */ + bool ready(); + + /** + * Perform USB processing + */ + void process(); + + /** + * Called when USBMSD needs to perform processing + * + * @param cb Callback called when USBMSD needs process() to be called + */ + void attach(mbed::Callback cb); + +protected: + + /* + * read one or more blocks on a storage chip + * + * @param data pointer where will be stored read data + * @param block starting block number + * @param count number of blocks to read + * @returns 0 if successful + */ + virtual int disk_read(uint8_t *data, uint64_t block, uint8_t count); + + /* + * write one or more blocks on a storage chip + * + * @param data data to write + * @param block starting block number + * @param count number of blocks to write + * @returns 0 if successful + */ + virtual int disk_write(const uint8_t *data, uint64_t block, uint8_t count); + + /* + * Disk initilization + */ + virtual int disk_initialize(); + + /* + * Return the number of blocks + * + * @returns number of blocks + */ + virtual uint64_t disk_sectors(); + + /* + * Return memory size + * + * @returns memory size + */ + virtual uint64_t disk_size(); + + /* + * To check the status of the storage chip + * + * @returns status: 0: OK, 1: disk not initialized, 2: no medium in the drive, 4: write protected + */ + virtual int disk_status(); + +private: + + // MSC Bulk-only Stage + enum Stage { + READ_CBW, // wait a CBW + ERROR, // error + PROCESS_CBW, // process a CBW request + SEND_CSW, // send a CSW + }; + + // Bulk-only CBW + typedef MBED_PACKED(struct) { + uint32_t Signature; + uint32_t Tag; + uint32_t DataLength; + uint8_t Flags; + uint8_t LUN; + uint8_t CBLength; + uint8_t CB[16]; + } CBW; + + // Bulk-only CSW + typedef MBED_PACKED(struct) { + uint32_t Signature; + uint32_t Tag; + uint32_t DataResidue; + uint8_t Status; + } CSW; + + //state of the bulk-only state machine + Stage _stage; + + // current CBW + CBW _cbw; + + // CSW which will be sent + CSW _csw; + + // addr where will be read or written data + uint32_t _addr; + + // length of a reading or writing + uint32_t _length; + + // memory OK (after a memoryVerify) + bool _mem_ok; + + // cache in RAM before writing in memory. Useful also to read a block. + uint8_t *_page; + + int _block_size; + uint64_t _memory_size; + uint64_t _block_count; + + // endpoints + usb_ep_t _bulk_in; + usb_ep_t _bulk_out; + uint8_t _bulk_in_buf[64]; + uint8_t _bulk_out_buf[64]; + bool _out_ready; + bool _in_ready; + uint32_t _bulk_out_size; + + // Interrupt to thread deferral + PolledQueue _queue; + Task _in_task; + Task _out_task; + Task _reset_task; + Task _control_task; + Task _configure_task; + + BlockDevice *_bd; + rtos::Mutex _mutex; + + // space for config descriptor + uint8_t _configuration_descriptor[32]; + + virtual const uint8_t *string_iproduct_desc(); + virtual const uint8_t *string_iinterface_desc(); + virtual const uint8_t *configuration_desc(uint8_t index); + virtual void callback_set_configuration(uint8_t configuration); + virtual void callback_set_interface(uint16_t interface, uint8_t alternate); + virtual void callback_state_change(DeviceState new_state); + virtual void callback_request(const setup_packet_t *setup); + virtual void callback_request_xfer_done(const setup_packet_t *setup, bool aborted); + + void _isr_out(usb_ep_t endpoint); + void _isr_in(usb_ep_t endpoint); + + void _out(); + void _in(); + void _reset(); + void _control(const setup_packet_t *request); + void _configure(); + + void _process(); + void _write_next(uint8_t *data, uint32_t size); + void _read_next(); + + void CBWDecode(uint8_t *buf, uint16_t size); + void sendCSW(void); + bool inquiryRequest(void); + bool write(uint8_t *buf, uint16_t size); + bool readFormatCapacity(); + bool readCapacity(void); + bool infoTransfer(void); + void memoryRead(void); + bool modeSense6(void); + void testUnitReady(void); + bool requestSense(void); + void memoryVerify(uint8_t *buf, uint16_t size); + void memoryWrite(uint8_t *buf, uint16_t size); + void msd_reset(); + void fail(); +}; + +#endif