--- Begin Message ---
Package: xserver-xorg-input-evtouch
Version: 0.8.7-3
Severity: normal
Tags: patch
The attached patch fixes race conditions in the timer code which can
result in some taps going missing. Also included is a fix to have a
timer go off immediately (if set to 0). And the ability to turn a
timer off (if set to -1).
Quortech's open source packages are available at:
ftp://ftp.quortech.com/eclipse/deb-packages/
The packages associated with xf86-input-evtouch_0.8.7-3quortech3.dsc
implement this patch.
-- System Information:
Debian Release: 4.0
APT prefers stable
APT policy: (500, 'stable')
Architecture: i386 (i686)
Shell: /bin/sh linked to /bin/bash
Kernel: Linux 2.6.22-14-generic
Locale: LANG=en_CA.UTF-8, LC_CTYPE=en_CA.UTF-8 (charmap=UTF-8)
# Description: This patch fixes a data-loss bug and enhances the timers
# in evtouch.
#
# Changes
# o disable_timers() : Move to state change routine, as old timers
# must be cancelled with *every* state change.
# o libtouchEnhancedTimerSet : Time values can also be off (-1) or
# immediate (0).
# o libtouchTriggerSM : Fix bug where not protected from reentrant
# call when timer goes off. Potential for state changes to be lost
# and output events not reflecting input events.
#
# Feel free to send comments, critics, suggestions to w...@acm.org. To
# apply simply change into the toplevel directory of the source to be
# modified and enter: patch -p1 < <PATH_TO_PATCH>
#
# All patches are available under the GNU GPL, I hope they might be
# useful for you (-:
# - Brett Wuth
#
Index: xf86-input-evtouch-0.8.7/evtouch.c
===================================================================
--- xf86-input-evtouch-0.8.7.orig/evtouch.c 2009-01-15 14:53:27.000000000
-0700
+++ xf86-input-evtouch-0.8.7/evtouch.c 2009-01-15 14:53:27.000000000 -0700
@@ -440,11 +440,12 @@
if (priv->emulate3) {
if ( (ev->value==1) && (priv->emulate3_timer==NULL) )
- priv->emulate3_timer = TimerSet(priv->emulate3_timer,
- 0,
- priv->emulate3_timeout,
- emulate3Timer,
- local);
+ priv->emulate3_timer =
+ libtouchEnhancedTimerSet( priv->emulate3_timer,
+ 0,
+ priv->emulate3_timeout,
+ emulate3Timer,
+ local );
if ( (ev->value == 1) && (ev->code == BTN_LEFT) ) {
priv->touch_flags |= LB_STAT;
Index: xf86-input-evtouch-0.8.7/libtouch.c
===================================================================
--- xf86-input-evtouch-0.8.7.orig/libtouch.c 2009-01-15 14:53:27.000000000
-0700
+++ xf86-input-evtouch-0.8.7/libtouch.c 2009-01-15 14:53:27.000000000 -0700
@@ -328,6 +328,50 @@
}
+/* A special value which means a timer should never trigger.
+ */
+#define TimerNever_M ((CARD32)-1)
+
+
+/* Set the timer but handle special values for millis (the time).
+ * if flags set to relative time, millis is interpretted as:
+ * 0 : trigger immediately (TimerSet ignores)
+ * TimerNever_M : never trigger
+ */
+OsTimerPtr
+libtouchEnhancedTimerSet( OsTimerPtr timer,
+ int flags,
+ CARD32 millis,
+ OsTimerCallback func,
+ pointer arg )
+{
+ int/*bool*/ relative = !(flags & TimerAbsolute);
+ int/*bool*/ immediate = relative && (millis == 0);
+
+ if (relative && (millis == TimerNever_M))
+ {
+ /* Still want to process other flags, but not to set the timer.
+ * 0 is a value that means that to TimerSet() */
+ millis = 0;
+ DBG(4, ErrorF( "LibTouch: timer set to NEVER\n" ));
+ }
+
+ /* If millis is 0, will not process timer, but will process other
+ * flags (TimerForceOld).
+ * See TimerSet definition in os/WaitFor.c
+ */
+ OsTimerPtr retVal = TimerSet( timer, flags, millis, func, arg );
+
+ if (immediate)
+ {
+ /* Since wasn't called by TimerSet, we call it ourselves */
+ (void) (*func)( NULL, GetTimeInMillis(), arg );
+ }
+
+ return (retVal);
+}
+
+
static CARD32
tap_timer_func(OsTimerPtr timer, CARD32 now, pointer _priv)
{
@@ -364,7 +408,6 @@
int bit_size = sizeof(priv->pressed_btn_stat) * 8;
priv->touch_flags = 0;
- disable_timers(priv);
/* do an untouch for all pressed buttons */
for (i = 0; i < bit_size; i++)
@@ -424,10 +467,10 @@
static void enter_touched(LibTouchRecPtr priv)
{
- disable_timers(priv);
- priv->longtouch_timer = TimerSet(priv->longtouch_timer, 0,
- priv->longtouch_timeo,
- longtouch_timer_func, priv);
+ priv->longtouch_timer =
+ libtouchEnhancedTimerSet( priv->longtouch_timer, 0,
+ priv->longtouch_timeo,
+ longtouch_timer_func, priv );
}
@@ -480,7 +523,6 @@
static void enter_moving(LibTouchRecPtr priv)
{
- disable_timers(priv);
}
@@ -495,7 +537,6 @@
static void enter_longtouched(LibTouchRecPtr priv)
{
- disable_timers(priv);
DBG(4, ErrorF("LibTouch: Issuing Button-press 1\n"));
issue_btn_event(priv, S_LONGTOUCHED, priv->cur_x, priv->cur_y);
}
@@ -534,10 +575,10 @@
static void enter_maybetap(LibTouchRecPtr priv)
{
- disable_timers(priv);
- priv->tap_timer = TimerSet(priv->tap_timer, 0,
- priv->tap_timeo,
- tap_timer_func, priv);
+ priv->tap_timer =
+ libtouchEnhancedTimerSet( priv->tap_timer, 0,
+ priv->tap_timeo,
+ tap_timer_func, priv );
}
@@ -610,10 +651,10 @@
static void enter_oneandahalftap(LibTouchRecPtr priv)
{
- disable_timers(priv);
- priv->longtouch_timer = TimerSet( priv->longtouch_timer, 0,
- priv->longtouch_timeo,
- longtouch_timer_func, priv);
+ priv->longtouch_timer =
+ libtouchEnhancedTimerSet( priv->longtouch_timer, 0,
+ priv->longtouch_timeo,
+ longtouch_timer_func, priv );
}
@@ -670,27 +711,108 @@
}
+/* Process a state machine event. May cause a change in state. */
void libtouchTriggerSM(LibTouchRecPtr libtouch, LibTouchState_t pen)
{
- static int state_idx = S_UNTOUCHED;
- int next_state_idx;
-
- if (pen != PEN_UNKNOWN)
- libtouch->pen = pen;
-
- DBG(4, ErrorF("LibTouch: Triggering SM pen = 0x%02x\n", pen));
-
- next_state_idx = state_ar[state_idx].handle_state(libtouch);
- if( (next_state_idx != state_idx) &&
- (state_ar[next_state_idx].enter_state != NULL) ) {
- state_ar[next_state_idx].enter_state(libtouch);
- }
-
- DBG(4, ErrorF("LibTouch: Next State %d = %s\n", next_state_idx,
- state_str[next_state_idx]));
-
- state_idx = next_state_idx;
- libtouch->past = libtouch->now;
- libtouch->xpos_changed = 0;
- libtouch->ypos_changed = 0;
+ /* This function must be reentrant, because it can be called twice
+ * simultaneously: once as a result of touch screen inputs (e.g. SYN
+ * received by evtouch.c) and, anywhere in the middle of our call
+ * for that, a second time for a timeout (e.g. tap_timer_func()).
+ */
+
+ static int state_idx = S_UNTOUCHED;
+
+
+ /* Disable timer interrupts to prevent reentrant execution of the
+ * critical region of this code.
+ */
+ int sigstate = xf86BlockSIGIO();
+
+
+ /* Changes to pen state are likely not to come from timeout events,
+ * so probably this doesn't need to be part of the critical region.
+ * But its low cost (fast) to keep in critical region so do so
+ * defensively in case a future writer does something fancy within a
+ * timer.
+ */
+ if (pen != PEN_UNKNOWN)
+ libtouch->pen = pen;
+ DBG(4, ErrorF("LibTouch: Triggering SM pen = 0x%02x\n", pen));
+
+
+ /* Let the state handler process the input events, possibly
+ * selecting a new state.
+ *
+ * This is in the critical region to avoid a race condition:
+ * handle_state() is handling input that will result in *no* state
+ * change. Timeout occurs and we would be called reentrantly
+ * resulting in a state change. handle_state() returns and state is
+ * overwritten with old state. As a result the state change from
+ * the timeout is lost.
+ */
+ int next_state_idx = state_ar[state_idx].handle_state(libtouch);
+ DBG(4, ErrorF("LibTouch: Next State %d = %s\n", next_state_idx,
+ state_str[next_state_idx]));
+
+
+ /* Mark the input events as processed.
+ *
+ * This is part of the critical region, so a state handler doesn't
+ * process input event twice.
+ */
+ libtouch->past = libtouch->now;
+ libtouch->xpos_changed = 0;
+ libtouch->ypos_changed = 0;
+
+ int state_change = (next_state_idx != state_idx);
+
+ if (state_change)
+ {
+ state_idx = next_state_idx;
+
+ /* A state change always means the timers associated with the
+ * previous state are no longer valid. (They would indicate a
+ * state transition path originating in a no-longer-current
+ * state.) This is therefore part of the critical region.
+ *
+ * Timeouts which are pending because of xf86BlockSIGIO() will
+ * not occur when unblocked, because disable_timers() calls
+ * TimerFree().
+ */
+ disable_timers( libtouch );
+ }
+
+
+ /* We're out of the critical region. Return timer interrupts
+ * to normal. */
+ xf86UnblockSIGIO( sigstate );
+
+
+ if (state_change && state_ar[next_state_idx].enter_state != NULL)
+ {
+ /* Set up for the new state.
+ *
+ * This is not part of the critical region because it does not affect
+ * this function's state.
+ *
+ * Any code (e.g. an enter_ function) called by enter_state()
+ * which sets up a timer, must be prepared for that timer to go
+ * off before the code completes the rest of its initialization.
+ * That is not our responsibility. We /could/ handle it here by
+ * moving this call into the critical region, but it would
+ * require libtouchEnhancedTimerSet() to be reimplemented to not
+ * call us recursively on a 0 timeout.
+ */
+ state_ar[next_state_idx].enter_state(libtouch);
+
+ /* enter_state() may cause a recursive call to this function (if
+ * a timeout is set to 0). Since this is outside the critical
+ * region, the behaviour is identical to what would happen if
+ * the call came from an actual time out.
+ *
+ * If a cascade of state transitions happen, each with 0
+ * millisecond timeouts, we could eventually blow the stack, but
+ * that would be a configuration error.
+ */
+ }
}
Index: xf86-input-evtouch-0.8.7/libtouch.h
===================================================================
--- xf86-input-evtouch-0.8.7.orig/libtouch.h 2009-01-15 14:53:27.000000000
-0700
+++ xf86-input-evtouch-0.8.7/libtouch.h 2009-01-15 14:53:27.000000000 -0700
@@ -164,4 +164,16 @@
void libtouchSetXPos(LibTouchRecPtr libtouch, int x);
void libtouchSetYPos(LibTouchRecPtr libtouch, int y);
+/* Set the timer but handle special values for millis (the time).
+ * if flags set to relative time, millis is interpretted as:
+ * 0 : trigger immediately (TimerSet ignores)
+ * TimerNever_M : never trigger
+ */
+OsTimerPtr
+libtouchEnhancedTimerSet( OsTimerPtr timer,
+ int flags,
+ CARD32 millis,
+ OsTimerCallback func,
+ pointer arg );
+
#endif
--- End Message ---