Date: Tue, 3 Jun 2014 20:27:08 +0000 (UTC) From: Jakub Wojciech Klama <jceel@FreeBSD.org> To: src-committers@freebsd.org, svn-src-user@freebsd.org Subject: svn commit: r267025 - in user/jceel/soc2014_evdev/head/sys: amd64/conf conf dev/usb/input Message-ID: <201406032027.s53KR8Mo067067@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: jceel Date: Tue Jun 3 20:27:07 2014 New Revision: 267025 URL: http://svnweb.freebsd.org/changeset/base/267025 Log: Add initial version of USB HID touchscreen driver, based on ums(4). Added: user/jceel/soc2014_evdev/head/sys/dev/usb/input/utouch.c (contents, props changed) Modified: user/jceel/soc2014_evdev/head/sys/amd64/conf/EVDEV user/jceel/soc2014_evdev/head/sys/conf/files Modified: user/jceel/soc2014_evdev/head/sys/amd64/conf/EVDEV ============================================================================== --- user/jceel/soc2014_evdev/head/sys/amd64/conf/EVDEV Tue Jun 3 20:09:39 2014 (r267024) +++ user/jceel/soc2014_evdev/head/sys/amd64/conf/EVDEV Tue Jun 3 20:27:07 2014 (r267025) @@ -320,7 +320,8 @@ device usb # USB Bus (required) device ukbd # Keyboard device umass # Disks/Mass storage - Requires scbus and da device ums -device ukbd +device uhid +device utouch # Sound support device sound # Generic sound driver (required) Modified: user/jceel/soc2014_evdev/head/sys/conf/files ============================================================================== --- user/jceel/soc2014_evdev/head/sys/conf/files Tue Jun 3 20:09:39 2014 (r267024) +++ user/jceel/soc2014_evdev/head/sys/conf/files Tue Jun 3 20:27:07 2014 (r267025) @@ -2476,6 +2476,7 @@ dev/usb/input/uhid.c optional uhid dev/usb/input/ukbd.c optional ukbd dev/usb/input/ums.c optional ums dev/usb/input/wsp.c optional wsp +dev/usb/input/utouch.c optional utouch # # USB quirks # Added: user/jceel/soc2014_evdev/head/sys/dev/usb/input/utouch.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ user/jceel/soc2014_evdev/head/sys/dev/usb/input/utouch.c Tue Jun 3 20:27:07 2014 (r267025) @@ -0,0 +1,493 @@ +/*- + * Copyright (c) 2014 Jakub Wojciech Klama <jceel@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/stdint.h> +#include <sys/stddef.h> +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/types.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/module.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/condvar.h> +#include <sys/sysctl.h> +#include <sys/sx.h> +#include <sys/unistd.h> +#include <sys/callout.h> +#include <sys/malloc.h> +#include <sys/priv.h> +#include <sys/conf.h> +#include <sys/fcntl.h> +#include <sys/sbuf.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usbhid.h> +#include "usbdevs.h" + +#define USB_DEBUG_VAR utouch_debug +#include <dev/usb/usb_debug.h> + +#include <dev/usb/quirk/usb_quirk.h> + +#include <dev/evdev/input.h> +#include <dev/evdev/evdev.h> + +#include <sys/ioccom.h> +#include <sys/filio.h> +#include <sys/tty.h> + +enum { + UTOUCH_INTR_DT, + UTOUCH_N_TRANSFER, +}; + +struct utouch_softc +{ + device_t sc_dev; + struct evdev_dev *sc_evdev; + struct mtx sc_mtx; + struct usb_xfer *sc_xfer[UTOUCH_N_TRANSFER]; + struct hid_location sc_loc_x; + struct hid_location sc_loc_y; + struct hid_location sc_loc_z; +#define UTOUCH_BUTTON_MAX 8 + struct hid_location sc_loc_btn[UTOUCH_BUTTON_MAX]; + uint8_t sc_iid; + uint8_t sc_iid_x; + uint8_t sc_iid_y; + uint8_t sc_iid_z; + uint8_t sc_iid_btn[UTOUCH_BUTTON_MAX]; + uint8_t sc_nbuttons; + uint32_t sc_flags; +#define UTOUCH_FLAG_X_AXIS 0x0001 +#define UTOUCH_FLAG_Y_AXIS 0x0002 +#define UTOUCH_FLAG_Z_AXIS 0x0004 +#define UTOUCH_FLAG_OPENED 0x0008 + + + int sc_oldx; + int sc_oldy; + int sc_oldz; + int sc_oldbuttons; + + uint8_t sc_temp[64]; +}; + + + +static usb_callback_t utouch_intr_callback; + +static device_probe_t utouch_probe; +static device_attach_t utouch_attach; +static device_detach_t utouch_detach; + +static evdev_open_t utouch_ev_open; +static evdev_close_t utouch_ev_close; + +static int utouch_hid_test(void *, uint16_t); +static void utouch_hid_parse(struct utouch_softc *, const void *, uint16_t); +static void utouch_report_event(struct utouch_softc *, int, int, int, int); + +static struct evdev_methods utouch_evdev_methods = { + .ev_open = &utouch_ev_open, + .ev_close = &utouch_ev_close, +}; + +static const struct usb_config utouch_config[UTOUCH_N_TRANSFER] = { + + [UTOUCH_INTR_DT] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .bufsize = 0, /* use wMaxPacketSize */ + .callback = &utouch_intr_callback, + }, +}; + +static int +utouch_probe(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + void *d_ptr; + uint16_t d_len; + int err; + + device_printf(dev, "utouch_probe()\n"); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + + if (uaa->info.bInterfaceClass != UICLASS_HID) + return (ENXIO); + + err = usbd_req_get_hid_desc(uaa->device, NULL, + &d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex); + + if (err) + return (ENXIO); + + if (utouch_hid_test(d_ptr, d_len)) + err = BUS_PROBE_DEFAULT; + else + err = ENXIO; + + device_printf(dev, "probe: err = %d\n", err); + + free(d_ptr, M_TEMP); + return (err); +} + +static int +utouch_attach(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct utouch_softc *sc = device_get_softc(dev); + struct input_absinfo absinfo = { 0 }; + void *d_ptr = NULL; + uint16_t d_len; + int isize, i, err; + + device_printf(dev, "utouch_attach()\n"); + + device_set_usb_desc(dev); + sc->sc_dev = dev; + + mtx_init(&sc->sc_mtx, "utouch lock", NULL, MTX_DEF | MTX_RECURSE); + + err = usbd_req_set_protocol(uaa->device, NULL, + uaa->info.bIfaceIndex, 1); + + err = usbd_transfer_setup(uaa->device, + &uaa->info.bIfaceIndex, sc->sc_xfer, utouch_config, + UTOUCH_N_TRANSFER, sc, &sc->sc_mtx); + if (err) + goto detach; + + err = usbd_req_get_hid_desc(uaa->device, NULL, &d_ptr, + &d_len, M_TEMP, uaa->info.bIfaceIndex); + if (err) + goto detach; + + isize = hid_report_size(d_ptr, d_len, hid_input, &sc->sc_iid); + + utouch_hid_parse(sc, d_ptr, d_len); + + sc->sc_evdev = evdev_alloc(); + evdev_set_name(sc->sc_evdev, device_get_desc(dev)); + evdev_set_serial(sc->sc_evdev, "0"); + evdev_set_softc(sc->sc_evdev, sc); + evdev_set_methods(sc->sc_evdev, &utouch_evdev_methods); + evdev_support_event(sc->sc_evdev, EV_SYN); + evdev_support_event(sc->sc_evdev, EV_ABS); + evdev_support_event(sc->sc_evdev, EV_REL); + evdev_support_event(sc->sc_evdev, EV_KEY); + + if (sc->sc_flags & UTOUCH_FLAG_X_AXIS) + evdev_support_abs(sc->sc_evdev, ABS_X); + + if (sc->sc_flags & UTOUCH_FLAG_Y_AXIS) + evdev_support_abs(sc->sc_evdev, ABS_Y); + + if (sc->sc_flags & UTOUCH_FLAG_Z_AXIS) + evdev_support_rel(sc->sc_evdev, REL_Z); + + for (i = 0; i < sc->sc_nbuttons; i++) + evdev_support_key(sc->sc_evdev, BTN_MOUSE + i); + + + /* Report absolute axes information */ + absinfo.minimum = 0; + absinfo.maximum = 0x7fff; /* XXX should read from HID descriptor */ + evdev_set_absinfo(sc->sc_evdev, ABS_X, &absinfo); + evdev_set_absinfo(sc->sc_evdev, ABS_Y, &absinfo); + + err = evdev_register(dev, sc->sc_evdev); + if (err) + goto detach; + + return (0); + +detach: + if (d_ptr) + free(d_ptr, M_TEMP); + + utouch_detach(dev); + return (ENXIO); +} + +static int +utouch_detach(device_t dev) +{ + return (0); +} + +static void +utouch_intr_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct utouch_softc *sc = usbd_xfer_softc(xfer); + struct usb_page_cache *pc; + void *buf = sc->sc_temp; + int x, y, dz, buttons = 0, changed = 0; + int len, i; + usbd_xfer_status(xfer, &len, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + if (len == 0) + goto tr_setup; + + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_out(pc, 0, buf, len); + + if (sc->sc_flags & UTOUCH_FLAG_X_AXIS) { + x = hid_get_data(buf, len, &sc->sc_loc_x); + changed += x != sc->sc_oldx; + } + + if (sc->sc_flags & UTOUCH_FLAG_Y_AXIS) { + y = hid_get_data(buf, len, &sc->sc_loc_y); + changed += y != sc->sc_oldy; + } + + if (sc->sc_flags & UTOUCH_FLAG_Z_AXIS) { + dz = hid_get_data(buf, len, &sc->sc_loc_z); + changed += dz != 0; + } + + for (i = 0; i < sc->sc_nbuttons; i++) { + if (hid_get_data(buf, len, &sc->sc_loc_btn[i])) + buttons |= (1 << i); + + changed += buttons != 0; + } + + if (changed) { + utouch_report_event(sc, x, y, dz, buttons); + sc->sc_oldx = x; + sc->sc_oldy = y; + sc->sc_oldbuttons = buttons; + } + + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + break; + default: + if (error != USB_ERR_CANCELLED) { + /* try clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + break; + } +} + +static void +utouch_ev_close(struct evdev_dev *evdev, void *ev_softc) +{ + struct utouch_softc *sc = (struct utouch_softc *)ev_softc; + + mtx_lock(&sc->sc_mtx); + usbd_transfer_stop(sc->sc_xfer[UTOUCH_INTR_DT]); + mtx_unlock(&sc->sc_mtx); +} + +static int +utouch_ev_open(struct evdev_dev *evdev, void *ev_softc) +{ + struct utouch_softc *sc = (struct utouch_softc *)ev_softc; + + mtx_lock(&sc->sc_mtx); + + usbd_transfer_stop(sc->sc_xfer[UTOUCH_INTR_DT]); + usbd_xfer_set_interval(sc->sc_xfer[UTOUCH_INTR_DT], 100); + usbd_transfer_start(sc->sc_xfer[UTOUCH_INTR_DT]); + + mtx_unlock(&sc->sc_mtx); + + return (0); +} + +static void +utouch_report_event(struct utouch_softc *sc, int x, int y, int dz, int buttons) +{ + int i; + + if (sc->sc_flags & UTOUCH_FLAG_X_AXIS) + evdev_push_event(sc->sc_evdev, EV_ABS, ABS_X, x); + + if (sc->sc_flags & UTOUCH_FLAG_Y_AXIS) + evdev_push_event(sc->sc_evdev, EV_ABS, ABS_Y, y); + + if (sc->sc_flags & UTOUCH_FLAG_Z_AXIS) + evdev_push_event(sc->sc_evdev, EV_REL, REL_Z, dz); + + for (i = 0; i < sc->sc_nbuttons; i++) { + if (((buttons & (1 << i)) ^ + (sc->sc_oldbuttons & (1 << i))) == 0) + continue; + + evdev_push_event(sc->sc_evdev, EV_KEY, + BTN_MOUSE + i, !!(buttons & (1 << i))); + } + + evdev_sync(sc->sc_evdev); + +} + +static int +utouch_hid_test(void *d_ptr, uint16_t d_len) +{ + struct hid_data *hd; + struct hid_item hi; + int mdepth; + int found; + + hd = hid_start_parse(d_ptr, d_len, 1 << hid_input); + if (hd == NULL) + return (0); + + mdepth = 0; + found = 0; + + while (hid_get_item(hd, &hi)) { + switch (hi.kind) { + case hid_collection: + if (mdepth != 0) + mdepth++; + else if (hi.collection == 1 && + hi.usage == + HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_POINTER)) + mdepth++; + break; + case hid_endcollection: + if (mdepth != 0) + mdepth--; + break; + case hid_input: + if (mdepth == 0) + break; + if (hi.usage == + HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X) && + (hi.flags & (HIO_CONST|HIO_VARIABLE|HIO_RELATIVE)) == HIO_VARIABLE) + found++; + if (hi.usage == + HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y) && + (hi.flags & (HIO_CONST|HIO_VARIABLE|HIO_RELATIVE)) == HIO_VARIABLE) + found++; + break; + default: + break; + } + } + hid_end_parse(hd); + return (found); +} + +static void +utouch_hid_parse(struct utouch_softc *sc, const void *buf, uint16_t len) +{ + uint32_t flags; + uint8_t i; + + if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), + hid_input, 0, &sc->sc_loc_x, &flags, &sc->sc_iid_x)) { + + if (flags & HIO_VARIABLE) { + sc->sc_flags |= UTOUCH_FLAG_X_AXIS; + } + } + if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), + hid_input, 0, &sc->sc_loc_y, &flags, &sc->sc_iid_y)) { + + if (flags & HIO_VARIABLE) { + sc->sc_flags |= UTOUCH_FLAG_Y_AXIS; + } + } + /* Try the wheel first as the Z activator since it's tradition. */ + if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, + HUG_WHEEL), hid_input, 0, &sc->sc_loc_z, &flags, + &sc->sc_iid_z) || + hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, + HUG_TWHEEL), hid_input, 0, &sc->sc_loc_z, &flags, + &sc->sc_iid_z)) { + if (flags & HIO_VARIABLE) + sc->sc_flags |= UTOUCH_FLAG_Z_AXIS; + } + + /* figure out the number of buttons */ + for (i = 0; i < UTOUCH_BUTTON_MAX; i++) { + if (!hid_locate(buf, len, HID_USAGE2(HUP_BUTTON, (i + 1)), + hid_input, 0, &sc->sc_loc_btn[i], NULL, + &sc->sc_iid_btn[i])) { + break; + } + } + + sc->sc_nbuttons = i; + + if (i > sc->sc_nbuttons) + sc->sc_nbuttons = i; + + if (sc->sc_flags == 0) + return; + + /* announce information about the mouse */ + device_printf(sc->sc_dev, "%d buttons and [%s%s%s] axes\n", + (sc->sc_nbuttons), + (sc->sc_flags & UTOUCH_FLAG_X_AXIS) ? "X" : "", + (sc->sc_flags & UTOUCH_FLAG_Y_AXIS) ? "Y" : "", + (sc->sc_flags & UTOUCH_FLAG_Z_AXIS) ? "Z" : ""); +} + +static devclass_t utouch_devclass; + +static device_method_t utouch_methods[] = { + DEVMETHOD(device_probe, utouch_probe), + DEVMETHOD(device_attach, utouch_attach), + DEVMETHOD(device_detach, utouch_detach), + + DEVMETHOD_END +}; + +static driver_t utouch_driver = { + .name = "utouch", + .methods = utouch_methods, + .size = sizeof(struct utouch_softc), +}; + +DRIVER_MODULE(utouch, uhub, utouch_driver, utouch_devclass, NULL, 0); +MODULE_DEPEND(utouch, usb, 1, 1, 1); +MODULE_VERSION(utouch, 1);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201406032027.s53KR8Mo067067>