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