Index | Thread | Search

From:
Kirill A. Korinsky <kirill@korins.ky>
Subject:
Re: Touchscreen support for the Samsung Galaxy Book4 Edge
To:
Marcus Glocker <marcus@nazgul.ch>
Cc:
tech@openbsd.org
Date:
Fri, 22 May 2026 20:35:20 +0200

Download raw body.

Thread
On Fri, 22 May 2026 19:52:48 +0200,
Marcus Glocker <marcus@nazgul.ch> 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 <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.
> 
>

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