Module Name: src Committed By: macallan Date: Thu Jun 30 06:30:44 UTC 2022
Modified Files: src/sys/dev/usb: files.usb Added Files: src/sys/dev/usb: uintuos.c Log Message: a driver for Wacom Intuos drawing tablets, from Yorick Hardy this has been sitting in my tree long enough and works fine with the hardware I have access to To generate a diff of this commit: cvs rdiff -u -r1.177 -r1.178 src/sys/dev/usb/files.usb cvs rdiff -u -r0 -r1.1 src/sys/dev/usb/uintuos.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/usb/files.usb diff -u src/sys/dev/usb/files.usb:1.177 src/sys/dev/usb/files.usb:1.178 --- src/sys/dev/usb/files.usb:1.177 Tue Jun 29 10:22:37 2021 +++ src/sys/dev/usb/files.usb Thu Jun 30 06:30:44 2022 @@ -1,4 +1,4 @@ -# $NetBSD: files.usb,v 1.177 2021/06/29 10:22:37 nia Exp $ +# $NetBSD: files.usb,v 1.178 2022/06/30 06:30:44 macallan Exp $ # # Config file and device description for machine-independent USB code. # Included by ports that need it. Ports that use it must provide @@ -182,6 +182,11 @@ device uep: wsmousedev, tpcalib attach uep at usbdevif file dev/usb/uep.c uep needs-flag +# Wacom Intuos PTS Pen +device uintuos: wsmousedev, tpcalib +attach uintuos at uhidbus +file dev/usb/uintuos.c uintuos + # Cypress microcontroller based serial adapters device ucycom: hid attach ucycom at uhidbus Added files: Index: src/sys/dev/usb/uintuos.c diff -u /dev/null src/sys/dev/usb/uintuos.c:1.1 --- /dev/null Thu Jun 30 06:30:44 2022 +++ src/sys/dev/usb/uintuos.c Thu Jun 30 06:30:44 2022 @@ -0,0 +1,397 @@ +/* $NetBSD: uintuos.c,v 1.1 2022/06/30 06:30:44 macallan Exp $ */ + +/* + * Copyright (c) 2019 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Yorick Hardy. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Wacom Intuos Pen driver. + * (partially based on uep.c and ums.c) + */ +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: uintuos.c,v 1.1 2022/06/30 06:30:44 macallan Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/device.h> +#include <sys/ioctl.h> +#include <sys/vnode.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbhid.h> + +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdivar.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usbdevs.h> +#include <dev/usb/uhidev.h> + +#include <dev/wscons/wsconsio.h> +#include <dev/wscons/wsmousevar.h> +#include <dev/wscons/tpcalibvar.h> + +struct uintuos_softc { + struct uhidev sc_hdev; + + device_t sc_wsmousedev; /* wsmouse device */ + struct tpcalib_softc sc_tpcalib; /* calibration */ + + u_char sc_enabled; + u_char sc_dying; +}; + +Static void uintuos_cth490_intr(struct uhidev *, void *, u_int); +Static void uintuos_ctl6100_intr(struct uhidev *, void *, u_int); + +Static int uintuos_enable(void *); +Static void uintuos_disable(void *); +Static int uintuos_ioctl(void *, u_long, void *, int, struct lwp *); + +const struct wsmouse_accessops uintuos_accessops = { + uintuos_enable, + uintuos_ioctl, + uintuos_disable, +}; + +int uintuos_match(device_t, cfdata_t, void *); +void uintuos_attach(device_t, device_t, void *); +void uintuos_childdet(device_t, device_t); +int uintuos_detach(device_t, int); +int uintuos_activate(device_t, enum devact); + +CFATTACH_DECL2_NEW(uintuos, sizeof(struct uintuos_softc), uintuos_match, uintuos_attach, + uintuos_detach, uintuos_activate, NULL, uintuos_childdet); + +int +uintuos_match(device_t parent, cfdata_t match, void *aux) +{ + struct uhidev_attach_arg *uha = aux; + + if ((uha->uiaa->uiaa_vendor == USB_VENDOR_WACOM) && + (uha->uiaa->uiaa_product == USB_PRODUCT_WACOM_CTH490K0) && + (uha->reportid == 16)) + return UMATCH_VENDOR_PRODUCT; + + if ((uha->uiaa->uiaa_vendor == USB_VENDOR_WACOM) && + (uha->uiaa->uiaa_product == USB_PRODUCT_WACOM_CTL6100WL) && + (uha->reportid == 16)) + return UMATCH_VENDOR_PRODUCT; + + return UMATCH_NONE; +} + +void +uintuos_attach(device_t parent, device_t self, void *aux) +{ + struct uintuos_softc *sc = device_private(self); + struct uhidev_attach_arg *uha = aux; + struct wsmousedev_attach_args a; + struct wsmouse_calibcoords default_calib; + + aprint_normal("\n"); + aprint_naive("\n"); + + sc->sc_hdev.sc_dev = self; + sc->sc_hdev.sc_parent = uha->parent; + sc->sc_hdev.sc_report_id = uha->reportid; + + switch (uha->uiaa->uiaa_product) { + case USB_PRODUCT_WACOM_CTH490K0: + default_calib.minx = 0, + default_calib.miny = 0, + default_calib.maxx = 7600, + default_calib.maxy = 4750, + sc->sc_hdev.sc_intr = uintuos_cth490_intr; + break; + case USB_PRODUCT_WACOM_CTL6100WL: + default_calib.minx = 0, + default_calib.miny = 0, + default_calib.maxx = 21600, + default_calib.maxy = 13471, + sc->sc_hdev.sc_intr = uintuos_ctl6100_intr; + break; + default: + sc->sc_hdev.sc_intr = uintuos_cth490_intr; + aprint_error_dev(self, "unsupported product\n"); + break; + } + + + if (!pmf_device_register(self, NULL, NULL)) + aprint_error_dev(self, "couldn't establish power handler\n"); + + a.accessops = &uintuos_accessops; + a.accesscookie = sc; + + sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint); + + default_calib.samplelen = WSMOUSE_CALIBCOORDS_RESET, + tpcalib_init(&sc->sc_tpcalib); + tpcalib_ioctl(&sc->sc_tpcalib, WSMOUSEIO_SCALIBCOORDS, + (void *)&default_calib, 0, 0); + + return; +} + +int +uintuos_detach(device_t self, int flags) +{ + struct uintuos_softc *sc = device_private(self); + int rv = 0; + + sc->sc_dying = 1; + + if (sc->sc_wsmousedev != NULL) + rv = config_detach(sc->sc_wsmousedev, flags); + + pmf_device_deregister(self); + + return rv; +} + +void +uintuos_childdet(device_t self, device_t child) +{ + struct uintuos_softc *sc = device_private(self); + + KASSERT(sc->sc_wsmousedev == child); + sc->sc_wsmousedev = NULL; +} + +int +uintuos_activate(device_t self, enum devact act) +{ + struct uintuos_softc *sc = device_private(self); + + switch (act) { + case DVACT_DEACTIVATE: + sc->sc_dying = 1; + return 0; + default: + return EOPNOTSUPP; + } +} + +Static int +uintuos_enable(void *v) +{ + struct uintuos_softc *sc = v; + int error; + + if (sc->sc_dying) + return EIO; + + if (sc->sc_enabled) + return EBUSY; + + sc->sc_enabled = 1; + + error = uhidev_open(&sc->sc_hdev); + if (error) + sc->sc_enabled = 0; + + return error; +} + +Static void +uintuos_disable(void *v) +{ + struct uintuos_softc *sc = v; + + if (!sc->sc_enabled) { + printf("uintuos_disable: not enabled\n"); + return; + } + + sc->sc_enabled = 0; + uhidev_close(&sc->sc_hdev); +} + +Static int +uintuos_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l) +{ + struct uintuos_softc *sc = v; + struct wsmouse_id *id; + + switch (cmd) { + case WSMOUSEIO_GTYPE: + *(u_int *)data = WSMOUSE_TYPE_TPANEL; + return 0; + + case WSMOUSEIO_GETID: + id = (struct wsmouse_id *)data; + if (id->type != WSMOUSE_ID_TYPE_UIDSTR) + return EINVAL; + + snprintf(id->data, WSMOUSE_ID_MAXLEN, "%s %s %s", + sc->sc_hdev.sc_parent->sc_udev->ud_vendor, + sc->sc_hdev.sc_parent->sc_udev->ud_product, + sc->sc_hdev.sc_parent->sc_udev->ud_serial); + id->length = strlen(id->data); + return 0; + + case WSMOUSEIO_SCALIBCOORDS: + case WSMOUSEIO_GCALIBCOORDS: + return tpcalib_ioctl(&sc->sc_tpcalib, cmd, data, flag, l); + } + + return EPASSTHROUGH; +} + +void +uintuos_cth490_intr(struct uhidev *addr, void *ibuf, u_int len) +{ + struct uintuos_softc *sc = (struct uintuos_softc *)addr; + u_char *p = ibuf; + u_int btns = 0; + int x = 0, y = 0, z = 0, s; + + if (len != 9) { + aprint_error_dev(sc->sc_hdev.sc_dev, "wrong report size - ignoring\n"); + return; + } + + /* + * Each report package contains 9 bytes as below (guessed by inspection): + * + * Byte 0 ?VR? ?21T + * Byte 1 X coordinate (high byte) + * Byte 2 X coordinate (low byte) + * Byte 3 Y coordinate (high byte) + * Byte 4 Y coordinate (low byte) + * Byte 5 Pressure (high byte) + * Byte 6 Pressure (low byte) + * Byte 7 zero + * Byte 8 quality + * + * V: 1=valid data, 0=don't use + * R: 1=in range, 2=cannot sense + * 1: barrel button 1, 1=pressed, 0=not pressed + * 2: barrel button 2, 1=pressed, 0=not pressed + * T: 1=touched, 0=not touched (unreliable?) + * quality: 0 - 255, 255 = most reliable? + * + */ + + /* no valid data or not in range */ + if ((p[0] & 0x40) == 0 || (p[0] & 0x20) == 0) + return; + + if (sc->sc_wsmousedev != NULL) { + x = (p[1] << 8) | p[2]; + y = (p[3] << 8) | p[4]; + z = (p[5] << 8) | p[6]; + + /* + * The "T" bit seems to require a *lot* of pressure to remain "1", + * use the pressure value instead (> 255) for button 1 + * + */ + if (p[5] != 0) + btns |= 1; + + /* barrel button 1 => button 2 */ + if (p[0] & 0x02) + btns |= 2; + + /* barrel button 2 => button 3 */ + if (p[0] & 0x04) + btns |= 4; + + tpcalib_trans(&sc->sc_tpcalib, x, y, &x, &y); + + s = spltty(); + wsmouse_input(sc->sc_wsmousedev, btns, x, y, z, 0, + WSMOUSE_INPUT_ABSOLUTE_X | + WSMOUSE_INPUT_ABSOLUTE_Y | + WSMOUSE_INPUT_ABSOLUTE_Z); + splx(s); + } +} + +void +uintuos_ctl6100_intr(struct uhidev *addr, void *ibuf, u_int len) +{ + struct uintuos_softc *sc = (struct uintuos_softc *)addr; + u_char *p = ibuf; + u_int btns = 0; + int x = 0, y = 0, z = 0, s; + + if (len != 26) { + aprint_error_dev(sc->sc_hdev.sc_dev, "wrong report size - ignoring\n"); + return; + } + + /* + * Each report package contains 26 bytes as below (guessed by inspection): + * + * Byte 0 ?VR? ?21T + * Byte 1 X coordinate (low byte) + * Byte 2 X coordinate (high byte) + * Byte 3 zero + * Byte 4 Y coordinate (low byte) + * Byte 5 Y coordinate (high byte) + * Byte 6 zero + * Byte 7 Pressure (low byte) + * Byte 8 Pressure (high byte) + * Byte 9 zero + * Byte 10..14 zero + * Byte 15 quality? + * Byte 16..25 unknown + * + * V: 1=valid data, 0=don't use + * R: 1=in range, 0=cannot sense + * 1: barrel button 1, 1=pressed, 0=not pressed + * 2: barrel button 2, 1=pressed, 0=not pressed + * T: 1=touched, 0=not touched + * quality: 0 - 63, 0 = most reliable? + * + */ + + /* no valid data or not in range */ + if ((p[0] & 0x40) == 0 || (p[0] & 0x20) == 0) + return; + + if (sc->sc_wsmousedev != NULL) { + x = (p[2] << 8) | p[1]; + y = (p[5] << 8) | p[4]; + z = (p[8] << 8) | p[7]; + + btns = p[0] & 0x7; + + tpcalib_trans(&sc->sc_tpcalib, x, y, &x, &y); + + s = spltty(); + wsmouse_input(sc->sc_wsmousedev, btns, x, y, z, 0, + WSMOUSE_INPUT_ABSOLUTE_X | + WSMOUSE_INPUT_ABSOLUTE_Y | + WSMOUSE_INPUT_ABSOLUTE_Z); + splx(s); + } +}