1208554Sglebius/*- 2208554Sglebius * Copyright 2010, Gleb Smirnoff <glebius@FreeBSD.org> 3208554Sglebius * All rights reserved. 4208554Sglebius * 5208554Sglebius * Redistribution and use in source and binary forms, with or without 6208554Sglebius * modification, are permitted provided that the following conditions 7208554Sglebius * are met: 8208554Sglebius * 1. Redistributions of source code must retain the above copyright 9208554Sglebius * notice, this list of conditions and the following disclaimer. 10208554Sglebius * 2. Redistributions in binary form must reproduce the above copyright 11208554Sglebius * notice, this list of conditions and the following disclaimer in the 12208554Sglebius * documentation and/or other materials provided with the distribution. 13208554Sglebius * 14208554Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15208554Sglebius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16208554Sglebius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17208554Sglebius * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18208554Sglebius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19208554Sglebius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20208554Sglebius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21208554Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22208554Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23208554Sglebius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24208554Sglebius * SUCH DAMAGE. 25208554Sglebius * 26208554Sglebius * $FreeBSD$ 27208554Sglebius */ 28208554Sglebius 29208554Sglebius/* 30208554Sglebius * http://home.eeti.com.tw/web20/drivers/Software%20Programming%20Guide_v2.0.pdf 31208554Sglebius */ 32208554Sglebius 33208554Sglebius#include <sys/param.h> 34208554Sglebius#include <sys/bus.h> 35208554Sglebius#include <sys/callout.h> 36208554Sglebius#include <sys/conf.h> 37208554Sglebius#include <sys/kernel.h> 38208554Sglebius#include <sys/lock.h> 39208554Sglebius#include <sys/module.h> 40208554Sglebius#include <sys/mutex.h> 41208554Sglebius#include <sys/sysctl.h> 42208554Sglebius#include <sys/systm.h> 43208554Sglebius 44208554Sglebius#include <dev/usb/usb.h> 45208554Sglebius#include <dev/usb/usbdi.h> 46208554Sglebius#include <dev/usb/usbdi_util.h> 47208554Sglebius#include <dev/usb/usbhid.h> 48208554Sglebius#include "usbdevs.h" 49208554Sglebius 50208554Sglebius#include <sys/ioccom.h> 51208554Sglebius#include <sys/fcntl.h> 52208554Sglebius#include <sys/tty.h> 53208554Sglebius 54208554Sglebius#define USB_DEBUG_VAR uep_debug 55208554Sglebius#include <dev/usb/usb_debug.h> 56208554Sglebius 57208554Sglebius#ifdef USB_DEBUG 58208554Sglebiusstatic int uep_debug = 0; 59208554Sglebius 60248085Smariusstatic SYSCTL_NODE(_hw_usb, OID_AUTO, uep, CTLFLAG_RW, 0, "USB uep"); 61208554SglebiusSYSCTL_INT(_hw_usb_uep, OID_AUTO, debug, CTLFLAG_RW, 62208554Sglebius &uep_debug, 0, "Debug level"); 63208554Sglebius#endif 64208554Sglebius 65208554Sglebius#define UEP_MAX_X 2047 66208554Sglebius#define UEP_MAX_Y 2047 67208554Sglebius 68208554Sglebius#define UEP_DOWN 0x01 69208554Sglebius#define UEP_PACKET_LEN_MAX 16 70208554Sglebius#define UEP_PACKET_LEN_REPORT 5 71208554Sglebius#define UEP_PACKET_LEN_REPORT2 6 72208554Sglebius#define UEP_PACKET_DIAG 0x0a 73208554Sglebius#define UEP_PACKET_REPORT_MASK 0xe0 74208554Sglebius#define UEP_PACKET_REPORT 0x80 75208554Sglebius#define UEP_PACKET_REPORT_PRESSURE 0xc0 76208554Sglebius#define UEP_PACKET_REPORT_PLAYER 0xa0 77208554Sglebius#define UEP_PACKET_LEN_MASK 78208554Sglebius 79208554Sglebius#define UEP_FIFO_BUF_SIZE 8 /* bytes */ 80208554Sglebius#define UEP_FIFO_QUEUE_MAXLEN 50 /* units */ 81208554Sglebius 82208554Sglebiusenum { 83208554Sglebius UEP_INTR_DT, 84208554Sglebius UEP_N_TRANSFER, 85208554Sglebius}; 86208554Sglebius 87208554Sglebiusstruct uep_softc { 88208554Sglebius struct mtx mtx; 89208554Sglebius 90208554Sglebius struct usb_xfer *xfer[UEP_N_TRANSFER]; 91208554Sglebius struct usb_fifo_sc fifo; 92208554Sglebius 93208554Sglebius u_int pollrate; 94208554Sglebius u_int state; 95208554Sglebius#define UEP_ENABLED 0x01 96208554Sglebius 97208554Sglebius /* Reassembling buffer. */ 98208554Sglebius u_char buf[UEP_PACKET_LEN_MAX]; 99208554Sglebius uint8_t buf_len; 100208554Sglebius}; 101208554Sglebius 102208554Sglebiusstatic usb_callback_t uep_intr_callback; 103208554Sglebius 104208554Sglebiusstatic device_probe_t uep_probe; 105208554Sglebiusstatic device_attach_t uep_attach; 106208554Sglebiusstatic device_detach_t uep_detach; 107208554Sglebius 108208554Sglebiusstatic usb_fifo_cmd_t uep_start_read; 109208554Sglebiusstatic usb_fifo_cmd_t uep_stop_read; 110208554Sglebiusstatic usb_fifo_open_t uep_open; 111208554Sglebiusstatic usb_fifo_close_t uep_close; 112208554Sglebius 113208554Sglebiusstatic void uep_put_queue(struct uep_softc *, u_char *); 114208554Sglebius 115208554Sglebiusstatic struct usb_fifo_methods uep_fifo_methods = { 116208554Sglebius .f_open = &uep_open, 117208554Sglebius .f_close = &uep_close, 118208554Sglebius .f_start_read = &uep_start_read, 119208554Sglebius .f_stop_read = &uep_stop_read, 120208554Sglebius .basename[0] = "uep", 121208554Sglebius}; 122208554Sglebius 123208554Sglebiusstatic int 124208554Sglebiusget_pkt_len(u_char *buf) 125208554Sglebius{ 126208554Sglebius if (buf[0] == UEP_PACKET_DIAG) { 127208554Sglebius int len; 128208554Sglebius 129208554Sglebius len = buf[1] + 2; 130208554Sglebius if (len > UEP_PACKET_LEN_MAX) { 131208554Sglebius DPRINTF("bad packet len %u\n", len); 132208554Sglebius return (UEP_PACKET_LEN_MAX); 133208554Sglebius } 134208554Sglebius 135208554Sglebius return (len); 136208554Sglebius } 137208554Sglebius 138208554Sglebius switch (buf[0] & UEP_PACKET_REPORT_MASK) { 139208554Sglebius case UEP_PACKET_REPORT: 140208554Sglebius return (UEP_PACKET_LEN_REPORT); 141208554Sglebius case UEP_PACKET_REPORT_PRESSURE: 142208554Sglebius case UEP_PACKET_REPORT_PLAYER: 143208554Sglebius case UEP_PACKET_REPORT_PRESSURE | UEP_PACKET_REPORT_PLAYER: 144208554Sglebius return (UEP_PACKET_LEN_REPORT2); 145208554Sglebius default: 146208554Sglebius DPRINTF("bad packet len 0\n"); 147208554Sglebius return (0); 148208554Sglebius } 149208554Sglebius} 150208554Sglebius 151208554Sglebiusstatic void 152208554Sglebiusuep_process_pkt(struct uep_softc *sc, u_char *buf) 153208554Sglebius{ 154208554Sglebius int32_t x, y; 155208554Sglebius 156208554Sglebius if ((buf[0] & 0xFE) != 0x80) { 157208554Sglebius DPRINTF("bad input packet format 0x%.2x\n", buf[0]); 158208554Sglebius return; 159208554Sglebius } 160208554Sglebius 161208554Sglebius /* 162208554Sglebius * Packet format is 5 bytes: 163208554Sglebius * 164208554Sglebius * 1000000T 165208554Sglebius * 0000AAAA 166208554Sglebius * 0AAAAAAA 167208554Sglebius * 0000BBBB 168208554Sglebius * 0BBBBBBB 169208554Sglebius * 170208554Sglebius * T: 1=touched 0=not touched 171208554Sglebius * A: bits of axis A position, MSB to LSB 172208554Sglebius * B: bits of axis B position, MSB to LSB 173208554Sglebius * 174208554Sglebius * For the unit I have, which is CTF1020-S from CarTFT.com, 175208554Sglebius * A = X and B = Y. But in NetBSD uep(4) it is other way round :) 176208554Sglebius * 177208554Sglebius * The controller sends a stream of T=1 events while the 178208554Sglebius * panel is touched, followed by a single T=0 event. 179208554Sglebius * 180208554Sglebius */ 181208554Sglebius 182208554Sglebius x = (buf[1] << 7) | buf[2]; 183208554Sglebius y = (buf[3] << 7) | buf[4]; 184208554Sglebius 185208554Sglebius DPRINTFN(2, "x %u y %u\n", x, y); 186208554Sglebius 187208554Sglebius uep_put_queue(sc, buf); 188208554Sglebius} 189208554Sglebius 190208554Sglebiusstatic void 191208554Sglebiusuep_intr_callback(struct usb_xfer *xfer, usb_error_t error) 192208554Sglebius{ 193208554Sglebius struct uep_softc *sc = usbd_xfer_softc(xfer); 194208554Sglebius int len; 195208554Sglebius 196208554Sglebius usbd_xfer_status(xfer, &len, NULL, NULL, NULL); 197208554Sglebius 198208554Sglebius switch (USB_GET_STATE(xfer)) { 199208554Sglebius case USB_ST_TRANSFERRED: 200208554Sglebius { 201208554Sglebius struct usb_page_cache *pc; 202208554Sglebius u_char buf[17], *p; 203208554Sglebius int pkt_len; 204208554Sglebius 205235000Shselasky if (len > (int)sizeof(buf)) { 206208554Sglebius DPRINTF("bad input length %d\n", len); 207208554Sglebius goto tr_setup; 208208554Sglebius } 209208554Sglebius 210208554Sglebius pc = usbd_xfer_get_frame(xfer, 0); 211208554Sglebius usbd_copy_out(pc, 0, buf, len); 212208554Sglebius 213208554Sglebius /* 214208554Sglebius * The below code mimics Linux a lot. I don't know 215208554Sglebius * why NetBSD reads complete packets, but we need 216208554Sglebius * to reassamble 'em like Linux does (tries?). 217208554Sglebius */ 218208554Sglebius if (sc->buf_len > 0) { 219208554Sglebius int res; 220208554Sglebius 221208554Sglebius if (sc->buf_len == 1) 222208554Sglebius sc->buf[1] = buf[0]; 223208554Sglebius 224208554Sglebius if ((pkt_len = get_pkt_len(sc->buf)) == 0) 225208554Sglebius goto tr_setup; 226208554Sglebius 227208554Sglebius res = pkt_len - sc->buf_len; 228208554Sglebius memcpy(sc->buf + sc->buf_len, buf, res); 229208554Sglebius uep_process_pkt(sc, sc->buf); 230208554Sglebius sc->buf_len = 0; 231208554Sglebius 232208554Sglebius p = buf + res; 233208554Sglebius len -= res; 234208554Sglebius } else 235208554Sglebius p = buf; 236208554Sglebius 237208554Sglebius if (len == 1) { 238208554Sglebius sc->buf[0] = buf[0]; 239208554Sglebius sc->buf_len = 1; 240208554Sglebius 241208554Sglebius goto tr_setup; 242208554Sglebius } 243208554Sglebius 244208554Sglebius while (len > 0) { 245208554Sglebius if ((pkt_len = get_pkt_len(p)) == 0) 246208554Sglebius goto tr_setup; 247208554Sglebius 248208554Sglebius /* full packet: process */ 249208554Sglebius if (pkt_len <= len) { 250208554Sglebius uep_process_pkt(sc, p); 251208554Sglebius } else { 252208554Sglebius /* incomplete packet: save in buffer */ 253208554Sglebius memcpy(sc->buf, p, len); 254208554Sglebius sc->buf_len = len; 255208554Sglebius } 256208554Sglebius p += pkt_len; 257208554Sglebius len -= pkt_len; 258208554Sglebius } 259208554Sglebius } 260208554Sglebius case USB_ST_SETUP: 261208554Sglebius tr_setup: 262208554Sglebius /* check if we can put more data into the FIFO */ 263208554Sglebius if (usb_fifo_put_bytes_max(sc->fifo.fp[USB_FIFO_RX]) != 0) { 264208554Sglebius usbd_xfer_set_frame_len(xfer, 0, 265208554Sglebius usbd_xfer_max_len(xfer)); 266208554Sglebius usbd_transfer_submit(xfer); 267208554Sglebius } 268208554Sglebius break; 269208554Sglebius 270208554Sglebius default: 271208554Sglebius if (error != USB_ERR_CANCELLED) { 272208554Sglebius /* try clear stall first */ 273208554Sglebius usbd_xfer_set_stall(xfer); 274208554Sglebius goto tr_setup; 275208554Sglebius } 276208554Sglebius break; 277208554Sglebius } 278208554Sglebius} 279208554Sglebius 280208554Sglebiusstatic const struct usb_config uep_config[UEP_N_TRANSFER] = { 281208554Sglebius [UEP_INTR_DT] = { 282208554Sglebius .type = UE_INTERRUPT, 283208554Sglebius .endpoint = UE_ADDR_ANY, 284208554Sglebius .direction = UE_DIR_IN, 285208554Sglebius .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 286208554Sglebius .bufsize = 0, /* use wMaxPacketSize */ 287208554Sglebius .callback = &uep_intr_callback, 288208554Sglebius }, 289208554Sglebius}; 290208554Sglebius 291223521Shselaskystatic const STRUCT_USB_HOST_ID uep_devs[] = { 292223521Shselasky {USB_VPI(USB_VENDOR_EGALAX, USB_PRODUCT_EGALAX_TPANEL, 0)}, 293223521Shselasky {USB_VPI(USB_VENDOR_EGALAX, USB_PRODUCT_EGALAX_TPANEL2, 0)}, 294223521Shselasky {USB_VPI(USB_VENDOR_EGALAX2, USB_PRODUCT_EGALAX2_TPANEL, 0)}, 295223521Shselasky}; 296223521Shselasky 297208554Sglebiusstatic int 298208554Sglebiusuep_probe(device_t dev) 299208554Sglebius{ 300208554Sglebius struct usb_attach_arg *uaa = device_get_ivars(dev); 301208554Sglebius 302208554Sglebius if (uaa->usb_mode != USB_MODE_HOST) 303208554Sglebius return (ENXIO); 304223521Shselasky if (uaa->info.bConfigIndex != 0) 305223521Shselasky return (ENXIO); 306223521Shselasky if (uaa->info.bIfaceIndex != 0) 307223521Shselasky return (ENXIO); 308208554Sglebius 309223521Shselasky return (usbd_lookup_id_by_uaa(uep_devs, sizeof(uep_devs), uaa)); 310208554Sglebius} 311208554Sglebius 312208554Sglebiusstatic int 313208554Sglebiusuep_attach(device_t dev) 314208554Sglebius{ 315208554Sglebius struct usb_attach_arg *uaa = device_get_ivars(dev); 316208554Sglebius struct uep_softc *sc = device_get_softc(dev); 317208554Sglebius int error; 318208554Sglebius 319208554Sglebius device_set_usb_desc(dev); 320208554Sglebius 321208554Sglebius mtx_init(&sc->mtx, "uep lock", NULL, MTX_DEF); 322208554Sglebius 323208554Sglebius error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, 324208554Sglebius sc->xfer, uep_config, UEP_N_TRANSFER, sc, &sc->mtx); 325208554Sglebius 326208554Sglebius if (error) { 327208554Sglebius DPRINTF("usbd_transfer_setup error=%s\n", usbd_errstr(error)); 328208554Sglebius goto detach; 329208554Sglebius } 330208554Sglebius 331208554Sglebius error = usb_fifo_attach(uaa->device, sc, &sc->mtx, &uep_fifo_methods, 332235000Shselasky &sc->fifo, device_get_unit(dev), -1, uaa->info.bIfaceIndex, 333208554Sglebius UID_ROOT, GID_OPERATOR, 0644); 334208554Sglebius 335208554Sglebius if (error) { 336208554Sglebius DPRINTF("usb_fifo_attach error=%s\n", usbd_errstr(error)); 337208554Sglebius goto detach; 338208554Sglebius } 339208554Sglebius 340208554Sglebius sc->buf_len = 0; 341208554Sglebius 342208554Sglebius return (0); 343208554Sglebius 344208554Sglebiusdetach: 345208554Sglebius uep_detach(dev); 346208554Sglebius 347208554Sglebius return (ENOMEM); /* XXX */ 348208554Sglebius} 349208554Sglebius 350208554Sglebiusstatic int 351208554Sglebiusuep_detach(device_t dev) 352208554Sglebius{ 353208554Sglebius struct uep_softc *sc = device_get_softc(dev); 354208554Sglebius 355208554Sglebius usb_fifo_detach(&sc->fifo); 356208554Sglebius 357208554Sglebius usbd_transfer_unsetup(sc->xfer, UEP_N_TRANSFER); 358208554Sglebius 359208554Sglebius mtx_destroy(&sc->mtx); 360208554Sglebius 361208554Sglebius return (0); 362208554Sglebius} 363208554Sglebius 364208554Sglebiusstatic void 365208554Sglebiusuep_start_read(struct usb_fifo *fifo) 366208554Sglebius{ 367208554Sglebius struct uep_softc *sc = usb_fifo_softc(fifo); 368208554Sglebius u_int rate; 369208554Sglebius 370208554Sglebius if ((rate = sc->pollrate) > 1000) 371208554Sglebius rate = 1000; 372208554Sglebius 373208554Sglebius if (rate > 0 && sc->xfer[UEP_INTR_DT] != NULL) { 374208554Sglebius usbd_transfer_stop(sc->xfer[UEP_INTR_DT]); 375208554Sglebius usbd_xfer_set_interval(sc->xfer[UEP_INTR_DT], 1000 / rate); 376208554Sglebius sc->pollrate = 0; 377208554Sglebius } 378208554Sglebius 379208554Sglebius usbd_transfer_start(sc->xfer[UEP_INTR_DT]); 380208554Sglebius} 381208554Sglebius 382208554Sglebiusstatic void 383208554Sglebiusuep_stop_read(struct usb_fifo *fifo) 384208554Sglebius{ 385208554Sglebius struct uep_softc *sc = usb_fifo_softc(fifo); 386208554Sglebius 387208554Sglebius usbd_transfer_stop(sc->xfer[UEP_INTR_DT]); 388208554Sglebius} 389208554Sglebius 390208554Sglebiusstatic void 391208554Sglebiusuep_put_queue(struct uep_softc *sc, u_char *buf) 392208554Sglebius{ 393208554Sglebius usb_fifo_put_data_linear(sc->fifo.fp[USB_FIFO_RX], buf, 394208554Sglebius UEP_PACKET_LEN_REPORT, 1); 395208554Sglebius} 396208554Sglebius 397208554Sglebiusstatic int 398208554Sglebiusuep_open(struct usb_fifo *fifo, int fflags) 399208554Sglebius{ 400208554Sglebius if (fflags & FREAD) { 401208554Sglebius struct uep_softc *sc = usb_fifo_softc(fifo); 402208554Sglebius 403208554Sglebius if (sc->state & UEP_ENABLED) 404208554Sglebius return (EBUSY); 405208554Sglebius if (usb_fifo_alloc_buffer(fifo, UEP_FIFO_BUF_SIZE, 406208554Sglebius UEP_FIFO_QUEUE_MAXLEN)) 407208554Sglebius return (ENOMEM); 408208554Sglebius 409208554Sglebius sc->state |= UEP_ENABLED; 410208554Sglebius } 411208554Sglebius 412208554Sglebius return (0); 413208554Sglebius} 414208554Sglebius 415208554Sglebiusstatic void 416208554Sglebiusuep_close(struct usb_fifo *fifo, int fflags) 417208554Sglebius{ 418208554Sglebius if (fflags & FREAD) { 419208554Sglebius struct uep_softc *sc = usb_fifo_softc(fifo); 420208554Sglebius 421208554Sglebius sc->state &= ~(UEP_ENABLED); 422208554Sglebius usb_fifo_free_buffer(fifo); 423208554Sglebius } 424208554Sglebius} 425208554Sglebius 426208554Sglebiusstatic devclass_t uep_devclass; 427208554Sglebius 428208554Sglebiusstatic device_method_t uep_methods[] = { 429208554Sglebius DEVMETHOD(device_probe, uep_probe), 430208554Sglebius DEVMETHOD(device_attach, uep_attach), 431208554Sglebius DEVMETHOD(device_detach, uep_detach), 432208554Sglebius { 0, 0 }, 433208554Sglebius}; 434208554Sglebius 435208554Sglebiusstatic driver_t uep_driver = { 436208554Sglebius .name = "uep", 437208554Sglebius .methods = uep_methods, 438208554Sglebius .size = sizeof(struct uep_softc), 439208554Sglebius}; 440208554Sglebius 441208554SglebiusDRIVER_MODULE(uep, uhub, uep_driver, uep_devclass, NULL, NULL); 442208554SglebiusMODULE_DEPEND(uep, usb, 1, 1, 1); 443212122SthompsaMODULE_VERSION(uep, 1); 444