From patchwork Thu Jul 7 16:58:57 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarrett Schultz X-Patchwork-Id: 12909984 X-Patchwork-Delegate: jikos@jikos.cz Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7D5EFCCA483 for ; Thu, 7 Jul 2022 16:59:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236204AbiGGQ7Z (ORCPT ); Thu, 7 Jul 2022 12:59:25 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37724 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236183AbiGGQ7Y (ORCPT ); Thu, 7 Jul 2022 12:59:24 -0400 Received: from mail-pg1-x52d.google.com (mail-pg1-x52d.google.com [IPv6:2607:f8b0:4864:20::52d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 45DA1B86B; Thu, 7 Jul 2022 09:59:24 -0700 (PDT) Received: by mail-pg1-x52d.google.com with SMTP id g4so19616649pgc.1; Thu, 07 Jul 2022 09:59:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=xLxczcJVOgE/FdoCqhxa5viyVGxpBFORTb2+ALl7/sk=; b=EnsomdrlsFIc+9VOx9G5+PL+tmUvq7FCx2fjNufQ3zHH6WBo4Iuo6+oIa48K8XBG61 Kevhs4OZMQfqzqONKm2sKTpXQYEDGmf1KBfDikghjX2wnoLG/8ST9KObJFJn7V6MbyQn zthx4sw5QBk/e/7KZIvf1qyqzA8m27XEIvaAjQIq1zrwiOisl5nyGUFicz7rtwevvmEU bvH6Gnk3FtO7PHdV7t6rX5xS2A45WvUM0w6SByFLjou2D2Joi5ThfQiaAsCl0GXV8y7O sLmU07OzK+/e54PjUnIXp2UE+CgwOjAIG/KBBSkd3nb9PTjjIK293JbkYOgZEPXUI/AF ThAA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=xLxczcJVOgE/FdoCqhxa5viyVGxpBFORTb2+ALl7/sk=; b=qXhDKnOFh30fl61BQ/8fMhZLL5wd9IbYQ8uZvYrpQUZh+4IVJNf0muALfKJW0OFJDo K0x5oBQwczfzVMefGneVKwdG+1SG8lz1zWvL2UsmXypnxOg0FuW4UZdmTTDKk6AeDDWl w3WGCvSmWIZftjcTlxTp8NG1LIAa2+4UiEsXUoW1/IDpVfzLdlU0udczjT6MEvfAtcU0 kHr7ruzxrCyUDu8H+gslKQhCVuA2ln2fv/K4TWRFjmDvlnkr/rx7Gya6GmLKQP5vx/zZ FY3Qs5GO4MrfyVvp1YZjss6tD8pKuWLBGJwNNBdJWb+1HicOYOYz32QF3z4WHaccVuqq BQ/g== X-Gm-Message-State: AJIora9kET9MIrOVKIhdRYCKMn+X3LZzFS8CUsxb8vKdDQeV2GfVwHLS QPqxd9RbVvJw6N2LTTVVrNfZ3fjNJ+ICRw== X-Google-Smtp-Source: AGRyM1vlxfrC1bW763DGjq3CmBGzp76w0O9+VB/oCxDN7P8WhKc/CDD1ugcnl21LFCYv4uLLqOydbA== X-Received: by 2002:a17:90a:e547:b0:1ef:95c2:cefb with SMTP id ei7-20020a17090ae54700b001ef95c2cefbmr5988976pjb.225.1657213163847; Thu, 07 Jul 2022 09:59:23 -0700 (PDT) Received: from jaschultz-Thelio-Major.corp.microsoft.com ([2001:4898:80e8:37:6a04:c27c:dcee:eb11]) by smtp.gmail.com with ESMTPSA id v14-20020aa7808e000000b00518e1251197sm28184185pff.148.2022.07.07.09.59.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 Jul 2022 09:59:23 -0700 (PDT) From: Jarrett Schultz X-Google-Original-From: Jarrett Schultz To: Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Jonathan Corbet , Catalin Marinas , Will Deacon , Jiri Kosina , Benjamin Tissoires , Bjorn Andersson , Shawn Guo , Geert Uytterhoeven , Marcel Ziswiler , Biju Das , Dmitry Baryshkov , Vinod Koul Cc: Dmitry Antipov , linux-input@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Jarrett Schultz Subject: [PATCH v5 1/6] HID: Add BUS_SPI support when printing out device info in hid_connect() Date: Thu, 7 Jul 2022 09:58:57 -0700 Message-Id: <20220707165902.3184-2-jaschultzMS@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220707165902.3184-1-jaschultzMS@gmail.com> References: <20220707165902.3184-1-jaschultzMS@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org From: Jarrett Schultz If connecting a hid_device with bus field indicating BUS_SPI print out "SPI" in the debug print. Signed-off-by: Dmitry Antipov --- drivers/hid/hid-core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 00154a1cd2d8..22f313716a12 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2219,6 +2219,9 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask) case BUS_I2C: bus = "I2C"; break; + case BUS_SPI: + bus = "SPI"; + break; case BUS_VIRTUAL: bus = "VIRTUAL"; break; From patchwork Thu Jul 7 16:58:58 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarrett Schultz X-Patchwork-Id: 12909986 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 51114C43334 for ; Thu, 7 Jul 2022 16:59:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236304AbiGGQ71 (ORCPT ); Thu, 7 Jul 2022 12:59:27 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37732 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235736AbiGGQ70 (ORCPT ); Thu, 7 Jul 2022 12:59:26 -0400 Received: from mail-pf1-x429.google.com (mail-pf1-x429.google.com [IPv6:2607:f8b0:4864:20::429]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 58318B86B; Thu, 7 Jul 2022 09:59:25 -0700 (PDT) Received: by mail-pf1-x429.google.com with SMTP id b9so4566456pfp.10; Thu, 07 Jul 2022 09:59:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=XTGQ5LbEyZu2f55IFJvbgPNJTNxn86SqhC5DP56QZFg=; b=loY1OkrabwiXHrmpFWtRljLitiSUlun3zzEHHFMoKSYeiV4DkbeIuZbsKgXBp8uOMb fLqlpccwENup4eoO48pUhXY4vqf0taD4aySRXaoG8zLA5QPKhdoUO3aS/xRre2vr9ZTz hTdgbxd5xtZBcheLQBFKIMFKxkTLkUnxxw7/k5fBimjxj4r6eGuUUj/y8bQQEn655G71 7M96moy5N9VmW5BlkZPaWE7COXX85yYHgAEpmy2sO9BGvBbORmTRz5ijhZCzrT5vlNrt 2Ffx6sj629KxgdoEyuremMld1xTjRwqM1kehUI2HxDvIRTNUYqVF6/It6iv/mlbkmp/X EZag== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=XTGQ5LbEyZu2f55IFJvbgPNJTNxn86SqhC5DP56QZFg=; b=0FKerzxIJlar/tPQmaTUPKbC/enn75EfXzO0XUyRVTfhX6fo3BjeoI5mQvJECTU911 BmkgsZo+lK/PuI6N2vhSJskTOXZD9W8XiO86W7SEySbEvX8rfvJ1SxosO0Kf7f0a1sE8 55Sno+wBTrmHtJF76uXrERh7luLP6NTHvV6raZgvxwj4zjtureBGc+7+n5Qne93TnlCI uzZ8+G3LMoSsRnkYyqzZfKo4Z0QzJMfvV1v/4WAyEYgVXZ7fVwv/Eyq60dWB55ER7H3Z PsfghRc3sKqpbbBzhY+jUUInVD9KZ9gAf/qcLJCioyfaR+jQHYA+Ca8kpteQexX+JSsW 0g0A== X-Gm-Message-State: AJIora8oC0BQB10oWTDoA0ci9z4CBfev+lVDFDciwTqJo6A64sEckZuH 9iSh3k6oprVxFU35nDxPqqA= X-Google-Smtp-Source: AGRyM1utogy8hlSzhnn4pDGsFmihEZc+ODN2o0jVhfJdc4COi7HXij1z8PItPcadIg/+DAAjv64EdQ== X-Received: by 2002:aa7:8703:0:b0:528:c4c7:35b1 with SMTP id b3-20020aa78703000000b00528c4c735b1mr6366829pfo.38.1657213164933; Thu, 07 Jul 2022 09:59:24 -0700 (PDT) Received: from jaschultz-Thelio-Major.corp.microsoft.com ([2001:4898:80e8:37:6a04:c27c:dcee:eb11]) by smtp.gmail.com with ESMTPSA id v14-20020aa7808e000000b00518e1251197sm28184185pff.148.2022.07.07.09.59.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 Jul 2022 09:59:24 -0700 (PDT) From: Jarrett Schultz X-Google-Original-From: Jarrett Schultz To: Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Jonathan Corbet , Catalin Marinas , Will Deacon , Jiri Kosina , Benjamin Tissoires , Bjorn Andersson , Shawn Guo , Geert Uytterhoeven , Marcel Ziswiler , Biju Das , Dmitry Baryshkov , Vinod Koul Cc: Dmitry Antipov , linux-input@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Jarrett Schultz Subject: [PATCH v5 2/6] HID: define HID_SPI_DEVICE macro in hid.h Date: Thu, 7 Jul 2022 09:58:58 -0700 Message-Id: <20220707165902.3184-3-jaschultzMS@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220707165902.3184-1-jaschultzMS@gmail.com> References: <20220707165902.3184-1-jaschultzMS@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org From: Jarrett Schultz Macro sets the bus field to BUS_SPI and uses arguments to set vendor product fields. Signed-off-by: Dmitry Antipov --- include/linux/hid.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/hid.h b/include/linux/hid.h index 4363a63b9775..7923fe1b51bb 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -715,6 +715,8 @@ struct hid_descriptor { .bus = BUS_BLUETOOTH, .vendor = (ven), .product = (prod) #define HID_I2C_DEVICE(ven, prod) \ .bus = BUS_I2C, .vendor = (ven), .product = (prod) +#define HID_SPI_DEVICE(ven, prod) \ + .bus = BUS_SPI, .vendor = (ven), .product = (prod) #define HID_REPORT_ID(rep) \ .report_type = (rep) From patchwork Thu Jul 7 16:58:59 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarrett Schultz X-Patchwork-Id: 12909989 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 967A9CCA481 for ; Thu, 7 Jul 2022 16:59:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236371AbiGGQ7d (ORCPT ); Thu, 7 Jul 2022 12:59:33 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37748 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236319AbiGGQ71 (ORCPT ); Thu, 7 Jul 2022 12:59:27 -0400 Received: from mail-pf1-x431.google.com (mail-pf1-x431.google.com [IPv6:2607:f8b0:4864:20::431]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 885B7140FD; Thu, 7 Jul 2022 09:59:26 -0700 (PDT) Received: by mail-pf1-x431.google.com with SMTP id d10so6811082pfd.9; Thu, 07 Jul 2022 09:59:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=eliIMioWidh5kLoAeqiqdJgwE4VwB0SBXU+BPYgusfQ=; b=plTFrtGtsJhhrfsS4lGaEY3+HCBiOH4su3Nkg7Zatn5j++2C5fjFSExy7+osqHb5Os GtFtevptBdPRU+VR07vfNGoRvB/3+yawdTtGbBSr2ZFY9sGlqXl6NvBAS+15ouuW4Afl U0/QvQBRe5WqiJ5SwVeEzERmGLy4X/PdLgV2dIIj0WiRLfiPrWKd0mMVbUG6TBVDaXIC WqEfENy8pfGKEQ7vwwpcEyYtAftG1dGnVEmDknMgtJoBX/k0QfItXSexjn4PIz4igXxQ vNY376sfBlguYevgagS1iIGGat3YlObwzuf6ZzVrG6e6Ew8cGE53l2AGNPG2Nj5NPLGF e17g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=eliIMioWidh5kLoAeqiqdJgwE4VwB0SBXU+BPYgusfQ=; b=CreKpvAkpuF01U7dY73MAxE2iJz4lvakLbSZsoTe69QIjcncISAu4IxfvZORJfLDHu 92vQ6KjPDhhl/E8SRTayLL6Zlh4/e84aClXiJYvzS0+ciEPrenH637/Yt9tvGAAbpi3f LHSeX4MAAR0NEd4qv183E+5cYKipptrspIJNfz4ypJiCrobw/ZzrxUxGKkka2iFU8TVI cErucAda9BhlLbp75c2UMkroS9tx9+BF1Cdph1+bQqsU4JO9mME6IB2MPQtkxMLO515u 1P26FXB2RRxIB7S3sVt/RxLSgwS+jIDrbi/J9763X9e7s6BqGV17V2vjppLsnyKddvCP anug== X-Gm-Message-State: AJIora/g/zr/MUC6XK3WhKgdcODOTMwViZSsJ4yWRznLRGH7w4m3Rq5+ BXH0esOydK+s1EDMKFxdrdO4h0FIVHqf9Q== X-Google-Smtp-Source: AGRyM1u+QpXuU1C7Ozi0UtchNSl/19rAChTfEtffBwDQzTYxAwJ/MURVaDBNgpuNy9qLcOFjKYhQCw== X-Received: by 2002:a05:6a00:1908:b0:525:5dad:cb1c with SMTP id y8-20020a056a00190800b005255dadcb1cmr54026824pfi.47.1657213165985; Thu, 07 Jul 2022 09:59:25 -0700 (PDT) Received: from jaschultz-Thelio-Major.corp.microsoft.com ([2001:4898:80e8:37:6a04:c27c:dcee:eb11]) by smtp.gmail.com with ESMTPSA id v14-20020aa7808e000000b00518e1251197sm28184185pff.148.2022.07.07.09.59.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 Jul 2022 09:59:25 -0700 (PDT) From: Jarrett Schultz X-Google-Original-From: Jarrett Schultz To: Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Jonathan Corbet , Catalin Marinas , Will Deacon , Jiri Kosina , Benjamin Tissoires , Bjorn Andersson , Shawn Guo , Geert Uytterhoeven , Marcel Ziswiler , Biju Das , Dmitry Baryshkov , Vinod Koul Cc: Dmitry Antipov , linux-input@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Jarrett Schultz Subject: [PATCH v5 3/6] dt-bindings: input: Document Microsoft G6 Touch Digitizer Date: Thu, 7 Jul 2022 09:58:59 -0700 Message-Id: <20220707165902.3184-4-jaschultzMS@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220707165902.3184-1-jaschultzMS@gmail.com> References: <20220707165902.3184-1-jaschultzMS@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org From: Jarrett Schultz Documentation describes the required and optional properties for implementing Device Tree for a Microsoft G6 Touch Digitizer that supports HID over SPI Protocol 1.0 specification. Signed-off-by: Dmitry Antipov Signed-off-by: Jarrett Schultz --- .../input/microsoft,g6-touch-digitizer.yaml | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/microsoft,g6-touch-digitizer.yaml diff --git a/Documentation/devicetree/bindings/input/microsoft,g6-touch-digitizer.yaml b/Documentation/devicetree/bindings/input/microsoft,g6-touch-digitizer.yaml new file mode 100644 index 000000000000..b607bbb32a42 --- /dev/null +++ b/Documentation/devicetree/bindings/input/microsoft,g6-touch-digitizer.yaml @@ -0,0 +1,135 @@ +# SPDX-License-Identifier: (GPL-2.0-only or BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/microsoft,g6-touch-digitizer.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microsoft G6 Touch Digitizer + +maintainers: + - Dmitry Antipov + +description: | + Microsoft G6 touch digitizer is a HID over SPI device supporting HID Over SPI + Protocol Specification 1.0, available at + https://www.microsoft.com/en-us/download/details.aspx?id=103325. + +properties: + compatible: + items: + - const: microsoft,g6-touch-digitizer + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + reset-gpios: + maxItems: 1 + description: + GPIO specifier for the digitizer's reset pin (active low). The line must + be flagged with GPIO_ACTIVE_LOW. + + vdd-supply: + description: + Regulator for the VDD supply voltage. + + input-report-header-address: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 16777215 + description: + A value to be included in the Read Approval packet, listing an address of + the input report header to be put on the SPI bus. This address has 24 + bits. + + input-report-body-address: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 16777215 + description: + A value to be included in the Read Approval packet, listing an address of + the input report body to be put on the SPI bus. This address has 24 bits. + + output-report-address: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 16777215 + description: + A value to be included in the Output Report sent by the host, listing an + address where the output report on the SPI bus is to be written to. This + address has 24 bits. + + post-power-on-delay-ms: + description: + Optional time in ms required by the device after enabling its regulators + or powering it on, before it is ready for communication. + + minimal-reset-delay-ms: + description: + Optional minimum amount of time in ms that device needs to be in reset + state for the reset to take effect. + + read-opcode: + description: + Value to be used in Read Approval packets. 1 byte. + + write-opcode: + description: + Value to be used in Write Approval packets. 1 byte. + + hid-over-spi-flags: + description: + 16 bits. + Bits 0-12 - Reserved (must be 0) + Bit 13 - SPI Write Mode. Possible values - + * 0b0- Writes are carried out in Single-SPI mode + * 0b1- Writes are carried out in the Multi-SPI mode specified by bits + 14-15 + Bits 14-15 - Multi-SPI Mode. Possible values - + * 0b00- Single SPI + * 0b01- Dual SPI + * 0b10- Quad SPI + +required: + - compatible + - interrupts + - reset-gpios + - vdd-supply + - input-report-header-address + - input-report-body-address + - output-report-address + - read-opcode + - write-opcode + - hid-over-spi-flags + +additionalProperties: false + +examples: + - | + #include + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + hid@0 { + compatible = "microsoft,g6-touch-digitizer"; + reg = <0x0>; + interrupts-extended = <&gpio 42 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&gpio 27 GPIO_ACTIVE_LOW>; + vdd-supply = <&pm8350c_l3>; + pinctrl-names = "default"; + pinctrl-0 = <&ts_d6_reset_assert &ts_d6_int_bias>; + input-report-header-address = <0x1000>; + input-report-body-address = <0x1004>; + output-report-address = <0x2000>; + read-opcode = <0x0b>; + write-opcode = <0x02>; + hid-over-spi-flags = <0x00>; + post-power-on-delay-ms = <5>; + minimal-reset-delay-ms = <5>; + }; + }; From patchwork Thu Jul 7 16:59:00 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarrett Schultz X-Patchwork-Id: 12909987 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1546DC433EF for ; Thu, 7 Jul 2022 16:59:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236384AbiGGQ7e (ORCPT ); Thu, 7 Jul 2022 12:59:34 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37762 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236330AbiGGQ72 (ORCPT ); Thu, 7 Jul 2022 12:59:28 -0400 Received: from mail-pg1-x533.google.com (mail-pg1-x533.google.com [IPv6:2607:f8b0:4864:20::533]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9C7F11F629; Thu, 7 Jul 2022 09:59:27 -0700 (PDT) Received: by mail-pg1-x533.google.com with SMTP id 23so19582156pgc.8; Thu, 07 Jul 2022 09:59:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=mouOf2yAGvzJretTdCLBZi7N9va1QAabtNdEx7dOaxA=; b=Iy06ZYNTT2bDPb3p26fQ1eLYPMR1Pfuej5t6SevUQcccl5Lf2yH7jGv/eRdB0SrGoZ n3HgRlZd4H8DvQKLEmTIR9zQQoL7zeUbHkifv0bN/i3f3n413ely87lB3uImJ6M5Y/9V 3zVoc8ssOi7Pp8RDPDCBNHngpchR+S7+RXsm9k0LDq2q5IxZPKh99yWve6IJyQIOwwIt bTh2urfAxauiKwEN7SfwNumJgEPIJd43h40ptT+Zay81OCik+yZBubzm7HixxJWX8pyN S3nSEhHkhI/BQbqfbW/xItKbsOVj5//o4/KNV9ySCNnxnvz8CKcuMWMqtCaaxfFNx/cg Uymg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=mouOf2yAGvzJretTdCLBZi7N9va1QAabtNdEx7dOaxA=; b=WyIzBdE9otzFDeJ8ELZxs9BQd31P2B8XFKDrC+vgJ/yTKp9WmxdU3YbV9OnjvrcRrj +AyHGFTxB9Cx1cBJVGXqLzZ6FnyeyNOr8PGGyRv4jJYRUijQSYHYZSMsyHSu1KN/lEsw uk/nCM/nz4wqRWFnRPsxbeW8jPk+LhVLScNvE+s5RGGXNjiHR4604draA1oT7vOsv06O 0vKy8z8Edxl91dyhnFJEzWMjCEUl1HHoelciZ0J0At1BH7FoJjHi4bXOsAQJ6CfrXpK4 gPVSTwRR5nsCeClU7+siF8zrDAgeAgETfu/B+ujmhPIyVTiSqi9QcVimZtu+hL8KQXJp GPzA== X-Gm-Message-State: AJIora/qo0l0Jc89Ej/lioXSekbOD/Gd9EJ/XAiEXAh1zPirY+0FJRON pG4HGB9L5H9Q58TuSzRZ690= X-Google-Smtp-Source: AGRyM1sqcCTln+QfjvynadCg0/JVQxDlFzJBICdvbGJkQLTJgO7dthIug1H0T4b1oVJhKKoKKDJXsg== X-Received: by 2002:a17:90a:bd92:b0:1ef:82d8:f2b9 with SMTP id z18-20020a17090abd9200b001ef82d8f2b9mr6051221pjr.83.1657213167120; Thu, 07 Jul 2022 09:59:27 -0700 (PDT) Received: from jaschultz-Thelio-Major.corp.microsoft.com ([2001:4898:80e8:37:6a04:c27c:dcee:eb11]) by smtp.gmail.com with ESMTPSA id v14-20020aa7808e000000b00518e1251197sm28184185pff.148.2022.07.07.09.59.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 Jul 2022 09:59:26 -0700 (PDT) From: Jarrett Schultz X-Google-Original-From: Jarrett Schultz To: Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Jonathan Corbet , Catalin Marinas , Will Deacon , Jiri Kosina , Benjamin Tissoires , Bjorn Andersson , Shawn Guo , Geert Uytterhoeven , Marcel Ziswiler , Biju Das , Dmitry Baryshkov , Vinod Koul Cc: Dmitry Antipov , linux-input@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Jarrett Schultz Subject: [PATCH v5 4/6] Documentation: Correction in HID output_report callback description. Date: Thu, 7 Jul 2022 09:59:00 -0700 Message-Id: <20220707165902.3184-5-jaschultzMS@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220707165902.3184-1-jaschultzMS@gmail.com> References: <20220707165902.3184-1-jaschultzMS@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org From: Jarrett Schultz Originally output_report callback was described as must-be asynchronous, but that is not the case in some implementations, namely i2c-hid. Correct the documentation to say that it may be asynchronous. Signed-off-by: Dmitry Antipov --- Documentation/hid/hid-transport.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/hid/hid-transport.rst b/Documentation/hid/hid-transport.rst index 6f1692da296c..2008cf432af1 100644 --- a/Documentation/hid/hid-transport.rst +++ b/Documentation/hid/hid-transport.rst @@ -327,8 +327,8 @@ The available HID callbacks are: Send raw output report via intr channel. Used by some HID device drivers which require high throughput for outgoing requests on the intr channel. This - must not cause SET_REPORT calls! This must be implemented as asynchronous - output report on the intr channel! + must not cause SET_REPORT calls! This call might be asynchronous, so the + caller should not expect an immediate response! :: From patchwork Thu Jul 7 16:59:01 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarrett Schultz X-Patchwork-Id: 12909990 X-Patchwork-Delegate: jikos@jikos.cz Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 82D4DCCA47F for ; Thu, 7 Jul 2022 16:59:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236405AbiGGQ7h (ORCPT ); Thu, 7 Jul 2022 12:59:37 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37808 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235736AbiGGQ7c (ORCPT ); Thu, 7 Jul 2022 12:59:32 -0400 Received: from mail-pg1-x52d.google.com (mail-pg1-x52d.google.com [IPv6:2607:f8b0:4864:20::52d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 18CD621E24; Thu, 7 Jul 2022 09:59:29 -0700 (PDT) Received: by mail-pg1-x52d.google.com with SMTP id e132so19593871pgc.5; Thu, 07 Jul 2022 09:59:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=i+8QxwaoZmvV6a812dsSCQ4nzYcbcePbk0+AOkHgL50=; b=I0Lsgc1j6ugLUKZde4KMSUmK4VTEABFQPD9JOztolKl/EUJJZ7FzwYlT5zVNlCtHtf o8jWmndwYqdgmL18zN8WolFkSiMJsNb+l1C15lGmy57N5vaTqdQvL3QritdVcD0Gj079 kPkwf3Ul2GDhZfm2He/29CBTwXj/EPy4+ZZJD4C3BEqekewFDehZhCDDAowDk7wV0kjm BZX4PlQwVn47BeJkRAM6gZPrcjmmDpufgoKhfUk95wwiimY3eUqXzp+Oz5o1MIUuBG96 2+y58zw3VQ2cPK7ZbjSoq+ThKwJiiAuswtqDPx/lNwW1N+12d/PYDlKP4kEbXqev1FLq 3dpg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=i+8QxwaoZmvV6a812dsSCQ4nzYcbcePbk0+AOkHgL50=; b=2cAeyA70I0r8GoisjrSgl9jWt3dIwqoXOcVKsk0OowfAmdtg5jnNYMVACte5YduqJO iu9X0TpkZ+7JTfWCDPRLrOGHAG039xICmrxmQmXGxJ5MWPSb+KYlIPNyNqEZfn6bLbt7 PRIvbi4C626fKUU08v5KPB7QtRB0jDU0eej7er4u8XTfACKXBZYsNohdTrPcveMRAK6N BaUIEdjJ2+Rp3lC34aXGfReY7SrAqjYXZVnhHq5QP7vuBoW+hQ7AY3LuHFVsmGBT2wNA AUNgUcRt4E/1Z8enwjii4Ap9sZ6tStBaf/VlqKeJAbyuSYLT84rFV25M+Yun7t2N6KOp 75+Q== X-Gm-Message-State: AJIora+tHROiPwwwHHs+099WZwXDHFTwMsU4EnjZPoPYT1KjgY4V/wOt PrRh+xRHMMUjj0tuHoitW9Y= X-Google-Smtp-Source: AGRyM1skrJJjRv3NAxOGwER+8WIywWocaROXk7uYC083V9Fd757IQ+oIW2CIXbads9YsjkITvsNyEw== X-Received: by 2002:a05:6a00:1592:b0:525:52ca:bee6 with SMTP id u18-20020a056a00159200b0052552cabee6mr53234154pfk.38.1657213168268; Thu, 07 Jul 2022 09:59:28 -0700 (PDT) Received: from jaschultz-Thelio-Major.corp.microsoft.com ([2001:4898:80e8:37:6a04:c27c:dcee:eb11]) by smtp.gmail.com with ESMTPSA id v14-20020aa7808e000000b00518e1251197sm28184185pff.148.2022.07.07.09.59.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 Jul 2022 09:59:27 -0700 (PDT) From: Jarrett Schultz X-Google-Original-From: Jarrett Schultz To: Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Jonathan Corbet , Catalin Marinas , Will Deacon , Jiri Kosina , Benjamin Tissoires , Bjorn Andersson , Shawn Guo , Geert Uytterhoeven , Marcel Ziswiler , Biju Das , Dmitry Baryshkov , Vinod Koul Cc: Dmitry Antipov , linux-input@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Jarrett Schultz Subject: [PATCH v5 5/6] HID: add spi-hid, transport driver for HID over SPI bus Date: Thu, 7 Jul 2022 09:59:01 -0700 Message-Id: <20220707165902.3184-6-jaschultzMS@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220707165902.3184-1-jaschultzMS@gmail.com> References: <20220707165902.3184-1-jaschultzMS@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org From: Jarrett Schultz This driver follows HID Over SPI Protocol Specification 1.0 available at https://www.microsoft.com/en-us/download/details.aspx?id=103325. The initial version of the driver does not support: 1) multi-fragment input reports, 2) sending GET_INPUT and COMMAND output report types and processing their respective acknowledge input reports, and 3) device sleep power state. Signed-off-by: Dmitry Antipov Reported-by: kernel test robot --- drivers/hid/Kconfig | 2 + drivers/hid/Makefile | 1 + drivers/hid/spi-hid/Kconfig | 25 + drivers/hid/spi-hid/Makefile | 12 + drivers/hid/spi-hid/spi-hid-core.c | 1326 +++++++++++++++++++++++++++ drivers/hid/spi-hid/spi-hid-core.h | 188 ++++ drivers/hid/spi-hid/spi-hid-of.c | 141 +++ drivers/hid/spi-hid/spi-hid-of.h | 30 + drivers/hid/spi-hid/spi-hid_trace.h | 194 ++++ drivers/hid/spi-hid/trace.c | 9 + 10 files changed, 1928 insertions(+) create mode 100644 drivers/hid/spi-hid/Kconfig create mode 100644 drivers/hid/spi-hid/Makefile create mode 100644 drivers/hid/spi-hid/spi-hid-core.c create mode 100644 drivers/hid/spi-hid/spi-hid-core.h create mode 100644 drivers/hid/spi-hid/spi-hid-of.c create mode 100644 drivers/hid/spi-hid/spi-hid-of.h create mode 100644 drivers/hid/spi-hid/spi-hid_trace.h create mode 100644 drivers/hid/spi-hid/trace.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 70da5931082f..a396829c4c0b 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -1314,6 +1314,8 @@ source "drivers/hid/usbhid/Kconfig" source "drivers/hid/i2c-hid/Kconfig" +source "drivers/hid/spi-hid/Kconfig" + source "drivers/hid/intel-ish-hid/Kconfig" source "drivers/hid/amd-sfh-hid/Kconfig" diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index cac2cbe26d11..1699863e4021 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -149,6 +149,7 @@ obj-$(CONFIG_USB_MOUSE) += usbhid/ obj-$(CONFIG_USB_KBD) += usbhid/ obj-$(CONFIG_I2C_HID_CORE) += i2c-hid/ +obj-$(CONFIG_SPI_HID) += spi-hid/ obj-$(CONFIG_INTEL_ISH_HID) += intel-ish-hid/ obj-$(INTEL_ISH_FIRMWARE_DOWNLOADER) += intel-ish-hid/ diff --git a/drivers/hid/spi-hid/Kconfig b/drivers/hid/spi-hid/Kconfig new file mode 100644 index 000000000000..37302d658162 --- /dev/null +++ b/drivers/hid/spi-hid/Kconfig @@ -0,0 +1,25 @@ +# +# Copyright (c) 2021 Microsoft Corporation +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 as published by +# the Free Software Foundation. +# +menu "SPI HID support" + depends on SPI + +config SPI_HID + tristate "HID over SPI transport layer" + default n + depends on SPI && INPUT && OF + select HID + help + Say Y here if you use a keyboard, a touchpad, a touchscreen, or any + other HID based devices which is connected to your computer via SPI. + + If unsure, say N. + + This support is also available as a module. If so, the module + will be called spi-hid. + +endmenu diff --git a/drivers/hid/spi-hid/Makefile b/drivers/hid/spi-hid/Makefile new file mode 100644 index 000000000000..b9f73982b956 --- /dev/null +++ b/drivers/hid/spi-hid/Makefile @@ -0,0 +1,12 @@ +# +# Copyright (c) 2021 Microsoft Corporation +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 as published by +# the Free Software Foundation. +# +# Makefile for the HID over SPI transport driver +# +CFLAGS_trace.o = -I$(src) +obj-$(CONFIG_SPI_HID) += spi-hid.o +spi-hid-objs := spi-hid-core.o spi-hid-of.o trace.o diff --git a/drivers/hid/spi-hid/spi-hid-core.c b/drivers/hid/spi-hid/spi-hid-core.c new file mode 100644 index 000000000000..8a29bfe40590 --- /dev/null +++ b/drivers/hid/spi-hid/spi-hid-core.c @@ -0,0 +1,1326 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * HID over SPI protocol implementation + * + * Copyright (c) 2021 Microsoft Corporation + * + * This code is partly based on "HID over I2C protocol implementation: + * + * Copyright (c) 2012 Benjamin Tissoires + * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France + * Copyright (c) 2012 Red Hat, Inc + * + * which in turn is partly based on "USB HID support for Linux": + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2007-2008 Oliver Neukum + * Copyright (c) 2006-2010 Jiri Kosina + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spi-hid-core.h" +#include "spi-hid_trace.h" +#include "spi-hid-of.h" +#include "../hid-ids.h" + +#define SPI_HID_MAX_RESET_ATTEMPTS 3 + +static struct hid_ll_driver spi_hid_ll_driver; + +static void spi_hid_populate_read_approvals(struct spi_hid_of_config *conf, + u8 *header_buf, u8 *body_buf) +{ + header_buf[0] = conf->read_opcode; + header_buf[1] = (conf->input_report_header_address >> 16) & 0xff; + header_buf[2] = (conf->input_report_header_address >> 8) & 0xff; + header_buf[3] = (conf->input_report_header_address >> 0) & 0xff; + header_buf[4] = SPI_HID_READ_APPROVAL_CONSTANT; + + body_buf[0] = conf->read_opcode; + body_buf[1] = (conf->input_report_body_address >> 16) & 0xff; + body_buf[2] = (conf->input_report_body_address >> 8) & 0xff; + body_buf[3] = (conf->input_report_body_address >> 0) & 0xff; + body_buf[4] = SPI_HID_READ_APPROVAL_CONSTANT; +} + +static void spi_hid_parse_dev_desc(struct spi_hid_device_desc_raw *raw, + struct spi_hid_device_descriptor *desc) +{ + desc->hid_version = le16_to_cpu(raw->bcdVersion); + desc->report_descriptor_length = le16_to_cpu(raw->wReportDescLength); + desc->max_input_length = le16_to_cpu(raw->wMaxInputLength); + desc->max_output_length = le16_to_cpu(raw->wMaxOutputLength); + + /* FIXME: multi-fragment not supported, field below not used */ + desc->max_fragment_length = le16_to_cpu(raw->wMaxFragmentLength); + + desc->vendor_id = le16_to_cpu(raw->wVendorID); + desc->product_id = le16_to_cpu(raw->wProductID); + desc->version_id = le16_to_cpu(raw->wVersionID); + desc->no_output_report_ack = le16_to_cpu(raw->wFlags) & BIT(0); +} + +static void spi_hid_populate_input_header(u8 *buf, + struct spi_hid_input_header *header) +{ + header->version = buf[0] & 0xf; + header->report_length = le16_to_cpu((buf[1] | ((buf[2] & 0x3f) << 8)) * 4); + header->last_fragment_flag = (buf[2] & 0x40) >> 6; + header->sync_const = buf[3]; +} + +static void spi_hid_populate_input_body(u8 *buf, + struct spi_hid_input_body *body) +{ + body->report_type = buf[0]; + body->content_length = le16_to_cpu(buf[1] | (buf[2] << 8)); + body->content_id = buf[3]; +} + +static void spi_hid_input_report_prepare(struct spi_hid_input_buf *buf, + struct spi_hid_input_report *report) +{ + struct spi_hid_input_header header; + struct spi_hid_input_body body; + + spi_hid_populate_input_header(buf->header, &header); + spi_hid_populate_input_body(buf->body, &body); + report->report_type = body.report_type; + report->content_length = body.content_length; + report->content_id = body.content_id; + report->content = buf->content; +} + +static void spi_hid_populate_output_header(u8 *buf, + struct spi_hid_of_config *conf, + struct spi_hid_output_report *report) +{ + buf[0] = conf->write_opcode; + buf[1] = (conf->output_report_address >> 16) & 0xff; + buf[2] = (conf->output_report_address >> 8) & 0xff; + buf[3] = (conf->output_report_address >> 0) & 0xff; + buf[4] = report->report_type; + buf[5] = report->content_length & 0xff; + buf[6] = (report->content_length >> 8) & 0xff; + buf[7] = report->content_id; +} + +static int spi_hid_input_sync(struct spi_hid *shid, void *buf, u16 length, + bool is_header) +{ + int ret; + struct device *dev = &shid->spi->dev; + + shid->input_transfer[0].tx_buf = is_header ? shid->read_approval_header : + shid->read_approval_body; + shid->input_transfer[0].len = SPI_HID_READ_APPROVAL_LEN; + + shid->input_transfer[1].rx_buf = buf; + shid->input_transfer[1].len = length; + + spi_message_init_with_transfers(&shid->input_message, + shid->input_transfer, 2); + + trace_spi_hid_input_sync(shid, + shid->input_transfer[0].tx_buf, + shid->input_transfer[0].len, + shid->input_transfer[1].rx_buf, + shid->input_transfer[1].len, 0); + + ret = spi_sync(shid->spi, &shid->input_message); + if (ret) { + dev_err(dev, "Error starting async transfer: %d, resetting\n", + ret); + shid->bus_error_count++; + shid->bus_last_error = ret; + schedule_work(&shid->error_work); + } + + return ret; +} + +static int spi_hid_output(struct spi_hid *shid, void *buf, u16 length) +{ + struct spi_transfer transfer; + struct spi_message message; + int ret; + + memset(&transfer, 0, sizeof(transfer)); + + transfer.tx_buf = buf; + transfer.len = length; + + spi_message_init_with_transfers(&message, &transfer, 1); + + trace_spi_hid_output_begin(shid, transfer.tx_buf, + transfer.len, NULL, 0, 0); + + ret = spi_sync(shid->spi, &message); + + trace_spi_hid_output_end(shid, transfer.tx_buf, + transfer.len, NULL, 0, ret); + + if (ret) { + shid->bus_error_count++; + shid->bus_last_error = ret; + } + + return ret; +} + +static const char *const spi_hid_power_mode_string(u8 power_state) +{ + switch (power_state) { + case SPI_HID_POWER_MODE_ON: + return "d0"; + case SPI_HID_POWER_MODE_SLEEP: + return "d2"; + case SPI_HID_POWER_MODE_OFF: + return "d3"; + case SPI_HID_POWER_MODE_WAKING_SLEEP: + return "d3*"; + default: + return "unknown"; + } +} + +static void spi_hid_suspend(struct spi_hid *shid) +{ + int ret; + struct device *dev = &shid->spi->dev; + + if (shid->power_state == SPI_HID_POWER_MODE_OFF) + return; + + if (shid->irq_enabled) { + disable_irq(shid->spi->irq); + shid->irq_enabled = false; + } else { + dev_err(dev, "%s called with interrupt already disabled\n", + __func__); + } + + shid->ready = false; + + spi_hid_of_assert_reset(&shid->conf); + + ret = spi_hid_of_power_down(&shid->conf); + if (ret) { + dev_err(dev, "%s: could not power down\n", __func__); + shid->regulator_error_count++; + shid->regulator_last_error = ret; + return; + } + + shid->power_state = SPI_HID_POWER_MODE_OFF; +} + +static void spi_hid_resume(struct spi_hid *shid) +{ + int ret; + struct device *dev = &shid->spi->dev; + + if (shid->power_state == SPI_HID_POWER_MODE_ON) + return; + + if (!shid->irq_enabled) { + enable_irq(shid->spi->irq); + shid->irq_enabled = true; + } else { + dev_err(dev, "%s called with interrupt already enabled\n", + __func__); + } + + ret = spi_hid_of_power_up(&shid->conf); + if (ret) { + dev_err(dev, "%s: could not power up\n", __func__); + shid->regulator_error_count++; + shid->regulator_last_error = ret; + return; + } + shid->power_state = SPI_HID_POWER_MODE_ON; + + spi_hid_of_deassert_reset(&shid->conf); +} + +static struct hid_device *spi_hid_disconnect_hid(struct spi_hid *shid) +{ + struct hid_device *hid = shid->hid; + + shid->hid = NULL; + + return hid; +} + +static void spi_hid_stop_hid(struct spi_hid *shid) +{ + struct hid_device *hid; + + hid = spi_hid_disconnect_hid(shid); + if (hid) { + cancel_work_sync(&shid->create_device_work); + cancel_work_sync(&shid->refresh_device_work); + hid_destroy_device(hid); + } +} + +static void spi_hid_error_work(struct work_struct *work) +{ + struct spi_hid *shid = container_of(work, struct spi_hid, error_work); + struct device *dev = &shid->spi->dev; + int ret; + + mutex_lock(&shid->power_lock); + if (shid->power_state == SPI_HID_POWER_MODE_OFF) + goto out; + + if (shid->attempts++ >= SPI_HID_MAX_RESET_ATTEMPTS) { + dev_err(dev, "unresponsive device, aborting.\n"); + spi_hid_stop_hid(shid); + spi_hid_of_assert_reset(&shid->conf); + ret = spi_hid_of_power_down(&shid->conf); + if (ret) { + dev_err(dev, "failed to disable regulator\n"); + shid->regulator_error_count++; + shid->regulator_last_error = ret; + } + goto out; + } + + trace_spi_hid_error_work(shid); + + shid->ready = false; + + spi_hid_of_assert_reset(&shid->conf); + + shid->power_state = SPI_HID_POWER_MODE_OFF; + cancel_work_sync(&shid->reset_response_work); + + spi_hid_of_sleep_minimal_reset_delay(&shid->conf); + + shid->power_state = SPI_HID_POWER_MODE_ON; + + spi_hid_of_deassert_reset(&shid->conf); +out: + mutex_unlock(&shid->power_lock); +} + +static int spi_hid_send_output_report(struct spi_hid *shid, + struct spi_hid_output_report *report) +{ + struct spi_hid_output_buf *buf = &shid->output; + struct device *dev = &shid->spi->dev; + u16 report_length; + u16 padded_length; + u8 padding; + int ret; + + if (report->content_length > shid->desc.max_output_length) { + dev_err(dev, "Output report too big, content_length 0x%x\n", + report->content_length); + ret = -E2BIG; + goto out; + } + + spi_hid_populate_output_header(buf->header, &shid->conf, report); + + if (report->content_length) + memcpy(&buf->content, report->content, report->content_length); + + report_length = sizeof(buf->header) + report->content_length; + padded_length = round_up(report_length, 4); + padding = padded_length - report_length; + memset(&buf->content[report->content_length], 0, padding); + + ret = spi_hid_output(shid, buf, padded_length); + if (ret) { + dev_err(dev, "Failed output transfer\n"); + goto out; + } + + return 0; + +out: + return ret; +} + +static int spi_hid_sync_request(struct spi_hid *shid, + struct spi_hid_output_report *report) +{ + struct device *dev = &shid->spi->dev; + int ret = 0; + + ret = spi_hid_send_output_report(shid, report); + if (ret) { + dev_err(dev, "Failed to transfer output report\n"); + return ret; + } + + mutex_unlock(&shid->output_lock); + ret = wait_for_completion_interruptible_timeout(&shid->output_done, + msecs_to_jiffies(1000)); + mutex_lock(&shid->output_lock); + if (ret == 0) { + dev_err(dev, "Response timed out\n"); + return -ETIMEDOUT; + } + + return 0; +} + +/** + * Handle the reset response from the FW by sending a request for the device + * descriptor. + */ +static void spi_hid_reset_response_work(struct work_struct *work) +{ + struct spi_hid *shid = + container_of(work, struct spi_hid, reset_response_work); + struct device *dev = &shid->spi->dev; + struct spi_hid_output_report report = { + .report_type = SPI_HID_OUTPUT_REPORT_TYPE_DEVICE_DESC_REQUEST, + .content_length = 0x0, + .content_id = SPI_HID_OUTPUT_REPORT_CONTENT_ID_DESC_REQUEST, + .content = NULL, + }; + int ret; + + trace_spi_hid_reset_response_work(shid); + + if (shid->ready) { + dev_err(dev, "Spontaneous FW reset!"); + shid->ready = false; + shid->dir_count++; + } + + if (shid->power_state == SPI_HID_POWER_MODE_OFF) + return; + + if (flush_work(&shid->create_device_work)) + dev_err(dev, "Reset handler waited for create_device_work"); + + if (flush_work(&shid->refresh_device_work)) + dev_err(dev, "Reset handler waited for refresh_device_work"); + + mutex_lock(&shid->output_lock); + ret = spi_hid_sync_request(shid, &report); + mutex_unlock(&shid->output_lock); + if (ret) { + dev_WARN_ONCE(dev, true, + "Failed to send device descriptor request\n"); + schedule_work(&shid->error_work); + } +} + +static int spi_hid_input_report_handler(struct spi_hid *shid, + struct spi_hid_input_buf *buf) +{ + struct device *dev = &shid->spi->dev; + struct spi_hid_input_report r; + int ret; + + trace_spi_hid_input_report_handler(shid); + + if (!shid->ready || shid->refresh_in_progress || !shid->hid) + return 0; + + spi_hid_input_report_prepare(buf, &r); + + ret = hid_input_report(shid->hid, HID_INPUT_REPORT, + r.content - 1, + r.content_length + 1, 1); + + if (ret == -ENODEV || ret == -EBUSY) { + dev_err(dev, "ignoring report --> %d\n", ret); + return 0; + } else if (ret) { + dev_err(dev, "Bad input report, error %d\n", ret); + } + + return ret; +} + +static void spi_hid_response_handler(struct spi_hid *shid, + struct spi_hid_input_buf *buf) +{ + trace_spi_hid_response_handler(shid); + + /* completion_done returns 0 if there are waiters, otherwise 1 */ + if (completion_done(&shid->output_done)) { + dev_err(&shid->spi->dev, "Unexpected response report\n"); + } else { + if (shid->input.body[0] == + SPI_HID_INPUT_REPORT_TYPE_REPORT_DESC || + shid->input.body[0] == + SPI_HID_INPUT_REPORT_TYPE_GET_FEATURE_RESP) { + size_t response_length = (shid->input.body[1] | + (shid->input.body[2] << 8)) + + sizeof(shid->input.body); + memcpy(shid->response.body, shid->input.body, + response_length); + } + complete(&shid->output_done); + } +} + +/* + * This function returns the length of the report descriptor, or a negative + * error code if something went wrong. + */ +static int spi_hid_report_descriptor_request(struct spi_hid *shid) +{ + int ret; + struct device *dev = &shid->spi->dev; + struct spi_hid_output_report report = { + .report_type = SPI_HID_OUTPUT_REPORT_TYPE_REPORT_DESC_REQUEST, + .content_length = 0, + .content_id = SPI_HID_OUTPUT_REPORT_CONTENT_ID_DESC_REQUEST, + .content = NULL, + }; + + ret = spi_hid_sync_request(shid, &report); + if (ret) { + dev_err(dev, + "Expected report descriptor not received! Error %d\n", + ret); + schedule_work(&shid->error_work); + goto out; + } + + ret = (shid->response.body[1] | (shid->response.body[2] << 8)); + if (ret != shid->desc.report_descriptor_length) { + ret = min_t(unsigned int, ret, + shid->desc.report_descriptor_length); + dev_err(dev, + "Received report descriptor length doesn't match device descriptor field, using min of the two: %d\n", + ret); + } +out: + return ret; +} + +static void spi_hid_process_input_report(struct spi_hid *shid, + struct spi_hid_input_buf *buf) +{ + struct spi_hid_input_header header; + struct spi_hid_input_body body; + struct device *dev = &shid->spi->dev; + struct spi_hid_device_desc_raw *raw; + int ret; + + trace_spi_hid_process_input_report(shid); + + spi_hid_populate_input_header(buf->header, &header); + spi_hid_populate_input_body(buf->body, &body); + + if (body.content_length > header.report_length) { + dev_err(dev, "Bad body length %d > %d\n", body.content_length, + header.report_length); + schedule_work(&shid->error_work); + return; + } + + switch (body.report_type) { + case SPI_HID_INPUT_REPORT_TYPE_DATA: + ret = spi_hid_input_report_handler(shid, buf); + if (ret) + schedule_work(&shid->error_work); + break; + case SPI_HID_INPUT_REPORT_TYPE_RESET_RESP: + schedule_work(&shid->reset_response_work); + break; + case SPI_HID_INPUT_REPORT_TYPE_DEVICE_DESC: + /* Mark the completion done to avoid timeout */ + spi_hid_response_handler(shid, buf); + + /* Reset attempts at every device descriptor fetch */ + shid->attempts = 0; + + raw = (struct spi_hid_device_desc_raw *)buf->content; + + /* Validate device descriptor length before parsing */ + if (body.content_length != SPI_HID_DEVICE_DESCRIPTOR_LENGTH) { + dev_err(dev, + "Invalid content length %d, expected %d\n", + body.content_length, + SPI_HID_DEVICE_DESCRIPTOR_LENGTH); + schedule_work(&shid->error_work); + break; + } + + if (le16_to_cpu(raw->wDeviceDescLength) != + SPI_HID_DEVICE_DESCRIPTOR_LENGTH) { + dev_err(dev, + "Invalid wDeviceDescLength %d, expected %d\n", + raw->wDeviceDescLength, + SPI_HID_DEVICE_DESCRIPTOR_LENGTH); + schedule_work(&shid->error_work); + break; + } + + spi_hid_parse_dev_desc(raw, &shid->desc); + + if (shid->desc.hid_version != SPI_HID_SUPPORTED_VERSION) { + dev_err(dev, + "Unsupported device descriptor version %4x\n", + shid->desc.hid_version); + schedule_work(&shid->error_work); + break; + } + + if (!shid->hid) + schedule_work(&shid->create_device_work); + else + schedule_work(&shid->refresh_device_work); + + break; + case SPI_HID_INPUT_REPORT_TYPE_SET_OUTPUT_REPORT_RESP: + if (shid->desc.no_output_report_ack) { + dev_err(dev, "Unexpected output report response\n"); + break; + } + fallthrough; + case SPI_HID_INPUT_REPORT_TYPE_GET_FEATURE_RESP: + case SPI_HID_INPUT_REPORT_TYPE_SET_FEATURE_RESP: + if (!shid->ready) { + dev_err(dev, + "Unexpected response report while not ready: 0x%x\n", + body.report_type); + break; + } + fallthrough; + case SPI_HID_INPUT_REPORT_TYPE_REPORT_DESC: + spi_hid_response_handler(shid, buf); + break; + /* + * FIXME: sending GET_INPUT and COMMAND reports not supported, thus + * throw away responses to those, they should never come. + */ + case SPI_HID_INPUT_REPORT_TYPE_GET_INPUT_REPORT_RESP: + case SPI_HID_INPUT_REPORT_TYPE_COMMAND_RESP: + dev_err(dev, "Not a supported report type: 0x%x\n", + body.report_type); + break; + default: + dev_err(dev, "Unknown input report: 0x%x\n", + body.report_type); + schedule_work(&shid->error_work); + break; + } +} + +static int spi_hid_bus_validate_header(struct spi_hid *shid, + struct spi_hid_input_header *header) +{ + struct device *dev = &shid->spi->dev; + + if (header->version != SPI_HID_INPUT_HEADER_VERSION) { + dev_err(dev, "Unknown input report version (v 0x%x)\n", + header->version); + return -EINVAL; + } + + if (shid->desc.max_input_length != 0 && + header->report_length > shid->desc.max_input_length) { + dev_err(dev, "Input report body size %u > max expected of %u\n", + header->report_length, + shid->desc.max_input_length); + return -EMSGSIZE; + } + + if (header->last_fragment_flag != 1) { + dev_err(dev, "Multi-fragment reports not supported\n"); + return -EOPNOTSUPP; + } + + if (header->sync_const != SPI_HID_INPUT_HEADER_SYNC_BYTE) { + dev_err(dev, "Invalid input report sync constant (0x%x)\n", + header->sync_const); + return -EINVAL; + } + + return 0; +} + +static int spi_hid_create_device(struct spi_hid *shid) +{ + struct hid_device *hid; + struct device *dev = &shid->spi->dev; + int ret; + + hid = hid_allocate_device(); + + if (IS_ERR(hid)) { + dev_err(dev, "Failed to allocate hid device: %ld\n", + PTR_ERR(hid)); + ret = PTR_ERR(hid); + return ret; + } + + hid->driver_data = shid->spi; + hid->ll_driver = &spi_hid_ll_driver; + hid->dev.parent = &shid->spi->dev; + hid->bus = BUS_SPI; + hid->version = shid->desc.hid_version; + hid->vendor = shid->desc.vendor_id; + hid->product = shid->desc.product_id; + + snprintf(hid->name, sizeof(hid->name), "spi %04hX:%04hX", + hid->vendor, hid->product); + strscpy(hid->phys, dev_name(&shid->spi->dev), sizeof(hid->phys)); + + shid->hid = hid; + + ret = hid_add_device(hid); + if (ret) { + dev_err(dev, "Failed to add hid device: %d\n", ret); + /* + * We likely got here because report descriptor request timed + * out. Let's disconnect and destroy the hid_device structure. + */ + hid = spi_hid_disconnect_hid(shid); + if (hid) + hid_destroy_device(hid); + return ret; + } + + return 0; +} + +static void spi_hid_create_device_work(struct work_struct *work) +{ + struct spi_hid *shid = + container_of(work, struct spi_hid, create_device_work); + struct device *dev = &shid->spi->dev; + u8 prev_state = shid->power_state; + int ret; + + trace_spi_hid_create_device_work(shid); + + mutex_lock(&shid->power_lock); + if (prev_state == SPI_HID_POWER_MODE_OFF) { + dev_err(dev, "%s: Powered off, returning", __func__); + goto out; + } + + ret = spi_hid_create_device(shid); + if (ret) { + dev_err(dev, "%s: Failed to create hid device\n", __func__); + goto out; + } + + spi_hid_suspend(shid); + +out: + mutex_unlock(&shid->power_lock); + + dev_dbg(dev, "%s: %s -> %s\n", __func__, + spi_hid_power_mode_string(prev_state), + spi_hid_power_mode_string(shid->power_state)); +} + +static void spi_hid_refresh_device_work(struct work_struct *work) +{ + struct spi_hid *shid = + container_of(work, struct spi_hid, refresh_device_work); + struct device *dev = &shid->spi->dev; + struct hid_device *hid; + int ret; + u32 new_crc32; + + trace_spi_hid_refresh_device_work(shid); + + mutex_lock(&shid->power_lock); + if (shid->power_state == SPI_HID_POWER_MODE_OFF) { + dev_err(dev, "%s: Powered off, returning", __func__); + goto out2; + } + + mutex_lock(&shid->output_lock); + ret = spi_hid_report_descriptor_request(shid); + mutex_unlock(&shid->output_lock); + if (ret < 0) { + dev_err(dev, + "Refresh: failed report descriptor request, error %d", + ret); + goto out2; + } + + new_crc32 = crc32_le(0, (unsigned char const *)shid->response.content, + (size_t)ret); + if (new_crc32 == shid->report_descriptor_crc32) + goto out1; + + shid->report_descriptor_crc32 = new_crc32; + shid->refresh_in_progress = true; + + hid = spi_hid_disconnect_hid(shid); + if (hid) + hid_destroy_device(hid); + + ret = spi_hid_create_device(shid); + if (ret) { + dev_err(dev, "%s: Failed to create hid device\n", __func__); + goto out2; + } + + shid->refresh_in_progress = false; + +out1: + if (completion_done(&shid->ready_done)) + dev_err(&shid->spi->dev, "Nobody waiting for ready_done\n"); + else + complete(&shid->ready_done); +out2: + mutex_unlock(&shid->power_lock); +} + +static int spi_hid_get_request(struct spi_hid *shid, u8 content_id) +{ + int ret; + struct device *dev = &shid->spi->dev; + struct spi_hid_output_report report = { + .report_type = SPI_HID_OUTPUT_REPORT_TYPE_HID_GET_FEATURE, + .content_length = 0, + .content_id = content_id, + .content = NULL, + }; + + ret = spi_hid_sync_request(shid, &report); + if (ret) { + dev_err(dev, + "Expected get request response not received! Error %d\n", + ret); + schedule_work(&shid->error_work); + } + + return ret; +} + +static int spi_hid_set_request(struct spi_hid *shid, + u8 *arg_buf, u16 arg_len, u8 content_id) +{ + struct spi_hid_output_report report = { + .report_type = SPI_HID_OUTPUT_REPORT_TYPE_HID_SET_FEATURE, + .content_length = arg_len, + .content_id = content_id, + .content = arg_buf, + }; + + return spi_hid_sync_request(shid, &report); +} + +static irqreturn_t spi_hid_dev_irq(int irq, void *_shid) +{ + struct spi_hid *shid = _shid; + struct device *dev = &shid->spi->dev; + struct spi_hid_input_header header; + int ret = 0; + + trace_spi_hid_dev_irq(shid, irq); + trace_spi_hid_header_transfer(shid); + + ret = spi_hid_input_sync(shid, shid->input.header, + sizeof(shid->input.header), true); + if (ret) { + dev_err(dev, "Failed to transfer header: %d\n", ret); + goto out; + } + + if (shid->power_state == SPI_HID_POWER_MODE_OFF) { + dev_warn(dev, "Device is off after header was received\n"); + goto out; + } + + trace_spi_hid_input_header_complete(shid, + shid->input_transfer[0].tx_buf, + shid->input_transfer[0].len, + shid->input_transfer[1].rx_buf, + shid->input_transfer[1].len, + shid->input_message.status); + + if (shid->input_message.status < 0) { + dev_warn(dev, "Error reading header: %d\n", + shid->input_message.status); + shid->bus_error_count++; + shid->bus_last_error = shid->input_message.status; + schedule_work(&shid->error_work); + goto out; + } + + spi_hid_populate_input_header(shid->input.header, &header); + + ret = spi_hid_bus_validate_header(shid, &header); + if (ret) { + dev_err(dev, "Failed to validate header: %d\n", ret); + print_hex_dump(KERN_ERR, "spi_hid: header buffer: ", + DUMP_PREFIX_NONE, 16, 1, + shid->input.header, + sizeof(shid->input.header), + false); + shid->bus_error_count++; + shid->bus_last_error = ret; + goto out; + } + + ret = spi_hid_input_sync(shid, shid->input.body, header.report_length, + false); + if (ret) + dev_err(dev, "Failed to transfer body: %d\n", ret); + + if (shid->power_state == SPI_HID_POWER_MODE_OFF) { + dev_warn(dev, "Device is off after body was received\n"); + goto out; + } + + trace_spi_hid_input_body_complete(shid, shid->input_transfer[0].tx_buf, + shid->input_transfer[0].len, + shid->input_transfer[1].rx_buf, + shid->input_transfer[1].len, + shid->input_message.status); + + if (shid->input_message.status < 0) { + dev_warn(dev, "Error reading body: %d\n", + shid->input_message.status); + shid->bus_error_count++; + shid->bus_last_error = shid->input_message.status; + schedule_work(&shid->error_work); + goto out; + } + + spi_hid_process_input_report(shid, &shid->input); + +out: + return IRQ_HANDLED; +} + +/* hid_ll_driver interface functions */ + +static int spi_hid_ll_start(struct hid_device *hid) +{ + struct spi_device *spi = hid->driver_data; + struct spi_hid *shid = spi_get_drvdata(spi); + + if (shid->desc.max_input_length < HID_MIN_BUFFER_SIZE) { + dev_err(&shid->spi->dev, + "HID_MIN_BUFFER_SIZE > max_input_length (%d)\n", + shid->desc.max_input_length); + return -EINVAL; + } + + return 0; +} + +static void spi_hid_ll_stop(struct hid_device *hid) +{ + hid->claimed = 0; +} + +static int spi_hid_ll_open(struct hid_device *hid) +{ + struct spi_device *spi = hid->driver_data; + struct spi_hid *shid = spi_get_drvdata(spi); + struct device *dev = &spi->dev; + u8 prev_state = shid->power_state; + int ret; + + if (shid->refresh_in_progress) + return 0; + + spi_hid_resume(shid); + + dev_dbg(dev, "%s: %s -> %s\n", __func__, + spi_hid_power_mode_string(prev_state), + spi_hid_power_mode_string(shid->power_state)); + + reinit_completion(&shid->ready_done); + ret = wait_for_completion_interruptible_timeout(&shid->ready_done, + msecs_to_jiffies(1000)); + if (ret == 0) { + dev_err(dev, "Handshake timed out\n"); + return -EAGAIN; + } else { + dev_dbg(dev, "%s: ready\n", __func__); + shid->ready = true; + return 0; + } +} + +static void spi_hid_ll_close(struct hid_device *hid) +{ + struct spi_device *spi = hid->driver_data; + struct spi_hid *shid = spi_get_drvdata(spi); + struct device *dev = &spi->dev; + u8 prev_state = shid->power_state; + + if (shid->refresh_in_progress) + return; + + mutex_lock(&shid->power_lock); + spi_hid_suspend(shid); + mutex_unlock(&shid->power_lock); + + shid->attempts = 0; + + dev_dbg(dev, "%s: %s -> %s\n", __func__, + spi_hid_power_mode_string(prev_state), + spi_hid_power_mode_string(shid->power_state)); +} + +static int spi_hid_ll_power(struct hid_device *hid, int level) +{ + struct spi_device *spi = hid->driver_data; + struct spi_hid *shid = spi_get_drvdata(spi); + int ret = 0; + + mutex_lock(&shid->output_lock); + if (!shid->hid) + ret = -ENODEV; + mutex_unlock(&shid->output_lock); + + return ret; +} + +static int spi_hid_ll_parse(struct hid_device *hid) +{ + struct spi_device *spi = hid->driver_data; + struct spi_hid *shid = spi_get_drvdata(spi); + struct device *dev = &spi->dev; + int ret, len; + + mutex_lock(&shid->output_lock); + + len = spi_hid_report_descriptor_request(shid); + if (len < 0) { + dev_err(dev, "Report descriptor request failed, %d\n", len); + ret = len; + goto out; + } + + /* + * FIXME: below call returning 0 doesn't mean that the report descriptor + * is good. We might be caching a crc32 of a corrupted r. d. or who + * knows what the FW sent. Need to have a feedback loop about r. d. + * being ok and only then cache it. + */ + ret = hid_parse_report(hid, (u8 *)shid->response.content, len); + if (ret) + dev_err(dev, "failed parsing report: %d\n", ret); + else + shid->report_descriptor_crc32 = crc32_le(0, + (unsigned char const *)shid->response.content, + len); + +out: + mutex_unlock(&shid->output_lock); + + return ret; +} + +static int spi_hid_ll_raw_request(struct hid_device *hid, + unsigned char reportnum, u8 *buf, size_t len, + unsigned char rtype, int reqtype) +{ + struct spi_device *spi = hid->driver_data; + struct spi_hid *shid = spi_get_drvdata(spi); + struct device *dev = &spi->dev; + int ret; + + if (!shid->ready) { + dev_err(&shid->spi->dev, "%s called in unready state\n", + __func__); + return -ENODEV; + } + + mutex_lock(&shid->output_lock); + + switch (reqtype) { + case HID_REQ_SET_REPORT: + if (buf[0] != reportnum) { + dev_err(dev, "report id mismatch\n"); + ret = -EINVAL; + break; + } + + ret = spi_hid_set_request(shid, &buf[1], len - 1, + reportnum); + if (ret) { + dev_err(dev, "failed to set report\n"); + break; + } + + ret = len; + break; + case HID_REQ_GET_REPORT: + ret = spi_hid_get_request(shid, reportnum); + if (ret) { + dev_err(dev, "failed to get report\n"); + break; + } + + ret = min_t(size_t, len, + shid->response.body[1] | (shid->response.body[2] << 8)); + memcpy(buf, &shid->response.content, ret); + break; + default: + dev_err(dev, "invalid request type\n"); + ret = -EIO; + } + + mutex_unlock(&shid->output_lock); + + return ret; +} + +static int spi_hid_ll_output_report(struct hid_device *hid, + u8 *buf, size_t len) +{ + int ret; + struct spi_device *spi = hid->driver_data; + struct spi_hid *shid = spi_get_drvdata(spi); + struct device *dev = &spi->dev; + struct spi_hid_output_report report = { + .report_type = SPI_HID_OUTPUT_REPORT_TYPE_HID_OUTPUT_REPORT, + .content_length = len - 1, + .content_id = buf[0], + .content = &buf[1], + }; + + mutex_lock(&shid->output_lock); + if (!shid->ready) { + dev_err(dev, "%s called in unready state\n", __func__); + ret = -ENODEV; + goto out; + } + + if (shid->desc.no_output_report_ack) + ret = spi_hid_send_output_report(shid, &report); + else + ret = spi_hid_sync_request(shid, &report); + + if (ret) + dev_err(dev, "failed to send output report\n"); + +out: + mutex_unlock(&shid->output_lock); + + if (ret > 0) + return -ret; + + if (ret < 0) + return ret; + + return len; +} + +static struct hid_ll_driver spi_hid_ll_driver = { + .start = spi_hid_ll_start, + .stop = spi_hid_ll_stop, + .open = spi_hid_ll_open, + .close = spi_hid_ll_close, + .power = spi_hid_ll_power, + .parse = spi_hid_ll_parse, + .output_report = spi_hid_ll_output_report, + .raw_request = spi_hid_ll_raw_request, +}; + +const struct of_device_id spi_hid_of_match[] = { + { .compatible = "hid-over-spi" }, + {}, +}; +MODULE_DEVICE_TABLE(of, spi_hid_of_match); + +static ssize_t bus_error_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spi_hid *shid = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%d (%d)\n", + shid->bus_error_count, shid->bus_last_error); +} +static DEVICE_ATTR_RO(bus_error_count); + +static ssize_t regulator_error_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spi_hid *shid = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%d (%d)\n", + shid->regulator_error_count, + shid->regulator_last_error); +} +static DEVICE_ATTR_RO(regulator_error_count); + +static ssize_t device_initiated_reset_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spi_hid *shid = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", shid->dir_count); +} +static DEVICE_ATTR_RO(device_initiated_reset_count); + +static const struct attribute *const spi_hid_attributes[] = { + &dev_attr_bus_error_count.attr, + &dev_attr_regulator_error_count.attr, + &dev_attr_device_initiated_reset_count.attr, + NULL /* Terminator */ +}; + +static int spi_hid_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct spi_hid *shid; + unsigned long irqflags; + int ret; + + if (spi->irq <= 0) { + dev_err(dev, "Missing IRQ\n"); + ret = spi->irq ?: -EINVAL; + goto err0; + } + + shid = devm_kzalloc(dev, sizeof(struct spi_hid), GFP_KERNEL); + if (!shid) { + ret = -ENOMEM; + goto err0; + } + + shid->spi = spi; + shid->power_state = SPI_HID_POWER_MODE_ON; + spi_set_drvdata(spi, shid); + + ret = sysfs_create_files(&dev->kobj, spi_hid_attributes); + if (ret) { + dev_err(dev, "Unable to create sysfs attributes\n"); + goto err0; + } + + ret = spi_hid_of_populate_config(&shid->conf, dev); + if (ret) { + dev_err(dev, "%s: unable to populate config data\n", __func__); + goto err1; + } + + /* Using now populated conf let's pre-calculate the read approvals */ + spi_hid_populate_read_approvals(&shid->conf, shid->read_approval_header, + shid->read_approval_body); + + mutex_init(&shid->output_lock); + mutex_init(&shid->power_lock); + init_completion(&shid->output_done); + init_completion(&shid->ready_done); + + INIT_WORK(&shid->reset_response_work, spi_hid_reset_response_work); + INIT_WORK(&shid->create_device_work, spi_hid_create_device_work); + INIT_WORK(&shid->refresh_device_work, spi_hid_refresh_device_work); + INIT_WORK(&shid->error_work, spi_hid_error_work); + + /* + * At the end of probe we initialize the device: + * 0) Default pinctrl in DT: assert reset, bias the interrupt line + * 1) sleep minimal reset delay + * 2) request IRQ + * 3) power up the device + * 4) deassert reset (high) + * After this we expect an IRQ with a reset response. + */ + + spi_hid_of_sleep_minimal_reset_delay(&shid->conf); + + irqflags = irq_get_trigger_type(spi->irq) | IRQF_ONESHOT; + ret = devm_request_threaded_irq(dev, spi->irq, NULL, spi_hid_dev_irq, + irqflags, dev_name(&spi->dev), shid); + if (ret) { + dev_err(dev, "%s: unable to request threaded IRQ\n", __func__); + goto err1; + } + shid->irq_enabled = true; + + ret = spi_hid_of_power_up(&shid->conf); + if (ret) { + dev_err(dev, "%s: could not power up\n", __func__); + shid->regulator_error_count++; + shid->regulator_last_error = ret; + goto err1; + } + + spi_hid_of_deassert_reset(&shid->conf); + + dev_dbg(dev, "%s: d3 -> %s\n", __func__, + spi_hid_power_mode_string(shid->power_state)); + + return 0; + +err1: + sysfs_remove_files(&dev->kobj, spi_hid_attributes); + +err0: + return ret; +} + +static int spi_hid_remove(struct spi_device *spi) +{ + struct spi_hid *shid = spi_get_drvdata(spi); + struct device *dev = &spi->dev; + int ret; + + spi_hid_of_assert_reset(&shid->conf); + ret = spi_hid_of_power_down(&shid->conf); + if (ret) { + dev_err(dev, "failed to disable regulator\n"); + shid->regulator_error_count++; + shid->regulator_last_error = ret; + } + sysfs_remove_files(&dev->kobj, spi_hid_attributes); + spi_hid_stop_hid(shid); + + return 0; +} + +static const struct spi_device_id spi_hid_id_table[] = { + { "hid", 0 }, + { "hid-over-spi", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(spi, spi_hid_id_table); + +static struct spi_driver spi_hid_driver = { + .driver = { + .name = "spi_hid", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(spi_hid_of_match), + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .probe = spi_hid_probe, + .remove = spi_hid_remove, + .id_table = spi_hid_id_table, +}; + +module_spi_driver(spi_hid_driver); + +MODULE_DESCRIPTION("HID over SPI transport driver"); +MODULE_AUTHOR("Dmitry Antipov "); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/spi-hid/spi-hid-core.h b/drivers/hid/spi-hid/spi-hid-core.h new file mode 100644 index 000000000000..45cf3fd2b369 --- /dev/null +++ b/drivers/hid/spi-hid/spi-hid-core.h @@ -0,0 +1,188 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 Microsoft Corporation + */ + +#ifndef SPI_HID_CORE_H +#define SPI_HID_CORE_H + +#include +#include +#include +#include +#include + +#include "spi-hid-of.h" + +/* Protocol constants */ +#define SPI_HID_READ_APPROVAL_CONSTANT 0xff +#define SPI_HID_INPUT_HEADER_SYNC_BYTE 0x5a +#define SPI_HID_INPUT_HEADER_VERSION 0x03 +#define SPI_HID_SUPPORTED_VERSION 0x0300 + +/* Protocol message size constants */ +#define SPI_HID_READ_APPROVAL_LEN 5 +#define SPI_HID_INPUT_HEADER_LEN 4 +#define SPI_HID_INPUT_BODY_LEN 4 +#define SPI_HID_OUTPUT_HEADER_LEN 8 +#define SPI_HID_DEVICE_DESCRIPTOR_LENGTH 24 + +/* Protocol message type constants */ +#define SPI_HID_INPUT_REPORT_TYPE_DATA 0x01 +#define SPI_HID_INPUT_REPORT_TYPE_RESET_RESP 0x03 +#define SPI_HID_INPUT_REPORT_TYPE_COMMAND_RESP 0x04 +#define SPI_HID_INPUT_REPORT_TYPE_GET_FEATURE_RESP 0x05 +#define SPI_HID_INPUT_REPORT_TYPE_DEVICE_DESC 0x07 +#define SPI_HID_INPUT_REPORT_TYPE_REPORT_DESC 0x08 +#define SPI_HID_INPUT_REPORT_TYPE_SET_FEATURE_RESP 0x09 +#define SPI_HID_INPUT_REPORT_TYPE_SET_OUTPUT_REPORT_RESP 0x0a +#define SPI_HID_INPUT_REPORT_TYPE_GET_INPUT_REPORT_RESP 0x0b + +#define SPI_HID_OUTPUT_REPORT_TYPE_DEVICE_DESC_REQUEST 0x01 +#define SPI_HID_OUTPUT_REPORT_TYPE_REPORT_DESC_REQUEST 0x02 +#define SPI_HID_OUTPUT_REPORT_TYPE_HID_SET_FEATURE 0x03 +#define SPI_HID_OUTPUT_REPORT_TYPE_HID_GET_FEATURE 0x04 +#define SPI_HID_OUTPUT_REPORT_TYPE_HID_OUTPUT_REPORT 0x05 +#define SPI_HID_OUTPUT_REPORT_TYPE_INPUT_REPORT_REQUEST 0x06 +#define SPI_HID_OUTPUT_REPORT_TYPE_COMMAND 0x07 + +#define SPI_HID_OUTPUT_REPORT_CONTENT_ID_DESC_REQUEST 0x00 + +/* Power mode constants */ +#define SPI_HID_POWER_MODE_ON 0x01 +#define SPI_HID_POWER_MODE_SLEEP 0x02 +#define SPI_HID_POWER_MODE_OFF 0x03 +#define SPI_HID_POWER_MODE_WAKING_SLEEP 0x04 + +/* Raw input buffer with data from the bus */ +struct spi_hid_input_buf { + u8 header[SPI_HID_INPUT_HEADER_LEN]; + u8 body[SPI_HID_INPUT_BODY_LEN]; + u8 content[SZ_8K]; +}; + +/* Processed data from input report header */ +struct spi_hid_input_header { + u8 version; + u16 report_length; + u8 last_fragment_flag; + u8 sync_const; +}; + +/* Processed data from input report body, excluding the content */ +struct spi_hid_input_body { + u8 report_type; + u16 content_length; + u8 content_id; +}; + +/* Processed data from an input report */ +struct spi_hid_input_report { + u8 report_type; + u16 content_length; + u8 content_id; + u8 *content; +}; + +/* Raw output report buffer to be put on the bus */ +struct spi_hid_output_buf { + u8 header[SPI_HID_OUTPUT_HEADER_LEN]; + u8 content[SZ_8K]; +}; + +/* Data necessary to send an output report */ +struct spi_hid_output_report { + u8 report_type; + u16 content_length; + u8 content_id; + u8 *content; +}; + +/* Raw content in device descriptor */ +struct spi_hid_device_desc_raw { + __le16 wDeviceDescLength; + __le16 bcdVersion; + __le16 wReportDescLength; + __le16 wMaxInputLength; + __le16 wMaxOutputLength; + __le16 wMaxFragmentLength; + __le16 wVendorID; + __le16 wProductID; + __le16 wVersionID; + __le16 wFlags; + u8 reserved[4]; +} __packed; + +/* Processed data from a device descriptor */ +struct spi_hid_device_descriptor { + u16 hid_version; + u16 report_descriptor_length; + u16 max_input_length; + u16 max_output_length; + u16 max_fragment_length; + u16 vendor_id; + u16 product_id; + u16 version_id; + u8 no_output_report_ack; +}; + +/* Driver context */ +struct spi_hid { + struct spi_device *spi; + struct hid_device *hid; + + struct spi_transfer input_transfer[2]; + struct spi_transfer output_transfer; + struct spi_message input_message; + struct spi_message output_message; + + struct spi_hid_of_config conf; + struct spi_hid_device_descriptor desc; + struct spi_hid_output_buf output; + struct spi_hid_input_buf input; + struct spi_hid_input_buf response; + + u32 input_transfer_pending; + + u8 power_state; + + u8 attempts; + + /* + * ready flag indicates that the FW is ready to accept commands and + * requests. The FW becomes ready after sending the report descriptor. + */ + bool ready; + /* + * refresh_in_progress is set to true while the refresh_device worker + * thread is destroying and recreating the hidraw device. When this flag + * is set to true, the ll_close and ll_open functions will not cause + * power state changes. + */ + bool refresh_in_progress; + + bool irq_enabled; + + struct work_struct reset_response_work; + struct work_struct create_device_work; + struct work_struct refresh_device_work; + struct work_struct error_work; + + struct mutex output_lock; + struct mutex power_lock; + struct completion output_done; + struct completion ready_done; + + u8 read_approval_header[SPI_HID_READ_APPROVAL_LEN]; + u8 read_approval_body[SPI_HID_READ_APPROVAL_LEN]; + + u32 report_descriptor_crc32; + + u32 regulator_error_count; + int regulator_last_error; + u32 bus_error_count; + int bus_last_error; + u32 dir_count; +}; + +#endif diff --git a/drivers/hid/spi-hid/spi-hid-of.c b/drivers/hid/spi-hid/spi-hid-of.c new file mode 100644 index 000000000000..d8d1d134cf29 --- /dev/null +++ b/drivers/hid/spi-hid/spi-hid-of.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * HID over SPI protocol, Open Firmware related code + * + * Copyright (c) 2021 Microsoft Corporation + * + * This code was forked out of the HID over SPI core code, which is partially + * based on "HID over I2C protocol implementation: + * + * Copyright (c) 2012 Benjamin Tissoires + * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France + * Copyright (c) 2012 Red Hat, Inc + * + * which in turn is partially based on "USB HID support for Linux": + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2007-2008 Oliver Neukum + * Copyright (c) 2006-2010 Jiri Kosina + */ +#include +#include +#include +#include + +#include "spi-hid-core.h" + +int spi_hid_of_populate_config(struct spi_hid_of_config *conf, + struct device *dev) +{ + int ret; + u32 val; + + ret = device_property_read_u32(dev, "input-report-header-address", + &val); + if (ret) { + dev_err(dev, "Input report header address not provided\n"); + return -ENODEV; + } + conf->input_report_header_address = val; + + ret = device_property_read_u32(dev, "input-report-body-address", &val); + if (ret) { + dev_err(dev, "Input report body address not provided\n"); + return -ENODEV; + } + conf->input_report_body_address = val; + + ret = device_property_read_u32(dev, "output-report-address", &val); + if (ret) { + dev_err(dev, "Output report address not provided\n"); + return -ENODEV; + } + conf->output_report_address = val; + + ret = device_property_read_u32(dev, "read-opcode", &val); + if (ret) { + dev_err(dev, "Read opcode not provided\n"); + return -ENODEV; + } + conf->read_opcode = val; + + ret = device_property_read_u32(dev, "write-opcode", &val); + if (ret) { + dev_err(dev, "Write opcode not provided\n"); + return -ENODEV; + } + conf->write_opcode = val; + + ret = device_property_read_u32(dev, "post-power-on-delay-ms", &val); + if (ret) { + dev_err(dev, "post-power-on-delay-ms not provided, using 10\n"); + val = 10; + } + conf->post_power_on_delay_ms = val; + + ret = device_property_read_u32(dev, "minimal-reset-delay-ms", &val); + if (ret) { + dev_err(dev, "minimal-reset-delay-ms not provided, using 100\n"); + val = 100; + } + conf->minimal_reset_delay_ms = val; + + /* FIXME: not reading hid-over-spi-flags, multi-SPI not supported */ + + conf->supply = devm_regulator_get(dev, "vdd"); + if (IS_ERR(conf->supply)) { + if (PTR_ERR(conf->supply) != -EPROBE_DEFER) + dev_err(dev, "Failed to get regulator: %ld\n", + PTR_ERR(conf->supply)); + return PTR_ERR(conf->supply); + } + + conf->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(conf->reset_gpio)) { + dev_err(dev, "%s: error getting reset GPIO\n", __func__); + return PTR_ERR(conf->reset_gpio); + } + + return 0; +} + +int spi_hid_of_power_down(struct spi_hid_of_config *conf) +{ + if (regulator_is_enabled(conf->supply) == 0) + return 0; + + return regulator_disable(conf->supply); +} + +int spi_hid_of_power_up(struct spi_hid_of_config *conf) +{ + int ret; + + if (regulator_is_enabled(conf->supply) > 0) + return 0; + + ret = regulator_enable(conf->supply); + + usleep_range(1000 * conf->post_power_on_delay_ms, + 1000 * (conf->post_power_on_delay_ms + 1)); + + return ret; +} + +void spi_hid_of_assert_reset(struct spi_hid_of_config *conf) +{ + gpiod_set_value(conf->reset_gpio, 1); +} + +void spi_hid_of_deassert_reset(struct spi_hid_of_config *conf) +{ + gpiod_set_value(conf->reset_gpio, 0); +} + +void spi_hid_of_sleep_minimal_reset_delay(struct spi_hid_of_config *conf) +{ + usleep_range(1000 * conf->minimal_reset_delay_ms, + 1000 * (conf->minimal_reset_delay_ms + 1)); +} diff --git a/drivers/hid/spi-hid/spi-hid-of.h b/drivers/hid/spi-hid/spi-hid-of.h new file mode 100644 index 000000000000..48d9dfe44e95 --- /dev/null +++ b/drivers/hid/spi-hid/spi-hid-of.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 Microsoft Corporation + */ + +#ifndef SPI_HID_OF_H +#define SPI_HID_OF_H + +/* Config structure is filled with data from Device Tree */ +struct spi_hid_of_config { + u32 input_report_header_address; + u32 input_report_body_address; + u32 output_report_address; + u8 read_opcode; + u8 write_opcode; + u32 post_power_on_delay_ms; + u32 minimal_reset_delay_ms; + struct gpio_desc *reset_gpio; + struct regulator *supply; +}; + +int spi_hid_of_populate_config(struct spi_hid_of_config *conf, + struct device *dev); +int spi_hid_of_power_down(struct spi_hid_of_config *conf); +int spi_hid_of_power_up(struct spi_hid_of_config *conf); +void spi_hid_of_assert_reset(struct spi_hid_of_config *conf); +void spi_hid_of_deassert_reset(struct spi_hid_of_config *conf); +void spi_hid_of_sleep_minimal_reset_delay(struct spi_hid_of_config *conf); + +#endif diff --git a/drivers/hid/spi-hid/spi-hid_trace.h b/drivers/hid/spi-hid/spi-hid_trace.h new file mode 100644 index 000000000000..23732ba493a0 --- /dev/null +++ b/drivers/hid/spi-hid/spi-hid_trace.h @@ -0,0 +1,194 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 Microsoft Corporation + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM spi_hid + +#if !defined(_SPI_HID_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _SPI_HID_TRACE_H + +#include +#include +#include "spi-hid-core.h" + +DECLARE_EVENT_CLASS(spi_hid_transfer, + TP_PROTO(struct spi_hid *shid, const void *tx_buf, int tx_len, + const void *rx_buf, u16 rx_len, int ret), + + TP_ARGS(shid, tx_buf, tx_len, rx_buf, rx_len, ret), + + TP_STRUCT__entry( + __field(int, bus_num) + __field(int, chip_select) + __field(int, len) + __field(int, ret) + __dynamic_array(u8, rx_buf, rx_len) + __dynamic_array(u8, tx_buf, tx_len) + ), + + TP_fast_assign( + __entry->bus_num = shid->spi->controller->bus_num; + __entry->chip_select = shid->spi->chip_select; + __entry->len = rx_len + tx_len; + __entry->ret = ret; + + memcpy(__get_dynamic_array(tx_buf), tx_buf, tx_len); + memcpy(__get_dynamic_array(rx_buf), rx_buf, rx_len); + ), + + TP_printk("spi%d.%d: len=%d tx=[%*phD] rx=[%*phD] --> %d", + __entry->bus_num, __entry->chip_select, __entry->len, + __get_dynamic_array_len(tx_buf), __get_dynamic_array(tx_buf), + __get_dynamic_array_len(rx_buf), __get_dynamic_array(rx_buf), + __entry->ret) +); + +DEFINE_EVENT(spi_hid_transfer, spi_hid_input_sync, + TP_PROTO(struct spi_hid *shid, const void *tx_buf, int tx_len, + const void *rx_buf, u16 rx_len, int ret), + TP_ARGS(shid, tx_buf, tx_len, rx_buf, rx_len, ret) +); + +DEFINE_EVENT(spi_hid_transfer, spi_hid_input_header_complete, + TP_PROTO(struct spi_hid *shid, const void *tx_buf, int tx_len, + const void *rx_buf, u16 rx_len, int ret), + TP_ARGS(shid, tx_buf, tx_len, rx_buf, rx_len, ret) +); + +DEFINE_EVENT(spi_hid_transfer, spi_hid_input_body_complete, + TP_PROTO(struct spi_hid *shid, const void *tx_buf, int tx_len, + const void *rx_buf, u16 rx_len, int ret), + TP_ARGS(shid, tx_buf, tx_len, rx_buf, rx_len, ret) +); + +DEFINE_EVENT(spi_hid_transfer, spi_hid_output_begin, + TP_PROTO(struct spi_hid *shid, const void *tx_buf, int tx_len, + const void *rx_buf, u16 rx_len, int ret), + TP_ARGS(shid, tx_buf, tx_len, rx_buf, rx_len, ret) +); + +DEFINE_EVENT(spi_hid_transfer, spi_hid_output_end, + TP_PROTO(struct spi_hid *shid, const void *tx_buf, int tx_len, + const void *rx_buf, u16 rx_len, int ret), + TP_ARGS(shid, tx_buf, tx_len, rx_buf, rx_len, ret) +); + +DECLARE_EVENT_CLASS(spi_hid_irq, + TP_PROTO(struct spi_hid *shid, int irq), + + TP_ARGS(shid, irq), + + TP_STRUCT__entry( + __field(int, bus_num) + __field(int, chip_select) + __field(int, irq) + ), + + TP_fast_assign( + __entry->bus_num = shid->spi->controller->bus_num; + __entry->chip_select = shid->spi->chip_select; + __entry->irq = irq; + ), + + TP_printk("spi%d.%d: IRQ %d", + __entry->bus_num, __entry->chip_select, __entry->irq) +); + +DEFINE_EVENT(spi_hid_irq, spi_hid_dev_irq, + TP_PROTO(struct spi_hid *shid, int irq), + TP_ARGS(shid, irq) +); + +DECLARE_EVENT_CLASS(spi_hid, + TP_PROTO(struct spi_hid *shid), + + TP_ARGS(shid), + + TP_STRUCT__entry( + __field(int, bus_num) + __field(int, chip_select) + __field(int, power_state) + __field(bool, ready) + + __field(int, vendor_id) + __field(int, product_id) + __field(int, max_input_length) + __field(int, max_output_length) + __field(u16, hid_version) + __field(u16, report_descriptor_length) + __field(u16, version_id) + ), + + TP_fast_assign( + __entry->bus_num = shid->spi->controller->bus_num; + __entry->chip_select = shid->spi->chip_select; + __entry->power_state = shid->power_state; + __entry->ready = shid->ready; + + __entry->vendor_id = shid->desc.vendor_id; + __entry->product_id = shid->desc.product_id; + __entry->max_input_length = shid->desc.max_input_length; + __entry->max_output_length = shid->desc.max_output_length; + __entry->hid_version = shid->desc.hid_version; + __entry->report_descriptor_length = + shid->desc.report_descriptor_length; + __entry->version_id = shid->desc.version_id; + ), + + TP_printk("spi%d.%d: (%04x:%04x v%d) HID v%d.%d state i:%d p:%d len i:%d o:%d r:%d flags %c", + __entry->bus_num, __entry->chip_select, __entry->vendor_id, + __entry->product_id, __entry->version_id, + __entry->hid_version >> 8, __entry->hid_version & 0xff, + __entry->power_state, __entry->max_input_length, + __entry->max_output_length, __entry->report_descriptor_length, + __entry->ready ? 'R' : 'r') +); + +DEFINE_EVENT(spi_hid, spi_hid_header_transfer, + TP_PROTO(struct spi_hid *shid), + TP_ARGS(shid) +); + +DEFINE_EVENT(spi_hid, spi_hid_process_input_report, + TP_PROTO(struct spi_hid *shid), + TP_ARGS(shid) +); + +DEFINE_EVENT(spi_hid, spi_hid_input_report_handler, + TP_PROTO(struct spi_hid *shid), + TP_ARGS(shid) +); + +DEFINE_EVENT(spi_hid, spi_hid_reset_response_work, + TP_PROTO(struct spi_hid *shid), + TP_ARGS(shid) +); + +DEFINE_EVENT(spi_hid, spi_hid_create_device_work, + TP_PROTO(struct spi_hid *shid), + TP_ARGS(shid) +); + +DEFINE_EVENT(spi_hid, spi_hid_refresh_device_work, + TP_PROTO(struct spi_hid *shid), + TP_ARGS(shid) +); + +DEFINE_EVENT(spi_hid, spi_hid_response_handler, + TP_PROTO(struct spi_hid *shid), + TP_ARGS(shid) +); + +DEFINE_EVENT(spi_hid, spi_hid_error_work, + TP_PROTO(struct spi_hid *shid), + TP_ARGS(shid) +); + +#endif /* _SPI_HID_TRACE_H */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE spi-hid_trace +#include diff --git a/drivers/hid/spi-hid/trace.c b/drivers/hid/spi-hid/trace.c new file mode 100644 index 000000000000..6cd686c9493e --- /dev/null +++ b/drivers/hid/spi-hid/trace.c @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 Microsoft Corporation + * + * Author: Felipe Balbi + */ + +#define CREATE_TRACE_POINTS +#include "spi-hid_trace.h" From patchwork Thu Jul 7 16:59:02 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarrett Schultz X-Patchwork-Id: 12909988 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A5334CCA47F for ; Thu, 7 Jul 2022 16:59:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236394AbiGGQ7e (ORCPT ); Thu, 7 Jul 2022 12:59:34 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37790 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236360AbiGGQ7a (ORCPT ); Thu, 7 Jul 2022 12:59:30 -0400 Received: from mail-pg1-x529.google.com (mail-pg1-x529.google.com [IPv6:2607:f8b0:4864:20::529]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B19A223BD3; Thu, 7 Jul 2022 09:59:29 -0700 (PDT) Received: by mail-pg1-x529.google.com with SMTP id bf13so6542063pgb.11; Thu, 07 Jul 2022 09:59:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=aCuy9HCuGOb/4o/JB1pE/O8o1Pj0qs6rlMMSXSwX4gQ=; b=oeWLvkGc74pEhVPGeRO4qxrGcwiWQtMrCyKy3h/uVtGGaP6vElja2Vr6sT+lqYUsbY SUH91UjIgU5ryiVWbKb7/RxdiQ1YqHf0ePPPvQ/auhjzA7Ec6YizG00e5KE1iDHro2VF yQuHUtPvcdXHlI/0yly1ZnSpyASWFPD5WgQyGXCB6fOYhUONQoPbJL0l4wJXeslK8Okn uTW4rcaFLIAZ9j8qHbiozGjtK/rfOiw/1VVzaeF9xHCZZ2wdZd78Z3vo+Rem4kc4wqUY oJRJ/OviUEf0fUevLlqCQmW0QDlSDaKyQlk4TUya+vv/EU6wI5JAk/K4bIanxSwCOmsn RwzQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=aCuy9HCuGOb/4o/JB1pE/O8o1Pj0qs6rlMMSXSwX4gQ=; b=fFdBnT4k/PdlGzUcYMI14Y4BmpgwWNb5ZwtdVqnJIgENShZQfTIjd9v+ysNCeth/R6 wxMmBUX5EPDX2zAVCh9tBLUjeJpWTOVEOPpUBZdyj1psNv0daDIa95e3dNhULYFNKRCz NB8In03yDOtgLZnVxKECMXwkhZnvzidGc+uYK7nqrSTysODs7SEUF8AWJ36hEmSQ79zS LZmPrArRknLKwnikxbsdTG0pg+mEr16gZeKGrWCf4hy7BjSZvmidlxjUyfsM+qLKsEOJ as8LSFzifeXsQrnwuguuBAMfmZR4fkesz1qY1Ca5t8TWAfeJcux5ZRkj/WzV2iw9Lokw nt8A== X-Gm-Message-State: AJIora/t2VZFFDWjLkh62sFaO3uDWkixDPooformNJFE2JXcMUAV4ptb WdafpHNyTw97rcvV60RuBq8= X-Google-Smtp-Source: AGRyM1tqHHvSjFqOODZz+u4tzjFjlRbky2Awv11XBgWQmDqOEr7/h909XjWVppBPflLvsJREx2bY5g== X-Received: by 2002:a65:6e96:0:b0:415:5973:b4f4 with SMTP id bm22-20020a656e96000000b004155973b4f4mr1109950pgb.568.1657213169193; Thu, 07 Jul 2022 09:59:29 -0700 (PDT) Received: from jaschultz-Thelio-Major.corp.microsoft.com ([2001:4898:80e8:37:6a04:c27c:dcee:eb11]) by smtp.gmail.com with ESMTPSA id v14-20020aa7808e000000b00518e1251197sm28184185pff.148.2022.07.07.09.59.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 Jul 2022 09:59:28 -0700 (PDT) From: Jarrett Schultz X-Google-Original-From: Jarrett Schultz To: Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Jonathan Corbet , Catalin Marinas , Will Deacon , Jiri Kosina , Benjamin Tissoires , Bjorn Andersson , Shawn Guo , Geert Uytterhoeven , Marcel Ziswiler , Biju Das , Dmitry Baryshkov , Vinod Koul Cc: Dmitry Antipov , linux-input@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Jarrett Schultz Subject: [PATCH v5 6/6] Enable building drivers/hid/spi-hid as a module Date: Thu, 7 Jul 2022 09:59:02 -0700 Message-Id: <20220707165902.3184-7-jaschultzMS@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220707165902.3184-1-jaschultzMS@gmail.com> References: <20220707165902.3184-1-jaschultzMS@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org From: Jarrett Schultz Signed-off-by: Jarrett Schultz --- arch/arm64/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 7d1105343bc2..731fc6e67f31 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -848,6 +848,7 @@ CONFIG_SND_AUDIO_GRAPH_CARD2=m CONFIG_HID_MULTITOUCH=m CONFIG_I2C_HID_ACPI=m CONFIG_I2C_HID_OF=m +CONFIG_SPI_HID=m CONFIG_USB=y CONFIG_USB_OTG=y CONFIG_USB_XHCI_HCD=y