626 lines
9.7 KiB
C
626 lines
9.7 KiB
C
![]() |
/*
|
||
|
* aoe sd driver, copyright © 2007 coraid
|
||
|
*/
|
||
|
|
||
|
#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"
|
||
|
#include "../port/aoe.h"
|
||
|
|
||
|
extern char Echange[];
|
||
|
extern char Enotup[];
|
||
|
|
||
|
#define uprint(...) snprint(up->genbuf, sizeof up->genbuf, __VA_ARGS__);
|
||
|
|
||
|
enum {
|
||
|
Nctlr = 32,
|
||
|
Maxpath = 128,
|
||
|
|
||
|
Probeintvl = 100, /* ms. between probes */
|
||
|
Probemax = 20, /* max probes */
|
||
|
};
|
||
|
|
||
|
enum {
|
||
|
/* sync with ahci.h */
|
||
|
Dllba = 1<<0,
|
||
|
Dsmart = 1<<1,
|
||
|
Dpower = 1<<2,
|
||
|
Dnop = 1<<3,
|
||
|
Datapi = 1<<4,
|
||
|
Datapi16= 1<<5,
|
||
|
};
|
||
|
|
||
|
static char *flagname[] = {
|
||
|
"llba",
|
||
|
"smart",
|
||
|
"power",
|
||
|
"nop",
|
||
|
"atapi",
|
||
|
"atapi16",
|
||
|
};
|
||
|
|
||
|
typedef struct Ctlr Ctlr;
|
||
|
struct Ctlr{
|
||
|
QLock;
|
||
|
|
||
|
Ctlr *next;
|
||
|
SDunit *unit;
|
||
|
|
||
|
char path[Maxpath];
|
||
|
Chan *c;
|
||
|
|
||
|
ulong vers;
|
||
|
uchar mediachange;
|
||
|
uchar flag;
|
||
|
uchar smart;
|
||
|
uchar smartrs;
|
||
|
uchar feat;
|
||
|
|
||
|
uvlong sectors;
|
||
|
char serial[20+1];
|
||
|
char firmware[8+1];
|
||
|
char model[40+1];
|
||
|
char ident[0x100];
|
||
|
};
|
||
|
|
||
|
void aoeidmove(char *p, ushort *a, unsigned n);
|
||
|
|
||
|
static Lock ctlrlock;
|
||
|
static Ctlr *head;
|
||
|
static Ctlr *tail;
|
||
|
|
||
|
SDifc sdaoeifc;
|
||
|
|
||
|
static ushort
|
||
|
gbit16(void *a)
|
||
|
{
|
||
|
uchar *i;
|
||
|
|
||
|
i = a;
|
||
|
return i[1] << 8 | i[0];
|
||
|
}
|
||
|
|
||
|
static ulong
|
||
|
gbit32(void *a)
|
||
|
{
|
||
|
ulong j;
|
||
|
uchar *i;
|
||
|
|
||
|
i = a;
|
||
|
j = i[3] << 24;
|
||
|
j |= i[2] << 16;
|
||
|
j |= i[1] << 8;
|
||
|
j |= i[0];
|
||
|
return j;
|
||
|
}
|
||
|
|
||
|
static uvlong
|
||
|
gbit64(void *a)
|
||
|
{
|
||
|
uchar *i;
|
||
|
|
||
|
i = a;
|
||
|
return (uvlong)gbit32(i+4)<<32 | gbit32(i);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
identify(Ctlr *c, ushort *id)
|
||
|
{
|
||
|
int i;
|
||
|
uchar oserial[21];
|
||
|
uvlong osectors, s;
|
||
|
|
||
|
osectors = c->sectors;
|
||
|
memmove(oserial, c->serial, sizeof c->serial);
|
||
|
|
||
|
c->feat &= ~(Dllba|Dpower|Dsmart|Dnop);
|
||
|
i = gbit16(id+83) | gbit16(id+86);
|
||
|
if(i & (1<<10)){
|
||
|
c->feat |= Dllba;
|
||
|
s = gbit64(id+100);
|
||
|
}else
|
||
|
s = gbit32(id+60);
|
||
|
|
||
|
i = gbit16(id+83);
|
||
|
if((i>>14) == 1) {
|
||
|
if(i & (1<<3))
|
||
|
c->feat |= Dpower;
|
||
|
i = gbit16(id+82);
|
||
|
if(i & 1)
|
||
|
c->feat |= Dsmart;
|
||
|
if(i & (1<<14))
|
||
|
c->feat |= Dnop;
|
||
|
}
|
||
|
|
||
|
aoeidmove(c->serial, id+10, 20);
|
||
|
aoeidmove(c->firmware, id+23, 8);
|
||
|
aoeidmove(c->model, id+27, 40);
|
||
|
|
||
|
if((osectors == 0 || osectors != s) &&
|
||
|
memcmp(oserial, c->serial, sizeof oserial) != 0){
|
||
|
c->sectors = s;
|
||
|
c->mediachange = 1;
|
||
|
c->vers++;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* must call with d qlocked */
|
||
|
static int
|
||
|
aoeidentify(Ctlr *d, SDunit *u)
|
||
|
{
|
||
|
Chan *c;
|
||
|
|
||
|
c = nil;
|
||
|
if(waserror()){
|
||
|
if(c)
|
||
|
cclose(c);
|
||
|
iprint("aoeidentify: %s\n", up->errstr);
|
||
|
nexterror();
|
||
|
}
|
||
|
|
||
|
uprint("%s/ident", d->path);
|
||
|
c = namec(up->genbuf, Aopen, OREAD, 0);
|
||
|
devtab[c->type]->read(c, d->ident, sizeof d->ident, 0);
|
||
|
|
||
|
poperror();
|
||
|
cclose(c);
|
||
|
|
||
|
d->feat = 0;
|
||
|
d->smart = 0;
|
||
|
identify(d, (ushort*)d->ident);
|
||
|
|
||
|
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, d->model, 40);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
|
||
|
/* race? */
|
||
|
if(ctlrlookup(path))
|
||
|
error(Eexist);
|
||
|
|
||
|
if((c = malloc(sizeof *c)) == nil)
|
||
|
return 0;
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/* don't call aoeprobe from within a loop; it loops internally retrying open. */
|
||
|
static SDev*
|
||
|
aoeprobe(char *path, SDev *s)
|
||
|
{
|
||
|
int n, i;
|
||
|
char *p;
|
||
|
Chan *c;
|
||
|
Ctlr *ctlr;
|
||
|
|
||
|
if((p = strrchr(path, '/')) == 0)
|
||
|
error(Ebadarg);
|
||
|
*p = 0;
|
||
|
uprint("%s/ctl", path);
|
||
|
*p = '/';
|
||
|
|
||
|
c = namec(up->genbuf, Aopen, OWRITE, 0);
|
||
|
if(waserror()) {
|
||
|
cclose(c);
|
||
|
nexterror();
|
||
|
}
|
||
|
n = uprint("discover %s", p+1);
|
||
|
devtab[c->type]->write(c, up->genbuf, n, 0);
|
||
|
poperror();
|
||
|
cclose(c);
|
||
|
|
||
|
for(i = 0; i < Probemax; i++){
|
||
|
tsleep(&up->sleep, return0, 0, Probeintvl);
|
||
|
uprint("%s/ident", path);
|
||
|
if(!waserror()) {
|
||
|
c = namec(up->genbuf, Aopen, OREAD, 0);
|
||
|
poperror();
|
||
|
cclose(c);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if(i >= Probemax)
|
||
|
error(Etimedout);
|
||
|
uprint("%s/ident", path);
|
||
|
ctlr = newctlr(path);
|
||
|
if(ctlr == nil || s == nil && (s = malloc(sizeof *s)) == nil)
|
||
|
return nil;
|
||
|
s->ctlr = ctlr;
|
||
|
s->ifc = &sdaoeifc;
|
||
|
s->nunit = 1;
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
static char *probef[32];
|
||
|
static int nprobe;
|
||
|
|
||
|
static int
|
||
|
pnpprobeid(char *s)
|
||
|
{
|
||
|
if(strlen(s) < 2)
|
||
|
return 0;
|
||
|
return s[1] == '!'? s[0]: 'e';
|
||
|
}
|
||
|
|
||
|
static SDev*
|
||
|
aoepnp(void)
|
||
|
{
|
||
|
int i, id;
|
||
|
char *p;
|
||
|
SDev *h, *t, *s;
|
||
|
|
||
|
if((p = getconf("aoedev")) == 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 = &sdaoeifc;
|
||
|
s->nunit = 1;
|
||
|
|
||
|
if(h)
|
||
|
t->next = s;
|
||
|
else
|
||
|
h = s;
|
||
|
t = s;
|
||
|
}
|
||
|
return h;
|
||
|
}
|
||
|
|
||
|
static Ctlr*
|
||
|
pnpprobe(SDev *sd)
|
||
|
{
|
||
|
ulong start;
|
||
|
char *p;
|
||
|
static int i;
|
||
|
|
||
|
if(i > nprobe)
|
||
|
return 0;
|
||
|
p = probef[i++];
|
||
|
if(strlen(p) < 2)
|
||
|
return 0;
|
||
|
if(p[1] == '!')
|
||
|
p += 2;
|
||
|
|
||
|
start = TK2MS(MACHP(0)->ticks);
|
||
|
if(waserror()){
|
||
|
print("#æ: pnpprobe failed in %lud ms: %s: %s\n",
|
||
|
TK2MS(MACHP(0)->ticks) - start, probef[i-1],
|
||
|
up->errstr);
|
||
|
return nil;
|
||
|
}
|
||
|
sd = aoeprobe(p, sd); /* does a round of probing */
|
||
|
poperror();
|
||
|
print("#æ: pnpprobe established %s in %lud ms\n",
|
||
|
probef[i-1], TK2MS(MACHP(0)->ticks) - start);
|
||
|
return sd->ctlr;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
aoeverify(SDunit *u)
|
||
|
{
|
||
|
SDev *s;
|
||
|
Ctlr *c;
|
||
|
|
||
|
s = u->dev;
|
||
|
c = s->ctlr;
|
||
|
if(c == nil && (s->ctlr = c = pnpprobe(s)) == nil)
|
||
|
return 0;
|
||
|
c->mediachange = 1;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
aoeconnect(SDunit *u, Ctlr *c)
|
||
|
{
|
||
|
qlock(c);
|
||
|
if(waserror()){
|
||
|
qunlock(c);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
aoeidentify(u->dev->ctlr, u);
|
||
|
if(c->c)
|
||
|
cclose(c->c);
|
||
|
c->c = 0;
|
||
|
uprint("%s/data", c->path);
|
||
|
c->c = namec(up->genbuf, Aopen, ORDWR, 0);
|
||
|
qunlock(c);
|
||
|
poperror();
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
aoeonline(SDunit *u)
|
||
|
{
|
||
|
Ctlr *c;
|
||
|
int r;
|
||
|
|
||
|
c = u->dev->ctlr;
|
||
|
r = 0;
|
||
|
|
||
|
if((c->feat&Datapi) && c->mediachange){
|
||
|
if(aoeconnect(u, c) == 0 && (r = scsionline(u)) > 0)
|
||
|
c->mediachange = 0;
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
if(c->mediachange){
|
||
|
if(aoeconnect(u, c) == -1)
|
||
|
return 0;
|
||
|
r = 2;
|
||
|
c->mediachange = 0;
|
||
|
u->sectors = c->sectors;
|
||
|
u->secsize = Aoesectsz;
|
||
|
} else
|
||
|
r = 1;
|
||
|
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
aoerio(SDreq *r)
|
||
|
{
|
||
|
int i, count;
|
||
|
uvlong lba;
|
||
|
char *name;
|
||
|
uchar *cmd;
|
||
|
long (*rio)(Chan*, void*, long, vlong);
|
||
|
Ctlr *c;
|
||
|
SDunit *unit;
|
||
|
|
||
|
unit = r->unit;
|
||
|
c = unit->dev->ctlr;
|
||
|
// if(c->feat & Datapi)
|
||
|
// return aoeriopkt(r, d);
|
||
|
|
||
|
cmd = r->cmd;
|
||
|
name = unit->name;
|
||
|
|
||
|
if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91){
|
||
|
// qlock(c);
|
||
|
// i = flushcache();
|
||
|
// qunlock(c);
|
||
|
// if(i == 0)
|
||
|
// return sdsetsense(r, SDok, 0, 0, 0);
|
||
|
return sdsetsense(r, SDcheck, 3, 0xc, 2);
|
||
|
}
|
||
|
|
||
|
if((i = sdfakescsi(r, c->ident, sizeof c->ident)) != SDnostatus){
|
||
|
r->status = i;
|
||
|
return i;
|
||
|
}
|
||
|
|
||
|
switch(*cmd){
|
||
|
case 0x88:
|
||
|
case 0x28:
|
||
|
rio = devtab[c->c->type]->read;
|
||
|
break;
|
||
|
case 0x8a:
|
||
|
case 0x2a:
|
||
|
rio = devtab[c->c->type]->write;
|
||
|
break;
|
||
|
default:
|
||
|
print("%s: bad cmd %#.2ux\n", name, cmd[0]);
|
||
|
r->status = SDcheck;
|
||
|
return SDcheck;
|
||
|
}
|
||
|
|
||
|
if(r->data == nil)
|
||
|
return SDok;
|
||
|
|
||
|
if(r->clen == 16){
|
||
|
if(cmd[2] || cmd[3])
|
||
|
return sdsetsense(r, SDcheck, 3, 0xc, 2);
|
||
|
lba = (uvlong)cmd[4]<<40 | (uvlong)cmd[5]<<32;
|
||
|
lba |= cmd[6]<<24 | cmd[7]<<16 | cmd[8]<<8 | cmd[9];
|
||
|
count = cmd[10]<<24 | cmd[11]<<16 | cmd[12]<<8 | cmd[13];
|
||
|
}else{
|
||
|
lba = cmd[2]<<24 | cmd[3]<<16 | cmd[4]<<8 | cmd[5];
|
||
|
count = cmd[7]<<8 | cmd[8];
|
||
|
}
|
||
|
|
||
|
count *= Aoesectsz;
|
||
|
|
||
|
if(r->dlen < count)
|
||
|
count = r->dlen & ~0x1ff;
|
||
|
|
||
|
if(waserror()){
|
||
|
if(strcmp(up->errstr, Echange) == 0 ||
|
||
|
strcmp(up->errstr, Enotup) == 0)
|
||
|
unit->sectors = 0;
|
||
|
nexterror();
|
||
|
}
|
||
|
r->rlen = rio(c->c, r->data, count, Aoesectsz * lba);
|
||
|
poperror();
|
||
|
r->status = SDok;
|
||
|
return SDok;
|
||
|
}
|
||
|
|
||
|
static char *smarttab[] = {
|
||
|
"unset",
|
||
|
"error",
|
||
|
"threshold exceeded",
|
||
|
"normal"
|
||
|
};
|
||
|
|
||
|
static char *
|
||
|
pflag(char *s, char *e, uchar f)
|
||
|
{
|
||
|
uchar i, m;
|
||
|
|
||
|
for(i = 0; i < 8; i++){
|
||
|
m = 1 << i;
|
||
|
if(f & m)
|
||
|
s = seprint(s, e, "%s ", flagname[i]);
|
||
|
}
|
||
|
return seprint(s, e, "\n");
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
aoerctl(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, "model\t%s\n", c->model);
|
||
|
p = seprint(p, e, "serial\t%s\n", c->serial);
|
||
|
p = seprint(p, e, "firm %s\n", c->firmware);
|
||
|
if(c->smartrs == 0xff)
|
||
|
p = seprint(p, e, "smart\tenable error\n");
|
||
|
else if(c->smartrs == 0)
|
||
|
p = seprint(p, e, "smart\tdisabled\n");
|
||
|
else
|
||
|
p = seprint(p, e, "smart\t%s\n", smarttab[c->smart]);
|
||
|
p = seprint(p, e, "flag ");
|
||
|
p = pflag(p, e, c->feat);
|
||
|
p = seprint(p, e, "geometry %llud %d\n", c->sectors, Aoesectsz);
|
||
|
return p-op;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
aoewctl(SDunit *, Cmdbuf *cmd)
|
||
|
{
|
||
|
cmderror(cmd, Ebadarg);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static SDev*
|
||
|
aoeprobew(DevConf *c)
|
||
|
{
|
||
|
char *p;
|
||
|
|
||
|
p = strchr(c->type, '/');
|
||
|
if(p == nil || strlen(p) > Maxpath - 11)
|
||
|
error(Ebadarg);
|
||
|
if(p[1] == '#')
|
||
|
p++; /* hack */
|
||
|
if(ctlrlookup(p))
|
||
|
error(Einuse);
|
||
|
return aoeprobe(p, 0);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
aoeclear(SDev *s)
|
||
|
{
|
||
|
delctlr((Ctlr *)s->ctlr);
|
||
|
}
|
||
|
|
||
|
static char*
|
||
|
aoertopctl(SDev *s, char *p, char *e)
|
||
|
{
|
||
|
Ctlr *c;
|
||
|
|
||
|
c = s->ctlr;
|
||
|
return seprint(p, e, "%s aoe %s\n", s->name, c->path);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
aoewtopctl(SDev *, Cmdbuf *cmd)
|
||
|
{
|
||
|
switch(cmd->nf){
|
||
|
default:
|
||
|
cmderror(cmd, Ebadarg);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
SDifc sdaoeifc = {
|
||
|
"aoe",
|
||
|
|
||
|
aoepnp,
|
||
|
nil, /* legacy */
|
||
|
nil, /* enable */
|
||
|
nil, /* disable */
|
||
|
|
||
|
aoeverify,
|
||
|
aoeonline,
|
||
|
aoerio,
|
||
|
aoerctl,
|
||
|
aoewctl,
|
||
|
|
||
|
scsibio,
|
||
|
aoeprobew, /* probe */
|
||
|
aoeclear, /* clear */
|
||
|
aoertopctl,
|
||
|
aoewtopctl,
|
||
|
};
|