summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chip/stm32/build.mk1
-rw-r--r--chip/stm32/registers.h1
-rw-r--r--chip/stm32/usb_ms.c320
-rw-r--r--chip/stm32/usb_ms_scsi.c795
-rw-r--r--common/console_output.c1
-rw-r--r--include/config.h9
-rw-r--r--include/console.h1
-rw-r--r--include/usb_ms.h63
-rw-r--r--include/usb_ms_scsi.h110
9 files changed, 1301 insertions, 0 deletions
diff --git a/chip/stm32/build.mk b/chip/stm32/build.mk
index 65fdb58876..c1ccab2994 100644
--- a/chip/stm32/build.mk
+++ b/chip/stm32/build.mk
@@ -40,4 +40,5 @@ chip-$(CONFIG_PWM)+=pwm.o
chip-$(CONFIG_USB)+=usb.o usb_endpoints.o
chip-$(CONFIG_USB_CONSOLE)+=usb_console.o
chip-$(CONFIG_USB_HID)+=usb_hid.o
+chip-$(CONFIG_USB_MS)+=usb_ms.o usb_ms_scsi.o
chip-$(CONFIG_USB_POWER_DELIVERY)+=usb_pd_phy.o
diff --git a/chip/stm32/registers.h b/chip/stm32/registers.h
index 0b64208e20..e6f98d8001 100644
--- a/chip/stm32/registers.h
+++ b/chip/stm32/registers.h
@@ -1258,6 +1258,7 @@ typedef volatile struct stm32_dma_regs stm32_dma_regs_t;
/* --- MISC --- */
+#define STM32_UNIQUE_ID 0x1ffff7ac
#define STM32_CEC_BASE 0x40007800 /* STM32F100 only */
#define STM32_LCD_BASE 0x40002400
#define STM32_FSMC_BASE 0xA0000000 /* STM32F10x only */
diff --git a/chip/stm32/usb_ms.c b/chip/stm32/usb_ms.c
new file mode 100644
index 0000000000..8c0abbe1d5
--- /dev/null
+++ b/chip/stm32/usb_ms.c
@@ -0,0 +1,320 @@
+/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "clock.h"
+#include "common.h"
+#include "config.h"
+#include "console.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "link_defs.h"
+#include "registers.h"
+#include "task.h"
+#include "timer.h"
+#include "util.h"
+#include "usb.h"
+#include "usb_ms.h"
+#include "usb_ms_scsi.h"
+
+/*
+ * Implements the USB Mass Storage Class specification using the
+ * Bulk-Only Transport (BBB) protocol with the transparent SCSI command set.
+ */
+
+/* Console output macros */
+#define CPUTS(outstr) cputs(CC_USBMS, outstr)
+#define CPRINTF(format, args...) cprintf(CC_USBMS, format, ## args)
+
+/* Mass storage descriptors */
+const struct usb_interface_descriptor USB_IFACE_DESC(USB_IFACE_MS) = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = USB_IFACE_MS,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_MASS_STORAGE,
+ .bInterfaceSubClass = USB_MS_SUBCLASS_SCSI,
+ .bInterfaceProtocol = USB_MS_PROTOCOL_BBB,
+ .iInterface = 0,
+};
+const struct usb_endpoint_descriptor USB_EP_DESC(USB_IFACE_MS, USB_EP_MS_TX) = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN | USB_EP_MS_TX,
+ .bmAttributes = 0x02 /* Bulk */,
+ .wMaxPacketSize = USB_MS_PACKET_SIZE,
+ .bInterval = 0,
+};
+const struct usb_endpoint_descriptor USB_EP_DESC(USB_IFACE_MS, USB_EP_MS_RX) = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_EP_MS_RX,
+ .bmAttributes = 0x02 /* Bulk */,
+ .wMaxPacketSize = USB_MS_PACKET_SIZE,
+ .bInterval = 0,
+};
+
+/* USB mass storage state machine */
+static enum usb_ms_state {
+ USB_MS_STATE_IDLE,
+ USB_MS_STATE_BUSY,
+ USB_MS_STATE_ERROR, /* received an invalid CBW */
+ USB_MS_STATE_PHASE_ERROR,
+} ms_state = USB_MS_STATE_IDLE;
+
+/* Hardware buffers for USB endpoints */
+usb_uint ms_ep_tx[USB_MS_PACKET_SIZE] __usb_ram;
+usb_uint ms_ep_rx[USB_MS_PACKET_SIZE] __usb_ram;
+
+static void ms_tx_reset(void)
+{
+ btable_ep[USB_EP_MS_TX].tx_addr = usb_sram_addr(ms_ep_tx);
+ btable_ep[USB_EP_MS_TX].tx_count = 0;
+ btable_ep[USB_EP_MS_TX].rx_count = 0;
+
+ STM32_USB_EP(USB_EP_MS_TX) =
+ (USB_EP_MS_TX << 0) /* Endpoint Address */ |
+ (2 << 4) /* TX NAK */ |
+ (0 << 9) /* Bulk EP */ |
+ (0 << 12) /* RX Disabled */;
+
+ ms_state = USB_MS_STATE_IDLE;
+ scsi_reset();
+}
+
+static void ms_rx_reset(void)
+{
+ btable_ep[USB_EP_MS_RX].rx_addr = usb_sram_addr(ms_ep_rx);
+ btable_ep[USB_EP_MS_RX].rx_count = 0x8000 |
+ ((USB_MS_PACKET_SIZE/32-1) << 10);
+ btable_ep[USB_EP_MS_RX].tx_count = 0;
+
+ STM32_USB_EP(USB_EP_MS_RX) =
+ (USB_EP_MS_RX << 0) /* Endpoint Address */ |
+ (0 << 4) /* TX Disabled */ |
+ (0 << 9) /* Bulk EP */ |
+ (3 << 12) /* RX VALID */;
+
+ ms_state = USB_MS_STATE_IDLE;
+ scsi_reset();
+}
+
+/*
+ * Construct and send a CSW.
+ */
+static void ms_send_csw(int ms_tag, int ms_xfer_len,
+ int scsi_rv, int scsi_xfer_len)
+{
+ struct usb_ms_csw *resp = (struct usb_ms_csw *) ms_ep_tx;
+
+ /* construct CSW response */
+ resp->signature = UBS_MS_CSW_SIGNATURE;
+ resp->tag = ms_tag;
+ resp->data_residue = (ms_xfer_len > scsi_xfer_len) ?
+ (ms_xfer_len - scsi_xfer_len) :
+ (scsi_xfer_len - ms_xfer_len);
+ if (scsi_rv != SCSI_SENSE_HARDWARE_ERROR)
+ resp->status = (scsi_rv == SCSI_SENSE_NO_SENSE) ?
+ USB_MS_CSW_CMD_PASSED :
+ USB_MS_CSW_CMD_FAILED;
+ else {
+ ms_state = USB_MS_STATE_PHASE_ERROR;
+ resp->status = USB_MS_CSW_CMD_PHASE_ERR;
+ }
+
+ /* set CSW response length */
+ btable_ep[USB_EP_MS_TX].tx_count = USB_MS_CSW_LENGTH;
+
+ /* wait for data to be read */
+ STM32_TOGGLE_EP(USB_EP_MS_TX, EP_TX_MASK, EP_TX_VALID, 0);
+}
+
+/*
+ * Send data already in the output buffer.
+ */
+static void ms_send_data(int ms_xfer_len, int *scsi_xfer_len)
+{
+ /* truncate if necessary */
+ if (btable_ep[USB_EP_MS_TX].tx_count > ms_xfer_len)
+ btable_ep[USB_EP_MS_TX].tx_count = ms_xfer_len;
+
+ /* increment sent data counter with actual length */
+ *scsi_xfer_len += btable_ep[USB_EP_MS_TX].tx_count;
+
+ /* wait for data to be read */
+ STM32_TOGGLE_EP(USB_EP_MS_TX, EP_TX_MASK, EP_TX_VALID, 0);
+}
+
+static void ms_tx(void)
+{
+ task_set_event(TASK_ID_USB_MS, TASK_EVENT_CUSTOM(USB_MS_EVENT_TX), 0);
+
+ STM32_USB_EP(USB_EP_MS_TX) &= EP_MASK;
+}
+
+static void ms_rx(void)
+{
+ task_set_event(TASK_ID_USB_MS, TASK_EVENT_CUSTOM(USB_MS_EVENT_RX), 0);
+
+ STM32_USB_EP(USB_EP_MS_RX) &= EP_MASK;
+}
+USB_DECLARE_EP(USB_EP_MS_TX, ms_tx, ms_tx, ms_tx_reset);
+USB_DECLARE_EP(USB_EP_MS_RX, ms_rx, ms_rx, ms_rx_reset);
+
+static void ms_iface_request(usb_uint *ep0_buf_rx, usb_uint *ep0_buf_tx)
+{
+ uint16_t *req = (uint16_t *) ep0_buf_rx;
+
+ if ((req[0] & (USB_DIR_OUT | USB_RECIP_INTERFACE | USB_TYPE_CLASS)) ==
+ (USB_DIR_OUT | USB_RECIP_INTERFACE | USB_TYPE_CLASS)) {
+ switch (req[0] >> 8) {
+ case USB_MS_REQ_RESET:
+ if (req[1] == 0 && req[2] == USB_IFACE_MS &&
+ req[3] == 0) {
+ ms_rx_reset();
+ }
+ break;
+ case USB_MS_REQ_GET_MAX_LUN:
+ if (req[1] == 0 && req[2] == USB_IFACE_MS &&
+ req[3] == 1) {
+ ep0_buf_tx[0] = SCSI_MAX_LUN;
+ btable_ep[0].tx_count = sizeof(uint8_t);
+ STM32_TOGGLE_EP(USB_EP_CONTROL, EP_TX_RX_MASK,
+ EP_TX_RX_VALID, 0);
+ }
+ break;
+ }
+ } else {
+ CPRINTF("ms stalling: %x %x %x %x\n",
+ req[0], req[1], req[2], req[3]);
+ STM32_TOGGLE_EP(USB_EP_CONTROL, EP_TX_RX_MASK,
+ EP_RX_VALID | EP_TX_STALL, 0);
+ }
+}
+USB_DECLARE_IFACE(USB_IFACE_MS, ms_iface_request);
+
+void ms_task(void)
+{
+ struct usb_ms_cbw *req = (struct usb_ms_cbw *) ms_ep_rx;
+ int scsi_rv, scsi_xfer_len = 0;
+ uint32_t ms_xfer_len = 0, ms_tag = 0;
+ uint8_t ms_dir = 0, evt;
+
+ while (1) {
+ /* wait for event or usb reset */
+ evt = (task_wait_event(-1) & 0xff);
+
+ switch (ms_state) {
+ case USB_MS_STATE_IDLE:
+ /* receiving data */
+ if (evt & USB_MS_EVENT_RX) {
+ /* CBW is not valid or meaningful */
+ if ((btable_ep[USB_EP_MS_RX].rx_count & 0x3ff)
+ != USB_MS_CBW_LENGTH ||
+ req->signature
+ != USB_MS_CBW_SIGNATURE ||
+ req->LUN & 0xf0 ||
+ req->length & 0xe0 ||
+ req->LUN > SCSI_MAX_LUN) {
+
+ ms_state = USB_MS_STATE_ERROR;
+ STM32_TOGGLE_EP(USB_EP_MS_TX,
+ EP_TX_MASK, EP_TX_STALL, 0);
+ STM32_TOGGLE_EP(USB_EP_MS_RX,
+ EP_RX_MASK, EP_RX_STALL, 0);
+ break;
+ }
+
+ /* have new packet */
+ ms_state = USB_MS_STATE_BUSY;
+
+ /* record packet details */
+ ms_tag = req->tag;
+ ms_xfer_len = req->data_transfer_length;
+ ms_dir = req->flags;
+ scsi_xfer_len = 0;
+
+ /* parse message and get next state */
+ scsi_rv = scsi_parse(req->command_block,
+ req->length);
+ if (scsi_rv == SCSI_STATUS_CONTINUE) {
+ if (ms_dir & USB_MS_CBW_DATA_IN)
+ /* send out data */
+ ms_send_data(ms_xfer_len,
+ &scsi_xfer_len);
+ else
+ /* receive more data */
+ STM32_TOGGLE_EP(USB_EP_MS_RX,
+ EP_RX_MASK, EP_RX_VALID, 0);
+ } else {
+ /* send message response */
+ ms_state = USB_MS_STATE_IDLE;
+ ms_send_csw(ms_tag, ms_xfer_len,
+ scsi_rv, scsi_xfer_len);
+ }
+ } else if (evt & USB_MS_EVENT_TX) {
+ /* just sent CSW, wait for next CBW */
+ STM32_TOGGLE_EP(USB_EP_MS_RX, EP_RX_MASK,
+ EP_RX_VALID, 0);
+ }
+ break;
+ case USB_MS_STATE_BUSY:
+ /* receiving data */
+ if (evt & USB_MS_EVENT_RX) {
+ /*
+ * received at least two CBW's in a row,
+ * go to error state
+ */
+ if (ms_dir & USB_MS_CBW_DATA_IN) {
+ ms_state = USB_MS_STATE_ERROR;
+ STM32_TOGGLE_EP(USB_EP_MS_TX,
+ EP_TX_MASK, EP_TX_STALL, 0);
+ STM32_TOGGLE_EP(USB_EP_MS_RX,
+ EP_RX_MASK, EP_RX_STALL, 0);
+ break;
+ }
+ /* receive data */
+ scsi_xfer_len +=
+ (btable_ep[USB_EP_MS_RX].rx_count & 0x3ff);
+ scsi_rv = scsi_parse(NULL, 0);
+ if (scsi_rv != SCSI_STATUS_CONTINUE) {
+ ms_state = USB_MS_STATE_IDLE;
+ ms_send_csw(ms_tag, ms_xfer_len,
+ scsi_rv, scsi_xfer_len);
+ }
+
+ /* wait for more data */
+ STM32_TOGGLE_EP(USB_EP_MS_RX,
+ EP_RX_MASK, EP_RX_VALID, 0);
+ } else if (evt & USB_MS_EVENT_TX) {
+ /* reparse message and get next state */
+ scsi_rv = scsi_parse(req->command_block,
+ req->length);
+ if (scsi_rv == SCSI_STATUS_CONTINUE) {
+ ms_send_data(ms_xfer_len,
+ &scsi_xfer_len);
+ } else {
+ ms_state = USB_MS_STATE_IDLE;
+ ms_send_csw(ms_tag, ms_xfer_len,
+ scsi_rv, scsi_xfer_len);
+ }
+ }
+ break;
+ case USB_MS_STATE_ERROR:
+ /* maintain error state until reset recovery */
+ break;
+ case USB_MS_STATE_PHASE_ERROR:
+ CPUTS("phase error!\n");
+
+ STM32_TOGGLE_EP(USB_EP_MS_TX, EP_TX_MASK,
+ EP_TX_STALL, 0);
+ STM32_TOGGLE_EP(USB_EP_MS_RX, EP_RX_MASK,
+ EP_RX_STALL, 0);
+ break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/chip/stm32/usb_ms_scsi.c b/chip/stm32/usb_ms_scsi.c
new file mode 100644
index 0000000000..feaf38e557
--- /dev/null
+++ b/chip/stm32/usb_ms_scsi.c
@@ -0,0 +1,795 @@
+/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "clock.h"
+#include "common.h"
+#include "config.h"
+#include "console.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "link_defs.h"
+#include "registers.h"
+#include "task.h"
+#include "timer.h"
+#include "spi.h"
+#include "spi_flash.h"
+#include "util.h"
+#include "usb.h"
+#include "usb_ms.h"
+#include "usb_ms_scsi.h"
+
+/*
+ * Implements the SCSI-3 Block Commands (SBC-3) standard for
+ * Direct Access Block Devices with respect to the
+ * SCSI Primary Commands - 4 (SPC-4) standard.
+ *
+ * Note: Not all SPC-4 mandatory commands implemented; only LUN 0 supported.
+ */
+
+/* Command operation codes */
+#define SCSI_INQUIRY 0x12
+#define SCSI_MODE_SENSE6 0x1a
+#define SCSI_READ10 0x28
+#define SCSI_READ_CAPACITY10 0x25
+#define SCSI_READ_FORMAT_CAPACITIES 0x23
+#define SCSI_REPORT_LUNS 0xa0
+#define SCSI_REQUEST_SENSE 0x03
+#define SCSI_START_STOP_UNIT 0x1b
+#define SCSI_SYNCHRONIZE_CACHE10 0x35
+#define SCSI_TEST_UNIT_READY 0x00
+#define SCSI_WRITE10 0x2a
+
+#define SCSI_STANDARD_INQUIRY_SIZE 62
+/* Standard inquiry response */
+static const uint8_t scsi_standard_inquiry[] = {
+ 0x00, /* Peripheral Qualifier | Peripheral Device Type (SBC-3) */
+ (1 << 7), /* RMB | LU_CONG | Reserved */
+ 0x06, /* Version (SPC-4) */
+ 0x02, /* Reserved | Reserved | NormACA | HiSup | Response Data Format */
+ (SCSI_STANDARD_INQUIRY_SIZE - 5), /* Additional Length */
+ 0x00, /* SCCS | ACC | TPGS | 3PC | Reserved | Protect */
+ 0x00, /* Obsolete | EncServ | VS | MultiP |
+ Obsolete | Reserved | Reserved | Addr16 */
+ 0x00, /* Obsolete | Reserved | WBUS16 | Syncs |
+ Obsolete | Reserved | CmdQue | VS */
+ 'G', 'O', 'O', 'G', 'L', 'E', '\0', '\0', /* Vendor ID */
+ 'S', 'P', 'I', 'F', 'l', 'a', 's', 'h', /* Product ID */
+ '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', /* Lot Number */
+ '1', '.', '0' , '0', /* Product Revision Level */
+ '\0', '\0', '\0', '\0', /* Vendor Specific */
+ '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', /* Vendor Specific */
+ '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', /* Vendor Specific */
+ 0x00, /* Reserved | Clocking | QAS | IUS */
+ 0x00, /* Reserved */
+ SCSI_VERSION_SBC3, /* Version 1 */
+ SCSI_VERSION_SPC4, /* Version 2 */
+};
+BUILD_ASSERT(sizeof(scsi_standard_inquiry) == SCSI_STANDARD_INQUIRY_SIZE);
+
+#define SCSI_VPD_SUPPORTED_PAGES_SIZE 7
+/* Vital product data (VPD) response for supported VPD pages */
+static const uint8_t scsi_vpd_supported_pages[] = {
+ 0x00, /* Peripheral Qualifier | Peripheral Device Type (SBC-3) */
+ SCSI_VPD_CODE_SUPPORTED_PAGES, /* Page Code */
+ 0x00, /* Page Length */
+ (SCSI_VPD_SUPPORTED_PAGES_SIZE - 4), /* Page Length */
+ SCSI_VPD_CODE_SUPPORTED_PAGES, /* Supported VPD Pages */
+ SCSI_VPD_CODE_SERIAL_NUMBER, /* Serial Number Page */
+ SCSI_VPD_CODE_DEVICE_ID, /* Device ID Page */
+};
+BUILD_ASSERT(sizeof(scsi_vpd_supported_pages) == SCSI_VPD_SUPPORTED_PAGES_SIZE);
+
+#define SCSI_VPD_SERIAL_NUMBER_SIZE 17
+/* Vital product data (VPD) response for serial number page */
+static const uint8_t scsi_vpd_serial_number[] = {
+ 0x00, /* Peripheral Qualifier | Peripheral Device Type (SBC-3) */
+ SCSI_VPD_CODE_SERIAL_NUMBER, /* Page Code */
+ 0x00, /* Page Length */
+ (SCSI_VPD_SERIAL_NUMBER_SIZE - 4), /* Page Length */
+ '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', /* Serial Number */
+ '\0', '\0', '\0', '\0', /* Serial Number */
+ '\0', /* Serial Number */
+};
+BUILD_ASSERT(sizeof(scsi_vpd_serial_number) == SCSI_VPD_SERIAL_NUMBER_SIZE);
+
+#define SCSI_VPD_DESIGNATOR_LENGTH 21
+#define SCSI_VPD_DEVICE_ID_SIZE (SCSI_VPD_DESIGNATOR_LENGTH + 4)
+/* Vital product data (VPD) response for device ID page */
+static const uint8_t scsi_vpd_device_id[] = {
+ 0x00, /* Peripheral Qualifier | Peripheral Device Type (SBC-3) */
+ SCSI_VPD_CODE_DEVICE_ID, /* Page Code */
+ 0x00, /* Designation Descriptor Length */
+ (SCSI_VPD_DEVICE_ID_SIZE - 3), /* Designation Descriptor Length */
+ 0x02, /* Protocol Identifier | Code Set (ASCII) */
+ 0x01, /* PIV | Reserved | Association | Designator Type (T10) */
+ 0x00, /* Reserved */
+ (SCSI_VPD_DESIGNATOR_LENGTH - 3), /* Designator Length */
+ 'G', 'O', 'O', 'G', 'L', 'E', '\0', '\0', /* Vendor ID */
+ 'S', 'P', 'I', 'F', 'l', 'a', 's', 'h', /* Vendor Specific ID */
+ '\0', /* Vendor Specific ID */
+};
+BUILD_ASSERT(sizeof(scsi_vpd_device_id) == SCSI_VPD_DEVICE_ID_SIZE);
+
+/* Capacity list response for read format capacities */
+static const struct scsi_capacity_list_response scsi_capacity_list = {
+ .header = 0x08, /* Reserved | List Length */
+ /* Number of Blocks */
+ .blocks = (CONFIG_SPI_FLASH_SIZE / SCSI_BLOCK_SIZE_BYTES),
+ /* Reserved | Descriptor Code | Block Length */
+ .block_length = (0x02 << 24) | SCSI_BLOCK_SIZE_BYTES,
+};
+
+/* Current state of SCSI state machine */
+static enum usb_ms_scsi_state state = USB_MS_SCSI_STATE_IDLE;
+static int buffer;
+static int offset;
+static int bytes;
+static uint8_t op;
+
+/* Current sense key */
+static struct scsi_sense_entry scsi_sense_data;
+
+/* Local buffer for caching */
+static uint8_t temp_buf[CONFIG_USB_MS_BUFFER_SIZE];
+
+static void scsi_sense_code(uint8_t sense, uint16_t code)
+{
+ scsi_sense_data.key = sense;
+ scsi_sense_data.ASC = SCSI_SENSE_CODE_ASC(code);
+ scsi_sense_data.ASCQ = SCSI_SENSE_CODE_ASCQ(code);
+}
+
+static int scsi_verify_cdb6(uint8_t *block, uint8_t in_len)
+{
+ /* message too short */
+ if (in_len < SCSI_CDB6_SIZE) {
+ scsi_sense_code(SCSI_SENSE_ILLEGAL_REQUEST,
+ SCSI_SENSE_CODE_NONE);
+ return -1;
+ }
+
+ /* NACA bit not supported */
+ if (block[5] & 0x4) {
+ scsi_sense_code(SCSI_SENSE_ILLEGAL_REQUEST,
+ SCSI_SENSE_CODE_INVALID_FIELD_IN_CDB);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int scsi_verify_cdb10(uint8_t *block, uint8_t in_len)
+{
+ /* message too short */
+ if (in_len < SCSI_CDB10_SIZE) {
+ scsi_sense_code(SCSI_SENSE_ILLEGAL_REQUEST,
+ SCSI_SENSE_CODE_NONE);
+ return -1;
+ }
+
+ /* NACA bit not supported */
+ if (block[9] & 0x4) {
+ scsi_sense_code(SCSI_SENSE_ILLEGAL_REQUEST,
+ SCSI_SENSE_CODE_INVALID_FIELD_IN_CDB);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Required by SPC-4.
+ */
+static void scsi_inquiry(uint8_t *block, uint8_t in_len)
+{
+ if (state == USB_MS_SCSI_STATE_PARSE) {
+ state = USB_MS_SCSI_STATE_DATA_OUT;
+
+ /* terminate if fail to verify */
+ if (scsi_verify_cdb6(block, in_len))
+ return;
+
+ /* EVPD bit set */
+ if (block[1] & 0x1) {
+ /* lookup VPD page */
+ switch (block[2]) {
+ case SCSI_VPD_CODE_SUPPORTED_PAGES:
+ /* return supported pages */
+ memcpy_usbram(ms_ep_tx,
+ scsi_vpd_supported_pages,
+ sizeof(scsi_vpd_supported_pages));
+ /* truncate response */
+ btable_ep[USB_EP_MS_TX].tx_count =
+ MIN(block[3] << 8 | block[4],
+ sizeof(scsi_vpd_supported_pages));
+ break;
+ case SCSI_VPD_CODE_SERIAL_NUMBER:
+ /* return serial number response */
+ memcpy_usbram(ms_ep_tx,
+ scsi_vpd_serial_number,
+ sizeof(scsi_vpd_serial_number));
+
+ /* copy STM32 LOT_NUM for serial number */
+ memcpy(temp_buf,
+ ((uint8_t *) STM32_UNIQUE_ID) + 4 + 1,
+ 7 * sizeof(uint8_t));
+ /* copy STM32 WAF_NUM for serial number */
+ temp_buf[7] = ((uint8_t *) STM32_UNIQUE_ID)[4];
+ /* copy STM32 UID for serial number */
+ memcpy(temp_buf + 8,
+ ((uint8_t *) STM32_UNIQUE_ID),
+ 4 * sizeof(uint8_t));
+
+ /* copy actual serial number */
+ memcpy_usbram((usb_uint *)
+ (((uint8_t *) ms_ep_tx) + 4),
+ temp_buf,
+ 12 * sizeof(uint8_t));
+
+ /* truncate response */
+ btable_ep[USB_EP_MS_TX].tx_count =
+ MIN(block[3] << 8 | block[4],
+ sizeof(scsi_vpd_serial_number));
+ break;
+ case SCSI_VPD_CODE_DEVICE_ID:
+ /* return device id */
+ memcpy_usbram(ms_ep_tx,
+ scsi_vpd_device_id,
+ sizeof(scsi_vpd_device_id));
+ /* truncate response */
+ btable_ep[USB_EP_MS_TX].tx_count =
+ MIN(block[3] << 8 | block[4],
+ sizeof(scsi_vpd_device_id));
+ break;
+ default:
+ /* not supported */
+ return scsi_sense_code(
+ SCSI_SENSE_ILLEGAL_REQUEST,
+ SCSI_SENSE_CODE_INVALID_FIELD_IN_CDB);
+ break;
+ }
+ /* EVPD not set but page code set */
+ } else if (block[2]) {
+ return scsi_sense_code(
+ SCSI_SENSE_ILLEGAL_REQUEST,
+ SCSI_SENSE_CODE_INVALID_FIELD_IN_CDB);
+ } else {
+ /* return standard inquiry data */
+ memcpy_usbram(ms_ep_tx, scsi_standard_inquiry,
+ sizeof(scsi_standard_inquiry));
+
+ /* copy STM32 LOT_NUM for vendor specific id */
+ memcpy_usbram((usb_uint *)
+ (((uint8_t *) ms_ep_tx) + 24),
+ ((uint8_t *) STM32_UNIQUE_ID) + 4 + 1,
+ 7 * sizeof(uint8_t));
+
+ /* truncate response */
+ btable_ep[USB_EP_MS_TX].tx_count =
+ MIN(block[3] << 8 | block[4],
+ sizeof(scsi_standard_inquiry));
+ }
+
+ } else if (state == USB_MS_SCSI_STATE_DATA_OUT)
+ state = USB_MS_SCSI_STATE_REPLY;
+
+ return scsi_sense_code(SCSI_SENSE_NO_SENSE,
+ SCSI_SENSE_CODE_NONE);
+}
+
+static void scsi_mode_sense6(uint8_t *block, uint8_t in_len)
+{
+ uint8_t response[4];
+
+ if (state == USB_MS_SCSI_STATE_PARSE) {
+ state = USB_MS_SCSI_STATE_DATA_OUT;
+
+ /* terminate if fail to verify */
+ if (scsi_verify_cdb6(block, in_len))
+ return;
+
+ /* response exceeds allocation length */
+ if (block[4] < sizeof(response))
+ return scsi_sense_code(SCSI_SENSE_ILLEGAL_REQUEST,
+ SCSI_SENSE_CODE_INVALID_FIELD_IN_CDB);
+
+ memset(response, 0, sizeof(response));
+ /* set WP bit if necessary */
+ response[2] = spi_flash_check_protect(0,
+ CONFIG_SPI_FLASH_SIZE) ?
+ (1 << 7) : 0;
+
+ memcpy_usbram(ms_ep_tx, (uint8_t *) response,
+ sizeof(response));
+ btable_ep[USB_EP_MS_TX].tx_count = sizeof(response);
+ } else if (state == USB_MS_SCSI_STATE_DATA_OUT)
+ state = USB_MS_SCSI_STATE_REPLY;
+
+ return scsi_sense_code(SCSI_SENSE_NO_SENSE,
+ SCSI_SENSE_CODE_NONE);
+}
+
+/*
+ * Required by SPC-4.
+ */
+static void scsi_read10(uint8_t *block, uint8_t in_len)
+{
+ int rv;
+ int read_len;
+
+ if (state == USB_MS_SCSI_STATE_PARSE) {
+ state = USB_MS_SCSI_STATE_DATA_OUT;
+
+ /* terminate if fail to verify */
+ if (scsi_verify_cdb10(block, in_len))
+ return;
+
+ /* RELADR bit not supported */
+ if (block[1] & 0x1)
+ return scsi_sense_code(SCSI_SENSE_ILLEGAL_REQUEST,
+ SCSI_SENSE_CODE_INVALID_FIELD_IN_CDB);
+
+ offset = SCSI_BLOCK_SIZE_BYTES *
+ (block[2] << 24 | block[3] << 16
+ | block[4] << 8 | block[5]);
+ bytes = SCSI_BLOCK_SIZE_BYTES *
+ (block[7] << 8 | block[8]);
+
+ /* Wait for any previous operation to complete */
+ rv = spi_flash_wait();
+ if (rv == EC_ERROR_TIMEOUT)
+ return scsi_sense_code(
+ SCSI_SENSE_HARDWARE_ERROR,
+ SCSI_SENSE_CODE_TIMEOUT);
+ }
+
+ if (state == USB_MS_SCSI_STATE_DATA_OUT) {
+ /* nothing left to read */
+ if (!bytes) {
+ state = USB_MS_SCSI_STATE_REPLY;
+ return scsi_sense_code(SCSI_SENSE_NO_SENSE,
+ SCSI_SENSE_CODE_NONE);
+ }
+
+ /* read in multiples of USB_MS_PACKET_SIZE, then bytes */
+ read_len = MIN(bytes, USB_MS_PACKET_SIZE);
+
+ rv = spi_flash_read(temp_buf, offset, read_len);
+ /* invalid address */
+ if (rv == EC_ERROR_INVAL)
+ return scsi_sense_code(SCSI_SENSE_ILLEGAL_REQUEST,
+ SCSI_SENSE_CODE_LBA_OUT_OF_RANGE);
+ else if (rv != EC_SUCCESS)
+ return scsi_sense_code(SCSI_SENSE_HARDWARE_ERROR,
+ SCSI_SENSE_CODE_UNRECOVERED_READ_ERROR);
+
+ /* temp buffer for chip addressing issues */
+ memcpy_usbram(ms_ep_tx, temp_buf, read_len);
+ offset += read_len;
+ bytes -= read_len;
+
+ btable_ep[USB_EP_MS_TX].tx_count = read_len;
+ }
+
+ return scsi_sense_code(SCSI_SENSE_NO_SENSE,
+ SCSI_SENSE_CODE_NONE);
+}
+
+/*
+ * Required by SPC-4.
+ */
+static void scsi_read_capacity10(uint8_t *block, uint8_t in_len)
+{
+ uint32_t response[2];
+
+ if (state == USB_MS_SCSI_STATE_PARSE) {
+ state = USB_MS_SCSI_STATE_DATA_OUT;
+
+ /* terminate if fail to verify */
+ if (scsi_verify_cdb10(block, in_len))
+ return;
+
+ /* RELADR bit not supported */
+ if (block[1] & 0x1)
+ return scsi_sense_code(SCSI_SENSE_ILLEGAL_REQUEST,
+ SCSI_SENSE_CODE_INVALID_FIELD_IN_CDB);
+
+ /* PMI bit or LBA not supported */
+ if (block[2] | block[3] | block[4] |
+ block[5] | (block[8] & 0x1))
+ return scsi_sense_code(SCSI_SENSE_ILLEGAL_REQUEST,
+ SCSI_SENSE_CODE_INVALID_FIELD_IN_CDB);
+
+ /* compute LBA and block size, send in big endian */
+ response[0] = __builtin_bswap32((CONFIG_SPI_FLASH_SIZE /
+ SCSI_BLOCK_SIZE_BYTES) - 1);
+ response[1] = __builtin_bswap32(SCSI_BLOCK_SIZE_BYTES);
+
+ memcpy_usbram(ms_ep_tx, (uint8_t *) response,
+ sizeof(response));
+ btable_ep[USB_EP_MS_TX].tx_count = sizeof(response);
+ } else if (state == USB_MS_SCSI_STATE_DATA_OUT)
+ state = USB_MS_SCSI_STATE_REPLY;
+
+ return scsi_sense_code(SCSI_SENSE_NO_SENSE,
+ SCSI_SENSE_CODE_NONE);
+}
+
+/*
+ * Used by UFI. Required by Windows XP.
+ */
+static void scsi_read_format_capacities(uint8_t *block, uint8_t in_len)
+{
+ if (state == USB_MS_SCSI_STATE_PARSE) {
+ state = USB_MS_SCSI_STATE_DATA_OUT;
+
+ /* terminate if fail to verify */
+ if (scsi_verify_cdb10(block, in_len))
+ return;
+
+ memcpy_usbram(ms_ep_tx, (uint8_t *) &scsi_capacity_list,
+ sizeof(scsi_capacity_list));
+ btable_ep[USB_EP_MS_TX].tx_count = sizeof(scsi_capacity_list);
+ } else if (state == USB_MS_SCSI_STATE_DATA_OUT)
+ state = USB_MS_SCSI_STATE_REPLY;
+
+ return scsi_sense_code(SCSI_SENSE_NO_SENSE,
+ SCSI_SENSE_CODE_NONE);
+}
+
+/*
+ * Required by SPC-4.
+ */
+static void scsi_report_luns(uint8_t *block, uint8_t in_len)
+{
+ uint32_t response[16];
+
+ if (state == USB_MS_SCSI_STATE_PARSE) {
+ state = USB_MS_SCSI_STATE_DATA_OUT;
+
+ /* terminate if fail to verify */
+ if (scsi_verify_cdb6(block, in_len))
+ return;
+
+ /* response exceeds allocation length */
+ if ((block[3] << 8 | block[4]) < sizeof(response))
+ return scsi_sense_code(SCSI_SENSE_ILLEGAL_REQUEST,
+ SCSI_SENSE_CODE_INVALID_FIELD_IN_CDB);
+
+ memset(response, 0, sizeof(response));
+ /* one LUN in the list */
+ response[3] = 1;
+
+ /* return response */
+ memcpy_usbram(ms_ep_tx, (uint8_t *) response,
+ sizeof(response));
+ btable_ep[USB_EP_MS_TX].tx_count = sizeof(response);
+ } else if (state == USB_MS_SCSI_STATE_DATA_OUT)
+ state = USB_MS_SCSI_STATE_REPLY;
+
+ return scsi_sense_code(SCSI_SENSE_NO_SENSE, SCSI_SENSE_CODE_NONE);
+}
+
+/*
+ * Required by SPC-4.
+ */
+static void scsi_request_sense(uint8_t *block, uint8_t in_len)
+{
+ uint8_t response[18];
+
+ if (state == USB_MS_SCSI_STATE_PARSE) {
+ state = USB_MS_SCSI_STATE_DATA_OUT;
+
+ /* terminate if fail to verify */
+ if (scsi_verify_cdb6(block, in_len))
+ return;
+
+ /* response exceeds allocation length */
+ if (block[4] < sizeof(response))
+ return scsi_sense_code(SCSI_SENSE_ILLEGAL_REQUEST,
+ SCSI_SENSE_CODE_INVALID_FIELD_IN_CDB);
+
+ memset(response, 0, sizeof(response));
+ /* Valid | Response Code */
+ response[0] = SCSI_SENSE_RESPONSE_CURRENT;
+ /* Filemark | EOM | ILI | SDAT_OVFL | Sense Key */
+ response[2] = scsi_sense_data.key;
+ /* Additional Sense Length */
+ response[7] = ARRAY_SIZE(response) - 7;
+ /* Additional Sense Code */
+ response[12] = scsi_sense_data.ASC;
+ /* Additional Sense Code Qualifier */
+ response[13] = scsi_sense_data.ASCQ;
+
+ /* return fixed format sense data */
+ memcpy_usbram(ms_ep_tx, response, sizeof(response));
+ btable_ep[USB_EP_MS_TX].tx_count = sizeof(response);
+ } else if (state == USB_MS_SCSI_STATE_DATA_OUT)
+ state = USB_MS_SCSI_STATE_REPLY;
+
+ return scsi_sense_code(SCSI_SENSE_NO_SENSE, SCSI_SENSE_CODE_NONE);
+}
+
+static void scsi_start_stop_unit(uint8_t *block, uint8_t in_len)
+{
+ state = USB_MS_SCSI_STATE_REPLY;
+
+ /* terminate if fail to verify */
+ if (scsi_verify_cdb6(block, in_len))
+ return;
+
+ /* do nothing */
+
+ return scsi_sense_code(SCSI_SENSE_NO_SENSE, SCSI_SENSE_CODE_NONE);
+}
+
+static void scsi_synchronize_cache10(uint8_t *block, uint8_t in_len)
+{
+ state = USB_MS_SCSI_STATE_REPLY;
+
+ /* terminate if fail to verify */
+ if (scsi_verify_cdb10(block, in_len))
+ return;
+
+ /* nothing to synchronize, return success */
+ return scsi_sense_code(SCSI_SENSE_NO_SENSE, SCSI_SENSE_CODE_NONE);
+}
+
+/*
+ * Required by SPC-4.
+ */
+static void scsi_test_unit_ready(uint8_t *block, uint8_t in_len)
+{
+ state = USB_MS_SCSI_STATE_REPLY;
+
+ /* terminate if fail to verify */
+ if (scsi_verify_cdb6(block, in_len))
+ return;
+
+ if (spi_enable(1))
+ return scsi_sense_code(SCSI_SENSE_NOT_READY,
+ SCSI_SENSE_CODE_NOT_READY);
+
+ return scsi_sense_code(SCSI_SENSE_NO_SENSE, SCSI_SENSE_CODE_NONE);
+}
+
+/*
+ * Required by SPC-4.
+ */
+static void scsi_write10(uint8_t *block, uint8_t in_len)
+{
+ int rv;
+ int write_len;
+
+ if (state == USB_MS_SCSI_STATE_PARSE) {
+ state = USB_MS_SCSI_STATE_DATA_IN;
+
+ /* terminate if fail to verify */
+ if (scsi_verify_cdb10(block, in_len))
+ return;
+
+ /* RELADR bit not supported */
+ if (block[1] & 0x1)
+ return scsi_sense_code(SCSI_SENSE_ILLEGAL_REQUEST,
+ SCSI_SENSE_CODE_INVALID_FIELD_IN_CDB);
+
+ buffer = 0;
+ offset = SCSI_BLOCK_SIZE_BYTES *
+ (block[2] << 24 | block[3] << 16 |
+ block[4] << 8 | block[5]);
+ bytes = SCSI_BLOCK_SIZE_BYTES * (block[7] << 8 | block[8]);
+
+ /* Chip has protection */
+ if (spi_flash_check_protect(offset, bytes))
+ return scsi_sense_code(SCSI_SENSE_DATA_PROTECT,
+ SCSI_SENSE_CODE_WRITE_PROTECTED);
+
+ /* Wait for any previous operation to complete */
+ rv = spi_flash_wait();
+ if (rv == EC_ERROR_TIMEOUT)
+ return scsi_sense_code(
+ SCSI_SENSE_HARDWARE_ERROR,
+ SCSI_SENSE_CODE_TIMEOUT);
+
+ rv = spi_flash_erase(offset, bytes);
+ /* invalid address */
+ if (rv == EC_ERROR_INVAL)
+ return scsi_sense_code(SCSI_SENSE_ILLEGAL_REQUEST,
+ SCSI_SENSE_CODE_LBA_OUT_OF_RANGE);
+ else if (rv != EC_SUCCESS)
+ return scsi_sense_code(SCSI_SENSE_HARDWARE_ERROR,
+ SCSI_SENSE_CODE_UNRECOVERED_READ_ERROR);
+ } else if (state == USB_MS_SCSI_STATE_DATA_IN) {
+ /* write whatever was received */
+ write_len = MIN(bytes,
+ btable_ep[USB_EP_MS_RX].rx_count & 0x3ff);
+ ASSERT(write_len <= SPI_FLASH_MAX_WRITE_SIZE);
+
+#if CONFIG_USB_MS_BUFFER_SIZE != USB_MS_PACKET_SIZE
+ /* perform write only when local buffer is over full */
+ if (buffer + write_len > CONFIG_USB_MS_BUFFER_SIZE) {
+ /* Wait for previous operation to complete */
+ rv = spi_flash_wait();
+ if (rv == EC_ERROR_TIMEOUT)
+ return scsi_sense_code(
+ SCSI_SENSE_HARDWARE_ERROR,
+ SCSI_SENSE_CODE_TIMEOUT);
+
+ rv = spi_flash_write(offset,
+ CONFIG_USB_MS_BUFFER_SIZE, temp_buf);
+ if (rv == EC_ERROR_INVAL)
+ return scsi_sense_code(
+ SCSI_SENSE_ILLEGAL_REQUEST,
+ SCSI_SENSE_CODE_LBA_OUT_OF_RANGE);
+ else if (rv != EC_SUCCESS)
+ return scsi_sense_code(
+ SCSI_SENSE_HARDWARE_ERROR,
+ SCSI_SENSE_CODE_UNRECOVERED_READ_ERROR);
+
+ offset += buffer;
+ bytes -= buffer;
+
+ buffer = 0;
+ }
+
+ /* copy data to local buffer */
+ memcpy(temp_buf + buffer, (uint8_t *) ms_ep_rx, write_len);
+ buffer += write_len;
+
+ /* on last write */
+ if (bytes == buffer) {
+ /* Wait for previous operation to complete */
+ rv = spi_flash_wait();
+ if (rv == EC_ERROR_TIMEOUT)
+ return scsi_sense_code(
+ SCSI_SENSE_HARDWARE_ERROR,
+ SCSI_SENSE_CODE_TIMEOUT);
+
+ rv = spi_flash_write(offset, buffer, temp_buf);
+ if (rv == EC_ERROR_INVAL)
+ return scsi_sense_code(
+ SCSI_SENSE_ILLEGAL_REQUEST,
+ SCSI_SENSE_CODE_LBA_OUT_OF_RANGE);
+ else if (rv == EC_ERROR_ACCESS_DENIED)
+ return scsi_sense_code(SCSI_SENSE_DATA_PROTECT,
+ SCSI_SENSE_CODE_WRITE_PROTECTED);
+ else if (rv != EC_SUCCESS)
+ return scsi_sense_code(
+ SCSI_SENSE_HARDWARE_ERROR,
+ SCSI_SENSE_CODE_UNRECOVERED_READ_ERROR);
+
+ /* Wait for last write to complete */
+ rv = spi_flash_wait();
+ if (rv == EC_ERROR_TIMEOUT)
+ return scsi_sense_code(
+ SCSI_SENSE_HARDWARE_ERROR,
+ SCSI_SENSE_CODE_TIMEOUT);
+
+ offset += buffer;
+ bytes -= buffer;
+
+ buffer = 0;
+ /* received too much data */
+ } else if (bytes < buffer)
+ return scsi_sense_code(SCSI_SENSE_ILLEGAL_REQUEST,
+ SCSI_SENSE_CODE_LBA_OUT_OF_RANGE);
+#else
+ memcpy(temp_buf, (uint8_t *) ms_ep_rx, write_len);
+
+ /* Wait for previous operation to complete */
+ rv = spi_flash_wait();
+ if (rv == EC_ERROR_TIMEOUT)
+ return scsi_sense_code(
+ SCSI_SENSE_HARDWARE_ERROR,
+ SCSI_SENSE_CODE_TIMEOUT);
+
+ rv = spi_flash_write(offset, write_len, temp_buf);
+ if (rv == EC_ERROR_INVAL)
+ return scsi_sense_code(
+ SCSI_SENSE_ILLEGAL_REQUEST,
+ SCSI_SENSE_CODE_LBA_OUT_OF_RANGE);
+ else if (rv == EC_ERROR_ACCESS_DENIED)
+ return scsi_sense_code(SCSI_SENSE_DATA_PROTECT,
+ SCSI_SENSE_CODE_WRITE_PROTECTED);
+ else if (rv != EC_SUCCESS)
+ return scsi_sense_code(
+ SCSI_SENSE_HARDWARE_ERROR,
+ SCSI_SENSE_CODE_UNRECOVERED_READ_ERROR);
+
+ offset += write_len;
+ bytes -= write_len;
+#endif
+
+ /* nothing left to write */
+ if (!bytes)
+ state = USB_MS_SCSI_STATE_REPLY;
+ }
+ return scsi_sense_code(SCSI_SENSE_NO_SENSE, SCSI_SENSE_CODE_NONE);
+}
+
+void scsi_reset(void)
+{
+ op = 0;
+
+ offset = 0;
+ bytes = 0;
+ buffer = 0;
+
+ state = USB_MS_SCSI_STATE_IDLE;
+ /* set status to success by default */
+ scsi_sense_code(SCSI_SENSE_NO_SENSE, SCSI_SENSE_CODE_NONE);
+}
+
+int scsi_parse(uint8_t *block, uint8_t in_len)
+{
+ /* set new operation */
+ if (state == USB_MS_SCSI_STATE_IDLE) {
+ state = USB_MS_SCSI_STATE_PARSE;
+
+ op = block[0];
+ }
+
+ /* skip operation if sending reply */
+ if (state != USB_MS_SCSI_STATE_REPLY) {
+ switch (op) {
+ case SCSI_INQUIRY:
+ scsi_inquiry(block, in_len);
+ break;
+ case SCSI_MODE_SENSE6:
+ scsi_mode_sense6(block, in_len);
+ break;
+ case SCSI_READ10:
+ scsi_read10(block, in_len);
+ break;
+ case SCSI_READ_CAPACITY10:
+ scsi_read_capacity10(block, in_len);
+ break;
+ case SCSI_READ_FORMAT_CAPACITIES:
+ scsi_read_format_capacities(block, in_len);
+ break;
+ case SCSI_REPORT_LUNS:
+ scsi_report_luns(block, in_len);
+ break;
+ case SCSI_REQUEST_SENSE:
+ scsi_request_sense(block, in_len);
+ break;
+ case SCSI_START_STOP_UNIT:
+ scsi_start_stop_unit(block, in_len);
+ break;
+ case SCSI_SYNCHRONIZE_CACHE10:
+ scsi_synchronize_cache10(block, in_len);
+ break;
+ case SCSI_TEST_UNIT_READY:
+ scsi_test_unit_ready(block, in_len);
+ break;
+ case SCSI_WRITE10:
+ scsi_write10(block, in_len);
+ break;
+ default:
+ state = USB_MS_SCSI_STATE_REPLY;
+ scsi_sense_code(SCSI_SENSE_ILLEGAL_REQUEST,
+ SCSI_SENSE_CODE_INVALID_COMMAND_OPERATION_CODE);
+ break;
+ }
+ }
+
+ /* error during data rx/tx */
+ if (((state == USB_MS_SCSI_STATE_DATA_OUT) ||
+ (state == USB_MS_SCSI_STATE_DATA_IN)) &&
+ scsi_sense_data.key) {
+ btable_ep[USB_EP_MS_TX].tx_count = 0;
+ state = USB_MS_SCSI_STATE_REPLY;
+ return SCSI_STATUS_CONTINUE;
+ }
+
+ /* done sending data */
+ if (state == USB_MS_SCSI_STATE_REPLY) {
+ state = USB_MS_SCSI_STATE_IDLE;
+ return scsi_sense_data.key;
+ }
+
+ /* still sending/receiving data and no error has occurred */
+ return SCSI_STATUS_CONTINUE;
+}
diff --git a/common/console_output.c b/common/console_output.c
index bc28a8a3b1..7f96d69310 100644
--- a/common/console_output.c
+++ b/common/console_output.c
@@ -57,6 +57,7 @@ static const char * const channel_names[] = {
"task",
"thermal",
"usb",
+ "usbms",
"usbcharge",
"usbpd",
"vboot",
diff --git a/include/config.h b/include/config.h
index 7ae00dfb44..4583146714 100644
--- a/include/config.h
+++ b/include/config.h
@@ -936,6 +936,15 @@
#undef CONFIG_USBC_SS_MUX
/*****************************************************************************/
+/* USB interfaces config */
+
+/* USB mass storage interface */
+#undef CONFIG_USB_MS
+
+/* USB mass storage local buffer size */
+#undef CONFIG_USB_MS_BUFFER_SIZE
+
+/*****************************************************************************/
/* Compile chip support for the USB device controller */
#undef CONFIG_USB
diff --git a/include/console.h b/include/console.h
index 8890e7c838..eb0d66c6fa 100644
--- a/include/console.h
+++ b/include/console.h
@@ -52,6 +52,7 @@ enum console_channel {
CC_TASK,
CC_THERMAL,
CC_USB,
+ CC_USBMS,
CC_USBCHARGE,
CC_USBPD,
CC_VBOOT,
diff --git a/include/usb_ms.h b/include/usb_ms.h
new file mode 100644
index 0000000000..f7cbbe3e77
--- /dev/null
+++ b/include/usb_ms.h
@@ -0,0 +1,63 @@
+/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * USB mass storage definitions.
+ */
+
+#ifndef USB_MS_H
+#define USB_MS_H
+
+#define USB_MS_SUBCLASS_RBC 0x01
+#define USB_MS_SUBCLASS_MMC5 0x02
+#define USB_MS_SUBCLASS_UFI 0x04
+#define USB_MS_SUBCLASS_SCSI 0x06
+#define USB_MS_SUBCLASS_LSDFS 0x07
+#define USB_MS_SUBCLASS_IEEE1667 0x08
+
+#define USB_MS_PROTOCOL_CBI_INTERRUPT 0x00
+#define USB_MS_PROTOCOL_CBI 0x01
+#define USB_MS_PROTOCOL_BBB 0x50
+#define USB_MS_PROTOCOL_UAS 0x62
+
+#define USB_MS_PACKET_SIZE (USB_MAX_PACKET_SIZE)
+
+/* USB Mass Storage Command Block Wrapper */
+struct usb_ms_cbw {
+ uint32_t signature;
+ uint32_t tag;
+ uint32_t data_transfer_length;
+ uint8_t flags;
+ uint8_t LUN;
+ uint8_t length;
+ uint8_t command_block[16];
+} __packed;
+#define USB_MS_CBW_LENGTH 31
+
+#define USB_MS_CBW_SIGNATURE 0x43425355
+#define USB_MS_CBW_DATA_IN (1 << 7)
+
+/* USB Mass Storage Command Status Wrapper */
+struct usb_ms_csw {
+ uint32_t signature;
+ uint32_t tag;
+ uint32_t data_residue;
+ uint8_t status;
+} __packed;
+#define USB_MS_CSW_LENGTH 13
+
+#define UBS_MS_CSW_SIGNATURE 0x53425355
+#define USB_MS_CSW_CMD_PASSED 0x0
+#define USB_MS_CSW_CMD_FAILED 0x1
+#define USB_MS_CSW_CMD_PHASE_ERR 0x2
+
+#define USB_MS_REQ_RESET 0xff
+#define USB_MS_REQ_GET_MAX_LUN 0xfe
+
+#define USB_MS_EVENT_TX (1 << 0)
+#define USB_MS_EVENT_RX (1 << 1)
+
+/* Maximum number of supported LUN's, defined in SCSI file */
+extern const uint8_t max_lun;
+
+#endif /* USB_MS_H */
diff --git a/include/usb_ms_scsi.h b/include/usb_ms_scsi.h
new file mode 100644
index 0000000000..72d5ee322d
--- /dev/null
+++ b/include/usb_ms_scsi.h
@@ -0,0 +1,110 @@
+/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * SCSI definitions.
+ */
+
+#ifndef USB_MS_SCSI_H
+#define USB_MS_SCSI_H
+
+#define SCSI_MAX_LUN 0
+
+/* Status values */
+#define SCSI_STATUS_GOOD 0x00
+#define SCSI_STATUS_CHECK_CONDITION 0x02
+#define SCSI_STATUS_CONDITION_MET 0x04
+#define SCSI_STATUS_BUSY 0x08
+#define SCSI_STATUS_RESERVATION_CONFLICT 0x18
+#define SCSI_STATUS_TASK_SET_FULL 0x28
+#define SCSI_STATUS_ACA_ACTIVE 0x30
+#define SCSI_STATUS_TASK_ABORTED 0x40
+
+/* Not part of standard, indicates operation not complete*/
+#define SCSI_STATUS_CONTINUE 0xFF
+
+/* Sense key values */
+#define SCSI_SENSE_NO_SENSE 0x0
+#define SCSI_SENSE_RECOVERED_ERROR 0x1
+#define SCSI_SENSE_NOT_READY 0x2
+#define SCSI_SENSE_MEDIUM_ERROR 0x3
+#define SCSI_SENSE_HARDWARE_ERROR 0x4
+#define SCSI_SENSE_ILLEGAL_REQUEST 0x5
+#define SCSI_SENSE_UNIT_ATTENTION 0x6
+#define SCSI_SENSE_DATA_PROTECT 0x7
+#define SCSI_SENSE_BLANK_CHECK 0x8
+#define SCSI_SENSE_VENDOR_SPECIFIC 0x9
+#define SCSI_SENSE_COPY_ABORTED 0xa
+#define SCSI_SENSE_ABORTED_COMMAND 0xb
+#define SCSI_SENSE_VOLUME_OVERFLOW 0xd
+#define SCSI_SENSE_MISCOMPARE 0xe
+#define SCSI_SENSE_COMPLETED 0xf
+
+/* Additional sense code (ASC) and additional sense code qualifier (ASCQ)
+ * fields. Stored as ASC | ACSQ */
+#define SCSI_SENSE_CODE_NONE ((0x00 << 4) | 0x00)
+#define SCSI_SENSE_CODE_INVALID_COMMAND_OPERATION_CODE ((0x20 << 4) | 0x00)
+#define SCSI_SENSE_CODE_INVALID_FIELD_IN_CDB ((0x24 << 4) | 0x00)
+#define SCSI_SENSE_CODE_UNRECOVERED_READ_ERROR ((0x11 << 4) | 0x00)
+#define SCSI_SENSE_CODE_NOT_READY ((0x04 << 4) | 0x00)
+#define SCSI_SENSE_CODE_COMMAND_TO_LUN_FAILED ((0x6e << 4) | 0x00)
+#define SCSI_SENSE_CODE_LBA_OUT_OF_RANGE ((0x21 << 4) | 0x00)
+#define SCSI_SENSE_CODE_WRITE_PROTECTED ((0x27 << 4) | 0x00)
+#define SCSI_SENSE_CODE_TIMEOUT ((0x3e) | 0x02)
+#define SCSI_SENSE_CODE_ASC(x) ((x & 0xf0) >> 8)
+#define SCSI_SENSE_CODE_ASCQ(x) (x & 0x0f)
+
+/* Version descriptor values */
+#define SCSI_VERSION_SBC3 0x04, 0xc0
+#define SCSI_VERSION_SPC4 0x04, 0x60
+
+/* Vital product data page codes */
+#define SCSI_VPD_CODE_SUPPORTED_PAGES 0x00
+#define SCSI_VPD_CODE_SERIAL_NUMBER 0x80
+#define SCSI_VPD_CODE_DEVICE_ID 0x83
+
+/* Mode pages */
+#define SCSI_MODE_PAGE_ALL 0x3f
+/* Response values for fixed-format sense data */
+#define SCSI_SENSE_RESPONSE_CURRENT 0x70
+#define SCSI_SENSE_RESPONSE_DEFERRED 0x71
+
+/* Size of various SCSI data structures */
+#define SCSI_CDB6_SIZE 6
+#define SCSI_CDB10_SIZE 10
+#define SCSI_CDB12_SIZE 12
+
+/* Block size for LBA addressing */
+#define SCSI_BLOCK_SIZE_BYTES (4 * 1024)
+
+/* USB mass storage SCSI state machine */
+enum usb_ms_scsi_state {
+ USB_MS_SCSI_STATE_IDLE,
+ USB_MS_SCSI_STATE_PARSE,
+ USB_MS_SCSI_STATE_DATA_IN,
+ USB_MS_SCSI_STATE_DATA_OUT,
+ USB_MS_SCSI_STATE_REPLY,
+};
+
+/* Structure defining sense key entry */
+struct scsi_sense_entry {
+ uint8_t key; /* Sense Key */
+ uint8_t ASC; /* Additional Sense Code */
+ uint8_t ASCQ; /* Additional Sense Qualifier */
+};
+
+/* Structure defining read format capacities response */
+struct scsi_capacity_list_response {
+ uint32_t header; /* Reserved | List Length */
+ uint32_t blocks; /* Number of Blocks */
+ uint32_t block_length; /* Reserved | Descriptor Code | Block Length */
+};
+
+/* USB endpoint buffers */
+extern usb_uint ms_ep_tx[USB_MS_PACKET_SIZE] __usb_ram;
+extern usb_uint ms_ep_rx[USB_MS_PACKET_SIZE] __usb_ram;
+
+int scsi_parse(uint8_t *block, uint8_t length);
+void scsi_reset(void);
+
+#endif /* USB_MS_SCSI_H */