kernel: laptop touchpads over I2C-HID
A laptop's touchpad is on the chipset's I2C bus, found only through ACPI (PNP0C50 / ACPI0C50), and SchOS had no I2C at all, so the touchpad of every recent laptop did nothing. kernel/acpi/i2c_hid.c drives the DesignWare I2C controller every x86 chipset uses (AMDI0010 on Ryzen) by polling, speaks HID over I2C to the device the _DSM points at, and feeds the reports through the USB HID parser. On AMD the interrupt pin's level is read every tick, so a report is read exactly when one is waiting; elsewhere the input register is polled. The touchpad is driven in mouse mode, which it starts in after a reset: motion, clicks and taps become /dev/input/event1 like any mouse. The HID parser now prefers a descriptor's relative pointer over an absolute one for this, and takes descriptors from transports other than USB. Untested on hardware: QEMU emulates no I2C touchpad. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
parent
4a7165c951
commit
e43f56c619
5 changed files with 733 additions and 15 deletions
|
|
@ -25,6 +25,7 @@
|
|||
#include "core/dma.h"
|
||||
#include "dev/usb.h"
|
||||
#include "dev/input.h"
|
||||
#include "dev/hid.h"
|
||||
#include "dev/kbd.h"
|
||||
#include "dev/vt.h"
|
||||
|
||||
|
|
@ -49,6 +50,11 @@ struct hid {
|
|||
// report, for a mouse whose descriptor could not be used.
|
||||
int boot;
|
||||
int report_id; // 0 when reports carry no ID byte
|
||||
// 1 + the only report ID to take pointer fields from, 0 for any; and
|
||||
// 1 + the first report ID with a relative X. A touchpad describes both a
|
||||
// mouse report and a multi-finger one with absolute coordinates, and only
|
||||
// the mouse report makes sense as a pointer; see hid_attach().
|
||||
int only_id1, rel_id1;
|
||||
struct hfield x, y, wheel, btn[HID_NBTN];
|
||||
|
||||
// A keyboard in report protocol: where its keys are, so that each report
|
||||
|
|
@ -343,6 +349,10 @@ take_field(struct hid *h, uint32 usage, int bit, int size, int lmin, int lmax,
|
|||
struct hfield *f = 0;
|
||||
int page = usage >> 16, u = usage & 0xFFFF;
|
||||
|
||||
if(page == PAGE_DESKTOP && u == U_X && rel && h->rel_id1 == 0)
|
||||
h->rel_id1 = id + 1;
|
||||
if(h->only_id1 && id + 1 != h->only_id1)
|
||||
return;
|
||||
if(page == PAGE_DESKTOP && u == U_X) f = &h->x;
|
||||
else if(page == PAGE_DESKTOP && u == U_Y) f = &h->y;
|
||||
else if(page == PAGE_DESKTOP && u == U_WHEEL) f = &h->wheel;
|
||||
|
|
@ -630,12 +640,8 @@ pointer_report(struct hid *h, const uchar *rep, int len)
|
|||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
hid_complete(struct usb_dev *dev, void *buf, int len)
|
||||
hid_input(struct hid *h, const uchar *buf, int len)
|
||||
{
|
||||
struct hid *h = dev->drvdata;
|
||||
|
||||
if(h == 0)
|
||||
return;
|
||||
// One interface can be both: a wireless receiver describes its keyboard
|
||||
// and its mouse as two report IDs of one descriptor.
|
||||
if(h->rkbd)
|
||||
|
|
@ -646,6 +652,70 @@ hid_complete(struct usb_dev *dev, void *buf, int len)
|
|||
pointer_report(h, buf, len);
|
||||
}
|
||||
|
||||
static void
|
||||
hid_complete(struct usb_dev *dev, void *buf, int len)
|
||||
{
|
||||
struct hid *h = dev->drvdata;
|
||||
|
||||
if(h != 0)
|
||||
hid_input(h, buf, len);
|
||||
}
|
||||
|
||||
static struct hid *
|
||||
hid_slot(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(!kqinit){
|
||||
initlock(&kqlock, "usbkbd");
|
||||
kqinit = 1;
|
||||
}
|
||||
for(i = 0; i < NHID; i++)
|
||||
if(!hids[i].in_use){
|
||||
memset(&hids[i], 0, sizeof(hids[i]));
|
||||
return &hids[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// A HID device on some other transport -- I2C, for a laptop's touchpad --
|
||||
// handing over its report descriptor. Keyboards and pointers come out of it
|
||||
// exactly as from USB. A descriptor with a relative pointer is driven
|
||||
// through that report alone, even if an absolute one comes first: that is a
|
||||
// touchpad's mouse report, which it sends until told to report fingers.
|
||||
void *
|
||||
hid_attach(const uchar *desc, int n, const char *what)
|
||||
{
|
||||
struct hid *h = hid_slot();
|
||||
int rel;
|
||||
|
||||
if(h == 0)
|
||||
return 0;
|
||||
if(parse_report(h, desc, n) < 0)
|
||||
return 0;
|
||||
rel = h->rel_id1;
|
||||
if(h->x.ok && !h->x.rel && rel){
|
||||
memset(h, 0, sizeof(*h));
|
||||
h->only_id1 = rel;
|
||||
if(parse_report(h, desc, n) < 0)
|
||||
return 0;
|
||||
}
|
||||
h->proto = !h->x.ok ? USB_HID_PROTO_KEYBOARD :
|
||||
h->x.rel ? USB_HID_PROTO_MOUSE : USB_HID_PROTO_TABLET;
|
||||
h->in_use = 1;
|
||||
cprintf("%s: %s%s\n", what,
|
||||
h->proto == USB_HID_PROTO_KEYBOARD ? "keyboard" :
|
||||
h->proto == USB_HID_PROTO_MOUSE ? "pointer" : "absolute pointer",
|
||||
h->rkbd && h->x.ok ? " and keyboard" : "");
|
||||
return h;
|
||||
}
|
||||
|
||||
void
|
||||
hid_attached_input(void *h, const uchar *rep, int len)
|
||||
{
|
||||
hid_input(h, rep, len);
|
||||
}
|
||||
|
||||
static int
|
||||
hid_request(struct usb_dev *dev, int request, int value)
|
||||
{
|
||||
|
|
@ -721,18 +791,10 @@ hid_probe(struct usb_dev *dev)
|
|||
cprintf("usb-hid: no interrupt IN endpoint; nothing to read\n");
|
||||
return -1;
|
||||
}
|
||||
if(!kqinit){
|
||||
initlock(&kqlock, "usbkbd");
|
||||
kqinit = 1;
|
||||
}
|
||||
|
||||
for(i = 0; i < NHID; i++)
|
||||
if(!hids[i].in_use){ h = &hids[i]; break; }
|
||||
if(h == 0){
|
||||
if((h = hid_slot()) == 0){
|
||||
cprintf("usb-hid: %d devices already; this one is not used\n", NHID);
|
||||
return -1;
|
||||
}
|
||||
memset(h, 0, sizeof(*h));
|
||||
|
||||
if(boot && proto == USB_HID_PROTO_KEYBOARD){
|
||||
// Ask for the fixed layout explicitly. A device that supports the boot
|
||||
|
|
|
|||
Loading…
Reference in a new issue