1f25a8a01SGleb Smirnoff /*- 2718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3718cf2ccSPedro F. Giffuni * 4f25a8a01SGleb Smirnoff * Copyright 2010, Gleb Smirnoff <glebius@FreeBSD.org> 5f25a8a01SGleb Smirnoff * All rights reserved. 6f25a8a01SGleb Smirnoff * 7f25a8a01SGleb Smirnoff * Redistribution and use in source and binary forms, with or without 8f25a8a01SGleb Smirnoff * modification, are permitted provided that the following conditions 9f25a8a01SGleb Smirnoff * are met: 10f25a8a01SGleb Smirnoff * 1. Redistributions of source code must retain the above copyright 11f25a8a01SGleb Smirnoff * notice, this list of conditions and the following disclaimer. 12f25a8a01SGleb Smirnoff * 2. Redistributions in binary form must reproduce the above copyright 13f25a8a01SGleb Smirnoff * notice, this list of conditions and the following disclaimer in the 14f25a8a01SGleb Smirnoff * documentation and/or other materials provided with the distribution. 15f25a8a01SGleb Smirnoff * 16f25a8a01SGleb Smirnoff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17f25a8a01SGleb Smirnoff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18f25a8a01SGleb Smirnoff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19f25a8a01SGleb Smirnoff * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20f25a8a01SGleb Smirnoff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21f25a8a01SGleb Smirnoff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22f25a8a01SGleb Smirnoff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23f25a8a01SGleb Smirnoff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24f25a8a01SGleb Smirnoff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25f25a8a01SGleb Smirnoff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26f25a8a01SGleb Smirnoff * SUCH DAMAGE. 27f25a8a01SGleb Smirnoff * 28f25a8a01SGleb Smirnoff * $FreeBSD$ 29f25a8a01SGleb Smirnoff */ 30f25a8a01SGleb Smirnoff 31f25a8a01SGleb Smirnoff /* 32e626c40eSKevin Lo * http://www.eeti.com.tw/pdf/Software%20Programming%20Guide_v2.0.pdf 33f25a8a01SGleb Smirnoff */ 34f25a8a01SGleb Smirnoff 3526f3e847SVladimir Kondratyev #include "opt_evdev.h" 3626f3e847SVladimir Kondratyev 37f25a8a01SGleb Smirnoff #include <sys/param.h> 38f25a8a01SGleb Smirnoff #include <sys/bus.h> 39f25a8a01SGleb Smirnoff #include <sys/callout.h> 40f25a8a01SGleb Smirnoff #include <sys/conf.h> 41f25a8a01SGleb Smirnoff #include <sys/kernel.h> 42f25a8a01SGleb Smirnoff #include <sys/lock.h> 43f25a8a01SGleb Smirnoff #include <sys/module.h> 44f25a8a01SGleb Smirnoff #include <sys/mutex.h> 45f25a8a01SGleb Smirnoff #include <sys/sysctl.h> 46f25a8a01SGleb Smirnoff #include <sys/systm.h> 47f25a8a01SGleb Smirnoff 48f25a8a01SGleb Smirnoff #include <dev/usb/usb.h> 49f25a8a01SGleb Smirnoff #include <dev/usb/usbdi.h> 50f25a8a01SGleb Smirnoff #include <dev/usb/usbdi_util.h> 51f25a8a01SGleb Smirnoff #include <dev/usb/usbhid.h> 52f25a8a01SGleb Smirnoff #include "usbdevs.h" 53f25a8a01SGleb Smirnoff 5426f3e847SVladimir Kondratyev #ifdef EVDEV_SUPPORT 5526f3e847SVladimir Kondratyev #include <dev/evdev/input.h> 5626f3e847SVladimir Kondratyev #include <dev/evdev/evdev.h> 5726f3e847SVladimir Kondratyev #else 58f25a8a01SGleb Smirnoff #include <sys/ioccom.h> 59f25a8a01SGleb Smirnoff #include <sys/fcntl.h> 60f25a8a01SGleb Smirnoff #include <sys/tty.h> 6126f3e847SVladimir Kondratyev #endif 62f25a8a01SGleb Smirnoff 63f25a8a01SGleb Smirnoff #define USB_DEBUG_VAR uep_debug 64f25a8a01SGleb Smirnoff #include <dev/usb/usb_debug.h> 65f25a8a01SGleb Smirnoff 66f25a8a01SGleb Smirnoff #ifdef USB_DEBUG 67f25a8a01SGleb Smirnoff static int uep_debug = 0; 68f25a8a01SGleb Smirnoff 696472ac3dSEd Schouten static SYSCTL_NODE(_hw_usb, OID_AUTO, uep, CTLFLAG_RW, 0, "USB uep"); 70ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_uep, OID_AUTO, debug, CTLFLAG_RWTUN, 71f25a8a01SGleb Smirnoff &uep_debug, 0, "Debug level"); 72f25a8a01SGleb Smirnoff #endif 73f25a8a01SGleb Smirnoff 74f25a8a01SGleb Smirnoff #define UEP_MAX_X 2047 75f25a8a01SGleb Smirnoff #define UEP_MAX_Y 2047 76f25a8a01SGleb Smirnoff 77f25a8a01SGleb Smirnoff #define UEP_DOWN 0x01 78f25a8a01SGleb Smirnoff #define UEP_PACKET_LEN_MAX 16 79f25a8a01SGleb Smirnoff #define UEP_PACKET_LEN_REPORT 5 80f25a8a01SGleb Smirnoff #define UEP_PACKET_LEN_REPORT2 6 81f25a8a01SGleb Smirnoff #define UEP_PACKET_DIAG 0x0a 82f25a8a01SGleb Smirnoff #define UEP_PACKET_REPORT_MASK 0xe0 83f25a8a01SGleb Smirnoff #define UEP_PACKET_REPORT 0x80 84f25a8a01SGleb Smirnoff #define UEP_PACKET_REPORT_PRESSURE 0xc0 85f25a8a01SGleb Smirnoff #define UEP_PACKET_REPORT_PLAYER 0xa0 86f25a8a01SGleb Smirnoff #define UEP_PACKET_LEN_MASK 87f25a8a01SGleb Smirnoff 88f25a8a01SGleb Smirnoff #define UEP_FIFO_BUF_SIZE 8 /* bytes */ 89f25a8a01SGleb Smirnoff #define UEP_FIFO_QUEUE_MAXLEN 50 /* units */ 90f25a8a01SGleb Smirnoff 91f25a8a01SGleb Smirnoff enum { 92f25a8a01SGleb Smirnoff UEP_INTR_DT, 93f25a8a01SGleb Smirnoff UEP_N_TRANSFER, 94f25a8a01SGleb Smirnoff }; 95f25a8a01SGleb Smirnoff 96f25a8a01SGleb Smirnoff struct uep_softc { 97f25a8a01SGleb Smirnoff struct mtx mtx; 98f25a8a01SGleb Smirnoff 99f25a8a01SGleb Smirnoff struct usb_xfer *xfer[UEP_N_TRANSFER]; 10026f3e847SVladimir Kondratyev #ifdef EVDEV_SUPPORT 10126f3e847SVladimir Kondratyev struct evdev_dev *evdev; 10226f3e847SVladimir Kondratyev #else 103f25a8a01SGleb Smirnoff struct usb_fifo_sc fifo; 104f25a8a01SGleb Smirnoff 105f25a8a01SGleb Smirnoff u_int pollrate; 106f25a8a01SGleb Smirnoff u_int state; 107f25a8a01SGleb Smirnoff #define UEP_ENABLED 0x01 10826f3e847SVladimir Kondratyev #endif 109f25a8a01SGleb Smirnoff 110f25a8a01SGleb Smirnoff /* Reassembling buffer. */ 111f25a8a01SGleb Smirnoff u_char buf[UEP_PACKET_LEN_MAX]; 112f25a8a01SGleb Smirnoff uint8_t buf_len; 113f25a8a01SGleb Smirnoff }; 114f25a8a01SGleb Smirnoff 115f25a8a01SGleb Smirnoff static usb_callback_t uep_intr_callback; 116f25a8a01SGleb Smirnoff 117f25a8a01SGleb Smirnoff static device_probe_t uep_probe; 118f25a8a01SGleb Smirnoff static device_attach_t uep_attach; 119f25a8a01SGleb Smirnoff static device_detach_t uep_detach; 120f25a8a01SGleb Smirnoff 12126f3e847SVladimir Kondratyev #ifdef EVDEV_SUPPORT 12226f3e847SVladimir Kondratyev 12326f3e847SVladimir Kondratyev static evdev_open_t uep_ev_open; 12426f3e847SVladimir Kondratyev static evdev_close_t uep_ev_close; 12526f3e847SVladimir Kondratyev 12626f3e847SVladimir Kondratyev static const struct evdev_methods uep_evdev_methods = { 12726f3e847SVladimir Kondratyev .ev_open = &uep_ev_open, 12826f3e847SVladimir Kondratyev .ev_close = &uep_ev_close, 12926f3e847SVladimir Kondratyev }; 13026f3e847SVladimir Kondratyev 13126f3e847SVladimir Kondratyev #else /* !EVDEV_SUPPORT */ 13226f3e847SVladimir Kondratyev 133f25a8a01SGleb Smirnoff static usb_fifo_cmd_t uep_start_read; 134f25a8a01SGleb Smirnoff static usb_fifo_cmd_t uep_stop_read; 135f25a8a01SGleb Smirnoff static usb_fifo_open_t uep_open; 136f25a8a01SGleb Smirnoff static usb_fifo_close_t uep_close; 137f25a8a01SGleb Smirnoff 138f25a8a01SGleb Smirnoff static void uep_put_queue(struct uep_softc *, u_char *); 139f25a8a01SGleb Smirnoff 140f25a8a01SGleb Smirnoff static struct usb_fifo_methods uep_fifo_methods = { 141f25a8a01SGleb Smirnoff .f_open = &uep_open, 142f25a8a01SGleb Smirnoff .f_close = &uep_close, 143f25a8a01SGleb Smirnoff .f_start_read = &uep_start_read, 144f25a8a01SGleb Smirnoff .f_stop_read = &uep_stop_read, 145f25a8a01SGleb Smirnoff .basename[0] = "uep", 146f25a8a01SGleb Smirnoff }; 14726f3e847SVladimir Kondratyev #endif /* !EVDEV_SUPPORT */ 148f25a8a01SGleb Smirnoff 149f25a8a01SGleb Smirnoff static int 150f25a8a01SGleb Smirnoff get_pkt_len(u_char *buf) 151f25a8a01SGleb Smirnoff { 152f25a8a01SGleb Smirnoff if (buf[0] == UEP_PACKET_DIAG) { 153f25a8a01SGleb Smirnoff int len; 154f25a8a01SGleb Smirnoff 155f25a8a01SGleb Smirnoff len = buf[1] + 2; 156f25a8a01SGleb Smirnoff if (len > UEP_PACKET_LEN_MAX) { 157f25a8a01SGleb Smirnoff DPRINTF("bad packet len %u\n", len); 158f25a8a01SGleb Smirnoff return (UEP_PACKET_LEN_MAX); 159f25a8a01SGleb Smirnoff } 160f25a8a01SGleb Smirnoff 161f25a8a01SGleb Smirnoff return (len); 162f25a8a01SGleb Smirnoff } 163f25a8a01SGleb Smirnoff 164f25a8a01SGleb Smirnoff switch (buf[0] & UEP_PACKET_REPORT_MASK) { 165f25a8a01SGleb Smirnoff case UEP_PACKET_REPORT: 166f25a8a01SGleb Smirnoff return (UEP_PACKET_LEN_REPORT); 167f25a8a01SGleb Smirnoff case UEP_PACKET_REPORT_PRESSURE: 168f25a8a01SGleb Smirnoff case UEP_PACKET_REPORT_PLAYER: 169f25a8a01SGleb Smirnoff case UEP_PACKET_REPORT_PRESSURE | UEP_PACKET_REPORT_PLAYER: 170f25a8a01SGleb Smirnoff return (UEP_PACKET_LEN_REPORT2); 171f25a8a01SGleb Smirnoff default: 172f25a8a01SGleb Smirnoff DPRINTF("bad packet len 0\n"); 173f25a8a01SGleb Smirnoff return (0); 174f25a8a01SGleb Smirnoff } 175f25a8a01SGleb Smirnoff } 176f25a8a01SGleb Smirnoff 177f25a8a01SGleb Smirnoff static void 178f25a8a01SGleb Smirnoff uep_process_pkt(struct uep_softc *sc, u_char *buf) 179f25a8a01SGleb Smirnoff { 180f25a8a01SGleb Smirnoff int32_t x, y; 18126f3e847SVladimir Kondratyev #ifdef EVDEV_SUPPORT 18226f3e847SVladimir Kondratyev int touch; 18326f3e847SVladimir Kondratyev #endif 184f25a8a01SGleb Smirnoff 185f25a8a01SGleb Smirnoff if ((buf[0] & 0xFE) != 0x80) { 186f25a8a01SGleb Smirnoff DPRINTF("bad input packet format 0x%.2x\n", buf[0]); 187f25a8a01SGleb Smirnoff return; 188f25a8a01SGleb Smirnoff } 189f25a8a01SGleb Smirnoff 190f25a8a01SGleb Smirnoff /* 191f25a8a01SGleb Smirnoff * Packet format is 5 bytes: 192f25a8a01SGleb Smirnoff * 193f25a8a01SGleb Smirnoff * 1000000T 194f25a8a01SGleb Smirnoff * 0000AAAA 195f25a8a01SGleb Smirnoff * 0AAAAAAA 196f25a8a01SGleb Smirnoff * 0000BBBB 197f25a8a01SGleb Smirnoff * 0BBBBBBB 198f25a8a01SGleb Smirnoff * 199f25a8a01SGleb Smirnoff * T: 1=touched 0=not touched 200f25a8a01SGleb Smirnoff * A: bits of axis A position, MSB to LSB 201f25a8a01SGleb Smirnoff * B: bits of axis B position, MSB to LSB 202f25a8a01SGleb Smirnoff * 203f25a8a01SGleb Smirnoff * For the unit I have, which is CTF1020-S from CarTFT.com, 204f25a8a01SGleb Smirnoff * A = X and B = Y. But in NetBSD uep(4) it is other way round :) 205f25a8a01SGleb Smirnoff * 206f25a8a01SGleb Smirnoff * The controller sends a stream of T=1 events while the 207f25a8a01SGleb Smirnoff * panel is touched, followed by a single T=0 event. 208f25a8a01SGleb Smirnoff * 209f25a8a01SGleb Smirnoff */ 210f25a8a01SGleb Smirnoff 211f25a8a01SGleb Smirnoff x = (buf[1] << 7) | buf[2]; 212f25a8a01SGleb Smirnoff y = (buf[3] << 7) | buf[4]; 213f25a8a01SGleb Smirnoff 214f25a8a01SGleb Smirnoff DPRINTFN(2, "x %u y %u\n", x, y); 215f25a8a01SGleb Smirnoff 21626f3e847SVladimir Kondratyev #ifdef EVDEV_SUPPORT 21726f3e847SVladimir Kondratyev touch = buf[0] & (1 << 0); 21826f3e847SVladimir Kondratyev if (touch) { 21926f3e847SVladimir Kondratyev evdev_push_abs(sc->evdev, ABS_X, x); 22026f3e847SVladimir Kondratyev evdev_push_abs(sc->evdev, ABS_Y, y); 22126f3e847SVladimir Kondratyev } 22226f3e847SVladimir Kondratyev evdev_push_key(sc->evdev, BTN_TOUCH, touch); 22326f3e847SVladimir Kondratyev evdev_sync(sc->evdev); 22426f3e847SVladimir Kondratyev #else 225f25a8a01SGleb Smirnoff uep_put_queue(sc, buf); 22626f3e847SVladimir Kondratyev #endif 227f25a8a01SGleb Smirnoff } 228f25a8a01SGleb Smirnoff 229f25a8a01SGleb Smirnoff static void 230f25a8a01SGleb Smirnoff uep_intr_callback(struct usb_xfer *xfer, usb_error_t error) 231f25a8a01SGleb Smirnoff { 232f25a8a01SGleb Smirnoff struct uep_softc *sc = usbd_xfer_softc(xfer); 233f25a8a01SGleb Smirnoff int len; 234f25a8a01SGleb Smirnoff 235f25a8a01SGleb Smirnoff usbd_xfer_status(xfer, &len, NULL, NULL, NULL); 236f25a8a01SGleb Smirnoff 237f25a8a01SGleb Smirnoff switch (USB_GET_STATE(xfer)) { 238f25a8a01SGleb Smirnoff case USB_ST_TRANSFERRED: 239f25a8a01SGleb Smirnoff { 240f25a8a01SGleb Smirnoff struct usb_page_cache *pc; 241f25a8a01SGleb Smirnoff u_char buf[17], *p; 242f25a8a01SGleb Smirnoff int pkt_len; 243f25a8a01SGleb Smirnoff 2446d917491SHans Petter Selasky if (len > (int)sizeof(buf)) { 245f25a8a01SGleb Smirnoff DPRINTF("bad input length %d\n", len); 246f25a8a01SGleb Smirnoff goto tr_setup; 247f25a8a01SGleb Smirnoff } 248f25a8a01SGleb Smirnoff 249f25a8a01SGleb Smirnoff pc = usbd_xfer_get_frame(xfer, 0); 250f25a8a01SGleb Smirnoff usbd_copy_out(pc, 0, buf, len); 251f25a8a01SGleb Smirnoff 252f25a8a01SGleb Smirnoff /* 253f25a8a01SGleb Smirnoff * The below code mimics Linux a lot. I don't know 254f25a8a01SGleb Smirnoff * why NetBSD reads complete packets, but we need 255f25a8a01SGleb Smirnoff * to reassamble 'em like Linux does (tries?). 256f25a8a01SGleb Smirnoff */ 257f25a8a01SGleb Smirnoff if (sc->buf_len > 0) { 258f25a8a01SGleb Smirnoff int res; 259f25a8a01SGleb Smirnoff 260f25a8a01SGleb Smirnoff if (sc->buf_len == 1) 261f25a8a01SGleb Smirnoff sc->buf[1] = buf[0]; 262f25a8a01SGleb Smirnoff 263f25a8a01SGleb Smirnoff if ((pkt_len = get_pkt_len(sc->buf)) == 0) 264f25a8a01SGleb Smirnoff goto tr_setup; 265f25a8a01SGleb Smirnoff 266f25a8a01SGleb Smirnoff res = pkt_len - sc->buf_len; 267f25a8a01SGleb Smirnoff memcpy(sc->buf + sc->buf_len, buf, res); 268f25a8a01SGleb Smirnoff uep_process_pkt(sc, sc->buf); 269f25a8a01SGleb Smirnoff sc->buf_len = 0; 270f25a8a01SGleb Smirnoff 271f25a8a01SGleb Smirnoff p = buf + res; 272f25a8a01SGleb Smirnoff len -= res; 273f25a8a01SGleb Smirnoff } else 274f25a8a01SGleb Smirnoff p = buf; 275f25a8a01SGleb Smirnoff 276f25a8a01SGleb Smirnoff if (len == 1) { 277f25a8a01SGleb Smirnoff sc->buf[0] = buf[0]; 278f25a8a01SGleb Smirnoff sc->buf_len = 1; 279f25a8a01SGleb Smirnoff 280f25a8a01SGleb Smirnoff goto tr_setup; 281f25a8a01SGleb Smirnoff } 282f25a8a01SGleb Smirnoff 283f25a8a01SGleb Smirnoff while (len > 0) { 284f25a8a01SGleb Smirnoff if ((pkt_len = get_pkt_len(p)) == 0) 285f25a8a01SGleb Smirnoff goto tr_setup; 286f25a8a01SGleb Smirnoff 287f25a8a01SGleb Smirnoff /* full packet: process */ 288f25a8a01SGleb Smirnoff if (pkt_len <= len) { 289f25a8a01SGleb Smirnoff uep_process_pkt(sc, p); 290f25a8a01SGleb Smirnoff } else { 291f25a8a01SGleb Smirnoff /* incomplete packet: save in buffer */ 292f25a8a01SGleb Smirnoff memcpy(sc->buf, p, len); 293f25a8a01SGleb Smirnoff sc->buf_len = len; 294f25a8a01SGleb Smirnoff } 295f25a8a01SGleb Smirnoff p += pkt_len; 296f25a8a01SGleb Smirnoff len -= pkt_len; 297f25a8a01SGleb Smirnoff } 298f25a8a01SGleb Smirnoff } 299f25a8a01SGleb Smirnoff case USB_ST_SETUP: 300f25a8a01SGleb Smirnoff tr_setup: 30126f3e847SVladimir Kondratyev #ifndef EVDEV_SUPPORT 302f25a8a01SGleb Smirnoff /* check if we can put more data into the FIFO */ 30326f3e847SVladimir Kondratyev if (usb_fifo_put_bytes_max(sc->fifo.fp[USB_FIFO_RX]) == 0) 30426f3e847SVladimir Kondratyev break; 30526f3e847SVladimir Kondratyev #endif 30626f3e847SVladimir Kondratyev usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 307f25a8a01SGleb Smirnoff usbd_transfer_submit(xfer); 308f25a8a01SGleb Smirnoff break; 309f25a8a01SGleb Smirnoff 310f25a8a01SGleb Smirnoff default: 311f25a8a01SGleb Smirnoff if (error != USB_ERR_CANCELLED) { 312f25a8a01SGleb Smirnoff /* try clear stall first */ 313f25a8a01SGleb Smirnoff usbd_xfer_set_stall(xfer); 314f25a8a01SGleb Smirnoff goto tr_setup; 315f25a8a01SGleb Smirnoff } 316f25a8a01SGleb Smirnoff break; 317f25a8a01SGleb Smirnoff } 318f25a8a01SGleb Smirnoff } 319f25a8a01SGleb Smirnoff 320f25a8a01SGleb Smirnoff static const struct usb_config uep_config[UEP_N_TRANSFER] = { 321f25a8a01SGleb Smirnoff [UEP_INTR_DT] = { 322f25a8a01SGleb Smirnoff .type = UE_INTERRUPT, 323f25a8a01SGleb Smirnoff .endpoint = UE_ADDR_ANY, 324f25a8a01SGleb Smirnoff .direction = UE_DIR_IN, 325f25a8a01SGleb Smirnoff .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 326f25a8a01SGleb Smirnoff .bufsize = 0, /* use wMaxPacketSize */ 327f25a8a01SGleb Smirnoff .callback = &uep_intr_callback, 328f25a8a01SGleb Smirnoff }, 329f25a8a01SGleb Smirnoff }; 330f25a8a01SGleb Smirnoff 3319c916c62SHans Petter Selasky static const STRUCT_USB_HOST_ID uep_devs[] = { 3329c916c62SHans Petter Selasky {USB_VPI(USB_VENDOR_EGALAX, USB_PRODUCT_EGALAX_TPANEL, 0)}, 3339c916c62SHans Petter Selasky {USB_VPI(USB_VENDOR_EGALAX, USB_PRODUCT_EGALAX_TPANEL2, 0)}, 3349c916c62SHans Petter Selasky {USB_VPI(USB_VENDOR_EGALAX2, USB_PRODUCT_EGALAX2_TPANEL, 0)}, 3359c916c62SHans Petter Selasky }; 3369c916c62SHans Petter Selasky 337f25a8a01SGleb Smirnoff static int 338f25a8a01SGleb Smirnoff uep_probe(device_t dev) 339f25a8a01SGleb Smirnoff { 340f25a8a01SGleb Smirnoff struct usb_attach_arg *uaa = device_get_ivars(dev); 341f25a8a01SGleb Smirnoff 342f25a8a01SGleb Smirnoff if (uaa->usb_mode != USB_MODE_HOST) 343f25a8a01SGleb Smirnoff return (ENXIO); 3449c916c62SHans Petter Selasky if (uaa->info.bConfigIndex != 0) 345f25a8a01SGleb Smirnoff return (ENXIO); 3469c916c62SHans Petter Selasky if (uaa->info.bIfaceIndex != 0) 3479c916c62SHans Petter Selasky return (ENXIO); 3489c916c62SHans Petter Selasky 3499c916c62SHans Petter Selasky return (usbd_lookup_id_by_uaa(uep_devs, sizeof(uep_devs), uaa)); 350f25a8a01SGleb Smirnoff } 351f25a8a01SGleb Smirnoff 352f25a8a01SGleb Smirnoff static int 353f25a8a01SGleb Smirnoff uep_attach(device_t dev) 354f25a8a01SGleb Smirnoff { 355f25a8a01SGleb Smirnoff struct usb_attach_arg *uaa = device_get_ivars(dev); 356f25a8a01SGleb Smirnoff struct uep_softc *sc = device_get_softc(dev); 357f25a8a01SGleb Smirnoff int error; 358f25a8a01SGleb Smirnoff 359f25a8a01SGleb Smirnoff device_set_usb_desc(dev); 360f25a8a01SGleb Smirnoff 361f25a8a01SGleb Smirnoff mtx_init(&sc->mtx, "uep lock", NULL, MTX_DEF); 362f25a8a01SGleb Smirnoff 363f25a8a01SGleb Smirnoff error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, 364f25a8a01SGleb Smirnoff sc->xfer, uep_config, UEP_N_TRANSFER, sc, &sc->mtx); 365f25a8a01SGleb Smirnoff 366f25a8a01SGleb Smirnoff if (error) { 367f25a8a01SGleb Smirnoff DPRINTF("usbd_transfer_setup error=%s\n", usbd_errstr(error)); 368f25a8a01SGleb Smirnoff goto detach; 369f25a8a01SGleb Smirnoff } 370f25a8a01SGleb Smirnoff 37126f3e847SVladimir Kondratyev #ifdef EVDEV_SUPPORT 37226f3e847SVladimir Kondratyev sc->evdev = evdev_alloc(); 37326f3e847SVladimir Kondratyev evdev_set_name(sc->evdev, device_get_desc(dev)); 37426f3e847SVladimir Kondratyev evdev_set_phys(sc->evdev, device_get_nameunit(dev)); 37526f3e847SVladimir Kondratyev evdev_set_id(sc->evdev, BUS_USB, uaa->info.idVendor, 37626f3e847SVladimir Kondratyev uaa->info.idProduct, 0); 37726f3e847SVladimir Kondratyev evdev_set_serial(sc->evdev, usb_get_serial(uaa->device)); 37826f3e847SVladimir Kondratyev evdev_set_methods(sc->evdev, sc, &uep_evdev_methods); 37926f3e847SVladimir Kondratyev evdev_support_prop(sc->evdev, INPUT_PROP_DIRECT); 38026f3e847SVladimir Kondratyev evdev_support_event(sc->evdev, EV_SYN); 38126f3e847SVladimir Kondratyev evdev_support_event(sc->evdev, EV_ABS); 38226f3e847SVladimir Kondratyev evdev_support_event(sc->evdev, EV_KEY); 38326f3e847SVladimir Kondratyev evdev_support_key(sc->evdev, BTN_TOUCH); 38426f3e847SVladimir Kondratyev evdev_support_abs(sc->evdev, ABS_X, 0, 0, UEP_MAX_X, 0, 0, 0); 38526f3e847SVladimir Kondratyev evdev_support_abs(sc->evdev, ABS_Y, 0, 0, UEP_MAX_Y, 0, 0, 0); 38626f3e847SVladimir Kondratyev 38726f3e847SVladimir Kondratyev error = evdev_register_mtx(sc->evdev, &sc->mtx); 38826f3e847SVladimir Kondratyev if (error) { 38926f3e847SVladimir Kondratyev DPRINTF("evdev_register_mtx error=%s\n", usbd_errstr(error)); 39026f3e847SVladimir Kondratyev goto detach; 39126f3e847SVladimir Kondratyev } 39226f3e847SVladimir Kondratyev #else /* !EVDEV_SUPPORT */ 393f25a8a01SGleb Smirnoff error = usb_fifo_attach(uaa->device, sc, &sc->mtx, &uep_fifo_methods, 3946d917491SHans Petter Selasky &sc->fifo, device_get_unit(dev), -1, uaa->info.bIfaceIndex, 395f25a8a01SGleb Smirnoff UID_ROOT, GID_OPERATOR, 0644); 396f25a8a01SGleb Smirnoff 397f25a8a01SGleb Smirnoff if (error) { 398f25a8a01SGleb Smirnoff DPRINTF("usb_fifo_attach error=%s\n", usbd_errstr(error)); 399f25a8a01SGleb Smirnoff goto detach; 400f25a8a01SGleb Smirnoff } 40126f3e847SVladimir Kondratyev #endif /* !EVDEV_SUPPORT */ 402f25a8a01SGleb Smirnoff 403f25a8a01SGleb Smirnoff sc->buf_len = 0; 404f25a8a01SGleb Smirnoff 405f25a8a01SGleb Smirnoff return (0); 406f25a8a01SGleb Smirnoff 407f25a8a01SGleb Smirnoff detach: 408f25a8a01SGleb Smirnoff uep_detach(dev); 409f25a8a01SGleb Smirnoff 410f25a8a01SGleb Smirnoff return (ENOMEM); /* XXX */ 411f25a8a01SGleb Smirnoff } 412f25a8a01SGleb Smirnoff 413f25a8a01SGleb Smirnoff static int 414f25a8a01SGleb Smirnoff uep_detach(device_t dev) 415f25a8a01SGleb Smirnoff { 416f25a8a01SGleb Smirnoff struct uep_softc *sc = device_get_softc(dev); 417f25a8a01SGleb Smirnoff 41826f3e847SVladimir Kondratyev #ifdef EVDEV_SUPPORT 41926f3e847SVladimir Kondratyev evdev_free(sc->evdev); 42026f3e847SVladimir Kondratyev #else 421f25a8a01SGleb Smirnoff usb_fifo_detach(&sc->fifo); 42226f3e847SVladimir Kondratyev #endif 423f25a8a01SGleb Smirnoff 424f25a8a01SGleb Smirnoff usbd_transfer_unsetup(sc->xfer, UEP_N_TRANSFER); 425f25a8a01SGleb Smirnoff 426f25a8a01SGleb Smirnoff mtx_destroy(&sc->mtx); 427f25a8a01SGleb Smirnoff 428f25a8a01SGleb Smirnoff return (0); 429f25a8a01SGleb Smirnoff } 430f25a8a01SGleb Smirnoff 43126f3e847SVladimir Kondratyev #ifdef EVDEV_SUPPORT 43226f3e847SVladimir Kondratyev 433*911aed94SVladimir Kondratyev static int 434*911aed94SVladimir Kondratyev uep_ev_close(struct evdev_dev *evdev) 43526f3e847SVladimir Kondratyev { 436*911aed94SVladimir Kondratyev struct uep_softc *sc = evdev_get_softc(evdev); 43726f3e847SVladimir Kondratyev 43826f3e847SVladimir Kondratyev mtx_assert(&sc->mtx, MA_OWNED); 43926f3e847SVladimir Kondratyev usbd_transfer_stop(sc->xfer[UEP_INTR_DT]); 440*911aed94SVladimir Kondratyev 441*911aed94SVladimir Kondratyev return (0); 44226f3e847SVladimir Kondratyev } 44326f3e847SVladimir Kondratyev 44426f3e847SVladimir Kondratyev static int 445*911aed94SVladimir Kondratyev uep_ev_open(struct evdev_dev *evdev) 44626f3e847SVladimir Kondratyev { 447*911aed94SVladimir Kondratyev struct uep_softc *sc = evdev_get_softc(evdev); 44826f3e847SVladimir Kondratyev 44926f3e847SVladimir Kondratyev mtx_assert(&sc->mtx, MA_OWNED); 45026f3e847SVladimir Kondratyev usbd_transfer_start(sc->xfer[UEP_INTR_DT]); 45126f3e847SVladimir Kondratyev 45226f3e847SVladimir Kondratyev return (0); 45326f3e847SVladimir Kondratyev } 45426f3e847SVladimir Kondratyev 45526f3e847SVladimir Kondratyev #else /* !EVDEV_SUPPORT */ 45626f3e847SVladimir Kondratyev 457f25a8a01SGleb Smirnoff static void 458f25a8a01SGleb Smirnoff uep_start_read(struct usb_fifo *fifo) 459f25a8a01SGleb Smirnoff { 460f25a8a01SGleb Smirnoff struct uep_softc *sc = usb_fifo_softc(fifo); 461f25a8a01SGleb Smirnoff u_int rate; 462f25a8a01SGleb Smirnoff 463f25a8a01SGleb Smirnoff if ((rate = sc->pollrate) > 1000) 464f25a8a01SGleb Smirnoff rate = 1000; 465f25a8a01SGleb Smirnoff 466f25a8a01SGleb Smirnoff if (rate > 0 && sc->xfer[UEP_INTR_DT] != NULL) { 467f25a8a01SGleb Smirnoff usbd_transfer_stop(sc->xfer[UEP_INTR_DT]); 468f25a8a01SGleb Smirnoff usbd_xfer_set_interval(sc->xfer[UEP_INTR_DT], 1000 / rate); 469f25a8a01SGleb Smirnoff sc->pollrate = 0; 470f25a8a01SGleb Smirnoff } 471f25a8a01SGleb Smirnoff 472f25a8a01SGleb Smirnoff usbd_transfer_start(sc->xfer[UEP_INTR_DT]); 473f25a8a01SGleb Smirnoff } 474f25a8a01SGleb Smirnoff 475f25a8a01SGleb Smirnoff static void 476f25a8a01SGleb Smirnoff uep_stop_read(struct usb_fifo *fifo) 477f25a8a01SGleb Smirnoff { 478f25a8a01SGleb Smirnoff struct uep_softc *sc = usb_fifo_softc(fifo); 479f25a8a01SGleb Smirnoff 480f25a8a01SGleb Smirnoff usbd_transfer_stop(sc->xfer[UEP_INTR_DT]); 481f25a8a01SGleb Smirnoff } 482f25a8a01SGleb Smirnoff 483f25a8a01SGleb Smirnoff static void 484f25a8a01SGleb Smirnoff uep_put_queue(struct uep_softc *sc, u_char *buf) 485f25a8a01SGleb Smirnoff { 486f25a8a01SGleb Smirnoff usb_fifo_put_data_linear(sc->fifo.fp[USB_FIFO_RX], buf, 487f25a8a01SGleb Smirnoff UEP_PACKET_LEN_REPORT, 1); 488f25a8a01SGleb Smirnoff } 489f25a8a01SGleb Smirnoff 490f25a8a01SGleb Smirnoff static int 491f25a8a01SGleb Smirnoff uep_open(struct usb_fifo *fifo, int fflags) 492f25a8a01SGleb Smirnoff { 493f25a8a01SGleb Smirnoff if (fflags & FREAD) { 494f25a8a01SGleb Smirnoff struct uep_softc *sc = usb_fifo_softc(fifo); 495f25a8a01SGleb Smirnoff 496f25a8a01SGleb Smirnoff if (sc->state & UEP_ENABLED) 497f25a8a01SGleb Smirnoff return (EBUSY); 498f25a8a01SGleb Smirnoff if (usb_fifo_alloc_buffer(fifo, UEP_FIFO_BUF_SIZE, 499f25a8a01SGleb Smirnoff UEP_FIFO_QUEUE_MAXLEN)) 500f25a8a01SGleb Smirnoff return (ENOMEM); 501f25a8a01SGleb Smirnoff 502f25a8a01SGleb Smirnoff sc->state |= UEP_ENABLED; 503f25a8a01SGleb Smirnoff } 504f25a8a01SGleb Smirnoff 505f25a8a01SGleb Smirnoff return (0); 506f25a8a01SGleb Smirnoff } 507f25a8a01SGleb Smirnoff 508f25a8a01SGleb Smirnoff static void 509f25a8a01SGleb Smirnoff uep_close(struct usb_fifo *fifo, int fflags) 510f25a8a01SGleb Smirnoff { 511f25a8a01SGleb Smirnoff if (fflags & FREAD) { 512f25a8a01SGleb Smirnoff struct uep_softc *sc = usb_fifo_softc(fifo); 513f25a8a01SGleb Smirnoff 514f25a8a01SGleb Smirnoff sc->state &= ~(UEP_ENABLED); 515f25a8a01SGleb Smirnoff usb_fifo_free_buffer(fifo); 516f25a8a01SGleb Smirnoff } 517f25a8a01SGleb Smirnoff } 51826f3e847SVladimir Kondratyev #endif /* !EVDEV_SUPPORT */ 519f25a8a01SGleb Smirnoff 520f25a8a01SGleb Smirnoff static devclass_t uep_devclass; 521f25a8a01SGleb Smirnoff 522f25a8a01SGleb Smirnoff static device_method_t uep_methods[] = { 523f25a8a01SGleb Smirnoff DEVMETHOD(device_probe, uep_probe), 524f25a8a01SGleb Smirnoff DEVMETHOD(device_attach, uep_attach), 525f25a8a01SGleb Smirnoff DEVMETHOD(device_detach, uep_detach), 526f25a8a01SGleb Smirnoff { 0, 0 }, 527f25a8a01SGleb Smirnoff }; 528f25a8a01SGleb Smirnoff 529f25a8a01SGleb Smirnoff static driver_t uep_driver = { 530f25a8a01SGleb Smirnoff .name = "uep", 531f25a8a01SGleb Smirnoff .methods = uep_methods, 532f25a8a01SGleb Smirnoff .size = sizeof(struct uep_softc), 533f25a8a01SGleb Smirnoff }; 534f25a8a01SGleb Smirnoff 535f25a8a01SGleb Smirnoff DRIVER_MODULE(uep, uhub, uep_driver, uep_devclass, NULL, NULL); 536f25a8a01SGleb Smirnoff MODULE_DEPEND(uep, usb, 1, 1, 1); 53726f3e847SVladimir Kondratyev #ifdef EVDEV_SUPPORT 53826f3e847SVladimir Kondratyev MODULE_DEPEND(uep, evdev, 1, 1, 1); 53926f3e847SVladimir Kondratyev #endif 540910cb8feSAndrew Thompson MODULE_VERSION(uep, 1); 541f809f280SWarner Losh USB_PNP_HOST_INFO(uep_devs); 542