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