Skip to contentSkip to conversation

iio: Introduce the generic counter interface

PATCH Superseded William Breathitt Gray 3 participants 16 messages Oct 5, 2017 – Oct 9, 2017

Subsystems (2)

v3 of 10 Superseded Latest is v10 →

lore
Share & tools
lore
#0 lore raw

v3 cover iio: Introduce the generic counter interface

There have been some significant implementation changes for this version of the patchset. Here's a brief summary of the updates:

- Inline comments throughout the industrial-counter.c file; this should help clarify some of the code and aid in the review of the system architecture

- Simplification of the driver API; primarily, dynamic updates to the Counter Values, Triggers, and Signals lists are no longer allowed -- all relationships must be defined before the Counter is registered to the system

- Dynamic component registration functions (iio_counter_value_register, et al.) have been removed, and now the signals, values, and triggers array members serve to register the components; signals, values, and triggers were previously named init_signals, init_values, and init_triggers respectively -- the "init_" prefix was removed now that dynamic registration is no longer allowed

- Requiring static component relationships means linked lists are no longer necessary so all the list related code has been removed; Signals, Triggers, and Values are now accessed directly from the arrays supplied in the iio_counter structure

- No dynamic additions/substraction of components eliminates the need for mutex locks, so those have been removed as well

- Previously, the entire iio_counter structure was copied and stored as the iio_dev private data; now, only a pointer to the supplied iio_counter structure is stored

- devm_iio_counter_register and devm_iio_counter_unregister functions were incorporated from Benjamin Gaignard's suggestions

- Dummy counter driver provided as a reference for a simple counter implementation utilizing the Generic Counter interface

In a previous discussion, Benjamin noted some conflicts between the Generic Counter and the underlying IIO core code: mapping does not always work out seemlessly and sometimes the IIO core functionality exposure provided via iio_counter is not quite flexible enough. I intend to address these shortcomings in the next version of this patchset hopefully.

In particular, I'm leaning towards separating the Generic Counter interface from the IIO core dependency entirely. Since the Generic Counter inteface focuses on a more abstract representation of a counter device, I don't think it provides a suitable interface to map onto IIO core, which appears to focus more on a representation of the physical hardware itself.

Separating Generic Counter from IIO core should allow a driver to use the IIO core functions (iio_device_register, et al.) to represent the physical nature of their hardware (voltages, currents, etc.), while also utilize the Generic Counter interface to represent the abstract relationships between those Signals and their ultimate counter Values.

In particular, I'm hoping for the Generic Counter system implementation itself to not require all this hoop-jumping (mapping to IIO core functions, jumping back to the parent iio_counter, handling non-matching parameter lists between iio_counter and iio_dev, etc.); that should make the code simpler to debug, more efficient, and more stable in the long-run. I will consider these advantages and disadvantages before committing to a separation however.

Regarding this version of the patchset in particular, I decided to remove the dynamic component registration functionality since I doubt that many devices would require such; almost all, if not all, hardware counters I encountered have static relationships between input lines and the count value (i.e. specific input lines correlate to specific count registers).

The requirement that Counter component relationships are static has opened the possibility of some optimizations in the Generic Counter interface code:

- Signals, Triggers, and Values are provided as arrays; these arrays could benefit from some amount of sorting based on component IDs

- Searching for a component can be more efficient in a sorted array; currently, the code just walks down the array elements and compare the IDs

- Similar to general searching, the arrays are initially verified to gurantee unique IDs; currently, the code walks down the array elements and compares the current element ID with all the elements before and after -- this could be made more efficient with some sorting

There are likely other areas of improvement, so let me know and I'll see what I can do.

William Breathitt Gray (6):
iio: Implement counter channel specification and IIO_SIGNAL constant
iio: Introduce the generic counter interface
iio: Documentation: Add IIO Generic Counter sysfs documentation
docs: Add IIO Generic Counter Interface documentation
iio: Add dummy counter driver
iio: 104-quad-8: Add IIO generic counter interface support

 .../testing/sysfs-bus-iio-generic-counter-sysfs    |  63 ++
 Documentation/driver-api/iio/generic-counter.txt   | 526 ++++++++++++
 MAINTAINERS                                        |   7 +
 drivers/iio/Kconfig                                |   8 +
 drivers/iio/Makefile                               |   1 +
 drivers/iio/counter/104-quad-8.c                   | 294 ++++++-
 drivers/iio/counter/Kconfig                        |  16 +
 drivers/iio/counter/Makefile                       |   1 +
 drivers/iio/counter/dummy-counter.c                | 293 +++++++
 drivers/iio/industrialio-core.c                    |  14 +-
 drivers/iio/industrialio-counter.c                 | 900 +++++++++++++++++++++
 include/linux/iio/counter.h                        | 166 ++++
 include/linux/iio/iio.h                            |   2 +
 include/uapi/linux/iio/types.h                     |   1 +
 14 files changed, 2274 insertions(+), 18 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs
 create mode 100644 Documentation/driver-api/iio/generic-counter.txt
 create mode 100644 drivers/iio/counter/dummy-counter.c
 create mode 100644 drivers/iio/industrialio-counter.c
 create mode 100644 include/linux/iio/counter.h
Signature · 1 line
2.14.1
#1 lore raw

PATCH v3 1/6 iio: Implement counter channel specification and IIO_SIGNAL constant

Counters are IIO devices that are exposed via a generic counter interface consisting of one or more counter signals (IIO_SIGNAL) linked to one or more counter values (IIO_COUNT).

This patch introduces the IIO_SIGNAL constant which represents a counter device signal line. Additionally, a new "counter" member is added to struct iio_chan_spec, with relevant support, to indicate that a channel is part of a counter.

---
 drivers/iio/industrialio-core.c | 14 +++++++++++++-
 include/linux/iio/iio.h         |  2 ++
 include/uapi/linux/iio/types.h  |  1 +
 3 files changed, 16 insertions(+), 1 deletion(-)
drivers/iio/industrialio-core.c +13 -1
index 7a5aa127c52e..ee508f2070a7 100644
@@ -85,6 +85,7 @@ static const char * const iio_chan_type_name_spec[] = {
 	[IIO_COUNT] = "count",
 	[IIO_INDEX] = "index",
 	[IIO_GRAVITY]  = "gravity",
+	[IIO_SIGNAL] = "signal",
 };
 
 static const char * const iio_modifier_names[] = {
@@ -989,7 +990,18 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
 			break;
 
 		case IIO_SEPARATE:
-			if (chan->indexed)
+			if (chan->counter) {
+				if (!chan->indexed) {
+					WARN(1, "Counter channels must be indexed\n");
+					ret = -EINVAL;
+					goto error_free_full_postfix;
+				}
+				name = kasprintf(GFP_KERNEL, "%s%d-%d_%s",
+						    iio_chan_type_name_spec[chan->type],
+						    chan->channel,
+						    chan->channel2,
+						    full_postfix);
+			} else if (chan->indexed)
 				name = kasprintf(GFP_KERNEL, "%s_%s%d_%s",
 						    iio_direction[chan->output],
 						    iio_chan_type_name_spec[chan->type],
include/linux/iio/iio.h +2 -0
index 50cae8504256..9f949dd74b60 100644
@@ -263,6 +263,7 @@ struct iio_event_spec {
  *			attributes but not for event codes.
  * @output:		Channel is output.
  * @differential:	Channel is differential.
+ * @counter:		Channel is part of a counter.
  */
 struct iio_chan_spec {
 	enum iio_chan_type	type;
@@ -295,6 +296,7 @@ struct iio_chan_spec {
 	unsigned		indexed:1;
 	unsigned		output:1;
 	unsigned		differential:1;
+	unsigned		counter:1;
 };
 
 
include/uapi/linux/iio/types.h +1 -0
index ffafd6c25a48..313899652ca7 100644
@@ -43,6 +43,7 @@ enum iio_chan_type {
 	IIO_COUNT,
 	IIO_INDEX,
 	IIO_GRAVITY,
+	IIO_SIGNAL,
 };
 
 enum iio_modifier {
Signature · 1 line
2.14.1
#3 lore raw

PATCH v3 2/6 iio: Introduce the generic counter interface

In reply to William Breathitt Gray

This patch introduces the IIO generic counter interface for supporting counter devices. The generic counter interface serves as a catch-all to enable rudimentary support for devices that qualify as counters. More specific and apt counter interfaces may be developed on top of the generic counter interface, and those interfaces should be used by drivers when possible rather than the generic counter interface.

In the context of the IIO generic counter interface, a counter is defined as a device that reports one or more "counter values" based on the state changes of one or more "counter signals" as evaluated by a defined "counter function."

The IIO generic counter interface piggybacks off of the IIO core. This is primarily used to leverage the existing sysfs setup: the IIO_COUNT channel attributes represent the Counter Value, while the IIO_SIGNAL channel attributes represent the Counter Signal; auxilary IIO_COUNT attributes represent the Counter Signal connections (Triggers) and their respective state change configurations which trigger an associated "counter function" evaluation.

The iio_counter_ops structure serves as a container for driver callbacks to communicate with the device; function callbacks are provided to read and write various Signals and Values, and set and get the "trigger mode" and "function mode" for various Triggers and Values respectively.

To support a counter device, a driver must first allocate the available Counter Signals via iio_counter_signal structures. These Signals should be stored as an array and set to the signals array member of an allocated iio_counter structure before the Counter is registered to the system.

Counter Values may be allocated via iio_counter_value structures, and respective Counter Signal associations (Triggers) made via iio_counter_trigger structures. Associated iio_counter_trigger structures are stored as an array and set to the the triggers array member of the respective iio_counter_value structure. These iio_counter_value structures are set to the values array member of an allocated iio_counter structure before the Counter is registered to the system.

A counter device is registered to the system by passing the respective initialized iio_counter structure to the iio_counter_register function; similarly, the iio_counter_unregister function unregisters the respective counter. The devm_iio_counter_register and iio_devm_iio_counter_unregister functions serve as device memory-managed versions of the iio_counter_register and iio_counter unregister functions respectively.

---
 MAINTAINERS                        |   7 +
 drivers/iio/Kconfig                |   8 +
 drivers/iio/Makefile               |   1 +
 drivers/iio/counter/Kconfig        |   1 +
 drivers/iio/industrialio-counter.c | 900 +++++++++++++++++++++++++++++++++++++
 include/linux/iio/counter.h        | 166 +++++++
 6 files changed, 1083 insertions(+)
 create mode 100644 drivers/iio/industrialio-counter.c
 create mode 100644 include/linux/iio/counter.h
MAINTAINERS +7 -0
index 2281af4b41b6..8b7c37bed252 100644
@@ -6693,6 +6693,13 @@ F:	Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
 F:	Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
 F:	drivers/iio/adc/envelope-detector.c
 
+IIO GENERIC COUNTER INTERFACE
+M:	William Breathitt Gray <[email protected]>
+L:	[email protected]
+S:	Maintained
+F:	drivers/iio/industrialio-counter.c
+F:	include/linux/iio/counter.h
+
 IIO MULTIPLEXER
 M:	Peter Rosin <[email protected]>
 L:	[email protected]
drivers/iio/Kconfig +8 -0
index b3c8c6ef0dff..78e01f4f5937 100644
@@ -30,6 +30,14 @@ config IIO_CONFIGFS
 	  (e.g. software triggers). For more info see
 	  Documentation/iio/iio_configfs.txt.
 
+config IIO_COUNTER
+	bool "Enable IIO counter support"
+	help
+	  Provides IIO core support for counters. This API provides
+	  a generic interface that serves as the building blocks to
+	  create more complex counter interfaces. Rudimentary support
+	  for counters is enabled.
+
 config IIO_TRIGGER
 	bool "Enable triggered sampling support"
 	help
drivers/iio/Makefile +1 -0
index 93c769cd99bf..6427ff38f964 100644
@@ -5,6 +5,7 @@
 obj-$(CONFIG_IIO) += industrialio.o
 industrialio-y := industrialio-core.o industrialio-event.o inkern.o
 industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
+industrialio-$(CONFIG_IIO_COUNTER) += industrialio-counter.o
 industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
 
 obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
drivers/iio/counter/Kconfig +1 -0
index 474e1ac4e7c0..c8becfe78e28 100644
@@ -4,6 +4,7 @@
 # When adding new entries keep the list in alphabetical order
 
 menu "Counters"
+	depends on IIO_COUNTER
 
 config 104_QUAD_8
 	tristate "ACCES 104-QUAD-8 driver"
drivers/iio/industrialio-counter.c new file +900 -0

900 lines not on this page · raw message

include/linux/iio/counter.h new file +166 -0

166 lines not on this page · raw message

Signature · 1 line
2.14.1
#6 lore raw

PATCH v3 3/6 iio: Documentation: Add IIO Generic Counter sysfs documentation

In reply to William Breathitt Gray

This patch adds standard documentation for the userspace sysfs atrributes of the IIO Generic Counter Interface.

---
 .../testing/sysfs-bus-iio-generic-counter-sysfs    | 63 ++++++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs
Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs new file +63 -0
new file mode 100644
index 000000000000..b2c09254ec5a
@@ -0,0 +1,63 @@
+What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_function
+KernelVersion:	4.14
+Contact:	[email protected]
+Description:
+		Counter function mode for Value Y of Counter X; counter function
+		evaluation triggered by conditions specified by the
+		countX-Y_trigger_signalX-Z attributes.
+
+What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_function_available
+KernelVersion:	4.14
+Contact:	[email protected]
+Description:
+		Discrete set of available function modes for the configuration
+		of the respective Value Y of Counter X are listed in this file.
+
+What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_name
+KernelVersion:	4.14
+Contact:	[email protected]
+Description:
+		Read-only attribute that indicates the device-specific name of
+		Value Y of Counter X.
+
+What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_raw
+KernelVersion:	4.14
+Contact:	[email protected]
+Description:
+		Raw count data of Value Y of Counter X.
+
+What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_triggers
+KernelVersion:	4.14
+Contact:	[email protected]
+Description:
+		List of Signals associated to Value Y of Counter X.
+
+What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_trigger_signalX-Z
+KernelVersion:	4.14
+Contact:	[email protected]
+Description:
+		Trigger mode of Value Y for Signal Z. This attribute indicates
+		the condition of Signal Z that triggers the counter function
+		evaluation for Value Y. Both Value Y and Signal Z are part of
+		Counter X.
+
+What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_trigger_signalX-Z_available
+KernelVersion:	4.14
+Contact:	[email protected]
+Description:
+		Discrete set of available trigger modes are listed in this file
+		for the configuration of the respective Trigger associating
+		Signal Z of Counter X with Value Y of Counter X.
+
+What:		/sys/bus/iio/devices/iio:deviceX/signalX-Y_name
+KernelVersion:	4.14
+Contact:	[email protected]
+Description:
+		Read-only attribute that indicates the device-specific name of
+		Signal Y of Counter X.
+
+What:		/sys/bus/iio/devices/iio:deviceX/signalX-Y_raw
+KernelVersion:	4.14
+Contact:	[email protected]
+Description:
+		Raw signal data of Signal Y of Counter X.
Signature · 1 line
2.14.1
#8 lore raw

PATCH v3 4/6 docs: Add IIO Generic Counter Interface documentation

In reply to William Breathitt Gray

This patch adds high-level documentation about the IIO Generic Counter Interface.

---
 Documentation/driver-api/iio/generic-counter.txt | 526 +++++++++++++++++++++++
 1 file changed, 526 insertions(+)
 create mode 100644 Documentation/driver-api/iio/generic-counter.txt
Documentation/driver-api/iio/generic-counter.txt new file +526 -0
new file mode 100644
index 000000000000..0ce43b71e1c7
@@ -0,0 +1,526 @@
+=========================
+Generic Counter Interface
+=========================
+
+Introduction
+============
+
+Counter devices are prevalent within a diverse spectrum of industries.
+The ubiquitous presence of these devices necessitates a common interface
+and standard of interaction and exposure. This driver API attempts to
+resolve the issue of duplicate code found among existing counter device
+drivers by introducing a generic counter interface for consumption. The
+generic counter interface enables drivers to support and expose a common
+set of components and functionality present in counter devices.
+
+Theory
+======
+
+Counter devices can vary greatly in design, but regardless of whether
+some devices are quadrature encoder counters or pedometers, all counter
+devices consist of a core set of components. This core set of
+components, shared by all counter devices, is what forms the essence of
+the generic counter interface.
+
+There are three core components to a counter:
+
+        VALUE
+        -----
+        A Value represents the count data for a set of Signals. A Value
+        has a count function mode (e.g. "increment" or "quadrature x4")
+        which respresents the update behavior for the count data. A
+        Value also has a set of one or more associated Signals.
+
+        SIGNAL
+        ------
+        A Signal represents a count input line. A Signal may be
+	associated to one or more Values.
+
+        TRIGGER
+	-------
+        A Trigger represents a Value's count function trigger condition
+        mode (e.g. "rising edge" or "double pulse") for an associated
+        Signal.	If a Signal is associated with a Value, a respective
+        Trigger	instance for that association exists -- albeit perhaps
+        with a trigger condition mode of "none."
+
+A counter is defined as a set of input signals associated to count data
+that are generated by the evaluation of the state of the associated
+input signals as defined by the respective count functions. Within the
+context of the generic counter interface, a counter consists of Values
+each associated to a set of Signals, whose respective Trigger instances
+represent the count function update conditions for the associated
+Values.
+
+Paradigm
+========
+
+The most basic counter device may be expressed as a single Value
+associated with a single Signal via a single Trigger. Take for example
+a hypothetical counter device which simply accumulates a count of rising
+edges on a source input line.
+
+        Value                Trigger        Signal
+        -----                -------        ------
++---------------------+
+| Data: Count         |    Rising Edge     ________
+| Function: Increment |  <-------------   / Source \
+|                     |                  ____________
++---------------------+
+
+In this example, the Signal is a source input line with a pulsing
+voltage, while the Value is a persistent count which increments. The
+Signal is associated with the Value via a respective Trigger. The
+increment function is triggered by the condition specified by the
+Triggered -- in this case a rising edge condition. In summary, the
+counter device existence and behavior is aptly represented by respective
+Value, Signal, and Trigger components: a rising edge condition triggers
+an incrementation function on an accumulating count datum.
+
+A counter device is not limited to a single Signal; in fact, in theory
+an unlimited number of Signals may be associated with a Value. For
+example, a quadrature encoder counter device can keep track of position
+based on the states of two input lines.
+
+           Value                 Trigger      Signal
+           -----                 -------      ------
++-------------------------+
+| Data: Position          |    Both Edges      ___
+| Function: Quadrature x4 |  <-------------   / A \
+|                         |                  _______
+|                         |
+|                         |    Both Edges      ___
+|                         |  <-------------   / B \
+|                         |                  _______
++-------------------------+
+
+In this example, two Signals (quadrature encoder lines A and B) are
+associated to a single Value: a rising or falling edge on either A or B
+triggers the "Quadrature x4" function which determines the direction of
+movement and updates the respective position data. The "Quadrature x4"
+function is likely implemented in the hardware of the quadrature encoder
+counter device; the Value, Triggers, and Signals simply represent this
+hardware behavior and functionality.
+
+Signal associated to the same Value can have differing trigger
+conditions. For example, a quadrature encoder counter device operating
+in a non-quadrature mode could have one input line dedicated for
+movement and a second input line dedicated for direction.
+
+           Value                  Trigger      Signal
+           -----                  -------      ------
++------------------------- +
+| Data: Position           |    Rising Edge     ___
+| Function: Non-quadrature |  <-------------   / A \ (Movement)
+|                          |                  _______
+|                          |
+|                          |       None         ___
+|                          |  <-------------   / B \ (Direction)
+|                          |                  _______
++--------------------------+
+
+Only Signal A triggers the "Non-quadrature" update function, but the
+state of Signal B is still required in order to know the direction in
+order to properly update the position data. So in the end, both Signals
+are associated to the same Value via two respective Triggers, but only
+one Trigger has an active trigger condition while the other is left in a
+"None" condition mode to indicate its respective Signal's availability
+for state evaluation despite its non-triggering mode.
+
+Although the examples thus far have been representations of physical
+devices, this is not a necessity. A counter simply represent the
+evaluation (Value) of input data (Signals) triggered by specific
+conditions (Triggers). A counter can be the representation of more
+abstract components.
+
+For example, suppose a counter representation is desired for a DNA
+sequence analysizer which detects possible genetic diseases.
+
+        Value                     Trigger           Signal
+        -----                     -------           ------
++---------------------+
+| Data: Diseases      |    Gene Transcript (EST)     _____
+| Function: Cancers   |  <-----------------------   / DNA \ (GAAGTGC...)
+|                     |                            _________
++---------------------+
+
+In this scenario, the Signal is a stream of DNA nucleotide bases (As,
+Ts, Cs, and Gs), the Trigger is expressed sequence tags (ESTs), and the
+Value is a list of diseases discovered (in this case the function is
+evaluating for possible cancers). Note how the Signal in this example
+does not represent a physical voltage line, nor does the Trigger
+represent a physical voltage line state change, nor does the Value
+represent a strictly decimal data value.
+
+The DNA sequence analysizer example is contrived to illustrate the
+flexibility of the generic counter paradigm by demonstrating its
+capability of representing abstract concepts; however, physical devices
+are likely to be more fitting for such a representation.
+
+The key concept is that the Signal, Trigger, and Value are abstract
+representations which do not need to be closely married to their
+respective physical sources. This allows the user of a counter to
+divorce themselves from the nuances of physical components (such as
+whether an input line is differential or single-ended) and focus on the
+core idea of what the data and process represent (an accumulated count
+of rising edges).
+
+Userspace Interface
+===================
+
+Several sysfs attributes are generated by the generic counter interface,
+and reside under the /sys/bus/iio/devices/iio:deviceX directory.
+
+Each counter has a respective set of countX-Y and signalX-Y prefixed
+attributes, where X is the id set in the counter structure, and Y is the
+id of the respective Value or Signal.
+
+The generic counter interface sysfs attributes are as follows:
+
+        countX-Y_function: count function mode
+        countX-Y_function_available: available count function modes
+        countX-Y_name: Value name
+        countX-Y_raw: Value data
+        countX-Y_triggers: Value's associated Signals
+        countX-Y_trigger_signalX-Z: Value Y trigger mode for Signal Z
+        countX-Y_trigger_signalX-Z_available: available Value Y trigger
+                                              modes for Signal Z
+        signalX-Y_name: Signal name
+        signalX-Y_raw: Signal data
+
+Through these sysfs attributes, programs and scripts may interact with
+the generic counter paradigm Values, Triggers, and Signals of respective
+counter devices.
+
+Driver API
+==========
+
+Driver authors may utilize the generic counter interface in their code
+by including the include/linux/iio/counter.h header file. This header
+file provides several core data structures and function prototypes for
+defining a generic counter.
+
+struct iio_counter_signal
+-------------------------
+
+This structure defines a generic counter paradigm Signal component;
+typically this will correlate with an input channel on a physical
+counter device. This structure is the simplest to define with only two
+structure members which require explicit configuration:
+
+        id:     Unique ID used to identify the Signal
+
+        name:   Device-specific Signal name (typically the device input
+                channel name)
+
+struct iio_counter_trigger
+--------------------------
+
+This structure defines a generic counter paradigm Trigger component. To
+properly utilize this structure, trigger modes and an associated Signal
+must be defined:
+
+        mode:                   Index of the current trigger mode state
+
+        trigger_modes:          Array of trigger modes each represented
+	                        by a character string
+
+        num_trigger_modes:      Number of trigger modes provided in
+	                        trigger_modes array
+
+        signal:                 Pointer to associated Signal
+
+struct iio_counter_value
+------------------------
+
+This structure defines a generic counter paradigm Value component;
+typically this will correlate with the read data (the "count" value)
+provided by a physical counter device. This structure requires the
+explicit configuration of an ID, name, function modes (the function
+triggered on a Trigger condition), and optionally a set of initial
+associated Triggers:
+
+        id:                     Unique ID used to identify the Signal
+
+        name:                   Device-specific Value name (typically
+	                        the device read channel name)
+
+        mode:                   Index of the current function mode state
+
+        function_modes:         Array of function modes each represented
+	                        by a character string
+
+        num_function_modes:     Number of function modes provided in
+	                        function_modes array
+
+        triggers:               Array of associated Triggers
+
+        num_triggers:           Number of Triggers provided in triggers
+	                        array
+
+struct iio_counter_ops
+----------------------
+
+This structure defines callbacks to interact with the Value, Trigger,
+and Signal components:
+
+        signal_read:            Function to request a signal value from
+	                        the device. Return value will specify
+				the type of value returned by the
+				device. val and val2 will contain the
+				elements making up the returned value.
+
+	signal_write:           Function to write a signal value to the
+	                        device. Parameters are interpreted the
+				same as signal_read.
+
+        trigger_mode_set:       Function to set the trigger mode. mode
+	                        is the index of the requested mode from
+				the value trigger_modes array.
+
+        trigger_mode_get:       Function to get the current trigger
+	                        mode. Return value will specify the
+				index of the current mode from the value
+				trigger_modes array.
+
+        value_read:             Function to request a value value from
+	                        the device. Return value will specify
+				the type of value returned by the
+				device. val and val2 will contain the
+				elements making up the returned value.
+
+        value_write:            Function to write a value value to the
+	                        device. Parameters are interpreted the
+				same as value_read.
+
+        value_function_set:     Function to set the value function mode.
+	                        mode is the index of the requested mode
+				from the value function_modes array.
+
+        value_function_get:     Function to get the current value
+	                        function mode. Return value will specify
+				the index of the current mode from the
+				value function_modes array.
+
+struct iio_counter
+------------------
+
+This is the main data structure for a counter device; access to all
+respective Values, Triggers, and Signals is possible from this
+structure. This structure allows the configuration of an ID, name,
+function callbacks, initial Signals and initial Values, auxiliary IIO
+core channels and callbacks, and driver-specific data:
+
+        id:                     Unique ID used to identify the counter
+
+        name:                   Name of the counter device
+
+        dev:                    Device structure, which should be
+				assigned a parent and owner
+
+        ops:                    Function callbacks for counter
+	                        components (Signal, Trigger, Value)
+
+        signals:                Array of Signals
+
+        num_signals:            Number of Signals specified in signals
+	                        array
+
+        values:                 Array of Values
+
+        num_values:             Number of Values specified in values
+	                        array
+
+        channels:               Optional IIO core channels specification
+	                        structure table
+
+        num_channels:           Number of channels specified in channels
+
+        info:                   IIO core function callbacks and constant
+	                        info from driver
+
+        driver_data:            Driver-specific data
+
+Registration functions
+----------------------
+
+Counters may be registered to the system via the iio_counter_register
+function and subsequently unregistered via the iio_counter_unregister
+function. The devm_iio_counter_register and devm_iio_counter_unregister
+functions serve as device memory-managed versions of the
+iio_counter_register and iio_counter_unregister functions.
+
+An initialized iio_counter structure, which defines the Counter, is
+required to be passed in for registration; the Signals, Values, and
+Triggers for the Counter passed in via the signals and values arrays as
+part of the iio_counter structure. If auxiliary IIO core channels and
+functionality are required, IIO core channels and callbacks may be
+passed in via the channels and info members of the passed-in iio_counter
+structure.
+
+Implementation
+==============
+
+The IIO generic counter interface piggybacks off of the IIO core. This
+is primarily used to leverage the existing sysfs setup: the IIO_COUNT
+channel attributes represent the Counter Value, while the IIO_SIGNAL
+channel attributes represent the Counter Signal; auxilary IIO_COUNT
+attributes represent the Counter Signal connections (Triggers) and their
+respective state change configurations which trigger an associated
+"counter function" evaluation.
+
+The iio_counter_ops structure serves as a container for driver callbacks
+to communicate with the device; function callbacks are provided to read
+and write various Signals and Values, and set and get the "trigger mode"
+and "function mode" for various Triggers and Values respectively.
+
+To support a counter device, a driver must first allocate the available
+Counter Signals via iio_counter_signal structures. These Signals should
+be stored as an array and set to the signals array member of an
+allocated iio_counter structure before the Counter is registered to the
+system.
+
+Counter Values may be allocated via iio_counter_value structures, and
+respective Counter Signal associations (Triggers) made via
+iio_counter_trigger structures. Associated iio_counter_trigger
+structures are stored as an array and set to the the triggers array
+member of the respective iio_counter_value structure. These
+iio_counter_value structures are set to the values array member of an
+allocated iio_counter structure before the Counter is registered to the
+system.
+
+A counter device is registered to the system by passing the respective
+initialized iio_counter structure to the iio_counter_register function;
+similarly, the iio_counter_unregister function unregisters the
+respective counter.
+
+Architecture
+============
+
+Although the IIO Generic Counter Interface utilizes IIO core under the
+hood, driver authors are not necessarily required to interact with IIO
+core data structures and functions directly -- in theory, such details
+of the system are abstracted away. Driver authors only need to concern
+themselves with the Generic Counter specific data structures and
+functions found in the include/linux/iio/counter.h header file.
+
+In other words, the driver API is intended to expose itself sufficiently
+upon the principles and concepts of the generic counter paradigm (i.e.
+Values, Triggers, Signals, etc.) such that it may be indepedent from its
+underlying implementation; theoretically, the IIO core code in the
+implementation could be replaced away in its entirely by an alternative
+implementation all without the need to update existing drivers utilizing
+the Generic Counter Interface driver API.
+
+This paradigm separation however does result in some mapping concerns
+between Generic Counter functions to IIO core functions; in particular,
+parameters for the IIO core functions expect IIO core data structures
+(e.g. iio_dev and iio_chan_spec) which are not provided directly by the
+parameters for the respective Generic Counter functions. This results in
+a somewhat opaque pathway from a iio_counter structure to its associated
+iio_dev in order to support the required IIO core calls.
+
+The following call graphs should help illustrate some of the main IIO
+core dependencies:
+
++----------------------+
+| iio_counter_register |
++----------------------+
+  |  |  |
+  |  |  +-----------------------------+
+  |  +------------------+             |
+  |                     |             |
+  V                     V             V
++------------------+  +----------+  +---------------------+
+| iio_device_alloc |  | iio_priv |  | iio_device_register |
++------------------+  +----------+  +---------------------+
+
+The iio_counter_register function allocates and initializes a new
+iio_dev structure which will serve as the respective Counter's gateway
+to IIO core support. The address of the parent iio_counter structure is
+stored with the iio_dev structure via iio_priv in order to allow access
+back to the Counter from within the IIO core functions. Finally, the
+iio_dev structure is registered via iio_device_register.
+
++-----------------------+   +----------------------+
+| iio_read_channel_info |-->| iio_counter_read_raw |
++-----------------------+   +----------------------+
+                              |  |  |
+  +---------------------------+  |  |
+  |                +-------------+  |
+  |                |               ++
+  |                |               |
+  V                V               V
+  IIO_SIGNAL       IIO_COUNT       IIO_*
++-------------+  +------------+  +----------+
+| signal_read |  | value_read |  | read_raw |
++-------------+  +------------+  +----------+
+
++------------------------+   +-----------------------+
+| iio_write_channel_info |-->| iio_counter_write_raw |
++------------------------+   +-----------------------+
+                               |  |  |
+  +----------------------------+  |  |
+  |                 +-------------+  |
+  |                 |                |
+  |                 |                |
+  V                 V                V
+  IIO_SIGNAL        IIO_COUNT        IIO_*
++--------------+  +-------------+  +-----------+
+| signal_write |  | value_write |  | write_raw |
++--------------+  +-------------+  +-----------+
+
+Normally, the IIO core iio_read_channel_info and iio_write_channel_info
+functions respectiveluy call the driver-supplied read_raw and write_raw
+callbacks directly. Since the generic counter interface serves as an
+abstraction above IIO core, drive authors do not generally directly
+configure a read_raw/write_raw callback.
+
+Instead, the IIO Generic Counter Interface hooks on to the
+iio_read_channel_info and iio_write_channel_info expected read_raw and
+write_raw callbacks respectively via iio_counter_read_raw and
+iio_counter_write_raw. The iio_counter_read_raw and
+iio_counter_write_raw functions then call the respective driver-supplied
+signal_read/value_read and signal_write/value_write callbacks
+respectively for the appropriate IIO_SIGNAL OR IIO_COUNT. If an IIO core
+channel that was not part of the generic counter paradigm was supplied
+via the channels member of the iio_counter structure, then the
+respective driver-supplied (via the iio_counter structure info member)
+read_raw and write_raw are called.
+
++---------------------------+        +----------------------------+
+| iio_read_channel_ext_info |        | iio_write_channel_ext_info |
++---------------------------+        +----------------------------+
+  |                                    |
+  V                                    V
++-------------------------------+    +--------------------------------+
+| iio_counter_trigger_mode_read |    | iio_counter_trigger_mode_write |
++-------------------------------+    +--------------------------------+
+  |                                    |
+  V                                    V
++------------------+                 +------------------+
+| trigger_mode_get |                 | trigger_mode_set |
++------------------+                 +------------------+
+
++---------------+                     +----------------+
+| iio_enum_read |                     | iio_enum_write |
++---------------+                     +----------------+
+  |                                     |
+  V                                     V
++--------------------------------+    +--------------------------------+
+| iio_counter_value_function_get |    | iio_counter_value_function_set |
++--------------------------------+    +--------------------------------+
+  |                                     |
+  V                                     V
++--------------------+                +--------------------+
+| value_function_get |                | value_function_set |
++--------------------+                +--------------------+
+
+The driver-supplied trigger_mode_get and trigger_mode_set callbacks hook
+on to the iio_read_channel_ext_info and iio_write_channel_ext_info
+functions respectively via the iio_counter_trigger_mode_read and
+iio_counter_trigger_mode_write functions. Similarly, the driver-supplied
+value_function_get and value_function set callbacks hook on to the
+iio_enum_read and iio_enum_write functions respectively via the
+iio_counter_value_function_get and iio_counter_value_function set
+functions.
Signature · 1 line
2.14.1
#10 lore raw

PATCH v3 5/6 iio: Add dummy counter driver

In reply to William Breathitt Gray

This patch introduces the dummy counter driver. The dummy counter driver serves as a reference implementation of a driver that utilizes the Generic Counter interface.

Writing individual '1' and '0' characters to the Signal attributes allows a user to simulate a typical Counter Signal input stream for evaluation; the Counter will evaluate the Signal data based on the respective trigger mode for the associated Signal, and trigger the associated counter function specified by the respective function mode. The current Value value may be read, and the Value value preset by a write.

---
 drivers/iio/counter/Kconfig         |  15 ++
 drivers/iio/counter/Makefile        |   1 +
 drivers/iio/counter/dummy-counter.c | 293 ++++++++++++++++++++++++++++++++++++
 3 files changed, 309 insertions(+)
 create mode 100644 drivers/iio/counter/dummy-counter.c
drivers/iio/counter/Kconfig +15 -0
index c8becfe78e28..494aed40e9c9 100644
@@ -22,6 +22,21 @@ config 104_QUAD_8
 	  The base port addresses for the devices may be configured via the base
 	  array module parameter.
 
+config DUMMY_COUNTER
+	tristate "Dummy counter driver"
+	help
+	  Select this option to enable the dummy counter driver. The dummy
+	  counter driver serves as a reference implementation of a driver that
+	  utilizes the Generic Counter interface.
+
+	  Writing individual '1' and '0' characters to the Signal attributes
+	  allows a user to simulate a typical Counter Signal input stream for
+	  evaluation; the Counter will evaluate the Signal data based on the
+	  respective trigger mode for the associated Signal, and trigger the
+	  associated counter function specified by the respective function mode.
+	  The current Value value may be read, and the Value value preset by a
+	  write.
+
 config STM32_LPTIMER_CNT
 	tristate "STM32 LP Timer encoder counter driver"
 	depends on MFD_STM32_LPTIMER || COMPILE_TEST
drivers/iio/counter/Makefile +1 -0
index 1b9a896eb488..8c2ef0115426 100644
@@ -5,4 +5,5 @@
 # When adding new entries keep the list in alphabetical order
 
 obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
+obj-$(CONFIG_DUMMY_COUNTER)	+= dummy-counter.o
 obj-$(CONFIG_STM32_LPTIMER_CNT)	+= stm32-lptimer-cnt.o
drivers/iio/counter/dummy-counter.c new file +293 -0

293 lines not on this page · raw message

Signature · 1 line
2.14.1
#13 lore raw

PATCH v3 6/6 iio: 104-quad-8: Add IIO generic counter interface support

In reply to William Breathitt Gray

This patch adds support for the IIO generic counter interface to the 104-QUAD-8 driver. The existing 104-QUAD-8 device interface should not be affected by this patch; all changes are intended as supplemental additions as perceived by the user.

IIO Counter Signals are defined for all quadrature input pairs (A and B), as well as index input lines. However, IIO Counter Triggers are not created for the index input Signals. IIO Counter Values are created for the eight quadrature channel counts, and their respective Signals are associated via IIO Counter Triggers.

The new generic counter interface sysfs attributes expose the same functionality and data available via the existing 104-QUAD-8 device interface. Four IIO Counter Value function modes are available, correlating to the four possible quadrature mode configurations:
"non-quadrature," "quadrature x1," "quadrature x2," and "quadrature x4."

---
 drivers/iio/counter/104-quad-8.c | 294 ++++++++++++++++++++++++++++++++++++---
 1 file changed, 277 insertions(+), 17 deletions(-)
drivers/iio/counter/104-quad-8.c +277 -17
index b56985078d8c..690a520e70df 100644
@@ -16,6 +16,7 @@
 #include <linux/bitops.h>
 #include <linux/device.h>
 #include <linux/errno.h>
+#include <linux/iio/counter.h>
 #include <linux/iio/iio.h>
 #include <linux/iio/types.h>
 #include <linux/io.h>
@@ -24,6 +25,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/string.h>
 #include <linux/types.h>
 
 #define QUAD8_EXTENT 32
@@ -37,6 +39,7 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
 
 /**
  * struct quad8_iio - IIO device private data structure
+ * @counter:		instance of the iio_counter
  * @preset:		array of preset values
  * @count_mode:		array of count mode configurations
  * @quadrature_mode:	array of quadrature mode configurations
@@ -48,6 +51,7 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
  * @base:		base port address of the IIO device
  */
 struct quad8_iio {
+	struct iio_counter counter;
 	unsigned int preset[QUAD8_NUM_COUNTERS];
 	unsigned int count_mode[QUAD8_NUM_COUNTERS];
 	unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
@@ -527,33 +531,289 @@ static const struct iio_chan_spec quad8_channels[] = {
 	QUAD8_COUNT_CHAN(7), QUAD8_INDEX_CHAN(7)
 };
 
+static int quad8_signal_read(struct iio_counter *counter,
+	struct iio_counter_signal *signal, int *val, int *val2)
+{
+	struct quad8_iio *const priv = counter->driver_data;
+
+	if (signal->id < 16)
+		return -EINVAL;
+
+	*val = !!(inb(priv->base + 0x16) & BIT(signal->id - 16));
+
+	return IIO_VAL_INT;
+}
+
+static int quad8_trigger_mode_get(struct iio_counter *counter,
+	struct iio_counter_value *value, struct iio_counter_trigger *trigger)
+{
+	struct quad8_iio *const priv = counter->driver_data;
+	const unsigned int mode = priv->quadrature_mode[value->id];
+	const unsigned int scale = priv->quadrature_scale[value->id];
+	unsigned int direction;
+	const unsigned int flag_addr = priv->base + 2 * value->id + 1;
+	const int signal_id = trigger->signal->id % 2;
+
+	if (mode)
+		switch (scale) {
+		case 0:
+			/* U/D flag: 1 = up, 0 = down */
+			/* direction: 0 = up, 1 = down */
+			direction = !(inb(flag_addr) & BIT(5));
+			if (!signal_id)
+				return direction + 1;
+			break;
+		case 1:
+			if (!signal_id)
+				return 3;
+			break;
+		case 2:
+			return 3;
+		}
+	else
+		if (!signal_id)
+			return 1;
+
+	return 0;
+}
+
+static int quad8_value_read(struct iio_counter *counter,
+	struct iio_counter_value *value, int *val, int *val2)
+{
+	struct quad8_iio *const priv = counter->driver_data;
+	const int base_offset = priv->base + 2 * value->id;
+	unsigned int flags;
+	unsigned int borrow;
+	unsigned int carry;
+	int i;
+
+	flags = inb(base_offset + 1);
+	borrow = flags & BIT(0);
+	carry = !!(flags & BIT(1));
+
+	/* Borrow XOR Carry effectively doubles count range */
+	*val = (borrow ^ carry) << 24;
+
+	/* Reset Byte Pointer; transfer Counter to Output Latch */
+	outb(0x11, base_offset + 1);
+
+	for (i = 0; i < 3; i++)
+		*val |= (unsigned int)inb(base_offset) << (8 * i);
+
+	return IIO_VAL_INT;
+}
+
+static int quad8_value_write(struct iio_counter *counter,
+	struct iio_counter_value *value, int val, int val2)
+{
+	struct quad8_iio *const priv = counter->driver_data;
+	const int base_offset = priv->base + 2 * value->id;
+	int i;
+
+	/* Only 24-bit values are supported */
+	if ((unsigned int)val > 0xFFFFFF)
+		return -EINVAL;
+
+	/* Reset Byte Pointer */
+	outb(0x01, base_offset + 1);
+
+	/* Counter can only be set via Preset Register */
+	for (i = 0; i < 3; i++)
+		outb(val >> (8 * i), base_offset);
+
+	/* Transfer Preset Register to Counter */
+	outb(0x08, base_offset + 1);
+
+	/* Reset Byte Pointer */
+	outb(0x01, base_offset + 1);
+
+	/* Set Preset Register back to original value */
+	val = priv->preset[value->id];
+	for (i = 0; i < 3; i++)
+		outb(val >> (8 * i), base_offset);
+
+	/* Reset Borrow, Carry, Compare, and Sign flags */
+	outb(0x02, base_offset + 1);
+	/* Reset Error flag */
+	outb(0x06, base_offset + 1);
+
+	return 0;
+}
+
+static int quad8_value_function_set(struct iio_counter *counter,
+	struct iio_counter_value *value, unsigned int mode)
+{
+	struct quad8_iio *const priv = counter->driver_data;
+	const unsigned int mode_cfg = mode << 3 |
+		priv->count_mode[value->id] << 1;
+	const unsigned int idr_cfg = priv->index_polarity[value->id] << 1;
+	const int base_offset = priv->base + 2 * value->id + 1;
+
+	if (mode)
+		priv->quadrature_scale[value->id] = mode - 1;
+	else {
+		/* Quadrature scaling only available in quadrature mode */
+		priv->quadrature_scale[value->id] = 0;
+
+		/* Synchronous function not supported in non-quadrature mode */
+		if (priv->synchronous_mode[value->id]) {
+			priv->synchronous_mode[value->id] = 0;
+			outb(0x60 | idr_cfg, base_offset);
+		}
+	}
+
+	priv->quadrature_mode[value->id] = !!mode;
+
+	/* Load mode configuration to Counter Mode Register */
+	outb(0x20 | mode_cfg, base_offset);
+
+	return 0;
+}
+
+static int quad8_value_function_get(struct iio_counter *counter,
+	struct iio_counter_value *value)
+{
+	struct quad8_iio *const priv = counter->driver_data;
+	unsigned int quadrature_mode = priv->quadrature_mode[value->id];
+
+	return (quadrature_mode) ? priv->quadrature_scale[value->id] + 1 : 0;
+}
+
+static const struct iio_counter_ops quad8_ops = {
+	.signal_read = quad8_signal_read,
+	.trigger_mode_get = quad8_trigger_mode_get,
+	.value_read = quad8_value_read,
+	.value_write = quad8_value_write,
+	.value_function_set = quad8_value_function_set,
+	.value_function_get = quad8_value_function_get
+};
+
+static const char *const quad8_function_modes[] = {
+	"non-quadrature",
+	"quadrature x1",
+	"quadrature x2",
+	"quadrature x4"
+};
+
+#define QUAD8_SIGNAL(_id, _name) {	\
+	.id = _id,			\
+	.name = _name			\
+}
+
+static const struct iio_counter_signal quad8_signals[] = {
+	QUAD8_SIGNAL(0, "Channel 1 Quadrature A"),
+	QUAD8_SIGNAL(1, "Channel 1 Quadrature B"),
+	QUAD8_SIGNAL(2, "Channel 2 Quadrature A"),
+	QUAD8_SIGNAL(3, "Channel 2 Quadrature B"),
+	QUAD8_SIGNAL(4, "Channel 3 Quadrature A"),
+	QUAD8_SIGNAL(5, "Channel 3 Quadrature B"),
+	QUAD8_SIGNAL(6, "Channel 4 Quadrature A"),
+	QUAD8_SIGNAL(7, "Channel 4 Quadrature B"),
+	QUAD8_SIGNAL(8, "Channel 5 Quadrature A"),
+	QUAD8_SIGNAL(9, "Channel 5 Quadrature B"),
+	QUAD8_SIGNAL(10, "Channel 6 Quadrature A"),
+	QUAD8_SIGNAL(11, "Channel 6 Quadrature B"),
+	QUAD8_SIGNAL(12, "Channel 7 Quadrature A"),
+	QUAD8_SIGNAL(13, "Channel 7 Quadrature B"),
+	QUAD8_SIGNAL(14, "Channel 8 Quadrature A"),
+	QUAD8_SIGNAL(15, "Channel 8 Quadrature B"),
+	QUAD8_SIGNAL(16, "Channel 1 Index"),
+	QUAD8_SIGNAL(17, "Channel 2 Index"),
+	QUAD8_SIGNAL(18, "Channel 3 Index"),
+	QUAD8_SIGNAL(19, "Channel 4 Index"),
+	QUAD8_SIGNAL(20, "Channel 5 Index"),
+	QUAD8_SIGNAL(21, "Channel 6 Index"),
+	QUAD8_SIGNAL(22, "Channel 7 Index"),
+	QUAD8_SIGNAL(23, "Channel 8 Index")
+};
+
+#define QUAD8_VALUE(_id, _name) {					\
+	.id = _id,							\
+	.name = _name,							\
+	.mode = 0,							\
+	.function_modes = quad8_function_modes,				\
+	.num_function_modes = ARRAY_SIZE(quad8_function_modes)		\
+}
+
+static const struct iio_counter_value quad8_values[] = {
+	QUAD8_VALUE(0, "Channel 1 Count"), QUAD8_VALUE(1, "Channel 2 Count"),
+	QUAD8_VALUE(2, "Channel 3 Count"), QUAD8_VALUE(3, "Channel 4 Count"),
+	QUAD8_VALUE(4, "Channel 5 Count"), QUAD8_VALUE(5, "Channel 6 Count"),
+	QUAD8_VALUE(6, "Channel 7 Count"), QUAD8_VALUE(7, "Channel 8 Count")
+};
+
+static const char *const quad8_trigger_modes[] = {
+	"none",
+	"rising edge",
+	"falling edge",
+	"both edges"
+};
+
 static int quad8_probe(struct device *dev, unsigned int id)
 {
-	struct iio_dev *indio_dev;
-	struct quad8_iio *priv;
+	struct iio_counter_signal *signals;
+	const size_t num_signals = ARRAY_SIZE(quad8_signals);
+	struct iio_counter_value *values;
+	const size_t num_values = ARRAY_SIZE(quad8_values);
+	struct iio_counter_trigger *triggers;
+	struct quad8_iio *quad8iio;
 	int i, j;
 	unsigned int base_offset;
 
-	indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
-	if (!indio_dev)
-		return -ENOMEM;
-
-	if (!devm_request_region(dev, base[id], QUAD8_EXTENT,
-		dev_name(dev))) {
+	if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) {
 		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
 			base[id], base[id] + QUAD8_EXTENT);
 		return -EBUSY;
 	}
 
-	indio_dev->info = &quad8_info;
-	indio_dev->modes = INDIO_DIRECT_MODE;
-	indio_dev->num_channels = ARRAY_SIZE(quad8_channels);
-	indio_dev->channels = quad8_channels;
-	indio_dev->name = dev_name(dev);
-	indio_dev->dev.parent = dev;
+	signals = devm_kmalloc(dev, sizeof(quad8_signals), GFP_KERNEL);
+	if (!signals)
+		return -ENOMEM;
+
+	memcpy(signals, quad8_signals, sizeof(quad8_signals));
+
+	values = devm_kmalloc(dev, sizeof(quad8_values), GFP_KERNEL);
+	if (!values)
+		return -ENOMEM;
+
+	memcpy(values, quad8_values, sizeof(quad8_values));
+
+	/* Associate values with their respective signals */
+	for (i = 0; i < num_values; i++) {
+		triggers = devm_kmalloc(dev, 2 * sizeof(*triggers), GFP_KERNEL);
+		if (!triggers)
+			return -ENOMEM;
+
+		/* Starts up in non-quadrature mode */
+		triggers[0].mode = 1;
+		triggers[0].trigger_modes = quad8_trigger_modes;
+		triggers[0].num_trigger_modes = ARRAY_SIZE(quad8_trigger_modes);
+		triggers[0].signal = &signals[2 * i];
+		triggers[1].mode = 0;
+		triggers[1].trigger_modes = quad8_trigger_modes;
+		triggers[1].num_trigger_modes = ARRAY_SIZE(quad8_trigger_modes);
+		triggers[1].signal = &signals[2 * i + 1];
+
+		values[i].triggers = triggers;
+		values[i].num_triggers = 2;
+	}
+
+	quad8iio = devm_kzalloc(dev, sizeof(*quad8iio), GFP_KERNEL);
+	if (!quad8iio)
+		return -ENOMEM;
 
-	priv = iio_priv(indio_dev);
-	priv->base = base[id];
+	quad8iio->counter.name = dev_name(dev);
+	quad8iio->counter.dev = dev;
+	quad8iio->counter.ops = &quad8_ops;
+	quad8iio->counter.signals = signals;
+	quad8iio->counter.num_signals = num_signals;
+	quad8iio->counter.values = values;
+	quad8iio->counter.num_values = num_values;
+	quad8iio->counter.channels = quad8_channels;
+	quad8iio->counter.num_channels = ARRAY_SIZE(quad8_channels);
+	quad8iio->counter.info = &quad8_info;
+	quad8iio->counter.driver_data = quad8iio;
+	quad8iio->base = base[id];
 
 	/* Reset all counters and disable interrupt function */
 	outb(0x01, base[id] + 0x11);
@@ -579,7 +839,7 @@ static int quad8_probe(struct device *dev, unsigned int id)
 	/* Enable all counters */
 	outb(0x00, base[id] + 0x11);
 
-	return devm_iio_device_register(dev, indio_dev);
+	return devm_iio_counter_register(dev, &quad8iio->counter);
 }
 
 static struct isa_driver quad8_driver = {
Signature · 1 line
2.14.1
maintainer
#11 lore raw

Re: [PATCH v3 5/6] iio: Add dummy counter driver

In reply to William Breathitt Gray
Quoted 3 lines · On Thu, 5 Oct 2017 14:14:38 -0400 William Breathitt Gray…

This patch introduces the dummy counter driver. The dummy counter driver serves as a reference implementation of a driver that utilizes the Generic Counter interface.

This is great - I was planning to write one of these to try out the interface and you've already done it :)

Quoted 10 lines

Writing individual '1' and '0' characters to the Signal attributes allows a user to simulate a typical Counter Signal input stream for evaluation; the Counter will evaluate the Signal data based on the respective trigger mode for the associated Signal, and trigger the associated counter function specified by the respective function mode. The current Value value may be read, and the Value value preset by a write.

Comments are more generic suggestions for improving the example than general comments on the ABI - that all seems to make reasonable sense (other than when the documents contain the wonderful
Value value - not confusing at all ;)

Quoted 85 lines
---
 drivers/iio/counter/Kconfig         |  15 ++
 drivers/iio/counter/Makefile        |   1 +
 drivers/iio/counter/dummy-counter.c | 293 ++++++++++++++++++++++++++++++++++++
 3 files changed, 309 insertions(+)
 create mode 100644 drivers/iio/counter/dummy-counter.c
drivers/iio/counter/Kconfig +15 -0
index c8becfe78e28..494aed40e9c9 100644
@@ -22,6 +22,21 @@ config 104_QUAD_8
 	  The base port addresses for the devices may be configured via the base
 	  array module parameter.
 
+config DUMMY_COUNTER
+	tristate "Dummy counter driver"
+	help
+	  Select this option to enable the dummy counter driver. The dummy
+	  counter driver serves as a reference implementation of a driver that
+	  utilizes the Generic Counter interface.
+
+	  Writing individual '1' and '0' characters to the Signal attributes
+	  allows a user to simulate a typical Counter Signal input stream for
+	  evaluation; the Counter will evaluate the Signal data based on the
+	  respective trigger mode for the associated Signal, and trigger the
+	  associated counter function specified by the respective function mode.
+	  The current Value value may be read, and the Value value preset by a
+	  write.
+
 config STM32_LPTIMER_CNT
 	tristate "STM32 LP Timer encoder counter driver"
 	depends on MFD_STM32_LPTIMER || COMPILE_TEST
drivers/iio/counter/Makefile +1 -0
index 1b9a896eb488..8c2ef0115426 100644
@@ -5,4 +5,5 @@
 # When adding new entries keep the list in alphabetical order
 
 obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
+obj-$(CONFIG_DUMMY_COUNTER)	+= dummy-counter.o
 obj-$(CONFIG_STM32_LPTIMER_CNT)	+= stm32-lptimer-cnt.o
drivers/iio/counter/dummy-counter.c new file +36 -0
new file mode 100644
index 000000000000..6ecc9854894f
@@ -0,0 +1,293 @@
+/*
+ * Dummy counter driver
+ * Copyright (C) 2017 William Breathitt Gray
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/iio/counter.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+#define DUMCNT_NUM_COUNTERS 2
+/**
+ * struct dumcnt - private data structure
+ * @counter:	instance of the iio_counter
+ * @counts:	array of accumulation values
+ * @states:	array of input line states
+ */
+struct dumcnt {
+	struct iio_counter counter;
+	unsigned int counts[DUMCNT_NUM_COUNTERS];
+	unsigned int states[DUMCNT_NUM_COUNTERS];
+};
+
+static int dumcnt_signal_read(struct iio_counter *counter,
+	struct iio_counter_signal *signal, int *val, int *val2)

drivers/iio/counter/dummy-counter.c line 46

Quoted 10 lines
drivers/iio/counter/dummy-counter.c new file +10 -0
+{
+	struct dumcnt *const priv = counter->driver_data;
+	*val = priv->states[signal->id];
+
+	return IIO_VAL_INT;
+}
+
+static int dumcnt_signal_write(struct iio_counter *counter,
+	struct iio_counter_signal *signal, int val, int val2)
+{

This is an odd one to have in the generic interface. What real hardware does writing the state make sense for? If it is only fake drivers - figure out a way to do it without having to add to the generic interfaces.

Quoted 92 lines
drivers/iio/counter/dummy-counter.c new file +92 -0
+	struct dumcnt *const priv = counter->driver_data;
+	const unsigned int id = signal->id;
+	const unsigned int prev_state = priv->states[id];
+	struct iio_counter_value *const value = counter->values + id;
+	const unsigned int function_mode = value->mode;
+	const unsigned int trigger_mode = value->triggers[0].mode;
+	unsigned int triggered = 0;
+
+	if (val && val != 1)
+		return -EINVAL;
+
+	/* If no state change then just exit */
+	if (prev_state == val)
+		return 0;
+
+	priv->states[id] = val;
+
+	switch (trigger_mode) {
+	/* "none" case */
+	case 0:
+		return 0;
+	/* "rising edge" case */
+	case 1:
+		if (!prev_state)
+			triggered = 1;
+		break;
+	/* "falling edge" case */
+	case 2:
+		if (prev_state)
+			triggered = 1;
+		break;
+	/* "both edges" case */
+	case 3:
+		triggered = 1;
+		break;
+	}
+
+	/* If counter function triggered */
+	if (triggered)
+		/* "increase" case */
+		if (function_mode)
+			priv->counts[id]++;
+		/* "decrease" case */
+		else
+			priv->counts[id]--;
+
+	return 0;
+}
+
+static int dumcnt_trigger_mode_set(struct iio_counter *counter,
+	struct iio_counter_value *value, struct iio_counter_trigger *trigger,
+	unsigned int mode)
+{
+	if (mode >= trigger->num_trigger_modes)
+		return -EINVAL;
+
+	trigger->mode = mode;
+
+	return 0;
+}
+
+static int dumcnt_trigger_mode_get(struct iio_counter *counter,
+	struct iio_counter_value *value, struct iio_counter_trigger *trigger)
+{
+	return trigger->mode;
+}
+
+static int dumcnt_value_read(struct iio_counter *counter,
+	struct iio_counter_value *value, int *val, int *val2)
+{
+	struct dumcnt *const priv = counter->driver_data;
+
+	*val = priv->counts[value->id];
+
+	return IIO_VAL_INT;
+}
+
+static int dumcnt_value_write(struct iio_counter *counter,
+	struct iio_counter_value *value, int val, int val2)
+{
+	struct dumcnt *const priv = counter->driver_data;
+
+	priv->counts[value->id] = val;
+
+	return 0;
+}
+
+static int dumcnt_value_function_set(struct iio_counter *counter,
+	struct iio_counter_value *value, unsigned int mode)
+{
+	if (mode >= value->num_function_modes)
+		return -EINVAL;

drivers/iio/counter/dummy-counter.c line 148

Quoted 10 lines
drivers/iio/counter/dummy-counter.c new file +10 -0
+
+	value->mode = mode;
+
+	return 0;
+}
+
+static int dumcnt_value_function_get(struct iio_counter *counter,
+	struct iio_counter_value *value)
+{
+	return value->mode;

If it's called function in the function name, call it function in the structure as well rather than mode.

Quoted 5 lines
drivers/iio/counter/dummy-counter.c new file +5 -0
+}
+
+static const struct iio_counter_ops dumcnt_ops = {
+	.signal_read = dumcnt_signal_read,
+	.signal_write = dumcnt_signal_write,

drivers/iio/counter/dummy-counter.c line 163

Quoted 10 lines
drivers/iio/counter/dummy-counter.c new file +10 -0
+	.trigger_mode_get = dumcnt_trigger_mode_get,
+	.trigger_mode_set = dumcnt_trigger_mode_set,
+	.value_read = dumcnt_value_read,
+	.value_write = dumcnt_value_write,
+	.value_function_set = dumcnt_value_function_set,
+	.value_function_get = dumcnt_value_function_get
+};
+
+static const char *const dumcnt_function_modes[] = {
+	"decrease",

I think increment was used somewhere in the docs... It's clearer, but you need to document this ABI to stop having subtle variations of it like this (even if I imagined it ;)

Quoted 15 lines
drivers/iio/counter/dummy-counter.c new file +15 -0
+	"increase"
+};
+
+#define DUMCNT_SIGNAL(_id, _name) {	\
+	.id = _id,			\
+	.name = _name			\
+}
+
+static const struct iio_counter_signal dumcnt_signals[] = {
+	DUMCNT_SIGNAL(0, "Signal A"), DUMCNT_SIGNAL(1, "Signal B")
+};
+
+#define DUMCNT_VALUE(_id, _name) {					\
+	.id = _id,							\
+	.name = _name,							\

drivers/iio/counter/dummy-counter.c line 188

Quoted 10 lines
drivers/iio/counter/dummy-counter.c new file +10 -0
+	.mode = 0,							\
+	.function_modes = dumcnt_function_modes,			\
+	.num_function_modes = ARRAY_SIZE(dumcnt_function_modes)		\
+}
+
+static const struct iio_counter_value dumcnt_values[] = {
+	DUMCNT_VALUE(0, "Count A"), DUMCNT_VALUE(1, "Count B")
+};
+
+static const char *const dumcnt_trigger_modes[] = {

As mentioned below, use an enum for the index as then you can make it obvious what 0 means when you set the mode to it later.

drivers/iio/counter/dummy-counter.c line 199

Quoted 11 lines
drivers/iio/counter/dummy-counter.c new file +11 -0
+	"none",
+	"rising edge",
+	"falling edge",
+	"both edges"
+};
+
+static int dumcnt_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct iio_counter_signal *signals;
+	const size_t num_signals = ARRAY_SIZE(dumcnt_signals);

Don't bother with local variable - makes it less obvious what is going on.

drivers/iio/counter/dummy-counter.c line 201

Quoted 2 lines
drivers/iio/counter/dummy-counter.c new file +2 -0
+	struct iio_counter_value *values;
+	const size_t num_values = ARRAY_SIZE(dumcnt_values);

Local variable doesn't add anything and if anything makes it slightly harder to check what is going on.

drivers/iio/counter/dummy-counter.c line 210

Quoted 9 lines
drivers/iio/counter/dummy-counter.c new file +9 -0
+	struct iio_counter_trigger *triggers;
+	int i;
+	struct dumcnt *dumcnt;
+
+	signals = devm_kmalloc(dev, sizeof(dumcnt_signals), GFP_KERNEL);
+	if (!signals)
+		return -ENOMEM;
+
+	memcpy(signals, dumcnt_signals, sizeof(dumcnt_signals));

devm_kmemdup?

drivers/iio/counter/dummy-counter.c line 216

Quoted 6 lines
drivers/iio/counter/dummy-counter.c new file +6 -0
+
+	values = devm_kmalloc(dev, sizeof(dumcnt_values), GFP_KERNEL);
+	if (!values)
+		return -ENOMEM;
+
+	memcpy(values, dumcnt_values, sizeof(dumcnt_values));

devm_kmemdup?

drivers/iio/counter/dummy-counter.c line 224

Quoted 8 lines
drivers/iio/counter/dummy-counter.c new file +8 -0
+
+	/* Associate values with their respective signals */
+	for (i = 0; i < num_values; i++) {
+		triggers = devm_kmalloc(dev, sizeof(*triggers), GFP_KERNEL);
+		if (!triggers)
+			return -ENOMEM;
+
+		triggers->mode = 0;

Use an enum for the dumcn_trigger_modes array index then specify by enum value here. Will make it more readable.

Quoted 15 lines
drivers/iio/counter/dummy-counter.c new file +15 -0
+		triggers->trigger_modes = dumcnt_trigger_modes;
+		triggers->num_trigger_modes = ARRAY_SIZE(dumcnt_trigger_modes);
+		triggers->signal = &signals[i];
+
+		values[i].triggers = triggers;
+		values[i].num_triggers = 1;
+	}
+
+	dumcnt = devm_kzalloc(dev, sizeof(*dumcnt), GFP_KERNEL);
+	if (!dumcnt)
+		return -ENOMEM;
+
+	dumcnt->counter.name = dev_name(dev);
+	dumcnt->counter.dev = dev;
+	dumcnt->counter.ops = &dumcnt_ops;

drivers/iio/counter/dummy-counter.c line 249

Quoted 10 lines
drivers/iio/counter/dummy-counter.c new file +10 -0
+	dumcnt->counter.signals = signals;
+	dumcnt->counter.num_signals = num_signals;
+	dumcnt->counter.values = values;
+	dumcnt->counter.num_values = num_values;
+	dumcnt->counter.driver_data = dumcnt;
+
+	return devm_iio_counter_register(dev, &dumcnt->counter);
+}
+
+static struct platform_device *dumcnt_device;

Support multiple instances - nick this stuff from the IIO dummy driver or more specifically the industrialio-sw-device.c

drivers/iio/counter/dummy-counter.c line 253

Quoted 4 lines
drivers/iio/counter/dummy-counter.c new file +4 -0
+
+static struct platform_driver dumcnt_driver = {
+	.driver = {
+		.name = "104-quad-8"

Don't do that! Give it it's own name.

drivers/iio/counter/dummy-counter.c line 266

Quoted 13 lines
drivers/iio/counter/dummy-counter.c new file +13 -0
+	}
+};
+
+static void __exit dumcnt_exit(void)
+{
+	platform_device_unregister(dumcnt_device);
+	platform_driver_unregister(&dumcnt_driver);
+}
+
+static int __init dumcnt_init(void)
+{
+	int err;
+

General thing, but if we are going to upstream this with the subsystem, make device instantiation happen via configfs.

Quoted 27 lines
drivers/iio/counter/dummy-counter.c new file +27 -0
+	dumcnt_device = platform_device_alloc(dumcnt_driver.driver.name, -1);
+	if (!dumcnt_device)
+		return -ENOMEM;
+
+	err = platform_device_add(dumcnt_device);
+	if (err)
+		goto err_platform_device;
+
+	err = platform_driver_probe(&dumcnt_driver, dumcnt_probe);
+	if (err)
+		goto err_platform_driver;
+
+	return 0;
+
+err_platform_driver:
+	platform_device_del(dumcnt_device);
+err_platform_device:
+	platform_device_put(dumcnt_device);
+	return err;
+}
+
+module_init(dumcnt_init);
+module_exit(dumcnt_exit);
+
+MODULE_AUTHOR("William Breathitt Gray <[email protected]>");
+MODULE_DESCRIPTION("Dummy counter driver");
+MODULE_LICENSE("GPL v2");
maintainer
#14 lore raw

Re: [PATCH v3 6/6] iio: 104-quad-8: Add IIO generic counter interface support

In reply to William Breathitt Gray
Quoted 11 lines · On Thu, 5 Oct 2017 14:14:53 -0400 William Breathitt Gray…

This patch adds support for the IIO generic counter interface to the 104-QUAD-8 driver. The existing 104-QUAD-8 device interface should not be affected by this patch; all changes are intended as supplemental additions as perceived by the user.

IIO Counter Signals are defined for all quadrature input pairs (A and B), as well as index input lines. However, IIO Counter Triggers are not created for the index input Signals. IIO Counter Values are created for the eight quadrature channel counts, and their respective Signals are associated via IIO Counter Triggers.

Quoted 7 lines

The new generic counter interface sysfs attributes expose the same functionality and data available via the existing 104-QUAD-8 device interface. Four IIO Counter Value function modes are available, correlating to the four possible quadrature mode configurations:
"non-quadrature," "quadrature x1," "quadrature x2," and "quadrature x4."

A few generic comments inline - nothing significant.

Jonathan

Quoted 294 lines
---
 drivers/iio/counter/104-quad-8.c | 294 ++++++++++++++++++++++++++++++++++++---
 1 file changed, 277 insertions(+), 17 deletions(-)
drivers/iio/counter/104-quad-8.c +230 -14
index b56985078d8c..690a520e70df 100644
@@ -16,6 +16,7 @@
 #include <linux/bitops.h>
 #include <linux/device.h>
 #include <linux/errno.h>
+#include <linux/iio/counter.h>
 #include <linux/iio/iio.h>
 #include <linux/iio/types.h>
 #include <linux/io.h>
@@ -24,6 +25,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/string.h>
 #include <linux/types.h>
 
 #define QUAD8_EXTENT 32
@@ -37,6 +39,7 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
 
 /**
  * struct quad8_iio - IIO device private data structure
+ * @counter:		instance of the iio_counter
  * @preset:		array of preset values
  * @count_mode:		array of count mode configurations
  * @quadrature_mode:	array of quadrature mode configurations
@@ -48,6 +51,7 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
  * @base:		base port address of the IIO device
  */
 struct quad8_iio {
+	struct iio_counter counter;
 	unsigned int preset[QUAD8_NUM_COUNTERS];
 	unsigned int count_mode[QUAD8_NUM_COUNTERS];
 	unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
@@ -527,33 +531,289 @@ static const struct iio_chan_spec quad8_channels[] = {
 	QUAD8_COUNT_CHAN(7), QUAD8_INDEX_CHAN(7)
 };
 
+static int quad8_signal_read(struct iio_counter *counter,
+	struct iio_counter_signal *signal, int *val, int *val2)
+{
+	struct quad8_iio *const priv = counter->driver_data;
+
+	if (signal->id < 16)
+		return -EINVAL;
+
+	*val = !!(inb(priv->base + 0x16) & BIT(signal->id - 16));
+
+	return IIO_VAL_INT;
+}
+
+static int quad8_trigger_mode_get(struct iio_counter *counter,
+	struct iio_counter_value *value, struct iio_counter_trigger *trigger)
+{
+	struct quad8_iio *const priv = counter->driver_data;
+	const unsigned int mode = priv->quadrature_mode[value->id];
+	const unsigned int scale = priv->quadrature_scale[value->id];
+	unsigned int direction;
+	const unsigned int flag_addr = priv->base + 2 * value->id + 1;
+	const int signal_id = trigger->signal->id % 2;
+
+	if (mode)
+		switch (scale) {
+		case 0:
+			/* U/D flag: 1 = up, 0 = down */
+			/* direction: 0 = up, 1 = down */
+			direction = !(inb(flag_addr) & BIT(5));
+			if (!signal_id)
+				return direction + 1;
+			break;
+		case 1:
+			if (!signal_id)
+				return 3;
+			break;
+		case 2:
+			return 3;
+		}
+	else
+		if (!signal_id)
+			return 1;
+
+	return 0;
+}
+
+static int quad8_value_read(struct iio_counter *counter,
+	struct iio_counter_value *value, int *val, int *val2)
+{
+	struct quad8_iio *const priv = counter->driver_data;
+	const int base_offset = priv->base + 2 * value->id;
+	unsigned int flags;
+	unsigned int borrow;
+	unsigned int carry;
+	int i;
+
+	flags = inb(base_offset + 1);
+	borrow = flags & BIT(0);
+	carry = !!(flags & BIT(1));
+
+	/* Borrow XOR Carry effectively doubles count range */
+	*val = (borrow ^ carry) << 24;
+
+	/* Reset Byte Pointer; transfer Counter to Output Latch */
+	outb(0x11, base_offset + 1);
+
+	for (i = 0; i < 3; i++)
+		*val |= (unsigned int)inb(base_offset) << (8 * i);
+
+	return IIO_VAL_INT;
+}
+
+static int quad8_value_write(struct iio_counter *counter,
+	struct iio_counter_value *value, int val, int val2)
+{
+	struct quad8_iio *const priv = counter->driver_data;
+	const int base_offset = priv->base + 2 * value->id;
+	int i;
+
+	/* Only 24-bit values are supported */
+	if ((unsigned int)val > 0xFFFFFF)
+		return -EINVAL;
+
+	/* Reset Byte Pointer */
+	outb(0x01, base_offset + 1);
+
+	/* Counter can only be set via Preset Register */
+	for (i = 0; i < 3; i++)
+		outb(val >> (8 * i), base_offset);
+
+	/* Transfer Preset Register to Counter */
+	outb(0x08, base_offset + 1);
+
+	/* Reset Byte Pointer */
+	outb(0x01, base_offset + 1);
+
+	/* Set Preset Register back to original value */
+	val = priv->preset[value->id];
+	for (i = 0; i < 3; i++)
+		outb(val >> (8 * i), base_offset);
+
+	/* Reset Borrow, Carry, Compare, and Sign flags */
+	outb(0x02, base_offset + 1);
+	/* Reset Error flag */
+	outb(0x06, base_offset + 1);
+
+	return 0;
+}
+
+static int quad8_value_function_set(struct iio_counter *counter,
+	struct iio_counter_value *value, unsigned int mode)
+{
+	struct quad8_iio *const priv = counter->driver_data;
+	const unsigned int mode_cfg = mode << 3 |
+		priv->count_mode[value->id] << 1;
+	const unsigned int idr_cfg = priv->index_polarity[value->id] << 1;
+	const int base_offset = priv->base + 2 * value->id + 1;
+
+	if (mode)
+		priv->quadrature_scale[value->id] = mode - 1;
+	else {
+		/* Quadrature scaling only available in quadrature mode */
+		priv->quadrature_scale[value->id] = 0;
+
+		/* Synchronous function not supported in non-quadrature mode */
+		if (priv->synchronous_mode[value->id]) {
+			priv->synchronous_mode[value->id] = 0;
+			outb(0x60 | idr_cfg, base_offset);
+		}
+	}
+
+	priv->quadrature_mode[value->id] = !!mode;
+
+	/* Load mode configuration to Counter Mode Register */
+	outb(0x20 | mode_cfg, base_offset);
+
+	return 0;
+}
+
+static int quad8_value_function_get(struct iio_counter *counter,
+	struct iio_counter_value *value)
+{
+	struct quad8_iio *const priv = counter->driver_data;
+	unsigned int quadrature_mode = priv->quadrature_mode[value->id];
+
+	return (quadrature_mode) ? priv->quadrature_scale[value->id] + 1 : 0;
+}
+
+static const struct iio_counter_ops quad8_ops = {
+	.signal_read = quad8_signal_read,
+	.trigger_mode_get = quad8_trigger_mode_get,
+	.value_read = quad8_value_read,
+	.value_write = quad8_value_write,
+	.value_function_set = quad8_value_function_set,
+	.value_function_get = quad8_value_function_get
+};
+
+static const char *const quad8_function_modes[] = {
+	"non-quadrature",
+	"quadrature x1",
+	"quadrature x2",
+	"quadrature x4"
+};
+
+#define QUAD8_SIGNAL(_id, _name) {	\
+	.id = _id,			\
+	.name = _name			\
+}
+
+static const struct iio_counter_signal quad8_signals[] = {
+	QUAD8_SIGNAL(0, "Channel 1 Quadrature A"),
+	QUAD8_SIGNAL(1, "Channel 1 Quadrature B"),
+	QUAD8_SIGNAL(2, "Channel 2 Quadrature A"),
+	QUAD8_SIGNAL(3, "Channel 2 Quadrature B"),
+	QUAD8_SIGNAL(4, "Channel 3 Quadrature A"),
+	QUAD8_SIGNAL(5, "Channel 3 Quadrature B"),
+	QUAD8_SIGNAL(6, "Channel 4 Quadrature A"),
+	QUAD8_SIGNAL(7, "Channel 4 Quadrature B"),
+	QUAD8_SIGNAL(8, "Channel 5 Quadrature A"),
+	QUAD8_SIGNAL(9, "Channel 5 Quadrature B"),
+	QUAD8_SIGNAL(10, "Channel 6 Quadrature A"),
+	QUAD8_SIGNAL(11, "Channel 6 Quadrature B"),
+	QUAD8_SIGNAL(12, "Channel 7 Quadrature A"),
+	QUAD8_SIGNAL(13, "Channel 7 Quadrature B"),
+	QUAD8_SIGNAL(14, "Channel 8 Quadrature A"),
+	QUAD8_SIGNAL(15, "Channel 8 Quadrature B"),
+	QUAD8_SIGNAL(16, "Channel 1 Index"),
+	QUAD8_SIGNAL(17, "Channel 2 Index"),
+	QUAD8_SIGNAL(18, "Channel 3 Index"),
+	QUAD8_SIGNAL(19, "Channel 4 Index"),
+	QUAD8_SIGNAL(20, "Channel 5 Index"),
+	QUAD8_SIGNAL(21, "Channel 6 Index"),
+	QUAD8_SIGNAL(22, "Channel 7 Index"),
+	QUAD8_SIGNAL(23, "Channel 8 Index")
+};
+
+#define QUAD8_VALUE(_id, _name) {					\
+	.id = _id,							\
+	.name = _name,							\
+	.mode = 0,							\
+	.function_modes = quad8_function_modes,				\
+	.num_function_modes = ARRAY_SIZE(quad8_function_modes)		\
+}
+
+static const struct iio_counter_value quad8_values[] = {
+	QUAD8_VALUE(0, "Channel 1 Count"), QUAD8_VALUE(1, "Channel 2 Count"),
+	QUAD8_VALUE(2, "Channel 3 Count"), QUAD8_VALUE(3, "Channel 4 Count"),
+	QUAD8_VALUE(4, "Channel 5 Count"), QUAD8_VALUE(5, "Channel 6 Count"),
+	QUAD8_VALUE(6, "Channel 7 Count"), QUAD8_VALUE(7, "Channel 8 Count")
+};
+
+static const char *const quad8_trigger_modes[] = {
+	"none",
+	"rising edge",
+	"falling edge",
+	"both edges"
+};
+
 static int quad8_probe(struct device *dev, unsigned int id)
 {
-	struct iio_dev *indio_dev;
-	struct quad8_iio *priv;
+	struct iio_counter_signal *signals;
+	const size_t num_signals = ARRAY_SIZE(quad8_signals);
+	struct iio_counter_value *values;
+	const size_t num_values = ARRAY_SIZE(quad8_values);
+	struct iio_counter_trigger *triggers;
+	struct quad8_iio *quad8iio;
 	int i, j;
 	unsigned int base_offset;
 
-	indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
-	if (!indio_dev)
-		return -ENOMEM;
-
-	if (!devm_request_region(dev, base[id], QUAD8_EXTENT,
-		dev_name(dev))) {
+	if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) {
 		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
 			base[id], base[id] + QUAD8_EXTENT);
 		return -EBUSY;
 	}
 
-	indio_dev->info = &quad8_info;
-	indio_dev->modes = INDIO_DIRECT_MODE;
-	indio_dev->num_channels = ARRAY_SIZE(quad8_channels);
-	indio_dev->channels = quad8_channels;
-	indio_dev->name = dev_name(dev);
-	indio_dev->dev.parent = dev;
+	signals = devm_kmalloc(dev, sizeof(quad8_signals), GFP_KERNEL);

drivers/iio/counter/104-quad-8.c static const struct iio_chan_spec quad8_channels[] = { · line 779

Quoted 10 lines
drivers/iio/counter/104-quad-8.c +10 -0
+	if (!signals)
+		return -ENOMEM;
+
+	memcpy(signals, quad8_signals, sizeof(quad8_signals));
+
+	values = devm_kmalloc(dev, sizeof(quad8_values), GFP_KERNEL);
+	if (!values)
+		return -ENOMEM;
+
+	memcpy(values, quad8_values, sizeof(quad8_values));

devm_kmemdup

drivers/iio/counter/104-quad-8.c static const struct iio_chan_spec quad8_channels[] = { · line 788

Quoted 9 lines
drivers/iio/counter/104-quad-8.c +9 -0
+
+	/* Associate values with their respective signals */
+	for (i = 0; i < num_values; i++) {
+		triggers = devm_kmalloc(dev, 2 * sizeof(*triggers), GFP_KERNEL);
+		if (!triggers)
+			return -ENOMEM;
+
+		/* Starts up in non-quadrature mode */
+		triggers[0].mode = 1;

As I suggested for the dummy driver - use an enum, then the enum naming will make it obvious what the value means here without a comment.

Quoted 42 lines
drivers/iio/counter/104-quad-8.c +28 -3
+		triggers[0].trigger_modes = quad8_trigger_modes;
+		triggers[0].num_trigger_modes = ARRAY_SIZE(quad8_trigger_modes);
+		triggers[0].signal = &signals[2 * i];
+		triggers[1].mode = 0;
+		triggers[1].trigger_modes = quad8_trigger_modes;
+		triggers[1].num_trigger_modes = ARRAY_SIZE(quad8_trigger_modes);
+		triggers[1].signal = &signals[2 * i + 1];
+
+		values[i].triggers = triggers;
+		values[i].num_triggers = 2;
+	}
+
+	quad8iio = devm_kzalloc(dev, sizeof(*quad8iio), GFP_KERNEL);
+	if (!quad8iio)
+		return -ENOMEM;
 
-	priv = iio_priv(indio_dev);
-	priv->base = base[id];
+	quad8iio->counter.name = dev_name(dev);
+	quad8iio->counter.dev = dev;
+	quad8iio->counter.ops = &quad8_ops;
+	quad8iio->counter.signals = signals;
+	quad8iio->counter.num_signals = num_signals;
+	quad8iio->counter.values = values;
+	quad8iio->counter.num_values = num_values;
+	quad8iio->counter.channels = quad8_channels;
+	quad8iio->counter.num_channels = ARRAY_SIZE(quad8_channels);
+	quad8iio->counter.info = &quad8_info;
+	quad8iio->counter.driver_data = quad8iio;
+	quad8iio->base = base[id];
 
 	/* Reset all counters and disable interrupt function */
 	outb(0x01, base[id] + 0x11);
@@ -579,7 +839,7 @@ static int quad8_probe(struct device *dev, unsigned int id)
 	/* Enable all counters */
 	outb(0x00, base[id] + 0x11);
 
-	return devm_iio_device_register(dev, indio_dev);
+	return devm_iio_counter_register(dev, &quad8iio->counter);
 }
 
 static struct isa_driver quad8_driver = {
maintainer
#15 lore raw
In reply to William Breathitt Gray
Quoted 58 lines · On Thu, 5 Oct 2017 14:13:14 -0400 William Breathitt Gray…

There have been some significant implementation changes for this version of the patchset. Here's a brief summary of the updates:

- Inline comments throughout the industrial-counter.c file; this should help clarify some of the code and aid in the review of the system architecture

- Simplification of the driver API; primarily, dynamic updates to the Counter Values, Triggers, and Signals lists are no longer allowed -- all relationships must be defined before the Counter is registered to the system

- Dynamic component registration functions (iio_counter_value_register, et al.) have been removed, and now the signals, values, and triggers array members serve to register the components; signals, values, and triggers were previously named init_signals, init_values, and init_triggers respectively -- the "init_" prefix was removed now that dynamic registration is no longer allowed

- Requiring static component relationships means linked lists are no longer necessary so all the list related code has been removed; Signals, Triggers, and Values are now accessed directly from the arrays supplied in the iio_counter structure

- No dynamic additions/substraction of components eliminates the need for mutex locks, so those have been removed as well

- Previously, the entire iio_counter structure was copied and stored as the iio_dev private data; now, only a pointer to the supplied iio_counter structure is stored

- devm_iio_counter_register and devm_iio_counter_unregister functions were incorporated from Benjamin Gaignard's suggestions

- Dummy counter driver provided as a reference for a simple counter implementation utilizing the Generic Counter interface

In a previous discussion, Benjamin noted some conflicts between the Generic Counter and the underlying IIO core code: mapping does not always work out seemlessly and sometimes the IIO core functionality exposure provided via iio_counter is not quite flexible enough. I intend to address these shortcomings in the next version of this patchset hopefully.

In particular, I'm leaning towards separating the Generic Counter interface from the IIO core dependency entirely. Since the Generic Counter inteface focuses on a more abstract representation of a counter device, I don't think it provides a suitable interface to map onto IIO core, which appears to focus more on a representation of the physical hardware itself.

Separating Generic Counter from IIO core should allow a driver to use the IIO core functions (iio_device_register, et al.) to represent the physical nature of their hardware (voltages, currents, etc.), while also utilize the Generic Counter interface to represent the abstract relationships between those Signals and their ultimate counter Values.

Quoted 7 lines

In particular, I'm hoping for the Generic Counter system implementation itself to not require all this hoop-jumping (mapping to IIO core functions, jumping back to the parent iio_counter, handling non-matching parameter lists between iio_counter and iio_dev, etc.); that should make the code simpler to debug, more efficient, and more stable in the long-run. I will consider these advantages and disadvantages before committing to a separation however.

Key things here:
1) userspace ABI - this bit we need to get right
2) driver ABI - right now we'll irritate a few rather nice and hopefully calm people - so it doesn't matter much :)
3) Maintainability. The key thing is to end up with something that is easy to look after. Particularly when the inevitable happens and you can no longer remember how some corner of your own code works.

Quoted 7 lines

Regarding this version of the patchset in particular, I decided to remove the dynamic component registration functionality since I doubt that many devices would require such; almost all, if not all, hardware counters I encountered have static relationships between input lines and the count value (i.e. specific input lines correlate to specific count registers).

Where this is not true of the bit we are writing a driver for the wider system should impose this relationship in most cases. The only exception would be where the counter resources are more constrained than the hardware it is interfaced to. So say we have 2 hardware counters on a system that has 4 encoders but there is some underlying reason why we don't need to use all the encoders at the same time... We can probably figure out how to represent that though even with the initialization time mappings etc.

Something to fix when we care - not from the outset!

Quoted 11 lines

The requirement that Counter component relationships are static has opened the possibility of some optimizations in the Generic Counter interface code:

- Signals, Triggers, and Values are provided as arrays; these arrays could benefit from some amount of sorting based on component IDs

- Searching for a component can be more efficient in a sorted array; currently, the code just walks down the array elements and compare the IDs

I raise the point that mostly (I think) the drivers know exactly where to look in these arrays from the index. My inclination is to give them the ability to provide that knowledge to the core. I wouldn't worry too much for now. There are lots of ways of dealing with the small performance enhancements once the rest is sorted - they don't effect the ABI so we can try what we like ;)

Quoted 6 lines

- Similar to general searching, the arrays are initially verified to gurantee unique IDs; currently, the code walks down the array elements and compares the current element ID with all the elements before and after -- this could be made more efficient with some sorting

It's not a fast path - don't bother. You'll add code complexity to little practical gain - 0.1 seconds on boot up or something less than that...

Quoted 3 lines

There are likely other areas of improvement, so let me know and I'll see what I can do.

It comes up in the individual patch reviews but my main takeaways would be:

1) Naming - we need to deal with trigger (and I think also value -> counter) to limit confusion.
2) Fuller definitions in documentation. Good start on the docs, but you need to be specifying allowable values for parameters as that's what a userspace interface will need to know.

Anyhow, sorry it took me so long to get to it and looking forward to the next revision!

Jonathan

Quoted 30 lines

William Breathitt Gray (6):
iio: Implement counter channel specification and IIO_SIGNAL constant
iio: Introduce the generic counter interface
iio: Documentation: Add IIO Generic Counter sysfs documentation
docs: Add IIO Generic Counter Interface documentation
iio: Add dummy counter driver
iio: 104-quad-8: Add IIO generic counter interface support

 .../testing/sysfs-bus-iio-generic-counter-sysfs    |  63 ++
 Documentation/driver-api/iio/generic-counter.txt   | 526 ++++++++++++
 MAINTAINERS                                        |   7 +
 drivers/iio/Kconfig                                |   8 +
 drivers/iio/Makefile                               |   1 +
 drivers/iio/counter/104-quad-8.c                   | 294 ++++++-
 drivers/iio/counter/Kconfig                        |  16 +
 drivers/iio/counter/Makefile                       |   1 +
 drivers/iio/counter/dummy-counter.c                | 293 +++++++
 drivers/iio/industrialio-core.c                    |  14 +-
 drivers/iio/industrialio-counter.c                 | 900 +++++++++++++++++++++
 include/linux/iio/counter.h                        | 166 ++++
 include/linux/iio/iio.h                            |   2 +
 include/uapi/linux/iio/types.h                     |   1 +
 14 files changed, 2274 insertions(+), 18 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs
 create mode 100644 Documentation/driver-api/iio/generic-counter.txt
 create mode 100644 drivers/iio/counter/dummy-counter.c
 create mode 100644 drivers/iio/industrialio-counter.c
 create mode 100644 include/linux/iio/counter.h
maintainer
#9 lore raw

Re: [PATCH v3 4/6] docs: Add IIO Generic Counter Interface documentation

In reply to William Breathitt Gray
Quoted 4 lines · On Thu, 5 Oct 2017 14:14:24 -0400 William Breathitt Gray…

This patch adds high-level documentation about the IIO Generic Counter Interface.

Put it into the relevant index.rst to get and make it an rst file so it gets built as part of the sphinx docs.

Quoted 24 lines
---
 Documentation/driver-api/iio/generic-counter.txt | 526 +++++++++++++++++++++++
 1 file changed, 526 insertions(+)
 create mode 100644 Documentation/driver-api/iio/generic-counter.txt
Documentation/driver-api/iio/generic-counter.txt new file +13 -0
new file mode 100644
index 000000000000..0ce43b71e1c7
@@ -0,0 +1,526 @@
+=========================
+Generic Counter Interface
+=========================
+
+Introduction
+============
+
+Counter devices are prevalent within a diverse spectrum of industries.
+The ubiquitous presence of these devices necessitates a common interface
+and standard of interaction and exposure. This driver API attempts to
+resolve the issue of duplicate code found among existing counter device
+drivers by introducing a generic counter interface for consumption. The
+generic counter interface enables drivers to support and expose a common

Documentation/driver-api/iio/generic-counter.txt line 23

Quoted 10 lines
Documentation/driver-api/iio/generic-counter.txt new file +10 -0
+set of components and functionality present in counter devices.
+
+Theory
+======
+
+Counter devices can vary greatly in design, but regardless of whether
+some devices are quadrature encoder counters or pedometers, all counter
+devices consist of a core set of components. This core set of
+components, shared by all counter devices, is what forms the essence of
+the generic counter interface.

Hmm. Whether it makes sense to move over pedometers is a bit of an open question... They tend to be fairly opaque wrt to where their data is coming from. We'll see, but I'd kind of prefer we keep it out of the docs until that case has been fully analysed!

Documentation/driver-api/iio/generic-counter.txt line 32

Quoted 9 lines
Documentation/driver-api/iio/generic-counter.txt new file +9 -0
+
+There are three core components to a counter:
+
+        VALUE
+        -----
+        A Value represents the count data for a set of Signals. A Value
+        has a count function mode (e.g. "increment" or "quadrature x4")
+        which respresents the update behavior for the count data. A
+        Value also has a set of one or more associated Signals.

The whole set of valid function modes needs to be documented somewhere. I don't think it currently is (but I may have missed it and haven't read all the patches yet!)

Interesting terminology - this Value is what most people would think of as a counter. Can we just call it that? The issue is that we are thinking from the hardware point of view that says a 'counter block' can have multiple counters so value is just one of them. I'm not sure we want to present that terminology to userspace.

Documentation/driver-api/iio/generic-counter.txt line 45

Quoted 13 lines
Documentation/driver-api/iio/generic-counter.txt new file +13 -0
+
+        SIGNAL
+        ------
+        A Signal represents a count input line. A Signal may be
+	associated to one or more Values.
+
+        TRIGGER
+	-------
+        A Trigger represents a Value's count function trigger condition
+        mode (e.g. "rising edge" or "double pulse") for an associated
+        Signal.	If a Signal is associated with a Value, a respective
+        Trigger	instance for that association exists -- albeit perhaps
+        with a trigger condition mode of "none."

Again, we need to enumerate the full acceptable list. Enforcing it can be by review, but we need a list that people can refer to or we will get multiple drivers doing subtly different variants.

Documentation/driver-api/iio/generic-counter.txt line 53

Quoted 8 lines
Documentation/driver-api/iio/generic-counter.txt new file +8 -0
+
+A counter is defined as a set of input signals associated to count data
+that are generated by the evaluation of the state of the associated
+input signals as defined by the respective count functions. Within the
+context of the generic counter interface, a counter consists of Values
+each associated to a set of Signals, whose respective Trigger instances
+represent the count function update conditions for the associated
+Values.

Could we call this a 'counter group' or 'counter device' or something to free the term counter up for a single instance of what is currently called value.

Documentation/driver-api/iio/generic-counter.txt line 61

Quoted 8 lines
Documentation/driver-api/iio/generic-counter.txt new file +8 -0
+
+Paradigm
+========
+
+The most basic counter device may be expressed as a single Value
+associated with a single Signal via a single Trigger. Take for example
+a hypothetical counter device which simply accumulates a count of rising
+edges on a source input line.

Drop hypothetical - this is a perfectly real example. Plenty of hardware out there does this!

Quoted 10 lines
Documentation/driver-api/iio/generic-counter.txt new file +10 -0
+
+        Value                Trigger        Signal
+        -----                -------        ------
++---------------------+
+| Data: Count         |    Rising Edge     ________
+| Function: Increment |  <-------------   / Source \
+|                     |                  ____________
++---------------------+
+
+In this example, the Signal is a source input line with a pulsing

Documentation/driver-api/iio/generic-counter.txt

Quoted 10 lines
Documentation/driver-api/iio/generic-counter.txt new file +10 -0
+voltage, while the Value is a persistent count which increments. The
+Signal is associated with the Value via a respective Trigger. The
+increment function is triggered by the condition specified by the
+Triggered -- in this case a rising edge condition. In summary, the
+counter device existence and behavior is aptly represented by respective
+Value, Signal, and Trigger components: a rising edge condition triggers
+an incrementation function on an accumulating count datum.
+
+A counter device is not limited to a single Signal; in fact, in theory
+an unlimited number of Signals may be associated with a Value.

Change that that to many signals. Otherwise someone pedantic will say it's limited by the implementation to 2^32 or similar..

Quoted 13 lines

For

Documentation/driver-api/iio/generic-counter.txt new file +12 -0
+example, a quadrature encoder counter device can keep track of position
+based on the states of two input lines.
+
+           Value                 Trigger      Signal
+           -----                 -------      ------
++-------------------------+
+| Data: Position          |    Both Edges      ___
+| Function: Quadrature x4 |  <-------------   / A \
+|                         |                  _______
+|                         |
+|                         |    Both Edges      ___
+|                         |  <-------------   / B \

Documentation/driver-api/iio/generic-counter.txt line 103

Quoted 10 lines
Documentation/driver-api/iio/generic-counter.txt new file +10 -0
+|                         |                  _______
++-------------------------+
+
+In this example, two Signals (quadrature encoder lines A and B) are
+associated to a single Value: a rising or falling edge on either A or B
+triggers the "Quadrature x4" function which determines the direction of
+movement and updates the respective position data. The "Quadrature x4"
+function is likely implemented in the hardware of the quadrature encoder
+counter device; the Value, Triggers, and Signals simply represent this
+hardware behavior and functionality.

Interesting that there is a relationship between valid trigger settings and valid Value:Function settings.

Quoted 7 lines
Documentation/driver-api/iio/generic-counter.txt new file +7 -0
+
+Signal associated to the same Value can have differing trigger
+conditions. For example, a quadrature encoder counter device operating
+in a non-quadrature mode could have one input line dedicated for
+movement and a second input line dedicated for direction.
+
+           Value                  Trigger      Signal

Documentation/driver-api/iio/generic-counter.txt line 120

Quoted 10 lines
Documentation/driver-api/iio/generic-counter.txt new file +10 -0
+           -----                  -------      ------
++------------------------- +
+| Data: Position           |    Rising Edge     ___
+| Function: Non-quadrature |  <-------------   / A \ (Movement)
+|                          |                  _______
+|                          |
+|                          |       None         ___
+|                          |  <-------------   / B \ (Direction)
+|                          |                  _______
++--------------------------+

Non-quadrature is a pretty odd way of defining it! I'd suggest something more positive - pulse-direction would be the standard terminology.

Documentation/driver-api/iio/generic-counter.txt line 128

Quoted 8 lines
Documentation/driver-api/iio/generic-counter.txt new file +8 -0
+
+Only Signal A triggers the "Non-quadrature" update function, but the
+state of Signal B is still required in order to know the direction in
+order to properly update the position data. So in the end, both Signals
+are associated to the same Value via two respective Triggers, but only
+one Trigger has an active trigger condition while the other is left in a
+"None" condition mode to indicate its respective Signal's availability
+for state evaluation despite its non-triggering mode.

Interesting - there sill be some confusion around the quadrature cases though as the 'value' will only change on say rising edge A, but the internal state machine is tracking the previous changes of edge B. I'd suggest we add a note about that somewhere here.

Quoted 50 lines
Documentation/driver-api/iio/generic-counter.txt new file +50 -0
+
+Although the examples thus far have been representations of physical
+devices, this is not a necessity. A counter simply represent the
+evaluation (Value) of input data (Signals) triggered by specific
+conditions (Triggers). A counter can be the representation of more
+abstract components.
+
+For example, suppose a counter representation is desired for a DNA
+sequence analysizer which detects possible genetic diseases.
+
+        Value                     Trigger           Signal
+        -----                     -------           ------
++---------------------+
+| Data: Diseases      |    Gene Transcript (EST)     _____
+| Function: Cancers   |  <-----------------------   / DNA \ (GAAGTGC...)
+|                     |                            _________
++---------------------+
+
+In this scenario, the Signal is a stream of DNA nucleotide bases (As,
+Ts, Cs, and Gs), the Trigger is expressed sequence tags (ESTs), and the
+Value is a list of diseases discovered (in this case the function is
+evaluating for possible cancers). Note how the Signal in this example
+does not represent a physical voltage line, nor does the Trigger
+represent a physical voltage line state change, nor does the Value
+represent a strictly decimal data value.
+
+The DNA sequence analysizer example is contrived to illustrate the
+flexibility of the generic counter paradigm by demonstrating its
+capability of representing abstract concepts; however, physical devices
+are likely to be more fitting for such a representation.
+
+The key concept is that the Signal, Trigger, and Value are abstract
+representations which do not need to be closely married to their
+respective physical sources. This allows the user of a counter to
+divorce themselves from the nuances of physical components (such as
+whether an input line is differential or single-ended) and focus on the
+core idea of what the data and process represent (an accumulated count
+of rising edges).
+
+Userspace Interface
+===================
+
+Several sysfs attributes are generated by the generic counter interface,
+and reside under the /sys/bus/iio/devices/iio:deviceX directory.
+
+Each counter has a respective set of countX-Y and signalX-Y prefixed
+attributes, where X is the id set in the counter structure, and Y is the
+id of the respective Value or Signal.
+
+The generic counter interface sysfs attributes are as follows:

Documentation/driver-api/iio/generic-counter.txt line 188

Quoted 10 lines
Documentation/driver-api/iio/generic-counter.txt new file +10 -0
+
+        countX-Y_function: count function mode
+        countX-Y_function_available: available count function modes
+        countX-Y_name: Value name
+        countX-Y_raw: Value data
+        countX-Y_triggers: Value's associated Signals
+        countX-Y_trigger_signalX-Z: Value Y trigger mode for Signal Z
+        countX-Y_trigger_signalX-Z_available: available Value Y trigger
+                                              modes for Signal Z
+        signalX-Y_name: Signal name

I mentioned this in one of the other patches - This naming stuff feels like, if we want to do it we should do it throughout IIO.

Possibly it maps to the data_sheet_name.. Higher level information beyond this device would to my mind require explicit representation of devices beyond this - i.e. what is attached to the wires that is generating things to count.

Quoted 28 lines
Documentation/driver-api/iio/generic-counter.txt new file +28 -0
+        signalX-Y_raw: Signal data
+
+Through these sysfs attributes, programs and scripts may interact with
+the generic counter paradigm Values, Triggers, and Signals of respective
+counter devices.
+
+Driver API
+==========
+
+Driver authors may utilize the generic counter interface in their code
+by including the include/linux/iio/counter.h header file. This header
+file provides several core data structures and function prototypes for
+defining a generic counter.
+
+struct iio_counter_signal
+-------------------------
+
+This structure defines a generic counter paradigm Signal component;
+typically this will correlate with an input channel on a physical
+counter device. This structure is the simplest to define with only two
+structure members which require explicit configuration:
+
+        id:     Unique ID used to identify the Signal
+
+        name:   Device-specific Signal name (typically the device input
+                channel name)
+
+struct iio_counter_trigger

Documentation/driver-api/iio/generic-counter.txt line 226

Quoted 10 lines
Documentation/driver-api/iio/generic-counter.txt new file +10 -0
+--------------------------
+
+This structure defines a generic counter paradigm Trigger component. To
+properly utilize this structure, trigger modes and an associated Signal
+must be defined:
+
+        mode:                   Index of the current trigger mode state
+
+        trigger_modes:          Array of trigger modes each represented
+	                        by a character string

As mentioned before these have to be explicitly listed somewhere. They form userspace ABI. We must not leave this empty. Defining these cleanly and generically may be one of the hardest aspects to agree on.

Quoted 23 lines
Documentation/driver-api/iio/generic-counter.txt new file +23 -0
+
+        num_trigger_modes:      Number of trigger modes provided in
+	                        trigger_modes array
+
+        signal:                 Pointer to associated Signal
+
+struct iio_counter_value
+------------------------
+
+This structure defines a generic counter paradigm Value component;
+typically this will correlate with the read data (the "count" value)
+provided by a physical counter device. This structure requires the
+explicit configuration of an ID, name, function modes (the function
+triggered on a Trigger condition), and optionally a set of initial
+associated Triggers:
+
+        id:                     Unique ID used to identify the Signal
+
+        name:                   Device-specific Value name (typically
+	                        the device read channel name)
+
+        mode:                   Index of the current function mode state
+

Documentation/driver-api/iio/generic-counter.txt line 259

Quoted 10 lines
Documentation/driver-api/iio/generic-counter.txt new file +10 -0
+        function_modes:         Array of function modes each represented
+	                        by a character string
+
+        num_function_modes:     Number of function modes provided in
+	                        function_modes array
+
+        triggers:               Array of associated Triggers
+
+        num_triggers:           Number of Triggers provided in triggers
+	                        array

It might also be convenient to have a flag in here to say if the triggers are fixed or not.

Documentation/driver-api/iio/generic-counter.txt line 262

Quoted 3 lines
Documentation/driver-api/iio/generic-counter.txt new file +3 -0
+
+struct iio_counter_ops
+----------------------

I'd suggest you push these details down in to kernel-doc in the code and pull it back in here using the relevant bits of markup. That stuff is a bit clunky but any docs in here of actual elements etc will probably rot unedited if stuff changes.

Documentation/driver-api/iio/generic-counter.txt line 271

Quoted 9 lines
Documentation/driver-api/iio/generic-counter.txt new file +9 -0
+
+This structure defines callbacks to interact with the Value, Trigger,
+and Signal components:
+
+        signal_read:            Function to request a signal value from
+	                        the device. Return value will specify
+				the type of value returned by the
+				device. val and val2 will contain the
+				elements making up the returned value.

Mention IIO_VAL_INT type return values as well.

Quoted 9 lines
Documentation/driver-api/iio/generic-counter.txt new file +9 -0
+
+	signal_write:           Function to write a signal value to the
+	                        device. Parameters are interpreted the
+				same as signal_read.
+
+        trigger_mode_set:       Function to set the trigger mode. mode
+	                        is the index of the requested mode from
+				the value trigger_modes array.
+

Documentation/driver-api/iio/generic-counter.txt line 290

Quoted 10 lines
Documentation/driver-api/iio/generic-counter.txt new file +10 -0
+        trigger_mode_get:       Function to get the current trigger
+	                        mode. Return value will specify the
+				index of the current mode from the value
+				trigger_modes array.
+
+        value_read:             Function to request a value value from
+	                        the device. Return value will specify
+				the type of value returned by the
+				device. val and val2 will contain the
+				elements making up the returned value.

This is the counter I think? Value is rather vague, counter_read will be less so.

Quoted 70 lines
Documentation/driver-api/iio/generic-counter.txt new file +70 -0
+
+        value_write:            Function to write a value value to the
+	                        device. Parameters are interpreted the
+				same as value_read.
+
+        value_function_set:     Function to set the value function mode.
+	                        mode is the index of the requested mode
+				from the value function_modes array.
+
+        value_function_get:     Function to get the current value
+	                        function mode. Return value will specify
+				the index of the current mode from the
+				value function_modes array.
+
+struct iio_counter
+------------------
+
+This is the main data structure for a counter device; access to all
+respective Values, Triggers, and Signals is possible from this
+structure. This structure allows the configuration of an ID, name,
+function callbacks, initial Signals and initial Values, auxiliary IIO
+core channels and callbacks, and driver-specific data:
+
+        id:                     Unique ID used to identify the counter
+
+        name:                   Name of the counter device
+
+        dev:                    Device structure, which should be
+				assigned a parent and owner
+
+        ops:                    Function callbacks for counter
+	                        components (Signal, Trigger, Value)
+
+        signals:                Array of Signals
+
+        num_signals:            Number of Signals specified in signals
+	                        array
+
+        values:                 Array of Values
+
+        num_values:             Number of Values specified in values
+	                        array
+
+        channels:               Optional IIO core channels specification
+	                        structure table
+
+        num_channels:           Number of channels specified in channels
+
+        info:                   IIO core function callbacks and constant
+	                        info from driver
+
+        driver_data:            Driver-specific data
+
+Registration functions
+----------------------
+
+Counters may be registered to the system via the iio_counter_register
+function and subsequently unregistered via the iio_counter_unregister
+function. The devm_iio_counter_register and devm_iio_counter_unregister
+functions serve as device memory-managed versions of the
+iio_counter_register and iio_counter_unregister functions.
+
+An initialized iio_counter structure, which defines the Counter, is
+required to be passed in for registration; the Signals, Values, and
+Triggers for the Counter passed in via the signals and values arrays as
+part of the iio_counter structure. If auxiliary IIO core channels and
+functionality are required, IIO core channels and callbacks may be
+passed in via the channels and info members of the passed-in iio_counter
+structure.
+

Documentation/driver-api/iio/generic-counter.txt line 370

Quoted 10 lines
Documentation/driver-api/iio/generic-counter.txt new file +10 -0
+Implementation
+==============
+
+The IIO generic counter interface piggybacks off of the IIO core. This
+is primarily used to leverage the existing sysfs setup: the IIO_COUNT
+channel attributes represent the Counter Value, while the IIO_SIGNAL
+channel attributes represent the Counter Signal; auxilary IIO_COUNT
+attributes represent the Counter Signal connections (Triggers) and their
+respective state change configurations which trigger an associated
+"counter function" evaluation.

If it makes more sense I wouldn't mind changing the form of that setup code to be more generic so you can use it without jumping through so many hoops.

Quoted 33 lines
Documentation/driver-api/iio/generic-counter.txt new file +33 -0
+
+The iio_counter_ops structure serves as a container for driver callbacks
+to communicate with the device; function callbacks are provided to read
+and write various Signals and Values, and set and get the "trigger mode"
+and "function mode" for various Triggers and Values respectively.
+
+To support a counter device, a driver must first allocate the available
+Counter Signals via iio_counter_signal structures. These Signals should
+be stored as an array and set to the signals array member of an
+allocated iio_counter structure before the Counter is registered to the
+system.
+
+Counter Values may be allocated via iio_counter_value structures, and
+respective Counter Signal associations (Triggers) made via
+iio_counter_trigger structures. Associated iio_counter_trigger
+structures are stored as an array and set to the the triggers array
+member of the respective iio_counter_value structure. These
+iio_counter_value structures are set to the values array member of an
+allocated iio_counter structure before the Counter is registered to the
+system.
+
+A counter device is registered to the system by passing the respective
+initialized iio_counter structure to the iio_counter_register function;
+similarly, the iio_counter_unregister function unregisters the
+respective counter.
+
+Architecture
+============
+
+Although the IIO Generic Counter Interface utilizes IIO core under the
+hood, driver authors are not necessarily required to interact with IIO
+core data structures and functions directly -- in theory, such details
+of the system are abstracted away. Driver authors only need to concern

Documentation/driver-api/iio/generic-counter.txt line 413

Quoted 10 lines
Documentation/driver-api/iio/generic-counter.txt new file +10 -0
+themselves with the Generic Counter specific data structures and
+functions found in the include/linux/iio/counter.h header file.
+
+In other words, the driver API is intended to expose itself sufficiently
+upon the principles and concepts of the generic counter paradigm (i.e.
+Values, Triggers, Signals, etc.) such that it may be indepedent from its
+underlying implementation; theoretically, the IIO core code in the
+implementation could be replaced away in its entirely by an alternative
+implementation all without the need to update existing drivers utilizing
+the Generic Counter Interface driver API.

Good. I hope it's not just in theory ;) May well make sense in the future as in particular IIO may get based on some new core infrastructure itself.

Quoted 103 lines
Documentation/driver-api/iio/generic-counter.txt new file +103 -0
+
+This paradigm separation however does result in some mapping concerns
+between Generic Counter functions to IIO core functions; in particular,
+parameters for the IIO core functions expect IIO core data structures
+(e.g. iio_dev and iio_chan_spec) which are not provided directly by the
+parameters for the respective Generic Counter functions. This results in
+a somewhat opaque pathway from a iio_counter structure to its associated
+iio_dev in order to support the required IIO core calls.
+
+The following call graphs should help illustrate some of the main IIO
+core dependencies:
+
++----------------------+
+| iio_counter_register |
++----------------------+
+  |  |  |
+  |  |  +-----------------------------+
+  |  +------------------+             |
+  |                     |             |
+  V                     V             V
++------------------+  +----------+  +---------------------+
+| iio_device_alloc |  | iio_priv |  | iio_device_register |
++------------------+  +----------+  +---------------------+
+
+The iio_counter_register function allocates and initializes a new
+iio_dev structure which will serve as the respective Counter's gateway
+to IIO core support. The address of the parent iio_counter structure is
+stored with the iio_dev structure via iio_priv in order to allow access
+back to the Counter from within the IIO core functions. Finally, the
+iio_dev structure is registered via iio_device_register.
+
++-----------------------+   +----------------------+
+| iio_read_channel_info |-->| iio_counter_read_raw |
++-----------------------+   +----------------------+
+                              |  |  |
+  +---------------------------+  |  |
+  |                +-------------+  |
+  |                |               ++
+  |                |               |
+  V                V               V
+  IIO_SIGNAL       IIO_COUNT       IIO_*
++-------------+  +------------+  +----------+
+| signal_read |  | value_read |  | read_raw |
++-------------+  +------------+  +----------+
+
++------------------------+   +-----------------------+
+| iio_write_channel_info |-->| iio_counter_write_raw |
++------------------------+   +-----------------------+
+                               |  |  |
+  +----------------------------+  |  |
+  |                 +-------------+  |
+  |                 |                |
+  |                 |                |
+  V                 V                V
+  IIO_SIGNAL        IIO_COUNT        IIO_*
++--------------+  +-------------+  +-----------+
+| signal_write |  | value_write |  | write_raw |
++--------------+  +-------------+  +-----------+
+
+Normally, the IIO core iio_read_channel_info and iio_write_channel_info
+functions respectiveluy call the driver-supplied read_raw and write_raw
+callbacks directly. Since the generic counter interface serves as an
+abstraction above IIO core, drive authors do not generally directly
+configure a read_raw/write_raw callback.
+
+Instead, the IIO Generic Counter Interface hooks on to the
+iio_read_channel_info and iio_write_channel_info expected read_raw and
+write_raw callbacks respectively via iio_counter_read_raw and
+iio_counter_write_raw. The iio_counter_read_raw and
+iio_counter_write_raw functions then call the respective driver-supplied
+signal_read/value_read and signal_write/value_write callbacks
+respectively for the appropriate IIO_SIGNAL OR IIO_COUNT. If an IIO core
+channel that was not part of the generic counter paradigm was supplied
+via the channels member of the iio_counter structure, then the
+respective driver-supplied (via the iio_counter structure info member)
+read_raw and write_raw are called.
+
++---------------------------+        +----------------------------+
+| iio_read_channel_ext_info |        | iio_write_channel_ext_info |
++---------------------------+        +----------------------------+
+  |                                    |
+  V                                    V
++-------------------------------+    +--------------------------------+
+| iio_counter_trigger_mode_read |    | iio_counter_trigger_mode_write |
++-------------------------------+    +--------------------------------+
+  |                                    |
+  V                                    V
++------------------+                 +------------------+
+| trigger_mode_get |                 | trigger_mode_set |
++------------------+                 +------------------+
+
++---------------+                     +----------------+
+| iio_enum_read |                     | iio_enum_write |
++---------------+                     +----------------+
+  |                                     |
+  V                                     V
++--------------------------------+    +--------------------------------+
+| iio_counter_value_function_get |    | iio_counter_value_function_set |
++--------------------------------+    +--------------------------------+
+  |                                     |
+  V                                     V
++--------------------+                +--------------------+
+| value_function_get |                | value_function_set |

Documentation/driver-api/iio/generic-counter.txt line 526

Quoted 10 lines
Documentation/driver-api/iio/generic-counter.txt new file +10 -0
++--------------------+                +--------------------+
+
+The driver-supplied trigger_mode_get and trigger_mode_set callbacks hook
+on to the iio_read_channel_ext_info and iio_write_channel_ext_info
+functions respectively via the iio_counter_trigger_mode_read and
+iio_counter_trigger_mode_write functions. Similarly, the driver-supplied
+value_function_get and value_function set callbacks hook on to the
+iio_enum_read and iio_enum_write functions respectively via the
+iio_counter_value_function_get and iio_counter_value_function set
+functions.

I'm inclined to agree with you when you suggest that separating things out more may well make sense.

This all seems way to involved just to share a relatively small amount of code.

The question is whether we loose anything particularly useful if we drop most of the complexity. If it makes sense for a counter to sit on the IIO bus in sysfs that's easily done (triggers in IIO for example do that already).

The main advantage then becomes one of grouping 'somewhat' related subsystems together... Can also share some utility code given the overall structures are somewhat similar of course.

Not sure...

Jonathan

maintainer
#4 lore raw
In reply to William Breathitt Gray
Quoted 6 lines · On Thu, 5 Oct 2017 14:13:44 -0400 William Breathitt Gray…

This patch introduces the IIO generic counter interface for supporting counter devices. The generic counter interface serves as a catch-all to enable rudimentary support for devices that qualify as counters. More specific and apt counter interfaces may be developed on top of the generic counter interface, and those interfaces should be used by drivers when possible rather than the generic counter interface.

I think you probably want to avoid any additional layering and try to push things down here (maybe with a few utility functions to simplify common cases), but that's a question for another day!

Quoted 34 lines

In the context of the IIO generic counter interface, a counter is defined as a device that reports one or more "counter values" based on the state changes of one or more "counter signals" as evaluated by a defined "counter function."

The IIO generic counter interface piggybacks off of the IIO core. This is primarily used to leverage the existing sysfs setup: the IIO_COUNT channel attributes represent the Counter Value, while the IIO_SIGNAL channel attributes represent the Counter Signal; auxilary IIO_COUNT attributes represent the Counter Signal connections (Triggers) and their respective state change configurations which trigger an associated "counter function" evaluation.

The iio_counter_ops structure serves as a container for driver callbacks to communicate with the device; function callbacks are provided to read and write various Signals and Values, and set and get the "trigger mode" and "function mode" for various Triggers and Values respectively.

To support a counter device, a driver must first allocate the available Counter Signals via iio_counter_signal structures. These Signals should be stored as an array and set to the signals array member of an allocated iio_counter structure before the Counter is registered to the system.

Counter Values may be allocated via iio_counter_value structures, and respective Counter Signal associations (Triggers) made via iio_counter_trigger structures. Associated iio_counter_trigger structures are stored as an array and set to the the triggers array member of the respective iio_counter_value structure. These iio_counter_value structures are set to the values array member of an allocated iio_counter structure before the Counter is registered to the system.

Quoted 7 lines

A counter device is registered to the system by passing the respective initialized iio_counter structure to the iio_counter_register function; similarly, the iio_counter_unregister function unregisters the respective counter. The devm_iio_counter_register and iio_devm_iio_counter_unregister functions serve as device memory-managed versions of the iio_counter_register and iio_counter unregister functions respectively.

All the find by ids are the main complexity introduced by layering this on IIO that you probably wouldn't have if it wasn't so layered. Hmm. I suggest inline that you could allow drivers to provide fast versions of that search given they can probably infer it directly from the index. Might be something to ignore for now though in the interests of initial simplicity.

Otherwise a few comments inline, but mostly this is coming together pretty well. I'm actually surprised the layering on top of IIO didn't end up more painful than it did. It's not horrendous - which is not to say you wouldn't be better breaking free of that entirely... (I'm not sure either way).

I think the big remaining questions are around naming more than anything else. I don't like the name value when a user will think they are looking for a counter. If you use counter though the current use of counter needs to change.

Trigger is also, as has been raised, a rather overloaded term. Not totally obvious what a better term is but we shouldn't reuse that one...

I'll try and think of something if you have no luck!

Jonathan

Quoted 18 lines
---
 MAINTAINERS                        |   7 +
 drivers/iio/Kconfig                |   8 +
 drivers/iio/Makefile               |   1 +
 drivers/iio/counter/Kconfig        |   1 +
 drivers/iio/industrialio-counter.c | 900 +++++++++++++++++++++++++++++++++++++
 include/linux/iio/counter.h        | 166 +++++++
 6 files changed, 1083 insertions(+)
 create mode 100644 drivers/iio/industrialio-counter.c
 create mode 100644 include/linux/iio/counter.h
MAINTAINERS +0 -0
index 2281af4b41b6..8b7c37bed252 100644

MAINTAINERS F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector · line 6701

Quoted 9 lines
MAINTAINERS +6 -0
@@ -6693,6 +6693,13 @@ F:	Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
 F:	Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
 F:	drivers/iio/adc/envelope-detector.c
 
+IIO GENERIC COUNTER INTERFACE
+M:	William Breathitt Gray <[email protected]>
+L:	[email protected]
+S:	Maintained
+F:	drivers/iio/industrialio-counter.c
+F:	include/linux/iio/counter.h

Don't forget the directory the drivers are going in ;) *laughs manically*

Quoted 108 lines
MAINTAINERS +1 -0
+
 IIO MULTIPLEXER
 M:	Peter Rosin <[email protected]>
 L:	[email protected]
drivers/iio/Kconfig +8 -0
index b3c8c6ef0dff..78e01f4f5937 100644
@@ -30,6 +30,14 @@ config IIO_CONFIGFS
 	  (e.g. software triggers). For more info see
 	  Documentation/iio/iio_configfs.txt.
 
+config IIO_COUNTER
+	bool "Enable IIO counter support"
+	help
+	  Provides IIO core support for counters. This API provides
+	  a generic interface that serves as the building blocks to
+	  create more complex counter interfaces. Rudimentary support
+	  for counters is enabled.
+
 config IIO_TRIGGER
 	bool "Enable triggered sampling support"
 	help
drivers/iio/Makefile +1 -0
index 93c769cd99bf..6427ff38f964 100644
@@ -5,6 +5,7 @@
 obj-$(CONFIG_IIO) += industrialio.o
 industrialio-y := industrialio-core.o industrialio-event.o inkern.o
 industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
+industrialio-$(CONFIG_IIO_COUNTER) += industrialio-counter.o
 industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
 
 obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
drivers/iio/counter/Kconfig +1 -0
index 474e1ac4e7c0..c8becfe78e28 100644
@@ -4,6 +4,7 @@
 # When adding new entries keep the list in alphabetical order
 
 menu "Counters"
+	depends on IIO_COUNTER
 
 config 104_QUAD_8
 	tristate "ACCES 104-QUAD-8 driver"
drivers/iio/industrialio-counter.c new file +55 -0
new file mode 100644
index 000000000000..dfb982dae3a8
@@ -0,0 +1,900 @@
+/*
+ * Industrial I/O counter interface functions
+ * Copyright (C) 2017 William Breathitt Gray
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/printk.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+
+#include <linux/iio/counter.h>
+
+#define iio_priv_get_counter(_indio_dev) \
+	(*(struct iio_counter **)iio_priv(_indio_dev))
+
+static struct iio_counter_signal *iio_counter_signal_find_by_id(
+	const struct iio_counter *const counter, const int id)
+{
+	size_t i;
+	struct iio_counter_signal *signal;
+
+	for (i = 0; i < counter->num_signals; i++) {
+		signal = counter->signals + i;
+		if (signal->id == id)
+			return signal;
+	}
+
+	return NULL;
+}
+
+static struct iio_counter_trigger *iio_counter_trigger_find_by_id(
+	const struct iio_counter_value *const value, const int id)
+{
+	size_t i;
+	struct iio_counter_trigger *trigger;
+
+	for (i = 0; i < value->num_triggers; i++) {
+		trigger = value->triggers + i;
+		if (trigger->signal->id == id)
+			return trigger;

drivers/iio/industrialio-counter.c line 65

Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10 -0
+	}
+
+	return NULL;
+}
+
+static struct iio_counter_value *iio_counter_value_find_by_id(
+	const struct iio_counter *const counter, const int id)
+{
+	size_t i;
+	struct iio_counter_value *value;

In most cases this mapping will be entirely obvious to the driver - perhaps provide an optional callback to let it provide it directly without any searching?

We could always add this later though if we start getting drivers with lots of instances of the various parts...

Quoted 24 lines
drivers/iio/industrialio-counter.c new file +24 -0
+
+	for (i = 0; i < counter->num_values; i++) {
+		value = counter->values + i;
+		if (value->id == id)
+			return value;
+	}
+
+	return NULL;
+}
+
+static ssize_t iio_counter_signal_name_read(struct iio_dev *indio_dev,
+	uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	const struct iio_counter_signal *signal;
+
+	signal = iio_counter_signal_find_by_id(counter, chan->channel2);
+	if (!signal)
+		return -EINVAL;
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n", signal->name);
+}
+
+static ssize_t iio_counter_value_name_read(struct iio_dev *indio_dev,

drivers/iio/industrialio-counter.c line 99

Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10 -0
+	uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	const struct iio_counter_value *value;
+
+	value = iio_counter_value_find_by_id(counter, chan->channel2);
+	if (!value)
+		return -EINVAL;
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n", value->name);

I mentioned before but I wonder if we can just push name down into the chan spec itself in some fashion. Whether to associate it with existing datasheet_name or not is an open question however.

Quoted 153 lines
drivers/iio/industrialio-counter.c new file +153 -0
+}
+
+static ssize_t iio_counter_value_triggers_read(struct iio_dev *indio_dev,
+	uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	struct iio_counter_value *value;
+	size_t i;
+	const struct iio_counter_trigger *trigger;
+	ssize_t len = 0;
+
+	value = iio_counter_value_find_by_id(counter, chan->channel2);
+	if (!value)
+		return -EINVAL;
+
+	/* Print out a list of every Signal association to Value */
+	for (i = 0; i < value->num_triggers; i++) {
+		trigger = value->triggers + i;
+		len += snprintf(buf + len, PAGE_SIZE - len, "%d\t%s\t%s\n",
+			trigger->signal->id, trigger->signal->name,
+			trigger->trigger_modes[trigger->mode]);
+		if (len >= PAGE_SIZE)
+			return -ENOMEM;
+	}
+
+	return len;
+}
+
+static ssize_t iio_counter_trigger_mode_read(struct iio_dev *indio_dev,
+	uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	struct iio_counter_value *value;
+	struct iio_counter_trigger *trigger;
+	const int signal_id = *(int *)((void *)priv);
+	int mode;
+
+	if (!counter->ops->trigger_mode_get)
+		return -EINVAL;
+
+	value = iio_counter_value_find_by_id(counter, chan->channel2);
+	if (!value)
+		return -EINVAL;
+
+	trigger = iio_counter_trigger_find_by_id(value, signal_id);
+	if (!trigger)
+		return -EINVAL;
+
+	mode = counter->ops->trigger_mode_get(counter, value, trigger);
+
+	if (mode < 0)
+		return mode;
+	else if (mode >= trigger->num_trigger_modes)
+		return -EINVAL;
+
+	trigger->mode = mode;
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n", trigger->trigger_modes[mode]);
+}
+
+static ssize_t iio_counter_trigger_mode_write(struct iio_dev *indio_dev,
+	uintptr_t priv, const struct iio_chan_spec *chan, const char *buf,
+	size_t len)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	struct iio_counter_value *value;
+	ssize_t err;
+	struct iio_counter_trigger *trigger;
+	const int signal_id = *(int *)((void *)priv);
+	unsigned int mode;
+
+	if (!counter->ops->trigger_mode_set)
+		return -EINVAL;
+
+	value = iio_counter_value_find_by_id(counter, chan->channel2);
+	if (!value)
+		return -EINVAL;
+
+	trigger = iio_counter_trigger_find_by_id(value, signal_id);
+	if (!trigger)
+		return -EINVAL;
+
+	for (mode = 0; mode < trigger->num_trigger_modes; mode++)
+		if (sysfs_streq(buf, trigger->trigger_modes[mode]))
+			break;
+
+	if (mode >= trigger->num_trigger_modes)
+		return -EINVAL;
+
+	err = counter->ops->trigger_mode_set(counter, value, trigger, mode);
+	if (err)
+		return err;
+
+	trigger->mode = mode;
+
+	return len;
+}
+
+static ssize_t iio_counter_trigger_mode_available_read(
+	struct iio_dev *indio_dev, uintptr_t priv,
+	const struct iio_chan_spec *chan, char *buf)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	struct iio_counter_value *value;
+	ssize_t len = 0;
+	struct iio_counter_trigger *trigger;
+	const int signal_id = *(int *)((void *)priv);
+	unsigned int i;
+
+	value = iio_counter_value_find_by_id(counter, chan->channel2);
+	if (!value)
+		return -EINVAL;
+
+	trigger = iio_counter_trigger_find_by_id(value, signal_id);
+	if (!trigger)
+		return -EINVAL;
+
+	for (i = 0; i < trigger->num_trigger_modes; i++)
+		len += scnprintf(buf + len, PAGE_SIZE - len, "%s ",
+			trigger->trigger_modes[i]);
+
+	buf[len - 1] = '\n';
+
+	return len;
+}
+
+static int iio_counter_value_function_set(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, unsigned int mode)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	struct iio_counter_value *value;
+	int err;
+
+	if (!counter->ops->value_function_set)
+		return -EINVAL;
+
+	/* Find relevant Value */
+	value = iio_counter_value_find_by_id(counter, chan->channel2);
+	if (!value)
+		return -EINVAL;
+
+	/* Map IIO core function_set to Generic Counter value_function_set */
+	err = counter->ops->value_function_set(counter, value, mode);
+	if (err)
+		return err;
+
+	value->mode = mode;
+
+	return 0;
+}
+
+static int iio_counter_value_function_get(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan)

drivers/iio/industrialio-counter.c line 262

Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10 -0
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	struct iio_counter_value *value;
+	int retval;
+
+	if (!counter->ops->value_function_get)
+		return -EINVAL;
+
+	/* Find relevant Value */
+	value = iio_counter_value_find_by_id(counter, chan->channel2);

Same argument as below - would the driver not be able to do this better? Often it would know a simple transform to get the right one...

Quoted 142 lines
drivers/iio/industrialio-counter.c new file +142 -0
+	if (!value)
+		return -EINVAL;
+
+	/* Map IIO core function_get to Generic Counter value_function_get */
+	retval = counter->ops->value_function_get(counter, value);
+	if (retval < 0)
+		return retval;
+	else if (retval >= value->num_function_modes)
+		return -EINVAL;
+
+	value->mode = retval;
+
+	return retval;
+}
+
+static int iio_counter_value_ext_info_alloc(struct iio_chan_spec *const chan,
+	struct iio_counter_value *const value)
+{
+	const struct iio_chan_spec_ext_info ext_info_default[] = {
+		{
+			.name = "name",
+			.shared = IIO_SEPARATE,
+			.read = iio_counter_value_name_read
+		},
+		IIO_ENUM("function", IIO_SEPARATE, &value->function_enum),
+		{
+			.name = "function_available",
+			.shared = IIO_SEPARATE,
+			.read = iio_enum_available_read,
+			.private = (void *)&value->function_enum
+		},
+		{
+			.name = "triggers",
+			.shared = IIO_SEPARATE,
+			.read = iio_counter_value_triggers_read
+		}
+	};
+	const size_t num_default = ARRAY_SIZE(ext_info_default);
+	const struct iio_chan_spec_ext_info ext_info_trigger[] = {
+		{
+			.shared = IIO_SEPARATE,
+			.read = iio_counter_trigger_mode_read,
+			.write = iio_counter_trigger_mode_write
+		},
+		{
+			.shared = IIO_SEPARATE,
+			.read = iio_counter_trigger_mode_available_read
+		}
+	};
+	const size_t num_ext_info_trigger = ARRAY_SIZE(ext_info_trigger);
+	const size_t num_triggers_ext_info = num_ext_info_trigger *
+		value->num_triggers;
+	const size_t num_ext_info = num_default + num_triggers_ext_info + 1;
+	int err;
+	struct iio_chan_spec_ext_info *ext_info;
+	const struct iio_counter_trigger *trigger;
+	size_t i;
+	size_t j;
+
+	/* Construct function_enum for Value */
+	value->function_enum.items = value->function_modes;
+	value->function_enum.num_items = value->num_function_modes;
+	value->function_enum.set = iio_counter_value_function_set;
+	value->function_enum.get = iio_counter_value_function_get;
+
+	ext_info = kmalloc_array(num_ext_info, sizeof(*ext_info), GFP_KERNEL);
+	if (!ext_info)
+		return -ENOMEM;
+	ext_info[num_ext_info - 1].name = NULL;
+
+	/* Add ext_info for the name, function, function_available, and triggers
+	 * attributes
+	 */
+	memcpy(ext_info, ext_info_default, sizeof(ext_info_default));
+	/* Add ext_info for the trigger_signalX-Z and
+	 * trigger_signalX-Z_available attributes for each Trigger
+	 */
+	for (i = 0; i < num_triggers_ext_info; i += num_ext_info_trigger)
+		memcpy(ext_info + num_default + i, ext_info_trigger,
+			sizeof(ext_info_trigger));
+
+	/* Set name for each trigger_signalX-Z and trigger_signalX-Z_available
+	 * attribute; store the respective Signal ID address in each ext_info
+	 * private member
+	 */
+	for (i = num_default, j = 0; j < value->num_triggers; i++, j++) {
+		trigger = value->triggers + j;
+
+		ext_info[i].name = kasprintf(GFP_KERNEL, "trigger_signal%d-%d",
+			chan->channel, trigger->signal->id);
+		if (!ext_info[i].name) {
+			err = -ENOMEM;
+			goto err_name_alloc;
+		}
+		ext_info[i].private = (void *)&trigger->signal->id;
+		i++;
+
+		ext_info[i].name = kasprintf(GFP_KERNEL,
+			"trigger_signal%d-%d_available",
+			chan->channel, trigger->signal->id);
+		if (!ext_info[i].name) {
+			err = -ENOMEM;
+			goto err_name_alloc;
+		}
+		ext_info[i].private = (void *)&trigger->signal->id;
+	}
+
+	chan->ext_info = ext_info;
+
+	return 0;
+
+err_name_alloc:
+	while (i-- > num_default)
+		kfree(ext_info[i].name);
+	kfree(ext_info);
+	return err;
+}
+
+static void iio_counter_value_ext_info_free(
+	const struct iio_chan_spec *const channel)
+{
+	size_t i;
+	const char *const prefix = "trigger_signal";
+	const size_t prefix_len = strlen(prefix);
+
+	for (i = 0; channel->ext_info[i].name; i++)
+		if (!strncmp(channel->ext_info[i].name, prefix, prefix_len))
+			kfree(channel->ext_info[i].name);
+	kfree(channel->ext_info);
+}
+
+static const struct iio_chan_spec_ext_info iio_counter_signal_ext_info[] = {
+	{
+		.name = "name",
+		.shared = IIO_SEPARATE,
+		.read = iio_counter_signal_name_read
+	},
+	{}
+};
+
+static int iio_counter_channels_alloc(struct iio_counter *const counter)
+{

drivers/iio/industrialio-counter.c line 414

Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10 -0
+	const size_t num_channels = counter->num_signals + counter->num_values +
+		counter->num_channels;
+	int err;
+	struct iio_chan_spec *channels;
+	size_t j;
+	struct iio_counter_value *value;
+	size_t i = counter->num_channels;
+	const struct iio_counter_signal *signal;
+
+	channels = kcalloc(num_channels, sizeof(*channels), GFP_KERNEL);

Bring the calculation of num_channels down here - it will make it more obvious that this allows space for both sources of channel.

Quoted 11 lines
drivers/iio/industrialio-counter.c new file +11 -0
+	if (!channels)
+		return -ENOMEM;
+
+	/* If any channels were supplied on Counter registration,
+	 * we add them here to the front of the array
+	 */
+	memcpy(channels, counter->channels,
+		counter->num_channels * sizeof(*counter->channels));
+
+	/* Add channel for each Value */
+	for (j = 0; j < counter->num_values; j++) {

drivers/iio/industrialio-counter.c line 435

Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10 -0
+		value = counter->values + j;
+
+		channels[i].type = IIO_COUNT;
+		channels[i].channel = counter->id;
+		channels[i].channel2 = value->id;
+		channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+		channels[i].indexed = 1;
+		channels[i].counter = 1;
+
+		/* Add channels for Triggers associated with Value */

Add channels? 'i' would need incrementing. Also that's not what this function
is doing that I can see...

Quoted 97 lines
drivers/iio/industrialio-counter.c new file +97 -0
+		err = iio_counter_value_ext_info_alloc(channels + i, value);
+		if (err)
+			goto err_value_ext_info_alloc;
+
+		i++;
+	}
+
+	/* Add channel for each Signal */
+	for (j = 0; j < counter->num_signals; j++) {
+		signal = counter->signals + j;
+
+		channels[i].type = IIO_SIGNAL;
+		channels[i].channel = counter->id;
+		channels[i].channel2 = signal->id;
+		channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+		channels[i].indexed = 1;
+		channels[i].counter = 1;
+		channels[i].ext_info = iio_counter_signal_ext_info;
+
+		i++;
+	}
+
+	counter->indio_dev->num_channels = num_channels;
+	counter->indio_dev->channels = channels;
+
+	return 0;
+
+err_value_ext_info_alloc:
+	while (i-- > counter->num_channels)
+		iio_counter_value_ext_info_free(channels + i);
+	kfree(channels);
+	return err;
+}
+
+static void iio_counter_channels_free(const struct iio_counter *const counter)
+{
+	size_t i = counter->num_channels + counter->indio_dev->num_channels;
+	const struct iio_chan_spec *const chans = counter->indio_dev->channels;
+
+	while (i-- > counter->num_channels)
+		/* Only IIO_COUNT channels need to be freed here */
+		if (chans[i].type == IIO_COUNT)
+			iio_counter_value_ext_info_free(chans + i);
+
+	kfree(chans);
+}
+
+static int iio_counter_read_raw(struct iio_dev *indio_dev,
+	struct iio_chan_spec const *chan, int *val, int *val2, long mask)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	struct iio_counter_signal *signal;
+	struct iio_counter_value *value;
+
+	if (mask != IIO_CHAN_INFO_RAW)
+		return -EINVAL;
+
+	switch (chan->type) {
+	/* Map read_raw to signal_read for Signal */
+	case IIO_SIGNAL:
+		if (!counter->ops->signal_read)
+			return -EINVAL;
+
+		signal = iio_counter_signal_find_by_id(counter, chan->channel2);
+		if (!signal)
+			return -EINVAL;
+
+		return counter->ops->signal_read(counter, signal, val, val2);
+	/* Map read_raw to value_read for Value */
+	case IIO_COUNT:
+		if (!counter->ops->value_read)
+			return -EINVAL;
+
+		value = iio_counter_value_find_by_id(counter, chan->channel2);
+		if (!value)
+			return -EINVAL;
+
+		return counter->ops->value_read(counter, value, val, val2);
+	/* Map read_raw to read_raw for non-counter channel */
+	default:
+		if (counter->info && counter->info->read_raw)
+			return counter->info->read_raw(indio_dev, chan, val,
+				val2, mask);
+	}
+
+	return -EINVAL;
+}
+
+static int iio_counter_write_raw(struct iio_dev *indio_dev,
+	struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	struct iio_counter_signal *signal;
+	struct iio_counter_value *value;
+
+	if (mask != IIO_CHAN_INFO_RAW)
+		return -EINVAL;

drivers/iio/industrialio-counter.c line 542

Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10 -0
+
+	switch (chan->type) {
+	/* Map write_raw to signal_write for Signal */
+	case IIO_SIGNAL:
+		if (!counter->ops->signal_write)
+			return -EINVAL;
+
+		signal = iio_counter_signal_find_by_id(counter, chan->channel2);
+		if (!signal)
+			return -EINVAL;

Hmm. have to search is certainly a little ugly. Particularly as the driver would be able to maintain this as a lookup. I'd be tempted to pass the signal id down into the callback rather than finding the signal in the core. Might turn out messier though... :)

drivers/iio/industrialio-counter.c line 550

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+
+		return counter->ops->signal_write(counter, signal, val, val2);
+	/* Map write_raw to value_write for Value */
+	case IIO_COUNT:
+		if (!counter->ops->value_write)
+			return -EINVAL;
+
+		value = iio_counter_value_find_by_id(counter, chan->channel2);

Again, this mapping is a simple lookup in the driver I think, perhaps push it down there?

Quoted 36 lines
drivers/iio/industrialio-counter.c new file +36 -0
+		if (!value)
+			return -EINVAL;
+
+		return counter->ops->value_write(counter, value, val, val2);
+	/* Map write_raw to write_raw for non-counter channel */
+	default:
+		if (counter->info && counter->info->write_raw)
+			return counter->info->write_raw(indio_dev, chan, val,
+				val2, mask);
+	}
+
+	return -EINVAL;
+}
+
+static int iio_counter_signals_validate(const struct iio_counter *const counter)
+{
+	size_t i;
+	const struct iio_counter_signal *signal;
+	size_t j;
+	int curr_id;
+
+	/* At least one Signal must be defined */
+	if (!counter->num_signals || !counter->signals) {
+		pr_err("Counter '%d' Signals undefined\n", counter->id);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < counter->num_signals; i++) {
+		signal = counter->signals + i;
+		/* No two Signals may share the same ID */
+		for (j = 0; j < i; j++) {
+			curr_id = counter->signals[j].id;
+			if (curr_id == signal->id) {
+				pr_err("Duplicate Counter '%d' Signal '%d'\n",
+						counter->id, signal->id);
+				return -EEXIST;

drivers/iio/industrialio-counter.c line 596

Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10 -0
+			}
+		}
+		for (j = i + 1; j < counter->num_signals; j++) {
+			curr_id = counter->signals[j].id;
+			if (curr_id == signal->id) {
+				pr_err("Duplicate Counter '%d' Signal '%d'\n",
+						counter->id, signal->id);
+				return -EEXIST;
+			}
+		}

Same as below.

Quoted 12 lines
drivers/iio/industrialio-counter.c new file +12 -0
+	}
+
+	return 0;
+}
+
+static int iio_counter_triggers_validate(const int counter_id,
+	const struct iio_counter_value *const value)
+{
+	size_t i;
+	const struct iio_counter_trigger *trigger;
+	size_t j;
+	int curr_id;

drivers/iio/industrialio-counter.c line 618

Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10 -0
+
+	/* At least one Trigger must be defined */
+	if (!value->num_triggers || !value->triggers) {
+		pr_err("Counter '%d' Value '%d' Triggers undefined\n",
+			counter_id, value->id);
+		return -EINVAL;
+	}
+
+	/* Ensure all Triggers have a defined Signal; this prevents a possible
+	 * NULL pointer dereference when later validating Trigger Signal IDs

Fix comment syntax throughout...

Quoted 20 lines
drivers/iio/industrialio-counter.c new file +20 -0
+	 */
+	for (i = 0; i < value->num_triggers; i++)
+		if (!value->triggers[i].signal) {
+			pr_err("Counter '%d' Trigger '%zu' Signal undefined\n",
+				counter_id, i);
+			return -EINVAL;
+		}
+
+	/* Verify validity of each Trigger */
+	for (i = 0; i < value->num_triggers; i++) {
+		trigger = value->triggers + i;
+		/* No two Trigger Signals may share the same ID */
+		for (j = 0; j < i; j++) {
+			curr_id = value->triggers[j].signal->id;
+			if (curr_id == trigger->signal->id) {
+				pr_err("Signal '%d' is already linked to Counter '%d' Value '%d'\n",
+					trigger->signal->id, counter_id,
+					value->id);
+				return -EEXIST;
+			}

drivers/iio/industrialio-counter.c line 648

Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10 -0
+		}
+		for (j = i + 1; j < value->num_triggers; j++) {
+			curr_id = value->triggers[j].signal->id;
+			if (curr_id == trigger->signal->id) {
+				pr_err("Signal '%d' is already linked to Counter '%d' Value '%d'\n",
+					trigger->signal->id, counter_id,
+					value->id);
+				return -EEXIST;
+			}
+		}

Again, one loop with the condition changed so it doesn't fault on the i = j case -- see below and note I'm reviewing backwards...

Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4 -0
+
+		/* At least one trigger mode must be defined for each Trigger */
+		if (!trigger->num_trigger_modes || !trigger->trigger_modes) {
+			pr_err("Counter '%d' Signal '%d' trigger modes undefined\n",

drivers/iio/industrialio-counter.c line 662

Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10 -0
+				counter_id, trigger->signal->id);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int iio_counter_values_validate(const struct iio_counter *const counter)
+{

Hmm. Pretty heavy weight checking. Ah well, up to you.

Quoted 7 lines
drivers/iio/industrialio-counter.c new file +7 -0
+	size_t i;
+	const struct iio_counter_value *value;
+	size_t j;
+	int curr_id;
+	int err;
+
+	/* At least one Value must be defined */

drivers/iio/industrialio-counter.c line 679

Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10 -0
+	if (!counter->num_values || !counter->values) {
+		pr_err("Counter '%d' Values undefined\n", counter->id);
+		return -EINVAL;
+	}
+
+	/* Verify validity of each Value */
+	for (i = 0; i < counter->num_values; i++) {
+		value = counter->values + i;
+		/* No two Values may share the same ID */
+		for (j = 0; j < i; j++) {

Single loop with a slightly change to condition
if ((i != j) and (curr_id == value->id))

Quoted 52 lines
drivers/iio/industrialio-counter.c new file +52 -0
+			curr_id = counter->values[j].id;
+			if (curr_id == value->id) {
+				pr_err("Duplicate Counter '%d' Value '%d'\n",
+					counter->id, value->id);
+				return -EEXIST;
+			}
+		}
+		for (j = i + 1; j < counter->num_values; j++) {
+			curr_id = counter->values[j].id;
+			if (curr_id == value->id) {
+				pr_err("Duplicate Counter '%d' Value '%d'\n",
+					counter->id, value->id);
+				return -EEXIST;
+			}
+		}
+
+		/* At least one function mode must be defined for each Value */
+		if (!value->num_function_modes || !value->function_modes) {
+			pr_err("Counter '%d' Value '%d' function modes undefined\n",
+				counter->id, value->id);
+			return -EINVAL;
+		}
+
+		/* Verify the Triggers associated with each Value */
+		err = iio_counter_triggers_validate(counter->id, value);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+/**
+ * iio_counter_register - register Counter to the system
+ * @counter: pointer to IIO Counter to register
+ *
+ * This function piggybacks off of iio_device_register. First, the relevant
+ * Counter members are validated; the signals and values members must be defined
+ * and populated by valid Signal and Value structures respectively. Next, a
+ * struct iio_dev is allocated by a call to iio_device_alloc and initialized for
+ * the Counter, IIO channels are allocated, the Counter address is stored as the
+ * private data, and finally iio_device_register is called.
+ */
+int iio_counter_register(struct iio_counter *const counter)
+{
+	const struct iio_info info_default = {
+		.read_raw = iio_counter_read_raw,
+		.write_raw = iio_counter_write_raw
+	};
+	int err;
+	struct iio_info *info;
+	struct iio_counter **priv;

drivers/iio/industrialio-counter.c line 741

Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10 -0
+
+	if (!counter)
+		return -EINVAL;
+
+	/* Verify that Signals are valid and IDs do not conflict */
+	err = iio_counter_signals_validate(counter);
+	if (err)
+		return err;
+
+	/* Verify that Values are valid and IDs do not conflict;

Fix multiline comment syntax to standard kernel syntax. /*
* Verify
*/

Quoted 201 lines
drivers/iio/industrialio-counter.c new file +159 -0
+	 * Triggers for each Value are also verified for validity
+	 */
+	err = iio_counter_values_validate(counter);
+	if (err)
+		return err;
+
+	counter->indio_dev = iio_device_alloc(sizeof(counter));
+	if (!counter->indio_dev)
+		return -ENOMEM;
+
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (!info) {
+		err = -ENOMEM;
+		goto err_info_alloc;
+	}
+	/* If an iio_info has been supplied than we use that,
+	 * otherwise we set all callbacks to NULL; iio_counter_read_raw
+	 * and iio_counter_write_raw is used for read_raw and write_raw
+	 * for either case in order to support counter functionality
+	 * (supplied read_raw/write_raw will be called from within
+	 * iio_counter_read_raw/iio_counter_write_raw for non-counter
+	 * channels)
+	 */
 
+	if (counter->info) {
+		memcpy(info, counter->info, sizeof(*counter->info));
+		info->read_raw = iio_counter_read_raw;
+		info->write_raw = iio_counter_write_raw;
+	} else {
+		memcpy(info, &info_default, sizeof(info_default));
+	}
+
+	counter->indio_dev->info = info;
+	counter->indio_dev->modes = INDIO_DIRECT_MODE;
+	counter->indio_dev->name = counter->name;
+	counter->indio_dev->dev.parent = counter->dev;
+
+	/* IIO channels are allocated and set for Signals, Values, and Triggers;
+	 * any auxiliary IIO channels provided in iio_counter are also set
+	 */
+	err = iio_counter_channels_alloc(counter);
+	if (err)
+		goto err_channels_alloc;
+
+	/* Pointer to the counter is stored in indio_dev as a way to refer
+	 * back to the counter from within various IIO callbacks
+	 */
+	priv = iio_priv(counter->indio_dev);
+	memcpy(priv, &counter, sizeof(*priv));
+
+	err = iio_device_register(counter->indio_dev);
+	if (err)
+		goto err_iio_device_register;
+
+	return 0;
+
+err_iio_device_register:
+	iio_counter_channels_free(counter);
+err_channels_alloc:
+	kfree(info);
+err_info_alloc:
+	iio_device_free(counter->indio_dev);
+	return err;
+}
+EXPORT_SYMBOL(iio_counter_register);
+
+/**
+ * iio_counter_unregister - unregister Counter from the system
+ * @counter: pointer to IIO Counter to unregister
+ *
+ * The Counter is unregistered from the system. The indio_dev is unregistered
+ * and all allocated memory is freed.
+ */
+void iio_counter_unregister(struct iio_counter *const counter)
+{
+	const struct iio_info *const info = counter->indio_dev->info;
+
+	if (!counter)
+		return;
+
+	iio_device_unregister(counter->indio_dev);
+
+	iio_counter_channels_free(counter);
+
+	kfree(info);
+	iio_device_free(counter->indio_dev);
+}
+EXPORT_SYMBOL(iio_counter_unregister);
+
+static void devm_iio_counter_unreg(struct device *dev, void *res)
+{
+	iio_counter_unregister(*(struct iio_counter **)res);
+}
+
+/**
+ * devm_iio_counter_register - Resource-managed iio_counter_register
+ * @dev: Device to allocate iio_counter for
+ * @counter: pointer to IIO Counter to register
+ *
+ * Managed iio_counter_register. The IIO counter registered with this
+ * function is automatically unregistered on driver detach. This function
+ * calls iio_counter_register internally. Refer to that function for more
+ * information.
+ *
+ * If an iio counter registered with this function needs to be unregistered
+ * separately, devm_iio_counter_unregister must be used.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int devm_iio_counter_register(struct device *dev,
+	struct iio_counter *const counter)
+{
+	struct iio_counter **ptr;
+	int ret;
+
+	ptr = devres_alloc(devm_iio_counter_unreg, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return -ENOMEM;
+
+	*ptr = counter;
+	ret = iio_counter_register(counter);
+	if (!ret)
+		devres_add(dev, ptr);
+	else
+		devres_free(ptr);
+
+	return ret;
+}
+EXPORT_SYMBOL(devm_iio_counter_register);
+
+static int devm_iio_counter_match(struct device *dev, void *res, void *data)
+{
+	struct iio_counter **r = res;
+
+	if (!r || !*r) {
+		WARN_ON(!r || !*r);
+		return 0;
+	}
+
+	return *r == data;
+}
+
+/**
+ * devm_iio_counter_unregister - Resource-managed iio_counter_unregister
+ * @dev: Device this iio_counter belongs to
+ * @counter: the iio counter associated with the device
+ *
+ * Unregister iio counter registered with devm_iio_counter_register.
+ */
+void devm_iio_counter_unregister(struct device *dev,
+	struct iio_counter *const counter)
+{
+	int rc;
+
+	rc = devres_release(dev, devm_iio_counter_unreg,
+		devm_iio_counter_match, counter);
+	WARN_ON(rc);
+}
+EXPORT_SYMBOL(devm_iio_counter_unregister);
include/linux/iio/counter.h new file +35 -0
new file mode 100644
index 000000000000..35645406711a
@@ -0,0 +1,166 @@
+/*
+ * Industrial I/O counter interface
+ * Copyright (C) 2017 William Breathitt Gray
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+#ifndef _IIO_COUNTER_H_
+#define _IIO_COUNTER_H_
+
+#ifdef CONFIG_IIO_COUNTER
+
+#include <linux/device.h>
+#include <linux/types.h>
+
+#include <linux/iio/iio.h>
+
+/**
+ * struct iio_counter_signal - IIO Counter Signal node
+ * @id:		[DRIVER] unique ID used to identify signal
+ * @name:	[DRIVER] device-specific signal name
+ */
+struct iio_counter_signal {
+	int		id;
+	const char	*name;
+};
+
+/**
+ * struct iio_counter_trigger - IIO Counter Trigger node

include/linux/iio/counter.h line 45

Quoted 10 lines
include/linux/iio/counter.h new file +10 -0
+ * @mode:		[DRIVER] current trigger mode state
+ * @trigger_modes:	[DRIVER] available trigger modes
+ * @num_trigger_modes:	[DRIVER] number of modes specified in @trigger_modes
+ * @signal:		[DRIVER] pointer to associated signal
+ */
+struct iio_counter_trigger {
+	unsigned int			mode;
+	const char *const		*trigger_modes;
+	unsigned int			num_trigger_modes;
+	struct iio_counter_signal	*signal;

Are signals actually changeable? - Or is that pointer constant?

Quoted 24 lines
include/linux/iio/counter.h new file +24 -0
+};
+
+/**
+ * struct iio_counter_value - IIO Counter Value node
+ * @id:			[DRIVER] unique ID used to identify value
+ * @name:		[DRIVER] device-specific value name
+ * @mode:		[DRIVER] current function mode state
+ * @function_modes:	[DRIVER] available function modes
+ * @num_function_modes:	[DRIVER] number of modes specified in @function_modes
+ * @triggers:		[DRIVER] array of triggers for initialization
+ * @num_triggers:	[DRIVER] number of triggers specified in @triggers
+ * @function_enum:	[INTERN] used internally to generate function attributes
+ */
+struct iio_counter_value {
+	int			id;
+	const char		*name;
+	unsigned int		mode;
+	const char *const	*function_modes;
+	unsigned int		num_function_modes;
+
+	struct iio_counter_trigger	*triggers;
+	size_t				num_triggers;
+
+	struct iio_enum		function_enum;

include/linux/iio/counter.h line 79

Quoted 10 lines
include/linux/iio/counter.h new file +10 -0
+};
+
+struct iio_counter;
+
+/**
+ * struct iio_counter_ops - IIO Counter related callbacks
+ * @signal_read:	function to request a signal value from the device.
+ *			Return value will specify the type of value returned by
+ *			the device. val and val2 will contain the elements
+ *			making up the returned value.

Add some detail on the importance of the return value.

include/linux/iio/counter.h line 81

Quoted 2 lines
include/linux/iio/counter.h new file +2 -0
+ * @signal_write:	function to write a signal value to the device.
+ *                      Parameters are interpreted the same as signal_read.

You need a means of establishing the correct format for write (we needed it in IIO fairly early on too)

Quoted 47 lines
include/linux/iio/counter.h new file +47 -0
+ * @trigger_mode_set:	function to set the trigger mode. mode is the index of
+ *			the requested mode from the value trigger_modes array.
+ * @trigger_mode_get:	function to get the current trigger mode. Return value
+ *			will specify the index of the current mode from the
+ *			value trigger_modes array.
+ * @value_read:		function to request a value value from the device.
+ *			Return value will specify the type of value returned by
+ *			the device. val and val2 will contain the elements
+ *			making up the returned value.
+ * @value_write:	function to write a value value to the device.
+ *                      Parameters are interpreted the same as value_read.
+ * @value_function_set: function to set the value function mode. mode is the
+ *			index of the requested mode from the value
+ *			function_modes array.
+ * @value_function_get: function to get the current value function mode. Return
+ *			value will specify the index of the current mode from
+ *			the value function_modes array.
+ */
+struct iio_counter_ops {
+	int (*signal_read)(struct iio_counter *counter,
+		struct iio_counter_signal *signal, int *val, int *val2);
+	int (*signal_write)(struct iio_counter *counter,
+		struct iio_counter_signal *signal, int val, int val2);
+	int (*trigger_mode_set)(struct iio_counter *counter,
+		struct iio_counter_value *value,
+		struct iio_counter_trigger *trigger, unsigned int mode);
+	int (*trigger_mode_get)(struct iio_counter *counter,
+		struct iio_counter_value *value,
+		struct iio_counter_trigger *trigger);
+	int (*value_read)(struct iio_counter *counter,
+		struct iio_counter_value *value, int *val, int *val2);
+	int (*value_write)(struct iio_counter *counter,
+		struct iio_counter_value *value, int val, int val2);
+	int (*value_function_set)(struct iio_counter *counter,
+		struct iio_counter_value *value, unsigned int mode);
+	int (*value_function_get)(struct iio_counter *counter,
+		struct iio_counter_value *value);
+};
+
+/**
+ * struct iio_counter - IIO Counter data structure
+ * @id:			[DRIVER] unique ID used to identify counter
+ * @name:		[DRIVER] name of the device
+ * @dev:		[DRIVER] device structure, should be assigned a parent
+ *			and owner
+ * @ops:		[DRIVER] callbacks from driver for counter components
+ * @signals:		[DRIVER] array of signals for initialization

include/linux/iio/counter.h line 138

Quoted 10 lines
include/linux/iio/counter.h new file +10 -0
+ * @num_signals:	[DRIVER] number of signals specified in @signals
+ * @values:		[DRIVER] array of values for initialization
+ * @num_values:		[DRIVER] number of values specified in @values
+ * @channels:		[DRIVER] channel specification structure table
+ * @num_channels:	[DRIVER] number of channels specified in @channels
+ * @info:		[DRIVER] callbacks and constant info from driver
+ * @indio_dev:		[INTERN] industrial I/O device structure
+ * @driver_data:	[DRIVER] driver data
+ */
+struct iio_counter {

Mentioned earlier - I think naming the device counter, is confusing. The counters should be named counter - call it iio_counter_device or iio_counter_group or something and keep counter for the things currently called value.

Quoted 28 lines
include/linux/iio/counter.h new file +28 -0
+	int				id;
+	const char			*name;
+	struct device			*dev;
+	const struct iio_counter_ops	*ops;
+
+	struct iio_counter_signal	*signals;
+	size_t				num_signals;
+	struct iio_counter_value	*values;
+	size_t				num_values;
+
+	const struct iio_chan_spec	*channels;
+	size_t				num_channels;
+	const struct iio_info		*info;
+
+	struct iio_dev	*indio_dev;
+	void		*driver_data;
+};
+
+int iio_counter_register(struct iio_counter *const counter);
+void iio_counter_unregister(struct iio_counter *const counter);
+int devm_iio_counter_register(struct device *dev,
+	struct iio_counter *const counter);
+void devm_iio_counter_unregister(struct device *dev,
+	struct iio_counter *const counter);
+
+#endif /* CONFIG_IIO_COUNTER */
+
+#endif /* _IIO_COUNTER_H_ */
maintainer
#2 lore raw

Re: [PATCH v3 1/6] iio: Implement counter channel specification and IIO_SIGNAL constant

In reply to William Breathitt Gray
Quoted 10 lines · On Thu, 5 Oct 2017 14:13:30 -0400 William Breathitt Gray…

Counters are IIO devices that are exposed via a generic counter interface consisting of one or more counter signals (IIO_SIGNAL) linked to one or more counter values (IIO_COUNT).

This patch introduces the IIO_SIGNAL constant which represents a counter device signal line. Additionally, a new "counter" member is added to struct iio_chan_spec, with relevant support, to indicate that a channel is part of a counter.

I have a feeling this support could be useful more generically
than just for counters. There are other cases where a couple
of different input signals are combined to give one result and we don't currently represent that combination.

A complex example would be differential channels where we have some elements specific to the individual lines (the ability to apply front end PGA type effects separately) - Not sure I've seen one of these though ;)

Simpler case is light sensors - two light sensors are combined to provide an illuminance output (one is typically all wavelengths and one is infrared only - as we want to remove that from signal).

Some of these devices have multiple sensor inputs and right now we don't make it clear which ones feed which illuminance values and we should do.

Now this introduces another issue - those channels are indexed and modified so already using both channel and channel2.

So... Do we ever need to represent provide the 'counter' element in an event, (which is where we are short on space in the ABI)? I don't think we do as channel is unique anyway (I think) so this isn't a problem.

If not - define a new element to take this index rather than overloading channel2 (again).

Jonathan

Quoted 20 lines
---
 drivers/iio/industrialio-core.c | 14 +++++++++++++-
 include/linux/iio/iio.h         |  2 ++
 include/uapi/linux/iio/types.h  |  1 +
 3 files changed, 16 insertions(+), 1 deletion(-)
drivers/iio/industrialio-core.c +1 -0
index 7a5aa127c52e..ee508f2070a7 100644
@@ -85,6 +85,7 @@ static const char * const iio_chan_type_name_spec[] = {
 	[IIO_COUNT] = "count",
 	[IIO_INDEX] = "index",
 	[IIO_GRAVITY]  = "gravity",
+	[IIO_SIGNAL] = "signal",
 };
 
 static const char * const iio_modifier_names[] = {
@@ -989,7 +990,18 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
 			break;

drivers/iio/industrialio-core.c int __iio_device_attr_init(struct device_attribute *dev_attr, · line 999

Quoted 10 lines
drivers/iio/industrialio-core.c +7 -1
 
 		case IIO_SEPARATE:
-			if (chan->indexed)
+			if (chan->counter) {
+				if (!chan->indexed) {
+					WARN(1, "Counter channels must be indexed\n");
+					ret = -EINVAL;
+					goto error_free_full_postfix;
+				}
+				name = kasprintf(GFP_KERNEL, "%s%d-%d_%s",

Hmm. - has a meaning in IIO already - perhaps we need a different symbol to represent this grouping concept?

drivers/iio/industrialio-core.c int __iio_device_attr_init(struct device_attribute *dev_attr, · line 1004

Quoted 5 lines
drivers/iio/industrialio-core.c +5 -0
+						    iio_chan_type_name_spec[chan->type],
+						    chan->channel,
+						    chan->channel2,
+						    full_postfix);
+			} else if (chan->indexed)

Ultimately probably want to cover the various shared cases as well.

Quoted 35 lines
drivers/iio/industrialio-core.c +0 -0
 				name = kasprintf(GFP_KERNEL, "%s_%s%d_%s",
 						    iio_direction[chan->output],
 						    iio_chan_type_name_spec[chan->type],
include/linux/iio/iio.h +2 -0
index 50cae8504256..9f949dd74b60 100644
@@ -263,6 +263,7 @@ struct iio_event_spec {
  *			attributes but not for event codes.
  * @output:		Channel is output.
  * @differential:	Channel is differential.
+ * @counter:		Channel is part of a counter.
  */
 struct iio_chan_spec {
 	enum iio_chan_type	type;
@@ -295,6 +296,7 @@ struct iio_chan_spec {
 	unsigned		indexed:1;
 	unsigned		output:1;
 	unsigned		differential:1;
+	unsigned		counter:1;
 };
 
 
include/uapi/linux/iio/types.h +1 -0
index ffafd6c25a48..313899652ca7 100644
@@ -43,6 +43,7 @@ enum iio_chan_type {
 	IIO_COUNT,
 	IIO_INDEX,
 	IIO_GRAVITY,
+	IIO_SIGNAL,
 };
 
 enum iio_modifier {
maintainer
#7 lore raw

Re: [PATCH v3 3/6] iio: Documentation: Add IIO Generic Counter sysfs documentation

In reply to William Breathitt Gray
Quoted 4 lines · On Thu, 5 Oct 2017 14:13:59 -0400 William Breathitt Gray…

This patch adds standard documentation for the userspace sysfs atrributes of the IIO Generic Counter Interface.

It's a good start - but I think we need to expand on some elements. I'm also worried that repurposing the - in here form being actual subtraction in the differential channels is confusing.

Quoted 15 lines
---
 .../testing/sysfs-bus-iio-generic-counter-sysfs    | 63 ++++++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs
Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs new file +4 -0
new file mode 100644
index 000000000000..b2c09254ec5a
@@ -0,0 +1,63 @@
+What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_function
+KernelVersion:	4.14
+Contact:	[email protected]
+Description:

Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs line 14

Quoted 10 lines
Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs new file +10 -0
+		Counter function mode for Value Y of Counter X; counter function
+		evaluation triggered by conditions specified by the
+		countX-Y_trigger_signalX-Z attributes.
+
+What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_function_available
+KernelVersion:	4.14
+Contact:	[email protected]
+Description:
+		Discrete set of available function modes for the configuration
+		of the respective Value Y of Counter X are listed in this file.

Docs need to list what they can be.

Quoted 9 lines
Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs new file +9 -0
+
+What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_name
+KernelVersion:	4.14
+Contact:	[email protected]
+Description:
+		Read-only attribute that indicates the device-specific name of
+		Value Y of Counter X.
+
+What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_raw

Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs line 33

Quoted 10 lines
Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs new file +10 -0
+KernelVersion:	4.14
+Contact:	[email protected]
+Description:
+		Raw count data of Value Y of Counter X.
+
+What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_triggers
+KernelVersion:	4.14
+Contact:	[email protected]
+Description:
+		List of Signals associated to Value Y of Counter X.

That description doesn't seem to match. This is triggers rather than signals.

Quoted 8 lines
Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs new file +8 -0
+
+What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_trigger_signalX-Z
+KernelVersion:	4.14
+Contact:	[email protected]
+Description:
+		Trigger mode of Value Y for Signal Z. This attribute indicates
+		the condition of Signal Z that triggers the counter function
+		evaluation for Value Y. Both Value Y and Signal Z are part of

Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs line 50

Quoted 10 lines
Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs new file +9 -0
+		Counter X.
 
+
+What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_trigger_signalX-Z_available
+KernelVersion:	4.14
+Contact:	[email protected]
+Description:
+		Discrete set of available trigger modes are listed in this file
+		for the configuration of the respective Trigger associating
+		Signal Z of Counter X with Value Y of Counter X.

it's another case where I think overloading the - is getting confusing. Not sure what the right answer is though!

Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs line 57

Quoted 7 lines
Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs new file +7 -0
+
+What:		/sys/bus/iio/devices/iio:deviceX/signalX-Y_name
+KernelVersion:	4.14
+Contact:	[email protected]
+Description:
+		Read-only attribute that indicates the device-specific name of
+		Signal Y of Counter X.

What is this name for? This doesn't feel like a counter specific thing, but rather a generic IIO addition.

Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs line 63

Quoted 6 lines
Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs new file +6 -0
+
+What:		/sys/bus/iio/devices/iio:deviceX/signalX-Y_raw
+KernelVersion:	4.14
+Contact:	[email protected]
+Description:
+		Raw signal data of Signal Y of Counter X.

While it might seem clunky - I'd prefer us to keep the channel direction prefixes throughout. So that means most of these would end up as in_signalX-Y_raw etc.

As I mentioned before I think overloading the meaning of - is probably a bad idea. Not sure what the right option would be though. Err. Something like in_signalX<Y>_raw? Or do we need to make it really obvious and end up with something like. in_signalX<CounterY>_raw - though that would require another level of info beyond and index to describe that we are dealing with a counter rather than a light channel for example.

I wonder if we would be better off not using the naming to reflect the grouping at all, but rather add something like in_signalX_group

in_counterY_groupmembers which lists the members. Are we trying to stretch the interface too far in sticking to doing it in naming? Not sure...

Jonathan

#12 lore raw

Re: [PATCH v3 5/6] iio: Add dummy counter driver

In reply to Jonathan Cameron

2017-10-08 15:41 GMT+02:00 Jonathan Cameron <[email protected]>:

Quoted 9 lines
Quoted 3 lines · On Thu, 5 Oct 2017 14:14:38 -0400 William Breathitt Gray…

This patch introduces the dummy counter driver. The dummy counter driver serves as a reference implementation of a driver that utilizes the Generic Counter interface.

This is great - I was planning to write one of these to try out the interface and you've already done it :)

May I suggest you to write a gpio-counter driver instead ? (that was in my todo list)
It is more or less the same than this driver but instead of simulate the signal it could use gpio to them provide a software quadratic counter.

Benjamin

Quoted 406 lines
Quoted 10 lines

Writing individual '1' and '0' characters to the Signal attributes allows a user to simulate a typical Counter Signal input stream for evaluation; the Counter will evaluate the Signal data based on the respective trigger mode for the associated Signal, and trigger the associated counter function specified by the respective function mode. The current Value value may be read, and the Value value preset by a write.

Comments are more generic suggestions for improving the example than general comments on the ABI - that all seems to make reasonable sense (other than when the documents contain the wonderful
Value value - not confusing at all ;)

Quoted 85 lines
---
 drivers/iio/counter/Kconfig         |  15 ++
 drivers/iio/counter/Makefile        |   1 +
 drivers/iio/counter/dummy-counter.c | 293 ++++++++++++++++++++++++++++++++++++
 3 files changed, 309 insertions(+)
 create mode 100644 drivers/iio/counter/dummy-counter.c
drivers/iio/counter/Kconfig +15 -0
index c8becfe78e28..494aed40e9c9 100644
@@ -22,6 +22,21 @@ config 104_QUAD_8
        The base port addresses for the devices may be configured via the base
        array module parameter.
 
+config DUMMY_COUNTER
+     tristate "Dummy counter driver"
+     help
+       Select this option to enable the dummy counter driver. The dummy
+       counter driver serves as a reference implementation of a driver that
+       utilizes the Generic Counter interface.
+
+       Writing individual '1' and '0' characters to the Signal attributes
+       allows a user to simulate a typical Counter Signal input stream for
+       evaluation; the Counter will evaluate the Signal data based on the
+       respective trigger mode for the associated Signal, and trigger the
+       associated counter function specified by the respective function mode.
+       The current Value value may be read, and the Value value preset by a
+       write.
+
 config STM32_LPTIMER_CNT
      tristate "STM32 LP Timer encoder counter driver"
      depends on MFD_STM32_LPTIMER || COMPILE_TEST
drivers/iio/counter/Makefile +1 -0
index 1b9a896eb488..8c2ef0115426 100644
@@ -5,4 +5,5 @@
 # When adding new entries keep the list in alphabetical order
 
 obj-$(CONFIG_104_QUAD_8)     += 104-quad-8.o
+obj-$(CONFIG_DUMMY_COUNTER)  += dummy-counter.o
 obj-$(CONFIG_STM32_LPTIMER_CNT)      += stm32-lptimer-cnt.o
drivers/iio/counter/dummy-counter.c new file +36 -0
new file mode 100644
index 000000000000..6ecc9854894f
@@ -0,0 +1,293 @@
+/*
+ * Dummy counter driver
+ * Copyright (C) 2017 William Breathitt Gray
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/iio/counter.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+#define DUMCNT_NUM_COUNTERS 2
+/**
+ * struct dumcnt - private data structure
+ * @counter: instance of the iio_counter
+ * @counts:  array of accumulation values
+ * @states:  array of input line states
+ */
+struct dumcnt {
+     struct iio_counter counter;
+     unsigned int counts[DUMCNT_NUM_COUNTERS];
+     unsigned int states[DUMCNT_NUM_COUNTERS];
+};
+
+static int dumcnt_signal_read(struct iio_counter *counter,
+     struct iio_counter_signal *signal, int *val, int *val2)

drivers/iio/counter/dummy-counter.c line 46

Quoted 10 lines
drivers/iio/counter/dummy-counter.c new file +10 -0
+{
+     struct dumcnt *const priv = counter->driver_data;
+     *val = priv->states[signal->id];
+
+     return IIO_VAL_INT;
+}
+
+static int dumcnt_signal_write(struct iio_counter *counter,
+     struct iio_counter_signal *signal, int val, int val2)
+{

This is an odd one to have in the generic interface. What real hardware does writing the state make sense for? If it is only fake drivers - figure out a way to do it without having to add to the generic interfaces.

Quoted 92 lines
drivers/iio/counter/dummy-counter.c new file +92 -0
+     struct dumcnt *const priv = counter->driver_data;
+     const unsigned int id = signal->id;
+     const unsigned int prev_state = priv->states[id];
+     struct iio_counter_value *const value = counter->values + id;
+     const unsigned int function_mode = value->mode;
+     const unsigned int trigger_mode = value->triggers[0].mode;
+     unsigned int triggered = 0;
+
+     if (val && val != 1)
+             return -EINVAL;
+
+     /* If no state change then just exit */
+     if (prev_state == val)
+             return 0;
+
+     priv->states[id] = val;
+
+     switch (trigger_mode) {
+     /* "none" case */
+     case 0:
+             return 0;
+     /* "rising edge" case */
+     case 1:
+             if (!prev_state)
+                     triggered = 1;
+             break;
+     /* "falling edge" case */
+     case 2:
+             if (prev_state)
+                     triggered = 1;
+             break;
+     /* "both edges" case */
+     case 3:
+             triggered = 1;
+             break;
+     }
+
+     /* If counter function triggered */
+     if (triggered)
+             /* "increase" case */
+             if (function_mode)
+                     priv->counts[id]++;
+             /* "decrease" case */
+             else
+                     priv->counts[id]--;
+
+     return 0;
+}
+
+static int dumcnt_trigger_mode_set(struct iio_counter *counter,
+     struct iio_counter_value *value, struct iio_counter_trigger *trigger,
+     unsigned int mode)
+{
+     if (mode >= trigger->num_trigger_modes)
+             return -EINVAL;
+
+     trigger->mode = mode;
+
+     return 0;
+}
+
+static int dumcnt_trigger_mode_get(struct iio_counter *counter,
+     struct iio_counter_value *value, struct iio_counter_trigger *trigger)
+{
+     return trigger->mode;
+}
+
+static int dumcnt_value_read(struct iio_counter *counter,
+     struct iio_counter_value *value, int *val, int *val2)
+{
+     struct dumcnt *const priv = counter->driver_data;
+
+     *val = priv->counts[value->id];
+
+     return IIO_VAL_INT;
+}
+
+static int dumcnt_value_write(struct iio_counter *counter,
+     struct iio_counter_value *value, int val, int val2)
+{
+     struct dumcnt *const priv = counter->driver_data;
+
+     priv->counts[value->id] = val;
+
+     return 0;
+}
+
+static int dumcnt_value_function_set(struct iio_counter *counter,
+     struct iio_counter_value *value, unsigned int mode)
+{
+     if (mode >= value->num_function_modes)
+             return -EINVAL;

drivers/iio/counter/dummy-counter.c line 148

Quoted 10 lines
drivers/iio/counter/dummy-counter.c new file +10 -0
+
+     value->mode = mode;
+
+     return 0;
+}
+
+static int dumcnt_value_function_get(struct iio_counter *counter,
+     struct iio_counter_value *value)
+{
+     return value->mode;

If it's called function in the function name, call it function in the structure as well rather than mode.

Quoted 5 lines
drivers/iio/counter/dummy-counter.c new file +5 -0
+}
+
+static const struct iio_counter_ops dumcnt_ops = {
+     .signal_read = dumcnt_signal_read,
+     .signal_write = dumcnt_signal_write,

drivers/iio/counter/dummy-counter.c line 163

Quoted 10 lines
drivers/iio/counter/dummy-counter.c new file +10 -0
+     .trigger_mode_get = dumcnt_trigger_mode_get,
+     .trigger_mode_set = dumcnt_trigger_mode_set,
+     .value_read = dumcnt_value_read,
+     .value_write = dumcnt_value_write,
+     .value_function_set = dumcnt_value_function_set,
+     .value_function_get = dumcnt_value_function_get
+};
+
+static const char *const dumcnt_function_modes[] = {
+     "decrease",

I think increment was used somewhere in the docs... It's clearer, but you need to document this ABI to stop having subtle variations of it like this (even if I imagined it ;)

Quoted 15 lines
drivers/iio/counter/dummy-counter.c new file +15 -0
+     "increase"
+};
+
+#define DUMCNT_SIGNAL(_id, _name) {  \
+     .id = _id,                      \
+     .name = _name                   \
+}
+
+static const struct iio_counter_signal dumcnt_signals[] = {
+     DUMCNT_SIGNAL(0, "Signal A"), DUMCNT_SIGNAL(1, "Signal B")
+};
+
+#define DUMCNT_VALUE(_id, _name) {                                   \
+     .id = _id,                                                      \
+     .name = _name,                                                  \

drivers/iio/counter/dummy-counter.c line 188

Quoted 10 lines
drivers/iio/counter/dummy-counter.c new file +10 -0
+     .mode = 0,                                                      \
+     .function_modes = dumcnt_function_modes,                        \
+     .num_function_modes = ARRAY_SIZE(dumcnt_function_modes)         \
+}
+
+static const struct iio_counter_value dumcnt_values[] = {
+     DUMCNT_VALUE(0, "Count A"), DUMCNT_VALUE(1, "Count B")
+};
+
+static const char *const dumcnt_trigger_modes[] = {

As mentioned below, use an enum for the index as then you can make it obvious what 0 means when you set the mode to it later.

drivers/iio/counter/dummy-counter.c line 199

Quoted 11 lines
drivers/iio/counter/dummy-counter.c new file +11 -0
+     "none",
+     "rising edge",
+     "falling edge",
+     "both edges"
+};
+
+static int dumcnt_probe(struct platform_device *pdev)
+{
+     struct device *dev = &pdev->dev;
+     struct iio_counter_signal *signals;
+     const size_t num_signals = ARRAY_SIZE(dumcnt_signals);

Don't bother with local variable - makes it less obvious what is going on.

drivers/iio/counter/dummy-counter.c line 201

Quoted 2 lines
drivers/iio/counter/dummy-counter.c new file +2 -0
+     struct iio_counter_value *values;
+     const size_t num_values = ARRAY_SIZE(dumcnt_values);

Local variable doesn't add anything and if anything makes it slightly harder to check what is going on.

drivers/iio/counter/dummy-counter.c line 210

Quoted 9 lines
drivers/iio/counter/dummy-counter.c new file +9 -0
+     struct iio_counter_trigger *triggers;
+     int i;
+     struct dumcnt *dumcnt;
+
+     signals = devm_kmalloc(dev, sizeof(dumcnt_signals), GFP_KERNEL);
+     if (!signals)
+             return -ENOMEM;
+
+     memcpy(signals, dumcnt_signals, sizeof(dumcnt_signals));

devm_kmemdup?

drivers/iio/counter/dummy-counter.c line 216

Quoted 6 lines
drivers/iio/counter/dummy-counter.c new file +6 -0
+
+     values = devm_kmalloc(dev, sizeof(dumcnt_values), GFP_KERNEL);
+     if (!values)
+             return -ENOMEM;
+
+     memcpy(values, dumcnt_values, sizeof(dumcnt_values));

devm_kmemdup?

drivers/iio/counter/dummy-counter.c line 224

Quoted 8 lines
drivers/iio/counter/dummy-counter.c new file +8 -0
+
+     /* Associate values with their respective signals */
+     for (i = 0; i < num_values; i++) {
+             triggers = devm_kmalloc(dev, sizeof(*triggers), GFP_KERNEL);
+             if (!triggers)
+                     return -ENOMEM;
+
+             triggers->mode = 0;

Use an enum for the dumcn_trigger_modes array index then specify by enum value here. Will make it more readable.

Quoted 15 lines
drivers/iio/counter/dummy-counter.c new file +15 -0
+             triggers->trigger_modes = dumcnt_trigger_modes;
+             triggers->num_trigger_modes = ARRAY_SIZE(dumcnt_trigger_modes);
+             triggers->signal = &signals[i];
+
+             values[i].triggers = triggers;
+             values[i].num_triggers = 1;
+     }
+
+     dumcnt = devm_kzalloc(dev, sizeof(*dumcnt), GFP_KERNEL);
+     if (!dumcnt)
+             return -ENOMEM;
+
+     dumcnt->counter.name = dev_name(dev);
+     dumcnt->counter.dev = dev;
+     dumcnt->counter.ops = &dumcnt_ops;

drivers/iio/counter/dummy-counter.c line 249

Quoted 10 lines
drivers/iio/counter/dummy-counter.c new file +10 -0
+     dumcnt->counter.signals = signals;
+     dumcnt->counter.num_signals = num_signals;
+     dumcnt->counter.values = values;
+     dumcnt->counter.num_values = num_values;
+     dumcnt->counter.driver_data = dumcnt;
+
+     return devm_iio_counter_register(dev, &dumcnt->counter);
+}
+
+static struct platform_device *dumcnt_device;

Support multiple instances - nick this stuff from the IIO dummy driver or more specifically the industrialio-sw-device.c

drivers/iio/counter/dummy-counter.c line 253

Quoted 4 lines
drivers/iio/counter/dummy-counter.c new file +4 -0
+
+static struct platform_driver dumcnt_driver = {
+     .driver = {
+             .name = "104-quad-8"

Don't do that! Give it it's own name.

drivers/iio/counter/dummy-counter.c line 266

Quoted 13 lines
drivers/iio/counter/dummy-counter.c new file +13 -0
+     }
+};
+
+static void __exit dumcnt_exit(void)
+{
+     platform_device_unregister(dumcnt_device);
+     platform_driver_unregister(&dumcnt_driver);
+}
+
+static int __init dumcnt_init(void)
+{
+     int err;
+

General thing, but if we are going to upstream this with the subsystem, make device instantiation happen via configfs.

Quoted 27 lines
drivers/iio/counter/dummy-counter.c new file +27 -0
+     dumcnt_device = platform_device_alloc(dumcnt_driver.driver.name, -1);
+     if (!dumcnt_device)
+             return -ENOMEM;
+
+     err = platform_device_add(dumcnt_device);
+     if (err)
+             goto err_platform_device;
+
+     err = platform_driver_probe(&dumcnt_driver, dumcnt_probe);
+     if (err)
+             goto err_platform_driver;
+
+     return 0;
+
+err_platform_driver:
+     platform_device_del(dumcnt_device);
+err_platform_device:
+     platform_device_put(dumcnt_device);
+     return err;
+}
+
+module_init(dumcnt_init);
+module_exit(dumcnt_exit);
+
+MODULE_AUTHOR("William Breathitt Gray <[email protected]>");
+MODULE_DESCRIPTION("Dummy counter driver");
+MODULE_LICENSE("GPL v2");
#5 lore raw
In reply to Jonathan Cameron

2017-10-08 16:30 GMT+02:00 Jonathan Cameron <[email protected]>:

Quoted 62 lines
Quoted 6 lines · On Thu, 5 Oct 2017 14:13:44 -0400 William Breathitt Gray…

This patch introduces the IIO generic counter interface for supporting counter devices. The generic counter interface serves as a catch-all to enable rudimentary support for devices that qualify as counters. More specific and apt counter interfaces may be developed on top of the generic counter interface, and those interfaces should be used by drivers when possible rather than the generic counter interface.

I think you probably want to avoid any additional layering and try to push things down here (maybe with a few utility functions to simplify common cases), but that's a question for another day!

Quoted 34 lines

In the context of the IIO generic counter interface, a counter is defined as a device that reports one or more "counter values" based on the state changes of one or more "counter signals" as evaluated by a defined "counter function."

The IIO generic counter interface piggybacks off of the IIO core. This is primarily used to leverage the existing sysfs setup: the IIO_COUNT channel attributes represent the Counter Value, while the IIO_SIGNAL channel attributes represent the Counter Signal; auxilary IIO_COUNT attributes represent the Counter Signal connections (Triggers) and their respective state change configurations which trigger an associated "counter function" evaluation.

The iio_counter_ops structure serves as a container for driver callbacks to communicate with the device; function callbacks are provided to read and write various Signals and Values, and set and get the "trigger mode" and "function mode" for various Triggers and Values respectively.

To support a counter device, a driver must first allocate the available Counter Signals via iio_counter_signal structures. These Signals should be stored as an array and set to the signals array member of an allocated iio_counter structure before the Counter is registered to the system.

Counter Values may be allocated via iio_counter_value structures, and respective Counter Signal associations (Triggers) made via iio_counter_trigger structures. Associated iio_counter_trigger structures are stored as an array and set to the the triggers array member of the respective iio_counter_value structure. These iio_counter_value structures are set to the values array member of an allocated iio_counter structure before the Counter is registered to the system.

Quoted 7 lines

A counter device is registered to the system by passing the respective initialized iio_counter structure to the iio_counter_register function; similarly, the iio_counter_unregister function unregisters the respective counter. The devm_iio_counter_register and iio_devm_iio_counter_unregister functions serve as device memory-managed versions of the iio_counter_register and iio_counter unregister functions respectively.

All the find by ids are the main complexity introduced by layering this on IIO that you probably wouldn't have if it wasn't so layered. Hmm. I suggest inline that you could allow drivers to provide fast versions of that search given they can probably infer it directly from the index. Might be something to ignore for now though in the interests of initial simplicity.

Quoted 8 lines

Otherwise a few comments inline, but mostly this is coming together pretty well. I'm actually surprised the layering on top of IIO didn't end up more painful than it did. It's not horrendous - which is not to say you wouldn't be better breaking free of that entirely... (I'm not sure either way).

I think the big remaining questions are around naming more than anything else. I don't like the name value when a user will think they are lookin=

g

Quoted 1 line

for a counter. If you use counter though the current use of counter need=

s

Quoted 3 lines

to change.

Trigger is also, as has been raised, a rather overloaded term. Not totall=

y

Quoted 13 lines

obvious what a better term is but we shouldn't reuse that one...

I'll try and think of something if you have no luck!

Jonathan

Quoted 8 lines
---
 MAINTAINERS                        |   7 +
 drivers/iio/Kconfig                |   8 +
 drivers/iio/Makefile               |   1 +
 drivers/iio/counter/Kconfig        |   1 +

drivers/iio/industrialio-counter.c | 900 ++++++++++++++++++++++++++++++=

+++++++

Quoted 10 lines

MAINTAINERS F: Documentation/ABI/testing/sysfs-bus-iio-ad=

Quoted 10 lines
 include/linux/iio/counter.h        | 166 +++++++
 6 files changed, 1083 insertions(+)
 create mode 100644 drivers/iio/industrialio-counter.c
 create mode 100644 include/linux/iio/counter.h
MAINTAINERS +0 -0
index 2281af4b41b6..8b7c37bed252 100644
@@ -6693,6 +6693,13 @@ F:     Documentation/ABI/testing/sysfs-bus-iio-ad=

c-envelope-detector

Quoted 54 lines

MAINTAINERS F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector · line 6701

Quoted 9 lines
MAINTAINERS +6 -0
 F:   Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
 F:   drivers/iio/adc/envelope-detector.c
 
+IIO GENERIC COUNTER INTERFACE
+M:   William Breathitt Gray <[email protected]>
+L:   [email protected]
+S:   Maintained
+F:   drivers/iio/industrialio-counter.c
+F:   include/linux/iio/counter.h

Don't forget the directory the drivers are going in ;) *laughs manically*

Quoted 42 lines
MAINTAINERS +1 -0
+
 IIO MULTIPLEXER
 M:   Peter Rosin <[email protected]>
 L:   [email protected]
drivers/iio/Kconfig +8 -0
index b3c8c6ef0dff..78e01f4f5937 100644
@@ -30,6 +30,14 @@ config IIO_CONFIGFS
        (e.g. software triggers). For more info see
        Documentation/iio/iio_configfs.txt.
 
+config IIO_COUNTER
+     bool "Enable IIO counter support"
+     help
+       Provides IIO core support for counters. This API provides
+       a generic interface that serves as the building blocks to
+       create more complex counter interfaces. Rudimentary support
+       for counters is enabled.
+
 config IIO_TRIGGER
      bool "Enable triggered sampling support"
      help
drivers/iio/Makefile +1 -0
index 93c769cd99bf..6427ff38f964 100644
@@ -5,6 +5,7 @@
 obj-$(CONFIG_IIO) +=3D industrialio.o
 industrialio-y :=3D industrialio-core.o industrialio-event.o inkern.o
 industrialio-$(CONFIG_IIO_BUFFER) +=3D industrialio-buffer.o
+industrialio-$(CONFIG_IIO_COUNTER) +=3D industrialio-counter.o
 industrialio-$(CONFIG_IIO_TRIGGER) +=3D industrialio-trigger.o
 
 obj-$(CONFIG_IIO_CONFIGFS) +=3D industrialio-configfs.o
drivers/iio/counter/Kconfig +0 -0
index 474e1ac4e7c0..c8becfe78e28 100644
@@ -4,6 +4,7 @@
 # When adding new entries keep the list in alphabetical order
 
Quoted 6 lines

drivers/iio/industrial=

Quoted 6 lines
drivers/iio/counter/Kconfig +1 -0
 menu "Counters"
+     depends on IIO_COUNTER
 
 config 104_QUAD_8
      tristate "ACCES 104-QUAD-8 driver"
drivers/iio/industrial= renamed from drivers/iio/industrialio-counter.c +0 -0

io-counter.c

Quoted 78 lines
Quoted 60 lines

new file mode 100644
index 000000000000..dfb982dae3a8

drivers/iio/industrialio-counter.c new file +55 -0
@@ -0,0 +1,900 @@
+/*
+ * Industrial I/O counter interface functions
+ * Copyright (C) 2017 William Breathitt Gray
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/printk.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+
+#include <linux/iio/counter.h>
+
+#define iio_priv_get_counter(_indio_dev) \
+     (*(struct iio_counter **)iio_priv(_indio_dev))
+
+static struct iio_counter_signal *iio_counter_signal_find_by_id(
+     const struct iio_counter *const counter, const int id)
+{
+     size_t i;
+     struct iio_counter_signal *signal;
+
+     for (i =3D 0; i < counter->num_signals; i++) {
+             signal =3D counter->signals + i;
+             if (signal->id =3D=3D id)
+                     return signal;
+     }
+
+     return NULL;
+}
+
+static struct iio_counter_trigger *iio_counter_trigger_find_by_id(
+     const struct iio_counter_value *const value, const int id)
+{
+     size_t i;
+     struct iio_counter_trigger *trigger;
+
+     for (i =3D 0; i < value->num_triggers; i++) {
+             trigger =3D value->triggers + i;
+             if (trigger->signal->id =3D=3D id)
+                     return trigger;

drivers/iio/industrialio-counter.c line 65

Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10 -0
+     }
+
+     return NULL;
+}
+
+static struct iio_counter_value *iio_counter_value_find_by_id(
+     const struct iio_counter *const counter, const int id)
+{
+     size_t i;
+     struct iio_counter_value *value;

In most cases this mapping will be entirely obvious to the driver - perhaps provide an optional callback to let it provide it directly without any searching?

We could always add this later though if we start getting drivers with lots of instances of the various parts...

Quoted 14 lines
Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4 -0
+
+     for (i =3D 0; i < counter->num_values; i++) {
+             value =3D counter->values + i;
+             if (value->id =3D=3D id)

drivers/iio/industrialio-counter.c

Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10 -0
+                     return value;
+     }
+
+     return NULL;
+}
+
+static ssize_t iio_counter_signal_name_read(struct iio_dev *indio_dev,
+     uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
+{
+     struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=

ev);

Quoted 5 lines
Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5 -0
+     const struct iio_counter_signal *signal;
+
+     signal =3D iio_counter_signal_find_by_id(counter, chan->channel2);
+     if (!signal)
+             return -EINVAL;
Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+
+     return scnprintf(buf, PAGE_SIZE, "%s\n", signal->name);
+}
+
+static ssize_t iio_counter_value_name_read(struct iio_dev *indio_dev,
+     uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
+{
+     struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=

ev);

Quoted 12 lines

drivers/iio/industrialio-counter.c line 99

Quoted 7 lines
drivers/iio/industrialio-counter.c new file +7 -0
+     const struct iio_counter_value *value;
+
+     value =3D iio_counter_value_find_by_id(counter, chan->channel2);
+     if (!value)
+             return -EINVAL;
+
+     return scnprintf(buf, PAGE_SIZE, "%s\n", value->name);

I mentioned before but I wonder if we can just push name down into the chan spec itself in some fashion. Whether to associate it with existing datasheet_name or not is an open question however.

Quoted 3 lines

drivers/iio/industrialio-counter.c

Quoted 3 lines
drivers/iio/industrialio-counter.c new file +3 -0
+}
+
+static ssize_t iio_counter_value_triggers_read(struct iio_dev *indio_de=

v,

Quoted 3 lines

drivers/iio/industrialio-counter.c

Quoted 3 lines
drivers/iio/industrialio-counter.c new file +3 -0
+     uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
+{
+     struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=

ev);

Quoted 5 lines
Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5 -0
+     struct iio_counter_value *value;
+     size_t i;
+     const struct iio_counter_trigger *trigger;
+     ssize_t len =3D 0;
+
Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+     value =3D iio_counter_value_find_by_id(counter, chan->channel2);
+     if (!value)
+             return -EINVAL;
+
+     /* Print out a list of every Signal association to Value */
+     for (i =3D 0; i < value->num_triggers; i++) {
+             trigger =3D value->triggers + i;
+             len +=3D snprintf(buf + len, PAGE_SIZE - len, "%d\t%s\t%s\=

n",

Quoted 5 lines
Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5 -0
+                     trigger->signal->id, trigger->signal->name,
+                     trigger->trigger_modes[trigger->mode]);
+             if (len >=3D PAGE_SIZE)
+                     return -ENOMEM;
+     }
Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+
+     return len;
+}
+
+static ssize_t iio_counter_trigger_mode_read(struct iio_dev *indio_dev,
+     uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
+{
+     struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=

ev);

Quoted 18 lines
Quoted 18 lines
drivers/iio/industrialio-counter.c new file +18 -0
+     struct iio_counter_value *value;
+     struct iio_counter_trigger *trigger;
+     const int signal_id =3D *(int *)((void *)priv);
+     int mode;
+
+     if (!counter->ops->trigger_mode_get)
+             return -EINVAL;
+
+     value =3D iio_counter_value_find_by_id(counter, chan->channel2);
+     if (!value)
+             return -EINVAL;
+
+     trigger =3D iio_counter_trigger_find_by_id(value, signal_id);
+     if (!trigger)
+             return -EINVAL;
+
+     mode =3D counter->ops->trigger_mode_get(counter, value, trigger);
+
Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+     if (mode < 0)
+             return mode;
+     else if (mode >=3D trigger->num_trigger_modes)
+             return -EINVAL;
+
+     trigger->mode =3D mode;
+
+     return scnprintf(buf, PAGE_SIZE, "%s\n", trigger->trigger_modes[mo=

de]);

Quoted 3 lines

drivers/iio/industrialio-counter.c

Quoted 3 lines
drivers/iio/industrialio-counter.c new file +3 -0
+}
+
+static ssize_t iio_counter_trigger_mode_write(struct iio_dev *indio_dev=

,

Quoted 4 lines

drivers/iio/industrialio-counter.c

Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4 -0
+     uintptr_t priv, const struct iio_chan_spec *chan, const char *buf,
+     size_t len)
+{
+     struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=

ev);

Quoted 17 lines
Quoted 17 lines
drivers/iio/industrialio-counter.c new file +17 -0
+     struct iio_counter_value *value;
+     ssize_t err;
+     struct iio_counter_trigger *trigger;
+     const int signal_id =3D *(int *)((void *)priv);
+     unsigned int mode;
+
+     if (!counter->ops->trigger_mode_set)
+             return -EINVAL;
+
+     value =3D iio_counter_value_find_by_id(counter, chan->channel2);
+     if (!value)
+             return -EINVAL;
+
+     trigger =3D iio_counter_trigger_find_by_id(value, signal_id);
+     if (!trigger)
+             return -EINVAL;
+
Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+     for (mode =3D 0; mode < trigger->num_trigger_modes; mode++)
+             if (sysfs_streq(buf, trigger->trigger_modes[mode]))
+                     break;
+
+     if (mode >=3D trigger->num_trigger_modes)
+             return -EINVAL;
+
+     err =3D counter->ops->trigger_mode_set(counter, value, trigger, mo=

de);

Quoted 5 lines
Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5 -0
+     if (err)
+             return err;
+
+     trigger->mode =3D mode;
+
Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+     return len;
+}
+
+static ssize_t iio_counter_trigger_mode_available_read(
+     struct iio_dev *indio_dev, uintptr_t priv,
+     const struct iio_chan_spec *chan, char *buf)
+{
+     struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=

ev);

Quoted 19 lines
Quoted 19 lines
drivers/iio/industrialio-counter.c new file +19 -0
+     struct iio_counter_value *value;
+     ssize_t len =3D 0;
+     struct iio_counter_trigger *trigger;
+     const int signal_id =3D *(int *)((void *)priv);
+     unsigned int i;
+
+     value =3D iio_counter_value_find_by_id(counter, chan->channel2);
+     if (!value)
+             return -EINVAL;
+
+     trigger =3D iio_counter_trigger_find_by_id(value, signal_id);
+     if (!trigger)
+             return -EINVAL;
+
+     for (i =3D 0; i < trigger->num_trigger_modes; i++)
+             len +=3D scnprintf(buf + len, PAGE_SIZE - len, "%s ",
+                     trigger->trigger_modes[i]);
+
+     buf[len - 1] =3D '\n';
Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+
+     return len;
+}
+
+static int iio_counter_value_function_set(struct iio_dev *indio_dev,
+     const struct iio_chan_spec *chan, unsigned int mode)
+{
+     struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=

ev);

Quoted 4 lines
Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4 -0
+     struct iio_counter_value *value;
+     int err;
+
+     if (!counter->ops->value_function_set)
Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+             return -EINVAL;
+
+     /* Find relevant Value */
+     value =3D iio_counter_value_find_by_id(counter, chan->channel2);
+     if (!value)
+             return -EINVAL;
+
+     /* Map IIO core function_set to Generic Counter value_function_set=

*/

Quoted 5 lines
Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5 -0
+     err =3D counter->ops->value_function_set(counter, value, mode);
+     if (err)
+             return err;
+
+     value->mode =3D mode;
Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+
+     return 0;
+}
+
+static int iio_counter_value_function_get(struct iio_dev *indio_dev,
+     const struct iio_chan_spec *chan)
+{
+     struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=

ev);

Quoted 17 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+     struct iio_counter_value *value;
+     int retval;
+
+     if (!counter->ops->value_function_get)
+             return -EINVAL;
+
+     /* Find relevant Value */
+     value =3D iio_counter_value_find_by_id(counter, chan->channel2);

Same argument as below - would the driver not be able to do this better? Often it would know a simple transform to get the right one...

Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4 -0
+     if (!value)
+             return -EINVAL;
+
+     /* Map IIO core function_get to Generic Counter value_function_get=

*/

Quoted 4 lines
Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4 -0
+     retval =3D counter->ops->value_function_get(counter, value);
+     if (retval < 0)
+             return retval;
+     else if (retval >=3D value->num_function_modes)
Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+             return -EINVAL;
+
+     value->mode =3D retval;
+
+     return retval;
+}
+
+static int iio_counter_value_ext_info_alloc(struct iio_chan_spec *const=

chan,

Quoted 26 lines
Quoted 26 lines
drivers/iio/industrialio-counter.c new file +26 -0
+     struct iio_counter_value *const value)
+{
+     const struct iio_chan_spec_ext_info ext_info_default[] =3D {
+             {
+                     .name =3D "name",
+                     .shared =3D IIO_SEPARATE,
+                     .read =3D iio_counter_value_name_read
+             },
+             IIO_ENUM("function", IIO_SEPARATE, &value->function_enum),
+             {
+                     .name =3D "function_available",
+                     .shared =3D IIO_SEPARATE,
+                     .read =3D iio_enum_available_read,
+                     .private =3D (void *)&value->function_enum
+             },
+             {
+                     .name =3D "triggers",
+                     .shared =3D IIO_SEPARATE,
+                     .read =3D iio_counter_value_triggers_read
+             }
+     };
+     const size_t num_default =3D ARRAY_SIZE(ext_info_default);
+     const struct iio_chan_spec_ext_info ext_info_trigger[] =3D {
+             {
+                     .shared =3D IIO_SEPARATE,
+                     .read =3D iio_counter_trigger_mode_read,
Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+                     .write =3D iio_counter_trigger_mode_write
+             },
+             {
+                     .shared =3D IIO_SEPARATE,
+                     .read =3D iio_counter_trigger_mode_available_read
+             }
+     };
+     const size_t num_ext_info_trigger =3D ARRAY_SIZE(ext_info_trigger)=

;

Quoted 3 lines

drivers/iio/industrialio-counter.c line 315

Quoted 3 lines
drivers/iio/industrialio-counter.c new file +3 -0
+     const size_t num_triggers_ext_info =3D num_ext_info_trigger *
+             value->num_triggers;
+     const size_t num_ext_info =3D num_default + num_triggers_ext_info =

+ 1;

Quoted 5 lines
Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5 -0
+     int err;
+     struct iio_chan_spec_ext_info *ext_info;
+     const struct iio_counter_trigger *trigger;
+     size_t i;
+     size_t j;
Quoted 8 lines

drivers/iio/industrialio-counter.c line 328

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+
+     /* Construct function_enum for Value */
+     value->function_enum.items =3D value->function_modes;
+     value->function_enum.num_items =3D value->num_function_modes;
+     value->function_enum.set =3D iio_counter_value_function_set;
+     value->function_enum.get =3D iio_counter_value_function_get;
+
+     ext_info =3D kmalloc_array(num_ext_info, sizeof(*ext_info), GFP_KE=

RNEL);

Quoted 5 lines

drivers/iio/industrialio-counter.c

Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5 -0
+     if (!ext_info)
+             return -ENOMEM;
+     ext_info[num_ext_info - 1].name =3D NULL;
+
+     /* Add ext_info for the name, function, function_available, and tr=

iggers

Quoted 7 lines

drivers/iio/industrialio-counter.c

Quoted 7 lines
drivers/iio/industrialio-counter.c new file +7 -0
+      * attributes
+      */
+     memcpy(ext_info, ext_info_default, sizeof(ext_info_default));
+     /* Add ext_info for the trigger_signalX-Z and
+      * trigger_signalX-Z_available attributes for each Trigger
+      */
+     for (i =3D 0; i < num_triggers_ext_info; i +=3D num_ext_info_trigg=

er)

Quoted 4 lines

drivers/iio/industrialio-counter.c

Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4 -0
+             memcpy(ext_info + num_default + i, ext_info_trigger,
+                     sizeof(ext_info_trigger));
+
+     /* Set name for each trigger_signalX-Z and trigger_signalX-Z_avail=

able

Quoted 1 line

drivers/iio/industrialio-counter.c line 345

Quoted 1 line
drivers/iio/industrialio-counter.c new file +1 -0
+      * attribute; store the respective Signal ID address in each ext_i=

nfo

Quoted 3 lines

drivers/iio/industrialio-counter.c

Quoted 3 lines
drivers/iio/industrialio-counter.c new file +3 -0
+      * private member
+      */
+     for (i =3D num_default, j =3D 0; j < value->num_triggers; i++, j++=

) {

Quoted 3 lines

drivers/iio/industrialio-counter.c line 351

Quoted 3 lines
drivers/iio/industrialio-counter.c new file +3 -0
+             trigger =3D value->triggers + j;
+
+             ext_info[i].name =3D kasprintf(GFP_KERNEL, "trigger_signal=

%d-%d",

Quoted 30 lines
Quoted 30 lines
drivers/iio/industrialio-counter.c new file +30 -0
+                     chan->channel, trigger->signal->id);
+             if (!ext_info[i].name) {
+                     err =3D -ENOMEM;
+                     goto err_name_alloc;
+             }
+             ext_info[i].private =3D (void *)&trigger->signal->id;
+             i++;
+
+             ext_info[i].name =3D kasprintf(GFP_KERNEL,
+                     "trigger_signal%d-%d_available",
+                     chan->channel, trigger->signal->id);
+             if (!ext_info[i].name) {
+                     err =3D -ENOMEM;
+                     goto err_name_alloc;
+             }
+             ext_info[i].private =3D (void *)&trigger->signal->id;
+     }
+
+     chan->ext_info =3D ext_info;
+
+     return 0;
+
+err_name_alloc:
+     while (i-- > num_default)
+             kfree(ext_info[i].name);
+     kfree(ext_info);
+     return err;
+}
+
+static void iio_counter_value_ext_info_free(
Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+     const struct iio_chan_spec *const channel)
+{
+     size_t i;
+     const char *const prefix =3D "trigger_signal";
+     const size_t prefix_len =3D strlen(prefix);
+
+     for (i =3D 0; channel->ext_info[i].name; i++)
+             if (!strncmp(channel->ext_info[i].name, prefix, prefix_len=

))

Quoted 5 lines

drivers/iio/industrialio-counter.c

Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5 -0
+                     kfree(channel->ext_info[i].name);
+     kfree(channel->ext_info);
+}
+
+static const struct iio_chan_spec_ext_info iio_counter_signal_ext_info[=

] =3D {

Quoted 9 lines

drivers/iio/industrialio-counter.c

Quoted 9 lines
drivers/iio/industrialio-counter.c new file +9 -0
+     {
+             .name =3D "name",
+             .shared =3D IIO_SEPARATE,
+             .read =3D iio_counter_signal_name_read
+     },
+     {}
+};
+
+static int iio_counter_channels_alloc(struct iio_counter *const counter=

)

Quoted 2 lines

drivers/iio/industrialio-counter.c line 405

Quoted 2 lines
drivers/iio/industrialio-counter.c new file +2 -0
+{
+     const size_t num_channels =3D counter->num_signals + counter->num_=

values +

Quoted 27 lines

drivers/iio/industrialio-counter.c

Quoted 9 lines
drivers/iio/industrialio-counter.c new file +9 -0
+             counter->num_channels;
+     int err;
+     struct iio_chan_spec *channels;
+     size_t j;
+     struct iio_counter_value *value;
+     size_t i =3D counter->num_channels;
+     const struct iio_counter_signal *signal;
+
+     channels =3D kcalloc(num_channels, sizeof(*channels), GFP_KERNEL);

Bring the calculation of num_channels down here - it will make it more obvious that this allows space for both sources of channel.

Quoted 14 lines
drivers/iio/industrialio-counter.c new file +14 -0
+     if (!channels)
+             return -ENOMEM;
+
+     /* If any channels were supplied on Counter registration,
+      * we add them here to the front of the array
+      */
+     memcpy(channels, counter->channels,
+             counter->num_channels * sizeof(*counter->channels));
+
+     /* Add channel for each Value */
+     for (j =3D 0; j < counter->num_values; j++) {
+             value =3D counter->values + j;
+
+             channels[i].type =3D IIO_COUNT;
Quoted 8 lines

drivers/iio/industrialio-counter.c line 435

Quoted 7 lines
drivers/iio/industrialio-counter.c new file +7 -0
+             channels[i].channel =3D counter->id;
+             channels[i].channel2 =3D value->id;
+             channels[i].info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW);
+             channels[i].indexed =3D 1;
+             channels[i].counter =3D 1;
+
+             /* Add channels for Triggers associated with Value */

Add channels? 'i' would need incrementing. Also that's not what this f=

unction

Quoted 2 lines

is doing that I can see...

Quoted 1 line
drivers/iio/industrialio-counter.c new file +1 -0
+             err =3D iio_counter_value_ext_info_alloc(channels + i, val=

ue);

Quoted 26 lines
Quoted 26 lines
drivers/iio/industrialio-counter.c new file +26 -0
+             if (err)
+                     goto err_value_ext_info_alloc;
+
+             i++;
+     }
+
+     /* Add channel for each Signal */
+     for (j =3D 0; j < counter->num_signals; j++) {
+             signal =3D counter->signals + j;
+
+             channels[i].type =3D IIO_SIGNAL;
+             channels[i].channel =3D counter->id;
+             channels[i].channel2 =3D signal->id;
+             channels[i].info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW);
+             channels[i].indexed =3D 1;
+             channels[i].counter =3D 1;
+             channels[i].ext_info =3D iio_counter_signal_ext_info;
+
+             i++;
+     }
+
+     counter->indio_dev->num_channels =3D num_channels;
+     counter->indio_dev->channels =3D channels;
+
+     return 0;
+
Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+err_value_ext_info_alloc:
+     while (i-- > counter->num_channels)
+             iio_counter_value_ext_info_free(channels + i);
+     kfree(channels);
+     return err;
+}
+
+static void iio_counter_channels_free(const struct iio_counter *const c=

ounter)

Quoted 2 lines

drivers/iio/industrialio-counter.c line 472

Quoted 2 lines
drivers/iio/industrialio-counter.c new file +2 -0
+{
+     size_t i =3D counter->num_channels + counter->indio_dev->num_chann=

els;

Quoted 1 line

drivers/iio/industrialio-counter.c line 473

Quoted 1 line
drivers/iio/industrialio-counter.c new file +1 -0
+     const struct iio_chan_spec *const chans =3D counter->indio_dev->ch=

annels;

Quoted 5 lines
Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5 -0
+
+     while (i-- > counter->num_channels)
+             /* Only IIO_COUNT channels need to be freed here */
+             if (chans[i].type =3D=3D IIO_COUNT)
+                     iio_counter_value_ext_info_free(chans + i);
Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+
+     kfree(chans);
+}
+
+static int iio_counter_read_raw(struct iio_dev *indio_dev,
+     struct iio_chan_spec const *chan, int *val, int *val2, long mask)
+{
+     struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=

ev);

Quoted 5 lines
Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5 -0
+     struct iio_counter_signal *signal;
+     struct iio_counter_value *value;
+
+     if (mask !=3D IIO_CHAN_INFO_RAW)
+             return -EINVAL;
Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+
+     switch (chan->type) {
+     /* Map read_raw to signal_read for Signal */
+     case IIO_SIGNAL:
+             if (!counter->ops->signal_read)
+                     return -EINVAL;
+
+             signal =3D iio_counter_signal_find_by_id(counter, chan->ch=

annel2);

Quoted 4 lines

drivers/iio/industrialio-counter.c

Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4 -0
+             if (!signal)
+                     return -EINVAL;
+
+             return counter->ops->signal_read(counter, signal, val, val=

2);

Quoted 6 lines

drivers/iio/industrialio-counter.c

Quoted 6 lines
drivers/iio/industrialio-counter.c new file +6 -0
+     /* Map read_raw to value_read for Value */
+     case IIO_COUNT:
+             if (!counter->ops->value_read)
+                     return -EINVAL;
+
+             value =3D iio_counter_value_find_by_id(counter, chan->chan=

nel2);

Quoted 4 lines

drivers/iio/industrialio-counter.c

Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4 -0
+             if (!value)
+                     return -EINVAL;
+
+             return counter->ops->value_read(counter, value, val, val2)=

;

Quoted 4 lines

drivers/iio/industrialio-counter.c

Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4 -0
+     /* Map read_raw to read_raw for non-counter channel */
+     default:
+             if (counter->info && counter->info->read_raw)
+                     return counter->info->read_raw(indio_dev, chan, va=

l,

Quoted 10 lines

drivers/iio/industrialio-counter.c

Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10 -0
+                             val2, mask);
+     }
+
+     return -EINVAL;
+}
+
+static int iio_counter_write_raw(struct iio_dev *indio_dev,
+     struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+     struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=

ev);

Quoted 5 lines
Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5 -0
+     struct iio_counter_signal *signal;
+     struct iio_counter_value *value;
+
+     if (mask !=3D IIO_CHAN_INFO_RAW)
+             return -EINVAL;
Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+
+     switch (chan->type) {
+     /* Map write_raw to signal_write for Signal */
+     case IIO_SIGNAL:
+             if (!counter->ops->signal_write)
+                     return -EINVAL;
+
+             signal =3D iio_counter_signal_find_by_id(counter, chan->ch=

annel2);

Quoted 4 lines

drivers/iio/industrialio-counter.c line 542

Quoted 2 lines
drivers/iio/industrialio-counter.c new file +2 -0
+             if (!signal)
+                     return -EINVAL;

Hmm. have to search is certainly a little ugly. Particularly as =

the driver

Quoted 1 line

would be able to maintain this as a lookup. I'd be tempted to pa=

ss the

Quoted 1 line

signal id down into the callback rather than finding the signal i=

n the core.

Quoted 4 lines

Might turn out messier though... :)

Quoted 2 lines
drivers/iio/industrialio-counter.c new file +2 -0
+
+             return counter->ops->signal_write(counter, signal, val, va=

l2);

Quoted 6 lines

drivers/iio/industrialio-counter.c

Quoted 6 lines
drivers/iio/industrialio-counter.c new file +6 -0
+     /* Map write_raw to value_write for Value */
+     case IIO_COUNT:
+             if (!counter->ops->value_write)
+                     return -EINVAL;
+
+             value =3D iio_counter_value_find_by_id(counter, chan->chan=

nel2);

Quoted 2 lines

Again, this mapping is a simple lookup in the driver I think, perhaps pus=

h

Quoted 6 lines

it down there?

Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4 -0
+             if (!value)
+                     return -EINVAL;
+
+             return counter->ops->value_write(counter, value, val, val2=

);

Quoted 4 lines

drivers/iio/industrialio-counter.c

Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4 -0
+     /* Map write_raw to write_raw for non-counter channel */
+     default:
+             if (counter->info && counter->info->write_raw)
+                     return counter->info->write_raw(indio_dev, chan, v=

al,

Quoted 7 lines

drivers/iio/industrialio-counter.c

Quoted 7 lines
drivers/iio/industrialio-counter.c new file +7 -0
+                             val2, mask);
+     }
+
+     return -EINVAL;
+}
+
+static int iio_counter_signals_validate(const struct iio_counter *const=

counter)

Quoted 11 lines
Quoted 11 lines
drivers/iio/industrialio-counter.c new file +11 -0
+{
+     size_t i;
+     const struct iio_counter_signal *signal;
+     size_t j;
+     int curr_id;
+
+     /* At least one Signal must be defined */
+     if (!counter->num_signals || !counter->signals) {
+             pr_err("Counter '%d' Signals undefined\n", counter->id);
+             return -EINVAL;
+     }
Quoted 8 lines

drivers/iio/industrialio-counter.c line 584

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+
+     for (i =3D 0; i < counter->num_signals; i++) {
+             signal =3D counter->signals + i;
+             /* No two Signals may share the same ID */
+             for (j =3D 0; j < i; j++) {
+                     curr_id =3D counter->signals[j].id;
+                     if (curr_id =3D=3D signal->id) {
+                             pr_err("Duplicate Counter '%d' Signal '%d'=

\n",

Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+                                             counter->id, signal->id);
+                             return -EEXIST;
+                     }
+             }
+             for (j =3D i + 1; j < counter->num_signals; j++) {
+                     curr_id =3D counter->signals[j].id;
+                     if (curr_id =3D=3D signal->id) {
+                             pr_err("Duplicate Counter '%d' Signal '%d'=

\n",

Quoted 20 lines

drivers/iio/industrialio-counter.c line 588

Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4 -0
+                                             counter->id, signal->id);
+                             return -EEXIST;
+                     }
+             }

Same as below.

Quoted 13 lines
drivers/iio/industrialio-counter.c new file +13 -0
+     }
+
+     return 0;
+}
+
+static int iio_counter_triggers_validate(const int counter_id,
+     const struct iio_counter_value *const value)
+{
+     size_t i;
+     const struct iio_counter_trigger *trigger;
+     size_t j;
+     int curr_id;
+
Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+     /* At least one Trigger must be defined */
+     if (!value->num_triggers || !value->triggers) {
+             pr_err("Counter '%d' Value '%d' Triggers undefined\n",
+                     counter_id, value->id);
+             return -EINVAL;
+     }
+
+     /* Ensure all Triggers have a defined Signal; this prevents a poss=

ible

Quoted 1 line

drivers/iio/industrialio-counter.c line 618

Quoted 1 line
drivers/iio/industrialio-counter.c new file +1 -0
+      * NULL pointer dereference when later validating Trigger Signal I=

Ds

Quoted 7 lines

Fix comment syntax throughout...

Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4 -0
+      */
+     for (i =3D 0; i < value->num_triggers; i++)
+             if (!value->triggers[i].signal) {
+                     pr_err("Counter '%d' Trigger '%zu' Signal undefine=

d\n",

Quoted 4 lines
Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4 -0
+                             counter_id, i);
+                     return -EINVAL;
+             }
+
Quoted 8 lines

drivers/iio/industrialio-counter.c line 634

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+     /* Verify validity of each Trigger */
+     for (i =3D 0; i < value->num_triggers; i++) {
+             trigger =3D value->triggers + i;
+             /* No two Trigger Signals may share the same ID */
+             for (j =3D 0; j < i; j++) {
+                     curr_id =3D value->triggers[j].signal->id;
+                     if (curr_id =3D=3D trigger->signal->id) {
+                             pr_err("Signal '%d' is already linked to C=

ounter '%d' Value '%d'\n",

Quoted 9 lines

drivers/iio/industrialio-counter.c

Quoted 9 lines
drivers/iio/industrialio-counter.c new file +9 -0
+                                     trigger->signal->id, counter_id,
+                                     value->id);
+                             return -EEXIST;
+                     }
+             }
+             for (j =3D i + 1; j < value->num_triggers; j++) {
+                     curr_id =3D value->triggers[j].signal->id;
+                     if (curr_id =3D=3D trigger->signal->id) {
+                             pr_err("Signal '%d' is already linked to C=

ounter '%d' Value '%d'\n",

Quoted 6 lines

drivers/iio/industrialio-counter.c line 639

Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5 -0
+                                     trigger->signal->id, counter_id,
+                                     value->id);
+                             return -EEXIST;
+                     }
+             }

Again, one loop with the condition changed so it doesn't fault on the i =

=3D j case -- see below and

Quoted 5 lines

note I'm reviewing backwards...

Quoted 2 lines
drivers/iio/industrialio-counter.c new file +2 -0
+
+             /* At least one trigger mode must be defined for each Trig=

ger */

Quoted 1 line

drivers/iio/industrialio-counter.c line 651

Quoted 1 line
drivers/iio/industrialio-counter.c new file +1 -0
+             if (!trigger->num_trigger_modes || !trigger->trigger_modes=

) {

Quoted 1 line

drivers/iio/industrialio-counter.c line 652

Quoted 1 line
drivers/iio/industrialio-counter.c new file +1 -0
+                     pr_err("Counter '%d' Signal '%d' trigger modes und=

efined\n",

Quoted 9 lines

drivers/iio/industrialio-counter.c

Quoted 9 lines
drivers/iio/industrialio-counter.c new file +9 -0
+                             counter_id, trigger->signal->id);
+                     return -EINVAL;
+             }
+     }
+
+     return 0;
+}
+
+static int iio_counter_values_validate(const struct iio_counter *const =

counter)

Quoted 16 lines

drivers/iio/industrialio-counter.c line 662

Quoted 1 line
drivers/iio/industrialio-counter.c new file +1 -0
+{

Hmm. Pretty heavy weight checking. Ah well, up to you.

Quoted 12 lines
drivers/iio/industrialio-counter.c new file +12 -0
+     size_t i;
+     const struct iio_counter_value *value;
+     size_t j;
+     int curr_id;
+     int err;
+
+     /* At least one Value must be defined */
+     if (!counter->num_values || !counter->values) {
+             pr_err("Counter '%d' Values undefined\n", counter->id);
+             return -EINVAL;
+     }
+
Quoted 12 lines

drivers/iio/industrialio-counter.c line 679

Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5 -0
+     /* Verify validity of each Value */
+     for (i =3D 0; i < counter->num_values; i++) {
+             value =3D counter->values + i;
+             /* No two Values may share the same ID */
+             for (j =3D 0; j < i; j++) {

Single loop with a slightly change to condition
if ((i !=3D j) and (curr_id =3D=3D value->id))

Quoted 3 lines
drivers/iio/industrialio-counter.c new file +3 -0
+                     curr_id =3D counter->values[j].id;
+                     if (curr_id =3D=3D value->id) {
+                             pr_err("Duplicate Counter '%d' Value '%d'\=

n",

Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+                                     counter->id, value->id);
+                             return -EEXIST;
+                     }
+             }
+             for (j =3D i + 1; j < counter->num_values; j++) {
+                     curr_id =3D counter->values[j].id;
+                     if (curr_id =3D=3D value->id) {
+                             pr_err("Duplicate Counter '%d' Value '%d'\=

n",

Quoted 6 lines

drivers/iio/industrialio-counter.c

Quoted 6 lines
drivers/iio/industrialio-counter.c new file +6 -0
+                                     counter->id, value->id);
+                             return -EEXIST;
+                     }
+             }
+
+             /* At least one function mode must be defined for each Val=

ue */

Quoted 1 line

drivers/iio/industrialio-counter.c line 697

Quoted 1 line
drivers/iio/industrialio-counter.c new file +1 -0
+             if (!value->num_function_modes || !value->function_modes) =

{

Quoted 1 line

drivers/iio/industrialio-counter.c line 698

Quoted 1 line
drivers/iio/industrialio-counter.c new file +1 -0
+                     pr_err("Counter '%d' Value '%d' function modes und=

efined\n",

Quoted 10 lines
Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10 -0
+                             counter->id, value->id);
+                     return -EINVAL;
+             }
+
+             /* Verify the Triggers associated with each Value */
+             err =3D iio_counter_triggers_validate(counter->id, value);
+             if (err)
+                     return err;
+     }
+
Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+     return 0;
+}
+
+/**
+ * iio_counter_register - register Counter to the system
+ * @counter: pointer to IIO Counter to register
+ *
+ * This function piggybacks off of iio_device_register. First, the rele=

vant

Quoted 1 line

drivers/iio/industrialio-counter.c line 717

Quoted 1 line
drivers/iio/industrialio-counter.c new file +1 -0
+ * Counter members are validated; the signals and values members must b=

e defined

Quoted 1 line

drivers/iio/industrialio-counter.c line 718

Quoted 1 line
drivers/iio/industrialio-counter.c new file +1 -0
+ * and populated by valid Signal and Value structures respectively. Nex=

t, a

Quoted 1 line

drivers/iio/industrialio-counter.c line 719

Quoted 1 line
drivers/iio/industrialio-counter.c new file +1 -0
+ * struct iio_dev is allocated by a call to iio_device_alloc and initia=

lized for

Quoted 1 line

drivers/iio/industrialio-counter.c line 720

Quoted 1 line
drivers/iio/industrialio-counter.c new file +1 -0
+ * the Counter, IIO channels are allocated, the Counter address is stor=

ed as the

Quoted 17 lines
Quoted 17 lines
drivers/iio/industrialio-counter.c new file +17 -0
+ * private data, and finally iio_device_register is called.
+ */
+int iio_counter_register(struct iio_counter *const counter)
+{
+     const struct iio_info info_default =3D {
+             .read_raw =3D iio_counter_read_raw,
+             .write_raw =3D iio_counter_write_raw
+     };
+     int err;
+     struct iio_info *info;
+     struct iio_counter **priv;
+
+     if (!counter)
+             return -EINVAL;
+
+     /* Verify that Signals are valid and IDs do not conflict */
+     err =3D iio_counter_signals_validate(counter);
Quoted 42 lines

drivers/iio/industrialio-counter.c line 741

Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4 -0
+     if (err)
+             return err;
+
+     /* Verify that Values are valid and IDs do not conflict;

Fix multiline comment syntax to standard kernel syntax. /*
* Verify
*/

Quoted 34 lines
drivers/iio/industrialio-counter.c new file +33 -0
+      * Triggers for each Value are also verified for validity
+      */
+     err =3D iio_counter_values_validate(counter);
+     if (err)
+             return err;
+
+     counter->indio_dev =3D iio_device_alloc(sizeof(counter));
+     if (!counter->indio_dev)
+             return -ENOMEM;
+
+     info =3D kmalloc(sizeof(*info), GFP_KERNEL);
+     if (!info) {
+             err =3D -ENOMEM;
+             goto err_info_alloc;
+     }
+     /* If an iio_info has been supplied than we use that,
+      * otherwise we set all callbacks to NULL; iio_counter_read_raw
+      * and iio_counter_write_raw is used for read_raw and write_raw
+      * for either case in order to support counter functionality
+      * (supplied read_raw/write_raw will be called from within
+      * iio_counter_read_raw/iio_counter_write_raw for non-counter
+      * channels)
+      */
 
+     if (counter->info) {
+             memcpy(info, counter->info, sizeof(*counter->info));
+             info->read_raw =3D iio_counter_read_raw;
+             info->write_raw =3D iio_counter_write_raw;
+     } else {
+             memcpy(info, &info_default, sizeof(info_default));
+     }
+
+     counter->indio_dev->info =3D info;
+     counter->indio_dev->modes =3D INDIO_DIRECT_MODE;

As I have said in the previous version, stm32 driver will not work with this mode, so I need way to change it.
One solution could be to use iio_priv to store an iio_counter structure wit= h
ops, arrays etc... Maybe even iio_counter structure could be hiden from dri= vers.
It could require to split iio_counter allocation and registration to functi= ons.

Quoted 4 lines

drivers/iio/industrialio-counter.c

Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4 -0
+     counter->indio_dev->name =3D counter->name;
+     counter->indio_dev->dev.parent =3D counter->dev;
+
+     /* IIO channels are allocated and set for Signals, Values, and Tri=

ggers;

Quoted 25 lines
Quoted 25 lines
drivers/iio/industrialio-counter.c new file +25 -0
+      * any auxiliary IIO channels provided in iio_counter are also set
+      */
+     err =3D iio_counter_channels_alloc(counter);
+     if (err)
+             goto err_channels_alloc;
+
+     /* Pointer to the counter is stored in indio_dev as a way to refer
+      * back to the counter from within various IIO callbacks
+      */
+     priv =3D iio_priv(counter->indio_dev);
+     memcpy(priv, &counter, sizeof(*priv));
+
+     err =3D iio_device_register(counter->indio_dev);
+     if (err)
+             goto err_iio_device_register;
+
+     return 0;
+
+err_iio_device_register:
+     iio_counter_channels_free(counter);
+err_channels_alloc:
+     kfree(info);
+err_info_alloc:
+     iio_device_free(counter->indio_dev);
+     return err;
Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+}
+EXPORT_SYMBOL(iio_counter_register);
+
+/**
+ * iio_counter_unregister - unregister Counter from the system
+ * @counter: pointer to IIO Counter to unregister
+ *
+ * The Counter is unregistered from the system. The indio_dev is unregi=

stered

Quoted 22 lines
Quoted 22 lines

+ * and all allocated memory is freed.
+ */

drivers/iio/industrialio-counter.c new file +20 -0
+void iio_counter_unregister(struct iio_counter *const counter)
+{
+     const struct iio_info *const info =3D counter->indio_dev->info;
+
+     if (!counter)
+             return;
+
+     iio_device_unregister(counter->indio_dev);
+
+     iio_counter_channels_free(counter);
+
+     kfree(info);
+     iio_device_free(counter->indio_dev);
+}
+EXPORT_SYMBOL(iio_counter_unregister);
+
+static void devm_iio_counter_unreg(struct device *dev, void *res)
+{
+     iio_counter_unregister(*(struct iio_counter **)res);
+}
Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+
+/**
+ * devm_iio_counter_register - Resource-managed iio_counter_register
+ * @dev: Device to allocate iio_counter for
+ * @counter: pointer to IIO Counter to register
+ *
+ * Managed iio_counter_register. The IIO counter registered with this
+ * function is automatically unregistered on driver detach. This functi=

on

Quoted 1 line
Quoted 1 line

+ * calls iio_counter_register internally. Refer to that function for mo=

re

Quoted 3 lines
Quoted 3 lines

+ * information.
+ *
+ * If an iio counter registered with this function needs to be unregist=

ered

Quoted 4 lines
Quoted 4 lines

+ * separately, devm_iio_counter_unregister must be used.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.

Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines

+ */

drivers/iio/industrialio-counter.c new file +7 -0
+int devm_iio_counter_register(struct device *dev,
+     struct iio_counter *const counter)
+{
+     struct iio_counter **ptr;
+     int ret;
+
+     ptr =3D devres_alloc(devm_iio_counter_unreg, sizeof(*ptr), GFP_KER=

NEL);

Quoted 7 lines
Quoted 7 lines
drivers/iio/industrialio-counter.c new file +7 -0
+     if (!ptr)
+             return -ENOMEM;
+
+     *ptr =3D counter;
+     ret =3D iio_counter_register(counter);
+     if (!ret)
+             devres_add(dev, ptr);
Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+     else
+             devres_free(ptr);
+
+     return ret;
+}
+EXPORT_SYMBOL(devm_iio_counter_register);
+
+static int devm_iio_counter_match(struct device *dev, void *res, void *=

data)

Quoted 5 lines
Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5 -0
+{
+     struct iio_counter **r =3D res;
+
+     if (!r || !*r) {
+             WARN_ON(!r || !*r);
Quoted 8 lines

drivers/iio/industrialio-counter.c

Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8 -0
+             return 0;
+     }
+
+     return *r =3D=3D data;
+}
+
+/**
+ * devm_iio_counter_unregister - Resource-managed iio_counter_unregiste=

r

Quoted 51 lines
Quoted 51 lines

+ * @dev: Device this iio_counter belongs to
+ * @counter: the iio counter associated with the device
+ *
+ * Unregister iio counter registered with devm_iio_counter_register.
+ */

drivers/iio/industrialio-counter.c new file +10 -0
+void devm_iio_counter_unregister(struct device *dev,
+     struct iio_counter *const counter)
+{
+     int rc;
+
+     rc =3D devres_release(dev, devm_iio_counter_unreg,
+             devm_iio_counter_match, counter);
+     WARN_ON(rc);
+}
+EXPORT_SYMBOL(devm_iio_counter_unregister);
include/linux/iio/counter.h new file +30 -0
new file mode 100644
index 000000000000..35645406711a
@@ -0,0 +1,166 @@
+/*
+ * Industrial I/O counter interface
+ * Copyright (C) 2017 William Breathitt Gray
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+#ifndef _IIO_COUNTER_H_
+#define _IIO_COUNTER_H_
+
+#ifdef CONFIG_IIO_COUNTER
+
+#include <linux/device.h>
+#include <linux/types.h>
+
+#include <linux/iio/iio.h>
+
+/**
+ * struct iio_counter_signal - IIO Counter Signal node
+ * @id:              [DRIVER] unique ID used to identify signal
+ * @name:    [DRIVER] device-specific signal name
+ */
+struct iio_counter_signal {
+     int             id;
Quoted 8 lines

include/linux/iio/counter.h

Quoted 8 lines
include/linux/iio/counter.h new file +8 -0
+     const char      *name;
+};
+
+/**
+ * struct iio_counter_trigger - IIO Counter Trigger node
+ * @mode:            [DRIVER] current trigger mode state
+ * @trigger_modes:   [DRIVER] available trigger modes
+ * @num_trigger_modes:       [DRIVER] number of modes specified in @tri=

gger_modes

Quoted 10 lines

include/linux/iio/counter.h line 45

Quoted 7 lines
include/linux/iio/counter.h new file +7 -0
+ * @signal:          [DRIVER] pointer to associated signal
+ */
+struct iio_counter_trigger {
+     unsigned int                    mode;
+     const char *const               *trigger_modes;
+     unsigned int                    num_trigger_modes;
+     struct iio_counter_signal       *signal;

Are signals actually changeable? - Or is that pointer constant?

Quoted 9 lines

include/linux/iio/counter.h

Quoted 9 lines
include/linux/iio/counter.h new file +9 -0
+};
+
+/**
+ * struct iio_counter_value - IIO Counter Value node
+ * @id:                      [DRIVER] unique ID used to identify value
+ * @name:            [DRIVER] device-specific value name
+ * @mode:            [DRIVER] current function mode state
+ * @function_modes:  [DRIVER] available function modes
+ * @num_function_modes:      [DRIVER] number of modes specified in @fun=

ction_modes

Quoted 1 line

include/linux/iio/counter.h line 55

Quoted 1 line
include/linux/iio/counter.h new file +1 -0
+ * @triggers:                [DRIVER] array of triggers for initializat=

ion

Quoted 2 lines

include/linux/iio/counter.h line 57

Quoted 2 lines
include/linux/iio/counter.h new file +2 -0
+ * @num_triggers:    [DRIVER] number of triggers specified in @triggers
+ * @function_enum:   [INTERN] used internally to generate function attr=

ibutes

Quoted 11 lines
Quoted 11 lines
include/linux/iio/counter.h new file +11 -0
+ */
+struct iio_counter_value {
+     int                     id;
+     const char              *name;
+     unsigned int            mode;
+     const char *const       *function_modes;
+     unsigned int            num_function_modes;
+
+     struct iio_counter_trigger      *triggers;
+     size_t                          num_triggers;
+
Quoted 8 lines

include/linux/iio/counter.h

Quoted 8 lines
include/linux/iio/counter.h new file +8 -0
+     struct iio_enum         function_enum;
+};
+
+struct iio_counter;
+
+/**
+ * struct iio_counter_ops - IIO Counter related callbacks
+ * @signal_read:     function to request a signal value from the device=

.

Quoted 1 line

include/linux/iio/counter.h line 77

Quoted 1 line
include/linux/iio/counter.h new file +1 -0
+ *                   Return value will specify the type of value return=

ed by

Quoted 7 lines

include/linux/iio/counter.h line 79

Quoted 2 lines
include/linux/iio/counter.h new file +2 -0
+ *                   the device. val and val2 will contain the elements
+ *                   making up the returned value.

Add some detail on the importance of the return value.

Quoted 2 lines
include/linux/iio/counter.h new file +2 -0
+ * @signal_write:    function to write a signal value to the device.
+ *                      Parameters are interpreted the same as signal_r=

ead.

Quoted 2 lines

You need a means of establishing the correct format for write (we needed =

it

Quoted 3 lines

in IIO fairly early on too)

Quoted 1 line
include/linux/iio/counter.h new file +1 -0
+ * @trigger_mode_set:        function to set the trigger mode. mode is =

the index of

Quoted 1 line

include/linux/iio/counter.h line 83

Quoted 1 line
include/linux/iio/counter.h new file +1 -0
+ *                   the requested mode from the value trigger_modes ar=

ray.

Quoted 1 line

include/linux/iio/counter.h line 84

Quoted 1 line
include/linux/iio/counter.h new file +1 -0
+ * @trigger_mode_get:        function to get the current trigger mode. =

Return value

Quoted 1 line

include/linux/iio/counter.h line 85

Quoted 1 line
include/linux/iio/counter.h new file +1 -0
+ *                   will specify the index of the current mode from th=

e

Quoted 2 lines

include/linux/iio/counter.h line 87

Quoted 2 lines
include/linux/iio/counter.h new file +2 -0
+ *                   value trigger_modes array.
+ * @value_read:              function to request a value value from the=

device.

Quoted 1 line

include/linux/iio/counter.h line 88

Quoted 1 line
include/linux/iio/counter.h new file +1 -0
+ *                   Return value will specify the type of value return=

ed by

Quoted 4 lines

include/linux/iio/counter.h

Quoted 4 lines
include/linux/iio/counter.h new file +4 -0
+ *                   the device. val and val2 will contain the elements
+ *                   making up the returned value.
+ * @value_write:     function to write a value value to the device.
+ *                      Parameters are interpreted the same as value_re=

ad.

Quoted 1 line

include/linux/iio/counter.h line 93

Quoted 1 line
include/linux/iio/counter.h new file +1 -0
+ * @value_function_set: function to set the value function mode. mode i=

s the

Quoted 3 lines

include/linux/iio/counter.h

Quoted 3 lines
include/linux/iio/counter.h new file +3 -0
+ *                   index of the requested mode from the value
+ *                   function_modes array.
+ * @value_function_get: function to get the current value function mode=

. Return

Quoted 1 line

include/linux/iio/counter.h line 97

Quoted 1 line
include/linux/iio/counter.h new file +1 -0
+ *                   value will specify the index of the current mode f=

rom

Quoted 18 lines
Quoted 18 lines
include/linux/iio/counter.h new file +18 -0
+ *                   the value function_modes array.
+ */
+struct iio_counter_ops {
+     int (*signal_read)(struct iio_counter *counter,
+             struct iio_counter_signal *signal, int *val, int *val2);
+     int (*signal_write)(struct iio_counter *counter,
+             struct iio_counter_signal *signal, int val, int val2);
+     int (*trigger_mode_set)(struct iio_counter *counter,
+             struct iio_counter_value *value,
+             struct iio_counter_trigger *trigger, unsigned int mode);
+     int (*trigger_mode_get)(struct iio_counter *counter,
+             struct iio_counter_value *value,
+             struct iio_counter_trigger *trigger);
+     int (*value_read)(struct iio_counter *counter,
+             struct iio_counter_value *value, int *val, int *val2);
+     int (*value_write)(struct iio_counter *counter,
+             struct iio_counter_value *value, int val, int val2);
+     int (*value_function_set)(struct iio_counter *counter,
Quoted 8 lines

include/linux/iio/counter.h

Quoted 8 lines
include/linux/iio/counter.h new file +8 -0
+             struct iio_counter_value *value, unsigned int mode);
+     int (*value_function_get)(struct iio_counter *counter,
+             struct iio_counter_value *value);
+};
+
+/**
+ * struct iio_counter - IIO Counter data structure
+ * @id:                      [DRIVER] unique ID used to identify counte=

r

Quoted 2 lines

include/linux/iio/counter.h line 125

Quoted 2 lines
include/linux/iio/counter.h new file +2 -0
+ * @name:            [DRIVER] name of the device
+ * @dev:             [DRIVER] device structure, should be assigned a pa=

rent

Quoted 2 lines

include/linux/iio/counter.h line 127

Quoted 2 lines
include/linux/iio/counter.h new file +2 -0
+ *                   and owner
+ * @ops:             [DRIVER] callbacks from driver for counter compone=

nts

Quoted 4 lines

include/linux/iio/counter.h

Quoted 4 lines
include/linux/iio/counter.h new file +4 -0
+ * @signals:         [DRIVER] array of signals for initialization
+ * @num_signals:     [DRIVER] number of signals specified in @signals
+ * @values:          [DRIVER] array of values for initialization
+ * @num_values:              [DRIVER] number of values specified in @va=

lues

Quoted 1 line

include/linux/iio/counter.h line 132

Quoted 1 line
include/linux/iio/counter.h new file +1 -0
+ * @channels:                [DRIVER] channel specification structure t=

able

Quoted 32 lines

include/linux/iio/counter.h line 138

Quoted 6 lines
include/linux/iio/counter.h new file +6 -0
+ * @num_channels:    [DRIVER] number of channels specified in @channels
+ * @info:            [DRIVER] callbacks and constant info from driver
+ * @indio_dev:               [INTERN] industrial I/O device structure
+ * @driver_data:     [DRIVER] driver data
+ */
+struct iio_counter {

Mentioned earlier - I think naming the device counter, is confusing. The counters should be named counter - call it iio_counter_device or iio_counter_group or something and keep counter for the things currently called value.

Quoted 20 lines
include/linux/iio/counter.h new file +20 -0
+     int                             id;
+     const char                      *name;
+     struct device                   *dev;
+     const struct iio_counter_ops    *ops;
+
+     struct iio_counter_signal       *signals;
+     size_t                          num_signals;
+     struct iio_counter_value        *values;
+     size_t                          num_values;
+
+     const struct iio_chan_spec      *channels;
+     size_t                          num_channels;
+     const struct iio_info           *info;
+
+     struct iio_dev  *indio_dev;
+     void            *driver_data;
+};
+
+int iio_counter_register(struct iio_counter *const counter);
+void iio_counter_unregister(struct iio_counter *const counter);
Quoted 9 lines

include/linux/iio/counter.h line 166

Quoted 8 lines
include/linux/iio/counter.h new file +8 -0
+int devm_iio_counter_register(struct device *dev,
+     struct iio_counter *const counter);
+void devm_iio_counter_unregister(struct device *dev,
+     struct iio_counter *const counter);
+
+#endif /* CONFIG_IIO_COUNTER */
+
+#endif /* _IIO_COUNTER_H_ */

--=20
Benjamin Gaignard

Graphic Study Group

Linaro.org =E2=94=82 Open source software for ARM SoCs

Follow Linaro: Facebook | Twitter | Blog