From patchwork Sun Sep 1 21:45:18 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Herrmann X-Patchwork-Id: 2852611 X-Patchwork-Delegate: jikos@jikos.cz Return-Path: X-Original-To: patchwork-linux-input@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 4C182C0AB5 for ; Sun, 1 Sep 2013 21:45:52 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 176BB20137 for ; Sun, 1 Sep 2013 21:45:51 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id DE14B200E5 for ; Sun, 1 Sep 2013 21:45:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753551Ab3IAVpt (ORCPT ); Sun, 1 Sep 2013 17:45:49 -0400 Received: from mail-ea0-f173.google.com ([209.85.215.173]:55517 "EHLO mail-ea0-f173.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753100Ab3IAVps (ORCPT ); Sun, 1 Sep 2013 17:45:48 -0400 Received: by mail-ea0-f173.google.com with SMTP id g10so1982792eak.32 for ; Sun, 01 Sep 2013 14:45:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id; bh=pP0uaNGN+vijoSzKWAEBmeMV3skB3LfQexAGUazuf+w=; b=UzfZ0bif2EtuKaGbS2xXMB/3n3193Bd8k8RWHLvKj2fLQ2rls0sKDJT8hBNQodH96r /g8mk+drcHGYLWdlT774/UAI/23sJhWEW/S/N/8UVLLOqtLPCAQLtwpJYaQOOOpT5Fm4 ALj31vBBTpsGicWcQppO4juXLQFJOk7Xvheorb0g3CAFmGko2JqXIzZbK3zP4nGHqCJX tWcC4scxGlagHiXTUZtzlGbtLTxmrGJw44nYkxAU6IDc5mFe9xyxPNb0r6NTxdxj33Kt J8+lgQhDrBqezSYLrETh+XIrLprBbBbybCFiBxF+d4+JwfNDjFH7ZLS3Wpk5HZ9IGXws dydA== X-Received: by 10.15.75.73 with SMTP id k49mr31361667eey.36.1378071947092; Sun, 01 Sep 2013 14:45:47 -0700 (PDT) Received: from localhost.localdomain (stgt-5f719c52.pool.mediaWays.net. [95.113.156.82]) by mx.google.com with ESMTPSA id x47sm16544731eea.16.1969.12.31.16.00.00 (version=TLSv1.2 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Sun, 01 Sep 2013 14:45:46 -0700 (PDT) From: David Herrmann To: linux-input@vger.kernel.org Cc: Jiri Kosina , Anderson Lizardo , David Herrmann Subject: [PATCH] hid: uhid: improve uhid example client Date: Sun, 1 Sep 2013 23:45:18 +0200 Message-Id: <1378071918-1152-1-git-send-email-dh.herrmann@gmail.com> X-Mailer: git-send-email 1.8.4 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Spam-Status: No, score=-9.2 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=ham 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 This extends the uhid example client. It properly documents the built-in report-descriptor an adds explicit report-numbers. Furthermore, LED output reports are added to utilize the new UHID output reports of the kernel. Support for 3 basic LEDs is added and a small report-parser to print debug messages if output reports were received. To test this, simply write the EV_LED+LED_CAPSL+1 event to the evdev device-node of the uhid-device and the kernel will forward it to your uhid client. Signed-off-by: David Herrmann --- Hi Jiri This is actually the first time that I wrote an HID report-descriptor. The old 3-button mouse was just copied from a real mouse. My descriptor works perfectly well but I am kind of scared I am no longer allowed to make fun of firmware authors.. Anyway, I extended the example to have a test-case for uhid output reports and it all works as expected. I got asked for some help on this so I wrote up this patch to push it mainline. Cheers David #include #include #include #include #include #include int main(int argc, char **argv) { int fd, r; struct input_event ev; assert(argc >= 3); fd = open(argv[1], O_RDWR | O_CLOEXEC); assert(fd >= 0); memset(&ev, 0, sizeof(ev)); ev.type = EV_LED; ev.code = LED_CAPSL; ev.value = atoi(argv[2]); r = write(fd, &ev, sizeof(ev)); assert(r == sizeof(ev)); close(fd); return 0; } samples/uhid/uhid-example.c | 123 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 103 insertions(+), 20 deletions(-) diff --git a/samples/uhid/uhid-example.c b/samples/uhid/uhid-example.c index 03ce3c0..7d58a4b 100644 --- a/samples/uhid/uhid-example.c +++ b/samples/uhid/uhid-example.c @@ -1,14 +1,15 @@ /* * UHID Example * - * Copyright (c) 2012 David Herrmann + * Copyright (c) 2012-2013 David Herrmann * * The code may be used by anyone for any purpose, * and can serve as a starting point for developing * applications using uhid. */ -/* UHID Example +/* + * UHID Example * This example emulates a basic 3 buttons mouse with wheel over UHID. Run this * program as root and then use the following keys to control the mouse: * q: Quit the application @@ -22,6 +23,11 @@ * r: Move wheel up * f: Move wheel down * + * Additionally to 3 button mouse, 3 keyboard LEDs are also supported (LED_NUML, + * LED_CAPSL and LED_SCROLLL). The device doesn't generate any related keyboard + * events, though. You need to manually write the EV_LED/LED_XY/1 activation + * input event to the evdev device to see it being sent to this device. + * * If uhid is not available as /dev/uhid, then you can pass a different path as * first argument. * If is not installed in /usr, then compile this with: @@ -41,11 +47,12 @@ #include #include -/* HID Report Desciptor - * We emulate a basic 3 button mouse with wheel. This is the report-descriptor - * as the kernel will parse it: +/* + * HID Report Desciptor + * We emulate a basic 3 button mouse with wheel and 3 keyboard LEDs. This is + * the report-descriptor as the kernel will parse it: * - * INPUT[INPUT] + * INPUT(1)[INPUT] * Field(0) * Physical(GenericDesktop.Pointer) * Application(GenericDesktop.Mouse) @@ -72,6 +79,19 @@ * Report Count(3) * Report Offset(8) * Flags( Variable Relative ) + * OUTPUT(2)[OUTPUT] + * Field(0) + * Application(GenericDesktop.Keyboard) + * Usage(3) + * LED.NumLock + * LED.CapsLock + * LED.ScrollLock + * Logical Minimum(0) + * Logical Maximum(1) + * Report Size(1) + * Report Count(3) + * Report Offset(0) + * Flags( Variable Absolute ) * * This is the mapping that we expect: * Button.0001 ---> Key.LeftBtn @@ -80,19 +100,59 @@ * GenericDesktop.X ---> Relative.X * GenericDesktop.Y ---> Relative.Y * GenericDesktop.Wheel ---> Relative.Wheel + * LED.NumLock ---> LED.NumLock + * LED.CapsLock ---> LED.CapsLock + * LED.ScrollLock ---> LED.ScrollLock * * This information can be verified by reading /sys/kernel/debug/hid//rdesc * This file should print the same information as showed above. */ static unsigned char rdesc[] = { - 0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x09, 0x01, - 0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03, - 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, - 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01, - 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x38, - 0x15, 0x80, 0x25, 0x7f, 0x75, 0x08, 0x95, 0x03, - 0x81, 0x06, 0xc0, 0xc0, + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x02, /* USAGE (Mouse) */ + 0xa1, 0x01, /* COLLECTION (Application) */ + 0x09, 0x01, /* USAGE (Pointer) */ + 0xa1, 0x00, /* COLLECTION (Physical) */ + 0x85, 0x01, /* REPORT_ID (1) */ + 0x05, 0x09, /* USAGE_PAGE (Button) */ + 0x19, 0x01, /* USAGE_MINIMUM (Button 1) */ + 0x29, 0x03, /* USAGE_MAXIMUM (Button 3) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x95, 0x03, /* REPORT_COUNT (3) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x81, 0x02, /* INPUT (Data,Var,Abs) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x75, 0x05, /* REPORT_SIZE (5) */ + 0x81, 0x01, /* INPUT (Cnst,Var,Abs) */ + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x30, /* USAGE (X) */ + 0x09, 0x31, /* USAGE (Y) */ + 0x09, 0x38, /* USAGE (WHEEL) */ + 0x15, 0x81, /* LOGICAL_MINIMUM (-127) */ + 0x25, 0x7f, /* LOGICAL_MAXIMUM (127) */ + 0x75, 0x08, /* REPORT_SIZE (8) */ + 0x95, 0x03, /* REPORT_COUNT (3) */ + 0x81, 0x06, /* INPUT (Data,Var,Rel) */ + 0xc0, /* END_COLLECTION */ + 0xc0, /* END_COLLECTION */ + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x06, /* USAGE (Keyboard) */ + 0xa1, 0x01, /* COLLECTION (Application) */ + 0x85, 0x02, /* REPORT_ID (2) */ + 0x05, 0x08, /* USAGE_PAGE (Led) */ + 0x19, 0x01, /* USAGE_MINIMUM (1) */ + 0x29, 0x03, /* USAGE_MAXIMUM (3) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x95, 0x03, /* REPORT_COUNT (3) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x91, 0x02, /* Output (Data,Var,Abs) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x75, 0x05, /* REPORT_SIZE (5) */ + 0x91, 0x01, /* Output (Cnst,Var,Abs) */ + 0xc0, /* END_COLLECTION */ }; static int uhid_write(int fd, const struct uhid_event *ev) @@ -140,6 +200,27 @@ static void destroy(int fd) uhid_write(fd, &ev); } +/* This parses raw output reports sent by the kernel to the device. A normal + * uhid program shouldn't do this but instead just forward the raw report. + * However, for ducomentational purposes, we try to detect LED events here and + * print debug messages for it. */ +static void handle_output(struct uhid_event *ev) +{ + /* LED messages are adverised via OUTPUT reports; ignore the rest */ + if (ev->u.output.rtype != UHID_OUTPUT_REPORT) + return; + /* LED reports have length 2 bytes */ + if (ev->u.output.size != 2) + return; + /* first byte is report-id which is 0x02 for LEDs in our rdesc */ + if (ev->u.output.data[0] != 0x2) + return; + + /* print flags payload */ + fprintf(stderr, "LED output report received with flags %x\n", + ev->u.output.data[1]); +} + static int event(int fd) { struct uhid_event ev; @@ -174,6 +255,7 @@ static int event(int fd) break; case UHID_OUTPUT: fprintf(stderr, "UHID_OUTPUT from uhid-dev\n"); + handle_output(&ev); break; case UHID_OUTPUT_EV: fprintf(stderr, "UHID_OUTPUT_EV from uhid-dev\n"); @@ -198,18 +280,19 @@ static int send_event(int fd) memset(&ev, 0, sizeof(ev)); ev.type = UHID_INPUT; - ev.u.input.size = 4; + ev.u.input.size = 5; + ev.u.input.data[0] = 0x1; if (btn1_down) - ev.u.input.data[0] |= 0x1; + ev.u.input.data[1] |= 0x1; if (btn2_down) - ev.u.input.data[0] |= 0x2; + ev.u.input.data[1] |= 0x2; if (btn3_down) - ev.u.input.data[0] |= 0x4; + ev.u.input.data[1] |= 0x4; - ev.u.input.data[1] = abs_hor; - ev.u.input.data[2] = abs_ver; - ev.u.input.data[3] = wheel; + ev.u.input.data[2] = abs_hor; + ev.u.input.data[3] = abs_ver; + ev.u.input.data[4] = wheel; return uhid_write(fd, &ev); }