diff -up xf86-input-evdev-2.0.4/man/evdev.man.reopen-device xf86-input-evdev-2.0.4/man/evdev.man --- xf86-input-evdev-2.0.4/man/evdev.man.reopen-device 2008-08-14 22:27:13.000000000 -0400 +++ xf86-input-evdev-2.0.4/man/evdev.man 2008-09-12 15:45:50.000000000 -0400 @@ -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 -up xf86-input-evdev-2.0.4/src/evdev.c.reopen-device xf86-input-evdev-2.0.4/src/evdev.c --- xf86-input-evdev-2.0.4/src/evdev.c.reopen-device 2008-08-14 22:27:13.000000000 -0400 +++ xf86-input-evdev-2.0.4/src/evdev.c 2008-09-12 15:55:42.000000000 -0400 @@ -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,48 @@ PostKbdEvent(InputInfoPtr pInfo, struct 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; + pEvdev->reopen_timer = NULL; + 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); + pEvdev->reopen_timer = NULL; + return 0; + } + + return 100; /* come back in 100 ms */ +} + static void EvdevReadInput(InputInfoPtr pInfo) { @@ -173,6 +218,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; + pEvdev->reopen_timer = TimerSet(NULL, 0, 100, EvdevReopenTimer, pInfo); + } break; } @@ -836,6 +890,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; + pEvdev->reopen_timer = 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 +946,30 @@ 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 (pEvdev->reopen_timer) { + TimerCancel(pEvdev->reopen_timer); + pEvdev->reopen_timer = NULL; + } + if (pInfo->fd != -1) + close(pInfo->fd); break; } @@ -1060,11 +1155,12 @@ EvdevPreInit(InputDriverPtr drv, IDevPtr 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 +1168,8 @@ EvdevPreInit(InputDriverPtr drv, IDevPtr return NULL; } + pEvdev->reopen_attempts = xf86SetIntOption(pInfo->options, "ReopenAttempts", 10); + pEvdev->noXkb = noXkbExtension; /* parse the XKB options during kbd setup */ diff -up xf86-input-evdev-2.0.4/src/evdev.h.reopen-device xf86-input-evdev-2.0.4/src/evdev.h --- xf86-input-evdev-2.0.4/src/evdev.h.reopen-device 2008-08-14 22:27:13.000000000 -0400 +++ xf86-input-evdev-2.0.4/src/evdev.h 2008-09-12 15:55:44.000000000 -0400 @@ -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,10 @@ 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 */ + OsTimerPtr reopen_timer; } EvdevRec, *EvdevPtr; /* Middle Button emulation */