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