linux-iio.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v5 0/5] Introduce the Counter character device interface
@ 2020-09-27  2:18 William Breathitt Gray
  2020-09-27  2:18 ` [PATCH v5 2/5] docs: counter: Update to reflect sysfs internalization William Breathitt Gray
                   ` (5 more replies)
  0 siblings, 6 replies; 35+ messages in thread
From: William Breathitt Gray @ 2020-09-27  2:18 UTC (permalink / raw)
  To: jic23
  Cc: kamel.bouhara, gwendal, alexandre.belloni, david, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue, William Breathitt Gray

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

-- 
2.28.0


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v5 2/5] docs: counter: Update to reflect sysfs internalization
  2020-09-27  2:18 [PATCH v5 0/5] Introduce the Counter character device interface William Breathitt Gray
@ 2020-09-27  2:18 ` William Breathitt Gray
  2020-09-27  2:18 ` [PATCH v5 3/5] counter: Add character device interface William Breathitt Gray
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 35+ messages in thread
From: William Breathitt Gray @ 2020-09-27  2:18 UTC (permalink / raw)
  To: jic23
  Cc: kamel.bouhara, gwendal, alexandre.belloni, david, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue, William Breathitt Gray

The 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.

Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 Documentation/driver-api/generic-counter.rst | 242 ++++++++++++++-----
 1 file changed, 176 insertions(+), 66 deletions(-)

diff --git a/Documentation/driver-api/generic-counter.rst b/Documentation/driver-api/generic-counter.rst
index b02c52cd69d6..b842ddbbd8a0 100644
--- a/Documentation/driver-api/generic-counter.rst
+++ b/Documentation/driver-api/generic-counter.rst
@@ -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.
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v5 3/5] counter: Add character device interface
  2020-09-27  2:18 [PATCH v5 0/5] Introduce the Counter character device interface William Breathitt Gray
  2020-09-27  2:18 ` [PATCH v5 2/5] docs: counter: Update to reflect sysfs internalization William Breathitt Gray
@ 2020-09-27  2:18 ` William Breathitt Gray
  2020-10-14  1:40   ` David Lechner
                     ` (2 more replies)
  2020-09-27  2:18 ` [PATCH v5 4/5] docs: counter: Document " William Breathitt Gray
                   ` (3 subsequent siblings)
  5 siblings, 3 replies; 35+ messages in thread
From: William Breathitt Gray @ 2020-09-27  2:18 UTC (permalink / raw)
  To: jic23
  Cc: kamel.bouhara, gwendal, alexandre.belloni, david, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue, 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.

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;
    }

Cc: David Lechner <david@lechnology.com>
Cc: Gwendal Grignou <gwendal@chromium.org>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 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

diff --git a/MAINTAINERS b/MAINTAINERS
index 4969e997061a..de6685a683c8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -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 <f.fainelli@gmail.com>
diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile
index cbe1d06af6a9..c4870eb5b1dd 100644
--- a/drivers/counter/Makefile
+++ b/drivers/counter/Makefile
@@ -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
diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c
new file mode 100644
index 000000000000..2be3846e4105
--- /dev/null
+++ b/drivers/counter/counter-chrdev.c
@@ -0,0 +1,451 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generic Counter character device interface
+ * Copyright (C) 2020 William Breathitt Gray
+ */
+
+#include <linux/cdev.h>
+#include <linux/counter.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/poll.h>
+#include <linux/kdev_t.h>
+#include <linux/kfifo.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/timekeeping.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+
+#include "counter-chrdev.h"
+
+struct counter_comp_node {
+	struct list_head l;
+	struct counter_component component;
+	struct counter_comp comp;
+	void *parent;
+};
+
+struct counter_event_node {
+	struct list_head l;
+	u8 event;
+	u8 channel;
+	struct list_head comp_list;
+};
+
+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)
+			return err;
+	} while (!copied);
+
+	return copied;
+}
+
+static __poll_t counter_chrdev_poll(struct file *filp,
+				    struct poll_table_struct *pollt)
+{
+	struct counter_device *const counter = filp->private_data;
+	__poll_t events = 0;
+
+	poll_wait(filp, &counter->events_wait, pollt);
+
+	if (!kfifo_is_empty(&counter->events))
+		events = EPOLLIN | EPOLLRDNORM;
+
+	return events;
+}
+
+static void counter_events_list_free(struct list_head *const events_list)
+{
+	struct counter_event_node *p, *n;
+	struct counter_comp_node *q, *o;
+
+	list_for_each_entry_safe(p, n, events_list, l) {
+		/* Free associated component nodes */
+		list_for_each_entry_safe(q, o, &p->comp_list, l) {
+			list_del(&q->l);
+			kfree(q);
+		}
+
+		/* Free event node */
+		list_del(&p->l);
+		kfree(p);
+	}
+}
+
+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);
+	}
+
+	/* 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;
+
+	/* Allocate component node */
+	comp_node = kmalloc(sizeof(*comp_node), GFP_ATOMIC);
+	if (!comp_node) {
+		err = -ENOMEM;
+		goto err_comp_node;
+	}
+	*comp_node = *cfg;
+
+	/* Add component node to event node */
+	list_add_tail(&comp_node->l, &event_node->comp_list);
+
+	return 0;
+
+err_comp_node:
+	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;
+
+	/* 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)
+			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)
+			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;
+		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)
+			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)
+			return -EINVAL;
+
+		comp_node.comp = ext[id];
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (!comp_node.comp.count_u8_read)
+		return -EFAULT;
+	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;
+	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);
+		break;
+	case COUNTER_SET_WATCH_IOCTL:
+		return counter_set_watch(counter, arg);
+	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);
+		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)
+{
+	struct counter_device *const counter = filp->private_data;
+	unsigned long flags;
+
+	put_device(&counter->dev);
+
+	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;
+}
+
+static const struct file_operations counter_fops = {
+	.llseek = no_llseek,
+	.read = counter_chrdev_read,
+	.poll = counter_chrdev_poll,
+	.unlocked_ioctl = counter_chrdev_ioctl,
+	.open = counter_chrdev_open,
+	.release = counter_chrdev_release,
+};
+
+int counter_chrdev_add(struct counter_device *const counter,
+		       const dev_t counter_devt)
+{
+	struct device *const dev = &counter->dev;
+	struct cdev *const chrdev = &counter->chrdev;
+
+	/* Initialize Counter events lists */
+	INIT_LIST_HEAD(&counter->events_list);
+	INIT_LIST_HEAD(&counter->next_events_list);
+	raw_spin_lock_init(&counter->events_lock);
+
+	/* Initialize Counter events queue */
+	INIT_KFIFO(counter->events);
+	init_waitqueue_head(&counter->events_wait);
+
+	/* Initialize character device */
+	cdev_init(chrdev, &counter_fops);
+	dev->devt = MKDEV(MAJOR(counter_devt), counter->id);
+	cdev_set_parent(chrdev, &dev->kobj);
+
+	return cdev_add(chrdev, dev->devt, 1);
+}
+
+void counter_chrdev_remove(struct counter_device *const counter)
+{
+	cdev_del(&counter->chrdev);
+}
+
+static int counter_get_data(struct counter_device *const counter,
+			    const struct counter_comp_node *const comp_node,
+			    void *const value)
+{
+	const struct counter_comp *const comp = &comp_node->comp;
+	void *const parent = comp_node->parent;
+
+	switch (comp->type) {
+	case COUNTER_COMP_U8:
+	case COUNTER_COMP_BOOL:
+	case COUNTER_COMP_SIGNAL_LEVEL:
+	case COUNTER_COMP_FUNCTION:
+	case COUNTER_COMP_ENUM:
+	case COUNTER_COMP_COUNT_DIRECTION:
+	case COUNTER_COMP_COUNT_MODE:
+		switch (comp_node->component.scope) {
+		case COUNTER_SCOPE_DEVICE:
+			return comp->device_u8_read(counter, value);
+		case COUNTER_SCOPE_SIGNAL:
+			return comp->signal_u8_read(counter, parent, value);
+		case COUNTER_SCOPE_COUNT:
+			return comp->count_u8_read(counter, parent, value);
+		}
+		break;
+	case COUNTER_COMP_U64:
+		switch (comp_node->component.scope) {
+		case COUNTER_SCOPE_DEVICE:
+			return comp->device_u64_read(counter, value);
+		case COUNTER_SCOPE_SIGNAL:
+			return comp->signal_u64_read(counter, parent, value);
+		case COUNTER_SCOPE_COUNT:
+			return comp->count_u64_read(counter, parent, value);
+		}
+		break;
+	case COUNTER_COMP_SYNAPSE_ACTION:
+		return comp->action_read(counter, parent, comp->priv, value);
+	}
+
+	return 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 &&
+		    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);
+
+	return 0;
+
+err_counter_get_data:
+	raw_spin_unlock_irqrestore(&counter->events_lock, flags);
+	return err;
+}
+EXPORT_SYMBOL_GPL(counter_push_event);
diff --git a/drivers/counter/counter-chrdev.h b/drivers/counter/counter-chrdev.h
new file mode 100644
index 000000000000..cf5a318fe540
--- /dev/null
+++ b/drivers/counter/counter-chrdev.h
@@ -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_ */
diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c
index 987c6e8277eb..e9c01de36fcf 100644
--- a/drivers/counter/counter-core.c
+++ b/drivers/counter/counter-core.c
@@ -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);
 }
 
diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c
index e66ed99dd5ea..cefef61f170d 100644
--- a/drivers/counter/counter-sysfs.c
+++ b/drivers/counter/counter-sysfs.c
@@ -434,6 +434,7 @@ static ssize_t counter_comp_name_show(struct device *dev,
 
 static int counter_name_attr_create(struct device *const dev,
 				    struct counter_attribute_group *const group,
+				    const char *const attr_name,
 				    const char *const name)
 {
 	struct counter_attribute *counter_attr;
@@ -448,7 +449,7 @@ static int counter_name_attr_create(struct device *const dev,
 
 	/* Configure device attribute */
 	sysfs_attr_init(&counter_attr->dev_attr.attr);
-	counter_attr->dev_attr.attr.name = "name";
+	counter_attr->dev_attr.attr.name = attr_name;
 	counter_attr->dev_attr.attr.mode = 0444;
 	counter_attr->dev_attr.show = counter_comp_name_show;
 
@@ -459,6 +460,76 @@ static int counter_name_attr_create(struct device *const dev,
 	return 0;
 }
 
+static ssize_t counter_comp_width_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	switch (to_counter_attribute(attr)->comp.type) {
+	case COUNTER_COMP_U8:
+	case COUNTER_COMP_BOOL:
+	case COUNTER_COMP_SIGNAL_LEVEL:
+	case COUNTER_COMP_FUNCTION:
+	case COUNTER_COMP_SYNAPSE_ACTION:
+	case COUNTER_COMP_ENUM:
+	case COUNTER_COMP_COUNT_DIRECTION:
+	case COUNTER_COMP_COUNT_MODE:
+		return sprintf(buf, "8\n");
+	case COUNTER_COMP_U64:
+		return sprintf(buf, "64\n");
+	}
+
+	return 0;
+}
+
+static int counter_ext_width_attr_create(struct device *const dev,
+	struct counter_attribute_group *const group, const size_t i,
+	const struct counter_comp *const ext)
+{
+	struct counter_attribute *counter_attr;
+	const char *name;
+
+	/* Allocate Counter attribute */
+	counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
+	if (!counter_attr)
+		return -ENOMEM;
+
+	/* Generate attribute name */
+	name = devm_kasprintf(dev, GFP_KERNEL, "extension%zu_width", i);
+	if (!name)
+		return -ENOMEM;
+
+	/* Configure Counter attribute */
+	counter_attr->comp.type = ext->type;
+
+	/* Configure device attribute */
+	sysfs_attr_init(&counter_attr->dev_attr.attr);
+	counter_attr->dev_attr.attr.name = name;
+	counter_attr->dev_attr.attr.mode = 0444;
+	counter_attr->dev_attr.show = counter_comp_width_show;
+
+	/* Store list node */
+	list_add(&counter_attr->l, &group->attr_list);
+	group->num_attr++;
+
+	return 0;
+}
+
+static int counter_ext_meta_attrs_create(struct device *const dev,
+	struct counter_attribute_group *const group, const size_t i,
+	const struct counter_comp *const ext)
+{
+	const char *attr_name;
+	int err;
+
+	attr_name = devm_kasprintf(dev, GFP_KERNEL, "extension%zu_name", i);
+	if (!attr_name)
+		return -ENOMEM;
+
+	err = counter_name_attr_create(dev, group, attr_name, ext->name);
+	if (err)
+		return err;
+
+	return counter_ext_width_attr_create(dev, group, i, ext);
+}
 
 static struct counter_comp counter_signal_comp = {
 	.type = COUNTER_COMP_SIGNAL_LEVEL,
@@ -474,6 +545,7 @@ static int counter_signal_attrs_create(struct counter_device *const counter,
 	int err;
 	struct counter_comp comp;
 	size_t i;
+	struct counter_comp *ext;
 
 	/* Create main Signal attribute */
 	comp = counter_signal_comp;
@@ -483,14 +555,19 @@ static int counter_signal_attrs_create(struct counter_device *const counter,
 		return err;
 
 	/* Create Signal name attribute */
-	err = counter_name_attr_create(dev, group, signal->name);
+	err = counter_name_attr_create(dev, group, "name", signal->name);
 	if (err)
 		return err;
 
 	/* Create an attribute for each extension */
 	for (i = 0; i < signal->num_ext; i++) {
-		err = counter_attr_create(dev, group, signal->ext + i, scope,
-					  signal);
+		ext = signal->ext + i;
+
+		err = counter_attr_create(dev, group, ext, scope, signal);
+		if (err)
+			return err;
+
+		err = counter_ext_meta_attrs_create(dev, group, i, ext);
 		if (err)
 			return err;
 	}
@@ -575,6 +652,7 @@ static int counter_count_attrs_create(struct counter_device *const counter,
 	int err;
 	struct counter_comp comp;
 	size_t i;
+	struct counter_comp *ext;
 
 	/* Create main Count attribute */
 	comp = counter_count_comp;
@@ -585,7 +663,7 @@ static int counter_count_attrs_create(struct counter_device *const counter,
 		return err;
 
 	/* Create Count name attribute */
-	err = counter_name_attr_create(dev, group, count->name);
+	err = counter_name_attr_create(dev, group, "name", count->name);
 	if (err)
 		return err;
 
@@ -599,8 +677,13 @@ static int counter_count_attrs_create(struct counter_device *const counter,
 
 	/* Create an attribute for each extension */
 	for (i = 0; i < count->num_ext; i++) {
-		err = counter_attr_create(dev, group, count->ext + i, scope,
-					  count);
+		ext = count->ext + i;
+
+		err = counter_attr_create(dev, group, ext, scope, count);
+		if (err)
+			return err;
+
+		err = counter_ext_meta_attrs_create(dev, group, i, ext);
 		if (err)
 			return err;
 	}
@@ -664,6 +747,7 @@ static int counter_device_register(struct counter_device *const counter,
 	struct device *const dev = &counter->dev;
 	int err;
 	size_t i;
+	struct counter_comp *ext;
 
 	/* Register Signals */
 	err = counter_signals_register(counter, group);
@@ -678,7 +762,7 @@ static int counter_device_register(struct counter_device *const counter,
 	group += counter->num_counts;
 
 	/* Create name attribute */
-	err = counter_name_attr_create(dev, group, counter->name);
+	err = counter_name_attr_create(dev, group, "name", counter->name);
 	if (err)
 		return err;
 
@@ -696,8 +780,13 @@ static int counter_device_register(struct counter_device *const counter,
 
 	/* Create an attribute for each extension */
 	for (i = 0; i < counter->num_ext; i++) {
-		err = counter_attr_create(dev, group, counter->ext + i, scope,
-					  NULL);
+		ext = counter->ext + i;
+
+		err = counter_attr_create(dev, group, ext, scope, NULL);
+		if (err)
+			return err;
+
+		err = counter_ext_meta_attrs_create(dev, group, i, ext);
 		if (err)
 			return err;
 	}
diff --git a/include/linux/counter.h b/include/linux/counter.h
index 132bfecca5c3..25d79047c9fc 100644
--- a/include/linux/counter.h
+++ b/include/linux/counter.h
@@ -6,10 +6,15 @@
 #ifndef _COUNTER_H_
 #define _COUNTER_H_
 
+#include <linux/cdev.h>
 #include <linux/device.h>
 #include <linux/kernel.h>
+#include <linux/kfifo.h>
 #include <linux/list.h>
+#include <linux/spinlock_types.h>
 #include <linux/types.h>
+#include <linux/wait.h>
+#include <uapi/linux/counter.h>
 
 struct counter_device;
 struct counter_count;
@@ -28,35 +33,6 @@ enum counter_comp_type {
 	COUNTER_COMP_COUNT_MODE,
 };
 
-#define COUNTER_SCOPE_DEVICE 0
-#define COUNTER_SCOPE_SIGNAL 1
-#define COUNTER_SCOPE_COUNT 2
-
-#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
-
-#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
-
 struct counter_comp {
 	enum counter_comp_type type;
 	const char *name;
@@ -205,6 +181,12 @@ struct counter_ops {
  * @priv:		optional private data supplied by driver
  * @id:			unique ID used to identify the Counter
  * @dev:		internal device structure
+ * @chrdev:		internal character device structure
+ * @events_lock:	synchronization lock for Counter events
+ * @events_list:	list of current watching Counter events
+ * @next_events_list:	list of next watching Counter events
+ * @events:		queue of detected Counter events
+ * @events_wait:	wait queue to allow blocking reads of Counter events
  */
 struct counter_device {
 	const char *name;
@@ -224,6 +206,12 @@ struct counter_device {
 
 	int id;
 	struct device dev;
+	struct cdev chrdev;
+	raw_spinlock_t events_lock;
+	struct list_head events_list;
+	struct list_head next_events_list;
+	DECLARE_KFIFO(events, struct counter_event, 64);
+	wait_queue_head_t events_wait;
 };
 
 int counter_register(struct counter_device *const counter);
@@ -232,6 +220,8 @@ int devm_counter_register(struct device *dev,
 			  struct counter_device *const counter);
 void devm_counter_unregister(struct device *dev,
 			     struct counter_device *const counter);
+int counter_push_event(struct counter_device *const counter, const u8 event,
+		       const u8 channel);
 
 #define COUNTER_COMP_DEVICE_U8(_name, _read, _write) \
 { \
diff --git a/include/uapi/linux/counter.h b/include/uapi/linux/counter.h
new file mode 100644
index 000000000000..ad9a8686b2b0
--- /dev/null
+++ b/include/uapi/linux/counter.h
@@ -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>
+
+#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
+
+/**
+ * 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;
+};
+
+#define COUNTER_EVENT_OVERFLOW 0
+#define COUNTER_EVENT_UNDERFLOW 1
+#define COUNTER_EVENT_OVERFLOW_UNDERFLOW 2
+#define COUNTER_EVENT_THRESHOLD 3
+#define COUNTER_EVENT_INDEX 4
+
+/**
+ * struct counter_watch - Counter component watch configuration
+ * @event: event that triggers
+ * @channel: event channel
+ * @component: component to watch when event triggers
+ */
+struct counter_watch {
+	__u8 event;
+	__u8 channel;
+	struct counter_component component;
+};
+
+#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;
+	struct counter_watch watch;
+	union {
+		__u8 value_u8;
+		__u64 value_u64;
+	};
+};
+
+#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
+
+#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_ */
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v5 4/5] docs: counter: Document character device interface
  2020-09-27  2:18 [PATCH v5 0/5] Introduce the Counter character device interface William Breathitt Gray
  2020-09-27  2:18 ` [PATCH v5 2/5] docs: counter: Update to reflect sysfs internalization William Breathitt Gray
  2020-09-27  2:18 ` [PATCH v5 3/5] counter: Add character device interface William Breathitt Gray
@ 2020-09-27  2:18 ` William Breathitt Gray
  2020-10-08  8:09   ` Pavel Machek
  2020-09-27  2:18 ` [PATCH v5 5/5] counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8 William Breathitt Gray
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 35+ messages in thread
From: William Breathitt Gray @ 2020-09-27  2:18 UTC (permalink / raw)
  To: jic23
  Cc: kamel.bouhara, gwendal, alexandre.belloni, david, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue, William Breathitt Gray

This patch adds high-level documentation about the Counter subsystem
character device interface.

Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 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(-)

diff --git a/Documentation/ABI/testing/sysfs-bus-counter b/Documentation/ABI/testing/sysfs-bus-counter
index 566bd99fe0a5..b7fdb14ae891 100644
--- a/Documentation/ABI/testing/sysfs-bus-counter
+++ b/Documentation/ABI/testing/sysfs-bus-counter
@@ -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:	linux-iio@vger.kernel.org
+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:	linux-iio@vger.kernel.org
+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:	linux-iio@vger.kernel.org
diff --git a/Documentation/driver-api/generic-counter.rst b/Documentation/driver-api/generic-counter.rst
index b842ddbbd8a0..6077bf162ac3 100644
--- a/Documentation/driver-api/generic-counter.rst
+++ b/Documentation/driver-api/generic-counter.rst
@@ -223,19 +223,6 @@ whether an input line is differential or single-ended) and instead focus
 on the core idea of what the data and process represent (e.g. position
 as interpreted from quadrature encoding data).
 
-Userspace Interface
-===================
-
-Several sysfs attributes are generated by the Generic Counter interface,
-and reside under the /sys/bus/counter/devices/counterX directory, where
-counterX refers to the respective counter device. Please see
-Documentation/ABI/testing/sysfs-bus-counter for detailed
-information on each Generic Counter interface sysfs attribute.
-
-Through these sysfs attributes, programs and scripts may interact with
-the Generic Counter paradigm Counts, Signals, and Synapses of respective
-counter devices.
-
 Driver API
 ==========
 
@@ -387,16 +374,16 @@ userspace interface components::
                         / driver callbacks /
                         -------------------
                                 |
-                +---------------+
-                |
-                V
-        +--------------------+
-        | Counter sysfs      |
-        +--------------------+
-        | Translates to the  |
-        | standard Counter   |
-        | sysfs output       |
-        +--------------------+
+                +---------------+---------------+
+                |                               |
+                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::
@@ -427,23 +414,30 @@ driver and Counter userspace interface::
                         / u64     /
                         ----------
                                 |
-                +---------------+
-                |
-                V
-        +--------------------+
-        | Counter sysfs      |
-        +--------------------+
-        | Translates to the  |
-        | standard Counter   |
-        | sysfs output       |
-        |--------------------|
-        | Type: const char * |
-        | Value: "42"        |
-        +--------------------+
-                |
-         ---------------
-        / const char * /
-        ---------------
+                +---------------+---------------+
+                |                               |
+                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
         +--------------------------------------------------+
@@ -452,7 +446,7 @@ driver and Counter userspace interface::
         \ Count: "42"                                      /
          --------------------------------------------------
 
-There are three primary components involved:
+There are four primary components involved:
 
 Counter device driver
 ---------------------
@@ -472,3 +466,155 @@ 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.
+
+Counter chrdev
+--------------
+Translates counter data to the standard Counter character device; data
+is transferred via standard character device read calls, while Counter
+events are configured via ioctl calls.
+
+Sysfs Interface
+===============
+
+Several sysfs attributes are generated by the Generic Counter interface,
+and reside under the `/sys/bus/counter/devices/counterX` directory,
+where `X` is to the respective counter device id. Please see
+`Documentation/ABI/testing/sysfs-bus-counter` for detailed information
+on each Generic Counter interface sysfs attribute.
+
+Through these sysfs attributes, programs and scripts may interact with
+the Generic Counter paradigm Counts, Signals, and Synapses of respective
+counter devices.
+
+Counter Character Device
+========================
+
+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;
+        }
diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
index 2a198838fca9..f6e96bb780cd 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
@@ -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:linux-iio@vger.kernel.org>
 '!'   00-1F  uapi/linux/seccomp.h
 '#'   00-3F                                                          IEEE 1394 Subsystem
                                                                      Block for the entire subsystem
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v5 5/5] counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8
  2020-09-27  2:18 [PATCH v5 0/5] Introduce the Counter character device interface William Breathitt Gray
                   ` (2 preceding siblings ...)
  2020-09-27  2:18 ` [PATCH v5 4/5] docs: counter: Document " William Breathitt Gray
@ 2020-09-27  2:18 ` William Breathitt Gray
  2020-10-14  0:13   ` David Lechner
  2020-10-13  0:35 ` [PATCH v5 0/5] Introduce the Counter character device interface David Lechner
       [not found] ` <e38f6dc3a08bf2510034334262776a6ed1df8b89.1601170670.git.vilhelm.gray@gmail.com>
  5 siblings, 1 reply; 35+ messages in thread
From: William Breathitt Gray @ 2020-09-27  2:18 UTC (permalink / raw)
  To: jic23
  Cc: kamel.bouhara, gwendal, alexandre.belloni, david, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue, William Breathitt Gray

The 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.

Reviewed-by: Syed Nayyar Waris <syednwaris@gmail.com>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 .../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(-)

diff --git a/Documentation/ABI/testing/sysfs-bus-counter-104-quad-8 b/Documentation/ABI/testing/sysfs-bus-counter-104-quad-8
index eac32180c40d..995bf08d365d 100644
--- a/Documentation/ABI/testing/sysfs-bus-counter-104-quad-8
+++ b/Documentation/ABI/testing/sysfs-bus-counter-104-quad-8
@@ -1,3 +1,35 @@
+What:		/sys/bus/counter/devices/counterX/countY/irq_trigger
+KernelVersion:	5.11
+Contact:	linux-iio@vger.kernel.org
+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:	linux-iio@vger.kernel.org
+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:	linux-iio@vger.kernel.org
diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c
index 92a3809609e9..d0be75a539dd 100644
--- a/drivers/counter/104-quad-8.c
+++ b/drivers/counter/104-quad-8.c
@@ -13,23 +13,30 @@
 #include <linux/iio/types.h>
 #include <linux/io.h>
 #include <linux/ioport.h>
+#include <linux/interrupt.h>
 #include <linux/isa.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/types.h>
+#include <linux/spinlock.h>
 
 #define QUAD8_EXTENT 32
 
 static unsigned int base[max_num_isa_dev(QUAD8_EXTENT)];
 static unsigned int num_quad8;
-module_param_array(base, uint, &num_quad8, 0);
+module_param_hw_array(base, uint, ioport, &num_quad8, 0);
 MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
 
+static unsigned int irq[max_num_isa_dev(QUAD8_EXTENT)];
+module_param_hw_array(irq, uint, irq, NULL, 0);
+MODULE_PARM_DESC(irq, "ACCES 104-QUAD-8 interrupt line numbers");
+
 #define QUAD8_NUM_COUNTERS 8
 
 /**
  * struct quad8_iio - IIO device private data structure
+ * @lock:		synchronization lock to prevent I/O race conditions
  * @counter:		instance of the counter_device
  * @fck_prescaler:	array of filter clock prescaler configurations
  * @preset:		array of preset values
@@ -38,13 +45,14 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
  * @quadrature_scale:	array of quadrature mode scale configurations
  * @ab_enable:		array of A and B inputs enable configurations
  * @preset_enable:	array of set_to_preset_on_index attribute configurations
+ * @irq_trigger:	array of interrupt trigger function configurations
  * @synchronous_mode:	array of index function synchronous mode configurations
  * @index_polarity:	array of index function polarity configurations
  * @cable_fault_enable:	differential encoder cable status enable configurations
  * @base:		base port address of the IIO device
  */
 struct quad8_iio {
-	struct mutex lock;
+	raw_spinlock_t lock;
 	struct counter_device counter;
 	unsigned int fck_prescaler[QUAD8_NUM_COUNTERS];
 	unsigned int preset[QUAD8_NUM_COUNTERS];
@@ -53,13 +61,16 @@ struct quad8_iio {
 	unsigned int quadrature_scale[QUAD8_NUM_COUNTERS];
 	unsigned int ab_enable[QUAD8_NUM_COUNTERS];
 	unsigned int preset_enable[QUAD8_NUM_COUNTERS];
+	unsigned int irq_trigger[QUAD8_NUM_COUNTERS];
 	unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
 	unsigned int index_polarity[QUAD8_NUM_COUNTERS];
 	unsigned int cable_fault_enable;
 	unsigned int base;
 };
 
+#define QUAD8_REG_INTERRUPT_STATUS 0x10
 #define QUAD8_REG_CHAN_OP 0x11
+#define QUAD8_REG_INDEX_INTERRUPT 0x12
 #define QUAD8_REG_INDEX_INPUT_LEVELS 0x16
 #define QUAD8_DIFF_ENCODER_CABLE_STATUS 0x17
 /* Borrow Toggle flip-flop */
@@ -92,8 +103,8 @@ struct quad8_iio {
 #define QUAD8_RLD_CNTR_OUT 0x10
 /* Transfer Preset Register LSB to FCK Prescaler */
 #define QUAD8_RLD_PRESET_PSC 0x18
-#define QUAD8_CHAN_OP_ENABLE_COUNTERS 0x00
 #define QUAD8_CHAN_OP_RESET_COUNTERS 0x01
+#define QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC 0x04
 #define QUAD8_CMR_QUADRATURE_X1 0x08
 #define QUAD8_CMR_QUADRATURE_X2 0x10
 #define QUAD8_CMR_QUADRATURE_X4 0x18
@@ -107,6 +118,7 @@ static int quad8_read_raw(struct iio_dev *indio_dev,
 	unsigned int flags;
 	unsigned int borrow;
 	unsigned int carry;
+	unsigned long irqflags;
 	int i;
 
 	switch (mask) {
@@ -124,7 +136,7 @@ static int quad8_read_raw(struct iio_dev *indio_dev,
 		/* Borrow XOR Carry effectively doubles count range */
 		*val = (borrow ^ carry) << 24;
 
-		mutex_lock(&priv->lock);
+		raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 		/* Reset Byte Pointer; transfer Counter to Output Latch */
 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
@@ -133,7 +145,7 @@ static int quad8_read_raw(struct iio_dev *indio_dev,
 		for (i = 0; i < 3; i++)
 			*val |= (unsigned int)inb(base_offset) << (8 * i);
 
-		mutex_unlock(&priv->lock);
+		raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 		return IIO_VAL_INT;
 	case IIO_CHAN_INFO_ENABLE:
@@ -153,6 +165,7 @@ static int quad8_write_raw(struct iio_dev *indio_dev,
 {
 	struct quad8_iio *const priv = iio_priv(indio_dev);
 	const int base_offset = priv->base + 2 * chan->channel;
+	unsigned long flags;
 	int i;
 	unsigned int ior_cfg;
 
@@ -165,7 +178,7 @@ static int quad8_write_raw(struct iio_dev *indio_dev,
 		if ((unsigned int)val > 0xFFFFFF)
 			return -EINVAL;
 
-		mutex_lock(&priv->lock);
+		raw_spin_lock_irqsave(&priv->lock, flags);
 
 		/* Reset Byte Pointer */
 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
@@ -190,7 +203,7 @@ static int quad8_write_raw(struct iio_dev *indio_dev,
 		/* Reset Error flag */
 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
 
-		mutex_unlock(&priv->lock);
+		raw_spin_unlock_irqrestore(&priv->lock, flags);
 
 		return 0;
 	case IIO_CHAN_INFO_ENABLE:
@@ -198,25 +211,26 @@ static int quad8_write_raw(struct iio_dev *indio_dev,
 		if (val < 0 || val > 1)
 			return -EINVAL;
 
-		mutex_lock(&priv->lock);
+		raw_spin_lock_irqsave(&priv->lock, flags);
 
 		priv->ab_enable[chan->channel] = val;
 
-		ior_cfg = val | priv->preset_enable[chan->channel] << 1;
+		ior_cfg = val | priv->preset_enable[chan->channel] << 1 |
+			  priv->irq_trigger[chan->channel] << 3;
 
 		/* Load I/O control configuration */
 		outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
 
-		mutex_unlock(&priv->lock);
+		raw_spin_unlock_irqrestore(&priv->lock, flags);
 
 		return 0;
 	case IIO_CHAN_INFO_SCALE:
-		mutex_lock(&priv->lock);
+		raw_spin_lock_irqsave(&priv->lock, flags);
 
 		/* Quadrature scaling only available in quadrature mode */
 		if (!priv->quadrature_mode[chan->channel] &&
 				(val2 || val != 1)) {
-			mutex_unlock(&priv->lock);
+			raw_spin_unlock_irqrestore(&priv->lock, flags);
 			return -EINVAL;
 		}
 
@@ -232,15 +246,15 @@ static int quad8_write_raw(struct iio_dev *indio_dev,
 				priv->quadrature_scale[chan->channel] = 2;
 				break;
 			default:
-				mutex_unlock(&priv->lock);
+				raw_spin_unlock_irqrestore(&priv->lock, flags);
 				return -EINVAL;
 			}
 		else {
-			mutex_unlock(&priv->lock);
+			raw_spin_unlock_irqrestore(&priv->lock, flags);
 			return -EINVAL;
 		}
 
-		mutex_unlock(&priv->lock);
+		raw_spin_unlock_irqrestore(&priv->lock, flags);
 		return 0;
 	}
 
@@ -266,6 +280,7 @@ static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private,
 	struct quad8_iio *const priv = iio_priv(indio_dev);
 	const int base_offset = priv->base + 2 * chan->channel;
 	unsigned int preset;
+	unsigned long irqflags;
 	int ret;
 	int i;
 
@@ -277,7 +292,7 @@ static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private,
 	if (preset > 0xFFFFFF)
 		return -EINVAL;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	priv->preset[chan->channel] = preset;
 
@@ -288,7 +303,7 @@ static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private,
 	for (i = 0; i < 3; i++)
 		outb(preset >> (8 * i), base_offset);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return len;
 }
@@ -309,6 +324,7 @@ static ssize_t quad8_write_set_to_preset_on_index(struct iio_dev *indio_dev,
 	struct quad8_iio *const priv = iio_priv(indio_dev);
 	const int base_offset = priv->base + 2 * chan->channel + 1;
 	bool preset_enable;
+	unsigned long irqflags;
 	int ret;
 	unsigned int ior_cfg;
 
@@ -319,17 +335,18 @@ static ssize_t quad8_write_set_to_preset_on_index(struct iio_dev *indio_dev,
 	/* Preset enable is active low in Input/Output Control register */
 	preset_enable = !preset_enable;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	priv->preset_enable[chan->channel] = preset_enable;
 
 	ior_cfg = priv->ab_enable[chan->channel] |
-		(unsigned int)preset_enable << 1;
+		  (unsigned int)preset_enable << 1 |
+		  priv->irq_trigger[chan->channel] << 3;
 
 	/* Load I/O control configuration to Input / Output Control Register */
 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return len;
 }
@@ -387,8 +404,9 @@ static int quad8_set_count_mode(struct iio_dev *indio_dev,
 	struct quad8_iio *const priv = iio_priv(indio_dev);
 	unsigned int mode_cfg = cnt_mode << 1;
 	const int base_offset = priv->base + 2 * chan->channel + 1;
+	unsigned long irqflags;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	priv->count_mode[chan->channel] = cnt_mode;
 
@@ -399,7 +417,7 @@ static int quad8_set_count_mode(struct iio_dev *indio_dev,
 	/* Load mode configuration to Counter Mode Register */
 	outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -430,14 +448,15 @@ static int quad8_set_synchronous_mode(struct iio_dev *indio_dev,
 	struct quad8_iio *const priv = iio_priv(indio_dev);
 	const int base_offset = priv->base + 2 * chan->channel + 1;
 	unsigned int idr_cfg = synchronous_mode;
+	unsigned long irqflags;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	idr_cfg |= priv->index_polarity[chan->channel] << 1;
 
 	/* Index function must be non-synchronous in non-quadrature mode */
 	if (synchronous_mode && !priv->quadrature_mode[chan->channel]) {
-		mutex_unlock(&priv->lock);
+		raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 		return -EINVAL;
 	}
 
@@ -446,7 +465,7 @@ static int quad8_set_synchronous_mode(struct iio_dev *indio_dev,
 	/* Load Index Control configuration to Index Control Register */
 	outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -476,9 +495,10 @@ static int quad8_set_quadrature_mode(struct iio_dev *indio_dev,
 {
 	struct quad8_iio *const priv = iio_priv(indio_dev);
 	const int base_offset = priv->base + 2 * chan->channel + 1;
+	unsigned long irqflags;
 	unsigned int mode_cfg;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	mode_cfg = priv->count_mode[chan->channel] << 1;
 
@@ -498,7 +518,7 @@ static int quad8_set_quadrature_mode(struct iio_dev *indio_dev,
 	/* Load mode configuration to Counter Mode Register */
 	outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -529,8 +549,9 @@ static int quad8_set_index_polarity(struct iio_dev *indio_dev,
 	struct quad8_iio *const priv = iio_priv(indio_dev);
 	const int base_offset = priv->base + 2 * chan->channel + 1;
 	unsigned int idr_cfg = index_polarity << 1;
+	unsigned long irqflags;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	idr_cfg |= priv->synchronous_mode[chan->channel];
 
@@ -539,7 +560,7 @@ static int quad8_set_index_polarity(struct iio_dev *indio_dev,
 	/* Load Index Control configuration to Index Control Register */
 	outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -646,6 +667,7 @@ static int quad8_count_read(struct counter_device *counter,
 	unsigned int flags;
 	unsigned int borrow;
 	unsigned int carry;
+	unsigned long irqflags;
 	int i;
 
 	flags = inb(base_offset + 1);
@@ -655,7 +677,7 @@ static int quad8_count_read(struct counter_device *counter,
 	/* Borrow XOR Carry effectively doubles count range */
 	*val = (unsigned long)(borrow ^ carry) << 24;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	/* Reset Byte Pointer; transfer Counter to Output Latch */
 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
@@ -664,7 +686,7 @@ static int quad8_count_read(struct counter_device *counter,
 	for (i = 0; i < 3; i++)
 		*val |= (unsigned long)inb(base_offset) << (8 * i);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -674,13 +696,14 @@ static int quad8_count_write(struct counter_device *counter,
 {
 	struct quad8_iio *const priv = counter->priv;
 	const int base_offset = priv->base + 2 * count->id;
+	unsigned long irqflags;
 	int i;
 
 	/* Only 24-bit values are supported */
 	if (val > 0xFFFFFF)
 		return -EINVAL;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	/* Reset Byte Pointer */
 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
@@ -705,7 +728,7 @@ static int quad8_count_write(struct counter_device *counter,
 	/* Reset Error flag */
 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -723,8 +746,9 @@ static int quad8_function_read(struct counter_device *counter,
 {
 	struct quad8_iio *const priv = counter->priv;
 	const int id = count->id;
+	unsigned long irqflags;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	if (priv->quadrature_mode[id])
 		switch (priv->quadrature_scale[id]) {
@@ -741,7 +765,7 @@ static int quad8_function_read(struct counter_device *counter,
 	else
 		*function = COUNTER_FUNCTION_PULSE_DIRECTION;
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -755,10 +779,11 @@ static int quad8_function_write(struct counter_device *counter,
 	unsigned int *const scale = priv->quadrature_scale + id;
 	unsigned int *const synchronous_mode = priv->synchronous_mode + id;
 	const int base_offset = priv->base + 2 * id + 1;
+	unsigned long irqflags;
 	unsigned int mode_cfg;
 	unsigned int idr_cfg;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	mode_cfg = priv->count_mode[id] << 1;
 	idr_cfg = priv->index_polarity[id] << 1;
@@ -797,7 +822,7 @@ static int quad8_function_write(struct counter_device *counter,
 	/* Load mode configuration to Counter Mode Register */
 	outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -915,9 +940,10 @@ static int quad8_index_polarity_set(struct counter_device *counter,
 	struct quad8_iio *const priv = counter->priv;
 	const size_t channel_id = signal->id - 16;
 	const int base_offset = priv->base + 2 * channel_id + 1;
+	unsigned long irqflags;
 	unsigned int idr_cfg = index_polarity << 1;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	idr_cfg |= priv->synchronous_mode[channel_id];
 
@@ -926,7 +952,7 @@ static int quad8_index_polarity_set(struct counter_device *counter,
 	/* Load Index Control configuration to Index Control Register */
 	outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -950,15 +976,16 @@ static int quad8_synchronous_mode_set(struct counter_device *counter,
 	struct quad8_iio *const priv = counter->priv;
 	const size_t channel_id = signal->id - 16;
 	const int base_offset = priv->base + 2 * channel_id + 1;
+	unsigned long irqflags;
 	unsigned int idr_cfg = synchronous_mode;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	idr_cfg |= priv->index_polarity[channel_id] << 1;
 
 	/* Index function must be non-synchronous in non-quadrature mode */
 	if (synchronous_mode && !priv->quadrature_mode[channel_id]) {
-		mutex_unlock(&priv->lock);
+		raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 		return -EINVAL;
 	}
 
@@ -967,7 +994,7 @@ static int quad8_synchronous_mode_set(struct counter_device *counter,
 	/* Load Index Control configuration to Index Control Register */
 	outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -1012,6 +1039,7 @@ static int quad8_count_mode_write(struct counter_device *counter,
 	unsigned int count_mode;
 	unsigned int mode_cfg;
 	const int base_offset = priv->base + 2 * count->id + 1;
+	unsigned long irqflags;
 
 	/* Map Generic Counter count mode to 104-QUAD-8 count mode */
 	switch (cnt_mode) {
@@ -1029,7 +1057,7 @@ static int quad8_count_mode_write(struct counter_device *counter,
 		break;
 	}
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	priv->count_mode[count->id] = count_mode;
 
@@ -1043,7 +1071,7 @@ static int quad8_count_mode_write(struct counter_device *counter,
 	/* Load mode configuration to Counter Mode Register */
 	outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -1063,18 +1091,20 @@ static int quad8_count_enable_write(struct counter_device *counter,
 {
 	struct quad8_iio *const priv = counter->priv;
 	const int base_offset = priv->base + 2 * count->id;
+	unsigned long irqflags;
 	unsigned int ior_cfg;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	priv->ab_enable[count->id] = enable;
 
-	ior_cfg = enable | priv->preset_enable[count->id] << 1;
+	ior_cfg = enable | priv->preset_enable[count->id] << 1 |
+		  priv->irq_trigger[count->id] << 3;
 
 	/* Load I/O control configuration */
 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -1120,16 +1150,17 @@ static int quad8_count_preset_write(struct counter_device *counter,
 				    struct counter_count *count, u64 preset)
 {
 	struct quad8_iio *const priv = counter->priv;
+	unsigned long irqflags;
 
 	/* Only 24-bit values are supported */
 	if (preset > 0xFFFFFF)
 		return -EINVAL;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	quad8_preset_register_set(priv, count->id, preset);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -1138,8 +1169,9 @@ static int quad8_count_ceiling_read(struct counter_device *counter,
 				    struct counter_count *count, u64 *ceiling)
 {
 	struct quad8_iio *const priv = counter->priv;
+	unsigned long irqflags;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
 	switch (priv->count_mode[count->id]) {
@@ -1153,7 +1185,7 @@ static int quad8_count_ceiling_read(struct counter_device *counter,
 		break;
 	}
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -1162,12 +1194,13 @@ static int quad8_count_ceiling_write(struct counter_device *counter,
 				     struct counter_count *count, u64 ceiling)
 {
 	struct quad8_iio *const priv = counter->priv;
+	unsigned long irqflags;
 
 	/* Only 24-bit values are supported */
 	if (ceiling > 0xFFFFFF)
 		return -EINVAL;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
 	switch (priv->count_mode[count->id]) {
@@ -1177,7 +1210,7 @@ static int quad8_count_ceiling_write(struct counter_device *counter,
 		break;
 	}
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return -EINVAL;
 }
@@ -1199,21 +1232,91 @@ static int quad8_count_preset_enable_write(struct counter_device *counter,
 {
 	struct quad8_iio *const priv = counter->priv;
 	const int base_offset = priv->base + 2 * count->id + 1;
+	unsigned long irqflags;
 	unsigned int ior_cfg;
 
 	/* Preset enable is active low in Input/Output Control register */
 	preset_enable = !preset_enable;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	priv->preset_enable[count->id] = preset_enable;
 
-	ior_cfg = priv->ab_enable[count->id] | preset_enable << 1;
+	ior_cfg = priv->ab_enable[count->id] | preset_enable << 1 |
+		  priv->irq_trigger[count->id] << 3;
+
+	/* Load I/O control configuration to Input / Output Control Register */
+	outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
+
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
+
+	return 0;
+}
+
+static int quad8_irq_trigger_get(struct counter_device *counter,
+				 struct counter_count *count, u8 *irq_trigger)
+{
+	const struct quad8_iio *const priv = counter->priv;
+
+	*irq_trigger = priv->irq_trigger[count->id];
+
+	return 0;
+}
+
+static int quad8_irq_trigger_set(struct counter_device *counter,
+				 struct counter_count *count, u8 irq_trigger)
+{
+	struct quad8_iio *const priv = counter->priv;
+	const unsigned long base_offset = priv->base + 2 * count->id + 1;
+	unsigned long irqflags;
+	unsigned long ior_cfg;
+
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
+
+	priv->irq_trigger[count->id] = irq_trigger;
+
+	ior_cfg = priv->ab_enable[count->id] |
+		  priv->preset_enable[count->id] << 1 | irq_trigger << 3;
 
 	/* Load I/O control configuration to Input / Output Control Register */
 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
+
+	return 0;
+}
+
+static int quad8_irq_trigger_enable_read(struct counter_device *counter,
+					 struct counter_count *count, u8 *state)
+{
+	const struct quad8_iio *const priv = counter->priv;
+	unsigned long irq_enabled;
+
+	irq_enabled = inb(priv->base + QUAD8_REG_INDEX_INTERRUPT);
+	*state = !!(irq_enabled & BIT(count->id));
+
+	return 0;
+}
+
+static int quad8_irq_trigger_enable_write(struct counter_device *counter,
+				    struct counter_count *count, u8 state)
+{
+	struct quad8_iio *const priv = counter->priv;
+	unsigned long irqflags;
+	unsigned long irq_enabled;
+
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
+
+	irq_enabled = inb(priv->base + QUAD8_REG_INDEX_INTERRUPT);
+
+	if (state)
+		irq_enabled |= BIT(count->id);
+	else
+		irq_enabled &= ~BIT(count->id);
+
+	outb(irq_enabled, priv->base + QUAD8_REG_INDEX_INTERRUPT);
+
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -1224,22 +1327,23 @@ static int quad8_signal_cable_fault_read(struct counter_device *counter,
 {
 	struct quad8_iio *const priv = counter->priv;
 	const size_t channel_id = signal->id / 2;
+	unsigned long irqflags;
 	bool disabled;
 	unsigned int status;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	disabled = !(priv->cable_fault_enable & BIT(channel_id));
 
 	if (disabled) {
-		mutex_unlock(&priv->lock);
+		raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 		return -EINVAL;
 	}
 
 	/* Logic 0 = cable fault */
 	status = inb(priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	/* Mask respective channel and invert logic */
 	*cable_fault = !(status & BIT(channel_id));
@@ -1265,9 +1369,10 @@ static int quad8_signal_cable_fault_enable_write(struct counter_device *counter,
 {
 	struct quad8_iio *const priv = counter->priv;
 	const size_t channel_id = signal->id / 2;
+	unsigned long irqflags;
 	unsigned int cable_fault_enable;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	if (enable)
 		priv->cable_fault_enable |= BIT(channel_id);
@@ -1279,7 +1384,7 @@ static int quad8_signal_cable_fault_enable_write(struct counter_device *counter,
 
 	outb(cable_fault_enable, priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -1302,8 +1407,9 @@ static int quad8_signal_fck_prescaler_write(struct counter_device *counter,
 	struct quad8_iio *const priv = counter->priv;
 	const size_t channel_id = signal->id / 2;
 	const int base_offset = priv->base + 2 * channel_id;
+	unsigned long irqflags;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	priv->fck_prescaler[channel_id] = prescaler;
 
@@ -1315,7 +1421,7 @@ static int quad8_signal_fck_prescaler_write(struct counter_device *counter,
 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
 	     base_offset + 1);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -1418,7 +1524,15 @@ static const u8 quad8_cnt_modes[] = {
 
 static DEFINE_COUNTER_AVAILABLE(quad8_count_mode_available, quad8_cnt_modes);
 
+static const char *const quad8_irq_trigger_states[] = {
+	"carry",
+	"compare",
+	"carry-borrow",
+	"index",
+};
+
 static DEFINE_COUNTER_ENUM(quad8_error_noise_enum, quad8_noise_error_states);
+static DEFINE_COUNTER_ENUM(quad8_irq_trigger_enum, quad8_irq_trigger_states);
 
 static struct counter_comp quad8_count_ext[] = {
 	COUNTER_COMP_CEILING(quad8_count_ceiling_read,
@@ -1433,6 +1547,11 @@ static struct counter_comp quad8_count_ext[] = {
 	COUNTER_COMP_PRESET(quad8_count_preset_read, quad8_count_preset_write),
 	COUNTER_COMP_PRESET_ENABLE(quad8_count_preset_enable_read,
 				   quad8_count_preset_enable_write),
+	COUNTER_COMP_COUNT_ENUM("irq_trigger", quad8_irq_trigger_get,
+				quad8_irq_trigger_set, quad8_irq_trigger_enum),
+	COUNTER_COMP_COUNT_BOOL("irq_trigger_enable",
+				quad8_irq_trigger_enable_read,
+				quad8_irq_trigger_enable_write),
 };
 
 #define QUAD8_COUNT(_id, _cntname) {				\
@@ -1457,6 +1576,48 @@ static struct counter_count quad8_counts[] = {
 	QUAD8_COUNT(7, "Channel 8 Count")
 };
 
+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;
+		case 3:
+			event = COUNTER_EVENT_INDEX;
+				break;
+		default:
+			/* We should never reach here */
+			return -EINVAL;
+		}
+		err = counter_push_event(&priv->counter, event, channel);
+		if (err)
+			return err;
+	}
+
+	/* Clear pending interrupts on device */
+	outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base + QUAD8_REG_CHAN_OP);
+
+	return IRQ_HANDLED;
+}
+
 static int quad8_probe(struct device *dev, unsigned int id)
 {
 	struct iio_dev *indio_dev;
@@ -1495,9 +1656,10 @@ static int quad8_probe(struct device *dev, unsigned int id)
 	quad8iio->counter.priv = quad8iio;
 	quad8iio->base = base[id];
 
-	/* Initialize mutex */
-	mutex_init(&quad8iio->lock);
+	raw_spin_lock_init(&quad8iio->lock);
 
+	/* Reset Index/Interrupt Register */
+	outb(0x00, base[id] + QUAD8_REG_INDEX_INTERRUPT);
 	/* Reset all counters and disable interrupt function */
 	outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
 	/* Set initial configuration for all counters */
@@ -1527,8 +1689,8 @@ static int quad8_probe(struct device *dev, unsigned int id)
 	}
 	/* Disable Differential Encoder Cable Status for all channels */
 	outb(0xFF, base[id] + QUAD8_DIFF_ENCODER_CABLE_STATUS);
-	/* Enable all counters */
-	outb(QUAD8_CHAN_OP_ENABLE_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
+	/* Enable all counters and enable interrupt function */
+	outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base[id] + QUAD8_REG_CHAN_OP);
 
 	/* Register IIO device */
 	err = devm_iio_device_register(dev, indio_dev);
@@ -1536,7 +1698,12 @@ static int quad8_probe(struct device *dev, unsigned int id)
 		return err;
 
 	/* Register Counter device */
-	return devm_counter_register(dev, &quad8iio->counter);
+	err = devm_counter_register(dev, &quad8iio->counter);
+	if (err)
+		return err;
+
+	return devm_request_irq(dev, irq[id], quad8_irq_handler, IRQF_SHARED,
+				quad8iio->counter.name, quad8iio);
 }
 
 static struct isa_driver quad8_driver = {
diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig
index 2de53ab0dd25..bd42df98f522 100644
--- a/drivers/counter/Kconfig
+++ b/drivers/counter/Kconfig
@@ -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"
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 4/5] docs: counter: Document character device interface
  2020-09-27  2:18 ` [PATCH v5 4/5] docs: counter: Document " William Breathitt Gray
@ 2020-10-08  8:09   ` Pavel Machek
  2020-10-08 12:28     ` William Breathitt Gray
  0 siblings, 1 reply; 35+ messages in thread
From: Pavel Machek @ 2020-10-08  8:09 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, david,
	linux-iio, linux-kernel, linux-stm32, linux-arm-kernel,
	syednwaris, patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

[-- Attachment #1: Type: text/plain, Size: 929 bytes --]

Hi!

> +        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?

> +                        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
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 4/5] docs: counter: Document character device interface
  2020-10-08  8:09   ` Pavel Machek
@ 2020-10-08 12:28     ` William Breathitt Gray
  2020-10-12 17:04       ` David Lechner
  0 siblings, 1 reply; 35+ messages in thread
From: William Breathitt Gray @ 2020-10-08 12:28 UTC (permalink / raw)
  To: Pavel Machek
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, david,
	linux-iio, linux-kernel, linux-stm32, linux-arm-kernel,
	syednwaris, patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

[-- Attachment #1: Type: text/plain, Size: 1491 bytes --]

On Thu, Oct 08, 2020 at 10:09:09AM +0200, Pavel Machek wrote:
> Hi!
> 
> > +        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?
> 
> > +                        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
> -- 
> (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

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 4/5] docs: counter: Document character device interface
  2020-10-08 12:28     ` William Breathitt Gray
@ 2020-10-12 17:04       ` David Lechner
  2020-10-13 18:58         ` William Breathitt Gray
  0 siblings, 1 reply; 35+ messages in thread
From: David Lechner @ 2020-10-12 17:04 UTC (permalink / raw)
  To: William Breathitt Gray, Pavel Machek
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

On 10/8/20 7:28 AM, William Breathitt Gray wrote:
> On Thu, Oct 08, 2020 at 10:09:09AM +0200, Pavel Machek wrote:
>> Hi!
>>
>>> +        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?
>>
>>> +                        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
>> -- 
>> (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
> 

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.


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 0/5] Introduce the Counter character device interface
  2020-09-27  2:18 [PATCH v5 0/5] Introduce the Counter character device interface William Breathitt Gray
                   ` (3 preceding siblings ...)
  2020-09-27  2:18 ` [PATCH v5 5/5] counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8 William Breathitt Gray
@ 2020-10-13  0:35 ` David Lechner
  2020-10-18 14:14   ` William Breathitt Gray
       [not found] ` <e38f6dc3a08bf2510034334262776a6ed1df8b89.1601170670.git.vilhelm.gray@gmail.com>
  5 siblings, 1 reply; 35+ messages in thread
From: David Lechner @ 2020-10-13  0:35 UTC (permalink / raw)
  To: William Breathitt Gray, jic23
  Cc: kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

On 9/26/20 9:18 PM, William Breathitt Gray wrote:
> 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?

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.

> 
> 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?
> 
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)?

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 1/5] counter: Internalize sysfs interface code
       [not found] ` <e38f6dc3a08bf2510034334262776a6ed1df8b89.1601170670.git.vilhelm.gray@gmail.com>
@ 2020-10-13  2:15   ` David Lechner
  2020-10-18 14:49     ` William Breathitt Gray
  2020-10-15  1:38   ` David Lechner
  1 sibling, 1 reply; 35+ messages in thread
From: David Lechner @ 2020-10-13  2:15 UTC (permalink / raw)
  To: William Breathitt Gray, jic23
  Cc: kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

On 9/26/20 9:18 PM, William Breathitt Gray wrote:
> 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.

> diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c
> new file mode 100644
> index 000000000000..987c6e8277eb
> --- /dev/null
> +++ b/drivers/counter/counter-core.c


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


> +	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?

> +	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;
> +
> +	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.

> +{
> +	counter_unregister(*(struct counter_device **)res);
> +}
> +

> diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c
> new file mode 100644
> index 000000000000..e66ed99dd5ea
> --- /dev/null
> +++ b/drivers/counter/counter-sysfs.c

> +/**
> + * 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).
> + */
> +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.

> +	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);
> +
> +	/* 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.)


> diff --git a/drivers/counter/ti-eqep.c b/drivers/counter/ti-eqep.c
> index 1ff07faef27f..938085dead80 100644
> --- a/drivers/counter/ti-eqep.c
> +++ b/drivers/counter/ti-eqep.c


> @@ -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.

> diff --git a/include/linux/counter.h b/include/linux/counter.h
> index 9dbd5df4cd34..132bfecca5c3 100644
> --- a/include/linux/counter.h
> +++ b/include/linux/counter.h
> @@ -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/types.h>
>   


It would be helpful to have kernel doc comments on everything in this file.


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 4/5] docs: counter: Document character device interface
  2020-10-12 17:04       ` David Lechner
@ 2020-10-13 18:58         ` William Breathitt Gray
  2020-10-13 19:08           ` David Lechner
  0 siblings, 1 reply; 35+ messages in thread
From: William Breathitt Gray @ 2020-10-13 18:58 UTC (permalink / raw)
  To: David Lechner
  Cc: Pavel Machek, jic23, kamel.bouhara, gwendal, alexandre.belloni,
	linux-iio, linux-kernel, linux-stm32, linux-arm-kernel,
	syednwaris, patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

[-- Attachment #1: Type: text/plain, Size: 2337 bytes --]

On Mon, Oct 12, 2020 at 12:04:10PM -0500, David Lechner wrote:
> On 10/8/20 7:28 AM, William Breathitt Gray wrote:
> > On Thu, Oct 08, 2020 at 10:09:09AM +0200, Pavel Machek wrote:
> >> Hi!
> >>
> >>> +        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?
> >>
> >>> +                        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
> >> -- 
> >> (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
> > 
> 
> 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

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 4/5] docs: counter: Document character device interface
  2020-10-13 18:58         ` William Breathitt Gray
@ 2020-10-13 19:08           ` David Lechner
  2020-10-13 19:27             ` William Breathitt Gray
  0 siblings, 1 reply; 35+ messages in thread
From: David Lechner @ 2020-10-13 19:08 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: Pavel Machek, jic23, kamel.bouhara, gwendal, alexandre.belloni,
	linux-iio, linux-kernel, linux-stm32, linux-arm-kernel,
	syednwaris, patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

On 10/13/20 1:58 PM, William Breathitt Gray wrote:
> On Mon, Oct 12, 2020 at 12:04:10PM -0500, David Lechner wrote:
>> On 10/8/20 7:28 AM, William Breathitt Gray wrote:
>>> On Thu, Oct 08, 2020 at 10:09:09AM +0200, Pavel Machek wrote:
>>>> Hi!
>>>>
>>>>> +        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?
>>>>
>>>>> +                        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
>>>> -- 
>>>> (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
>>>
>>
>> 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
> 

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.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 4/5] docs: counter: Document character device interface
  2020-10-13 19:08           ` David Lechner
@ 2020-10-13 19:27             ` William Breathitt Gray
  0 siblings, 0 replies; 35+ messages in thread
From: William Breathitt Gray @ 2020-10-13 19:27 UTC (permalink / raw)
  To: David Lechner
  Cc: Pavel Machek, jic23, kamel.bouhara, gwendal, alexandre.belloni,
	linux-iio, linux-kernel, linux-stm32, linux-arm-kernel,
	syednwaris, patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

[-- Attachment #1: Type: text/plain, Size: 3223 bytes --]

On Tue, Oct 13, 2020 at 02:08:45PM -0500, David Lechner wrote:
> On 10/13/20 1:58 PM, William Breathitt Gray wrote:
> > On Mon, Oct 12, 2020 at 12:04:10PM -0500, David Lechner wrote:
> >> On 10/8/20 7:28 AM, William Breathitt Gray wrote:
> >>> On Thu, Oct 08, 2020 at 10:09:09AM +0200, Pavel Machek wrote:
> >>>> Hi!
> >>>>
> >>>>> +        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?
> >>>>
> >>>>> +                        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
> >>>> -- 
> >>>> (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
> >>>
> >>
> >> 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
> > 
> 
> 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

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 5/5] counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8
  2020-09-27  2:18 ` [PATCH v5 5/5] counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8 William Breathitt Gray
@ 2020-10-14  0:13   ` David Lechner
  2020-10-18 14:50     ` William Breathitt Gray
  0 siblings, 1 reply; 35+ messages in thread
From: David Lechner @ 2020-10-14  0:13 UTC (permalink / raw)
  To: William Breathitt Gray, jic23
  Cc: kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

On 9/26/20 9:18 PM, William Breathitt Gray wrote:
> +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;
> +		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?

> +		}
> +		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.

> +	}
> +
> +	/* Clear pending interrupts on device */
> +	outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base + QUAD8_REG_CHAN_OP);
> +
> +	return IRQ_HANDLED;
> +}
> +


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 3/5] counter: Add character device interface
  2020-09-27  2:18 ` [PATCH v5 3/5] counter: Add character device interface William Breathitt Gray
@ 2020-10-14  1:40   ` David Lechner
  2020-10-18 16:49     ` William Breathitt Gray
  2020-10-14 17:43   ` David Lechner
  2020-10-14 22:40   ` David Lechner
  2 siblings, 1 reply; 35+ messages in thread
From: David Lechner @ 2020-10-14  1:40 UTC (permalink / raw)
  To: William Breathitt Gray, jic23
  Cc: kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

On 9/26/20 9:18 PM, William Breathitt Gray wrote:
> 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.


> diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c
> new file mode 100644
> index 000000000000..2be3846e4105
> --- /dev/null
> +++ b/drivers/counter/counter-chrdev.c


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

> +
> +	/* 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.

> +	}
> +	*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.

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

> +			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.

> +			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;
> +		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)

> +			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)

> +			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.

> +		return -EFAULT;

Would EOPNOTSUPP be a better error choice?

> +	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;
> +	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.

> +		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?


> +	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.

> +		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)
> +{
> +	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.

> +
> +	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);
> +
> +	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.

> +}
> +EXPORT_SYMBOL_GPL(counter_push_event);


> diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c
> index e66ed99dd5ea..cefef61f170d 100644
> --- a/drivers/counter/counter-sysfs.c
> +++ b/drivers/counter/counter-sysfs.c


Not sure why sysfs changes are in the chrdev patch. Are these
changes related somehow?


> diff --git a/include/uapi/linux/counter.h b/include/uapi/linux/counter.h
> new file mode 100644
> index 000000000000..ad9a8686b2b0
> --- /dev/null
> +++ b/include/uapi/linux/counter.h
> @@ -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.

> +
> +#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.

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


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.

> +
> +#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.

> +#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.)

> + * @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.

> +
> +#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;
> +	struct counter_watch watch;
> +	union {
> +		__u8 value_u8;
> +		__u64 value_u64;
> +	};
> +};

Should probably use __aligned_u64 here too.

> +
> +#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
> +
> +#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.

[1]: https://github.com/dlech/linux/tree/bone-counter

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 3/5] counter: Add character device interface
  2020-09-27  2:18 ` [PATCH v5 3/5] counter: Add character device interface William Breathitt Gray
  2020-10-14  1:40   ` David Lechner
@ 2020-10-14 17:43   ` David Lechner
  2020-10-14 19:05     ` William Breathitt Gray
  2020-10-14 22:40   ` David Lechner
  2 siblings, 1 reply; 35+ messages in thread
From: David Lechner @ 2020-10-14 17:43 UTC (permalink / raw)
  To: William Breathitt Gray, jic23
  Cc: kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

On 9/26/20 9:18 PM, William Breathitt Gray wrote:
> diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c
> new file mode 100644
> index 000000000000..2be3846e4105
> --- /dev/null
> +++ b/drivers/counter/counter-chrdev.c


> +/**
> + * 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 &&
> +		    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.



> +		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;
> +}


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 3/5] counter: Add character device interface
  2020-10-14 17:43   ` David Lechner
@ 2020-10-14 19:05     ` William Breathitt Gray
  2020-10-14 22:32       ` David Lechner
  0 siblings, 1 reply; 35+ messages in thread
From: William Breathitt Gray @ 2020-10-14 19:05 UTC (permalink / raw)
  To: David Lechner
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

[-- Attachment #1: Type: text/plain, Size: 3143 bytes --]

On Wed, Oct 14, 2020 at 12:43:08PM -0500, David Lechner wrote:
> On 9/26/20 9:18 PM, William Breathitt Gray wrote:
> > diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c
> > new file mode 100644
> > index 000000000000..2be3846e4105
> > --- /dev/null
> > +++ b/drivers/counter/counter-chrdev.c
> 
> 
> > +/**
> > + * 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 &&
> > +		    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.

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

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 3/5] counter: Add character device interface
  2020-10-14 19:05     ` William Breathitt Gray
@ 2020-10-14 22:32       ` David Lechner
  0 siblings, 0 replies; 35+ messages in thread
From: David Lechner @ 2020-10-14 22:32 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

On 10/14/20 2:05 PM, William Breathitt Gray wrote:
> On Wed, Oct 14, 2020 at 12:43:08PM -0500, David Lechner wrote:
>> On 9/26/20 9:18 PM, William Breathitt Gray wrote:
>>> diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c
>>> new file mode 100644
>>> index 000000000000..2be3846e4105
>>> --- /dev/null
>>> +++ b/drivers/counter/counter-chrdev.c
>>
>>
>>> +/**
>>> + * 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 &&
>>> +		    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.
> 
> 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
> 

I made the same suggestion in one of my other replies - except
I called it COUNTER_SCOPE_NONE.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 3/5] counter: Add character device interface
  2020-09-27  2:18 ` [PATCH v5 3/5] counter: Add character device interface William Breathitt Gray
  2020-10-14  1:40   ` David Lechner
  2020-10-14 17:43   ` David Lechner
@ 2020-10-14 22:40   ` David Lechner
  2020-10-18 16:58     ` William Breathitt Gray
  2 siblings, 1 reply; 35+ messages in thread
From: David Lechner @ 2020-10-14 22:40 UTC (permalink / raw)
  To: William Breathitt Gray, jic23
  Cc: kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

On 9/26/20 9:18 PM, William Breathitt Gray wrote:
> +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)
> +			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;
}

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 1/5] counter: Internalize sysfs interface code
       [not found] ` <e38f6dc3a08bf2510034334262776a6ed1df8b89.1601170670.git.vilhelm.gray@gmail.com>
  2020-10-13  2:15   ` [PATCH v5 1/5] counter: Internalize sysfs interface code David Lechner
@ 2020-10-15  1:38   ` David Lechner
  2020-10-18 17:00     ` William Breathitt Gray
  1 sibling, 1 reply; 35+ messages in thread
From: David Lechner @ 2020-10-15  1:38 UTC (permalink / raw)
  To: William Breathitt Gray, jic23
  Cc: kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

On 9/26/20 9:18 PM, William Breathitt Gray wrote:
> +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;
> +		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.

> +	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;
> +}


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 0/5] Introduce the Counter character device interface
  2020-10-13  0:35 ` [PATCH v5 0/5] Introduce the Counter character device interface David Lechner
@ 2020-10-18 14:14   ` William Breathitt Gray
  0 siblings, 0 replies; 35+ messages in thread
From: William Breathitt Gray @ 2020-10-18 14:14 UTC (permalink / raw)
  To: David Lechner
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

[-- Attachment #1: Type: text/plain, Size: 2571 bytes --]

On Mon, Oct 12, 2020 at 07:35:11PM -0500, David Lechner wrote:
> On 9/26/20 9:18 PM, William Breathitt Gray wrote:
> > 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?
> 
> 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.

> > 
> > 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?
> > 
> 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

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 1/5] counter: Internalize sysfs interface code
  2020-10-13  2:15   ` [PATCH v5 1/5] counter: Internalize sysfs interface code David Lechner
@ 2020-10-18 14:49     ` William Breathitt Gray
  2020-10-20 15:38       ` David Lechner
  0 siblings, 1 reply; 35+ messages in thread
From: William Breathitt Gray @ 2020-10-18 14:49 UTC (permalink / raw)
  To: David Lechner
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

[-- Attachment #1: Type: text/plain, Size: 7970 bytes --]

On Mon, Oct 12, 2020 at 09:15:00PM -0500, David Lechner wrote:
> On 9/26/20 9:18 PM, William Breathitt Gray wrote:
> > 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.
> 
> > diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c
> > new file mode 100644
> > index 000000000000..987c6e8277eb
> > --- /dev/null
> > +++ b/drivers/counter/counter-core.c
> 
> 
> > +/**
> > + * 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;
> > +
> > +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.

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?

> > +	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?

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.

> > +	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;
> > +
> > +	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.

> > +{
> > +	counter_unregister(*(struct counter_device **)res);
> > +}
> > +
> 
> > diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c
> > new file mode 100644
> > index 000000000000..e66ed99dd5ea
> > --- /dev/null
> > +++ b/drivers/counter/counter-sysfs.c
> 
> > +/**
> > + * 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).
> > + */
> > +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.

> > +	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);
> > +
> > +	/* 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.

> > diff --git a/drivers/counter/ti-eqep.c b/drivers/counter/ti-eqep.c
> > index 1ff07faef27f..938085dead80 100644
> > --- a/drivers/counter/ti-eqep.c
> > +++ b/drivers/counter/ti-eqep.c
> 
> 
> > @@ -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.

Yeah, it was an unintentional change. I'll fix this. :-)

> > diff --git a/include/linux/counter.h b/include/linux/counter.h
> > index 9dbd5df4cd34..132bfecca5c3 100644
> > --- a/include/linux/counter.h
> > +++ b/include/linux/counter.h
> > @@ -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?

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.

> >   #include <linux/types.h>
> >   
> 
> 
> It would be helpful to have kernel doc comments on everything in this file.

Ack.

William Breathitt Gray

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 5/5] counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8
  2020-10-14  0:13   ` David Lechner
@ 2020-10-18 14:50     ` William Breathitt Gray
  0 siblings, 0 replies; 35+ messages in thread
From: William Breathitt Gray @ 2020-10-18 14:50 UTC (permalink / raw)
  To: David Lechner
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

[-- Attachment #1: Type: text/plain, Size: 1624 bytes --]

On Tue, Oct 13, 2020 at 07:13:32PM -0500, David Lechner wrote:
> On 9/26/20 9:18 PM, William Breathitt Gray wrote:
> > +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;
> > +		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?

Ack.

> > +		}
> > +		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

> > +	}
> > +
> > +	/* Clear pending interrupts on device */
> > +	outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base + QUAD8_REG_CHAN_OP);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 3/5] counter: Add character device interface
  2020-10-14  1:40   ` David Lechner
@ 2020-10-18 16:49     ` William Breathitt Gray
  2020-10-20 15:53       ` David Lechner
  0 siblings, 1 reply; 35+ messages in thread
From: William Breathitt Gray @ 2020-10-18 16:49 UTC (permalink / raw)
  To: David Lechner
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

[-- Attachment #1: Type: text/plain, Size: 20297 bytes --]

On Tue, Oct 13, 2020 at 08:40:18PM -0500, David Lechner wrote:
> On 9/26/20 9:18 PM, William Breathitt Gray wrote:
> > 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.

> > diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c
> > new file mode 100644
> > index 000000000000..2be3846e4105
> > --- /dev/null
> > +++ b/drivers/counter/counter-chrdev.c
> 
> 
> > +
> > +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);
> > +	}
> > +
> > +	/* 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.

> > +
> > +	/* 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.

> > +	}
> > +	*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.

Ack.

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

Ack.

> > +			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.

> > +			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;
> > +		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.

> > +			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.

> > +			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.

Ack.

> > +		return -EFAULT;
> 
> Would EOPNOTSUPP be a better error choice?

You're right, that would be a more apt return.

> > +	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;
> > +	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.

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.

> > +		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?

That's a good point because "set" might easily be confused for a
configuration operation. I'll rename this to be clearer.

> > +	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.

Ack, I'll add in a callback for interrupt configuration where needed.

> > +		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)
> > +{
> > +	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.

> > +
> > +	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);
> > +
> > +	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.

> > +}
> > +EXPORT_SYMBOL_GPL(counter_push_event);
> 
> 
> > diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c
> > index e66ed99dd5ea..cefef61f170d 100644
> > --- a/drivers/counter/counter-sysfs.c
> > +++ b/drivers/counter/counter-sysfs.c
> 
> 
> Not 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.

> > diff --git a/include/uapi/linux/counter.h b/include/uapi/linux/counter.h
> > new file mode 100644
> > index 000000000000..ad9a8686b2b0
> > --- /dev/null
> > +++ b/include/uapi/linux/counter.h
> > @@ -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.

Ack.

> > +
> > +#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.

I'll come up with a solution for this use-case.

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

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.

> > +
> > +#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.

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.

> > +#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.

> > + * @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.

> > +
> > +#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;
> > +	struct counter_watch watch;
> > +	union {
> > +		__u8 value_u8;
> > +		__u64 value_u64;
> > +	};
> > +};
> 
> Should probably use __aligned_u64 here too.

Ack.

> > +
> > +#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
> > +
> > +#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.
> 
> [1]: https://github.com/dlech/linux/tree/bone-counter

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

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 3/5] counter: Add character device interface
  2020-10-14 22:40   ` David Lechner
@ 2020-10-18 16:58     ` William Breathitt Gray
  2020-10-20 16:06       ` David Lechner
  0 siblings, 1 reply; 35+ messages in thread
From: William Breathitt Gray @ 2020-10-18 16:58 UTC (permalink / raw)
  To: David Lechner
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

[-- Attachment #1: Type: text/plain, Size: 1594 bytes --]

On Wed, Oct 14, 2020 at 05:40:44PM -0500, David Lechner wrote:
> On 9/26/20 9:18 PM, William Breathitt Gray wrote:
> > +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)
> > +			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

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 1/5] counter: Internalize sysfs interface code
  2020-10-15  1:38   ` David Lechner
@ 2020-10-18 17:00     ` William Breathitt Gray
  0 siblings, 0 replies; 35+ messages in thread
From: William Breathitt Gray @ 2020-10-18 17:00 UTC (permalink / raw)
  To: David Lechner
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

[-- Attachment #1: Type: text/plain, Size: 2557 bytes --]

On Wed, Oct 14, 2020 at 08:38:40PM -0500, David Lechner wrote:
> On 9/26/20 9:18 PM, William Breathitt Gray wrote:
> > +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;
> > +		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.

Thank you for pointing this out, I'll fix this.

William Breathitt Gray

> > +	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;
> > +}
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 1/5] counter: Internalize sysfs interface code
  2020-10-18 14:49     ` William Breathitt Gray
@ 2020-10-20 15:38       ` David Lechner
  2020-10-23 13:12         ` William Breathitt Gray
  0 siblings, 1 reply; 35+ messages in thread
From: David Lechner @ 2020-10-20 15:38 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

On 10/18/20 9:49 AM, William Breathitt Gray wrote:
> On Mon, Oct 12, 2020 at 09:15:00PM -0500, David Lechner wrote:
>> On 9/26/20 9:18 PM, William Breathitt Gray wrote:
>>> 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.
>>
>>> diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c
>>> new file mode 100644
>>> index 000000000000..987c6e8277eb
>>> --- /dev/null
>>> +++ b/drivers/counter/counter-core.c
>>
>>
>>> +/**
>>> + * 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;
>>> +
>>> +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.
> 
> 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().

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

> 
> 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?

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().

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 3/5] counter: Add character device interface
  2020-10-18 16:49     ` William Breathitt Gray
@ 2020-10-20 15:53       ` David Lechner
  2020-10-25 12:55         ` William Breathitt Gray
  0 siblings, 1 reply; 35+ messages in thread
From: David Lechner @ 2020-10-20 15:53 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue


>>> +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;
>>> +	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.
> 
> 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.
> 

Got it. I think I missed the fact that list_replace_init() clears
next_events_list.

>>> +
>>> +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.
> 
> 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.
> 

It isn't possible the some memory allocated with devm_kalloc() could be
be referenced after calling put_device() now or in the future?

>>> +}
>>> +EXPORT_SYMBOL_GPL(counter_push_event);
>>
>>
>>> diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c
>>> index e66ed99dd5ea..cefef61f170d 100644
>>> --- a/drivers/counter/counter-sysfs.c
>>> +++ b/drivers/counter/counter-sysfs.c
>>
>>
>> Not 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.
> 

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?

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 3/5] counter: Add character device interface
  2020-10-18 16:58     ` William Breathitt Gray
@ 2020-10-20 16:06       ` David Lechner
  2020-10-25 13:18         ` William Breathitt Gray
  0 siblings, 1 reply; 35+ messages in thread
From: David Lechner @ 2020-10-20 16:06 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

On 10/18/20 11:58 AM, William Breathitt Gray wrote:
> On Wed, Oct 14, 2020 at 05:40:44PM -0500, David Lechner wrote:
>> On 9/26/20 9:18 PM, William Breathitt Gray wrote:
>>> +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)
>>> +			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.
> 


The way I understand it, locking is only needed for concurrent readers
and locking between reader and writer is not needed.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 1/5] counter: Internalize sysfs interface code
  2020-10-20 15:38       ` David Lechner
@ 2020-10-23 13:12         ` William Breathitt Gray
  0 siblings, 0 replies; 35+ messages in thread
From: William Breathitt Gray @ 2020-10-23 13:12 UTC (permalink / raw)
  To: David Lechner
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

[-- Attachment #1: Type: text/plain, Size: 3796 bytes --]

On Tue, Oct 20, 2020 at 10:38:43AM -0500, David Lechner wrote:
> On 10/18/20 9:49 AM, William Breathitt Gray wrote:
> > On Mon, Oct 12, 2020 at 09:15:00PM -0500, David Lechner wrote:
> >> On 9/26/20 9:18 PM, William Breathitt Gray wrote:
> >>> diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c
> >>> new file mode 100644
> >>> index 000000000000..987c6e8277eb
> >>> --- /dev/null
> >>> +++ b/drivers/counter/counter-core.c
> >>
> >>
> >>> +/**
> >>> + * 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;
> >>> +
> >>> +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.
> > 
> > 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().
> 
> 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

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.

> > 
> > 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?
> 
> 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

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 3/5] counter: Add character device interface
  2020-10-20 15:53       ` David Lechner
@ 2020-10-25 12:55         ` William Breathitt Gray
  2020-10-25 16:36           ` David Lechner
  0 siblings, 1 reply; 35+ messages in thread
From: William Breathitt Gray @ 2020-10-25 12:55 UTC (permalink / raw)
  To: David Lechner
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

[-- Attachment #1: Type: text/plain, Size: 3943 bytes --]

On Tue, Oct 20, 2020 at 10:53:32AM -0500, David Lechner wrote:
> >>> +
> >>> +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.
> > 
> > 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.
> > 
> 
> It isn't possible the some memory allocated with devm_kalloc() could be
> be referenced after calling put_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.

> >>> diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c
> >>> index e66ed99dd5ea..cefef61f170d 100644
> >>> --- a/drivers/counter/counter-sysfs.c
> >>> +++ b/drivers/counter/counter-sysfs.c
> >>
> >>
> >> Not 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.
> > 
> 
> 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

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 3/5] counter: Add character device interface
  2020-10-20 16:06       ` David Lechner
@ 2020-10-25 13:18         ` William Breathitt Gray
  2020-10-25 16:34           ` David Lechner
  0 siblings, 1 reply; 35+ messages in thread
From: William Breathitt Gray @ 2020-10-25 13:18 UTC (permalink / raw)
  To: David Lechner
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

[-- Attachment #1: Type: text/plain, Size: 2288 bytes --]

On Tue, Oct 20, 2020 at 11:06:42AM -0500, David Lechner wrote:
> On 10/18/20 11:58 AM, William Breathitt Gray wrote:
> > On Wed, Oct 14, 2020 at 05:40:44PM -0500, David Lechner wrote:
> >> On 9/26/20 9:18 PM, William Breathitt Gray wrote:
> >>> +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)
> >>> +			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.
> > 
> 
> 
> 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

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 3/5] counter: Add character device interface
  2020-10-25 13:18         ` William Breathitt Gray
@ 2020-10-25 16:34           ` David Lechner
  2020-10-25 17:53             ` William Breathitt Gray
  0 siblings, 1 reply; 35+ messages in thread
From: David Lechner @ 2020-10-25 16:34 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

On 10/25/20 8:18 AM, William Breathitt Gray wrote:
> On Tue, Oct 20, 2020 at 11:06:42AM -0500, David Lechner wrote:
>> On 10/18/20 11:58 AM, William Breathitt Gray wrote:
>>> On Wed, Oct 14, 2020 at 05:40:44PM -0500, David Lechner wrote:
>>>> On 9/26/20 9:18 PM, William Breathitt Gray wrote:
>>>>> +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)
>>>>> +			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.
>>>
>>
>>
>> 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
> 

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.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 3/5] counter: Add character device interface
  2020-10-25 12:55         ` William Breathitt Gray
@ 2020-10-25 16:36           ` David Lechner
  0 siblings, 0 replies; 35+ messages in thread
From: David Lechner @ 2020-10-25 16:36 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue


>>>>> diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c
>>>>> index e66ed99dd5ea..cefef61f170d 100644
>>>>> --- a/drivers/counter/counter-sysfs.c
>>>>> +++ b/drivers/counter/counter-sysfs.c
>>>>
>>>>
>>>> Not 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.
>>>
>>
>> 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
> 

Sounds reasonable to me.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v5 3/5] counter: Add character device interface
  2020-10-25 16:34           ` David Lechner
@ 2020-10-25 17:53             ` William Breathitt Gray
  0 siblings, 0 replies; 35+ messages in thread
From: William Breathitt Gray @ 2020-10-25 17:53 UTC (permalink / raw)
  To: David Lechner
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

[-- Attachment #1: Type: text/plain, Size: 2919 bytes --]

On Sun, Oct 25, 2020 at 11:34:43AM -0500, David Lechner wrote:
> On 10/25/20 8:18 AM, William Breathitt Gray wrote:
> > On Tue, Oct 20, 2020 at 11:06:42AM -0500, David Lechner wrote:
> >> On 10/18/20 11:58 AM, William Breathitt Gray wrote:
> >>> On Wed, Oct 14, 2020 at 05:40:44PM -0500, David Lechner wrote:
> >>>> On 9/26/20 9:18 PM, William Breathitt Gray wrote:
> >>>>> +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)
> >>>>> +			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.
> >>>
> >>
> >>
> >> 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
> > 
> 
> 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

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

end of thread, other threads:[~2020-10-25 17:53 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-27  2:18 [PATCH v5 0/5] Introduce the Counter character device interface William Breathitt Gray
2020-09-27  2:18 ` [PATCH v5 2/5] docs: counter: Update to reflect sysfs internalization William Breathitt Gray
2020-09-27  2:18 ` [PATCH v5 3/5] counter: Add character device interface William Breathitt Gray
2020-10-14  1:40   ` David Lechner
2020-10-18 16:49     ` William Breathitt Gray
2020-10-20 15:53       ` David Lechner
2020-10-25 12:55         ` William Breathitt Gray
2020-10-25 16:36           ` David Lechner
2020-10-14 17:43   ` David Lechner
2020-10-14 19:05     ` William Breathitt Gray
2020-10-14 22:32       ` David Lechner
2020-10-14 22:40   ` David Lechner
2020-10-18 16:58     ` William Breathitt Gray
2020-10-20 16:06       ` David Lechner
2020-10-25 13:18         ` William Breathitt Gray
2020-10-25 16:34           ` David Lechner
2020-10-25 17:53             ` William Breathitt Gray
2020-09-27  2:18 ` [PATCH v5 4/5] docs: counter: Document " William Breathitt Gray
2020-10-08  8:09   ` Pavel Machek
2020-10-08 12:28     ` William Breathitt Gray
2020-10-12 17:04       ` David Lechner
2020-10-13 18:58         ` William Breathitt Gray
2020-10-13 19:08           ` David Lechner
2020-10-13 19:27             ` William Breathitt Gray
2020-09-27  2:18 ` [PATCH v5 5/5] counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8 William Breathitt Gray
2020-10-14  0:13   ` David Lechner
2020-10-18 14:50     ` William Breathitt Gray
2020-10-13  0:35 ` [PATCH v5 0/5] Introduce the Counter character device interface David Lechner
2020-10-18 14:14   ` William Breathitt Gray
     [not found] ` <e38f6dc3a08bf2510034334262776a6ed1df8b89.1601170670.git.vilhelm.gray@gmail.com>
2020-10-13  2:15   ` [PATCH v5 1/5] counter: Internalize sysfs interface code David Lechner
2020-10-18 14:49     ` William Breathitt Gray
2020-10-20 15:38       ` David Lechner
2020-10-23 13:12         ` William Breathitt Gray
2020-10-15  1:38   ` David Lechner
2020-10-18 17:00     ` William Breathitt Gray

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).