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