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