mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-05 14:31:31 +00:00
USB: Delete mass storage driver
This was an attempt at providing support for flashing a SPI flash device over USB, but it suffered from being rather complex and large. A simpler solution of bridging SPI over USB directly and writing a SPI over USB driver for flashrom is being used instead. Signed-off-by: Anton Staaf <robotboy@chromium.org> BRANCH=None BUG=None TEST=make buildall -j Change-Id: I0d1ef8f17f5d6a4de46003096a8bff4a33b41cb7 Reviewed-on: https://chromium-review.googlesource.com/238763 Tested-by: Anton Staaf <robotboy@chromium.org> Reviewed-by: David Schneider <dnschneid@chromium.org> Reviewed-by: Vic Yang <victoryang@chromium.org> Reviewed-by: Vincent Palatin <vpalatin@chromium.org> Commit-Queue: Anton Staaf <robotboy@chromium.org> Trybot-Ready: Anton Staaf <robotboy@chromium.org>
This commit is contained in:
committed by
ChromeOS Commit Bot
parent
0825fdf352
commit
b1f0a4ca4e
@@ -18,8 +18,6 @@
|
||||
/* Optional features */
|
||||
#define CONFIG_STM_HWTIMER32
|
||||
#define CONFIG_USB
|
||||
#define CONFIG_USB_MS
|
||||
#define CONFIG_USB_MS_BUFFER_SIZE SPI_FLASH_MAX_WRITE_SIZE
|
||||
#define CONFIG_USB_POWER_DELIVERY
|
||||
#define CONFIG_USB_PD_ALT_MODE
|
||||
#define CONFIG_USB_PD_ALT_MODE_DFP
|
||||
|
||||
@@ -19,5 +19,4 @@
|
||||
#define CONFIG_TASK_LIST \
|
||||
TASK_ALWAYS(HOOKS, hook_task, NULL, TASK_STACK_SIZE) \
|
||||
TASK_ALWAYS(CONSOLE, console_task, NULL, LARGER_TASK_STACK_SIZE) \
|
||||
TASK_ALWAYS(USB_MS, ms_task, NULL, TASK_STACK_SIZE) \
|
||||
TASK_ALWAYS(PD, pd_task, NULL, TASK_STACK_SIZE)
|
||||
|
||||
@@ -52,6 +52,5 @@ chip-$(CONFIG_USB)+=usb.o usb-$(CHIP_FAMILY).o usb_endpoints.o
|
||||
chip-$(CONFIG_USB_CONSOLE)+=usb_console.o
|
||||
chip-$(CONFIG_USB_GPIO)+=usb_gpio.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
|
||||
chip-$(CONFIG_USB_SPI)+=usb_spi.o
|
||||
|
||||
@@ -1,318 +0,0 @@
|
||||
/* 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 int 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))
|
||||
return 1;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,795 +0,0 @@
|
||||
/* 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;
|
||||
}
|
||||
@@ -1189,15 +1189,6 @@
|
||||
/* USB Device version of product */
|
||||
#undef CONFIG_USB_BCD_DEV
|
||||
|
||||
/*****************************************************************************/
|
||||
/* 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 */
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/* 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 */
|
||||
@@ -1,110 +0,0 @@
|
||||
/* 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 */
|
||||
Reference in New Issue
Block a user