usb_msctest.c revision 228232
1/* $FreeBSD: head/sys/dev/usb/usb_msctest.c 228232 2011-12-03 14:54:44Z hselasky $ */ 2/*- 3 * Copyright (c) 2008,2011 Hans Petter Selasky. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27/* 28 * The following file contains code that will detect USB autoinstall 29 * disks. 30 * 31 * TODO: Potentially we could add code to automatically detect USB 32 * mass storage quirks for not supported SCSI commands! 33 */ 34 35#include <sys/stdint.h> 36#include <sys/stddef.h> 37#include <sys/param.h> 38#include <sys/queue.h> 39#include <sys/types.h> 40#include <sys/systm.h> 41#include <sys/kernel.h> 42#include <sys/bus.h> 43#include <sys/module.h> 44#include <sys/lock.h> 45#include <sys/mutex.h> 46#include <sys/condvar.h> 47#include <sys/sysctl.h> 48#include <sys/sx.h> 49#include <sys/unistd.h> 50#include <sys/callout.h> 51#include <sys/malloc.h> 52#include <sys/priv.h> 53 54#include <dev/usb/usb.h> 55#include <dev/usb/usbdi.h> 56#include <dev/usb/usbdi_util.h> 57 58#define USB_DEBUG_VAR usb_debug 59 60#include <dev/usb/usb_busdma.h> 61#include <dev/usb/usb_process.h> 62#include <dev/usb/usb_transfer.h> 63#include <dev/usb/usb_msctest.h> 64#include <dev/usb/usb_debug.h> 65#include <dev/usb/usb_device.h> 66#include <dev/usb/usb_request.h> 67#include <dev/usb/usb_util.h> 68#include <dev/usb/quirk/usb_quirk.h> 69 70enum { 71 ST_COMMAND, 72 ST_DATA_RD, 73 ST_DATA_RD_CS, 74 ST_DATA_WR, 75 ST_DATA_WR_CS, 76 ST_STATUS, 77 ST_MAX, 78}; 79 80enum { 81 DIR_IN, 82 DIR_OUT, 83 DIR_NONE, 84}; 85 86#define SCSI_MAX_LEN 0x100 87#define SCSI_INQ_LEN 0x24 88#define SCSI_SENSE_LEN 0xFF 89 90static uint8_t scsi_test_unit_ready[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 91static uint8_t scsi_inquiry[] = { 0x12, 0x00, 0x00, 0x00, SCSI_INQ_LEN, 0x00 }; 92static uint8_t scsi_rezero_init[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }; 93static uint8_t scsi_start_stop_unit[] = { 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00 }; 94static uint8_t scsi_ztestor_eject[] = { 0x85, 0x01, 0x01, 0x01, 0x18, 0x01, 95 0x01, 0x01, 0x01, 0x01, 0x00, 0x00 }; 96static uint8_t scsi_cmotech_eject[] = { 0xff, 0x52, 0x44, 0x45, 0x56, 0x43, 97 0x48, 0x47 }; 98static uint8_t scsi_huawei_eject[] = { 0x11, 0x06, 0x00, 0x00, 0x00, 0x00, 99 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 100 0x00, 0x00, 0x00, 0x00 }; 101static uint8_t scsi_tct_eject[] = { 0x06, 0xf5, 0x04, 0x02, 0x52, 0x70 }; 102static uint8_t scsi_sync_cache[] = { 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 103 0x00, 0x00, 0x00, 0x00 }; 104static uint8_t scsi_request_sense[] = { 0x03, 0x00, 0x00, 0x00, 0x12, 0x00, 105 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 106 107#define BULK_SIZE 64 /* dummy */ 108#define ERR_CSW_FAILED -1 109 110/* Command Block Wrapper */ 111struct bbb_cbw { 112 uDWord dCBWSignature; 113#define CBWSIGNATURE 0x43425355 114 uDWord dCBWTag; 115 uDWord dCBWDataTransferLength; 116 uByte bCBWFlags; 117#define CBWFLAGS_OUT 0x00 118#define CBWFLAGS_IN 0x80 119 uByte bCBWLUN; 120 uByte bCDBLength; 121#define CBWCDBLENGTH 16 122 uByte CBWCDB[CBWCDBLENGTH]; 123} __packed; 124 125/* Command Status Wrapper */ 126struct bbb_csw { 127 uDWord dCSWSignature; 128#define CSWSIGNATURE 0x53425355 129 uDWord dCSWTag; 130 uDWord dCSWDataResidue; 131 uByte bCSWStatus; 132#define CSWSTATUS_GOOD 0x0 133#define CSWSTATUS_FAILED 0x1 134#define CSWSTATUS_PHASE 0x2 135} __packed; 136 137struct bbb_transfer { 138 struct mtx mtx; 139 struct cv cv; 140 struct bbb_cbw cbw; 141 struct bbb_csw csw; 142 143 struct usb_xfer *xfer[ST_MAX]; 144 145 uint8_t *data_ptr; 146 147 usb_size_t data_len; /* bytes */ 148 usb_size_t data_rem; /* bytes */ 149 usb_timeout_t data_timeout; /* ms */ 150 usb_frlength_t actlen; /* bytes */ 151 152 uint8_t cmd_len; /* bytes */ 153 uint8_t dir; 154 uint8_t lun; 155 uint8_t state; 156 uint8_t status_try; 157 int error; 158 159 uint8_t buffer[SCSI_MAX_LEN] __aligned(4); 160}; 161 162static usb_callback_t bbb_command_callback; 163static usb_callback_t bbb_data_read_callback; 164static usb_callback_t bbb_data_rd_cs_callback; 165static usb_callback_t bbb_data_write_callback; 166static usb_callback_t bbb_data_wr_cs_callback; 167static usb_callback_t bbb_status_callback; 168 169static void bbb_done(struct bbb_transfer *, int); 170static void bbb_transfer_start(struct bbb_transfer *, uint8_t); 171static void bbb_data_clear_stall_callback(struct usb_xfer *, uint8_t, 172 uint8_t); 173static int bbb_command_start(struct bbb_transfer *, uint8_t, uint8_t, 174 void *, size_t, void *, size_t, usb_timeout_t); 175static struct bbb_transfer *bbb_attach(struct usb_device *, uint8_t); 176static void bbb_detach(struct bbb_transfer *); 177 178static const struct usb_config bbb_config[ST_MAX] = { 179 180 [ST_COMMAND] = { 181 .type = UE_BULK, 182 .endpoint = UE_ADDR_ANY, 183 .direction = UE_DIR_OUT, 184 .bufsize = sizeof(struct bbb_cbw), 185 .flags = {.ext_buffer = 1,}, 186 .callback = &bbb_command_callback, 187 .timeout = 4 * USB_MS_HZ, /* 4 seconds */ 188 }, 189 190 [ST_DATA_RD] = { 191 .type = UE_BULK, 192 .endpoint = UE_ADDR_ANY, 193 .direction = UE_DIR_IN, 194 .bufsize = BULK_SIZE, 195 .flags = {.ext_buffer = 1,.proxy_buffer = 1,.short_xfer_ok = 1,}, 196 .callback = &bbb_data_read_callback, 197 .timeout = 4 * USB_MS_HZ, /* 4 seconds */ 198 }, 199 200 [ST_DATA_RD_CS] = { 201 .type = UE_CONTROL, 202 .endpoint = 0x00, /* Control pipe */ 203 .direction = UE_DIR_ANY, 204 .bufsize = sizeof(struct usb_device_request), 205 .callback = &bbb_data_rd_cs_callback, 206 .timeout = 1 * USB_MS_HZ, /* 1 second */ 207 }, 208 209 [ST_DATA_WR] = { 210 .type = UE_BULK, 211 .endpoint = UE_ADDR_ANY, 212 .direction = UE_DIR_OUT, 213 .bufsize = BULK_SIZE, 214 .flags = {.ext_buffer = 1,.proxy_buffer = 1,}, 215 .callback = &bbb_data_write_callback, 216 .timeout = 4 * USB_MS_HZ, /* 4 seconds */ 217 }, 218 219 [ST_DATA_WR_CS] = { 220 .type = UE_CONTROL, 221 .endpoint = 0x00, /* Control pipe */ 222 .direction = UE_DIR_ANY, 223 .bufsize = sizeof(struct usb_device_request), 224 .callback = &bbb_data_wr_cs_callback, 225 .timeout = 1 * USB_MS_HZ, /* 1 second */ 226 }, 227 228 [ST_STATUS] = { 229 .type = UE_BULK, 230 .endpoint = UE_ADDR_ANY, 231 .direction = UE_DIR_IN, 232 .bufsize = sizeof(struct bbb_csw), 233 .flags = {.ext_buffer = 1,.short_xfer_ok = 1,}, 234 .callback = &bbb_status_callback, 235 .timeout = 1 * USB_MS_HZ, /* 1 second */ 236 }, 237}; 238 239static void 240bbb_done(struct bbb_transfer *sc, int error) 241{ 242 243 sc->error = error; 244 sc->state = ST_COMMAND; 245 sc->status_try = 1; 246 cv_signal(&sc->cv); 247} 248 249static void 250bbb_transfer_start(struct bbb_transfer *sc, uint8_t xfer_index) 251{ 252 sc->state = xfer_index; 253 usbd_transfer_start(sc->xfer[xfer_index]); 254} 255 256static void 257bbb_data_clear_stall_callback(struct usb_xfer *xfer, 258 uint8_t next_xfer, uint8_t stall_xfer) 259{ 260 struct bbb_transfer *sc = usbd_xfer_softc(xfer); 261 262 if (usbd_clear_stall_callback(xfer, sc->xfer[stall_xfer])) { 263 switch (USB_GET_STATE(xfer)) { 264 case USB_ST_SETUP: 265 case USB_ST_TRANSFERRED: 266 bbb_transfer_start(sc, next_xfer); 267 break; 268 default: 269 bbb_done(sc, USB_ERR_STALLED); 270 break; 271 } 272 } 273} 274 275static void 276bbb_command_callback(struct usb_xfer *xfer, usb_error_t error) 277{ 278 struct bbb_transfer *sc = usbd_xfer_softc(xfer); 279 uint32_t tag; 280 281 switch (USB_GET_STATE(xfer)) { 282 case USB_ST_TRANSFERRED: 283 bbb_transfer_start 284 (sc, ((sc->dir == DIR_IN) ? ST_DATA_RD : 285 (sc->dir == DIR_OUT) ? ST_DATA_WR : 286 ST_STATUS)); 287 break; 288 289 case USB_ST_SETUP: 290 sc->status_try = 0; 291 tag = UGETDW(sc->cbw.dCBWTag) + 1; 292 USETDW(sc->cbw.dCBWSignature, CBWSIGNATURE); 293 USETDW(sc->cbw.dCBWTag, tag); 294 USETDW(sc->cbw.dCBWDataTransferLength, (uint32_t)sc->data_len); 295 sc->cbw.bCBWFlags = ((sc->dir == DIR_IN) ? CBWFLAGS_IN : CBWFLAGS_OUT); 296 sc->cbw.bCBWLUN = sc->lun; 297 sc->cbw.bCDBLength = sc->cmd_len; 298 if (sc->cbw.bCDBLength > sizeof(sc->cbw.CBWCDB)) { 299 sc->cbw.bCDBLength = sizeof(sc->cbw.CBWCDB); 300 DPRINTFN(0, "Truncating long command\n"); 301 } 302 usbd_xfer_set_frame_data(xfer, 0, &sc->cbw, sizeof(sc->cbw)); 303 usbd_transfer_submit(xfer); 304 break; 305 306 default: /* Error */ 307 bbb_done(sc, error); 308 break; 309 } 310} 311 312static void 313bbb_data_read_callback(struct usb_xfer *xfer, usb_error_t error) 314{ 315 struct bbb_transfer *sc = usbd_xfer_softc(xfer); 316 usb_frlength_t max_bulk = usbd_xfer_max_len(xfer); 317 int actlen, sumlen; 318 319 usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); 320 321 switch (USB_GET_STATE(xfer)) { 322 case USB_ST_TRANSFERRED: 323 sc->data_rem -= actlen; 324 sc->data_ptr += actlen; 325 sc->actlen += actlen; 326 327 if (actlen < sumlen) { 328 /* short transfer */ 329 sc->data_rem = 0; 330 } 331 case USB_ST_SETUP: 332 DPRINTF("max_bulk=%d, data_rem=%d\n", 333 max_bulk, sc->data_rem); 334 335 if (sc->data_rem == 0) { 336 bbb_transfer_start(sc, ST_STATUS); 337 break; 338 } 339 if (max_bulk > sc->data_rem) { 340 max_bulk = sc->data_rem; 341 } 342 usbd_xfer_set_timeout(xfer, sc->data_timeout); 343 usbd_xfer_set_frame_data(xfer, 0, sc->data_ptr, max_bulk); 344 usbd_transfer_submit(xfer); 345 break; 346 347 default: /* Error */ 348 if (error == USB_ERR_CANCELLED) { 349 bbb_done(sc, error); 350 } else { 351 bbb_transfer_start(sc, ST_DATA_RD_CS); 352 } 353 break; 354 } 355} 356 357static void 358bbb_data_rd_cs_callback(struct usb_xfer *xfer, usb_error_t error) 359{ 360 bbb_data_clear_stall_callback(xfer, ST_STATUS, 361 ST_DATA_RD); 362} 363 364static void 365bbb_data_write_callback(struct usb_xfer *xfer, usb_error_t error) 366{ 367 struct bbb_transfer *sc = usbd_xfer_softc(xfer); 368 usb_frlength_t max_bulk = usbd_xfer_max_len(xfer); 369 int actlen, sumlen; 370 371 usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); 372 373 switch (USB_GET_STATE(xfer)) { 374 case USB_ST_TRANSFERRED: 375 sc->data_rem -= actlen; 376 sc->data_ptr += actlen; 377 sc->actlen += actlen; 378 379 if (actlen < sumlen) { 380 /* short transfer */ 381 sc->data_rem = 0; 382 } 383 case USB_ST_SETUP: 384 DPRINTF("max_bulk=%d, data_rem=%d\n", 385 max_bulk, sc->data_rem); 386 387 if (sc->data_rem == 0) { 388 bbb_transfer_start(sc, ST_STATUS); 389 return; 390 } 391 if (max_bulk > sc->data_rem) { 392 max_bulk = sc->data_rem; 393 } 394 usbd_xfer_set_timeout(xfer, sc->data_timeout); 395 usbd_xfer_set_frame_data(xfer, 0, sc->data_ptr, max_bulk); 396 usbd_transfer_submit(xfer); 397 return; 398 399 default: /* Error */ 400 if (error == USB_ERR_CANCELLED) { 401 bbb_done(sc, error); 402 } else { 403 bbb_transfer_start(sc, ST_DATA_WR_CS); 404 } 405 return; 406 407 } 408} 409 410static void 411bbb_data_wr_cs_callback(struct usb_xfer *xfer, usb_error_t error) 412{ 413 bbb_data_clear_stall_callback(xfer, ST_STATUS, 414 ST_DATA_WR); 415} 416 417static void 418bbb_status_callback(struct usb_xfer *xfer, usb_error_t error) 419{ 420 struct bbb_transfer *sc = usbd_xfer_softc(xfer); 421 int actlen, sumlen; 422 423 usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); 424 425 switch (USB_GET_STATE(xfer)) { 426 case USB_ST_TRANSFERRED: 427 428 /* very simple status check */ 429 430 if (actlen < sizeof(sc->csw)) { 431 bbb_done(sc, USB_ERR_SHORT_XFER); 432 } else if (sc->csw.bCSWStatus == CSWSTATUS_GOOD) { 433 bbb_done(sc, 0); /* success */ 434 } else { 435 bbb_done(sc, ERR_CSW_FAILED); /* error */ 436 } 437 break; 438 439 case USB_ST_SETUP: 440 usbd_xfer_set_frame_data(xfer, 0, &sc->csw, sizeof(sc->csw)); 441 usbd_transfer_submit(xfer); 442 break; 443 444 default: 445 DPRINTF("Failed to read CSW: %s, try %d\n", 446 usbd_errstr(error), sc->status_try); 447 448 if (error == USB_ERR_CANCELLED || sc->status_try) { 449 bbb_done(sc, error); 450 } else { 451 sc->status_try = 1; 452 bbb_transfer_start(sc, ST_DATA_RD_CS); 453 } 454 break; 455 } 456} 457 458/*------------------------------------------------------------------------* 459 * bbb_command_start - execute a SCSI command synchronously 460 * 461 * Return values 462 * 0: Success 463 * Else: Failure 464 *------------------------------------------------------------------------*/ 465static int 466bbb_command_start(struct bbb_transfer *sc, uint8_t dir, uint8_t lun, 467 void *data_ptr, size_t data_len, void *cmd_ptr, size_t cmd_len, 468 usb_timeout_t data_timeout) 469{ 470 sc->lun = lun; 471 sc->dir = data_len ? dir : DIR_NONE; 472 sc->data_ptr = data_ptr; 473 sc->data_len = data_len; 474 sc->data_rem = data_len; 475 sc->data_timeout = (data_timeout + USB_MS_HZ); 476 sc->actlen = 0; 477 sc->cmd_len = cmd_len; 478 memset(&sc->cbw.CBWCDB, 0, sizeof(sc->cbw.CBWCDB)); 479 memcpy(&sc->cbw.CBWCDB, cmd_ptr, cmd_len); 480 DPRINTFN(1, "SCSI cmd = %*D\n", (int)cmd_len, (char *)sc->cbw.CBWCDB, ":"); 481 482 mtx_lock(&sc->mtx); 483 usbd_transfer_start(sc->xfer[sc->state]); 484 485 while (usbd_transfer_pending(sc->xfer[sc->state])) { 486 cv_wait(&sc->cv, &sc->mtx); 487 } 488 mtx_unlock(&sc->mtx); 489 return (sc->error); 490} 491 492static struct bbb_transfer * 493bbb_attach(struct usb_device *udev, uint8_t iface_index) 494{ 495 struct usb_interface *iface; 496 struct usb_interface_descriptor *id; 497 struct bbb_transfer *sc; 498 usb_error_t err; 499 uint8_t do_unlock; 500 501 /* automatic locking */ 502 if (usbd_enum_is_locked(udev)) { 503 do_unlock = 0; 504 } else { 505 do_unlock = 1; 506 usbd_enum_lock(udev); 507 } 508 509 /* 510 * Make sure any driver which is hooked up to this interface, 511 * like umass is gone: 512 */ 513 usb_detach_device(udev, iface_index, 0); 514 515 if (do_unlock) 516 usbd_enum_unlock(udev); 517 518 iface = usbd_get_iface(udev, iface_index); 519 if (iface == NULL) 520 return (NULL); 521 522 id = iface->idesc; 523 if (id == NULL || id->bInterfaceClass != UICLASS_MASS) 524 return (NULL); 525 526 switch (id->bInterfaceSubClass) { 527 case UISUBCLASS_SCSI: 528 case UISUBCLASS_UFI: 529 case UISUBCLASS_SFF8020I: 530 case UISUBCLASS_SFF8070I: 531 break; 532 default: 533 return (NULL); 534 } 535 536 switch (id->bInterfaceProtocol) { 537 case UIPROTO_MASS_BBB_OLD: 538 case UIPROTO_MASS_BBB: 539 break; 540 default: 541 return (NULL); 542 } 543 544 sc = malloc(sizeof(*sc), M_USB, M_WAITOK | M_ZERO); 545 mtx_init(&sc->mtx, "USB autoinstall", NULL, MTX_DEF); 546 cv_init(&sc->cv, "WBBB"); 547 548 err = usbd_transfer_setup(udev, &iface_index, sc->xfer, bbb_config, 549 ST_MAX, sc, &sc->mtx); 550 if (err) { 551 bbb_detach(sc); 552 return (NULL); 553 } 554 return (sc); 555} 556 557static void 558bbb_detach(struct bbb_transfer *sc) 559{ 560 usbd_transfer_unsetup(sc->xfer, ST_MAX); 561 mtx_destroy(&sc->mtx); 562 cv_destroy(&sc->cv); 563 free(sc, M_USB); 564} 565 566/*------------------------------------------------------------------------* 567 * usb_iface_is_cdrom 568 * 569 * Return values: 570 * 1: This interface is an auto install disk (CD-ROM) 571 * 0: Not an auto install disk. 572 *------------------------------------------------------------------------*/ 573int 574usb_iface_is_cdrom(struct usb_device *udev, uint8_t iface_index) 575{ 576 struct bbb_transfer *sc; 577 uint8_t timeout; 578 uint8_t is_cdrom; 579 uint8_t sid_type; 580 int err; 581 582 sc = bbb_attach(udev, iface_index); 583 if (sc == NULL) 584 return (0); 585 586 is_cdrom = 0; 587 timeout = 4; /* tries */ 588 while (--timeout) { 589 err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, 590 SCSI_INQ_LEN, &scsi_inquiry, sizeof(scsi_inquiry), 591 USB_MS_HZ); 592 593 if (err == 0 && sc->actlen > 0) { 594 sid_type = sc->buffer[0] & 0x1F; 595 if (sid_type == 0x05) 596 is_cdrom = 1; 597 break; 598 } else if (err != ERR_CSW_FAILED) 599 break; /* non retryable error */ 600 usb_pause_mtx(NULL, hz); 601 } 602 bbb_detach(sc); 603 return (is_cdrom); 604} 605 606static uint8_t 607usb_msc_get_max_lun(struct usb_device *udev, uint8_t iface_index) 608{ 609 struct usb_device_request req; 610 usb_error_t err; 611 uint8_t buf = 0; 612 613 614 /* The Get Max Lun command is a class-specific request. */ 615 req.bmRequestType = UT_READ_CLASS_INTERFACE; 616 req.bRequest = 0xFE; /* GET_MAX_LUN */ 617 USETW(req.wValue, 0); 618 req.wIndex[0] = iface_index; 619 req.wIndex[1] = 0; 620 USETW(req.wLength, 1); 621 622 err = usbd_do_request(udev, NULL, &req, &buf); 623 if (err) 624 buf = 0; 625 626 return (buf); 627} 628 629usb_error_t 630usb_msc_auto_quirk(struct usb_device *udev, uint8_t iface_index) 631{ 632 struct bbb_transfer *sc; 633 uint8_t timeout; 634 uint8_t is_no_direct; 635 uint8_t sid_type; 636 int err; 637 638 sc = bbb_attach(udev, iface_index); 639 if (sc == NULL) 640 return (0); 641 642 /* 643 * Some devices need a delay after that the configuration 644 * value is set to function properly: 645 */ 646 usb_pause_mtx(NULL, hz); 647 648 if (usb_msc_get_max_lun(udev, iface_index) == 0) { 649 DPRINTF("Device has only got one LUN.\n"); 650 usbd_add_dynamic_quirk(udev, UQ_MSC_NO_GETMAXLUN); 651 } 652 653 is_no_direct = 1; 654 for (timeout = 4; timeout; timeout--) { 655 err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, 656 SCSI_INQ_LEN, &scsi_inquiry, sizeof(scsi_inquiry), 657 USB_MS_HZ); 658 659 if (err == 0 && sc->actlen > 0) { 660 sid_type = sc->buffer[0] & 0x1F; 661 if (sid_type == 0x00) 662 is_no_direct = 0; 663 break; 664 } else if (err != ERR_CSW_FAILED) 665 break; /* non retryable error */ 666 usb_pause_mtx(NULL, hz); 667 } 668 669 if (is_no_direct) { 670 DPRINTF("Device is not direct access.\n"); 671 goto done; 672 } 673 674 err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, 675 &scsi_test_unit_ready, sizeof(scsi_test_unit_ready), 676 USB_MS_HZ); 677 678 if (err != 0) { 679 680 if (err != ERR_CSW_FAILED) 681 goto error; 682 } 683 684 err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, 685 &scsi_sync_cache, sizeof(scsi_sync_cache), 686 USB_MS_HZ); 687 688 if (err != 0) { 689 690 if (err != ERR_CSW_FAILED) 691 goto error; 692 693 DPRINTF("Device doesn't handle synchronize cache\n"); 694 695 usbd_add_dynamic_quirk(udev, UQ_MSC_NO_SYNC_CACHE); 696 } 697 698 /* clear sense status of any failed commands on the device */ 699 700 err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, 701 SCSI_INQ_LEN, &scsi_inquiry, sizeof(scsi_inquiry), 702 USB_MS_HZ); 703 704 DPRINTF("Inquiry = %d\n", err); 705 706 if (err != 0) { 707 708 if (err != ERR_CSW_FAILED) 709 goto error; 710 } 711 712 err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, 713 SCSI_SENSE_LEN, &scsi_request_sense, 714 sizeof(scsi_request_sense), USB_MS_HZ); 715 716 DPRINTF("Request sense = %d\n", err); 717 718 if (err != 0) { 719 720 if (err != ERR_CSW_FAILED) 721 goto error; 722 } 723 724done: 725 bbb_detach(sc); 726 return (0); 727 728error: 729 bbb_detach(sc); 730 731 DPRINTF("Device did not respond, enabling all quirks\n"); 732 733 usbd_add_dynamic_quirk(udev, UQ_MSC_NO_SYNC_CACHE); 734 usbd_add_dynamic_quirk(udev, UQ_MSC_NO_TEST_UNIT_READY); 735 736 /* Need to re-enumerate the device */ 737 usbd_req_re_enumerate(udev, NULL); 738 739 return (USB_ERR_STALLED); 740} 741 742usb_error_t 743usb_msc_eject(struct usb_device *udev, uint8_t iface_index, int method) 744{ 745 struct bbb_transfer *sc; 746 usb_error_t err; 747 748 sc = bbb_attach(udev, iface_index); 749 if (sc == NULL) 750 return (USB_ERR_INVAL); 751 752 err = 0; 753 switch (method) { 754 case MSC_EJECT_STOPUNIT: 755 err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, 756 &scsi_test_unit_ready, sizeof(scsi_test_unit_ready), 757 USB_MS_HZ); 758 DPRINTF("Test unit ready status: %s\n", usbd_errstr(err)); 759 err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, 760 &scsi_start_stop_unit, sizeof(scsi_start_stop_unit), 761 USB_MS_HZ); 762 break; 763 case MSC_EJECT_REZERO: 764 err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, 765 &scsi_rezero_init, sizeof(scsi_rezero_init), 766 USB_MS_HZ); 767 break; 768 case MSC_EJECT_ZTESTOR: 769 err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, 770 &scsi_ztestor_eject, sizeof(scsi_ztestor_eject), 771 USB_MS_HZ); 772 break; 773 case MSC_EJECT_CMOTECH: 774 err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, 775 &scsi_cmotech_eject, sizeof(scsi_cmotech_eject), 776 USB_MS_HZ); 777 break; 778 case MSC_EJECT_HUAWEI: 779 err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, 780 &scsi_huawei_eject, sizeof(scsi_huawei_eject), 781 USB_MS_HZ); 782 break; 783 case MSC_EJECT_TCT: 784 /* 785 * TCTMobile needs DIR_IN flag. To get it, we 786 * supply a dummy data with the command. 787 */ 788 err = bbb_command_start(sc, DIR_IN, 0, &sc->buffer, 789 sizeof(sc->buffer), &scsi_tct_eject, 790 sizeof(scsi_tct_eject), USB_MS_HZ); 791 break; 792 default: 793 printf("usb_msc_eject: unknown eject method (%d)\n", method); 794 break; 795 } 796 DPRINTF("Eject CD command status: %s\n", usbd_errstr(err)); 797 798 bbb_detach(sc); 799 return (0); 800} 801