From: Kirill A. Korinsky Subject: Re: Touchscreen support for the Samsung Galaxy Book4 Edge To: Marcus Glocker Cc: tech@openbsd.org Date: Fri, 22 May 2026 20:35:20 +0200 On Fri, 22 May 2026 19:52:48 +0200, Marcus Glocker wrote: > > 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 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 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. > > Doesn't work and I haven't see changes in dmesg. > 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; -- wbr, Kirill