Introduce the Counter character device interface
Subsystems (9)
v5 cover Introduce the Counter character device interface
Changes in v5:
- Fixed typographical errors in documentation and comments
- Updated flow charts in documentation for clarity
- Moved uapi header to be part of the character device intro patch
- Fix git squash mistake in 104-quad-8.c; remove redundant changes
- Fix git merge mistake in 104-quad-8.c; fix locking race condition
- Minor code cleanup for clarity; adjust whitespace/flow
- Use put_device if device_add fails
- Document sysfs structures
- Rename "owner" symbols to "scope"; more apt name
- Use resource-managed devm_* allocation functions
- Rename *_free functions to *_remove; following common convention
- Rename COUNTER_DATA* to COUNTER_COMP*; more obvious meaning
- Rename various symbol and define names for clarity
- Bring back static ops function; more secure to have static const
- Rename counter_available union members to "enums" and "strs"
- Implement COUNTER_EVENT* constants; event types are now standard
- Implement atomic Counter watches swap; no more racy event config
Over the past couple years we have noticed some shortcomings with the Counter sysfs interface. Although useful in the majority of situations, there are certain use-cases where interacting through sysfs attributes can become cumbersome and inefficient. A desire to support more advanced functionality such as timestamps, multi-axes positioning tables, and other such latency-sensitive applications, has motivated a reevaluation of the Counter subsystem. I believe a character device interface will be helpful for this more niche area of counter device use.
To quell any concerns from the offset: this patchset makes no changes to the existing Counter sysfs userspace interface -- existing userspace applications will continue to work with no modifications necessary. I request that driver maintainers please test their applications to verify that this is true, and report any discrepancies if they arise.
However, this patchset does contain a major reimplementation of the Counter subsystem core and driver API. A reimplementation was necessary in order to separate the sysfs code from the counter device drivers and internalize it as a dedicated component of the core Counter subsystem module. A minor benefit from all of this is that the sysfs interface is now ensured a certain amount of consistency because the translation is performed outside of individual counter device drivers.
Essentially, the reimplementation has enabled counter device drivers to pass and handle data as native C datatypes now rather than the sysfs strings from before.
A high-level view of how a count value is passed down from a counter driver is exemplified by the following. The driver callbacks are first registered to the Counter core component for use by the Counter userspace interface components:
+----------------------------+
| Counter device driver |
+----------------------------+
| Processes data from device |
+----------------------------+
|
-------------------
/ driver callbacks /
-------------------
|
V
+----------------------+
| Counter core |
+----------------------+
| Routes device driver |
| callbacks to the |
| userspace interfaces |
+----------------------+
|
-------------------
/ driver callbacks /
-------------------
|
+---------------+---------------+
| |
V V
+--------------------+ +---------------------+
| Counter sysfs | | Counter chrdev |
+--------------------+ +---------------------+
| Translates to the | | Translates to the |
| standard Counter | | standard Counter |
| sysfs output | | character device |
+--------------------+ +---------------------+Thereafter, data can be transferred directly between the Counter device driver and Counter userspace interface:
----------------------
/ Counter device \
+----------------------+
| Count register: 0x28 |
+----------------------+
|
-----------------
/ raw count data /
-----------------
|
V
+----------------------------+
| Counter device driver |
+----------------------------+
| Processes data from device |
|----------------------------|
| Type: u64 |
| Value: 42 |
+----------------------------+
|
----------
/ u64 /
----------
|
+---------------+---------------+
| |
V V
+--------------------+ +---------------------+
| Counter sysfs | | Counter chrdev |
+--------------------+ +---------------------+
| Translates to the | | Translates to the |
| standard Counter | | standard Counter |
| sysfs output | | character device |
|--------------------| |---------------------|
| Type: const char * | | Type: u64 |
| Value: "42" | | Value: 42 |
+--------------------+ +---------------------+
| |
--------------- -----------------------
/ const char * / / struct counter_event /
--------------- -----------------------
| |
| V
| +-----------+
| | read |
| +-----------+
| \ Count: 42 /
| -----------
|
V
+--------------------------------------------------+
| `/sys/bus/counter/devices/counterX/countY/count` |
+--------------------------------------------------+
\ Count: "42" /
--------------------------------------------------Counter device data is exposed through standard character device read operations. Device data is gathered when a Counter event is pushed by the respective Counter device driver. Configuration is handled via ioctl operations on the respective Counter character device node.
The following are some questions I have about this patchset:
1. Should standard Counter component data types be defined as u8 or u32?
Many standard Counter component types such COUNTER_COMP_SIGNAL_LEVEL have standard values defined (e.g. COUNTER_SIGNAL_LEVEL_LOW and COUNTER_SIGNAL_LEVEL_HIGH). These values are currently handled by the Counter subsystem code as u8 data types.
If u32 is used for these values instead, C enum structures could be used by driver authors to implicit cast these values via the driver callback parameters; userspace would still use u32 with no issue.
In theory this can work because GCC will treat enums are having a 32-bit size; but I worry about the possibility of build targets that have -fshort-enums enabled, resulting in enums having a size less than 32 bits. Would this be a problem?
2. Should I have reserved members in the userspace structures?
The structures in include/uapi/linux/counter.h are available to userspace applications. Should I reserve space in these structures for future additions and usage? Will endianess and packing be a concern here?
William Breathitt Gray (5):
counter: Internalize sysfs interface code
docs: counter: Update to reflect sysfs internalization
counter: Add character device interface
docs: counter: Document character device interface
counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8
Documentation/ABI/testing/sysfs-bus-counter | 18 + .../ABI/testing/sysfs-bus-counter-104-quad-8 | 32 + Documentation/driver-api/generic-counter.rst | 408 ++++- .../userspace-api/ioctl/ioctl-number.rst | 1 + MAINTAINERS | 2 +- drivers/counter/104-quad-8.c | 775 +++++---- drivers/counter/Kconfig | 6 +- drivers/counter/Makefile | 1 + drivers/counter/counter-chrdev.c | 451 +++++ drivers/counter/counter-chrdev.h | 16 + drivers/counter/counter-core.c | 190 +++ drivers/counter/counter-sysfs.c | 862 ++++++++++ drivers/counter/counter-sysfs.h | 13 + drivers/counter/counter.c | 1496 ----------------- drivers/counter/ftm-quaddec.c | 60 +- drivers/counter/microchip-tcb-capture.c | 100 +- drivers/counter/stm32-lptimer-cnt.c | 175 +- drivers/counter/stm32-timer-cnt.c | 145 +- drivers/counter/ti-eqep.c | 226 +-- include/linux/counter.h | 618 +++---- include/linux/counter_enum.h | 45 - include/uapi/linux/counter.h | 99 ++ 22 files changed, 3053 insertions(+), 2686 deletions(-) create mode 100644 drivers/counter/counter-chrdev.c create mode 100644 drivers/counter/counter-chrdev.h create mode 100644 drivers/counter/counter-core.c create mode 100644 drivers/counter/counter-sysfs.c create mode 100644 drivers/counter/counter-sysfs.h delete mode 100644 drivers/counter/counter.c delete mode 100644 include/linux/counter_enum.h create mode 100644 include/uapi/linux/counter.h
Signature · 1 line
2.28.0
PATCH v5 1/5 counter: Internalize sysfs interface code
This is a reimplementation of the Generic Counter driver interface. There are no modifications to the Counter subsystem userspace interface, so existing userspace applications should continue to run seamlessly.
Overview
========
The purpose of this patch is to internalize the sysfs interface code among the various counter drivers into a shared module. Counter drivers pass and take data natively (i.e. u8, u64, etc.) and the shared counter module handles the translation between the sysfs interface. This guarantees a standard userspace interface for all counter drivers, and helps generalize the Generic Counter driver ABI in order to support the Generic Counter chrdev interface (introduced in a subsequent patch) without significant changes to the existing counter drivers.
A high-level view of how a count value is passed down from a counter driver is exemplified by the following. The driver callbacks are first registered to the Counter core component for use by the Counter userspace interface components:
+----------------------------+
| Counter device driver |
+----------------------------+
| Processes data from device |
+----------------------------+
|
-------------------
/ driver callbacks /
-------------------
|
V
+----------------------+
| Counter core |
+----------------------+
| Routes device driver |
| callbacks to the |
| userspace interfaces |
+----------------------+
|
-------------------
/ driver callbacks /
-------------------
|
+---------------+
|
V
+--------------------+
| Counter sysfs |
+--------------------+
| Translates to the |
| standard Counter |
| sysfs output |
+--------------------+Thereafter, data can be transferred directly between the Counter device driver and Counter userspace interface:
----------------------
/ Counter device \
+----------------------+
| Count register: 0x28 |
+----------------------+
|
-----------------
/ raw count data /
-----------------
|
V
+----------------------------+
| Counter device driver |
+----------------------------+
| Processes data from device |
|----------------------------|
| Type: u64 |
| Value: 42 |
+----------------------------+
|
----------
/ u64 /
----------
|
+---------------+
|
V
+--------------------+
| Counter sysfs |
+--------------------+
| Translates to the |
| standard Counter |
| sysfs output |
|--------------------|
| Type: const char * |
| Value: "42" |
+--------------------+
|
---------------
/ const char * /
---------------
|
V
+--------------------------------------------------+
| `/sys/bus/counter/devices/counterX/countY/count` |
+--------------------------------------------------+
\ Count: "42" /
--------------------------------------------------There are three primary components involved:
Counter device driver
---------------------
Communicates with the hardware device to read/write data; e.g. counter drivers for quadrature encoders, timers, etc.
Counter core
------------
Registers the counter device driver to the system so that the respective callbacks are called during userspace interaction.
Counter sysfs
-------------
Translates counter data to the standard Counter sysfs interface format and vice versa.
Driver ABI
==========
This reimplementation entails several changes to the driver ABI. In particular, the device driver callbacks are now expected to handle standard C datatypes rather than translating the sysfs I/O directly.
To that end, the struct counter_comp structure is introduced to establish counter extensions for Signals, Synapses, and Counts.
The "type" member specifies the type of high-level data (e.g. BOOL, COUNT_DIRECTION, etc.) handled by this extension. The "*_read" and "*_write" members can then be set by the counter device driver with callbacks to handle that data using native C data types (i.e. u8, u64, etc.).
Convenience macros such as COUNTER_COMP_COUNT_U64 are provided for use by driver authors. In particular, driver authors are expected to use the provided macros for standard Counter subsystem attributes in order to maintain a consistent interface for userspace. For example, a counter device driver may define several standard attributes like so:
struct counter_comp count_ext[] = {
COUNTER_COMP_DIRECTION(count_direction_read), COUNTER_COMP_ENABLE(count_enable_read, count_enable_write), COUNTER_COMP_CEILING(count_ceiling_read, count_ceiling_write),
};
This makes it simple to see, add, and modify the attributes that are supported by this driver ("direction", "enable", and "ceiling") and to maintain this code without getting lost in a web of struct braces.
Callbacks must match the function type expected for the respective component or extension. These function types are defined in the struct counter_comp structure as the "*_read" and "*_write" union members.
The corresponding callback prototypes for the extensions above would be:
int count_direction_read(struct counter_device *counter,
struct counter_count *count, u8 *direction); int count_enable_read(struct counter_device *counter, struct counter_count *count, u8 *enable); int count_enable_write(struct counter_device *counter, struct counter_count *count, u8 enable); int count_ceiling_read(struct counter_device *counter, struct counter_count *count, u64 *ceiling); int count_ceiling_write(struct counter_device *counter, struct counter_count *count, u64 ceiling);
In this way, driver authors no longer need to mess with sysfs strings and are instead able to focus on what they actually care about doing -- getting data to/from the devices -- while the Generic Counter interface handles the translation of that data for them between the various userspace interfaces (e.g. sysfs and chardev).
Architecture
============
Counter device registration is the same as before: drivers populate a struct counter_device with components and callbacks, then pass the structure to the devm_counter_register function. However, what's different now is how the Counter subsystem code handles this registration internally.
Whereas before callbacks would interact directly with sysfs data, this interaction is now abstracted and instead callbacks interact with native C data types. The counter_comp structure forms the basis for Counter extensions.
The counter-sysfs.c file contains the code to parse through the counter_device structure and register the requested components and extensions. Attributes are created and populated based on type, with respective translation functions to handle the mapping between sysfs and the counter driver callbacks.
The translation performed for each attribute is straightforward: the attribute type and data is parsed from the counter_attribute structure, the respective counter driver read/write callback is called, and sysfs I/O is handled before or after the driver read/write function is called.
--- MAINTAINERS | 1 - drivers/counter/104-quad-8.c | 476 +++----- drivers/counter/Makefile | 1 + drivers/counter/counter-core.c | 160 +++ drivers/counter/counter-sysfs.c | 773 ++++++++++++ drivers/counter/counter-sysfs.h | 13 + drivers/counter/counter.c | 1496 ----------------------- drivers/counter/ftm-quaddec.c | 60 +- drivers/counter/microchip-tcb-capture.c | 100 +- drivers/counter/stm32-lptimer-cnt.c | 175 +-- drivers/counter/stm32-timer-cnt.c | 145 +-- drivers/counter/ti-eqep.c | 226 ++-- include/linux/counter.h | 628 +++++----- include/linux/counter_enum.h | 45 - 14 files changed, 1758 insertions(+), 2541 deletions(-) create mode 100644 drivers/counter/counter-core.c create mode 100644 drivers/counter/counter-sysfs.c create mode 100644 drivers/counter/counter-sysfs.h delete mode 100644 drivers/counter/counter.c delete mode 100644 include/linux/counter_enum.h
MAINTAINERS +0 -1
@@ -4459,7 +4459,6 @@ F: Documentation/ABI/testing/sysfs-bus-counter* F: Documentation/driver-api/generic-counter.rst F: drivers/counter/ F: include/linux/counter.h -F: include/linux/counter_enum.h CPMAC ETHERNET DRIVER M: Florian Fainelli <[email protected]>
drivers/counter/104-quad-8.c +202 -274
819 lines not on this page · raw message
drivers/counter/Makefile +1 -0
@@ -4,6 +4,7 @@ # obj-$(CONFIG_COUNTER) += counter.o +counter-y := counter-core.o counter-sysfs.o obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o obj-$(CONFIG_STM32_TIMER_CNT) += stm32-timer-cnt.o
drivers/counter/counter-core.c new file +160 -0
160 lines not on this page · raw message
drivers/counter/counter-sysfs.c new file +773 -0
773 lines not on this page · raw message
drivers/counter/counter-sysfs.h new file +13 -0
@@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* Counter sysfs interface +* Copyright (C) 2020 William Breathitt Gray +*/ +#ifndef _COUNTER_SYSFS_H_ +#define _COUNTER_SYSFS_H_ + +#include <linux/counter.h> + +int counter_sysfs_add (struct counter_device *const counter); + +#endif /* _COUNTER_SYSFS_H_ */
drivers/counter/counter.c deleted +0 -1496
1,221 lines not on this page · raw message
drivers/counter/ftm-quaddec.c +24 -36
60 lines not on this page · raw message
drivers/counter/microchip-tcb-capture.c +42 -58
100 lines not on this page · raw message
drivers/counter/stm32-lptimer-cnt.c +92 -83
175 lines not on this page · raw message
drivers/counter/stm32-timer-cnt.c +58 -87
145 lines not on this page · raw message
drivers/counter/ti-eqep.c +117 -109
226 lines not on this page · raw message
include/linux/counter.h +276 -352
628 lines not on this page · raw message
include/linux/counter_enum.h deleted +0 -45
45 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Signature · 1 line
2.28.0
Part of the diff is not shown here. View the raw message.
David Lechner
reviewerRe: [PATCH v5 1/5] counter: Internalize sysfs interface code
Quoted 1 line · William Breathitt Gray
This is a reimplementation of the Generic Counter driver interface.
I'll follow up if I find any problems while testing but here are some comments I had from looking over the patch.
Quoted 43 lines
drivers/counter/counter-core.c new file +36
-0+/** +* counter_register - register Counter to the system +* @counter: pointer to Counter to register +* +* This function registers a Counter to the system. A sysfs "counter" directory +* will be created and populated with sysfs attributes correlating with the +* Counter Signals, Synapses, and Counts respectively. +*/ +int counter_register (struct counter_device *const counter) +{ +struct device *const dev = &counter->dev; +int err; + +/* Acquire unique ID */ + counter->id =ida_simple_get (&counter_ida,0 ,0 ,GFP_KERNEL ); +if (counter->id <0 ) +return counter->id; + +/* Configure device structure for Counter */ + dev->type = &counter_device_type; + dev->bus = &counter_bus_type; +if (counter->parent) { + dev->parent = counter->parent; + dev->of_node = counter->parent->of_node; + } +dev_set_name (dev,"counter%d" , counter->id); + device_initialize(dev);> + dev_set_drvdata(dev, counter); + +/* Add Counter sysfs attributes */ + err =counter_sysfs_add (counter); +if (err) + goto err_free_id; + +/* Add device to system */ + err =device_add (dev); +if (err) {
drivers/counter/counter-core.c line 81
Quoted 10 lines
drivers/counter/counter-core.c new file +10
-0+put_device (dev); + goto err_free_id; + } + +return 0 ; + +err_free_id: +/* get_device/put_device combo used to free managed resources */ +get_device (dev); +put_device (dev);
I've never seen this in a driver before, so it makes me think this is not the "right way" to do this. After device_initialize() is called, we already should have a reference to dev, so only device_put() is needed.
drivers/counter/counter-core.c
Quoted 1 line
drivers/counter/counter-core.c new file +1
-0+ida_simple_remove (&counter_ida, counter->id);
In the case of error after device_initialize() is called, won't this result in ida_simple_remove() being called twice, once here and once in the release callback?
Quoted 10 lines
drivers/counter/counter-core.c new file +10
-0+return err; +} +EXPORT_SYMBOL_GPL (counter_register); + +/** +* counter_unregister - unregister Counter from the system +* @counter: pointer to Counter to unregister +* +* The Counter is unregistered from the system; all allocated memory is freed. +*/
drivers/counter/counter-core.c line 102
Quoted 10 lines
drivers/counter/counter-core.c new file +10
-0+void counter_unregister (struct counter_device *const counter) +{ +if (!counter) +return ; + +device_unregister (&counter->dev); +} +EXPORT_SYMBOL_GPL (counter_unregister); + +static void devm_counter_unreg (struct device *dev,void *res)
To be consistent, it would be nice to spell out unregister.
Quoted 16 lines
drivers/counter/counter-core.c new file +4
-0+{ +counter_unregister (*(struct counter_device **)res); +} +drivers/counter/counter-sysfs.c new file +5
-0+/** +* counter_sysfs_add - Adds Counter sysfs attributes to the device structure +* @counter: Pointer to the Counter device structure +* +* Counter sysfs attributes are created and added to the respective device
drivers/counter/counter-sysfs.c line 722
Quoted 10 lines
drivers/counter/counter-sysfs.c new file +10
-0+* structure for later registration to the system. Resource-managed memory +* allocation is performed by this function, and this memory should be freed +* when no longer needed (automatically by a device_unregister call, or +* manually by a devres_release_all call). +*/ +int counter_sysfs_add (struct counter_device *const counter) +{ +struct device *const dev = &counter->dev; +const size_t num_groups = counter->num_signals + counter->num_counts + +1 ;
It is OK to go past 80 columns, especially for just for a few characters.
Quoted 7 lines
drivers/counter/counter-sysfs.c new file +7
-0+struct counter_attribute_group *groups; +size_t i, j; +int err; +struct attribute_group *group; +struct counter_attribute *p; + +/* Allocate space for attribute groups (signals, counts, and ext) */
drivers/counter/counter-sysfs.c line 739
Quoted 10 lines
drivers/counter/counter-sysfs.c new file +10
-0+ groups =devm_kcalloc (dev, num_groups,sizeof (*groups),GFP_KERNEL ); +if (!groups) +return -ENOMEM ; + +/* Initialize attribute lists */ +for (i =0 ; i < num_groups; i++) +INIT_LIST_HEAD (&groups[i].attr_list); + +/* Register Counter device attributes */ + err =counter_device_register (counter, groups);
This function name is a bit misleading. At first I though we were registering a new counter device (struct device). Maybe counter_sysfs_create_attrs() would be a better name? (I wouldn't mind having all functions in this file having a "counter_sysfs_" prefix for that matter.)
Quoted 7 lines
drivers/counter/ti-eqep.c +0
-0
drivers/counter/ti-eqep.c static int ti_eqep_probe(struct platform_device *pdev) · line 420
Quoted 8 lines
drivers/counter/ti-eqep.c +1
-1@@ -406,7 +414,7 @@ static int ti_eqep_probe(struct platform_device *pdev) priv->counter.name =dev_name (dev); priv->counter.parent = dev; - priv->counter.ops = &ti_eqep_counter_ops; + priv->counter.parent = &ti_eqep_counter_ops; priv->counter.counts = ti_eqep_counts; priv->counter.num_counts =ARRAY_SIZE (ti_eqep_counts); priv->counter.signals = ti_eqep_signals;
This looks like an unintentional change and causes a compile error.
include/linux/counter.h line 11
Quoted 12 lines
include/linux/counter.h +2
-1@@ -6,417 +6,195 @@#ifndef _COUNTER_H_#define _COUNTER_H_ -#include <linux/counter_enum.h> #include <linux/device.h> +#include <linux/kernel.h> +#include <linux/list.h>
struct list_head is defined in linux/types.h. Is there something else we are using from linux/list.h in this file?
include/linux/counter.h line 13
Quoted 2 lines
include/linux/counter.h +0
-0#include <linux/types.h>
It would be helpful to have kernel doc comments on everything in this file.
_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Re: [PATCH v5 1/5] counter: Internalize sysfs interface code
Quoted 55 lines · David Lechner
Quoted 1 line · William Breathitt Gray
This is a reimplementation of the Generic Counter driver interface.
I'll follow up if I find any problems while testing but here are some comments I had from looking over the patch.
Quoted 49 lines
drivers/counter/counter-core.c new file +42
-0+/** +* counter_register - register Counter to the system +* @counter: pointer to Counter to register +* +* This function registers a Counter to the system. A sysfs "counter" directory +* will be created and populated with sysfs attributes correlating with the +* Counter Signals, Synapses, and Counts respectively. +*/ +int counter_register (struct counter_device *const counter) +{ +struct device *const dev = &counter->dev; +int err; + +/* Acquire unique ID */ + counter->id =ida_simple_get (&counter_ida,0 ,0 ,GFP_KERNEL ); +if (counter->id <0 ) +return counter->id; + +/* Configure device structure for Counter */ + dev->type = &counter_device_type; + dev->bus = &counter_bus_type; +if (counter->parent) { + dev->parent = counter->parent; + dev->of_node = counter->parent->of_node; + } +dev_set_name (dev,"counter%d" , counter->id); + device_initialize(dev);> + dev_set_drvdata(dev, counter); + +/* Add Counter sysfs attributes */ + err =counter_sysfs_add (counter); +if (err) + goto err_free_id; + +/* Add device to system */ + err =device_add (dev); +if (err) { +put_device (dev); + goto err_free_id; + } + +return 0 ; +
Quoted 8 lines
drivers/counter/counter-core.c line 81
Quoted 4 lines
drivers/counter/counter-core.c new file +4
-0+err_free_id: +/* get_device/put_device combo used to free managed resources */ +get_device (dev); +put_device (dev);I've never seen this in a driver before, so it makes me think this is not the "right way" to do this. After
device_initialize()is called, we already should have a reference to dev, so onlydevice_put()is needed.
I do admit this feels very hacky, but I'm not sure how to handle this more elegantly. The problem is that device_initialize() is not enough by itself -- it just initializes the structure, while device_add() increments the reference count via a call to get_device().
So let's assume counter_sysfs_add() fails and we go to err_free_id before device_add() is called; if we ignore get_device(), the reference count will be 0 at this point. We then call put_device(), which calls kobject_put(), kref_put(), and eventually refcount_dec_and_test().
The refcount_dec_and_test() function returns 0 at this point because the reference count can't be decremented further (refcount is already 0), so release() is never called and we end up leaking all the memory we allocated in counter_sysfs_add().
Is my logic correct here?
Quoted 5 lines
drivers/counter/counter-core.c
Quoted 1 line
drivers/counter/counter-core.c new file +1
-0+ida_simple_remove (&counter_ida, counter->id);In the case of error after
device_initialize()is called, won't this result inida_simple_remove()being called twice, once here and once in the release callback?
I hadn't considered that. I suppose the ida_simple_remove here is not necessary because we will always call ida_simple_remove() in counter_device_release() when we call put_device(). I'll remove this ida_simple_remove() from counter_register() then.
Quoted 14 lines
Quoted 14 lines
drivers/counter/counter-core.c new file +14
-0+return err; +} +EXPORT_SYMBOL_GPL (counter_register); + +/** +* counter_unregister - unregister Counter from the system +* @counter: pointer to Counter to unregister +* +* The Counter is unregistered from the system; all allocated memory is freed. +*/ +void counter_unregister (struct counter_device *const counter) +{ +if (!counter) +return ;
Quoted 8 lines
drivers/counter/counter-core.c line 102
Quoted 6 lines
drivers/counter/counter-core.c new file +6
-0+ +device_unregister (&counter->dev); +} +EXPORT_SYMBOL_GPL (counter_unregister); + +static void devm_counter_unreg (struct device *dev,void *res)To be consistent, it would be nice to spell out unregister.
Ack.
Quoted 20 lines
Quoted 20 lines
drivers/counter/counter-core.c new file +4
-0+{ +counter_unregister (*(struct counter_device **)res); +} +drivers/counter/counter-sysfs.c new file +9
-0+/** +* counter_sysfs_add - Adds Counter sysfs attributes to the device structure +* @counter: Pointer to the Counter device structure +* +* Counter sysfs attributes are created and added to the respective device +* structure for later registration to the system. Resource-managed memory +* allocation is performed by this function, and this memory should be freed +* when no longer needed (automatically by a device_unregister call, or +* manually by a devres_release_all call).
Quoted 8 lines
drivers/counter/counter-sysfs.c line 722
Quoted 6 lines
+ */
drivers/counter/counter-sysfs.c new file +5
-0+int counter_sysfs_add (struct counter_device *const counter) +{ +struct device *const dev = &counter->dev; +const size_t num_groups = counter->num_signals + counter->num_counts + +1 ;It is OK to go past 80 columns, especially for just for a few characters.
Ack.
Quoted 14 lines
Quoted 14 lines
drivers/counter/counter-sysfs.c new file +14
-0+struct counter_attribute_group *groups; +size_t i, j; +int err; +struct attribute_group *group; +struct counter_attribute *p; + +/* Allocate space for attribute groups (signals, counts, and ext) */ + groups =devm_kcalloc (dev, num_groups,sizeof (*groups),GFP_KERNEL ); +if (!groups) +return -ENOMEM ; + +/* Initialize attribute lists */ +for (i =0 ; i < num_groups; i++) +INIT_LIST_HEAD (&groups[i].attr_list);
Quoted 8 lines
drivers/counter/counter-sysfs.c line 739
Quoted 3 lines
drivers/counter/counter-sysfs.c new file +3
-0+ +/* Register Counter device attributes */ + err =counter_device_register (counter, groups);This function name is a bit misleading. At first I though we were registering a new counter device (struct device). Maybe
counter_sysfs_create_attrs()would be a better name? (I wouldn't mind having all functions in this file having a "counter_sysfs_" prefix for that matter.)
Good point, I'll rename these to make it clearer.
Quoted 8 lines
Quoted 8 lines
drivers/counter/ti-eqep.c +0
-0@@ -406,7 +414,7 @@ static int ti_eqep_probe(struct platform_device *pdev)
Quoted 9 lines
drivers/counter/ti-eqep.c static int ti_eqep_probe(struct platform_device *pdev) · line 420
Quoted 7 lines
drivers/counter/ti-eqep.c +1
-1priv->counter.name =dev_name (dev); priv->counter.parent = dev; - priv->counter.ops = &ti_eqep_counter_ops; + priv->counter.parent = &ti_eqep_counter_ops; priv->counter.counts = ti_eqep_counts; priv->counter.num_counts =ARRAY_SIZE (ti_eqep_counts); priv->counter.signals = ti_eqep_signals;This looks like an unintentional change and causes a compile error.
Yeah, it was an unintentional change. I'll fix this. :-)
Quoted 8 lines
Quoted 8 lines
include/linux/counter.h +0
-0@@ -6,417 +6,195 @@#ifndef _COUNTER_H_#define _COUNTER_H_
Quoted 7 lines
include/linux/counter.h line 11
Quoted 4 lines
include/linux/counter.h +2
-1-#include <linux/counter_enum.h> #include <linux/device.h> +#include <linux/kernel.h> +#include <linux/list.h> struct
list_headis defined inlinux/types.h. Is there something else we are using fromlinux/list.hin this file?
I think this was left over from when I had list_add() in this file in an earlier version; I'll remove this header now since it doesn't look necessary.
Quoted 5 lines
include/linux/counter.h line 13
Quoted 2 lines
include/linux/counter.h +0
-0#include <linux/types.h> It would be helpful to have kernel doc comments on everything in this file.
Ack.
William Breathitt Gray
David Lechner
reviewerRe: [PATCH v5 1/5] counter: Internalize sysfs interface code
Quoted 65 lines · William Breathitt Gray
Quoted 55 lines · David Lechner
Quoted 1 line · William Breathitt Gray
This is a reimplementation of the Generic Counter driver interface.
I'll follow up if I find any problems while testing but here are some comments I had from looking over the patch.
Quoted 49 lines
drivers/counter/counter-core.c new file +42
-0+/** +* counter_register - register Counter to the system +* @counter: pointer to Counter to register +* +* This function registers a Counter to the system. A sysfs "counter" directory +* will be created and populated with sysfs attributes correlating with the +* Counter Signals, Synapses, and Counts respectively. +*/ +int counter_register (struct counter_device *const counter) +{ +struct device *const dev = &counter->dev; +int err; + +/* Acquire unique ID */ + counter->id =ida_simple_get (&counter_ida,0 ,0 ,GFP_KERNEL ); +if (counter->id <0 ) +return counter->id; + +/* Configure device structure for Counter */ + dev->type = &counter_device_type; + dev->bus = &counter_bus_type; +if (counter->parent) { + dev->parent = counter->parent; + dev->of_node = counter->parent->of_node; + } +dev_set_name (dev,"counter%d" , counter->id); + device_initialize(dev);> + dev_set_drvdata(dev, counter); + +/* Add Counter sysfs attributes */ + err =counter_sysfs_add (counter); +if (err) + goto err_free_id; + +/* Add device to system */ + err =device_add (dev); +if (err) { +put_device (dev); + goto err_free_id; + } + +return 0 ; +Quoted 8 lines
drivers/counter/counter-core.c line 81
Quoted 4 lines
drivers/counter/counter-core.c new file +4
-0+err_free_id: +/* get_device/put_device combo used to free managed resources */ +get_device (dev); +put_device (dev);I've never seen this in a driver before, so it makes me think this is not the "right way" to do this. After
device_initialize()is called, we already should have a reference to dev, so onlydevice_put()is needed.
Quoted 4 lines
I do admit this feels very hacky, but I'm not sure how to handle this more elegantly. The problem is that
device_initialize()is not enough by itself -- it just initializes the structure, whiledevice_add()increments the reference count via a call toget_device().
add_device() also releases this reference by calling put_device() in all return paths. The reference count is initialized to 1 in device_initialize().
device_initialize > kobject_init > kobject_init_internal > kref_init
Quoted 6 lines
So let's assume
counter_sysfs_add()fails and we go toerr_free_idbeforedevice_add()is called; if we ignoreget_device(), the reference count will be 0 at this point. We then callput_device(), which callskobject_put(),kref_put(), and eventuallyrefcount_dec_and_test().
Quoted 6 lines
The
refcount_dec_and_test()function returns 0 at this point because the reference count can't be decremented further (refcount is already 0), sorelease()is never called and we end up leaking all the memory we allocated incounter_sysfs_add().Is my logic correct here?
Not quite. I think you missed a few things I mentioned above. So we never have a ref count of 0 until the very last call to put_device().
Re: [PATCH v5 1/5] counter: Internalize sysfs interface code
Quoted 66 lines · David Lechner
Quoted 60 lines · William Breathitt Gray
Quoted 50 lines · David Lechner
Quoted 49 lines · William Breathitt Gray
drivers/counter/counter-core.c new file +42
-0+/** +* counter_register - register Counter to the system +* @counter: pointer to Counter to register +* +* This function registers a Counter to the system. A sysfs "counter" directory +* will be created and populated with sysfs attributes correlating with the +* Counter Signals, Synapses, and Counts respectively. +*/ +int counter_register (struct counter_device *const counter) +{ +struct device *const dev = &counter->dev; +int err; + +/* Acquire unique ID */ + counter->id =ida_simple_get (&counter_ida,0 ,0 ,GFP_KERNEL ); +if (counter->id <0 ) +return counter->id; + +/* Configure device structure for Counter */ + dev->type = &counter_device_type; + dev->bus = &counter_bus_type; +if (counter->parent) { + dev->parent = counter->parent; + dev->of_node = counter->parent->of_node; + } +dev_set_name (dev,"counter%d" , counter->id); + device_initialize(dev);> + dev_set_drvdata(dev, counter); + +/* Add Counter sysfs attributes */ + err =counter_sysfs_add (counter); +if (err) + goto err_free_id; + +/* Add device to system */ + err =device_add (dev); +if (err) { +put_device (dev); + goto err_free_id; + } + +return 0 ; +Quoted 8 lines
drivers/counter/counter-core.c line 81
Quoted 4 lines
drivers/counter/counter-core.c new file +4
-0+err_free_id: +/* get_device/put_device combo used to free managed resources */ +get_device (dev); +put_device (dev);I've never seen this in a driver before, so it makes me think this is not the "right way" to do this. After
device_initialize()is called, we already should have a reference to dev, so onlydevice_put()is needed.Quoted 4 lines
I do admit this feels very hacky, but I'm not sure how to handle this more elegantly. The problem is that
device_initialize()is not enough by itself -- it just initializes the structure, whiledevice_add()increments the reference count via a call toget_device().
Quoted 4 lines
add_device()also releases this reference by callingput_device()in all return paths. The reference count is initialized to 1 indevice_initialize().
device_initialize>kobject_init>kobject_init_internal>kref_init
You're right, I completely overlooked this: kref_init() initializes the reference count to 1. Therefore, the get_device() call is incorrect and should be be removed.
Quoted 11 lines
Quoted 11 lines
So let's assume
counter_sysfs_add()fails and we go toerr_free_idbeforedevice_add()is called; if we ignoreget_device(), the reference count will be 0 at this point. We then callput_device(), which callskobject_put(),kref_put(), and eventuallyrefcount_dec_and_test().The
refcount_dec_and_test()function returns 0 at this point because the reference count can't be decremented further (refcount is already 0), sorelease()is never called and we end up leaking all the memory we allocated incounter_sysfs_add().
Quoted 4 lines
Quoted 1 line
Is my logic correct here?
Not quite. I think you missed a few things I mentioned above. So we never have a ref count of 0 until the very last call to
put_device().
Ack.
William Breathitt Gray
David Lechner
reviewerRe: [PATCH v5 1/5] counter: Internalize sysfs interface code
In reply to William Breathitt GrayQuoted 31 lines · William Breathitt Gray
drivers/counter/counter-sysfs.c new file +31
-0+static ssize_t counter_comp_u8_store (struct device *dev, +struct device_attribute *attr, +const char *buf,size_t len) +{ +const struct counter_attribute *const a =to_counter_attribute (attr); +struct counter_device *const counter =dev_get_drvdata (dev); +struct counter_count *const count = a->parent; +struct counter_synapse *const synapse = a->comp.priv; +const struct counter_available *const avail = a->comp.priv; +int err; +bool bool_data; +u8 data; + +switch (a->comp.type) { +case COUNTER_COMP_BOOL : + err =kstrtobool (buf, &bool_data); + data = bool_data; +break ; +case COUNTER_COMP_FUNCTION : + err =find_in_string_array (&data, count->functions_list, + count->num_functions, buf, + counter_function_str); +break ; +case COUNTER_COMP_SYNAPSE_ACTION : + err =find_in_string_array (&data, synapse->actions_list, + synapse->num_actions, buf, + counter_synapse_action_str); +break ; +case COUNTER_COMP_ENUM : + err =__sysfs_match_string (avail->strs, avail->num_items, buf); + data = err;
drivers/counter/counter-sysfs.c line 189
Quoted 10 lines
drivers/counter/counter-sysfs.c new file +10
-0+break ; +case COUNTER_COMP_COUNT_MODE : + err =find_in_string_array (&data, avail->enums, + avail->num_items, buf, + counter_count_mode_str); +break ; +default : + err =kstrtou8 (buf,0 , &data); +break ; + }
In this function, return values are not always errors. So it would make sense to call the err variable ret instead and check for ret < 0 below.
Setting enums to a value with index > 0 fails currently because of this.
Quoted 23 lines
drivers/counter/counter-sysfs.c new file +23
-0+if (err) +return err; + +switch (a->scope) { +case COUNTER_SCOPE_DEVICE : + err = a->comp.device_u8_write (counter, data); +break ; +case COUNTER_SCOPE_SIGNAL : + err = a->comp.signal_u8_write (counter, a->parent, data); +break ; +case COUNTER_SCOPE_COUNT : +if (a->comp.type ==COUNTER_COMP_SYNAPSE_ACTION ) + err = a->comp.action_write (counter, count, synapse, + data); +else + err = a->comp.count_u8_write (counter, count, data); +break ; + } +if (err) +return err; + +return len; +}
Re: [PATCH v5 1/5] counter: Internalize sysfs interface code
Quoted 46 lines · David Lechner
Quoted 31 lines · William Breathitt Gray
drivers/counter/counter-sysfs.c new file +31
-0+static ssize_t counter_comp_u8_store (struct device *dev, +struct device_attribute *attr, +const char *buf,size_t len) +{ +const struct counter_attribute *const a =to_counter_attribute (attr); +struct counter_device *const counter =dev_get_drvdata (dev); +struct counter_count *const count = a->parent; +struct counter_synapse *const synapse = a->comp.priv; +const struct counter_available *const avail = a->comp.priv; +int err; +bool bool_data; +u8 data; + +switch (a->comp.type) { +case COUNTER_COMP_BOOL : + err =kstrtobool (buf, &bool_data); + data = bool_data; +break ; +case COUNTER_COMP_FUNCTION : + err =find_in_string_array (&data, count->functions_list, + count->num_functions, buf, + counter_function_str); +break ; +case COUNTER_COMP_SYNAPSE_ACTION : + err =find_in_string_array (&data, synapse->actions_list, + synapse->num_actions, buf, + counter_synapse_action_str); +break ; +case COUNTER_COMP_ENUM : + err =__sysfs_match_string (avail->strs, avail->num_items, buf); + data = err;drivers/counter/counter-sysfs.c line 189
Quoted 10 lines
drivers/counter/counter-sysfs.c new file +10
-0+break ; +case COUNTER_COMP_COUNT_MODE : + err =find_in_string_array (&data, avail->enums, + avail->num_items, buf, + counter_count_mode_str); +break ; +default : + err =kstrtou8 (buf,0 , &data); +break ; + }In this function, return values are not always errors. So it would make sense to call the err variable ret instead and check for ret < 0 below.
Quoted 1 line
Setting enums to a value with index > 0 fails currently because of this.
Thank you for pointing this out, I'll fix this.
William Breathitt Gray
Quoted 24 lines
Quoted 23 lines
drivers/counter/counter-sysfs.c new file +23
-0+if (err) +return err; + +switch (a->scope) { +case COUNTER_SCOPE_DEVICE : + err = a->comp.device_u8_write (counter, data); +break ; +case COUNTER_SCOPE_SIGNAL : + err = a->comp.signal_u8_write (counter, a->parent, data); +break ; +case COUNTER_SCOPE_COUNT : +if (a->comp.type ==COUNTER_COMP_SYNAPSE_ACTION ) + err = a->comp.action_write (counter, count, synapse, + data); +else + err = a->comp.count_u8_write (counter, count, data); +break ; + } +if (err) +return err; + +return len; +}
PATCH v5 2/5 docs: counter: Update to reflect sysfs internalization
In reply to William Breathitt GrayThe Counter subsystem architecture and driver implementations have changed in order to handle Counter sysfs interactions in a more consistent way. This patch updates the Generic Counter interface documentation to reflect the changes.
--- Documentation/driver-api/generic-counter.rst | 242 ++++++++++++++----- 1 file changed, 176 insertions(+), 66 deletions(-)
Documentation/driver-api/generic-counter.rst +176 -66
@@ -250,8 +250,8 @@ for defining a counter device. .. kernel-doc:: drivers/counter/counter.c :export: -Implementation -============== +Driver Implementation +===================== To support a counter device, a driver must first allocate the available Counter Signals via counter_signal structures. These Signals should
@@ -267,25 +267,59 @@ respective counter_count structure. These counter_count structures are set to the counts array member of an allocated counter_device structure before the Counter is registered to the system. -Driver callbacks should be provided to the counter_device structure via -a constant counter_ops structure in order to communicate with the -device: to read and write various Signals and Counts, and to set and get -the "action mode" and "function mode" for various Synapses and Counts -respectively. +Driver callbacks must be provided to the counter_device structure in +order to communicate with the device: to read and write various Signals +and Counts, and to set and get the "action mode" and "function mode" for +various Synapses and Counts respectively. A defined counter_device structure may be registered to the system by passing it to the counter_register function, and unregistered by passing it to the counter_unregister function. Similarly, the -devm_counter_register and devm_counter_unregister functions may be used -if device memory-managed registration is desired. - -Extension sysfs attributes can be created for auxiliary functionality -and data by passing in defined counter_device_ext, counter_count_ext, -and counter_signal_ext structures. In these cases, the -counter_device_ext structure is used for global/miscellaneous exposure -and configuration of the respective Counter device, while the -counter_count_ext and counter_signal_ext structures allow for auxiliary -exposure and configuration of a specific Count or Signal respectively. +devm_counter_register function may be used if device memory-managed +registration is desired. + +The struct counter_comp structure is used to define counter extensions +for Signals, Synapses, and Counts. + +The "type" member specifies the type of high-level data (e.g. BOOL, +COUNT_DIRECTION, etc.) handled by this extension. The "`*_read`" and +"`*_write`" members can then be set by the counter device driver with +callbacks to handle that data using native C data types (i.e. u8, u64, +etc.). + +Convenience macros such as `COUNTER_COMP_COUNT_U64` are provided for use +by driver authors. In particular, driver authors are expected to use +the provided macros for standard Counter subsystem attributes in order +to maintain a consistent interface for userspace. For example, a counter +device driver may define several standard attributes like so:: + + struct counter_comp count_ext[] = { + COUNTER_COMP_DIRECTION(count_direction_read), + COUNTER_COMP_ENABLE(count_enable_read, count_enable_write), + COUNTER_COMP_CEILING(count_ceiling_read, count_ceiling_write), + }; + +This makes it simple to see, add, and modify the attributes that are +supported by this driver ("direction", "enable", and "ceiling") and to +maintain this code without getting lost in a web of struct braces. + +Callbacks must match the function type expected for the respective +component or extension. These function types are defined in the struct +counter_comp structure as the "`*_read`" and "`*_write`" union members. + +The corresponding callback prototypes for the extensions mentioned in +the previous example above would be:: + + int count_direction_read(struct counter_device *counter, + struct counter_count *count, u8 *direction); + int count_enable_read(struct counter_device *counter, + struct counter_count *count, u8 *enable); + int count_enable_write(struct counter_device *counter, + struct counter_count *count, u8 enable); + int count_ceiling_read(struct counter_device *counter, + struct counter_count *count, u64 *ceiling); + int count_ceiling_write(struct counter_device *counter, + struct counter_count *count, u64 ceiling); Determining the type of extension to create is a matter of scope.
@@ -313,52 +347,128 @@ Determining the type of extension to create is a matter of scope. chip overheated via a device extension called "error_overtemp": /sys/bus/counter/devices/counterX/error_overtemp -Architecture -============ - -When the Generic Counter interface counter module is loaded, the -counter_init function is called which registers a bus_type named -"counter" to the system. Subsequently, when the module is unloaded, the -counter_exit function is called which unregisters the bus_type named -"counter" from the system. - -Counter devices are registered to the system via the counter_register -function, and later removed via the counter_unregister function. The -counter_register function establishes a unique ID for the Counter -device and creates a respective sysfs directory, where X is the -mentioned unique ID: - - /sys/bus/counter/devices/counterX - -Sysfs attributes are created within the counterX directory to expose -functionality, configurations, and data relating to the Counts, Signals, -and Synapses of the Counter device, as well as options and information -for the Counter device itself. - -Each Signal has a directory created to house its relevant sysfs -attributes, where Y is the unique ID of the respective Signal: - - /sys/bus/counter/devices/counterX/signalY - -Similarly, each Count has a directory created to house its relevant -sysfs attributes, where Y is the unique ID of the respective Count: - - /sys/bus/counter/devices/counterX/countY - -For a more detailed breakdown of the available Generic Counter interface -sysfs attributes, please refer to the -Documentation/ABI/testing/sysfs-bus-counter file. - -The Signals and Counts associated with the Counter device are registered -to the system as well by the counter_register function. The -signal_read/signal_write driver callbacks are associated with their -respective Signal attributes, while the count_read/count_write and -function_get/function_set driver callbacks are associated with their -respective Count attributes; similarly, the same is true for the -action_get/action_set driver callbacks and their respective Synapse -attributes. If a driver callback is left undefined, then the respective -read/write permission is left disabled for the relevant attributes. - -Similarly, extension sysfs attributes are created for the defined -counter_device_ext, counter_count_ext, and counter_signal_ext -structures that are passed in. +Subsystem Architecture +====================== + +Counter drivers pass and take data natively (i.e. `u8`, `u64`, etc.) and +the shared counter module handles the translation between the sysfs +interface. This gurantees a standard userspace interface for all counter +drivers, and helps generalize the Generic Counter driver ABI in order to +support the Generic Counter chrdev interface without significant changes +to the existing counter drivers. + +A high-level view of how a count value is passed down from a counter +driver is exemplified by the following. The driver callbacks are first +registered to the Counter core component for use by the Counter +userspace interface components:: + + Driver callbacks registration: + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +----------------------------+ + | Counter device driver | + +----------------------------+ + | Processes data from device | + +----------------------------+ + | + ------------------- + / driver callbacks / + ------------------- + | + V + +----------------------+ + | Counter core | + +----------------------+ + | Routes device driver | + | callbacks to the | + | userspace interfaces | + +----------------------+ + | + ------------------- + / driver callbacks / + ------------------- + | + +---------------+ + | + V + +--------------------+ + | Counter sysfs | + +--------------------+ + | Translates to the | + | standard Counter | + | sysfs output | + +--------------------+ + +Thereafter, data can be transferred directly between the Counter device +driver and Counter userspace interface:: + + Count data request: + ~~~~~~~~~~~~~~~~~~~ + ---------------------- + / Counter device \ + +----------------------+ + | Count register: 0x28 | + +----------------------+ + | + ----------------- + / raw count data / + ----------------- + | + V + +----------------------------+ + | Counter device driver | + +----------------------------+ + | Processes data from device | + |----------------------------| + | Type: u64 | + | Value: 42 | + +----------------------------+ + | + ---------- + / u64 / + ---------- + | + +---------------+ + | + V + +--------------------+ + | Counter sysfs | + +--------------------+ + | Translates to the | + | standard Counter | + | sysfs output | + |--------------------| + | Type: const char * | + | Value: "42" | + +--------------------+ + | + --------------- + / const char * / + --------------- + | + V + +--------------------------------------------------+ + | `/sys/bus/counter/devices/counterX/countY/count` | + +--------------------------------------------------+ + \ Count: "42" / + -------------------------------------------------- + +There are three primary components involved: + +Counter device driver +--------------------- +Communicates with the hardware device to read/write data; e.g. counter +drivers for quadrature encoders, timers, etc. + +Counter core +------------ +Registers the counter device driver to the system so that the respective +callbacks are called during userspace interaction. + +Counter sysfs +------------- +Translates counter data to the standard Counter sysfs interface format +and vice versa. + +Please refer to the `Documentation/ABI/testing/sysfs-bus-counter` file +for a detailed breakdown of the available Generic Counter interface +sysfs attributes.
Signature · 1 line
2.28.0
PATCH v5 3/5 counter: Add character device interface
In reply to William Breathitt GrayThis patch introduces a character device interface for the Counter subsystem. Device data is exposed through standard character device read operations. Device data is gathered when a Counter event is pushed by the respective Counter device driver. Configuration is handled via ioctl operations on the respective Counter character device node.
A high-level view of how a count value is passed down from a counter driver is exemplified by the following. The driver callbacks are first registered to the Counter core component for use by the Counter userspace interface components:
+----------------------------+
| Counter device driver |
+----------------------------+
| Processes data from device |
+----------------------------+
|
-------------------
/ driver callbacks /
-------------------
|
V
+----------------------+
| Counter core |
+----------------------+
| Routes device driver |
| callbacks to the |
| userspace interfaces |
+----------------------+
|
-------------------
/ driver callbacks /
-------------------
|
+---------------+---------------+
| |
V V
+--------------------+ +---------------------+
| Counter sysfs | | Counter chrdev |
+--------------------+ +---------------------+
| Translates to the | | Translates to the |
| standard Counter | | standard Counter |
| sysfs output | | character device |
+--------------------+ +---------------------+Thereafter, data can be transferred directly between the Counter device driver and Counter userspace interface:
----------------------
/ Counter device \
+----------------------+
| Count register: 0x28 |
+----------------------+
|
-----------------
/ raw count data /
-----------------
|
V
+----------------------------+
| Counter device driver |
+----------------------------+
| Processes data from device |
|----------------------------|
| Type: u64 |
| Value: 42 |
+----------------------------+
|
----------
/ u64 /
----------
|
+---------------+---------------+
| |
V V
+--------------------+ +---------------------+
| Counter sysfs | | Counter chrdev |
+--------------------+ +---------------------+
| Translates to the | | Translates to the |
| standard Counter | | standard Counter |
| sysfs output | | character device |
|--------------------| |---------------------|
| Type: const char * | | Type: u64 |
| Value: "42" | | Value: 42 |
+--------------------+ +---------------------+
| |
--------------- -----------------------
/ const char * / / struct counter_event /
--------------- -----------------------
| |
| V
| +-----------+
| | read |
| +-----------+
| \ Count: 42 /
| -----------
|
V
+--------------------------------------------------+
| `/sys/bus/counter/devices/counterX/countY/count` |
+--------------------------------------------------+
\ Count: "42" /
--------------------------------------------------Counter character device nodes are created under the /dev directory as counterX, where X is the respective counter device id. Defines for the standard Counter data types are exposed via the userspace include/uapi/linux/counter.h file.
Counter events
--------------
Counter device drivers can support Counter events by utilizing the counter_push_event function:
int counter_push_event(struct counter_device *const counter,
const u8 event, const u8 channel);
The event id is specified by the event parameter; the event channel id is specified by the channel parameter. When this function is called, the Counter data associated with the respective event is gathered, and a struct counter_event is generated for each datum and pushed to userspace.
Counter events can be configured by users to report various Counter data of interest. This can be conceptualized as a list of Counter component read calls to perform. For example:
+~~~~~~~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~~~~~~+
| COUNTER_EVENT_OVERFLOW | COUNTER_EVENT_INDEX |
+~~~~~~~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~~~~~~+
| Channel 0 | Channel 0 |
+------------------------+------------------------+
| * Count 0 | * Signal 0 |
| * Count 1 | * Signal 0 Extension 0 |
| * Signal 3 | * Extension 4 |
| * Count 4 Extension 2 +------------------------+
| * Signal 5 Extension 0 | Channel 1 |
| +------------------------+
| | * Signal 4 |
| | * Signal 4 Extension 0 |
| | * Count 7 |
+------------------------+------------------------+
When counter_push_event(counter, COUNTER_EVENT_INDEX, 1) is called for example, it will go down the list for the COUNTER_EVENT_INDEX event channel 1 and execute the read callbacks for Signal 4, Signal 4 Extension 0, and Count 4 -- the data returned for each is pushed to a kfifo as a struct counter_event, which userspace can retrieve via a standard read operation on the respective character device node.
Userspace
---------
Userspace applications can configure Counter events via ioctl operations on the Counter character device node. There following ioctl codes are supported and provided by the linux/counter.h userspace header file:
* COUNTER_CLEAR_WATCHES_IOCTL:
Clear all Counter watches from all events
* COUNTER_SET_WATCH_IOCTL:
Set a Counter watch for the specified event
* COUNTER_LOAD_WATCHES_IOCTL:
Activates the Counter watches set earlier
To configure events to gather Counter data, users first populate a struct counter_watch with the relevant event id, event channel id, and the information for the desired Counter component from which to read, and then pass it via the COUNTER_SET_WATCH_IOCTL ioctl command.
The COUNTER_SET_WATCH_IOCTL command will buffer these Counter watches. When ready, the COUNTER_LOAD_WATCHES_IOCTL ioctl command may be used to activate these Counter watches.
Userspace applications can then execute a read operation (optionally calling poll first) on the Counter character device node to retrieve struct counter_event elements with the desired data.
For example, the following userspace code opens /dev/counter0, configures the COUNTER_EVENT_INDEX event channel 0 to gather Count 0 and Count 1, and prints out the data as it becomes available on the character device node:
#include <fcntl.h>
#include <linux/counter.h>
#include <poll.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>
struct counter_watch watches[2] = {
{
.event = COUNTER_EVENT_INDEX,
.channel = 0,
.component.scope = COUNTER_SCOPE_COUNT,
.component.parent = 0,
.component.type = COUNTER_COMPONENT_COUNT,
},
{
.event = COUNTER_EVENT_INDEX,
.channel = 0,
.component.scope = COUNTER_SCOPE_COUNT,
.component.parent = 1,
.component.type = COUNTER_COMPONENT_COUNT,
},
};
int main(void)
{
struct pollfd pfd = { .events = POLLIN };
struct counter_event event_data[2];
pfd.fd = open("/dev/counter0", O_RDWR);
ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches);
ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches + 1);
ioctl(pfd.fd, COUNTER_LOAD_WATCHES_IOCTL);
for (;;) {
poll(&pfd, 1, -1);
read(pfd.fd, event_data, sizeof(event_data));
printf("Timestamp 0: %llu\nCount 0: %llu\n"
"Timestamp 1: %llu\nCount 1: %llu\n",
(unsigned long long)event_data[0].timestamp,
(unsigned long long)event_data[0].value_u64,
(unsigned long long)event_data[1].timestamp,
(unsigned long long)event_data[1].value_u64);
}
return 0;
}--- MAINTAINERS | 1 + drivers/counter/Makefile | 2 +- drivers/counter/counter-chrdev.c | 451 +++++++++++++++++++++++++++++++ drivers/counter/counter-chrdev.h | 16 ++ drivers/counter/counter-core.c | 38 ++- drivers/counter/counter-sysfs.c | 109 +++++++- include/linux/counter.h | 48 ++-- include/uapi/linux/counter.h | 99 +++++++ 8 files changed, 720 insertions(+), 44 deletions(-) create mode 100644 drivers/counter/counter-chrdev.c create mode 100644 drivers/counter/counter-chrdev.h create mode 100644 include/uapi/linux/counter.h
MAINTAINERS +1 -0
@@ -4459,6 +4459,7 @@ F: Documentation/ABI/testing/sysfs-bus-counter* F: Documentation/driver-api/generic-counter.rst F: drivers/counter/ F: include/linux/counter.h +F: include/uapi/linux/counter.h CPMAC ETHERNET DRIVER M: Florian Fainelli <[email protected]>
drivers/counter/Makefile +1 -1
@@ -4,7 +4,7 @@ # obj-$(CONFIG_COUNTER) += counter.o -counter-y := counter-core.o counter-sysfs.o +counter-y := counter-core.o counter-sysfs.o counter-chrdev.o obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o obj-$(CONFIG_STM32_TIMER_CNT) += stm32-timer-cnt.o
drivers/counter/counter-chrdev.c new file +451 -0
451 lines not on this page · raw message
drivers/counter/counter-chrdev.h new file +16 -0
@@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* Counter character device interface +* Copyright (C) 2020 William Breathitt Gray +*/ +#ifndef _COUNTER_CHRDEV_H_ +#define _COUNTER_CHRDEV_H_ + +#include <linux/counter.h> +#include <linux/types.h> + +int counter_chrdev_add (struct counter_device *const counter, +const dev_t counter_devt); +void counter_chrdev_remove (struct counter_device *const counter); + +#endif /* _COUNTER_CHRDEV_H_ */
drivers/counter/counter-core.c +34 -4
@@ -5,12 +5,16 @@*/ #include <linux/counter.h> #include <linux/device.h> +#include <linux/device/bus.h> #include <linux/export.h> +#include <linux/fs.h> #include <linux/gfp.h> #include <linux/idr.h> #include <linux/init.h> #include <linux/module.h> +#include <linux/types.h> +#include "counter-chrdev.h" #include "counter-sysfs.h" /* Provides a unique ID for each counter device */
@@ -32,6 +36,8 @@ static struct bus_type counter_bus_type = { .name = "counter" }; +static dev_t counter_devt; +/** * counter_register - register Counter to the system * @counter: pointer to Counter to register
@@ -50,7 +56,6 @@ int counter_register(struct counter_device *const counter)if (counter->id <0 )return counter->id; -/* Configure device structure for Counter */ dev->type = &counter_device_type; dev->bus = &counter_bus_type;if (counter->parent) {
@@ -61,20 +66,27 @@ int counter_register(struct counter_device *const counter)device_initialize (dev);dev_set_drvdata (dev, counter); +/* Add Counter character device */ + err =counter_chrdev_add (counter, counter_devt); +if (err) + goto err_free_id; +/* Add Counter sysfs attributes */ err =counter_sysfs_add (counter);if (err) - goto err_free_id; + goto err_remove_chrdev;/* Add device to system */ err =device_add (dev);if (err) {put_device (dev); - goto err_free_id; + goto err_remove_chrdev; }return 0 ; +err_remove_chrdev: +counter_chrdev_remove (counter); err_free_id:/* get_device/put_device combo used to free managed resources */ get_device (dev);
@@ -96,6 +108,7 @@ void counter_unregister(struct counter_device *const counter)return ;device_unregister (&counter->dev); +counter_chrdev_remove (counter); }EXPORT_SYMBOL_GPL (counter_unregister);
@@ -142,13 +155,30 @@ int devm_counter_register(struct device *dev, }EXPORT_SYMBOL_GPL (devm_counter_register); +#define COUNTER_DEV_MAX 256 +static int __init counter_init (void ) { -return bus_register (&counter_bus_type); +int err; + + err =bus_register (&counter_bus_type); +if (err <0 ) +return err; + + err =alloc_chrdev_region (&counter_devt,0 ,COUNTER_DEV_MAX ,"counter" ); +if (err <0 ) + goto err_unregister_bus; + +return 0 ; + +err_unregister_bus: +bus_unregister (&counter_bus_type); +return err; } static void __exit counter_exit(void) { +unregister_chrdev_region (counter_devt,COUNTER_DEV_MAX );bus_unregister (&counter_bus_type); }
drivers/counter/counter-sysfs.c +99 -10
180 lines not on this page · raw message
include/linux/counter.h +19 -29
82 lines not on this page · raw message
include/uapi/linux/counter.h new file +99 -0
99 lines not on this page · raw message
Signature · 1 line
2.28.0
David Lechner
reviewerRe: [PATCH v5 3/5] counter: Add character device interface
Quoted 6 lines · William Breathitt Gray
This patch introduces a character device interface for the Counter subsystem. Device data is exposed through standard character device read operations. Device data is gathered when a Counter event is pushed by the respective Counter device driver. Configuration is handled via ioctl operations on the respective Counter character device node.
Probably don't need to duplicate the full documentation in the commit message.
Quoted 31 lines
drivers/counter/counter-chrdev.c new file +24
-0+ +static int counter_set_event_node (struct counter_device *const counter, +struct counter_watch *const watch, +const struct counter_comp_node *const cfg) +{ +struct counter_event_node *event_node; +int err; +struct counter_comp_node *comp_node; + +/* Search for event in the list */ +list_for_each_entry (event_node, &counter->next_events_list, l) +if (event_node->event == watch->event && + event_node->channel == watch->channel) +break ; + +/* If event is not already in the list */ +if (&event_node->l == &counter->next_events_list) { +/* Allocate new event node */ + event_node =kmalloc (sizeof (*event_node),GFP_ATOMIC ); +if (!event_node) +return -ENOMEM ; + +/* Configure event node and add to the list */ + event_node->event = watch->event;
drivers/counter/counter-chrdev.c line 137
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+ event_node->channel = watch->channel; +INIT_LIST_HEAD (&event_node->comp_list); +list_add (&event_node->l, &counter->next_events_list); + } + +/* Check if component watch has already been set before */ +list_for_each_entry (comp_node, &event_node->comp_list, l) +if (comp_node->parent == cfg->parent && + comp_node->comp.count_u8_read == cfg->comp.count_u8_read) +return -EINVAL ;
There are already enough things that could cause EINVAL, we could probably skip this duplicate check to make troubleshooting easier.
drivers/counter/counter-chrdev.c line 143
Quoted 6 lines
drivers/counter/counter-chrdev.c new file +6
-0+ +/* Allocate component node */ + comp_node =kmalloc (sizeof (*comp_node),GFP_ATOMIC ); +if (!comp_node) { + err = -ENOMEM ; + goto err_comp_node;
Since there is only one goto, we could just handle the error and return here instead.
drivers/counter/counter-chrdev.c line 152
Quoted 9 lines
drivers/counter/counter-chrdev.c new file +9
-0+ } + *comp_node = *cfg; + +/* Add component node to event node */ +list_add_tail (&comp_node->l, &event_node->comp_list); + +return 0 ; + +err_comp_node:
A comment explaining the list_empty() check would be nice. It makes sense if you think about it, but it is not super obvious.
Quoted 22 lines
drivers/counter/counter-chrdev.c new file +22
-0+if (list_empty (&event_node->comp_list)) { +list_del (&event_node->l); +kfree (event_node); + } +return err; +} + +static int counter_set_watch (struct counter_device *const counter, +const unsigned long arg) +{ +void __user *const uwatch = (void __user *)arg; +struct counter_watch watch; +struct counter_comp_node comp_node; +size_t parent, id; +struct counter_comp *ext; +size_t num_ext; + +if (copy_from_user (&watch, uwatch,sizeof (watch))) +return -EFAULT ; + parent = watch.component.parent; + id = watch.component.id; +
drivers/counter/counter-chrdev.c line 184
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+/* Configure parent component info for comp node */ +switch (watch.component.scope) { +case COUNTER_SCOPE_DEVICE : + comp_node.parent =NULL ; + + ext = counter->ext; + num_ext = counter->num_ext; +break ; +case COUNTER_SCOPE_SIGNAL : +if (counter->num_signals < parent +1 )
I think it would be more conventional this way:
if (parent >= counter->num_signals)
drivers/counter/counter-chrdev.c line 193
Quoted 9 lines
drivers/counter/counter-chrdev.c new file +9
-0+return -EINVAL ; + + comp_node.parent = counter->signals + parent; + + ext = counter->signals[parent].ext; + num_ext = counter->signals[parent].num_ext; +break ; +case COUNTER_SCOPE_COUNT : +if (counter->num_counts < parent +1 )
Same here.
Quoted 28 lines
drivers/counter/counter-chrdev.c new file +28
-0+return -EINVAL ; + + comp_node.parent = counter->counts + parent; + + ext = counter->counts[parent].ext; + num_ext = counter->counts[parent].num_ext; +break ; +default : +return -EINVAL ; + } + +/* Configure component info for comp node */ +switch (watch.component.type) { +case COUNTER_COMPONENT_SIGNAL : +if (watch.component.scope !=COUNTER_SCOPE_SIGNAL ) +return -EINVAL ; + + comp_node.comp.type =COUNTER_COMP_SIGNAL_LEVEL ; + comp_node.comp.signal_u8_read = counter->ops->signal_read; +break ; +case COUNTER_COMPONENT_COUNT : +if (watch.component.scope !=COUNTER_SCOPE_COUNT ) +return -EINVAL ; + + comp_node.comp.type =COUNTER_COMP_U64 ; + comp_node.comp.count_u64_read = counter->ops->count_read; +break ; +case COUNTER_COMPONENT_FUNCTION :
drivers/counter/counter-chrdev.c line 231
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+if (watch.component.scope !=COUNTER_SCOPE_COUNT ) +return -EINVAL ; + + comp_node.comp.type =COUNTER_COMP_FUNCTION ; + comp_node.comp.count_u8_read = counter->ops->function_read; +break ; +case COUNTER_COMPONENT_SYNAPSE_ACTION : +if (watch.component.scope !=COUNTER_SCOPE_COUNT ) +return -EINVAL ; +if (counter->counts[parent].num_synapses < id +1 )
if (id >= counter->counts[parent].num_synapses)
drivers/counter/counter-chrdev.c line 239
Quoted 8 lines
drivers/counter/counter-chrdev.c new file +8
-0+return -EINVAL ; + + comp_node.comp.type =COUNTER_COMP_SYNAPSE_ACTION ; + comp_node.comp.action_read = counter->ops->action_read; + comp_node.comp.priv = counter->counts[parent].synapses + id; +break ; +case COUNTER_COMPONENT_EXTENSION : +if (num_ext < id +1 )
if (id >= num_ext)
drivers/counter/counter-chrdev.c line 247
Quoted 8 lines
drivers/counter/counter-chrdev.c new file +8
-0+return -EINVAL ; + + comp_node.comp = ext[id]; +break ; +default : +return -EINVAL ; + } +if (!comp_node.comp.count_u8_read)
A comment reminding us that count_u8_read is part of a union would be helpful here since this is checking that any read function was set, not just count_u8_read.
drivers/counter/counter-chrdev.c
Quoted 1 line
drivers/counter/counter-chrdev.c new file +1
-0+return -EFAULT ;
Would EOPNOTSUPP be a better error choice?
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+ comp_node.component = watch.component; + +return counter_set_event_node (counter, &watch, &comp_node); +} + +static long counter_chrdev_ioctl (struct file *filp,unsigned int cmd, +unsigned long arg) +{ +struct counter_device *const counter = filp->private_data; +raw_spinlock_t *const events_lock = &counter->events_lock;
drivers/counter/counter-chrdev.c line 268
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+unsigned long flags; +struct list_head *const events_list = &counter->events_list; +struct list_head *const next_events_list = &counter->next_events_list; + +switch (cmd) { +case COUNTER_CLEAR_WATCHES_IOCTL : +raw_spin_lock_irqsave (events_lock, flags); +counter_events_list_free (events_list); +raw_spin_unlock_irqrestore (events_lock, flags); +counter_events_list_free (next_events_list);
I think this ioctl is doing too much. If we have to use it for both stopping events and clearing the list accumulated by COUNTER_SET_WATCH_IOCTL, then we have a race condition of no events after clearing watches during the time we are adding new ones and until we load the new ones.
It would probably make more sense to call this ioctl COUNTER_STOP_WATCHES_IOCTL and move counter_events_list_free( next_events_list) to the end of COUNTER_LOAD_WATCHES_IOCTL.
drivers/counter/counter-chrdev.c line 271
Quoted 3 lines
drivers/counter/counter-chrdev.c new file +3
-0+break ; +case COUNTER_SET_WATCH_IOCTL : +return counter_set_watch (counter, arg);
Would it make more sense to call this COUNTER_ADD_WATCH_IOCTL since it adds a watch to the list that will be loaded/enabled later?
drivers/counter/counter-chrdev.c line 276
Quoted 5 lines
drivers/counter/counter-chrdev.c new file +5
-0+case COUNTER_LOAD_WATCHES_IOCTL : +raw_spin_lock_irqsave (events_lock, flags); +counter_events_list_free (events_list); +list_replace_init (next_events_list, events_list); +raw_spin_unlock_irqrestore (events_lock, flags);
For the eQEP driver, we will need a callback here to configure interrupts for the events. Likewise a callback would be needed here (for the case when we are replacing already loaded watches) and in COUNTER_STOP_WATCHES_IOCTL to unconfigure them.
Quoted 16 lines
drivers/counter/counter-chrdev.c new file +16
-0+break ; +default : +return -ENOIOCTLCMD ; + } + +return 0 ; +} + +static int counter_chrdev_open (struct inode *inode,struct file *filp) +{ +struct counter_device *const counter =container_of (inode->i_cdev, +typeof (*counter), + chrdev); + +get_device (&counter->dev); + filp->private_data = counter;
drivers/counter/counter-chrdev.c line 302
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+ +return nonseekable_open (inode, filp); +} + +static int counter_chrdev_release (struct inode *inode,struct file *filp) +{ +struct counter_device *const counter = filp->private_data; +unsigned long flags; + +put_device (&counter->dev);
put_device() should be at the end of the function in case it is the last reference.
Quoted 48 lines
drivers/counter/counter-chrdev.c new file +47
-0+ +raw_spin_lock_irqsave (&counter->events_lock, flags); +counter_events_list_free (&counter->events_list); +raw_spin_unlock_irqrestore (&counter->events_lock, flags); +counter_events_list_free (&counter->next_events_list); + +return 0 ; +} + +int counter_push_event (struct counter_device *const counter,const u8 event, +const u8 channel) +{ +struct counter_event ev = {0 }; +unsigned int copied =0 ; +unsigned long flags; +struct counter_event_node *event_node; +struct counter_comp_node *comp_node; +int err; + + ev.timestamp =ktime_get_ns (); + ev.watch.event = event; + ev.watch.channel = channel; + +raw_spin_lock_irqsave (&counter->events_lock, flags); + +/* Search for event in the list */ +list_for_each_entry (event_node, &counter->events_list, l) +if (event_node->event == event && + event_node->channel == channel) +break ; + +/* If event is not in the list */ +if (&event_node->l == &counter->events_list) + goto exit_early; + +/* Read and queue relevant comp for userspace */ +list_for_each_entry (comp_node, &event_node->comp_list, l) { + err =counter_get_data (counter, comp_node, &ev.value_u8); +if (err) + goto err_counter_get_data; + + ev.watch.component = comp_node->component; + + copied +=kfifo_put (&counter->events, ev); + } + +if (copied)
drivers/counter/counter-chrdev.c line 449
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+wake_up_poll (&counter->events_wait,EPOLLIN ); + +exit_early: +raw_spin_unlock_irqrestore (&counter->events_lock, flags); + +return 0 ; + +err_counter_get_data: +raw_spin_unlock_irqrestore (&counter->events_lock, flags); +return err;
This seems slightly redundant. Could just initialize err to 0 and combine the two labels.
drivers/counter/counter-sysfs.c
Quoted 8 lines
drivers/counter/counter-chrdev.c new file +2
-0+} +EXPORT_SYMBOL_GPL (counter_push_event);drivers/counter/counter-sysfs.c +0
-0
Not sure why sysfs changes are in the chrdev patch. Are these changes related somehow?
Quoted 6 lines
include/uapi/linux/counter.h new file +0
-0
include/uapi/linux/counter.h line 10
Quoted 10 lines
include/uapi/linux/counter.h new file +10
-0@@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* +* Userspace ABI for Counter character devices +* Copyright (C) 2020 William Breathitt Gray +*/ +#ifndef _UAPI_COUNTER_H_ +#define _UAPI_COUNTER_H_ + +#include <linux/ioctl.h> +#include <linux/types.h>
It would be very helpful to have documentation comments for all of the defines in this file.
include/uapi/linux/counter.h line 20
Quoted 10 lines
include/uapi/linux/counter.h new file +10
-0+ +#define COUNTER_SCOPE_DEVICE 0 +#define COUNTER_SCOPE_SIGNAL 1 +#define COUNTER_SCOPE_COUNT 2 + +#define COUNTER_COMPONENT_SIGNAL 0 +#define COUNTER_COMPONENT_COUNT 1 +#define COUNTER_COMPONENT_FUNCTION 2 +#define COUNTER_COMPONENT_SYNAPSE_ACTION 3 +#define COUNTER_COMPONENT_EXTENSION 4
There are probably some cases where we will want to just have an event without reading a component value, so we could use something like COUNTER_SCOPE_NONE and COUNTER_COMPONENT_NONE here.
Quoted 4 lines
include/uapi/linux/counter.h new file +4
-0+ +/** +* struct counter_component - Counter component identification +* @scope: component scope (Device, Count, or Signal)
include/uapi/linux/counter.h line 34
Quoted 10 lines
include/uapi/linux/counter.h new file +10
-0+* @parent: parent component identification number +* @type: component type (Count, extension, etc.) +* @id: component identification number +*/ +struct counter_component { +__u8 scope; +__u64 parent; +__u8 type; +__u64 id; +};
This will have alignment issues, e.g. when 64-bit kernel is used with a 32-bit userspace. It looks like there is a __aligned_u64 type to help with this. But, unless we expect more that 4 billion counter devices, there doesn't seem to be a reason for parent and id to be 64-bit.
include/uapi/linux/counter.h line 38
Quoted 4 lines
include/uapi/linux/counter.h new file +4
-0+ +#define COUNTER_EVENT_OVERFLOW 0 +#define COUNTER_EVENT_UNDERFLOW 1 +#define COUNTER_EVENT_OVERFLOW_UNDERFLOW 2
Would it make sense to just have individual overflow and underflow events? For example, the Carry-Borrow interrupt on 104-quad-8 could just call counter_push_event() twice.
include/uapi/linux/counter.h line 45
Quoted 7 lines
include/uapi/linux/counter.h new file +7
-0+#define COUNTER_EVENT_THRESHOLD 3 +#define COUNTER_EVENT_INDEX 4 + +/** +* struct counter_watch - Counter component watch configuration +* @event: event that triggers +* @channel: event channel
How is channel intended to be used? (The device I am working with I assume would ignore the channel since it is just a single counter.)
include/uapi/linux/counter.h line 52
Quoted 7 lines
include/uapi/linux/counter.h new file +7
-0+* @component: component to watch when event triggers +*/ +struct counter_watch { +__u8 event; +__u8 channel; +struct counter_component component; +};
To get the most compact layout, usually fields are ordered largest to smallest.
Quoted 10 lines
include/uapi/linux/counter.h new file +10
-0+ +#define COUNTER_CLEAR_WATCHES_IOCTL _IO(0x3E, 0x00) +#define COUNTER_SET_WATCH_IOCTL _IOW(0x3E, 0x01, struct counter_watch) +#define COUNTER_LOAD_WATCHES_IOCTL _IO(0x3E, 0x02) + +/** +* struct counter_event - Counter event data +* @timestamp: best estimate of time of event occurrence, in nanoseconds +* @watch: component watch configuration +* @value_u8: component value as __u8 data type
include/uapi/linux/counter.h line 72
Quoted 10 lines
include/uapi/linux/counter.h new file +10
-0+* @value_u64: component value as __u64 data type +*/ +struct counter_event { +__u64 timestamp; +struct counter_watch watch; +union { +__u8 value_u8; +__u64 value_u64; + }; +};
Should probably use __aligned_u64 here too.
Quoted 18 lines
include/uapi/linux/counter.h new file +17
-0+ +#define COUNTER_COUNT_DIRECTION_FORWARD 0 +#define COUNTER_COUNT_DIRECTION_BACKWARD 1 + +#define COUNTER_COUNT_MODE_NORMAL 0 +#define COUNTER_COUNT_MODE_RANGE_LIMIT 1 +#define COUNTER_COUNT_MODE_NON_RECYCLE 2 +#define COUNTER_COUNT_MODE_MODULO_N 3 + +#define COUNTER_FUNCTION_INCREASE 0 +#define COUNTER_FUNCTION_DECREASE 1 +#define COUNTER_FUNCTION_PULSE_DIRECTION 2 +#define COUNTER_FUNCTION_QUADRATURE_X1_A 3 +#define COUNTER_FUNCTION_QUADRATURE_X1_B 4 +#define COUNTER_FUNCTION_QUADRATURE_X2_A 5 +#define COUNTER_FUNCTION_QUADRATURE_X2_B 6 +#define COUNTER_FUNCTION_QUADRATURE_X4 7
include/uapi/linux/counter.h line 99
Quoted 10 lines
include/uapi/linux/counter.h new file +10
-0+ +#define COUNTER_SIGNAL_LEVEL_LOW 0 +#define COUNTER_SIGNAL_LEVEL_HIGH 1 + +#define COUNTER_SYNAPSE_ACTION_NONE 0 +#define COUNTER_SYNAPSE_ACTION_RISING_EDGE 1 +#define COUNTER_SYNAPSE_ACTION_FALLING_EDGE 2 +#define COUNTER_SYNAPSE_ACTION_BOTH_EDGES 3 + +#endif /* _UAPI_COUNTER_H_ */
Overall, I think we are getting close. I've started adding events to the TI eQEP driver. You can find the code at [1]. I still have a bit of work to do to add the timer and edge capture events.
Re: [PATCH v5 3/5] counter: Add character device interface
Quoted 10 lines · David Lechner
Quoted 6 lines · William Breathitt Gray
This patch introduces a character device interface for the Counter subsystem. Device data is exposed through standard character device read operations. Device data is gathered when a Counter event is pushed by the respective Counter device driver. Configuration is handled via ioctl operations on the respective Counter character device node.
Probably don't need to duplicate the full documentation in the commit message.
Ack.
Quoted 36 lines
Quoted 36 lines
drivers/counter/counter-chrdev.c new file +29
-0+ +static int counter_set_event_node (struct counter_device *const counter, +struct counter_watch *const watch, +const struct counter_comp_node *const cfg) +{ +struct counter_event_node *event_node; +int err; +struct counter_comp_node *comp_node; + +/* Search for event in the list */ +list_for_each_entry (event_node, &counter->next_events_list, l) +if (event_node->event == watch->event && + event_node->channel == watch->channel) +break ; + +/* If event is not already in the list */ +if (&event_node->l == &counter->next_events_list) { +/* Allocate new event node */ + event_node =kmalloc (sizeof (*event_node),GFP_ATOMIC ); +if (!event_node) +return -ENOMEM ; + +/* Configure event node and add to the list */ + event_node->event = watch->event; + event_node->channel = watch->channel; +INIT_LIST_HEAD (&event_node->comp_list); +list_add (&event_node->l, &counter->next_events_list); + } +
Quoted 8 lines
drivers/counter/counter-chrdev.c line 137
Quoted 5 lines
drivers/counter/counter-chrdev.c new file +5
-0+/* Check if component watch has already been set before */ +list_for_each_entry (comp_node, &event_node->comp_list, l) +if (comp_node->parent == cfg->parent && + comp_node->comp.count_u8_read == cfg->comp.count_u8_read) +return -EINVAL ;There are already enough things that could cause EINVAL, we could probably skip this duplicate check to make troubleshooting easier.
I'm not sure it would be safe to remove this check. This prevents duplicate watches from being allocated for the same component -- the rationale is that there's no benefit in repeating a component value grab for the same event. Although a typical user will not have the motivation to do this, a bad actor could purposely keep allocating duplicate watches in order to starve all the available memory.
Quoted 9 lines
drivers/counter/counter-chrdev.c line 143
Quoted 6 lines
drivers/counter/counter-chrdev.c new file +6
-0+ +/* Allocate component node */ + comp_node =kmalloc (sizeof (*comp_node),GFP_ATOMIC ); +if (!comp_node) { + err = -ENOMEM ; + goto err_comp_node;Since there is only one goto, we could just handle the error and return here instead.
Ack.
Quoted 5 lines
Quoted 5 lines
drivers/counter/counter-chrdev.c new file +5
-0+ } + *comp_node = *cfg; + +/* Add component node to event node */ +list_add_tail (&comp_node->l, &event_node->comp_list);
Quoted 8 lines
drivers/counter/counter-chrdev.c line 152
Quoted 4 lines
drivers/counter/counter-chrdev.c new file +4
-0+ +return 0 ; + +err_comp_node:A comment explaining the
list_empty()check would be nice. It makes sense if you think about it, but it is not super obvious.
Ack.
Quoted 35 lines
Quoted 22 lines
drivers/counter/counter-chrdev.c new file +22
-0+if (list_empty (&event_node->comp_list)) { +list_del (&event_node->l); +kfree (event_node); + } +return err; +} + +static int counter_set_watch (struct counter_device *const counter, +const unsigned long arg) +{ +void __user *const uwatch = (void __user *)arg; +struct counter_watch watch; +struct counter_comp_node comp_node; +size_t parent, id; +struct counter_comp *ext; +size_t num_ext; + +if (copy_from_user (&watch, uwatch,sizeof (watch))) +return -EFAULT ; + parent = watch.component.parent; + id = watch.component.id; +drivers/counter/counter-chrdev.c line 184
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+/* Configure parent component info for comp node */ +switch (watch.component.scope) { +case COUNTER_SCOPE_DEVICE : + comp_node.parent =NULL ; + + ext = counter->ext; + num_ext = counter->num_ext; +break ; +case COUNTER_SCOPE_SIGNAL : +if (counter->num_signals < parent +1 )I think it would be more conventional this way:
Quoted 1 line
if (parent >= counter->num_signals)
Ack.
Quoted 11 lines
drivers/counter/counter-chrdev.c line 193
Quoted 9 lines
drivers/counter/counter-chrdev.c new file +9
-0+return -EINVAL ; + + comp_node.parent = counter->signals + parent; + + ext = counter->signals[parent].ext; + num_ext = counter->signals[parent].num_ext; +break ; +case COUNTER_SCOPE_COUNT : +if (counter->num_counts < parent +1 )Same here.
Ack.
Quoted 32 lines
Quoted 32 lines
drivers/counter/counter-chrdev.c new file +32
-0+return -EINVAL ; + + comp_node.parent = counter->counts + parent; + + ext = counter->counts[parent].ext; + num_ext = counter->counts[parent].num_ext; +break ; +default : +return -EINVAL ; + } + +/* Configure component info for comp node */ +switch (watch.component.type) { +case COUNTER_COMPONENT_SIGNAL : +if (watch.component.scope !=COUNTER_SCOPE_SIGNAL ) +return -EINVAL ; + + comp_node.comp.type =COUNTER_COMP_SIGNAL_LEVEL ; + comp_node.comp.signal_u8_read = counter->ops->signal_read; +break ; +case COUNTER_COMPONENT_COUNT : +if (watch.component.scope !=COUNTER_SCOPE_COUNT ) +return -EINVAL ; + + comp_node.comp.type =COUNTER_COMP_U64 ; + comp_node.comp.count_u64_read = counter->ops->count_read; +break ; +case COUNTER_COMPONENT_FUNCTION : +if (watch.component.scope !=COUNTER_SCOPE_COUNT ) +return -EINVAL ; + + comp_node.comp.type =COUNTER_COMP_FUNCTION ;
Quoted 8 lines
drivers/counter/counter-chrdev.c line 231
Quoted 6 lines
drivers/counter/counter-chrdev.c new file +6
-0+ comp_node.comp.count_u8_read = counter->ops->function_read; +break ; +case COUNTER_COMPONENT_SYNAPSE_ACTION : +if (watch.component.scope !=COUNTER_SCOPE_COUNT ) +return -EINVAL ; +if (counter->counts[parent].num_synapses < id +1 )if (id >= counter->counts[parent].num_synapses)
Ack.
Quoted 10 lines
drivers/counter/counter-chrdev.c line 239
Quoted 8 lines
drivers/counter/counter-chrdev.c new file +8
-0+return -EINVAL ; + + comp_node.comp.type =COUNTER_COMP_SYNAPSE_ACTION ; + comp_node.comp.action_read = counter->ops->action_read; + comp_node.comp.priv = counter->counts[parent].synapses + id; +break ; +case COUNTER_COMPONENT_EXTENSION : +if (num_ext < id +1 )if (id >= num_ext)
Ack.
Quoted 4 lines
Quoted 4 lines
drivers/counter/counter-chrdev.c new file +4
-0+return -EINVAL ; + + comp_node.comp = ext[id]; +break ;
Quoted 8 lines
drivers/counter/counter-chrdev.c line 247
Quoted 4 lines
drivers/counter/counter-chrdev.c new file +4
-0+default : +return -EINVAL ; + } +if (!comp_node.comp.count_u8_read)A comment reminding us that
count_u8_readis part of a union would be helpful here since this is checking that any read function was set, not justcount_u8_read.
Ack.
Quoted 3 lines
drivers/counter/counter-chrdev.c
Quoted 1 line
drivers/counter/counter-chrdev.c new file +1
-0+return -EFAULT ;Would EOPNOTSUPP be a better error choice?
You're right, that would be a more apt return.
Quoted 27 lines
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+ comp_node.component = watch.component; + +return counter_set_event_node (counter, &watch, &comp_node); +} + +static long counter_chrdev_ioctl (struct file *filp,unsigned int cmd, +unsigned long arg) +{ +struct counter_device *const counter = filp->private_data; +raw_spinlock_t *const events_lock = &counter->events_lock;drivers/counter/counter-chrdev.c line 268
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+unsigned long flags; +struct list_head *const events_list = &counter->events_list; +struct list_head *const next_events_list = &counter->next_events_list; + +switch (cmd) { +case COUNTER_CLEAR_WATCHES_IOCTL : +raw_spin_lock_irqsave (events_lock, flags); +counter_events_list_free (events_list); +raw_spin_unlock_irqrestore (events_lock, flags); +counter_events_list_free (next_events_list);I think this ioctl is doing too much. If we have to use it for both stopping events and clearing the list accumulated by
COUNTER_SET_WATCH_IOCTL, then we have a race condition of no events after clearing watches during the time we are adding new ones and until we load the new ones.
Quoted 3 lines
It would probably make more sense to call this ioctl
COUNTER_STOP_WATCHES_IOCTLand movecounter_events_list_free( next_events_list)to the end ofCOUNTER_LOAD_WATCHES_IOCTL.
I don't think we will necessarily have a race condition here. COUNTER_CLEAR_WATCHES_IOCTL is intended to just clear the watches; e.g. bring us back to a clear state when some sort of job has completely finished and the user is no longer going to watch events for a while (maybe they're adjusting the conveyor for the next job or some similar operation).
I think the scenario you're concerned about is when you need to swap watches in the middle of a job without losing events. In this case, you wouldn't need to use COUNTER_CLEAR_WATCHES_IOCTL at all. Instead, you would just set up the watches via COUNTER_SET_WATCH_IOCTL, and then use COUNTER_LOAD_WATCHES_IOCTL to perform the swap; after COUNTER_LOAD_WATCHES_IOCTL completes, next_events_list is empty (thanks to list_replace_init()) and you're ready for the next set of watches.
Quoted 7 lines
drivers/counter/counter-chrdev.c line 271
Quoted 3 lines
drivers/counter/counter-chrdev.c new file +3
-0+break ; +case COUNTER_SET_WATCH_IOCTL : +return counter_set_watch (counter, arg);Would it make more sense to call this
COUNTER_ADD_WATCH_IOCTLsince it adds a watch to the list that will be loaded/enabled later?
That's a good point because "set" might easily be confused for a configuration operation. I'll rename this to be clearer.
Quoted 10 lines
drivers/counter/counter-chrdev.c line 276
Quoted 5 lines
drivers/counter/counter-chrdev.c new file +5
-0+case COUNTER_LOAD_WATCHES_IOCTL : +raw_spin_lock_irqsave (events_lock, flags); +counter_events_list_free (events_list); +list_replace_init (next_events_list, events_list); +raw_spin_unlock_irqrestore (events_lock, flags);For the eQEP driver, we will need a callback here to configure interrupts for the events. Likewise a callback would be needed here (for the case when we are replacing already loaded watches) and in
COUNTER_STOP_WATCHES_IOCTLto unconfigure them.
Ack, I'll add in a callback for interrupt configuration where needed.
Quoted 21 lines
Quoted 21 lines
drivers/counter/counter-chrdev.c new file +21
-0+break ; +default : +return -ENOIOCTLCMD ; + } + +return 0 ; +} + +static int counter_chrdev_open (struct inode *inode,struct file *filp) +{ +struct counter_device *const counter =container_of (inode->i_cdev, +typeof (*counter), + chrdev); + +get_device (&counter->dev); + filp->private_data = counter; + +return nonseekable_open (inode, filp); +} + +static int counter_chrdev_release (struct inode *inode,struct file *filp)
Quoted 8 lines
drivers/counter/counter-chrdev.c line 302
Quoted 5 lines
drivers/counter/counter-chrdev.c new file +5
-0+{ +struct counter_device *const counter = filp->private_data; +unsigned long flags; + +put_device (&counter->dev);
put_device()should be at the end of the function in case it is the last reference.
put_device() shouldn't affect the counter_device events members, so I don't think there's a difference in this case if it's called at the beginning or end of the counter_chrdev_release function.
Quoted 53 lines
Quoted 53 lines
drivers/counter/counter-chrdev.c new file +52
-0+ +raw_spin_lock_irqsave (&counter->events_lock, flags); +counter_events_list_free (&counter->events_list); +raw_spin_unlock_irqrestore (&counter->events_lock, flags); +counter_events_list_free (&counter->next_events_list); + +return 0 ; +} + +int counter_push_event (struct counter_device *const counter,const u8 event, +const u8 channel) +{ +struct counter_event ev = {0 }; +unsigned int copied =0 ; +unsigned long flags; +struct counter_event_node *event_node; +struct counter_comp_node *comp_node; +int err; + + ev.timestamp =ktime_get_ns (); + ev.watch.event = event; + ev.watch.channel = channel; + +raw_spin_lock_irqsave (&counter->events_lock, flags); + +/* Search for event in the list */ +list_for_each_entry (event_node, &counter->events_list, l) +if (event_node->event == event && + event_node->channel == channel) +break ; + +/* If event is not in the list */ +if (&event_node->l == &counter->events_list) + goto exit_early; + +/* Read and queue relevant comp for userspace */ +list_for_each_entry (comp_node, &event_node->comp_list, l) { + err =counter_get_data (counter, comp_node, &ev.value_u8); +if (err) + goto err_counter_get_data; + + ev.watch.component = comp_node->component; + + copied +=kfifo_put (&counter->events, ev); + } + +if (copied) +wake_up_poll (&counter->events_wait,EPOLLIN ); + +exit_early: +raw_spin_unlock_irqrestore (&counter->events_lock, flags); +
Quoted 8 lines
drivers/counter/counter-chrdev.c line 449
Quoted 5 lines
drivers/counter/counter-chrdev.c new file +5
-0+return 0 ; + +err_counter_get_data: +raw_spin_unlock_irqrestore (&counter->events_lock, flags); +return err;This seems slightly redundant. Could just initialize err to 0 and combine the two labels.
Ack.
Quoted 4 lines
Quoted 2 lines
drivers/counter/counter-chrdev.c new file +2
-0+} +EXPORT_SYMBOL_GPL (counter_push_event);
Quoted 8 lines
drivers/counter/counter-sysfs.c
Quoted 4 lines
drivers/counter/counter-sysfs.c +0
-0Not sure why sysfs changes are in the chrdev patch. Are these changes related somehow?
Sorry, I forgot to explain this in the cover letter. The changes here are only useful for the character device interface. These changes introduce the extensionZ_name and extensionZ_width sysfs attributes.
In the character device interface, extensions are selected by their id number, and the value returned depends on the type of data. The new sysfs attributes introduced here allow users to match the id of an extension with its name, as well as the bit width of the value returned so that the user knows whether to use the value_u8 or value_u64 union member in struct counter_event.
Quoted 11 lines
Quoted 11 lines
include/uapi/linux/counter.h new file +5
-0@@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* +* Userspace ABI for Counter character devices +* Copyright (C) 2020 William Breathitt Gray +*/
Quoted 8 lines
include/uapi/linux/counter.h line 10
Quoted 5 lines
include/uapi/linux/counter.h new file +5
-0+#ifndef _UAPI_COUNTER_H_ +#define _UAPI_COUNTER_H_ + +#include <linux/ioctl.h> +#include <linux/types.h> It would be very helpful to have documentation comments for all of the defines in this file.
Ack.
Quoted 7 lines
Quoted 7 lines
include/uapi/linux/counter.h new file +7
-0+ +#define COUNTER_SCOPE_DEVICE 0 +#define COUNTER_SCOPE_SIGNAL 1 +#define COUNTER_SCOPE_COUNT 2 + +#define COUNTER_COMPONENT_SIGNAL 0 +#define COUNTER_COMPONENT_COUNT 1
Quoted 8 lines
include/uapi/linux/counter.h line 20
Quoted 3 lines
include/uapi/linux/counter.h new file +3
-0+#define COUNTER_COMPONENT_FUNCTION 2 +#define COUNTER_COMPONENT_SYNAPSE_ACTION 3 +#define COUNTER_COMPONENT_EXTENSION 4There are probably some cases where we will want to just have an event without reading a component value, so we could use something like
COUNTER_SCOPE_NONEandCOUNTER_COMPONENT_NONEhere.
I'll come up with a solution for this use-case.
Quoted 13 lines
Quoted 13 lines
include/uapi/linux/counter.h new file +13
-0+ +/** +* struct counter_component - Counter component identification +* @scope: component scope (Device, Count, or Signal) +* @parent: parent component identification number +* @type: component type (Count, extension, etc.) +* @id: component identification number +*/ +struct counter_component { +__u8 scope; +__u64 parent; +__u8 type; +__u64 id;
Quoted 8 lines
include/uapi/linux/counter.h line 34
Quoted 1 line
include/uapi/linux/counter.h new file +1
-0+};This will have alignment issues, e.g. when 64-bit kernel is used with a 32-bit userspace. It looks like there is a
__aligned_u64type to help with this. But, unless we expect more that 4 billion counter devices, there doesn't seem to be a reason for parent and id to be 64-bit.
Fair point, I'll redefine these id as __u8 since it's unlikely we'll ever have a device that needs more than 255 components.
Quoted 8 lines
include/uapi/linux/counter.h line 38
Quoted 4 lines
include/uapi/linux/counter.h new file +4
-0+ +#define COUNTER_EVENT_OVERFLOW 0 +#define COUNTER_EVENT_UNDERFLOW 1 +#define COUNTER_EVENT_OVERFLOW_UNDERFLOW 2Would it make sense to just have individual overflow and underflow events? For example, the Carry-Borrow interrupt on 104-quad-8 could just call
counter_push_event()twice.
I can see what you mean, but I think a dedicated OVERFLOW_UNDERFLOW event is useful because of the directional implications of an OVERFLOW or UNDERFLOW event by itself: if we see an OVERFLOW event we can be certain that the counter changed conceptually from a ceiling value to floor value (and vice-versa for an UNDERFLOW event). By providing the OVERFLOW_UNDERFLOW event we have an explicit way to indicate that the device is not concerned with the direction of the change that trigger the event. I think that's useful information to have for a user.
Quoted 10 lines
include/uapi/linux/counter.h line 45
Quoted 7 lines
include/uapi/linux/counter.h new file +7
-0+#define COUNTER_EVENT_THRESHOLD 3 +#define COUNTER_EVENT_INDEX 4 + +/** +* struct counter_watch - Counter component watch configuration +* @event: event that triggers +* @channel: event channel How is channel intended to be used? (The device I am working with I assume would ignore the channel since it is just a single counter.)
Yes, this is for physical devices that have multiple counters ("channels") such as the 104-QUAD-8 which has 8 available on a single PC104 card. For the device you're working on, you would probably just set channel to 0 for all your uses since you only have one counter.
Quoted 9 lines
include/uapi/linux/counter.h line 52
Quoted 7 lines
include/uapi/linux/counter.h new file +7
-0+* @component: component to watch when event triggers +*/ +struct counter_watch { +__u8 event; +__u8 channel; +struct counter_component component; +};To get the most compact layout, usually fields are ordered largest to smallest.
Ack.
Quoted 14 lines
Quoted 14 lines
include/uapi/linux/counter.h new file +14
-0+ +#define COUNTER_CLEAR_WATCHES_IOCTL _IO(0x3E, 0x00) +#define COUNTER_SET_WATCH_IOCTL _IOW(0x3E, 0x01, struct counter_watch) +#define COUNTER_LOAD_WATCHES_IOCTL _IO(0x3E, 0x02) + +/** +* struct counter_event - Counter event data +* @timestamp: best estimate of time of event occurrence, in nanoseconds +* @watch: component watch configuration +* @value_u8: component value as __u8 data type +* @value_u64: component value as __u64 data type +*/ +struct counter_event { +__u64 timestamp;
Quoted 8 lines
include/uapi/linux/counter.h line 72
Quoted 6 lines
include/uapi/linux/counter.h new file +6
-0+struct counter_watch watch; +union { +__u8 value_u8; +__u64 value_u64; + }; +};Should probably use
__aligned_u64here too.
Ack.
Quoted 35 lines
Quoted 18 lines
include/uapi/linux/counter.h new file +17
-0+ +#define COUNTER_COUNT_DIRECTION_FORWARD 0 +#define COUNTER_COUNT_DIRECTION_BACKWARD 1 + +#define COUNTER_COUNT_MODE_NORMAL 0 +#define COUNTER_COUNT_MODE_RANGE_LIMIT 1 +#define COUNTER_COUNT_MODE_NON_RECYCLE 2 +#define COUNTER_COUNT_MODE_MODULO_N 3 + +#define COUNTER_FUNCTION_INCREASE 0 +#define COUNTER_FUNCTION_DECREASE 1 +#define COUNTER_FUNCTION_PULSE_DIRECTION 2 +#define COUNTER_FUNCTION_QUADRATURE_X1_A 3 +#define COUNTER_FUNCTION_QUADRATURE_X1_B 4 +#define COUNTER_FUNCTION_QUADRATURE_X2_A 5 +#define COUNTER_FUNCTION_QUADRATURE_X2_B 6 +#define COUNTER_FUNCTION_QUADRATURE_X4 7include/uapi/linux/counter.h line 99
Quoted 10 lines
include/uapi/linux/counter.h new file +10
-0+ +#define COUNTER_SIGNAL_LEVEL_LOW 0 +#define COUNTER_SIGNAL_LEVEL_HIGH 1 + +#define COUNTER_SYNAPSE_ACTION_NONE 0 +#define COUNTER_SYNAPSE_ACTION_RISING_EDGE 1 +#define COUNTER_SYNAPSE_ACTION_FALLING_EDGE 2 +#define COUNTER_SYNAPSE_ACTION_BOTH_EDGES 3 + +#endif /* _UAPI_COUNTER_H_ */ Overall, I think we are getting close. I've started adding events to the TI eQEP driver. You can find the code at [1]. I still have a bit of work to do to add the timer and edge capture events.
Quoted 1 line
I just want to mention again that I appreciate all the reviews you send for these patchsets, it really helps to have someone to bounce ideas off of as well an extra set of eyes to catch all the silly bugs that I miss during the revisions. :-)
Thanks,
William Breathitt Gray
David Lechner
reviewerRe: [PATCH v5 3/5] counter: Add character device interface
Quoted 34 lines
Quoted 23 lines
Quoted 6 lines
drivers/counter/counter-chrdev.c new file +5
-0+static long counter_chrdev_ioctl (struct file *filp,unsigned int cmd, +unsigned long arg) +{ +struct counter_device *const counter = filp->private_data; +raw_spinlock_t *const events_lock = &counter->events_lock;drivers/counter/counter-chrdev.c line 268
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+unsigned long flags; +struct list_head *const events_list = &counter->events_list; +struct list_head *const next_events_list = &counter->next_events_list; + +switch (cmd) { +case COUNTER_CLEAR_WATCHES_IOCTL : +raw_spin_lock_irqsave (events_lock, flags); +counter_events_list_free (events_list); +raw_spin_unlock_irqrestore (events_lock, flags); +counter_events_list_free (next_events_list);I think this ioctl is doing too much. If we have to use it for both stopping events and clearing the list accumulated by
COUNTER_SET_WATCH_IOCTL, then we have a race condition of no events after clearing watches during the time we are adding new ones and until we load the new ones.Quoted 3 lines
It would probably make more sense to call this ioctl
COUNTER_STOP_WATCHES_IOCTLand movecounter_events_list_free( next_events_list)to the end ofCOUNTER_LOAD_WATCHES_IOCTL.I don't think we will necessarily have a race condition here.
COUNTER_CLEAR_WATCHES_IOCTLis intended to just clear the watches; e.g. bring us back to a clear state when some sort of job has completely finished and the user is no longer going to watch events for a while (maybe they're adjusting the conveyor for the next job or some similar operation).
Quoted 8 lines
I think the scenario you're concerned about is when you need to swap watches in the middle of a job without losing events. In this case, you wouldn't need to use
COUNTER_CLEAR_WATCHES_IOCTLat all. Instead, you would just set up the watches viaCOUNTER_SET_WATCH_IOCTL, and then useCOUNTER_LOAD_WATCHES_IOCTLto perform the swap; afterCOUNTER_LOAD_WATCHES_IOCTLcompletes,next_events_listis empty (thanks tolist_replace_init()) and you're ready for the next set of watches.
Got it. I think I missed the fact that list_replace_init() clears next_events_list.
Quoted 11 lines
Quoted 10 lines
drivers/counter/counter-chrdev.c line 302
Quoted 7 lines
drivers/counter/counter-chrdev.c new file +7
-0+ +static int counter_chrdev_release (struct inode *inode,struct file *filp) +{ +struct counter_device *const counter = filp->private_data; +unsigned long flags; + +put_device (&counter->dev);
put_device()should be at the end of the function in case it is the last reference.
Quoted 4 lines
put_device()shouldn't affect thecounter_deviceevents members, so I don't think there's a difference in this case if it's called at the beginning or end of thecounter_chrdev_releasefunction.
It isn't possible the some memory allocated with devm_kalloc() could be be referenced after calling put_device() now or in the future?
Quoted 17 lines
Quoted 4 lines
Quoted 2 lines
(quoted diff) +2
-0+} +EXPORT_SYMBOL_GPL(counter_push_event);Quoted 8 lines
drivers/counter/counter-sysfs.c
Quoted 4 lines
drivers/counter/counter-sysfs.c +0
-0Not sure why sysfs changes are in the chrdev patch. Are these changes related somehow?
Sorry, I forgot to explain this in the cover letter. The changes here are only useful for the character device interface. These changes introduce the
extensionZ_nameandextensionZ_widthsysfs attributes.
Quoted 7 lines
In the character device interface, extensions are selected by their id number, and the value returned depends on the type of data. The new sysfs attributes introduced here allow users to match the id of an extension with its name, as well as the bit width of the value returned so that the user knows whether to use the
value_u8orvalue_u64union member in structcounter_event.
Are we sure that all value types will always be CPU-endian unsigned integers? Or should we make an enum to describe the data type instead of just the width?
Re: [PATCH v5 3/5] counter: Add character device interface
Quoted 11 lines · David Lechner
Quoted 11 lines
Quoted 10 lines
drivers/counter/counter-chrdev.c line 302
Quoted 7 lines
drivers/counter/counter-chrdev.c new file +7
-0+ +static int counter_chrdev_release (struct inode *inode,struct file *filp) +{ +struct counter_device *const counter = filp->private_data; +unsigned long flags; + +put_device (&counter->dev);
put_device()should be at the end of the function in case it is the last reference.
Quoted 7 lines
Quoted 4 lines
put_device()shouldn't affect thecounter_deviceevents members, so I don't think there's a difference in this case if it's called at the beginning or end of thecounter_chrdev_releasefunction.It isn't possible the some memory allocated with
devm_kalloc()could be be referenced after callingput_device()now or in the future?
You're right, if put_device() is called before then we could end up in a garbage state where the device memory is released but events list has not yet been freed. I'll move put_device() to after the events list is freed then.
Quoted 21 lines
Quoted 13 lines
Quoted 8 lines
drivers/counter/counter-sysfs.c
Quoted 4 lines
drivers/counter/counter-sysfs.c +0
-0Not sure why sysfs changes are in the chrdev patch. Are these changes related somehow?
Sorry, I forgot to explain this in the cover letter. The changes here are only useful for the character device interface. These changes introduce the
extensionZ_nameandextensionZ_widthsysfs attributes.Quoted 7 lines
In the character device interface, extensions are selected by their id number, and the value returned depends on the type of data. The new sysfs attributes introduced here allow users to match the id of an extension with its name, as well as the bit width of the value returned so that the user knows whether to use the
value_u8orvalue_u64union member in structcounter_event.
Quoted 3 lines
Are we sure that all value types will always be CPU-endian unsigned integers? Or should we make an enum to describe the data type instead of just the width?
It should be safe to assume that the character device interface will only ever return CPU-endian unsigned integers. The device driver should handle the conversion of any strange endianness from the device before the character device interface, while userspace is the one responsible for interpreting the meaning of count in the context of the application.
Let's create a scenario for the sake of example. Suppose we want to use a counter device to track the vertical position of a component moved by a linear actuator. The operator considers some vertical position as the horizon, where anything above would be a positive position and anything below a negative position. The counter device stores its count in big-endian format; but the system CPU expects little-endian.
The flow of data for this scenario would look like the following (where BE = big-endian, LE = little-endian):
+----------+ +---------------+ +--------+ | Raw data | - BE -> | Device driver | -> LE -> | chrdev | - u64 -> +----------+ +---------------+ +--------+
At this point, the userspace application would read the unsigned integer from the character device and determine how to interpret the position -- whether the count be converted to a signed value to represent a negative physical position.
Whether or not a position should be considered negative is dependent on the user application and context. Because the character device does not know the context of the user application, it should only provide unsigned integers in order to ensure a standard interface for counter devices; userspace will be responsible for interpreting those counts to something meaningful for the context of their applications.
William Breathitt Gray
David Lechner
reviewerRe: [PATCH v5 3/5] counter: Add character device interface
Quoted 51 lines
Quoted 22 lines
Quoted 14 lines
Quoted 9 lines
drivers/counter/counter-sysfs.c
Quoted 5 lines
drivers/counter/counter-sysfs.c +0
-0Not sure why sysfs changes are in the chrdev patch. Are these changes related somehow?
Sorry, I forgot to explain this in the cover letter. The changes here are only useful for the character device interface. These changes introduce the
extensionZ_nameandextensionZ_widthsysfs attributes.Quoted 7 lines
In the character device interface, extensions are selected by their id number, and the value returned depends on the type of data. The new sysfs attributes introduced here allow users to match the id of an extension with its name, as well as the bit width of the value returned so that the user knows whether to use the
value_u8orvalue_u64union member in structcounter_event.Quoted 3 lines
Are we sure that all value types will always be CPU-endian unsigned integers? Or should we make an enum to describe the data type instead of just the width?
It should be safe to assume that the character device interface will only ever return CPU-endian unsigned integers. The device driver should handle the conversion of any strange endianness from the device before the character device interface, while userspace is the one responsible for interpreting the meaning of count in the context of the application.
Let's create a scenario for the sake of example. Suppose we want to use a counter device to track the vertical position of a component moved by a linear actuator. The operator considers some vertical position as the horizon, where anything above would be a positive position and anything below a negative position. The counter device stores its count in big-endian format; but the system CPU expects little-endian.
The flow of data for this scenario would look like the following (where BE = big-endian, LE = little-endian):
+----------+ +---------------+ +--------+ | Raw data | - BE -> | Device driver | -> LE -> | chrdev | - u64 -> +----------+ +---------------+ +--------+At this point, the userspace application would read the unsigned integer from the character device and determine how to interpret the position -- whether the count be converted to a signed value to represent a negative physical position.
Quoted 9 lines
Whether or not a position should be considered negative is dependent on the user application and context. Because the character device does not know the context of the user application, it should only provide unsigned integers in order to ensure a standard interface for counter devices; userspace will be responsible for interpreting those counts to something meaningful for the context of their applications.
William Breathitt Gray
Sounds reasonable to me.
David Lechner
reviewerRe: [PATCH v5 3/5] counter: Add character device interface
In reply to William Breathitt GrayQuoted 38 lines · William Breathitt Gray
drivers/counter/counter-chrdev.c new file +31
-0+/** +* counter_push_event - queue event for userspace reading +* @counter: pointer to Counter structure +* @event: triggered event +* @channel: event channel +* +* Note: If no one is watching for the respective event, it is silently +* discarded. +* +* RETURNS: +* 0 on success, negative error number on failure. +*/ +int counter_push_event (struct counter_device *const counter,const u8 event, +const u8 channel) +{ +struct counter_event ev = {0 }; +unsigned int copied =0 ; +unsigned long flags; +struct counter_event_node *event_node; +struct counter_comp_node *comp_node; +int err; + + ev.timestamp =ktime_get_ns (); + ev.watch.event = event; + ev.watch.channel = channel; + +raw_spin_lock_irqsave (&counter->events_lock, flags); + +/* Search for event in the list */ +list_for_each_entry (event_node, &counter->events_list, l) + if (event_node->event == event &&
drivers/counter/counter-chrdev.c line 430
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+ event_node->channel == channel) +break ; + +/* If event is not in the list */ +if (&event_node->l == &counter->events_list) + goto exit_early; + +/* Read and queue relevant comp for userspace */ +list_for_each_entry (comp_node, &event_node->comp_list, l) { + err =counter_get_data (counter, comp_node, &ev.value_u8);
Currently all counter devices are memory mapped devices so calling counter_get_data() here with interrupts disabled is probably OK, but if any counter drivers are added that use I2C/SPI/etc. that will take a long time to read, it would cause problems leaving interrupts disabled here.
Brainstorming: Would it make sense to separate the event from the component value being read? As I mentioned in one of my previous reviews, I think there are some cases where we would just want to know when an event happened and not read any additional data anyway. In the case of a slow communication bus, this would also let us queue the event in the kfifo and notify poll right away and then defer the reads in a workqueue for later.
Quoted 20 lines
drivers/counter/counter-chrdev.c new file +20
-0+if (err) + goto err_counter_get_data; + + ev.watch.component = comp_node->component; + + copied +=kfifo_put (&counter->events, ev); + } + +if (copied) +wake_up_poll (&counter->events_wait,EPOLLIN ); + +exit_early: +raw_spin_unlock_irqrestore (&counter->events_lock, flags); + +return 0 ; + +err_counter_get_data: +raw_spin_unlock_irqrestore (&counter->events_lock, flags); +return err; +}
Re: [PATCH v5 3/5] counter: Add character device interface
Quoted 56 lines · David Lechner
Quoted 38 lines · William Breathitt Gray
drivers/counter/counter-chrdev.c new file +31
-0+/** +* counter_push_event - queue event for userspace reading +* @counter: pointer to Counter structure +* @event: triggered event +* @channel: event channel +* +* Note: If no one is watching for the respective event, it is silently +* discarded. +* +* RETURNS: +* 0 on success, negative error number on failure. +*/ +int counter_push_event (struct counter_device *const counter,const u8 event, +const u8 channel) +{ +struct counter_event ev = {0 }; +unsigned int copied =0 ; +unsigned long flags; +struct counter_event_node *event_node; +struct counter_comp_node *comp_node; +int err; + + ev.timestamp =ktime_get_ns (); + ev.watch.event = event; + ev.watch.channel = channel; + +raw_spin_lock_irqsave (&counter->events_lock, flags); + +/* Search for event in the list */ +list_for_each_entry (event_node, &counter->events_list, l) + if (event_node->event == event &&drivers/counter/counter-chrdev.c line 430
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+ event_node->channel == channel) +break ; + +/* If event is not in the list */ +if (&event_node->l == &counter->events_list) + goto exit_early; + +/* Read and queue relevant comp for userspace */ +list_for_each_entry (comp_node, &event_node->comp_list, l) { + err =counter_get_data (counter, comp_node, &ev.value_u8);Currently all counter devices are memory mapped devices so calling
counter_get_data()here with interrupts disabled is probably OK, but if any counter drivers are added that use I2C/SPI/etc. that will take a long time to read, it would cause problems leaving interrupts disabled here.
Quoted 7 lines
Brainstorming: Would it make sense to separate the event from the component value being read? As I mentioned in one of my previous reviews, I think there are some cases where we would just want to know when an event happened and not read any additional data anyway. In the case of a slow communication bus, this would also let us queue the event in the kfifo and notify poll right away and then defer the reads in a workqueue for later.
I don't see any problems with reporting just an event without any component value attached (e.g. userspace could handle the component reads via sysfs at a later point). We would just need a way to inform userspace that the struct counter_component in the struct counter_watch returned should be ignored.
Perhaps we can add an additional member to struct counter_watch indicating whether it's an empty watch; or alternatively, add a new component scope define to differentiate between an actual component and an empty one (e.g. COUNTER_SCOPE_EVENT). What do you think?
William Breathitt Gray
David Lechner
reviewerRe: [PATCH v5 3/5] counter: Add character device interface
Quoted 71 lines · William Breathitt Gray
Quoted 56 lines · David Lechner
Quoted 38 lines · William Breathitt Gray
drivers/counter/counter-chrdev.c new file +31
-0+/** +* counter_push_event - queue event for userspace reading +* @counter: pointer to Counter structure +* @event: triggered event +* @channel: event channel +* +* Note: If no one is watching for the respective event, it is silently +* discarded. +* +* RETURNS: +* 0 on success, negative error number on failure. +*/ +int counter_push_event (struct counter_device *const counter,const u8 event, +const u8 channel) +{ +struct counter_event ev = {0 }; +unsigned int copied =0 ; +unsigned long flags; +struct counter_event_node *event_node; +struct counter_comp_node *comp_node; +int err; + + ev.timestamp =ktime_get_ns (); + ev.watch.event = event; + ev.watch.channel = channel; + +raw_spin_lock_irqsave (&counter->events_lock, flags); + +/* Search for event in the list */ +list_for_each_entry (event_node, &counter->events_list, l) + if (event_node->event == event &&drivers/counter/counter-chrdev.c line 430
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+ event_node->channel == channel) +break ; + +/* If event is not in the list */ +if (&event_node->l == &counter->events_list) + goto exit_early; + +/* Read and queue relevant comp for userspace */ +list_for_each_entry (comp_node, &event_node->comp_list, l) { + err =counter_get_data (counter, comp_node, &ev.value_u8);Currently all counter devices are memory mapped devices so calling
counter_get_data()here with interrupts disabled is probably OK, but if any counter drivers are added that use I2C/SPI/etc. that will take a long time to read, it would cause problems leaving interrupts disabled here.Quoted 7 lines
Brainstorming: Would it make sense to separate the event from the component value being read? As I mentioned in one of my previous reviews, I think there are some cases where we would just want to know when an event happened and not read any additional data anyway. In the case of a slow communication bus, this would also let us queue the event in the kfifo and notify poll right away and then defer the reads in a workqueue for later.
I don't see any problems with reporting just an event without any component value attached (e.g. userspace could handle the component reads via sysfs at a later point). We would just need a way to inform userspace that the struct
counter_componentin the structcounter_watchreturned should be ignored.
Quoted 7 lines
Perhaps we can add an additional member to struct
counter_watchindicating whether it's an empty watch; or alternatively, add a new component scope define to differentiate between an actual component and an empty one (e.g.COUNTER_SCOPE_EVENT). What do you think?William Breathitt Gray
I made the same suggestion in one of my other replies - except I called it COUNTER_SCOPE_NONE.
David Lechner
reviewerRe: [PATCH v5 3/5] counter: Add character device interface
In reply to William Breathitt GrayQuoted 21 lines · William Breathitt Gray
drivers/counter/counter-chrdev.c new file +21
-0+static ssize_t counter_chrdev_read (struct file *filp,char __user *buf, +size_t len,loff_t *f_ps) +{ +struct counter_device *const counter = filp->private_data; +int err; +unsigned long flags; +unsigned int copied; + +if (len <sizeof (struct counter_event )) +return -EINVAL ; + + do { + if (kfifo_is_empty(&counter->events)) { + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + + err = wait_event_interruptible(counter->events_wait, + !kfifo_is_empty(&counter->events)); + if (err) + return err; + }
drivers/counter/counter-chrdev.c line 71
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+ +raw_spin_lock_irqsave (&counter->events_lock, flags); + err =kfifo_to_user (&counter->events, buf, len, &copied); +raw_spin_unlock_irqrestore (&counter->events_lock, flags); +if (err) +return err; + }while (!copied); + +return copied; +}
All other uses of kfifo_to_user() I saw use a mutex instead of spin lock. I don't see a reason for disabling interrupts here.
Example:
static ssize_t iio_event_chrdev_read(struct file *filep,
char __user *buf,
size_t count,
loff_t *f_ps)
{
struct iio_dev *indio_dev = filep->private_data;
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
struct iio_event_interface *ev_int = iio_dev_opaque->event_interface;
unsigned int copied;
int ret;
if (!indio_dev->info)
return -ENODEV;
if (count < sizeof(struct iio_event_data))
return -EINVAL;
do {
if (kfifo_is_empty(&ev_int->det_events)) {
if (filep->f_flags & O_NONBLOCK)
return -EAGAIN;
ret = wait_event_interruptible(ev_int->wait,
!kfifo_is_empty(&ev_int->det_events) ||
indio_dev->info == NULL);
if (ret)
return ret;
if (indio_dev->info == NULL)
return -ENODEV;
}
if (mutex_lock_interruptible(&ev_int->read_lock))
return -ERESTARTSYS;
ret = kfifo_to_user(&ev_int->det_events, buf, count, &copied);
mutex_unlock(&ev_int->read_lock);
if (ret)
return ret;
/*
* If we couldn't read anything from the fifo (a different
* thread might have been faster) we either return -EAGAIN if
* the file descriptor is non-blocking, otherwise we go back to
* sleep and wait for more data to arrive.
*/
if (copied == 0 && (filep->f_flags & O_NONBLOCK))
return -EAGAIN;
} while (copied == 0);
return copied;
}Re: [PATCH v5 3/5] counter: Add character device interface
Quoted 27 lines · David Lechner
Quoted 26 lines · William Breathitt Gray
drivers/counter/counter-chrdev.c new file +26
-0+static ssize_t counter_chrdev_read (struct file *filp,char __user *buf, +size_t len,loff_t *f_ps) +{ +struct counter_device *const counter = filp->private_data; +int err; +unsigned long flags; +unsigned int copied; + +if (len <sizeof (struct counter_event )) +return -EINVAL ; + + do { + if (kfifo_is_empty(&counter->events)) { + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + + err = wait_event_interruptible(counter->events_wait, + !kfifo_is_empty(&counter->events)); + if (err) + return err; + } + + raw_spin_lock_irqsave(&counter->events_lock, flags); + err = kfifo_to_user(&counter->events, buf, len, &copied); + raw_spin_unlock_irqrestore(&counter->events_lock, flags); + if (err)
Quoted 8 lines
drivers/counter/counter-chrdev.c line 71
Quoted 5 lines
drivers/counter/counter-chrdev.c new file +5
-0+return err; + }while (!copied); + +return copied; +}All other uses of
kfifo_to_user()I saw use a mutex instead of spin lock. I don't see a reason for disabling interrupts here.
The Counter character device interface is special in this case because counter->events could be accessed from an interrupt context. This is possible if counter_push_event() is called for an interrupt (as is the case for the 104_quad_8 driver). In this case, we can't use mutex because we can't sleep in an interrupt context, so our only option is to use spin lock.
William Breathitt Gray
David Lechner
reviewerRe: [PATCH v5 3/5] counter: Add character device interface
Quoted 37 lines · William Breathitt Gray
Quoted 27 lines · David Lechner
Quoted 26 lines · William Breathitt Gray
drivers/counter/counter-chrdev.c new file +26
-0+static ssize_t counter_chrdev_read (struct file *filp,char __user *buf, +size_t len,loff_t *f_ps) +{ +struct counter_device *const counter = filp->private_data; +int err; +unsigned long flags; +unsigned int copied; + +if (len <sizeof (struct counter_event )) +return -EINVAL ; + + do { + if (kfifo_is_empty(&counter->events)) { + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + + err = wait_event_interruptible(counter->events_wait, + !kfifo_is_empty(&counter->events)); + if (err) + return err; + } + + raw_spin_lock_irqsave(&counter->events_lock, flags); + err = kfifo_to_user(&counter->events, buf, len, &copied); + raw_spin_unlock_irqrestore(&counter->events_lock, flags); + if (err)Quoted 8 lines
drivers/counter/counter-chrdev.c line 71
Quoted 5 lines
drivers/counter/counter-chrdev.c new file +5
-0+return err; + }while (!copied); + +return copied; +}All other uses of
kfifo_to_user()I saw use a mutex instead of spin lock. I don't see a reason for disabling interrupts here.
Quoted 7 lines
The Counter character device interface is special in this case because
counter->eventscould be accessed from an interrupt context. This is possible ifcounter_push_event()is called for an interrupt (as is the case for the 104_quad_8 driver). In this case, we can't use mutex because we can't sleep in an interrupt context, so our only option is to use spin lock.
The way I understand it, locking is only needed for concurrent readers and locking between reader and writer is not needed.
Re: [PATCH v5 3/5] counter: Add character device interface
Quoted 47 lines · David Lechner
Quoted 37 lines · William Breathitt Gray
Quoted 27 lines · David Lechner
Quoted 26 lines · William Breathitt Gray
drivers/counter/counter-chrdev.c new file +26
-0+static ssize_t counter_chrdev_read (struct file *filp,char __user *buf, +size_t len,loff_t *f_ps) +{ +struct counter_device *const counter = filp->private_data; +int err; +unsigned long flags; +unsigned int copied; + +if (len <sizeof (struct counter_event )) +return -EINVAL ; + + do { + if (kfifo_is_empty(&counter->events)) { + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + + err = wait_event_interruptible(counter->events_wait, + !kfifo_is_empty(&counter->events)); + if (err) + return err; + } + + raw_spin_lock_irqsave(&counter->events_lock, flags); + err = kfifo_to_user(&counter->events, buf, len, &copied); + raw_spin_unlock_irqrestore(&counter->events_lock, flags); + if (err)Quoted 8 lines
drivers/counter/counter-chrdev.c line 71
Quoted 5 lines
drivers/counter/counter-chrdev.c new file +5
-0+return err; + }while (!copied); + +return copied; +}All other uses of
kfifo_to_user()I saw use a mutex instead of spin lock. I don't see a reason for disabling interrupts here.Quoted 7 lines
The Counter character device interface is special in this case because
counter->eventscould be accessed from an interrupt context. This is possible ifcounter_push_event()is called for an interrupt (as is the case for the 104_quad_8 driver). In this case, we can't use mutex because we can't sleep in an interrupt context, so our only option is to use spin lock.
Quoted 2 lines
The way I understand it, locking is only needed for concurrent readers and locking between reader and writer is not needed.
You're right, it does say in the kfifo.h comments that with only one concurrent reader and one current write, we don't need extra locking to use these macros. Because we only have one kfifo_to_user() operating on counter->events, does that mean we don't need locking at all here for the counter_chrdev_read() function?
William Breathitt Gray
David Lechner
reviewerRe: [PATCH v5 3/5] counter: Add character device interface
Quoted 51 lines · William Breathitt Gray
Quoted 47 lines · David Lechner
Quoted 37 lines · William Breathitt Gray
Quoted 27 lines · David Lechner
Quoted 26 lines · William Breathitt Gray
drivers/counter/counter-chrdev.c new file +26
-0+static ssize_t counter_chrdev_read (struct file *filp,char __user *buf, +size_t len,loff_t *f_ps) +{ +struct counter_device *const counter = filp->private_data; +int err; +unsigned long flags; +unsigned int copied; + +if (len <sizeof (struct counter_event )) +return -EINVAL ; + + do { + if (kfifo_is_empty(&counter->events)) { + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + + err = wait_event_interruptible(counter->events_wait, + !kfifo_is_empty(&counter->events)); + if (err) + return err; + } + + raw_spin_lock_irqsave(&counter->events_lock, flags); + err = kfifo_to_user(&counter->events, buf, len, &copied); + raw_spin_unlock_irqrestore(&counter->events_lock, flags); + if (err)Quoted 8 lines
drivers/counter/counter-chrdev.c line 71
Quoted 5 lines
drivers/counter/counter-chrdev.c new file +5
-0+return err; + }while (!copied); + +return copied; +}All other uses of
kfifo_to_user()I saw use a mutex instead of spin lock. I don't see a reason for disabling interrupts here.Quoted 7 lines
The Counter character device interface is special in this case because
counter->eventscould be accessed from an interrupt context. This is possible ifcounter_push_event()is called for an interrupt (as is the case for the 104_quad_8 driver). In this case, we can't use mutex because we can't sleep in an interrupt context, so our only option is to use spin lock.Quoted 2 lines
The way I understand it, locking is only needed for concurrent readers and locking between reader and writer is not needed.
Quoted 8 lines
You're right, it does say in the
kfifo.hcomments that with only one concurrent reader and one current write, we don't need extra locking to use these macros. Because we only have onekfifo_to_user()operating oncounter->events, does that mean we don't need locking at all here for thecounter_chrdev_read()function?William Breathitt Gray
Even if we have the policy that only one file handle to the chrdev can be open at a time, it is still possible that the it could be read from multiple threads. So it I think it makes sense to keep it just to be safe.
Re: [PATCH v5 3/5] counter: Add character device interface
Quoted 58 lines · David Lechner
Quoted 57 lines · William Breathitt Gray
Quoted 47 lines · David Lechner
Quoted 37 lines · William Breathitt Gray
Quoted 27 lines · David Lechner
Quoted 26 lines · William Breathitt Gray
drivers/counter/counter-chrdev.c new file +26
-0+static ssize_t counter_chrdev_read (struct file *filp,char __user *buf, +size_t len,loff_t *f_ps) +{ +struct counter_device *const counter = filp->private_data; +int err; +unsigned long flags; +unsigned int copied; + +if (len <sizeof (struct counter_event )) +return -EINVAL ; + + do { + if (kfifo_is_empty(&counter->events)) { + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + + err = wait_event_interruptible(counter->events_wait, + !kfifo_is_empty(&counter->events)); + if (err) + return err; + } + + raw_spin_lock_irqsave(&counter->events_lock, flags); + err = kfifo_to_user(&counter->events, buf, len, &copied); + raw_spin_unlock_irqrestore(&counter->events_lock, flags); + if (err)Quoted 8 lines
drivers/counter/counter-chrdev.c line 71
Quoted 5 lines
drivers/counter/counter-chrdev.c new file +5
-0+return err; + }while (!copied); + +return copied; +}All other uses of
kfifo_to_user()I saw use a mutex instead of spin lock. I don't see a reason for disabling interrupts here.Quoted 7 lines
The Counter character device interface is special in this case because
counter->eventscould be accessed from an interrupt context. This is possible ifcounter_push_event()is called for an interrupt (as is the case for the 104_quad_8 driver). In this case, we can't use mutex because we can't sleep in an interrupt context, so our only option is to use spin lock.Quoted 2 lines
The way I understand it, locking is only needed for concurrent readers and locking between reader and writer is not needed.
You're right, it does say in the
kfifo.hcomments that with only one concurrent reader and one current write, we don't need extra locking to use these macros. Because we only have onekfifo_to_user()operating oncounter->events, does that mean we don't need locking at all here for thecounter_chrdev_read()function?
Quoted 7 lines
Quoted 2 lines
William Breathitt Gray
Even if we have the policy that only one file handle to the chrdev can be open at a time, it is still possible that the it could be read from multiple threads. So it I think it makes sense to keep it just to be safe.
All right, I'll keep the locks in the code for now to keep it safe in case we have multiple threads reading.
William Breathitt Gray
PATCH v5 4/5 docs: counter: Document character device interface
In reply to William Breathitt GrayThis patch adds high-level documentation about the Counter subsystem character device interface.
--- Documentation/ABI/testing/sysfs-bus-counter | 18 ++ Documentation/driver-api/generic-counter.rst | 228 ++++++++++++++---- .../userspace-api/ioctl/ioctl-number.rst | 1 + 3 files changed, 206 insertions(+), 41 deletions(-)
Documentation/ABI/testing/sysfs-bus-counter +18 -0
@@ -99,6 +99,24 @@ Description: Read-only attribute that indicates whether excessive noise is present at the channel Y counter inputs. +What: /sys/bus/counter/devices/counterX/countY/extensionZ_name +What: /sys/bus/counter/devices/counterX/extensionZ_name +What: /sys/bus/counter/devices/counterX/signalY/extensionZ_name +KernelVersion: 5.11 +Contact: [email protected] +Description: + Read-only attribute that indicates the component name of + Extension Z. + +What: /sys/bus/counter/devices/counterX/countY/extensionZ_width +What: /sys/bus/counter/devices/counterX/extensionZ_width +What: /sys/bus/counter/devices/counterX/signalY/extensionZ_width +KernelVersion: 5.11 +Contact: [email protected] +Description: + Read-only attribute that indicates the data width of value of + Extension Z. + What: /sys/bus/counter/devices/counterX/countY/function KernelVersion: 5.2 Contact: [email protected]
Documentation/driver-api/generic-counter.rst +187 -41
255 lines not on this page · raw message
Documentation/userspace-api/ioctl/ioctl-number.rst +1 -0
@@ -88,6 +88,7 @@ Code Seq# Include File Comments <http://infiniband.sourceforge.net/> 0x20 all drivers/cdrom/cm206.h 0x22 all scsi/sg.h +0x3E 00-0F linux/counter.h <mailto:[email protected]> '!' 00-1F uapi/linux/seccomp.h '#' 00-3F IEEE 1394 Subsystem Block for the entire subsystem
Signature · 1 line
2.28.0
Re: [PATCH v5 4/5] docs: counter: Document character device interface
Hi!
Documentation/driver-api/generic-counter.rst and vice versa. · line 607
Quoted 13 lines
Documentation/driver-api/generic-counter.rst +13
-0+ int main(void) + { + struct pollfd pfd = { .events = POLLIN }; + struct counter_event event_data[2]; + + pfd.fd = open("/dev/counter0", O_RDWR); + + ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches); + ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches + 1); + ioctl(pfd.fd, COUNTER_LOAD_WATCHES_IOCTL); + + for (;;) { + poll(&pfd, 1, -1);
Why do poll, when you are doing blocking read?
Quoted 1 line
+
read(pfd.fd, event_data, sizeof(event_data));
Does your new chrdev always guarantee returning complete buffer?
If so, should it behave like that?
Best regards,
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [PATCH v5 4/5] docs: counter: Document character device interface
Quoted 18 lines · Pavel Machek
Hi!
Documentation/driver-api/generic-counter.rst and vice versa. · line 607
Quoted 13 lines
Documentation/driver-api/generic-counter.rst +13
-0+ int main(void) + { + struct pollfd pfd = { .events = POLLIN }; + struct counter_event event_data[2]; + + pfd.fd = open("/dev/counter0", O_RDWR); + + ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches); + ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches + 1); + ioctl(pfd.fd, COUNTER_LOAD_WATCHES_IOCTL); + + for (;;) { + poll(&pfd, 1, -1);Why do poll, when you are doing blocking read?
Quoted 11 lines
Quoted 1 line
+
read(pfd.fd, event_data, sizeof(event_data));Does your new chrdev always guarantee returning complete buffer?
If so, should it behave like that?
Best regards,
PavelSignature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
I suppose you're right: a poll() should be redundant now with this version of the character device implementation because buffers will always return complete; so a blocking read() should achieve the same behavior that a poll() with read() would.
I'll give some more time for additional feedback to come in for this version of the patchset, and then likely remove support for poll() in the v6 submission.
William Breathitt Gray
David Lechner
reviewerRe: [PATCH v5 4/5] docs: counter: Document character device interface
Quoted 36 lines · William Breathitt Gray
Quoted 18 lines · Pavel Machek
Hi!
Documentation/driver-api/generic-counter.rst and vice versa. · line 607
Quoted 13 lines
Documentation/driver-api/generic-counter.rst +13
-0+ int main(void) + { + struct pollfd pfd = { .events = POLLIN }; + struct counter_event event_data[2]; + + pfd.fd = open("/dev/counter0", O_RDWR); + + ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches); + ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches + 1); + ioctl(pfd.fd, COUNTER_LOAD_WATCHES_IOCTL); + + for (;;) { + poll(&pfd, 1, -1);Why do poll, when you are doing blocking read?
Quoted 11 lines
Quoted 1 line
+
read(pfd.fd, event_data, sizeof(event_data));Does your new chrdev always guarantee returning complete buffer?
If so, should it behave like that?
Best regards,
PavelSignature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.htmlI suppose you're right: a
poll()should be redundant now with this version of the character device implementation because buffers will always return complete; so a blockingread()should achieve the same behavior that apoll()withread()would.
Quoted 6 lines
I'll give some more time for additional feedback to come in for this version of the patchset, and then likely remove support for
poll()in the v6 submission.William Breathitt Gray
I hope that you mean that you will just remove it from the example and not from the chardev. Otherwise it won't be possible to integrate this with an event loop.
Re: [PATCH v5 4/5] docs: counter: Document character device interface
Quoted 41 lines · David Lechner
Quoted 40 lines · William Breathitt Gray
Quoted 18 lines · Pavel Machek
Hi!
Documentation/driver-api/generic-counter.rst and vice versa. · line 607
Quoted 13 lines
Documentation/driver-api/generic-counter.rst +13
-0+ int main(void) + { + struct pollfd pfd = { .events = POLLIN }; + struct counter_event event_data[2]; + + pfd.fd = open("/dev/counter0", O_RDWR); + + ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches); + ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches + 1); + ioctl(pfd.fd, COUNTER_LOAD_WATCHES_IOCTL); + + for (;;) { + poll(&pfd, 1, -1);Why do poll, when you are doing blocking read?
Quoted 11 lines
Quoted 1 line
+
read(pfd.fd, event_data, sizeof(event_data));Does your new chrdev always guarantee returning complete buffer?
If so, should it behave like that?
Best regards,
PavelSignature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.htmlI suppose you're right: a
poll()should be redundant now with this version of the character device implementation because buffers will always return complete; so a blockingread()should achieve the same behavior that apoll()withread()would.I'll give some more time for additional feedback to come in for this version of the patchset, and then likely remove support for
poll()in the v6 submission.
Quoted 6 lines
Quoted 2 lines
William Breathitt Gray
I hope that you mean that you will just remove it from the example and not from the chardev. Otherwise it won't be possible to integrate this with an event loop.
Would you elaborate a bit further on this? My thought process is that because users must set the Counter Events they want to watch, and only those Counter Events show up in the character device node, a blocking read() would effectively behave the same as poll() with read(); if none of the Counter Events occur, the read() just blocks until one does, thus making the use of a poll() call redundant.
William Breathitt Gray
David Lechner
reviewerRe: [PATCH v5 4/5] docs: counter: Document character device interface
Quoted 49 lines · William Breathitt Gray
Quoted 41 lines · David Lechner
Quoted 40 lines · William Breathitt Gray
Quoted 18 lines · Pavel Machek
Hi!
Documentation/driver-api/generic-counter.rst and vice versa. · line 607
Quoted 13 lines
Documentation/driver-api/generic-counter.rst +13
-0+ int main(void) + { + struct pollfd pfd = { .events = POLLIN }; + struct counter_event event_data[2]; + + pfd.fd = open("/dev/counter0", O_RDWR); + + ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches); + ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches + 1); + ioctl(pfd.fd, COUNTER_LOAD_WATCHES_IOCTL); + + for (;;) { + poll(&pfd, 1, -1);Why do poll, when you are doing blocking read?
Quoted 11 lines
Quoted 1 line
+
read(pfd.fd, event_data, sizeof(event_data));Does your new chrdev always guarantee returning complete buffer?
If so, should it behave like that?
Best regards,
PavelSignature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.htmlI suppose you're right: a
poll()should be redundant now with this version of the character device implementation because buffers will always return complete; so a blockingread()should achieve the same behavior that apoll()withread()would.I'll give some more time for additional feedback to come in for this version of the patchset, and then likely remove support for
poll()in the v6 submission.Quoted 6 lines
Quoted 2 lines
William Breathitt Gray
I hope that you mean that you will just remove it from the example and not from the chardev. Otherwise it won't be possible to integrate this with an event loop.
Quoted 9 lines
Would you elaborate a bit further on this? My thought process is that because users must set the Counter Events they want to watch, and only those Counter Events show up in the character device node, a blocking
read()would effectively behave the same aspoll()withread(); if none of the Counter Events occur, theread()just blocks until one does, thus making the use of apoll()call redundant.William Breathitt Gray
If the counter device was the only file descriptor being read, then yes it wouldn't matter. But if we are using this in combination with other file descriptors, then it is common to poll all of the file descriptors using a single syscall to see which one is ready to read rather than doing a non-blocking read on all of the file descriptors, which would result in many unnecessary syscalls.
Re: [PATCH v5 4/5] docs: counter: Document character device interface
Quoted 60 lines · David Lechner
Quoted 49 lines · William Breathitt Gray
Quoted 41 lines · David Lechner
Quoted 40 lines · William Breathitt Gray
Quoted 18 lines · Pavel Machek
Hi!
Documentation/driver-api/generic-counter.rst and vice versa. · line 607
Quoted 13 lines
Documentation/driver-api/generic-counter.rst +13
-0+ int main(void) + { + struct pollfd pfd = { .events = POLLIN }; + struct counter_event event_data[2]; + + pfd.fd = open("/dev/counter0", O_RDWR); + + ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches); + ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches + 1); + ioctl(pfd.fd, COUNTER_LOAD_WATCHES_IOCTL); + + for (;;) { + poll(&pfd, 1, -1);Why do poll, when you are doing blocking read?
Quoted 11 lines
Quoted 1 line
+
read(pfd.fd, event_data, sizeof(event_data));Does your new chrdev always guarantee returning complete buffer?
If so, should it behave like that?
Best regards,
PavelSignature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.htmlI suppose you're right: a
poll()should be redundant now with this version of the character device implementation because buffers will always return complete; so a blockingread()should achieve the same behavior that apoll()withread()would.I'll give some more time for additional feedback to come in for this version of the patchset, and then likely remove support for
poll()in the v6 submission.Quoted 6 lines
Quoted 2 lines
William Breathitt Gray
I hope that you mean that you will just remove it from the example and not from the chardev. Otherwise it won't be possible to integrate this with an event loop.
Quoted 9 lines
Would you elaborate a bit further on this? My thought process is that because users must set the Counter Events they want to watch, and only those Counter Events show up in the character device node, a blocking
read()would effectively behave the same aspoll()withread(); if none of the Counter Events occur, theread()just blocks until one does, thus making the use of apoll()call redundant.William Breathitt Gray
Quoted 6 lines
If the counter device was the only file descriptor being read, then yes it wouldn't matter. But if we are using this in combination with other file descriptors, then it is common to poll all of the file descriptors using a single syscall to see which one is ready to read rather than doing a non-blocking read on all of the file descriptors, which would result in many unnecessary syscalls.
Ah, that's a fair point, my original view was somewhat myopic there. I'll leave poll support in the Counter chrdev then and just simplify the documentation example code to not use it.
William Breathitt Gray
PATCH v5 5/5 counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8
In reply to William Breathitt GrayThe LSI/CSI LS7266R1 chip provides programmable output via the FLG pins. When interrupts are enabled on the ACCES 104-QUAD-8, they occur whenever FLG1 is active. Four functions are available for the FLG1 signal: Carry, Compare, Carry-Borrow, and Index.
Carry:
Interrupt generated on active low Carry signal. Carry signal toggles every time the respective channel's counter overflows.
Compare:
Interrupt generated on active low Compare signal. Compare signal toggles every time respective channel's preset register is equal to the respective channel's counter.
Carry-Borrow:
Interrupt generated on active low Carry signal and active low Borrow signal. Carry signal toggles every time the respective channel's counter overflows. Borrow signal toggles every time the respective channel's counter underflows.
Index:
Interrupt generated on active high Index signal.
The irq_trigger Count extension is introduced to allow the selection of the desired IRQ trigger function per channel. The irq_trigger_enable Count extension is introduced to allow the enablement of interrupts for a respective channel. Interrupts push Counter events to event channel X, where 'X' is the respective channel whose FLG1 activated.
This patch adds IRQ support for the ACCES 104-QUAD-8. The interrupt line numbers for the devices may be configured via the irq array module parameter.
--- .../ABI/testing/sysfs-bus-counter-104-quad-8 | 32 ++ drivers/counter/104-quad-8.c | 303 ++++++++++++++---- drivers/counter/Kconfig | 6 +- 3 files changed, 270 insertions(+), 71 deletions(-)
Documentation/ABI/testing/sysfs-bus-counter-104-quad-8 +32 -0
@@ -1,3 +1,35 @@ +What: /sys/bus/counter/devices/counterX/countY/irq_trigger +KernelVersion: 5.11 +Contact: [email protected] +Description: + IRQ trigger function for channel Y. Four trigger functions are + available: carry, compare, carry-borrow, and index. + + carry: + Interrupt generated on active low Carry signal. Carry + signal toggles every time channel Y counter overflows. + + compare: + Interrupt generated on active low Compare signal. + Compare signal toggles every time channel Y preset + register is equal to channel Y counter. + + carry-borrow: + Interrupt generated on active low Carry signal and + active low Borrow signal. Carry signal toggles every + time channel Y counter overflows. Borrow signal toggles + every time channel Y counter underflows. + + index: + Interrupt generated on active high Index signal. + +What: /sys/bus/counter/devices/counterX/countY/irq_trigger_enable +KernelVersion: 5.11 +Contact: [email protected] +Description: + Whether generation of interrupts is enabled for channel Y. Valid + attribute values are boolean. + What: /sys/bus/counter/devices/counterX/signalY/cable_fault KernelVersion: 5.7 Contact: [email protected]
drivers/counter/104-quad-8.c +235 -68
797 lines not on this page · raw message
drivers/counter/Kconfig +3 -3
@@ -23,11 +23,11 @@ config 104_QUAD_8 A counter's respective error flag may be cleared by performing a write operation on the respective count value attribute. Although the 104-QUAD-8 counters have a 25-bit range, only the lower 24 bits may be - set, either directly or via the counter's preset attribute. Interrupts - are not supported by this driver. + set, either directly or via the counter's preset attribute. The base port addresses for the devices may be configured via the base - array module parameter. + array module parameter. The interrupt line numbers for the devices may + be configured via the irq array module parameter. config STM32_TIMER_CNT tristate "STM32 Timer encoder counter driver"
Signature · 1 line
2.28.0
David Lechner
reviewerRe: [PATCH v5 5/5] counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8
Quoted 20 lines · William Breathitt Gray
drivers/counter/104-quad-8.c +20
-0+static irqreturn_t quad8_irq_handler (int irq,void *quad8iio) +{ +struct quad8_iio *const priv = quad8iio; +const unsigned long base = priv->base; +unsigned long irq_status; +unsigned long channel; +u8 event; +int err; + + irq_status =inb (base +QUAD8_REG_INTERRUPT_STATUS ); +if (!irq_status) +return IRQ_NONE ; + +for_each_set_bit (channel, &irq_status,QUAD8_NUM_COUNTERS ) { +switch (priv->irq_trigger[channel]) { +case 0 : + event =COUNTER_EVENT_OVERFLOW ; +break ; +case 1 : + event =COUNTER_EVENT_THRESHOLD ;
drivers/counter/104-quad-8.c static struct counter_count quad8_counts[] = { · line 1608
Quoted 10 lines
drivers/counter/104-quad-8.c +10
-0+break ; +case 2 : + event =COUNTER_EVENT_OVERFLOW_UNDERFLOW ; +break ; +case 3 : + event =COUNTER_EVENT_INDEX ; +break ; +default : +/* We should never reach here */ +return -EINVAL ;
This is not a valid return value for an IRQ handler. Maybe WARN_ONCE instead?
drivers/counter/104-quad-8.c static struct counter_count quad8_counts[] = { · line 1612
Quoted 4 lines
drivers/counter/104-quad-8.c +4
-0+ } + err =counter_push_event (&priv->counter, event, channel); +if (err) +return err;
Same here. Otherwise, I think we could end up with interrupts in an endless loop since the interrupt would never be cleared.
Quoted 8 lines
drivers/counter/104-quad-8.c +8
-0+ } + +/* Clear pending interrupts on device */ +outb (QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC , base +QUAD8_REG_CHAN_OP ); + +return IRQ_HANDLED ; +} +
Re: [PATCH v5 5/5] counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8
Quoted 25 lines · David Lechner
Quoted 24 lines · William Breathitt Gray
drivers/counter/104-quad-8.c +24
-0+static irqreturn_t quad8_irq_handler (int irq,void *quad8iio) +{ +struct quad8_iio *const priv = quad8iio; +const unsigned long base = priv->base; +unsigned long irq_status; +unsigned long channel; +u8 event; +int err; + + irq_status =inb (base +QUAD8_REG_INTERRUPT_STATUS ); +if (!irq_status) +return IRQ_NONE ; + +for_each_set_bit (channel, &irq_status,QUAD8_NUM_COUNTERS ) { +switch (priv->irq_trigger[channel]) { +case 0 : + event =COUNTER_EVENT_OVERFLOW ; +break ; +case 1 : + event =COUNTER_EVENT_THRESHOLD ; +break ; +case 2 : + event =COUNTER_EVENT_OVERFLOW_UNDERFLOW ; +break ;
Quoted 8 lines
drivers/counter/104-quad-8.c static struct counter_count quad8_counts[] = { · line 1608
Quoted 6 lines
drivers/counter/104-quad-8.c +6
-0+case 3 : + event =COUNTER_EVENT_INDEX ; +break ; +default : +/* We should never reach here */ +return -EINVAL ;This is not a valid return value for an IRQ handler. Maybe
WARN_ONCEinstead?
Ack.
Quoted 7 lines
drivers/counter/104-quad-8.c static struct counter_count quad8_counts[] = { · line 1612
Quoted 4 lines
drivers/counter/104-quad-8.c +4
-0+ } + err =counter_push_event (&priv->counter, event, channel); +if (err) +return err;Same here. Otherwise, I think we could end up with interrupts in an endless loop since the interrupt would never be cleared.
Good point, I'll change this and the one above.
William Breathitt Gray
Quoted 9 lines
Quoted 8 lines
drivers/counter/104-quad-8.c +8
-0+ } + +/* Clear pending interrupts on device */ +outb (QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC , base +QUAD8_REG_CHAN_OP ); + +return IRQ_HANDLED ; +} +
David Lechner
reviewerQuoted 9 lines · William Breathitt Gray
The following are some questions I have about this patchset:
1. Should standard Counter component data types be defined as u8 or u32?
Many standard Counter component types such
COUNTER_COMP_SIGNAL_LEVELhave standard values defined (e.g.COUNTER_SIGNAL_LEVEL_LOWandCOUNTER_SIGNAL_LEVEL_HIGH). These values are currently handled by the Counter subsystem code as u8 data types.
Quoted 8 lines
If u32 is used for these values instead, C enum structures could be used by driver authors to implicit cast these values via the driver callback parameters; userspace would still use u32 with no issue.
In theory this can work because GCC will treat enums are having a 32-bit size; but I worry about the possibility of build targets that have -fshort-enums enabled, resulting in enums having a size less than 32 bits. Would this be a problem?
We shouldn't have to worry about userspace programs using -fshort-enums since that would break all kernel interfaces that use enums, not just these - so no one should be using that compiler flag.
So I am in favor of using strongly typed enums with u32 as the "generic" enum member type.
Quoted 8 lines
2. Should I have reserved members in the userspace structures?
The structures in
include/uapi/linux/counter.hare available to userspace applications. Should I reserve space in these structures for future additions and usage? Will endianess and packing be a concern here?
Since there doesn't seem to be a large number of counter devices this probably isn't critical. Are there any aspects of counter devices in general that couldn't be described with the proposed structures? For example, could there be components more than two levels deep (i.e. it would need id, parent id and grandparent id to describe fully)?
_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Quoted 19 lines · David Lechner
Quoted 9 lines · William Breathitt Gray
The following are some questions I have about this patchset:
1. Should standard Counter component data types be defined as u8 or u32?
Many standard Counter component types such
COUNTER_COMP_SIGNAL_LEVELhave standard values defined (e.g.COUNTER_SIGNAL_LEVEL_LOWandCOUNTER_SIGNAL_LEVEL_HIGH). These values are currently handled by the Counter subsystem code as u8 data types.Quoted 8 lines
If u32 is used for these values instead, C enum structures could be used by driver authors to implicit cast these values via the driver callback parameters; userspace would still use u32 with no issue.
In theory this can work because GCC will treat enums are having a 32-bit size; but I worry about the possibility of build targets that have -fshort-enums enabled, resulting in enums having a size less than 32 bits. Would this be a problem?
Quoted 6 lines
We shouldn't have to worry about userspace programs using -fshort-enums since that would break all kernel interfaces that use enums, not just these - so no one should be using that compiler flag.
So I am in favor of using strongly typed enums with u32 as the "generic" enum member type.
That reasoning pacifies my worries. I'll test out strongly typed enums in the next revision of this patchset.
Quoted 8 lines
Quoted 8 lines
2. Should I have reserved members in the userspace structures?
The structures in
include/uapi/linux/counter.hare available to userspace applications. Should I reserve space in these structures for future additions and usage? Will endianess and packing be a concern here?
Quoted 6 lines
Since there doesn't seem to be a large number of counter devices this probably isn't critical. Are there any aspects of counter devices in general that couldn't be described with the proposed structures? For example, could there be components more than two levels deep (i.e. it would need id, parent id and grandparent id to describe fully)?
I can't think of any devices that would require more depth, so we probably don't need any additional members added to the userspace structures. The current structure should be flexible enough for future expansion, and any additional functionality we come across can be handled by implementing new extension types as we have for the sysfs attributes.
William Breathitt Gray