xref: /freebsd/sys/dev/usb/usb_msctest.c (revision 6d917491f5e4d12a9c833584e66600486ec55a09)
102ac6454SAndrew Thompson /* $FreeBSD$ */
202ac6454SAndrew Thompson /*-
3d334432fSHans Petter Selasky  * Copyright (c) 2008,2011 Hans Petter Selasky. All rights reserved.
402ac6454SAndrew Thompson  *
502ac6454SAndrew Thompson  * Redistribution and use in source and binary forms, with or without
602ac6454SAndrew Thompson  * modification, are permitted provided that the following conditions
702ac6454SAndrew Thompson  * are met:
802ac6454SAndrew Thompson  * 1. Redistributions of source code must retain the above copyright
902ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer.
1002ac6454SAndrew Thompson  * 2. Redistributions in binary form must reproduce the above copyright
1102ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer in the
1202ac6454SAndrew Thompson  *    documentation and/or other materials provided with the distribution.
1302ac6454SAndrew Thompson  *
1402ac6454SAndrew Thompson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1502ac6454SAndrew Thompson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1602ac6454SAndrew Thompson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1702ac6454SAndrew Thompson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1802ac6454SAndrew Thompson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1902ac6454SAndrew Thompson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2002ac6454SAndrew Thompson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2102ac6454SAndrew Thompson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2202ac6454SAndrew Thompson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2302ac6454SAndrew Thompson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2402ac6454SAndrew Thompson  * SUCH DAMAGE.
2502ac6454SAndrew Thompson  */
2602ac6454SAndrew Thompson 
2702ac6454SAndrew Thompson /*
2802ac6454SAndrew Thompson  * The following file contains code that will detect USB autoinstall
2902ac6454SAndrew Thompson  * disks.
3002ac6454SAndrew Thompson  *
3102ac6454SAndrew Thompson  * TODO: Potentially we could add code to automatically detect USB
3202ac6454SAndrew Thompson  * mass storage quirks for not supported SCSI commands!
3302ac6454SAndrew Thompson  */
3402ac6454SAndrew Thompson 
35ed6d949aSAndrew Thompson #include <sys/stdint.h>
36ed6d949aSAndrew Thompson #include <sys/stddef.h>
37ed6d949aSAndrew Thompson #include <sys/param.h>
38ed6d949aSAndrew Thompson #include <sys/queue.h>
39ed6d949aSAndrew Thompson #include <sys/types.h>
40ed6d949aSAndrew Thompson #include <sys/systm.h>
41ed6d949aSAndrew Thompson #include <sys/kernel.h>
42ed6d949aSAndrew Thompson #include <sys/bus.h>
43ed6d949aSAndrew Thompson #include <sys/module.h>
44ed6d949aSAndrew Thompson #include <sys/lock.h>
45ed6d949aSAndrew Thompson #include <sys/mutex.h>
46ed6d949aSAndrew Thompson #include <sys/condvar.h>
47ed6d949aSAndrew Thompson #include <sys/sysctl.h>
48ed6d949aSAndrew Thompson #include <sys/sx.h>
49ed6d949aSAndrew Thompson #include <sys/unistd.h>
50ed6d949aSAndrew Thompson #include <sys/callout.h>
51ed6d949aSAndrew Thompson #include <sys/malloc.h>
52ed6d949aSAndrew Thompson #include <sys/priv.h>
53ed6d949aSAndrew Thompson 
5402ac6454SAndrew Thompson #include <dev/usb/usb.h>
55ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
56ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h>
5702ac6454SAndrew Thompson 
58a593f6b8SAndrew Thompson #define	USB_DEBUG_VAR usb_debug
5902ac6454SAndrew Thompson 
6002ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h>
6102ac6454SAndrew Thompson #include <dev/usb/usb_process.h>
6202ac6454SAndrew Thompson #include <dev/usb/usb_transfer.h>
6302ac6454SAndrew Thompson #include <dev/usb/usb_msctest.h>
6402ac6454SAndrew Thompson #include <dev/usb/usb_debug.h>
6502ac6454SAndrew Thompson #include <dev/usb/usb_device.h>
6602ac6454SAndrew Thompson #include <dev/usb/usb_request.h>
6702ac6454SAndrew Thompson #include <dev/usb/usb_util.h>
68d84a79e7SAndrew Thompson #include <dev/usb/quirk/usb_quirk.h>
6902ac6454SAndrew Thompson 
7002ac6454SAndrew Thompson enum {
7102ac6454SAndrew Thompson 	ST_COMMAND,
7202ac6454SAndrew Thompson 	ST_DATA_RD,
7302ac6454SAndrew Thompson 	ST_DATA_RD_CS,
7402ac6454SAndrew Thompson 	ST_DATA_WR,
7502ac6454SAndrew Thompson 	ST_DATA_WR_CS,
7602ac6454SAndrew Thompson 	ST_STATUS,
7702ac6454SAndrew Thompson 	ST_MAX,
7802ac6454SAndrew Thompson };
7902ac6454SAndrew Thompson 
8002ac6454SAndrew Thompson enum {
8102ac6454SAndrew Thompson 	DIR_IN,
8202ac6454SAndrew Thompson 	DIR_OUT,
8302ac6454SAndrew Thompson 	DIR_NONE,
8402ac6454SAndrew Thompson };
8502ac6454SAndrew Thompson 
86d334432fSHans Petter Selasky #define	SCSI_MAX_LEN	0x100
87d84a79e7SAndrew Thompson #define	SCSI_INQ_LEN	0x24
88d334432fSHans Petter Selasky #define	SCSI_SENSE_LEN	0xFF
89d334432fSHans Petter Selasky 
90d84a79e7SAndrew Thompson static uint8_t scsi_test_unit_ready[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
91d84a79e7SAndrew Thompson static uint8_t scsi_inquiry[] = { 0x12, 0x00, 0x00, 0x00, SCSI_INQ_LEN, 0x00 };
92d84a79e7SAndrew Thompson static uint8_t scsi_rezero_init[] =     { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
93d84a79e7SAndrew Thompson static uint8_t scsi_start_stop_unit[] = { 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00 };
94d84a79e7SAndrew Thompson static uint8_t scsi_ztestor_eject[] =   { 0x85, 0x01, 0x01, 0x01, 0x18, 0x01,
95d84a79e7SAndrew Thompson 					  0x01, 0x01, 0x01, 0x01, 0x00, 0x00 };
96d84a79e7SAndrew Thompson static uint8_t scsi_cmotech_eject[] =   { 0xff, 0x52, 0x44, 0x45, 0x56, 0x43,
97d84a79e7SAndrew Thompson 					  0x48, 0x47 };
982386d714SAndrew Thompson static uint8_t scsi_huawei_eject[] =	{ 0x11, 0x06, 0x00, 0x00, 0x00, 0x00,
992386d714SAndrew Thompson 					  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1002386d714SAndrew Thompson 					  0x00, 0x00, 0x00, 0x00 };
101fb7a4d49SGleb Smirnoff static uint8_t scsi_tct_eject[] =	{ 0x06, 0xf5, 0x04, 0x02, 0x52, 0x70 };
102d46dc4adSHans Petter Selasky static uint8_t scsi_sync_cache[] =	{ 0x35, 0x00, 0x00, 0x00, 0x00, 0x00,
103d46dc4adSHans Petter Selasky 					  0x00, 0x00, 0x00, 0x00 };
104d334432fSHans Petter Selasky static uint8_t scsi_request_sense[] =	{ 0x03, 0x00, 0x00, 0x00, 0x12, 0x00,
105d334432fSHans Petter Selasky 					  0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
106d84a79e7SAndrew Thompson 
10702ac6454SAndrew Thompson #define	BULK_SIZE		64	/* dummy */
108d84a79e7SAndrew Thompson #define	ERR_CSW_FAILED		-1
10902ac6454SAndrew Thompson 
11002ac6454SAndrew Thompson /* Command Block Wrapper */
11102ac6454SAndrew Thompson struct bbb_cbw {
11202ac6454SAndrew Thompson 	uDWord	dCBWSignature;
11302ac6454SAndrew Thompson #define	CBWSIGNATURE	0x43425355
11402ac6454SAndrew Thompson 	uDWord	dCBWTag;
11502ac6454SAndrew Thompson 	uDWord	dCBWDataTransferLength;
11602ac6454SAndrew Thompson 	uByte	bCBWFlags;
11702ac6454SAndrew Thompson #define	CBWFLAGS_OUT	0x00
11802ac6454SAndrew Thompson #define	CBWFLAGS_IN	0x80
11902ac6454SAndrew Thompson 	uByte	bCBWLUN;
12002ac6454SAndrew Thompson 	uByte	bCDBLength;
12102ac6454SAndrew Thompson #define	CBWCDBLENGTH	16
12202ac6454SAndrew Thompson 	uByte	CBWCDB[CBWCDBLENGTH];
12302ac6454SAndrew Thompson } __packed;
12402ac6454SAndrew Thompson 
12502ac6454SAndrew Thompson /* Command Status Wrapper */
12602ac6454SAndrew Thompson struct bbb_csw {
12702ac6454SAndrew Thompson 	uDWord	dCSWSignature;
12802ac6454SAndrew Thompson #define	CSWSIGNATURE	0x53425355
12902ac6454SAndrew Thompson 	uDWord	dCSWTag;
13002ac6454SAndrew Thompson 	uDWord	dCSWDataResidue;
13102ac6454SAndrew Thompson 	uByte	bCSWStatus;
13202ac6454SAndrew Thompson #define	CSWSTATUS_GOOD	0x0
13302ac6454SAndrew Thompson #define	CSWSTATUS_FAILED	0x1
13402ac6454SAndrew Thompson #define	CSWSTATUS_PHASE	0x2
13502ac6454SAndrew Thompson } __packed;
13602ac6454SAndrew Thompson 
13702ac6454SAndrew Thompson struct bbb_transfer {
13802ac6454SAndrew Thompson 	struct mtx mtx;
13902ac6454SAndrew Thompson 	struct cv cv;
14002ac6454SAndrew Thompson 	struct bbb_cbw cbw;
14102ac6454SAndrew Thompson 	struct bbb_csw csw;
14202ac6454SAndrew Thompson 
143760bc48eSAndrew Thompson 	struct usb_xfer *xfer[ST_MAX];
14402ac6454SAndrew Thompson 
14502ac6454SAndrew Thompson 	uint8_t *data_ptr;
14602ac6454SAndrew Thompson 
147f9cb546cSAndrew Thompson 	usb_size_t data_len;		/* bytes */
148f9cb546cSAndrew Thompson 	usb_size_t data_rem;		/* bytes */
149e0a69b51SAndrew Thompson 	usb_timeout_t data_timeout;	/* ms */
150e0a69b51SAndrew Thompson 	usb_frlength_t actlen;		/* bytes */
15102ac6454SAndrew Thompson 
15202ac6454SAndrew Thompson 	uint8_t	cmd_len;		/* bytes */
15302ac6454SAndrew Thompson 	uint8_t	dir;
15402ac6454SAndrew Thompson 	uint8_t	lun;
15502ac6454SAndrew Thompson 	uint8_t	state;
15602ac6454SAndrew Thompson 	uint8_t	status_try;
157d84a79e7SAndrew Thompson 	int	error;
15802ac6454SAndrew Thompson 
159d334432fSHans Petter Selasky 	uint8_t	buffer[SCSI_MAX_LEN] __aligned(4);
16002ac6454SAndrew Thompson };
16102ac6454SAndrew Thompson 
162e0a69b51SAndrew Thompson static usb_callback_t bbb_command_callback;
163e0a69b51SAndrew Thompson static usb_callback_t bbb_data_read_callback;
164e0a69b51SAndrew Thompson static usb_callback_t bbb_data_rd_cs_callback;
165e0a69b51SAndrew Thompson static usb_callback_t bbb_data_write_callback;
166e0a69b51SAndrew Thompson static usb_callback_t bbb_data_wr_cs_callback;
167e0a69b51SAndrew Thompson static usb_callback_t bbb_status_callback;
16802ac6454SAndrew Thompson 
169d84a79e7SAndrew Thompson static void	bbb_done(struct bbb_transfer *, int);
170d84a79e7SAndrew Thompson static void	bbb_transfer_start(struct bbb_transfer *, uint8_t);
171d84a79e7SAndrew Thompson static void	bbb_data_clear_stall_callback(struct usb_xfer *, uint8_t,
172d84a79e7SAndrew Thompson 		    uint8_t);
173d46dc4adSHans Petter Selasky static int	bbb_command_start(struct bbb_transfer *, uint8_t, uint8_t,
174d84a79e7SAndrew Thompson 		    void *, size_t, void *, size_t, usb_timeout_t);
175d84a79e7SAndrew Thompson static struct bbb_transfer *bbb_attach(struct usb_device *, uint8_t);
176d84a79e7SAndrew Thompson static void	bbb_detach(struct bbb_transfer *);
177d84a79e7SAndrew Thompson 
178760bc48eSAndrew Thompson static const struct usb_config bbb_config[ST_MAX] = {
17902ac6454SAndrew Thompson 
18002ac6454SAndrew Thompson 	[ST_COMMAND] = {
18102ac6454SAndrew Thompson 		.type = UE_BULK,
18202ac6454SAndrew Thompson 		.endpoint = UE_ADDR_ANY,
18302ac6454SAndrew Thompson 		.direction = UE_DIR_OUT,
1844eae601eSAndrew Thompson 		.bufsize = sizeof(struct bbb_cbw),
1857cd72d85SHans Petter Selasky 		.flags = {.ext_buffer = 1,},
1864eae601eSAndrew Thompson 		.callback = &bbb_command_callback,
1874eae601eSAndrew Thompson 		.timeout = 4 * USB_MS_HZ,	/* 4 seconds */
18802ac6454SAndrew Thompson 	},
18902ac6454SAndrew Thompson 
19002ac6454SAndrew Thompson 	[ST_DATA_RD] = {
19102ac6454SAndrew Thompson 		.type = UE_BULK,
19202ac6454SAndrew Thompson 		.endpoint = UE_ADDR_ANY,
19302ac6454SAndrew Thompson 		.direction = UE_DIR_IN,
1944eae601eSAndrew Thompson 		.bufsize = BULK_SIZE,
1957cd72d85SHans Petter Selasky 		.flags = {.ext_buffer = 1,.proxy_buffer = 1,.short_xfer_ok = 1,},
1964eae601eSAndrew Thompson 		.callback = &bbb_data_read_callback,
1974eae601eSAndrew Thompson 		.timeout = 4 * USB_MS_HZ,	/* 4 seconds */
19802ac6454SAndrew Thompson 	},
19902ac6454SAndrew Thompson 
20002ac6454SAndrew Thompson 	[ST_DATA_RD_CS] = {
20102ac6454SAndrew Thompson 		.type = UE_CONTROL,
20202ac6454SAndrew Thompson 		.endpoint = 0x00,	/* Control pipe */
20302ac6454SAndrew Thompson 		.direction = UE_DIR_ANY,
204760bc48eSAndrew Thompson 		.bufsize = sizeof(struct usb_device_request),
2054eae601eSAndrew Thompson 		.callback = &bbb_data_rd_cs_callback,
2064eae601eSAndrew Thompson 		.timeout = 1 * USB_MS_HZ,	/* 1 second  */
20702ac6454SAndrew Thompson 	},
20802ac6454SAndrew Thompson 
20902ac6454SAndrew Thompson 	[ST_DATA_WR] = {
21002ac6454SAndrew Thompson 		.type = UE_BULK,
21102ac6454SAndrew Thompson 		.endpoint = UE_ADDR_ANY,
21202ac6454SAndrew Thompson 		.direction = UE_DIR_OUT,
2134eae601eSAndrew Thompson 		.bufsize = BULK_SIZE,
2147cd72d85SHans Petter Selasky 		.flags = {.ext_buffer = 1,.proxy_buffer = 1,},
2154eae601eSAndrew Thompson 		.callback = &bbb_data_write_callback,
2164eae601eSAndrew Thompson 		.timeout = 4 * USB_MS_HZ,	/* 4 seconds */
21702ac6454SAndrew Thompson 	},
21802ac6454SAndrew Thompson 
21902ac6454SAndrew Thompson 	[ST_DATA_WR_CS] = {
22002ac6454SAndrew Thompson 		.type = UE_CONTROL,
22102ac6454SAndrew Thompson 		.endpoint = 0x00,	/* Control pipe */
22202ac6454SAndrew Thompson 		.direction = UE_DIR_ANY,
223760bc48eSAndrew Thompson 		.bufsize = sizeof(struct usb_device_request),
2244eae601eSAndrew Thompson 		.callback = &bbb_data_wr_cs_callback,
2254eae601eSAndrew Thompson 		.timeout = 1 * USB_MS_HZ,	/* 1 second  */
22602ac6454SAndrew Thompson 	},
22702ac6454SAndrew Thompson 
22802ac6454SAndrew Thompson 	[ST_STATUS] = {
22902ac6454SAndrew Thompson 		.type = UE_BULK,
23002ac6454SAndrew Thompson 		.endpoint = UE_ADDR_ANY,
23102ac6454SAndrew Thompson 		.direction = UE_DIR_IN,
2324eae601eSAndrew Thompson 		.bufsize = sizeof(struct bbb_csw),
2337cd72d85SHans Petter Selasky 		.flags = {.ext_buffer = 1,.short_xfer_ok = 1,},
2344eae601eSAndrew Thompson 		.callback = &bbb_status_callback,
2354eae601eSAndrew Thompson 		.timeout = 1 * USB_MS_HZ,	/* 1 second  */
23602ac6454SAndrew Thompson 	},
23702ac6454SAndrew Thompson };
23802ac6454SAndrew Thompson 
23902ac6454SAndrew Thompson static void
240d84a79e7SAndrew Thompson bbb_done(struct bbb_transfer *sc, int error)
24102ac6454SAndrew Thompson {
24202ac6454SAndrew Thompson 
24302ac6454SAndrew Thompson 	sc->error = error;
24402ac6454SAndrew Thompson 	sc->state = ST_COMMAND;
24502ac6454SAndrew Thompson 	sc->status_try = 1;
2468437751dSAndrew Thompson 	cv_signal(&sc->cv);
24702ac6454SAndrew Thompson }
24802ac6454SAndrew Thompson 
24902ac6454SAndrew Thompson static void
25002ac6454SAndrew Thompson bbb_transfer_start(struct bbb_transfer *sc, uint8_t xfer_index)
25102ac6454SAndrew Thompson {
25202ac6454SAndrew Thompson 	sc->state = xfer_index;
253a593f6b8SAndrew Thompson 	usbd_transfer_start(sc->xfer[xfer_index]);
25402ac6454SAndrew Thompson }
25502ac6454SAndrew Thompson 
25602ac6454SAndrew Thompson static void
257760bc48eSAndrew Thompson bbb_data_clear_stall_callback(struct usb_xfer *xfer,
25802ac6454SAndrew Thompson     uint8_t next_xfer, uint8_t stall_xfer)
25902ac6454SAndrew Thompson {
260ed6d949aSAndrew Thompson 	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
26102ac6454SAndrew Thompson 
262a593f6b8SAndrew Thompson 	if (usbd_clear_stall_callback(xfer, sc->xfer[stall_xfer])) {
26302ac6454SAndrew Thompson 		switch (USB_GET_STATE(xfer)) {
26402ac6454SAndrew Thompson 		case USB_ST_SETUP:
26502ac6454SAndrew Thompson 		case USB_ST_TRANSFERRED:
26602ac6454SAndrew Thompson 			bbb_transfer_start(sc, next_xfer);
26702ac6454SAndrew Thompson 			break;
26802ac6454SAndrew Thompson 		default:
269d84a79e7SAndrew Thompson 			bbb_done(sc, USB_ERR_STALLED);
27002ac6454SAndrew Thompson 			break;
27102ac6454SAndrew Thompson 		}
27202ac6454SAndrew Thompson 	}
27302ac6454SAndrew Thompson }
27402ac6454SAndrew Thompson 
27502ac6454SAndrew Thompson static void
276ed6d949aSAndrew Thompson bbb_command_callback(struct usb_xfer *xfer, usb_error_t error)
27702ac6454SAndrew Thompson {
278ed6d949aSAndrew Thompson 	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
27902ac6454SAndrew Thompson 	uint32_t tag;
28002ac6454SAndrew Thompson 
28102ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
28202ac6454SAndrew Thompson 	case USB_ST_TRANSFERRED:
28302ac6454SAndrew Thompson 		bbb_transfer_start
28402ac6454SAndrew Thompson 		    (sc, ((sc->dir == DIR_IN) ? ST_DATA_RD :
28502ac6454SAndrew Thompson 		    (sc->dir == DIR_OUT) ? ST_DATA_WR :
28602ac6454SAndrew Thompson 		    ST_STATUS));
28702ac6454SAndrew Thompson 		break;
28802ac6454SAndrew Thompson 
28902ac6454SAndrew Thompson 	case USB_ST_SETUP:
29002ac6454SAndrew Thompson 		sc->status_try = 0;
29102ac6454SAndrew Thompson 		tag = UGETDW(sc->cbw.dCBWTag) + 1;
29202ac6454SAndrew Thompson 		USETDW(sc->cbw.dCBWSignature, CBWSIGNATURE);
29302ac6454SAndrew Thompson 		USETDW(sc->cbw.dCBWTag, tag);
294578d0effSAndrew Thompson 		USETDW(sc->cbw.dCBWDataTransferLength, (uint32_t)sc->data_len);
29502ac6454SAndrew Thompson 		sc->cbw.bCBWFlags = ((sc->dir == DIR_IN) ? CBWFLAGS_IN : CBWFLAGS_OUT);
29602ac6454SAndrew Thompson 		sc->cbw.bCBWLUN = sc->lun;
29702ac6454SAndrew Thompson 		sc->cbw.bCDBLength = sc->cmd_len;
29802ac6454SAndrew Thompson 		if (sc->cbw.bCDBLength > sizeof(sc->cbw.CBWCDB)) {
29902ac6454SAndrew Thompson 			sc->cbw.bCDBLength = sizeof(sc->cbw.CBWCDB);
300767cb2e2SAndrew Thompson 			DPRINTFN(0, "Truncating long command\n");
30102ac6454SAndrew Thompson 		}
302ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_data(xfer, 0, &sc->cbw, sizeof(sc->cbw));
303a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
30402ac6454SAndrew Thompson 		break;
30502ac6454SAndrew Thompson 
30602ac6454SAndrew Thompson 	default:			/* Error */
307d84a79e7SAndrew Thompson 		bbb_done(sc, error);
30802ac6454SAndrew Thompson 		break;
30902ac6454SAndrew Thompson 	}
31002ac6454SAndrew Thompson }
31102ac6454SAndrew Thompson 
31202ac6454SAndrew Thompson static void
313ed6d949aSAndrew Thompson bbb_data_read_callback(struct usb_xfer *xfer, usb_error_t error)
31402ac6454SAndrew Thompson {
315ed6d949aSAndrew Thompson 	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
316ed6d949aSAndrew Thompson 	usb_frlength_t max_bulk = usbd_xfer_max_len(xfer);
317ed6d949aSAndrew Thompson 	int actlen, sumlen;
318ed6d949aSAndrew Thompson 
319ed6d949aSAndrew Thompson 	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
32002ac6454SAndrew Thompson 
32102ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
32202ac6454SAndrew Thompson 	case USB_ST_TRANSFERRED:
323ed6d949aSAndrew Thompson 		sc->data_rem -= actlen;
324ed6d949aSAndrew Thompson 		sc->data_ptr += actlen;
325ed6d949aSAndrew Thompson 		sc->actlen += actlen;
32602ac6454SAndrew Thompson 
327ed6d949aSAndrew Thompson 		if (actlen < sumlen) {
32802ac6454SAndrew Thompson 			/* short transfer */
32902ac6454SAndrew Thompson 			sc->data_rem = 0;
33002ac6454SAndrew Thompson 		}
33102ac6454SAndrew Thompson 	case USB_ST_SETUP:
33202ac6454SAndrew Thompson 		DPRINTF("max_bulk=%d, data_rem=%d\n",
33302ac6454SAndrew Thompson 		    max_bulk, sc->data_rem);
33402ac6454SAndrew Thompson 
33502ac6454SAndrew Thompson 		if (sc->data_rem == 0) {
33602ac6454SAndrew Thompson 			bbb_transfer_start(sc, ST_STATUS);
33702ac6454SAndrew Thompson 			break;
33802ac6454SAndrew Thompson 		}
33902ac6454SAndrew Thompson 		if (max_bulk > sc->data_rem) {
34002ac6454SAndrew Thompson 			max_bulk = sc->data_rem;
34102ac6454SAndrew Thompson 		}
342ed6d949aSAndrew Thompson 		usbd_xfer_set_timeout(xfer, sc->data_timeout);
343ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_data(xfer, 0, sc->data_ptr, max_bulk);
344a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
34502ac6454SAndrew Thompson 		break;
34602ac6454SAndrew Thompson 
34702ac6454SAndrew Thompson 	default:			/* Error */
348ed6d949aSAndrew Thompson 		if (error == USB_ERR_CANCELLED) {
349d84a79e7SAndrew Thompson 			bbb_done(sc, error);
35002ac6454SAndrew Thompson 		} else {
35102ac6454SAndrew Thompson 			bbb_transfer_start(sc, ST_DATA_RD_CS);
35202ac6454SAndrew Thompson 		}
35302ac6454SAndrew Thompson 		break;
35402ac6454SAndrew Thompson 	}
35502ac6454SAndrew Thompson }
35602ac6454SAndrew Thompson 
35702ac6454SAndrew Thompson static void
358ed6d949aSAndrew Thompson bbb_data_rd_cs_callback(struct usb_xfer *xfer, usb_error_t error)
35902ac6454SAndrew Thompson {
36002ac6454SAndrew Thompson 	bbb_data_clear_stall_callback(xfer, ST_STATUS,
36102ac6454SAndrew Thompson 	    ST_DATA_RD);
36202ac6454SAndrew Thompson }
36302ac6454SAndrew Thompson 
36402ac6454SAndrew Thompson static void
365ed6d949aSAndrew Thompson bbb_data_write_callback(struct usb_xfer *xfer, usb_error_t error)
36602ac6454SAndrew Thompson {
367ed6d949aSAndrew Thompson 	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
368ed6d949aSAndrew Thompson 	usb_frlength_t max_bulk = usbd_xfer_max_len(xfer);
369ed6d949aSAndrew Thompson 	int actlen, sumlen;
370ed6d949aSAndrew Thompson 
371ed6d949aSAndrew Thompson 	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
37202ac6454SAndrew Thompson 
37302ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
37402ac6454SAndrew Thompson 	case USB_ST_TRANSFERRED:
375ed6d949aSAndrew Thompson 		sc->data_rem -= actlen;
376ed6d949aSAndrew Thompson 		sc->data_ptr += actlen;
377ed6d949aSAndrew Thompson 		sc->actlen += actlen;
37802ac6454SAndrew Thompson 
379ed6d949aSAndrew Thompson 		if (actlen < sumlen) {
38002ac6454SAndrew Thompson 			/* short transfer */
38102ac6454SAndrew Thompson 			sc->data_rem = 0;
38202ac6454SAndrew Thompson 		}
38302ac6454SAndrew Thompson 	case USB_ST_SETUP:
38402ac6454SAndrew Thompson 		DPRINTF("max_bulk=%d, data_rem=%d\n",
38502ac6454SAndrew Thompson 		    max_bulk, sc->data_rem);
38602ac6454SAndrew Thompson 
38702ac6454SAndrew Thompson 		if (sc->data_rem == 0) {
38802ac6454SAndrew Thompson 			bbb_transfer_start(sc, ST_STATUS);
38902ac6454SAndrew Thompson 			return;
39002ac6454SAndrew Thompson 		}
39102ac6454SAndrew Thompson 		if (max_bulk > sc->data_rem) {
39202ac6454SAndrew Thompson 			max_bulk = sc->data_rem;
39302ac6454SAndrew Thompson 		}
394ed6d949aSAndrew Thompson 		usbd_xfer_set_timeout(xfer, sc->data_timeout);
395ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_data(xfer, 0, sc->data_ptr, max_bulk);
396a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
39702ac6454SAndrew Thompson 		return;
39802ac6454SAndrew Thompson 
39902ac6454SAndrew Thompson 	default:			/* Error */
400ed6d949aSAndrew Thompson 		if (error == USB_ERR_CANCELLED) {
401d84a79e7SAndrew Thompson 			bbb_done(sc, error);
40202ac6454SAndrew Thompson 		} else {
40302ac6454SAndrew Thompson 			bbb_transfer_start(sc, ST_DATA_WR_CS);
40402ac6454SAndrew Thompson 		}
40502ac6454SAndrew Thompson 		return;
40602ac6454SAndrew Thompson 
40702ac6454SAndrew Thompson 	}
40802ac6454SAndrew Thompson }
40902ac6454SAndrew Thompson 
41002ac6454SAndrew Thompson static void
411ed6d949aSAndrew Thompson bbb_data_wr_cs_callback(struct usb_xfer *xfer, usb_error_t error)
41202ac6454SAndrew Thompson {
41302ac6454SAndrew Thompson 	bbb_data_clear_stall_callback(xfer, ST_STATUS,
41402ac6454SAndrew Thompson 	    ST_DATA_WR);
41502ac6454SAndrew Thompson }
41602ac6454SAndrew Thompson 
41702ac6454SAndrew Thompson static void
418ed6d949aSAndrew Thompson bbb_status_callback(struct usb_xfer *xfer, usb_error_t error)
41902ac6454SAndrew Thompson {
420ed6d949aSAndrew Thompson 	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
421*6d917491SHans Petter Selasky 	int actlen;
422*6d917491SHans Petter Selasky 	int sumlen;
423ed6d949aSAndrew Thompson 
424ed6d949aSAndrew Thompson 	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
42502ac6454SAndrew Thompson 
42602ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
42702ac6454SAndrew Thompson 	case USB_ST_TRANSFERRED:
42802ac6454SAndrew Thompson 
42902ac6454SAndrew Thompson 		/* very simple status check */
43002ac6454SAndrew Thompson 
431*6d917491SHans Petter Selasky 		if (actlen < (int)sizeof(sc->csw)) {
432d84a79e7SAndrew Thompson 			bbb_done(sc, USB_ERR_SHORT_XFER);
43302ac6454SAndrew Thompson 		} else if (sc->csw.bCSWStatus == CSWSTATUS_GOOD) {
43402ac6454SAndrew Thompson 			bbb_done(sc, 0);	/* success */
43502ac6454SAndrew Thompson 		} else {
436d84a79e7SAndrew Thompson 			bbb_done(sc, ERR_CSW_FAILED);	/* error */
43702ac6454SAndrew Thompson 		}
43802ac6454SAndrew Thompson 		break;
43902ac6454SAndrew Thompson 
44002ac6454SAndrew Thompson 	case USB_ST_SETUP:
441ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_data(xfer, 0, &sc->csw, sizeof(sc->csw));
442a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
44302ac6454SAndrew Thompson 		break;
44402ac6454SAndrew Thompson 
44502ac6454SAndrew Thompson 	default:
446d84a79e7SAndrew Thompson 		DPRINTF("Failed to read CSW: %s, try %d\n",
447ed6d949aSAndrew Thompson 		    usbd_errstr(error), sc->status_try);
44802ac6454SAndrew Thompson 
449ed6d949aSAndrew Thompson 		if (error == USB_ERR_CANCELLED || sc->status_try) {
450d84a79e7SAndrew Thompson 			bbb_done(sc, error);
45102ac6454SAndrew Thompson 		} else {
45202ac6454SAndrew Thompson 			sc->status_try = 1;
45302ac6454SAndrew Thompson 			bbb_transfer_start(sc, ST_DATA_RD_CS);
45402ac6454SAndrew Thompson 		}
45502ac6454SAndrew Thompson 		break;
45602ac6454SAndrew Thompson 	}
45702ac6454SAndrew Thompson }
45802ac6454SAndrew Thompson 
45902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
46002ac6454SAndrew Thompson  *	bbb_command_start - execute a SCSI command synchronously
46102ac6454SAndrew Thompson  *
46202ac6454SAndrew Thompson  * Return values
46302ac6454SAndrew Thompson  * 0: Success
46402ac6454SAndrew Thompson  * Else: Failure
46502ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
466d46dc4adSHans Petter Selasky static int
46702ac6454SAndrew Thompson bbb_command_start(struct bbb_transfer *sc, uint8_t dir, uint8_t lun,
468d84a79e7SAndrew Thompson     void *data_ptr, size_t data_len, void *cmd_ptr, size_t cmd_len,
469e0a69b51SAndrew Thompson     usb_timeout_t data_timeout)
47002ac6454SAndrew Thompson {
47102ac6454SAndrew Thompson 	sc->lun = lun;
47202ac6454SAndrew Thompson 	sc->dir = data_len ? dir : DIR_NONE;
47302ac6454SAndrew Thompson 	sc->data_ptr = data_ptr;
47402ac6454SAndrew Thompson 	sc->data_len = data_len;
47502ac6454SAndrew Thompson 	sc->data_rem = data_len;
47602ac6454SAndrew Thompson 	sc->data_timeout = (data_timeout + USB_MS_HZ);
47702ac6454SAndrew Thompson 	sc->actlen = 0;
47802ac6454SAndrew Thompson 	sc->cmd_len = cmd_len;
479271ae033SHans Petter Selasky 	memset(&sc->cbw.CBWCDB, 0, sizeof(sc->cbw.CBWCDB));
480271ae033SHans Petter Selasky 	memcpy(&sc->cbw.CBWCDB, cmd_ptr, cmd_len);
48187812fceSHans Petter Selasky 	DPRINTFN(1, "SCSI cmd = %*D\n", (int)cmd_len, (char *)sc->cbw.CBWCDB, ":");
48202ac6454SAndrew Thompson 
483d84a79e7SAndrew Thompson 	mtx_lock(&sc->mtx);
484a593f6b8SAndrew Thompson 	usbd_transfer_start(sc->xfer[sc->state]);
48502ac6454SAndrew Thompson 
486a593f6b8SAndrew Thompson 	while (usbd_transfer_pending(sc->xfer[sc->state])) {
4878437751dSAndrew Thompson 		cv_wait(&sc->cv, &sc->mtx);
48802ac6454SAndrew Thompson 	}
489d84a79e7SAndrew Thompson 	mtx_unlock(&sc->mtx);
49002ac6454SAndrew Thompson 	return (sc->error);
49102ac6454SAndrew Thompson }
49202ac6454SAndrew Thompson 
493d84a79e7SAndrew Thompson static struct bbb_transfer *
494d84a79e7SAndrew Thompson bbb_attach(struct usb_device *udev, uint8_t iface_index)
49502ac6454SAndrew Thompson {
496760bc48eSAndrew Thompson 	struct usb_interface *iface;
497760bc48eSAndrew Thompson 	struct usb_interface_descriptor *id;
49802ac6454SAndrew Thompson 	struct bbb_transfer *sc;
499d84a79e7SAndrew Thompson 	usb_error_t err;
500a6b60150SHans Petter Selasky 	uint8_t do_unlock;
501a6b60150SHans Petter Selasky 
502a6b60150SHans Petter Selasky 	/* automatic locking */
503a6b60150SHans Petter Selasky 	if (usbd_enum_is_locked(udev)) {
504a6b60150SHans Petter Selasky 		do_unlock = 0;
505a6b60150SHans Petter Selasky 	} else {
506a6b60150SHans Petter Selasky 		do_unlock = 1;
507a6b60150SHans Petter Selasky 		usbd_enum_lock(udev);
508a6b60150SHans Petter Selasky 	}
509a6b60150SHans Petter Selasky 
510a6b60150SHans Petter Selasky 	/*
511a6b60150SHans Petter Selasky 	 * Make sure any driver which is hooked up to this interface,
512a6b60150SHans Petter Selasky 	 * like umass is gone:
513a6b60150SHans Petter Selasky 	 */
514a6b60150SHans Petter Selasky 	usb_detach_device(udev, iface_index, 0);
515a6b60150SHans Petter Selasky 
516a6b60150SHans Petter Selasky 	if (do_unlock)
517a6b60150SHans Petter Selasky 		usbd_enum_unlock(udev);
51802ac6454SAndrew Thompson 
519a593f6b8SAndrew Thompson 	iface = usbd_get_iface(udev, iface_index);
520d84a79e7SAndrew Thompson 	if (iface == NULL)
521d84a79e7SAndrew Thompson 		return (NULL);
522d84a79e7SAndrew Thompson 
52302ac6454SAndrew Thompson 	id = iface->idesc;
524d84a79e7SAndrew Thompson 	if (id == NULL || id->bInterfaceClass != UICLASS_MASS)
525d84a79e7SAndrew Thompson 		return (NULL);
526d84a79e7SAndrew Thompson 
52702ac6454SAndrew Thompson 	switch (id->bInterfaceSubClass) {
52802ac6454SAndrew Thompson 	case UISUBCLASS_SCSI:
52902ac6454SAndrew Thompson 	case UISUBCLASS_UFI:
530d84a79e7SAndrew Thompson 	case UISUBCLASS_SFF8020I:
531d84a79e7SAndrew Thompson 	case UISUBCLASS_SFF8070I:
53202ac6454SAndrew Thompson 		break;
53302ac6454SAndrew Thompson 	default:
534d84a79e7SAndrew Thompson 		return (NULL);
53502ac6454SAndrew Thompson 	}
53602ac6454SAndrew Thompson 
53702ac6454SAndrew Thompson 	switch (id->bInterfaceProtocol) {
53802ac6454SAndrew Thompson 	case UIPROTO_MASS_BBB_OLD:
53902ac6454SAndrew Thompson 	case UIPROTO_MASS_BBB:
54002ac6454SAndrew Thompson 		break;
54102ac6454SAndrew Thompson 	default:
542d84a79e7SAndrew Thompson 		return (NULL);
54302ac6454SAndrew Thompson 	}
54402ac6454SAndrew Thompson 
54502ac6454SAndrew Thompson 	sc = malloc(sizeof(*sc), M_USB, M_WAITOK | M_ZERO);
54602ac6454SAndrew Thompson 	mtx_init(&sc->mtx, "USB autoinstall", NULL, MTX_DEF);
5478437751dSAndrew Thompson 	cv_init(&sc->cv, "WBBB");
54802ac6454SAndrew Thompson 
549d84a79e7SAndrew Thompson 	err = usbd_transfer_setup(udev, &iface_index, sc->xfer, bbb_config,
55002ac6454SAndrew Thompson 	    ST_MAX, sc, &sc->mtx);
55102ac6454SAndrew Thompson 	if (err) {
552d84a79e7SAndrew Thompson 		bbb_detach(sc);
553d84a79e7SAndrew Thompson 		return (NULL);
55402ac6454SAndrew Thompson 	}
555d84a79e7SAndrew Thompson 	return (sc);
55602ac6454SAndrew Thompson }
55702ac6454SAndrew Thompson 
558d84a79e7SAndrew Thompson static void
559d84a79e7SAndrew Thompson bbb_detach(struct bbb_transfer *sc)
560d84a79e7SAndrew Thompson {
561a593f6b8SAndrew Thompson 	usbd_transfer_unsetup(sc->xfer, ST_MAX);
56202ac6454SAndrew Thompson 	mtx_destroy(&sc->mtx);
5638437751dSAndrew Thompson 	cv_destroy(&sc->cv);
56402ac6454SAndrew Thompson 	free(sc, M_USB);
565d84a79e7SAndrew Thompson }
566d84a79e7SAndrew Thompson 
567d84a79e7SAndrew Thompson /*------------------------------------------------------------------------*
568d84a79e7SAndrew Thompson  *	usb_iface_is_cdrom
569d84a79e7SAndrew Thompson  *
570d84a79e7SAndrew Thompson  * Return values:
571d84a79e7SAndrew Thompson  * 1: This interface is an auto install disk (CD-ROM)
572d84a79e7SAndrew Thompson  * 0: Not an auto install disk.
573d84a79e7SAndrew Thompson  *------------------------------------------------------------------------*/
574d84a79e7SAndrew Thompson int
575d84a79e7SAndrew Thompson usb_iface_is_cdrom(struct usb_device *udev, uint8_t iface_index)
576d84a79e7SAndrew Thompson {
577d84a79e7SAndrew Thompson 	struct bbb_transfer *sc;
578d46dc4adSHans Petter Selasky 	uint8_t timeout;
579d46dc4adSHans Petter Selasky 	uint8_t is_cdrom;
580d84a79e7SAndrew Thompson 	uint8_t sid_type;
581d46dc4adSHans Petter Selasky 	int err;
582d84a79e7SAndrew Thompson 
583d84a79e7SAndrew Thompson 	sc = bbb_attach(udev, iface_index);
584d84a79e7SAndrew Thompson 	if (sc == NULL)
585d84a79e7SAndrew Thompson 		return (0);
586d84a79e7SAndrew Thompson 
587d84a79e7SAndrew Thompson 	is_cdrom = 0;
588d84a79e7SAndrew Thompson 	timeout = 4;	/* tries */
589d84a79e7SAndrew Thompson 	while (--timeout) {
590d84a79e7SAndrew Thompson 		err = bbb_command_start(sc, DIR_IN, 0, sc->buffer,
591d84a79e7SAndrew Thompson 		    SCSI_INQ_LEN, &scsi_inquiry, sizeof(scsi_inquiry),
592d84a79e7SAndrew Thompson 		    USB_MS_HZ);
593d84a79e7SAndrew Thompson 
594d84a79e7SAndrew Thompson 		if (err == 0 && sc->actlen > 0) {
595d84a79e7SAndrew Thompson 			sid_type = sc->buffer[0] & 0x1F;
596d84a79e7SAndrew Thompson 			if (sid_type == 0x05)
597d84a79e7SAndrew Thompson 				is_cdrom = 1;
598d84a79e7SAndrew Thompson 			break;
599d84a79e7SAndrew Thompson 		} else if (err != ERR_CSW_FAILED)
600d84a79e7SAndrew Thompson 			break;	/* non retryable error */
601d84a79e7SAndrew Thompson 		usb_pause_mtx(NULL, hz);
602d84a79e7SAndrew Thompson 	}
603d84a79e7SAndrew Thompson 	bbb_detach(sc);
604d84a79e7SAndrew Thompson 	return (is_cdrom);
605d84a79e7SAndrew Thompson }
606d84a79e7SAndrew Thompson 
6079157ad4bSHans Petter Selasky static uint8_t
6089157ad4bSHans Petter Selasky usb_msc_get_max_lun(struct usb_device *udev, uint8_t iface_index)
6099157ad4bSHans Petter Selasky {
6109157ad4bSHans Petter Selasky 	struct usb_device_request req;
6119157ad4bSHans Petter Selasky 	usb_error_t err;
6129157ad4bSHans Petter Selasky 	uint8_t buf = 0;
6139157ad4bSHans Petter Selasky 
6149157ad4bSHans Petter Selasky 
6159157ad4bSHans Petter Selasky 	/* The Get Max Lun command is a class-specific request. */
6169157ad4bSHans Petter Selasky 	req.bmRequestType = UT_READ_CLASS_INTERFACE;
6179157ad4bSHans Petter Selasky 	req.bRequest = 0xFE;		/* GET_MAX_LUN */
6189157ad4bSHans Petter Selasky 	USETW(req.wValue, 0);
6199157ad4bSHans Petter Selasky 	req.wIndex[0] = iface_index;
6209157ad4bSHans Petter Selasky 	req.wIndex[1] = 0;
6219157ad4bSHans Petter Selasky 	USETW(req.wLength, 1);
6229157ad4bSHans Petter Selasky 
6239157ad4bSHans Petter Selasky 	err = usbd_do_request(udev, NULL, &req, &buf);
6249157ad4bSHans Petter Selasky 	if (err)
6259157ad4bSHans Petter Selasky 		buf = 0;
6269157ad4bSHans Petter Selasky 
6279157ad4bSHans Petter Selasky 	return (buf);
6289157ad4bSHans Petter Selasky }
6299157ad4bSHans Petter Selasky 
630d84a79e7SAndrew Thompson usb_error_t
631d46dc4adSHans Petter Selasky usb_msc_auto_quirk(struct usb_device *udev, uint8_t iface_index)
632d46dc4adSHans Petter Selasky {
633d46dc4adSHans Petter Selasky 	struct bbb_transfer *sc;
634d46dc4adSHans Petter Selasky 	uint8_t timeout;
635d46dc4adSHans Petter Selasky 	uint8_t is_no_direct;
636d46dc4adSHans Petter Selasky 	uint8_t sid_type;
637d46dc4adSHans Petter Selasky 	int err;
638d46dc4adSHans Petter Selasky 
639d46dc4adSHans Petter Selasky 	sc = bbb_attach(udev, iface_index);
640d46dc4adSHans Petter Selasky 	if (sc == NULL)
641d46dc4adSHans Petter Selasky 		return (0);
642d46dc4adSHans Petter Selasky 
643d46dc4adSHans Petter Selasky 	/*
644d46dc4adSHans Petter Selasky 	 * Some devices need a delay after that the configuration
645d46dc4adSHans Petter Selasky 	 * value is set to function properly:
646d46dc4adSHans Petter Selasky 	 */
647d46dc4adSHans Petter Selasky 	usb_pause_mtx(NULL, hz);
648d46dc4adSHans Petter Selasky 
6499157ad4bSHans Petter Selasky 	if (usb_msc_get_max_lun(udev, iface_index) == 0) {
6509157ad4bSHans Petter Selasky 		DPRINTF("Device has only got one LUN.\n");
6519157ad4bSHans Petter Selasky 		usbd_add_dynamic_quirk(udev, UQ_MSC_NO_GETMAXLUN);
6529157ad4bSHans Petter Selasky 	}
6539157ad4bSHans Petter Selasky 
654d46dc4adSHans Petter Selasky 	is_no_direct = 1;
655d46dc4adSHans Petter Selasky 	for (timeout = 4; timeout; timeout--) {
656d46dc4adSHans Petter Selasky 		err = bbb_command_start(sc, DIR_IN, 0, sc->buffer,
657d46dc4adSHans Petter Selasky 		    SCSI_INQ_LEN, &scsi_inquiry, sizeof(scsi_inquiry),
658d46dc4adSHans Petter Selasky 		    USB_MS_HZ);
659d46dc4adSHans Petter Selasky 
660d46dc4adSHans Petter Selasky 		if (err == 0 && sc->actlen > 0) {
661d46dc4adSHans Petter Selasky 			sid_type = sc->buffer[0] & 0x1F;
662d46dc4adSHans Petter Selasky 			if (sid_type == 0x00)
663d46dc4adSHans Petter Selasky 				is_no_direct = 0;
664d46dc4adSHans Petter Selasky 			break;
665d46dc4adSHans Petter Selasky 		} else if (err != ERR_CSW_FAILED)
666d46dc4adSHans Petter Selasky 			break;	/* non retryable error */
667d46dc4adSHans Petter Selasky 		usb_pause_mtx(NULL, hz);
668d46dc4adSHans Petter Selasky 	}
669d46dc4adSHans Petter Selasky 
670d46dc4adSHans Petter Selasky 	if (is_no_direct) {
671d46dc4adSHans Petter Selasky 		DPRINTF("Device is not direct access.\n");
672d46dc4adSHans Petter Selasky 		goto done;
673d46dc4adSHans Petter Selasky 	}
674d46dc4adSHans Petter Selasky 
675d46dc4adSHans Petter Selasky 	err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
676d46dc4adSHans Petter Selasky 	    &scsi_test_unit_ready, sizeof(scsi_test_unit_ready),
677d46dc4adSHans Petter Selasky 	    USB_MS_HZ);
678d46dc4adSHans Petter Selasky 
679d46dc4adSHans Petter Selasky 	if (err != 0) {
680d46dc4adSHans Petter Selasky 
681d46dc4adSHans Petter Selasky 		if (err != ERR_CSW_FAILED)
682d46dc4adSHans Petter Selasky 			goto error;
683d46dc4adSHans Petter Selasky 	}
684d46dc4adSHans Petter Selasky 
685d46dc4adSHans Petter Selasky 	err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
686d46dc4adSHans Petter Selasky 	    &scsi_sync_cache, sizeof(scsi_sync_cache),
687d46dc4adSHans Petter Selasky 	    USB_MS_HZ);
688d46dc4adSHans Petter Selasky 
689d46dc4adSHans Petter Selasky 	if (err != 0) {
690d46dc4adSHans Petter Selasky 
691d46dc4adSHans Petter Selasky 		if (err != ERR_CSW_FAILED)
692d46dc4adSHans Petter Selasky 			goto error;
693d46dc4adSHans Petter Selasky 
694d46dc4adSHans Petter Selasky 		DPRINTF("Device doesn't handle synchronize cache\n");
695d46dc4adSHans Petter Selasky 
696d46dc4adSHans Petter Selasky 		usbd_add_dynamic_quirk(udev, UQ_MSC_NO_SYNC_CACHE);
697d46dc4adSHans Petter Selasky 	}
698d46dc4adSHans Petter Selasky 
699d334432fSHans Petter Selasky 	/* clear sense status of any failed commands on the device */
700d334432fSHans Petter Selasky 
701d334432fSHans Petter Selasky 	err = bbb_command_start(sc, DIR_IN, 0, sc->buffer,
702d334432fSHans Petter Selasky 	    SCSI_INQ_LEN, &scsi_inquiry, sizeof(scsi_inquiry),
703d334432fSHans Petter Selasky 	    USB_MS_HZ);
704d334432fSHans Petter Selasky 
705d334432fSHans Petter Selasky 	DPRINTF("Inquiry = %d\n", err);
706d334432fSHans Petter Selasky 
707d334432fSHans Petter Selasky 	if (err != 0) {
708d334432fSHans Petter Selasky 
709d334432fSHans Petter Selasky 		if (err != ERR_CSW_FAILED)
710d334432fSHans Petter Selasky 			goto error;
711d334432fSHans Petter Selasky 	}
712d334432fSHans Petter Selasky 
713d334432fSHans Petter Selasky 	err = bbb_command_start(sc, DIR_IN, 0, sc->buffer,
714d334432fSHans Petter Selasky 	    SCSI_SENSE_LEN, &scsi_request_sense,
715d334432fSHans Petter Selasky 	    sizeof(scsi_request_sense), USB_MS_HZ);
716d334432fSHans Petter Selasky 
717d334432fSHans Petter Selasky 	DPRINTF("Request sense = %d\n", err);
718d334432fSHans Petter Selasky 
719d334432fSHans Petter Selasky 	if (err != 0) {
720d334432fSHans Petter Selasky 
721d334432fSHans Petter Selasky 		if (err != ERR_CSW_FAILED)
722d334432fSHans Petter Selasky 			goto error;
723d334432fSHans Petter Selasky 	}
724d334432fSHans Petter Selasky 
725d46dc4adSHans Petter Selasky done:
726d46dc4adSHans Petter Selasky 	bbb_detach(sc);
727d46dc4adSHans Petter Selasky 	return (0);
728d46dc4adSHans Petter Selasky 
729d46dc4adSHans Petter Selasky error:
730d46dc4adSHans Petter Selasky  	bbb_detach(sc);
731d46dc4adSHans Petter Selasky 
732d46dc4adSHans Petter Selasky 	DPRINTF("Device did not respond, enabling all quirks\n");
733d46dc4adSHans Petter Selasky 
734d46dc4adSHans Petter Selasky 	usbd_add_dynamic_quirk(udev, UQ_MSC_NO_SYNC_CACHE);
735d46dc4adSHans Petter Selasky 	usbd_add_dynamic_quirk(udev, UQ_MSC_NO_TEST_UNIT_READY);
736d46dc4adSHans Petter Selasky 
737d46dc4adSHans Petter Selasky 	/* Need to re-enumerate the device */
738d46dc4adSHans Petter Selasky 	usbd_req_re_enumerate(udev, NULL);
739d46dc4adSHans Petter Selasky 
740d46dc4adSHans Petter Selasky 	return (USB_ERR_STALLED);
741d46dc4adSHans Petter Selasky }
742d46dc4adSHans Petter Selasky 
743d46dc4adSHans Petter Selasky usb_error_t
744d84a79e7SAndrew Thompson usb_msc_eject(struct usb_device *udev, uint8_t iface_index, int method)
745d84a79e7SAndrew Thompson {
746d84a79e7SAndrew Thompson 	struct bbb_transfer *sc;
747d84a79e7SAndrew Thompson 	usb_error_t err;
748d84a79e7SAndrew Thompson 
749d84a79e7SAndrew Thompson 	sc = bbb_attach(udev, iface_index);
750d84a79e7SAndrew Thompson 	if (sc == NULL)
751d84a79e7SAndrew Thompson 		return (USB_ERR_INVAL);
752d84a79e7SAndrew Thompson 
753d84a79e7SAndrew Thompson 	err = 0;
754d84a79e7SAndrew Thompson 	switch (method) {
755d84a79e7SAndrew Thompson 	case MSC_EJECT_STOPUNIT:
756d84a79e7SAndrew Thompson 		err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
757d84a79e7SAndrew Thompson 		    &scsi_test_unit_ready, sizeof(scsi_test_unit_ready),
758d84a79e7SAndrew Thompson 		    USB_MS_HZ);
759d84a79e7SAndrew Thompson 		DPRINTF("Test unit ready status: %s\n", usbd_errstr(err));
760d84a79e7SAndrew Thompson 		err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
761d84a79e7SAndrew Thompson 		    &scsi_start_stop_unit, sizeof(scsi_start_stop_unit),
762d84a79e7SAndrew Thompson 		    USB_MS_HZ);
763d84a79e7SAndrew Thompson 		break;
764d84a79e7SAndrew Thompson 	case MSC_EJECT_REZERO:
765d84a79e7SAndrew Thompson 		err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
766d84a79e7SAndrew Thompson 		    &scsi_rezero_init, sizeof(scsi_rezero_init),
767d84a79e7SAndrew Thompson 		    USB_MS_HZ);
768d84a79e7SAndrew Thompson 		break;
769d84a79e7SAndrew Thompson 	case MSC_EJECT_ZTESTOR:
770d84a79e7SAndrew Thompson 		err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
771d84a79e7SAndrew Thompson 		    &scsi_ztestor_eject, sizeof(scsi_ztestor_eject),
772d84a79e7SAndrew Thompson 		    USB_MS_HZ);
773d84a79e7SAndrew Thompson 		break;
774d84a79e7SAndrew Thompson 	case MSC_EJECT_CMOTECH:
775d84a79e7SAndrew Thompson 		err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
776d84a79e7SAndrew Thompson 		    &scsi_cmotech_eject, sizeof(scsi_cmotech_eject),
777d84a79e7SAndrew Thompson 		    USB_MS_HZ);
778d84a79e7SAndrew Thompson 		break;
7792386d714SAndrew Thompson 	case MSC_EJECT_HUAWEI:
7802386d714SAndrew Thompson 		err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
7812386d714SAndrew Thompson 		    &scsi_huawei_eject, sizeof(scsi_huawei_eject),
7822386d714SAndrew Thompson 		    USB_MS_HZ);
7832386d714SAndrew Thompson 		break;
784fb7a4d49SGleb Smirnoff 	case MSC_EJECT_TCT:
78519f54349SGleb Smirnoff 		/*
78619f54349SGleb Smirnoff 		 * TCTMobile needs DIR_IN flag. To get it, we
78719f54349SGleb Smirnoff 		 * supply a dummy data with the command.
78819f54349SGleb Smirnoff 		 */
78919f54349SGleb Smirnoff 		err = bbb_command_start(sc, DIR_IN, 0, &sc->buffer,
79019f54349SGleb Smirnoff 		    sizeof(sc->buffer), &scsi_tct_eject,
791fb7a4d49SGleb Smirnoff 		    sizeof(scsi_tct_eject), USB_MS_HZ);
792fb7a4d49SGleb Smirnoff 		break;
793d84a79e7SAndrew Thompson 	default:
794d84a79e7SAndrew Thompson 		printf("usb_msc_eject: unknown eject method (%d)\n", method);
795d84a79e7SAndrew Thompson 		break;
796d84a79e7SAndrew Thompson 	}
797d84a79e7SAndrew Thompson 	DPRINTF("Eject CD command status: %s\n", usbd_errstr(err));
798d84a79e7SAndrew Thompson 
799d84a79e7SAndrew Thompson 	bbb_detach(sc);
800d84a79e7SAndrew Thompson 	return (0);
80102ac6454SAndrew Thompson }
802