xref: /dflybsd-src/sys/bus/u4b/storage/umass.c (revision 2b3f93ea6d1f70880f3e87f3c2cbe0dc0bfc9332)
112bd3c8bSSascha Wildner /*-
212bd3c8bSSascha Wildner  * Copyright (c) 1999 MAEKAWA Masahide <bishop@rr.iij4u.or.jp>,
312bd3c8bSSascha Wildner  *		      Nick Hibma <n_hibma@FreeBSD.org>
412bd3c8bSSascha Wildner  * All rights reserved.
512bd3c8bSSascha Wildner  *
612bd3c8bSSascha Wildner  * Redistribution and use in source and binary forms, with or without
712bd3c8bSSascha Wildner  * modification, are permitted provided that the following conditions
812bd3c8bSSascha Wildner  * are met:
912bd3c8bSSascha Wildner  * 1. Redistributions of source code must retain the above copyright
1012bd3c8bSSascha Wildner  *    notice, this list of conditions and the following disclaimer.
1112bd3c8bSSascha Wildner  * 2. Redistributions in binary form must reproduce the above copyright
1212bd3c8bSSascha Wildner  *    notice, this list of conditions and the following disclaimer in the
1312bd3c8bSSascha Wildner  *    documentation and/or other materials provided with the distribution.
1412bd3c8bSSascha Wildner  *
1512bd3c8bSSascha Wildner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1612bd3c8bSSascha Wildner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1712bd3c8bSSascha Wildner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1812bd3c8bSSascha Wildner  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1912bd3c8bSSascha Wildner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2012bd3c8bSSascha Wildner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2112bd3c8bSSascha Wildner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2212bd3c8bSSascha Wildner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2312bd3c8bSSascha Wildner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2412bd3c8bSSascha Wildner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2512bd3c8bSSascha Wildner  * SUCH DAMAGE.
2612bd3c8bSSascha Wildner  *
2712bd3c8bSSascha Wildner  *	$FreeBSD$
2812bd3c8bSSascha Wildner  *	$NetBSD: umass.c,v 1.28 2000/04/02 23:46:53 augustss Exp $
2912bd3c8bSSascha Wildner  */
3012bd3c8bSSascha Wildner 
3112bd3c8bSSascha Wildner /* Also already merged from NetBSD:
3212bd3c8bSSascha Wildner  *	$NetBSD: umass.c,v 1.67 2001/11/25 19:05:22 augustss Exp $
3312bd3c8bSSascha Wildner  *	$NetBSD: umass.c,v 1.90 2002/11/04 19:17:33 pooka Exp $
3412bd3c8bSSascha Wildner  *	$NetBSD: umass.c,v 1.108 2003/11/07 17:03:25 wiz Exp $
3512bd3c8bSSascha Wildner  *	$NetBSD: umass.c,v 1.109 2003/12/04 13:57:31 keihan Exp $
3612bd3c8bSSascha Wildner  */
3712bd3c8bSSascha Wildner 
3812bd3c8bSSascha Wildner /*
3912bd3c8bSSascha Wildner  * Universal Serial Bus Mass Storage Class specs:
4012bd3c8bSSascha Wildner  * http://www.usb.org/developers/devclass_docs/usb_msc_overview_1.2.pdf
4112bd3c8bSSascha Wildner  * http://www.usb.org/developers/devclass_docs/usbmassbulk_10.pdf
4212bd3c8bSSascha Wildner  * http://www.usb.org/developers/devclass_docs/usb_msc_cbi_1.1.pdf
4312bd3c8bSSascha Wildner  * http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf
4412bd3c8bSSascha Wildner  */
4512bd3c8bSSascha Wildner 
4612bd3c8bSSascha Wildner /*
4712bd3c8bSSascha Wildner  * Ported to NetBSD by Lennart Augustsson <augustss@NetBSD.org>.
4812bd3c8bSSascha Wildner  * Parts of the code written by Jason R. Thorpe <thorpej@shagadelic.org>.
4912bd3c8bSSascha Wildner  */
5012bd3c8bSSascha Wildner 
5112bd3c8bSSascha Wildner /*
5212bd3c8bSSascha Wildner  * The driver handles 3 Wire Protocols
5312bd3c8bSSascha Wildner  * - Command/Bulk/Interrupt (CBI)
5412bd3c8bSSascha Wildner  * - Command/Bulk/Interrupt with Command Completion Interrupt (CBI with CCI)
5512bd3c8bSSascha Wildner  * - Mass Storage Bulk-Only (BBB)
5612bd3c8bSSascha Wildner  *   (BBB refers Bulk/Bulk/Bulk for Command/Data/Status phases)
5712bd3c8bSSascha Wildner  *
5812bd3c8bSSascha Wildner  * Over these wire protocols it handles the following command protocols
5912bd3c8bSSascha Wildner  * - SCSI
6012bd3c8bSSascha Wildner  * - UFI (floppy command set)
6112bd3c8bSSascha Wildner  * - 8070i (ATAPI)
6212bd3c8bSSascha Wildner  *
6312bd3c8bSSascha Wildner  * UFI and 8070i (ATAPI) are transformed versions of the SCSI command set. The
6412bd3c8bSSascha Wildner  * sc->sc_transform method is used to convert the commands into the appropriate
6512bd3c8bSSascha Wildner  * format (if at all necessary). For example, UFI requires all commands to be
6612bd3c8bSSascha Wildner  * 12 bytes in length amongst other things.
6712bd3c8bSSascha Wildner  *
6812bd3c8bSSascha Wildner  * The source code below is marked and can be split into a number of pieces
6912bd3c8bSSascha Wildner  * (in this order):
7012bd3c8bSSascha Wildner  *
7112bd3c8bSSascha Wildner  * - probe/attach/detach
7212bd3c8bSSascha Wildner  * - generic transfer routines
7312bd3c8bSSascha Wildner  * - BBB
7412bd3c8bSSascha Wildner  * - CBI
7512bd3c8bSSascha Wildner  * - CBI_I (in addition to functions from CBI)
7612bd3c8bSSascha Wildner  * - CAM (Common Access Method)
7712bd3c8bSSascha Wildner  * - SCSI
7812bd3c8bSSascha Wildner  * - UFI
7912bd3c8bSSascha Wildner  * - 8070i (ATAPI)
8012bd3c8bSSascha Wildner  *
8112bd3c8bSSascha Wildner  * The protocols are implemented using a state machine, for the transfers as
8212bd3c8bSSascha Wildner  * well as for the resets. The state machine is contained in umass_t_*_callback.
8312bd3c8bSSascha Wildner  * The state machine is started through either umass_command_start() or
8412bd3c8bSSascha Wildner  * umass_reset().
8512bd3c8bSSascha Wildner  *
8612bd3c8bSSascha Wildner  * The reason for doing this is a) CAM performs a lot better this way and b) it
8712bd3c8bSSascha Wildner  * avoids using tsleep from interrupt context (for example after a failed
8812bd3c8bSSascha Wildner  * transfer).
8912bd3c8bSSascha Wildner  */
9012bd3c8bSSascha Wildner 
9112bd3c8bSSascha Wildner /*
9212bd3c8bSSascha Wildner  * The SCSI related part of this driver has been derived from the
9312bd3c8bSSascha Wildner  * dev/ppbus/vpo.c driver, by Nicolas Souchu (nsouch@FreeBSD.org).
9412bd3c8bSSascha Wildner  *
9512bd3c8bSSascha Wildner  * The CAM layer uses so called actions which are messages sent to the host
9612bd3c8bSSascha Wildner  * adapter for completion. The actions come in through umass_cam_action. The
9712bd3c8bSSascha Wildner  * appropriate block of routines is called depending on the transport protocol
9812bd3c8bSSascha Wildner  * in use. When the transfer has finished, these routines call
9912bd3c8bSSascha Wildner  * umass_cam_cb again to complete the CAM command.
10012bd3c8bSSascha Wildner  */
10112bd3c8bSSascha Wildner 
10212bd3c8bSSascha Wildner #include <sys/stdint.h>
10312bd3c8bSSascha Wildner #include <sys/param.h>
10412bd3c8bSSascha Wildner #include <sys/queue.h>
10512bd3c8bSSascha Wildner #include <sys/types.h>
10612bd3c8bSSascha Wildner #include <sys/systm.h>
10712bd3c8bSSascha Wildner #include <sys/kernel.h>
10812bd3c8bSSascha Wildner #include <sys/bus.h>
10912bd3c8bSSascha Wildner #include <sys/module.h>
11012bd3c8bSSascha Wildner #include <sys/lock.h>
11112bd3c8bSSascha Wildner #include <sys/condvar.h>
11212bd3c8bSSascha Wildner #include <sys/sysctl.h>
11312bd3c8bSSascha Wildner #include <sys/unistd.h>
11412bd3c8bSSascha Wildner #include <sys/callout.h>
11512bd3c8bSSascha Wildner #include <sys/malloc.h>
116*2b3f93eaSMatthew Dillon #include <sys/caps.h>
11712bd3c8bSSascha Wildner 
118722d05c3SSascha Wildner #include <bus/u4b/usb.h>
119722d05c3SSascha Wildner #include <bus/u4b/usbdi.h>
120722d05c3SSascha Wildner #include <bus/u4b/usbdi_util.h>
121ef4aa9ffSSascha Wildner #include "usbdevs.h"
12263da4a34SSascha Wildner 
123722d05c3SSascha Wildner #include <bus/u4b/quirk/usb_quirk.h>
12412bd3c8bSSascha Wildner 
125722d05c3SSascha Wildner #include <bus/cam/cam.h>
126722d05c3SSascha Wildner #include <bus/cam/cam_ccb.h>
127722d05c3SSascha Wildner #include <bus/cam/cam_sim.h>
128cec957e9SMatthew Dillon #include <bus/cam/cam_xpt.h>
129722d05c3SSascha Wildner #include <bus/cam/cam_xpt_sim.h>
130cec957e9SMatthew Dillon #include <bus/cam/cam_xpt_periph.h>
131cec957e9SMatthew Dillon #include <bus/cam/cam_periph.h>
132722d05c3SSascha Wildner #include <bus/cam/scsi/scsi_all.h>
133722d05c3SSascha Wildner #include <bus/cam/scsi/scsi_da.h>
13412bd3c8bSSascha Wildner 
13557bed822SMarkus Pfeiffer #if 0
13612bd3c8bSSascha Wildner #define UMASS_EXT_BUFFER
13712bd3c8bSSascha Wildner #ifdef UMASS_EXT_BUFFER
13812bd3c8bSSascha Wildner /* this enables loading of virtual buffers into DMA */
13912bd3c8bSSascha Wildner #define	UMASS_USB_FLAGS .ext_buffer=1,
14012bd3c8bSSascha Wildner #else
14112bd3c8bSSascha Wildner #define	UMASS_USB_FLAGS
14212bd3c8bSSascha Wildner #endif
14357bed822SMarkus Pfeiffer #endif
14412bd3c8bSSascha Wildner 
14512bd3c8bSSascha Wildner #ifdef USB_DEBUG
14612bd3c8bSSascha Wildner #define	DIF(m, x)				\
14712bd3c8bSSascha Wildner   do {						\
14812bd3c8bSSascha Wildner     if (umass_debug & (m)) { x ; }		\
14912bd3c8bSSascha Wildner   } while (0)
15012bd3c8bSSascha Wildner 
15112bd3c8bSSascha Wildner #define	DPRINTF(sc, m, fmt, ...)			\
15212bd3c8bSSascha Wildner   do {							\
15312bd3c8bSSascha Wildner     if (umass_debug & (m)) {				\
154722d05c3SSascha Wildner         kprintf("%s:%s: " fmt,				\
15512bd3c8bSSascha Wildner 	       (sc) ? (const char *)(sc)->sc_name :	\
15612bd3c8bSSascha Wildner 	       (const char *)"umassX",			\
1577fd4e1a1SSascha Wildner 		__func__ ,## __VA_ARGS__);		\
15812bd3c8bSSascha Wildner     }							\
15912bd3c8bSSascha Wildner   } while (0)
16012bd3c8bSSascha Wildner 
16112bd3c8bSSascha Wildner #define	UDMASS_GEN	0x00010000	/* general */
16212bd3c8bSSascha Wildner #define	UDMASS_SCSI	0x00020000	/* scsi */
16312bd3c8bSSascha Wildner #define	UDMASS_UFI	0x00040000	/* ufi command set */
16412bd3c8bSSascha Wildner #define	UDMASS_ATAPI	0x00080000	/* 8070i command set */
16512bd3c8bSSascha Wildner #define	UDMASS_CMD	(UDMASS_SCSI|UDMASS_UFI|UDMASS_ATAPI)
16612bd3c8bSSascha Wildner #define	UDMASS_USB	0x00100000	/* USB general */
16712bd3c8bSSascha Wildner #define	UDMASS_BBB	0x00200000	/* Bulk-Only transfers */
16812bd3c8bSSascha Wildner #define	UDMASS_CBI	0x00400000	/* CBI transfers */
16912bd3c8bSSascha Wildner #define	UDMASS_WIRE	(UDMASS_BBB|UDMASS_CBI)
17012bd3c8bSSascha Wildner #define	UDMASS_ALL	0xffff0000	/* all of the above */
17112bd3c8bSSascha Wildner static int umass_debug = 0;
17257bed822SMarkus Pfeiffer static int umass_throttle = 0;
17312bd3c8bSSascha Wildner 
17412bd3c8bSSascha Wildner static SYSCTL_NODE(_hw_usb, OID_AUTO, umass, CTLFLAG_RW, 0, "USB umass");
17512bd3c8bSSascha Wildner SYSCTL_INT(_hw_usb_umass, OID_AUTO, debug, CTLFLAG_RW,
17612bd3c8bSSascha Wildner     &umass_debug, 0, "umass debug level");
17712bd3c8bSSascha Wildner 
17812bd3c8bSSascha Wildner TUNABLE_INT("hw.usb.umass.debug", &umass_debug);
17957bed822SMarkus Pfeiffer SYSCTL_INT(_hw_usb_umass, OID_AUTO, throttle, CTLFLAG_RW,
18057bed822SMarkus Pfeiffer     &umass_throttle, 0, "Forced delay between commands in milliseconds");
18157bed822SMarkus Pfeiffer TUNABLE_INT("hw.usb.umass.throttle", &umass_throttle);
18212bd3c8bSSascha Wildner #else
18312bd3c8bSSascha Wildner #define	DIF(...) do { } while (0)
18412bd3c8bSSascha Wildner #define	DPRINTF(...) do { } while (0)
18512bd3c8bSSascha Wildner #endif
18612bd3c8bSSascha Wildner 
18712bd3c8bSSascha Wildner #define	UMASS_BULK_SIZE (1 << 17)
18812bd3c8bSSascha Wildner #define	UMASS_CBI_DIAGNOSTIC_CMDLEN 12	/* bytes */
18912bd3c8bSSascha Wildner #define	UMASS_MAX_CMDLEN MAX(12, CAM_MAX_CDBLEN)	/* bytes */
19012bd3c8bSSascha Wildner 
19112bd3c8bSSascha Wildner /* USB transfer definitions */
19212bd3c8bSSascha Wildner 
19312bd3c8bSSascha Wildner #define	UMASS_T_BBB_RESET1      0	/* Bulk-Only */
19412bd3c8bSSascha Wildner #define	UMASS_T_BBB_RESET2      1
19512bd3c8bSSascha Wildner #define	UMASS_T_BBB_RESET3      2
19612bd3c8bSSascha Wildner #define	UMASS_T_BBB_COMMAND     3
19712bd3c8bSSascha Wildner #define	UMASS_T_BBB_DATA_READ   4
19812bd3c8bSSascha Wildner #define	UMASS_T_BBB_DATA_RD_CS  5
19912bd3c8bSSascha Wildner #define	UMASS_T_BBB_DATA_WRITE  6
20012bd3c8bSSascha Wildner #define	UMASS_T_BBB_DATA_WR_CS  7
20112bd3c8bSSascha Wildner #define	UMASS_T_BBB_STATUS      8
20212bd3c8bSSascha Wildner #define	UMASS_T_BBB_MAX         9
20312bd3c8bSSascha Wildner 
20412bd3c8bSSascha Wildner #define	UMASS_T_CBI_RESET1      0	/* CBI */
20512bd3c8bSSascha Wildner #define	UMASS_T_CBI_RESET2      1
20612bd3c8bSSascha Wildner #define	UMASS_T_CBI_RESET3      2
20712bd3c8bSSascha Wildner #define	UMASS_T_CBI_COMMAND     3
20812bd3c8bSSascha Wildner #define	UMASS_T_CBI_DATA_READ   4
20912bd3c8bSSascha Wildner #define	UMASS_T_CBI_DATA_RD_CS  5
21012bd3c8bSSascha Wildner #define	UMASS_T_CBI_DATA_WRITE  6
21112bd3c8bSSascha Wildner #define	UMASS_T_CBI_DATA_WR_CS  7
21212bd3c8bSSascha Wildner #define	UMASS_T_CBI_STATUS      8
21312bd3c8bSSascha Wildner #define	UMASS_T_CBI_RESET4      9
21412bd3c8bSSascha Wildner #define	UMASS_T_CBI_MAX        10
21512bd3c8bSSascha Wildner 
21612bd3c8bSSascha Wildner #define	UMASS_T_MAX MAX(UMASS_T_CBI_MAX, UMASS_T_BBB_MAX)
21712bd3c8bSSascha Wildner 
21812bd3c8bSSascha Wildner /* Generic definitions */
21912bd3c8bSSascha Wildner 
22012bd3c8bSSascha Wildner /* Direction for transfer */
22112bd3c8bSSascha Wildner #define	DIR_NONE	0
22212bd3c8bSSascha Wildner #define	DIR_IN		1
22312bd3c8bSSascha Wildner #define	DIR_OUT		2
22412bd3c8bSSascha Wildner 
22512bd3c8bSSascha Wildner /* device name */
22612bd3c8bSSascha Wildner #define	DEVNAME		"umass"
22712bd3c8bSSascha Wildner #define	DEVNAME_SIM	"umass-sim"
22812bd3c8bSSascha Wildner 
22912bd3c8bSSascha Wildner /* Approximate maximum transfer speeds (assumes 33% overhead). */
23012bd3c8bSSascha Wildner #define	UMASS_FULL_TRANSFER_SPEED	1000
23112bd3c8bSSascha Wildner #define	UMASS_HIGH_TRANSFER_SPEED	40000
23212bd3c8bSSascha Wildner #define	UMASS_SUPER_TRANSFER_SPEED	400000
23312bd3c8bSSascha Wildner #define	UMASS_FLOPPY_TRANSFER_SPEED	20
23412bd3c8bSSascha Wildner 
23512bd3c8bSSascha Wildner #define	UMASS_TIMEOUT			5000	/* ms */
23612bd3c8bSSascha Wildner 
23712bd3c8bSSascha Wildner /* CAM specific definitions */
23812bd3c8bSSascha Wildner 
23912bd3c8bSSascha Wildner #define	UMASS_SCSIID_MAX	1	/* maximum number of drives expected */
24012bd3c8bSSascha Wildner #define	UMASS_SCSIID_HOST	UMASS_SCSIID_MAX
24112bd3c8bSSascha Wildner 
24212bd3c8bSSascha Wildner /* Bulk-Only features */
24312bd3c8bSSascha Wildner 
24412bd3c8bSSascha Wildner #define	UR_BBB_RESET		0xff	/* Bulk-Only reset */
24512bd3c8bSSascha Wildner #define	UR_BBB_GET_MAX_LUN	0xfe	/* Get maximum lun */
24612bd3c8bSSascha Wildner 
24712bd3c8bSSascha Wildner /* Command Block Wrapper */
24812bd3c8bSSascha Wildner typedef struct {
24912bd3c8bSSascha Wildner 	uDWord	dCBWSignature;
25012bd3c8bSSascha Wildner #define	CBWSIGNATURE	0x43425355
25112bd3c8bSSascha Wildner 	uDWord	dCBWTag;
25212bd3c8bSSascha Wildner 	uDWord	dCBWDataTransferLength;
25312bd3c8bSSascha Wildner 	uByte	bCBWFlags;
25412bd3c8bSSascha Wildner #define	CBWFLAGS_OUT	0x00
25512bd3c8bSSascha Wildner #define	CBWFLAGS_IN	0x80
25612bd3c8bSSascha Wildner 	uByte	bCBWLUN;
25712bd3c8bSSascha Wildner 	uByte	bCDBLength;
25812bd3c8bSSascha Wildner #define	CBWCDBLENGTH	16
25912bd3c8bSSascha Wildner 	uByte	CBWCDB[CBWCDBLENGTH];
26012bd3c8bSSascha Wildner } __packed umass_bbb_cbw_t;
26112bd3c8bSSascha Wildner 
26212bd3c8bSSascha Wildner #define	UMASS_BBB_CBW_SIZE	31
26312bd3c8bSSascha Wildner 
26412bd3c8bSSascha Wildner /* Command Status Wrapper */
26512bd3c8bSSascha Wildner typedef struct {
26612bd3c8bSSascha Wildner 	uDWord	dCSWSignature;
26712bd3c8bSSascha Wildner #define	CSWSIGNATURE	0x53425355
26812bd3c8bSSascha Wildner #define	CSWSIGNATURE_IMAGINATION_DBX1	0x43425355
26912bd3c8bSSascha Wildner #define	CSWSIGNATURE_OLYMPUS_C1	0x55425355
27012bd3c8bSSascha Wildner 	uDWord	dCSWTag;
27112bd3c8bSSascha Wildner 	uDWord	dCSWDataResidue;
27212bd3c8bSSascha Wildner 	uByte	bCSWStatus;
27312bd3c8bSSascha Wildner #define	CSWSTATUS_GOOD	0x0
27412bd3c8bSSascha Wildner #define	CSWSTATUS_FAILED	0x1
27512bd3c8bSSascha Wildner #define	CSWSTATUS_PHASE	0x2
27612bd3c8bSSascha Wildner } __packed umass_bbb_csw_t;
27712bd3c8bSSascha Wildner 
27812bd3c8bSSascha Wildner #define	UMASS_BBB_CSW_SIZE	13
27912bd3c8bSSascha Wildner 
28012bd3c8bSSascha Wildner /* CBI features */
28112bd3c8bSSascha Wildner 
28212bd3c8bSSascha Wildner #define	UR_CBI_ADSC	0x00
28312bd3c8bSSascha Wildner 
28412bd3c8bSSascha Wildner typedef union {
28512bd3c8bSSascha Wildner 	struct {
28612bd3c8bSSascha Wildner 		uint8_t	type;
28712bd3c8bSSascha Wildner #define	IDB_TYPE_CCI		0x00
28812bd3c8bSSascha Wildner 		uint8_t	value;
28912bd3c8bSSascha Wildner #define	IDB_VALUE_PASS		0x00
29012bd3c8bSSascha Wildner #define	IDB_VALUE_FAIL		0x01
29112bd3c8bSSascha Wildner #define	IDB_VALUE_PHASE		0x02
29212bd3c8bSSascha Wildner #define	IDB_VALUE_PERSISTENT	0x03
29312bd3c8bSSascha Wildner #define	IDB_VALUE_STATUS_MASK	0x03
29412bd3c8bSSascha Wildner 	} __packed common;
29512bd3c8bSSascha Wildner 
29612bd3c8bSSascha Wildner 	struct {
29712bd3c8bSSascha Wildner 		uint8_t	asc;
29812bd3c8bSSascha Wildner 		uint8_t	ascq;
29912bd3c8bSSascha Wildner 	} __packed ufi;
30012bd3c8bSSascha Wildner } __packed umass_cbi_sbl_t;
30112bd3c8bSSascha Wildner 
30212bd3c8bSSascha Wildner struct umass_softc;			/* see below */
30312bd3c8bSSascha Wildner 
30412bd3c8bSSascha Wildner typedef void (umass_callback_t)(struct umass_softc *sc, union ccb *ccb,
30512bd3c8bSSascha Wildner     	uint32_t residue, uint8_t status);
30612bd3c8bSSascha Wildner 
30712bd3c8bSSascha Wildner #define	STATUS_CMD_OK		0	/* everything ok */
30812bd3c8bSSascha Wildner #define	STATUS_CMD_UNKNOWN	1	/* will have to fetch sense */
30912bd3c8bSSascha Wildner #define	STATUS_CMD_FAILED	2	/* transfer was ok, command failed */
31012bd3c8bSSascha Wildner #define	STATUS_WIRE_FAILED	3	/* couldn't even get command across */
31112bd3c8bSSascha Wildner 
31212bd3c8bSSascha Wildner typedef uint8_t (umass_transform_t)(struct umass_softc *sc, uint8_t *cmd_ptr,
31312bd3c8bSSascha Wildner     	uint8_t cmd_len);
31412bd3c8bSSascha Wildner 
31512bd3c8bSSascha Wildner /* Wire and command protocol */
31612bd3c8bSSascha Wildner #define	UMASS_PROTO_BBB		0x0001	/* USB wire protocol */
31712bd3c8bSSascha Wildner #define	UMASS_PROTO_CBI		0x0002
31812bd3c8bSSascha Wildner #define	UMASS_PROTO_CBI_I	0x0004
31912bd3c8bSSascha Wildner #define	UMASS_PROTO_WIRE	0x00ff	/* USB wire protocol mask */
32012bd3c8bSSascha Wildner #define	UMASS_PROTO_SCSI	0x0100	/* command protocol */
32112bd3c8bSSascha Wildner #define	UMASS_PROTO_ATAPI	0x0200
32212bd3c8bSSascha Wildner #define	UMASS_PROTO_UFI		0x0400
32312bd3c8bSSascha Wildner #define	UMASS_PROTO_RBC		0x0800
32412bd3c8bSSascha Wildner #define	UMASS_PROTO_COMMAND	0xff00	/* command protocol mask */
32512bd3c8bSSascha Wildner 
32612bd3c8bSSascha Wildner /* Device specific quirks */
32712bd3c8bSSascha Wildner #define	NO_QUIRKS		0x0000
32812bd3c8bSSascha Wildner 	/*
32912bd3c8bSSascha Wildner 	 * The drive does not support Test Unit Ready. Convert to Start Unit
33012bd3c8bSSascha Wildner 	 */
33112bd3c8bSSascha Wildner #define	NO_TEST_UNIT_READY	0x0001
33212bd3c8bSSascha Wildner 	/*
33312bd3c8bSSascha Wildner 	 * The drive does not reset the Unit Attention state after REQUEST
33412bd3c8bSSascha Wildner 	 * SENSE has been sent. The INQUIRY command does not reset the UA
33512bd3c8bSSascha Wildner 	 * either, and so CAM runs in circles trying to retrieve the initial
33612bd3c8bSSascha Wildner 	 * INQUIRY data.
33712bd3c8bSSascha Wildner 	 */
33812bd3c8bSSascha Wildner #define	RS_NO_CLEAR_UA		0x0002
33912bd3c8bSSascha Wildner 	/* The drive does not support START STOP.  */
34012bd3c8bSSascha Wildner #define	NO_START_STOP		0x0004
34112bd3c8bSSascha Wildner 	/* Don't ask for full inquiry data (255b).  */
34212bd3c8bSSascha Wildner #define	FORCE_SHORT_INQUIRY	0x0008
34312bd3c8bSSascha Wildner 	/* Needs to be initialised the Shuttle way */
34412bd3c8bSSascha Wildner #define	SHUTTLE_INIT		0x0010
34512bd3c8bSSascha Wildner 	/* Drive needs to be switched to alternate iface 1 */
34612bd3c8bSSascha Wildner #define	ALT_IFACE_1		0x0020
34712bd3c8bSSascha Wildner 	/* Drive does not do 1Mb/s, but just floppy speeds (20kb/s) */
34812bd3c8bSSascha Wildner #define	FLOPPY_SPEED		0x0040
34912bd3c8bSSascha Wildner 	/* The device can't count and gets the residue of transfers wrong */
35012bd3c8bSSascha Wildner #define	IGNORE_RESIDUE		0x0080
35112bd3c8bSSascha Wildner 	/* No GetMaxLun call */
35212bd3c8bSSascha Wildner #define	NO_GETMAXLUN		0x0100
35312bd3c8bSSascha Wildner 	/* The device uses a weird CSWSIGNATURE. */
35412bd3c8bSSascha Wildner #define	WRONG_CSWSIG		0x0200
35512bd3c8bSSascha Wildner 	/* Device cannot handle INQUIRY so fake a generic response */
35612bd3c8bSSascha Wildner #define	NO_INQUIRY		0x0400
35712bd3c8bSSascha Wildner 	/* Device cannot handle INQUIRY EVPD, return CHECK CONDITION */
35812bd3c8bSSascha Wildner #define	NO_INQUIRY_EVPD		0x0800
35912bd3c8bSSascha Wildner 	/* Pad all RBC requests to 12 bytes. */
36012bd3c8bSSascha Wildner #define	RBC_PAD_TO_12		0x1000
36112bd3c8bSSascha Wildner 	/*
36212bd3c8bSSascha Wildner 	 * Device reports number of sectors from READ_CAPACITY, not max
36312bd3c8bSSascha Wildner 	 * sector number.
36412bd3c8bSSascha Wildner 	 */
36512bd3c8bSSascha Wildner #define	READ_CAPACITY_OFFBY1	0x2000
36612bd3c8bSSascha Wildner 	/*
36712bd3c8bSSascha Wildner 	 * Device cannot handle a SCSI synchronize cache command.  Normally
36812bd3c8bSSascha Wildner 	 * this quirk would be handled in the cam layer, but for IDE bridges
36912bd3c8bSSascha Wildner 	 * we need to associate the quirk with the bridge and not the
37012bd3c8bSSascha Wildner 	 * underlying disk device.  This is handled by faking a success
37112bd3c8bSSascha Wildner 	 * result.
37212bd3c8bSSascha Wildner 	 */
37312bd3c8bSSascha Wildner #define	NO_SYNCHRONIZE_CACHE	0x4000
37457bed822SMarkus Pfeiffer 	/* Device does not support 'PREVENT/ALLOW MEDIUM REMOVAL'. */
37557bed822SMarkus Pfeiffer #define	NO_PREVENT_ALLOW	0x8000
37612bd3c8bSSascha Wildner 
37712bd3c8bSSascha Wildner struct umass_softc {
37812bd3c8bSSascha Wildner 
37912bd3c8bSSascha Wildner 	struct scsi_sense cam_scsi_sense;
38012bd3c8bSSascha Wildner 	struct scsi_test_unit_ready cam_scsi_test_unit_ready;
381722d05c3SSascha Wildner 	struct lock sc_lock;
38212bd3c8bSSascha Wildner 	struct {
38312bd3c8bSSascha Wildner 		uint8_t *data_ptr;
38412bd3c8bSSascha Wildner 		union ccb *ccb;
38512bd3c8bSSascha Wildner 		umass_callback_t *callback;
38612bd3c8bSSascha Wildner 
38712bd3c8bSSascha Wildner 		uint32_t data_len;	/* bytes */
38812bd3c8bSSascha Wildner 		uint32_t data_rem;	/* bytes */
38912bd3c8bSSascha Wildner 		uint32_t data_timeout;	/* ms */
39012bd3c8bSSascha Wildner 		uint32_t actlen;	/* bytes */
39112bd3c8bSSascha Wildner 
39212bd3c8bSSascha Wildner 		uint8_t	cmd_data[UMASS_MAX_CMDLEN];
39312bd3c8bSSascha Wildner 		uint8_t	cmd_len;	/* bytes */
39412bd3c8bSSascha Wildner 		uint8_t	dir;
39512bd3c8bSSascha Wildner 		uint8_t	lun;
39612bd3c8bSSascha Wildner 	}	sc_transfer;
39712bd3c8bSSascha Wildner 
39812bd3c8bSSascha Wildner 	/* Bulk specific variables for transfers in progress */
39912bd3c8bSSascha Wildner 	umass_bbb_cbw_t cbw;		/* command block wrapper */
40012bd3c8bSSascha Wildner 	umass_bbb_csw_t csw;		/* command status wrapper */
40112bd3c8bSSascha Wildner 
40212bd3c8bSSascha Wildner 	/* CBI specific variables for transfers in progress */
40312bd3c8bSSascha Wildner 	umass_cbi_sbl_t sbl;		/* status block */
40412bd3c8bSSascha Wildner 
40512bd3c8bSSascha Wildner 	device_t sc_dev;
40612bd3c8bSSascha Wildner 	struct usb_device *sc_udev;
40712bd3c8bSSascha Wildner 	struct cam_sim *sc_sim;		/* SCSI Interface Module */
40812bd3c8bSSascha Wildner 	struct usb_xfer *sc_xfer[UMASS_T_MAX];
40912bd3c8bSSascha Wildner 
41012bd3c8bSSascha Wildner 	/*
41112bd3c8bSSascha Wildner 	 * The command transform function is used to convert the SCSI
41212bd3c8bSSascha Wildner 	 * commands into their derivatives, like UFI, ATAPI, and friends.
41312bd3c8bSSascha Wildner 	 */
41412bd3c8bSSascha Wildner 	umass_transform_t *sc_transform;
41512bd3c8bSSascha Wildner 
41612bd3c8bSSascha Wildner 	uint32_t sc_unit;
41712bd3c8bSSascha Wildner 	uint32_t sc_quirks;		/* they got it almost right */
41812bd3c8bSSascha Wildner 	uint32_t sc_proto;		/* wire and cmd protocol */
41912bd3c8bSSascha Wildner 
42012bd3c8bSSascha Wildner 	uint8_t	sc_name[16];
42112bd3c8bSSascha Wildner 	uint8_t	sc_iface_no;		/* interface number */
42212bd3c8bSSascha Wildner 	uint8_t	sc_maxlun;		/* maximum LUN number, inclusive */
42312bd3c8bSSascha Wildner 	uint8_t	sc_last_xfer_index;
42412bd3c8bSSascha Wildner 	uint8_t	sc_status_try;
42550fee36bSMarkus Pfeiffer 
42650fee36bSMarkus Pfeiffer 	struct usb_callout sc_rescan_timeout;
42712bd3c8bSSascha Wildner };
42812bd3c8bSSascha Wildner 
42912bd3c8bSSascha Wildner struct umass_probe_proto {
43012bd3c8bSSascha Wildner 	uint32_t quirks;
43112bd3c8bSSascha Wildner 	uint32_t proto;
43212bd3c8bSSascha Wildner 
43312bd3c8bSSascha Wildner 	int	error;
43412bd3c8bSSascha Wildner };
43512bd3c8bSSascha Wildner 
43612bd3c8bSSascha Wildner /* prototypes */
43712bd3c8bSSascha Wildner 
43812bd3c8bSSascha Wildner static device_probe_t umass_probe;
43912bd3c8bSSascha Wildner static device_attach_t umass_attach;
44012bd3c8bSSascha Wildner static device_detach_t umass_detach;
44112bd3c8bSSascha Wildner 
44212bd3c8bSSascha Wildner static usb_callback_t umass_tr_error;
44312bd3c8bSSascha Wildner static usb_callback_t umass_t_bbb_reset1_callback;
44412bd3c8bSSascha Wildner static usb_callback_t umass_t_bbb_reset2_callback;
44512bd3c8bSSascha Wildner static usb_callback_t umass_t_bbb_reset3_callback;
44612bd3c8bSSascha Wildner static usb_callback_t umass_t_bbb_command_callback;
44712bd3c8bSSascha Wildner static usb_callback_t umass_t_bbb_data_read_callback;
44812bd3c8bSSascha Wildner static usb_callback_t umass_t_bbb_data_rd_cs_callback;
44912bd3c8bSSascha Wildner static usb_callback_t umass_t_bbb_data_write_callback;
45012bd3c8bSSascha Wildner static usb_callback_t umass_t_bbb_data_wr_cs_callback;
45112bd3c8bSSascha Wildner static usb_callback_t umass_t_bbb_status_callback;
45212bd3c8bSSascha Wildner static usb_callback_t umass_t_cbi_reset1_callback;
45312bd3c8bSSascha Wildner static usb_callback_t umass_t_cbi_reset2_callback;
45412bd3c8bSSascha Wildner static usb_callback_t umass_t_cbi_reset3_callback;
45512bd3c8bSSascha Wildner static usb_callback_t umass_t_cbi_reset4_callback;
45612bd3c8bSSascha Wildner static usb_callback_t umass_t_cbi_command_callback;
45712bd3c8bSSascha Wildner static usb_callback_t umass_t_cbi_data_read_callback;
45812bd3c8bSSascha Wildner static usb_callback_t umass_t_cbi_data_rd_cs_callback;
45912bd3c8bSSascha Wildner static usb_callback_t umass_t_cbi_data_write_callback;
46012bd3c8bSSascha Wildner static usb_callback_t umass_t_cbi_data_wr_cs_callback;
46112bd3c8bSSascha Wildner static usb_callback_t umass_t_cbi_status_callback;
46212bd3c8bSSascha Wildner 
46312bd3c8bSSascha Wildner static void	umass_cancel_ccb(struct umass_softc *);
46412bd3c8bSSascha Wildner static void	umass_init_shuttle(struct umass_softc *);
46512bd3c8bSSascha Wildner static void	umass_reset(struct umass_softc *);
46612bd3c8bSSascha Wildner static void	umass_t_bbb_data_clear_stall_callback(struct usb_xfer *,
46712bd3c8bSSascha Wildner 		    uint8_t, uint8_t, usb_error_t);
46812bd3c8bSSascha Wildner static void	umass_command_start(struct umass_softc *, uint8_t, void *,
46912bd3c8bSSascha Wildner 		    uint32_t, uint32_t, umass_callback_t *, union ccb *);
47012bd3c8bSSascha Wildner static uint8_t	umass_bbb_get_max_lun(struct umass_softc *);
47112bd3c8bSSascha Wildner static void	umass_cbi_start_status(struct umass_softc *);
47212bd3c8bSSascha Wildner static void	umass_t_cbi_data_clear_stall_callback(struct usb_xfer *,
47312bd3c8bSSascha Wildner 		    uint8_t, uint8_t, usb_error_t);
47412bd3c8bSSascha Wildner static int	umass_cam_attach_sim(struct umass_softc *);
47512bd3c8bSSascha Wildner static void	umass_cam_attach(struct umass_softc *);
47612bd3c8bSSascha Wildner static void	umass_cam_detach_sim(struct umass_softc *);
47712bd3c8bSSascha Wildner static void	umass_cam_action(struct cam_sim *, union ccb *);
47812bd3c8bSSascha Wildner static void	umass_cam_poll(struct cam_sim *);
47912bd3c8bSSascha Wildner static void	umass_cam_cb(struct umass_softc *, union ccb *, uint32_t,
48012bd3c8bSSascha Wildner 		    uint8_t);
48112bd3c8bSSascha Wildner static void	umass_cam_sense_cb(struct umass_softc *, union ccb *, uint32_t,
48212bd3c8bSSascha Wildner 		    uint8_t);
48312bd3c8bSSascha Wildner static void	umass_cam_quirk_cb(struct umass_softc *, union ccb *, uint32_t,
48412bd3c8bSSascha Wildner 		    uint8_t);
48512bd3c8bSSascha Wildner static uint8_t	umass_scsi_transform(struct umass_softc *, uint8_t *, uint8_t);
48612bd3c8bSSascha Wildner static uint8_t	umass_rbc_transform(struct umass_softc *, uint8_t *, uint8_t);
48712bd3c8bSSascha Wildner static uint8_t	umass_ufi_transform(struct umass_softc *, uint8_t *, uint8_t);
48812bd3c8bSSascha Wildner static uint8_t	umass_atapi_transform(struct umass_softc *, uint8_t *,
48912bd3c8bSSascha Wildner 		    uint8_t);
49012bd3c8bSSascha Wildner static uint8_t	umass_no_transform(struct umass_softc *, uint8_t *, uint8_t);
49112bd3c8bSSascha Wildner static uint8_t	umass_std_transform(struct umass_softc *, union ccb *, uint8_t
49212bd3c8bSSascha Wildner 		    *, uint8_t);
49312bd3c8bSSascha Wildner 
49412bd3c8bSSascha Wildner #ifdef USB_DEBUG
49512bd3c8bSSascha Wildner static void	umass_bbb_dump_cbw(struct umass_softc *, umass_bbb_cbw_t *);
49612bd3c8bSSascha Wildner static void	umass_bbb_dump_csw(struct umass_softc *, umass_bbb_csw_t *);
49712bd3c8bSSascha Wildner static void	umass_cbi_dump_cmd(struct umass_softc *, void *, uint8_t);
49812bd3c8bSSascha Wildner static void	umass_dump_buffer(struct umass_softc *, uint8_t *, uint32_t,
49912bd3c8bSSascha Wildner 		    uint32_t);
50012bd3c8bSSascha Wildner #endif
50112bd3c8bSSascha Wildner 
50212bd3c8bSSascha Wildner static struct usb_config umass_bbb_config[UMASS_T_BBB_MAX] = {
50312bd3c8bSSascha Wildner 
50412bd3c8bSSascha Wildner 	[UMASS_T_BBB_RESET1] = {
50512bd3c8bSSascha Wildner 		.type = UE_CONTROL,
50612bd3c8bSSascha Wildner 		.endpoint = 0x00,	/* Control pipe */
50712bd3c8bSSascha Wildner 		.direction = UE_DIR_ANY,
50812bd3c8bSSascha Wildner 		.bufsize = sizeof(struct usb_device_request),
50912bd3c8bSSascha Wildner 		.callback = &umass_t_bbb_reset1_callback,
51012bd3c8bSSascha Wildner 		.timeout = 5000,	/* 5 seconds */
51112bd3c8bSSascha Wildner 		.interval = 500,	/* 500 milliseconds */
51212bd3c8bSSascha Wildner 	},
51312bd3c8bSSascha Wildner 
51412bd3c8bSSascha Wildner 	[UMASS_T_BBB_RESET2] = {
51512bd3c8bSSascha Wildner 		.type = UE_CONTROL,
51612bd3c8bSSascha Wildner 		.endpoint = 0x00,	/* Control pipe */
51712bd3c8bSSascha Wildner 		.direction = UE_DIR_ANY,
51812bd3c8bSSascha Wildner 		.bufsize = sizeof(struct usb_device_request),
51912bd3c8bSSascha Wildner 		.callback = &umass_t_bbb_reset2_callback,
52012bd3c8bSSascha Wildner 		.timeout = 5000,	/* 5 seconds */
52112bd3c8bSSascha Wildner 		.interval = 50,	/* 50 milliseconds */
52212bd3c8bSSascha Wildner 	},
52312bd3c8bSSascha Wildner 
52412bd3c8bSSascha Wildner 	[UMASS_T_BBB_RESET3] = {
52512bd3c8bSSascha Wildner 		.type = UE_CONTROL,
52612bd3c8bSSascha Wildner 		.endpoint = 0x00,	/* Control pipe */
52712bd3c8bSSascha Wildner 		.direction = UE_DIR_ANY,
52812bd3c8bSSascha Wildner 		.bufsize = sizeof(struct usb_device_request),
52912bd3c8bSSascha Wildner 		.callback = &umass_t_bbb_reset3_callback,
53012bd3c8bSSascha Wildner 		.timeout = 5000,	/* 5 seconds */
53112bd3c8bSSascha Wildner 		.interval = 50,	/* 50 milliseconds */
53212bd3c8bSSascha Wildner 	},
53312bd3c8bSSascha Wildner 
53412bd3c8bSSascha Wildner 	[UMASS_T_BBB_COMMAND] = {
53512bd3c8bSSascha Wildner 		.type = UE_BULK,
53612bd3c8bSSascha Wildner 		.endpoint = UE_ADDR_ANY,
53712bd3c8bSSascha Wildner 		.direction = UE_DIR_OUT,
53812bd3c8bSSascha Wildner 		.bufsize = sizeof(umass_bbb_cbw_t),
53912bd3c8bSSascha Wildner 		.callback = &umass_t_bbb_command_callback,
54012bd3c8bSSascha Wildner 		.timeout = 5000,	/* 5 seconds */
54112bd3c8bSSascha Wildner 	},
54212bd3c8bSSascha Wildner 
54312bd3c8bSSascha Wildner 	[UMASS_T_BBB_DATA_READ] = {
54412bd3c8bSSascha Wildner 		.type = UE_BULK,
54512bd3c8bSSascha Wildner 		.endpoint = UE_ADDR_ANY,
54612bd3c8bSSascha Wildner 		.direction = UE_DIR_IN,
54712bd3c8bSSascha Wildner 		.bufsize = UMASS_BULK_SIZE,
54857bed822SMarkus Pfeiffer 		.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,.ext_buffer=1,},
54912bd3c8bSSascha Wildner 		.callback = &umass_t_bbb_data_read_callback,
55012bd3c8bSSascha Wildner 		.timeout = 0,	/* overwritten later */
55112bd3c8bSSascha Wildner 	},
55212bd3c8bSSascha Wildner 
55312bd3c8bSSascha Wildner 	[UMASS_T_BBB_DATA_RD_CS] = {
55412bd3c8bSSascha Wildner 		.type = UE_CONTROL,
55512bd3c8bSSascha Wildner 		.endpoint = 0x00,	/* Control pipe */
55612bd3c8bSSascha Wildner 		.direction = UE_DIR_ANY,
55712bd3c8bSSascha Wildner 		.bufsize = sizeof(struct usb_device_request),
55812bd3c8bSSascha Wildner 		.callback = &umass_t_bbb_data_rd_cs_callback,
55912bd3c8bSSascha Wildner 		.timeout = 5000,	/* 5 seconds */
56012bd3c8bSSascha Wildner 	},
56112bd3c8bSSascha Wildner 
56212bd3c8bSSascha Wildner 	[UMASS_T_BBB_DATA_WRITE] = {
56312bd3c8bSSascha Wildner 		.type = UE_BULK,
56412bd3c8bSSascha Wildner 		.endpoint = UE_ADDR_ANY,
56512bd3c8bSSascha Wildner 		.direction = UE_DIR_OUT,
56612bd3c8bSSascha Wildner 		.bufsize = UMASS_BULK_SIZE,
56757bed822SMarkus Pfeiffer 		.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,.ext_buffer=1,},
56812bd3c8bSSascha Wildner 		.callback = &umass_t_bbb_data_write_callback,
56912bd3c8bSSascha Wildner 		.timeout = 0,	/* overwritten later */
57012bd3c8bSSascha Wildner 	},
57112bd3c8bSSascha Wildner 
57212bd3c8bSSascha Wildner 	[UMASS_T_BBB_DATA_WR_CS] = {
57312bd3c8bSSascha Wildner 		.type = UE_CONTROL,
57412bd3c8bSSascha Wildner 		.endpoint = 0x00,	/* Control pipe */
57512bd3c8bSSascha Wildner 		.direction = UE_DIR_ANY,
57612bd3c8bSSascha Wildner 		.bufsize = sizeof(struct usb_device_request),
57712bd3c8bSSascha Wildner 		.callback = &umass_t_bbb_data_wr_cs_callback,
57812bd3c8bSSascha Wildner 		.timeout = 5000,	/* 5 seconds */
57912bd3c8bSSascha Wildner 	},
58012bd3c8bSSascha Wildner 
58112bd3c8bSSascha Wildner 	[UMASS_T_BBB_STATUS] = {
58212bd3c8bSSascha Wildner 		.type = UE_BULK,
58312bd3c8bSSascha Wildner 		.endpoint = UE_ADDR_ANY,
58412bd3c8bSSascha Wildner 		.direction = UE_DIR_IN,
58512bd3c8bSSascha Wildner 		.bufsize = sizeof(umass_bbb_csw_t),
58612bd3c8bSSascha Wildner 		.flags = {.short_xfer_ok = 1,},
58712bd3c8bSSascha Wildner 		.callback = &umass_t_bbb_status_callback,
58812bd3c8bSSascha Wildner 		.timeout = 5000,	/* ms */
58912bd3c8bSSascha Wildner 	},
59012bd3c8bSSascha Wildner };
59112bd3c8bSSascha Wildner 
59212bd3c8bSSascha Wildner static struct usb_config umass_cbi_config[UMASS_T_CBI_MAX] = {
59312bd3c8bSSascha Wildner 
59412bd3c8bSSascha Wildner 	[UMASS_T_CBI_RESET1] = {
59512bd3c8bSSascha Wildner 		.type = UE_CONTROL,
59612bd3c8bSSascha Wildner 		.endpoint = 0x00,	/* Control pipe */
59712bd3c8bSSascha Wildner 		.direction = UE_DIR_ANY,
59812bd3c8bSSascha Wildner 		.bufsize = (sizeof(struct usb_device_request) +
59912bd3c8bSSascha Wildner 		    UMASS_CBI_DIAGNOSTIC_CMDLEN),
60012bd3c8bSSascha Wildner 		.callback = &umass_t_cbi_reset1_callback,
60112bd3c8bSSascha Wildner 		.timeout = 5000,	/* 5 seconds */
60212bd3c8bSSascha Wildner 		.interval = 500,	/* 500 milliseconds */
60312bd3c8bSSascha Wildner 	},
60412bd3c8bSSascha Wildner 
60512bd3c8bSSascha Wildner 	[UMASS_T_CBI_RESET2] = {
60612bd3c8bSSascha Wildner 		.type = UE_CONTROL,
60712bd3c8bSSascha Wildner 		.endpoint = 0x00,	/* Control pipe */
60812bd3c8bSSascha Wildner 		.direction = UE_DIR_ANY,
60912bd3c8bSSascha Wildner 		.bufsize = sizeof(struct usb_device_request),
61012bd3c8bSSascha Wildner 		.callback = &umass_t_cbi_reset2_callback,
61112bd3c8bSSascha Wildner 		.timeout = 5000,	/* 5 seconds */
61212bd3c8bSSascha Wildner 		.interval = 50,	/* 50 milliseconds */
61312bd3c8bSSascha Wildner 	},
61412bd3c8bSSascha Wildner 
61512bd3c8bSSascha Wildner 	[UMASS_T_CBI_RESET3] = {
61612bd3c8bSSascha Wildner 		.type = UE_CONTROL,
61712bd3c8bSSascha Wildner 		.endpoint = 0x00,	/* Control pipe */
61812bd3c8bSSascha Wildner 		.direction = UE_DIR_ANY,
61912bd3c8bSSascha Wildner 		.bufsize = sizeof(struct usb_device_request),
62012bd3c8bSSascha Wildner 		.callback = &umass_t_cbi_reset3_callback,
62112bd3c8bSSascha Wildner 		.timeout = 5000,	/* 5 seconds */
62212bd3c8bSSascha Wildner 		.interval = 50,	/* 50 milliseconds */
62312bd3c8bSSascha Wildner 	},
62412bd3c8bSSascha Wildner 
62512bd3c8bSSascha Wildner 	[UMASS_T_CBI_COMMAND] = {
62612bd3c8bSSascha Wildner 		.type = UE_CONTROL,
62712bd3c8bSSascha Wildner 		.endpoint = 0x00,	/* Control pipe */
62812bd3c8bSSascha Wildner 		.direction = UE_DIR_ANY,
62912bd3c8bSSascha Wildner 		.bufsize = (sizeof(struct usb_device_request) +
63012bd3c8bSSascha Wildner 		    UMASS_MAX_CMDLEN),
63112bd3c8bSSascha Wildner 		.callback = &umass_t_cbi_command_callback,
63212bd3c8bSSascha Wildner 		.timeout = 5000,	/* 5 seconds */
63312bd3c8bSSascha Wildner 	},
63412bd3c8bSSascha Wildner 
63512bd3c8bSSascha Wildner 	[UMASS_T_CBI_DATA_READ] = {
63612bd3c8bSSascha Wildner 		.type = UE_BULK,
63712bd3c8bSSascha Wildner 		.endpoint = UE_ADDR_ANY,
63812bd3c8bSSascha Wildner 		.direction = UE_DIR_IN,
63912bd3c8bSSascha Wildner 		.bufsize = UMASS_BULK_SIZE,
64057bed822SMarkus Pfeiffer 		.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,.ext_buffer=1,},
64112bd3c8bSSascha Wildner 		.callback = &umass_t_cbi_data_read_callback,
64212bd3c8bSSascha Wildner 		.timeout = 0,	/* overwritten later */
64312bd3c8bSSascha Wildner 	},
64412bd3c8bSSascha Wildner 
64512bd3c8bSSascha Wildner 	[UMASS_T_CBI_DATA_RD_CS] = {
64612bd3c8bSSascha Wildner 		.type = UE_CONTROL,
64712bd3c8bSSascha Wildner 		.endpoint = 0x00,	/* Control pipe */
64812bd3c8bSSascha Wildner 		.direction = UE_DIR_ANY,
64912bd3c8bSSascha Wildner 		.bufsize = sizeof(struct usb_device_request),
65012bd3c8bSSascha Wildner 		.callback = &umass_t_cbi_data_rd_cs_callback,
65112bd3c8bSSascha Wildner 		.timeout = 5000,	/* 5 seconds */
65212bd3c8bSSascha Wildner 	},
65312bd3c8bSSascha Wildner 
65412bd3c8bSSascha Wildner 	[UMASS_T_CBI_DATA_WRITE] = {
65512bd3c8bSSascha Wildner 		.type = UE_BULK,
65612bd3c8bSSascha Wildner 		.endpoint = UE_ADDR_ANY,
65712bd3c8bSSascha Wildner 		.direction = UE_DIR_OUT,
65812bd3c8bSSascha Wildner 		.bufsize = UMASS_BULK_SIZE,
65957bed822SMarkus Pfeiffer 		.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,.ext_buffer=1,},
66012bd3c8bSSascha Wildner 		.callback = &umass_t_cbi_data_write_callback,
66112bd3c8bSSascha Wildner 		.timeout = 0,	/* overwritten later */
66212bd3c8bSSascha Wildner 	},
66312bd3c8bSSascha Wildner 
66412bd3c8bSSascha Wildner 	[UMASS_T_CBI_DATA_WR_CS] = {
66512bd3c8bSSascha Wildner 		.type = UE_CONTROL,
66612bd3c8bSSascha Wildner 		.endpoint = 0x00,	/* Control pipe */
66712bd3c8bSSascha Wildner 		.direction = UE_DIR_ANY,
66812bd3c8bSSascha Wildner 		.bufsize = sizeof(struct usb_device_request),
66912bd3c8bSSascha Wildner 		.callback = &umass_t_cbi_data_wr_cs_callback,
67012bd3c8bSSascha Wildner 		.timeout = 5000,	/* 5 seconds */
67112bd3c8bSSascha Wildner 	},
67212bd3c8bSSascha Wildner 
67312bd3c8bSSascha Wildner 	[UMASS_T_CBI_STATUS] = {
67412bd3c8bSSascha Wildner 		.type = UE_INTERRUPT,
67512bd3c8bSSascha Wildner 		.endpoint = UE_ADDR_ANY,
67612bd3c8bSSascha Wildner 		.direction = UE_DIR_IN,
67712bd3c8bSSascha Wildner 		.flags = {.short_xfer_ok = 1,.no_pipe_ok = 1,},
67812bd3c8bSSascha Wildner 		.bufsize = sizeof(umass_cbi_sbl_t),
67912bd3c8bSSascha Wildner 		.callback = &umass_t_cbi_status_callback,
68012bd3c8bSSascha Wildner 		.timeout = 5000,	/* ms */
68112bd3c8bSSascha Wildner 	},
68212bd3c8bSSascha Wildner 
68312bd3c8bSSascha Wildner 	[UMASS_T_CBI_RESET4] = {
68412bd3c8bSSascha Wildner 		.type = UE_CONTROL,
68512bd3c8bSSascha Wildner 		.endpoint = 0x00,	/* Control pipe */
68612bd3c8bSSascha Wildner 		.direction = UE_DIR_ANY,
68712bd3c8bSSascha Wildner 		.bufsize = sizeof(struct usb_device_request),
68812bd3c8bSSascha Wildner 		.callback = &umass_t_cbi_reset4_callback,
68912bd3c8bSSascha Wildner 		.timeout = 5000,	/* ms */
69012bd3c8bSSascha Wildner 	},
69112bd3c8bSSascha Wildner };
69212bd3c8bSSascha Wildner 
69312bd3c8bSSascha Wildner /* If device cannot return valid inquiry data, fake it */
69412bd3c8bSSascha Wildner static const uint8_t fake_inq_data[SHORT_INQUIRY_LENGTH] = {
69512bd3c8bSSascha Wildner 	0, /* removable */ 0x80, SCSI_REV_2, SCSI_REV_2,
69612bd3c8bSSascha Wildner 	 /* additional_length */ 31, 0, 0, 0
69712bd3c8bSSascha Wildner };
69812bd3c8bSSascha Wildner 
69912bd3c8bSSascha Wildner #define	UFI_COMMAND_LENGTH	12	/* UFI commands are always 12 bytes */
70012bd3c8bSSascha Wildner #define	ATAPI_COMMAND_LENGTH	12	/* ATAPI commands are always 12 bytes */
70112bd3c8bSSascha Wildner 
70212bd3c8bSSascha Wildner static devclass_t umass_devclass;
70312bd3c8bSSascha Wildner 
70412bd3c8bSSascha Wildner static device_method_t umass_methods[] = {
70512bd3c8bSSascha Wildner 	/* Device interface */
70612bd3c8bSSascha Wildner 	DEVMETHOD(device_probe, umass_probe),
70712bd3c8bSSascha Wildner 	DEVMETHOD(device_attach, umass_attach),
70812bd3c8bSSascha Wildner 	DEVMETHOD(device_detach, umass_detach),
709d3c9c58eSSascha Wildner 	DEVMETHOD_END
71012bd3c8bSSascha Wildner };
71112bd3c8bSSascha Wildner 
71212bd3c8bSSascha Wildner static driver_t umass_driver = {
71312bd3c8bSSascha Wildner 	.name = "umass",
71412bd3c8bSSascha Wildner 	.methods = umass_methods,
71512bd3c8bSSascha Wildner 	.size = sizeof(struct umass_softc),
71612bd3c8bSSascha Wildner };
71712bd3c8bSSascha Wildner 
71815130067Szrj static const STRUCT_USB_HOST_ID __used umass_devs[] = {
71915130067Szrj 	/* generic mass storage class */
72015130067Szrj 	{USB_IFACE_CLASS(UICLASS_MASS),},
72115130067Szrj };
72215130067Szrj 
72315f415f6SSascha Wildner DRIVER_MODULE(umass, uhub, umass_driver, umass_devclass, NULL, NULL);
72412bd3c8bSSascha Wildner MODULE_DEPEND(umass, usb, 1, 1, 1);
72512bd3c8bSSascha Wildner MODULE_DEPEND(umass, cam, 1, 1, 1);
72612bd3c8bSSascha Wildner MODULE_VERSION(umass, 1);
72712bd3c8bSSascha Wildner 
72812bd3c8bSSascha Wildner /*
72912bd3c8bSSascha Wildner  * USB device probe/attach/detach
73012bd3c8bSSascha Wildner  */
73112bd3c8bSSascha Wildner 
73212bd3c8bSSascha Wildner static uint16_t
umass_get_proto(struct usb_interface * iface)73312bd3c8bSSascha Wildner umass_get_proto(struct usb_interface *iface)
73412bd3c8bSSascha Wildner {
73512bd3c8bSSascha Wildner 	struct usb_interface_descriptor *id;
73612bd3c8bSSascha Wildner 	uint16_t retval;
73712bd3c8bSSascha Wildner 
73812bd3c8bSSascha Wildner 	retval = 0;
73912bd3c8bSSascha Wildner 
74012bd3c8bSSascha Wildner 	/* Check for a standards compliant device */
74112bd3c8bSSascha Wildner 	id = usbd_get_interface_descriptor(iface);
74212bd3c8bSSascha Wildner 	if ((id == NULL) ||
74312bd3c8bSSascha Wildner 	    (id->bInterfaceClass != UICLASS_MASS)) {
74412bd3c8bSSascha Wildner 		goto done;
74512bd3c8bSSascha Wildner 	}
74612bd3c8bSSascha Wildner 	switch (id->bInterfaceSubClass) {
74712bd3c8bSSascha Wildner 	case UISUBCLASS_SCSI:
74812bd3c8bSSascha Wildner 		retval |= UMASS_PROTO_SCSI;
74912bd3c8bSSascha Wildner 		break;
75012bd3c8bSSascha Wildner 	case UISUBCLASS_UFI:
75112bd3c8bSSascha Wildner 		retval |= UMASS_PROTO_UFI;
75212bd3c8bSSascha Wildner 		break;
75312bd3c8bSSascha Wildner 	case UISUBCLASS_RBC:
75412bd3c8bSSascha Wildner 		retval |= UMASS_PROTO_RBC;
75512bd3c8bSSascha Wildner 		break;
75612bd3c8bSSascha Wildner 	case UISUBCLASS_SFF8020I:
75712bd3c8bSSascha Wildner 	case UISUBCLASS_SFF8070I:
75812bd3c8bSSascha Wildner 		retval |= UMASS_PROTO_ATAPI;
75912bd3c8bSSascha Wildner 		break;
76012bd3c8bSSascha Wildner 	default:
76112bd3c8bSSascha Wildner 		goto done;
76212bd3c8bSSascha Wildner 	}
76312bd3c8bSSascha Wildner 
76412bd3c8bSSascha Wildner 	switch (id->bInterfaceProtocol) {
76512bd3c8bSSascha Wildner 	case UIPROTO_MASS_CBI:
76612bd3c8bSSascha Wildner 		retval |= UMASS_PROTO_CBI;
76712bd3c8bSSascha Wildner 		break;
76812bd3c8bSSascha Wildner 	case UIPROTO_MASS_CBI_I:
76912bd3c8bSSascha Wildner 		retval |= UMASS_PROTO_CBI_I;
77012bd3c8bSSascha Wildner 		break;
77112bd3c8bSSascha Wildner 	case UIPROTO_MASS_BBB_OLD:
77212bd3c8bSSascha Wildner 	case UIPROTO_MASS_BBB:
77312bd3c8bSSascha Wildner 		retval |= UMASS_PROTO_BBB;
77412bd3c8bSSascha Wildner 		break;
77512bd3c8bSSascha Wildner 	default:
77612bd3c8bSSascha Wildner 		goto done;
77712bd3c8bSSascha Wildner 	}
77812bd3c8bSSascha Wildner done:
77912bd3c8bSSascha Wildner 	return (retval);
78012bd3c8bSSascha Wildner }
78112bd3c8bSSascha Wildner 
78212bd3c8bSSascha Wildner /*
78312bd3c8bSSascha Wildner  * Match the device we are seeing with the devices supported.
78412bd3c8bSSascha Wildner  */
78512bd3c8bSSascha Wildner static struct umass_probe_proto
umass_probe_proto(device_t dev,struct usb_attach_arg * uaa)78612bd3c8bSSascha Wildner umass_probe_proto(device_t dev, struct usb_attach_arg *uaa)
78712bd3c8bSSascha Wildner {
78812bd3c8bSSascha Wildner 	struct umass_probe_proto ret;
78912bd3c8bSSascha Wildner 	uint32_t quirks = NO_QUIRKS;
79012bd3c8bSSascha Wildner 	uint32_t proto = umass_get_proto(uaa->iface);
79112bd3c8bSSascha Wildner 
79212bd3c8bSSascha Wildner 	memset(&ret, 0, sizeof(ret));
79312bd3c8bSSascha Wildner 	ret.error = BUS_PROBE_GENERIC;
79412bd3c8bSSascha Wildner 
79512bd3c8bSSascha Wildner 	/* Search for protocol enforcement */
79612bd3c8bSSascha Wildner 
79712bd3c8bSSascha Wildner 	if (usb_test_quirk(uaa, UQ_MSC_FORCE_WIRE_BBB)) {
79812bd3c8bSSascha Wildner 		proto &= ~UMASS_PROTO_WIRE;
79912bd3c8bSSascha Wildner 		proto |= UMASS_PROTO_BBB;
80012bd3c8bSSascha Wildner 	} else if (usb_test_quirk(uaa, UQ_MSC_FORCE_WIRE_CBI)) {
80112bd3c8bSSascha Wildner 		proto &= ~UMASS_PROTO_WIRE;
80212bd3c8bSSascha Wildner 		proto |= UMASS_PROTO_CBI;
80312bd3c8bSSascha Wildner 	} else if (usb_test_quirk(uaa, UQ_MSC_FORCE_WIRE_CBI_I)) {
80412bd3c8bSSascha Wildner 		proto &= ~UMASS_PROTO_WIRE;
80512bd3c8bSSascha Wildner 		proto |= UMASS_PROTO_CBI_I;
80612bd3c8bSSascha Wildner 	}
80712bd3c8bSSascha Wildner 
80812bd3c8bSSascha Wildner 	if (usb_test_quirk(uaa, UQ_MSC_FORCE_PROTO_SCSI)) {
80912bd3c8bSSascha Wildner 		proto &= ~UMASS_PROTO_COMMAND;
81012bd3c8bSSascha Wildner 		proto |= UMASS_PROTO_SCSI;
81112bd3c8bSSascha Wildner 	} else if (usb_test_quirk(uaa, UQ_MSC_FORCE_PROTO_ATAPI)) {
81212bd3c8bSSascha Wildner 		proto &= ~UMASS_PROTO_COMMAND;
81312bd3c8bSSascha Wildner 		proto |= UMASS_PROTO_ATAPI;
81412bd3c8bSSascha Wildner 	} else if (usb_test_quirk(uaa, UQ_MSC_FORCE_PROTO_UFI)) {
81512bd3c8bSSascha Wildner 		proto &= ~UMASS_PROTO_COMMAND;
81612bd3c8bSSascha Wildner 		proto |= UMASS_PROTO_UFI;
81712bd3c8bSSascha Wildner 	} else if (usb_test_quirk(uaa, UQ_MSC_FORCE_PROTO_RBC)) {
81812bd3c8bSSascha Wildner 		proto &= ~UMASS_PROTO_COMMAND;
81912bd3c8bSSascha Wildner 		proto |= UMASS_PROTO_RBC;
82012bd3c8bSSascha Wildner 	}
82112bd3c8bSSascha Wildner 
82212bd3c8bSSascha Wildner 	/* Check if the protocol is invalid */
82312bd3c8bSSascha Wildner 
82412bd3c8bSSascha Wildner 	if ((proto & UMASS_PROTO_COMMAND) == 0) {
82512bd3c8bSSascha Wildner 		ret.error = ENXIO;
82612bd3c8bSSascha Wildner 		goto done;
82712bd3c8bSSascha Wildner 	}
82812bd3c8bSSascha Wildner 
82912bd3c8bSSascha Wildner 	if ((proto & UMASS_PROTO_WIRE) == 0) {
83012bd3c8bSSascha Wildner 		ret.error = ENXIO;
83112bd3c8bSSascha Wildner 		goto done;
83212bd3c8bSSascha Wildner 	}
83312bd3c8bSSascha Wildner 
83412bd3c8bSSascha Wildner 	/* Search for quirks */
83512bd3c8bSSascha Wildner 
83612bd3c8bSSascha Wildner 	if (usb_test_quirk(uaa, UQ_MSC_NO_TEST_UNIT_READY))
83712bd3c8bSSascha Wildner 		quirks |= NO_TEST_UNIT_READY;
83812bd3c8bSSascha Wildner 	if (usb_test_quirk(uaa, UQ_MSC_NO_RS_CLEAR_UA))
83912bd3c8bSSascha Wildner 		quirks |= RS_NO_CLEAR_UA;
84012bd3c8bSSascha Wildner 	if (usb_test_quirk(uaa, UQ_MSC_NO_START_STOP))
84112bd3c8bSSascha Wildner 		quirks |= NO_START_STOP;
84212bd3c8bSSascha Wildner 	if (usb_test_quirk(uaa, UQ_MSC_NO_GETMAXLUN))
84312bd3c8bSSascha Wildner 		quirks |= NO_GETMAXLUN;
84412bd3c8bSSascha Wildner 	if (usb_test_quirk(uaa, UQ_MSC_NO_INQUIRY))
84512bd3c8bSSascha Wildner 		quirks |= NO_INQUIRY;
84612bd3c8bSSascha Wildner 	if (usb_test_quirk(uaa, UQ_MSC_NO_INQUIRY_EVPD))
84712bd3c8bSSascha Wildner 		quirks |= NO_INQUIRY_EVPD;
84857bed822SMarkus Pfeiffer 	if (usb_test_quirk(uaa, UQ_MSC_NO_PREVENT_ALLOW))
84957bed822SMarkus Pfeiffer 		quirks |= NO_PREVENT_ALLOW;
85012bd3c8bSSascha Wildner 	if (usb_test_quirk(uaa, UQ_MSC_NO_SYNC_CACHE))
85112bd3c8bSSascha Wildner 		quirks |= NO_SYNCHRONIZE_CACHE;
85212bd3c8bSSascha Wildner 	if (usb_test_quirk(uaa, UQ_MSC_SHUTTLE_INIT))
85312bd3c8bSSascha Wildner 		quirks |= SHUTTLE_INIT;
85412bd3c8bSSascha Wildner 	if (usb_test_quirk(uaa, UQ_MSC_ALT_IFACE_1))
85512bd3c8bSSascha Wildner 		quirks |= ALT_IFACE_1;
85612bd3c8bSSascha Wildner 	if (usb_test_quirk(uaa, UQ_MSC_FLOPPY_SPEED))
85712bd3c8bSSascha Wildner 		quirks |= FLOPPY_SPEED;
85812bd3c8bSSascha Wildner 	if (usb_test_quirk(uaa, UQ_MSC_IGNORE_RESIDUE))
85912bd3c8bSSascha Wildner 		quirks |= IGNORE_RESIDUE;
86012bd3c8bSSascha Wildner 	if (usb_test_quirk(uaa, UQ_MSC_WRONG_CSWSIG))
86112bd3c8bSSascha Wildner 		quirks |= WRONG_CSWSIG;
86212bd3c8bSSascha Wildner 	if (usb_test_quirk(uaa, UQ_MSC_RBC_PAD_TO_12))
86312bd3c8bSSascha Wildner 		quirks |= RBC_PAD_TO_12;
86412bd3c8bSSascha Wildner 	if (usb_test_quirk(uaa, UQ_MSC_READ_CAP_OFFBY1))
86512bd3c8bSSascha Wildner 		quirks |= READ_CAPACITY_OFFBY1;
86612bd3c8bSSascha Wildner 	if (usb_test_quirk(uaa, UQ_MSC_FORCE_SHORT_INQ))
86712bd3c8bSSascha Wildner 		quirks |= FORCE_SHORT_INQUIRY;
86812bd3c8bSSascha Wildner 
86912bd3c8bSSascha Wildner done:
87012bd3c8bSSascha Wildner 	ret.quirks = quirks;
87112bd3c8bSSascha Wildner 	ret.proto = proto;
87212bd3c8bSSascha Wildner 	return (ret);
87312bd3c8bSSascha Wildner }
87412bd3c8bSSascha Wildner 
87512bd3c8bSSascha Wildner static int
umass_probe(device_t dev)87612bd3c8bSSascha Wildner umass_probe(device_t dev)
87712bd3c8bSSascha Wildner {
87812bd3c8bSSascha Wildner 	struct usb_attach_arg *uaa = device_get_ivars(dev);
87912bd3c8bSSascha Wildner 	struct umass_probe_proto temp;
88012bd3c8bSSascha Wildner 
88112bd3c8bSSascha Wildner 	if (uaa->usb_mode != USB_MODE_HOST) {
88212bd3c8bSSascha Wildner 		return (ENXIO);
88312bd3c8bSSascha Wildner 	}
88412bd3c8bSSascha Wildner 	temp = umass_probe_proto(dev, uaa);
88512bd3c8bSSascha Wildner 
88612bd3c8bSSascha Wildner 	return (temp.error);
88712bd3c8bSSascha Wildner }
88812bd3c8bSSascha Wildner 
88912bd3c8bSSascha Wildner static int
umass_attach(device_t dev)89012bd3c8bSSascha Wildner umass_attach(device_t dev)
89112bd3c8bSSascha Wildner {
89212bd3c8bSSascha Wildner 	struct umass_softc *sc = device_get_softc(dev);
89312bd3c8bSSascha Wildner 	struct usb_attach_arg *uaa = device_get_ivars(dev);
89412bd3c8bSSascha Wildner 	struct umass_probe_proto temp = umass_probe_proto(dev, uaa);
89512bd3c8bSSascha Wildner 	struct usb_interface_descriptor *id;
89657bed822SMarkus Pfeiffer 	int err;
89712bd3c8bSSascha Wildner 
89812bd3c8bSSascha Wildner 	/*
89912bd3c8bSSascha Wildner 	 * NOTE: the softc struct is cleared in device_set_driver.
90012bd3c8bSSascha Wildner 	 * We can safely call umass_detach without specifically
90112bd3c8bSSascha Wildner 	 * initializing the struct.
90212bd3c8bSSascha Wildner 	 */
90312bd3c8bSSascha Wildner 
90412bd3c8bSSascha Wildner 	sc->sc_dev = dev;
90512bd3c8bSSascha Wildner 	sc->sc_udev = uaa->device;
90612bd3c8bSSascha Wildner 	sc->sc_proto = temp.proto;
90712bd3c8bSSascha Wildner 	sc->sc_quirks = temp.quirks;
90812bd3c8bSSascha Wildner 	sc->sc_unit = device_get_unit(dev);
90912bd3c8bSSascha Wildner 
910722d05c3SSascha Wildner 	ksnprintf(sc->sc_name, sizeof(sc->sc_name),
91112bd3c8bSSascha Wildner 	    "%s", device_get_nameunit(dev));
91212bd3c8bSSascha Wildner 
91312bd3c8bSSascha Wildner 	device_set_usb_desc(dev);
91412bd3c8bSSascha Wildner 
915722d05c3SSascha Wildner 	lockinit(&sc->sc_lock, device_get_nameunit(dev), 0, LK_CANRECURSE);
91612bd3c8bSSascha Wildner 
91712bd3c8bSSascha Wildner 	/* get interface index */
91812bd3c8bSSascha Wildner 
91912bd3c8bSSascha Wildner 	id = usbd_get_interface_descriptor(uaa->iface);
92012bd3c8bSSascha Wildner 	if (id == NULL) {
92112bd3c8bSSascha Wildner 		device_printf(dev, "failed to get "
92212bd3c8bSSascha Wildner 		    "interface number\n");
92312bd3c8bSSascha Wildner 		goto detach;
92412bd3c8bSSascha Wildner 	}
92512bd3c8bSSascha Wildner 	sc->sc_iface_no = id->bInterfaceNumber;
92612bd3c8bSSascha Wildner 
92712bd3c8bSSascha Wildner #ifdef USB_DEBUG
92812bd3c8bSSascha Wildner 	device_printf(dev, " ");
92912bd3c8bSSascha Wildner 
93012bd3c8bSSascha Wildner 	switch (sc->sc_proto & UMASS_PROTO_COMMAND) {
93112bd3c8bSSascha Wildner 	case UMASS_PROTO_SCSI:
932722d05c3SSascha Wildner 		kprintf("SCSI");
93312bd3c8bSSascha Wildner 		break;
93412bd3c8bSSascha Wildner 	case UMASS_PROTO_ATAPI:
935722d05c3SSascha Wildner 		kprintf("8070i (ATAPI)");
93612bd3c8bSSascha Wildner 		break;
93712bd3c8bSSascha Wildner 	case UMASS_PROTO_UFI:
938722d05c3SSascha Wildner 		kprintf("UFI");
93912bd3c8bSSascha Wildner 		break;
94012bd3c8bSSascha Wildner 	case UMASS_PROTO_RBC:
941722d05c3SSascha Wildner 		kprintf("RBC");
94212bd3c8bSSascha Wildner 		break;
94312bd3c8bSSascha Wildner 	default:
944722d05c3SSascha Wildner 		kprintf("(unknown 0x%02x)",
94512bd3c8bSSascha Wildner 		    sc->sc_proto & UMASS_PROTO_COMMAND);
94612bd3c8bSSascha Wildner 		break;
94712bd3c8bSSascha Wildner 	}
94812bd3c8bSSascha Wildner 
949722d05c3SSascha Wildner 	kprintf(" over ");
95012bd3c8bSSascha Wildner 
95112bd3c8bSSascha Wildner 	switch (sc->sc_proto & UMASS_PROTO_WIRE) {
95212bd3c8bSSascha Wildner 	case UMASS_PROTO_BBB:
953722d05c3SSascha Wildner 		kprintf("Bulk-Only");
95412bd3c8bSSascha Wildner 		break;
95512bd3c8bSSascha Wildner 	case UMASS_PROTO_CBI:		/* uses Comand/Bulk pipes */
956722d05c3SSascha Wildner 		kprintf("CBI");
95712bd3c8bSSascha Wildner 		break;
95812bd3c8bSSascha Wildner 	case UMASS_PROTO_CBI_I:	/* uses Comand/Bulk/Interrupt pipes */
959722d05c3SSascha Wildner 		kprintf("CBI with CCI");
96012bd3c8bSSascha Wildner 		break;
96112bd3c8bSSascha Wildner 	default:
962722d05c3SSascha Wildner 		kprintf("(unknown 0x%02x)",
96312bd3c8bSSascha Wildner 		    sc->sc_proto & UMASS_PROTO_WIRE);
96412bd3c8bSSascha Wildner 	}
96512bd3c8bSSascha Wildner 
966722d05c3SSascha Wildner 	kprintf("; quirks = 0x%04x\n", sc->sc_quirks);
96712bd3c8bSSascha Wildner #endif
96812bd3c8bSSascha Wildner 
96912bd3c8bSSascha Wildner 	if (sc->sc_quirks & ALT_IFACE_1) {
97012bd3c8bSSascha Wildner 		err = usbd_set_alt_interface_index
97112bd3c8bSSascha Wildner 		    (uaa->device, uaa->info.bIfaceIndex, 1);
97212bd3c8bSSascha Wildner 
97312bd3c8bSSascha Wildner 		if (err) {
97412bd3c8bSSascha Wildner 			DPRINTF(sc, UDMASS_USB, "could not switch to "
97512bd3c8bSSascha Wildner 			    "Alt Interface 1\n");
97612bd3c8bSSascha Wildner 			goto detach;
97712bd3c8bSSascha Wildner 		}
97812bd3c8bSSascha Wildner 	}
97912bd3c8bSSascha Wildner 	/* allocate all required USB transfers */
98012bd3c8bSSascha Wildner 
98112bd3c8bSSascha Wildner 	if (sc->sc_proto & UMASS_PROTO_BBB) {
98212bd3c8bSSascha Wildner 
98312bd3c8bSSascha Wildner 		err = usbd_transfer_setup(uaa->device,
98412bd3c8bSSascha Wildner 		    &uaa->info.bIfaceIndex, sc->sc_xfer, umass_bbb_config,
985722d05c3SSascha Wildner 		    UMASS_T_BBB_MAX, sc, &sc->sc_lock);
98612bd3c8bSSascha Wildner 
98712bd3c8bSSascha Wildner 		/* skip reset first time */
98812bd3c8bSSascha Wildner 		sc->sc_last_xfer_index = UMASS_T_BBB_COMMAND;
98912bd3c8bSSascha Wildner 
99012bd3c8bSSascha Wildner 	} else if (sc->sc_proto & (UMASS_PROTO_CBI | UMASS_PROTO_CBI_I)) {
99112bd3c8bSSascha Wildner 
99212bd3c8bSSascha Wildner 		err = usbd_transfer_setup(uaa->device,
99312bd3c8bSSascha Wildner 		    &uaa->info.bIfaceIndex, sc->sc_xfer, umass_cbi_config,
994722d05c3SSascha Wildner 		    UMASS_T_CBI_MAX, sc, &sc->sc_lock);
99512bd3c8bSSascha Wildner 
99612bd3c8bSSascha Wildner 		/* skip reset first time */
99712bd3c8bSSascha Wildner 		sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND;
99812bd3c8bSSascha Wildner 
99912bd3c8bSSascha Wildner 	} else {
100012bd3c8bSSascha Wildner 		err = USB_ERR_INVAL;
100112bd3c8bSSascha Wildner 	}
100212bd3c8bSSascha Wildner 
100312bd3c8bSSascha Wildner 	if (err) {
100412bd3c8bSSascha Wildner 		device_printf(dev, "could not setup required "
100512bd3c8bSSascha Wildner 		    "transfers, %s\n", usbd_errstr(err));
100612bd3c8bSSascha Wildner 		goto detach;
100712bd3c8bSSascha Wildner 	}
100857bed822SMarkus Pfeiffer #ifdef USB_DEBUG
100957bed822SMarkus Pfeiffer 	if (umass_throttle > 0) {
101057bed822SMarkus Pfeiffer 		uint8_t x;
101157bed822SMarkus Pfeiffer 		int iv;
101257bed822SMarkus Pfeiffer 
101357bed822SMarkus Pfeiffer 		iv = umass_throttle;
101457bed822SMarkus Pfeiffer 
101557bed822SMarkus Pfeiffer 		if (iv < 1)
101657bed822SMarkus Pfeiffer 			iv = 1;
101757bed822SMarkus Pfeiffer 		else if (iv > 8000)
101857bed822SMarkus Pfeiffer 			iv = 8000;
101957bed822SMarkus Pfeiffer 
102057bed822SMarkus Pfeiffer 		for (x = 0; x != UMASS_T_MAX; x++) {
102157bed822SMarkus Pfeiffer 			if (sc->sc_xfer[x] != NULL)
102257bed822SMarkus Pfeiffer 				usbd_xfer_set_interval(sc->sc_xfer[x], iv);
102357bed822SMarkus Pfeiffer 		}
102457bed822SMarkus Pfeiffer 	}
102557bed822SMarkus Pfeiffer #endif
102612bd3c8bSSascha Wildner 	sc->sc_transform =
102712bd3c8bSSascha Wildner 	    (sc->sc_proto & UMASS_PROTO_SCSI) ? &umass_scsi_transform :
102812bd3c8bSSascha Wildner 	    (sc->sc_proto & UMASS_PROTO_UFI) ? &umass_ufi_transform :
102912bd3c8bSSascha Wildner 	    (sc->sc_proto & UMASS_PROTO_ATAPI) ? &umass_atapi_transform :
103012bd3c8bSSascha Wildner 	    (sc->sc_proto & UMASS_PROTO_RBC) ? &umass_rbc_transform :
103112bd3c8bSSascha Wildner 	    &umass_no_transform;
103212bd3c8bSSascha Wildner 
103312bd3c8bSSascha Wildner 	/* from here onwards the device can be used. */
103412bd3c8bSSascha Wildner 
103512bd3c8bSSascha Wildner 	if (sc->sc_quirks & SHUTTLE_INIT) {
103612bd3c8bSSascha Wildner 		umass_init_shuttle(sc);
103712bd3c8bSSascha Wildner 	}
103812bd3c8bSSascha Wildner 	/* get the maximum LUN supported by the device */
103912bd3c8bSSascha Wildner 
104012bd3c8bSSascha Wildner 	if (((sc->sc_proto & UMASS_PROTO_WIRE) == UMASS_PROTO_BBB) &&
104112bd3c8bSSascha Wildner 	    !(sc->sc_quirks & NO_GETMAXLUN))
104212bd3c8bSSascha Wildner 		sc->sc_maxlun = umass_bbb_get_max_lun(sc);
104312bd3c8bSSascha Wildner 	else
104412bd3c8bSSascha Wildner 		sc->sc_maxlun = 0;
104512bd3c8bSSascha Wildner 
104612bd3c8bSSascha Wildner 	/* Prepare the SCSI command block */
104712bd3c8bSSascha Wildner 	sc->cam_scsi_sense.opcode = REQUEST_SENSE;
104812bd3c8bSSascha Wildner 	sc->cam_scsi_test_unit_ready.opcode = TEST_UNIT_READY;
104912bd3c8bSSascha Wildner 
105012bd3c8bSSascha Wildner 	/* register the SIM */
105112bd3c8bSSascha Wildner 	err = umass_cam_attach_sim(sc);
105212bd3c8bSSascha Wildner 	if (err) {
105312bd3c8bSSascha Wildner 		goto detach;
105412bd3c8bSSascha Wildner 	}
105512bd3c8bSSascha Wildner 	/* scan the SIM */
105612bd3c8bSSascha Wildner 	umass_cam_attach(sc);
105712bd3c8bSSascha Wildner 
105812bd3c8bSSascha Wildner 	DPRINTF(sc, UDMASS_GEN, "Attach finished\n");
105912bd3c8bSSascha Wildner 
106012bd3c8bSSascha Wildner 	return (0);			/* success */
106112bd3c8bSSascha Wildner 
106212bd3c8bSSascha Wildner detach:
106312bd3c8bSSascha Wildner 	umass_detach(dev);
106412bd3c8bSSascha Wildner 	return (ENXIO);			/* failure */
106512bd3c8bSSascha Wildner }
106612bd3c8bSSascha Wildner 
106712bd3c8bSSascha Wildner static int
umass_detach(device_t dev)106812bd3c8bSSascha Wildner umass_detach(device_t dev)
106912bd3c8bSSascha Wildner {
107012bd3c8bSSascha Wildner 	struct umass_softc *sc = device_get_softc(dev);
107112bd3c8bSSascha Wildner 
107212bd3c8bSSascha Wildner 	DPRINTF(sc, UDMASS_USB, "\n");
107312bd3c8bSSascha Wildner 
107412bd3c8bSSascha Wildner 	/* teardown our statemachine */
107512bd3c8bSSascha Wildner 
107612bd3c8bSSascha Wildner 	usbd_transfer_unsetup(sc->sc_xfer, UMASS_T_MAX);
107712bd3c8bSSascha Wildner 
1078722d05c3SSascha Wildner 	lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
107957bed822SMarkus Pfeiffer 
108057bed822SMarkus Pfeiffer 	/* cancel any leftover CCBs */
108157bed822SMarkus Pfeiffer 	umass_cancel_ccb(sc);
108257bed822SMarkus Pfeiffer 
108312bd3c8bSSascha Wildner 	umass_cam_detach_sim(sc);
108412bd3c8bSSascha Wildner 
1085722d05c3SSascha Wildner 	lockmgr(&sc->sc_lock, LK_RELEASE);
1086722d05c3SSascha Wildner 	lockuninit(&sc->sc_lock);
108712bd3c8bSSascha Wildner 
108812bd3c8bSSascha Wildner 	return (0);			/* success */
108912bd3c8bSSascha Wildner }
109012bd3c8bSSascha Wildner 
109112bd3c8bSSascha Wildner static void
umass_init_shuttle(struct umass_softc * sc)109212bd3c8bSSascha Wildner umass_init_shuttle(struct umass_softc *sc)
109312bd3c8bSSascha Wildner {
109412bd3c8bSSascha Wildner 	struct usb_device_request req;
109512bd3c8bSSascha Wildner 	usb_error_t err;
109612bd3c8bSSascha Wildner 	uint8_t status[2] = {0, 0};
109712bd3c8bSSascha Wildner 
109812bd3c8bSSascha Wildner 	/*
109912bd3c8bSSascha Wildner 	 * The Linux driver does this, but no one can tell us what the
110012bd3c8bSSascha Wildner 	 * command does.
110112bd3c8bSSascha Wildner 	 */
110212bd3c8bSSascha Wildner 	req.bmRequestType = UT_READ_VENDOR_DEVICE;
110312bd3c8bSSascha Wildner 	req.bRequest = 1;		/* XXX unknown command */
110412bd3c8bSSascha Wildner 	USETW(req.wValue, 0);
110512bd3c8bSSascha Wildner 	req.wIndex[0] = sc->sc_iface_no;
110612bd3c8bSSascha Wildner 	req.wIndex[1] = 0;
110712bd3c8bSSascha Wildner 	USETW(req.wLength, sizeof(status));
110812bd3c8bSSascha Wildner 	err = usbd_do_request(sc->sc_udev, NULL, &req, &status);
110912bd3c8bSSascha Wildner 
111012bd3c8bSSascha Wildner 	DPRINTF(sc, UDMASS_GEN, "Shuttle init returned 0x%02x%02x\n",
111112bd3c8bSSascha Wildner 	    status[0], status[1]);
111212bd3c8bSSascha Wildner }
111312bd3c8bSSascha Wildner 
111412bd3c8bSSascha Wildner /*
111512bd3c8bSSascha Wildner  * Generic functions to handle transfers
111612bd3c8bSSascha Wildner  */
111712bd3c8bSSascha Wildner 
111812bd3c8bSSascha Wildner static void
umass_transfer_start(struct umass_softc * sc,uint8_t xfer_index)111912bd3c8bSSascha Wildner umass_transfer_start(struct umass_softc *sc, uint8_t xfer_index)
112012bd3c8bSSascha Wildner {
112112bd3c8bSSascha Wildner 	DPRINTF(sc, UDMASS_GEN, "transfer index = "
112212bd3c8bSSascha Wildner 	    "%d\n", xfer_index);
112312bd3c8bSSascha Wildner 
112412bd3c8bSSascha Wildner 	if (sc->sc_xfer[xfer_index]) {
112512bd3c8bSSascha Wildner 		sc->sc_last_xfer_index = xfer_index;
112612bd3c8bSSascha Wildner 		usbd_transfer_start(sc->sc_xfer[xfer_index]);
112712bd3c8bSSascha Wildner 	} else {
112812bd3c8bSSascha Wildner 		umass_cancel_ccb(sc);
112912bd3c8bSSascha Wildner 	}
113012bd3c8bSSascha Wildner }
113112bd3c8bSSascha Wildner 
113212bd3c8bSSascha Wildner static void
umass_reset(struct umass_softc * sc)113312bd3c8bSSascha Wildner umass_reset(struct umass_softc *sc)
113412bd3c8bSSascha Wildner {
113512bd3c8bSSascha Wildner 	DPRINTF(sc, UDMASS_GEN, "resetting device\n");
113612bd3c8bSSascha Wildner 
113712bd3c8bSSascha Wildner 	/*
113812bd3c8bSSascha Wildner 	 * stop the last transfer, if not already stopped:
113912bd3c8bSSascha Wildner 	 */
114012bd3c8bSSascha Wildner 	usbd_transfer_stop(sc->sc_xfer[sc->sc_last_xfer_index]);
114112bd3c8bSSascha Wildner 	umass_transfer_start(sc, 0);
114212bd3c8bSSascha Wildner }
114312bd3c8bSSascha Wildner 
114412bd3c8bSSascha Wildner static void
umass_cancel_ccb(struct umass_softc * sc)114512bd3c8bSSascha Wildner umass_cancel_ccb(struct umass_softc *sc)
114612bd3c8bSSascha Wildner {
114712bd3c8bSSascha Wildner 	union ccb *ccb;
114812bd3c8bSSascha Wildner 
114963da4a34SSascha Wildner #if 0
115063da4a34SSascha Wildner 	KKASSERT(lockstatus(&sc->sc_lock, curthread) != 0);
115163da4a34SSascha Wildner #endif
115263da4a34SSascha Wildner 
115312bd3c8bSSascha Wildner 	ccb = sc->sc_transfer.ccb;
115412bd3c8bSSascha Wildner 	sc->sc_transfer.ccb = NULL;
115512bd3c8bSSascha Wildner 	sc->sc_last_xfer_index = 0;
115612bd3c8bSSascha Wildner 
115712bd3c8bSSascha Wildner 	if (ccb) {
115812bd3c8bSSascha Wildner 		(sc->sc_transfer.callback)
115912bd3c8bSSascha Wildner 		    (sc, ccb, (sc->sc_transfer.data_len -
116012bd3c8bSSascha Wildner 		    sc->sc_transfer.actlen), STATUS_WIRE_FAILED);
116112bd3c8bSSascha Wildner 	}
116212bd3c8bSSascha Wildner }
116312bd3c8bSSascha Wildner 
116412bd3c8bSSascha Wildner static void
umass_tr_error(struct usb_xfer * xfer,usb_error_t error)116512bd3c8bSSascha Wildner umass_tr_error(struct usb_xfer *xfer, usb_error_t error)
116612bd3c8bSSascha Wildner {
116712bd3c8bSSascha Wildner 	struct umass_softc *sc = usbd_xfer_softc(xfer);
116812bd3c8bSSascha Wildner 
116912bd3c8bSSascha Wildner 	if (error != USB_ERR_CANCELLED) {
117012bd3c8bSSascha Wildner 
117112bd3c8bSSascha Wildner 		DPRINTF(sc, UDMASS_GEN, "transfer error, %s -> "
117212bd3c8bSSascha Wildner 		    "reset\n", usbd_errstr(error));
117312bd3c8bSSascha Wildner 	}
117412bd3c8bSSascha Wildner 	umass_cancel_ccb(sc);
117512bd3c8bSSascha Wildner }
117612bd3c8bSSascha Wildner 
117712bd3c8bSSascha Wildner /*
117812bd3c8bSSascha Wildner  * BBB protocol specific functions
117912bd3c8bSSascha Wildner  */
118012bd3c8bSSascha Wildner 
118112bd3c8bSSascha Wildner static void
umass_t_bbb_reset1_callback(struct usb_xfer * xfer,usb_error_t error)118212bd3c8bSSascha Wildner umass_t_bbb_reset1_callback(struct usb_xfer *xfer, usb_error_t error)
118312bd3c8bSSascha Wildner {
118412bd3c8bSSascha Wildner 	struct umass_softc *sc = usbd_xfer_softc(xfer);
118512bd3c8bSSascha Wildner 	struct usb_device_request req;
118612bd3c8bSSascha Wildner 	struct usb_page_cache *pc;
118712bd3c8bSSascha Wildner 
118812bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
118912bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
119012bd3c8bSSascha Wildner 		umass_transfer_start(sc, UMASS_T_BBB_RESET2);
119112bd3c8bSSascha Wildner 		return;
119212bd3c8bSSascha Wildner 
119312bd3c8bSSascha Wildner 	case USB_ST_SETUP:
119412bd3c8bSSascha Wildner 		/*
119512bd3c8bSSascha Wildner 		 * Reset recovery (5.3.4 in Universal Serial Bus Mass Storage Class)
119612bd3c8bSSascha Wildner 		 *
119712bd3c8bSSascha Wildner 		 * For Reset Recovery the host shall issue in the following order:
119812bd3c8bSSascha Wildner 		 * a) a Bulk-Only Mass Storage Reset
119912bd3c8bSSascha Wildner 		 * b) a Clear Feature HALT to the Bulk-In endpoint
120012bd3c8bSSascha Wildner 		 * c) a Clear Feature HALT to the Bulk-Out endpoint
120112bd3c8bSSascha Wildner 		 *
120212bd3c8bSSascha Wildner 		 * This is done in 3 steps, using 3 transfers:
120312bd3c8bSSascha Wildner 		 * UMASS_T_BBB_RESET1
120412bd3c8bSSascha Wildner 		 * UMASS_T_BBB_RESET2
120512bd3c8bSSascha Wildner 		 * UMASS_T_BBB_RESET3
120612bd3c8bSSascha Wildner 		 */
120712bd3c8bSSascha Wildner 
120812bd3c8bSSascha Wildner 		DPRINTF(sc, UDMASS_BBB, "BBB reset!\n");
120912bd3c8bSSascha Wildner 
121012bd3c8bSSascha Wildner 		req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
121112bd3c8bSSascha Wildner 		req.bRequest = UR_BBB_RESET;	/* bulk only reset */
121212bd3c8bSSascha Wildner 		USETW(req.wValue, 0);
121312bd3c8bSSascha Wildner 		req.wIndex[0] = sc->sc_iface_no;
121412bd3c8bSSascha Wildner 		req.wIndex[1] = 0;
121512bd3c8bSSascha Wildner 		USETW(req.wLength, 0);
121612bd3c8bSSascha Wildner 
121712bd3c8bSSascha Wildner 		pc = usbd_xfer_get_frame(xfer, 0);
121812bd3c8bSSascha Wildner 		usbd_copy_in(pc, 0, &req, sizeof(req));
121912bd3c8bSSascha Wildner 
122012bd3c8bSSascha Wildner 		usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
122112bd3c8bSSascha Wildner 		usbd_xfer_set_frames(xfer, 1);
122212bd3c8bSSascha Wildner 		usbd_transfer_submit(xfer);
122312bd3c8bSSascha Wildner 		return;
122412bd3c8bSSascha Wildner 
122512bd3c8bSSascha Wildner 	default:			/* Error */
122612bd3c8bSSascha Wildner 		umass_tr_error(xfer, error);
122712bd3c8bSSascha Wildner 		return;
122812bd3c8bSSascha Wildner 	}
122912bd3c8bSSascha Wildner }
123012bd3c8bSSascha Wildner 
123112bd3c8bSSascha Wildner static void
umass_t_bbb_reset2_callback(struct usb_xfer * xfer,usb_error_t error)123212bd3c8bSSascha Wildner umass_t_bbb_reset2_callback(struct usb_xfer *xfer, usb_error_t error)
123312bd3c8bSSascha Wildner {
123412bd3c8bSSascha Wildner 	umass_t_bbb_data_clear_stall_callback(xfer, UMASS_T_BBB_RESET3,
123512bd3c8bSSascha Wildner 	    UMASS_T_BBB_DATA_READ, error);
123612bd3c8bSSascha Wildner }
123712bd3c8bSSascha Wildner 
123812bd3c8bSSascha Wildner static void
umass_t_bbb_reset3_callback(struct usb_xfer * xfer,usb_error_t error)123912bd3c8bSSascha Wildner umass_t_bbb_reset3_callback(struct usb_xfer *xfer, usb_error_t error)
124012bd3c8bSSascha Wildner {
124112bd3c8bSSascha Wildner 	umass_t_bbb_data_clear_stall_callback(xfer, UMASS_T_BBB_COMMAND,
124212bd3c8bSSascha Wildner 	    UMASS_T_BBB_DATA_WRITE, error);
124312bd3c8bSSascha Wildner }
124412bd3c8bSSascha Wildner 
124512bd3c8bSSascha Wildner static void
umass_t_bbb_data_clear_stall_callback(struct usb_xfer * xfer,uint8_t next_xfer,uint8_t stall_xfer,usb_error_t error)124612bd3c8bSSascha Wildner umass_t_bbb_data_clear_stall_callback(struct usb_xfer *xfer,
124712bd3c8bSSascha Wildner     uint8_t next_xfer, uint8_t stall_xfer, usb_error_t error)
124812bd3c8bSSascha Wildner {
124912bd3c8bSSascha Wildner 	struct umass_softc *sc = usbd_xfer_softc(xfer);
125012bd3c8bSSascha Wildner 
125112bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
125212bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
125312bd3c8bSSascha Wildner tr_transferred:
125412bd3c8bSSascha Wildner 		umass_transfer_start(sc, next_xfer);
125512bd3c8bSSascha Wildner 		return;
125612bd3c8bSSascha Wildner 
125712bd3c8bSSascha Wildner 	case USB_ST_SETUP:
125812bd3c8bSSascha Wildner 		if (usbd_clear_stall_callback(xfer, sc->sc_xfer[stall_xfer])) {
125912bd3c8bSSascha Wildner 			goto tr_transferred;
126012bd3c8bSSascha Wildner 		}
126112bd3c8bSSascha Wildner 		return;
126212bd3c8bSSascha Wildner 
126312bd3c8bSSascha Wildner 	default:			/* Error */
126412bd3c8bSSascha Wildner 		umass_tr_error(xfer, error);
126512bd3c8bSSascha Wildner 		return;
126612bd3c8bSSascha Wildner 	}
126712bd3c8bSSascha Wildner }
126812bd3c8bSSascha Wildner 
126912bd3c8bSSascha Wildner static void
umass_t_bbb_command_callback(struct usb_xfer * xfer,usb_error_t error)127012bd3c8bSSascha Wildner umass_t_bbb_command_callback(struct usb_xfer *xfer, usb_error_t error)
127112bd3c8bSSascha Wildner {
127212bd3c8bSSascha Wildner 	struct umass_softc *sc = usbd_xfer_softc(xfer);
127312bd3c8bSSascha Wildner 	union ccb *ccb = sc->sc_transfer.ccb;
127412bd3c8bSSascha Wildner 	struct usb_page_cache *pc;
127512bd3c8bSSascha Wildner 	uint32_t tag;
127612bd3c8bSSascha Wildner 
127712bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
127812bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
127912bd3c8bSSascha Wildner 		umass_transfer_start
128012bd3c8bSSascha Wildner 		    (sc, ((sc->sc_transfer.dir == DIR_IN) ? UMASS_T_BBB_DATA_READ :
128112bd3c8bSSascha Wildner 		    (sc->sc_transfer.dir == DIR_OUT) ? UMASS_T_BBB_DATA_WRITE :
128212bd3c8bSSascha Wildner 		    UMASS_T_BBB_STATUS));
128312bd3c8bSSascha Wildner 		return;
128412bd3c8bSSascha Wildner 
128512bd3c8bSSascha Wildner 	case USB_ST_SETUP:
128612bd3c8bSSascha Wildner 
128712bd3c8bSSascha Wildner 		sc->sc_status_try = 0;
128812bd3c8bSSascha Wildner 
128912bd3c8bSSascha Wildner 		if (ccb) {
129012bd3c8bSSascha Wildner 
129112bd3c8bSSascha Wildner 			/*
129212bd3c8bSSascha Wildner 		         * the initial value is not important,
129312bd3c8bSSascha Wildner 		         * as long as the values are unique:
129412bd3c8bSSascha Wildner 		         */
129512bd3c8bSSascha Wildner 			tag = UGETDW(sc->cbw.dCBWTag) + 1;
129612bd3c8bSSascha Wildner 
129712bd3c8bSSascha Wildner 			USETDW(sc->cbw.dCBWSignature, CBWSIGNATURE);
129812bd3c8bSSascha Wildner 			USETDW(sc->cbw.dCBWTag, tag);
129912bd3c8bSSascha Wildner 
130012bd3c8bSSascha Wildner 			/*
130112bd3c8bSSascha Wildner 		         * dCBWDataTransferLength:
130212bd3c8bSSascha Wildner 		         *   This field indicates the number of bytes of data that the host
130312bd3c8bSSascha Wildner 		         *   intends to transfer on the IN or OUT Bulk endpoint(as indicated by
130412bd3c8bSSascha Wildner 		         *   the Direction bit) during the execution of this command. If this
130512bd3c8bSSascha Wildner 		         *   field is set to 0, the device will expect that no data will be
130612bd3c8bSSascha Wildner 		         *   transferred IN or OUT during this command, regardless of the value
130712bd3c8bSSascha Wildner 		         *   of the Direction bit defined in dCBWFlags.
130812bd3c8bSSascha Wildner 		         */
130912bd3c8bSSascha Wildner 			USETDW(sc->cbw.dCBWDataTransferLength, sc->sc_transfer.data_len);
131012bd3c8bSSascha Wildner 
131112bd3c8bSSascha Wildner 			/*
131212bd3c8bSSascha Wildner 		         * dCBWFlags:
131312bd3c8bSSascha Wildner 		         *   The bits of the Flags field are defined as follows:
131412bd3c8bSSascha Wildner 		         *     Bits 0-6  reserved
131512bd3c8bSSascha Wildner 		         *     Bit  7    Direction - this bit shall be ignored if the
131612bd3c8bSSascha Wildner 		         *                           dCBWDataTransferLength field is zero.
131712bd3c8bSSascha Wildner 		         *               0 = data Out from host to device
131812bd3c8bSSascha Wildner 		         *               1 = data In from device to host
131912bd3c8bSSascha Wildner 		         */
132012bd3c8bSSascha Wildner 			sc->cbw.bCBWFlags = ((sc->sc_transfer.dir == DIR_IN) ?
132112bd3c8bSSascha Wildner 			    CBWFLAGS_IN : CBWFLAGS_OUT);
132212bd3c8bSSascha Wildner 			sc->cbw.bCBWLUN = sc->sc_transfer.lun;
132312bd3c8bSSascha Wildner 
132412bd3c8bSSascha Wildner 			if (sc->sc_transfer.cmd_len > sizeof(sc->cbw.CBWCDB)) {
132512bd3c8bSSascha Wildner 				sc->sc_transfer.cmd_len = sizeof(sc->cbw.CBWCDB);
132612bd3c8bSSascha Wildner 				DPRINTF(sc, UDMASS_BBB, "Truncating long command!\n");
132712bd3c8bSSascha Wildner 			}
132812bd3c8bSSascha Wildner 			sc->cbw.bCDBLength = sc->sc_transfer.cmd_len;
132912bd3c8bSSascha Wildner 
1330a41b1dd4SMarkus Pfeiffer 			/* copy SCSI command data */
133112bd3c8bSSascha Wildner 			memcpy(sc->cbw.CBWCDB, sc->sc_transfer.cmd_data,
133212bd3c8bSSascha Wildner 			    sc->sc_transfer.cmd_len);
133312bd3c8bSSascha Wildner 
1334a41b1dd4SMarkus Pfeiffer 			/* clear remaining command area */
1335a41b1dd4SMarkus Pfeiffer 			memset(sc->cbw.CBWCDB +
133612bd3c8bSSascha Wildner 			    sc->sc_transfer.cmd_len, 0,
133712bd3c8bSSascha Wildner 			    sizeof(sc->cbw.CBWCDB) -
133812bd3c8bSSascha Wildner 			    sc->sc_transfer.cmd_len);
133912bd3c8bSSascha Wildner 
134012bd3c8bSSascha Wildner 			DIF(UDMASS_BBB, umass_bbb_dump_cbw(sc, &sc->cbw));
134112bd3c8bSSascha Wildner 
134212bd3c8bSSascha Wildner 			pc = usbd_xfer_get_frame(xfer, 0);
134312bd3c8bSSascha Wildner 			usbd_copy_in(pc, 0, &sc->cbw, sizeof(sc->cbw));
134412bd3c8bSSascha Wildner 			usbd_xfer_set_frame_len(xfer, 0, sizeof(sc->cbw));
134512bd3c8bSSascha Wildner 
134612bd3c8bSSascha Wildner 			usbd_transfer_submit(xfer);
134712bd3c8bSSascha Wildner 		}
134812bd3c8bSSascha Wildner 		return;
134912bd3c8bSSascha Wildner 
135012bd3c8bSSascha Wildner 	default:			/* Error */
135112bd3c8bSSascha Wildner 		umass_tr_error(xfer, error);
135212bd3c8bSSascha Wildner 		return;
135312bd3c8bSSascha Wildner 	}
135412bd3c8bSSascha Wildner }
135512bd3c8bSSascha Wildner 
135612bd3c8bSSascha Wildner static void
umass_t_bbb_data_read_callback(struct usb_xfer * xfer,usb_error_t error)135712bd3c8bSSascha Wildner umass_t_bbb_data_read_callback(struct usb_xfer *xfer, usb_error_t error)
135812bd3c8bSSascha Wildner {
135912bd3c8bSSascha Wildner 	struct umass_softc *sc = usbd_xfer_softc(xfer);
136012bd3c8bSSascha Wildner 	uint32_t max_bulk = usbd_xfer_max_len(xfer);
136112bd3c8bSSascha Wildner 	int actlen, sumlen;
136212bd3c8bSSascha Wildner 
136312bd3c8bSSascha Wildner 	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
136412bd3c8bSSascha Wildner 
136512bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
136612bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
136712bd3c8bSSascha Wildner 		sc->sc_transfer.data_rem -= actlen;
136812bd3c8bSSascha Wildner 		sc->sc_transfer.data_ptr += actlen;
136912bd3c8bSSascha Wildner 		sc->sc_transfer.actlen += actlen;
137012bd3c8bSSascha Wildner 
137112bd3c8bSSascha Wildner 		if (actlen < sumlen) {
137212bd3c8bSSascha Wildner 			/* short transfer */
137312bd3c8bSSascha Wildner 			sc->sc_transfer.data_rem = 0;
137412bd3c8bSSascha Wildner 		}
137512bd3c8bSSascha Wildner 	case USB_ST_SETUP:
137612bd3c8bSSascha Wildner 		DPRINTF(sc, UDMASS_BBB, "max_bulk=%d, data_rem=%d\n",
137712bd3c8bSSascha Wildner 		    max_bulk, sc->sc_transfer.data_rem);
137812bd3c8bSSascha Wildner 
137912bd3c8bSSascha Wildner 		if (sc->sc_transfer.data_rem == 0) {
138012bd3c8bSSascha Wildner 			umass_transfer_start(sc, UMASS_T_BBB_STATUS);
138112bd3c8bSSascha Wildner 			return;
138212bd3c8bSSascha Wildner 		}
138312bd3c8bSSascha Wildner 		if (max_bulk > sc->sc_transfer.data_rem) {
138412bd3c8bSSascha Wildner 			max_bulk = sc->sc_transfer.data_rem;
138512bd3c8bSSascha Wildner 		}
138612bd3c8bSSascha Wildner 		usbd_xfer_set_timeout(xfer, sc->sc_transfer.data_timeout);
138712bd3c8bSSascha Wildner 
138812bd3c8bSSascha Wildner 		usbd_xfer_set_frame_data(xfer, 0, sc->sc_transfer.data_ptr,
138912bd3c8bSSascha Wildner 		    max_bulk);
139057bed822SMarkus Pfeiffer 
139112bd3c8bSSascha Wildner 		usbd_transfer_submit(xfer);
139212bd3c8bSSascha Wildner 		return;
139312bd3c8bSSascha Wildner 
139412bd3c8bSSascha Wildner 	default:			/* Error */
139512bd3c8bSSascha Wildner 		if (error == USB_ERR_CANCELLED) {
139612bd3c8bSSascha Wildner 			umass_tr_error(xfer, error);
139712bd3c8bSSascha Wildner 		} else {
139812bd3c8bSSascha Wildner 			umass_transfer_start(sc, UMASS_T_BBB_DATA_RD_CS);
139912bd3c8bSSascha Wildner 		}
140012bd3c8bSSascha Wildner 		return;
140112bd3c8bSSascha Wildner 	}
140212bd3c8bSSascha Wildner }
140312bd3c8bSSascha Wildner 
140412bd3c8bSSascha Wildner static void
umass_t_bbb_data_rd_cs_callback(struct usb_xfer * xfer,usb_error_t error)140512bd3c8bSSascha Wildner umass_t_bbb_data_rd_cs_callback(struct usb_xfer *xfer, usb_error_t error)
140612bd3c8bSSascha Wildner {
140712bd3c8bSSascha Wildner 	umass_t_bbb_data_clear_stall_callback(xfer, UMASS_T_BBB_STATUS,
140812bd3c8bSSascha Wildner 	    UMASS_T_BBB_DATA_READ, error);
140912bd3c8bSSascha Wildner }
141012bd3c8bSSascha Wildner 
141112bd3c8bSSascha Wildner static void
umass_t_bbb_data_write_callback(struct usb_xfer * xfer,usb_error_t error)141212bd3c8bSSascha Wildner umass_t_bbb_data_write_callback(struct usb_xfer *xfer, usb_error_t error)
141312bd3c8bSSascha Wildner {
141412bd3c8bSSascha Wildner 	struct umass_softc *sc = usbd_xfer_softc(xfer);
141512bd3c8bSSascha Wildner 	uint32_t max_bulk = usbd_xfer_max_len(xfer);
141612bd3c8bSSascha Wildner 	int actlen, sumlen;
141712bd3c8bSSascha Wildner 
141812bd3c8bSSascha Wildner 	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
141912bd3c8bSSascha Wildner 
142012bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
142112bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
142212bd3c8bSSascha Wildner 		sc->sc_transfer.data_rem -= actlen;
142312bd3c8bSSascha Wildner 		sc->sc_transfer.data_ptr += actlen;
142412bd3c8bSSascha Wildner 		sc->sc_transfer.actlen += actlen;
142512bd3c8bSSascha Wildner 
142612bd3c8bSSascha Wildner 		if (actlen < sumlen) {
142712bd3c8bSSascha Wildner 			/* short transfer */
142812bd3c8bSSascha Wildner 			sc->sc_transfer.data_rem = 0;
142912bd3c8bSSascha Wildner 		}
143012bd3c8bSSascha Wildner 	case USB_ST_SETUP:
143112bd3c8bSSascha Wildner 		DPRINTF(sc, UDMASS_BBB, "max_bulk=%d, data_rem=%d\n",
143212bd3c8bSSascha Wildner 		    max_bulk, sc->sc_transfer.data_rem);
143312bd3c8bSSascha Wildner 
143412bd3c8bSSascha Wildner 		if (sc->sc_transfer.data_rem == 0) {
143512bd3c8bSSascha Wildner 			umass_transfer_start(sc, UMASS_T_BBB_STATUS);
143612bd3c8bSSascha Wildner 			return;
143712bd3c8bSSascha Wildner 		}
143812bd3c8bSSascha Wildner 		if (max_bulk > sc->sc_transfer.data_rem) {
143912bd3c8bSSascha Wildner 			max_bulk = sc->sc_transfer.data_rem;
144012bd3c8bSSascha Wildner 		}
144112bd3c8bSSascha Wildner 		usbd_xfer_set_timeout(xfer, sc->sc_transfer.data_timeout);
144212bd3c8bSSascha Wildner 
144312bd3c8bSSascha Wildner 		usbd_xfer_set_frame_data(xfer, 0, sc->sc_transfer.data_ptr,
144412bd3c8bSSascha Wildner 		    max_bulk);
144512bd3c8bSSascha Wildner 
144612bd3c8bSSascha Wildner 		usbd_transfer_submit(xfer);
144712bd3c8bSSascha Wildner 		return;
144812bd3c8bSSascha Wildner 
144912bd3c8bSSascha Wildner 	default:			/* Error */
145012bd3c8bSSascha Wildner 		if (error == USB_ERR_CANCELLED) {
145112bd3c8bSSascha Wildner 			umass_tr_error(xfer, error);
145212bd3c8bSSascha Wildner 		} else {
145312bd3c8bSSascha Wildner 			umass_transfer_start(sc, UMASS_T_BBB_DATA_WR_CS);
145412bd3c8bSSascha Wildner 		}
145512bd3c8bSSascha Wildner 		return;
145612bd3c8bSSascha Wildner 	}
145712bd3c8bSSascha Wildner }
145812bd3c8bSSascha Wildner 
145912bd3c8bSSascha Wildner static void
umass_t_bbb_data_wr_cs_callback(struct usb_xfer * xfer,usb_error_t error)146012bd3c8bSSascha Wildner umass_t_bbb_data_wr_cs_callback(struct usb_xfer *xfer, usb_error_t error)
146112bd3c8bSSascha Wildner {
146212bd3c8bSSascha Wildner 	umass_t_bbb_data_clear_stall_callback(xfer, UMASS_T_BBB_STATUS,
146312bd3c8bSSascha Wildner 	    UMASS_T_BBB_DATA_WRITE, error);
146412bd3c8bSSascha Wildner }
146512bd3c8bSSascha Wildner 
146612bd3c8bSSascha Wildner static void
umass_t_bbb_status_callback(struct usb_xfer * xfer,usb_error_t error)146712bd3c8bSSascha Wildner umass_t_bbb_status_callback(struct usb_xfer *xfer, usb_error_t error)
146812bd3c8bSSascha Wildner {
146912bd3c8bSSascha Wildner 	struct umass_softc *sc = usbd_xfer_softc(xfer);
147012bd3c8bSSascha Wildner 	union ccb *ccb = sc->sc_transfer.ccb;
147112bd3c8bSSascha Wildner 	struct usb_page_cache *pc;
147212bd3c8bSSascha Wildner 	uint32_t residue;
147312bd3c8bSSascha Wildner 	int actlen;
147412bd3c8bSSascha Wildner 
147512bd3c8bSSascha Wildner 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
147612bd3c8bSSascha Wildner 
147712bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
147812bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
147912bd3c8bSSascha Wildner 
148012bd3c8bSSascha Wildner 		/*
148112bd3c8bSSascha Wildner 		 * Do a full reset if there is something wrong with the CSW:
148212bd3c8bSSascha Wildner 		 */
148312bd3c8bSSascha Wildner 		sc->sc_status_try = 1;
148412bd3c8bSSascha Wildner 
148512bd3c8bSSascha Wildner 		/* Zero missing parts of the CSW: */
148612bd3c8bSSascha Wildner 
148757bed822SMarkus Pfeiffer 		if (actlen < (int)sizeof(sc->csw))
148812bd3c8bSSascha Wildner 			memset(&sc->csw, 0, sizeof(sc->csw));
148912bd3c8bSSascha Wildner 
149012bd3c8bSSascha Wildner 		pc = usbd_xfer_get_frame(xfer, 0);
149112bd3c8bSSascha Wildner 		usbd_copy_out(pc, 0, &sc->csw, actlen);
149212bd3c8bSSascha Wildner 
149312bd3c8bSSascha Wildner 		DIF(UDMASS_BBB, umass_bbb_dump_csw(sc, &sc->csw));
149412bd3c8bSSascha Wildner 
149512bd3c8bSSascha Wildner 		residue = UGETDW(sc->csw.dCSWDataResidue);
149612bd3c8bSSascha Wildner 
149712bd3c8bSSascha Wildner 		if ((!residue) || (sc->sc_quirks & IGNORE_RESIDUE)) {
149812bd3c8bSSascha Wildner 			residue = (sc->sc_transfer.data_len -
149912bd3c8bSSascha Wildner 			    sc->sc_transfer.actlen);
150012bd3c8bSSascha Wildner 		}
150112bd3c8bSSascha Wildner 		if (residue > sc->sc_transfer.data_len) {
150212bd3c8bSSascha Wildner 			DPRINTF(sc, UDMASS_BBB, "truncating residue from %d "
150312bd3c8bSSascha Wildner 			    "to %d bytes\n", residue, sc->sc_transfer.data_len);
150412bd3c8bSSascha Wildner 			residue = sc->sc_transfer.data_len;
150512bd3c8bSSascha Wildner 		}
150612bd3c8bSSascha Wildner 		/* translate weird command-status signatures: */
150712bd3c8bSSascha Wildner 		if (sc->sc_quirks & WRONG_CSWSIG) {
150812bd3c8bSSascha Wildner 
150912bd3c8bSSascha Wildner 			uint32_t temp = UGETDW(sc->csw.dCSWSignature);
151012bd3c8bSSascha Wildner 
151112bd3c8bSSascha Wildner 			if ((temp == CSWSIGNATURE_OLYMPUS_C1) ||
151212bd3c8bSSascha Wildner 			    (temp == CSWSIGNATURE_IMAGINATION_DBX1)) {
151312bd3c8bSSascha Wildner 				USETDW(sc->csw.dCSWSignature, CSWSIGNATURE);
151412bd3c8bSSascha Wildner 			}
151512bd3c8bSSascha Wildner 		}
151612bd3c8bSSascha Wildner 		/* check CSW and handle eventual error */
151712bd3c8bSSascha Wildner 		if (UGETDW(sc->csw.dCSWSignature) != CSWSIGNATURE) {
151812bd3c8bSSascha Wildner 			DPRINTF(sc, UDMASS_BBB, "bad CSW signature 0x%08x != 0x%08x\n",
151912bd3c8bSSascha Wildner 			    UGETDW(sc->csw.dCSWSignature), CSWSIGNATURE);
152012bd3c8bSSascha Wildner 			/*
152112bd3c8bSSascha Wildner 			 * Invalid CSW: Wrong signature or wrong tag might
152212bd3c8bSSascha Wildner 			 * indicate that we lost synchronization. Reset the
152312bd3c8bSSascha Wildner 			 * device.
152412bd3c8bSSascha Wildner 			 */
152512bd3c8bSSascha Wildner 			goto tr_error;
152612bd3c8bSSascha Wildner 		} else if (UGETDW(sc->csw.dCSWTag) != UGETDW(sc->cbw.dCBWTag)) {
152712bd3c8bSSascha Wildner 			DPRINTF(sc, UDMASS_BBB, "Invalid CSW: tag 0x%08x should be "
152812bd3c8bSSascha Wildner 			    "0x%08x\n", UGETDW(sc->csw.dCSWTag),
152912bd3c8bSSascha Wildner 			    UGETDW(sc->cbw.dCBWTag));
153012bd3c8bSSascha Wildner 			goto tr_error;
153112bd3c8bSSascha Wildner 		} else if (sc->csw.bCSWStatus > CSWSTATUS_PHASE) {
153212bd3c8bSSascha Wildner 			DPRINTF(sc, UDMASS_BBB, "Invalid CSW: status %d > %d\n",
153312bd3c8bSSascha Wildner 			    sc->csw.bCSWStatus, CSWSTATUS_PHASE);
153412bd3c8bSSascha Wildner 			goto tr_error;
153512bd3c8bSSascha Wildner 		} else if (sc->csw.bCSWStatus == CSWSTATUS_PHASE) {
153612bd3c8bSSascha Wildner 			DPRINTF(sc, UDMASS_BBB, "Phase error, residue = "
153712bd3c8bSSascha Wildner 			    "%d\n", residue);
153812bd3c8bSSascha Wildner 			goto tr_error;
153912bd3c8bSSascha Wildner 		} else if (sc->sc_transfer.actlen > sc->sc_transfer.data_len) {
154012bd3c8bSSascha Wildner 			DPRINTF(sc, UDMASS_BBB, "Buffer overrun %d > %d\n",
154112bd3c8bSSascha Wildner 			    sc->sc_transfer.actlen, sc->sc_transfer.data_len);
154212bd3c8bSSascha Wildner 			goto tr_error;
154312bd3c8bSSascha Wildner 		} else if (sc->csw.bCSWStatus == CSWSTATUS_FAILED) {
154412bd3c8bSSascha Wildner 			DPRINTF(sc, UDMASS_BBB, "Command failed, residue = "
154512bd3c8bSSascha Wildner 			    "%d\n", residue);
154612bd3c8bSSascha Wildner 
154712bd3c8bSSascha Wildner 			sc->sc_transfer.ccb = NULL;
154812bd3c8bSSascha Wildner 
154912bd3c8bSSascha Wildner 			sc->sc_last_xfer_index = UMASS_T_BBB_COMMAND;
155012bd3c8bSSascha Wildner 
155112bd3c8bSSascha Wildner 			(sc->sc_transfer.callback)
155212bd3c8bSSascha Wildner 			    (sc, ccb, residue, STATUS_CMD_FAILED);
155312bd3c8bSSascha Wildner 		} else {
155412bd3c8bSSascha Wildner 			sc->sc_transfer.ccb = NULL;
155512bd3c8bSSascha Wildner 
155612bd3c8bSSascha Wildner 			sc->sc_last_xfer_index = UMASS_T_BBB_COMMAND;
155712bd3c8bSSascha Wildner 
155812bd3c8bSSascha Wildner 			(sc->sc_transfer.callback)
155912bd3c8bSSascha Wildner 			    (sc, ccb, residue, STATUS_CMD_OK);
156012bd3c8bSSascha Wildner 		}
156112bd3c8bSSascha Wildner 		return;
156212bd3c8bSSascha Wildner 
156312bd3c8bSSascha Wildner 	case USB_ST_SETUP:
156412bd3c8bSSascha Wildner 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
156512bd3c8bSSascha Wildner 		usbd_transfer_submit(xfer);
156612bd3c8bSSascha Wildner 		return;
156712bd3c8bSSascha Wildner 
156812bd3c8bSSascha Wildner 	default:
156912bd3c8bSSascha Wildner tr_error:
157012bd3c8bSSascha Wildner 		DPRINTF(sc, UDMASS_BBB, "Failed to read CSW: %s, try %d\n",
157112bd3c8bSSascha Wildner 		    usbd_errstr(error), sc->sc_status_try);
157212bd3c8bSSascha Wildner 
157312bd3c8bSSascha Wildner 		if ((error == USB_ERR_CANCELLED) ||
157412bd3c8bSSascha Wildner 		    (sc->sc_status_try)) {
157512bd3c8bSSascha Wildner 			umass_tr_error(xfer, error);
157612bd3c8bSSascha Wildner 		} else {
157712bd3c8bSSascha Wildner 			sc->sc_status_try = 1;
157812bd3c8bSSascha Wildner 			umass_transfer_start(sc, UMASS_T_BBB_DATA_RD_CS);
157912bd3c8bSSascha Wildner 		}
158012bd3c8bSSascha Wildner 		return;
158112bd3c8bSSascha Wildner 	}
158212bd3c8bSSascha Wildner }
158312bd3c8bSSascha Wildner 
158412bd3c8bSSascha Wildner static void
umass_command_start(struct umass_softc * sc,uint8_t dir,void * data_ptr,uint32_t data_len,uint32_t data_timeout,umass_callback_t * callback,union ccb * ccb)158512bd3c8bSSascha Wildner umass_command_start(struct umass_softc *sc, uint8_t dir,
158612bd3c8bSSascha Wildner     void *data_ptr, uint32_t data_len,
158712bd3c8bSSascha Wildner     uint32_t data_timeout, umass_callback_t *callback,
158812bd3c8bSSascha Wildner     union ccb *ccb)
158912bd3c8bSSascha Wildner {
159012bd3c8bSSascha Wildner 	sc->sc_transfer.lun = ccb->ccb_h.target_lun;
159112bd3c8bSSascha Wildner 
159212bd3c8bSSascha Wildner 	/*
159312bd3c8bSSascha Wildner 	 * NOTE: assumes that "sc->sc_transfer.cmd_data" and
159412bd3c8bSSascha Wildner 	 * "sc->sc_transfer.cmd_len" has been properly
159512bd3c8bSSascha Wildner 	 * initialized.
159612bd3c8bSSascha Wildner 	 */
159712bd3c8bSSascha Wildner 
159812bd3c8bSSascha Wildner 	sc->sc_transfer.dir = data_len ? dir : DIR_NONE;
159912bd3c8bSSascha Wildner 	sc->sc_transfer.data_ptr = data_ptr;
160012bd3c8bSSascha Wildner 	sc->sc_transfer.data_len = data_len;
160112bd3c8bSSascha Wildner 	sc->sc_transfer.data_rem = data_len;
160212bd3c8bSSascha Wildner 	sc->sc_transfer.data_timeout = (data_timeout + UMASS_TIMEOUT);
160312bd3c8bSSascha Wildner 
160412bd3c8bSSascha Wildner 	sc->sc_transfer.actlen = 0;
160512bd3c8bSSascha Wildner 	sc->sc_transfer.callback = callback;
160612bd3c8bSSascha Wildner 	sc->sc_transfer.ccb = ccb;
160712bd3c8bSSascha Wildner 
160812bd3c8bSSascha Wildner 	if (sc->sc_xfer[sc->sc_last_xfer_index]) {
160912bd3c8bSSascha Wildner 		usbd_transfer_start(sc->sc_xfer[sc->sc_last_xfer_index]);
161012bd3c8bSSascha Wildner 	} else {
161157bed822SMarkus Pfeiffer 		umass_cancel_ccb(sc);
161212bd3c8bSSascha Wildner 	}
161312bd3c8bSSascha Wildner }
161412bd3c8bSSascha Wildner 
161512bd3c8bSSascha Wildner static uint8_t
umass_bbb_get_max_lun(struct umass_softc * sc)161612bd3c8bSSascha Wildner umass_bbb_get_max_lun(struct umass_softc *sc)
161712bd3c8bSSascha Wildner {
161812bd3c8bSSascha Wildner 	struct usb_device_request req;
161912bd3c8bSSascha Wildner 	usb_error_t err;
162012bd3c8bSSascha Wildner 	uint8_t buf = 0;
162112bd3c8bSSascha Wildner 
162212bd3c8bSSascha Wildner 	/* The Get Max Lun command is a class-specific request. */
162312bd3c8bSSascha Wildner 	req.bmRequestType = UT_READ_CLASS_INTERFACE;
162412bd3c8bSSascha Wildner 	req.bRequest = UR_BBB_GET_MAX_LUN;
162512bd3c8bSSascha Wildner 	USETW(req.wValue, 0);
162612bd3c8bSSascha Wildner 	req.wIndex[0] = sc->sc_iface_no;
162712bd3c8bSSascha Wildner 	req.wIndex[1] = 0;
162812bd3c8bSSascha Wildner 	USETW(req.wLength, 1);
162912bd3c8bSSascha Wildner 
163012bd3c8bSSascha Wildner 	err = usbd_do_request(sc->sc_udev, NULL, &req, &buf);
163112bd3c8bSSascha Wildner 	if (err) {
163212bd3c8bSSascha Wildner 		buf = 0;
163312bd3c8bSSascha Wildner 
163412bd3c8bSSascha Wildner 		/* Device doesn't support Get Max Lun request. */
1635722d05c3SSascha Wildner 		kprintf("%s: Get Max Lun not supported (%s)\n",
163612bd3c8bSSascha Wildner 		    sc->sc_name, usbd_errstr(err));
163712bd3c8bSSascha Wildner 	}
163812bd3c8bSSascha Wildner 	return (buf);
163912bd3c8bSSascha Wildner }
164012bd3c8bSSascha Wildner 
164112bd3c8bSSascha Wildner /*
164212bd3c8bSSascha Wildner  * Command/Bulk/Interrupt (CBI) specific functions
164312bd3c8bSSascha Wildner  */
164412bd3c8bSSascha Wildner 
164512bd3c8bSSascha Wildner static void
umass_cbi_start_status(struct umass_softc * sc)164612bd3c8bSSascha Wildner umass_cbi_start_status(struct umass_softc *sc)
164712bd3c8bSSascha Wildner {
164812bd3c8bSSascha Wildner 	if (sc->sc_xfer[UMASS_T_CBI_STATUS]) {
164912bd3c8bSSascha Wildner 		umass_transfer_start(sc, UMASS_T_CBI_STATUS);
165012bd3c8bSSascha Wildner 	} else {
165112bd3c8bSSascha Wildner 		union ccb *ccb = sc->sc_transfer.ccb;
165212bd3c8bSSascha Wildner 
165312bd3c8bSSascha Wildner 		sc->sc_transfer.ccb = NULL;
165412bd3c8bSSascha Wildner 
165512bd3c8bSSascha Wildner 		sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND;
165612bd3c8bSSascha Wildner 
165712bd3c8bSSascha Wildner 		(sc->sc_transfer.callback)
165812bd3c8bSSascha Wildner 		    (sc, ccb, (sc->sc_transfer.data_len -
165912bd3c8bSSascha Wildner 		    sc->sc_transfer.actlen), STATUS_CMD_UNKNOWN);
166012bd3c8bSSascha Wildner 	}
166112bd3c8bSSascha Wildner }
166212bd3c8bSSascha Wildner 
166312bd3c8bSSascha Wildner static void
umass_t_cbi_reset1_callback(struct usb_xfer * xfer,usb_error_t error)166412bd3c8bSSascha Wildner umass_t_cbi_reset1_callback(struct usb_xfer *xfer, usb_error_t error)
166512bd3c8bSSascha Wildner {
166612bd3c8bSSascha Wildner 	struct umass_softc *sc = usbd_xfer_softc(xfer);
166712bd3c8bSSascha Wildner 	struct usb_device_request req;
166812bd3c8bSSascha Wildner 	struct usb_page_cache *pc;
166912bd3c8bSSascha Wildner 	uint8_t buf[UMASS_CBI_DIAGNOSTIC_CMDLEN];
167012bd3c8bSSascha Wildner 
167112bd3c8bSSascha Wildner 	uint8_t i;
167212bd3c8bSSascha Wildner 
167312bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
167412bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
167512bd3c8bSSascha Wildner 		umass_transfer_start(sc, UMASS_T_CBI_RESET2);
167612bd3c8bSSascha Wildner 		break;
167712bd3c8bSSascha Wildner 
167812bd3c8bSSascha Wildner 	case USB_ST_SETUP:
167912bd3c8bSSascha Wildner 		/*
168012bd3c8bSSascha Wildner 		 * Command Block Reset Protocol
168112bd3c8bSSascha Wildner 		 *
168212bd3c8bSSascha Wildner 		 * First send a reset request to the device. Then clear
168312bd3c8bSSascha Wildner 		 * any possibly stalled bulk endpoints.
168412bd3c8bSSascha Wildner 		 *
168512bd3c8bSSascha Wildner 		 * This is done in 3 steps, using 3 transfers:
168612bd3c8bSSascha Wildner 		 * UMASS_T_CBI_RESET1
168712bd3c8bSSascha Wildner 		 * UMASS_T_CBI_RESET2
168812bd3c8bSSascha Wildner 		 * UMASS_T_CBI_RESET3
168912bd3c8bSSascha Wildner 		 * UMASS_T_CBI_RESET4 (only if there is an interrupt endpoint)
169012bd3c8bSSascha Wildner 		 */
169112bd3c8bSSascha Wildner 
169212bd3c8bSSascha Wildner 		DPRINTF(sc, UDMASS_CBI, "CBI reset!\n");
169312bd3c8bSSascha Wildner 
169412bd3c8bSSascha Wildner 		req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
169512bd3c8bSSascha Wildner 		req.bRequest = UR_CBI_ADSC;
169612bd3c8bSSascha Wildner 		USETW(req.wValue, 0);
169712bd3c8bSSascha Wildner 		req.wIndex[0] = sc->sc_iface_no;
169812bd3c8bSSascha Wildner 		req.wIndex[1] = 0;
169912bd3c8bSSascha Wildner 		USETW(req.wLength, UMASS_CBI_DIAGNOSTIC_CMDLEN);
170012bd3c8bSSascha Wildner 
170112bd3c8bSSascha Wildner 		/*
170212bd3c8bSSascha Wildner 		 * The 0x1d code is the SEND DIAGNOSTIC command. To
170312bd3c8bSSascha Wildner 		 * distinguish between the two, the last 10 bytes of the CBL
170412bd3c8bSSascha Wildner 		 * is filled with 0xff (section 2.2 of the CBI
170512bd3c8bSSascha Wildner 		 * specification)
170612bd3c8bSSascha Wildner 		 */
170712bd3c8bSSascha Wildner 		buf[0] = 0x1d;		/* Command Block Reset */
170812bd3c8bSSascha Wildner 		buf[1] = 0x04;
170912bd3c8bSSascha Wildner 
171012bd3c8bSSascha Wildner 		for (i = 2; i < UMASS_CBI_DIAGNOSTIC_CMDLEN; i++) {
171112bd3c8bSSascha Wildner 			buf[i] = 0xff;
171212bd3c8bSSascha Wildner 		}
171312bd3c8bSSascha Wildner 
171412bd3c8bSSascha Wildner 		pc = usbd_xfer_get_frame(xfer, 0);
171512bd3c8bSSascha Wildner 		usbd_copy_in(pc, 0, &req, sizeof(req));
171612bd3c8bSSascha Wildner 		pc = usbd_xfer_get_frame(xfer, 1);
171712bd3c8bSSascha Wildner 		usbd_copy_in(pc, 0, buf, sizeof(buf));
171812bd3c8bSSascha Wildner 
171912bd3c8bSSascha Wildner 		usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
172012bd3c8bSSascha Wildner 		usbd_xfer_set_frame_len(xfer, 1, sizeof(buf));
172112bd3c8bSSascha Wildner 		usbd_xfer_set_frames(xfer, 2);
172212bd3c8bSSascha Wildner 		usbd_transfer_submit(xfer);
172312bd3c8bSSascha Wildner 		break;
172412bd3c8bSSascha Wildner 
172512bd3c8bSSascha Wildner 	default:			/* Error */
172612bd3c8bSSascha Wildner 		if (error == USB_ERR_CANCELLED)
172712bd3c8bSSascha Wildner 			umass_tr_error(xfer, error);
172812bd3c8bSSascha Wildner 		else
172912bd3c8bSSascha Wildner 			umass_transfer_start(sc, UMASS_T_CBI_RESET2);
173012bd3c8bSSascha Wildner 		break;
173112bd3c8bSSascha Wildner 	}
173212bd3c8bSSascha Wildner }
173312bd3c8bSSascha Wildner 
173412bd3c8bSSascha Wildner static void
umass_t_cbi_reset2_callback(struct usb_xfer * xfer,usb_error_t error)173512bd3c8bSSascha Wildner umass_t_cbi_reset2_callback(struct usb_xfer *xfer, usb_error_t error)
173612bd3c8bSSascha Wildner {
173712bd3c8bSSascha Wildner 	umass_t_cbi_data_clear_stall_callback(xfer, UMASS_T_CBI_RESET3,
173812bd3c8bSSascha Wildner 	    UMASS_T_CBI_DATA_READ, error);
173912bd3c8bSSascha Wildner }
174012bd3c8bSSascha Wildner 
174112bd3c8bSSascha Wildner static void
umass_t_cbi_reset3_callback(struct usb_xfer * xfer,usb_error_t error)174212bd3c8bSSascha Wildner umass_t_cbi_reset3_callback(struct usb_xfer *xfer, usb_error_t error)
174312bd3c8bSSascha Wildner {
174412bd3c8bSSascha Wildner 	struct umass_softc *sc = usbd_xfer_softc(xfer);
174512bd3c8bSSascha Wildner 
174612bd3c8bSSascha Wildner 	umass_t_cbi_data_clear_stall_callback
174712bd3c8bSSascha Wildner 	    (xfer, (sc->sc_xfer[UMASS_T_CBI_RESET4] &&
174812bd3c8bSSascha Wildner 	    sc->sc_xfer[UMASS_T_CBI_STATUS]) ?
174912bd3c8bSSascha Wildner 	    UMASS_T_CBI_RESET4 : UMASS_T_CBI_COMMAND,
175012bd3c8bSSascha Wildner 	    UMASS_T_CBI_DATA_WRITE, error);
175112bd3c8bSSascha Wildner }
175212bd3c8bSSascha Wildner 
175312bd3c8bSSascha Wildner static void
umass_t_cbi_reset4_callback(struct usb_xfer * xfer,usb_error_t error)175412bd3c8bSSascha Wildner umass_t_cbi_reset4_callback(struct usb_xfer *xfer, usb_error_t error)
175512bd3c8bSSascha Wildner {
175612bd3c8bSSascha Wildner 	umass_t_cbi_data_clear_stall_callback(xfer, UMASS_T_CBI_COMMAND,
175712bd3c8bSSascha Wildner 	    UMASS_T_CBI_STATUS, error);
175812bd3c8bSSascha Wildner }
175912bd3c8bSSascha Wildner 
176012bd3c8bSSascha Wildner static void
umass_t_cbi_data_clear_stall_callback(struct usb_xfer * xfer,uint8_t next_xfer,uint8_t stall_xfer,usb_error_t error)176112bd3c8bSSascha Wildner umass_t_cbi_data_clear_stall_callback(struct usb_xfer *xfer,
176212bd3c8bSSascha Wildner     uint8_t next_xfer, uint8_t stall_xfer, usb_error_t error)
176312bd3c8bSSascha Wildner {
176412bd3c8bSSascha Wildner 	struct umass_softc *sc = usbd_xfer_softc(xfer);
176512bd3c8bSSascha Wildner 
176612bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
176712bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
176812bd3c8bSSascha Wildner tr_transferred:
176912bd3c8bSSascha Wildner 		if (next_xfer == UMASS_T_CBI_STATUS) {
177012bd3c8bSSascha Wildner 			umass_cbi_start_status(sc);
177112bd3c8bSSascha Wildner 		} else {
177212bd3c8bSSascha Wildner 			umass_transfer_start(sc, next_xfer);
177312bd3c8bSSascha Wildner 		}
177412bd3c8bSSascha Wildner 		break;
177512bd3c8bSSascha Wildner 
177612bd3c8bSSascha Wildner 	case USB_ST_SETUP:
177712bd3c8bSSascha Wildner 		if (usbd_clear_stall_callback(xfer, sc->sc_xfer[stall_xfer])) {
177812bd3c8bSSascha Wildner 			goto tr_transferred;	/* should not happen */
177912bd3c8bSSascha Wildner 		}
178012bd3c8bSSascha Wildner 		break;
178112bd3c8bSSascha Wildner 
178212bd3c8bSSascha Wildner 	default:			/* Error */
178312bd3c8bSSascha Wildner 		umass_tr_error(xfer, error);
178412bd3c8bSSascha Wildner 		break;
178512bd3c8bSSascha Wildner 	}
178612bd3c8bSSascha Wildner }
178712bd3c8bSSascha Wildner 
178812bd3c8bSSascha Wildner static void
umass_t_cbi_command_callback(struct usb_xfer * xfer,usb_error_t error)178912bd3c8bSSascha Wildner umass_t_cbi_command_callback(struct usb_xfer *xfer, usb_error_t error)
179012bd3c8bSSascha Wildner {
179112bd3c8bSSascha Wildner 	struct umass_softc *sc = usbd_xfer_softc(xfer);
179212bd3c8bSSascha Wildner 	union ccb *ccb = sc->sc_transfer.ccb;
179312bd3c8bSSascha Wildner 	struct usb_device_request req;
179412bd3c8bSSascha Wildner 	struct usb_page_cache *pc;
179512bd3c8bSSascha Wildner 
179612bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
179712bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
179812bd3c8bSSascha Wildner 
179912bd3c8bSSascha Wildner 		if (sc->sc_transfer.dir == DIR_NONE) {
180012bd3c8bSSascha Wildner 			umass_cbi_start_status(sc);
180112bd3c8bSSascha Wildner 		} else {
180212bd3c8bSSascha Wildner 			umass_transfer_start
180312bd3c8bSSascha Wildner 			    (sc, (sc->sc_transfer.dir == DIR_IN) ?
180412bd3c8bSSascha Wildner 			    UMASS_T_CBI_DATA_READ : UMASS_T_CBI_DATA_WRITE);
180512bd3c8bSSascha Wildner 		}
180612bd3c8bSSascha Wildner 		break;
180712bd3c8bSSascha Wildner 
180812bd3c8bSSascha Wildner 	case USB_ST_SETUP:
180912bd3c8bSSascha Wildner 
181012bd3c8bSSascha Wildner 		if (ccb) {
181112bd3c8bSSascha Wildner 
181212bd3c8bSSascha Wildner 			/*
181312bd3c8bSSascha Wildner 		         * do a CBI transfer with cmd_len bytes from
181412bd3c8bSSascha Wildner 		         * cmd_data, possibly a data phase of data_len
181512bd3c8bSSascha Wildner 		         * bytes from/to the device and finally a status
181612bd3c8bSSascha Wildner 		         * read phase.
181712bd3c8bSSascha Wildner 		         */
181812bd3c8bSSascha Wildner 
181912bd3c8bSSascha Wildner 			req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
182012bd3c8bSSascha Wildner 			req.bRequest = UR_CBI_ADSC;
182112bd3c8bSSascha Wildner 			USETW(req.wValue, 0);
182212bd3c8bSSascha Wildner 			req.wIndex[0] = sc->sc_iface_no;
182312bd3c8bSSascha Wildner 			req.wIndex[1] = 0;
182412bd3c8bSSascha Wildner 			req.wLength[0] = sc->sc_transfer.cmd_len;
182512bd3c8bSSascha Wildner 			req.wLength[1] = 0;
182612bd3c8bSSascha Wildner 
182712bd3c8bSSascha Wildner 			pc = usbd_xfer_get_frame(xfer, 0);
182812bd3c8bSSascha Wildner 			usbd_copy_in(pc, 0, &req, sizeof(req));
182912bd3c8bSSascha Wildner 			pc = usbd_xfer_get_frame(xfer, 1);
183012bd3c8bSSascha Wildner 			usbd_copy_in(pc, 0, sc->sc_transfer.cmd_data,
183112bd3c8bSSascha Wildner 			    sc->sc_transfer.cmd_len);
183212bd3c8bSSascha Wildner 
183312bd3c8bSSascha Wildner 			usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
183412bd3c8bSSascha Wildner 			usbd_xfer_set_frame_len(xfer, 1, sc->sc_transfer.cmd_len);
183512bd3c8bSSascha Wildner 			usbd_xfer_set_frames(xfer,
183612bd3c8bSSascha Wildner 			    sc->sc_transfer.cmd_len ? 2 : 1);
183712bd3c8bSSascha Wildner 
183812bd3c8bSSascha Wildner 			DIF(UDMASS_CBI,
183912bd3c8bSSascha Wildner 			    umass_cbi_dump_cmd(sc,
184012bd3c8bSSascha Wildner 			    sc->sc_transfer.cmd_data,
184112bd3c8bSSascha Wildner 			    sc->sc_transfer.cmd_len));
184212bd3c8bSSascha Wildner 
184312bd3c8bSSascha Wildner 			usbd_transfer_submit(xfer);
184412bd3c8bSSascha Wildner 		}
184512bd3c8bSSascha Wildner 		break;
184612bd3c8bSSascha Wildner 
184712bd3c8bSSascha Wildner 	default:			/* Error */
184812bd3c8bSSascha Wildner 		/*
184912bd3c8bSSascha Wildner 		 * STALL on the control pipe can be result of the command error.
185012bd3c8bSSascha Wildner 		 * Attempt to clear this STALL same as for bulk pipe also
185112bd3c8bSSascha Wildner 		 * results in command completion interrupt, but ASC/ASCQ there
185212bd3c8bSSascha Wildner 		 * look like not always valid, so don't bother about it.
185312bd3c8bSSascha Wildner 		 */
185412bd3c8bSSascha Wildner 		if ((error == USB_ERR_STALLED) ||
185512bd3c8bSSascha Wildner 		    (sc->sc_transfer.callback == &umass_cam_cb)) {
185612bd3c8bSSascha Wildner 			sc->sc_transfer.ccb = NULL;
185712bd3c8bSSascha Wildner 			(sc->sc_transfer.callback)
185812bd3c8bSSascha Wildner 			    (sc, ccb, sc->sc_transfer.data_len,
185912bd3c8bSSascha Wildner 			    STATUS_CMD_UNKNOWN);
186012bd3c8bSSascha Wildner 		} else {
186112bd3c8bSSascha Wildner 			umass_tr_error(xfer, error);
186212bd3c8bSSascha Wildner 			/* skip reset */
186312bd3c8bSSascha Wildner 			sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND;
186412bd3c8bSSascha Wildner 		}
186512bd3c8bSSascha Wildner 		break;
186612bd3c8bSSascha Wildner 	}
186712bd3c8bSSascha Wildner }
186812bd3c8bSSascha Wildner 
186912bd3c8bSSascha Wildner static void
umass_t_cbi_data_read_callback(struct usb_xfer * xfer,usb_error_t error)187012bd3c8bSSascha Wildner umass_t_cbi_data_read_callback(struct usb_xfer *xfer, usb_error_t error)
187112bd3c8bSSascha Wildner {
187212bd3c8bSSascha Wildner 	struct umass_softc *sc = usbd_xfer_softc(xfer);
187312bd3c8bSSascha Wildner 	uint32_t max_bulk = usbd_xfer_max_len(xfer);
187412bd3c8bSSascha Wildner 	int actlen, sumlen;
187512bd3c8bSSascha Wildner 
187612bd3c8bSSascha Wildner 	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
187712bd3c8bSSascha Wildner 
187812bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
187912bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
188012bd3c8bSSascha Wildner 		sc->sc_transfer.data_rem -= actlen;
188112bd3c8bSSascha Wildner 		sc->sc_transfer.data_ptr += actlen;
188212bd3c8bSSascha Wildner 		sc->sc_transfer.actlen += actlen;
188312bd3c8bSSascha Wildner 
188412bd3c8bSSascha Wildner 		if (actlen < sumlen) {
188512bd3c8bSSascha Wildner 			/* short transfer */
188612bd3c8bSSascha Wildner 			sc->sc_transfer.data_rem = 0;
188712bd3c8bSSascha Wildner 		}
188812bd3c8bSSascha Wildner 	case USB_ST_SETUP:
188912bd3c8bSSascha Wildner 		DPRINTF(sc, UDMASS_CBI, "max_bulk=%d, data_rem=%d\n",
189012bd3c8bSSascha Wildner 		    max_bulk, sc->sc_transfer.data_rem);
189112bd3c8bSSascha Wildner 
189212bd3c8bSSascha Wildner 		if (sc->sc_transfer.data_rem == 0) {
189312bd3c8bSSascha Wildner 			umass_cbi_start_status(sc);
189412bd3c8bSSascha Wildner 			break;
189512bd3c8bSSascha Wildner 		}
189612bd3c8bSSascha Wildner 		if (max_bulk > sc->sc_transfer.data_rem) {
189712bd3c8bSSascha Wildner 			max_bulk = sc->sc_transfer.data_rem;
189812bd3c8bSSascha Wildner 		}
189912bd3c8bSSascha Wildner 		usbd_xfer_set_timeout(xfer, sc->sc_transfer.data_timeout);
190012bd3c8bSSascha Wildner 
190112bd3c8bSSascha Wildner 		usbd_xfer_set_frame_data(xfer, 0, sc->sc_transfer.data_ptr,
190212bd3c8bSSascha Wildner 		    max_bulk);
190357bed822SMarkus Pfeiffer 
190412bd3c8bSSascha Wildner 		usbd_transfer_submit(xfer);
190512bd3c8bSSascha Wildner 		break;
190612bd3c8bSSascha Wildner 
190712bd3c8bSSascha Wildner 	default:			/* Error */
190812bd3c8bSSascha Wildner 		if ((error == USB_ERR_CANCELLED) ||
190912bd3c8bSSascha Wildner 		    (sc->sc_transfer.callback != &umass_cam_cb)) {
191012bd3c8bSSascha Wildner 			umass_tr_error(xfer, error);
191112bd3c8bSSascha Wildner 		} else {
191212bd3c8bSSascha Wildner 			umass_transfer_start(sc, UMASS_T_CBI_DATA_RD_CS);
191312bd3c8bSSascha Wildner 		}
191412bd3c8bSSascha Wildner 		break;
191512bd3c8bSSascha Wildner 	}
191612bd3c8bSSascha Wildner }
191712bd3c8bSSascha Wildner 
191812bd3c8bSSascha Wildner static void
umass_t_cbi_data_rd_cs_callback(struct usb_xfer * xfer,usb_error_t error)191912bd3c8bSSascha Wildner umass_t_cbi_data_rd_cs_callback(struct usb_xfer *xfer, usb_error_t error)
192012bd3c8bSSascha Wildner {
192112bd3c8bSSascha Wildner 	umass_t_cbi_data_clear_stall_callback(xfer, UMASS_T_CBI_STATUS,
192212bd3c8bSSascha Wildner 	    UMASS_T_CBI_DATA_READ, error);
192312bd3c8bSSascha Wildner }
192412bd3c8bSSascha Wildner 
192512bd3c8bSSascha Wildner static void
umass_t_cbi_data_write_callback(struct usb_xfer * xfer,usb_error_t error)192612bd3c8bSSascha Wildner umass_t_cbi_data_write_callback(struct usb_xfer *xfer, usb_error_t error)
192712bd3c8bSSascha Wildner {
192812bd3c8bSSascha Wildner 	struct umass_softc *sc = usbd_xfer_softc(xfer);
192912bd3c8bSSascha Wildner 	uint32_t max_bulk = usbd_xfer_max_len(xfer);
193012bd3c8bSSascha Wildner 	int actlen, sumlen;
193112bd3c8bSSascha Wildner 
193212bd3c8bSSascha Wildner 	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
193312bd3c8bSSascha Wildner 
193412bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
193512bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
193612bd3c8bSSascha Wildner 		sc->sc_transfer.data_rem -= actlen;
193712bd3c8bSSascha Wildner 		sc->sc_transfer.data_ptr += actlen;
193812bd3c8bSSascha Wildner 		sc->sc_transfer.actlen += actlen;
193912bd3c8bSSascha Wildner 
194012bd3c8bSSascha Wildner 		if (actlen < sumlen) {
194112bd3c8bSSascha Wildner 			/* short transfer */
194212bd3c8bSSascha Wildner 			sc->sc_transfer.data_rem = 0;
194312bd3c8bSSascha Wildner 		}
194412bd3c8bSSascha Wildner 	case USB_ST_SETUP:
194512bd3c8bSSascha Wildner 		DPRINTF(sc, UDMASS_CBI, "max_bulk=%d, data_rem=%d\n",
194612bd3c8bSSascha Wildner 		    max_bulk, sc->sc_transfer.data_rem);
194712bd3c8bSSascha Wildner 
194812bd3c8bSSascha Wildner 		if (sc->sc_transfer.data_rem == 0) {
194912bd3c8bSSascha Wildner 			umass_cbi_start_status(sc);
195012bd3c8bSSascha Wildner 			break;
195112bd3c8bSSascha Wildner 		}
195212bd3c8bSSascha Wildner 		if (max_bulk > sc->sc_transfer.data_rem) {
195312bd3c8bSSascha Wildner 			max_bulk = sc->sc_transfer.data_rem;
195412bd3c8bSSascha Wildner 		}
195512bd3c8bSSascha Wildner 		usbd_xfer_set_timeout(xfer, sc->sc_transfer.data_timeout);
195612bd3c8bSSascha Wildner 
195712bd3c8bSSascha Wildner 		usbd_xfer_set_frame_data(xfer, 0, sc->sc_transfer.data_ptr,
195812bd3c8bSSascha Wildner 		    max_bulk);
195912bd3c8bSSascha Wildner 
196012bd3c8bSSascha Wildner 		usbd_transfer_submit(xfer);
196112bd3c8bSSascha Wildner 		break;
196212bd3c8bSSascha Wildner 
196312bd3c8bSSascha Wildner 	default:			/* Error */
196412bd3c8bSSascha Wildner 		if ((error == USB_ERR_CANCELLED) ||
196512bd3c8bSSascha Wildner 		    (sc->sc_transfer.callback != &umass_cam_cb)) {
196612bd3c8bSSascha Wildner 			umass_tr_error(xfer, error);
196712bd3c8bSSascha Wildner 		} else {
196812bd3c8bSSascha Wildner 			umass_transfer_start(sc, UMASS_T_CBI_DATA_WR_CS);
196912bd3c8bSSascha Wildner 		}
197012bd3c8bSSascha Wildner 		break;
197112bd3c8bSSascha Wildner 	}
197212bd3c8bSSascha Wildner }
197312bd3c8bSSascha Wildner 
197412bd3c8bSSascha Wildner static void
umass_t_cbi_data_wr_cs_callback(struct usb_xfer * xfer,usb_error_t error)197512bd3c8bSSascha Wildner umass_t_cbi_data_wr_cs_callback(struct usb_xfer *xfer, usb_error_t error)
197612bd3c8bSSascha Wildner {
197712bd3c8bSSascha Wildner 	umass_t_cbi_data_clear_stall_callback(xfer, UMASS_T_CBI_STATUS,
197812bd3c8bSSascha Wildner 	    UMASS_T_CBI_DATA_WRITE, error);
197912bd3c8bSSascha Wildner }
198012bd3c8bSSascha Wildner 
198112bd3c8bSSascha Wildner static void
umass_t_cbi_status_callback(struct usb_xfer * xfer,usb_error_t error)198212bd3c8bSSascha Wildner umass_t_cbi_status_callback(struct usb_xfer *xfer, usb_error_t error)
198312bd3c8bSSascha Wildner {
198412bd3c8bSSascha Wildner 	struct umass_softc *sc = usbd_xfer_softc(xfer);
198512bd3c8bSSascha Wildner 	union ccb *ccb = sc->sc_transfer.ccb;
198612bd3c8bSSascha Wildner 	struct usb_page_cache *pc;
198712bd3c8bSSascha Wildner 	uint32_t residue;
198812bd3c8bSSascha Wildner 	uint8_t status;
198912bd3c8bSSascha Wildner 	int actlen;
199012bd3c8bSSascha Wildner 
199112bd3c8bSSascha Wildner 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
199212bd3c8bSSascha Wildner 
199312bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
199412bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
199512bd3c8bSSascha Wildner 
199657bed822SMarkus Pfeiffer 		if (actlen < (int)sizeof(sc->sbl)) {
199712bd3c8bSSascha Wildner 			goto tr_setup;
199812bd3c8bSSascha Wildner 		}
199912bd3c8bSSascha Wildner 		pc = usbd_xfer_get_frame(xfer, 0);
200012bd3c8bSSascha Wildner 		usbd_copy_out(pc, 0, &sc->sbl, sizeof(sc->sbl));
200112bd3c8bSSascha Wildner 
200212bd3c8bSSascha Wildner 		residue = (sc->sc_transfer.data_len -
200312bd3c8bSSascha Wildner 		    sc->sc_transfer.actlen);
200412bd3c8bSSascha Wildner 
200512bd3c8bSSascha Wildner 		/* dissect the information in the buffer */
200612bd3c8bSSascha Wildner 
200712bd3c8bSSascha Wildner 		if (sc->sc_proto & UMASS_PROTO_UFI) {
200812bd3c8bSSascha Wildner 
200912bd3c8bSSascha Wildner 			/*
201012bd3c8bSSascha Wildner 			 * Section 3.4.3.1.3 specifies that the UFI command
201112bd3c8bSSascha Wildner 			 * protocol returns an ASC and ASCQ in the interrupt
201212bd3c8bSSascha Wildner 			 * data block.
201312bd3c8bSSascha Wildner 			 */
201412bd3c8bSSascha Wildner 
201512bd3c8bSSascha Wildner 			DPRINTF(sc, UDMASS_CBI, "UFI CCI, ASC = 0x%02x, "
201612bd3c8bSSascha Wildner 			    "ASCQ = 0x%02x\n", sc->sbl.ufi.asc,
201712bd3c8bSSascha Wildner 			    sc->sbl.ufi.ascq);
201812bd3c8bSSascha Wildner 
201912bd3c8bSSascha Wildner 			status = (((sc->sbl.ufi.asc == 0) &&
202012bd3c8bSSascha Wildner 			    (sc->sbl.ufi.ascq == 0)) ?
202112bd3c8bSSascha Wildner 			    STATUS_CMD_OK : STATUS_CMD_FAILED);
202212bd3c8bSSascha Wildner 
202312bd3c8bSSascha Wildner 			sc->sc_transfer.ccb = NULL;
202412bd3c8bSSascha Wildner 
202512bd3c8bSSascha Wildner 			sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND;
202612bd3c8bSSascha Wildner 
202712bd3c8bSSascha Wildner 			(sc->sc_transfer.callback)
202812bd3c8bSSascha Wildner 			    (sc, ccb, residue, status);
202912bd3c8bSSascha Wildner 
203012bd3c8bSSascha Wildner 			break;
203112bd3c8bSSascha Wildner 
203212bd3c8bSSascha Wildner 		} else {
203312bd3c8bSSascha Wildner 
203412bd3c8bSSascha Wildner 			/* Command Interrupt Data Block */
203512bd3c8bSSascha Wildner 
203612bd3c8bSSascha Wildner 			DPRINTF(sc, UDMASS_CBI, "type=0x%02x, value=0x%02x\n",
203712bd3c8bSSascha Wildner 			    sc->sbl.common.type, sc->sbl.common.value);
203812bd3c8bSSascha Wildner 
203912bd3c8bSSascha Wildner 			if (sc->sbl.common.type == IDB_TYPE_CCI) {
204012bd3c8bSSascha Wildner 
204112bd3c8bSSascha Wildner 				status = (sc->sbl.common.value & IDB_VALUE_STATUS_MASK);
204212bd3c8bSSascha Wildner 
204312bd3c8bSSascha Wildner 				status = ((status == IDB_VALUE_PASS) ? STATUS_CMD_OK :
204412bd3c8bSSascha Wildner 				    (status == IDB_VALUE_FAIL) ? STATUS_CMD_FAILED :
204512bd3c8bSSascha Wildner 				    (status == IDB_VALUE_PERSISTENT) ? STATUS_CMD_FAILED :
204612bd3c8bSSascha Wildner 				    STATUS_WIRE_FAILED);
204712bd3c8bSSascha Wildner 
204812bd3c8bSSascha Wildner 				sc->sc_transfer.ccb = NULL;
204912bd3c8bSSascha Wildner 
205012bd3c8bSSascha Wildner 				sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND;
205112bd3c8bSSascha Wildner 
205212bd3c8bSSascha Wildner 				(sc->sc_transfer.callback)
205312bd3c8bSSascha Wildner 				    (sc, ccb, residue, status);
205412bd3c8bSSascha Wildner 
205512bd3c8bSSascha Wildner 				break;
205612bd3c8bSSascha Wildner 			}
205712bd3c8bSSascha Wildner 		}
205812bd3c8bSSascha Wildner 
205912bd3c8bSSascha Wildner 		/* fallthrough */
206012bd3c8bSSascha Wildner 
206112bd3c8bSSascha Wildner 	case USB_ST_SETUP:
206212bd3c8bSSascha Wildner tr_setup:
206312bd3c8bSSascha Wildner 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
206412bd3c8bSSascha Wildner 		usbd_transfer_submit(xfer);
206512bd3c8bSSascha Wildner 		break;
206612bd3c8bSSascha Wildner 
206712bd3c8bSSascha Wildner 	default:			/* Error */
206812bd3c8bSSascha Wildner 		DPRINTF(sc, UDMASS_CBI, "Failed to read CSW: %s\n",
206912bd3c8bSSascha Wildner 		    usbd_errstr(error));
207012bd3c8bSSascha Wildner 		umass_tr_error(xfer, error);
207112bd3c8bSSascha Wildner 		break;
207212bd3c8bSSascha Wildner 	}
207312bd3c8bSSascha Wildner }
207412bd3c8bSSascha Wildner 
207512bd3c8bSSascha Wildner /*
207612bd3c8bSSascha Wildner  * CAM specific functions (used by SCSI, UFI, 8070i (ATAPI))
207712bd3c8bSSascha Wildner  */
207812bd3c8bSSascha Wildner 
207912bd3c8bSSascha Wildner static int
umass_cam_attach_sim(struct umass_softc * sc)208012bd3c8bSSascha Wildner umass_cam_attach_sim(struct umass_softc *sc)
208112bd3c8bSSascha Wildner {
208212bd3c8bSSascha Wildner 	struct cam_devq *devq;		/* Per device Queue */
208312bd3c8bSSascha Wildner 
208412bd3c8bSSascha Wildner 	/*
208512bd3c8bSSascha Wildner 	 * A HBA is attached to the CAM layer.
208612bd3c8bSSascha Wildner 	 *
208712bd3c8bSSascha Wildner 	 * The CAM layer will then after a while start probing for devices on
208812bd3c8bSSascha Wildner 	 * the bus. The number of SIMs is limited to one.
208912bd3c8bSSascha Wildner 	 */
209012bd3c8bSSascha Wildner 
209112bd3c8bSSascha Wildner 	devq = cam_simq_alloc(1 /* maximum openings */ );
209212bd3c8bSSascha Wildner 	if (devq == NULL) {
209312bd3c8bSSascha Wildner 		return (ENOMEM);
209412bd3c8bSSascha Wildner 	}
209512bd3c8bSSascha Wildner 	sc->sc_sim = cam_sim_alloc
2096722d05c3SSascha Wildner 	    (umass_cam_action, umass_cam_poll,
209712bd3c8bSSascha Wildner 	    DEVNAME_SIM,
209812bd3c8bSSascha Wildner 	    sc /* priv */ ,
209912bd3c8bSSascha Wildner 	    sc->sc_unit /* unit number */ ,
2100722d05c3SSascha Wildner 	    &sc->sc_lock /* mutex */ ,
210112bd3c8bSSascha Wildner 	    1 /* maximum device openings */ ,
210212bd3c8bSSascha Wildner 	    0 /* maximum tagged device openings */ ,
210312bd3c8bSSascha Wildner 	    devq);
210412bd3c8bSSascha Wildner 
2105722d05c3SSascha Wildner 	cam_simq_release(devq);
210657bed822SMarkus Pfeiffer 
210712bd3c8bSSascha Wildner 	if (sc->sc_sim == NULL) {
210812bd3c8bSSascha Wildner 		return (ENOMEM);
210912bd3c8bSSascha Wildner 	}
2110f40404a4SMatthew Dillon 	usb_callout_init_mtx(&sc->sc_rescan_timeout, &sc->sc_lock, 0);
211112bd3c8bSSascha Wildner 
2112722d05c3SSascha Wildner 	lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
211312bd3c8bSSascha Wildner 
211412bd3c8bSSascha Wildner 	if (xpt_bus_register(sc->sc_sim, sc->sc_unit) != CAM_SUCCESS) {
2115722d05c3SSascha Wildner 		lockmgr(&sc->sc_lock, LK_RELEASE);
211612bd3c8bSSascha Wildner 		return (ENOMEM);
211712bd3c8bSSascha Wildner 	}
211812bd3c8bSSascha Wildner 
2119722d05c3SSascha Wildner 	lockmgr(&sc->sc_lock, LK_RELEASE);
212012bd3c8bSSascha Wildner 	return (0);
212112bd3c8bSSascha Wildner }
212212bd3c8bSSascha Wildner 
2123d668e4abSMarkus Pfeiffer /*
2124d668e4abSMarkus Pfeiffer  * (mp) We need this for DragonflyBSD to realise that there
2125d668e4abSMarkus Pfeiffer  * is a new device present
2126d668e4abSMarkus Pfeiffer  */
2127d668e4abSMarkus Pfeiffer 
2128d668e4abSMarkus Pfeiffer static void
umass_cam_rescan_callback(struct cam_periph * periph,union ccb * ccb)2129d668e4abSMarkus Pfeiffer umass_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb)
2130d668e4abSMarkus Pfeiffer {
2131d668e4abSMarkus Pfeiffer #ifdef USB_DEBUG
2132d668e4abSMarkus Pfeiffer 	if (ccb->ccb_h.status != CAM_REQ_CMP) {
2133d668e4abSMarkus Pfeiffer 		kprintf("%s:%d Rescan failed, 0x%04x\n",
2134d668e4abSMarkus Pfeiffer 		    periph->periph_name, periph->unit_number,
2135d668e4abSMarkus Pfeiffer 		    ccb->ccb_h.status);
2136d668e4abSMarkus Pfeiffer 	} else {
2137d668e4abSMarkus Pfeiffer 		kprintf("%s%d: Rescan succeeded\n",
2138d668e4abSMarkus Pfeiffer 		    periph->periph_name, periph->unit_number);
2139d668e4abSMarkus Pfeiffer 	}
2140d668e4abSMarkus Pfeiffer #endif
2141d668e4abSMarkus Pfeiffer 
2142d668e4abSMarkus Pfeiffer 	xpt_free_path(ccb->ccb_h.path);
2143cec957e9SMatthew Dillon 	xpt_free_ccb(&ccb->ccb_h);
2144d668e4abSMarkus Pfeiffer }
2145d668e4abSMarkus Pfeiffer 
2146d668e4abSMarkus Pfeiffer /*
2147d668e4abSMarkus Pfeiffer  * Rescan the SCSI bus to detect newly added devices.  We use
2148d668e4abSMarkus Pfeiffer  * an async rescan to avoid reentrancy issues.
2149d668e4abSMarkus Pfeiffer  */
2150d668e4abSMarkus Pfeiffer static void
umass_cam_rescan(void * addr)2151d668e4abSMarkus Pfeiffer umass_cam_rescan(void *addr)
2152d668e4abSMarkus Pfeiffer {
2153d668e4abSMarkus Pfeiffer 	struct umass_softc *sc = (struct umass_softc *) addr;
2154d668e4abSMarkus Pfeiffer 	struct cam_path *path;
2155d668e4abSMarkus Pfeiffer 	union ccb *ccb;
2156d668e4abSMarkus Pfeiffer 
2157cec957e9SMatthew Dillon 	ccb = xpt_alloc_ccb();
2158d668e4abSMarkus Pfeiffer 
2159d668e4abSMarkus Pfeiffer 	DPRINTF(sc, UDMASS_SCSI, "scbus%d: scanning for %s:%d:%d:%d\n",
2160d668e4abSMarkus Pfeiffer 	    cam_sim_path(sc->sc_sim),
2161d668e4abSMarkus Pfeiffer 	    device_get_nameunit(sc->sc_dev), cam_sim_path(sc->sc_sim),
2162d668e4abSMarkus Pfeiffer 	    device_get_unit(sc->sc_dev), CAM_LUN_WILDCARD);
2163d668e4abSMarkus Pfeiffer 
2164d668e4abSMarkus Pfeiffer 	if (xpt_create_path(&path, xpt_periph, cam_sim_path(sc->sc_sim),
21653b964699Szrj 	    CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
2166cec957e9SMatthew Dillon 		xpt_free_ccb(&ccb->ccb_h);
2167d668e4abSMarkus Pfeiffer 		return;
2168d668e4abSMarkus Pfeiffer 	}
2169d668e4abSMarkus Pfeiffer 
2170d668e4abSMarkus Pfeiffer 	xpt_setup_ccb(&ccb->ccb_h, path, 5/*priority (low)*/);
2171d668e4abSMarkus Pfeiffer 	ccb->ccb_h.func_code = XPT_SCAN_BUS;
2172d668e4abSMarkus Pfeiffer 	ccb->ccb_h.cbfcnp = umass_cam_rescan_callback;
2173d668e4abSMarkus Pfeiffer 	ccb->crcn.flags = CAM_FLAG_NONE;
2174d668e4abSMarkus Pfeiffer 	xpt_action_async(ccb);
2175d668e4abSMarkus Pfeiffer 
2176d668e4abSMarkus Pfeiffer 	/* The scan is in progress now. */
2177d668e4abSMarkus Pfeiffer }
2178d668e4abSMarkus Pfeiffer 
217912bd3c8bSSascha Wildner static void
umass_cam_attach(struct umass_softc * sc)218012bd3c8bSSascha Wildner umass_cam_attach(struct umass_softc *sc)
218112bd3c8bSSascha Wildner {
218212bd3c8bSSascha Wildner #ifndef USB_DEBUG
218312bd3c8bSSascha Wildner 	if (bootverbose)
218412bd3c8bSSascha Wildner #endif
2185722d05c3SSascha Wildner 		kprintf("%s:%d:%d:%d: Attached to scbus%d\n",
218612bd3c8bSSascha Wildner 		    sc->sc_name, cam_sim_path(sc->sc_sim),
218712bd3c8bSSascha Wildner 		    sc->sc_unit, CAM_LUN_WILDCARD,
218812bd3c8bSSascha Wildner 		    cam_sim_path(sc->sc_sim));
2189d668e4abSMarkus Pfeiffer 
2190d668e4abSMarkus Pfeiffer 	if(!cold) {
2191d668e4abSMarkus Pfeiffer 		usb_callout_reset(&sc->sc_rescan_timeout, USB_MS_TO_TICKS(200),
2192d668e4abSMarkus Pfeiffer 		   umass_cam_rescan, sc);
2193d668e4abSMarkus Pfeiffer 	}
219412bd3c8bSSascha Wildner }
219512bd3c8bSSascha Wildner 
219612bd3c8bSSascha Wildner /* umass_cam_detach
219712bd3c8bSSascha Wildner  *	detach from the CAM layer
219812bd3c8bSSascha Wildner  */
219912bd3c8bSSascha Wildner 
220012bd3c8bSSascha Wildner static void
umass_cam_detach_sim(struct umass_softc * sc)220112bd3c8bSSascha Wildner umass_cam_detach_sim(struct umass_softc *sc)
220212bd3c8bSSascha Wildner {
220312bd3c8bSSascha Wildner 	if (sc->sc_sim != NULL) {
2204f40404a4SMatthew Dillon 		usb_callout_stop(&sc->sc_rescan_timeout);
220512bd3c8bSSascha Wildner 		if (xpt_bus_deregister(cam_sim_path(sc->sc_sim))) {
220612bd3c8bSSascha Wildner 			/* accessing the softc is not possible after this */
220757bed822SMarkus Pfeiffer 			sc->sc_sim->softc = NULL;
2208722d05c3SSascha Wildner 			cam_sim_free(sc->sc_sim);
220912bd3c8bSSascha Wildner 		} else {
221012bd3c8bSSascha Wildner 			panic("%s: CAM layer is busy\n",
221112bd3c8bSSascha Wildner 			    sc->sc_name);
221212bd3c8bSSascha Wildner 		}
221312bd3c8bSSascha Wildner 		sc->sc_sim = NULL;
221412bd3c8bSSascha Wildner 	}
221512bd3c8bSSascha Wildner }
221612bd3c8bSSascha Wildner 
221712bd3c8bSSascha Wildner /* umass_cam_action
221812bd3c8bSSascha Wildner  * 	CAM requests for action come through here
221912bd3c8bSSascha Wildner  */
222012bd3c8bSSascha Wildner 
222112bd3c8bSSascha Wildner static void
umass_cam_action(struct cam_sim * sim,union ccb * ccb)222212bd3c8bSSascha Wildner umass_cam_action(struct cam_sim *sim, union ccb *ccb)
222312bd3c8bSSascha Wildner {
222412bd3c8bSSascha Wildner 	struct umass_softc *sc = (struct umass_softc *)sim->softc;
222512bd3c8bSSascha Wildner 
222657bed822SMarkus Pfeiffer 	if (sc == NULL) {
222712bd3c8bSSascha Wildner 		ccb->ccb_h.status = CAM_SEL_TIMEOUT;
222812bd3c8bSSascha Wildner 		xpt_done(ccb);
222912bd3c8bSSascha Wildner 		return;
223012bd3c8bSSascha Wildner 	}
223112bd3c8bSSascha Wildner 
223212bd3c8bSSascha Wildner 	/* Perform the requested action */
223312bd3c8bSSascha Wildner 	switch (ccb->ccb_h.func_code) {
223412bd3c8bSSascha Wildner 	case XPT_SCSI_IO:
223512bd3c8bSSascha Wildner 		{
223612bd3c8bSSascha Wildner 			uint8_t *cmd;
223712bd3c8bSSascha Wildner 			uint8_t dir;
223812bd3c8bSSascha Wildner 
223912bd3c8bSSascha Wildner 			if (ccb->csio.ccb_h.flags & CAM_CDB_POINTER) {
224012bd3c8bSSascha Wildner 				cmd = (uint8_t *)(ccb->csio.cdb_io.cdb_ptr);
224112bd3c8bSSascha Wildner 			} else {
224212bd3c8bSSascha Wildner 				cmd = (uint8_t *)(ccb->csio.cdb_io.cdb_bytes);
224312bd3c8bSSascha Wildner 			}
224412bd3c8bSSascha Wildner 
2245a41b1dd4SMarkus Pfeiffer 			DPRINTF(sc, UDMASS_SCSI, "%d:%d:%jx:XPT_SCSI_IO: "
224612bd3c8bSSascha Wildner 			    "cmd: 0x%02x, flags: 0x%02x, "
224712bd3c8bSSascha Wildner 			    "%db cmd/%db data/%db sense\n",
224812bd3c8bSSascha Wildner 			    cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id,
2249a41b1dd4SMarkus Pfeiffer 			    (uintmax_t)ccb->ccb_h.target_lun, cmd[0],
225012bd3c8bSSascha Wildner 			    ccb->ccb_h.flags & CAM_DIR_MASK, ccb->csio.cdb_len,
225112bd3c8bSSascha Wildner 			    ccb->csio.dxfer_len, ccb->csio.sense_len);
225212bd3c8bSSascha Wildner 
225312bd3c8bSSascha Wildner 			if (sc->sc_transfer.ccb) {
2254a41b1dd4SMarkus Pfeiffer 				DPRINTF(sc, UDMASS_SCSI, "%d:%d:%jx:XPT_SCSI_IO: "
225512bd3c8bSSascha Wildner 				    "I/O in progress, deferring\n",
225612bd3c8bSSascha Wildner 				    cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id,
2257a41b1dd4SMarkus Pfeiffer 				    (uintmax_t)ccb->ccb_h.target_lun);
225812bd3c8bSSascha Wildner 				ccb->ccb_h.status = CAM_SCSI_BUSY;
225912bd3c8bSSascha Wildner 				xpt_done(ccb);
226012bd3c8bSSascha Wildner 				goto done;
226112bd3c8bSSascha Wildner 			}
226212bd3c8bSSascha Wildner 			switch (ccb->ccb_h.flags & CAM_DIR_MASK) {
226312bd3c8bSSascha Wildner 			case CAM_DIR_IN:
226412bd3c8bSSascha Wildner 				dir = DIR_IN;
226512bd3c8bSSascha Wildner 				break;
226612bd3c8bSSascha Wildner 			case CAM_DIR_OUT:
226712bd3c8bSSascha Wildner 				dir = DIR_OUT;
226812bd3c8bSSascha Wildner 				DIF(UDMASS_SCSI,
226912bd3c8bSSascha Wildner 				    umass_dump_buffer(sc, ccb->csio.data_ptr,
227012bd3c8bSSascha Wildner 				    ccb->csio.dxfer_len, 48));
227112bd3c8bSSascha Wildner 				break;
227212bd3c8bSSascha Wildner 			default:
227312bd3c8bSSascha Wildner 				dir = DIR_NONE;
227412bd3c8bSSascha Wildner 			}
227512bd3c8bSSascha Wildner 
227612bd3c8bSSascha Wildner 			ccb->ccb_h.status = CAM_REQ_INPROG | CAM_SIM_QUEUED;
227712bd3c8bSSascha Wildner 
227812bd3c8bSSascha Wildner 			/*
227912bd3c8bSSascha Wildner 			 * sc->sc_transform will convert the command to the
228012bd3c8bSSascha Wildner 			 * command format needed by the specific command set
228112bd3c8bSSascha Wildner 			 * and return the converted command in
228212bd3c8bSSascha Wildner 			 * "sc->sc_transfer.cmd_data"
228312bd3c8bSSascha Wildner 			 */
228412bd3c8bSSascha Wildner 			if (umass_std_transform(sc, ccb, cmd, ccb->csio.cdb_len)) {
228512bd3c8bSSascha Wildner 
228612bd3c8bSSascha Wildner 				if (sc->sc_transfer.cmd_data[0] == INQUIRY) {
228712bd3c8bSSascha Wildner 					const char *pserial;
228812bd3c8bSSascha Wildner 
228912bd3c8bSSascha Wildner 					pserial = usb_get_serial(sc->sc_udev);
229012bd3c8bSSascha Wildner 
229112bd3c8bSSascha Wildner 					/*
229212bd3c8bSSascha Wildner 					 * Umass devices don't generally report their serial numbers
229312bd3c8bSSascha Wildner 					 * in the usual SCSI way.  Emulate it here.
229412bd3c8bSSascha Wildner 					 */
229512bd3c8bSSascha Wildner 					if ((sc->sc_transfer.cmd_data[1] & SI_EVPD) &&
229612bd3c8bSSascha Wildner 					    (sc->sc_transfer.cmd_data[2] == SVPD_UNIT_SERIAL_NUMBER) &&
229712bd3c8bSSascha Wildner 					    (pserial[0] != '\0')) {
229812bd3c8bSSascha Wildner 						struct scsi_vpd_unit_serial_number *vpd_serial;
229912bd3c8bSSascha Wildner 
230012bd3c8bSSascha Wildner 						vpd_serial = (struct scsi_vpd_unit_serial_number *)ccb->csio.data_ptr;
230112bd3c8bSSascha Wildner 						vpd_serial->length = strlen(pserial);
230212bd3c8bSSascha Wildner 						if (vpd_serial->length > sizeof(vpd_serial->serial_num))
230312bd3c8bSSascha Wildner 							vpd_serial->length = sizeof(vpd_serial->serial_num);
230412bd3c8bSSascha Wildner 						memcpy(vpd_serial->serial_num, pserial, vpd_serial->length);
230512bd3c8bSSascha Wildner 						ccb->csio.scsi_status = SCSI_STATUS_OK;
230612bd3c8bSSascha Wildner 						ccb->ccb_h.status = CAM_REQ_CMP;
230712bd3c8bSSascha Wildner 						xpt_done(ccb);
230812bd3c8bSSascha Wildner 						goto done;
230912bd3c8bSSascha Wildner 					}
231012bd3c8bSSascha Wildner 
231112bd3c8bSSascha Wildner 					/*
231212bd3c8bSSascha Wildner 					 * Handle EVPD inquiry for broken devices first
231312bd3c8bSSascha Wildner 					 * NO_INQUIRY also implies NO_INQUIRY_EVPD
231412bd3c8bSSascha Wildner 					 */
231512bd3c8bSSascha Wildner 					if ((sc->sc_quirks & (NO_INQUIRY_EVPD | NO_INQUIRY)) &&
231612bd3c8bSSascha Wildner 					    (sc->sc_transfer.cmd_data[1] & SI_EVPD)) {
23173a420460SMarkus Pfeiffer 						struct scsi_sense_data *sense;
231812bd3c8bSSascha Wildner 
23193a420460SMarkus Pfeiffer 						sense = &ccb->csio.sense_data;
23203a420460SMarkus Pfeiffer 						bzero(sense, sizeof(*sense));
23213a420460SMarkus Pfeiffer 						sense->error_code = SSD_CURRENT_ERROR;
23223a420460SMarkus Pfeiffer 						sense->flags = SSD_KEY_ILLEGAL_REQUEST;
23233a420460SMarkus Pfeiffer 						sense->add_sense_code = 0x24;
23243a420460SMarkus Pfeiffer 						sense->extra_len = 10;
23253a420460SMarkus Pfeiffer 
232612bd3c8bSSascha Wildner 						ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
232757bed822SMarkus Pfeiffer 						ccb->ccb_h.status =
232857bed822SMarkus Pfeiffer 						    CAM_SCSI_STATUS_ERROR |
232957bed822SMarkus Pfeiffer 						    CAM_AUTOSNS_VALID |
233057bed822SMarkus Pfeiffer 						    CAM_DEV_QFRZN;
233157bed822SMarkus Pfeiffer 						xpt_freeze_devq(ccb->ccb_h.path, 1);
233212bd3c8bSSascha Wildner 						xpt_done(ccb);
233312bd3c8bSSascha Wildner 						goto done;
233412bd3c8bSSascha Wildner 					}
233512bd3c8bSSascha Wildner 					/*
233612bd3c8bSSascha Wildner 					 * Return fake inquiry data for
233712bd3c8bSSascha Wildner 					 * broken devices
233812bd3c8bSSascha Wildner 					 */
233912bd3c8bSSascha Wildner 					if (sc->sc_quirks & NO_INQUIRY) {
234012bd3c8bSSascha Wildner 						memcpy(ccb->csio.data_ptr, &fake_inq_data,
234112bd3c8bSSascha Wildner 						    sizeof(fake_inq_data));
234212bd3c8bSSascha Wildner 						ccb->csio.scsi_status = SCSI_STATUS_OK;
234312bd3c8bSSascha Wildner 						ccb->ccb_h.status = CAM_REQ_CMP;
234412bd3c8bSSascha Wildner 						xpt_done(ccb);
234512bd3c8bSSascha Wildner 						goto done;
234612bd3c8bSSascha Wildner 					}
234712bd3c8bSSascha Wildner 					if (sc->sc_quirks & FORCE_SHORT_INQUIRY) {
234812bd3c8bSSascha Wildner 						ccb->csio.dxfer_len = SHORT_INQUIRY_LENGTH;
234912bd3c8bSSascha Wildner 					}
235057bed822SMarkus Pfeiffer 				} else if (sc->sc_transfer.cmd_data[0] == PREVENT_ALLOW) {
235157bed822SMarkus Pfeiffer 					if (sc->sc_quirks & NO_PREVENT_ALLOW) {
235257bed822SMarkus Pfeiffer 						ccb->csio.scsi_status = SCSI_STATUS_OK;
235357bed822SMarkus Pfeiffer 						ccb->ccb_h.status = CAM_REQ_CMP;
235457bed822SMarkus Pfeiffer 						xpt_done(ccb);
235557bed822SMarkus Pfeiffer 						goto done;
235657bed822SMarkus Pfeiffer 					}
235712bd3c8bSSascha Wildner 				} else if (sc->sc_transfer.cmd_data[0] == SYNCHRONIZE_CACHE) {
235812bd3c8bSSascha Wildner 					if (sc->sc_quirks & NO_SYNCHRONIZE_CACHE) {
235912bd3c8bSSascha Wildner 						ccb->csio.scsi_status = SCSI_STATUS_OK;
236012bd3c8bSSascha Wildner 						ccb->ccb_h.status = CAM_REQ_CMP;
236112bd3c8bSSascha Wildner 						xpt_done(ccb);
236212bd3c8bSSascha Wildner 						goto done;
236312bd3c8bSSascha Wildner 					}
236412bd3c8bSSascha Wildner 				}
236512bd3c8bSSascha Wildner 				umass_command_start(sc, dir, ccb->csio.data_ptr,
236612bd3c8bSSascha Wildner 				    ccb->csio.dxfer_len,
236712bd3c8bSSascha Wildner 				    ccb->ccb_h.timeout,
236812bd3c8bSSascha Wildner 				    &umass_cam_cb, ccb);
236912bd3c8bSSascha Wildner 			}
237012bd3c8bSSascha Wildner 			break;
237112bd3c8bSSascha Wildner 		}
237212bd3c8bSSascha Wildner 	case XPT_PATH_INQ:
237312bd3c8bSSascha Wildner 		{
237412bd3c8bSSascha Wildner 			struct ccb_pathinq *cpi = &ccb->cpi;
237512bd3c8bSSascha Wildner 
2376a41b1dd4SMarkus Pfeiffer 			DPRINTF(sc, UDMASS_SCSI, "%d:%d:%jx:XPT_PATH_INQ:.\n",
237712bd3c8bSSascha Wildner 			    sc ? cam_sim_path(sc->sc_sim) : -1, ccb->ccb_h.target_id,
2378a41b1dd4SMarkus Pfeiffer 			    (uintmax_t)ccb->ccb_h.target_lun);
237912bd3c8bSSascha Wildner 
238012bd3c8bSSascha Wildner 			/* host specific information */
238112bd3c8bSSascha Wildner 			cpi->version_num = 1;
238212bd3c8bSSascha Wildner 			cpi->hba_inquiry = 0;
238312bd3c8bSSascha Wildner 			cpi->target_sprt = 0;
238412bd3c8bSSascha Wildner 			cpi->hba_misc = PIM_NO_6_BYTE;
238512bd3c8bSSascha Wildner 			cpi->hba_eng_cnt = 0;
238612bd3c8bSSascha Wildner 			cpi->max_target = UMASS_SCSIID_MAX;	/* one target */
238712bd3c8bSSascha Wildner 			cpi->initiator_id = UMASS_SCSIID_HOST;
238812bd3c8bSSascha Wildner 			strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
238912bd3c8bSSascha Wildner 			strlcpy(cpi->hba_vid, "USB SCSI", HBA_IDLEN);
239012bd3c8bSSascha Wildner 			strlcpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
239112bd3c8bSSascha Wildner 			cpi->unit_number = cam_sim_unit(sim);
239212bd3c8bSSascha Wildner 			cpi->bus_id = sc->sc_unit;
239312bd3c8bSSascha Wildner 			cpi->protocol = PROTO_SCSI;
239412bd3c8bSSascha Wildner 			cpi->protocol_version = SCSI_REV_2;
239512bd3c8bSSascha Wildner 			cpi->transport = XPORT_USB;
239612bd3c8bSSascha Wildner 			cpi->transport_version = 0;
239757bed822SMarkus Pfeiffer 
239812bd3c8bSSascha Wildner 			if (sc == NULL) {
239912bd3c8bSSascha Wildner 				cpi->base_transfer_speed = 0;
240012bd3c8bSSascha Wildner 				cpi->max_lun = 0;
240112bd3c8bSSascha Wildner 			} else {
240212bd3c8bSSascha Wildner 				if (sc->sc_quirks & FLOPPY_SPEED) {
240312bd3c8bSSascha Wildner 					cpi->base_transfer_speed =
240412bd3c8bSSascha Wildner 					    UMASS_FLOPPY_TRANSFER_SPEED;
240512bd3c8bSSascha Wildner 				} else {
240612bd3c8bSSascha Wildner 					switch (usbd_get_speed(sc->sc_udev)) {
240712bd3c8bSSascha Wildner 					case USB_SPEED_SUPER:
240812bd3c8bSSascha Wildner 						cpi->base_transfer_speed =
240912bd3c8bSSascha Wildner 						    UMASS_SUPER_TRANSFER_SPEED;
241063da4a34SSascha Wildner #if 0 /* XXX */
241112bd3c8bSSascha Wildner 						cpi->maxio = MAXPHYS;
241263da4a34SSascha Wildner #endif
241312bd3c8bSSascha Wildner 						break;
241412bd3c8bSSascha Wildner 					case USB_SPEED_HIGH:
241512bd3c8bSSascha Wildner 						cpi->base_transfer_speed =
241612bd3c8bSSascha Wildner 						    UMASS_HIGH_TRANSFER_SPEED;
241712bd3c8bSSascha Wildner 						break;
241812bd3c8bSSascha Wildner 					default:
241912bd3c8bSSascha Wildner 						cpi->base_transfer_speed =
242012bd3c8bSSascha Wildner 						    UMASS_FULL_TRANSFER_SPEED;
242112bd3c8bSSascha Wildner 						break;
242212bd3c8bSSascha Wildner 					}
242312bd3c8bSSascha Wildner 				}
242412bd3c8bSSascha Wildner 				cpi->max_lun = sc->sc_maxlun;
242512bd3c8bSSascha Wildner 			}
242612bd3c8bSSascha Wildner 
242712bd3c8bSSascha Wildner 			cpi->ccb_h.status = CAM_REQ_CMP;
242812bd3c8bSSascha Wildner 			xpt_done(ccb);
242912bd3c8bSSascha Wildner 			break;
243012bd3c8bSSascha Wildner 		}
243112bd3c8bSSascha Wildner 	case XPT_RESET_DEV:
243212bd3c8bSSascha Wildner 		{
2433a41b1dd4SMarkus Pfeiffer 			DPRINTF(sc, UDMASS_SCSI, "%d:%d:%jx:XPT_RESET_DEV:.\n",
243412bd3c8bSSascha Wildner 			    cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id,
2435a41b1dd4SMarkus Pfeiffer 			    (uintmax_t)ccb->ccb_h.target_lun);
243612bd3c8bSSascha Wildner 
243712bd3c8bSSascha Wildner 			umass_reset(sc);
243812bd3c8bSSascha Wildner 
243912bd3c8bSSascha Wildner 			ccb->ccb_h.status = CAM_REQ_CMP;
244012bd3c8bSSascha Wildner 			xpt_done(ccb);
244112bd3c8bSSascha Wildner 			break;
244212bd3c8bSSascha Wildner 		}
244312bd3c8bSSascha Wildner 	case XPT_GET_TRAN_SETTINGS:
244412bd3c8bSSascha Wildner 		{
244512bd3c8bSSascha Wildner 			struct ccb_trans_settings *cts = &ccb->cts;
244612bd3c8bSSascha Wildner 
2447a41b1dd4SMarkus Pfeiffer 			DPRINTF(sc, UDMASS_SCSI, "%d:%d:%jx:XPT_GET_TRAN_SETTINGS:.\n",
244812bd3c8bSSascha Wildner 			    cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id,
2449a41b1dd4SMarkus Pfeiffer 			    (uintmax_t)ccb->ccb_h.target_lun);
245012bd3c8bSSascha Wildner 
245112bd3c8bSSascha Wildner 			cts->protocol = PROTO_SCSI;
245212bd3c8bSSascha Wildner 			cts->protocol_version = SCSI_REV_2;
245312bd3c8bSSascha Wildner 			cts->transport = XPORT_USB;
245412bd3c8bSSascha Wildner 			cts->transport_version = 0;
245512bd3c8bSSascha Wildner 			cts->xport_specific.valid = 0;
245657bed822SMarkus Pfeiffer 
245712bd3c8bSSascha Wildner 			ccb->ccb_h.status = CAM_REQ_CMP;
245812bd3c8bSSascha Wildner 			xpt_done(ccb);
245912bd3c8bSSascha Wildner 			break;
246012bd3c8bSSascha Wildner 		}
246112bd3c8bSSascha Wildner 	case XPT_SET_TRAN_SETTINGS:
246212bd3c8bSSascha Wildner 		{
2463a41b1dd4SMarkus Pfeiffer 			DPRINTF(sc, UDMASS_SCSI, "%d:%d:%jx:XPT_SET_TRAN_SETTINGS:.\n",
246412bd3c8bSSascha Wildner 			    cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id,
2465a41b1dd4SMarkus Pfeiffer 			    (uintmax_t)ccb->ccb_h.target_lun);
246612bd3c8bSSascha Wildner 
246712bd3c8bSSascha Wildner 			ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
246812bd3c8bSSascha Wildner 			xpt_done(ccb);
246912bd3c8bSSascha Wildner 			break;
247012bd3c8bSSascha Wildner 		}
247112bd3c8bSSascha Wildner 	case XPT_CALC_GEOMETRY:
247212bd3c8bSSascha Wildner 		{
247312bd3c8bSSascha Wildner 			cam_calc_geometry(&ccb->ccg, /* extended */ 1);
247412bd3c8bSSascha Wildner 			xpt_done(ccb);
247512bd3c8bSSascha Wildner 			break;
247612bd3c8bSSascha Wildner 		}
247712bd3c8bSSascha Wildner 	case XPT_NOOP:
247812bd3c8bSSascha Wildner 		{
2479a41b1dd4SMarkus Pfeiffer 			DPRINTF(sc, UDMASS_SCSI, "%d:%d:%jx:XPT_NOOP:.\n",
248012bd3c8bSSascha Wildner 			    sc ? cam_sim_path(sc->sc_sim) : -1, ccb->ccb_h.target_id,
2481a41b1dd4SMarkus Pfeiffer 			    (uintmax_t)ccb->ccb_h.target_lun);
248212bd3c8bSSascha Wildner 
248312bd3c8bSSascha Wildner 			ccb->ccb_h.status = CAM_REQ_CMP;
248412bd3c8bSSascha Wildner 			xpt_done(ccb);
248512bd3c8bSSascha Wildner 			break;
248612bd3c8bSSascha Wildner 		}
248712bd3c8bSSascha Wildner 	default:
2488a41b1dd4SMarkus Pfeiffer 		DPRINTF(sc, UDMASS_SCSI, "%d:%d:%jx:func_code 0x%04x: "
248912bd3c8bSSascha Wildner 		    "Not implemented\n",
249012bd3c8bSSascha Wildner 		    sc ? cam_sim_path(sc->sc_sim) : -1, ccb->ccb_h.target_id,
2491a41b1dd4SMarkus Pfeiffer 		    (uintmax_t)ccb->ccb_h.target_lun, ccb->ccb_h.func_code);
249212bd3c8bSSascha Wildner 
249312bd3c8bSSascha Wildner 		ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
249412bd3c8bSSascha Wildner 		xpt_done(ccb);
249512bd3c8bSSascha Wildner 		break;
249612bd3c8bSSascha Wildner 	}
249712bd3c8bSSascha Wildner 
249812bd3c8bSSascha Wildner done:
249912bd3c8bSSascha Wildner 	return;
250012bd3c8bSSascha Wildner }
250112bd3c8bSSascha Wildner 
250212bd3c8bSSascha Wildner static void
umass_cam_poll(struct cam_sim * sim)250312bd3c8bSSascha Wildner umass_cam_poll(struct cam_sim *sim)
250412bd3c8bSSascha Wildner {
250512bd3c8bSSascha Wildner 	struct umass_softc *sc = (struct umass_softc *)sim->softc;
250612bd3c8bSSascha Wildner 
250757bed822SMarkus Pfeiffer 	if (sc == NULL)
250812bd3c8bSSascha Wildner 		return;
250912bd3c8bSSascha Wildner 
251012bd3c8bSSascha Wildner 	DPRINTF(sc, UDMASS_SCSI, "CAM poll\n");
251112bd3c8bSSascha Wildner 
251212bd3c8bSSascha Wildner 	usbd_transfer_poll(sc->sc_xfer, UMASS_T_MAX);
251312bd3c8bSSascha Wildner }
251412bd3c8bSSascha Wildner 
251512bd3c8bSSascha Wildner 
251612bd3c8bSSascha Wildner /* umass_cam_cb
251712bd3c8bSSascha Wildner  *	finalise a completed CAM command
251812bd3c8bSSascha Wildner  */
251912bd3c8bSSascha Wildner 
252012bd3c8bSSascha Wildner static void
umass_cam_cb(struct umass_softc * sc,union ccb * ccb,uint32_t residue,uint8_t status)252112bd3c8bSSascha Wildner umass_cam_cb(struct umass_softc *sc, union ccb *ccb, uint32_t residue,
252212bd3c8bSSascha Wildner     uint8_t status)
252312bd3c8bSSascha Wildner {
252412bd3c8bSSascha Wildner 	ccb->csio.resid = residue;
252512bd3c8bSSascha Wildner 
252612bd3c8bSSascha Wildner 	switch (status) {
252712bd3c8bSSascha Wildner 	case STATUS_CMD_OK:
252812bd3c8bSSascha Wildner 		ccb->ccb_h.status = CAM_REQ_CMP;
252912bd3c8bSSascha Wildner 		if ((sc->sc_quirks & READ_CAPACITY_OFFBY1) &&
253012bd3c8bSSascha Wildner 		    (ccb->ccb_h.func_code == XPT_SCSI_IO) &&
253112bd3c8bSSascha Wildner 		    (ccb->csio.cdb_io.cdb_bytes[0] == READ_CAPACITY)) {
253212bd3c8bSSascha Wildner 			struct scsi_read_capacity_data *rcap;
253312bd3c8bSSascha Wildner 			uint32_t maxsector;
253412bd3c8bSSascha Wildner 
253512bd3c8bSSascha Wildner 			rcap = (void *)(ccb->csio.data_ptr);
253612bd3c8bSSascha Wildner 			maxsector = scsi_4btoul(rcap->addr) - 1;
253712bd3c8bSSascha Wildner 			scsi_ulto4b(maxsector, rcap->addr);
253812bd3c8bSSascha Wildner 		}
253912bd3c8bSSascha Wildner 		/*
254012bd3c8bSSascha Wildner 		 * We have to add SVPD_UNIT_SERIAL_NUMBER to the list
254112bd3c8bSSascha Wildner 		 * of pages supported by the device - otherwise, CAM
254212bd3c8bSSascha Wildner 		 * will never ask us for the serial number if the
254312bd3c8bSSascha Wildner 		 * device cannot handle that by itself.
254412bd3c8bSSascha Wildner 		 */
254512bd3c8bSSascha Wildner 		if (ccb->ccb_h.func_code == XPT_SCSI_IO &&
254612bd3c8bSSascha Wildner 		    sc->sc_transfer.cmd_data[0] == INQUIRY &&
254712bd3c8bSSascha Wildner 		    (sc->sc_transfer.cmd_data[1] & SI_EVPD) &&
254812bd3c8bSSascha Wildner 		    sc->sc_transfer.cmd_data[2] == SVPD_SUPPORTED_PAGE_LIST &&
254912bd3c8bSSascha Wildner 		    (usb_get_serial(sc->sc_udev)[0] != '\0')) {
255012bd3c8bSSascha Wildner 			struct ccb_scsiio *csio;
255112bd3c8bSSascha Wildner 			struct scsi_vpd_supported_page_list *page_list;
255212bd3c8bSSascha Wildner 
255312bd3c8bSSascha Wildner 			csio = &ccb->csio;
255412bd3c8bSSascha Wildner 			page_list = (struct scsi_vpd_supported_page_list *)csio->data_ptr;
255512bd3c8bSSascha Wildner 			if (page_list->length + 1 < SVPD_SUPPORTED_PAGES_SIZE) {
255612bd3c8bSSascha Wildner 				page_list->list[page_list->length] = SVPD_UNIT_SERIAL_NUMBER;
255712bd3c8bSSascha Wildner 				page_list->length++;
255812bd3c8bSSascha Wildner 			}
255912bd3c8bSSascha Wildner 		}
256012bd3c8bSSascha Wildner 		xpt_done(ccb);
256112bd3c8bSSascha Wildner 		break;
256212bd3c8bSSascha Wildner 
256312bd3c8bSSascha Wildner 	case STATUS_CMD_UNKNOWN:
256412bd3c8bSSascha Wildner 	case STATUS_CMD_FAILED:
256512bd3c8bSSascha Wildner 
256612bd3c8bSSascha Wildner 		/* fetch sense data */
256712bd3c8bSSascha Wildner 
256812bd3c8bSSascha Wildner 		/* the rest of the command was filled in at attach */
256912bd3c8bSSascha Wildner 		sc->cam_scsi_sense.length = ccb->csio.sense_len;
257012bd3c8bSSascha Wildner 
257112bd3c8bSSascha Wildner 		DPRINTF(sc, UDMASS_SCSI, "Fetching %d bytes of "
257212bd3c8bSSascha Wildner 		    "sense data\n", ccb->csio.sense_len);
257312bd3c8bSSascha Wildner 
257412bd3c8bSSascha Wildner 		if (umass_std_transform(sc, ccb, &sc->cam_scsi_sense.opcode,
257512bd3c8bSSascha Wildner 		    sizeof(sc->cam_scsi_sense))) {
257612bd3c8bSSascha Wildner 
257712bd3c8bSSascha Wildner 			if ((sc->sc_quirks & FORCE_SHORT_INQUIRY) &&
257812bd3c8bSSascha Wildner 			    (sc->sc_transfer.cmd_data[0] == INQUIRY)) {
257912bd3c8bSSascha Wildner 				ccb->csio.sense_len = SHORT_INQUIRY_LENGTH;
258012bd3c8bSSascha Wildner 			}
258112bd3c8bSSascha Wildner 			umass_command_start(sc, DIR_IN, &ccb->csio.sense_data.error_code,
258212bd3c8bSSascha Wildner 			    ccb->csio.sense_len, ccb->ccb_h.timeout,
258312bd3c8bSSascha Wildner 			    &umass_cam_sense_cb, ccb);
258412bd3c8bSSascha Wildner 		}
258512bd3c8bSSascha Wildner 		break;
258612bd3c8bSSascha Wildner 
258712bd3c8bSSascha Wildner 	default:
258812bd3c8bSSascha Wildner 		/*
258912bd3c8bSSascha Wildner 		 * The wire protocol failed and will hopefully have
259012bd3c8bSSascha Wildner 		 * recovered. We return an error to CAM and let CAM
259112bd3c8bSSascha Wildner 		 * retry the command if necessary.
259212bd3c8bSSascha Wildner 		 */
259357bed822SMarkus Pfeiffer 		xpt_freeze_devq(ccb->ccb_h.path, 1);
259457bed822SMarkus Pfeiffer 		ccb->ccb_h.status = CAM_REQ_CMP_ERR | CAM_DEV_QFRZN;
259512bd3c8bSSascha Wildner 		xpt_done(ccb);
259612bd3c8bSSascha Wildner 		break;
259712bd3c8bSSascha Wildner 	}
259812bd3c8bSSascha Wildner }
259912bd3c8bSSascha Wildner 
260012bd3c8bSSascha Wildner /*
260112bd3c8bSSascha Wildner  * Finalise a completed autosense operation
260212bd3c8bSSascha Wildner  */
260312bd3c8bSSascha Wildner static void
umass_cam_sense_cb(struct umass_softc * sc,union ccb * ccb,uint32_t residue,uint8_t status)260412bd3c8bSSascha Wildner umass_cam_sense_cb(struct umass_softc *sc, union ccb *ccb, uint32_t residue,
260512bd3c8bSSascha Wildner     uint8_t status)
260612bd3c8bSSascha Wildner {
260712bd3c8bSSascha Wildner 	uint8_t *cmd;
260812bd3c8bSSascha Wildner 
260912bd3c8bSSascha Wildner 	switch (status) {
261012bd3c8bSSascha Wildner 	case STATUS_CMD_OK:
261112bd3c8bSSascha Wildner 	case STATUS_CMD_UNKNOWN:
26123b964699Szrj 	case STATUS_CMD_FAILED: {
2613722d05c3SSascha Wildner 		int error, key, asc, ascq;
2614ce69e397SMarkus Pfeiffer 		uint8_t sense_len;
261512bd3c8bSSascha Wildner 
261612bd3c8bSSascha Wildner 		ccb->csio.sense_resid = residue;
261712bd3c8bSSascha Wildner 		sense_len = ccb->csio.sense_len - ccb->csio.sense_resid;
26181cb87edaSMarkus Pfeiffer 		scsi_extract_sense(&ccb->csio.sense_data,
26193b964699Szrj 		    &error, &key, &asc, &ascq);
262012bd3c8bSSascha Wildner 		if (ccb->csio.ccb_h.flags & CAM_CDB_POINTER) {
262112bd3c8bSSascha Wildner 			cmd = (uint8_t *)(ccb->csio.cdb_io.cdb_ptr);
262212bd3c8bSSascha Wildner 		} else {
262312bd3c8bSSascha Wildner 			cmd = (uint8_t *)(ccb->csio.cdb_io.cdb_bytes);
262412bd3c8bSSascha Wildner 		}
262512bd3c8bSSascha Wildner 
262612bd3c8bSSascha Wildner 		/*
262712bd3c8bSSascha Wildner 		 * Getting sense data always succeeds (apart from wire
262812bd3c8bSSascha Wildner 		 * failures):
262912bd3c8bSSascha Wildner 		 */
263012bd3c8bSSascha Wildner 		if ((sc->sc_quirks & RS_NO_CLEAR_UA) &&
263112bd3c8bSSascha Wildner 		    (cmd[0] == INQUIRY) &&
263212bd3c8bSSascha Wildner 		    (key == SSD_KEY_UNIT_ATTENTION)) {
263312bd3c8bSSascha Wildner 			/*
263412bd3c8bSSascha Wildner 			 * Ignore unit attention errors in the case where
263512bd3c8bSSascha Wildner 			 * the Unit Attention state is not cleared on
263612bd3c8bSSascha Wildner 			 * REQUEST SENSE. They will appear again at the next
263712bd3c8bSSascha Wildner 			 * command.
263812bd3c8bSSascha Wildner 			 */
263912bd3c8bSSascha Wildner 			ccb->ccb_h.status = CAM_REQ_CMP;
264012bd3c8bSSascha Wildner 		} else if (key == SSD_KEY_NO_SENSE) {
264112bd3c8bSSascha Wildner 			/*
264212bd3c8bSSascha Wildner 			 * No problem after all (in the case of CBI without
264312bd3c8bSSascha Wildner 			 * CCI)
264412bd3c8bSSascha Wildner 			 */
264512bd3c8bSSascha Wildner 			ccb->ccb_h.status = CAM_REQ_CMP;
264612bd3c8bSSascha Wildner 		} else if ((sc->sc_quirks & RS_NO_CLEAR_UA) &&
264712bd3c8bSSascha Wildner 			    (cmd[0] == READ_CAPACITY) &&
264812bd3c8bSSascha Wildner 		    (key == SSD_KEY_UNIT_ATTENTION)) {
264912bd3c8bSSascha Wildner 			/*
265012bd3c8bSSascha Wildner 			 * Some devices do not clear the unit attention error
265112bd3c8bSSascha Wildner 			 * on request sense. We insert a test unit ready
265212bd3c8bSSascha Wildner 			 * command to make sure we clear the unit attention
265312bd3c8bSSascha Wildner 			 * condition, then allow the retry to proceed as
265412bd3c8bSSascha Wildner 			 * usual.
265512bd3c8bSSascha Wildner 			 */
265612bd3c8bSSascha Wildner 
265757bed822SMarkus Pfeiffer 			xpt_freeze_devq(ccb->ccb_h.path, 1);
265812bd3c8bSSascha Wildner 			ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR
265957bed822SMarkus Pfeiffer 			    | CAM_AUTOSNS_VALID | CAM_DEV_QFRZN;
266012bd3c8bSSascha Wildner 			ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
266112bd3c8bSSascha Wildner 
266212bd3c8bSSascha Wildner #if 0
266312bd3c8bSSascha Wildner 			DELAY(300000);
266412bd3c8bSSascha Wildner #endif
266512bd3c8bSSascha Wildner 			DPRINTF(sc, UDMASS_SCSI, "Doing a sneaky"
266612bd3c8bSSascha Wildner 			    "TEST_UNIT_READY\n");
266712bd3c8bSSascha Wildner 
266812bd3c8bSSascha Wildner 			/* the rest of the command was filled in at attach */
266912bd3c8bSSascha Wildner 
267057bed822SMarkus Pfeiffer 			if ((sc->sc_transform)(sc,
267112bd3c8bSSascha Wildner 			    &sc->cam_scsi_test_unit_ready.opcode,
267257bed822SMarkus Pfeiffer 			    sizeof(sc->cam_scsi_test_unit_ready)) == 1) {
267312bd3c8bSSascha Wildner 				umass_command_start(sc, DIR_NONE, NULL, 0,
267412bd3c8bSSascha Wildner 				    ccb->ccb_h.timeout,
267512bd3c8bSSascha Wildner 				    &umass_cam_quirk_cb, ccb);
267612bd3c8bSSascha Wildner 				break;
267757bed822SMarkus Pfeiffer 			}
267812bd3c8bSSascha Wildner 		} else {
267957bed822SMarkus Pfeiffer 			xpt_freeze_devq(ccb->ccb_h.path, 1);
268057bed822SMarkus Pfeiffer 			if (key >= 0) {
268112bd3c8bSSascha Wildner 				ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR
268257bed822SMarkus Pfeiffer 				    | CAM_AUTOSNS_VALID | CAM_DEV_QFRZN;
268312bd3c8bSSascha Wildner 				ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
268457bed822SMarkus Pfeiffer 			} else
268557bed822SMarkus Pfeiffer 				ccb->ccb_h.status = CAM_AUTOSENSE_FAIL
268657bed822SMarkus Pfeiffer 				    | CAM_DEV_QFRZN;
268712bd3c8bSSascha Wildner 		}
268812bd3c8bSSascha Wildner 		xpt_done(ccb);
268912bd3c8bSSascha Wildner 		break;
269012bd3c8bSSascha Wildner 	}
269112bd3c8bSSascha Wildner 	default:
269212bd3c8bSSascha Wildner 		DPRINTF(sc, UDMASS_SCSI, "Autosense failed, "
269312bd3c8bSSascha Wildner 		    "status %d\n", status);
269457bed822SMarkus Pfeiffer 		xpt_freeze_devq(ccb->ccb_h.path, 1);
269557bed822SMarkus Pfeiffer 		ccb->ccb_h.status = CAM_AUTOSENSE_FAIL | CAM_DEV_QFRZN;
269612bd3c8bSSascha Wildner 		xpt_done(ccb);
269712bd3c8bSSascha Wildner 	}
269812bd3c8bSSascha Wildner }
269912bd3c8bSSascha Wildner 
270012bd3c8bSSascha Wildner /*
270112bd3c8bSSascha Wildner  * This completion code just handles the fact that we sent a test-unit-ready
270257bed822SMarkus Pfeiffer  * after having previously failed a READ CAPACITY with CHECK_COND.  The CCB
270357bed822SMarkus Pfeiffer  * status for CAM is already set earlier.
270412bd3c8bSSascha Wildner  */
270512bd3c8bSSascha Wildner static void
umass_cam_quirk_cb(struct umass_softc * sc,union ccb * ccb,uint32_t residue,uint8_t status)270612bd3c8bSSascha Wildner umass_cam_quirk_cb(struct umass_softc *sc, union ccb *ccb, uint32_t residue,
270712bd3c8bSSascha Wildner     uint8_t status)
270812bd3c8bSSascha Wildner {
270912bd3c8bSSascha Wildner 	DPRINTF(sc, UDMASS_SCSI, "Test unit ready "
271012bd3c8bSSascha Wildner 	    "returned status %d\n", status);
271112bd3c8bSSascha Wildner 
271212bd3c8bSSascha Wildner 	xpt_done(ccb);
271312bd3c8bSSascha Wildner }
271412bd3c8bSSascha Wildner 
271512bd3c8bSSascha Wildner /*
271612bd3c8bSSascha Wildner  * SCSI specific functions
271712bd3c8bSSascha Wildner  */
271812bd3c8bSSascha Wildner 
271912bd3c8bSSascha Wildner static uint8_t
umass_scsi_transform(struct umass_softc * sc,uint8_t * cmd_ptr,uint8_t cmd_len)272012bd3c8bSSascha Wildner umass_scsi_transform(struct umass_softc *sc, uint8_t *cmd_ptr,
272112bd3c8bSSascha Wildner     uint8_t cmd_len)
272212bd3c8bSSascha Wildner {
272312bd3c8bSSascha Wildner 	if ((cmd_len == 0) ||
272412bd3c8bSSascha Wildner 	    (cmd_len > sizeof(sc->sc_transfer.cmd_data))) {
272512bd3c8bSSascha Wildner 		DPRINTF(sc, UDMASS_SCSI, "Invalid command "
272612bd3c8bSSascha Wildner 		    "length: %d bytes\n", cmd_len);
272712bd3c8bSSascha Wildner 		return (0);		/* failure */
272812bd3c8bSSascha Wildner 	}
272912bd3c8bSSascha Wildner 	sc->sc_transfer.cmd_len = cmd_len;
273012bd3c8bSSascha Wildner 
273112bd3c8bSSascha Wildner 	switch (cmd_ptr[0]) {
273212bd3c8bSSascha Wildner 	case TEST_UNIT_READY:
273312bd3c8bSSascha Wildner 		if (sc->sc_quirks & NO_TEST_UNIT_READY) {
273412bd3c8bSSascha Wildner 			DPRINTF(sc, UDMASS_SCSI, "Converted TEST_UNIT_READY "
273512bd3c8bSSascha Wildner 			    "to START_UNIT\n");
273612bd3c8bSSascha Wildner 			memset(sc->sc_transfer.cmd_data, 0, cmd_len);
273712bd3c8bSSascha Wildner 			sc->sc_transfer.cmd_data[0] = START_STOP_UNIT;
273812bd3c8bSSascha Wildner 			sc->sc_transfer.cmd_data[4] = SSS_START;
273912bd3c8bSSascha Wildner 			return (1);
274012bd3c8bSSascha Wildner 		}
274112bd3c8bSSascha Wildner 		break;
274212bd3c8bSSascha Wildner 
274312bd3c8bSSascha Wildner 	case INQUIRY:
274412bd3c8bSSascha Wildner 		/*
274512bd3c8bSSascha Wildner 		 * some drives wedge when asked for full inquiry
274612bd3c8bSSascha Wildner 		 * information.
274712bd3c8bSSascha Wildner 		 */
274812bd3c8bSSascha Wildner 		if (sc->sc_quirks & FORCE_SHORT_INQUIRY) {
274912bd3c8bSSascha Wildner 			memcpy(sc->sc_transfer.cmd_data, cmd_ptr, cmd_len);
275012bd3c8bSSascha Wildner 			sc->sc_transfer.cmd_data[4] = SHORT_INQUIRY_LENGTH;
275112bd3c8bSSascha Wildner 			return (1);
275212bd3c8bSSascha Wildner 		}
275312bd3c8bSSascha Wildner 		break;
275412bd3c8bSSascha Wildner 	}
275512bd3c8bSSascha Wildner 
275612bd3c8bSSascha Wildner 	memcpy(sc->sc_transfer.cmd_data, cmd_ptr, cmd_len);
275712bd3c8bSSascha Wildner 	return (1);
275812bd3c8bSSascha Wildner }
275912bd3c8bSSascha Wildner 
276012bd3c8bSSascha Wildner static uint8_t
umass_rbc_transform(struct umass_softc * sc,uint8_t * cmd_ptr,uint8_t cmd_len)276112bd3c8bSSascha Wildner umass_rbc_transform(struct umass_softc *sc, uint8_t *cmd_ptr, uint8_t cmd_len)
276212bd3c8bSSascha Wildner {
276312bd3c8bSSascha Wildner 	if ((cmd_len == 0) ||
276412bd3c8bSSascha Wildner 	    (cmd_len > sizeof(sc->sc_transfer.cmd_data))) {
276512bd3c8bSSascha Wildner 		DPRINTF(sc, UDMASS_SCSI, "Invalid command "
276612bd3c8bSSascha Wildner 		    "length: %d bytes\n", cmd_len);
276712bd3c8bSSascha Wildner 		return (0);		/* failure */
276812bd3c8bSSascha Wildner 	}
276912bd3c8bSSascha Wildner 	switch (cmd_ptr[0]) {
277012bd3c8bSSascha Wildner 		/* these commands are defined in RBC: */
277112bd3c8bSSascha Wildner 	case READ_10:
277212bd3c8bSSascha Wildner 	case READ_CAPACITY:
277312bd3c8bSSascha Wildner 	case START_STOP_UNIT:
277412bd3c8bSSascha Wildner 	case SYNCHRONIZE_CACHE:
277512bd3c8bSSascha Wildner 	case WRITE_10:
277612bd3c8bSSascha Wildner 	case 0x2f:			/* VERIFY_10 is absent from
277712bd3c8bSSascha Wildner 					 * scsi_all.h??? */
277812bd3c8bSSascha Wildner 	case INQUIRY:
277912bd3c8bSSascha Wildner 	case MODE_SELECT_10:
278012bd3c8bSSascha Wildner 	case MODE_SENSE_10:
278112bd3c8bSSascha Wildner 	case TEST_UNIT_READY:
278212bd3c8bSSascha Wildner 	case WRITE_BUFFER:
278312bd3c8bSSascha Wildner 		/*
278412bd3c8bSSascha Wildner 		 * The following commands are not listed in my copy of the
278512bd3c8bSSascha Wildner 		 * RBC specs. CAM however seems to want those, and at least
278612bd3c8bSSascha Wildner 		 * the Sony DSC device appears to support those as well
278712bd3c8bSSascha Wildner 		 */
278812bd3c8bSSascha Wildner 	case REQUEST_SENSE:
278912bd3c8bSSascha Wildner 	case PREVENT_ALLOW:
279012bd3c8bSSascha Wildner 
279112bd3c8bSSascha Wildner 		memcpy(sc->sc_transfer.cmd_data, cmd_ptr, cmd_len);
279212bd3c8bSSascha Wildner 
279312bd3c8bSSascha Wildner 		if ((sc->sc_quirks & RBC_PAD_TO_12) && (cmd_len < 12)) {
279412bd3c8bSSascha Wildner 			memset(sc->sc_transfer.cmd_data + cmd_len,
279512bd3c8bSSascha Wildner 			    0, 12 - cmd_len);
279612bd3c8bSSascha Wildner 			cmd_len = 12;
279712bd3c8bSSascha Wildner 		}
279812bd3c8bSSascha Wildner 		sc->sc_transfer.cmd_len = cmd_len;
279912bd3c8bSSascha Wildner 		return (1);		/* sucess */
280012bd3c8bSSascha Wildner 
280112bd3c8bSSascha Wildner 		/* All other commands are not legal in RBC */
280212bd3c8bSSascha Wildner 	default:
280312bd3c8bSSascha Wildner 		DPRINTF(sc, UDMASS_SCSI, "Unsupported RBC "
280412bd3c8bSSascha Wildner 		    "command 0x%02x\n", cmd_ptr[0]);
280512bd3c8bSSascha Wildner 		return (0);		/* failure */
280612bd3c8bSSascha Wildner 	}
280712bd3c8bSSascha Wildner }
280812bd3c8bSSascha Wildner 
280912bd3c8bSSascha Wildner static uint8_t
umass_ufi_transform(struct umass_softc * sc,uint8_t * cmd_ptr,uint8_t cmd_len)281012bd3c8bSSascha Wildner umass_ufi_transform(struct umass_softc *sc, uint8_t *cmd_ptr,
281112bd3c8bSSascha Wildner     uint8_t cmd_len)
281212bd3c8bSSascha Wildner {
281312bd3c8bSSascha Wildner 	if ((cmd_len == 0) ||
281412bd3c8bSSascha Wildner 	    (cmd_len > sizeof(sc->sc_transfer.cmd_data))) {
281512bd3c8bSSascha Wildner 		DPRINTF(sc, UDMASS_SCSI, "Invalid command "
281612bd3c8bSSascha Wildner 		    "length: %d bytes\n", cmd_len);
281712bd3c8bSSascha Wildner 		return (0);		/* failure */
281812bd3c8bSSascha Wildner 	}
281912bd3c8bSSascha Wildner 	/* An UFI command is always 12 bytes in length */
282012bd3c8bSSascha Wildner 	sc->sc_transfer.cmd_len = UFI_COMMAND_LENGTH;
282112bd3c8bSSascha Wildner 
282212bd3c8bSSascha Wildner 	/* Zero the command data */
282312bd3c8bSSascha Wildner 	memset(sc->sc_transfer.cmd_data, 0, UFI_COMMAND_LENGTH);
282412bd3c8bSSascha Wildner 
282512bd3c8bSSascha Wildner 	switch (cmd_ptr[0]) {
282612bd3c8bSSascha Wildner 		/*
282712bd3c8bSSascha Wildner 		 * Commands of which the format has been verified. They
282812bd3c8bSSascha Wildner 		 * should work. Copy the command into the (zeroed out)
282912bd3c8bSSascha Wildner 		 * destination buffer.
283012bd3c8bSSascha Wildner 		 */
283112bd3c8bSSascha Wildner 	case TEST_UNIT_READY:
283212bd3c8bSSascha Wildner 		if (sc->sc_quirks & NO_TEST_UNIT_READY) {
283312bd3c8bSSascha Wildner 			/*
283412bd3c8bSSascha Wildner 			 * Some devices do not support this command. Start
283512bd3c8bSSascha Wildner 			 * Stop Unit should give the same results
283612bd3c8bSSascha Wildner 			 */
283712bd3c8bSSascha Wildner 			DPRINTF(sc, UDMASS_UFI, "Converted TEST_UNIT_READY "
283812bd3c8bSSascha Wildner 			    "to START_UNIT\n");
283912bd3c8bSSascha Wildner 
284012bd3c8bSSascha Wildner 			sc->sc_transfer.cmd_data[0] = START_STOP_UNIT;
284112bd3c8bSSascha Wildner 			sc->sc_transfer.cmd_data[4] = SSS_START;
284212bd3c8bSSascha Wildner 			return (1);
284312bd3c8bSSascha Wildner 		}
284412bd3c8bSSascha Wildner 		break;
284512bd3c8bSSascha Wildner 
284612bd3c8bSSascha Wildner 	case REZERO_UNIT:
284712bd3c8bSSascha Wildner 	case REQUEST_SENSE:
284812bd3c8bSSascha Wildner 	case FORMAT_UNIT:
284912bd3c8bSSascha Wildner 	case INQUIRY:
285012bd3c8bSSascha Wildner 	case START_STOP_UNIT:
285112bd3c8bSSascha Wildner 	case SEND_DIAGNOSTIC:
285212bd3c8bSSascha Wildner 	case PREVENT_ALLOW:
285312bd3c8bSSascha Wildner 	case READ_CAPACITY:
285412bd3c8bSSascha Wildner 	case READ_10:
285512bd3c8bSSascha Wildner 	case WRITE_10:
285612bd3c8bSSascha Wildner 	case POSITION_TO_ELEMENT:	/* SEEK_10 */
285712bd3c8bSSascha Wildner 	case WRITE_AND_VERIFY:
285812bd3c8bSSascha Wildner 	case VERIFY:
285912bd3c8bSSascha Wildner 	case MODE_SELECT_10:
286012bd3c8bSSascha Wildner 	case MODE_SENSE_10:
286112bd3c8bSSascha Wildner 	case READ_12:
286212bd3c8bSSascha Wildner 	case WRITE_12:
286312bd3c8bSSascha Wildner 	case READ_FORMAT_CAPACITIES:
286412bd3c8bSSascha Wildner 		break;
286512bd3c8bSSascha Wildner 
286612bd3c8bSSascha Wildner 		/*
286712bd3c8bSSascha Wildner 		 * SYNCHRONIZE_CACHE isn't supported by UFI, nor should it be
286812bd3c8bSSascha Wildner 		 * required for UFI devices, so it is appropriate to fake
286912bd3c8bSSascha Wildner 		 * success.
287012bd3c8bSSascha Wildner 		 */
287112bd3c8bSSascha Wildner 	case SYNCHRONIZE_CACHE:
287212bd3c8bSSascha Wildner 		return (2);
287312bd3c8bSSascha Wildner 
287412bd3c8bSSascha Wildner 	default:
287512bd3c8bSSascha Wildner 		DPRINTF(sc, UDMASS_SCSI, "Unsupported UFI "
287612bd3c8bSSascha Wildner 		    "command 0x%02x\n", cmd_ptr[0]);
287712bd3c8bSSascha Wildner 		return (0);		/* failure */
287812bd3c8bSSascha Wildner 	}
287912bd3c8bSSascha Wildner 
288012bd3c8bSSascha Wildner 	memcpy(sc->sc_transfer.cmd_data, cmd_ptr, cmd_len);
288112bd3c8bSSascha Wildner 	return (1);			/* success */
288212bd3c8bSSascha Wildner }
288312bd3c8bSSascha Wildner 
288412bd3c8bSSascha Wildner /*
288512bd3c8bSSascha Wildner  * 8070i (ATAPI) specific functions
288612bd3c8bSSascha Wildner  */
288712bd3c8bSSascha Wildner static uint8_t
umass_atapi_transform(struct umass_softc * sc,uint8_t * cmd_ptr,uint8_t cmd_len)288812bd3c8bSSascha Wildner umass_atapi_transform(struct umass_softc *sc, uint8_t *cmd_ptr,
288912bd3c8bSSascha Wildner     uint8_t cmd_len)
289012bd3c8bSSascha Wildner {
289112bd3c8bSSascha Wildner 	if ((cmd_len == 0) ||
289212bd3c8bSSascha Wildner 	    (cmd_len > sizeof(sc->sc_transfer.cmd_data))) {
289312bd3c8bSSascha Wildner 		DPRINTF(sc, UDMASS_SCSI, "Invalid command "
289412bd3c8bSSascha Wildner 		    "length: %d bytes\n", cmd_len);
289512bd3c8bSSascha Wildner 		return (0);		/* failure */
289612bd3c8bSSascha Wildner 	}
289712bd3c8bSSascha Wildner 	/* An ATAPI command is always 12 bytes in length. */
289812bd3c8bSSascha Wildner 	sc->sc_transfer.cmd_len = ATAPI_COMMAND_LENGTH;
289912bd3c8bSSascha Wildner 
290012bd3c8bSSascha Wildner 	/* Zero the command data */
290112bd3c8bSSascha Wildner 	memset(sc->sc_transfer.cmd_data, 0, ATAPI_COMMAND_LENGTH);
290212bd3c8bSSascha Wildner 
290312bd3c8bSSascha Wildner 	switch (cmd_ptr[0]) {
290412bd3c8bSSascha Wildner 		/*
290512bd3c8bSSascha Wildner 		 * Commands of which the format has been verified. They
290612bd3c8bSSascha Wildner 		 * should work. Copy the command into the destination
290712bd3c8bSSascha Wildner 		 * buffer.
290812bd3c8bSSascha Wildner 		 */
290912bd3c8bSSascha Wildner 	case INQUIRY:
291012bd3c8bSSascha Wildner 		/*
291112bd3c8bSSascha Wildner 		 * some drives wedge when asked for full inquiry
291212bd3c8bSSascha Wildner 		 * information.
291312bd3c8bSSascha Wildner 		 */
291412bd3c8bSSascha Wildner 		if (sc->sc_quirks & FORCE_SHORT_INQUIRY) {
291512bd3c8bSSascha Wildner 			memcpy(sc->sc_transfer.cmd_data, cmd_ptr, cmd_len);
291612bd3c8bSSascha Wildner 
291712bd3c8bSSascha Wildner 			sc->sc_transfer.cmd_data[4] = SHORT_INQUIRY_LENGTH;
291812bd3c8bSSascha Wildner 			return (1);
291912bd3c8bSSascha Wildner 		}
292012bd3c8bSSascha Wildner 		break;
292112bd3c8bSSascha Wildner 
292212bd3c8bSSascha Wildner 	case TEST_UNIT_READY:
292312bd3c8bSSascha Wildner 		if (sc->sc_quirks & NO_TEST_UNIT_READY) {
292412bd3c8bSSascha Wildner 			DPRINTF(sc, UDMASS_SCSI, "Converted TEST_UNIT_READY "
292512bd3c8bSSascha Wildner 			    "to START_UNIT\n");
292612bd3c8bSSascha Wildner 			sc->sc_transfer.cmd_data[0] = START_STOP_UNIT;
292712bd3c8bSSascha Wildner 			sc->sc_transfer.cmd_data[4] = SSS_START;
292812bd3c8bSSascha Wildner 			return (1);
292912bd3c8bSSascha Wildner 		}
293012bd3c8bSSascha Wildner 		break;
293112bd3c8bSSascha Wildner 
293212bd3c8bSSascha Wildner 	case REZERO_UNIT:
293312bd3c8bSSascha Wildner 	case REQUEST_SENSE:
293412bd3c8bSSascha Wildner 	case START_STOP_UNIT:
293512bd3c8bSSascha Wildner 	case SEND_DIAGNOSTIC:
293612bd3c8bSSascha Wildner 	case PREVENT_ALLOW:
293712bd3c8bSSascha Wildner 	case READ_CAPACITY:
293812bd3c8bSSascha Wildner 	case READ_10:
293912bd3c8bSSascha Wildner 	case WRITE_10:
294012bd3c8bSSascha Wildner 	case POSITION_TO_ELEMENT:	/* SEEK_10 */
294112bd3c8bSSascha Wildner 	case SYNCHRONIZE_CACHE:
294212bd3c8bSSascha Wildner 	case MODE_SELECT_10:
294312bd3c8bSSascha Wildner 	case MODE_SENSE_10:
294412bd3c8bSSascha Wildner 	case READ_BUFFER:
294512bd3c8bSSascha Wildner 	case 0x42:			/* READ_SUBCHANNEL */
294612bd3c8bSSascha Wildner 	case 0x43:			/* READ_TOC */
294712bd3c8bSSascha Wildner 	case 0x44:			/* READ_HEADER */
294812bd3c8bSSascha Wildner 	case 0x47:			/* PLAY_MSF (Play Minute/Second/Frame) */
294912bd3c8bSSascha Wildner 	case 0x48:			/* PLAY_TRACK */
295012bd3c8bSSascha Wildner 	case 0x49:			/* PLAY_TRACK_REL */
295112bd3c8bSSascha Wildner 	case 0x4b:			/* PAUSE */
295212bd3c8bSSascha Wildner 	case 0x51:			/* READ_DISK_INFO */
295312bd3c8bSSascha Wildner 	case 0x52:			/* READ_TRACK_INFO */
295412bd3c8bSSascha Wildner 	case 0x54:			/* SEND_OPC */
295512bd3c8bSSascha Wildner 	case 0x59:			/* READ_MASTER_CUE */
295612bd3c8bSSascha Wildner 	case 0x5b:			/* CLOSE_TR_SESSION */
295712bd3c8bSSascha Wildner 	case 0x5c:			/* READ_BUFFER_CAP */
295812bd3c8bSSascha Wildner 	case 0x5d:			/* SEND_CUE_SHEET */
295912bd3c8bSSascha Wildner 	case 0xa1:			/* BLANK */
296012bd3c8bSSascha Wildner 	case 0xa5:			/* PLAY_12 */
296112bd3c8bSSascha Wildner 	case 0xa6:			/* EXCHANGE_MEDIUM */
296212bd3c8bSSascha Wildner 	case 0xad:			/* READ_DVD_STRUCTURE */
296312bd3c8bSSascha Wildner 	case 0xbb:			/* SET_CD_SPEED */
296412bd3c8bSSascha Wildner 	case 0xe5:			/* READ_TRACK_INFO_PHILIPS */
296512bd3c8bSSascha Wildner 		break;
296612bd3c8bSSascha Wildner 
296712bd3c8bSSascha Wildner 	case READ_12:
296812bd3c8bSSascha Wildner 	case WRITE_12:
296912bd3c8bSSascha Wildner 	default:
297012bd3c8bSSascha Wildner 		DPRINTF(sc, UDMASS_SCSI, "Unsupported ATAPI "
297112bd3c8bSSascha Wildner 		    "command 0x%02x - trying anyway\n",
297212bd3c8bSSascha Wildner 		    cmd_ptr[0]);
297312bd3c8bSSascha Wildner 		break;
297412bd3c8bSSascha Wildner 	}
297512bd3c8bSSascha Wildner 
297612bd3c8bSSascha Wildner 	memcpy(sc->sc_transfer.cmd_data, cmd_ptr, cmd_len);
297712bd3c8bSSascha Wildner 	return (1);			/* success */
297812bd3c8bSSascha Wildner }
297912bd3c8bSSascha Wildner 
298012bd3c8bSSascha Wildner static uint8_t
umass_no_transform(struct umass_softc * sc,uint8_t * cmd,uint8_t cmdlen)298112bd3c8bSSascha Wildner umass_no_transform(struct umass_softc *sc, uint8_t *cmd,
298212bd3c8bSSascha Wildner     uint8_t cmdlen)
298312bd3c8bSSascha Wildner {
298412bd3c8bSSascha Wildner 	return (0);			/* failure */
298512bd3c8bSSascha Wildner }
298612bd3c8bSSascha Wildner 
298712bd3c8bSSascha Wildner static uint8_t
umass_std_transform(struct umass_softc * sc,union ccb * ccb,uint8_t * cmd,uint8_t cmdlen)298812bd3c8bSSascha Wildner umass_std_transform(struct umass_softc *sc, union ccb *ccb,
298912bd3c8bSSascha Wildner     uint8_t *cmd, uint8_t cmdlen)
299012bd3c8bSSascha Wildner {
299112bd3c8bSSascha Wildner 	uint8_t retval;
299212bd3c8bSSascha Wildner 
299312bd3c8bSSascha Wildner 	retval = (sc->sc_transform) (sc, cmd, cmdlen);
299412bd3c8bSSascha Wildner 
299512bd3c8bSSascha Wildner 	if (retval == 2) {
299612bd3c8bSSascha Wildner 		ccb->ccb_h.status = CAM_REQ_CMP;
299712bd3c8bSSascha Wildner 		xpt_done(ccb);
299812bd3c8bSSascha Wildner 		return (0);
299912bd3c8bSSascha Wildner 	} else if (retval == 0) {
300057bed822SMarkus Pfeiffer 		xpt_freeze_devq(ccb->ccb_h.path, 1);
300157bed822SMarkus Pfeiffer 		ccb->ccb_h.status = CAM_REQ_INVALID | CAM_DEV_QFRZN;
300212bd3c8bSSascha Wildner 		xpt_done(ccb);
300312bd3c8bSSascha Wildner 		return (0);
300412bd3c8bSSascha Wildner 	}
300512bd3c8bSSascha Wildner 	/* Command should be executed */
300612bd3c8bSSascha Wildner 	return (1);
300712bd3c8bSSascha Wildner }
300812bd3c8bSSascha Wildner 
300912bd3c8bSSascha Wildner #ifdef USB_DEBUG
301012bd3c8bSSascha Wildner static void
umass_bbb_dump_cbw(struct umass_softc * sc,umass_bbb_cbw_t * cbw)301112bd3c8bSSascha Wildner umass_bbb_dump_cbw(struct umass_softc *sc, umass_bbb_cbw_t *cbw)
301212bd3c8bSSascha Wildner {
301312bd3c8bSSascha Wildner 	uint8_t *c = cbw->CBWCDB;
301412bd3c8bSSascha Wildner 
301512bd3c8bSSascha Wildner 	uint32_t dlen = UGETDW(cbw->dCBWDataTransferLength);
301612bd3c8bSSascha Wildner 	uint32_t tag = UGETDW(cbw->dCBWTag);
301712bd3c8bSSascha Wildner 
301812bd3c8bSSascha Wildner 	uint8_t clen = cbw->bCDBLength;
301912bd3c8bSSascha Wildner 	uint8_t flags = cbw->bCBWFlags;
302012bd3c8bSSascha Wildner 	uint8_t lun = cbw->bCBWLUN;
302112bd3c8bSSascha Wildner 
302212bd3c8bSSascha Wildner 	DPRINTF(sc, UDMASS_BBB, "CBW %d: cmd = %db "
302312bd3c8bSSascha Wildner 	    "(0x%02x%02x%02x%02x%02x%02x%s), "
302412bd3c8bSSascha Wildner 	    "data = %db, lun = %d, dir = %s\n",
302512bd3c8bSSascha Wildner 	    tag, clen,
302612bd3c8bSSascha Wildner 	    c[0], c[1], c[2], c[3], c[4], c[5], (clen > 6 ? "..." : ""),
302712bd3c8bSSascha Wildner 	    dlen, lun, (flags == CBWFLAGS_IN ? "in" :
302812bd3c8bSSascha Wildner 	    (flags == CBWFLAGS_OUT ? "out" : "<invalid>")));
302912bd3c8bSSascha Wildner }
303012bd3c8bSSascha Wildner 
303112bd3c8bSSascha Wildner static void
umass_bbb_dump_csw(struct umass_softc * sc,umass_bbb_csw_t * csw)303212bd3c8bSSascha Wildner umass_bbb_dump_csw(struct umass_softc *sc, umass_bbb_csw_t *csw)
303312bd3c8bSSascha Wildner {
303412bd3c8bSSascha Wildner 	uint32_t sig = UGETDW(csw->dCSWSignature);
303512bd3c8bSSascha Wildner 	uint32_t tag = UGETDW(csw->dCSWTag);
303612bd3c8bSSascha Wildner 	uint32_t res = UGETDW(csw->dCSWDataResidue);
303712bd3c8bSSascha Wildner 	uint8_t status = csw->bCSWStatus;
303812bd3c8bSSascha Wildner 
303912bd3c8bSSascha Wildner 	DPRINTF(sc, UDMASS_BBB, "CSW %d: sig = 0x%08x (%s), tag = 0x%08x, "
304012bd3c8bSSascha Wildner 	    "res = %d, status = 0x%02x (%s)\n",
304112bd3c8bSSascha Wildner 	    tag, sig, (sig == CSWSIGNATURE ? "valid" : "invalid"),
304212bd3c8bSSascha Wildner 	    tag, res,
304312bd3c8bSSascha Wildner 	    status, (status == CSWSTATUS_GOOD ? "good" :
304412bd3c8bSSascha Wildner 	    (status == CSWSTATUS_FAILED ? "failed" :
304512bd3c8bSSascha Wildner 	    (status == CSWSTATUS_PHASE ? "phase" : "<invalid>"))));
304612bd3c8bSSascha Wildner }
304712bd3c8bSSascha Wildner 
304812bd3c8bSSascha Wildner static void
umass_cbi_dump_cmd(struct umass_softc * sc,void * cmd,uint8_t cmdlen)304912bd3c8bSSascha Wildner umass_cbi_dump_cmd(struct umass_softc *sc, void *cmd, uint8_t cmdlen)
305012bd3c8bSSascha Wildner {
305112bd3c8bSSascha Wildner 	uint8_t *c = cmd;
305212bd3c8bSSascha Wildner 	uint8_t dir = sc->sc_transfer.dir;
305312bd3c8bSSascha Wildner 
305412bd3c8bSSascha Wildner 	DPRINTF(sc, UDMASS_BBB, "cmd = %db "
305512bd3c8bSSascha Wildner 	    "(0x%02x%02x%02x%02x%02x%02x%s), "
305612bd3c8bSSascha Wildner 	    "data = %db, dir = %s\n",
305712bd3c8bSSascha Wildner 	    cmdlen,
305812bd3c8bSSascha Wildner 	    c[0], c[1], c[2], c[3], c[4], c[5], (cmdlen > 6 ? "..." : ""),
305912bd3c8bSSascha Wildner 	    sc->sc_transfer.data_len,
306012bd3c8bSSascha Wildner 	    (dir == DIR_IN ? "in" :
306112bd3c8bSSascha Wildner 	    (dir == DIR_OUT ? "out" :
306212bd3c8bSSascha Wildner 	    (dir == DIR_NONE ? "no data phase" : "<invalid>"))));
306312bd3c8bSSascha Wildner }
306412bd3c8bSSascha Wildner 
306512bd3c8bSSascha Wildner static void
umass_dump_buffer(struct umass_softc * sc,uint8_t * buffer,uint32_t buflen,uint32_t printlen)306612bd3c8bSSascha Wildner umass_dump_buffer(struct umass_softc *sc, uint8_t *buffer, uint32_t buflen,
306712bd3c8bSSascha Wildner     uint32_t printlen)
306812bd3c8bSSascha Wildner {
306912bd3c8bSSascha Wildner 	uint32_t i, j;
307012bd3c8bSSascha Wildner 	char s1[40];
307112bd3c8bSSascha Wildner 	char s2[40];
307212bd3c8bSSascha Wildner 	char s3[5];
307312bd3c8bSSascha Wildner 
307412bd3c8bSSascha Wildner 	s1[0] = '\0';
307512bd3c8bSSascha Wildner 	s3[0] = '\0';
307612bd3c8bSSascha Wildner 
3077722d05c3SSascha Wildner 	ksprintf(s2, " buffer=%p, buflen=%d", buffer, buflen);
307812bd3c8bSSascha Wildner 	for (i = 0; (i < buflen) && (i < printlen); i++) {
307912bd3c8bSSascha Wildner 		j = i % 16;
308012bd3c8bSSascha Wildner 		if (j == 0 && i != 0) {
308112bd3c8bSSascha Wildner 			DPRINTF(sc, UDMASS_GEN, "0x %s%s\n",
308212bd3c8bSSascha Wildner 			    s1, s2);
308312bd3c8bSSascha Wildner 			s2[0] = '\0';
308412bd3c8bSSascha Wildner 		}
3085722d05c3SSascha Wildner 		ksprintf(&s1[j * 2], "%02x", buffer[i] & 0xff);
308612bd3c8bSSascha Wildner 	}
308712bd3c8bSSascha Wildner 	if (buflen > printlen)
3088722d05c3SSascha Wildner 		ksprintf(s3, " ...");
308912bd3c8bSSascha Wildner 	DPRINTF(sc, UDMASS_GEN, "0x %s%s%s\n",
309012bd3c8bSSascha Wildner 	    s1, s2, s3);
309112bd3c8bSSascha Wildner }
309212bd3c8bSSascha Wildner 
309312bd3c8bSSascha Wildner #endif
3094