1 /* $NetBSD: uintuos.c,v 1.1 2022/06/30 06:30:44 macallan Exp $ */
2
3 /*
4 * Copyright (c) 2019 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Yorick Hardy.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * Wacom Intuos Pen driver.
34 * (partially based on uep.c and ums.c)
35 */
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: uintuos.c,v 1.1 2022/06/30 06:30:44 macallan Exp $");
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/device.h>
43 #include <sys/ioctl.h>
44 #include <sys/vnode.h>
45
46 #include <dev/usb/usb.h>
47 #include <dev/usb/usbhid.h>
48
49 #include <dev/usb/usbdi.h>
50 #include <dev/usb/usbdivar.h>
51 #include <dev/usb/usbdi_util.h>
52 #include <dev/usb/usbdevs.h>
53 #include <dev/usb/uhidev.h>
54
55 #include <dev/wscons/wsconsio.h>
56 #include <dev/wscons/wsmousevar.h>
57 #include <dev/wscons/tpcalibvar.h>
58
59 struct uintuos_softc {
60 struct uhidev sc_hdev;
61
62 device_t sc_wsmousedev; /* wsmouse device */
63 struct tpcalib_softc sc_tpcalib; /* calibration */
64
65 u_char sc_enabled;
66 u_char sc_dying;
67 };
68
69 Static void uintuos_cth490_intr(struct uhidev *, void *, u_int);
70 Static void uintuos_ctl6100_intr(struct uhidev *, void *, u_int);
71
72 Static int uintuos_enable(void *);
73 Static void uintuos_disable(void *);
74 Static int uintuos_ioctl(void *, u_long, void *, int, struct lwp *);
75
76 const struct wsmouse_accessops uintuos_accessops = {
77 uintuos_enable,
78 uintuos_ioctl,
79 uintuos_disable,
80 };
81
82 int uintuos_match(device_t, cfdata_t, void *);
83 void uintuos_attach(device_t, device_t, void *);
84 void uintuos_childdet(device_t, device_t);
85 int uintuos_detach(device_t, int);
86 int uintuos_activate(device_t, enum devact);
87
88 CFATTACH_DECL2_NEW(uintuos, sizeof(struct uintuos_softc), uintuos_match, uintuos_attach,
89 uintuos_detach, uintuos_activate, NULL, uintuos_childdet);
90
91 int
uintuos_match(device_t parent,cfdata_t match,void * aux)92 uintuos_match(device_t parent, cfdata_t match, void *aux)
93 {
94 struct uhidev_attach_arg *uha = aux;
95
96 if ((uha->uiaa->uiaa_vendor == USB_VENDOR_WACOM) &&
97 (uha->uiaa->uiaa_product == USB_PRODUCT_WACOM_CTH490K0) &&
98 (uha->reportid == 16))
99 return UMATCH_VENDOR_PRODUCT;
100
101 if ((uha->uiaa->uiaa_vendor == USB_VENDOR_WACOM) &&
102 (uha->uiaa->uiaa_product == USB_PRODUCT_WACOM_CTL6100WL) &&
103 (uha->reportid == 16))
104 return UMATCH_VENDOR_PRODUCT;
105
106 return UMATCH_NONE;
107 }
108
109 void
uintuos_attach(device_t parent,device_t self,void * aux)110 uintuos_attach(device_t parent, device_t self, void *aux)
111 {
112 struct uintuos_softc *sc = device_private(self);
113 struct uhidev_attach_arg *uha = aux;
114 struct wsmousedev_attach_args a;
115 struct wsmouse_calibcoords default_calib;
116
117 aprint_normal("\n");
118 aprint_naive("\n");
119
120 sc->sc_hdev.sc_dev = self;
121 sc->sc_hdev.sc_parent = uha->parent;
122 sc->sc_hdev.sc_report_id = uha->reportid;
123
124 switch (uha->uiaa->uiaa_product) {
125 case USB_PRODUCT_WACOM_CTH490K0:
126 default_calib.minx = 0,
127 default_calib.miny = 0,
128 default_calib.maxx = 7600,
129 default_calib.maxy = 4750,
130 sc->sc_hdev.sc_intr = uintuos_cth490_intr;
131 break;
132 case USB_PRODUCT_WACOM_CTL6100WL:
133 default_calib.minx = 0,
134 default_calib.miny = 0,
135 default_calib.maxx = 21600,
136 default_calib.maxy = 13471,
137 sc->sc_hdev.sc_intr = uintuos_ctl6100_intr;
138 break;
139 default:
140 sc->sc_hdev.sc_intr = uintuos_cth490_intr;
141 aprint_error_dev(self, "unsupported product\n");
142 break;
143 }
144
145
146 if (!pmf_device_register(self, NULL, NULL))
147 aprint_error_dev(self, "couldn't establish power handler\n");
148
149 a.accessops = &uintuos_accessops;
150 a.accesscookie = sc;
151
152 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
153
154 default_calib.samplelen = WSMOUSE_CALIBCOORDS_RESET,
155 tpcalib_init(&sc->sc_tpcalib);
156 tpcalib_ioctl(&sc->sc_tpcalib, WSMOUSEIO_SCALIBCOORDS,
157 (void *)&default_calib, 0, 0);
158
159 return;
160 }
161
162 int
uintuos_detach(device_t self,int flags)163 uintuos_detach(device_t self, int flags)
164 {
165 struct uintuos_softc *sc = device_private(self);
166 int rv = 0;
167
168 sc->sc_dying = 1;
169
170 if (sc->sc_wsmousedev != NULL)
171 rv = config_detach(sc->sc_wsmousedev, flags);
172
173 pmf_device_deregister(self);
174
175 return rv;
176 }
177
178 void
uintuos_childdet(device_t self,device_t child)179 uintuos_childdet(device_t self, device_t child)
180 {
181 struct uintuos_softc *sc = device_private(self);
182
183 KASSERT(sc->sc_wsmousedev == child);
184 sc->sc_wsmousedev = NULL;
185 }
186
187 int
uintuos_activate(device_t self,enum devact act)188 uintuos_activate(device_t self, enum devact act)
189 {
190 struct uintuos_softc *sc = device_private(self);
191
192 switch (act) {
193 case DVACT_DEACTIVATE:
194 sc->sc_dying = 1;
195 return 0;
196 default:
197 return EOPNOTSUPP;
198 }
199 }
200
201 Static int
uintuos_enable(void * v)202 uintuos_enable(void *v)
203 {
204 struct uintuos_softc *sc = v;
205 int error;
206
207 if (sc->sc_dying)
208 return EIO;
209
210 if (sc->sc_enabled)
211 return EBUSY;
212
213 sc->sc_enabled = 1;
214
215 error = uhidev_open(&sc->sc_hdev);
216 if (error)
217 sc->sc_enabled = 0;
218
219 return error;
220 }
221
222 Static void
uintuos_disable(void * v)223 uintuos_disable(void *v)
224 {
225 struct uintuos_softc *sc = v;
226
227 if (!sc->sc_enabled) {
228 printf("uintuos_disable: not enabled\n");
229 return;
230 }
231
232 sc->sc_enabled = 0;
233 uhidev_close(&sc->sc_hdev);
234 }
235
236 Static int
uintuos_ioctl(void * v,u_long cmd,void * data,int flag,struct lwp * l)237 uintuos_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
238 {
239 struct uintuos_softc *sc = v;
240 struct wsmouse_id *id;
241
242 switch (cmd) {
243 case WSMOUSEIO_GTYPE:
244 *(u_int *)data = WSMOUSE_TYPE_TPANEL;
245 return 0;
246
247 case WSMOUSEIO_GETID:
248 id = (struct wsmouse_id *)data;
249 if (id->type != WSMOUSE_ID_TYPE_UIDSTR)
250 return EINVAL;
251
252 snprintf(id->data, WSMOUSE_ID_MAXLEN, "%s %s %s",
253 sc->sc_hdev.sc_parent->sc_udev->ud_vendor,
254 sc->sc_hdev.sc_parent->sc_udev->ud_product,
255 sc->sc_hdev.sc_parent->sc_udev->ud_serial);
256 id->length = strlen(id->data);
257 return 0;
258
259 case WSMOUSEIO_SCALIBCOORDS:
260 case WSMOUSEIO_GCALIBCOORDS:
261 return tpcalib_ioctl(&sc->sc_tpcalib, cmd, data, flag, l);
262 }
263
264 return EPASSTHROUGH;
265 }
266
267 void
uintuos_cth490_intr(struct uhidev * addr,void * ibuf,u_int len)268 uintuos_cth490_intr(struct uhidev *addr, void *ibuf, u_int len)
269 {
270 struct uintuos_softc *sc = (struct uintuos_softc *)addr;
271 u_char *p = ibuf;
272 u_int btns = 0;
273 int x = 0, y = 0, z = 0, s;
274
275 if (len != 9) {
276 aprint_error_dev(sc->sc_hdev.sc_dev, "wrong report size - ignoring\n");
277 return;
278 }
279
280 /*
281 * Each report package contains 9 bytes as below (guessed by inspection):
282 *
283 * Byte 0 ?VR? ?21T
284 * Byte 1 X coordinate (high byte)
285 * Byte 2 X coordinate (low byte)
286 * Byte 3 Y coordinate (high byte)
287 * Byte 4 Y coordinate (low byte)
288 * Byte 5 Pressure (high byte)
289 * Byte 6 Pressure (low byte)
290 * Byte 7 zero
291 * Byte 8 quality
292 *
293 * V: 1=valid data, 0=don't use
294 * R: 1=in range, 2=cannot sense
295 * 1: barrel button 1, 1=pressed, 0=not pressed
296 * 2: barrel button 2, 1=pressed, 0=not pressed
297 * T: 1=touched, 0=not touched (unreliable?)
298 * quality: 0 - 255, 255 = most reliable?
299 *
300 */
301
302 /* no valid data or not in range */
303 if ((p[0] & 0x40) == 0 || (p[0] & 0x20) == 0)
304 return;
305
306 if (sc->sc_wsmousedev != NULL) {
307 x = (p[1] << 8) | p[2];
308 y = (p[3] << 8) | p[4];
309 z = (p[5] << 8) | p[6];
310
311 /*
312 * The "T" bit seems to require a *lot* of pressure to remain "1",
313 * use the pressure value instead (> 255) for button 1
314 *
315 */
316 if (p[5] != 0)
317 btns |= 1;
318
319 /* barrel button 1 => button 2 */
320 if (p[0] & 0x02)
321 btns |= 2;
322
323 /* barrel button 2 => button 3 */
324 if (p[0] & 0x04)
325 btns |= 4;
326
327 tpcalib_trans(&sc->sc_tpcalib, x, y, &x, &y);
328
329 s = spltty();
330 wsmouse_input(sc->sc_wsmousedev, btns, x, y, z, 0,
331 WSMOUSE_INPUT_ABSOLUTE_X |
332 WSMOUSE_INPUT_ABSOLUTE_Y |
333 WSMOUSE_INPUT_ABSOLUTE_Z);
334 splx(s);
335 }
336 }
337
338 void
uintuos_ctl6100_intr(struct uhidev * addr,void * ibuf,u_int len)339 uintuos_ctl6100_intr(struct uhidev *addr, void *ibuf, u_int len)
340 {
341 struct uintuos_softc *sc = (struct uintuos_softc *)addr;
342 u_char *p = ibuf;
343 u_int btns = 0;
344 int x = 0, y = 0, z = 0, s;
345
346 if (len != 26) {
347 aprint_error_dev(sc->sc_hdev.sc_dev, "wrong report size - ignoring\n");
348 return;
349 }
350
351 /*
352 * Each report package contains 26 bytes as below (guessed by inspection):
353 *
354 * Byte 0 ?VR? ?21T
355 * Byte 1 X coordinate (low byte)
356 * Byte 2 X coordinate (high byte)
357 * Byte 3 zero
358 * Byte 4 Y coordinate (low byte)
359 * Byte 5 Y coordinate (high byte)
360 * Byte 6 zero
361 * Byte 7 Pressure (low byte)
362 * Byte 8 Pressure (high byte)
363 * Byte 9 zero
364 * Byte 10..14 zero
365 * Byte 15 quality?
366 * Byte 16..25 unknown
367 *
368 * V: 1=valid data, 0=don't use
369 * R: 1=in range, 0=cannot sense
370 * 1: barrel button 1, 1=pressed, 0=not pressed
371 * 2: barrel button 2, 1=pressed, 0=not pressed
372 * T: 1=touched, 0=not touched
373 * quality: 0 - 63, 0 = most reliable?
374 *
375 */
376
377 /* no valid data or not in range */
378 if ((p[0] & 0x40) == 0 || (p[0] & 0x20) == 0)
379 return;
380
381 if (sc->sc_wsmousedev != NULL) {
382 x = (p[2] << 8) | p[1];
383 y = (p[5] << 8) | p[4];
384 z = (p[8] << 8) | p[7];
385
386 btns = p[0] & 0x7;
387
388 tpcalib_trans(&sc->sc_tpcalib, x, y, &x, &y);
389
390 s = spltty();
391 wsmouse_input(sc->sc_wsmousedev, btns, x, y, z, 0,
392 WSMOUSE_INPUT_ABSOLUTE_X |
393 WSMOUSE_INPUT_ABSOLUTE_Y |
394 WSMOUSE_INPUT_ABSOLUTE_Z);
395 splx(s);
396 }
397 }
398