reform/pm: provide /dev/kbdoled to display a 126x32x1 bitmap on the keyboard OLED

This commit is contained in:
Sigrid Solveig Haflínudóttir 2022-10-08 01:20:32 +00:00
parent 9d20339e47
commit 4f6e074f97
2 changed files with 74 additions and 17 deletions

View file

@ -109,6 +109,10 @@ Returns attached battery array statistics in the same manner as
.B cputemp
Exposes the current temperature reading of the CPU.
.TP
.B kbdoled
An image can be displayed on the keyboard OLED by writing a 126x32x1
uncompressed Plan 9 image (with or without a header).
.TP
.B light
Provides a way to control the backlight of the built-in LCD by
writing \fIlcd [-+]N\fR or \fIkbd [-+]N\fR,

View file

@ -3,6 +3,8 @@
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include <draw.h>
#include <memdraw.h>
enum
{
@ -23,8 +25,12 @@ enum
Light = 1,
Temp,
Battery,
Kbdoled,
Pmctl,
KbdoledW = 126,
KbdoledH = 32,
Lcd = 0,
Kbd,
@ -72,11 +78,13 @@ enum
STAT_RR = 1<<3,
};
static char *uid = "pm";
static Reqqueue *lpcreq;
static u32int *pwm2, *tmu, *spi2;
static int kbdlight = 0;
static int kbdhidfd = -1;
static char *uid = "pm";
static Memimage *kbdoled;
static u8int kbdoledraw[4+KbdoledW*KbdoledH/8] = {'W', 'B', 'I', 'T', 0};
static void
wr(u32int *base, int reg, u32int v)
@ -125,23 +133,54 @@ openkbdhid(void)
char path[32], *s, *k, *e;
int f;
if(kbdhidfd >= 0)
return kbdhidfd;
if((f = open("/dev/usb/ctl", OREAD)) >= 0){
if(kbdhidfd < 0 && (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);
if((kbdhidfd = open(path, OWRITE)) >= 0 && write(kbdhidfd, "rawon", 5) != 5){
close(kbdhidfd);
kbdhidfd = -1;
}
}
free(s);
close(f);
}
return kbdhidfd;
return kbdhidfd < 0;
}
static int
loadkbdoled(void *data, int size)
{
int x, y, i, k, v, bpl;
u8int *p, q;
bpl = bytesperline(kbdoled->r, kbdoled->depth);
if(size == 60+bpl*KbdoledH){
data = (u8int*)data + 60;
size -= 60;
}else if(size != bpl*KbdoledH){
werrstr("invalid image: expected %dx%d GREY1 (%d bytes)", KbdoledW, KbdoledH, bpl*KbdoledH);
return -1;
}
k = loadmemimage(kbdoled, kbdoled->r, data, size);
if(k < 0 || openkbdhid() != 0)
return -1;
for(y = 0, i = 4; y < KbdoledH; y += 8){
for(x = v = 0; x < KbdoledW; x++, v = (v+1)&7){
SET(p);
if(v == 0)
p = byteaddr(kbdoled, Pt(x,y));
for(k = q = 0; k < 8; k++)
q |= ((p[bpl*k] >> (7-v)) & 1) << k;
kbdoledraw[i++] = q;
}
}
return write(kbdhidfd, kbdoledraw, sizeof(kbdoledraw));
}
static int
@ -158,7 +197,7 @@ setlight(int k, int p)
v = Pwmsrcclk / rd(pwm2, PWMSAR);
wr(pwm2, PWMPR, (Pwmsrcclk/(v*p/100))-2);
return 0;
}else if(k == Kbd && (kbdhidfd = openkbdhid()) >= 0){
}else if(k == Kbd && openkbdhid() == 0){
v = Kbdlightmax*p/100;
if(fprint(kbdhidfd, "LITE%c", '0'+v) > 0){
kbdlight = v;
@ -420,10 +459,21 @@ fswrite(Req *r)
int nf, v, p, k;
void *aux;
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*)Kbdoled){
if(loadkbdoled(r->ifcall.data, r->ifcall.count) < 0){
Err:
responderror(r);
return;
}
r->ofcall.count = r->ifcall.count;
respond(r, nil);
return;
}
snprint(msg, sizeof(msg), "%.*s", utfnlen(r->ifcall.data, r->ifcall.count), r->ifcall.data);
nf = tokenize(msg, f, nelem(f));
if(aux == (void*)Light){
if(nf < 2){
Bad:
@ -439,10 +489,8 @@ Bad:
v = atoi(f[1]);
if(*f[1] == '+' || *f[1] == '-')
v += getlight(k);
if(setlight(k, v) != 0){
responderror(r);
return;
}
if(setlight(k, v) != 0)
goto Err;
}else if(aux == (void*)Pmctl){
p = -1;
if(nf >= 2 && strcmp(f[0], "power") == 0){
@ -451,7 +499,7 @@ Bad:
* LPC firmware might not be up to date so try
* shutting down through the keyboard first
*/
if((kbdhidfd = openkbdhid()) >= 0){
if(openkbdhid() == 0){
write(kbdhidfd, "PWR0", 4);
sleep(2000); /* give it a chance */
}
@ -520,11 +568,16 @@ threadmain(int argc, char **argv)
if((spi2 = segattach(0, "ecspi2", 0, 0x20)) == (void*)-1)
sysfatal("no spi2");
tmuinit();
if(memimageinit() != 0)
sysfatal("%r");
if((kbdoled = allocmemimage(Rect(0, 0, KbdoledW, KbdoledH), GREY1)) == nil)
sysfatal("%r");
lpcreq = reqqueuecreate();
fs.tree = alloctree(uid, uid, DMDIR|0555, nil);
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, "kbdoled", uid, 0222, (void*)Kbdoled);
createfile(fs.tree->root, "pmctl", uid, 0666, (void*)Pmctl);
threadpostmountsrv(&fs, srv, mtpt, MAFTER);