1292932Sdim/*- 2292932Sdim * Copyright 2010, Gleb Smirnoff <glebius@FreeBSD.org> 3353358Sdim * All rights reserved. 4353358Sdim * 5353358Sdim * Redistribution and use in source and binary forms, with or without 6292932Sdim * modification, are permitted provided that the following conditions 7292932Sdim * are met: 8292932Sdim * 1. Redistributions of source code must retain the above copyright 9292932Sdim * notice, this list of conditions and the following disclaimer. 10292932Sdim * 2. Redistributions in binary form must reproduce the above copyright 11292932Sdim * notice, this list of conditions and the following disclaimer in the 12292932Sdim * documentation and/or other materials provided with the distribution. 13353358Sdim * 14292932Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15292932Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16360784Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17360784Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18292932Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19292932Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20292932Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21314564Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22314564Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23360784Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24292932Sdim * SUCH DAMAGE. 25314564Sdim * 26314564Sdim * $FreeBSD: stable/11/sys/dev/usb/input/uep.c 356020 2019-12-22 19:06:45Z kevans $ 27292932Sdim */ 28314564Sdim 29314564Sdim/* 30360784Sdim * http://www.eeti.com.tw/pdf/Software%20Programming%20Guide_v2.0.pdf 31360784Sdim */ 32360784Sdim 33360784Sdim#include <sys/param.h> 34314564Sdim#include <sys/bus.h> 35314564Sdim#include <sys/callout.h> 36314564Sdim#include <sys/conf.h> 37314564Sdim#include <sys/kernel.h> 38314564Sdim#include <sys/lock.h> 39314564Sdim#include <sys/module.h> 40314564Sdim#include <sys/mutex.h> 41314564Sdim#include <sys/sysctl.h> 42353358Sdim#include <sys/systm.h> 43314564Sdim 44314564Sdim#include <dev/usb/usb.h> 45314564Sdim#include <dev/usb/usbdi.h> 46353358Sdim#include <dev/usb/usbdi_util.h> 47314564Sdim#include <dev/usb/usbhid.h> 48314564Sdim#include "usbdevs.h" 49314564Sdim 50353358Sdim#include <sys/ioccom.h> 51314564Sdim#include <sys/fcntl.h> 52314564Sdim 53314564Sdim#define USB_DEBUG_VAR uep_debug 54353358Sdim#include <dev/usb/usb_debug.h> 55314564Sdim 56314564Sdim#ifdef USB_DEBUG 57314564Sdimstatic int uep_debug = 0; 58353358Sdim 59353358Sdimstatic SYSCTL_NODE(_hw_usb, OID_AUTO, uep, CTLFLAG_RW, 0, "USB uep"); 60314564SdimSYSCTL_INT(_hw_usb_uep, OID_AUTO, debug, CTLFLAG_RWTUN, 61314564Sdim &uep_debug, 0, "Debug level"); 62314564Sdim#endif 63314564Sdim 64314564Sdim#define UEP_MAX_X 2047 65353358Sdim#define UEP_MAX_Y 2047 66314564Sdim 67314564Sdim#define UEP_DOWN 0x01 68353358Sdim#define UEP_PACKET_LEN_MAX 16 69314564Sdim#define UEP_PACKET_LEN_REPORT 5 70314564Sdim#define UEP_PACKET_LEN_REPORT2 6 71314564Sdim#define UEP_PACKET_DIAG 0x0a 72353358Sdim#define UEP_PACKET_REPORT_MASK 0xe0 73314564Sdim#define UEP_PACKET_REPORT 0x80 74314564Sdim#define UEP_PACKET_REPORT_PRESSURE 0xc0 75314564Sdim#define UEP_PACKET_REPORT_PLAYER 0xa0 76353358Sdim#define UEP_PACKET_LEN_MASK 77314564Sdim 78314564Sdim#define UEP_FIFO_BUF_SIZE 8 /* bytes */ 79314564Sdim#define UEP_FIFO_QUEUE_MAXLEN 50 /* units */ 80314564Sdim 81314564Sdimenum { 82314564Sdim UEP_INTR_DT, 83314564Sdim UEP_N_TRANSFER, 84314564Sdim}; 85314564Sdim 86314564Sdimstruct uep_softc { 87314564Sdim struct mtx mtx; 88353358Sdim 89314564Sdim struct usb_xfer *xfer[UEP_N_TRANSFER]; 90314564Sdim struct usb_fifo_sc fifo; 91314564Sdim 92314564Sdim u_int pollrate; 93353358Sdim u_int state; 94314564Sdim#define UEP_ENABLED 0x01 95314564Sdim 96314564Sdim /* Reassembling buffer. */ 97314564Sdim u_char buf[UEP_PACKET_LEN_MAX]; 98314564Sdim uint8_t buf_len; 99314564Sdim}; 100314564Sdim 101314564Sdimstatic usb_callback_t uep_intr_callback; 102314564Sdim 103314564Sdimstatic device_probe_t uep_probe; 104353358Sdimstatic device_attach_t uep_attach; 105314564Sdimstatic device_detach_t uep_detach; 106353358Sdim 107314564Sdimstatic usb_fifo_cmd_t uep_start_read; 108314564Sdimstatic usb_fifo_cmd_t uep_stop_read; 109353358Sdimstatic usb_fifo_open_t uep_open; 110314564Sdimstatic usb_fifo_close_t uep_close; 111314564Sdim 112292932Sdimstatic void uep_put_queue(struct uep_softc *, u_char *); 113314564Sdim 114292932Sdimstatic struct usb_fifo_methods uep_fifo_methods = { 115292932Sdim .f_open = &uep_open, 116292932Sdim .f_close = &uep_close, 117 .f_start_read = &uep_start_read, 118 .f_stop_read = &uep_stop_read, 119 .basename[0] = "uep", 120}; 121 122static int 123get_pkt_len(u_char *buf) 124{ 125 if (buf[0] == UEP_PACKET_DIAG) { 126 int len; 127 128 len = buf[1] + 2; 129 if (len > UEP_PACKET_LEN_MAX) { 130 DPRINTF("bad packet len %u\n", len); 131 return (UEP_PACKET_LEN_MAX); 132 } 133 134 return (len); 135 } 136 137 switch (buf[0] & UEP_PACKET_REPORT_MASK) { 138 case UEP_PACKET_REPORT: 139 return (UEP_PACKET_LEN_REPORT); 140 case UEP_PACKET_REPORT_PRESSURE: 141 case UEP_PACKET_REPORT_PLAYER: 142 case UEP_PACKET_REPORT_PRESSURE | UEP_PACKET_REPORT_PLAYER: 143 return (UEP_PACKET_LEN_REPORT2); 144 default: 145 DPRINTF("bad packet len 0\n"); 146 return (0); 147 } 148} 149 150static void 151uep_process_pkt(struct uep_softc *sc, u_char *buf) 152{ 153 int32_t x, y; 154 155 if ((buf[0] & 0xFE) != 0x80) { 156 DPRINTF("bad input packet format 0x%.2x\n", buf[0]); 157 return; 158 } 159 160 /* 161 * Packet format is 5 bytes: 162 * 163 * 1000000T 164 * 0000AAAA 165 * 0AAAAAAA 166 * 0000BBBB 167 * 0BBBBBBB 168 * 169 * T: 1=touched 0=not touched 170 * A: bits of axis A position, MSB to LSB 171 * B: bits of axis B position, MSB to LSB 172 * 173 * For the unit I have, which is CTF1020-S from CarTFT.com, 174 * A = X and B = Y. But in NetBSD uep(4) it is other way round :) 175 * 176 * The controller sends a stream of T=1 events while the 177 * panel is touched, followed by a single T=0 event. 178 * 179 */ 180 181 x = (buf[1] << 7) | buf[2]; 182 y = (buf[3] << 7) | buf[4]; 183 184 DPRINTFN(2, "x %u y %u\n", x, y); 185 186 uep_put_queue(sc, buf); 187} 188 189static void 190uep_intr_callback(struct usb_xfer *xfer, usb_error_t error) 191{ 192 struct uep_softc *sc = usbd_xfer_softc(xfer); 193 int len; 194 195 usbd_xfer_status(xfer, &len, NULL, NULL, NULL); 196 197 switch (USB_GET_STATE(xfer)) { 198 case USB_ST_TRANSFERRED: 199 { 200 struct usb_page_cache *pc; 201 u_char buf[17], *p; 202 int pkt_len; 203 204 if (len > (int)sizeof(buf)) { 205 DPRINTF("bad input length %d\n", len); 206 goto tr_setup; 207 } 208 209 pc = usbd_xfer_get_frame(xfer, 0); 210 usbd_copy_out(pc, 0, buf, len); 211 212 /* 213 * The below code mimics Linux a lot. I don't know 214 * why NetBSD reads complete packets, but we need 215 * to reassamble 'em like Linux does (tries?). 216 */ 217 if (sc->buf_len > 0) { 218 int res; 219 220 if (sc->buf_len == 1) 221 sc->buf[1] = buf[0]; 222 223 if ((pkt_len = get_pkt_len(sc->buf)) == 0) 224 goto tr_setup; 225 226 res = pkt_len - sc->buf_len; 227 memcpy(sc->buf + sc->buf_len, buf, res); 228 uep_process_pkt(sc, sc->buf); 229 sc->buf_len = 0; 230 231 p = buf + res; 232 len -= res; 233 } else 234 p = buf; 235 236 if (len == 1) { 237 sc->buf[0] = buf[0]; 238 sc->buf_len = 1; 239 240 goto tr_setup; 241 } 242 243 while (len > 0) { 244 if ((pkt_len = get_pkt_len(p)) == 0) 245 goto tr_setup; 246 247 /* full packet: process */ 248 if (pkt_len <= len) { 249 uep_process_pkt(sc, p); 250 } else { 251 /* incomplete packet: save in buffer */ 252 memcpy(sc->buf, p, len); 253 sc->buf_len = len; 254 } 255 p += pkt_len; 256 len -= pkt_len; 257 } 258 } 259 case USB_ST_SETUP: 260 tr_setup: 261 /* check if we can put more data into the FIFO */ 262 if (usb_fifo_put_bytes_max(sc->fifo.fp[USB_FIFO_RX]) != 0) { 263 usbd_xfer_set_frame_len(xfer, 0, 264 usbd_xfer_max_len(xfer)); 265 usbd_transfer_submit(xfer); 266 } 267 break; 268 269 default: 270 if (error != USB_ERR_CANCELLED) { 271 /* try clear stall first */ 272 usbd_xfer_set_stall(xfer); 273 goto tr_setup; 274 } 275 break; 276 } 277} 278 279static const struct usb_config uep_config[UEP_N_TRANSFER] = { 280 [UEP_INTR_DT] = { 281 .type = UE_INTERRUPT, 282 .endpoint = UE_ADDR_ANY, 283 .direction = UE_DIR_IN, 284 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 285 .bufsize = 0, /* use wMaxPacketSize */ 286 .callback = &uep_intr_callback, 287 }, 288}; 289 290static const STRUCT_USB_HOST_ID uep_devs[] = { 291 {USB_VPI(USB_VENDOR_EGALAX, USB_PRODUCT_EGALAX_TPANEL, 0)}, 292 {USB_VPI(USB_VENDOR_EGALAX, USB_PRODUCT_EGALAX_TPANEL2, 0)}, 293 {USB_VPI(USB_VENDOR_EGALAX2, USB_PRODUCT_EGALAX2_TPANEL, 0)}, 294}; 295 296static int 297uep_probe(device_t dev) 298{ 299 struct usb_attach_arg *uaa = device_get_ivars(dev); 300 301 if (uaa->usb_mode != USB_MODE_HOST) 302 return (ENXIO); 303 if (uaa->info.bConfigIndex != 0) 304 return (ENXIO); 305 if (uaa->info.bIfaceIndex != 0) 306 return (ENXIO); 307 308 return (usbd_lookup_id_by_uaa(uep_devs, sizeof(uep_devs), uaa)); 309} 310 311static int 312uep_attach(device_t dev) 313{ 314 struct usb_attach_arg *uaa = device_get_ivars(dev); 315 struct uep_softc *sc = device_get_softc(dev); 316 int error; 317 318 device_set_usb_desc(dev); 319 320 mtx_init(&sc->mtx, "uep lock", NULL, MTX_DEF); 321 322 error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, 323 sc->xfer, uep_config, UEP_N_TRANSFER, sc, &sc->mtx); 324 325 if (error) { 326 DPRINTF("usbd_transfer_setup error=%s\n", usbd_errstr(error)); 327 goto detach; 328 } 329 330 error = usb_fifo_attach(uaa->device, sc, &sc->mtx, &uep_fifo_methods, 331 &sc->fifo, device_get_unit(dev), -1, uaa->info.bIfaceIndex, 332 UID_ROOT, GID_OPERATOR, 0644); 333 334 if (error) { 335 DPRINTF("usb_fifo_attach error=%s\n", usbd_errstr(error)); 336 goto detach; 337 } 338 339 sc->buf_len = 0; 340 341 return (0); 342 343detach: 344 uep_detach(dev); 345 346 return (ENOMEM); /* XXX */ 347} 348 349static int 350uep_detach(device_t dev) 351{ 352 struct uep_softc *sc = device_get_softc(dev); 353 354 usb_fifo_detach(&sc->fifo); 355 356 usbd_transfer_unsetup(sc->xfer, UEP_N_TRANSFER); 357 358 mtx_destroy(&sc->mtx); 359 360 return (0); 361} 362 363static void 364uep_start_read(struct usb_fifo *fifo) 365{ 366 struct uep_softc *sc = usb_fifo_softc(fifo); 367 u_int rate; 368 369 if ((rate = sc->pollrate) > 1000) 370 rate = 1000; 371 372 if (rate > 0 && sc->xfer[UEP_INTR_DT] != NULL) { 373 usbd_transfer_stop(sc->xfer[UEP_INTR_DT]); 374 usbd_xfer_set_interval(sc->xfer[UEP_INTR_DT], 1000 / rate); 375 sc->pollrate = 0; 376 } 377 378 usbd_transfer_start(sc->xfer[UEP_INTR_DT]); 379} 380 381static void 382uep_stop_read(struct usb_fifo *fifo) 383{ 384 struct uep_softc *sc = usb_fifo_softc(fifo); 385 386 usbd_transfer_stop(sc->xfer[UEP_INTR_DT]); 387} 388 389static void 390uep_put_queue(struct uep_softc *sc, u_char *buf) 391{ 392 usb_fifo_put_data_linear(sc->fifo.fp[USB_FIFO_RX], buf, 393 UEP_PACKET_LEN_REPORT, 1); 394} 395 396static int 397uep_open(struct usb_fifo *fifo, int fflags) 398{ 399 if (fflags & FREAD) { 400 struct uep_softc *sc = usb_fifo_softc(fifo); 401 402 if (sc->state & UEP_ENABLED) 403 return (EBUSY); 404 if (usb_fifo_alloc_buffer(fifo, UEP_FIFO_BUF_SIZE, 405 UEP_FIFO_QUEUE_MAXLEN)) 406 return (ENOMEM); 407 408 sc->state |= UEP_ENABLED; 409 } 410 411 return (0); 412} 413 414static void 415uep_close(struct usb_fifo *fifo, int fflags) 416{ 417 if (fflags & FREAD) { 418 struct uep_softc *sc = usb_fifo_softc(fifo); 419 420 sc->state &= ~(UEP_ENABLED); 421 usb_fifo_free_buffer(fifo); 422 } 423} 424 425static devclass_t uep_devclass; 426 427static device_method_t uep_methods[] = { 428 DEVMETHOD(device_probe, uep_probe), 429 DEVMETHOD(device_attach, uep_attach), 430 DEVMETHOD(device_detach, uep_detach), 431 { 0, 0 }, 432}; 433 434static driver_t uep_driver = { 435 .name = "uep", 436 .methods = uep_methods, 437 .size = sizeof(struct uep_softc), 438}; 439 440DRIVER_MODULE(uep, uhub, uep_driver, uep_devclass, NULL, NULL); 441MODULE_DEPEND(uep, usb, 1, 1, 1); 442MODULE_VERSION(uep, 1); 443USB_PNP_HOST_INFO(uep_devs); 444