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