1944 lines
48 KiB
C
1944 lines
48 KiB
C
/* $FreeBSD$ */
|
|
/*-
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*
|
|
* Copyright (C) 2003-2005 Alan Stern
|
|
* Copyright (C) 2008 Hans Petter Selasky
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions, and the following disclaimer,
|
|
* without modification.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. The names of the above-listed copyright holders may not be used
|
|
* to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* NOTE: Much of the SCSI statemachine handling code derives from the
|
|
* Linux USB gadget stack.
|
|
*/
|
|
|
|
#ifdef USB_GLOBAL_INCLUDE_FILE
|
|
#include USB_GLOBAL_INCLUDE_FILE
|
|
#else
|
|
#include <sys/stdint.h>
|
|
#include <sys/stddef.h>
|
|
#include <sys/param.h>
|
|
#include <sys/queue.h>
|
|
#include <sys/types.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/module.h>
|
|
#include <sys/lock.h>
|
|
#include <sys/mutex.h>
|
|
#include <sys/condvar.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/sx.h>
|
|
#include <sys/unistd.h>
|
|
#include <sys/callout.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/priv.h>
|
|
|
|
#include <dev/usb/usb.h>
|
|
#include <dev/usb/usbdi.h>
|
|
#include "usbdevs.h"
|
|
#include "usb_if.h"
|
|
|
|
#define USB_DEBUG_VAR ustorage_fs_debug
|
|
#include <dev/usb/usb_debug.h>
|
|
#endif /* USB_GLOBAL_INCLUDE_FILE */
|
|
|
|
#ifdef USB_DEBUG
|
|
static int ustorage_fs_debug = 0;
|
|
|
|
SYSCTL_NODE(_hw_usb, OID_AUTO, ustorage_fs, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
|
|
"USB ustorage_fs");
|
|
SYSCTL_INT(_hw_usb_ustorage_fs, OID_AUTO, debug, CTLFLAG_RWTUN,
|
|
&ustorage_fs_debug, 0, "ustorage_fs debug level");
|
|
#endif
|
|
|
|
/* Define some limits */
|
|
|
|
#ifndef USTORAGE_FS_BULK_SIZE
|
|
#define USTORAGE_FS_BULK_SIZE (1U << 17) /* bytes */
|
|
#endif
|
|
|
|
#ifndef USTORAGE_FS_MAX_LUN
|
|
#define USTORAGE_FS_MAX_LUN 8 /* units */
|
|
#endif
|
|
|
|
#ifndef USTORAGE_QDATA_MAX
|
|
#define USTORAGE_QDATA_MAX 40 /* bytes */
|
|
#endif
|
|
|
|
/*
|
|
* The SCSI ID string must be exactly 28 characters long
|
|
* exluding the terminating zero.
|
|
*/
|
|
#ifndef USTORAGE_FS_ID_STRING
|
|
#define USTORAGE_FS_ID_STRING \
|
|
"FreeBSD " /* 8 */ \
|
|
"File-Stor Gadget" /* 16 */ \
|
|
"0101" /* 4 */
|
|
#endif
|
|
|
|
/*
|
|
* The following macro defines the number of
|
|
* sectors to be allocated for the RAM disk:
|
|
*/
|
|
#ifndef USTORAGE_FS_RAM_SECT
|
|
#define USTORAGE_FS_RAM_SECT (1UL << 13)
|
|
#endif
|
|
|
|
static uint8_t *ustorage_fs_ramdisk;
|
|
|
|
/* USB transfer definitions */
|
|
|
|
#define USTORAGE_FS_T_BBB_COMMAND 0
|
|
#define USTORAGE_FS_T_BBB_DATA_DUMP 1
|
|
#define USTORAGE_FS_T_BBB_DATA_READ 2
|
|
#define USTORAGE_FS_T_BBB_DATA_WRITE 3
|
|
#define USTORAGE_FS_T_BBB_STATUS 4
|
|
#define USTORAGE_FS_T_BBB_MAX 5
|
|
|
|
/* USB data stage direction */
|
|
|
|
#define DIR_NONE 0
|
|
#define DIR_READ 1
|
|
#define DIR_WRITE 2
|
|
|
|
/* USB interface specific control request */
|
|
|
|
#define UR_BBB_RESET 0xff /* Bulk-Only reset */
|
|
#define UR_BBB_GET_MAX_LUN 0xfe /* Get maximum lun */
|
|
|
|
/* Command Block Wrapper */
|
|
typedef struct {
|
|
uDWord dCBWSignature;
|
|
#define CBWSIGNATURE 0x43425355
|
|
uDWord dCBWTag;
|
|
uDWord dCBWDataTransferLength;
|
|
uByte bCBWFlags;
|
|
#define CBWFLAGS_OUT 0x00
|
|
#define CBWFLAGS_IN 0x80
|
|
uByte bCBWLUN;
|
|
uByte bCDBLength;
|
|
#define CBWCDBLENGTH 16
|
|
uByte CBWCDB[CBWCDBLENGTH];
|
|
} __packed ustorage_fs_bbb_cbw_t;
|
|
|
|
#define USTORAGE_FS_BBB_CBW_SIZE 31
|
|
|
|
/* Command Status Wrapper */
|
|
typedef struct {
|
|
uDWord dCSWSignature;
|
|
#define CSWSIGNATURE 0x53425355
|
|
uDWord dCSWTag;
|
|
uDWord dCSWDataResidue;
|
|
uByte bCSWStatus;
|
|
#define CSWSTATUS_GOOD 0x0
|
|
#define CSWSTATUS_FAILED 0x1
|
|
#define CSWSTATUS_PHASE 0x2
|
|
} __packed ustorage_fs_bbb_csw_t;
|
|
|
|
#define USTORAGE_FS_BBB_CSW_SIZE 13
|
|
|
|
struct ustorage_fs_lun {
|
|
uint8_t *memory_image;
|
|
|
|
uint32_t num_sectors;
|
|
uint32_t sense_data;
|
|
uint32_t sense_data_info;
|
|
uint32_t unit_attention_data;
|
|
|
|
uint8_t read_only:1;
|
|
uint8_t prevent_medium_removal:1;
|
|
uint8_t info_valid:1;
|
|
uint8_t removable:1;
|
|
};
|
|
|
|
struct ustorage_fs_softc {
|
|
ustorage_fs_bbb_cbw_t *sc_cbw; /* Command Wrapper Block */
|
|
ustorage_fs_bbb_csw_t *sc_csw; /* Command Status Block */
|
|
void *sc_dma_ptr; /* Main data buffer */
|
|
|
|
struct mtx sc_mtx;
|
|
|
|
struct ustorage_fs_lun sc_lun[USTORAGE_FS_MAX_LUN];
|
|
|
|
struct {
|
|
uint8_t *data_ptr;
|
|
struct ustorage_fs_lun *currlun;
|
|
|
|
uint32_t data_rem; /* bytes, as reported by the command
|
|
* block wrapper */
|
|
uint32_t offset; /* bytes */
|
|
|
|
uint8_t cbw_dir;
|
|
uint8_t cmd_dir;
|
|
uint8_t lun;
|
|
uint8_t cmd_len;
|
|
uint8_t data_short:1;
|
|
uint8_t data_error:1;
|
|
} sc_transfer;
|
|
|
|
device_t sc_dev;
|
|
struct usb_device *sc_udev;
|
|
struct usb_xfer *sc_xfer[USTORAGE_FS_T_BBB_MAX];
|
|
|
|
uint8_t sc_iface_no; /* interface number */
|
|
uint8_t sc_last_lun;
|
|
uint8_t sc_last_xfer_index;
|
|
uint8_t sc_qdata[USTORAGE_QDATA_MAX];
|
|
};
|
|
|
|
/* prototypes */
|
|
|
|
static device_probe_t ustorage_fs_probe;
|
|
static device_attach_t ustorage_fs_attach;
|
|
static device_detach_t ustorage_fs_detach;
|
|
static device_suspend_t ustorage_fs_suspend;
|
|
static device_resume_t ustorage_fs_resume;
|
|
static usb_handle_request_t ustorage_fs_handle_request;
|
|
|
|
static usb_callback_t ustorage_fs_t_bbb_command_callback;
|
|
static usb_callback_t ustorage_fs_t_bbb_data_dump_callback;
|
|
static usb_callback_t ustorage_fs_t_bbb_data_read_callback;
|
|
static usb_callback_t ustorage_fs_t_bbb_data_write_callback;
|
|
static usb_callback_t ustorage_fs_t_bbb_status_callback;
|
|
|
|
static void ustorage_fs_transfer_start(struct ustorage_fs_softc *sc, uint8_t xfer_index);
|
|
static void ustorage_fs_transfer_stop(struct ustorage_fs_softc *sc);
|
|
|
|
static uint8_t ustorage_fs_verify(struct ustorage_fs_softc *sc);
|
|
static uint8_t ustorage_fs_inquiry(struct ustorage_fs_softc *sc);
|
|
static uint8_t ustorage_fs_request_sense(struct ustorage_fs_softc *sc);
|
|
static uint8_t ustorage_fs_read_capacity(struct ustorage_fs_softc *sc);
|
|
static uint8_t ustorage_fs_mode_sense(struct ustorage_fs_softc *sc);
|
|
static uint8_t ustorage_fs_start_stop(struct ustorage_fs_softc *sc);
|
|
static uint8_t ustorage_fs_prevent_allow(struct ustorage_fs_softc *sc);
|
|
static uint8_t ustorage_fs_read_format_capacities(struct ustorage_fs_softc *sc);
|
|
static uint8_t ustorage_fs_mode_select(struct ustorage_fs_softc *sc);
|
|
static uint8_t ustorage_fs_min_len(struct ustorage_fs_softc *sc, uint32_t len, uint32_t mask);
|
|
static uint8_t ustorage_fs_read(struct ustorage_fs_softc *sc);
|
|
static uint8_t ustorage_fs_write(struct ustorage_fs_softc *sc);
|
|
static uint8_t ustorage_fs_check_cmd(struct ustorage_fs_softc *sc, uint8_t cmd_size, uint16_t mask, uint8_t needs_medium);
|
|
static uint8_t ustorage_fs_do_cmd(struct ustorage_fs_softc *sc);
|
|
|
|
static device_method_t ustorage_fs_methods[] = {
|
|
/* USB interface */
|
|
DEVMETHOD(usb_handle_request, ustorage_fs_handle_request),
|
|
|
|
/* Device interface */
|
|
DEVMETHOD(device_probe, ustorage_fs_probe),
|
|
DEVMETHOD(device_attach, ustorage_fs_attach),
|
|
DEVMETHOD(device_detach, ustorage_fs_detach),
|
|
DEVMETHOD(device_suspend, ustorage_fs_suspend),
|
|
DEVMETHOD(device_resume, ustorage_fs_resume),
|
|
|
|
DEVMETHOD_END
|
|
};
|
|
|
|
static driver_t ustorage_fs_driver = {
|
|
.name = "ustorage_fs",
|
|
.methods = ustorage_fs_methods,
|
|
.size = sizeof(struct ustorage_fs_softc),
|
|
};
|
|
|
|
DRIVER_MODULE(ustorage_fs, uhub, ustorage_fs_driver, NULL, NULL);
|
|
MODULE_VERSION(ustorage_fs, 0);
|
|
MODULE_DEPEND(ustorage_fs, usb, 1, 1, 1);
|
|
|
|
static struct usb_config ustorage_fs_bbb_config[USTORAGE_FS_T_BBB_MAX] = {
|
|
[USTORAGE_FS_T_BBB_COMMAND] = {
|
|
.type = UE_BULK,
|
|
.endpoint = UE_ADDR_ANY,
|
|
.direction = UE_DIR_OUT,
|
|
.bufsize = sizeof(ustorage_fs_bbb_cbw_t),
|
|
.callback = &ustorage_fs_t_bbb_command_callback,
|
|
.usb_mode = USB_MODE_DEVICE,
|
|
},
|
|
|
|
[USTORAGE_FS_T_BBB_DATA_DUMP] = {
|
|
.type = UE_BULK,
|
|
.endpoint = UE_ADDR_ANY,
|
|
.direction = UE_DIR_OUT,
|
|
.bufsize = 0, /* use wMaxPacketSize */
|
|
.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,},
|
|
.callback = &ustorage_fs_t_bbb_data_dump_callback,
|
|
.usb_mode = USB_MODE_DEVICE,
|
|
},
|
|
|
|
[USTORAGE_FS_T_BBB_DATA_READ] = {
|
|
.type = UE_BULK,
|
|
.endpoint = UE_ADDR_ANY,
|
|
.direction = UE_DIR_OUT,
|
|
.bufsize = USTORAGE_FS_BULK_SIZE,
|
|
.flags = {.proxy_buffer = 1,.short_xfer_ok = 1},
|
|
.callback = &ustorage_fs_t_bbb_data_read_callback,
|
|
.usb_mode = USB_MODE_DEVICE,
|
|
},
|
|
|
|
[USTORAGE_FS_T_BBB_DATA_WRITE] = {
|
|
.type = UE_BULK,
|
|
.endpoint = UE_ADDR_ANY,
|
|
.direction = UE_DIR_IN,
|
|
.bufsize = USTORAGE_FS_BULK_SIZE,
|
|
.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,.ext_buffer = 1},
|
|
.callback = &ustorage_fs_t_bbb_data_write_callback,
|
|
.usb_mode = USB_MODE_DEVICE,
|
|
},
|
|
|
|
[USTORAGE_FS_T_BBB_STATUS] = {
|
|
.type = UE_BULK,
|
|
.endpoint = UE_ADDR_ANY,
|
|
.direction = UE_DIR_IN,
|
|
.bufsize = sizeof(ustorage_fs_bbb_csw_t),
|
|
.flags = {.short_xfer_ok = 1},
|
|
.callback = &ustorage_fs_t_bbb_status_callback,
|
|
.usb_mode = USB_MODE_DEVICE,
|
|
},
|
|
};
|
|
|
|
/*
|
|
* USB device probe/attach/detach
|
|
*/
|
|
|
|
static int
|
|
ustorage_fs_probe(device_t dev)
|
|
{
|
|
struct usb_attach_arg *uaa = device_get_ivars(dev);
|
|
struct usb_interface_descriptor *id;
|
|
|
|
if (uaa->usb_mode != USB_MODE_DEVICE) {
|
|
return (ENXIO);
|
|
}
|
|
/* Check for a standards compliant device */
|
|
id = usbd_get_interface_descriptor(uaa->iface);
|
|
if ((id == NULL) ||
|
|
(id->bInterfaceClass != UICLASS_MASS) ||
|
|
(id->bInterfaceSubClass != UISUBCLASS_SCSI) ||
|
|
(id->bInterfaceProtocol != UIPROTO_MASS_BBB)) {
|
|
return (ENXIO);
|
|
}
|
|
return (BUS_PROBE_GENERIC);
|
|
}
|
|
|
|
static int
|
|
ustorage_fs_attach(device_t dev)
|
|
{
|
|
struct ustorage_fs_softc *sc = device_get_softc(dev);
|
|
struct usb_attach_arg *uaa = device_get_ivars(dev);
|
|
struct usb_interface_descriptor *id;
|
|
int err;
|
|
int unit;
|
|
|
|
/*
|
|
* NOTE: the softc struct is cleared in device_set_driver.
|
|
* We can safely call ustorage_fs_detach without specifically
|
|
* initializing the struct.
|
|
*/
|
|
|
|
sc->sc_dev = dev;
|
|
sc->sc_udev = uaa->device;
|
|
unit = device_get_unit(dev);
|
|
|
|
/* enable power saving mode */
|
|
usbd_set_power_mode(uaa->device, USB_POWER_MODE_SAVE);
|
|
|
|
if (unit == 0) {
|
|
if (ustorage_fs_ramdisk == NULL) {
|
|
/*
|
|
* allocate a memory image for our ramdisk until
|
|
* further
|
|
*/
|
|
ustorage_fs_ramdisk =
|
|
malloc(USTORAGE_FS_RAM_SECT << 9, M_USB,
|
|
M_ZERO | M_WAITOK);
|
|
}
|
|
sc->sc_lun[0].memory_image = ustorage_fs_ramdisk;
|
|
sc->sc_lun[0].num_sectors = USTORAGE_FS_RAM_SECT;
|
|
sc->sc_lun[0].removable = 1;
|
|
}
|
|
|
|
device_set_usb_desc(dev);
|
|
|
|
mtx_init(&sc->sc_mtx, "USTORAGE_FS lock",
|
|
NULL, (MTX_DEF | MTX_RECURSE));
|
|
|
|
/* get interface index */
|
|
|
|
id = usbd_get_interface_descriptor(uaa->iface);
|
|
if (id == NULL) {
|
|
device_printf(dev, "failed to get "
|
|
"interface number\n");
|
|
goto detach;
|
|
}
|
|
sc->sc_iface_no = id->bInterfaceNumber;
|
|
|
|
err = usbd_transfer_setup(uaa->device,
|
|
&uaa->info.bIfaceIndex, sc->sc_xfer, ustorage_fs_bbb_config,
|
|
USTORAGE_FS_T_BBB_MAX, sc, &sc->sc_mtx);
|
|
if (err) {
|
|
device_printf(dev, "could not setup required "
|
|
"transfers, %s\n", usbd_errstr(err));
|
|
goto detach;
|
|
}
|
|
|
|
sc->sc_cbw = usbd_xfer_get_frame_buffer(sc->sc_xfer[
|
|
USTORAGE_FS_T_BBB_COMMAND], 0);
|
|
sc->sc_csw = usbd_xfer_get_frame_buffer(sc->sc_xfer[
|
|
USTORAGE_FS_T_BBB_STATUS], 0);
|
|
sc->sc_dma_ptr = usbd_xfer_get_frame_buffer(sc->sc_xfer[
|
|
USTORAGE_FS_T_BBB_DATA_READ], 0);
|
|
|
|
/* start Mass Storage State Machine */
|
|
|
|
mtx_lock(&sc->sc_mtx);
|
|
ustorage_fs_transfer_start(sc, USTORAGE_FS_T_BBB_COMMAND);
|
|
mtx_unlock(&sc->sc_mtx);
|
|
|
|
return (0); /* success */
|
|
|
|
detach:
|
|
ustorage_fs_detach(dev);
|
|
return (ENXIO); /* failure */
|
|
}
|
|
|
|
static int
|
|
ustorage_fs_detach(device_t dev)
|
|
{
|
|
struct ustorage_fs_softc *sc = device_get_softc(dev);
|
|
|
|
/* teardown our statemachine */
|
|
|
|
usbd_transfer_unsetup(sc->sc_xfer, USTORAGE_FS_T_BBB_MAX);
|
|
|
|
mtx_destroy(&sc->sc_mtx);
|
|
|
|
return (0); /* success */
|
|
}
|
|
|
|
static int
|
|
ustorage_fs_suspend(device_t dev)
|
|
{
|
|
device_printf(dev, "suspending\n");
|
|
return (0); /* success */
|
|
}
|
|
|
|
static int
|
|
ustorage_fs_resume(device_t dev)
|
|
{
|
|
device_printf(dev, "resuming\n");
|
|
return (0); /* success */
|
|
}
|
|
|
|
/*
|
|
* Generic functions to handle transfers
|
|
*/
|
|
|
|
static void
|
|
ustorage_fs_transfer_start(struct ustorage_fs_softc *sc, uint8_t xfer_index)
|
|
{
|
|
if (sc->sc_xfer[xfer_index]) {
|
|
sc->sc_last_xfer_index = xfer_index;
|
|
usbd_transfer_start(sc->sc_xfer[xfer_index]);
|
|
}
|
|
}
|
|
|
|
static void
|
|
ustorage_fs_transfer_stop(struct ustorage_fs_softc *sc)
|
|
{
|
|
usbd_transfer_stop(sc->sc_xfer[sc->sc_last_xfer_index]);
|
|
mtx_unlock(&sc->sc_mtx);
|
|
usbd_transfer_drain(sc->sc_xfer[sc->sc_last_xfer_index]);
|
|
mtx_lock(&sc->sc_mtx);
|
|
}
|
|
|
|
static int
|
|
ustorage_fs_handle_request(device_t dev,
|
|
const void *preq, void **pptr, uint16_t *plen,
|
|
uint16_t offset, uint8_t *pstate)
|
|
{
|
|
struct ustorage_fs_softc *sc = device_get_softc(dev);
|
|
const struct usb_device_request *req = preq;
|
|
uint8_t is_complete = *pstate;
|
|
|
|
if (!is_complete) {
|
|
if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
|
|
(req->bRequest == UR_BBB_RESET)) {
|
|
*plen = 0;
|
|
mtx_lock(&sc->sc_mtx);
|
|
ustorage_fs_transfer_stop(sc);
|
|
sc->sc_transfer.data_error = 1;
|
|
ustorage_fs_transfer_start(sc,
|
|
USTORAGE_FS_T_BBB_COMMAND);
|
|
mtx_unlock(&sc->sc_mtx);
|
|
return (0);
|
|
} else if ((req->bmRequestType == UT_READ_CLASS_INTERFACE) &&
|
|
(req->bRequest == UR_BBB_GET_MAX_LUN)) {
|
|
if (offset == 0) {
|
|
*plen = 1;
|
|
*pptr = &sc->sc_last_lun;
|
|
} else {
|
|
*plen = 0;
|
|
}
|
|
return (0);
|
|
}
|
|
}
|
|
return (ENXIO); /* use builtin handler */
|
|
}
|
|
|
|
static void
|
|
ustorage_fs_t_bbb_command_callback(struct usb_xfer *xfer, usb_error_t error)
|
|
{
|
|
struct ustorage_fs_softc *sc = usbd_xfer_softc(xfer);
|
|
uint32_t tag;
|
|
uint8_t err = 0;
|
|
|
|
DPRINTF("\n");
|
|
|
|
switch (USB_GET_STATE(xfer)) {
|
|
case USB_ST_TRANSFERRED:
|
|
|
|
tag = UGETDW(sc->sc_cbw->dCBWSignature);
|
|
|
|
if (tag != CBWSIGNATURE) {
|
|
/* do nothing */
|
|
DPRINTF("invalid signature 0x%08x\n", tag);
|
|
break;
|
|
}
|
|
tag = UGETDW(sc->sc_cbw->dCBWTag);
|
|
|
|
/* echo back tag */
|
|
USETDW(sc->sc_csw->dCSWTag, tag);
|
|
|
|
/* reset status */
|
|
sc->sc_csw->bCSWStatus = 0;
|
|
|
|
/* reset data offset, data length and data remainder */
|
|
sc->sc_transfer.offset = 0;
|
|
sc->sc_transfer.data_rem =
|
|
UGETDW(sc->sc_cbw->dCBWDataTransferLength);
|
|
|
|
/* reset data flags */
|
|
sc->sc_transfer.data_short = 0;
|
|
|
|
/* extract LUN */
|
|
sc->sc_transfer.lun = sc->sc_cbw->bCBWLUN;
|
|
|
|
if (sc->sc_transfer.data_rem == 0) {
|
|
sc->sc_transfer.cbw_dir = DIR_NONE;
|
|
} else {
|
|
if (sc->sc_cbw->bCBWFlags & CBWFLAGS_IN) {
|
|
sc->sc_transfer.cbw_dir = DIR_WRITE;
|
|
} else {
|
|
sc->sc_transfer.cbw_dir = DIR_READ;
|
|
}
|
|
}
|
|
|
|
sc->sc_transfer.cmd_len = sc->sc_cbw->bCDBLength;
|
|
if ((sc->sc_transfer.cmd_len > sizeof(sc->sc_cbw->CBWCDB)) ||
|
|
(sc->sc_transfer.cmd_len == 0)) {
|
|
/* just halt - this is invalid */
|
|
DPRINTF("invalid command length %d bytes\n",
|
|
sc->sc_transfer.cmd_len);
|
|
break;
|
|
}
|
|
|
|
err = ustorage_fs_do_cmd(sc);
|
|
if (err) {
|
|
/* got an error */
|
|
DPRINTF("command failed\n");
|
|
break;
|
|
}
|
|
if ((sc->sc_transfer.data_rem > 0) &&
|
|
(sc->sc_transfer.cbw_dir != sc->sc_transfer.cmd_dir)) {
|
|
/* contradicting data transfer direction */
|
|
err = 1;
|
|
DPRINTF("data direction mismatch\n");
|
|
break;
|
|
}
|
|
switch (sc->sc_transfer.cbw_dir) {
|
|
case DIR_READ:
|
|
ustorage_fs_transfer_start(sc, USTORAGE_FS_T_BBB_DATA_READ);
|
|
break;
|
|
case DIR_WRITE:
|
|
ustorage_fs_transfer_start(sc, USTORAGE_FS_T_BBB_DATA_WRITE);
|
|
break;
|
|
default:
|
|
ustorage_fs_transfer_start(sc,
|
|
USTORAGE_FS_T_BBB_STATUS);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case USB_ST_SETUP:
|
|
tr_setup:
|
|
if (sc->sc_transfer.data_error) {
|
|
sc->sc_transfer.data_error = 0;
|
|
usbd_xfer_set_stall(xfer);
|
|
DPRINTF("stall pipe\n");
|
|
}
|
|
usbd_xfer_set_frame_len(xfer, 0,
|
|
sizeof(ustorage_fs_bbb_cbw_t));
|
|
usbd_transfer_submit(xfer);
|
|
break;
|
|
|
|
default: /* Error */
|
|
DPRINTF("error\n");
|
|
if (error == USB_ERR_CANCELLED) {
|
|
break;
|
|
}
|
|
/* If the pipe is already stalled, don't do another stall */
|
|
if (!usbd_xfer_is_stalled(xfer))
|
|
sc->sc_transfer.data_error = 1;
|
|
|
|
/* try again */
|
|
goto tr_setup;
|
|
}
|
|
if (err) {
|
|
if (sc->sc_csw->bCSWStatus == 0) {
|
|
/* set some default error code */
|
|
sc->sc_csw->bCSWStatus = CSWSTATUS_FAILED;
|
|
}
|
|
if (sc->sc_transfer.cbw_dir == DIR_READ) {
|
|
/* dump all data */
|
|
ustorage_fs_transfer_start(sc,
|
|
USTORAGE_FS_T_BBB_DATA_DUMP);
|
|
return;
|
|
}
|
|
if (sc->sc_transfer.cbw_dir == DIR_WRITE) {
|
|
/* need to stall before status */
|
|
sc->sc_transfer.data_error = 1;
|
|
}
|
|
ustorage_fs_transfer_start(sc, USTORAGE_FS_T_BBB_STATUS);
|
|
}
|
|
}
|
|
|
|
static void
|
|
ustorage_fs_t_bbb_data_dump_callback(struct usb_xfer *xfer, usb_error_t error)
|
|
{
|
|
struct ustorage_fs_softc *sc = usbd_xfer_softc(xfer);
|
|
uint32_t max_bulk = usbd_xfer_max_len(xfer);
|
|
int actlen, sumlen;
|
|
|
|
usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
|
|
|
|
DPRINTF("\n");
|
|
|
|
switch (USB_GET_STATE(xfer)) {
|
|
case USB_ST_TRANSFERRED:
|
|
sc->sc_transfer.data_rem -= actlen;
|
|
sc->sc_transfer.offset += actlen;
|
|
|
|
if (actlen != sumlen || sc->sc_transfer.data_rem == 0) {
|
|
/* short transfer or end of data */
|
|
ustorage_fs_transfer_start(sc,
|
|
USTORAGE_FS_T_BBB_STATUS);
|
|
break;
|
|
}
|
|
/* Fallthrough */
|
|
|
|
case USB_ST_SETUP:
|
|
tr_setup:
|
|
if (max_bulk > sc->sc_transfer.data_rem) {
|
|
max_bulk = sc->sc_transfer.data_rem;
|
|
}
|
|
if (sc->sc_transfer.data_error) {
|
|
sc->sc_transfer.data_error = 0;
|
|
usbd_xfer_set_stall(xfer);
|
|
}
|
|
usbd_xfer_set_frame_len(xfer, 0, max_bulk);
|
|
usbd_transfer_submit(xfer);
|
|
break;
|
|
|
|
default: /* Error */
|
|
if (error == USB_ERR_CANCELLED) {
|
|
break;
|
|
}
|
|
/*
|
|
* If the pipe is already stalled, don't do another stall:
|
|
*/
|
|
if (!usbd_xfer_is_stalled(xfer))
|
|
sc->sc_transfer.data_error = 1;
|
|
|
|
/* try again */
|
|
goto tr_setup;
|
|
}
|
|
}
|
|
|
|
static void
|
|
ustorage_fs_t_bbb_data_read_callback(struct usb_xfer *xfer, usb_error_t error)
|
|
{
|
|
struct ustorage_fs_softc *sc = usbd_xfer_softc(xfer);
|
|
uint32_t max_bulk = usbd_xfer_max_len(xfer);
|
|
int actlen, sumlen;
|
|
|
|
usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
|
|
|
|
DPRINTF("\n");
|
|
|
|
switch (USB_GET_STATE(xfer)) {
|
|
case USB_ST_TRANSFERRED:
|
|
/* XXX copy data from DMA buffer */
|
|
memcpy(sc->sc_transfer.data_ptr, sc->sc_dma_ptr, actlen);
|
|
|
|
sc->sc_transfer.data_rem -= actlen;
|
|
sc->sc_transfer.data_ptr += actlen;
|
|
sc->sc_transfer.offset += actlen;
|
|
|
|
if (actlen != sumlen || sc->sc_transfer.data_rem == 0) {
|
|
/* short transfer or end of data */
|
|
ustorage_fs_transfer_start(sc,
|
|
USTORAGE_FS_T_BBB_STATUS);
|
|
break;
|
|
}
|
|
/* Fallthrough */
|
|
|
|
case USB_ST_SETUP:
|
|
tr_setup:
|
|
if (max_bulk > sc->sc_transfer.data_rem) {
|
|
max_bulk = sc->sc_transfer.data_rem;
|
|
}
|
|
if (sc->sc_transfer.data_error) {
|
|
sc->sc_transfer.data_error = 0;
|
|
usbd_xfer_set_stall(xfer);
|
|
}
|
|
|
|
usbd_xfer_set_frame_data(xfer, 0, sc->sc_dma_ptr, max_bulk);
|
|
usbd_transfer_submit(xfer);
|
|
break;
|
|
|
|
default: /* Error */
|
|
if (error == USB_ERR_CANCELLED) {
|
|
break;
|
|
}
|
|
/* If the pipe is already stalled, don't do another stall */
|
|
if (!usbd_xfer_is_stalled(xfer))
|
|
sc->sc_transfer.data_error = 1;
|
|
|
|
/* try again */
|
|
goto tr_setup;
|
|
}
|
|
}
|
|
|
|
static void
|
|
ustorage_fs_t_bbb_data_write_callback(struct usb_xfer *xfer, usb_error_t error)
|
|
{
|
|
struct ustorage_fs_softc *sc = usbd_xfer_softc(xfer);
|
|
uint32_t max_bulk = usbd_xfer_max_len(xfer);
|
|
int actlen, sumlen;
|
|
|
|
usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
|
|
|
|
DPRINTF("\n");
|
|
|
|
switch (USB_GET_STATE(xfer)) {
|
|
case USB_ST_TRANSFERRED:
|
|
sc->sc_transfer.data_rem -= actlen;
|
|
sc->sc_transfer.data_ptr += actlen;
|
|
sc->sc_transfer.offset += actlen;
|
|
|
|
if (actlen != sumlen || sc->sc_transfer.data_rem == 0) {
|
|
/* short transfer or end of data */
|
|
ustorage_fs_transfer_start(sc,
|
|
USTORAGE_FS_T_BBB_STATUS);
|
|
break;
|
|
}
|
|
case USB_ST_SETUP:
|
|
tr_setup:
|
|
if (max_bulk >= sc->sc_transfer.data_rem) {
|
|
max_bulk = sc->sc_transfer.data_rem;
|
|
if (sc->sc_transfer.data_short)
|
|
usbd_xfer_set_flag(xfer, USB_FORCE_SHORT_XFER);
|
|
else
|
|
usbd_xfer_clr_flag(xfer, USB_FORCE_SHORT_XFER);
|
|
} else
|
|
usbd_xfer_clr_flag(xfer, USB_FORCE_SHORT_XFER);
|
|
|
|
if (sc->sc_transfer.data_error) {
|
|
sc->sc_transfer.data_error = 0;
|
|
usbd_xfer_set_stall(xfer);
|
|
}
|
|
|
|
/* XXX copy data to DMA buffer */
|
|
memcpy(sc->sc_dma_ptr, sc->sc_transfer.data_ptr, max_bulk);
|
|
|
|
usbd_xfer_set_frame_data(xfer, 0, sc->sc_dma_ptr, max_bulk);
|
|
usbd_transfer_submit(xfer);
|
|
break;
|
|
|
|
default: /* Error */
|
|
if (error == USB_ERR_CANCELLED) {
|
|
break;
|
|
}
|
|
/*
|
|
* If the pipe is already stalled, don't do another
|
|
* stall
|
|
*/
|
|
if (!usbd_xfer_is_stalled(xfer))
|
|
sc->sc_transfer.data_error = 1;
|
|
|
|
/* try again */
|
|
goto tr_setup;
|
|
}
|
|
}
|
|
|
|
static void
|
|
ustorage_fs_t_bbb_status_callback(struct usb_xfer *xfer, usb_error_t error)
|
|
{
|
|
struct ustorage_fs_softc *sc = usbd_xfer_softc(xfer);
|
|
|
|
DPRINTF("\n");
|
|
|
|
switch (USB_GET_STATE(xfer)) {
|
|
case USB_ST_TRANSFERRED:
|
|
ustorage_fs_transfer_start(sc, USTORAGE_FS_T_BBB_COMMAND);
|
|
break;
|
|
|
|
case USB_ST_SETUP:
|
|
tr_setup:
|
|
USETDW(sc->sc_csw->dCSWSignature, CSWSIGNATURE);
|
|
USETDW(sc->sc_csw->dCSWDataResidue, sc->sc_transfer.data_rem);
|
|
|
|
if (sc->sc_transfer.data_error) {
|
|
sc->sc_transfer.data_error = 0;
|
|
usbd_xfer_set_stall(xfer);
|
|
}
|
|
usbd_xfer_set_frame_len(xfer, 0,
|
|
sizeof(ustorage_fs_bbb_csw_t));
|
|
usbd_transfer_submit(xfer);
|
|
break;
|
|
|
|
default:
|
|
if (error == USB_ERR_CANCELLED) {
|
|
break;
|
|
}
|
|
/* If the pipe is already stalled, don't do another stall */
|
|
if (!usbd_xfer_is_stalled(xfer))
|
|
sc->sc_transfer.data_error = 1;
|
|
|
|
/* try again */
|
|
goto tr_setup;
|
|
}
|
|
}
|
|
|
|
/* SCSI commands that we recognize */
|
|
#define SC_FORMAT_UNIT 0x04
|
|
#define SC_INQUIRY 0x12
|
|
#define SC_MODE_SELECT_6 0x15
|
|
#define SC_MODE_SELECT_10 0x55
|
|
#define SC_MODE_SENSE_6 0x1a
|
|
#define SC_MODE_SENSE_10 0x5a
|
|
#define SC_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e
|
|
#define SC_READ_6 0x08
|
|
#define SC_READ_10 0x28
|
|
#define SC_READ_12 0xa8
|
|
#define SC_READ_CAPACITY 0x25
|
|
#define SC_READ_FORMAT_CAPACITIES 0x23
|
|
#define SC_RELEASE 0x17
|
|
#define SC_REQUEST_SENSE 0x03
|
|
#define SC_RESERVE 0x16
|
|
#define SC_SEND_DIAGNOSTIC 0x1d
|
|
#define SC_START_STOP_UNIT 0x1b
|
|
#define SC_SYNCHRONIZE_CACHE 0x35
|
|
#define SC_TEST_UNIT_READY 0x00
|
|
#define SC_VERIFY 0x2f
|
|
#define SC_WRITE_6 0x0a
|
|
#define SC_WRITE_10 0x2a
|
|
#define SC_WRITE_12 0xaa
|
|
|
|
/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */
|
|
#define SS_NO_SENSE 0
|
|
#define SS_COMMUNICATION_FAILURE 0x040800
|
|
#define SS_INVALID_COMMAND 0x052000
|
|
#define SS_INVALID_FIELD_IN_CDB 0x052400
|
|
#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100
|
|
#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500
|
|
#define SS_MEDIUM_NOT_PRESENT 0x023a00
|
|
#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302
|
|
#define SS_NOT_READY_TO_READY_TRANSITION 0x062800
|
|
#define SS_RESET_OCCURRED 0x062900
|
|
#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900
|
|
#define SS_UNRECOVERED_READ_ERROR 0x031100
|
|
#define SS_WRITE_ERROR 0x030c02
|
|
#define SS_WRITE_PROTECTED 0x072700
|
|
|
|
#define SK(x) ((uint8_t) ((x) >> 16)) /* Sense Key byte, etc. */
|
|
#define ASC(x) ((uint8_t) ((x) >> 8))
|
|
#define ASCQ(x) ((uint8_t) (x))
|
|
|
|
/* Routines for unaligned data access */
|
|
|
|
static uint16_t
|
|
get_be16(uint8_t *buf)
|
|
{
|
|
return ((uint16_t)buf[0] << 8) | ((uint16_t)buf[1]);
|
|
}
|
|
|
|
static uint32_t
|
|
get_be32(uint8_t *buf)
|
|
{
|
|
return ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) |
|
|
((uint32_t)buf[2] << 8) | ((uint32_t)buf[3]);
|
|
}
|
|
|
|
static void
|
|
put_be16(uint8_t *buf, uint16_t val)
|
|
{
|
|
buf[0] = val >> 8;
|
|
buf[1] = val;
|
|
}
|
|
|
|
static void
|
|
put_be32(uint8_t *buf, uint32_t val)
|
|
{
|
|
buf[0] = val >> 24;
|
|
buf[1] = val >> 16;
|
|
buf[2] = val >> 8;
|
|
buf[3] = val & 0xff;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* ustorage_fs_verify
|
|
*
|
|
* Returns:
|
|
* 0: Success
|
|
* Else: Failure
|
|
*------------------------------------------------------------------------*/
|
|
static uint8_t
|
|
ustorage_fs_verify(struct ustorage_fs_softc *sc)
|
|
{
|
|
struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
|
|
uint32_t lba;
|
|
uint32_t vlen;
|
|
|
|
/*
|
|
* Get the starting Logical Block Address
|
|
*/
|
|
lba = get_be32(&sc->sc_cbw->CBWCDB[2]);
|
|
|
|
/*
|
|
* We allow DPO (Disable Page Out = don't save data in the cache)
|
|
* but we don't implement it.
|
|
*/
|
|
if ((sc->sc_cbw->CBWCDB[1] & ~0x10) != 0) {
|
|
currlun->sense_data = SS_INVALID_FIELD_IN_CDB;
|
|
return (1);
|
|
}
|
|
vlen = get_be16(&sc->sc_cbw->CBWCDB[7]);
|
|
if (vlen == 0) {
|
|
goto done;
|
|
}
|
|
/* No default reply */
|
|
|
|
/* Range check */
|
|
vlen += lba;
|
|
|
|
if ((vlen < lba) ||
|
|
(vlen > currlun->num_sectors) ||
|
|
(lba >= currlun->num_sectors)) {
|
|
currlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
|
|
return (1);
|
|
}
|
|
/* XXX TODO: verify that data is readable */
|
|
done:
|
|
return (ustorage_fs_min_len(sc, 0, -1U));
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* ustorage_fs_inquiry
|
|
*
|
|
* Returns:
|
|
* 0: Success
|
|
* Else: Failure
|
|
*------------------------------------------------------------------------*/
|
|
static uint8_t
|
|
ustorage_fs_inquiry(struct ustorage_fs_softc *sc)
|
|
{
|
|
uint8_t *buf = sc->sc_transfer.data_ptr;
|
|
|
|
struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
|
|
|
|
if (!sc->sc_transfer.currlun) {
|
|
/* Unsupported LUNs are okay */
|
|
memset(buf, 0, 36);
|
|
buf[0] = 0x7f;
|
|
/* Unsupported, no device - type */
|
|
return (ustorage_fs_min_len(sc, 36, -1U));
|
|
}
|
|
memset(buf, 0, 8);
|
|
/* Non - removable, direct - access device */
|
|
if (currlun->removable)
|
|
buf[1] = 0x80;
|
|
buf[2] = 2;
|
|
/* ANSI SCSI level 2 */
|
|
buf[3] = 2;
|
|
/* SCSI - 2 INQUIRY data format */
|
|
buf[4] = 31;
|
|
/* Additional length */
|
|
/* No special options */
|
|
/* Copy in ID string */
|
|
memcpy(buf + 8, USTORAGE_FS_ID_STRING, 28);
|
|
|
|
#if (USTORAGE_QDATA_MAX < 36)
|
|
#error "(USTORAGE_QDATA_MAX < 36)"
|
|
#endif
|
|
return (ustorage_fs_min_len(sc, 36, -1U));
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* ustorage_fs_request_sense
|
|
*
|
|
* Returns:
|
|
* 0: Success
|
|
* Else: Failure
|
|
*------------------------------------------------------------------------*/
|
|
static uint8_t
|
|
ustorage_fs_request_sense(struct ustorage_fs_softc *sc)
|
|
{
|
|
uint8_t *buf = sc->sc_transfer.data_ptr;
|
|
struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
|
|
uint32_t sd;
|
|
uint32_t sdinfo;
|
|
uint8_t valid;
|
|
|
|
/*
|
|
* From the SCSI-2 spec., section 7.9 (Unit attention condition):
|
|
*
|
|
* If a REQUEST SENSE command is received from an initiator
|
|
* with a pending unit attention condition (before the target
|
|
* generates the contingent allegiance condition), then the
|
|
* target shall either:
|
|
* a) report any pending sense data and preserve the unit
|
|
* attention condition on the logical unit, or,
|
|
* b) report the unit attention condition, may discard any
|
|
* pending sense data, and clear the unit attention
|
|
* condition on the logical unit for that initiator.
|
|
*
|
|
* FSG normally uses option a); enable this code to use option b).
|
|
*/
|
|
#if 0
|
|
if (currlun && currlun->unit_attention_data != SS_NO_SENSE) {
|
|
currlun->sense_data = currlun->unit_attention_data;
|
|
currlun->unit_attention_data = SS_NO_SENSE;
|
|
}
|
|
#endif
|
|
|
|
if (!currlun) {
|
|
/* Unsupported LUNs are okay */
|
|
sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
|
|
sdinfo = 0;
|
|
valid = 0;
|
|
} else {
|
|
sd = currlun->sense_data;
|
|
sdinfo = currlun->sense_data_info;
|
|
valid = currlun->info_valid << 7;
|
|
currlun->sense_data = SS_NO_SENSE;
|
|
currlun->sense_data_info = 0;
|
|
currlun->info_valid = 0;
|
|
}
|
|
|
|
memset(buf, 0, 18);
|
|
buf[0] = valid | 0x70;
|
|
/* Valid, current error */
|
|
buf[2] = SK(sd);
|
|
put_be32(&buf[3], sdinfo);
|
|
/* Sense information */
|
|
buf[7] = 18 - 8;
|
|
/* Additional sense length */
|
|
buf[12] = ASC(sd);
|
|
buf[13] = ASCQ(sd);
|
|
|
|
#if (USTORAGE_QDATA_MAX < 18)
|
|
#error "(USTORAGE_QDATA_MAX < 18)"
|
|
#endif
|
|
return (ustorage_fs_min_len(sc, 18, -1U));
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* ustorage_fs_read_capacity
|
|
*
|
|
* Returns:
|
|
* 0: Success
|
|
* Else: Failure
|
|
*------------------------------------------------------------------------*/
|
|
static uint8_t
|
|
ustorage_fs_read_capacity(struct ustorage_fs_softc *sc)
|
|
{
|
|
uint8_t *buf = sc->sc_transfer.data_ptr;
|
|
struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
|
|
uint32_t lba = get_be32(&sc->sc_cbw->CBWCDB[2]);
|
|
uint8_t pmi = sc->sc_cbw->CBWCDB[8];
|
|
|
|
/* Check the PMI and LBA fields */
|
|
if ((pmi > 1) || ((pmi == 0) && (lba != 0))) {
|
|
currlun->sense_data = SS_INVALID_FIELD_IN_CDB;
|
|
return (1);
|
|
}
|
|
/* Max logical block */
|
|
put_be32(&buf[0], currlun->num_sectors - 1);
|
|
/* Block length */
|
|
put_be32(&buf[4], 512);
|
|
|
|
#if (USTORAGE_QDATA_MAX < 8)
|
|
#error "(USTORAGE_QDATA_MAX < 8)"
|
|
#endif
|
|
return (ustorage_fs_min_len(sc, 8, -1U));
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* ustorage_fs_mode_sense
|
|
*
|
|
* Returns:
|
|
* 0: Success
|
|
* Else: Failure
|
|
*------------------------------------------------------------------------*/
|
|
static uint8_t
|
|
ustorage_fs_mode_sense(struct ustorage_fs_softc *sc)
|
|
{
|
|
uint8_t *buf = sc->sc_transfer.data_ptr;
|
|
struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
|
|
uint8_t *buf0;
|
|
uint16_t len;
|
|
uint16_t limit;
|
|
uint8_t mscmnd = sc->sc_cbw->CBWCDB[0];
|
|
uint8_t pc;
|
|
uint8_t page_code;
|
|
uint8_t changeable_values;
|
|
uint8_t all_pages;
|
|
|
|
buf0 = buf;
|
|
|
|
if ((sc->sc_cbw->CBWCDB[1] & ~0x08) != 0) {
|
|
/* Mask away DBD */
|
|
currlun->sense_data = SS_INVALID_FIELD_IN_CDB;
|
|
return (1);
|
|
}
|
|
pc = sc->sc_cbw->CBWCDB[2] >> 6;
|
|
page_code = sc->sc_cbw->CBWCDB[2] & 0x3f;
|
|
if (pc == 3) {
|
|
currlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED;
|
|
return (1);
|
|
}
|
|
changeable_values = (pc == 1);
|
|
all_pages = (page_code == 0x3f);
|
|
|
|
/*
|
|
* Write the mode parameter header. Fixed values are: default
|
|
* medium type, no cache control (DPOFUA), and no block descriptors.
|
|
* The only variable value is the WriteProtect bit. We will fill in
|
|
* the mode data length later.
|
|
*/
|
|
memset(buf, 0, 8);
|
|
if (mscmnd == SC_MODE_SENSE_6) {
|
|
buf[2] = (currlun->read_only ? 0x80 : 0x00);
|
|
/* WP, DPOFUA */
|
|
buf += 4;
|
|
limit = 255;
|
|
} else {
|
|
/* SC_MODE_SENSE_10 */
|
|
buf[3] = (currlun->read_only ? 0x80 : 0x00);
|
|
/* WP, DPOFUA */
|
|
buf += 8;
|
|
limit = 65535;
|
|
/* Should really be mod_data.buflen */
|
|
}
|
|
|
|
/* No block descriptors */
|
|
|
|
/*
|
|
* The mode pages, in numerical order.
|
|
*/
|
|
if ((page_code == 0x08) || all_pages) {
|
|
buf[0] = 0x08;
|
|
/* Page code */
|
|
buf[1] = 10;
|
|
/* Page length */
|
|
memset(buf + 2, 0, 10);
|
|
/* None of the fields are changeable */
|
|
|
|
if (!changeable_values) {
|
|
buf[2] = 0x04;
|
|
/* Write cache enable, */
|
|
/* Read cache not disabled */
|
|
/* No cache retention priorities */
|
|
put_be16(&buf[4], 0xffff);
|
|
/* Don 't disable prefetch */
|
|
/* Minimum prefetch = 0 */
|
|
put_be16(&buf[8], 0xffff);
|
|
/* Maximum prefetch */
|
|
put_be16(&buf[10], 0xffff);
|
|
/* Maximum prefetch ceiling */
|
|
}
|
|
buf += 12;
|
|
}
|
|
/*
|
|
* Check that a valid page was requested and the mode data length
|
|
* isn't too long.
|
|
*/
|
|
len = buf - buf0;
|
|
if (len > limit) {
|
|
currlun->sense_data = SS_INVALID_FIELD_IN_CDB;
|
|
return (1);
|
|
}
|
|
/* Store the mode data length */
|
|
if (mscmnd == SC_MODE_SENSE_6)
|
|
buf0[0] = len - 1;
|
|
else
|
|
put_be16(buf0, len - 2);
|
|
|
|
#if (USTORAGE_QDATA_MAX < 24)
|
|
#error "(USTORAGE_QDATA_MAX < 24)"
|
|
#endif
|
|
return (ustorage_fs_min_len(sc, len, -1U));
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* ustorage_fs_start_stop
|
|
*
|
|
* Returns:
|
|
* 0: Success
|
|
* Else: Failure
|
|
*------------------------------------------------------------------------*/
|
|
static uint8_t
|
|
ustorage_fs_start_stop(struct ustorage_fs_softc *sc)
|
|
{
|
|
struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
|
|
uint8_t loej;
|
|
uint8_t start;
|
|
uint8_t immed;
|
|
|
|
if (!currlun->removable) {
|
|
currlun->sense_data = SS_INVALID_COMMAND;
|
|
return (1);
|
|
}
|
|
immed = sc->sc_cbw->CBWCDB[1] & 0x01;
|
|
loej = sc->sc_cbw->CBWCDB[4] & 0x02;
|
|
start = sc->sc_cbw->CBWCDB[4] & 0x01;
|
|
|
|
if (immed || loej || start) {
|
|
/* compile fix */
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* ustorage_fs_prevent_allow
|
|
*
|
|
* Returns:
|
|
* 0: Success
|
|
* Else: Failure
|
|
*------------------------------------------------------------------------*/
|
|
static uint8_t
|
|
ustorage_fs_prevent_allow(struct ustorage_fs_softc *sc)
|
|
{
|
|
struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
|
|
uint8_t prevent;
|
|
|
|
if (!currlun->removable) {
|
|
currlun->sense_data = SS_INVALID_COMMAND;
|
|
return (1);
|
|
}
|
|
prevent = sc->sc_cbw->CBWCDB[4] & 0x01;
|
|
if ((sc->sc_cbw->CBWCDB[4] & ~0x01) != 0) {
|
|
/* Mask away Prevent */
|
|
currlun->sense_data = SS_INVALID_FIELD_IN_CDB;
|
|
return (1);
|
|
}
|
|
if (currlun->prevent_medium_removal && !prevent) {
|
|
//fsync_sub(currlun);
|
|
}
|
|
currlun->prevent_medium_removal = prevent;
|
|
return (0);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* ustorage_fs_read_format_capacities
|
|
*
|
|
* Returns:
|
|
* 0: Success
|
|
* Else: Failure
|
|
*------------------------------------------------------------------------*/
|
|
static uint8_t
|
|
ustorage_fs_read_format_capacities(struct ustorage_fs_softc *sc)
|
|
{
|
|
uint8_t *buf = sc->sc_transfer.data_ptr;
|
|
struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
|
|
|
|
buf[0] = buf[1] = buf[2] = 0;
|
|
buf[3] = 8;
|
|
/* Only the Current / Maximum Capacity Descriptor */
|
|
buf += 4;
|
|
|
|
/* Number of blocks */
|
|
put_be32(&buf[0], currlun->num_sectors);
|
|
/* Block length */
|
|
put_be32(&buf[4], 512);
|
|
/* Current capacity */
|
|
buf[4] = 0x02;
|
|
|
|
#if (USTORAGE_QDATA_MAX < 12)
|
|
#error "(USTORAGE_QDATA_MAX < 12)"
|
|
#endif
|
|
return (ustorage_fs_min_len(sc, 12, -1U));
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* ustorage_fs_mode_select
|
|
*
|
|
* Return values:
|
|
* 0: Success
|
|
* Else: Failure
|
|
*------------------------------------------------------------------------*/
|
|
static uint8_t
|
|
ustorage_fs_mode_select(struct ustorage_fs_softc *sc)
|
|
{
|
|
struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
|
|
|
|
/* We don't support MODE SELECT */
|
|
currlun->sense_data = SS_INVALID_COMMAND;
|
|
return (1);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* ustorage_fs_synchronize_cache
|
|
*
|
|
* Return values:
|
|
* 0: Success
|
|
* Else: Failure
|
|
*------------------------------------------------------------------------*/
|
|
static uint8_t
|
|
ustorage_fs_synchronize_cache(struct ustorage_fs_softc *sc)
|
|
{
|
|
#if 0
|
|
struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
|
|
uint8_t rc;
|
|
|
|
/*
|
|
* We ignore the requested LBA and write out all dirty data buffers.
|
|
*/
|
|
rc = 0;
|
|
if (rc) {
|
|
currlun->sense_data = SS_WRITE_ERROR;
|
|
}
|
|
#endif
|
|
return (0);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* ustorage_fs_read - read data from disk
|
|
*
|
|
* Return values:
|
|
* 0: Success
|
|
* Else: Failure
|
|
*------------------------------------------------------------------------*/
|
|
static uint8_t
|
|
ustorage_fs_read(struct ustorage_fs_softc *sc)
|
|
{
|
|
struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
|
|
uint64_t file_offset;
|
|
uint32_t lba;
|
|
uint32_t len;
|
|
|
|
/*
|
|
* Get the starting Logical Block Address and check that it's not
|
|
* too big
|
|
*/
|
|
if (sc->sc_cbw->CBWCDB[0] == SC_READ_6) {
|
|
lba = (((uint32_t)sc->sc_cbw->CBWCDB[1]) << 16) |
|
|
get_be16(&sc->sc_cbw->CBWCDB[2]);
|
|
} else {
|
|
lba = get_be32(&sc->sc_cbw->CBWCDB[2]);
|
|
|
|
/*
|
|
* We allow DPO (Disable Page Out = don't save data in the
|
|
* cache) and FUA (Force Unit Access = don't read from the
|
|
* cache), but we don't implement them.
|
|
*/
|
|
if ((sc->sc_cbw->CBWCDB[1] & ~0x18) != 0) {
|
|
currlun->sense_data = SS_INVALID_FIELD_IN_CDB;
|
|
return (1);
|
|
}
|
|
}
|
|
len = sc->sc_transfer.data_rem >> 9;
|
|
len += lba;
|
|
|
|
if ((len < lba) ||
|
|
(len > currlun->num_sectors) ||
|
|
(lba >= currlun->num_sectors)) {
|
|
currlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
|
|
return (1);
|
|
}
|
|
file_offset = lba;
|
|
file_offset <<= 9;
|
|
|
|
sc->sc_transfer.data_ptr = currlun->memory_image + file_offset;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* ustorage_fs_write - write data to disk
|
|
*
|
|
* Return values:
|
|
* 0: Success
|
|
* Else: Failure
|
|
*------------------------------------------------------------------------*/
|
|
static uint8_t
|
|
ustorage_fs_write(struct ustorage_fs_softc *sc)
|
|
{
|
|
struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
|
|
uint64_t file_offset;
|
|
uint32_t lba;
|
|
uint32_t len;
|
|
|
|
if (currlun->read_only) {
|
|
currlun->sense_data = SS_WRITE_PROTECTED;
|
|
return (1);
|
|
}
|
|
/* XXX clear SYNC */
|
|
|
|
/*
|
|
* Get the starting Logical Block Address and check that it's not
|
|
* too big.
|
|
*/
|
|
if (sc->sc_cbw->CBWCDB[0] == SC_WRITE_6)
|
|
lba = (((uint32_t)sc->sc_cbw->CBWCDB[1]) << 16) |
|
|
get_be16(&sc->sc_cbw->CBWCDB[2]);
|
|
else {
|
|
lba = get_be32(&sc->sc_cbw->CBWCDB[2]);
|
|
|
|
/*
|
|
* We allow DPO (Disable Page Out = don't save data in the
|
|
* cache) and FUA (Force Unit Access = write directly to the
|
|
* medium). We don't implement DPO; we implement FUA by
|
|
* performing synchronous output.
|
|
*/
|
|
if ((sc->sc_cbw->CBWCDB[1] & ~0x18) != 0) {
|
|
currlun->sense_data = SS_INVALID_FIELD_IN_CDB;
|
|
return (1);
|
|
}
|
|
if (sc->sc_cbw->CBWCDB[1] & 0x08) {
|
|
/* FUA */
|
|
/* XXX set SYNC flag here */
|
|
}
|
|
}
|
|
|
|
len = sc->sc_transfer.data_rem >> 9;
|
|
len += lba;
|
|
|
|
if ((len < lba) ||
|
|
(len > currlun->num_sectors) ||
|
|
(lba >= currlun->num_sectors)) {
|
|
currlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
|
|
return (1);
|
|
}
|
|
file_offset = lba;
|
|
file_offset <<= 9;
|
|
|
|
sc->sc_transfer.data_ptr = currlun->memory_image + file_offset;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* ustorage_fs_min_len
|
|
*
|
|
* Return values:
|
|
* 0: Success
|
|
* Else: Failure
|
|
*------------------------------------------------------------------------*/
|
|
static uint8_t
|
|
ustorage_fs_min_len(struct ustorage_fs_softc *sc, uint32_t len, uint32_t mask)
|
|
{
|
|
if (len != sc->sc_transfer.data_rem) {
|
|
if (sc->sc_transfer.cbw_dir == DIR_READ) {
|
|
/*
|
|
* there must be something wrong about this SCSI
|
|
* command
|
|
*/
|
|
sc->sc_csw->bCSWStatus = CSWSTATUS_PHASE;
|
|
return (1);
|
|
}
|
|
/* compute the minimum length */
|
|
|
|
if (sc->sc_transfer.data_rem > len) {
|
|
/* data ends prematurely */
|
|
sc->sc_transfer.data_rem = len;
|
|
sc->sc_transfer.data_short = 1;
|
|
}
|
|
/* check length alignment */
|
|
|
|
if (sc->sc_transfer.data_rem & ~mask) {
|
|
/* data ends prematurely */
|
|
sc->sc_transfer.data_rem &= mask;
|
|
sc->sc_transfer.data_short = 1;
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* ustorage_fs_check_cmd - check command routine
|
|
*
|
|
* Check whether the command is properly formed and whether its data
|
|
* size and direction agree with the values we already have.
|
|
*
|
|
* Return values:
|
|
* 0: Success
|
|
* Else: Failure
|
|
*------------------------------------------------------------------------*/
|
|
static uint8_t
|
|
ustorage_fs_check_cmd(struct ustorage_fs_softc *sc, uint8_t min_cmd_size,
|
|
uint16_t mask, uint8_t needs_medium)
|
|
{
|
|
struct ustorage_fs_lun *currlun;
|
|
uint8_t lun = (sc->sc_cbw->CBWCDB[1] >> 5);
|
|
uint8_t i;
|
|
|
|
/* Verify the length of the command itself */
|
|
if (min_cmd_size > sc->sc_transfer.cmd_len) {
|
|
DPRINTF("%u > %u\n",
|
|
min_cmd_size, sc->sc_transfer.cmd_len);
|
|
sc->sc_csw->bCSWStatus = CSWSTATUS_PHASE;
|
|
return (1);
|
|
}
|
|
/* Mask away the LUN */
|
|
sc->sc_cbw->CBWCDB[1] &= 0x1f;
|
|
|
|
/* Check if LUN is correct */
|
|
if (lun != sc->sc_transfer.lun) {
|
|
}
|
|
/* Check the LUN */
|
|
if (sc->sc_transfer.lun <= sc->sc_last_lun) {
|
|
sc->sc_transfer.currlun = currlun =
|
|
sc->sc_lun + sc->sc_transfer.lun;
|
|
if (sc->sc_cbw->CBWCDB[0] != SC_REQUEST_SENSE) {
|
|
currlun->sense_data = SS_NO_SENSE;
|
|
currlun->sense_data_info = 0;
|
|
currlun->info_valid = 0;
|
|
}
|
|
/*
|
|
* If a unit attention condition exists, only INQUIRY
|
|
* and REQUEST SENSE commands are allowed. Anything
|
|
* else must fail!
|
|
*/
|
|
if ((currlun->unit_attention_data != SS_NO_SENSE) &&
|
|
(sc->sc_cbw->CBWCDB[0] != SC_INQUIRY) &&
|
|
(sc->sc_cbw->CBWCDB[0] != SC_REQUEST_SENSE)) {
|
|
currlun->sense_data = currlun->unit_attention_data;
|
|
currlun->unit_attention_data = SS_NO_SENSE;
|
|
return (1);
|
|
}
|
|
} else {
|
|
sc->sc_transfer.currlun = currlun = NULL;
|
|
|
|
/*
|
|
* INQUIRY and REQUEST SENSE commands are explicitly allowed
|
|
* to use unsupported LUNs; all others may not.
|
|
*/
|
|
if ((sc->sc_cbw->CBWCDB[0] != SC_INQUIRY) &&
|
|
(sc->sc_cbw->CBWCDB[0] != SC_REQUEST_SENSE)) {
|
|
return (1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check that only command bytes listed in the mask are
|
|
* non-zero.
|
|
*/
|
|
for (i = 0; i != min_cmd_size; i++) {
|
|
if (sc->sc_cbw->CBWCDB[i] && !(mask & (1UL << i))) {
|
|
if (currlun) {
|
|
currlun->sense_data = SS_INVALID_FIELD_IN_CDB;
|
|
}
|
|
return (1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If the medium isn't mounted and the command needs to access
|
|
* it, return an error.
|
|
*/
|
|
if (currlun && (!currlun->memory_image) && needs_medium) {
|
|
currlun->sense_data = SS_MEDIUM_NOT_PRESENT;
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* ustorage_fs_do_cmd - do command
|
|
*
|
|
* Return values:
|
|
* 0: Success
|
|
* Else: Failure
|
|
*------------------------------------------------------------------------*/
|
|
static uint8_t
|
|
ustorage_fs_do_cmd(struct ustorage_fs_softc *sc)
|
|
{
|
|
uint8_t error = 1;
|
|
uint8_t i;
|
|
uint32_t temp;
|
|
const uint32_t mask9 = (0xFFFFFFFFUL >> 9) << 9;
|
|
|
|
/* set default data transfer pointer */
|
|
sc->sc_transfer.data_ptr = sc->sc_qdata;
|
|
|
|
DPRINTF("cmd_data[0]=0x%02x, data_rem=0x%08x\n",
|
|
sc->sc_cbw->CBWCDB[0], sc->sc_transfer.data_rem);
|
|
|
|
switch (sc->sc_cbw->CBWCDB[0]) {
|
|
case SC_INQUIRY:
|
|
sc->sc_transfer.cmd_dir = DIR_WRITE;
|
|
error = ustorage_fs_min_len(sc, sc->sc_cbw->CBWCDB[4], -1U);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_check_cmd(sc, 6,
|
|
(1UL << 4) | 1, 0);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_inquiry(sc);
|
|
|
|
break;
|
|
|
|
case SC_MODE_SELECT_6:
|
|
sc->sc_transfer.cmd_dir = DIR_READ;
|
|
error = ustorage_fs_min_len(sc, sc->sc_cbw->CBWCDB[4], -1U);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_check_cmd(sc, 6,
|
|
(1UL << 1) | (1UL << 4) | 1, 0);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_mode_select(sc);
|
|
|
|
break;
|
|
|
|
case SC_MODE_SELECT_10:
|
|
sc->sc_transfer.cmd_dir = DIR_READ;
|
|
error = ustorage_fs_min_len(sc,
|
|
get_be16(&sc->sc_cbw->CBWCDB[7]), -1U);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_check_cmd(sc, 10,
|
|
(1UL << 1) | (3UL << 7) | 1, 0);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_mode_select(sc);
|
|
|
|
break;
|
|
|
|
case SC_MODE_SENSE_6:
|
|
sc->sc_transfer.cmd_dir = DIR_WRITE;
|
|
error = ustorage_fs_min_len(sc, sc->sc_cbw->CBWCDB[4], -1U);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_check_cmd(sc, 6,
|
|
(1UL << 1) | (1UL << 2) | (1UL << 4) | 1, 0);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_mode_sense(sc);
|
|
|
|
break;
|
|
|
|
case SC_MODE_SENSE_10:
|
|
sc->sc_transfer.cmd_dir = DIR_WRITE;
|
|
error = ustorage_fs_min_len(sc,
|
|
get_be16(&sc->sc_cbw->CBWCDB[7]), -1U);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_check_cmd(sc, 10,
|
|
(1UL << 1) | (1UL << 2) | (3UL << 7) | 1, 0);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_mode_sense(sc);
|
|
|
|
break;
|
|
|
|
case SC_PREVENT_ALLOW_MEDIUM_REMOVAL:
|
|
error = ustorage_fs_min_len(sc, 0, -1U);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_check_cmd(sc, 6,
|
|
(1UL << 4) | 1, 0);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_prevent_allow(sc);
|
|
|
|
break;
|
|
|
|
case SC_READ_6:
|
|
i = sc->sc_cbw->CBWCDB[4];
|
|
sc->sc_transfer.cmd_dir = DIR_WRITE;
|
|
temp = ((i == 0) ? 256UL : i);
|
|
error = ustorage_fs_min_len(sc, temp << 9, mask9);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_check_cmd(sc, 6,
|
|
(7UL << 1) | (1UL << 4) | 1, 1);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_read(sc);
|
|
|
|
break;
|
|
|
|
case SC_READ_10:
|
|
sc->sc_transfer.cmd_dir = DIR_WRITE;
|
|
temp = get_be16(&sc->sc_cbw->CBWCDB[7]);
|
|
error = ustorage_fs_min_len(sc, temp << 9, mask9);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_check_cmd(sc, 10,
|
|
(1UL << 1) | (0xfUL << 2) | (3UL << 7) | 1, 1);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_read(sc);
|
|
|
|
break;
|
|
|
|
case SC_READ_12:
|
|
sc->sc_transfer.cmd_dir = DIR_WRITE;
|
|
temp = get_be32(&sc->sc_cbw->CBWCDB[6]);
|
|
if (temp >= (1UL << (32 - 9))) {
|
|
/* numerical overflow */
|
|
sc->sc_csw->bCSWStatus = CSWSTATUS_FAILED;
|
|
error = 1;
|
|
break;
|
|
}
|
|
error = ustorage_fs_min_len(sc, temp << 9, mask9);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_check_cmd(sc, 12,
|
|
(1UL << 1) | (0xfUL << 2) | (0xfUL << 6) | 1, 1);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_read(sc);
|
|
|
|
break;
|
|
|
|
case SC_READ_CAPACITY:
|
|
sc->sc_transfer.cmd_dir = DIR_WRITE;
|
|
error = ustorage_fs_check_cmd(sc, 10,
|
|
(0xfUL << 2) | (1UL << 8) | 1, 1);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_read_capacity(sc);
|
|
|
|
break;
|
|
|
|
case SC_READ_FORMAT_CAPACITIES:
|
|
sc->sc_transfer.cmd_dir = DIR_WRITE;
|
|
error = ustorage_fs_min_len(sc,
|
|
get_be16(&sc->sc_cbw->CBWCDB[7]), -1U);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_check_cmd(sc, 10,
|
|
(3UL << 7) | 1, 1);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_read_format_capacities(sc);
|
|
|
|
break;
|
|
|
|
case SC_REQUEST_SENSE:
|
|
sc->sc_transfer.cmd_dir = DIR_WRITE;
|
|
error = ustorage_fs_min_len(sc, sc->sc_cbw->CBWCDB[4], -1U);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_check_cmd(sc, 6,
|
|
(1UL << 4) | 1, 0);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_request_sense(sc);
|
|
|
|
break;
|
|
|
|
case SC_START_STOP_UNIT:
|
|
error = ustorage_fs_min_len(sc, 0, -1U);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_check_cmd(sc, 6,
|
|
(1UL << 1) | (1UL << 4) | 1, 0);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_start_stop(sc);
|
|
|
|
break;
|
|
|
|
case SC_SYNCHRONIZE_CACHE:
|
|
error = ustorage_fs_min_len(sc, 0, -1U);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_check_cmd(sc, 10,
|
|
(0xfUL << 2) | (3UL << 7) | 1, 1);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_synchronize_cache(sc);
|
|
|
|
break;
|
|
|
|
case SC_TEST_UNIT_READY:
|
|
error = ustorage_fs_min_len(sc, 0, -1U);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_check_cmd(sc, 6,
|
|
0 | 1, 1);
|
|
break;
|
|
|
|
/*
|
|
* Although optional, this command is used by MS-Windows.
|
|
* We support a minimal version: BytChk must be 0.
|
|
*/
|
|
case SC_VERIFY:
|
|
error = ustorage_fs_min_len(sc, 0, -1U);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_check_cmd(sc, 10,
|
|
(1UL << 1) | (0xfUL << 2) | (3UL << 7) | 1, 1);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_verify(sc);
|
|
|
|
break;
|
|
|
|
case SC_WRITE_6:
|
|
i = sc->sc_cbw->CBWCDB[4];
|
|
sc->sc_transfer.cmd_dir = DIR_READ;
|
|
temp = ((i == 0) ? 256UL : i);
|
|
error = ustorage_fs_min_len(sc, temp << 9, mask9);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_check_cmd(sc, 6,
|
|
(7UL << 1) | (1UL << 4) | 1, 1);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_write(sc);
|
|
|
|
break;
|
|
|
|
case SC_WRITE_10:
|
|
sc->sc_transfer.cmd_dir = DIR_READ;
|
|
temp = get_be16(&sc->sc_cbw->CBWCDB[7]);
|
|
error = ustorage_fs_min_len(sc, temp << 9, mask9);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_check_cmd(sc, 10,
|
|
(1UL << 1) | (0xfUL << 2) | (3UL << 7) | 1, 1);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_write(sc);
|
|
|
|
break;
|
|
|
|
case SC_WRITE_12:
|
|
sc->sc_transfer.cmd_dir = DIR_READ;
|
|
temp = get_be32(&sc->sc_cbw->CBWCDB[6]);
|
|
if (temp > (mask9 >> 9)) {
|
|
/* numerical overflow */
|
|
sc->sc_csw->bCSWStatus = CSWSTATUS_FAILED;
|
|
error = 1;
|
|
break;
|
|
}
|
|
error = ustorage_fs_min_len(sc, temp << 9, mask9);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_check_cmd(sc, 12,
|
|
(1UL << 1) | (0xfUL << 2) | (0xfUL << 6) | 1, 1);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_write(sc);
|
|
|
|
break;
|
|
|
|
/*
|
|
* Some mandatory commands that we recognize but don't
|
|
* implement. They don't mean much in this setting.
|
|
* It's left as an exercise for anyone interested to
|
|
* implement RESERVE and RELEASE in terms of Posix
|
|
* locks.
|
|
*/
|
|
case SC_FORMAT_UNIT:
|
|
case SC_RELEASE:
|
|
case SC_RESERVE:
|
|
case SC_SEND_DIAGNOSTIC:
|
|
/* Fallthrough */
|
|
|
|
default:
|
|
error = ustorage_fs_min_len(sc, 0, -1U);
|
|
if (error) {
|
|
break;
|
|
}
|
|
error = ustorage_fs_check_cmd(sc, sc->sc_transfer.cmd_len,
|
|
0xff, 0);
|
|
if (error) {
|
|
break;
|
|
}
|
|
sc->sc_transfer.currlun->sense_data =
|
|
SS_INVALID_COMMAND;
|
|
error = 1;
|
|
|
|
break;
|
|
}
|
|
return (error);
|
|
}
|