lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <ZJRKndTB4HEz/Ztf@smile.fi.intel.com>
Date:   Thu, 22 Jun 2023 16:20:29 +0300
From:   Andy Shevchenko <andriy.shevchenko@...el.com>
To:     Martin Zaťovič <m.zatovic1@...il.com>
Cc:     linux-kernel@...r.kernel.org, robh+dt@...nel.org,
        krzysztof.kozlowski+dt@...aro.org, conor+dt@...nel.org,
        gregkh@...uxfoundation.org, beanhuo@...ron.com,
        nipun.gupta@....com, linus.walleij@...aro.org, mwen@...lia.com,
        bvanassche@....org, arnd@...db.de, ogabbay@...nel.org,
        linux@...y.sk, jacek.lawrynowicz@...ux.intel.com,
        geert+renesas@...der.be, benjamin.tissoires@...hat.com,
        masahiroy@...nel.org, yangyicong@...ilicon.com,
        devicetree@...r.kernel.org
Subject: Re: [PATCHv4 2/4] wiegand: add Wiegand bus driver

On Wed, May 10, 2023 at 06:22:41PM +0200, Martin Zaťovič wrote:

This needs much more work. See my comments below.

> Add a bus driver for Wiegand protocol. The 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 represents a master and contains
> attributes such as the payload_len for configuring the size
> of a single Wiegand message in bits. It also stores the
> controller attributes defined in the devicetree.
> 
> Each Wiegand controller should be associated with one Wiegand
> device, as Wiegand is typically a point-to-point bus.

Any chance to have a Datasheet: (or Link:) tag to point to the protocol
specifications?

...

> +config WIEGAND
> +        tristate "Wiegand Bus driver"
> +        help
> +	  The "Wiegand Interface" is an asynchronous low-level protocol
> +	  or wiring standard. It is typically used for point-to-point
> +	  communication. The data length of Wiegand messages is not defined,
> +	  so the devices usually default to 26, 36 or 37 bits per message.
> +	  The throughput of Wiegand depends on the selected pulse length and
> +	  the intervals between pulses, in comparison to other busses it
> +	  is generally rather slow.
> +
> +	  Despite its higher age, Wiegand remains widely used in access
> +	  control systems to connect a card swipe mechanism. Such mechanisms
> +	  utilize the Wiegand effect to transfer data from the card to
> +	  the reader.
> +
> +	  Wiegand uses two wires to transmit the data D0 and D1. Both lines
> +	  are initially pulled up. When a bit of value 0 is being transmitted,
> +	  the D0 line is pulled down. Similarly, when a bit of value 1 is being
> +	  transmitted, the D1 line is pulled down.

&You have an indentation issues in all lines above, except the first one.

Should be

config ...
<TAB>tristate
<TAB>help
<TAB><sp><sp>help text

<TAB> — tabulation
<sp> — space

...

> +#include <linux/cdev.h>

+ container_of.h

> +#include <linux/device.h>
> +#include <linux/dmaengine.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/property.h>
> +#include <linux/slab.h>

+ types.h

> +#include <linux/wiegand.h>

...

> +/**
> + * 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];
> +};

> +DEFINE_IDA(wiegand_controller_ida);

Why not static?

> +static inline void wiegand_device_put(struct wiegand_device *wiegand);
> +static inline struct wiegand_device *to_wiegand_device(struct device *dev);
> +
> +static int wiegand_fopen(struct inode *ino, struct file *filp);
> +static int wiegand_frelease(struct inode *ino, struct file *filp);
> +static ssize_t wiegand_fwrite(struct file *filp, char __user const *buf, size_t len,
> +				loff_t *offset);

Why do you need forward declarations?

...

> +struct wiegand_controller *wiegand_alloc_controller(struct device *dev, unsigned int size,
> +						bool secondary)
> +{
> +	struct wiegand_controller *ctlr;
> +	size_t ctlr_size = ALIGN(sizeof(*ctlr), dma_get_cache_alignment());

> +	if (!dev)
> +		return NULL;

Hmm... Why this check is needed?

> +	ctlr = kzalloc(size + ctlr_size, GFP_KERNEL);

Perhaps you need to use one of the macros from overflow.h.

> +	if (!ctlr)
> +		return NULL;
> +
> +	device_initialize(&ctlr->dev);
> +
> +	ctlr->bus_num = -1;
> +	ctlr->secondary = secondary;
> +	ctlr->dev.parent = dev;
> +	ctlr->dev.release = wiegand_controller_release;
> +
> +	wiegand_controller_set_devdata(ctlr, (void *)ctlr + ctlr_size);
> +
> +	return ctlr;
> +}

...

> +EXPORT_SYMBOL_GPL(wiegand_alloc_controller);

Can we have it namespaced from day 1, please?

...

> +struct wiegand_controller *devm_wiegand_alloc_controller(struct device *dev, unsigned int size,
> +							bool secondary)
> +{
> +	struct wiegand_controller *ctlr, **ptr;
> +
> +	ptr = devres_alloc(devm_wiegand_release_controller, sizeof(*ptr), GFP_KERNEL);
> +	if (!ptr)
> +		return NULL;
> +
> +	ctlr = wiegand_alloc_controller(dev, size, secondary);
> +	if (ctlr) {
> +		ctlr->devm_allocated = true;
> +		*ptr = ctlr;
> +		devres_add(dev, ptr);
> +	} else {
> +		devres_free(ptr);
> +	}

Please, use devm_add_action_or_reset().

> +	return ctlr;
> +}

> +/**
> + * register_wiegand_device - allocates and registers a new Wiegand device
> + * @ctlr: controller structure to attach device to
> + * @nc: devicetree node for the device

Have you run kernel doc validator?
And it's not device tree only anymore, use word "firmware" instead.

> + */

...

> +	wiegand = wiegand_alloc_device(ctlr);
> +	if (!wiegand) {

> +		dev_err(&ctlr->dev, "wiegad_device alloc error for %pOF\n", fwnode);

We don't print an error messages for -ENOMEM.

> +		ret = -ENOMEM;
> +		goto err_out;

Are you sure we need to put the device in this case? When it will be not NULL here?

> +	}

> +	fwnode_handle_get(fwnode);
> +	wiegand->dev.fwnode = fwnode;

Not sure why you need a bumped reference, but in any case the problematic
is the second line. Please, avoid dereferencing fwnode in struct device.
Use respective APIs, here is device_set_node().

> +	ret = wiegand_add_device(wiegand);
> +	if (ret) {
> +		dev_err(&ctlr->dev, "wiegand_device register error %pOF\n", fwnode);
> +		goto err_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 using the same pulse length, format or else.\n");
> +
> +	return wiegand;
> +
> +err_node_put:
> +	fwnode_handle_put(fwnode);
> +err_out:
> +	wiegand_device_put(wiegand);
> +	return ERR_PTR(ret);

...

> +static void register_wiegand_devices(struct wiegand_controller *ctlr)
> +{
> +	struct wiegand_device *wiegand;
> +	struct fwnode_handle *fwnode;

> +	if (!ctlr->dev.fwnode)
> +		return;

This is a dup, which is implied already by the below for_each call.

> +	fwnode_for_each_available_child_node(ctlr->dev.fwnode, fwnode) {
> +		wiegand = register_wiegand_device(ctlr, fwnode);
> +		if (IS_ERR(wiegand))
> +			dev_warn(&ctlr->dev, "failed to create wiegand device for %pOF\n", fwnode);

You are using wrong specifier for fwnode. PLease, fix it everywhere.

> +	}
> +}

...

> +static void wiegand_controller_parse_property(struct device *dev, const char *prop_name,
> +				       u32 *cur_val_p, u32 def_val, bool use_def)
> +{
> +	int ret;
> +
> +	ret = device_property_read_u32(dev, prop_name, cur_val_p);
> +	if ((ret && use_def) || *cur_val_p == 0)
> +		*cur_val_p = def_val;
> +
> +	dev_dbg(dev, "%s: %u\n", prop_name, *cur_val_p);

Why do we need this message? What for?

> +}

> +#define USE_DEFAULT_VAL 1

Redundant. hence redundant second parameter to the above call.

...

> +static void wiegand_controller_parse_properties(struct wiegand_controller *ctlr)
> +{
> +	wiegand_controller_parse_property(&ctlr->dev, "pulse-len-us", &ctlr->pulse_len,
> +					  50, USE_DEFAULT_VAL);
> +	wiegand_controller_parse_property(&ctlr->dev, "interval-len-us", &ctlr->interval_len,
> +					  2000, USE_DEFAULT_VAL);
> +	wiegand_controller_parse_property(&ctlr->dev, "frame-gap-us", &ctlr->frame_gap,
> +					  2000, USE_DEFAULT_VAL);

Default make more sense from group parsing, like I²C core does for timings.

> +}

...

> +int wiegand_register_controller(struct wiegand_controller *ctlr)
> +{
> +	struct device *dev = ctlr->dev.parent;
> +	int status, id;

> +	if (!dev)
> +		return -ENODEV;

Why?

> +	status = wiegand_controller_check_ops(ctlr);
> +	if (status)
> +		return status;
> +
> +	id = ida_alloc(&wiegand_controller_ida, GFP_KERNEL);
> +	if (WARN(id < 0, "couldn't get an id\n"))

Is it really needs to be WARN()?

> +		return id;
> +	ctlr->bus_num = id;

> +	dev_set_name(&ctlr->dev, "wiegand%u", ctlr->bus_num);

No check?

> +	ctlr->miscdev.name = kasprintf(GFP_KERNEL, "wiegand1");
> +	if (!ctlr->miscdev.name)
> +		return -ENOMEM;

...

> +free_bus_id:

out_free_bus_id:

> +	ida_free(&wiegand_controller_ida, ctlr->bus_num);
> +	misc_deregister(&ctlr->miscdev);
> +	kfree(ctlr->miscdev.name);
> +	return status;
> +}

...

> +static inline void wiegand_device_put(struct wiegand_device *wiegand)

And where is the corresponding get()?

> +{
> +	if (wiegand)
> +		put_device(&wiegand->dev);

> +	if (wiegand->controller->cleanup)
> +		wiegand->controller->cleanup(wiegand);

Dup of wiegand_cleanup().

> +}

...

> +static int wiegand_dev_set_name(struct wiegand_device *wiegand, u8 id)
> +{
> +	int ret = dev_set_name(&wiegand->dev, "%s.%u", dev_name(&wiegand->controller->dev), id);
> +	return ret;

	return dev_set_name(...); ?

> +}

...

> +int wiegand_setup(struct wiegand_device *wiegand)
> +{
> +	int status = 0;

Redundant assignment, see below.

> +	if (wiegand->controller->setup) {
> +		status = wiegand->controller->setup(wiegand);
> +		if (status) {
> +			dev_err(&wiegand->controller->dev, "failed to setup device: %d\n", status);

In all such cases you may make the code neater with

	struct device *ctrl_dev = &wiegand->controller->dev;

Also consider

	struct ... *ctrl = wiegand->controller;
	struct device *ctrl_dev = &controller->dev;

> +			return status;
> +		}
> +	}

> +	return status;

Here it's always 0, right?

	return 0;

> +}

...

> +void wiegand_unregister_device(struct wiegand_device *wiegand)
> +{
> +	if (!wiegand)
> +		return;

> +	if (wiegand->dev.fwnode)

Yet another dup conditional, implied by fwnode API.

> +		fwnode_handle_put(wiegand->dev.fwnode);

> +	fwnode_remove_software_node(wiegand->dev.fwnode);

Where this has come from? Leftover from some copy'n'paste?

> +	device_del(&wiegand->dev);
> +	wiegand_cleanup(wiegand);
> +	put_device(&wiegand->dev);
> +}

...

> +static ssize_t wiegand_get_user_data(struct wiegand_controller *ctlr, char __user const *buf,
> +					  size_t len)
> +{
> +	int i;
> +	char data_buffer[WIEGAND_MAX_PAYLEN_BYTES];
> +
> +	if (len > WIEGAND_MAX_PAYLEN_BYTES)
> +		return -EBADMSG;
> +
> +	if (copy_from_user(&data_buffer[0], buf, WIEGAND_MAX_PAYLEN_BYTES))

&data_buffer[0] --> data_buffer

> +		return -EFAULT;
> +
> +	for (i = 0; i < len; i++)
> +		bitmap_set_value8(ctlr->data_bitmap, data_buffer[i], i * 8);
> +
> +	return len;
> +}

I'm wondering why you can't use bitmap_parse_user() with the respective format?
Or even bitmap_parselist_user() (if you feel like this would be better, I feel
like not really, but who knows).

...

> +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;
> +	u32 msg_length = ctlr->payload_len;
> +
> +	if (!buf || len == 0 || DIV_ROUND_UP(msg_length, 8) > len)

BITS_TO_BYTES()

> +		return -EINVAL;
> +
> +	ret = wiegand_get_user_data(ctlr, buf, len);
> +	if (ret < 0)
> +		return ret;
> +
> +	ctlr->transfer_message(ctlr);
> +
> +	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);
> +
> +	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 err;
> +	}
> +
> +	mutex_unlock(&ctlr->file_lock);
> +
> +	return 0;
> +err:

err_unlock:

> +	mutex_unlock(&ctlr->file_lock);

> +	mutex_destroy(&ctlr->file_lock);

Huh?!

> +	return ret;
> +}

...

> +	return (strcmp(wiegand_dev->modalias, drv->name) == 0);

Outer parentheses are redundant.

...

> +static int wiegand_probe(struct device *dev)
> +{
> +	struct wiegand_device *wiegand = to_wiegand_device(dev);
> +	const struct wiegand_driver *wdrv = to_wiegand_driver(dev->driver);

> +	if (wdrv->probe)

Shouldn't this be a mandatory callback? Or i.o.w. can you elaborate the use
case with probe == NULL?

> +		return wdrv->probe(wiegand);
> +
> +	return 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);

> +		return ret;
> +	}
> +
> +	return 0;

	return ret;

> +}

...

> +postcore_initcall_sync(wiegand_init);

Move it closer to the callback itself.

...


> +#ifndef H_LINUX_INCLUDE_LINUX_WIEGAND_H
> +#define H_LINUX_INCLUDE_LINUX_WIEGAND_H

container_of.h

> +#include <linux/device.h>
> +#include <linux/miscdevice.h>

> +#include <linux/mod_devicetable.h>

Not sure it's in use.

> +#include <linux/mutex.h>

types.h

> +#define WIEGAND_NAME_SIZE 32
> +#define WIEGAND_MAX_PAYLEN_BYTES 256

> +extern struct bus_type wiegand_type;

So, extern or static? Please, be consistent.

...

> +	struct file_operations fops;

Missing header for this data type.

...

> +#define wiegand_primary_get_devdata(_ctlr) wiegand_controller_get_devdata(_ctlr)
> +#define wiegand_primary_set_devdata(_ctlr, _data) wiegand_controller_set_devdata(_ctlr, _data)

Why not _data --> data?

...

> +extern void wiegand_unregister_device(struct wiegand_device *wiegand);
> +extern struct wiegand_controller *wiegand_device_get_controller(struct wiegand_device *dev);
> +
> +extern int wiegand_send_message(struct wiegand_device *wiegand, unsigned long *msg_bmp, u8 bitlen);

...and so on...

Drop extern from the function declarations.

...

> +/* Wiegand driver section  */

Single space is enough.

-- 
With Best Regards,
Andy Shevchenko


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ