Download raw body.
Touchscreen support for the Samsung Galaxy Book4 Edge
On Fri, May 22, 2026 at 01:04:29PM +0200, Kirill A. Korinsky wrote:
> On Fri, 22 May 2026 05:52:43 +0200,
> Marcus Glocker <marcus@nazgul.ch> wrote:
> >
> > On Fri, May 22, 2026 at 12:58:54AM +0200, Kirill A. Korinsky wrote:
> >
> > > On Thu, 21 May 2026 22:01:07 +0200,
> > > Marcus Glocker <marcus@nazgul.ch> wrote:
> > > >
> > > > This diff enables touchscreen support for the Samsung Galaxy Book4
> > > > Edge. For now we need to stick with polling since we can't figure
> > > > out the GPIO pin for the touchscreen interrupts.
> > > >
> > > > In a summary, those are the changes we had to do in the different
> > > > areas:
> > > >
> > > > - DTS: Power up the device and declare it on the I2C bus; no interrupt
> > > > line, so it runs in polling mode (we can't determine the IRQ pin).
> > > >
> > > > - hidmt: Teach the shared multitouch layer to handle absolute
> > > > touchscreens, not just touchpads.
> > > >
> > > > - imt: Recognize and claim the panel as a touchscreen so it's driven by
> > > > the multitouch layer instead of being mistaken for a plain mouse.
> > > >
> > > > - ihidev: Synthesize the "finger lifted" event in polling mode, since
> > > > the device signals release only by going silent.
> > > >
> > > > If you have an i2c-Precision touchpad or an i2c-HID keyboard, please
> > > > test it still works.
> > > >
> > > > Please note that the diff doesn't introduce multi-finger gestures,
> > > > just absolute pointer support (tap/drag).
> > > >
> > > > Otherwise, feedback, OKs?
> > > >
> > >
> > > It brokes touchscreen on my honor.
> >
> > Could you send me a dmesg before and after the diff please?
> >
>
> Sure, two dmesg attached, also here a diff.
>
> Keep in mind that multitouch works not each boot, and both boots was without
> multitouch, and symptop of that: ihidev2: failed fetching report
>
> I had add some hacks which allows to have multitouch on touchpad, but it
> seems not enough.
>
> Anyway, here a diff:
Thanks for the report.
It looks like your panel has a config report, while my hasn't, which
has caused trying to switch the input mode where it shouldn't.
Can you please try this updated diff? Your touchscreen should still
attach to imt instead of ims, but hopefully works this time.
Index: sys/dev/hid/hidmt.c
===================================================================
RCS file: /cvs/src/sys/dev/hid/hidmt.c,v
diff -u -p -u -p -r1.16 hidmt.c
--- sys/dev/hid/hidmt.c 21 Jul 2025 21:46:40 -0000 1.16
+++ sys/dev/hid/hidmt.c 22 May 2026 17:18:37 -0000
@@ -203,8 +203,10 @@ hidmt_setup(struct device *self, struct
mt->sc_resy = hidmt_get_resolution(&h);
}
break;
- case HID_USAGE2(HUP_DIGITIZERS, HUD_TIP_SWITCH):
case HID_USAGE2(HUP_DIGITIZERS, HUD_CONFIDENCE):
+ mt->sc_flags |= HIDMT_HASCONFIDENCE;
+ /* FALLTHROUGH */
+ case HID_USAGE2(HUP_DIGITIZERS, HUD_TIP_SWITCH):
case HID_USAGE2(HUP_DIGITIZERS, HUD_WIDTH):
case HID_USAGE2(HUP_DIGITIZERS, HUD_HEIGHT):
case HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACTID):
@@ -239,7 +241,17 @@ hidmt_setup(struct device *self, struct
goto fail;
}
- if (hidmt_set_input_mode(mt, HIDMT_INPUT_MODE_MT_TOUCHPAD)) {
+ /* default touchscreen calibration to the device's reported range */
+ mt->sc_calibminx = mt->sc_minx;
+ mt->sc_calibmaxx = mt->sc_maxx;
+ mt->sc_calibminy = mt->sc_miny;
+ mt->sc_calibmaxy = mt->sc_maxy;
+ mt->sc_calibswapxy = 0;
+
+ /* touchpads need switching into multitouch mode; touchscreens
+ * are already there and some mishandle the write, so skip it */
+ if (!(mt->sc_flags & HIDMT_TOUCHSCREEN) &&
+ hidmt_set_input_mode(mt, HIDMT_INPUT_MODE_MT_TOUCHPAD)) {
printf("\n%s: switch to multitouch mode failed\n",
self->dv_xname);
goto fail;
@@ -268,9 +280,14 @@ hidmt_configure(struct hidmt *mt)
return;
hw = wsmouse_get_hw(mt->sc_wsmousedev);
- hw->type = WSMOUSE_TYPE_TOUCHPAD;
- hw->hw_type = (mt->sc_clickpad
- ? WSMOUSEHW_CLICKPAD : WSMOUSEHW_TOUCHPAD);
+ if (mt->sc_flags & HIDMT_TOUCHSCREEN) {
+ hw->type = WSMOUSE_TYPE_TPANEL;
+ hw->hw_type = WSMOUSEHW_TPANEL;
+ } else {
+ hw->type = WSMOUSE_TYPE_TOUCHPAD;
+ hw->hw_type = (mt->sc_clickpad
+ ? WSMOUSEHW_CLICKPAD : WSMOUSEHW_TOUCHPAD);
+ }
hw->x_min = mt->sc_minx;
hw->x_max = mt->sc_maxx;
hw->y_min = mt->sc_miny;
@@ -287,9 +304,13 @@ hidmt_attach(struct hidmt *mt, const str
{
struct wsmousedev_attach_args a;
- printf(": %spad, %d contact%s\n",
- (mt->sc_clickpad ? "click" : "touch"), mt->sc_num_contacts,
- (mt->sc_num_contacts == 1 ? "" : "s"));
+ if (mt->sc_flags & HIDMT_TOUCHSCREEN)
+ printf(": touchscreen, %d contact%s\n", mt->sc_num_contacts,
+ (mt->sc_num_contacts == 1 ? "" : "s"));
+ else
+ printf(": %spad, %d contact%s\n",
+ (mt->sc_clickpad ? "click" : "touch"), mt->sc_num_contacts,
+ (mt->sc_num_contacts == 1 ? "" : "s"));
a.accessops = ops;
a.accesscookie = mt->sc_device;
@@ -455,11 +476,30 @@ hidmt_input(struct hidmt *mt, uint8_t *d
seencontacts++;
}
+ /* no physical buttons on a touchscreen: a finger down is a click */
+ if (mt->sc_flags & HIDMT_TOUCHSCREEN)
+ buttons = tips ? 1 : 0;
+
s = spltty();
if (mt->sc_buttons != buttons) {
wsmouse_buttons(mt->sc_wsmousedev, buttons);
mt->sc_buttons = buttons;
}
+
+ /* all fingers up: release exactly the slots we recorded as down */
+ if ((mt->sc_flags & HIDMT_TOUCHSCREEN) && tips == 0) {
+ for (i = 0; i < HIDMT_MAX_CONTACTS; i++) {
+ if (!(mt->sc_touches & (1 << i)))
+ continue;
+ mt->sc_contacts[i].seen = 0;
+ wsmouse_mtstate(mt->sc_wsmousedev, i, 0, 0, 0);
+ }
+ mt->sc_touches = 0;
+ wsmouse_input_sync(mt->sc_wsmousedev);
+ splx(s);
+ return;
+ }
+
for (i = 0; i < HIDMT_MAX_CONTACTS; i++) {
if (!mt->sc_contacts[i].seen)
continue;
@@ -480,13 +520,19 @@ hidmt_input(struct hidmt *mt, uint8_t *d
mt->sc_contacts[i].height,
mt->sc_buttons));
- if (mt->sc_contacts[i].tip && !mt->sc_contacts[i].confidence)
+ if ((mt->sc_flags & HIDMT_HASCONFIDENCE) &&
+ mt->sc_contacts[i].tip && !mt->sc_contacts[i].confidence)
continue;
/* Report width as pressure. */
z = (mt->sc_contacts[i].tip
? imax(mt->sc_contacts[i].width, 50) : 0);
+ if (z > 0)
+ mt->sc_touches |= (1 << i);
+ else
+ mt->sc_touches &= ~(1 << i);
+
wsmouse_mtstate(mt->sc_wsmousedev,
i, mt->sc_contacts[i].x, mt->sc_contacts[i].y, z);
}
@@ -520,12 +566,20 @@ hidmt_ioctl(struct hidmt *mt, u_long cmd
break;
}
+ case WSMOUSEIO_SCALIBCOORDS:
+ mt->sc_calibminx = wsmc->minx;
+ mt->sc_calibmaxx = wsmc->maxx;
+ mt->sc_calibminy = wsmc->miny;
+ mt->sc_calibmaxy = wsmc->maxy;
+ mt->sc_calibswapxy = wsmc->swapxy;
+ break;
+
case WSMOUSEIO_GCALIBCOORDS:
- wsmc->minx = mt->sc_minx;
- wsmc->maxx = mt->sc_maxx;
- wsmc->miny = mt->sc_miny;
- wsmc->maxy = mt->sc_maxy;
- wsmc->swapxy = 0;
+ wsmc->minx = mt->sc_calibminx;
+ wsmc->maxx = mt->sc_calibmaxx;
+ wsmc->miny = mt->sc_calibminy;
+ wsmc->maxy = mt->sc_calibmaxy;
+ wsmc->swapxy = mt->sc_calibswapxy;
wsmc->resx = mt->sc_resx;
wsmc->resy = mt->sc_resy;
break;
@@ -593,4 +647,45 @@ hidmt_find_winptp_reports(const void *de
nitems(cap_usages), cap_usages, ptp_collections);
return (*input_rid > 0 && *config_rid > 0 && *cap_rid > 0);
+}
+
+/* Like hidmt_find_winptp_reports() but for a HUD_TOUCHSCREEN: no
+ * Confidence usage, and the Input Mode config report is optional. */
+int
+hidmt_find_touchscreen_reports(const void *desc, int len, int *input_rid,
+ int *config_rid, int *cap_rid)
+{
+ static int32_t ts_collections[] = {
+ HID_USAGE2(HUP_DIGITIZERS, HUD_FINGER), 0
+ };
+ static int32_t input_usages[] = {
+ /* report-level */
+ HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACTCOUNT),
+ /* contact-level */
+ HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
+ HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
+ HID_USAGE2(HUP_DIGITIZERS, HUD_TIP_SWITCH),
+ HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACTID),
+ };
+ static int32_t cfg_usages[] = {
+ HID_USAGE2(HUP_DIGITIZERS, HUD_INPUT_MODE),
+ };
+ static int32_t cap_usages[] = {
+ HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACT_MAX),
+ };
+
+ *input_rid = hid_find_report(desc, len, hid_input,
+ HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCHSCREEN),
+ nitems(input_usages), input_usages, ts_collections);
+ *cap_rid = hid_find_report(desc, len, hid_feature,
+ HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCHSCREEN),
+ nitems(cap_usages), cap_usages, ts_collections);
+
+ *config_rid = hid_find_report(desc, len, hid_feature,
+ HID_USAGE2(HUP_DIGITIZERS, HUD_CONFIG),
+ nitems(cfg_usages), cfg_usages, ts_collections);
+ if (*config_rid < 0)
+ *config_rid = 0;
+
+ return (*input_rid > 0 && *cap_rid > 0);
}
Index: sys/dev/hid/hidmtvar.h
===================================================================
RCS file: /cvs/src/sys/dev/hid/hidmtvar.h,v
diff -u -p -u -p -r1.10 hidmtvar.h
--- sys/dev/hid/hidmtvar.h 28 Oct 2025 15:36:46 -0000 1.10
+++ sys/dev/hid/hidmtvar.h 22 May 2026 17:18:37 -0000
@@ -36,7 +36,9 @@ struct hidmt_contact {
struct hidmt {
int sc_enabled;
uint32_t sc_flags;
-#define HIDMT_REVY 0x0001 /* Y-axis is reversed ("natural" scrolling) */
+#define HIDMT_REVY 0x0001 /* Y-axis is reversed ("natural" scrolling) */
+#define HIDMT_TOUCHSCREEN 0x0002 /* absolute touch panel, not a touchpad */
+#define HIDMT_HASCONFIDENCE 0x0004 /* device reports a Confidence usage */
struct device *sc_device;
int (*hidev_report_type_conv)(int);
@@ -61,9 +63,15 @@ struct hidmt {
int sc_miny, sc_maxy;
int sc_resx, sc_resy;
+ /* touchscreen calibration set via WSMOUSEIO_SCALIBCOORDS */
+ int sc_calibminx, sc_calibmaxx;
+ int sc_calibminy, sc_calibmaxy;
+ int sc_calibswapxy;
+
struct hidmt_contact sc_contacts[HIDMT_MAX_CONTACTS];
int sc_cur_contactcount;
int sc_buttons;
+ uint32_t sc_touches; /* bitmap of slots currently down */
};
int hidmt_set_input_mode(struct hidmt *, uint16_t);
@@ -78,3 +86,4 @@ void hidmt_input(struct hidmt *, uint8_t
int hidmt_ioctl(struct hidmt *, u_long, caddr_t, int, struct proc *);
int hidmt_setup(struct device *, struct hidmt *, void *, int);
int hidmt_find_winptp_reports(const void *, int, int *, int *, int *);
+int hidmt_find_touchscreen_reports(const void *, int, int *, int *, int *);
Index: sys/dev/i2c/ihidev.c
===================================================================
RCS file: /cvs/src/sys/dev/i2c/ihidev.c,v
diff -u -p -u -p -r1.41 ihidev.c
--- sys/dev/i2c/ihidev.c 28 Oct 2025 15:36:46 -0000 1.41
+++ sys/dev/i2c/ihidev.c 22 May 2026 17:18:37 -0000
@@ -142,6 +142,7 @@ ihidev_attach(struct device *parent, str
sc->sc_tag = ia->ia_tag;
sc->sc_addr = ia->ia_addr;
sc->sc_hid_desc_addr = ia->ia_size;
+ sc->sc_lastrepid = -1;
if (ihidev_hid_command(sc, I2C_HID_CMD_DESCR, NULL) ||
ihidev_hid_desc_parse(sc)) {
@@ -159,10 +160,13 @@ ihidev_attach(struct device *parent, str
/* find largest report size and allocate memory for input buffer */
sc->sc_isize = letoh16(sc->hid_desc.wMaxInputLength);
+ sc->sc_repsizes = mallocarray(sc->sc_nrepid, sizeof(int),
+ M_DEVBUF, M_WAITOK | M_ZERO);
for (repid = 0; repid < sc->sc_nrepid; repid++) {
repsz = hid_report_size(sc->sc_report, sc->sc_reportlen,
hid_input, repid);
repsizes[repid] = repsz;
+ sc->sc_repsizes[repid] = repsz;
if (repsz > sc->sc_isize)
sc->sc_isize = repsz;
if (repsz != 0)
@@ -255,6 +259,9 @@ ihidev_detach(struct device *self, int f
if (sc->sc_report != NULL)
free(sc->sc_report, M_DEVBUF, sc->sc_reportlen);
+ if (sc->sc_repsizes != NULL)
+ free(sc->sc_repsizes, M_DEVBUF, sc->sc_nrepid * sizeof(int));
+
return (0);
}
@@ -718,10 +725,21 @@ ihidev_intr(void *arg)
psize = sc->sc_ibuf[0] | sc->sc_ibuf[1] << 8;
if (psize <= 2 || psize > sc->sc_isize) {
if (sc->sc_poll) {
- /*
- * TODO: all fingers are up, should we pass to hid
- * layer?
- */
+ /* empty packet: hand the last subdev a zeroed report
+ * once so it releases its contacts (polled finger-up) */
+ int lrep = sc->sc_lastrepid;
+ int rsz;
+
+ if (lrep >= 0 && lrep < sc->sc_nrepid &&
+ (scd = sc->sc_subdevs[lrep]) != NULL &&
+ (scd->sc_state & IHIDEV_OPEN) && !sc->sc_dying) {
+ rsz = sc->sc_repsizes[lrep];
+ if (rsz > 0 && rsz <= sc->sc_isize) {
+ memset(sc->sc_ibuf, 0, rsz);
+ scd->sc_intr(scd, sc->sc_ibuf, rsz);
+ }
+ sc->sc_lastrepid = -1;
+ }
sc->sc_fastpoll = 0;
goto more_polling;
} else
@@ -770,8 +788,10 @@ ihidev_intr(void *arg)
return (1);
}
- if (!sc->sc_dying)
+ if (!sc->sc_dying) {
+ sc->sc_lastrepid = rep;
scd->sc_intr(scd, p, psize);
+ }
if (sc->sc_poll && (fast != sc->sc_fastpoll)) {
DPRINTF(("%s: %s->%s polling\n", sc->sc_dev.dv_xname,
Index: sys/dev/i2c/ihidev.h
===================================================================
RCS file: /cvs/src/sys/dev/i2c/ihidev.h,v
diff -u -p -u -p -r1.11 ihidev.h
--- sys/dev/i2c/ihidev.h 7 Jan 2025 19:26:14 -0000 1.11
+++ sys/dev/i2c/ihidev.h 22 May 2026 17:18:37 -0000
@@ -85,6 +85,8 @@ struct ihidev_softc {
u_int sc_isize;
u_char *sc_ibuf;
+ int sc_lastrepid; /* report id of last non-empty input */
+ int *sc_repsizes; /* per-report input size, for poll path */
int sc_refcnt;
Index: sys/dev/i2c/imt.c
===================================================================
RCS file: /cvs/src/sys/dev/i2c/imt.c,v
diff -u -p -u -p -r1.7 imt.c
--- sys/dev/i2c/imt.c 21 Jul 2025 21:46:40 -0000 1.7
+++ sys/dev/i2c/imt.c 22 May 2026 17:18:37 -0000
@@ -83,11 +83,15 @@ imt_match(struct device *parent, void *m
if (iha->reportid == IHIDEV_CLAIM_MULTIPLEID) {
ihidev_get_report_desc(iha->parent, &desc, &size);
if (hidmt_find_winptp_reports(desc, size,
+ &input_rid, &conf_rid, &cap_rid) ||
+ hidmt_find_touchscreen_reports(desc, size,
&input_rid, &conf_rid, &cap_rid)) {
- iha->claims[0] = input_rid;
- iha->claims[1] = conf_rid;
- iha->claims[2] = cap_rid;
- iha->nclaims = 3;
+ /* a touchscreen may have no config (Input Mode) report */
+ iha->nclaims = 0;
+ iha->claims[iha->nclaims++] = input_rid;
+ if (conf_rid > 0)
+ iha->claims[iha->nclaims++] = conf_rid;
+ iha->claims[iha->nclaims++] = cap_rid;
return (IMATCH_DEVCLASS_DEVSUBCLASS);
}
}
@@ -108,13 +112,18 @@ imt_attach(struct device *parent, struct
sc->sc_hdev.sc_parent = iha->parent;
ihidev_get_report_desc(iha->parent, &desc, &size);
- hidmt_find_winptp_reports(desc, size, &sc->sc_rep_input,
- &sc->sc_rep_config, &sc->sc_rep_cap);
memset(mt, 0, sizeof(sc->sc_mt));
- /* assume everything has "natural scrolling" where Y axis is reversed */
- mt->sc_flags = HIDMT_REVY;
+ if (hidmt_find_winptp_reports(desc, size, &sc->sc_rep_input,
+ &sc->sc_rep_config, &sc->sc_rep_cap)) {
+ /* assume "natural scrolling" where the Y axis is reversed */
+ mt->sc_flags = HIDMT_REVY;
+ } else if (hidmt_find_touchscreen_reports(desc, size, &sc->sc_rep_input,
+ &sc->sc_rep_config, &sc->sc_rep_cap)) {
+ /* an absolute touch panel maps 1:1, so do not reverse Y */
+ mt->sc_flags = HIDMT_TOUCHSCREEN;
+ }
mt->hidev_report_type_conv = ihidev_report_type_conv;
mt->hidev_get_report = imt_hidev_get_report;
Touchscreen support for the Samsung Galaxy Book4 Edge