/* * 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 fixedsectors; uvlong sectors; uint sectsize;}; static Lock ctlrlock;static Ctlr *head;static Ctlr *tail; SDifc sdloopifc; /* must call with c qlocked */static voididentify(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){ if(c->fixedsectors != 0) c->sectors = c->fixedsectors; else 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 voiddelctlr(Ctlr *c){ Ctlr *x, *prev; lock(&ctlrlock); for(prev = nil, x = head; x; prev = x, x = c->next) if(strcmp(c->path, x->path) == 0) break; if(x == nil){ 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, *q; uint sectsize; uvlong sectors; Ctlr *c; sectsize = 0; sectors = 0; if(p = strchr(path, '!')){ *p++ = 0; if((q = strchr(p, '!')) != nil){ sectors = strtoull(p, nil, 0); p = q + 1; } sectsize = strtoul(p, nil, 0); } c = newctlr(path); c->sectsize = sectsize? sectsize: Devsectsize; c->fixedsectors = sectors; 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 intpnpprobeid(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 nil; 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 nil; p = probef[i++]; if(strlen(p) < 2) return nil; if(p[1] == '!') p += 2; return probe(p, s)->ctlr;} static intloopverify(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 intconnect(SDunit *u, Ctlr *c){ qlock(c); if(waserror()){ qunlock(c); return -1; } identify(u->dev->ctlr, u); qunlock(c); poperror(); return 0;} static intlooponline(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 longloopbio(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 intlooprio(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)) != 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 intlooprctl(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 intloopwctl(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, nil);} static voidloopclear(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 intloopwtopctl(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,};
/*
* 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 fixedsectors;
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){
if(c->fixedsectors != 0)
c->sectors = c->fixedsectors;
else
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 = nil, x = head; x; prev = x, x = c->next)
if(strcmp(c->path, x->path) == 0)
break;
if(x == nil){
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, *q;
uint sectsize;
uvlong sectors;
Ctlr *c;
sectsize = 0;
sectors = 0;
if(p = strchr(path, '!')){
*p++ = 0;
if((q = strchr(p, '!')) != nil){
sectors = strtoull(p, nil, 0);
p = q + 1;
}
sectsize = strtoul(p, nil, 0);
}
c = newctlr(path);
c->sectsize = sectsize? sectsize: Devsectsize;
c->fixedsectors = sectors;
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 nil;
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 nil;
p = probef[i++];
if(strlen(p) < 2)
return nil;
if(p[1] == '!')
p += 2;
return probe(p, 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)) != 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, nil);
}
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,
};