plan9fox/sys/src/cmd/aux/acpi.c
2021-01-22 10:57:43 +01:00

633 lines
11 KiB
C

#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include <aml.h>
typedef struct Batstat Batstat;
typedef struct Battery Battery;
typedef struct Dfile Dfile;
typedef struct Tbl Tbl;
typedef struct Thermal Thermal;
struct Batstat {
int rate;
int capacity;
int state;
int voltage;
};
struct Battery {
char *unit;
void *bst;
int fullcharge;
int capacity;
int capacitywarn;
int capacitylow;
int voltage;
};
struct Dfile {
Qid qid;
char *name;
ulong mode;
void (*read)(Req*);
void (*write)(Req*);
};
struct Tbl {
uchar sig[4];
uchar len[4];
uchar rev;
uchar csum;
uchar oemid[6];
uchar oemtid[8];
uchar oemrev[4];
uchar cid[4];
uchar crev[4];
uchar data[];
};
struct Thermal {
uint cpus;
void *tmp;
};
enum {
Stacksz = 16384,
Tblsz = 4+4+1+1+6+8+4+4+4,
Qroot = 0,
Qbattery,
Qcputemp,
Qctl,
Qdisable = (uvlong)-1,
};
static void rootread(Req*);
static void ctlread(Req*);
static void ctlwrite(Req*);
static void batteryread(Req*);
static void tmpread(Req*);
int ec, mem, iofd[5], nbats, ntherms, rp, wp;
char *units[] = {"mW", "mA"};
Battery bats[4];
Thermal therms[4];
Channel *creq, *cevent;
Req *rlist, **tailp;
Dfile dfile[] = {
{{Qroot,0,QTDIR}, "/", DMDIR|0555, rootread, nil},
{{Qbattery}, "battery", 0444, batteryread, nil},
{{Qcputemp}, "cputemp", 0444, tmpread, nil},
{{Qctl}, "ctl", 0666, ctlread, ctlwrite},
};
static char*
eisaid(void *v)
{
static char id[8];
ulong b, l;
int i;
if(amltag(v) == 's')
return v;
b = amlint(v);
for(l = 0, i=24; i>=0; i -= 8, b >>= 8)
l |= (b & 0xFF) << i;
id[7] = 0;
for(i=6; i>=3; i--, l >>= 4)
id[i] = "0123456789ABCDEF"[l & 0xF];
for(i=2; i>=0; i--, l >>= 5)
id[i] = '@' + (l & 0x1F);
return id;
}
static int
enumec(void *dot, void *)
{
void *p;
char *id;
id = eisaid(amlval(amlwalk(dot, "^_HID")));
if (id == nil || strcmp(id, "PNP0C09") != 0)
return 1;
p = amlwalk(dot, "^_REG");
if (p != nil) {
amleval(p, "ii", 0x3, 1, nil);
}
return 1;
}
static int
enumbat(void *dot, void *)
{
void *p, *r, **rr;
Battery *b;
int n;
if(nbats >= nelem(bats))
return 1;
if((p = amlwalk(dot, "^_STA")) == nil)
return 1;
if(amleval(p, "", &r) < 0 || (amlint(r)&3) != 3)
return 1;
if(amleval(dot, "", &r) < 0) /* _BIF */
return 1;
if(r == nil || amltag(r) != 'p' || amllen(r) < 7)
return 1;
rr = amlval(r);
b = &bats[nbats];
if((n = amlint(rr[0])) >= nelem(units) || n < 0)
b->unit = "??";
else
b->unit = units[n];
b->capacity = amlint(rr[1]);
if((int)b->capacity < 0) /* even though _STA tells it's there */
return 1;
b->fullcharge = amlint(rr[2]);
b->voltage = amlint(rr[4]);
b->capacitywarn = amlint(rr[5]);
b->capacitylow = amlint(rr[6]);
b->bst = amlwalk(dot, "^_BST");
if(b->bst != nil){
amltake(b->bst);
nbats++;
}
return 1;
}
static int
enumtmp(void *dot, void *)
{
void *r, **rr;
char s[64];
int i, n;
uint cpus;
cpus = 0;
if(ntherms < nelem(therms) && amleval(dot, "", &r) >= 0 && amllen(r) > 0 && (rr = amlval(r)) != nil){
for(i = 0; i < amllen(r); i++){
snprint(s, sizeof(s), "%N", amlval(rr[i]));
if((n = strlen(s)) > 0){
for(n--; n > 3; n--){
if(s[n-2] == 'C' && s[n-1] == 'P' && s[n] == 'U' && s[n+1] >= '0' && s[n+1] <= '9'){
cpus |= 1<<atoi(&s[n+1]);
break;
}
}
}
}
}
if(cpus != 0 && (dot = amlwalk(dot, "^_TMP")) != nil){
therms[ntherms].cpus = cpus;
therms[ntherms].tmp = dot;
ntherms++;
}
return 1;
}
static int
batstat(Battery *b, Batstat *s)
{
void *r, **rr;
if(amleval(b->bst, "", &r) < 0)
return -1;
if(r == nil || amltag(r) != 'p' || amllen(r) < 4)
return -1;
rr = amlval(r);
s->state = amlint(rr[0]);
s->rate = amlint(rr[1]);
s->capacity = amlint(rr[2]);
s->voltage = amlint(rr[3]);
return 0;
}
static void
batteryread(Req *r)
{
char buf[nelem(bats)*120], *ep, *p, *state;
Battery *b;
Batstat st;
int n, x, h, m, s;
p = buf;
*p = 0;
ep = buf + sizeof(buf);
for(n = 0; n < nbats; n++){
b = &bats[n];
st.rate = -1;
st.capacity = -1;
st.state = 0;
st.voltage = -1;
batstat(b, &st);
h = m = s = 0;
if(st.state & 4)
state = "critical";
else if(st.state & 1)
state = "discharging";
else if(st.state & 2)
state = "charging";
else
state = "unknown";
if(st.rate > 0){
s = ((st.state & 2) ? bats[n].fullcharge - st.capacity : st.capacity) * 3600 / st.rate;
h = s/3600;
s -= 3600*(s/3600);
m = s/60;
s -= 60*(s/60);
}
x = bats[n].fullcharge > 0 ? st.capacity * 100 / bats[n].fullcharge : -1;
p += snprint(p, ep-p, "%d %s %d %d %d %d %d %s %d %d %02d:%02d:%02d %s\n",
x,
bats[n].unit, st.capacity, b->fullcharge, b->capacity, b->capacitywarn, b->capacitylow,
"mV", st.voltage, b->voltage,
h, m, s,
state
);
}
readstr(r, buf);
respond(r, nil);
}
static void
tmpread(Req *r)
{
char buf[32], *ep, *p;
void *er;
int n, t;
p = buf;
ep = buf + sizeof(buf);
for(n = 0; n < ntherms; n++){
t = 0;
if(amleval(therms[n].tmp, "", &er) >= 0)
t = amlint(er);
p += snprint(p, ep-p, "%d\n", (t - 2732)/10);
}
readstr(r, buf);
respond(r, nil);
}
static void
ctlread(Req *r)
{
respond(r, "no.");
}
static void
ctlwrite(Req *r)
{
respond(r, "no.");
}
void*
emalloc(ulong n)
{
void *v;
v = malloc(n);
if(v == nil)
sysfatal("out of memory allocating %lud", n);
memset(v, 0, n);
setmalloctag(v, getcallerpc(&n));
return v;
}
char*
estrdup(char *s)
{
int l;
char *t;
if (s == nil)
return nil;
l = strlen(s)+1;
t = emalloc(l);
memcpy(t, s, l);
setmalloctag(t, getcallerpc(&s));
return t;
}
static int
fillstat(uvlong path, Dir *d, int doalloc)
{
int i;
for(i=0; i<nelem(dfile); i++)
if(path == dfile[i].qid.path)
break;
if(i == nelem(dfile))
return -1;
memset(d, 0, sizeof *d);
d->uid = doalloc ? estrdup("acpi") : "acpi";
d->gid = doalloc ? estrdup("acpi") : "acpi";
d->length = 0;
d->name = doalloc ? estrdup(dfile[i].name) : dfile[i].name;
d->mode = dfile[i].mode;
d->atime = d->mtime = time(0);
d->qid = dfile[i].qid;
return 0;
}
static char*
fswalk1(Fid *fid, char *name, Qid *qid)
{
int i;
if(strcmp(name, "..") == 0){
*qid = dfile[0].qid;
fid->qid = *qid;
return nil;
}
for(i = 1; i < nelem(dfile); i++){ /* i=1: 0 is root dir */
if(dfile[i].qid.path != Qdisable && strcmp(dfile[i].name, name) == 0){
*qid = dfile[i].qid;
fid->qid = *qid;
return nil;
}
}
return "file does not exist";
}
static void
fsopen(Req *r)
{
switch((ulong)r->fid->qid.path){
case Qroot:
r->fid->aux = (void*)0;
respond(r, nil);
return;
case Qbattery:
case Qcputemp:
if(r->ifcall.mode == OREAD){
respond(r, nil);
return;
}
break;
case Qctl:
if((r->ifcall.mode & ~(OTRUNC|OREAD|OWRITE|ORDWR)) == 0){
respond(r, nil);
return;
}
break;
}
respond(r, "permission denied");
return;
}
static void
fsstat(Req *r)
{
fillstat(r->fid->qid.path, &r->d, 1);
respond(r, nil);
}
static void
fsread(Req *r)
{
dfile[r->fid->qid.path].read(r);
}
static void
fswrite(Req *r)
{
dfile[r->fid->qid.path].write(r);
}
static void
rootread(Req *r)
{
int n;
uvlong offset;
char *p, *ep;
Dir d;
offset = r->ifcall.offset == 0 ? 0 : (uvlong)r->fid->aux;
p = r->ofcall.data;
ep = r->ofcall.data + r->ifcall.count;
if(offset == 0) /* skip root */
offset = 1;
for(; p+2 < ep && offset < nelem(dfile); p += n){
if(fillstat(offset, &d, 0) < 0)
n = 0;
else{
n = convD2M(&d, (uchar*)p, ep-p);
if(n <= BIT16SZ)
break;
}
offset++;
}
r->fid->aux = (void*)offset;
r->ofcall.count = p - r->ofcall.data;
respond(r, nil);
}
static void
fsattach(Req *r)
{
if(r->ifcall.aname && r->ifcall.aname[0]){
respond(r, "invalid attach specifier");
return;
}
r->fid->qid = dfile[0].qid;
r->ofcall.qid = dfile[0].qid;
respond(r, nil);
}
static void
usage(void)
{
fprint(2, "usage: aux/acpi [-Dp] [-m /mnt/acpi] [-s service]\n");
exits("usage");
}
static ulong
get32(uchar *p){
return p[3]<<24 | p[2]<<16 | p[1]<<8 | p[0];
}
Srv fs = {
.attach = fsattach,
.walk1 = fswalk1,
.open = fsopen,
.read = fsread,
.write = fswrite,
.stat = fsstat,
};
void
threadmain(int argc, char **argv)
{
char *mtpt, *srv;
Tbl *t;
int fd, n, l;
mtpt = "/mnt/acpi";
srv = nil;
ARGBEGIN{
case 'D':
chatty9p = 1;
break;
case 'm':
mtpt = EARGF(usage());
break;
case 's':
srv = EARGF(usage());
break;
case 'p':
amldebug++;
break;
default:
usage();
}ARGEND
if((ec = open("/dev/ec", ORDWR)) < 0)
if((ec = open("#P/ec", ORDWR)) < 0)
goto fail;
if((mem = open("/dev/acpimem", ORDWR)) < 0)
mem = open("#P/acpimem", ORDWR);
if((iofd[1] = open("/dev/iob", ORDWR)) < 0)
if((iofd[1] = open("#P/iob", ORDWR)) < 0)
goto fail;
if((iofd[2] = open("/dev/iow", ORDWR)) < 0)
if((iofd[2] = open("#P/iow", ORDWR)) < 0)
goto fail;
if((iofd[4] = open("/dev/iol", ORDWR)) < 0)
if((iofd[4] = open("#P/iol", ORDWR)) < 0)
goto fail;
if((fd = open("/dev/acpitbls", OREAD)) < 0)
if((fd = open("#P/acpitbls", OREAD)) < 0)
goto fail;
amlinit();
for(;;){
t = malloc(sizeof(*t));
if((n = readn(fd, t, Tblsz)) <= 0)
break;
if(n != Tblsz)
goto fail;
l = get32(t->len);
if(l < Tblsz)
goto fail;
l -= Tblsz;
t = realloc(t, sizeof(*t) + l);
if(readn(fd, t->data, l) != l)
goto fail;
if(memcmp("DSDT", t->sig, 4) == 0){
amlintmask = (~0ULL) >> (t->rev <= 1)*32;
amlload(t->data, l);
}else if(memcmp("SSDT", t->sig, 4) == 0)
amlload(t->data, l);
}
close(fd);
amlenum(amlroot, "_HID", enumec, nil);
amlenum(amlroot, "_BIF", enumbat, nil);
amlenum(amlroot, "_PSL", enumtmp, nil);
if(nbats < 1)
dfile[Qbattery].qid.path = Qdisable;
if(ntherms < 1)
dfile[Qcputemp].qid.path = Qdisable;
threadpostmountsrv(&fs, srv, mtpt, MREPL);
threadexits(nil);
fail:
fprint(2, "%r\n");
amlexit();
threadexitsall("acpi");
}
static int
readec(Amlio *, void *data, int len, int off)
{
return pread(ec, data, len, off);
}
static int
writeec(Amlio *, void *data, int len, int off)
{
return pwrite(ec, data, len, off);
}
static int
readio(Amlio *io, void *data, int len, int port)
{
assert(len == 1 || len == 2 || len == 4);
return pread(iofd[len], data, len, io->off+port);
}
static int
writeio(Amlio *io, void *data, int len, int port)
{
assert(len == 1 || len == 2 || len == 4);
return pwrite(iofd[len], data, len, io->off+port);
}
static int
memread(Amlio *io, void *data, int len, int addr)
{
return pread(mem, data, len, io->off+addr);
}
static int
memwrite(Amlio *io, void *data, int len, int addr)
{
return pwrite(mem, data, len, io->off+addr);
}
static int
dummy(Amlio *, void *, int len, int)
{
return len;
}
int
amlmapio(Amlio *io)
{
switch(io->space){
case EbctlSpace:
io->read = readec;
io->write = writeec;
break;
case IoSpace:
io->read = readio;
io->write = writeio;
break;
case MemSpace:
io->read = mem >= 0 ? memread : dummy;
io->write = mem >= 0 ? memwrite : dummy;
break;
default:
io->read = dummy;
io->write = dummy;
break;
}
return 0;
}
void
amlunmapio(Amlio *)
{
}