diff --git a/sys/src/cmd/aux/acpi.c b/sys/src/cmd/aux/acpi.c new file mode 100644 index 000000000..328aaa3a2 --- /dev/null +++ b/sys/src/cmd/aux/acpi.c @@ -0,0 +1,594 @@ +#include +#include +#include +#include +#include <9p.h> +#include + +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<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<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 *) +{ +} diff --git a/sys/src/cmd/aux/mkfile b/sys/src/cmd/aux/mkfile index aa0043c36..e6684f46e 100644 --- a/sys/src/cmd/aux/mkfile +++ b/sys/src/cmd/aux/mkfile @@ -5,6 +5,7 @@ TARG=\ 9pcon\ accupoint\ acidleak\ + acpi\ apm\ astarld\ bflz\