mirror of
https://github.com/X11Libre/xf86-input-evdev.git
synced 2026-04-14 11:44:16 +00:00
Change the calls to refer to EvdevAxes instead of Abs and Rel. Include
mipointer.h. (Fixes a compile error.) Remove an unused variable from
EvdevReadInput.
Removed evdev_abs.c and evdev_rel.c, added evdev_axes.c. Added count to rel
and abs. Moved btnMap from rel to the new axes. Shuffle some includes,
and include stdarg.h.
Removed.
New, a mix of rel and abs that should actually work in the rel case.
Rearrange include order. btnMap was moved from being part of rel to being
part of axes.
Removed.
Rearrange include order.
Rearrange include order.
NOTE: This commit has been compile tested, not run tested.
I have some other changes to make before I can do more heavy testing.
431 lines
10 KiB
C
431 lines
10 KiB
C
/*
|
|
* Copyright © 2006 Zephaniah E. Hull
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Soft-
|
|
* ware"), to deal in the Software without restriction, including without
|
|
* limitation the rights to use, copy, modify, merge, publish, distribute,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, provided that the above copyright
|
|
* notice(s) and this permission notice appear in all copies of the Soft-
|
|
* ware and that both the above copyright notice(s) and this permission
|
|
* notice appear in supporting documentation.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
|
|
* ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY
|
|
* RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN
|
|
* THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSE-
|
|
* QUENTIAL 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 PERFOR-
|
|
* MANCE OF THIS SOFTWARE.
|
|
*
|
|
* Except as contained in this notice, the name of a copyright holder shall
|
|
* not be used in advertising or otherwise to promote the sale, use or
|
|
* other dealings in this Software without prior written authorization of
|
|
* the copyright holder.
|
|
*
|
|
* Author: Zephaniah E. Hull (warp@aehallh.com)
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "evdev.h"
|
|
|
|
#include "xf86_OSlib.h"
|
|
|
|
#include <xf86.h>
|
|
#include <fnmatch.h>
|
|
|
|
#ifndef SYSCALL
|
|
#define SYSCALL(call) while(((call) == -1) && (errno == EINTR))
|
|
#endif
|
|
|
|
static Bool evdev_alive = FALSE;
|
|
static InputInfoPtr evdev_pInfo = NULL;
|
|
static evdevDriverPtr evdev_drivers = NULL;
|
|
static int evdev_seq;
|
|
|
|
int
|
|
evdevGetFDForDevice (evdevDevicePtr device)
|
|
{
|
|
int fd;
|
|
|
|
if (!device)
|
|
return -1;
|
|
|
|
|
|
if (device->device) {
|
|
SYSCALL(fd = open (device->device, O_RDWR | O_NONBLOCK));
|
|
if (fd == -1)
|
|
xf86Msg(X_ERROR, "%s (%d): Open failed: %s\n", __FILE__, __LINE__, strerror(errno));
|
|
return fd;
|
|
} else
|
|
return -1;
|
|
}
|
|
|
|
#define device_add(driver,device) do { \
|
|
device->next = driver->devices; \
|
|
driver->devices = device; \
|
|
} while (0)
|
|
|
|
typedef struct {
|
|
evdevBitsRec bits;
|
|
char name[256];
|
|
char phys[256];
|
|
char dev[256];
|
|
struct input_id id;
|
|
} evdevDevInfoRec, *evdevDevInfoPtr;
|
|
|
|
static Bool
|
|
MatchAll (long *dev, long *match, int len)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < len; i++)
|
|
if ((dev[i] & match[i]) != match[i])
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static Bool
|
|
MatchNot (long *dev, long *match, int len)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < len; i++)
|
|
if ((dev[i] & match[i]))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static Bool
|
|
MatchAny (long *dev, long *match, int len)
|
|
{
|
|
int i, found = 0;
|
|
|
|
for (i = 0; i < len; i++)
|
|
if (match[i]) {
|
|
found = 1;
|
|
if ((dev[i] & match[i]))
|
|
return TRUE;
|
|
}
|
|
|
|
if (found)
|
|
return FALSE;
|
|
else
|
|
return TRUE;
|
|
}
|
|
|
|
static Bool
|
|
MatchDriver (evdevDriverPtr driver, evdevDevInfoPtr info)
|
|
{
|
|
if (driver->name && fnmatch(driver->name, info->name, 0))
|
|
return FALSE;
|
|
if (driver->phys && fnmatch(driver->phys, info->phys, 0))
|
|
return FALSE;
|
|
if (driver->device && fnmatch(driver->device, info->dev, 0))
|
|
return FALSE;
|
|
|
|
if (driver->id.bustype && driver->id.bustype != info->id.bustype)
|
|
return FALSE;
|
|
if (driver->id.vendor && driver->id.vendor != info->id.vendor)
|
|
return FALSE;
|
|
if (driver->id.product && driver->id.product != info->id.product)
|
|
return FALSE;
|
|
if (driver->id.version && driver->id.version != info->id.version)
|
|
return FALSE;
|
|
|
|
#define match(which) \
|
|
if (!MatchAll(info->bits.which, driver->all_bits.which, \
|
|
sizeof(driver->all_bits.which) / \
|
|
sizeof(driver->all_bits.which[0]))) \
|
|
return FALSE; \
|
|
if (!MatchNot(info->bits.which, driver->not_bits.which, \
|
|
sizeof(driver->not_bits.which) / \
|
|
sizeof(driver->not_bits.which[0]))) \
|
|
return FALSE; \
|
|
if (!MatchAny(info->bits.which, driver->any_bits.which, \
|
|
sizeof(driver->any_bits.which) / \
|
|
sizeof(driver->any_bits.which[0]))) \
|
|
return FALSE;
|
|
|
|
match(ev)
|
|
match(key)
|
|
match(rel)
|
|
match(abs)
|
|
match(msc)
|
|
match(led)
|
|
match(snd)
|
|
match(ff)
|
|
|
|
#undef match
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static Bool
|
|
MatchDevice (evdevDevicePtr device, evdevDevInfoPtr info)
|
|
{
|
|
int i, len;
|
|
|
|
if (device->id.bustype != info->id.bustype)
|
|
return FALSE;
|
|
if (device->id.vendor != info->id.vendor)
|
|
return FALSE;
|
|
if (device->id.product != info->id.product)
|
|
return FALSE;
|
|
if (device->id.version != info->id.version)
|
|
return FALSE;
|
|
|
|
if (strcmp(device->name, info->name))
|
|
return FALSE;
|
|
|
|
len = sizeof(info->bits.ev) / sizeof(info->bits.ev[0]);
|
|
for (i = 0; i < len; i++)
|
|
if (device->bits.ev[i] != info->bits.ev[i])
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static Bool
|
|
evdevScanDevice (evdevDriverPtr driver, evdevDevInfoPtr info)
|
|
{
|
|
evdevDevicePtr device;
|
|
int found;
|
|
|
|
if (!MatchDriver (driver, info))
|
|
return FALSE;
|
|
|
|
found = 0;
|
|
for (device = driver->devices; device; device = device->next) {
|
|
if (MatchDevice (device, info)) {
|
|
if (device->seen != (evdev_seq - 1)) {
|
|
device->device = xstrdup(info->dev);
|
|
device->phys = xstrdup(info->phys);
|
|
device->callback(device->pInfo->dev, DEVICE_ON);
|
|
}
|
|
|
|
device->seen = evdev_seq;
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
device = Xcalloc (sizeof (evdevDeviceRec));
|
|
|
|
device->device = xstrdup(info->dev);
|
|
device->name = xstrdup(info->name);
|
|
device->phys = xstrdup(info->phys);
|
|
device->id.bustype = info->id.bustype;
|
|
device->id.vendor = info->id.vendor;
|
|
device->id.product = info->id.product;
|
|
device->id.version = info->id.version;
|
|
device->seen = evdev_seq;
|
|
device_add(driver, device);
|
|
driver->callback(driver, device);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static Bool
|
|
FillDevInfo (char *dev, evdevDevInfoPtr info)
|
|
{
|
|
int fd;
|
|
|
|
SYSCALL(fd = open (dev, O_RDWR | O_NONBLOCK));
|
|
if (fd == -1)
|
|
return FALSE;
|
|
|
|
if (ioctl(fd, EVIOCGNAME(sizeof(info->name)), info->name) == -1)
|
|
info->name[0] = '\0';
|
|
if (ioctl(fd, EVIOCGPHYS(sizeof(info->phys)), info->phys) == -1)
|
|
info->phys[0] = '\0';
|
|
if (ioctl(fd, EVIOCGID, &info->id) == -1) {
|
|
close (fd);
|
|
return FALSE;
|
|
}
|
|
if (!evdevGetBits (fd, &info->bits)) {
|
|
close (fd);
|
|
return FALSE;
|
|
}
|
|
|
|
strncpy (info->dev, dev, sizeof(info->dev));
|
|
close (fd);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
evdevRescanDevices (InputInfoPtr pInfo)
|
|
{
|
|
char dev[20];
|
|
int i, j, found;
|
|
evdevDriverPtr driver;
|
|
evdevDevicePtr device;
|
|
evdevDevInfoRec info;
|
|
|
|
evdev_seq++;
|
|
xf86Msg(X_INFO, "%s: Rescanning devices (%d).\n", pInfo->name, evdev_seq);
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
snprintf(dev, sizeof(dev), "/dev/input/event%d", i);
|
|
|
|
if (!FillDevInfo (dev, &info))
|
|
continue;
|
|
|
|
found = 0;
|
|
|
|
for (j = 0; j <= 3 && !found; j++) {
|
|
for (driver = evdev_drivers; driver && !found; driver = driver->next) {
|
|
if ((driver->pass == j) && (found = evdevScanDevice (driver, &info)))
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (driver = evdev_drivers; driver; driver = driver->next)
|
|
for (device = driver->devices; device; device = device->next)
|
|
if (device->seen == (evdev_seq - 1)) {
|
|
device->callback(device->pInfo->dev, DEVICE_OFF);
|
|
|
|
if (device->device)
|
|
xfree(device->device);
|
|
device->device = NULL;
|
|
|
|
if (device->phys)
|
|
xfree(device->phys);
|
|
device->phys = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
evdevReadInput (InputInfoPtr pInfo)
|
|
{
|
|
/*
|
|
* XXX: Freezing the server for a moment is not really friendly.
|
|
* But we need to wait until udev has actually created the device.
|
|
*/
|
|
usleep (500000);
|
|
evdevRescanDevices (pInfo);
|
|
}
|
|
|
|
static int
|
|
evdevControl(DeviceIntPtr pPointer, int what)
|
|
{
|
|
InputInfoPtr pInfo;
|
|
|
|
pInfo = pPointer->public.devicePrivate;
|
|
|
|
switch (what) {
|
|
case DEVICE_INIT:
|
|
pPointer->public.on = FALSE;
|
|
break;
|
|
|
|
case DEVICE_ON:
|
|
/*
|
|
* XXX: We do /proc/bus/usb/devices instead of /proc/bus/input/devices
|
|
* because the only hotplug input devices at the moment are USB...
|
|
* And because the latter is useless to poll/select against.
|
|
* FIXME: Get a patch in the kernel which fixes the latter.
|
|
*/
|
|
pInfo->fd = open ("/proc/bus/usb/devices", O_RDONLY);
|
|
if (pInfo->fd == -1) {
|
|
xf86Msg(X_ERROR, "%s: cannot open /proc/bus/usb/devices.\n", pInfo->name);
|
|
return BadRequest;
|
|
}
|
|
xf86FlushInput(pInfo->fd);
|
|
AddEnabledDevice(pInfo->fd);
|
|
pPointer->public.on = TRUE;
|
|
evdevRescanDevices (pInfo);
|
|
break;
|
|
|
|
case DEVICE_OFF:
|
|
case DEVICE_CLOSE:
|
|
if (pInfo->fd != -1) {
|
|
RemoveEnabledDevice(pInfo->fd);
|
|
close (pInfo->fd);
|
|
pInfo->fd = -1;
|
|
}
|
|
pPointer->public.on = FALSE;
|
|
break;
|
|
}
|
|
return Success;
|
|
}
|
|
|
|
Bool
|
|
evdevStart (InputDriverPtr drv)
|
|
{
|
|
InputInfoRec *pInfo;
|
|
|
|
if (evdev_alive)
|
|
return TRUE;
|
|
|
|
if (!(pInfo = xf86AllocateInput(drv, 0)))
|
|
return FALSE;
|
|
|
|
evdev_alive = TRUE;
|
|
|
|
pInfo->name = "evdev brain";
|
|
pInfo->type_name = "evdev brain";
|
|
pInfo->device_control = evdevControl;
|
|
pInfo->read_input = evdevReadInput;
|
|
pInfo->fd = -1;
|
|
pInfo->flags = XI86_CONFIGURED | XI86_OPEN_ON_INIT;
|
|
|
|
evdev_pInfo = pInfo;
|
|
return TRUE;
|
|
}
|
|
|
|
Bool
|
|
evdevNewDriver (evdevDriverPtr driver)
|
|
{
|
|
if (!evdev_alive)
|
|
return FALSE;
|
|
/* FIXME: Make this check valid given all the ways to look. */
|
|
#if 0
|
|
if (!(driver->name || driver->phys || driver->device))
|
|
return FALSE;
|
|
#endif
|
|
if (!driver->callback)
|
|
return FALSE;
|
|
|
|
driver->next = evdev_drivers;
|
|
evdev_drivers = driver;
|
|
|
|
evdevRescanDevices (evdev_pInfo);
|
|
driver->configured = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
Bool
|
|
evdevGetBits (int fd, evdevBitsPtr bits)
|
|
{
|
|
#define get_bitmask(fd, which, where) \
|
|
if (ioctl(fd, EVIOCGBIT(which, sizeof (where)), where) < 0) { \
|
|
xf86Msg(X_ERROR, "ioctl EVIOCGBIT %s failed: %s\n", #which, strerror(errno)); \
|
|
return FALSE; \
|
|
}
|
|
|
|
get_bitmask (fd, 0, bits->ev);
|
|
get_bitmask (fd, EV_KEY, bits->key);
|
|
get_bitmask (fd, EV_REL, bits->rel);
|
|
get_bitmask (fd, EV_ABS, bits->abs);
|
|
get_bitmask (fd, EV_MSC, bits->msc);
|
|
get_bitmask (fd, EV_LED, bits->led);
|
|
get_bitmask (fd, EV_SND, bits->snd);
|
|
get_bitmask (fd, EV_FF, bits->ff);
|
|
|
|
#undef get_bitmask
|
|
|
|
return TRUE;
|
|
}
|
|
|