ref: f949591aed3da532176d1c14835e195a53e1b13f
parent: fbd36a8797de2b5b298c06dd96003537a6305162
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Wed Sep 14 17:33:26 EDT 2022
aux/acpi: simplify/rework; support "power off" via pmctl
--- a/sys/man/8/acpi
+++ b/sys/man/8/acpi
@@ -49,9 +49,11 @@
Each line provides a current temperature reading of a specific CPU.
.TP
.B pmctl
-The
+Writing
+.I power off
+to
.B pmctl
-file currently does not provide any functionality.
+will turn off the power.
.SH SOURCE
.B /sys/src/cmd/aux/acpi.c
.SH SEE ALSO
--- a/sys/src/cmd/aux/acpi.c
+++ b/sys/src/cmd/aux/acpi.c
@@ -6,10 +6,9 @@
#include <aml.h>
typedef struct Batstat Batstat;
-typedef struct Battery Battery;
-typedef struct Dfile Dfile;
+typedef struct Bat Bat;
typedef struct Tbl Tbl;
-typedef struct Thermal Thermal;
+typedef struct Therm Therm;
struct Batstat {
int rate;
@@ -18,7 +17,7 @@
int voltage;
};
-struct Battery {
+struct Bat {
char *unit;
void *bst;
int fullcharge;
@@ -28,14 +27,6 @@
int voltage;
};
-struct Dfile {
- Qid qid;
- char *name;
- ulong mode;
- void (*read)(Req*);
- void (*write)(Req*);
-};
-
struct Tbl {
uchar sig[4];
uchar len[4];
@@ -49,43 +40,29 @@
uchar data[];
};
-struct Thermal {
+struct Therm {
uint cpus;
void *tmp;
};
enum {
- Stacksz = 16384,
Tblsz = 4+4+1+1+6+8+4+4+4,
- Qroot = 0,
- Qbattery,
- Qcputemp,
- Qpmctl,
+ Temp = 1,
+ Battery,
+ Pmctl,
- Qdisable = (uvlong)-1,
+ SLP_EN = 0x2000,
+ SLP_TM = 0x1c00,
};
-static void rootread(Req*);
-static void ctlread(Req*);
-static void ctlwrite(Req*);
-static void batteryread(Req*);
-static void tmpread(Req*);
+static ulong PM1a_CNT_BLK, PM1b_CNT_BLK, SLP_TYPa, SLP_TYPb;
+static ulong GPE0_BLK, GPE1_BLK, GPE0_BLK_LEN, GPE1_BLK_LEN;
+static int ec, mem, iofd[5], nbats, ntherms, facp;
+static char *uid = "pm", *units[] = {"mW", "mA"};
+static Therm therms[16];
+static Bat bats[4];
-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},
- {{Qpmctl}, "pmctl", 0666, ctlread, ctlwrite},
-};
-
static char*
eisaid(void *v)
{
@@ -96,12 +73,12 @@
if(amltag(v) == 's')
return v;
b = amlint(v);
- for(l = 0, i=24; i>=0; i -= 8, b >>= 8)
+ 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)
+ for(i = 6; i >= 3; i--, l >>= 4)
id[i] = "0123456789ABCDEF"[l & 0xF];
- for(i=2; i>=0; i--, l >>= 5)
+ for(i = 2; i >= 0; i--, l >>= 5)
id[i] = '@' + (l & 0x1F);
return id;
}
@@ -112,12 +89,11 @@
void *p;
char *id;
id = eisaid(amlval(amlwalk(dot, "^_HID")));
- if (id == nil || strcmp(id, "PNP0C09") != 0)
+ if(id == nil || strcmp(id, "PNP0C09") != 0)
return 1;
p = amlwalk(dot, "^_REG");
- if (p != nil) {
+ if(p != nil)
amleval(p, "ii", 0x3, 1, nil);
- }
return 1;
}
@@ -125,7 +101,7 @@
enumbat(void *dot, void *)
{
void *p, *r, **rr;
- Battery *b;
+ Bat *b;
int n;
if(nbats >= nelem(bats))
@@ -147,7 +123,7 @@
else
b->unit = units[n];
b->capacity = amlint(rr[1]);
- if((int)b->capacity < 0) /* even though _STA tells it's there */
+ if(b->capacity < 0) /* even though _STA tells it's there */
return 1;
b->fullcharge = amlint(rr[2]);
b->voltage = amlint(rr[4]);
@@ -167,19 +143,17 @@
{
void *r, **rr;
char s[64];
- int i, n;
uint cpus;
+ int i, n;
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;
- }
+ for(n = strlen(s)-1; 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;
}
}
}
@@ -195,7 +169,7 @@
}
static int
-batstat(Battery *b, Batstat *s)
+batstat(Bat *b, Batstat *s)
{
void *r, **rr;
@@ -212,244 +186,186 @@
}
static void
-batteryread(Req *r)
+batteryread(char *s, char *e)
{
- char buf[nelem(bats)*120], *ep, *p, *state;
- Battery *b;
+ int n, x, hh, mm, ss;
+ char *state;
Batstat st;
- int n, x, h, m, s;
+ Bat *b;
- 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);
+ state = "unknown";
+ ss = x = 0;
- 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);
+ if(batstat(b, &st) == 0){
+ if(st.state & 4)
+ state = "critical";
+ else if(st.state & 1)
+ state = "discharging";
+ else if(st.state & 2)
+ state = "charging";
+ if(st.rate > 0 && (st.state & 2) == 0)
+ ss = st.capacity * 3600 / st.rate;
+ if(bats[n].fullcharge > 0){
+ x = st.capacity * 100 / bats[n].fullcharge;
+ if(st.state & 2)
+ ss = (bats[n].fullcharge - st.capacity) * 3600 / st.rate;
+ }
+ }else{
+ memset(&st, 0, sizeof(st));
}
- 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",
+
+ hh = ss / 3600;
+ ss -= 3600 * (ss / 3600);
+ mm = ss / 60;
+ ss -= 60 * (ss / 60);
+ s = seprint(s, e, "%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,
+ hh, mm, ss,
state
);
}
-
- readstr(r, buf);
- respond(r, nil);
}
static void
-tmpread(Req *r)
+tmpread(char *s, char *e)
{
- char buf[32], *ep, *p;
void *er;
int n, t;
- p = buf;
- ep = buf + sizeof(buf);
-
for(n = 0; n < ntherms; n++){
- t = 0;
+ t = 2732;
if(amleval(therms[n].tmp, "", &er) >= 0)
t = amlint(er);
- p += snprint(p, ep-p, "%d.0\n", (t - 2732)/10);
+ s = seprint(s, e, "%d.0\n", (t - 2732)/10);
}
-
- readstr(r, buf);
- respond(r, nil);
}
static void
-ctlread(Req *r)
+wirecpu0(void)
{
- respond(r, "no.");
+ char buf[128];
+ int ctl;
+
+ snprint(buf, sizeof(buf), "/proc/%d/ctl", getpid());
+ if((ctl = open(buf, OWRITE)) < 0){
+ snprint(buf, sizeof(buf), "#p/%d/ctl", getpid());
+ if((ctl = open(buf, OWRITE)) < 0)
+ return;
+ }
+ write(ctl, "wired 0", 7);
+ close(ctl);
}
static void
-ctlwrite(Req *r)
+outw(long addr, ushort val)
{
- respond(r, "no.");
-}
+ uchar buf[2];
-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;
+ if(addr == 0)
+ return;
+ buf[0] = val;
+ buf[1] = val >> 8;
+ pwrite(iofd[2], buf, 2, addr);
}
-char*
-estrdup(char *s)
+static void
+poweroff(void)
{
- int l;
- char *t;
+ int n;
- if (s == nil)
- return nil;
- l = strlen(s)+1;
- t = emalloc(l);
- memcpy(t, s, l);
- setmalloctag(t, getcallerpc(&s));
- return t;
-}
+ if(facp == 0){
+ werrstr("no FACP");
+ return;
+ }
-static int
-fillstat(uvlong path, Dir *d, int doalloc)
-{
- int i;
+ wirecpu0();
- 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;
+ /* disable GPEs */
+ for(n = 0; GPE0_BLK > 0 && n < GPE0_BLK_LEN/2; n += 2){
+ outw(GPE0_BLK + GPE0_BLK_LEN/2 + n, 0); /* EN */
+ outw(GPE0_BLK + n, 0xffff); /* STS */
}
-
- 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;
- }
+ for(n = 0; GPE1_BLK > 0 && n < GPE1_BLK_LEN/2; n += 2){
+ outw(GPE1_BLK + GPE1_BLK_LEN/2 + n, 0); /* EN */
+ outw(GPE1_BLK + n, 0xffff); /* STS */
}
- 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;
+ outw(PM1a_CNT_BLK, ((SLP_TYPa << 10) & SLP_TM) | SLP_EN);
+ outw(PM1b_CNT_BLK, ((SLP_TYPb << 10) & SLP_TM) | SLP_EN);
+ sleep(100);
- case Qbattery:
- case Qcputemp:
- if(r->ifcall.mode == OREAD){
- respond(r, nil);
- return;
- }
- break;
+ /*
+ * The SetSystemSleeping() example from the ACPI spec
+ * writes the same value in both registers. But Linux/BSD
+ * write distinct values from the _Sx package (like the
+ * code above). The _S5 package on a HP DC5700 is
+ * Package(0x2){0x0, 0x7} and writing SLP_TYPa of 0 to
+ * PM1a_CNT_BLK seems to have no effect but 0x7 seems
+ * to work fine. So trying the following as a last effort.
+ */
+ SLP_TYPa |= SLP_TYPb;
+ outw(PM1a_CNT_BLK, ((SLP_TYPa << 10) & SLP_TM) | SLP_EN);
+ outw(PM1b_CNT_BLK, ((SLP_TYPa << 10) & SLP_TM) | SLP_EN);
+ sleep(100);
- case Qpmctl:
- if((r->ifcall.mode & ~(OTRUNC|OREAD|OWRITE|ORDWR)) == 0){
- respond(r, nil);
- return;
- }
- break;
- }
- respond(r, "permission denied");
- return;
+ werrstr("acpi failed");
}
static void
-fsstat(Req *r)
+pmctlread(char *s, char *e)
{
- fillstat(r->fid->qid.path, &r->d, 1);
- respond(r, nil);
+ USED(s, e);
}
static void
fsread(Req *r)
{
- dfile[r->fid->qid.path].read(r);
-}
+ char msg[512], *s, *e;
+ void *aux;
-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++;
+ s = msg;
+ e = s + sizeof(msg);
+ *s = 0;
+ aux = r->fid->file->aux;
+ if(r->ifcall.offset == 0){
+ if(aux == (void*)Temp)
+ tmpread(s, e);
+ else if(aux == (void*)Battery)
+ batteryread(s, e);
+ else if(aux == (void*)Pmctl)
+ pmctlread(s, e);
}
- r->fid->aux = (void*)offset;
- r->ofcall.count = p - r->ofcall.data;
+
+ readstr(r, msg);
respond(r, nil);
}
static void
-fsattach(Req *r)
+fswrite(Req *r)
{
- if(r->ifcall.aname && r->ifcall.aname[0]){
- respond(r, "invalid attach specifier");
+ char msg[256], *f[4];
+ void *aux;
+ int nf;
+
+ snprint(msg, sizeof(msg), "%.*s",
+ utfnlen((char*)r->ifcall.data, r->ifcall.count), (char*)r->ifcall.data);
+ nf = tokenize(msg, f, nelem(f));
+ aux = r->fid->file->aux;
+ if(aux == (void*)Pmctl){
+ if(nf == 2 && strcmp(f[0], "power") == 0 && strcmp(f[1], "off") == 0)
+ poweroff(); /* should not go any further here */
+ else
+ werrstr("invalid ctl message");
+ responderror(r);
return;
}
- r->fid->qid = dfile[0].qid;
- r->ofcall.qid = dfile[0].qid;
+
+ r->ofcall.count = r->ifcall.count;
respond(r, nil);
}
@@ -460,26 +376,24 @@
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,
+static Srv fs = {
.read = fsread,
.write = fswrite,
- .stat = fsstat,
};
+static ulong
+get32(uchar *p)
+{
+ return p[3]<<24 | p[2]<<16 | p[1]<<8 | p[0];
+}
+
void
threadmain(int argc, char **argv)
{
char *mtpt, *srv;
- Tbl *t;
+ void *r, **rr;
int fd, n, l;
+ Tbl *t;
mtpt = "/dev";
srv = nil;
@@ -535,9 +449,23 @@
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)
+ }else if(memcmp("SSDT", t->sig, 4) == 0){
amlload(t->data, l);
+ }else if(memcmp("FACP", t->sig, 4) == 0){
+ facp = 1;
+ PM1a_CNT_BLK = get32(((uchar*)t) + 64);
+ PM1b_CNT_BLK = get32(((uchar*)t) + 68);
+ GPE0_BLK = get32(((uchar*)t) + 80);
+ GPE1_BLK = get32(((uchar*)t) + 84);
+ GPE0_BLK_LEN = *(((uchar*)t) + 92);
+ GPE1_BLK_LEN = *(((uchar*)t) + 93);
+ }
}
+ if(amleval(amlwalk(amlroot, "_S5"), "", &r) >= 0 && amltag(r) == 'p' && amllen(r) >= 2){
+ rr = amlval(r);
+ SLP_TYPa = amlint(rr[0]);
+ SLP_TYPb = amlint(rr[1]);
+ }
close(fd);
amlenum(amlroot, "_HID", enumec, nil);
@@ -544,18 +472,18 @@
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;
+ fs.tree = alloctree(uid, uid, DMDIR|0555, nil);
+ if(nbats > 0)
+ createfile(fs.tree->root, "battery", uid, 0444, (void*)Battery);
+ if(ntherms > 0)
+ createfile(fs.tree->root, "cputemp", uid, 0444, (void*)Temp);
+ createfile(fs.tree->root, "pmctl", uid, 0666, (void*)Pmctl);
threadpostmountsrv(&fs, srv, mtpt, MAFTER);
threadexits(nil);
fail:
- fprint(2, "%r\n");
- amlexit();
- threadexitsall("acpi");
+ sysfatal("%r");
}
static int