usb_msctest.c revision 185290
167754Smsmith/* $FreeBSD: head/sys/dev/usb2/core/usb2_msctest.c 185290 2008-11-25 08:04:40Z alfred $ */
267754Smsmith/*-
367754Smsmith * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
467754Smsmith *
567754Smsmith * Redistribution and use in source and binary forms, with or without
667754Smsmith * modification, are permitted provided that the following conditions
7217365Sjkim * are met:
8278970Sjkim * 1. Redistributions of source code must retain the above copyright
970243Smsmith *    notice, this list of conditions and the following disclaimer.
1067754Smsmith * 2. Redistributions in binary form must reproduce the above copyright
11217365Sjkim *    notice, this list of conditions and the following disclaimer in the
12217365Sjkim *    documentation and/or other materials provided with the distribution.
13217365Sjkim *
14217365Sjkim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15217365Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16217365Sjkim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17217365Sjkim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18217365Sjkim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19217365Sjkim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20217365Sjkim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21217365Sjkim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22217365Sjkim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23217365Sjkim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24217365Sjkim * SUCH DAMAGE.
2567754Smsmith */
26217365Sjkim
27217365Sjkim/*
28217365Sjkim * The following file contains code that will detect USB autoinstall
2967754Smsmith * disks.
30217365Sjkim *
31217365Sjkim * TODO: Potentially we could add code to automatically detect USB
32217365Sjkim * mass storage quirks for not supported SCSI commands!
33217365Sjkim */
34217365Sjkim
35217365Sjkim#include <dev/usb2/include/usb2_defs.h>
36217365Sjkim#include <dev/usb2/include/usb2_mfunc.h>
37217365Sjkim#include <dev/usb2/include/usb2_error.h>
38217365Sjkim#include <dev/usb2/include/usb2_standard.h>
39217365Sjkim#include <dev/usb2/include/usb2_devid.h>
40217365Sjkim
41217365Sjkim#define	USB_DEBUG_VAR usb2_debug
42217365Sjkim
4367754Smsmith#include <dev/usb2/core/usb2_core.h>
44193341Sjkim#include <dev/usb2/core/usb2_busdma.h>
45193341Sjkim#include <dev/usb2/core/usb2_process.h>
46193341Sjkim#include <dev/usb2/core/usb2_transfer.h>
47193341Sjkim#include <dev/usb2/core/usb2_msctest.h>
48193341Sjkim#include <dev/usb2/core/usb2_debug.h>
49193341Sjkim#include <dev/usb2/core/usb2_busdma.h>
50193341Sjkim#include <dev/usb2/core/usb2_device.h>
51193341Sjkim#include <dev/usb2/core/usb2_request.h>
52193341Sjkim#include <dev/usb2/core/usb2_util.h>
5367754Smsmith#include <dev/usb2/core/usb2_lookup.h>
5467754Smsmith
55102550Siwasaki#include <dev/usb2/include/usb2_mfunc.h>
5667754Smsmith#include <dev/usb2/include/usb2_error.h>
57102550Siwasaki#include <dev/usb2/include/usb2_standard.h>
5891116Smsmith
5967754Smsmithenum {
60151937Sjkim	ST_COMMAND,
6167754Smsmith	ST_DATA_RD,
62151937Sjkim	ST_DATA_RD_CS,
63151937Sjkim	ST_DATA_WR,
64151937Sjkim	ST_DATA_WR_CS,
65151937Sjkim	ST_STATUS,
66151937Sjkim	ST_MAX,
67151937Sjkim};
68151937Sjkim
69151937Sjkimenum {
70249663Sjkim	DIR_IN,
71249663Sjkim	DIR_OUT,
72249663Sjkim	DIR_NONE,
73249663Sjkim};
74249663Sjkim
75249663Sjkim#define	BULK_SIZE		64	/* dummy */
76151937Sjkim
77220663Sjkim/* Command Block Wrapper */
78220663Sjkimstruct bbb_cbw {
79220663Sjkim	uDWord	dCBWSignature;
80220663Sjkim#define	CBWSIGNATURE	0x43425355
81220663Sjkim	uDWord	dCBWTag;
82220663Sjkim	uDWord	dCBWDataTransferLength;
83220663Sjkim	uByte	bCBWFlags;
84249663Sjkim#define	CBWFLAGS_OUT	0x00
85220663Sjkim#define	CBWFLAGS_IN	0x80
86220663Sjkim	uByte	bCBWLUN;
87220663Sjkim	uByte	bCDBLength;
88220663Sjkim#define	CBWCDBLENGTH	16
89220663Sjkim	uByte	CBWCDB[CBWCDBLENGTH];
90220663Sjkim} __packed;
91220663Sjkim
92220663Sjkim/* Command Status Wrapper */
93220663Sjkimstruct bbb_csw {
94220663Sjkim	uDWord	dCSWSignature;
95220663Sjkim#define	CSWSIGNATURE	0x53425355
96220663Sjkim	uDWord	dCSWTag;
97220663Sjkim	uDWord	dCSWDataResidue;
98220663Sjkim	uByte	bCSWStatus;
99228110Sjkim#define	CSWSTATUS_GOOD	0x0
100228110Sjkim#define	CSWSTATUS_FAILED	0x1
101220663Sjkim#define	CSWSTATUS_PHASE	0x2
102220663Sjkim} __packed;
103220663Sjkim
104220663Sjkimstruct bbb_transfer {
105220663Sjkim	struct mtx mtx;
106220663Sjkim	struct cv cv;
107220663Sjkim	struct bbb_cbw cbw;
108220663Sjkim	struct bbb_csw csw;
109220663Sjkim
110220663Sjkim	struct usb2_xfer *xfer[ST_MAX];
111220663Sjkim
112220663Sjkim	uint8_t *data_ptr;
113220663Sjkim
114220663Sjkim	uint32_t data_len;		/* bytes */
115220663Sjkim	uint32_t data_rem;		/* bytes */
116234623Sjkim	uint32_t data_timeout;		/* ms */
117234623Sjkim	uint32_t actlen;		/* bytes */
118220663Sjkim
119220663Sjkim	uint8_t	cmd_len;		/* bytes */
120220663Sjkim	uint8_t	dir;
121220663Sjkim	uint8_t	lun;
122220663Sjkim	uint8_t	state;
123220663Sjkim	uint8_t	error;
124151937Sjkim	uint8_t	status_try;
12567754Smsmith
12667754Smsmith	uint8_t	buffer[256];
12767754Smsmith};
12867754Smsmith
12967754Smsmithstatic usb2_callback_t bbb_command_callback;
13067754Smsmithstatic usb2_callback_t bbb_data_read_callback;
13167754Smsmithstatic usb2_callback_t bbb_data_rd_cs_callback;
13267754Smsmithstatic usb2_callback_t bbb_data_write_callback;
13367754Smsmithstatic usb2_callback_t bbb_data_wr_cs_callback;
134151937Sjkimstatic usb2_callback_t bbb_status_callback;
13567754Smsmith
136151937Sjkimstatic const struct usb2_config bbb_config[ST_MAX] = {
13767754Smsmith
13867754Smsmith	[ST_COMMAND] = {
13967754Smsmith		.type = UE_BULK,
14067754Smsmith		.endpoint = UE_ADDR_ANY,
141252279Sjkim		.direction = UE_DIR_OUT,
14267754Smsmith		.mh.bufsize = sizeof(struct bbb_cbw),
14367754Smsmith		.mh.flags = {},
144284583Sjkim		.mh.callback = &bbb_command_callback,
145252279Sjkim		.mh.timeout = 4 * USB_MS_HZ,	/* 4 seconds */
14667754Smsmith	},
14767754Smsmith
14867754Smsmith	[ST_DATA_RD] = {
14967754Smsmith		.type = UE_BULK,
15067754Smsmith		.endpoint = UE_ADDR_ANY,
15167754Smsmith		.direction = UE_DIR_IN,
15267754Smsmith		.mh.bufsize = BULK_SIZE,
15367754Smsmith		.mh.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,},
15467754Smsmith		.mh.callback = &bbb_data_read_callback,
15567754Smsmith		.mh.timeout = 4 * USB_MS_HZ,	/* 4 seconds */
15667754Smsmith	},
15767754Smsmith
15867754Smsmith	[ST_DATA_RD_CS] = {
15967754Smsmith		.type = UE_CONTROL,
16067754Smsmith		.endpoint = 0x00,	/* Control pipe */
16167754Smsmith		.direction = UE_DIR_ANY,
162151937Sjkim		.mh.bufsize = sizeof(struct usb2_device_request),
16367754Smsmith		.mh.flags = {},
16467754Smsmith		.mh.callback = &bbb_data_rd_cs_callback,
16567754Smsmith		.mh.timeout = 1 * USB_MS_HZ,	/* 1 second  */
16683174Smsmith	},
16767754Smsmith
16867754Smsmith	[ST_DATA_WR] = {
16999679Siwasaki		.type = UE_BULK,
17067754Smsmith		.endpoint = UE_ADDR_ANY,
17167754Smsmith		.direction = UE_DIR_OUT,
17299679Siwasaki		.mh.bufsize = BULK_SIZE,
17367754Smsmith		.mh.flags = {.proxy_buffer = 1,},
174151937Sjkim		.mh.callback = &bbb_data_write_callback,
175151937Sjkim		.mh.timeout = 4 * USB_MS_HZ,	/* 4 seconds */
17667754Smsmith	},
17799679Siwasaki
17899679Siwasaki	[ST_DATA_WR_CS] = {
17999679Siwasaki		.type = UE_CONTROL,
18067754Smsmith		.endpoint = 0x00,	/* Control pipe */
18167754Smsmith		.direction = UE_DIR_ANY,
18267754Smsmith		.mh.bufsize = sizeof(struct usb2_device_request),
18367754Smsmith		.mh.flags = {},
18467754Smsmith		.mh.callback = &bbb_data_wr_cs_callback,
18567754Smsmith		.mh.timeout = 1 * USB_MS_HZ,	/* 1 second  */
18667754Smsmith	},
187241973Sjkim
18867754Smsmith	[ST_STATUS] = {
18967754Smsmith		.type = UE_BULK,
19067754Smsmith		.endpoint = UE_ADDR_ANY,
19167754Smsmith		.direction = UE_DIR_IN,
19267754Smsmith		.mh.bufsize = sizeof(struct bbb_csw),
19367754Smsmith		.mh.flags = {.short_xfer_ok = 1,},
19467754Smsmith		.mh.callback = &bbb_status_callback,
19567754Smsmith		.mh.timeout = 1 * USB_MS_HZ,	/* 1 second  */
19667754Smsmith	},
19767754Smsmith};
19867754Smsmith
199114237Snjlstatic void
200114237Snjlbbb_done(struct bbb_transfer *sc, uint8_t error)
20167754Smsmith{
20267754Smsmith	struct usb2_xfer *xfer;
20367754Smsmith
20487031Smsmith	xfer = sc->xfer[sc->state];
20567754Smsmith
206114237Snjl	/* verify the error code */
20767754Smsmith
20867754Smsmith	if (error) {
20967754Smsmith		switch (USB_GET_STATE(xfer)) {
21067754Smsmith		case USB_ST_SETUP:
21167754Smsmith		case USB_ST_TRANSFERRED:
21267754Smsmith			error = 1;
21367754Smsmith			break;
21467754Smsmith		default:
21567754Smsmith			error = 2;
21667754Smsmith			break;
21767754Smsmith		}
21867754Smsmith	}
21967754Smsmith	sc->error = error;
22067754Smsmith	sc->state = ST_COMMAND;
221151937Sjkim	sc->status_try = 1;
22267754Smsmith	usb2_cv_signal(&sc->cv);
22367754Smsmith	return;
22467754Smsmith}
22567754Smsmith
22667754Smsmithstatic void
22767754Smsmithbbb_transfer_start(struct bbb_transfer *sc, uint8_t xfer_index)
22867754Smsmith{
22967754Smsmith	sc->state = xfer_index;
23067754Smsmith	usb2_transfer_start(sc->xfer[xfer_index]);
23167754Smsmith	return;
23267754Smsmith}
23367754Smsmith
23467754Smsmithstatic void
23567754Smsmithbbb_data_clear_stall_callback(struct usb2_xfer *xfer,
23667754Smsmith    uint8_t next_xfer, uint8_t stall_xfer)
23767754Smsmith{
23867754Smsmith	struct bbb_transfer *sc = xfer->priv_sc;
23967754Smsmith
24067754Smsmith	if (usb2_clear_stall_callback(xfer, sc->xfer[stall_xfer])) {
24167754Smsmith		switch (USB_GET_STATE(xfer)) {
24267754Smsmith		case USB_ST_SETUP:
24367754Smsmith		case USB_ST_TRANSFERRED:
24467754Smsmith			bbb_transfer_start(sc, next_xfer);
24567754Smsmith			break;
246151937Sjkim		default:
247151937Sjkim			bbb_done(sc, 1);
24867754Smsmith			break;
24967754Smsmith		}
25067754Smsmith	}
25167754Smsmith	return;
25267754Smsmith}
25391116Smsmith
25467754Smsmithstatic void
25591116Smsmithbbb_command_callback(struct usb2_xfer *xfer)
25667754Smsmith{
25791116Smsmith	struct bbb_transfer *sc = xfer->priv_sc;
25891116Smsmith	uint32_t tag;
25967754Smsmith
26067754Smsmith	switch (USB_GET_STATE(xfer)) {
261151937Sjkim	case USB_ST_TRANSFERRED:
262151937Sjkim		bbb_transfer_start
26367754Smsmith		    (sc, ((sc->dir == DIR_IN) ? ST_DATA_RD :
26467754Smsmith		    (sc->dir == DIR_OUT) ? ST_DATA_WR :
26567754Smsmith		    ST_STATUS));
26667754Smsmith		break;
267151937Sjkim
26867754Smsmith	case USB_ST_SETUP:
26999679Siwasaki		sc->status_try = 0;
27091116Smsmith		tag = UGETDW(sc->cbw.dCBWTag) + 1;
27191116Smsmith		USETDW(sc->cbw.dCBWSignature, CBWSIGNATURE);
27291116Smsmith		USETDW(sc->cbw.dCBWTag, tag);
27367754Smsmith		USETDW(sc->cbw.dCBWDataTransferLength, sc->data_len);
27467754Smsmith		sc->cbw.bCBWFlags = ((sc->dir == DIR_IN) ? CBWFLAGS_IN : CBWFLAGS_OUT);
275151937Sjkim		sc->cbw.bCBWLUN = sc->lun;
276151937Sjkim		sc->cbw.bCDBLength = sc->cmd_len;
27767754Smsmith		if (sc->cbw.bCDBLength > sizeof(sc->cbw.CBWCDB)) {
27867754Smsmith			sc->cbw.bCDBLength = sizeof(sc->cbw.CBWCDB);
27967754Smsmith			DPRINTFN(0, "Truncating long command!\n");
280241973Sjkim		}
281151937Sjkim		xfer->frlengths[0] = sizeof(sc->cbw);
28277424Smsmith
28391116Smsmith		usb2_set_frame_data(xfer, &sc->cbw, 0);
28467754Smsmith		usb2_start_hardware(xfer);
28591116Smsmith		break;
28691116Smsmith
28791116Smsmith	default:			/* Error */
28891116Smsmith		bbb_done(sc, 1);
28967754Smsmith		break;
29067754Smsmith	}
291151937Sjkim	return;
292151937Sjkim}
29367754Smsmith
29467754Smsmithstatic void
29567754Smsmithbbb_data_read_callback(struct usb2_xfer *xfer)
296241973Sjkim{
297151937Sjkim	struct bbb_transfer *sc = xfer->priv_sc;
29867754Smsmith	uint32_t max_bulk = xfer->max_data_length;
29991116Smsmith
30067754Smsmith	switch (USB_GET_STATE(xfer)) {
30191116Smsmith	case USB_ST_TRANSFERRED:
30291116Smsmith		sc->data_rem -= xfer->actlen;
30391116Smsmith		sc->data_ptr += xfer->actlen;
30491116Smsmith		sc->actlen += xfer->actlen;
305272444Sjkim
306272444Sjkim		if (xfer->actlen < xfer->sumlen) {
307272444Sjkim			/* short transfer */
308272444Sjkim			sc->data_rem = 0;
30967754Smsmith		}
31067754Smsmith	case USB_ST_SETUP:
31167754Smsmith		DPRINTF("max_bulk=%d, data_rem=%d\n",
31267754Smsmith		    max_bulk, sc->data_rem);
31367754Smsmith
31467754Smsmith		if (sc->data_rem == 0) {
31567754Smsmith			bbb_transfer_start(sc, ST_STATUS);
31667754Smsmith			break;
317241973Sjkim		}
31891116Smsmith		if (max_bulk > sc->data_rem) {
31967754Smsmith			max_bulk = sc->data_rem;
32067754Smsmith		}
32167754Smsmith		xfer->timeout = sc->data_timeout;
32267754Smsmith		xfer->frlengths[0] = max_bulk;
32367754Smsmith
32467754Smsmith		usb2_set_frame_data(xfer, sc->data_ptr, 0);
32567754Smsmith		usb2_start_hardware(xfer);
32667754Smsmith		break;
32767754Smsmith
32867754Smsmith	default:			/* Error */
32967754Smsmith		if (xfer->error == USB_ERR_CANCELLED) {
33067754Smsmith			bbb_done(sc, 1);
33167754Smsmith		} else {
33267754Smsmith			bbb_transfer_start(sc, ST_DATA_RD_CS);
333151937Sjkim		}
334151937Sjkim		break;
33567754Smsmith	}
33667754Smsmith	return;
33767754Smsmith}
33867754Smsmith
33967754Smsmithstatic void
34067754Smsmithbbb_data_rd_cs_callback(struct usb2_xfer *xfer)
34167754Smsmith{
34269746Smsmith	bbb_data_clear_stall_callback(xfer, ST_STATUS,
34369746Smsmith	    ST_DATA_RD);
344151937Sjkim	return;
345151937Sjkim}
34669746Smsmith
34769746Smsmithstatic void
34867754Smsmithbbb_data_write_callback(struct usb2_xfer *xfer)
34967754Smsmith{
35067754Smsmith	struct bbb_transfer *sc = xfer->priv_sc;
35167754Smsmith	uint32_t max_bulk = xfer->max_data_length;
35267754Smsmith
35367754Smsmith	switch (USB_GET_STATE(xfer)) {
354241973Sjkim	case USB_ST_TRANSFERRED:
355151937Sjkim		sc->data_rem -= xfer->actlen;
356167802Sjkim		sc->data_ptr += xfer->actlen;
35767754Smsmith		sc->actlen += xfer->actlen;
35887031Smsmith
35987031Smsmith		if (xfer->actlen < xfer->sumlen) {
36067754Smsmith			/* short transfer */
36187031Smsmith			sc->data_rem = 0;
36287031Smsmith		}
36367754Smsmith	case USB_ST_SETUP:
364151937Sjkim		DPRINTF("max_bulk=%d, data_rem=%d\n",
365151937Sjkim		    max_bulk, sc->data_rem);
36667754Smsmith
36767754Smsmith		if (sc->data_rem == 0) {
36867754Smsmith			bbb_transfer_start(sc, ST_STATUS);
369241973Sjkim			return;
370151937Sjkim		}
37187031Smsmith		if (max_bulk > sc->data_rem) {
37267754Smsmith			max_bulk = sc->data_rem;
37367754Smsmith		}
37467754Smsmith		xfer->timeout = sc->data_timeout;
37567754Smsmith		xfer->frlengths[0] = max_bulk;
37667754Smsmith
37767754Smsmith		usb2_set_frame_data(xfer, sc->data_ptr, 0);
37867754Smsmith		usb2_start_hardware(xfer);
37967754Smsmith		return;
38067754Smsmith
38167754Smsmith	default:			/* Error */
38267754Smsmith		if (xfer->error == USB_ERR_CANCELLED) {
38367754Smsmith			bbb_done(sc, 1);
38467754Smsmith		} else {
38567754Smsmith			bbb_transfer_start(sc, ST_DATA_WR_CS);
38667754Smsmith		}
38767754Smsmith		return;
38867754Smsmith
38967754Smsmith	}
39067754Smsmith}
39167754Smsmith
39267754Smsmithstatic void
39367754Smsmithbbb_data_wr_cs_callback(struct usb2_xfer *xfer)
39467754Smsmith{
39567754Smsmith	bbb_data_clear_stall_callback(xfer, ST_STATUS,
39667754Smsmith	    ST_DATA_WR);
39783174Smsmith	return;
39867754Smsmith}
39967754Smsmith
40067754Smsmithstatic void
40167754Smsmithbbb_status_callback(struct usb2_xfer *xfer)
40267754Smsmith{
40367754Smsmith	struct bbb_transfer *sc = xfer->priv_sc;
40467754Smsmith
40567754Smsmith	switch (USB_GET_STATE(xfer)) {
40667754Smsmith	case USB_ST_TRANSFERRED:
40767754Smsmith
40867754Smsmith		/* very simple status check */
40967754Smsmith
41067754Smsmith		if (xfer->actlen < sizeof(sc->csw)) {
41167754Smsmith			bbb_done(sc, 1);/* error */
41267754Smsmith		} else if (sc->csw.bCSWStatus == CSWSTATUS_GOOD) {
41367754Smsmith			bbb_done(sc, 0);/* success */
41467754Smsmith		} else {
41587031Smsmith			bbb_done(sc, 1);/* error */
41667754Smsmith		}
417123315Snjl		break;
418123315Snjl
419167802Sjkim	case USB_ST_SETUP:
420167802Sjkim		xfer->frlengths[0] = sizeof(sc->csw);
421167802Sjkim
42267754Smsmith		usb2_set_frame_data(xfer, &sc->csw, 0);
42367754Smsmith		usb2_start_hardware(xfer);
42467754Smsmith		break;
42599679Siwasaki
42667754Smsmith	default:
42799679Siwasaki		DPRINTFN(0, "Failed to read CSW: %s, try %d\n",
42867754Smsmith		    usb2_errstr(xfer->error), sc->status_try);
42967754Smsmith
43067754Smsmith		if ((xfer->error == USB_ERR_CANCELLED) ||
43167754Smsmith		    (sc->status_try)) {
43267754Smsmith			bbb_done(sc, 1);
43367754Smsmith		} else {
43467754Smsmith			sc->status_try = 1;
43567754Smsmith			bbb_transfer_start(sc, ST_DATA_RD_CS);
43667754Smsmith		}
43767754Smsmith		break;
43867754Smsmith	}
43967754Smsmith	return;
44067754Smsmith}
44167754Smsmith
44267754Smsmith/*------------------------------------------------------------------------*
44367754Smsmith *	bbb_command_start - execute a SCSI command synchronously
44467754Smsmith *
44567754Smsmith * Return values
44667754Smsmith * 0: Success
44799679Siwasaki * Else: Failure
44885756Smsmith *------------------------------------------------------------------------*/
44967754Smsmithstatic uint8_t
45085756Smsmithbbb_command_start(struct bbb_transfer *sc, uint8_t dir, uint8_t lun,
451250838Sjkim    void *data_ptr, uint32_t data_len, uint8_t cmd_len,
45267754Smsmith    uint32_t data_timeout)
45367754Smsmith{
45467754Smsmith	sc->lun = lun;
45567754Smsmith	sc->dir = data_len ? dir : DIR_NONE;
45667754Smsmith	sc->data_ptr = data_ptr;
45767754Smsmith	sc->data_len = data_len;
45867754Smsmith	sc->data_rem = data_len;
45967754Smsmith	sc->data_timeout = (data_timeout + USB_MS_HZ);
46085756Smsmith	sc->actlen = 0;
461250838Sjkim	sc->cmd_len = cmd_len;
46285756Smsmith
46385756Smsmith	usb2_transfer_start(sc->xfer[sc->state]);
46485756Smsmith
46585756Smsmith	while (usb2_transfer_pending(sc->xfer[sc->state])) {
46667754Smsmith		usb2_cv_wait(&sc->cv, &sc->mtx);
467250838Sjkim	}
46867754Smsmith	return (sc->error);
46967754Smsmith}
47067754Smsmith
47167754Smsmith/*------------------------------------------------------------------------*
47267754Smsmith *	usb2_test_autoinstall
47367754Smsmith *
47467754Smsmith * Return values:
47567754Smsmith * 0: This interface is an auto install disk (CD-ROM)
47667754Smsmith * Else: Not an auto install disk.
47767754Smsmith *------------------------------------------------------------------------*/
47867754Smsmithusb2_error_t
47967754Smsmithusb2_test_autoinstall(struct usb2_device *udev, uint8_t iface_index,
480151937Sjkim    uint8_t do_eject)
481151937Sjkim{
482151937Sjkim	struct usb2_interface *iface;
48367754Smsmith	struct usb2_interface_descriptor *id;
484151937Sjkim	usb2_error_t err;
485151937Sjkim	uint8_t timeout;
486151937Sjkim	uint8_t sid_type;
48767754Smsmith	struct bbb_transfer *sc;
48867754Smsmith
48967754Smsmith	if (udev == NULL) {
49067754Smsmith		return (USB_ERR_INVAL);
49167754Smsmith	}
49267754Smsmith	iface = usb2_get_iface(udev, iface_index);
49367754Smsmith	if (iface == NULL) {
49467754Smsmith		return (USB_ERR_INVAL);
49567754Smsmith	}
49667754Smsmith	id = iface->idesc;
49767754Smsmith	if (id == NULL) {
49867754Smsmith		return (USB_ERR_INVAL);
49967754Smsmith	}
50067754Smsmith	if (id->bInterfaceClass != UICLASS_MASS) {
50167754Smsmith		return (USB_ERR_INVAL);
50267754Smsmith	}
503151937Sjkim	switch (id->bInterfaceSubClass) {
504151937Sjkim	case UISUBCLASS_SCSI:
50567754Smsmith	case UISUBCLASS_UFI:
50667754Smsmith		break;
50767754Smsmith	default:
50867754Smsmith		return (USB_ERR_INVAL);
50967754Smsmith	}
51067754Smsmith
51167754Smsmith	switch (id->bInterfaceProtocol) {
51267754Smsmith	case UIPROTO_MASS_BBB_OLD:
51367754Smsmith	case UIPROTO_MASS_BBB:
51467754Smsmith		break;
51567754Smsmith	default:
516117521Snjl		return (USB_ERR_INVAL);
51767754Smsmith	}
51867754Smsmith
51967754Smsmith	sc = malloc(sizeof(*sc), M_USB, M_WAITOK | M_ZERO);
52067754Smsmith	if (sc == NULL) {
52167754Smsmith		return (USB_ERR_NOMEM);
52267754Smsmith	}
52367754Smsmith	mtx_init(&sc->mtx, "USB autoinstall", NULL, MTX_DEF);
52467754Smsmith	usb2_cv_init(&sc->cv, "WBBB");
52567754Smsmith
52667754Smsmith	err = usb2_transfer_setup(udev,
52767754Smsmith	    &iface_index, sc->xfer, bbb_config,
52867754Smsmith	    ST_MAX, sc, &sc->mtx);
52967754Smsmith
53067754Smsmith	if (err) {
53167754Smsmith		goto done;
53267754Smsmith	}
533151937Sjkim	mtx_lock(&sc->mtx);
534151937Sjkim
53567754Smsmith	timeout = 4;			/* tries */
53667754Smsmith
53767754Smsmithrepeat_inquiry:
53867754Smsmith
53967754Smsmith	sc->cbw.CBWCDB[0] = 0x12;	/* INQUIRY */
54067754Smsmith	sc->cbw.CBWCDB[1] = 0;
54167754Smsmith	sc->cbw.CBWCDB[2] = 0;
54267754Smsmith	sc->cbw.CBWCDB[3] = 0;
54367754Smsmith	sc->cbw.CBWCDB[4] = 0x24;	/* length */
54467754Smsmith	sc->cbw.CBWCDB[5] = 0;
54567754Smsmith	err = bbb_command_start(sc, DIR_IN, 0,
546117521Snjl	    sc->buffer, 0x24, 6, USB_MS_HZ);
54767754Smsmith
54867754Smsmith	if ((sc->actlen != 0) && (err == 0)) {
54967754Smsmith		sid_type = sc->buffer[0] & 0x1F;
55067754Smsmith		if (sid_type == 0x05) {
55167754Smsmith			/* CD-ROM */
55267754Smsmith			if (do_eject) {
55367754Smsmith				/* 0: opcode: SCSI START/STOP */
55467754Smsmith				sc->cbw.CBWCDB[0] = 0x1b;
55567754Smsmith				/* 1: byte2: Not immediate */
55667754Smsmith				sc->cbw.CBWCDB[1] = 0x00;
55767754Smsmith				/* 2..3: reserved */
55867754Smsmith				sc->cbw.CBWCDB[2] = 0x00;
55967754Smsmith				sc->cbw.CBWCDB[3] = 0x00;
56067754Smsmith				/* 4: Load/Eject command */
56167754Smsmith				sc->cbw.CBWCDB[4] = 0x02;
56267754Smsmith				/* 5: control */
563151937Sjkim				sc->cbw.CBWCDB[5] = 0x00;
564151937Sjkim				err = bbb_command_start(sc, DIR_OUT, 0,
56567754Smsmith				    NULL, 0, 6, USB_MS_HZ);
56667754Smsmith
56767754Smsmith				DPRINTFN(0, "Eject CD command "
56867754Smsmith				    "status: %s\n", usb2_errstr(err));
569167802Sjkim			}
57067754Smsmith			err = 0;
571167802Sjkim			goto done;
572167802Sjkim		}
57367754Smsmith	} else if ((err != 2) && --timeout) {
57467754Smsmith		usb2_pause_mtx(&sc->mtx, USB_MS_HZ);
57567754Smsmith		goto repeat_inquiry;
57667754Smsmith	}
57767754Smsmith	err = USB_ERR_INVAL;
57867754Smsmith	goto done;
57967754Smsmith
58067754Smsmithdone:
58167754Smsmith	mtx_unlock(&sc->mtx);
58267754Smsmith	usb2_transfer_unsetup(sc->xfer, ST_MAX);
583123315Snjl	mtx_destroy(&sc->mtx);
58467754Smsmith	usb2_cv_destroy(&sc->cv);
58569746Smsmith	free(sc, M_USB);
58669746Smsmith	return (err);
587167802Sjkim}
58869746Smsmith
58967754Smsmith/*
59087031Smsmith * NOTE: The entries marked with XXX should be checked for the correct
591167802Sjkim * speed indication to set the buffer sizes.
59269746Smsmith */
593167802Sjkimstatic const struct usb2_device_id u3g_devs[] = {
594167802Sjkim	/* OEM: Option */
595167802Sjkim	{USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3G, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},
596167802Sjkim	{USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GQUAD, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},
597167802Sjkim	{USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GPLUS, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},
598167802Sjkim	{USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAX36, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))},
59967754Smsmith	{USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAXHSUPA, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))},
600167802Sjkim	{USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_VODAFONEMC3G, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},
601209746Sjkim	/* OEM: Qualcomm, Inc. */
602117521Snjl	{USB_VPI(USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_ZTE_STOR, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))},
603167802Sjkim	{USB_VPI(USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_CDMA_MSM, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))},
604167802Sjkim	/* OEM: Huawei */
605167802Sjkim	{USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE, U3GINFO(U3GSP_HSDPA, U3GFL_HUAWEI_INIT))},
606167802Sjkim	{USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220, U3GINFO(U3GSP_HSPA, U3GFL_HUAWEI_INIT))},
607167802Sjkim	/* OEM: Novatel */
608167802Sjkim	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_CDMA_MODEM, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))},
60967754Smsmith	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ES620, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))},	/* XXX */
61067754Smsmith	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_MC950D, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))},
61167754Smsmith	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U720, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))},	/* XXX */
61267754Smsmith	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U727, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))},	/* XXX */
61367754Smsmith	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))},
61467754Smsmith	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740_2, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))},
61567754Smsmith	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U870, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))},	/* XXX */
61667754Smsmith	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V620, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))},	/* XXX */
61767754Smsmith	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V640, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))},	/* XXX */
61867754Smsmith	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V720, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))},	/* XXX */
61967754Smsmith	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))},
62067754Smsmith	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_X950D, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))},
62167754Smsmith	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_XU870, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))},
62267754Smsmith	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ZEROCD, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))},
62367754Smsmith	{USB_VPI(USB_VENDOR_DELL, USB_PRODUCT_DELL_U740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))},
62467754Smsmith	/* OEM: Merlin */
62567754Smsmith	{USB_VPI(USB_VENDOR_MERLIN, USB_PRODUCT_MERLIN_V620, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
626151937Sjkim	/* OEM: Sierra Wireless: */
627151937Sjkim	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD580, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
62867754Smsmith	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD595, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
62967754Smsmith	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC595U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
63067754Smsmith	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC597E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
63167754Smsmith	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_C597, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
63267754Smsmith	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
63367754Smsmith	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
63467754Smsmith	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
63567754Smsmith	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
63667754Smsmith	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
63767754Smsmith	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
63867754Smsmith	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_EM5625, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
63967754Smsmith	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
64069450Smsmith	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720_2, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
64167754Smsmith	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5725, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
64267754Smsmith	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MINI5725, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
64399679Siwasaki	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD875, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
64467754Smsmith	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
64569450Smsmith	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_2, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
64667754Smsmith	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_3, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
647123315Snjl	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8765, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
64867754Smsmith	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC875U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
64967754Smsmith	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8775_2, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
65067754Smsmith	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8780, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
65167754Smsmith	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8781, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},	/* XXX */
65267754Smsmith	/* Sierra TruInstaller device ID */
65367754Smsmith	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_TRUINSTALL, U3GINFO(U3GSP_UMTS, U3GFL_SIERRA_INIT))},
65467754Smsmith};
65567754Smsmith
656117521Snjlstatic void
657117521Snjlu3g_sierra_init(struct usb2_device *udev)
658241973Sjkim{
659117521Snjl	struct usb2_device_request req;
660117521Snjl
661117521Snjl	DPRINTFN(0, "\n");
662123315Snjl
663117521Snjl	req.bmRequestType = UT_VENDOR;
664117521Snjl	req.bRequest = UR_SET_INTERFACE;
665117521Snjl	USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
666117521Snjl	USETW(req.wIndex, UHF_PORT_CONNECTION);
667117521Snjl	USETW(req.wLength, 0);
668241973Sjkim
669117521Snjl	if (usb2_do_request_flags(udev, NULL, &req,
670241973Sjkim	    NULL, 0, NULL, USB_MS_HZ)) {
671117521Snjl		/* ignore any errors */
672117521Snjl	}
673193267Sjkim	return;
674117521Snjl}
675117521Snjl
676241973Sjkimstatic void
677241973Sjkimu3g_huawei_init(struct usb2_device *udev)
678241973Sjkim{
679241973Sjkim	struct usb2_device_request req;
680241973Sjkim
681117521Snjl	DPRINTFN(0, "\n");
682241973Sjkim
683197104Sjkim	req.bmRequestType = UT_WRITE_DEVICE;
684117521Snjl	req.bRequest = UR_SET_FEATURE;
685197104Sjkim	USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
686197104Sjkim	USETW(req.wIndex, UHF_PORT_SUSPEND);
687197104Sjkim	USETW(req.wLength, 0);
688197104Sjkim
689117521Snjl	if (usb2_do_request_flags(udev, NULL, &req,
690241973Sjkim	    NULL, 0, NULL, USB_MS_HZ)) {
691241973Sjkim		/* ignore any errors */
692241973Sjkim	}
693241973Sjkim	return;
694241973Sjkim}
695241973Sjkim
696241973Sjkimint
697241973Sjkimusb2_lookup_huawei(struct usb2_attach_arg *uaa)
698241973Sjkim{
699241973Sjkim	/* Calling the lookup function will also set the driver info! */
700241973Sjkim	return (usb2_lookup_id_by_uaa(u3g_devs, sizeof(u3g_devs), uaa));
701241973Sjkim}
702241973Sjkim
703241973Sjkim/*
704241973Sjkim * The following function handles 3G modem devices (E220, Mobile,
705241973Sjkim * etc.) with auto-install flash disks for Windows/MacOSX on the first
706241973Sjkim * interface.  After some command or some delay they change appearance
707241973Sjkim * to a modem.
708241973Sjkim */
709117521Snjlusb2_error_t
710197104Sjkimusb2_test_huawei(struct usb2_device *udev, struct usb2_attach_arg *uaa)
711197104Sjkim{
712197104Sjkim	struct usb2_interface *iface;
713117521Snjl	struct usb2_interface_descriptor *id;
714197104Sjkim	uint32_t flags;
715117521Snjl
716197104Sjkim	if (udev == NULL) {
717117521Snjl		return (USB_ERR_INVAL);
718241973Sjkim	}
719241973Sjkim	iface = usb2_get_iface(udev, 0);
720241973Sjkim	if (iface == NULL) {
721241973Sjkim		return (USB_ERR_INVAL);
722197104Sjkim	}
723197104Sjkim	id = iface->idesc;
724197104Sjkim	if (id == NULL) {
725197104Sjkim		return (USB_ERR_INVAL);
726209746Sjkim	}
727197104Sjkim	if (id->bInterfaceClass != UICLASS_MASS) {
728197104Sjkim		return (USB_ERR_INVAL);
729197104Sjkim	}
730197104Sjkim	if (usb2_lookup_huawei(uaa)) {
731197104Sjkim		/* no device match */
732117521Snjl		return (USB_ERR_INVAL);
733117521Snjl	}
734117521Snjl	flags = USB_GET_DRIVER_INFO(uaa);
735117521Snjl
736117521Snjl	if (flags & U3GFL_HUAWEI_INIT) {
73767754Smsmith		u3g_huawei_init(udev);
73867754Smsmith	} else if (flags & U3GFL_SCSI_EJECT) {
73967754Smsmith		return (usb2_test_autoinstall(udev, 0, 1));
74067754Smsmith	} else if (flags & U3GFL_SIERRA_INIT) {
74167754Smsmith		u3g_sierra_init(udev);
74267754Smsmith	} else {
74367754Smsmith		/* no quirks */
74467754Smsmith		return (USB_ERR_INVAL);
74567754Smsmith	}
74691116Smsmith	return (0);			/* success */
74791116Smsmith}
74891116Smsmith