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