code: plan9front

Download patch

ref: e1949eedf6893c76472360189b6539bc0d573378
parent: a945c6f5b1f69b9bf7a6a3a5404b14b9fe6c5e5c
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Thu Oct 6 19:21:16 EDT 2022

reform/pm: add keyboard light level controlling to /dev/light

--- a/sys/man/1/reform
+++ b/sys/man/1/reform
@@ -111,7 +111,7 @@
 .TP
 .B light
 Provides a way to control the backlight of the built-in LCD by
-writing \fIlcd [-+]N\fR,
+writing \fIlcd [-+]N\fR or \fIkbd [-+]N\fR,
 where
 .I N
 is expressed in percentage, either as an absolute value (0-100) or
@@ -174,8 +174,9 @@
 .B Light
 was chosen as a shorter alternative to
 .BR brightness .
-In the future it might support controlling keyboard and trackball
-light levels.
+.PP
+Current keyboard light level reading is only an indication, there is
+no way to get the actual value from the keyboard.
 .PP
 Values displayed in the
 .B battery
--- a/sys/src/cmd/reform/pm.c
+++ b/sys/src/cmd/reform/pm.c
@@ -8,6 +8,7 @@
 {
 	Mhz = 1000*1000,
 	Pwmsrcclk = 25*Mhz,
+	Kbdlightmax = 8,
 
 	Scharge = 0,
 	Sovervolted,
@@ -24,6 +25,9 @@
 	Battery,
 	Pmctl,
 
+	Lcd = 0,
+	Kbd,
+
 	PWMSAR = 0x0c/4,
 	PWMPR = 0x10/4,
 
@@ -70,6 +74,8 @@
 
 static Reqqueue *lpcreq;
 static u32int *pwm2, *tmu, *spi2;
+static int kbdlight = 0;
+static int kbdhidfd = -1;
 static char *uid = "pm";
 
 static void
@@ -86,9 +92,61 @@
 	return base != nil ? base[reg] : -1;
 }
 
-static void
-setlight(int p)
+static char *
+readall(int f)
 {
+	int bufsz, sz, n;
+	char *s;
+
+	bufsz = 2047;
+	s = nil;
+	for(sz = 0;; sz += n){
+		if(bufsz-sz < 2048){
+			bufsz *= 2;
+			s = realloc(s, bufsz);
+		}
+		if((n = readn(f, s+sz, bufsz-sz-1)) < 1)
+			break;
+	}
+	if(n < 0 || sz < 1){
+		if(n == 0)
+			werrstr("empty");
+		free(s);
+		return nil;
+	}
+	s[sz] = 0;
+
+	return s;
+}
+
+static int
+openkbdhid(void)
+{
+	char path[32], *s, *k, *e;
+	int f;
+
+	if(kbdhidfd >= 0)
+		return kbdhidfd;
+
+	if((f = open("/dev/usb/ctl", OREAD)) >= 0){
+		if((s = readall(f)) != nil &&
+			(k = strstr(s, "MNT 'Reform Keyboard'")) != nil &&
+			(e = strchr(k+22, ' ')) != nil){
+			*e = 0;
+			snprint(path, sizeof(path), "/dev/hidU%sctl", k+22);
+			if((kbdhidfd = open(path, OWRITE)) >= 0)
+				write(kbdhidfd, "rawon", 5);
+		}
+		free(s);
+		close(f);
+	}
+
+	return kbdhidfd;
+}
+
+static int
+setlight(int k, int p)
+{
 	u32int v;
 
 	if(p < 0)
@@ -96,17 +154,37 @@
 	if(p > 100)
 		p = 100;
 
-	v = Pwmsrcclk / rd(pwm2, PWMSAR);
-	wr(pwm2, PWMPR, (Pwmsrcclk/(v*p/100))-2);
+	if(k == Lcd){
+		v = Pwmsrcclk / rd(pwm2, PWMSAR);
+		wr(pwm2, PWMPR, (Pwmsrcclk/(v*p/100))-2);
+		return 0;
+	}else if(k == Kbd && (kbdhidfd = openkbdhid()) >= 0){
+		v = Kbdlightmax*p/100;
+		if(fprint(kbdhidfd, "LITE%c", '0'+v) > 0){
+			kbdlight = v;
+			return 0;
+		}
+		close(kbdhidfd);
+		kbdhidfd = -1;
+		kbdlight = 0;
+	}
+
+	return -1;
 }
 
 static int
-getlight(void)
+getlight(int k)
 {
 	u32int m, v;
 
-	m = Pwmsrcclk / rd(pwm2, PWMSAR);
-	v = Pwmsrcclk / (rd(pwm2, PWMPR)+2);
+	SET(m, v);
+	if(k == Lcd){
+		m = Pwmsrcclk / rd(pwm2, PWMSAR);
+		v = Pwmsrcclk / (rd(pwm2, PWMPR)+2);
+	}else if(k == Kbd){
+		m = Kbdlightmax;
+		v = kbdlight;
+	}
 	return v*100/m;
 }
 
@@ -194,17 +272,17 @@
 	wr(spi2, SPIx_TXDATA, cmd);
 	wr(spi2, SPIx_TXDATA, arg);
 	wr(spi2, SPIx_CONREG, con | CON_XCH);
-	sleep(60);
 
-	/* LPC buffers 3 bytes without responding, ignore (including garbage) */
+	/*
+	 * LPC buffers 3 bytes without responding, but spends some time
+	 * to prepare the response. 50ms should be safe, add a bit more
+	 * to be sure LPC is blocked waiting for the chip select to go
+	 * active again.
+	 */
+	sleep(60);
 	while(rd(spi2, SPIx_STATREG) & STAT_RR)
 		rd(spi2, SPIx_RXDATA);
 
-	/*
-	 * at this point LPC hopefully is blocked waiting for
-	 * chip select to go active
-	 */
-
 	/* expecting 8 bytes, start the exchange */
 	for(i = 0; i < 8; i++)
 		wr(spi2, SPIx_TXDATA, 0);
@@ -315,7 +393,7 @@
 	if(r->ifcall.offset == 0){
 		aux = r->fid->file->aux;
 		if(aux == (void*)Light){
-			snprint(msg, sizeof(msg), "lcd %d\n", getlight());
+			snprint(msg, sizeof(msg), "lcd %d\nkbd %d\n", getlight(Lcd), getlight(Kbd));
 		}else if(aux == (void*)Temp){
 			if((c = getcputemp()) < 0){
 				responderror(r);
@@ -339,7 +417,7 @@
 fswrite(Req *r)
 {
 	char msg[256], *f[4];
-	int nf, v, p;
+	int nf, v, p, k;
 	void *aux;
 
 	snprint(msg, sizeof(msg), "%.*s",
@@ -352,16 +430,25 @@
 			respond(r, "invalid ctl message");
 			return;
 		}
-		if(strcmp(f[0], "lcd") == 0){
-			v = atoi(f[1]);
-			if(*f[1] == '+' || *f[1] == '-')
-				v += getlight();
-			setlight(v);
+		if(strcmp(f[0], "lcd") == 0)
+			k = Lcd;
+		else if(strcmp(f[0], "kbd") == 0)
+			k = Kbd;
+		else
+			goto Bad;
+		v = atoi(f[1]);
+		if(*f[1] == '+' || *f[1] == '-')
+			v += getlight(k);
+		if(setlight(k, v) != 0){
+			responderror(r);
+			return;
 		}
 	}else if(aux == (void*)Pmctl){
 		p = -1;
-		if(nf == 2 && strcmp(f[0], "power") == 0 && strcmp(f[1], "off") == 0)
-			p = Psomoff;
+		if(nf >= 2 && strcmp(f[0], "power") == 0){
+			if(nf == 2 && strcmp(f[1], "off") == 0)
+				p = Psomoff;
+		}
 		if(p < 0)
 			goto Bad;
 		lpccall('p', p, msg);
@@ -427,7 +514,7 @@
 	tmuinit();
 	lpcreq = reqqueuecreate();
 	fs.tree = alloctree(uid, uid, DMDIR|0555, nil);
-	createfile(fs.tree->root, "battery", uid, 0444,(void*)Battery);
+	createfile(fs.tree->root, "battery", uid, 0444, (void*)Battery);
 	createfile(fs.tree->root, "cputemp", uid, 0444, (void*)Temp);
 	createfile(fs.tree->root, "light", uid, 0666, (void*)Light);
 	createfile(fs.tree->root, "pmctl", uid, 0666, (void*)Pmctl);