xref: /dragonfly/sys/bus/u4b/storage/ustorage_fs.c (revision 2b3f93ea)
112bd3c8bSSascha Wildner /* $FreeBSD$ */
212bd3c8bSSascha Wildner /*-
312bd3c8bSSascha Wildner  * Copyright (C) 2003-2005 Alan Stern
412bd3c8bSSascha Wildner  * Copyright (C) 2008 Hans Petter Selasky
512bd3c8bSSascha Wildner  * All rights reserved.
612bd3c8bSSascha Wildner  *
712bd3c8bSSascha Wildner  * Redistribution and use in source and binary forms, with or without
812bd3c8bSSascha Wildner  * modification, are permitted provided that the following conditions
912bd3c8bSSascha Wildner  * are met:
1012bd3c8bSSascha Wildner  * 1. Redistributions of source code must retain the above copyright
1112bd3c8bSSascha Wildner  *    notice, this list of conditions, and the following disclaimer,
1212bd3c8bSSascha Wildner  *    without modification.
1312bd3c8bSSascha Wildner  * 2. Redistributions in binary form must reproduce the above copyright
1412bd3c8bSSascha Wildner  *    notice, this list of conditions and the following disclaimer in the
1512bd3c8bSSascha Wildner  *    documentation and/or other materials provided with the distribution.
1612bd3c8bSSascha Wildner  * 3. The names of the above-listed copyright holders may not be used
1712bd3c8bSSascha Wildner  *    to endorse or promote products derived from this software without
1812bd3c8bSSascha Wildner  *    specific prior written permission.
1912bd3c8bSSascha Wildner  *
2012bd3c8bSSascha Wildner  *
2112bd3c8bSSascha Wildner  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
2212bd3c8bSSascha Wildner  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
2312bd3c8bSSascha Wildner  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2412bd3c8bSSascha Wildner  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
2512bd3c8bSSascha Wildner  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
2612bd3c8bSSascha Wildner  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2712bd3c8bSSascha Wildner  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
2812bd3c8bSSascha Wildner  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
2912bd3c8bSSascha Wildner  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
3012bd3c8bSSascha Wildner  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3112bd3c8bSSascha Wildner  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3212bd3c8bSSascha Wildner  */
3312bd3c8bSSascha Wildner 
3412bd3c8bSSascha Wildner /*
3512bd3c8bSSascha Wildner  * NOTE: Much of the SCSI statemachine handling code derives from the
3612bd3c8bSSascha Wildner  * Linux USB gadget stack.
3712bd3c8bSSascha Wildner  */
3812bd3c8bSSascha Wildner 
3912bd3c8bSSascha Wildner #include <sys/stdint.h>
4012bd3c8bSSascha Wildner #include <sys/param.h>
4112bd3c8bSSascha Wildner #include <sys/queue.h>
4212bd3c8bSSascha Wildner #include <sys/types.h>
4312bd3c8bSSascha Wildner #include <sys/systm.h>
4412bd3c8bSSascha Wildner #include <sys/kernel.h>
4512bd3c8bSSascha Wildner #include <sys/bus.h>
4612bd3c8bSSascha Wildner #include <sys/module.h>
4712bd3c8bSSascha Wildner #include <sys/lock.h>
4812bd3c8bSSascha Wildner #include <sys/condvar.h>
4912bd3c8bSSascha Wildner #include <sys/sysctl.h>
5012bd3c8bSSascha Wildner #include <sys/unistd.h>
5112bd3c8bSSascha Wildner #include <sys/callout.h>
5212bd3c8bSSascha Wildner #include <sys/malloc.h>
53*2b3f93eaSMatthew Dillon #include <sys/caps.h>
5412bd3c8bSSascha Wildner 
55722d05c3SSascha Wildner #include <bus/u4b/usb.h>
56722d05c3SSascha Wildner #include <bus/u4b/usbdi.h>
57ef4aa9ffSSascha Wildner #include "usbdevs.h"
5812bd3c8bSSascha Wildner #include "usb_if.h"
5912bd3c8bSSascha Wildner 
6012bd3c8bSSascha Wildner #define	USB_DEBUG_VAR ustorage_fs_debug
61722d05c3SSascha Wildner #include <bus/u4b/usb_debug.h>
6212bd3c8bSSascha Wildner 
6312bd3c8bSSascha Wildner #ifdef USB_DEBUG
6412bd3c8bSSascha Wildner static int ustorage_fs_debug = 0;
6512bd3c8bSSascha Wildner 
6612bd3c8bSSascha Wildner SYSCTL_NODE(_hw_usb, OID_AUTO, ustorage_fs, CTLFLAG_RW, 0, "USB ustorage_fs");
67a9b765b7SSascha Wildner SYSCTL_INT(_hw_usb_ustorage_fs, OID_AUTO, debug, CTLFLAG_RW,
6812bd3c8bSSascha Wildner     &ustorage_fs_debug, 0, "ustorage_fs debug level");
6912bd3c8bSSascha Wildner #endif
7012bd3c8bSSascha Wildner 
7112bd3c8bSSascha Wildner /* Define some limits */
7212bd3c8bSSascha Wildner 
7312bd3c8bSSascha Wildner #ifndef USTORAGE_FS_BULK_SIZE
7412bd3c8bSSascha Wildner #define	USTORAGE_FS_BULK_SIZE (1UL << 17)	/* bytes */
7512bd3c8bSSascha Wildner #endif
7612bd3c8bSSascha Wildner 
7712bd3c8bSSascha Wildner #ifndef	USTORAGE_FS_MAX_LUN
7812bd3c8bSSascha Wildner #define	USTORAGE_FS_MAX_LUN	8	/* units */
7912bd3c8bSSascha Wildner #endif
8012bd3c8bSSascha Wildner 
8112bd3c8bSSascha Wildner #ifndef USTORAGE_QDATA_MAX
8212bd3c8bSSascha Wildner #define	USTORAGE_QDATA_MAX	40	/* bytes */
8312bd3c8bSSascha Wildner #endif
8412bd3c8bSSascha Wildner 
8512bd3c8bSSascha Wildner #define sc_cmd_data sc_cbw.CBWCDB
8612bd3c8bSSascha Wildner 
8712bd3c8bSSascha Wildner /*
8812bd3c8bSSascha Wildner  * The SCSI ID string must be exactly 28 characters long
8912bd3c8bSSascha Wildner  * exluding the terminating zero.
9012bd3c8bSSascha Wildner  */
9112bd3c8bSSascha Wildner #ifndef USTORAGE_FS_ID_STRING
9212bd3c8bSSascha Wildner #define	USTORAGE_FS_ID_STRING \
9312bd3c8bSSascha Wildner 	"FreeBSD " /* 8 */ \
9412bd3c8bSSascha Wildner 	"File-Stor Gadget" /* 16 */ \
9512bd3c8bSSascha Wildner 	"0101" /* 4 */
9612bd3c8bSSascha Wildner #endif
9712bd3c8bSSascha Wildner 
9812bd3c8bSSascha Wildner /*
9912bd3c8bSSascha Wildner  * The following macro defines the number of
10012bd3c8bSSascha Wildner  * sectors to be allocated for the RAM disk:
10112bd3c8bSSascha Wildner  */
10212bd3c8bSSascha Wildner #ifndef USTORAGE_FS_RAM_SECT
10312bd3c8bSSascha Wildner #define	USTORAGE_FS_RAM_SECT (1UL << 13)
10412bd3c8bSSascha Wildner #endif
10512bd3c8bSSascha Wildner 
10612bd3c8bSSascha Wildner static uint8_t *ustorage_fs_ramdisk;
10712bd3c8bSSascha Wildner 
10812bd3c8bSSascha Wildner /* USB transfer definitions */
10912bd3c8bSSascha Wildner 
11012bd3c8bSSascha Wildner #define	USTORAGE_FS_T_BBB_COMMAND     0
11112bd3c8bSSascha Wildner #define	USTORAGE_FS_T_BBB_DATA_DUMP   1
11212bd3c8bSSascha Wildner #define	USTORAGE_FS_T_BBB_DATA_READ   2
11312bd3c8bSSascha Wildner #define	USTORAGE_FS_T_BBB_DATA_WRITE  3
11412bd3c8bSSascha Wildner #define	USTORAGE_FS_T_BBB_STATUS      4
11512bd3c8bSSascha Wildner #define	USTORAGE_FS_T_BBB_MAX         5
11612bd3c8bSSascha Wildner 
11712bd3c8bSSascha Wildner /* USB data stage direction */
11812bd3c8bSSascha Wildner 
11912bd3c8bSSascha Wildner #define	DIR_NONE	0
12012bd3c8bSSascha Wildner #define	DIR_READ	1
12112bd3c8bSSascha Wildner #define	DIR_WRITE	2
12212bd3c8bSSascha Wildner 
12312bd3c8bSSascha Wildner /* USB interface specific control request */
12412bd3c8bSSascha Wildner 
12512bd3c8bSSascha Wildner #define	UR_BBB_RESET		0xff	/* Bulk-Only reset */
12612bd3c8bSSascha Wildner #define	UR_BBB_GET_MAX_LUN	0xfe	/* Get maximum lun */
12712bd3c8bSSascha Wildner 
12812bd3c8bSSascha Wildner /* Command Block Wrapper */
12912bd3c8bSSascha Wildner typedef struct {
13012bd3c8bSSascha Wildner 	uDWord	dCBWSignature;
13112bd3c8bSSascha Wildner #define	CBWSIGNATURE	0x43425355
13212bd3c8bSSascha Wildner 	uDWord	dCBWTag;
13312bd3c8bSSascha Wildner 	uDWord	dCBWDataTransferLength;
13412bd3c8bSSascha Wildner 	uByte	bCBWFlags;
13512bd3c8bSSascha Wildner #define	CBWFLAGS_OUT	0x00
13612bd3c8bSSascha Wildner #define	CBWFLAGS_IN	0x80
13712bd3c8bSSascha Wildner 	uByte	bCBWLUN;
13812bd3c8bSSascha Wildner 	uByte	bCDBLength;
13912bd3c8bSSascha Wildner #define	CBWCDBLENGTH	16
14012bd3c8bSSascha Wildner 	uByte	CBWCDB[CBWCDBLENGTH];
14112bd3c8bSSascha Wildner } __packed ustorage_fs_bbb_cbw_t;
14212bd3c8bSSascha Wildner 
14312bd3c8bSSascha Wildner #define	USTORAGE_FS_BBB_CBW_SIZE	31
14412bd3c8bSSascha Wildner 
14512bd3c8bSSascha Wildner /* Command Status Wrapper */
14612bd3c8bSSascha Wildner typedef struct {
14712bd3c8bSSascha Wildner 	uDWord	dCSWSignature;
14812bd3c8bSSascha Wildner #define	CSWSIGNATURE	0x53425355
14912bd3c8bSSascha Wildner 	uDWord	dCSWTag;
15012bd3c8bSSascha Wildner 	uDWord	dCSWDataResidue;
15112bd3c8bSSascha Wildner 	uByte	bCSWStatus;
15212bd3c8bSSascha Wildner #define	CSWSTATUS_GOOD	0x0
15312bd3c8bSSascha Wildner #define	CSWSTATUS_FAILED	0x1
15412bd3c8bSSascha Wildner #define	CSWSTATUS_PHASE	0x2
15512bd3c8bSSascha Wildner } __packed ustorage_fs_bbb_csw_t;
15612bd3c8bSSascha Wildner 
15712bd3c8bSSascha Wildner #define	USTORAGE_FS_BBB_CSW_SIZE	13
15812bd3c8bSSascha Wildner 
15912bd3c8bSSascha Wildner struct ustorage_fs_lun {
16012bd3c8bSSascha Wildner 
16112bd3c8bSSascha Wildner 	uint8_t	*memory_image;
16212bd3c8bSSascha Wildner 
16312bd3c8bSSascha Wildner 	uint32_t num_sectors;
16412bd3c8bSSascha Wildner 	uint32_t sense_data;
16512bd3c8bSSascha Wildner 	uint32_t sense_data_info;
16612bd3c8bSSascha Wildner 	uint32_t unit_attention_data;
16712bd3c8bSSascha Wildner 
16812bd3c8bSSascha Wildner 	uint8_t	read_only:1;
16912bd3c8bSSascha Wildner 	uint8_t	prevent_medium_removal:1;
17012bd3c8bSSascha Wildner 	uint8_t	info_valid:1;
17112bd3c8bSSascha Wildner 	uint8_t	removable:1;
17212bd3c8bSSascha Wildner };
17312bd3c8bSSascha Wildner 
17412bd3c8bSSascha Wildner struct ustorage_fs_softc {
17512bd3c8bSSascha Wildner 
176a41b1dd4SMarkus Pfeiffer 	ustorage_fs_bbb_cbw_t *sc_cbw;	/* Command Wrapper Block */
177a41b1dd4SMarkus Pfeiffer 	ustorage_fs_bbb_csw_t *sc_csw;	/* Command Status Block */
178a41b1dd4SMarkus Pfeiffer 	void *sc_dma_ptr;		/* Main data buffer */
17912bd3c8bSSascha Wildner 
180722d05c3SSascha Wildner 	struct lock sc_lock;
18112bd3c8bSSascha Wildner 
18212bd3c8bSSascha Wildner 	struct ustorage_fs_lun sc_lun[USTORAGE_FS_MAX_LUN];
18312bd3c8bSSascha Wildner 
18412bd3c8bSSascha Wildner 	struct {
18512bd3c8bSSascha Wildner 		uint8_t *data_ptr;
18612bd3c8bSSascha Wildner 		struct ustorage_fs_lun *currlun;
18712bd3c8bSSascha Wildner 
18812bd3c8bSSascha Wildner 		uint32_t data_rem;	/* bytes, as reported by the command
18912bd3c8bSSascha Wildner 					 * block wrapper */
19012bd3c8bSSascha Wildner 		uint32_t offset;	/* bytes */
19112bd3c8bSSascha Wildner 
19212bd3c8bSSascha Wildner 		uint8_t	cbw_dir;
19312bd3c8bSSascha Wildner 		uint8_t	cmd_dir;
19412bd3c8bSSascha Wildner 		uint8_t	lun;
19512bd3c8bSSascha Wildner 		uint8_t	cmd_len;
19612bd3c8bSSascha Wildner 		uint8_t	data_short:1;
19712bd3c8bSSascha Wildner 		uint8_t	data_error:1;
19812bd3c8bSSascha Wildner 	}	sc_transfer;
19912bd3c8bSSascha Wildner 
20012bd3c8bSSascha Wildner 	device_t sc_dev;
20112bd3c8bSSascha Wildner 	struct usb_device *sc_udev;
20212bd3c8bSSascha Wildner 	struct usb_xfer *sc_xfer[USTORAGE_FS_T_BBB_MAX];
20312bd3c8bSSascha Wildner 
20412bd3c8bSSascha Wildner 	uint8_t	sc_iface_no;		/* interface number */
20512bd3c8bSSascha Wildner 	uint8_t	sc_last_lun;
20612bd3c8bSSascha Wildner 	uint8_t	sc_last_xfer_index;
20712bd3c8bSSascha Wildner 	uint8_t	sc_qdata[USTORAGE_QDATA_MAX];
20812bd3c8bSSascha Wildner };
20912bd3c8bSSascha Wildner 
21012bd3c8bSSascha Wildner /* prototypes */
21112bd3c8bSSascha Wildner 
21212bd3c8bSSascha Wildner static device_probe_t ustorage_fs_probe;
21312bd3c8bSSascha Wildner static device_attach_t ustorage_fs_attach;
21412bd3c8bSSascha Wildner static device_detach_t ustorage_fs_detach;
21512bd3c8bSSascha Wildner static device_suspend_t ustorage_fs_suspend;
21612bd3c8bSSascha Wildner static device_resume_t ustorage_fs_resume;
21712bd3c8bSSascha Wildner static usb_handle_request_t ustorage_fs_handle_request;
21812bd3c8bSSascha Wildner 
21912bd3c8bSSascha Wildner static usb_callback_t ustorage_fs_t_bbb_command_callback;
22012bd3c8bSSascha Wildner static usb_callback_t ustorage_fs_t_bbb_data_dump_callback;
22112bd3c8bSSascha Wildner static usb_callback_t ustorage_fs_t_bbb_data_read_callback;
22212bd3c8bSSascha Wildner static usb_callback_t ustorage_fs_t_bbb_data_write_callback;
22312bd3c8bSSascha Wildner static usb_callback_t ustorage_fs_t_bbb_status_callback;
22412bd3c8bSSascha Wildner 
22512bd3c8bSSascha Wildner static void ustorage_fs_transfer_start(struct ustorage_fs_softc *sc, uint8_t xfer_index);
22612bd3c8bSSascha Wildner static void ustorage_fs_transfer_stop(struct ustorage_fs_softc *sc);
22712bd3c8bSSascha Wildner 
22812bd3c8bSSascha Wildner static uint8_t ustorage_fs_verify(struct ustorage_fs_softc *sc);
22912bd3c8bSSascha Wildner static uint8_t ustorage_fs_inquiry(struct ustorage_fs_softc *sc);
23012bd3c8bSSascha Wildner static uint8_t ustorage_fs_request_sense(struct ustorage_fs_softc *sc);
23112bd3c8bSSascha Wildner static uint8_t ustorage_fs_read_capacity(struct ustorage_fs_softc *sc);
23212bd3c8bSSascha Wildner static uint8_t ustorage_fs_mode_sense(struct ustorage_fs_softc *sc);
23312bd3c8bSSascha Wildner static uint8_t ustorage_fs_start_stop(struct ustorage_fs_softc *sc);
23412bd3c8bSSascha Wildner static uint8_t ustorage_fs_prevent_allow(struct ustorage_fs_softc *sc);
23512bd3c8bSSascha Wildner static uint8_t ustorage_fs_read_format_capacities(struct ustorage_fs_softc *sc);
23612bd3c8bSSascha Wildner static uint8_t ustorage_fs_mode_select(struct ustorage_fs_softc *sc);
23712bd3c8bSSascha Wildner static uint8_t ustorage_fs_min_len(struct ustorage_fs_softc *sc, uint32_t len, uint32_t mask);
23812bd3c8bSSascha Wildner static uint8_t ustorage_fs_read(struct ustorage_fs_softc *sc);
23912bd3c8bSSascha Wildner static uint8_t ustorage_fs_write(struct ustorage_fs_softc *sc);
24012bd3c8bSSascha Wildner static uint8_t ustorage_fs_check_cmd(struct ustorage_fs_softc *sc, uint8_t cmd_size, uint16_t mask, uint8_t needs_medium);
24112bd3c8bSSascha Wildner static uint8_t ustorage_fs_do_cmd(struct ustorage_fs_softc *sc);
24212bd3c8bSSascha Wildner 
24312bd3c8bSSascha Wildner static device_method_t ustorage_fs_methods[] = {
24412bd3c8bSSascha Wildner 	/* USB interface */
24512bd3c8bSSascha Wildner 	DEVMETHOD(usb_handle_request, ustorage_fs_handle_request),
24612bd3c8bSSascha Wildner 
24712bd3c8bSSascha Wildner 	/* Device interface */
24812bd3c8bSSascha Wildner 	DEVMETHOD(device_probe, ustorage_fs_probe),
24912bd3c8bSSascha Wildner 	DEVMETHOD(device_attach, ustorage_fs_attach),
25012bd3c8bSSascha Wildner 	DEVMETHOD(device_detach, ustorage_fs_detach),
25112bd3c8bSSascha Wildner 	DEVMETHOD(device_suspend, ustorage_fs_suspend),
25212bd3c8bSSascha Wildner 	DEVMETHOD(device_resume, ustorage_fs_resume),
25312bd3c8bSSascha Wildner 
254d3c9c58eSSascha Wildner 	DEVMETHOD_END
25512bd3c8bSSascha Wildner };
25612bd3c8bSSascha Wildner 
25712bd3c8bSSascha Wildner static driver_t ustorage_fs_driver = {
25812bd3c8bSSascha Wildner 	.name = "ustorage_fs",
25912bd3c8bSSascha Wildner 	.methods = ustorage_fs_methods,
26012bd3c8bSSascha Wildner 	.size = sizeof(struct ustorage_fs_softc),
26112bd3c8bSSascha Wildner };
26212bd3c8bSSascha Wildner 
26312bd3c8bSSascha Wildner static devclass_t ustorage_fs_devclass;
26412bd3c8bSSascha Wildner 
26515f415f6SSascha Wildner DRIVER_MODULE(ustorage_fs, uhub, ustorage_fs_driver, ustorage_fs_devclass, NULL, NULL);
26612bd3c8bSSascha Wildner MODULE_VERSION(ustorage_fs, 0);
26712bd3c8bSSascha Wildner MODULE_DEPEND(ustorage_fs, usb, 1, 1, 1);
26812bd3c8bSSascha Wildner 
26912bd3c8bSSascha Wildner static struct usb_config ustorage_fs_bbb_config[USTORAGE_FS_T_BBB_MAX] = {
27012bd3c8bSSascha Wildner 
27112bd3c8bSSascha Wildner 	[USTORAGE_FS_T_BBB_COMMAND] = {
27212bd3c8bSSascha Wildner 		.type = UE_BULK,
27312bd3c8bSSascha Wildner 		.endpoint = UE_ADDR_ANY,
27412bd3c8bSSascha Wildner 		.direction = UE_DIR_OUT,
27512bd3c8bSSascha Wildner 		.bufsize = sizeof(ustorage_fs_bbb_cbw_t),
27612bd3c8bSSascha Wildner 		.callback = &ustorage_fs_t_bbb_command_callback,
27712bd3c8bSSascha Wildner 		.usb_mode = USB_MODE_DEVICE,
27812bd3c8bSSascha Wildner 	},
27912bd3c8bSSascha Wildner 
28012bd3c8bSSascha Wildner 	[USTORAGE_FS_T_BBB_DATA_DUMP] = {
28112bd3c8bSSascha Wildner 		.type = UE_BULK,
28212bd3c8bSSascha Wildner 		.endpoint = UE_ADDR_ANY,
28312bd3c8bSSascha Wildner 		.direction = UE_DIR_OUT,
28412bd3c8bSSascha Wildner 		.bufsize = 0,	/* use wMaxPacketSize */
28512bd3c8bSSascha Wildner 		.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,},
28612bd3c8bSSascha Wildner 		.callback = &ustorage_fs_t_bbb_data_dump_callback,
28712bd3c8bSSascha Wildner 		.usb_mode = USB_MODE_DEVICE,
28812bd3c8bSSascha Wildner 	},
28912bd3c8bSSascha Wildner 
29012bd3c8bSSascha Wildner 	[USTORAGE_FS_T_BBB_DATA_READ] = {
29112bd3c8bSSascha Wildner 		.type = UE_BULK,
29212bd3c8bSSascha Wildner 		.endpoint = UE_ADDR_ANY,
29312bd3c8bSSascha Wildner 		.direction = UE_DIR_OUT,
29412bd3c8bSSascha Wildner 		.bufsize = USTORAGE_FS_BULK_SIZE,
295a41b1dd4SMarkus Pfeiffer 		.flags = {.proxy_buffer = 1,.short_xfer_ok = 1},
29612bd3c8bSSascha Wildner 		.callback = &ustorage_fs_t_bbb_data_read_callback,
29712bd3c8bSSascha Wildner 		.usb_mode = USB_MODE_DEVICE,
29812bd3c8bSSascha Wildner 	},
29912bd3c8bSSascha Wildner 
30012bd3c8bSSascha Wildner 	[USTORAGE_FS_T_BBB_DATA_WRITE] = {
30112bd3c8bSSascha Wildner 		.type = UE_BULK,
30212bd3c8bSSascha Wildner 		.endpoint = UE_ADDR_ANY,
30312bd3c8bSSascha Wildner 		.direction = UE_DIR_IN,
30412bd3c8bSSascha Wildner 		.bufsize = USTORAGE_FS_BULK_SIZE,
30512bd3c8bSSascha Wildner 		.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,.ext_buffer = 1},
30612bd3c8bSSascha Wildner 		.callback = &ustorage_fs_t_bbb_data_write_callback,
30712bd3c8bSSascha Wildner 		.usb_mode = USB_MODE_DEVICE,
30812bd3c8bSSascha Wildner 	},
30912bd3c8bSSascha Wildner 
31012bd3c8bSSascha Wildner 	[USTORAGE_FS_T_BBB_STATUS] = {
31112bd3c8bSSascha Wildner 		.type = UE_BULK,
31212bd3c8bSSascha Wildner 		.endpoint = UE_ADDR_ANY,
31312bd3c8bSSascha Wildner 		.direction = UE_DIR_IN,
31412bd3c8bSSascha Wildner 		.bufsize = sizeof(ustorage_fs_bbb_csw_t),
315a41b1dd4SMarkus Pfeiffer 		.flags = {.short_xfer_ok = 1},
31612bd3c8bSSascha Wildner 		.callback = &ustorage_fs_t_bbb_status_callback,
31712bd3c8bSSascha Wildner 		.usb_mode = USB_MODE_DEVICE,
31812bd3c8bSSascha Wildner 	},
31912bd3c8bSSascha Wildner };
32012bd3c8bSSascha Wildner 
32112bd3c8bSSascha Wildner /*
32212bd3c8bSSascha Wildner  * USB device probe/attach/detach
32312bd3c8bSSascha Wildner  */
32412bd3c8bSSascha Wildner 
32512bd3c8bSSascha Wildner static int
ustorage_fs_probe(device_t dev)32612bd3c8bSSascha Wildner ustorage_fs_probe(device_t dev)
32712bd3c8bSSascha Wildner {
32812bd3c8bSSascha Wildner 	struct usb_attach_arg *uaa = device_get_ivars(dev);
32912bd3c8bSSascha Wildner 	struct usb_interface_descriptor *id;
33012bd3c8bSSascha Wildner 
33112bd3c8bSSascha Wildner 	if (uaa->usb_mode != USB_MODE_DEVICE) {
33212bd3c8bSSascha Wildner 		return (ENXIO);
33312bd3c8bSSascha Wildner 	}
33412bd3c8bSSascha Wildner 	/* Check for a standards compliant device */
33512bd3c8bSSascha Wildner 	id = usbd_get_interface_descriptor(uaa->iface);
33612bd3c8bSSascha Wildner 	if ((id == NULL) ||
33712bd3c8bSSascha Wildner 	    (id->bInterfaceClass != UICLASS_MASS) ||
33812bd3c8bSSascha Wildner 	    (id->bInterfaceSubClass != UISUBCLASS_SCSI) ||
33912bd3c8bSSascha Wildner 	    (id->bInterfaceProtocol != UIPROTO_MASS_BBB)) {
34012bd3c8bSSascha Wildner 		return (ENXIO);
34112bd3c8bSSascha Wildner 	}
34212bd3c8bSSascha Wildner 	return (BUS_PROBE_GENERIC);
34312bd3c8bSSascha Wildner }
34412bd3c8bSSascha Wildner 
34512bd3c8bSSascha Wildner static int
ustorage_fs_attach(device_t dev)34612bd3c8bSSascha Wildner ustorage_fs_attach(device_t dev)
34712bd3c8bSSascha Wildner {
34812bd3c8bSSascha Wildner 	struct ustorage_fs_softc *sc = device_get_softc(dev);
34912bd3c8bSSascha Wildner 	struct usb_attach_arg *uaa = device_get_ivars(dev);
35012bd3c8bSSascha Wildner 	struct usb_interface_descriptor *id;
35112bd3c8bSSascha Wildner 	int err;
35212bd3c8bSSascha Wildner 	int unit;
35312bd3c8bSSascha Wildner 
35412bd3c8bSSascha Wildner 	/*
35512bd3c8bSSascha Wildner 	 * NOTE: the softc struct is cleared in device_set_driver.
35612bd3c8bSSascha Wildner 	 * We can safely call ustorage_fs_detach without specifically
35712bd3c8bSSascha Wildner 	 * initializing the struct.
35812bd3c8bSSascha Wildner 	 */
35912bd3c8bSSascha Wildner 
36012bd3c8bSSascha Wildner 	sc->sc_dev = dev;
36112bd3c8bSSascha Wildner 	sc->sc_udev = uaa->device;
36212bd3c8bSSascha Wildner 	unit = device_get_unit(dev);
36312bd3c8bSSascha Wildner 
36412bd3c8bSSascha Wildner 	/* enable power saving mode */
36512bd3c8bSSascha Wildner 	usbd_set_power_mode(uaa->device, USB_POWER_MODE_SAVE);
36612bd3c8bSSascha Wildner 
36712bd3c8bSSascha Wildner 	if (unit == 0) {
36812bd3c8bSSascha Wildner 		if (ustorage_fs_ramdisk == NULL) {
36912bd3c8bSSascha Wildner 			/*
37012bd3c8bSSascha Wildner 			 * allocate a memory image for our ramdisk until
37112bd3c8bSSascha Wildner 			 * further
37212bd3c8bSSascha Wildner 			 */
37312bd3c8bSSascha Wildner 			ustorage_fs_ramdisk =
374722d05c3SSascha Wildner 			    kmalloc(USTORAGE_FS_RAM_SECT << 9, M_USB,
37512bd3c8bSSascha Wildner 			    M_ZERO | M_WAITOK);
376a41b1dd4SMarkus Pfeiffer 
377a41b1dd4SMarkus Pfeiffer 			if (ustorage_fs_ramdisk == NULL) {
378a41b1dd4SMarkus Pfeiffer 				return (ENOMEM);
379a41b1dd4SMarkus Pfeiffer 			}
38012bd3c8bSSascha Wildner 		}
38112bd3c8bSSascha Wildner 		sc->sc_lun[0].memory_image = ustorage_fs_ramdisk;
38212bd3c8bSSascha Wildner 		sc->sc_lun[0].num_sectors = USTORAGE_FS_RAM_SECT;
38312bd3c8bSSascha Wildner 		sc->sc_lun[0].removable = 1;
38412bd3c8bSSascha Wildner 	}
38512bd3c8bSSascha Wildner 
38612bd3c8bSSascha Wildner 	device_set_usb_desc(dev);
38712bd3c8bSSascha Wildner 
388722d05c3SSascha Wildner 	lockinit(&sc->sc_lock, "USTORAGE_FS lock", 0, LK_CANRECURSE);
38963da4a34SSascha Wildner 
39012bd3c8bSSascha Wildner 	/* get interface index */
39112bd3c8bSSascha Wildner 
39212bd3c8bSSascha Wildner 	id = usbd_get_interface_descriptor(uaa->iface);
39312bd3c8bSSascha Wildner 	if (id == NULL) {
39412bd3c8bSSascha Wildner 		device_printf(dev, "failed to get "
39512bd3c8bSSascha Wildner 		    "interface number\n");
39612bd3c8bSSascha Wildner 		goto detach;
39712bd3c8bSSascha Wildner 	}
39812bd3c8bSSascha Wildner 	sc->sc_iface_no = id->bInterfaceNumber;
39912bd3c8bSSascha Wildner 
40012bd3c8bSSascha Wildner 	err = usbd_transfer_setup(uaa->device,
40112bd3c8bSSascha Wildner 	    &uaa->info.bIfaceIndex, sc->sc_xfer, ustorage_fs_bbb_config,
402722d05c3SSascha Wildner 	    USTORAGE_FS_T_BBB_MAX, sc, &sc->sc_lock);
40312bd3c8bSSascha Wildner 	if (err) {
40412bd3c8bSSascha Wildner 		device_printf(dev, "could not setup required "
40512bd3c8bSSascha Wildner 		    "transfers, %s\n", usbd_errstr(err));
40612bd3c8bSSascha Wildner 		goto detach;
40712bd3c8bSSascha Wildner 	}
408a41b1dd4SMarkus Pfeiffer 
409a41b1dd4SMarkus Pfeiffer 	sc->sc_cbw = usbd_xfer_get_frame_buffer(sc->sc_xfer[
410a41b1dd4SMarkus Pfeiffer 	    USTORAGE_FS_T_BBB_COMMAND], 0);
411a41b1dd4SMarkus Pfeiffer 	sc->sc_csw = usbd_xfer_get_frame_buffer(sc->sc_xfer[
412a41b1dd4SMarkus Pfeiffer 	    USTORAGE_FS_T_BBB_STATUS], 0);
413a41b1dd4SMarkus Pfeiffer  	sc->sc_dma_ptr = usbd_xfer_get_frame_buffer(sc->sc_xfer[
414a41b1dd4SMarkus Pfeiffer 	    USTORAGE_FS_T_BBB_DATA_READ], 0);
415a41b1dd4SMarkus Pfeiffer 
41612bd3c8bSSascha Wildner 	/* start Mass Storage State Machine */
41712bd3c8bSSascha Wildner 
418722d05c3SSascha Wildner 	lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
41912bd3c8bSSascha Wildner 	ustorage_fs_transfer_start(sc, USTORAGE_FS_T_BBB_COMMAND);
420722d05c3SSascha Wildner 	lockmgr(&sc->sc_lock, LK_RELEASE);
42112bd3c8bSSascha Wildner 
42212bd3c8bSSascha Wildner 	return (0);			/* success */
42312bd3c8bSSascha Wildner 
42412bd3c8bSSascha Wildner detach:
42512bd3c8bSSascha Wildner 	ustorage_fs_detach(dev);
42612bd3c8bSSascha Wildner 	return (ENXIO);			/* failure */
42712bd3c8bSSascha Wildner }
42812bd3c8bSSascha Wildner 
42912bd3c8bSSascha Wildner static int
ustorage_fs_detach(device_t dev)43012bd3c8bSSascha Wildner ustorage_fs_detach(device_t dev)
43112bd3c8bSSascha Wildner {
43212bd3c8bSSascha Wildner 	struct ustorage_fs_softc *sc = device_get_softc(dev);
43312bd3c8bSSascha Wildner 
43412bd3c8bSSascha Wildner 	/* teardown our statemachine */
43512bd3c8bSSascha Wildner 
43612bd3c8bSSascha Wildner 	usbd_transfer_unsetup(sc->sc_xfer, USTORAGE_FS_T_BBB_MAX);
43712bd3c8bSSascha Wildner 
438722d05c3SSascha Wildner 	lockuninit(&sc->sc_lock);
43912bd3c8bSSascha Wildner 
44012bd3c8bSSascha Wildner 	return (0);			/* success */
44112bd3c8bSSascha Wildner }
44212bd3c8bSSascha Wildner 
44312bd3c8bSSascha Wildner static int
ustorage_fs_suspend(device_t dev)44412bd3c8bSSascha Wildner ustorage_fs_suspend(device_t dev)
44512bd3c8bSSascha Wildner {
44612bd3c8bSSascha Wildner 	device_printf(dev, "suspending\n");
44712bd3c8bSSascha Wildner 	return (0);			/* success */
44812bd3c8bSSascha Wildner }
44912bd3c8bSSascha Wildner 
45012bd3c8bSSascha Wildner static int
ustorage_fs_resume(device_t dev)45112bd3c8bSSascha Wildner ustorage_fs_resume(device_t dev)
45212bd3c8bSSascha Wildner {
45312bd3c8bSSascha Wildner 	device_printf(dev, "resuming\n");
45412bd3c8bSSascha Wildner 	return (0);			/* success */
45512bd3c8bSSascha Wildner }
45612bd3c8bSSascha Wildner 
45712bd3c8bSSascha Wildner /*
45812bd3c8bSSascha Wildner  * Generic functions to handle transfers
45912bd3c8bSSascha Wildner  */
46012bd3c8bSSascha Wildner 
46112bd3c8bSSascha Wildner static void
ustorage_fs_transfer_start(struct ustorage_fs_softc * sc,uint8_t xfer_index)46212bd3c8bSSascha Wildner ustorage_fs_transfer_start(struct ustorage_fs_softc *sc, uint8_t xfer_index)
46312bd3c8bSSascha Wildner {
46412bd3c8bSSascha Wildner 	if (sc->sc_xfer[xfer_index]) {
46512bd3c8bSSascha Wildner 		sc->sc_last_xfer_index = xfer_index;
46612bd3c8bSSascha Wildner 		usbd_transfer_start(sc->sc_xfer[xfer_index]);
46712bd3c8bSSascha Wildner 	}
46812bd3c8bSSascha Wildner }
46912bd3c8bSSascha Wildner 
47012bd3c8bSSascha Wildner static void
ustorage_fs_transfer_stop(struct ustorage_fs_softc * sc)47112bd3c8bSSascha Wildner ustorage_fs_transfer_stop(struct ustorage_fs_softc *sc)
47212bd3c8bSSascha Wildner {
47312bd3c8bSSascha Wildner 	usbd_transfer_stop(sc->sc_xfer[sc->sc_last_xfer_index]);
474722d05c3SSascha Wildner 	lockmgr(&sc->sc_lock, LK_RELEASE);
47512bd3c8bSSascha Wildner 	usbd_transfer_drain(sc->sc_xfer[sc->sc_last_xfer_index]);
476722d05c3SSascha Wildner 	lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
47712bd3c8bSSascha Wildner }
47812bd3c8bSSascha Wildner 
47912bd3c8bSSascha Wildner static int
ustorage_fs_handle_request(device_t dev,const void * preq,void ** pptr,uint16_t * plen,uint16_t offset,uint8_t * pstate)48012bd3c8bSSascha Wildner ustorage_fs_handle_request(device_t dev,
48112bd3c8bSSascha Wildner     const void *preq, void **pptr, uint16_t *plen,
48212bd3c8bSSascha Wildner     uint16_t offset, uint8_t *pstate)
48312bd3c8bSSascha Wildner {
48412bd3c8bSSascha Wildner 	struct ustorage_fs_softc *sc = device_get_softc(dev);
48512bd3c8bSSascha Wildner 	const struct usb_device_request *req = preq;
48612bd3c8bSSascha Wildner 	uint8_t is_complete = *pstate;
48712bd3c8bSSascha Wildner 
48812bd3c8bSSascha Wildner 	if (!is_complete) {
48912bd3c8bSSascha Wildner 		if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
49012bd3c8bSSascha Wildner 		    (req->bRequest == UR_BBB_RESET)) {
49112bd3c8bSSascha Wildner 			*plen = 0;
492722d05c3SSascha Wildner 			lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
49312bd3c8bSSascha Wildner 			ustorage_fs_transfer_stop(sc);
49412bd3c8bSSascha Wildner 			sc->sc_transfer.data_error = 1;
49512bd3c8bSSascha Wildner 			ustorage_fs_transfer_start(sc,
49612bd3c8bSSascha Wildner 			    USTORAGE_FS_T_BBB_COMMAND);
497722d05c3SSascha Wildner 			lockmgr(&sc->sc_lock, LK_RELEASE);
49812bd3c8bSSascha Wildner 			return (0);
49912bd3c8bSSascha Wildner 		} else if ((req->bmRequestType == UT_READ_CLASS_INTERFACE) &&
50012bd3c8bSSascha Wildner 			   (req->bRequest == UR_BBB_GET_MAX_LUN)) {
50112bd3c8bSSascha Wildner 			if (offset == 0) {
50212bd3c8bSSascha Wildner 				*plen = 1;
50312bd3c8bSSascha Wildner 				*pptr = &sc->sc_last_lun;
50412bd3c8bSSascha Wildner 			} else {
50512bd3c8bSSascha Wildner 				*plen = 0;
50612bd3c8bSSascha Wildner 			}
50712bd3c8bSSascha Wildner 			return (0);
50812bd3c8bSSascha Wildner 		}
50912bd3c8bSSascha Wildner 	}
51012bd3c8bSSascha Wildner 	return (ENXIO);			/* use builtin handler */
51112bd3c8bSSascha Wildner }
51212bd3c8bSSascha Wildner 
51312bd3c8bSSascha Wildner static void
ustorage_fs_t_bbb_command_callback(struct usb_xfer * xfer,usb_error_t error)51412bd3c8bSSascha Wildner ustorage_fs_t_bbb_command_callback(struct usb_xfer *xfer, usb_error_t error)
51512bd3c8bSSascha Wildner {
51612bd3c8bSSascha Wildner 	struct ustorage_fs_softc *sc = usbd_xfer_softc(xfer);
51712bd3c8bSSascha Wildner 	uint32_t tag;
51812bd3c8bSSascha Wildner 	uint8_t err = 0;
51912bd3c8bSSascha Wildner 
52012bd3c8bSSascha Wildner 	DPRINTF("\n");
52112bd3c8bSSascha Wildner 
52212bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
52312bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
52412bd3c8bSSascha Wildner 
525a41b1dd4SMarkus Pfeiffer 		tag = UGETDW(sc->sc_cbw->dCBWSignature);
52612bd3c8bSSascha Wildner 
52712bd3c8bSSascha Wildner 		if (tag != CBWSIGNATURE) {
52812bd3c8bSSascha Wildner 			/* do nothing */
52912bd3c8bSSascha Wildner 			DPRINTF("invalid signature 0x%08x\n", tag);
53012bd3c8bSSascha Wildner 			break;
53112bd3c8bSSascha Wildner 		}
532a41b1dd4SMarkus Pfeiffer 		tag = UGETDW(sc->sc_cbw->dCBWTag);
53312bd3c8bSSascha Wildner 
53412bd3c8bSSascha Wildner 		/* echo back tag */
535a41b1dd4SMarkus Pfeiffer 		USETDW(sc->sc_csw->dCSWTag, tag);
53612bd3c8bSSascha Wildner 
53712bd3c8bSSascha Wildner 		/* reset status */
538a41b1dd4SMarkus Pfeiffer 		sc->sc_csw->bCSWStatus = 0;
53912bd3c8bSSascha Wildner 
54012bd3c8bSSascha Wildner 		/* reset data offset, data length and data remainder */
54112bd3c8bSSascha Wildner 		sc->sc_transfer.offset = 0;
54212bd3c8bSSascha Wildner 		sc->sc_transfer.data_rem =
543a41b1dd4SMarkus Pfeiffer 		    UGETDW(sc->sc_cbw->dCBWDataTransferLength);
54412bd3c8bSSascha Wildner 
54512bd3c8bSSascha Wildner 		/* reset data flags */
54612bd3c8bSSascha Wildner 		sc->sc_transfer.data_short = 0;
54712bd3c8bSSascha Wildner 
54812bd3c8bSSascha Wildner 		/* extract LUN */
549a41b1dd4SMarkus Pfeiffer 		sc->sc_transfer.lun = sc->sc_cbw->bCBWLUN;
55012bd3c8bSSascha Wildner 
55112bd3c8bSSascha Wildner 		if (sc->sc_transfer.data_rem == 0) {
55212bd3c8bSSascha Wildner 			sc->sc_transfer.cbw_dir = DIR_NONE;
55312bd3c8bSSascha Wildner 		} else {
554a41b1dd4SMarkus Pfeiffer 			if (sc->sc_cbw->bCBWFlags & CBWFLAGS_IN) {
55512bd3c8bSSascha Wildner 				sc->sc_transfer.cbw_dir = DIR_WRITE;
55612bd3c8bSSascha Wildner 			} else {
55712bd3c8bSSascha Wildner 				sc->sc_transfer.cbw_dir = DIR_READ;
55812bd3c8bSSascha Wildner 			}
55912bd3c8bSSascha Wildner 		}
56012bd3c8bSSascha Wildner 
561a41b1dd4SMarkus Pfeiffer 		sc->sc_transfer.cmd_len = sc->sc_cbw->bCDBLength;
562a41b1dd4SMarkus Pfeiffer 		if ((sc->sc_transfer.cmd_len > sizeof(sc->sc_cbw->CBWCDB)) ||
56312bd3c8bSSascha Wildner 		    (sc->sc_transfer.cmd_len == 0)) {
56412bd3c8bSSascha Wildner 			/* just halt - this is invalid */
56512bd3c8bSSascha Wildner 			DPRINTF("invalid command length %d bytes\n",
56612bd3c8bSSascha Wildner 			    sc->sc_transfer.cmd_len);
56712bd3c8bSSascha Wildner 			break;
56812bd3c8bSSascha Wildner 		}
56912bd3c8bSSascha Wildner 
57012bd3c8bSSascha Wildner 		err = ustorage_fs_do_cmd(sc);
57112bd3c8bSSascha Wildner 		if (err) {
57212bd3c8bSSascha Wildner 			/* got an error */
57312bd3c8bSSascha Wildner 			DPRINTF("command failed\n");
57412bd3c8bSSascha Wildner 			break;
57512bd3c8bSSascha Wildner 		}
57612bd3c8bSSascha Wildner 		if ((sc->sc_transfer.data_rem > 0) &&
57712bd3c8bSSascha Wildner 		    (sc->sc_transfer.cbw_dir != sc->sc_transfer.cmd_dir)) {
57812bd3c8bSSascha Wildner 			/* contradicting data transfer direction */
57912bd3c8bSSascha Wildner 			err = 1;
58012bd3c8bSSascha Wildner 			DPRINTF("data direction mismatch\n");
58112bd3c8bSSascha Wildner 			break;
58212bd3c8bSSascha Wildner 		}
58312bd3c8bSSascha Wildner 		switch (sc->sc_transfer.cbw_dir) {
58412bd3c8bSSascha Wildner 		case DIR_READ:
58512bd3c8bSSascha Wildner 			ustorage_fs_transfer_start(sc, USTORAGE_FS_T_BBB_DATA_READ);
58612bd3c8bSSascha Wildner 			break;
58712bd3c8bSSascha Wildner 		case DIR_WRITE:
58812bd3c8bSSascha Wildner 			ustorage_fs_transfer_start(sc, USTORAGE_FS_T_BBB_DATA_WRITE);
58912bd3c8bSSascha Wildner 			break;
59012bd3c8bSSascha Wildner 		default:
59112bd3c8bSSascha Wildner 			ustorage_fs_transfer_start(sc,
59212bd3c8bSSascha Wildner 			    USTORAGE_FS_T_BBB_STATUS);
59312bd3c8bSSascha Wildner 			break;
59412bd3c8bSSascha Wildner 		}
59512bd3c8bSSascha Wildner 		break;
59612bd3c8bSSascha Wildner 
59712bd3c8bSSascha Wildner 	case USB_ST_SETUP:
59812bd3c8bSSascha Wildner tr_setup:
59912bd3c8bSSascha Wildner 		if (sc->sc_transfer.data_error) {
60012bd3c8bSSascha Wildner 			sc->sc_transfer.data_error = 0;
60112bd3c8bSSascha Wildner 			usbd_xfer_set_stall(xfer);
60212bd3c8bSSascha Wildner 			DPRINTF("stall pipe\n");
60312bd3c8bSSascha Wildner 		}
604a41b1dd4SMarkus Pfeiffer 		usbd_xfer_set_frame_len(xfer, 0,
605a41b1dd4SMarkus Pfeiffer 		    sizeof(ustorage_fs_bbb_cbw_t));
60612bd3c8bSSascha Wildner 		usbd_transfer_submit(xfer);
60712bd3c8bSSascha Wildner 		break;
60812bd3c8bSSascha Wildner 
60912bd3c8bSSascha Wildner 	default:			/* Error */
61012bd3c8bSSascha Wildner 		DPRINTF("error\n");
61112bd3c8bSSascha Wildner 		if (error == USB_ERR_CANCELLED) {
61212bd3c8bSSascha Wildner 			break;
61312bd3c8bSSascha Wildner 		}
61412bd3c8bSSascha Wildner 		/* If the pipe is already stalled, don't do another stall */
61512bd3c8bSSascha Wildner 		if (!usbd_xfer_is_stalled(xfer))
61612bd3c8bSSascha Wildner 			sc->sc_transfer.data_error = 1;
61712bd3c8bSSascha Wildner 
61812bd3c8bSSascha Wildner 		/* try again */
61912bd3c8bSSascha Wildner 		goto tr_setup;
62012bd3c8bSSascha Wildner 	}
62112bd3c8bSSascha Wildner 	if (err) {
622a41b1dd4SMarkus Pfeiffer 		if (sc->sc_csw->bCSWStatus == 0) {
62312bd3c8bSSascha Wildner 			/* set some default error code */
624a41b1dd4SMarkus Pfeiffer 			sc->sc_csw->bCSWStatus = CSWSTATUS_FAILED;
62512bd3c8bSSascha Wildner 		}
62612bd3c8bSSascha Wildner 		if (sc->sc_transfer.cbw_dir == DIR_READ) {
62712bd3c8bSSascha Wildner 			/* dump all data */
62812bd3c8bSSascha Wildner 			ustorage_fs_transfer_start(sc,
62912bd3c8bSSascha Wildner 			    USTORAGE_FS_T_BBB_DATA_DUMP);
63012bd3c8bSSascha Wildner 			return;
63112bd3c8bSSascha Wildner 		}
63212bd3c8bSSascha Wildner 		if (sc->sc_transfer.cbw_dir == DIR_WRITE) {
63312bd3c8bSSascha Wildner 			/* need to stall before status */
63412bd3c8bSSascha Wildner 			sc->sc_transfer.data_error = 1;
63512bd3c8bSSascha Wildner 		}
63612bd3c8bSSascha Wildner 		ustorage_fs_transfer_start(sc, USTORAGE_FS_T_BBB_STATUS);
63712bd3c8bSSascha Wildner 	}
63812bd3c8bSSascha Wildner }
63912bd3c8bSSascha Wildner 
64012bd3c8bSSascha Wildner static void
ustorage_fs_t_bbb_data_dump_callback(struct usb_xfer * xfer,usb_error_t error)64112bd3c8bSSascha Wildner ustorage_fs_t_bbb_data_dump_callback(struct usb_xfer *xfer, usb_error_t error)
64212bd3c8bSSascha Wildner {
64312bd3c8bSSascha Wildner 	struct ustorage_fs_softc *sc = usbd_xfer_softc(xfer);
64412bd3c8bSSascha Wildner 	uint32_t max_bulk = usbd_xfer_max_len(xfer);
64512bd3c8bSSascha Wildner 	int actlen, sumlen;
64612bd3c8bSSascha Wildner 
64712bd3c8bSSascha Wildner 	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
64812bd3c8bSSascha Wildner 
64912bd3c8bSSascha Wildner 	DPRINTF("\n");
65012bd3c8bSSascha Wildner 
65112bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
65212bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
65312bd3c8bSSascha Wildner 		sc->sc_transfer.data_rem -= actlen;
65412bd3c8bSSascha Wildner 		sc->sc_transfer.offset += actlen;
65512bd3c8bSSascha Wildner 
65612bd3c8bSSascha Wildner 		if (actlen != sumlen || sc->sc_transfer.data_rem == 0) {
65712bd3c8bSSascha Wildner 			/* short transfer or end of data */
65812bd3c8bSSascha Wildner 			ustorage_fs_transfer_start(sc,
65912bd3c8bSSascha Wildner 			    USTORAGE_FS_T_BBB_STATUS);
66012bd3c8bSSascha Wildner 			break;
66112bd3c8bSSascha Wildner 		}
66212bd3c8bSSascha Wildner 		/* Fallthrough */
66312bd3c8bSSascha Wildner 
66412bd3c8bSSascha Wildner 	case USB_ST_SETUP:
66512bd3c8bSSascha Wildner tr_setup:
66612bd3c8bSSascha Wildner 		if (max_bulk > sc->sc_transfer.data_rem) {
66712bd3c8bSSascha Wildner 			max_bulk = sc->sc_transfer.data_rem;
66812bd3c8bSSascha Wildner 		}
66912bd3c8bSSascha Wildner 		if (sc->sc_transfer.data_error) {
67012bd3c8bSSascha Wildner 			sc->sc_transfer.data_error = 0;
67112bd3c8bSSascha Wildner 			usbd_xfer_set_stall(xfer);
67212bd3c8bSSascha Wildner 		}
67312bd3c8bSSascha Wildner 		usbd_xfer_set_frame_len(xfer, 0, max_bulk);
67412bd3c8bSSascha Wildner 		usbd_transfer_submit(xfer);
67512bd3c8bSSascha Wildner 		break;
67612bd3c8bSSascha Wildner 
67712bd3c8bSSascha Wildner 	default:			/* Error */
67812bd3c8bSSascha Wildner 		if (error == USB_ERR_CANCELLED) {
67912bd3c8bSSascha Wildner 			break;
68012bd3c8bSSascha Wildner 		}
68112bd3c8bSSascha Wildner 		/*
68212bd3c8bSSascha Wildner 		 * If the pipe is already stalled, don't do another stall:
68312bd3c8bSSascha Wildner 		 */
68412bd3c8bSSascha Wildner 		if (!usbd_xfer_is_stalled(xfer))
68512bd3c8bSSascha Wildner 			sc->sc_transfer.data_error = 1;
68612bd3c8bSSascha Wildner 
68712bd3c8bSSascha Wildner 		/* try again */
68812bd3c8bSSascha Wildner 		goto tr_setup;
68912bd3c8bSSascha Wildner 	}
69012bd3c8bSSascha Wildner }
69112bd3c8bSSascha Wildner 
69212bd3c8bSSascha Wildner static void
ustorage_fs_t_bbb_data_read_callback(struct usb_xfer * xfer,usb_error_t error)69312bd3c8bSSascha Wildner ustorage_fs_t_bbb_data_read_callback(struct usb_xfer *xfer, usb_error_t error)
69412bd3c8bSSascha Wildner {
69512bd3c8bSSascha Wildner 	struct ustorage_fs_softc *sc = usbd_xfer_softc(xfer);
69612bd3c8bSSascha Wildner 	uint32_t max_bulk = usbd_xfer_max_len(xfer);
69712bd3c8bSSascha Wildner 	int actlen, sumlen;
69812bd3c8bSSascha Wildner 
69912bd3c8bSSascha Wildner 	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
70012bd3c8bSSascha Wildner 
70112bd3c8bSSascha Wildner 	DPRINTF("\n");
70212bd3c8bSSascha Wildner 
70312bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
70412bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
705a41b1dd4SMarkus Pfeiffer 		/* XXX copy data from DMA buffer */
706a41b1dd4SMarkus Pfeiffer 		memcpy(sc->sc_transfer.data_ptr, sc->sc_dma_ptr, actlen);
707a41b1dd4SMarkus Pfeiffer 
70812bd3c8bSSascha Wildner 		sc->sc_transfer.data_rem -= actlen;
70912bd3c8bSSascha Wildner 		sc->sc_transfer.data_ptr += actlen;
71012bd3c8bSSascha Wildner 		sc->sc_transfer.offset += actlen;
71112bd3c8bSSascha Wildner 
71212bd3c8bSSascha Wildner 		if (actlen != sumlen || sc->sc_transfer.data_rem == 0) {
71312bd3c8bSSascha Wildner 			/* short transfer or end of data */
71412bd3c8bSSascha Wildner 			ustorage_fs_transfer_start(sc,
71512bd3c8bSSascha Wildner 			    USTORAGE_FS_T_BBB_STATUS);
71612bd3c8bSSascha Wildner 			break;
71712bd3c8bSSascha Wildner 		}
71812bd3c8bSSascha Wildner 		/* Fallthrough */
71912bd3c8bSSascha Wildner 
72012bd3c8bSSascha Wildner 	case USB_ST_SETUP:
72112bd3c8bSSascha Wildner tr_setup:
72212bd3c8bSSascha Wildner 		if (max_bulk > sc->sc_transfer.data_rem) {
72312bd3c8bSSascha Wildner 			max_bulk = sc->sc_transfer.data_rem;
72412bd3c8bSSascha Wildner 		}
72512bd3c8bSSascha Wildner 		if (sc->sc_transfer.data_error) {
72612bd3c8bSSascha Wildner 			sc->sc_transfer.data_error = 0;
72712bd3c8bSSascha Wildner 			usbd_xfer_set_stall(xfer);
72812bd3c8bSSascha Wildner 		}
72912bd3c8bSSascha Wildner 
730a41b1dd4SMarkus Pfeiffer 		usbd_xfer_set_frame_data(xfer, 0, sc->sc_dma_ptr, max_bulk);
73112bd3c8bSSascha Wildner 		usbd_transfer_submit(xfer);
73212bd3c8bSSascha Wildner 		break;
73312bd3c8bSSascha Wildner 
73412bd3c8bSSascha Wildner 	default:			/* Error */
73512bd3c8bSSascha Wildner 		if (error == USB_ERR_CANCELLED) {
73612bd3c8bSSascha Wildner 			break;
73712bd3c8bSSascha Wildner 		}
73812bd3c8bSSascha Wildner 		/* If the pipe is already stalled, don't do another stall */
73912bd3c8bSSascha Wildner 		if (!usbd_xfer_is_stalled(xfer))
74012bd3c8bSSascha Wildner 			sc->sc_transfer.data_error = 1;
74112bd3c8bSSascha Wildner 
74212bd3c8bSSascha Wildner 		/* try again */
74312bd3c8bSSascha Wildner 		goto tr_setup;
74412bd3c8bSSascha Wildner 	}
74512bd3c8bSSascha Wildner }
74612bd3c8bSSascha Wildner 
74712bd3c8bSSascha Wildner static void
ustorage_fs_t_bbb_data_write_callback(struct usb_xfer * xfer,usb_error_t error)74812bd3c8bSSascha Wildner ustorage_fs_t_bbb_data_write_callback(struct usb_xfer *xfer, usb_error_t error)
74912bd3c8bSSascha Wildner {
75012bd3c8bSSascha Wildner 	struct ustorage_fs_softc *sc = usbd_xfer_softc(xfer);
75112bd3c8bSSascha Wildner 	uint32_t max_bulk = usbd_xfer_max_len(xfer);
75212bd3c8bSSascha Wildner 	int actlen, sumlen;
75312bd3c8bSSascha Wildner 
75412bd3c8bSSascha Wildner 	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
75512bd3c8bSSascha Wildner 
75612bd3c8bSSascha Wildner 	DPRINTF("\n");
75712bd3c8bSSascha Wildner 
75812bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
75912bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
76012bd3c8bSSascha Wildner 		sc->sc_transfer.data_rem -= actlen;
76112bd3c8bSSascha Wildner 		sc->sc_transfer.data_ptr += actlen;
76212bd3c8bSSascha Wildner 		sc->sc_transfer.offset += actlen;
76312bd3c8bSSascha Wildner 
76412bd3c8bSSascha Wildner 		if (actlen != sumlen || sc->sc_transfer.data_rem == 0) {
76512bd3c8bSSascha Wildner 			/* short transfer or end of data */
76612bd3c8bSSascha Wildner 			ustorage_fs_transfer_start(sc,
76712bd3c8bSSascha Wildner 			    USTORAGE_FS_T_BBB_STATUS);
76812bd3c8bSSascha Wildner 			break;
76912bd3c8bSSascha Wildner 		}
77012bd3c8bSSascha Wildner 	case USB_ST_SETUP:
77112bd3c8bSSascha Wildner tr_setup:
77212bd3c8bSSascha Wildner 		if (max_bulk >= sc->sc_transfer.data_rem) {
77312bd3c8bSSascha Wildner 			max_bulk = sc->sc_transfer.data_rem;
77412bd3c8bSSascha Wildner 			if (sc->sc_transfer.data_short)
77512bd3c8bSSascha Wildner 				usbd_xfer_set_flag(xfer, USB_FORCE_SHORT_XFER);
77612bd3c8bSSascha Wildner 			else
77712bd3c8bSSascha Wildner 				usbd_xfer_clr_flag(xfer, USB_FORCE_SHORT_XFER);
77812bd3c8bSSascha Wildner 		} else
77912bd3c8bSSascha Wildner 			usbd_xfer_clr_flag(xfer, USB_FORCE_SHORT_XFER);
78012bd3c8bSSascha Wildner 
78112bd3c8bSSascha Wildner 		if (sc->sc_transfer.data_error) {
78212bd3c8bSSascha Wildner 			sc->sc_transfer.data_error = 0;
78312bd3c8bSSascha Wildner 			usbd_xfer_set_stall(xfer);
78412bd3c8bSSascha Wildner 		}
78512bd3c8bSSascha Wildner 
786a41b1dd4SMarkus Pfeiffer 		/* XXX copy data to DMA buffer */
787a41b1dd4SMarkus Pfeiffer 		memcpy(sc->sc_dma_ptr, sc->sc_transfer.data_ptr, max_bulk);
788a41b1dd4SMarkus Pfeiffer 
789a41b1dd4SMarkus Pfeiffer 		usbd_xfer_set_frame_data(xfer, 0, sc->sc_dma_ptr, max_bulk);
79012bd3c8bSSascha Wildner 		usbd_transfer_submit(xfer);
79112bd3c8bSSascha Wildner 		break;
79212bd3c8bSSascha Wildner 
79312bd3c8bSSascha Wildner 	default:			/* Error */
79412bd3c8bSSascha Wildner 		if (error == USB_ERR_CANCELLED) {
79512bd3c8bSSascha Wildner 			break;
79612bd3c8bSSascha Wildner 		}
79712bd3c8bSSascha Wildner 		/*
79812bd3c8bSSascha Wildner 		 * If the pipe is already stalled, don't do another
79912bd3c8bSSascha Wildner 		 * stall
80012bd3c8bSSascha Wildner 		 */
80112bd3c8bSSascha Wildner 		if (!usbd_xfer_is_stalled(xfer))
80212bd3c8bSSascha Wildner 			sc->sc_transfer.data_error = 1;
80312bd3c8bSSascha Wildner 
80412bd3c8bSSascha Wildner 		/* try again */
80512bd3c8bSSascha Wildner 		goto tr_setup;
80612bd3c8bSSascha Wildner 	}
80712bd3c8bSSascha Wildner }
80812bd3c8bSSascha Wildner 
80912bd3c8bSSascha Wildner static void
ustorage_fs_t_bbb_status_callback(struct usb_xfer * xfer,usb_error_t error)81012bd3c8bSSascha Wildner ustorage_fs_t_bbb_status_callback(struct usb_xfer *xfer, usb_error_t error)
81112bd3c8bSSascha Wildner {
81212bd3c8bSSascha Wildner 	struct ustorage_fs_softc *sc = usbd_xfer_softc(xfer);
81312bd3c8bSSascha Wildner 
81412bd3c8bSSascha Wildner 	DPRINTF("\n");
81512bd3c8bSSascha Wildner 
81612bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
81712bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
81812bd3c8bSSascha Wildner 		ustorage_fs_transfer_start(sc, USTORAGE_FS_T_BBB_COMMAND);
81912bd3c8bSSascha Wildner 		break;
82012bd3c8bSSascha Wildner 
82112bd3c8bSSascha Wildner 	case USB_ST_SETUP:
82212bd3c8bSSascha Wildner tr_setup:
823a41b1dd4SMarkus Pfeiffer 		USETDW(sc->sc_csw->dCSWSignature, CSWSIGNATURE);
824a41b1dd4SMarkus Pfeiffer 		USETDW(sc->sc_csw->dCSWDataResidue, sc->sc_transfer.data_rem);
82512bd3c8bSSascha Wildner 
82612bd3c8bSSascha Wildner 		if (sc->sc_transfer.data_error) {
82712bd3c8bSSascha Wildner 			sc->sc_transfer.data_error = 0;
82812bd3c8bSSascha Wildner 			usbd_xfer_set_stall(xfer);
82912bd3c8bSSascha Wildner 		}
830a41b1dd4SMarkus Pfeiffer 		usbd_xfer_set_frame_len(xfer, 0,
831a41b1dd4SMarkus Pfeiffer 		    sizeof(ustorage_fs_bbb_csw_t));
83212bd3c8bSSascha Wildner 		usbd_transfer_submit(xfer);
83312bd3c8bSSascha Wildner 		break;
83412bd3c8bSSascha Wildner 
83512bd3c8bSSascha Wildner 	default:
83612bd3c8bSSascha Wildner 		if (error == USB_ERR_CANCELLED) {
83712bd3c8bSSascha Wildner 			break;
83812bd3c8bSSascha Wildner 		}
83912bd3c8bSSascha Wildner 		/* If the pipe is already stalled, don't do another stall */
84012bd3c8bSSascha Wildner 		if (!usbd_xfer_is_stalled(xfer))
84112bd3c8bSSascha Wildner 			sc->sc_transfer.data_error = 1;
84212bd3c8bSSascha Wildner 
84312bd3c8bSSascha Wildner 		/* try again */
84412bd3c8bSSascha Wildner 		goto tr_setup;
84512bd3c8bSSascha Wildner 	}
84612bd3c8bSSascha Wildner }
84712bd3c8bSSascha Wildner 
84812bd3c8bSSascha Wildner /* SCSI commands that we recognize */
84912bd3c8bSSascha Wildner #define	SC_FORMAT_UNIT			0x04
85012bd3c8bSSascha Wildner #define	SC_INQUIRY			0x12
85112bd3c8bSSascha Wildner #define	SC_MODE_SELECT_6		0x15
85212bd3c8bSSascha Wildner #define	SC_MODE_SELECT_10		0x55
85312bd3c8bSSascha Wildner #define	SC_MODE_SENSE_6			0x1a
85412bd3c8bSSascha Wildner #define	SC_MODE_SENSE_10		0x5a
85512bd3c8bSSascha Wildner #define	SC_PREVENT_ALLOW_MEDIUM_REMOVAL	0x1e
85612bd3c8bSSascha Wildner #define	SC_READ_6			0x08
85712bd3c8bSSascha Wildner #define	SC_READ_10			0x28
85812bd3c8bSSascha Wildner #define	SC_READ_12			0xa8
85912bd3c8bSSascha Wildner #define	SC_READ_CAPACITY		0x25
86012bd3c8bSSascha Wildner #define	SC_READ_FORMAT_CAPACITIES	0x23
86112bd3c8bSSascha Wildner #define	SC_RELEASE			0x17
86212bd3c8bSSascha Wildner #define	SC_REQUEST_SENSE		0x03
86312bd3c8bSSascha Wildner #define	SC_RESERVE			0x16
86412bd3c8bSSascha Wildner #define	SC_SEND_DIAGNOSTIC		0x1d
86512bd3c8bSSascha Wildner #define	SC_START_STOP_UNIT		0x1b
86612bd3c8bSSascha Wildner #define	SC_SYNCHRONIZE_CACHE		0x35
86712bd3c8bSSascha Wildner #define	SC_TEST_UNIT_READY		0x00
86812bd3c8bSSascha Wildner #define	SC_VERIFY			0x2f
86912bd3c8bSSascha Wildner #define	SC_WRITE_6			0x0a
87012bd3c8bSSascha Wildner #define	SC_WRITE_10			0x2a
87112bd3c8bSSascha Wildner #define	SC_WRITE_12			0xaa
87212bd3c8bSSascha Wildner 
87312bd3c8bSSascha Wildner /* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */
87412bd3c8bSSascha Wildner #define	SS_NO_SENSE				0
87512bd3c8bSSascha Wildner #define	SS_COMMUNICATION_FAILURE		0x040800
87612bd3c8bSSascha Wildner #define	SS_INVALID_COMMAND			0x052000
87712bd3c8bSSascha Wildner #define	SS_INVALID_FIELD_IN_CDB			0x052400
87812bd3c8bSSascha Wildner #define	SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE	0x052100
87912bd3c8bSSascha Wildner #define	SS_LOGICAL_UNIT_NOT_SUPPORTED		0x052500
88012bd3c8bSSascha Wildner #define	SS_MEDIUM_NOT_PRESENT			0x023a00
88112bd3c8bSSascha Wildner #define	SS_MEDIUM_REMOVAL_PREVENTED		0x055302
88212bd3c8bSSascha Wildner #define	SS_NOT_READY_TO_READY_TRANSITION	0x062800
88312bd3c8bSSascha Wildner #define	SS_RESET_OCCURRED			0x062900
88412bd3c8bSSascha Wildner #define	SS_SAVING_PARAMETERS_NOT_SUPPORTED	0x053900
88512bd3c8bSSascha Wildner #define	SS_UNRECOVERED_READ_ERROR		0x031100
88612bd3c8bSSascha Wildner #define	SS_WRITE_ERROR				0x030c02
88712bd3c8bSSascha Wildner #define	SS_WRITE_PROTECTED			0x072700
88812bd3c8bSSascha Wildner 
88912bd3c8bSSascha Wildner #define	SK(x)		((uint8_t) ((x) >> 16))	/* Sense Key byte, etc. */
89012bd3c8bSSascha Wildner #define	ASC(x)		((uint8_t) ((x) >> 8))
89112bd3c8bSSascha Wildner #define	ASCQ(x)		((uint8_t) (x))
89212bd3c8bSSascha Wildner 
89312bd3c8bSSascha Wildner /* Routines for unaligned data access */
89412bd3c8bSSascha Wildner 
89512bd3c8bSSascha Wildner static uint16_t
get_be16(uint8_t * buf)89612bd3c8bSSascha Wildner get_be16(uint8_t *buf)
89712bd3c8bSSascha Wildner {
89812bd3c8bSSascha Wildner 	return ((uint16_t)buf[0] << 8) | ((uint16_t)buf[1]);
89912bd3c8bSSascha Wildner }
90012bd3c8bSSascha Wildner 
90112bd3c8bSSascha Wildner static uint32_t
get_be32(uint8_t * buf)90212bd3c8bSSascha Wildner get_be32(uint8_t *buf)
90312bd3c8bSSascha Wildner {
90412bd3c8bSSascha Wildner 	return ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) |
90512bd3c8bSSascha Wildner 	((uint32_t)buf[2] << 8) | ((uint32_t)buf[3]);
90612bd3c8bSSascha Wildner }
90712bd3c8bSSascha Wildner 
90812bd3c8bSSascha Wildner static void
put_be16(uint8_t * buf,uint16_t val)90912bd3c8bSSascha Wildner put_be16(uint8_t *buf, uint16_t val)
91012bd3c8bSSascha Wildner {
91112bd3c8bSSascha Wildner 	buf[0] = val >> 8;
91212bd3c8bSSascha Wildner 	buf[1] = val;
91312bd3c8bSSascha Wildner }
91412bd3c8bSSascha Wildner 
91512bd3c8bSSascha Wildner static void
put_be32(uint8_t * buf,uint32_t val)91612bd3c8bSSascha Wildner put_be32(uint8_t *buf, uint32_t val)
91712bd3c8bSSascha Wildner {
91812bd3c8bSSascha Wildner 	buf[0] = val >> 24;
91912bd3c8bSSascha Wildner 	buf[1] = val >> 16;
92012bd3c8bSSascha Wildner 	buf[2] = val >> 8;
92112bd3c8bSSascha Wildner 	buf[3] = val & 0xff;
92212bd3c8bSSascha Wildner }
92312bd3c8bSSascha Wildner 
92412bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
92512bd3c8bSSascha Wildner  *	ustorage_fs_verify
92612bd3c8bSSascha Wildner  *
92712bd3c8bSSascha Wildner  * Returns:
92812bd3c8bSSascha Wildner  *    0: Success
92912bd3c8bSSascha Wildner  * Else: Failure
93012bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
93112bd3c8bSSascha Wildner static uint8_t
ustorage_fs_verify(struct ustorage_fs_softc * sc)93212bd3c8bSSascha Wildner ustorage_fs_verify(struct ustorage_fs_softc *sc)
93312bd3c8bSSascha Wildner {
93412bd3c8bSSascha Wildner 	struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
93512bd3c8bSSascha Wildner 	uint32_t lba;
93612bd3c8bSSascha Wildner 	uint32_t vlen;
93712bd3c8bSSascha Wildner 	uint64_t file_offset;
93812bd3c8bSSascha Wildner 	uint64_t amount_left;
93912bd3c8bSSascha Wildner 
94012bd3c8bSSascha Wildner 	/*
94112bd3c8bSSascha Wildner 	 * Get the starting Logical Block Address
94212bd3c8bSSascha Wildner 	 */
943a41b1dd4SMarkus Pfeiffer 	lba = get_be32(&sc->sc_cbw->CBWCDB[2]);
94412bd3c8bSSascha Wildner 
94512bd3c8bSSascha Wildner 	/*
94612bd3c8bSSascha Wildner 	 * We allow DPO (Disable Page Out = don't save data in the cache)
94712bd3c8bSSascha Wildner 	 * but we don't implement it.
94812bd3c8bSSascha Wildner 	 */
949a41b1dd4SMarkus Pfeiffer 	if ((sc->sc_cbw->CBWCDB[1] & ~0x10) != 0) {
95012bd3c8bSSascha Wildner 		currlun->sense_data = SS_INVALID_FIELD_IN_CDB;
95112bd3c8bSSascha Wildner 		return (1);
95212bd3c8bSSascha Wildner 	}
953a41b1dd4SMarkus Pfeiffer 	vlen = get_be16(&sc->sc_cbw->CBWCDB[7]);
95412bd3c8bSSascha Wildner 	if (vlen == 0) {
95512bd3c8bSSascha Wildner 		goto done;
95612bd3c8bSSascha Wildner 	}
95712bd3c8bSSascha Wildner 	/* No default reply */
95812bd3c8bSSascha Wildner 
95912bd3c8bSSascha Wildner 	/* Prepare to carry out the file verify */
96012bd3c8bSSascha Wildner 	amount_left = vlen;
96112bd3c8bSSascha Wildner 	amount_left <<= 9;
96212bd3c8bSSascha Wildner 	file_offset = lba;
96312bd3c8bSSascha Wildner 	file_offset <<= 9;
96412bd3c8bSSascha Wildner 
96512bd3c8bSSascha Wildner 	/* Range check */
96612bd3c8bSSascha Wildner 	vlen += lba;
96712bd3c8bSSascha Wildner 
96812bd3c8bSSascha Wildner 	if ((vlen < lba) ||
96912bd3c8bSSascha Wildner 	    (vlen > currlun->num_sectors) ||
97012bd3c8bSSascha Wildner 	    (lba >= currlun->num_sectors)) {
97112bd3c8bSSascha Wildner 		currlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
97212bd3c8bSSascha Wildner 		return (1);
97312bd3c8bSSascha Wildner 	}
97412bd3c8bSSascha Wildner 	/* XXX TODO: verify that data is readable */
97512bd3c8bSSascha Wildner done:
976a41b1dd4SMarkus Pfeiffer 	return (ustorage_fs_min_len(sc, 0, -1U));
97712bd3c8bSSascha Wildner }
97812bd3c8bSSascha Wildner 
97912bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
98012bd3c8bSSascha Wildner  *	ustorage_fs_inquiry
98112bd3c8bSSascha Wildner  *
98212bd3c8bSSascha Wildner  * Returns:
98312bd3c8bSSascha Wildner  *    0: Success
98412bd3c8bSSascha Wildner  * Else: Failure
98512bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
98612bd3c8bSSascha Wildner static uint8_t
ustorage_fs_inquiry(struct ustorage_fs_softc * sc)98712bd3c8bSSascha Wildner ustorage_fs_inquiry(struct ustorage_fs_softc *sc)
98812bd3c8bSSascha Wildner {
98912bd3c8bSSascha Wildner 	uint8_t *buf = sc->sc_transfer.data_ptr;
99012bd3c8bSSascha Wildner 
99112bd3c8bSSascha Wildner 	struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
99212bd3c8bSSascha Wildner 
99312bd3c8bSSascha Wildner 	if (!sc->sc_transfer.currlun) {
99412bd3c8bSSascha Wildner 		/* Unsupported LUNs are okay */
99512bd3c8bSSascha Wildner 		memset(buf, 0, 36);
99612bd3c8bSSascha Wildner 		buf[0] = 0x7f;
99712bd3c8bSSascha Wildner 		/* Unsupported, no device - type */
998a41b1dd4SMarkus Pfeiffer 		return (ustorage_fs_min_len(sc, 36, -1U));
99912bd3c8bSSascha Wildner 	}
100012bd3c8bSSascha Wildner 	memset(buf, 0, 8);
100112bd3c8bSSascha Wildner 	/* Non - removable, direct - access device */
100212bd3c8bSSascha Wildner 	if (currlun->removable)
100312bd3c8bSSascha Wildner 		buf[1] = 0x80;
100412bd3c8bSSascha Wildner 	buf[2] = 2;
100512bd3c8bSSascha Wildner 	/* ANSI SCSI level 2 */
100612bd3c8bSSascha Wildner 	buf[3] = 2;
100712bd3c8bSSascha Wildner 	/* SCSI - 2 INQUIRY data format */
100812bd3c8bSSascha Wildner 	buf[4] = 31;
100912bd3c8bSSascha Wildner 	/* Additional length */
101012bd3c8bSSascha Wildner 	/* No special options */
101112bd3c8bSSascha Wildner 	/* Copy in ID string */
101212bd3c8bSSascha Wildner 	memcpy(buf + 8, USTORAGE_FS_ID_STRING, 28);
101312bd3c8bSSascha Wildner 
101412bd3c8bSSascha Wildner #if (USTORAGE_QDATA_MAX < 36)
101512bd3c8bSSascha Wildner #error "(USTORAGE_QDATA_MAX < 36)"
101612bd3c8bSSascha Wildner #endif
1017a41b1dd4SMarkus Pfeiffer 	return (ustorage_fs_min_len(sc, 36, -1U));
101812bd3c8bSSascha Wildner }
101912bd3c8bSSascha Wildner 
102012bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
102112bd3c8bSSascha Wildner  *	ustorage_fs_request_sense
102212bd3c8bSSascha Wildner  *
102312bd3c8bSSascha Wildner  * Returns:
102412bd3c8bSSascha Wildner  *    0: Success
102512bd3c8bSSascha Wildner  * Else: Failure
102612bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
102712bd3c8bSSascha Wildner static uint8_t
ustorage_fs_request_sense(struct ustorage_fs_softc * sc)102812bd3c8bSSascha Wildner ustorage_fs_request_sense(struct ustorage_fs_softc *sc)
102912bd3c8bSSascha Wildner {
103012bd3c8bSSascha Wildner 	uint8_t *buf = sc->sc_transfer.data_ptr;
103112bd3c8bSSascha Wildner 	struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
103212bd3c8bSSascha Wildner 	uint32_t sd;
103312bd3c8bSSascha Wildner 	uint32_t sdinfo;
103412bd3c8bSSascha Wildner 	uint8_t valid;
103512bd3c8bSSascha Wildner 
103612bd3c8bSSascha Wildner 	/*
103712bd3c8bSSascha Wildner 	 * From the SCSI-2 spec., section 7.9 (Unit attention condition):
103812bd3c8bSSascha Wildner 	 *
103912bd3c8bSSascha Wildner 	 * If a REQUEST SENSE command is received from an initiator
104012bd3c8bSSascha Wildner 	 * with a pending unit attention condition (before the target
104112bd3c8bSSascha Wildner 	 * generates the contingent allegiance condition), then the
104212bd3c8bSSascha Wildner 	 * target shall either:
104312bd3c8bSSascha Wildner 	 *   a) report any pending sense data and preserve the unit
104412bd3c8bSSascha Wildner 	 *	attention condition on the logical unit, or,
104512bd3c8bSSascha Wildner 	 *   b) report the unit attention condition, may discard any
104612bd3c8bSSascha Wildner 	 *	pending sense data, and clear the unit attention
104712bd3c8bSSascha Wildner 	 *	condition on the logical unit for that initiator.
104812bd3c8bSSascha Wildner 	 *
104912bd3c8bSSascha Wildner 	 * FSG normally uses option a); enable this code to use option b).
105012bd3c8bSSascha Wildner 	 */
105112bd3c8bSSascha Wildner #if 0
105212bd3c8bSSascha Wildner 	if (currlun && currlun->unit_attention_data != SS_NO_SENSE) {
105312bd3c8bSSascha Wildner 		currlun->sense_data = currlun->unit_attention_data;
105412bd3c8bSSascha Wildner 		currlun->unit_attention_data = SS_NO_SENSE;
105512bd3c8bSSascha Wildner 	}
105612bd3c8bSSascha Wildner #endif
105712bd3c8bSSascha Wildner 
105812bd3c8bSSascha Wildner 	if (!currlun) {
105912bd3c8bSSascha Wildner 		/* Unsupported LUNs are okay */
106012bd3c8bSSascha Wildner 		sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
106112bd3c8bSSascha Wildner 		sdinfo = 0;
106212bd3c8bSSascha Wildner 		valid = 0;
106312bd3c8bSSascha Wildner 	} else {
106412bd3c8bSSascha Wildner 		sd = currlun->sense_data;
106512bd3c8bSSascha Wildner 		sdinfo = currlun->sense_data_info;
106612bd3c8bSSascha Wildner 		valid = currlun->info_valid << 7;
106712bd3c8bSSascha Wildner 		currlun->sense_data = SS_NO_SENSE;
106812bd3c8bSSascha Wildner 		currlun->sense_data_info = 0;
106912bd3c8bSSascha Wildner 		currlun->info_valid = 0;
107012bd3c8bSSascha Wildner 	}
107112bd3c8bSSascha Wildner 
107212bd3c8bSSascha Wildner 	memset(buf, 0, 18);
107312bd3c8bSSascha Wildner 	buf[0] = valid | 0x70;
107412bd3c8bSSascha Wildner 	/* Valid, current error */
107512bd3c8bSSascha Wildner 	buf[2] = SK(sd);
107612bd3c8bSSascha Wildner 	put_be32(&buf[3], sdinfo);
107712bd3c8bSSascha Wildner 	/* Sense information */
107812bd3c8bSSascha Wildner 	buf[7] = 18 - 8;
107912bd3c8bSSascha Wildner 	/* Additional sense length */
108012bd3c8bSSascha Wildner 	buf[12] = ASC(sd);
108112bd3c8bSSascha Wildner 	buf[13] = ASCQ(sd);
108212bd3c8bSSascha Wildner 
108312bd3c8bSSascha Wildner #if (USTORAGE_QDATA_MAX < 18)
108412bd3c8bSSascha Wildner #error "(USTORAGE_QDATA_MAX < 18)"
108512bd3c8bSSascha Wildner #endif
1086a41b1dd4SMarkus Pfeiffer 	return (ustorage_fs_min_len(sc, 18, -1U));
108712bd3c8bSSascha Wildner }
108812bd3c8bSSascha Wildner 
108912bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
109012bd3c8bSSascha Wildner  *	ustorage_fs_read_capacity
109112bd3c8bSSascha Wildner  *
109212bd3c8bSSascha Wildner  * Returns:
109312bd3c8bSSascha Wildner  *    0: Success
109412bd3c8bSSascha Wildner  * Else: Failure
109512bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
109612bd3c8bSSascha Wildner static uint8_t
ustorage_fs_read_capacity(struct ustorage_fs_softc * sc)109712bd3c8bSSascha Wildner ustorage_fs_read_capacity(struct ustorage_fs_softc *sc)
109812bd3c8bSSascha Wildner {
109912bd3c8bSSascha Wildner 	uint8_t *buf = sc->sc_transfer.data_ptr;
110012bd3c8bSSascha Wildner 	struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
1101a41b1dd4SMarkus Pfeiffer 	uint32_t lba = get_be32(&sc->sc_cbw->CBWCDB[2]);
1102a41b1dd4SMarkus Pfeiffer 	uint8_t pmi = sc->sc_cbw->CBWCDB[8];
110312bd3c8bSSascha Wildner 
110412bd3c8bSSascha Wildner 	/* Check the PMI and LBA fields */
110512bd3c8bSSascha Wildner 	if ((pmi > 1) || ((pmi == 0) && (lba != 0))) {
110612bd3c8bSSascha Wildner 		currlun->sense_data = SS_INVALID_FIELD_IN_CDB;
110712bd3c8bSSascha Wildner 		return (1);
110812bd3c8bSSascha Wildner 	}
110912bd3c8bSSascha Wildner 	/* Max logical block */
111012bd3c8bSSascha Wildner 	put_be32(&buf[0], currlun->num_sectors - 1);
111112bd3c8bSSascha Wildner 	/* Block length */
111212bd3c8bSSascha Wildner 	put_be32(&buf[4], 512);
111312bd3c8bSSascha Wildner 
111412bd3c8bSSascha Wildner #if (USTORAGE_QDATA_MAX < 8)
111512bd3c8bSSascha Wildner #error "(USTORAGE_QDATA_MAX < 8)"
111612bd3c8bSSascha Wildner #endif
1117a41b1dd4SMarkus Pfeiffer 	return (ustorage_fs_min_len(sc, 8, -1U));
111812bd3c8bSSascha Wildner }
111912bd3c8bSSascha Wildner 
112012bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
112112bd3c8bSSascha Wildner  *	ustorage_fs_mode_sense
112212bd3c8bSSascha Wildner  *
112312bd3c8bSSascha Wildner  * Returns:
112412bd3c8bSSascha Wildner  *    0: Success
112512bd3c8bSSascha Wildner  * Else: Failure
112612bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
112712bd3c8bSSascha Wildner static uint8_t
ustorage_fs_mode_sense(struct ustorage_fs_softc * sc)112812bd3c8bSSascha Wildner ustorage_fs_mode_sense(struct ustorage_fs_softc *sc)
112912bd3c8bSSascha Wildner {
113012bd3c8bSSascha Wildner 	uint8_t *buf = sc->sc_transfer.data_ptr;
113112bd3c8bSSascha Wildner 	struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
113212bd3c8bSSascha Wildner 	uint8_t *buf0;
113312bd3c8bSSascha Wildner 	uint16_t len;
113412bd3c8bSSascha Wildner 	uint16_t limit;
1135a41b1dd4SMarkus Pfeiffer 	uint8_t mscmnd = sc->sc_cbw->CBWCDB[0];
113612bd3c8bSSascha Wildner 	uint8_t pc;
113712bd3c8bSSascha Wildner 	uint8_t page_code;
113812bd3c8bSSascha Wildner 	uint8_t changeable_values;
113912bd3c8bSSascha Wildner 	uint8_t all_pages;
114012bd3c8bSSascha Wildner 
114112bd3c8bSSascha Wildner 	buf0 = buf;
114212bd3c8bSSascha Wildner 
1143a41b1dd4SMarkus Pfeiffer 	if ((sc->sc_cbw->CBWCDB[1] & ~0x08) != 0) {
114412bd3c8bSSascha Wildner 		/* Mask away DBD */
114512bd3c8bSSascha Wildner 		currlun->sense_data = SS_INVALID_FIELD_IN_CDB;
114612bd3c8bSSascha Wildner 		return (1);
114712bd3c8bSSascha Wildner 	}
1148a41b1dd4SMarkus Pfeiffer 	pc = sc->sc_cbw->CBWCDB[2] >> 6;
1149a41b1dd4SMarkus Pfeiffer 	page_code = sc->sc_cbw->CBWCDB[2] & 0x3f;
115012bd3c8bSSascha Wildner 	if (pc == 3) {
115112bd3c8bSSascha Wildner 		currlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED;
115212bd3c8bSSascha Wildner 		return (1);
115312bd3c8bSSascha Wildner 	}
115412bd3c8bSSascha Wildner 	changeable_values = (pc == 1);
115512bd3c8bSSascha Wildner 	all_pages = (page_code == 0x3f);
115612bd3c8bSSascha Wildner 
115712bd3c8bSSascha Wildner 	/*
115812bd3c8bSSascha Wildner 	 * Write the mode parameter header.  Fixed values are: default
115912bd3c8bSSascha Wildner 	 * medium type, no cache control (DPOFUA), and no block descriptors.
116012bd3c8bSSascha Wildner 	 * The only variable value is the WriteProtect bit.  We will fill in
116112bd3c8bSSascha Wildner 	 * the mode data length later.
116212bd3c8bSSascha Wildner 	 */
116312bd3c8bSSascha Wildner 	memset(buf, 0, 8);
116412bd3c8bSSascha Wildner 	if (mscmnd == SC_MODE_SENSE_6) {
116512bd3c8bSSascha Wildner 		buf[2] = (currlun->read_only ? 0x80 : 0x00);
116612bd3c8bSSascha Wildner 		/* WP, DPOFUA */
116712bd3c8bSSascha Wildner 		buf += 4;
116812bd3c8bSSascha Wildner 		limit = 255;
116912bd3c8bSSascha Wildner 	} else {
117012bd3c8bSSascha Wildner 		/* SC_MODE_SENSE_10 */
117112bd3c8bSSascha Wildner 		buf[3] = (currlun->read_only ? 0x80 : 0x00);
117212bd3c8bSSascha Wildner 		/* WP, DPOFUA */
117312bd3c8bSSascha Wildner 		buf += 8;
117412bd3c8bSSascha Wildner 		limit = 65535;
117512bd3c8bSSascha Wildner 		/* Should really be mod_data.buflen */
117612bd3c8bSSascha Wildner 	}
117712bd3c8bSSascha Wildner 
117812bd3c8bSSascha Wildner 	/* No block descriptors */
117912bd3c8bSSascha Wildner 
118012bd3c8bSSascha Wildner 	/*
118112bd3c8bSSascha Wildner 	 * The mode pages, in numerical order.
118212bd3c8bSSascha Wildner 	 */
118312bd3c8bSSascha Wildner 	if ((page_code == 0x08) || all_pages) {
118412bd3c8bSSascha Wildner 		buf[0] = 0x08;
118512bd3c8bSSascha Wildner 		/* Page code */
118612bd3c8bSSascha Wildner 		buf[1] = 10;
118712bd3c8bSSascha Wildner 		/* Page length */
118812bd3c8bSSascha Wildner 		memset(buf + 2, 0, 10);
118912bd3c8bSSascha Wildner 		/* None of the fields are changeable */
119012bd3c8bSSascha Wildner 
119112bd3c8bSSascha Wildner 		if (!changeable_values) {
119212bd3c8bSSascha Wildner 			buf[2] = 0x04;
119312bd3c8bSSascha Wildner 			/* Write cache enable, */
119412bd3c8bSSascha Wildner 			/* Read cache not disabled */
119512bd3c8bSSascha Wildner 			/* No cache retention priorities */
119612bd3c8bSSascha Wildner 			put_be16(&buf[4], 0xffff);
119712bd3c8bSSascha Wildner 			/* Don 't disable prefetch */
119812bd3c8bSSascha Wildner 			/* Minimum prefetch = 0 */
119912bd3c8bSSascha Wildner 			put_be16(&buf[8], 0xffff);
120012bd3c8bSSascha Wildner 			/* Maximum prefetch */
120112bd3c8bSSascha Wildner 			put_be16(&buf[10], 0xffff);
120212bd3c8bSSascha Wildner 			/* Maximum prefetch ceiling */
120312bd3c8bSSascha Wildner 		}
120412bd3c8bSSascha Wildner 		buf += 12;
120512bd3c8bSSascha Wildner 	}
120612bd3c8bSSascha Wildner 	/*
120712bd3c8bSSascha Wildner 	 * Check that a valid page was requested and the mode data length
120812bd3c8bSSascha Wildner 	 * isn't too long.
120912bd3c8bSSascha Wildner 	 */
121012bd3c8bSSascha Wildner 	len = buf - buf0;
121112bd3c8bSSascha Wildner 	if (len > limit) {
121212bd3c8bSSascha Wildner 		currlun->sense_data = SS_INVALID_FIELD_IN_CDB;
121312bd3c8bSSascha Wildner 		return (1);
121412bd3c8bSSascha Wildner 	}
121512bd3c8bSSascha Wildner 	/* Store the mode data length */
121612bd3c8bSSascha Wildner 	if (mscmnd == SC_MODE_SENSE_6)
121712bd3c8bSSascha Wildner 		buf0[0] = len - 1;
121812bd3c8bSSascha Wildner 	else
121912bd3c8bSSascha Wildner 		put_be16(buf0, len - 2);
122012bd3c8bSSascha Wildner 
122112bd3c8bSSascha Wildner #if (USTORAGE_QDATA_MAX < 24)
122212bd3c8bSSascha Wildner #error "(USTORAGE_QDATA_MAX < 24)"
122312bd3c8bSSascha Wildner #endif
1224a41b1dd4SMarkus Pfeiffer 	return (ustorage_fs_min_len(sc, len, -1U));
122512bd3c8bSSascha Wildner }
122612bd3c8bSSascha Wildner 
122712bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
122812bd3c8bSSascha Wildner  *	ustorage_fs_start_stop
122912bd3c8bSSascha Wildner  *
123012bd3c8bSSascha Wildner  * Returns:
123112bd3c8bSSascha Wildner  *    0: Success
123212bd3c8bSSascha Wildner  * Else: Failure
123312bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
123412bd3c8bSSascha Wildner static uint8_t
ustorage_fs_start_stop(struct ustorage_fs_softc * sc)123512bd3c8bSSascha Wildner ustorage_fs_start_stop(struct ustorage_fs_softc *sc)
123612bd3c8bSSascha Wildner {
123712bd3c8bSSascha Wildner 	struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
123812bd3c8bSSascha Wildner 	uint8_t loej;
123912bd3c8bSSascha Wildner 	uint8_t start;
124012bd3c8bSSascha Wildner 	uint8_t immed;
124112bd3c8bSSascha Wildner 
124212bd3c8bSSascha Wildner 	if (!currlun->removable) {
124312bd3c8bSSascha Wildner 		currlun->sense_data = SS_INVALID_COMMAND;
124412bd3c8bSSascha Wildner 		return (1);
124512bd3c8bSSascha Wildner 	}
1246a41b1dd4SMarkus Pfeiffer 	immed = sc->sc_cbw->CBWCDB[1] & 0x01;
1247a41b1dd4SMarkus Pfeiffer 	loej = sc->sc_cbw->CBWCDB[4] & 0x02;
1248a41b1dd4SMarkus Pfeiffer 	start = sc->sc_cbw->CBWCDB[4] & 0x01;
124912bd3c8bSSascha Wildner 
125012bd3c8bSSascha Wildner 	if (immed || loej || start) {
125112bd3c8bSSascha Wildner 		/* compile fix */
125212bd3c8bSSascha Wildner 	}
125312bd3c8bSSascha Wildner 	return (0);
125412bd3c8bSSascha Wildner }
125512bd3c8bSSascha Wildner 
125612bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
125712bd3c8bSSascha Wildner  *	ustorage_fs_prevent_allow
125812bd3c8bSSascha Wildner  *
125912bd3c8bSSascha Wildner  * Returns:
126012bd3c8bSSascha Wildner  *    0: Success
126112bd3c8bSSascha Wildner  * Else: Failure
126212bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
126312bd3c8bSSascha Wildner static uint8_t
ustorage_fs_prevent_allow(struct ustorage_fs_softc * sc)126412bd3c8bSSascha Wildner ustorage_fs_prevent_allow(struct ustorage_fs_softc *sc)
126512bd3c8bSSascha Wildner {
126612bd3c8bSSascha Wildner 	struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
126712bd3c8bSSascha Wildner 	uint8_t prevent;
126812bd3c8bSSascha Wildner 
126912bd3c8bSSascha Wildner 	if (!currlun->removable) {
127012bd3c8bSSascha Wildner 		currlun->sense_data = SS_INVALID_COMMAND;
127112bd3c8bSSascha Wildner 		return (1);
127212bd3c8bSSascha Wildner 	}
1273a41b1dd4SMarkus Pfeiffer 	prevent = sc->sc_cbw->CBWCDB[4] & 0x01;
1274a41b1dd4SMarkus Pfeiffer 	if ((sc->sc_cbw->CBWCDB[4] & ~0x01) != 0) {
127512bd3c8bSSascha Wildner 		/* Mask away Prevent */
127612bd3c8bSSascha Wildner 		currlun->sense_data = SS_INVALID_FIELD_IN_CDB;
127712bd3c8bSSascha Wildner 		return (1);
127812bd3c8bSSascha Wildner 	}
127912bd3c8bSSascha Wildner 	if (currlun->prevent_medium_removal && !prevent) {
128012bd3c8bSSascha Wildner 		//fsync_sub(currlun);
128112bd3c8bSSascha Wildner 	}
128212bd3c8bSSascha Wildner 	currlun->prevent_medium_removal = prevent;
128312bd3c8bSSascha Wildner 	return (0);
128412bd3c8bSSascha Wildner }
128512bd3c8bSSascha Wildner 
128612bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
128712bd3c8bSSascha Wildner  *	ustorage_fs_read_format_capacities
128812bd3c8bSSascha Wildner  *
128912bd3c8bSSascha Wildner  * Returns:
129012bd3c8bSSascha Wildner  *    0: Success
129112bd3c8bSSascha Wildner  * Else: Failure
129212bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
129312bd3c8bSSascha Wildner static uint8_t
ustorage_fs_read_format_capacities(struct ustorage_fs_softc * sc)129412bd3c8bSSascha Wildner ustorage_fs_read_format_capacities(struct ustorage_fs_softc *sc)
129512bd3c8bSSascha Wildner {
129612bd3c8bSSascha Wildner 	uint8_t *buf = sc->sc_transfer.data_ptr;
129712bd3c8bSSascha Wildner 	struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
129812bd3c8bSSascha Wildner 
129912bd3c8bSSascha Wildner 	buf[0] = buf[1] = buf[2] = 0;
130012bd3c8bSSascha Wildner 	buf[3] = 8;
130112bd3c8bSSascha Wildner 	/* Only the Current / Maximum Capacity Descriptor */
130212bd3c8bSSascha Wildner 	buf += 4;
130312bd3c8bSSascha Wildner 
130412bd3c8bSSascha Wildner 	/* Number of blocks */
130512bd3c8bSSascha Wildner 	put_be32(&buf[0], currlun->num_sectors);
130612bd3c8bSSascha Wildner 	/* Block length */
130712bd3c8bSSascha Wildner 	put_be32(&buf[4], 512);
130812bd3c8bSSascha Wildner 	/* Current capacity */
130912bd3c8bSSascha Wildner 	buf[4] = 0x02;
131012bd3c8bSSascha Wildner 
131112bd3c8bSSascha Wildner #if (USTORAGE_QDATA_MAX < 12)
131212bd3c8bSSascha Wildner #error "(USTORAGE_QDATA_MAX < 12)"
131312bd3c8bSSascha Wildner #endif
1314a41b1dd4SMarkus Pfeiffer 	return (ustorage_fs_min_len(sc, 12, -1U));
131512bd3c8bSSascha Wildner }
131612bd3c8bSSascha Wildner 
131712bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
131812bd3c8bSSascha Wildner  *	ustorage_fs_mode_select
131912bd3c8bSSascha Wildner  *
132012bd3c8bSSascha Wildner  * Return values:
132112bd3c8bSSascha Wildner  *    0: Success
132212bd3c8bSSascha Wildner  * Else: Failure
132312bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
132412bd3c8bSSascha Wildner static uint8_t
ustorage_fs_mode_select(struct ustorage_fs_softc * sc)132512bd3c8bSSascha Wildner ustorage_fs_mode_select(struct ustorage_fs_softc *sc)
132612bd3c8bSSascha Wildner {
132712bd3c8bSSascha Wildner 	struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
132812bd3c8bSSascha Wildner 
132912bd3c8bSSascha Wildner 	/* We don't support MODE SELECT */
133012bd3c8bSSascha Wildner 	currlun->sense_data = SS_INVALID_COMMAND;
133112bd3c8bSSascha Wildner 	return (1);
133212bd3c8bSSascha Wildner }
133312bd3c8bSSascha Wildner 
133412bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
133512bd3c8bSSascha Wildner  *	ustorage_fs_synchronize_cache
133612bd3c8bSSascha Wildner  *
133712bd3c8bSSascha Wildner  * Return values:
133812bd3c8bSSascha Wildner  *    0: Success
133912bd3c8bSSascha Wildner  * Else: Failure
134012bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
134112bd3c8bSSascha Wildner static uint8_t
ustorage_fs_synchronize_cache(struct ustorage_fs_softc * sc)134212bd3c8bSSascha Wildner ustorage_fs_synchronize_cache(struct ustorage_fs_softc *sc)
134312bd3c8bSSascha Wildner {
134412bd3c8bSSascha Wildner #if 0
134512bd3c8bSSascha Wildner 	struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
134612bd3c8bSSascha Wildner 	uint8_t rc;
134712bd3c8bSSascha Wildner 
134812bd3c8bSSascha Wildner 	/*
134912bd3c8bSSascha Wildner 	 * We ignore the requested LBA and write out all dirty data buffers.
135012bd3c8bSSascha Wildner 	 */
135112bd3c8bSSascha Wildner 	rc = 0;
135212bd3c8bSSascha Wildner 	if (rc) {
135312bd3c8bSSascha Wildner 		currlun->sense_data = SS_WRITE_ERROR;
135412bd3c8bSSascha Wildner 	}
135512bd3c8bSSascha Wildner #endif
135612bd3c8bSSascha Wildner 	return (0);
135712bd3c8bSSascha Wildner }
135812bd3c8bSSascha Wildner 
135912bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
136012bd3c8bSSascha Wildner  *	ustorage_fs_read - read data from disk
136112bd3c8bSSascha Wildner  *
136212bd3c8bSSascha Wildner  * Return values:
136312bd3c8bSSascha Wildner  *    0: Success
136412bd3c8bSSascha Wildner  * Else: Failure
136512bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
136612bd3c8bSSascha Wildner static uint8_t
ustorage_fs_read(struct ustorage_fs_softc * sc)136712bd3c8bSSascha Wildner ustorage_fs_read(struct ustorage_fs_softc *sc)
136812bd3c8bSSascha Wildner {
136912bd3c8bSSascha Wildner 	struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
137012bd3c8bSSascha Wildner 	uint64_t file_offset;
137112bd3c8bSSascha Wildner 	uint32_t lba;
137212bd3c8bSSascha Wildner 	uint32_t len;
137312bd3c8bSSascha Wildner 
137412bd3c8bSSascha Wildner 	/*
137512bd3c8bSSascha Wildner 	 * Get the starting Logical Block Address and check that it's not
137612bd3c8bSSascha Wildner 	 * too big
137712bd3c8bSSascha Wildner 	 */
1378a41b1dd4SMarkus Pfeiffer 	if (sc->sc_cbw->CBWCDB[0] == SC_READ_6) {
1379a41b1dd4SMarkus Pfeiffer 		lba = (((uint32_t)sc->sc_cbw->CBWCDB[1]) << 16) |
1380a41b1dd4SMarkus Pfeiffer 		    get_be16(&sc->sc_cbw->CBWCDB[2]);
138112bd3c8bSSascha Wildner 	} else {
1382a41b1dd4SMarkus Pfeiffer 		lba = get_be32(&sc->sc_cbw->CBWCDB[2]);
138312bd3c8bSSascha Wildner 
138412bd3c8bSSascha Wildner 		/*
138512bd3c8bSSascha Wildner 		 * We allow DPO (Disable Page Out = don't save data in the
138612bd3c8bSSascha Wildner 		 * cache) and FUA (Force Unit Access = don't read from the
138712bd3c8bSSascha Wildner 		 * cache), but we don't implement them.
138812bd3c8bSSascha Wildner 		 */
1389a41b1dd4SMarkus Pfeiffer 		if ((sc->sc_cbw->CBWCDB[1] & ~0x18) != 0) {
139012bd3c8bSSascha Wildner 			currlun->sense_data = SS_INVALID_FIELD_IN_CDB;
139112bd3c8bSSascha Wildner 			return (1);
139212bd3c8bSSascha Wildner 		}
139312bd3c8bSSascha Wildner 	}
139412bd3c8bSSascha Wildner 	len = sc->sc_transfer.data_rem >> 9;
139512bd3c8bSSascha Wildner 	len += lba;
139612bd3c8bSSascha Wildner 
139712bd3c8bSSascha Wildner 	if ((len < lba) ||
139812bd3c8bSSascha Wildner 	    (len > currlun->num_sectors) ||
139912bd3c8bSSascha Wildner 	    (lba >= currlun->num_sectors)) {
140012bd3c8bSSascha Wildner 		currlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
140112bd3c8bSSascha Wildner 		return (1);
140212bd3c8bSSascha Wildner 	}
140312bd3c8bSSascha Wildner 	file_offset = lba;
140412bd3c8bSSascha Wildner 	file_offset <<= 9;
140512bd3c8bSSascha Wildner 
140612bd3c8bSSascha Wildner 	sc->sc_transfer.data_ptr = currlun->memory_image + file_offset;
140712bd3c8bSSascha Wildner 
140812bd3c8bSSascha Wildner 	return (0);
140912bd3c8bSSascha Wildner }
141012bd3c8bSSascha Wildner 
141112bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
141212bd3c8bSSascha Wildner  *	ustorage_fs_write - write data to disk
141312bd3c8bSSascha Wildner  *
141412bd3c8bSSascha Wildner  * Return values:
141512bd3c8bSSascha Wildner  *    0: Success
141612bd3c8bSSascha Wildner  * Else: Failure
141712bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
141812bd3c8bSSascha Wildner static uint8_t
ustorage_fs_write(struct ustorage_fs_softc * sc)141912bd3c8bSSascha Wildner ustorage_fs_write(struct ustorage_fs_softc *sc)
142012bd3c8bSSascha Wildner {
142112bd3c8bSSascha Wildner 	struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
142212bd3c8bSSascha Wildner 	uint64_t file_offset;
142312bd3c8bSSascha Wildner 	uint32_t lba;
142412bd3c8bSSascha Wildner 	uint32_t len;
142512bd3c8bSSascha Wildner 
142612bd3c8bSSascha Wildner 	if (currlun->read_only) {
142712bd3c8bSSascha Wildner 		currlun->sense_data = SS_WRITE_PROTECTED;
142812bd3c8bSSascha Wildner 		return (1);
142912bd3c8bSSascha Wildner 	}
143012bd3c8bSSascha Wildner 	/* XXX clear SYNC */
143112bd3c8bSSascha Wildner 
143212bd3c8bSSascha Wildner 	/*
143312bd3c8bSSascha Wildner 	 * Get the starting Logical Block Address and check that it's not
143412bd3c8bSSascha Wildner 	 * too big.
143512bd3c8bSSascha Wildner 	 */
1436a41b1dd4SMarkus Pfeiffer 	if (sc->sc_cbw->CBWCDB[0] == SC_WRITE_6)
1437a41b1dd4SMarkus Pfeiffer 		lba = (((uint32_t)sc->sc_cbw->CBWCDB[1]) << 16) |
1438a41b1dd4SMarkus Pfeiffer 		    get_be16(&sc->sc_cbw->CBWCDB[2]);
143912bd3c8bSSascha Wildner 	else {
1440a41b1dd4SMarkus Pfeiffer 		lba = get_be32(&sc->sc_cbw->CBWCDB[2]);
144112bd3c8bSSascha Wildner 
144212bd3c8bSSascha Wildner 		/*
144312bd3c8bSSascha Wildner 		 * We allow DPO (Disable Page Out = don't save data in the
144412bd3c8bSSascha Wildner 		 * cache) and FUA (Force Unit Access = write directly to the
144512bd3c8bSSascha Wildner 		 * medium).  We don't implement DPO; we implement FUA by
144612bd3c8bSSascha Wildner 		 * performing synchronous output.
144712bd3c8bSSascha Wildner 		 */
1448a41b1dd4SMarkus Pfeiffer 		if ((sc->sc_cbw->CBWCDB[1] & ~0x18) != 0) {
144912bd3c8bSSascha Wildner 			currlun->sense_data = SS_INVALID_FIELD_IN_CDB;
145012bd3c8bSSascha Wildner 			return (1);
145112bd3c8bSSascha Wildner 		}
1452a41b1dd4SMarkus Pfeiffer 		if (sc->sc_cbw->CBWCDB[1] & 0x08) {
145312bd3c8bSSascha Wildner 			/* FUA */
145412bd3c8bSSascha Wildner 			/* XXX set SYNC flag here */
145512bd3c8bSSascha Wildner 		}
145612bd3c8bSSascha Wildner 	}
145712bd3c8bSSascha Wildner 
145812bd3c8bSSascha Wildner 	len = sc->sc_transfer.data_rem >> 9;
145912bd3c8bSSascha Wildner 	len += lba;
146012bd3c8bSSascha Wildner 
146112bd3c8bSSascha Wildner 	if ((len < lba) ||
146212bd3c8bSSascha Wildner 	    (len > currlun->num_sectors) ||
146312bd3c8bSSascha Wildner 	    (lba >= currlun->num_sectors)) {
146412bd3c8bSSascha Wildner 		currlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
146512bd3c8bSSascha Wildner 		return (1);
146612bd3c8bSSascha Wildner 	}
146712bd3c8bSSascha Wildner 	file_offset = lba;
146812bd3c8bSSascha Wildner 	file_offset <<= 9;
146912bd3c8bSSascha Wildner 
147012bd3c8bSSascha Wildner 	sc->sc_transfer.data_ptr = currlun->memory_image + file_offset;
147112bd3c8bSSascha Wildner 
147212bd3c8bSSascha Wildner 	return (0);
147312bd3c8bSSascha Wildner }
147412bd3c8bSSascha Wildner 
147512bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
147612bd3c8bSSascha Wildner  *	ustorage_fs_min_len
147712bd3c8bSSascha Wildner  *
147812bd3c8bSSascha Wildner  * Return values:
147912bd3c8bSSascha Wildner  *    0: Success
148012bd3c8bSSascha Wildner  * Else: Failure
148112bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
148212bd3c8bSSascha Wildner static uint8_t
ustorage_fs_min_len(struct ustorage_fs_softc * sc,uint32_t len,uint32_t mask)148312bd3c8bSSascha Wildner ustorage_fs_min_len(struct ustorage_fs_softc *sc, uint32_t len, uint32_t mask)
148412bd3c8bSSascha Wildner {
148512bd3c8bSSascha Wildner 	if (len != sc->sc_transfer.data_rem) {
148612bd3c8bSSascha Wildner 
148712bd3c8bSSascha Wildner 		if (sc->sc_transfer.cbw_dir == DIR_READ) {
148812bd3c8bSSascha Wildner 			/*
148912bd3c8bSSascha Wildner 			 * there must be something wrong about this SCSI
149012bd3c8bSSascha Wildner 			 * command
149112bd3c8bSSascha Wildner 			 */
1492a41b1dd4SMarkus Pfeiffer 			sc->sc_csw->bCSWStatus = CSWSTATUS_PHASE;
149312bd3c8bSSascha Wildner 			return (1);
149412bd3c8bSSascha Wildner 		}
149512bd3c8bSSascha Wildner 		/* compute the minimum length */
149612bd3c8bSSascha Wildner 
149712bd3c8bSSascha Wildner 		if (sc->sc_transfer.data_rem > len) {
149812bd3c8bSSascha Wildner 			/* data ends prematurely */
149912bd3c8bSSascha Wildner 			sc->sc_transfer.data_rem = len;
150012bd3c8bSSascha Wildner 			sc->sc_transfer.data_short = 1;
150112bd3c8bSSascha Wildner 		}
150212bd3c8bSSascha Wildner 		/* check length alignment */
150312bd3c8bSSascha Wildner 
150412bd3c8bSSascha Wildner 		if (sc->sc_transfer.data_rem & ~mask) {
150512bd3c8bSSascha Wildner 			/* data ends prematurely */
150612bd3c8bSSascha Wildner 			sc->sc_transfer.data_rem &= mask;
150712bd3c8bSSascha Wildner 			sc->sc_transfer.data_short = 1;
150812bd3c8bSSascha Wildner 		}
150912bd3c8bSSascha Wildner 	}
151012bd3c8bSSascha Wildner 	return (0);
151112bd3c8bSSascha Wildner }
151212bd3c8bSSascha Wildner 
151312bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
151412bd3c8bSSascha Wildner  *	ustorage_fs_check_cmd - check command routine
151512bd3c8bSSascha Wildner  *
151612bd3c8bSSascha Wildner  * Check whether the command is properly formed and whether its data
151712bd3c8bSSascha Wildner  * size and direction agree with the values we already have.
151812bd3c8bSSascha Wildner  *
151912bd3c8bSSascha Wildner  * Return values:
152012bd3c8bSSascha Wildner  *    0: Success
152112bd3c8bSSascha Wildner  * Else: Failure
152212bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
152312bd3c8bSSascha Wildner static uint8_t
ustorage_fs_check_cmd(struct ustorage_fs_softc * sc,uint8_t min_cmd_size,uint16_t mask,uint8_t needs_medium)152412bd3c8bSSascha Wildner ustorage_fs_check_cmd(struct ustorage_fs_softc *sc, uint8_t min_cmd_size,
152512bd3c8bSSascha Wildner     uint16_t mask, uint8_t needs_medium)
152612bd3c8bSSascha Wildner {
152712bd3c8bSSascha Wildner 	struct ustorage_fs_lun *currlun;
1528a41b1dd4SMarkus Pfeiffer 	uint8_t lun = (sc->sc_cbw->CBWCDB[1] >> 5);
152912bd3c8bSSascha Wildner 	uint8_t i;
153012bd3c8bSSascha Wildner 
153112bd3c8bSSascha Wildner 	/* Verify the length of the command itself */
153212bd3c8bSSascha Wildner 	if (min_cmd_size > sc->sc_transfer.cmd_len) {
153312bd3c8bSSascha Wildner 		DPRINTF("%u > %u\n",
153412bd3c8bSSascha Wildner 		    min_cmd_size, sc->sc_transfer.cmd_len);
1535a41b1dd4SMarkus Pfeiffer 		sc->sc_csw->bCSWStatus = CSWSTATUS_PHASE;
153612bd3c8bSSascha Wildner 		return (1);
153712bd3c8bSSascha Wildner 	}
153812bd3c8bSSascha Wildner 	/* Mask away the LUN */
1539a41b1dd4SMarkus Pfeiffer 	sc->sc_cbw->CBWCDB[1] &= 0x1f;
154012bd3c8bSSascha Wildner 
154112bd3c8bSSascha Wildner 	/* Check if LUN is correct */
154212bd3c8bSSascha Wildner 	if (lun != sc->sc_transfer.lun) {
154312bd3c8bSSascha Wildner 
154412bd3c8bSSascha Wildner 	}
154512bd3c8bSSascha Wildner 	/* Check the LUN */
154612bd3c8bSSascha Wildner 	if (sc->sc_transfer.lun <= sc->sc_last_lun) {
154712bd3c8bSSascha Wildner 		sc->sc_transfer.currlun = currlun =
154812bd3c8bSSascha Wildner 		    sc->sc_lun + sc->sc_transfer.lun;
1549a41b1dd4SMarkus Pfeiffer 		if (sc->sc_cbw->CBWCDB[0] != SC_REQUEST_SENSE) {
155012bd3c8bSSascha Wildner 			currlun->sense_data = SS_NO_SENSE;
155112bd3c8bSSascha Wildner 			currlun->sense_data_info = 0;
155212bd3c8bSSascha Wildner 			currlun->info_valid = 0;
155312bd3c8bSSascha Wildner 		}
155412bd3c8bSSascha Wildner 		/*
155512bd3c8bSSascha Wildner 		 * If a unit attention condition exists, only INQUIRY
155612bd3c8bSSascha Wildner 		 * and REQUEST SENSE commands are allowed. Anything
155712bd3c8bSSascha Wildner 		 * else must fail!
155812bd3c8bSSascha Wildner 		 */
155912bd3c8bSSascha Wildner 		if ((currlun->unit_attention_data != SS_NO_SENSE) &&
1560a41b1dd4SMarkus Pfeiffer 		    (sc->sc_cbw->CBWCDB[0] != SC_INQUIRY) &&
1561a41b1dd4SMarkus Pfeiffer 		    (sc->sc_cbw->CBWCDB[0] != SC_REQUEST_SENSE)) {
156212bd3c8bSSascha Wildner 			currlun->sense_data = currlun->unit_attention_data;
156312bd3c8bSSascha Wildner 			currlun->unit_attention_data = SS_NO_SENSE;
156412bd3c8bSSascha Wildner 			return (1);
156512bd3c8bSSascha Wildner 		}
156612bd3c8bSSascha Wildner 	} else {
156712bd3c8bSSascha Wildner 		sc->sc_transfer.currlun = currlun = NULL;
156812bd3c8bSSascha Wildner 
156912bd3c8bSSascha Wildner 		/*
157012bd3c8bSSascha Wildner 		 * INQUIRY and REQUEST SENSE commands are explicitly allowed
157112bd3c8bSSascha Wildner 		 * to use unsupported LUNs; all others may not.
157212bd3c8bSSascha Wildner 		 */
1573a41b1dd4SMarkus Pfeiffer 		if ((sc->sc_cbw->CBWCDB[0] != SC_INQUIRY) &&
1574a41b1dd4SMarkus Pfeiffer 		    (sc->sc_cbw->CBWCDB[0] != SC_REQUEST_SENSE)) {
157512bd3c8bSSascha Wildner 			return (1);
157612bd3c8bSSascha Wildner 		}
157712bd3c8bSSascha Wildner 	}
157812bd3c8bSSascha Wildner 
157912bd3c8bSSascha Wildner 	/*
158012bd3c8bSSascha Wildner 	 * Check that only command bytes listed in the mask are
158112bd3c8bSSascha Wildner 	 * non-zero.
158212bd3c8bSSascha Wildner 	 */
158312bd3c8bSSascha Wildner 	for (i = 0; i != min_cmd_size; i++) {
1584a41b1dd4SMarkus Pfeiffer 		if (sc->sc_cbw->CBWCDB[i] && !(mask & (1UL << i))) {
158512bd3c8bSSascha Wildner 			if (currlun) {
158612bd3c8bSSascha Wildner 				currlun->sense_data = SS_INVALID_FIELD_IN_CDB;
158712bd3c8bSSascha Wildner 			}
158812bd3c8bSSascha Wildner 			return (1);
158912bd3c8bSSascha Wildner 		}
159012bd3c8bSSascha Wildner 	}
159112bd3c8bSSascha Wildner 
159212bd3c8bSSascha Wildner 	/*
159312bd3c8bSSascha Wildner 	 * If the medium isn't mounted and the command needs to access
159412bd3c8bSSascha Wildner 	 * it, return an error.
159512bd3c8bSSascha Wildner 	 */
159612bd3c8bSSascha Wildner 	if (currlun && (!currlun->memory_image) && needs_medium) {
159712bd3c8bSSascha Wildner 		currlun->sense_data = SS_MEDIUM_NOT_PRESENT;
159812bd3c8bSSascha Wildner 		return (1);
159912bd3c8bSSascha Wildner 	}
160012bd3c8bSSascha Wildner 	return (0);
160112bd3c8bSSascha Wildner }
160212bd3c8bSSascha Wildner 
160312bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
160412bd3c8bSSascha Wildner  *	ustorage_fs_do_cmd - do command
160512bd3c8bSSascha Wildner  *
160612bd3c8bSSascha Wildner  * Return values:
160712bd3c8bSSascha Wildner  *    0: Success
160812bd3c8bSSascha Wildner  * Else: Failure
160912bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
161012bd3c8bSSascha Wildner static uint8_t
ustorage_fs_do_cmd(struct ustorage_fs_softc * sc)161112bd3c8bSSascha Wildner ustorage_fs_do_cmd(struct ustorage_fs_softc *sc)
161212bd3c8bSSascha Wildner {
161312bd3c8bSSascha Wildner 	uint8_t error = 1;
161412bd3c8bSSascha Wildner 	uint8_t i;
161512bd3c8bSSascha Wildner 	uint32_t temp;
161612bd3c8bSSascha Wildner 	const uint32_t mask9 = (0xFFFFFFFFUL >> 9) << 9;
161712bd3c8bSSascha Wildner 
161812bd3c8bSSascha Wildner 	/* set default data transfer pointer */
161912bd3c8bSSascha Wildner 	sc->sc_transfer.data_ptr = sc->sc_qdata;
162012bd3c8bSSascha Wildner 
162112bd3c8bSSascha Wildner 	DPRINTF("cmd_data[0]=0x%02x, data_rem=0x%08x\n",
1622a41b1dd4SMarkus Pfeiffer 	    sc->sc_cbw->CBWCDB[0], sc->sc_transfer.data_rem);
162312bd3c8bSSascha Wildner 
1624a41b1dd4SMarkus Pfeiffer 	switch (sc->sc_cbw->CBWCDB[0]) {
162512bd3c8bSSascha Wildner 	case SC_INQUIRY:
162612bd3c8bSSascha Wildner 		sc->sc_transfer.cmd_dir = DIR_WRITE;
1627a41b1dd4SMarkus Pfeiffer 		error = ustorage_fs_min_len(sc, sc->sc_cbw->CBWCDB[4], -1U);
162812bd3c8bSSascha Wildner 		if (error) {
162912bd3c8bSSascha Wildner 			break;
163012bd3c8bSSascha Wildner 		}
163112bd3c8bSSascha Wildner 		error = ustorage_fs_check_cmd(sc, 6,
163212bd3c8bSSascha Wildner 		    (1UL << 4) | 1, 0);
163312bd3c8bSSascha Wildner 		if (error) {
163412bd3c8bSSascha Wildner 			break;
163512bd3c8bSSascha Wildner 		}
163612bd3c8bSSascha Wildner 		error = ustorage_fs_inquiry(sc);
163712bd3c8bSSascha Wildner 
163812bd3c8bSSascha Wildner 		break;
163912bd3c8bSSascha Wildner 
164012bd3c8bSSascha Wildner 	case SC_MODE_SELECT_6:
164112bd3c8bSSascha Wildner 		sc->sc_transfer.cmd_dir = DIR_READ;
1642a41b1dd4SMarkus Pfeiffer 		error = ustorage_fs_min_len(sc, sc->sc_cbw->CBWCDB[4], -1U);
164312bd3c8bSSascha Wildner 		if (error) {
164412bd3c8bSSascha Wildner 			break;
164512bd3c8bSSascha Wildner 		}
164612bd3c8bSSascha Wildner 		error = ustorage_fs_check_cmd(sc, 6,
164712bd3c8bSSascha Wildner 		    (1UL << 1) | (1UL << 4) | 1, 0);
164812bd3c8bSSascha Wildner 		if (error) {
164912bd3c8bSSascha Wildner 			break;
165012bd3c8bSSascha Wildner 		}
165112bd3c8bSSascha Wildner 		error = ustorage_fs_mode_select(sc);
165212bd3c8bSSascha Wildner 
165312bd3c8bSSascha Wildner 		break;
165412bd3c8bSSascha Wildner 
165512bd3c8bSSascha Wildner 	case SC_MODE_SELECT_10:
165612bd3c8bSSascha Wildner 		sc->sc_transfer.cmd_dir = DIR_READ;
165712bd3c8bSSascha Wildner 		error = ustorage_fs_min_len(sc,
1658a41b1dd4SMarkus Pfeiffer 		    get_be16(&sc->sc_cbw->CBWCDB[7]), -1U);
165912bd3c8bSSascha Wildner 		if (error) {
166012bd3c8bSSascha Wildner 			break;
166112bd3c8bSSascha Wildner 		}
166212bd3c8bSSascha Wildner 		error = ustorage_fs_check_cmd(sc, 10,
166312bd3c8bSSascha Wildner 		    (1UL << 1) | (3UL << 7) | 1, 0);
166412bd3c8bSSascha Wildner 		if (error) {
166512bd3c8bSSascha Wildner 			break;
166612bd3c8bSSascha Wildner 		}
166712bd3c8bSSascha Wildner 		error = ustorage_fs_mode_select(sc);
166812bd3c8bSSascha Wildner 
166912bd3c8bSSascha Wildner 		break;
167012bd3c8bSSascha Wildner 
167112bd3c8bSSascha Wildner 	case SC_MODE_SENSE_6:
167212bd3c8bSSascha Wildner 		sc->sc_transfer.cmd_dir = DIR_WRITE;
1673a41b1dd4SMarkus Pfeiffer 		error = ustorage_fs_min_len(sc, sc->sc_cbw->CBWCDB[4], -1U);
167412bd3c8bSSascha Wildner 		if (error) {
167512bd3c8bSSascha Wildner 			break;
167612bd3c8bSSascha Wildner 		}
167712bd3c8bSSascha Wildner 		error = ustorage_fs_check_cmd(sc, 6,
167812bd3c8bSSascha Wildner 		    (1UL << 1) | (1UL << 2) | (1UL << 4) | 1, 0);
167912bd3c8bSSascha Wildner 		if (error) {
168012bd3c8bSSascha Wildner 			break;
168112bd3c8bSSascha Wildner 		}
168212bd3c8bSSascha Wildner 		error = ustorage_fs_mode_sense(sc);
168312bd3c8bSSascha Wildner 
168412bd3c8bSSascha Wildner 		break;
168512bd3c8bSSascha Wildner 
168612bd3c8bSSascha Wildner 	case SC_MODE_SENSE_10:
168712bd3c8bSSascha Wildner 		sc->sc_transfer.cmd_dir = DIR_WRITE;
168812bd3c8bSSascha Wildner 		error = ustorage_fs_min_len(sc,
1689a41b1dd4SMarkus Pfeiffer 		    get_be16(&sc->sc_cbw->CBWCDB[7]), -1U);
169012bd3c8bSSascha Wildner 		if (error) {
169112bd3c8bSSascha Wildner 			break;
169212bd3c8bSSascha Wildner 		}
169312bd3c8bSSascha Wildner 		error = ustorage_fs_check_cmd(sc, 10,
169412bd3c8bSSascha Wildner 		    (1UL << 1) | (1UL << 2) | (3UL << 7) | 1, 0);
169512bd3c8bSSascha Wildner 		if (error) {
169612bd3c8bSSascha Wildner 			break;
169712bd3c8bSSascha Wildner 		}
169812bd3c8bSSascha Wildner 		error = ustorage_fs_mode_sense(sc);
169912bd3c8bSSascha Wildner 
170012bd3c8bSSascha Wildner 		break;
170112bd3c8bSSascha Wildner 
170212bd3c8bSSascha Wildner 	case SC_PREVENT_ALLOW_MEDIUM_REMOVAL:
1703a41b1dd4SMarkus Pfeiffer 		error = ustorage_fs_min_len(sc, 0, -1U);
170412bd3c8bSSascha Wildner 		if (error) {
170512bd3c8bSSascha Wildner 			break;
170612bd3c8bSSascha Wildner 		}
170712bd3c8bSSascha Wildner 		error = ustorage_fs_check_cmd(sc, 6,
170812bd3c8bSSascha Wildner 		    (1UL << 4) | 1, 0);
170912bd3c8bSSascha Wildner 		if (error) {
171012bd3c8bSSascha Wildner 			break;
171112bd3c8bSSascha Wildner 		}
171212bd3c8bSSascha Wildner 		error = ustorage_fs_prevent_allow(sc);
171312bd3c8bSSascha Wildner 
171412bd3c8bSSascha Wildner 		break;
171512bd3c8bSSascha Wildner 
171612bd3c8bSSascha Wildner 	case SC_READ_6:
1717a41b1dd4SMarkus Pfeiffer 		i = sc->sc_cbw->CBWCDB[4];
171812bd3c8bSSascha Wildner 		sc->sc_transfer.cmd_dir = DIR_WRITE;
171912bd3c8bSSascha Wildner 		temp = ((i == 0) ? 256UL : i);
172012bd3c8bSSascha Wildner 		error = ustorage_fs_min_len(sc, temp << 9, mask9);
172112bd3c8bSSascha Wildner 		if (error) {
172212bd3c8bSSascha Wildner 			break;
172312bd3c8bSSascha Wildner 		}
172412bd3c8bSSascha Wildner 		error = ustorage_fs_check_cmd(sc, 6,
172512bd3c8bSSascha Wildner 		    (7UL << 1) | (1UL << 4) | 1, 1);
172612bd3c8bSSascha Wildner 		if (error) {
172712bd3c8bSSascha Wildner 			break;
172812bd3c8bSSascha Wildner 		}
172912bd3c8bSSascha Wildner 		error = ustorage_fs_read(sc);
173012bd3c8bSSascha Wildner 
173112bd3c8bSSascha Wildner 		break;
173212bd3c8bSSascha Wildner 
173312bd3c8bSSascha Wildner 	case SC_READ_10:
173412bd3c8bSSascha Wildner 		sc->sc_transfer.cmd_dir = DIR_WRITE;
1735a41b1dd4SMarkus Pfeiffer 		temp = get_be16(&sc->sc_cbw->CBWCDB[7]);
173612bd3c8bSSascha Wildner 		error = ustorage_fs_min_len(sc, temp << 9, mask9);
173712bd3c8bSSascha Wildner 		if (error) {
173812bd3c8bSSascha Wildner 			break;
173912bd3c8bSSascha Wildner 		}
174012bd3c8bSSascha Wildner 		error = ustorage_fs_check_cmd(sc, 10,
174112bd3c8bSSascha Wildner 		    (1UL << 1) | (0xfUL << 2) | (3UL << 7) | 1, 1);
174212bd3c8bSSascha Wildner 		if (error) {
174312bd3c8bSSascha Wildner 			break;
174412bd3c8bSSascha Wildner 		}
174512bd3c8bSSascha Wildner 		error = ustorage_fs_read(sc);
174612bd3c8bSSascha Wildner 
174712bd3c8bSSascha Wildner 		break;
174812bd3c8bSSascha Wildner 
174912bd3c8bSSascha Wildner 	case SC_READ_12:
175012bd3c8bSSascha Wildner 		sc->sc_transfer.cmd_dir = DIR_WRITE;
1751a41b1dd4SMarkus Pfeiffer 		temp = get_be32(&sc->sc_cbw->CBWCDB[6]);
175212bd3c8bSSascha Wildner 		if (temp >= (1UL << (32 - 9))) {
175312bd3c8bSSascha Wildner 			/* numerical overflow */
1754a41b1dd4SMarkus Pfeiffer 			sc->sc_csw->bCSWStatus = CSWSTATUS_FAILED;
175512bd3c8bSSascha Wildner 			error = 1;
175612bd3c8bSSascha Wildner 			break;
175712bd3c8bSSascha Wildner 		}
175812bd3c8bSSascha Wildner 		error = ustorage_fs_min_len(sc, temp << 9, mask9);
175912bd3c8bSSascha Wildner 		if (error) {
176012bd3c8bSSascha Wildner 			break;
176112bd3c8bSSascha Wildner 		}
176212bd3c8bSSascha Wildner 		error = ustorage_fs_check_cmd(sc, 12,
176312bd3c8bSSascha Wildner 		    (1UL << 1) | (0xfUL << 2) | (0xfUL << 6) | 1, 1);
176412bd3c8bSSascha Wildner 		if (error) {
176512bd3c8bSSascha Wildner 			break;
176612bd3c8bSSascha Wildner 		}
176712bd3c8bSSascha Wildner 		error = ustorage_fs_read(sc);
176812bd3c8bSSascha Wildner 
176912bd3c8bSSascha Wildner 		break;
177012bd3c8bSSascha Wildner 
177112bd3c8bSSascha Wildner 	case SC_READ_CAPACITY:
177212bd3c8bSSascha Wildner 		sc->sc_transfer.cmd_dir = DIR_WRITE;
177312bd3c8bSSascha Wildner 		error = ustorage_fs_check_cmd(sc, 10,
177412bd3c8bSSascha Wildner 		    (0xfUL << 2) | (1UL << 8) | 1, 1);
177512bd3c8bSSascha Wildner 		if (error) {
177612bd3c8bSSascha Wildner 			break;
177712bd3c8bSSascha Wildner 		}
177812bd3c8bSSascha Wildner 		error = ustorage_fs_read_capacity(sc);
177912bd3c8bSSascha Wildner 
178012bd3c8bSSascha Wildner 		break;
178112bd3c8bSSascha Wildner 
178212bd3c8bSSascha Wildner 	case SC_READ_FORMAT_CAPACITIES:
178312bd3c8bSSascha Wildner 		sc->sc_transfer.cmd_dir = DIR_WRITE;
178412bd3c8bSSascha Wildner 		error = ustorage_fs_min_len(sc,
1785a41b1dd4SMarkus Pfeiffer 		    get_be16(&sc->sc_cbw->CBWCDB[7]), -1U);
178612bd3c8bSSascha Wildner 		if (error) {
178712bd3c8bSSascha Wildner 			break;
178812bd3c8bSSascha Wildner 		}
178912bd3c8bSSascha Wildner 		error = ustorage_fs_check_cmd(sc, 10,
179012bd3c8bSSascha Wildner 		    (3UL << 7) | 1, 1);
179112bd3c8bSSascha Wildner 		if (error) {
179212bd3c8bSSascha Wildner 			break;
179312bd3c8bSSascha Wildner 		}
179412bd3c8bSSascha Wildner 		error = ustorage_fs_read_format_capacities(sc);
179512bd3c8bSSascha Wildner 
179612bd3c8bSSascha Wildner 		break;
179712bd3c8bSSascha Wildner 
179812bd3c8bSSascha Wildner 	case SC_REQUEST_SENSE:
179912bd3c8bSSascha Wildner 		sc->sc_transfer.cmd_dir = DIR_WRITE;
1800a41b1dd4SMarkus Pfeiffer 		error = ustorage_fs_min_len(sc, sc->sc_cbw->CBWCDB[4], -1U);
180112bd3c8bSSascha Wildner 		if (error) {
180212bd3c8bSSascha Wildner 			break;
180312bd3c8bSSascha Wildner 		}
180412bd3c8bSSascha Wildner 		error = ustorage_fs_check_cmd(sc, 6,
180512bd3c8bSSascha Wildner 		    (1UL << 4) | 1, 0);
180612bd3c8bSSascha Wildner 		if (error) {
180712bd3c8bSSascha Wildner 			break;
180812bd3c8bSSascha Wildner 		}
180912bd3c8bSSascha Wildner 		error = ustorage_fs_request_sense(sc);
181012bd3c8bSSascha Wildner 
181112bd3c8bSSascha Wildner 		break;
181212bd3c8bSSascha Wildner 
181312bd3c8bSSascha Wildner 	case SC_START_STOP_UNIT:
1814a41b1dd4SMarkus Pfeiffer 		error = ustorage_fs_min_len(sc, 0, -1U);
181512bd3c8bSSascha Wildner 		if (error) {
181612bd3c8bSSascha Wildner 			break;
181712bd3c8bSSascha Wildner 		}
181812bd3c8bSSascha Wildner 		error = ustorage_fs_check_cmd(sc, 6,
181912bd3c8bSSascha Wildner 		    (1UL << 1) | (1UL << 4) | 1, 0);
182012bd3c8bSSascha Wildner 		if (error) {
182112bd3c8bSSascha Wildner 			break;
182212bd3c8bSSascha Wildner 		}
182312bd3c8bSSascha Wildner 		error = ustorage_fs_start_stop(sc);
182412bd3c8bSSascha Wildner 
182512bd3c8bSSascha Wildner 		break;
182612bd3c8bSSascha Wildner 
182712bd3c8bSSascha Wildner 	case SC_SYNCHRONIZE_CACHE:
1828a41b1dd4SMarkus Pfeiffer 		error = ustorage_fs_min_len(sc, 0, -1U);
182912bd3c8bSSascha Wildner 		if (error) {
183012bd3c8bSSascha Wildner 			break;
183112bd3c8bSSascha Wildner 		}
183212bd3c8bSSascha Wildner 		error = ustorage_fs_check_cmd(sc, 10,
183312bd3c8bSSascha Wildner 		    (0xfUL << 2) | (3UL << 7) | 1, 1);
183412bd3c8bSSascha Wildner 		if (error) {
183512bd3c8bSSascha Wildner 			break;
183612bd3c8bSSascha Wildner 		}
183712bd3c8bSSascha Wildner 		error = ustorage_fs_synchronize_cache(sc);
183812bd3c8bSSascha Wildner 
183912bd3c8bSSascha Wildner 		break;
184012bd3c8bSSascha Wildner 
184112bd3c8bSSascha Wildner 	case SC_TEST_UNIT_READY:
1842a41b1dd4SMarkus Pfeiffer 		error = ustorage_fs_min_len(sc, 0, -1U);
184312bd3c8bSSascha Wildner 		if (error) {
184412bd3c8bSSascha Wildner 			break;
184512bd3c8bSSascha Wildner 		}
184612bd3c8bSSascha Wildner 		error = ustorage_fs_check_cmd(sc, 6,
184712bd3c8bSSascha Wildner 		    0 | 1, 1);
184812bd3c8bSSascha Wildner 		break;
184912bd3c8bSSascha Wildner 
185012bd3c8bSSascha Wildner 		/*
185112bd3c8bSSascha Wildner 		 * Although optional, this command is used by MS-Windows.
185212bd3c8bSSascha Wildner 		 * We support a minimal version: BytChk must be 0.
185312bd3c8bSSascha Wildner 		 */
185412bd3c8bSSascha Wildner 	case SC_VERIFY:
1855a41b1dd4SMarkus Pfeiffer 		error = ustorage_fs_min_len(sc, 0, -1U);
185612bd3c8bSSascha Wildner 		if (error) {
185712bd3c8bSSascha Wildner 			break;
185812bd3c8bSSascha Wildner 		}
185912bd3c8bSSascha Wildner 		error = ustorage_fs_check_cmd(sc, 10,
186012bd3c8bSSascha Wildner 		    (1UL << 1) | (0xfUL << 2) | (3UL << 7) | 1, 1);
186112bd3c8bSSascha Wildner 		if (error) {
186212bd3c8bSSascha Wildner 			break;
186312bd3c8bSSascha Wildner 		}
186412bd3c8bSSascha Wildner 		error = ustorage_fs_verify(sc);
186512bd3c8bSSascha Wildner 
186612bd3c8bSSascha Wildner 		break;
186712bd3c8bSSascha Wildner 
186812bd3c8bSSascha Wildner 	case SC_WRITE_6:
1869a41b1dd4SMarkus Pfeiffer 		i = sc->sc_cbw->CBWCDB[4];
187012bd3c8bSSascha Wildner 		sc->sc_transfer.cmd_dir = DIR_READ;
187112bd3c8bSSascha Wildner 		temp = ((i == 0) ? 256UL : i);
187212bd3c8bSSascha Wildner 		error = ustorage_fs_min_len(sc, temp << 9, mask9);
187312bd3c8bSSascha Wildner 		if (error) {
187412bd3c8bSSascha Wildner 			break;
187512bd3c8bSSascha Wildner 		}
187612bd3c8bSSascha Wildner 		error = ustorage_fs_check_cmd(sc, 6,
187712bd3c8bSSascha Wildner 		    (7UL << 1) | (1UL << 4) | 1, 1);
187812bd3c8bSSascha Wildner 		if (error) {
187912bd3c8bSSascha Wildner 			break;
188012bd3c8bSSascha Wildner 		}
188112bd3c8bSSascha Wildner 		error = ustorage_fs_write(sc);
188212bd3c8bSSascha Wildner 
188312bd3c8bSSascha Wildner 		break;
188412bd3c8bSSascha Wildner 
188512bd3c8bSSascha Wildner 	case SC_WRITE_10:
188612bd3c8bSSascha Wildner 		sc->sc_transfer.cmd_dir = DIR_READ;
1887a41b1dd4SMarkus Pfeiffer 		temp = get_be16(&sc->sc_cbw->CBWCDB[7]);
188812bd3c8bSSascha Wildner 		error = ustorage_fs_min_len(sc, temp << 9, mask9);
188912bd3c8bSSascha Wildner 		if (error) {
189012bd3c8bSSascha Wildner 			break;
189112bd3c8bSSascha Wildner 		}
189212bd3c8bSSascha Wildner 		error = ustorage_fs_check_cmd(sc, 10,
189312bd3c8bSSascha Wildner 		    (1UL << 1) | (0xfUL << 2) | (3UL << 7) | 1, 1);
189412bd3c8bSSascha Wildner 		if (error) {
189512bd3c8bSSascha Wildner 			break;
189612bd3c8bSSascha Wildner 		}
189712bd3c8bSSascha Wildner 		error = ustorage_fs_write(sc);
189812bd3c8bSSascha Wildner 
189912bd3c8bSSascha Wildner 		break;
190012bd3c8bSSascha Wildner 
190112bd3c8bSSascha Wildner 	case SC_WRITE_12:
190212bd3c8bSSascha Wildner 		sc->sc_transfer.cmd_dir = DIR_READ;
1903a41b1dd4SMarkus Pfeiffer 		temp = get_be32(&sc->sc_cbw->CBWCDB[6]);
190412bd3c8bSSascha Wildner 		if (temp > (mask9 >> 9)) {
190512bd3c8bSSascha Wildner 			/* numerical overflow */
1906a41b1dd4SMarkus Pfeiffer 			sc->sc_csw->bCSWStatus = CSWSTATUS_FAILED;
190712bd3c8bSSascha Wildner 			error = 1;
190812bd3c8bSSascha Wildner 			break;
190912bd3c8bSSascha Wildner 		}
191012bd3c8bSSascha Wildner 		error = ustorage_fs_min_len(sc, temp << 9, mask9);
191112bd3c8bSSascha Wildner 		if (error) {
191212bd3c8bSSascha Wildner 			break;
191312bd3c8bSSascha Wildner 		}
191412bd3c8bSSascha Wildner 		error = ustorage_fs_check_cmd(sc, 12,
191512bd3c8bSSascha Wildner 		    (1UL << 1) | (0xfUL << 2) | (0xfUL << 6) | 1, 1);
191612bd3c8bSSascha Wildner 		if (error) {
191712bd3c8bSSascha Wildner 			break;
191812bd3c8bSSascha Wildner 		}
191912bd3c8bSSascha Wildner 		error = ustorage_fs_write(sc);
192012bd3c8bSSascha Wildner 
192112bd3c8bSSascha Wildner 		break;
192212bd3c8bSSascha Wildner 
192312bd3c8bSSascha Wildner 		/*
192412bd3c8bSSascha Wildner 		 * Some mandatory commands that we recognize but don't
192512bd3c8bSSascha Wildner 		 * implement.  They don't mean much in this setting.
192612bd3c8bSSascha Wildner 		 * It's left as an exercise for anyone interested to
192712bd3c8bSSascha Wildner 		 * implement RESERVE and RELEASE in terms of Posix
192812bd3c8bSSascha Wildner 		 * locks.
192912bd3c8bSSascha Wildner 		 */
193012bd3c8bSSascha Wildner 	case SC_FORMAT_UNIT:
193112bd3c8bSSascha Wildner 	case SC_RELEASE:
193212bd3c8bSSascha Wildner 	case SC_RESERVE:
193312bd3c8bSSascha Wildner 	case SC_SEND_DIAGNOSTIC:
193412bd3c8bSSascha Wildner 		/* Fallthrough */
193512bd3c8bSSascha Wildner 
193612bd3c8bSSascha Wildner 	default:
1937a41b1dd4SMarkus Pfeiffer 		error = ustorage_fs_min_len(sc, 0, -1U);
193812bd3c8bSSascha Wildner 		if (error) {
193912bd3c8bSSascha Wildner 			break;
194012bd3c8bSSascha Wildner 		}
194112bd3c8bSSascha Wildner 		error = ustorage_fs_check_cmd(sc, sc->sc_transfer.cmd_len,
194212bd3c8bSSascha Wildner 		    0xff, 0);
194312bd3c8bSSascha Wildner 		if (error) {
194412bd3c8bSSascha Wildner 			break;
194512bd3c8bSSascha Wildner 		}
194612bd3c8bSSascha Wildner 		sc->sc_transfer.currlun->sense_data =
194712bd3c8bSSascha Wildner 		    SS_INVALID_COMMAND;
194812bd3c8bSSascha Wildner 		error = 1;
194912bd3c8bSSascha Wildner 
195012bd3c8bSSascha Wildner 		break;
195112bd3c8bSSascha Wildner 	}
195212bd3c8bSSascha Wildner 	return (error);
195312bd3c8bSSascha Wildner }
1954