diff options
author | Alexandr Shadchin <shadchin@cvs.openbsd.org> | 2011-04-09 08:38:43 +0000 |
---|---|---|
committer | Alexandr Shadchin <shadchin@cvs.openbsd.org> | 2011-04-09 08:38:43 +0000 |
commit | d7e280ac3a05f25fa5b098f203aa40e92d040a5d (patch) | |
tree | e716a94142e8509677a0147ad6242d5072351dc8 /driver/xf86-input-synaptics/src/eventcomm.c | |
parent | b9f5f7ebfd9cf7882a73f6072406855aae88cc9e (diff) |
Import xf86-input-synaptics 1.4.0
ok matthieu@
Diffstat (limited to 'driver/xf86-input-synaptics/src/eventcomm.c')
-rw-r--r-- | driver/xf86-input-synaptics/src/eventcomm.c | 499 |
1 files changed, 499 insertions, 0 deletions
diff --git a/driver/xf86-input-synaptics/src/eventcomm.c b/driver/xf86-input-synaptics/src/eventcomm.c new file mode 100644 index 000000000..1a31c544f --- /dev/null +++ b/driver/xf86-input-synaptics/src/eventcomm.c @@ -0,0 +1,499 @@ +/* + * Copyright © 2004-2007 Peter Osterlund + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of Red Hat + * not be used in advertising or publicity pertaining to distribution + * of the software without specific, written prior permission. Red + * Hat makes no representations about the suitability of this software + * for any purpose. It is provided "as is" without express or implied + * warranty. + * + * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, 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. + * + * Authors: + * Peter Osterlund (petero2@telia.com) + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <xorg-server.h> +#include "eventcomm.h" +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <dirent.h> +#include <string.h> +#include <stdio.h> +#include "synproto.h" +#include "synaptics.h" +#include "synapticsstr.h" +#include <xf86.h> + + +#define SYSCALL(call) while (((call) == -1) && (errno == EINTR)) + +#define LONG_BITS (sizeof(long) * 8) +#define NBITS(x) (((x) + LONG_BITS - 1) / LONG_BITS) +#define OFF(x) ((x) % LONG_BITS) +#define LONG(x) ((x) / LONG_BITS) +#define TEST_BIT(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1) + +/***************************************************************************** + * Function Definitions + ****************************************************************************/ + +static void +EventDeviceOnHook(InputInfoPtr pInfo, SynapticsParameters *para) +{ + SynapticsPrivate *priv = (SynapticsPrivate *)pInfo->private; + BOOL *need_grab; + + if (!priv->proto_data) + priv->proto_data = calloc(1, sizeof(BOOL)); + + need_grab = (BOOL*)priv->proto_data; + + if (para->grab_event_device) { + /* Try to grab the event device so that data don't leak to /dev/input/mice */ + int ret; + SYSCALL(ret = ioctl(pInfo->fd, EVIOCGRAB, (pointer)1)); + if (ret < 0) { + xf86Msg(X_WARNING, "%s can't grab event device, errno=%d\n", + pInfo->name, errno); + } + } + + *need_grab = FALSE; +} + +static Bool +event_query_is_touchpad(int fd, BOOL grab) +{ + int ret = FALSE, rc; + unsigned long evbits[NBITS(EV_MAX)] = {0}; + unsigned long absbits[NBITS(ABS_MAX)] = {0}; + unsigned long keybits[NBITS(KEY_MAX)] = {0}; + + if (grab) + { + SYSCALL(rc = ioctl(fd, EVIOCGRAB, (pointer)1)); + if (rc < 0) + return FALSE; + } + + /* Check for ABS_X, ABS_Y, ABS_PRESSURE and BTN_TOOL_FINGER */ + + SYSCALL(rc = ioctl(fd, EVIOCGBIT(0, sizeof(evbits)), evbits)); + if (rc < 0) + goto unwind; + if (!TEST_BIT(EV_SYN, evbits) || + !TEST_BIT(EV_ABS, evbits) || + !TEST_BIT(EV_KEY, evbits)) + goto unwind; + + SYSCALL(rc = ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbits)), absbits)); + if (rc < 0) + goto unwind; + if (!TEST_BIT(ABS_X, absbits) || + !TEST_BIT(ABS_Y, absbits)) + goto unwind; + + SYSCALL(rc = ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybits)), keybits)); + if (rc < 0) + goto unwind; + + /* we expect touchpad either report raw pressure or touches */ + if (!TEST_BIT(ABS_PRESSURE, absbits) && !TEST_BIT(BTN_TOUCH, keybits)) + goto unwind; + /* all Synaptics-like touchpad report BTN_TOOL_FINGER */ + if (!TEST_BIT(BTN_TOOL_FINGER, keybits)) + goto unwind; + if (TEST_BIT(BTN_TOOL_PEN, keybits)) + goto unwind; /* Don't match wacom tablets */ + + ret = TRUE; + +unwind: + if (grab) + SYSCALL(ioctl(fd, EVIOCGRAB, (pointer)0)); + + return (ret == TRUE); +} + +typedef struct { + short vendor; + short product; + enum TouchpadModel model; +} model_lookup_t; +#define PRODUCT_ANY 0x0000 + +static model_lookup_t model_lookup_table[] = { + {0x0002, 0x0007, MODEL_SYNAPTICS}, + {0x0002, 0x0008, MODEL_ALPS}, + {0x05ac, PRODUCT_ANY, MODEL_APPLETOUCH}, + {0x0, 0x0, 0x0} +}; + +static void +event_query_info(InputInfoPtr pInfo) +{ + SynapticsPrivate *priv = (SynapticsPrivate *)pInfo->private; + short id[4]; + int rc; + model_lookup_t *model_lookup; + + SYSCALL(rc = ioctl(pInfo->fd, EVIOCGID, id)); + if (rc < 0) + return; + + for(model_lookup = model_lookup_table; model_lookup->vendor; model_lookup++) { + if(model_lookup->vendor == id[ID_VENDOR] && + (model_lookup->product == id[ID_PRODUCT] || model_lookup->product == PRODUCT_ANY)) + priv->model = model_lookup->model; + } +} + +/* Query device for axis ranges */ +static void +event_query_axis_ranges(InputInfoPtr pInfo) +{ + SynapticsPrivate *priv = (SynapticsPrivate *)pInfo->private; + struct input_absinfo abs = {0}; + unsigned long absbits[NBITS(ABS_MAX)] = {0}; + unsigned long keybits[NBITS(KEY_MAX)] = {0}; + char buf[256]; + int rc; + + SYSCALL(rc = ioctl(pInfo->fd, EVIOCGABS(ABS_X), &abs)); + if (rc >= 0) + { + xf86Msg(X_PROBED, "%s: x-axis range %d - %d\n", pInfo->name, + abs.minimum, abs.maximum); + priv->minx = abs.minimum; + priv->maxx = abs.maximum; + /* The kernel's fuzziness concept seems a bit weird, but it can more or + * less be applied as hysteresis directly, i.e. no factor here. Though, + * we don't trust a zero fuzz as it probably is just a lazy value. */ + if (abs.fuzz > 0) + priv->synpara.hyst_x = abs.fuzz; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30) + priv->resx = abs.resolution; +#endif + } else + xf86Msg(X_ERROR, "%s: failed to query axis range (%s)\n", pInfo->name, + strerror(errno)); + + SYSCALL(rc = ioctl(pInfo->fd, EVIOCGABS(ABS_Y), &abs)); + if (rc >= 0) + { + xf86Msg(X_PROBED, "%s: y-axis range %d - %d\n", pInfo->name, + abs.minimum, abs.maximum); + priv->miny = abs.minimum; + priv->maxy = abs.maximum; + /* don't trust a zero fuzz */ + if (abs.fuzz > 0) + priv->synpara.hyst_y = abs.fuzz; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30) + priv->resy = abs.resolution; +#endif + } else + xf86Msg(X_ERROR, "%s: failed to query axis range (%s)\n", pInfo->name, + strerror(errno)); + + priv->has_pressure = FALSE; + priv->has_width = FALSE; + SYSCALL(rc = ioctl(pInfo->fd, EVIOCGBIT(EV_ABS, sizeof(absbits)), absbits)); + if (rc >= 0) + { + priv->has_pressure = (TEST_BIT(ABS_PRESSURE, absbits) != 0); + priv->has_width = (TEST_BIT(ABS_TOOL_WIDTH, absbits) != 0); + } + else + xf86Msg(X_ERROR, "%s: failed to query ABS bits (%s)\n", pInfo->name, + strerror(errno)); + + if (priv->has_pressure) + { + SYSCALL(rc = ioctl(pInfo->fd, EVIOCGABS(ABS_PRESSURE), &abs)); + if (rc >= 0) + { + xf86Msg(X_PROBED, "%s: pressure range %d - %d\n", pInfo->name, + abs.minimum, abs.maximum); + priv->minp = abs.minimum; + priv->maxp = abs.maximum; + } + } else + xf86Msg(X_INFO, + "%s: device does not report pressure, will use touch data.\n", + pInfo->name); + + if (priv->has_width) + { + SYSCALL(rc = ioctl(pInfo->fd, EVIOCGABS(ABS_TOOL_WIDTH), &abs)); + if (rc >= 0) + { + xf86Msg(X_PROBED, "%s: finger width range %d - %d\n", pInfo->name, + abs.minimum, abs.maximum); + priv->minw = abs.minimum; + priv->maxw = abs.maximum; + } + } + + SYSCALL(rc = ioctl(pInfo->fd, EVIOCGBIT(EV_KEY, sizeof(keybits)), keybits)); + if (rc >= 0) + { + buf[0] = 0; + if ((priv->has_left = (TEST_BIT(BTN_LEFT, keybits) != 0))) + strcat(buf, " left"); + if ((priv->has_right = (TEST_BIT(BTN_RIGHT, keybits) != 0))) + strcat(buf, " right"); + if ((priv->has_middle = (TEST_BIT(BTN_MIDDLE, keybits) != 0))) + strcat(buf, " middle"); + if ((priv->has_double = (TEST_BIT(BTN_TOOL_DOUBLETAP, keybits) != 0))) + strcat(buf, " double"); + if ((priv->has_triple = (TEST_BIT(BTN_TOOL_TRIPLETAP, keybits) != 0))) + strcat(buf, " triple"); + + if ((TEST_BIT(BTN_0, keybits) != 0) || + (TEST_BIT(BTN_1, keybits) != 0) || + (TEST_BIT(BTN_2, keybits) != 0) || + (TEST_BIT(BTN_3, keybits) != 0)) + { + priv->has_scrollbuttons = 1; + strcat(buf, " scroll-buttons"); + } + + xf86Msg(X_PROBED, "%s: buttons:%s\n", pInfo->name, buf); + } +} + +static Bool +EventQueryHardware(InputInfoPtr pInfo) +{ + SynapticsPrivate *priv = (SynapticsPrivate *)pInfo->private; + BOOL *need_grab = (BOOL*)priv->proto_data; + + if (!event_query_is_touchpad(pInfo->fd, (need_grab) ? *need_grab : TRUE)) + return FALSE; + + xf86Msg(X_PROBED, "%s: touchpad found\n", pInfo->name); + + return TRUE; +} + +static Bool +SynapticsReadEvent(InputInfoPtr pInfo, struct input_event *ev) +{ + int rc = TRUE; + ssize_t len; + + len = read(pInfo->fd, ev, sizeof(*ev)); + if (len <= 0) + { + /* We use X_NONE here because it doesn't alloc */ + if (errno != EAGAIN) + xf86MsgVerb(X_NONE, 0, "%s: Read error %s\n", pInfo->name, strerror(errno)); + rc = FALSE; + } else if (len % sizeof(*ev)) { + xf86MsgVerb(X_NONE, 0, "%s: Read error, invalid number of bytes.", pInfo->name); + rc = FALSE; + } + return rc; +} + +static Bool +EventReadHwState(InputInfoPtr pInfo, + struct SynapticsProtocolOperations *proto_ops, + struct CommData *comm, struct SynapticsHwState *hwRet) +{ + struct input_event ev; + Bool v; + struct SynapticsHwState *hw = &(comm->hwState); + SynapticsPrivate *priv = (SynapticsPrivate *)pInfo->private; + SynapticsParameters *para = &priv->synpara; + + while (SynapticsReadEvent(pInfo, &ev)) { + switch (ev.type) { + case EV_SYN: + switch (ev.code) { + case SYN_REPORT: + if (comm->oneFinger) + hw->numFingers = 1; + else if (comm->twoFingers) + hw->numFingers = 2; + else if (comm->threeFingers) + hw->numFingers = 3; + else + hw->numFingers = 0; + *hwRet = *hw; + return TRUE; + } + case EV_KEY: + v = (ev.value ? TRUE : FALSE); + switch (ev.code) { + case BTN_LEFT: + hw->left = v; + break; + case BTN_RIGHT: + hw->right = v; + break; + case BTN_MIDDLE: + hw->middle = v; + break; + case BTN_FORWARD: + hw->up = v; + break; + case BTN_BACK: + hw->down = v; + break; + case BTN_0: + hw->multi[0] = v; + break; + case BTN_1: + hw->multi[1] = v; + break; + case BTN_2: + hw->multi[2] = v; + break; + case BTN_3: + hw->multi[3] = v; + break; + case BTN_4: + hw->multi[4] = v; + break; + case BTN_5: + hw->multi[5] = v; + break; + case BTN_6: + hw->multi[6] = v; + break; + case BTN_7: + hw->multi[7] = v; + break; + case BTN_TOOL_FINGER: + comm->oneFinger = v; + break; + case BTN_TOOL_DOUBLETAP: + comm->twoFingers = v; + break; + case BTN_TOOL_TRIPLETAP: + comm->threeFingers = v; + break; + case BTN_TOUCH: + if (!priv->has_pressure) + hw->z = v ? para->finger_high + 1 : 0; + break; + } + break; + case EV_ABS: + switch (ev.code) { + case ABS_X: + hw->x = ev.value; + break; + case ABS_Y: + hw->y = ev.value; + break; + case ABS_PRESSURE: + hw->z = ev.value; + break; + case ABS_TOOL_WIDTH: + hw->fingerWidth = ev.value; + break; + } + break; + } + } + return FALSE; +} + +/* filter for the AutoDevProbe scandir on /dev/input */ +static int EventDevOnly(const struct dirent *dir) { + return strncmp(EVENT_DEV_NAME, dir->d_name, 5) == 0; +} + +/** + * Probe the open device for dimensions. + */ +static void +EventReadDevDimensions(InputInfoPtr pInfo) +{ + SynapticsPrivate *priv = (SynapticsPrivate *)pInfo->private; + BOOL *need_grab = (BOOL*)priv->proto_data; + + if (event_query_is_touchpad(pInfo->fd, (need_grab) ? *need_grab : TRUE)) + event_query_axis_ranges(pInfo); + event_query_info(pInfo); +} + +static Bool +EventAutoDevProbe(InputInfoPtr pInfo) +{ + /* We are trying to find the right eventX device or fall back to + the psaux protocol and the given device from XF86Config */ + int i; + Bool touchpad_found = FALSE; + struct dirent **namelist; + + i = scandir(DEV_INPUT_EVENT, &namelist, EventDevOnly, alphasort); + if (i < 0) { + xf86Msg(X_ERROR, "Couldn't open %s\n", DEV_INPUT_EVENT); + return FALSE; + } + else if (i == 0) { + xf86Msg(X_ERROR, "%s The /dev/input/event* device nodes seem to be missing\n", + pInfo->name); + free(namelist); + return FALSE; + } + + while (i--) { + char fname[64]; + int fd = -1; + + if (!touchpad_found) { + sprintf(fname, "%s/%s", DEV_INPUT_EVENT, namelist[i]->d_name); + SYSCALL(fd = open(fname, O_RDONLY)); + if (fd < 0) + continue; + + if (event_query_is_touchpad(fd, TRUE)) { + touchpad_found = TRUE; + xf86Msg(X_PROBED, "%s auto-dev sets device to %s\n", + pInfo->name, fname); + pInfo->options = + xf86ReplaceStrOption(pInfo->options, "Device", fname); + } + SYSCALL(close(fd)); + } + free(namelist[i]); + } + free(namelist); + + if (!touchpad_found) { + xf86Msg(X_ERROR, "%s no synaptics event device found\n", pInfo->name); + return FALSE; + } + return TRUE; +} + +struct SynapticsProtocolOperations event_proto_operations = { + EventDeviceOnHook, + NULL, + EventQueryHardware, + EventReadHwState, + EventAutoDevProbe, + EventReadDevDimensions +}; |