From: Andy Shevchenko <andriy.shevchenko@intel.com>
To: "Martin Zaťovič" <m.zatovic1@gmail.com>
Cc: linux-kernel@vger.kernel.org, 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
Subject: Re: [PATCHv5 2/4] wiegand: add Wiegand bus driver
Date: Thu, 24 Aug 2023 16:26:56 +0300 [thread overview]
Message-ID: <ZOdaoE8jLpwGYPFr@smile.fi.intel.com> (raw)
In-Reply-To: <20230824111015.57765-3-m.zatovic1@gmail.com>
On Thu, Aug 24, 2023 at 01:10:13PM +0200, Martin Zaťovič wrote:
> The Wiegand protocol serves as a standardized interface protocol, extensively
> employed within electronic access control systems, to facilitate data exchange
> between credentials, readers, and door controllers. Its inception can be
> attributed to the widespread adoption of Wiegand card readers, leveraging the
> 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 two
> distinct data wires designated as DATA0 and DATA1. During periods of inactivity,
> both DATA0 and DATA1 lines remain pulled up. For transmitting a '0,' the DATA0
> 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 within the
> message or enforces a timeout, often set at around ten times the inter-pulse 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 allocates
> the initial and terminal bits for parity. The subsequent eight bits following
> the initial parity bit are reserved for the Facility Code designed for distinct
> 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 specifications.
>
> 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 contains
> 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 device,
> as Wiegand is typically a point-to-point bus.
...
> +#include <linux/bitops.h>
Should be bitmap.h
> +#include <linux/cdev.h>
> +#include <linux/container_of.h>
> +#include <linux/device.h>
> +#include <linux/dmaengine.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/module.h>
mutex.h ?
> +#include <linux/of_device.h>
> +#include <linux/property.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +#include <linux/wiegand.h>
...
> +static ssize_t wiegand_fwrite(struct file *filp, char __user const *buf, size_t len,
> + loff_t *offset)
> +{
> + int ret;
> + struct wiegand_controller *ctlr = filp->private_data;
> +
> + mutex_lock(&ctlr->file_lock);
> +
> + if (!buf || len == 0)
Ouch?!
To avoid this, you can use cleanup.h and guard your functions with a lock,
moreover in this case the check is not even needed to be performed under
the lock.
> + return -EINVAL;
> +
> + ret = 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 = container_of(filp->f_op, struct wiegand_controller, fops);
> +
> + filp->private_data = ctlr;
> +
> + mutex_lock(&ctlr->file_lock);
Do you care about the call being interrupted by a signal?
Ditto for other system calls in the framework.
> + if ((filp->f_flags & O_ACCMODE) == O_RDONLY || (filp->f_flags & O_ACCMODE) == O_RDWR) {
> + dev_err(ctlr->miscdev.this_device, "device is write only\n");
> + ret = -EIO;
> + goto out_unlock;
> + }
> +
> + mutex_unlock(&ctlr->file_lock);
> + return 0;
> +
> +out_unlock:
> + mutex_unlock(&ctlr->file_lock);
> + return ret;
> +}
...
> + size_t ctlr_size = ALIGN(sizeof(*ctlr), dma_get_cache_alignment());
Yeah, yet another user for a new macro in overflow.h (not yet there, though).
> + size_t total_size;
> +
> + if (check_add_overflow(size, ctlr_size, &total_size))
> + return NULL;
> +
> + ctlr = kzalloc(total_size, GFP_KERNEL);
> + if (!ctlr)
> + return NULL;
...
> +struct wiegand_controller *devm_wiegand_alloc_controller(struct device *dev, unsigned int size,
> + bool secondary)
> +{
> + struct wiegand_controller *ctlr;
> +
> + ctlr = wiegand_alloc_controller(dev, size, secondary);
> + if (ctlr)
> + ctlr->devm_allocated = true;
> + else
> + return NULL;
if (!ctrl)
return NULL;
or maybe
return ERR_PTR(-ENOMEM);
> + if (devm_add_action_or_reset(dev, wiegand_controller_put, ctlr))
> + return NULL;
ret = ...
if (ret)
return ERR_PTR(ret);
?
> +
> + return ctlr;
...
> +/**
> + * register_wiegand_device - allocates and registers a new Wiegand device
> + *
> + * @ctlr: controller structure to attach device to
> + * @node: firmware node for the device
Run
scripts/kernel-doc -v -none -Wall ...
against this file and fix all warnings.
> + */
> + struct fwnode_handle *node)
Usually we call it fwnode.
...
> + device_set_node(&wiegand->dev, node);
device_set_node(&wiegand->dev, fwnode_handle_get(fwnode));
> +out_node_put:
> + fwnode_handle_put(node);
Hmm... Shouldn't we do this at the ->release()?
> + put_device(&wiegand->dev);
> + wiegand_cleanup(wiegand);
> + return ERR_PTR(ret);
> +}
...
> + struct fwnode_handle *node;
fwnode
here and everywhere else.
...
> + fwnode_for_each_available_child_node(ctlr->dev.fwnode, node) {
device_for_each_child_node()
> + wiegand = register_wiegand_device(ctlr, node);
> + if (IS_ERR(wiegand))
> + dev_warn(&ctlr->dev, "failed to create wiegand device for %pfwf\n", node);
> + }
...
> + struct device *ctlr_dev = &ctlr->dev;
Just name it "dev". You can use similar approach in another functions,
like the above, to make them look nicer.
...
> + ret = device_property_read_u32(ctlr_dev, "pulse-len-us", &pulse_len);
> + if (!ret && pulse_len > 0)
> + timing->pulse_len = pulse_len;
> + else
> + timing->pulse_len = WIEGAND_DEFAULT_PULSE_LEN;
What about
/* Use default if property is absent or can't be read */
pulse_len = WIEGAND_DEFAULT_PULSE_LEN;
device_property_read_u32(dev, "pulse-len-us", &pulse_len);
timing->pulse_len = pulse_len;
Wrong values should be caught up by DT schema linter, no? If the parameters are
0 it's not your problem, although you can warn.
I'm not sure about this, so your current variant is okay.
Ditto for the rest.
...
> +static int __unregister(struct device *dev, void *null)
It is recommended to use namespace even if it's static function.
...
> +void wiegand_unregister_controller(void *ptr)
> +{
Why the parameter is not properly typed?
Yes for devm you probably need a wrapper
static devm_..._unregister_...(void *ctrl)
{
wiegand_unregister_controller(ctrl)
}
For the exported _unregister, taking into account the possible optional support
for this, you may need to check the parameter to be valid.
if (IS_ERR_OR_NULL(...)) // _OR_NULL probably is not needed as per above
return;
> +}
> + status = 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);
Shouldn't it be needed to be initialized before device itself?
...
> + status = device_add(&ctlr->dev);
> + if (status < 0)
Don't we need to call something like device_del() or put_device() at this point?
OK, read doc for device_add() it clarifies this.
> + goto out_free_ida_name_misc;
...
> + status = __wiegand_add_device(wiegand);
> + if (!status) {
> + ctlr->device_count++;
> + wiegand->id = wiegand->controller->device_count;
> + }
> +
> + return status;
if (status)
return status;
...
return 0;
...
> +void wiegand_unregister_device(struct wiegand_device *wiegand)
> +{
> + if (!wiegand)
IS_ERR_OR_NULL() or alike (see above)
> + return;
> +
> + fwnode_handle_put(wiegand->dev.fwnode);
No, do not dereference fwnode in struct device, always use proper API
and/or dev_fwnode() accessor.
> + device_del(&wiegand->dev);
> + wiegand_cleanup(wiegand);
> + put_device(&wiegand->dev);
> +}
...
> +static int wiegand_match_device(struct device *dev, struct device_driver *drv)
> +{
> + struct wiegand_device *wiegand_dev = to_wiegand_device(dev);
> +
> + if (of_driver_match_device(dev, drv))
> + return 1;
I would add ACPI support as well as
if (acpi_driver_match_device(dev, drv))
return 1;
> + return strcmp(wiegand_dev->modalias, drv->name) == 0;
> +}
...
> +static int __init wiegand_init(void)
> +{
> + int ret;
> +
> + ret = bus_register(&wiegand_bus_type);
> + if (ret < 0)
> + pr_err("Wiegand bus registration failed: %d\n", ret);
pr_fmt() may help to have unified prefix for all messages printed with help of
pr_*() macros.
> + return ret;
> +}
...
> +#define WIEGAND_MAX_PAYLEN_BYTES 256
I don't see you use BYTES, but rather BITS. Can you define _BITS instead?
...
> +struct wiegand_device;
+ Blank line.
> +/**
> + * struct wiegand_timing - Wiegand timings in microseconds
Perhaps timings ?
> + * @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 high phase)
> + */
...
> + struct wiegand_timing timing;
Perhaps timings ?
...
> +#endif /* H_LINUX_INCLUDE_LINUX_WIEGAND_H */
Leading H is something unusual?
--
With Best Regards,
Andy Shevchenko
next prev parent reply other threads:[~2023-08-24 13:51 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 [this message]
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 ` [PATCHv5 4/4] wiegand: add Wiegand GPIO bitbanged controller driver Martin Zaťovič
2023-08-24 13:35 ` 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=ZOdaoE8jLpwGYPFr@smile.fi.intel.com \
--to=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=m.zatovic1@gmail.com \
--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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.