commit 7414e9996dc38431669b1da77013891e3f8940d6
parent b73f901985b9aaada2c11d977276a2d064555048
Author: Russ Cox <rsc@swtch.com>
Date: Sat, 28 Jun 2008 11:40:18 -0400
9vx/OSX: add devsd, add device size code to devfs-posix
Diffstat:
7 files changed, 2547 insertions(+), 0 deletions(-)
diff --git a/src/9vx/Makefrag b/src/9vx/Makefrag
@@ -45,6 +45,7 @@ PLAN9_OBJS = \
main.o \
mmu.o \
sched.o \
+ sdloop.o \
stub.o \
term.o \
time.o \
@@ -74,6 +75,7 @@ PLAN9_A_OBJS = \
devproc.o \
devpipe.o \
devroot.o \
+ devsd.o \
devsrv.o \
devssl.o \
devtls.o \
@@ -93,6 +95,7 @@ PLAN9_A_OBJS = \
proc.o \
qio.o \
qlock.o \
+ sdscsi.o \
segment.o \
strecpy.o \
sysfile.o \
diff --git a/src/9vx/a/devsd.c b/src/9vx/a/devsd.c
@@ -0,0 +1,1645 @@
+/*
+ * Storage Device.
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "error.h"
+
+#include "sd.h"
+
+extern Dev sddevtab;
+extern SDifc* sdifc[];
+
+static char Echange[] = "media or partition has changed";
+
+static char devletters[] = "0123456789"
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+static SDev *devs[sizeof devletters-1];
+static QLock devslock;
+
+enum {
+ Rawcmd,
+ Rawdata,
+ Rawstatus,
+};
+
+enum {
+ Qtopdir = 1, /* top level directory */
+ Qtopbase,
+ Qtopctl = Qtopbase,
+
+ Qunitdir, /* directory per unit */
+ Qunitbase,
+ Qctl = Qunitbase,
+ Qraw,
+ Qpart,
+
+ TypeLOG = 4,
+ NType = (1<<TypeLOG),
+ TypeMASK = (NType-1),
+ TypeSHIFT = 0,
+
+ PartLOG = 8,
+ NPart = (1<<PartLOG),
+ PartMASK = (NPart-1),
+ PartSHIFT = TypeLOG,
+
+ UnitLOG = 8,
+ NUnit = (1<<UnitLOG),
+ UnitMASK = (NUnit-1),
+ UnitSHIFT = (PartLOG+TypeLOG),
+
+ DevLOG = 8,
+ NDev = (1 << DevLOG),
+ DevMASK = (NDev-1),
+ DevSHIFT = (UnitLOG+PartLOG+TypeLOG),
+
+ Ncmd = 20,
+};
+
+#define TYPE(q) ((((ulong)(q).path)>>TypeSHIFT) & TypeMASK)
+#define PART(q) ((((ulong)(q).path)>>PartSHIFT) & PartMASK)
+#define UNIT(q) ((((ulong)(q).path)>>UnitSHIFT) & UnitMASK)
+#define DEV(q) ((((ulong)(q).path)>>DevSHIFT) & DevMASK)
+#define QID(d,u, p, t) (((d)<<DevSHIFT)|((u)<<UnitSHIFT)|\
+ ((p)<<PartSHIFT)|((t)<<TypeSHIFT))
+
+
+static void
+sdaddpart(SDunit* unit, char* name, uvlong start, uvlong end)
+{
+ SDpart *pp;
+ int i, partno;
+
+ /*
+ * Check name not already used
+ * and look for a free slot.
+ */
+ if(unit->part != nil){
+ partno = -1;
+ for(i = 0; i < unit->npart; i++){
+ pp = &unit->part[i];
+ if(!pp->valid){
+ if(partno == -1)
+ partno = i;
+ break;
+ }
+ if(strcmp(name, pp->perm.name) == 0){
+ if(pp->start == start && pp->end == end)
+ return;
+ error(Ebadctl);
+ }
+ }
+ }
+ else{
+ if((unit->part = malloc(sizeof(SDpart)*SDnpart)) == nil)
+ error(Enomem);
+ unit->npart = SDnpart;
+ partno = 0;
+ }
+
+ /*
+ * If no free slot found then increase the
+ * array size (can't get here with unit->part == nil).
+ */
+ if(partno == -1){
+ if(unit->npart >= NPart)
+ error(Enomem);
+ if((pp = malloc(sizeof(SDpart)*(unit->npart+SDnpart))) == nil)
+ error(Enomem);
+ memmove(pp, unit->part, sizeof(SDpart)*unit->npart);
+ free(unit->part);
+ unit->part = pp;
+ partno = unit->npart;
+ unit->npart += SDnpart;
+ }
+
+ /*
+ * Check size and extent are valid.
+ */
+ if(start > end || end > unit->sectors)
+ error(Eio);
+ pp = &unit->part[partno];
+ pp->start = start;
+ pp->end = end;
+ kstrdup(&pp->perm.name, name);
+ kstrdup(&pp->perm.user, eve);
+ pp->perm.perm = 0640;
+ pp->valid = 1;
+}
+
+static void
+sddelpart(SDunit* unit, char* name)
+{
+ int i;
+ SDpart *pp;
+
+ /*
+ * Look for the partition to delete.
+ * Can't delete if someone still has it open.
+ */
+ pp = unit->part;
+ for(i = 0; i < unit->npart; i++){
+ if(strcmp(name, pp->perm.name) == 0)
+ break;
+ pp++;
+ }
+ if(i >= unit->npart)
+ error(Ebadctl);
+ if(strcmp(up->user, pp->perm.user) && !iseve())
+ error(Eperm);
+ pp->valid = 0;
+ pp->vers++;
+}
+
+static void
+sdincvers(SDunit *unit)
+{
+ int i;
+
+ unit->vers++;
+ if(unit->part){
+ for(i = 0; i < unit->npart; i++){
+ unit->part[i].valid = 0;
+ unit->part[i].vers++;
+ }
+ }
+}
+
+static int
+sdinitpart(SDunit* unit)
+{
+ int nf;
+ uvlong start, end;
+ char *f[4], *p, *q, buf[10];
+
+ if(unit->sectors > 0){
+ unit->sectors = unit->secsize = 0;
+ sdincvers(unit);
+ }
+
+ if(unit->inquiry[0] & 0xC0)
+ return 0;
+ switch(unit->inquiry[0] & 0x1F){
+ case 0x00: /* DA */
+ case 0x04: /* WORM */
+ case 0x05: /* CD-ROM */
+ case 0x07: /* MO */
+ break;
+ default:
+ return 0;
+ }
+
+ if(unit->dev->ifc->online)
+ unit->dev->ifc->online(unit);
+ if(unit->sectors){
+ sdincvers(unit);
+ sdaddpart(unit, "data", 0, unit->sectors);
+#if 0
+ /*
+ * Use partitions passed from boot program,
+ * e.g.
+ * sdC0part=dos 63 123123/plan9 123123 456456
+ * This happens before /boot sets hostname so the
+ * partitions will have the null-string for user.
+ * The gen functions patch it up.
+ */
+ snprint(buf, sizeof buf, "%spart", unit->perm.name);
+ for(p = getconf(buf); p != nil; p = q){
+ if(q = strchr(p, '/'))
+ *q++ = '\0';
+ nf = tokenize(p, f, nelem(f));
+ if(nf < 3)
+ continue;
+
+ start = strtoull(f[1], 0, 0);
+ end = strtoull(f[2], 0, 0);
+ if(!waserror()){
+ sdaddpart(unit, f[0], start, end);
+ poperror();
+ }
+ }
+#endif
+ }
+
+ return 1;
+}
+
+static int
+sdindex(int idno)
+{
+ char *p;
+
+ p = strchr(devletters, idno);
+ if(p == nil)
+ return -1;
+ return p-devletters;
+}
+
+static SDev*
+sdgetdev(int idno)
+{
+ SDev *sdev;
+ int i;
+
+ if((i = sdindex(idno)) < 0)
+ return nil;
+
+ qlock(&devslock);
+ if(sdev = devs[i])
+ incref(&sdev->r);
+ qunlock(&devslock);
+ return sdev;
+}
+
+static SDunit*
+sdgetunit(SDev* sdev, int subno)
+{
+ SDunit *unit;
+ char buf[32];
+
+ /*
+ * Associate a unit with a given device and sub-unit
+ * number on that device.
+ * The device will be probed if it has not already been
+ * successfully accessed.
+ */
+ qlock(&sdev->unitlock);
+ if(subno > sdev->nunit){
+ qunlock(&sdev->unitlock);
+ return nil;
+ }
+
+ unit = sdev->unit[subno];
+ if(unit == nil){
+ /*
+ * Probe the unit only once. This decision
+ * may be a little severe and reviewed later.
+ */
+ if(sdev->unitflg[subno]){
+ qunlock(&sdev->unitlock);
+ return nil;
+ }
+ if((unit = malloc(sizeof(SDunit))) == nil){
+ qunlock(&sdev->unitlock);
+ return nil;
+ }
+ sdev->unitflg[subno] = 1;
+
+ snprint(buf, sizeof(buf), "%s%d", sdev->name, subno);
+ kstrdup(&unit->perm.name, buf);
+ kstrdup(&unit->perm.user, eve);
+ unit->perm.perm = 0555;
+ unit->subno = subno;
+ unit->dev = sdev;
+
+ if(sdev->enabled == 0 && sdev->ifc->enable)
+ sdev->ifc->enable(sdev);
+ sdev->enabled = 1;
+
+ /*
+ * No need to lock anything here as this is only
+ * called before the unit is made available in the
+ * sdunit[] array.
+ */
+ if(unit->dev->ifc->verify(unit) == 0){
+ qunlock(&sdev->unitlock);
+ free(unit);
+ return nil;
+ }
+ sdev->unit[subno] = unit;
+ }
+ qunlock(&sdev->unitlock);
+ return unit;
+}
+
+static void
+sdreset(void)
+{
+ int i;
+ SDev *sdev;
+
+ /*
+ * Probe all known controller types and register any devices found.
+ */
+ for(i = 0; sdifc[i] != nil; i++){
+ if(sdifc[i]->pnp == nil || (sdev = sdifc[i]->pnp()) == nil)
+ continue;
+ sdadddevs(sdev);
+ }
+}
+
+void
+sdadddevs(SDev *sdev)
+{
+ int i, j, id;
+ SDev *next;
+
+ for(; sdev; sdev=next){
+ next = sdev->next;
+
+ sdev->unit = (SDunit**)malloc(sdev->nunit * sizeof(SDunit*));
+ sdev->unitflg = (int*)malloc(sdev->nunit * sizeof(int));
+ if(sdev->unit == nil || sdev->unitflg == nil){
+ print("sdadddevs: out of memory\n");
+ giveup:
+ free(sdev->unit);
+ free(sdev->unitflg);
+ if(sdev->ifc->clear)
+ sdev->ifc->clear(sdev);
+ free(sdev);
+ continue;
+ }
+ id = sdindex(sdev->idno);
+ if(id == -1){
+ print("sdadddevs: bad id number %d (%C)\n", id, id);
+ goto giveup;
+ }
+ qlock(&devslock);
+ for(i=0; i<nelem(devs); i++){
+ if(devs[j = (id+i)%nelem(devs)] == nil){
+ sdev->idno = devletters[j];
+ devs[j] = sdev;
+ snprint(sdev->name, sizeof sdev->name, "sd%c", devletters[j]);
+ break;
+ }
+ }
+ qunlock(&devslock);
+ if(i == nelem(devs)){
+ print("sdadddevs: out of device letters\n");
+ goto giveup;
+ }
+ }
+}
+
+// void
+// sdrmdevs(SDev *sdev)
+// {
+// char buf[2];
+//
+// snprint(buf, sizeof buf, "%c", sdev->idno);
+// unconfigure(buf);
+// }
+
+static int
+sd2gen(Chan* c, int i, Dir* dp)
+{
+ Qid q;
+ uvlong l;
+ SDpart *pp;
+ SDperm *perm;
+ SDunit *unit;
+ SDev *sdev;
+ int rv;
+
+ sdev = sdgetdev(DEV(c->qid));
+ assert(sdev);
+ unit = sdev->unit[UNIT(c->qid)];
+
+ rv = -1;
+ switch(i){
+ case Qctl:
+ mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qctl),
+ unit->vers, QTFILE);
+ perm = &unit->ctlperm;
+ if(emptystr(perm->user)){
+ kstrdup(&perm->user, eve);
+ perm->perm = 0640;
+ }
+ devdir(c, q, "ctl", 0, perm->user, perm->perm, dp);
+ rv = 1;
+ break;
+
+ case Qraw:
+ mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qraw),
+ unit->vers, QTFILE);
+ perm = &unit->rawperm;
+ if(emptystr(perm->user)){
+ kstrdup(&perm->user, eve);
+ perm->perm = DMEXCL|0600;
+ }
+ devdir(c, q, "raw", 0, perm->user, perm->perm, dp);
+ rv = 1;
+ break;
+
+ case Qpart:
+ pp = &unit->part[PART(c->qid)];
+ l = (pp->end - pp->start) * unit->secsize;
+ mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qpart),
+ unit->vers+pp->vers, QTFILE);
+ if(emptystr(pp->perm.user))
+ kstrdup(&pp->perm.user, eve);
+ devdir(c, q, pp->perm.name, l, pp->perm.user, pp->perm.perm, dp);
+ rv = 1;
+ break;
+ }
+
+ decref(&sdev->r);
+ return rv;
+}
+
+static int
+sd1gen(Chan* c, int i, Dir* dp)
+{
+ Qid q;
+
+ switch(i){
+ case Qtopctl:
+ mkqid(&q, QID(0, 0, 0, Qtopctl), 0, QTFILE);
+ devdir(c, q, "sdctl", 0, eve, 0640, dp);
+ return 1;
+ }
+ return -1;
+}
+
+static int
+sdgen(Chan* c, char *name, Dirtab *dt, int j, int s, Dir* dp)
+{
+ Qid q;
+ uvlong l;
+ int i, r;
+ SDpart *pp;
+ SDunit *unit;
+ SDev *sdev;
+
+ switch(TYPE(c->qid)){
+ case Qtopdir:
+ if(s == DEVDOTDOT){
+ mkqid(&q, QID(0, 0, 0, Qtopdir), 0, QTDIR);
+ sprint(up->genbuf, "#%C", sddevtab.dc);
+ devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+ return 1;
+ }
+
+ if(s+Qtopbase < Qunitdir)
+ return sd1gen(c, s+Qtopbase, dp);
+ s -= (Qunitdir-Qtopbase);
+
+ qlock(&devslock);
+ for(i=0; i<nelem(devs); i++){
+ if(devs[i]){
+ if(s < devs[i]->nunit)
+ break;
+ s -= devs[i]->nunit;
+ }
+ }
+
+ if(i == nelem(devs)){
+ /* Run off the end of the list */
+ qunlock(&devslock);
+ return -1;
+ }
+
+ if((sdev = devs[i]) == nil){
+ qunlock(&devslock);
+ return 0;
+ }
+
+ incref(&sdev->r);
+ qunlock(&devslock);
+
+ if((unit = sdev->unit[s]) == nil)
+ if((unit = sdgetunit(sdev, s)) == nil){
+ decref(&sdev->r);
+ return 0;
+ }
+
+ mkqid(&q, QID(sdev->idno, s, 0, Qunitdir), 0, QTDIR);
+ if(emptystr(unit->perm.user))
+ kstrdup(&unit->perm.user, eve);
+ devdir(c, q, unit->perm.name, 0, unit->perm.user, unit->perm.perm, dp);
+ decref(&sdev->r);
+ return 1;
+
+ case Qunitdir:
+ if(s == DEVDOTDOT){
+ mkqid(&q, QID(0, 0, 0, Qtopdir), 0, QTDIR);
+ sprint(up->genbuf, "#%C", sddevtab.dc);
+ devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+ return 1;
+ }
+
+ if((sdev = sdgetdev(DEV(c->qid))) == nil){
+ devdir(c, c->qid, "unavailable", 0, eve, 0, dp);
+ return 1;
+ }
+
+ unit = sdev->unit[UNIT(c->qid)];
+ qlock(&unit->ctl);
+
+ /*
+ * Check for media change.
+ * If one has already been detected, sectors will be zero.
+ * If there is one waiting to be detected, online
+ * will return > 1.
+ * Online is a bit of a large hammer but does the job.
+ */
+ if(unit->sectors == 0
+ || (unit->dev->ifc->online && unit->dev->ifc->online(unit) > 1))
+ sdinitpart(unit);
+
+ i = s+Qunitbase;
+ if(i < Qpart){
+ r = sd2gen(c, i, dp);
+ qunlock(&unit->ctl);
+ decref(&sdev->r);
+ return r;
+ }
+ i -= Qpart;
+ if(unit->part == nil || i >= unit->npart){
+ qunlock(&unit->ctl);
+ decref(&sdev->r);
+ break;
+ }
+ pp = &unit->part[i];
+ if(!pp->valid){
+ qunlock(&unit->ctl);
+ decref(&sdev->r);
+ return 0;
+ }
+ l = (pp->end - pp->start) * unit->secsize;
+ mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), i, Qpart),
+ unit->vers+pp->vers, QTFILE);
+ if(emptystr(pp->perm.user))
+ kstrdup(&pp->perm.user, eve);
+ devdir(c, q, pp->perm.name, l, pp->perm.user, pp->perm.perm, dp);
+ qunlock(&unit->ctl);
+ decref(&sdev->r);
+ return 1;
+ case Qraw:
+ case Qctl:
+ case Qpart:
+ if((sdev = sdgetdev(DEV(c->qid))) == nil){
+ devdir(c, q, "unavailable", 0, eve, 0, dp);
+ return 1;
+ }
+ unit = sdev->unit[UNIT(c->qid)];
+ qlock(&unit->ctl);
+ r = sd2gen(c, TYPE(c->qid), dp);
+ qunlock(&unit->ctl);
+ decref(&sdev->r);
+ return r;
+ case Qtopctl:
+ return sd1gen(c, TYPE(c->qid), dp);
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+static Chan*
+sdattach(char* spec)
+{
+ Chan *c;
+ char *p;
+ SDev *sdev;
+ int idno, subno;
+
+ if(*spec == '\0'){
+ c = devattach(sddevtab.dc, spec);
+ mkqid(&c->qid, QID(0, 0, 0, Qtopdir), 0, QTDIR);
+ return c;
+ }
+
+ if(spec[0] != 's' || spec[1] != 'd')
+ error(Ebadspec);
+ idno = spec[2];
+ subno = strtol(&spec[3], &p, 0);
+ if(p == &spec[3])
+ error(Ebadspec);
+
+ if((sdev=sdgetdev(idno)) == nil)
+ error(Enonexist);
+ if(sdgetunit(sdev, subno) == nil){
+ decref(&sdev->r);
+ error(Enonexist);
+ }
+
+ c = devattach(sddevtab.dc, spec);
+ mkqid(&c->qid, QID(sdev->idno, subno, 0, Qunitdir), 0, QTDIR);
+ c->dev = (sdev->idno << UnitLOG) + subno;
+ decref(&sdev->r);
+ return c;
+}
+
+static Walkqid*
+sdwalk(Chan* c, Chan* nc, char** name, int nname)
+{
+ return devwalk(c, nc, name, nname, nil, 0, sdgen);
+}
+
+static int
+sdstat(Chan* c, uchar* db, int n)
+{
+ return devstat(c, db, n, nil, 0, sdgen);
+}
+
+static Chan*
+sdopen(Chan* c, int omode)
+{
+ SDpart *pp;
+ SDunit *unit;
+ SDev *sdev;
+ uchar tp;
+
+ c = devopen(c, omode, 0, 0, sdgen);
+ if((tp = TYPE(c->qid)) != Qctl && tp != Qraw && tp != Qpart)
+ return c;
+
+ sdev = sdgetdev(DEV(c->qid));
+ if(sdev == nil)
+ error(Enonexist);
+
+ unit = sdev->unit[UNIT(c->qid)];
+
+ switch(TYPE(c->qid)){
+ case Qctl:
+ c->qid.vers = unit->vers;
+ break;
+ case Qraw:
+ c->qid.vers = unit->vers;
+ if(tas(&unit->rawinuse) != 0){
+ c->flag &= ~COPEN;
+ decref(&sdev->r);
+ error(Einuse);
+ }
+ unit->state = Rawcmd;
+ break;
+ case Qpart:
+ qlock(&unit->ctl);
+ if(waserror()){
+ qunlock(&unit->ctl);
+ c->flag &= ~COPEN;
+ decref(&sdev->r);
+ nexterror();
+ }
+ pp = &unit->part[PART(c->qid)];
+ c->qid.vers = unit->vers+pp->vers;
+ qunlock(&unit->ctl);
+ poperror();
+ break;
+ }
+ decref(&sdev->r);
+ return c;
+}
+
+static void
+sdclose(Chan* c)
+{
+ SDunit *unit;
+ SDev *sdev;
+
+ if(c->qid.type & QTDIR)
+ return;
+ if(!(c->flag & COPEN))
+ return;
+
+ switch(TYPE(c->qid)){
+ default:
+ break;
+ case Qraw:
+ sdev = sdgetdev(DEV(c->qid));
+ if(sdev){
+ unit = sdev->unit[UNIT(c->qid)];
+ unit->rawinuse = 0;
+ decref(&sdev->r);
+ }
+ break;
+ }
+}
+
+static long
+sdbio(Chan* c, int write, char* a, long len, uvlong off)
+{
+ int nchange;
+ long l;
+ uchar *b;
+ SDpart *pp;
+ SDunit *unit;
+ SDev *sdev;
+ ulong max, nb, offset;
+ uvlong bno;
+
+ sdev = sdgetdev(DEV(c->qid));
+ if(sdev == nil){
+ decref(&sdev->r);
+ error(Enonexist);
+ }
+ unit = sdev->unit[UNIT(c->qid)];
+ if(unit == nil)
+ error(Enonexist);
+
+ nchange = 0;
+ qlock(&unit->ctl);
+ while(waserror()){
+ /* notification of media change; go around again */
+ if(strcmp(up->errstr, Eio) == 0 && unit->sectors == 0 && nchange++ == 0){
+ sdinitpart(unit);
+ continue;
+ }
+
+ /* other errors; give up */
+ qunlock(&unit->ctl);
+ decref(&sdev->r);
+ nexterror();
+ }
+ pp = &unit->part[PART(c->qid)];
+ if(unit->vers+pp->vers != c->qid.vers)
+ error(Echange);
+
+ /*
+ * Check the request is within bounds.
+ * Removeable drives are locked throughout the I/O
+ * in case the media changes unexpectedly.
+ * Non-removeable drives are not locked during the I/O
+ * to allow the hardware to optimise if it can; this is
+ * a little fast and loose.
+ * It's assumed that non-removeable media parameters
+ * (sectors, secsize) can't change once the drive has
+ * been brought online.
+ */
+ bno = (off/unit->secsize) + pp->start;
+ nb = ((off+len+unit->secsize-1)/unit->secsize) + pp->start - bno;
+ max = SDmaxio/unit->secsize;
+ if(nb > max)
+ nb = max;
+ if(bno+nb > pp->end)
+ nb = pp->end - bno;
+ if(bno >= pp->end || nb == 0){
+ if(write)
+ error(Eio);
+ qunlock(&unit->ctl);
+ decref(&sdev->r);
+ poperror();
+ return 0;
+ }
+ if(!(unit->inquiry[1] & 0x80)){
+ qunlock(&unit->ctl);
+ poperror();
+ }
+
+ b = sdmalloc(nb*unit->secsize);
+ if(b == nil)
+ error(Enomem);
+ if(waserror()){
+ sdfree(b);
+ if(!(unit->inquiry[1] & 0x80))
+ decref(&sdev->r); /* gadverdamme! */
+ nexterror();
+ }
+
+ offset = off%unit->secsize;
+ if(offset+len > nb*unit->secsize)
+ len = nb*unit->secsize - offset;
+ if(write){
+ if(offset || (len%unit->secsize)){
+ l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno);
+ if(l < 0)
+ error(Eio);
+ if(l < (nb*unit->secsize)){
+ nb = l/unit->secsize;
+ l = nb*unit->secsize - offset;
+ if(len > l)
+ len = l;
+ }
+ }
+ memmove(b+offset, a, len);
+ l = unit->dev->ifc->bio(unit, 0, 1, b, nb, bno);
+ if(l < 0)
+ error(Eio);
+ if(l < offset)
+ len = 0;
+ else if(len > l - offset)
+ len = l - offset;
+ }
+ else{
+ l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno);
+ if(l < 0)
+ error(Eio);
+ if(l < offset)
+ len = 0;
+ else if(len > l - offset)
+ len = l - offset;
+ memmove(a, b+offset, len);
+ }
+ sdfree(b);
+ poperror();
+
+ if(unit->inquiry[1] & 0x80){
+ qunlock(&unit->ctl);
+ poperror();
+ }
+
+ decref(&sdev->r);
+ return len;
+}
+
+static long
+sdrio(SDreq* r, void* a, long n)
+{
+ void *data;
+
+ if(n >= SDmaxio || n < 0)
+ error(Etoobig);
+
+ data = nil;
+ if(n){
+ if((data = sdmalloc(n)) == nil)
+ error(Enomem);
+ if(r->write)
+ memmove(data, a, n);
+ }
+ r->data = data;
+ r->dlen = n;
+
+ if(waserror()){
+ sdfree(data);
+ r->data = nil;
+ nexterror();
+ }
+
+ if(r->unit->dev->ifc->rio(r) != SDok)
+ error(Eio);
+
+ if(!r->write && r->rlen > 0)
+ memmove(a, data, r->rlen);
+ sdfree(data);
+ r->data = nil;
+ poperror();
+
+ return r->rlen;
+}
+
+/*
+ * SCSI simulation for non-SCSI devices
+ */
+int
+sdsetsense(SDreq *r, int status, int key, int asc, int ascq)
+{
+ int len;
+ SDunit *unit;
+
+ unit = r->unit;
+ unit->sense[2] = key;
+ unit->sense[12] = asc;
+ unit->sense[13] = ascq;
+
+ r->status = status;
+ if(status == SDcheck && !(r->flags & SDnosense)){
+ /* request sense case from sdfakescsi */
+ len = sizeof unit->sense;
+ if(len > sizeof r->sense-1)
+ len = sizeof r->sense-1;
+ memmove(r->sense, unit->sense, len);
+ unit->sense[2] = 0;
+ unit->sense[12] = 0;
+ unit->sense[13] = 0;
+ r->flags |= SDvalidsense;
+ return SDok;
+ }
+ return status;
+}
+
+int
+sdmodesense(SDreq *r, uchar *cmd, void *info, int ilen)
+{
+ int len;
+ uchar *data;
+
+ /*
+ * Fake a vendor-specific request with page code 0,
+ * return the drive info.
+ */
+ if((cmd[2] & 0x3F) != 0 && (cmd[2] & 0x3F) != 0x3F)
+ return sdsetsense(r, SDcheck, 0x05, 0x24, 0);
+ len = (cmd[7]<<8)|cmd[8];
+ if(len == 0)
+ return SDok;
+ if(len < 8+ilen)
+ return sdsetsense(r, SDcheck, 0x05, 0x1A, 0);
+ if(r->data == nil || r->dlen < len)
+ return sdsetsense(r, SDcheck, 0x05, 0x20, 1);
+ data = r->data;
+ memset(data, 0, 8);
+ data[0] = ilen>>8;
+ data[1] = ilen;
+ if(ilen)
+ memmove(data+8, info, ilen);
+ r->rlen = 8+ilen;
+ return sdsetsense(r, SDok, 0, 0, 0);
+}
+
+int
+sdfakescsi(SDreq *r, void *info, int ilen)
+{
+ uchar *cmd, *p;
+ uvlong len;
+ SDunit *unit;
+
+ cmd = r->cmd;
+ r->rlen = 0;
+ unit = r->unit;
+
+ /*
+ * Rewrite read(6)/write(6) into read(10)/write(10).
+ */
+ switch(cmd[0]){
+ case 0x08: /* read */
+ case 0x0A: /* write */
+ cmd[9] = 0;
+ cmd[8] = cmd[4];
+ cmd[7] = 0;
+ cmd[6] = 0;
+ cmd[5] = cmd[3];
+ cmd[4] = cmd[2];
+ cmd[3] = cmd[1] & 0x0F;
+ cmd[2] = 0;
+ cmd[1] &= 0xE0;
+ cmd[0] |= 0x20;
+ break;
+ }
+
+ /*
+ * Map SCSI commands into ATA commands for discs.
+ * Fail any command with a LUN except INQUIRY which
+ * will return 'logical unit not supported'.
+ */
+ if((cmd[1]>>5) && cmd[0] != 0x12)
+ return sdsetsense(r, SDcheck, 0x05, 0x25, 0);
+
+ switch(cmd[0]){
+ default:
+ return sdsetsense(r, SDcheck, 0x05, 0x20, 0);
+
+ case 0x00: /* test unit ready */
+ return sdsetsense(r, SDok, 0, 0, 0);
+
+ case 0x03: /* request sense */
+ if(cmd[4] < sizeof unit->sense)
+ len = cmd[4];
+ else
+ len = sizeof unit->sense;
+ if(r->data && r->dlen >= len){
+ memmove(r->data, unit->sense, len);
+ r->rlen = len;
+ }
+ return sdsetsense(r, SDok, 0, 0, 0);
+
+ case 0x12: /* inquiry */
+ if(cmd[4] < sizeof unit->inquiry)
+ len = cmd[4];
+ else
+ len = sizeof unit->inquiry;
+ if(r->data && r->dlen >= len){
+ memmove(r->data, unit->inquiry, len);
+ r->rlen = len;
+ }
+ return sdsetsense(r, SDok, 0, 0, 0);
+
+ case 0x1B: /* start/stop unit */
+ /*
+ * nop for now, can use power management later.
+ */
+ return sdsetsense(r, SDok, 0, 0, 0);
+
+ case 0x25: /* read capacity */
+ if((cmd[1] & 0x01) || cmd[2] || cmd[3])
+ return sdsetsense(r, SDcheck, 0x05, 0x24, 0);
+ if(r->data == nil || r->dlen < 8)
+ return sdsetsense(r, SDcheck, 0x05, 0x20, 1);
+
+ /*
+ * Read capacity returns the LBA of the last sector.
+ */
+ len = unit->sectors - 1;
+ p = r->data;
+ *p++ = len>>24;
+ *p++ = len>>16;
+ *p++ = len>>8;
+ *p++ = len;
+ len = 512;
+ *p++ = len>>24;
+ *p++ = len>>16;
+ *p++ = len>>8;
+ *p++ = len;
+ r->rlen = p - (uchar*)r->data;
+ return sdsetsense(r, SDok, 0, 0, 0);
+
+ case 0x9E: /* long read capacity */
+ if((cmd[1] & 0x01) || cmd[2] || cmd[3])
+ return sdsetsense(r, SDcheck, 0x05, 0x24, 0);
+ if(r->data == nil || r->dlen < 8)
+ return sdsetsense(r, SDcheck, 0x05, 0x20, 1);
+ /*
+ * Read capcity returns the LBA of the last sector.
+ */
+ len = unit->sectors - 1;
+ p = r->data;
+ *p++ = len>>56;
+ *p++ = len>>48;
+ *p++ = len>>40;
+ *p++ = len>>32;
+ *p++ = len>>24;
+ *p++ = len>>16;
+ *p++ = len>>8;
+ *p++ = len;
+ len = 512;
+ *p++ = len>>24;
+ *p++ = len>>16;
+ *p++ = len>>8;
+ *p++ = len;
+ r->rlen = p - (uchar*)r->data;
+ return sdsetsense(r, SDok, 0, 0, 0);
+
+ case 0x5A: /* mode sense */
+ return sdmodesense(r, cmd, info, ilen);
+
+ case 0x28: /* read */
+ case 0x2A: /* write */
+ case 0x88: /* read16 */
+ case 0x8a: /* write16 */
+ return SDnostatus;
+ }
+}
+
+static long
+sdread(Chan *c, void *a, long n, vlong off)
+{
+ char *p, *e, *buf;
+ SDpart *pp;
+ SDunit *unit;
+ SDev *sdev;
+ ulong offset;
+ int i, l, m, status;
+
+ offset = off;
+ switch(TYPE(c->qid)){
+ default:
+ error(Eperm);
+ case Qtopctl:
+ m = 64*1024; /* room for register dumps */
+ p = buf = malloc(m);
+ assert(p);
+ e = p + m;
+ qlock(&devslock);
+ for(i = 0; i < nelem(devs); i++){
+ sdev = devs[i];
+ if(sdev && sdev->ifc->rtopctl)
+ p = sdev->ifc->rtopctl(sdev, p, e);
+ }
+ qunlock(&devslock);
+ n = readstr(off, a, n, buf);
+ free(buf);
+ return n;
+
+ case Qtopdir:
+ case Qunitdir:
+ return devdirread(c, a, n, 0, 0, sdgen);
+
+ case Qctl:
+ sdev = sdgetdev(DEV(c->qid));
+ if(sdev == nil)
+ error(Enonexist);
+
+ unit = sdev->unit[UNIT(c->qid)];
+ m = 16*1024; /* room for register dumps */
+ p = malloc(m);
+ l = snprint(p, m, "inquiry %.48s\n",
+ (char*)unit->inquiry+8);
+ qlock(&unit->ctl);
+ /*
+ * If there's a device specific routine it must
+ * provide all information pertaining to night geometry
+ * and the garscadden trains.
+ */
+ if(unit->dev->ifc->rctl)
+ l += unit->dev->ifc->rctl(unit, p+l, m-l);
+ if(unit->sectors == 0)
+ sdinitpart(unit);
+ if(unit->sectors){
+ if(unit->dev->ifc->rctl == nil)
+ l += snprint(p+l, m-l,
+ "geometry %llud %lud\n",
+ unit->sectors, unit->secsize);
+ pp = unit->part;
+ for(i = 0; i < unit->npart; i++){
+ if(pp->valid)
+ l += snprint(p+l, m-l,
+ "part %s %llud %llud\n",
+ pp->perm.name, pp->start, pp->end);
+ pp++;
+ }
+ }
+ qunlock(&unit->ctl);
+ decref(&sdev->r);
+ l = readstr(offset, a, n, p);
+ free(p);
+ return l;
+
+ case Qraw:
+ sdev = sdgetdev(DEV(c->qid));
+ if(sdev == nil)
+ error(Enonexist);
+
+ unit = sdev->unit[UNIT(c->qid)];
+ qlock(&unit->raw);
+ if(waserror()){
+ qunlock(&unit->raw);
+ decref(&sdev->r);
+ nexterror();
+ }
+ if(unit->state == Rawdata){
+ unit->state = Rawstatus;
+ i = sdrio(unit->req, a, n);
+ }
+ else if(unit->state == Rawstatus){
+ status = unit->req->status;
+ unit->state = Rawcmd;
+ free(unit->req);
+ unit->req = nil;
+ i = readnum(0, a, n, status, NUMSIZE);
+ } else
+ i = 0;
+ qunlock(&unit->raw);
+ decref(&sdev->r);
+ poperror();
+ return i;
+
+ case Qpart:
+ return sdbio(c, 0, a, n, off);
+ }
+}
+
+static void legacytopctl(Cmdbuf*);
+
+static long
+sdwrite(Chan* c, void* a, long n, vlong off)
+{
+ char *f0;
+ int i;
+ uvlong end, start;
+ Cmdbuf *cb;
+ SDifc *ifc;
+ SDreq *req;
+ SDunit *unit;
+ SDev *sdev;
+
+ switch(TYPE(c->qid)){
+ default:
+ error(Eperm);
+ case Qtopctl:
+ cb = parsecmd(a, n);
+ if(waserror()){
+ free(cb);
+ nexterror();
+ }
+ if(cb->nf == 0)
+ error("empty control message");
+ f0 = cb->f[0];
+ cb->f++;
+ cb->nf--;
+ if(strcmp(f0, "config") == 0){
+ /* wormhole into ugly legacy interface */
+ legacytopctl(cb);
+ poperror();
+ free(cb);
+ break;
+ }
+ /*
+ * "ata arg..." invokes sdifc[i]->wtopctl(nil, cb),
+ * where sdifc[i]->name=="ata" and cb contains the args.
+ */
+ ifc = nil;
+ sdev = nil;
+ for(i=0; sdifc[i]; i++){
+ if(strcmp(sdifc[i]->name, f0) == 0){
+ ifc = sdifc[i];
+ sdev = nil;
+ goto subtopctl;
+ }
+ }
+ /*
+ * "sd1 arg..." invokes sdifc[i]->wtopctl(sdev, cb),
+ * where sdifc[i] and sdev match controller letter "1",
+ * and cb contains the args.
+ */
+ if(f0[0]=='s' && f0[1]=='d' && f0[2] && f0[3] == 0){
+ if((sdev = sdgetdev(f0[2])) != nil){
+ ifc = sdev->ifc;
+ goto subtopctl;
+ }
+ }
+ error("unknown interface");
+
+ subtopctl:
+ if(waserror()){
+ if(sdev)
+ decref(&sdev->r);
+ nexterror();
+ }
+ if(ifc->wtopctl)
+ ifc->wtopctl(sdev, cb);
+ else
+ error(Ebadctl);
+ poperror();
+ poperror();
+ if (sdev)
+ decref(&sdev->r);
+ free(cb);
+ break;
+
+ case Qctl:
+ cb = parsecmd(a, n);
+ sdev = sdgetdev(DEV(c->qid));
+ if(sdev == nil)
+ error(Enonexist);
+ unit = sdev->unit[UNIT(c->qid)];
+
+ qlock(&unit->ctl);
+ if(waserror()){
+ qunlock(&unit->ctl);
+ decref(&sdev->r);
+ free(cb);
+ nexterror();
+ }
+ if(unit->vers != c->qid.vers)
+ error(Echange);
+
+ if(cb->nf < 1)
+ error(Ebadctl);
+ if(strcmp(cb->f[0], "part") == 0){
+ if(cb->nf != 4)
+ error(Ebadctl);
+ if(unit->sectors == 0 && !sdinitpart(unit))
+ error(Eio);
+ start = strtoull(cb->f[2], 0, 0);
+ end = strtoull(cb->f[3], 0, 0);
+ sdaddpart(unit, cb->f[1], start, end);
+ }
+ else if(strcmp(cb->f[0], "delpart") == 0){
+ if(cb->nf != 2 || unit->part == nil)
+ error(Ebadctl);
+ sddelpart(unit, cb->f[1]);
+ }
+ else if(unit->dev->ifc->wctl)
+ unit->dev->ifc->wctl(unit, cb);
+ else
+ error(Ebadctl);
+ qunlock(&unit->ctl);
+ decref(&sdev->r);
+ poperror();
+ free(cb);
+ break;
+
+ case Qraw:
+ sdev = sdgetdev(DEV(c->qid));
+ if(sdev == nil)
+ error(Enonexist);
+ unit = sdev->unit[UNIT(c->qid)];
+ qlock(&unit->raw);
+ if(waserror()){
+ qunlock(&unit->raw);
+ decref(&sdev->r);
+ nexterror();
+ }
+ switch(unit->state){
+ case Rawcmd:
+ if(n < 6 || n > sizeof(req->cmd))
+ error(Ebadarg);
+ if((req = malloc(sizeof(SDreq))) == nil)
+ error(Enomem);
+ req->unit = unit;
+ memmove(req->cmd, a, n);
+ req->clen = n;
+ req->flags = SDnosense;
+ req->status = ~0;
+
+ unit->req = req;
+ unit->state = Rawdata;
+ break;
+
+ case Rawstatus:
+ unit->state = Rawcmd;
+ free(unit->req);
+ unit->req = nil;
+ error(Ebadusefd);
+
+ case Rawdata:
+ unit->state = Rawstatus;
+ unit->req->write = 1;
+ n = sdrio(unit->req, a, n);
+ }
+ qunlock(&unit->raw);
+ decref(&sdev->r);
+ poperror();
+ break;
+ case Qpart:
+ return sdbio(c, 1, a, n, off);
+ }
+
+ return n;
+}
+
+static int
+sdwstat(Chan* c, uchar* dp, int n)
+{
+ Dir *d;
+ SDpart *pp;
+ SDperm *perm;
+ SDunit *unit;
+ SDev *sdev;
+
+ if(c->qid.type & QTDIR)
+ error(Eperm);
+
+ sdev = sdgetdev(DEV(c->qid));
+ if(sdev == nil)
+ error(Enonexist);
+ unit = sdev->unit[UNIT(c->qid)];
+ qlock(&unit->ctl);
+ d = nil;
+ if(waserror()){
+ free(d);
+ qunlock(&unit->ctl);
+ decref(&sdev->r);
+ nexterror();
+ }
+
+ switch(TYPE(c->qid)){
+ default:
+ error(Eperm);
+ case Qctl:
+ perm = &unit->ctlperm;
+ break;
+ case Qraw:
+ perm = &unit->rawperm;
+ break;
+ case Qpart:
+ pp = &unit->part[PART(c->qid)];
+ if(unit->vers+pp->vers != c->qid.vers)
+ error(Enonexist);
+ perm = &pp->perm;
+ break;
+ }
+
+ if(strcmp(up->user, perm->user) && !iseve())
+ error(Eperm);
+
+ d = smalloc(sizeof(Dir)+n);
+ n = convM2D(dp, n, &d[0], (char*)&d[1]);
+ if(n == 0)
+ error(Eshortstat);
+ if(!emptystr(d[0].uid))
+ kstrdup(&perm->user, d[0].uid);
+ if(d[0].mode != ~0UL)
+ perm->perm = (perm->perm & ~0777) | (d[0].mode & 0777);
+
+ free(d);
+ qunlock(&unit->ctl);
+ decref(&sdev->r);
+ poperror();
+ return n;
+}
+
+static int
+configure(char* spec, DevConf* cf)
+{
+ SDev *s, *sdev;
+ char *p;
+ int i;
+
+ if(sdindex(*spec) < 0)
+ error("bad sd spec");
+
+ if((p = strchr(cf->type, '/')) != nil)
+ *p++ = '\0';
+
+ for(i = 0; sdifc[i] != nil; i++)
+ if(strcmp(sdifc[i]->name, cf->type) == 0)
+ break;
+ if(sdifc[i] == nil)
+ error("sd type not found");
+ if(p)
+ *(p-1) = '/';
+
+ if(sdifc[i]->probe == nil)
+ error("sd type cannot probe");
+
+ sdev = sdifc[i]->probe(cf);
+ for(s=sdev; s; s=s->next)
+ s->idno = *spec;
+ sdadddevs(sdev);
+ return 0;
+}
+
+static int
+unconfigure(char* spec)
+{
+ int i;
+ SDev *sdev;
+ SDunit *unit;
+
+ if((i = sdindex(*spec)) < 0)
+ error(Enonexist);
+
+ qlock(&devslock);
+ if((sdev = devs[i]) == nil){
+ qunlock(&devslock);
+ error(Enonexist);
+ }
+ if(sdev->r.ref){
+ qunlock(&devslock);
+ error(Einuse);
+ }
+ devs[i] = nil;
+ qunlock(&devslock);
+
+ /* make sure no interrupts arrive anymore before removing resources */
+ if(sdev->enabled && sdev->ifc->disable)
+ sdev->ifc->disable(sdev);
+
+ for(i = 0; i != sdev->nunit; i++){
+ if(unit = sdev->unit[i]){
+ free(unit->perm.name);
+ free(unit->perm.user);
+ free(unit);
+ }
+ }
+
+ if(sdev->ifc->clear)
+ sdev->ifc->clear(sdev);
+ free(sdev);
+ return 0;
+}
+
+static int
+sdconfig(int on, char* spec, DevConf* cf)
+{
+ if(on)
+ return configure(spec, cf);
+ return unconfigure(spec);
+}
+
+Dev sddevtab = {
+ 'S',
+ "sd",
+
+ sdreset,
+ devinit,
+ devshutdown,
+ sdattach,
+ sdwalk,
+ sdstat,
+ sdopen,
+ devcreate,
+ sdclose,
+ sdread,
+ devbread,
+ sdwrite,
+ devbwrite,
+ devremove,
+ sdwstat,
+ devpower,
+ sdconfig,
+};
+
+/*
+ * This is wrong for so many reasons. This code must go.
+ */
+typedef struct Confdata Confdata;
+struct Confdata {
+ int on;
+ char* spec;
+ DevConf cf;
+};
+
+static void
+parseswitch(Confdata* cd, char* option)
+{
+ if(!strcmp("on", option))
+ cd->on = 1;
+ else if(!strcmp("off", option))
+ cd->on = 0;
+ else
+ error(Ebadarg);
+}
+
+static void
+parsespec(Confdata* cd, char* option)
+{
+ if(strlen(option) > 1)
+ error(Ebadarg);
+ cd->spec = option;
+}
+
+static Devport*
+getnewport(DevConf* dc)
+{
+ Devport *p;
+
+ p = (Devport *)malloc((dc->nports + 1) * sizeof(Devport));
+ if(dc->nports > 0){
+ memmove(p, dc->ports, dc->nports * sizeof(Devport));
+ free(dc->ports);
+ }
+ dc->ports = p;
+ p = &dc->ports[dc->nports++];
+ p->size = -1;
+ p->port = (ulong)-1;
+ return p;
+}
+
+static void
+parseport(Confdata* cd, char* option)
+{
+ char *e;
+ Devport *p;
+
+ if(cd->cf.nports == 0 || cd->cf.ports[cd->cf.nports-1].port != (ulong)-1)
+ p = getnewport(&cd->cf);
+ else
+ p = &cd->cf.ports[cd->cf.nports-1];
+ p->port = strtol(option, &e, 0);
+ if(e == nil || *e != '\0')
+ error(Ebadarg);
+}
+
+static void
+parsesize(Confdata* cd, char* option)
+{
+ char *e;
+ Devport *p;
+
+ if(cd->cf.nports == 0 || cd->cf.ports[cd->cf.nports-1].size != -1)
+ p = getnewport(&cd->cf);
+ else
+ p = &cd->cf.ports[cd->cf.nports-1];
+ p->size = (int)strtol(option, &e, 0);
+ if(e == nil || *e != '\0')
+ error(Ebadarg);
+}
+
+static void
+parseirq(Confdata* cd, char* option)
+{
+ char *e;
+
+ cd->cf.intnum = strtoul(option, &e, 0);
+ if(e == nil || *e != '\0')
+ error(Ebadarg);
+}
+
+static void
+parsetype(Confdata* cd, char* option)
+{
+ cd->cf.type = option;
+}
+
+static struct {
+ char *name;
+ void (*parse)(Confdata*, char*);
+} options[] = {
+ "switch", parseswitch,
+ "spec", parsespec,
+ "port", parseport,
+ "size", parsesize,
+ "irq", parseirq,
+ "type", parsetype,
+};
+
+static void
+legacytopctl(Cmdbuf *cb)
+{
+ char *opt;
+ int i, j;
+ Confdata cd;
+
+ memset(&cd, 0, sizeof cd);
+ cd.on = -1;
+ for(i=0; i<cb->nf; i+=2){
+ if(i+2 > cb->nf)
+ error(Ebadarg);
+ opt = cb->f[i];
+ for(j=0; j<nelem(options); j++)
+ if(strcmp(opt, options[j].name) == 0){
+ options[j].parse(&cd, cb->f[i+1]);
+ break;
+ }
+ if(j == nelem(options))
+ error(Ebadarg);
+ }
+ /* this has been rewritten to accomodate sdaoe */
+ if(cd.on < 0 || cd.spec == 0)
+ error(Ebadarg);
+ if(cd.on && cd.cf.type == nil)
+ error(Ebadarg);
+ sdconfig(cd.on, cd.spec, &cd.cf);
+}
diff --git a/src/9vx/a/sd.h b/src/9vx/a/sd.h
@@ -0,0 +1,137 @@
+/*
+ * Storage Device.
+ */
+typedef struct SDev SDev;
+typedef struct SDifc SDifc;
+typedef struct SDpart SDpart;
+typedef struct SDperm SDperm;
+typedef struct SDreq SDreq;
+typedef struct SDunit SDunit;
+
+struct SDperm {
+ char* name;
+ char* user;
+ ulong perm;
+};
+
+struct SDpart {
+ uvlong start;
+ uvlong end;
+ SDperm perm;
+ int valid;
+ ulong vers;
+};
+
+struct SDunit {
+ SDev* dev;
+ int subno;
+ uchar inquiry[255]; /* format follows SCSI spec */
+ uchar sense[18]; /* format follows SCSI spec */
+ SDperm perm;
+
+ QLock ctl;
+ uvlong sectors;
+ ulong secsize;
+ SDpart* part; /* nil or array of size npart */
+ int npart;
+ ulong vers;
+ SDperm ctlperm;
+
+ QLock raw; /* raw read or write in progress */
+ ulong rawinuse; /* really just a test-and-set */
+ int state;
+ SDreq* req;
+ SDperm rawperm;
+};
+
+/*
+ * Each controller is represented by a SDev.
+ */
+struct SDev {
+ Ref r; /* Number of callers using device */
+ SDifc* ifc; /* pnp/legacy */
+ void* ctlr;
+ int idno;
+ char name[8];
+ SDev* next;
+
+ QLock lk; /* enable/disable */
+ int enabled;
+ int nunit; /* Number of units */
+ QLock unitlock; /* `Loading' of units */
+ int* unitflg; /* Unit flags */
+ SDunit**unit;
+};
+
+struct SDifc {
+ char* name;
+
+ SDev* (*pnp)(void);
+ SDev* (*legacy)(int, int);
+ int (*enable)(SDev*);
+ int (*disable)(SDev*);
+
+ int (*verify)(SDunit*);
+ int (*online)(SDunit*);
+ int (*rio)(SDreq*);
+ int (*rctl)(SDunit*, char*, int);
+ int (*wctl)(SDunit*, Cmdbuf*);
+
+ long (*bio)(SDunit*, int, int, void*, long, uvlong);
+ SDev* (*probe)(DevConf*);
+ void (*clear)(SDev*);
+ char* (*rtopctl)(SDev*, char*, char*);
+ int (*wtopctl)(SDev*, Cmdbuf*);
+};
+
+struct SDreq {
+ SDunit* unit;
+ int lun;
+ int write;
+ uchar cmd[16];
+ int clen;
+ void* data;
+ int dlen;
+
+ int flags;
+
+ int status;
+ long rlen;
+ uchar sense[256];
+};
+
+enum {
+ SDnosense = 0x00000001,
+ SDvalidsense = 0x00010000,
+};
+
+enum {
+ SDretry = -5, /* internal to controllers */
+ SDmalloc = -4,
+ SDeio = -3,
+ SDtimeout = -2,
+ SDnostatus = -1,
+
+ SDok = 0,
+
+ SDcheck = 0x02, /* check condition */
+ SDbusy = 0x08, /* busy */
+
+ SDmaxio = 2048*1024,
+ SDnpart = 16,
+};
+
+#define sdmalloc(n) malloc(n)
+#define sdfree(p) free(p)
+
+/* devsd.c */
+extern void sdadddevs(SDev*);
+extern int sdsetsense(SDreq*, int, int, int, int);
+extern int sdmodesense(SDreq*, uchar*, void*, int);
+extern int sdfakescsi(SDreq*, void*, int);
+
+/* sdscsi.c */
+extern int scsiverify(SDunit*);
+extern int scsionline(SDunit*);
+extern long scsibio(SDunit*, int, int, void*, long, uvlong);
+extern SDev* scsiid(SDev*, SDifc*);
diff --git a/src/9vx/a/sdscsi.c b/src/9vx/a/sdscsi.c
@@ -0,0 +1,424 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "error.h"
+
+#include "sd.h"
+
+static int
+scsitest(SDreq* r)
+{
+ r->write = 0;
+ memset(r->cmd, 0, sizeof(r->cmd));
+ r->cmd[1] = r->lun<<5;
+ r->clen = 6;
+ r->data = nil;
+ r->dlen = 0;
+ r->flags = 0;
+
+ r->status = ~0;
+
+ return r->unit->dev->ifc->rio(r);
+}
+
+int
+scsiverify(SDunit* unit)
+{
+ SDreq *r;
+ int i, status;
+ uchar *inquiry;
+
+ if((r = malloc(sizeof(SDreq))) == nil)
+ return 0;
+ if((inquiry = sdmalloc(sizeof(unit->inquiry))) == nil){
+ free(r);
+ return 0;
+ }
+ r->unit = unit;
+ r->lun = 0; /* ??? */
+
+ memset(unit->inquiry, 0, sizeof(unit->inquiry));
+ r->write = 0;
+ r->cmd[0] = 0x12;
+ r->cmd[1] = r->lun<<5;
+ r->cmd[4] = sizeof(unit->inquiry)-1;
+ r->clen = 6;
+ r->data = inquiry;
+ r->dlen = sizeof(unit->inquiry)-1;
+ r->flags = 0;
+
+ r->status = ~0;
+ if(unit->dev->ifc->rio(r) != SDok){
+ free(r);
+ return 0;
+ }
+ memmove(unit->inquiry, inquiry, r->dlen);
+ free(inquiry);
+
+ status = 0;
+ for(i = 0; i < 3; i++){
+ while((status = scsitest(r)) == SDbusy)
+ ;
+ if(status == SDok || status != SDcheck)
+ break;
+ if(!(r->flags & SDvalidsense))
+ break;
+ if((r->sense[2] & 0x0F) != 0x02)
+ continue;
+
+ /*
+ * Unit is 'not ready'.
+ * If it is in the process of becoming ready or needs
+ * an initialising command, set status so it will be spun-up
+ * below.
+ * If there's no medium, that's OK too, but don't
+ * try to spin it up.
+ */
+ if(r->sense[12] == 0x04){
+ if(r->sense[13] == 0x02 || r->sense[13] == 0x01){
+ status = SDok;
+ break;
+ }
+ }
+ if(r->sense[12] == 0x3A)
+ break;
+ }
+
+ if(status == SDok){
+ /*
+ * Try to ensure a direct-access device is spinning.
+ * Don't wait for completion, ignore the result.
+ */
+ if((unit->inquiry[0] & 0x1F) == 0){
+ memset(r->cmd, 0, sizeof(r->cmd));
+ r->write = 0;
+ r->cmd[0] = 0x1B;
+ r->cmd[1] = (r->lun<<5)|0x01;
+ r->cmd[4] = 1;
+ r->clen = 6;
+ r->data = nil;
+ r->dlen = 0;
+ r->flags = 0;
+
+ r->status = ~0;
+ unit->dev->ifc->rio(r);
+ }
+ }
+ free(r);
+
+ if(status == SDok || status == SDcheck)
+ return 1;
+ return 0;
+}
+
+static int
+scsirio(SDreq* r)
+{
+ /*
+ * Perform an I/O request, returning
+ * -1 failure
+ * 0 ok
+ * 1 no medium present
+ * 2 retry
+ * The contents of r may be altered so the
+ * caller should re-initialise if necesary.
+ */
+ r->status = ~0;
+ switch(r->unit->dev->ifc->rio(r)){
+ default:
+ break;
+ case SDcheck:
+ if(!(r->flags & SDvalidsense))
+ break;
+ switch(r->sense[2] & 0x0F){
+ case 0x00: /* no sense */
+ case 0x01: /* recovered error */
+ return 2;
+ case 0x06: /* check condition */
+ /*
+ * 0x28 - not ready to ready transition,
+ * medium may have changed.
+ * 0x29 - power on or some type of reset.
+ */
+ if(r->sense[12] == 0x28 && r->sense[13] == 0)
+ return 2;
+ if(r->sense[12] == 0x29)
+ return 2;
+ break;
+ case 0x02: /* not ready */
+ /*
+ * If no medium present, bail out.
+ * If unit is becoming ready, rather than not
+ * not ready, wait a little then poke it again. */
+ if(r->sense[12] == 0x3A)
+ break;
+ if(r->sense[12] != 0x04 || r->sense[13] != 0x01)
+ break;
+
+ while(waserror())
+ ;
+ tsleep(&up->sleep, return0, 0, 500);
+ poperror();
+ scsitest(r);
+ return 2;
+ default:
+ break;
+ }
+ break;
+ case SDok:
+ return 0;
+ }
+ return -1;
+}
+
+int
+scsionline(SDunit* unit)
+{
+ SDreq *r;
+ uchar *p;
+ int ok, retries;
+
+ if((r = malloc(sizeof(SDreq))) == nil)
+ return 0;
+ if((p = sdmalloc(8)) == nil){
+ free(r);
+ return 0;
+ }
+
+ ok = 0;
+
+ r->unit = unit;
+ r->lun = 0; /* ??? */
+ for(retries = 0; retries < 10; retries++){
+ /*
+ * Read-capacity is mandatory for DA, WORM, CD-ROM and
+ * MO. It may return 'not ready' if type DA is not
+ * spun up, type MO or type CD-ROM are not loaded or just
+ * plain slow getting their act together after a reset.
+ */
+ r->write = 0;
+ memset(r->cmd, 0, sizeof(r->cmd));
+ r->cmd[0] = 0x25;
+ r->cmd[1] = r->lun<<5;
+ r->clen = 10;
+ r->data = p;
+ r->dlen = 8;
+ r->flags = 0;
+
+ r->status = ~0;
+ switch(scsirio(r)){
+ default:
+ break;
+ case 0:
+ unit->sectors = (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
+ unit->secsize = (p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7];
+
+ /*
+ * Some ATAPI CD readers lie about the block size.
+ * Since we don't read audio via this interface
+ * it's okay to always fudge this.
+ */
+ if(unit->secsize == 2352)
+ unit->secsize = 2048;
+ /*
+ * Devices with removable media may return 0 sectors
+ * when they have empty media (e.g. sata dvd writers);
+ * if so, keep the count zero.
+ *
+ * Read-capacity returns the LBA of the last sector,
+ * therefore the number of sectors must be incremented.
+ */
+ if(unit->sectors != 0)
+ unit->sectors++;
+ ok = 1;
+ break;
+ case 1:
+ ok = 1;
+ break;
+ case 2:
+ continue;
+ }
+ break;
+ }
+ free(p);
+ free(r);
+
+ if(ok)
+ return ok+retries;
+ else
+ return 0;
+}
+
+int
+scsiexec(SDunit* unit, int write, uchar* cmd, int clen, void* data, int* dlen)
+{
+ SDreq *r;
+ int status;
+
+ if((r = malloc(sizeof(SDreq))) == nil)
+ return SDmalloc;
+ r->unit = unit;
+ r->lun = cmd[1]>>5; /* ??? */
+ r->write = write;
+ memmove(r->cmd, cmd, clen);
+ r->clen = clen;
+ r->data = data;
+ if(dlen)
+ r->dlen = *dlen;
+ r->flags = 0;
+
+ r->status = ~0;
+
+ /*
+ * Call the device-specific I/O routine.
+ * There should be no calls to 'error()' below this
+ * which percolate back up.
+ */
+ switch(status = unit->dev->ifc->rio(r)){
+ case SDok:
+ if(dlen)
+ *dlen = r->rlen;
+ /*FALLTHROUGH*/
+ case SDcheck:
+ /*FALLTHROUGH*/
+ default:
+ /*
+ * It's more complicated than this. There are conditions
+ * which are 'ok' but for which the returned status code
+ * is not 'SDok'.
+ * Also, not all conditions require a reqsense, might
+ * need to do a reqsense here and make it available to the
+ * caller somehow.
+ *
+ * MaƱana.
+ */
+ break;
+ }
+ sdfree(r);
+
+ return status;
+}
+
+static void
+scsifmt10(SDreq *r, int write, int lun, ulong nb, uvlong bno)
+{
+ uchar *c;
+
+ c = r->cmd;
+ if(write == 0)
+ c[0] = 0x28;
+ else
+ c[0] = 0x2A;
+ c[1] = lun<<5;
+ c[2] = bno>>24;
+ c[3] = bno>>16;
+ c[4] = bno>>8;
+ c[5] = bno;
+ c[6] = 0;
+ c[7] = nb>>8;
+ c[8] = nb;
+ c[9] = 0;
+
+ r->clen = 10;
+}
+
+static void
+scsifmt16(SDreq *r, int write, int lun, ulong nb, uvlong bno)
+{
+ uchar *c;
+
+ c = r->cmd;
+ if(write == 0)
+ c[0] = 0x88;
+ else
+ c[0] = 0x8A;
+ c[1] = lun<<5; /* so wrong */
+ c[2] = bno>>56;
+ c[3] = bno>>48;
+ c[4] = bno>>40;
+ c[5] = bno>>32;
+ c[6] = bno>>24;
+ c[7] = bno>>16;
+ c[8] = bno>>8;
+ c[9] = bno;
+ c[10] = nb>>24;
+ c[11] = nb>>16;
+ c[12] = nb>>8;
+ c[13] = nb;
+ c[14] = 0;
+ c[15] = 0;
+
+ r->clen = 16;
+}
+
+long
+scsibio(SDunit* unit, int lun, int write, void* data, long nb, uvlong bno)
+{
+ SDreq *r;
+ long rlen;
+
+ if((r = malloc(sizeof(SDreq))) == nil)
+ error(Enomem);
+ r->unit = unit;
+ r->lun = lun;
+again:
+ r->write = write;
+ if(bno >= (1ULL<<32))
+ scsifmt16(r, write, lun, nb, bno);
+ else
+ scsifmt10(r, write, lun, nb, bno);
+ r->data = data;
+ r->dlen = nb*unit->secsize;
+ r->flags = 0;
+
+ r->status = ~0;
+ switch(scsirio(r)){
+ default:
+ rlen = -1;
+ break;
+ case 0:
+ rlen = r->rlen;
+ break;
+ case 2:
+ rlen = -1;
+ if(!(r->flags & SDvalidsense))
+ break;
+ switch(r->sense[2] & 0x0F){
+ default:
+ break;
+ case 0x01: /* recovered error */
+ print("%s: recovered error at sector %llud\n",
+ unit->perm.name, bno);
+ rlen = r->rlen;
+ break;
+ case 0x06: /* check condition */
+ /*
+ * Check for a removeable media change.
+ * If so, mark it by zapping the geometry info
+ * to force an online request.
+ */
+ if(r->sense[12] != 0x28 || r->sense[13] != 0)
+ break;
+ if(unit->inquiry[1] & 0x80)
+ unit->sectors = 0;
+ break;
+ case 0x02: /* not ready */
+ /*
+ * If unit is becoming ready,
+ * rather than not not ready, try again.
+ */
+ if(r->sense[12] == 0x04 && r->sense[13] == 0x01)
+ goto again;
+ break;
+ }
+ break;
+ }
+ free(r);
+
+ return rlen;
+}
+
diff --git a/src/9vx/devfs-posix.c b/src/9vx/devfs-posix.c
@@ -4,6 +4,16 @@
#include <grp.h> /* going to regret this - getgrgid is a stack smasher */
#include <sys/socket.h>
#include <sys/un.h>
+#if defined(__FreeBSD__)
+#include <sys/disk.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#endif
+#if defined(__linux__)
+#include <linux/hdreg.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#endif
#include "lib.h"
#include "mem.h"
#include "dat.h"
@@ -24,6 +34,8 @@ static char *gidtoname(int);
static int nametouid(char*);
static int nametogid(char*);
+static vlong disksize(int, struct stat*);
+
typedef struct UnixFd UnixFd;
struct UnixFd
{
@@ -247,6 +259,7 @@ fswalk(Chan *c, Chan *nc, char **name, int nname)
static int
fsdirstat(char *path, int dev, Dir *d)
{
+ int fd;
struct stat st;
if(stat(path, &st) < 0 && lstat(path, &st) < 0)
@@ -261,6 +274,10 @@ fsdirstat(char *path, int dev, Dir *d)
d->atime = st.st_atime;
d->mtime = st.st_mtime;
d->length = st.st_size;
+ if(S_ISBLK(st.st_mode) && (fd = open(path, O_RDONLY)) >= 0){
+ d->length = disksize(fd, &st);
+ close(fd);
+ }
d->type = FsChar;
d->dev = dev;
return 0;
@@ -844,3 +861,48 @@ nametogid(char *name)
return -1;
return u->id;
}
+
+#if defined(__linux__)
+
+static vlong
+disksize(int fd, struct stat *st)
+{
+ uvlong u64;
+ long l;
+ struct hd_geometry geo;
+
+ memset(&geo, 0, sizeof geo);
+ l = 0;
+ u64 = 0;
+#ifdef BLKGETSIZE64
+ if(ioctl(fd, BLKGETSIZE64, &u64) >= 0)
+ return u64;
+#endif
+ if(ioctl(fd, BLKGETSIZE, &l) >= 0)
+ return l*512;
+ if(ioctl(fd, HDIO_GETGEO, &geo) >= 0)
+ return (vlong)geo.heads*geo.sectors*geo.cylinders*512;
+ return 0;
+}
+
+#elif defined(__FreeBSD__) && defined(DIOCGMEDIASIZE)
+
+static vlong
+disksize(int fd, struct stat *st)
+{
+ off_t mediasize;
+
+ if(ioctl(fd, DIOCGMEDIASIZE, &mediasize) >= 0)
+ return mediasize;
+ return 0;
+}
+
+#else
+
+static vlong
+disksize(int fd, struct stat *st)
+{
+ return 0;
+}
+
+#endif
diff --git a/src/9vx/devtab.c b/src/9vx/devtab.c
@@ -21,6 +21,7 @@ extern Dev srvdevtab;
extern Dev procdevtab;
extern Dev mntloopdevtab;
extern Dev dupdevtab;
+extern Dev sddevtab;
Dev *devtab[] = {
&rootdevtab, /* must be first */
@@ -39,6 +40,7 @@ Dev *devtab[] = {
&srvdevtab,
&ssldevtab,
&tlsdevtab,
+ &sddevtab,
0
};
diff --git a/src/9vx/sdloop.c b/src/9vx/sdloop.c
@@ -0,0 +1,274 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "error.h"
+
+#include "sd.h"
+
+void loopdev(char*, int);
+
+typedef struct Ctlr Ctlr;
+struct Ctlr{
+ Ctlr *next;
+ Ctlr *prev;
+
+ QLock lk;
+ SDev *sdev;
+
+ Chan *c;
+ int mode;
+ uvlong qidpath;
+};
+
+static Lock ctlrlock;
+static Ctlr *ctlrhead;
+static Ctlr *ctlrtail;
+
+SDifc sdloopifc;
+
+static SDev*
+looppnp(void)
+{
+ return nil;
+}
+
+/*
+ * Cannot error.
+ * Check that unit is available.
+ * Return 1 if so, 0 if not.
+ */
+static int
+loopverify(SDunit *u)
+{
+ return 1;
+}
+
+/*
+ * Cannot error.
+ * Check that unit is online.
+ * If media changed, return 2.
+ * If ready, return 1.
+ * If not ready, return 0.
+ */
+static int
+looponline(SDunit *unit)
+{
+ uchar buf[sizeof(Dir)+100];
+ Chan *c;
+ SDev *sdev;
+ Ctlr *ctlr;
+ Dir dir;
+ long n;
+
+ if(waserror())
+ return 0;
+
+ sdev = unit->dev;
+ ctlr = sdev->ctlr;
+ c = ctlr->c;
+ n = devtab[c->type]->stat(c, buf, sizeof buf);
+ if(convM2D(buf, n, &dir, nil) == 0)
+ error("internal error: stat error in looponline");
+ if(ctlr->qidpath != dir.qid.path){
+ unit->sectors = dir.length/512;
+ unit->secsize = 512;
+ ctlr->qidpath = dir.qid.path;
+ poperror();
+ return 2;
+ }
+ poperror();
+ return 1;
+}
+
+static int
+looprio(SDreq *r)
+{
+ SDev *sdev;
+ SDunit *unit;
+ Ctlr *ctlr;
+ uchar *cmd;
+ uvlong lba;
+ long count, n;
+ Chan *c;
+ int status;
+
+ unit = r->unit;
+ sdev = unit->dev;
+ ctlr = sdev->ctlr;
+ cmd = r->cmd;
+
+#if 0
+ if((status = sdfakescsi(r, ctlr->info, sizeof ctlr->info)) != SDnostatus){
+ /* XXX check for SDcheck here */
+ r->status = status;
+ return status;
+ }
+#endif
+
+ switch(cmd[0]){
+ case 0x28: /* read */
+ case 0x2A: /* write */
+ break;
+ default:
+ print("%s: bad cmd 0x%.2ux\n", unit->perm.name, cmd[0]);
+ r->status = SDcheck;
+ return SDcheck;
+ }
+
+ lba = (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5];
+ count = (cmd[7]<<8)|cmd[8];
+ if(r->data == nil)
+ return SDok;
+ if(r->dlen < count*512)
+ count = r->dlen/512;
+
+ c = ctlr->c;
+ if(cmd[0] == 0x28)
+ n = devtab[c->type]->read(c, r->data, count*512, lba*512);
+ else
+ n = devtab[c->type]->write(c, r->data, count*512, lba*512);
+ r->rlen = n;
+ return SDok;
+}
+
+static int
+looprctl(SDunit *unit, char *p, int l)
+{
+ Ctlr *ctlr;
+ char *e, *op;
+
+ ctlr = unit->dev->ctlr;
+ e = p+l;
+ op = p;
+
+ p = seprint(p, e, "loop %s %s\n", ctlr->mode == ORDWR ? "rw" : "ro", chanpath(ctlr->c));
+ return p - op;
+}
+
+static int
+loopwctl(SDunit *u, Cmdbuf *cmd)
+{
+ cmderror(cmd, Ebadarg);
+ return 0;
+}
+
+static void
+loopclear1(Ctlr *ctlr)
+{
+ lock(&ctlrlock);
+ if(ctlr->prev)
+ ctlr->prev->next = ctlr->next;
+ else
+ ctlrhead = ctlr;
+ if(ctlr->next)
+ ctlr->next->prev = ctlr->prev;
+ else
+ ctlrtail = ctlr->prev;
+ unlock(&ctlrlock);
+
+ cclose(ctlr->c);
+ free(ctlr);
+}
+
+static void
+loopclear(SDev *sdev)
+{
+ loopclear1(sdev->ctlr);
+}
+
+static int
+loopwtopctl(SDev *sdev, Cmdbuf *cb)
+{
+ int mode;
+
+ mode = 0;
+ if(cb->nf != 2)
+ cmderror(cb, Ebadarg);
+ if(strcmp(cb->f[0], "rw") == 0)
+ mode = ORDWR;
+ else if(strcmp(cb->f[0], "ro") == 0)
+ mode = OREAD;
+ else
+ cmderror(cb, Ebadarg);
+
+ loopdev(cb->f[1], mode);
+ return 0;
+}
+
+void
+loopdev(char *name, int mode)
+{
+ Chan *c;
+ Ctlr *volatile ctlr;
+ SDev *volatile sdev;
+
+ c = namec(name, Aopen, mode, 0);
+ ctlr = nil;
+ sdev = nil;
+ if(waserror()){
+ cclose(c);
+ if(ctlr)
+ free(ctlr);
+ if(sdev)
+ free(sdev);
+ nexterror();
+ }
+
+ ctlr = smalloc(sizeof *ctlr);
+ sdev = smalloc(sizeof *sdev);
+ sdev->ifc = &sdloopifc;
+ sdev->ctlr = ctlr;
+ sdev->nunit = 1;
+ sdev->idno = '0';
+ ctlr->sdev = sdev;
+ ctlr->c = c;
+ ctlr->mode = mode;
+ poperror();
+
+ lock(&ctlrlock);
+ ctlr->next = nil;
+ ctlr->prev = ctlrtail;
+ ctlrtail = ctlr;
+ if(ctlr->prev)
+ ctlr->prev->next = ctlr;
+ else
+ ctlrhead = ctlr;
+ unlock(&ctlrlock);
+
+ sdadddevs(sdev);
+}
+
+
+SDifc sdloopifc = {
+ "loop",
+
+ looppnp,
+ nil, /* legacy */
+ nil, /* enable */
+ nil, /* disable */
+
+ loopverify,
+ looponline,
+ looprio,
+ looprctl,
+ loopwctl,
+
+ scsibio,
+ nil, /* probe */
+ loopclear, /* clear */
+ nil,
+ loopwtopctl,
+};
+
+SDifc *sdifc[] =
+{
+ &sdloopifc,
+ nil
+};
+
+
+