xref: /dragonfly/sys/bus/u4b/usb_msctest.c (revision 2b3f93ea)
18930c119SMarkus Pfeiffer /* $FreeBSD: head/sys/dev/usb/usb_msctest.c 269578 2014-08-05 09:59:16Z n_hibma $ */
212bd3c8bSSascha Wildner /*-
312bd3c8bSSascha Wildner  * Copyright (c) 2008,2011 Hans Petter Selasky. All rights reserved.
412bd3c8bSSascha Wildner  *
512bd3c8bSSascha Wildner  * Redistribution and use in source and binary forms, with or without
612bd3c8bSSascha Wildner  * modification, are permitted provided that the following conditions
712bd3c8bSSascha Wildner  * are met:
812bd3c8bSSascha Wildner  * 1. Redistributions of source code must retain the above copyright
912bd3c8bSSascha Wildner  *    notice, this list of conditions and the following disclaimer.
1012bd3c8bSSascha Wildner  * 2. Redistributions in binary form must reproduce the above copyright
1112bd3c8bSSascha Wildner  *    notice, this list of conditions and the following disclaimer in the
1212bd3c8bSSascha Wildner  *    documentation and/or other materials provided with the distribution.
1312bd3c8bSSascha Wildner  *
1412bd3c8bSSascha Wildner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1512bd3c8bSSascha Wildner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1612bd3c8bSSascha Wildner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1712bd3c8bSSascha Wildner  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1812bd3c8bSSascha Wildner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1912bd3c8bSSascha Wildner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2012bd3c8bSSascha Wildner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2112bd3c8bSSascha Wildner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2212bd3c8bSSascha Wildner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2312bd3c8bSSascha Wildner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2412bd3c8bSSascha Wildner  * SUCH DAMAGE.
2512bd3c8bSSascha Wildner  */
2612bd3c8bSSascha Wildner 
2712bd3c8bSSascha Wildner /*
2812bd3c8bSSascha Wildner  * The following file contains code that will detect USB autoinstall
2912bd3c8bSSascha Wildner  * disks.
3012bd3c8bSSascha Wildner  *
3112bd3c8bSSascha Wildner  * TODO: Potentially we could add code to automatically detect USB
3212bd3c8bSSascha Wildner  * mass storage quirks for not supported SCSI commands!
3312bd3c8bSSascha Wildner  */
3412bd3c8bSSascha Wildner 
3512bd3c8bSSascha Wildner #include <sys/stdint.h>
3612bd3c8bSSascha Wildner #include <sys/param.h>
3712bd3c8bSSascha Wildner #include <sys/queue.h>
3812bd3c8bSSascha Wildner #include <sys/types.h>
3912bd3c8bSSascha Wildner #include <sys/systm.h>
4012bd3c8bSSascha Wildner #include <sys/kernel.h>
4112bd3c8bSSascha Wildner #include <sys/bus.h>
4212bd3c8bSSascha Wildner #include <sys/module.h>
4312bd3c8bSSascha Wildner #include <sys/lock.h>
4412bd3c8bSSascha Wildner #include <sys/condvar.h>
4512bd3c8bSSascha Wildner #include <sys/sysctl.h>
4612bd3c8bSSascha Wildner #include <sys/unistd.h>
4712bd3c8bSSascha Wildner #include <sys/callout.h>
4812bd3c8bSSascha Wildner #include <sys/malloc.h>
49*2b3f93eaSMatthew Dillon #include <sys/caps.h>
5012bd3c8bSSascha Wildner 
51722d05c3SSascha Wildner #include <bus/u4b/usb.h>
52722d05c3SSascha Wildner #include <bus/u4b/usbdi.h>
53722d05c3SSascha Wildner #include <bus/u4b/usbdi_util.h>
5412bd3c8bSSascha Wildner 
5512bd3c8bSSascha Wildner #define	USB_DEBUG_VAR usb_debug
5612bd3c8bSSascha Wildner 
57722d05c3SSascha Wildner #include <bus/u4b/usb_busdma.h>
58722d05c3SSascha Wildner #include <bus/u4b/usb_process.h>
59722d05c3SSascha Wildner #include <bus/u4b/usb_transfer.h>
60722d05c3SSascha Wildner #include <bus/u4b/usb_msctest.h>
61722d05c3SSascha Wildner #include <bus/u4b/usb_debug.h>
62722d05c3SSascha Wildner #include <bus/u4b/usb_device.h>
63722d05c3SSascha Wildner #include <bus/u4b/usb_request.h>
64722d05c3SSascha Wildner #include <bus/u4b/usb_util.h>
65722d05c3SSascha Wildner #include <bus/u4b/quirk/usb_quirk.h>
6612bd3c8bSSascha Wildner 
6712bd3c8bSSascha Wildner enum {
6812bd3c8bSSascha Wildner 	ST_COMMAND,
6912bd3c8bSSascha Wildner 	ST_DATA_RD,
7012bd3c8bSSascha Wildner 	ST_DATA_RD_CS,
7112bd3c8bSSascha Wildner 	ST_DATA_WR,
7212bd3c8bSSascha Wildner 	ST_DATA_WR_CS,
7312bd3c8bSSascha Wildner 	ST_STATUS,
7412bd3c8bSSascha Wildner 	ST_MAX,
7512bd3c8bSSascha Wildner };
7612bd3c8bSSascha Wildner 
7712bd3c8bSSascha Wildner enum {
7812bd3c8bSSascha Wildner 	DIR_IN,
7912bd3c8bSSascha Wildner 	DIR_OUT,
8012bd3c8bSSascha Wildner 	DIR_NONE,
8112bd3c8bSSascha Wildner };
8212bd3c8bSSascha Wildner 
838930c119SMarkus Pfeiffer #define	SCSI_MAX_LEN	MAX(SCSI_FIXED_BLOCK_SIZE, USB_MSCTEST_BULK_SIZE)
8412bd3c8bSSascha Wildner #define	SCSI_INQ_LEN	0x24
8512bd3c8bSSascha Wildner #define	SCSI_SENSE_LEN	0xFF
868930c119SMarkus Pfeiffer #define	SCSI_FIXED_BLOCK_SIZE 512	/* bytes */
8712bd3c8bSSascha Wildner 
8812bd3c8bSSascha Wildner static uint8_t scsi_test_unit_ready[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8912bd3c8bSSascha Wildner static uint8_t scsi_inquiry[] = { 0x12, 0x00, 0x00, 0x00, SCSI_INQ_LEN, 0x00 };
9012bd3c8bSSascha Wildner static uint8_t scsi_rezero_init[] =     { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
9112bd3c8bSSascha Wildner static uint8_t scsi_start_stop_unit[] = { 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00 };
9212bd3c8bSSascha Wildner static uint8_t scsi_ztestor_eject[] =   { 0x85, 0x01, 0x01, 0x01, 0x18, 0x01,
9312bd3c8bSSascha Wildner 					  0x01, 0x01, 0x01, 0x01, 0x00, 0x00 };
9412bd3c8bSSascha Wildner static uint8_t scsi_cmotech_eject[] =   { 0xff, 0x52, 0x44, 0x45, 0x56, 0x43,
9512bd3c8bSSascha Wildner 					  0x48, 0x47 };
9612bd3c8bSSascha Wildner static uint8_t scsi_huawei_eject[] =	{ 0x11, 0x06, 0x00, 0x00, 0x00, 0x00,
9712bd3c8bSSascha Wildner 					  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9812bd3c8bSSascha Wildner 					  0x00, 0x00, 0x00, 0x00 };
998930c119SMarkus Pfeiffer static uint8_t scsi_huawei_eject2[] =	{ 0x11, 0x06, 0x20, 0x00, 0x00, 0x01,
1008930c119SMarkus Pfeiffer 					  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1018930c119SMarkus Pfeiffer 					  0x00, 0x00, 0x00, 0x00 };
10212bd3c8bSSascha Wildner static uint8_t scsi_tct_eject[] =	{ 0x06, 0xf5, 0x04, 0x02, 0x52, 0x70 };
10312bd3c8bSSascha Wildner static uint8_t scsi_sync_cache[] =	{ 0x35, 0x00, 0x00, 0x00, 0x00, 0x00,
10412bd3c8bSSascha Wildner 					  0x00, 0x00, 0x00, 0x00 };
10512bd3c8bSSascha Wildner static uint8_t scsi_request_sense[] =	{ 0x03, 0x00, 0x00, 0x00, 0x12, 0x00,
10612bd3c8bSSascha Wildner 					  0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
10757bed822SMarkus Pfeiffer static uint8_t scsi_read_capacity[] =	{ 0x25, 0x00, 0x00, 0x00, 0x00, 0x00,
10857bed822SMarkus Pfeiffer 					  0x00, 0x00, 0x00, 0x00 };
109dd681da6SMatthew Dillon static uint8_t scsi_prevent_removal[] =	{ 0x1e, 0, 0, 0, 1, 0 };
110dd681da6SMatthew Dillon static uint8_t scsi_allow_removal[] =	{ 0x1e, 0, 0, 0, 0, 0 };
11112bd3c8bSSascha Wildner 
1128930c119SMarkus Pfeiffer #ifndef USB_MSCTEST_BULK_SIZE
1138930c119SMarkus Pfeiffer #define	USB_MSCTEST_BULK_SIZE	64	/* dummy */
1148930c119SMarkus Pfeiffer #endif
1158930c119SMarkus Pfeiffer 
11612bd3c8bSSascha Wildner #define	ERR_CSW_FAILED		-1
11712bd3c8bSSascha Wildner 
11812bd3c8bSSascha Wildner /* Command Block Wrapper */
11912bd3c8bSSascha Wildner struct bbb_cbw {
12012bd3c8bSSascha Wildner 	uDWord	dCBWSignature;
12112bd3c8bSSascha Wildner #define	CBWSIGNATURE	0x43425355
12212bd3c8bSSascha Wildner 	uDWord	dCBWTag;
12312bd3c8bSSascha Wildner 	uDWord	dCBWDataTransferLength;
12412bd3c8bSSascha Wildner 	uByte	bCBWFlags;
12512bd3c8bSSascha Wildner #define	CBWFLAGS_OUT	0x00
12612bd3c8bSSascha Wildner #define	CBWFLAGS_IN	0x80
12712bd3c8bSSascha Wildner 	uByte	bCBWLUN;
12812bd3c8bSSascha Wildner 	uByte	bCDBLength;
12912bd3c8bSSascha Wildner #define	CBWCDBLENGTH	16
13012bd3c8bSSascha Wildner 	uByte	CBWCDB[CBWCDBLENGTH];
13112bd3c8bSSascha Wildner } __packed;
13212bd3c8bSSascha Wildner 
13312bd3c8bSSascha Wildner /* Command Status Wrapper */
13412bd3c8bSSascha Wildner struct bbb_csw {
13512bd3c8bSSascha Wildner 	uDWord	dCSWSignature;
13612bd3c8bSSascha Wildner #define	CSWSIGNATURE	0x53425355
13712bd3c8bSSascha Wildner 	uDWord	dCSWTag;
13812bd3c8bSSascha Wildner 	uDWord	dCSWDataResidue;
13912bd3c8bSSascha Wildner 	uByte	bCSWStatus;
14012bd3c8bSSascha Wildner #define	CSWSTATUS_GOOD	0x0
14112bd3c8bSSascha Wildner #define	CSWSTATUS_FAILED	0x1
14212bd3c8bSSascha Wildner #define	CSWSTATUS_PHASE	0x2
14312bd3c8bSSascha Wildner } __packed;
14412bd3c8bSSascha Wildner 
14512bd3c8bSSascha Wildner struct bbb_transfer {
146722d05c3SSascha Wildner 	struct lock lock;
14712bd3c8bSSascha Wildner 	struct cv cv;
14857bed822SMarkus Pfeiffer 	struct bbb_cbw *cbw;
14957bed822SMarkus Pfeiffer 	struct bbb_csw *csw;
15012bd3c8bSSascha Wildner 
15112bd3c8bSSascha Wildner 	struct usb_xfer *xfer[ST_MAX];
15212bd3c8bSSascha Wildner 
15312bd3c8bSSascha Wildner 	uint8_t *data_ptr;
15412bd3c8bSSascha Wildner 
15512bd3c8bSSascha Wildner 	usb_size_t data_len;		/* bytes */
15612bd3c8bSSascha Wildner 	usb_size_t data_rem;		/* bytes */
15712bd3c8bSSascha Wildner 	usb_timeout_t data_timeout;	/* ms */
15812bd3c8bSSascha Wildner 	usb_frlength_t actlen;		/* bytes */
15957bed822SMarkus Pfeiffer 	usb_frlength_t buffer_size;    	/* bytes */
16012bd3c8bSSascha Wildner 
16112bd3c8bSSascha Wildner 	uint8_t	cmd_len;		/* bytes */
16212bd3c8bSSascha Wildner 	uint8_t	dir;
16312bd3c8bSSascha Wildner 	uint8_t	lun;
16412bd3c8bSSascha Wildner 	uint8_t	state;
16512bd3c8bSSascha Wildner 	uint8_t	status_try;
16612bd3c8bSSascha Wildner 	int	error;
16712bd3c8bSSascha Wildner 
16857bed822SMarkus Pfeiffer 	uint8_t	*buffer;
16912bd3c8bSSascha Wildner };
17012bd3c8bSSascha Wildner 
17112bd3c8bSSascha Wildner static usb_callback_t bbb_command_callback;
17212bd3c8bSSascha Wildner static usb_callback_t bbb_data_read_callback;
17312bd3c8bSSascha Wildner static usb_callback_t bbb_data_rd_cs_callback;
17412bd3c8bSSascha Wildner static usb_callback_t bbb_data_write_callback;
17512bd3c8bSSascha Wildner static usb_callback_t bbb_data_wr_cs_callback;
17612bd3c8bSSascha Wildner static usb_callback_t bbb_status_callback;
177520083baSSascha Wildner static usb_callback_t bbb_raw_write_callback;
17812bd3c8bSSascha Wildner 
17912bd3c8bSSascha Wildner static void	bbb_done(struct bbb_transfer *, int);
18012bd3c8bSSascha Wildner static void	bbb_transfer_start(struct bbb_transfer *, uint8_t);
18112bd3c8bSSascha Wildner static void	bbb_data_clear_stall_callback(struct usb_xfer *, uint8_t,
18212bd3c8bSSascha Wildner 		    uint8_t);
18312bd3c8bSSascha Wildner static int	bbb_command_start(struct bbb_transfer *, uint8_t, uint8_t,
18412bd3c8bSSascha Wildner 		    void *, size_t, void *, size_t, usb_timeout_t);
185520083baSSascha Wildner static struct bbb_transfer *bbb_attach(struct usb_device *, uint8_t, uint8_t);
18612bd3c8bSSascha Wildner static void	bbb_detach(struct bbb_transfer *);
18712bd3c8bSSascha Wildner 
18812bd3c8bSSascha Wildner static const struct usb_config bbb_config[ST_MAX] = {
18912bd3c8bSSascha Wildner 
19012bd3c8bSSascha Wildner 	[ST_COMMAND] = {
19112bd3c8bSSascha Wildner 		.type = UE_BULK,
19212bd3c8bSSascha Wildner 		.endpoint = UE_ADDR_ANY,
19312bd3c8bSSascha Wildner 		.direction = UE_DIR_OUT,
19412bd3c8bSSascha Wildner 		.bufsize = sizeof(struct bbb_cbw),
19512bd3c8bSSascha Wildner 		.callback = &bbb_command_callback,
19612bd3c8bSSascha Wildner 		.timeout = 4 * USB_MS_HZ,	/* 4 seconds */
19712bd3c8bSSascha Wildner 	},
19812bd3c8bSSascha Wildner 
19912bd3c8bSSascha Wildner 	[ST_DATA_RD] = {
20012bd3c8bSSascha Wildner 		.type = UE_BULK,
20112bd3c8bSSascha Wildner 		.endpoint = UE_ADDR_ANY,
20212bd3c8bSSascha Wildner 		.direction = UE_DIR_IN,
20357bed822SMarkus Pfeiffer 		.bufsize = SCSI_MAX_LEN,
20457bed822SMarkus Pfeiffer 		.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,},
20512bd3c8bSSascha Wildner 		.callback = &bbb_data_read_callback,
20612bd3c8bSSascha Wildner 		.timeout = 4 * USB_MS_HZ,	/* 4 seconds */
20712bd3c8bSSascha Wildner 	},
20812bd3c8bSSascha Wildner 
20912bd3c8bSSascha Wildner 	[ST_DATA_RD_CS] = {
21012bd3c8bSSascha Wildner 		.type = UE_CONTROL,
21112bd3c8bSSascha Wildner 		.endpoint = 0x00,	/* Control pipe */
21212bd3c8bSSascha Wildner 		.direction = UE_DIR_ANY,
21312bd3c8bSSascha Wildner 		.bufsize = sizeof(struct usb_device_request),
21412bd3c8bSSascha Wildner 		.callback = &bbb_data_rd_cs_callback,
21512bd3c8bSSascha Wildner 		.timeout = 1 * USB_MS_HZ,	/* 1 second  */
21612bd3c8bSSascha Wildner 	},
21712bd3c8bSSascha Wildner 
21812bd3c8bSSascha Wildner 	[ST_DATA_WR] = {
21912bd3c8bSSascha Wildner 		.type = UE_BULK,
22012bd3c8bSSascha Wildner 		.endpoint = UE_ADDR_ANY,
22112bd3c8bSSascha Wildner 		.direction = UE_DIR_OUT,
22257bed822SMarkus Pfeiffer 		.bufsize = SCSI_MAX_LEN,
22312bd3c8bSSascha Wildner 		.flags = {.ext_buffer = 1,.proxy_buffer = 1,},
22412bd3c8bSSascha Wildner 		.callback = &bbb_data_write_callback,
22512bd3c8bSSascha Wildner 		.timeout = 4 * USB_MS_HZ,	/* 4 seconds */
22612bd3c8bSSascha Wildner 	},
22712bd3c8bSSascha Wildner 
22812bd3c8bSSascha Wildner 	[ST_DATA_WR_CS] = {
22912bd3c8bSSascha Wildner 		.type = UE_CONTROL,
23012bd3c8bSSascha Wildner 		.endpoint = 0x00,	/* Control pipe */
23112bd3c8bSSascha Wildner 		.direction = UE_DIR_ANY,
23212bd3c8bSSascha Wildner 		.bufsize = sizeof(struct usb_device_request),
23312bd3c8bSSascha Wildner 		.callback = &bbb_data_wr_cs_callback,
23412bd3c8bSSascha Wildner 		.timeout = 1 * USB_MS_HZ,	/* 1 second  */
23512bd3c8bSSascha Wildner 	},
23612bd3c8bSSascha Wildner 
23712bd3c8bSSascha Wildner 	[ST_STATUS] = {
23812bd3c8bSSascha Wildner 		.type = UE_BULK,
23912bd3c8bSSascha Wildner 		.endpoint = UE_ADDR_ANY,
24012bd3c8bSSascha Wildner 		.direction = UE_DIR_IN,
24112bd3c8bSSascha Wildner 		.bufsize = sizeof(struct bbb_csw),
24257bed822SMarkus Pfeiffer 		.flags = {.short_xfer_ok = 1,},
24312bd3c8bSSascha Wildner 		.callback = &bbb_status_callback,
24412bd3c8bSSascha Wildner 		.timeout = 1 * USB_MS_HZ,	/* 1 second  */
24512bd3c8bSSascha Wildner 	},
24612bd3c8bSSascha Wildner };
24712bd3c8bSSascha Wildner 
248520083baSSascha Wildner static const struct usb_config bbb_raw_config[1] = {
249520083baSSascha Wildner 
250520083baSSascha Wildner 	[0] = {
251520083baSSascha Wildner 		.type = UE_BULK_INTR,
252520083baSSascha Wildner 		.endpoint = UE_ADDR_ANY,
253520083baSSascha Wildner 		.direction = UE_DIR_OUT,
254520083baSSascha Wildner 		.bufsize = SCSI_MAX_LEN,
255520083baSSascha Wildner 		.flags = {.ext_buffer = 1,.proxy_buffer = 1,},
256520083baSSascha Wildner 		.callback = &bbb_raw_write_callback,
257520083baSSascha Wildner 		.timeout = 1 * USB_MS_HZ,	/* 1 second */
258520083baSSascha Wildner 	},
259520083baSSascha Wildner };
260520083baSSascha Wildner 
26112bd3c8bSSascha Wildner static void
bbb_done(struct bbb_transfer * sc,int error)26212bd3c8bSSascha Wildner bbb_done(struct bbb_transfer *sc, int error)
26312bd3c8bSSascha Wildner {
26412bd3c8bSSascha Wildner 	sc->error = error;
26512bd3c8bSSascha Wildner 	sc->state = ST_COMMAND;
26612bd3c8bSSascha Wildner 	sc->status_try = 1;
26712bd3c8bSSascha Wildner 	cv_signal(&sc->cv);
26812bd3c8bSSascha Wildner }
26912bd3c8bSSascha Wildner 
27012bd3c8bSSascha Wildner static void
bbb_transfer_start(struct bbb_transfer * sc,uint8_t xfer_index)27112bd3c8bSSascha Wildner bbb_transfer_start(struct bbb_transfer *sc, uint8_t xfer_index)
27212bd3c8bSSascha Wildner {
27312bd3c8bSSascha Wildner 	sc->state = xfer_index;
27412bd3c8bSSascha Wildner 	usbd_transfer_start(sc->xfer[xfer_index]);
27512bd3c8bSSascha Wildner }
27612bd3c8bSSascha Wildner 
27712bd3c8bSSascha Wildner static void
bbb_data_clear_stall_callback(struct usb_xfer * xfer,uint8_t next_xfer,uint8_t stall_xfer)27812bd3c8bSSascha Wildner bbb_data_clear_stall_callback(struct usb_xfer *xfer,
27912bd3c8bSSascha Wildner     uint8_t next_xfer, uint8_t stall_xfer)
28012bd3c8bSSascha Wildner {
28112bd3c8bSSascha Wildner 	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
28212bd3c8bSSascha Wildner 
28312bd3c8bSSascha Wildner 	if (usbd_clear_stall_callback(xfer, sc->xfer[stall_xfer])) {
28412bd3c8bSSascha Wildner 		switch (USB_GET_STATE(xfer)) {
28512bd3c8bSSascha Wildner 		case USB_ST_SETUP:
28612bd3c8bSSascha Wildner 		case USB_ST_TRANSFERRED:
28712bd3c8bSSascha Wildner 			bbb_transfer_start(sc, next_xfer);
28812bd3c8bSSascha Wildner 			break;
28912bd3c8bSSascha Wildner 		default:
29012bd3c8bSSascha Wildner 			bbb_done(sc, USB_ERR_STALLED);
29112bd3c8bSSascha Wildner 			break;
29212bd3c8bSSascha Wildner 		}
29312bd3c8bSSascha Wildner 	}
29412bd3c8bSSascha Wildner }
29512bd3c8bSSascha Wildner 
29612bd3c8bSSascha Wildner static void
bbb_command_callback(struct usb_xfer * xfer,usb_error_t error)29712bd3c8bSSascha Wildner bbb_command_callback(struct usb_xfer *xfer, usb_error_t error)
29812bd3c8bSSascha Wildner {
29912bd3c8bSSascha Wildner 	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
30012bd3c8bSSascha Wildner 	uint32_t tag;
30112bd3c8bSSascha Wildner 
30212bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
30312bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
30412bd3c8bSSascha Wildner 		bbb_transfer_start
30512bd3c8bSSascha Wildner 		    (sc, ((sc->dir == DIR_IN) ? ST_DATA_RD :
30612bd3c8bSSascha Wildner 		    (sc->dir == DIR_OUT) ? ST_DATA_WR :
30712bd3c8bSSascha Wildner 		    ST_STATUS));
30812bd3c8bSSascha Wildner 		break;
30912bd3c8bSSascha Wildner 
31012bd3c8bSSascha Wildner 	case USB_ST_SETUP:
31112bd3c8bSSascha Wildner 		sc->status_try = 0;
31257bed822SMarkus Pfeiffer 		tag = UGETDW(sc->cbw->dCBWTag) + 1;
31357bed822SMarkus Pfeiffer 		USETDW(sc->cbw->dCBWSignature, CBWSIGNATURE);
31457bed822SMarkus Pfeiffer 		USETDW(sc->cbw->dCBWTag, tag);
31557bed822SMarkus Pfeiffer 		USETDW(sc->cbw->dCBWDataTransferLength, (uint32_t)sc->data_len);
31657bed822SMarkus Pfeiffer 		sc->cbw->bCBWFlags = ((sc->dir == DIR_IN) ? CBWFLAGS_IN : CBWFLAGS_OUT);
31757bed822SMarkus Pfeiffer 		sc->cbw->bCBWLUN = sc->lun;
31857bed822SMarkus Pfeiffer 		sc->cbw->bCDBLength = sc->cmd_len;
31957bed822SMarkus Pfeiffer 		if (sc->cbw->bCDBLength > sizeof(sc->cbw->CBWCDB)) {
32057bed822SMarkus Pfeiffer 			sc->cbw->bCDBLength = sizeof(sc->cbw->CBWCDB);
32112bd3c8bSSascha Wildner 			DPRINTFN(0, "Truncating long command\n");
32212bd3c8bSSascha Wildner 		}
32357bed822SMarkus Pfeiffer 		usbd_xfer_set_frame_len(xfer, 0,
32457bed822SMarkus Pfeiffer 		    sizeof(struct bbb_cbw));
32512bd3c8bSSascha Wildner 		usbd_transfer_submit(xfer);
32612bd3c8bSSascha Wildner 		break;
32712bd3c8bSSascha Wildner 
32812bd3c8bSSascha Wildner 	default:			/* Error */
32912bd3c8bSSascha Wildner 		bbb_done(sc, error);
33012bd3c8bSSascha Wildner 		break;
33112bd3c8bSSascha Wildner 	}
33212bd3c8bSSascha Wildner }
33312bd3c8bSSascha Wildner 
33412bd3c8bSSascha Wildner static void
bbb_data_read_callback(struct usb_xfer * xfer,usb_error_t error)33512bd3c8bSSascha Wildner bbb_data_read_callback(struct usb_xfer *xfer, usb_error_t error)
33612bd3c8bSSascha Wildner {
33712bd3c8bSSascha Wildner 	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
33812bd3c8bSSascha Wildner 	usb_frlength_t max_bulk = usbd_xfer_max_len(xfer);
33912bd3c8bSSascha Wildner 	int actlen, sumlen;
34012bd3c8bSSascha Wildner 
34112bd3c8bSSascha Wildner 	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
34212bd3c8bSSascha Wildner 
34312bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
34412bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
34512bd3c8bSSascha Wildner 		sc->data_rem -= actlen;
34612bd3c8bSSascha Wildner 		sc->data_ptr += actlen;
34712bd3c8bSSascha Wildner 		sc->actlen += actlen;
34812bd3c8bSSascha Wildner 
34912bd3c8bSSascha Wildner 		if (actlen < sumlen) {
35012bd3c8bSSascha Wildner 			/* short transfer */
35112bd3c8bSSascha Wildner 			sc->data_rem = 0;
35212bd3c8bSSascha Wildner 		}
35312bd3c8bSSascha Wildner 	case USB_ST_SETUP:
35412bd3c8bSSascha Wildner 		DPRINTF("max_bulk=%d, data_rem=%d\n",
35512bd3c8bSSascha Wildner 		    max_bulk, sc->data_rem);
35612bd3c8bSSascha Wildner 
35712bd3c8bSSascha Wildner 		if (sc->data_rem == 0) {
35812bd3c8bSSascha Wildner 			bbb_transfer_start(sc, ST_STATUS);
35912bd3c8bSSascha Wildner 			break;
36012bd3c8bSSascha Wildner 		}
36112bd3c8bSSascha Wildner 		if (max_bulk > sc->data_rem) {
36212bd3c8bSSascha Wildner 			max_bulk = sc->data_rem;
36312bd3c8bSSascha Wildner 		}
36412bd3c8bSSascha Wildner 		usbd_xfer_set_timeout(xfer, sc->data_timeout);
36512bd3c8bSSascha Wildner 		usbd_xfer_set_frame_data(xfer, 0, sc->data_ptr, max_bulk);
36612bd3c8bSSascha Wildner 		usbd_transfer_submit(xfer);
36712bd3c8bSSascha Wildner 		break;
36812bd3c8bSSascha Wildner 
36912bd3c8bSSascha Wildner 	default:			/* Error */
37012bd3c8bSSascha Wildner 		if (error == USB_ERR_CANCELLED) {
37112bd3c8bSSascha Wildner 			bbb_done(sc, error);
37212bd3c8bSSascha Wildner 		} else {
37312bd3c8bSSascha Wildner 			bbb_transfer_start(sc, ST_DATA_RD_CS);
37412bd3c8bSSascha Wildner 		}
37512bd3c8bSSascha Wildner 		break;
37612bd3c8bSSascha Wildner 	}
37712bd3c8bSSascha Wildner }
37812bd3c8bSSascha Wildner 
37912bd3c8bSSascha Wildner static void
bbb_data_rd_cs_callback(struct usb_xfer * xfer,usb_error_t error)38012bd3c8bSSascha Wildner bbb_data_rd_cs_callback(struct usb_xfer *xfer, usb_error_t error)
38112bd3c8bSSascha Wildner {
38212bd3c8bSSascha Wildner 	bbb_data_clear_stall_callback(xfer, ST_STATUS,
38312bd3c8bSSascha Wildner 	    ST_DATA_RD);
38412bd3c8bSSascha Wildner }
38512bd3c8bSSascha Wildner 
38612bd3c8bSSascha Wildner static void
bbb_data_write_callback(struct usb_xfer * xfer,usb_error_t error)38712bd3c8bSSascha Wildner bbb_data_write_callback(struct usb_xfer *xfer, usb_error_t error)
38812bd3c8bSSascha Wildner {
38912bd3c8bSSascha Wildner 	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
39012bd3c8bSSascha Wildner 	usb_frlength_t max_bulk = usbd_xfer_max_len(xfer);
39112bd3c8bSSascha Wildner 	int actlen, sumlen;
39212bd3c8bSSascha Wildner 
39312bd3c8bSSascha Wildner 	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
39412bd3c8bSSascha Wildner 
39512bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
39612bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
39712bd3c8bSSascha Wildner 		sc->data_rem -= actlen;
39812bd3c8bSSascha Wildner 		sc->data_ptr += actlen;
39912bd3c8bSSascha Wildner 		sc->actlen += actlen;
40012bd3c8bSSascha Wildner 
40112bd3c8bSSascha Wildner 		if (actlen < sumlen) {
40212bd3c8bSSascha Wildner 			/* short transfer */
40312bd3c8bSSascha Wildner 			sc->data_rem = 0;
40412bd3c8bSSascha Wildner 		}
40512bd3c8bSSascha Wildner 	case USB_ST_SETUP:
40612bd3c8bSSascha Wildner 		DPRINTF("max_bulk=%d, data_rem=%d\n",
40712bd3c8bSSascha Wildner 		    max_bulk, sc->data_rem);
40812bd3c8bSSascha Wildner 
40912bd3c8bSSascha Wildner 		if (sc->data_rem == 0) {
41012bd3c8bSSascha Wildner 			bbb_transfer_start(sc, ST_STATUS);
41157bed822SMarkus Pfeiffer 			break;
41212bd3c8bSSascha Wildner 		}
41312bd3c8bSSascha Wildner 		if (max_bulk > sc->data_rem) {
41412bd3c8bSSascha Wildner 			max_bulk = sc->data_rem;
41512bd3c8bSSascha Wildner 		}
41612bd3c8bSSascha Wildner 		usbd_xfer_set_timeout(xfer, sc->data_timeout);
41712bd3c8bSSascha Wildner 		usbd_xfer_set_frame_data(xfer, 0, sc->data_ptr, max_bulk);
41812bd3c8bSSascha Wildner 		usbd_transfer_submit(xfer);
41957bed822SMarkus Pfeiffer 		break;
42012bd3c8bSSascha Wildner 
42112bd3c8bSSascha Wildner 	default:			/* Error */
42212bd3c8bSSascha Wildner 		if (error == USB_ERR_CANCELLED) {
42312bd3c8bSSascha Wildner 			bbb_done(sc, error);
42412bd3c8bSSascha Wildner 		} else {
42512bd3c8bSSascha Wildner 			bbb_transfer_start(sc, ST_DATA_WR_CS);
42612bd3c8bSSascha Wildner 		}
42757bed822SMarkus Pfeiffer 		break;
42812bd3c8bSSascha Wildner 	}
42912bd3c8bSSascha Wildner }
43012bd3c8bSSascha Wildner 
43112bd3c8bSSascha Wildner static void
bbb_data_wr_cs_callback(struct usb_xfer * xfer,usb_error_t error)43212bd3c8bSSascha Wildner bbb_data_wr_cs_callback(struct usb_xfer *xfer, usb_error_t error)
43312bd3c8bSSascha Wildner {
43412bd3c8bSSascha Wildner 	bbb_data_clear_stall_callback(xfer, ST_STATUS,
43512bd3c8bSSascha Wildner 	    ST_DATA_WR);
43612bd3c8bSSascha Wildner }
43712bd3c8bSSascha Wildner 
43812bd3c8bSSascha Wildner static void
bbb_status_callback(struct usb_xfer * xfer,usb_error_t error)43912bd3c8bSSascha Wildner bbb_status_callback(struct usb_xfer *xfer, usb_error_t error)
44012bd3c8bSSascha Wildner {
44112bd3c8bSSascha Wildner 	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
44257bed822SMarkus Pfeiffer 	int actlen;
44357bed822SMarkus Pfeiffer 	int sumlen;
44412bd3c8bSSascha Wildner 
44512bd3c8bSSascha Wildner 	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
44612bd3c8bSSascha Wildner 
44712bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
44812bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
44912bd3c8bSSascha Wildner 
45012bd3c8bSSascha Wildner 		/* very simple status check */
45112bd3c8bSSascha Wildner 
45257bed822SMarkus Pfeiffer 		if (actlen < (int)sizeof(struct bbb_csw)) {
45312bd3c8bSSascha Wildner 			bbb_done(sc, USB_ERR_SHORT_XFER);
45457bed822SMarkus Pfeiffer 		} else if (sc->csw->bCSWStatus == CSWSTATUS_GOOD) {
45512bd3c8bSSascha Wildner 			bbb_done(sc, 0);	/* success */
45612bd3c8bSSascha Wildner 		} else {
45712bd3c8bSSascha Wildner 			bbb_done(sc, ERR_CSW_FAILED);	/* error */
45812bd3c8bSSascha Wildner 		}
45912bd3c8bSSascha Wildner 		break;
46012bd3c8bSSascha Wildner 
46112bd3c8bSSascha Wildner 	case USB_ST_SETUP:
46257bed822SMarkus Pfeiffer 		usbd_xfer_set_frame_len(xfer, 0,
46357bed822SMarkus Pfeiffer 		    sizeof(struct bbb_csw));
46412bd3c8bSSascha Wildner 		usbd_transfer_submit(xfer);
46512bd3c8bSSascha Wildner 		break;
46612bd3c8bSSascha Wildner 
46712bd3c8bSSascha Wildner 	default:
46812bd3c8bSSascha Wildner 		DPRINTF("Failed to read CSW: %s, try %d\n",
46912bd3c8bSSascha Wildner 		    usbd_errstr(error), sc->status_try);
47012bd3c8bSSascha Wildner 
47112bd3c8bSSascha Wildner 		if (error == USB_ERR_CANCELLED || sc->status_try) {
47212bd3c8bSSascha Wildner 			bbb_done(sc, error);
47312bd3c8bSSascha Wildner 		} else {
47412bd3c8bSSascha Wildner 			sc->status_try = 1;
47512bd3c8bSSascha Wildner 			bbb_transfer_start(sc, ST_DATA_RD_CS);
47612bd3c8bSSascha Wildner 		}
47712bd3c8bSSascha Wildner 		break;
47812bd3c8bSSascha Wildner 	}
47912bd3c8bSSascha Wildner }
48012bd3c8bSSascha Wildner 
481520083baSSascha Wildner static void
bbb_raw_write_callback(struct usb_xfer * xfer,usb_error_t error)482520083baSSascha Wildner bbb_raw_write_callback(struct usb_xfer *xfer, usb_error_t error)
483520083baSSascha Wildner {
484520083baSSascha Wildner 	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
485520083baSSascha Wildner 	usb_frlength_t max_bulk = usbd_xfer_max_len(xfer);
486520083baSSascha Wildner 	int actlen, sumlen;
487520083baSSascha Wildner 
488520083baSSascha Wildner 	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
489520083baSSascha Wildner 
490520083baSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
491520083baSSascha Wildner 	case USB_ST_TRANSFERRED:
492520083baSSascha Wildner 		sc->data_rem -= actlen;
493520083baSSascha Wildner 		sc->data_ptr += actlen;
494520083baSSascha Wildner 		sc->actlen += actlen;
495520083baSSascha Wildner 
496520083baSSascha Wildner 		if (actlen < sumlen) {
497520083baSSascha Wildner 			/* short transfer */
498520083baSSascha Wildner 			sc->data_rem = 0;
499520083baSSascha Wildner 		}
500520083baSSascha Wildner 	case USB_ST_SETUP:
501520083baSSascha Wildner 		DPRINTF("max_bulk=%d, data_rem=%d\n",
502520083baSSascha Wildner 		    max_bulk, sc->data_rem);
503520083baSSascha Wildner 
504520083baSSascha Wildner 		if (sc->data_rem == 0) {
505520083baSSascha Wildner 			bbb_done(sc, 0);
506520083baSSascha Wildner 			break;
507520083baSSascha Wildner 		}
508520083baSSascha Wildner 		if (max_bulk > sc->data_rem) {
509520083baSSascha Wildner 			max_bulk = sc->data_rem;
510520083baSSascha Wildner 		}
511520083baSSascha Wildner 		usbd_xfer_set_timeout(xfer, sc->data_timeout);
512520083baSSascha Wildner 		usbd_xfer_set_frame_data(xfer, 0, sc->data_ptr, max_bulk);
513520083baSSascha Wildner 		usbd_transfer_submit(xfer);
514520083baSSascha Wildner 		break;
515520083baSSascha Wildner 
516520083baSSascha Wildner 	default:			/* Error */
517520083baSSascha Wildner 		bbb_done(sc, error);
518520083baSSascha Wildner 		break;
519520083baSSascha Wildner 	}
520520083baSSascha Wildner }
521520083baSSascha Wildner 
52212bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
52312bd3c8bSSascha Wildner  *	bbb_command_start - execute a SCSI command synchronously
52412bd3c8bSSascha Wildner  *
52512bd3c8bSSascha Wildner  * Return values
52612bd3c8bSSascha Wildner  * 0: Success
52712bd3c8bSSascha Wildner  * Else: Failure
52812bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
52912bd3c8bSSascha Wildner static int
bbb_command_start(struct bbb_transfer * sc,uint8_t dir,uint8_t lun,void * data_ptr,size_t data_len,void * cmd_ptr,size_t cmd_len,usb_timeout_t data_timeout)53012bd3c8bSSascha Wildner bbb_command_start(struct bbb_transfer *sc, uint8_t dir, uint8_t lun,
53112bd3c8bSSascha Wildner     void *data_ptr, size_t data_len, void *cmd_ptr, size_t cmd_len,
53212bd3c8bSSascha Wildner     usb_timeout_t data_timeout)
53312bd3c8bSSascha Wildner {
53412bd3c8bSSascha Wildner 	sc->lun = lun;
53512bd3c8bSSascha Wildner 	sc->dir = data_len ? dir : DIR_NONE;
53612bd3c8bSSascha Wildner 	sc->data_ptr = data_ptr;
53712bd3c8bSSascha Wildner 	sc->data_len = data_len;
53812bd3c8bSSascha Wildner 	sc->data_rem = data_len;
53912bd3c8bSSascha Wildner 	sc->data_timeout = (data_timeout + USB_MS_HZ);
54012bd3c8bSSascha Wildner 	sc->actlen = 0;
5418930c119SMarkus Pfeiffer 	sc->error = 0;
54212bd3c8bSSascha Wildner 	sc->cmd_len = cmd_len;
54357bed822SMarkus Pfeiffer 	memset(&sc->cbw->CBWCDB, 0, sizeof(sc->cbw->CBWCDB));
54457bed822SMarkus Pfeiffer 	memcpy(&sc->cbw->CBWCDB, cmd_ptr, cmd_len);
54570ccf487SJohannes Hofmann 	DPRINTFN(1, "SCSI cmd_len = %d, cmd %s\n", (int)cmd_len, (char *)sc->cbw->CBWCDB);
54657bed822SMarkus Pfeiffer 
547722d05c3SSascha Wildner 	lockmgr(&sc->lock, LK_EXCLUSIVE);
54812bd3c8bSSascha Wildner 	usbd_transfer_start(sc->xfer[sc->state]);
54912bd3c8bSSascha Wildner 
55012bd3c8bSSascha Wildner 	while (usbd_transfer_pending(sc->xfer[sc->state])) {
551722d05c3SSascha Wildner 		cv_wait(&sc->cv, &sc->lock);
55212bd3c8bSSascha Wildner 	}
553722d05c3SSascha Wildner 	lockmgr(&sc->lock, LK_RELEASE);
55412bd3c8bSSascha Wildner 	return (sc->error);
55512bd3c8bSSascha Wildner }
55612bd3c8bSSascha Wildner 
557520083baSSascha Wildner /*------------------------------------------------------------------------*
558520083baSSascha Wildner  *	bbb_raw_write - write a raw BULK message synchronously
559520083baSSascha Wildner  *
560520083baSSascha Wildner  * Return values
561520083baSSascha Wildner  * 0: Success
562520083baSSascha Wildner  * Else: Failure
563520083baSSascha Wildner  *------------------------------------------------------------------------*/
564520083baSSascha Wildner static int
bbb_raw_write(struct bbb_transfer * sc,const void * data_ptr,size_t data_len,usb_timeout_t data_timeout)565520083baSSascha Wildner bbb_raw_write(struct bbb_transfer *sc, const void *data_ptr, size_t data_len,
566520083baSSascha Wildner     usb_timeout_t data_timeout)
567520083baSSascha Wildner {
568520083baSSascha Wildner 	sc->data_ptr = __DECONST(void *, data_ptr);
569520083baSSascha Wildner 	sc->data_len = data_len;
570520083baSSascha Wildner 	sc->data_rem = data_len;
571520083baSSascha Wildner 	sc->data_timeout = (data_timeout + USB_MS_HZ);
572520083baSSascha Wildner 	sc->actlen = 0;
573520083baSSascha Wildner 	sc->error = 0;
574520083baSSascha Wildner 
575520083baSSascha Wildner //	DPRINTFN(1, "BULK DATA = %*D\n", (int)data_len,
576520083baSSascha Wildner //	    (const char *)data_ptr, ":");
577520083baSSascha Wildner 
578520083baSSascha Wildner 	lockmgr(&sc->lock, LK_EXCLUSIVE);
579520083baSSascha Wildner 	usbd_transfer_start(sc->xfer[0]);
580520083baSSascha Wildner 	while (usbd_transfer_pending(sc->xfer[0]))
581520083baSSascha Wildner 		cv_wait(&sc->cv, &sc->lock);
582520083baSSascha Wildner 	lockmgr(&sc->lock, LK_RELEASE);
583520083baSSascha Wildner 	return (sc->error);
584520083baSSascha Wildner }
585520083baSSascha Wildner 
58612bd3c8bSSascha Wildner static struct bbb_transfer *
bbb_attach(struct usb_device * udev,uint8_t iface_index,uint8_t bInterfaceClass)587520083baSSascha Wildner bbb_attach(struct usb_device *udev, uint8_t iface_index,
588520083baSSascha Wildner     uint8_t bInterfaceClass)
58912bd3c8bSSascha Wildner {
59012bd3c8bSSascha Wildner 	struct usb_interface *iface;
59112bd3c8bSSascha Wildner 	struct usb_interface_descriptor *id;
592520083baSSascha Wildner 	const struct usb_config *pconfig;
59312bd3c8bSSascha Wildner 	struct bbb_transfer *sc;
59412bd3c8bSSascha Wildner 	usb_error_t err;
595520083baSSascha Wildner 	int nconfig;
5968930c119SMarkus Pfeiffer 
5978930c119SMarkus Pfeiffer #if USB_HAVE_MSCTEST_DETACH
59812bd3c8bSSascha Wildner 	uint8_t do_unlock;
59912bd3c8bSSascha Wildner 
60057bed822SMarkus Pfeiffer 	/* Prevent re-enumeration */
60157bed822SMarkus Pfeiffer 	do_unlock = usbd_enum_lock(udev);
60212bd3c8bSSascha Wildner 
60312bd3c8bSSascha Wildner 	/*
60412bd3c8bSSascha Wildner 	 * Make sure any driver which is hooked up to this interface,
60512bd3c8bSSascha Wildner 	 * like umass is gone:
60612bd3c8bSSascha Wildner 	 */
60712bd3c8bSSascha Wildner 	usb_detach_device(udev, iface_index, 0);
60812bd3c8bSSascha Wildner 
60912bd3c8bSSascha Wildner 	if (do_unlock)
61012bd3c8bSSascha Wildner 		usbd_enum_unlock(udev);
6118930c119SMarkus Pfeiffer #endif
61212bd3c8bSSascha Wildner 
61312bd3c8bSSascha Wildner 	iface = usbd_get_iface(udev, iface_index);
61412bd3c8bSSascha Wildner 	if (iface == NULL)
61512bd3c8bSSascha Wildner 		return (NULL);
61612bd3c8bSSascha Wildner 
61712bd3c8bSSascha Wildner 	id = iface->idesc;
618520083baSSascha Wildner 	if (id == NULL || id->bInterfaceClass != bInterfaceClass)
61912bd3c8bSSascha Wildner 		return (NULL);
62012bd3c8bSSascha Wildner 
621520083baSSascha Wildner 	switch (id->bInterfaceClass) {
622520083baSSascha Wildner 	case UICLASS_MASS:
62312bd3c8bSSascha Wildner 		switch (id->bInterfaceSubClass) {
62412bd3c8bSSascha Wildner 		case UISUBCLASS_SCSI:
62512bd3c8bSSascha Wildner 		case UISUBCLASS_UFI:
62612bd3c8bSSascha Wildner 		case UISUBCLASS_SFF8020I:
62712bd3c8bSSascha Wildner 		case UISUBCLASS_SFF8070I:
62812bd3c8bSSascha Wildner 			break;
62912bd3c8bSSascha Wildner 		default:
63012bd3c8bSSascha Wildner 			return (NULL);
63112bd3c8bSSascha Wildner 		}
63212bd3c8bSSascha Wildner 		switch (id->bInterfaceProtocol) {
63312bd3c8bSSascha Wildner 		case UIPROTO_MASS_BBB_OLD:
63412bd3c8bSSascha Wildner 		case UIPROTO_MASS_BBB:
63512bd3c8bSSascha Wildner 			break;
63612bd3c8bSSascha Wildner 		default:
63712bd3c8bSSascha Wildner 			return (NULL);
63812bd3c8bSSascha Wildner 		}
639520083baSSascha Wildner 		pconfig = bbb_config;
640520083baSSascha Wildner 		nconfig = ST_MAX;
641520083baSSascha Wildner 		break;
642520083baSSascha Wildner 	case UICLASS_HID:
643520083baSSascha Wildner 		switch (id->bInterfaceSubClass) {
644520083baSSascha Wildner 		case 0:
645520083baSSascha Wildner 			break;
646520083baSSascha Wildner 		default:
647520083baSSascha Wildner 			return (NULL);
648520083baSSascha Wildner 		}
649520083baSSascha Wildner 		pconfig = bbb_raw_config;
650520083baSSascha Wildner 		nconfig = 1;
651520083baSSascha Wildner 		break;
652520083baSSascha Wildner 	default:
653520083baSSascha Wildner 		return (NULL);
654520083baSSascha Wildner 	}
65512bd3c8bSSascha Wildner 
656722d05c3SSascha Wildner 	sc = kmalloc(sizeof(*sc), M_USB, M_WAITOK | M_ZERO);
657722d05c3SSascha Wildner 	lockinit(&sc->lock, "USB autoinstall", 0, 0);
65812bd3c8bSSascha Wildner 	cv_init(&sc->cv, "WBBB");
65912bd3c8bSSascha Wildner 
660520083baSSascha Wildner 	err = usbd_transfer_setup(udev, &iface_index, sc->xfer, pconfig,
661520083baSSascha Wildner 	    nconfig, sc, &sc->lock);
66212bd3c8bSSascha Wildner 	if (err) {
66312bd3c8bSSascha Wildner 		bbb_detach(sc);
66412bd3c8bSSascha Wildner 		return (NULL);
66512bd3c8bSSascha Wildner 	}
666520083baSSascha Wildner 	switch (id->bInterfaceClass) {
667520083baSSascha Wildner 	case UICLASS_MASS:
66857bed822SMarkus Pfeiffer 		/* store pointer to DMA buffers */
66957bed822SMarkus Pfeiffer 		sc->buffer = usbd_xfer_get_frame_buffer(
67057bed822SMarkus Pfeiffer 		    sc->xfer[ST_DATA_RD], 0);
67157bed822SMarkus Pfeiffer 		sc->buffer_size =
67257bed822SMarkus Pfeiffer 		    usbd_xfer_max_len(sc->xfer[ST_DATA_RD]);
67357bed822SMarkus Pfeiffer 		sc->cbw = usbd_xfer_get_frame_buffer(
67457bed822SMarkus Pfeiffer 		    sc->xfer[ST_COMMAND], 0);
67557bed822SMarkus Pfeiffer 		sc->csw = usbd_xfer_get_frame_buffer(
67657bed822SMarkus Pfeiffer 		    sc->xfer[ST_STATUS], 0);
677520083baSSascha Wildner 		break;
678520083baSSascha Wildner 	default:
679520083baSSascha Wildner 		break;
680520083baSSascha Wildner 	}
68112bd3c8bSSascha Wildner 	return (sc);
68212bd3c8bSSascha Wildner }
68312bd3c8bSSascha Wildner 
68412bd3c8bSSascha Wildner static void
bbb_detach(struct bbb_transfer * sc)68512bd3c8bSSascha Wildner bbb_detach(struct bbb_transfer *sc)
68612bd3c8bSSascha Wildner {
68712bd3c8bSSascha Wildner 	usbd_transfer_unsetup(sc->xfer, ST_MAX);
688722d05c3SSascha Wildner 	lockuninit(&sc->lock);
68912bd3c8bSSascha Wildner 	cv_destroy(&sc->cv);
690722d05c3SSascha Wildner 	kfree(sc, M_USB);
69112bd3c8bSSascha Wildner }
69212bd3c8bSSascha Wildner 
69312bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
69412bd3c8bSSascha Wildner  *	usb_iface_is_cdrom
69512bd3c8bSSascha Wildner  *
69612bd3c8bSSascha Wildner  * Return values:
69712bd3c8bSSascha Wildner  * 1: This interface is an auto install disk (CD-ROM)
69812bd3c8bSSascha Wildner  * 0: Not an auto install disk.
69912bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
70012bd3c8bSSascha Wildner int
usb_iface_is_cdrom(struct usb_device * udev,uint8_t iface_index)70112bd3c8bSSascha Wildner usb_iface_is_cdrom(struct usb_device *udev, uint8_t iface_index)
70212bd3c8bSSascha Wildner {
70312bd3c8bSSascha Wildner 	struct bbb_transfer *sc;
70412bd3c8bSSascha Wildner 	uint8_t timeout;
70512bd3c8bSSascha Wildner 	uint8_t is_cdrom;
70612bd3c8bSSascha Wildner 	uint8_t sid_type;
70712bd3c8bSSascha Wildner 	int err;
70812bd3c8bSSascha Wildner 
709520083baSSascha Wildner 	sc = bbb_attach(udev, iface_index, UICLASS_MASS);
71012bd3c8bSSascha Wildner 	if (sc == NULL)
71112bd3c8bSSascha Wildner 		return (0);
71212bd3c8bSSascha Wildner 
71312bd3c8bSSascha Wildner 	is_cdrom = 0;
71412bd3c8bSSascha Wildner 	timeout = 4;	/* tries */
71512bd3c8bSSascha Wildner 	while (--timeout) {
71612bd3c8bSSascha Wildner 		err = bbb_command_start(sc, DIR_IN, 0, sc->buffer,
71712bd3c8bSSascha Wildner 		    SCSI_INQ_LEN, &scsi_inquiry, sizeof(scsi_inquiry),
71812bd3c8bSSascha Wildner 		    USB_MS_HZ);
71912bd3c8bSSascha Wildner 
72012bd3c8bSSascha Wildner 		if (err == 0 && sc->actlen > 0) {
72112bd3c8bSSascha Wildner 			sid_type = sc->buffer[0] & 0x1F;
72212bd3c8bSSascha Wildner 			if (sid_type == 0x05)
72312bd3c8bSSascha Wildner 				is_cdrom = 1;
72412bd3c8bSSascha Wildner 			break;
72512bd3c8bSSascha Wildner 		} else if (err != ERR_CSW_FAILED)
72612bd3c8bSSascha Wildner 			break;	/* non retryable error */
72712bd3c8bSSascha Wildner 		usb_pause_mtx(NULL, hz);
72812bd3c8bSSascha Wildner 	}
72912bd3c8bSSascha Wildner 	bbb_detach(sc);
73012bd3c8bSSascha Wildner 	return (is_cdrom);
73112bd3c8bSSascha Wildner }
73212bd3c8bSSascha Wildner 
73312bd3c8bSSascha Wildner static uint8_t
usb_msc_get_max_lun(struct usb_device * udev,uint8_t iface_index)73412bd3c8bSSascha Wildner usb_msc_get_max_lun(struct usb_device *udev, uint8_t iface_index)
73512bd3c8bSSascha Wildner {
73612bd3c8bSSascha Wildner 	struct usb_device_request req;
73712bd3c8bSSascha Wildner 	usb_error_t err;
73812bd3c8bSSascha Wildner 	uint8_t buf = 0;
73912bd3c8bSSascha Wildner 
74012bd3c8bSSascha Wildner 
74112bd3c8bSSascha Wildner 	/* The Get Max Lun command is a class-specific request. */
74212bd3c8bSSascha Wildner 	req.bmRequestType = UT_READ_CLASS_INTERFACE;
74312bd3c8bSSascha Wildner 	req.bRequest = 0xFE;		/* GET_MAX_LUN */
74412bd3c8bSSascha Wildner 	USETW(req.wValue, 0);
74512bd3c8bSSascha Wildner 	req.wIndex[0] = iface_index;
74612bd3c8bSSascha Wildner 	req.wIndex[1] = 0;
74712bd3c8bSSascha Wildner 	USETW(req.wLength, 1);
74812bd3c8bSSascha Wildner 
74912bd3c8bSSascha Wildner 	err = usbd_do_request(udev, NULL, &req, &buf);
75012bd3c8bSSascha Wildner 	if (err)
75112bd3c8bSSascha Wildner 		buf = 0;
75212bd3c8bSSascha Wildner 
75312bd3c8bSSascha Wildner 	return (buf);
75412bd3c8bSSascha Wildner }
75512bd3c8bSSascha Wildner 
75612bd3c8bSSascha Wildner usb_error_t
usb_msc_auto_quirk(struct usb_device * udev,uint8_t iface_index)75712bd3c8bSSascha Wildner usb_msc_auto_quirk(struct usb_device *udev, uint8_t iface_index)
75812bd3c8bSSascha Wildner {
75912bd3c8bSSascha Wildner 	struct bbb_transfer *sc;
76012bd3c8bSSascha Wildner 	uint8_t timeout;
76112bd3c8bSSascha Wildner 	uint8_t is_no_direct;
76212bd3c8bSSascha Wildner 	uint8_t sid_type;
76312bd3c8bSSascha Wildner 	int err;
76412bd3c8bSSascha Wildner 
765520083baSSascha Wildner 	sc = bbb_attach(udev, iface_index, UICLASS_MASS);
76612bd3c8bSSascha Wildner 	if (sc == NULL)
76712bd3c8bSSascha Wildner 		return (0);
76812bd3c8bSSascha Wildner 
76912bd3c8bSSascha Wildner 	/*
77012bd3c8bSSascha Wildner 	 * Some devices need a delay after that the configuration
77112bd3c8bSSascha Wildner 	 * value is set to function properly:
77212bd3c8bSSascha Wildner 	 */
77312bd3c8bSSascha Wildner 	usb_pause_mtx(NULL, hz);
77412bd3c8bSSascha Wildner 
77512bd3c8bSSascha Wildner 	if (usb_msc_get_max_lun(udev, iface_index) == 0) {
77612bd3c8bSSascha Wildner 		DPRINTF("Device has only got one LUN.\n");
77712bd3c8bSSascha Wildner 		usbd_add_dynamic_quirk(udev, UQ_MSC_NO_GETMAXLUN);
77812bd3c8bSSascha Wildner 	}
77912bd3c8bSSascha Wildner 
78012bd3c8bSSascha Wildner 	is_no_direct = 1;
78157bed822SMarkus Pfeiffer 	for (timeout = 4; timeout != 0; timeout--) {
78212bd3c8bSSascha Wildner 		err = bbb_command_start(sc, DIR_IN, 0, sc->buffer,
78312bd3c8bSSascha Wildner 		    SCSI_INQ_LEN, &scsi_inquiry, sizeof(scsi_inquiry),
78412bd3c8bSSascha Wildner 		    USB_MS_HZ);
78512bd3c8bSSascha Wildner 
78612bd3c8bSSascha Wildner 		if (err == 0 && sc->actlen > 0) {
78712bd3c8bSSascha Wildner 			sid_type = sc->buffer[0] & 0x1F;
78812bd3c8bSSascha Wildner 			if (sid_type == 0x00)
78912bd3c8bSSascha Wildner 				is_no_direct = 0;
79012bd3c8bSSascha Wildner 			break;
79157bed822SMarkus Pfeiffer 		} else if (err != ERR_CSW_FAILED) {
79257bed822SMarkus Pfeiffer 			DPRINTF("Device is not responding "
79357bed822SMarkus Pfeiffer 			    "properly to SCSI INQUIRY command.\n");
79457bed822SMarkus Pfeiffer 			goto error;	/* non retryable error */
79557bed822SMarkus Pfeiffer 		}
79612bd3c8bSSascha Wildner 		usb_pause_mtx(NULL, hz);
79712bd3c8bSSascha Wildner 	}
79812bd3c8bSSascha Wildner 
79912bd3c8bSSascha Wildner 	if (is_no_direct) {
80012bd3c8bSSascha Wildner 		DPRINTF("Device is not direct access.\n");
80112bd3c8bSSascha Wildner 		goto done;
80212bd3c8bSSascha Wildner 	}
80312bd3c8bSSascha Wildner 
80412bd3c8bSSascha Wildner 	err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
80512bd3c8bSSascha Wildner 	    &scsi_test_unit_ready, sizeof(scsi_test_unit_ready),
80612bd3c8bSSascha Wildner 	    USB_MS_HZ);
80712bd3c8bSSascha Wildner 
80812bd3c8bSSascha Wildner 	if (err != 0) {
80912bd3c8bSSascha Wildner 		if (err != ERR_CSW_FAILED)
81012bd3c8bSSascha Wildner 			goto error;
811dd681da6SMatthew Dillon 		DPRINTF("Test unit ready failed\n");
81212bd3c8bSSascha Wildner 	}
813dd681da6SMatthew Dillon 
814dd681da6SMatthew Dillon 	err = bbb_command_start(sc, DIR_OUT, 0, NULL, 0,
815dd681da6SMatthew Dillon 	    &scsi_prevent_removal, sizeof(scsi_prevent_removal),
816dd681da6SMatthew Dillon 	    USB_MS_HZ);
817dd681da6SMatthew Dillon 
818dd681da6SMatthew Dillon 	if (err == 0) {
819dd681da6SMatthew Dillon 		err = bbb_command_start(sc, DIR_OUT, 0, NULL, 0,
820dd681da6SMatthew Dillon 		    &scsi_allow_removal, sizeof(scsi_allow_removal),
821dd681da6SMatthew Dillon 		    USB_MS_HZ);
822dd681da6SMatthew Dillon 	}
823dd681da6SMatthew Dillon 
824dd681da6SMatthew Dillon 	if (err != 0) {
825dd681da6SMatthew Dillon 		if (err != ERR_CSW_FAILED)
826dd681da6SMatthew Dillon 			goto error;
827dd681da6SMatthew Dillon 		DPRINTF("Device doesn't handle prevent and allow removal\n");
828dd681da6SMatthew Dillon 		usbd_add_dynamic_quirk(udev, UQ_MSC_NO_PREVENT_ALLOW);
829dd681da6SMatthew Dillon 	}
830dd681da6SMatthew Dillon 
83157bed822SMarkus Pfeiffer 	timeout = 1;
83212bd3c8bSSascha Wildner 
83357bed822SMarkus Pfeiffer retry_sync_cache:
83412bd3c8bSSascha Wildner 	err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
83512bd3c8bSSascha Wildner 	    &scsi_sync_cache, sizeof(scsi_sync_cache),
83612bd3c8bSSascha Wildner 	    USB_MS_HZ);
83712bd3c8bSSascha Wildner 
83812bd3c8bSSascha Wildner 	if (err != 0) {
83912bd3c8bSSascha Wildner 
84012bd3c8bSSascha Wildner 		if (err != ERR_CSW_FAILED)
84112bd3c8bSSascha Wildner 			goto error;
84212bd3c8bSSascha Wildner 
84312bd3c8bSSascha Wildner 		DPRINTF("Device doesn't handle synchronize cache\n");
84412bd3c8bSSascha Wildner 
84512bd3c8bSSascha Wildner 		usbd_add_dynamic_quirk(udev, UQ_MSC_NO_SYNC_CACHE);
84657bed822SMarkus Pfeiffer 	} else {
84757bed822SMarkus Pfeiffer 
84857bed822SMarkus Pfeiffer 		/*
84957bed822SMarkus Pfeiffer 		 * Certain Kingston memory sticks fail the first
85057bed822SMarkus Pfeiffer 		 * read capacity after a synchronize cache command
85157bed822SMarkus Pfeiffer 		 * has been issued. Disable the synchronize cache
85257bed822SMarkus Pfeiffer 		 * command for such devices.
85357bed822SMarkus Pfeiffer 		 */
85457bed822SMarkus Pfeiffer 
85557bed822SMarkus Pfeiffer 		err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, 8,
85657bed822SMarkus Pfeiffer 		    &scsi_read_capacity, sizeof(scsi_read_capacity),
85757bed822SMarkus Pfeiffer 		    USB_MS_HZ);
85857bed822SMarkus Pfeiffer 
85957bed822SMarkus Pfeiffer 		if (err != 0) {
86057bed822SMarkus Pfeiffer 			if (err != ERR_CSW_FAILED)
86157bed822SMarkus Pfeiffer 				goto error;
86257bed822SMarkus Pfeiffer 
86357bed822SMarkus Pfeiffer 			err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, 8,
86457bed822SMarkus Pfeiffer 			    &scsi_read_capacity, sizeof(scsi_read_capacity),
86557bed822SMarkus Pfeiffer 			    USB_MS_HZ);
86657bed822SMarkus Pfeiffer 
86757bed822SMarkus Pfeiffer 			if (err == 0) {
86857bed822SMarkus Pfeiffer 				if (timeout--)
86957bed822SMarkus Pfeiffer 					goto retry_sync_cache;
87057bed822SMarkus Pfeiffer 
87157bed822SMarkus Pfeiffer 				DPRINTF("Device most likely doesn't "
87257bed822SMarkus Pfeiffer 				    "handle synchronize cache\n");
87357bed822SMarkus Pfeiffer 
87457bed822SMarkus Pfeiffer 				usbd_add_dynamic_quirk(udev,
87557bed822SMarkus Pfeiffer 				    UQ_MSC_NO_SYNC_CACHE);
87657bed822SMarkus Pfeiffer 			} else {
87757bed822SMarkus Pfeiffer 				if (err != ERR_CSW_FAILED)
87857bed822SMarkus Pfeiffer 					goto error;
87957bed822SMarkus Pfeiffer 			}
88057bed822SMarkus Pfeiffer 		}
88112bd3c8bSSascha Wildner 	}
88212bd3c8bSSascha Wildner 
88312bd3c8bSSascha Wildner 	/* clear sense status of any failed commands on the device */
88412bd3c8bSSascha Wildner 
88512bd3c8bSSascha Wildner 	err = bbb_command_start(sc, DIR_IN, 0, sc->buffer,
88612bd3c8bSSascha Wildner 	    SCSI_INQ_LEN, &scsi_inquiry, sizeof(scsi_inquiry),
88712bd3c8bSSascha Wildner 	    USB_MS_HZ);
88812bd3c8bSSascha Wildner 
88912bd3c8bSSascha Wildner 	DPRINTF("Inquiry = %d\n", err);
89012bd3c8bSSascha Wildner 
89112bd3c8bSSascha Wildner 	if (err != 0) {
89212bd3c8bSSascha Wildner 
89312bd3c8bSSascha Wildner 		if (err != ERR_CSW_FAILED)
89412bd3c8bSSascha Wildner 			goto error;
89512bd3c8bSSascha Wildner 	}
89612bd3c8bSSascha Wildner 
89712bd3c8bSSascha Wildner 	err = bbb_command_start(sc, DIR_IN, 0, sc->buffer,
89812bd3c8bSSascha Wildner 	    SCSI_SENSE_LEN, &scsi_request_sense,
89912bd3c8bSSascha Wildner 	    sizeof(scsi_request_sense), USB_MS_HZ);
90012bd3c8bSSascha Wildner 
90112bd3c8bSSascha Wildner 	DPRINTF("Request sense = %d\n", err);
90212bd3c8bSSascha Wildner 
90312bd3c8bSSascha Wildner 	if (err != 0) {
90412bd3c8bSSascha Wildner 
90512bd3c8bSSascha Wildner 		if (err != ERR_CSW_FAILED)
90612bd3c8bSSascha Wildner 			goto error;
90712bd3c8bSSascha Wildner 	}
90812bd3c8bSSascha Wildner 
90912bd3c8bSSascha Wildner done:
91012bd3c8bSSascha Wildner 	bbb_detach(sc);
91112bd3c8bSSascha Wildner 	return (0);
91212bd3c8bSSascha Wildner 
91312bd3c8bSSascha Wildner error:
91412bd3c8bSSascha Wildner  	bbb_detach(sc);
91512bd3c8bSSascha Wildner 
91612bd3c8bSSascha Wildner 	DPRINTF("Device did not respond, enabling all quirks\n");
91712bd3c8bSSascha Wildner 
91812bd3c8bSSascha Wildner 	usbd_add_dynamic_quirk(udev, UQ_MSC_NO_SYNC_CACHE);
919dd681da6SMatthew Dillon 	usbd_add_dynamic_quirk(udev, UQ_MSC_NO_PREVENT_ALLOW);
92012bd3c8bSSascha Wildner 	usbd_add_dynamic_quirk(udev, UQ_MSC_NO_TEST_UNIT_READY);
92112bd3c8bSSascha Wildner 
92212bd3c8bSSascha Wildner 	/* Need to re-enumerate the device */
92312bd3c8bSSascha Wildner 	usbd_req_re_enumerate(udev, NULL);
92412bd3c8bSSascha Wildner 
92512bd3c8bSSascha Wildner 	return (USB_ERR_STALLED);
92612bd3c8bSSascha Wildner }
92712bd3c8bSSascha Wildner 
92812bd3c8bSSascha Wildner usb_error_t
usb_msc_eject(struct usb_device * udev,uint8_t iface_index,int method)92912bd3c8bSSascha Wildner usb_msc_eject(struct usb_device *udev, uint8_t iface_index, int method)
93012bd3c8bSSascha Wildner {
93112bd3c8bSSascha Wildner 	struct bbb_transfer *sc;
93212bd3c8bSSascha Wildner 	usb_error_t err;
93312bd3c8bSSascha Wildner 
934520083baSSascha Wildner 	sc = bbb_attach(udev, iface_index, UICLASS_MASS);
93512bd3c8bSSascha Wildner 	if (sc == NULL)
93612bd3c8bSSascha Wildner 		return (USB_ERR_INVAL);
93712bd3c8bSSascha Wildner 
93812bd3c8bSSascha Wildner 	switch (method) {
93912bd3c8bSSascha Wildner 	case MSC_EJECT_STOPUNIT:
94012bd3c8bSSascha Wildner 		err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
94112bd3c8bSSascha Wildner 		    &scsi_test_unit_ready, sizeof(scsi_test_unit_ready),
94212bd3c8bSSascha Wildner 		    USB_MS_HZ);
94312bd3c8bSSascha Wildner 		DPRINTF("Test unit ready status: %s\n", usbd_errstr(err));
94412bd3c8bSSascha Wildner 		err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
94512bd3c8bSSascha Wildner 		    &scsi_start_stop_unit, sizeof(scsi_start_stop_unit),
94612bd3c8bSSascha Wildner 		    USB_MS_HZ);
94712bd3c8bSSascha Wildner 		break;
94812bd3c8bSSascha Wildner 	case MSC_EJECT_REZERO:
94912bd3c8bSSascha Wildner 		err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
95012bd3c8bSSascha Wildner 		    &scsi_rezero_init, sizeof(scsi_rezero_init),
95112bd3c8bSSascha Wildner 		    USB_MS_HZ);
95212bd3c8bSSascha Wildner 		break;
95312bd3c8bSSascha Wildner 	case MSC_EJECT_ZTESTOR:
95412bd3c8bSSascha Wildner 		err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
95512bd3c8bSSascha Wildner 		    &scsi_ztestor_eject, sizeof(scsi_ztestor_eject),
95612bd3c8bSSascha Wildner 		    USB_MS_HZ);
95712bd3c8bSSascha Wildner 		break;
95812bd3c8bSSascha Wildner 	case MSC_EJECT_CMOTECH:
95912bd3c8bSSascha Wildner 		err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
96012bd3c8bSSascha Wildner 		    &scsi_cmotech_eject, sizeof(scsi_cmotech_eject),
96112bd3c8bSSascha Wildner 		    USB_MS_HZ);
96212bd3c8bSSascha Wildner 		break;
96312bd3c8bSSascha Wildner 	case MSC_EJECT_HUAWEI:
96412bd3c8bSSascha Wildner 		err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
96512bd3c8bSSascha Wildner 		    &scsi_huawei_eject, sizeof(scsi_huawei_eject),
96612bd3c8bSSascha Wildner 		    USB_MS_HZ);
96712bd3c8bSSascha Wildner 		break;
9688930c119SMarkus Pfeiffer 	case MSC_EJECT_HUAWEI2:
9698930c119SMarkus Pfeiffer 		err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
9708930c119SMarkus Pfeiffer 		    &scsi_huawei_eject2, sizeof(scsi_huawei_eject2),
9718930c119SMarkus Pfeiffer 		    USB_MS_HZ);
9728930c119SMarkus Pfeiffer 		break;
97312bd3c8bSSascha Wildner 	case MSC_EJECT_TCT:
97412bd3c8bSSascha Wildner 		/*
97512bd3c8bSSascha Wildner 		 * TCTMobile needs DIR_IN flag. To get it, we
97612bd3c8bSSascha Wildner 		 * supply a dummy data with the command.
97712bd3c8bSSascha Wildner 		 */
97857bed822SMarkus Pfeiffer 		err = bbb_command_start(sc, DIR_IN, 0, sc->buffer,
97957bed822SMarkus Pfeiffer 		    sc->buffer_size, &scsi_tct_eject,
98012bd3c8bSSascha Wildner 		    sizeof(scsi_tct_eject), USB_MS_HZ);
98112bd3c8bSSascha Wildner 		break;
98212bd3c8bSSascha Wildner 	default:
9838922de18SMarkus Pfeiffer 		DPRINTF("Unknown eject method (%d)\n", method);
9848930c119SMarkus Pfeiffer 		bbb_detach(sc);
9858930c119SMarkus Pfeiffer 		return (USB_ERR_INVAL);
98612bd3c8bSSascha Wildner 	}
9878930c119SMarkus Pfeiffer 
98812bd3c8bSSascha Wildner 	DPRINTF("Eject CD command status: %s\n", usbd_errstr(err));
98912bd3c8bSSascha Wildner 
99012bd3c8bSSascha Wildner 	bbb_detach(sc);
99112bd3c8bSSascha Wildner 	return (0);
99212bd3c8bSSascha Wildner }
9938930c119SMarkus Pfeiffer 
9948930c119SMarkus Pfeiffer usb_error_t
usb_dymo_eject(struct usb_device * udev,uint8_t iface_index)995520083baSSascha Wildner usb_dymo_eject(struct usb_device *udev, uint8_t iface_index)
996520083baSSascha Wildner {
997520083baSSascha Wildner 	static const uint8_t data[3] = { 0x1b, 0x5a, 0x01 };
998520083baSSascha Wildner 	struct bbb_transfer *sc;
999520083baSSascha Wildner 	usb_error_t err;
1000520083baSSascha Wildner 
1001520083baSSascha Wildner 	sc = bbb_attach(udev, iface_index, UICLASS_HID);
1002520083baSSascha Wildner 	if (sc == NULL)
1003520083baSSascha Wildner 		return (USB_ERR_INVAL);
1004520083baSSascha Wildner 	err = bbb_raw_write(sc, data, sizeof(data), USB_MS_HZ);
1005520083baSSascha Wildner 	bbb_detach(sc);
1006520083baSSascha Wildner 	return (err);
1007520083baSSascha Wildner }
1008520083baSSascha Wildner 
1009520083baSSascha Wildner usb_error_t
usb_msc_read_10(struct usb_device * udev,uint8_t iface_index,uint32_t lba,uint32_t blocks,void * buffer)10108930c119SMarkus Pfeiffer usb_msc_read_10(struct usb_device *udev, uint8_t iface_index,
10118930c119SMarkus Pfeiffer     uint32_t lba, uint32_t blocks, void *buffer)
10128930c119SMarkus Pfeiffer {
10138930c119SMarkus Pfeiffer 	struct bbb_transfer *sc;
10148930c119SMarkus Pfeiffer 	uint8_t cmd[10];
10158930c119SMarkus Pfeiffer 	usb_error_t err;
10168930c119SMarkus Pfeiffer 
10178930c119SMarkus Pfeiffer 	cmd[0] = 0x28;		/* READ_10 */
10188930c119SMarkus Pfeiffer 	cmd[1] = 0;
10198930c119SMarkus Pfeiffer 	cmd[2] = lba >> 24;
10208930c119SMarkus Pfeiffer 	cmd[3] = lba >> 16;
10218930c119SMarkus Pfeiffer 	cmd[4] = lba >> 8;
10228930c119SMarkus Pfeiffer 	cmd[5] = lba >> 0;
10238930c119SMarkus Pfeiffer 	cmd[6] = 0;
10248930c119SMarkus Pfeiffer 	cmd[7] = blocks >> 8;
10258930c119SMarkus Pfeiffer 	cmd[8] = blocks;
10268930c119SMarkus Pfeiffer 	cmd[9] = 0;
10278930c119SMarkus Pfeiffer 
1028520083baSSascha Wildner 	sc = bbb_attach(udev, iface_index, UICLASS_MASS);
10298930c119SMarkus Pfeiffer 	if (sc == NULL)
10308930c119SMarkus Pfeiffer 		return (USB_ERR_INVAL);
10318930c119SMarkus Pfeiffer 
10328930c119SMarkus Pfeiffer 	err = bbb_command_start(sc, DIR_IN, 0, buffer,
10338930c119SMarkus Pfeiffer 	    blocks * SCSI_FIXED_BLOCK_SIZE, cmd, 10, USB_MS_HZ);
10348930c119SMarkus Pfeiffer 
10358930c119SMarkus Pfeiffer 	bbb_detach(sc);
10368930c119SMarkus Pfeiffer 
10378930c119SMarkus Pfeiffer 	return (err);
10388930c119SMarkus Pfeiffer }
10398930c119SMarkus Pfeiffer 
10408930c119SMarkus Pfeiffer usb_error_t
usb_msc_write_10(struct usb_device * udev,uint8_t iface_index,uint32_t lba,uint32_t blocks,void * buffer)10418930c119SMarkus Pfeiffer usb_msc_write_10(struct usb_device *udev, uint8_t iface_index,
10428930c119SMarkus Pfeiffer     uint32_t lba, uint32_t blocks, void *buffer)
10438930c119SMarkus Pfeiffer {
10448930c119SMarkus Pfeiffer 	struct bbb_transfer *sc;
10458930c119SMarkus Pfeiffer 	uint8_t cmd[10];
10468930c119SMarkus Pfeiffer 	usb_error_t err;
10478930c119SMarkus Pfeiffer 
10488930c119SMarkus Pfeiffer 	cmd[0] = 0x2a;		/* WRITE_10 */
10498930c119SMarkus Pfeiffer 	cmd[1] = 0;
10508930c119SMarkus Pfeiffer 	cmd[2] = lba >> 24;
10518930c119SMarkus Pfeiffer 	cmd[3] = lba >> 16;
10528930c119SMarkus Pfeiffer 	cmd[4] = lba >> 8;
10538930c119SMarkus Pfeiffer 	cmd[5] = lba >> 0;
10548930c119SMarkus Pfeiffer 	cmd[6] = 0;
10558930c119SMarkus Pfeiffer 	cmd[7] = blocks >> 8;
10568930c119SMarkus Pfeiffer 	cmd[8] = blocks;
10578930c119SMarkus Pfeiffer 	cmd[9] = 0;
10588930c119SMarkus Pfeiffer 
1059520083baSSascha Wildner 	sc = bbb_attach(udev, iface_index, UICLASS_MASS);
10608930c119SMarkus Pfeiffer 	if (sc == NULL)
10618930c119SMarkus Pfeiffer 		return (USB_ERR_INVAL);
10628930c119SMarkus Pfeiffer 
10638930c119SMarkus Pfeiffer 	err = bbb_command_start(sc, DIR_OUT, 0, buffer,
10648930c119SMarkus Pfeiffer 	    blocks * SCSI_FIXED_BLOCK_SIZE, cmd, 10, USB_MS_HZ);
10658930c119SMarkus Pfeiffer 
10668930c119SMarkus Pfeiffer 	bbb_detach(sc);
10678930c119SMarkus Pfeiffer 
10688930c119SMarkus Pfeiffer 	return (err);
10698930c119SMarkus Pfeiffer }
10708930c119SMarkus Pfeiffer 
10718930c119SMarkus Pfeiffer usb_error_t
usb_msc_read_capacity(struct usb_device * udev,uint8_t iface_index,uint32_t * lba_last,uint32_t * block_size)10728930c119SMarkus Pfeiffer usb_msc_read_capacity(struct usb_device *udev, uint8_t iface_index,
10738930c119SMarkus Pfeiffer     uint32_t *lba_last, uint32_t *block_size)
10748930c119SMarkus Pfeiffer {
10758930c119SMarkus Pfeiffer 	struct bbb_transfer *sc;
10768930c119SMarkus Pfeiffer 	usb_error_t err;
10778930c119SMarkus Pfeiffer 
1078520083baSSascha Wildner 	sc = bbb_attach(udev, iface_index, UICLASS_MASS);
10798930c119SMarkus Pfeiffer 	if (sc == NULL)
10808930c119SMarkus Pfeiffer 		return (USB_ERR_INVAL);
10818930c119SMarkus Pfeiffer 
10828930c119SMarkus Pfeiffer 	err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, 8,
10838930c119SMarkus Pfeiffer 	    &scsi_read_capacity, sizeof(scsi_read_capacity),
10848930c119SMarkus Pfeiffer 	    USB_MS_HZ);
10858930c119SMarkus Pfeiffer 
10868930c119SMarkus Pfeiffer 	*lba_last =
10878930c119SMarkus Pfeiffer 	    (sc->buffer[0] << 24) |
10888930c119SMarkus Pfeiffer 	    (sc->buffer[1] << 16) |
10898930c119SMarkus Pfeiffer 	    (sc->buffer[2] << 8) |
10908930c119SMarkus Pfeiffer 	    (sc->buffer[3]);
10918930c119SMarkus Pfeiffer 
10928930c119SMarkus Pfeiffer 	*block_size =
10938930c119SMarkus Pfeiffer 	    (sc->buffer[4] << 24) |
10948930c119SMarkus Pfeiffer 	    (sc->buffer[5] << 16) |
10958930c119SMarkus Pfeiffer 	    (sc->buffer[6] << 8) |
10968930c119SMarkus Pfeiffer 	    (sc->buffer[7]);
10978930c119SMarkus Pfeiffer 
10988930c119SMarkus Pfeiffer 	/* we currently only support one block size */
10998930c119SMarkus Pfeiffer 	if (*block_size != SCSI_FIXED_BLOCK_SIZE)
11008930c119SMarkus Pfeiffer 		err = USB_ERR_INVAL;
11018930c119SMarkus Pfeiffer 
11028930c119SMarkus Pfeiffer 	bbb_detach(sc);
11038930c119SMarkus Pfeiffer 
11048930c119SMarkus Pfeiffer 	return (err);
11058930c119SMarkus Pfeiffer }
1106