aux/acpi: batteries and CPU temp reading so far
This commit is contained in:
parent
e01577f8f6
commit
26e00d014c
2 changed files with 595 additions and 0 deletions
594
sys/src/cmd/aux/acpi.c
Normal file
594
sys/src/cmd/aux/acpi.c
Normal file
|
@ -0,0 +1,594 @@
|
|||
#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 id;
|
||||
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,
|
||||
};
|
||||
|
||||
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 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->id = amlint(amlwalk(dot, "^_UID"));
|
||||
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 %d %s %d %d %d %d %d %s %d %d %02d:%02d:%02d %s\n",
|
||||
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, cpu;
|
||||
|
||||
p = buf;
|
||||
ep = buf + sizeof(buf);
|
||||
|
||||
for(n = 0; n < ntherms; n++){
|
||||
t = 0;
|
||||
if(amleval(therms[n].tmp, "", &er) >= 0)
|
||||
t = amlint(er);
|
||||
for(cpu = 0; cpu < 32; cpu++){
|
||||
if(therms[n].cpus & (1<<cpu))
|
||||
p += snprint(p, ep-p, "%d %d\n", cpu, (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(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; p += n){
|
||||
if(fillstat(offset, &d, 0) < 0)
|
||||
break;
|
||||
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 [-Di] [-d /dev] [-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 'i':
|
||||
fs.nopipe++;
|
||||
fs.infd = 0;
|
||||
fs.outfd = 1;
|
||||
mtpt = nil;
|
||||
break;
|
||||
case 'm':
|
||||
mtpt = EARGF(usage());
|
||||
break;
|
||||
case 's':
|
||||
srv = EARGF(usage());
|
||||
break;
|
||||
}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, "_BIF", enumbat, nil);
|
||||
amlenum(amlroot, "_PSL", enumtmp, nil);
|
||||
|
||||
rfork(RFNOTEG);
|
||||
threadpostmountsrv(&fs, srv, mtpt, MREPL);
|
||||
return;
|
||||
|
||||
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 *)
|
||||
{
|
||||
}
|
|
@ -5,6 +5,7 @@ TARG=\
|
|||
9pcon\
|
||||
accupoint\
|
||||
acidleak\
|
||||
acpi\
|
||||
apm\
|
||||
astarld\
|
||||
bflz\
|
||||
|
|
Loading…
Reference in a new issue