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, *e; 387 Umsc *lun; 388 Ums *ums; 389 Qid q; 390 391 q = fid->qid; 392 path = fid->qid.path & ~fs->qid; 393 ums = fs->dev->aux; 394 lun = fs->aux; 395 qlock(ums); 396 switch(path){ 397 case Qdir: 398 count = usbdirread(fs, q, data, count, offset, dirgen, nil); 399 break; 400 case Qctl: 401 e = buf + sizeof(buf); 402 s = seprint(buf, e, "%s lun %ld: ", fs->dev->dir, lun - &ums->lun[0]); 403 if(lun->flags & Finqok) 404 s = seprint(s, e, "inquiry %s ", lun->inq); 405 if(lun->blocks > 0) 406 s = seprint(s, e, "geometry %llud %ld", lun->blocks, 407 lun->lbsize); 408 s = seprint(s, e, "\n"); 409 count = usbreadbuf(data, count, offset, buf, s - buf); 410 break; 411 case Qraw: 412 if(lun->lbsize <= 0 && umscapacity(lun) < 0){ 413 qunlock(ums); 414 return -1; 415 } 416 switch(lun->phase){ 417 case Pcmd: 418 qunlock(ums); 419 werrstr("phase error"); 420 return -1; 421 case Pdata: 422 lun->data.p = (uchar*)data; 423 lun->data.count = count; 424 lun->data.write = 0; 425 count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status); 426 lun->phase = Pstatus; 427 if(count < 0){ 428 lun->lbsize = 0; /* medium may have changed */ 429 qunlock(ums); 430 return -1; 431 } 432 break; 433 case Pstatus: 434 n = snprint(buf, sizeof buf, "%11.0ud ", lun->status); 435 count = usbreadbuf(data, count, 0LL, buf, n); 436 lun->phase = Pcmd; 437 break; 438 } 439 break; 440 case Qdata: 441 if(lun->lbsize <= 0 && umscapacity(lun) < 0){ 442 qunlock(ums); 443 return -1; 444 } 445 bno = offset / lun->lbsize; 446 nb = (offset + count + lun->lbsize - 1) / lun->lbsize - bno; 447 if(bno + nb > lun->blocks) 448 nb = lun->blocks - bno; 449 if(bno >= lun->blocks || nb == 0){ 450 count = 0; 451 break; 452 } 453 if(nb * lun->lbsize > maxiosize) 454 nb = maxiosize / lun->lbsize; 455 p = emallocz(nb * lun->lbsize, 0); /* could use a static buffer */ 456 lun->offset = offset / lun->lbsize; 457 n = SRread(lun, p, nb * lun->lbsize); 458 if(n < 0){ 459 free(p); 460 lun->lbsize = 0; /* medium may have changed */ 461 qunlock(ums); 462 return -1; 463 } 464 len = count; 465 off = offset % lun->lbsize; 466 if(off + len > n) 467 len = n - off; 468 count = len; 469 memmove(data, p + off, len); 470 free(p); 471 break; 472 } 473 qunlock(ums); 474 return count; 475 } 476 477 static long 478 dwrite(Usbfs *fs, Fid *fid, void *buf, long count, vlong offset) 479 { 480 int bno, nb, len, off; 481 ulong path; 482 char *p; 483 Ums *ums; 484 Umsc *lun; 485 char *data; 486 487 ums = fs->dev->aux; 488 lun = fs->aux; 489 path = fid->qid.path & ~fs->qid; 490 data = buf; 491 qlock(ums); 492 switch(path){ 493 default: 494 qunlock(ums); 495 werrstr(Eperm); 496 return -1; 497 case Qctl: 498 qunlock(ums); 499 dprint(2, "usb/disk: ctl ignored\n"); 500 return count; 501 case Qraw: 502 if(lun->lbsize <= 0 && umscapacity(lun) < 0){ 503 qunlock(ums); 504 return -1; 505 } 506 switch(lun->phase){ 507 case Pcmd: 508 if(count != 6 && count != 10){ 509 qunlock(ums); 510 werrstr("bad command length"); 511 return -1; 512 } 513 memmove(lun->rawcmd, data, count); 514 lun->cmd.p = lun->rawcmd; 515 lun->cmd.count = count; 516 lun->cmd.write = 1; 517 lun->phase = Pdata; 518 break; 519 case Pdata: 520 lun->data.p = (uchar*)data; 521 lun->data.count = count; 522 lun->data.write = 1; 523 count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status); 524 lun->phase = Pstatus; 525 if(count < 0){ 526 lun->lbsize = 0; /* medium may have changed */ 527 qunlock(ums); 528 return -1; 529 } 530 break; 531 case Pstatus: 532 lun->phase = Pcmd; 533 qunlock(ums); 534 werrstr("phase error"); 535 return -1; 536 } 537 break; 538 case Qdata: 539 if(lun->lbsize <= 0 && umscapacity(lun) < 0){ 540 qunlock(ums); 541 return -1; 542 } 543 bno = offset / lun->lbsize; 544 nb = (offset + count + lun->lbsize-1) / lun->lbsize - bno; 545 if(bno + nb > lun->blocks) 546 nb = lun->blocks - bno; 547 if(bno >= lun->blocks || nb == 0){ 548 count = 0; 549 break; 550 } 551 if(nb * lun->lbsize > maxiosize) 552 nb = maxiosize / lun->lbsize; 553 p = emallocz(nb * lun->lbsize, 0); 554 off = offset % lun->lbsize; 555 len = count; 556 if(off || (len % lun->lbsize) != 0){ 557 lun->offset = offset / lun->lbsize; 558 count = SRread(lun, p, nb * lun->lbsize); 559 if(count < 0){ 560 free(p); 561 lun->lbsize = 0; /* medium may have changed */ 562 qunlock(ums); 563 return -1; 564 } 565 if(off + len > count) 566 len = count - off; 567 } 568 memmove(p+off, data, len); 569 lun->offset = offset / lun->lbsize; 570 count = SRwrite(lun, p, nb * lun->lbsize); 571 if(count < 0){ 572 free(p); 573 lun->lbsize = 0; /* medium may have changed */ 574 qunlock(ums); 575 return -1; 576 } 577 if(off+len > count) 578 len = count - off; 579 count = len; 580 free(p); 581 break; 582 } 583 qunlock(ums); 584 return count; 585 } 586 587 int 588 findendpoints(Ums *ums) 589 { 590 Ep *ep; 591 Usbdev *ud; 592 ulong csp, sc; 593 int i, epin, epout; 594 595 epin = epout = -1; 596 ud = ums->dev->usb; 597 for(i = 0; i < nelem(ud->ep); i++){ 598 if((ep = ud->ep[i]) == nil) 599 continue; 600 csp = ep->iface->csp; 601 sc = Subclass(csp); 602 if(!(Class(csp) == Clstorage && (Proto(csp) == Protobulk))) 603 continue; 604 if(sc != Subatapi && sc != Sub8070 && sc != Subscsi) 605 fprint(2, "disk: subclass %#ulx not supported. trying anyway\n", sc); 606 if(ep->type == Ebulk){ 607 if(ep->dir == Eboth || ep->dir == Ein) 608 if(epin == -1) 609 epin = ep->id; 610 if(ep->dir == Eboth || ep->dir == Eout) 611 if(epout == -1) 612 epout = ep->id; 613 } 614 } 615 dprint(2, "disk: ep ids: in %d out %d\n", epin, epout); 616 if(epin == -1 || epout == -1) 617 return -1; 618 ums->epin = openep(ums->dev, epin); 619 if(ums->epin == nil){ 620 fprint(2, "disk: openep %d: %r\n", epin); 621 return -1; 622 } 623 if(epout == epin){ 624 incref(ums->epin); 625 ums->epout = ums->epin; 626 }else 627 ums->epout = openep(ums->dev, epout); 628 if(ums->epout == nil){ 629 fprint(2, "disk: openep %d: %r\n", epout); 630 closedev(ums->epin); 631 return -1; 632 } 633 if(ums->epin == ums->epout) 634 opendevdata(ums->epin, ORDWR); 635 else{ 636 opendevdata(ums->epin, OREAD); 637 opendevdata(ums->epout, OWRITE); 638 } 639 if(ums->epin->dfd < 0 || ums->epout->dfd < 0){ 640 fprint(2, "disk: open i/o ep data: %r\n"); 641 closedev(ums->epin); 642 closedev(ums->epout); 643 return -1; 644 } 645 dprint(2, "disk: ep in %s out %s\n", ums->epin->dir, ums->epout->dir); 646 647 devctl(ums->epin, "timeout 2000"); 648 devctl(ums->epout, "timeout 2000"); 649 650 if(usbdebug > 1 || diskdebug > 2){ 651 devctl(ums->epin, "debug 1"); 652 devctl(ums->epout, "debug 1"); 653 devctl(ums->dev, "debug 1"); 654 } 655 return 0; 656 } 657 658 static int 659 usage(void) 660 { 661 werrstr("usage: usb/disk [-d] [-N nb]"); 662 return -1; 663 } 664 665 static void 666 umsdevfree(void *a) 667 { 668 Ums *ums = a; 669 670 if(ums == nil) 671 return; 672 closedev(ums->epin); 673 closedev(ums->epout); 674 ums->epin = ums->epout = nil; 675 free(ums->lun); 676 free(ums); 677 } 678 679 static Usbfs diskfs = { 680 .walk = dwalk, 681 .open = dopen, 682 .read = dread, 683 .write = dwrite, 684 .stat = dstat, 685 }; 686 687 int 688 diskmain(Dev *dev, int argc, char **argv) 689 { 690 Ums *ums; 691 Umsc *lun; 692 int i, devid; 693 694 devid = dev->id; 695 ARGBEGIN{ 696 case 'd': 697 scsidebug(diskdebug); 698 diskdebug++; 699 break; 700 case 'N': 701 devid = atoi(EARGF(usage())); 702 break; 703 default: 704 return usage(); 705 }ARGEND 706 if(argc != 0) { 707 return usage(); 708 } 709 710 ums = dev->aux = emallocz(sizeof(Ums), 1); 711 ums->maxlun = -1; 712 ums->dev = dev; 713 dev->free = umsdevfree; 714 if(findendpoints(ums) < 0){ 715 werrstr("disk: endpoints not found"); 716 return -1; 717 } 718 719 /* 720 * SanDISK 512M gets residues wrong. 721 */ 722 if(dev->usb->vid == 0x0781 && dev->usb->did == 0x5150) 723 ums->wrongresidues = 1; 724 725 if(umsinit(ums) < 0){ 726 dprint(2, "disk: umsinit: %r\n"); 727 return -1; 728 } 729 730 for(i = 0; i <= ums->maxlun; i++){ 731 lun = &ums->lun[i]; 732 lun->fs = diskfs; 733 snprint(lun->fs.name, sizeof(lun->fs.name), "sdU%d.%d", devid, i); 734 lun->fs.dev = dev; 735 incref(dev); 736 lun->fs.aux = lun; 737 usbfsadd(&lun->fs); 738 } 739 return 0; 740 } 741