From patchwork Thu May 20 00:22:47 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kenneth Albanowski X-Patchwork-Id: 12268695 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id E85E7C433B4 for ; Thu, 20 May 2021 00:23:20 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id B2D2D6008E for ; Thu, 20 May 2021 00:23:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229808AbhETAYk (ORCPT ); Wed, 19 May 2021 20:24:40 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:35522 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229525AbhETAYj (ORCPT ); Wed, 19 May 2021 20:24:39 -0400 Received: from mail-yb1-xb4a.google.com (mail-yb1-xb4a.google.com [IPv6:2607:f8b0:4864:20::b4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 808CDC061574 for ; Wed, 19 May 2021 17:23:16 -0700 (PDT) Received: by mail-yb1-xb4a.google.com with SMTP id s5-20020a252d450000b02905161dc6c7e1so3582490ybe.23 for ; Wed, 19 May 2021 17:23:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=8e4OVut70ZQa0ZDUJJCtb+WnyeU95+VW5Jt/vnIGs1o=; b=bGXRfNP4lhUMkXycO2t4wKMaT6gSoW4u6JS+bnluKieKSni//7PtfFGGatr0fHW+sr Rt61gbxrxKtWAmNxnSHmv/6BE+nQ0gkj5U4LzBB2pstXpqNoqZvhJK0nXHonO0Sx6TXh H7tGhS06Avt4L781wYs+0PEE4YdfXHu42hz4U7KWAnjvvad1gpsZutVSl8UlscZeXA29 XuRTLqnPCSqWOE6qh/fqzRXB/QwP47NPtLGGGDnQK1rxToCVziChNVkgEd9AY0DMZv+Y bd8GVh0xLk43VNrLUPUQ4AvP5gYTeClP9kQLCoZhtDf8N0HV9BicX8OVKxfVRI4/jdqJ HWmQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=8e4OVut70ZQa0ZDUJJCtb+WnyeU95+VW5Jt/vnIGs1o=; b=caJX6nMYXsRHmYspBvPz6UvNS3Q6ALlC12jPMJT92LAXlICW+Q/GTTzs/yP52RMEHz +0jd7f5F5gA/+5c4RFX3YqUM8IEQqKrczjGLwJxesD4hmzhf4QLL+n+yn8KZnAFqCfyP 8EEqrZERAMtL5+HK1btug1lgPI4ongzZniw+6ix70tpum8v8+ni4jsPWlke0QSuFTeIa fgQuqpIDvJUuhn9anYsmQiKISb/OgBsPH8ogV8f3qTIvuCqEBVCB08bvw65IJcI+BO3H Fiux4Sqp8BRDViXCW7BR/yhaIrgq8eBewd2GstWtqhdbgzXpPZB0Sx7Es3zAsP21T5lU eUtA== X-Gm-Message-State: AOAM533PWk5QmnkVaqJmecPIlcaYs1Mx+LfVb+3O3PYAR6v/H2MQNIyH KpsF4KJANE0wIeLQv2LEXfAIC43W4CqpHkXdnHg30jme+pZrL3inEp7nWck6Mba645IsTi/e0jw ZwqLR2ON/LyBO8xFIHNX+FPnVrPkYk4vmT6wORvikE4xIcAO67VTEl2SfPEBHACyoIip7DMw= X-Google-Smtp-Source: ABdhPJzAOHna+Qbnxjti5LFCW4HLvYs2ov8fiLSqjr+7T2rYQoGUrRSB44fGLg/r2zF5BxtQnVSKiHuls5g2 X-Received: from kenalba1.mtv.corp.google.com ([2620:15c:203:200:4404:83b2:506f:fd71]) (user=kenalba job=sendgmr) by 2002:a25:b78c:: with SMTP id n12mr3372016ybh.291.1621470195636; Wed, 19 May 2021 17:23:15 -0700 (PDT) Date: Wed, 19 May 2021 17:22:47 -0700 In-Reply-To: <20210520002249.361821-1-kenalba@google.com> Message-Id: <20210519143836.1.I8e23cff04fc537877668462d98d5054bac10d940@changeid> Mime-Version: 1.0 References: <20210520002249.361821-1-kenalba@google.com> X-Mailer: git-send-email 2.31.1.818.g46aad6cb9e-goog Subject: [PATCH 1/3] [hid] Minor cleanup From: Kenneth Albanowski To: "open list:HID CORE LAYER" Cc: Dmitry Torokhov , Benjamin Tissoires , Jiri Kosina , Peter Hutterer , Jason Gerecke , Kenneth Albanowski Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org No functional changes, just cleanup. Clarify actual integer size limits in a few comments. Make one constant explicit to improve readability. Replace open-coded sign-extension with common routine. Signed-off-by: Kenneth Albanowski --- drivers/hid/hid-core.c | 19 +++++++------------ include/linux/hid.h | 3 ++- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 5b30783e2353d..19b2b0aae0c7e 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -337,7 +337,8 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign } /* - * Read data value from item. + * Read data value from global items, which are + * a maximum of 32 bits in size. */ static u32 item_udata(struct hid_item *item) @@ -709,7 +710,7 @@ static void hid_device_release(struct device *dev) /* * Fetch a report description item from the data stream. We support long - * items, though they are not used yet. + * items, though there are not yet any defined uses for them. */ static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item) @@ -745,6 +746,7 @@ static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item) item->format = HID_ITEM_FORMAT_SHORT; item->size = b & 3; + /* Map size values 0,1,2,3 to actual sizes 0,1,2,4 */ switch (item->size) { case 0: return start; @@ -763,7 +765,7 @@ static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item) return start; case 3: - item->size++; + item->size = 4; if ((end - start) < 4) return NULL; item->data.u32 = get_unaligned_le32(start); @@ -1300,9 +1302,7 @@ int hid_open_report(struct hid_device *device) EXPORT_SYMBOL_GPL(hid_open_report); /* - * Convert a signed n-bit integer to signed 32-bit integer. Common - * cases are done through the compiler, the screwed things has to be - * done by hand. + * Convert a signed n-bit integer to signed 32-bit integer. */ static s32 snto32(__u32 value, unsigned n) @@ -1310,12 +1310,7 @@ static s32 snto32(__u32 value, unsigned n) if (!value || !n) return 0; - switch (n) { - case 8: return ((__s8)value); - case 16: return ((__s16)value); - case 32: return ((__s32)value); - } - return value & (1 << (n - 1)) ? value | (~0U << n) : value; + return sign_extend32(value, n - 1); } s32 hid_snto32(__u32 value, unsigned n) diff --git a/include/linux/hid.h b/include/linux/hid.h index 7a13e9d3441a4..59828a6080e83 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -391,6 +391,7 @@ struct hid_item { struct hid_global { unsigned usage_page; + /* HID Global fields are constrained by spec to 32-bits */ __s32 logical_minimum; __s32 logical_maximum; __s32 physical_minimum; @@ -457,7 +458,7 @@ struct hid_field { unsigned maxusage; /* maximum usage index */ unsigned flags; /* main-item flags (i.e. volatile,array,constant) */ unsigned report_offset; /* bit offset in the report */ - unsigned report_size; /* size of this field in the report */ + unsigned report_size; /* size of this field in the report, in bits */ unsigned report_count; /* number of this field in the report */ unsigned report_type; /* (input,output,feature) */ __s32 *value; /* last known value(s) */ From patchwork Thu May 20 00:22:48 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kenneth Albanowski X-Patchwork-Id: 12268697 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1CC30C43460 for ; Thu, 20 May 2021 00:23:23 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id EA5C86008E for ; Thu, 20 May 2021 00:23:22 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229953AbhETAYl (ORCPT ); Wed, 19 May 2021 20:24:41 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:35532 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229525AbhETAYl (ORCPT ); Wed, 19 May 2021 20:24:41 -0400 Received: from mail-qv1-xf49.google.com (mail-qv1-xf49.google.com [IPv6:2607:f8b0:4864:20::f49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 247C0C061574 for ; Wed, 19 May 2021 17:23:20 -0700 (PDT) Received: by mail-qv1-xf49.google.com with SMTP id bc3-20020ad456830000b02901f47dbd7ef6so1826804qvb.6 for ; Wed, 19 May 2021 17:23:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=ZLOR33UPVhxDYUxNdoVaJuwz26aIKW+kNnu2OBb4Wco=; b=r1gK0lN5H/ZJ3Rm+UEYOJHJyYvkhQcs2yF3A0vWFiVFIj8kQQS6BOJywHxlsOQPi5H wRryVfBt1QPqItzjQD2JyiZUdCu3fYmR4FOP7wEgurSyBbDq1Y40fAU6aSLOdZpLnooP c41UlmUvgzMdkeucvQqJyQv8QJYTUM9JkK1bCnok2rnpoTplFTfTrldzkB5lpyBnV876 emENsgUfLoh6cU1Sxnp6VFWBrcsiSCGmB0eTM/gSwWDQFMWbGBw6l1cGWh0CdAzfzvH9 i3DtOt6Dl5QEI3P5XD/vgDCzCcprgZJ+INElcuo0GRzAsCB4R5V8lhjHwqFFzH6tou4M QcIA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=ZLOR33UPVhxDYUxNdoVaJuwz26aIKW+kNnu2OBb4Wco=; b=OU9RF1gGg9g3RjDPwGHjJ3XB9TBE6l7o+EnfiuBIbtyPdxgxnrUUvJbNaq4zgF+Kr+ VjY55+eAWwgKcf1b+YG36EQZJI6dadISUddoNFOnOLjqpSma8LA/zYjac4ptW70fBVR/ zA5DXOvGJmVI/njyBU1GzEDup35J+MnVcewJ24KGOg2ytin87gcZY7s1G79oHDeijLJc sBaZlwI58mePIr3Z8sG4dEgcfGmiE+8nURT3dVEV2m2xZ2guJp1BNEWE1JsdVuKGF8eY LX8mUkdfIUiPAVgNFKnbSPVezdP2rSZ8bXg+UW8/iVhlIsCsVBGQQssqf/+vjtTKdTF/ Y9kg== X-Gm-Message-State: AOAM531LNp6uxIRGs1m1cfPr01sih+F0IZzTD//3bUXm/IcMN7QIkfK7 H4PL4x6jcztbtSV6mXNdDHuHn188KwNoDw9w0DiaaJhguPQ+q72L+PyEuuASwmNJhQkLTTDXFoC yn3F81GM8mZX6k16ELd2AOjj6FXuFCTHBCj3mY3Kfg393tFrTbvmFOeX2ixZ/t8fZci0FU38= X-Google-Smtp-Source: ABdhPJyt4ojNrkn8gaLajRMcN3I+aQf7Kf8KuWru8fieAQpzDgoj5rVpBqsCjuu5SUAyl8jZ1MI837whXNub X-Received: from kenalba1.mtv.corp.google.com ([2620:15c:203:200:4404:83b2:506f:fd71]) (user=kenalba job=sendgmr) by 2002:a05:6214:b27:: with SMTP id w7mr2606582qvj.40.1621470199160; Wed, 19 May 2021 17:23:19 -0700 (PDT) Date: Wed, 19 May 2021 17:22:48 -0700 In-Reply-To: <20210520002249.361821-1-kenalba@google.com> Message-Id: <20210519143836.2.I62308b62b6ebddf086c125fbf1a5aa159bda891a@changeid> Mime-Version: 1.0 References: <20210520002249.361821-1-kenalba@google.com> X-Mailer: git-send-email 2.31.1.818.g46aad6cb9e-goog Subject: [PATCH 2/3] [hid] Enable processing of fields larger than 32 bits. From: Kenneth Albanowski To: "open list:HID CORE LAYER" Cc: Dmitry Torokhov , Benjamin Tissoires , Jiri Kosina , Peter Hutterer , Jason Gerecke , Kenneth Albanowski Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org Extends hid-core to process HID fields larger than 32 bits, by storing them as multiple consecutive 32-bit values. hid-input and hid-debug are specifically changed to match. Currently, no other hid consumers get an interface to see fields larger than 32 bits. All existing code, including hid-input processing, will by default see the least significant 32-bits of the field, so they have unchanged behaviour (sign extension does not change: for any field longer than 32 bits, extension was already a no-op, so the least significant 32-bit value will be identical. The most significant value, at the far end, will now be extended to fill its 32-bit value.) Logical min/max interaction with larger fields is limited, as min/max and other item parameters can only be described with 32-bit values (sometimes signed). hid-input is expected to ignore min/max in scenarios where a specific larger field is involved. hid-debug 'events' debugfs text report format is changed, but only for HID fields larger than 32 bits. Signed-off-by: Kenneth Albanowski --- Documentation/hid/hiddev.rst | 6 ++- drivers/hid/hid-core.c | 80 ++++++++++++++++++++++++++---------- drivers/hid/hid-debug.c | 27 ++++++++---- drivers/hid/hid-input.c | 10 ++++- include/linux/hid-debug.h | 4 +- include/linux/hid.h | 2 +- 6 files changed, 92 insertions(+), 37 deletions(-) diff --git a/Documentation/hid/hiddev.rst b/Documentation/hid/hiddev.rst index 209e6ba4e019e..f22fcf1b55b9e 100644 --- a/Documentation/hid/hiddev.rst +++ b/Documentation/hid/hiddev.rst @@ -72,8 +72,10 @@ The hiddev API uses a read() interface, and a set of ioctl() calls. HID devices exchange data with the host computer using data bundles called "reports". Each report is divided into "fields", -each of which can have one or more "usages". In the hid-core, -each one of these usages has a single signed 32 bit value. +each of which can have one or more "usages". Each of these usages +has a value, usually a 32 bit or smaller signed value. (The +hid-core can process larger values, but these are not currently +exposed through hiddev.) read(): ------- diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 19b2b0aae0c7e..e588c3a35a593 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -86,22 +86,35 @@ struct hid_report *hid_register_report(struct hid_device *device, } EXPORT_SYMBOL_GPL(hid_register_report); +// How many 32-bit values are needed to store a field? +static unsigned hid_field_size_in_values(unsigned flags, unsigned size_in_bits) +{ + if (!(flags & HID_MAIN_ITEM_VARIABLE)) { + return 1; + } else { + return DIV_ROUND_UP(size_in_bits, 32); + } +} + /* * Register a new field for this report. */ -static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages) +static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages, unsigned flags, unsigned size_in_bits) { struct hid_field *field; + unsigned size_in_values; if (report->maxfield == HID_MAX_FIELDS) { hid_err(report->device, "too many fields in report\n"); return NULL; } + size_in_values = hid_field_size_in_values(flags, size_in_bits); + field = kzalloc((sizeof(struct hid_field) + usages * sizeof(struct hid_usage) + - usages * sizeof(unsigned)), GFP_KERNEL); + usages * size_in_values * sizeof(s32)), GFP_KERNEL); if (!field) return NULL; @@ -300,7 +313,7 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign usages = max_t(unsigned, parser->local.usage_index, parser->global.report_count); - field = hid_register_field(report, usages); + field = hid_register_field(report, usages, flags, parser->global.report_size); if (!field) return 0; @@ -1340,6 +1353,9 @@ static u32 s32ton(__s32 value, unsigned n) * While the USB HID spec allows unlimited length bit fields in "report * descriptors", most devices never use more than 16 bits. * One model of UPS is claimed to report "LINEV" as a 32-bit field. + * Some digitizers report stylus transducer IDs as 64-bit fields. + * The outer routines will extract multiple 32-bit parts if necessary + * to retrieve an entire field. * Search linux-kernel and linux-usb-devel archives for "hid-core extract". */ @@ -1495,16 +1511,19 @@ static int hid_match_usage(struct hid_device *hid, struct hid_usage *usage) } static void hid_process_event(struct hid_device *hid, struct hid_field *field, - struct hid_usage *usage, __s32 value, int interrupt) + struct hid_usage *usage, const __s32 * values, unsigned value_count, int interrupt) { struct hid_driver *hdrv = hid->driver; int ret; + if (unlikely(value_count == 0)) + return; + if (!list_empty(&hid->debug_list)) - hid_dump_input(hid, usage, value); + hid_dump_input(hid, usage, values, value_count); if (hdrv && hdrv->event && hid_match_usage(hid, usage)) { - ret = hdrv->event(hid, field, usage, value); + ret = hdrv->event(hid, field, usage, values[0]); if (ret != 0) { if (ret < 0) hid_err(hid, "%s's event failed with %d\n", @@ -1514,9 +1533,9 @@ static void hid_process_event(struct hid_device *hid, struct hid_field *field, } if (hid->claimed & HID_CLAIMED_INPUT) - hidinput_hid_event(hid, field, usage, value); + hidinput_hid_event(hid, field, usage, values, value_count); if (hid->claimed & HID_CLAIMED_HIDDEV && interrupt && hid->hiddev_hid_event) - hid->hiddev_hid_event(hid, field, usage, value); + hid->hiddev_hid_event(hid, field, usage, values[0]); } /* @@ -1528,24 +1547,41 @@ static void hid_process_event(struct hid_device *hid, struct hid_field *field, static void hid_input_field(struct hid_device *hid, struct hid_field *field, __u8 *data, int interrupt) { - unsigned n; + unsigned n, v; unsigned count = field->report_count; unsigned offset = field->report_offset; - unsigned size = field->report_size; + unsigned size_in_bits = field->report_size; + unsigned size_in_values; /* storage size in 32-bit values, always >= 1 */ + unsigned last_value_size_in_bits; /* bits in most significant/last value */ + unsigned bit_pos; __s32 min = field->logical_minimum; __s32 max = field->logical_maximum; __s32 *value; + static const __s32 zero = 0; + static const __s32 one = 1; + + size_in_values = hid_field_size_in_values(field->flags, size_in_bits); + last_value_size_in_bits = (size_in_bits % 32) ?: 32; - value = kmalloc_array(count, sizeof(__s32), GFP_ATOMIC); + value = kmalloc_array(count * size_in_values, sizeof(__s32), GFP_ATOMIC); if (!value) return; - for (n = 0; n < count; n++) { + for (n = 0; n < count * size_in_values; n += size_in_values) { + v = 0; + bit_pos = offset + (n * size_in_bits); + + // Extract least significant values for fields longer than 32 bits. + if (size_in_values > 1) { + for (; v < size_in_values - 1; v++, bit_pos += 32) + value[n+v] = hid_field_extract(hid, data, bit_pos, 32); + } - value[n] = min < 0 ? - snto32(hid_field_extract(hid, data, offset + n * size, - size), size) : - hid_field_extract(hid, data, offset + n * size, size); + // May need to sign extend the most significant value. + value[n+v] = min < 0 ? + sign_extend32(hid_field_extract(hid, data, bit_pos, + last_value_size_in_bits), last_value_size_in_bits) : + hid_field_extract(hid, data, bit_pos, last_value_size_in_bits); /* Ignore report if ErrorRollOver */ if (!(field->flags & HID_MAIN_ITEM_VARIABLE) && @@ -1555,10 +1591,10 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field, goto exit; } - for (n = 0; n < count; n++) { + for (n = 0; n < count * size_in_values; n += size_in_values) { if (HID_MAIN_ITEM_VARIABLE & field->flags) { - hid_process_event(hid, field, &field->usage[n], value[n], interrupt); + hid_process_event(hid, field, &field->usage[n], value + n, size_in_values, interrupt); continue; } @@ -1566,16 +1602,16 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field, && field->value[n] - min < field->maxusage && field->usage[field->value[n] - min].hid && search(value, field->value[n], count)) - hid_process_event(hid, field, &field->usage[field->value[n] - min], 0, interrupt); + hid_process_event(hid, field, &field->usage[field->value[n] - min], &zero, 1, interrupt); if (value[n] >= min && value[n] <= max && value[n] - min < field->maxusage && field->usage[value[n] - min].hid && search(field->value, value[n], count)) - hid_process_event(hid, field, &field->usage[value[n] - min], 1, interrupt); + hid_process_event(hid, field, &field->usage[value[n] - min], &one, 1, interrupt); } - memcpy(field->value, value, count * sizeof(__s32)); + memcpy(field->value, value, count * size_in_values * sizeof(__s32)); exit: kfree(value); } @@ -1662,7 +1698,7 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value) size = field->report_size; - hid_dump_input(field->report->device, field->usage + offset, value); + hid_dump_input(field->report->device, field->usage + offset, &value, 1); if (offset >= field->report_count) { hid_err(field->report->device, "offset (%d) exceeds report_count (%d)\n", diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 9453147d020db..bc25b0e8b6b63 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -692,21 +692,30 @@ void hid_dump_report(struct hid_device *hid, int type, u8 *data, } EXPORT_SYMBOL_GPL(hid_dump_report); -void hid_dump_input(struct hid_device *hdev, struct hid_usage *usage, __s32 value) +void hid_dump_input(struct hid_device *hdev, struct hid_usage *usage, const __s32 *values, unsigned value_count) { char *buf; int len; + unsigned n; - buf = hid_resolv_usage(usage->hid, NULL); - if (!buf) - return; - len = strlen(buf); - snprintf(buf + len, HID_DEBUG_BUFSIZE - len - 1, " = %d\n", value); + for (n = 0; n < value_count; n++) { - hid_debug_event(hdev, buf); + buf = hid_resolv_usage(usage->hid, NULL); + if (!buf) + return; - kfree(buf); - wake_up_interruptible(&hdev->debug_wait); + len = strlen(buf); + + if (value_count == 1) + snprintf(buf + len, HID_DEBUG_BUFSIZE - len - 1, " = %d\n", values[n]); + else + snprintf(buf + len, HID_DEBUG_BUFSIZE - len - 1, "[%d] = %d (%08x)\n", n, values[n], values[n]); + + hid_debug_event(hdev, buf); + + kfree(buf); + wake_up_interruptible(&hdev->debug_wait); + } } EXPORT_SYMBOL_GPL(hid_dump_input); diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index bceccd75b488e..ee9e8d31a45ba 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1273,10 +1273,18 @@ static void hidinput_handle_scroll(struct hid_usage *usage, input_event(input, EV_REL, usage->code, hi_res); } -void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) +void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, const __s32 *values, unsigned value_count) { struct input_dev *input; unsigned *quirks = &hid->quirks; + __s32 value; + + if (unlikely(value_count == 0)) + return; + + // The majority of this code was writen to only understand 32-bit sized + // values, and anything larger was truncated: we continue that tradition. + value = values[0]; if (!usage->type) return; diff --git a/include/linux/hid-debug.h b/include/linux/hid-debug.h index ea7b23d13bfdf..b4fb1a73817b4 100644 --- a/include/linux/hid-debug.h +++ b/include/linux/hid-debug.h @@ -16,7 +16,7 @@ #define HID_DEBUG_BUFSIZE 512 #define HID_DEBUG_FIFOSIZE 512 -void hid_dump_input(struct hid_device *, struct hid_usage *, __s32); +void hid_dump_input(struct hid_device *, struct hid_usage *, const __s32 *, unsigned); void hid_dump_report(struct hid_device *, int , u8 *, int); void hid_dump_device(struct hid_device *, struct seq_file *); void hid_dump_field(struct hid_field *, int, struct seq_file *); @@ -37,7 +37,7 @@ struct hid_debug_list { #else -#define hid_dump_input(a,b,c) do { } while (0) +#define hid_dump_input(a,b,c,d) do { } while (0) #define hid_dump_report(a,b,c,d) do { } while (0) #define hid_dump_device(a,b) do { } while (0) #define hid_dump_field(a,b,c) do { } while (0) diff --git a/include/linux/hid.h b/include/linux/hid.h index 59828a6080e83..8494b1995b10b 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -878,7 +878,7 @@ extern void hid_unregister_driver(struct hid_driver *); module_driver(__hid_driver, hid_register_driver, \ hid_unregister_driver) -extern void hidinput_hid_event(struct hid_device *, struct hid_field *, struct hid_usage *, __s32); +extern void hidinput_hid_event(struct hid_device *, struct hid_field *, struct hid_usage *, const __s32 *values, unsigned value_count); extern void hidinput_report_event(struct hid_device *hid, struct hid_report *report); extern int hidinput_connect(struct hid_device *hid, unsigned int force); extern void hidinput_disconnect(struct hid_device *); From patchwork Thu May 20 00:22:49 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kenneth Albanowski X-Patchwork-Id: 12268699 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A4FCBC433ED for ; Thu, 20 May 2021 00:23:25 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 81CAD6008E for ; Thu, 20 May 2021 00:23:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229955AbhETAYp (ORCPT ); Wed, 19 May 2021 20:24:45 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:35546 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229525AbhETAYo (ORCPT ); Wed, 19 May 2021 20:24:44 -0400 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6D705C061574 for ; Wed, 19 May 2021 17:23:23 -0700 (PDT) Received: by mail-yb1-xb49.google.com with SMTP id 129-20020a2504870000b0290513326cc5e0so7543727ybe.10 for ; Wed, 19 May 2021 17:23:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=AenL15v8bpaPcpQGDiRTmDFhfPfdIWtA5+WOLaYpXgw=; b=VvcQtCUz9ro5f/2GJPPyTD4MOGVxvgaJgQJRgMlwvvVGz7PAAvKZpLujlAY+9C68Bg B5DY04hCio/63XC1NHKynQL1c7hZpSVf/+ScVEwp7UpeuybzDuP09qIkJIOiZmCP7YYd b9VD78GBCfTVEVYO/nnJvz3L/CBIxB6qqb3IE20374UAfE5Y9/DTNAdE2hBUQviuQn77 tqE7IEOkTINGr7jjNpJXwoaDOE/rzBkxtp1UQMjVX14pIvdFFygpx3Y/AkVdwT0xXpOL T9o1CtTzx3Ivq8c+KVs9+8oFfWSkrKFo+XAE7zd8dA8Y9AXGtKgixXdH9nOZZlCEqjNj lRHA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=AenL15v8bpaPcpQGDiRTmDFhfPfdIWtA5+WOLaYpXgw=; b=dAZTO94QpeqasyQ+ehnxCUrLoa+s5am+Qwhq5kvF12e9+tUU5baWVVwUAc0Str05pw mKxyR3gTev90pXaZL3kclWocW2b9HgPy+eGopvcKWzQzXPtXDVOXOmmg6HSZx7NPIMig dha0LyZErfPkKR6LLjjQCFyUwG45PiHN93/dXigkDOTKvs37TlWdM0R2xaLDVaivhHe2 4hMjhhFroKCOUS89pX4i3jv62MPAcCIMJcFNTEyRqiSgSHs5+JAMvNLFyjHvvumZMG2k rWoJMvYbYtnbXId+YeeShO1JMxNcpH273dI6YB82VPSAbuVeajo9a3MGio5wgtIasG8u gcdQ== X-Gm-Message-State: AOAM532ufcRRgfZ+x42o4uf8JbvL0Z9D7UzdSKScRcG7U4EObJJwko6l bh/9KEW8nn4KT64HtbLxsWYQjZWUtPnE1nhWG26coKVD8eLF/6NMf045hrZQN1zYS4sqSbsuCij lOIdbxI3Uer+r/U32rkW1SUUaMzKiTGCQX2NQZ3R/QmPbWZHljRtru6wa/e1GKeCHr0FOjQ4= X-Google-Smtp-Source: ABdhPJxQVZWYbp3hCkjs303e+9sQBrIy9DoypVnOE0YqeU+1J7Dl5a5/v6a7VLY/7cGFZnaZNr011LBTJjjf X-Received: from kenalba1.mtv.corp.google.com ([2620:15c:203:200:4404:83b2:506f:fd71]) (user=kenalba job=sendgmr) by 2002:a25:1c3:: with SMTP id 186mr3084790ybb.111.1621470202534; Wed, 19 May 2021 17:23:22 -0700 (PDT) Date: Wed, 19 May 2021 17:22:49 -0700 In-Reply-To: <20210520002249.361821-1-kenalba@google.com> Message-Id: <20210519143836.3.I9d559632e582daaecdafd995ce7dfd9c89e64838@changeid> Mime-Version: 1.0 References: <20210520002249.361821-1-kenalba@google.com> X-Mailer: git-send-email 2.31.1.818.g46aad6cb9e-goog Subject: [PATCH 3/3] [hid] Emit digitizer serial number through power_supply From: Kenneth Albanowski To: "open list:HID CORE LAYER" Cc: Dmitry Torokhov , Benjamin Tissoires , Jiri Kosina , Peter Hutterer , Jason Gerecke , Kenneth Albanowski Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org HID devices that expose a battery strength can have associated power_supply nodes. This fills in the SERIAL_NUMBER power_supply field if the same HID device also has a Digitizer.Transducer Serial Number usage, effectively allowing that particular stylus to be identified. If the field is present and non-zero, the serial number will be 'DG-ABCD' where 'ABCD' is up to sixteen hex digits -- field lengths of up to 64-bits are supported, the largest currently known about. Devices are expected to emit zero if the transducer does not have a serial number, or the serial number has not yet been acquired; zeros will be ignored. Note that logical min/max (and other HID item parameters) will be ignored for this field. Signed-off-by: Kenneth Albanowski --- drivers/hid/hid-input.c | 100 +++++++++++++++++++++++++++++++++++++--- include/linux/hid.h | 5 ++ 2 files changed, 99 insertions(+), 6 deletions(-) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index ee9e8d31a45ba..c5767ceb4a61c 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -286,6 +286,7 @@ static enum power_supply_property hidinput_battery_props[] = { POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_SERIAL_NUMBER, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_SCOPE, }; @@ -402,6 +403,26 @@ static int hidinput_get_battery_property(struct power_supply *psy, val->strval = dev->name; break; + case POWER_SUPPLY_PROP_SERIAL_NUMBER: + /* Serial number does not have an active HID query + * mechanism like hidinput_query_battery_capacity, as the + * only devices expected to have serial numbers are digitizers, + * which are unlikely to be able to pull the serial number from + * an untethered pen on demand. + */ + if (dev->battery_serial_number == 0) { + /* Make no claims about S/N format if we haven't actually seen a value yet. */ + strcpy(dev->battery_serial_number_str, ""); + } else { + snprintf(dev->battery_serial_number_str, + sizeof(dev->battery_serial_number_str), + "DG-%0*llX", + DIV_ROUND_UP(dev->battery_serial_number_bits, 4), + dev->battery_serial_number); + } + val->strval = dev->battery_serial_number_str; + break; + case POWER_SUPPLY_PROP_STATUS: if (dev->battery_status != HID_BATTERY_REPORTED && !dev->battery_avoid_query) { @@ -485,6 +506,8 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, dev->battery_max = max; dev->battery_report_type = report_type; dev->battery_report_id = field->report->id; + dev->battery_changed = false; + dev->battery_reported = false; /* * Stylus is normally not connected to the device and thus we @@ -526,7 +549,8 @@ static void hidinput_cleanup_battery(struct hid_device *dev) dev->battery = NULL; } -static void hidinput_update_battery(struct hid_device *dev, int value) +static void hidinput_update_battery_capacity(struct hid_device *dev, + __s32 value) { int capacity; @@ -538,11 +562,57 @@ static void hidinput_update_battery(struct hid_device *dev, int value) capacity = hidinput_scale_battery_capacity(dev, value); + if (capacity != dev->battery_capacity) { + dev->battery_capacity = capacity; + dev->battery_changed = true; + } + dev->battery_reported = true; +} + +static void hidinput_update_battery_serial(struct hid_device *dev, + const __s32 *values, int bits) +{ + __u64 value; + + if (!dev->battery) + return; + + if (bits > 64) + bits = 64; + + value = (__u64)(__u32)values[0]; + if (bits > 32) + value |= (__u64)values[1] << 32; + + if (value == 0) + return; + + if (value != dev->battery_serial_number) { + dev->battery_serial_number = value; + dev->battery_serial_number_bits = bits; + dev->battery_changed = true; + } + dev->battery_reported = true; +} + +static void hidinput_flush_battery(struct hid_device *dev) +{ + if (!dev->battery) + return; + + /* Only consider pushing a battery change if there is a + * battery field in this report. + */ + if (!dev->battery_reported) + return; + + dev->battery_reported = false; + if (dev->battery_status != HID_BATTERY_REPORTED || - capacity != dev->battery_capacity || + dev->battery_changed || ktime_after(ktime_get_coarse(), dev->battery_ratelimit_time)) { - dev->battery_capacity = capacity; dev->battery_status = HID_BATTERY_REPORTED; + dev->battery_changed = false; dev->battery_ratelimit_time = ktime_add_ms(ktime_get_coarse(), 30 * 1000); power_supply_changed(dev->battery); @@ -559,7 +629,17 @@ static void hidinput_cleanup_battery(struct hid_device *dev) { } -static void hidinput_update_battery(struct hid_device *dev, int value) +static void hidinput_update_battery_capacity(struct hid_device *dev, + __s32 value) +{ +} + +static void hidinput_update_battery_serial(struct hid_device *dev, + const __s32 *values, int bits) +{ +} + +static void hidinput_flush_battery(struct hid_device *dev) { } #endif /* CONFIG_HID_BATTERY_STRENGTH */ @@ -1273,7 +1353,9 @@ static void hidinput_handle_scroll(struct hid_usage *usage, input_event(input, EV_REL, usage->code, hi_res); } -void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, const __s32 *values, unsigned value_count) +void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, + struct hid_usage *usage, const __s32 *values, + unsigned value_count) { struct input_dev *input; unsigned *quirks = &hid->quirks; @@ -1290,9 +1372,13 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct return; if (usage->type == EV_PWR) { - hidinput_update_battery(hid, value); + hidinput_update_battery_capacity(hid, value); return; } + if (usage->type == EV_MSC && usage->code == MSC_SERIAL) { + hidinput_update_battery_serial(hid, values, field->report_size); + /* fall through to normal standard MSC_SERIAL processing */ + } if (!field->hidinput) return; @@ -1423,6 +1509,8 @@ void hidinput_report_event(struct hid_device *hid, struct hid_report *report) { struct hid_input *hidinput; + hidinput_flush_battery(hid); + if (hid->quirks & HID_QUIRK_NO_INPUT_SYNC) return; diff --git a/include/linux/hid.h b/include/linux/hid.h index 8494b1995b10b..d5585a99b5ad9 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -587,8 +587,13 @@ struct hid_device { /* device report descriptor */ __s32 battery_max; __s32 battery_report_type; __s32 battery_report_id; + __u64 battery_serial_number; + int battery_serial_number_bits; /* Actual number of bits in SN */ + char battery_serial_number_str[20]; /* Space for "DG-" + max 16 hex digits */ enum hid_battery_status battery_status; bool battery_avoid_query; + bool battery_changed; + bool battery_reported; ktime_t battery_ratelimit_time; #endif