staging: speakup: fix speakup-r empty line lockup

When cursor is at beginning of an empty or whitespace-only line and
speakup-r typed, kernel locks up. This happens because deadlock of in
input_event function over dev->event_lock, as demonstrated by lockdep
logs. The reason for that is speakup simulates a down arrow - because
cursor is at an empty line - while inside key press notifier handler
which is ultimately triggered from input_event function. The simulated
key press leads to input_event being called again, this time under its
own context. So the spinlock is dev->event_lock is acquired while still
being held.

This patch ensures that key press is not simulated from inside key press
notifier handler. Instead it delegates to cursor_timer. It starts the
timer and passes RA_DOWN_ARROW as argument. When timer handler runs and
sees RA_DOWN_ARROW, it will then call kbd_fakekey2(RA_DOWN_ARROW) which
will correctly simulate the keypress inside timer context.

When not inside key press notifier callback, the behaviour will remain
the same as before this patch.

Signed-off-by: Okash Khawaja <okash.khawaja@gmail.com>
Reviewed-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by Okash Khawaja and committed by Greg Kroah-Hartman e5f5d0e2 b72703e2

+12 -3
+12 -3
drivers/staging/speakup/main.c
··· 1376 1376 1377 1377 static int read_all_key; 1378 1378 1379 + static int in_keyboard_notifier; 1380 + 1379 1381 static void start_read_all_timer(struct vc_data *vc, int command); 1380 1382 1381 1383 enum { ··· 1410 1408 cursor_track = read_all_mode; 1411 1409 spk_reset_index_count(0); 1412 1410 if (get_sentence_buf(vc, 0) == -1) { 1413 - kbd_fakekey2(vc, RA_DOWN_ARROW); 1411 + del_timer(&cursor_timer); 1412 + if (!in_keyboard_notifier) 1413 + speakup_fake_down_arrow(); 1414 + start_read_all_timer(vc, RA_DOWN_ARROW); 1414 1415 } else { 1415 1416 say_sentence_num(0, 0); 1416 1417 synth_insert_next_index(0); ··· 2217 2212 int ret = NOTIFY_OK; 2218 2213 static int keycode; /* to hold the current keycode */ 2219 2214 2215 + in_keyboard_notifier = 1; 2216 + 2220 2217 if (vc->vc_mode == KD_GRAPHICS) 2221 - return ret; 2218 + goto out; 2222 2219 2223 2220 /* 2224 2221 * First, determine whether we are handling a fake keypress on ··· 2232 2225 */ 2233 2226 2234 2227 if (speakup_fake_key_pressed()) 2235 - return ret; 2228 + goto out; 2236 2229 2237 2230 switch (code) { 2238 2231 case KBD_KEYCODE: ··· 2273 2266 break; 2274 2267 } 2275 2268 } 2269 + out: 2270 + in_keyboard_notifier = 0; 2276 2271 return ret; 2277 2272 } 2278 2273