#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "evdev.h"
#include "xf86_OSlib.h"
#include <xf86.h>
#include <fnmatch.h>
#include <sys/poll.h>
#include "inotify.h"
#include "inotify-syscalls.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;
static int evdev_inotify;
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 (unsigned long *dev, unsigned 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 (unsigned long *dev, unsigned long *match, int len)
{
int i;
for (i = 0; i < len; i++)
if ((dev[i] & match[i]))
return FALSE;
return TRUE;
}
static Bool
MatchAny (unsigned long *dev, unsigned 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)
{
int scan = 0, i, len;
char buf[4096];
struct inotify_event *event;
if (evdev_inotify) {
while ((len = read (pInfo->fd, buf, sizeof(buf))) >= 0) {
for (i = 0; i < len; i += sizeof (struct inotify_event) + event->len) {
event = (struct inotify_event *) &buf[i];
if (!event->len)
continue;
if (event->mask & IN_ISDIR)
continue;
if (strncmp("event", event->name, 5))
continue;
scan = 1;
}
}
if (scan)
evdevRescanDevices (pInfo);
} else {
usleep (500000);
evdevRescanDevices (pInfo);
}
}
static int
evdevControl(DeviceIntPtr pPointer, int what)
{
InputInfoPtr pInfo;
int i, flags;
pInfo = pPointer->public.devicePrivate;
switch (what) {
case DEVICE_INIT:
pPointer->public.on = FALSE;
break;
case DEVICE_ON:
evdev_inotify = 1;
SYSCALL(pInfo->fd = inotify_init());
if (pInfo->fd < 0) {
xf86Msg(X_ERROR, "%s: Unable to initialize inotify, using fallback. (errno: %d)\n", pInfo->name, errno);
evdev_inotify = 0;
}
SYSCALL (i = inotify_add_watch (pInfo->fd, "/dev/input/", IN_CREATE | IN_DELETE));
if (i < 0) {
xf86Msg(X_ERROR, "%s: Unable to initialize inotify, using fallback. (errno: %d)\n", pInfo->name, errno);
evdev_inotify = 0;
SYSCALL (close (pInfo->fd));
pInfo->fd = -1;
}
if ((flags = fcntl(pInfo->fd, F_GETFL)) < 0) {
xf86Msg(X_ERROR, "%s: Unable to NONBLOCK inotify, using fallback. "
"(errno: %d)\n", pInfo->name, errno);
evdev_inotify = 0;
SYSCALL (close (pInfo->fd));
pInfo->fd = -1;
} else if (fcntl(pInfo->fd, F_SETFL, flags | O_NONBLOCK) < 0) {
xf86Msg(X_ERROR, "%s: Unable to NONBLOCK inotify, using fallback. "
"(errno: %d)\n", pInfo->name, errno);
evdev_inotify = 0;
SYSCALL (close (pInfo->fd));
pInfo->fd = -1;
}
if (!evdev_inotify) {
SYSCALL (pInfo->fd = open ("/proc/bus/usb/devices", O_RDONLY));
if (pInfo->fd < 0) {
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);
SYSCALL (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;
#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;
}
void
evdevRemoveDevice (evdevDevicePtr pEvdev)
{
evdevDriverPtr driver, prev;
evdevDevicePtr *device;
prev = evdev_drivers;
for (driver = evdev_drivers; driver; prev = driver, driver = driver->next) {
for (device = &driver->devices; *device; device = &(*device)->next) {
if (*device == pEvdev) {
*device = pEvdev->next;
pEvdev->next = NULL;
if (!driver->devices)
{
evdevDeleteDevice(pEvdev);
evdevRemoveDriver(driver);
}
return;
}
}
}
}
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;
}
void
evdevDeleteDevice(evdevDevicePtr pEvdev)
{
xfree(pEvdev->name);
xfree(pEvdev->phys);
xfree(pEvdev->device);
xfree(pEvdev);
}
void evdevRemoveDriver(evdevDriverPtr drv)
{
evdevDriverPtr driver, prev;
evdevDevicePtr device;
if (drv == evdev_drivers)
evdev_drivers = evdev_drivers->next;
else
for (prev = evdev_drivers, driver = prev->next; driver;
prev = driver, driver = driver->next)
{
if (driver == drv)
{
prev->next = driver->next;
}
}
xfree(drv->name);
xfree(drv->phys);
xfree(drv->device);
device = drv->devices;
while(device)
{
evdevDeleteDevice(device);
device = device->next;
}
xfree(drv);
}