From: "Martin Zaťovič" <m.zatovic1@gmail.com>
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,
"Martin Zaťovič" <m.zatovic1@gmail.com>
Subject: [PATCHv5 4/4] wiegand: add Wiegand GPIO bitbanged controller driver
Date: Thu, 24 Aug 2023 13:10:15 +0200 [thread overview]
Message-ID: <20230824111015.57765-5-m.zatovic1@gmail.com> (raw)
In-Reply-To: <20230824111015.57765-1-m.zatovic1@gmail.com>
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 <linus.walleij@linaro.org>
Signed-off-by: Martin Zaťovič <m.zatovic1@gmail.com>
---
.../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/Documentation/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ťovič <m.zatovic1@gmail.com>
+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ťovič <m.zatovic1@gmail.com>
S: Maintained
+F: Documentation/ABI/testing/sysfs-driver-wiegand-gpio
F: Documentation/devicetree/bindings/wiegand/wiegand-gpio.yaml
+F: drivers/wiegand/wiegand-gpio.c
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
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) += wiegand.o
+obj-$(CONFIG_WIEGAND_GPIO) += 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 <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/wiegand.h>
+
+#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, unsigned long max)
+{
+ int rc;
+ u32 new;
+
+ rc = kstrtou32(buf, 0, &new);
+ if (rc)
+ return rc;
+
+ if (new > max)
+ return -ERANGE;
+
+ *val = new;
+ return size;
+}
+
+/*
+ * Attribute file for setting payload length of Wiegand messages.
+ */
+static ssize_t payload_len_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct wiegand_gpio *wiegand_gpio = dev_get_drvdata(dev);
+ struct wiegand_controller *ctlr = wiegand_gpio->ctlr;
+
+ return sysfs_emit(buf, "%u\n", ctlr->payload_len);
+}
+
+static ssize_t payload_len_store(struct device *dev, struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct wiegand_gpio *wiegand_gpio = dev_get_drvdata(dev);
+ struct wiegand_controller *ctlr = wiegand_gpio->ctlr;
+
+ return store_ulong(&(ctlr->payload_len), buf, count, WIEGAND_MAX_PAYLEN_BYTES * 8);
+}
+static DEVICE_ATTR_RW(payload_len);
+
+static struct attribute *wiegand_gpio_attrs[] = {
+ &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 = &wiegand_gpio->ctlr->timing;
+ u32 pulse_len = timing->pulse_len;
+ u32 interval_len = timing->interval_len;
+ u32 frame_gap = timing->frame_gap;
+ struct gpio_desc *gpio = 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 = frame_gap - pulse_len;
+ else
+ sleep_len = interval_len - pulse_len;
+
+ fsleep(sleep_len);
+}
+
+static int wiegand_gpio_write_by_bits(struct wiegand_gpio *wiegand_gpio, u16 bitlen)
+{
+ unsigned int i;
+ bool value;
+
+ for (i = 0; i < bitlen - 1; i++) {
+ value = test_bit(i, wiegand_gpio->ctlr->data_bitmap);
+ wiegand_gpio_send_bit(wiegand_gpio, value, false);
+ }
+
+ value = 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 = wiegand_primary_get_devdata(ctlr);
+ u8 msg_bitlen = 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 *wiegand_gpio)
+{
+ wiegand_gpio->gpios = 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 = &device->dev;
+
+ primary = devm_wiegand_alloc_controller(dev, sizeof(*wiegand_gpio), false);
+ if (!primary)
+ return -ENOMEM;
+
+ primary->transfer_message = &wiegand_gpio_transfer_message;
+ primary->payload_len = WIEGAND_DEFAULT_PAYLOAD_LEN;
+
+ wiegand_gpio = wiegand_primary_get_devdata(primary);
+ wiegand_gpio->ctlr = primary;
+
+ platform_set_drvdata(device, wiegand_gpio);
+
+ primary->bus_num = device->id;
+ wiegand_gpio->dev = dev;
+
+ status = wiegand_gpio_request(dev, wiegand_gpio);
+ if (status)
+ return dev_err_probe(dev, status, "failed at requesting GPIOs\n");
+
+ status = gpiod_direction_output(wiegand_gpio->gpios->desc[0], 1);
+ if (status)
+ return dev_err_probe(dev, status, "failed to set GPIOs direction\n");
+
+ status = gpiod_direction_output(wiegand_gpio->gpios->desc[1], 1);
+ if (status)
+ return dev_err_probe(dev, status, "failed to set GPIOs direction\n");
+
+ status = 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[] = {
+ { .compatible = "wiegand-gpio", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, wiegand_gpio_dt_idtable);
+
+static struct platform_driver wiegand_gpio_driver = {
+ .driver = {
+ .name = "wiegand-gpio",
+ .of_match_table = wiegand_gpio_dt_idtable,
+ .dev_groups = wiegand_gpio_groups,
+ },
+ .probe = 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ťovič <m.zatovic1@gmail.com>");
--
2.40.1
next prev parent reply other threads:[~2023-08-24 11:11 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-08-24 11:10 [PATCHv5 0/4] Wiegand bus driver and GPIO bitbanged controller Martin Zaťovič
2023-08-24 11:10 ` [PATCHv5 1/4] dt-bindings: wiegand: add Wiegand controller common properties Martin Zaťovič
2023-08-24 13:37 ` Rob Herring
2023-08-25 13:17 ` Martin Zaťovič
2023-08-24 11:10 ` [PATCHv5 2/4] wiegand: add Wiegand bus driver Martin Zaťovič
2023-08-24 11:40 ` Greg KH
2023-08-24 12:53 ` Martin Zaťovič
2023-08-24 13:08 ` Greg KH
2023-08-24 13:28 ` Andy Shevchenko
2023-08-24 13:26 ` Andy Shevchenko
2023-08-24 11:10 ` [PATCHv5 3/4] dt-bindings: wiegand: add GPIO bitbanged Wiegand controller Martin Zaťovič
2023-08-24 12:32 ` Rob Herring
2023-08-24 13:40 ` Rob Herring
2023-08-24 11:10 ` Martin Zaťovič [this message]
2023-08-24 13:35 ` [PATCHv5 4/4] wiegand: add Wiegand GPIO bitbanged controller driver Andy Shevchenko
2023-08-24 13:44 ` Andy Shevchenko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20230824111015.57765-5-m.zatovic1@gmail.com \
--to=m.zatovic1@gmail.com \
--cc=andriy.shevchenko@intel.com \
--cc=arnd@arndb.de \
--cc=axboe@kernel.dk \
--cc=benjamin.tissoires@redhat.com \
--cc=conor+dt@kernel.org \
--cc=damien.lemoal@opensource.wdc.com \
--cc=devicetree@vger.kernel.org \
--cc=geert+renesas@glider.be \
--cc=gregkh@linuxfoundation.org \
--cc=jacek.lawrynowicz@linux.intel.com \
--cc=krzysztof.kozlowski+dt@linaro.org \
--cc=linus.walleij@linaro.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@zary.sk \
--cc=masahiroy@kernel.org \
--cc=mathieu.poirier@linaro.org \
--cc=nipun.gupta@amd.com \
--cc=ogabbay@kernel.org \
--cc=quic_jhugo@quicinc.com \
--cc=robh+dt@kernel.org \
--cc=tzimmermann@suse.de \
--cc=yangyicong@hisilicon.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox