diff --git a/firmware/hw_layer/mass_storage/Readme.md b/firmware/hw_layer/mass_storage/Readme.md
new file mode 100644
index 0000000000..4a1c314432
--- /dev/null
+++ b/firmware/hw_layer/mass_storage/Readme.md
@@ -0,0 +1,40 @@
+https://github.com/tegesoft/Chibios-USB-Devices/
+http://www.chibios.com/forum/viewtopic.php?f=3&t=619
+
+Mass Storage Device
+===================
+
+This driver implements a USB mass storage device. It requires a Chibios block device (e.g mmc_spi or SDC)
+
+Example usage:
+--------------
+```c
+
+USBMassStorageDriver UMSD1;
+
+mmcObjectInit(&MMCD1);
+mmcStart(&MMCD1, &mmccfg);
+mmcConnect(&MMCD1);
+
+msdInit(&USBD1, &MMCD1, &UMSD1);
+```
+
+Events:
+--------------
+```c
+chEvtRegisterMask(&UMSD1.evt_connected, &listener_connected, 1);
+chEvtRegisterMask(&UMSD1.evt_ejected, &listener_ejected, 2);
+
+while(TRUE) {
+ if(chEvtWaitOneTimeout(1, TIME_IMMEDIATE)) {
+ /* drive is now connected */
+
+ /* wait until the drive is ejected */
+ chEvtWaitOne(2);
+
+ /* drive is now ejected. do something */
+ }
+
+ chThdSleepMilliseconds(1000);
+}
+```
\ No newline at end of file
diff --git a/firmware/hw_layer/mass_storage/usb_msd.c b/firmware/hw_layer/mass_storage/usb_msd.c
new file mode 100644
index 0000000000..c50dbd0120
--- /dev/null
+++ b/firmware/hw_layer/mass_storage/usb_msd.c
@@ -0,0 +1,1412 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
+ 2011,2012,2013 Giovanni Di Sirio.
+
+ This file is part of ChibiOS/RT.
+
+ ChibiOS/RT is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ ChibiOS/RT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+/**
+ * @file usb_msd.c
+ * @brief USB Mass Storage Driver code.
+ *
+ * @addtogroup MSD_USB
+ * @{
+ */
+
+#define HAL_USE_MASS_STORAGE_USB true
+
+#include "ch.h"
+#include "hal.h"
+#include "usb_msd.h"
+#include "chprintf.h"
+#include "string.h"
+
+
+#if HAL_USE_MASS_STORAGE_USB || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+#define MSD_ENABLE_PERF_DEBUG_GPIOS FALSE
+#define MSD_DEBUG_NESTING FALSE
+#define MSD_DEBUG FALSE
+//#define MSD_DEBUG (palReadPad(GPIOI, GPIOI_PIN4))
+
+#define msd_debug_print(chp_arg, args ...) if (MSD_DEBUG && chp_arg != NULL ) { chprintf(chp_arg, args); }
+#define msd_debug_nest_print(chp_arg, args ...) if ( MSD_DEBUG_NESTING && chp_arg != NULL ) { chprintf(chp_arg, args); }
+#define msd_debug_err_print(chp_arg, args ...) if ( chp_arg != NULL ) { chprintf(chp_arg, args); }
+
+
+
+
+
+//#define MSD_DEBUG_SCSI_COMMAND_STATE_STRING(description) g_debug_info.msd.scsi_command_state = description;
+
+#if !defined(MSD_RW_LED_ON)
+#define MSD_RW_LED_ON()
+#endif
+
+#if !defined(MSD_RW_LED_OFF)
+#define MSD_RW_LED_OFF()
+#endif
+
+#if !defined(MSD_R_LED_ON)
+#define MSD_R_LED_ON()
+#endif
+
+#if !defined(MSD_R_LED_OFF)
+#define MSD_R_LED_OFF()
+#endif
+
+#if !defined(MSD_W_LED_ON)
+#define MSD_W_LED_ON()
+#endif
+
+#if !defined(MSD_W_LED_OFF)
+#define MSD_W_LED_OFF()
+#endif
+
+
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+
+
+static msg_t MassStorageUSBTransferThd(void *arg);
+static msg_t MassStorageThd(void *arg);
+
+static Thread *msdThd = NULL;
+static Thread *msdUSBTransferThd = NULL;
+
+#define WAIT_ISR_SUCCESS 0
+#define WAIT_ISR_BUSS_RESET_OR_RECONNECT 1
+
+static uint8_t msdWaitForISR(USBMassStorageDriver *msdp, const bool_t check_reset, const msd_wait_mode_t wait_mode);
+static void msdSetDefaultSenseKey(USBMassStorageDriver *msdp);
+
+#define BLOCK_SIZE_INCREMENT 512
+#define BLOCK_WRITE_ITTERATION_COUNT 16
+
+#define MSD_START_TRANSMIT(msdp) \
+ chSysLock(); \
+ msdp->bulk_in_interupt_flag = false; \
+ usbStartTransmitI(msdp->usbp, msdp->ms_ep_number); \
+ chSysUnlock();
+
+
+#define MSD_START_RECEIVED(msdp) \
+ chSysLock(); \
+ msdp->bulk_out_interupt_flag = false; \
+ usbStartReceiveI(msdp->usbp, msdp->ms_ep_number); \
+ chSysUnlock();
+
+typedef enum {
+ MSD_USB_TRANSFER_STATUS_RUNNING = 0,
+ MSD_USB_TRANSFER_STATUS_DONE_SUCCESSFUL,
+ MSD_USB_TRANSFER_STATUS_DONE_FAILED,
+} msd_usb_transfer_status_t;
+
+typedef struct {
+ //uint8_t is_transfer_done;
+ msd_usb_transfer_status_t transfer_status;
+ /*Number of blocks actually read from USB IN endpoint that should be written to SD card*/
+ int num_blocks_to_write;
+ /*Number of blocks to read from USB IN endpoint*/
+ int max_blocks_to_read;
+ uint8_t buf[(BLOCK_SIZE_INCREMENT * BLOCK_WRITE_ITTERATION_COUNT)];
+} rw_usb_sd_buffer_t;
+
+static volatile rw_usb_sd_buffer_t rw_ping_pong_buffer[2];
+static uint8_t read_buffer[2][BLOCK_SIZE_INCREMENT];
+
+
+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))
+
+inline uint32_t swap_4byte_buffer(uint8_t *buff) {
+ //Note: this is specifically to avoid pointer aliasing and de-referencing words on non-word boundaries
+ uint32_t temp = 0;
+ memcpy(&temp, buff, sizeof(temp));
+ return(swap_uint32(temp));
+}
+
+inline uint16_t swap_2byte_buffer(uint8_t *buff) {
+ //Note: this is specifically to avoid pointer aliasing and de-referencing words on non-half-word boundaries
+ uint16_t temp = 0;
+ memcpy(&temp, buff, sizeof(temp));
+ return(swap_uint16(temp));
+}
+
+/**
+ * @brief USB Event handler calback
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ * @param[in] ep USB Endpoint Number
+ *
+ * @api
+ */
+void msdBulkInCallbackComplete(USBDriver *usbp, usbep_t ep) {
+ (void)usbp;
+ (void)ep;
+
+ if (ep > 0 && usbp->in_params[ep - 1] != NULL) {
+ USBMassStorageDriver *msdp = (USBMassStorageDriver *)usbp->in_params[ep - 1];
+
+ chSysLockFromIsr();
+ chBSemSignalI(&(msdp->bsem));
+
+ msdp->bulk_in_interupt_flag = true;
+
+
+ chSysUnlockFromIsr();
+ }
+}
+
+/**
+ * @brief USB Event handler calback
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ * @param[in] ep USB Endpoint Number
+ *
+ * @api
+ */
+void msdBulkOutCallbackComplete(USBDriver *usbp, usbep_t ep) {
+ (void)usbp;
+ (void)ep;
+
+ if (ep > 0 && usbp->in_params[ep - 1] != NULL) {
+ USBMassStorageDriver *msdp = (USBMassStorageDriver *)usbp->in_params[ep - 1];
+
+ chSysLockFromIsr();
+ chBSemSignalI(&(msdp->bsem));
+
+ msdp->bulk_out_interupt_flag = true;
+
+ chSysUnlockFromIsr();
+ }
+}
+
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+
+/**
+ * @brief This function will initialize a USBMassStorageDriver structure.
+ *
+ * @pre Upon entry, the BlockDevice state should be BLK_READY. If it's not BLK_READY, then this will
+ * wait untile it becomes BLK_READY.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ * @param[in] bbdp pointer to the @p BaseBlockDevice object, such as an SDCDriver object
+ * @param[in] msdp pointer to the @p USBMassStorageDriver object
+ * @param[in] ms_ep_number USB Endpoint Number to be used by the mass storage endpoint
+ *
+ * @init
+ */
+usb_msd_driver_state_t msdInit(USBDriver *usbp, BaseBlockDevice *bbdp, USBMassStorageDriver *msdp,
+ const usbep_t ms_ep_number, const uint16_t msd_interface_number) {
+
+ msdp->usbp = usbp;
+ msdp->driver_state = USB_MSD_DRIVER_OK;
+ msdp->state = MSD_STATE_IDLE;
+ msdp->trigger_transfer_index = UINT32_MAX;
+ msdp->bbdp = bbdp;
+ msdp->ms_ep_number = ms_ep_number;
+ msdp->msd_interface_number = msd_interface_number;
+ msdp->chp = NULL;
+ msdp->enable_media_removial = true;
+ msdp->block_dev_info_valid_flag = false;
+
+ chEvtInit(&msdp->evt_connected);
+ chEvtInit(&msdp->evt_ejected);
+
+ /* Initialize binary semaphore as taken, will cause the thread to initially
+ * wait on the */
+ chBSemInit(&msdp->bsem, TRUE);
+ /* Initialize binary semaphore as NOT taken */
+ chBSemInit(&msdp->usb_transfer_thread_bsem, FALSE);
+ chBSemInit(&msdp->mass_sorage_thd_bsem, FALSE);
+
+ /* Initialize sense structure to zero */
+ memset(&msdp->sense, 0, sizeof(msdp->sense));
+
+ /* Response code = 0x70, additional sense length = 0x0A */
+ msdp->sense.byte[0] = 0x70;//FIXME use #define, what is this???
+ msdp->sense.byte[7] = 0x0A;//FIXME use #define, what is this???
+
+ /* make sure block device is working and get info */
+ msdSetDefaultSenseKey(msdp);
+
+ const uint32_t sleep_ms = 50;
+ uint32_t t;
+ for(t = 0; t <= 250 && (blkGetDriverState(bbdp) != BLK_READY); t += sleep_ms ) {
+ chThdSleepMilliseconds(sleep_ms);
+ }
+
+ if( blkGetDriverState(bbdp) == BLK_READY ) {
+ blkGetInfo(bbdp, &msdp->block_dev_info);
+ msdp->block_dev_info_valid_flag = true;
+ } else {
+ //msdp->driver_state = USB_MSD_DRIVER_ERROR_BLK_DEV_NOT_READY;
+ }
+
+ usbp->in_params[ms_ep_number - 1] = (void *)msdp;
+
+ return(msdp->driver_state);
+}
+
+/**
+ * @brief Starts data handling threads for USB mass storage driver
+ *
+ * @param[in] msdp pointer to the @p USBMassStorageDriver object
+ *
+ * @api
+ */
+usb_msd_driver_state_t msdStart(USBMassStorageDriver *msdp) {
+ /*upon entry, USB bus should be disconnected*/
+
+ if (msdThd == NULL ) {
+ msdThd = chThdCreateStatic(msdp->waMassStorage, sizeof(msdp->waMassStorage), NORMALPRIO,
+ MassStorageThd, msdp);
+ }
+
+ if (msdUSBTransferThd == NULL ) {
+ msdUSBTransferThd = chThdCreateStatic(msdp->waMassStorageUSBTransfer,
+ sizeof(msdp->waMassStorageUSBTransfer),
+ NORMALPRIO, MassStorageUSBTransferThd,
+ msdp);
+ }
+
+ return(msdp->driver_state);
+}
+
+usb_msd_driver_state_t msdStop(USBMassStorageDriver *msdp) {
+ usb_msd_driver_state_t final_state = USB_MSD_DRIVER_STOPPED;
+
+ if (msdThd != NULL) {
+ chThdTerminate(msdThd);
+ int i;
+ for(i = 0; i < 20 && msdThd->p_state != THD_STATE_FINAL; i++ ) {
+ chThdSleepMilliseconds(20);
+ }
+
+ if( msdThd->p_state == THD_STATE_FINAL ) {
+ final_state = USB_MSD_DRIVER_ERROR;
+ }
+ msdThd = NULL;
+ }
+
+
+ if (msdUSBTransferThd == NULL) {
+ chThdTerminate(msdUSBTransferThd);
+ int i;
+ for(i = 0; i < 20 && msdUSBTransferThd->p_state != THD_STATE_FINAL; i++ ) {
+ chThdSleepMilliseconds(20);
+ }
+
+ if( msdUSBTransferThd->p_state == THD_STATE_FINAL ) {
+ final_state = USB_MSD_DRIVER_ERROR;
+ }
+ msdUSBTransferThd = NULL;
+ }
+
+
+ msdp->driver_state = final_state;
+ return(msdp->driver_state);
+}
+
+
+/**
+ * @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.
+ *
+ * @api
+ */
+bool_t msdRequestsHook(USBDriver *usbp) {
+ return(msdRequestsHook2(usbp, NULL));
+}
+
+/**
+ * @brief Alternate request hook, useful for USB composite devices
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ * @param[in] msdp pointer to the @p USBMassStorageDriver object
+ * @return The hook status.
+ * @retval TRUE Message handled internally.
+ * @retval FALSE Message not handled.
+ *
+ * @api
+ */
+bool_t msdRequestsHook2(USBDriver *usbp, USBMassStorageDriver *msdp) {
+ 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 the MSD interface number.*/
+ if( msdp != NULL ) {
+ if (MSD_SETUP_INDEX(usbp->setup) != msdp->msd_interface_number)
+ return FALSE;
+ } else 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;
+
+ //static uint8_t len_buf[1] = {0};
+ msdp->data.max_lun_len_buf[0] = 0;
+ usbSetupTransfer(usbp, msdp->data.max_lun_len_buf, 1, NULL);
+ return TRUE;
+ default:
+ return FALSE;
+ break;
+ }
+ }
+ return FALSE;
+}
+
+
+const char* usb_msd_driver_state_t_to_str(const usb_msd_driver_state_t driver_state) {
+ switch (driver_state) {
+ case USB_MSD_DRIVER_UNINITIALIZED:
+ return ("USB_MSD_DRIVER_UNINITIALIZED");
+ case USB_MSD_DRIVER_ERROR:
+ return ("USB_MSD_DRIVER_ERROR");
+ case USB_MSD_DRIVER_OK:
+ return ("USB_MSD_DRIVER_OK");
+ case USB_MSD_DRIVER_STOPPED:
+ return("USB_MSD_DRIVER_STOPPED");
+ case USB_MSD_DRIVER_ERROR_BLK_DEV_NOT_READY:
+ return ("USB_MSD_DRIVER_ERROR_BLK_DEV_NOT_READY");
+ }
+ return ("USB_MSD_DRIVER_UNKNOWN");
+}
+
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+
+/* Event Flow Functions */
+
+
+static uint8_t msdWaitForISR(USBMassStorageDriver *msdp, const bool_t check_reset, const msd_wait_mode_t wait_mode) {
+ uint8_t ret = WAIT_ISR_SUCCESS;
+ /* sleep until the ISR completes */
+ chSysLock();
+ msd_debug_print(msdp->chp, "WaitISR(mode=%d)\r\n", wait_mode);
+ for (;;) {
+ const msg_t m = chBSemWaitTimeoutS(&msdp->bsem, 1);
+ if (m == RDY_OK && wait_mode == MSD_WAIT_MODE_NONE ) {
+ break;
+ }
+
+ if( wait_mode == MSD_WAIT_MODE_BULK_IN && msdp->bulk_in_interupt_flag ) {
+ break;
+ } else if( wait_mode == MSD_WAIT_MODE_BULK_OUT && msdp->bulk_out_interupt_flag ) {
+ break;
+ }
+
+ if (check_reset && msdp->reconfigured_or_reset_event) {
+ ret = WAIT_ISR_BUSS_RESET_OR_RECONNECT;
+ break;
+ }
+
+ if( chThdShouldTerminate() ) {
+ break;
+ }
+ }
+ chSysUnlock();
+ return (ret);
+}
+
+
+static void WaitForUSBTransferComplete(USBMassStorageDriver *msdp,
+ const int ping_pong_buffer_index) {
+ msd_debug_nest_print(msdp->chp, "A");
+ while (TRUE) {
+ chBSemWaitTimeout(&msdp->mass_sorage_thd_bsem, MS2ST(1));
+
+ if (rw_ping_pong_buffer[ping_pong_buffer_index].transfer_status != MSD_USB_TRANSFER_STATUS_RUNNING) {
+ break;
+ } else {
+ //chThdSleepMilliseconds(1);
+ }
+ }
+ msd_debug_nest_print(msdp->chp, "a");
+}
+
+
+
+/* SCSI Functions */
+
+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;
+}
+
+
+static void msdSetDefaultSenseKey(USBMassStorageDriver *msdp) {
+ SCSISetSense(msdp, SCSI_SENSE_KEY_GOOD,
+ SCSI_ASENSE_NO_ADDITIONAL_INFORMATION,
+ SCSI_ASENSEQ_NO_QUALIFIER);
+}
+
+
+#ifndef USB_MASS_STORAGE_INQUIRY_RESPONSE_STRING_0
+# define USB_MASS_STORAGE_INQUIRY_RESPONSE_STRING_0 "Chibios"
+#endif
+
+#ifndef USB_MASS_STORAGE_INQUIRY_RESPONSE_STRING_1
+# define USB_MASS_STORAGE_INQUIRY_RESPONSE_STRING_1 "Mass Storage"
+#endif
+
+#ifndef USB_MASS_STORAGE_INQUIRY_RESPONSE_STRING_2
+# define USB_MASS_STORAGE_INQUIRY_RESPONSE_STRING_2 {'v', CH_KERNEL_MAJOR + '0', '.', CH_KERNEL_MINOR + '0'}
+#endif
+
+static const scsi_inquiry_response_t default_scsi_inquiry_response =
+ {0x00, /* peripheral, direct access block device */
+ 0x80, /* removable */
+ 0x04, /* version, SPC-2 */
+ 0x02, /* response data format */
+ 0x20, /* additional_length, response has 0x20 + 4 bytes */
+ 0x00, /* sccstp*/
+ 0x00, /* bqueetc*/
+ 0x00, /* cmdqueue*/
+ USB_MASS_STORAGE_INQUIRY_RESPONSE_STRING_0,
+ USB_MASS_STORAGE_INQUIRY_RESPONSE_STRING_1,
+ USB_MASS_STORAGE_INQUIRY_RESPONSE_STRING_2,
+ };
+
+
+static msd_wait_mode_t SCSICommandInquiry(USBMassStorageDriver *msdp) {
+ msd_cbw_t *cbw = &(msdp->cbw);
+ msdp->data.scsi_inquiry_response = default_scsi_inquiry_response;
+
+ 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 */
+ msd_debug_err_print(msdp->chp, " INQ ERR 0x%X 0x%X\r\n", cbw->scsi_cmd_data[1], cbw->scsi_cmd_data[2]);
+ SCSISetSense(msdp, SCSI_SENSE_KEY_ILLEGAL_REQUEST,
+ SCSI_ASENSE_INVALID_FIELD_IN_CDB, SCSI_ASENSEQ_NO_QUALIFIER);
+
+ //we should indicate that the command failed to the host, but still return data
+ msdp->command_succeeded_flag = false;
+ }
+
+ usbPrepareTransmit(msdp->usbp, msdp->ms_ep_number, (uint8_t *)&msdp->data.scsi_inquiry_response,
+ sizeof(scsi_inquiry_response_t));
+
+
+ MSD_START_TRANSMIT(msdp);
+
+ /* wait for ISR */
+ return MSD_WAIT_MODE_BULK_IN;
+}
+
+static msd_wait_mode_t SCSICommandRequestSense(USBMassStorageDriver *msdp) {
+ //This command should not affect the sense key
+ usbPrepareTransmit(msdp->usbp, msdp->ms_ep_number, (uint8_t *)&msdp->sense,
+ sizeof(scsi_sense_response_t));
+
+ MSD_START_TRANSMIT(msdp);
+
+ /* wait for ISR */
+ return MSD_WAIT_MODE_BULK_IN;
+}
+
+static msd_wait_mode_t SCSICommandReadFormatCapacity(USBMassStorageDriver *msdp) {
+ msdSetDefaultSenseKey(msdp);
+
+ const uint32_t formated_capactiy_descriptor_code = 0x02;//see usbmass-ufi10.doc for codes
+
+ msdp->data.format_capacity_response.payload_byte_length[3] = 8;
+ msdp->data.format_capacity_response.last_block_addr = swap_uint32(msdp->block_dev_info.blk_num - 1);
+ msdp->data.format_capacity_response.block_size = swap_uint32(msdp->block_dev_info.blk_size) | formated_capactiy_descriptor_code;
+
+ usbPrepareTransmit(msdp->usbp, msdp->ms_ep_number, (uint8_t *)&msdp->data.format_capacity_response,
+ sizeof(msdp->data.format_capacity_response));
+
+ MSD_START_TRANSMIT(msdp);
+
+ /* wait for ISR */
+ return MSD_WAIT_MODE_BULK_IN;
+}
+
+static msd_wait_mode_t SCSICommandReadCapacity10(USBMassStorageDriver *msdp) {
+ msdSetDefaultSenseKey(msdp);
+
+ msdp->data.read_capacity10_response.block_size = swap_uint32(msdp->block_dev_info.blk_size);
+ msdp->data.read_capacity10_response.last_block_addr = swap_uint32(msdp->block_dev_info.blk_num - 1);
+
+ usbPrepareTransmit(msdp->usbp, msdp->ms_ep_number, (uint8_t *)&msdp->data.read_capacity10_response,
+ sizeof(msdp->data.read_capacity10_response));
+
+ MSD_START_TRANSMIT(msdp);
+
+ /* wait for ISR */
+ return MSD_WAIT_MODE_BULK_IN;
+}
+
+static msd_wait_mode_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);
+
+ msdp->command_succeeded_flag = false;
+ return MSD_WAIT_MODE_NONE;
+ }
+
+ /* TODO: actually perform the test */
+
+ /* don't wait for ISR */
+ return MSD_WAIT_MODE_NONE;
+}
+
+static void SCSIWriteTransferPingPong(USBMassStorageDriver *msdp,
+ volatile rw_usb_sd_buffer_t *dest_buffer) {
+ msd_debug_nest_print(msdp->chp, "B");
+ int cnt;
+ dest_buffer->transfer_status = MSD_USB_TRANSFER_STATUS_RUNNING;
+ dest_buffer->num_blocks_to_write = 0;
+
+#if MSD_ENABLE_PERF_DEBUG_GPIOS
+ palSetPad(GPIOH, GPIOH_LED2);
+#endif
+
+ for (cnt = 0;
+ cnt < BLOCK_WRITE_ITTERATION_COUNT
+ && cnt < dest_buffer->max_blocks_to_read; cnt++) {
+
+ msdp->transfer_thread_state = "RX-Prep";
+ usbPrepareReceive(msdp->usbp, msdp->ms_ep_number,
+ (uint8_t*)&dest_buffer->buf[cnt * BLOCK_SIZE_INCREMENT],
+ (msdp->block_dev_info.blk_size));
+
+ msdp->transfer_thread_state = "RX";
+ MSD_START_RECEIVED(msdp);
+
+ msdp->transfer_thread_state = "RX-Wait";
+ msdWaitForISR(msdp, FALSE, MSD_WAIT_MODE_BULK_OUT);
+
+ dest_buffer->num_blocks_to_write++;
+ }
+ dest_buffer->transfer_status = MSD_USB_TRANSFER_STATUS_DONE_SUCCESSFUL;
+ msdp->transfer_thread_state = "RX-Done";
+ //FIXME we need to handle setting the status to error if something failed, like a usb reset or something
+
+#if MSD_ENABLE_PERF_DEBUG_GPIOS
+ palClearPad(GPIOH, GPIOH_LED2);
+#endif
+
+ msd_debug_nest_print(msdp->chp, "b");
+}
+
+
+static msd_wait_mode_t SCSICommandStartReadWrite10(USBMassStorageDriver *msdp) {
+ msd_cbw_t *cbw = &(msdp->cbw);
+ int read_success;
+ int retry_count;
+
+ msdSetDefaultSenseKey(msdp);
+
+ if ((cbw->scsi_cmd_data[0] == SCSI_CMD_WRITE_10) && blkIsWriteProtected(msdp->bbdp)) {
+ msd_debug_err_print(msdp->chp, "\r\nWrite Protected!\r\n");
+ /* 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_NOT_READY, SCSI_ASENSE_WRITE_PROTECTED,
+ SCSI_ASENSEQ_NO_QUALIFIER);
+
+ msdp->command_succeeded_flag = false;
+ msdp->stall_in_endpoint = true;
+ return MSD_WAIT_MODE_NONE;
+ }
+
+ //FIXME, which of these?
+ //uint32_t rw_block_address = swap_4byte_buffer(&cbw->scsi_cmd_data[2]);
+ //const uint16_t total_blocks = swap_2byte_buffer(&cbw->scsi_cmd_data[7]);
+ uint32_t rw_block_address = swap_uint32(*(uint32_t *)&cbw->scsi_cmd_data[2]);
+ const uint16_t total_blocks = swap_uint16(*(uint16_t *)&cbw->scsi_cmd_data[7]);
+ const uint32_t rw_block_address_origional = rw_block_address;
+ uint16_t i = 0;
+
+ if (rw_block_address >= msdp->block_dev_info.blk_num) {
+ msd_debug_err_print(msdp->chp, "\r\nBlock Address too large %u > %u\r\n", 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_ILLEGAL_REQUEST, SCSI_ASENSE_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE,
+ SCSI_ASENSEQ_NO_QUALIFIER);
+
+ msdp->command_succeeded_flag = false;
+ msdp->stall_in_endpoint = true;
+
+ /* don't wait for ISR */
+ return MSD_WAIT_MODE_NONE;
+ }
+
+ /*initialized ping pong buffer*/
+ for (i = 0; i < 2; i++) {
+ rw_ping_pong_buffer[i].max_blocks_to_read = 0;
+ rw_ping_pong_buffer[i].num_blocks_to_write = 0;
+ rw_ping_pong_buffer[i].transfer_status = MSD_USB_TRANSFER_STATUS_RUNNING;
+ }
+
+ msd_debug_nest_print(msdp->chp, "\r\nG");
+ if (cbw->scsi_cmd_data[0] == SCSI_CMD_WRITE_10) {
+ /* loop over each block */
+
+ uint32_t ping_pong_buffer_index = 0;
+ /*initiate a transfer*/
+ rw_ping_pong_buffer[ping_pong_buffer_index].transfer_status = MSD_USB_TRANSFER_STATUS_RUNNING;
+ rw_ping_pong_buffer[ping_pong_buffer_index].max_blocks_to_read = total_blocks;
+
+ /*Trigger the transfer in the other thread*/
+ msdp->trigger_transfer_index = ping_pong_buffer_index;
+
+ /*wake other thread on semaphore to trigger the transfer*/
+ chBSemSignal(&msdp->usb_transfer_thread_bsem);
+
+ WaitForUSBTransferComplete(msdp, ping_pong_buffer_index);
+
+ for (i = 0; i < total_blocks;) {
+ const int done_buffer_index = ping_pong_buffer_index;
+ const int empty_buffer_index = ((ping_pong_buffer_index + 1) % 2);
+
+ /*initiate another transfer in the other ping pong buffer*/
+ //const bool_t queue_another_transfer = ((i + BLOCK_WRITE_ITTERATION_COUNT) < total_blocks);
+ const bool_t queue_another_transfer = ((i + rw_ping_pong_buffer[done_buffer_index].num_blocks_to_write) < total_blocks);
+
+ msd_debug_nest_print(msdp->chp, "D");
+ if (queue_another_transfer) {
+ while (TRUE) {
+ if (msdp->trigger_transfer_index == UINT32_MAX) {
+ rw_ping_pong_buffer[empty_buffer_index].max_blocks_to_read =
+ total_blocks - i - rw_ping_pong_buffer[done_buffer_index].num_blocks_to_write;
+
+ msdp->trigger_transfer_index = empty_buffer_index;
+
+ /*wake other thread on semaphore to trigger the transfer*/
+ chBSemSignal(&msdp->usb_transfer_thread_bsem);
+ break;
+ } else {
+ chThdSleepMilliseconds(1);
+ }
+ }
+ }
+ msd_debug_nest_print(msdp->chp, "d");
+
+ if (rw_ping_pong_buffer[done_buffer_index].num_blocks_to_write <= 0) {
+ /*This should never happen!!! Something is seriously wrong!*/
+ msd_debug_err_print(
+ msdp->chp, "\r\nCant write 0 blocks, this should not happen, halting\r\n");
+ chThdSleepMilliseconds(50);
+ chSysHalt();
+ }
+
+ /* now write the block to the block device */
+ MSD_W_LED_ON();
+ msd_debug_nest_print(msdp->chp, "E");
+ if (blkWrite(msdp->bbdp, rw_block_address,
+ (uint8_t*)rw_ping_pong_buffer[done_buffer_index].buf,
+ rw_ping_pong_buffer[done_buffer_index].num_blocks_to_write)
+ == CH_FAILED) {
+ msd_debug_err_print(msdp->chp, "\r\nSD Block Write Error\r\n");
+ chThdSleepMilliseconds(50);
+ msdp->write_error_count++;
+
+ msdp->command_succeeded_flag = false;
+
+ SCSISetSense(msdp, SCSI_SENSE_KEY_MEDIUM_ERROR,
+ SCSI_ASENSE_PEREPHERIAL_DEVICE_WRITE_FAULT,
+ SCSI_ASENSEQ_PEREPHERIAL_DEVICE_WRITE_FAULT);
+
+
+#define OLD_WRITE_ERROR_HANDLING FALSE
+#if OLD_WRITE_ERROR_HANDLING
+ /*
+ * I think that this mode of error handling is incorrect, and was causing ACM0 usb device resets in the event of EMMC write errors.
+ * I confirmed using the Beagle USB 480 analyzer that the host gets a failed staus, and retries the write to the given error block, at which point the block write succeeds for thist test case.
+ * This code should be purged at some point
+ */
+ msdp->stall_out_endpoint = true;
+
+ if (queue_another_transfer) {
+ /*Let the previous queued transfer finish and ignore it.*/
+ WaitForUSBTransferComplete(msdp, empty_buffer_index);
+ }
+
+ MSD_W_LED_OFF();
+ return (MSD_WAIT_MODE_NONE);
+#endif
+ } else {
+ msdp->write_success_count++;
+ }
+ msd_debug_nest_print(msdp->chp, "e");
+ MSD_W_LED_OFF();
+
+ rw_block_address += rw_ping_pong_buffer[done_buffer_index].num_blocks_to_write;
+ i += rw_ping_pong_buffer[done_buffer_index].num_blocks_to_write;
+ rw_ping_pong_buffer[done_buffer_index].transfer_status = MSD_USB_TRANSFER_STATUS_RUNNING;
+ rw_ping_pong_buffer[done_buffer_index].num_blocks_to_write = 0;
+
+ msd_debug_nest_print(msdp->chp, "F");
+ if (queue_another_transfer) {
+ if( !( i< total_blocks) ) {
+ msd_debug_err_print(msdp->chp, "\r\nERROR: Queued another transfer but not going to rx the data\r\n");
+ }
+ WaitForUSBTransferComplete(msdp, empty_buffer_index);
+ }
+ msd_debug_nest_print(msdp->chp, "f");
+
+ /*Swap the ping pong buffers*/
+ ping_pong_buffer_index = empty_buffer_index;
+ }
+
+ if( i != total_blocks ) {
+ msd_debug_err_print(msdp->chp, "\r\ni!=total_blocks, %u, %u\r\n", i, total_blocks);
+ }
+
+ msd_debug_nest_print(msdp->chp, "(%u,%u,%u)", rw_block_address_origional, total_blocks, i);
+
+ } else {
+ /* FIXME: For some reason, when doing a blkRead on a SanDisk 8g sd card, it takes 2.5ms to read a block, limiting
+ * max read performance to about 200k/s. However, when using an 8g transcend SD card, we can get up to 2.0 megabytes/sec
+ * read through put. What's the difference? configuration for the SD driver?
+ */
+
+ i = 0;
+ /* read the first block from block device */
+ read_success = FALSE;
+ for (retry_count = 0; retry_count < 3; retry_count++) {
+ if (blkRead(msdp->bbdp, rw_block_address, read_buffer[i % 2], 1)
+ == CH_FAILED) {
+ msd_debug_err_print(msdp->chp, "\r\nSD Block Read Error: block # %u\r\n", rw_block_address);
+ msdp->read_error_count++;
+ } else {
+ msdp->read_success_count++;
+ if( retry_count > 0 ) {
+ msd_debug_err_print(msdp->chp, "Successful Block Read Retry\r\n");
+ }
+ read_success = TRUE;
+ break;
+ }
+ }
+
+
+ if ((!read_success) ) {
+ msd_debug_err_print(msdp->chp, "\r\nSD Block Read Error 1, breaking read sequence, block # %u\r\n", rw_block_address);
+
+ /*wait for printing to finish*/
+ chThdSleepMilliseconds(10);
+
+ msdp->command_succeeded_flag = false;
+ msdp->stall_in_endpoint = true;
+
+ msd_debug_err_print(
+ msdp->chp, "\r\nSetting sense code %u\r\n", SCSI_SENSE_KEY_MEDIUM_ERROR);
+
+ SCSISetSense(msdp, SCSI_SENSE_KEY_MEDIUM_ERROR,
+ SCSI_ASENSE_UNRECOVERED_READ_ERROR,
+ SCSI_ASENSEQ_NO_QUALIFIER);
+
+ return MSD_WAIT_MODE_NONE;
+ }
+
+ rw_block_address++;
+
+ /* loop over each block */
+ for (i = 0; i < total_blocks; i++) {
+
+ /* transmit the block */
+ //while (usbGetTransmitStatusI(msdp->usbp, msdp->ms_ep_number)) {
+ //wait for the prior transmit to complete
+ //}
+ usbPrepareTransmit(msdp->usbp, msdp->ms_ep_number, read_buffer[i % 2],
+ msdp->block_dev_info.blk_size);
+
+ MSD_START_TRANSMIT(msdp);
+
+ if (i < (total_blocks - 1)) {
+ /* there is at least one more block to be read from device */
+ /* so read that while the USB transfer takes place */
+ read_success = FALSE;
+ MSD_R_LED_ON();
+ for (retry_count = 0; retry_count < 3; retry_count++) {
+ if (blkRead(msdp->bbdp, rw_block_address, read_buffer[(i+1) % 2], 1)
+ == CH_FAILED) {
+ msd_debug_err_print(msdp->chp, "\r\nSD Block Read Error 2: block # %u\r\n", rw_block_address);
+
+ msdp->read_error_count++;
+ } else {
+ if( retry_count > 0 ) {
+ msd_debug_err_print(msdp->chp, "Successful Block Read Retry: block # %u\r\n", rw_block_address);
+ }
+ read_success = TRUE;
+ msdp->read_success_count++;
+ break;
+ }
+ }
+ MSD_R_LED_OFF();
+
+ if( !read_success ) {
+ msd_debug_err_print(
+ msdp->chp, "\r\nSD Block Read Error 22, addr=%d, halting\r\n", rw_block_address);
+
+ /*wait for printing to finish*/
+ chThdSleepMilliseconds(70);
+
+ msdp->command_succeeded_flag = false;
+ msdp->stall_in_endpoint = true;
+
+ msd_debug_err_print(
+ msdp->chp, "\r\nSetting sense code %u\r\n", SCSI_SENSE_KEY_MEDIUM_ERROR);
+
+ SCSISetSense(msdp, SCSI_SENSE_KEY_MEDIUM_ERROR,
+ SCSI_ASENSE_UNRECOVERED_READ_ERROR,
+ SCSI_ASENSEQ_NO_QUALIFIER);
+ return MSD_WAIT_MODE_NONE;
+ }
+
+ rw_block_address++;
+ }
+
+ /*FIXME In the event that the USB connection is unplugged while we're waiting for a bulk
+ * endpoint ISR, this will never return, and when re-plugged into the host, the drive will
+ * not show back up on the host. We need a way to break out of this loop when disconnected from the bus.
+ */
+
+ if (msdWaitForISR(msdp, TRUE, MSD_WAIT_MODE_BULK_IN) == WAIT_ISR_BUSS_RESET_OR_RECONNECT) {
+ //fixme are we handling the reset case properly
+ return MSD_WAIT_MODE_NONE;
+ }
+ }
+ }
+ msd_debug_nest_print(msdp->chp, "g");
+
+ /* don't wait for ISR */
+ return MSD_WAIT_MODE_NONE;
+}
+
+static msd_wait_mode_t SCSICommandStartStopUnit(USBMassStorageDriver *msdp) {
+ scsi_start_stop_unit_request_t *ssu =
+ (scsi_start_stop_unit_request_t *)&(msdp->cbw.scsi_cmd_data);
+
+ if ((ssu->loej_start & 0b00000011) == 0b00000010) {
+ /* device has been ejected */
+ if (!msdp->disable_usb_bus_disconnect_on_eject) {
+ chEvtBroadcast(&msdp->evt_ejected);
+ msdp->state = MSD_STATE_EJECTED;
+ }
+ }
+
+ /* don't wait for ISR */
+ return MSD_WAIT_MODE_NONE;
+}
+
+static msd_wait_mode_t SCSICommandPreventAllowMediumRemovial(USBMassStorageDriver *msdp) {
+ msd_cbw_t *cbw = &(msdp->cbw);
+
+ if( (cbw->scsi_cmd_data[4] & 0x01) ) {
+ //prohibit media removal
+ if( msdp->enable_media_removial ) {
+ //this can have positive performance
+ msdp->command_succeeded_flag = false;
+ SCSISetSense(msdp, SCSI_SENSE_KEY_ILLEGAL_REQUEST,
+ SCSI_ASENSE_INVALID_COMMAND, SCSI_ASENSEQ_NO_QUALIFIER);
+ }
+ }
+
+ /* don't wait for ISR */
+ return MSD_WAIT_MODE_NONE;
+}
+
+
+
+static msd_wait_mode_t SCSICommandModeSense6(USBMassStorageDriver *msdp) {
+ //FIXME check for unsupported mode page set sense code, see page 161(144) of USB Mass storage book
+ memset(&msdp->data.mode_sense6_response, 0, sizeof(msdp->data.mode_sense6_response));
+ msdp->data.mode_sense6_response.mode_data_length = 3;
+ if( blkIsWriteProtected(msdp->bbdp) ) {
+ msdp->data.mode_sense6_response.device_specifc_paramters |= (1<<7);
+ }
+
+ usbPrepareTransmit(msdp->usbp, msdp->ms_ep_number, (uint8_t*)&msdp->data.mode_sense6_response, 4);
+
+ MSD_START_TRANSMIT(msdp);
+
+ /* wait for ISR */
+ return MSD_WAIT_MODE_BULK_IN;
+}
+
+static msd_wait_mode_t msdWaitForCommandBlock(USBMassStorageDriver *msdp) {
+ usbPrepareReceive(msdp->usbp, msdp->ms_ep_number, (uint8_t *)&msdp->cbw,
+ sizeof(msd_cbw_t));
+
+ MSD_START_RECEIVED(msdp);
+
+ msdp->state = MSD_STATE_READ_CMD_BLOCK;
+
+ return(MSD_WAIT_MODE_BULK_OUT);/* wait for ISR */
+}
+
+/* */
+/**
+ * @brief A command block has been received
+
+ *
+ * @param[in] p1 description of parameter one
+ * @param[out] p2 description of parameter two
+ * @param[in,out] p3 description of parameter three
+ * @return Description of the returned value, must be omitted if
+ * a function returns void.
+ * @retval TRUE On success
+ * @retval FALSE On failure
+ *
+ */
+static msd_wait_mode_t msdProcessCommandBlock(USBMassStorageDriver *msdp) {
+ msd_cbw_t *cbw = &(msdp->cbw);
+
+ /* by default transition back to the idle state */
+ msdp->state = MSD_STATE_IDLE;
+
+ msd_debug_print(msdp->chp, " CMD 0x%X\r\n", cbw->scsi_cmd_data[0]);
+ msdp->command_succeeded_flag = true;
+ msdp->stall_in_endpoint = false;
+ msdp->stall_out_endpoint = false;
+ msd_wait_mode_t wait_mode = MSD_WAIT_MODE_NONE;
+
+
+ /* 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))
+ {
+
+ msdp->scsi_command_state = "Bad CBW";
+ msd_debug_err_print(msdp->chp, "Bad CBW, sig=0x%X, lun=0x%X, data_len=0x%X, flags=0x%X, scsi_cmd_len=0x%X\r\n", cbw->signature, cbw->lun, cbw->data_len, cbw->flags, cbw->scsi_cmd_len);
+ /* stall both IN and OUT endpoints */
+ msdp->stall_out_endpoint = true;
+ msdp->stall_in_endpoint = true;
+ msdp->command_succeeded_flag = false;
+ SCSISetSense(msdp, SCSI_SENSE_KEY_ILLEGAL_REQUEST,
+ SCSI_ASENSE_INVALID_FIELD_IN_CDB, SCSI_ASENSEQ_NO_QUALIFIER);
+
+#if 0
+ chSysLock();
+ msdp->bulk_out_interupt_flag = false;
+ usbStallReceiveI(msdp->usbp, msdp->ms_ep_number);
+ //usbStallTransmitI(msdp->usbp, msdp->ms_ep_number);
+ chSysUnlock();
+
+ //wait for the host to clear the stall
+ msdWaitForISR(msdp, TRUE, MSD_WAIT_MODE_BULK_OUT);
+
+ /* don't wait for ISR */
+ return MSD_WAIT_MODE_NONE;
+#endif
+ }
+
+
+ if( msdp->command_succeeded_flag ) {
+ switch ( (msd_scsi_command_t) cbw->scsi_cmd_data[0] ) {
+ case SCSI_CMD_INQUIRY:
+ msdp->scsi_command_state = "CMD_INQ";
+ msd_debug_print(msdp->chp, "CMD_INQ\r\n");
+ wait_mode = SCSICommandInquiry(msdp);
+ msdp->scsi_command_state = "CMD_INQ-Done";
+ break;
+ case SCSI_CMD_REQUEST_SENSE_6:
+ msdp->scsi_command_state = "CMD_RS";
+ msd_debug_print(msdp->chp, "\r\nCMD_RS\r\n");
+ wait_mode = SCSICommandRequestSense(msdp);
+ msdp->scsi_command_state = "CMD_RS-Done";
+ break;
+ case SCSI_CMD_READ_FORMAT_CAPACITY:
+ msdp->scsi_command_state = "CMD_RFC";
+ msd_debug_print(msdp->chp, "CMD_RFC\r\n");
+ wait_mode = SCSICommandReadFormatCapacity(msdp);
+ msdp->scsi_command_state = "CMD_RFC-Done";
+ break;
+ case SCSI_CMD_READ_CAPACITY_10:
+ msdp->scsi_command_state = "CMD_RC10";
+ msd_debug_print(msdp->chp, "CMD_RC10\r\n");
+ wait_mode = SCSICommandReadCapacity10(msdp);
+ msdp->scsi_command_state = "CMD_RC10-Done";
+ break;
+ case SCSI_CMD_READ_10:
+ case SCSI_CMD_WRITE_10:
+ msdp->scsi_command_state = "CMD_RW";
+ msd_debug_print(msdp->chp, "CMD_RW\r\n");
+ MSD_RW_LED_ON();
+ wait_mode = SCSICommandStartReadWrite10(msdp);
+ MSD_RW_LED_OFF();
+ msdp->scsi_command_state = "CMD_RW-Done";
+ break;
+ case SCSI_CMD_SEND_DIAGNOSTIC:
+ msdp->scsi_command_state = "CMD_DIA";
+ msd_debug_print(msdp->chp, "CMD_DIA\r\n");
+ wait_mode = SCSICommandSendDiagnostic(msdp);
+ msdp->scsi_command_state = "CMD_DIA-Done";
+ break;
+ case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
+ msdp->scsi_command_state = "CMD_PAMR";
+ msd_debug_print(msdp->chp, "CMD_PAMR\r\n");
+ wait_mode = SCSICommandPreventAllowMediumRemovial(msdp);
+ msdp->scsi_command_state = "CMD_PAMR-Done";
+ break;
+ case SCSI_CMD_TEST_UNIT_READY:
+ case SCSI_CMD_VERIFY_10:
+ msdp->scsi_command_state = "CMD_00_1E_2F";
+ msd_debug_print(msdp->chp, "CMD_00_1E_2F\r\n");
+ msdp->scsi_command_state = "CMD_00_1E_2F-Done";
+ /* don't handle */
+ break;
+ case SCSI_CMD_MODE_SENSE_6:
+ msdp->scsi_command_state = "CMD_S6";
+ msd_debug_print(msdp->chp, "\r\nCMD_S6\r\n");
+ wait_mode = SCSICommandModeSense6(msdp);
+ msdp->scsi_command_state = "CMD_S6-Done";
+ break;
+ case SCSI_CMD_START_STOP_UNIT:
+ msdp->scsi_command_state = "CMD_STOP";
+ msd_debug_print(msdp->chp, "CMD_STOP\r\n");
+ wait_mode = SCSICommandStartStopUnit(msdp);
+ msdp->scsi_command_state = "CMD_STOP-Done";
+ break;
+ case SCSI_CMD_SYNCHRONIZE_CACHE_10:
+ msdp->scsi_command_state = "SYNC_10";
+ msd_debug_print(msdp->chp, "SYNC_10\r\n");
+ //FIXME impliment this and flush data to the MMC card, Linux sends this command. We are implicitly synchronized
+ //in our writes since we never leave data in RAM
+
+ SCSISetSense(msdp, SCSI_SENSE_KEY_GOOD,
+ SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, SCSI_ASENSEQ_NO_QUALIFIER);
+ wait_mode = MSD_WAIT_MODE_NONE;
+
+ msdp->scsi_command_state = "SYNC_10-Done";
+ break;
+ default:
+ msdp->last_bad_scsi_command = cbw->scsi_cmd_data[0];
+
+ msdp->scsi_command_state = "CMD_Def";
+ msd_debug_err_print(msdp->chp, "CMD Unknown: 0x%X, using default CMD handler\r\n", cbw->scsi_cmd_data[0]);
+ msdp->command_succeeded_flag = false;
+ SCSISetSense(msdp, SCSI_SENSE_KEY_ILLEGAL_REQUEST,
+ SCSI_ASENSE_INVALID_COMMAND, SCSI_ASENSEQ_NO_QUALIFIER);
+
+#if 0
+ /* stall IN endpoint */
+ chSysLock()
+ msdp->bulk_in_interupt_flag = false;
+ usbStallTransmitI(msdp->usbp, msdp->ms_ep_number);
+ chSysUnlock()
+
+ msdWaitForISR(msdp, TRUE, MSD_WAIT_MODE_BULK_IN);
+#else
+ //msdp->stall_out_endpoint = true;
+ //msdp->stall_in_endpoint = true;
+#endif
+
+ msdp->scsi_command_state = "CMD_Def-Done";
+ cbw->data_len = 0;
+#if 0
+ return MSD_WAIT_MODE_NONE;
+#endif
+ }
+ }
+
+ cbw->data_len = 0;
+
+
+ if( msdp->stall_in_endpoint || msdp->stall_out_endpoint ) {
+ msdp->scsi_command_state = "Stall";
+ /* stall IN endpoint */
+ chSysLock();
+ if( msdp->stall_in_endpoint ) {
+ msd_debug_err_print(msdp->chp, "stalling IN endpoint\r\n");
+ msdp->bulk_in_interupt_flag = false;
+ usbStallTransmitI(msdp->usbp, msdp->ms_ep_number);
+ }
+ if( msdp->stall_out_endpoint ) {
+ msd_debug_err_print(msdp->chp, "stalling OUT endpoint\r\n");
+ msdp->bulk_out_interupt_flag = false;
+ usbStallReceiveI(msdp->usbp, msdp->ms_ep_number);
+ }
+ chSysUnlock();
+
+ if( msdp->stall_in_endpoint ) {
+ msdWaitForISR(msdp, TRUE, MSD_WAIT_MODE_BULK_IN);
+ }
+
+ if( msdp->stall_out_endpoint ) {
+ msdWaitForISR(msdp, TRUE, MSD_WAIT_MODE_BULK_OUT);
+ }
+ }
+
+ msdp->scsi_command_state = "Wait";
+
+ if (wait_mode != MSD_WAIT_MODE_NONE ) {
+ msd_debug_nest_print(msdp->chp, "H");
+ if (msdWaitForISR(msdp, TRUE, wait_mode) == WAIT_ISR_BUSS_RESET_OR_RECONNECT) {
+ msd_debug_nest_print(msdp->chp, "h");
+ return (MSD_WAIT_MODE_NONE);
+ }
+ msd_debug_nest_print(msdp->chp, "h");
+ }
+
+ msdp->scsi_command_state = "Wait-Done";
+
+ msd_csw_t *csw = &(msdp->csw);
+
+
+ csw->status = (msdp->command_succeeded_flag) ? MSD_COMMAND_PASSED : MSD_COMMAND_FAILED;
+ csw->signature = MSD_CSW_SIGNATURE;
+ csw->data_residue = cbw->data_len;
+ csw->tag = cbw->tag;
+
+ msdp->scsi_command_state = "TX";
+ msd_debug_nest_print(msdp->chp, "I");
+ usbPrepareTransmit(msdp->usbp, msdp->ms_ep_number, (uint8_t *)csw,
+ sizeof(msd_csw_t));
+
+ chSysLock();
+ msdp->bulk_out_interupt_flag = false;
+ msdWaitForCommandBlock(msdp);
+
+ msdp->bulk_in_interupt_flag = false;
+ usbStartTransmitI(msdp->usbp, msdp->ms_ep_number);
+ chSysUnlock();
+ msd_debug_nest_print(msdp->chp, "i");
+
+ msdWaitForISR(msdp, TRUE, MSD_WAIT_MODE_BULK_IN);//wait for our status to be sent back
+
+ msdp->scsi_command_state = "Done All";
+
+ /* wait on ISR */
+ //return MSD_WAIT_MODE_BULK_IN;
+ return MSD_WAIT_MODE_BULK_OUT;
+}
+
+
+
+
+
+/*===========================================================================*/
+/* Threads */
+/*===========================================================================*/
+
+/**
+ * @brief This thread is responsible for triggering a USB write of date
+ * from the MCU to the host. It is run as a separate thread to allow
+ * for concurrent RXing of data and writing of data to the SD card to
+ * thus significantly improve performance.
+ *
+ * @param[in] arg pointer to the @p USBMassStorageDriver object
+ *
+ * @special
+ */
+static msg_t MassStorageUSBTransferThd(void *arg) {
+ USBMassStorageDriver *msdp = (USBMassStorageDriver *)arg;
+
+ chRegSetThreadName("MSD-Transfer");
+
+ while ( !chThdShouldTerminate() ) {
+ if (msdp->suspend_threads_callback != NULL && msdp->suspend_threads_callback()) {
+ /* Suspend the thread for power savings mode */
+ chSysLock();
+ chSchGoSleepS(THD_STATE_SUSPENDED);
+ chSysUnlock();
+ }
+
+ if (msdp->trigger_transfer_index != UINT32_MAX) {
+ msdp->transfer_thread_state = "PP";
+ SCSIWriteTransferPingPong(
+ msdp, &rw_ping_pong_buffer[msdp->trigger_transfer_index]);
+ msdp->trigger_transfer_index = UINT32_MAX;
+ /*notify other thread*/
+ chBSemSignal(&msdp->mass_sorage_thd_bsem);
+ }
+
+ chBSemWaitTimeout(&msdp->usb_transfer_thread_bsem, MS2ST(1));
+ }
+
+ return (0);
+}
+
+
+
+static msg_t MassStorageThd(void *arg) {
+ USBMassStorageDriver *msdp = (USBMassStorageDriver *)arg;
+ chRegSetThreadName("MSD");
+
+ msd_wait_mode_t wait_for_isr = MSD_WAIT_MODE_NONE;
+
+ /* wait for the usb to be initialized */
+ msd_debug_print(msdp->chp, "Y");
+ msdWaitForISR(msdp, FALSE, MSD_WAIT_MODE_NONE);
+ msd_debug_print(msdp->chp, "y");
+
+ while ( !chThdShouldTerminate() ) {
+
+#if 0
+ if( msdp->suspend_threads_callback != NULL && msdp->suspend_threads_callback() ) {
+ /* Suspend the thread for power savings mode */
+ chSysLock();
+ chSchGoSleepS(THD_STATE_SUSPENDED);
+ chSysUnlock();
+ }
+#endif
+
+
+ wait_for_isr = MSD_WAIT_MODE_NONE;
+
+ if (msdp->reconfigured_or_reset_event) {
+ /*If the devices is unplugged and re-plugged but did not have a CPU reset,
+ * we must set the state back to idle.*/
+ msdp->reconfigured_or_reset_event = FALSE;
+ msdp->state = MSD_STATE_IDLE;
+
+ msdSetDefaultSenseKey(msdp);
+ }
+
+ bool_t enable_msd = true;
+ if (msdp->enable_msd_callback != NULL) {
+ enable_msd = msdp->enable_msd_callback();
+ }
+
+ if( msdp->driver_state != USB_MSD_DRIVER_OK ) {
+ enable_msd = false;
+ }
+ msdp->debug_enable_msd = enable_msd;
+
+ if ( enable_msd ) {
+
+ if( ! msdp->block_dev_info_valid_flag ) {
+ if( blkGetDriverState(msdp->bbdp) == BLK_READY ) {
+ blkGetInfo(msdp->bbdp, &msdp->block_dev_info);
+ msdp->block_dev_info_valid_flag = true;
+ }
+ }
+
+ msd_debug_print(msdp->chp, "state=%d\r\n", msdp->state);
+ /* wait on data depending on the current state */
+ switch (msdp->state) {
+ case MSD_STATE_IDLE:
+ msdp->msd_thread_state = "IDL";
+ msd_debug_print(msdp->chp, "IDL");
+ wait_for_isr = msdWaitForCommandBlock(msdp);
+ msd_debug_print(msdp->chp, "x\r\n");
+ break;
+ case MSD_STATE_READ_CMD_BLOCK:
+ msdp->msd_thread_state = "RCB";
+ msd_debug_print(msdp->chp, "RCB");
+ wait_for_isr = msdProcessCommandBlock(msdp);
+ msd_debug_print(msdp->chp, "x\r\n");
+ break;
+ case MSD_STATE_EJECTED:
+ /* disconnect usb device */
+ msd_debug_print(msdp->chp, "ejected\r\n");
+ if (!msdp->disable_usb_bus_disconnect_on_eject) {
+ msdp->msd_thread_state = "Ejected";
+ chThdSleepMilliseconds(70);
+ usbDisconnectBus(msdp->usbp);
+ usbStop(msdp->usbp);
+ chThdExit(0);
+ }
+ return 0;
+ }
+ }
+
+ msdp->debug_wait_for_isr = wait_for_isr;
+
+ if( enable_msd ) {
+ msd_debug_nest_print(msdp->chp, "L");
+ } else {
+ msd_debug_nest_print(msdp->chp, "M");
+ }
+
+ if (wait_for_isr) {
+ msd_debug_nest_print(msdp->chp, "J");
+ } else {
+ msd_debug_nest_print(msdp->chp, "K");
+ }
+
+ if (wait_for_isr && (!msdp->reconfigured_or_reset_event)) {
+ /* wait until the ISR wakes thread */
+ msd_debug_print(msdp->chp, "W%d,%d", wait_for_isr, msdp->state);
+ msdWaitForISR(msdp, TRUE, wait_for_isr);
+ msd_debug_print(msdp->chp, "w\r\n");
+ } else if( ! enable_msd ) {
+ chThdSleepMilliseconds(5);
+ }
+
+
+ if (wait_for_isr) {
+ msd_debug_nest_print(msdp->chp, "j");
+ } else {
+ msd_debug_nest_print(msdp->chp, "k");
+ }
+ }
+
+ return 0;
+}
+
+
+
+
+
+#endif /* HAL_USE_MASS_STORAGE_USB */
diff --git a/firmware/hw_layer/mass_storage/usb_msd.h b/firmware/hw_layer/mass_storage/usb_msd.h
new file mode 100644
index 0000000000..4fa2c47e2b
--- /dev/null
+++ b/firmware/hw_layer/mass_storage/usb_msd.h
@@ -0,0 +1,300 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
+ 2011,2012,2013 Giovanni Di Sirio.
+
+ This file is part of ChibiOS/RT.
+
+ ChibiOS/RT is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ ChibiOS/RT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#ifndef _USB_MSD_H_
+#define _USB_MSD_H_
+
+#include "hal.h"
+
+/* Default to disabled for USB Mass Storage */
+#if !defined(HAL_USE_MASS_STORAGE_USB)
+# define HAL_USE_MASS_STORAGE_USB FALSE
+#endif
+
+#if HAL_USE_MASS_STORAGE_USB || defined(__DOXYGEN__)
+
+
+#if 0
+#define MSD_RW_LED_ON() palSetPad(GPIOI, GPIOI_TRI_LED_BLUE)
+#define MSD_RW_LED_OFF() palClearPad(GPIOI, GPIOI_TRI_LED_BLUE)
+#define MSD_R_LED_ON() palSetPad(GPIOI, GPIOI_TRI_LED_BLUE)
+#define MSD_R_LED_OFF() palClearPad(GPIOI, GPIOI_TRI_LED_BLUE)
+#define MSD_W_LED_ON() palSetPad(GPIOH, GPIOI_TRI_LED_BLUE)
+#define MSD_W_LED_OFF() palClearPad(GPIOH, GPIOI_TRI_LED_BLUE)
+#else
+#define MSD_RW_LED_ON()
+#define MSD_RW_LED_OFF()
+#define MSD_R_LED_ON()
+#define MSD_R_LED_OFF()
+#define MSD_W_LED_ON()
+#define MSD_W_LED_OFF()
+#endif
+
+
+#if STM32_USB_USE_OTG2 && STM32_USE_USB_OTG2_HS
+# define USB_MS_EP_SIZE 512
+#else
+# define USB_MS_EP_SIZE 64
+#endif
+
+#define MSD_THREAD_STACK_SIZE 1024
+
+#define MSD_REQ_RESET 0xFF
+#define MSD_GET_MAX_LUN 0xFE
+#define MSD_CBW_SIGNATURE 0x43425355
+#define MSD_CSW_SIGNATURE 0x53425355
+#define MSD_COMMAND_DIR_DATA_OUT (0 << 7)
+#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)
+
+typedef enum {
+ SCSI_CMD_TEST_UNIT_READY = 0x00,
+ SCSI_CMD_REQUEST_SENSE_6 = 0x03,
+ SCSI_CMD_INQUIRY = 0x12,
+ SCSI_CMD_MODE_SENSE_6 = 0x1A,
+ SCSI_CMD_START_STOP_UNIT = 0x1B,
+ SCSI_CMD_SEND_DIAGNOSTIC = 0x1D,
+ SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL = 0x1E,
+ SCSI_CMD_READ_FORMAT_CAPACITY = 0x23,
+ SCSI_CMD_READ_CAPACITY_10 = 0x25,
+ SCSI_CMD_READ_10 = 0x28,
+ SCSI_CMD_WRITE_10 = 0x2A,
+ SCSI_CMD_VERIFY_10 = 0x2F,
+ SCSI_CMD_SYNCHRONIZE_CACHE_10 = 0x35,
+} msd_scsi_command_t;
+
+
+
+#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_PEREPHERIAL_DEVICE_WRITE_FAULT 0x03
+#define SCSI_ASENSE_LOGICAL_UNIT_NOT_READY 0x04
+#define SCSI_ASENSE_UNRECOVERED_READ_ERROR 0x11
+#define SCSI_ASENSE_INVALID_COMMAND 0x20
+#define SCSI_ASENSE_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x21
+#define SCSI_ASENSE_INVALID_FIELD_IN_CDB 0x24
+#define SCSI_ASENSE_WRITE_PROTECTED 0x27
+#define SCSI_ASENSE_NOT_READY_TO_READY_CHANGE 0x28
+#define SCSI_ASENSE_FORMAT_ERROR 0x31
+#define SCSI_ASENSE_MEDIUM_NOT_PRESENT 0x3A
+
+#define SCSI_ASENSEQ_NO_QUALIFIER 0x00
+#define SCSI_ASENSEQ_PEREPHERIAL_DEVICE_WRITE_FAULT 0x00
+#define SCSI_ASENSEQ_FORMAT_COMMAND_FAILED 0x01
+#define SCSI_ASENSEQ_INITIALIZING_COMMAND_REQUIRED 0x02
+#define SCSI_ASENSEQ_OPERATION_IN_PROGRESS 0x07
+
+
+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];
+} PACK_STRUCT_STRUCT scsi_sense_response_t;
+
+typedef struct {
+ uint8_t mode_data_length; //Number of bytes that follow
+ uint8_t medium_type; //0x00 for SBC devices
+ uint8_t device_specifc_paramters; //bit 7 is the write protect bit
+ uint8_t block_descriptor_length; //Length in bytes of all block descriptors in the mode parameter list.
+} PACK_STRUCT_STRUCT scsi_mode_sense6_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 {
+ uint8_t payload_byte_length[4];
+ uint32_t last_block_addr;
+ uint32_t block_size;
+} PACK_STRUCT_STRUCT scsi_read_format_capacity_response_t PACK_STRUCT_END;
+
+PACK_STRUCT_BEGIN typedef struct {
+ uint32_t last_block_addr;
+ uint32_t block_size;
+} PACK_STRUCT_STRUCT scsi_read_capacity10_response_t PACK_STRUCT_END;
+
+PACK_STRUCT_BEGIN typedef struct {
+ uint8_t op_code;
+ uint8_t lun_immed;
+ uint8_t res1;
+ uint8_t res2;
+ uint8_t loej_start;
+ uint8_t control;
+} PACK_STRUCT_STRUCT scsi_start_stop_unit_request_t;
+
+
+
+typedef enum {
+ MSD_WAIT_MODE_NONE = 0,
+ MSD_WAIT_MODE_BULK_IN,
+ MSD_WAIT_MODE_BULK_OUT
+} msd_wait_mode_t;
+
+typedef enum {
+ MSD_STATE_IDLE = 0,
+ MSD_STATE_READ_CMD_BLOCK,
+ MSD_STATE_EJECTED
+} msd_state_t;
+
+typedef enum {
+ USB_MSD_DRIVER_UNINITIALIZED = 0,
+ USB_MSD_DRIVER_ERROR,
+ USB_MSD_DRIVER_OK,
+ USB_MSD_DRIVER_STOPPED,
+ USB_MSD_DRIVER_ERROR_BLK_DEV_NOT_READY,
+} usb_msd_driver_state_t;
+
+typedef struct USBMassStorageDriver USBMassStorageDriver;
+
+struct USBMassStorageDriver {
+ /* Driver Setup Data */
+ USBDriver *usbp;
+ BaseBlockDevice *bbdp;
+ EventSource evt_connected;
+ EventSource evt_ejected;
+ BlockDeviceInfo block_dev_info;
+ bool block_dev_info_valid_flag;
+ usb_msd_driver_state_t driver_state;
+ usbep_t ms_ep_number;
+ uint16_t msd_interface_number;
+ bool_t (*enable_msd_callback)(void);
+ bool_t (*suspend_threads_callback)(void);
+
+ /* Externally modifiable settings */
+ bool_t enable_media_removial;
+ bool_t disable_usb_bus_disconnect_on_eject;
+ BaseSequentialStream *chp; /*For debug logging*/
+
+ /*Internal data for operation of the driver */
+ BinarySemaphore bsem;
+ BinarySemaphore usb_transfer_thread_bsem;
+ BinarySemaphore mass_sorage_thd_bsem;
+ volatile uint32_t trigger_transfer_index;
+
+ volatile bool_t bulk_in_interupt_flag;
+ volatile bool_t bulk_out_interupt_flag;
+
+ struct {
+ scsi_read_format_capacity_response_t format_capacity_response;
+ scsi_read_capacity10_response_t read_capacity10_response;
+ scsi_mode_sense6_response_t mode_sense6_response;
+ uint8_t max_lun_len_buf[1];
+ scsi_inquiry_response_t scsi_inquiry_response;
+ } data;
+
+ msd_state_t state;
+ msd_cbw_t cbw;
+ msd_csw_t csw;
+ scsi_sense_response_t sense;
+
+ volatile bool_t reconfigured_or_reset_event;
+
+ bool_t command_succeeded_flag;
+ bool_t stall_in_endpoint;
+ bool_t stall_out_endpoint;
+
+
+ /*Debugging Information*/
+ volatile uint32_t read_error_count;
+ volatile uint32_t write_error_count;
+ volatile uint32_t read_success_count;
+ volatile uint32_t write_success_count;
+ char *msd_thread_state;
+ char *transfer_thread_state;
+ char *scsi_command_state;
+ volatile uint8_t last_bad_scsi_command;
+
+ /* Externally readable values */
+ volatile bool_t debug_enable_msd;
+ volatile msd_wait_mode_t debug_wait_for_isr;
+
+ WORKING_AREA(waMassStorage, MSD_THREAD_STACK_SIZE);
+ WORKING_AREA(waMassStorageUSBTransfer, MSD_THREAD_STACK_SIZE);
+};
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+usb_msd_driver_state_t msdInit(USBDriver *usbp, BaseBlockDevice *bbdp, USBMassStorageDriver *msdp, const usbep_t ms_ep_number, const uint16_t msd_interface_number);
+usb_msd_driver_state_t msdStart(USBMassStorageDriver *msdp);
+usb_msd_driver_state_t msdStop(USBMassStorageDriver *msdp);
+void msdBulkInCallbackComplete(USBDriver *usbp, usbep_t ep);
+void msdBulkOutCallbackComplete(USBDriver *usbp, usbep_t ep);
+bool_t msdRequestsHook(USBDriver *usbp);
+bool_t msdRequestsHook2(USBDriver *usbp, USBMassStorageDriver *msdp);
+const char* usb_msd_driver_state_t_to_str(const usb_msd_driver_state_t driver_state);
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* HAL_USE_MASS_STORAGE_USB */
+
+#endif /* _USB_MSD_H_ */