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