code: plan9front

Download patch

ref: d2049c206c14fad18cd307d1e6fc2b319c95f581
parent: aa57098a3ca539c91d48d3d053ef0bd4cf0b3cce
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Tue Oct 4 19:47:47 EDT 2022

aux/kbdfs, nusb/kb: add basic media keys support; add /dev/hidNctl to change repeat/delay; fix a race condition

--- a/sys/include/keyboard.h
+++ b/sys/include/keyboard.h
@@ -44,6 +44,14 @@
 	Kscrolloneup=	KF|0x20,
 	Kscrollonedown=	KF|0x21,
 
+	/* multimedia keys - no refunds */
+	Ksbwd=	KF|0x22,
+	Ksfwd=	KF|0x23,
+	Kpause=	KF|0x24,
+	Kvoldn=	KF|0x25,
+	Kvolup=	KF|0x26,
+	Kmute=	KF|0x27,
+
 	Ksoh=	0x01,
 	Kstx=	0x02,
 	Ketx=	0x03,
--- a/sys/lib/kbmap/us
+++ b/sys/lib/kbmap/us
@@ -270,7 +270,7 @@
 2	13	0
 2	14	0
 2	15	0
-2	16	0
+2	16	0xf022
 2	17	0
 2	18	0
 2	19	0
@@ -279,7 +279,7 @@
 2	22	0
 2	23	0
 2	24	0
-2	25	0
+2	25	0xf023
 2	26	0
 2	27	0
 2	28	^J
@@ -286,9 +286,9 @@
 2	29	0xf862
 2	30	0
 2	31	0
-2	32	0
+2	32	0xf027
 2	33	0
-2	34	0
+2	34	0xf024
 2	35	0
 2	36	0
 2	37	0
@@ -300,9 +300,9 @@
 2	43	0
 2	44	0
 2	45	0
-2	46	0
+2	46	0xf025
 2	47	0
-2	48	0
+2	48	0xf026
 2	49	0
 2	50	0
 2	51	0
--- a/sys/man/4/nusb
+++ b/sys/man/4/nusb
@@ -145,8 +145,12 @@
 .IR kbdfs (8)
 process them.
 Mouse events are sent to
-.BR /dev/mousein
+.B /dev/mousein
 in the same way.
+A file
+.BI /dev/hid N ctl
+supports setting keyboard repeat and delay setting, the unit is
+milliseconds.
 .SS Joysticks
 .I Joy
 parses data packets from a given endpoint and prints back
--- a/sys/src/cmd/aux/kbdfs/kbdfs.c
+++ b/sys/src/cmd/aux/kbdfs/kbdfs.c
@@ -129,7 +129,7 @@
  */
 Rune kbtab[Nscan] = 
 {
-[0x00]	0,	0x1b,	'1',	'2',	'3',	'4',	'5',	'6',
+[0x00]	0,	Kesc,	'1',	'2',	'3',	'4',	'5',	'6',
 [0x08]	'7',	'8',	'9',	'0',	'-',	'=',	'\b',	'\t',
 [0x10]	'q',	'w',	'e',	'r',	't',	'y',	'u',	'i',
 [0x18]	'o',	'p',	'[',	']',	'\n',	Kctl,	'a',	's',
@@ -149,7 +149,7 @@
 
 Rune kbtabshift[Nscan] =
 {
-[0x00]	0,	0x1b,	'!',	'@',	'#',	'$',	'%',	'^',
+[0x00]	0,	Kesc,	'!',	'@',	'#',	'$',	'%',	'^',
 [0x08]	'&',	'*',	'(',	')',	'_',	'+',	'\b',	'\t',
 [0x10]	'Q',	'W',	'E',	'R',	'T',	'Y',	'U',	'I',
 [0x18]	'O',	'P',	'{',	'}',	'\n',	Kctl,	'A',	'S',
@@ -171,11 +171,11 @@
 {
 [0x00]	0,	0,	0,	0,	0,	0,	0,	0,
 [0x08]	0,	0,	0,	0,	0,	0,	0,	0,
-[0x10]	0,	0,	0,	0,	0,	0,	0,	0,
-[0x18]	0,	0,	0,	0,	'\n',	Kctl,	0,	0,
-[0x20]	0,	0,	0,	0,	0,	0,	0,	0,
-[0x28]	0,	0,	0,	0,	0,	0,	0,	0,
-[0x30]	0,	0,	0,	0,	0,	'/',	0,	Kprint,
+[0x10]	Ksbwd,	0,	0,	0,	0,	0,	0,	0,
+[0x18]	0,	Ksfwd,	0,	0,	'\n',	Kctl,	0,	0,
+[0x20]	Kmute,	0,	Kpause,	0,	0,	0,	0,	0,
+[0x28]	0,	0,	0,	0,	0,	0,	Kvoldn,	0,
+[0x30]	Kvolup,	0,	0,	0,	0,	'/',	0,	Kprint,
 [0x38]	Kaltgr,	0,	0,	0,	0,	0,	0,	0,
 [0x40]	0,	0,	0,	0,	0,	0,	Kbreak,	Khome,
 [0x48]	Kup,	Kpgup,	0,	Kleft,	0,	Kright,	0,	Kend,
--- a/sys/src/cmd/nusb/kb/hid.h
+++ b/sys/src/cmd/nusb/kb/hid.h
@@ -4,6 +4,7 @@
 enum {
 
 	Stack = 32 * 1024,
+	Nkey = 64,
 
 	/* HID class subclass protocol ids */
 	PtrCSP		= 0x020103,	/* mouse.boot.hid */
--- a/sys/src/cmd/nusb/kb/kb.c
+++ b/sys/src/cmd/nusb/kb/kb.c
@@ -13,7 +13,9 @@
 
 #include <u.h>
 #include <libc.h>
+#include <fcall.h>
 #include <thread.h>
+#include <9p.h>
 #include "usb.h"
 #include "hid.h"
 
@@ -23,11 +25,7 @@
 	Diemsg = 0xbeefbeef,
 };
 
-enum
-{
-	Kbdelay = 500,
-	Kbrepeat = 100,
-};
+char user[] = "kb";
 
 typedef struct Hiddev Hiddev;
 struct Hiddev
@@ -78,7 +76,7 @@
 	Hidslot	s[16];
 
 	int	nk;
-	uchar	k[64];
+	ushort	k[Nkey];
 
 	int	o;
 	uchar	*e;
@@ -92,6 +90,7 @@
 	/* Scan codes (see kbd.c) */
 	SCesc1		= 0xe0,		/* first of a 2-character sequence */
 	SCesc2		= 0xe1,
+	Consumer	= 0x100,	/* the key is on consumer page */
 	Keyup		= 0x80,		/* flag bit */
 	Keymask		= 0x7f,		/* regular scan code bits */
 };
@@ -101,12 +100,12 @@
  */
 #define isext(sc)	((sc) >= 0x80)
 
+static char sctab[2*256] =
+{
 /*
  * key code to scan code; for the page table used by
  * the logitech bluetooth keyboard.
  */
-static char sctab[256] = 
-{
 [0x00]	0x0,	0x0,	0x0,	0x0,	0x1e,	0x30,	0x2e,	0x20,
 [0x08]	0x12,	0x21,	0x22,	0x23,	0x17,	0x24,	0x25,	0x26,
 [0x10]	0x32,	0x31,	0x18,	0x19,	0x10,	0x13,	0x1f,	0x14,
@@ -139,6 +138,39 @@
 [0xe8]	0x0,	0x0,	0x0,	0x0,	0x0,	0xf3,	0xf2,	0xf1,
 [0xf0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
 [0xf8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+/* consumer page to scan code */
+[0x100]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x108]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x110]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x118]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x120]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x128]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x130]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x138]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x140]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x148]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x150]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x158]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x160]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x168]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x170]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x178]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x180]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x188]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x190]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x198]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x1a0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x1a8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x1b0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x99,	0x90,	0x0,
+[0x1b8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x1c0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x1c8]	0x0,	0x0,	0x0,	0x0,	0x0,	0xa2,	0x0,	0x0,
+[0x1d0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x1d8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x1e0]	0x0,	0x0,	0xa0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x1e8]	0x0,	0xb0,	0xae,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x1f0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
+[0x1f8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
 };
 
 static uchar kbdbootrep[] = {
@@ -165,6 +197,9 @@
 };
 
 static int debug = 0;
+static int kbdelay = 500;
+static int kbrepeat = 100;
+static int havekbd;
 
 static int
 signext(int v, int bits)
@@ -498,9 +533,9 @@
 			continue;
 		}
 		sc = l & 0xff;
-		t = Kbdelay;
+		t = kbdelay;
 		if(alt(a) == 1){
-			t = Kbrepeat;
+			t = kbrepeat;
 			while(alt(a) == 1)
 				putscan(f, sc, 0);
 		}
@@ -528,7 +563,7 @@
 {
 	Hidreport *p = a;
 	Hidslot *s = &p->s[p->ns];
-	int v, m;
+	int v, m, cp;
 
 	switch(t){
 	case Input:
@@ -571,11 +606,11 @@
 	if(debug > 1)
 		fprint(2, "hidparse: t=%x f=%x usage=%x v=%x\n", t, f, l[Usage], v);
 
-	if((l[Usage]>>16) == 0x07){	/* keycode */
+	if((cp = ((l[Usage]>>16) == 0x0c)) || (l[Usage]>>16) == 0x07){	/* consumer/keycode */
 		if((f & (Fvar|Farray)) == Fvar)
 			if(v != 0) v = l[Usage] & 0xFF;
 		if(p->nk < nelem(p->k) && v != 0)
-			p->k[p->nk++] = v;
+			p->k[p->nk++] = v | cp*Consumer;
 		return;
 	}
 
@@ -677,12 +712,23 @@
 	close(fd);
 }
 
+static ushort *
+keykey(ushort *a, ushort k, int n)
+{
+	while(n > 0){
+		if(*a++ == k)
+			return a-1;
+		n--;
+	}
+	return nil;
+}
+
 static void
 readerproc(void* a)
 {
 	char	err[ERRMAX], mbuf[80];
-	uchar	lastk[64], uk, dk;
-	int	i, c, nerrs, bpress, lastb, nlastk;
+	ushort	lastk[Nkey], uks[Nkey], dks[Nkey], nuks, ndks;
+	int	i, c, nerrs, bpress, lastb, nlastk, stopped;
 	int	abs, x, y, z, b;
 	Hidreport p;
 	Hidslot lasts[nelem(p.s)], *s, *l;
@@ -730,7 +776,7 @@
 			if(debug){
 				fprint(2, "kbd: ");
 				for(i = 0; i < p.nk; i++)
-					fprint(2, "%#2.2ux ", p.k[i]);
+					fprint(2, "%#4.4ux ", p.k[i]);
 				fprint(2, "\n");
 			}
 
@@ -745,25 +791,36 @@
 				proccreate(repeatproc, f, Stack);
 			}
 	
-			dk = uk = 0;
-			for(i=0; i<nlastk; i++){
-				if(memchr(p.k, lastk[i], p.nk) == nil){
-					uk = sctab[lastk[i]];
-					putscan(f, uk, Keyup);
-				}
+			/* collect key presses/releases */
+			for(i=nuks=0; i<nlastk; i++){
+				if(keykey(p.k, lastk[i], p.nk) == nil)
+					uks[nuks++] = sctab[lastk[i]];
 			}
-			for(i=0; i<p.nk; i++){
-				if(memchr(lastk, p.k[i], nlastk) == nil){
-					dk = sctab[p.k[i]];
-					putscan(f, dk, 0);
+			for(i=ndks=0; i<p.nk; i++){
+				if(keykey(lastk, p.k[i], nlastk) == nil)
+					dks[ndks++] = sctab[p.k[i]];
+			}
+
+			/*
+			 * stop the repeats first to avoid race condition when
+			 * the key is released but the repeat happens right after
+			 */
+			for(stopped = i = 0; i < nuks; i++){
+				if(ndks == 0 || keykey(dks, uks[i], ndks) != nil){
+					stoprepeat(f);
+					stopped = 1;
+					break;
 				}
 			}
-			if(uk != 0 && (dk == 0 || dk == uk))
-				stoprepeat(f);
-			else if(dk != 0)
-				startrepeat(f, dk);
+			for(i = 0; i < nuks; i++)
+				putscan(f, uks[i], Keyup);
+			for(i = 0; i < ndks; i++)
+				putscan(f, dks[i], 0);
 
-			memmove(lastk, p.k, nlastk = p.nk);
+			if(stopped == 0 && ndks > 0)
+				startrepeat(f, dks[ndks-1]);
+
+			memmove(lastk, p.k, (nlastk = p.nk)*sizeof(lastk[0]));
 			p.nk = 0;
 		}
 
@@ -902,6 +959,49 @@
 }
 
 static void
+fsread(Req *r)
+{
+	char msg[48], *s, *e;
+
+	s = msg;
+	e = msg+sizeof(msg);
+	*s = 0;
+	if(havekbd)
+		e = seprint(s, e, "repeat %d\ndelay %d\n", kbrepeat, kbdelay);
+	USED(e);
+	readstr(r, msg);
+	respond(r, nil);
+}
+
+static void
+fswrite(Req *r)
+{
+	char msg[256], *f[4];
+	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));
+	if(nf < 2){
+		respond(r, "invalid ctl message");
+		return;
+	}
+	if(strcmp(f[0], "repeat") == 0)
+		kbrepeat = atoi(f[1]);
+	else if(strcmp(f[0], "delay") == 0)
+		kbdelay = atoi(f[1]);
+	else if(strcmp(f[0], "debug") == 0)
+		debug = atoi(f[1]);
+	r->ofcall.count = r->ifcall.count;
+	respond(r, nil);
+}
+
+static Srv fs = {
+	.read = fsread,
+	.write = fswrite,
+};
+
+static void
 usage(void)
 {
 	fprint(2, "usage: %s [-d] devid\n", argv0);
@@ -915,6 +1015,7 @@
 	Dev *d;
 	Ep *ep;
 	Usbdev *ud;
+	char buf[32];
 
 	ARGBEGIN{
 	case 'd':
@@ -936,6 +1037,7 @@
 			continue;
 		switch(ep->iface->csp){
 		case KbdCSP:
+			havekbd = 1;
 		case PtrCSP:
 		case PtrNonBootCSP:
 		case HidCSP:
@@ -943,6 +1045,11 @@
 			break;
 		}
 	}
+	fs.tree = alloctree(user, "usb", DMDIR|0555, nil);
+	snprint(buf, sizeof buf, "hidU%sctl", d->hname);
+	createfile(fs.tree->root, buf, user, 0666, nil);
+	snprint(buf, sizeof buf, "%s.hid", d->hname);
 	closedev(d);
+	threadpostsharesrv(&fs, nil, "usb", buf);
 	threadexits(nil);
 }