xref: /openbsd-src/sys/dev/usb/uwacom.c (revision cdf254201b73713dd63037c9e8b9b303393d60a4)
1*cdf25420Smiod /*	$OpenBSD: uwacom.c,v 1.8 2023/08/12 20:47:06 miod Exp $	*/
27af0d1f0Smpi 
37af0d1f0Smpi /*
47af0d1f0Smpi  * Copyright (c) 2016 Frank Groeneveld <frank@frankgroeneveld.nl>
57af0d1f0Smpi  *
67af0d1f0Smpi  * Permission to use, copy, modify, and distribute this software for any
77af0d1f0Smpi  * purpose with or without fee is hereby granted, provided that the above
87af0d1f0Smpi  * copyright notice and this permission notice appear in all copies.
97af0d1f0Smpi  *
107af0d1f0Smpi  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCAIMS ALL WARRANTIES
117af0d1f0Smpi  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
127af0d1f0Smpi  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
137af0d1f0Smpi  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
147af0d1f0Smpi  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
157af0d1f0Smpi  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
167af0d1f0Smpi  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
177af0d1f0Smpi  */
187af0d1f0Smpi 
197af0d1f0Smpi /* Driver for USB Wacom tablets */
207af0d1f0Smpi 
217af0d1f0Smpi #include <sys/param.h>
227af0d1f0Smpi #include <sys/systm.h>
237af0d1f0Smpi #include <sys/device.h>
247af0d1f0Smpi 
257af0d1f0Smpi #include <dev/usb/usb.h>
267af0d1f0Smpi #include <dev/usb/usbhid.h>
277af0d1f0Smpi 
287af0d1f0Smpi #include <dev/usb/usbdi.h>
290eaeb466Smglocker #include <dev/usb/usbdi_util.h>
307af0d1f0Smpi #include <dev/usb/usbdevs.h>
317af0d1f0Smpi #include <dev/usb/uhidev.h>
327af0d1f0Smpi 
337af0d1f0Smpi #include <dev/wscons/wsconsio.h>
347af0d1f0Smpi #include <dev/wscons/wsmousevar.h>
357af0d1f0Smpi 
367af0d1f0Smpi #include <dev/hid/hidmsvar.h>
377af0d1f0Smpi 
38a55840e5Ssdk #define	UWACOM_USE_PRESSURE	0x0001 /* button 0 is flaky, use tip pressure */
39a55840e5Ssdk #define	UWACOM_BIG_ENDIAN	0x0002 /* XY reporting byte order */
40a55840e5Ssdk 
417af0d1f0Smpi struct uwacom_softc {
427af0d1f0Smpi 	struct uhidev		sc_hdev;
437af0d1f0Smpi 	struct hidms		sc_ms;
447af0d1f0Smpi 	struct hid_location	sc_loc_tip_press;
45a55840e5Ssdk 	int			sc_flags;
46*cdf25420Smiod 	int			sc_x, sc_y, sc_z, sc_w;
47*cdf25420Smiod 	int			sc_moved;
487af0d1f0Smpi };
497af0d1f0Smpi 
507af0d1f0Smpi struct cfdriver uwacom_cd = {
517af0d1f0Smpi 	NULL, "uwacom", DV_DULL
527af0d1f0Smpi };
537af0d1f0Smpi 
547af0d1f0Smpi const struct usb_devno uwacom_devs[] = {
55a55840e5Ssdk 	{ USB_VENDOR_WACOM, USB_PRODUCT_WACOM_INTUOS_DRAW },
56f58d75a9Smglocker 	{ USB_VENDOR_WACOM, USB_PRODUCT_WACOM_ONE_S },
57*cdf25420Smiod 	{ USB_VENDOR_WACOM, USB_PRODUCT_WACOM_ONE_M },
58*cdf25420Smiod 	{ USB_VENDOR_WACOM, USB_PRODUCT_WACOM_INTUOS_S }
597af0d1f0Smpi };
607af0d1f0Smpi 
617af0d1f0Smpi int	uwacom_match(struct device *, void *, void *);
627af0d1f0Smpi void	uwacom_attach(struct device *, struct device *, void *);
637af0d1f0Smpi int	uwacom_detach(struct device *, int);
64*cdf25420Smiod void	uwacom_intr_legacy(struct uhidev *, void *, u_int);
657af0d1f0Smpi void	uwacom_intr(struct uhidev *, void *, u_int);
667af0d1f0Smpi int	uwacom_enable(void *);
677af0d1f0Smpi void	uwacom_disable(void *);
687af0d1f0Smpi int	uwacom_ioctl(void *, u_long, caddr_t, int, struct proc *);
697af0d1f0Smpi 
707af0d1f0Smpi const struct cfattach uwacom_ca = {
717af0d1f0Smpi 	sizeof(struct uwacom_softc), uwacom_match, uwacom_attach, uwacom_detach
727af0d1f0Smpi };
737af0d1f0Smpi 
747af0d1f0Smpi const struct wsmouse_accessops uwacom_accessops = {
757af0d1f0Smpi 	uwacom_enable,
767af0d1f0Smpi 	uwacom_ioctl,
777af0d1f0Smpi 	uwacom_disable,
787af0d1f0Smpi };
797af0d1f0Smpi 
807af0d1f0Smpi int
uwacom_match(struct device * parent,void * match,void * aux)817af0d1f0Smpi uwacom_match(struct device *parent, void *match, void *aux)
827af0d1f0Smpi {
837af0d1f0Smpi 	struct uhidev_attach_arg *uha = aux;
847af0d1f0Smpi 	int size;
857af0d1f0Smpi 	void *desc;
867af0d1f0Smpi 
8736b6d2a9Santon 	if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha))
8836b6d2a9Santon 		return (UMATCH_NONE);
8936b6d2a9Santon 
907af0d1f0Smpi 	if (usb_lookup(uwacom_devs, uha->uaa->vendor,
917af0d1f0Smpi 	    uha->uaa->product) == NULL)
927af0d1f0Smpi 		return (UMATCH_NONE);
937af0d1f0Smpi 
947af0d1f0Smpi 	uhidev_get_report_desc(uha->parent, &desc, &size);
957af0d1f0Smpi 
96*cdf25420Smiod 	if (hid_is_collection(desc, size, uha->reportid,
97*cdf25420Smiod 	    HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_DIGITIZER)))
98*cdf25420Smiod 		return (UMATCH_IFACECLASS);
997af0d1f0Smpi 	if (!hid_locate(desc, size, HID_USAGE2(HUP_WACOM, HUG_POINTER),
1007af0d1f0Smpi 	    uha->reportid, hid_input, NULL, NULL))
1017af0d1f0Smpi 		return (UMATCH_NONE);
1027af0d1f0Smpi 
1037af0d1f0Smpi 	return (UMATCH_IFACECLASS);
1047af0d1f0Smpi }
1057af0d1f0Smpi 
1067af0d1f0Smpi void
uwacom_attach(struct device * parent,struct device * self,void * aux)1077af0d1f0Smpi uwacom_attach(struct device *parent, struct device *self, void *aux)
1087af0d1f0Smpi {
1097af0d1f0Smpi 	struct uwacom_softc *sc = (struct uwacom_softc *)self;
1107af0d1f0Smpi 	struct hidms *ms = &sc->sc_ms;
1117af0d1f0Smpi 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
1127af0d1f0Smpi 	struct usb_attach_arg *uaa = uha->uaa;
113*cdf25420Smiod 	static uByte wacom_report_buf[2] = { 0x02, 0x02 };
1146cdbbe9bSanton 	int size, repid;
1157af0d1f0Smpi 	void *desc;
1167af0d1f0Smpi 
117*cdf25420Smiod 	sc->sc_hdev.sc_intr = uwacom_intr_legacy;
1187af0d1f0Smpi 	sc->sc_hdev.sc_parent = uha->parent;
1197af0d1f0Smpi 	sc->sc_hdev.sc_udev = uaa->device;
1207af0d1f0Smpi 	sc->sc_hdev.sc_report_id = uha->reportid;
1217af0d1f0Smpi 
1220eaeb466Smglocker 	usbd_set_idle(uha->parent->sc_udev, uha->parent->sc_ifaceno, 0, 0);
1230eaeb466Smglocker 
1247af0d1f0Smpi 	uhidev_get_report_desc(uha->parent, &desc, &size);
1256cdbbe9bSanton 	repid = uha->reportid;
126a55840e5Ssdk 
1276cdbbe9bSanton 	sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid);
1286cdbbe9bSanton 	sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid);
1296cdbbe9bSanton 	sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid);
1307af0d1f0Smpi 
1317af0d1f0Smpi 	ms->sc_device = self;
1327af0d1f0Smpi 	ms->sc_rawmode = 1;
1337af0d1f0Smpi 	ms->sc_flags = HIDMS_ABSX | HIDMS_ABSY;
1347af0d1f0Smpi 	ms->sc_num_buttons = 3;
135a55840e5Ssdk 
1367af0d1f0Smpi 	ms->sc_loc_x.pos = 8;
1377af0d1f0Smpi 	ms->sc_loc_x.size = 16;
1387af0d1f0Smpi 	ms->sc_loc_y.pos = 24;
1397af0d1f0Smpi 	ms->sc_loc_y.size = 16;
1407af0d1f0Smpi 
1417af0d1f0Smpi 	ms->sc_tsscale.minx = 0;
1427af0d1f0Smpi 	ms->sc_tsscale.miny = 0;
1437af0d1f0Smpi 
1447af0d1f0Smpi 	ms->sc_loc_btn[0].pos = 0;
1457af0d1f0Smpi 	ms->sc_loc_btn[0].size = 1;
1467af0d1f0Smpi 	ms->sc_loc_btn[1].pos = 1;
1477af0d1f0Smpi 	ms->sc_loc_btn[1].size = 1;
1487af0d1f0Smpi 	ms->sc_loc_btn[2].pos = 2;
1497af0d1f0Smpi 	ms->sc_loc_btn[2].size = 1;
1507af0d1f0Smpi 
151*cdf25420Smiod 	switch (uha->uaa->product) {
152*cdf25420Smiod 	case USB_PRODUCT_WACOM_ONE_S:
153*cdf25420Smiod 	case USB_PRODUCT_WACOM_INTUOS_S:
154*cdf25420Smiod 		uhidev_set_report(uha->parent, UHID_FEATURE_REPORT,
155*cdf25420Smiod 		    sc->sc_hdev.sc_report_id, &wacom_report_buf,
156*cdf25420Smiod 		    sizeof(wacom_report_buf));
157*cdf25420Smiod 		sc->sc_hdev.sc_intr = uwacom_intr;
158*cdf25420Smiod 		hidms_setup((struct device *)sc, ms, HIDMS_WACOM_SETUP,
159*cdf25420Smiod 		    repid, desc, size);
160*cdf25420Smiod 		break;
161*cdf25420Smiod 	case USB_PRODUCT_WACOM_INTUOS_DRAW:
162a55840e5Ssdk 		sc->sc_flags = UWACOM_USE_PRESSURE | UWACOM_BIG_ENDIAN;
1637af0d1f0Smpi 		sc->sc_loc_tip_press.pos = 43;
1647af0d1f0Smpi 		sc->sc_loc_tip_press.size = 8;
165a55840e5Ssdk 		ms->sc_tsscale.maxx = 7600;
166a55840e5Ssdk 		ms->sc_tsscale.maxy = 4750;
167*cdf25420Smiod 		break;
168a55840e5Ssdk 	}
1697af0d1f0Smpi 
1707af0d1f0Smpi 	hidms_attach(ms, &uwacom_accessops);
1717af0d1f0Smpi }
1727af0d1f0Smpi 
1737af0d1f0Smpi int
uwacom_detach(struct device * self,int flags)1747af0d1f0Smpi uwacom_detach(struct device *self, int flags)
1757af0d1f0Smpi {
1767af0d1f0Smpi 	struct uwacom_softc *sc = (struct uwacom_softc *)self;
1777af0d1f0Smpi 	struct hidms *ms = &sc->sc_ms;
1787af0d1f0Smpi 
1797af0d1f0Smpi 	return hidms_detach(ms, flags);
1807af0d1f0Smpi }
1817af0d1f0Smpi 
1827af0d1f0Smpi void
uwacom_intr_legacy(struct uhidev * addr,void * buf,u_int len)183*cdf25420Smiod uwacom_intr_legacy(struct uhidev *addr, void *buf, u_int len)
1847af0d1f0Smpi {
1857af0d1f0Smpi 	struct uwacom_softc *sc = (struct uwacom_softc *)addr;
1867af0d1f0Smpi 	struct hidms *ms = &sc->sc_ms;
1877af0d1f0Smpi 	u_int32_t buttons = 0;
1887af0d1f0Smpi 	uint8_t *data = (uint8_t *)buf;
1897af0d1f0Smpi 	int i, x, y, pressure;
1907af0d1f0Smpi 
1917af0d1f0Smpi 	if (ms->sc_enabled == 0)
1927af0d1f0Smpi 		return;
1937af0d1f0Smpi 
1947af0d1f0Smpi 	/* ignore proximity, it will cause invalid button 2 events */
1957af0d1f0Smpi 	if ((data[0] & 0xf0) == 0xc0)
1967af0d1f0Smpi 		return;
1977af0d1f0Smpi 
198a55840e5Ssdk 	x = hid_get_data(data, len, &ms->sc_loc_x);
199a55840e5Ssdk 	y = hid_get_data(data, len, &ms->sc_loc_y);
200a55840e5Ssdk 
201a55840e5Ssdk 	if (sc->sc_flags & UWACOM_BIG_ENDIAN) {
202a55840e5Ssdk 		x = be16toh(x);
203a55840e5Ssdk 		y = be16toh(y);
204a55840e5Ssdk 	}
2057af0d1f0Smpi 
2067af0d1f0Smpi 	for (i = 0; i < ms->sc_num_buttons; i++)
2077af0d1f0Smpi 		if (hid_get_data(data, len, &ms->sc_loc_btn[i]))
208*cdf25420Smiod 			buttons |= 1 << i;
2097af0d1f0Smpi 
210a55840e5Ssdk 	if (sc->sc_flags & UWACOM_USE_PRESSURE) {
211a55840e5Ssdk 		pressure = hid_get_data(data, len, &sc->sc_loc_tip_press);
2127af0d1f0Smpi 		if (pressure > 10)
2137af0d1f0Smpi 			buttons |= 1;
2147af0d1f0Smpi 		else
2157af0d1f0Smpi 			buttons &= ~1;
216a55840e5Ssdk 	}
2177af0d1f0Smpi 
2187af0d1f0Smpi 	if (x != 0 || y != 0 || buttons != ms->sc_buttons) {
2197af0d1f0Smpi 		wsmouse_position(ms->sc_wsmousedev, x, y);
2207af0d1f0Smpi 		wsmouse_buttons(ms->sc_wsmousedev, buttons);
2217af0d1f0Smpi 		wsmouse_input_sync(ms->sc_wsmousedev);
2227af0d1f0Smpi 	}
2237af0d1f0Smpi }
2247af0d1f0Smpi 
225*cdf25420Smiod void
uwacom_intr(struct uhidev * addr,void * buf,u_int len)226*cdf25420Smiod uwacom_intr(struct uhidev *addr, void *buf, u_int len)
227*cdf25420Smiod {
228*cdf25420Smiod 	struct uwacom_softc *sc = (struct uwacom_softc *)addr;
229*cdf25420Smiod 	struct hidms *ms = &sc->sc_ms;
230*cdf25420Smiod 	u_int32_t buttons = 0;
231*cdf25420Smiod 	uint8_t *data = (uint8_t *)buf;
232*cdf25420Smiod 	int i, j, x, y, dx, dy, dz, dw, pressure, distance;
233*cdf25420Smiod 
234*cdf25420Smiod 	if (ms->sc_enabled == 0)
235*cdf25420Smiod 		return;
236*cdf25420Smiod 
237*cdf25420Smiod 	x = hid_get_data(data, len, &ms->sc_loc_x);
238*cdf25420Smiod 	y = hid_get_data(data, len, &ms->sc_loc_y);
239*cdf25420Smiod 	pressure = hid_get_data(data, len, &ms->sc_loc_z);
240*cdf25420Smiod 	distance = hid_get_data(data, len, &ms->sc_loc_w);
241*cdf25420Smiod 
242*cdf25420Smiod 	if (!sc->sc_moved) {
243*cdf25420Smiod 		sc->sc_x = x;
244*cdf25420Smiod 		sc->sc_y = y;
245*cdf25420Smiod 		sc->sc_z = pressure;
246*cdf25420Smiod 		sc->sc_w = distance;
247*cdf25420Smiod 		sc->sc_moved = 1;
248*cdf25420Smiod 	}
249*cdf25420Smiod 
250*cdf25420Smiod 	dx = sc->sc_x - x;
251*cdf25420Smiod 	dy = sc->sc_y - y;
252*cdf25420Smiod 	/* Clamp sensitivity to +/-127 */
253*cdf25420Smiod 	dz = sc->sc_z / 32 - pressure / 32;
254*cdf25420Smiod 	dw = sc->sc_w - distance;
255*cdf25420Smiod 
256*cdf25420Smiod 	sc->sc_x = x;
257*cdf25420Smiod 	sc->sc_y = y;
258*cdf25420Smiod 	sc->sc_z = pressure;
259*cdf25420Smiod 	sc->sc_w = distance;
260*cdf25420Smiod 
261*cdf25420Smiod 	if (sc->sc_flags & UWACOM_BIG_ENDIAN) {
262*cdf25420Smiod 		x = be16toh(x);
263*cdf25420Smiod 		y = be16toh(y);
264*cdf25420Smiod 	}
265*cdf25420Smiod 
266*cdf25420Smiod 	for (i = 0; i < ms->sc_num_stylus_buttons; i++)
267*cdf25420Smiod 		if (hid_get_data(data, len, &ms->sc_loc_btn[i]))
268*cdf25420Smiod 			buttons |= 1 << i;
269*cdf25420Smiod 
270*cdf25420Smiod 	for (j = 0; i < ms->sc_num_buttons; i++, j++)
271*cdf25420Smiod 		if (hid_get_data(data, len, &ms->sc_loc_btn[i]))
272*cdf25420Smiod 			buttons |= 1 << j;
273*cdf25420Smiod 
274*cdf25420Smiod 	if (x != 0 || y != 0 || pressure != 0 || distance != 0 ||
275*cdf25420Smiod 	    buttons != ms->sc_buttons) {
276*cdf25420Smiod 		wsmouse_motion(ms->sc_wsmousedev, -dx, dy, dz, dw);
277*cdf25420Smiod 		wsmouse_buttons(ms->sc_wsmousedev, buttons);
278*cdf25420Smiod 		wsmouse_input_sync(ms->sc_wsmousedev);
279*cdf25420Smiod 	}
280*cdf25420Smiod }
281*cdf25420Smiod 
2827af0d1f0Smpi int
uwacom_enable(void * v)2837af0d1f0Smpi uwacom_enable(void *v)
2847af0d1f0Smpi {
2857af0d1f0Smpi 	struct uwacom_softc *sc = v;
2867af0d1f0Smpi 	struct hidms *ms = &sc->sc_ms;
2877af0d1f0Smpi 	int rv;
2887af0d1f0Smpi 
2897af0d1f0Smpi 	if (usbd_is_dying(sc->sc_hdev.sc_udev))
2907af0d1f0Smpi 		return EIO;
2917af0d1f0Smpi 
2927af0d1f0Smpi 	if ((rv = hidms_enable(ms)) != 0)
2937af0d1f0Smpi 		return rv;
2947af0d1f0Smpi 
2957af0d1f0Smpi 	return uhidev_open(&sc->sc_hdev);
2967af0d1f0Smpi }
2977af0d1f0Smpi 
2987af0d1f0Smpi void
uwacom_disable(void * v)2997af0d1f0Smpi uwacom_disable(void *v)
3007af0d1f0Smpi {
3017af0d1f0Smpi 	struct uwacom_softc *sc = v;
3027af0d1f0Smpi 	struct hidms *ms = &sc->sc_ms;
3037af0d1f0Smpi 
3047af0d1f0Smpi 	hidms_disable(ms);
3057af0d1f0Smpi 	uhidev_close(&sc->sc_hdev);
3067af0d1f0Smpi }
3077af0d1f0Smpi 
3087af0d1f0Smpi int
uwacom_ioctl(void * v,u_long cmd,caddr_t data,int flag,struct proc * p)3097af0d1f0Smpi uwacom_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
3107af0d1f0Smpi {
3117af0d1f0Smpi 	struct uwacom_softc *sc = v;
3127af0d1f0Smpi 	struct hidms *ms = &sc->sc_ms;
3137af0d1f0Smpi 	int rc;
3147af0d1f0Smpi 
3157af0d1f0Smpi 	switch (cmd) {
3167af0d1f0Smpi 	case WSMOUSEIO_GTYPE:
3177af0d1f0Smpi 		*(u_int *)data = WSMOUSE_TYPE_TPANEL;
3187af0d1f0Smpi 		return 0;
3197af0d1f0Smpi 	}
3207af0d1f0Smpi 
3217af0d1f0Smpi 	rc = uhidev_ioctl(&sc->sc_hdev, cmd, data, flag, p);
3227af0d1f0Smpi 	if (rc != -1)
3237af0d1f0Smpi 		return rc;
3247af0d1f0Smpi 
3257af0d1f0Smpi 	return hidms_ioctl(ms, cmd, data, flag, p);
3267af0d1f0Smpi }
3277af0d1f0Smpi 
328