From patchwork Tue Mar 28 00:01:19 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marcos Paulo de Souza X-Patchwork-Id: 9647715 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 4E2BC602BF for ; Tue, 28 Mar 2017 00:05:53 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3B944271CB for ; Tue, 28 Mar 2017 00:05:53 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2DE4D2815E; Tue, 28 Mar 2017 00:05:53 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.3 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RCVD_IN_SORBS_SPAM, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0F784271CB for ; Tue, 28 Mar 2017 00:05:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753528AbdC1AFv (ORCPT ); Mon, 27 Mar 2017 20:05:51 -0400 Received: from mail-qk0-f194.google.com ([209.85.220.194]:34862 "EHLO mail-qk0-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752814AbdC1AFu (ORCPT ); Mon, 27 Mar 2017 20:05:50 -0400 Received: by mail-qk0-f194.google.com with SMTP id f11so7348744qkb.2; Mon, 27 Mar 2017 17:05:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=hcgooKkVZomdbQR0UnP1eZFmEx/Yg+60XB9gg5kkxFE=; b=U3V/dMYaBMAKhCdG3pLLFyjKRbuy1eShUTknLDC6L+AIQElYpPOQmlBJ4aLJFkh9Gr fFOSyW/wtQCehxIT81nMzz07PVDDO5gevooYKMo6Eb3tsbmwMupTLDPCzV8MWPHucgaP uJjBS6tz6xdU9PoTNsCa5sBTFG7OlL4ZkBz2DmCS0IVJlcE7y2QGCdF4HxaipAiyjsrj 5fOliK+si/eSH6fPxi4sWkIBFlp0uoyxGUVZNrv8ujHn1HXmnb1pYhqqbWT8AMWx1SzN A/nBSS80d/VvgFVEXgfZaxfOuDQp2GlVWsqLyJbLwRMV+SRJc/TiiNZ+xgzGAEFQB7Qs JxoA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=hcgooKkVZomdbQR0UnP1eZFmEx/Yg+60XB9gg5kkxFE=; b=RSsBOrYsEHvfEiPx9BSO/9UYwtgNJ/r2HUa3rri6tOd58TW6REe/8ZDI4K7aouVmIn f9A6wbkag1Ydb0drQ6w8GNgkCGy9PeqyqVxra1wUR1pkhNH29tP7gFfFXOCPPfLBb8i8 h4qQAHRo3WCWgGJ/UjV3eeyIyaIo+7OAskh6jP+71q8VQaEzrUL3aLZ/Hcp5Ul7Rihpw IzD3feKOMIWC1rjVIMRCULxTWoDHv4gNRVEPSjL9BHP97O60DeCOokYxymmoHXvg0/ul 59k9lLILUVuyTXC1XHr3M152naVkgdyEudVm0DBiMjRrxP1K0tJasfapjX5XNutrFBUX Hbug== X-Gm-Message-State: AFeK/H2d+ev/O8hJdx8U3o3r9UrxNGOs8nRHPrEmptFXk9LC2upFa+dYR45qKrLrYCBLHQ== X-Received: by 10.55.221.8 with SMTP id n8mr21303281qki.187.1490659548311; Mon, 27 Mar 2017 17:05:48 -0700 (PDT) Received: from localhost.localdomain ([177.132.129.254]) by smtp.gmail.com with ESMTPSA id k4sm1492712qta.5.2017.03.27.17.05.44 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 27 Mar 2017 17:05:47 -0700 (PDT) From: Marcos Paulo de Souza To: corbet@lwn.net, linux-doc@vger.kernel.org, dmitry.torokhov@gmail.com, linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, peter.hutterer@who-t.net Cc: Marcos Paulo de Souza Subject: [PATCH v4] Documentation: Input: Add uinput documentation Date: Mon, 27 Mar 2017 21:01:19 -0300 Message-Id: <20170328000306.6264-1-marcos.souza.org@gmail.com> X-Mailer: git-send-email 2.9.3 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Marcos Paulo de Souza --- v3 -> v4: Add comment and a sleep call before UI_DEV_DESTROY (requested by Peter) On emit function, add an fd parameter, avoiding global variables Check return of ioctl related to older kernels that don't have UI_GET_VERSION Fix typos v2 -> v3 Changes in libevdev's description (suggested by Peter) Added uinput version check when using the old interface (suggested by Peter) Removed section numbers from sections, sphinx creates these indexes (suggestion by Jon) v1 -> v2: Changes all over the place, including better descriptions (suggested by Peter) Added comments about the need of a sleep call (suggested by Peter) Documentation/input/uinput.rst | 229 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 Documentation/input/uinput.rst diff --git a/Documentation/input/uinput.rst b/Documentation/input/uinput.rst new file mode 100644 index 0000000..47a5c4d --- /dev/null +++ b/Documentation/input/uinput.rst @@ -0,0 +1,229 @@ +============= +uinput module +============= + +Introduction +============ + +uinput is a kernel module that makes it possible to emulate input devices from +userspace. By writing to the module's /dev/uinput (or /dev/input/uinput), a +process can create a virtual device with specific capabilities. +Once created, the process can send events through that virtual device. + +Interface +========= + +:: + + linux/uinput.h + +The uinput header defines ioctls to create, setup and destroy virtual devices. + +libevdev +======== + +libevdev is a wrapper library for evdev devices that provides interfaces to +create uinput devices and send events. libevdev is less error-prone than +accessing uinput directly and should be considered for new software + +For examples and more information about libevdev: +https://www.freedesktop.org/software/libevdev/doc/latest/ + +Examples +======== + +Keyboard events +--------------- + +This first example shows how to create a new virtual device and how to send a +key event. All default imports and error handlers were removed for the sake of +simplicity. + +.. code-block:: c + + #include + + void emit(int fd, int type, int code, int val) + { + struct input_event ie; + + ie.type = type; + ie.code = code; + ie.value = val; + /* below timestamp values are ignored */ + ie.time.tv_sec = 0; + ie.time.tv_usec = 0; + + write(fd, &ie, sizeof(ie)); + } + + int main(void) + { + struct uinput_setup usetup; + + int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); + + /* the ioctls below enables the to be created device to send key + * events, in this case the space key + */ + ioctl(fd, UI_SET_EVBIT, EV_KEY); + ioctl(fd, UI_SET_KEYBIT, KEY_SPACE); + + memset(&usetup, 0, sizeof(usetup)); + usetup.id.bustype = BUS_USB; + usetup.id.vendor = 0x1234; /* sample vendor */ + usetup.id.product = 0x5678; /* sample product */ + strcpy(usetup.name, "Example device"); + + ioctl(fd, UI_DEV_SETUP, &usetup); + ioctl(fd, UI_DEV_CREATE); + + /* + * On UI_DEV_CREATE the kernel creates the device nodes for this device. + * Insert a pause so that userspace has time to detect, initialize the + * new device, and can start to listen to events from this device + */ + sleep(1); + + /* key press, report the event, send key release, and report again */ + emit(fd, EV_KEY, KEY_SPACE, 1); + emit(fd, EV_SYN, SYN_REPORT, 0); + emit(fd, EV_KEY, KEY_SPACE, 0); + emit(fd, EV_SYN, SYN_REPORT, 0); + + /* Give userspace some time to read the events before we destroy the + * device with UI_DEV_DESTOY + */ + sleep(1); + + ioctl(fd, UI_DEV_DESTROY); + close(fd); + + return 0; + } + +Mouse movements +--------------- + +This example shows how to create a virtual device that behaves like a physical +mouse. + +.. code-block:: c + + #include + + /* emit function is identical to of the first example */ + + int main(void) + { + struct uinput_setup usetup; + int i = 50; + + int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); + + /* enable mouse button left and relative events */ + ioctl(fd, UI_SET_EVBIT, EV_KEY); + ioctl(fd, UI_SET_KEYBIT, BTN_LEFT); + + ioctl(fd, UI_SET_EVBIT, EV_REL); + ioctl(fd, UI_SET_RELBIT, REL_X); + ioctl(fd, UI_SET_RELBIT, REL_Y); + + memset(&usetup, 0, sizeof(usetup)); + usetup.id.bustype = BUS_USB; + usetup.id.vendor = 0x1234; /* sample vendor */ + usetup.id.product = 0x5678; /* sample product */ + strcpy(usetup.name, "Example device"); + + ioctl(fd, UI_DEV_SETUP, &usetup); + ioctl(fd, UI_DEV_CREATE); + + /* + * On UI_DEV_CREATE the kernel creates the device nodes for this device. + * Insert a pause so that userspace has time to detect, initialize the + * new device, and can start to listen to events from this device + */ + sleep(1); + + /* moves the mouse diagonally, 5 units per axis */ + while (i--) { + emit(fd, EV_REL, REL_X, 5); + emit(fd, EV_REL, REL_Y, 5); + emit(fd, EV_SYN, SYN_REPORT, 0); + usleep(15000); + } + + /* Give userspace some time to read the events before we destroy the + * device with UI_DEV_DESTOY + */ + sleep(1); + + ioctl(fd, UI_DEV_DESTROY); + close(fd); + + return 0; + } + + +uinput old interface +-------------------- + +Before uinput version 5, there wasn't a proper ioctl to setup a virtual device. +In this case, the user needs to fill a different struct and call write o the +uinput file descriptor to configure the new uinput device. New code should not +use the old interface, and interact with uinput via ioctl calls, or using +libevdev. + +.. code-block:: c + + #include + + /* emit function is identical to of the first example */ + + int main(void) + { + struct uinput_user_dev uud; + int version; + + int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); + ioctl(fd, UI_GET_VERSION, &version); + + if (version >= 4) { + /* + * the ioctls below enables the to be created device to key + * events, in this case the space key + */ + ioctl(fd, UI_SET_EVBIT, EV_KEY); + ioctl(fd, UI_SET_KEYBIT, KEY_SPACE); + + memset(&uud, 0, sizeof(uud)); + snprintf(uud.name, UINPUT_MAX_NAME_SIZE, "uinput old interface"); + write(fd, &uud, sizeof(uud)); + + ioctl(fd, UI_DEV_CREATE); + + /* + * On UI_DEV_CREATE the kernel creates the device nodes for this device + * Insert a pause so that userspace has time to detect, initialize the + * new device, and can start to listen to events from this device + */ + sleep(1); + + /* key press, report the event, send key release, and report again */ + emit(fd, EV_KEY, KEY_SPACE, 1); + emit(fd, EV_SYN, SYN_REPORT, 0); + emit(fd, EV_KEY, KEY_SPACE, 0); + emit(fd, EV_SYN, SYN_REPORT, 0); + + /* Give userspace some time to read the events before we destroy the + * device with UI_DEV_DESTOY + */ + sleep(1); + + ioctl(fd, UI_DEV_DESTROY); + } + + close(fd); + return 0; + } +