xref: /openbsd/sys/dev/usb/uwacom.c (revision 097a140d)
1 /*	$OpenBSD: uwacom.c,v 1.2 2020/08/23 11:08:02 mglocker 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 struct uwacom_softc {
39 	struct uhidev		sc_hdev;
40 	struct hidms		sc_ms;
41 	struct hid_location	sc_loc_tip_press;
42 };
43 
44 struct cfdriver uwacom_cd = {
45 	NULL, "uwacom", DV_DULL
46 };
47 
48 
49 const struct usb_devno uwacom_devs[] = {
50 	{ USB_VENDOR_WACOM, USB_PRODUCT_WACOM_INTUOS_DRAW }
51 };
52 
53 int	uwacom_match(struct device *, void *, void *);
54 void	uwacom_attach(struct device *, struct device *, void *);
55 int	uwacom_detach(struct device *, int);
56 void	uwacom_intr(struct uhidev *, void *, u_int);
57 int	uwacom_enable(void *);
58 void	uwacom_disable(void *);
59 int	uwacom_ioctl(void *, u_long, caddr_t, int, struct proc *);
60 
61 const struct cfattach uwacom_ca = {
62 	sizeof(struct uwacom_softc), uwacom_match, uwacom_attach, uwacom_detach
63 };
64 
65 const struct wsmouse_accessops uwacom_accessops = {
66 	uwacom_enable,
67 	uwacom_ioctl,
68 	uwacom_disable,
69 };
70 
71 int
72 uwacom_match(struct device *parent, void *match, void *aux)
73 {
74 	struct uhidev_attach_arg *uha = aux;
75 	int size;
76 	void *desc;
77 
78 	if (usb_lookup(uwacom_devs, uha->uaa->vendor,
79 	    uha->uaa->product) == NULL)
80 		return (UMATCH_NONE);
81 
82 	uhidev_get_report_desc(uha->parent, &desc, &size);
83 
84 	if (!hid_locate(desc, size, HID_USAGE2(HUP_WACOM, HUG_POINTER),
85 	    uha->reportid, hid_input, NULL, NULL))
86 		return (UMATCH_NONE);
87 
88 	return (UMATCH_IFACECLASS);
89 }
90 
91 void
92 uwacom_attach(struct device *parent, struct device *self, void *aux)
93 {
94 	struct uwacom_softc *sc = (struct uwacom_softc *)self;
95 	struct hidms *ms = &sc->sc_ms;
96 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
97 	struct usb_attach_arg *uaa = uha->uaa;
98 	int size, repid;
99 	void *desc;
100 
101 	sc->sc_hdev.sc_intr = uwacom_intr;
102 	sc->sc_hdev.sc_parent = uha->parent;
103 	sc->sc_hdev.sc_udev = uaa->device;
104 	sc->sc_hdev.sc_report_id = uha->reportid;
105 
106 	usbd_set_idle(uha->parent->sc_udev, uha->parent->sc_ifaceno, 0, 0);
107 
108 	uhidev_get_report_desc(uha->parent, &desc, &size);
109 	repid = uha->reportid;
110 	sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid);
111 	sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid);
112 	sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid);
113 
114 	ms->sc_device = self;
115 	ms->sc_rawmode = 1;
116 	ms->sc_flags = HIDMS_ABSX | HIDMS_ABSY;
117 	ms->sc_num_buttons = 3;
118 	ms->sc_loc_x.pos = 8;
119 	ms->sc_loc_x.size = 16;
120 	ms->sc_loc_y.pos = 24;
121 	ms->sc_loc_y.size = 16;
122 
123 	ms->sc_tsscale.minx = 0;
124 	ms->sc_tsscale.maxx = 7600;
125 	ms->sc_tsscale.miny = 0;
126 	ms->sc_tsscale.maxy = 4750;
127 
128 	ms->sc_loc_btn[0].pos = 0;
129 	ms->sc_loc_btn[0].size = 1;
130 	ms->sc_loc_btn[1].pos = 1;
131 	ms->sc_loc_btn[1].size = 1;
132 	ms->sc_loc_btn[2].pos = 2;
133 	ms->sc_loc_btn[2].size = 1;
134 
135 	sc->sc_loc_tip_press.pos = 43;
136 	sc->sc_loc_tip_press.size = 8;
137 
138 	hidms_attach(ms, &uwacom_accessops);
139 }
140 
141 int
142 uwacom_detach(struct device *self, int flags)
143 {
144 	struct uwacom_softc *sc = (struct uwacom_softc *)self;
145 	struct hidms *ms = &sc->sc_ms;
146 
147 	return hidms_detach(ms, flags);
148 }
149 
150 void
151 uwacom_intr(struct uhidev *addr, void *buf, u_int len)
152 {
153 	struct uwacom_softc *sc = (struct uwacom_softc *)addr;
154 	struct hidms *ms = &sc->sc_ms;
155 	u_int32_t buttons = 0;
156 	uint8_t *data = (uint8_t *)buf;
157 	int i, x, y, pressure;
158 
159 	if (ms->sc_enabled == 0)
160 		return;
161 
162 	/* ignore proximity, it will cause invalid button 2 events */
163 	if ((data[0] & 0xf0) == 0xc0)
164 		return;
165 
166 	x = be16toh(hid_get_data(data, len, &ms->sc_loc_x));
167 	y = be16toh(hid_get_data(data, len, &ms->sc_loc_y));
168 	pressure = hid_get_data(data, len, &sc->sc_loc_tip_press);
169 
170 	for (i = 0; i < ms->sc_num_buttons; i++)
171 		if (hid_get_data(data, len, &ms->sc_loc_btn[i]))
172 			buttons |= (1 << i);
173 
174 	/* button 0 reporting is flaky, use tip pressure for it */
175 	if (pressure > 10)
176 		buttons |= 1;
177 	else
178 		buttons &= ~1;
179 
180 	if (x != 0 || y != 0 || buttons != ms->sc_buttons) {
181 		wsmouse_position(ms->sc_wsmousedev, x, y);
182 		wsmouse_buttons(ms->sc_wsmousedev, buttons);
183 		wsmouse_input_sync(ms->sc_wsmousedev);
184 	}
185 }
186 
187 int
188 uwacom_enable(void *v)
189 {
190 	struct uwacom_softc *sc = v;
191 	struct hidms *ms = &sc->sc_ms;
192 	int rv;
193 
194 	if (usbd_is_dying(sc->sc_hdev.sc_udev))
195 		return EIO;
196 
197 	if ((rv = hidms_enable(ms)) != 0)
198 		return rv;
199 
200 	return uhidev_open(&sc->sc_hdev);
201 }
202 
203 void
204 uwacom_disable(void *v)
205 {
206 	struct uwacom_softc *sc = v;
207 	struct hidms *ms = &sc->sc_ms;
208 
209 	hidms_disable(ms);
210 	uhidev_close(&sc->sc_hdev);
211 }
212 
213 int
214 uwacom_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
215 {
216 	struct uwacom_softc *sc = v;
217 	struct hidms *ms = &sc->sc_ms;
218 	int rc;
219 
220 	switch (cmd) {
221 	case WSMOUSEIO_GTYPE:
222 		*(u_int *)data = WSMOUSE_TYPE_TPANEL;
223 		return 0;
224 	}
225 
226 	rc = uhidev_ioctl(&sc->sc_hdev, cmd, data, flag, p);
227 	if (rc != -1)
228 		return rc;
229 
230 	return hidms_ioctl(ms, cmd, data, flag, p);
231 }
232 
233