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