diff options
Diffstat (limited to 'src/wsconscomm.c')
-rw-r--r-- | src/wsconscomm.c | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/src/wsconscomm.c b/src/wsconscomm.c new file mode 100644 index 0000000..0e0c81d --- /dev/null +++ b/src/wsconscomm.c @@ -0,0 +1,318 @@ +/* + * Copyright © 2011 Alexandr Shadchin + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <xorg-server.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <string.h> +#include "synproto.h" +#include "synapticsstr.h" +#include <xf86.h> + +#include <fcntl.h> +#include <dev/wscons/wsconsio.h> + +#ifdef X_PRIVSEP +extern int priv_open_device(const char *); +#endif + +#define DEFAULT_WSMOUSE_DEV "/dev/wsmouse0" + +static Bool +WSConsIsTouchpad(InputInfoPtr pInfo, const char *device) +{ + int wsmouse_type, fd = -1; + Bool rc = FALSE; + + if (device) { +#ifndef X_PRIVSEP + fd = open(device, O_RDWR); +#else + fd = priv_open_device(device); +#endif + } else + fd = pInfo->fd; + + if (fd < 0) + return FALSE; + + if (ioctl(fd, WSMOUSEIO_GTYPE, &wsmouse_type) == -1) { + xf86IDrvMsg(pInfo, X_ERROR, "cannot get mouse type\n"); + goto out; + } + + if (wsmouse_type == WSMOUSE_TYPE_SYNAPTICS || + wsmouse_type == WSMOUSE_TYPE_ALPS || + wsmouse_type == WSMOUSE_TYPE_ELANTECH) + rc = TRUE; + +out: + if (device) + close(fd); + + return rc; +} + +static Bool +WSConsReadEvent(InputInfoPtr pInfo, struct wscons_event *event) +{ + Bool rc = TRUE; + ssize_t len; + + len = read(pInfo->fd, event, sizeof(struct wscons_event)); + if (len <= 0) { + if (errno != EAGAIN) + xf86IDrvMsg(pInfo, X_ERROR, "read error %s\n", strerror(errno)); + rc = FALSE; + } else if (len % sizeof(struct wscons_event)) { + xf86IDrvMsg(pInfo, X_ERROR, "read error, invalid number of bytes\n"); + rc = FALSE; + } + + return rc; +} + +static Bool +WSConsDeviceOnHook(InputInfoPtr pInfo, SynapticsParameters *para) +{ + int wsmouse_mode = WSMOUSE_NATIVE; + + if (ioctl(pInfo->fd, WSMOUSEIO_SETMODE, &wsmouse_mode) == -1) { + xf86IDrvMsg(pInfo, X_ERROR, "cannot set native mode\n"); + return FALSE; + } + + return TRUE; +} + +static Bool +WSConsDeviceOffHook(InputInfoPtr pInfo) +{ + int wsmouse_mode = WSMOUSE_COMPAT; + + if (ioctl(pInfo->fd, WSMOUSEIO_SETMODE, &wsmouse_mode) == -1) { + xf86IDrvMsg(pInfo, X_ERROR, "cannot set compat mode\n"); + return FALSE; + } + + return TRUE; +} + +static Bool +WSConsQueryHardware(InputInfoPtr pInfo) +{ + return WSConsIsTouchpad(pInfo, NULL); +} + +static Bool +WSConsReadHwState(InputInfoPtr pInfo, + struct CommData *comm, struct SynapticsHwState *hwRet) +{ + SynapticsPrivate *priv = (SynapticsPrivate *)pInfo->private; + struct SynapticsHwState *hw = comm->hwState; + struct wscons_event event; + Bool v; + + /* Reset cumulative values if buttons were not previously pressed */ + if (!hw->left && !hw->right && !hw->middle) { + hw->cumulative_dx = hw->x; + hw->cumulative_dy = hw->y; + } + + while (WSConsReadEvent(pInfo, &event)) { + switch (event.type) { + case WSCONS_EVENT_MOUSE_UP: + case WSCONS_EVENT_MOUSE_DOWN: + v = (event.type == WSCONS_EVENT_MOUSE_DOWN) ? TRUE : FALSE; + switch (event.value) { + case 0: + hw->left = v; + break; + case 1: + hw->middle = v; + break; + case 2: + hw->right = v; + break; + case 3: + hw->up = v; + break; + case 4: + hw->down = v; + break; + case 5: + hw->multi[0] = v; + break; + case 6: + hw->multi[1] = v; + break; + case 7: + hw->multi[2] = v; + break; + case 8: + hw->multi[3] = v; + break; + case 9: + hw->multi[4] = v; + break; + case 10: + hw->multi[5] = v; + break; + case 11: + hw->multi[6] = v; + break; + case 12: + hw->multi[7] = v; + break; + } + break; + case WSCONS_EVENT_MOUSE_ABSOLUTE_X: + hw->x = event.value; + break; + case WSCONS_EVENT_MOUSE_ABSOLUTE_Y: + hw->y = priv->maxy - event.value + priv->miny; + break; + case WSCONS_EVENT_MOUSE_ABSOLUTE_Z: + hw->z = event.value; + break; + case WSCONS_EVENT_MOUSE_ABSOLUTE_W: + if (priv->model == MODEL_ELANTECH) { + /* Elantech touchpads report number of fingers directly. */ + hw->fingerWidth = 5; + hw->numFingers = event.value; + break; + } + /* XXX magic number mapping which is mirrored in pms driver */ + switch (event.value) { + case 0: + hw->fingerWidth = 5; + hw->numFingers = 2; + break; + case 1: + hw->fingerWidth = 5; + hw->numFingers = 3; + break; + case 4 ... 5: + hw->fingerWidth = event.value; + hw->numFingers = 1; + break; + } + break; + case WSCONS_EVENT_SYNC: + hw->millis = 1000 * event.time.tv_sec + event.time.tv_nsec / 1000000; + SynapticsCopyHwState(hwRet, hw); + return TRUE; + default: + return FALSE; + } + } + + return FALSE; +} + +static Bool +WSConsAutoDevProbe(InputInfoPtr pInfo, const char *device) +{ + int i; + + if (device && WSConsIsTouchpad(pInfo, device)) + return TRUE; + + if (WSConsIsTouchpad(pInfo, DEFAULT_WSMOUSE_DEV)) { + xf86IDrvMsg(pInfo, X_PROBED, "auto-dev sets device to %s\n", + DEFAULT_WSMOUSE_DEV); + xf86ReplaceStrOption(pInfo->options, "Device", DEFAULT_WSMOUSE_DEV); + return TRUE; + } + + return FALSE; +} + +static void +WSConsReadDevDimensions(InputInfoPtr pInfo) +{ + SynapticsPrivate *priv = (SynapticsPrivate *)pInfo->private; + struct wsmouse_calibcoords wsmc; + int wsmouse_type; + + if (ioctl(pInfo->fd, WSMOUSEIO_GCALIBCOORDS, &wsmc) != 0) { + xf86IDrvMsg(pInfo, X_ERROR, "failed to query axis range (%s)\n", + strerror(errno)); + return; + } + + priv->minx = wsmc.minx; + priv->maxx = wsmc.maxx; + priv->resx = wsmc.resx; + xf86IDrvMsg(pInfo, X_PROBED, "x-axis range %d - %d resolution %d\n", + priv->minx, priv->maxx, priv->resx); + + priv->miny = wsmc.miny; + priv->maxy = wsmc.maxy; + priv->resy = wsmc.resy; + xf86IDrvMsg(pInfo, X_PROBED, "y-axis range %d - %d resolution %d\n", + priv->miny, priv->maxy, priv->resy); + + priv->minp = 0; + priv->maxp = 255; + + priv->minw = 0; + priv->maxw = 15; + + priv->has_pressure = TRUE; + priv->has_left = TRUE; + priv->has_right = TRUE; + priv->has_middle = TRUE; + + if (ioctl(pInfo->fd, WSMOUSEIO_GTYPE, &wsmouse_type) == -1) + xf86IDrvMsg(pInfo, X_ERROR, "cannot get mouse type\n"); + + switch (wsmouse_type) { + default: + case WSMOUSE_TYPE_SYNAPTICS: + priv->model = MODEL_SYNAPTICS; + priv->has_width = TRUE; + priv->has_double = TRUE; + priv->has_triple = TRUE; + break; + case WSMOUSE_TYPE_ALPS: + priv->model = MODEL_ALPS; + priv->has_width = FALSE; + priv->has_double = FALSE; + priv->has_triple = FALSE; + break; + case WSMOUSE_TYPE_ELANTECH: + priv->model = MODEL_ELANTECH; + priv->has_width = TRUE; + priv->has_double = TRUE; + priv->has_triple = TRUE; + break; + } +} + +struct SynapticsProtocolOperations wscons_proto_operations = { + WSConsDeviceOnHook, + WSConsDeviceOffHook, + WSConsQueryHardware, + WSConsReadHwState, + WSConsAutoDevProbe, + WSConsReadDevDimensions +}; |