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