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