1 /* 2 * usb/disk - usb mass storage file server 3 * 4 * supports only the scsi command interface, not ata. 5 */ 6 7 #include <u.h> 8 #include <libc.h> 9 #include <ctype.h> 10 #include <fcall.h> 11 #include <thread.h> 12 #include "scsireq.h" 13 #include "usb.h" 14 #include "usbfs.h" 15 #include "ums.h" 16 17 enum 18 { 19 Qdir = 0, 20 Qctl, 21 Qraw, 22 Qdata, 23 Qmax, 24 }; 25 26 typedef struct Dirtab Dirtab; 27 struct Dirtab 28 { 29 char *name; 30 int mode; 31 }; 32 33 static Dirtab dirtab[] = 34 { 35 [Qdir] "/", DMDIR|0555, 36 [Qctl] "ctl", 0664, /* nothing secret here */ 37 [Qraw] "raw", 0640, 38 [Qdata] "data", 0640, 39 }; 40 41 /* 42 * These are used by scuzz scsireq 43 */ 44 int exabyte, force6bytecmds; 45 46 int diskdebug; 47 48 static void 49 ding(void *, char *msg) 50 { 51 if(strstr(msg, "alarm") != nil) 52 noted(NCONT); 53 noted(NDFLT); 54 } 55 56 static int 57 getmaxlun(Dev *dev) 58 { 59 uchar max; 60 int r; 61 62 max = 0; 63 r = Rd2h|Rclass|Riface; 64 if(usbcmd(dev, r, Getmaxlun, 0, 0, &max, 1) < 0){ 65 dprint(2, "disk: %s: getmaxlun failed: %r\n", dev->dir); 66 }else{ 67 max &= 017; /* 15 is the max. allowed */ 68 dprint(2, "disk: %s: maxlun %d\n", dev->dir, max); 69 } 70 return max; 71 } 72 73 static int 74 umsreset(Ums *ums) 75 { 76 int r; 77 78 r = Rh2d|Rclass|Riface; 79 if(usbcmd(ums->dev, r, Umsreset, 0, 0, nil, 0) < 0){ 80 fprint(2, "disk: reset: %r\n"); 81 return -1; 82 } 83 return 0; 84 } 85 86 static int 87 umsrecover(Ums *ums) 88 { 89 if(umsreset(ums) < 0) 90 return -1; 91 if(unstall(ums->dev, ums->epin, Ein) < 0) 92 dprint(2, "disk: unstall epin: %r\n"); 93 94 /* do we need this when epin == epout? */ 95 if(unstall(ums->dev, ums->epout, Eout) < 0) 96 dprint(2, "disk: unstall epout: %r\n"); 97 return 0; 98 } 99 100 static void 101 umsfatal(Ums *ums) 102 { 103 int i; 104 105 devctl(ums->dev, "detach"); 106 for(i = 0; i < ums->maxlun; i++) 107 usbfsdel(&ums->lun[i].fs); 108 } 109 110 static int 111 ispow2(uvlong ul) 112 { 113 return (ul & (ul - 1)) == 0; 114 } 115 116 /* 117 * return smallest power of 2 >= n 118 */ 119 static int 120 log2(int n) 121 { 122 int i; 123 124 for(i = 0; (1 << i) < n; i++) 125 ; 126 return i; 127 } 128 129 static int 130 umscapacity(Umsc *lun) 131 { 132 uchar data[32]; 133 134 lun->blocks = 0; 135 lun->capacity = 0; 136 lun->lbsize = 0; 137 memset(data, 0, sizeof data); 138 if(SRrcapacity(lun, data) < 0 && SRrcapacity(lun, data) < 0) 139 return -1; 140 lun->blocks = GETBELONG(data); 141 lun->lbsize = GETBELONG(data+4); 142 if(lun->blocks == 0xFFFFFFFF){ 143 if(SRrcapacity16(lun, data) < 0){ 144 lun->lbsize = 0; 145 lun->blocks = 0; 146 return -1; 147 }else{ 148 lun->lbsize = GETBELONG(data + 8); 149 lun->blocks = (uvlong)GETBELONG(data)<<32 | 150 GETBELONG(data + 4); 151 } 152 } 153 lun->blocks++; /* SRcapacity returns LBA of last block */ 154 lun->capacity = (vlong)lun->blocks * lun->lbsize; 155 if(diskdebug) 156 fprint(2, "disk: logical block size %lud, # blocks %llud\n", 157 lun->lbsize, lun->blocks); 158 return 0; 159 } 160 161 static int 162 umsinit(Ums *ums) 163 { 164 uchar i; 165 Umsc *lun; 166 int some; 167 168 umsreset(ums); 169 ums->maxlun = getmaxlun(ums->dev); 170 ums->lun = mallocz((ums->maxlun+1) * sizeof(*ums->lun), 1); 171 some = 0; 172 for(i = 0; i <= ums->maxlun; i++){ 173 lun = &ums->lun[i]; 174 lun->ums = ums; 175 lun->umsc = lun; 176 lun->lun = i; 177 lun->flags = Fopen | Fusb | Frw10; 178 if(SRinquiry(lun) < 0 && SRinquiry(lun) < 0){ 179 dprint(2, "disk: lun %d inquiry failed\n", i); 180 continue; 181 } 182 switch(lun->inquiry[0]){ 183 case Devdir: 184 case Devworm: /* a little different than the others */ 185 case Devcd: 186 case Devmo: 187 break; 188 default: 189 fprint(2, "disk: lun %d is not a disk (type %#02x)\n", 190 i, lun->inquiry[0]); 191 continue; 192 } 193 SRstart(lun, 1); 194 /* 195 * we ignore the device type reported by inquiry. 196 * Some devices return a wrong value but would still work. 197 */ 198 some++; 199 lun->inq = smprint("%.48s", (char *)lun->inquiry+8); 200 umscapacity(lun); 201 } 202 if(some == 0){ 203 dprint(2, "disk: all luns failed\n"); 204 devctl(ums->dev, "detach"); 205 return -1; 206 } 207 return 0; 208 } 209 210 211 /* 212 * called by SR*() commands provided by scuzz's scsireq 213 */ 214 long 215 umsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status) 216 { 217 Cbw cbw; 218 Csw csw; 219 int n, nio, left; 220 Ums *ums; 221 222 ums = umsc->ums; 223 224 memcpy(cbw.signature, "USBC", 4); 225 cbw.tag = ++ums->seq; 226 cbw.datalen = data->count; 227 cbw.flags = data->write? CbwDataOut: CbwDataIn; 228 cbw.lun = umsc->lun; 229 if(cmd->count < 1 || cmd->count > 16) 230 fprint(2, "disk: umsrequest: bad cmd count: %ld\n", cmd->count); 231 232 cbw.len = cmd->count; 233 assert(cmd->count <= sizeof(cbw.command)); 234 memcpy(cbw.command, cmd->p, cmd->count); 235 memset(cbw.command + cmd->count, 0, sizeof(cbw.command) - cmd->count); 236 237 werrstr(""); /* we use %r later even for n == 0 */ 238 if(diskdebug){ 239 fprint(2, "disk: cmd: tag %#lx: ", cbw.tag); 240 for(n = 0; n < cbw.len; n++) 241 fprint(2, " %2.2x", cbw.command[n]&0xFF); 242 fprint(2, " datalen: %ld\n", cbw.datalen); 243 } 244 245 /* issue tunnelled scsi command */ 246 if(write(ums->epout->dfd, &cbw, CbwLen) != CbwLen){ 247 fprint(2, "disk: cmd: %r\n"); 248 goto Fail; 249 } 250 251 /* transfer the data */ 252 nio = data->count; 253 if(nio != 0){ 254 if(data->write) 255 n = write(ums->epout->dfd, data->p, nio); 256 else{ 257 n = read(ums->epin->dfd, data->p, nio); 258 left = nio - n; 259 if (n >= 0 && left > 0) /* didn't fill data->p? */ 260 memset(data->p + n, 0, left); 261 } 262 nio = n; 263 if(diskdebug) 264 if(n < 0) 265 fprint(2, "disk: data: %r\n"); 266 else 267 fprint(2, "disk: data: %d bytes\n", n); 268 if(n <= 0) 269 if(data->write == 0) 270 unstall(ums->dev, ums->epin, Ein); 271 } 272 273 /* read the transfer's status */ 274 n = read(ums->epin->dfd, &csw, CswLen); 275 if(n <= 0){ 276 /* n == 0 means "stalled" */ 277 unstall(ums->dev, ums->epin, Ein); 278 n = read(ums->epin->dfd, &csw, CswLen); 279 } 280 281 if(n != CswLen || strncmp(csw.signature, "USBS", 4) != 0){ 282 dprint(2, "disk: read n=%d: status: %r\n", n); 283 goto Fail; 284 } 285 if(csw.tag != cbw.tag){ 286 dprint(2, "disk: status tag mismatch\n"); 287 goto Fail; 288 } 289 if(csw.status >= CswPhaseErr){ 290 dprint(2, "disk: phase error\n"); 291 goto Fail; 292 } 293 if(csw.dataresidue == 0 || ums->wrongresidues) 294 csw.dataresidue = data->count - nio; 295 if(diskdebug){ 296 fprint(2, "disk: status: %2.2ux residue: %ld\n", 297 csw.status, csw.dataresidue); 298 if(cbw.command[0] == ScmdRsense){ 299 fprint(2, "sense data:"); 300 for(n = 0; n < data->count - csw.dataresidue; n++) 301 fprint(2, " %2.2x", data->p[n]); 302 fprint(2, "\n"); 303 } 304 } 305 switch(csw.status){ 306 case CswOk: 307 *status = STok; 308 break; 309 case CswFailed: 310 *status = STcheck; 311 break; 312 default: 313 dprint(2, "disk: phase error\n"); 314 goto Fail; 315 } 316 ums->nerrs = 0; 317 return data->count - csw.dataresidue; 318 319 Fail: 320 *status = STharderr; 321 if(ums->nerrs++ > 15){ 322 fprint(2, "disk: %s: too many errors: device detached\n", ums->dev->dir); 323 umsfatal(ums); 324 }else 325 umsrecover(ums); 326 return -1; 327 } 328 329 static int 330 dwalk(Usbfs *fs, Fid *fid, char *name) 331 { 332 int i; 333 Qid qid; 334 335 qid = fid->qid; 336 if((qid.type & QTDIR) == 0){ 337 werrstr("walk in non-directory"); 338 return -1; 339 } 340 341 if(strcmp(name, "..") == 0) 342 return 0; 343 344 for(i = 1; i < nelem(dirtab); i++) 345 if(strcmp(name, dirtab[i].name) == 0){ 346 qid.path = i | fs->qid; 347 qid.vers = 0; 348 qid.type = dirtab[i].mode >> 24; 349 fid->qid = qid; 350 return 0; 351 } 352 werrstr(Enotfound); 353 return -1; 354 } 355 356 static void 357 dostat(Usbfs *fs, int path, Dir *d) 358 { 359 Dirtab *t; 360 Umsc *lun; 361 362 t = &dirtab[path]; 363 d->qid.path = path; 364 d->qid.type = t->mode >> 24; 365 d->mode = t->mode; 366 d->name = t->name; 367 lun = fs->aux; 368 if(path == Qdata) 369 d->length = lun->capacity; 370 else 371 d->length = 0; 372 } 373 374 static int 375 dirgen(Usbfs *fs, Qid, int i, Dir *d, void*) 376 { 377 i++; /* skip dir */ 378 if(i >= Qmax) 379 return -1; 380 else{ 381 dostat(fs, i, d); 382 d->qid.path |= fs->qid; 383 return 0; 384 } 385 } 386 387 static int 388 dstat(Usbfs *fs, Qid qid, Dir *d) 389 { 390 int path; 391 392 path = qid.path & ~fs->qid; 393 dostat(fs, path, d); 394 d->qid.path |= fs->qid; 395 return 0; 396 } 397 398 static int 399 dopen(Usbfs *fs, Fid *fid, int) 400 { 401 ulong path; 402 Umsc *lun; 403 404 path = fid->qid.path & ~fs->qid; 405 lun = fs->aux; 406 switch(path){ 407 case Qraw: 408 lun->phase = Pcmd; 409 break; 410 } 411 return 0; 412 } 413 414 /* 415 * check i/o parameters and compute values needed later. 416 * we shift & mask manually to avoid run-time calls to _divv and _modv, 417 * since we don't need general division nor its cost. 418 */ 419 static int 420 setup(Umsc *lun, char *data, int count, vlong offset) 421 { 422 long nb, lbsize, lbshift, lbmask; 423 uvlong bno; 424 425 if(count < 0 || lun->lbsize <= 0 && umscapacity(lun) < 0 || 426 lun->lbsize == 0) 427 return -1; 428 lbsize = lun->lbsize; 429 assert(ispow2(lbsize)); 430 lbshift = log2(lbsize); 431 lbmask = lbsize - 1; 432 433 bno = offset >> lbshift; /* offset / lbsize */ 434 nb = ((offset + count + lbsize - 1) >> lbshift) - bno; 435 436 if(bno + nb > lun->blocks) /* past end of device? */ 437 nb = lun->blocks - bno; 438 if(nb * lbsize > Maxiosize) 439 nb = Maxiosize / lbsize; 440 lun->nb = nb; 441 if(bno >= lun->blocks || nb == 0) 442 return 0; 443 444 lun->offset = bno; 445 lun->off = offset & lbmask; /* offset % lbsize */ 446 if(lun->off == 0 && (count & lbmask) == 0) 447 lun->bufp = data; 448 else 449 /* not transferring full, aligned blocks; need intermediary */ 450 lun->bufp = lun->buf; 451 return count; 452 } 453 454 /* 455 * Upon SRread/SRwrite errors we assume the medium may have changed, 456 * and ask again for the capacity of the media. 457 * BUG: How to proceed to avoid confusing dossrv?? 458 * 459 * ctl reads must match the format documented in sd(3) exactly 460 * to interoperate with the rest of the system. 461 */ 462 static long 463 dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset) 464 { 465 long n; 466 ulong path; 467 char buf[1024]; 468 char *s, *e; 469 Umsc *lun; 470 Ums *ums; 471 Qid q; 472 473 q = fid->qid; 474 path = fid->qid.path & ~fs->qid; 475 ums = fs->dev->aux; 476 lun = fs->aux; 477 478 qlock(ums); 479 switch(path){ 480 case Qdir: 481 count = usbdirread(fs, q, data, count, offset, dirgen, nil); 482 break; 483 case Qctl: 484 s = buf; 485 e = buf + sizeof(buf); 486 if(lun->flags & Finqok) 487 s = seprint(s, e, "inquiry %s lun %ld: %s\n", 488 fs->dev->dir, lun - &ums->lun[0], lun->inq); 489 if(lun->blocks > 0) 490 s = seprint(s, e, "geometry %llud %ld\n", 491 lun->blocks, lun->lbsize); 492 count = usbreadbuf(data, count, offset, buf, s - buf); 493 break; 494 case Qraw: 495 if(lun->lbsize <= 0 && umscapacity(lun) < 0){ 496 count = -1; 497 break; 498 } 499 switch(lun->phase){ 500 case Pcmd: 501 qunlock(ums); 502 werrstr("phase error"); 503 return -1; 504 case Pdata: 505 lun->data.p = data; 506 lun->data.count = count; 507 lun->data.write = 0; 508 count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status); 509 lun->phase = Pstatus; 510 if(count < 0) 511 lun->lbsize = 0; /* medium may have changed */ 512 break; 513 case Pstatus: 514 n = snprint(buf, sizeof buf, "%11.0ud ", lun->status); 515 count = usbreadbuf(data, count, 0LL, buf, n); 516 lun->phase = Pcmd; 517 break; 518 } 519 break; 520 case Qdata: 521 count = setup(lun, data, count, offset); 522 if (count <= 0) 523 break; 524 n = SRread(lun, lun->bufp, lun->nb * lun->lbsize); 525 if(n < 0){ 526 lun->lbsize = 0; /* medium may have changed */ 527 count = -1; 528 } else if (lun->bufp == data) 529 count = n; 530 else{ 531 /* 532 * if n == lun->nb*lun->lbsize (as expected), 533 * just copy count bytes. 534 */ 535 if(lun->off + count > n) 536 count = n - lun->off; /* short read */ 537 if(count > 0) 538 memmove(data, lun->bufp + lun->off, count); 539 } 540 break; 541 } 542 qunlock(ums); 543 return count; 544 } 545 546 static long 547 dwrite(Usbfs *fs, Fid *fid, void *data, long count, vlong offset) 548 { 549 long len, ocount; 550 ulong path; 551 uvlong bno; 552 Ums *ums; 553 Umsc *lun; 554 555 ums = fs->dev->aux; 556 lun = fs->aux; 557 path = fid->qid.path & ~fs->qid; 558 559 qlock(ums); 560 switch(path){ 561 default: 562 werrstr(Eperm); 563 count = -1; 564 break; 565 case Qctl: 566 dprint(2, "usb/disk: ctl ignored\n"); 567 break; 568 case Qraw: 569 if(lun->lbsize <= 0 && umscapacity(lun) < 0){ 570 count = -1; 571 break; 572 } 573 switch(lun->phase){ 574 case Pcmd: 575 if(count != 6 && count != 10){ 576 qunlock(ums); 577 werrstr("bad command length"); 578 return -1; 579 } 580 memmove(lun->rawcmd, data, count); 581 lun->cmd.p = lun->rawcmd; 582 lun->cmd.count = count; 583 lun->cmd.write = 1; 584 lun->phase = Pdata; 585 break; 586 case Pdata: 587 lun->data.p = data; 588 lun->data.count = count; 589 lun->data.write = 1; 590 count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status); 591 lun->phase = Pstatus; 592 if(count < 0) 593 lun->lbsize = 0; /* medium may have changed */ 594 break; 595 case Pstatus: 596 lun->phase = Pcmd; 597 werrstr("phase error"); 598 count = -1; 599 break; 600 } 601 break; 602 case Qdata: 603 len = ocount = count; 604 count = setup(lun, data, count, offset); 605 if (count <= 0) 606 break; 607 bno = lun->offset; 608 if (lun->bufp == lun->buf) { 609 count = SRread(lun, lun->bufp, lun->nb * lun->lbsize); 610 if(count < 0) { 611 lun->lbsize = 0; /* medium may have changed */ 612 break; 613 } 614 /* 615 * if count == lun->nb*lun->lbsize, as expected, just 616 * copy len (the original count) bytes of user data. 617 */ 618 if(lun->off + len > count) 619 len = count - lun->off; /* short read */ 620 if(len > 0) 621 memmove(lun->bufp + lun->off, data, len); 622 } 623 624 lun->offset = bno; 625 count = SRwrite(lun, lun->bufp, lun->nb * lun->lbsize); 626 if(count < 0) 627 lun->lbsize = 0; /* medium may have changed */ 628 else{ 629 if(lun->off + len > count) 630 count -= lun->off; /* short write */ 631 /* never report more bytes written than requested */ 632 if(count < 0) 633 count = 0; 634 else if(count > ocount) 635 count = ocount; 636 } 637 break; 638 } 639 qunlock(ums); 640 return count; 641 } 642 643 int 644 findendpoints(Ums *ums) 645 { 646 Ep *ep; 647 Usbdev *ud; 648 ulong csp, sc; 649 int i, epin, epout; 650 651 epin = epout = -1; 652 ud = ums->dev->usb; 653 for(i = 0; i < nelem(ud->ep); i++){ 654 if((ep = ud->ep[i]) == nil) 655 continue; 656 csp = ep->iface->csp; 657 sc = Subclass(csp); 658 if(!(Class(csp) == Clstorage && (Proto(csp) == Protobulk))) 659 continue; 660 if(sc != Subatapi && sc != Sub8070 && sc != Subscsi) 661 fprint(2, "disk: subclass %#ulx not supported. trying anyway\n", sc); 662 if(ep->type == Ebulk){ 663 if(ep->dir == Eboth || ep->dir == Ein) 664 if(epin == -1) 665 epin = ep->id; 666 if(ep->dir == Eboth || ep->dir == Eout) 667 if(epout == -1) 668 epout = ep->id; 669 } 670 } 671 dprint(2, "disk: ep ids: in %d out %d\n", epin, epout); 672 if(epin == -1 || epout == -1) 673 return -1; 674 ums->epin = openep(ums->dev, epin); 675 if(ums->epin == nil){ 676 fprint(2, "disk: openep %d: %r\n", epin); 677 return -1; 678 } 679 if(epout == epin){ 680 incref(ums->epin); 681 ums->epout = ums->epin; 682 }else 683 ums->epout = openep(ums->dev, epout); 684 if(ums->epout == nil){ 685 fprint(2, "disk: openep %d: %r\n", epout); 686 closedev(ums->epin); 687 return -1; 688 } 689 if(ums->epin == ums->epout) 690 opendevdata(ums->epin, ORDWR); 691 else{ 692 opendevdata(ums->epin, OREAD); 693 opendevdata(ums->epout, OWRITE); 694 } 695 if(ums->epin->dfd < 0 || ums->epout->dfd < 0){ 696 fprint(2, "disk: open i/o ep data: %r\n"); 697 closedev(ums->epin); 698 closedev(ums->epout); 699 return -1; 700 } 701 dprint(2, "disk: ep in %s out %s\n", ums->epin->dir, ums->epout->dir); 702 703 devctl(ums->epin, "timeout 2000"); 704 devctl(ums->epout, "timeout 2000"); 705 706 if(usbdebug > 1 || diskdebug > 2){ 707 devctl(ums->epin, "debug 1"); 708 devctl(ums->epout, "debug 1"); 709 devctl(ums->dev, "debug 1"); 710 } 711 return 0; 712 } 713 714 static int 715 usage(void) 716 { 717 werrstr("usage: usb/disk [-d] [-N nb]"); 718 return -1; 719 } 720 721 static void 722 umsdevfree(void *a) 723 { 724 Ums *ums = a; 725 726 if(ums == nil) 727 return; 728 closedev(ums->epin); 729 closedev(ums->epout); 730 ums->epin = ums->epout = nil; 731 free(ums->lun); 732 free(ums); 733 } 734 735 static Usbfs diskfs = { 736 .walk = dwalk, 737 .open = dopen, 738 .read = dread, 739 .write = dwrite, 740 .stat = dstat, 741 }; 742 743 int 744 diskmain(Dev *dev, int argc, char **argv) 745 { 746 Ums *ums; 747 Umsc *lun; 748 int i, devid; 749 750 devid = dev->id; 751 ARGBEGIN{ 752 case 'd': 753 scsidebug(diskdebug); 754 diskdebug++; 755 break; 756 case 'N': 757 devid = atoi(EARGF(usage())); 758 break; 759 default: 760 return usage(); 761 }ARGEND 762 if(argc != 0) { 763 return usage(); 764 } 765 766 // notify(ding); 767 ums = dev->aux = emallocz(sizeof(Ums), 1); 768 ums->maxlun = -1; 769 ums->dev = dev; 770 dev->free = umsdevfree; 771 if(findendpoints(ums) < 0){ 772 werrstr("disk: endpoints not found"); 773 return -1; 774 } 775 776 /* 777 * SanDISK 512M gets residues wrong. 778 */ 779 if(dev->usb->vid == 0x0781 && dev->usb->did == 0x5150) 780 ums->wrongresidues = 1; 781 782 if(umsinit(ums) < 0){ 783 dprint(2, "disk: umsinit: %r\n"); 784 return -1; 785 } 786 787 for(i = 0; i <= ums->maxlun; i++){ 788 lun = &ums->lun[i]; 789 lun->fs = diskfs; 790 snprint(lun->fs.name, sizeof(lun->fs.name), "sdU%d.%d", devid, i); 791 lun->fs.dev = dev; 792 incref(dev); 793 lun->fs.aux = lun; 794 usbfsadd(&lun->fs); 795 } 796 return 0; 797 } 798