ref: 26e00d014cebde8bc07725700b5ac9c0cade8171
parent: e01577f8f61023ab90d8022e75cb00027399428c
author: ftrvxmtrx <[email protected]>
date: Tue Oct 18 19:05:09 EDT 2016
aux/acpi: batteries and CPU temp reading so far
--- /dev/null
+++ b/sys/src/cmd/aux/acpi.c
@@ -1,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 *)
+{
+}
--- a/sys/src/cmd/aux/mkfile
+++ b/sys/src/cmd/aux/mkfile
@@ -5,6 +5,7 @@
9pcon\
accupoint\
acidleak\
+ acpi\
apm\
astarld\
bflz\