1 /* $NetBSD: uep.c,v 1.23 2019/12/01 08:27:54 maxv Exp $ */ 2 3 /* 4 * Copyright (c) 2004 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Tyler C. Sarna (tsarna@netbsd.org). 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 * eGalax USB touchpanel controller driver. 34 */ 35 #include <sys/cdefs.h> 36 __KERNEL_RCSID(0, "$NetBSD: uep.c,v 1.23 2019/12/01 08:27:54 maxv Exp $"); 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/kernel.h> 41 #include <sys/kmem.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/usbdi.h> 48 #include <dev/usb/usbdi_util.h> 49 #include <dev/usb/usbdevs.h> 50 #include <dev/usb/usb_quirks.h> 51 52 #include <dev/wscons/wsconsio.h> 53 #include <dev/wscons/wsmousevar.h> 54 #include <dev/wscons/tpcalibvar.h> 55 56 #define UIDSTR "eGalax USB SN000000" 57 /* calibration - integer values, perhaps sysctls? */ 58 #define X_RATIO 293 59 #define X_OFFSET -28 60 #define Y_RATIO -348 61 #define Y_OFFSET 537 62 /* an X_RATIO of ``312'' means : reduce by a factor 3.12 x axis amplitude */ 63 /* an Y_RATIO of ``-157'' means : reduce by a factor 1.57 y axis amplitude, 64 * and reverse y motion */ 65 66 struct uep_softc { 67 device_t sc_dev; 68 struct usbd_device *sc_udev; /* device */ 69 struct usbd_interface *sc_iface; /* interface */ 70 int sc_iface_number; 71 72 int sc_intr_number; /* interrupt number */ 73 struct usbd_pipe * sc_intr_pipe; /* interrupt pipe */ 74 u_char *sc_ibuf; 75 int sc_isize; 76 77 device_t sc_wsmousedev; /* wsmouse device */ 78 struct tpcalib_softc sc_tpcalib; /* calibration */ 79 80 u_char sc_enabled; 81 u_char sc_dying; 82 }; 83 84 static struct wsmouse_calibcoords default_calib = { 85 .minx = 0, 86 .miny = 0, 87 .maxx = 2047, 88 .maxy = 2047, 89 .samplelen = WSMOUSE_CALIBCOORDS_RESET, 90 }; 91 92 Static void uep_intr(struct usbd_xfer *, void *, usbd_status); 93 94 Static int uep_enable(void *); 95 Static void uep_disable(void *); 96 Static int uep_ioctl(void *, u_long, void *, int, struct lwp *); 97 98 static const struct wsmouse_accessops uep_accessops = { 99 uep_enable, 100 uep_ioctl, 101 uep_disable, 102 }; 103 104 static int uep_match(device_t, cfdata_t, void *); 105 static void uep_attach(device_t, device_t, void *); 106 static void uep_childdet(device_t, device_t); 107 static int uep_detach(device_t, int); 108 static int uep_activate(device_t, enum devact); 109 110 CFATTACH_DECL2_NEW(uep, sizeof(struct uep_softc), uep_match, uep_attach, 111 uep_detach, uep_activate, NULL, uep_childdet); 112 113 static int 114 uep_match(device_t parent, cfdata_t match, void *aux) 115 { 116 struct usb_attach_arg *uaa = aux; 117 118 if ((uaa->uaa_vendor == USB_VENDOR_EGALAX) && ( 119 (uaa->uaa_product == USB_PRODUCT_EGALAX_TPANEL) 120 || (uaa->uaa_product == USB_PRODUCT_EGALAX_TPANEL2))) 121 return UMATCH_VENDOR_PRODUCT; 122 123 if ((uaa->uaa_vendor == USB_VENDOR_EGALAX2) 124 && (uaa->uaa_product == USB_PRODUCT_EGALAX2_TPANEL)) 125 return UMATCH_VENDOR_PRODUCT; 126 127 128 return UMATCH_NONE; 129 } 130 131 static void 132 uep_attach(device_t parent, device_t self, void *aux) 133 { 134 struct uep_softc *sc = device_private(self); 135 struct usb_attach_arg *uaa = aux; 136 struct usbd_device *dev = uaa->uaa_device; 137 usb_config_descriptor_t *cdesc; 138 usb_interface_descriptor_t *id; 139 usb_endpoint_descriptor_t *ed; 140 usb_device_request_t req; 141 uByte act; 142 struct wsmousedev_attach_args a; 143 char *devinfop; 144 usbd_status err; 145 int i; 146 147 sc->sc_dev = self; 148 149 aprint_naive("\n"); 150 aprint_normal("\n"); 151 152 devinfop = usbd_devinfo_alloc(dev, 0); 153 aprint_normal_dev(self, "%s\n", devinfop); 154 usbd_devinfo_free(devinfop); 155 sc->sc_udev = dev; 156 sc->sc_intr_number = -1; 157 sc->sc_intr_pipe = NULL; 158 sc->sc_enabled = sc->sc_isize = 0; 159 160 /* Move the device into the configured state. */ 161 err = usbd_set_config_index(dev, 0, 1); 162 if (err) { 163 aprint_error("\n%s: failed to set configuration, err=%s\n", 164 device_xname(sc->sc_dev), usbd_errstr(err)); 165 sc->sc_dying = 1; 166 return; 167 } 168 169 /* get the config descriptor */ 170 cdesc = usbd_get_config_descriptor(sc->sc_udev); 171 if (cdesc == NULL) { 172 aprint_error_dev(self, 173 "failed to get configuration descriptor\n"); 174 sc->sc_dying = 1; 175 return; 176 } 177 178 /* get the interface */ 179 err = usbd_device2interface_handle(dev, 0, &sc->sc_iface); 180 if (err) { 181 aprint_error("\n%s: failed to get interface, err=%s\n", 182 device_xname(sc->sc_dev), usbd_errstr(err)); 183 sc->sc_dying = 1; 184 return; 185 } 186 187 /* Find the interrupt endpoint */ 188 id = usbd_get_interface_descriptor(sc->sc_iface); 189 sc->sc_iface_number = id->bInterfaceNumber; 190 191 for (i = 0; i < id->bNumEndpoints; i++) { 192 ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); 193 if (ed == NULL) { 194 aprint_error_dev(self, 195 "no endpoint descriptor for %d\n", i); 196 sc->sc_dying = 1; 197 return; 198 } 199 200 if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && 201 UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { 202 sc->sc_intr_number = ed->bEndpointAddress; 203 sc->sc_isize = UGETW(ed->wMaxPacketSize); 204 } 205 } 206 207 if (sc->sc_intr_number== -1) { 208 aprint_error_dev(self, "Could not find interrupt in\n"); 209 sc->sc_dying = 1; 210 return; 211 } 212 213 /* Newer controllers need an activation command */ 214 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 215 req.bRequest = 0x0a; 216 USETW(req.wValue, 'A'); 217 USETW(req.wIndex, 0); 218 USETW(req.wLength, 1); 219 usbd_do_request(dev, &req, &act); 220 221 usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev); 222 223 a.accessops = &uep_accessops; 224 a.accesscookie = sc; 225 226 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint); 227 228 tpcalib_init(&sc->sc_tpcalib); 229 tpcalib_ioctl(&sc->sc_tpcalib, WSMOUSEIO_SCALIBCOORDS, 230 (void *)&default_calib, 0, 0); 231 232 return; 233 } 234 235 static int 236 uep_detach(device_t self, int flags) 237 { 238 struct uep_softc *sc = device_private(self); 239 int rv = 0; 240 241 if (sc->sc_intr_pipe != NULL) { 242 usbd_abort_pipe(sc->sc_intr_pipe); 243 usbd_close_pipe(sc->sc_intr_pipe); 244 sc->sc_intr_pipe = NULL; 245 } 246 sc->sc_dying = 1; 247 248 /* save current calib as defaults */ 249 default_calib = sc->sc_tpcalib.sc_saved; 250 251 if (sc->sc_wsmousedev != NULL) 252 rv = config_detach(sc->sc_wsmousedev, flags); 253 254 usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev); 255 return rv; 256 } 257 258 static void 259 uep_childdet(device_t self, device_t child) 260 { 261 struct uep_softc *sc = device_private(self); 262 263 KASSERT(sc->sc_wsmousedev == child); 264 sc->sc_wsmousedev = NULL; 265 } 266 267 static int 268 uep_activate(device_t self, enum devact act) 269 { 270 struct uep_softc *sc = device_private(self); 271 272 switch (act) { 273 case DVACT_DEACTIVATE: 274 sc->sc_dying = 1; 275 return 0; 276 default: 277 return EOPNOTSUPP; 278 } 279 } 280 281 Static int 282 uep_enable(void *v) 283 { 284 struct uep_softc *sc = v; 285 int err; 286 287 if (sc->sc_dying) 288 return EIO; 289 290 if (sc->sc_enabled) 291 return EBUSY; 292 293 if (sc->sc_isize == 0) 294 return 0; 295 296 sc->sc_ibuf = kmem_alloc(sc->sc_isize, KM_SLEEP); 297 err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_intr_number, 298 USBD_SHORT_XFER_OK, &sc->sc_intr_pipe, sc, sc->sc_ibuf, 299 sc->sc_isize, uep_intr, USBD_DEFAULT_INTERVAL); 300 if (err) { 301 kmem_free(sc->sc_ibuf, sc->sc_isize); 302 sc->sc_intr_pipe = NULL; 303 return EIO; 304 } 305 306 sc->sc_enabled = 1; 307 308 return 0; 309 } 310 311 Static void 312 uep_disable(void *v) 313 { 314 struct uep_softc *sc = v; 315 316 if (!sc->sc_enabled) { 317 printf("uep_disable: already disabled!\n"); 318 return; 319 } 320 321 /* Disable interrupts. */ 322 if (sc->sc_intr_pipe != NULL) { 323 usbd_abort_pipe(sc->sc_intr_pipe); 324 usbd_close_pipe(sc->sc_intr_pipe); 325 sc->sc_intr_pipe = NULL; 326 } 327 328 if (sc->sc_ibuf != NULL) { 329 kmem_free(sc->sc_ibuf, sc->sc_isize); 330 sc->sc_ibuf = NULL; 331 } 332 333 sc->sc_enabled = 0; 334 } 335 336 Static int 337 uep_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l) 338 { 339 struct uep_softc *sc = v; 340 struct wsmouse_id *id; 341 342 switch (cmd) { 343 case WSMOUSEIO_GTYPE: 344 *(u_int *)data = WSMOUSE_TYPE_TPANEL; 345 return 0; 346 347 case WSMOUSEIO_GETID: 348 /* 349 * return unique ID string 350 * "<vendor> <model> <serial number>" 351 * unfortunately we have no serial number... 352 */ 353 id = (struct wsmouse_id *)data; 354 if (id->type != WSMOUSE_ID_TYPE_UIDSTR) 355 return EINVAL; 356 357 strcpy(id->data, UIDSTR); 358 id->length = strlen(UIDSTR); 359 return 0; 360 361 case WSMOUSEIO_SCALIBCOORDS: 362 case WSMOUSEIO_GCALIBCOORDS: 363 return tpcalib_ioctl(&sc->sc_tpcalib, cmd, data, flag, l); 364 } 365 366 return EPASSTHROUGH; 367 } 368 369 static int 370 uep_adjust(int v, int off, int rat) 371 { 372 int num = 100 * v; 373 int quot = num / rat; 374 int rem = num % rat; 375 if (num >= 0 && rem < 0) 376 quot++; 377 return quot + off; 378 } 379 380 void 381 uep_intr(struct usbd_xfer *xfer, void *addr, usbd_status status) 382 { 383 struct uep_softc *sc = addr; 384 u_char *p = sc->sc_ibuf; 385 u_char msk; 386 uint32_t len; 387 int x = 0, y = 0, s; 388 389 usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL); 390 391 if (status == USBD_CANCELLED) 392 return; 393 394 if (status != USBD_NORMAL_COMPLETION) { 395 aprint_error_dev(sc->sc_dev, "status %d\n", status); 396 usbd_clear_endpoint_stall_async(sc->sc_intr_pipe); 397 return; 398 } 399 400 /* First bit is always set to 1 */ 401 if ((p[0] & 0x80) != 0x80) { 402 aprint_error_dev(sc->sc_dev, "bad input packet format\n"); 403 return; 404 } 405 406 if (sc->sc_wsmousedev != NULL) { 407 /* 408 * Each report package may contain 5 or 6 bytes as below: 409 * 410 * Byte 0 1ZM00HLT 411 * Byte 1 0AAAAAAA 412 * Byte 2 0AAAAAAA 413 * Byte 3 0BBBBBBB 414 * Byte 4 0BBBBBBB 415 * Byte 5 0PPPPPPP 416 * 417 * Z: 1=byte 5 is pressure information, 0=no pressure 418 * M: 1=byte 5 is play id, 0=no player id 419 * T: 1=touched, 0=not touched 420 * H,L: Resolution 421 * 0,0: 11 bits 422 * 0,1: 12 bits 423 * 1,0: 13 bits 424 * 1,1: 14 bits 425 * A: bits of axis A position, MSB to LSB 426 * B: bits of axis B position, MSB to LSB 427 * 428 * The packet has six bytes only if Z or M is set. 429 * Byte 5, if sent, is ignored. 430 * 431 * For the unit I have, A = Y and B = X. 432 * I don't know if units exist with A=X and B=Y, 433 * if so we'll cross that bridge when we come to it. 434 * 435 * The controller sends a stream of T=1 events while the 436 * panel is touched, followed by a single T=0 event. 437 */ 438 switch (p[0] & 0x06) { 439 case 0x02: 440 msk = 0x1f; 441 break; 442 case 0x04: 443 msk = 0x3f; 444 break; 445 case 0x06: 446 msk = 0x7f; 447 break; 448 default: 449 msk = 0x0f; /* H=0, L=0 */ 450 } 451 x = uep_adjust(((p[3] & msk) << 7) | p[4], X_OFFSET, X_RATIO); 452 y = uep_adjust(((p[1] & msk) << 7) | p[2], Y_OFFSET, Y_RATIO); 453 454 tpcalib_trans(&sc->sc_tpcalib, x, y, &x, &y); 455 456 s = spltty(); 457 wsmouse_input(sc->sc_wsmousedev, p[0] & 0x01, x, y, 0, 0, 458 WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y); 459 splx(s); 460 } 461 } 462