1 /* $OpenBSD: uwacom.c,v 1.8 2023/08/12 20:47:06 miod 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 #define UWACOM_USE_PRESSURE 0x0001 /* button 0 is flaky, use tip pressure */ 39 #define UWACOM_BIG_ENDIAN 0x0002 /* XY reporting byte order */ 40 41 struct uwacom_softc { 42 struct uhidev sc_hdev; 43 struct hidms sc_ms; 44 struct hid_location sc_loc_tip_press; 45 int sc_flags; 46 int sc_x, sc_y, sc_z, sc_w; 47 int sc_moved; 48 }; 49 50 struct cfdriver uwacom_cd = { 51 NULL, "uwacom", DV_DULL 52 }; 53 54 const struct usb_devno uwacom_devs[] = { 55 { USB_VENDOR_WACOM, USB_PRODUCT_WACOM_INTUOS_DRAW }, 56 { USB_VENDOR_WACOM, USB_PRODUCT_WACOM_ONE_S }, 57 { USB_VENDOR_WACOM, USB_PRODUCT_WACOM_ONE_M }, 58 { USB_VENDOR_WACOM, USB_PRODUCT_WACOM_INTUOS_S } 59 }; 60 61 int uwacom_match(struct device *, void *, void *); 62 void uwacom_attach(struct device *, struct device *, void *); 63 int uwacom_detach(struct device *, int); 64 void uwacom_intr_legacy(struct uhidev *, void *, u_int); 65 void uwacom_intr(struct uhidev *, void *, u_int); 66 int uwacom_enable(void *); 67 void uwacom_disable(void *); 68 int uwacom_ioctl(void *, u_long, caddr_t, int, struct proc *); 69 70 const struct cfattach uwacom_ca = { 71 sizeof(struct uwacom_softc), uwacom_match, uwacom_attach, uwacom_detach 72 }; 73 74 const struct wsmouse_accessops uwacom_accessops = { 75 uwacom_enable, 76 uwacom_ioctl, 77 uwacom_disable, 78 }; 79 80 int 81 uwacom_match(struct device *parent, void *match, void *aux) 82 { 83 struct uhidev_attach_arg *uha = aux; 84 int size; 85 void *desc; 86 87 if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha)) 88 return (UMATCH_NONE); 89 90 if (usb_lookup(uwacom_devs, uha->uaa->vendor, 91 uha->uaa->product) == NULL) 92 return (UMATCH_NONE); 93 94 uhidev_get_report_desc(uha->parent, &desc, &size); 95 96 if (hid_is_collection(desc, size, uha->reportid, 97 HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_DIGITIZER))) 98 return (UMATCH_IFACECLASS); 99 if (!hid_locate(desc, size, HID_USAGE2(HUP_WACOM, HUG_POINTER), 100 uha->reportid, hid_input, NULL, NULL)) 101 return (UMATCH_NONE); 102 103 return (UMATCH_IFACECLASS); 104 } 105 106 void 107 uwacom_attach(struct device *parent, struct device *self, void *aux) 108 { 109 struct uwacom_softc *sc = (struct uwacom_softc *)self; 110 struct hidms *ms = &sc->sc_ms; 111 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 112 struct usb_attach_arg *uaa = uha->uaa; 113 static uByte wacom_report_buf[2] = { 0x02, 0x02 }; 114 int size, repid; 115 void *desc; 116 117 sc->sc_hdev.sc_intr = uwacom_intr_legacy; 118 sc->sc_hdev.sc_parent = uha->parent; 119 sc->sc_hdev.sc_udev = uaa->device; 120 sc->sc_hdev.sc_report_id = uha->reportid; 121 122 usbd_set_idle(uha->parent->sc_udev, uha->parent->sc_ifaceno, 0, 0); 123 124 uhidev_get_report_desc(uha->parent, &desc, &size); 125 repid = uha->reportid; 126 127 sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid); 128 sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid); 129 sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid); 130 131 ms->sc_device = self; 132 ms->sc_rawmode = 1; 133 ms->sc_flags = HIDMS_ABSX | HIDMS_ABSY; 134 ms->sc_num_buttons = 3; 135 136 ms->sc_loc_x.pos = 8; 137 ms->sc_loc_x.size = 16; 138 ms->sc_loc_y.pos = 24; 139 ms->sc_loc_y.size = 16; 140 141 ms->sc_tsscale.minx = 0; 142 ms->sc_tsscale.miny = 0; 143 144 ms->sc_loc_btn[0].pos = 0; 145 ms->sc_loc_btn[0].size = 1; 146 ms->sc_loc_btn[1].pos = 1; 147 ms->sc_loc_btn[1].size = 1; 148 ms->sc_loc_btn[2].pos = 2; 149 ms->sc_loc_btn[2].size = 1; 150 151 switch (uha->uaa->product) { 152 case USB_PRODUCT_WACOM_ONE_S: 153 case USB_PRODUCT_WACOM_INTUOS_S: 154 uhidev_set_report(uha->parent, UHID_FEATURE_REPORT, 155 sc->sc_hdev.sc_report_id, &wacom_report_buf, 156 sizeof(wacom_report_buf)); 157 sc->sc_hdev.sc_intr = uwacom_intr; 158 hidms_setup((struct device *)sc, ms, HIDMS_WACOM_SETUP, 159 repid, desc, size); 160 break; 161 case USB_PRODUCT_WACOM_INTUOS_DRAW: 162 sc->sc_flags = UWACOM_USE_PRESSURE | UWACOM_BIG_ENDIAN; 163 sc->sc_loc_tip_press.pos = 43; 164 sc->sc_loc_tip_press.size = 8; 165 ms->sc_tsscale.maxx = 7600; 166 ms->sc_tsscale.maxy = 4750; 167 break; 168 } 169 170 hidms_attach(ms, &uwacom_accessops); 171 } 172 173 int 174 uwacom_detach(struct device *self, int flags) 175 { 176 struct uwacom_softc *sc = (struct uwacom_softc *)self; 177 struct hidms *ms = &sc->sc_ms; 178 179 return hidms_detach(ms, flags); 180 } 181 182 void 183 uwacom_intr_legacy(struct uhidev *addr, void *buf, u_int len) 184 { 185 struct uwacom_softc *sc = (struct uwacom_softc *)addr; 186 struct hidms *ms = &sc->sc_ms; 187 u_int32_t buttons = 0; 188 uint8_t *data = (uint8_t *)buf; 189 int i, x, y, pressure; 190 191 if (ms->sc_enabled == 0) 192 return; 193 194 /* ignore proximity, it will cause invalid button 2 events */ 195 if ((data[0] & 0xf0) == 0xc0) 196 return; 197 198 x = hid_get_data(data, len, &ms->sc_loc_x); 199 y = hid_get_data(data, len, &ms->sc_loc_y); 200 201 if (sc->sc_flags & UWACOM_BIG_ENDIAN) { 202 x = be16toh(x); 203 y = be16toh(y); 204 } 205 206 for (i = 0; i < ms->sc_num_buttons; i++) 207 if (hid_get_data(data, len, &ms->sc_loc_btn[i])) 208 buttons |= 1 << i; 209 210 if (sc->sc_flags & UWACOM_USE_PRESSURE) { 211 pressure = hid_get_data(data, len, &sc->sc_loc_tip_press); 212 if (pressure > 10) 213 buttons |= 1; 214 else 215 buttons &= ~1; 216 } 217 218 if (x != 0 || y != 0 || buttons != ms->sc_buttons) { 219 wsmouse_position(ms->sc_wsmousedev, x, y); 220 wsmouse_buttons(ms->sc_wsmousedev, buttons); 221 wsmouse_input_sync(ms->sc_wsmousedev); 222 } 223 } 224 225 void 226 uwacom_intr(struct uhidev *addr, void *buf, u_int len) 227 { 228 struct uwacom_softc *sc = (struct uwacom_softc *)addr; 229 struct hidms *ms = &sc->sc_ms; 230 u_int32_t buttons = 0; 231 uint8_t *data = (uint8_t *)buf; 232 int i, j, x, y, dx, dy, dz, dw, pressure, distance; 233 234 if (ms->sc_enabled == 0) 235 return; 236 237 x = hid_get_data(data, len, &ms->sc_loc_x); 238 y = hid_get_data(data, len, &ms->sc_loc_y); 239 pressure = hid_get_data(data, len, &ms->sc_loc_z); 240 distance = hid_get_data(data, len, &ms->sc_loc_w); 241 242 if (!sc->sc_moved) { 243 sc->sc_x = x; 244 sc->sc_y = y; 245 sc->sc_z = pressure; 246 sc->sc_w = distance; 247 sc->sc_moved = 1; 248 } 249 250 dx = sc->sc_x - x; 251 dy = sc->sc_y - y; 252 /* Clamp sensitivity to +/-127 */ 253 dz = sc->sc_z / 32 - pressure / 32; 254 dw = sc->sc_w - distance; 255 256 sc->sc_x = x; 257 sc->sc_y = y; 258 sc->sc_z = pressure; 259 sc->sc_w = distance; 260 261 if (sc->sc_flags & UWACOM_BIG_ENDIAN) { 262 x = be16toh(x); 263 y = be16toh(y); 264 } 265 266 for (i = 0; i < ms->sc_num_stylus_buttons; i++) 267 if (hid_get_data(data, len, &ms->sc_loc_btn[i])) 268 buttons |= 1 << i; 269 270 for (j = 0; i < ms->sc_num_buttons; i++, j++) 271 if (hid_get_data(data, len, &ms->sc_loc_btn[i])) 272 buttons |= 1 << j; 273 274 if (x != 0 || y != 0 || pressure != 0 || distance != 0 || 275 buttons != ms->sc_buttons) { 276 wsmouse_motion(ms->sc_wsmousedev, -dx, dy, dz, dw); 277 wsmouse_buttons(ms->sc_wsmousedev, buttons); 278 wsmouse_input_sync(ms->sc_wsmousedev); 279 } 280 } 281 282 int 283 uwacom_enable(void *v) 284 { 285 struct uwacom_softc *sc = v; 286 struct hidms *ms = &sc->sc_ms; 287 int rv; 288 289 if (usbd_is_dying(sc->sc_hdev.sc_udev)) 290 return EIO; 291 292 if ((rv = hidms_enable(ms)) != 0) 293 return rv; 294 295 return uhidev_open(&sc->sc_hdev); 296 } 297 298 void 299 uwacom_disable(void *v) 300 { 301 struct uwacom_softc *sc = v; 302 struct hidms *ms = &sc->sc_ms; 303 304 hidms_disable(ms); 305 uhidev_close(&sc->sc_hdev); 306 } 307 308 int 309 uwacom_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 310 { 311 struct uwacom_softc *sc = v; 312 struct hidms *ms = &sc->sc_ms; 313 int rc; 314 315 switch (cmd) { 316 case WSMOUSEIO_GTYPE: 317 *(u_int *)data = WSMOUSE_TYPE_TPANEL; 318 return 0; 319 } 320 321 rc = uhidev_ioctl(&sc->sc_hdev, cmd, data, flag, p); 322 if (rc != -1) 323 return rc; 324 325 return hidms_ioctl(ms, cmd, data, flag, p); 326 } 327 328