/* * 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,}; Dev *dev;Ums *ums; 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;} intaddpart(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; } p = lookpart(lun, name); if(p != nil){ /* adding identical partition is no-op */ if(p->offset == start && p->length == end - start && p->mode == mode) return 0; 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;} intdelpart(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;} voidfixlength(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; } }} voidmakeparts(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(Umsc *lun){ Part *p, *part; Fmt fmt; part = &lun->part[0]; fmtstrinit(&fmt); if(lun->flags & Finqok) fmtprint(&fmt, "inquiry %s\n", lun->inq); fmtprint(&fmt, "dev %s\n", dev->dir); fmtprint(&fmt, "lun %zd\n", lun - &ums->lun[0]); 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 intctlparse(Umsc *lun, char *msg){ vlong start, end; char *argv[16]; int argc; 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 intgetmaxlun(void){ uchar max; int r; max = 0; r = Rd2h|Rclass|Riface; if(usbcmd(dev, r, Getmaxlun, 0, 0, &max, 1) < 0){ dprint(2, "%s: %s: getmaxlun failed: %r\n", argv0, dev->dir); }else{ max &= 017; /* 15 is the max. allowed */ dprint(2, "%s: %s: maxlun %d\n", argv0, dev->dir, max); } /* 0e8d:1887 does not respond to followup commands without a delay */ sleep(1); return max;} static intumsreset(void){ int r; r = Rh2d|Rclass|Riface; if(usbcmd(dev, r, Umsreset, 0, 0, nil, 0) < 0){ fprint(2, "%s: reset: %r\n", argv0); return -1; } sleep(100); return 0;} static voidumsrecover(void){ if(umsreset() < 0){ /* kill the 9p procs */ postnote(PNGROUP, getpid(), "shutdown"); sysfatal("umsrecover: %r"); } if(unstall(dev, ums->epin, Ein) < 0) dprint(2, "%s: unstall epin: %r\n", argv0); /* do we need this when epin == epout? */ if(unstall(dev, ums->epout, Eout) < 0) dprint(2, "%s: unstall epout: %r\n", argv0);} static intispow2(uvlong ul){ return (ul & (ul - 1)) == 0;} /* * return smallest power of 2 >= n */static intlog2(int n){ int i; for(i = 0; (1 << i) < n; i++) ; return i;} static intumsready(Umsc *lun){ int i; for(i=0; i<3; i++){ if(SRready(lun) != -1) break; if(lun->status != Status_SD) continue; /* * logical unit is in process of becoming ready * or initializing command required */ if(lun->sense[12] == 0x04) if(lun->sense[13] == 0x02 || lun->sense[13] == 0x01) break; /* medium not present */ if(lun->sense[12] == 0x3A) return -1; } /* start unit */ if((lun->inquiry[0] & 0x1F) == 0){ SRstart(lun, 1); sleep(250); } return 0;} static intumscapacity(Umsc *lun){ uchar data[32]; lun->blocks = 0; lun->capacity = 0; lun->lbsize = 0; memset(data, 0, sizeof data); if(umsready(lun) < 0) return -1; if(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, "%s: logical block size %lud, # blocks %llud\n", argv0, lun->lbsize, lun->blocks); return 0;} static intumsinit(void){ uchar i; Umsc *lun; int some; umsreset(); ums->maxlun = getmaxlun(); 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, "%s: lun %d inquiry failed\n", argv0, i); continue; } switch(lun->inquiry[0]){ case Devdir: case Devworm: /* a little different than the others */ case Devcd: case Devmo: break; default: fprint(2, "%s: lun %d is not a disk (type %#02x)\n", argv0, i, lun->inquiry[0]); continue; } /* * 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, "%s: all luns failed\n", argv0); return -1; } return 0;} /* * called by SR*() commands provided by scuzz's scsireq */longumsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status){ Cbw cbw; Csw csw; int n, nio; 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) fprint(2, "%s: umsrequest: bad cmd count: %ld\n", argv0, 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, "%s: cmd: tag %#lx: ", argv0, 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, "%s: cmd: %r\n", argv0); 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); if (n >= 0 && n < nio) /* didn't fill data->p? */ memset(data->p + n, 0, nio - n); } if(diskdebug) if(n < 0) fprint(2, "%s: data: %r\n", argv0); else fprint(2, "%s: data: %d bytes (nio: %d)\n", argv0, n, nio); nio = n; if(n <= 0){ nio = 0; if(data->write == 0) unstall(dev, ums->epin, Ein); } } Again: /* read the transfer's status */ n = read(ums->epin->dfd, &csw, CswLen); if(n <= 0){ /* n == 0 means "stalled" */ unstall(dev, ums->epin, Ein); n = read(ums->epin->dfd, &csw, CswLen); } if(n != CswLen || strncmp(csw.signature, "USBS", 4) != 0){ dprint(2, "%s: read n=%d: status: %r\n", argv0, n); goto Fail; } if(csw.tag != cbw.tag){ dprint(2, "%s: status tag mismatch: %lux != %lux\n", argv0, csw.tag, cbw.tag); goto Again; } if(csw.status >= CswPhaseErr){ dprint(2, "%s: phase error\n", argv0); goto Fail; } if(csw.dataresidue == 0 || ums->wrongresidues) csw.dataresidue = data->count - nio; if(diskdebug){ fprint(2, "%s: status: %2.2ux residue: %ld\n", argv0, 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;Lines 1–500 of 1137. Load more
/* * 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, }; Dev *dev; Ums *ums; 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; } p = lookpart(lun, name); if(p != nil){ /* adding identical partition is no-op */ if(p->offset == start && p->length == end - start && p->mode == mode) return 0; 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(Umsc *lun) { Part *p, *part; Fmt fmt; part = &lun->part[0]; fmtstrinit(&fmt); if(lun->flags & Finqok) fmtprint(&fmt, "inquiry %s\n", lun->inq); fmtprint(&fmt, "dev %s\n", dev->dir); fmtprint(&fmt, "lun %zd\n", lun - &ums->lun[0]); 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(Umsc *lun, char *msg) { vlong start, end; char *argv[16]; int argc; 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 int getmaxlun(void) { uchar max; int r; max = 0; r = Rd2h|Rclass|Riface; if(usbcmd(dev, r, Getmaxlun, 0, 0, &max, 1) < 0){ dprint(2, "%s: %s: getmaxlun failed: %r\n", argv0, dev->dir); }else{ max &= 017; /* 15 is the max. allowed */ dprint(2, "%s: %s: maxlun %d\n", argv0, dev->dir, max); } /* 0e8d:1887 does not respond to followup commands without a delay */ sleep(1); return max; } static int umsreset(void) { int r; r = Rh2d|Rclass|Riface; if(usbcmd(dev, r, Umsreset, 0, 0, nil, 0) < 0){ fprint(2, "%s: reset: %r\n", argv0); return -1; } sleep(100); return 0; } static void umsrecover(void) { if(umsreset() < 0){ /* kill the 9p procs */ postnote(PNGROUP, getpid(), "shutdown"); sysfatal("umsrecover: %r"); } if(unstall(dev, ums->epin, Ein) < 0) dprint(2, "%s: unstall epin: %r\n", argv0); /* do we need this when epin == epout? */ if(unstall(dev, ums->epout, Eout) < 0) dprint(2, "%s: unstall epout: %r\n", argv0); } 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 umsready(Umsc *lun) { int i; for(i=0; i<3; i++){ if(SRready(lun) != -1) break; if(lun->status != Status_SD) continue; /* * logical unit is in process of becoming ready * or initializing command required */ if(lun->sense[12] == 0x04) if(lun->sense[13] == 0x02 || lun->sense[13] == 0x01) break; /* medium not present */ if(lun->sense[12] == 0x3A) return -1; } /* start unit */ if((lun->inquiry[0] & 0x1F) == 0){ SRstart(lun, 1); sleep(250); } return 0; } static int umscapacity(Umsc *lun) { uchar data[32]; lun->blocks = 0; lun->capacity = 0; lun->lbsize = 0; memset(data, 0, sizeof data); if(umsready(lun) < 0) return -1; if(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, "%s: logical block size %lud, # blocks %llud\n", argv0, lun->lbsize, lun->blocks); return 0; } static int umsinit(void) { uchar i; Umsc *lun; int some; umsreset(); ums->maxlun = getmaxlun(); 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, "%s: lun %d inquiry failed\n", argv0, i); continue; } switch(lun->inquiry[0]){ case Devdir: case Devworm: /* a little different than the others */ case Devcd: case Devmo: break; default: fprint(2, "%s: lun %d is not a disk (type %#02x)\n", argv0, i, lun->inquiry[0]); continue; } /* * 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, "%s: all luns failed\n", argv0); 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; 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) fprint(2, "%s: umsrequest: bad cmd count: %ld\n", argv0, 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, "%s: cmd: tag %#lx: ", argv0, 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, "%s: cmd: %r\n", argv0); 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); if (n >= 0 && n < nio) /* didn't fill data->p? */ memset(data->p + n, 0, nio - n); } if(diskdebug) if(n < 0) fprint(2, "%s: data: %r\n", argv0); else fprint(2, "%s: data: %d bytes (nio: %d)\n", argv0, n, nio); nio = n; if(n <= 0){ nio = 0; if(data->write == 0) unstall(dev, ums->epin, Ein); } } Again: /* read the transfer's status */ n = read(ums->epin->dfd, &csw, CswLen); if(n <= 0){ /* n == 0 means "stalled" */ unstall(dev, ums->epin, Ein); n = read(ums->epin->dfd, &csw, CswLen); } if(n != CswLen || strncmp(csw.signature, "USBS", 4) != 0){ dprint(2, "%s: read n=%d: status: %r\n", argv0, n); goto Fail; } if(csw.tag != cbw.tag){ dprint(2, "%s: status tag mismatch: %lux != %lux\n", argv0, csw.tag, cbw.tag); goto Again; } if(csw.status >= CswPhaseErr){ dprint(2, "%s: phase error\n", argv0); goto Fail; } if(csw.dataresidue == 0 || ums->wrongresidues) csw.dataresidue = data->count - nio; if(diskdebug){ fprint(2, "%s: status: %2.2ux residue: %ld\n", argv0, 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;