1 /* $FreeBSD$ */ 2 /*- 3 * Copyright (c) 2008 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/linker_set.h> 44 #include <sys/module.h> 45 #include <sys/lock.h> 46 #include <sys/mutex.h> 47 #include <sys/condvar.h> 48 #include <sys/sysctl.h> 49 #include <sys/sx.h> 50 #include <sys/unistd.h> 51 #include <sys/callout.h> 52 #include <sys/malloc.h> 53 #include <sys/priv.h> 54 55 #include <dev/usb/usb.h> 56 #include <dev/usb/usbdi.h> 57 #include <dev/usb/usbdi_util.h> 58 59 #define USB_DEBUG_VAR usb_debug 60 61 #include <dev/usb/usb_busdma.h> 62 #include <dev/usb/usb_process.h> 63 #include <dev/usb/usb_transfer.h> 64 #include <dev/usb/usb_msctest.h> 65 #include <dev/usb/usb_debug.h> 66 #include <dev/usb/usb_busdma.h> 67 #include <dev/usb/usb_device.h> 68 #include <dev/usb/usb_request.h> 69 #include <dev/usb/usb_util.h> 70 71 #include <dev/usb/usb.h> 72 73 enum { 74 ST_COMMAND, 75 ST_DATA_RD, 76 ST_DATA_RD_CS, 77 ST_DATA_WR, 78 ST_DATA_WR_CS, 79 ST_STATUS, 80 ST_MAX, 81 }; 82 83 enum { 84 DIR_IN, 85 DIR_OUT, 86 DIR_NONE, 87 }; 88 89 #define BULK_SIZE 64 /* dummy */ 90 91 /* Command Block Wrapper */ 92 struct bbb_cbw { 93 uDWord dCBWSignature; 94 #define CBWSIGNATURE 0x43425355 95 uDWord dCBWTag; 96 uDWord dCBWDataTransferLength; 97 uByte bCBWFlags; 98 #define CBWFLAGS_OUT 0x00 99 #define CBWFLAGS_IN 0x80 100 uByte bCBWLUN; 101 uByte bCDBLength; 102 #define CBWCDBLENGTH 16 103 uByte CBWCDB[CBWCDBLENGTH]; 104 } __packed; 105 106 /* Command Status Wrapper */ 107 struct bbb_csw { 108 uDWord dCSWSignature; 109 #define CSWSIGNATURE 0x53425355 110 uDWord dCSWTag; 111 uDWord dCSWDataResidue; 112 uByte bCSWStatus; 113 #define CSWSTATUS_GOOD 0x0 114 #define CSWSTATUS_FAILED 0x1 115 #define CSWSTATUS_PHASE 0x2 116 } __packed; 117 118 struct bbb_transfer { 119 struct mtx mtx; 120 struct cv cv; 121 struct bbb_cbw cbw; 122 struct bbb_csw csw; 123 124 struct usb_xfer *xfer[ST_MAX]; 125 126 uint8_t *data_ptr; 127 128 usb_size_t data_len; /* bytes */ 129 usb_size_t data_rem; /* bytes */ 130 usb_timeout_t data_timeout; /* ms */ 131 usb_frlength_t actlen; /* bytes */ 132 133 uint8_t cmd_len; /* bytes */ 134 uint8_t dir; 135 uint8_t lun; 136 uint8_t state; 137 uint8_t error; 138 uint8_t status_try; 139 140 uint8_t buffer[256]; 141 }; 142 143 static usb_callback_t bbb_command_callback; 144 static usb_callback_t bbb_data_read_callback; 145 static usb_callback_t bbb_data_rd_cs_callback; 146 static usb_callback_t bbb_data_write_callback; 147 static usb_callback_t bbb_data_wr_cs_callback; 148 static usb_callback_t bbb_status_callback; 149 150 static const struct usb_config bbb_config[ST_MAX] = { 151 152 [ST_COMMAND] = { 153 .type = UE_BULK, 154 .endpoint = UE_ADDR_ANY, 155 .direction = UE_DIR_OUT, 156 .bufsize = sizeof(struct bbb_cbw), 157 .callback = &bbb_command_callback, 158 .timeout = 4 * USB_MS_HZ, /* 4 seconds */ 159 }, 160 161 [ST_DATA_RD] = { 162 .type = UE_BULK, 163 .endpoint = UE_ADDR_ANY, 164 .direction = UE_DIR_IN, 165 .bufsize = BULK_SIZE, 166 .flags = {.proxy_buffer = 1,.short_xfer_ok = 1,}, 167 .callback = &bbb_data_read_callback, 168 .timeout = 4 * USB_MS_HZ, /* 4 seconds */ 169 }, 170 171 [ST_DATA_RD_CS] = { 172 .type = UE_CONTROL, 173 .endpoint = 0x00, /* Control pipe */ 174 .direction = UE_DIR_ANY, 175 .bufsize = sizeof(struct usb_device_request), 176 .callback = &bbb_data_rd_cs_callback, 177 .timeout = 1 * USB_MS_HZ, /* 1 second */ 178 }, 179 180 [ST_DATA_WR] = { 181 .type = UE_BULK, 182 .endpoint = UE_ADDR_ANY, 183 .direction = UE_DIR_OUT, 184 .bufsize = BULK_SIZE, 185 .flags = {.proxy_buffer = 1,}, 186 .callback = &bbb_data_write_callback, 187 .timeout = 4 * USB_MS_HZ, /* 4 seconds */ 188 }, 189 190 [ST_DATA_WR_CS] = { 191 .type = UE_CONTROL, 192 .endpoint = 0x00, /* Control pipe */ 193 .direction = UE_DIR_ANY, 194 .bufsize = sizeof(struct usb_device_request), 195 .callback = &bbb_data_wr_cs_callback, 196 .timeout = 1 * USB_MS_HZ, /* 1 second */ 197 }, 198 199 [ST_STATUS] = { 200 .type = UE_BULK, 201 .endpoint = UE_ADDR_ANY, 202 .direction = UE_DIR_IN, 203 .bufsize = sizeof(struct bbb_csw), 204 .flags = {.short_xfer_ok = 1,}, 205 .callback = &bbb_status_callback, 206 .timeout = 1 * USB_MS_HZ, /* 1 second */ 207 }, 208 }; 209 210 static void 211 bbb_done(struct bbb_transfer *sc, uint8_t error) 212 { 213 struct usb_xfer *xfer; 214 215 xfer = sc->xfer[sc->state]; 216 217 /* verify the error code */ 218 219 if (error) { 220 switch (USB_GET_STATE(xfer)) { 221 case USB_ST_SETUP: 222 case USB_ST_TRANSFERRED: 223 error = 1; 224 break; 225 default: 226 error = 2; 227 break; 228 } 229 } 230 sc->error = error; 231 sc->state = ST_COMMAND; 232 sc->status_try = 1; 233 cv_signal(&sc->cv); 234 } 235 236 static void 237 bbb_transfer_start(struct bbb_transfer *sc, uint8_t xfer_index) 238 { 239 sc->state = xfer_index; 240 usbd_transfer_start(sc->xfer[xfer_index]); 241 } 242 243 static void 244 bbb_data_clear_stall_callback(struct usb_xfer *xfer, 245 uint8_t next_xfer, uint8_t stall_xfer) 246 { 247 struct bbb_transfer *sc = usbd_xfer_softc(xfer); 248 249 if (usbd_clear_stall_callback(xfer, sc->xfer[stall_xfer])) { 250 switch (USB_GET_STATE(xfer)) { 251 case USB_ST_SETUP: 252 case USB_ST_TRANSFERRED: 253 bbb_transfer_start(sc, next_xfer); 254 break; 255 default: 256 bbb_done(sc, 1); 257 break; 258 } 259 } 260 } 261 262 static void 263 bbb_command_callback(struct usb_xfer *xfer, usb_error_t error) 264 { 265 struct bbb_transfer *sc = usbd_xfer_softc(xfer); 266 uint32_t tag; 267 268 switch (USB_GET_STATE(xfer)) { 269 case USB_ST_TRANSFERRED: 270 bbb_transfer_start 271 (sc, ((sc->dir == DIR_IN) ? ST_DATA_RD : 272 (sc->dir == DIR_OUT) ? ST_DATA_WR : 273 ST_STATUS)); 274 break; 275 276 case USB_ST_SETUP: 277 sc->status_try = 0; 278 tag = UGETDW(sc->cbw.dCBWTag) + 1; 279 USETDW(sc->cbw.dCBWSignature, CBWSIGNATURE); 280 USETDW(sc->cbw.dCBWTag, tag); 281 USETDW(sc->cbw.dCBWDataTransferLength, (uint32_t)sc->data_len); 282 sc->cbw.bCBWFlags = ((sc->dir == DIR_IN) ? CBWFLAGS_IN : CBWFLAGS_OUT); 283 sc->cbw.bCBWLUN = sc->lun; 284 sc->cbw.bCDBLength = sc->cmd_len; 285 if (sc->cbw.bCDBLength > sizeof(sc->cbw.CBWCDB)) { 286 sc->cbw.bCDBLength = sizeof(sc->cbw.CBWCDB); 287 DPRINTFN(0, "Truncating long command!\n"); 288 } 289 usbd_xfer_set_frame_data(xfer, 0, &sc->cbw, sizeof(sc->cbw)); 290 usbd_transfer_submit(xfer); 291 break; 292 293 default: /* Error */ 294 bbb_done(sc, 1); 295 break; 296 } 297 } 298 299 static void 300 bbb_data_read_callback(struct usb_xfer *xfer, usb_error_t error) 301 { 302 struct bbb_transfer *sc = usbd_xfer_softc(xfer); 303 usb_frlength_t max_bulk = usbd_xfer_max_len(xfer); 304 int actlen, sumlen; 305 306 usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); 307 308 switch (USB_GET_STATE(xfer)) { 309 case USB_ST_TRANSFERRED: 310 sc->data_rem -= actlen; 311 sc->data_ptr += actlen; 312 sc->actlen += actlen; 313 314 if (actlen < sumlen) { 315 /* short transfer */ 316 sc->data_rem = 0; 317 } 318 case USB_ST_SETUP: 319 DPRINTF("max_bulk=%d, data_rem=%d\n", 320 max_bulk, sc->data_rem); 321 322 if (sc->data_rem == 0) { 323 bbb_transfer_start(sc, ST_STATUS); 324 break; 325 } 326 if (max_bulk > sc->data_rem) { 327 max_bulk = sc->data_rem; 328 } 329 usbd_xfer_set_timeout(xfer, sc->data_timeout); 330 usbd_xfer_set_frame_data(xfer, 0, sc->data_ptr, max_bulk); 331 usbd_transfer_submit(xfer); 332 break; 333 334 default: /* Error */ 335 if (error == USB_ERR_CANCELLED) { 336 bbb_done(sc, 1); 337 } else { 338 bbb_transfer_start(sc, ST_DATA_RD_CS); 339 } 340 break; 341 } 342 } 343 344 static void 345 bbb_data_rd_cs_callback(struct usb_xfer *xfer, usb_error_t error) 346 { 347 bbb_data_clear_stall_callback(xfer, ST_STATUS, 348 ST_DATA_RD); 349 } 350 351 static void 352 bbb_data_write_callback(struct usb_xfer *xfer, usb_error_t error) 353 { 354 struct bbb_transfer *sc = usbd_xfer_softc(xfer); 355 usb_frlength_t max_bulk = usbd_xfer_max_len(xfer); 356 int actlen, sumlen; 357 358 usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); 359 360 switch (USB_GET_STATE(xfer)) { 361 case USB_ST_TRANSFERRED: 362 sc->data_rem -= actlen; 363 sc->data_ptr += actlen; 364 sc->actlen += actlen; 365 366 if (actlen < sumlen) { 367 /* short transfer */ 368 sc->data_rem = 0; 369 } 370 case USB_ST_SETUP: 371 DPRINTF("max_bulk=%d, data_rem=%d\n", 372 max_bulk, sc->data_rem); 373 374 if (sc->data_rem == 0) { 375 bbb_transfer_start(sc, ST_STATUS); 376 return; 377 } 378 if (max_bulk > sc->data_rem) { 379 max_bulk = sc->data_rem; 380 } 381 usbd_xfer_set_timeout(xfer, sc->data_timeout); 382 usbd_xfer_set_frame_data(xfer, 0, sc->data_ptr, max_bulk); 383 usbd_transfer_submit(xfer); 384 return; 385 386 default: /* Error */ 387 if (error == USB_ERR_CANCELLED) { 388 bbb_done(sc, 1); 389 } else { 390 bbb_transfer_start(sc, ST_DATA_WR_CS); 391 } 392 return; 393 394 } 395 } 396 397 static void 398 bbb_data_wr_cs_callback(struct usb_xfer *xfer, usb_error_t error) 399 { 400 bbb_data_clear_stall_callback(xfer, ST_STATUS, 401 ST_DATA_WR); 402 } 403 404 static void 405 bbb_status_callback(struct usb_xfer *xfer, usb_error_t error) 406 { 407 struct bbb_transfer *sc = usbd_xfer_softc(xfer); 408 int actlen, sumlen; 409 410 usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); 411 412 switch (USB_GET_STATE(xfer)) { 413 case USB_ST_TRANSFERRED: 414 415 /* very simple status check */ 416 417 if (actlen < sizeof(sc->csw)) { 418 bbb_done(sc, 1);/* error */ 419 } else if (sc->csw.bCSWStatus == CSWSTATUS_GOOD) { 420 bbb_done(sc, 0);/* success */ 421 } else { 422 bbb_done(sc, 1);/* error */ 423 } 424 break; 425 426 case USB_ST_SETUP: 427 usbd_xfer_set_frame_data(xfer, 0, &sc->csw, sizeof(sc->csw)); 428 usbd_transfer_submit(xfer); 429 break; 430 431 default: 432 DPRINTFN(0, "Failed to read CSW: %s, try %d\n", 433 usbd_errstr(error), sc->status_try); 434 435 if (error == USB_ERR_CANCELLED || sc->status_try) { 436 bbb_done(sc, 1); 437 } else { 438 sc->status_try = 1; 439 bbb_transfer_start(sc, ST_DATA_RD_CS); 440 } 441 break; 442 } 443 } 444 445 /*------------------------------------------------------------------------* 446 * bbb_command_start - execute a SCSI command synchronously 447 * 448 * Return values 449 * 0: Success 450 * Else: Failure 451 *------------------------------------------------------------------------*/ 452 static uint8_t 453 bbb_command_start(struct bbb_transfer *sc, uint8_t dir, uint8_t lun, 454 void *data_ptr, usb_size_t data_len, uint8_t cmd_len, 455 usb_timeout_t data_timeout) 456 { 457 sc->lun = lun; 458 sc->dir = data_len ? dir : DIR_NONE; 459 sc->data_ptr = data_ptr; 460 sc->data_len = data_len; 461 sc->data_rem = data_len; 462 sc->data_timeout = (data_timeout + USB_MS_HZ); 463 sc->actlen = 0; 464 sc->cmd_len = cmd_len; 465 466 usbd_transfer_start(sc->xfer[sc->state]); 467 468 while (usbd_transfer_pending(sc->xfer[sc->state])) { 469 cv_wait(&sc->cv, &sc->mtx); 470 } 471 return (sc->error); 472 } 473 474 /*------------------------------------------------------------------------* 475 * usb_test_autoinstall 476 * 477 * Return values: 478 * 0: This interface is an auto install disk (CD-ROM) 479 * Else: Not an auto install disk. 480 *------------------------------------------------------------------------*/ 481 usb_error_t 482 usb_test_autoinstall(struct usb_device *udev, uint8_t iface_index, 483 uint8_t do_eject) 484 { 485 struct usb_interface *iface; 486 struct usb_interface_descriptor *id; 487 usb_error_t err; 488 uint8_t timeout; 489 uint8_t sid_type; 490 struct bbb_transfer *sc; 491 492 if (udev == NULL) { 493 return (USB_ERR_INVAL); 494 } 495 iface = usbd_get_iface(udev, iface_index); 496 if (iface == NULL) { 497 return (USB_ERR_INVAL); 498 } 499 id = iface->idesc; 500 if (id == NULL) { 501 return (USB_ERR_INVAL); 502 } 503 if (id->bInterfaceClass != UICLASS_MASS) { 504 return (USB_ERR_INVAL); 505 } 506 switch (id->bInterfaceSubClass) { 507 case UISUBCLASS_SCSI: 508 case UISUBCLASS_UFI: 509 break; 510 default: 511 return (USB_ERR_INVAL); 512 } 513 514 switch (id->bInterfaceProtocol) { 515 case UIPROTO_MASS_BBB_OLD: 516 case UIPROTO_MASS_BBB: 517 break; 518 default: 519 return (USB_ERR_INVAL); 520 } 521 522 sc = malloc(sizeof(*sc), M_USB, M_WAITOK | M_ZERO); 523 if (sc == NULL) { 524 return (USB_ERR_NOMEM); 525 } 526 mtx_init(&sc->mtx, "USB autoinstall", NULL, MTX_DEF); 527 cv_init(&sc->cv, "WBBB"); 528 529 err = usbd_transfer_setup(udev, 530 &iface_index, sc->xfer, bbb_config, 531 ST_MAX, sc, &sc->mtx); 532 533 if (err) { 534 goto done; 535 } 536 mtx_lock(&sc->mtx); 537 538 timeout = 4; /* tries */ 539 540 repeat_inquiry: 541 542 sc->cbw.CBWCDB[0] = 0x12; /* INQUIRY */ 543 sc->cbw.CBWCDB[1] = 0; 544 sc->cbw.CBWCDB[2] = 0; 545 sc->cbw.CBWCDB[3] = 0; 546 sc->cbw.CBWCDB[4] = 0x24; /* length */ 547 sc->cbw.CBWCDB[5] = 0; 548 err = bbb_command_start(sc, DIR_IN, 0, 549 sc->buffer, 0x24, 6, USB_MS_HZ); 550 551 if ((sc->actlen != 0) && (err == 0)) { 552 sid_type = sc->buffer[0] & 0x1F; 553 if (sid_type == 0x05) { 554 /* CD-ROM */ 555 if (do_eject) { 556 /* 0: opcode: SCSI START/STOP */ 557 sc->cbw.CBWCDB[0] = 0x1b; 558 /* 1: byte2: Not immediate */ 559 sc->cbw.CBWCDB[1] = 0x00; 560 /* 2..3: reserved */ 561 sc->cbw.CBWCDB[2] = 0x00; 562 sc->cbw.CBWCDB[3] = 0x00; 563 /* 4: Load/Eject command */ 564 sc->cbw.CBWCDB[4] = 0x02; 565 /* 5: control */ 566 sc->cbw.CBWCDB[5] = 0x00; 567 err = bbb_command_start(sc, DIR_OUT, 0, 568 NULL, 0, 6, USB_MS_HZ); 569 570 DPRINTFN(0, "Eject CD command " 571 "status: %s\n", usbd_errstr(err)); 572 } 573 err = 0; 574 goto done; 575 } 576 } else if ((err != 2) && --timeout) { 577 usb_pause_mtx(&sc->mtx, hz); 578 goto repeat_inquiry; 579 } 580 err = USB_ERR_INVAL; 581 goto done; 582 583 done: 584 mtx_unlock(&sc->mtx); 585 usbd_transfer_unsetup(sc->xfer, ST_MAX); 586 mtx_destroy(&sc->mtx); 587 cv_destroy(&sc->cv); 588 free(sc, M_USB); 589 return (err); 590 } 591