From patchwork Tue Dec 17 15:48:51 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Herrmann X-Patchwork-Id: 3362541 Return-Path: X-Original-To: patchwork-linux-input@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 7673D9F344 for ; Tue, 17 Dec 2013 15:53:45 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 9598120144 for ; Tue, 17 Dec 2013 15:53:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 258222009C for ; Tue, 17 Dec 2013 15:53:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754646Ab3LQPtY (ORCPT ); Tue, 17 Dec 2013 10:49:24 -0500 Received: from mail-ee0-f53.google.com ([74.125.83.53]:46163 "EHLO mail-ee0-f53.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754708Ab3LQPtV (ORCPT ); Tue, 17 Dec 2013 10:49:21 -0500 Received: by mail-ee0-f53.google.com with SMTP id b57so2981008eek.12 for ; Tue, 17 Dec 2013 07:49:20 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=WKzzKG5IqI2Ym9Py+2ILZRpz9/8KhN1DbFGhF5aeRGw=; b=KYJiIZdSyok0hjdFCOQJMXS6XStErpc5K4yjKpZGMraXPiX/mkRR9SYqtqnTugjPKk vuYbgCtXYV9qo+Sx0WQp/Kriff2DQmxuG5tehO1YH6Cj2c3Lo4iqq3QBtYHgWZD7+CJt h0MF/d3MnuxLs9gt2li0kaolDXPqXRDODCLaEPeO0BGwlCIjJEeOG5pjgOBBA5sV3ak+ maK80oaOZxEIKEFgqinHLPCWVkNxOAlRz3Nn5PklbMSXwI9cbs1Hqd1FW5o4DgquaJLJ c4NEUidj7hDx7J4Pa8j5uX3+Wn01x1Knlbw1xh/H3aY5DRjIXBgrXt/VrvQeYr9c2N2C /uBg== X-Received: by 10.15.83.8 with SMTP id b8mr23929912eez.6.1387295360098; Tue, 17 Dec 2013 07:49:20 -0800 (PST) Received: from david-ub.localdomain (stgt-4d024e4a.pool.mediaWays.net. [77.2.78.74]) by mx.google.com with ESMTPSA id l4sm54116077een.13.2013.12.17.07.49.17 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 17 Dec 2013 07:49:18 -0800 (PST) From: David Herrmann To: linux-input@vger.kernel.org Cc: Dmitry Torokhov , Jiri Kosina , Benjamin Tissoires , Peter Hutterer , Antonio Ospite , linux-kernel@vger.kernel.org, input-tools@lists.freedesktop.org, David Herrmann Subject: [PATCH 1/4] Input: uinput: add full absinfo support Date: Tue, 17 Dec 2013 16:48:51 +0100 Message-Id: <1387295334-1744-2-git-send-email-dh.herrmann@gmail.com> X-Mailer: git-send-email 1.8.5.1 In-Reply-To: <1387295334-1744-1-git-send-email-dh.herrmann@gmail.com> References: <1387295334-1744-1-git-send-email-dh.herrmann@gmail.com> Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Spam-Status: No, score=-7.3 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP We currently lack support for abs-resolution and abs-value parameters during uinput ABS initialization. Furthermore, our parsers don't allow growing ABS_CNT values. Therefore, introduce uinput_user_dev2. User-space is free to write uinput_user_dev2 objects instead of uinput_user_dev legacy objects now. If the kernel lacks support for it, our comparison for "count != sizeof(struct uinput_user_dev)" will catch this and return EINVAL. User-space shall retry with the legacy mode then. Internally, we transform the legacy object into uinput_user_dev2 and then handle both the same way. The new uinput_user_dev2 object has multiple new features: - abs payload now has "value" and "resolution" parameters as part of the switch to "struct input_absinfo". We simply copy these over. - Our parser allows growing ABS_CNT. We automatically detect the payload size of the caller, thus, calculating the ABS_CNT the program was compiled with. - A "version" field to denote the uinput-version used. This is required to properly support changing "struct input_user_dev2" changes in the future. Due to the dynamic ABS_CNT support, we cannot simply add new fields, as we cannot deduce the structure size from the user-space given size. Thus, we need the "version" field to allow changing the object and properly detecting it in our write() handler. Signed-off-by: David Herrmann Reviewed-by: Peter Hutterer --- drivers/input/misc/uinput.c | 142 ++++++++++++++++++++++++++++++++------------ include/uapi/linux/uinput.h | 9 +++ 2 files changed, 114 insertions(+), 37 deletions(-) diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 7728359..927ad9a 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -358,14 +358,16 @@ static int uinput_allocate_device(struct uinput_device *udev) } static int uinput_setup_device(struct uinput_device *udev, - const char __user *buffer, size_t count) + struct uinput_user_dev2 *user_dev2, + size_t abscnt) { - struct uinput_user_dev *user_dev; struct input_dev *dev; int i; int retval; + struct input_absinfo *abs; - if (count != sizeof(struct uinput_user_dev)) + /* Ensure name is filled in */ + if (!user_dev2->name[0]) return -EINVAL; if (!udev->dev) { @@ -375,37 +377,27 @@ static int uinput_setup_device(struct uinput_device *udev, } dev = udev->dev; - - user_dev = memdup_user(buffer, sizeof(struct uinput_user_dev)); - if (IS_ERR(user_dev)) - return PTR_ERR(user_dev); - - udev->ff_effects_max = user_dev->ff_effects_max; - - /* Ensure name is filled in */ - if (!user_dev->name[0]) { - retval = -EINVAL; - goto exit; - } + udev->ff_effects_max = user_dev2->ff_effects_max; kfree(dev->name); - dev->name = kstrndup(user_dev->name, UINPUT_MAX_NAME_SIZE, + dev->name = kstrndup(user_dev2->name, UINPUT_MAX_NAME_SIZE, GFP_KERNEL); - if (!dev->name) { - retval = -ENOMEM; - goto exit; - } - - dev->id.bustype = user_dev->id.bustype; - dev->id.vendor = user_dev->id.vendor; - dev->id.product = user_dev->id.product; - dev->id.version = user_dev->id.version; + if (!dev->name) + return -ENOMEM; - for (i = 0; i < ABS_CNT; i++) { - input_abs_set_max(dev, i, user_dev->absmax[i]); - input_abs_set_min(dev, i, user_dev->absmin[i]); - input_abs_set_fuzz(dev, i, user_dev->absfuzz[i]); - input_abs_set_flat(dev, i, user_dev->absflat[i]); + dev->id.bustype = user_dev2->id.bustype; + dev->id.vendor = user_dev2->id.vendor; + dev->id.product = user_dev2->id.product; + dev->id.version = user_dev2->id.version; + + for (i = 0; i < abscnt; i++) { + abs = &user_dev2->abs[i]; + input_abs_set_val(dev, i, abs->value); + input_abs_set_max(dev, i, abs->maximum); + input_abs_set_min(dev, i, abs->minimum); + input_abs_set_fuzz(dev, i, abs->fuzz); + input_abs_set_flat(dev, i, abs->flat); + input_abs_set_res(dev, i, abs->resolution); } /* check if absmin/absmax/absfuzz/absflat are filled as @@ -413,7 +405,7 @@ static int uinput_setup_device(struct uinput_device *udev, if (test_bit(EV_ABS, dev->evbit)) { retval = uinput_validate_absbits(dev); if (retval < 0) - goto exit; + return retval; if (test_bit(ABS_MT_SLOT, dev->absbit)) { int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; input_mt_init_slots(dev, nslot, 0); @@ -423,11 +415,84 @@ static int uinput_setup_device(struct uinput_device *udev, } udev->state = UIST_SETUP_COMPLETE; - retval = count; + return 0; +} + +static int uinput_setup_device1(struct uinput_device *udev, + const char __user *buffer, size_t count) +{ + struct uinput_user_dev *user_dev; + struct uinput_user_dev2 *user_dev2; + int i; + int retval; + + if (count != sizeof(struct uinput_user_dev)) + return -EINVAL; + + user_dev = memdup_user(buffer, sizeof(struct uinput_user_dev)); + if (IS_ERR(user_dev)) + return PTR_ERR(user_dev); + + user_dev2 = kmalloc(sizeof(*user_dev2), GFP_KERNEL); + if (!user_dev2) { + kfree(user_dev); + return -ENOMEM; + } + + user_dev2->version = UINPUT_VERSION; + memcpy(user_dev2->name, user_dev->name, UINPUT_MAX_NAME_SIZE); + memcpy(&user_dev2->id, &user_dev->id, sizeof(struct input_id)); + user_dev2->ff_effects_max = user_dev->ff_effects_max; + + for (i = 0; i < ABS_CNT; ++i) { + user_dev2->abs[i].value = 0; + user_dev2->abs[i].maximum = user_dev->absmax[i]; + user_dev2->abs[i].minimum = user_dev->absmin[i]; + user_dev2->abs[i].fuzz = user_dev->absfuzz[i]; + user_dev2->abs[i].flat = user_dev->absflat[i]; + user_dev2->abs[i].resolution = 0; + } + + retval = uinput_setup_device(udev, user_dev2, ABS_CNT); - exit: kfree(user_dev); - return retval; + kfree(user_dev2); + + return retval ? retval : count; +} + +static int uinput_setup_device2(struct uinput_device *udev, + const char __user *buffer, size_t count) +{ + struct uinput_user_dev2 *user_dev2; + int retval; + size_t off, abscnt, max; + + /* The first revision of "uinput_user_dev2" is bigger than + * "uinput_user_dev" and growing. Disallow any smaller payloads. */ + if (count <= sizeof(struct uinput_user_dev)) + return -EINVAL; + + /* rough check to avoid huge kernel space allocations */ + max = ABS_CNT * sizeof(*user_dev2->abs) + sizeof(*user_dev2); + if (count > max) + return -EINVAL; + + user_dev2 = memdup_user(buffer, count); + if (IS_ERR(user_dev2)) + return PTR_ERR(user_dev2); + + if (user_dev2->version > UINPUT_VERSION) { + retval = -EINVAL; + } else { + off = offsetof(struct uinput_user_dev2, abs); + abscnt = (count - off) / sizeof(*user_dev2->abs); + retval = uinput_setup_device(udev, user_dev2, abscnt); + } + + kfree(user_dev2); + + return retval ? retval : count; } static ssize_t uinput_inject_events(struct uinput_device *udev, @@ -469,9 +534,12 @@ static ssize_t uinput_write(struct file *file, const char __user *buffer, if (retval) return retval; - retval = udev->state == UIST_CREATED ? - uinput_inject_events(udev, buffer, count) : - uinput_setup_device(udev, buffer, count); + if (udev->state == UIST_CREATED) + retval = uinput_inject_events(udev, buffer, count); + else if (count <= sizeof(struct uinput_user_dev)) + retval = uinput_setup_device1(udev, buffer, count); + else + retval = uinput_setup_device2(udev, buffer, count); mutex_unlock(&udev->mutex); diff --git a/include/uapi/linux/uinput.h b/include/uapi/linux/uinput.h index fe46431..c2e8710 100644 --- a/include/uapi/linux/uinput.h +++ b/include/uapi/linux/uinput.h @@ -134,4 +134,13 @@ struct uinput_user_dev { __s32 absfuzz[ABS_CNT]; __s32 absflat[ABS_CNT]; }; + +struct uinput_user_dev2 { + __u8 version; + char name[UINPUT_MAX_NAME_SIZE]; + struct input_id id; + __u32 ff_effects_max; + struct input_absinfo abs[ABS_CNT]; +}; + #endif /* _UAPI__UINPUT_H_ */