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