mirror of https://github.com/ARMmbed/mbed-os.git
Update USBMSD
Update the USBMSD class for the new API. Add support for passing in a BlockDevice directly without the need to extend USBMSD.pull/9768/head
parent
33756e2316
commit
c08da25aa7
|
@ -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<void()> 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;
|
||||
}
|
|
@ -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<void()> 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<void()> _in_task;
|
||||
Task<void()> _out_task;
|
||||
Task<void()> _reset_task;
|
||||
Task<void(const setup_packet_t *)> _control_task;
|
||||
Task<void()> _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
|
Loading…
Reference in New Issue