1184610Salfred/* $FreeBSD$ */ 2184610Salfred/*- 3225400Shselasky * Copyright (c) 2008,2011 Hans Petter Selasky. All rights reserved. 4184610Salfred * 5184610Salfred * Redistribution and use in source and binary forms, with or without 6184610Salfred * modification, are permitted provided that the following conditions 7184610Salfred * are met: 8184610Salfred * 1. Redistributions of source code must retain the above copyright 9184610Salfred * notice, this list of conditions and the following disclaimer. 10184610Salfred * 2. Redistributions in binary form must reproduce the above copyright 11184610Salfred * notice, this list of conditions and the following disclaimer in the 12184610Salfred * documentation and/or other materials provided with the distribution. 13184610Salfred * 14184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17184610Salfred * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24184610Salfred * SUCH DAMAGE. 25184610Salfred */ 26184610Salfred 27184610Salfred/* 28184610Salfred * The following file contains code that will detect USB autoinstall 29184610Salfred * disks. 30184610Salfred * 31184610Salfred * TODO: Potentially we could add code to automatically detect USB 32184610Salfred * mass storage quirks for not supported SCSI commands! 33184610Salfred */ 34184610Salfred 35194677Sthompsa#include <sys/stdint.h> 36194677Sthompsa#include <sys/stddef.h> 37194677Sthompsa#include <sys/param.h> 38194677Sthompsa#include <sys/queue.h> 39194677Sthompsa#include <sys/types.h> 40194677Sthompsa#include <sys/systm.h> 41194677Sthompsa#include <sys/kernel.h> 42194677Sthompsa#include <sys/bus.h> 43194677Sthompsa#include <sys/module.h> 44194677Sthompsa#include <sys/lock.h> 45194677Sthompsa#include <sys/mutex.h> 46194677Sthompsa#include <sys/condvar.h> 47194677Sthompsa#include <sys/sysctl.h> 48194677Sthompsa#include <sys/sx.h> 49194677Sthompsa#include <sys/unistd.h> 50194677Sthompsa#include <sys/callout.h> 51194677Sthompsa#include <sys/malloc.h> 52194677Sthompsa#include <sys/priv.h> 53194677Sthompsa 54188942Sthompsa#include <dev/usb/usb.h> 55194677Sthompsa#include <dev/usb/usbdi.h> 56194677Sthompsa#include <dev/usb/usbdi_util.h> 57184610Salfred 58194228Sthompsa#define USB_DEBUG_VAR usb_debug 59184610Salfred 60188942Sthompsa#include <dev/usb/usb_busdma.h> 61188942Sthompsa#include <dev/usb/usb_process.h> 62188942Sthompsa#include <dev/usb/usb_transfer.h> 63188942Sthompsa#include <dev/usb/usb_msctest.h> 64188942Sthompsa#include <dev/usb/usb_debug.h> 65188942Sthompsa#include <dev/usb/usb_device.h> 66188942Sthompsa#include <dev/usb/usb_request.h> 67188942Sthompsa#include <dev/usb/usb_util.h> 68201681Sthompsa#include <dev/usb/quirk/usb_quirk.h> 69184610Salfred 70184610Salfredenum { 71184610Salfred ST_COMMAND, 72184610Salfred ST_DATA_RD, 73184610Salfred ST_DATA_RD_CS, 74184610Salfred ST_DATA_WR, 75184610Salfred ST_DATA_WR_CS, 76184610Salfred ST_STATUS, 77184610Salfred ST_MAX, 78184610Salfred}; 79184610Salfred 80184610Salfredenum { 81184610Salfred DIR_IN, 82184610Salfred DIR_OUT, 83184610Salfred DIR_NONE, 84184610Salfred}; 85184610Salfred 86260575Shselasky#define SCSI_MAX_LEN MAX(0x100, BULK_SIZE) 87201681Sthompsa#define SCSI_INQ_LEN 0x24 88225400Shselasky#define SCSI_SENSE_LEN 0xFF 89225400Shselasky 90201681Sthompsastatic uint8_t scsi_test_unit_ready[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 91201681Sthompsastatic uint8_t scsi_inquiry[] = { 0x12, 0x00, 0x00, 0x00, SCSI_INQ_LEN, 0x00 }; 92201681Sthompsastatic uint8_t scsi_rezero_init[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }; 93201681Sthompsastatic uint8_t scsi_start_stop_unit[] = { 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00 }; 94201681Sthompsastatic uint8_t scsi_ztestor_eject[] = { 0x85, 0x01, 0x01, 0x01, 0x18, 0x01, 95201681Sthompsa 0x01, 0x01, 0x01, 0x01, 0x00, 0x00 }; 96201681Sthompsastatic uint8_t scsi_cmotech_eject[] = { 0xff, 0x52, 0x44, 0x45, 0x56, 0x43, 97201681Sthompsa 0x48, 0x47 }; 98203905Sthompsastatic uint8_t scsi_huawei_eject[] = { 0x11, 0x06, 0x00, 0x00, 0x00, 0x00, 99203905Sthompsa 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 100203905Sthompsa 0x00, 0x00, 0x00, 0x00 }; 101213480Sglebiusstatic uint8_t scsi_tct_eject[] = { 0x06, 0xf5, 0x04, 0x02, 0x52, 0x70 }; 102225350Shselaskystatic uint8_t scsi_sync_cache[] = { 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 103225350Shselasky 0x00, 0x00, 0x00, 0x00 }; 104225400Shselaskystatic uint8_t scsi_request_sense[] = { 0x03, 0x00, 0x00, 0x00, 0x12, 0x00, 105225400Shselasky 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 106240666Shselaskystatic uint8_t scsi_read_capacity[] = { 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 107240666Shselasky 0x00, 0x00, 0x00, 0x00 }; 108201681Sthompsa 109185290Salfred#define BULK_SIZE 64 /* dummy */ 110201681Sthompsa#define ERR_CSW_FAILED -1 111184610Salfred 112184610Salfred/* Command Block Wrapper */ 113184610Salfredstruct bbb_cbw { 114184610Salfred uDWord dCBWSignature; 115184610Salfred#define CBWSIGNATURE 0x43425355 116184610Salfred uDWord dCBWTag; 117184610Salfred uDWord dCBWDataTransferLength; 118184610Salfred uByte bCBWFlags; 119184610Salfred#define CBWFLAGS_OUT 0x00 120184610Salfred#define CBWFLAGS_IN 0x80 121184610Salfred uByte bCBWLUN; 122184610Salfred uByte bCDBLength; 123184610Salfred#define CBWCDBLENGTH 16 124184610Salfred uByte CBWCDB[CBWCDBLENGTH]; 125184610Salfred} __packed; 126184610Salfred 127184610Salfred/* Command Status Wrapper */ 128184610Salfredstruct bbb_csw { 129184610Salfred uDWord dCSWSignature; 130184610Salfred#define CSWSIGNATURE 0x53425355 131184610Salfred uDWord dCSWTag; 132184610Salfred uDWord dCSWDataResidue; 133184610Salfred uByte bCSWStatus; 134184610Salfred#define CSWSTATUS_GOOD 0x0 135184610Salfred#define CSWSTATUS_FAILED 0x1 136184610Salfred#define CSWSTATUS_PHASE 0x2 137184610Salfred} __packed; 138184610Salfred 139184610Salfredstruct bbb_transfer { 140184610Salfred struct mtx mtx; 141184610Salfred struct cv cv; 142259454Shselasky struct bbb_cbw *cbw; 143259454Shselasky struct bbb_csw *csw; 144184610Salfred 145192984Sthompsa struct usb_xfer *xfer[ST_MAX]; 146184610Salfred 147184610Salfred uint8_t *data_ptr; 148184610Salfred 149193074Sthompsa usb_size_t data_len; /* bytes */ 150193074Sthompsa usb_size_t data_rem; /* bytes */ 151193045Sthompsa usb_timeout_t data_timeout; /* ms */ 152193045Sthompsa usb_frlength_t actlen; /* bytes */ 153260575Shselasky usb_frlength_t buffer_size; /* bytes */ 154184610Salfred 155184610Salfred uint8_t cmd_len; /* bytes */ 156184610Salfred uint8_t dir; 157184610Salfred uint8_t lun; 158184610Salfred uint8_t state; 159184610Salfred uint8_t status_try; 160201681Sthompsa int error; 161184610Salfred 162259454Shselasky uint8_t *buffer; 163184610Salfred}; 164184610Salfred 165193045Sthompsastatic usb_callback_t bbb_command_callback; 166193045Sthompsastatic usb_callback_t bbb_data_read_callback; 167193045Sthompsastatic usb_callback_t bbb_data_rd_cs_callback; 168193045Sthompsastatic usb_callback_t bbb_data_write_callback; 169193045Sthompsastatic usb_callback_t bbb_data_wr_cs_callback; 170193045Sthompsastatic usb_callback_t bbb_status_callback; 171184610Salfred 172201681Sthompsastatic void bbb_done(struct bbb_transfer *, int); 173201681Sthompsastatic void bbb_transfer_start(struct bbb_transfer *, uint8_t); 174201681Sthompsastatic void bbb_data_clear_stall_callback(struct usb_xfer *, uint8_t, 175201681Sthompsa uint8_t); 176225350Shselaskystatic int bbb_command_start(struct bbb_transfer *, uint8_t, uint8_t, 177201681Sthompsa void *, size_t, void *, size_t, usb_timeout_t); 178201681Sthompsastatic struct bbb_transfer *bbb_attach(struct usb_device *, uint8_t); 179201681Sthompsastatic void bbb_detach(struct bbb_transfer *); 180201681Sthompsa 181192984Sthompsastatic const struct usb_config bbb_config[ST_MAX] = { 182184610Salfred 183184610Salfred [ST_COMMAND] = { 184184610Salfred .type = UE_BULK, 185184610Salfred .endpoint = UE_ADDR_ANY, 186184610Salfred .direction = UE_DIR_OUT, 187190734Sthompsa .bufsize = sizeof(struct bbb_cbw), 188190734Sthompsa .callback = &bbb_command_callback, 189190734Sthompsa .timeout = 4 * USB_MS_HZ, /* 4 seconds */ 190184610Salfred }, 191184610Salfred 192184610Salfred [ST_DATA_RD] = { 193184610Salfred .type = UE_BULK, 194184610Salfred .endpoint = UE_ADDR_ANY, 195184610Salfred .direction = UE_DIR_IN, 196260575Shselasky .bufsize = SCSI_MAX_LEN, 197259454Shselasky .flags = {.proxy_buffer = 1,.short_xfer_ok = 1,}, 198190734Sthompsa .callback = &bbb_data_read_callback, 199190734Sthompsa .timeout = 4 * USB_MS_HZ, /* 4 seconds */ 200184610Salfred }, 201184610Salfred 202184610Salfred [ST_DATA_RD_CS] = { 203184610Salfred .type = UE_CONTROL, 204184610Salfred .endpoint = 0x00, /* Control pipe */ 205184610Salfred .direction = UE_DIR_ANY, 206192984Sthompsa .bufsize = sizeof(struct usb_device_request), 207190734Sthompsa .callback = &bbb_data_rd_cs_callback, 208190734Sthompsa .timeout = 1 * USB_MS_HZ, /* 1 second */ 209184610Salfred }, 210184610Salfred 211184610Salfred [ST_DATA_WR] = { 212184610Salfred .type = UE_BULK, 213184610Salfred .endpoint = UE_ADDR_ANY, 214184610Salfred .direction = UE_DIR_OUT, 215260575Shselasky .bufsize = SCSI_MAX_LEN, 216225556Shselasky .flags = {.ext_buffer = 1,.proxy_buffer = 1,}, 217190734Sthompsa .callback = &bbb_data_write_callback, 218190734Sthompsa .timeout = 4 * USB_MS_HZ, /* 4 seconds */ 219184610Salfred }, 220184610Salfred 221184610Salfred [ST_DATA_WR_CS] = { 222184610Salfred .type = UE_CONTROL, 223184610Salfred .endpoint = 0x00, /* Control pipe */ 224184610Salfred .direction = UE_DIR_ANY, 225192984Sthompsa .bufsize = sizeof(struct usb_device_request), 226190734Sthompsa .callback = &bbb_data_wr_cs_callback, 227190734Sthompsa .timeout = 1 * USB_MS_HZ, /* 1 second */ 228184610Salfred }, 229184610Salfred 230184610Salfred [ST_STATUS] = { 231184610Salfred .type = UE_BULK, 232184610Salfred .endpoint = UE_ADDR_ANY, 233184610Salfred .direction = UE_DIR_IN, 234190734Sthompsa .bufsize = sizeof(struct bbb_csw), 235259454Shselasky .flags = {.short_xfer_ok = 1,}, 236190734Sthompsa .callback = &bbb_status_callback, 237190734Sthompsa .timeout = 1 * USB_MS_HZ, /* 1 second */ 238184610Salfred }, 239184610Salfred}; 240184610Salfred 241184610Salfredstatic void 242201681Sthompsabbb_done(struct bbb_transfer *sc, int error) 243184610Salfred{ 244184610Salfred sc->error = error; 245184610Salfred sc->state = ST_COMMAND; 246184610Salfred sc->status_try = 1; 247194227Sthompsa cv_signal(&sc->cv); 248184610Salfred} 249184610Salfred 250184610Salfredstatic void 251184610Salfredbbb_transfer_start(struct bbb_transfer *sc, uint8_t xfer_index) 252184610Salfred{ 253184610Salfred sc->state = xfer_index; 254194228Sthompsa usbd_transfer_start(sc->xfer[xfer_index]); 255184610Salfred} 256184610Salfred 257184610Salfredstatic void 258192984Sthompsabbb_data_clear_stall_callback(struct usb_xfer *xfer, 259184610Salfred uint8_t next_xfer, uint8_t stall_xfer) 260184610Salfred{ 261194677Sthompsa struct bbb_transfer *sc = usbd_xfer_softc(xfer); 262184610Salfred 263194228Sthompsa if (usbd_clear_stall_callback(xfer, sc->xfer[stall_xfer])) { 264184610Salfred switch (USB_GET_STATE(xfer)) { 265184610Salfred case USB_ST_SETUP: 266184610Salfred case USB_ST_TRANSFERRED: 267184610Salfred bbb_transfer_start(sc, next_xfer); 268184610Salfred break; 269184610Salfred default: 270201681Sthompsa bbb_done(sc, USB_ERR_STALLED); 271184610Salfred break; 272184610Salfred } 273184610Salfred } 274184610Salfred} 275184610Salfred 276184610Salfredstatic void 277194677Sthompsabbb_command_callback(struct usb_xfer *xfer, usb_error_t error) 278184610Salfred{ 279194677Sthompsa struct bbb_transfer *sc = usbd_xfer_softc(xfer); 280184610Salfred uint32_t tag; 281184610Salfred 282184610Salfred switch (USB_GET_STATE(xfer)) { 283184610Salfred case USB_ST_TRANSFERRED: 284184610Salfred bbb_transfer_start 285184610Salfred (sc, ((sc->dir == DIR_IN) ? ST_DATA_RD : 286184610Salfred (sc->dir == DIR_OUT) ? ST_DATA_WR : 287184610Salfred ST_STATUS)); 288184610Salfred break; 289184610Salfred 290184610Salfred case USB_ST_SETUP: 291184610Salfred sc->status_try = 0; 292259454Shselasky tag = UGETDW(sc->cbw->dCBWTag) + 1; 293259454Shselasky USETDW(sc->cbw->dCBWSignature, CBWSIGNATURE); 294259454Shselasky USETDW(sc->cbw->dCBWTag, tag); 295259454Shselasky USETDW(sc->cbw->dCBWDataTransferLength, (uint32_t)sc->data_len); 296259454Shselasky sc->cbw->bCBWFlags = ((sc->dir == DIR_IN) ? CBWFLAGS_IN : CBWFLAGS_OUT); 297259454Shselasky sc->cbw->bCBWLUN = sc->lun; 298259454Shselasky sc->cbw->bCDBLength = sc->cmd_len; 299259454Shselasky if (sc->cbw->bCDBLength > sizeof(sc->cbw->CBWCDB)) { 300259454Shselasky sc->cbw->bCDBLength = sizeof(sc->cbw->CBWCDB); 301199816Sthompsa DPRINTFN(0, "Truncating long command\n"); 302184610Salfred } 303260575Shselasky usbd_xfer_set_frame_len(xfer, 0, 304260575Shselasky sizeof(struct bbb_cbw)); 305194228Sthompsa usbd_transfer_submit(xfer); 306184610Salfred break; 307184610Salfred 308184610Salfred default: /* Error */ 309201681Sthompsa bbb_done(sc, error); 310184610Salfred break; 311184610Salfred } 312184610Salfred} 313184610Salfred 314184610Salfredstatic void 315194677Sthompsabbb_data_read_callback(struct usb_xfer *xfer, usb_error_t error) 316184610Salfred{ 317194677Sthompsa struct bbb_transfer *sc = usbd_xfer_softc(xfer); 318194677Sthompsa usb_frlength_t max_bulk = usbd_xfer_max_len(xfer); 319194677Sthompsa int actlen, sumlen; 320184610Salfred 321194677Sthompsa usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); 322194677Sthompsa 323184610Salfred switch (USB_GET_STATE(xfer)) { 324184610Salfred case USB_ST_TRANSFERRED: 325194677Sthompsa sc->data_rem -= actlen; 326194677Sthompsa sc->data_ptr += actlen; 327194677Sthompsa sc->actlen += actlen; 328184610Salfred 329194677Sthompsa if (actlen < sumlen) { 330184610Salfred /* short transfer */ 331184610Salfred sc->data_rem = 0; 332184610Salfred } 333184610Salfred case USB_ST_SETUP: 334184610Salfred DPRINTF("max_bulk=%d, data_rem=%d\n", 335184610Salfred max_bulk, sc->data_rem); 336184610Salfred 337184610Salfred if (sc->data_rem == 0) { 338184610Salfred bbb_transfer_start(sc, ST_STATUS); 339184610Salfred break; 340184610Salfred } 341184610Salfred if (max_bulk > sc->data_rem) { 342184610Salfred max_bulk = sc->data_rem; 343184610Salfred } 344194677Sthompsa usbd_xfer_set_timeout(xfer, sc->data_timeout); 345194677Sthompsa usbd_xfer_set_frame_data(xfer, 0, sc->data_ptr, max_bulk); 346194228Sthompsa usbd_transfer_submit(xfer); 347184610Salfred break; 348184610Salfred 349184610Salfred default: /* Error */ 350194677Sthompsa if (error == USB_ERR_CANCELLED) { 351201681Sthompsa bbb_done(sc, error); 352184610Salfred } else { 353184610Salfred bbb_transfer_start(sc, ST_DATA_RD_CS); 354184610Salfred } 355184610Salfred break; 356184610Salfred } 357184610Salfred} 358184610Salfred 359184610Salfredstatic void 360194677Sthompsabbb_data_rd_cs_callback(struct usb_xfer *xfer, usb_error_t error) 361184610Salfred{ 362184610Salfred bbb_data_clear_stall_callback(xfer, ST_STATUS, 363184610Salfred ST_DATA_RD); 364184610Salfred} 365184610Salfred 366184610Salfredstatic void 367194677Sthompsabbb_data_write_callback(struct usb_xfer *xfer, usb_error_t error) 368184610Salfred{ 369194677Sthompsa struct bbb_transfer *sc = usbd_xfer_softc(xfer); 370194677Sthompsa usb_frlength_t max_bulk = usbd_xfer_max_len(xfer); 371194677Sthompsa int actlen, sumlen; 372184610Salfred 373194677Sthompsa usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); 374194677Sthompsa 375184610Salfred switch (USB_GET_STATE(xfer)) { 376184610Salfred case USB_ST_TRANSFERRED: 377194677Sthompsa sc->data_rem -= actlen; 378194677Sthompsa sc->data_ptr += actlen; 379194677Sthompsa sc->actlen += actlen; 380184610Salfred 381194677Sthompsa if (actlen < sumlen) { 382184610Salfred /* short transfer */ 383184610Salfred sc->data_rem = 0; 384184610Salfred } 385184610Salfred case USB_ST_SETUP: 386184610Salfred DPRINTF("max_bulk=%d, data_rem=%d\n", 387184610Salfred max_bulk, sc->data_rem); 388184610Salfred 389184610Salfred if (sc->data_rem == 0) { 390184610Salfred bbb_transfer_start(sc, ST_STATUS); 391260575Shselasky break; 392184610Salfred } 393184610Salfred if (max_bulk > sc->data_rem) { 394184610Salfred max_bulk = sc->data_rem; 395184610Salfred } 396194677Sthompsa usbd_xfer_set_timeout(xfer, sc->data_timeout); 397194677Sthompsa usbd_xfer_set_frame_data(xfer, 0, sc->data_ptr, max_bulk); 398194228Sthompsa usbd_transfer_submit(xfer); 399260575Shselasky break; 400184610Salfred 401184610Salfred default: /* Error */ 402194677Sthompsa if (error == USB_ERR_CANCELLED) { 403201681Sthompsa bbb_done(sc, error); 404184610Salfred } else { 405184610Salfred bbb_transfer_start(sc, ST_DATA_WR_CS); 406184610Salfred } 407260575Shselasky break; 408184610Salfred } 409184610Salfred} 410184610Salfred 411184610Salfredstatic void 412194677Sthompsabbb_data_wr_cs_callback(struct usb_xfer *xfer, usb_error_t error) 413184610Salfred{ 414184610Salfred bbb_data_clear_stall_callback(xfer, ST_STATUS, 415184610Salfred ST_DATA_WR); 416184610Salfred} 417184610Salfred 418184610Salfredstatic void 419194677Sthompsabbb_status_callback(struct usb_xfer *xfer, usb_error_t error) 420184610Salfred{ 421194677Sthompsa struct bbb_transfer *sc = usbd_xfer_softc(xfer); 422235000Shselasky int actlen; 423235000Shselasky int sumlen; 424184610Salfred 425194677Sthompsa usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); 426194677Sthompsa 427184610Salfred switch (USB_GET_STATE(xfer)) { 428184610Salfred case USB_ST_TRANSFERRED: 429184610Salfred 430184610Salfred /* very simple status check */ 431184610Salfred 432259454Shselasky if (actlen < (int)sizeof(struct bbb_csw)) { 433201681Sthompsa bbb_done(sc, USB_ERR_SHORT_XFER); 434259454Shselasky } else if (sc->csw->bCSWStatus == CSWSTATUS_GOOD) { 435201681Sthompsa bbb_done(sc, 0); /* success */ 436184610Salfred } else { 437201681Sthompsa bbb_done(sc, ERR_CSW_FAILED); /* error */ 438184610Salfred } 439184610Salfred break; 440184610Salfred 441184610Salfred case USB_ST_SETUP: 442260575Shselasky usbd_xfer_set_frame_len(xfer, 0, 443260575Shselasky sizeof(struct bbb_csw)); 444194228Sthompsa usbd_transfer_submit(xfer); 445184610Salfred break; 446184610Salfred 447184610Salfred default: 448201681Sthompsa DPRINTF("Failed to read CSW: %s, try %d\n", 449194677Sthompsa usbd_errstr(error), sc->status_try); 450184610Salfred 451194677Sthompsa if (error == USB_ERR_CANCELLED || sc->status_try) { 452201681Sthompsa bbb_done(sc, error); 453184610Salfred } else { 454184610Salfred sc->status_try = 1; 455184610Salfred bbb_transfer_start(sc, ST_DATA_RD_CS); 456184610Salfred } 457184610Salfred break; 458184610Salfred } 459184610Salfred} 460184610Salfred 461184610Salfred/*------------------------------------------------------------------------* 462184610Salfred * bbb_command_start - execute a SCSI command synchronously 463184610Salfred * 464184610Salfred * Return values 465184610Salfred * 0: Success 466184610Salfred * Else: Failure 467184610Salfred *------------------------------------------------------------------------*/ 468225350Shselaskystatic int 469184610Salfredbbb_command_start(struct bbb_transfer *sc, uint8_t dir, uint8_t lun, 470201681Sthompsa void *data_ptr, size_t data_len, void *cmd_ptr, size_t cmd_len, 471193045Sthompsa usb_timeout_t data_timeout) 472184610Salfred{ 473184610Salfred sc->lun = lun; 474184610Salfred sc->dir = data_len ? dir : DIR_NONE; 475184610Salfred sc->data_ptr = data_ptr; 476184610Salfred sc->data_len = data_len; 477184610Salfred sc->data_rem = data_len; 478184610Salfred sc->data_timeout = (data_timeout + USB_MS_HZ); 479184610Salfred sc->actlen = 0; 480184610Salfred sc->cmd_len = cmd_len; 481259454Shselasky memset(&sc->cbw->CBWCDB, 0, sizeof(sc->cbw->CBWCDB)); 482259454Shselasky memcpy(&sc->cbw->CBWCDB, cmd_ptr, cmd_len); 483259454Shselasky DPRINTFN(1, "SCSI cmd = %*D\n", (int)cmd_len, (char *)sc->cbw->CBWCDB, ":"); 484184610Salfred 485201681Sthompsa mtx_lock(&sc->mtx); 486194228Sthompsa usbd_transfer_start(sc->xfer[sc->state]); 487184610Salfred 488194228Sthompsa while (usbd_transfer_pending(sc->xfer[sc->state])) { 489194227Sthompsa cv_wait(&sc->cv, &sc->mtx); 490184610Salfred } 491201681Sthompsa mtx_unlock(&sc->mtx); 492184610Salfred return (sc->error); 493184610Salfred} 494184610Salfred 495201681Sthompsastatic struct bbb_transfer * 496201681Sthompsabbb_attach(struct usb_device *udev, uint8_t iface_index) 497184610Salfred{ 498192984Sthompsa struct usb_interface *iface; 499192984Sthompsa struct usb_interface_descriptor *id; 500201681Sthompsa struct bbb_transfer *sc; 501193045Sthompsa usb_error_t err; 502223512Shselasky uint8_t do_unlock; 503184610Salfred 504247090Shselasky /* Prevent re-enumeration */ 505247090Shselasky do_unlock = usbd_enum_lock(udev); 506223512Shselasky 507223512Shselasky /* 508223512Shselasky * Make sure any driver which is hooked up to this interface, 509223512Shselasky * like umass is gone: 510223512Shselasky */ 511223512Shselasky usb_detach_device(udev, iface_index, 0); 512223512Shselasky 513223512Shselasky if (do_unlock) 514223512Shselasky usbd_enum_unlock(udev); 515223512Shselasky 516194228Sthompsa iface = usbd_get_iface(udev, iface_index); 517201681Sthompsa if (iface == NULL) 518201681Sthompsa return (NULL); 519201681Sthompsa 520184610Salfred id = iface->idesc; 521201681Sthompsa if (id == NULL || id->bInterfaceClass != UICLASS_MASS) 522201681Sthompsa return (NULL); 523201681Sthompsa 524184610Salfred switch (id->bInterfaceSubClass) { 525184610Salfred case UISUBCLASS_SCSI: 526184610Salfred case UISUBCLASS_UFI: 527201681Sthompsa case UISUBCLASS_SFF8020I: 528201681Sthompsa case UISUBCLASS_SFF8070I: 529184610Salfred break; 530184610Salfred default: 531201681Sthompsa return (NULL); 532184610Salfred } 533184610Salfred 534184610Salfred switch (id->bInterfaceProtocol) { 535184610Salfred case UIPROTO_MASS_BBB_OLD: 536184610Salfred case UIPROTO_MASS_BBB: 537184610Salfred break; 538184610Salfred default: 539201681Sthompsa return (NULL); 540184610Salfred } 541184610Salfred 542184610Salfred sc = malloc(sizeof(*sc), M_USB, M_WAITOK | M_ZERO); 543184610Salfred mtx_init(&sc->mtx, "USB autoinstall", NULL, MTX_DEF); 544194227Sthompsa cv_init(&sc->cv, "WBBB"); 545184610Salfred 546201681Sthompsa err = usbd_transfer_setup(udev, &iface_index, sc->xfer, bbb_config, 547184610Salfred ST_MAX, sc, &sc->mtx); 548184610Salfred if (err) { 549201681Sthompsa bbb_detach(sc); 550201681Sthompsa return (NULL); 551184610Salfred } 552259454Shselasky /* store pointer to DMA buffers */ 553259454Shselasky sc->buffer = usbd_xfer_get_frame_buffer( 554259454Shselasky sc->xfer[ST_DATA_RD], 0); 555260575Shselasky sc->buffer_size = 556260575Shselasky usbd_xfer_max_len(sc->xfer[ST_DATA_RD]); 557259454Shselasky sc->cbw = usbd_xfer_get_frame_buffer( 558259454Shselasky sc->xfer[ST_COMMAND], 0); 559259454Shselasky sc->csw = usbd_xfer_get_frame_buffer( 560259454Shselasky sc->xfer[ST_STATUS], 0); 561259454Shselasky 562201681Sthompsa return (sc); 563201681Sthompsa} 564184610Salfred 565201681Sthompsastatic void 566201681Sthompsabbb_detach(struct bbb_transfer *sc) 567201681Sthompsa{ 568201681Sthompsa usbd_transfer_unsetup(sc->xfer, ST_MAX); 569201681Sthompsa mtx_destroy(&sc->mtx); 570201681Sthompsa cv_destroy(&sc->cv); 571201681Sthompsa free(sc, M_USB); 572201681Sthompsa} 573184610Salfred 574201681Sthompsa/*------------------------------------------------------------------------* 575201681Sthompsa * usb_iface_is_cdrom 576201681Sthompsa * 577201681Sthompsa * Return values: 578201681Sthompsa * 1: This interface is an auto install disk (CD-ROM) 579201681Sthompsa * 0: Not an auto install disk. 580201681Sthompsa *------------------------------------------------------------------------*/ 581201681Sthompsaint 582201681Sthompsausb_iface_is_cdrom(struct usb_device *udev, uint8_t iface_index) 583201681Sthompsa{ 584201681Sthompsa struct bbb_transfer *sc; 585225350Shselasky uint8_t timeout; 586225350Shselasky uint8_t is_cdrom; 587201681Sthompsa uint8_t sid_type; 588225350Shselasky int err; 589184610Salfred 590201681Sthompsa sc = bbb_attach(udev, iface_index); 591201681Sthompsa if (sc == NULL) 592201681Sthompsa return (0); 593184610Salfred 594201681Sthompsa is_cdrom = 0; 595201681Sthompsa timeout = 4; /* tries */ 596201681Sthompsa while (--timeout) { 597201681Sthompsa err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, 598201681Sthompsa SCSI_INQ_LEN, &scsi_inquiry, sizeof(scsi_inquiry), 599201681Sthompsa USB_MS_HZ); 600185290Salfred 601201681Sthompsa if (err == 0 && sc->actlen > 0) { 602201681Sthompsa sid_type = sc->buffer[0] & 0x1F; 603201681Sthompsa if (sid_type == 0x05) 604201681Sthompsa is_cdrom = 1; 605201681Sthompsa break; 606201681Sthompsa } else if (err != ERR_CSW_FAILED) 607201681Sthompsa break; /* non retryable error */ 608201681Sthompsa usb_pause_mtx(NULL, hz); 609184610Salfred } 610201681Sthompsa bbb_detach(sc); 611201681Sthompsa return (is_cdrom); 612201681Sthompsa} 613184610Salfred 614227007Shselaskystatic uint8_t 615227007Shselaskyusb_msc_get_max_lun(struct usb_device *udev, uint8_t iface_index) 616227007Shselasky{ 617227007Shselasky struct usb_device_request req; 618227007Shselasky usb_error_t err; 619227007Shselasky uint8_t buf = 0; 620227007Shselasky 621227007Shselasky 622227007Shselasky /* The Get Max Lun command is a class-specific request. */ 623227007Shselasky req.bmRequestType = UT_READ_CLASS_INTERFACE; 624227007Shselasky req.bRequest = 0xFE; /* GET_MAX_LUN */ 625227007Shselasky USETW(req.wValue, 0); 626227007Shselasky req.wIndex[0] = iface_index; 627227007Shselasky req.wIndex[1] = 0; 628227007Shselasky USETW(req.wLength, 1); 629227007Shselasky 630227007Shselasky err = usbd_do_request(udev, NULL, &req, &buf); 631227007Shselasky if (err) 632227007Shselasky buf = 0; 633227007Shselasky 634227007Shselasky return (buf); 635227007Shselasky} 636227007Shselasky 637201681Sthompsausb_error_t 638225350Shselaskyusb_msc_auto_quirk(struct usb_device *udev, uint8_t iface_index) 639225350Shselasky{ 640225350Shselasky struct bbb_transfer *sc; 641225350Shselasky uint8_t timeout; 642225350Shselasky uint8_t is_no_direct; 643225350Shselasky uint8_t sid_type; 644225350Shselasky int err; 645225350Shselasky 646225350Shselasky sc = bbb_attach(udev, iface_index); 647225350Shselasky if (sc == NULL) 648225350Shselasky return (0); 649225350Shselasky 650225350Shselasky /* 651225350Shselasky * Some devices need a delay after that the configuration 652225350Shselasky * value is set to function properly: 653225350Shselasky */ 654225350Shselasky usb_pause_mtx(NULL, hz); 655225350Shselasky 656227007Shselasky if (usb_msc_get_max_lun(udev, iface_index) == 0) { 657227007Shselasky DPRINTF("Device has only got one LUN.\n"); 658227007Shselasky usbd_add_dynamic_quirk(udev, UQ_MSC_NO_GETMAXLUN); 659227007Shselasky } 660227007Shselasky 661225350Shselasky is_no_direct = 1; 662240666Shselasky for (timeout = 4; timeout != 0; timeout--) { 663225350Shselasky err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, 664225350Shselasky SCSI_INQ_LEN, &scsi_inquiry, sizeof(scsi_inquiry), 665225350Shselasky USB_MS_HZ); 666225350Shselasky 667225350Shselasky if (err == 0 && sc->actlen > 0) { 668225350Shselasky sid_type = sc->buffer[0] & 0x1F; 669225350Shselasky if (sid_type == 0x00) 670225350Shselasky is_no_direct = 0; 671225350Shselasky break; 672242821Shselasky } else if (err != ERR_CSW_FAILED) { 673242821Shselasky DPRINTF("Device is not responding " 674242821Shselasky "properly to SCSI INQUIRY command.\n"); 675242821Shselasky goto error; /* non retryable error */ 676242821Shselasky } 677225350Shselasky usb_pause_mtx(NULL, hz); 678225350Shselasky } 679225350Shselasky 680225350Shselasky if (is_no_direct) { 681225350Shselasky DPRINTF("Device is not direct access.\n"); 682225350Shselasky goto done; 683225350Shselasky } 684225350Shselasky 685225350Shselasky err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, 686225350Shselasky &scsi_test_unit_ready, sizeof(scsi_test_unit_ready), 687225350Shselasky USB_MS_HZ); 688225350Shselasky 689225350Shselasky if (err != 0) { 690225350Shselasky 691225350Shselasky if (err != ERR_CSW_FAILED) 692225350Shselasky goto error; 693225350Shselasky } 694240666Shselasky timeout = 1; 695225350Shselasky 696240666Shselaskyretry_sync_cache: 697225350Shselasky err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, 698225350Shselasky &scsi_sync_cache, sizeof(scsi_sync_cache), 699225350Shselasky USB_MS_HZ); 700225350Shselasky 701225350Shselasky if (err != 0) { 702225350Shselasky 703225350Shselasky if (err != ERR_CSW_FAILED) 704225350Shselasky goto error; 705225350Shselasky 706225350Shselasky DPRINTF("Device doesn't handle synchronize cache\n"); 707225350Shselasky 708225350Shselasky usbd_add_dynamic_quirk(udev, UQ_MSC_NO_SYNC_CACHE); 709240666Shselasky 710240666Shselasky } else { 711240666Shselasky 712240666Shselasky /* 713240666Shselasky * Certain Kingston memory sticks fail the first 714240666Shselasky * read capacity after a synchronize cache command 715240666Shselasky * has been issued. Disable the synchronize cache 716240666Shselasky * command for such devices. 717240666Shselasky */ 718240666Shselasky 719240666Shselasky err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, 8, 720240666Shselasky &scsi_read_capacity, sizeof(scsi_read_capacity), 721240666Shselasky USB_MS_HZ); 722240666Shselasky 723240666Shselasky if (err != 0) { 724240666Shselasky if (err != ERR_CSW_FAILED) 725240666Shselasky goto error; 726240666Shselasky 727240666Shselasky err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, 8, 728240666Shselasky &scsi_read_capacity, sizeof(scsi_read_capacity), 729240666Shselasky USB_MS_HZ); 730240666Shselasky 731240666Shselasky if (err == 0) { 732240666Shselasky if (timeout--) 733240666Shselasky goto retry_sync_cache; 734240666Shselasky 735240666Shselasky DPRINTF("Device most likely doesn't " 736240666Shselasky "handle synchronize cache\n"); 737240666Shselasky 738240666Shselasky usbd_add_dynamic_quirk(udev, 739240666Shselasky UQ_MSC_NO_SYNC_CACHE); 740240666Shselasky } else { 741240666Shselasky if (err != ERR_CSW_FAILED) 742240666Shselasky goto error; 743240666Shselasky } 744240666Shselasky } 745225350Shselasky } 746225350Shselasky 747225400Shselasky /* clear sense status of any failed commands on the device */ 748225400Shselasky 749225400Shselasky err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, 750225400Shselasky SCSI_INQ_LEN, &scsi_inquiry, sizeof(scsi_inquiry), 751225400Shselasky USB_MS_HZ); 752225400Shselasky 753225400Shselasky DPRINTF("Inquiry = %d\n", err); 754225400Shselasky 755225400Shselasky if (err != 0) { 756225400Shselasky 757225400Shselasky if (err != ERR_CSW_FAILED) 758225400Shselasky goto error; 759225400Shselasky } 760225400Shselasky 761225400Shselasky err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, 762225400Shselasky SCSI_SENSE_LEN, &scsi_request_sense, 763225400Shselasky sizeof(scsi_request_sense), USB_MS_HZ); 764225400Shselasky 765225400Shselasky DPRINTF("Request sense = %d\n", err); 766225400Shselasky 767225400Shselasky if (err != 0) { 768225400Shselasky 769225400Shselasky if (err != ERR_CSW_FAILED) 770225400Shselasky goto error; 771225400Shselasky } 772225400Shselasky 773225350Shselaskydone: 774225350Shselasky bbb_detach(sc); 775225350Shselasky return (0); 776225350Shselasky 777225350Shselaskyerror: 778225350Shselasky bbb_detach(sc); 779225350Shselasky 780225350Shselasky DPRINTF("Device did not respond, enabling all quirks\n"); 781225350Shselasky 782225350Shselasky usbd_add_dynamic_quirk(udev, UQ_MSC_NO_SYNC_CACHE); 783225350Shselasky usbd_add_dynamic_quirk(udev, UQ_MSC_NO_TEST_UNIT_READY); 784225350Shselasky 785225350Shselasky /* Need to re-enumerate the device */ 786225350Shselasky usbd_req_re_enumerate(udev, NULL); 787225350Shselasky 788225350Shselasky return (USB_ERR_STALLED); 789225350Shselasky} 790225350Shselasky 791225350Shselaskyusb_error_t 792201681Sthompsausb_msc_eject(struct usb_device *udev, uint8_t iface_index, int method) 793201681Sthompsa{ 794201681Sthompsa struct bbb_transfer *sc; 795201681Sthompsa usb_error_t err; 796201681Sthompsa 797201681Sthompsa sc = bbb_attach(udev, iface_index); 798201681Sthompsa if (sc == NULL) 799201681Sthompsa return (USB_ERR_INVAL); 800201681Sthompsa 801201681Sthompsa err = 0; 802201681Sthompsa switch (method) { 803201681Sthompsa case MSC_EJECT_STOPUNIT: 804201681Sthompsa err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, 805201681Sthompsa &scsi_test_unit_ready, sizeof(scsi_test_unit_ready), 806201681Sthompsa USB_MS_HZ); 807201681Sthompsa DPRINTF("Test unit ready status: %s\n", usbd_errstr(err)); 808201681Sthompsa err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, 809201681Sthompsa &scsi_start_stop_unit, sizeof(scsi_start_stop_unit), 810201681Sthompsa USB_MS_HZ); 811201681Sthompsa break; 812201681Sthompsa case MSC_EJECT_REZERO: 813201681Sthompsa err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, 814201681Sthompsa &scsi_rezero_init, sizeof(scsi_rezero_init), 815201681Sthompsa USB_MS_HZ); 816201681Sthompsa break; 817201681Sthompsa case MSC_EJECT_ZTESTOR: 818201681Sthompsa err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, 819201681Sthompsa &scsi_ztestor_eject, sizeof(scsi_ztestor_eject), 820201681Sthompsa USB_MS_HZ); 821201681Sthompsa break; 822201681Sthompsa case MSC_EJECT_CMOTECH: 823201681Sthompsa err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, 824201681Sthompsa &scsi_cmotech_eject, sizeof(scsi_cmotech_eject), 825201681Sthompsa USB_MS_HZ); 826201681Sthompsa break; 827203905Sthompsa case MSC_EJECT_HUAWEI: 828203905Sthompsa err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, 829203905Sthompsa &scsi_huawei_eject, sizeof(scsi_huawei_eject), 830203905Sthompsa USB_MS_HZ); 831203905Sthompsa break; 832213480Sglebius case MSC_EJECT_TCT: 833213717Sglebius /* 834213717Sglebius * TCTMobile needs DIR_IN flag. To get it, we 835213717Sglebius * supply a dummy data with the command. 836213717Sglebius */ 837260575Shselasky err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, 838260575Shselasky sc->buffer_size, &scsi_tct_eject, 839213480Sglebius sizeof(scsi_tct_eject), USB_MS_HZ); 840213480Sglebius break; 841201681Sthompsa default: 842201681Sthompsa printf("usb_msc_eject: unknown eject method (%d)\n", method); 843201681Sthompsa break; 844201681Sthompsa } 845201681Sthompsa DPRINTF("Eject CD command status: %s\n", usbd_errstr(err)); 846201681Sthompsa 847201681Sthompsa bbb_detach(sc); 848201681Sthompsa return (0); 849184610Salfred} 850