From nobody Tue Sep 9 21:32:25 2025 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 01B87EE49AD for ; Thu, 24 Aug 2023 11:11:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240972AbjHXLKn (ORCPT ); Thu, 24 Aug 2023 07:10:43 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58462 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240917AbjHXLK1 (ORCPT ); Thu, 24 Aug 2023 07:10:27 -0400 Received: from mail-lf1-x130.google.com (mail-lf1-x130.google.com [IPv6:2a00:1450:4864:20::130]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BF0E110F3; Thu, 24 Aug 2023 04:10:25 -0700 (PDT) Received: by mail-lf1-x130.google.com with SMTP id 2adb3069b0e04-5007f3d3235so6963186e87.2; Thu, 24 Aug 2023 04:10:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1692875424; x=1693480224; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=zOqOGUu9XTpMby37J2/6NyuYZOYyXurniqTokuMhOjc=; b=M+ZdVGijwgF6EDYjuIa4KPI6rGuaBeF2uOMBDDa4zccLcLAbMc/XFmBKGBlZYLqPnK MpmxyILHpN4EohLUOiONW5ayd+bxYabRsixhhMsTRPFemanQUWr/xderFFq2rwx24tmU 9IQBZn7/RF2Bqo2dy+2FhxnMrhXR8n7W/c0dIiSq1+mEif58WDN4fyZOfpIPavGZQSjP csExQjJSkrrJzuotfeE/+SBb+SKQBiSXIGjycTgLXq9jyOH04k2FoOfpSn6Ks46/lYr3 GnaiYFhc1XVBTEY6F8VirYZNjeIyiozusv92fk/403/2dyo1Na99Jctu1LH5gMoMeKZI uqYQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1692875424; x=1693480224; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=zOqOGUu9XTpMby37J2/6NyuYZOYyXurniqTokuMhOjc=; b=BbAT8tukKkAnJXGsXx1VOvhLaUa7iL/2XXMoQTM0PWH2jgE012S6wpREZlPkwGqs2a 1N6bIAe/vcTDpANUIP5xCs7VtklCOlyZXbKXWq/e/y0Krh5W6pmmdccNyEson0qOAWKk vnE/1PjE9Foh5shl0jdd4AIyVB09c7tViGQvDkDDg/T2RzFRSXJmm5ia0qLKfQl/7lIG ntKFJxf8JAGZU4XQA1EAYcsKov3svxdO7EImYpDSVwGl1nbxrjxVwKegDrGAyKevZ24b Af0rf1OEQf694bQedOwKTBkesqjSdGuQPiO1GpDrdpYXjSSgWEZTBzbTVCB/hIgSFq8W uR3A== X-Gm-Message-State: AOJu0Yz8mm3aRRII+DyyVSHtRk0GskmZuoptGYsFsgD/T7lr46paN5iY 3H+Tzh28kO+guEYwn/Hnnk7pLyf/j3nHXw== X-Google-Smtp-Source: AGHT+IFkgXnin4CVmaceoQG0UKn5UdMSGuy1/gC4FMXOoNcQgvU/eaGx1Qm/guMgT3XO8XeajCOYWg== X-Received: by 2002:a05:6512:b05:b0:500:9026:a290 with SMTP id w5-20020a0565120b0500b005009026a290mr5938686lfu.9.1692875423457; Thu, 24 Aug 2023 04:10:23 -0700 (PDT) Received: from fedora.dev.tbscz ([85.93.123.158]) by smtp.gmail.com with ESMTPSA id l8-20020a056402028800b005232e637c24sm972876edv.84.2023.08.24.04.10.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Aug 2023 04:10:22 -0700 (PDT) From: =?UTF-8?q?Martin=20Za=C5=A5ovi=C4=8D?= To: linux-kernel@vger.kernel.org Cc: robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, conor+dt@kernel.org, gregkh@linuxfoundation.org, linus.walleij@linaro.org, quic_jhugo@quicinc.com, nipun.gupta@amd.com, tzimmermann@suse.de, ogabbay@kernel.org, mathieu.poirier@linaro.org, axboe@kernel.dk, damien.lemoal@opensource.wdc.com, linux@zary.sk, arnd@arndb.de, yangyicong@hisilicon.com, benjamin.tissoires@redhat.com, masahiroy@kernel.org, jacek.lawrynowicz@linux.intel.com, geert+renesas@glider.be, devicetree@vger.kernel.org, andriy.shevchenko@intel.com, =?UTF-8?q?Martin=20Za=C5=A5ovi=C4=8D?= , Rob Herring Subject: [PATCHv5 1/4] dt-bindings: wiegand: add Wiegand controller common properties Date: Thu, 24 Aug 2023 13:10:12 +0200 Message-Id: <20230824111015.57765-2-m.zatovic1@gmail.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230824111015.57765-1-m.zatovic1@gmail.com> References: <20230824111015.57765-1-m.zatovic1@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Wiegand bus is defined by a Wiegand controller node. This node can contain one or more device nodes for devices attached to the controller(it is advised to only connect one device as Wiegand is a point-to-point bus). Wiegand controller needs to specify several attributes such as the pulse length in order to function properly. These attributes are documented here. Acked-by: Linus Walleij Reviewed-by: Rob Herring Signed-off-by: Martin Za=C5=A5ovi=C4=8D --- .../bindings/wiegand/wiegand-controller.yaml | 39 +++++++++++++++++++ MAINTAINERS | 5 +++ 2 files changed, 44 insertions(+) create mode 100644 Documentation/devicetree/bindings/wiegand/wiegand-contr= oller.yaml diff --git a/Documentation/devicetree/bindings/wiegand/wiegand-controller.y= aml b/Documentation/devicetree/bindings/wiegand/wiegand-controller.yaml new file mode 100644 index 000000000000..8f36287e4fed --- /dev/null +++ b/Documentation/devicetree/bindings/wiegand/wiegand-controller.yaml @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/wiegand/wiegand-controller.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Wiegand Generic Controller Common Properties + +maintainers: + - Martin Za=C5=A5ovi=C4=8D + +description: + Wiegand busses can be described with a node for the Wiegand controller d= evice + and a set of child nodes for each SPI slave on the bus. + +properties: + $nodename: + pattern: "^wiegand(@.*|-[0-9a-f])?$" + + pulse-len-us: + description: + Length of the low pulse in microseconds. + + interval-len-us: + description: + Length of a whole bit (both the pulse and the high phase) in microse= conds. + + frame-gap-us: + description: + Length of the last bit of a frame (both the pulse and the high phase= ) in + microseconds. + +required: + - compatible + - pulse-len-us + - interval-len-us + - frame-gap-us + +additionalProperties: true diff --git a/MAINTAINERS b/MAINTAINERS index d590ce31aa72..75462d3746ca 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22922,6 +22922,11 @@ L: linux-input@vger.kernel.org S: Maintained F: drivers/hid/hid-wiimote* =20 +WIEGAND BUS DRIVER +M: Martin Za=C5=A5ovi=C4=8D +S: Maintained +F: Documentation/devicetree/bindings/wiegand/wiegand-controller.yaml + WILOCITY WIL6210 WIRELESS DRIVER L: linux-wireless@vger.kernel.org S: Orphan --=20 2.40.1 From nobody Tue Sep 9 21:32:25 2025 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 D66FFEE49A6 for ; Thu, 24 Aug 2023 11:11:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240966AbjHXLKm (ORCPT ); Thu, 24 Aug 2023 07:10:42 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49202 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240950AbjHXLKc (ORCPT ); Thu, 24 Aug 2023 07:10:32 -0400 Received: from mail-lf1-x131.google.com (mail-lf1-x131.google.com [IPv6:2a00:1450:4864:20::131]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E0FCF10F3; Thu, 24 Aug 2023 04:10:27 -0700 (PDT) Received: by mail-lf1-x131.google.com with SMTP id 2adb3069b0e04-50091b91a83so3628140e87.3; Thu, 24 Aug 2023 04:10:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1692875426; x=1693480226; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=s42+DFrevokm9VL5slrY4KpYoHMGEtCKcBKMFJcHsF8=; b=bZ69Q1bTEFb9HG2ao6kvUwcWSiGT3jimWQ1pHvND7XELw7738tgozh7MrHACAtXGBW mw7DUrO44jisP9b5grh4KH78aP8Nf4f97sQhbQmMW9yI9SyzF4kxAoAk/mVfrkrdYLrJ jMcAj8fqUtWji4EtGiAf7jh/JwOhbUy86oxV7JmSEmMuFGFbS6xOTUFfTAfxPTw+EX7k p3h5YzneDVEt05Wjor+sJ0oGj+hdgz5EbYHQHdmYcXwcVcF/8UgjHClCymdMje7y0S84 0H40rEXv6dpKpiNc9ICMlGVC2dOCkwtTaXjcd3tjrZFL/OdYmGlyp8ZRjWZgdP2VnYoG 9oSQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1692875426; x=1693480226; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=s42+DFrevokm9VL5slrY4KpYoHMGEtCKcBKMFJcHsF8=; b=WApW2HDK7NACf5L+2P0TH9AInyKtBot+SdqtPJm7e2lMgIgQ1mVE0weOit51PJSvZA ns2jeZjWbXy9kaQqw0xVL47VtvKFq/jDaLk4Loy1y36HbyWoHNOXF90o5C0FvHKuDb8U mTIwAi0ysKNYBk0Hnj0VeSGLNv/w8hJoGlmut+ENItWT5pbxncn5MgMDucmcLu18r4vP RQDPaDJ1AD3v9twMEZaLrlbfmmZA1BkMjgj0+6YHI6qio2ff/zelyzssYAQ99fuMxoPn C4tAo9dOxJNJgwPs9KD4LtiBp50O5TLhgp2AZN7ZPlStomkVZKIc59tr867d/4MqFkyE tOqA== X-Gm-Message-State: AOJu0YybjNJA440HfIZSpavBCk2kSixOkLHaUzYLvCcTrP/bja39nIGC uLzVZSkM8vjyZyZUS/8ciTxIE/KB844L6w== X-Google-Smtp-Source: AGHT+IGAcoU3owbnj3ogWUVD22tLIval59j2f0nMSWMw+kFV2pGZTOmGDL+9nB6MRtAIGqYquZs7oA== X-Received: by 2002:a05:6512:2524:b0:4fe:5a4b:911d with SMTP id be36-20020a056512252400b004fe5a4b911dmr11383178lfb.64.1692875425546; Thu, 24 Aug 2023 04:10:25 -0700 (PDT) Received: from fedora.dev.tbscz ([85.93.123.158]) by smtp.gmail.com with ESMTPSA id l8-20020a056402028800b005232e637c24sm972876edv.84.2023.08.24.04.10.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Aug 2023 04:10:24 -0700 (PDT) From: =?UTF-8?q?Martin=20Za=C5=A5ovi=C4=8D?= To: linux-kernel@vger.kernel.org Cc: robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, conor+dt@kernel.org, gregkh@linuxfoundation.org, linus.walleij@linaro.org, quic_jhugo@quicinc.com, nipun.gupta@amd.com, tzimmermann@suse.de, ogabbay@kernel.org, mathieu.poirier@linaro.org, axboe@kernel.dk, damien.lemoal@opensource.wdc.com, linux@zary.sk, arnd@arndb.de, yangyicong@hisilicon.com, benjamin.tissoires@redhat.com, masahiroy@kernel.org, jacek.lawrynowicz@linux.intel.com, geert+renesas@glider.be, devicetree@vger.kernel.org, andriy.shevchenko@intel.com, =?UTF-8?q?Martin=20Za=C5=A5ovi=C4=8D?= Subject: [PATCHv5 2/4] wiegand: add Wiegand bus driver Date: Thu, 24 Aug 2023 13:10:13 +0200 Message-Id: <20230824111015.57765-3-m.zatovic1@gmail.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230824111015.57765-1-m.zatovic1@gmail.com> References: <20230824111015.57765-1-m.zatovic1@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The Wiegand protocol serves as a standardized interface protocol, extensive= ly employed within electronic access control systems, to facilitate data excha= nge between credentials, readers, and door controllers. Its inception can be attributed to the widespread adoption of Wiegand card readers, leveraging t= he Wiegand effect - a physical phenomenon wherein a Wiegand wire (or card) generates small yet distinct magnetic fields. A Wiegand reader detects the magnetic pulses emitted by the internal wires of the card. Three wires are central to the Wiegand protocol: a common ground wire and t= wo distinct data wires designated as DATA0 and DATA1. During periods of inacti= vity, both DATA0 and DATA1 lines remain pulled up. For transmitting a '0,' the DA= TA0 line is pulled down while DATA1 remains pulled up; conversely, transmitting a '1' causes DATA1 to be pulled down while DATA0 remains pulled up. Notably, this protocol ensures that the two lines never simultaneously experience a = low state. Timing characteristics within the Wiegand protocol lack a uniform standardization, introducing variability between devices. Generally, pulse durations hover between 50 to 100 microseconds, while inter-pulse gaps span= 20 to 100 milliseconds. There is no stop bit or similar delimiter to signal the conclusion of a message. Instead, the receiver either counts the bits withi= n the message or enforces a timeout, often set at around ten times the inter-puls= e gap duration. The 26-Bit Wiegand Interface Standard, or 26-Bit Wiegand Format outlines the message format most commonly used among Wiegand devices. This format alloca= tes the initial and terminal bits for parity. The subsequent eight bits followi= ng the initial parity bit are reserved for the Facility Code designed for dist= inct location identification. The remaining bits house the unique ID number. As = the technology evolved, new Wiegand formats emerged, including 36-bit and 37-bit formats. It was also common practice for manufacturers to engineer devices compatible with proprietary Wiegand formats tailored to their specification= s. The Wiegand bus driver handles Wiegand controller and Wiegand device managemement and driver matching. The bus driver defines the structures for Wiegand controllers and Wiegand devices. Wiegand controller structure conta= ins all attributes that define the communication such as the payload_len for configuring the size of a single Wiegand message in bits, thus choosing a format. Each Wiegand controller should be associated with one Wiegand devic= e, as Wiegand is typically a point-to-point bus. Acked-by: Linus Walleij Signed-off-by: Martin Za=C5=A5ovi=C4=8D --- MAINTAINERS | 2 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/wiegand/Kconfig | 25 ++ drivers/wiegand/Makefile | 1 + drivers/wiegand/wiegand.c | 590 ++++++++++++++++++++++++++++++++++++++ include/linux/wiegand.h | 148 ++++++++++ 7 files changed, 769 insertions(+) create mode 100644 drivers/wiegand/Kconfig create mode 100644 drivers/wiegand/Makefile create mode 100644 drivers/wiegand/wiegand.c create mode 100644 include/linux/wiegand.h diff --git a/MAINTAINERS b/MAINTAINERS index 75462d3746ca..0afcc88a38d2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22926,6 +22926,8 @@ WIEGAND BUS DRIVER M: Martin Za=C5=A5ovi=C4=8D S: Maintained F: Documentation/devicetree/bindings/wiegand/wiegand-controller.yaml +F: drivers/wiegand/wiegand.c +F: include/linux/wiegand.h =20 WILOCITY WIL6210 WIRELESS DRIVER L: linux-wireless@vger.kernel.org diff --git a/drivers/Kconfig b/drivers/Kconfig index 514ae6b24cb2..6609dfd6635f 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -145,6 +145,8 @@ source "drivers/vdpa/Kconfig" =20 source "drivers/vhost/Kconfig" =20 +source "drivers/wiegand/Kconfig" + source "drivers/hv/Kconfig" =20 source "drivers/xen/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 7241d80a7b29..898ec50cfcc4 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -150,6 +150,7 @@ obj-$(CONFIG_VHOST_RING) +=3D vhost/ obj-$(CONFIG_VHOST_IOTLB) +=3D vhost/ obj-$(CONFIG_VHOST) +=3D vhost/ obj-$(CONFIG_VLYNQ) +=3D vlynq/ +obj-$(CONFIG_WIEGAND) +=3D wiegand/ obj-$(CONFIG_GREYBUS) +=3D greybus/ obj-$(CONFIG_COMEDI) +=3D comedi/ obj-$(CONFIG_STAGING) +=3D staging/ diff --git a/drivers/wiegand/Kconfig b/drivers/wiegand/Kconfig new file mode 100644 index 000000000000..09ac10217ede --- /dev/null +++ b/drivers/wiegand/Kconfig @@ -0,0 +1,25 @@ +config WIEGAND + tristate "Wiegand Bus driver" + help + The "Wiegand Interface" stands as an asynchronous low-lever protocol + or a wiring standard. Its primary application is within point-to-point + communication scenarios, especially between a credential and a reader + as well as between card reader and a door controller. Wiegand's data + throughput tends to be modest relatively to other protocols and it + depends upon the selected pulse duration and intervals between pulses. + + Despite its relatively advanced age, Wiegand remains extensively + employed in access control systems, particularly for linking card + swipe mechanisms. These mechanisms harness the Wiegand effect as a + means of transferring data from a card to a reader. + + Wiegand uses two wires to transmit the data - DATA0 and DATA1. Both + lines are initially pulled up. When a bit of value 0 is being + transmitted, the DATA0 line is pulled down. Similarly, when a bit of + value 1 is being transmitted, the DATA1 line is pulled down. + + If you want Wiegand support, you should say Y here and also to the + specific controller driver(s) below. + + This Wiegand support can also be built as a module. If so, the module + will be called wiegand. diff --git a/drivers/wiegand/Makefile b/drivers/wiegand/Makefile new file mode 100644 index 000000000000..d17ecb722c6e --- /dev/null +++ b/drivers/wiegand/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_WIEGAND) +=3D wiegand.o diff --git a/drivers/wiegand/wiegand.c b/drivers/wiegand/wiegand.c new file mode 100644 index 000000000000..7cdffdd47ea3 --- /dev/null +++ b/drivers/wiegand/wiegand.c @@ -0,0 +1,590 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WIEGAND_DEFAULT_PULSE_LEN 50 +#define WIEGAND_DEFAULT_INTERVAL_LEN 2000 +#define WIEGAND_DEFAULT_FRAME_GAP 2000 + +struct bus_type wiegand_bus_type; + +/** + * struct wiegand_device - Wiegand listener device + * + * @dev: drivers structure of the device + * @id: unique device id + * @controller: Wiegand controller associated with the device + * @modalias: Name of the driver to use with this device, or its alias. + */ +struct wiegand_device { + struct device dev; + u8 id; + struct wiegand_controller *controller; + char modalias[WIEGAND_NAME_SIZE]; +}; + +static DEFINE_IDA(wiegand_controller_ida); + +static ssize_t wiegand_get_user_data(struct wiegand_controller *ctlr, char= __user const *buf, + size_t len) +{ + int ret; + + ret =3D bitmap_parse_user(buf, len, ctlr->data_bitmap, WIEGAND_MAX_PAYLEN= _BYTES * 8); + if (ret) + return ret; + + return len; +} + +static int wiegand_frelease(struct inode *ino, struct file *filp) +{ + struct wiegand_controller *ctlr =3D filp->private_data; + + mutex_destroy(&ctlr->file_lock); + return 0; +} + +static ssize_t wiegand_fwrite(struct file *filp, char __user const *buf, s= ize_t len, + loff_t *offset) +{ + int ret; + struct wiegand_controller *ctlr =3D filp->private_data; + + mutex_lock(&ctlr->file_lock); + + if (!buf || len =3D=3D 0) + return -EINVAL; + + ret =3D wiegand_get_user_data(ctlr, buf, len); + if (ret < 0) + return ret; + + ctlr->transfer_message(ctlr); + + mutex_unlock(&ctlr->file_lock); + return len; +} + +static int wiegand_fopen(struct inode *ino, struct file *filp) +{ + int ret; + struct wiegand_controller *ctlr =3D container_of(filp->f_op, struct wiega= nd_controller, fops); + + filp->private_data =3D ctlr; + + mutex_lock(&ctlr->file_lock); + + if ((filp->f_flags & O_ACCMODE) =3D=3D O_RDONLY || (filp->f_flags & O_ACC= MODE) =3D=3D O_RDWR) { + dev_err(ctlr->miscdev.this_device, "device is write only\n"); + ret =3D -EIO; + goto out_unlock; + } + + mutex_unlock(&ctlr->file_lock); + return 0; + +out_unlock: + mutex_unlock(&ctlr->file_lock); + return ret; +} + +static inline struct wiegand_device *to_wiegand_device(struct device *dev) +{ + return dev ? container_of(dev, struct wiegand_device, dev) : NULL; +} + +static void wiegand_cleanup(struct wiegand_device *wiegand) +{ + if (wiegand->controller->cleanup) + wiegand->controller->cleanup(wiegand); +} + +/** + * wiegand_controller_release - called after the final refererence decreme= nt + * + * @dev: the controller device + */ +static void wiegand_controller_release(struct device *dev) +{ + struct wiegand_controller *ctlr; + + ctlr =3D container_of(dev, struct wiegand_controller, dev); + kfree(ctlr); +} + +/** + * wiegand_alloc_controller - allocate a new Wiegand controller + * + * @dev: the controller device + * @size: size of the private data to be allocated for the caller + * @secondary: true if the controller is a secondary controller(reads data) + * + * This function is only by Wiegand controller drivers to allocate a new W= iegand controller + * structure before registering it using wiegand_register_controller(). + */ +struct wiegand_controller *wiegand_alloc_controller(struct device *dev, si= ze_t size, + bool secondary) +{ + struct wiegand_controller *ctlr; + size_t ctlr_size =3D ALIGN(sizeof(*ctlr), dma_get_cache_alignment()); + size_t total_size; + + if (check_add_overflow(size, ctlr_size, &total_size)) + return NULL; + + ctlr =3D kzalloc(total_size, GFP_KERNEL); + if (!ctlr) + return NULL; + + device_initialize(&ctlr->dev); + + ctlr->bus_num =3D -1; + ctlr->secondary =3D secondary; + ctlr->dev.parent =3D dev; + ctlr->dev.release =3D wiegand_controller_release; + + wiegand_controller_set_devdata(ctlr, (void *)ctlr + ctlr_size); + + return ctlr; +} +EXPORT_SYMBOL_NS_GPL(wiegand_alloc_controller, WIEGAND); + +/** + * devm_wiegand_alloc_controller - device managed allocation of a new Wieg= and controller + * @dev: physical device of Wiegand controller + * @size: size of the private data to be allocated for the caller + * @secondary: true if the controller is a secondary controller(reads data) + * + * Device managed version of wiegand_alloc_controller(). The Wiegand contr= oller is automatically + * freed on driver detach. + */ +struct wiegand_controller *devm_wiegand_alloc_controller(struct device *de= v, unsigned int size, + bool secondary) +{ + struct wiegand_controller *ctlr; + + ctlr =3D wiegand_alloc_controller(dev, size, secondary); + if (ctlr) + ctlr->devm_allocated =3D true; + else + return NULL; + + if (devm_add_action_or_reset(dev, wiegand_controller_put, ctlr)) + return NULL; + + return ctlr; +} +EXPORT_SYMBOL_NS_GPL(devm_wiegand_alloc_controller, WIEGAND); + +static int wiegand_controller_check_ops(struct wiegand_controller *ctlr) +{ + if (!ctlr->transfer_message) + return -EINVAL; + return 0; +} + +/** + * register_wiegand_device - allocates and registers a new Wiegand device + * + * @ctlr: controller structure to attach device to + * @node: firmware node for the device + */ +static struct wiegand_device *register_wiegand_device(struct wiegand_contr= oller *ctlr, + struct fwnode_handle *node) +{ + struct wiegand_device *wiegand; + int ret; + + wiegand =3D wiegand_alloc_device(ctlr); + if (!wiegand) + return NULL; + + fwnode_handle_get(node); + device_set_node(&wiegand->dev, node); + + ret =3D wiegand_add_device(wiegand); + if (ret) { + dev_err(&ctlr->dev, "wiegand_device register error %pfwf\n", node); + goto out_node_put; + } + + /* check if more devices are connected to the bus */ + if (ctlr->device_count > 1) + dev_warn(&ctlr->dev, "Wiegand is a point-to-point bus, it is advised to = only connect one device per Wiegand bus. The devices may not communicate us= ing the same pulse length, format or else.\n"); + + return wiegand; + +out_node_put: + fwnode_handle_put(node); + put_device(&wiegand->dev); + wiegand_cleanup(wiegand); + return ERR_PTR(ret); +} + +static void register_wiegand_devices(struct wiegand_controller *ctlr) +{ + struct wiegand_device *wiegand; + struct fwnode_handle *node; + + fwnode_for_each_available_child_node(ctlr->dev.fwnode, node) { + wiegand =3D register_wiegand_device(ctlr, node); + if (IS_ERR(wiegand)) + dev_warn(&ctlr->dev, "failed to create wiegand device for %pfwf\n", nod= e); + } +} + +/** + * wiegand_controller_get_timings - gets timings using a firmware node + * + * @ctlr: pointer to a wiegand_controller structure + * + * Tries to get timing data from a firmware node, if they are not defined,= then default values are + * set. + */ +static void wiegand_controller_get_timings(struct wiegand_controller *ctlr) +{ + int ret; + u32 pulse_len, interval_len, frame_gap; + struct wiegand_timing *timing =3D &ctlr->timing; + struct device *ctlr_dev =3D &ctlr->dev; + + ret =3D device_property_read_u32(ctlr_dev, "pulse-len-us", &pulse_len); + if (!ret && pulse_len > 0) + timing->pulse_len =3D pulse_len; + else + timing->pulse_len =3D WIEGAND_DEFAULT_PULSE_LEN; + + ret =3D device_property_read_u32(ctlr_dev, "interval-len-us", &interval_l= en); + if (!ret && interval_len > 0) + timing->interval_len =3D interval_len; + else + timing->interval_len =3D WIEGAND_DEFAULT_INTERVAL_LEN; + + ret =3D device_property_read_u32(ctlr_dev, "frame-gap-us", &frame_gap); + if (!ret && frame_gap > 0) + timing->frame_gap =3D frame_gap; + else + timing->frame_gap =3D WIEGAND_DEFAULT_FRAME_GAP; +} + +static int __unregister(struct device *dev, void *null) +{ + wiegand_unregister_device(to_wiegand_device(dev)); + return 0; +} + +/** + * wiegand_unregister_controller - unregisters controller structure within= Wiegand bus + * + * @ptr: pointer to a wiegand_controller structure + * + * Frees all resources allocated by the wiegand_register_controller() func= tion. + * If the controller was registered using devm_wiegand_alloc_controller() = then + * this function is called automatically on driver detach.Otherwise the fu= nction needs + * to be called manually. If controller is not devm managed, then the refe= rence to the + * controller structure is put. + */ +void wiegand_unregister_controller(void *ptr) +{ + struct wiegand_controller *ctlr =3D ptr; + unsigned int id =3D ctlr->bus_num; + + device_for_each_child(&ctlr->dev, NULL, __unregister); + ida_free(&wiegand_controller_ida, id); + device_del(&ctlr->dev); + + kfree(ctlr->miscdev.name); + misc_deregister(&ctlr->miscdev); + + if (!ctlr->devm_allocated) + put_device(&ctlr->dev); +} +EXPORT_SYMBOL_NS_GPL(wiegand_unregister_controller, WIEGAND); + +/** + * wiegand_register_controller - registers controller structure within bus + * @ctlr: controller structure to register + * + * Function checks that the message transfer functions is defined for pass= ed controller structure, + * gets its attributes and finally adds the controller device and register= s the controller on the + * bus. + */ +int wiegand_register_controller(struct wiegand_controller *ctlr) +{ + int status, id; + + status =3D wiegand_controller_check_ops(ctlr); + if (status) + return status; + + id =3D ida_alloc(&wiegand_controller_ida, GFP_KERNEL); + if (id < 0) + return id; + ctlr->bus_num =3D id; + + wiegand_controller_get_timings(ctlr); + + status =3D dev_set_name(&ctlr->dev, "wiegand%u", ctlr->bus_num); + if (status < 0) + goto out_free_ida; + + ctlr->miscdev.name =3D kasprintf(GFP_KERNEL, "wiegand1"); + if (!ctlr->miscdev.name) { + status =3D -ENOMEM; + goto out_free_ida; + } + + ctlr->fops.owner =3D THIS_MODULE; + ctlr->fops.open =3D wiegand_fopen; + ctlr->fops.release =3D wiegand_frelease; + ctlr->fops.write =3D wiegand_fwrite; + ctlr->miscdev.fops =3D &ctlr->fops; + ctlr->miscdev.minor =3D MISC_DYNAMIC_MINOR; + + status =3D misc_register(&ctlr->miscdev); + if (status) { + dev_err(&ctlr->dev, "couldn't register misc device\n"); + goto out_free_ida_name; + } + + mutex_init(&ctlr->file_lock); + + status =3D device_add(&ctlr->dev); + if (status < 0) + goto out_free_ida_name_misc; + + ctlr->device_count =3D 0; + ctlr->miscdev.parent =3D &ctlr->dev; + register_wiegand_devices(ctlr); + + return status; + +out_free_ida_name_misc: + misc_deregister(&ctlr->miscdev); + +out_free_ida_name: + kfree(ctlr->miscdev.name); + +out_free_ida: + ida_free(&wiegand_controller_ida, ctlr->bus_num); + return status; +} +EXPORT_SYMBOL_NS_GPL(wiegand_register_controller, WIEGAND); + +int devm_wiegand_register_controller(struct device *dev, struct wiegand_co= ntroller *ctlr) +{ + int ret; + + ret =3D wiegand_register_controller(ctlr); + if (ret < 0) + return ret; + + return devm_add_action_or_reset(dev, wiegand_unregister_controller, ctlr); +} +EXPORT_SYMBOL_NS_GPL(devm_wiegand_register_controller, WIEGAND); + +struct wiegand_controller *wiegand_device_get_controller(struct wiegand_de= vice *dev) +{ + return dev->controller; +} +EXPORT_SYMBOL_NS_GPL(wiegand_device_get_controller, WIEGAND); + +/** + * wiegand_dev_release - called after the final reference count decrement + * @dev: device to release + */ +static void wiegand_dev_release(struct device *dev) +{ + struct wiegand_device *wiegand =3D to_wiegand_device(dev); + + wiegand_controller_put(wiegand->controller); + kfree(wiegand); +} + +struct wiegand_device *wiegand_alloc_device(struct wiegand_controller *ctl= r) +{ + struct wiegand_device *wiegand; + + wiegand =3D kzalloc(sizeof(*wiegand), GFP_KERNEL); + if (!wiegand) { + wiegand_controller_put(ctlr); + return NULL; + } + + wiegand->controller =3D ctlr; + wiegand->dev.parent =3D &ctlr->dev; + wiegand->dev.bus =3D &wiegand_bus_type; + wiegand->dev.release =3D wiegand_dev_release; + + device_initialize(&wiegand->dev); + return wiegand; +} +EXPORT_SYMBOL_NS_GPL(wiegand_alloc_device, WIEGAND); + +static int wiegand_dev_set_name(struct wiegand_device *wiegand, u8 id) +{ + return dev_set_name(&wiegand->dev, "%s.%u", dev_name(&wiegand->controller= ->dev), id); +} + +static int __wiegand_add_device(struct wiegand_device *wiegand) +{ + struct wiegand_controller *ctlr =3D wiegand->controller; + struct device *dev =3D ctlr->dev.parent; + int status; + + status =3D wiegand_setup(wiegand); + if (status < 0) { + dev_err(dev, "can't setup %s, status %d\n", + dev_name(&wiegand->dev), status); + return status; + } + + status =3D device_add(&wiegand->dev); + if (status < 0) { + dev_err(dev, "can't add %s, status %d\n", dev_name(&wiegand->dev), statu= s); + wiegand_cleanup(wiegand); + } else { + dev_dbg(dev, "registered child %s\n", dev_name(&wiegand->dev)); + } + + return status; +} + +int wiegand_add_device(struct wiegand_device *wiegand) +{ + struct wiegand_controller *ctlr =3D wiegand->controller; + int status; + + status =3D wiegand_dev_set_name(wiegand, ctlr->device_count); + if (status) + return status; + + status =3D __wiegand_add_device(wiegand); + if (!status) { + ctlr->device_count++; + wiegand->id =3D wiegand->controller->device_count; + } + + return status; +} + +int wiegand_setup(struct wiegand_device *wiegand) +{ + int status; + struct device *ctlr_dev =3D &wiegand->controller->dev; + struct wiegand_controller *ctlr =3D wiegand->controller; + + if (ctlr->setup) { + status =3D ctlr->setup(wiegand); + if (status) { + dev_err(ctlr_dev, "failed to setup device: %d\n", status); + return status; + } + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(wiegand_setup, WIEGAND); + +void wiegand_unregister_device(struct wiegand_device *wiegand) +{ + if (!wiegand) + return; + + fwnode_handle_put(wiegand->dev.fwnode); + device_del(&wiegand->dev); + wiegand_cleanup(wiegand); + put_device(&wiegand->dev); +} +EXPORT_SYMBOL_NS_GPL(wiegand_unregister_device, WIEGAND); + +int wiegand_send_message(struct wiegand_device *wiegand, unsigned long *ms= g_bmp, u8 bitlen) +{ + struct wiegand_primary *primary =3D wiegand->controller; + + if (msg_bmp =3D=3D NULL) + return -EINVAL; + + if (primary->transfer_message) + primary->transfer_message(primary); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(wiegand_send_message, WIEGAND); + +static int wiegand_match_device(struct device *dev, struct device_driver *= drv) +{ + struct wiegand_device *wiegand_dev =3D to_wiegand_device(dev); + + if (of_driver_match_device(dev, drv)) + return 1; + + return strcmp(wiegand_dev->modalias, drv->name) =3D=3D 0; +} + +static int wiegand_probe(struct device *dev) +{ + struct wiegand_device *wiegand =3D to_wiegand_device(dev); + const struct wiegand_driver *wdrv =3D to_wiegand_driver(dev->driver); + + return wdrv->probe(wiegand); +} + +static void wiegand_remove(struct device *dev) +{ + const struct wiegand_driver *wdrv =3D to_wiegand_driver(dev->driver); + + if (wdrv->remove) + wdrv->remove(to_wiegand_device(dev)); +} + +struct bus_type wiegand_bus_type =3D { + .name =3D "wiegand", + .match =3D wiegand_match_device, + .probe =3D wiegand_probe, + .remove =3D wiegand_remove, +}; +EXPORT_SYMBOL_NS_GPL(wiegand_bus_type, WIEGAND); + +int __wiegand_register_driver(struct module *owner, struct wiegand_driver = *wdrv) +{ + wdrv->driver.owner =3D owner; + wdrv->driver.bus =3D &wiegand_bus_type; + + return driver_register(&wdrv->driver); +} +EXPORT_SYMBOL_NS_GPL(__wiegand_register_driver, WIEGAND); + +static int __init wiegand_init(void) +{ + int ret; + + ret =3D bus_register(&wiegand_bus_type); + if (ret < 0) + pr_err("Wiegand bus registration failed: %d\n", ret); + + return ret; +} +postcore_initcall_sync(wiegand_init); + +static void __exit wiegand_exit(void) +{ + ida_destroy(&wiegand_controller_ida); + bus_unregister(&wiegand_bus_type); +} +module_exit(wiegand_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Wiegand bus driver"); +MODULE_AUTHOR("Martin Za=C5=A5ovi=C4=8D "); diff --git a/include/linux/wiegand.h b/include/linux/wiegand.h new file mode 100644 index 000000000000..52d49f4efdd4 --- /dev/null +++ b/include/linux/wiegand.h @@ -0,0 +1,148 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef H_LINUX_INCLUDE_LINUX_WIEGAND_H +#define H_LINUX_INCLUDE_LINUX_WIEGAND_H + +#include +#include +#include +#include +#include + +#define WIEGAND_NAME_SIZE 32 +#define WIEGAND_MAX_PAYLEN_BYTES 256 + +extern struct bus_type wiegand_type; + +struct wiegand_device; +/** + * struct wiegand_timing - Wiegand timings in microseconds + * @pulse_len: length of the low pulse + * @interval_len: length of a whole bit (both the pulse and the high phase) + * @frame_gap: length of the last bit of a frame (both the pulse and the h= igh phase) + */ +struct wiegand_timing { + u32 pulse_len; + u32 interval_len; + u32 frame_gap; +}; + +/** + * struct wiegand_controller - Wiegand primary or secondary interface + * @dev: Device interface of the controller + * @miscdev: Misc device associated with controller + * @fops: File operations + * @file_lock: File mutex + * @bus_num: Board-specific identifier for Wiegand controller + * @secondary: Whether the controller is a secondary(receives data) + * @devm_allocated: Whether the allocation of this struct is devres-managed + * @timing: Stores timing parameters for Wiegand communication + * @payload_len: Number of bits in a Wiegand message + * @device_count: Counter of devices connected to the same Wiegand bus(con= troller) + * @data_bitmap: Data buffer + * @transfer_message: Send a message on the bus + * @setup: Setup a device + * @cleanup: Cleanup after a device + */ +struct wiegand_controller { + struct device dev; + struct miscdevice miscdev; + + struct file_operations fops; + struct mutex file_lock; + + unsigned int bus_num; + + bool secondary; + bool devm_allocated; + + struct wiegand_timing timing; + u32 payload_len; + + u8 device_count; + + DECLARE_BITMAP(data_bitmap, WIEGAND_MAX_PAYLEN_BYTES * 8); + + int (*transfer_message)(struct wiegand_controller *ctlr); + int (*setup)(struct wiegand_device *wiegand); + void (*cleanup)(struct wiegand_device *wiegand); +}; + +struct wiegand_driver { + struct device_driver driver; + const struct wiegand_device_id *id_table; + int (*probe)(struct wiegand_device *wiegand); + void (*remove)(struct wiegand_device *wiegand); +}; + +#define wiegand_primary wiegand_controller +struct wiegand_controller *wiegand_alloc_controller(struct device *host, s= ize_t size, + bool secondary); + +struct wiegand_controller *devm_wiegand_alloc_controller(struct device *de= v, + unsigned int size, bool secondary); +static inline struct wiegand_controller *devm_wiegand_alloc_primary(struct= device *dev, + unsigned int size) +{ + return devm_wiegand_alloc_controller(dev, size, false); +} + +int wiegand_register_controller(struct wiegand_controller *ctlr); +int devm_wiegand_register_controller(struct device *dev, struct wiegand_co= ntroller *ctlr); +#define wiegand_register_primary(_ctlr) wiegand_register_controller(_ctlr) +#define devm_wiegand_register_primary(_dev, _ctlr)devm_wiegand_register_co= ntroller(_dev, _ctlr) +void wiegand_unregister_controller(void *ctlr); +#define wiegand_unregister_primary(_ctlr) wiegand_unregister_controller(_c= tlr) +struct wiegand_primary *wiegand_busnum_to_primary(u16 bus_num); + +static inline void *wiegand_controller_get_devdata(struct wiegand_controll= er *ctlr) +{ + return dev_get_drvdata(&ctlr->dev); +} + +static inline void wiegand_controller_set_devdata(struct wiegand_controlle= r *ctlr, void *data) +{ + dev_set_drvdata(&ctlr->dev, data); +} + +#define wiegand_primary_get_devdata(_ctlr) wiegand_controller_get_devdata(= _ctlr) +#define wiegand_primary_set_devdata(_ctlr, data) wiegand_controller_set_de= vdata(_ctlr, data) + +static inline struct wiegand_controller *wiegand_controller_get(struct wie= gand_controller *ctlr) +{ + if (!ctlr || !get_device(&ctlr->dev)) + return NULL; + return ctlr; +} + +static inline void wiegand_controller_put(void *ptr) +{ + struct wiegand_controller *ctlr =3D ptr; + + if (ctlr) + put_device(&ctlr->dev); +} + +struct wiegand_device *wiegand_alloc_device(struct wiegand_controller *ctl= r); +int wiegand_add_device(struct wiegand_device *wiegand); +int wiegand_setup(struct wiegand_device *wiegand); +void wiegand_unregister_device(struct wiegand_device *wiegand); +struct wiegand_controller *wiegand_device_get_controller(struct wiegand_de= vice *dev); + +int wiegand_send_message(struct wiegand_device *wiegand, unsigned long *ms= g_bmp, u8 bitlen); + +int __wiegand_register_driver(struct module *owner, struct wiegand_driver = *wdrv); +#define wiegand_register_driver(driver) __wiegand_register_driver(THIS_MOD= ULE, driver) + +static inline void wiegand_unregister_driver(struct wiegand_driver *wdrv) +{ + if (wdrv) + driver_unregister(&wdrv->driver); +} + +static inline struct wiegand_driver *to_wiegand_driver(struct device_drive= r *drv) +{ + return drv ? container_of(drv, struct wiegand_driver, driver) : NULL; +} + +#endif /* H_LINUX_INCLUDE_LINUX_WIEGAND_H */ --=20 2.40.1 From nobody Tue Sep 9 21:32:25 2025 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 7BB0DC3DA6F for ; Thu, 24 Aug 2023 11:11:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240953AbjHXLKk (ORCPT ); Thu, 24 Aug 2023 07:10:40 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49220 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240951AbjHXLKc (ORCPT ); Thu, 24 Aug 2023 07:10:32 -0400 Received: from mail-lf1-x12b.google.com (mail-lf1-x12b.google.com [IPv6:2a00:1450:4864:20::12b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7FA86170C; Thu, 24 Aug 2023 04:10:29 -0700 (PDT) Received: by mail-lf1-x12b.google.com with SMTP id 2adb3069b0e04-500a398cda5so90396e87.0; Thu, 24 Aug 2023 04:10:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1692875428; x=1693480228; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=31bCEXmDXAJQ0gY++ElNzOZWxHPc79yuZ9Uvwsb2/Ew=; b=MZ/e9Hi8fWa745BDD29FtA/l8cF+6yUJcOLOrO1U/yNmfUWAspS0Ky7R09NYcSPphK hqzLtLRg7RqhuwOjRtk9VBcOJce3POH79UrAQ+ykjx7AZGYQnn/2cPrvctwERcdH86ww icvFvN4GB06yD1OuUkKvkOaqoMWta/rZI0pV0MWyv4wJtKWANfVeuQ84Nqu/QViZ/1h+ wGlYAMxdOC6Z0qlRjSS88CNJOF/C0cMHNtA7N3MK6aFM/33t52OuDqG7wx0iiGigxkHI KlgQqrvrwjF0tM2IM+ZPEV+XFNxlD9dxAcD+GFsf1ZOrVlhM4vPfEzSu+KShbnRffY+f Nk+Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1692875428; x=1693480228; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=31bCEXmDXAJQ0gY++ElNzOZWxHPc79yuZ9Uvwsb2/Ew=; b=G3Jff2bluSuhE9COF5S0/txd1A1JJuuEfAXEgucVzj+4x/8eR3w0P8D9wSlTP7cUYp rbfqAkH6p9gFbvPJRDG2JbD1wJ8xQiMiXn7NvqeeJrpMOIn9xAmmwsfdgI9Qn9Ege3hT tjk2r8dGqQlmf/v0AE7Nn42Dj5zf0SsXTkBOUk35hAACVLzddWJa0T14IZ9oTf8G+l6C 7yystLdych3pMPXudpSKsiHVW5nBpfmvBK+5rRCbU7ldbKlYDr8mWhLbbQSJxtfJXC3/ agUOxtnknK7bq01YPBytIt/vVJv0wFBuqOPL5j6W8zljtUb3oq5S+mWQqtOp4FGfTL+I m1mg== X-Gm-Message-State: AOJu0YwNOPvkLqkSUCkn4vY0xCM5bK2SYKahgpckyRwmudgznrk9HKee ZufGXB3A6gDmVyXc0SQ3muZR+iF9fCKfPA== X-Google-Smtp-Source: AGHT+IGbyantVPiUOpRerZwnnJOgXoZjRWfuNk+iLwnCifvQpJ/NCiOTeiEOIFBjhSRk77RYXAqapA== X-Received: by 2002:ac2:4db9:0:b0:500:8723:e457 with SMTP id h25-20020ac24db9000000b005008723e457mr6194863lfe.30.1692875427677; Thu, 24 Aug 2023 04:10:27 -0700 (PDT) Received: from fedora.dev.tbscz ([85.93.123.158]) by smtp.gmail.com with ESMTPSA id l8-20020a056402028800b005232e637c24sm972876edv.84.2023.08.24.04.10.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Aug 2023 04:10:27 -0700 (PDT) From: =?UTF-8?q?Martin=20Za=C5=A5ovi=C4=8D?= To: linux-kernel@vger.kernel.org Cc: robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, conor+dt@kernel.org, gregkh@linuxfoundation.org, linus.walleij@linaro.org, quic_jhugo@quicinc.com, nipun.gupta@amd.com, tzimmermann@suse.de, ogabbay@kernel.org, mathieu.poirier@linaro.org, axboe@kernel.dk, damien.lemoal@opensource.wdc.com, linux@zary.sk, arnd@arndb.de, yangyicong@hisilicon.com, benjamin.tissoires@redhat.com, masahiroy@kernel.org, jacek.lawrynowicz@linux.intel.com, geert+renesas@glider.be, devicetree@vger.kernel.org, andriy.shevchenko@intel.com, =?UTF-8?q?Martin=20Za=C5=A5ovi=C4=8D?= Subject: [PATCHv5 3/4] dt-bindings: wiegand: add GPIO bitbanged Wiegand controller Date: Thu, 24 Aug 2023 13:10:14 +0200 Message-Id: <20230824111015.57765-4-m.zatovic1@gmail.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230824111015.57765-1-m.zatovic1@gmail.com> References: <20230824111015.57765-1-m.zatovic1@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org GPIO bitbanged Wiegand controller requires definitions of GPIO lines to be used on top of the common Wiegand properties. Wiegand utilizes two such lines - DATA0(low data line) and DATA1(high data line). Acked-by: Linus Walleij Signed-off-by: Martin Za=C5=A5ovi=C4=8D --- .../bindings/wiegand/wiegand-gpio.yaml | 46 +++++++++++++++++++ MAINTAINERS | 5 ++ 2 files changed, 51 insertions(+) create mode 100644 Documentation/devicetree/bindings/wiegand/wiegand-gpio.= yaml diff --git a/Documentation/devicetree/bindings/wiegand/wiegand-gpio.yaml b/= Documentation/devicetree/bindings/wiegand/wiegand-gpio.yaml new file mode 100644 index 000000000000..cf2cb938de02 --- /dev/null +++ b/Documentation/devicetree/bindings/wiegand/wiegand-gpio.yaml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/wiegand/wiegand-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: GPIO bitbanged Wiegand interface properties + +maintainers: + - Martin Za=C5=A5ovi=C4=8D + +description: + This represents the GPIO lines used for bit-banged Wiegand on dedicated = GPIO + lines. + +allOf: + - $ref: /schemas/wiegand/wiegand-controller.yaml# + +properties: + compatible: + const: wiegand-gpio + + data-gpios: + description: GPIOs used as Wiegand data lines, DATA0 and DATA1 respect= ivelly. + maxItems: 2 + +required: + - compatible + - data-gpios + +unevaluatedProperties: false + +examples: + - | + #include + + wiegand@f00 { + compatible =3D "wiegand-gpio"; + pulse-len-us =3D <50>; + interval-len-us =3D <2000>; + frame-gap-us =3D <2000>; + data-gpios =3D <&gpio2 6 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>, + <&gpio2 7 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; + + /* devices */ + }; diff --git a/MAINTAINERS b/MAINTAINERS index 0afcc88a38d2..fb158e74ca4b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22929,6 +22929,11 @@ F: Documentation/devicetree/bindings/wiegand/wiega= nd-controller.yaml F: drivers/wiegand/wiegand.c F: include/linux/wiegand.h =20 +WIEGAND GPIO BITBANG DRIVER +M: Martin Za=C5=A5ovi=C4=8D +S: Maintained +F: Documentation/devicetree/bindings/wiegand/wiegand-gpio.yaml + WILOCITY WIL6210 WIRELESS DRIVER L: linux-wireless@vger.kernel.org S: Orphan --=20 2.40.1 From nobody Tue Sep 9 21:32:25 2025 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 17F5AEE49B2 for ; Thu, 24 Aug 2023 11:11:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240978AbjHXLKo (ORCPT ); Thu, 24 Aug 2023 07:10:44 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49306 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240959AbjHXLKf (ORCPT ); Thu, 24 Aug 2023 07:10:35 -0400 Received: from mail-lf1-x130.google.com (mail-lf1-x130.google.com [IPv6:2a00:1450:4864:20::130]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9F51A10FA; Thu, 24 Aug 2023 04:10:32 -0700 (PDT) Received: by mail-lf1-x130.google.com with SMTP id 2adb3069b0e04-4fe2d152f62so10474887e87.0; Thu, 24 Aug 2023 04:10:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1692875431; x=1693480231; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=fzTn37NhbD/APGPKPSueAFzFYve8TPijkZqKUT+Scqc=; b=gf8WV3zaeFI5FGp20fEVwAKmNRV4ZIV3Vd5dQpRtrctM9MGVJJpbHJ1gGEoyqmyIhK YPJ0r4eRQOxaTzR6vRkBebSaEQaC2EstipahIgYU5V1q78keL8mrRSo/ZVOYFh7LecwP 5hbTfvuHOkD3aYlj5ovBds99hgMcY9mMKWoZGmUnw1/AjyvbZT1TTRHrtODHwlaknqdB JUvzXmr0SRzGDVNmDSgPdVkpOnNQa541bRQ8FDlEoppxa/jilJXtlCZdG8qIijG/+/rv 5KbDwR2ep9hSfuqmQH0Cn88pJr1mVDk0loc//vggSihdstBzqUrppETH8F6qkRs2VIZi ClWw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1692875431; x=1693480231; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=fzTn37NhbD/APGPKPSueAFzFYve8TPijkZqKUT+Scqc=; b=eTxn3eMes9kOMfhWO4Fs16M24dXPWjc8QXrpBIvpSDApaWynBvISFFo9cjnAm4Zgf2 nsAz1PjA8qcPuHUQdRhWSAhF01+wlFGjeuEyVXBKHsiKuR59HgcYHuEc1aKc5booYv3t 2AUDLea8UHvQTP1W09mTJ9T1oyEvSts1l29YHYmJ9XjoXBG6Nr8oeueb5qdpAaDcPY5g v2FlBkdBdhkI6CJKg297C4CiWNNiX0i2IDY9pS3VJvAvSGnYNYl3UuMAxLxiLmxQOfJy BQrOis357woSxlVeppVEQCaJmEcPW1AcaouF1dY7WvNnGaNtXXumVs8wFUFQ2W7hTaEY aW9g== X-Gm-Message-State: AOJu0Yxgh1u3RI2G2qczKUPFc4VdyI/K/CN3usgZqpbiUzVw3gN4Uk0d ZqR4ALlouDCfzcsOrTibbESZ0STKPwCxjg== X-Google-Smtp-Source: AGHT+IErmwwg+0aSYpkqgZXk+jMOBmAeeR0WHAVP8ASzCMlqgS72erkEW2vhcLVs4nU4ZXV15JSrWA== X-Received: by 2002:a05:6512:ad3:b0:4f7:6966:36fb with SMTP id n19-20020a0565120ad300b004f7696636fbmr12263084lfu.12.1692875430726; Thu, 24 Aug 2023 04:10:30 -0700 (PDT) Received: from fedora.dev.tbscz ([85.93.123.158]) by smtp.gmail.com with ESMTPSA id l8-20020a056402028800b005232e637c24sm972876edv.84.2023.08.24.04.10.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Aug 2023 04:10:30 -0700 (PDT) From: =?UTF-8?q?Martin=20Za=C5=A5ovi=C4=8D?= To: linux-kernel@vger.kernel.org Cc: robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, conor+dt@kernel.org, gregkh@linuxfoundation.org, linus.walleij@linaro.org, quic_jhugo@quicinc.com, nipun.gupta@amd.com, tzimmermann@suse.de, ogabbay@kernel.org, mathieu.poirier@linaro.org, axboe@kernel.dk, damien.lemoal@opensource.wdc.com, linux@zary.sk, arnd@arndb.de, yangyicong@hisilicon.com, benjamin.tissoires@redhat.com, masahiroy@kernel.org, jacek.lawrynowicz@linux.intel.com, geert+renesas@glider.be, devicetree@vger.kernel.org, andriy.shevchenko@intel.com, =?UTF-8?q?Martin=20Za=C5=A5ovi=C4=8D?= Subject: [PATCHv5 4/4] wiegand: add Wiegand GPIO bitbanged controller driver Date: Thu, 24 Aug 2023 13:10:15 +0200 Message-Id: <20230824111015.57765-5-m.zatovic1@gmail.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230824111015.57765-1-m.zatovic1@gmail.com> References: <20230824111015.57765-1-m.zatovic1@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This controller formats the data to a Wiegand format and bit-bangs the message on devicetree defined GPIO lines. The driver creates a dev file for writing messages on the bus. It also creates a sysfs file to control the payload length of messages(in bits). If a message is shorter than the set payload length, it will be discarded. On the other hand, if a message is longer, the additional bits will be stripped off. Acked-by: Linus Walleij Signed-off-by: Martin Za=C5=A5ovi=C4=8D --- .../ABI/testing/sysfs-driver-wiegand-gpio | 9 + MAINTAINERS | 2 + drivers/wiegand/Kconfig | 11 + drivers/wiegand/Makefile | 1 + drivers/wiegand/wiegand-gpio.c | 192 ++++++++++++++++++ 5 files changed, 215 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-driver-wiegand-gpio create mode 100644 drivers/wiegand/wiegand-gpio.c diff --git a/Documentation/ABI/testing/sysfs-driver-wiegand-gpio b/Document= ation/ABI/testing/sysfs-driver-wiegand-gpio new file mode 100644 index 000000000000..6bf6c2b93bf7 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-wiegand-gpio @@ -0,0 +1,9 @@ +What: /sys/devices/platform/.../wiegand-gpio-attributes/payload_len +Date: August 2023 +Contact: Martin Za=C5=A5ovi=C4=8D +Description: + Read/set the payload length of messages sent by Wiegand GPIO + bit-banging controller in bits. The default value is 26, as + that is the most widely-used message length of Wiegand messages. + Controller will only send messages of at least the set length + and it will strip off bits of longer messages. diff --git a/MAINTAINERS b/MAINTAINERS index fb158e74ca4b..221ffa766ed0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22932,7 +22932,9 @@ F: include/linux/wiegand.h WIEGAND GPIO BITBANG DRIVER M: Martin Za=C5=A5ovi=C4=8D S: Maintained +F: Documentation/ABI/testing/sysfs-driver-wiegand-gpio F: Documentation/devicetree/bindings/wiegand/wiegand-gpio.yaml +F: drivers/wiegand/wiegand-gpio.c =20 WILOCITY WIL6210 WIRELESS DRIVER L: linux-wireless@vger.kernel.org diff --git a/drivers/wiegand/Kconfig b/drivers/wiegand/Kconfig index 09ac10217ede..17fd367a9004 100644 --- a/drivers/wiegand/Kconfig +++ b/drivers/wiegand/Kconfig @@ -23,3 +23,14 @@ config WIEGAND =20 This Wiegand support can also be built as a module. If so, the module will be called wiegand. + +config WIEGAND_GPIO + tristate "GPIO-based wiegand master (write only)" + depends on WIEGAND + help + This GPIO bitbanging Wiegand controller uses the libgpiod library to + utilize GPIO lines for sending Wiegand data. Userspace may access the + Wiegand GPIO interface via a dev file. + + This support is also available as a module. If so, the module will be + called wiegand-gpio. diff --git a/drivers/wiegand/Makefile b/drivers/wiegand/Makefile index d17ecb722c6e..ddf697e21088 100644 --- a/drivers/wiegand/Makefile +++ b/drivers/wiegand/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_WIEGAND) +=3D wiegand.o +obj-$(CONFIG_WIEGAND_GPIO) +=3D wiegand-gpio.o diff --git a/drivers/wiegand/wiegand-gpio.c b/drivers/wiegand/wiegand-gpio.c new file mode 100644 index 000000000000..448bfefaa7d9 --- /dev/null +++ b/drivers/wiegand/wiegand-gpio.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include + +#define WIEGAND_DEFAULT_PAYLOAD_LEN 26 + +struct wiegand_gpio { + struct device *dev; + struct wiegand_controller *ctlr; + struct gpio_descs *gpios; +}; + +static ssize_t store_ulong(u32 *val, const char *buf, size_t size, unsigne= d long max) +{ + int rc; + u32 new; + + rc =3D kstrtou32(buf, 0, &new); + if (rc) + return rc; + + if (new > max) + return -ERANGE; + + *val =3D new; + return size; +} + +/* + * Attribute file for setting payload length of Wiegand messages. + */ +static ssize_t payload_len_show(struct device *dev, struct device_attribut= e *attr, char *buf) +{ + struct wiegand_gpio *wiegand_gpio =3D dev_get_drvdata(dev); + struct wiegand_controller *ctlr =3D wiegand_gpio->ctlr; + + return sysfs_emit(buf, "%u\n", ctlr->payload_len); +} + +static ssize_t payload_len_store(struct device *dev, struct device_attribu= te *attr, const char *buf, + size_t count) +{ + struct wiegand_gpio *wiegand_gpio =3D dev_get_drvdata(dev); + struct wiegand_controller *ctlr =3D wiegand_gpio->ctlr; + + return store_ulong(&(ctlr->payload_len), buf, count, WIEGAND_MAX_PAYLEN_B= YTES * 8); +} +static DEVICE_ATTR_RW(payload_len); + +static struct attribute *wiegand_gpio_attrs[] =3D { + &dev_attr_payload_len.attr, + NULL +}; +ATTRIBUTE_GROUPS(wiegand_gpio); + +/** + * wiegand_gpio_send_bit - Sends a single bit + * + * @wiegand_gpio: Instance of the Wiegand + * @value: Bit value to send + * @last: Indicates last bit of a message + * + * To send a bit of value 1 following the wiegand protocol, one must set + * the wiegand_data_hi to low for the duration of pulse. Similarly to send + * a bit of value 0, the wiegand_data_lo is set to low for pulse duration. + * This way the two lines are never low at the same time. + */ +void wiegand_gpio_send_bit(struct wiegand_gpio *wiegand_gpio, bool value, = bool last) +{ + u32 sleep_len; + struct wiegand_timing *timing =3D &wiegand_gpio->ctlr->timing; + u32 pulse_len =3D timing->pulse_len; + u32 interval_len =3D timing->interval_len; + u32 frame_gap =3D timing->frame_gap; + struct gpio_desc *gpio =3D value ? wiegand_gpio->gpios->desc[1] : + wiegand_gpio->gpios->desc[0]; + + gpiod_set_value_cansleep(gpio, 0); + udelay(pulse_len); + gpiod_set_value_cansleep(gpio, 1); + + if (last) + sleep_len =3D frame_gap - pulse_len; + else + sleep_len =3D interval_len - pulse_len; + + fsleep(sleep_len); +} + +static int wiegand_gpio_write_by_bits(struct wiegand_gpio *wiegand_gpio, u= 16 bitlen) +{ + unsigned int i; + bool value; + + for (i =3D 0; i < bitlen - 1; i++) { + value =3D test_bit(i, wiegand_gpio->ctlr->data_bitmap); + wiegand_gpio_send_bit(wiegand_gpio, value, false); + } + + value =3D test_bit(bitlen - 1, wiegand_gpio->ctlr->data_bitmap); + wiegand_gpio_send_bit(wiegand_gpio, value, true); + + return 0; +} + +int wiegand_gpio_transfer_message(struct wiegand_controller *ctlr) +{ + struct wiegand_gpio *wiegand_gpio =3D wiegand_primary_get_devdata(ctlr); + u8 msg_bitlen =3D ctlr->payload_len; + + wiegand_gpio_write_by_bits(wiegand_gpio, msg_bitlen); + + return 0; +} + +static int wiegand_gpio_request(struct device *dev, struct wiegand_gpio *w= iegand_gpio) +{ + wiegand_gpio->gpios =3D devm_gpiod_get_array(dev, "data", GPIOD_OUT_HIGH); + + if (IS_ERR(wiegand_gpio->gpios)) + dev_err(dev, "unable to get gpios\n"); + + return PTR_ERR_OR_ZERO(wiegand_gpio->gpios); +} + +static int wiegand_gpio_probe(struct platform_device *device) +{ + int status; + struct wiegand_controller *primary; + struct wiegand_gpio *wiegand_gpio; + struct device *dev =3D &device->dev; + + primary =3D devm_wiegand_alloc_controller(dev, sizeof(*wiegand_gpio), fal= se); + if (!primary) + return -ENOMEM; + + primary->transfer_message =3D &wiegand_gpio_transfer_message; + primary->payload_len =3D WIEGAND_DEFAULT_PAYLOAD_LEN; + + wiegand_gpio =3D wiegand_primary_get_devdata(primary); + wiegand_gpio->ctlr =3D primary; + + platform_set_drvdata(device, wiegand_gpio); + + primary->bus_num =3D device->id; + wiegand_gpio->dev =3D dev; + + status =3D wiegand_gpio_request(dev, wiegand_gpio); + if (status) + return dev_err_probe(dev, status, "failed at requesting GPIOs\n"); + + status =3D gpiod_direction_output(wiegand_gpio->gpios->desc[0], 1); + if (status) + return dev_err_probe(dev, status, "failed to set GPIOs direction\n"); + + status =3D gpiod_direction_output(wiegand_gpio->gpios->desc[1], 1); + if (status) + return dev_err_probe(dev, status, "failed to set GPIOs direction\n"); + + status =3D devm_wiegand_register_controller(dev, primary); + if (status) + dev_err_probe(dev, status, "failed to register primary\n"); + return 0; +} + +static const struct of_device_id wiegand_gpio_dt_idtable[] =3D { + { .compatible =3D "wiegand-gpio", }, + {} +}; +MODULE_DEVICE_TABLE(of, wiegand_gpio_dt_idtable); + +static struct platform_driver wiegand_gpio_driver =3D { + .driver =3D { + .name =3D "wiegand-gpio", + .of_match_table =3D wiegand_gpio_dt_idtable, + .dev_groups =3D wiegand_gpio_groups, + }, + .probe =3D wiegand_gpio_probe, +}; +module_platform_driver(wiegand_gpio_driver); + +MODULE_IMPORT_NS(WIEGAND); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Wiegand write-only driver realized by GPIOs"); +MODULE_AUTHOR("Martin Za=C5=A5ovi=C4=8D "); --=20 2.40.1