/* $OpenBSD: uhts.c,v 1.1 2009/12/09 21:27:19 matthieu Exp $ */ /* * Copyright (c) 2009 Matthieu Herrb * Copyright (c) 2007 Robert Nagy * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USB_DEBUG #define DPRINTF(x) do { if (uhtsdebug) printf x; } while (0) #define DPRINTFN(n,x) do { if (uhtsdebug>(n)) printf x; } while (0) int uhtsdebug = 0; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif #define UHTSUNIT(s) (minor(s)) struct tsscale { int minx, maxx; int miny, maxy; int swapxy; int resx, resy; }; struct uhts_softc { struct uhidev sc_hdev; struct hid_location sc_loc_x, sc_loc_y; struct hid_location sc_loc_btn; int sc_enabled; u_int32_t sc_buttons; /* mouse button status */ int sc_rawmode; int sc_oldx, sc_oldy; struct tsscale sc_tsscale; struct device *sc_wsmousedev; char sc_dying; }; struct uhts_pos { int down; int x, y; int z; /* touch pressure */ }; void uhts_intr(struct uhidev *, void *, u_int); int uhts_enable(void *); void uhts_disable(void *); int uhts_ioctl(void *, u_long, caddr_t, int, struct proc *); const struct wsmouse_accessops uhts_accessops = { uhts_enable, uhts_ioctl, uhts_disable }; int uhts_match(struct device *, void *, void *); void uhts_attach(struct device *, struct device *, void *); int uhts_detach(struct device *, int); int uhts_activate(struct device *, int); void uhts_parse_desc(struct uhts_softc *); struct cfdriver uhts_cd = { NULL, "uhts", DV_DULL }; const struct cfattach uhts_ca = { sizeof(struct uhts_softc), uhts_match, uhts_attach, uhts_detach, uhts_activate }; int uhts_match(struct device *parent, void *match, void *aux) { struct usb_attach_arg *uaa = aux; struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa; int size; void *desc; uhidev_get_report_desc(uha->parent, &desc, &size); if (!hid_is_collection(desc, size, uha->reportid, HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCHSCREEN))) return (UMATCH_NONE); return (UMATCH_IFACECLASS); } void uhts_parse_desc(struct uhts_softc *sc) { struct hid_data *d; struct hid_item h; int size; void *desc; uhidev_get_report_desc(sc->sc_hdev.sc_parent, &desc, &size); d = hid_start_parse(desc, size, hid_input); while (hid_get_item(d, &h)) { if (h.kind != hid_input || HID_GET_USAGE_PAGE(h.usage) != HUP_GENERIC_DESKTOP || h.report_ID != sc->sc_hdev.sc_report_id) continue; DPRINTF(("uhts: usage=0x%x range %d..%d\n", h.usage, h.logical_minimum, h.logical_maximum)); switch (HID_GET_USAGE(h.usage)) { case HUG_X: sc->sc_tsscale.minx = h.logical_minimum; sc->sc_tsscale.maxx = h.logical_maximum; break; case HUG_Y: sc->sc_tsscale.miny = h.logical_minimum; sc->sc_tsscale.maxy = h.logical_maximum; break; } } } void uhts_attach(struct device *parent, struct device *self, void *aux) { struct uhts_softc *sc = (struct uhts_softc *)self; struct usb_attach_arg *uaa = aux; struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa; struct wsmousedev_attach_args a; void *desc; int size; u_int32_t flags; sc->sc_hdev.sc_intr = uhts_intr; sc->sc_hdev.sc_parent = uha->parent; sc->sc_hdev.sc_report_id = uha->reportid; uhidev_get_report_desc(uha->parent, &desc, &size); if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), uha->reportid, hid_input, &sc->sc_loc_x, &flags)) { printf("\n%s: touchscreen has no X report\n", sc->sc_hdev.sc_dev.dv_xname); return; } if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), uha->reportid, hid_input, &sc->sc_loc_y, &flags)) { printf("\n%s: touchscreen has no Y report\n", sc->sc_hdev.sc_dev.dv_xname); return; } if (!hid_locate(desc, size, HID_USAGE2(HUP_DIGITIZERS, HUD_TIP_SWITCH), uha->reportid, hid_input, &sc->sc_loc_btn, 0)){ printf("\n%s: touch screen has no button\n", sc->sc_hdev.sc_dev.dv_xname); return; } printf("\n"); a.accessops = &uhts_accessops; a.accesscookie = sc; uhts_parse_desc(sc); sc->sc_rawmode = 0; /* wild guess */ sc->sc_tsscale.swapxy = 0; sc->sc_tsscale.resx = 1024; sc->sc_tsscale.resy = 768; sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint); } int uhts_activate(struct device *self, int act) { struct uhts_softc *sc = (struct uhts_softc *)self; int rv = 0; switch (act) { case DVACT_ACTIVATE: break; case DVACT_DEACTIVATE: if (sc->sc_wsmousedev != NULL) rv = config_deactivate(sc->sc_wsmousedev); sc->sc_dying = 1; break; } return (rv); } int uhts_detach(struct device *self, int flags) { struct uhts_softc *sc = (struct uhts_softc *)self; int rv = 0; DPRINTF(("uhts_detach: sc=%p flags=%d\n", sc, flags)); /* wsmouse takes care of reference counting */ if (sc->sc_wsmousedev != NULL) rv = config_detach(sc->sc_wsmousedev, flags); return (rv); } void uhts_intr(struct uhidev *addr, void *buf, u_int len) { struct uhts_softc *sc = (struct uhts_softc *)addr; u_char *ibuf = (u_char *)buf; struct uhts_pos tp; int x, y, s; u_int32_t buttons = 0; DPRINTFN(5, ("uhts_intr: len=%d\n", len)); x = hid_get_data(ibuf, &sc->sc_loc_x); y = hid_get_data(ibuf, &sc->sc_loc_y); if (hid_get_data(ibuf, &sc->sc_loc_btn)) buttons = 1; DPRINTFN(10, ("uhts_intr: x:%d y:%d buttons:0x%x\n", x, y, buttons)); if (buttons) { if (sc->sc_tsscale.swapxy && !sc->sc_rawmode) { /* Swap X/Y-Axis */ tp.y = x; tp.x = y; } else { tp.x = x; tp.y = y; } if (!sc->sc_rawmode) { /* Scale down to the screen resolution. */ tp.x = ((tp.x - sc->sc_tsscale.minx) * sc->sc_tsscale.resx) / (sc->sc_tsscale.maxx - sc->sc_tsscale.minx); tp.y = ((tp.y - sc->sc_tsscale.miny) * sc->sc_tsscale.resy) / (sc->sc_tsscale.maxy - sc->sc_tsscale.miny); } } else { tp.x = sc->sc_oldx; tp.y = sc->sc_oldy; } sc->sc_buttons = buttons; tp.z = buttons; if (sc->sc_wsmousedev != NULL) { s = spltty(); wsmouse_input(sc->sc_wsmousedev, buttons, tp.x, tp.y, tp.z, 0, WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y | WSMOUSE_INPUT_ABSOLUTE_Z); sc->sc_oldx = tp.x; sc->sc_oldy = tp.y; splx(s); } } int uhts_enable(void *v) { struct uhts_softc *sc = v; DPRINTFN(1, ("uhts_enable: sc=%p\n", sc)); if (sc->sc_dying) return (EIO); if (sc->sc_enabled) return (EBUSY); sc->sc_enabled = 1; sc->sc_buttons = 0; return (uhidev_open(&sc->sc_hdev)); } void uhts_disable(void *v) { struct uhts_softc *sc = v; DPRINTFN(1, ("uhts_disable: sc=%p\n", sc)); #ifdef DIAGNOSTIC if (!sc->sc_enabled) { printf("uhts_disable: not enabled\n"); return; } #endif sc->sc_enabled = 0; uhidev_close(&sc->sc_hdev); } int uhts_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) { struct uhts_softc *sc = v; struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data; int error = 0; DPRINTF(("uhts_ioctl(%d, '%c', %d)\n", IOCPARM_LEN(cmd), IOCGROUP(cmd), cmd & 0xff)); switch (cmd) { case WSMOUSEIO_GTYPE: *(u_int *)data = WSMOUSE_TYPE_TPANEL; return (0); case WSMOUSEIO_SCALIBCOORDS: if (!(wsmc->minx >= 0 && wsmc->maxx >= 0 && wsmc->miny >= 0 && wsmc->maxy >= 0 && wsmc->resx >= 0 && wsmc->resy >= 0 && wsmc->minx < 32768 && wsmc->maxx < 32768 && wsmc->miny < 32768 && wsmc->maxy < 32768 && wsmc->resx < 32768 && wsmc->resy < 32768 && wsmc->swapxy >= 0 && wsmc->swapxy <= 1 && wsmc->samplelen >= 0 && wsmc->samplelen <= 1)) return (EINVAL); sc->sc_tsscale.minx = wsmc->minx; sc->sc_tsscale.maxx = wsmc->maxx; sc->sc_tsscale.miny = wsmc->miny; sc->sc_tsscale.maxy = wsmc->maxy; sc->sc_tsscale.swapxy = wsmc->swapxy; sc->sc_tsscale.resx = wsmc->resx; sc->sc_tsscale.resy = wsmc->resy; sc->sc_rawmode = wsmc->samplelen; break; case WSMOUSEIO_GCALIBCOORDS: wsmc->minx = sc->sc_tsscale.minx; wsmc->maxx = sc->sc_tsscale.maxx; wsmc->miny = sc->sc_tsscale.miny; wsmc->maxy = sc->sc_tsscale.maxy; wsmc->swapxy = sc->sc_tsscale.swapxy; wsmc->resx = sc->sc_tsscale.resx; wsmc->resy = sc->sc_tsscale.resy; wsmc->samplelen = sc->sc_rawmode; break; default: error = ENOTTY; break; } return error; }