/* * sd loopback driver, * copyright © 2009-10 erik quanstrom */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/error.h" #include "../port/sd.h" #include "../port/netif.h" extern char Echange[]; extern char Enotup[]; #define uprint(...) snprint(up->genbuf, sizeof up->genbuf, __VA_ARGS__); enum { Maxpath = 256, Devsectsize = 512, }; typedef struct Ctlr Ctlr; struct Ctlr { QLock; Ctlr *next; SDunit *unit; char path[Maxpath]; Chan *c; uint vers; uchar drivechange; uvlong sectors; uint sectsize; }; static Lock ctlrlock; static Ctlr *head; static Ctlr *tail; SDifc sdloopifc; /* must call with c qlocked */ static void identify(Ctlr *c, SDunit *u) { int n; uvlong s, osectors; uchar buf[sizeof(Dir) + 100]; Dir dir; if(waserror()){ iprint("sdloop: identify: %s\n", up->errstr); nexterror(); } osectors = c->sectors; n = devtab[c->c->type]->stat(c->c, buf, sizeof buf); if(convM2D(buf, n, &dir, nil) == 0) error("internal error: stat error in seek"); s = dir.length / c->sectsize; poperror(); memset(u->inquiry, 0, sizeof u->inquiry); u->inquiry[2] = 2; u->inquiry[3] = 2; u->inquiry[4] = sizeof u->inquiry - 4; memmove(u->inquiry+8, c->path, 40); if(osectors == 0 || osectors != s){ c->sectors = s; c->drivechange = 1; c->vers++; } } static Ctlr* ctlrlookup(char *path) { Ctlr *c; lock(&ctlrlock); for(c = head; c; c = c->next) if(strcmp(c->path, path) == 0) break; unlock(&ctlrlock); return c; } static Ctlr* newctlr(char *path) { Ctlr *c; if(ctlrlookup(path)) error(Eexist); if((c = malloc(sizeof *c)) == nil) error(Enomem); if(waserror()){ free(c); nexterror(); } c->c = namec(path, Aopen, ORDWR, 0); poperror(); kstrcpy(c->path, path, sizeof c->path); lock(&ctlrlock); if(head != nil) tail->next = c; else head = c; tail = c; unlock(&ctlrlock); return c; } static void delctlr(Ctlr *c) { Ctlr *x, *prev; lock(&ctlrlock); for(prev = 0, x = head; x; prev = x, x = c->next) if(strcmp(c->path, x->path) == 0) break; if(x == 0){ unlock(&ctlrlock); error(Enonexist); } if(prev) prev->next = x->next; else head = x->next; if(x->next == nil) tail = prev; unlock(&ctlrlock); if(x->c) cclose(x->c); free(x); } static SDev* probe(char *path, SDev *s) { char *p; uint sectsize; Ctlr *c; sectsize = 0; if(p = strchr(path, '!')){ *p = 0; sectsize = strtoul(p + 1, 0, 0); } c = newctlr(path); c->sectsize = sectsize? sectsize: Devsectsize; if(s == nil && (s = malloc(sizeof *s)) == nil) return nil; s->ctlr = c; s->ifc = &sdloopifc; s->nunit = 1; return s; } static char *probef[32]; static int nprobe; static int pnpprobeid(char *s) { int id; if(strlen(s) < 2) return 0; id = 'l'; if(s[1] == '!') id = s[0]; return id; } static SDev* pnp(void) { int i, id; char *p; SDev *h, *t, *s; if((p = getconf("loopdev")) == 0) return 0; nprobe = tokenize(p, probef, nelem(probef)); h = t = 0; for(i = 0; i < nprobe; i++){ id = pnpprobeid(probef[i]); if(id == 0) continue; s = malloc(sizeof *s); if(s == nil) break; s->ctlr = 0; s->idno = id; s->ifc = &sdloopifc; s->nunit = 1; if(h) t->next = s; else h = s; t = s; } return h; } static Ctlr* pnpprobe(SDev *s) { char *p; static int i; if(i > nprobe) return 0; p = probef[i++]; if(strlen(p) < 2) return 0; if(p[1] == '!') p += 2; s = probe(p, s); return s->ctlr; } static int loopverify(SDunit *u) { SDev *s; Ctlr *c; s = u->dev; c = s->ctlr; if(c == nil){ if(waserror()) return 0; s->ctlr = c = pnpprobe(s); poperror(); } c->drivechange = 1; return 1; } static int connect(SDunit *u, Ctlr *c) { qlock(c); if(waserror()){ qunlock(c); return -1; } identify(u->dev->ctlr, u); qunlock(c); poperror(); return 0; } static int looponline(SDunit *u) { Ctlr *c; int r; c = u->dev->ctlr; if(c->drivechange){ if(connect(u, c) == -1) return 0; r = 2; c->drivechange = 0; u->sectors = c->sectors; u->secsize = c->sectsize; } else r = 1; return r; } static long loopbio(SDunit *u, int, int write, void *a, long count, uvlong lba) { uchar *data; int n; long (*rio)(Chan*, void*, long, vlong); Ctlr *c; c = u->dev->ctlr; data = a; if(write) rio = devtab[c->c->type]->write; else rio = devtab[c->c->type]->read; if(waserror()){ if(strcmp(up->errstr, Echange) == 0 || strcmp(up->errstr, Enotup) == 0) u->sectors = 0; nexterror(); } n = rio(c->c, data, c->sectsize * count, c->sectsize * lba); poperror(); return n; } static int looprio(SDreq *r) { int i, count, rw; uvlong lba; SDunit *u; u = r->unit; if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91) return sdsetsense(r, SDok, 0, 0, 0); if((i = sdfakescsi(r, nil, 0)) != SDnostatus) return r->status = i; if((i = sdfakescsirw(r, &lba, &count, &rw)) != SDnostatus) return i; r->rlen = loopbio(u, r->lun, rw == SDwrite, r->data, count, lba); return r->status = SDok; } static int looprctl(SDunit *u, char *p, int l) { Ctlr *c; char *e, *op; if((c = u->dev->ctlr) == nil) return 0; e = p+l; op = p; p = seprint(p, e, "path\t%s\n", c->path); p = seprint(p, e, "geometry %llud %d\n", c->sectors, c->sectsize); return p - op; } static int loopwctl(SDunit *, Cmdbuf *cmd) { cmderror(cmd, Ebadarg); return 0; } static SDev* loopprobew(DevConf *c) { char *p; p = strchr(c->type, '/'); if(p == nil || strlen(p) > Maxpath - 1) error(Ebadarg); p++; if(ctlrlookup(p)) error(Einuse); return probe(p, 0); } static void loopclear(SDev *s) { delctlr((Ctlr *)s->ctlr); } static char* looprtopctl(SDev *s, char *p, char *e) { Ctlr *c; c = s->ctlr; return seprint(p, e, "%s loop %s\n", s->name, c? c->path: ""); } static int loopwtopctl(SDev *, Cmdbuf *cmd) { switch(cmd->nf){ default: cmderror(cmd, Ebadarg); } return 0; } SDifc sdloopifc = { "loop", pnp, nil, /* legacy */ nil, /* enable */ nil, /* disable */ loopverify, looponline, looprio, looprctl, loopwctl, loopbio, loopprobew, /* probe */ loopclear, /* clear */ looprtopctl, loopwtopctl, };