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