diff options
author | aiju <aiju@phicode.de> | 2011-07-27 20:07:30 +0200 |
---|---|---|
committer | aiju <aiju@phicode.de> | 2011-07-27 20:07:30 +0200 |
commit | 05d09f086fdb76206ab5ff0e26a1e1bc2f34b6a3 (patch) | |
tree | c62905a4a8467110b426843a683136a835c8be14 /sys/src/cmd/nusb/disk | |
parent | 5181f2e576dc6b92dfa7dcaa2f60397b931fbc4a (diff) |
nusb: improved
Diffstat (limited to 'sys/src/cmd/nusb/disk')
-rw-r--r-- | sys/src/cmd/nusb/disk/disk.c | 982 | ||||
-rw-r--r-- | sys/src/cmd/nusb/disk/mkfile | 24 | ||||
-rwxr-xr-x | sys/src/cmd/nusb/disk/mkscsierrs | 32 | ||||
-rw-r--r-- | sys/src/cmd/nusb/disk/scsireq.c | 986 | ||||
-rw-r--r-- | sys/src/cmd/nusb/disk/scsireq.h | 238 | ||||
-rw-r--r-- | sys/src/cmd/nusb/disk/ums.h | 124 |
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**); |