code: plan9front

Download patch

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