2 * usb/disk - usb mass storage file server
4 * supports only the scsi command interface, not ata.
26 typedef struct Dirtab Dirtab;
33 static Dirtab dirtab[] =
35 [Qdir] "/", DMDIR|0555,
36 [Qctl] "ctl", 0664, /* nothing secret here */
44 * Partition management (adapted from disk/partfs)
47 addpart(Umsc *lun, char *name, vlong start, vlong end)
51 if(start < 0 || start > end || end > lun->blocks){
52 werrstr("bad partition boundaries");
56 if (strcmp(name, "ctl") == 0 || strcmp(name, "data") == 0) {
57 werrstr("partition name already in use");
60 for (p = lun->part; p < lun->part + Maxparts && p->inuse; p++)
61 if (strcmp(p->name, name) == 0) {
62 werrstr("partition name already in use");
65 if(p == lun->part + Maxparts){
66 werrstr("no free partition slots");
72 p->name = estrdup(name);
74 p->length = end - start;
81 delpart(Umsc *lun, char *s)
85 for (p = lun->part; p < lun->part + Maxparts; p++)
86 if(p->inuse && strcmp(p->name, s) == 0)
88 if(p == lun->part + Maxparts){
89 werrstr("partition not found");
100 * ctl parsing & formatting (adapted from partfs)
113 part = &lun->part[0];
116 fmtprint(&fmt, "%s lun %ld: ", fs->dev->dir, lun - &ums->lun[0]);
117 if(lun->flags & Finqok)
118 fmtprint(&fmt, "inquiry %s ", lun->inq);
120 fmtprint(&fmt, "geometry %llud %ld", lun->blocks, lun->lbsize);
121 fmtprint(&fmt, "\n");
122 for (p = part; p < &part[Maxparts]; p++)
124 fmtprint(&fmt, "part %s %lld %lld\n",
125 p->name, p->offset, p->length);
126 return fmtstrflush(&fmt);
130 ctlparse(Usbfs *fs, char *msg)
138 argc = tokenize(msg, argv, nelem(argv));
141 werrstr("empty control message");
145 if(strcmp(argv[0], "part") == 0){
147 werrstr("part takes 3 args");
150 start = strtoll(argv[2], 0, 0);
151 end = strtoll(argv[3], 0, 0);
152 return addpart(lun, argv[1], start, end);
153 }else if(strcmp(argv[0], "delpart") == 0){
155 werrstr("delpart takes 1 arg");
158 return delpart(lun, argv[1]);
160 werrstr("unknown ctl");
165 * These are used by scuzz scsireq
167 int exabyte, force6bytecmds;
172 ding(void *, char *msg)
174 if(strstr(msg, "alarm") != nil)
186 r = Rd2h|Rclass|Riface;
187 if(usbcmd(dev, r, Getmaxlun, 0, 0, &max, 1) < 0){
188 dprint(2, "disk: %s: getmaxlun failed: %r\n", dev->dir);
190 max &= 017; /* 15 is the max. allowed */
191 dprint(2, "disk: %s: maxlun %d\n", dev->dir, max);
201 r = Rh2d|Rclass|Riface;
202 if(usbcmd(ums->dev, r, Umsreset, 0, 0, nil, 0) < 0){
203 fprint(2, "disk: reset: %r\n");
212 if(umsreset(ums) < 0)
214 if(unstall(ums->dev, ums->epin, Ein) < 0)
215 dprint(2, "disk: unstall epin: %r\n");
217 /* do we need this when epin == epout? */
218 if(unstall(ums->dev, ums->epout, Eout) < 0)
219 dprint(2, "disk: unstall epout: %r\n");
228 devctl(ums->dev, "detach");
229 for(i = 0; i < ums->maxlun; i++)
230 usbfsdel(&ums->lun[i].fs);
236 return (ul & (ul - 1)) == 0;
240 * return smallest power of 2 >= n
247 for(i = 0; (1 << i) < n; i++)
253 umscapacity(Umsc *lun)
260 memset(data, 0, sizeof data);
261 if(SRrcapacity(lun, data) < 0 && SRrcapacity(lun, data) < 0)
263 lun->blocks = GETBELONG(data);
264 lun->lbsize = GETBELONG(data+4);
265 if(lun->blocks == 0xFFFFFFFF){
266 if(SRrcapacity16(lun, data) < 0){
271 lun->lbsize = GETBELONG(data + 8);
272 lun->blocks = (uvlong)GETBELONG(data)<<32 |
276 lun->blocks++; /* SRcapacity returns LBA of last block */
277 lun->capacity = (vlong)lun->blocks * lun->lbsize;
279 fprint(2, "disk: logical block size %lud, # blocks %llud\n",
280 lun->lbsize, lun->blocks);
292 ums->maxlun = getmaxlun(ums->dev);
293 ums->lun = mallocz((ums->maxlun+1) * sizeof(*ums->lun), 1);
295 for(i = 0; i <= ums->maxlun; i++){
300 lun->flags = Fopen | Fusb | Frw10;
301 if(SRinquiry(lun) < 0 && SRinquiry(lun) < 0){
302 dprint(2, "disk: lun %d inquiry failed\n", i);
305 switch(lun->inquiry[0]){
307 case Devworm: /* a little different than the others */
312 fprint(2, "disk: lun %d is not a disk (type %#02x)\n",
318 * we ignore the device type reported by inquiry.
319 * Some devices return a wrong value but would still work.
322 lun->inq = smprint("%.48s", (char *)lun->inquiry+8);
326 dprint(2, "disk: all luns failed\n");
327 devctl(ums->dev, "detach");
335 * called by SR*() commands provided by scuzz's scsireq
338 umsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status)
347 memcpy(cbw.signature, "USBC", 4);
348 cbw.tag = ++ums->seq;
349 cbw.datalen = data->count;
350 cbw.flags = data->write? CbwDataOut: CbwDataIn;
352 if(cmd->count < 1 || cmd->count > 16)
353 print("disk: umsrequest: bad cmd count: %ld\n", cmd->count);
355 cbw.len = cmd->count;
356 assert(cmd->count <= sizeof(cbw.command));
357 memcpy(cbw.command, cmd->p, cmd->count);
358 memset(cbw.command + cmd->count, 0, sizeof(cbw.command) - cmd->count);
360 werrstr(""); /* we use %r later even for n == 0 */
362 fprint(2, "disk: cmd: tag %#lx: ", cbw.tag);
363 for(n = 0; n < cbw.len; n++)
364 fprint(2, " %2.2x", cbw.command[n]&0xFF);
365 fprint(2, " datalen: %ld\n", cbw.datalen);
368 /* issue tunnelled scsi command */
369 if(write(ums->epout->dfd, &cbw, CbwLen) != CbwLen){
370 fprint(2, "disk: cmd: %r\n");
374 /* transfer the data */
378 n = write(ums->epout->dfd, data->p, nio);
380 n = read(ums->epin->dfd, data->p, nio);
382 if (n >= 0 && left > 0) /* didn't fill data->p? */
383 memset(data->p + n, 0, left);
388 fprint(2, "disk: data: %r\n");
390 fprint(2, "disk: data: %d bytes\n", n);
393 unstall(ums->dev, ums->epin, Ein);
396 /* read the transfer's status */
397 n = read(ums->epin->dfd, &csw, CswLen);
399 /* n == 0 means "stalled" */
400 unstall(ums->dev, ums->epin, Ein);
401 n = read(ums->epin->dfd, &csw, CswLen);
404 if(n != CswLen || strncmp(csw.signature, "USBS", 4) != 0){
405 dprint(2, "disk: read n=%d: status: %r\n", n);
408 if(csw.tag != cbw.tag){
409 dprint(2, "disk: status tag mismatch\n");
412 if(csw.status >= CswPhaseErr){
413 dprint(2, "disk: phase error\n");
416 if(csw.dataresidue == 0 || ums->wrongresidues)
417 csw.dataresidue = data->count - nio;
419 fprint(2, "disk: status: %2.2ux residue: %ld\n",
420 csw.status, csw.dataresidue);
421 if(cbw.command[0] == ScmdRsense){
422 fprint(2, "sense data:");
423 for(n = 0; n < data->count - csw.dataresidue; n++)
424 fprint(2, " %2.2x", data->p[n]);
436 dprint(2, "disk: phase error\n");
440 return data->count - csw.dataresidue;
444 if(ums->nerrs++ > 15){
445 fprint(2, "disk: %s: too many errors: device detached\n", ums->dev->dir);
453 dwalk(Usbfs *fs, Fid *fid, char *name)
459 if((qid.type & QTDIR) == 0){
460 werrstr("walk in non-directory");
464 if(strcmp(name, "..") == 0)
467 for(i = 1; i < nelem(dirtab); i++)
468 if(strcmp(name, dirtab[i].name) == 0){
469 qid.path = i | fs->qid;
471 qid.type = dirtab[i].mode >> 24;
480 dostat(Usbfs *fs, int path, Dir *d)
487 d->qid.type = t->mode >> 24;
492 d->length = lun->capacity;
498 dirgen(Usbfs *fs, Qid, int i, Dir *d, void*)
505 d->qid.path |= fs->qid;
511 dstat(Usbfs *fs, Qid qid, Dir *d)
515 path = qid.path & ~fs->qid;
517 d->qid.path |= fs->qid;
522 dopen(Usbfs *fs, Fid *fid, int)
527 path = fid->qid.path & ~fs->qid;
538 * check i/o parameters and compute values needed later.
539 * we shift & mask manually to avoid run-time calls to _divv and _modv,
540 * since we don't need general division nor its cost.
543 setup(Umsc *lun, char *data, int count, vlong offset)
545 long nb, lbsize, lbshift, lbmask;
548 if(count < 0 || lun->lbsize <= 0 && umscapacity(lun) < 0 ||
551 lbsize = lun->lbsize;
552 assert(ispow2(lbsize));
553 lbshift = log2(lbsize);
556 bno = offset >> lbshift; /* offset / lbsize */
557 nb = ((offset + count + lbsize - 1) >> lbshift) - bno;
559 if(bno + nb > lun->blocks) /* past end of device? */
560 nb = lun->blocks - bno;
561 if(nb * lbsize > Maxiosize)
562 nb = Maxiosize / lbsize;
564 if(bno >= lun->blocks || nb == 0)
568 lun->off = offset & lbmask; /* offset % lbsize */
569 if(lun->off == 0 && (count & lbmask) == 0)
572 /* not transferring full, aligned blocks; need intermediary */
573 lun->bufp = lun->buf;
578 * Upon SRread/SRwrite errors we assume the medium may have changed,
579 * and ask again for the capacity of the media.
580 * BUG: How to proceed to avoid confussing dossrv??
583 dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
594 path = fid->qid.path & ~fs->qid;
601 count = usbdirread(fs, q, data, count, offset, dirgen, nil);
605 count = usbreadbuf(data, count, offset, s, strlen(s));
609 if(lun->lbsize <= 0 && umscapacity(lun) < 0){
616 werrstr("phase error");
620 lun->data.count = count;
622 count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status);
623 lun->phase = Pstatus;
625 lun->lbsize = 0; /* medium may have changed */
628 n = snprint(buf, sizeof buf, "%11.0ud ", lun->status);
629 count = usbreadbuf(data, count, 0LL, buf, n);
635 count = setup(lun, data, count, offset);
638 n = SRread(lun, lun->bufp, lun->nb * lun->lbsize);
640 lun->lbsize = 0; /* medium may have changed */
642 } else if (lun->bufp == data)
646 * if n == lun->nb*lun->lbsize (as expected),
647 * just copy count bytes.
649 if(lun->off + count > n)
650 count = n - lun->off; /* short read */
652 memmove(data, lun->bufp + lun->off, count);
661 dwrite(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
672 path = fid->qid.path & ~fs->qid;
681 s = emallocz(count+1, 1);
682 memmove(s, data, count);
683 if(s[count-1] == '\n')
685 if(ctlparse(fs, s) == -1)
690 if(lun->lbsize <= 0 && umscapacity(lun) < 0){
696 if(count != 6 && count != 10){
698 werrstr("bad command length");
701 memmove(lun->rawcmd, data, count);
702 lun->cmd.p = lun->rawcmd;
703 lun->cmd.count = count;
709 lun->data.count = count;
711 count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status);
712 lun->phase = Pstatus;
714 lun->lbsize = 0; /* medium may have changed */
718 werrstr("phase error");
724 len = ocount = count;
725 count = setup(lun, data, count, offset);
729 if (lun->bufp == lun->buf) {
730 count = SRread(lun, lun->bufp, lun->nb * lun->lbsize);
732 lun->lbsize = 0; /* medium may have changed */
736 * if count == lun->nb*lun->lbsize, as expected, just
737 * copy len (the original count) bytes of user data.
739 if(lun->off + len > count)
740 len = count - lun->off; /* short read */
742 memmove(lun->bufp + lun->off, data, len);
746 count = SRwrite(lun, lun->bufp, lun->nb * lun->lbsize);
748 lun->lbsize = 0; /* medium may have changed */
750 if(lun->off + len > count)
751 count -= lun->off; /* short write */
752 /* never report more bytes written than requested */
755 else if(count > ocount)
765 findendpoints(Ums *ums)
774 for(i = 0; i < nelem(ud->ep); i++){
775 if((ep = ud->ep[i]) == nil)
777 csp = ep->iface->csp;
779 if(!(Class(csp) == Clstorage && (Proto(csp) == Protobulk)))
781 if(sc != Subatapi && sc != Sub8070 && sc != Subscsi)
782 fprint(2, "disk: subclass %#ulx not supported. trying anyway\n", sc);
783 if(ep->type == Ebulk){
784 if(ep->dir == Eboth || ep->dir == Ein)
787 if(ep->dir == Eboth || ep->dir == Eout)
792 dprint(2, "disk: ep ids: in %d out %d\n", epin, epout);
793 if(epin == -1 || epout == -1)
795 ums->epin = openep(ums->dev, epin);
796 if(ums->epin == nil){
797 fprint(2, "disk: openep %d: %r\n", epin);
802 ums->epout = ums->epin;
804 ums->epout = openep(ums->dev, epout);
805 if(ums->epout == nil){
806 fprint(2, "disk: openep %d: %r\n", epout);
810 if(ums->epin == ums->epout)
811 opendevdata(ums->epin, ORDWR);
813 opendevdata(ums->epin, OREAD);
814 opendevdata(ums->epout, OWRITE);
816 if(ums->epin->dfd < 0 || ums->epout->dfd < 0){
817 fprint(2, "disk: open i/o ep data: %r\n");
819 closedev(ums->epout);
822 dprint(2, "disk: ep in %s out %s\n", ums->epin->dir, ums->epout->dir);
824 devctl(ums->epin, "timeout 2000");
825 devctl(ums->epout, "timeout 2000");
827 if(usbdebug > 1 || diskdebug > 2){
828 devctl(ums->epin, "debug 1");
829 devctl(ums->epout, "debug 1");
830 devctl(ums->dev, "debug 1");
838 werrstr("usage: usb/disk [-d] [-N nb]");
850 closedev(ums->epout);
851 ums->epin = ums->epout = nil;
856 static Usbfs diskfs = {
865 diskmain(Dev *dev, int argc, char **argv)
874 scsidebug(diskdebug);
878 devid = atoi(EARGF(usage()));
888 ums = dev->aux = emallocz(sizeof(Ums), 1);
891 dev->free = umsdevfree;
892 if(findendpoints(ums) < 0){
893 werrstr("disk: endpoints not found");
898 * SanDISK 512M gets residues wrong.
900 if(dev->usb->vid == 0x0781 && dev->usb->did == 0x5150)
901 ums->wrongresidues = 1;
903 if(umsinit(ums) < 0){
904 dprint(2, "disk: umsinit: %r\n");
908 for(i = 0; i <= ums->maxlun; i++){
911 snprint(lun->fs.name, sizeof(lun->fs.name), "sdU%d.%d", devid, i);