From patchwork Mon Jan 7 11:58:23 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Herrmann X-Patchwork-Id: 1940901 Return-Path: X-Original-To: patchwork-linux-input@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork2.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork2.kernel.org (Postfix) with ESMTP id 27C43DF230 for ; Mon, 7 Jan 2013 12:03:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754330Ab3AGMDn (ORCPT ); Mon, 7 Jan 2013 07:03:43 -0500 Received: from mail-bk0-f49.google.com ([209.85.214.49]:47005 "EHLO mail-bk0-f49.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754183Ab3AGMDm (ORCPT ); Mon, 7 Jan 2013 07:03:42 -0500 Received: by mail-bk0-f49.google.com with SMTP id jm19so8491263bkc.36 for ; Mon, 07 Jan 2013 04:03:41 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=x-received:from:to:cc:subject:date:message-id:x-mailer; bh=ZJl/hOCVD5eih+U4vE/j8k9jFPghB1LLDtQisHtRDQw=; b=RP+AKlP75fdjlIljKLvbGm9mBZj+yfYisITK2xIiIozo+CGrRong97zdMNImxFBiLI xPpwDlDL+suDxELOzd40fcSYlhaB4hxVfnshO8N2c2lZVfmDy5dXPv03INW1OaEhVfUb eN7VPBf1AXTPXkM7XUinw46OK2z3XYpVJrxSIxfQi7xAEqhr5GcM+4lCO2hAQ/waylr3 YINb7FbwYqDe7sPJEWtbGnvAqEBTEGFlGakZPGYO5TvH81lqfe9zzQcaepJRv/kQU/hr yyET8IiCbi0Xv84Mey0mzgKxOkcpORLB4A/06xoK61xgWJADtec6uhdXt1kVdsCV96Gw 6i6w== X-Received: by 10.204.147.135 with SMTP id l7mr30593863bkv.119.1357559801445; Mon, 07 Jan 2013 03:56:41 -0800 (PST) Received: from localhost.localdomain (stgt-5f71bc77.pool.mediaWays.net. [95.113.188.119]) by mx.google.com with ESMTPS id u3sm41993283bkw.9.2013.01.07.03.56.39 (version=TLSv1/SSLv3 cipher=OTHER); Mon, 07 Jan 2013 03:56:40 -0800 (PST) From: David Herrmann To: linux-input@vger.kernel.org Cc: Dmitry Torokhov , Ran Benita , David Herrmann Subject: [PATCH] Input: new EVIOCGKEYF to get keystate and flush queues Date: Mon, 7 Jan 2013 12:58:23 +0100 Message-Id: <1357559903-1881-1-git-send-email-dh.herrmann@googlemail.com> X-Mailer: git-send-email 1.8.1 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org EVIOCGKEYF returns the current keystate similar to EVIOCGKEY but also flushes the outgoing evdev event queue. This allows user-space devices to reliably retrieve the current state without having old events still in the read()-queue. Userspace libraries like XKB/xkbcommon use stacking key trackers, so they allow the kernel to map multiple keys to the same keycode. For instance if left-shift and right-shift map to the same keycode, xkbcommon allows the kernel to send key-down twice but also requires it to send key-up twice. A single input device doesn't support that, but this is very useful if you want multiple connected keyboards to have one shared modifier state: Pressing Shift on the one keyboard affects keys on the other keyboard. This is not the usual setup but it is supported and known to be used (for instance if you have external num-pads etc.). When a program opens input devices, it can read the keystate via EVIOCGKEY to update its internal keyboard state. This allows to keep CTRL pressed while switching VTs and having both applications remember that CTRL is still pressed. However, if there are still outgoing events in the evdev queue, the application will update its internal keyboard state and then read the event again, even though it already processed it via EVIOCGKEY. EVIOCGKEYF solves this race condition by flushing the evdev queue while returning the keystate. We cannot change EVIOCGKEY because we don't know whether userspace depends on a clean read() history and flushing the queue effectively drops events. This race could be fixed in user-space by keeping an exact copy of the keystate array for each input device that we opened and ignoring double-events for a single keycode. However, if we, as mentioned, want a shared keyboard-state, it seems to be a waste of computing power and memory to keep another copy for each input device if we could just use something like EVIOCGKEYF. Signed-off-by: David Herrmann --- Hi I am actually not sure whether I did the locking in the right order. However, I need to take event_lock _and_ buffer_lock to be sure neither the kernel-internal keystate nor the output queue is changed as this ioctl needs to be atomic. Regards David drivers/input/evdev.c | 10 ++++++++++ include/uapi/linux/input.h | 1 + 2 files changed, 11 insertions(+) diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index f0f8928..f178ae0 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -782,6 +782,16 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, case EVIOCGSW(0): return bits_to_user(dev->sw, SW_MAX, size, p, compat_mode); + case EVIOCGKEYF(0): + spin_lock_irq(&dev->event_lock); + spin_lock(&client->buffer_lock); + error = bits_to_user(dev->key, KEY_MAX, size, p, compat_mode); + if (error >= 0) + client->tail = client->packet_head = client->head; + spin_unlock(&client->buffer_lock); + spin_unlock_irq(&dev->event_lock); + return error; + case EVIOCGNAME(0): return str_to_user(dev->name, size, p); diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h index 5588285..ade47d1 100644 --- a/include/uapi/linux/input.h +++ b/include/uapi/linux/input.h @@ -142,6 +142,7 @@ struct input_keymap_entry { #define EVIOCGLED(len) _IOC(_IOC_READ, 'E', 0x19, len) /* get all LEDs */ #define EVIOCGSND(len) _IOC(_IOC_READ, 'E', 0x1a, len) /* get all sounds status */ #define EVIOCGSW(len) _IOC(_IOC_READ, 'E', 0x1b, len) /* get all switch states */ +#define EVIOCGKEYF(len) _IOC(_IOC_READ, 'E', 0x1c, len) /* get global key state and flush queue */ #define EVIOCGBIT(ev,len) _IOC(_IOC_READ, 'E', 0x20 + (ev), len) /* get event bits */ #define EVIOCGABS(abs) _IOR('E', 0x40 + (abs), struct input_absinfo) /* get abs value/limits */