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