USB mass storage driver

This commit is contained in:
Thomas Saunders 2012-09-14 11:35:52 +01:00
commit 89a5f95a00
3 changed files with 848 additions and 0 deletions

0
README.md Normal file
View File

707
mass_storage/usb_msd.c Normal file
View File

@ -0,0 +1,707 @@
#include "ch.h"
#include "hal.h"
#include "usb_msd.h"
static WORKING_AREA(waMassStorage, 1024);
static msg_t MassStorageThd(void *arg);
static Thread *msdThd = NULL;
/* TODO: need a way of specifying the size of this */
static uint8_t rw_buf[512];
inline uint32_t swap_uint32( uint32_t val ) {
val = ((val << 8) & 0xFF00FF00 ) | ((val >> 8) & 0xFF00FF );
return ((val << 16) & 0xFFFF0000) | ((val >> 16) & 0x0000FFFF);
}
#define swap_uint16(x) ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))
#if !defined(MSD_RW_LED_ON)
#define MSD_RW_LED_ON()
#endif
#if !defined(MSD_RW_LED_OFF)
#define MSD_RW_LED_OFF()
#endif
/*===========================================================================*/
/* USB related stuff. */
/*===========================================================================*/
/*
* USB Driver structure.
*/
static USBMassStorageDriver UMSD1;
/*
* USB Device Descriptor.
*/
static const uint8_t msd_device_descriptor_data[18] = {
USB_DESC_DEVICE (0x0200, /* bcdUSB (2.0). */
0x00, /* bDeviceClass (None). */
0x00, /* bDeviceSubClass. */
0x00, /* bDeviceProtocol. */
0x40, /* Control Endpoint Size. */
0x0483, /* idVendor (ST). */
0x5742, /* idProduct. */
0x0100, /* bcdDevice. */
1, /* iManufacturer. */
2, /* iProduct. */
3, /* iSerialNumber. */
1) /* bNumConfigurations. */
};
/*
* Device Descriptor wrapper.
*/
static const USBDescriptor msd_device_descriptor = {
sizeof msd_device_descriptor_data,
msd_device_descriptor_data
};
/* Configuration Descriptor tree for a CDC.*/
static const uint8_t msd_configuration_descriptor_data[] = {
/* Configuration Descriptor.*/
USB_DESC_CONFIGURATION(0x0020, /* wTotalLength. */
0x01, /* bNumInterfaces. */
0x01, /* bConfigurationValue. */
0, /* iConfiguration. */
0xC0, /* bmAttributes (self powered). */
0x32), /* bMaxPower (100mA). */
/* Interface Descriptor.*/
USB_DESC_INTERFACE (0x00, /* bInterfaceNumber. */
0x00, /* bAlternateSetting. */
0x02, /* bNumEndpoints. */
0x08, /* bInterfaceClass (Mass Storage) 0x08 */
0x06, /* bInterfaceSubClass (SCSI
Transparent storage class) */
0x50, /* bInterfaceProtocol (Bulk Only) */
0), /* iInterface. (none) */
/* Mass Storage Data In Endpoint Descriptor.*/
USB_DESC_ENDPOINT (USB_MS_DATA_EP|0x80,
0x02, /* bmAttributes (Bulk). */
USB_MS_EP_SIZE,/* wMaxPacketSize. */
0x05), /* bInterval. 1ms */
/* Mass Storage Data In Endpoint Descriptor.*/
USB_DESC_ENDPOINT (USB_MS_DATA_EP,
0x02, /* bmAttributes (Bulk). */
USB_MS_EP_SIZE,/* wMaxPacketSize. */
0x05) /* bInterval. 1ms */
};
/*
* Configuration Descriptor wrapper.
*/
static const USBDescriptor msd_configuration_descriptor = {
sizeof msd_configuration_descriptor_data,
msd_configuration_descriptor_data
};
/*
* U.S. English language identifier.
*/
static const uint8_t msd_string0[] = {
USB_DESC_BYTE(4), /* bLength. */
USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */
USB_DESC_WORD(0x0409) /* wLANGID (U.S. English). */
};
/*
* Vendor string.
*/
static const uint8_t msd_string1[] = {
USB_DESC_BYTE(38), /* bLength. */
USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */
'S', 0, 'T', 0, 'M', 0, 'i', 0, 'c', 0, 'r', 0, 'o', 0, 'e', 0,
'l', 0, 'e', 0, 'c', 0, 't', 0, 'r', 0, 'o', 0, 'n', 0, 'i', 0,
'c', 0, 's', 0
};
/*
* Device Description string.
*/
static const uint8_t msd_string2[] = {
USB_DESC_BYTE(62), /* bLength. */
USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */
'C', 0, 'h', 0, 'i', 0, 'b', 0, 'i', 0, 'O', 0, 'S', 0, '/', 0,
'R', 0, 'T', 0, ' ', 0, 'M', 0, 'a', 0, 's', 0, 's', 0, ' ', 0,
'S', 0, 't', 0, 'o', 0, 'r', 0, 'a', 0, 'g', 0, 'e', 0, ' ', 0,
'D', 0, 'e', 0, 'v', 0, 'i', 0, 'c', 0, 'e'
};
static const uint8_t msd_string3[] = {
USB_DESC_BYTE(26), /* bLength. */
USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */
'A', 0, 'E', 0, 'C', 0, 'C', 0, 'E', 0, 'C', 0, 'C', 0, 'C', 0, 'C', 0,
'0' + CH_KERNEL_MAJOR, 0,
'0' + CH_KERNEL_MINOR, 0,
'0' + CH_KERNEL_PATCH, 0
};
/*
* Strings wrappers array.
*/
static const USBDescriptor msd_strings[] = {
{sizeof msd_string0, msd_string0},
{sizeof msd_string1, msd_string1},
{sizeof msd_string2, msd_string2},
{sizeof msd_string3, msd_string3}
};
/*
* Handles the GET_DESCRIPTOR callback. All required descriptors must be
* handled here.
*/
static const USBDescriptor *get_descriptor(USBDriver *usbp,
uint8_t dtype,
uint8_t dindex,
uint16_t lang) {
(void)usbp;
(void)lang;
switch (dtype) {
case USB_DESCRIPTOR_DEVICE:
return &msd_device_descriptor;
case USB_DESCRIPTOR_CONFIGURATION:
return &msd_configuration_descriptor;
case USB_DESCRIPTOR_STRING:
if (dindex < 4)
return &msd_strings[dindex];
}
return NULL;
}
void msdUsbEvent(USBDriver *usbp, usbep_t ep) {
(void)usbp;
(void)ep;
chSysLockFromIsr();
chSchReadyI(msdThd);
chSysUnlockFromIsr();
}
/**
* @brief Default requests hook.
*
* @param[in] usbp pointer to the @p USBDriver object
* @return The hook status.
* @retval TRUE Message handled internally.
* @retval FALSE Message not handled.
*/
bool_t msdRequestsHook(USBDriver *usbp) {
if (((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) &&
((usbp->setup[0] & USB_RTYPE_RECIPIENT_MASK) == USB_RTYPE_RECIPIENT_INTERFACE)) {
/* check that the request is for interface 0.*/
if(MSD_SETUP_INDEX(usbp->setup) != 0)
return FALSE;
/* act depending on bRequest = setup[1] */
switch(usbp->setup[1]) {
case MSD_REQ_RESET:
/* check that it is a HOST2DEV request */
if(((usbp->setup[0] & USB_RTYPE_DIR_MASK) != USB_RTYPE_DIR_HOST2DEV) ||
(MSD_SETUP_LENGTH(usbp->setup) != 0) ||
(MSD_SETUP_VALUE(usbp->setup) != 0))
return FALSE;
/* reset all endpoints */
/* TODO!*/
/* The device shall NAK the status stage of the device request until
* the Bulk-Only Mass Storage Reset is complete.
*/
return TRUE;
case MSD_GET_MAX_LUN:
/* check that it is a DEV2HOST request */
if(((usbp->setup[0] & USB_RTYPE_DIR_MASK) != USB_RTYPE_DIR_DEV2HOST) ||
(MSD_SETUP_LENGTH(usbp->setup) != 1) ||
(MSD_SETUP_VALUE(usbp->setup) != 0))
return FALSE;
/* stall to indicate that we don't support LUN */
//usbSetupTransfer(usbp, len_buf, 1, NULL);
return FALSE;
default:
return FALSE;
break;
}
}
return FALSE;
}
/**
* @brief IN EP1 state.
*/
static USBInEndpointState ep1InState, ep1OutState;
/**
* @brief EP1 initialization structure (IN only).
*/
static const USBEndpointConfig epDataConfig = {
USB_EP_MODE_TYPE_BULK,
NULL,
msdUsbEvent,
msdUsbEvent,
USB_MS_EP_SIZE,
USB_MS_EP_SIZE,
&ep1InState,
&ep1OutState,
1,
NULL
};
/*
* Handles the USB driver global events.
*/
static void usb_event(USBDriver *usbp, usbevent_t event) {
USBMassStorageDriver *msdp = (USBMassStorageDriver *)usbp->param;
switch (event) {
case USB_EVENT_RESET:
return;
case USB_EVENT_ADDRESS:
return;
case USB_EVENT_CONFIGURED:
chSysLockFromIsr();
usbInitEndpointI(usbp, USB_MS_DATA_EP, &epDataConfig);
chSysUnlockFromIsr();
/* initialise the thread */
chSysLock();
chSchReadyI(msdThd);
chSysUnlock();
return;
case USB_EVENT_SUSPEND:
return;
case USB_EVENT_WAKEUP:
return;
case USB_EVENT_STALLED:
return;
}
return;
}
static const USBConfig msd_usb_config = {
usb_event,
get_descriptor,
msdRequestsHook,
NULL
};
static void WaitForISR(void) {
/* sleep until it completes */
chSysLock();
chSchGoSleepS(THD_STATE_SUSPENDED);
chSysUnlock();
}
static inline void SCSISetSense(USBMassStorageDriver *msdp, uint8_t key, uint8_t acode, uint8_t aqual) {
msdp->sense.byte[2] = key;
msdp->sense.byte[12] = acode;
msdp->sense.byte[13] = aqual;
}
bool_t SCSICommandInquiry(USBMassStorageDriver *msdp) {
msd_cbw_t *cbw = &(msdp->cbw);
static const scsi_inquiry_response_t inquiry = {
0x00, // direct access block device
0x80, // removable
0x04, // SPC-2
0x02, // response data format
0x20, // response has 0x20 + 4 bytes
0x00,
0x00,
0x00,
"Chibios",
"Mass Storage",
{'v',CH_KERNEL_MAJOR+'0','.',CH_KERNEL_MINOR+'0'},
};
if((cbw->scsi_cmd_data[1] & ((1 << 0) | (1 << 1))) ||
cbw->scsi_cmd_data[2]) {
/* Optional but unsupported bits set - update the SENSE key and fail the request */
SCSISetSense( msdp,
SCSI_SENSE_KEY_ILLEGAL_REQUEST,
SCSI_ASENSE_INVALID_FIELD_IN_CDB,
SCSI_ASENSEQ_NO_QUALIFIER);
return FALSE;
}
usbPrepareTransmit(msdp->usbp, USB_MS_DATA_EP, (uint8_t *)&inquiry,
sizeof(scsi_inquiry_response_t));
chSysLock();
usbStartTransmitI(msdp->usbp, USB_MS_DATA_EP);
chSysUnlock();
msdp->result = TRUE;
/* wait for ISR */
return TRUE;
}
bool_t SCSICommandRequestSense(USBMassStorageDriver *msdp) {
usbPrepareTransmit(msdp->usbp, USB_MS_DATA_EP, (uint8_t *)&msdp->sense,
sizeof(scsi_sense_response_t));
chSysLock();
usbStartTransmitI(msdp->usbp, USB_MS_DATA_EP);
chSysUnlock();
msdp->result = TRUE;
/* wait for ISR */
return TRUE;
}
bool_t SCSICommandReadCapacity10(USBMassStorageDriver *msdp) {
static SCSIReadCapacity10Response_t response;
response.block_size = swap_uint32(msdp->block_dev_info.blk_size);
response.last_block_addr = swap_uint32(msdp->block_dev_info.blk_num-1);
usbPrepareTransmit(msdp->usbp, USB_MS_DATA_EP, (uint8_t *)&response,
sizeof(SCSIReadCapacity10Response_t));
chSysLock();
usbStartTransmitI(msdp->usbp, USB_MS_DATA_EP);
chSysUnlock();
msdp->result = TRUE;
/* wait for ISR */
return TRUE;
}
bool_t SCSICommandSendDiagnostic(USBMassStorageDriver *msdp) {
msd_cbw_t *cbw = &(msdp->cbw);
if(!cbw->scsi_cmd_data[1] & (1 << 2)) {
/* Only self-test supported - update SENSE key and fail the command */
SCSISetSense( msdp,
SCSI_SENSE_KEY_ILLEGAL_REQUEST,
SCSI_ASENSE_INVALID_FIELD_IN_CDB,
SCSI_ASENSEQ_NO_QUALIFIER);
return FALSE;
}
/* TODO: actually perform the test */
msdp->result = TRUE;
/* don't wait for ISR */
return FALSE;
}
bool_t SCSICommandStartReadWrite10(USBMassStorageDriver *msdp) {
msd_cbw_t *cbw = &(msdp->cbw);
if((cbw->scsi_cmd_data[0] == SCSI_CMD_WRITE_10) &&
blkIsWriteProtected(msdp->bbdp)) {
/* device is write protected and a write has been issued */
/* Block address is invalid, update SENSE key and return command fail */
SCSISetSense( msdp,
SCSI_SENSE_KEY_DATA_PROTECT,
SCSI_ASENSE_WRITE_PROTECTED,
SCSI_ASENSEQ_NO_QUALIFIER);
msdp->result = FALSE;
return FALSE;
}
uint32_t rw_block_address = swap_uint32(*(uint32_t *)&cbw->scsi_cmd_data[2]);
uint16_t total = swap_uint16(*(uint16_t *)&cbw->scsi_cmd_data[7]);
uint16_t i = 0;
if(rw_block_address >= msdp->block_dev_info.blk_num) {
/* Block address is invalid, update SENSE key and return command fail */
SCSISetSense( msdp,
SCSI_SENSE_KEY_DATA_PROTECT,
SCSI_ASENSE_WRITE_PROTECTED,
SCSI_ASENSEQ_NO_QUALIFIER);
msdp->result = FALSE;
/* don't wait for ISR */
return FALSE;
}
/* set state according to read / write */
msdp->state = (cbw->scsi_cmd_data[0] == SCSI_CMD_WRITE_10) ? writing : reading;
if(msdp->state == writing) {
/* loop over each block */
for(i = 0; i < total; i++) {
/* request a usb read into rw_buf */
usbPrepareReceive(msdp->usbp, USB_MS_DATA_EP, rw_buf,
msdp->block_dev_info.blk_size);
chSysLock();
usbStartReceiveI(msdp->usbp, USB_MS_DATA_EP);
chSysUnlock();
WaitForISR();
/* now write the block to the block device */
if(blkWrite(msdp->bbdp, rw_block_address++, rw_buf, 1) == CH_FAILED) {
/* TODO: handle this */
chSysHalt();
}
}
} else {
/* loop over each block */
for(i = 0; i < total; i++) {
/* read */
if(blkRead(msdp->bbdp, rw_block_address++, rw_buf, 1) == CH_FAILED) {
/* TODO: handle this */
chSysHalt();
}
/* transmit the block */
usbPrepareTransmit(msdp->usbp, USB_MS_DATA_EP, rw_buf,
msdp->block_dev_info.blk_size);
chSysLock();
usbStartTransmitI(msdp->usbp, USB_MS_DATA_EP);
chSysUnlock();
WaitForISR();
}
}
msdp->result = TRUE;
/* don't wait for ISR */
return FALSE;
}
bool_t SCSICommandModeSense6(USBMassStorageDriver *msdp) {
/* Send an empty header response with the Write Protect flag status */
/* TODO set byte3 to 0x80 if disk is read only */
static uint8_t response[4] = {0x00, 0x00, 0x00, 0x00};
usbPrepareTransmit(msdp->usbp, USB_MS_DATA_EP, response, 4);
chSysLock();
usbStartTransmitI(msdp->usbp, USB_MS_DATA_EP);
chSysUnlock();
msdp->result = TRUE;
/* wait for ISR */
return TRUE;
}
bool_t msdWaitForCommandBlock(USBMassStorageDriver *msdp) {
usbPrepareReceive(msdp->usbp, USB_MS_DATA_EP,
(uint8_t *)&msdp->cbw, sizeof(msd_cbw_t));
chSysLock();
usbStartReceiveI(msdp->usbp, USB_MS_DATA_EP);
chSysUnlock();
msdp->state = read_cmd_block;
/* wait for ISR */
return TRUE;
}
/* A command block has been received */
bool_t msdReadCommandBlock(USBMassStorageDriver *msdp) {
msd_cbw_t *cbw = &(msdp->cbw);
/*if(msdp->outState->rxcnt == 0)
return TRUE;*/
/* check the command */
if((cbw->signature != MSD_CBW_SIGNATURE) ||
(cbw->lun > 0) ||
((cbw->data_len > 0) && (cbw->flags & 0x1F)) ||
(cbw->scsi_cmd_len == 0) ||
(cbw->scsi_cmd_len > 16)) {
/* stall both IN and OUT endpoints */
chSysLockFromIsr();
usbStallReceiveI(msdp->usbp, USB_MS_DATA_EP);
chSysUnlockFromIsr();
/* don't wait for ISR */
return FALSE;
}
/* make sure that we've already read at least the full packet length (the host might've sent more than was required) */
/*if(msdp->outState->rxcnt < (sizeof(msd_cbw_t) - 16 + cbw->scsi_cmd_len))
chSysHalt();*/
bool_t sleep = FALSE;
switch(cbw->scsi_cmd_data[0]) {
case SCSI_CMD_INQUIRY:
sleep = SCSICommandInquiry(msdp);
break;
case SCSI_CMD_REQUEST_SENSE:
sleep = SCSICommandRequestSense(msdp);
break;
case SCSI_CMD_READ_CAPACITY_10:
sleep = SCSICommandReadCapacity10(msdp);
break;
case SCSI_CMD_READ_10:
case SCSI_CMD_WRITE_10:
MSD_RW_LED_ON();
sleep = SCSICommandStartReadWrite10(msdp);
MSD_RW_LED_OFF();
break;
case SCSI_CMD_SEND_DIAGNOSTIC:
sleep = SCSICommandSendDiagnostic(msdp);
break;
case SCSI_CMD_TEST_UNIT_READY:
msdp->result = TRUE;
break;
case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
msdp->result = TRUE;
break;
case SCSI_CMD_VERIFY_10:
/* don't handle */
msdp->result = TRUE;
break;
case SCSI_CMD_MODE_SENSE_6:
sleep = SCSICommandModeSense6(msdp);
break;
default:
SCSISetSense( msdp,
SCSI_SENSE_KEY_ILLEGAL_REQUEST,
SCSI_ASENSE_INVALID_COMMAND,
SCSI_ASENSEQ_NO_QUALIFIER);
/* stall IN endpoint */
chSysLockFromIsr();
usbStallTransmitI(msdp->usbp, USB_MS_DATA_EP);
chSysUnlockFromIsr();
msdp->state = idle;
cbw->data_len = 0;
return FALSE;
}
cbw->data_len = 0;
msdp->state = send_csw;
if(msdp->result) {
/* update sense with success status */
SCSISetSense( msdp,
SCSI_SENSE_KEY_GOOD,
SCSI_ASENSE_NO_ADDITIONAL_INFORMATION,
SCSI_ASENSEQ_NO_QUALIFIER);
} else {
/* stall IN endpoint */
chSysLockFromIsr();
usbStallTransmitI(msdp->usbp, USB_MS_DATA_EP);
chSysUnlockFromIsr();
msdp->state = idle;
cbw->data_len = 0;
return FALSE;
}
return sleep;
}
bool_t msdSendCSW(USBMassStorageDriver *msdp) {
msd_cbw_t *cbw = &(msdp->cbw);
msd_csw_t *csw = &(msdp->csw);
if(!msdp->result && cbw->data_len) {
/* still bytes left to send, this is too early to send CSW? */
chSysLockFromIsr();
usbStallReceiveI(msdp->usbp, USB_MS_DATA_EP);
usbStallTransmitI(msdp->usbp, USB_MS_DATA_EP);
chSysUnlockFromIsr();
return TRUE;
}
csw->status = (msdp->result) ? MSD_COMMAND_PASSED : MSD_COMMAND_FAILED;
csw->signature = MSD_CSW_SIGNATURE;
csw->data_residue = cbw->data_len;
csw->tag = cbw->tag;
usbPrepareTransmit(msdp->usbp, USB_MS_DATA_EP, (uint8_t *)csw,
sizeof(msd_csw_t));
chSysLock();
usbStartTransmitI(msdp->usbp, USB_MS_DATA_EP);
chSysUnlock();
msdp->state = idle;
return TRUE;
}
static msg_t MassStorageThd(void *arg) {
USBMassStorageDriver *msdp = (USBMassStorageDriver *)arg;
chRegSetThreadName("USB-MSD");
bool_t wait_for_isr = FALSE;
WaitForISR();
while (TRUE) {
wait_for_isr = FALSE;
/* wait on data depending on the current state */
switch(msdp->state) {
case idle:
wait_for_isr = msdWaitForCommandBlock(msdp);
break;
case read_cmd_block:
wait_for_isr = msdReadCommandBlock(msdp);
break;
case send_csw:
wait_for_isr = msdSendCSW(msdp);
break;
default:
break;
}
if(!wait_for_isr)
continue;
/* wait until the ISR wakes thread */
WaitForISR();
}
return 0;
}
void msdInit(USBDriver *usbp, BaseBlockDevice *bbdp) {
uint8_t i;
UMSD1.usbp = usbp;
UMSD1.state = idle;
UMSD1.bbdp = bbdp;
/* initialise sense values to zero */
for(i = 0; i < sizeof(scsi_sense_response_t); i++)
UMSD1.sense.byte[i] = 0x00;
/* Response code = 0x70, additional sense length = 0x0A */
UMSD1.sense.byte[0] = 0x70;
UMSD1.sense.byte[7] = 0x0A;
/* make sure block device is working and get info */
while(TRUE) {
blkstate_t state = blkGetDriverState(bbdp);
if(state == BLK_READY)
break;
chThdSleepMilliseconds(50);
}
blkGetInfo(bbdp, &UMSD1.block_dev_info);
usbDisconnectBus(UMSD1.usbp);
chThdSleepMilliseconds(1000);
UMSD1.usbp->param = &UMSD1;
usbStart(UMSD1.usbp, &msd_usb_config);
usbConnectBus(UMSD1.usbp);
msdThd = chThdCreateStatic(waMassStorage, sizeof(waMassStorage), NORMALPRIO, MassStorageThd, &UMSD1);
}

141
mass_storage/usb_msd.h Normal file
View File

@ -0,0 +1,141 @@
#define USB_MS_DATA_EP 1
/*
#define USB_MS_DATA_IN_EP 1
#define USB_MS_DATA_OUT_EP 2*/
#define USB_MS_EP_SIZE 64
#define MSD_REQ_RESET 0xFF
#define MSD_GET_MAX_LUN 0xFE
#define MSD_CBW_SIGNATURE 0x43425355
#define MSD_CSW_SIGNATURE 0x53425355
/** Mask for a Command Block Wrapper's flags attribute to specify a command with data sent from host-to-device. */
#define MSD_COMMAND_DIR_DATA_OUT (0 << 7)
/** Mask for a Command Block Wrapper's flags attribute to specify a command with data sent from device-to-host. */
#define MSD_COMMAND_DIR_DATA_IN (1 << 7)
#define MSD_SETUP_WORD(setup, index) (uint16_t)(((uint16_t)setup[index+1] << 8) | (setup[index] & 0x00FF))
#define MSD_SETUP_VALUE(setup) MSD_SETUP_WORD(setup, 2)
#define MSD_SETUP_INDEX(setup) MSD_SETUP_WORD(setup, 4)
#define MSD_SETUP_LENGTH(setup) MSD_SETUP_WORD(setup, 6)
#define SCSI_CMD_INQUIRY 0x12
#define SCSI_CMD_REQUEST_SENSE 0x03
#define SCSI_CMD_READ_CAPACITY_10 0x25
#define SCSI_CMD_READ_10 0x28
#define SCSI_CMD_WRITE_10 0x2A
#define SCSI_CMD_TEST_UNIT_READY 0x00
#define SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1E
#define SCSI_CMD_VERIFY_10 0x2F
#define SCSI_CMD_SEND_DIAGNOSTIC 0x1D
#define SCSI_CMD_MODE_SENSE_6 0x1A
#define MSD_COMMAND_PASSED 0x00
#define MSD_COMMAND_FAILED 0x01
#define MSD_COMMAND_PHASE_ERROR 0x02
#define SCSI_SENSE_KEY_GOOD 0x00
#define SCSI_SENSE_KEY_RECOVERED_ERROR 0x01
#define SCSI_SENSE_KEY_NOT_READY 0x02
#define SCSI_SENSE_KEY_MEDIUM_ERROR 0x03
#define SCSI_SENSE_KEY_HARDWARE_ERROR 0x04
#define SCSI_SENSE_KEY_ILLEGAL_REQUEST 0x05
#define SCSI_SENSE_KEY_UNIT_ATTENTION 0x06
#define SCSI_SENSE_KEY_DATA_PROTECT 0x07
#define SCSI_SENSE_KEY_BLANK_CHECK 0x08
#define SCSI_SENSE_KEY_VENDOR_SPECIFIC 0x09
#define SCSI_SENSE_KEY_COPY_ABORTED 0x0A
#define SCSI_SENSE_KEY_ABORTED_COMMAND 0x0B
#define SCSI_SENSE_KEY_VOLUME_OVERFLOW 0x0D
#define SCSI_SENSE_KEY_MISCOMPARE 0x0E
#define SCSI_ASENSE_NO_ADDITIONAL_INFORMATION 0x00
#define SCSI_ASENSE_LOGICAL_UNIT_NOT_READY 0x04
#define SCSI_ASENSE_INVALID_FIELD_IN_CDB 0x24
#define SCSI_ASENSE_NOT_READY_TO_READY_CHANGE 0x28
#define SCSI_ASENSE_WRITE_PROTECTED 0x27
#define SCSI_ASENSE_FORMAT_ERROR 0x31
#define SCSI_ASENSE_INVALID_COMMAND 0x20
#define SCSI_ASENSE_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x21
#define SCSI_ASENSE_MEDIUM_NOT_PRESENT 0x3A
#define SCSI_ASENSEQ_NO_QUALIFIER 0x00
#define SCSI_ASENSEQ_FORMAT_COMMAND_FAILED 0x01
#define SCSI_ASENSEQ_INITIALIZING_COMMAND_REQUIRED 0x02
#define SCSI_ASENSEQ_OPERATION_IN_PROGRESS 0x07
typedef struct {
/**
* @brief USB driver to use.
*/
USBDriver *usbp;
/**
* @brief USB driver configuration structure.
*/
USBConfig usb_config;
} USBMassStorageDeviceConfig;
PACK_STRUCT_BEGIN typedef struct {
uint32_t signature;
uint32_t tag;
uint32_t data_len;
uint8_t flags;
uint8_t lun;
uint8_t scsi_cmd_len;
uint8_t scsi_cmd_data[16];
} PACK_STRUCT_STRUCT msd_cbw_t PACK_STRUCT_END;
PACK_STRUCT_BEGIN typedef struct {
uint32_t signature;
uint32_t tag;
uint32_t data_residue;
uint8_t status;
} PACK_STRUCT_STRUCT msd_csw_t PACK_STRUCT_END;
typedef struct {
uint8_t byte[18];
} __attribute__ ((packed)) scsi_sense_response_t;
PACK_STRUCT_BEGIN typedef struct
{
uint8_t peripheral;
uint8_t removable;
uint8_t version;
uint8_t response_data_format;
uint8_t additional_length;
uint8_t sccstp;
uint8_t bqueetc;
uint8_t cmdque;
uint8_t vendorID[8];
uint8_t productID[16];
uint8_t productRev[4];
} PACK_STRUCT_STRUCT scsi_inquiry_response_t PACK_STRUCT_END;
PACK_STRUCT_BEGIN typedef struct {
uint32_t last_block_addr;
uint32_t block_size;
} PACK_STRUCT_STRUCT SCSIReadCapacity10Response_t PACK_STRUCT_END;
typedef struct USBMassStorageDriver USBMassStorageDriver;
typedef enum { idle, read_cmd_block, send_csw, reading, writing} msd_state_t;
struct USBMassStorageDriver {
USBDriver *usbp;
BaseBlockDevice *bbdp;
BlockDeviceInfo block_dev_info;
msd_state_t state;
msd_cbw_t cbw;
msd_csw_t csw;
scsi_sense_response_t sense;
bool_t result;
};
#ifdef __cplusplus
extern "C" {
#endif
void msdInit(USBDriver *usbp, BaseBlockDevice *bdp);
#ifdef __cplusplus
}
#endif