ref: c8e25d2a18c0395431abc5818a1d2f0561b0181f
dir: /sys/src/cmd/aux/acpi.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.0\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/pm] [-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/pm"; 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 *) { }