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