code: plan9front

Download patch

ref: f57df708538093ffb47727a522a0217b9ee81125
parent: 8188b4f4f0e07b6669e6ae3c6c1099af917eaab4
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun Jan 29 11:36:57 EST 2023

nusb/kb: GAOMON S620 tablet support

The GAOMON S620 tablet is recognized as a HID device,
but is restricted in the X-axis to the width of a mobile
phone.

I sniffed usb traffic for generic windows 10 hid driver
and there didnt seem anything out of the ordinary.

It turns out that this is some kind of phone feature
and i suspect they have some heuristic for how windows
reads the device and config descriptors to decide if
this is windows or android.

Checking the DIGImend project git repository, they suggest
this is some kind of UCLOGIC compatible tablet, which
once a series of string properties have been read will
switch itself to some different mode.

The report descriptor is supposed to be generated from
the string properties, but the report format didnt really
match theirs.

So i ignore the string properties and just looked at the
report data.

The format after reading the magic string properties
seems to be:

08 - report id
bb - buttons: 0x80 = inrange, 0x04 = eraser, 0x02, barrel, 0x01 = touch
xx xx - x-axis: [0x0000-0x80000)
yy yy - y-axis: [0x0000-0x50000)
pp pp - pressure: [0x0000-0xffff]
?? ?? - unknown

So i hand rolled a hid report descriptor for this and
call it a day :)

--- a/sys/src/cmd/nusb/kb/kb.c
+++ b/sys/src/cmd/nusb/kb/kb.c
@@ -898,11 +898,78 @@
 }
 
 static void
+gaomons620(Hiddev *f)
+{
+	static uchar descr[] = {
+		0x05, 0x0D,	/* Usage Page (Digitizer),	*/
+		0x09, 0x01,	/* Usage (Digitizer),		*/
+		0xA1, 0x01,	/* Collection (Application),	*/
+		0x85, 0x08,	/* Report ID (8),		*/
+		0x09, 0x20,	/* Usage (Stylus),		*/
+		0xA0,		/* Collection (Physical),	*/
+		0x14,		/*	Logical Minimum (0),	*/
+		0x25, 0x01,	/*	Logical Maximum (1),	*/
+		0x75, 0x01,	/*	Report Size (1),	*/
+		0x09, 0x42,	/*	Usage (Tip Switch),	*/
+		0x09, 0x44,	/*	Usage (Barrel Switch),	*/
+		0x09, 0x45,	/*	Usage (Eraser),		*/
+		0x95, 0x03,	/*	Report Count (3),	*/
+		0x81, 0x02,	/*	Input (Variable),	*/
+
+		0x95, 0x04,	/*	Report Count (4),	*/
+		0x81, 0x03,	/*	Input (Constant, Variable), */
+
+		0x09, 0x32,	/*	Usage (In Range),	*/
+		0x95, 0x01,	/*	Report Count (1),	*/
+		0x81, 0x02,	/*	Input (Variable),	*/
+
+		0xA4,		/*	Push,			*/
+		0x05, 0x01,	/*	Usage Page (Desktop),	*/
+		0x65, 0x13,	/*	Unit (Inch),		*/
+		0x55, 0xFD,	/*	Unit Exponent (-3),	*/
+		0x75, 0x10,	/*	Report Size (16),	*/
+		0x34,		/*	Physical Minimum (0),	*/
+		0x09, 0x30,	/*	Usage (X),		*/
+		0x27, 0x00, 0x80, 0x00, 0x00,	/*	Logical Maximum, */
+		0x47, 0x00, 0x80, 0x00, 0x00,	/*	Physical Maximum, */
+		0x81, 0x02,	/*	Input (Variable),	*/
+
+		0x09, 0x31,	/*	Usage (Y),		*/
+		0x27, 0x00, 0x50, 0x00, 0x00,	/*	Logical Maximum, */
+		0x47, 0x00, 0x50, 0x00, 0x00,	/*	Physical Maximum, */
+		0x81, 0x02,	/*	Input (Variable),	*/
+
+		0xB4,		/*	Pop,			*/
+		0x09, 0x30,	/*	Usage (Tip Pressure),	*/
+		0x75, 0x10,	/*	Report Size (16),	*/
+		0x27, 0x00, 0x00, 0x01, 0x00,	/*	Logical Maximum, */
+		0x81, 0x02,	/*	Input (Variable),	*/
+		0xC0,		/* End Collection,		*/
+		0xC0		/* End Collection		*/
+	};
+	static int idx[] = { 0x64, 0x65, 0x6e, 0x79, 0x7a, 0x7b, 0xc8, 0xc9 };
+	Dev *d = f->dev;	
+	int i;
+
+	/* we have to fetch a bunch of strings to switch to non-phone mode */
+	for(i=0; i < nelem(idx); i++)
+		free(loaddevstr(d, idx[i]));
+
+	/* override report descriptor */
+	memmove(f->rep, descr, f->nrep = sizeof(descr));
+}
+
+static void
 quirks(Hiddev *f)
 {
 	Dev *d;
 	
 	d = f->dev;
+
+	if(d->usb->vid == 0x256c && d->usb->did == 0x006d){
+		gaomons620(f);
+		return;
+	}
 	
 	/* Elecom trackball report descriptor lies by
 	 * omission, failing to mention all its buttons.
@@ -921,6 +988,7 @@
 		f->rep[13] = 8;
 		f->rep[21] = 8;
 		f->rep[31] = 0;
+		return;
 	}
 }