diff options
-rw-r--r-- | chip/stm32/build.mk | 1 | ||||
-rw-r--r-- | chip/stm32/registers.h | 1 | ||||
-rw-r--r-- | chip/stm32/usb_ms.c | 320 | ||||
-rw-r--r-- | chip/stm32/usb_ms_scsi.c | 795 | ||||
-rw-r--r-- | common/console_output.c | 1 | ||||
-rw-r--r-- | include/config.h | 9 | ||||
-rw-r--r-- | include/console.h | 1 | ||||
-rw-r--r-- | include/usb_ms.h | 63 | ||||
-rw-r--r-- | include/usb_ms_scsi.h | 110 |
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 */ |