1 /* $OpenBSD: uwacom.c,v 1.8 2023/08/12 20:47:06 miod Exp $ */
2
3 /*
4 * Copyright (c) 2016 Frank Groeneveld <frank@frankgroeneveld.nl>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 /* Driver for USB Wacom tablets */
20
21 #include <sys/param.h>
22 #include <sys/systm.h>
23 #include <sys/device.h>
24
25 #include <dev/usb/usb.h>
26 #include <dev/usb/usbhid.h>
27
28 #include <dev/usb/usbdi.h>
29 #include <dev/usb/usbdi_util.h>
30 #include <dev/usb/usbdevs.h>
31 #include <dev/usb/uhidev.h>
32
33 #include <dev/wscons/wsconsio.h>
34 #include <dev/wscons/wsmousevar.h>
35
36 #include <dev/hid/hidmsvar.h>
37
38 #define UWACOM_USE_PRESSURE 0x0001 /* button 0 is flaky, use tip pressure */
39 #define UWACOM_BIG_ENDIAN 0x0002 /* XY reporting byte order */
40
41 struct uwacom_softc {
42 struct uhidev sc_hdev;
43 struct hidms sc_ms;
44 struct hid_location sc_loc_tip_press;
45 int sc_flags;
46 int sc_x, sc_y, sc_z, sc_w;
47 int sc_moved;
48 };
49
50 struct cfdriver uwacom_cd = {
51 NULL, "uwacom", DV_DULL
52 };
53
54 const struct usb_devno uwacom_devs[] = {
55 { USB_VENDOR_WACOM, USB_PRODUCT_WACOM_INTUOS_DRAW },
56 { USB_VENDOR_WACOM, USB_PRODUCT_WACOM_ONE_S },
57 { USB_VENDOR_WACOM, USB_PRODUCT_WACOM_ONE_M },
58 { USB_VENDOR_WACOM, USB_PRODUCT_WACOM_INTUOS_S }
59 };
60
61 int uwacom_match(struct device *, void *, void *);
62 void uwacom_attach(struct device *, struct device *, void *);
63 int uwacom_detach(struct device *, int);
64 void uwacom_intr_legacy(struct uhidev *, void *, u_int);
65 void uwacom_intr(struct uhidev *, void *, u_int);
66 int uwacom_enable(void *);
67 void uwacom_disable(void *);
68 int uwacom_ioctl(void *, u_long, caddr_t, int, struct proc *);
69
70 const struct cfattach uwacom_ca = {
71 sizeof(struct uwacom_softc), uwacom_match, uwacom_attach, uwacom_detach
72 };
73
74 const struct wsmouse_accessops uwacom_accessops = {
75 uwacom_enable,
76 uwacom_ioctl,
77 uwacom_disable,
78 };
79
80 int
uwacom_match(struct device * parent,void * match,void * aux)81 uwacom_match(struct device *parent, void *match, void *aux)
82 {
83 struct uhidev_attach_arg *uha = aux;
84 int size;
85 void *desc;
86
87 if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha))
88 return (UMATCH_NONE);
89
90 if (usb_lookup(uwacom_devs, uha->uaa->vendor,
91 uha->uaa->product) == NULL)
92 return (UMATCH_NONE);
93
94 uhidev_get_report_desc(uha->parent, &desc, &size);
95
96 if (hid_is_collection(desc, size, uha->reportid,
97 HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_DIGITIZER)))
98 return (UMATCH_IFACECLASS);
99 if (!hid_locate(desc, size, HID_USAGE2(HUP_WACOM, HUG_POINTER),
100 uha->reportid, hid_input, NULL, NULL))
101 return (UMATCH_NONE);
102
103 return (UMATCH_IFACECLASS);
104 }
105
106 void
uwacom_attach(struct device * parent,struct device * self,void * aux)107 uwacom_attach(struct device *parent, struct device *self, void *aux)
108 {
109 struct uwacom_softc *sc = (struct uwacom_softc *)self;
110 struct hidms *ms = &sc->sc_ms;
111 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
112 struct usb_attach_arg *uaa = uha->uaa;
113 static uByte wacom_report_buf[2] = { 0x02, 0x02 };
114 int size, repid;
115 void *desc;
116
117 sc->sc_hdev.sc_intr = uwacom_intr_legacy;
118 sc->sc_hdev.sc_parent = uha->parent;
119 sc->sc_hdev.sc_udev = uaa->device;
120 sc->sc_hdev.sc_report_id = uha->reportid;
121
122 usbd_set_idle(uha->parent->sc_udev, uha->parent->sc_ifaceno, 0, 0);
123
124 uhidev_get_report_desc(uha->parent, &desc, &size);
125 repid = uha->reportid;
126
127 sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid);
128 sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid);
129 sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid);
130
131 ms->sc_device = self;
132 ms->sc_rawmode = 1;
133 ms->sc_flags = HIDMS_ABSX | HIDMS_ABSY;
134 ms->sc_num_buttons = 3;
135
136 ms->sc_loc_x.pos = 8;
137 ms->sc_loc_x.size = 16;
138 ms->sc_loc_y.pos = 24;
139 ms->sc_loc_y.size = 16;
140
141 ms->sc_tsscale.minx = 0;
142 ms->sc_tsscale.miny = 0;
143
144 ms->sc_loc_btn[0].pos = 0;
145 ms->sc_loc_btn[0].size = 1;
146 ms->sc_loc_btn[1].pos = 1;
147 ms->sc_loc_btn[1].size = 1;
148 ms->sc_loc_btn[2].pos = 2;
149 ms->sc_loc_btn[2].size = 1;
150
151 switch (uha->uaa->product) {
152 case USB_PRODUCT_WACOM_ONE_S:
153 case USB_PRODUCT_WACOM_INTUOS_S:
154 uhidev_set_report(uha->parent, UHID_FEATURE_REPORT,
155 sc->sc_hdev.sc_report_id, &wacom_report_buf,
156 sizeof(wacom_report_buf));
157 sc->sc_hdev.sc_intr = uwacom_intr;
158 hidms_setup((struct device *)sc, ms, HIDMS_WACOM_SETUP,
159 repid, desc, size);
160 break;
161 case USB_PRODUCT_WACOM_INTUOS_DRAW:
162 sc->sc_flags = UWACOM_USE_PRESSURE | UWACOM_BIG_ENDIAN;
163 sc->sc_loc_tip_press.pos = 43;
164 sc->sc_loc_tip_press.size = 8;
165 ms->sc_tsscale.maxx = 7600;
166 ms->sc_tsscale.maxy = 4750;
167 break;
168 }
169
170 hidms_attach(ms, &uwacom_accessops);
171 }
172
173 int
uwacom_detach(struct device * self,int flags)174 uwacom_detach(struct device *self, int flags)
175 {
176 struct uwacom_softc *sc = (struct uwacom_softc *)self;
177 struct hidms *ms = &sc->sc_ms;
178
179 return hidms_detach(ms, flags);
180 }
181
182 void
uwacom_intr_legacy(struct uhidev * addr,void * buf,u_int len)183 uwacom_intr_legacy(struct uhidev *addr, void *buf, u_int len)
184 {
185 struct uwacom_softc *sc = (struct uwacom_softc *)addr;
186 struct hidms *ms = &sc->sc_ms;
187 u_int32_t buttons = 0;
188 uint8_t *data = (uint8_t *)buf;
189 int i, x, y, pressure;
190
191 if (ms->sc_enabled == 0)
192 return;
193
194 /* ignore proximity, it will cause invalid button 2 events */
195 if ((data[0] & 0xf0) == 0xc0)
196 return;
197
198 x = hid_get_data(data, len, &ms->sc_loc_x);
199 y = hid_get_data(data, len, &ms->sc_loc_y);
200
201 if (sc->sc_flags & UWACOM_BIG_ENDIAN) {
202 x = be16toh(x);
203 y = be16toh(y);
204 }
205
206 for (i = 0; i < ms->sc_num_buttons; i++)
207 if (hid_get_data(data, len, &ms->sc_loc_btn[i]))
208 buttons |= 1 << i;
209
210 if (sc->sc_flags & UWACOM_USE_PRESSURE) {
211 pressure = hid_get_data(data, len, &sc->sc_loc_tip_press);
212 if (pressure > 10)
213 buttons |= 1;
214 else
215 buttons &= ~1;
216 }
217
218 if (x != 0 || y != 0 || buttons != ms->sc_buttons) {
219 wsmouse_position(ms->sc_wsmousedev, x, y);
220 wsmouse_buttons(ms->sc_wsmousedev, buttons);
221 wsmouse_input_sync(ms->sc_wsmousedev);
222 }
223 }
224
225 void
uwacom_intr(struct uhidev * addr,void * buf,u_int len)226 uwacom_intr(struct uhidev *addr, void *buf, u_int len)
227 {
228 struct uwacom_softc *sc = (struct uwacom_softc *)addr;
229 struct hidms *ms = &sc->sc_ms;
230 u_int32_t buttons = 0;
231 uint8_t *data = (uint8_t *)buf;
232 int i, j, x, y, dx, dy, dz, dw, pressure, distance;
233
234 if (ms->sc_enabled == 0)
235 return;
236
237 x = hid_get_data(data, len, &ms->sc_loc_x);
238 y = hid_get_data(data, len, &ms->sc_loc_y);
239 pressure = hid_get_data(data, len, &ms->sc_loc_z);
240 distance = hid_get_data(data, len, &ms->sc_loc_w);
241
242 if (!sc->sc_moved) {
243 sc->sc_x = x;
244 sc->sc_y = y;
245 sc->sc_z = pressure;
246 sc->sc_w = distance;
247 sc->sc_moved = 1;
248 }
249
250 dx = sc->sc_x - x;
251 dy = sc->sc_y - y;
252 /* Clamp sensitivity to +/-127 */
253 dz = sc->sc_z / 32 - pressure / 32;
254 dw = sc->sc_w - distance;
255
256 sc->sc_x = x;
257 sc->sc_y = y;
258 sc->sc_z = pressure;
259 sc->sc_w = distance;
260
261 if (sc->sc_flags & UWACOM_BIG_ENDIAN) {
262 x = be16toh(x);
263 y = be16toh(y);
264 }
265
266 for (i = 0; i < ms->sc_num_stylus_buttons; i++)
267 if (hid_get_data(data, len, &ms->sc_loc_btn[i]))
268 buttons |= 1 << i;
269
270 for (j = 0; i < ms->sc_num_buttons; i++, j++)
271 if (hid_get_data(data, len, &ms->sc_loc_btn[i]))
272 buttons |= 1 << j;
273
274 if (x != 0 || y != 0 || pressure != 0 || distance != 0 ||
275 buttons != ms->sc_buttons) {
276 wsmouse_motion(ms->sc_wsmousedev, -dx, dy, dz, dw);
277 wsmouse_buttons(ms->sc_wsmousedev, buttons);
278 wsmouse_input_sync(ms->sc_wsmousedev);
279 }
280 }
281
282 int
uwacom_enable(void * v)283 uwacom_enable(void *v)
284 {
285 struct uwacom_softc *sc = v;
286 struct hidms *ms = &sc->sc_ms;
287 int rv;
288
289 if (usbd_is_dying(sc->sc_hdev.sc_udev))
290 return EIO;
291
292 if ((rv = hidms_enable(ms)) != 0)
293 return rv;
294
295 return uhidev_open(&sc->sc_hdev);
296 }
297
298 void
uwacom_disable(void * v)299 uwacom_disable(void *v)
300 {
301 struct uwacom_softc *sc = v;
302 struct hidms *ms = &sc->sc_ms;
303
304 hidms_disable(ms);
305 uhidev_close(&sc->sc_hdev);
306 }
307
308 int
uwacom_ioctl(void * v,u_long cmd,caddr_t data,int flag,struct proc * p)309 uwacom_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
310 {
311 struct uwacom_softc *sc = v;
312 struct hidms *ms = &sc->sc_ms;
313 int rc;
314
315 switch (cmd) {
316 case WSMOUSEIO_GTYPE:
317 *(u_int *)data = WSMOUSE_TYPE_TPANEL;
318 return 0;
319 }
320
321 rc = uhidev_ioctl(&sc->sc_hdev, cmd, data, flag, p);
322 if (rc != -1)
323 return rc;
324
325 return hidms_ioctl(ms, cmd, data, flag, p);
326 }
327
328