summaryrefslogtreecommitdiff
path: root/sys/src/cmd/nusb/disk
diff options
context:
space:
mode:
authoraiju <aiju@phicode.de>2011-07-27 20:07:30 +0200
committeraiju <aiju@phicode.de>2011-07-27 20:07:30 +0200
commit05d09f086fdb76206ab5ff0e26a1e1bc2f34b6a3 (patch)
treec62905a4a8467110b426843a683136a835c8be14 /sys/src/cmd/nusb/disk
parent5181f2e576dc6b92dfa7dcaa2f60397b931fbc4a (diff)
nusb: improved
Diffstat (limited to 'sys/src/cmd/nusb/disk')
-rw-r--r--sys/src/cmd/nusb/disk/disk.c982
-rw-r--r--sys/src/cmd/nusb/disk/mkfile24
-rwxr-xr-xsys/src/cmd/nusb/disk/mkscsierrs32
-rw-r--r--sys/src/cmd/nusb/disk/scsireq.c986
-rw-r--r--sys/src/cmd/nusb/disk/scsireq.h238
-rw-r--r--sys/src/cmd/nusb/disk/ums.h124
6 files changed, 2386 insertions, 0 deletions
diff --git a/sys/src/cmd/nusb/disk/disk.c b/sys/src/cmd/nusb/disk/disk.c
new file mode 100644
index 000000000..de69bc8b6
--- /dev/null
+++ b/sys/src/cmd/nusb/disk/disk.c
@@ -0,0 +1,982 @@
+/*
+ * usb/disk - usb mass storage file server
+ *
+ * supports only the scsi command interface, not ata.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "scsireq.h"
+#include "usb.h"
+#include "ums.h"
+
+enum
+{
+ Qdir = 0,
+ Qctl,
+ Qraw,
+ Qdata,
+ Qpart,
+ Qmax = Maxparts,
+};
+
+typedef struct Dirtab Dirtab;
+struct Dirtab
+{
+ char *name;
+ int mode;
+};
+
+ulong ctlmode = 0664;
+
+/*
+ * Partition management (adapted from disk/partfs)
+ */
+
+Part *
+lookpart(Umsc *lun, char *name)
+{
+ Part *part, *p;
+
+ part = lun->part;
+ for(p=part; p < &part[Qmax]; p++){
+ if(p->inuse && strcmp(p->name, name) == 0)
+ return p;
+ }
+ return nil;
+}
+
+Part *
+freepart(Umsc *lun)
+{
+ Part *part, *p;
+
+ part = lun->part;
+ for(p=part; p < &part[Qmax]; p++){
+ if(!p->inuse)
+ return p;
+ }
+ return nil;
+}
+
+int
+addpart(Umsc *lun, char *name, vlong start, vlong end, ulong mode)
+{
+ Part *p;
+
+ if(start < 0 || start > end || end > lun->blocks){
+ werrstr("bad partition boundaries");
+ return -1;
+ }
+ if(lookpart(lun, name) != nil) {
+ werrstr("partition name already in use");
+ return -1;
+ }
+ p = freepart(lun);
+ if(p == nil){
+ werrstr("no free partition slots");
+ return -1;
+ }
+ p->inuse = 1;
+ free(p->name);
+ p->id = p - lun->part;
+ p->name = estrdup(name);
+ p->offset = start;
+ p->length = end - start;
+ p->mode = mode;
+ return 0;
+}
+
+int
+delpart(Umsc *lun, char *s)
+{
+ Part *p;
+
+ p = lookpart(lun, s);
+ if(p == nil || p->id <= Qdata){
+ werrstr("partition not found");
+ return -1;
+ }
+ p->inuse = 0;
+ free(p->name);
+ p->name = nil;
+ p->vers++;
+ return 0;
+}
+
+void
+fixlength(Umsc *lun, vlong blocks)
+{
+ Part *part, *p;
+
+ part = lun->part;
+ part[Qdata].length = blocks;
+ for(p=&part[Qdata+1]; p < &part[Qmax]; p++){
+ if(p->inuse && p->offset + p->length > blocks){
+ if(p->offset > blocks){
+ p->offset =blocks;
+ p->length = 0;
+ }else
+ p->length = blocks - p->offset;
+ }
+ }
+}
+
+void
+makeparts(Umsc *lun)
+{
+ addpart(lun, "/", 0, 0, DMDIR | 0555);
+ addpart(lun, "ctl", 0, 0, 0664);
+ addpart(lun, "raw", 0, 0, 0640);
+ addpart(lun, "data", 0, lun->blocks, 0640);
+}
+
+/*
+ * ctl parsing & formatting (adapted from partfs)
+ */
+
+static char*
+ctlstring(Usbfs *fs)
+{
+ Part *p, *part;
+ Fmt fmt;
+ Umsc *lun;
+ Ums *ums;
+
+ ums = fs->dev->aux;
+ lun = fs->aux;
+ part = &lun->part[0];
+
+ fmtstrinit(&fmt);
+ fmtprint(&fmt, "dev %s\n", fs->dev->dir);
+ fmtprint(&fmt, "lun %ld\n", lun - &ums->lun[0]);
+ if(lun->flags & Finqok)
+ fmtprint(&fmt, "inquiry %s\n", lun->inq);
+ if(lun->blocks > 0)
+ fmtprint(&fmt, "geometry %llud %ld\n", lun->blocks, lun->lbsize);
+ for (p = &part[Qdata+1]; p < &part[Qmax]; p++)
+ if (p->inuse)
+ fmtprint(&fmt, "part %s %lld %lld\n",
+ p->name, p->offset, p->offset + p->length);
+ return fmtstrflush(&fmt);
+}
+
+static int
+ctlparse(Usbfs *fs, char *msg)
+{
+ vlong start, end;
+ char *argv[16];
+ int argc;
+ Umsc *lun;
+
+ lun = fs->aux;
+ argc = tokenize(msg, argv, nelem(argv));
+
+ if(argc < 1){
+ werrstr("empty control message");
+ return -1;
+ }
+
+ if(strcmp(argv[0], "part") == 0){
+ if(argc != 4){
+ werrstr("part takes 3 args");
+ return -1;
+ }
+ start = strtoll(argv[2], 0, 0);
+ end = strtoll(argv[3], 0, 0);
+ return addpart(lun, argv[1], start, end, 0640);
+ }else if(strcmp(argv[0], "delpart") == 0){
+ if(argc != 2){
+ werrstr("delpart takes 1 arg");
+ return -1;
+ }
+ return delpart(lun, argv[1]);
+ }
+ werrstr("unknown ctl");
+ return -1;
+}
+
+/*
+ * These are used by scuzz scsireq
+ */
+int exabyte, force6bytecmds;
+
+int diskdebug;
+
+static void
+ding(void *, char *msg)
+{
+ if(strstr(msg, "alarm") != nil)
+ noted(NCONT);
+ noted(NDFLT);
+}
+
+static int
+getmaxlun(Dev *dev)
+{
+ uchar max;
+ int r;
+
+ max = 0;
+ r = Rd2h|Rclass|Riface;
+ if(usbcmd(dev, r, Getmaxlun, 0, 0, &max, 1) < 0){
+ dprint(2, "disk: %s: getmaxlun failed: %r\n", dev->dir);
+ }else{
+ max &= 017; /* 15 is the max. allowed */
+ dprint(2, "disk: %s: maxlun %d\n", dev->dir, max);
+ }
+ return max;
+}
+
+static int
+umsreset(Ums *ums)
+{
+ int r;
+
+ r = Rh2d|Rclass|Riface;
+ if(usbcmd(ums->dev, r, Umsreset, 0, 0, nil, 0) < 0){
+ fprint(2, "disk: reset: %r\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int
+umsrecover(Ums *ums)
+{
+ if(umsreset(ums) < 0)
+ return -1;
+ if(unstall(ums->dev, ums->epin, Ein) < 0)
+ dprint(2, "disk: unstall epin: %r\n");
+
+ /* do we need this when epin == epout? */
+ if(unstall(ums->dev, ums->epout, Eout) < 0)
+ dprint(2, "disk: unstall epout: %r\n");
+ return 0;
+}
+
+static void
+umsfatal(Ums *ums)
+{
+ int i;
+
+ devctl(ums->dev, "detach");
+ for(i = 0; i < ums->maxlun; i++)
+ usbfsdel(&ums->lun[i].fs);
+}
+
+static int
+ispow2(uvlong ul)
+{
+ return (ul & (ul - 1)) == 0;
+}
+
+/*
+ * return smallest power of 2 >= n
+ */
+static int
+log2(int n)
+{
+ int i;
+
+ for(i = 0; (1 << i) < n; i++)
+ ;
+ return i;
+}
+
+static int
+umscapacity(Umsc *lun)
+{
+ uchar data[32];
+
+ lun->blocks = 0;
+ lun->capacity = 0;
+ lun->lbsize = 0;
+ memset(data, 0, sizeof data);
+ if(SRrcapacity(lun, data) < 0 && SRrcapacity(lun, data) < 0)
+ return -1;
+ lun->blocks = GETBELONG(data);
+ lun->lbsize = GETBELONG(data+4);
+ if(lun->blocks == 0xFFFFFFFF){
+ if(SRrcapacity16(lun, data) < 0){
+ lun->lbsize = 0;
+ lun->blocks = 0;
+ return -1;
+ }else{
+ lun->lbsize = GETBELONG(data + 8);
+ lun->blocks = (uvlong)GETBELONG(data)<<32 |
+ GETBELONG(data + 4);
+ }
+ }
+ lun->blocks++; /* SRcapacity returns LBA of last block */
+ lun->capacity = (vlong)lun->blocks * lun->lbsize;
+ fixlength(lun, lun->blocks);
+ if(diskdebug)
+ fprint(2, "disk: logical block size %lud, # blocks %llud\n",
+ lun->lbsize, lun->blocks);
+ return 0;
+}
+
+static int
+umsinit(Ums *ums)
+{
+ uchar i;
+ Umsc *lun;
+ int some;
+
+ umsreset(ums);
+ ums->maxlun = getmaxlun(ums->dev);
+ ums->lun = mallocz((ums->maxlun+1) * sizeof(*ums->lun), 1);
+ some = 0;
+ for(i = 0; i <= ums->maxlun; i++){
+ lun = &ums->lun[i];
+ lun->ums = ums;
+ lun->umsc = lun;
+ lun->lun = i;
+ lun->flags = Fopen | Fusb | Frw10;
+ if(SRinquiry(lun) < 0 && SRinquiry(lun) < 0){
+ dprint(2, "disk: lun %d inquiry failed\n", i);
+ continue;
+ }
+ switch(lun->inquiry[0]){
+ case Devdir:
+ case Devworm: /* a little different than the others */
+ case Devcd:
+ case Devmo:
+ break;
+ default:
+ fprint(2, "disk: lun %d is not a disk (type %#02x)\n",
+ i, lun->inquiry[0]);
+ continue;
+ }
+ SRstart(lun, 1);
+ /*
+ * we ignore the device type reported by inquiry.
+ * Some devices return a wrong value but would still work.
+ */
+ some++;
+ lun->inq = smprint("%.48s", (char *)lun->inquiry+8);
+ umscapacity(lun);
+ }
+ if(some == 0){
+ dprint(2, "disk: all luns failed\n");
+ devctl(ums->dev, "detach");
+ return -1;
+ }
+ return 0;
+}
+
+
+/*
+ * called by SR*() commands provided by scuzz's scsireq
+ */
+long
+umsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status)
+{
+ Cbw cbw;
+ Csw csw;
+ int n, nio, left;
+ Ums *ums;
+
+ ums = umsc->ums;
+
+ memcpy(cbw.signature, "USBC", 4);
+ cbw.tag = ++ums->seq;
+ cbw.datalen = data->count;
+ cbw.flags = data->write? CbwDataOut: CbwDataIn;
+ cbw.lun = umsc->lun;
+ if(cmd->count < 1 || cmd->count > 16)
+ print("disk: umsrequest: bad cmd count: %ld\n", cmd->count);
+
+ cbw.len = cmd->count;
+ assert(cmd->count <= sizeof(cbw.command));
+ memcpy(cbw.command, cmd->p, cmd->count);
+ memset(cbw.command + cmd->count, 0, sizeof(cbw.command) - cmd->count);
+
+ werrstr(""); /* we use %r later even for n == 0 */
+ if(diskdebug){
+ fprint(2, "disk: cmd: tag %#lx: ", cbw.tag);
+ for(n = 0; n < cbw.len; n++)
+ fprint(2, " %2.2x", cbw.command[n]&0xFF);
+ fprint(2, " datalen: %ld\n", cbw.datalen);
+ }
+
+ /* issue tunnelled scsi command */
+ if(write(ums->epout->dfd, &cbw, CbwLen) != CbwLen){
+ fprint(2, "disk: cmd: %r\n");
+ goto Fail;
+ }
+
+ /* transfer the data */
+ nio = data->count;
+ if(nio != 0){
+ if(data->write)
+ n = write(ums->epout->dfd, data->p, nio);
+ else{
+ n = read(ums->epin->dfd, data->p, nio);
+ left = nio - n;
+ if (n >= 0 && left > 0) /* didn't fill data->p? */
+ memset(data->p + n, 0, left);
+ }
+ nio = n;
+ if(diskdebug)
+ if(n < 0)
+ fprint(2, "disk: data: %r\n");
+ else
+ fprint(2, "disk: data: %d bytes\n", n);
+ if(n <= 0)
+ if(data->write == 0)
+ unstall(ums->dev, ums->epin, Ein);
+ }
+
+ /* read the transfer's status */
+ n = read(ums->epin->dfd, &csw, CswLen);
+ if(n <= 0){
+ /* n == 0 means "stalled" */
+ unstall(ums->dev, ums->epin, Ein);
+ n = read(ums->epin->dfd, &csw, CswLen);
+ }
+
+ if(n != CswLen || strncmp(csw.signature, "USBS", 4) != 0){
+ dprint(2, "disk: read n=%d: status: %r\n", n);
+ goto Fail;
+ }
+ if(csw.tag != cbw.tag){
+ dprint(2, "disk: status tag mismatch\n");
+ goto Fail;
+ }
+ if(csw.status >= CswPhaseErr){
+ dprint(2, "disk: phase error\n");
+ goto Fail;
+ }
+ if(csw.dataresidue == 0 || ums->wrongresidues)
+ csw.dataresidue = data->count - nio;
+ if(diskdebug){
+ fprint(2, "disk: status: %2.2ux residue: %ld\n",
+ csw.status, csw.dataresidue);
+ if(cbw.command[0] == ScmdRsense){
+ fprint(2, "sense data:");
+ for(n = 0; n < data->count - csw.dataresidue; n++)
+ fprint(2, " %2.2x", data->p[n]);
+ fprint(2, "\n");
+ }
+ }
+ switch(csw.status){
+ case CswOk:
+ *status = STok;
+ break;
+ case CswFailed:
+ *status = STcheck;
+ break;
+ default:
+ dprint(2, "disk: phase error\n");
+ goto Fail;
+ }
+ ums->nerrs = 0;
+ return data->count - csw.dataresidue;
+
+Fail:
+ *status = STharderr;
+ if(ums->nerrs++ > 15){
+ fprint(2, "disk: %s: too many errors: device detached\n", ums->dev->dir);
+ umsfatal(ums);
+ }else
+ umsrecover(ums);
+ return -1;
+}
+
+static int
+dwalk(Usbfs *fs, Fid *fid, char *name)
+{
+ Umsc *lun;
+ Part *p;
+
+ lun = fs->aux;
+
+ if((fid->qid.type & QTDIR) == 0){
+ werrstr("walk in non-directory");
+ return -1;
+ }
+ if(strcmp(name, "..") == 0)
+ return 0;
+
+ p = lookpart(lun, name);
+ if(p == nil){
+ werrstr(Enotfound);
+ return -1;
+ }
+ fid->qid.path = p->id | fs->qid;
+ fid->qid.vers = p->vers;
+ fid->qid.type = p->mode >> 24;
+ return 0;
+}
+static int
+dstat(Usbfs *fs, Qid qid, Dir *d);
+
+static void
+dostat(Usbfs *fs, int path, Dir *d)
+{
+ Umsc *lun;
+ Part *p;
+
+ lun = fs->aux;
+ p = &lun->part[path];
+ d->qid.path = path;
+ d->qid.vers = p->vers;
+ d->qid.type =p->mode >> 24;
+ d->mode = p->mode;
+ d->length = (vlong) p->length * lun->lbsize;
+ strecpy(d->name, d->name + Namesz - 1, p->name);
+}
+
+static int
+dirgen(Usbfs *fs, Qid, int n, Dir *d, void*)
+{
+ Umsc *lun;
+ int i;
+
+ lun = fs->aux;
+ for(i = Qctl; i < Qmax; i++){
+ if(lun->part[i].inuse == 0)
+ continue;
+ if(n-- == 0)
+ break;
+ }
+ if(i == Qmax)
+ return -1;
+ dostat(fs, i, d);
+ d->qid.path |= fs->qid;
+ return 0;
+}
+
+static int
+dstat(Usbfs *fs, Qid qid, Dir *d)
+{
+ int path;
+
+ path = qid.path & ~fs->qid;
+ dostat(fs, path, d);
+ d->qid.path |= fs->qid;
+ return 0;
+}
+
+static int
+dopen(Usbfs *fs, Fid *fid, int)
+{
+ ulong path;
+ Umsc *lun;
+
+ path = fid->qid.path & ~fs->qid;
+ lun = fs->aux;
+ switch(path){
+ case Qraw:
+ lun->phase = Pcmd;
+ break;
+ }
+ return 0;
+}
+
+/*
+ * check i/o parameters and compute values needed later.
+ * we shift & mask manually to avoid run-time calls to _divv and _modv,
+ * since we don't need general division nor its cost.
+ */
+static int
+setup(Umsc *lun, Part *p, char *data, int count, vlong offset)
+{
+ long nb, lbsize, lbshift, lbmask;
+ uvlong bno;
+
+ if(count < 0 || lun->lbsize <= 0 && umscapacity(lun) < 0 ||
+ lun->lbsize == 0)
+ return -1;
+ lbsize = lun->lbsize;
+ assert(ispow2(lbsize));
+ lbshift = log2(lbsize);
+ lbmask = lbsize - 1;
+
+ bno = offset >> lbshift; /* offset / lbsize */
+ nb = ((offset + count + lbsize - 1) >> lbshift) - bno;
+
+ if(bno + nb > p->length) /* past end of partition? */
+ nb = p->length - bno;
+ if(nb * lbsize > Maxiosize)
+ nb = Maxiosize / lbsize;
+ lun->nb = nb;
+ if(bno >= p->length || nb == 0)
+ return 0;
+
+ bno += p->offset; /* start of partition */
+ lun->offset = bno;
+ lun->off = offset & lbmask; /* offset % lbsize */
+ if(lun->off == 0 && (count & lbmask) == 0)
+ lun->bufp = data;
+ else
+ /* not transferring full, aligned blocks; need intermediary */
+ lun->bufp = lun->buf;
+ return count;
+}
+
+/*
+ * Upon SRread/SRwrite errors we assume the medium may have changed,
+ * and ask again for the capacity of the media.
+ * BUG: How to proceed to avoid confussing dossrv??
+ */
+static long
+dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
+{
+ long n;
+ ulong path;
+ char buf[64];
+ char *s;
+ Part *p;
+ Umsc *lun;
+ Ums *ums;
+ Qid q;
+
+ q = fid->qid;
+ path = fid->qid.path & ~fs->qid;
+ ums = fs->dev->aux;
+ lun = fs->aux;
+
+ qlock(ums);
+ switch(path){
+ case Qdir:
+ count = usbdirread(fs, q, data, count, offset, dirgen, nil);
+ break;
+ case Qctl:
+ s = ctlstring(fs);
+ count = usbreadbuf(data, count, offset, s, strlen(s));
+ free(s);
+ break;
+ case Qraw:
+ if(lun->lbsize <= 0 && umscapacity(lun) < 0){
+ count = -1;
+ break;
+ }
+ switch(lun->phase){
+ case Pcmd:
+ qunlock(ums);
+ werrstr("phase error");
+ return -1;
+ case Pdata:
+ lun->data.p = data;
+ lun->data.count = count;
+ lun->data.write = 0;
+ count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status);
+ lun->phase = Pstatus;
+ if(count < 0)
+ lun->lbsize = 0; /* medium may have changed */
+ break;
+ case Pstatus:
+ n = snprint(buf, sizeof buf, "%11.0ud ", lun->status);
+ count = usbreadbuf(data, count, 0LL, buf, n);
+ lun->phase = Pcmd;
+ break;
+ }
+ break;
+ case Qdata:
+ default:
+ p = &lun->part[path];
+ if(!p->inuse){
+ count = -1;
+ werrstr(Eperm);
+ break;
+ }
+ count = setup(lun, p, data, count, offset);
+ if (count <= 0)
+ break;
+ n = SRread(lun, lun->bufp, lun->nb * lun->lbsize);
+ if(n < 0){
+ lun->lbsize = 0; /* medium may have changed */
+ count = -1;
+ } else if (lun->bufp == data)
+ count = n;
+ else{
+ /*
+ * if n == lun->nb*lun->lbsize (as expected),
+ * just copy count bytes.
+ */
+ if(lun->off + count > n)
+ count = n - lun->off; /* short read */
+ if(count > 0)
+ memmove(data, lun->bufp + lun->off, count);
+ }
+ break;
+ }
+ qunlock(ums);
+ return count;
+}
+
+static long
+dwrite(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
+{
+ long len, ocount;
+ ulong path;
+ uvlong bno;
+ Ums *ums;
+ Part *p;
+ Umsc *lun;
+ char *s;
+
+ ums = fs->dev->aux;
+ lun = fs->aux;
+ path = fid->qid.path & ~fs->qid;
+
+ qlock(ums);
+ switch(path){
+ case Qdir:
+ count = -1;
+ werrstr(Eperm);
+ break;
+ case Qctl:
+ s = emallocz(count+1, 1);
+ memmove(s, data, count);
+ if(s[count-1] == '\n')
+ s[count-1] = 0;
+ if(ctlparse(fs, s) == -1)
+ count = -1;
+ free(s);
+ break;
+ case Qraw:
+ if(lun->lbsize <= 0 && umscapacity(lun) < 0){
+ count = -1;
+ break;
+ }
+ switch(lun->phase){
+ case Pcmd:
+ if(count != 6 && count != 10){
+ qunlock(ums);
+ werrstr("bad command length");
+ return -1;
+ }
+ memmove(lun->rawcmd, data, count);
+ lun->cmd.p = lun->rawcmd;
+ lun->cmd.count = count;
+ lun->cmd.write = 1;
+ lun->phase = Pdata;
+ break;
+ case Pdata:
+ lun->data.p = data;
+ lun->data.count = count;
+ lun->data.write = 1;
+ count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status);
+ lun->phase = Pstatus;
+ if(count < 0)
+ lun->lbsize = 0; /* medium may have changed */
+ break;
+ case Pstatus:
+ lun->phase = Pcmd;
+ werrstr("phase error");
+ count = -1;
+ break;
+ }
+ break;
+ case Qdata:
+ default:
+ p = &lun->part[path];
+ if(!p->inuse){
+ count = -1;
+ werrstr(Eperm);
+ break;
+ }
+ len = ocount = count;
+ count = setup(lun, p, data, count, offset);
+ if (count <= 0)
+ break;
+ bno = lun->offset;
+ if (lun->bufp == lun->buf) {
+ count = SRread(lun, lun->bufp, lun->nb * lun->lbsize);
+ if(count < 0) {
+ lun->lbsize = 0; /* medium may have changed */
+ break;
+ }
+ /*
+ * if count == lun->nb*lun->lbsize, as expected, just
+ * copy len (the original count) bytes of user data.
+ */
+ if(lun->off + len > count)
+ len = count - lun->off; /* short read */
+ if(len > 0)
+ memmove(lun->bufp + lun->off, data, len);
+ }
+
+ lun->offset = bno;
+ count = SRwrite(lun, lun->bufp, lun->nb * lun->lbsize);
+ if(count < 0)
+ lun->lbsize = 0; /* medium may have changed */
+ else{
+ if(lun->off + len > count)
+ count -= lun->off; /* short write */
+ /* never report more bytes written than requested */
+ if(count < 0)
+ count = 0;
+ else if(count > ocount)
+ count = ocount;
+ }
+ break;
+ }
+ qunlock(ums);
+ return count;
+}
+
+int
+findendpoints(Ums *ums)
+{
+ Ep *ep;
+ Usbdev *ud;
+ ulong csp, sc;
+ int i, epin, epout;
+
+ epin = epout = -1;
+ ud = ums->dev->usb;
+ for(i = 0; i < nelem(ud->ep); i++){
+ if((ep = ud->ep[i]) == nil)
+ continue;
+ csp = ep->iface->csp;
+ sc = Subclass(csp);
+ if(!(Class(csp) == Clstorage && (Proto(csp) == Protobulk)))
+ continue;
+ if(sc != Subatapi && sc != Sub8070 && sc != Subscsi)
+ fprint(2, "disk: subclass %#ulx not supported. trying anyway\n", sc);
+ if(ep->type == Ebulk){
+ if(ep->dir == Eboth || ep->dir == Ein)
+ if(epin == -1)
+ epin = ep->id;
+ if(ep->dir == Eboth || ep->dir == Eout)
+ if(epout == -1)
+ epout = ep->id;
+ }
+ }
+ dprint(2, "disk: ep ids: in %d out %d\n", epin, epout);
+ if(epin == -1 || epout == -1)
+ return -1;
+ ums->epin = openep(ums->dev, epin);
+ if(ums->epin == nil){
+ fprint(2, "disk: openep %d: %r\n", epin);
+ return -1;
+ }
+ if(epout == epin){
+ incref(ums->epin);
+ ums->epout = ums->epin;
+ }else
+ ums->epout = openep(ums->dev, epout);
+ if(ums->epout == nil){
+ fprint(2, "disk: openep %d: %r\n", epout);
+ closedev(ums->epin);
+ return -1;
+ }
+ if(ums->epin == ums->epout)
+ opendevdata(ums->epin, ORDWR);
+ else{
+ opendevdata(ums->epin, OREAD);
+ opendevdata(ums->epout, OWRITE);
+ }
+ if(ums->epin->dfd < 0 || ums->epout->dfd < 0){
+ fprint(2, "disk: open i/o ep data: %r\n");
+ closedev(ums->epin);
+ closedev(ums->epout);
+ return -1;
+ }
+ dprint(2, "disk: ep in %s out %s\n", ums->epin->dir, ums->epout->dir);
+
+ devctl(ums->epin, "timeout 2000");
+ devctl(ums->epout, "timeout 2000");
+
+ if(usbdebug > 1 || diskdebug > 2){
+ devctl(ums->epin, "debug 1");
+ devctl(ums->epout, "debug 1");
+ devctl(ums->dev, "debug 1");
+ }
+ return 0;
+}
+
+static int
+usage(void)
+{
+ werrstr("usage: usb/disk [-d] [-N nb]");
+ return -1;
+}
+
+static void
+umsdevfree(void *a)
+{
+ Ums *ums = a;
+
+ if(ums == nil)
+ return;
+ closedev(ums->epin);
+ closedev(ums->epout);
+ ums->epin = ums->epout = nil;
+ free(ums->lun);
+ free(ums);
+}
+
+static Srv diskfs = {
+ .walk = dwalk,
+ .open = dopen,
+ .read = dread,
+ .write = dwrite,
+ .stat = dstat,
+};
+
+int
+diskmain(Dev *dev, int argc, char **argv)
+{
+ Ums *ums;
+ Umsc *lun;
+ int i, devid;
+
+ devid = dev->id;
+ ARGBEGIN{
+ case 'd':
+ scsidebug(diskdebug);
+ diskdebug++;
+ break;
+ case 'N':
+ devid = atoi(EARGF(usage()));
+ break;
+ default:
+ return usage();
+ }ARGEND
+ if(argc != 0) {
+ return usage();
+ }
+
+// notify(ding);
+ ums = dev->aux = emallocz(sizeof(Ums), 1);
+ ums->maxlun = -1;
+ ums->dev = dev;
+ dev->free = umsdevfree;
+ if(findendpoints(ums) < 0){
+ werrstr("disk: endpoints not found");
+ return -1;
+ }
+
+ /*
+ * SanDISK 512M gets residues wrong.
+ */
+ if(dev->usb->vid == 0x0781 && dev->usb->did == 0x5150)
+ ums->wrongresidues = 1;
+
+ if(umsinit(ums) < 0){
+ dprint(2, "disk: umsinit: %r\n");
+ return -1;
+ }
+
+ for(i = 0; i <= ums->maxlun; i++){
+ lun = &ums->lun[i];
+ lun->fs = diskfs;
+ snprint(lun->fs.name, sizeof(lun->fs.name), "sdU%d.%d", devid, i);
+ lun->fs.dev = dev;
+ incref(dev);
+ lun->fs.aux = lun;
+ makeparts(lun);
+ usbfsadd(&lun->fs);
+ }
+ return 0;
+}
diff --git a/sys/src/cmd/nusb/disk/mkfile b/sys/src/cmd/nusb/disk/mkfile
new file mode 100644
index 000000000..e23c72851
--- /dev/null
+++ b/sys/src/cmd/nusb/disk/mkfile
@@ -0,0 +1,24 @@
+</$objtype/mkfile
+
+TARG=disk
+OFILES=\
+ disk.$O\
+ scsireq.$O\
+ scsierrs.$O\
+
+HFILES =\
+ scsireq.h\
+ ../lib/usb.h\
+ ums.h\
+
+LIB=../lib/usb.a$O
+
+BIN=/$objtype/bin/usb
+
+</sys/src/cmd/mkone
+CFLAGS=-I../lib $CFLAGS
+CLEANFILES=scsierrs.c
+
+scsierrs.c: /sys/lib/scsicodes mkscsierrs
+ mkscsierrs >scsierrs.c
+
diff --git a/sys/src/cmd/nusb/disk/mkscsierrs b/sys/src/cmd/nusb/disk/mkscsierrs
new file mode 100755
index 000000000..a7cc32e5d
--- /dev/null
+++ b/sys/src/cmd/nusb/disk/mkscsierrs
@@ -0,0 +1,32 @@
+#!/bin/rc
+
+cat <<EOF
+#include <u.h>
+#include <libc.h>
+
+typedef struct Err Err;
+struct Err
+{
+ int n;
+ char *s;
+};
+
+static Err scsierrs[] = {
+EOF
+
+grep '^[0-9a-c][0-9a-c][0-9a-c][0-9a-c][ ]' /sys/lib/scsicodes |
+ sed -e 's/^(....) (.*)/ {0x\1, "\2"},\n/'
+cat <<EOF
+};
+
+char*
+scsierrmsg(int n)
+{
+ int i;
+
+ for(i = 0; i < nelem(scsierrs); i++)
+ if(scsierrs[i].n == n)
+ return scsierrs[i].s;
+ return "scsi error";
+}
+EOF
diff --git a/sys/src/cmd/nusb/disk/scsireq.c b/sys/src/cmd/nusb/disk/scsireq.c
new file mode 100644
index 000000000..f9994e286
--- /dev/null
+++ b/sys/src/cmd/nusb/disk/scsireq.c
@@ -0,0 +1,986 @@
+/*
+ * This is /sys/src/cmd/scuzz/scsireq.c
+ * changed to add more debug support, to keep
+ * disk compiling without a scuzz that includes these changes.
+ * Also, this includes minor tweaks for usb:
+ * we set req.lun/unit to rp->lun/unit in SRreqsense
+ * we set the rp->sense[0] bit Sd0valid in SRreqsense
+ * This does not use libdisk to retrieve the scsi error to make
+ * user see the diagnostics if we boot with debug enabled.
+ *
+ * BUGS:
+ * no luns
+ * and incomplete in many other ways
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include "scsireq.h"
+
+enum {
+ Debug = 0,
+};
+
+/*
+ * exabyte tape drives, at least old ones like the 8200 and 8505,
+ * are dumb: you have to read the exact block size on the tape,
+ * they don't take 10-byte SCSI commands, and various other fine points.
+ */
+extern int exabyte, force6bytecmds;
+
+static int debug = Debug;
+
+static char *scmdnames[256] = {
+[ScmdTur] "Tur",
+[ScmdRewind] "Rewind",
+[ScmdRsense] "Rsense",
+[ScmdFormat] "Format",
+[ScmdRblimits] "Rblimits",
+[ScmdRead] "Read",
+[ScmdWrite] "Write",
+[ScmdSeek] "Seek",
+[ScmdFmark] "Fmark",
+[ScmdSpace] "Space",
+[ScmdInq] "Inq",
+[ScmdMselect6] "Mselect6",
+[ScmdMselect10] "Mselect10",
+[ScmdMsense6] "Msense6",
+[ScmdMsense10] "Msense10",
+[ScmdStart] "Start",
+[ScmdRcapacity] "Rcapacity",
+[ScmdRcapacity16] "Rcap16",
+[ScmdExtread] "Extread",
+[ScmdExtwrite] "Extwrite",
+[ScmdExtseek] "Extseek",
+
+[ScmdSynccache] "Synccache",
+[ScmdRTOC] "RTOC",
+[ScmdRdiscinfo] "Rdiscinfo",
+[ScmdRtrackinfo] "Rtrackinfo",
+[ScmdReserve] "Reserve",
+[ScmdBlank] "Blank",
+
+[ScmdCDpause] "CDpause",
+[ScmdCDstop] "CDstop",
+[ScmdCDplay] "CDplay",
+[ScmdCDload] "CDload",
+[ScmdCDscan] "CDscan",
+[ScmdCDstatus] "CDstatus",
+[Scmdgetconf] "getconf",
+};
+
+long
+SRready(ScsiReq *rp)
+{
+ uchar cmd[6];
+
+ memset(cmd, 0, sizeof cmd);
+ rp->cmd.p = cmd;
+ rp->cmd.count = sizeof cmd;
+ rp->data.p = cmd;
+ rp->data.count = 0;
+ rp->data.write = 1;
+ return SRrequest(rp);
+}
+
+long
+SRrewind(ScsiReq *rp)
+{
+ uchar cmd[6];
+
+ memset(cmd, 0, sizeof cmd);
+ cmd[0] = ScmdRewind;
+ rp->cmd.p = cmd;
+ rp->cmd.count = sizeof cmd;
+ rp->data.p = cmd;
+ rp->data.count = 0;
+ rp->data.write = 1;
+ if(SRrequest(rp) >= 0){
+ rp->offset = 0;
+ return 0;
+ }
+ return -1;
+}
+
+long
+SRreqsense(ScsiReq *rp)
+{
+ uchar cmd[6];
+ ScsiReq req;
+ long status;
+
+ if(rp->status == Status_SD){
+ rp->status = STok;
+ return 0;
+ }
+ memset(cmd, 0, sizeof cmd);
+ cmd[0] = ScmdRsense;
+ cmd[4] = sizeof(req.sense);
+ memset(&req, 0, sizeof(req));
+ if(rp->flags&Fusb)
+ req.flags |= Fusb;
+ req.lun = rp->lun;
+ req.unit = rp->unit;
+ req.fd = rp->fd;
+ req.umsc = rp->umsc;
+ req.cmd.p = cmd;
+ req.cmd.count = sizeof cmd;
+ req.data.p = rp->sense;
+ req.data.count = sizeof(rp->sense);
+ req.data.write = 0;
+ status = SRrequest(&req);
+ rp->status = req.status;
+ if(status != -1)
+ rp->sense[0] |= Sd0valid;
+ return status;
+}
+
+long
+SRformat(ScsiReq *rp)
+{
+ uchar cmd[6];
+
+ memset(cmd, 0, sizeof cmd);
+ cmd[0] = ScmdFormat;
+ rp->cmd.p = cmd;
+ rp->cmd.count = sizeof cmd;
+ rp->data.p = cmd;
+ rp->data.count = 6;
+ rp->data.write = 0;
+ return SRrequest(rp);
+}
+
+long
+SRrblimits(ScsiReq *rp, uchar *list)
+{
+ uchar cmd[6];
+
+ memset(cmd, 0, sizeof cmd);
+ cmd[0] = ScmdRblimits;
+ rp->cmd.p = cmd;
+ rp->cmd.count = sizeof cmd;
+ rp->data.p = list;
+ rp->data.count = 6;
+ rp->data.write = 0;
+ return SRrequest(rp);
+}
+
+static int
+dirdevrw(ScsiReq *rp, uchar *cmd, long nbytes)
+{
+ long n;
+
+ n = nbytes / rp->lbsize;
+ if(rp->offset <= Max24off && n <= 256 && (rp->flags & Frw10) == 0){
+ PUTBE24(cmd+1, rp->offset);
+ cmd[4] = n;
+ cmd[5] = 0;
+ return 6;
+ }
+ cmd[0] |= ScmdExtread;
+ cmd[1] = 0;
+ PUTBELONG(cmd+2, rp->offset);
+ cmd[6] = 0;
+ cmd[7] = n>>8;
+ cmd[8] = n;
+ cmd[9] = 0;
+ return 10;
+}
+
+static int
+seqdevrw(ScsiReq *rp, uchar *cmd, long nbytes)
+{
+ long n;
+
+ /* don't set Cmd1sili; we want the ILI bit instead of a fatal error */
+ cmd[1] = rp->flags&Fbfixed? Cmd1fixed: 0;
+ n = nbytes / rp->lbsize;
+ PUTBE24(cmd+2, n);
+ cmd[5] = 0;
+ return 6;
+}
+
+extern int diskdebug;
+
+long
+SRread(ScsiReq *rp, void *buf, long nbytes)
+{
+ uchar cmd[10];
+ long n;
+
+ if(rp->lbsize == 0 || (nbytes % rp->lbsize) || nbytes > Maxiosize){
+ if(diskdebug)
+ if (nbytes % rp->lbsize)
+ fprint(2, "disk: i/o size %ld %% %ld != 0\n",
+ nbytes, rp->lbsize);
+ else
+ fprint(2, "disk: i/o size %ld > %d\n",
+ nbytes, Maxiosize);
+ rp->status = Status_BADARG;
+ return -1;
+ }
+
+ /* set up scsi read cmd */
+ cmd[0] = ScmdRead;
+ if(rp->flags & Fseqdev)
+ rp->cmd.count = seqdevrw(rp, cmd, nbytes);
+ else
+ rp->cmd.count = dirdevrw(rp, cmd, nbytes);
+ rp->cmd.p = cmd;
+ rp->data.p = buf;
+ rp->data.count = nbytes;
+ rp->data.write = 0;
+
+ /* issue it */
+ n = SRrequest(rp);
+ if(n != -1){ /* it worked? */
+ rp->offset += n / rp->lbsize;
+ return n;
+ }
+
+ /* request failed; maybe we just read a short record? */
+ if (exabyte) {
+ fprint(2, "read error\n");
+ rp->status = STcheck;
+ return n;
+ }
+ if(rp->status != Status_SD || !(rp->sense[0] & Sd0valid))
+ return -1;
+ /* compute # of bytes not read */
+ n = GETBELONG(rp->sense+3) * rp->lbsize;
+ if(!(rp->flags & Fseqdev))
+ return -1;
+
+ /* device is a tape or something similar */
+ if (rp->sense[2] == Sd2filemark || rp->sense[2] == 0x08 ||
+ rp->sense[2] & Sd2ili && n > 0)
+ rp->data.count = nbytes - n;
+ else
+ return -1;
+ n = rp->data.count;
+ if (!rp->readblock++ || debug)
+ fprint(2, "SRread: tape data count %ld%s\n", n,
+ (rp->sense[2] & Sd2ili? " with ILI": ""));
+ rp->status = STok;
+ rp->offset += n / rp->lbsize;
+ return n;
+}
+
+long
+SRwrite(ScsiReq *rp, void *buf, long nbytes)
+{
+ uchar cmd[10];
+ long n;
+
+ if(rp->lbsize == 0 || (nbytes % rp->lbsize) || nbytes > Maxiosize){
+ if(diskdebug)
+ if (nbytes % rp->lbsize)
+ fprint(2, "disk: i/o size %ld %% %ld != 0\n",
+ nbytes, rp->lbsize);
+ else
+ fprint(2, "disk: i/o size %ld > %d\n",
+ nbytes, Maxiosize);
+ rp->status = Status_BADARG;
+ return -1;
+ }
+
+ /* set up scsi write cmd */
+ cmd[0] = ScmdWrite;
+ if(rp->flags & Fseqdev)
+ rp->cmd.count = seqdevrw(rp, cmd, nbytes);
+ else
+ rp->cmd.count = dirdevrw(rp, cmd, nbytes);
+ rp->cmd.p = cmd;
+ rp->data.p = buf;
+ rp->data.count = nbytes;
+ rp->data.write = 1;
+
+ /* issue it */
+ if((n = SRrequest(rp)) == -1){
+ if (exabyte) {
+ fprint(2, "write error\n");
+ rp->status = STcheck;
+ return n;
+ }
+ if(rp->status != Status_SD || rp->sense[2] != Sd2eom)
+ return -1;
+ if(rp->sense[0] & Sd0valid){
+ n -= GETBELONG(rp->sense+3) * rp->lbsize;
+ rp->data.count = nbytes - n;
+ }
+ else
+ rp->data.count = nbytes;
+ n = rp->data.count;
+ }
+ rp->offset += n / rp->lbsize;
+ return n;
+}
+
+long
+SRseek(ScsiReq *rp, long offset, int type)
+{
+ uchar cmd[10];
+
+ switch(type){
+
+ case 0:
+ break;
+
+ case 1:
+ offset += rp->offset;
+ if(offset >= 0)
+ break;
+ /*FALLTHROUGH*/
+
+ default:
+ if(diskdebug)
+ fprint(2, "disk: seek failed\n");
+ rp->status = Status_BADARG;
+ return -1;
+ }
+ memset(cmd, 0, sizeof cmd);
+ if(offset <= Max24off && (rp->flags & Frw10) == 0){
+ cmd[0] = ScmdSeek;
+ PUTBE24(cmd+1, offset & Max24off);
+ rp->cmd.count = 6;
+ }else{
+ cmd[0] = ScmdExtseek;
+ PUTBELONG(cmd+2, offset);
+ rp->cmd.count = 10;
+ }
+ rp->cmd.p = cmd;
+ rp->data.p = cmd;
+ rp->data.count = 0;
+ rp->data.write = 1;
+ SRrequest(rp);
+ if(rp->status == STok) {
+ rp->offset = offset;
+ return offset;
+ }
+ return -1;
+}
+
+long
+SRfilemark(ScsiReq *rp, ulong howmany)
+{
+ uchar cmd[6];
+
+ memset(cmd, 0, sizeof cmd);
+ cmd[0] = ScmdFmark;
+ PUTBE24(cmd+2, howmany);
+ rp->cmd.p = cmd;
+ rp->cmd.count = sizeof cmd;
+ rp->data.p = cmd;
+ rp->data.count = 0;
+ rp->data.write = 1;
+ return SRrequest(rp);
+}
+
+long
+SRspace(ScsiReq *rp, uchar code, long howmany)
+{
+ uchar cmd[6];
+
+ memset(cmd, 0, sizeof cmd);
+ cmd[0] = ScmdSpace;
+ cmd[1] = code;
+ PUTBE24(cmd+2, howmany);
+ rp->cmd.p = cmd;
+ rp->cmd.count = sizeof cmd;
+ rp->data.p = cmd;
+ rp->data.count = 0;
+ rp->data.write = 1;
+ /*
+ * what about rp->offset?
+ */
+ return SRrequest(rp);
+}
+
+long
+SRinquiry(ScsiReq *rp)
+{
+ uchar cmd[6];
+
+ memset(cmd, 0, sizeof cmd);
+ cmd[0] = ScmdInq;
+ cmd[4] = sizeof rp->inquiry;
+ rp->cmd.p = cmd;
+ rp->cmd.count = sizeof cmd;
+ memset(rp->inquiry, 0, sizeof rp->inquiry);
+ rp->data.p = rp->inquiry;
+ rp->data.count = sizeof rp->inquiry;
+ rp->data.write = 0;
+ if(SRrequest(rp) >= 0){
+ rp->flags |= Finqok;
+ return 0;
+ }
+ rp->flags &= ~Finqok;
+ return -1;
+}
+
+long
+SRmodeselect6(ScsiReq *rp, uchar *list, long nbytes)
+{
+ uchar cmd[6];
+
+ memset(cmd, 0, sizeof cmd);
+ cmd[0] = ScmdMselect6;
+ if((rp->flags & Finqok) && (rp->inquiry[2] & 0x07) >= 2)
+ cmd[1] = 0x10;
+ cmd[4] = nbytes;
+ rp->cmd.p = cmd;
+ rp->cmd.count = sizeof cmd;
+ rp->data.p = list;
+ rp->data.count = nbytes;
+ rp->data.write = 1;
+ return SRrequest(rp);
+}
+
+long
+SRmodeselect10(ScsiReq *rp, uchar *list, long nbytes)
+{
+ uchar cmd[10];
+
+ memset(cmd, 0, sizeof cmd);
+ if((rp->flags & Finqok) && (rp->inquiry[2] & 0x07) >= 2)
+ cmd[1] = 0x10;
+ cmd[0] = ScmdMselect10;
+ cmd[7] = nbytes>>8;
+ cmd[8] = nbytes;
+ rp->cmd.p = cmd;
+ rp->cmd.count = sizeof cmd;
+ rp->data.p = list;
+ rp->data.count = nbytes;
+ rp->data.write = 1;
+ return SRrequest(rp);
+}
+
+long
+SRmodesense6(ScsiReq *rp, uchar page, uchar *list, long nbytes)
+{
+ uchar cmd[6];
+
+ memset(cmd, 0, sizeof cmd);
+ cmd[0] = ScmdMsense6;
+ cmd[2] = page;
+ cmd[4] = nbytes;
+ rp->cmd.p = cmd;
+ rp->cmd.count = sizeof cmd;
+ rp->data.p = list;
+ rp->data.count = nbytes;
+ rp->data.write = 0;
+ return SRrequest(rp);
+}
+
+long
+SRmodesense10(ScsiReq *rp, uchar page, uchar *list, long nbytes)
+{
+ uchar cmd[10];
+
+ memset(cmd, 0, sizeof cmd);
+ cmd[0] = ScmdMsense10;
+ cmd[2] = page;
+ cmd[7] = nbytes>>8;
+ cmd[8] = nbytes;
+ rp->cmd.p = cmd;
+ rp->cmd.count = sizeof cmd;
+ rp->data.p = list;
+ rp->data.count = nbytes;
+ rp->data.write = 0;
+ return SRrequest(rp);
+}
+
+long
+SRstart(ScsiReq *rp, uchar code)
+{
+ uchar cmd[6];
+
+ memset(cmd, 0, sizeof cmd);
+ cmd[0] = ScmdStart;
+ cmd[4] = code;
+ rp->cmd.p = cmd;
+ rp->cmd.count = sizeof cmd;
+ rp->data.p = cmd;
+ rp->data.count = 0;
+ rp->data.write = 1;
+ return SRrequest(rp);
+}
+
+long
+SRrcapacity(ScsiReq *rp, uchar *data)
+{
+ uchar cmd[10];
+
+ memset(cmd, 0, sizeof cmd);
+ cmd[0] = ScmdRcapacity;
+ rp->cmd.p = cmd;
+ rp->cmd.count = sizeof cmd;
+ rp->data.p = data;
+ rp->data.count = 8;
+ rp->data.write = 0;
+ return SRrequest(rp);
+}
+
+long
+SRrcapacity16(ScsiReq *rp, uchar *data)
+{
+ uchar cmd[16];
+ uint i;
+
+ i = 32;
+ memset(cmd, 0, sizeof cmd);
+ cmd[0] = ScmdRcapacity16;
+ cmd[1] = 0x10;
+ cmd[10] = i>>24;
+ cmd[11] = i>>16;
+ cmd[12] = i>>8;
+ cmd[13] = i;
+
+ rp->cmd.p = cmd;
+ rp->cmd.count = sizeof cmd;
+ rp->data.p = data;
+ rp->data.count = i;
+ rp->data.write = 0;
+ return SRrequest(rp);
+}
+
+void
+scsidebug(int d)
+{
+ debug = d;
+ if(debug)
+ fprint(2, "scsidebug on\n");
+}
+
+static long
+request(int fd, ScsiPtr *cmd, ScsiPtr *data, int *status)
+{
+ long n, r;
+ char buf[16];
+
+ /* this was an experiment but it seems to be a good idea */
+ *status = STok;
+
+ /* send SCSI command */
+ if(write(fd, cmd->p, cmd->count) != cmd->count){
+ fprint(2, "scsireq: write cmd: %r\n");
+ *status = Status_SW;
+ return -1;
+ }
+
+ /* read or write actual data */
+ werrstr("");
+// alarm(5*1000);
+ if(data->write)
+ n = write(fd, data->p, data->count);
+ else {
+ n = read(fd, data->p, data->count);
+ if (n < 0)
+ memset(data->p, 0, data->count);
+ else if (n < data->count)
+ memset(data->p + n, 0, data->count - n);
+ }
+// alarm(0);
+ if (n != data->count && n <= 0) {
+ if (debug)
+ fprint(2,
+ "request: tried to %s %ld bytes of data for cmd 0x%x but got %r\n",
+ (data->write? "write": "read"),
+ data->count, cmd->p[0]);
+ } else if (n != data->count && (data->write || debug))
+ fprint(2, "request: %s %ld of %ld bytes of actual data\n",
+ (data->write? "wrote": "read"), n, data->count);
+
+ /* read status */
+ buf[0] = '\0';
+ r = read(fd, buf, sizeof buf-1);
+ if(exabyte && r <= 0 || !exabyte && r < 0){
+ fprint(2, "scsireq: read status: %r\n");
+ *status = Status_SW;
+ return -1;
+ }
+ if (r >= 0)
+ buf[r] = '\0';
+ *status = atoi(buf);
+ if(n < 0 && (exabyte || *status != STcheck))
+ fprint(2, "scsireq: status 0x%2.2uX: data transfer: %r\n",
+ *status);
+ return n;
+}
+
+static char*
+seprintcmd(char *s, char* e, char *cmd, int count, int args)
+{
+ uint c;
+
+ if(count < 6)
+ return seprint(s, e, "<short cmd>");
+ c = cmd[0];
+ if(scmdnames[c] != nil)
+ s = seprint(s, e, "%s", scmdnames[c]);
+ else
+ s = seprint(s, e, "cmd:%#02uX", c);
+ if(args != 0)
+ switch(c){
+ case ScmdRsense:
+ case ScmdInq:
+ case ScmdMselect6:
+ case ScmdMsense6:
+ s = seprint(s, e, " sz %d", cmd[4]);
+ break;
+ case ScmdSpace:
+ s = seprint(s, e, " code %d", cmd[1]);
+ break;
+ case ScmdStart:
+ s = seprint(s, e, " code %d", cmd[4]);
+ break;
+
+ }
+ return s;
+}
+
+static char*
+seprintdata(char *s, char *se, uchar *p, int count)
+{
+ int i;
+
+ if(count == 0)
+ return s;
+ for(i = 0; i < 20 && i < count; i++)
+ s = seprint(s, se, " %02x", p[i]);
+ return s;
+}
+
+static void
+SRdumpReq(ScsiReq *rp)
+{
+ char buf[128];
+ char *s;
+ char *se;
+
+ se = buf+sizeof(buf);
+ s = seprint(buf, se, "lun %d ", rp->lun);
+ s = seprintcmd(s, se, (char*)rp->cmd.p, rp->cmd.count, 1);
+ s = seprint(s, se, " [%ld]", rp->data.count);
+ if(rp->cmd.write)
+ seprintdata(s, se, rp->data.p, rp->data.count);
+ fprint(2, "scsi⇒ %s\n", buf);
+}
+
+static void
+SRdumpRep(ScsiReq *rp)
+{
+ char buf[128];
+ char *s;
+ char *se;
+
+ se = buf+sizeof(buf);
+ s = seprint(buf, se, "lun %d ", rp->lun);
+ s = seprintcmd(s, se, (char*)rp->cmd.p, rp->cmd.count, 0);
+ switch(rp->status){
+ case STok:
+ s = seprint(s, se, " good [%ld] ", rp->data.count);
+ if(rp->cmd.write == 0)
+ s = seprintdata(s, se, rp->data.p, rp->data.count);
+ break;
+ case STnomem:
+ s = seprint(s, se, " buffer allocation failed");
+ break;
+ case STharderr:
+ s = seprint(s, se, " controller error");
+ break;
+ case STtimeout:
+ s = seprint(s, se, " bus timeout");
+ break;
+ case STcheck:
+ s = seprint(s, se, " check condition");
+ break;
+ case STcondmet:
+ s = seprint(s, se, " condition met/good");
+ break;
+ case STbusy:
+ s = seprint(s, se, " busy");
+ break;
+ case STintok:
+ s = seprint(s, se, " intermediate/good");
+ break;
+ case STintcondmet:
+ s = seprint(s, se, " intermediate/condition met/good");
+ break;
+ case STresconf:
+ s = seprint(s, se, " reservation conflict");
+ break;
+ case STterminated:
+ s = seprint(s, se, " command terminated");
+ break;
+ case STqfull:
+ s = seprint(s, se, " queue full");
+ break;
+ default:
+ s = seprint(s, se, " sts=%#x", rp->status);
+ }
+ USED(s);
+ fprint(2, "scsi← %s\n", buf);
+}
+
+static char*
+scsierr(ScsiReq *rp)
+{
+ int ec;
+
+ switch(rp->status){
+ case 0:
+ return "";
+ case Status_SD:
+ ec = (rp->sense[12] << 8) | rp->sense[13];
+ return scsierrmsg(ec);
+ case Status_SW:
+ return "software error";
+ case Status_BADARG:
+ return "bad argument";
+ case Status_RO:
+ return "device is read only";
+ default:
+ return "unknown";
+ }
+}
+
+static void
+SRdumpErr(ScsiReq *rp)
+{
+ char buf[128];
+ char *se;
+
+ se = buf+sizeof(buf);
+ seprintcmd(buf, se, (char*)rp->cmd.p, rp->cmd.count, 0);
+ print("\t%s status: %s\n", buf, scsierr(rp));
+}
+
+long
+SRrequest(ScsiReq *rp)
+{
+ long n;
+ int status;
+
+retry:
+ if(debug)
+ SRdumpReq(rp);
+ if(rp->flags&Fusb)
+ n = umsrequest(rp->umsc, &rp->cmd, &rp->data, &status);
+ else
+ n = request(rp->fd, &rp->cmd, &rp->data, &status);
+ rp->status = status;
+ if(status == STok)
+ rp->data.count = n;
+ if(debug)
+ SRdumpRep(rp);
+ switch(status){
+ case STok:
+ break;
+ case STcheck:
+ if(rp->cmd.p[0] != ScmdRsense && SRreqsense(rp) != -1)
+ rp->status = Status_SD;
+ if(debug || exabyte)
+ SRdumpErr(rp);
+ werrstr("%s", scsierr(rp));
+ return -1;
+ case STbusy:
+ sleep(1000); /* TODO: try a shorter sleep? */
+ goto retry;
+ default:
+ if(debug || exabyte)
+ SRdumpErr(rp);
+ werrstr("%s", scsierr(rp));
+ return -1;
+ }
+ return n;
+}
+
+int
+SRclose(ScsiReq *rp)
+{
+ if((rp->flags & Fopen) == 0){
+ if(diskdebug)
+ fprint(2, "disk: closing closed file\n");
+ rp->status = Status_BADARG;
+ return -1;
+ }
+ close(rp->fd);
+ rp->flags = 0;
+ return 0;
+}
+
+static int
+dirdevopen(ScsiReq *rp)
+{
+ uvlong blocks;
+ uchar data[8+4+20]; /* 16-byte result: lba, blksize, reserved */
+
+ memset(data, 0, sizeof data);
+ if(SRstart(rp, 1) == -1 || SRrcapacity(rp, data) == -1)
+ return -1;
+ rp->lbsize = GETBELONG(data+4);
+ blocks = GETBELONG(data);
+ if(debug)
+ fprint(2, "disk: dirdevopen: 10-byte logical block size %lud, "
+ "# blocks %llud\n", rp->lbsize, blocks);
+ if(blocks == 0xffffffff){
+ if(SRrcapacity16(rp, data) == -1)
+ return -1;
+ rp->lbsize = GETBELONG(data + 8);
+ blocks = (vlong)GETBELONG(data)<<32 | GETBELONG(data + 4);
+ if(debug)
+ fprint(2, "disk: dirdevopen: 16-byte logical block size"
+ " %lud, # blocks %llud\n", rp->lbsize, blocks);
+ }
+ /* some newer dev's don't support 6-byte commands */
+ if(blocks > Max24off && !force6bytecmds)
+ rp->flags |= Frw10;
+ return 0;
+}
+
+static int
+seqdevopen(ScsiReq *rp)
+{
+ uchar mode[16], limits[6];
+
+ if(SRrblimits(rp, limits) == -1)
+ return -1;
+ if(limits[1] == 0 && limits[2] == limits[4] && limits[3] == limits[5]){
+ rp->flags |= Fbfixed;
+ rp->lbsize = limits[4]<<8 | limits[5];
+ if(debug)
+ fprint(2, "disk: seqdevopen: 10-byte logical block size %lud\n",
+ rp->lbsize);
+ return 0;
+ }
+ /*
+ * On some older hardware the optional 10-byte
+ * modeselect command isn't implemented.
+ */
+ if (force6bytecmds)
+ rp->flags |= Fmode6;
+ if(!(rp->flags & Fmode6)){
+ /* try 10-byte command first */
+ memset(mode, 0, sizeof mode);
+ mode[3] = 0x10; /* device-specific param. */
+ mode[7] = 8; /* block descriptor length */
+ /*
+ * exabytes can't handle this, and
+ * modeselect(10) is optional.
+ */
+ if(SRmodeselect10(rp, mode, sizeof mode) != -1){
+ rp->lbsize = 1;
+ return 0; /* success */
+ }
+ /* can't do 10-byte commands, back off to 6-byte ones */
+ rp->flags |= Fmode6;
+ }
+
+ /* 6-byte command */
+ memset(mode, 0, sizeof mode);
+ mode[2] = 0x10; /* device-specific param. */
+ mode[3] = 8; /* block descriptor length */
+ /*
+ * bsd sez exabytes need this bit (NBE: no busy enable) in
+ * vendor-specific page (0), but so far we haven't needed it.
+ mode[12] |= 8;
+ */
+ if(SRmodeselect6(rp, mode, 4+8) == -1)
+ return -1;
+ rp->lbsize = 1;
+ return 0;
+}
+
+static int
+wormdevopen(ScsiReq *rp)
+{
+ long status;
+ uchar list[MaxDirData];
+
+ if (SRstart(rp, 1) == -1 ||
+ (status = SRmodesense10(rp, Allmodepages, list, sizeof list)) == -1)
+ return -1;
+ /* nbytes = list[0]<<8 | list[1]; */
+
+ /* # of bytes of block descriptors of 8 bytes each; not even 1? */
+ if((list[6]<<8 | list[7]) < 8)
+ rp->lbsize = 2048;
+ else
+ /* last 3 bytes of block 0 descriptor */
+ rp->lbsize = GETBE24(list+13);
+ if(debug)
+ fprint(2, "disk: wormdevopen: 10-byte logical block size %lud\n",
+ rp->lbsize);
+ return status;
+}
+
+int
+SRopenraw(ScsiReq *rp, char *unit)
+{
+ char name[128];
+
+ if(rp->flags & Fopen){
+ if(diskdebug)
+ fprint(2, "disk: opening open file\n");
+ rp->status = Status_BADARG;
+ return -1;
+ }
+ memset(rp, 0, sizeof *rp);
+ rp->unit = unit;
+
+ snprint(name, sizeof name, "%s/raw", unit);
+ if((rp->fd = open(name, ORDWR)) == -1){
+ rp->status = STtimeout;
+ return -1;
+ }
+ rp->flags = Fopen;
+ return 0;
+}
+
+int
+SRopen(ScsiReq *rp, char *unit)
+{
+ if(SRopenraw(rp, unit) == -1)
+ return -1;
+ SRready(rp);
+ if(SRinquiry(rp) >= 0){
+ switch(rp->inquiry[0]){
+
+ default:
+ fprint(2, "unknown device type 0x%.2x\n", rp->inquiry[0]);
+ rp->status = Status_SW;
+ break;
+
+ case Devdir:
+ case Devcd:
+ case Devmo:
+ if(dirdevopen(rp) == -1)
+ break;
+ return 0;
+
+ case Devseq:
+ rp->flags |= Fseqdev;
+ if(seqdevopen(rp) == -1)
+ break;
+ return 0;
+
+ case Devprint:
+ rp->flags |= Fprintdev;
+ return 0;
+
+ case Devworm:
+ rp->flags |= Fwormdev;
+ if(wormdevopen(rp) == -1)
+ break;
+ return 0;
+
+ case Devjuke:
+ rp->flags |= Fchanger;
+ return 0;
+ }
+ }
+ SRclose(rp);
+ return -1;
+}
diff --git a/sys/src/cmd/nusb/disk/scsireq.h b/sys/src/cmd/nusb/disk/scsireq.h
new file mode 100644
index 000000000..0fbd2b938
--- /dev/null
+++ b/sys/src/cmd/nusb/disk/scsireq.h
@@ -0,0 +1,238 @@
+/*
+ * This is /sys/src/cmd/scuzz/scsireq.h
+ * changed to add more debug support, and to keep
+ * disk compiling without a scuzz that includes these changes.
+ *
+ * scsireq.h is also included by usb/disk and cdfs.
+ */
+typedef struct Umsc Umsc;
+#pragma incomplete Umsc
+
+enum { /* fundamental constants/defaults */
+ MaxDirData = 255, /* max. direct data returned */
+ /*
+ * Because we are accessed via devmnt, we can never get i/o counts
+ * larger than 8216 (Msgsize and devmnt's offered iounit) - 24
+ * (IOHDRSZ) = 8K.
+ */
+ Maxiosize = 8216 - IOHDRSZ, /* max. I/O transfer size */
+};
+
+typedef struct {
+ uchar *p;
+ long count;
+ uchar write;
+} ScsiPtr;
+
+typedef struct {
+ int flags;
+ char *unit; /* unit directory */
+ int lun;
+ ulong lbsize;
+ uvlong offset; /* in blocks of lbsize bytes */
+ int fd;
+ Umsc *umsc; /* lun */
+ ScsiPtr cmd;
+ ScsiPtr data;
+ int status; /* returned status */
+ uchar sense[MaxDirData]; /* returned sense data */
+ uchar inquiry[MaxDirData]; /* returned inquiry data */
+ int readblock; /* flag: read a block since open */
+} ScsiReq;
+
+enum { /* software flags */
+ Fopen = 0x0001, /* open */
+ Fseqdev = 0x0002, /* sequential-access device */
+ Fwritten = 0x0004, /* device written */
+ Fronly = 0x0008, /* device is read-only */
+ Fwormdev = 0x0010, /* write-once read-multiple device */
+ Fprintdev = 0x0020, /* printer */
+ Fbfixed = 0x0040, /* fixed block size */
+ Fchanger = 0x0080, /* medium-changer device */
+ Finqok = 0x0100, /* inquiry data is OK */
+ Fmode6 = 0x0200, /* use 6-byte modeselect */
+ Frw10 = 0x0400, /* use 10-byte read/write */
+ Fusb = 0x0800, /* USB transparent scsi */
+};
+
+enum {
+ STnomem =-4, /* buffer allocation failed */
+ STharderr =-3, /* controller error of some kind */
+ STtimeout =-2, /* bus timeout */
+ STok = 0, /* good */
+ STcheck = 0x02, /* check condition */
+ STcondmet = 0x04, /* condition met/good */
+ STbusy = 0x08, /* busy */
+ STintok = 0x10, /* intermediate/good */
+ STintcondmet = 0x14, /* intermediate/condition met/good */
+ STresconf = 0x18, /* reservation conflict */
+ STterminated = 0x22, /* command terminated */
+ STqfull = 0x28, /* queue full */
+};
+
+enum { /* status */
+ Status_SD = 0x80, /* sense-data available */
+ Status_SW = 0x83, /* internal software error */
+ Status_BADARG = 0x84, /* bad argument to request */
+ Status_RO = 0x85, /* device is read-only */
+};
+
+enum { /* SCSI command codes */
+ ScmdTur = 0x00, /* test unit ready */
+ ScmdRewind = 0x01, /* rezero/rewind */
+ ScmdRsense = 0x03, /* request sense */
+ ScmdFormat = 0x04, /* format unit */
+ ScmdRblimits = 0x05, /* read block limits */
+ ScmdRead = 0x08, /* read */
+ ScmdWrite = 0x0A, /* write */
+ ScmdSeek = 0x0B, /* seek */
+ ScmdFmark = 0x10, /* write filemarks */
+ ScmdSpace = 0x11, /* space forward/backward */
+ ScmdInq = 0x12, /* inquiry */
+ ScmdMselect6 = 0x15, /* mode select */
+ ScmdMselect10 = 0x55, /* mode select */
+ ScmdMsense6 = 0x1A, /* mode sense */
+ ScmdMsense10 = 0x5A, /* mode sense */
+ ScmdStart = 0x1B, /* start/stop unit */
+ ScmdRcapacity = 0x25, /* read capacity */
+ ScmdRcapacity16 = 0x9e, /* long read capacity */
+ ScmdExtread = 0x28, /* extended read */
+ ScmdExtwrite = 0x2A, /* extended write */
+ ScmdExtseek = 0x2B, /* extended seek */
+
+ ScmdSynccache = 0x35, /* flush cache */
+ ScmdRTOC = 0x43, /* read TOC data */
+ ScmdRdiscinfo = 0x51, /* read disc information */
+ ScmdRtrackinfo = 0x52, /* read track information */
+ ScmdReserve = 0x53, /* reserve track */
+ ScmdBlank = 0xA1, /* blank *-RW media */
+
+ ScmdCDpause = 0x4B, /* pause/resume */
+ ScmdCDstop = 0x4E, /* stop play/scan */
+ ScmdCDplay = 0xA5, /* play audio */
+ ScmdCDload = 0xA6, /* load/unload */
+ ScmdCDscan = 0xBA, /* fast forward/reverse */
+ ScmdCDstatus = 0xBD, /* mechanism status */
+ Scmdgetconf = 0x46, /* get configuration */
+
+ ScmdEInitialise = 0x07, /* initialise element status */
+ ScmdMMove = 0xA5, /* move medium */
+ ScmdEStatus = 0xB8, /* read element status */
+ ScmdMExchange = 0xA6, /* exchange medium */
+ ScmdEposition = 0x2B, /* position to element */
+
+ ScmdReadDVD = 0xAD, /* read dvd structure */
+ ScmdReportKey = 0xA4, /* read dvd key */
+ ScmdSendKey = 0xA3, /* write dvd key */
+
+ ScmdClosetracksess= 0x5B,
+ ScmdRead12 = 0xA8,
+ ScmdSetcdspeed = 0xBB,
+ ScmdReadcd = 0xBE,
+
+ /* vendor-specific */
+ ScmdFwaddr = 0xE2, /* first writeable address */
+ ScmdTreserve = 0xE4, /* reserve track */
+ ScmdTinfo = 0xE5, /* read track info */
+ ScmdTwrite = 0xE6, /* write track */
+ ScmdMload = 0xE7, /* medium load/unload */
+ ScmdFixation = 0xE9, /* fixation */
+};
+
+enum {
+ /* sense data byte 0 */
+ Sd0valid = 0x80, /* valid sense data present */
+
+ /* sense data byte 2 */
+ /* incorrect-length indicator, difference in bytes 3—6 */
+ Sd2ili = 0x20,
+ Sd2eom = 0x40, /* end of medium (tape) */
+ Sd2filemark = 0x80, /* at a filemark (tape) */
+
+ /* command byte 1 */
+ Cmd1fixed = 1, /* use fixed-length blocks */
+ Cmd1sili = 2, /* don't set Sd2ili */
+
+ /* limit of block #s in 24-bit ccbs */
+ Max24off = (1<<21) - 1, /* 2⁲ⁱ - 1 */
+
+ /* mode pages */
+ Allmodepages = 0x3F,
+};
+
+/* scsi device types, from the scsi standards */
+enum {
+ Devdir, /* usually disk */
+ Devseq, /* usually tape */
+ Devprint,
+ Dev3,
+ Devworm, /* also direct, but special */
+ Devcd, /* also direct */
+ Dev6,
+ Devmo, /* also direct */
+ Devjuke,
+};
+
+/* p arguments should be of type uchar* */
+#define GETBELONG(p) ((ulong)(p)[0]<<24 | (ulong)(p)[1]<<16 | (p)[2]<<8 | (p)[3])
+#define PUTBELONG(p, ul) ((p)[0] = (ul)>>24, (p)[1] = (ul)>>16, \
+ (p)[2] = (ul)>>8, (p)[3] = (ul))
+#define GETBE24(p) ((ulong)(p)[0]<<16 | (p)[1]<<8 | (p)[2])
+#define PUTBE24(p, ul) ((p)[0] = (ul)>>16, (p)[1] = (ul)>>8, (p)[2] = (ul))
+
+long SRready(ScsiReq*);
+long SRrewind(ScsiReq*);
+long SRreqsense(ScsiReq*);
+long SRformat(ScsiReq*);
+long SRrblimits(ScsiReq*, uchar*);
+long SRread(ScsiReq*, void*, long);
+long SRwrite(ScsiReq*, void*, long);
+long SRseek(ScsiReq*, long, int);
+long SRfilemark(ScsiReq*, ulong);
+long SRspace(ScsiReq*, uchar, long);
+long SRinquiry(ScsiReq*);
+long SRmodeselect6(ScsiReq*, uchar*, long);
+long SRmodeselect10(ScsiReq*, uchar*, long);
+long SRmodesense6(ScsiReq*, uchar, uchar*, long);
+long SRmodesense10(ScsiReq*, uchar, uchar*, long);
+long SRstart(ScsiReq*, uchar);
+long SRrcapacity(ScsiReq*, uchar*);
+long SRrcapacity16(ScsiReq*, uchar*);
+
+long SRblank(ScsiReq*, uchar, uchar); /* MMC CD-R/CD-RW commands */
+long SRsynccache(ScsiReq*);
+long SRTOC(ScsiReq*, void*, int, uchar, uchar);
+long SRrdiscinfo(ScsiReq*, void*, int);
+long SRrtrackinfo(ScsiReq*, void*, int, int);
+
+long SRcdpause(ScsiReq*, int); /* MMC CD audio commands */
+long SRcdstop(ScsiReq*);
+long SRcdload(ScsiReq*, int, int);
+long SRcdplay(ScsiReq*, int, long, long);
+long SRcdstatus(ScsiReq*, uchar*, int);
+long SRgetconf(ScsiReq*, uchar*, int);
+
+/* old CD-R/CD-RW commands */
+long SRfwaddr(ScsiReq*, uchar, uchar, uchar, uchar*);
+long SRtreserve(ScsiReq*, long);
+long SRtinfo(ScsiReq*, uchar, uchar*);
+long SRwtrack(ScsiReq*, void*, long, uchar, uchar);
+long SRmload(ScsiReq*, uchar);
+long SRfixation(ScsiReq*, uchar);
+
+long SReinitialise(ScsiReq*); /* CHANGER commands */
+long SRestatus(ScsiReq*, uchar, uchar*, int);
+long SRmmove(ScsiReq*, int, int, int, int);
+
+long SRrequest(ScsiReq*);
+int SRclose(ScsiReq*);
+int SRopenraw(ScsiReq*, char*);
+int SRopen(ScsiReq*, char*);
+
+void makesense(ScsiReq*);
+
+long umsrequest(struct Umsc*, ScsiPtr*, ScsiPtr*, int*);
+
+void scsidebug(int);
+
+char* scsierrmsg(int n);
diff --git a/sys/src/cmd/nusb/disk/ums.h b/sys/src/cmd/nusb/disk/ums.h
new file mode 100644
index 000000000..9598b9270
--- /dev/null
+++ b/sys/src/cmd/nusb/disk/ums.h
@@ -0,0 +1,124 @@
+/*
+ * mass storage transport protocols and subclasses,
+ * from usb mass storage class specification overview rev 1.2
+ */
+
+typedef struct Umsc Umsc;
+typedef struct Ums Ums;
+typedef struct Cbw Cbw; /* command block wrapper */
+typedef struct Csw Csw; /* command status wrapper */
+typedef struct Part Part;
+
+enum
+{
+ Protocbi = 0, /* control/bulk/interrupt; mainly floppies */
+ Protocb = 1, /* " with no interrupt; mainly floppies */
+ Protobulk = 0x50, /* bulk only */
+
+ Subrbc = 1, /* reduced blk cmds */
+ Subatapi = 2, /* cd/dvd using sff-8020i or mmc-2 cmd blks */
+ Subqic = 3, /* QIC-157 tapes */
+ Subufi = 4, /* floppy */
+ Sub8070 = 5, /* removable media, atapi-like */
+ Subscsi = 6, /* scsi transparent cmd set */
+ Subisd200 = 7, /* ISD200 ATA */
+ Subdev = 0xff, /* use device's value */
+
+ Umsreset = 0xFF,
+ Getmaxlun = 0xFE,
+
+// Maxlun = 256,
+ Maxlun = 32,
+
+ CMreset = 1,
+
+ Pcmd = 0,
+ Pdata,
+ Pstatus,
+
+ CbwLen = 31,
+ CbwDataIn = 0x80,
+ CbwDataOut = 0x00,
+ CswLen = 13,
+ CswOk = 0,
+ CswFailed = 1,
+ CswPhaseErr = 2,
+
+ Maxparts = 16,
+};
+
+/*
+ * corresponds to a lun.
+ * these are ~600+Maxiosize bytes each; ScsiReq is not tiny.
+ */
+
+struct Part
+{
+ int id;
+ int inuse;
+ int vers;
+ ulong mode;
+ char *name;
+ vlong offset; /* in lbsize units */
+ vlong length; /* in lbsize units */
+};
+
+
+struct Umsc
+{
+ ScsiReq;
+ uvlong blocks;
+ vlong capacity;
+
+ /* from setup */
+ char *bufp;
+ long off; /* offset within a block */
+ long nb; /* byte count */
+
+ /* partitions */
+ Part part[Maxparts];
+
+ uchar rawcmd[10];
+ uchar phase;
+ char *inq;
+ Ums *ums;
+ char buf[Maxiosize];
+};
+
+struct Ums
+{
+ QLock;
+ Dev *dev;
+ Dev *epin;
+ Dev *epout;
+ Umsc *lun;
+ uchar maxlun;
+ int seq;
+ int nerrs;
+ int wrongresidues;
+};
+
+/*
+ * USB transparent SCSI devices
+ */
+struct Cbw
+{
+ char signature[4]; /* "USBC" */
+ long tag;
+ long datalen;
+ uchar flags;
+ uchar lun;
+ uchar len;
+ char command[16];
+};
+
+struct Csw
+{
+ char signature[4]; /* "USBS" */
+ long tag;
+ long dataresidue;
+ uchar status;
+};
+
+
+int diskmain(Dev*, int, char**);