From 18c1927e967b7134e277cd85823617381be3838b Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 28 Aug 2008 02:26:38 +0000 Subject: [PATCH] - evdev-2.0.4-reopen-device.patch: try to reopen devices if a read error occurs on the fd. - evdev-2.0.4-cache-info.patch: cache device info to ensure reopened device isn't different to previous one. --- evdev-2.0.4-cache-info.patch | 222 ++++++++++++++++++++++++++ evdev-2.0.4-reopen-device.patch | 265 ++++++++++++++++++++++++++++++++ xorg-x11-drv-evdev.spec | 14 +- 3 files changed, 500 insertions(+), 1 deletion(-) create mode 100644 evdev-2.0.4-cache-info.patch create mode 100644 evdev-2.0.4-reopen-device.patch diff --git a/evdev-2.0.4-cache-info.patch b/evdev-2.0.4-cache-info.patch new file mode 100644 index 0000000..35b4a4c --- /dev/null +++ b/evdev-2.0.4-cache-info.patch @@ -0,0 +1,222 @@ +From 441a97c22933db462dd53e000d1cb269dab6e825 Mon Sep 17 00:00:00 2001 +From: Peter Hutterer +Date: Thu, 28 Aug 2008 10:29:26 +0930 +Subject: [PATCH] Cache device information and compare against info after re-open. + +This way we ensure that if the topology changes under us, we don't open a +completely different device. If a device has changed, we disable it. +(cherry picked from commit 3bb7d100570134058eb4c906d4902c655148a8be) + +Conflicts: + + src/evdev.c + src/evdev.h +--- + src/evdev.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- + src/evdev.h | 12 +++++ + 2 files changed, 137 insertions(+), 6 deletions(-) + +diff --git a/src/evdev.c b/src/evdev.c +index 16cf67f..ca6da6e 100644 +--- a/src/evdev.c ++++ b/src/evdev.c +@@ -98,6 +98,7 @@ static const char *evdevDefaults[] = { + }; + + static int EvdevOn(DeviceIntPtr); ++static int EvdevCacheCompare(InputInfoPtr pInfo, Bool compare); + + static void + SetXkbOption(InputInfoPtr pInfo, char *name, char **option) +@@ -176,11 +177,19 @@ EvdevReopenTimer(OsTimerPtr timer, CARD32 time, pointer arg) + + if (pInfo->fd != -1) + { +- xf86Msg(X_INFO, "%s: Device reopened after %d attempts.\n", pInfo->name, +- pEvdev->reopen_attempts - pEvdev->reopen_left); +- + pEvdev->reopen_left = 0; +- EvdevOn(pInfo->dev); ++ ++ if (EvdevCacheCompare(pInfo, TRUE) == Success) ++ { ++ xf86Msg(X_INFO, "%s: Device reopened after %d attempts.\n", pInfo->name, ++ pEvdev->reopen_attempts - pEvdev->reopen_left); ++ EvdevOn(pInfo->dev); ++ } else ++ { ++ xf86Msg(X_ERROR, "%s: Device has changed - disabling.\n", ++ pInfo->name); ++ DisableDevice(pInfo->dev); ++ } + return 0; + } + +@@ -368,8 +377,6 @@ EvdevReadInput(InputInfoPtr pInfo) + } + } + +-#define LONG_BITS (sizeof(long) * 8) +-#define NBITS(x) (((x) + LONG_BITS - 1) / LONG_BITS) + #define TestBit(bit, array) (array[(bit) / LONG_BITS]) & (1 << ((bit) % LONG_BITS)) + + static void +@@ -994,6 +1001,116 @@ EvdevConvert(InputInfoPtr pInfo, int first, int num, int v0, int v1, int v2, + return TRUE; + } + ++/** ++ * Get as much information as we can from the fd and cache it. ++ * If compare is True, then the information retrieved will be compared to the ++ * one already cached. If the information does not match, then this function ++ * returns an error. ++ * ++ * @return Success if the information was cached, or !Success otherwise. ++ */ ++static int ++EvdevCacheCompare(InputInfoPtr pInfo, Bool compare) ++{ ++ int i; ++ EvdevPtr pEvdev = pInfo->private; ++ char name[1024] = {0}; ++ long bitmask[NBITS(EV_MAX)] = {0}; ++ long key_bitmask[NBITS(KEY_MAX)] = {0}; ++ long rel_bitmask[NBITS(REL_MAX)] = {0}; ++ long abs_bitmask[NBITS(ABS_MAX)] = {0}; ++ long led_bitmask[NBITS(LED_MAX)] = {0}; ++ struct input_absinfo absinfo[ABS_MAX]; ++ ++ if (ioctl(pInfo->fd, ++ EVIOCGNAME(sizeof(name) - 1), name) < 0) { ++ xf86Msg(X_ERROR, "ioctl EVIOCGNAME failed: %s\n", strerror(errno)); ++ goto error; ++ } ++ ++ if (compare && strcmp(pEvdev->name, name)) ++ goto error; ++ ++ if (ioctl(pInfo->fd, ++ EVIOCGBIT(0, sizeof(bitmask)), bitmask) < 0) { ++ xf86Msg(X_ERROR, "ioctl EVIOCGNAME failed: %s\n", strerror(errno)); ++ goto error; ++ } ++ ++ if (compare && memcmp(pEvdev->bitmask, bitmask, sizeof(bitmask))) ++ goto error; ++ ++ ++ if (ioctl(pInfo->fd, ++ EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) < 0) { ++ xf86Msg(X_ERROR, "ioctl EVIOCGBIT failed: %s\n", strerror(errno)); ++ goto error; ++ } ++ ++ if (compare && memcmp(pEvdev->rel_bitmask, rel_bitmask, sizeof(rel_bitmask))) ++ goto error; ++ ++ if (ioctl(pInfo->fd, ++ EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask) < 0) { ++ xf86Msg(X_ERROR, "ioctl EVIOCGBIT failed: %s\n", strerror(errno)); ++ goto error; ++ } ++ ++ if (compare && memcmp(pEvdev->abs_bitmask, abs_bitmask, sizeof(abs_bitmask))) ++ goto error; ++ ++ if (ioctl(pInfo->fd, ++ EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) < 0) { ++ xf86Msg(X_ERROR, "ioctl EVIOCGBIT failed: %s\n", strerror(errno)); ++ goto error; ++ } ++ ++ if (compare && memcmp(pEvdev->key_bitmask, key_bitmask, sizeof(key_bitmask))) ++ goto error; ++ ++ if (ioctl(pInfo->fd, ++ EVIOCGBIT(EV_LED, sizeof(led_bitmask)), led_bitmask) < 0) { ++ xf86Msg(X_ERROR, "ioctl EVIOCGBIT failed: %s\n", strerror(errno)); ++ goto error; ++ } ++ ++ if (compare && memcmp(pEvdev->led_bitmask, led_bitmask, sizeof(led_bitmask))) ++ goto error; ++ ++ memset(absinfo, 0, sizeof(absinfo)); ++ ++ for (i = 0; i < ABS_MAX; i++) ++ { ++ if (TestBit(i, abs_bitmask)) ++ { ++ if (ioctl(pInfo->fd, EVIOCGABS(i), &absinfo[i]) < 0) { ++ xf86Msg(X_ERROR, "ioctl EVIOCGABS failed: %s\n", strerror(errno)); ++ goto error; ++ } ++ } ++ } ++ ++ if (compare && memcmp(pEvdev->absinfo, absinfo, sizeof(absinfo))) ++ goto error; ++ ++ /* cache info */ ++ if (!compare) ++ { ++ strcpy(pEvdev->name, name); ++ memcpy(pEvdev->bitmask, bitmask, sizeof(bitmask)); ++ memcpy(pEvdev->key_bitmask, key_bitmask, sizeof(key_bitmask)); ++ memcpy(pEvdev->rel_bitmask, rel_bitmask, sizeof(rel_bitmask)); ++ memcpy(pEvdev->abs_bitmask, abs_bitmask, sizeof(abs_bitmask)); ++ memcpy(pEvdev->led_bitmask, led_bitmask, sizeof(led_bitmask)); ++ memcpy(pEvdev->absinfo, absinfo, sizeof(absinfo)); ++ } ++ ++ return Success; ++ ++error: ++ return !Success; ++} ++ + static int + EvdevProbe(InputInfoPtr pInfo) + { +@@ -1173,6 +1290,8 @@ EvdevPreInit(InputDriverPtr drv, IDevPtr dev, int flags) + return NULL; + } + ++ EvdevCacheCompare(pInfo, FALSE); /* cache device data */ ++ + return pInfo; + } + +diff --git a/src/evdev.h b/src/evdev.h +index 0f8cf4b..47bbff2 100644 +--- a/src/evdev.h ++++ b/src/evdev.h +@@ -40,6 +40,9 @@ + #include + #endif + ++#define LONG_BITS (sizeof(long) * 8) ++#define NBITS(x) (((x) + LONG_BITS - 1) / LONG_BITS) ++ + typedef struct { + const char *device; + int kernel24; +@@ -71,6 +74,15 @@ typedef struct { + + int reopen_attempts; /* max attempts to re-open after read failure */ + int reopen_left; /* number of attempts left to re-open the device */ ++ ++ /* Cached info from device. */ ++ char name[1024]; ++ long bitmask[NBITS(EV_MAX)]; ++ long key_bitmask[NBITS(KEY_MAX)]; ++ long rel_bitmask[NBITS(REL_MAX)]; ++ long abs_bitmask[NBITS(ABS_MAX)]; ++ long led_bitmask[NBITS(LED_MAX)]; ++ struct input_absinfo absinfo[ABS_MAX]; + } EvdevRec, *EvdevPtr; + + /* Middle Button emulation */ +-- +1.5.6.4 + diff --git a/evdev-2.0.4-reopen-device.patch b/evdev-2.0.4-reopen-device.patch new file mode 100644 index 0000000..9fd6a0e --- /dev/null +++ b/evdev-2.0.4-reopen-device.patch @@ -0,0 +1,265 @@ +From 188f07089d9c91d503391d05f0c3776360d7446b Mon Sep 17 00:00:00 2001 +From: Peter Hutterer +Date: Thu, 28 Aug 2008 10:24:33 +0930 +Subject: [PATCH] Attempt to re-open devices on read errors. + +Coming back from resume may leave us with a file descriptor that can be opened +but fails on the first read (ENODEV). +In this case, try to open the device until it becomes available or until the +predefined count expires. + +Adds option "ReopenAttempts" +(cherry picked from commit b41d39a745cce9e91241453935ea4c702772c5da) + +Conflicts: + + man/evdev.man + src/evdev.c + src/evdev.h +--- + man/evdev.man | 5 ++ + src/evdev.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++-------- + src/evdev.h | 4 ++ + 3 files changed, 120 insertions(+), 19 deletions(-) + +diff --git a/man/evdev.man b/man/evdev.man +index f438f78..530f979 100644 +--- a/man/evdev.man ++++ b/man/evdev.man +@@ -67,6 +67,11 @@ button event is registered. + Sets the timeout (in milliseconds) that the driver waits before deciding + if two buttons where pressed "simultaneously" when 3 button emulation is + enabled. Default: 50. ++.TP 7 ++.BI "Option \*qReopenAttempts\*q \*q" integer \*q ++Number of reopen attempts after a read error occurs on the device (e.g. after ++waking up from suspend). In between each attempt is a 100ms wait. Default: 10. ++ + .SH AUTHORS + Kristian Høgsberg. + .SH "SEE ALSO" +diff --git a/src/evdev.c b/src/evdev.c +index a857db3..16cf67f 100644 +--- a/src/evdev.c ++++ b/src/evdev.c +@@ -73,6 +73,7 @@ + #define EVDEV_RELATIVE_EVENTS (1 << 2) + #define EVDEV_ABSOLUTE_EVENTS (1 << 3) + #define EVDEV_TOUCHPAD (1 << 4) ++#define EVDEV_INITIALIZED (1 << 5) /* WheelInit etc. called already? */ + + #define MIN_KEYCODE 8 + #define GLYPHS_PER_KEY 2 +@@ -96,6 +97,8 @@ static const char *evdevDefaults[] = { + NULL + }; + ++static int EvdevOn(DeviceIntPtr); ++ + static void + SetXkbOption(InputInfoPtr pInfo, char *name, char **option) + { +@@ -154,6 +157,46 @@ PostKbdEvent(InputInfoPtr pInfo, struct input_event *ev, int value) + xf86PostKeyboardEvent(pInfo->dev, code, value); + } + ++ ++/** ++ * Coming back from resume may leave us with a file descriptor that can be ++ * opened but fails on the first read (ENODEV). ++ * In this case, try to open the device until it becomes available or until ++ * the predefined count expires. ++ */ ++static CARD32 ++EvdevReopenTimer(OsTimerPtr timer, CARD32 time, pointer arg) ++{ ++ InputInfoPtr pInfo = (InputInfoPtr)arg; ++ EvdevPtr pEvdev = pInfo->private; ++ ++ do { ++ pInfo->fd = open(pEvdev->device, O_RDWR, 0); ++ } while (pInfo->fd < 0 && errno == EINTR); ++ ++ if (pInfo->fd != -1) ++ { ++ xf86Msg(X_INFO, "%s: Device reopened after %d attempts.\n", pInfo->name, ++ pEvdev->reopen_attempts - pEvdev->reopen_left); ++ ++ pEvdev->reopen_left = 0; ++ EvdevOn(pInfo->dev); ++ return 0; ++ } ++ ++ pEvdev->reopen_left--; ++ ++ if (!pEvdev->reopen_left) ++ { ++ xf86Msg(X_ERROR, "%s: Failed to reopen device after %d attempts.\n", ++ pInfo->name, pEvdev->reopen_attempts); ++ DisableDevice(pInfo->dev); ++ return 0; ++ } ++ ++ return 100; /* come back in 100 ms */ ++} ++ + static void + EvdevReadInput(InputInfoPtr pInfo) + { +@@ -173,6 +216,15 @@ EvdevReadInput(InputInfoPtr pInfo) + /* The kernel promises that we always only read a complete + * event, so len != sizeof ev is an error. */ + xf86Msg(X_ERROR, "%s: Read error: %s\n", pInfo->name, strerror(errno)); ++ ++ if (errno == ENODEV) /* May happen after resume */ ++ { ++ xf86RemoveEnabledDevice(pInfo); ++ close(pInfo->fd); ++ pInfo->fd = -1; ++ pEvdev->reopen_left = pEvdev->reopen_attempts; ++ TimerSet(NULL, 0, 100, EvdevReopenTimer, pInfo); ++ } + break; + } + +@@ -836,6 +888,47 @@ EvdevInit(DeviceIntPtr device) + return Success; + } + ++/** ++ * Init all extras (wheel emulation, etc.) and grab the device. ++ * ++ * Coming from a resume, the grab may fail with ENODEV. In this case, we set a ++ * timer to wake up and try to reopen the device later. ++ */ ++static int ++EvdevOn(DeviceIntPtr device) ++{ ++ InputInfoPtr pInfo; ++ EvdevPtr pEvdev; ++ ++ pInfo = device->public.devicePrivate; ++ pEvdev = pInfo->private; ++ ++ if (!pEvdev->kernel24 && ioctl(pInfo->fd, EVIOCGRAB, (void *)1)) ++ xf86Msg(X_WARNING, "%s: Grab failed (%s)\n", pInfo->name, ++ strerror(errno)); ++ ++ if (errno == ENODEV) /* may happen after resume */ ++ { ++ close(pInfo->fd); ++ pInfo->fd = -1; ++ pEvdev->reopen_left = pEvdev->reopen_attempts; ++ TimerSet(NULL, 0, 100, EvdevReopenTimer, pInfo); ++ } else ++ { ++ xf86AddEnabledDevice(pInfo); ++ if ((pEvdev->flags & EVDEV_BUTTON_EVENTS) && ++ !(pEvdev->flags & EVDEV_INITIALIZED)) ++ { ++ EvdevMBEmuPreInit(pInfo); ++ } ++ pEvdev->flags |= EVDEV_INITIALIZED; ++ device->public.on = TRUE; ++ } ++ ++ return Success; ++} ++ ++ + static int + EvdevProc(DeviceIntPtr device, int what) + { +@@ -851,30 +944,26 @@ EvdevProc(DeviceIntPtr device, int what) + return EvdevInit(device); + + case DEVICE_ON: +- if (!pEvdev->kernel24 && ioctl(pInfo->fd, EVIOCGRAB, (void *)1)) +- xf86Msg(X_WARNING, "%s: Grab failed (%s)\n", pInfo->name, +- strerror(errno)); +- if (errno != ENODEV) +- { +- xf86AddEnabledDevice(pInfo); +- if (pEvdev->flags & EVDEV_BUTTON_EVENTS) +- EvdevMBEmuPreInit(pInfo); +- device->public.on = TRUE; +- } +- break; ++ return EvdevOn(device); + + case DEVICE_OFF: +- if (!pEvdev->kernel24 && ioctl(pInfo->fd, EVIOCGRAB, (void *)0)) +- xf86Msg(X_WARNING, "%s: Release failed (%s)\n", pInfo->name, +- strerror(errno)); +- xf86RemoveEnabledDevice(pInfo); +- EvdevMBEmuFinalize(pInfo); ++ if (pInfo->fd != -1) ++ { ++ if (!pEvdev->kernel24 && ioctl(pInfo->fd, EVIOCGRAB, (void *)0)) ++ xf86Msg(X_WARNING, "%s: Release failed (%s)\n", pInfo->name, ++ strerror(errno)); ++ xf86RemoveEnabledDevice(pInfo); ++ } ++ if (pEvdev->flags & EVDEV_INITIALIZED) ++ EvdevMBEmuFinalize(pInfo); ++ pEvdev->flags &= ~EVDEV_INITIALIZED; + device->public.on = FALSE; + break; + + case DEVICE_CLOSE: + xf86Msg(X_INFO, "%s: Close\n", pInfo->name); +- close(pInfo->fd); ++ if (pInfo->fd != -1) ++ close(pInfo->fd); + break; + } + +@@ -1060,11 +1149,12 @@ EvdevPreInit(InputDriverPtr drv, IDevPtr dev, int flags) + return NULL; + } + ++ pEvdev->device = device; ++ + xf86Msg(deviceFrom, "%s: Device: \"%s\"\n", pInfo->name, device); + do { + pInfo->fd = open(device, O_RDWR, 0); +- } +- while (pInfo->fd < 0 && errno == EINTR); ++ } while (pInfo->fd < 0 && errno == EINTR); + + if (pInfo->fd < 0) { + xf86Msg(X_ERROR, "Unable to open evdev device \"%s\".\n", device); +@@ -1072,6 +1162,8 @@ EvdevPreInit(InputDriverPtr drv, IDevPtr dev, int flags) + return NULL; + } + ++ pEvdev->reopen_attempts = xf86SetIntOption(pInfo->options, "ReopenAttempts", 10); ++ + pEvdev->noXkb = noXkbExtension; + /* parse the XKB options during kbd setup */ + +diff --git a/src/evdev.h b/src/evdev.h +index cad1eed..0f8cf4b 100644 +--- a/src/evdev.h ++++ b/src/evdev.h +@@ -41,6 +41,7 @@ + #endif + + typedef struct { ++ const char *device; + int kernel24; + int screen; + int min_x, min_y, max_x, max_y; +@@ -67,6 +68,9 @@ typedef struct { + Time expires; /* time of expiry */ + Time timeout; + } emulateMB; ++ ++ int reopen_attempts; /* max attempts to re-open after read failure */ ++ int reopen_left; /* number of attempts left to re-open the device */ + } EvdevRec, *EvdevPtr; + + /* Middle Button emulation */ +-- +1.5.6.4 + diff --git a/xorg-x11-drv-evdev.spec b/xorg-x11-drv-evdev.spec index 51331c8..05df848 100644 --- a/xorg-x11-drv-evdev.spec +++ b/xorg-x11-drv-evdev.spec @@ -7,7 +7,7 @@ Summary: Xorg X11 evdev input driver Name: xorg-x11-drv-evdev Version: 2.0.4 -Release: 1%{?dist} +Release: 2%{?dist} URL: http://www.x.org License: MIT Group: User Interface/X Hardware Support @@ -17,6 +17,9 @@ Source0: ftp://ftp.x.org/pub/individual/driver/%{tarball}-%{version}.tar.bz2 #Source0: %{tarball}-%{gitdate}.tar.bz2 Source1: make-git-snapshot.sh +Patch001: evdev-2.0.4-reopen-device.patch +Patch002: evdev-2.0.4-cache-info.patch + ExcludeArch: s390 s390x BuildRequires: autoconf automake libtool @@ -31,6 +34,9 @@ X.Org X11 evdev input driver. #%setup -q -n %{tarball}-%{gitdate} %setup -q -n %{tarball}-%{version} +# apply patches +%patch1 -p1 -b .reopen-device.patch +%patch2 -p1 -b .cache-info.patch %build autoreconf -v --install || exit 1 @@ -55,6 +61,12 @@ rm -rf $RPM_BUILD_ROOT %{_mandir}/man4/evdev.4* %changelog +* Thu Aug 28 2008 Peter Hutterer 2.0.4-2 +- evdev-2.0.4-reopen-device.patch: try to reopen devices if a read error + occurs on the fd. +- evdev-2.0.4-cache-info.patch: cache device info to ensure reopened device + isn't different to previous one. + * Mon Aug 25 2008 Peter Hutterer 2.0.4-1 - evdev 2.0.4